@hiro-c/agent-gate 1.0.2 → 1.2.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 +2 -0
- package/dist/config/Config.d.ts +17 -0
- package/dist/config/Config.d.ts.map +1 -1
- package/dist/config/Config.js +7 -0
- package/dist/deterministic/engine.d.ts +13 -1
- package/dist/deterministic/engine.d.ts.map +1 -1
- package/dist/deterministic/engine.js +17 -2
- package/dist/hooks/processHookData.d.ts.map +1 -1
- package/dist/hooks/processHookData.js +18 -6
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/validation/models/AgentSdkClient.d.ts +40 -0
- package/dist/validation/models/AgentSdkClient.d.ts.map +1 -0
- package/dist/validation/models/AgentSdkClient.js +92 -0
- package/dist/validation/validator.d.ts +5 -1
- package/dist/validation/validator.d.ts.map +1 -1
- package/dist/validation/validator.js +7 -1
- package/package.json +5 -2
package/README.md
CHANGED
|
@@ -87,6 +87,8 @@ Full options: see [docs/config.md](docs/config.md) (TODO) or `AgentGatePluginCon
|
|
|
87
87
|
| `AGENT_GATE_REASON_LANG` | AI reason language: `auto` (default) / `en` / `ja` / etc. |
|
|
88
88
|
| `AGENT_GATE_LOG` | `1` writes decisions to `~/.agent-gate/log.jsonl` |
|
|
89
89
|
| `AGENT_GATE_API_KEY` | Use Anthropic API directly instead of `claude` CLI |
|
|
90
|
+
| `AGENT_GATE_USE_SDK` | `1` prefers the Anthropic agent SDK over API/CLI (no API key needed; works best with daemon mode) |
|
|
91
|
+
| `AGENT_GATE_ON_ERROR` | `block` to fail-closed when a rule or AI client throws (default `allow`) |
|
|
90
92
|
| `AGENT_GATE_DAEMON` | `1` routes through the daemon if it is running |
|
|
91
93
|
|
|
92
94
|
## Supported AI tools
|
package/dist/config/Config.d.ts
CHANGED
|
@@ -12,6 +12,21 @@ export type ConfigOptions = {
|
|
|
12
12
|
* - "en", "ja", "zh", "ko", etc.: write reasons in that language.
|
|
13
13
|
*/
|
|
14
14
|
reasonLang?: string;
|
|
15
|
+
/**
|
|
16
|
+
* Prefer Anthropic's agent SDK (`@anthropic-ai/claude-agent-sdk`) as the
|
|
17
|
+
* first model client. Reuses the host process's Claude auth, removing
|
|
18
|
+
* the need for AGENT_GATE_API_KEY. The SDK has noticeable cold-start
|
|
19
|
+
* cost, so this is most useful when paired with `agent-gate daemon`.
|
|
20
|
+
*/
|
|
21
|
+
useSdk?: boolean;
|
|
22
|
+
/**
|
|
23
|
+
* Behavior when a rule or model client throws unexpectedly.
|
|
24
|
+
* - "allow" (default): swallow the error and let the operation through.
|
|
25
|
+
* Optimized for developer ergonomics so flaky AI does not block work.
|
|
26
|
+
* - "block": turn the error into a block verdict. Recommended for
|
|
27
|
+
* production / enterprise pipelines that prefer fail-closed safety.
|
|
28
|
+
*/
|
|
29
|
+
onError?: 'allow' | 'block';
|
|
15
30
|
};
|
|
16
31
|
export declare class Config {
|
|
17
32
|
readonly model: string;
|
|
@@ -20,6 +35,8 @@ export declare class Config {
|
|
|
20
35
|
readonly disabled: boolean;
|
|
21
36
|
readonly useSystemClaude: boolean;
|
|
22
37
|
readonly reasonLang: string | undefined;
|
|
38
|
+
readonly useSdk: boolean;
|
|
39
|
+
readonly onError: 'allow' | 'block';
|
|
23
40
|
constructor(options?: ConfigOptions);
|
|
24
41
|
get useApi(): boolean;
|
|
25
42
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Config.d.ts","sourceRoot":"","sources":["../../src/config/Config.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,sBAAsB,CAAA;AAEhD,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;
|
|
1
|
+
{"version":3,"file":"Config.d.ts","sourceRoot":"","sources":["../../src/config/Config.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,aAAa,sBAAsB,CAAA;AAEhD,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,CAAC,EAAE,MAAM,CAAA;IACd,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB;;;;;OAKG;IACH,MAAM,CAAC,EAAE,OAAO,CAAA;IAChB;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;CAC5B,CAAA;AAED,qBAAa,MAAM;IACjB,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,CAAA;IACnC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;IACzB,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAA;IAC1B,QAAQ,CAAC,eAAe,EAAE,OAAO,CAAA;IACjC,QAAQ,CAAC,UAAU,EAAE,MAAM,GAAG,SAAS,CAAA;IACvC,QAAQ,CAAC,MAAM,EAAE,OAAO,CAAA;IACxB,QAAQ,CAAC,OAAO,EAAE,OAAO,GAAG,OAAO,CAAA;gBAEvB,OAAO,CAAC,EAAE,aAAa;IAenC,IAAI,MAAM,IAAI,OAAO,CAEpB;CACF"}
|
package/dist/config/Config.js
CHANGED
|
@@ -9,6 +9,8 @@ class Config {
|
|
|
9
9
|
disabled;
|
|
10
10
|
useSystemClaude;
|
|
11
11
|
reasonLang;
|
|
12
|
+
useSdk;
|
|
13
|
+
onError;
|
|
12
14
|
constructor(options) {
|
|
13
15
|
this.model = options?.model ?? process.env.AGENT_GATE_MODEL ?? exports.DEFAULT_MODEL;
|
|
14
16
|
this.apiKey = options?.apiKey ?? process.env.AGENT_GATE_API_KEY;
|
|
@@ -17,6 +19,11 @@ class Config {
|
|
|
17
19
|
this.disabled = options?.disabled ?? process.env.AGENT_GATE_DISABLED === 'true';
|
|
18
20
|
this.useSystemClaude = options?.useSystemClaude ?? process.env.USE_SYSTEM_CLAUDE === 'true';
|
|
19
21
|
this.reasonLang = options?.reasonLang ?? process.env.AGENT_GATE_REASON_LANG;
|
|
22
|
+
this.useSdk = options?.useSdk ?? process.env.AGENT_GATE_USE_SDK === '1';
|
|
23
|
+
const envOnError = process.env.AGENT_GATE_ON_ERROR;
|
|
24
|
+
this.onError =
|
|
25
|
+
options?.onError ??
|
|
26
|
+
(envOnError === 'block' ? 'block' : 'allow');
|
|
20
27
|
}
|
|
21
28
|
get useApi() {
|
|
22
29
|
return this.apiKey !== undefined && this.apiKey !== '';
|
|
@@ -7,5 +7,17 @@ export type EngineResult = {
|
|
|
7
7
|
reason: string;
|
|
8
8
|
ruleId: string;
|
|
9
9
|
};
|
|
10
|
-
export
|
|
10
|
+
export type OnErrorPolicy = 'allow' | 'block';
|
|
11
|
+
export interface RunDeterministicRulesOptions {
|
|
12
|
+
/**
|
|
13
|
+
* What to do when a rule's check function throws.
|
|
14
|
+
* - "allow" (default): swallow the exception and continue with the
|
|
15
|
+
* next rule. Compatible with prior versions.
|
|
16
|
+
* - "block": turn the exception into a block verdict and stop the
|
|
17
|
+
* engine. Suitable for enterprise / production use where the
|
|
18
|
+
* safest response to a misbehaving rule is to halt.
|
|
19
|
+
*/
|
|
20
|
+
onError?: OnErrorPolicy;
|
|
21
|
+
}
|
|
22
|
+
export declare function runDeterministicRules(toolName: string, toolInput: Record<string, unknown>, rules: DeterministicRule[], ctx?: SessionContext, options?: RunDeterministicRulesOptions): EngineResult;
|
|
11
23
|
//# sourceMappingURL=engine.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/deterministic/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAA;AAElE,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAErD,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,KAAK,EAAE,iBAAiB,EAAE,EAC1B,GAAG,CAAC,EAAE,cAAc,
|
|
1
|
+
{"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../src/deterministic/engine.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAA;AAC3C,OAAO,EAAE,cAAc,EAAE,MAAM,mCAAmC,CAAA;AAElE,MAAM,MAAM,YAAY,GACpB;IAAE,IAAI,EAAE,OAAO,CAAA;CAAE,GACjB;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAA;AAErD,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,OAAO,CAAA;AAE7C,MAAM,WAAW,4BAA4B;IAC3C;;;;;;;OAOG;IACH,OAAO,CAAC,EAAE,aAAa,CAAA;CACxB;AAED,wBAAgB,qBAAqB,CACnC,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,KAAK,EAAE,iBAAiB,EAAE,EAC1B,GAAG,CAAC,EAAE,cAAc,EACpB,OAAO,CAAC,EAAE,4BAA4B,GACrC,YAAY,CAuBd"}
|
|
@@ -1,9 +1,24 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.runDeterministicRules = runDeterministicRules;
|
|
4
|
-
function runDeterministicRules(toolName, toolInput, rules, ctx) {
|
|
4
|
+
function runDeterministicRules(toolName, toolInput, rules, ctx, options) {
|
|
5
|
+
const onError = options?.onError ?? 'allow';
|
|
5
6
|
for (const rule of rules) {
|
|
6
|
-
|
|
7
|
+
let verdict;
|
|
8
|
+
try {
|
|
9
|
+
verdict = rule.check(toolName, toolInput, ctx);
|
|
10
|
+
}
|
|
11
|
+
catch (e) {
|
|
12
|
+
if (onError === 'block') {
|
|
13
|
+
const reason = e instanceof Error ? e.message : String(e);
|
|
14
|
+
return {
|
|
15
|
+
kind: 'block',
|
|
16
|
+
reason: `Rule "${rule.id}" failed: ${reason}. Failing closed.`,
|
|
17
|
+
ruleId: rule.id,
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
continue;
|
|
21
|
+
}
|
|
7
22
|
if (verdict.kind === 'block') {
|
|
8
23
|
return { kind: 'block', reason: verdict.reason, ruleId: rule.id };
|
|
9
24
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"processHookData.d.ts","sourceRoot":"","sources":["../../src/hooks/processHookData.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAA;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAE7D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;
|
|
1
|
+
{"version":3,"file":"processHookData.d.ts","sourceRoot":"","sources":["../../src/hooks/processHookData.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAA;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAE7D,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAA;AACnD,OAAO,EAAE,MAAM,EAAE,MAAM,kBAAkB,CAAA;AAKzC,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAG1D,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAE7C,OAAO,EACL,eAAe,EAEhB,MAAM,2BAA2B,CAAA;AAMlC,OAAO,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAA;AAWpD,MAAM,WAAW,aAAa;IAC5B,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAAA;IAChC,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAA;CAC7C;AA4BD,MAAM,WAAW,mBAAmB;IAClC,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,eAAe,CAAA;IACjC,SAAS,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,UAAU,EAAE,CAAA;IACzC,WAAW,CAAC,EAAE,OAAO,SAAS,CAAA;IAC9B,cAAc,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,YAAY,CAAA;IACjD,aAAa,CAAC,EAAE,aAAa,CAAA;IAC7B,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,kBAAkB,CAAC,EAAE,iBAAiB,EAAE,CAAA;IACxC,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB;;;;OAIG;IACH,QAAQ,CAAC,EAAE,QAAQ,CAAA;IACnB,wEAAwE;IACxE,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,OAAO,CAAC;QAAC,IAAI,CAAC,EAAE,MAAM,CAAA;KAAE,CAAA;CAC9C;AAaD,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,mBAAmB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CAuI3B"}
|
|
@@ -11,6 +11,7 @@ const Config_1 = require("../config/Config");
|
|
|
11
11
|
const ClaudeCli_1 = require("../validation/models/ClaudeCli");
|
|
12
12
|
const AnthropicApi_1 = require("../validation/models/AnthropicApi");
|
|
13
13
|
const CompositeModelClient_1 = require("../validation/models/CompositeModelClient");
|
|
14
|
+
const AgentSdkClient_1 = require("../validation/models/AgentSdkClient");
|
|
14
15
|
const engine_1 = require("../deterministic/engine");
|
|
15
16
|
const defaultRules_1 = require("../deterministic/defaultRules");
|
|
16
17
|
const adapter_1 = require("../adapters/claude-code/adapter");
|
|
@@ -88,7 +89,7 @@ async function processHookData(input, deps) {
|
|
|
88
89
|
// any cooldown or AI check.
|
|
89
90
|
const deterministicRules = deps?.deterministicRules ??
|
|
90
91
|
(0, defaultRules_1.buildDefaultDeterministicRules)(agentGateConfig);
|
|
91
|
-
const ruleVerdict = (0, engine_1.runDeterministicRules)(toolName, toolInput, deterministicRules, sessionContext);
|
|
92
|
+
const ruleVerdict = (0, engine_1.runDeterministicRules)(toolName, toolInput, deterministicRules, sessionContext, { onError: config.onError });
|
|
92
93
|
if (ruleVerdict.kind === 'block') {
|
|
93
94
|
bus.emit({
|
|
94
95
|
type: 'rule.fired',
|
|
@@ -138,7 +139,9 @@ async function processHookData(input, deps) {
|
|
|
138
139
|
rulesCount: rules.length,
|
|
139
140
|
});
|
|
140
141
|
const aiStart = Date.now();
|
|
141
|
-
const result = await validate(rules, toolName, toolInput, modelClient
|
|
142
|
+
const result = await validate(rules, toolName, toolInput, modelClient, {
|
|
143
|
+
onError: config.onError,
|
|
144
|
+
});
|
|
142
145
|
const aiLatency = Date.now() - aiStart;
|
|
143
146
|
// Update cooldown timestamp AFTER successful validation
|
|
144
147
|
if (cooldownStore) {
|
|
@@ -165,11 +168,20 @@ async function processHookData(input, deps) {
|
|
|
165
168
|
return result;
|
|
166
169
|
}
|
|
167
170
|
function createModelClient(config, cwd) {
|
|
168
|
-
// Build a fallback chain: the most-preferred client comes first
|
|
169
|
-
//
|
|
170
|
-
//
|
|
171
|
-
//
|
|
171
|
+
// Build a fallback chain: the most-preferred client comes first.
|
|
172
|
+
// The CompositeModelClient tries each in order; if one fails, the next
|
|
173
|
+
// runs. The validator's outer try/catch fail-opens if every client has
|
|
174
|
+
// failed.
|
|
175
|
+
//
|
|
176
|
+
// Order: AgentSdkClient (when AGENT_GATE_USE_SDK=1) -> AnthropicApi
|
|
177
|
+
// (when an API key is set) -> ClaudeCli (always available as a fallback).
|
|
178
|
+
// AgentSdkClient is preferred when enabled because it reuses the host
|
|
179
|
+
// agent's existing Claude authentication, removing the need for a
|
|
180
|
+
// separate API key.
|
|
172
181
|
const clients = [];
|
|
182
|
+
if (config.useSdk) {
|
|
183
|
+
clients.push(new AgentSdkClient_1.AgentSdkClient({ config }));
|
|
184
|
+
}
|
|
173
185
|
if (config.useApi) {
|
|
174
186
|
clients.push(new AnthropicApi_1.AnthropicApi(config));
|
|
175
187
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -20,6 +20,8 @@ export { cursorAdapter } from './adapters/cursor/adapter';
|
|
|
20
20
|
export type { Adapter, ReadHistoryOptions } from './adapters/Adapter';
|
|
21
21
|
export { CompositeModelClient } from './validation/models/CompositeModelClient';
|
|
22
22
|
export type { CompositeModelClientOptions } from './validation/models/CompositeModelClient';
|
|
23
|
+
export { AgentSdkClient } from './validation/models/AgentSdkClient';
|
|
24
|
+
export type { AgentSdkClientOptions, AgentSdkQueryFn, } from './validation/models/AgentSdkClient';
|
|
23
25
|
export { lintRuleSources } from './doctor/lintRuleSources';
|
|
24
26
|
export { formatFindings } from './doctor/formatFindings';
|
|
25
27
|
export type { Finding, FindingCode, Severity, } from './doctor/findings';
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAClE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AAG9D,YAAY,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAA;AAC1E,YAAY,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAC1D,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AACjE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC9E,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAClE,YAAY,EACV,cAAc,EACd,YAAY,GACb,MAAM,kCAAkC,CAAA;AAGzC,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAGnE,YAAY,EACV,iBAAiB,EACjB,WAAW,GACZ,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACL,yBAAyB,EACzB,8BAA8B,GAC/B,MAAM,8BAA8B,CAAA;AAGrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,YAAY,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAGrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAA;AAC/E,YAAY,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAA;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,MAAM,EAAE,MAAM,iBAAiB,CAAA;AACxC,YAAY,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACpD,OAAO,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAA;AACpD,YAAY,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAA;AAClE,YAAY,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,EAAE,mBAAmB,EAAE,MAAM,0BAA0B,CAAA;AAC9D,OAAO,EAAE,gBAAgB,EAAE,MAAM,6BAA6B,CAAA;AAG9D,YAAY,EAAE,gBAAgB,EAAE,MAAM,oCAAoC,CAAA;AAC1E,YAAY,EAAE,QAAQ,EAAE,MAAM,4BAA4B,CAAA;AAC1D,YAAY,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAA;AACjE,YAAY,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,8BAA8B,CAAA;AAC9E,YAAY,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,0BAA0B,CAAA;AAClE,YAAY,EACV,cAAc,EACd,YAAY,GACb,MAAM,kCAAkC,CAAA;AAGzC,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AAGnE,YAAY,EACV,iBAAiB,EACjB,WAAW,GACZ,MAAM,uBAAuB,CAAA;AAC9B,OAAO,EACL,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,GACtB,MAAM,2BAA2B,CAAA;AAClC,OAAO,EACL,yBAAyB,EACzB,8BAA8B,GAC/B,MAAM,8BAA8B,CAAA;AAGrC,OAAO,EAAE,iBAAiB,EAAE,MAAM,gCAAgC,CAAA;AAClE,OAAO,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAA;AACzD,YAAY,EAAE,OAAO,EAAE,kBAAkB,EAAE,MAAM,oBAAoB,CAAA;AAGrE,OAAO,EAAE,oBAAoB,EAAE,MAAM,0CAA0C,CAAA;AAC/E,YAAY,EAAE,2BAA2B,EAAE,MAAM,0CAA0C,CAAA;AAC3F,OAAO,EAAE,cAAc,EAAE,MAAM,oCAAoC,CAAA;AACnE,YAAY,EACV,qBAAqB,EACrB,eAAe,GAChB,MAAM,oCAAoC,CAAA;AAG3C,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC1D,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAA;AACxD,YAAY,EACV,OAAO,EACP,WAAW,EACX,QAAQ,GACT,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,YAAY,EAAE,mBAAmB,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAA;AACzE,OAAO,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAA;AAC9C,YAAY,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAA;AAC1D,OAAO,EACL,iBAAiB,IAAI,uBAAuB,GAC7C,MAAM,mBAAmB,CAAA;AAC1B,YAAY,EACV,aAAa,EACb,cAAc,EACd,mBAAmB,GACpB,MAAM,mBAAmB,CAAA;AAG1B,OAAO,EAAE,QAAQ,EAAE,MAAM,0BAA0B,CAAA;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,qCAAqC,CAAA;AACnE,YAAY,EACV,aAAa,EACb,cAAc,EACd,gBAAgB,EAChB,gBAAgB,EAChB,mBAAmB,EACnB,kBAAkB,EAClB,IAAI,GACL,MAAM,4BAA4B,CAAA;AAGnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,gCAAgC,CAAA;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAA;AAClD,OAAO,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.processHookData = exports.validator = exports.collectRuleSources = exports.JsonlFileSink = exports.EventBus = exports.defaultDaemonSocketPath = exports.sendToDaemon = exports.DaemonServer = exports.formatFindings = exports.lintRuleSources = exports.CompositeModelClient = exports.cursorAdapter = exports.claudeCodeAdapter = exports.buildDefaultDeterministicRules = exports.defaultDeterministicRules = exports.forbidFilePathPattern = exports.forbidContentPattern = exports.forbidCommandPattern = exports.HookDataSchema = exports.loadPluginConfig = exports.loadAgentGateConfig = exports.defineConfig = exports.Config = void 0;
|
|
3
|
+
exports.processHookData = exports.validator = exports.collectRuleSources = exports.JsonlFileSink = exports.EventBus = exports.defaultDaemonSocketPath = exports.sendToDaemon = exports.DaemonServer = exports.formatFindings = exports.lintRuleSources = exports.AgentSdkClient = exports.CompositeModelClient = exports.cursorAdapter = exports.claudeCodeAdapter = exports.buildDefaultDeterministicRules = exports.defaultDeterministicRules = exports.forbidFilePathPattern = exports.forbidContentPattern = exports.forbidCommandPattern = exports.HookDataSchema = exports.loadPluginConfig = exports.loadAgentGateConfig = exports.defineConfig = exports.Config = void 0;
|
|
4
4
|
// Config
|
|
5
5
|
var Config_1 = require("./config/Config");
|
|
6
6
|
Object.defineProperty(exports, "Config", { enumerable: true, get: function () { return Config_1.Config; } });
|
|
@@ -28,6 +28,8 @@ Object.defineProperty(exports, "cursorAdapter", { enumerable: true, get: functio
|
|
|
28
28
|
// Model clients (for users wiring up custom AI fallback chains)
|
|
29
29
|
var CompositeModelClient_1 = require("./validation/models/CompositeModelClient");
|
|
30
30
|
Object.defineProperty(exports, "CompositeModelClient", { enumerable: true, get: function () { return CompositeModelClient_1.CompositeModelClient; } });
|
|
31
|
+
var AgentSdkClient_1 = require("./validation/models/AgentSdkClient");
|
|
32
|
+
Object.defineProperty(exports, "AgentSdkClient", { enumerable: true, get: function () { return AgentSdkClient_1.AgentSdkClient; } });
|
|
31
33
|
// Doctor (CLAUDE.md linter)
|
|
32
34
|
var lintRuleSources_1 = require("./doctor/lintRuleSources");
|
|
33
35
|
Object.defineProperty(exports, "lintRuleSources", { enumerable: true, get: function () { return lintRuleSources_1.lintRuleSources; } });
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { IModelClient } from '../../contracts/types/ModelClient';
|
|
2
|
+
import { Config } from '../../config/Config';
|
|
3
|
+
/**
|
|
4
|
+
* Shape of the `query` export from `@anthropic-ai/claude-agent-sdk`,
|
|
5
|
+
* as documented by Anthropic. The SDK returns an AsyncIterable of
|
|
6
|
+
* streamed messages and accepts a small set of options.
|
|
7
|
+
*/
|
|
8
|
+
export type AgentSdkQueryFn = (args: {
|
|
9
|
+
prompt: string;
|
|
10
|
+
options?: {
|
|
11
|
+
model?: string;
|
|
12
|
+
allowedTools?: string[];
|
|
13
|
+
permissionMode?: 'default' | 'bypassPermissions' | 'dontAsk';
|
|
14
|
+
maxTurns?: number;
|
|
15
|
+
};
|
|
16
|
+
}) => AsyncIterable<unknown>;
|
|
17
|
+
export interface AgentSdkClientOptions {
|
|
18
|
+
config: Config;
|
|
19
|
+
/**
|
|
20
|
+
* Override the SDK loader. Production builds default to a dynamic
|
|
21
|
+
* import of `@anthropic-ai/claude-agent-sdk`; tests inject a fake.
|
|
22
|
+
*/
|
|
23
|
+
loadQuery?: () => Promise<AgentSdkQueryFn>;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* IModelClient that runs validation through Anthropic's agent SDK
|
|
27
|
+
* (`@anthropic-ai/claude-agent-sdk`). Reuses the host process's
|
|
28
|
+
* existing Claude authentication so no separate API key is required.
|
|
29
|
+
*
|
|
30
|
+
* Tradeoff: the SDK has noticeable cold-start cost on each call.
|
|
31
|
+
* Recommended paired with `agent-gate daemon` so the SDK initialization
|
|
32
|
+
* is amortized across hook invocations.
|
|
33
|
+
*/
|
|
34
|
+
export declare class AgentSdkClient implements IModelClient {
|
|
35
|
+
private readonly config;
|
|
36
|
+
private readonly loadQuery;
|
|
37
|
+
constructor(opts: AgentSdkClientOptions);
|
|
38
|
+
ask(prompt: string): Promise<string>;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=AgentSdkClient.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"AgentSdkClient.d.ts","sourceRoot":"","sources":["../../../src/validation/models/AgentSdkClient.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,mCAAmC,CAAA;AAChE,OAAO,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAA;AAG5C;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,CAAC,IAAI,EAAE;IACnC,MAAM,EAAE,MAAM,CAAA;IACd,OAAO,CAAC,EAAE;QACR,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,YAAY,CAAC,EAAE,MAAM,EAAE,CAAA;QACvB,cAAc,CAAC,EAAE,SAAS,GAAG,mBAAmB,GAAG,SAAS,CAAA;QAC5D,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB,CAAA;CACF,KAAK,aAAa,CAAC,OAAO,CAAC,CAAA;AAE5B,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,MAAM,CAAA;IACd;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,OAAO,CAAC,eAAe,CAAC,CAAA;CAC3C;AAED;;;;;;;;GAQG;AACH,qBAAa,cAAe,YAAW,YAAY;IACjD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAQ;IAC/B,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgC;gBAE9C,IAAI,EAAE,qBAAqB;IAKjC,GAAG,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;CAgC3C"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.AgentSdkClient = void 0;
|
|
4
|
+
const system_prompt_1 = require("../prompts/system-prompt");
|
|
5
|
+
/**
|
|
6
|
+
* IModelClient that runs validation through Anthropic's agent SDK
|
|
7
|
+
* (`@anthropic-ai/claude-agent-sdk`). Reuses the host process's
|
|
8
|
+
* existing Claude authentication so no separate API key is required.
|
|
9
|
+
*
|
|
10
|
+
* Tradeoff: the SDK has noticeable cold-start cost on each call.
|
|
11
|
+
* Recommended paired with `agent-gate daemon` so the SDK initialization
|
|
12
|
+
* is amortized across hook invocations.
|
|
13
|
+
*/
|
|
14
|
+
class AgentSdkClient {
|
|
15
|
+
config;
|
|
16
|
+
loadQuery;
|
|
17
|
+
constructor(opts) {
|
|
18
|
+
this.config = opts.config;
|
|
19
|
+
this.loadQuery = opts.loadQuery ?? defaultLoadQuery;
|
|
20
|
+
}
|
|
21
|
+
async ask(prompt) {
|
|
22
|
+
let query;
|
|
23
|
+
try {
|
|
24
|
+
query = await this.loadQuery();
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
28
|
+
throw new Error(`Agent SDK loader failed; install @anthropic-ai/claude-agent-sdk or fall back to another client. Underlying: ${msg}`);
|
|
29
|
+
}
|
|
30
|
+
const fullPrompt = `${(0, system_prompt_1.getSystemPrompt)(this.config.reasonLang)}\n\n${prompt}`;
|
|
31
|
+
let lastText = '';
|
|
32
|
+
for await (const message of query({
|
|
33
|
+
prompt: fullPrompt,
|
|
34
|
+
options: {
|
|
35
|
+
model: this.config.model,
|
|
36
|
+
allowedTools: [],
|
|
37
|
+
permissionMode: 'bypassPermissions',
|
|
38
|
+
maxTurns: 1,
|
|
39
|
+
},
|
|
40
|
+
})) {
|
|
41
|
+
const t = extractText(message);
|
|
42
|
+
if (t)
|
|
43
|
+
lastText = t;
|
|
44
|
+
}
|
|
45
|
+
if (!lastText) {
|
|
46
|
+
throw new Error('No response from agent SDK stream');
|
|
47
|
+
}
|
|
48
|
+
return lastText;
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
exports.AgentSdkClient = AgentSdkClient;
|
|
52
|
+
async function defaultLoadQuery() {
|
|
53
|
+
const mod = (await import('@anthropic-ai/claude-agent-sdk'));
|
|
54
|
+
if (typeof mod.query !== 'function') {
|
|
55
|
+
throw new Error('@anthropic-ai/claude-agent-sdk did not export query()');
|
|
56
|
+
}
|
|
57
|
+
return mod.query;
|
|
58
|
+
}
|
|
59
|
+
/**
|
|
60
|
+
* Pulls assistant text out of one streamed message. The SDK emits a
|
|
61
|
+
* mix of system / assistant / result envelopes; we cover the common
|
|
62
|
+
* shapes and ignore the rest.
|
|
63
|
+
*/
|
|
64
|
+
function extractText(message) {
|
|
65
|
+
if (typeof message !== 'object' || message === null)
|
|
66
|
+
return null;
|
|
67
|
+
const m = message;
|
|
68
|
+
if (typeof m.result === 'string' && m.result.length > 0)
|
|
69
|
+
return m.result;
|
|
70
|
+
const inner = m.message;
|
|
71
|
+
if (typeof inner === 'object' && inner !== null) {
|
|
72
|
+
const content = inner.content;
|
|
73
|
+
if (Array.isArray(content)) {
|
|
74
|
+
const text = content
|
|
75
|
+
.map((c) => {
|
|
76
|
+
if (typeof c === 'object' && c !== null) {
|
|
77
|
+
const block = c;
|
|
78
|
+
if (typeof block.text === 'string')
|
|
79
|
+
return block.text;
|
|
80
|
+
}
|
|
81
|
+
return null;
|
|
82
|
+
})
|
|
83
|
+
.filter((s) => Boolean(s))
|
|
84
|
+
.join('\n');
|
|
85
|
+
if (text.length > 0)
|
|
86
|
+
return text;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
if (typeof m.text === 'string' && m.text.length > 0)
|
|
90
|
+
return m.text;
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
import { ValidationResult } from '../contracts/types/ValidationResult';
|
|
2
2
|
import { RuleSource } from '../contracts/types/RuleSource';
|
|
3
3
|
import { IModelClient } from '../contracts/types/ModelClient';
|
|
4
|
-
export
|
|
4
|
+
export interface ValidatorOptions {
|
|
5
|
+
/** Fail-closed behavior on model client failures. Default: "allow". */
|
|
6
|
+
onError?: 'allow' | 'block';
|
|
7
|
+
}
|
|
8
|
+
export declare function validator(rules: RuleSource[], toolName: string, toolInput: Record<string, unknown>, modelClient: IModelClient, options?: ValidatorOptions): Promise<ValidationResult>;
|
|
5
9
|
//# sourceMappingURL=validator.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/validation/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAA;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAQ7D,wBAAsB,SAAS,CAC7B,KAAK,EAAE,UAAU,EAAE,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,WAAW,EAAE,YAAY,
|
|
1
|
+
{"version":3,"file":"validator.d.ts","sourceRoot":"","sources":["../../src/validation/validator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAA;AACtE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAA;AAC1D,OAAO,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAA;AAQ7D,MAAM,WAAW,gBAAgB;IAC/B,uEAAuE;IACvE,OAAO,CAAC,EAAE,OAAO,GAAG,OAAO,CAAA;CAC5B;AAED,wBAAsB,SAAS,CAC7B,KAAK,EAAE,UAAU,EAAE,EACnB,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAClC,WAAW,EAAE,YAAY,EACzB,OAAO,CAAC,EAAE,gBAAgB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CAmB3B"}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.validator = validator;
|
|
4
4
|
const context_1 = require("./prompts/context");
|
|
5
|
-
async function validator(rules, toolName, toolInput, modelClient) {
|
|
5
|
+
async function validator(rules, toolName, toolInput, modelClient, options) {
|
|
6
6
|
try {
|
|
7
7
|
const prompt = (0, context_1.buildPrompt)(rules, toolName, toolInput);
|
|
8
8
|
const response = await modelClient.ask(prompt);
|
|
@@ -10,6 +10,12 @@ async function validator(rules, toolName, toolInput, modelClient) {
|
|
|
10
10
|
}
|
|
11
11
|
catch (error) {
|
|
12
12
|
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
13
|
+
if (options?.onError === 'block') {
|
|
14
|
+
return {
|
|
15
|
+
decision: 'block',
|
|
16
|
+
reason: `Validation failed (failing closed): ${errorMessage}`,
|
|
17
|
+
};
|
|
18
|
+
}
|
|
13
19
|
return {
|
|
14
20
|
decision: undefined,
|
|
15
21
|
reason: `Validation error (allowing operation): ${errorMessage}`,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hiro-c/agent-gate",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.2.0",
|
|
4
4
|
"description": "Runtime rule enforcer for AI coding agents. Reads CLAUDE.md / AGENTS.md / .cursorrules and enforces them via Claude Code and Cursor hooks, with a deterministic safety baseline.",
|
|
5
5
|
"author": "Hiro-Chiba",
|
|
6
6
|
"license": "MIT",
|
|
@@ -55,7 +55,7 @@
|
|
|
55
55
|
"prepublishOnly": "npm run checks && npm run build"
|
|
56
56
|
},
|
|
57
57
|
"dependencies": {
|
|
58
|
-
"@anthropic-ai/sdk": "^0.
|
|
58
|
+
"@anthropic-ai/sdk": "^0.93.0",
|
|
59
59
|
"jiti": "^2.7.0",
|
|
60
60
|
"zod": "^4.3.6"
|
|
61
61
|
},
|
|
@@ -63,5 +63,8 @@
|
|
|
63
63
|
"@types/node": "^25.6.0",
|
|
64
64
|
"typescript": "^6.0.3",
|
|
65
65
|
"vitest": "^4.1.4"
|
|
66
|
+
},
|
|
67
|
+
"optionalDependencies": {
|
|
68
|
+
"@anthropic-ai/claude-agent-sdk": "^0.3.0"
|
|
66
69
|
}
|
|
67
70
|
}
|