@deftai/directive-core 0.108.0 → 0.109.1
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/cache/operations.js +1 -1
- package/dist/check/gate-lists.d.ts +14 -0
- package/dist/check/gate-lists.js +26 -3
- package/dist/check/session-completed-ac.d.ts +1 -1
- package/dist/check/session-completed-ac.js +1 -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/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 +7 -0
- package/dist/design-critique/citation-grammar.js +1 -1
- package/dist/design-critique/completed-arc-record.d.ts +8 -1
- package/dist/design-critique/completed-arc-record.js +98 -14
- package/dist/hooks/classify/host-session-identity.d.ts +25 -12
- package/dist/hooks/classify/host-session-identity.js +64 -49
- package/dist/hooks/classify/index.d.ts +2 -2
- package/dist/hooks/classify/index.js +2 -2
- package/dist/hooks/classify/paths.d.ts +2 -0
- package/dist/hooks/classify/paths.js +8 -4
- package/dist/hooks/classify/stdin.js +69 -3
- package/dist/hooks/dest-form.d.ts +20 -1
- package/dist/hooks/dest-form.js +158 -21
- package/dist/hooks/dispatcher.d.ts +22 -2
- package/dist/hooks/dispatcher.js +338 -66
- package/dist/hooks/fixtures/cases.d.ts +6 -0
- package/dist/hooks/fixtures/cases.js +53 -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 +3 -0
- package/dist/hooks/index.js +3 -0
- package/dist/hooks/owner-liveness.d.ts +92 -0
- package/dist/hooks/owner-liveness.js +103 -0
- 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 +49 -2
- package/dist/hooks/tools.js +96 -1
- package/dist/init-deposit/agent-hooks.d.ts +10 -0
- package/dist/init-deposit/agent-hooks.js +39 -0
- package/dist/init-deposit/gitignore.d.ts +7 -0
- package/dist/init-deposit/gitignore.js +24 -0
- package/dist/init-deposit/host-tool-coverage.d.ts +53 -0
- package/dist/init-deposit/host-tool-coverage.js +150 -0
- package/dist/init-deposit/index.d.ts +1 -0
- package/dist/init-deposit/index.js +1 -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/issue-ingest.d.ts +6 -1
- package/dist/intake/issue-ingest.js +21 -2
- 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/orchestration/subagent-monitor.d.ts +6 -0
- package/dist/orchestration/subagent-monitor.js +23 -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 +27 -0
- package/dist/orphan-active/evaluate.js +63 -6
- package/dist/orphan-active/index.d.ts +1 -0
- package/dist/orphan-active/index.js +1 -0
- package/dist/policy/merge-approval-head.js +7 -6
- 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.js +36 -9
- package/dist/pr-wait-mergeable/wrappers.js +3 -3
- package/dist/product-first-done-gate/evaluate.js +6 -2
- package/dist/product-first-done-gate/types.js +2 -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 +2 -0
- package/dist/release/index.js +2 -0
- package/dist/release/pipeline.js +55 -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 +6 -0
- package/dist/review-monitor/github-lease.js +4 -3
- 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 +41 -11
- package/dist/scm/gh-rest.d.ts +25 -12
- package/dist/scm/gh-rest.js +65 -15
- package/dist/scm/index.d.ts +2 -0
- package/dist/scm/index.js +2 -0
- 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/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/child-occupancy.d.ts +72 -0
- package/dist/session/child-occupancy.js +209 -0
- package/dist/session/host-session-owner.d.ts +93 -0
- package/dist/session/host-session-owner.js +148 -0
- package/dist/session/index.d.ts +2 -0
- package/dist/session/index.js +2 -0
- package/dist/session/occupancy.d.ts +107 -2
- package/dist/session/occupancy.js +272 -34
- package/dist/session/verify-ac-session-cache.d.ts +2 -2
- package/dist/session/verify-ac-session-cache.js +2 -2
- package/dist/swarm/complete-cohort.js +2 -0
- package/dist/swarm/pre-dispatch.js +2 -0
- package/dist/swarm/subagent-status-dir.d.ts +2 -1
- package/dist/swarm/subagent-status-dir.js +11 -2
- package/dist/swarm/worktrees.js +2 -0
- package/dist/triage/evaluate/worktrees.js +153 -6
- 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/verify-ac/evaluate.d.ts +9 -0
- package/dist/verify-ac/evaluate.js +32 -9
- package/dist/verify-env/agent-hooks.d.ts +6 -1
- package/dist/verify-env/agent-hooks.js +28 -2
- 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/semantic-single-source.d.ts +36 -0
- package/dist/verify-source/semantic-single-source.js +349 -0
- package/package.json +3 -3
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Stage the @deftai/directive-content pack the same way prepack does, then
|
|
3
|
+
* rewrite relative markdown links onto the flattened deposit layout (#3937).
|
|
4
|
+
*
|
|
5
|
+
* Must not write into the source tree: destDir is the pack root (packages/content
|
|
6
|
+
* during prepack, or an out-of-tree temp dir in tests).
|
|
7
|
+
*/
|
|
8
|
+
import { cpSync, existsSync, mkdirSync, readdirSync, readFileSync, rmSync, } from "node:fs";
|
|
9
|
+
import { join, relative } from "node:path";
|
|
10
|
+
import { containedWrite } from "../fs/contained-write.js";
|
|
11
|
+
import { rewriteMarkdownDepositLinks, sourceRelForPackRel } from "./rewrite-deposit-links.js";
|
|
12
|
+
const ENGINE_ENTRIES = [".githooks", "Taskfile.yml", "tasks"];
|
|
13
|
+
const HARNESS_ENTRIES = ["main.md", "SKILL.md"];
|
|
14
|
+
function keepCopy(src) {
|
|
15
|
+
return !src.includes("__pycache__") && !src.endsWith(".pyc") && !src.endsWith(".py");
|
|
16
|
+
}
|
|
17
|
+
function rewriteStagedMarkdown(destDir) {
|
|
18
|
+
let filesRewritten = 0;
|
|
19
|
+
const walk = (dir) => {
|
|
20
|
+
let entries;
|
|
21
|
+
try {
|
|
22
|
+
entries = readdirSync(dir, { withFileTypes: true });
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
return;
|
|
26
|
+
}
|
|
27
|
+
for (const entry of entries) {
|
|
28
|
+
const full = join(dir, entry.name);
|
|
29
|
+
if (entry.isDirectory()) {
|
|
30
|
+
walk(full);
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (!entry.isFile() || !entry.name.endsWith(".md"))
|
|
34
|
+
continue;
|
|
35
|
+
const packRel = relative(destDir, full).replace(/\\/g, "/");
|
|
36
|
+
const original = readFileSync(full, "utf8");
|
|
37
|
+
const { content, rewriteCount } = rewriteMarkdownDepositLinks({
|
|
38
|
+
content: original,
|
|
39
|
+
sourceFileRel: sourceRelForPackRel(packRel),
|
|
40
|
+
packFileRel: packRel,
|
|
41
|
+
});
|
|
42
|
+
if (rewriteCount > 0 && content !== original) {
|
|
43
|
+
containedWrite({
|
|
44
|
+
root: destDir,
|
|
45
|
+
target: packRel,
|
|
46
|
+
data: content,
|
|
47
|
+
mode: "replace",
|
|
48
|
+
});
|
|
49
|
+
filesRewritten += 1;
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
walk(destDir);
|
|
54
|
+
return filesRewritten;
|
|
55
|
+
}
|
|
56
|
+
/** Copy content/ + harness + engine entries, then rewrite markdown links. */
|
|
57
|
+
export function stageContentPack(options) {
|
|
58
|
+
const { repoRoot, destDir } = options;
|
|
59
|
+
mkdirSync(destDir, { recursive: true });
|
|
60
|
+
const contentSrc = join(repoRoot, "content");
|
|
61
|
+
for (const name of readdirSync(contentSrc)) {
|
|
62
|
+
const from = join(contentSrc, name);
|
|
63
|
+
const to = join(destDir, name);
|
|
64
|
+
if (existsSync(to))
|
|
65
|
+
rmSync(to, { recursive: true, force: true });
|
|
66
|
+
cpSync(from, to, { recursive: true, filter: (src) => keepCopy(src) });
|
|
67
|
+
}
|
|
68
|
+
for (const name of ENGINE_ENTRIES) {
|
|
69
|
+
const from = join(repoRoot, name);
|
|
70
|
+
if (!existsSync(from))
|
|
71
|
+
continue;
|
|
72
|
+
const to = join(destDir, name);
|
|
73
|
+
if (existsSync(to))
|
|
74
|
+
rmSync(to, { recursive: true, force: true });
|
|
75
|
+
cpSync(from, to, { recursive: true, filter: (src) => keepCopy(src) });
|
|
76
|
+
}
|
|
77
|
+
for (const name of HARNESS_ENTRIES) {
|
|
78
|
+
const from = join(repoRoot, name);
|
|
79
|
+
if (!existsSync(from))
|
|
80
|
+
continue;
|
|
81
|
+
cpSync(from, join(destDir, name));
|
|
82
|
+
}
|
|
83
|
+
const filesRewritten = rewriteStagedMarkdown(destDir);
|
|
84
|
+
return { filesRewritten };
|
|
85
|
+
}
|
|
86
|
+
/** prepack cwd is packages/content. */
|
|
87
|
+
export function runPrepackFromContentPackage(pkgDir) {
|
|
88
|
+
const repoRoot = join(pkgDir, "..", "..");
|
|
89
|
+
stageContentPack({ repoRoot, destDir: pkgDir });
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=stage-content-pack.js.map
|
|
@@ -44,6 +44,13 @@ export type CitationScan = {
|
|
|
44
44
|
};
|
|
45
45
|
/** The published closed set, in the order the contract lists it. */
|
|
46
46
|
export declare const ACCEPTED_CITATION_FORMS: readonly string[];
|
|
47
|
+
/**
|
|
48
|
+
* Refused positions are exactly the five published classes. An indented code
|
|
49
|
+
* block and an HTML comment are deliberately not classified: a four-space
|
|
50
|
+
* indent is also ordinary list-continuation content, so refusing it would block
|
|
51
|
+
* valid records more often than it would catch example text.
|
|
52
|
+
*/
|
|
53
|
+
export declare function classifyPosition(body: string, offset: number): CitationRejectionClass | null;
|
|
47
54
|
/**
|
|
48
55
|
* Parse the closed citation grammar out of one comment body. Refused positions
|
|
49
56
|
* are reported rather than dropped so a block detail can echo the observation
|
|
@@ -239,7 +239,7 @@ function blockPosition(body, offset) {
|
|
|
239
239
|
* indent is also ordinary list-continuation content, so refusing it would block
|
|
240
240
|
* valid records more often than it would catch example text.
|
|
241
241
|
*/
|
|
242
|
-
function classifyPosition(body, offset) {
|
|
242
|
+
export function classifyPosition(body, offset) {
|
|
243
243
|
const block = blockPosition(body, offset);
|
|
244
244
|
if (block.insideFence)
|
|
245
245
|
return "code-fence";
|
|
@@ -15,7 +15,14 @@ export type ThreadComment = {
|
|
|
15
15
|
readonly id: number;
|
|
16
16
|
readonly body: string;
|
|
17
17
|
};
|
|
18
|
-
|
|
18
|
+
/**
|
|
19
|
+
* Closed reason set, published in `content/contracts/design-critique.md`
|
|
20
|
+
* `### One parser, set membership, observed diagnostics`. The union is derived
|
|
21
|
+
* from this array so a member added here without a contract row fails the
|
|
22
|
+
* content-contract suite (#3942).
|
|
23
|
+
*/
|
|
24
|
+
export declare const COMPLETED_ARC_BLOCK_REASONS: readonly ["missing-record", "lone-shape", "cite-not-lean", "missing-table-cite", "unshaped-table-cite", "ambiguous-table-cite"];
|
|
25
|
+
export type CompletedArcBlockReason = (typeof COMPLETED_ARC_BLOCK_REASONS)[number];
|
|
19
26
|
export type CompletedArcVerdict = {
|
|
20
27
|
readonly status: "not-in-arc";
|
|
21
28
|
} | {
|
|
@@ -13,6 +13,20 @@
|
|
|
13
13
|
*/
|
|
14
14
|
import { ACCEPTED_CITATION_FORMS, scanCitations, } from "./citation-grammar.js";
|
|
15
15
|
import { DESIGN_CRITIQUE_CATALOG_CHIPS } from "./exclusive-chip.js";
|
|
16
|
+
/**
|
|
17
|
+
* Closed reason set, published in `content/contracts/design-critique.md`
|
|
18
|
+
* `### One parser, set membership, observed diagnostics`. The union is derived
|
|
19
|
+
* from this array so a member added here without a contract row fails the
|
|
20
|
+
* content-contract suite (#3942).
|
|
21
|
+
*/
|
|
22
|
+
export const COMPLETED_ARC_BLOCK_REASONS = [
|
|
23
|
+
"missing-record",
|
|
24
|
+
"lone-shape",
|
|
25
|
+
"cite-not-lean",
|
|
26
|
+
"missing-table-cite",
|
|
27
|
+
"unshaped-table-cite",
|
|
28
|
+
"ambiguous-table-cite",
|
|
29
|
+
];
|
|
16
30
|
const SYNTHESIS_SHAPE_RE = /(?:^|\n)\s*design-critique:\s*synthesis accepted,\s*because\b/i;
|
|
17
31
|
const LEAN_HEADING_RE = /(?:^|\n)\s*\*{0,2}Lean:\*{0,2}/;
|
|
18
32
|
const TABLE_HEADING_RE = /(?:^|\n)\s*##\s+Verified-claims table\b/;
|
|
@@ -126,6 +140,86 @@ function resolveCitedLean(citations, byCommentId, latestLean) {
|
|
|
126
140
|
.map((row) => byCommentId.get(row.id))
|
|
127
141
|
.find((row) => row !== undefined && isSuccessorLeanBody(row.body));
|
|
128
142
|
}
|
|
143
|
+
/**
|
|
144
|
+
* Table resolution precedence (#3932).
|
|
145
|
+
*
|
|
146
|
+
* A typed `verified-claims table <id>` citation is the synthesis naming its own
|
|
147
|
+
* table, so it decides resolution: a generic citation neither satisfies it nor
|
|
148
|
+
* masks it. Scanning every citation for the first table-shaped body -- what
|
|
149
|
+
* this did before -- let an unrelated table-shaped comment clear a record whose
|
|
150
|
+
* claimed table is not a table, because the `missing-table-cite` refusal ran
|
|
151
|
+
* only after that untyped search failed.
|
|
152
|
+
*
|
|
153
|
+
* Cardinality rule: every table-kind claim must resolve to the same
|
|
154
|
+
* verified-claims table artifact. One valid typed claim must not launder an
|
|
155
|
+
* invalid one -- filtering to typed citations and then taking the first
|
|
156
|
+
* table-shaped body finds the real table in a valid-plus-ghost pair and never
|
|
157
|
+
* examines the ghost. `scanCitations` deduplicates on `(id, kind)`, so naming
|
|
158
|
+
* one table id twice is a single claim, while two distinct typed ids are two
|
|
159
|
+
* claims and cannot both be this record's table.
|
|
160
|
+
*
|
|
161
|
+
* With no typed claim the generic scan stands unchanged: permalink and bare
|
|
162
|
+
* `comment` citations scan as kind `comment`, and that is the published form
|
|
163
|
+
* these records use. Narrowing the citation contract so a table must be named
|
|
164
|
+
* by keyword is a separate decision needing its own migration criteria.
|
|
165
|
+
*
|
|
166
|
+
* Refusal partition (#3942). A typed claim fails in two states, and only one of
|
|
167
|
+
* them is fixed by adding the heading: the id is not a comment on this thread,
|
|
168
|
+
* or it is a comment whose body fails `isVerifiedClaimsTableBody`. One reason
|
|
169
|
+
* and one detail for both asserted the first in either case, so an author whose
|
|
170
|
+
* table is on the thread read a true citation being called false and had no
|
|
171
|
+
* path to the missing heading. Absent ids rank first because a body that is not
|
|
172
|
+
* there cannot be given a heading.
|
|
173
|
+
*/
|
|
174
|
+
function resolveCitedTable(citations, byCommentId) {
|
|
175
|
+
const claimed = citations.filter((row) => row.kind === "table");
|
|
176
|
+
if (claimed.length === 0) {
|
|
177
|
+
return {
|
|
178
|
+
ok: true,
|
|
179
|
+
table: citations
|
|
180
|
+
.map((row) => byCommentId.get(row.id))
|
|
181
|
+
.find((row) => row !== undefined && isVerifiedClaimsTableBody(row.body)),
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
const resolved = claimed.map((row) => ({ id: row.id, cited: byCommentId.get(row.id) }));
|
|
185
|
+
const absent = resolved.filter((row) => row.cited === undefined).map((row) => row.id);
|
|
186
|
+
const unshaped = resolved
|
|
187
|
+
.filter((row) => row.cited !== undefined && !isVerifiedClaimsTableBody(row.cited.body))
|
|
188
|
+
.map((row) => row.id);
|
|
189
|
+
if (absent.length > 0) {
|
|
190
|
+
return {
|
|
191
|
+
ok: false,
|
|
192
|
+
reason: "missing-table-cite",
|
|
193
|
+
detail: "synthesis cites a verified-claims table id that is not a comment on this thread: " +
|
|
194
|
+
renderIds(absent) +
|
|
195
|
+
(unshaped.length > 0
|
|
196
|
+
? "; also cited, on this thread and carrying no verified-claims-table heading: " +
|
|
197
|
+
renderIds(unshaped)
|
|
198
|
+
: ""),
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
if (unshaped.length > 0) {
|
|
202
|
+
return {
|
|
203
|
+
ok: false,
|
|
204
|
+
reason: "unshaped-table-cite",
|
|
205
|
+
detail: "synthesis cites a verified-claims table id that is a comment on this thread but " +
|
|
206
|
+
"opens no line with the `## Verified-claims table` heading: " +
|
|
207
|
+
renderIds(unshaped) +
|
|
208
|
+
"; add that heading to the cited comment",
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
const distinct = [...new Set(claimed.map((row) => row.id))];
|
|
212
|
+
if (distinct.length > 1) {
|
|
213
|
+
return {
|
|
214
|
+
ok: false,
|
|
215
|
+
reason: "ambiguous-table-cite",
|
|
216
|
+
detail: "synthesis claims more than one verified-claims table; every table citation must name " +
|
|
217
|
+
"the same table: " +
|
|
218
|
+
renderIds(distinct),
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
return { ok: true, table: resolved.find((row) => row.cited !== undefined)?.cited };
|
|
222
|
+
}
|
|
129
223
|
function verdictForSynthesis(comment, comments) {
|
|
130
224
|
const scan = scanCitations(comment.body);
|
|
131
225
|
const citations = scan.citations;
|
|
@@ -146,25 +240,15 @@ function verdictForSynthesis(comment, comments) {
|
|
|
146
240
|
renderIds(citations.map((row) => row.id)),
|
|
147
241
|
};
|
|
148
242
|
}
|
|
149
|
-
const citedTable = citations
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
if (citedTable === undefined) {
|
|
153
|
-
const claimed = citations.filter((row) => row.kind === "table");
|
|
154
|
-
if (claimed.length > 0) {
|
|
155
|
-
return {
|
|
156
|
-
status: "blocked",
|
|
157
|
-
reason: "missing-table-cite",
|
|
158
|
-
detail: "synthesis cites a verified-claims table id that is not a table on this thread: " +
|
|
159
|
-
renderIds(claimed.map((row) => row.id)),
|
|
160
|
-
};
|
|
161
|
-
}
|
|
243
|
+
const citedTable = resolveCitedTable(citations, byCommentId);
|
|
244
|
+
if (!citedTable.ok) {
|
|
245
|
+
return { status: "blocked", reason: citedTable.reason, detail: citedTable.detail };
|
|
162
246
|
}
|
|
163
247
|
return {
|
|
164
248
|
status: "complete",
|
|
165
249
|
synthesisCommentId: comment.id,
|
|
166
250
|
citedLeanId: citedLean.id,
|
|
167
|
-
citedTableId: citedTable?.id ?? null,
|
|
251
|
+
citedTableId: citedTable.table?.id ?? null,
|
|
168
252
|
};
|
|
169
253
|
}
|
|
170
254
|
/**
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cooperative host-session identity and exact lifecycle-command rewriting (#3611).
|
|
3
3
|
*
|
|
4
|
-
* This is payload classification, not authentication. Host hook JSON
|
|
5
|
-
* forgeable under the occupancy model's cooperating-process
|
|
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).
|
|
6
9
|
*/
|
|
7
|
-
|
|
8
|
-
export type HookHostIdentityProvider
|
|
9
|
-
export declare const MAX_HOOK_HOST_IDENTITY_UTF8_BYTES = 512;
|
|
10
|
+
import { ambientHostSessionOwner, HOST_IDENTITY_PROVIDERS, type HookHostIdentityProvider, type HookHostIdentitySource, hookHostIdentitySource, MAX_HOOK_HOST_IDENTITY_UTF8_BYTES } from "../../session/host-session-owner.js";
|
|
11
|
+
export { ambientHostSessionOwner, HOST_IDENTITY_PROVIDERS, type HookHostIdentityProvider, type HookHostIdentitySource, hookHostIdentitySource, MAX_HOOK_HOST_IDENTITY_UTF8_BYTES, };
|
|
10
12
|
export type HookHostIdentityStatus = "ok" | "missing" | "invalid" | "conflict" | "unsupported";
|
|
11
13
|
export type HookHostIdentityResolution = {
|
|
12
14
|
readonly status: "ok";
|
|
@@ -24,13 +26,25 @@ export type HookHostIdentityResolution = {
|
|
|
24
26
|
/**
|
|
25
27
|
* Resolve the stable conversation/session-family key documented by each host.
|
|
26
28
|
*
|
|
27
|
-
* - Codex: `session_id` (parent and subagents share it).
|
|
28
|
-
* - Claude Code: `session_id` (`agent_id` is
|
|
29
|
-
* - Cursor: `conversation_id`; simultaneous `session_id` must agree.
|
|
30
|
-
* - Grok
|
|
29
|
+
* - Codex: payload `session_id` (parent and subagents share it).
|
|
30
|
+
* - Claude Code: payload `session_id` (`agent_id` is not the owner key).
|
|
31
|
+
* - Cursor: payload `conversation_id`; simultaneous `session_id` must agree.
|
|
32
|
+
* - Grok: hook process `GROK_SESSION_ID` (#3873). The payload `session_id` is
|
|
33
|
+
* deliberately not read -- that contract is unverified.
|
|
34
|
+
* - Unknown hosts: no guessed identity; callers keep the explicit/env flow.
|
|
31
35
|
*/
|
|
32
|
-
export declare function resolveHookHostIdentity(host: string, payload: unknown): HookHostIdentityResolution;
|
|
33
|
-
|
|
36
|
+
export declare function resolveHookHostIdentity(host: string, payload: unknown, environ?: NodeJS.ProcessEnv): HookHostIdentityResolution;
|
|
37
|
+
/**
|
|
38
|
+
* True when a `host-env` provider simply did not publish its variable (#3873).
|
|
39
|
+
*
|
|
40
|
+
* Absence is the pre-#3873 state rather than a broken contract, so callers keep
|
|
41
|
+
* the documented explicit `--session-id` / `DEFT_SESSION_ID` flow instead of
|
|
42
|
+
* failing a host that never had a hook identity to begin with. It is never an
|
|
43
|
+
* admission: with no explicit owner the actor stays unset and the write gate
|
|
44
|
+
* still denies. A present-but-malformed variable is `invalid` and fails closed.
|
|
45
|
+
*/
|
|
46
|
+
export declare function hostIdentityFallsBackToExplicitOwner(host: string, resolution: HookHostIdentityResolution): boolean;
|
|
47
|
+
export declare const EXACT_LIFECYCLE_VERBS: readonly ["session:start", "session:ready", "session:end", "occupancy:steal", "occupancy:release", "occupancy:heartbeat", "occupancy:grant", "swarm:launch"];
|
|
34
48
|
export type ExactLifecycleVerb = (typeof EXACT_LIFECYCLE_VERBS)[number];
|
|
35
49
|
export interface ExactLifecycleCommandRewrite {
|
|
36
50
|
readonly kind: "rewrite";
|
|
@@ -72,5 +86,4 @@ export declare function inspectExactLifecycleCommand(payload: unknown): ExactLif
|
|
|
72
86
|
* operator input.
|
|
73
87
|
*/
|
|
74
88
|
export declare function rewriteExactLifecycleCommand(payload: unknown, sessionId: string): ExactLifecycleCommandResult;
|
|
75
|
-
export {};
|
|
76
89
|
//# sourceMappingURL=host-session-identity.d.ts.map
|
|
@@ -1,47 +1,22 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Cooperative host-session identity and exact lifecycle-command rewriting (#3611).
|
|
3
3
|
*
|
|
4
|
-
* This is payload classification, not authentication. Host hook JSON
|
|
5
|
-
* forgeable under the occupancy model's cooperating-process
|
|
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).
|
|
6
9
|
*/
|
|
10
|
+
import { ambientHostSessionOwner, CANONICAL_OWNER_PATTERN, canonicalHostSessionId, HOST_IDENTITY_PROVIDERS, hookHostIdentitySource, isUsableHostSessionId, MAX_HOOK_HOST_IDENTITY_UTF8_BYTES, readHostEnvIdentity, } from "../../session/host-session-owner.js";
|
|
7
11
|
import { isShellTool } from "../tools.js";
|
|
8
12
|
import { record, toolInputRecord } from "./payload.js";
|
|
9
13
|
import { hookToolName } from "./tool-name.js";
|
|
10
|
-
export
|
|
11
|
-
export const MAX_HOOK_HOST_IDENTITY_UTF8_BYTES = 512;
|
|
12
|
-
function hasControlCharacter(value) {
|
|
13
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
14
|
-
const unit = value.charCodeAt(index);
|
|
15
|
-
if (unit <= 0x1f || (unit >= 0x7f && unit <= 0x9f))
|
|
16
|
-
return true;
|
|
17
|
-
}
|
|
18
|
-
return false;
|
|
19
|
-
}
|
|
20
|
-
function hasUnpairedUtf16Surrogate(value) {
|
|
21
|
-
for (let index = 0; index < value.length; index += 1) {
|
|
22
|
-
const unit = value.charCodeAt(index);
|
|
23
|
-
if (unit >= 0xd800 && unit <= 0xdbff) {
|
|
24
|
-
const next = value.charCodeAt(index + 1);
|
|
25
|
-
if (!(next >= 0xdc00 && next <= 0xdfff))
|
|
26
|
-
return true;
|
|
27
|
-
index += 1;
|
|
28
|
-
continue;
|
|
29
|
-
}
|
|
30
|
-
if (unit >= 0xdc00 && unit <= 0xdfff)
|
|
31
|
-
return true;
|
|
32
|
-
}
|
|
33
|
-
return false;
|
|
34
|
-
}
|
|
14
|
+
export { ambientHostSessionOwner, HOST_IDENTITY_PROVIDERS, hookHostIdentitySource, MAX_HOOK_HOST_IDENTITY_UTF8_BYTES, };
|
|
35
15
|
function identityField(input, fieldName) {
|
|
36
16
|
if (!(fieldName in input))
|
|
37
17
|
return { status: "missing", value: null };
|
|
38
18
|
const raw = input[fieldName];
|
|
39
|
-
if (typeof raw !== "string" ||
|
|
40
|
-
raw.length === 0 ||
|
|
41
|
-
raw !== raw.trim() ||
|
|
42
|
-
hasControlCharacter(raw) ||
|
|
43
|
-
hasUnpairedUtf16Surrogate(raw) ||
|
|
44
|
-
Buffer.byteLength(raw, "utf8") > MAX_HOOK_HOST_IDENTITY_UTF8_BYTES) {
|
|
19
|
+
if (typeof raw !== "string" || !isUsableHostSessionId(raw)) {
|
|
45
20
|
return { status: "invalid", value: null };
|
|
46
21
|
}
|
|
47
22
|
return { status: "ok", value: raw };
|
|
@@ -49,10 +24,6 @@ function identityField(input, fieldName) {
|
|
|
49
24
|
function unresolvedHostIdentity(status, provider, message) {
|
|
50
25
|
return { status, provider, rawSessionId: null, sessionId: null, message };
|
|
51
26
|
}
|
|
52
|
-
function canonicalHostSessionId(provider, rawSessionId) {
|
|
53
|
-
const encoded = Buffer.from(rawSessionId, "utf8").toString("base64url");
|
|
54
|
-
return `host:${provider}:v1:${encoded}`;
|
|
55
|
-
}
|
|
56
27
|
function resolvedHostIdentity(provider, rawSessionId) {
|
|
57
28
|
return {
|
|
58
29
|
status: "ok",
|
|
@@ -65,28 +36,39 @@ function resolvedHostIdentity(provider, rawSessionId) {
|
|
|
65
36
|
/**
|
|
66
37
|
* Resolve the stable conversation/session-family key documented by each host.
|
|
67
38
|
*
|
|
68
|
-
* - Codex: `session_id` (parent and subagents share it).
|
|
69
|
-
* - Claude Code: `session_id` (`agent_id` is
|
|
70
|
-
* - Cursor: `conversation_id`; simultaneous `session_id` must agree.
|
|
71
|
-
* - Grok
|
|
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.
|
|
72
45
|
*/
|
|
73
|
-
export function resolveHookHostIdentity(host, payload) {
|
|
74
|
-
|
|
75
|
-
|
|
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.`);
|
|
76
50
|
}
|
|
77
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
|
+
}
|
|
78
60
|
const input = record(payload);
|
|
79
61
|
if (input === null) {
|
|
80
62
|
return unresolvedHostIdentity("invalid", provider, `${provider} hook payload is not an object.`);
|
|
81
63
|
}
|
|
82
|
-
if (provider
|
|
83
|
-
const session = identityField(input,
|
|
64
|
+
if (provider !== "cursor") {
|
|
65
|
+
const session = identityField(input, source.field);
|
|
84
66
|
if (session.status !== "ok") {
|
|
85
|
-
return unresolvedHostIdentity(session.status, provider, `${provider} hook payload ${session.status === "missing" ? "omits" : "has invalid"}
|
|
67
|
+
return unresolvedHostIdentity(session.status, provider, `${provider} hook payload ${session.status === "missing" ? "omits" : "has invalid"} ${source.field}.`);
|
|
86
68
|
}
|
|
87
69
|
return resolvedHostIdentity(provider, session.value);
|
|
88
70
|
}
|
|
89
|
-
const conversation = identityField(input,
|
|
71
|
+
const conversation = identityField(input, source.field);
|
|
90
72
|
if (conversation.status !== "ok") {
|
|
91
73
|
return unresolvedHostIdentity(conversation.status, provider, `Cursor hook payload ${conversation.status === "missing" ? "omits" : "has invalid"} conversation_id.`);
|
|
92
74
|
}
|
|
@@ -99,6 +81,18 @@ export function resolveHookHostIdentity(host, payload) {
|
|
|
99
81
|
}
|
|
100
82
|
return resolvedHostIdentity(provider, conversation.value);
|
|
101
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
|
+
}
|
|
102
96
|
export const EXACT_LIFECYCLE_VERBS = [
|
|
103
97
|
"session:start",
|
|
104
98
|
"session:ready",
|
|
@@ -106,6 +100,10 @@ export const EXACT_LIFECYCLE_VERBS = [
|
|
|
106
100
|
"occupancy:steal",
|
|
107
101
|
"occupancy:release",
|
|
108
102
|
"occupancy:heartbeat",
|
|
103
|
+
// #3954 item 2: `occupancy:grant` (and its `--revoke` arm) was the only
|
|
104
|
+
// occupancy lifecycle verb absent from this table, so the one verb that must
|
|
105
|
+
// be run by the occupant had no way to be told who the occupant is.
|
|
106
|
+
"occupancy:grant",
|
|
109
107
|
"swarm:launch",
|
|
110
108
|
];
|
|
111
109
|
const DIRECT_LIFECYCLE_VERBS = {
|
|
@@ -115,9 +113,9 @@ const DIRECT_LIFECYCLE_VERBS = {
|
|
|
115
113
|
"occupancy:steal": "occupancy:steal",
|
|
116
114
|
"occupancy:release": "occupancy:release",
|
|
117
115
|
"occupancy:heartbeat": "occupancy:heartbeat",
|
|
116
|
+
"occupancy:grant": "occupancy:grant",
|
|
118
117
|
"swarm-launch": "swarm:launch",
|
|
119
118
|
};
|
|
120
|
-
const CANONICAL_OWNER_PATTERN = /^host:(?:codex|claude|cursor):v1:[A-Za-z0-9_-]+$/;
|
|
121
119
|
// Shell expansion markers are deliberately absent: `$`/backticks for POSIX,
|
|
122
120
|
// `@` splatting for PowerShell, and `%NAME%` expansion for command shells.
|
|
123
121
|
// Backslashes are inspectable so Windows path-bearing lifecycle commands fail
|
|
@@ -179,6 +177,23 @@ const LIFECYCLE_ARGUMENT_POLICIES = {
|
|
|
179
177
|
valueFlags: new Set(["--session-id", "--project-root"]),
|
|
180
178
|
rewriteUnsafeFlags: new Set(["--project-root"]),
|
|
181
179
|
},
|
|
180
|
+
"occupancy:grant": {
|
|
181
|
+
booleanFlags: new Set(["--revoke"]),
|
|
182
|
+
valueFlags: new Set([
|
|
183
|
+
"--session-id",
|
|
184
|
+
"--child-session-id",
|
|
185
|
+
"--role",
|
|
186
|
+
"--worktree",
|
|
187
|
+
"--ttl-minutes",
|
|
188
|
+
"--host",
|
|
189
|
+
"--address",
|
|
190
|
+
"--join-protocol",
|
|
191
|
+
"--project-root",
|
|
192
|
+
]),
|
|
193
|
+
// `--worktree` rebinds the tree the grant covers, so it joins the
|
|
194
|
+
// path/destination flags kept outside the auto-approved rewrite surface.
|
|
195
|
+
rewriteUnsafeFlags: new Set(["--project-root", "--worktree"]),
|
|
196
|
+
},
|
|
182
197
|
"swarm:launch": {
|
|
183
198
|
booleanFlags: new Set([
|
|
184
199
|
"--autonomous",
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Policy / ritual / scope / permission emission stay in dispatcher + CLI adapter.
|
|
6
6
|
*/
|
|
7
7
|
export { classifyHookPayload } from "./classify.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 HookHostIdentityStatus, inspectExactLifecycleCommand, MAX_HOOK_HOST_IDENTITY_UTF8_BYTES, resolveHookHostIdentity, rewriteExactLifecycleCommand, } from "./host-session-identity.js";
|
|
9
|
-
export { hookApplyPatchBodyPaths, hookMcpArgsText, hookMutationTargetPaths, 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";
|
|
10
10
|
export { fieldPresent, fieldString, firstString, hookPayloadTopLevelKeys, record, toolInputRecord, } from "./payload.js";
|
|
11
11
|
export { applyPatchMutationPaths, parseHookStdin, stripUtf8Bom } from "./stdin.js";
|
|
12
12
|
export { hookToolName, inferCursorDirectWriteToolName, type MissingToolNameInput, missingToolNameMessage, } from "./tool-name.js";
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
* Policy / ritual / scope / permission emission stay in dispatcher + CLI adapter.
|
|
6
6
|
*/
|
|
7
7
|
export { classifyHookPayload } from "./classify.js";
|
|
8
|
-
export { EXACT_LIFECYCLE_VERBS, exactLifecycleCommandVerb, HOST_IDENTITY_PROVIDERS, inspectExactLifecycleCommand, MAX_HOOK_HOST_IDENTITY_UTF8_BYTES, resolveHookHostIdentity, rewriteExactLifecycleCommand, } from "./host-session-identity.js";
|
|
9
|
-
export { hookApplyPatchBodyPaths, hookMcpArgsText, hookMutationTargetPaths, 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";
|
|
10
10
|
export { fieldPresent, fieldString, firstString, hookPayloadTopLevelKeys, record, toolInputRecord, } from "./payload.js";
|
|
11
11
|
export { applyPatchMutationPaths, parseHookStdin, stripUtf8Bom } from "./stdin.js";
|
|
12
12
|
export { hookToolName, inferCursorDirectWriteToolName, missingToolNameMessage, } from "./tool-name.js";
|
|
@@ -7,6 +7,8 @@
|
|
|
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;
|
|
10
12
|
/** ApplyPatch body paths from patch / unified_diff / diff fields. */
|
|
11
13
|
export declare function hookApplyPatchBodyPaths(payload: unknown): string[];
|
|
12
14
|
/**
|
|
@@ -22,13 +22,13 @@ export function hookWriteTargetPath(payload) {
|
|
|
22
22
|
input.path,
|
|
23
23
|
]);
|
|
24
24
|
}
|
|
25
|
-
/** ApplyPatch body
|
|
26
|
-
export function
|
|
25
|
+
/** Raw ApplyPatch body text from patch / unified_diff / diff fields. */
|
|
26
|
+
export function hookApplyPatchBodyText(payload) {
|
|
27
27
|
const input = record(payload);
|
|
28
28
|
if (input === null)
|
|
29
|
-
return
|
|
29
|
+
return null;
|
|
30
30
|
const toolInput = toolInputRecord(input);
|
|
31
|
-
|
|
31
|
+
return firstString([
|
|
32
32
|
toolInput?.patch,
|
|
33
33
|
toolInput?.unified_diff,
|
|
34
34
|
toolInput?.diff,
|
|
@@ -36,6 +36,10 @@ export function hookApplyPatchBodyPaths(payload) {
|
|
|
36
36
|
input.unified_diff,
|
|
37
37
|
input.diff,
|
|
38
38
|
]);
|
|
39
|
+
}
|
|
40
|
+
/** ApplyPatch body paths from patch / unified_diff / diff fields. */
|
|
41
|
+
export function hookApplyPatchBodyPaths(payload) {
|
|
42
|
+
const patch = hookApplyPatchBodyText(payload);
|
|
39
43
|
return patch === null ? [] : applyPatchMutationPaths(patch);
|
|
40
44
|
}
|
|
41
45
|
/**
|