@hiro-c/agent-gate 1.2.0 → 1.4.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/cache/DecisionCache.d.ts +36 -0
- package/dist/cache/DecisionCache.d.ts.map +1 -0
- package/dist/cache/DecisionCache.js +80 -0
- package/dist/cli/agent-gate.d.ts.map +1 -1
- package/dist/cli/agent-gate.js +10 -0
- package/dist/deterministic/bashAnalysis.d.ts +31 -0
- package/dist/deterministic/bashAnalysis.d.ts.map +1 -0
- package/dist/deterministic/bashAnalysis.js +108 -0
- package/dist/deterministic/rules/preventBashSecretWrite.d.ts.map +1 -1
- package/dist/deterministic/rules/preventBashSecretWrite.js +19 -11
- package/dist/deterministic/rules/preventRmRfRoot.d.ts.map +1 -1
- package/dist/deterministic/rules/preventRmRfRoot.js +28 -10
- package/dist/hooks/processHookData.d.ts +7 -0
- package/dist/hooks/processHookData.d.ts.map +1 -1
- package/dist/hooks/processHookData.js +26 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -90,6 +90,8 @@ Full options: see [docs/config.md](docs/config.md) (TODO) or `AgentGatePluginCon
|
|
|
90
90
|
| `AGENT_GATE_USE_SDK` | `1` prefers the Anthropic agent SDK over API/CLI (no API key needed; works best with daemon mode) |
|
|
91
91
|
| `AGENT_GATE_ON_ERROR` | `block` to fail-closed when a rule or AI client throws (default `allow`) |
|
|
92
92
|
| `AGENT_GATE_DAEMON` | `1` routes through the daemon if it is running |
|
|
93
|
+
| `AGENT_GATE_CACHE_TTL_SEC` | Daemon decision cache TTL in seconds (default `60`) |
|
|
94
|
+
| `AGENT_GATE_CACHE_SIZE` | Daemon decision cache max entries (default `256`) |
|
|
93
95
|
|
|
94
96
|
## Supported AI tools
|
|
95
97
|
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { ValidationResult } from '../contracts/types/ValidationResult';
|
|
2
|
+
export interface CacheKey {
|
|
3
|
+
adapter: string;
|
|
4
|
+
toolName: string;
|
|
5
|
+
toolInput: Record<string, unknown>;
|
|
6
|
+
cwd: string;
|
|
7
|
+
}
|
|
8
|
+
export interface DecisionCacheOptions {
|
|
9
|
+
/** Time to live in seconds. Entries past this age miss. */
|
|
10
|
+
ttlSec: number;
|
|
11
|
+
/** Max entries before LRU eviction kicks in. */
|
|
12
|
+
maxEntries: number;
|
|
13
|
+
/** Clock injection for testing. Defaults to Date.now. */
|
|
14
|
+
now?: () => number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* In-process LRU + TTL cache of agent-gate decisions.
|
|
18
|
+
*
|
|
19
|
+
* Most useful inside `agent-gate daemon` where the cache persists
|
|
20
|
+
* across hook invocations. Same {adapter, toolName, toolInput, cwd}
|
|
21
|
+
* within the TTL skips the entire pipeline (deterministic engine,
|
|
22
|
+
* collector, AI call) and returns the prior verdict.
|
|
23
|
+
*/
|
|
24
|
+
export declare class DecisionCache {
|
|
25
|
+
private readonly ttlMs;
|
|
26
|
+
private readonly maxEntries;
|
|
27
|
+
private readonly now;
|
|
28
|
+
private readonly store;
|
|
29
|
+
constructor(opts: DecisionCacheOptions);
|
|
30
|
+
private hash;
|
|
31
|
+
get(key: CacheKey): ValidationResult | null;
|
|
32
|
+
set(key: CacheKey, value: ValidationResult): void;
|
|
33
|
+
get size(): number;
|
|
34
|
+
clear(): void;
|
|
35
|
+
}
|
|
36
|
+
//# sourceMappingURL=DecisionCache.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"DecisionCache.d.ts","sourceRoot":"","sources":["../../src/cache/DecisionCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAA;AAEtE,MAAM,WAAW,QAAQ;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAClC,GAAG,EAAE,MAAM,CAAA;CACZ;AAED,MAAM,WAAW,oBAAoB;IACnC,2DAA2D;IAC3D,MAAM,EAAE,MAAM,CAAA;IACd,gDAAgD;IAChD,UAAU,EAAE,MAAM,CAAA;IAClB,yDAAyD;IACzD,GAAG,CAAC,EAAE,MAAM,MAAM,CAAA;CACnB;AAOD;;;;;;;GAOG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAQ;IAC9B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAQ;IACnC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAc;IAClC,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAgC;gBAE1C,IAAI,EAAE,oBAAoB;IAMtC,OAAO,CAAC,IAAI;IASZ,GAAG,CAAC,GAAG,EAAE,QAAQ,GAAG,gBAAgB,GAAG,IAAI;IAc3C,GAAG,CAAC,GAAG,EAAE,QAAQ,EAAE,KAAK,EAAE,gBAAgB,GAAG,IAAI;IAWjD,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED,KAAK,IAAI,IAAI;CAGd"}
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DecisionCache = void 0;
|
|
4
|
+
/**
|
|
5
|
+
* In-process LRU + TTL cache of agent-gate decisions.
|
|
6
|
+
*
|
|
7
|
+
* Most useful inside `agent-gate daemon` where the cache persists
|
|
8
|
+
* across hook invocations. Same {adapter, toolName, toolInput, cwd}
|
|
9
|
+
* within the TTL skips the entire pipeline (deterministic engine,
|
|
10
|
+
* collector, AI call) and returns the prior verdict.
|
|
11
|
+
*/
|
|
12
|
+
class DecisionCache {
|
|
13
|
+
ttlMs;
|
|
14
|
+
maxEntries;
|
|
15
|
+
now;
|
|
16
|
+
store = new Map();
|
|
17
|
+
constructor(opts) {
|
|
18
|
+
this.ttlMs = opts.ttlSec * 1000;
|
|
19
|
+
this.maxEntries = opts.maxEntries;
|
|
20
|
+
this.now = opts.now ?? Date.now;
|
|
21
|
+
}
|
|
22
|
+
hash(key) {
|
|
23
|
+
return JSON.stringify({
|
|
24
|
+
a: key.adapter,
|
|
25
|
+
t: key.toolName,
|
|
26
|
+
c: key.cwd,
|
|
27
|
+
i: canonicalize(key.toolInput),
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
get(key) {
|
|
31
|
+
const k = this.hash(key);
|
|
32
|
+
const entry = this.store.get(k);
|
|
33
|
+
if (!entry)
|
|
34
|
+
return null;
|
|
35
|
+
if (entry.expiresAt <= this.now()) {
|
|
36
|
+
this.store.delete(k);
|
|
37
|
+
return null;
|
|
38
|
+
}
|
|
39
|
+
// Refresh LRU order: re-insert.
|
|
40
|
+
this.store.delete(k);
|
|
41
|
+
this.store.set(k, entry);
|
|
42
|
+
return entry.value;
|
|
43
|
+
}
|
|
44
|
+
set(key, value) {
|
|
45
|
+
const k = this.hash(key);
|
|
46
|
+
this.store.delete(k);
|
|
47
|
+
this.store.set(k, { value, expiresAt: this.now() + this.ttlMs });
|
|
48
|
+
while (this.store.size > this.maxEntries) {
|
|
49
|
+
const oldest = this.store.keys().next().value;
|
|
50
|
+
if (oldest === undefined)
|
|
51
|
+
break;
|
|
52
|
+
this.store.delete(oldest);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
get size() {
|
|
56
|
+
return this.store.size;
|
|
57
|
+
}
|
|
58
|
+
clear() {
|
|
59
|
+
this.store.clear();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
exports.DecisionCache = DecisionCache;
|
|
63
|
+
/**
|
|
64
|
+
* Stable string representation of an arbitrary JSON-like input.
|
|
65
|
+
* Sorts object keys so key-order does not affect cache hits.
|
|
66
|
+
*/
|
|
67
|
+
function canonicalize(value) {
|
|
68
|
+
if (Array.isArray(value)) {
|
|
69
|
+
return value.map(canonicalize);
|
|
70
|
+
}
|
|
71
|
+
if (value !== null && typeof value === 'object') {
|
|
72
|
+
const obj = value;
|
|
73
|
+
const sorted = {};
|
|
74
|
+
for (const k of Object.keys(obj).sort()) {
|
|
75
|
+
sorted[k] = canonicalize(obj[k]);
|
|
76
|
+
}
|
|
77
|
+
return sorted;
|
|
78
|
+
}
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-gate.d.ts","sourceRoot":"","sources":["../../src/cli/agent-gate.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAA;AAYtE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"agent-gate.d.ts","sourceRoot":"","sources":["../../src/cli/agent-gate.ts"],"names":[],"mappings":";AAIA,OAAO,EAAE,gBAAgB,EAAE,MAAM,qCAAqC,CAAA;AAYtE,OAAO,EAAE,OAAO,EAAE,MAAM,qBAAqB,CAAA;AAuC7C,wBAAsB,GAAG,CACvB,KAAK,EAAE,MAAM,EACb,OAAO,CAAC,EAAE,OAAO,GAChB,OAAO,CAAC,gBAAgB,CAAC,CAE3B;AAuHD,UAAU,UAAU;IAClB,UAAU,EAAE,MAAM,EAAE,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,OAAO,CAAA;IACjB,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,UAAU,CAgCpD"}
|
package/dist/cli/agent-gate.js
CHANGED
|
@@ -15,6 +15,7 @@ const formatFindings_1 = require("../doctor/formatFindings");
|
|
|
15
15
|
const server_1 = require("../daemon/server");
|
|
16
16
|
const client_1 = require("../daemon/client");
|
|
17
17
|
const protocol_1 = require("../daemon/protocol");
|
|
18
|
+
const DecisionCache_1 = require("../cache/DecisionCache");
|
|
18
19
|
const HELP_TEXT = `agent-gate — runtime enforcer for AI coding agent rules
|
|
19
20
|
|
|
20
21
|
Usage:
|
|
@@ -75,6 +76,14 @@ function runHookMode(adapter) {
|
|
|
75
76
|
}
|
|
76
77
|
async function runDaemon() {
|
|
77
78
|
const socketPath = process.env.AGENT_GATE_SOCKET_PATH ?? (0, protocol_1.defaultSocketPath)();
|
|
79
|
+
// Shared decision cache lives for the lifetime of the daemon, so every
|
|
80
|
+
// hook invocation benefits from prior verdicts.
|
|
81
|
+
const ttlSec = parseInt(process.env.AGENT_GATE_CACHE_TTL_SEC ?? '60', 10);
|
|
82
|
+
const maxEntries = parseInt(process.env.AGENT_GATE_CACHE_SIZE ?? '256', 10);
|
|
83
|
+
const cache = new DecisionCache_1.DecisionCache({
|
|
84
|
+
ttlSec: Number.isNaN(ttlSec) ? 60 : ttlSec,
|
|
85
|
+
maxEntries: Number.isNaN(maxEntries) ? 256 : maxEntries,
|
|
86
|
+
});
|
|
78
87
|
const server = new server_1.DaemonServer({
|
|
79
88
|
socketPath,
|
|
80
89
|
handler: async (req) => {
|
|
@@ -89,6 +98,7 @@ async function runDaemon() {
|
|
|
89
98
|
const result = await (0, processHookData_1.processHookData)(req.payload, {
|
|
90
99
|
adapter,
|
|
91
100
|
cwd: req.cwd,
|
|
101
|
+
cache,
|
|
92
102
|
});
|
|
93
103
|
return { output: adapter.formatResponse(result) };
|
|
94
104
|
},
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Light-weight bash analysis utilities used by deterministic rules to
|
|
3
|
+
* see past common obfuscation patterns. This is intentionally not a
|
|
4
|
+
* full shell parser; it handles the cases that matter for guardrails
|
|
5
|
+
* (separators, quoting, heredoc redirects, command substitution
|
|
6
|
+
* markers) and stays small.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* Split a command line into top-level statements separated by `;`,
|
|
10
|
+
* `&&`, `||`, `|`, or newline. Respects single- and double-quoted
|
|
11
|
+
* regions so separators inside strings are preserved as part of the
|
|
12
|
+
* statement.
|
|
13
|
+
*
|
|
14
|
+
* Trims and drops empty results.
|
|
15
|
+
*/
|
|
16
|
+
export declare function splitStatements(command: string): string[];
|
|
17
|
+
/**
|
|
18
|
+
* Return any redirect targets that follow a heredoc operator. Each
|
|
19
|
+
* `cat <<EOF > target` (or `>> target`) anywhere in the command
|
|
20
|
+
* contributes one entry. Returns the raw target token without quote
|
|
21
|
+
* stripping.
|
|
22
|
+
*/
|
|
23
|
+
export declare function extractHeredocTargets(command: string): string[];
|
|
24
|
+
/**
|
|
25
|
+
* True when the command uses command substitution `$(...)` / backticks,
|
|
26
|
+
* or a non-literal variable reference (anything other than the well-
|
|
27
|
+
* known `$HOME` / `$USER` / `$PWD` which the catastrophic-path rule
|
|
28
|
+
* already understands).
|
|
29
|
+
*/
|
|
30
|
+
export declare function hasObfuscation(command: string): boolean;
|
|
31
|
+
//# sourceMappingURL=bashAnalysis.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bashAnalysis.d.ts","sourceRoot":"","sources":["../../src/deterministic/bashAnalysis.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;;;;;GAOG;AACH,wBAAgB,eAAe,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CA2CzD;AAED;;;;;GAKG;AACH,wBAAgB,qBAAqB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,EAAE,CAS/D;AAWD;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAYvD"}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Light-weight bash analysis utilities used by deterministic rules to
|
|
4
|
+
* see past common obfuscation patterns. This is intentionally not a
|
|
5
|
+
* full shell parser; it handles the cases that matter for guardrails
|
|
6
|
+
* (separators, quoting, heredoc redirects, command substitution
|
|
7
|
+
* markers) and stays small.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.splitStatements = splitStatements;
|
|
11
|
+
exports.extractHeredocTargets = extractHeredocTargets;
|
|
12
|
+
exports.hasObfuscation = hasObfuscation;
|
|
13
|
+
/**
|
|
14
|
+
* Split a command line into top-level statements separated by `;`,
|
|
15
|
+
* `&&`, `||`, `|`, or newline. Respects single- and double-quoted
|
|
16
|
+
* regions so separators inside strings are preserved as part of the
|
|
17
|
+
* statement.
|
|
18
|
+
*
|
|
19
|
+
* Trims and drops empty results.
|
|
20
|
+
*/
|
|
21
|
+
function splitStatements(command) {
|
|
22
|
+
const out = [];
|
|
23
|
+
let buf = '';
|
|
24
|
+
let i = 0;
|
|
25
|
+
let quote = null;
|
|
26
|
+
while (i < command.length) {
|
|
27
|
+
const c = command[i];
|
|
28
|
+
if (quote) {
|
|
29
|
+
buf += c;
|
|
30
|
+
if (c === quote)
|
|
31
|
+
quote = null;
|
|
32
|
+
i++;
|
|
33
|
+
continue;
|
|
34
|
+
}
|
|
35
|
+
if (c === '"' || c === "'") {
|
|
36
|
+
quote = c;
|
|
37
|
+
buf += c;
|
|
38
|
+
i++;
|
|
39
|
+
continue;
|
|
40
|
+
}
|
|
41
|
+
if (c === ';' || c === '\n' || c === '|') {
|
|
42
|
+
// `||` collapses with `|`; `&&` handled below.
|
|
43
|
+
out.push(buf);
|
|
44
|
+
buf = '';
|
|
45
|
+
i++;
|
|
46
|
+
// collapse a trailing | of `||`
|
|
47
|
+
if (c === '|' && command[i] === '|')
|
|
48
|
+
i++;
|
|
49
|
+
continue;
|
|
50
|
+
}
|
|
51
|
+
if (c === '&' && command[i + 1] === '&') {
|
|
52
|
+
out.push(buf);
|
|
53
|
+
buf = '';
|
|
54
|
+
i += 2;
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
buf += c;
|
|
58
|
+
i++;
|
|
59
|
+
}
|
|
60
|
+
out.push(buf);
|
|
61
|
+
return out.map((s) => s.trim()).filter((s) => s.length > 0);
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Return any redirect targets that follow a heredoc operator. Each
|
|
65
|
+
* `cat <<EOF > target` (or `>> target`) anywhere in the command
|
|
66
|
+
* contributes one entry. Returns the raw target token without quote
|
|
67
|
+
* stripping.
|
|
68
|
+
*/
|
|
69
|
+
function extractHeredocTargets(command) {
|
|
70
|
+
const targets = [];
|
|
71
|
+
// `<<` or `<<-`, optional `'TAG'` or `"TAG"` or bare TAG, then a redirect.
|
|
72
|
+
// We are permissive about whitespace.
|
|
73
|
+
const re = /<<-?\s*(?:['"]?\w+['"]?)\s*(?:>>?)\s*([^\s;|&<>]+)/g;
|
|
74
|
+
for (const m of command.matchAll(re)) {
|
|
75
|
+
if (m[1])
|
|
76
|
+
targets.push(m[1]);
|
|
77
|
+
}
|
|
78
|
+
return targets;
|
|
79
|
+
}
|
|
80
|
+
const KNOWN_LITERAL_VARS = new Set([
|
|
81
|
+
'$HOME',
|
|
82
|
+
'${HOME}',
|
|
83
|
+
'$PWD',
|
|
84
|
+
'${PWD}',
|
|
85
|
+
'$USER',
|
|
86
|
+
'${USER}',
|
|
87
|
+
]);
|
|
88
|
+
/**
|
|
89
|
+
* True when the command uses command substitution `$(...)` / backticks,
|
|
90
|
+
* or a non-literal variable reference (anything other than the well-
|
|
91
|
+
* known `$HOME` / `$USER` / `$PWD` which the catastrophic-path rule
|
|
92
|
+
* already understands).
|
|
93
|
+
*/
|
|
94
|
+
function hasObfuscation(command) {
|
|
95
|
+
if (/\$\([^)]*\)/.test(command))
|
|
96
|
+
return true;
|
|
97
|
+
if (/`[^`]*`/.test(command))
|
|
98
|
+
return true;
|
|
99
|
+
// Match each $VAR / ${VAR} occurrence and decide whether it is one
|
|
100
|
+
// of the literal allowlist.
|
|
101
|
+
const re = /\$\{?[A-Za-z_][A-Za-z0-9_]*\}?/g;
|
|
102
|
+
for (const m of command.matchAll(re)) {
|
|
103
|
+
const tok = m[0];
|
|
104
|
+
if (!KNOWN_LITERAL_VARS.has(tok))
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
return false;
|
|
108
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preventBashSecretWrite.d.ts","sourceRoot":"","sources":["../../../src/deterministic/rules/preventBashSecretWrite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAe,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"preventBashSecretWrite.d.ts","sourceRoot":"","sources":["../../../src/deterministic/rules/preventBashSecretWrite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAe,MAAM,UAAU,CAAA;AA6DzD,eAAO,MAAM,sBAAsB,EAAE,iBAsBpC,CAAA"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.preventBashSecretWrite = void 0;
|
|
4
|
+
const bashAnalysis_1 = require("../bashAnalysis");
|
|
4
5
|
const TEMPLATE_SUFFIXES = ['.example', '.sample', '.template', '.dist'];
|
|
5
6
|
function basename(path) {
|
|
6
7
|
const cleaned = path.replace(/[\s'"]+$/, '');
|
|
@@ -30,18 +31,18 @@ function isSecretTargetPath(rawPath) {
|
|
|
30
31
|
return false;
|
|
31
32
|
}
|
|
32
33
|
/**
|
|
33
|
-
* Extracts
|
|
34
|
-
*
|
|
34
|
+
* Extracts every file the command writes to:
|
|
35
|
+
* 1. Standard redirects (`>`, `>>`, `1>`, `2>`, `&>`)
|
|
36
|
+
* 2. `tee [-a] FILE [FILE ...]`
|
|
37
|
+
* 3. Heredoc redirects (`cat <<EOF > file`)
|
|
35
38
|
*/
|
|
36
39
|
function extractWriteTargets(command) {
|
|
37
40
|
const targets = [];
|
|
38
|
-
// Redirect operators: >, >>, &>, &>>, 1>, 2> etc.
|
|
39
41
|
const redirectRe = /[12&]?>>?\s*([^\s;|&<>]+)/g;
|
|
40
42
|
for (const m of command.matchAll(redirectRe)) {
|
|
41
43
|
if (m[1])
|
|
42
44
|
targets.push(m[1]);
|
|
43
45
|
}
|
|
44
|
-
// tee [-a] FILE [FILE ...]
|
|
45
46
|
const teeRe = /\btee\b(?:\s+-[A-Za-z]+)*\s+([^\s;|&<>]+(?:\s+[^\s;|&<>]+)*)/g;
|
|
46
47
|
for (const m of command.matchAll(teeRe)) {
|
|
47
48
|
if (m[1]) {
|
|
@@ -51,6 +52,9 @@ function extractWriteTargets(command) {
|
|
|
51
52
|
}
|
|
52
53
|
}
|
|
53
54
|
}
|
|
55
|
+
for (const t of (0, bashAnalysis_1.extractHeredocTargets)(command)) {
|
|
56
|
+
targets.push(t);
|
|
57
|
+
}
|
|
54
58
|
return targets;
|
|
55
59
|
}
|
|
56
60
|
exports.preventBashSecretWrite = {
|
|
@@ -61,13 +65,17 @@ exports.preventBashSecretWrite = {
|
|
|
61
65
|
const command = toolInput.command;
|
|
62
66
|
if (typeof command !== 'string')
|
|
63
67
|
return { kind: 'allow' };
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
// Inspect each top-level statement; heredoc spans newlines so the
|
|
69
|
+
// statement splitter preserves the heredoc body inside its segment.
|
|
70
|
+
for (const stmt of (0, bashAnalysis_1.splitStatements)(command)) {
|
|
71
|
+
const targets = extractWriteTargets(stmt);
|
|
72
|
+
for (const target of targets) {
|
|
73
|
+
if (isSecretTargetPath(target)) {
|
|
74
|
+
return {
|
|
75
|
+
kind: 'block',
|
|
76
|
+
reason: `Refusing to write to a likely secret/credential file via shell redirect: ${target}. If this is intentional, run the command manually outside of the agent.`,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
71
79
|
}
|
|
72
80
|
}
|
|
73
81
|
return { kind: 'allow' };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"preventRmRfRoot.d.ts","sourceRoot":"","sources":["../../../src/deterministic/rules/preventRmRfRoot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAe,MAAM,UAAU,CAAA;
|
|
1
|
+
{"version":3,"file":"preventRmRfRoot.d.ts","sourceRoot":"","sources":["../../../src/deterministic/rules/preventRmRfRoot.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAe,MAAM,UAAU,CAAA;AA0EzD,eAAO,MAAM,eAAe,EAAE,iBAa7B,CAAA"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.preventRmRfRoot = void 0;
|
|
4
|
+
const bashAnalysis_1 = require("../bashAnalysis");
|
|
4
5
|
const CATASTROPHIC_TARGETS = new Set([
|
|
5
6
|
'/',
|
|
6
7
|
'$HOME',
|
|
@@ -44,6 +45,29 @@ function extractTargets(command) {
|
|
|
44
45
|
const tokens = trimmed.split(/\s+/);
|
|
45
46
|
return tokens.slice(1).filter((t) => !t.startsWith('-'));
|
|
46
47
|
}
|
|
48
|
+
function evaluateStatement(stmt) {
|
|
49
|
+
if (!isRecursiveForceRm(stmt))
|
|
50
|
+
return { kind: 'allow' };
|
|
51
|
+
// Substitution / unresolved variable targets cannot be evaluated
|
|
52
|
+
// statically. Treat them as suspicious: we cannot prove they are not
|
|
53
|
+
// catastrophic, so block.
|
|
54
|
+
if ((0, bashAnalysis_1.hasObfuscation)(stmt)) {
|
|
55
|
+
return {
|
|
56
|
+
kind: 'block',
|
|
57
|
+
reason: 'Refusing recursive rm whose target is a command substitution or unresolved variable. The target cannot be evaluated statically, so the operation is rejected as a safety precaution.',
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
const targets = extractTargets(stmt);
|
|
61
|
+
for (const target of targets) {
|
|
62
|
+
if (CATASTROPHIC_TARGETS.has(target)) {
|
|
63
|
+
return {
|
|
64
|
+
kind: 'block',
|
|
65
|
+
reason: `Refusing to run recursive rm on a catastrophic path: ${target}. If this is genuinely intended, run the command manually outside of the agent.`,
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return { kind: 'allow' };
|
|
70
|
+
}
|
|
47
71
|
exports.preventRmRfRoot = {
|
|
48
72
|
id: 'prevent-rm-rf-root',
|
|
49
73
|
check(toolName, toolInput) {
|
|
@@ -52,16 +76,10 @@ exports.preventRmRfRoot = {
|
|
|
52
76
|
const command = toolInput.command;
|
|
53
77
|
if (typeof command !== 'string')
|
|
54
78
|
return { kind: 'allow' };
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
if (CATASTROPHIC_TARGETS.has(target)) {
|
|
60
|
-
return {
|
|
61
|
-
kind: 'block',
|
|
62
|
-
reason: `Refusing to run recursive rm on a catastrophic path: ${target}. If this is genuinely intended, run the command manually outside of the agent.`,
|
|
63
|
-
};
|
|
64
|
-
}
|
|
79
|
+
for (const stmt of (0, bashAnalysis_1.splitStatements)(command)) {
|
|
80
|
+
const v = evaluateStatement(stmt);
|
|
81
|
+
if (v.kind === 'block')
|
|
82
|
+
return v;
|
|
65
83
|
}
|
|
66
84
|
return { kind: 'allow' };
|
|
67
85
|
},
|
|
@@ -6,6 +6,7 @@ import { Config } from '../config/Config';
|
|
|
6
6
|
import { DeterministicRule } from '../deterministic/types';
|
|
7
7
|
import { Adapter } from '../adapters/Adapter';
|
|
8
8
|
import { AgentGateConfig } from '../config/AgentGateConfig';
|
|
9
|
+
import { DecisionCache } from '../cache/DecisionCache';
|
|
9
10
|
import { EventBus } from '../observability/eventBus';
|
|
10
11
|
export interface CooldownStore {
|
|
11
12
|
getLastTime(key: string): number;
|
|
@@ -32,6 +33,12 @@ export interface ProcessHookDataDeps {
|
|
|
32
33
|
enabled: boolean;
|
|
33
34
|
path?: string;
|
|
34
35
|
};
|
|
36
|
+
/**
|
|
37
|
+
* Optional in-process decision cache. Most useful in daemon mode where
|
|
38
|
+
* the cache survives across hook invocations and short-circuits the
|
|
39
|
+
* entire pipeline on a hit.
|
|
40
|
+
*/
|
|
41
|
+
cache?: DecisionCache;
|
|
35
42
|
}
|
|
36
43
|
export declare function processHookData(input: string, deps?: ProcessHookDataDeps): Promise<ValidationResult>;
|
|
37
44
|
//# sourceMappingURL=processHookData.d.ts.map
|
|
@@ -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;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;
|
|
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,aAAa,EAAE,MAAM,wBAAwB,CAAA;AACtD,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;IAC7C;;;;OAIG;IACH,KAAK,CAAC,EAAE,aAAa,CAAA;CACtB;AAaD,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,EACb,IAAI,CAAC,EAAE,mBAAmB,GACzB,OAAO,CAAC,gBAAgB,CAAC,CA0K3B"}
|
|
@@ -68,6 +68,18 @@ async function processHookData(input, deps) {
|
|
|
68
68
|
const { toolName, toolInput } = parsed.action;
|
|
69
69
|
// Resolve cwd early so the agent-gate config can be loaded relative to it.
|
|
70
70
|
const cwd = deps?.cwd ?? process.cwd();
|
|
71
|
+
// Cache lookup: if a recent verdict for the exact same (adapter, tool,
|
|
72
|
+
// input, cwd) is still valid, return it without re-running the pipeline.
|
|
73
|
+
if (deps?.cache) {
|
|
74
|
+
const cached = deps.cache.get({
|
|
75
|
+
adapter: adapter.id,
|
|
76
|
+
toolName,
|
|
77
|
+
toolInput,
|
|
78
|
+
cwd,
|
|
79
|
+
});
|
|
80
|
+
if (cached)
|
|
81
|
+
return cached;
|
|
82
|
+
}
|
|
71
83
|
const agentGateConfig = deps?.agentGateConfig ?? (0, AgentGateConfig_1.loadAgentGateConfig)(cwd);
|
|
72
84
|
// Build SessionContext (history from adapter, projectRoot defaults to cwd
|
|
73
85
|
// for v1; future work can detect the actual project root).
|
|
@@ -108,7 +120,14 @@ async function processHookData(input, deps) {
|
|
|
108
120
|
source: 'deterministic',
|
|
109
121
|
ruleId: ruleVerdict.ruleId,
|
|
110
122
|
});
|
|
111
|
-
|
|
123
|
+
const verdict = {
|
|
124
|
+
decision: 'block',
|
|
125
|
+
reason: ruleVerdict.reason,
|
|
126
|
+
};
|
|
127
|
+
if (deps?.cache) {
|
|
128
|
+
deps.cache.set({ adapter: adapter.id, toolName, toolInput, cwd }, verdict);
|
|
129
|
+
}
|
|
130
|
+
return verdict;
|
|
112
131
|
}
|
|
113
132
|
// Cooldown check (file-based, persists across process invocations)
|
|
114
133
|
const cooldownStore = config.cooldown > 0
|
|
@@ -125,6 +144,9 @@ async function processHookData(input, deps) {
|
|
|
125
144
|
const collect = deps?.collectFn ?? collectRuleSources_1.collectRuleSources;
|
|
126
145
|
const rules = collect(cwd);
|
|
127
146
|
if (rules.length === 0) {
|
|
147
|
+
if (deps?.cache) {
|
|
148
|
+
deps.cache.set({ adapter: adapter.id, toolName, toolInput, cwd }, PASS);
|
|
149
|
+
}
|
|
128
150
|
return PASS;
|
|
129
151
|
}
|
|
130
152
|
// Get model client
|
|
@@ -165,6 +187,9 @@ async function processHookData(input, deps) {
|
|
|
165
187
|
reason: result.reason,
|
|
166
188
|
source,
|
|
167
189
|
});
|
|
190
|
+
if (deps?.cache) {
|
|
191
|
+
deps.cache.set({ adapter: adapter.id, toolName, toolInput, cwd }, result);
|
|
192
|
+
}
|
|
168
193
|
return result;
|
|
169
194
|
}
|
|
170
195
|
function createModelClient(config, cwd) {
|
package/dist/index.d.ts
CHANGED
|
@@ -25,6 +25,8 @@ export type { AgentSdkClientOptions, AgentSdkQueryFn, } from './validation/model
|
|
|
25
25
|
export { lintRuleSources } from './doctor/lintRuleSources';
|
|
26
26
|
export { formatFindings } from './doctor/formatFindings';
|
|
27
27
|
export type { Finding, FindingCode, Severity, } from './doctor/findings';
|
|
28
|
+
export { DecisionCache } from './cache/DecisionCache';
|
|
29
|
+
export type { CacheKey, DecisionCacheOptions, } from './cache/DecisionCache';
|
|
28
30
|
export { DaemonServer } from './daemon/server';
|
|
29
31
|
export type { DaemonServerOptions, DaemonHandler } from './daemon/server';
|
|
30
32
|
export { sendToDaemon } from './daemon/client';
|
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;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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
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.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;
|
|
3
|
+
exports.processHookData = exports.validator = exports.collectRuleSources = exports.JsonlFileSink = exports.EventBus = exports.defaultDaemonSocketPath = exports.sendToDaemon = exports.DaemonServer = exports.DecisionCache = 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; } });
|
|
@@ -35,6 +35,9 @@ var lintRuleSources_1 = require("./doctor/lintRuleSources");
|
|
|
35
35
|
Object.defineProperty(exports, "lintRuleSources", { enumerable: true, get: function () { return lintRuleSources_1.lintRuleSources; } });
|
|
36
36
|
var formatFindings_1 = require("./doctor/formatFindings");
|
|
37
37
|
Object.defineProperty(exports, "formatFindings", { enumerable: true, get: function () { return formatFindings_1.formatFindings; } });
|
|
38
|
+
// Cache
|
|
39
|
+
var DecisionCache_1 = require("./cache/DecisionCache");
|
|
40
|
+
Object.defineProperty(exports, "DecisionCache", { enumerable: true, get: function () { return DecisionCache_1.DecisionCache; } });
|
|
38
41
|
// Daemon
|
|
39
42
|
var server_1 = require("./daemon/server");
|
|
40
43
|
Object.defineProperty(exports, "DaemonServer", { enumerable: true, get: function () { return server_1.DaemonServer; } });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hiro-c/agent-gate",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.4.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",
|