@bookedsolid/reagent 0.7.1 → 0.10.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/motion-designer-interactive.md +5 -4
- package/agents/engineering/pr-voice-reviewer.md +229 -0
- package/agents/engineering/qa-engineer-automation.md +5 -4
- package/agents/engineering/qa-engineer-manual.md +5 -4
- package/agents/engineering/qa-lead.md +5 -4
- package/agents/engineering/security-engineer-appsec.md +5 -4
- package/agents/engineering/security-engineer-compliance.md +5 -4
- package/agents/engineering/security-qa-engineer.md +5 -4
- package/agents/engineering/technical-writer.md +5 -4
- 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/index.d.ts +5 -0
- package/dist/cli/commands/daemon/index.d.ts.map +1 -0
- package/dist/cli/commands/daemon/index.js +59 -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 +12 -4
- 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 +10 -2
- package/profiles/bst-internal.json +12 -2
- package/profiles/client-engagement.json +12 -2
|
@@ -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"}
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
import { InvocationStatus } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Known prompt injection phrases (lowercase for case-insensitive matching).
|
|
4
|
+
* These patterns are commonly used to override system instructions in tool
|
|
5
|
+
* descriptions or resource content returned by downstream MCP servers.
|
|
6
|
+
*/
|
|
7
|
+
const INJECTION_PHRASES = [
|
|
8
|
+
'ignore previous instructions',
|
|
9
|
+
'disregard your',
|
|
10
|
+
'your new instructions are',
|
|
11
|
+
'system prompt override',
|
|
12
|
+
'forget all previous',
|
|
13
|
+
// 'you are now' is too broad — fires on "you are now connected", "you are now in /home/foo", etc.
|
|
14
|
+
// The role-reassignment vector is "you are now a [different persona]" — the trailing space+article
|
|
15
|
+
// is what distinguishes injection from ordinary status messages.
|
|
16
|
+
'you are now a ',
|
|
17
|
+
'you are now an ',
|
|
18
|
+
];
|
|
19
|
+
/**
|
|
20
|
+
* Decode a base64 string, returning the decoded text or null if decoding fails.
|
|
21
|
+
* Only decodes if the input looks like base64 (64-char alphabet, length divisible by 4 or padded).
|
|
22
|
+
*/
|
|
23
|
+
function tryDecodeBase64(input) {
|
|
24
|
+
// Quick heuristic: must be at least 20 chars and use only base64 chars
|
|
25
|
+
if (input.length < 20)
|
|
26
|
+
return null;
|
|
27
|
+
if (!/^[A-Za-z0-9+/]+=*$/.test(input))
|
|
28
|
+
return null;
|
|
29
|
+
try {
|
|
30
|
+
return Buffer.from(input, 'base64').toString('utf8');
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Scan a string for known prompt injection phrases.
|
|
38
|
+
* Also decodes base64 tokens and checks the decoded content.
|
|
39
|
+
* Returns an array of matched phrase descriptions, empty if clean.
|
|
40
|
+
*/
|
|
41
|
+
export function scanForInjection(input) {
|
|
42
|
+
if (!input || typeof input !== 'string')
|
|
43
|
+
return [];
|
|
44
|
+
const lower = input.toLowerCase();
|
|
45
|
+
const matches = [];
|
|
46
|
+
// Check literal phrases
|
|
47
|
+
for (const phrase of INJECTION_PHRASES) {
|
|
48
|
+
if (lower.includes(phrase)) {
|
|
49
|
+
matches.push(`literal: "${phrase}"`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Check base64-encoded variants — scan word-like tokens that look like base64
|
|
53
|
+
const base64Tokens = input.match(/[A-Za-z0-9+/]{20,}={0,2}/g) ?? [];
|
|
54
|
+
for (const token of base64Tokens) {
|
|
55
|
+
const decoded = tryDecodeBase64(token);
|
|
56
|
+
if (!decoded)
|
|
57
|
+
continue;
|
|
58
|
+
const decodedLower = decoded.toLowerCase();
|
|
59
|
+
for (const phrase of INJECTION_PHRASES) {
|
|
60
|
+
if (decodedLower.includes(phrase)) {
|
|
61
|
+
matches.push(`base64-encoded: "${phrase}"`);
|
|
62
|
+
break; // One report per token is enough
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return matches;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Scan an unknown value recursively, collecting all injection matches.
|
|
70
|
+
* Walks strings, arrays, and plain objects.
|
|
71
|
+
*/
|
|
72
|
+
function scanValue(value, matches) {
|
|
73
|
+
if (typeof value === 'string') {
|
|
74
|
+
matches.push(...scanForInjection(value));
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
if (Array.isArray(value)) {
|
|
78
|
+
for (const item of value) {
|
|
79
|
+
scanValue(item, matches);
|
|
80
|
+
}
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (value !== null && typeof value === 'object') {
|
|
84
|
+
for (const v of Object.values(value)) {
|
|
85
|
+
scanValue(v, matches);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* PostToolUse middleware: scans tool results for prompt injection patterns.
|
|
91
|
+
*
|
|
92
|
+
* Operates on tool output (ctx.result) returned from downstream MCP servers.
|
|
93
|
+
* On detection:
|
|
94
|
+
* - Always logs to audit metadata and emits a warning to stderr.
|
|
95
|
+
* - If action is 'block' (default), sets ctx.status to Denied and blocks the result.
|
|
96
|
+
* - If action is 'warn', allows the result through with a warning only.
|
|
97
|
+
*
|
|
98
|
+
* SECURITY: Checking PostToolUse (after downstream execution, before the result
|
|
99
|
+
* reaches the LLM) is the correct place to catch injection in tool descriptions
|
|
100
|
+
* and resource content coming from potentially untrusted downstream servers.
|
|
101
|
+
*/
|
|
102
|
+
export function createInjectionMiddleware(action = 'block') {
|
|
103
|
+
return async (ctx, next) => {
|
|
104
|
+
await next();
|
|
105
|
+
// Only scan if we have a result to inspect
|
|
106
|
+
if (ctx.result == null)
|
|
107
|
+
return;
|
|
108
|
+
const matches = [];
|
|
109
|
+
scanValue(ctx.result, matches);
|
|
110
|
+
if (matches.length === 0)
|
|
111
|
+
return;
|
|
112
|
+
// Deduplicate matches
|
|
113
|
+
const unique = [...new Set(matches)];
|
|
114
|
+
// Always log to audit metadata
|
|
115
|
+
ctx.metadata.injection_matches = unique;
|
|
116
|
+
// Always emit warning to stderr
|
|
117
|
+
process.stderr.write(`[reagent] INJECTION-GUARD: Prompt injection pattern detected in tool "${ctx.tool_name}" result\n`);
|
|
118
|
+
for (const match of unique) {
|
|
119
|
+
process.stderr.write(` Pattern: ${match}\n`);
|
|
120
|
+
}
|
|
121
|
+
process.stderr.write(` Action: ${action} — review the downstream server "${ctx.server_name}" for compromise.\n`);
|
|
122
|
+
if (action === 'block') {
|
|
123
|
+
ctx.status = InvocationStatus.Denied;
|
|
124
|
+
ctx.error = `Prompt injection detected in tool result (${unique.length} pattern(s) matched). Result blocked.`;
|
|
125
|
+
ctx.result = undefined;
|
|
126
|
+
}
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
//# sourceMappingURL=injection.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injection.js","sourceRoot":"","sources":["../../../src/gateway/middleware/injection.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AutonomyLevel, InvocationStatus, Tier } from '../../types/index.js';
|
|
2
2
|
import { classifyTool, isToolBlocked } from '../../config/tier-map.js';
|
|
3
|
-
import {
|
|
3
|
+
import { loadPolicyAsync } from '../../config/policy-loader.js';
|
|
4
4
|
/**
|
|
5
5
|
* Autonomy level tier permissions:
|
|
6
6
|
* - L0: Read only
|
|
@@ -33,7 +33,7 @@ export function createPolicyMiddleware(initialPolicy, gatewayConfig, baseDir) {
|
|
|
33
33
|
let policy = lastGoodPolicy;
|
|
34
34
|
if (baseDir) {
|
|
35
35
|
try {
|
|
36
|
-
policy =
|
|
36
|
+
policy = await loadPolicyAsync(baseDir);
|
|
37
37
|
lastGoodPolicy = policy; // Cache successful parse
|
|
38
38
|
}
|
|
39
39
|
catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/gateway/middleware/policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"policy.js","sourceRoot":"","sources":["../../../src/gateway/middleware/policy.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,gBAAgB,EAAE,IAAI,EAAE,MAAM,sBAAsB,CAAC;AAC7E,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAC;AACvE,OAAO,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AAIhE;;;;;;GAMG;AACH,MAAM,YAAY,GAAqC;IACrD,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACxC,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC;IACpD,CAAC,aAAa,CAAC,EAAE,CAAC,EAAE,IAAI,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,WAAW,CAAC,CAAC;CACvE,CAAC;AAEF;;;;;;;GAOG;AACH,MAAM,UAAU,sBAAsB,CACpC,aAAqB,EACrB,aAA6B,EAC7B,OAAgB;IAEhB,gEAAgE;IAChE,6EAA6E;IAC7E,+DAA+D;IAC/D,IAAI,cAAc,GAAG,aAAa,CAAC;IAEnC,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,yEAAyE;QACzE,iEAAiE;QACjE,IAAI,MAAM,GAAG,cAAc,CAAC;QAC5B,IAAI,OAAO,EAAE,CAAC;YACZ,IAAI,CAAC;gBACH,MAAM,GAAG,MAAM,eAAe,CAAC,OAAO,CAAC,CAAC;gBACxC,cAAc,GAAG,MAAM,CAAC,CAAC,yBAAyB;YACpD,CAAC;YAAC,MAAM,CAAC;gBACP,kEAAkE;YACpE,CAAC;QACH,CAAC;QAED,sCAAsC;QACtC,IAAI,aAAa,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,WAAW,EAAE,aAAa,CAAC,EAAE,CAAC;YACjE,GAAG,CAAC,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC;YACrC,GAAG,CAAC,KAAK,GAAG,SAAS,GAAG,CAAC,SAAS,2CAA2C,CAAC;YAC9E,OAAO;QACT,CAAC;QAED,yFAAyF;QACzF,qFAAqF;QACrF,MAAM,IAAI,GAAG,YAAY,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,WAAW,EAAE,aAAa,CAAC,CAAC;QACzE,GAAG,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC,8CAA8C;QAE/D,mCAAmC;QACnC,MAAM,OAAO,GAAG,YAAY,CAAC,MAAM,CAAC,cAAc,CAAC,CAAC;QACpD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,GAAG,CAAC,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC;YACrC,GAAG,CAAC,KAAK,GAAG,2BAA2B,MAAM,CAAC,cAAc,uBAAuB,CAAC;YACpF,OAAO;QACT,CAAC;QAED,mEAAmE;QACnE,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC;YACvB,GAAG,CAAC,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC;YACrC,GAAG,CAAC,KAAK,GAAG,kBAAkB,MAAM,CAAC,cAAc,mBAAmB,IAAI,sBAAsB,GAAG,CAAC,SAAS,EAAE,CAAC;YAChH,OAAO;QACT,CAAC;QAED,gEAAgE;QAChE,GAAG,CAAC,QAAQ,CAAC,cAAc,GAAG,MAAM,CAAC,cAAc,CAAC;QAEpD,MAAM,IAAI,EAAE,CAAC;IACf,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { RateLimiter } from '../rate-limiter.js';
|
|
2
|
+
import type { Middleware } from './chain.js';
|
|
3
|
+
/**
|
|
4
|
+
* PreToolUse + PostToolUse middleware: enforces per-server concurrency and rate limits.
|
|
5
|
+
*
|
|
6
|
+
* On entry: tries to acquire a slot. If over limit, denies the call immediately
|
|
7
|
+
* without ever reaching the downstream server.
|
|
8
|
+
*
|
|
9
|
+
* On exit (finally): releases the concurrency slot so the next queued call can proceed.
|
|
10
|
+
* The slot is released even if the downstream call errors — we track capacity, not success.
|
|
11
|
+
*/
|
|
12
|
+
export declare function createRateLimitMiddleware(limiter: RateLimiter): Middleware;
|
|
13
|
+
//# sourceMappingURL=rate-limit.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.d.ts","sourceRoot":"","sources":["../../../src/gateway/middleware/rate-limit.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAC;AACtD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAE7C;;;;;;;;GAQG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAqB1E"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { InvocationStatus } from '../../types/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* PreToolUse + PostToolUse middleware: enforces per-server concurrency and rate limits.
|
|
4
|
+
*
|
|
5
|
+
* On entry: tries to acquire a slot. If over limit, denies the call immediately
|
|
6
|
+
* without ever reaching the downstream server.
|
|
7
|
+
*
|
|
8
|
+
* On exit (finally): releases the concurrency slot so the next queued call can proceed.
|
|
9
|
+
* The slot is released even if the downstream call errors — we track capacity, not success.
|
|
10
|
+
*/
|
|
11
|
+
export function createRateLimitMiddleware(limiter) {
|
|
12
|
+
return async (ctx, next) => {
|
|
13
|
+
const err = limiter.tryAcquire(ctx.server_name);
|
|
14
|
+
if (err !== null) {
|
|
15
|
+
ctx.status = InvocationStatus.Denied;
|
|
16
|
+
ctx.error = err.message;
|
|
17
|
+
ctx.metadata.rate_limit_exceeded = {
|
|
18
|
+
type: err.type,
|
|
19
|
+
current: err.current,
|
|
20
|
+
limit: err.limit,
|
|
21
|
+
};
|
|
22
|
+
return; // Do not call next() — gate is closed
|
|
23
|
+
}
|
|
24
|
+
try {
|
|
25
|
+
await next();
|
|
26
|
+
}
|
|
27
|
+
finally {
|
|
28
|
+
limiter.release(ctx.server_name);
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=rate-limit.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limit.js","sourceRoot":"","sources":["../../../src/gateway/middleware/rate-limit.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAIxD;;;;;;;;GAQG;AACH,MAAM,UAAU,yBAAyB,CAAC,OAAoB;IAC5D,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,MAAM,GAAG,GAAG,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QAEhD,IAAI,GAAG,KAAK,IAAI,EAAE,CAAC;YACjB,GAAG,CAAC,MAAM,GAAG,gBAAgB,CAAC,MAAM,CAAC;YACrC,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,OAAO,CAAC;YACxB,GAAG,CAAC,QAAQ,CAAC,mBAAmB,GAAG;gBACjC,IAAI,EAAE,GAAG,CAAC,IAAI;gBACd,OAAO,EAAE,GAAG,CAAC,OAAO;gBACpB,KAAK,EAAE,GAAG,CAAC,KAAK;aACjB,CAAC;YACF,OAAO,CAAC,sCAAsC;QAChD,CAAC;QAED,IAAI,CAAC;YACH,MAAM,IAAI,EAAE,CAAC;QACf,CAAC;gBAAS,CAAC;YACT,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,WAAW,CAAC,CAAC;QACnC,CAAC;IACH,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../../src/gateway/middleware/redact.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;
|
|
1
|
+
{"version":3,"file":"redact.d.ts","sourceRoot":"","sources":["../../../src/gateway/middleware/redact.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAyC7C;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,EAAE,CAAA;CAAE,CAenF;AAED;;;;;;GAMG;AACH,eAAO,MAAM,gBAAgB,EAAE,UA6B9B,CAAC"}
|
|
@@ -21,6 +21,13 @@ const SECRET_PATTERNS = [
|
|
|
21
21
|
{ name: 'Discord Token', pattern: /[MN][A-Za-z\d]{23,}\.[\w-]{6}\.[\w-]{27,}/g },
|
|
22
22
|
// Base64-encoded AWS access key (AKIA... in base64 starts with QUTJQ)
|
|
23
23
|
{ name: 'Base64 AWS Key', pattern: /QUtJQ[A-Za-z0-9+/]{17,}={0,2}/g },
|
|
24
|
+
// Anthropic API keys (sk-ant-api03-... and similar)
|
|
25
|
+
{ name: 'Anthropic API Key', pattern: /sk-ant-[a-zA-Z0-9\-_]{32,}/g },
|
|
26
|
+
// OpenAI API keys — project keys (sk-proj-...) and legacy (sk-...)
|
|
27
|
+
{ name: 'OpenAI Project Key', pattern: /sk-proj-[a-zA-Z0-9\-_]{32,}/g },
|
|
28
|
+
{ name: 'OpenAI API Key', pattern: /sk-[a-zA-Z0-9]{32,}/g },
|
|
29
|
+
// Hugging Face access tokens
|
|
30
|
+
{ name: 'Hugging Face Token', pattern: /hf_[a-zA-Z0-9]{32,}/g },
|
|
24
31
|
];
|
|
25
32
|
/**
|
|
26
33
|
* Strip null bytes and other control characters that could break regex matching.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"redact.js","sourceRoot":"","sources":["../../../src/gateway/middleware/redact.ts"],"names":[],"mappings":"AAEA;;;;;;GAMG;AACH,MAAM,eAAe,GAA6C;IAChE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,oBAAoB,EAAE;IACzD;QACE,IAAI,EAAE,gBAAgB;QACtB,OAAO,EAAE,oEAAoE;KAC9E;IACD,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,8BAA8B,EAAE;IACjE;QACE,IAAI,EAAE,iBAAiB;QACvB,OAAO,EAAE,kEAAkE;KAC5E;IACD,EAAE,IAAI,EAAE,cAAc,EAAE,OAAO,EAAE,kCAAkC,EAAE;IACrE,EAAE,IAAI,EAAE,aAAa,EAAE,OAAO,EAAE,2DAA2D,EAAE;IAC7F,EAAE,IAAI,EAAE,eAAe,EAAE,OAAO,EAAE,4CAA4C,EAAE;IAChF,sEAAsE;IACtE,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,gCAAgC,EAAE;
|
|
1
|
+
{"version":3,"file":"redact.js","sourceRoot":"","sources":["../../../src/gateway/middleware/redact.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { GatewayConfig } from '../../types/index.js';
|
|
2
|
+
import type { Middleware } from './chain.js';
|
|
3
|
+
/**
|
|
4
|
+
* PostToolUse middleware: truncates tool results that exceed a configurable size cap.
|
|
5
|
+
*
|
|
6
|
+
* Operates on the serialized form of the result — if the downstream tool returns
|
|
7
|
+
* a large object or binary-encoded string, we measure and truncate the serialized
|
|
8
|
+
* representation. A human-readable notice is appended so the agent knows the
|
|
9
|
+
* result was cut.
|
|
10
|
+
*
|
|
11
|
+
* Default cap: 512 KB. Override via `gateway.max_result_size_kb` in gateway.yaml.
|
|
12
|
+
*/
|
|
13
|
+
export declare function createResultSizeCapMiddleware(gatewayConfig?: GatewayConfig): Middleware;
|
|
14
|
+
//# sourceMappingURL=result-size-cap.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-size-cap.d.ts","sourceRoot":"","sources":["../../../src/gateway/middleware/result-size-cap.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAC1D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAiB7C;;;;;;;;;GASG;AACH,wBAAgB,6BAA6B,CAAC,aAAa,CAAC,EAAE,aAAa,GAAG,UAAU,CA+BvF"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
const DEFAULT_CAP_KB = 512;
|
|
2
|
+
/**
|
|
3
|
+
* Serialize any result to a string for size measurement.
|
|
4
|
+
* If it's already a string, use it directly; otherwise JSON.stringify.
|
|
5
|
+
*/
|
|
6
|
+
function resultToString(value) {
|
|
7
|
+
if (typeof value === 'string')
|
|
8
|
+
return value;
|
|
9
|
+
try {
|
|
10
|
+
return JSON.stringify(value);
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return String(value);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* PostToolUse middleware: truncates tool results that exceed a configurable size cap.
|
|
18
|
+
*
|
|
19
|
+
* Operates on the serialized form of the result — if the downstream tool returns
|
|
20
|
+
* a large object or binary-encoded string, we measure and truncate the serialized
|
|
21
|
+
* representation. A human-readable notice is appended so the agent knows the
|
|
22
|
+
* result was cut.
|
|
23
|
+
*
|
|
24
|
+
* Default cap: 512 KB. Override via `gateway.max_result_size_kb` in gateway.yaml.
|
|
25
|
+
*/
|
|
26
|
+
export function createResultSizeCapMiddleware(gatewayConfig) {
|
|
27
|
+
const capKb = gatewayConfig?.gateway?.max_result_size_kb ?? DEFAULT_CAP_KB;
|
|
28
|
+
const capBytes = capKb * 1024;
|
|
29
|
+
return async (ctx, next) => {
|
|
30
|
+
await next();
|
|
31
|
+
if (ctx.result == null)
|
|
32
|
+
return;
|
|
33
|
+
const serialized = resultToString(ctx.result);
|
|
34
|
+
const byteLength = Buffer.byteLength(serialized, 'utf8');
|
|
35
|
+
if (byteLength <= capBytes)
|
|
36
|
+
return;
|
|
37
|
+
const removedBytes = byteLength - capBytes;
|
|
38
|
+
const removedKb = Math.ceil(removedBytes / 1024);
|
|
39
|
+
const notice = `\n[TRUNCATED: result exceeded ${capKb}KB limit. ${removedKb}KB removed.]`;
|
|
40
|
+
// Truncate to cap, then append notice. We truncate the serialized string and
|
|
41
|
+
// store it back — the agent sees the notice and knows context was lost.
|
|
42
|
+
const noticeBytes = Buffer.byteLength(notice, 'utf8');
|
|
43
|
+
const keepBytes = capBytes - noticeBytes;
|
|
44
|
+
const truncated = Buffer.from(serialized, 'utf8').subarray(0, keepBytes).toString('utf8') + notice;
|
|
45
|
+
ctx.result = truncated;
|
|
46
|
+
console.error(`[reagent] result-size-cap: truncated tool "${ctx.tool_name}" result from ${Math.ceil(byteLength / 1024)}KB to ${capKb}KB`);
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=result-size-cap.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"result-size-cap.js","sourceRoot":"","sources":["../../../src/gateway/middleware/result-size-cap.ts"],"names":[],"mappings":"AAGA,MAAM,cAAc,GAAG,GAAG,CAAC;AAE3B;;;GAGG;AACH,SAAS,cAAc,CAAC,KAAc;IACpC,IAAI,OAAO,KAAK,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;IAC/B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,MAAM,CAAC,KAAK,CAAC,CAAC;IACvB,CAAC;AACH,CAAC;AAED;;;;;;;;;GASG;AACH,MAAM,UAAU,6BAA6B,CAAC,aAA6B;IACzE,MAAM,KAAK,GAAG,aAAa,EAAE,OAAO,EAAE,kBAAkB,IAAI,cAAc,CAAC;IAC3E,MAAM,QAAQ,GAAG,KAAK,GAAG,IAAI,CAAC;IAE9B,OAAO,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,EAAE;QACzB,MAAM,IAAI,EAAE,CAAC;QAEb,IAAI,GAAG,CAAC,MAAM,IAAI,IAAI;YAAE,OAAO;QAE/B,MAAM,UAAU,GAAG,cAAc,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC;QAC9C,MAAM,UAAU,GAAG,MAAM,CAAC,UAAU,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC;QAEzD,IAAI,UAAU,IAAI,QAAQ;YAAE,OAAO;QAEnC,MAAM,YAAY,GAAG,UAAU,GAAG,QAAQ,CAAC;QAC3C,MAAM,SAAS,GAAG,IAAI,CAAC,IAAI,CAAC,YAAY,GAAG,IAAI,CAAC,CAAC;QACjD,MAAM,MAAM,GAAG,iCAAiC,KAAK,aAAa,SAAS,cAAc,CAAC;QAE1F,6EAA6E;QAC7E,wEAAwE;QACxE,MAAM,WAAW,GAAG,MAAM,CAAC,UAAU,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACtD,MAAM,SAAS,GAAG,QAAQ,GAAG,WAAW,CAAC;QACzC,MAAM,SAAS,GACb,MAAM,CAAC,IAAI,CAAC,UAAU,EAAE,MAAM,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,SAAS,CAAC,CAAC,QAAQ,CAAC,MAAM,CAAC,GAAG,MAAM,CAAC;QAEnF,GAAG,CAAC,MAAM,GAAG,SAAS,CAAC;QAEvB,OAAO,CAAC,KAAK,CACX,8CAA8C,GAAG,CAAC,SAAS,iBAAiB,IAAI,CAAC,IAAI,CAAC,UAAU,GAAG,IAAI,CAAC,SAAS,KAAK,IAAI,CAC3H,CAAC;IACJ,CAAC,CAAC;AACJ,CAAC"}
|
|
@@ -19,7 +19,7 @@ export function registerNativeTools(gateway, baseDir, middlewares) {
|
|
|
19
19
|
tool_name: toolName,
|
|
20
20
|
server_name: 'reagent',
|
|
21
21
|
arguments: params,
|
|
22
|
-
session_id:
|
|
22
|
+
session_id: crypto.randomUUID(),
|
|
23
23
|
status: InvocationStatus.Allowed,
|
|
24
24
|
start_time: Date.now(),
|
|
25
25
|
metadata: {},
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-tools.js","sourceRoot":"","sources":["../../src/gateway/native-tools.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,SAAS,EAAE,MAAM,qBAAqB,CAAC;AAChD,OAAO,EAAE,YAAY,EAAE,MAAM,wBAAwB,CAAC;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,2BAA2B,CAAC;AAE5D,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AACrD,OAAO,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAErD;;;GAGG;AACH,MAAM,UAAU,mBAAmB,CACjC,OAAkB,EAClB,OAAe,EACf,WAAyB;IAEzB,MAAM,KAAK,GAAG,IAAI,SAAS,CAAC,OAAO,CAAC,CAAC;IACrC,MAAM,MAAM,GAAG,IAAI,YAAY,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC;IAC7C,MAAM,OAAO,GAAG,IAAI,eAAe,CAAC,OAAO,CAAC,CAAC;IAC7C,IAAI,KAAK,GAAG,CAAC,CAAC;IAEd,SAAS,WAAW,CAClB,QAAgB,EAChB,OAAmD;QAInD,OAAO,KAAK,EAAE,MAAM,EAAE,EAAE;YACtB,MAAM,GAAG,GAAsB;gBAC7B,SAAS,EAAE,QAAQ;gBACnB,WAAW,EAAE,SAAS;gBACtB,SAAS,EAAE,MAAM;gBACjB,UAAU,EAAE,EAAE;
|
|
1
|
+
{"version":3,"file":"native-tools.js","sourceRoot":"","sources":["../../src/gateway/native-tools.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import type { GatewayConfig } from '../types/index.js';
|
|
2
|
+
export interface LimitExceededError {
|
|
3
|
+
type: 'concurrency' | 'rate';
|
|
4
|
+
serverName: string;
|
|
5
|
+
current: number;
|
|
6
|
+
limit: number;
|
|
7
|
+
message: string;
|
|
8
|
+
}
|
|
9
|
+
interface ServerState {
|
|
10
|
+
/** Number of calls currently in-flight */
|
|
11
|
+
activeCalls: number;
|
|
12
|
+
/** Timestamps (ms) of calls in the current rate window */
|
|
13
|
+
callTimestamps: number[];
|
|
14
|
+
maxConcurrent: number;
|
|
15
|
+
callsPerMinute: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* In-memory per-server rate limiter and concurrency cap.
|
|
19
|
+
*
|
|
20
|
+
* Concurrency: tracks active in-flight calls. If at the limit, new calls
|
|
21
|
+
* are rejected immediately with a structured error.
|
|
22
|
+
*
|
|
23
|
+
* Rate: sliding window — calls in the last 60 seconds must not exceed the
|
|
24
|
+
* configured calls_per_minute. 0 means unlimited for either dimension.
|
|
25
|
+
*
|
|
26
|
+
* State is kept in a Map keyed by server name. No disk I/O.
|
|
27
|
+
*/
|
|
28
|
+
export declare class RateLimiter {
|
|
29
|
+
private state;
|
|
30
|
+
constructor(gatewayConfig?: GatewayConfig);
|
|
31
|
+
/**
|
|
32
|
+
* Try to acquire a slot for a call to `serverName`.
|
|
33
|
+
* Returns null on success, or a LimitExceededError if rejected.
|
|
34
|
+
*
|
|
35
|
+
* On success, call `release()` when the downstream call completes.
|
|
36
|
+
*/
|
|
37
|
+
tryAcquire(serverName: string): LimitExceededError | null;
|
|
38
|
+
/**
|
|
39
|
+
* Release a previously acquired concurrency slot.
|
|
40
|
+
* Safe to call even if `serverName` is unknown — no-op.
|
|
41
|
+
*/
|
|
42
|
+
release(serverName: string): void;
|
|
43
|
+
/** Expose state for testing */
|
|
44
|
+
getState(serverName: string): ServerState | undefined;
|
|
45
|
+
}
|
|
46
|
+
export {};
|
|
47
|
+
//# sourceMappingURL=rate-limiter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rate-limiter.d.ts","sourceRoot":"","sources":["../../src/gateway/rate-limiter.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEvD,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,aAAa,GAAG,MAAM,CAAC;IAC7B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,UAAU,WAAW;IACnB,0CAA0C;IAC1C,WAAW,EAAE,MAAM,CAAC;IACpB,0DAA0D;IAC1D,cAAc,EAAE,MAAM,EAAE,CAAC;IACzB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;CACxB;AAID;;;;;;;;;;GAUG;AACH,qBAAa,WAAW;IACtB,OAAO,CAAC,KAAK,CAAkC;gBAEnC,aAAa,CAAC,EAAE,aAAa;IAYzC;;;;;OAKG;IACH,UAAU,CAAC,UAAU,EAAE,MAAM,GAAG,kBAAkB,GAAG,IAAI;IA6CzD;;;OAGG;IACH,OAAO,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI;IAMjC,+BAA+B;IAC/B,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,WAAW,GAAG,SAAS;CAGtD"}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
const WINDOW_MS = 60_000;
|
|
2
|
+
/**
|
|
3
|
+
* In-memory per-server rate limiter and concurrency cap.
|
|
4
|
+
*
|
|
5
|
+
* Concurrency: tracks active in-flight calls. If at the limit, new calls
|
|
6
|
+
* are rejected immediately with a structured error.
|
|
7
|
+
*
|
|
8
|
+
* Rate: sliding window — calls in the last 60 seconds must not exceed the
|
|
9
|
+
* configured calls_per_minute. 0 means unlimited for either dimension.
|
|
10
|
+
*
|
|
11
|
+
* State is kept in a Map keyed by server name. No disk I/O.
|
|
12
|
+
*/
|
|
13
|
+
export class RateLimiter {
|
|
14
|
+
state = new Map();
|
|
15
|
+
constructor(gatewayConfig) {
|
|
16
|
+
if (!gatewayConfig)
|
|
17
|
+
return;
|
|
18
|
+
for (const [name, serverCfg] of Object.entries(gatewayConfig.servers)) {
|
|
19
|
+
this.state.set(name, {
|
|
20
|
+
activeCalls: 0,
|
|
21
|
+
callTimestamps: [],
|
|
22
|
+
maxConcurrent: serverCfg.max_concurrent_calls ?? 0,
|
|
23
|
+
callsPerMinute: serverCfg.calls_per_minute ?? 0,
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Try to acquire a slot for a call to `serverName`.
|
|
29
|
+
* Returns null on success, or a LimitExceededError if rejected.
|
|
30
|
+
*
|
|
31
|
+
* On success, call `release()` when the downstream call completes.
|
|
32
|
+
*/
|
|
33
|
+
tryAcquire(serverName) {
|
|
34
|
+
let s = this.state.get(serverName);
|
|
35
|
+
if (!s) {
|
|
36
|
+
// Server not in config — allow through (no limits configured)
|
|
37
|
+
this.state.set(serverName, {
|
|
38
|
+
activeCalls: 0,
|
|
39
|
+
callTimestamps: [],
|
|
40
|
+
maxConcurrent: 0,
|
|
41
|
+
callsPerMinute: 0,
|
|
42
|
+
});
|
|
43
|
+
s = this.state.get(serverName);
|
|
44
|
+
}
|
|
45
|
+
// Prune timestamps outside the sliding window before checking rate
|
|
46
|
+
const now = Date.now();
|
|
47
|
+
s.callTimestamps = s.callTimestamps.filter((t) => now - t < WINDOW_MS);
|
|
48
|
+
// Check rate limit
|
|
49
|
+
if (s.callsPerMinute > 0 && s.callTimestamps.length >= s.callsPerMinute) {
|
|
50
|
+
return {
|
|
51
|
+
type: 'rate',
|
|
52
|
+
serverName,
|
|
53
|
+
current: s.callTimestamps.length,
|
|
54
|
+
limit: s.callsPerMinute,
|
|
55
|
+
message: `Rate limit exceeded for server "${serverName}": ${s.callTimestamps.length}/${s.callsPerMinute} calls in the last 60s`,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
// Check concurrency limit
|
|
59
|
+
if (s.maxConcurrent > 0 && s.activeCalls >= s.maxConcurrent) {
|
|
60
|
+
return {
|
|
61
|
+
type: 'concurrency',
|
|
62
|
+
serverName,
|
|
63
|
+
current: s.activeCalls,
|
|
64
|
+
limit: s.maxConcurrent,
|
|
65
|
+
message: `Concurrency limit exceeded for server "${serverName}": ${s.activeCalls}/${s.maxConcurrent} active calls`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
// Acquire
|
|
69
|
+
s.activeCalls++;
|
|
70
|
+
s.callTimestamps.push(now);
|
|
71
|
+
return null;
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Release a previously acquired concurrency slot.
|
|
75
|
+
* Safe to call even if `serverName` is unknown — no-op.
|
|
76
|
+
*/
|
|
77
|
+
release(serverName) {
|
|
78
|
+
const s = this.state.get(serverName);
|
|
79
|
+
if (!s)
|
|
80
|
+
return;
|
|
81
|
+
if (s.activeCalls > 0)
|
|
82
|
+
s.activeCalls--;
|
|
83
|
+
}
|
|
84
|
+
/** Expose state for testing */
|
|
85
|
+
getState(serverName) {
|
|
86
|
+
return this.state.get(serverName);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
//# sourceMappingURL=rate-limiter.js.map
|