@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
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completed-arc record for design-critique ingest (#3806).
|
|
3
|
+
*
|
|
4
|
+
* Catalog chips are list-visible convenience, not clearance. Ingest waits on
|
|
5
|
+
* `design-critique: synthesis accepted, because ...` citing an accepted
|
|
6
|
+
* successor lean (and the verified-claims table when that comment exists).
|
|
7
|
+
*
|
|
8
|
+
* Citations are parsed once, by `scanCitations` (#3831). The accepted forms and
|
|
9
|
+
* the refused positions are published in `content/contracts/design-critique.md`
|
|
10
|
+
* `## Citation grammar`. Clearance is set membership against the latest
|
|
11
|
+
* successor lean, not position in the body, so citing the superseded lean --
|
|
12
|
+
* which `## Successor lean` requires -- cannot block.
|
|
13
|
+
*/
|
|
14
|
+
export type ThreadComment = {
|
|
15
|
+
readonly id: number;
|
|
16
|
+
readonly body: string;
|
|
17
|
+
};
|
|
18
|
+
export type CompletedArcBlockReason = "missing-record" | "lone-shape" | "cite-not-lean" | "missing-table-cite" | "ambiguous-table-cite";
|
|
19
|
+
export type CompletedArcVerdict = {
|
|
20
|
+
readonly status: "not-in-arc";
|
|
21
|
+
} | {
|
|
22
|
+
readonly status: "complete";
|
|
23
|
+
readonly synthesisCommentId: number;
|
|
24
|
+
readonly citedLeanId: number;
|
|
25
|
+
readonly citedTableId: number | null;
|
|
26
|
+
} | {
|
|
27
|
+
readonly status: "blocked";
|
|
28
|
+
readonly reason: CompletedArcBlockReason;
|
|
29
|
+
readonly detail: string;
|
|
30
|
+
};
|
|
31
|
+
export declare class DesignCritiqueIngestBlockedError extends Error {
|
|
32
|
+
readonly issueNumber: number;
|
|
33
|
+
readonly reason: CompletedArcBlockReason;
|
|
34
|
+
constructor(issueNumber: number, reason: CompletedArcBlockReason, detail: string);
|
|
35
|
+
}
|
|
36
|
+
export declare function isSynthesisAcceptedShape(body: string): boolean;
|
|
37
|
+
export declare function isSuccessorLeanBody(body: string): boolean;
|
|
38
|
+
export declare function isVerifiedClaimsTableBody(body: string): boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Ids cited by one comment body, in document order. Thin projection of the
|
|
41
|
+
* shared parser -- the grammar itself lives in `citation-grammar.ts`.
|
|
42
|
+
*/
|
|
43
|
+
export declare function extractCitedCommentIds(body: string): number[];
|
|
44
|
+
export declare function hasDesignCritiqueCatalogChip(labels: readonly string[]): boolean;
|
|
45
|
+
export declare function isPanelDepositBody(body: string): boolean;
|
|
46
|
+
export declare function isInFlightCritiqueThread(comments: readonly ThreadComment[]): boolean;
|
|
47
|
+
/**
|
|
48
|
+
* Ingest clearance from thread structure. Labels and author identity are not
|
|
49
|
+
* predicates. A lone synthesis-accepted sentence shape is not the record.
|
|
50
|
+
* Clearance cites the latest successor lean; an older complete record does not
|
|
51
|
+
* clear a later recut. A panel-deposit is in-flight even before critic posts.
|
|
52
|
+
*/
|
|
53
|
+
export declare function evaluateCompletedArcRecord(input: {
|
|
54
|
+
readonly labels?: readonly string[];
|
|
55
|
+
readonly comments: readonly ThreadComment[];
|
|
56
|
+
}): CompletedArcVerdict;
|
|
57
|
+
export declare function assertCompletedArcAllowsIngest(input: {
|
|
58
|
+
readonly issueNumber: number;
|
|
59
|
+
readonly labels?: readonly string[];
|
|
60
|
+
readonly comments: readonly ThreadComment[];
|
|
61
|
+
}): CompletedArcVerdict;
|
|
62
|
+
//# sourceMappingURL=completed-arc-record.d.ts.map
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Completed-arc record for design-critique ingest (#3806).
|
|
3
|
+
*
|
|
4
|
+
* Catalog chips are list-visible convenience, not clearance. Ingest waits on
|
|
5
|
+
* `design-critique: synthesis accepted, because ...` citing an accepted
|
|
6
|
+
* successor lean (and the verified-claims table when that comment exists).
|
|
7
|
+
*
|
|
8
|
+
* Citations are parsed once, by `scanCitations` (#3831). The accepted forms and
|
|
9
|
+
* the refused positions are published in `content/contracts/design-critique.md`
|
|
10
|
+
* `## Citation grammar`. Clearance is set membership against the latest
|
|
11
|
+
* successor lean, not position in the body, so citing the superseded lean --
|
|
12
|
+
* which `## Successor lean` requires -- cannot block.
|
|
13
|
+
*/
|
|
14
|
+
import { ACCEPTED_CITATION_FORMS, scanCitations, } from "./citation-grammar.js";
|
|
15
|
+
import { DESIGN_CRITIQUE_CATALOG_CHIPS } from "./exclusive-chip.js";
|
|
16
|
+
const SYNTHESIS_SHAPE_RE = /(?:^|\n)\s*design-critique:\s*synthesis accepted,\s*because\b/i;
|
|
17
|
+
const LEAN_HEADING_RE = /(?:^|\n)\s*\*{0,2}Lean:\*{0,2}/;
|
|
18
|
+
const TABLE_HEADING_RE = /(?:^|\n)\s*##\s+Verified-claims table\b/;
|
|
19
|
+
/** How many ids a block detail lists before it truncates. */
|
|
20
|
+
const DETAIL_ID_LIMIT = 5;
|
|
21
|
+
const CATALOG = new Set(DESIGN_CRITIQUE_CATALOG_CHIPS);
|
|
22
|
+
export class DesignCritiqueIngestBlockedError extends Error {
|
|
23
|
+
issueNumber;
|
|
24
|
+
reason;
|
|
25
|
+
constructor(issueNumber, reason, detail) {
|
|
26
|
+
super(`issue:ingest refused #${issueNumber}: design-critique ${reason} (${detail}) -- nothing written.`);
|
|
27
|
+
this.name = "DesignCritiqueIngestBlockedError";
|
|
28
|
+
this.issueNumber = issueNumber;
|
|
29
|
+
this.reason = reason;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
export function isSynthesisAcceptedShape(body) {
|
|
33
|
+
return SYNTHESIS_SHAPE_RE.test(body);
|
|
34
|
+
}
|
|
35
|
+
export function isSuccessorLeanBody(body) {
|
|
36
|
+
return LEAN_HEADING_RE.test(body);
|
|
37
|
+
}
|
|
38
|
+
export function isVerifiedClaimsTableBody(body) {
|
|
39
|
+
return TABLE_HEADING_RE.test(body);
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Ids cited by one comment body, in document order. Thin projection of the
|
|
43
|
+
* shared parser -- the grammar itself lives in `citation-grammar.ts`.
|
|
44
|
+
*/
|
|
45
|
+
export function extractCitedCommentIds(body) {
|
|
46
|
+
const ids = [];
|
|
47
|
+
const seen = new Set();
|
|
48
|
+
for (const citation of scanCitations(body).citations) {
|
|
49
|
+
if (seen.has(citation.id))
|
|
50
|
+
continue;
|
|
51
|
+
seen.add(citation.id);
|
|
52
|
+
ids.push(citation.id);
|
|
53
|
+
}
|
|
54
|
+
return ids;
|
|
55
|
+
}
|
|
56
|
+
export function hasDesignCritiqueCatalogChip(labels) {
|
|
57
|
+
return labels.some((name) => CATALOG.has(name));
|
|
58
|
+
}
|
|
59
|
+
const CRITIC_ROLE_RE = /(?:^|\n)\s*role:\s*critic\b/i;
|
|
60
|
+
const MECHANISM_SHAPED_FIELD_RE = /(?:^|\n)\s*mechanism-shaped:\s*true\b/i;
|
|
61
|
+
const PANEL_DEPOSIT_RE = /(?:^|\n)\s*panel-deposit\b/i;
|
|
62
|
+
const PARENT_ROLE_RE = /(?:^|\n)\s*role:\s*parent\b/i;
|
|
63
|
+
const SIBLINGS_FIELD_RE = /(?:^|\n)\s*siblings:\s*\d+/i;
|
|
64
|
+
const INPUT_CEILING_FIELD_RE = /(?:^|\n)\s*input-ceiling:\s*\d+/i;
|
|
65
|
+
export function isPanelDepositBody(body) {
|
|
66
|
+
if (PANEL_DEPOSIT_RE.test(body))
|
|
67
|
+
return true;
|
|
68
|
+
return (PARENT_ROLE_RE.test(body) && SIBLINGS_FIELD_RE.test(body) && INPUT_CEILING_FIELD_RE.test(body));
|
|
69
|
+
}
|
|
70
|
+
export function isInFlightCritiqueThread(comments) {
|
|
71
|
+
return comments.some((comment) => isSuccessorLeanBody(comment.body) ||
|
|
72
|
+
CRITIC_ROLE_RE.test(comment.body) ||
|
|
73
|
+
MECHANISM_SHAPED_FIELD_RE.test(comment.body) ||
|
|
74
|
+
isPanelDepositBody(comment.body));
|
|
75
|
+
}
|
|
76
|
+
function byId(comments) {
|
|
77
|
+
const map = new Map();
|
|
78
|
+
for (const comment of comments) {
|
|
79
|
+
map.set(comment.id, comment);
|
|
80
|
+
}
|
|
81
|
+
return map;
|
|
82
|
+
}
|
|
83
|
+
function latestSuccessorLean(comments) {
|
|
84
|
+
let latest;
|
|
85
|
+
for (const comment of comments) {
|
|
86
|
+
if (!isSuccessorLeanBody(comment.body))
|
|
87
|
+
continue;
|
|
88
|
+
if (latest === undefined || comment.id > latest.id)
|
|
89
|
+
latest = comment;
|
|
90
|
+
}
|
|
91
|
+
return latest;
|
|
92
|
+
}
|
|
93
|
+
function renderIds(ids) {
|
|
94
|
+
const shown = ids.slice(0, DETAIL_ID_LIMIT).join(", ");
|
|
95
|
+
return ids.length > DETAIL_ID_LIMIT
|
|
96
|
+
? `${shown}, and ${ids.length - DETAIL_ID_LIMIT} more`
|
|
97
|
+
: shown;
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Report what was scanned and what was found. A guess at the cause sends the
|
|
101
|
+
* operator back to re-post the same body and reproduce the block (#3831).
|
|
102
|
+
*/
|
|
103
|
+
function loneShapeDetail(scan) {
|
|
104
|
+
const parts = [
|
|
105
|
+
"synthesis-accepted sentence shape is present but cites no accepted successor lean",
|
|
106
|
+
];
|
|
107
|
+
if (scan.idShapedRuns.length === 0) {
|
|
108
|
+
parts.push("no 8-or-more digit id appears in the body");
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
parts.push(`${scan.idShapedRuns.length} 8-or-more digit id(s) appear in the body: ` +
|
|
112
|
+
renderIds(scan.idShapedRuns));
|
|
113
|
+
}
|
|
114
|
+
if (scan.rejected.length > 0) {
|
|
115
|
+
const classes = [...new Set(scan.rejected.map((row) => row.reason))].sort().join(", ");
|
|
116
|
+
parts.push(`${scan.rejected.length} keyword-anchored occurrence(s) refused by position (${classes})`);
|
|
117
|
+
}
|
|
118
|
+
parts.push(`accepted forms: ${ACCEPTED_CITATION_FORMS.join(" | ")}`);
|
|
119
|
+
return parts.join("; ");
|
|
120
|
+
}
|
|
121
|
+
function resolveCitedLean(citations, byCommentId, latestLean) {
|
|
122
|
+
if (latestLean !== undefined && citations.some((row) => row.id === latestLean.id)) {
|
|
123
|
+
return latestLean;
|
|
124
|
+
}
|
|
125
|
+
return citations
|
|
126
|
+
.map((row) => byCommentId.get(row.id))
|
|
127
|
+
.find((row) => row !== undefined && isSuccessorLeanBody(row.body));
|
|
128
|
+
}
|
|
129
|
+
/**
|
|
130
|
+
* Table resolution precedence (#3932).
|
|
131
|
+
*
|
|
132
|
+
* A typed `verified-claims table <id>` citation is the synthesis naming its own
|
|
133
|
+
* table, so it decides resolution: a generic citation neither satisfies it nor
|
|
134
|
+
* masks it. Scanning every citation for the first table-shaped body -- what
|
|
135
|
+
* this did before -- let an unrelated table-shaped comment clear a record whose
|
|
136
|
+
* claimed table is not a table, because the `missing-table-cite` refusal ran
|
|
137
|
+
* only after that untyped search failed.
|
|
138
|
+
*
|
|
139
|
+
* Cardinality rule: every table-kind claim must resolve to the same
|
|
140
|
+
* verified-claims table artifact. One valid typed claim must not launder an
|
|
141
|
+
* invalid one -- filtering to typed citations and then taking the first
|
|
142
|
+
* table-shaped body finds the real table in a valid-plus-ghost pair and never
|
|
143
|
+
* examines the ghost. `scanCitations` deduplicates on `(id, kind)`, so naming
|
|
144
|
+
* one table id twice is a single claim, while two distinct typed ids are two
|
|
145
|
+
* claims and cannot both be this record's table.
|
|
146
|
+
*
|
|
147
|
+
* With no typed claim the generic scan stands unchanged: permalink and bare
|
|
148
|
+
* `comment` citations scan as kind `comment`, and that is the published form
|
|
149
|
+
* these records use. Narrowing the citation contract so a table must be named
|
|
150
|
+
* by keyword is a separate decision needing its own migration criteria.
|
|
151
|
+
*/
|
|
152
|
+
function resolveCitedTable(citations, byCommentId) {
|
|
153
|
+
const claimed = citations.filter((row) => row.kind === "table");
|
|
154
|
+
if (claimed.length === 0) {
|
|
155
|
+
return {
|
|
156
|
+
ok: true,
|
|
157
|
+
table: citations
|
|
158
|
+
.map((row) => byCommentId.get(row.id))
|
|
159
|
+
.find((row) => row !== undefined && isVerifiedClaimsTableBody(row.body)),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const resolved = claimed.map((row) => ({ id: row.id, cited: byCommentId.get(row.id) }));
|
|
163
|
+
const unresolved = resolved.filter((row) => row.cited === undefined || !isVerifiedClaimsTableBody(row.cited.body));
|
|
164
|
+
if (unresolved.length > 0) {
|
|
165
|
+
return {
|
|
166
|
+
ok: false,
|
|
167
|
+
reason: "missing-table-cite",
|
|
168
|
+
detail: "synthesis cites a verified-claims table id that is not a table on this thread: " +
|
|
169
|
+
renderIds(unresolved.map((row) => row.id)),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
const distinct = [...new Set(claimed.map((row) => row.id))];
|
|
173
|
+
if (distinct.length > 1) {
|
|
174
|
+
return {
|
|
175
|
+
ok: false,
|
|
176
|
+
reason: "ambiguous-table-cite",
|
|
177
|
+
detail: "synthesis claims more than one verified-claims table; every table citation must name " +
|
|
178
|
+
"the same table: " +
|
|
179
|
+
renderIds(distinct),
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
return { ok: true, table: resolved.find((row) => row.cited !== undefined)?.cited };
|
|
183
|
+
}
|
|
184
|
+
function verdictForSynthesis(comment, comments) {
|
|
185
|
+
const scan = scanCitations(comment.body);
|
|
186
|
+
const citations = scan.citations;
|
|
187
|
+
if (citations.length === 0) {
|
|
188
|
+
return {
|
|
189
|
+
status: "blocked",
|
|
190
|
+
reason: "lone-shape",
|
|
191
|
+
detail: loneShapeDetail(scan),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
const byCommentId = byId(comments);
|
|
195
|
+
const citedLean = resolveCitedLean(citations, byCommentId, latestSuccessorLean(comments));
|
|
196
|
+
if (citedLean === undefined) {
|
|
197
|
+
return {
|
|
198
|
+
status: "blocked",
|
|
199
|
+
reason: "cite-not-lean",
|
|
200
|
+
detail: "no cited id is a successor lean on this thread; cited: " +
|
|
201
|
+
renderIds(citations.map((row) => row.id)),
|
|
202
|
+
};
|
|
203
|
+
}
|
|
204
|
+
const citedTable = resolveCitedTable(citations, byCommentId);
|
|
205
|
+
if (!citedTable.ok) {
|
|
206
|
+
return { status: "blocked", reason: citedTable.reason, detail: citedTable.detail };
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
status: "complete",
|
|
210
|
+
synthesisCommentId: comment.id,
|
|
211
|
+
citedLeanId: citedLean.id,
|
|
212
|
+
citedTableId: citedTable.table?.id ?? null,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
/**
|
|
216
|
+
* Ingest clearance from thread structure. Labels and author identity are not
|
|
217
|
+
* predicates. A lone synthesis-accepted sentence shape is not the record.
|
|
218
|
+
* Clearance cites the latest successor lean; an older complete record does not
|
|
219
|
+
* clear a later recut. A panel-deposit is in-flight even before critic posts.
|
|
220
|
+
*/
|
|
221
|
+
export function evaluateCompletedArcRecord(input) {
|
|
222
|
+
const comments = input.comments;
|
|
223
|
+
const labels = input.labels ?? [];
|
|
224
|
+
const synthesis = comments.filter((c) => isSynthesisAcceptedShape(c.body));
|
|
225
|
+
const completeRecords = synthesis
|
|
226
|
+
.map((comment) => verdictForSynthesis(comment, comments))
|
|
227
|
+
.filter((verdict) => {
|
|
228
|
+
return verdict.status === "complete";
|
|
229
|
+
});
|
|
230
|
+
if (completeRecords.length > 0) {
|
|
231
|
+
const latestLean = latestSuccessorLean(comments);
|
|
232
|
+
const matching = latestLean === undefined
|
|
233
|
+
? completeRecords
|
|
234
|
+
: completeRecords.filter((record) => record.citedLeanId === latestLean.id);
|
|
235
|
+
if (matching.length > 0) {
|
|
236
|
+
return matching.reduce((a, b) => (a.synthesisCommentId >= b.synthesisCommentId ? a : b));
|
|
237
|
+
}
|
|
238
|
+
const latestCompleteId = completeRecords.reduce((max, record) => Math.max(max, record.synthesisCommentId), 0);
|
|
239
|
+
const laterSynthesis = synthesis.filter((comment) => comment.id > latestCompleteId);
|
|
240
|
+
if (laterSynthesis.length > 0) {
|
|
241
|
+
const latest = laterSynthesis.reduce((a, b) => (a.id >= b.id ? a : b));
|
|
242
|
+
return verdictForSynthesis(latest, comments);
|
|
243
|
+
}
|
|
244
|
+
const citedLeanIds = completeRecords.map((record) => record.citedLeanId);
|
|
245
|
+
const latestLeanId = latestLean === undefined ? "unknown" : String(latestLean.id);
|
|
246
|
+
return {
|
|
247
|
+
status: "blocked",
|
|
248
|
+
reason: "missing-record",
|
|
249
|
+
detail: `no completed-arc record cites the latest successor lean ${latestLeanId} on this thread; ` +
|
|
250
|
+
`existing record(s) cite lean ${renderIds(citedLeanIds)}; ` +
|
|
251
|
+
`ingest waits on a record citing lean ${latestLeanId}`,
|
|
252
|
+
};
|
|
253
|
+
}
|
|
254
|
+
if (synthesis.length > 0) {
|
|
255
|
+
const latest = synthesis.reduce((a, b) => (a.id >= b.id ? a : b));
|
|
256
|
+
return verdictForSynthesis(latest, comments);
|
|
257
|
+
}
|
|
258
|
+
const inArc = hasDesignCritiqueCatalogChip(labels) || isInFlightCritiqueThread(comments);
|
|
259
|
+
if (!inArc) {
|
|
260
|
+
return { status: "not-in-arc" };
|
|
261
|
+
}
|
|
262
|
+
return {
|
|
263
|
+
status: "blocked",
|
|
264
|
+
reason: "missing-record",
|
|
265
|
+
detail: "design-critique is in flight but the completed-arc record is missing: " +
|
|
266
|
+
"`design-critique: synthesis accepted, because ...` citing the accepted successor lean; " +
|
|
267
|
+
`accepted forms: ${ACCEPTED_CITATION_FORMS.join(" | ")}`,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
export function assertCompletedArcAllowsIngest(input) {
|
|
271
|
+
const verdict = evaluateCompletedArcRecord(input);
|
|
272
|
+
if (verdict.status === "blocked") {
|
|
273
|
+
throw new DesignCritiqueIngestBlockedError(input.issueNumber, verdict.reason, verdict.detail);
|
|
274
|
+
}
|
|
275
|
+
return verdict;
|
|
276
|
+
}
|
|
277
|
+
//# sourceMappingURL=completed-arc-record.js.map
|
package/dist/freshness/bind.d.ts
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Host-agnostic storage:
|
|
5
5
|
* - Default (no sessionId): `.deft/session-bind.json` — single-operator convenience.
|
|
6
|
-
* - With sessionId: `.deft/session-binds/<
|
|
6
|
+
* - With sessionId: `.deft/session-binds/<sha256-slice>.json` — isolated multi-session binds.
|
|
7
7
|
*
|
|
8
8
|
* Multi-agent hosts MUST pass a stable host session identity when binding and
|
|
9
9
|
* reporting so one session cannot certify another as current.
|
|
@@ -16,10 +16,39 @@ export declare const SESSION_BINDS_DIR_REL: string;
|
|
|
16
16
|
export declare function sessionBindPath(projectRoot: string, sessionId?: string | null): string;
|
|
17
17
|
/**
|
|
18
18
|
* Stable filesystem-safe file name for a host session id.
|
|
19
|
-
*
|
|
20
|
-
*
|
|
19
|
+
*
|
|
20
|
+
* Hash only (#3768): a 24-hex SHA-256 slice of the id. Hex cannot carry a path
|
|
21
|
+
* separator, so path injection is structurally impossible, and the name stays
|
|
22
|
+
* derivable from the id — lookup is one computed path, never a directory scan.
|
|
23
|
+
*
|
|
24
|
+
* Records written before #3768 also carried the first 32 characters of the id
|
|
25
|
+
* (`legacySessionFileName`). Dropping that prefix is hygiene, not a security
|
|
26
|
+
* fix: the same id is already published in `.deft/occupancy.json`,
|
|
27
|
+
* `.deft/ritual-state.json` and the bind record body (#3611 / #3754).
|
|
28
|
+
*
|
|
29
|
+
* Coupling warning: if `.deft/` JSON bodies are ever tightened (say to `0600`)
|
|
30
|
+
* while names still carry the prefix, the prefix becomes independently
|
|
31
|
+
* load-bearing — directory read permission alone would recover an id that the
|
|
32
|
+
* file mode was meant to protect. Anyone tightening those modes must keep bind
|
|
33
|
+
* names prefix-free.
|
|
21
34
|
*/
|
|
22
35
|
export declare function safeSessionFileName(sessionId: string): string;
|
|
36
|
+
/**
|
|
37
|
+
* Pre-#3768 record name: sanitized 32-character id prefix plus the same hash.
|
|
38
|
+
*
|
|
39
|
+
* Existing records are **tolerated, not migrated**. `readBoundGeneration` falls
|
|
40
|
+
* back to this name when the hash-only record is absent, so freshness pins
|
|
41
|
+
* written before the rename are not orphaned; writes always use the hash-only
|
|
42
|
+
* name, so the next bind supersedes the legacy record. Both names derive from
|
|
43
|
+
* the id, so the fallback stays O(1) and adds no directory scan.
|
|
44
|
+
*
|
|
45
|
+
* Character filter is O(n) (no regex) — CodeQL poly-redos on uncontrolled ids.
|
|
46
|
+
*/
|
|
47
|
+
export declare function legacySessionFileName(sessionId: string): string;
|
|
48
|
+
/**
|
|
49
|
+
* Derived path of a pre-#3768 record. Read fallback only — never a write target.
|
|
50
|
+
*/
|
|
51
|
+
export declare function legacySessionBindPath(projectRoot: string, sessionId: string): string;
|
|
23
52
|
export interface BindSessionOptions {
|
|
24
53
|
readonly sessionId?: string | null;
|
|
25
54
|
readonly nowIso?: string;
|
package/dist/freshness/bind.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Host-agnostic storage:
|
|
5
5
|
* - Default (no sessionId): `.deft/session-bind.json` — single-operator convenience.
|
|
6
|
-
* - With sessionId: `.deft/session-binds/<
|
|
6
|
+
* - With sessionId: `.deft/session-binds/<sha256-slice>.json` — isolated multi-session binds.
|
|
7
7
|
*
|
|
8
8
|
* Multi-agent hosts MUST pass a stable host session identity when binding and
|
|
9
9
|
* reporting so one session cannot certify another as current.
|
|
@@ -26,14 +26,44 @@ export function sessionBindPath(projectRoot, sessionId) {
|
|
|
26
26
|
}
|
|
27
27
|
return join(root, SESSION_BINDS_DIR_REL, safeSessionFileName(id));
|
|
28
28
|
}
|
|
29
|
+
function sessionIdHash(sessionId) {
|
|
30
|
+
return createHash("sha256").update(sessionId.trim(), "utf8").digest("hex").slice(0, 24);
|
|
31
|
+
}
|
|
29
32
|
/**
|
|
30
33
|
* Stable filesystem-safe file name for a host session id.
|
|
31
|
-
*
|
|
32
|
-
*
|
|
34
|
+
*
|
|
35
|
+
* Hash only (#3768): a 24-hex SHA-256 slice of the id. Hex cannot carry a path
|
|
36
|
+
* separator, so path injection is structurally impossible, and the name stays
|
|
37
|
+
* derivable from the id — lookup is one computed path, never a directory scan.
|
|
38
|
+
*
|
|
39
|
+
* Records written before #3768 also carried the first 32 characters of the id
|
|
40
|
+
* (`legacySessionFileName`). Dropping that prefix is hygiene, not a security
|
|
41
|
+
* fix: the same id is already published in `.deft/occupancy.json`,
|
|
42
|
+
* `.deft/ritual-state.json` and the bind record body (#3611 / #3754).
|
|
43
|
+
*
|
|
44
|
+
* Coupling warning: if `.deft/` JSON bodies are ever tightened (say to `0600`)
|
|
45
|
+
* while names still carry the prefix, the prefix becomes independently
|
|
46
|
+
* load-bearing — directory read permission alone would recover an id that the
|
|
47
|
+
* file mode was meant to protect. Anyone tightening those modes must keep bind
|
|
48
|
+
* names prefix-free.
|
|
33
49
|
*/
|
|
34
50
|
export function safeSessionFileName(sessionId) {
|
|
51
|
+
return `${sessionIdHash(sessionId)}.json`;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Pre-#3768 record name: sanitized 32-character id prefix plus the same hash.
|
|
55
|
+
*
|
|
56
|
+
* Existing records are **tolerated, not migrated**. `readBoundGeneration` falls
|
|
57
|
+
* back to this name when the hash-only record is absent, so freshness pins
|
|
58
|
+
* written before the rename are not orphaned; writes always use the hash-only
|
|
59
|
+
* name, so the next bind supersedes the legacy record. Both names derive from
|
|
60
|
+
* the id, so the fallback stays O(1) and adds no directory scan.
|
|
61
|
+
*
|
|
62
|
+
* Character filter is O(n) (no regex) — CodeQL poly-redos on uncontrolled ids.
|
|
63
|
+
*/
|
|
64
|
+
export function legacySessionFileName(sessionId) {
|
|
35
65
|
const trimmed = sessionId.trim();
|
|
36
|
-
const hash =
|
|
66
|
+
const hash = sessionIdHash(trimmed);
|
|
37
67
|
let prefix = "";
|
|
38
68
|
for (let i = 0; i < trimmed.length && prefix.length < 32; i++) {
|
|
39
69
|
const ch = trimmed[i] ?? "";
|
|
@@ -60,6 +90,12 @@ export function safeSessionFileName(sessionId) {
|
|
|
60
90
|
const base = prefix.length > 0 ? `${prefix}-${hash}` : hash;
|
|
61
91
|
return `${base}.json`;
|
|
62
92
|
}
|
|
93
|
+
/**
|
|
94
|
+
* Derived path of a pre-#3768 record. Read fallback only — never a write target.
|
|
95
|
+
*/
|
|
96
|
+
export function legacySessionBindPath(projectRoot, sessionId) {
|
|
97
|
+
return join(resolve(projectRoot), SESSION_BINDS_DIR_REL, legacySessionFileName(sessionId));
|
|
98
|
+
}
|
|
63
99
|
function nowIsoDefault() {
|
|
64
100
|
return new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
|
|
65
101
|
}
|
|
@@ -108,12 +144,7 @@ export function parseBoundGeneration(raw) {
|
|
|
108
144
|
...(payloadLoaded ? { payloadLoaded: true } : {}),
|
|
109
145
|
};
|
|
110
146
|
}
|
|
111
|
-
|
|
112
|
-
export function readBoundGeneration(projectRoot, options = {}) {
|
|
113
|
-
const path = sessionBindPath(projectRoot, options.sessionId);
|
|
114
|
-
if (!existsSync(path)) {
|
|
115
|
-
return null;
|
|
116
|
-
}
|
|
147
|
+
function readBoundAt(path, wantSessionId) {
|
|
117
148
|
try {
|
|
118
149
|
const text = readFileSync(path, "utf8");
|
|
119
150
|
const bound = parseBoundGeneration(JSON.parse(text));
|
|
@@ -122,8 +153,7 @@ export function readBoundGeneration(projectRoot, options = {}) {
|
|
|
122
153
|
}
|
|
123
154
|
// When reading a session-scoped bind, refuse a record whose embedded
|
|
124
155
|
// sessionId disagrees (tamper / wrong file).
|
|
125
|
-
|
|
126
|
-
if (want.length > 0 && bound.sessionId && bound.sessionId !== want) {
|
|
156
|
+
if (wantSessionId.length > 0 && bound.sessionId && bound.sessionId !== wantSessionId) {
|
|
127
157
|
return null;
|
|
128
158
|
}
|
|
129
159
|
return bound;
|
|
@@ -132,6 +162,22 @@ export function readBoundGeneration(projectRoot, options = {}) {
|
|
|
132
162
|
return null;
|
|
133
163
|
}
|
|
134
164
|
}
|
|
165
|
+
/** Read the session bind record (null when absent/unreadable). */
|
|
166
|
+
export function readBoundGeneration(projectRoot, options = {}) {
|
|
167
|
+
const want = typeof options.sessionId === "string" ? options.sessionId.trim() : "";
|
|
168
|
+
const path = sessionBindPath(projectRoot, options.sessionId);
|
|
169
|
+
if (existsSync(path)) {
|
|
170
|
+
return readBoundAt(path, want);
|
|
171
|
+
}
|
|
172
|
+
if (want.length === 0) {
|
|
173
|
+
return null;
|
|
174
|
+
}
|
|
175
|
+
// Pre-#3768 records are tolerated, not migrated: one extra derived path and
|
|
176
|
+
// still no directory scan, so pins written under the old name resolve until
|
|
177
|
+
// the next bind supersedes them.
|
|
178
|
+
const legacy = legacySessionBindPath(projectRoot, want);
|
|
179
|
+
return existsSync(legacy) ? readBoundAt(legacy, want) : null;
|
|
180
|
+
}
|
|
135
181
|
function writeBoundAt(projectRoot, bound, sessionId) {
|
|
136
182
|
const root = resolve(projectRoot);
|
|
137
183
|
const target = sessionBindPath(projectRoot, sessionId);
|
|
@@ -0,0 +1,89 @@
|
|
|
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, 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, };
|
|
12
|
+
export type HookHostIdentityStatus = "ok" | "missing" | "invalid" | "conflict" | "unsupported";
|
|
13
|
+
export type HookHostIdentityResolution = {
|
|
14
|
+
readonly status: "ok";
|
|
15
|
+
readonly provider: HookHostIdentityProvider;
|
|
16
|
+
readonly rawSessionId: string;
|
|
17
|
+
readonly sessionId: string;
|
|
18
|
+
readonly message: null;
|
|
19
|
+
} | {
|
|
20
|
+
readonly status: Exclude<HookHostIdentityStatus, "ok">;
|
|
21
|
+
readonly provider: string;
|
|
22
|
+
readonly rawSessionId: null;
|
|
23
|
+
readonly sessionId: null;
|
|
24
|
+
readonly message: string;
|
|
25
|
+
};
|
|
26
|
+
/**
|
|
27
|
+
* Resolve the stable conversation/session-family key documented by each host.
|
|
28
|
+
*
|
|
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.
|
|
35
|
+
*/
|
|
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", "swarm:launch"];
|
|
48
|
+
export type ExactLifecycleVerb = (typeof EXACT_LIFECYCLE_VERBS)[number];
|
|
49
|
+
export interface ExactLifecycleCommandRewrite {
|
|
50
|
+
readonly kind: "rewrite";
|
|
51
|
+
readonly verb: ExactLifecycleVerb;
|
|
52
|
+
readonly originalCommand: string;
|
|
53
|
+
readonly rewrittenCommand: string;
|
|
54
|
+
readonly updatedInput: Record<string, unknown>;
|
|
55
|
+
}
|
|
56
|
+
export interface ExactLifecycleCommandConflict {
|
|
57
|
+
readonly kind: "conflict";
|
|
58
|
+
readonly verb: ExactLifecycleVerb;
|
|
59
|
+
readonly existingSessionId: string;
|
|
60
|
+
readonly requestedSessionId: string;
|
|
61
|
+
readonly message: string;
|
|
62
|
+
}
|
|
63
|
+
export type ExactLifecycleCommandResult = ExactLifecycleCommandRewrite | ExactLifecycleCommandConflict | null;
|
|
64
|
+
/** Logical lifecycle verb for one exact, simple shell command; otherwise null. */
|
|
65
|
+
export declare function exactLifecycleCommandVerb(payload: unknown): ExactLifecycleVerb | null;
|
|
66
|
+
interface SessionIdArgs {
|
|
67
|
+
readonly status: "absent" | "present" | "invalid";
|
|
68
|
+
readonly values: readonly string[];
|
|
69
|
+
}
|
|
70
|
+
export interface ExactLifecycleCommandInspection {
|
|
71
|
+
readonly verb: ExactLifecycleVerb;
|
|
72
|
+
readonly task: boolean;
|
|
73
|
+
readonly rewriteSafe: boolean;
|
|
74
|
+
readonly requiresOwner: boolean;
|
|
75
|
+
readonly sessionIdStatus: SessionIdArgs["status"];
|
|
76
|
+
readonly sessionId: string | null;
|
|
77
|
+
}
|
|
78
|
+
/** Inspect one exact lifecycle command without requiring a host identity. */
|
|
79
|
+
export declare function inspectExactLifecycleCommand(payload: unknown): ExactLifecycleCommandInspection | null;
|
|
80
|
+
/**
|
|
81
|
+
* Append a canonical host owner to one exact Directive lifecycle command.
|
|
82
|
+
*
|
|
83
|
+
* The returned `updatedInput` is a complete shallow clone of the original
|
|
84
|
+
* host tool input. `null` means no safe rewrite. An explicit foreign identity
|
|
85
|
+
* is surfaced as a conflict so dispatcher wiring can deny instead of replacing
|
|
86
|
+
* operator input.
|
|
87
|
+
*/
|
|
88
|
+
export declare function rewriteExactLifecycleCommand(payload: unknown, sessionId: string): ExactLifecycleCommandResult;
|
|
89
|
+
//# sourceMappingURL=host-session-identity.d.ts.map
|