@hiro-c/agent-gate 1.0.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.
Files changed (154) hide show
  1. package/AGENTS.md +76 -0
  2. package/LICENSE +21 -0
  3. package/README.md +205 -0
  4. package/dist/adapters/Adapter.d.ts +32 -0
  5. package/dist/adapters/Adapter.d.ts.map +1 -0
  6. package/dist/adapters/Adapter.js +2 -0
  7. package/dist/adapters/claude-code/adapter.d.ts +3 -0
  8. package/dist/adapters/claude-code/adapter.d.ts.map +1 -0
  9. package/dist/adapters/claude-code/adapter.js +45 -0
  10. package/dist/adapters/claude-code/transcript.d.ts +16 -0
  11. package/dist/adapters/claude-code/transcript.d.ts.map +1 -0
  12. package/dist/adapters/claude-code/transcript.js +104 -0
  13. package/dist/adapters/cursor/adapter.d.ts +3 -0
  14. package/dist/adapters/cursor/adapter.d.ts.map +1 -0
  15. package/dist/adapters/cursor/adapter.js +89 -0
  16. package/dist/adapters/index.d.ts +8 -0
  17. package/dist/adapters/index.d.ts.map +1 -0
  18. package/dist/adapters/index.js +20 -0
  19. package/dist/cli/agent-gate.d.ts +13 -0
  20. package/dist/cli/agent-gate.d.ts.map +1 -0
  21. package/dist/cli/agent-gate.js +226 -0
  22. package/dist/cli/installer.d.ts +21 -0
  23. package/dist/cli/installer.d.ts.map +1 -0
  24. package/dist/cli/installer.js +71 -0
  25. package/dist/collector/collectClaudeMd.d.ts +3 -0
  26. package/dist/collector/collectClaudeMd.d.ts.map +1 -0
  27. package/dist/collector/collectClaudeMd.js +87 -0
  28. package/dist/collector/collectRuleSources.d.ts +3 -0
  29. package/dist/collector/collectRuleSources.d.ts.map +1 -0
  30. package/dist/collector/collectRuleSources.js +151 -0
  31. package/dist/config/AgentGateConfig.d.ts +18 -0
  32. package/dist/config/AgentGateConfig.d.ts.map +1 -0
  33. package/dist/config/AgentGateConfig.js +34 -0
  34. package/dist/config/Config.d.ts +26 -0
  35. package/dist/config/Config.d.ts.map +1 -0
  36. package/dist/config/Config.js +25 -0
  37. package/dist/config/PluginConfigLoader.d.ts +3 -0
  38. package/dist/config/PluginConfigLoader.d.ts.map +1 -0
  39. package/dist/config/PluginConfigLoader.js +85 -0
  40. package/dist/config/defineConfig.d.ts +23 -0
  41. package/dist/config/defineConfig.d.ts.map +1 -0
  42. package/dist/config/defineConfig.js +10 -0
  43. package/dist/contracts/schemas/hookDataSchema.d.ts +7 -0
  44. package/dist/contracts/schemas/hookDataSchema.d.ts.map +1 -0
  45. package/dist/contracts/schemas/hookDataSchema.js +9 -0
  46. package/dist/contracts/types/Action.d.ts +23 -0
  47. package/dist/contracts/types/Action.d.ts.map +1 -0
  48. package/dist/contracts/types/Action.js +2 -0
  49. package/dist/contracts/types/ClaudeMdFile.d.ts +5 -0
  50. package/dist/contracts/types/ClaudeMdFile.d.ts.map +1 -0
  51. package/dist/contracts/types/ClaudeMdFile.js +2 -0
  52. package/dist/contracts/types/HookData.d.ts +6 -0
  53. package/dist/contracts/types/HookData.d.ts.map +1 -0
  54. package/dist/contracts/types/HookData.js +2 -0
  55. package/dist/contracts/types/ModelClient.d.ts +4 -0
  56. package/dist/contracts/types/ModelClient.d.ts.map +1 -0
  57. package/dist/contracts/types/ModelClient.js +2 -0
  58. package/dist/contracts/types/RuleSource.d.ts +7 -0
  59. package/dist/contracts/types/RuleSource.d.ts.map +1 -0
  60. package/dist/contracts/types/RuleSource.js +2 -0
  61. package/dist/contracts/types/SessionContext.d.ts +25 -0
  62. package/dist/contracts/types/SessionContext.d.ts.map +1 -0
  63. package/dist/contracts/types/SessionContext.js +2 -0
  64. package/dist/contracts/types/ValidationResult.d.ts +5 -0
  65. package/dist/contracts/types/ValidationResult.d.ts.map +1 -0
  66. package/dist/contracts/types/ValidationResult.js +2 -0
  67. package/dist/daemon/client.d.ts +17 -0
  68. package/dist/daemon/client.d.ts.map +1 -0
  69. package/dist/daemon/client.js +59 -0
  70. package/dist/daemon/protocol.d.ts +17 -0
  71. package/dist/daemon/protocol.d.ts.map +1 -0
  72. package/dist/daemon/protocol.js +8 -0
  73. package/dist/daemon/server.d.ts +27 -0
  74. package/dist/daemon/server.d.ts.map +1 -0
  75. package/dist/daemon/server.js +100 -0
  76. package/dist/deterministic/defaultRules.d.ts +11 -0
  77. package/dist/deterministic/defaultRules.d.ts.map +1 -0
  78. package/dist/deterministic/defaultRules.js +33 -0
  79. package/dist/deterministic/engine.d.ts +11 -0
  80. package/dist/deterministic/engine.d.ts.map +1 -0
  81. package/dist/deterministic/engine.js +12 -0
  82. package/dist/deterministic/factories.d.ts +20 -0
  83. package/dist/deterministic/factories.d.ts.map +1 -0
  84. package/dist/deterministic/factories.js +56 -0
  85. package/dist/deterministic/rules/preventBashSecretWrite.d.ts +3 -0
  86. package/dist/deterministic/rules/preventBashSecretWrite.d.ts.map +1 -0
  87. package/dist/deterministic/rules/preventBashSecretWrite.js +75 -0
  88. package/dist/deterministic/rules/preventForcePushMain.d.ts +7 -0
  89. package/dist/deterministic/rules/preventForcePushMain.d.ts.map +1 -0
  90. package/dist/deterministic/rules/preventForcePushMain.js +85 -0
  91. package/dist/deterministic/rules/preventRmRfRoot.d.ts +3 -0
  92. package/dist/deterministic/rules/preventRmRfRoot.d.ts.map +1 -0
  93. package/dist/deterministic/rules/preventRmRfRoot.js +68 -0
  94. package/dist/deterministic/rules/preventSecretFileWrite.d.ts +7 -0
  95. package/dist/deterministic/rules/preventSecretFileWrite.d.ts.map +1 -0
  96. package/dist/deterministic/rules/preventSecretFileWrite.js +55 -0
  97. package/dist/deterministic/rules/preventSystemPathWrite.d.ts +3 -0
  98. package/dist/deterministic/rules/preventSystemPathWrite.d.ts.map +1 -0
  99. package/dist/deterministic/rules/preventSystemPathWrite.js +38 -0
  100. package/dist/deterministic/types.d.ts +20 -0
  101. package/dist/deterministic/types.d.ts.map +1 -0
  102. package/dist/deterministic/types.js +2 -0
  103. package/dist/doctor/findings.d.ts +15 -0
  104. package/dist/doctor/findings.d.ts.map +1 -0
  105. package/dist/doctor/findings.js +2 -0
  106. package/dist/doctor/formatFindings.d.ts +3 -0
  107. package/dist/doctor/formatFindings.d.ts.map +1 -0
  108. package/dist/doctor/formatFindings.js +37 -0
  109. package/dist/doctor/lintRuleSources.d.ts +4 -0
  110. package/dist/doctor/lintRuleSources.d.ts.map +1 -0
  111. package/dist/doctor/lintRuleSources.js +87 -0
  112. package/dist/hooks/processHookData.d.ts +37 -0
  113. package/dist/hooks/processHookData.d.ts.map +1 -0
  114. package/dist/hooks/processHookData.js +181 -0
  115. package/dist/index.d.ts +38 -0
  116. package/dist/index.d.ts.map +1 -0
  117. package/dist/index.js +54 -0
  118. package/dist/observability/decisionLogger.d.ts +15 -0
  119. package/dist/observability/decisionLogger.d.ts.map +1 -0
  120. package/dist/observability/decisionLogger.js +20 -0
  121. package/dist/observability/eventBus.d.ts +15 -0
  122. package/dist/observability/eventBus.d.ts.map +1 -0
  123. package/dist/observability/eventBus.js +33 -0
  124. package/dist/observability/sinks/JsonlFileSink.d.ts +13 -0
  125. package/dist/observability/sinks/JsonlFileSink.d.ts.map +1 -0
  126. package/dist/observability/sinks/JsonlFileSink.js +36 -0
  127. package/dist/observability/sinks/Sink.d.ts +46 -0
  128. package/dist/observability/sinks/Sink.d.ts.map +1 -0
  129. package/dist/observability/sinks/Sink.js +2 -0
  130. package/dist/observability/stats.d.ts +14 -0
  131. package/dist/observability/stats.d.ts.map +1 -0
  132. package/dist/observability/stats.js +78 -0
  133. package/dist/validation/models/AnthropicApi.d.ts +9 -0
  134. package/dist/validation/models/AnthropicApi.d.ts.map +1 -0
  135. package/dist/validation/models/AnthropicApi.js +44 -0
  136. package/dist/validation/models/ClaudeCli.d.ts +10 -0
  137. package/dist/validation/models/ClaudeCli.d.ts.map +1 -0
  138. package/dist/validation/models/ClaudeCli.js +64 -0
  139. package/dist/validation/models/CompositeModelClient.d.ts +20 -0
  140. package/dist/validation/models/CompositeModelClient.d.ts.map +1 -0
  141. package/dist/validation/models/CompositeModelClient.js +53 -0
  142. package/dist/validation/prompts/context.d.ts +3 -0
  143. package/dist/validation/prompts/context.d.ts.map +1 -0
  144. package/dist/validation/prompts/context.js +29 -0
  145. package/dist/validation/prompts/response.d.ts +2 -0
  146. package/dist/validation/prompts/response.d.ts.map +1 -0
  147. package/dist/validation/prompts/response.js +20 -0
  148. package/dist/validation/prompts/system-prompt.d.ts +7 -0
  149. package/dist/validation/prompts/system-prompt.d.ts.map +1 -0
  150. package/dist/validation/prompts/system-prompt.js +69 -0
  151. package/dist/validation/validator.d.ts +5 -0
  152. package/dist/validation/validator.d.ts.map +1 -0
  153. package/dist/validation/validator.js +98 -0
  154. package/package.json +67 -0
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.lintRuleSources = lintRuleSources;
4
+ const AMBIGUOUS_PATTERNS = [
5
+ { regex: /\bwhere possible\b/i, label: 'where possible' },
6
+ { regex: /\bas (?:needed|appropriate)\b/i, label: 'as needed/appropriate' },
7
+ { regex: /\bwhen appropriate\b/i, label: 'when appropriate' },
8
+ { regex: /\bif possible\b/i, label: 'if possible' },
9
+ { regex: /\bshould consider\b/i, label: 'should consider' },
10
+ { regex: /\bgenerally speaking\b/i, label: 'generally speaking' },
11
+ { regex: /\btry to\b/i, label: 'try to' },
12
+ { regex: /適切に/, label: '適切に' },
13
+ { regex: /なるべく/, label: 'なるべく' },
14
+ { regex: /可能な限り/, label: '可能な限り' },
15
+ { regex: /必要に応じて/, label: '必要に応じて' },
16
+ { regex: /できれば/, label: 'できれば' },
17
+ { regex: /状況に応じて/, label: '状況に応じて' },
18
+ ];
19
+ const IMPERATIVE_HINTS = [
20
+ /^[\s\-*\d.]*(?:never|always|do not|don[''']t|must|should|use|avoid|prefer|run|check)\b/i,
21
+ /^\s*[-*]\s+/, // bullet items
22
+ /^\s*\d+\.\s+/, // numbered items
23
+ /禁止|必須|常に|決して|してはいけない|すること|してください|する/, // Japanese imperatives
24
+ ];
25
+ function isEmpty(content) {
26
+ return content.trim().length === 0;
27
+ }
28
+ function hasConcreteRules(content) {
29
+ const lines = content.split('\n');
30
+ return lines.some((line) => IMPERATIVE_HINTS.some((re) => re.test(line)));
31
+ }
32
+ function findAmbiguousLines(content) {
33
+ const out = [];
34
+ const lines = content.split('\n');
35
+ for (let i = 0; i < lines.length; i++) {
36
+ const line = lines[i];
37
+ for (const { regex, label } of AMBIGUOUS_PATTERNS) {
38
+ if (regex.test(line)) {
39
+ out.push({ line: i + 1, match: label });
40
+ break;
41
+ }
42
+ }
43
+ }
44
+ return out;
45
+ }
46
+ function trimExcerpt(line) {
47
+ const t = line.trim();
48
+ return t.length > 80 ? t.slice(0, 77) + '...' : t;
49
+ }
50
+ function lintRuleSources(sources) {
51
+ const findings = [];
52
+ for (const source of sources) {
53
+ if (isEmpty(source.content)) {
54
+ findings.push({
55
+ ruleSourcePath: source.path,
56
+ ruleSourceKind: source.kind,
57
+ severity: 'warning',
58
+ code: 'empty-file',
59
+ message: 'Instruction file is empty. Either remove it or fill in concrete project rules so the AI has something to enforce.',
60
+ });
61
+ continue;
62
+ }
63
+ if (!hasConcreteRules(source.content)) {
64
+ findings.push({
65
+ ruleSourcePath: source.path,
66
+ ruleSourceKind: source.kind,
67
+ severity: 'warning',
68
+ code: 'no-concrete-rules',
69
+ message: 'No imperatives (must / should / never / "use X") or bulleted/numbered items detected. The AI may struggle to extract actionable rules.',
70
+ });
71
+ }
72
+ const ambiguousLines = findAmbiguousLines(source.content);
73
+ for (const a of ambiguousLines) {
74
+ const excerpt = trimExcerpt(source.content.split('\n')[a.line - 1] ?? '');
75
+ findings.push({
76
+ ruleSourcePath: source.path,
77
+ ruleSourceKind: source.kind,
78
+ severity: 'info',
79
+ code: 'ambiguous-modifier',
80
+ message: `Ambiguous modifier "${a.match}" makes the rule hard for AI to enforce reliably. Replace with a concrete condition or threshold.`,
81
+ line: a.line,
82
+ excerpt,
83
+ });
84
+ }
85
+ }
86
+ return findings;
87
+ }
@@ -0,0 +1,37 @@
1
+ import { ValidationResult } from '../contracts/types/ValidationResult';
2
+ import { RuleSource } from '../contracts/types/RuleSource';
3
+ import { IModelClient } from '../contracts/types/ModelClient';
4
+ import { validator } from '../validation/validator';
5
+ import { Config } from '../config/Config';
6
+ import { DeterministicRule } from '../deterministic/types';
7
+ import { Adapter } from '../adapters/Adapter';
8
+ import { AgentGateConfig } from '../config/AgentGateConfig';
9
+ import { EventBus } from '../observability/eventBus';
10
+ export interface CooldownStore {
11
+ getLastTime(key: string): number;
12
+ setLastTime(key: string, time: number): void;
13
+ }
14
+ export interface ProcessHookDataDeps {
15
+ config?: Config;
16
+ agentGateConfig?: AgentGateConfig;
17
+ collectFn?: (cwd: string) => RuleSource[];
18
+ validatorFn?: typeof validator;
19
+ getModelClient?: (config: Config) => IModelClient;
20
+ cooldownStore?: CooldownStore;
21
+ cwd?: string;
22
+ deterministicRules?: DeterministicRule[];
23
+ adapter?: Adapter;
24
+ /**
25
+ * Pre-configured event bus. When omitted, a default bus is built
26
+ * and a JsonlFileSink is auto-subscribed iff AGENT_GATE_LOG=1 (or
27
+ * `logging.enabled` is true).
28
+ */
29
+ eventBus?: EventBus;
30
+ /** Override logging behavior. When provided, replaces env detection. */
31
+ logging?: {
32
+ enabled: boolean;
33
+ path?: string;
34
+ };
35
+ }
36
+ export declare function processHookData(input: string, deps?: ProcessHookDataDeps): Promise<ValidationResult>;
37
+ //# sourceMappingURL=processHookData.d.ts.map
@@ -0,0 +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;AAIzC,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,CAoI3B"}
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.processHookData = processHookData;
4
+ const fs_1 = require("fs");
5
+ const crypto_1 = require("crypto");
6
+ const path_1 = require("path");
7
+ const os_1 = require("os");
8
+ const collectRuleSources_1 = require("../collector/collectRuleSources");
9
+ const validator_1 = require("../validation/validator");
10
+ const Config_1 = require("../config/Config");
11
+ const ClaudeCli_1 = require("../validation/models/ClaudeCli");
12
+ const AnthropicApi_1 = require("../validation/models/AnthropicApi");
13
+ const CompositeModelClient_1 = require("../validation/models/CompositeModelClient");
14
+ const engine_1 = require("../deterministic/engine");
15
+ const defaultRules_1 = require("../deterministic/defaultRules");
16
+ const adapter_1 = require("../adapters/claude-code/adapter");
17
+ const AgentGateConfig_1 = require("../config/AgentGateConfig");
18
+ const decisionLogger_1 = require("../observability/decisionLogger");
19
+ const eventBus_1 = require("../observability/eventBus");
20
+ const JsonlFileSink_1 = require("../observability/sinks/JsonlFileSink");
21
+ const PASS = { decision: undefined, reason: '' };
22
+ const COOLDOWN_DIR_NAME = 'agent-gate';
23
+ class FileCooldownStore {
24
+ dir;
25
+ constructor() {
26
+ this.dir = (0, path_1.join)((0, os_1.tmpdir)(), COOLDOWN_DIR_NAME);
27
+ (0, fs_1.mkdirSync)(this.dir, { recursive: true });
28
+ }
29
+ stampPath(key) {
30
+ const hash = (0, crypto_1.createHash)('sha256').update(key).digest('hex');
31
+ return (0, path_1.join)(this.dir, hash);
32
+ }
33
+ getLastTime(key) {
34
+ try {
35
+ return parseInt((0, fs_1.readFileSync)(this.stampPath(key), 'utf-8'), 10) || 0;
36
+ }
37
+ catch {
38
+ return 0;
39
+ }
40
+ }
41
+ setLastTime(key, time) {
42
+ (0, fs_1.writeFileSync)(this.stampPath(key), String(time));
43
+ }
44
+ }
45
+ function resolveEventBus(deps) {
46
+ if (deps?.eventBus)
47
+ return deps.eventBus;
48
+ const bus = new eventBus_1.EventBus();
49
+ const enabled = deps?.logging ? deps.logging.enabled : (0, decisionLogger_1.isLoggingEnabled)();
50
+ if (enabled) {
51
+ const path = deps?.logging?.path ?? (0, decisionLogger_1.defaultLogPath)();
52
+ bus.subscribe(new JsonlFileSink_1.JsonlFileSink(path));
53
+ }
54
+ return bus;
55
+ }
56
+ async function processHookData(input, deps) {
57
+ const config = deps?.config ?? new Config_1.Config();
58
+ if (config.disabled) {
59
+ return PASS;
60
+ }
61
+ const adapter = deps?.adapter ?? adapter_1.claudeCodeAdapter;
62
+ const bus = resolveEventBus(deps);
63
+ const parsed = adapter.parseHook(input);
64
+ if (parsed.kind === 'skip') {
65
+ return PASS;
66
+ }
67
+ const { toolName, toolInput } = parsed.action;
68
+ // Resolve cwd early so the agent-gate config can be loaded relative to it.
69
+ const cwd = deps?.cwd ?? process.cwd();
70
+ const agentGateConfig = deps?.agentGateConfig ?? (0, AgentGateConfig_1.loadAgentGateConfig)(cwd);
71
+ // Build SessionContext (history from adapter, projectRoot defaults to cwd
72
+ // for v1; future work can detect the actual project root).
73
+ let history = [];
74
+ if (typeof adapter.readHistory === 'function') {
75
+ try {
76
+ history = await adapter.readHistory({ cwd, limit: 20 });
77
+ }
78
+ catch {
79
+ history = [];
80
+ }
81
+ }
82
+ const sessionContext = {
83
+ cwd,
84
+ projectRoot: cwd,
85
+ history,
86
+ };
87
+ // Deterministic rules: a fast, cheap safety baseline that runs before
88
+ // any cooldown or AI check.
89
+ const deterministicRules = deps?.deterministicRules ??
90
+ (0, defaultRules_1.buildDefaultDeterministicRules)(agentGateConfig);
91
+ const ruleVerdict = (0, engine_1.runDeterministicRules)(toolName, toolInput, deterministicRules, sessionContext);
92
+ if (ruleVerdict.kind === 'block') {
93
+ bus.emit({
94
+ type: 'rule.fired',
95
+ adapter: adapter.id,
96
+ toolName,
97
+ ruleId: ruleVerdict.ruleId,
98
+ decision: 'block',
99
+ reason: ruleVerdict.reason,
100
+ });
101
+ bus.emit({
102
+ type: 'verdict.decided',
103
+ adapter: adapter.id,
104
+ toolName,
105
+ decision: 'block',
106
+ reason: ruleVerdict.reason,
107
+ source: 'deterministic',
108
+ ruleId: ruleVerdict.ruleId,
109
+ });
110
+ return { decision: 'block', reason: ruleVerdict.reason };
111
+ }
112
+ // Cooldown check (file-based, persists across process invocations)
113
+ const cooldownStore = config.cooldown > 0
114
+ ? deps?.cooldownStore ?? new FileCooldownStore()
115
+ : undefined;
116
+ if (cooldownStore && config.cooldown > 0) {
117
+ const now = Math.floor(Date.now() / 1000);
118
+ const lastTime = cooldownStore.getLastTime(cwd);
119
+ if (now - lastTime < config.cooldown) {
120
+ return PASS;
121
+ }
122
+ }
123
+ // Collect rule sources (CLAUDE.md, AGENTS.md, .cursorrules, ...)
124
+ const collect = deps?.collectFn ?? collectRuleSources_1.collectRuleSources;
125
+ const rules = collect(cwd);
126
+ if (rules.length === 0) {
127
+ return PASS;
128
+ }
129
+ // Get model client
130
+ const getClient = deps?.getModelClient ?? ((c) => createModelClient(c, cwd));
131
+ const modelClient = getClient(config);
132
+ // Validate
133
+ const validate = deps?.validatorFn ?? validator_1.validator;
134
+ bus.emit({
135
+ type: 'ai.requested',
136
+ adapter: adapter.id,
137
+ toolName,
138
+ rulesCount: rules.length,
139
+ });
140
+ const aiStart = Date.now();
141
+ const result = await validate(rules, toolName, toolInput, modelClient);
142
+ const aiLatency = Date.now() - aiStart;
143
+ // Update cooldown timestamp AFTER successful validation
144
+ if (cooldownStore) {
145
+ cooldownStore.setLastTime(cwd, Math.floor(Date.now() / 1000));
146
+ }
147
+ const decision = result.decision === 'block' ? 'block' : 'allow';
148
+ const source = 'ai';
149
+ bus.emit({
150
+ type: 'ai.completed',
151
+ adapter: adapter.id,
152
+ toolName,
153
+ decision,
154
+ reason: result.reason,
155
+ latencyMs: aiLatency,
156
+ });
157
+ bus.emit({
158
+ type: 'verdict.decided',
159
+ adapter: adapter.id,
160
+ toolName,
161
+ decision,
162
+ reason: result.reason,
163
+ source,
164
+ });
165
+ return result;
166
+ }
167
+ function createModelClient(config, cwd) {
168
+ // Build a fallback chain: the most-preferred client comes first, with
169
+ // ClaudeCli as a secondary path. The CompositeModelClient tries each in
170
+ // order; if the first fails, the next runs. The validator's outer
171
+ // try/catch fail-opens when every client has failed.
172
+ const clients = [];
173
+ if (config.useApi) {
174
+ clients.push(new AnthropicApi_1.AnthropicApi(config));
175
+ }
176
+ clients.push(new ClaudeCli_1.ClaudeCli(config, cwd));
177
+ if (clients.length === 1) {
178
+ return clients[0];
179
+ }
180
+ return new CompositeModelClient_1.CompositeModelClient(clients);
181
+ }
@@ -0,0 +1,38 @@
1
+ export { Config } from './config/Config';
2
+ export type { ConfigOptions } from './config/Config';
3
+ export { defineConfig } from './config/defineConfig';
4
+ export type { AgentGatePluginConfig } from './config/defineConfig';
5
+ export type { AgentGateConfig } from './config/AgentGateConfig';
6
+ export { loadAgentGateConfig } from './config/AgentGateConfig';
7
+ export { loadPluginConfig } from './config/PluginConfigLoader';
8
+ export type { ValidationResult } from './contracts/types/ValidationResult';
9
+ export type { HookData } from './contracts/types/HookData';
10
+ export type { IModelClient } from './contracts/types/ModelClient';
11
+ export type { RuleSource, RuleSourceKind } from './contracts/types/RuleSource';
12
+ export type { Action, ParsedHook } from './contracts/types/Action';
13
+ export type { SessionContext, SessionEvent, } from './contracts/types/SessionContext';
14
+ export { HookDataSchema } from './contracts/schemas/hookDataSchema';
15
+ export type { DeterministicRule, RuleVerdict, } from './deterministic/types';
16
+ export { forbidCommandPattern, forbidContentPattern, forbidFilePathPattern, } from './deterministic/factories';
17
+ export { defaultDeterministicRules, buildDefaultDeterministicRules, } from './deterministic/defaultRules';
18
+ export { claudeCodeAdapter } from './adapters/claude-code/adapter';
19
+ export { cursorAdapter } from './adapters/cursor/adapter';
20
+ export type { Adapter, ReadHistoryOptions } from './adapters/Adapter';
21
+ export { CompositeModelClient } from './validation/models/CompositeModelClient';
22
+ export type { CompositeModelClientOptions } from './validation/models/CompositeModelClient';
23
+ export { lintRuleSources } from './doctor/lintRuleSources';
24
+ export { formatFindings } from './doctor/formatFindings';
25
+ export type { Finding, FindingCode, Severity, } from './doctor/findings';
26
+ export { DaemonServer } from './daemon/server';
27
+ export type { DaemonServerOptions, DaemonHandler } from './daemon/server';
28
+ export { sendToDaemon } from './daemon/client';
29
+ export type { SendToDaemonOptions } from './daemon/client';
30
+ export { defaultSocketPath as defaultDaemonSocketPath, } from './daemon/protocol';
31
+ export type { DaemonRequest, DaemonResponse, DaemonErrorResponse, } from './daemon/protocol';
32
+ export { EventBus } from './observability/eventBus';
33
+ export { JsonlFileSink } from './observability/sinks/JsonlFileSink';
34
+ export type { PipelineEvent, RuleFiredEvent, AiRequestedEvent, AiCompletedEvent, VerdictDecidedEvent, PipelineErrorEvent, Sink, } from './observability/sinks/Sink';
35
+ export { collectRuleSources } from './collector/collectRuleSources';
36
+ export { validator } from './validation/validator';
37
+ export { processHookData } from './hooks/processHookData';
38
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +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;AAG3F,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 ADDED
@@ -0,0 +1,54 @@
1
+ "use strict";
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;
4
+ // Config
5
+ var Config_1 = require("./config/Config");
6
+ Object.defineProperty(exports, "Config", { enumerable: true, get: function () { return Config_1.Config; } });
7
+ var defineConfig_1 = require("./config/defineConfig");
8
+ Object.defineProperty(exports, "defineConfig", { enumerable: true, get: function () { return defineConfig_1.defineConfig; } });
9
+ var AgentGateConfig_1 = require("./config/AgentGateConfig");
10
+ Object.defineProperty(exports, "loadAgentGateConfig", { enumerable: true, get: function () { return AgentGateConfig_1.loadAgentGateConfig; } });
11
+ var PluginConfigLoader_1 = require("./config/PluginConfigLoader");
12
+ Object.defineProperty(exports, "loadPluginConfig", { enumerable: true, get: function () { return PluginConfigLoader_1.loadPluginConfig; } });
13
+ // Schemas
14
+ var hookDataSchema_1 = require("./contracts/schemas/hookDataSchema");
15
+ Object.defineProperty(exports, "HookDataSchema", { enumerable: true, get: function () { return hookDataSchema_1.HookDataSchema; } });
16
+ var factories_1 = require("./deterministic/factories");
17
+ Object.defineProperty(exports, "forbidCommandPattern", { enumerable: true, get: function () { return factories_1.forbidCommandPattern; } });
18
+ Object.defineProperty(exports, "forbidContentPattern", { enumerable: true, get: function () { return factories_1.forbidContentPattern; } });
19
+ Object.defineProperty(exports, "forbidFilePathPattern", { enumerable: true, get: function () { return factories_1.forbidFilePathPattern; } });
20
+ var defaultRules_1 = require("./deterministic/defaultRules");
21
+ Object.defineProperty(exports, "defaultDeterministicRules", { enumerable: true, get: function () { return defaultRules_1.defaultDeterministicRules; } });
22
+ Object.defineProperty(exports, "buildDefaultDeterministicRules", { enumerable: true, get: function () { return defaultRules_1.buildDefaultDeterministicRules; } });
23
+ // Adapters
24
+ var adapter_1 = require("./adapters/claude-code/adapter");
25
+ Object.defineProperty(exports, "claudeCodeAdapter", { enumerable: true, get: function () { return adapter_1.claudeCodeAdapter; } });
26
+ var adapter_2 = require("./adapters/cursor/adapter");
27
+ Object.defineProperty(exports, "cursorAdapter", { enumerable: true, get: function () { return adapter_2.cursorAdapter; } });
28
+ // Model clients (for users wiring up custom AI fallback chains)
29
+ var CompositeModelClient_1 = require("./validation/models/CompositeModelClient");
30
+ Object.defineProperty(exports, "CompositeModelClient", { enumerable: true, get: function () { return CompositeModelClient_1.CompositeModelClient; } });
31
+ // Doctor (CLAUDE.md linter)
32
+ var lintRuleSources_1 = require("./doctor/lintRuleSources");
33
+ Object.defineProperty(exports, "lintRuleSources", { enumerable: true, get: function () { return lintRuleSources_1.lintRuleSources; } });
34
+ var formatFindings_1 = require("./doctor/formatFindings");
35
+ Object.defineProperty(exports, "formatFindings", { enumerable: true, get: function () { return formatFindings_1.formatFindings; } });
36
+ // Daemon
37
+ var server_1 = require("./daemon/server");
38
+ Object.defineProperty(exports, "DaemonServer", { enumerable: true, get: function () { return server_1.DaemonServer; } });
39
+ var client_1 = require("./daemon/client");
40
+ Object.defineProperty(exports, "sendToDaemon", { enumerable: true, get: function () { return client_1.sendToDaemon; } });
41
+ var protocol_1 = require("./daemon/protocol");
42
+ Object.defineProperty(exports, "defaultDaemonSocketPath", { enumerable: true, get: function () { return protocol_1.defaultSocketPath; } });
43
+ // Observability
44
+ var eventBus_1 = require("./observability/eventBus");
45
+ Object.defineProperty(exports, "EventBus", { enumerable: true, get: function () { return eventBus_1.EventBus; } });
46
+ var JsonlFileSink_1 = require("./observability/sinks/JsonlFileSink");
47
+ Object.defineProperty(exports, "JsonlFileSink", { enumerable: true, get: function () { return JsonlFileSink_1.JsonlFileSink; } });
48
+ // Core
49
+ var collectRuleSources_1 = require("./collector/collectRuleSources");
50
+ Object.defineProperty(exports, "collectRuleSources", { enumerable: true, get: function () { return collectRuleSources_1.collectRuleSources; } });
51
+ var validator_1 = require("./validation/validator");
52
+ Object.defineProperty(exports, "validator", { enumerable: true, get: function () { return validator_1.validator; } });
53
+ var processHookData_1 = require("./hooks/processHookData");
54
+ Object.defineProperty(exports, "processHookData", { enumerable: true, get: function () { return processHookData_1.processHookData; } });
@@ -0,0 +1,15 @@
1
+ export type DecisionSource = 'deterministic' | 'ai' | 'pass';
2
+ export interface DecisionLogEntry {
3
+ timestamp: string;
4
+ adapter: string;
5
+ toolName: string;
6
+ decision: 'block' | 'allow';
7
+ reason: string;
8
+ source: DecisionSource;
9
+ /** Rule id when the decision came from the deterministic engine. */
10
+ ruleId?: string;
11
+ }
12
+ export declare function defaultLogPath(): string;
13
+ export declare function appendDecision(entry: DecisionLogEntry, logPath?: string): void;
14
+ export declare function isLoggingEnabled(env?: NodeJS.ProcessEnv): boolean;
15
+ //# sourceMappingURL=decisionLogger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"decisionLogger.d.ts","sourceRoot":"","sources":["../../src/observability/decisionLogger.ts"],"names":[],"mappings":"AAIA,MAAM,MAAM,cAAc,GAAG,eAAe,GAAG,IAAI,GAAG,MAAM,CAAA;AAE5D,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAA;IACjB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,cAAc,CAAA;IACtB,oEAAoE;IACpE,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAKD,wBAAgB,cAAc,IAAI,MAAM,CAEvC;AAED,wBAAgB,cAAc,CAC5B,KAAK,EAAE,gBAAgB,EACvB,OAAO,GAAE,MAAyB,GACjC,IAAI,CAGN;AAED,wBAAgB,gBAAgB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,OAAO,CAE9E"}
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.defaultLogPath = defaultLogPath;
4
+ exports.appendDecision = appendDecision;
5
+ exports.isLoggingEnabled = isLoggingEnabled;
6
+ const fs_1 = require("fs");
7
+ const path_1 = require("path");
8
+ const os_1 = require("os");
9
+ const DEFAULT_LOG_DIR = '.agent-gate';
10
+ const DEFAULT_LOG_FILENAME = 'log.jsonl';
11
+ function defaultLogPath() {
12
+ return (0, path_1.join)((0, os_1.homedir)(), DEFAULT_LOG_DIR, DEFAULT_LOG_FILENAME);
13
+ }
14
+ function appendDecision(entry, logPath = defaultLogPath()) {
15
+ (0, fs_1.mkdirSync)((0, path_1.dirname)(logPath), { recursive: true });
16
+ (0, fs_1.appendFileSync)(logPath, JSON.stringify(entry) + '\n');
17
+ }
18
+ function isLoggingEnabled(env = process.env) {
19
+ return env.AGENT_GATE_LOG === '1' || env.AGENT_GATE_LOG === 'true';
20
+ }
@@ -0,0 +1,15 @@
1
+ import { PipelineEvent, Sink } from './sinks/Sink';
2
+ /**
3
+ * Tiny synchronous event bus for the pipeline. Sinks subscribe at
4
+ * pipeline construction time and receive every event emitted.
5
+ *
6
+ * The bus catches sink exceptions so a broken sink can never bring
7
+ * down the hook. Async sinks are allowed but the bus does not await
8
+ * them; observability is fire-and-forget by design.
9
+ */
10
+ export declare class EventBus {
11
+ private readonly sinks;
12
+ subscribe(sink: Sink): void;
13
+ emit(event: PipelineEvent): void;
14
+ }
15
+ //# sourceMappingURL=eventBus.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eventBus.d.ts","sourceRoot":"","sources":["../../src/observability/eventBus.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AAElD;;;;;;;GAOG;AACH,qBAAa,QAAQ;IACnB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAAa;IAEnC,SAAS,CAAC,IAAI,EAAE,IAAI,GAAG,IAAI;IAI3B,IAAI,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;CAcjC"}
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventBus = void 0;
4
+ /**
5
+ * Tiny synchronous event bus for the pipeline. Sinks subscribe at
6
+ * pipeline construction time and receive every event emitted.
7
+ *
8
+ * The bus catches sink exceptions so a broken sink can never bring
9
+ * down the hook. Async sinks are allowed but the bus does not await
10
+ * them; observability is fire-and-forget by design.
11
+ */
12
+ class EventBus {
13
+ sinks = [];
14
+ subscribe(sink) {
15
+ this.sinks.push(sink);
16
+ }
17
+ emit(event) {
18
+ for (const sink of this.sinks) {
19
+ try {
20
+ const r = sink.handle(event);
21
+ if (r instanceof Promise) {
22
+ r.catch(() => {
23
+ // swallow async sink failures
24
+ });
25
+ }
26
+ }
27
+ catch {
28
+ // swallow sync sink failures
29
+ }
30
+ }
31
+ }
32
+ }
33
+ exports.EventBus = EventBus;
@@ -0,0 +1,13 @@
1
+ import { PipelineEvent, Sink } from './Sink';
2
+ /**
3
+ * Persists verdict.decided events to a JSONL file (one event per line).
4
+ * Other event types are dropped. The output shape matches the legacy
5
+ * DecisionLogEntry so existing log readers and the `agent-gate stats`
6
+ * subcommand continue to work unchanged.
7
+ */
8
+ export declare class JsonlFileSink implements Sink {
9
+ private readonly path;
10
+ constructor(path: string);
11
+ handle(event: PipelineEvent): void;
12
+ }
13
+ //# sourceMappingURL=JsonlFileSink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"JsonlFileSink.d.ts","sourceRoot":"","sources":["../../../src/observability/sinks/JsonlFileSink.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAA;AAM5C;;;;;GAKG;AACH,qBAAa,aAAc,YAAW,IAAI;IAC5B,OAAO,CAAC,QAAQ,CAAC,IAAI;gBAAJ,IAAI,EAAE,MAAM;IAEzC,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI;CAiBnC"}
@@ -0,0 +1,36 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JsonlFileSink = void 0;
4
+ const decisionLogger_1 = require("../decisionLogger");
5
+ /**
6
+ * Persists verdict.decided events to a JSONL file (one event per line).
7
+ * Other event types are dropped. The output shape matches the legacy
8
+ * DecisionLogEntry so existing log readers and the `agent-gate stats`
9
+ * subcommand continue to work unchanged.
10
+ */
11
+ class JsonlFileSink {
12
+ path;
13
+ constructor(path) {
14
+ this.path = path;
15
+ }
16
+ handle(event) {
17
+ if (event.type !== 'verdict.decided')
18
+ return;
19
+ const entry = {
20
+ timestamp: new Date().toISOString(),
21
+ adapter: event.adapter,
22
+ toolName: event.toolName,
23
+ decision: event.decision,
24
+ reason: event.reason,
25
+ source: event.source,
26
+ ruleId: event.ruleId,
27
+ };
28
+ try {
29
+ (0, decisionLogger_1.appendDecision)(entry, this.path);
30
+ }
31
+ catch {
32
+ // sink swallows its own failures per Sink contract
33
+ }
34
+ }
35
+ }
36
+ exports.JsonlFileSink = JsonlFileSink;
@@ -0,0 +1,46 @@
1
+ export interface RuleFiredEvent {
2
+ type: 'rule.fired';
3
+ adapter: string;
4
+ toolName: string;
5
+ ruleId: string;
6
+ decision: 'block' | 'allow';
7
+ reason: string;
8
+ }
9
+ export interface AiRequestedEvent {
10
+ type: 'ai.requested';
11
+ adapter: string;
12
+ toolName: string;
13
+ rulesCount: number;
14
+ }
15
+ export interface AiCompletedEvent {
16
+ type: 'ai.completed';
17
+ adapter: string;
18
+ toolName: string;
19
+ decision: 'block' | 'allow';
20
+ reason: string;
21
+ latencyMs: number;
22
+ }
23
+ export interface VerdictDecidedEvent {
24
+ type: 'verdict.decided';
25
+ adapter: string;
26
+ toolName: string;
27
+ decision: 'block' | 'allow';
28
+ reason: string;
29
+ source: 'deterministic' | 'ai' | 'pass';
30
+ ruleId?: string;
31
+ }
32
+ export interface PipelineErrorEvent {
33
+ type: 'pipeline.error';
34
+ stage: string;
35
+ error: string;
36
+ }
37
+ export type PipelineEvent = RuleFiredEvent | AiRequestedEvent | AiCompletedEvent | VerdictDecidedEvent | PipelineErrorEvent;
38
+ export interface Sink {
39
+ /**
40
+ * Handle a single pipeline event. Sinks must be defensive: throwing
41
+ * here is caught by the EventBus and does not affect other sinks
42
+ * or the pipeline.
43
+ */
44
+ handle(event: PipelineEvent): void | Promise<void>;
45
+ }
46
+ //# sourceMappingURL=Sink.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sink.d.ts","sourceRoot":"","sources":["../../../src/observability/sinks/Sink.ts"],"names":[],"mappings":"AAAA,MAAM,WAAW,cAAc;IAC7B,IAAI,EAAE,YAAY,CAAA;IAClB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;CACf;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,cAAc,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,UAAU,EAAE,MAAM,CAAA;CACnB;AAED,MAAM,WAAW,gBAAgB;IAC/B,IAAI,EAAE,cAAc,CAAA;IACpB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,iBAAiB,CAAA;IACvB,OAAO,EAAE,MAAM,CAAA;IACf,QAAQ,EAAE,MAAM,CAAA;IAChB,QAAQ,EAAE,OAAO,GAAG,OAAO,CAAA;IAC3B,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,eAAe,GAAG,IAAI,GAAG,MAAM,CAAA;IACvC,MAAM,CAAC,EAAE,MAAM,CAAA;CAChB;AAED,MAAM,WAAW,kBAAkB;IACjC,IAAI,EAAE,gBAAgB,CAAA;IACtB,KAAK,EAAE,MAAM,CAAA;IACb,KAAK,EAAE,MAAM,CAAA;CACd;AAED,MAAM,MAAM,aAAa,GACrB,cAAc,GACd,gBAAgB,GAChB,gBAAgB,GAChB,mBAAmB,GACnB,kBAAkB,CAAA;AAEtB,MAAM,WAAW,IAAI;IACnB;;;;OAIG;IACH,MAAM,CAAC,KAAK,EAAE,aAAa,GAAG,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CACnD"}
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,14 @@
1
+ import { DecisionLogEntry, DecisionSource } from './decisionLogger';
2
+ export interface Stats {
3
+ total: number;
4
+ blocks: number;
5
+ allows: number;
6
+ byRuleId: Record<string, number>;
7
+ bySource: Record<DecisionSource, number>;
8
+ byAdapter: Record<string, number>;
9
+ byTool: Record<string, number>;
10
+ }
11
+ export declare function aggregateStats(entries: DecisionLogEntry[]): Stats;
12
+ export declare function readStats(logPath: string): Stats;
13
+ export declare function formatStats(stats: Stats): string;
14
+ //# sourceMappingURL=stats.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stats.d.ts","sourceRoot":"","sources":["../../src/observability/stats.ts"],"names":[],"mappings":"AACA,OAAO,EACL,gBAAgB,EAChB,cAAc,EACf,MAAM,kBAAkB,CAAA;AAEzB,MAAM,WAAW,KAAK;IACpB,KAAK,EAAE,MAAM,CAAA;IACb,MAAM,EAAE,MAAM,CAAA;IACd,MAAM,EAAE,MAAM,CAAA;IACd,QAAQ,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IAChC,QAAQ,EAAE,MAAM,CAAC,cAAc,EAAE,MAAM,CAAC,CAAA;IACxC,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;IACjC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CAC/B;AAkBD,wBAAgB,cAAc,CAAC,OAAO,EAAE,gBAAgB,EAAE,GAAG,KAAK,CAYjE;AAED,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,KAAK,CAchD;AASD,wBAAgB,WAAW,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAiBhD"}