@deftai/directive-core 0.87.0 → 0.89.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 (193) hide show
  1. package/dist/authz/actions.d.ts +55 -0
  2. package/dist/authz/actions.js +125 -0
  3. package/dist/authz/classify.d.ts +23 -0
  4. package/dist/authz/classify.js +217 -0
  5. package/dist/authz/closed-verb.d.ts +42 -0
  6. package/dist/authz/closed-verb.js +279 -0
  7. package/dist/authz/evaluate.d.ts +41 -0
  8. package/dist/authz/evaluate.js +298 -0
  9. package/dist/authz/index.d.ts +15 -0
  10. package/dist/authz/index.js +15 -0
  11. package/dist/authz/origin.d.ts +28 -0
  12. package/dist/authz/origin.js +64 -0
  13. package/dist/authz/paths.d.ts +6 -0
  14. package/dist/authz/paths.js +19 -0
  15. package/dist/authz/store.d.ts +40 -0
  16. package/dist/authz/store.js +317 -0
  17. package/dist/authz/templates.d.ts +139 -0
  18. package/dist/authz/templates.js +241 -0
  19. package/dist/authz/types.d.ts +111 -0
  20. package/dist/authz/types.js +41 -0
  21. package/dist/authz/verb-classification.d.ts +44 -0
  22. package/dist/authz/verb-classification.js +237 -0
  23. package/dist/branch/evaluate.js +6 -1
  24. package/dist/cache/fetch.js +10 -5
  25. package/dist/cache/io.d.ts +9 -2
  26. package/dist/cache/io.js +30 -10
  27. package/dist/cache/scanner.d.ts +11 -1
  28. package/dist/cache/scanner.js +29 -4
  29. package/dist/cache/task-cache/store.js +11 -7
  30. package/dist/capacity/backfill.js +10 -4
  31. package/dist/category-b-namespace/index.js +10 -4
  32. package/dist/check/gate-lists.js +1 -0
  33. package/dist/codebase/default-extractor.js +3 -0
  34. package/dist/codebase/map.js +11 -4
  35. package/dist/content-contracts/skills/helpers.d.ts +10 -0
  36. package/dist/content-contracts/skills/helpers.js +35 -0
  37. package/dist/deposit/copy-tree.d.ts +19 -1
  38. package/dist/deposit/copy-tree.js +134 -5
  39. package/dist/doctor/doctor-state.js +28 -4
  40. package/dist/doctor/main.d.ts +10 -0
  41. package/dist/doctor/main.js +208 -0
  42. package/dist/doctor/types.d.ts +11 -0
  43. package/dist/escalation/actions.d.ts +63 -0
  44. package/dist/escalation/actions.js +137 -0
  45. package/dist/escalation/index.d.ts +8 -0
  46. package/dist/escalation/index.js +8 -0
  47. package/dist/escalation/paths.d.ts +3 -0
  48. package/dist/escalation/paths.js +10 -0
  49. package/dist/escalation/store.d.ts +17 -0
  50. package/dist/escalation/store.js +172 -0
  51. package/dist/escalation/types.d.ts +73 -0
  52. package/dist/escalation/types.js +43 -0
  53. package/dist/eval/crud-telemetry.js +30 -10
  54. package/dist/eval/health.js +22 -7
  55. package/dist/eval/readback.js +11 -10
  56. package/dist/eval/run.js +32 -16
  57. package/dist/events/attribution-enrichment.js +10 -4
  58. package/dist/finish-loop/directive-finish-loop.d.ts +37 -0
  59. package/dist/finish-loop/directive-finish-loop.js +239 -0
  60. package/dist/finish-loop/grant-gate.d.ts +31 -0
  61. package/dist/finish-loop/grant-gate.js +169 -0
  62. package/dist/finish-loop/index.d.ts +11 -0
  63. package/dist/finish-loop/index.js +11 -0
  64. package/dist/finish-loop/main.d.ts +35 -0
  65. package/dist/finish-loop/main.js +365 -0
  66. package/dist/finish-loop/pr-finish-loop.d.ts +34 -0
  67. package/dist/finish-loop/pr-finish-loop.js +217 -0
  68. package/dist/finish-loop/progress.d.ts +17 -0
  69. package/dist/finish-loop/progress.js +45 -0
  70. package/dist/finish-loop/queue.d.ts +16 -0
  71. package/dist/finish-loop/queue.js +65 -0
  72. package/dist/finish-loop/types.d.ts +52 -0
  73. package/dist/finish-loop/types.js +10 -0
  74. package/dist/fs/contained-write.d.ts +101 -0
  75. package/dist/fs/contained-write.js +281 -0
  76. package/dist/fs/projection-containment.d.ts +18 -0
  77. package/dist/fs/projection-containment.js +40 -0
  78. package/dist/hooks/classify/classify.d.ts +10 -0
  79. package/dist/hooks/classify/classify.js +37 -0
  80. package/dist/hooks/classify/index.d.ts +14 -0
  81. package/dist/hooks/classify/index.js +13 -0
  82. package/dist/hooks/classify/paths.d.ts +22 -0
  83. package/dist/hooks/classify/paths.js +72 -0
  84. package/dist/hooks/classify/payload.d.ts +16 -0
  85. package/dist/hooks/classify/payload.js +45 -0
  86. package/dist/hooks/classify/stdin.d.ts +12 -0
  87. package/dist/hooks/classify/stdin.js +63 -0
  88. package/dist/hooks/classify/tool-name.d.ts +18 -0
  89. package/dist/hooks/classify/tool-name.js +85 -0
  90. package/dist/hooks/classify/types.d.ts +41 -0
  91. package/dist/hooks/classify/types.js +7 -0
  92. package/dist/hooks/dispatcher.d.ts +30 -15
  93. package/dist/hooks/dispatcher.js +326 -134
  94. package/dist/hooks/fixtures/cases.d.ts +44 -0
  95. package/dist/hooks/fixtures/cases.js +595 -0
  96. package/dist/hooks/fixtures/index.d.ts +2 -0
  97. package/dist/hooks/fixtures/index.js +2 -0
  98. package/dist/hooks/index.d.ts +2 -0
  99. package/dist/hooks/index.js +2 -0
  100. package/dist/hooks/tools.d.ts +31 -0
  101. package/dist/hooks/tools.js +74 -0
  102. package/dist/index.d.ts +4 -0
  103. package/dist/index.js +5 -0
  104. package/dist/init-deposit/agent-hooks.d.ts +1 -1
  105. package/dist/init-deposit/agent-hooks.js +65 -10
  106. package/dist/init-deposit/gitignore.js +11 -3
  107. package/dist/init-deposit/headless-manifest.js +12 -4
  108. package/dist/init-deposit/hygiene.d.ts +16 -0
  109. package/dist/init-deposit/hygiene.js +26 -0
  110. package/dist/init-deposit/init-dispatch.js +28 -0
  111. package/dist/init-deposit/migrate.d.ts +1 -1
  112. package/dist/init-deposit/migrate.js +13 -3
  113. package/dist/init-deposit/prettierignore.js +12 -5
  114. package/dist/init-deposit/refresh.js +38 -7
  115. package/dist/init-deposit/scaffold.js +42 -29
  116. package/dist/init-deposit/xbrief-projections.js +17 -10
  117. package/dist/intake/candidates-log.js +46 -35
  118. package/dist/intake/github-body-cli.d.ts +6 -0
  119. package/dist/intake/github-body-cli.js +17 -0
  120. package/dist/intake/github-body.d.ts +46 -3
  121. package/dist/intake/github-body.js +191 -20
  122. package/dist/intake/issue-emit.d.ts +1 -0
  123. package/dist/intake/issue-emit.js +41 -23
  124. package/dist/intake/issue-ingest.js +11 -2
  125. package/dist/lifecycle/event-detect.js +12 -4
  126. package/dist/lifecycle/events.js +29 -20
  127. package/dist/lifecycle/lifecycle-hygiene.js +9 -3
  128. package/dist/packs/pack-render.d.ts +33 -0
  129. package/dist/packs/pack-render.js +172 -13
  130. package/dist/packs/quarantine-ext.d.ts +10 -0
  131. package/dist/packs/quarantine-ext.js +26 -2
  132. package/dist/plan-sequence/store.js +11 -4
  133. package/dist/policy/hotfix-criteria.d.ts +79 -0
  134. package/dist/policy/hotfix-criteria.js +197 -0
  135. package/dist/policy/index.d.ts +5 -0
  136. package/dist/policy/index.js +32 -1
  137. package/dist/policy/intent-ceiling.d.ts +79 -0
  138. package/dist/policy/intent-ceiling.js +181 -0
  139. package/dist/policy/no-deft-directive.d.ts +59 -0
  140. package/dist/policy/no-deft-directive.js +110 -0
  141. package/dist/policy/org-force-on-migration.d.ts +52 -0
  142. package/dist/policy/org-force-on-migration.js +269 -27
  143. package/dist/policy/require-human-merge.d.ts +75 -0
  144. package/dist/policy/require-human-merge.js +324 -0
  145. package/dist/policy/resolve.js +17 -6
  146. package/dist/policy/runtime-authority.d.ts +56 -2
  147. package/dist/policy/runtime-authority.js +297 -3
  148. package/dist/policy/write-fence.d.ts +78 -0
  149. package/dist/policy/write-fence.js +164 -0
  150. package/dist/pr-wait-mergeable/cascade.d.ts +10 -0
  151. package/dist/pr-wait-mergeable/cascade.js +28 -0
  152. package/dist/preflight/evaluate.js +10 -0
  153. package/dist/product-signal/consent.js +21 -5
  154. package/dist/product-signal/submit.js +22 -12
  155. package/dist/release/build-dist-runner.js +5 -2
  156. package/dist/release/build-dist.d.ts +19 -1
  157. package/dist/release/build-dist.js +50 -2
  158. package/dist/release/native-steps.js +7 -2
  159. package/dist/release-publish/pipeline.d.ts +13 -0
  160. package/dist/release-publish/pipeline.js +46 -1
  161. package/dist/scope/audit-log.js +19 -24
  162. package/dist/scope/decompose.js +10 -5
  163. package/dist/scope/decomposed-refs.js +18 -3
  164. package/dist/scope/demote.js +9 -2
  165. package/dist/scope/undo.js +9 -2
  166. package/dist/session/release-availability.js +26 -6
  167. package/dist/session/ritual-sentinel.js +19 -12
  168. package/dist/session/session-start-hook.d.ts +3 -0
  169. package/dist/session/session-start-hook.js +15 -0
  170. package/dist/session/session-start.js +37 -0
  171. package/dist/staleness-tickler/state.js +26 -6
  172. package/dist/swarm/launch.js +13 -3
  173. package/dist/swarm/routing.js +9 -3
  174. package/dist/task-surface/index.js +24 -9
  175. package/dist/triage/bootstrap/gitignore.js +53 -10
  176. package/dist/triage/cache-path.js +10 -3
  177. package/dist/triage/welcome/summary.js +11 -4
  178. package/dist/triage/welcome/writers.js +45 -16
  179. package/dist/validate-content/validate-links.js +5 -0
  180. package/dist/value/readback.js +11 -6
  181. package/dist/vbrief-activate/activate.js +12 -4
  182. package/dist/vbrief-build/project-definition-io.js +14 -6
  183. package/dist/verify-source/contained-writes.d.ts +59 -0
  184. package/dist/verify-source/contained-writes.js +272 -0
  185. package/dist/verify-source/cursor-tier1.js +7 -2
  186. package/dist/verify-source/index.d.ts +1 -0
  187. package/dist/verify-source/index.js +1 -0
  188. package/dist/verify-source/openclaw-tier1.js +19 -1
  189. package/dist/verify-source/verify-stubs.js +3 -0
  190. package/dist/xbrief-migrate/migrate-project.js +26 -12
  191. package/dist/xbrief-migrate/transforms.d.ts +4 -2
  192. package/dist/xbrief-migrate/transforms.js +37 -14
  193. package/package.json +16 -3
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Pure write-target / shell-command extraction from host payloads (#2950).
3
+ * No process I/O — does not resolve against projectRoot or realpath.
4
+ */
5
+ /**
6
+ * Best-effort write-target path from host PreToolUse payloads (#2625).
7
+ * Hosts disagree on nesting (`tool_input.file_path` vs top-level `path`).
8
+ */
9
+ export declare function hookWriteTargetPath(payload: unknown): string | null;
10
+ /**
11
+ * Best-effort shell command string from host PreToolUse payloads (#2711).
12
+ * Hosts disagree on nesting (`tool_input.command` vs top-level `command`).
13
+ */
14
+ export declare function hookShellCommand(payload: unknown): string | null;
15
+ /** Serialize tool args for MCP classification when nested objects are present (#2711). */
16
+ export declare function hookMcpArgsText(payload: unknown): string | null;
17
+ /**
18
+ * Collect distinct path-like strings from a payload for fixture assertions.
19
+ * Write target first when present; does not invent paths from free text.
20
+ */
21
+ export declare function hookPathSet(payload: unknown): string[];
22
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Pure write-target / shell-command extraction from host payloads (#2950).
3
+ * No process I/O — does not resolve against projectRoot or realpath.
4
+ */
5
+ import { firstString, record, toolInputRecord } from "./payload.js";
6
+ /**
7
+ * Best-effort write-target path from host PreToolUse payloads (#2625).
8
+ * Hosts disagree on nesting (`tool_input.file_path` vs top-level `path`).
9
+ */
10
+ export function hookWriteTargetPath(payload) {
11
+ const input = record(payload);
12
+ if (input === null)
13
+ return null;
14
+ const toolInput = toolInputRecord(input);
15
+ return firstString([
16
+ toolInput?.file_path,
17
+ toolInput?.filePath,
18
+ toolInput?.path,
19
+ input.file_path,
20
+ input.filePath,
21
+ input.path,
22
+ ]);
23
+ }
24
+ /**
25
+ * Best-effort shell command string from host PreToolUse payloads (#2711).
26
+ * Hosts disagree on nesting (`tool_input.command` vs top-level `command`).
27
+ */
28
+ export function hookShellCommand(payload) {
29
+ const input = record(payload);
30
+ if (input === null)
31
+ return null;
32
+ const toolInput = toolInputRecord(input);
33
+ return firstString([
34
+ toolInput !== null ? toolInput.command : null,
35
+ toolInput !== null ? toolInput.cmd : null,
36
+ toolInput !== null ? toolInput.shell_command : null,
37
+ input.command,
38
+ input.cmd,
39
+ ]);
40
+ }
41
+ /** Serialize tool args for MCP classification when nested objects are present (#2711). */
42
+ export function hookMcpArgsText(payload) {
43
+ const input = record(payload);
44
+ if (input === null)
45
+ return null;
46
+ const toolInput = toolInputRecord(input);
47
+ if (toolInput === null)
48
+ return null;
49
+ try {
50
+ return JSON.stringify(toolInput);
51
+ }
52
+ catch {
53
+ return null;
54
+ }
55
+ }
56
+ /**
57
+ * Collect distinct path-like strings from a payload for fixture assertions.
58
+ * Write target first when present; does not invent paths from free text.
59
+ */
60
+ export function hookPathSet(payload) {
61
+ const seen = new Set();
62
+ const out = [];
63
+ const push = (value) => {
64
+ if (value === null || value.length === 0 || seen.has(value))
65
+ return;
66
+ seen.add(value);
67
+ out.push(value);
68
+ };
69
+ push(hookWriteTargetPath(payload));
70
+ return out;
71
+ }
72
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Pure payload field accessors for host PreToolUse shapes (#2950).
3
+ * No process I/O.
4
+ */
5
+ export declare function record(value: unknown): Record<string, unknown> | null;
6
+ /** First non-empty trimmed string from candidates. */
7
+ export declare function firstString(values: readonly unknown[]): string | null;
8
+ export declare function fieldPresent(input: Record<string, unknown>, key: string): boolean;
9
+ export declare function fieldString(input: Record<string, unknown>, key: string): string | null;
10
+ /**
11
+ * Nested tool input record across host spellings
12
+ * (`tool_input` / `arguments` / `tool_call.arguments` / …).
13
+ */
14
+ export declare function toolInputRecord(payload: Record<string, unknown>): Record<string, unknown> | null;
15
+ export declare function hookPayloadTopLevelKeys(payload: unknown): string[];
16
+ //# sourceMappingURL=payload.d.ts.map
@@ -0,0 +1,45 @@
1
+ /**
2
+ * Pure payload field accessors for host PreToolUse shapes (#2950).
3
+ * No process I/O.
4
+ */
5
+ export function record(value) {
6
+ return value !== null && typeof value === "object" && !Array.isArray(value)
7
+ ? value
8
+ : null;
9
+ }
10
+ /** First non-empty trimmed string from candidates. */
11
+ export function firstString(values) {
12
+ for (const value of values) {
13
+ if (typeof value === "string" && value.trim().length > 0)
14
+ return value.trim();
15
+ }
16
+ return null;
17
+ }
18
+ export function fieldPresent(input, key) {
19
+ return key in input;
20
+ }
21
+ export function fieldString(input, key) {
22
+ const value = input[key];
23
+ if (typeof value === "string" && value.trim().length > 0)
24
+ return value.trim();
25
+ return null;
26
+ }
27
+ /**
28
+ * Nested tool input record across host spellings
29
+ * (`tool_input` / `arguments` / `tool_call.arguments` / …).
30
+ */
31
+ export function toolInputRecord(payload) {
32
+ const toolCall = record(payload.tool_call) ?? record(payload.toolCall);
33
+ return (record(payload.tool_input) ??
34
+ record(payload.toolInput) ??
35
+ record(payload.input) ??
36
+ record(payload.arguments) ??
37
+ (toolCall !== null ? record(toolCall.arguments) : null));
38
+ }
39
+ export function hookPayloadTopLevelKeys(payload) {
40
+ const input = record(payload);
41
+ if (input === null)
42
+ return [];
43
+ return Object.keys(input).sort();
44
+ }
45
+ //# sourceMappingURL=payload.js.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Pure stdin → payload parse for hook-dispatch (#2734 / #2738 / #2950).
3
+ * No process I/O — operates on an already-read string.
4
+ */
5
+ import type { ParsedHookPayload } from "./types.js";
6
+ export declare function stripUtf8Bom(raw: string): string;
7
+ /**
8
+ * Parse host hook stdin text into a payload + parse context.
9
+ * Empty → stdinEmpty; invalid JSON without free-form ApplyPatch → parseFailed.
10
+ */
11
+ export declare function parseHookStdin(raw: string): ParsedHookPayload;
12
+ //# sourceMappingURL=stdin.d.ts.map
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Pure stdin → payload parse for hook-dispatch (#2734 / #2738 / #2950).
3
+ * No process I/O — operates on an already-read string.
4
+ */
5
+ const UTF8_BOM = "\uFEFF";
6
+ const APPLY_PATCH_BEGIN_MARKER = "*** Begin Patch";
7
+ /** Single-file Add/Update only — other *** … File: ops must fail closed (#2738 Greptile). */
8
+ const APPLY_PATCH_MUTATION_LINE_RE = /^\*\*\* (Add File|Update File|Delete File|Move File|Rename File): (.+)$/gm;
9
+ export function stripUtf8Bom(raw) {
10
+ return raw.startsWith(UTF8_BOM) ? raw.slice(UTF8_BOM.length) : raw;
11
+ }
12
+ function trySynthesizeFreeFormApplyPatch(normalized) {
13
+ if (!normalized.includes(APPLY_PATCH_BEGIN_MARKER))
14
+ return null;
15
+ const mutations = [];
16
+ for (const match of normalized.matchAll(APPLY_PATCH_MUTATION_LINE_RE)) {
17
+ const op = match[1];
18
+ const path = match[2]?.trim();
19
+ if (op === undefined || !path)
20
+ continue;
21
+ mutations.push({ op, path });
22
+ }
23
+ if (mutations.length !== 1)
24
+ return null;
25
+ const sole = mutations[0];
26
+ if (sole === undefined || (sole.op !== "Add File" && sole.op !== "Update File"))
27
+ return null;
28
+ return {
29
+ payload: {
30
+ tool_name: "ApplyPatch",
31
+ tool_input: {
32
+ path: sole.path,
33
+ patch: normalized,
34
+ },
35
+ },
36
+ context: {},
37
+ };
38
+ }
39
+ /**
40
+ * Parse host hook stdin text into a payload + parse context.
41
+ * Empty → stdinEmpty; invalid JSON without free-form ApplyPatch → parseFailed.
42
+ */
43
+ export function parseHookStdin(raw) {
44
+ if (raw.trim().length === 0) {
45
+ return { payload: {}, context: { stdinEmpty: true } };
46
+ }
47
+ const normalized = stripUtf8Bom(raw);
48
+ if (normalized.trim().length === 0) {
49
+ return { payload: {}, context: { stdinEmpty: true } };
50
+ }
51
+ try {
52
+ return { payload: JSON.parse(normalized), context: {} };
53
+ }
54
+ catch {
55
+ const synthesized = trySynthesizeFreeFormApplyPatch(normalized);
56
+ if (synthesized !== null)
57
+ return synthesized;
58
+ // tool.before is installed only on direct-write matchers, so an unreadable
59
+ // payload becomes a missing-tool denial rather than a fail-open crash.
60
+ return { payload: {}, context: { parseFailed: true } };
61
+ }
62
+ }
63
+ //# sourceMappingURL=stdin.js.map
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Pure tool-name resolution from host PreToolUse payloads (#2950 / #2628 / #2669).
3
+ * No process I/O.
4
+ */
5
+ import type { ClassifyHookHost, HookPayloadContext } from "./types.js";
6
+ /**
7
+ * Cursor preToolUse payloads sometimes omit `tool_name` even when the hook matcher
8
+ * fired for a direct-write tool (#2628). Infer from nested tool input when possible.
9
+ */
10
+ export declare function inferCursorDirectWriteToolName(payload: Record<string, unknown>): string | null;
11
+ export declare function hookToolName(payload: unknown, host?: ClassifyHookHost | string): string | null;
12
+ export interface MissingToolNameInput {
13
+ readonly host: ClassifyHookHost | string;
14
+ readonly payload: unknown;
15
+ readonly context?: HookPayloadContext;
16
+ }
17
+ export declare function missingToolNameMessage(input: MissingToolNameInput): string;
18
+ //# sourceMappingURL=tool-name.d.ts.map
@@ -0,0 +1,85 @@
1
+ /**
2
+ * Pure tool-name resolution from host PreToolUse payloads (#2950 / #2628 / #2669).
3
+ * No process I/O.
4
+ */
5
+ import { hookWriteTargetPath } from "./paths.js";
6
+ import { fieldPresent, fieldString, hookPayloadTopLevelKeys, record, toolInputRecord, } from "./payload.js";
7
+ /**
8
+ * Cursor preToolUse payloads sometimes omit `tool_name` even when the hook matcher
9
+ * fired for a direct-write tool (#2628). Infer from nested tool input when possible.
10
+ */
11
+ export function inferCursorDirectWriteToolName(payload) {
12
+ const toolInput = toolInputRecord(payload);
13
+ if (toolInput !== null) {
14
+ if (fieldPresent(toolInput, "new_string") ||
15
+ fieldPresent(toolInput, "newString") ||
16
+ fieldPresent(toolInput, "old_string") ||
17
+ fieldPresent(toolInput, "oldString")) {
18
+ return "StrReplace";
19
+ }
20
+ if (fieldString(toolInput, "contents") !== null ||
21
+ fieldString(toolInput, "content") !== null ||
22
+ fieldString(toolInput, "text") !== null) {
23
+ return "Write";
24
+ }
25
+ if (fieldString(toolInput, "patch") !== null ||
26
+ fieldString(toolInput, "unified_diff") !== null ||
27
+ fieldString(toolInput, "diff") !== null) {
28
+ return "ApplyPatch";
29
+ }
30
+ if (Array.isArray(toolInput.edits))
31
+ return "MultiEdit";
32
+ if (Array.isArray(toolInput.cells) || toolInput.cell_id != null) {
33
+ return "NotebookEdit";
34
+ }
35
+ }
36
+ // Cursor maps Claude Edit → Write; a write target without contents is still a direct write.
37
+ if (hookWriteTargetPath(payload) !== null)
38
+ return "Write";
39
+ return null;
40
+ }
41
+ export function hookToolName(payload, host) {
42
+ const input = record(payload);
43
+ if (input === null)
44
+ return null;
45
+ const toolObject = record(input.tool);
46
+ const toolCall = record(input.tool_call) ?? record(input.toolCall);
47
+ // OpenAI-style nestings are host-agnostic; checked before Cursor-only inference.
48
+ const direct = fieldString(input, "tool_name") ??
49
+ fieldString(input, "toolName") ??
50
+ fieldString(input, "tool") ??
51
+ (toolObject !== null ? fieldString(toolObject, "name") : null) ??
52
+ (toolCall !== null ? fieldString(toolCall, "name") : null);
53
+ if (direct !== null)
54
+ return direct;
55
+ if (host === "cursor")
56
+ return inferCursorDirectWriteToolName(input);
57
+ return null;
58
+ }
59
+ export function missingToolNameMessage(input) {
60
+ const { host, payload, context } = input;
61
+ if (host === "cursor") {
62
+ if (context?.stdinEmpty) {
63
+ return ("Directive denied this Cursor preToolUse event because the host sent an empty payload " +
64
+ "(stdin was empty — not a session ritual or scope failure). " +
65
+ "If write tools should pass, update Directive or report the payload shape from Cursor.");
66
+ }
67
+ if (context?.parseFailed) {
68
+ return ("Directive denied this Cursor preToolUse event because the host payload was not valid JSON " +
69
+ "(host-integration mismatch — not a session ritual or scope failure). " +
70
+ "If write tools should pass, update Directive or report the payload shape from Cursor.");
71
+ }
72
+ const keys = hookPayloadTopLevelKeys(payload);
73
+ if (keys.length > 0) {
74
+ return ("Directive denied this Cursor preToolUse event because the host payload omitted a " +
75
+ "recognizable tool name (host-integration mismatch — not a session ritual or scope failure). " +
76
+ `Top-level payload keys: ${keys.join(", ")}. ` +
77
+ "If write tools should pass, update Directive or report the payload shape from Cursor.");
78
+ }
79
+ return ("Directive denied this Cursor preToolUse event because the host payload omitted a " +
80
+ "recognizable tool name (host-integration mismatch — not a session ritual or scope failure). " +
81
+ "If write tools should pass, update Directive or report the payload shape from Cursor.");
82
+ }
83
+ return "Directive denied this matched write event because the host payload omitted its tool name.";
84
+ }
85
+ //# sourceMappingURL=tool-name.js.map
@@ -0,0 +1,41 @@
1
+ /**
2
+ * Pure hook classification types (#2950 Phase A).
3
+ * No process I/O — payload/shape only.
4
+ */
5
+ /** Host ids that PreToolUse / SessionStart dispatch understands. */
6
+ export declare const CLASSIFY_HOOK_HOSTS: readonly ["claude", "grok", "cursor", "codex"];
7
+ export type ClassifyHookHost = (typeof CLASSIFY_HOOK_HOSTS)[number];
8
+ /**
9
+ * Write / mutation intent derived from tool identity alone (before policy gates).
10
+ * Decision codes (allow/deny) live in the dispatcher; this layer only classifies.
11
+ */
12
+ export type HookWriteIntent = "direct-write" | "spawn" | "shell" | "mcp" | "other" | "unknown";
13
+ /** Stdin parse metadata from hook-dispatch; absent when callers supply payload directly. */
14
+ export interface HookPayloadContext {
15
+ readonly stdinEmpty?: boolean;
16
+ readonly parseFailed?: boolean;
17
+ }
18
+ export interface ParsedHookPayload {
19
+ readonly payload: unknown;
20
+ readonly context: HookPayloadContext;
21
+ }
22
+ /**
23
+ * Pure classification of a host PreToolUse (or equivalent) payload.
24
+ * Paths are extracted as raw host strings — project-relative normalization is
25
+ * dispatcher-owned (needs projectRoot + optional realpath).
26
+ */
27
+ export interface HookClassification {
28
+ readonly toolName: string | null;
29
+ readonly writeIntent: HookWriteIntent;
30
+ readonly writeTargetPath: string | null;
31
+ readonly shellCommand: string | null;
32
+ readonly topLevelKeys: readonly string[];
33
+ /** Distinct path-like strings found on the payload (write target first when present). */
34
+ readonly paths: readonly string[];
35
+ }
36
+ export interface ClassifyHookPayloadInput {
37
+ readonly payload: unknown;
38
+ /** Host id; Cursor enables tool-name inference when `tool_name` is omitted. */
39
+ readonly host?: ClassifyHookHost | string;
40
+ }
41
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Pure hook classification types (#2950 Phase A).
3
+ * No process I/O — payload/shape only.
4
+ */
5
+ /** Host ids that PreToolUse / SessionStart dispatch understands. */
6
+ export const CLASSIFY_HOOK_HOSTS = ["claude", "grok", "cursor", "codex"];
7
+ //# sourceMappingURL=types.js.map
@@ -1,8 +1,11 @@
1
+ import { type AuthzState, type HumanOriginGrant } from "../authz/index.js";
2
+ import { detectNoDeftDirective } from "../policy/no-deft-directive.js";
1
3
  import { type RuntimeAuthorityPolicy } from "../policy/runtime-authority.js";
2
4
  import { type VerifyResult } from "../session/verify-session-ritual.js";
5
+ import { type HookPayloadContext } from "./classify/index.js";
3
6
  import { type ActiveScopeInspection } from "./scope.js";
4
7
  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";
8
+ export { DIRECT_WRITE_HOOK_MATCHER, DIRECT_WRITE_TOOL_NAMES, isDirectWriteTool, isMcpTool, isShellTool, isSpawnTool, MCP_HOOK_MATCHER, MCP_PUSH_MERGE_BARE_NAMES, READ_ONLY_HOOK_ENV, SHELL_HOOK_MATCHER, SHELL_TOOL_NAMES, SPAWN_HOOK_MATCHER, SPAWN_TOOL_NAMES, } from "./tools.js";
6
9
  export declare const HOOK_HOSTS: readonly ["claude", "grok", "cursor", "codex"];
7
10
  export type HookHost = (typeof HOOK_HOSTS)[number];
8
11
  export declare const HOOK_EVENTS: readonly ["session.start", "session.compact", "tool.before"];
@@ -13,9 +16,17 @@ export type CompactHookHost = (typeof COMPACT_HOOK_HOSTS)[number];
13
16
  /** Hosts without a native compact hook surface — deposit skips cleanly (#2113). */
14
17
  export declare const COMPACT_HOOK_SKIP_HOSTS: readonly ["codex"];
15
18
  export type HookVerdict = "allow" | "deny";
16
- export type HookDecisionCode = "session-start" | "session-start-degraded" | "session-compact-rearm" | "session-compact-rearm-degraded" | "session-compact-noop" | "not-direct-write" | "invalid-input"
19
+ export type HookDecisionCode = "session-start" | "session-start-disabled" | "session-start-degraded" | "session-compact-rearm" | "session-compact-rearm-degraded" | "session-compact-noop" | "not-direct-write" | "invalid-input"
17
20
  /** Host closed stdin with zero bytes — integration failure, not a policy gate (#2864). */
18
- | "stdin-empty" | "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";
21
+ | "stdin-empty" | "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"
22
+ /** Shell/MCP classifiable push/merge allowed under runtimeAuthority (#2711). */
23
+ | "shell-op-ready"
24
+ /** Shell/MCP tool seen but command/tool not classifiable as push/merge — fail open (#2711). */
25
+ | "shell-op-unclassifiable"
26
+ /** UAT lease / human-origin grant denial (#2944). */
27
+ | "authz-uat-deny" | "authz-grant-missing" | "authz-grant-origin-reject" | "authz-grant-scope-deny" | "authz-grant-expired" | "authz-grant-revoked" | "authz-grant-single-use-spent"
28
+ /** Slash-command intent ceiling denial (#1193). */
29
+ | "intent-ceiling-deny";
19
30
  export interface HookDecision {
20
31
  readonly verdict: HookVerdict;
21
32
  readonly code: HookDecisionCode;
@@ -26,11 +37,6 @@ export interface HookDecision {
26
37
  readonly message: string;
27
38
  readonly scopePath: string | null;
28
39
  }
29
- /** Stdin parse metadata from hook-dispatch; absent when callers supply payload directly. */
30
- export interface HookPayloadContext {
31
- readonly stdinEmpty?: boolean;
32
- readonly parseFailed?: boolean;
33
- }
34
40
  export interface HookDispatchInput {
35
41
  readonly host: HookHost;
36
42
  readonly event: HookEvent;
@@ -48,20 +54,29 @@ export interface HookPolicySeams {
48
54
  stdout: string;
49
55
  stderr: string;
50
56
  };
57
+ /** Test seam for #2926 root opt-out on SessionStart. */
58
+ readonly detectNoDeftDirective?: typeof detectNoDeftDirective;
51
59
  readonly markCompactStale?: (projectRoot: string) => {
52
60
  changed: boolean;
53
61
  statePath: string;
54
62
  message: string;
55
63
  };
56
64
  readonly loadRuntimeAuthority?: (projectRoot: string) => RuntimeAuthorityPolicy;
65
+ /**
66
+ * Test seam for #516 / #2443 story write fence.
67
+ * Defaults to reading `plan.metadata.swarm.file_scope` (+ writeScope alias)
68
+ * from the active scope path when present.
69
+ */
70
+ readonly loadStoryWriteFence?: (projectRoot: string, scopePath: string | null) => {
71
+ readonly fileScope: readonly string[];
72
+ readonly denyPaths: readonly string[];
73
+ };
74
+ /** Test seam for #2944 UAT lease + human-origin grants. */
75
+ readonly loadAuthzState?: (projectRoot: string) => AuthzState;
76
+ readonly loadAuthzGrants?: (projectRoot: string, state: AuthzState) => readonly HumanOriginGrant[];
77
+ /** When false, skip writing `.deft/authz/audit.jsonl` (tests). Default true. */
78
+ readonly authzAudit?: boolean;
57
79
  }
58
- export declare function hookPayloadTopLevelKeys(payload: unknown): string[];
59
- export declare function hookToolName(payload: unknown, host?: HookHost): string | null;
60
- /**
61
- * Best-effort write-target path from host PreToolUse payloads (#2625).
62
- * Hosts disagree on nesting (`tool_input.file_path` vs top-level `path`).
63
- */
64
- export declare function hookWriteTargetPath(payload: unknown): string | null;
65
80
  /** POSIX-ish project-relative path for lifecycle matching. */
66
81
  export declare function toProjectRelativePosix(projectRoot: string, targetPath: string): string;
67
82
  /**