@deftai/directive-core 0.107.0 → 0.109.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 (271) hide show
  1. package/dist/architecture/sor-preflight.js +2 -0
  2. package/dist/cache/operations.js +1 -1
  3. package/dist/check/cached-orchestrator.js +28 -3
  4. package/dist/check/gate-lists.d.ts +14 -0
  5. package/dist/check/gate-lists.js +26 -3
  6. package/dist/check/named-cause.js +18 -3
  7. package/dist/check/session-completed-ac.d.ts +1 -1
  8. package/dist/check/session-completed-ac.js +1 -1
  9. package/dist/codebase/provider.js +7 -1
  10. package/dist/consumer-check-contract/evaluate.d.ts +47 -0
  11. package/dist/consumer-check-contract/evaluate.js +181 -15
  12. package/dist/delivery-attempt/evaluate.d.ts +9 -1
  13. package/dist/delivery-attempt/evaluate.js +69 -0
  14. package/dist/delivery-attempt/handoff.js +1 -1
  15. package/dist/delivery-attempt/index.d.ts +1 -1
  16. package/dist/delivery-attempt/index.js +1 -1
  17. package/dist/deposit/live-procedure-exclusions.d.ts +18 -0
  18. package/dist/deposit/live-procedure-exclusions.js +110 -0
  19. package/dist/deposit/live-procedure-targets.d.ts +45 -0
  20. package/dist/deposit/live-procedure-targets.js +274 -0
  21. package/dist/deposit/python-free.d.ts +6 -0
  22. package/dist/deposit/python-free.js +15 -0
  23. package/dist/deposit/rewrite-deposit-links.d.ts +42 -0
  24. package/dist/deposit/rewrite-deposit-links.js +148 -0
  25. package/dist/deposit/run-stage-content-pack.d.ts +2 -0
  26. package/dist/deposit/run-stage-content-pack.js +3 -0
  27. package/dist/deposit/stage-content-pack.d.ts +17 -0
  28. package/dist/deposit/stage-content-pack.js +91 -0
  29. package/dist/design-critique/citation-grammar.d.ts +60 -0
  30. package/dist/design-critique/citation-grammar.js +303 -0
  31. package/dist/design-critique/completed-arc-record.d.ts +62 -0
  32. package/dist/design-critique/completed-arc-record.js +277 -0
  33. package/dist/freshness/bind.d.ts +32 -3
  34. package/dist/freshness/bind.js +58 -12
  35. package/dist/hooks/classify/host-session-identity.d.ts +89 -0
  36. package/dist/hooks/classify/host-session-identity.js +467 -0
  37. package/dist/hooks/classify/index.d.ts +3 -2
  38. package/dist/hooks/classify/index.js +3 -2
  39. package/dist/hooks/classify/paths.d.ts +9 -0
  40. package/dist/hooks/classify/paths.js +39 -0
  41. package/dist/hooks/classify/stdin.d.ts +5 -0
  42. package/dist/hooks/classify/stdin.js +94 -1
  43. package/dist/hooks/dest-form.d.ts +20 -1
  44. package/dist/hooks/dest-form.js +158 -21
  45. package/dist/hooks/dispatcher.d.ts +58 -3
  46. package/dist/hooks/dispatcher.js +762 -73
  47. package/dist/hooks/fixtures/cases.d.ts +22 -2
  48. package/dist/hooks/fixtures/cases.js +228 -0
  49. package/dist/hooks/git-destructive-log.d.ts +32 -0
  50. package/dist/hooks/git-destructive-log.js +46 -0
  51. package/dist/hooks/index.d.ts +2 -0
  52. package/dist/hooks/index.js +2 -0
  53. package/dist/hooks/scope.js +3 -1
  54. package/dist/hooks/shell-write-targets.d.ts +10 -0
  55. package/dist/hooks/shell-write-targets.js +274 -0
  56. package/dist/hooks/tools.d.ts +20 -2
  57. package/dist/hooks/tools.js +30 -1
  58. package/dist/init-deposit/agent-hooks.d.ts +2 -1
  59. package/dist/init-deposit/agent-hooks.js +8 -1
  60. package/dist/init-deposit/gitignore.d.ts +7 -0
  61. package/dist/init-deposit/gitignore.js +24 -0
  62. package/dist/init-deposit/hook-runtime-travel.d.ts +77 -0
  63. package/dist/init-deposit/hook-runtime-travel.js +269 -0
  64. package/dist/init-deposit/init-deposit.js +3 -0
  65. package/dist/init-deposit/refresh.js +7 -0
  66. package/dist/init-deposit/runtime-writers.d.ts +14 -0
  67. package/dist/init-deposit/runtime-writers.js +33 -0
  68. package/dist/intake/clause-derivation.js +9 -3
  69. package/dist/intake/issue-ingest.d.ts +6 -1
  70. package/dist/intake/issue-ingest.js +83 -4
  71. package/dist/intake/platform-capabilities.d.ts +9 -2
  72. package/dist/intake/platform-capabilities.js +38 -9
  73. package/dist/intake/reconcile-issues.js +13 -13
  74. package/dist/lifecycle/brief-envelope.d.ts +25 -0
  75. package/dist/lifecycle/brief-envelope.js +42 -0
  76. package/dist/lifecycle/index.d.ts +1 -0
  77. package/dist/lifecycle/index.js +1 -0
  78. package/dist/literal-acceptance/evaluate.js +14 -5
  79. package/dist/literal-acceptance/index.d.ts +1 -1
  80. package/dist/literal-acceptance/index.js +1 -1
  81. package/dist/literal-acceptance/run.d.ts +2 -0
  82. package/dist/literal-acceptance/run.js +19 -1
  83. package/dist/orphan-active/candidate-scope.d.ts +53 -0
  84. package/dist/orphan-active/candidate-scope.js +157 -0
  85. package/dist/orphan-active/evaluate.d.ts +52 -0
  86. package/dist/orphan-active/evaluate.js +250 -128
  87. package/dist/orphan-active/index.d.ts +2 -0
  88. package/dist/orphan-active/index.js +2 -0
  89. package/dist/orphan-active/issue-state.d.ts +121 -0
  90. package/dist/orphan-active/issue-state.js +278 -0
  91. package/dist/platform/cursor-managed-runtime.d.ts +95 -0
  92. package/dist/platform/cursor-managed-runtime.js +241 -0
  93. package/dist/platform/index.d.ts +1 -0
  94. package/dist/platform/index.js +1 -0
  95. package/dist/platform/platform-capabilities.d.ts +23 -1
  96. package/dist/platform/platform-capabilities.js +55 -15
  97. package/dist/policy/ceremony-dial.js +5 -9
  98. package/dist/policy/host-hooks.js +4 -9
  99. package/dist/policy/merge-approval-head.js +7 -6
  100. package/dist/policy/org-force-on-migration.js +5 -10
  101. package/dist/policy/plan-extensions.d.ts +14 -2
  102. package/dist/policy/plan-extensions.js +24 -3
  103. package/dist/policy/product-signal.js +5 -10
  104. package/dist/policy/require-human-merge.js +5 -9
  105. package/dist/policy/resolve.js +77 -16
  106. package/dist/policy/value-feedback.js +9 -18
  107. package/dist/pr-closeout-attestable/evaluate.d.ts +80 -0
  108. package/dist/pr-closeout-attestable/evaluate.js +306 -0
  109. package/dist/pr-closeout-attestable/index.d.ts +2 -0
  110. package/dist/pr-closeout-attestable/index.js +2 -0
  111. package/dist/pr-closing-keywords/gh.js +32 -9
  112. package/dist/pr-closing-keywords/main.d.ts +1 -0
  113. package/dist/pr-closing-keywords/main.js +68 -3
  114. package/dist/pr-closing-keywords/types.d.ts +2 -0
  115. package/dist/pr-merge-readiness/gh.js +32 -9
  116. package/dist/pr-protected-issues/gh.d.ts +6 -2
  117. package/dist/pr-protected-issues/gh.js +42 -11
  118. package/dist/pr-wait-mergeable/cascade.d.ts +9 -1
  119. package/dist/pr-wait-mergeable/cascade.js +21 -1
  120. package/dist/pr-wait-mergeable/types.d.ts +2 -0
  121. package/dist/pr-wait-mergeable/wrappers.d.ts +6 -0
  122. package/dist/pr-wait-mergeable/wrappers.js +14 -3
  123. package/dist/preflight/evaluate.d.ts +15 -0
  124. package/dist/preflight/evaluate.js +29 -3
  125. package/dist/product-first-done-gate/acceptance-resolver.d.ts +14 -2
  126. package/dist/product-first-done-gate/acceptance-resolver.js +13 -2
  127. package/dist/product-first-done-gate/evaluate.js +23 -9
  128. package/dist/product-first-done-gate/types.js +2 -0
  129. package/dist/release/cli-drift-report.d.ts +68 -0
  130. package/dist/release/cli-drift-report.js +189 -0
  131. package/dist/release/consumer-hard-stops.d.ts +48 -0
  132. package/dist/release/consumer-hard-stops.js +140 -0
  133. package/dist/release/consumer-readiness-disclosure.d.ts +18 -0
  134. package/dist/release/consumer-readiness-disclosure.js +51 -0
  135. package/dist/release/index.d.ts +3 -0
  136. package/dist/release/index.js +3 -0
  137. package/dist/release/issue-state-fetch.d.ts +5 -3
  138. package/dist/release/issue-state-fetch.js +86 -21
  139. package/dist/release/native-steps.js +1 -0
  140. package/dist/release/pipeline.js +74 -0
  141. package/dist/release/run-consumer-readiness.d.ts +15 -0
  142. package/dist/release/run-consumer-readiness.js +24 -0
  143. package/dist/release/types.d.ts +20 -0
  144. package/dist/render/constants.d.ts +4 -0
  145. package/dist/render/constants.js +11 -8
  146. package/dist/render/export-spec.js +31 -5
  147. package/dist/render/index.d.ts +1 -1
  148. package/dist/render/index.js +1 -1
  149. package/dist/render/prd-render.d.ts +4 -1
  150. package/dist/render/prd-render.js +82 -23
  151. package/dist/render/project-render.js +14 -7
  152. package/dist/render/scope-outlook.d.ts +2 -0
  153. package/dist/render/scope-outlook.js +3 -0
  154. package/dist/render/spec-render.js +2 -2
  155. package/dist/resolution/package-manager.d.ts +33 -4
  156. package/dist/resolution/package-manager.js +210 -9
  157. package/dist/review-monitor/constants.d.ts +7 -0
  158. package/dist/review-monitor/constants.js +7 -0
  159. package/dist/review-monitor/github-lease.d.ts +98 -1
  160. package/dist/review-monitor/github-lease.js +186 -4
  161. package/dist/review-monitor/lease-comment.d.ts +36 -0
  162. package/dist/review-monitor/lease-comment.js +103 -2
  163. package/dist/run-summary/types.d.ts +2 -2
  164. package/dist/scm/build-command.d.ts +2 -2
  165. package/dist/scm/build-command.js +2 -2
  166. package/dist/scm/call-shape.d.ts +25 -0
  167. package/dist/scm/call-shape.js +59 -0
  168. package/dist/scm/call.d.ts +7 -4
  169. package/dist/scm/call.js +48 -9
  170. package/dist/scm/design-critique-chip.d.ts +1 -0
  171. package/dist/scm/design-critique-chip.js +22 -6
  172. package/dist/scm/gh-rest.d.ts +34 -13
  173. package/dist/scm/gh-rest.js +168 -15
  174. package/dist/scm/index.d.ts +2 -0
  175. package/dist/scm/index.js +2 -0
  176. package/dist/scm/readiness.d.ts +2 -0
  177. package/dist/scm/readiness.js +28 -4
  178. package/dist/scm/spawn-status.d.ts +33 -0
  179. package/dist/scm/spawn-status.js +53 -0
  180. package/dist/scope/acceptance-evidence.d.ts +1 -1
  181. package/dist/scope/project-definition-sync.js +14 -7
  182. package/dist/scope/transition.js +4 -14
  183. package/dist/session/ac-pass-banking.d.ts +2 -2
  184. package/dist/session/ac-pass-banking.js +2 -2
  185. package/dist/session/git.d.ts +24 -0
  186. package/dist/session/git.js +138 -2
  187. package/dist/session/host-session-owner.d.ts +53 -0
  188. package/dist/session/host-session-owner.js +89 -0
  189. package/dist/session/index.d.ts +1 -0
  190. package/dist/session/index.js +1 -0
  191. package/dist/session/occupancy.d.ts +259 -13
  192. package/dist/session/occupancy.js +877 -33
  193. package/dist/session/ritual-sentinel.d.ts +5 -0
  194. package/dist/session/ritual-sentinel.js +5 -0
  195. package/dist/session/session-ready.d.ts +5 -1
  196. package/dist/session/session-ready.js +98 -8
  197. package/dist/session/session-start.d.ts +5 -1
  198. package/dist/session/session-start.js +32 -17
  199. package/dist/session/toolchain-preflight.d.ts +13 -3
  200. package/dist/session/toolchain-preflight.js +93 -18
  201. package/dist/session/verify-ac-session-cache.d.ts +2 -2
  202. package/dist/session/verify-ac-session-cache.js +2 -2
  203. package/dist/session/verify-session-ritual.d.ts +11 -0
  204. package/dist/session/verify-session-ritual.js +60 -15
  205. package/dist/slice/constants.d.ts +1 -1
  206. package/dist/spec-authority/constants.d.ts +10 -2
  207. package/dist/spec-authority/constants.js +53 -8
  208. package/dist/spec-authority/resolver.d.ts +3 -0
  209. package/dist/spec-authority/resolver.js +55 -7
  210. package/dist/subprocess/max-buffer.d.ts +14 -0
  211. package/dist/subprocess/max-buffer.js +15 -0
  212. package/dist/swarm/complete-cohort.d.ts +2 -0
  213. package/dist/swarm/complete-cohort.js +21 -13
  214. package/dist/swarm/index.d.ts +1 -0
  215. package/dist/swarm/index.js +1 -0
  216. package/dist/swarm/launch-cli.js +53 -33
  217. package/dist/swarm/launch.d.ts +8 -0
  218. package/dist/swarm/launch.js +63 -43
  219. package/dist/swarm/pre-dispatch-cli.js +2 -1
  220. package/dist/swarm/pre-dispatch.js +10 -1
  221. package/dist/swarm/subagent-status-dir.d.ts +27 -0
  222. package/dist/swarm/subagent-status-dir.js +42 -0
  223. package/dist/swarm/worktrees.d.ts +17 -3
  224. package/dist/swarm/worktrees.js +48 -4
  225. package/dist/triage/evaluate/worktrees.js +153 -6
  226. package/dist/triage/scope/mutations-core.d.ts +2 -6
  227. package/dist/triage/scope/mutations-core.js +23 -55
  228. package/dist/triage/scope-drift/add-ignore.js +5 -39
  229. package/dist/triage/subscribe/index.d.ts +2 -6
  230. package/dist/triage/subscribe/index.js +15 -65
  231. package/dist/triage/welcome/writers.js +15 -74
  232. package/dist/umbrella-current-shape/index.d.ts +51 -3
  233. package/dist/umbrella-current-shape/index.js +106 -18
  234. package/dist/validate-content/deposit-required.d.ts +39 -0
  235. package/dist/validate-content/deposit-required.js +147 -0
  236. package/dist/validate-content/index.d.ts +1 -0
  237. package/dist/validate-content/index.js +1 -0
  238. package/dist/validate-content/validate-links.d.ts +2 -3
  239. package/dist/validate-content/validate-links.js +29 -2
  240. package/dist/vbrief-activate/activate.d.ts +7 -2
  241. package/dist/vbrief-activate/activate.js +26 -13
  242. package/dist/vbrief-build/index.d.ts +2 -1
  243. package/dist/vbrief-build/index.js +2 -1
  244. package/dist/vbrief-build/parity-scenarios.js +5 -5
  245. package/dist/vbrief-build/project-definition-io.d.ts +63 -3
  246. package/dist/vbrief-build/project-definition-io.js +357 -31
  247. package/dist/vbrief-build/project-definition-mutation.d.ts +32 -0
  248. package/dist/vbrief-build/project-definition-mutation.js +43 -0
  249. package/dist/vbrief-validate/conformance.d.ts +1 -0
  250. package/dist/vbrief-validate/conformance.js +85 -8
  251. package/dist/vbrief-validate/main.js +2 -0
  252. package/dist/vbrief-validate/precutover.js +5 -10
  253. package/dist/verify-ac/clauses.d.ts +62 -2
  254. package/dist/verify-ac/clauses.js +183 -111
  255. package/dist/verify-ac/evaluate.d.ts +9 -0
  256. package/dist/verify-ac/evaluate.js +32 -9
  257. package/dist/verify-ac/index.d.ts +1 -1
  258. package/dist/verify-ac/index.js +1 -1
  259. package/dist/verify-env/node-runtime.d.ts +8 -4
  260. package/dist/verify-env/node-runtime.js +9 -6
  261. package/dist/verify-env/toolchain-check.d.ts +35 -3
  262. package/dist/verify-env/toolchain-check.js +155 -36
  263. package/dist/verify-source/deposit-closure.d.ts +23 -0
  264. package/dist/verify-source/deposit-closure.js +162 -0
  265. package/dist/verify-source/index.d.ts +2 -0
  266. package/dist/verify-source/index.js +2 -0
  267. package/dist/verify-source/project-definition-mutation-boundary.d.ts +104 -0
  268. package/dist/verify-source/project-definition-mutation-boundary.js +326 -0
  269. package/dist/verify-source/semantic-single-source.d.ts +36 -0
  270. package/dist/verify-source/semantic-single-source.js +349 -0
  271. package/package.json +7 -3
@@ -0,0 +1,467 @@
1
+ /**
2
+ * Cooperative host-session identity and exact lifecycle-command rewriting (#3611).
3
+ *
4
+ * This is payload/environment classification, not authentication. Host hook JSON
5
+ * is locally forgeable under the occupancy model's cooperating-process
6
+ * assumption. The provider table and the canonical owner form live in
7
+ * `session/host-session-owner.ts` because the CLI claim path resolves the same
8
+ * owner from the same host (#3873).
9
+ */
10
+ import { ambientHostSessionOwner, canonicalHostSessionId, HOST_IDENTITY_PROVIDERS, hookHostIdentitySource, isUsableHostSessionId, MAX_HOOK_HOST_IDENTITY_UTF8_BYTES, readHostEnvIdentity, } from "../../session/host-session-owner.js";
11
+ import { isShellTool } from "../tools.js";
12
+ import { record, toolInputRecord } from "./payload.js";
13
+ import { hookToolName } from "./tool-name.js";
14
+ export { ambientHostSessionOwner, HOST_IDENTITY_PROVIDERS, hookHostIdentitySource, MAX_HOOK_HOST_IDENTITY_UTF8_BYTES, };
15
+ function identityField(input, fieldName) {
16
+ if (!(fieldName in input))
17
+ return { status: "missing", value: null };
18
+ const raw = input[fieldName];
19
+ if (typeof raw !== "string" || !isUsableHostSessionId(raw)) {
20
+ return { status: "invalid", value: null };
21
+ }
22
+ return { status: "ok", value: raw };
23
+ }
24
+ function unresolvedHostIdentity(status, provider, message) {
25
+ return { status, provider, rawSessionId: null, sessionId: null, message };
26
+ }
27
+ function resolvedHostIdentity(provider, rawSessionId) {
28
+ return {
29
+ status: "ok",
30
+ provider,
31
+ rawSessionId,
32
+ sessionId: canonicalHostSessionId(provider, rawSessionId),
33
+ message: null,
34
+ };
35
+ }
36
+ /**
37
+ * Resolve the stable conversation/session-family key documented by each host.
38
+ *
39
+ * - Codex: payload `session_id` (parent and subagents share it).
40
+ * - Claude Code: payload `session_id` (`agent_id` is not the owner key).
41
+ * - Cursor: payload `conversation_id`; simultaneous `session_id` must agree.
42
+ * - Grok: hook process `GROK_SESSION_ID` (#3873). The payload `session_id` is
43
+ * deliberately not read -- that contract is unverified.
44
+ * - Unknown hosts: no guessed identity; callers keep the explicit/env flow.
45
+ */
46
+ export function resolveHookHostIdentity(host, payload, environ = process.env) {
47
+ const source = hookHostIdentitySource(host);
48
+ if (source === null) {
49
+ return unresolvedHostIdentity("unsupported", host, `Host ${host || "<empty>"} has no verified session identity contract.`);
50
+ }
51
+ const provider = host;
52
+ if (source.kind === "host-env") {
53
+ const value = readHostEnvIdentity(environ, source.variable);
54
+ if (value.status !== "ok") {
55
+ return unresolvedHostIdentity(value.status, provider, `${provider} hook process environment ` +
56
+ `${value.status === "missing" ? "omits" : "has invalid"} ${source.variable}.`);
57
+ }
58
+ return resolvedHostIdentity(provider, value.rawSessionId);
59
+ }
60
+ const input = record(payload);
61
+ if (input === null) {
62
+ return unresolvedHostIdentity("invalid", provider, `${provider} hook payload is not an object.`);
63
+ }
64
+ if (provider !== "cursor") {
65
+ const session = identityField(input, source.field);
66
+ if (session.status !== "ok") {
67
+ return unresolvedHostIdentity(session.status, provider, `${provider} hook payload ${session.status === "missing" ? "omits" : "has invalid"} ${source.field}.`);
68
+ }
69
+ return resolvedHostIdentity(provider, session.value);
70
+ }
71
+ const conversation = identityField(input, source.field);
72
+ if (conversation.status !== "ok") {
73
+ return unresolvedHostIdentity(conversation.status, provider, `Cursor hook payload ${conversation.status === "missing" ? "omits" : "has invalid"} conversation_id.`);
74
+ }
75
+ const simultaneousSession = identityField(input, "session_id");
76
+ if (simultaneousSession.status === "invalid") {
77
+ return unresolvedHostIdentity("invalid", provider, "Cursor hook payload has invalid session_id alongside conversation_id.");
78
+ }
79
+ if (simultaneousSession.status === "ok" && simultaneousSession.value !== conversation.value) {
80
+ return unresolvedHostIdentity("conflict", provider, "Cursor hook identity conflict: conversation_id and session_id differ.");
81
+ }
82
+ return resolvedHostIdentity(provider, conversation.value);
83
+ }
84
+ /**
85
+ * True when a `host-env` provider simply did not publish its variable (#3873).
86
+ *
87
+ * Absence is the pre-#3873 state rather than a broken contract, so callers keep
88
+ * the documented explicit `--session-id` / `DEFT_SESSION_ID` flow instead of
89
+ * failing a host that never had a hook identity to begin with. It is never an
90
+ * admission: with no explicit owner the actor stays unset and the write gate
91
+ * still denies. A present-but-malformed variable is `invalid` and fails closed.
92
+ */
93
+ export function hostIdentityFallsBackToExplicitOwner(host, resolution) {
94
+ return resolution.status === "missing" && hookHostIdentitySource(host)?.kind === "host-env";
95
+ }
96
+ export const EXACT_LIFECYCLE_VERBS = [
97
+ "session:start",
98
+ "session:ready",
99
+ "session:end",
100
+ "occupancy:steal",
101
+ "occupancy:release",
102
+ "occupancy:heartbeat",
103
+ "swarm:launch",
104
+ ];
105
+ const DIRECT_LIFECYCLE_VERBS = {
106
+ "session:start": "session:start",
107
+ "session:ready": "session:ready",
108
+ "session:end": "session:end",
109
+ "occupancy:steal": "occupancy:steal",
110
+ "occupancy:release": "occupancy:release",
111
+ "occupancy:heartbeat": "occupancy:heartbeat",
112
+ "swarm-launch": "swarm:launch",
113
+ };
114
+ // Derived from the provider list so the rewrite surface cannot drift from the
115
+ // identity surface: a provider added to one is added to both (#3873). Provider
116
+ // ids are lowercase ASCII words, so the alternation needs no escaping.
117
+ const CANONICAL_OWNER_PATTERN = new RegExp(`^host:(?:${HOST_IDENTITY_PROVIDERS.join("|")}):v1:[A-Za-z0-9_-]+$`);
118
+ // Shell expansion markers are deliberately absent: `$`/backticks for POSIX,
119
+ // `@` splatting for PowerShell, and `%NAME%` expansion for command shells.
120
+ // Backslashes are inspectable so Windows path-bearing lifecycle commands fail
121
+ // closed, but they remain outside the auto-approved rewrite surface below.
122
+ const INSPECTABLE_TOKEN_PATTERN = /^[A-Za-z0-9_./\\:+=,-]+$/;
123
+ const LIFECYCLE_ARGUMENT_POLICIES = {
124
+ "session:start": {
125
+ booleanFlags: new Set([
126
+ "--json",
127
+ "--no-history",
128
+ "--read-only",
129
+ "--with-network",
130
+ "--rearm",
131
+ "--hard-budget",
132
+ "--compact",
133
+ "--steal",
134
+ "--confirm",
135
+ ]),
136
+ valueFlags: new Set([
137
+ "--tier",
138
+ "--defer",
139
+ "--task-size",
140
+ "--ceremony-task-size",
141
+ "--model-tier",
142
+ "--ceremony-model-tier",
143
+ "--project-shape",
144
+ "--ceremony-project-shape",
145
+ "--ceremony-depth",
146
+ "--max-turns",
147
+ "--max-budget",
148
+ "--session-id",
149
+ "--occupant",
150
+ "--project-root",
151
+ ]),
152
+ rewriteUnsafeFlags: new Set(["--project-root"]),
153
+ },
154
+ "session:ready": {
155
+ booleanFlags: new Set(["--json", "--with-network"]),
156
+ valueFlags: new Set(["--repo", "--session-id", "--project-root"]),
157
+ rewriteUnsafeFlags: new Set(["--project-root"]),
158
+ },
159
+ "session:end": {
160
+ booleanFlags: new Set(),
161
+ valueFlags: new Set(["--session-id", "--project-root"]),
162
+ rewriteUnsafeFlags: new Set(["--project-root"]),
163
+ },
164
+ "occupancy:steal": {
165
+ booleanFlags: new Set(["--confirm"]),
166
+ valueFlags: new Set(["--occupant", "--session-id", "--project-root"]),
167
+ rewriteUnsafeFlags: new Set(["--project-root"]),
168
+ },
169
+ "occupancy:release": {
170
+ booleanFlags: new Set(),
171
+ valueFlags: new Set(["--session-id", "--project-root"]),
172
+ rewriteUnsafeFlags: new Set(["--project-root"]),
173
+ },
174
+ "occupancy:heartbeat": {
175
+ booleanFlags: new Set(),
176
+ valueFlags: new Set(["--session-id", "--project-root"]),
177
+ rewriteUnsafeFlags: new Set(["--project-root"]),
178
+ },
179
+ "swarm:launch": {
180
+ booleanFlags: new Set([
181
+ "--autonomous",
182
+ "--no-create-worktrees",
183
+ "--enforce-gates",
184
+ "--no-audit",
185
+ ]),
186
+ valueFlags: new Set([
187
+ "--stories",
188
+ "--paths",
189
+ "--group",
190
+ "--worktree-map",
191
+ "--base-branch",
192
+ "--allocation-plan-id",
193
+ "--batching-rationale",
194
+ "--operator-approval",
195
+ "--output",
196
+ "--gate-clearances",
197
+ "--project-root",
198
+ "--session-id",
199
+ ]),
200
+ rewriteUnsafeFlags: new Set([
201
+ "--paths",
202
+ "--worktree-map",
203
+ "--output",
204
+ "--gate-clearances",
205
+ "--project-root",
206
+ "--no-audit",
207
+ ]),
208
+ separateOnlyValueFlags: new Set([
209
+ "--stories",
210
+ "--paths",
211
+ "--group",
212
+ "--worktree-map",
213
+ "--base-branch",
214
+ "--allocation-plan-id",
215
+ "--batching-rationale",
216
+ "--operator-approval",
217
+ "--output",
218
+ "--gate-clearances",
219
+ "--project-root",
220
+ ]),
221
+ },
222
+ };
223
+ function lifecycleArgumentValueIsRewriteSafe(verb, flag, value) {
224
+ if (verb !== "swarm:launch" || flag !== "--stories")
225
+ return true;
226
+ // `swarm:launch --stories` accepts both logical IDs and filesystem paths.
227
+ // Only the ID form belongs in the auto-approved rewrite bridge; path-bearing
228
+ // invocations must keep the host's ordinary permission decision.
229
+ return value
230
+ .split(",")
231
+ .every((story) => story.length > 0 &&
232
+ !story.endsWith(".json") &&
233
+ !story.includes("/") &&
234
+ !story.includes("\\"));
235
+ }
236
+ /**
237
+ * Validate the complete CLI argument surface before returning an allow+rewrite.
238
+ * Path rebinding/destination flags and unknown/future flags are recognized but
239
+ * kept outside the auto-rewrite bridge. Supported hosts must carry the matching
240
+ * owner explicitly before those commands retain normal permission handling.
241
+ */
242
+ function analyzeLifecycleArguments(verb, args) {
243
+ const policy = LIFECYCLE_ARGUMENT_POLICIES[verb];
244
+ let rewriteSafe = true;
245
+ let readOnly = false;
246
+ for (let index = 0; index < args.length; index += 1) {
247
+ const token = args[index] ?? "";
248
+ if (policy.booleanFlags.has(token)) {
249
+ if (verb === "session:start" && token === "--read-only")
250
+ readOnly = true;
251
+ if (policy.rewriteUnsafeFlags.has(token))
252
+ rewriteSafe = false;
253
+ continue;
254
+ }
255
+ const equals = token.indexOf("=");
256
+ if (equals > 0) {
257
+ const flag = token.slice(0, equals);
258
+ const value = token.slice(equals + 1);
259
+ if (!policy.valueFlags.has(flag)) {
260
+ rewriteSafe = false;
261
+ continue;
262
+ }
263
+ if (policy.rewriteUnsafeFlags.has(flag))
264
+ rewriteSafe = false;
265
+ if (policy.separateOnlyValueFlags?.has(flag) === true)
266
+ rewriteSafe = false;
267
+ // Keep malformed session identity visible to inspection so the
268
+ // dispatcher can deny ambiguity instead of silently skipping rewrite.
269
+ if (value.length === 0 && flag !== "--session-id")
270
+ rewriteSafe = false;
271
+ if (!lifecycleArgumentValueIsRewriteSafe(verb, flag, value))
272
+ rewriteSafe = false;
273
+ continue;
274
+ }
275
+ if (!policy.valueFlags.has(token)) {
276
+ rewriteSafe = false;
277
+ continue;
278
+ }
279
+ if (policy.rewriteUnsafeFlags.has(token))
280
+ rewriteSafe = false;
281
+ const value = args[index + 1];
282
+ if (value === undefined) {
283
+ if (token !== "--session-id")
284
+ rewriteSafe = false;
285
+ continue;
286
+ }
287
+ if (value.startsWith("--"))
288
+ rewriteSafe = false;
289
+ if (!lifecycleArgumentValueIsRewriteSafe(verb, token, value))
290
+ rewriteSafe = false;
291
+ // Match the real lifecycle parsers: known value flags consume the next
292
+ // argv token even when it begins with `--`. This prevents a swallowed
293
+ // `--session-id=...` from being mistaken for an effective owner.
294
+ index += 1;
295
+ }
296
+ return { rewriteSafe, readOnly };
297
+ }
298
+ /**
299
+ * Inspect single-space-separated lifecycle commands while excluding shell
300
+ * quoting, substitution, redirection, chaining, aliases, and wrappers.
301
+ * Windows path separators remain inspectable but make rewriting unsafe.
302
+ */
303
+ function exactLifecycleInvocation(command) {
304
+ if (command.length === 0 || command !== command.trim())
305
+ return null;
306
+ const tokens = command.split(" ");
307
+ if (tokens.some((token) => token.length === 0 || !INSPECTABLE_TOKEN_PATTERN.test(token))) {
308
+ return null;
309
+ }
310
+ if (tokens.length < 2)
311
+ return null;
312
+ const executable = tokens[0];
313
+ const verb = tokens[1];
314
+ if (executable === "deft" || executable === "directive") {
315
+ const typedVerb = verb === undefined ? undefined : DIRECT_LIFECYCLE_VERBS[verb];
316
+ if (typedVerb === undefined)
317
+ return null;
318
+ const forwardedArgs = tokens.slice(2);
319
+ const { rewriteSafe, readOnly } = analyzeLifecycleArguments(typedVerb, forwardedArgs);
320
+ const requiresOwner = typedVerb !== "session:start" || !readOnly;
321
+ return {
322
+ verb: typedVerb,
323
+ task: false,
324
+ forwardedArgs,
325
+ rewriteSafe: rewriteSafe && forwardedArgs.every((token) => !token.includes("\\")),
326
+ requiresOwner,
327
+ };
328
+ }
329
+ if (executable !== "task")
330
+ return null;
331
+ if (!EXACT_LIFECYCLE_VERBS.includes(verb ?? ""))
332
+ return null;
333
+ const typedVerb = verb;
334
+ if (tokens.length === 2) {
335
+ return {
336
+ verb: typedVerb,
337
+ task: true,
338
+ forwardedArgs: [],
339
+ rewriteSafe: true,
340
+ requiresOwner: true,
341
+ };
342
+ }
343
+ // Go Task's canonical CLI_ARGS boundary. Flags without `--` are ambiguous
344
+ // Task CLI flags and must not receive an auto-approving rewrite.
345
+ if (tokens[2] !== "--")
346
+ return null;
347
+ const forwardedArgs = tokens.slice(3);
348
+ const { rewriteSafe, readOnly } = analyzeLifecycleArguments(typedVerb, forwardedArgs);
349
+ const requiresOwner = typedVerb !== "session:start" || !readOnly;
350
+ return {
351
+ verb: typedVerb,
352
+ task: true,
353
+ forwardedArgs,
354
+ rewriteSafe: rewriteSafe && forwardedArgs.every((token) => !token.includes("\\")),
355
+ requiresOwner,
356
+ };
357
+ }
358
+ function exactLifecyclePayload(payload) {
359
+ const toolName = hookToolName(payload);
360
+ if (toolName === null || !isShellTool(toolName)) {
361
+ return null;
362
+ }
363
+ const input = record(payload);
364
+ if (input === null)
365
+ return null;
366
+ const toolInput = toolInputRecord(input);
367
+ if (toolInput === null || typeof toolInput.command !== "string")
368
+ return null;
369
+ const originalCommand = toolInput.command;
370
+ const invocation = exactLifecycleInvocation(originalCommand);
371
+ return invocation === null ? null : { invocation, originalCommand, toolInput };
372
+ }
373
+ /** Logical lifecycle verb for one exact, simple shell command; otherwise null. */
374
+ export function exactLifecycleCommandVerb(payload) {
375
+ return exactLifecyclePayload(payload)?.invocation.verb ?? null;
376
+ }
377
+ function sessionIdArgs(verb, args) {
378
+ const policy = LIFECYCLE_ARGUMENT_POLICIES[verb];
379
+ const values = [];
380
+ for (let i = 0; i < args.length; i += 1) {
381
+ const token = args[i] ?? "";
382
+ if (token.startsWith("--session-id=")) {
383
+ const value = token.slice("--session-id=".length);
384
+ if (value.length === 0)
385
+ return { status: "invalid", values: [] };
386
+ values.push(value);
387
+ continue;
388
+ }
389
+ if (token === "--session-id") {
390
+ const value = args[i + 1];
391
+ if (value === undefined ||
392
+ value.length === 0 ||
393
+ (verb === "swarm:launch" && value.startsWith("--"))) {
394
+ return { status: "invalid", values: [] };
395
+ }
396
+ values.push(value);
397
+ i += 1;
398
+ continue;
399
+ }
400
+ if (token.indexOf("=") < 0 && policy.valueFlags.has(token) && args[i + 1] !== undefined)
401
+ i += 1;
402
+ }
403
+ if (values.length === 0)
404
+ return { status: "absent", values };
405
+ if (values.length !== 1)
406
+ return { status: "invalid", values };
407
+ return { status: "present", values };
408
+ }
409
+ /** Inspect one exact lifecycle command without requiring a host identity. */
410
+ export function inspectExactLifecycleCommand(payload) {
411
+ const exact = exactLifecyclePayload(payload);
412
+ if (exact === null)
413
+ return null;
414
+ const session = sessionIdArgs(exact.invocation.verb, exact.invocation.forwardedArgs);
415
+ return {
416
+ verb: exact.invocation.verb,
417
+ task: exact.invocation.task,
418
+ rewriteSafe: exact.invocation.rewriteSafe,
419
+ requiresOwner: exact.invocation.requiresOwner,
420
+ sessionIdStatus: session.status,
421
+ sessionId: session.status === "present" ? (session.values[0] ?? null) : null,
422
+ };
423
+ }
424
+ /**
425
+ * Append a canonical host owner to one exact Directive lifecycle command.
426
+ *
427
+ * The returned `updatedInput` is a complete shallow clone of the original
428
+ * host tool input. `null` means no safe rewrite. An explicit foreign identity
429
+ * is surfaced as a conflict so dispatcher wiring can deny instead of replacing
430
+ * operator input.
431
+ */
432
+ export function rewriteExactLifecycleCommand(payload, sessionId) {
433
+ if (!CANONICAL_OWNER_PATTERN.test(sessionId))
434
+ return null;
435
+ const exact = exactLifecyclePayload(payload);
436
+ if (exact === null)
437
+ return null;
438
+ const { invocation, originalCommand, toolInput } = exact;
439
+ if (!invocation.rewriteSafe || !invocation.requiresOwner)
440
+ return null;
441
+ const existing = sessionIdArgs(invocation.verb, invocation.forwardedArgs);
442
+ if (existing.status === "invalid")
443
+ return null;
444
+ if (existing.status === "present") {
445
+ const existingSessionId = existing.values[0] ?? "";
446
+ if (existingSessionId === sessionId)
447
+ return null;
448
+ return {
449
+ kind: "conflict",
450
+ verb: invocation.verb,
451
+ existingSessionId,
452
+ requestedSessionId: sessionId,
453
+ message: `Lifecycle command ${invocation.verb} already names a different ` +
454
+ "--session-id; refusing rewrite.",
455
+ };
456
+ }
457
+ const taskForwarding = invocation.task && originalCommand.split(" ").length === 2 ? " --" : "";
458
+ const rewrittenCommand = `${originalCommand}${taskForwarding} --session-id=${sessionId}`;
459
+ return {
460
+ kind: "rewrite",
461
+ verb: invocation.verb,
462
+ originalCommand,
463
+ rewrittenCommand,
464
+ updatedInput: { ...toolInput, command: rewrittenCommand },
465
+ };
466
+ }
467
+ //# sourceMappingURL=host-session-identity.js.map
@@ -5,9 +5,10 @@
5
5
  * Policy / ritual / scope / permission emission stay in dispatcher + CLI adapter.
6
6
  */
7
7
  export { classifyHookPayload } from "./classify.js";
8
- export { hookMcpArgsText, hookPathSet, hookShellCommand, hookWriteTargetPath, } from "./paths.js";
8
+ export { EXACT_LIFECYCLE_VERBS, type ExactLifecycleCommandConflict, type ExactLifecycleCommandInspection, type ExactLifecycleCommandResult, type ExactLifecycleCommandRewrite, type ExactLifecycleVerb, exactLifecycleCommandVerb, HOST_IDENTITY_PROVIDERS, type HookHostIdentityProvider, type HookHostIdentityResolution, type HookHostIdentitySource, type HookHostIdentityStatus, hookHostIdentitySource, hostIdentityFallsBackToExplicitOwner, inspectExactLifecycleCommand, MAX_HOOK_HOST_IDENTITY_UTF8_BYTES, resolveHookHostIdentity, rewriteExactLifecycleCommand, } from "./host-session-identity.js";
9
+ export { hookApplyPatchBodyPaths, hookApplyPatchBodyText, hookMcpArgsText, hookMutationTargetPaths, hookPathSet, hookShellCommand, hookWriteTargetPath, } from "./paths.js";
9
10
  export { fieldPresent, fieldString, firstString, hookPayloadTopLevelKeys, record, toolInputRecord, } from "./payload.js";
10
- export { parseHookStdin, stripUtf8Bom } from "./stdin.js";
11
+ export { applyPatchMutationPaths, parseHookStdin, stripUtf8Bom } from "./stdin.js";
11
12
  export { hookToolName, inferCursorDirectWriteToolName, type MissingToolNameInput, missingToolNameMessage, } from "./tool-name.js";
12
13
  export type { ClassifyHookHost, ClassifyHookPayloadInput, HookClassification, HookPayloadContext, HookWriteIntent, ParsedHookPayload, } from "./types.js";
13
14
  export { CLASSIFY_HOOK_HOSTS } from "./types.js";
@@ -5,9 +5,10 @@
5
5
  * Policy / ritual / scope / permission emission stay in dispatcher + CLI adapter.
6
6
  */
7
7
  export { classifyHookPayload } from "./classify.js";
8
- export { hookMcpArgsText, hookPathSet, hookShellCommand, hookWriteTargetPath, } from "./paths.js";
8
+ export { EXACT_LIFECYCLE_VERBS, exactLifecycleCommandVerb, HOST_IDENTITY_PROVIDERS, hookHostIdentitySource, hostIdentityFallsBackToExplicitOwner, inspectExactLifecycleCommand, MAX_HOOK_HOST_IDENTITY_UTF8_BYTES, resolveHookHostIdentity, rewriteExactLifecycleCommand, } from "./host-session-identity.js";
9
+ export { hookApplyPatchBodyPaths, hookApplyPatchBodyText, hookMcpArgsText, hookMutationTargetPaths, hookPathSet, hookShellCommand, hookWriteTargetPath, } from "./paths.js";
9
10
  export { fieldPresent, fieldString, firstString, hookPayloadTopLevelKeys, record, toolInputRecord, } from "./payload.js";
10
- export { parseHookStdin, stripUtf8Bom } from "./stdin.js";
11
+ export { applyPatchMutationPaths, parseHookStdin, stripUtf8Bom } from "./stdin.js";
11
12
  export { hookToolName, inferCursorDirectWriteToolName, missingToolNameMessage, } from "./tool-name.js";
12
13
  export { CLASSIFY_HOOK_HOSTS } from "./types.js";
13
14
  //# sourceMappingURL=index.js.map
@@ -7,6 +7,15 @@
7
7
  * Hosts disagree on nesting (`tool_input.file_path` vs top-level `path`).
8
8
  */
9
9
  export declare function hookWriteTargetPath(payload: unknown): string | null;
10
+ /** Raw ApplyPatch body text from patch / unified_diff / diff fields. */
11
+ export declare function hookApplyPatchBodyText(payload: unknown): string | null;
12
+ /** ApplyPatch body paths from patch / unified_diff / diff fields. */
13
+ export declare function hookApplyPatchBodyPaths(payload: unknown): string[];
14
+ /**
15
+ * Declared write target plus ApplyPatch body members. Used to admit one
16
+ * effectiveRoot; a span across two Git toplevels is refused (#3794).
17
+ */
18
+ export declare function hookMutationTargetPaths(payload: unknown): string[];
10
19
  /**
11
20
  * Best-effort shell command string from host PreToolUse payloads (#2711).
12
21
  * Hosts disagree on nesting (`tool_input.command` vs top-level `command`).
@@ -3,6 +3,7 @@
3
3
  * No process I/O — does not resolve against projectRoot or realpath.
4
4
  */
5
5
  import { firstString, record, toolInputRecord } from "./payload.js";
6
+ import { applyPatchMutationPaths } from "./stdin.js";
6
7
  /**
7
8
  * Best-effort write-target path from host PreToolUse payloads (#2625).
8
9
  * Hosts disagree on nesting (`tool_input.file_path` vs top-level `path`).
@@ -21,6 +22,44 @@ export function hookWriteTargetPath(payload) {
21
22
  input.path,
22
23
  ]);
23
24
  }
25
+ /** Raw ApplyPatch body text from patch / unified_diff / diff fields. */
26
+ export function hookApplyPatchBodyText(payload) {
27
+ const input = record(payload);
28
+ if (input === null)
29
+ return null;
30
+ const toolInput = toolInputRecord(input);
31
+ return firstString([
32
+ toolInput?.patch,
33
+ toolInput?.unified_diff,
34
+ toolInput?.diff,
35
+ input.patch,
36
+ input.unified_diff,
37
+ input.diff,
38
+ ]);
39
+ }
40
+ /** ApplyPatch body paths from patch / unified_diff / diff fields. */
41
+ export function hookApplyPatchBodyPaths(payload) {
42
+ const patch = hookApplyPatchBodyText(payload);
43
+ return patch === null ? [] : applyPatchMutationPaths(patch);
44
+ }
45
+ /**
46
+ * Declared write target plus ApplyPatch body members. Used to admit one
47
+ * effectiveRoot; a span across two Git toplevels is refused (#3794).
48
+ */
49
+ export function hookMutationTargetPaths(payload) {
50
+ const seen = new Set();
51
+ const out = [];
52
+ const push = (value) => {
53
+ if (value === null || value.length === 0 || seen.has(value))
54
+ return;
55
+ seen.add(value);
56
+ out.push(value);
57
+ };
58
+ push(hookWriteTargetPath(payload));
59
+ for (const path of hookApplyPatchBodyPaths(payload))
60
+ push(path);
61
+ return out;
62
+ }
24
63
  /**
25
64
  * Best-effort shell command string from host PreToolUse payloads (#2711).
26
65
  * Hosts disagree on nesting (`tool_input.command` vs top-level `command`).
@@ -4,6 +4,11 @@
4
4
  */
5
5
  import type { ParsedHookPayload } from "./types.js";
6
6
  export declare function stripUtf8Bom(raw: string): string;
7
+ /**
8
+ * Paths named by ApplyPatch mutation headers, plus `*** Move to:` rename
9
+ * destinations. Order preserved, duplicates dropped.
10
+ */
11
+ export declare function applyPatchMutationPaths(text: string): string[];
7
12
  /**
8
13
  * Parse host hook stdin text into a payload + parse context.
9
14
  * Empty → stdinEmpty; invalid JSON without free-form ApplyPatch → parseFailed.