@guilz-dev/belay 0.1.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 (266) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +268 -0
  3. package/agent-belay-logo.png +0 -0
  4. package/dist/adapters/claude/adapter.d.ts +7 -0
  5. package/dist/adapters/claude/adapter.js +114 -0
  6. package/dist/adapters/claude/hooks.d.ts +13 -0
  7. package/dist/adapters/claude/hooks.js +49 -0
  8. package/dist/adapters/claude/runtime-entry.d.ts +4 -0
  9. package/dist/adapters/claude/runtime-entry.js +260 -0
  10. package/dist/adapters/codex/adapter.d.ts +7 -0
  11. package/dist/adapters/codex/adapter.js +73 -0
  12. package/dist/adapters/codex/hooks.d.ts +21 -0
  13. package/dist/adapters/codex/hooks.js +78 -0
  14. package/dist/adapters/codex/runtime-entry.d.ts +4 -0
  15. package/dist/adapters/codex/runtime-entry.js +237 -0
  16. package/dist/adapters/cursor/adapter.d.ts +7 -0
  17. package/dist/adapters/cursor/adapter.js +29 -0
  18. package/dist/adapters/cursor/hooks.d.ts +2 -0
  19. package/dist/adapters/cursor/hooks.js +26 -0
  20. package/dist/adapters/cursor/runtime-entry.d.ts +4 -0
  21. package/dist/adapters/cursor/runtime-entry.js +143 -0
  22. package/dist/adapters/layouts/claude.d.ts +2 -0
  23. package/dist/adapters/layouts/claude.js +40 -0
  24. package/dist/adapters/layouts/codex.d.ts +2 -0
  25. package/dist/adapters/layouts/codex.js +43 -0
  26. package/dist/adapters/layouts/cursor.d.ts +2 -0
  27. package/dist/adapters/layouts/cursor.js +40 -0
  28. package/dist/adapters/layouts/index.d.ts +7 -0
  29. package/dist/adapters/layouts/index.js +23 -0
  30. package/dist/adapters/layouts/protected-paths.d.ts +3 -0
  31. package/dist/adapters/layouts/protected-paths.js +15 -0
  32. package/dist/adapters/layouts/scope.d.ts +19 -0
  33. package/dist/adapters/layouts/scope.js +86 -0
  34. package/dist/adapters/layouts/types.d.ts +14 -0
  35. package/dist/adapters/layouts/types.js +1 -0
  36. package/dist/adapters/registry.d.ts +4 -0
  37. package/dist/adapters/registry.js +14 -0
  38. package/dist/adapters/shared/gate-runtime.d.ts +51 -0
  39. package/dist/adapters/shared/gate-runtime.js +518 -0
  40. package/dist/adapters/shared/repo-root.d.ts +2 -0
  41. package/dist/adapters/shared/repo-root.js +17 -0
  42. package/dist/adapters/types.d.ts +19 -0
  43. package/dist/adapters/types.js +1 -0
  44. package/dist/branding.d.ts +3 -0
  45. package/dist/branding.js +3 -0
  46. package/dist/bundle/claude-runtime.mjs +5323 -0
  47. package/dist/bundle/codex-runtime.mjs +5310 -0
  48. package/dist/bundle/cursor-runtime.mjs +5208 -0
  49. package/dist/cleanup-orphans.d.ts +7 -0
  50. package/dist/cleanup-orphans.js +59 -0
  51. package/dist/cli.d.ts +2 -0
  52. package/dist/cli.js +631 -0
  53. package/dist/commands/approve.d.ts +14 -0
  54. package/dist/commands/approve.js +65 -0
  55. package/dist/commands/audit.d.ts +59 -0
  56. package/dist/commands/audit.js +132 -0
  57. package/dist/commands/classify-for-report.d.ts +9 -0
  58. package/dist/commands/classify-for-report.js +85 -0
  59. package/dist/commands/doctor.d.ts +3 -0
  60. package/dist/commands/doctor.js +366 -0
  61. package/dist/commands/dogfood.d.ts +5 -0
  62. package/dist/commands/dogfood.js +71 -0
  63. package/dist/commands/explain.d.ts +3 -0
  64. package/dist/commands/explain.js +133 -0
  65. package/dist/commands/health-snapshot.d.ts +2 -0
  66. package/dist/commands/health-snapshot.js +166 -0
  67. package/dist/commands/init-wizard.d.ts +16 -0
  68. package/dist/commands/init-wizard.js +50 -0
  69. package/dist/commands/metrics.d.ts +7 -0
  70. package/dist/commands/metrics.js +89 -0
  71. package/dist/commands/recover.d.ts +3 -0
  72. package/dist/commands/recover.js +105 -0
  73. package/dist/commands/report.d.ts +3 -0
  74. package/dist/commands/report.js +65 -0
  75. package/dist/commands/revoke.d.ts +5 -0
  76. package/dist/commands/revoke.js +22 -0
  77. package/dist/commands/simulate.d.ts +14 -0
  78. package/dist/commands/simulate.js +55 -0
  79. package/dist/commands/status.d.ts +5 -0
  80. package/dist/commands/status.js +107 -0
  81. package/dist/config-io.d.ts +23 -0
  82. package/dist/config-io.js +180 -0
  83. package/dist/conformance/guarantee-table.d.ts +14 -0
  84. package/dist/conformance/guarantee-table.js +95 -0
  85. package/dist/conformance/types.d.ts +6 -0
  86. package/dist/conformance/types.js +1 -0
  87. package/dist/core/approval-service.d.ts +26 -0
  88. package/dist/core/approval-service.js +41 -0
  89. package/dist/core/approval-token.d.ts +11 -0
  90. package/dist/core/approval-token.js +61 -0
  91. package/dist/core/approval.d.ts +19 -0
  92. package/dist/core/approval.js +58 -0
  93. package/dist/core/audit-analysis.d.ts +10 -0
  94. package/dist/core/audit-analysis.js +147 -0
  95. package/dist/core/audit-metrics.d.ts +51 -0
  96. package/dist/core/audit-metrics.js +155 -0
  97. package/dist/core/audit-query.d.ts +11 -0
  98. package/dist/core/audit-query.js +142 -0
  99. package/dist/core/audit-summary.d.ts +33 -0
  100. package/dist/core/audit-summary.js +111 -0
  101. package/dist/core/audit-types.d.ts +65 -0
  102. package/dist/core/audit-types.js +2 -0
  103. package/dist/core/capability/allowlist.d.ts +10 -0
  104. package/dist/core/capability/allowlist.js +53 -0
  105. package/dist/core/capability/broker.d.ts +17 -0
  106. package/dist/core/capability/broker.js +29 -0
  107. package/dist/core/capability/index.d.ts +5 -0
  108. package/dist/core/capability/index.js +4 -0
  109. package/dist/core/capability/paths.d.ts +1 -0
  110. package/dist/core/capability/paths.js +20 -0
  111. package/dist/core/capability/reasons.d.ts +2 -0
  112. package/dist/core/capability/reasons.js +4 -0
  113. package/dist/core/capability/types.d.ts +10 -0
  114. package/dist/core/capability/types.js +1 -0
  115. package/dist/core/capability-approval.d.ts +28 -0
  116. package/dist/core/capability-approval.js +43 -0
  117. package/dist/core/classify-subagent.d.ts +2 -0
  118. package/dist/core/classify-subagent.js +69 -0
  119. package/dist/core/classify-tool.d.ts +3 -0
  120. package/dist/core/classify-tool.js +311 -0
  121. package/dist/core/config-layers.d.ts +23 -0
  122. package/dist/core/config-layers.js +59 -0
  123. package/dist/core/config.d.ts +219 -0
  124. package/dist/core/config.js +720 -0
  125. package/dist/core/control-plane-isolation.d.ts +10 -0
  126. package/dist/core/control-plane-isolation.js +83 -0
  127. package/dist/core/custom-command-match.d.ts +2 -0
  128. package/dist/core/custom-command-match.js +8 -0
  129. package/dist/core/egress/allowlist.d.ts +7 -0
  130. package/dist/core/egress/allowlist.js +33 -0
  131. package/dist/core/egress/env.d.ts +3 -0
  132. package/dist/core/egress/env.js +17 -0
  133. package/dist/core/egress/fingerprint.d.ts +3 -0
  134. package/dist/core/egress/fingerprint.js +35 -0
  135. package/dist/core/egress/policy.d.ts +8 -0
  136. package/dist/core/egress/policy.js +47 -0
  137. package/dist/core/egress/proxy-server.d.ts +21 -0
  138. package/dist/core/egress/proxy-server.js +263 -0
  139. package/dist/core/egress/types.d.ts +25 -0
  140. package/dist/core/egress/types.js +1 -0
  141. package/dist/core/egress-approval.d.ts +48 -0
  142. package/dist/core/egress-approval.js +129 -0
  143. package/dist/core/fingerprint.d.ts +6 -0
  144. package/dist/core/fingerprint.js +24 -0
  145. package/dist/core/gate-contract.d.ts +48 -0
  146. package/dist/core/gate-contract.js +50 -0
  147. package/dist/core/gate-engine.d.ts +20 -0
  148. package/dist/core/gate-engine.js +172 -0
  149. package/dist/core/glob.d.ts +1 -0
  150. package/dist/core/glob.js +39 -0
  151. package/dist/core/index.d.ts +19 -0
  152. package/dist/core/index.js +15 -0
  153. package/dist/core/integrity.d.ts +15 -0
  154. package/dist/core/integrity.js +68 -0
  155. package/dist/core/judge-api-key.d.ts +4 -0
  156. package/dist/core/judge-api-key.js +11 -0
  157. package/dist/core/judge-config.d.ts +29 -0
  158. package/dist/core/judge-config.js +85 -0
  159. package/dist/core/judge-doctor.d.ts +7 -0
  160. package/dist/core/judge-doctor.js +124 -0
  161. package/dist/core/judgment.d.ts +6 -0
  162. package/dist/core/judgment.js +38 -0
  163. package/dist/core/notify.d.ts +13 -0
  164. package/dist/core/notify.js +44 -0
  165. package/dist/core/path-utils.d.ts +12 -0
  166. package/dist/core/path-utils.js +107 -0
  167. package/dist/core/reclassify.d.ts +15 -0
  168. package/dist/core/reclassify.js +82 -0
  169. package/dist/core/recover-advice.d.ts +30 -0
  170. package/dist/core/recover-advice.js +177 -0
  171. package/dist/core/recover-git-probe.d.ts +8 -0
  172. package/dist/core/recover-git-probe.js +50 -0
  173. package/dist/core/recover-select.d.ts +10 -0
  174. package/dist/core/recover-select.js +60 -0
  175. package/dist/core/scrub.d.ts +3 -0
  176. package/dist/core/scrub.js +87 -0
  177. package/dist/core/shell-substitution.d.ts +6 -0
  178. package/dist/core/shell-substitution.js +130 -0
  179. package/dist/core/shell-tokenizer.d.ts +5 -0
  180. package/dist/core/shell-tokenizer.js +129 -0
  181. package/dist/core/shell-unparseable.d.ts +4 -0
  182. package/dist/core/shell-unparseable.js +96 -0
  183. package/dist/core/transactional/diff-evaluator.d.ts +2 -0
  184. package/dist/core/transactional/diff-evaluator.js +84 -0
  185. package/dist/core/transactional/eligibility.d.ts +4 -0
  186. package/dist/core/transactional/eligibility.js +44 -0
  187. package/dist/core/transactional/git-worktree.d.ts +13 -0
  188. package/dist/core/transactional/git-worktree.js +189 -0
  189. package/dist/core/transactional/index.d.ts +5 -0
  190. package/dist/core/transactional/index.js +4 -0
  191. package/dist/core/transactional/reasons.d.ts +4 -0
  192. package/dist/core/transactional/reasons.js +8 -0
  193. package/dist/core/transactional/runner.d.ts +2 -0
  194. package/dist/core/transactional/runner.js +113 -0
  195. package/dist/core/transactional/types.d.ts +46 -0
  196. package/dist/core/transactional/types.js +1 -0
  197. package/dist/core/types.d.ts +90 -0
  198. package/dist/core/types.js +1 -0
  199. package/dist/core/v2/adapter.d.ts +14 -0
  200. package/dist/core/v2/adapter.js +118 -0
  201. package/dist/core/v2/containment.d.ts +19 -0
  202. package/dist/core/v2/containment.js +91 -0
  203. package/dist/core/v2/egress-classify.d.ts +7 -0
  204. package/dist/core/v2/egress-classify.js +216 -0
  205. package/dist/core/v2/fingerprint.d.ts +1 -0
  206. package/dist/core/v2/fingerprint.js +4 -0
  207. package/dist/core/v2/index.d.ts +12 -0
  208. package/dist/core/v2/index.js +10 -0
  209. package/dist/core/v2/judge-audit.d.ts +2 -0
  210. package/dist/core/v2/judge-audit.js +15 -0
  211. package/dist/core/v2/judge-factory.d.ts +25 -0
  212. package/dist/core/v2/judge-factory.js +75 -0
  213. package/dist/core/v2/judge-outbound.d.ts +12 -0
  214. package/dist/core/v2/judge-outbound.js +73 -0
  215. package/dist/core/v2/judge.d.ts +47 -0
  216. package/dist/core/v2/judge.js +264 -0
  217. package/dist/core/v2/launcher-resolve.d.ts +12 -0
  218. package/dist/core/v2/launcher-resolve.js +190 -0
  219. package/dist/core/v2/overrides.d.ts +7 -0
  220. package/dist/core/v2/overrides.js +37 -0
  221. package/dist/core/v2/parser.d.ts +21 -0
  222. package/dist/core/v2/parser.js +213 -0
  223. package/dist/core/v2/types.d.ts +67 -0
  224. package/dist/core/v2/types.js +1 -0
  225. package/dist/core/v2/verdict.d.ts +2 -0
  226. package/dist/core/v2/verdict.js +699 -0
  227. package/dist/corpus/evaluate.d.ts +24 -0
  228. package/dist/corpus/evaluate.js +69 -0
  229. package/dist/defaults.d.ts +18 -0
  230. package/dist/defaults.js +155 -0
  231. package/dist/egress-daemon.d.ts +1 -0
  232. package/dist/egress-daemon.js +52 -0
  233. package/dist/index.d.ts +17 -0
  234. package/dist/index.js +15 -0
  235. package/dist/installer/bootstrap.d.ts +5 -0
  236. package/dist/installer/bootstrap.js +61 -0
  237. package/dist/installer/runtime-artifacts.d.ts +3 -0
  238. package/dist/installer/runtime-artifacts.js +23 -0
  239. package/dist/installer/scope-config.d.ts +8 -0
  240. package/dist/installer/scope-config.js +25 -0
  241. package/dist/installer.d.ts +22 -0
  242. package/dist/installer.js +169 -0
  243. package/dist/node-resolution.d.ts +8 -0
  244. package/dist/node-resolution.js +237 -0
  245. package/dist/operational-insights.d.ts +19 -0
  246. package/dist/operational-insights.js +24 -0
  247. package/dist/presets.d.ts +4 -0
  248. package/dist/presets.js +95 -0
  249. package/dist/services/egress-service.d.ts +57 -0
  250. package/dist/services/egress-service.js +334 -0
  251. package/dist/services/sandbox-service.d.ts +38 -0
  252. package/dist/services/sandbox-service.js +95 -0
  253. package/dist/templates.d.ts +7 -0
  254. package/dist/templates.js +56 -0
  255. package/dist/types.d.ts +230 -0
  256. package/dist/types.js +1 -0
  257. package/dist/version.d.ts +1 -0
  258. package/dist/version.js +1 -0
  259. package/package.json +65 -0
  260. package/skills/belay/SKILL.md +52 -0
  261. package/skills/belay/belay-approve.md +7 -0
  262. package/skills/belay/belay-explain.md +11 -0
  263. package/skills/belay/belay-recover.md +13 -0
  264. package/skills/belay/belay-report.md +7 -0
  265. package/skills/belay/belay-status.md +9 -0
  266. package/skills/belay/belay-why.md +11 -0
@@ -0,0 +1,65 @@
1
+ import type { Assessment } from './types.js';
2
+ export declare const AUDIT_METRICS_SCHEMA_VERSION = 2;
3
+ export declare const GATE_EVENTS: Set<string>;
4
+ export interface AuditRecord {
5
+ timestamp?: string;
6
+ event?: string;
7
+ kind?: string;
8
+ verdict?: string;
9
+ reason?: string;
10
+ fingerprint?: string;
11
+ summary?: string;
12
+ approvalId?: string;
13
+ wouldBlock?: boolean;
14
+ permission?: string;
15
+ mode?: string;
16
+ assessment?: Assessment;
17
+ predictedAssessment?: Assessment;
18
+ observedAssessment?: Assessment;
19
+ transactional?: boolean;
20
+ transactionalReason?: string;
21
+ transactionalCategories?: string[];
22
+ transactionalChangeCount?: number;
23
+ transactionalSkipReason?: string;
24
+ [key: string]: unknown;
25
+ }
26
+ export interface AuditFilter {
27
+ since?: string;
28
+ until?: string;
29
+ verdict?: string;
30
+ reason?: string;
31
+ kind?: string;
32
+ fingerprint?: string;
33
+ event?: string;
34
+ limit?: number;
35
+ location?: string;
36
+ opacity?: string;
37
+ effect?: string;
38
+ confidence?: string;
39
+ }
40
+ export interface ApprovalRoundTrip {
41
+ denyTimestamp: string;
42
+ approvalTimestamp?: string;
43
+ executeTimestamp?: string;
44
+ approvalId?: string;
45
+ fingerprint: string;
46
+ reason: string;
47
+ summary: string;
48
+ kind: string;
49
+ approvalLatencyMs?: number;
50
+ }
51
+ export interface BypassAttempt {
52
+ afterDenyTimestamp: string;
53
+ denyFingerprint: string;
54
+ denySummary: string;
55
+ attemptTimestamp: string;
56
+ attemptSummary: string;
57
+ attemptFingerprint: string;
58
+ signal: 'similar_command' | 'agent_assessment_mismatch' | 'wrapper_pattern';
59
+ }
60
+ export interface NoisyRuleCandidate {
61
+ reason: string;
62
+ denyCount: number;
63
+ approvedCount: number;
64
+ approvalRate: number;
65
+ }
@@ -0,0 +1,2 @@
1
+ export const AUDIT_METRICS_SCHEMA_VERSION = 2;
2
+ export const GATE_EVENTS = new Set(['beforeShellExecution', 'preToolUse', 'subagentGate']);
@@ -0,0 +1,10 @@
1
+ import type { BelayConfigV3 } from '../config.js';
2
+ import type { FsScopeAllowlistEntry, FsScopeAllowlistFile } from './types.js';
3
+ export declare function fsScopeAllowlistPath(config: BelayConfigV3, repoLocalStateDir: string): string;
4
+ export declare function loadFsScopeAllowlist(filePath: string): Promise<FsScopeAllowlistFile>;
5
+ export declare function loadFsScopeAllowlistSync(filePath: string): FsScopeAllowlistFile;
6
+ export declare function saveFsScopeAllowlist(filePath: string, state: FsScopeAllowlistFile): Promise<void>;
7
+ export declare function normalizeAllowlistPath(targetPath: string): string;
8
+ export declare function isPathAllowlisted(absolutePath: string, allowlist: FsScopeAllowlistFile): boolean;
9
+ export declare function allPathsAllowlisted(absolutePaths: string[], allowlist: FsScopeAllowlistFile): boolean;
10
+ export declare function addPathToAllowlist(allowlist: FsScopeAllowlistFile, entry: FsScopeAllowlistEntry): FsScopeAllowlistFile;
@@ -0,0 +1,53 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { mkdir, readFile, writeFile } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+ import { belayStateDir } from '../config.js';
5
+ import { canonicalPath, pathWithinRoot } from '../path-utils.js';
6
+ export function fsScopeAllowlistPath(config, repoLocalStateDir) {
7
+ return path.join(belayStateDir(config, repoLocalStateDir), 'fs-scope-allowlist.json');
8
+ }
9
+ export async function loadFsScopeAllowlist(filePath) {
10
+ if (!existsSync(filePath)) {
11
+ return { version: 1, paths: [] };
12
+ }
13
+ const raw = JSON.parse(await readFile(filePath, 'utf8'));
14
+ return {
15
+ version: 1,
16
+ paths: Array.isArray(raw.paths) ? raw.paths : [],
17
+ };
18
+ }
19
+ export function loadFsScopeAllowlistSync(filePath) {
20
+ if (!existsSync(filePath)) {
21
+ return { version: 1, paths: [] };
22
+ }
23
+ const raw = JSON.parse(readFileSync(filePath, 'utf8'));
24
+ return {
25
+ version: 1,
26
+ paths: Array.isArray(raw.paths) ? raw.paths : [],
27
+ };
28
+ }
29
+ export async function saveFsScopeAllowlist(filePath, state) {
30
+ await mkdir(path.dirname(filePath), { recursive: true });
31
+ await writeFile(filePath, `${JSON.stringify(state, null, 2)}\n`, 'utf8');
32
+ }
33
+ export function normalizeAllowlistPath(targetPath) {
34
+ return canonicalPath(targetPath);
35
+ }
36
+ export function isPathAllowlisted(absolutePath, allowlist) {
37
+ const resolved = normalizeAllowlistPath(absolutePath);
38
+ return allowlist.paths.some((entry) => {
39
+ const scope = normalizeAllowlistPath(entry.path);
40
+ return resolved === scope || pathWithinRoot(scope, resolved);
41
+ });
42
+ }
43
+ export function allPathsAllowlisted(absolutePaths, allowlist) {
44
+ return (absolutePaths.length > 0 && absolutePaths.every((entry) => isPathAllowlisted(entry, allowlist)));
45
+ }
46
+ export function addPathToAllowlist(allowlist, entry) {
47
+ const normalized = normalizeAllowlistPath(entry.path);
48
+ const filtered = allowlist.paths.filter((existing) => normalizeAllowlistPath(existing.path) !== normalized);
49
+ return {
50
+ version: 1,
51
+ paths: [...filtered, { ...entry, path: normalized }],
52
+ };
53
+ }
@@ -0,0 +1,17 @@
1
+ import type { BelayConfigV3 } from '../config.js';
2
+ export declare function isSandboxBrokerEnabled(config: BelayConfigV3): boolean;
3
+ export declare function hasSandboxRuntime(config: BelayConfigV3): boolean;
4
+ /** FS-scope demotion requires a configured external sandbox runtime, not sandbox.enabled alone. */
5
+ export declare function isCapabilityBrokerDemotionActive(config: BelayConfigV3): boolean;
6
+ export interface L1FullStatus {
7
+ active: boolean;
8
+ sandbox: boolean;
9
+ egress: boolean;
10
+ egressProxyRunning: boolean;
11
+ controlPlaneIsolation: boolean;
12
+ approvalSigningRequired: boolean;
13
+ }
14
+ export declare function evaluateL1FullStatus(params: {
15
+ config: BelayConfigV3;
16
+ egressProxyRunning: boolean;
17
+ }): L1FullStatus;
@@ -0,0 +1,29 @@
1
+ export function isSandboxBrokerEnabled(config) {
2
+ return config.sandbox.enabled;
3
+ }
4
+ export function hasSandboxRuntime(config) {
5
+ return config.sandbox.enabled && config.sandbox.runtime !== 'none';
6
+ }
7
+ /** FS-scope demotion requires a configured external sandbox runtime, not sandbox.enabled alone. */
8
+ export function isCapabilityBrokerDemotionActive(config) {
9
+ return hasSandboxRuntime(config);
10
+ }
11
+ export function evaluateL1FullStatus(params) {
12
+ const sandbox = hasSandboxRuntime(params.config);
13
+ const egress = params.config.egress.enabled;
14
+ const controlPlaneIsolation = params.config.controlPlane.isolation.mode !== 'none';
15
+ const approvalSigningRequired = params.config.approvalSigning.required;
16
+ const active = sandbox &&
17
+ egress &&
18
+ params.egressProxyRunning &&
19
+ controlPlaneIsolation &&
20
+ approvalSigningRequired;
21
+ return {
22
+ active,
23
+ sandbox,
24
+ egress,
25
+ egressProxyRunning: params.egressProxyRunning,
26
+ controlPlaneIsolation,
27
+ approvalSigningRequired,
28
+ };
29
+ }
@@ -0,0 +1,5 @@
1
+ export { addPathToAllowlist, allPathsAllowlisted, fsScopeAllowlistPath, isPathAllowlisted, loadFsScopeAllowlist, loadFsScopeAllowlistSync, saveFsScopeAllowlist, } from './allowlist.js';
2
+ export { evaluateL1FullStatus, hasSandboxRuntime, isCapabilityBrokerDemotionActive, isSandboxBrokerEnabled, } from './broker.js';
3
+ export { collectOutsideRepoPaths } from './paths.js';
4
+ export { FS_SCOPE_REASONS, shouldSkipBrokerApprovedOnce } from './reasons.js';
5
+ export type { CapabilityApprovalScope, FsScopeAllowlistEntry, FsScopeAllowlistFile, } from './types.js';
@@ -0,0 +1,4 @@
1
+ export { addPathToAllowlist, allPathsAllowlisted, fsScopeAllowlistPath, isPathAllowlisted, loadFsScopeAllowlist, loadFsScopeAllowlistSync, saveFsScopeAllowlist, } from './allowlist.js';
2
+ export { evaluateL1FullStatus, hasSandboxRuntime, isCapabilityBrokerDemotionActive, isSandboxBrokerEnabled, } from './broker.js';
3
+ export { collectOutsideRepoPaths } from './paths.js';
4
+ export { FS_SCOPE_REASONS, shouldSkipBrokerApprovedOnce } from './reasons.js';
@@ -0,0 +1 @@
1
+ export declare function collectOutsideRepoPaths(command: string, cwd: string, repoRoot: string): string[];
@@ -0,0 +1,20 @@
1
+ import { relativeWithinRepo, resolveMutationTarget } from '../path-utils.js';
2
+ import { extractRedirectTargets, tokenizeShell } from '../shell-tokenizer.js';
3
+ export function collectOutsideRepoPaths(command, cwd, repoRoot) {
4
+ const tokens = tokenizeShell(command);
5
+ const redirects = extractRedirectTargets(tokens);
6
+ const paths = new Set();
7
+ for (const token of tokens.slice(1)) {
8
+ const resolved = resolveMutationTarget(token, cwd);
9
+ if (resolved && relativeWithinRepo(repoRoot, resolved) === null) {
10
+ paths.add(resolved);
11
+ }
12
+ }
13
+ for (const redirect of redirects) {
14
+ const resolved = resolveMutationTarget(redirect, cwd);
15
+ if (resolved && relativeWithinRepo(repoRoot, resolved) === null) {
16
+ paths.add(resolved);
17
+ }
18
+ }
19
+ return [...paths];
20
+ }
@@ -0,0 +1,2 @@
1
+ export declare const FS_SCOPE_REASONS: Set<string>;
2
+ export declare function shouldSkipBrokerApprovedOnce(brokerActive: boolean, reason: string): boolean;
@@ -0,0 +1,4 @@
1
+ export const FS_SCOPE_REASONS = new Set(['outside_repo_mutation', 'outside_repo_redirect']);
2
+ export function shouldSkipBrokerApprovedOnce(brokerActive, reason) {
3
+ return brokerActive && FS_SCOPE_REASONS.has(reason);
4
+ }
@@ -0,0 +1,10 @@
1
+ export type CapabilityApprovalScope = 'once' | 'path';
2
+ export interface FsScopeAllowlistEntry {
3
+ path: string;
4
+ approvedAt: string;
5
+ approvalId: string;
6
+ }
7
+ export interface FsScopeAllowlistFile {
8
+ version: 1;
9
+ paths: FsScopeAllowlistEntry[];
10
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,28 @@
1
+ import type { CapabilityApprovalScope } from './capability/types.js';
2
+ import type { BelayConfigV3 } from './config.js';
3
+ import type { ApprovalStateFile } from './types.js';
4
+ export interface CapabilityApprovalStore {
5
+ loadPending: () => Promise<{
6
+ filePath: string;
7
+ state: ApprovalStateFile;
8
+ }>;
9
+ loadApproved: () => Promise<{
10
+ filePath: string;
11
+ state: ApprovalStateFile;
12
+ }>;
13
+ writePending: (filePath: string, state: ApprovalStateFile) => Promise<void>;
14
+ writeApproved: (filePath: string, state: ApprovalStateFile) => Promise<void>;
15
+ allowlistPath: string;
16
+ }
17
+ export declare function recordCapabilityApproval(params: {
18
+ approvalId: string;
19
+ config: BelayConfigV3;
20
+ store: CapabilityApprovalStore;
21
+ scope?: CapabilityApprovalScope;
22
+ scopePath?: string;
23
+ token?: string;
24
+ requireSignedToken?: boolean;
25
+ }): Promise<{
26
+ ok: boolean;
27
+ message: string;
28
+ }>;
@@ -0,0 +1,43 @@
1
+ import { recordApproval } from './approval-service.js';
2
+ import { addPathToAllowlist, loadFsScopeAllowlist, normalizeAllowlistPath, saveFsScopeAllowlist, } from './capability/allowlist.js';
3
+ import { FS_SCOPE_REASONS } from './capability/reasons.js';
4
+ export async function recordCapabilityApproval(params) {
5
+ const pending = await params.store.loadPending();
6
+ const match = pending.state.approvals.find((approval) => approval.approvalId === params.approvalId);
7
+ if (params.scope === 'path') {
8
+ if (!match || !FS_SCOPE_REASONS.has(match.reason)) {
9
+ return {
10
+ ok: false,
11
+ message: 'Path scope approvals apply only to pending outside-repo shell actions (outside_repo_mutation / outside_repo_redirect).',
12
+ };
13
+ }
14
+ if (!params.scopePath?.trim()) {
15
+ return {
16
+ ok: false,
17
+ message: 'approve --scope path requires --path <absolute-or-relative-path>.',
18
+ };
19
+ }
20
+ }
21
+ const result = await recordApproval({
22
+ approvalId: params.approvalId,
23
+ config: params.config,
24
+ token: params.token,
25
+ requireSignedToken: params.requireSignedToken ?? false,
26
+ store: params.store,
27
+ });
28
+ if (!result.ok || params.scope !== 'path' || !params.scopePath?.trim()) {
29
+ return { ok: result.ok, message: result.message };
30
+ }
31
+ const allowlist = await loadFsScopeAllowlist(params.store.allowlistPath);
32
+ const normalizedPath = normalizeAllowlistPath(params.scopePath.trim());
33
+ const updated = addPathToAllowlist(allowlist, {
34
+ path: normalizedPath,
35
+ approvedAt: new Date().toISOString(),
36
+ approvalId: params.approvalId,
37
+ });
38
+ await saveFsScopeAllowlist(params.store.allowlistPath, updated);
39
+ return {
40
+ ok: true,
41
+ message: `${result.message} Path ${normalizedPath} added to fs-scope allowlist.`,
42
+ };
43
+ }
@@ -0,0 +1,2 @@
1
+ import type { ClassifierOptions, ClassifyResult } from './types.js';
2
+ export declare function classifySubagent(payload: Record<string, unknown>, repoRoot: string, options?: ClassifierOptions): ClassifyResult;
@@ -0,0 +1,69 @@
1
+ import { canonicalStringify, subagentFingerprint } from './fingerprint.js';
2
+ import { scrubValue } from './scrub.js';
3
+ const EXTERNAL_TERMS = ['deploy', 'production', 'publish', 'release', 'ship', 'notify', 'email'];
4
+ function extractSubagentText(payload, options) {
5
+ const toolInput = payload.tool_input;
6
+ if (toolInput && typeof toolInput === 'object') {
7
+ const input = toolInput;
8
+ const description = typeof input.description === 'string' ? input.description : '';
9
+ const prompt = typeof input.prompt === 'string' ? input.prompt : '';
10
+ return [description, prompt].filter(Boolean).join(' ');
11
+ }
12
+ const task = payload.task;
13
+ if (typeof task === 'string') {
14
+ return task;
15
+ }
16
+ if (task && typeof task === 'object') {
17
+ const taskObj = task;
18
+ const description = typeof taskObj.description === 'string' ? taskObj.description : '';
19
+ const prompt = typeof taskObj.prompt === 'string' ? taskObj.prompt : '';
20
+ return [description, prompt].filter(Boolean).join(' ');
21
+ }
22
+ return canonicalStringify(scrubValue(payload, options.scrubOptions));
23
+ }
24
+ function fingerprintSource(payload, options) {
25
+ const toolInput = payload.tool_input;
26
+ if (toolInput && typeof toolInput === 'object') {
27
+ const input = toolInput;
28
+ return scrubValue({
29
+ description: input.description ?? '',
30
+ prompt: input.prompt ?? '',
31
+ }, options.scrubOptions);
32
+ }
33
+ const task = payload.task;
34
+ if (typeof task === 'string') {
35
+ return scrubValue({ task }, options.scrubOptions);
36
+ }
37
+ if (task && typeof task === 'object') {
38
+ const taskObj = task;
39
+ return scrubValue({
40
+ description: taskObj.description ?? '',
41
+ prompt: taskObj.prompt ?? '',
42
+ }, options.scrubOptions);
43
+ }
44
+ return scrubValue(payload, options.scrubOptions);
45
+ }
46
+ export function classifySubagent(payload, repoRoot, options = {}) {
47
+ const kind = payload.tool_name === 'Task' ? 'Task' : String(payload.subagent_type ?? 'generalPurpose');
48
+ const scrubbed = fingerprintSource(payload, options);
49
+ const summary = extractSubagentText(payload, options);
50
+ const lowered = summary.toLowerCase();
51
+ const fingerprint = subagentFingerprint(kind, scrubbed, repoRoot);
52
+ const hasExternalTerm = EXTERNAL_TERMS.some((term) => {
53
+ const pattern = new RegExp(`\\b${term}\\b`, 'i');
54
+ return pattern.test(lowered);
55
+ });
56
+ return {
57
+ verdict: 'allow_flagged',
58
+ reason: 'subagent_review',
59
+ summary,
60
+ fingerprint,
61
+ assessment: {
62
+ reversibility: 'recoverable_with_cost',
63
+ external: false,
64
+ blastRadius: 'subagent task scope',
65
+ confidence: hasExternalTerm ? 0.7 : 0.67,
66
+ signals: hasExternalTerm ? ['subagent_external_intent_hint'] : ['subagent_default_review'],
67
+ },
68
+ };
69
+ }
@@ -0,0 +1,3 @@
1
+ import type { BelayConfigV3 } from './config.js';
2
+ import type { ClassifierOptions, ClassifyResult } from './types.js';
3
+ export declare function classifyToolUse(payload: Record<string, unknown>, repoRoot: string, cwd: string, config: BelayConfigV3, options?: ClassifierOptions): Promise<ClassifyResult>;