@guilz-dev/belay 0.4.0 → 0.7.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 (240) hide show
  1. package/README.md +27 -2
  2. package/dist/adapters/claude/runtime-entry.js +54 -25
  3. package/dist/adapters/layouts/scope.js +3 -2
  4. package/dist/adapters/shared/gate-runtime.d.ts +8 -2
  5. package/dist/adapters/shared/gate-runtime.js +579 -181
  6. package/dist/bundle/claude-runtime.mjs +13574 -6334
  7. package/dist/bundle/codex-runtime.mjs +13108 -5903
  8. package/dist/bundle/cursor-runtime.mjs +12676 -5476
  9. package/dist/cli.js +80 -2
  10. package/dist/commands/approve.js +34 -10
  11. package/dist/commands/classify-for-report.js +1 -1
  12. package/dist/commands/config.d.ts +8 -0
  13. package/dist/commands/config.js +134 -11
  14. package/dist/commands/doctor.js +87 -1
  15. package/dist/commands/explain.js +11 -1
  16. package/dist/commands/judge.js +3 -2
  17. package/dist/commands/recovery-checkpoints.d.ts +164 -0
  18. package/dist/commands/recovery-checkpoints.js +347 -0
  19. package/dist/commands/revoke.js +19 -7
  20. package/dist/commands/session.d.ts +20 -0
  21. package/dist/commands/session.js +51 -0
  22. package/dist/commands/status.js +7 -0
  23. package/dist/config-io.js +91 -23
  24. package/dist/conformance/guarantee-posture.d.ts +21 -0
  25. package/dist/conformance/guarantee-posture.js +46 -0
  26. package/dist/conformance/guarantee-table.d.ts +1 -0
  27. package/dist/conformance/guarantee-table.js +20 -9
  28. package/dist/conformance/types.d.ts +1 -0
  29. package/dist/core/approval-replay.d.ts +1 -1
  30. package/dist/core/approval-replay.js +4 -1
  31. package/dist/core/approval-service.d.ts +6 -1
  32. package/dist/core/approval-service.js +87 -24
  33. package/dist/core/approval.d.ts +4 -0
  34. package/dist/core/approval.js +19 -5
  35. package/dist/core/audit-types.d.ts +13 -0
  36. package/dist/core/capability/approval-state-mutation.d.ts +32 -0
  37. package/dist/core/capability/approval-state-mutation.js +165 -0
  38. package/dist/core/capability/approval-v3.d.ts +32 -0
  39. package/dist/core/capability/approval-v3.js +114 -0
  40. package/dist/core/capability/attestation.d.ts +16 -0
  41. package/dist/core/capability/attestation.js +62 -0
  42. package/dist/core/capability/boundary-attestation-sign.d.ts +21 -0
  43. package/dist/core/capability/boundary-attestation-sign.js +84 -0
  44. package/dist/core/capability/boundary-driver-container.d.ts +19 -0
  45. package/dist/core/capability/boundary-driver-container.js +189 -0
  46. package/dist/core/capability/boundary-driver.d.ts +29 -0
  47. package/dist/core/capability/boundary-driver.js +44 -0
  48. package/dist/core/capability/boundary-egress.d.ts +21 -0
  49. package/dist/core/capability/boundary-egress.js +109 -0
  50. package/dist/core/capability/boundary-grant-materialize.d.ts +14 -0
  51. package/dist/core/capability/boundary-grant-materialize.js +93 -0
  52. package/dist/core/capability/boundary-profile.d.ts +15 -0
  53. package/dist/core/capability/boundary-profile.js +25 -0
  54. package/dist/core/capability/boundary-run.d.ts +48 -0
  55. package/dist/core/capability/boundary-run.js +51 -0
  56. package/dist/core/capability/boundary-session.d.ts +61 -0
  57. package/dist/core/capability/boundary-session.js +138 -0
  58. package/dist/core/capability/boundary-workspace-mount.d.ts +5 -0
  59. package/dist/core/capability/boundary-workspace-mount.js +68 -0
  60. package/dist/core/capability/capability-request-hash.d.ts +2 -0
  61. package/dist/core/capability/capability-request-hash.js +8 -0
  62. package/dist/core/capability/gate-latency-ratchet.d.ts +7 -0
  63. package/dist/core/capability/gate-latency-ratchet.js +18 -0
  64. package/dist/core/capability/gate-policy-shadow.d.ts +25 -0
  65. package/dist/core/capability/gate-policy-shadow.js +130 -0
  66. package/dist/core/capability/gate-shadow-audit.d.ts +1 -0
  67. package/dist/core/capability/gate-shadow-audit.js +1 -0
  68. package/dist/core/capability/gate-shadow-ratchet.d.ts +14 -0
  69. package/dist/core/capability/gate-shadow-ratchet.js +81 -0
  70. package/dist/core/capability/grant-consumption.d.ts +4 -0
  71. package/dist/core/capability/grant-consumption.js +11 -0
  72. package/dist/core/capability/grant-lease.d.ts +44 -0
  73. package/dist/core/capability/grant-lease.js +243 -0
  74. package/dist/core/capability/grant-loader.d.ts +12 -0
  75. package/dist/core/capability/grant-loader.js +23 -0
  76. package/dist/core/capability/grant-match.d.ts +6 -0
  77. package/dist/core/capability/grant-match.js +96 -0
  78. package/dist/core/capability/grant.d.ts +16 -0
  79. package/dist/core/capability/grant.js +16 -0
  80. package/dist/core/capability/index.d.ts +8 -0
  81. package/dist/core/capability/index.js +7 -0
  82. package/dist/core/capability/limits.d.ts +7 -0
  83. package/dist/core/capability/limits.js +55 -0
  84. package/dist/core/capability/policy-bridge.d.ts +18 -0
  85. package/dist/core/capability/policy-bridge.js +51 -0
  86. package/dist/core/capability/policy-engine.d.ts +77 -0
  87. package/dist/core/capability/policy-engine.js +663 -0
  88. package/dist/core/capability/policy-types.d.ts +20 -0
  89. package/dist/core/capability/policy-types.js +1 -0
  90. package/dist/core/capability/request.d.ts +48 -0
  91. package/dist/core/capability/request.js +1 -0
  92. package/dist/core/capability/resolver.d.ts +4 -0
  93. package/dist/core/capability/resolver.js +4 -0
  94. package/dist/core/classify-subagent.d.ts +2 -1
  95. package/dist/core/classify-subagent.js +38 -2
  96. package/dist/core/classify-tool.js +126 -62
  97. package/dist/core/config.d.ts +34 -3
  98. package/dist/core/config.js +118 -3
  99. package/dist/core/effect-ir/audit.d.ts +9 -0
  100. package/dist/core/effect-ir/audit.js +86 -0
  101. package/dist/core/effect-ir/build.d.ts +31 -0
  102. package/dist/core/effect-ir/build.js +277 -0
  103. package/dist/core/effect-ir/index.d.ts +8 -0
  104. package/dist/core/effect-ir/index.js +7 -0
  105. package/dist/core/effect-ir/normalize.d.ts +4 -0
  106. package/dist/core/effect-ir/normalize.js +104 -0
  107. package/dist/core/effect-ir/package-exec-eval.d.ts +8 -0
  108. package/dist/core/effect-ir/package-exec-eval.js +152 -0
  109. package/dist/core/effect-ir/package-exec.d.ts +29 -0
  110. package/dist/core/effect-ir/package-exec.js +292 -0
  111. package/dist/core/effect-ir/policy.d.ts +13 -0
  112. package/dist/core/effect-ir/policy.js +77 -0
  113. package/dist/core/effect-ir/types.d.ts +55 -0
  114. package/dist/core/effect-ir/types.js +1 -0
  115. package/dist/core/egress/allowlist.d.ts +1 -0
  116. package/dist/core/egress/allowlist.js +8 -0
  117. package/dist/core/egress-approval.js +45 -51
  118. package/dist/core/gate-contract.d.ts +5 -0
  119. package/dist/core/gate-contract.js +5 -0
  120. package/dist/core/gate-engine.js +30 -8
  121. package/dist/core/index.d.ts +3 -1
  122. package/dist/core/index.js +2 -1
  123. package/dist/core/judge-cloud-consent.js +29 -15
  124. package/dist/core/judge-doctor.js +39 -14
  125. package/dist/core/judge-fallback-hints.d.ts +13 -0
  126. package/dist/core/judge-fallback-hints.js +108 -0
  127. package/dist/core/network-endpoint.d.ts +10 -0
  128. package/dist/core/network-endpoint.js +51 -0
  129. package/dist/core/path-utils.d.ts +1 -0
  130. package/dist/core/path-utils.js +5 -2
  131. package/dist/core/process-runner.d.ts +10 -0
  132. package/dist/core/process-runner.js +124 -0
  133. package/dist/core/recover-advice.js +2 -1
  134. package/dist/core/recovery/artifact-store.d.ts +29 -0
  135. package/dist/core/recovery/artifact-store.js +336 -0
  136. package/dist/core/recovery/capability.d.ts +3 -0
  137. package/dist/core/recovery/capability.js +22 -0
  138. package/dist/core/recovery/checkpoint.d.ts +25 -0
  139. package/dist/core/recovery/checkpoint.js +260 -0
  140. package/dist/core/recovery/fail-closed.d.ts +6 -0
  141. package/dist/core/recovery/fail-closed.js +25 -0
  142. package/dist/core/recovery/index.d.ts +5 -0
  143. package/dist/core/recovery/index.js +4 -0
  144. package/dist/core/recovery/operator-guidance.d.ts +7 -0
  145. package/dist/core/recovery/operator-guidance.js +39 -0
  146. package/dist/core/recovery/reconcile.d.ts +3 -0
  147. package/dist/core/recovery/reconcile.js +48 -0
  148. package/dist/core/recovery/resource-identity.d.ts +4 -0
  149. package/dist/core/recovery/resource-identity.js +34 -0
  150. package/dist/core/recovery/restore.d.ts +13 -0
  151. package/dist/core/recovery/restore.js +114 -0
  152. package/dist/core/recovery/snapshot-node.d.ts +27 -0
  153. package/dist/core/recovery/snapshot-node.js +261 -0
  154. package/dist/core/recovery/types.d.ts +104 -0
  155. package/dist/core/recovery/types.js +1 -0
  156. package/dist/core/standing-allow.js +3 -0
  157. package/dist/core/transactional/apply-observed-changes.d.ts +13 -0
  158. package/dist/core/transactional/apply-observed-changes.js +275 -0
  159. package/dist/core/transactional/backend-selector.d.ts +8 -0
  160. package/dist/core/transactional/backend-selector.js +118 -0
  161. package/dist/core/transactional/backend.d.ts +55 -0
  162. package/dist/core/transactional/backend.js +1 -0
  163. package/dist/core/transactional/diff-evaluator.js +4 -1
  164. package/dist/core/transactional/eligibility.js +4 -0
  165. package/dist/core/transactional/file-checkpoint-backend.d.ts +3 -0
  166. package/dist/core/transactional/file-checkpoint-backend.js +278 -0
  167. package/dist/core/transactional/file-checkpoint-git.d.ts +11 -0
  168. package/dist/core/transactional/file-checkpoint-git.js +217 -0
  169. package/dist/core/transactional/file-checkpoint-staging.d.ts +13 -0
  170. package/dist/core/transactional/file-checkpoint-staging.js +54 -0
  171. package/dist/core/transactional/file-clone.d.ts +15 -0
  172. package/dist/core/transactional/file-clone.js +139 -0
  173. package/dist/core/transactional/file-tree-path.d.ts +7 -0
  174. package/dist/core/transactional/file-tree-path.js +50 -0
  175. package/dist/core/transactional/file-tree.d.ts +41 -0
  176. package/dist/core/transactional/file-tree.js +186 -0
  177. package/dist/core/transactional/git-worktree-backend.d.ts +2 -0
  178. package/dist/core/transactional/git-worktree-backend.js +50 -0
  179. package/dist/core/transactional/git-worktree.d.ts +14 -9
  180. package/dist/core/transactional/git-worktree.js +62 -87
  181. package/dist/core/transactional/index.d.ts +11 -0
  182. package/dist/core/transactional/index.js +10 -0
  183. package/dist/core/transactional/reasons.d.ts +1 -0
  184. package/dist/core/transactional/reasons.js +5 -0
  185. package/dist/core/transactional/runner.js +171 -23
  186. package/dist/core/transactional/snapshot-node.d.ts +26 -0
  187. package/dist/core/transactional/snapshot-node.js +87 -0
  188. package/dist/core/transactional/types.d.ts +21 -1
  189. package/dist/core/types.d.ts +30 -3
  190. package/dist/core/verdict/adapter.d.ts +3 -4
  191. package/dist/core/verdict/adapter.js +24 -12
  192. package/dist/core/verdict/cursor-acp-client.d.ts +67 -0
  193. package/dist/core/verdict/cursor-acp-client.js +345 -0
  194. package/dist/core/verdict/egress-classify.d.ts +2 -2
  195. package/dist/core/verdict/egress-classify.js +2 -2
  196. package/dist/core/verdict/git-classifier.d.ts +26 -0
  197. package/dist/core/verdict/git-classifier.js +533 -0
  198. package/dist/core/verdict/judge-audit.js +1 -0
  199. package/dist/core/verdict/judge-baseline.d.ts +1 -1
  200. package/dist/core/verdict/judge-baseline.js +1 -0
  201. package/dist/core/verdict/judge-broker-service.js +12 -0
  202. package/dist/core/verdict/judge-catalog.js +1 -0
  203. package/dist/core/verdict/judge-cli-fingerprint.d.ts +1 -0
  204. package/dist/core/verdict/judge-cli-fingerprint.js +6 -0
  205. package/dist/core/verdict/judge-cli.js +5 -3
  206. package/dist/core/verdict/judge-provider-matrix.js +1 -1
  207. package/dist/core/verdict/judge-runtime-config.d.ts +2 -0
  208. package/dist/core/verdict/judge-runtime-config.js +10 -6
  209. package/dist/core/verdict/judge-session-broker.d.ts +1 -1
  210. package/dist/core/verdict/judge-session-broker.js +10 -5
  211. package/dist/core/verdict/judge-transport.d.ts +5 -1
  212. package/dist/core/verdict/judge-transport.js +24 -14
  213. package/dist/core/verdict/judge.d.ts +2 -15
  214. package/dist/core/verdict/judge.js +5 -68
  215. package/dist/core/verdict/launcher-resolve.d.ts +2 -0
  216. package/dist/core/verdict/launcher-resolve.js +14 -16
  217. package/dist/core/verdict/prescan.d.ts +15 -0
  218. package/dist/core/verdict/prescan.js +68 -0
  219. package/dist/core/verdict/shell-policy.d.ts +26 -0
  220. package/dist/core/verdict/shell-policy.js +40 -0
  221. package/dist/core/verdict/shell-semantics.d.ts +24 -0
  222. package/dist/core/verdict/shell-semantics.js +225 -0
  223. package/dist/core/verdict/types.d.ts +18 -1
  224. package/dist/core/verdict/verdict.js +313 -178
  225. package/dist/corpus/evaluate.js +3 -7
  226. package/dist/corpus/gate-latency-budget.d.ts +39 -0
  227. package/dist/corpus/gate-latency-budget.js +33 -0
  228. package/dist/corpus/mutators.d.ts +1 -1
  229. package/dist/corpus/mutators.js +11 -0
  230. package/dist/corpus/runtime-match.js +1 -3
  231. package/dist/corpus/standing-allow-catalog.generated.js +29 -9
  232. package/dist/judge-broker-daemon.js +15 -1
  233. package/dist/services/sandbox-service.d.ts +4 -0
  234. package/dist/services/sandbox-service.js +28 -2
  235. package/dist/types.d.ts +13 -2
  236. package/dist/version.d.ts +1 -1
  237. package/dist/version.js +1 -1
  238. package/package.json +1 -1
  239. package/skills/belay/SKILL.md +8 -3
  240. package/skills/belay/belay-approve.md +10 -7
@@ -3,7 +3,6 @@ import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { classifierOptionsFromConfig, DEFAULT_CONFIG_V3 } from '../core/config.js';
5
5
  import { classifyShell } from '../core/verdict/adapter.js';
6
- import { createDeterministicJudgeStub } from '../core/verdict/judge.js';
7
6
  import { computeCategoryGates, isMustAskMiss, } from './gates.js';
8
7
  import { defaultCorpusEvalPaths, enrichProvablyBenignRuntimeKeys } from './runtime-match.js';
9
8
  import { countByCategory, countProvenanceBySource, parseCorpusCases, } from './types.js';
@@ -31,9 +30,8 @@ export async function evaluateCorpus(cases, repoRoot = defaultCorpusEvalPaths().
31
30
  for (const expected of VERDICTS) {
32
31
  confusion[expected] = { allow: 0, allow_flagged: 0, deny_pending_approval: 0 };
33
32
  }
34
- const judge = createDeterministicJudgeStub();
35
33
  for (const testCase of cases) {
36
- const result = await classifyShell(testCase.command, cwd, repoRoot, DEFAULT_CONFIG_V3, options, judge);
34
+ const result = await classifyShell(testCase.command, cwd, repoRoot, DEFAULT_CONFIG_V3, options);
37
35
  results.push({ actual: result.verdict, reason: result.reason });
38
36
  confusion[testCase.verdict][result.verdict] += 1;
39
37
  const verdictOk = result.verdict === testCase.verdict;
@@ -83,10 +81,9 @@ export async function runCorpusEvaluation(corpusDir) {
83
81
  export async function evaluateCommandBatch(cases, repoRoot = defaultCorpusEvalPaths().repoRoot) {
84
82
  const { cwd } = defaultCorpusEvalPaths(repoRoot);
85
83
  const options = classifierOptionsFromConfig(DEFAULT_CONFIG_V3);
86
- const judge = createDeterministicJudgeStub();
87
84
  const results = [];
88
85
  for (const testCase of cases) {
89
- const result = await classifyShell(testCase.command, cwd, repoRoot, DEFAULT_CONFIG_V3, options, judge);
86
+ const result = await classifyShell(testCase.command, cwd, repoRoot, DEFAULT_CONFIG_V3, options);
90
87
  const isFn = isMustAskMiss({
91
88
  category: 'must-ask',
92
89
  command: testCase.command,
@@ -109,10 +106,9 @@ export async function evaluateCommandBatch(cases, repoRoot = defaultCorpusEvalPa
109
106
  export async function evaluateFpCommandBatch(cases, repoRoot = defaultCorpusEvalPaths().repoRoot) {
110
107
  const { cwd } = defaultCorpusEvalPaths(repoRoot);
111
108
  const options = classifierOptionsFromConfig(DEFAULT_CONFIG_V3);
112
- const judge = createDeterministicJudgeStub();
113
109
  const results = [];
114
110
  for (const testCase of cases) {
115
- const result = await classifyShell(testCase.command, cwd, repoRoot, DEFAULT_CONFIG_V3, options, judge);
111
+ const result = await classifyShell(testCase.command, cwd, repoRoot, DEFAULT_CONFIG_V3, options);
116
112
  results.push({
117
113
  command: testCase.command,
118
114
  actual: result.verdict,
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Gate sync classification latency budgets (measured baselines + CI thresholds).
3
+ * PLAN target: p95 <= 100ms, max <= 500ms for full corpus — ratchet toward these over time.
4
+ */
5
+ /** Warm local corpus run (2026-08-06). Step 3 uses max(measured × 1.2, Step 1 floor); floors dominate until re-measured and lowered. */
6
+ export declare const GATE_LATENCY_MEASURED_BASELINE: {
7
+ readonly shell: {
8
+ readonly p95Ms: 1;
9
+ readonly maxMs: 1;
10
+ };
11
+ readonly tool: {
12
+ readonly p95Ms: 1;
13
+ readonly maxMs: 1;
14
+ };
15
+ readonly subagent: {
16
+ readonly p95Ms: 7;
17
+ readonly maxMs: 7;
18
+ };
19
+ };
20
+ /** Step 3 CI thresholds: max(measured p95 × 1.2, Step 1 floor). */
21
+ export declare const GATE_LATENCY_BUDGET: {
22
+ readonly shell: {
23
+ p95Ms: number;
24
+ maxMs: number;
25
+ };
26
+ readonly tool: {
27
+ p95Ms: number;
28
+ maxMs: number;
29
+ };
30
+ readonly subagent: {
31
+ p95Ms: number;
32
+ maxMs: number;
33
+ };
34
+ };
35
+ /** PLAN acceptance targets (quality-loop ratchet goal). */
36
+ export declare const GATE_LATENCY_PLAN_TARGET: {
37
+ readonly p95Ms: 100;
38
+ readonly maxMs: 500;
39
+ };
@@ -0,0 +1,33 @@
1
+ /**
2
+ * Gate sync classification latency budgets (measured baselines + CI thresholds).
3
+ * PLAN target: p95 <= 100ms, max <= 500ms for full corpus — ratchet toward these over time.
4
+ */
5
+ /** Warm local corpus run (2026-08-06). Step 3 uses max(measured × 1.2, Step 1 floor); floors dominate until re-measured and lowered. */
6
+ export const GATE_LATENCY_MEASURED_BASELINE = {
7
+ shell: { p95Ms: 1, maxMs: 1 },
8
+ tool: { p95Ms: 1, maxMs: 1 },
9
+ subagent: { p95Ms: 7, maxMs: 7 },
10
+ };
11
+ /** Step 1 rollout floors — CI thresholds stay at these until measured × 1.2 exceeds them. */
12
+ const GATE_LATENCY_STEP1_FLOOR = {
13
+ shell: { p95Ms: 200, maxMs: 1000 },
14
+ tool: { p95Ms: 300, maxMs: 1500 },
15
+ subagent: { p95Ms: 200, maxMs: 1000 },
16
+ };
17
+ function ciBudgetFromBaseline(baseline, floor) {
18
+ return {
19
+ p95Ms: Math.max(floor.p95Ms, Math.ceil(baseline.p95Ms * 1.2)),
20
+ maxMs: Math.max(floor.maxMs, Math.ceil(baseline.maxMs * 1.2)),
21
+ };
22
+ }
23
+ /** Step 3 CI thresholds: max(measured p95 × 1.2, Step 1 floor). */
24
+ export const GATE_LATENCY_BUDGET = {
25
+ shell: ciBudgetFromBaseline(GATE_LATENCY_MEASURED_BASELINE.shell, GATE_LATENCY_STEP1_FLOOR.shell),
26
+ tool: ciBudgetFromBaseline(GATE_LATENCY_MEASURED_BASELINE.tool, GATE_LATENCY_STEP1_FLOOR.tool),
27
+ subagent: ciBudgetFromBaseline(GATE_LATENCY_MEASURED_BASELINE.subagent, GATE_LATENCY_STEP1_FLOOR.subagent),
28
+ };
29
+ /** PLAN acceptance targets (quality-loop ratchet goal). */
30
+ export const GATE_LATENCY_PLAN_TARGET = {
31
+ p95Ms: 100,
32
+ maxMs: 500,
33
+ };
@@ -18,7 +18,7 @@ export interface MutatedCase {
18
18
  command: string;
19
19
  }
20
20
  /** Must-ask catastrophic cores used as mutation seeds. */
21
- export declare const CATASTROPHIC_CORES: readonly ["rm -rf .git", "git push --force", "dropdb prod", "npm publish", "aws s3 rm s3://bucket/key", "terraform apply", "rm -rf ~"];
21
+ export declare const CATASTROPHIC_CORES: readonly ["rm -rf .git", "git push --force", "git reset --hard", "git clean -fdx", "dropdb prod", "npm publish", "aws s3 rm s3://bucket/key", "terraform apply", "rm -rf ~"];
22
22
  /** Phase A initial set — conservative semantics-preserving mutators only. */
23
23
  export declare const AUTO_LABEL_MUTATORS: readonly CommandMutator[];
24
24
  /** Structural probes and promotion candidates — not used for auto-label in Phase A. */
@@ -8,6 +8,8 @@
8
8
  export const CATASTROPHIC_CORES = [
9
9
  'rm -rf .git',
10
10
  'git push --force',
11
+ 'git reset --hard',
12
+ 'git clean -fdx',
11
13
  'dropdb prod',
12
14
  'npm publish',
13
15
  'aws s3 rm s3://bucket/key',
@@ -120,6 +122,14 @@ const xargsProbe = {
120
122
  tests: ['structural-suite MUST-ASK catastrophic bypass equivalence'],
121
123
  apply: (core) => `xargs ${core} <<< ''`,
122
124
  };
125
+ const findExecProbe = {
126
+ id: 'find_exec',
127
+ purpose: 'find -exec invocation probe',
128
+ preservesSemantics: 'Not semantics-preserving — find -exec changes execution context',
129
+ forbiddenWhen: 'Always probe-only; do not auto-label',
130
+ tests: ['structural-suite MUST-ASK catastrophic bypass equivalence'],
131
+ apply: (core) => `find . -name '*.tmp' -exec ${core} {} \\;`,
132
+ };
123
133
  /** Structural probes and promotion candidates — not used for auto-label in Phase A. */
124
134
  export const STRUCTURAL_PROBES = [
125
135
  nohup,
@@ -130,6 +140,7 @@ export const STRUCTURAL_PROBES = [
130
140
  base64PipeSh,
131
141
  pythonPipe,
132
142
  xargsProbe,
143
+ findExecProbe,
133
144
  ];
134
145
  /** All wrappers for structural-suite catastrophic bypass tests. */
135
146
  export const ALL_STRUCTURAL_WRAPPERS = [
@@ -1,7 +1,6 @@
1
1
  import path from 'node:path';
2
2
  import { classifierOptionsFromConfig, DEFAULT_CONFIG_V3 } from '../core/config.js';
3
3
  import { classifyShell } from '../core/verdict/adapter.js';
4
- import { createDeterministicJudgeStub } from '../core/verdict/judge.js';
5
4
  import { CorpusSchemaError } from './types.js';
6
5
  /** Fixed evaluation harness paths — keep in sync with `evaluateCorpus`. */
7
6
  export const DEFAULT_CORPUS_REPO_ROOT = '/workspace/project';
@@ -14,8 +13,7 @@ export function defaultCorpusEvalPaths(repoRoot = DEFAULT_CORPUS_REPO_ROOT) {
14
13
  */
15
14
  export async function deriveShellCorpusRuntimeKey(command, repoRoot = DEFAULT_CORPUS_REPO_ROOT, cwd = path.join(repoRoot, 'src')) {
16
15
  const options = classifierOptionsFromConfig(DEFAULT_CONFIG_V3);
17
- const judge = createDeterministicJudgeStub();
18
- const result = await classifyShell(command, cwd, repoRoot, DEFAULT_CONFIG_V3, options, judge);
16
+ const result = await classifyShell(command, cwd, repoRoot, DEFAULT_CONFIG_V3, options);
19
17
  return result.fingerprint;
20
18
  }
21
19
  /**
@@ -7,6 +7,16 @@ export const STANDING_ALLOW_CATALOG = {
7
7
  normalizedCommand: 'git status',
8
8
  runtimeKey: '37e6be21e63e6268ee6e4212ad811f6f48ab3c3c10ce1b971c1a90d92c753a2a',
9
9
  },
10
+ {
11
+ command: 'git branch',
12
+ normalizedCommand: 'git branch',
13
+ runtimeKey: 'd7f8388c566e751503ab82ba1ed22787389803eb32d8b5f2294cc7ca448fa285',
14
+ },
15
+ {
16
+ command: 'git branch --show-current',
17
+ normalizedCommand: 'git branch --show-current',
18
+ runtimeKey: '98d585412d3cd7c3b5a87a417d91bc36d7b6791e11451b52ced7888e003d9d33',
19
+ },
10
20
  {
11
21
  command: 'rg plan src',
12
22
  normalizedCommand: 'rg plan src',
@@ -23,19 +33,29 @@ export const STANDING_ALLOW_CATALOG = {
23
33
  runtimeKey: '782298e3015fcb9c17bee2dad12a395a8859043481f93f0ca596499028bb8af2',
24
34
  },
25
35
  {
26
- command: 'curl https://example.com',
27
- normalizedCommand: 'curl https://example.com',
28
- runtimeKey: '09fa1bf897aa68a2d0be02cff3b0cbfa44a28fa17c501e8a39f2081208785e55',
36
+ command: 'git -C /workspace/project status',
37
+ normalizedCommand: 'git -C /workspace/project status',
38
+ runtimeKey: '9f88bd017c52277f5dbfeafec7d845460035a7a14bfb37b55724d7e2a9fc238b',
39
+ },
40
+ {
41
+ command: 'git show-ref --heads',
42
+ normalizedCommand: 'git show-ref --heads',
43
+ runtimeKey: 'aa5aa0106dc7647003f254524becbf549673610d3672f28408ac0f60481124b7',
44
+ },
45
+ {
46
+ command: 'git reflog -5',
47
+ normalizedCommand: 'git reflog -5',
48
+ runtimeKey: '9213a9357953c24886a4e82c2c8cdc6db2d106c7ff98af525f7fbc2b70a58ff9',
29
49
  },
30
50
  {
31
- command: 'wget https://example.com',
32
- normalizedCommand: 'wget https://example.com',
33
- runtimeKey: 'c408836b4bb4498b68b52e08a7c852757a19e156cae7579ffd5c98f13728cacb',
51
+ command: 'grep -R pattern src',
52
+ normalizedCommand: 'grep -R pattern src',
53
+ runtimeKey: '40f5e0328e668909241aa0cd13ce2a51af269aa7ae600cc493dceae7b90dd9d8',
34
54
  },
35
55
  {
36
- command: 'ls\ncurl https://example.com',
37
- normalizedCommand: 'ls\ncurl https://example.com',
38
- runtimeKey: 'a895f27dda259649bbe0d27fcc04ab7076fadfcd4c2ea3ab55f557028b01b11f',
56
+ command: 'jq . package.json',
57
+ normalizedCommand: 'jq . package.json',
58
+ runtimeKey: 'dd7b14d350b84a41e0e73eae0d1a643c1b6c262df8d9007696987354f4f993e9',
39
59
  },
40
60
  ],
41
61
  mustAllow: [
@@ -1,6 +1,7 @@
1
1
  import { mkdir, readFile, unlink, writeFile } from 'node:fs/promises';
2
2
  import { createServer } from 'node:net';
3
3
  import path from 'node:path';
4
+ import { CursorAcpJudgeRunner } from './core/verdict/cursor-acp-client.js';
4
5
  import { cleanupJudgeBrokerArtifacts, JUDGE_BROKER_SESSION, judgeBrokerPaths, } from './core/verdict/judge-broker-service.js';
5
6
  import { runCliJsonWithTimeouts } from './core/verdict/judge-cli.js';
6
7
  import { DEFAULT_JUDGE_SESSION_CONFIG, normalizeJudgeSessionConfig, resolveSessionEvalTimeoutMs, } from './core/verdict/judge-runtime-config.js';
@@ -27,9 +28,21 @@ async function main() {
27
28
  await mkdir(stateDir, { recursive: true, mode: 0o700 });
28
29
  const sessionConfig = await loadBrokerSessionConfig(stateDir);
29
30
  const defaultEvalTimeoutMs = resolveSessionEvalTimeoutMs(sessionConfig, 25_000);
31
+ const judgeWorkspace = path.join(stateDir, 'judge-workspace');
32
+ await mkdir(judgeWorkspace, { recursive: true, mode: 0o700 });
33
+ const cursorAcp = new CursorAcpJudgeRunner({
34
+ cwd: judgeWorkspace,
35
+ connectTimeoutMs: sessionConfig.connectTimeoutMs,
36
+ });
30
37
  const broker = new JudgeSessionBroker({
31
38
  config: sessionConfig,
32
- runCommand: (invocation, timeoutMs) => runCliJsonWithTimeouts(invocation, { evalTimeoutMs: timeoutMs }),
39
+ runCommand: (invocation, timeoutMs) => {
40
+ if (invocation.binary === 'cursor-agent') {
41
+ return cursorAcp.run(invocation, timeoutMs);
42
+ }
43
+ return runCliJsonWithTimeouts(invocation, { evalTimeoutMs: timeoutMs });
44
+ },
45
+ extractResumeId: (providerId) => (providerId === 'cursor' ? cursorAcp.sessionId : null),
33
46
  });
34
47
  let idleTimer = null;
35
48
  let server;
@@ -43,6 +56,7 @@ async function main() {
43
56
  };
44
57
  const shutdown = async (reason) => {
45
58
  broker.stopAll('manual_stop');
59
+ await cursorAcp.stop();
46
60
  server?.close();
47
61
  await cleanupJudgeBrokerArtifacts(paths);
48
62
  process.stderr.write(`judge broker stopped (${reason})\n`);
@@ -1,4 +1,5 @@
1
1
  import { loadConfigFile } from '../config-io.js';
2
+ import { evaluateGuaranteePosture } from '../conformance/guarantee-posture.js';
2
3
  export interface SandboxServiceOptions {
3
4
  targetDir?: string;
4
5
  }
@@ -12,6 +13,7 @@ export interface SandboxStatusReport {
12
13
  controlPlaneIsolationMode: string;
13
14
  controlPlaneIsolationOk: boolean;
14
15
  l1FullActive: boolean;
16
+ guaranteePosture: ReturnType<typeof evaluateGuaranteePosture>;
15
17
  l1Full: {
16
18
  sandbox: boolean;
17
19
  egress: boolean;
@@ -20,6 +22,8 @@ export interface SandboxStatusReport {
20
22
  approvalSigningRequired: boolean;
21
23
  };
22
24
  issues: string[];
25
+ /** Quality-loop ratchet hints — do not affect sandbox status exit code. */
26
+ advisories: string[];
23
27
  }
24
28
  export declare function sandboxStatus(options?: SandboxServiceOptions): Promise<SandboxStatusReport>;
25
29
  export declare function createCapabilityApprovalStore(repoRoot: string, config: Awaited<ReturnType<typeof loadConfigFile>>): {
@@ -1,9 +1,13 @@
1
1
  import path from 'node:path';
2
2
  import { loadConfigFile, repoLocalStateDirFor } from '../config-io.js';
3
+ import { evaluateGuaranteePosture } from '../conformance/guarantee-posture.js';
3
4
  import { fsScopeAllowlistPath, loadFsScopeAllowlist } from '../core/capability/allowlist.js';
5
+ import { boundaryAttestationPath, loadBoundaryAttestation, } from '../core/capability/boundary-session.js';
4
6
  import { evaluateL1FullStatus, isCapabilityBrokerDemotionActive, } from '../core/capability/broker.js';
7
+ import { evaluateGateLatencyRatchetAdvisories } from '../core/capability/gate-latency-ratchet.js';
8
+ import { loadShadowRatchetWarnings } from '../core/capability/gate-shadow-ratchet.js';
5
9
  import { trustedWorkspaceRootsPath } from '../core/capability/trusted-workspace-roots.js';
6
- import { configuredControlPlaneDir } from '../core/config.js';
10
+ import { belayStateDir, configuredControlPlaneDir } from '../core/config.js';
7
11
  import { verifyControlPlaneIsolation } from '../core/control-plane-isolation.js';
8
12
  import { egressStatus } from './egress-service.js';
9
13
  export async function sandboxStatus(options = {}) {
@@ -14,10 +18,18 @@ export async function sandboxStatus(options = {}) {
14
18
  const allowlist = await loadFsScopeAllowlist(allowlistPath);
15
19
  const egress = await egressStatus({ targetDir: repoRoot });
16
20
  const isolation = verifyControlPlaneIsolation(configuredControlPlaneDir(config), config.controlPlane.isolation);
21
+ const egressProxyRunning = egress.running && !egress.foreignProxy && !egress.repoRootMismatch;
17
22
  const l1Full = evaluateL1FullStatus({
18
23
  config,
19
- egressProxyRunning: egress.running && !egress.foreignProxy && !egress.repoRootMismatch,
24
+ egressProxyRunning,
20
25
  });
26
+ const attestation = await loadBoundaryAttestation(boundaryAttestationPath(repoRoot, config), repoRoot, configuredControlPlaneDir(config));
27
+ const guaranteePosture = evaluateGuaranteePosture({
28
+ config,
29
+ attestation,
30
+ egressProxyRunning,
31
+ });
32
+ const shadowRatchetWarnings = await loadShadowRatchetWarnings(belayStateDir(config, repoLocalStateDir));
21
33
  const issues = [...isolation.issues];
22
34
  if (config.sandbox.enabled && config.sandbox.runtime === 'none') {
23
35
  issues.push('sandbox.enabled is true but sandbox.runtime is none');
@@ -31,6 +43,10 @@ export async function sandboxStatus(options = {}) {
31
43
  if (l1Full.sandbox && !l1Full.approvalSigningRequired) {
32
44
  issues.push('L1-full requires approvalSigning.required=true');
33
45
  }
46
+ if (guaranteePosture.postureMismatch) {
47
+ issues.push(`L1-full is configured but attestation only supports ${guaranteePosture.attestedProfile} (run belay session start)`);
48
+ }
49
+ const advisories = [...evaluateGateLatencyRatchetAdvisories(), ...shadowRatchetWarnings];
34
50
  return {
35
51
  repoRoot,
36
52
  sandboxEnabled: config.sandbox.enabled,
@@ -41,8 +57,10 @@ export async function sandboxStatus(options = {}) {
41
57
  controlPlaneIsolationMode: config.controlPlane.isolation.mode,
42
58
  controlPlaneIsolationOk: isolation.ok,
43
59
  l1FullActive: l1Full.active,
60
+ guaranteePosture,
44
61
  l1Full,
45
62
  issues,
63
+ advisories,
46
64
  };
47
65
  }
48
66
  export function createCapabilityApprovalStore(repoRoot, config) {
@@ -84,6 +102,8 @@ export function formatSandboxStatusReport(report) {
84
102
  `FS-scope allowlist entries: ${report.fsScopeAllowlistCount}`,
85
103
  `Control-plane isolation: ${report.controlPlaneIsolationMode} (ok=${report.controlPlaneIsolationOk})`,
86
104
  `L1-full active: ${report.l1FullActive}`,
105
+ `Guarantee posture: configured=${report.guaranteePosture.configuredProfile} attested=${report.guaranteePosture.attestedProfile}`,
106
+ ` attestationFresh=${report.guaranteePosture.attestationFresh} l1FullConfigured=${report.guaranteePosture.l1FullConfigured} l1FullAttested=${report.guaranteePosture.l1FullAttested}`,
87
107
  ` sandbox=${report.l1Full.sandbox} egress=${report.l1Full.egress} proxy=${report.l1Full.egressProxyRunning}`,
88
108
  ` isolation=${report.l1Full.controlPlaneIsolation} signing=${report.l1Full.approvalSigningRequired}`,
89
109
  ];
@@ -93,5 +113,11 @@ export function formatSandboxStatusReport(report) {
93
113
  lines.push(` - ${issue}`);
94
114
  }
95
115
  }
116
+ if (report.advisories.length > 0) {
117
+ lines.push('Advisories:');
118
+ for (const advisory of report.advisories) {
119
+ lines.push(` - ${advisory}`);
120
+ }
121
+ }
96
122
  return `${lines.join('\n')}\n`;
97
123
  }
package/dist/types.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- export type { BelayConfig, BelayConfigV1, BelayConfigV2, BelayConfigV3, BelayControlPlaneConfig, BelayEgressConfig, BelayMode, BelayOverridesConfig, BelayPolicyConfig, BelayRedactionConfig, BelaySandboxConfig, BelayTransactionalConfig, UnknownLocalEffectPolicy, } from './core/config.js';
2
- export type { ApprovalRecord, ApprovalStateFile, Assessment, ClassifyResult, HookVerdict, } from './core/types.js';
1
+ export type { BelayConfig, BelayConfigV1, BelayConfigV2, BelayConfigV3, BelayControlPlaneConfig, BelayEgressConfig, BelayOverridesConfig, BelayPolicyConfig, BelayRedactionConfig, BelaySandboxConfig, BelayTransactionalConfig, UnknownLocalEffectPolicy, } from './core/config.js';
2
+ export type { ApprovalRecord, ApprovalStateFile, Assessment, BelayMode, ClassifyResult, HookVerdict, } from './core/types.js';
3
3
  import type { InstallScope } from './adapters/layouts/scope.js';
4
4
  import type { RecentAskEntry } from './core/audit-summary.js';
5
5
  import type { BelayEgressConfig, BelayOverridesConfig, BelayPolicyConfig, BelaySandboxConfig } from './core/config.js';
@@ -138,6 +138,17 @@ export interface StatusReport {
138
138
  dogfood: DogfoodStatus;
139
139
  health: HealthSnapshot;
140
140
  visibility: AuditVisibilityReport;
141
+ fileCheckpoint: {
142
+ enabled: boolean;
143
+ allowNonGit: boolean;
144
+ transactionalEnabled: boolean;
145
+ durableCheckpointEnabled: boolean;
146
+ maxFiles: number;
147
+ maxSourceBytes: number;
148
+ maxWorkspaceBytes: number;
149
+ prepareTimeoutMs: number;
150
+ copyConcurrency: number;
151
+ };
141
152
  }
142
153
  export interface ReportOptions {
143
154
  targetDir?: string;
package/dist/version.d.ts CHANGED
@@ -1 +1 @@
1
- export declare const PACKAGE_VERSION = "0.4.0";
1
+ export declare const PACKAGE_VERSION = "0.7.0";
package/dist/version.js CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by scripts/sync-version.mjs — do not edit.
2
- export const PACKAGE_VERSION = '0.4.0';
2
+ export const PACKAGE_VERSION = '0.7.0';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guilz-dev/belay",
3
- "version": "0.4.0",
3
+ "version": "0.7.0",
4
4
  "description": "Belay-style approval and audit gating for agent runtimes.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -37,9 +37,14 @@ setup (or `npx @guilz-dev/belay init` for non-interactive install). Run
37
37
  ## When belay blocks an action
38
38
 
39
39
  1. Read the approval ID in the deny message.
40
- 2. Approve once with `/belay-approve <approval-id>` or `belay approve <approval-id>`.
41
- 3. Retry the original action unchanged (default `approval.flow` is `one_step` for shell the
42
- approval response includes a replay hint when auto-replay is enabled).
40
+ 2. In the editor, approve once with `/belay-approve <approval-id>`.
41
+ 3. With default `approval.flow: one_step`, editor approval atomically claims the one-shot grant and
42
+ replays the exact denied shell action through the configured boundary driver. No follow-up prompt
43
+ is required. Failed or timed-out replay requires fresh approval. Tool and subagent actions still
44
+ require retrying the original action unchanged.
45
+
46
+ An instruction may follow the approval line in the same prompt. Belay runs the approved shell action
47
+ first and continues the remaining prompt only after replay succeeds.
43
48
 
44
49
  For shell-only CLI replay after approval: `belay approve <approval-id> --replay`.
45
50
 
@@ -1,21 +1,24 @@
1
1
  Run after belay denies a high-risk action and returns an approval ID.
2
2
 
3
- ```bash
4
- belay approve <approval-id>
3
+ ```text
4
+ /belay-approve <approval-id>
5
5
  ```
6
6
 
7
- Then retry the original action unchanged.
7
+ With default `approval.flow: one_step`, the editor hook atomically claims the one-shot grant and
8
+ immediately replays the exact denied shell action through the configured boundary driver. No
9
+ follow-up prompt is required. Failed or timed-out replay requires fresh approval. Tool and subagent
10
+ approvals still require a manual retry of the original action unchanged.
8
11
 
9
- With default `approval.flow: one_step`, shell approvals return a replay hint from editor
10
- hooks. Tool and subagent approvals still require a manual retry.
12
+ An instruction may follow the approval line in the same prompt. Belay runs the approved shell action
13
+ first and continues the remaining prompt only after replay succeeds.
11
14
 
12
- To run a denied shell command from the CLI after approval (explicit opt-in; do not also retry via hooks):
15
+ For CLI approval, replay remains explicit:
13
16
 
14
17
  ```bash
15
18
  belay approve <approval-id> --replay
16
19
  ```
17
20
 
18
- Successful CLI replay consumes the one-shot grant. On failure, the approval stays active for one hook retry.
21
+ CLI replay claims the one-shot grant before execution. Failure or timeout requires fresh approval.
19
22
 
20
23
  Restore legacy two-step UX in `belay.config.json`:
21
24