hub-proposal-policy.mjs
123 lines 4.1 KB
Raw
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor ⚠ breaking 1 day ago
1 /**
2 * Proposal LLM + evaluation policy for self-hosted Hub (data/hub_proposal_policy.json).
3 * Precedence per field: explicit env (1/true or 0/false) overrides file; else file; else default false.
4 *
5 * Env keys:
6 * - HUB_PROPOSAL_EVALUATION_REQUIRED
7 * - KNOWTATION_HUB_PROPOSAL_REVIEW_HINTS
8 * - KNOWTATION_HUB_PROPOSAL_ENRICH
9 */
10
11 import fs from 'fs';
12 import path from 'path';
13
14 const POLICY_FILE = 'hub_proposal_policy.json';
15
16 /** @param {unknown} v */
17 function envTriState(v) {
18 if (v === '1' || v === 'true') return true;
19 if (v === '0' || v === 'false') return false;
20 return null;
21 }
22
23 /**
24 * @param {string} dataDir
25 * @returns {{ proposal_evaluation_required?: boolean, review_hints_enabled?: boolean, enrich_enabled?: boolean }}
26 */
27 export function readProposalPolicyFile(dataDir) {
28 const fp = path.join(dataDir, POLICY_FILE);
29 if (!fs.existsSync(fp)) return {};
30 try {
31 const j = JSON.parse(fs.readFileSync(fp, 'utf8'));
32 if (!j || typeof j !== 'object') return {};
33 const out = {};
34 if (typeof j.proposal_evaluation_required === 'boolean') out.proposal_evaluation_required = j.proposal_evaluation_required;
35 if (typeof j.review_hints_enabled === 'boolean') out.review_hints_enabled = j.review_hints_enabled;
36 if (typeof j.enrich_enabled === 'boolean') out.enrich_enabled = j.enrich_enabled;
37 return out;
38 } catch {
39 return {};
40 }
41 }
42
43 /**
44 * @param {string} dataDir
45 * @returns {boolean}
46 */
47 export function getProposalEvaluationRequired(dataDir) {
48 const fromEnv = envTriState(process.env.HUB_PROPOSAL_EVALUATION_REQUIRED);
49 if (fromEnv !== null) return fromEnv;
50 const file = readProposalPolicyFile(dataDir);
51 return file.proposal_evaluation_required === true;
52 }
53
54 /**
55 * @param {string} dataDir
56 * @returns {boolean}
57 */
58 export function getProposalReviewHintsEnabled(dataDir) {
59 const fromEnv = envTriState(process.env.KNOWTATION_HUB_PROPOSAL_REVIEW_HINTS);
60 if (fromEnv !== null) return fromEnv;
61 return readProposalPolicyFile(dataDir).review_hints_enabled === true;
62 }
63
64 /**
65 * @param {string} dataDir
66 * @returns {boolean}
67 */
68 export function getProposalEnrichEnabled(dataDir) {
69 const fromEnv = envTriState(process.env.KNOWTATION_HUB_PROPOSAL_ENRICH);
70 if (fromEnv !== null) return fromEnv;
71 return readProposalPolicyFile(dataDir).enrich_enabled === true;
72 }
73
74 /**
75 * When true, that field is fixed by env and must not be edited via Settings file API.
76 * @returns {{
77 * proposal_evaluation_required: boolean,
78 * review_hints_enabled: boolean,
79 * enrich_enabled: boolean,
80 * }}
81 */
82 export function proposalPolicyEnvLocked() {
83 return {
84 proposal_evaluation_required: envTriState(process.env.HUB_PROPOSAL_EVALUATION_REQUIRED) !== null,
85 review_hints_enabled: envTriState(process.env.KNOWTATION_HUB_PROPOSAL_REVIEW_HINTS) !== null,
86 enrich_enabled: envTriState(process.env.KNOWTATION_HUB_PROPOSAL_ENRICH) !== null,
87 };
88 }
89
90 /**
91 * Merge partial policy into hub_proposal_policy.json (only keys present in partial; skips env-locked keys).
92 * @param {string} dataDir
93 * @param {{
94 * proposal_evaluation_required?: boolean,
95 * review_hints_enabled?: boolean,
96 * enrich_enabled?: boolean,
97 * }} partial
98 */
99 export function writeProposalPolicyMerge(dataDir, partial) {
100 const locks = proposalPolicyEnvLocked();
101 const fp = path.join(dataDir, POLICY_FILE);
102 let existing = {};
103 if (fs.existsSync(fp)) {
104 try {
105 const j = JSON.parse(fs.readFileSync(fp, 'utf8'));
106 if (j && typeof j === 'object') existing = j;
107 } catch {
108 existing = {};
109 }
110 }
111 const next = { ...existing };
112 if (partial.proposal_evaluation_required !== undefined && !locks.proposal_evaluation_required) {
113 next.proposal_evaluation_required = Boolean(partial.proposal_evaluation_required);
114 }
115 if (partial.review_hints_enabled !== undefined && !locks.review_hints_enabled) {
116 next.review_hints_enabled = Boolean(partial.review_hints_enabled);
117 }
118 if (partial.enrich_enabled !== undefined && !locks.enrich_enabled) {
119 next.enrich_enabled = Boolean(partial.enrich_enabled);
120 }
121 fs.mkdirSync(dataDir, { recursive: true });
122 fs.writeFileSync(fp, JSON.stringify(next, null, 2), 'utf8');
123 }
File History 2 commits
sha256:65ccb454656ea5acdea0a10e559b78bcde1eb6ff753ecc2911bc99d1c3d7cadd feat(calendar): enforce agent context tiers in retrieval AP… Human minor 1 day ago
sha256:9103f98c89257ed2b01c237cea895dabb3e85ea337dccb1161c175e4422355b6 docs: accept Calendar Events v0 spec with Phase 0 security … Human 2 days ago