@deftai/directive-core 0.71.1 → 0.72.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/events/attribution-enrichment.d.ts +35 -0
- package/dist/events/attribution-enrichment.js +74 -0
- package/dist/events/attribution-ledger.js +6 -1
- package/dist/policy/index.js +3 -3
- package/dist/policy/value-feedback-autoenable.d.ts +33 -0
- package/dist/policy/value-feedback-autoenable.js +67 -0
- package/dist/policy/value-feedback.d.ts +30 -5
- package/dist/policy/value-feedback.js +52 -34
- package/package.json +3 -3
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/** Record-shape version for attribution ledger entries (#2376). Bump on shape changes. */
|
|
2
|
+
export declare const ATTRIBUTION_SCHEMA_VERSION: 1;
|
|
3
|
+
/** Project-local, gitignored install-identity file. */
|
|
4
|
+
export declare const INSTALL_ID_REL: string;
|
|
5
|
+
/** Identity + provenance fields stamped onto every attribution record (#2376). */
|
|
6
|
+
export interface AttributionEnrichment {
|
|
7
|
+
/** Normalized `owner/name` of the origin remote, or null when unavailable. */
|
|
8
|
+
readonly repo: string | null;
|
|
9
|
+
/** directive engine version at emit time. */
|
|
10
|
+
readonly directive_version: string;
|
|
11
|
+
/** Stable per-checkout uuid, or null when it cannot be read/created. */
|
|
12
|
+
readonly install_id: string | null;
|
|
13
|
+
/** Attribution record-shape version. */
|
|
14
|
+
readonly schema_version: number;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* Read-or-create a stable per-checkout install id in `.deft-cache/install-id`.
|
|
18
|
+
* Best-effort: returns null (never throws) if the file cannot be read or written.
|
|
19
|
+
*/
|
|
20
|
+
export declare function resolveInstallId(projectRoot: string): string | null;
|
|
21
|
+
export interface BuildAttributionEnrichmentOptions {
|
|
22
|
+
/** Test seam for origin-repo resolution; defaults to `inferRepoFromGit`. */
|
|
23
|
+
readonly repoResolver?: (projectRoot: string) => string | null;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Build the identity/provenance enrichment stamped on attribution records (#2376).
|
|
27
|
+
*
|
|
28
|
+
* Memoized per `projectRoot` (identity/version/repo are stable within a session)
|
|
29
|
+
* so a caller that emits many signals in one run does not spawn a git process and
|
|
30
|
+
* open the install-id file per event -- mirrors `detectOriginOrg`'s cache to keep
|
|
31
|
+
* this off the hot emit path (#2377 review). A custom `repoResolver` bypasses the
|
|
32
|
+
* cache so tests stay deterministic. Every field degrades gracefully; never throws.
|
|
33
|
+
*/
|
|
34
|
+
export declare function buildAttributionEnrichment(projectRoot: string, options?: BuildAttributionEnrichmentOptions): AttributionEnrichment;
|
|
35
|
+
//# sourceMappingURL=attribution-enrichment.d.ts.map
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { randomUUID } from "node:crypto";
|
|
2
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { dirname, join, resolve } from "node:path";
|
|
4
|
+
import { readCorePackageVersion } from "../engine-version.js";
|
|
5
|
+
import { inferRepoFromGit } from "../triage/queue/repo.js";
|
|
6
|
+
/** Record-shape version for attribution ledger entries (#2376). Bump on shape changes. */
|
|
7
|
+
export const ATTRIBUTION_SCHEMA_VERSION = 1;
|
|
8
|
+
/** Project-local, gitignored install-identity file. */
|
|
9
|
+
export const INSTALL_ID_REL = join(".deft-cache", "install-id");
|
|
10
|
+
/**
|
|
11
|
+
* Read-or-create a stable per-checkout install id in `.deft-cache/install-id`.
|
|
12
|
+
* Best-effort: returns null (never throws) if the file cannot be read or written.
|
|
13
|
+
*/
|
|
14
|
+
export function resolveInstallId(projectRoot) {
|
|
15
|
+
const path = resolve(projectRoot, INSTALL_ID_REL);
|
|
16
|
+
try {
|
|
17
|
+
if (existsSync(path)) {
|
|
18
|
+
const existing = readFileSync(path, "utf8").trim();
|
|
19
|
+
if (existing.length > 0) {
|
|
20
|
+
return existing;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// fall through and try to (re)create
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const id = randomUUID();
|
|
29
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
30
|
+
writeFileSync(path, `${id}\n`, "utf8");
|
|
31
|
+
return id;
|
|
32
|
+
}
|
|
33
|
+
catch {
|
|
34
|
+
return null;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
const enrichmentCache = new Map();
|
|
38
|
+
/**
|
|
39
|
+
* Build the identity/provenance enrichment stamped on attribution records (#2376).
|
|
40
|
+
*
|
|
41
|
+
* Memoized per `projectRoot` (identity/version/repo are stable within a session)
|
|
42
|
+
* so a caller that emits many signals in one run does not spawn a git process and
|
|
43
|
+
* open the install-id file per event -- mirrors `detectOriginOrg`'s cache to keep
|
|
44
|
+
* this off the hot emit path (#2377 review). A custom `repoResolver` bypasses the
|
|
45
|
+
* cache so tests stay deterministic. Every field degrades gracefully; never throws.
|
|
46
|
+
*/
|
|
47
|
+
export function buildAttributionEnrichment(projectRoot, options = {}) {
|
|
48
|
+
const useCache = options.repoResolver === undefined;
|
|
49
|
+
if (useCache) {
|
|
50
|
+
const cached = enrichmentCache.get(projectRoot);
|
|
51
|
+
if (cached !== undefined) {
|
|
52
|
+
return cached;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
const resolver = options.repoResolver ?? inferRepoFromGit;
|
|
56
|
+
let repo = null;
|
|
57
|
+
try {
|
|
58
|
+
repo = resolver(projectRoot);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
repo = null;
|
|
62
|
+
}
|
|
63
|
+
const enrichment = {
|
|
64
|
+
repo,
|
|
65
|
+
directive_version: readCorePackageVersion(),
|
|
66
|
+
install_id: resolveInstallId(projectRoot),
|
|
67
|
+
schema_version: ATTRIBUTION_SCHEMA_VERSION,
|
|
68
|
+
};
|
|
69
|
+
if (useCache) {
|
|
70
|
+
enrichmentCache.set(projectRoot, enrichment);
|
|
71
|
+
}
|
|
72
|
+
return enrichment;
|
|
73
|
+
}
|
|
74
|
+
//# sourceMappingURL=attribution-enrichment.js.map
|
|
@@ -2,6 +2,7 @@ import { resolve } from "node:path";
|
|
|
2
2
|
import { DEFAULT_EVENT_LOG, emit } from "../lifecycle/events.js";
|
|
3
3
|
import { isValueFeedbackPathAllowed, resolveValueFeedback, } from "../policy/value-feedback.js";
|
|
4
4
|
import { ATTRIBUTION_EVENT_NAMES } from "./attribution-constants.js";
|
|
5
|
+
import { buildAttributionEnrichment } from "./attribution-enrichment.js";
|
|
5
6
|
export { ALL_ATTRIBUTION_EVENT_NAMES, ATTRIBUTION_EVENT_NAMES, ATTRIBUTION_REQUIRED_PAYLOAD, } from "./attribution-constants.js";
|
|
6
7
|
function resolveLedgerLogPath(projectRoot, logPath) {
|
|
7
8
|
if (logPath !== undefined && logPath !== null) {
|
|
@@ -28,9 +29,13 @@ export function emitAttributionSignal(name, payload, options) {
|
|
|
28
29
|
}
|
|
29
30
|
const signalClass = signalClassForEvent(name);
|
|
30
31
|
const logPath = resolveLedgerLogPath(options.projectRoot, options.logPath);
|
|
32
|
+
const enrichment = buildAttributionEnrichment(options.projectRoot);
|
|
33
|
+
// Authoritative fields (signal_class + provenance enrichment) are spread LAST
|
|
34
|
+
// so a caller payload can never silently shadow them (#2377 review).
|
|
31
35
|
return emit(name, {
|
|
32
|
-
signal_class: signalClass,
|
|
33
36
|
...payload,
|
|
37
|
+
signal_class: signalClass,
|
|
38
|
+
...enrichment,
|
|
34
39
|
}, { logPath });
|
|
35
40
|
}
|
|
36
41
|
catch {
|
package/dist/policy/index.js
CHANGED
|
@@ -215,8 +215,8 @@ function inspectSwarmSubagentBackend(data) {
|
|
|
215
215
|
source: "default-on-error",
|
|
216
216
|
};
|
|
217
217
|
}
|
|
218
|
-
function inspectValueFeedbackField(data) {
|
|
219
|
-
const field = inspectValueFeedback(data);
|
|
218
|
+
function inspectValueFeedbackField(data, projectRoot) {
|
|
219
|
+
const field = inspectValueFeedback(data, projectRoot);
|
|
220
220
|
return {
|
|
221
221
|
name: field.name,
|
|
222
222
|
current: field.current,
|
|
@@ -241,7 +241,7 @@ const REGISTERED_POLICIES = [
|
|
|
241
241
|
/** Walk registered inspectors and return one row per field (#1148). */
|
|
242
242
|
export function inspectAllPolicies(projectRoot) {
|
|
243
243
|
const [data] = loadProjectDefinition(projectRoot);
|
|
244
|
-
return REGISTERED_POLICIES.map((inspect) => inspect(data));
|
|
244
|
+
return REGISTERED_POLICIES.map((inspect) => inspect(data, projectRoot));
|
|
245
245
|
}
|
|
246
246
|
/** Look up a single registered field by canonical dotted-path name (or CLI alias). */
|
|
247
247
|
export function inspectOnePolicy(name, projectRoot) {
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Company-owned GitHub orgs whose repos auto-enable LOCAL value-feedback
|
|
3
|
+
* collection (#2376). Org membership is the consent for local, no-egress
|
|
4
|
+
* collection; network/upstream surfaces stay opt-in regardless.
|
|
5
|
+
*/
|
|
6
|
+
export declare const DEFAULT_AUTOENABLE_ORGS: readonly string[];
|
|
7
|
+
/** Optional comma-separated env override that EXTENDS the built-in trusted-org set. */
|
|
8
|
+
export declare const AUTOENABLE_ORGS_ENV = "DEFT_VALUE_AUTOENABLE_ORGS";
|
|
9
|
+
/** Built-in trusted orgs plus any from the env override, normalized to lowercase. */
|
|
10
|
+
export declare function resolveTrustedOrgs(env?: NodeJS.ProcessEnv): Set<string>;
|
|
11
|
+
/** Test/hygiene hook: drop the memoized origin-org lookups. */
|
|
12
|
+
export declare function clearOriginOrgCache(): void;
|
|
13
|
+
export interface OriginOrgOptions {
|
|
14
|
+
/** Test seam for origin-repo resolution; defaults to `inferRepoFromGit`. */
|
|
15
|
+
readonly repoResolver?: (projectRoot: string) => string | null;
|
|
16
|
+
/** Override caching; defaults to caching only when no custom resolver is supplied. */
|
|
17
|
+
readonly useCache?: boolean;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Lowercased owner of the origin remote for `projectRoot`, or null when there is
|
|
21
|
+
* no remote / not a git checkout. Memoized per projectRoot (the org does not
|
|
22
|
+
* change within a session) to keep the resolver off the hot emit path.
|
|
23
|
+
*/
|
|
24
|
+
export declare function detectOriginOrg(projectRoot: string, options?: OriginOrgOptions): string | null;
|
|
25
|
+
export interface OrgAutoEnableOptions extends OriginOrgOptions {
|
|
26
|
+
readonly env?: NodeJS.ProcessEnv;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* True when `projectRoot`'s origin org is in the trusted-org set (#2376).
|
|
30
|
+
* Fail-safe: any resolution failure (no remote, non-GitHub, git absent) is false.
|
|
31
|
+
*/
|
|
32
|
+
export declare function isTrustedOrgAutoEnable(projectRoot: string, options?: OrgAutoEnableOptions): boolean;
|
|
33
|
+
//# sourceMappingURL=value-feedback-autoenable.d.ts.map
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { inferRepoFromGit } from "../triage/queue/repo.js";
|
|
2
|
+
/**
|
|
3
|
+
* Company-owned GitHub orgs whose repos auto-enable LOCAL value-feedback
|
|
4
|
+
* collection (#2376). Org membership is the consent for local, no-egress
|
|
5
|
+
* collection; network/upstream surfaces stay opt-in regardless.
|
|
6
|
+
*/
|
|
7
|
+
export const DEFAULT_AUTOENABLE_ORGS = ["deftai"];
|
|
8
|
+
/** Optional comma-separated env override that EXTENDS the built-in trusted-org set. */
|
|
9
|
+
export const AUTOENABLE_ORGS_ENV = "DEFT_VALUE_AUTOENABLE_ORGS";
|
|
10
|
+
/** Built-in trusted orgs plus any from the env override, normalized to lowercase. */
|
|
11
|
+
export function resolveTrustedOrgs(env = process.env) {
|
|
12
|
+
const orgs = new Set(DEFAULT_AUTOENABLE_ORGS.map((o) => o.toLowerCase()));
|
|
13
|
+
const raw = env[AUTOENABLE_ORGS_ENV];
|
|
14
|
+
if (typeof raw === "string") {
|
|
15
|
+
for (const part of raw.split(",")) {
|
|
16
|
+
const trimmed = part.trim().toLowerCase();
|
|
17
|
+
if (trimmed.length > 0) {
|
|
18
|
+
orgs.add(trimmed);
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
return orgs;
|
|
23
|
+
}
|
|
24
|
+
const originOrgCache = new Map();
|
|
25
|
+
/** Test/hygiene hook: drop the memoized origin-org lookups. */
|
|
26
|
+
export function clearOriginOrgCache() {
|
|
27
|
+
originOrgCache.clear();
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Lowercased owner of the origin remote for `projectRoot`, or null when there is
|
|
31
|
+
* no remote / not a git checkout. Memoized per projectRoot (the org does not
|
|
32
|
+
* change within a session) to keep the resolver off the hot emit path.
|
|
33
|
+
*/
|
|
34
|
+
export function detectOriginOrg(projectRoot, options = {}) {
|
|
35
|
+
const useCache = options.useCache ?? options.repoResolver === undefined;
|
|
36
|
+
if (useCache && originOrgCache.has(projectRoot)) {
|
|
37
|
+
return originOrgCache.get(projectRoot) ?? null;
|
|
38
|
+
}
|
|
39
|
+
const resolver = options.repoResolver ?? inferRepoFromGit;
|
|
40
|
+
let org = null;
|
|
41
|
+
try {
|
|
42
|
+
const repo = resolver(projectRoot);
|
|
43
|
+
if (typeof repo === "string") {
|
|
44
|
+
const owner = repo.split("/")[0]?.trim().toLowerCase() ?? "";
|
|
45
|
+
org = owner.length > 0 ? owner : null;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
org = null;
|
|
50
|
+
}
|
|
51
|
+
if (useCache) {
|
|
52
|
+
originOrgCache.set(projectRoot, org);
|
|
53
|
+
}
|
|
54
|
+
return org;
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* True when `projectRoot`'s origin org is in the trusted-org set (#2376).
|
|
58
|
+
* Fail-safe: any resolution failure (no remote, non-GitHub, git absent) is false.
|
|
59
|
+
*/
|
|
60
|
+
export function isTrustedOrgAutoEnable(projectRoot, options = {}) {
|
|
61
|
+
const org = detectOriginOrg(projectRoot, options);
|
|
62
|
+
if (org === null) {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
return resolveTrustedOrgs(options.env).has(org);
|
|
66
|
+
}
|
|
67
|
+
//# sourceMappingURL=value-feedback-autoenable.js.map
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type OrgAutoEnableOptions } from "./value-feedback-autoenable.js";
|
|
1
2
|
/** Canonical registered policy field name (matches other FIELD_* dotted paths). */
|
|
2
3
|
export declare const FIELD_VALUE_FEEDBACK = "plan.policy.valueFeedback";
|
|
3
4
|
/** Short alias accepted by `policy:show --field=valueFeedback` (#1709). */
|
|
@@ -16,7 +17,7 @@ export interface ValueFeedbackConfig {
|
|
|
16
17
|
readonly sessionLine: boolean;
|
|
17
18
|
readonly upstreamPrompt: boolean;
|
|
18
19
|
}
|
|
19
|
-
export type ValueFeedbackSource = "typed" | "default" | "default-on-error";
|
|
20
|
+
export type ValueFeedbackSource = "typed" | "org-auto" | "default" | "default-on-error";
|
|
20
21
|
export interface ValueFeedbackResolved extends ValueFeedbackConfig {
|
|
21
22
|
readonly source: ValueFeedbackSource;
|
|
22
23
|
readonly error: string | null;
|
|
@@ -24,8 +25,20 @@ export interface ValueFeedbackResolved extends ValueFeedbackConfig {
|
|
|
24
25
|
export declare const VALUE_FEEDBACK_CAPABILITY_COST_DISCLOSURE: string;
|
|
25
26
|
/** Validate a `plan.policy.valueFeedback` payload. */
|
|
26
27
|
export declare function validateValueFeedback(value: unknown): string[];
|
|
27
|
-
|
|
28
|
-
|
|
28
|
+
export interface ResolveValueFeedbackOptions {
|
|
29
|
+
/** Test seam for origin-org auto-enable resolution (#2376). */
|
|
30
|
+
readonly autoEnable?: OrgAutoEnableOptions;
|
|
31
|
+
}
|
|
32
|
+
/**
|
|
33
|
+
* Resolve `plan.policy.valueFeedback` from PROJECT-DEFINITION (#1709).
|
|
34
|
+
*
|
|
35
|
+
* Precedence (#2376): an explicit typed `valueFeedback` block always wins
|
|
36
|
+
* (including `enabled: false`). Only when the typed flag is ABSENT does the
|
|
37
|
+
* trusted-org auto-enable layer apply -- for company-owned (deftai) repos it
|
|
38
|
+
* turns LOCAL emit + session readback ON while leaving network/upstream OFF.
|
|
39
|
+
* Any other repo (or no origin remote) stays OFF.
|
|
40
|
+
*/
|
|
41
|
+
export declare function resolveValueFeedback(projectRoot: string, options?: ResolveValueFeedbackOptions): ValueFeedbackResolved;
|
|
29
42
|
/** Master gate: when `enabled` is false, every downstream path is rejected. */
|
|
30
43
|
export declare function isValueFeedbackPathAllowed(path: ValueFeedbackSubFlag, policy: ValueFeedbackResolved): boolean;
|
|
31
44
|
/** Resolved per-path gate booleans for policy:show and enable status output. */
|
|
@@ -38,8 +51,20 @@ export interface ValueFeedbackPolicyField {
|
|
|
38
51
|
readonly default: ValueFeedbackConfig;
|
|
39
52
|
readonly source: string;
|
|
40
53
|
}
|
|
41
|
-
|
|
42
|
-
|
|
54
|
+
export interface InspectValueFeedbackOptions {
|
|
55
|
+
/** Test seam for origin-org auto-enable resolution (#2376). */
|
|
56
|
+
readonly autoEnable?: OrgAutoEnableOptions;
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Inspector row for `policy:show --field=valueFeedback`.
|
|
60
|
+
*
|
|
61
|
+
* When `projectRoot` is supplied this MUST mirror {@link resolveValueFeedback}'s
|
|
62
|
+
* precedence exactly (#2377 review): with no explicit typed block, a trusted-org
|
|
63
|
+
* checkout resolves to `org-auto`/ON so `policy:show` never reports OFF while the
|
|
64
|
+
* ledger is actively collecting. Omitting `projectRoot` keeps the legacy
|
|
65
|
+
* data-only behavior for callers that only need the field name/shape.
|
|
66
|
+
*/
|
|
67
|
+
export declare function inspectValueFeedback(data: Record<string, unknown> | null, projectRoot?: string, options?: InspectValueFeedbackOptions): ValueFeedbackPolicyField;
|
|
43
68
|
export interface EnableValueFeedbackOptions {
|
|
44
69
|
readonly confirm: boolean;
|
|
45
70
|
readonly actor?: string;
|
|
@@ -2,6 +2,7 @@ import { readFileSync } from "node:fs";
|
|
|
2
2
|
import { atomicWriteProjectDefinition, projectDefinitionMutationLock, } from "../vbrief-build/project-definition-io.js";
|
|
3
3
|
import { migrateLegacyPolicyKey, PLAN_POLICY_KEY, readPlanPolicy } from "./plan-extensions.js";
|
|
4
4
|
import { appendAuditLog, loadProjectDefinition, projectDefinitionPath } from "./resolve.js";
|
|
5
|
+
import { isTrustedOrgAutoEnable } from "./value-feedback-autoenable.js";
|
|
5
6
|
/** Canonical registered policy field name (matches other FIELD_* dotted paths). */
|
|
6
7
|
export const FIELD_VALUE_FEEDBACK = "plan.policy.valueFeedback";
|
|
7
8
|
/** Short alias accepted by `policy:show --field=valueFeedback` (#1709). */
|
|
@@ -33,6 +34,20 @@ function defaultResolved(source, error = null) {
|
|
|
33
34
|
error,
|
|
34
35
|
};
|
|
35
36
|
}
|
|
37
|
+
/**
|
|
38
|
+
* Trusted-org auto-enable resolution (#2376): LOCAL emit + session readback ON,
|
|
39
|
+
* network/upstream OFF. Applies only when the typed flag is absent.
|
|
40
|
+
*/
|
|
41
|
+
function orgAutoResolved() {
|
|
42
|
+
return {
|
|
43
|
+
enabled: true,
|
|
44
|
+
emitEvents: VALUE_FEEDBACK_SUBFLAG_DEFAULTS_WHEN_ENABLED.emitEvents,
|
|
45
|
+
sessionLine: VALUE_FEEDBACK_SUBFLAG_DEFAULTS_WHEN_ENABLED.sessionLine,
|
|
46
|
+
upstreamPrompt: false,
|
|
47
|
+
source: "org-auto",
|
|
48
|
+
error: null,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
36
51
|
function readSubFlag(block, key, masterEnabled) {
|
|
37
52
|
if (!masterEnabled) {
|
|
38
53
|
return false;
|
|
@@ -88,8 +103,16 @@ function resolveFromPolicyBlock(raw) {
|
|
|
88
103
|
error: null,
|
|
89
104
|
};
|
|
90
105
|
}
|
|
91
|
-
/**
|
|
92
|
-
|
|
106
|
+
/**
|
|
107
|
+
* Resolve `plan.policy.valueFeedback` from PROJECT-DEFINITION (#1709).
|
|
108
|
+
*
|
|
109
|
+
* Precedence (#2376): an explicit typed `valueFeedback` block always wins
|
|
110
|
+
* (including `enabled: false`). Only when the typed flag is ABSENT does the
|
|
111
|
+
* trusted-org auto-enable layer apply -- for company-owned (deftai) repos it
|
|
112
|
+
* turns LOCAL emit + session readback ON while leaving network/upstream OFF.
|
|
113
|
+
* Any other repo (or no origin remote) stays OFF.
|
|
114
|
+
*/
|
|
115
|
+
export function resolveValueFeedback(projectRoot, options = {}) {
|
|
93
116
|
const [data, err] = loadProjectDefinition(projectRoot);
|
|
94
117
|
if (data === null) {
|
|
95
118
|
return defaultResolved("default-on-error", err);
|
|
@@ -99,6 +122,9 @@ export function resolveValueFeedback(projectRoot) {
|
|
|
99
122
|
policyBlock === null ||
|
|
100
123
|
Array.isArray(policyBlock) ||
|
|
101
124
|
!("valueFeedback" in policyBlock)) {
|
|
125
|
+
if (isTrustedOrgAutoEnable(projectRoot, options.autoEnable)) {
|
|
126
|
+
return orgAutoResolved();
|
|
127
|
+
}
|
|
102
128
|
return defaultResolved("default");
|
|
103
129
|
}
|
|
104
130
|
return resolveFromPolicyBlock(policyBlock.valueFeedback);
|
|
@@ -126,57 +152,49 @@ export function formatValueFeedbackStatusLine(policy) {
|
|
|
126
152
|
`sessionLine=${String(policy.sessionLine)} (path=${String(gates.sessionLine)}) ` +
|
|
127
153
|
`upstreamPrompt=${String(policy.upstreamPrompt)} (path=${String(gates.upstreamPrompt)}).`);
|
|
128
154
|
}
|
|
129
|
-
function
|
|
130
|
-
const defaults = defaultResolved("default");
|
|
131
|
-
return {
|
|
132
|
-
enabled: defaults.enabled,
|
|
133
|
-
emitEvents: defaults.emitEvents,
|
|
134
|
-
sessionLine: defaults.sessionLine,
|
|
135
|
-
upstreamPrompt: defaults.upstreamPrompt,
|
|
136
|
-
};
|
|
137
|
-
}
|
|
138
|
-
function buildDefaultInspectorField() {
|
|
155
|
+
function fieldFromResolved(resolved) {
|
|
139
156
|
return {
|
|
140
157
|
name: FIELD_VALUE_FEEDBACK,
|
|
141
|
-
current:
|
|
158
|
+
current: {
|
|
159
|
+
enabled: resolved.enabled,
|
|
160
|
+
emitEvents: resolved.emitEvents,
|
|
161
|
+
sessionLine: resolved.sessionLine,
|
|
162
|
+
upstreamPrompt: resolved.upstreamPrompt,
|
|
163
|
+
},
|
|
142
164
|
default: {
|
|
143
165
|
enabled: DEFAULT_VALUE_FEEDBACK_ENABLED,
|
|
144
166
|
emitEvents: false,
|
|
145
167
|
sessionLine: false,
|
|
146
168
|
upstreamPrompt: false,
|
|
147
169
|
},
|
|
148
|
-
source:
|
|
170
|
+
source: resolved.source,
|
|
149
171
|
};
|
|
150
172
|
}
|
|
151
|
-
/**
|
|
152
|
-
|
|
173
|
+
/**
|
|
174
|
+
* Inspector row for `policy:show --field=valueFeedback`.
|
|
175
|
+
*
|
|
176
|
+
* When `projectRoot` is supplied this MUST mirror {@link resolveValueFeedback}'s
|
|
177
|
+
* precedence exactly (#2377 review): with no explicit typed block, a trusted-org
|
|
178
|
+
* checkout resolves to `org-auto`/ON so `policy:show` never reports OFF while the
|
|
179
|
+
* ledger is actively collecting. Omitting `projectRoot` keeps the legacy
|
|
180
|
+
* data-only behavior for callers that only need the field name/shape.
|
|
181
|
+
*/
|
|
182
|
+
export function inspectValueFeedback(data, projectRoot, options = {}) {
|
|
153
183
|
if (data === null) {
|
|
154
|
-
return
|
|
184
|
+
return fieldFromResolved(defaultResolved("default"));
|
|
155
185
|
}
|
|
156
186
|
const policyBlock = readPlanPolicy(data.plan);
|
|
157
187
|
if (typeof policyBlock !== "object" ||
|
|
158
188
|
policyBlock === null ||
|
|
159
189
|
Array.isArray(policyBlock) ||
|
|
160
190
|
!("valueFeedback" in policyBlock)) {
|
|
161
|
-
|
|
191
|
+
if (projectRoot !== undefined && isTrustedOrgAutoEnable(projectRoot, options.autoEnable)) {
|
|
192
|
+
return fieldFromResolved(orgAutoResolved());
|
|
193
|
+
}
|
|
194
|
+
return fieldFromResolved(defaultResolved("default"));
|
|
162
195
|
}
|
|
163
196
|
const resolved = resolveFromPolicyBlock(policyBlock.valueFeedback);
|
|
164
|
-
return
|
|
165
|
-
name: FIELD_VALUE_FEEDBACK,
|
|
166
|
-
current: {
|
|
167
|
-
enabled: resolved.enabled,
|
|
168
|
-
emitEvents: resolved.emitEvents,
|
|
169
|
-
sessionLine: resolved.sessionLine,
|
|
170
|
-
upstreamPrompt: resolved.upstreamPrompt,
|
|
171
|
-
},
|
|
172
|
-
default: {
|
|
173
|
-
enabled: DEFAULT_VALUE_FEEDBACK_ENABLED,
|
|
174
|
-
emitEvents: false,
|
|
175
|
-
sessionLine: false,
|
|
176
|
-
upstreamPrompt: false,
|
|
177
|
-
},
|
|
178
|
-
source: resolved.source,
|
|
179
|
-
};
|
|
197
|
+
return fieldFromResolved(resolved);
|
|
180
198
|
}
|
|
181
199
|
/** Persist `valueFeedback.enabled=true` after capability-cost disclosure (#1709). */
|
|
182
200
|
export function enableValueFeedback(projectRoot, options) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@deftai/directive-core",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.72.0",
|
|
4
4
|
"description": "TypeScript engine core for the Directive framework.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.js",
|
|
@@ -281,8 +281,8 @@
|
|
|
281
281
|
"provenance": true
|
|
282
282
|
},
|
|
283
283
|
"dependencies": {
|
|
284
|
-
"@deftai/directive-content": "^0.
|
|
285
|
-
"@deftai/directive-types": "^0.
|
|
284
|
+
"@deftai/directive-content": "^0.72.0",
|
|
285
|
+
"@deftai/directive-types": "^0.72.0",
|
|
286
286
|
"archiver": "^8.0.0"
|
|
287
287
|
},
|
|
288
288
|
"scripts": {
|