@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.
- package/dist/architecture/sor-preflight.js +2 -0
- package/dist/cache/operations.js +1 -1
- package/dist/check/cached-orchestrator.js +28 -3
- package/dist/check/gate-lists.d.ts +14 -0
- package/dist/check/gate-lists.js +26 -3
- package/dist/check/named-cause.js +18 -3
- package/dist/check/session-completed-ac.d.ts +1 -1
- package/dist/check/session-completed-ac.js +1 -1
- package/dist/codebase/provider.js +7 -1
- package/dist/consumer-check-contract/evaluate.d.ts +47 -0
- package/dist/consumer-check-contract/evaluate.js +181 -15
- package/dist/delivery-attempt/evaluate.d.ts +9 -1
- package/dist/delivery-attempt/evaluate.js +69 -0
- package/dist/delivery-attempt/handoff.js +1 -1
- package/dist/delivery-attempt/index.d.ts +1 -1
- package/dist/delivery-attempt/index.js +1 -1
- package/dist/deposit/live-procedure-exclusions.d.ts +18 -0
- package/dist/deposit/live-procedure-exclusions.js +110 -0
- package/dist/deposit/live-procedure-targets.d.ts +45 -0
- package/dist/deposit/live-procedure-targets.js +274 -0
- package/dist/deposit/python-free.d.ts +6 -0
- package/dist/deposit/python-free.js +15 -0
- package/dist/deposit/rewrite-deposit-links.d.ts +42 -0
- package/dist/deposit/rewrite-deposit-links.js +148 -0
- package/dist/deposit/run-stage-content-pack.d.ts +2 -0
- package/dist/deposit/run-stage-content-pack.js +3 -0
- package/dist/deposit/stage-content-pack.d.ts +17 -0
- package/dist/deposit/stage-content-pack.js +91 -0
- package/dist/design-critique/citation-grammar.d.ts +60 -0
- package/dist/design-critique/citation-grammar.js +303 -0
- package/dist/design-critique/completed-arc-record.d.ts +62 -0
- package/dist/design-critique/completed-arc-record.js +277 -0
- package/dist/freshness/bind.d.ts +32 -3
- package/dist/freshness/bind.js +58 -12
- package/dist/hooks/classify/host-session-identity.d.ts +89 -0
- package/dist/hooks/classify/host-session-identity.js +467 -0
- package/dist/hooks/classify/index.d.ts +3 -2
- package/dist/hooks/classify/index.js +3 -2
- package/dist/hooks/classify/paths.d.ts +9 -0
- package/dist/hooks/classify/paths.js +39 -0
- package/dist/hooks/classify/stdin.d.ts +5 -0
- package/dist/hooks/classify/stdin.js +94 -1
- package/dist/hooks/dest-form.d.ts +20 -1
- package/dist/hooks/dest-form.js +158 -21
- package/dist/hooks/dispatcher.d.ts +58 -3
- package/dist/hooks/dispatcher.js +762 -73
- package/dist/hooks/fixtures/cases.d.ts +22 -2
- package/dist/hooks/fixtures/cases.js +228 -0
- package/dist/hooks/git-destructive-log.d.ts +32 -0
- package/dist/hooks/git-destructive-log.js +46 -0
- package/dist/hooks/index.d.ts +2 -0
- package/dist/hooks/index.js +2 -0
- package/dist/hooks/scope.js +3 -1
- package/dist/hooks/shell-write-targets.d.ts +10 -0
- package/dist/hooks/shell-write-targets.js +274 -0
- package/dist/hooks/tools.d.ts +20 -2
- package/dist/hooks/tools.js +30 -1
- package/dist/init-deposit/agent-hooks.d.ts +2 -1
- package/dist/init-deposit/agent-hooks.js +8 -1
- package/dist/init-deposit/gitignore.d.ts +7 -0
- package/dist/init-deposit/gitignore.js +24 -0
- package/dist/init-deposit/hook-runtime-travel.d.ts +77 -0
- package/dist/init-deposit/hook-runtime-travel.js +269 -0
- package/dist/init-deposit/init-deposit.js +3 -0
- package/dist/init-deposit/refresh.js +7 -0
- package/dist/init-deposit/runtime-writers.d.ts +14 -0
- package/dist/init-deposit/runtime-writers.js +33 -0
- package/dist/intake/clause-derivation.js +9 -3
- package/dist/intake/issue-ingest.d.ts +6 -1
- package/dist/intake/issue-ingest.js +83 -4
- package/dist/intake/platform-capabilities.d.ts +9 -2
- package/dist/intake/platform-capabilities.js +38 -9
- package/dist/intake/reconcile-issues.js +13 -13
- package/dist/lifecycle/brief-envelope.d.ts +25 -0
- package/dist/lifecycle/brief-envelope.js +42 -0
- package/dist/lifecycle/index.d.ts +1 -0
- package/dist/lifecycle/index.js +1 -0
- package/dist/literal-acceptance/evaluate.js +14 -5
- package/dist/literal-acceptance/index.d.ts +1 -1
- package/dist/literal-acceptance/index.js +1 -1
- package/dist/literal-acceptance/run.d.ts +2 -0
- package/dist/literal-acceptance/run.js +19 -1
- package/dist/orphan-active/candidate-scope.d.ts +53 -0
- package/dist/orphan-active/candidate-scope.js +157 -0
- package/dist/orphan-active/evaluate.d.ts +52 -0
- package/dist/orphan-active/evaluate.js +250 -128
- package/dist/orphan-active/index.d.ts +2 -0
- package/dist/orphan-active/index.js +2 -0
- package/dist/orphan-active/issue-state.d.ts +121 -0
- package/dist/orphan-active/issue-state.js +278 -0
- package/dist/platform/cursor-managed-runtime.d.ts +95 -0
- package/dist/platform/cursor-managed-runtime.js +241 -0
- package/dist/platform/index.d.ts +1 -0
- package/dist/platform/index.js +1 -0
- package/dist/platform/platform-capabilities.d.ts +23 -1
- package/dist/platform/platform-capabilities.js +55 -15
- package/dist/policy/ceremony-dial.js +5 -9
- package/dist/policy/host-hooks.js +4 -9
- package/dist/policy/merge-approval-head.js +7 -6
- package/dist/policy/org-force-on-migration.js +5 -10
- package/dist/policy/plan-extensions.d.ts +14 -2
- package/dist/policy/plan-extensions.js +24 -3
- package/dist/policy/product-signal.js +5 -10
- package/dist/policy/require-human-merge.js +5 -9
- package/dist/policy/resolve.js +77 -16
- package/dist/policy/value-feedback.js +9 -18
- package/dist/pr-closeout-attestable/evaluate.d.ts +80 -0
- package/dist/pr-closeout-attestable/evaluate.js +306 -0
- package/dist/pr-closeout-attestable/index.d.ts +2 -0
- package/dist/pr-closeout-attestable/index.js +2 -0
- package/dist/pr-closing-keywords/gh.js +32 -9
- package/dist/pr-closing-keywords/main.d.ts +1 -0
- package/dist/pr-closing-keywords/main.js +68 -3
- package/dist/pr-closing-keywords/types.d.ts +2 -0
- package/dist/pr-merge-readiness/gh.js +32 -9
- package/dist/pr-protected-issues/gh.d.ts +6 -2
- package/dist/pr-protected-issues/gh.js +42 -11
- package/dist/pr-wait-mergeable/cascade.d.ts +9 -1
- package/dist/pr-wait-mergeable/cascade.js +21 -1
- package/dist/pr-wait-mergeable/types.d.ts +2 -0
- package/dist/pr-wait-mergeable/wrappers.d.ts +6 -0
- package/dist/pr-wait-mergeable/wrappers.js +14 -3
- package/dist/preflight/evaluate.d.ts +15 -0
- package/dist/preflight/evaluate.js +29 -3
- package/dist/product-first-done-gate/acceptance-resolver.d.ts +14 -2
- package/dist/product-first-done-gate/acceptance-resolver.js +13 -2
- package/dist/product-first-done-gate/evaluate.js +23 -9
- package/dist/product-first-done-gate/types.js +2 -0
- package/dist/release/cli-drift-report.d.ts +68 -0
- package/dist/release/cli-drift-report.js +189 -0
- package/dist/release/consumer-hard-stops.d.ts +48 -0
- package/dist/release/consumer-hard-stops.js +140 -0
- package/dist/release/consumer-readiness-disclosure.d.ts +18 -0
- package/dist/release/consumer-readiness-disclosure.js +51 -0
- package/dist/release/index.d.ts +3 -0
- package/dist/release/index.js +3 -0
- package/dist/release/issue-state-fetch.d.ts +5 -3
- package/dist/release/issue-state-fetch.js +86 -21
- package/dist/release/native-steps.js +1 -0
- package/dist/release/pipeline.js +74 -0
- package/dist/release/run-consumer-readiness.d.ts +15 -0
- package/dist/release/run-consumer-readiness.js +24 -0
- package/dist/release/types.d.ts +20 -0
- package/dist/render/constants.d.ts +4 -0
- package/dist/render/constants.js +11 -8
- package/dist/render/export-spec.js +31 -5
- package/dist/render/index.d.ts +1 -1
- package/dist/render/index.js +1 -1
- package/dist/render/prd-render.d.ts +4 -1
- package/dist/render/prd-render.js +82 -23
- package/dist/render/project-render.js +14 -7
- package/dist/render/scope-outlook.d.ts +2 -0
- package/dist/render/scope-outlook.js +3 -0
- package/dist/render/spec-render.js +2 -2
- package/dist/resolution/package-manager.d.ts +33 -4
- package/dist/resolution/package-manager.js +210 -9
- package/dist/review-monitor/constants.d.ts +7 -0
- package/dist/review-monitor/constants.js +7 -0
- package/dist/review-monitor/github-lease.d.ts +98 -1
- package/dist/review-monitor/github-lease.js +186 -4
- package/dist/review-monitor/lease-comment.d.ts +36 -0
- package/dist/review-monitor/lease-comment.js +103 -2
- package/dist/run-summary/types.d.ts +2 -2
- package/dist/scm/build-command.d.ts +2 -2
- package/dist/scm/build-command.js +2 -2
- package/dist/scm/call-shape.d.ts +25 -0
- package/dist/scm/call-shape.js +59 -0
- package/dist/scm/call.d.ts +7 -4
- package/dist/scm/call.js +48 -9
- package/dist/scm/design-critique-chip.d.ts +1 -0
- package/dist/scm/design-critique-chip.js +22 -6
- package/dist/scm/gh-rest.d.ts +34 -13
- package/dist/scm/gh-rest.js +168 -15
- package/dist/scm/index.d.ts +2 -0
- package/dist/scm/index.js +2 -0
- package/dist/scm/readiness.d.ts +2 -0
- package/dist/scm/readiness.js +28 -4
- package/dist/scm/spawn-status.d.ts +33 -0
- package/dist/scm/spawn-status.js +53 -0
- package/dist/scope/acceptance-evidence.d.ts +1 -1
- package/dist/scope/project-definition-sync.js +14 -7
- package/dist/scope/transition.js +4 -14
- package/dist/session/ac-pass-banking.d.ts +2 -2
- package/dist/session/ac-pass-banking.js +2 -2
- package/dist/session/git.d.ts +24 -0
- package/dist/session/git.js +138 -2
- package/dist/session/host-session-owner.d.ts +53 -0
- package/dist/session/host-session-owner.js +89 -0
- package/dist/session/index.d.ts +1 -0
- package/dist/session/index.js +1 -0
- package/dist/session/occupancy.d.ts +259 -13
- package/dist/session/occupancy.js +877 -33
- package/dist/session/ritual-sentinel.d.ts +5 -0
- package/dist/session/ritual-sentinel.js +5 -0
- package/dist/session/session-ready.d.ts +5 -1
- package/dist/session/session-ready.js +98 -8
- package/dist/session/session-start.d.ts +5 -1
- package/dist/session/session-start.js +32 -17
- package/dist/session/toolchain-preflight.d.ts +13 -3
- package/dist/session/toolchain-preflight.js +93 -18
- package/dist/session/verify-ac-session-cache.d.ts +2 -2
- package/dist/session/verify-ac-session-cache.js +2 -2
- package/dist/session/verify-session-ritual.d.ts +11 -0
- package/dist/session/verify-session-ritual.js +60 -15
- package/dist/slice/constants.d.ts +1 -1
- package/dist/spec-authority/constants.d.ts +10 -2
- package/dist/spec-authority/constants.js +53 -8
- package/dist/spec-authority/resolver.d.ts +3 -0
- package/dist/spec-authority/resolver.js +55 -7
- package/dist/subprocess/max-buffer.d.ts +14 -0
- package/dist/subprocess/max-buffer.js +15 -0
- package/dist/swarm/complete-cohort.d.ts +2 -0
- package/dist/swarm/complete-cohort.js +21 -13
- package/dist/swarm/index.d.ts +1 -0
- package/dist/swarm/index.js +1 -0
- package/dist/swarm/launch-cli.js +53 -33
- package/dist/swarm/launch.d.ts +8 -0
- package/dist/swarm/launch.js +63 -43
- package/dist/swarm/pre-dispatch-cli.js +2 -1
- package/dist/swarm/pre-dispatch.js +10 -1
- package/dist/swarm/subagent-status-dir.d.ts +27 -0
- package/dist/swarm/subagent-status-dir.js +42 -0
- package/dist/swarm/worktrees.d.ts +17 -3
- package/dist/swarm/worktrees.js +48 -4
- package/dist/triage/evaluate/worktrees.js +153 -6
- package/dist/triage/scope/mutations-core.d.ts +2 -6
- package/dist/triage/scope/mutations-core.js +23 -55
- package/dist/triage/scope-drift/add-ignore.js +5 -39
- package/dist/triage/subscribe/index.d.ts +2 -6
- package/dist/triage/subscribe/index.js +15 -65
- package/dist/triage/welcome/writers.js +15 -74
- package/dist/umbrella-current-shape/index.d.ts +51 -3
- package/dist/umbrella-current-shape/index.js +106 -18
- package/dist/validate-content/deposit-required.d.ts +39 -0
- package/dist/validate-content/deposit-required.js +147 -0
- package/dist/validate-content/index.d.ts +1 -0
- package/dist/validate-content/index.js +1 -0
- package/dist/validate-content/validate-links.d.ts +2 -3
- package/dist/validate-content/validate-links.js +29 -2
- package/dist/vbrief-activate/activate.d.ts +7 -2
- package/dist/vbrief-activate/activate.js +26 -13
- package/dist/vbrief-build/index.d.ts +2 -1
- package/dist/vbrief-build/index.js +2 -1
- package/dist/vbrief-build/parity-scenarios.js +5 -5
- package/dist/vbrief-build/project-definition-io.d.ts +63 -3
- package/dist/vbrief-build/project-definition-io.js +357 -31
- package/dist/vbrief-build/project-definition-mutation.d.ts +32 -0
- package/dist/vbrief-build/project-definition-mutation.js +43 -0
- package/dist/vbrief-validate/conformance.d.ts +1 -0
- package/dist/vbrief-validate/conformance.js +85 -8
- package/dist/vbrief-validate/main.js +2 -0
- package/dist/vbrief-validate/precutover.js +5 -10
- package/dist/verify-ac/clauses.d.ts +62 -2
- package/dist/verify-ac/clauses.js +183 -111
- package/dist/verify-ac/evaluate.d.ts +9 -0
- package/dist/verify-ac/evaluate.js +32 -9
- package/dist/verify-ac/index.d.ts +1 -1
- package/dist/verify-ac/index.js +1 -1
- package/dist/verify-env/node-runtime.d.ts +8 -4
- package/dist/verify-env/node-runtime.js +9 -6
- package/dist/verify-env/toolchain-check.d.ts +35 -3
- package/dist/verify-env/toolchain-check.js +155 -36
- package/dist/verify-source/deposit-closure.d.ts +23 -0
- package/dist/verify-source/deposit-closure.js +162 -0
- package/dist/verify-source/index.d.ts +2 -0
- package/dist/verify-source/index.js +2 -0
- package/dist/verify-source/project-definition-mutation-boundary.d.ts +104 -0
- package/dist/verify-source/project-definition-mutation-boundary.js +326 -0
- package/dist/verify-source/semantic-single-source.d.ts +36 -0
- package/dist/verify-source/semantic-single-source.js +349 -0
- package/package.json +7 -3
package/dist/hooks/tools.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/** Host spellings that install-time matchers and runtime classification share. */
|
|
2
|
-
export declare const DIRECT_WRITE_TOOL_NAMES: readonly ["Edit", "Write", "WriteFile", "CreateFile", "MultiEdit", "NotebookEdit", "StrReplace", "SearchReplace", "Delete", "DeleteFile", "ApplyPatch", "apply_patch"];
|
|
2
|
+
export declare const DIRECT_WRITE_TOOL_NAMES: readonly ["Edit", "Write", "WriteFile", "CreateFile", "MultiEdit", "NotebookEdit", "StrReplace", "SearchReplace", "Delete", "DeleteFile", "ApplyPatch", "apply_patch", "write", "search_replace"];
|
|
3
3
|
/** PreToolUse spawn / sub-agent dispatch tools (#1185 / #2437). */
|
|
4
4
|
export declare const SPAWN_TOOL_NAMES: readonly ["Task", "SubagentStart", "spawn_subagent", "start_agent", "CreateAgent"];
|
|
5
5
|
/**
|
|
6
6
|
* Host spellings for Shell/Bash execution tools (#2711).
|
|
7
7
|
* Used for runtimeAuthority scopes.push / scopes.merge classification.
|
|
8
8
|
*/
|
|
9
|
-
export declare const SHELL_TOOL_NAMES: readonly ["Shell", "Bash", "BashTool", "shell", "bash"];
|
|
9
|
+
export declare const SHELL_TOOL_NAMES: readonly ["Shell", "Bash", "BashTool", "shell", "bash", "run_terminal_command"];
|
|
10
10
|
/** Env override forcing hook-level read-only write denial (#1185). */
|
|
11
11
|
export declare const READ_ONLY_HOOK_ENV = "DEFT_HOOK_READ_ONLY";
|
|
12
12
|
export declare function isDirectWriteTool(toolName: string): boolean;
|
|
@@ -32,6 +32,24 @@ export declare const MCP_PUSH_MERGE_BARE_NAMES: readonly ["merge_pull_request",
|
|
|
32
32
|
export declare const DIRECT_WRITE_HOOK_MATCHER: string;
|
|
33
33
|
export declare const SPAWN_HOOK_MATCHER: string;
|
|
34
34
|
export declare const SHELL_HOOK_MATCHER: string;
|
|
35
|
+
/** Known mutation tool names per host. Deposited matchers must cover each name (#3987). */
|
|
36
|
+
export declare const GROK_MUTATION_TOOL_CATALOG: {
|
|
37
|
+
readonly directWrite: readonly ["write", "search_replace"];
|
|
38
|
+
readonly shell: readonly ["run_terminal_command"];
|
|
39
|
+
readonly spawn: readonly ["spawn_subagent"];
|
|
40
|
+
};
|
|
41
|
+
/** Tools on this host that are intentionally not mutation-gated. */
|
|
42
|
+
export declare const GROK_NON_MUTATION_TOOLS: {
|
|
43
|
+
readonly read_file: "read";
|
|
44
|
+
readonly grep: "read";
|
|
45
|
+
readonly list_dir: "read";
|
|
46
|
+
readonly todo_write: "session-local non-product scratch";
|
|
47
|
+
readonly get_command_or_subagent_output: "poll, not a mutation; elapsed bound is evaluateInFlight";
|
|
48
|
+
readonly kill_command_or_subagent: "process control";
|
|
49
|
+
readonly search_tool: "read";
|
|
50
|
+
readonly web_search: "read";
|
|
51
|
+
};
|
|
52
|
+
export declare function matcherHasLiteralToken(matcher: string, toolName: string): boolean;
|
|
35
53
|
/**
|
|
36
54
|
* PreToolUse matcher for MCP-class push/merge tools (#2711).
|
|
37
55
|
* Regex-friendly for Claude/nested hosts. Prefer broad install match +
|
package/dist/hooks/tools.js
CHANGED
|
@@ -12,6 +12,8 @@ export const DIRECT_WRITE_TOOL_NAMES = [
|
|
|
12
12
|
"DeleteFile",
|
|
13
13
|
"ApplyPatch",
|
|
14
14
|
"apply_patch",
|
|
15
|
+
"write",
|
|
16
|
+
"search_replace",
|
|
15
17
|
];
|
|
16
18
|
/** PreToolUse spawn / sub-agent dispatch tools (#1185 / #2437). */
|
|
17
19
|
export const SPAWN_TOOL_NAMES = [
|
|
@@ -25,7 +27,14 @@ export const SPAWN_TOOL_NAMES = [
|
|
|
25
27
|
* Host spellings for Shell/Bash execution tools (#2711).
|
|
26
28
|
* Used for runtimeAuthority scopes.push / scopes.merge classification.
|
|
27
29
|
*/
|
|
28
|
-
export const SHELL_TOOL_NAMES = [
|
|
30
|
+
export const SHELL_TOOL_NAMES = [
|
|
31
|
+
"Shell",
|
|
32
|
+
"Bash",
|
|
33
|
+
"BashTool",
|
|
34
|
+
"shell",
|
|
35
|
+
"bash",
|
|
36
|
+
"run_terminal_command",
|
|
37
|
+
];
|
|
29
38
|
/** Env override forcing hook-level read-only write denial (#1185). */
|
|
30
39
|
export const READ_ONLY_HOOK_ENV = "DEFT_HOOK_READ_ONLY";
|
|
31
40
|
function normalizedToolName(toolName) {
|
|
@@ -87,6 +96,26 @@ export const MCP_PUSH_MERGE_BARE_NAMES = [
|
|
|
87
96
|
export const DIRECT_WRITE_HOOK_MATCHER = DIRECT_WRITE_TOOL_NAMES.join("|");
|
|
88
97
|
export const SPAWN_HOOK_MATCHER = SPAWN_TOOL_NAMES.join("|");
|
|
89
98
|
export const SHELL_HOOK_MATCHER = SHELL_TOOL_NAMES.join("|");
|
|
99
|
+
/** Known mutation tool names per host. Deposited matchers must cover each name (#3987). */
|
|
100
|
+
export const GROK_MUTATION_TOOL_CATALOG = {
|
|
101
|
+
directWrite: ["write", "search_replace"],
|
|
102
|
+
shell: ["run_terminal_command"],
|
|
103
|
+
spawn: ["spawn_subagent"],
|
|
104
|
+
};
|
|
105
|
+
/** Tools on this host that are intentionally not mutation-gated. */
|
|
106
|
+
export const GROK_NON_MUTATION_TOOLS = {
|
|
107
|
+
read_file: "read",
|
|
108
|
+
grep: "read",
|
|
109
|
+
list_dir: "read",
|
|
110
|
+
todo_write: "session-local non-product scratch",
|
|
111
|
+
get_command_or_subagent_output: "poll, not a mutation; elapsed bound is evaluateInFlight",
|
|
112
|
+
kill_command_or_subagent: "process control",
|
|
113
|
+
search_tool: "read",
|
|
114
|
+
web_search: "read",
|
|
115
|
+
};
|
|
116
|
+
export function matcherHasLiteralToken(matcher, toolName) {
|
|
117
|
+
return matcher.split("|").includes(toolName);
|
|
118
|
+
}
|
|
90
119
|
/**
|
|
91
120
|
* PreToolUse matcher for MCP-class push/merge tools (#2711).
|
|
92
121
|
* Regex-friendly for Claude/nested hosts. Prefer broad install match +
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { HookHost } from "../hooks/dispatcher.js";
|
|
2
2
|
import { type HostHooksPolicy } from "../policy/host-hooks.js";
|
|
3
3
|
import type { InitDepositIo } from "./constants.js";
|
|
4
|
+
import { type HookRuntimeTravelSeams } from "./hook-runtime-travel.js";
|
|
4
5
|
export { DIRECT_WRITE_HOOK_MATCHER, MCP_HOOK_MATCHER, SHELL_HOOK_MATCHER, SPAWN_HOOK_MATCHER, } from "../hooks/tools.js";
|
|
5
6
|
export declare const DEFT_HOOK_COMMAND_MARKER = "deft-hook";
|
|
6
7
|
export declare const LEGACY_DEFT_HOOK_COMMAND_MARKER = "deft hook:dispatch";
|
|
@@ -41,7 +42,7 @@ export interface AgentHookDepositResult {
|
|
|
41
42
|
readonly changedPaths: AgentHookPath[];
|
|
42
43
|
}
|
|
43
44
|
/** Merge Directive-owned project hook entries without replacing user configuration. */
|
|
44
|
-
export declare function writeAgentHookDeposit(projectRoot: string, io?: InitDepositIo, hostHooksPolicy?: HostHooksPolicy): AgentHookDepositResult;
|
|
45
|
+
export declare function writeAgentHookDeposit(projectRoot: string, io?: InitDepositIo, hostHooksPolicy?: HostHooksPolicy, travelSeams?: HookRuntimeTravelSeams): AgentHookDepositResult;
|
|
45
46
|
/** Read-only registration probe shared by verify and doctor. */
|
|
46
47
|
export declare function inspectAgentHookDeposit(projectRoot: string, hostHooksPolicy?: HostHooksPolicy): AgentHookInspection[];
|
|
47
48
|
//# sourceMappingURL=agent-hooks.d.ts.map
|
|
@@ -4,6 +4,7 @@ import { assertDepositContained } from "../deposit/contain.js";
|
|
|
4
4
|
import { containedRemove, containedRename, containedWrite } from "../fs/contained-write.js";
|
|
5
5
|
import { DIRECT_WRITE_HOOK_MATCHER, MCP_HOOK_MATCHER, SHELL_HOOK_MATCHER, SPAWN_HOOK_MATCHER, } from "../hooks/tools.js";
|
|
6
6
|
import { isHostHookDepositEnabled, loadHostHooksPolicyFromProject, } from "../policy/host-hooks.js";
|
|
7
|
+
import { inspectHookRuntimeTravel } from "./hook-runtime-travel.js";
|
|
7
8
|
export { DIRECT_WRITE_HOOK_MATCHER, MCP_HOOK_MATCHER, SHELL_HOOK_MATCHER, SPAWN_HOOK_MATCHER, } from "../hooks/tools.js";
|
|
8
9
|
export const DEFT_HOOK_COMMAND_MARKER = "deft-hook";
|
|
9
10
|
export const LEGACY_DEFT_HOOK_COMMAND_MARKER = "deft hook:dispatch";
|
|
@@ -252,7 +253,7 @@ function writeJsonIfChanged(projectRoot, path, payload, kind) {
|
|
|
252
253
|
return true;
|
|
253
254
|
}
|
|
254
255
|
/** Merge Directive-owned project hook entries without replacing user configuration. */
|
|
255
|
-
export function writeAgentHookDeposit(projectRoot, io = { printf: () => undefined }, hostHooksPolicy = loadHostHooksPolicyFromProject(projectRoot)) {
|
|
256
|
+
export function writeAgentHookDeposit(projectRoot, io = { printf: () => undefined }, hostHooksPolicy = loadHostHooksPolicyFromProject(projectRoot), travelSeams = {}) {
|
|
256
257
|
const changedPaths = [];
|
|
257
258
|
const strippedPaths = [];
|
|
258
259
|
const definitions = [
|
|
@@ -332,6 +333,12 @@ export function writeAgentHookDeposit(projectRoot, io = { printf: () => undefine
|
|
|
332
333
|
if (changedPaths.length === 0 && strippedPaths.length === 0 && adaptersRemoved === 0) {
|
|
333
334
|
io.printf("Directive agent hooks already current.\n");
|
|
334
335
|
}
|
|
336
|
+
// #3785: the registration is trackable and the deposit that implements it is
|
|
337
|
+
// born-ignored, so warn when a clone would inherit the fence without a way to
|
|
338
|
+
// obtain the runtime it names.
|
|
339
|
+
const travel = inspectHookRuntimeTravel(projectRoot, definitions.map((definition) => ({ host: definition.host, path: definition.path })), hostHooksPolicy, travelSeams);
|
|
340
|
+
if (travel.warning !== null)
|
|
341
|
+
io.printf(`${travel.warning}\n`);
|
|
335
342
|
return {
|
|
336
343
|
changed: changedPaths.length + strippedPaths.length + adaptersRemoved > 0,
|
|
337
344
|
changedPaths: [...changedPaths, ...strippedPaths],
|
|
@@ -25,6 +25,13 @@ export interface ReconstituteDepositResult {
|
|
|
25
25
|
readonly reconstituted: boolean;
|
|
26
26
|
}
|
|
27
27
|
export type GitLsFiles = (projectDir: string, paths: readonly string[]) => string | null;
|
|
28
|
+
/**
|
|
29
|
+
* Strip leading/trailing slashes so `.deft/cache/` and `.deft/cache` match.
|
|
30
|
+
* Globs are not expanded: a writer covered only by a glob fails closed (#3612).
|
|
31
|
+
*/
|
|
32
|
+
export declare function normalizeGitignorePath(p: string): string;
|
|
33
|
+
export declare function gitignoreRuleCoversPath(rule: string, relPath: string): boolean;
|
|
34
|
+
export declare function ignoreSetCoversPath(rules: readonly string[], relPath: string): boolean;
|
|
28
35
|
/**
|
|
29
36
|
* Reports whether `.deft/core` is tracked in git. Returns `null` when git is
|
|
30
37
|
* unavailable or the tree is not a repository (treated as greenfield).
|
|
@@ -31,6 +31,13 @@ export const CANONICAL_GITIGNORE_BASELINE = [
|
|
|
31
31
|
".deft/last-session.json",
|
|
32
32
|
".deft/occupancy.json",
|
|
33
33
|
".deft/routing.local.json",
|
|
34
|
+
// #3612: remaining runtime writer roots kept at historical names (on-disk
|
|
35
|
+
// contracts). Relocating them would break existing consumer state.
|
|
36
|
+
".deft/authz/",
|
|
37
|
+
".deft/delivery-attempts/",
|
|
38
|
+
".deft/metrics/",
|
|
39
|
+
".deft/escalations/",
|
|
40
|
+
".deft/approved-scope/",
|
|
34
41
|
// Agent-host working state (#3502). Selective, NEVER blanket `.claude/`:
|
|
35
42
|
// `.claude/settings.json`, `.claude/skills/` and `.claude/commands/` are
|
|
36
43
|
// MANAGED DEPOSITS (agent-hooks.ts / skill-discovery-hosts.ts) and must stay
|
|
@@ -105,6 +112,23 @@ function gitignoreCoversLine(present, line) {
|
|
|
105
112
|
}
|
|
106
113
|
return false;
|
|
107
114
|
}
|
|
115
|
+
/**
|
|
116
|
+
* Strip leading/trailing slashes so `.deft/cache/` and `.deft/cache` match.
|
|
117
|
+
* Globs are not expanded: a writer covered only by a glob fails closed (#3612).
|
|
118
|
+
*/
|
|
119
|
+
export function normalizeGitignorePath(p) {
|
|
120
|
+
return p.replaceAll("\\", "/").replace(/^\/+/, "").replace(/\/+$/, "");
|
|
121
|
+
}
|
|
122
|
+
export function gitignoreRuleCoversPath(rule, relPath) {
|
|
123
|
+
const r = normalizeGitignorePath(stripGitignoreInlineComment(rule));
|
|
124
|
+
const p = normalizeGitignorePath(relPath);
|
|
125
|
+
if (!r || r.startsWith("#"))
|
|
126
|
+
return false;
|
|
127
|
+
return r === p || p.startsWith(`${r}/`);
|
|
128
|
+
}
|
|
129
|
+
export function ignoreSetCoversPath(rules, relPath) {
|
|
130
|
+
return rules.some((rule) => gitignoreRuleCoversPath(rule, relPath));
|
|
131
|
+
}
|
|
108
132
|
function collectPresentGitignoreLines(existing) {
|
|
109
133
|
const present = new Set();
|
|
110
134
|
for (const raw of existing.split("\n")) {
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook-runtime travel warning (#3785).
|
|
3
|
+
*
|
|
4
|
+
* Directive keeps the agent-hook registration files trackable by design and
|
|
5
|
+
* born-ignores the deposit that implements them. When a registration travels
|
|
6
|
+
* with the tree and nothing in that tree lets a clone obtain `deft-hook`, the
|
|
7
|
+
* registration arrives and the runtime does not. A fresh clone, CI runner, or
|
|
8
|
+
* container without a global install then fail-closes every mutation on an
|
|
9
|
+
* opaque exit 127, and no Directive code runs to explain it.
|
|
10
|
+
*
|
|
11
|
+
* "Travels" means tracked OR trackable-and-not-ignored. A first `deft init`
|
|
12
|
+
* writes the registration unstaged, so a tracked-only probe would go quiet on
|
|
13
|
+
* exactly the run that precedes the commit carrying the fence into every clone.
|
|
14
|
+
*
|
|
15
|
+
* The committed `package.json` dependency on `@deftai/directive` (#2264) is the
|
|
16
|
+
* existing reconstitution anchor. With it a clone can install the runtime the
|
|
17
|
+
* registration names; without it the registration names a command no clone can
|
|
18
|
+
* host. The anchor is read from the commit, not from the working tree or the
|
|
19
|
+
* index: a consumer can commit the generated registration and leave the
|
|
20
|
+
* manifest merely present, staged, or edited, and that is the case that strands
|
|
21
|
+
* the clone.
|
|
22
|
+
*
|
|
23
|
+
* Warn-only. Refusing `deft init` / `deft update` on this condition would be
|
|
24
|
+
* the same lockout from the other side, so the deposit still writes and
|
|
25
|
+
* reports. Absence of the runtime stays a deny — fail-open-on-absence was
|
|
26
|
+
* refuted as unimplementable and as a bypass primitive (#3156).
|
|
27
|
+
*
|
|
28
|
+
* Refs #3785, #3736, #3571, #2752, #2264, #3156.
|
|
29
|
+
*/
|
|
30
|
+
import { type HookHost, type HostHooksPolicy } from "../policy/host-hooks.js";
|
|
31
|
+
import { type PinReadResult } from "../resolution/pin.js";
|
|
32
|
+
/**
|
|
33
|
+
* Hosts whose registration is deposited `failClosed: true`. Only these turn a
|
|
34
|
+
* missing runtime into a denial; the rest fail open and merely lose the gate.
|
|
35
|
+
*/
|
|
36
|
+
export declare const FAIL_CLOSED_HOOK_HOSTS: readonly HookHost[];
|
|
37
|
+
/** Manifest carrying the committed runtime anchor. */
|
|
38
|
+
export declare const RUNTIME_ANCHOR_MANIFEST = "package.json";
|
|
39
|
+
/**
|
|
40
|
+
* Spec prefixes that name a location instead of a release. Each resolves
|
|
41
|
+
* against something outside the tree -- a path, a symlink target, a workspace
|
|
42
|
+
* member -- so the clone that receives the registration cannot install from it.
|
|
43
|
+
*/
|
|
44
|
+
export declare const LOCATION_SPEC_PREFIXES: readonly ["file:", "link:", "portal:", "workspace:", "git+file:"];
|
|
45
|
+
export type GitLsFilesProbe = (projectDir: string, paths: readonly string[]) => string | null;
|
|
46
|
+
/** Contents of a path as committed at `HEAD`, or null when there is none. */
|
|
47
|
+
export type CommittedFileProbe = (projectDir: string, path: string) => string | null;
|
|
48
|
+
export interface HookRuntimeTravelSeams {
|
|
49
|
+
readonly gitLsFiles?: GitLsFilesProbe;
|
|
50
|
+
readonly readCommittedFile?: CommittedFileProbe;
|
|
51
|
+
readonly readPin?: (projectRoot: string) => PinReadResult;
|
|
52
|
+
}
|
|
53
|
+
/** One deposited registration considered by the probe. */
|
|
54
|
+
export interface HookRegistrationRef {
|
|
55
|
+
readonly host: HookHost;
|
|
56
|
+
readonly path: string;
|
|
57
|
+
}
|
|
58
|
+
export interface HookRuntimeTravelResult {
|
|
59
|
+
/** Enabled registrations that travel with this repository. */
|
|
60
|
+
readonly travelingRegistrations: readonly string[];
|
|
61
|
+
/** Subset of {@link travelingRegistrations} deposited fail-closed. */
|
|
62
|
+
readonly failClosedRegistrations: readonly string[];
|
|
63
|
+
/** A committed manifest declares `@deftai/directive`, so a clone can install it. */
|
|
64
|
+
readonly runtimeTravels: boolean;
|
|
65
|
+
/** Null when the pairing is safe or git could not answer. */
|
|
66
|
+
readonly warning: string | null;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Report whether any enabled agent-hook registration travels with this tree
|
|
70
|
+
* while no manifest travelling alongside it lets a clone install the runtime it
|
|
71
|
+
* names.
|
|
72
|
+
*
|
|
73
|
+
* Disabled hosts are excluded: their registration is stripped, so a leftover
|
|
74
|
+
* copy cannot deny anything.
|
|
75
|
+
*/
|
|
76
|
+
export declare function inspectHookRuntimeTravel(projectRoot: string, registrations: readonly HookRegistrationRef[], hostHooksPolicy: HostHooksPolicy, seams?: HookRuntimeTravelSeams): HookRuntimeTravelResult;
|
|
77
|
+
//# sourceMappingURL=hook-runtime-travel.d.ts.map
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hook-runtime travel warning (#3785).
|
|
3
|
+
*
|
|
4
|
+
* Directive keeps the agent-hook registration files trackable by design and
|
|
5
|
+
* born-ignores the deposit that implements them. When a registration travels
|
|
6
|
+
* with the tree and nothing in that tree lets a clone obtain `deft-hook`, the
|
|
7
|
+
* registration arrives and the runtime does not. A fresh clone, CI runner, or
|
|
8
|
+
* container without a global install then fail-closes every mutation on an
|
|
9
|
+
* opaque exit 127, and no Directive code runs to explain it.
|
|
10
|
+
*
|
|
11
|
+
* "Travels" means tracked OR trackable-and-not-ignored. A first `deft init`
|
|
12
|
+
* writes the registration unstaged, so a tracked-only probe would go quiet on
|
|
13
|
+
* exactly the run that precedes the commit carrying the fence into every clone.
|
|
14
|
+
*
|
|
15
|
+
* The committed `package.json` dependency on `@deftai/directive` (#2264) is the
|
|
16
|
+
* existing reconstitution anchor. With it a clone can install the runtime the
|
|
17
|
+
* registration names; without it the registration names a command no clone can
|
|
18
|
+
* host. The anchor is read from the commit, not from the working tree or the
|
|
19
|
+
* index: a consumer can commit the generated registration and leave the
|
|
20
|
+
* manifest merely present, staged, or edited, and that is the case that strands
|
|
21
|
+
* the clone.
|
|
22
|
+
*
|
|
23
|
+
* Warn-only. Refusing `deft init` / `deft update` on this condition would be
|
|
24
|
+
* the same lockout from the other side, so the deposit still writes and
|
|
25
|
+
* reports. Absence of the runtime stays a deny — fail-open-on-absence was
|
|
26
|
+
* refuted as unimplementable and as a bypass primitive (#3156).
|
|
27
|
+
*
|
|
28
|
+
* Refs #3785, #3736, #3571, #2752, #2264, #3156.
|
|
29
|
+
*/
|
|
30
|
+
import { execFileSync } from "node:child_process";
|
|
31
|
+
import { disableHostHooksInvocation, isHostHookDepositEnabled, } from "../policy/host-hooks.js";
|
|
32
|
+
import { PIN_DEPENDENCY_NAME, readPin } from "../resolution/pin.js";
|
|
33
|
+
/**
|
|
34
|
+
* Hosts whose registration is deposited `failClosed: true`. Only these turn a
|
|
35
|
+
* missing runtime into a denial; the rest fail open and merely lose the gate.
|
|
36
|
+
*/
|
|
37
|
+
export const FAIL_CLOSED_HOOK_HOSTS = ["cursor"];
|
|
38
|
+
/** Manifest carrying the committed runtime anchor. */
|
|
39
|
+
export const RUNTIME_ANCHOR_MANIFEST = "package.json";
|
|
40
|
+
/**
|
|
41
|
+
* Spec prefixes that name a location instead of a release. Each resolves
|
|
42
|
+
* against something outside the tree -- a path, a symlink target, a workspace
|
|
43
|
+
* member -- so the clone that receives the registration cannot install from it.
|
|
44
|
+
*/
|
|
45
|
+
export const LOCATION_SPEC_PREFIXES = [
|
|
46
|
+
"file:",
|
|
47
|
+
"link:",
|
|
48
|
+
"portal:",
|
|
49
|
+
"workspace:",
|
|
50
|
+
"git+file:",
|
|
51
|
+
];
|
|
52
|
+
/** Prefixes any clone can fetch: a registry alias or a remote repository. */
|
|
53
|
+
const REMOTE_SPEC_PREFIXES = [
|
|
54
|
+
"npm:",
|
|
55
|
+
"http:",
|
|
56
|
+
"https:",
|
|
57
|
+
"git:",
|
|
58
|
+
"git+http:",
|
|
59
|
+
"git+https:",
|
|
60
|
+
"git+ssh:",
|
|
61
|
+
"github:",
|
|
62
|
+
"gitlab:",
|
|
63
|
+
"bitbucket:",
|
|
64
|
+
];
|
|
65
|
+
/**
|
|
66
|
+
* Whether a clone could install `deft-hook` from this spec.
|
|
67
|
+
*
|
|
68
|
+
* Allowlist, not denylist. Location forms are open-ended -- `file:`,
|
|
69
|
+
* `git+file:`, `../directive`, `/opt/directive`, `C:\src\directive`, `~/dev` --
|
|
70
|
+
* so enumerating them leaves the next shape uncovered, and an uncovered shape
|
|
71
|
+
* silences the warning. Anything that is not recognisably a registry release or
|
|
72
|
+
* a fetchable remote is therefore treated as not travelling. A bare
|
|
73
|
+
* `owner/repo` shorthand is refused on the same rule: it warns where the
|
|
74
|
+
* runtime might in fact arrive, which is the harmless direction for a
|
|
75
|
+
* warn-only probe.
|
|
76
|
+
*/
|
|
77
|
+
function reconstitutesForAClone(rawSpec) {
|
|
78
|
+
const spec = rawSpec.trim();
|
|
79
|
+
if (spec.length === 0)
|
|
80
|
+
return false;
|
|
81
|
+
const lower = spec.toLowerCase();
|
|
82
|
+
if (LOCATION_SPEC_PREFIXES.some((prefix) => lower.startsWith(prefix)))
|
|
83
|
+
return false;
|
|
84
|
+
if (REMOTE_SPEC_PREFIXES.some((prefix) => lower.startsWith(prefix)))
|
|
85
|
+
return true;
|
|
86
|
+
// Bare paths: relative, home-anchored, POSIX-absolute, or a Windows drive.
|
|
87
|
+
if (/^[.~/\\]/.test(spec) || /^[a-z]:[\\/]/i.test(spec))
|
|
88
|
+
return false;
|
|
89
|
+
// Anything else with a separator is a path or a repository shorthand, not a
|
|
90
|
+
// version, a range, or a dist-tag.
|
|
91
|
+
return !(spec.includes("/") || spec.includes("\\"));
|
|
92
|
+
}
|
|
93
|
+
function defaultGitLsFiles(projectDir, paths) {
|
|
94
|
+
try {
|
|
95
|
+
// Ask git rather than looking for `.git`: a project deposited into a
|
|
96
|
+
// subdirectory of a repository has no `.git` of its own, yet its
|
|
97
|
+
// registration is trackable by the parent. `ls-files` answers from any
|
|
98
|
+
// depth, relative to this directory, and declines outside a repository.
|
|
99
|
+
//
|
|
100
|
+
// `--cached` reports what is in the index, `--others --exclude-standard`
|
|
101
|
+
// what a first `deft init` just wrote and the next `git add` would sweep
|
|
102
|
+
// in; ignored paths are omitted, since they cannot reach a clone.
|
|
103
|
+
return execFileSync("git", ["ls-files", "--cached", "--others", "--exclude-standard", "--", ...paths], {
|
|
104
|
+
cwd: projectDir,
|
|
105
|
+
encoding: "utf8",
|
|
106
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
107
|
+
windowsHide: true,
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
// git absent, or not a repository: whether these paths travel has no answer
|
|
112
|
+
// here, so the caller stays silent rather than guessing.
|
|
113
|
+
return null;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
/** Paths that travel, as reported by git, or null when git could not answer. */
|
|
117
|
+
function travelingPaths(output) {
|
|
118
|
+
if (output === null)
|
|
119
|
+
return null;
|
|
120
|
+
return new Set(output
|
|
121
|
+
.split("\n")
|
|
122
|
+
.map((line) => line.trim())
|
|
123
|
+
.filter((line) => line.length > 0));
|
|
124
|
+
}
|
|
125
|
+
function defaultReadCommittedFile(projectDir, path) {
|
|
126
|
+
try {
|
|
127
|
+
// `HEAD:./<path>` resolves relative to this directory, so a project nested
|
|
128
|
+
// in a repository reads its own manifest rather than the parent's.
|
|
129
|
+
return execFileSync("git", ["show", `HEAD:./${path}`], {
|
|
130
|
+
cwd: projectDir,
|
|
131
|
+
encoding: "utf8",
|
|
132
|
+
stdio: ["ignore", "pipe", "ignore"],
|
|
133
|
+
windowsHide: true,
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
catch {
|
|
137
|
+
// No commit, no such path in it, or no repository: nothing is committed
|
|
138
|
+
// here that a clone would receive.
|
|
139
|
+
return null;
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/** The `@deftai/directive` spec as committed, or null when none is. */
|
|
143
|
+
function committedRuntimeSpec(manifest) {
|
|
144
|
+
if (manifest === null)
|
|
145
|
+
return null;
|
|
146
|
+
let parsed;
|
|
147
|
+
try {
|
|
148
|
+
parsed = JSON.parse(manifest);
|
|
149
|
+
}
|
|
150
|
+
catch {
|
|
151
|
+
return null;
|
|
152
|
+
}
|
|
153
|
+
if (typeof parsed !== "object" || parsed === null)
|
|
154
|
+
return null;
|
|
155
|
+
const pkg = parsed;
|
|
156
|
+
for (const block of ["devDependencies", "dependencies"]) {
|
|
157
|
+
const deps = pkg[block];
|
|
158
|
+
if (typeof deps !== "object" || deps === null || Array.isArray(deps))
|
|
159
|
+
continue;
|
|
160
|
+
const spec = deps[PIN_DEPENDENCY_NAME];
|
|
161
|
+
if (typeof spec === "string" && spec.trim().length > 0)
|
|
162
|
+
return spec.trim();
|
|
163
|
+
}
|
|
164
|
+
return null;
|
|
165
|
+
}
|
|
166
|
+
function isFailClosed(host) {
|
|
167
|
+
return FAIL_CLOSED_HOOK_HOSTS.includes(host);
|
|
168
|
+
}
|
|
169
|
+
function recoveryHosts(failClosed) {
|
|
170
|
+
const hosts = [...new Set(failClosed.map((entry) => entry.host))];
|
|
171
|
+
return hosts
|
|
172
|
+
.map((host) => `\`${disableHostHooksInvocation(` --host ${host} --confirm`)}\``)
|
|
173
|
+
.join(" / ");
|
|
174
|
+
}
|
|
175
|
+
function causeLine(anchor) {
|
|
176
|
+
if (anchor.kind === "uncommitted") {
|
|
177
|
+
return ` ${RUNTIME_ANCHOR_MANIFEST} declares ${PIN_DEPENDENCY_NAME} but is not committed, so a fresh`;
|
|
178
|
+
}
|
|
179
|
+
if (anchor.kind === "non-portable") {
|
|
180
|
+
return ` ${RUNTIME_ANCHOR_MANIFEST} pins ${PIN_DEPENDENCY_NAME} to \`${anchor.spec}\`, which no clone can install, so a fresh`;
|
|
181
|
+
}
|
|
182
|
+
return ` No ${RUNTIME_ANCHOR_MANIFEST} dependency on ${PIN_DEPENDENCY_NAME} travels with this tree, so a fresh`;
|
|
183
|
+
}
|
|
184
|
+
function fixLine(anchor) {
|
|
185
|
+
if (anchor.kind === "uncommitted") {
|
|
186
|
+
return ` Fix: commit ${RUNTIME_ANCHOR_MANIFEST} so the runtime travels with the registration.`;
|
|
187
|
+
}
|
|
188
|
+
if (anchor.kind === "non-portable") {
|
|
189
|
+
return ` Fix: pin ${PIN_DEPENDENCY_NAME} to a published version so a clone can install it.`;
|
|
190
|
+
}
|
|
191
|
+
return ` Fix: commit a ${RUNTIME_ANCHOR_MANIFEST} dependency on ${PIN_DEPENDENCY_NAME} so the runtime travels too.`;
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* Judge the anchor from the commit, not the working tree or the index.
|
|
195
|
+
*
|
|
196
|
+
* A clone receives commits. A manifest that is merely present, merely staged,
|
|
197
|
+
* or edited-but-uncommitted declares the runtime to this machine only, and the
|
|
198
|
+
* consumer is free to commit the generated registration without it.
|
|
199
|
+
*/
|
|
200
|
+
function resolveAnchorState(committedSpec, workingSpec) {
|
|
201
|
+
if (committedSpec === null) {
|
|
202
|
+
return workingSpec === null ? { kind: "absent" } : { kind: "uncommitted" };
|
|
203
|
+
}
|
|
204
|
+
if (!reconstitutesForAClone(committedSpec)) {
|
|
205
|
+
return { kind: "non-portable", spec: committedSpec };
|
|
206
|
+
}
|
|
207
|
+
return { kind: "travels" };
|
|
208
|
+
}
|
|
209
|
+
function buildWarning(traveling, failClosed, anchor) {
|
|
210
|
+
const listed = traveling
|
|
211
|
+
.map((entry) => `${entry.path}${isFailClosed(entry.host) ? " (fail-closed)" : ""}`)
|
|
212
|
+
.join(", ");
|
|
213
|
+
const cause = causeLine(anchor);
|
|
214
|
+
const lines = [
|
|
215
|
+
`\u26a0 Hook registration travels without its runtime (#3785): ${listed}`,
|
|
216
|
+
cause,
|
|
217
|
+
" clone, CI runner, or container cannot obtain the `deft-hook` command these files name.",
|
|
218
|
+
];
|
|
219
|
+
if (failClosed.length > 0) {
|
|
220
|
+
lines.push(" Fail-closed hosts then deny every mutation on an opaque exit 127, and no Directive", " code runs to say why. There is no in-session recovery on a host without Node.");
|
|
221
|
+
}
|
|
222
|
+
lines.push(fixLine(anchor));
|
|
223
|
+
if (failClosed.length > 0) {
|
|
224
|
+
lines.push(` Or, accepting the capability cost: ${recoveryHosts(failClosed)}.`);
|
|
225
|
+
}
|
|
226
|
+
lines.push(" \u2297 Do not hand-edit `failClosed` in the deposited file -- the next `deft update` restores it.", " Detail: .deft/core/docs/hook-runtime-unavailable.md");
|
|
227
|
+
return lines.join("\n");
|
|
228
|
+
}
|
|
229
|
+
/**
|
|
230
|
+
* Report whether any enabled agent-hook registration travels with this tree
|
|
231
|
+
* while no manifest travelling alongside it lets a clone install the runtime it
|
|
232
|
+
* names.
|
|
233
|
+
*
|
|
234
|
+
* Disabled hosts are excluded: their registration is stripped, so a leftover
|
|
235
|
+
* copy cannot deny anything.
|
|
236
|
+
*/
|
|
237
|
+
export function inspectHookRuntimeTravel(projectRoot, registrations, hostHooksPolicy, seams = {}) {
|
|
238
|
+
const enabled = registrations.filter((entry) => isHostHookDepositEnabled(entry.host, hostHooksPolicy));
|
|
239
|
+
const probePaths = [...enabled.map((entry) => entry.path), RUNTIME_ANCHOR_MANIFEST];
|
|
240
|
+
const gitLsFiles = seams.gitLsFiles ?? defaultGitLsFiles;
|
|
241
|
+
const traveling = travelingPaths(gitLsFiles(projectRoot, probePaths));
|
|
242
|
+
if (traveling === null) {
|
|
243
|
+
return {
|
|
244
|
+
travelingRegistrations: [],
|
|
245
|
+
failClosedRegistrations: [],
|
|
246
|
+
runtimeTravels: false,
|
|
247
|
+
warning: null,
|
|
248
|
+
};
|
|
249
|
+
}
|
|
250
|
+
// The two sides are judged asymmetrically, deliberately. A registration is
|
|
251
|
+
// dangerous as soon as it is trackable: one `git add` sends it to every
|
|
252
|
+
// clone. An anchor counts only once a commit carries it, because a clone
|
|
253
|
+
// receives commits -- present, staged, and edited-but-uncommitted manifests
|
|
254
|
+
// all declare the runtime to this machine alone.
|
|
255
|
+
const readCommittedFile = seams.readCommittedFile ?? defaultReadCommittedFile;
|
|
256
|
+
const committedSpec = committedRuntimeSpec(readCommittedFile(projectRoot, RUNTIME_ANCHOR_MANIFEST));
|
|
257
|
+
const anchor = resolveAnchorState(committedSpec, (seams.readPin ?? readPin)(projectRoot).rawSpec);
|
|
258
|
+
const runtimeTravels = anchor.kind === "travels";
|
|
259
|
+
const travelingRegistrations = enabled.filter((entry) => traveling.has(entry.path));
|
|
260
|
+
const failClosedRegistrations = travelingRegistrations.filter((entry) => isFailClosed(entry.host));
|
|
261
|
+
const unsafe = travelingRegistrations.length > 0 && !runtimeTravels;
|
|
262
|
+
return {
|
|
263
|
+
travelingRegistrations: travelingRegistrations.map((entry) => entry.path),
|
|
264
|
+
failClosedRegistrations: failClosedRegistrations.map((entry) => entry.path),
|
|
265
|
+
runtimeTravels,
|
|
266
|
+
warning: unsafe ? buildWarning(travelingRegistrations, failClosedRegistrations, anchor) : null,
|
|
267
|
+
};
|
|
268
|
+
}
|
|
269
|
+
//# sourceMappingURL=hook-runtime-travel.js.map
|
|
@@ -10,6 +10,7 @@ import { homedir, platform } from "node:os";
|
|
|
10
10
|
import { basename, join, resolve } from "node:path";
|
|
11
11
|
import { assertDepositContained } from "../deposit/contain.js";
|
|
12
12
|
import { copyTree } from "../deposit/copy-tree.js";
|
|
13
|
+
import { assertLiveProcedureDepositClean } from "../deposit/live-procedure-targets.js";
|
|
13
14
|
import { prunePythonArtifactsFromDeposit } from "../deposit/python-free.js";
|
|
14
15
|
import { resolveInstalledContentRoot } from "../deposit/resolve-content.js";
|
|
15
16
|
import { readCorePackageVersion } from "../engine-version.js";
|
|
@@ -173,8 +174,10 @@ export async function runInitDeposit(args, io, seams = {}) {
|
|
|
173
174
|
const resolveContent = seams.resolveContentRoot ?? resolveInstalledContentRoot;
|
|
174
175
|
const copyContent = seams.copyContent ?? copyTree;
|
|
175
176
|
const contentRoot = await resolveContent();
|
|
177
|
+
assertLiveProcedureDepositClean(contentRoot);
|
|
176
178
|
await reconstituteDepositFromContent(contentRoot, deftDir, copyContent);
|
|
177
179
|
await prunePythonArtifactsFromDeposit(deftDir, projectDir, io);
|
|
180
|
+
assertLiveProcedureDepositClean(deftDir);
|
|
178
181
|
ensureInitGitignoreLines(projectDir, io);
|
|
179
182
|
ensurePrettierIgnoreLines(projectDir, io);
|
|
180
183
|
const nowIso = seams.nowIso ?? (() => new Date().toISOString().replace(/\.\d{3}Z$/, "Z"));
|
|
@@ -13,6 +13,7 @@ import { platform as osPlatform } from "node:os";
|
|
|
13
13
|
import { join, resolve } from "node:path";
|
|
14
14
|
import { assertDepositContained } from "../deposit/contain.js";
|
|
15
15
|
import { replaceTree } from "../deposit/copy-tree.js";
|
|
16
|
+
import { assertLiveProcedureDepositClean } from "../deposit/live-procedure-targets.js";
|
|
16
17
|
import { prunePythonArtifactsFromDeposit } from "../deposit/python-free.js";
|
|
17
18
|
import { resolveInstalledContentRoot } from "../deposit/resolve-content.js";
|
|
18
19
|
import { manifestTagToVersion, parseInstallManifest } from "../doctor/manifest.js";
|
|
@@ -476,9 +477,12 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
476
477
|
const strategy = alreadyCurrent ? "no-op" : "file-swap";
|
|
477
478
|
if (alreadyCurrent) {
|
|
478
479
|
io.printf("[deft update] Framework payload already current; skipping payload copy.\n");
|
|
480
|
+
// C3 even when VERSION matches: skip-copy is not skip-validation (#3602).
|
|
481
|
+
assertLiveProcedureDepositClean(contentRoot);
|
|
479
482
|
// #2913: still fail-closed reconcile so dst-only leftovers cannot linger when
|
|
480
483
|
// VERSION already matches (e.g. pre-#2804 additive deposits). Does not re-stamp.
|
|
481
484
|
await reconcileDepositToContentPackage(deftDir, contentRoot, io);
|
|
485
|
+
assertLiveProcedureDepositClean(deftDir);
|
|
482
486
|
migrateLegacyInstallManifest(projectDir, join(deftDir, "VERSION"));
|
|
483
487
|
// #3117: ensure a readable live generation token exists without advancing
|
|
484
488
|
// when the payload did not swap (already-current). stampLiveGeneration is a
|
|
@@ -504,8 +508,11 @@ export async function runRefreshDeposit(args, io, seams = {}) {
|
|
|
504
508
|
}
|
|
505
509
|
else {
|
|
506
510
|
// Full-tree replace (or injected seam). Additive copy is no longer the default.
|
|
511
|
+
// C3 the incoming package BEFORE replace so a reject cannot leave a broken deposit.
|
|
512
|
+
assertLiveProcedureDepositClean(contentRoot);
|
|
507
513
|
await copyContent(contentRoot, deftDir);
|
|
508
514
|
await prunePythonArtifactsFromDeposit(deftDir, projectDir, io);
|
|
515
|
+
assertLiveProcedureDepositClean(deftDir);
|
|
509
516
|
// #2913 / #2804 / #2347: fail-closed delete-not-in-source BEFORE VERSION stamp.
|
|
510
517
|
// replaceTree already drops dst-only paths; reconcile verifies and covers
|
|
511
518
|
// additive seams. Throws => no VERSION rewrite (refuse stamp until clean).
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime writer paths that land in a consumer working tree (#3612).
|
|
3
|
+
*
|
|
4
|
+
* The ignore baselines and this list are coupled: every exported writer path
|
|
5
|
+
* constant MUST appear here, and a fail-closed test asserts the ignore set
|
|
6
|
+
* covers each entry on both shipped surfaces. New writers join this registry;
|
|
7
|
+
* they do not get a silent extra .deft/ name.
|
|
8
|
+
*/
|
|
9
|
+
/**
|
|
10
|
+
* Imported writer path constants. Order is the lockstep contract with
|
|
11
|
+
* runtime-writer-paths.json (Go reads that file).
|
|
12
|
+
*/
|
|
13
|
+
export declare const RUNTIME_WRITER_PATHS: readonly string[];
|
|
14
|
+
//# sourceMappingURL=runtime-writers.d.ts.map
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Runtime writer paths that land in a consumer working tree (#3612).
|
|
3
|
+
*
|
|
4
|
+
* The ignore baselines and this list are coupled: every exported writer path
|
|
5
|
+
* constant MUST appear here, and a fail-closed test asserts the ignore set
|
|
6
|
+
* covers each entry on both shipped surfaces. New writers join this registry;
|
|
7
|
+
* they do not get a silent extra .deft/ name.
|
|
8
|
+
*/
|
|
9
|
+
import { AUTHZ_DIR } from "../authz/types.js";
|
|
10
|
+
import { DEFAULT_TASK_CACHE_ROOT } from "../cache/task-cache/constants.js";
|
|
11
|
+
import { SESSION_COMPLETED_MARKER_REL } from "../check/session-completed-ac.js";
|
|
12
|
+
import { DELIVERY_ATTEMPT_DIR } from "../delivery-attempt/types.js";
|
|
13
|
+
import { ESCALATION_DIR } from "../escalation/types.js";
|
|
14
|
+
import { PROJECT_LOCAL_METRICS_DIR } from "../metrics/resolve-metrics-home.js";
|
|
15
|
+
import { APPROVED_SCOPE_DIR } from "../scope-provenance/digest.js";
|
|
16
|
+
import { AC_PASS_BANK_DIR } from "../session/ac-pass-banking.js";
|
|
17
|
+
import { VERIFY_AC_SESSION_CACHE_DIR } from "../session/verify-ac-session-cache.js";
|
|
18
|
+
/**
|
|
19
|
+
* Imported writer path constants. Order is the lockstep contract with
|
|
20
|
+
* runtime-writer-paths.json (Go reads that file).
|
|
21
|
+
*/
|
|
22
|
+
export const RUNTIME_WRITER_PATHS = Object.freeze([
|
|
23
|
+
AC_PASS_BANK_DIR,
|
|
24
|
+
VERIFY_AC_SESSION_CACHE_DIR,
|
|
25
|
+
SESSION_COMPLETED_MARKER_REL.join("/"),
|
|
26
|
+
AUTHZ_DIR,
|
|
27
|
+
DELIVERY_ATTEMPT_DIR,
|
|
28
|
+
PROJECT_LOCAL_METRICS_DIR,
|
|
29
|
+
ESCALATION_DIR,
|
|
30
|
+
APPROVED_SCOPE_DIR,
|
|
31
|
+
DEFAULT_TASK_CACHE_ROOT,
|
|
32
|
+
]);
|
|
33
|
+
//# sourceMappingURL=runtime-writers.js.map
|