@deftai/directive-core 0.79.3 → 0.80.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 (77) hide show
  1. package/dist/coverage-hotspots/evaluate.d.ts +49 -0
  2. package/dist/coverage-hotspots/evaluate.js +262 -0
  3. package/dist/coverage-hotspots/index.d.ts +3 -0
  4. package/dist/coverage-hotspots/index.js +3 -0
  5. package/dist/coverage-hotspots/thresholds.d.ts +5 -0
  6. package/dist/coverage-hotspots/thresholds.js +48 -0
  7. package/dist/hooks/dispatcher.d.ts +25 -3
  8. package/dist/hooks/dispatcher.js +216 -65
  9. package/dist/hooks/readonly.d.ts +7 -0
  10. package/dist/hooks/readonly.js +83 -0
  11. package/dist/hooks/tools.d.ts +7 -0
  12. package/dist/hooks/tools.js +16 -0
  13. package/dist/index.d.ts +1 -0
  14. package/dist/index.js +1 -0
  15. package/dist/init-deposit/agent-hooks.d.ts +4 -1
  16. package/dist/init-deposit/agent-hooks.js +77 -21
  17. package/dist/init-deposit/xbrief-projections.d.ts +7 -0
  18. package/dist/init-deposit/xbrief-projections.js +29 -1
  19. package/dist/integration-e2e/bootstrap-cache-module.d.ts +2 -4
  20. package/dist/integration-e2e/bootstrap-cache-module.js +2 -20
  21. package/dist/policy/index.d.ts +2 -0
  22. package/dist/policy/index.js +31 -1
  23. package/dist/policy/runtime-authority.d.ts +54 -0
  24. package/dist/policy/runtime-authority.js +180 -0
  25. package/dist/policy/staleness-tickler.d.ts +20 -0
  26. package/dist/policy/staleness-tickler.js +151 -0
  27. package/dist/pr-merge-readiness/ci-gate.d.ts +7 -1
  28. package/dist/pr-merge-readiness/ci-gate.js +38 -5
  29. package/dist/pr-merge-readiness/gh.d.ts +4 -0
  30. package/dist/pr-merge-readiness/gh.js +10 -3
  31. package/dist/pr-merge-readiness/index.d.ts +2 -1
  32. package/dist/pr-merge-readiness/index.js +2 -1
  33. package/dist/pr-merge-readiness/runner-capacity-stall.d.ts +35 -0
  34. package/dist/pr-merge-readiness/runner-capacity-stall.js +46 -0
  35. package/dist/pr-wait-mergeable/cascade.js +8 -1
  36. package/dist/pr-wait-mergeable/classify.js +5 -1
  37. package/dist/pr-wait-mergeable/wrappers.js +15 -4
  38. package/dist/pr-watch/constants.d.ts +15 -0
  39. package/dist/pr-watch/constants.js +16 -1
  40. package/dist/pr-watch/main.js +12 -0
  41. package/dist/pr-watch/probe.js +13 -1
  42. package/dist/pr-watch/types.d.ts +9 -0
  43. package/dist/pr-watch/watch.js +24 -1
  44. package/dist/scope/main.js +12 -0
  45. package/dist/scope/undo.js +12 -2
  46. package/dist/session/release-availability.d.ts +21 -0
  47. package/dist/session/release-availability.js +109 -0
  48. package/dist/session/ritual-sentinel.d.ts +14 -0
  49. package/dist/session/ritual-sentinel.js +28 -0
  50. package/dist/session/session-start.d.ts +10 -0
  51. package/dist/session/session-start.js +18 -0
  52. package/dist/staleness-tickler/escalation.d.ts +14 -0
  53. package/dist/staleness-tickler/escalation.js +106 -0
  54. package/dist/staleness-tickler/idle.d.ts +18 -0
  55. package/dist/staleness-tickler/idle.js +47 -0
  56. package/dist/staleness-tickler/index.d.ts +8 -0
  57. package/dist/staleness-tickler/index.js +8 -0
  58. package/dist/staleness-tickler/probe-directive.d.ts +32 -0
  59. package/dist/staleness-tickler/probe-directive.js +106 -0
  60. package/dist/staleness-tickler/probe-xbrief.d.ts +12 -0
  61. package/dist/staleness-tickler/probe-xbrief.js +103 -0
  62. package/dist/staleness-tickler/run.d.ts +31 -0
  63. package/dist/staleness-tickler/run.js +227 -0
  64. package/dist/staleness-tickler/state.d.ts +10 -0
  65. package/dist/staleness-tickler/state.js +46 -0
  66. package/dist/staleness-tickler/types.d.ts +85 -0
  67. package/dist/staleness-tickler/types.js +5 -0
  68. package/dist/triage/bootstrap/cache-module.d.ts +25 -0
  69. package/dist/triage/bootstrap/cache-module.js +39 -0
  70. package/dist/triage/bootstrap/index.d.ts +1 -0
  71. package/dist/triage/bootstrap/index.js +7 -82
  72. package/dist/vbrief-validate/plan-hooks.d.ts +4 -0
  73. package/dist/vbrief-validate/plan-hooks.js +50 -0
  74. package/dist/verify-env/agent-hooks.js +4 -2
  75. package/dist/xbrief-migrate/agents-header.d.ts +0 -11
  76. package/dist/xbrief-migrate/agents-header.js +10 -1
  77. package/package.json +3 -3
@@ -0,0 +1,49 @@
1
+ import { type CoverageMetric, type CoverageTotals, summarizeCoverageFinal } from "../vitest-runner/coverage-debt.js";
2
+ import { type CoverageThresholds } from "./thresholds.js";
3
+ export declare const DEFAULT_MIN_HEADROOM_PP = 0.3;
4
+ export declare const DEFAULT_LOWEST_MODULE_LIMIT = 10;
5
+ export interface ModuleCoverage {
6
+ readonly path: string;
7
+ readonly lines: number;
8
+ readonly functions: number;
9
+ readonly branches: number;
10
+ readonly statements: number;
11
+ }
12
+ export interface UncoveredBranchSample {
13
+ readonly path: string;
14
+ readonly line: number;
15
+ readonly branchId: string;
16
+ readonly type: string;
17
+ }
18
+ export interface CoverageHotspotsReport {
19
+ readonly ok: boolean;
20
+ readonly global: CoverageTotals;
21
+ readonly thresholds: CoverageThresholds;
22
+ readonly headroomPp: Record<CoverageMetric, number>;
23
+ readonly minHeadroomPp: number;
24
+ readonly failReasons: readonly string[];
25
+ readonly lowestModules: readonly ModuleCoverage[];
26
+ readonly uncoveredBranches: readonly UncoveredBranchSample[];
27
+ readonly pathFilter: readonly string[];
28
+ readonly coverageReportPath: string;
29
+ }
30
+ export interface CoverageHotspotsResult {
31
+ readonly exitCode: 0 | 1 | 2;
32
+ readonly report: CoverageHotspotsReport | null;
33
+ readonly message: string;
34
+ }
35
+ export interface CoverageHotspotsOptions {
36
+ readonly projectRoot: string;
37
+ readonly coverageDir?: string;
38
+ readonly minHeadroomPp?: number;
39
+ readonly baseRef?: string | null;
40
+ readonly pathFilter?: readonly string[] | null;
41
+ readonly useDiffPaths?: boolean;
42
+ readonly lowestModuleLimit?: number;
43
+ }
44
+ export declare function evaluateCoverageHotspots(options: CoverageHotspotsOptions): CoverageHotspotsResult;
45
+ export declare function formatJsonReport(report: CoverageHotspotsReport): string;
46
+ export declare function formatTextReport(report: CoverageHotspotsReport, failed: boolean): string;
47
+ /** Re-export for tests that build synthetic reports. */
48
+ export { summarizeCoverageFinal };
49
+ //# sourceMappingURL=evaluate.d.ts.map
@@ -0,0 +1,262 @@
1
+ import * as childProcess from "node:child_process";
2
+ import { existsSync, readFileSync } from "node:fs";
3
+ import { join, relative } from "node:path";
4
+ import { summarizeCoverageFinal, } from "../vitest-runner/coverage-debt.js";
5
+ import { readProjectCoverageThresholds } from "./thresholds.js";
6
+ export const DEFAULT_MIN_HEADROOM_PP = 0.3;
7
+ export const DEFAULT_LOWEST_MODULE_LIMIT = 10;
8
+ class GitCommandError extends Error {
9
+ }
10
+ function runGit(args, projectRoot) {
11
+ try {
12
+ return childProcess.execFileSync("git", args, {
13
+ cwd: projectRoot,
14
+ encoding: "utf8",
15
+ stdio: ["ignore", "pipe", "pipe"],
16
+ });
17
+ }
18
+ catch (err) {
19
+ const e = err;
20
+ const stderr = String(e.stderr ?? e.message ?? err).trim();
21
+ throw new GitCommandError(`coverage-hotspots: git ${args.join(" ")} failed: ${stderr}`);
22
+ }
23
+ }
24
+ function splitLines(stdout) {
25
+ return stdout
26
+ .split("\n")
27
+ .map((line) => line.trim())
28
+ .filter((line) => line.length > 0);
29
+ }
30
+ function resolveBaseRef(projectRoot, override) {
31
+ if (override && override.trim().length > 0)
32
+ return override.trim();
33
+ for (const candidate of ["origin/master", "origin/main", "master", "main", "HEAD~1"]) {
34
+ try {
35
+ runGit(["rev-parse", "--verify", "-q", candidate], projectRoot);
36
+ return candidate;
37
+ }
38
+ catch {
39
+ // try next
40
+ }
41
+ }
42
+ return "HEAD~1";
43
+ }
44
+ function diffPaths(projectRoot, baseRef) {
45
+ return splitLines(runGit(["diff", "--name-only", baseRef, "HEAD"], projectRoot));
46
+ }
47
+ function pct(covered, total) {
48
+ return total === 0 ? 100 : (covered / total) * 100;
49
+ }
50
+ function summarizeFileCoverage(relPath, file) {
51
+ let stmtTotal = 0;
52
+ let stmtCovered = 0;
53
+ let fnTotal = 0;
54
+ let fnCovered = 0;
55
+ let branchTotal = 0;
56
+ let branchCovered = 0;
57
+ if (file.s) {
58
+ for (const hits of Object.values(file.s)) {
59
+ stmtTotal += 1;
60
+ if (hits > 0)
61
+ stmtCovered += 1;
62
+ }
63
+ }
64
+ if (file.f) {
65
+ for (const hits of Object.values(file.f)) {
66
+ fnTotal += 1;
67
+ if (hits > 0)
68
+ fnCovered += 1;
69
+ }
70
+ }
71
+ if (file.b) {
72
+ for (const paths of Object.values(file.b)) {
73
+ for (const hits of paths) {
74
+ branchTotal += 1;
75
+ if (hits > 0)
76
+ branchCovered += 1;
77
+ }
78
+ }
79
+ }
80
+ const statements = pct(stmtCovered, stmtTotal);
81
+ const functions = pct(fnCovered, fnTotal);
82
+ const branches = pct(branchCovered, branchTotal);
83
+ return {
84
+ path: relPath,
85
+ statements,
86
+ functions,
87
+ branches,
88
+ lines: statements,
89
+ };
90
+ }
91
+ function normalizeReportPath(projectRoot, rawKey, file) {
92
+ if (file.path && file.path.length > 0) {
93
+ const abs = file.path;
94
+ if (abs.startsWith(projectRoot)) {
95
+ return relative(projectRoot, abs).replace(/\\/g, "/");
96
+ }
97
+ return abs.replace(/\\/g, "/");
98
+ }
99
+ return rawKey.replace(/\\/g, "/");
100
+ }
101
+ function pathMatchesFilter(relPath, filters) {
102
+ if (filters.length === 0)
103
+ return true;
104
+ // Substring only — do not feed CLI/argv filters into RegExp (CodeQL js/regex-injection).
105
+ return filters.some((pattern) => relPath.includes(pattern));
106
+ }
107
+ function collectUncoveredBranches(relPath, file) {
108
+ if (!file.b)
109
+ return [];
110
+ const samples = [];
111
+ for (const [branchId, hits] of Object.entries(file.b)) {
112
+ if (!hits.some((count) => count === 0))
113
+ continue;
114
+ const meta = file.branchMap?.[branchId];
115
+ const line = meta?.line ?? meta?.loc?.start?.line ?? 0;
116
+ samples.push({
117
+ path: relPath,
118
+ line,
119
+ branchId,
120
+ type: meta?.type ?? "branch",
121
+ });
122
+ }
123
+ return samples;
124
+ }
125
+ function computeHeadroom(totals, thresholds) {
126
+ return {
127
+ lines: totals.lines - thresholds.lines,
128
+ functions: totals.functions - thresholds.functions,
129
+ branches: totals.branches - thresholds.branches,
130
+ statements: totals.statements - thresholds.statements,
131
+ };
132
+ }
133
+ function evaluatePassFail(totals, thresholds, headroomPp, minHeadroomPp) {
134
+ const reasons = [];
135
+ if (totals.branches + 1e-9 < thresholds.branches) {
136
+ reasons.push(`branches ${totals.branches.toFixed(2)}% below project floor ${thresholds.branches}%`);
137
+ }
138
+ if (headroomPp.branches + 1e-9 < minHeadroomPp) {
139
+ reasons.push(`branch headroom ${headroomPp.branches.toFixed(2)}pp below minimum ${minHeadroomPp}pp`);
140
+ }
141
+ return reasons;
142
+ }
143
+ function configError(message) {
144
+ return { exitCode: 2, report: null, message };
145
+ }
146
+ export function evaluateCoverageHotspots(options) {
147
+ const projectRoot = options.projectRoot;
148
+ const coverageDir = options.coverageDir ?? join(projectRoot, "coverage");
149
+ const minHeadroomPp = options.minHeadroomPp ?? DEFAULT_MIN_HEADROOM_PP;
150
+ const lowestModuleLimit = options.lowestModuleLimit ?? DEFAULT_LOWEST_MODULE_LIMIT;
151
+ const useDiffPaths = options.useDiffPaths ?? true;
152
+ const reportPath = join(coverageDir, "coverage-final.json");
153
+ if (!existsSync(reportPath)) {
154
+ return configError(`coverage-hotspots: coverage report missing at ${reportPath}\n` +
155
+ " Run tests with coverage first (e.g. vitest run --coverage or task test:coverage).");
156
+ }
157
+ let rawReport;
158
+ try {
159
+ rawReport = JSON.parse(readFileSync(reportPath, "utf8"));
160
+ }
161
+ catch (err) {
162
+ return configError(`coverage-hotspots: unreadable coverage report at ${reportPath}: ${String(err.message)}`);
163
+ }
164
+ const totals = summarizeCoverageFinal(rawReport);
165
+ const thresholds = readProjectCoverageThresholds(projectRoot);
166
+ const headroomPp = computeHeadroom(totals, thresholds);
167
+ let pathFilter = options.pathFilter ?? [];
168
+ if (pathFilter.length === 0 && useDiffPaths) {
169
+ try {
170
+ const baseRef = resolveBaseRef(projectRoot, options.baseRef);
171
+ pathFilter = diffPaths(projectRoot, baseRef);
172
+ }
173
+ catch (err) {
174
+ const message = err instanceof GitCommandError
175
+ ? `${err.message}\n Recovery: pass explicit --path filters or run inside a git repo.`
176
+ : `coverage-hotspots: ${String(err.message)}`;
177
+ return configError(message);
178
+ }
179
+ }
180
+ const modules = [];
181
+ const uncoveredBranches = [];
182
+ for (const [rawKey, file] of Object.entries(rawReport)) {
183
+ const relPath = normalizeReportPath(projectRoot, rawKey, file);
184
+ modules.push(summarizeFileCoverage(relPath, file));
185
+ if (pathMatchesFilter(relPath, pathFilter)) {
186
+ uncoveredBranches.push(...collectUncoveredBranches(relPath, file));
187
+ }
188
+ }
189
+ modules.sort((a, b) => a.branches - b.branches || a.path.localeCompare(b.path));
190
+ const lowestModules = modules.slice(0, lowestModuleLimit);
191
+ uncoveredBranches.sort((a, b) => a.path.localeCompare(b.path) || a.line - b.line || a.branchId.localeCompare(b.branchId));
192
+ const failReasons = evaluatePassFail(totals, thresholds, headroomPp, minHeadroomPp);
193
+ const report = {
194
+ ok: failReasons.length === 0,
195
+ global: totals,
196
+ thresholds,
197
+ headroomPp,
198
+ minHeadroomPp,
199
+ failReasons,
200
+ lowestModules,
201
+ uncoveredBranches,
202
+ pathFilter,
203
+ coverageReportPath: reportPath,
204
+ };
205
+ if (failReasons.length > 0) {
206
+ return {
207
+ exitCode: 1,
208
+ report,
209
+ message: formatTextReport(report, true),
210
+ };
211
+ }
212
+ return {
213
+ exitCode: 0,
214
+ report,
215
+ message: formatTextReport(report, false),
216
+ };
217
+ }
218
+ export function formatJsonReport(report) {
219
+ return `${JSON.stringify(report, null, 2)}\n`;
220
+ }
221
+ function metricLine(label, value, threshold, headroom) {
222
+ return ` ${label.padEnd(12)} ${value.toFixed(2)}% (floor ${threshold}% | headroom ${headroom.toFixed(2)}pp)`;
223
+ }
224
+ export function formatTextReport(report, failed) {
225
+ const lines = [];
226
+ lines.push(failed ? "coverage-hotspots: FAIL" : "coverage-hotspots: OK");
227
+ lines.push(`report: ${report.coverageReportPath}`);
228
+ lines.push("global:");
229
+ lines.push(metricLine("branches", report.global.branches, report.thresholds.branches, report.headroomPp.branches));
230
+ lines.push(metricLine("lines", report.global.lines, report.thresholds.lines, report.headroomPp.lines));
231
+ lines.push(metricLine("functions", report.global.functions, report.thresholds.functions, report.headroomPp.functions));
232
+ lines.push(metricLine("statements", report.global.statements, report.thresholds.statements, report.headroomPp.statements));
233
+ lines.push(`min branch headroom: ${report.minHeadroomPp}pp`);
234
+ if (report.pathFilter.length > 0) {
235
+ lines.push(`path filter (${report.pathFilter.length}): ${report.pathFilter.slice(0, 8).join(", ")}${report.pathFilter.length > 8 ? ", ..." : ""}`);
236
+ }
237
+ if (report.failReasons.length > 0) {
238
+ lines.push("fail reasons:");
239
+ for (const reason of report.failReasons) {
240
+ lines.push(` - ${reason}`);
241
+ }
242
+ }
243
+ if (report.lowestModules.length > 0) {
244
+ lines.push("lowest modules (branches):");
245
+ for (const mod of report.lowestModules.slice(0, 5)) {
246
+ lines.push(` - ${mod.path}: branches ${mod.branches.toFixed(2)}%`);
247
+ }
248
+ }
249
+ if (report.uncoveredBranches.length > 0) {
250
+ lines.push("uncovered branch samples:");
251
+ for (const sample of report.uncoveredBranches.slice(0, 12)) {
252
+ lines.push(` - ${sample.path}:${sample.line} (${sample.type} #${sample.branchId})`);
253
+ }
254
+ if (report.uncoveredBranches.length > 12) {
255
+ lines.push(` ... ${report.uncoveredBranches.length - 12} more`);
256
+ }
257
+ }
258
+ return `${lines.join("\n")}\n`;
259
+ }
260
+ /** Re-export for tests that build synthetic reports. */
261
+ export { summarizeCoverageFinal };
262
+ //# sourceMappingURL=evaluate.js.map
@@ -0,0 +1,3 @@
1
+ export * from "./evaluate.js";
2
+ export * from "./thresholds.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,3 @@
1
+ export * from "./evaluate.js";
2
+ export * from "./thresholds.js";
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,5 @@
1
+ import { type CoverageMetric } from "../vitest-runner/coverage-debt.js";
2
+ export type CoverageThresholds = Record<CoverageMetric, number>;
3
+ /** Best-effort read of vitest coverage thresholds; falls back to framework 85% defaults. */
4
+ export declare function readProjectCoverageThresholds(projectRoot: string): CoverageThresholds;
5
+ //# sourceMappingURL=thresholds.d.ts.map
@@ -0,0 +1,48 @@
1
+ import { existsSync, readFileSync } from "node:fs";
2
+ import { join } from "node:path";
3
+ import { COVERAGE_GOAL } from "../vitest-runner/coverage-debt.js";
4
+ const VITEST_CONFIG_CANDIDATES = [
5
+ "vitest.config.ts",
6
+ "vitest.config.mts",
7
+ "vitest.config.js",
8
+ "vitest.config.cjs",
9
+ "vitest.config.mjs",
10
+ ];
11
+ const METRIC_PATTERN = /(?:^|[\n\r{,])\s*(branches|lines|functions|statements)\s*:\s*(\d+(?:\.\d+)?)/g;
12
+ function parseMetricBlock(text, anchor) {
13
+ const match = anchor.exec(text);
14
+ if (match?.index === undefined)
15
+ return {};
16
+ const slice = text.slice(match.index, match.index + 800);
17
+ const out = {};
18
+ for (const metricMatch of slice.matchAll(METRIC_PATTERN)) {
19
+ const metric = metricMatch[1];
20
+ const value = metricMatch[2];
21
+ if (metric === undefined || value === undefined)
22
+ continue;
23
+ out[metric] = Number.parseFloat(value);
24
+ }
25
+ return out;
26
+ }
27
+ /** Best-effort read of vitest coverage thresholds; falls back to framework 85% defaults. */
28
+ export function readProjectCoverageThresholds(projectRoot) {
29
+ for (const name of VITEST_CONFIG_CANDIDATES) {
30
+ const path = join(projectRoot, name);
31
+ if (!existsSync(path))
32
+ continue;
33
+ const text = readFileSync(path, "utf8");
34
+ const fromCoverageThresholds = parseMetricBlock(text, /coverageThresholds\s*=\s*\{/);
35
+ const fromNested = parseMetricBlock(text, /thresholds\s*:\s*\{/);
36
+ const merged = { ...fromNested, ...fromCoverageThresholds };
37
+ if (Object.keys(merged).length > 0) {
38
+ return {
39
+ lines: merged.lines ?? COVERAGE_GOAL.lines,
40
+ functions: merged.functions ?? COVERAGE_GOAL.functions,
41
+ branches: merged.branches ?? COVERAGE_GOAL.branches,
42
+ statements: merged.statements ?? COVERAGE_GOAL.statements,
43
+ };
44
+ }
45
+ }
46
+ return { ...COVERAGE_GOAL };
47
+ }
48
+ //# sourceMappingURL=thresholds.js.map
@@ -1,12 +1,19 @@
1
+ import { type RuntimeAuthorityPolicy } from "../policy/runtime-authority.js";
1
2
  import { type VerifyResult } from "../session/verify-session-ritual.js";
2
3
  import { type ActiveScopeInspection } from "./scope.js";
3
- export { DIRECT_WRITE_TOOL_NAMES, isDirectWriteTool } from "./tools.js";
4
+ export { hookReadOnlyFromPayload, isExploreSpawn, isReadOnlyHookContext } from "./readonly.js";
5
+ export { DIRECT_WRITE_HOOK_MATCHER, DIRECT_WRITE_TOOL_NAMES, isDirectWriteTool, isSpawnTool, READ_ONLY_HOOK_ENV, SPAWN_HOOK_MATCHER, SPAWN_TOOL_NAMES, } from "./tools.js";
4
6
  export declare const HOOK_HOSTS: readonly ["claude", "grok", "cursor", "codex"];
5
7
  export type HookHost = (typeof HOOK_HOSTS)[number];
6
- export declare const HOOK_EVENTS: readonly ["session.start", "tool.before"];
8
+ export declare const HOOK_EVENTS: readonly ["session.start", "session.compact", "tool.before"];
7
9
  export type HookEvent = (typeof HOOK_EVENTS)[number];
10
+ /** Hosts that receive compact/resume hook deposits via init/update (#2113). */
11
+ export declare const COMPACT_HOOK_HOSTS: readonly ["claude", "grok", "cursor"];
12
+ export type CompactHookHost = (typeof COMPACT_HOOK_HOSTS)[number];
13
+ /** Hosts without a native compact hook surface — deposit skips cleanly (#2113). */
14
+ export declare const COMPACT_HOOK_SKIP_HOSTS: readonly ["codex"];
8
15
  export type HookVerdict = "allow" | "deny";
9
- export type HookDecisionCode = "session-start" | "session-start-degraded" | "not-direct-write" | "invalid-input" | "ritual-not-ready" | "scope-not-ready" | "write-propose-ready" | "write-ready";
16
+ export type HookDecisionCode = "session-start" | "session-start-degraded" | "session-compact-rearm" | "session-compact-rearm-degraded" | "session-compact-noop" | "not-direct-write" | "invalid-input" | "ritual-not-ready" | "scope-not-ready" | "write-propose-ready" | "write-ready" | "read-only-deny" | "spawn-explore-ready" | "spawn-ready" | "spawn-not-ready" | "runtime-policy-deny-path" | "runtime-policy-deny-scope";
10
17
  export interface HookDecision {
11
18
  readonly verdict: HookVerdict;
12
19
  readonly code: HookDecisionCode;
@@ -17,11 +24,19 @@ export interface HookDecision {
17
24
  readonly message: string;
18
25
  readonly scopePath: string | null;
19
26
  }
27
+ /** Stdin parse metadata from hook-dispatch; absent when callers supply payload directly. */
28
+ export interface HookPayloadContext {
29
+ readonly stdinEmpty?: boolean;
30
+ readonly parseFailed?: boolean;
31
+ }
20
32
  export interface HookDispatchInput {
21
33
  readonly host: HookHost;
22
34
  readonly event: HookEvent;
23
35
  readonly projectRoot: string;
24
36
  readonly payload: unknown;
37
+ readonly payloadContext?: HookPayloadContext;
38
+ /** Injected for tests; defaults to `process.env`. */
39
+ readonly environ?: NodeJS.ProcessEnv;
25
40
  }
26
41
  export interface HookPolicySeams {
27
42
  readonly inspectRitual?: (projectRoot: string) => VerifyResult;
@@ -31,7 +46,14 @@ export interface HookPolicySeams {
31
46
  stdout: string;
32
47
  stderr: string;
33
48
  };
49
+ readonly markCompactStale?: (projectRoot: string) => {
50
+ changed: boolean;
51
+ statePath: string;
52
+ message: string;
53
+ };
54
+ readonly loadRuntimeAuthority?: (projectRoot: string) => RuntimeAuthorityPolicy;
34
55
  }
56
+ export declare function hookPayloadTopLevelKeys(payload: unknown): string[];
35
57
  export declare function hookToolName(payload: unknown, host?: HookHost): string | null;
36
58
  /**
37
59
  * Best-effort write-target path from host PreToolUse payloads (#2625).