@deftai/directive-core 0.95.0 → 0.97.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/dist/cache/archive.d.ts +134 -0
  2. package/dist/cache/archive.js +636 -0
  3. package/dist/cache/index.d.ts +1 -0
  4. package/dist/cache/index.js +1 -0
  5. package/dist/cache/main.js +298 -1
  6. package/dist/check/gate-lists.js +8 -0
  7. package/dist/consumer-check-contract/evaluate.d.ts +124 -0
  8. package/dist/consumer-check-contract/evaluate.js +699 -0
  9. package/dist/consumer-check-contract/index.d.ts +5 -0
  10. package/dist/consumer-check-contract/index.js +5 -0
  11. package/dist/content-contracts/skills/helpers.d.ts +1 -1
  12. package/dist/content-contracts/skills/helpers.js +1 -0
  13. package/dist/delivery-attempt/disk-begin.d.ts +51 -0
  14. package/dist/delivery-attempt/disk-begin.js +68 -0
  15. package/dist/delivery-attempt/evaluate.d.ts +26 -0
  16. package/dist/delivery-attempt/evaluate.js +443 -0
  17. package/dist/delivery-attempt/fingerprint.d.ts +32 -0
  18. package/dist/delivery-attempt/fingerprint.js +100 -0
  19. package/dist/delivery-attempt/handoff.d.ts +25 -0
  20. package/dist/delivery-attempt/handoff.js +102 -0
  21. package/dist/delivery-attempt/index.d.ts +17 -0
  22. package/dist/delivery-attempt/index.js +17 -0
  23. package/dist/delivery-attempt/ledger.d.ts +169 -0
  24. package/dist/delivery-attempt/ledger.js +758 -0
  25. package/dist/delivery-attempt/material-delta.d.ts +38 -0
  26. package/dist/delivery-attempt/material-delta.js +126 -0
  27. package/dist/delivery-attempt/types.d.ts +210 -0
  28. package/dist/delivery-attempt/types.js +77 -0
  29. package/dist/doctor/index.d.ts +1 -0
  30. package/dist/doctor/index.js +1 -0
  31. package/dist/doctor/main.js +12 -0
  32. package/dist/doctor/openclaw-soft-rebind.d.ts +26 -0
  33. package/dist/doctor/openclaw-soft-rebind.js +164 -0
  34. package/dist/hooks/dispatcher.d.ts +2 -1
  35. package/dist/hooks/dispatcher.js +63 -9
  36. package/dist/index.d.ts +5 -0
  37. package/dist/index.js +5 -0
  38. package/dist/init-deposit/gitignore.js +7 -0
  39. package/dist/init-deposit/hygiene.d.ts +1 -1
  40. package/dist/init-deposit/hygiene.js +16 -4
  41. package/dist/init-deposit/init-deposit.js +5 -0
  42. package/dist/init-deposit/refresh.js +3 -0
  43. package/dist/init-deposit/scaffold.js +6 -5
  44. package/dist/parent-turn-shape/evaluate.d.ts +84 -0
  45. package/dist/parent-turn-shape/evaluate.js +353 -0
  46. package/dist/parent-turn-shape/index.d.ts +8 -0
  47. package/dist/parent-turn-shape/index.js +8 -0
  48. package/dist/pr-merge-readiness/ci-gate.d.ts +29 -1
  49. package/dist/pr-merge-readiness/ci-gate.js +191 -24
  50. package/dist/pr-merge-readiness/compute.js +10 -1
  51. package/dist/pr-merge-readiness/index.d.ts +2 -1
  52. package/dist/pr-merge-readiness/index.js +2 -1
  53. package/dist/pr-merge-readiness/output.js +14 -0
  54. package/dist/pr-merge-readiness/platform-status.d.ts +29 -0
  55. package/dist/pr-merge-readiness/platform-status.js +49 -0
  56. package/dist/pr-watch/constants.d.ts +10 -0
  57. package/dist/pr-watch/constants.js +12 -1
  58. package/dist/pr-watch/main.js +16 -1
  59. package/dist/pr-watch/probe.js +13 -1
  60. package/dist/pr-watch/types.d.ts +3 -2
  61. package/dist/pr-watch/watch.js +14 -7
  62. package/dist/review-monitor/constants.js +3 -2
  63. package/dist/review-monitor/tier-detection.d.ts +7 -3
  64. package/dist/review-monitor/tier-detection.js +18 -1
  65. package/dist/review-monitor/verify.js +18 -0
  66. package/dist/scope/index.d.ts +2 -0
  67. package/dist/scope/index.js +2 -0
  68. package/dist/scope/main.d.ts +10 -0
  69. package/dist/scope/main.js +109 -24
  70. package/dist/scope/promote-from-issue.d.ts +49 -0
  71. package/dist/scope/promote-from-issue.js +367 -0
  72. package/dist/scope/promote-path.d.ts +39 -0
  73. package/dist/scope/promote-path.js +105 -0
  74. package/dist/scope-provenance/digest.d.ts +67 -0
  75. package/dist/scope-provenance/digest.js +188 -0
  76. package/dist/scope-provenance/evaluate.d.ts +82 -0
  77. package/dist/scope-provenance/evaluate.js +528 -0
  78. package/dist/scope-provenance/index.d.ts +6 -0
  79. package/dist/scope-provenance/index.js +6 -0
  80. package/dist/session/compact-ritual.d.ts +96 -0
  81. package/dist/session/compact-ritual.js +237 -0
  82. package/dist/session/compact-ritual.spec.d.ts +2 -0
  83. package/dist/session/compact-ritual.spec.js +21 -0
  84. package/dist/session/index.d.ts +2 -0
  85. package/dist/session/index.js +2 -0
  86. package/dist/session/openclaw-soft-rebind-deposit.d.ts +46 -0
  87. package/dist/session/openclaw-soft-rebind-deposit.js +165 -0
  88. package/dist/swarm/routing.d.ts +4 -2
  89. package/dist/swarm/routing.js +26 -4
  90. package/dist/test-boundary/evaluate.d.ts +54 -0
  91. package/dist/test-boundary/evaluate.js +368 -0
  92. package/dist/test-boundary/index.d.ts +6 -0
  93. package/dist/test-boundary/index.js +6 -0
  94. package/dist/test-boundary/policy.d.ts +52 -0
  95. package/dist/test-boundary/policy.js +182 -0
  96. package/dist/triage/actions/index.js +62 -2
  97. package/dist/triage/actions/types.d.ts +8 -1
  98. package/dist/triage/author-filter.d.ts +51 -0
  99. package/dist/triage/author-filter.js +152 -0
  100. package/dist/triage/bootstrap/gitignore.d.ts +1 -1
  101. package/dist/triage/bootstrap/gitignore.js +15 -1
  102. package/dist/triage/classify/index.d.ts +2 -2
  103. package/dist/triage/classify/index.js +2 -2
  104. package/dist/triage/classify/label-mirror.d.ts +68 -5
  105. package/dist/triage/classify/label-mirror.js +261 -31
  106. package/dist/triage/help/registry-data.d.ts +49 -38
  107. package/dist/triage/help/registry-data.js +115 -40
  108. package/dist/triage/index.d.ts +1 -0
  109. package/dist/triage/index.js +1 -0
  110. package/dist/triage/queue/index.d.ts +1 -0
  111. package/dist/triage/queue/index.js +1 -0
  112. package/dist/triage/queue/render.d.ts +2 -0
  113. package/dist/triage/queue/render.js +6 -0
  114. package/dist/vbrief-activate/activate.js +22 -6
  115. package/dist/xbrief/styles.js +33 -17
  116. package/package.json +11 -3
@@ -0,0 +1,188 @@
1
+ /**
2
+ * Approved-scope digest helpers (#3145).
3
+ *
4
+ * At activation/approval time, record an immutable digest of the human-approved
5
+ * file_scope. PR verification compares the live active xBRIEF against this
6
+ * baseline so same-PR xBRIEF edits cannot self-authorize new paths.
7
+ */
8
+ import { createHash } from "node:crypto";
9
+ import { existsSync, mkdirSync, readdirSync, readFileSync } from "node:fs";
10
+ import { basename, join, resolve } from "node:path";
11
+ import { containedWrite } from "../fs/contained-write.js";
12
+ export const APPROVED_SCOPE_DIR = ".deft/approved-scope";
13
+ /** Normalize and sort scope paths for stable digests. */
14
+ export function normalizeFileScope(paths) {
15
+ const out = new Set();
16
+ for (const p of paths) {
17
+ if (typeof p !== "string")
18
+ continue;
19
+ const n = p.trim().replace(/\\/g, "/").replace(/^\.\//, "");
20
+ if (n.length > 0)
21
+ out.add(n);
22
+ }
23
+ return [...out].sort((a, b) => a.localeCompare(b));
24
+ }
25
+ /** sha256 hex of normalized file_scope (one path per line). */
26
+ export function computeFileScopeDigest(paths) {
27
+ const normalized = normalizeFileScope(paths);
28
+ const payload = `${normalized.join("\n")}\n`;
29
+ return createHash("sha256").update(payload, "utf8").digest("hex");
30
+ }
31
+ /** sha256 hex of raw UTF-8 text. */
32
+ export function computeTextDigest(text) {
33
+ return createHash("sha256").update(text, "utf8").digest("hex");
34
+ }
35
+ /** Extract plan.metadata.swarm.file_scope from an xBRIEF payload. */
36
+ export function extractFileScope(payload) {
37
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
38
+ return [];
39
+ }
40
+ const plan = payload.plan;
41
+ if (plan === null || typeof plan !== "object" || Array.isArray(plan)) {
42
+ return [];
43
+ }
44
+ const metadata = plan.metadata;
45
+ if (metadata === null || typeof metadata !== "object" || Array.isArray(metadata)) {
46
+ return [];
47
+ }
48
+ const swarm = metadata.swarm;
49
+ if (swarm === null || typeof swarm !== "object" || Array.isArray(swarm)) {
50
+ return [];
51
+ }
52
+ const fileScope = swarm.file_scope;
53
+ if (!Array.isArray(fileScope))
54
+ return [];
55
+ return fileScope.filter((x) => typeof x === "string");
56
+ }
57
+ /** Extract plan.id from an xBRIEF payload. */
58
+ export function extractPlanId(payload) {
59
+ if (payload === null || typeof payload !== "object" || Array.isArray(payload)) {
60
+ return null;
61
+ }
62
+ const plan = payload.plan;
63
+ if (plan === null || typeof plan !== "object" || Array.isArray(plan)) {
64
+ return null;
65
+ }
66
+ const id = plan.id;
67
+ return typeof id === "string" && id.trim().length > 0 ? id.trim() : null;
68
+ }
69
+ export function approvedScopeDir(projectRoot) {
70
+ return join(resolve(projectRoot), ...APPROVED_SCOPE_DIR.split("/"));
71
+ }
72
+ export function approvedScopeRecordPath(projectRoot, planId) {
73
+ const safe = planId.replace(/[^a-zA-Z0-9._-]/g, "_");
74
+ return join(approvedScopeDir(projectRoot), `${safe}.json`);
75
+ }
76
+ /** Build an ApprovedScopeRecord from xBRIEF payload + metadata. */
77
+ export function buildApprovedScopeRecord(input) {
78
+ const planId = extractPlanId(input.payload) ?? basename(input.xbriefRelPath, ".xbrief.json");
79
+ const fileScope = normalizeFileScope(extractFileScope(input.payload));
80
+ const fileScopeDigest = computeFileScopeDigest(fileScope);
81
+ const approvedAt = input.approvedAt ?? new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
82
+ const record = {
83
+ schemaVersion: 1,
84
+ xbriefRelPath: input.xbriefRelPath.replace(/\\/g, "/"),
85
+ planId,
86
+ approvedAt,
87
+ fileScope,
88
+ fileScopeDigest,
89
+ };
90
+ if (input.humanApproval !== undefined) {
91
+ return {
92
+ ...record,
93
+ humanApproval: input.humanApproval,
94
+ xbriefBodyDigest: input.xbriefRawText !== undefined ? computeTextDigest(input.xbriefRawText) : undefined,
95
+ };
96
+ }
97
+ if (input.xbriefRawText !== undefined) {
98
+ return { ...record, xbriefBodyDigest: computeTextDigest(input.xbriefRawText) };
99
+ }
100
+ return record;
101
+ }
102
+ /** Persist approved-scope record (activation / renewed human approval). */
103
+ export function writeApprovedScopeRecord(projectRoot, record) {
104
+ const dir = approvedScopeDir(projectRoot);
105
+ mkdirSync(dir, { recursive: true });
106
+ const path = approvedScopeRecordPath(projectRoot, record.planId);
107
+ containedWrite({
108
+ root: resolve(projectRoot),
109
+ target: path,
110
+ data: `${JSON.stringify(record, null, 2)}\n`,
111
+ mode: "replace",
112
+ });
113
+ return path;
114
+ }
115
+ /** Load one approved-scope record by plan id, or null if missing. */
116
+ export function readApprovedScopeRecord(projectRoot, planId) {
117
+ const path = approvedScopeRecordPath(projectRoot, planId);
118
+ if (!existsSync(path))
119
+ return null;
120
+ try {
121
+ const raw = JSON.parse(readFileSync(path, "utf8"));
122
+ if (raw === null || typeof raw !== "object" || Array.isArray(raw))
123
+ return null;
124
+ const rec = raw;
125
+ if (typeof rec.fileScopeDigest !== "string" || !Array.isArray(rec.fileScope))
126
+ return null;
127
+ return raw;
128
+ }
129
+ catch {
130
+ return null;
131
+ }
132
+ }
133
+ /** Load all approved-scope records under `.deft/approved-scope/`. */
134
+ export function listApprovedScopeRecords(projectRoot) {
135
+ const dir = approvedScopeDir(projectRoot);
136
+ if (!existsSync(dir))
137
+ return [];
138
+ const out = [];
139
+ for (const name of readdirSync(dir)) {
140
+ if (!name.endsWith(".json"))
141
+ continue;
142
+ try {
143
+ const raw = JSON.parse(readFileSync(join(dir, name), "utf8"));
144
+ if (raw !== null && typeof raw === "object" && !Array.isArray(raw)) {
145
+ const rec = raw;
146
+ if (typeof rec.fileScopeDigest === "string")
147
+ out.push(rec);
148
+ }
149
+ }
150
+ catch {
151
+ // skip malformed
152
+ }
153
+ }
154
+ return out;
155
+ }
156
+ /**
157
+ * Paths present in `current` but not in `approved` (normalized).
158
+ * These are the expansion set that cannot self-authorize.
159
+ */
160
+ export function scopeExpansion(approved, current) {
161
+ const base = new Set(normalizeFileScope(approved));
162
+ return normalizeFileScope(current).filter((p) => !base.has(p));
163
+ }
164
+ /** True when actor/kind look like human provenance (mirrors authz subset). */
165
+ export function isHumanApprovalStamp(stamp) {
166
+ if (stamp === null || stamp === undefined)
167
+ return false;
168
+ const kind = (stamp.kind ?? "").trim().toLowerCase();
169
+ const actor = (stamp.actor ?? "").trim().toLowerCase();
170
+ if (kind.length === 0 || actor.length === 0)
171
+ return false;
172
+ if (actor === "agent" || actor.startsWith("agent:") || actor === "self")
173
+ return false;
174
+ if (kind === "agent" || kind === "self" || kind === "xbrief" || kind === "dispatch")
175
+ return false;
176
+ // Accept common human kinds from #2944 surface
177
+ const humanKinds = new Set([
178
+ "cli",
179
+ "operator",
180
+ "human",
181
+ "user",
182
+ "github-user",
183
+ "interactive",
184
+ "renewed-approval",
185
+ ]);
186
+ return humanKinds.has(kind) || kind.startsWith("human");
187
+ }
188
+ //# sourceMappingURL=digest.js.map
@@ -0,0 +1,82 @@
1
+ /**
2
+ * verify:scope-provenance evaluation (#3145).
3
+ *
4
+ * Detects same-PR active xBRIEF expansion that would self-authorize new
5
+ * implementation paths. Requires renewed human approval (updated digest record
6
+ * with human stamp); the modified xBRIEF alone is never sufficient.
7
+ *
8
+ * Three-state exit: 0 clean / 1 self-authorization / 2 config.
9
+ * Migration: missing approved-scope records → warn (exit 0) unless --enforce.
10
+ */
11
+ import { type ApprovedScopeRecord } from "./digest.js";
12
+ export type ScopeProvenanceViolationKind = "self-authorizing-scope-expansion" | "active-xbrief-modified-without-digest" | "digest-mismatch-without-renewal";
13
+ export interface ScopeProvenanceFinding {
14
+ readonly xbriefRelPath: string;
15
+ readonly planId: string;
16
+ readonly kind: ScopeProvenanceViolationKind;
17
+ readonly expandedPaths: readonly string[];
18
+ readonly detail: string;
19
+ readonly remediation: string;
20
+ }
21
+ export interface ScopeProvenanceResult {
22
+ readonly exitCode: 0 | 1 | 2;
23
+ readonly findings: readonly ScopeProvenanceFinding[];
24
+ readonly message: string;
25
+ }
26
+ export interface ScopeProvenanceOptions {
27
+ /**
28
+ * When true, missing digests on modified active xBRIEFs fail closed.
29
+ * Default false = migration warn path.
30
+ */
31
+ readonly enforce?: boolean;
32
+ /** Base ref for diff (default HEAD). */
33
+ readonly baseRef?: string;
34
+ /** Inject changed files (repo-relative POSIX). */
35
+ readonly changedFiles?: readonly string[];
36
+ /** Inject active xBRIEF payloads: relPath -> raw JSON text. */
37
+ readonly activeXbriefs?: ReadonlyMap<string, string>;
38
+ /** Inject approved records (skips disk). */
39
+ readonly approvedRecords?: readonly ApprovedScopeRecord[];
40
+ /** Inject renewed-approval stamps keyed by planId (test seam). */
41
+ readonly renewedApprovals?: ReadonlyMap<string, ApprovedScopeRecord["humanApproval"]>;
42
+ }
43
+ /**
44
+ * Resolve a PR-aware base ref. Bare `HEAD` only shows uncommitted changes, so
45
+ * CI/PR checkouts would miss committed active-xBRIEF expansion. Prefer
46
+ * origin/master (or main) for merge-base comparison.
47
+ * Returns null when no merge-base candidate exists (caller fails closed).
48
+ */
49
+ export declare function resolveDefaultBaseRef(projectRoot: string): string | null;
50
+ /**
51
+ * Normalize git --name-only paths (including C-quoted paths).
52
+ * Decode C-quotes BEFORE converting remaining backslashes to `/` so escape
53
+ * sequences are not destroyed. Consecutive octal escapes decode as UTF-8
54
+ * bytes (e.g. \\303\\251 → é) so Unicode xBRIEF paths match filesystem names.
55
+ */
56
+ export declare function unquoteGitPath(raw: string): string;
57
+ /** Normalize repo-relative paths for exact set membership (always POSIX separators). */
58
+ export declare function normalizeRepoRelPath(p: string): string;
59
+ /**
60
+ * True when `rel` appears in the changed-path set under any normalization that
61
+ * Git C-quoting / slash folding may produce (Greptile conf=4 residual).
62
+ */
63
+ export declare function changedSetHasPath(changedSet: ReadonlySet<string>, rel: string): boolean;
64
+ /**
65
+ * Pure evaluation of one active xBRIEF against its approved baseline.
66
+ * Exported for unit tests without git.
67
+ */
68
+ export declare function evaluateOneScopeProvenance(input: {
69
+ readonly xbriefRelPath: string;
70
+ readonly currentPayload: unknown;
71
+ readonly approved: ApprovedScopeRecord | null;
72
+ readonly xbriefModifiedInChangeSet: boolean;
73
+ readonly enforce: boolean;
74
+ readonly renewedHumanApproval?: ApprovedScopeRecord["humanApproval"] | null;
75
+ }): ScopeProvenanceFinding | null;
76
+ /**
77
+ * Evaluate scope provenance for a project (or pure injected seams).
78
+ */
79
+ export declare function evaluateScopeProvenance(projectRoot: string, options?: ScopeProvenanceOptions): ScopeProvenanceResult;
80
+ /** Re-export builder for activation hook callers. */
81
+ export { buildApprovedScopeRecord, writeApprovedScopeRecord } from "./digest.js";
82
+ //# sourceMappingURL=evaluate.d.ts.map