@bookedsolid/reagent 0.7.2 → 0.11.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 +277 -140
- package/agents/engineering/pr-voice-reviewer.md +229 -0
- package/agents/product-owner.md +152 -0
- package/agents/reagent-orchestrator.md +8 -0
- package/commands/pm-status.md +230 -0
- package/commands/review-pr.md +197 -0
- package/dist/cli/commands/catalyze/gap-detector.d.ts.map +1 -1
- package/dist/cli/commands/catalyze/gap-detector.js +1 -3
- package/dist/cli/commands/catalyze/gap-detector.js.map +1 -1
- package/dist/cli/commands/daemon/eject.d.ts +13 -0
- package/dist/cli/commands/daemon/eject.d.ts.map +1 -0
- package/dist/cli/commands/daemon/eject.js +74 -0
- package/dist/cli/commands/daemon/eject.js.map +1 -0
- package/dist/cli/commands/daemon/index.d.ts +5 -0
- package/dist/cli/commands/daemon/index.d.ts.map +1 -0
- package/dist/cli/commands/daemon/index.js +64 -0
- package/dist/cli/commands/daemon/index.js.map +1 -0
- package/dist/cli/commands/daemon/restart.d.ts +10 -0
- package/dist/cli/commands/daemon/restart.d.ts.map +1 -0
- package/dist/cli/commands/daemon/restart.js +20 -0
- package/dist/cli/commands/daemon/restart.js.map +1 -0
- package/dist/cli/commands/daemon/start.d.ts +2 -0
- package/dist/cli/commands/daemon/start.d.ts.map +1 -0
- package/dist/cli/commands/daemon/start.js +143 -0
- package/dist/cli/commands/daemon/start.js.map +1 -0
- package/dist/cli/commands/daemon/status.d.ts +2 -0
- package/dist/cli/commands/daemon/status.d.ts.map +1 -0
- package/dist/cli/commands/daemon/status.js +90 -0
- package/dist/cli/commands/daemon/status.js.map +1 -0
- package/dist/cli/commands/daemon/stop.d.ts +2 -0
- package/dist/cli/commands/daemon/stop.d.ts.map +1 -0
- package/dist/cli/commands/daemon/stop.js +73 -0
- package/dist/cli/commands/daemon/stop.js.map +1 -0
- package/dist/cli/commands/init/claude-hooks.d.ts +1 -1
- package/dist/cli/commands/init/claude-hooks.d.ts.map +1 -1
- package/dist/cli/commands/init/claude-hooks.js +10 -4
- package/dist/cli/commands/init/claude-hooks.js.map +1 -1
- package/dist/cli/commands/init/index.d.ts.map +1 -1
- package/dist/cli/commands/init/index.js +5 -1
- package/dist/cli/commands/init/index.js.map +1 -1
- package/dist/cli/commands/init/policy.d.ts.map +1 -1
- package/dist/cli/commands/init/policy.js +21 -0
- package/dist/cli/commands/init/policy.js.map +1 -1
- package/dist/cli/commands/init/types.d.ts +16 -0
- package/dist/cli/commands/init/types.d.ts.map +1 -1
- package/dist/cli/index.js +9 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/config/daemon-loader.d.ts +16 -0
- package/dist/config/daemon-loader.d.ts.map +1 -0
- package/dist/config/daemon-loader.js +76 -0
- package/dist/config/daemon-loader.js.map +1 -0
- package/dist/config/gateway-config.d.ts.map +1 -1
- package/dist/config/gateway-config.js +6 -0
- package/dist/config/gateway-config.js.map +1 -1
- package/dist/config/policy-loader.d.ts +27 -0
- package/dist/config/policy-loader.d.ts.map +1 -1
- package/dist/config/policy-loader.js +103 -10
- package/dist/config/policy-loader.js.map +1 -1
- package/dist/gateway/circuit-breaker.d.ts +60 -0
- package/dist/gateway/circuit-breaker.d.ts.map +1 -0
- package/dist/gateway/circuit-breaker.js +104 -0
- package/dist/gateway/circuit-breaker.js.map +1 -0
- package/dist/gateway/collision-detector.d.ts +31 -0
- package/dist/gateway/collision-detector.d.ts.map +1 -0
- package/dist/gateway/collision-detector.js +53 -0
- package/dist/gateway/collision-detector.js.map +1 -0
- package/dist/gateway/middleware/blocked-paths.js +2 -2
- package/dist/gateway/middleware/blocked-paths.js.map +1 -1
- package/dist/gateway/middleware/circuit-breaker.d.ts +12 -0
- package/dist/gateway/middleware/circuit-breaker.d.ts.map +1 -0
- package/dist/gateway/middleware/circuit-breaker.js +44 -0
- package/dist/gateway/middleware/circuit-breaker.js.map +1 -0
- package/dist/gateway/middleware/injection.d.ts +23 -0
- package/dist/gateway/middleware/injection.d.ts.map +1 -0
- package/dist/gateway/middleware/injection.js +129 -0
- package/dist/gateway/middleware/injection.js.map +1 -0
- package/dist/gateway/middleware/policy.js +2 -2
- package/dist/gateway/middleware/policy.js.map +1 -1
- package/dist/gateway/middleware/rate-limit.d.ts +13 -0
- package/dist/gateway/middleware/rate-limit.d.ts.map +1 -0
- package/dist/gateway/middleware/rate-limit.js +32 -0
- package/dist/gateway/middleware/rate-limit.js.map +1 -0
- package/dist/gateway/middleware/redact.d.ts.map +1 -1
- package/dist/gateway/middleware/redact.js +7 -0
- package/dist/gateway/middleware/redact.js.map +1 -1
- package/dist/gateway/middleware/result-size-cap.d.ts +14 -0
- package/dist/gateway/middleware/result-size-cap.d.ts.map +1 -0
- package/dist/gateway/middleware/result-size-cap.js +49 -0
- package/dist/gateway/middleware/result-size-cap.js.map +1 -0
- package/dist/gateway/native-tools.js +1 -1
- package/dist/gateway/native-tools.js.map +1 -1
- package/dist/gateway/rate-limiter.d.ts +47 -0
- package/dist/gateway/rate-limiter.d.ts.map +1 -0
- package/dist/gateway/rate-limiter.js +89 -0
- package/dist/gateway/rate-limiter.js.map +1 -0
- package/dist/gateway/server.d.ts.map +1 -1
- package/dist/gateway/server.js +27 -1
- package/dist/gateway/server.js.map +1 -1
- package/dist/gateway/tool-proxy.js +1 -1
- package/dist/gateway/tool-proxy.js.map +1 -1
- package/dist/types/daemon.d.ts +45 -0
- package/dist/types/daemon.d.ts.map +1 -0
- package/dist/types/daemon.js +2 -0
- package/dist/types/daemon.js.map +1 -0
- package/dist/types/gateway.d.ts +9 -0
- package/dist/types/gateway.d.ts.map +1 -1
- package/dist/types/policy.d.ts +1 -0
- package/dist/types/policy.d.ts.map +1 -1
- package/hooks/_lib/discord.sh +75 -0
- package/hooks/blocked-paths-enforcer.sh +0 -1
- package/hooks/changeset-security-gate.sh +143 -0
- package/hooks/commit-review-gate.sh +40 -10
- package/hooks/import-guard.sh +14 -0
- package/hooks/network-exfil-guard.sh +20 -2
- package/hooks/pr-issue-link-gate.sh +65 -0
- package/hooks/push-review-gate.sh +17 -2
- package/hooks/rate-limit-guard.sh +26 -2
- package/hooks/reagent-notify.sh +65 -0
- package/hooks/security-disclosure-gate.sh +146 -0
- package/husky/pre-push.sh +84 -0
- package/package.json +12 -2
- package/profiles/bst-internal.json +12 -2
- package/profiles/client-engagement.json +12 -2
|
@@ -1,3 +1,30 @@
|
|
|
1
1
|
import type { Policy } from '../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Async policy loader with TTL cache and mtime-based invalidation.
|
|
4
|
+
*
|
|
5
|
+
* Cache behavior:
|
|
6
|
+
* - On each call, stat the file to get current mtime.
|
|
7
|
+
* - If mtime changed since the cached entry, invalidate immediately regardless of TTL.
|
|
8
|
+
* - If the entry is older than the TTL, re-read from disk.
|
|
9
|
+
* - Otherwise, return the cached entry.
|
|
10
|
+
*
|
|
11
|
+
* TTL is configurable via the REAGENT_POLICY_CACHE_TTL_MS environment variable.
|
|
12
|
+
*
|
|
13
|
+
* PERFORMANCE: fs.promises.readFile avoids blocking the event loop on every tool invocation.
|
|
14
|
+
* SECURITY: mtime invalidation ensures a tightened policy takes effect on the next call.
|
|
15
|
+
* CONCURRENCY: inflightReads map guarantees at most one disk read per baseDir at a time.
|
|
16
|
+
*/
|
|
17
|
+
export declare function loadPolicyAsync(baseDir: string): Promise<Policy>;
|
|
18
|
+
/**
|
|
19
|
+
* Synchronous policy loader — retained for CLI startup paths that must be sync.
|
|
20
|
+
* Does NOT use the cache — always reads from disk.
|
|
21
|
+
*
|
|
22
|
+
* Prefer loadPolicyAsync for middleware and any async context.
|
|
23
|
+
*/
|
|
2
24
|
export declare function loadPolicy(baseDir: string): Policy;
|
|
25
|
+
/**
|
|
26
|
+
* Invalidate the cache for a given baseDir.
|
|
27
|
+
* Exposed for testing — production code should rely on TTL and mtime invalidation.
|
|
28
|
+
*/
|
|
29
|
+
export declare function invalidatePolicyCache(baseDir?: string): void;
|
|
3
30
|
//# sourceMappingURL=policy-loader.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy-loader.d.ts","sourceRoot":"","sources":["../../src/config/policy-loader.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"policy-loader.d.ts","sourceRoot":"","sources":["../../src/config/policy-loader.ts"],"names":[],"mappings":"AAMA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AA+FhD;;;;;;;;;;;;;;GAcG;AACH,wBAAsB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CA6BtE;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CASlD;AAED;;;GAGG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAM5D"}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from 'node:fs';
|
|
2
|
+
import fsPromises from 'node:fs/promises';
|
|
2
3
|
import path from 'node:path';
|
|
3
4
|
import { parse as parseYaml } from 'yaml';
|
|
4
5
|
import { z } from 'zod';
|
|
@@ -22,12 +23,28 @@ const PolicySchema = z.object({
|
|
|
22
23
|
blocked_paths: z.array(z.string()),
|
|
23
24
|
notification_channel: z.string().default(''),
|
|
24
25
|
});
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
26
|
+
/** Default TTL for the policy cache — 30 seconds. Configurable via env var. */
|
|
27
|
+
const DEFAULT_CACHE_TTL_MS = 30_000;
|
|
28
|
+
/**
|
|
29
|
+
* Module-level cache: one entry per baseDir (singleton per process).
|
|
30
|
+
* SECURITY: Cache is never used to serve a more permissive policy than the file on disk.
|
|
31
|
+
* mtime-based invalidation ensures policy tightening takes effect before TTL expires.
|
|
32
|
+
*/
|
|
33
|
+
const policyCache = new Map();
|
|
34
|
+
// Coalesce concurrent cache misses — two callers racing to populate the same
|
|
35
|
+
// key should share one disk read, not stomp each other. On a gateway under load
|
|
36
|
+
// this is the difference between an occasional wasted syscall and a data race
|
|
37
|
+
// on a security boundary.
|
|
38
|
+
const inflightReads = new Map();
|
|
39
|
+
function applyMaxCeiling(policy) {
|
|
40
|
+
// SECURITY: Enforce max_autonomy_level ceiling — clamp if autonomy_level exceeds it.
|
|
41
|
+
if (LEVEL_ORDER[policy.autonomy_level] > LEVEL_ORDER[policy.max_autonomy_level]) {
|
|
42
|
+
console.error(`[reagent] WARNING: autonomy_level ${policy.autonomy_level} exceeds max_autonomy_level ${policy.max_autonomy_level} — clamping to ${policy.max_autonomy_level}`);
|
|
43
|
+
return { ...policy, autonomy_level: policy.max_autonomy_level };
|
|
29
44
|
}
|
|
30
|
-
|
|
45
|
+
return policy;
|
|
46
|
+
}
|
|
47
|
+
function parseRawPolicy(raw, policyPath) {
|
|
31
48
|
let parsed;
|
|
32
49
|
try {
|
|
33
50
|
parsed = parseYaml(raw);
|
|
@@ -42,11 +59,87 @@ export function loadPolicy(baseDir) {
|
|
|
42
59
|
catch (zodErr) {
|
|
43
60
|
throw new Error(`Invalid policy schema at ${policyPath}: ${zodErr instanceof Error ? zodErr.message : zodErr}`);
|
|
44
61
|
}
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
62
|
+
return applyMaxCeiling(policy);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Reads and parses the policy file from disk, then populates the cache.
|
|
66
|
+
* Kept separate so the inflight map can hold a single promise per baseDir
|
|
67
|
+
* and multiple concurrent callers can join it rather than each issuing their
|
|
68
|
+
* own stat+read pair.
|
|
69
|
+
*/
|
|
70
|
+
async function readPolicyFromDisk(baseDir, policyPath, currentMtime) {
|
|
71
|
+
const raw = await fsPromises.readFile(policyPath, 'utf8');
|
|
72
|
+
const policy = parseRawPolicy(raw, policyPath);
|
|
73
|
+
policyCache.set(baseDir, { policy, cachedAt: Date.now(), mtimeMs: currentMtime });
|
|
50
74
|
return policy;
|
|
51
75
|
}
|
|
76
|
+
/**
|
|
77
|
+
* Async policy loader with TTL cache and mtime-based invalidation.
|
|
78
|
+
*
|
|
79
|
+
* Cache behavior:
|
|
80
|
+
* - On each call, stat the file to get current mtime.
|
|
81
|
+
* - If mtime changed since the cached entry, invalidate immediately regardless of TTL.
|
|
82
|
+
* - If the entry is older than the TTL, re-read from disk.
|
|
83
|
+
* - Otherwise, return the cached entry.
|
|
84
|
+
*
|
|
85
|
+
* TTL is configurable via the REAGENT_POLICY_CACHE_TTL_MS environment variable.
|
|
86
|
+
*
|
|
87
|
+
* PERFORMANCE: fs.promises.readFile avoids blocking the event loop on every tool invocation.
|
|
88
|
+
* SECURITY: mtime invalidation ensures a tightened policy takes effect on the next call.
|
|
89
|
+
* CONCURRENCY: inflightReads map guarantees at most one disk read per baseDir at a time.
|
|
90
|
+
*/
|
|
91
|
+
export async function loadPolicyAsync(baseDir) {
|
|
92
|
+
const policyPath = path.join(baseDir, '.reagent', 'policy.yaml');
|
|
93
|
+
const ttlMs = Number(process.env.REAGENT_POLICY_CACHE_TTL_MS ?? DEFAULT_CACHE_TTL_MS);
|
|
94
|
+
const now = Date.now();
|
|
95
|
+
// Check mtime before consulting the cache — a changed file always invalidates.
|
|
96
|
+
let currentMtime;
|
|
97
|
+
try {
|
|
98
|
+
const stat = await fsPromises.stat(policyPath);
|
|
99
|
+
currentMtime = stat.mtimeMs;
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
throw new Error(`Policy file not found: ${policyPath}`);
|
|
103
|
+
}
|
|
104
|
+
const cached = policyCache.get(baseDir);
|
|
105
|
+
if (cached !== undefined && cached.mtimeMs === currentMtime && now - cached.cachedAt < ttlMs) {
|
|
106
|
+
return cached.policy;
|
|
107
|
+
}
|
|
108
|
+
// If another caller already kicked off a read for this baseDir, join it rather
|
|
109
|
+
// than issuing a second syscall and racing on the cache write.
|
|
110
|
+
const inflight = inflightReads.get(baseDir);
|
|
111
|
+
if (inflight)
|
|
112
|
+
return inflight;
|
|
113
|
+
const read = readPolicyFromDisk(baseDir, policyPath, currentMtime).finally(() => {
|
|
114
|
+
inflightReads.delete(baseDir);
|
|
115
|
+
});
|
|
116
|
+
inflightReads.set(baseDir, read);
|
|
117
|
+
return read;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* Synchronous policy loader — retained for CLI startup paths that must be sync.
|
|
121
|
+
* Does NOT use the cache — always reads from disk.
|
|
122
|
+
*
|
|
123
|
+
* Prefer loadPolicyAsync for middleware and any async context.
|
|
124
|
+
*/
|
|
125
|
+
export function loadPolicy(baseDir) {
|
|
126
|
+
const policyPath = path.join(baseDir, '.reagent', 'policy.yaml');
|
|
127
|
+
if (!fs.existsSync(policyPath)) {
|
|
128
|
+
throw new Error(`Policy file not found: ${policyPath}`);
|
|
129
|
+
}
|
|
130
|
+
const raw = fs.readFileSync(policyPath, 'utf8');
|
|
131
|
+
return parseRawPolicy(raw, policyPath);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Invalidate the cache for a given baseDir.
|
|
135
|
+
* Exposed for testing — production code should rely on TTL and mtime invalidation.
|
|
136
|
+
*/
|
|
137
|
+
export function invalidatePolicyCache(baseDir) {
|
|
138
|
+
if (baseDir === undefined) {
|
|
139
|
+
policyCache.clear();
|
|
140
|
+
}
|
|
141
|
+
else {
|
|
142
|
+
policyCache.delete(baseDir);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
52
145
|
//# sourceMappingURL=policy-loader.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy-loader.js","sourceRoot":"","sources":["../../src/config/policy-loader.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,SAAS,CAAC;AACzB,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,KAAK,IAAI,SAAS,EAAE,MAAM,MAAM,CAAC;AAC1C,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAGlD,8EAA8E;AAC9E,MAAM,WAAW,GAAkC;IACjD,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,CAAC;IACrB,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,CAAC;IACrB,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,CAAC;IACrB,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,CAAC;CACtB,CAAC;AAEF,MAAM,YAAY,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5B,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE;IACnB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE;IACxB,cAAc,EAAE,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC;IAC3C,kBAAkB,EAAE,CAAC,CAAC,UAAU,CAAC,aAAa,CAAC;IAC/C,iCAAiC,EAAE,CAAC,CAAC,OAAO,EAAE;IAC9C,oBAAoB,EAAE,CAAC,CAAC,OAAO,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC;IAChD,aAAa,EAAE,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,EAAE,CAAC;IAClC,oBAAoB,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,EAAE,CAAC;CAC7C,CAAC,CAAC;AAEH,MAAM,
|
|
1
|
+
{"version":3,"file":"policy-loader.js","sourceRoot":"","sources":["../../src/config/policy-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export type CircuitState = 'closed' | 'open' | 'half-open';
|
|
2
|
+
export interface CircuitBreakerOptions {
|
|
3
|
+
/** Consecutive failures before opening the circuit. Default: 5 */
|
|
4
|
+
failureThreshold?: number;
|
|
5
|
+
/** Milliseconds to wait in open state before moving to half-open. Default: 30_000 */
|
|
6
|
+
cooldownMs?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface CircuitStatus {
|
|
9
|
+
state: CircuitState;
|
|
10
|
+
serverName: string;
|
|
11
|
+
/** ISO timestamp of when the circuit will attempt recovery (only set when open) */
|
|
12
|
+
retryAt?: string;
|
|
13
|
+
}
|
|
14
|
+
interface CircuitEntry {
|
|
15
|
+
state: CircuitState;
|
|
16
|
+
consecutiveFailures: number;
|
|
17
|
+
openedAt: number | null;
|
|
18
|
+
failureThreshold: number;
|
|
19
|
+
cooldownMs: number;
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Per-server circuit breaker.
|
|
23
|
+
*
|
|
24
|
+
* State machine:
|
|
25
|
+
* closed → open after N consecutive failures
|
|
26
|
+
* open → half-open after cooldown period
|
|
27
|
+
* half-open → closed on next success
|
|
28
|
+
* half-open → open on next failure
|
|
29
|
+
*
|
|
30
|
+
* State is kept in memory — resets to closed on gateway restart.
|
|
31
|
+
*/
|
|
32
|
+
export declare class CircuitBreaker {
|
|
33
|
+
private circuits;
|
|
34
|
+
private defaultOptions;
|
|
35
|
+
constructor(defaults?: CircuitBreakerOptions);
|
|
36
|
+
private getOrCreate;
|
|
37
|
+
/**
|
|
38
|
+
* Check whether a call to `serverName` is allowed.
|
|
39
|
+
*
|
|
40
|
+
* Returns null if the call may proceed, or a CircuitStatus with the
|
|
41
|
+
* current state if the circuit is open (or still deciding in half-open).
|
|
42
|
+
*
|
|
43
|
+
* Side effect: transitions open → half-open if cooldown has elapsed.
|
|
44
|
+
*/
|
|
45
|
+
isAllowed(serverName: string): CircuitStatus | null;
|
|
46
|
+
/**
|
|
47
|
+
* Record a successful call. Transitions half-open → closed.
|
|
48
|
+
* In closed state, resets the consecutive failure counter.
|
|
49
|
+
*/
|
|
50
|
+
recordSuccess(serverName: string): void;
|
|
51
|
+
/**
|
|
52
|
+
* Record a failed call. Increments failure counter.
|
|
53
|
+
* Transitions closed → open when threshold is reached, or half-open → open immediately.
|
|
54
|
+
*/
|
|
55
|
+
recordFailure(serverName: string): void;
|
|
56
|
+
/** Expose internal state for testing */
|
|
57
|
+
getCircuit(serverName: string): CircuitEntry | undefined;
|
|
58
|
+
}
|
|
59
|
+
export {};
|
|
60
|
+
//# sourceMappingURL=circuit-breaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../src/gateway/circuit-breaker.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;AAE3D,MAAM,WAAW,qBAAqB;IACpC,kEAAkE;IAClE,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,qFAAqF;IACrF,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,YAAY,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,YAAY;IACpB,KAAK,EAAE,YAAY,CAAC;IACpB,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,gBAAgB,EAAE,MAAM,CAAC;IACzB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED;;;;;;;;;;GAUG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAmC;IACnD,OAAO,CAAC,cAAc,CAAkC;gBAE5C,QAAQ,GAAE,qBAA0B;IAOhD,OAAO,CAAC,WAAW;IAenB;;;;;;;OAOG;IACH,SAAS,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,GAAG,IAAI;IA6BnD;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAYvC;;;OAGG;IACH,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAoBvC,wCAAwC;IACxC,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,YAAY,GAAG,SAAS;CAGzD"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-server circuit breaker.
|
|
3
|
+
*
|
|
4
|
+
* State machine:
|
|
5
|
+
* closed → open after N consecutive failures
|
|
6
|
+
* open → half-open after cooldown period
|
|
7
|
+
* half-open → closed on next success
|
|
8
|
+
* half-open → open on next failure
|
|
9
|
+
*
|
|
10
|
+
* State is kept in memory — resets to closed on gateway restart.
|
|
11
|
+
*/
|
|
12
|
+
export class CircuitBreaker {
|
|
13
|
+
circuits = new Map();
|
|
14
|
+
defaultOptions;
|
|
15
|
+
constructor(defaults = {}) {
|
|
16
|
+
this.defaultOptions = {
|
|
17
|
+
failureThreshold: defaults.failureThreshold ?? 5,
|
|
18
|
+
cooldownMs: defaults.cooldownMs ?? 30_000,
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
getOrCreate(serverName) {
|
|
22
|
+
let entry = this.circuits.get(serverName);
|
|
23
|
+
if (!entry) {
|
|
24
|
+
entry = {
|
|
25
|
+
state: 'closed',
|
|
26
|
+
consecutiveFailures: 0,
|
|
27
|
+
openedAt: null,
|
|
28
|
+
failureThreshold: this.defaultOptions.failureThreshold,
|
|
29
|
+
cooldownMs: this.defaultOptions.cooldownMs,
|
|
30
|
+
};
|
|
31
|
+
this.circuits.set(serverName, entry);
|
|
32
|
+
}
|
|
33
|
+
return entry;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Check whether a call to `serverName` is allowed.
|
|
37
|
+
*
|
|
38
|
+
* Returns null if the call may proceed, or a CircuitStatus with the
|
|
39
|
+
* current state if the circuit is open (or still deciding in half-open).
|
|
40
|
+
*
|
|
41
|
+
* Side effect: transitions open → half-open if cooldown has elapsed.
|
|
42
|
+
*/
|
|
43
|
+
isAllowed(serverName) {
|
|
44
|
+
const entry = this.getOrCreate(serverName);
|
|
45
|
+
if (entry.state === 'closed')
|
|
46
|
+
return null;
|
|
47
|
+
if (entry.state === 'open') {
|
|
48
|
+
const elapsed = Date.now() - (entry.openedAt ?? 0);
|
|
49
|
+
if (elapsed >= entry.cooldownMs) {
|
|
50
|
+
// Move to half-open so the next call acts as a probe
|
|
51
|
+
entry.state = 'half-open';
|
|
52
|
+
entry.consecutiveFailures = 0;
|
|
53
|
+
console.error(`[reagent] circuit-breaker: "${serverName}" transitioned open → half-open (probing recovery)`);
|
|
54
|
+
return null; // Let the call through as the probe
|
|
55
|
+
}
|
|
56
|
+
const retryAt = new Date((entry.openedAt ?? 0) + entry.cooldownMs).toISOString();
|
|
57
|
+
return {
|
|
58
|
+
state: 'open',
|
|
59
|
+
serverName,
|
|
60
|
+
retryAt,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
// half-open: allow exactly one probe call through
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
/**
|
|
67
|
+
* Record a successful call. Transitions half-open → closed.
|
|
68
|
+
* In closed state, resets the consecutive failure counter.
|
|
69
|
+
*/
|
|
70
|
+
recordSuccess(serverName) {
|
|
71
|
+
const entry = this.getOrCreate(serverName);
|
|
72
|
+
if (entry.state === 'half-open') {
|
|
73
|
+
entry.state = 'closed';
|
|
74
|
+
entry.consecutiveFailures = 0;
|
|
75
|
+
entry.openedAt = null;
|
|
76
|
+
console.error(`[reagent] circuit-breaker: "${serverName}" recovered — circuit closed`);
|
|
77
|
+
}
|
|
78
|
+
else if (entry.state === 'closed') {
|
|
79
|
+
entry.consecutiveFailures = 0;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Record a failed call. Increments failure counter.
|
|
84
|
+
* Transitions closed → open when threshold is reached, or half-open → open immediately.
|
|
85
|
+
*/
|
|
86
|
+
recordFailure(serverName) {
|
|
87
|
+
const entry = this.getOrCreate(serverName);
|
|
88
|
+
if (entry.state === 'open')
|
|
89
|
+
return; // Already open — nothing to do
|
|
90
|
+
entry.consecutiveFailures++;
|
|
91
|
+
const shouldOpen = entry.state === 'half-open' || entry.consecutiveFailures >= entry.failureThreshold;
|
|
92
|
+
if (shouldOpen) {
|
|
93
|
+
entry.state = 'open';
|
|
94
|
+
entry.openedAt = Date.now();
|
|
95
|
+
const retryAt = new Date(entry.openedAt + entry.cooldownMs).toISOString();
|
|
96
|
+
console.error(`[reagent] circuit-breaker: "${serverName}" OPENED after ${entry.consecutiveFailures} failure(s) — will retry at ${retryAt}`);
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
/** Expose internal state for testing */
|
|
100
|
+
getCircuit(serverName) {
|
|
101
|
+
return this.circuits.get(serverName);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
//# sourceMappingURL=circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../src/gateway/circuit-breaker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { ClientManager } from './client-manager.js';
|
|
2
|
+
export interface CollisionEntry {
|
|
3
|
+
toolName: string;
|
|
4
|
+
/** Server that "owns" the original name */
|
|
5
|
+
primaryServer: string;
|
|
6
|
+
/** Server whose tool is shadowed and will be prefixed */
|
|
7
|
+
shadowedServer: string;
|
|
8
|
+
/** The namespaced name the shadowed tool will be registered under */
|
|
9
|
+
namespacedName: string;
|
|
10
|
+
}
|
|
11
|
+
export interface CollisionReport {
|
|
12
|
+
collisions: CollisionEntry[];
|
|
13
|
+
/**
|
|
14
|
+
* Maps `serverName/toolName` → registered name.
|
|
15
|
+
* For primary tools: registered name equals toolName.
|
|
16
|
+
* For shadowed tools: registered name is `serverName/toolName`.
|
|
17
|
+
*/
|
|
18
|
+
nameMap: Map<string, string>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Detects tool name collisions across all downstream servers.
|
|
22
|
+
*
|
|
23
|
+
* When two servers expose the same bare tool name, the first server encountered
|
|
24
|
+
* wins and keeps the original name. The second server's tool gets prefixed as
|
|
25
|
+
* `{serverName}/{toolName}`. This is deterministic (Map insertion order).
|
|
26
|
+
*
|
|
27
|
+
* Runs before the gateway starts accepting connections so admins know about
|
|
28
|
+
* shadowed tools at startup rather than silently losing one.
|
|
29
|
+
*/
|
|
30
|
+
export declare function detectToolCollisions(clientManager: ClientManager): Promise<CollisionReport>;
|
|
31
|
+
//# sourceMappingURL=collision-detector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collision-detector.d.ts","sourceRoot":"","sources":["../../src/gateway/collision-detector.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AAEzD,MAAM,WAAW,cAAc;IAC7B,QAAQ,EAAE,MAAM,CAAC;IACjB,2CAA2C;IAC3C,aAAa,EAAE,MAAM,CAAC;IACtB,yDAAyD;IACzD,cAAc,EAAE,MAAM,CAAC;IACvB,qEAAqE;IACrE,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,cAAc,EAAE,CAAC;IAC7B;;;;OAIG;IACH,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAC9B;AAED;;;;;;;;;GASG;AACH,wBAAsB,oBAAoB,CAAC,aAAa,EAAE,aAAa,GAAG,OAAO,CAAC,eAAe,CAAC,CAiDjG"}
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Detects tool name collisions across all downstream servers.
|
|
3
|
+
*
|
|
4
|
+
* When two servers expose the same bare tool name, the first server encountered
|
|
5
|
+
* wins and keeps the original name. The second server's tool gets prefixed as
|
|
6
|
+
* `{serverName}/{toolName}`. This is deterministic (Map insertion order).
|
|
7
|
+
*
|
|
8
|
+
* Runs before the gateway starts accepting connections so admins know about
|
|
9
|
+
* shadowed tools at startup rather than silently losing one.
|
|
10
|
+
*/
|
|
11
|
+
export async function detectToolCollisions(clientManager) {
|
|
12
|
+
// toolName → first server to register it
|
|
13
|
+
const seen = new Map();
|
|
14
|
+
const collisions = [];
|
|
15
|
+
// key = `serverName::toolName`, value = what name it will be registered as
|
|
16
|
+
const nameMap = new Map();
|
|
17
|
+
for (const [serverName, managed] of clientManager.getAllClients()) {
|
|
18
|
+
let tools;
|
|
19
|
+
try {
|
|
20
|
+
const result = await managed.client.listTools();
|
|
21
|
+
tools = result.tools;
|
|
22
|
+
}
|
|
23
|
+
catch (err) {
|
|
24
|
+
console.error(`[reagent] collision-detector: could not list tools from "${serverName}":`, err instanceof Error ? err.message : err);
|
|
25
|
+
continue;
|
|
26
|
+
}
|
|
27
|
+
for (const tool of tools) {
|
|
28
|
+
const key = `${serverName}::${tool.name}`;
|
|
29
|
+
const prior = seen.get(tool.name);
|
|
30
|
+
if (prior !== undefined) {
|
|
31
|
+
// This tool name was already claimed by `prior` server
|
|
32
|
+
const namespacedName = `${serverName}/${tool.name}`;
|
|
33
|
+
collisions.push({
|
|
34
|
+
toolName: tool.name,
|
|
35
|
+
primaryServer: prior,
|
|
36
|
+
shadowedServer: serverName,
|
|
37
|
+
namespacedName,
|
|
38
|
+
});
|
|
39
|
+
nameMap.set(key, namespacedName);
|
|
40
|
+
// Warn with enough detail that an admin can act on it
|
|
41
|
+
console.error(`[reagent] WARN [GHSA-4j9r] tool name collision: "${tool.name}" is exposed by ` +
|
|
42
|
+
`both "${prior}" (primary, keeps original name) and "${serverName}" ` +
|
|
43
|
+
`(shadowed, will be registered as "${namespacedName}")`);
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
seen.set(tool.name, serverName);
|
|
47
|
+
nameMap.set(key, tool.name);
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
return { collisions, nameMap };
|
|
52
|
+
}
|
|
53
|
+
//# sourceMappingURL=collision-detector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"collision-detector.js","sourceRoot":"","sources":["../../src/gateway/collision-detector.ts"],"names":[],"mappings":"AAsBA;;;;;;;;;GASG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CAAC,aAA4B;IACrE,yCAAyC;IACzC,MAAM,IAAI,GAAG,IAAI,GAAG,EAAkB,CAAC;IACvC,MAAM,UAAU,GAAqB,EAAE,CAAC;IACxC,2EAA2E;IAC3E,MAAM,OAAO,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE1C,KAAK,MAAM,CAAC,UAAU,EAAE,OAAO,CAAC,IAAI,aAAa,CAAC,aAAa,EAAE,EAAE,CAAC;QAClE,IAAI,KAA8B,CAAC;QACnC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,MAAM,CAAC,SAAS,EAAE,CAAC;YAChD,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;QACvB,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,OAAO,CAAC,KAAK,CACX,4DAA4D,UAAU,IAAI,EAC1E,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CACzC,CAAC;YACF,SAAS;QACX,CAAC;QAED,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;YACzB,MAAM,GAAG,GAAG,GAAG,UAAU,KAAK,IAAI,CAAC,IAAI,EAAE,CAAC;YAC1C,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;YAElC,IAAI,KAAK,KAAK,SAAS,EAAE,CAAC;gBACxB,uDAAuD;gBACvD,MAAM,cAAc,GAAG,GAAG,UAAU,IAAI,IAAI,CAAC,IAAI,EAAE,CAAC;gBACpD,UAAU,CAAC,IAAI,CAAC;oBACd,QAAQ,EAAE,IAAI,CAAC,IAAI;oBACnB,aAAa,EAAE,KAAK;oBACpB,cAAc,EAAE,UAAU;oBAC1B,cAAc;iBACf,CAAC,CAAC;gBACH,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;gBAEjC,sDAAsD;gBACtD,OAAO,CAAC,KAAK,CACX,oDAAoD,IAAI,CAAC,IAAI,kBAAkB;oBAC7E,SAAS,KAAK,yCAAyC,UAAU,IAAI;oBACrE,qCAAqC,cAAc,IAAI,CAC1D,CAAC;YACJ,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC;gBAChC,OAAO,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,IAAI,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,UAAU,EAAE,OAAO,EAAE,CAAC;AACjC,CAAC"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import path from 'node:path';
|
|
2
2
|
import { InvocationStatus } from '../../types/index.js';
|
|
3
|
-
import {
|
|
3
|
+
import { loadPolicyAsync } from '../../config/policy-loader.js';
|
|
4
4
|
/**
|
|
5
5
|
* Pre-execution middleware: denies tool invocations whose arguments
|
|
6
6
|
* reference paths that are in the policy's blocked_paths list.
|
|
@@ -16,7 +16,7 @@ export function createBlockedPathsMiddleware(initialPolicy, baseDir) {
|
|
|
16
16
|
let blockedPaths = initialPolicy.blocked_paths;
|
|
17
17
|
if (baseDir) {
|
|
18
18
|
try {
|
|
19
|
-
const policy =
|
|
19
|
+
const policy = await loadPolicyAsync(baseDir);
|
|
20
20
|
blockedPaths = policy.blocked_paths;
|
|
21
21
|
}
|
|
22
22
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blocked-paths.js","sourceRoot":"","sources":["../../../src/gateway/middleware/blocked-paths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"blocked-paths.js","sourceRoot":"","sources":["../../../src/gateway/middleware/blocked-paths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AACxD,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAIhE;;;;;;;;GAQG;AACH,MAAM,UAAU,4BAA4B,CAAC,aAAqB,EAAE,OAAgB;IAClF,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,oEAAoE;QACpE,IAAI,YAAY,GAAG,aAAa,CAAC,aAAa,CAAC;QAC/C,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;gBAC9C,YAAY,GAAG,MAAM,CAAC,aAAa,CAAC;YACtC,CAAC;YAAC,MAAM,CAAC;gBACP,8DAA8D;YAChE,CAAC;QACH,CAAC;QAED,gEAAgE;QAChE,MAAM,KAAK,GAAG,CAAC,GAAG,IAAI,GAAG,CAAC,CAAC,GAAG,YAAY,EAAE,WAAW,CAAC,CAAC,CAAC,CAAC;QAE3D,uDAAuD;QACvD,MAAM,YAAY,GAAG,mBAAmB,CAAC,GAAG,CAAC,SAAS,CAAC,CAAC;QAExD,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,YAAY,EAAE,CAAC;YACxC,KAAK,MAAM,OAAO,IAAI,KAAK,EAAE,CAAC;gBAC5B,IAAI,mBAAmB,CAAC,KAAK,EAAE,OAAO,CAAC,EAAE,CAAC;oBACxC,GAAG,CAAC,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC;oBACrC,GAAG,CAAC,KAAK,GAAG,aAAa,GAAG,8BAA8B,OAAO,YAAY,GAAG,CAAC,SAAS,EAAE,CAAC;oBAC7F,OAAO;gBACT,CAAC;YACH,CAAC;QACH,CAAC;QAED,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC;AAED;;;GAGG;AACH,SAAS,mBAAmB,CAC1B,GAAY,EACZ,MAAM,GAAG,EAAE,EACX,IAAI,GAAG,IAAI,OAAO,EAAE;IAEpB,MAAM,OAAO,GAA4B,EAAE,CAAC;IAE5C,IAAI,GAAG,KAAK,IAAI,IAAI,GAAG,KAAK,SAAS;QAAE,OAAO,OAAO,CAAC;IACtD,IAAI,OAAO,GAAG,KAAK,QAAQ,EAAE,CAAC;QAC5B,OAAO,CAAC,IAAI,CAAC,CAAC,MAAM,IAAI,OAAO,EAAE,GAAG,CAAC,CAAC,CAAC;QACvC,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,IAAI,OAAO,GAAG,KAAK,QAAQ;QAAE,OAAO,OAAO,CAAC;IAE5C,2BAA2B;IAC3B,MAAM,MAAM,GAAG,GAAa,CAAC;IAC7B,IAAI,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC;QAAE,OAAO,OAAO,CAAC;IACrC,IAAI,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;IAEjB,IAAI,KAAK,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;QACvB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,GAAG,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;YACpC,OAAO,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,MAAM,IAAI,CAAC,GAAG,EAAE,IAAI,CAAC,CAAC,CAAC;QACxE,CAAC;IACH,CAAC;SAAM,CAAC;QACN,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,CAAC;YAC/C,MAAM,OAAO,GAAG,MAAM,CAAC,CAAC,CAAC,GAAG,MAAM,IAAI,GAAG,EAAE,CAAC,CAAC,CAAC,GAAG,CAAC;YAClD,OAAO,CAAC,IAAI,CAAC,GAAG,mBAAmB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,CAAC,CAAC,CAAC;QAC7D,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;;;GAMG;AACH,SAAS,mBAAmB,CAAC,KAAa,EAAE,WAAmB;IAC7D,gFAAgF;IAChF,MAAM,UAAU,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;IACxC,MAAM,iBAAiB,GAAG,WAAW,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC;IAExE,8CAA8C;IAC9C,IAAI,UAAU,CAAC,QAAQ,CAAC,iBAAiB,CAAC;QAAE,OAAO,IAAI,CAAC;IAExD,6DAA6D;IAC7D,MAAM,QAAQ,GAAG,iBAAiB,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;IAC1D,IAAI,QAAQ,IAAI,UAAU,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IAE3D,OAAO,KAAK,CAAC;AACf,CAAC;AAED;;;;;;;GAOG;AACH,SAAS,aAAa,CAAC,KAAa;IAClC,IAAI,OAAO,GAAG,KAAK,CAAC;IAEpB,oEAAoE;IACpE,IAAI,CAAC;QACH,OAAO,GAAG,kBAAkB,CAAC,KAAK,CAAC,CAAC;IACtC,CAAC;IAAC,MAAM,CAAC;QACP,2EAA2E;IAC7E,CAAC;IAED,2CAA2C;IAC3C,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAEtC,6EAA6E;IAC7E,OAAO,GAAG,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;IAEtD,4CAA4C;IAC5C,OAAO,OAAO,CAAC,WAAW,EAAE,CAAC;AAC/B,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { CircuitBreaker } from '../circuit-breaker.js';
|
|
2
|
+
import type { Middleware } from './chain.js';
|
|
3
|
+
/**
|
|
4
|
+
* PreToolUse + PostToolUse middleware: wraps downstream calls with a circuit breaker.
|
|
5
|
+
*
|
|
6
|
+
* On entry: if the circuit is open, denies the call immediately and returns the
|
|
7
|
+
* structured error without hitting the downstream server.
|
|
8
|
+
*
|
|
9
|
+
* On exit: records success or failure so the breaker can track state transitions.
|
|
10
|
+
*/
|
|
11
|
+
export declare function createCircuitBreakerMiddleware(breaker: CircuitBreaker): Middleware;
|
|
12
|
+
//# sourceMappingURL=circuit-breaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../../src/gateway/middleware/circuit-breaker.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;GAOG;AACH,wBAAgB,8BAA8B,CAAC,OAAO,EAAE,cAAc,GAAG,UAAU,CAkClF"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { InvocationStatus } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* PreToolUse + PostToolUse middleware: wraps downstream calls with a circuit breaker.
|
|
4
|
+
*
|
|
5
|
+
* On entry: if the circuit is open, denies the call immediately and returns the
|
|
6
|
+
* structured error without hitting the downstream server.
|
|
7
|
+
*
|
|
8
|
+
* On exit: records success or failure so the breaker can track state transitions.
|
|
9
|
+
*/
|
|
10
|
+
export function createCircuitBreakerMiddleware(breaker) {
|
|
11
|
+
return async (ctx, next) => {
|
|
12
|
+
const status = breaker.isAllowed(ctx.server_name);
|
|
13
|
+
if (status !== null) {
|
|
14
|
+
// Circuit is open — fail fast
|
|
15
|
+
ctx.status = InvocationStatus.Denied;
|
|
16
|
+
ctx.error =
|
|
17
|
+
`Circuit breaker open for server "${status.serverName}": downstream is unavailable. ` +
|
|
18
|
+
`State: ${status.state}. Will retry at ${status.retryAt ?? 'unknown'}.`;
|
|
19
|
+
ctx.metadata.circuit_breaker = {
|
|
20
|
+
state: status.state,
|
|
21
|
+
serverName: status.serverName,
|
|
22
|
+
retryAt: status.retryAt,
|
|
23
|
+
};
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
await next();
|
|
28
|
+
// A Denied status from the middleware chain (policy, rate-limit, etc.) is not a
|
|
29
|
+
// downstream failure — only Error status means the server itself failed.
|
|
30
|
+
if (ctx.status === InvocationStatus.Error) {
|
|
31
|
+
breaker.recordFailure(ctx.server_name);
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
breaker.recordSuccess(ctx.server_name);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
catch (err) {
|
|
38
|
+
// Unhandled exceptions from inner middlewares also count as failures
|
|
39
|
+
breaker.recordFailure(ctx.server_name);
|
|
40
|
+
throw err;
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
//# sourceMappingURL=circuit-breaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../../src/gateway/middleware/circuit-breaker.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAIxD;;;;;;;GAOG;AACH,MAAM,UAAU,8BAA8B,CAAC,OAAuB;IACpE,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,MAAM,MAAM,GAAG,OAAO,CAAC,SAAS,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAElD,IAAI,MAAM,KAAK,IAAI,EAAE,CAAC;YACpB,8BAA8B;YAC9B,GAAG,CAAC,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC;YACrC,GAAG,CAAC,KAAK;gBACP,oCAAoC,MAAM,CAAC,UAAU,gCAAgC;oBACrF,UAAU,MAAM,CAAC,KAAK,mBAAmB,MAAM,CAAC,OAAO,IAAI,SAAS,GAAG,CAAC;YAC1E,GAAG,CAAC,QAAQ,CAAC,eAAe,GAAG;gBAC7B,KAAK,EAAE,MAAM,CAAC,KAAK;gBACnB,UAAU,EAAE,MAAM,CAAC,UAAU;gBAC7B,OAAO,EAAE,MAAM,CAAC,OAAO;aACxB,CAAC;YACF,OAAO;QACT,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,EAAE,CAAC;YAEb,gFAAgF;YAChF,yEAAyE;YACzE,IAAI,GAAG,CAAC,MAAM,KAAK,gBAAgB,CAAC,KAAK,EAAE,CAAC;gBAC1C,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACzC,CAAC;iBAAM,CAAC;gBACN,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACzC,CAAC;QACH,CAAC;QAAC,OAAO,GAAG,EAAE,CAAC;YACb,qEAAqE;YACrE,OAAO,CAAC,aAAa,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;YACvC,MAAM,GAAG,CAAC;QACZ,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { Middleware } from './chain.js';
|
|
2
|
+
/**
|
|
3
|
+
* Scan a string for known prompt injection phrases.
|
|
4
|
+
* Also decodes base64 tokens and checks the decoded content.
|
|
5
|
+
* Returns an array of matched phrase descriptions, empty if clean.
|
|
6
|
+
*/
|
|
7
|
+
export declare function scanForInjection(input: string): string[];
|
|
8
|
+
export type InjectionAction = 'block' | 'warn';
|
|
9
|
+
/**
|
|
10
|
+
* PostToolUse middleware: scans tool results for prompt injection patterns.
|
|
11
|
+
*
|
|
12
|
+
* Operates on tool output (ctx.result) returned from downstream MCP servers.
|
|
13
|
+
* On detection:
|
|
14
|
+
* - Always logs to audit metadata and emits a warning to stderr.
|
|
15
|
+
* - If action is 'block' (default), sets ctx.status to Denied and blocks the result.
|
|
16
|
+
* - If action is 'warn', allows the result through with a warning only.
|
|
17
|
+
*
|
|
18
|
+
* SECURITY: Checking PostToolUse (after downstream execution, before the result
|
|
19
|
+
* reaches the LLM) is the correct place to catch injection in tool descriptions
|
|
20
|
+
* and resource content coming from potentially untrusted downstream servers.
|
|
21
|
+
*/
|
|
22
|
+
export declare function createInjectionMiddleware(action?: InjectionAction): Middleware;
|
|
23
|
+
//# sourceMappingURL=injection.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injection.d.ts","sourceRoot":"","sources":["../../../src/gateway/middleware/injection.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAoC7C;;;;GAIG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,EAAE,CA4BxD;AAwBD,MAAM,MAAM,eAAe,GAAG,OAAO,GAAG,MAAM,CAAC;AAE/C;;;;;;;;;;;;GAYG;AACH,wBAAgB,yBAAyB,CAAC,MAAM,GAAE,eAAyB,GAAG,UAAU,CAmCvF"}
|