ultimate-pi 0.17.0 → 0.18.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/.agents/skills/harness-context/SKILL.md +13 -6
- package/.agents/skills/harness-debate-plan/SKILL.md +37 -20
- package/.agents/skills/harness-eval/SKILL.md +6 -21
- package/.agents/skills/harness-governor/SKILL.md +4 -3
- package/.agents/skills/harness-orchestration/SKILL.md +39 -51
- package/.agents/skills/harness-plan/SKILL.md +23 -12
- package/.agents/skills/harness-review/SKILL.md +52 -0
- package/.agents/skills/harness-sentrux-setup/SKILL.md +13 -1
- package/.agents/skills/harness-steer/SKILL.md +14 -0
- package/.pi/agents/harness/adversary.md +3 -10
- package/.pi/agents/harness/evaluator.md +3 -12
- package/.pi/agents/harness/executor.md +12 -14
- package/.pi/agents/harness/planning/decompose.md +7 -4
- package/.pi/agents/harness/planning/hypothesis-validator.md +2 -0
- package/.pi/agents/harness/planning/hypothesis.md +3 -1
- package/.pi/agents/harness/planning/plan-adversary.md +2 -0
- package/.pi/agents/harness/planning/plan-evaluator.md +2 -0
- package/.pi/agents/harness/planning/plan-synthesizer.md +25 -0
- package/.pi/agents/harness/planning/planning-context.md +48 -0
- package/.pi/agents/harness/planning/review-integrator.md +2 -0
- package/.pi/agents/harness/planning/scout-graphify.md +3 -1
- package/.pi/agents/harness/planning/scout-semantic.md +3 -1
- package/.pi/agents/harness/planning/scout-structure.md +3 -1
- package/.pi/agents/harness/planning/sprint-contract-auditor.md +2 -0
- package/.pi/agents/harness/sentrux-steward.md +51 -0
- package/.pi/extensions/00-posthog-network-bootstrap.ts +11 -0
- package/.pi/extensions/harness-live-widget.ts +27 -1
- package/.pi/extensions/harness-plan-approval.ts +62 -56
- package/.pi/extensions/harness-run-context.ts +541 -84
- package/.pi/extensions/harness-subagent-submit.ts +43 -10
- package/.pi/extensions/lib/harness-artifact-gate.ts +182 -0
- package/.pi/extensions/lib/harness-posthog.ts +9 -5
- package/.pi/extensions/lib/harness-spawn-topology.ts +188 -0
- package/.pi/extensions/lib/harness-subagent-auth.ts +1 -0
- package/.pi/extensions/lib/harness-subagent-policy.ts +23 -19
- package/.pi/extensions/lib/harness-subagent-precheck.ts +35 -9
- package/.pi/extensions/lib/harness-subagent-submit-pipeline.ts +66 -2
- package/.pi/extensions/lib/harness-subagent-submit-registry.ts +21 -3
- package/.pi/extensions/lib/harness-subagents-bridge.ts +7 -29
- package/.pi/extensions/lib/harness-subprocess-bootstrap.ts +73 -0
- package/.pi/extensions/lib/plan-approval/create-plan.ts +2 -3
- package/.pi/extensions/lib/plan-approval/resolve-disk.ts +102 -0
- package/.pi/extensions/lib/plan-approval/schema.ts +22 -8
- package/.pi/extensions/lib/plan-approval/types.ts +1 -1
- package/.pi/extensions/lib/plan-approval/validate.ts +2 -2
- package/.pi/extensions/lib/plan-approval-readiness.ts +241 -0
- package/.pi/extensions/lib/plan-debate-eligibility.ts +12 -5
- package/.pi/extensions/lib/plan-debate-gate.ts +22 -1
- package/.pi/extensions/lib/plan-debate-lanes.ts +32 -2
- package/.pi/extensions/lib/plan-review-gate.ts +8 -0
- package/.pi/extensions/lib/posthog-client.ts +76 -0
- package/.pi/extensions/policy-gate.ts +24 -19
- package/.pi/harness/agents.manifest.json +24 -16
- package/.pi/harness/corpus/cron.example +8 -0
- package/.pi/harness/corpus/graphify-kb-updater.config.json +159 -0
- package/.pi/harness/corpus/systemd/graphify-kb-updater.env.template +4 -0
- package/.pi/harness/corpus/systemd/graphify-kb-updater.service +17 -0
- package/.pi/harness/corpus/systemd/graphify-kb-updater.timer +11 -0
- package/.pi/harness/docs/adrs/0001-harness-constitution.md +2 -1
- package/.pi/harness/docs/adrs/0006-sentrux-dual-layer.md +7 -6
- package/.pi/harness/docs/adrs/0009-sentrux-rules-lifecycle.md +6 -1
- package/.pi/harness/docs/adrs/0031-harness-run-context.md +1 -1
- package/.pi/harness/docs/adrs/0032-harness-command-orchestration.md +7 -0
- package/.pi/harness/docs/adrs/0034-darwin-plan-research-pipeline.md +3 -3
- package/.pi/harness/docs/adrs/0036-implementation-research-and-selective-debate.md +8 -5
- package/.pi/harness/docs/adrs/0039-harness-post-run-review-gate.md +47 -0
- package/.pi/harness/docs/adrs/0040-practice-grounded-orchestration.md +40 -0
- package/.pi/harness/docs/adrs/0041-intelligent-planning-reconnaissance.md +39 -0
- package/.pi/harness/docs/adrs/0042-agent-native-orchestration.md +35 -0
- package/.pi/harness/docs/adrs/0043-path-first-harness-tools.md +38 -0
- package/.pi/harness/docs/adrs/0044-harness-steer-loop.md +36 -0
- package/.pi/harness/docs/adrs/README.md +10 -0
- package/.pi/harness/docs/graphify-kb-updater-runbook.md +157 -0
- package/.pi/harness/docs/practice-map.md +110 -0
- package/.pi/harness/env.harness.template +5 -3
- package/.pi/harness/evals/smoke/sentrux-stub.json +1 -1
- package/.pi/harness/evals/smoke/smoke-harness-plan.mjs +5 -2
- package/.pi/harness/specs/README.md +1 -1
- package/.pi/harness/specs/harness-run-context.schema.json +11 -0
- package/.pi/harness/specs/harness-spawn-context.schema.json +14 -0
- package/.pi/harness/specs/plan-execution-plan.schema.json +39 -1
- package/.pi/harness/specs/plan-packet.schema.json +4 -0
- package/.pi/harness/specs/plan-phase-status.schema.json +17 -0
- package/.pi/harness/specs/plan-phase-waiver.schema.json +25 -0
- package/.pi/harness/specs/plan-planning-context.schema.json +50 -0
- package/.pi/harness/specs/repair-brief.schema.json +45 -0
- package/.pi/harness/specs/review-outcome.schema.json +46 -0
- package/.pi/harness/specs/sentrux-manifest-proposal.schema.json +80 -0
- package/.pi/harness/specs/sentrux-signal.schema.json +43 -0
- package/.pi/harness/specs/steer-state.schema.json +20 -0
- package/.pi/lib/harness-context-mode-policy.ts +256 -0
- package/.pi/lib/harness-repair-brief.ts +145 -0
- package/.pi/lib/harness-run-context.ts +591 -32
- package/.pi/lib/harness-ui-state.ts +87 -9
- package/.pi/prompts/harness-auto.md +9 -9
- package/.pi/prompts/harness-critic.md +3 -30
- package/.pi/prompts/harness-eval.md +4 -37
- package/.pi/prompts/harness-plan.md +118 -54
- package/.pi/prompts/harness-review.md +150 -15
- package/.pi/prompts/harness-run.md +62 -10
- package/.pi/prompts/harness-sentrux-steward.md +55 -0
- package/.pi/prompts/harness-steer.md +30 -0
- package/.pi/scripts/graphify-kb-updater.mjs +358 -0
- package/.pi/scripts/harness-verify.mjs +22 -6
- package/.pi/scripts/harness-web-policy-guard.mjs +68 -0
- package/.pi/scripts/validate-plan-dag.mjs +3 -3
- package/AGENTS.md +1 -0
- package/CHANGELOG.md +11 -0
- package/package.json +5 -4
- package/.pi/prompts/git-sync.md +0 -124
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* graphify-kb-updater — conservative local updater for Graphify source corpus.
|
|
4
|
+
*
|
|
5
|
+
* Daily automation may auto-promote only explicitly approved allowlisted public
|
|
6
|
+
* sources with complete provenance and rights/access metadata. Risky or unclear
|
|
7
|
+
* classes (books, transcripts, YouTube, paid/mirrored/unknown content) remain
|
|
8
|
+
* staged until manually approved.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { createHash } from "node:crypto";
|
|
12
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from "node:fs";
|
|
13
|
+
import { basename, extname, join, relative, resolve } from "node:path";
|
|
14
|
+
import { spawnSync } from "node:child_process";
|
|
15
|
+
|
|
16
|
+
let ROOT = resolve(new URL("../..", import.meta.url).pathname);
|
|
17
|
+
const DEFAULT_CONFIG = ".pi/harness/corpus/graphify-kb-updater.config.json";
|
|
18
|
+
const DEFAULT_STATE_DIR = ".pi/harness/corpus/graphify-kb-updater-state";
|
|
19
|
+
const DEFAULT_RAW_DIR = "raw/graphify-kb-updates";
|
|
20
|
+
const DEFAULT_DATA_DIR = "data";
|
|
21
|
+
const DEFAULT_GRAPH_DIR = "graphify-out";
|
|
22
|
+
const REQUIRED_RIGHTS = ["license", "access", "approved_by", "approved_at"];
|
|
23
|
+
const RISKY_KINDS = new Set(["book", "transcript", "youtube"]);
|
|
24
|
+
|
|
25
|
+
function parseArgs(argv) {
|
|
26
|
+
const args = {
|
|
27
|
+
dryRun: true,
|
|
28
|
+
apply: false,
|
|
29
|
+
config: DEFAULT_CONFIG,
|
|
30
|
+
stateDir: DEFAULT_STATE_DIR,
|
|
31
|
+
rawDir: DEFAULT_RAW_DIR,
|
|
32
|
+
dataDir: DEFAULT_DATA_DIR,
|
|
33
|
+
graphDir: DEFAULT_GRAPH_DIR,
|
|
34
|
+
refreshGraph: false,
|
|
35
|
+
skipGraph: false,
|
|
36
|
+
pilotReport: false,
|
|
37
|
+
schedulerSmoke: false,
|
|
38
|
+
maxPromotions: 25,
|
|
39
|
+
projectRoot: ROOT,
|
|
40
|
+
};
|
|
41
|
+
for (let i = 0; i < argv.length; i++) {
|
|
42
|
+
const a = argv[i];
|
|
43
|
+
if (a === "--dry-run") args.dryRun = true;
|
|
44
|
+
else if (a === "--apply") { args.apply = true; args.dryRun = false; }
|
|
45
|
+
else if (a === "--refresh-graph") args.refreshGraph = true;
|
|
46
|
+
else if (a === "--skip-graph") args.skipGraph = true;
|
|
47
|
+
else if (a === "--pilot-report") args.pilotReport = true;
|
|
48
|
+
else if (a === "--scheduler-smoke") args.schedulerSmoke = true;
|
|
49
|
+
else if (a === "--config") args.config = argv[++i];
|
|
50
|
+
else if (a === "--state-dir") args.stateDir = argv[++i];
|
|
51
|
+
else if (a === "--raw-dir") args.rawDir = argv[++i];
|
|
52
|
+
else if (a === "--data-dir") args.dataDir = argv[++i];
|
|
53
|
+
else if (a === "--graph-dir") args.graphDir = argv[++i];
|
|
54
|
+
else if (a === "--project-root") args.projectRoot = argv[++i];
|
|
55
|
+
else if (a === "--max-promotions") args.maxPromotions = Number(argv[++i]);
|
|
56
|
+
else if (a === "--help") usage(0);
|
|
57
|
+
else throw new Error(`unknown argument: ${a}`);
|
|
58
|
+
}
|
|
59
|
+
return args;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function usage(code) {
|
|
63
|
+
console.log(`Usage: node .pi/scripts/graphify-kb-updater.mjs [--dry-run|--apply] [options]\n\nOptions:\n --config PATH JSON source policy config\n --state-dir PATH durable registry/run-log directory\n --raw-dir PATH stable promoted source corpus root\n --data-dir PATH local books/transcripts root\n --project-root PATH root used for corpus/state paths\n --refresh-graph run graphify update . after promotions\n --skip-graph never run graphify\n --pilot-report print frontier recall/precision/noise/graph proxy metrics\n --scheduler-smoke validate scheduler-oriented env without promotion\n --max-promotions N cap apply promotions per run (default 25)`);
|
|
64
|
+
process.exit(code);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function readJson(path, fallback) {
|
|
68
|
+
if (!existsSync(path)) return fallback;
|
|
69
|
+
return JSON.parse(readFileSync(path, "utf8"));
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function sortDeep(value) {
|
|
73
|
+
if (Array.isArray(value)) return value.map(sortDeep);
|
|
74
|
+
if (!value || typeof value !== "object") return value;
|
|
75
|
+
return Object.fromEntries(Object.keys(value).sort().map((k) => [k, sortDeep(value[k])]));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function stableJson(obj) { return `${JSON.stringify(sortDeep(obj), null, 2)}\n`; }
|
|
79
|
+
function sha256(text) { return createHash("sha256").update(text).digest("hex"); }
|
|
80
|
+
function slugify(s) { return s.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 90) || "source"; }
|
|
81
|
+
function nowIso() { return new Date().toISOString(); }
|
|
82
|
+
function rel(path) { return relative(ROOT, path) || "."; }
|
|
83
|
+
|
|
84
|
+
function loadConfig(args) {
|
|
85
|
+
const path = resolve(ROOT, args.config);
|
|
86
|
+
const cfg = readJson(path, {});
|
|
87
|
+
return {
|
|
88
|
+
schemaVersion: cfg.schema_version ?? "1.0.0",
|
|
89
|
+
policy: cfg.policy ?? "conservative-staged-review",
|
|
90
|
+
sourceTaxonomy: cfg.source_taxonomy ?? {},
|
|
91
|
+
competitorTaxonomy: cfg.competitor_taxonomy ?? {},
|
|
92
|
+
allowlist: (cfg.allowlist ?? []).map((entry) => typeof entry === "string" ? { domain: entry, approved: true } : entry),
|
|
93
|
+
reviewQueue: cfg.review_queue ?? [],
|
|
94
|
+
articleQueries: cfg.article_queries ?? [],
|
|
95
|
+
paperFeeds: cfg.paper_feeds ?? [],
|
|
96
|
+
localBooks: cfg.local_books ?? [{ path: "data/books" }],
|
|
97
|
+
localTranscripts: cfg.local_transcripts ?? [{ path: "data/youtube-transcripts" }],
|
|
98
|
+
youtubeCandidates: cfg.youtube_candidates ?? [],
|
|
99
|
+
autoPromoteAllowlist: cfg.auto_promote_allowlist === true,
|
|
100
|
+
path,
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function walkFiles(root, exts, max = 200) {
|
|
105
|
+
const out = [];
|
|
106
|
+
function walk(dir) {
|
|
107
|
+
if (out.length >= max || !existsSync(dir)) return;
|
|
108
|
+
for (const name of readdirSync(dir)) {
|
|
109
|
+
const p = join(dir, name);
|
|
110
|
+
const st = statSync(p);
|
|
111
|
+
if (st.isDirectory()) walk(p);
|
|
112
|
+
else if (exts.includes(extname(name).toLowerCase())) out.push(p);
|
|
113
|
+
if (out.length >= max) break;
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
walk(root);
|
|
117
|
+
return out;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function rightsFromSidecar(file) {
|
|
121
|
+
const json = `${file}.rights.json`;
|
|
122
|
+
if (existsSync(json)) return readJson(json, null);
|
|
123
|
+
const metaJson = file.replace(/\.[^.]+$/, ".meta.json");
|
|
124
|
+
if (existsSync(metaJson)) return readJson(metaJson, null)?.rights_access ?? null;
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function hasRightsApproval(candidate) {
|
|
129
|
+
const r = candidate.rights_access;
|
|
130
|
+
return Boolean(r && REQUIRED_RIGHTS.every((k) => typeof r[k] === "string" && r[k].trim()));
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
function urlDomain(url) {
|
|
134
|
+
try { return new URL(url).hostname.replace(/^www\./, ""); } catch { return ""; }
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function allowlistEntry(cfg, domain) {
|
|
138
|
+
return cfg.allowlist.find((entry) => entry.domain === domain || domain.endsWith(`.${entry.domain}`));
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function competitorLabels(cfg, candidate) {
|
|
142
|
+
const haystack = `${candidate.title ?? ""} ${candidate.url ?? ""} ${candidate.path ?? ""}`.toLowerCase();
|
|
143
|
+
const labels = [];
|
|
144
|
+
for (const [category, spec] of Object.entries(cfg.competitorTaxonomy ?? {})) {
|
|
145
|
+
const terms = Array.isArray(spec) ? spec : (spec.keywords ?? []);
|
|
146
|
+
if (terms.some((term) => haystack.includes(String(term).toLowerCase()))) labels.push(category);
|
|
147
|
+
}
|
|
148
|
+
return labels;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function candidateId(candidate) {
|
|
152
|
+
return sha256([candidate.kind, candidate.source_type, candidate.url ?? candidate.path ?? candidate.query ?? "", candidate.title ?? ""].join("\n")).slice(0, 16);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function normalizeCandidate(cfg, raw) {
|
|
156
|
+
const domain = raw.domain ?? urlDomain(raw.url);
|
|
157
|
+
const allow = domain ? allowlistEntry(cfg, domain) : null;
|
|
158
|
+
const taxonomy = cfg.sourceTaxonomy?.[raw.kind] ?? {};
|
|
159
|
+
const candidate = {
|
|
160
|
+
...raw,
|
|
161
|
+
domain,
|
|
162
|
+
category: raw.category ?? taxonomy.category ?? raw.kind,
|
|
163
|
+
risk_class: raw.risk_class ?? taxonomy.risk_class ?? (RISKY_KINDS.has(raw.kind) ? "high" : "medium"),
|
|
164
|
+
provenance: raw.provenance ?? { origin: raw.source_type, discovered_by: "graphify-kb-updater", locator: raw.url ?? raw.path ?? raw.query ?? null },
|
|
165
|
+
rights_access: raw.rights_access ?? null,
|
|
166
|
+
allowlist_state: allow ? { allowed: true, domain: allow.domain, approved: allow.approved === true, approved_by: allow.approved_by ?? null, approved_at: allow.approved_at ?? null } : { allowed: false },
|
|
167
|
+
approval_state: raw.approved === true ? "approved" : "not_approved",
|
|
168
|
+
};
|
|
169
|
+
candidate.competitor_labels = raw.competitor_labels ?? competitorLabels(cfg, candidate);
|
|
170
|
+
candidate.id = raw.id ?? candidateId(candidate);
|
|
171
|
+
candidate.content_hash = sha256(sourceBody(candidate));
|
|
172
|
+
return candidate;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function discoverCandidates(cfg, args) {
|
|
176
|
+
const candidates = [];
|
|
177
|
+
for (const query of cfg.articleQueries) candidates.push({ kind: "article", source_type: "web_search_query", title: query, query, review_required: true, promotion_policy: "stage_only" });
|
|
178
|
+
for (const feed of cfg.paperFeeds) candidates.push({ kind: "paper", source_type: "feed", title: feed.title ?? feed.url, url: feed.url, rights_access: feed.rights_access ?? null, review_required: true, promotion_policy: "stage_only", provenance: feed.provenance });
|
|
179
|
+
for (const entry of cfg.reviewQueue) {
|
|
180
|
+
const domain = urlDomain(entry.url);
|
|
181
|
+
const allow = allowlistEntry(cfg, domain);
|
|
182
|
+
const explicit = cfg.autoPromoteAllowlist && allow?.approved === true && entry.approved === true;
|
|
183
|
+
candidates.push({ ...entry, kind: entry.kind ?? "article", source_type: "review_queue", domain, review_required: !explicit, promotion_policy: explicit ? "allowlist_auto_promote" : "manual_review", rights_access: entry.rights_access ?? null });
|
|
184
|
+
}
|
|
185
|
+
for (const spec of cfg.localBooks) for (const file of walkFiles(resolve(ROOT, spec.path), [".md", ".txt", ".pdf"], spec.max_files ?? 50)) candidates.push({ kind: "book", source_type: "local_file", title: basename(file), path: rel(file), rights_access: rightsFromSidecar(file), review_required: true, promotion_policy: "manual_review" });
|
|
186
|
+
for (const spec of cfg.localTranscripts) for (const file of walkFiles(resolve(ROOT, spec.path), [".md", ".txt", ".vtt"], spec.max_files ?? 80)) candidates.push({ kind: "transcript", source_type: "local_file", title: basename(file), path: rel(file), rights_access: rightsFromSidecar(file), review_required: true, promotion_policy: "manual_review" });
|
|
187
|
+
for (const yt of cfg.youtubeCandidates) candidates.push({ ...yt, kind: "youtube", source_type: "youtube_candidate", review_required: true, promotion_policy: "manual_review", rights_access: yt.rights_access ?? null });
|
|
188
|
+
return candidates.map((c) => normalizeCandidate(cfg, c));
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function loadRegistry(args) {
|
|
192
|
+
const dir = resolve(ROOT, args.stateDir);
|
|
193
|
+
return readJson(join(dir, "registry.json"), { schema_version: "1.1.0", candidates: {}, runs: [] });
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function writeRegistry(args, registry) {
|
|
197
|
+
const dir = resolve(ROOT, args.stateDir);
|
|
198
|
+
mkdirSync(dir, { recursive: true });
|
|
199
|
+
writeFileSync(join(dir, "registry.json"), stableJson(registry));
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
function sourceBody(candidate) {
|
|
203
|
+
if (candidate.source_type === "local_file" && candidate.path) {
|
|
204
|
+
const abs = resolve(ROOT, candidate.path);
|
|
205
|
+
if (existsSync(abs) && extname(abs).toLowerCase() !== ".pdf") return readFileSync(abs, "utf8");
|
|
206
|
+
}
|
|
207
|
+
return `# ${candidate.title ?? candidate.id}\n\nSource staged by graphify-kb-updater. Fetch/parse content via approved harness web/API workflow before broad use.\n`;
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
function promotionAllowed(candidate) {
|
|
211
|
+
if (!hasRightsApproval(candidate)) return { ok: false, reason: "missing_rights_access_approval" };
|
|
212
|
+
if (RISKY_KINDS.has(candidate.kind) && candidate.approved !== true) return { ok: false, reason: "manual_approval_required" };
|
|
213
|
+
if (candidate.source_type === "review_queue" && candidate.promotion_policy === "allowlist_auto_promote" && candidate.allowlist_state.allowed && candidate.allowlist_state.approved) return { ok: true };
|
|
214
|
+
return candidate.approved === true ? { ok: true } : { ok: false, reason: "manual_approval_required" };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function promote(candidate, args) {
|
|
218
|
+
const body = sourceBody(candidate);
|
|
219
|
+
const contentHash = sha256(body);
|
|
220
|
+
const dir = resolve(ROOT, args.rawDir, candidate.kind);
|
|
221
|
+
const base = `${new Date().toISOString().slice(0, 10)}-${slugify(candidate.title ?? candidate.id)}-${contentHash.slice(0, 8)}`;
|
|
222
|
+
const md = join(dir, `${base}.md`);
|
|
223
|
+
const prov = join(dir, `${base}.provenance.json`);
|
|
224
|
+
mkdirSync(dir, { recursive: true });
|
|
225
|
+
const header = `---\nsource_id: ${candidate.id}\nkind: ${candidate.kind}\ncategory: ${candidate.category}\ncontent_sha256: ${contentHash}\nrights_license: ${candidate.rights_access.license}\nrights_access: ${candidate.rights_access.access}\napproved_by: ${candidate.rights_access.approved_by}\napproved_at: ${candidate.rights_access.approved_at}\ncompetitor_labels: ${JSON.stringify(candidate.competitor_labels)}\n---\n\n`;
|
|
226
|
+
writeFileSync(md, header + body);
|
|
227
|
+
writeFileSync(prov, stableJson({ ...candidate, content_sha256: contentHash, promoted_path: rel(md), promoted_at: nowIso() }));
|
|
228
|
+
return { path: rel(md), provenance_path: rel(prov), content_hash: contentHash };
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function refreshGraph(args, changedCount) {
|
|
232
|
+
if (args.skipGraph) return { action: "skipped_by_flag" };
|
|
233
|
+
if (!args.refreshGraph) return { action: changedCount > 0 ? "planned" : "skipped_noop" };
|
|
234
|
+
if (changedCount === 0) return { action: "skipped_noop" };
|
|
235
|
+
const run = spawnSync("graphify", ["update", "."], { cwd: ROOT, encoding: "utf8", timeout: 20 * 60 * 1000 });
|
|
236
|
+
const report = resolve(ROOT, args.graphDir, "GRAPH_REPORT.md");
|
|
237
|
+
return { action: "graphify_update", exit_status: run.status, ok: run.status === 0 && existsSync(report), report: rel(report), stderr: (run.stderr ?? "").slice(0, 1200) };
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function appendRunLog(args, summary) {
|
|
241
|
+
const dir = resolve(ROOT, args.stateDir, "logs");
|
|
242
|
+
mkdirSync(dir, { recursive: true });
|
|
243
|
+
writeFileSync(join(dir, `${summary.run_id}.json`), stableJson(summary));
|
|
244
|
+
const line = `${JSON.stringify(summary)}\n`;
|
|
245
|
+
const jsonl = join(dir, "runs.jsonl");
|
|
246
|
+
writeFileSync(jsonl, existsSync(jsonl) ? readFileSync(jsonl, "utf8") + line : line);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
function byCount(items, keyFn) {
|
|
250
|
+
const out = {};
|
|
251
|
+
for (const item of items) for (const key of [keyFn(item)].flat().filter(Boolean)) out[key] = (out[key] ?? 0) + 1;
|
|
252
|
+
return out;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function pilotMetrics(summary) {
|
|
256
|
+
const considered = Math.max(summary.candidate_count, 1);
|
|
257
|
+
const promoted = summary.promoted_count;
|
|
258
|
+
const duplicates = summary.duplicate_skips;
|
|
259
|
+
return {
|
|
260
|
+
frontier_recall_proxy: Number(((summary.candidate_count - duplicates) / considered).toFixed(3)),
|
|
261
|
+
promoted_precision_proxy: promoted === 0 ? 1 : Number((promoted / Math.max(promoted + summary.failure_count, 1)).toFixed(3)),
|
|
262
|
+
duplicate_noise_rate: Number((duplicates / considered).toFixed(3)),
|
|
263
|
+
graphify_success: ["skipped_noop", "planned", "skipped_by_flag"].includes(summary.graph.action) || summary.graph.ok === true,
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function schedulerSmoke() {
|
|
268
|
+
const service = readFileSync(resolve(ROOT, ".pi/harness/corpus/systemd/graphify-kb-updater.service"), "utf8");
|
|
269
|
+
const timer = readFileSync(resolve(ROOT, ".pi/harness/corpus/systemd/graphify-kb-updater.timer"), "utf8");
|
|
270
|
+
const cron = readFileSync(resolve(ROOT, ".pi/harness/corpus/cron.example"), "utf8");
|
|
271
|
+
const checks = {
|
|
272
|
+
systemd_daily: /OnCalendar=\*-\*-\*\s+08:30:00|OnCalendar=daily/i.test(timer),
|
|
273
|
+
cron_daily: /^30\s+8\s+\*\s+\*\s+\*/m.test(cron),
|
|
274
|
+
bounded_timeout: /timeout 45m/.test(service) && /timeout 45m/.test(cron),
|
|
275
|
+
locked_no_overlap: /flock -n/.test(service) && /flock -n/.test(cron),
|
|
276
|
+
explicit_env: /EnvironmentFile/.test(service) && /UP_ROOT/.test(cron),
|
|
277
|
+
logged: /StandardOutput=append/.test(service) && /HARNESS_GRAPHIFY_KB_LOG/.test(cron),
|
|
278
|
+
refresh_intent: /--refresh-graph/.test(cron),
|
|
279
|
+
};
|
|
280
|
+
const ok = Object.values(checks).every(Boolean);
|
|
281
|
+
console.log(JSON.stringify({ ok, checks }, null, 2));
|
|
282
|
+
process.exit(ok ? 0 : 1);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
function main() {
|
|
286
|
+
const started = Date.now();
|
|
287
|
+
const args = parseArgs(process.argv.slice(2));
|
|
288
|
+
ROOT = resolve(args.projectRoot);
|
|
289
|
+
if (args.schedulerSmoke) schedulerSmoke();
|
|
290
|
+
const cfg = loadConfig(args);
|
|
291
|
+
const registry = loadRegistry(args);
|
|
292
|
+
const candidates = discoverCandidates(cfg, args);
|
|
293
|
+
let duplicates = 0, promoted = 0, failed = 0, changedExisting = 0;
|
|
294
|
+
const planned = [], blocked = [], promotedRefs = [], skipped = [];
|
|
295
|
+
const runAt = nowIso();
|
|
296
|
+
|
|
297
|
+
for (const c of candidates) {
|
|
298
|
+
const prior = registry.candidates[c.id];
|
|
299
|
+
const contentChanged = Boolean(prior?.content_hash && prior.content_hash !== c.content_hash);
|
|
300
|
+
if (prior?.status === "promoted" && !contentChanged) {
|
|
301
|
+
duplicates++;
|
|
302
|
+
registry.candidates[c.id] = { ...prior, last_seen_at: runAt, content_state: "unchanged" };
|
|
303
|
+
skipped.push({ id: c.id, reason: "duplicate_unchanged", content_state: "unchanged" });
|
|
304
|
+
continue;
|
|
305
|
+
}
|
|
306
|
+
if (contentChanged) changedExisting++;
|
|
307
|
+
const gate = promotionAllowed(c);
|
|
308
|
+
registry.candidates[c.id] = { ...(prior ?? {}), ...c, first_seen_at: prior?.first_seen_at ?? runAt, last_seen_at: runAt, status: gate.ok ? "promotable" : "review_required", block_reason: gate.reason ?? null, content_state: contentChanged ? "changed" : "new" };
|
|
309
|
+
if (!gate.ok) { blocked.push({ id: c.id, title: c.title, reason: gate.reason, allowlist_state: c.allowlist_state, category: c.category, competitor_labels: c.competitor_labels }); continue; }
|
|
310
|
+
planned.push(c);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
if (args.apply) {
|
|
314
|
+
for (const c of planned.slice(0, args.maxPromotions)) {
|
|
315
|
+
try {
|
|
316
|
+
const ref = promote(c, args);
|
|
317
|
+
registry.candidates[c.id] = { ...registry.candidates[c.id], ...ref, status: "promoted", promoted_at: nowIso() };
|
|
318
|
+
promotedRefs.push(ref); promoted++;
|
|
319
|
+
} catch (err) {
|
|
320
|
+
failed++;
|
|
321
|
+
registry.candidates[c.id] = { ...registry.candidates[c.id], status: "failed", error: String(err?.message ?? err) };
|
|
322
|
+
}
|
|
323
|
+
}
|
|
324
|
+
for (const c of planned.slice(args.maxPromotions)) skipped.push({ id: c.id, reason: "max_promotions_cap" });
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
const graph = refreshGraph(args, promoted);
|
|
328
|
+
const stale = registry.runs.at?.(-1)?.run_id ? [] : ["no_prior_apply_run_recorded"];
|
|
329
|
+
const summary = {
|
|
330
|
+
run_id: `kb-${Date.now()}`,
|
|
331
|
+
last_run_at: runAt,
|
|
332
|
+
mode: args.dryRun ? "dry-run" : "apply",
|
|
333
|
+
candidate_count: candidates.length,
|
|
334
|
+
planned_promotions: planned.length,
|
|
335
|
+
promoted_count: promoted,
|
|
336
|
+
duplicate_skips: duplicates,
|
|
337
|
+
blocked_count: blocked.length,
|
|
338
|
+
skipped_count: skipped.length,
|
|
339
|
+
failure_count: failed,
|
|
340
|
+
changed_existing_count: changedExisting,
|
|
341
|
+
runtime_ms: Date.now() - started,
|
|
342
|
+
counts: { by_kind: byCount(candidates, (c) => c.kind), by_source_type: byCount(candidates, (c) => c.source_type), by_competitor_label: byCount(candidates, (c) => c.competitor_labels), allowlisted: candidates.filter((c) => c.allowlist_state.allowed).length },
|
|
343
|
+
stale_warnings: stale,
|
|
344
|
+
graph,
|
|
345
|
+
exit_status: failed || graph.ok === false ? 1 : 0,
|
|
346
|
+
promoted: promotedRefs,
|
|
347
|
+
blocked: blocked.slice(0, 50),
|
|
348
|
+
skipped: skipped.slice(0, 50),
|
|
349
|
+
config: rel(cfg.path),
|
|
350
|
+
};
|
|
351
|
+
if (args.pilotReport) summary.pilot = pilotMetrics(summary);
|
|
352
|
+
registry.runs.push(summary);
|
|
353
|
+
if (args.apply) { writeRegistry(args, registry); appendRunLog(args, summary); }
|
|
354
|
+
console.log(JSON.stringify(summary, null, 2));
|
|
355
|
+
process.exit(summary.exit_status);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
try { main(); } catch (err) { console.error(`graphify-kb-updater: ${err.stack ?? err}`); process.exit(2); }
|
|
@@ -23,6 +23,8 @@ const REQUIRED_SCHEMAS = [
|
|
|
23
23
|
"eval-verdict.schema.json",
|
|
24
24
|
"harness-spawn-context.schema.json",
|
|
25
25
|
"harness-turn.schema.json",
|
|
26
|
+
"sentrux-manifest-proposal.schema.json",
|
|
27
|
+
"sentrux-signal.schema.json",
|
|
26
28
|
];
|
|
27
29
|
|
|
28
30
|
const REQUIRED_ADRS = [
|
|
@@ -39,6 +41,7 @@ const REQUIRED_ADRS = [
|
|
|
39
41
|
"0032-harness-command-orchestration.md",
|
|
40
42
|
"0037-subagent-submit-tools.md",
|
|
41
43
|
"0038-budget-telemetry-only.md",
|
|
44
|
+
"0040-practice-grounded-orchestration.md",
|
|
42
45
|
];
|
|
43
46
|
|
|
44
47
|
const REQUIRED_EXTENSIONS = [
|
|
@@ -180,13 +183,21 @@ async function checkSentruxGate() {
|
|
|
180
183
|
ok("Sentrux MCP stub gate skipped (HARNESS_SENTRUX_REQUIRED not set)");
|
|
181
184
|
return;
|
|
182
185
|
}
|
|
183
|
-
const
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
186
|
+
const runDir = process.env.HARNESS_RUN_DIR?.trim();
|
|
187
|
+
const runSignalPath = runDir
|
|
188
|
+
? join(runDir, "artifacts", "sentrux-signal.yaml")
|
|
189
|
+
: null;
|
|
190
|
+
if (runSignalPath && (await fileExists(runSignalPath))) {
|
|
191
|
+
ok(`Sentrux run signal present (${runSignalPath})`);
|
|
192
|
+
} else {
|
|
193
|
+
const stubPath = join(ROOT, ".pi", "harness", "evals", "smoke", "sentrux-stub.json");
|
|
194
|
+
if (!(await fileExists(stubPath))) {
|
|
195
|
+
fail(
|
|
196
|
+
"HARNESS_SENTRUX_REQUIRED=true but no artifacts/sentrux-signal.yaml (set HARNESS_RUN_DIR) and .pi/harness/evals/smoke/sentrux-stub.json missing",
|
|
197
|
+
);
|
|
198
|
+
}
|
|
199
|
+
ok("Sentrux stub present (run signal absent — prefer artifacts/sentrux-signal.yaml from /harness-run)");
|
|
188
200
|
}
|
|
189
|
-
ok("Sentrux stub present");
|
|
190
201
|
|
|
191
202
|
const { code, out } = await runNodeScript(
|
|
192
203
|
join(ROOT, ".pi", "scripts", "sentrux-rules-sync.mjs"),
|
|
@@ -290,6 +301,11 @@ async function main() {
|
|
|
290
301
|
if (!policyGateSrc.includes('pi.on("tool_call", async (event, ctx)')) {
|
|
291
302
|
fail("policy-gate tool_call must receive ctx for run context");
|
|
292
303
|
}
|
|
304
|
+
if (!policyGateSrc.includes("evaluateContextModeMutation")) {
|
|
305
|
+
fail(
|
|
306
|
+
"policy-gate.ts must evaluate context-mode execute tools via evaluateContextModeMutation",
|
|
307
|
+
);
|
|
308
|
+
}
|
|
293
309
|
ok("policy-gate plan-phase writes");
|
|
294
310
|
|
|
295
311
|
const runCtxFixture = join(SMOKE, "run-context.fixture.json");
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/** Package-wide web-policy guard.
|
|
3
|
+
* Rejects raw HTTP shell/client paths unless they are in approved harness/API
|
|
4
|
+
* abstraction files. This is a static smoke guard; runtime blocking remains in
|
|
5
|
+
* .pi/extensions/harness-web-guard.ts.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
9
|
+
import { join, relative, resolve } from "node:path";
|
|
10
|
+
|
|
11
|
+
const ROOT = resolve(new URL("../..", import.meta.url).pathname);
|
|
12
|
+
const ALLOWED_FILES = new Set([
|
|
13
|
+
".pi/extensions/harness-web-guard.ts",
|
|
14
|
+
".pi/extensions/harness-web-tools.ts",
|
|
15
|
+
".pi/extensions/lib/harness-web/run-cli.ts",
|
|
16
|
+
".pi/extensions/harness-run-context.ts",
|
|
17
|
+
".pi/extensions/lib/ask-user/schema.ts",
|
|
18
|
+
".pi/scripts/harness-web.py",
|
|
19
|
+
".pi/scripts/harness-web-search.md",
|
|
20
|
+
".pi/scripts/harness-web-policy-guard.mjs",
|
|
21
|
+
".agents/skills/scrapling-web/SKILL.md",
|
|
22
|
+
".pi/scripts/harness-cli-verify.sh",
|
|
23
|
+
".pi/scripts/harness_web/output.py",
|
|
24
|
+
"AGENTS.md",
|
|
25
|
+
]);
|
|
26
|
+
const SKIP_DIRS = new Set([".git", "node_modules", "vendor", "graphify-out", "graphify-books-out", ".web", ".cocoindex_code", ".agents", ".cursor", "raw"]);
|
|
27
|
+
const TEXT_EXTS = new Set([".js", ".mjs", ".ts", ".tsx", ".json", ".yaml", ".yml", ".py", ".sh", ".toml", ".example", ".template"]);
|
|
28
|
+
const NEEDLES = [
|
|
29
|
+
{ name: "raw curl/wget URL", re: /\b(?:curl|wget)\b[^\n]*https?:\/\//i },
|
|
30
|
+
{ name: "raw Node HTTP client", re: /\b(?:fetch|request|get)\s*\(\s*[`'"]https?:\/\//i },
|
|
31
|
+
{ name: "raw Python HTTP client", re: /\brequests\.(?:get|post|put|delete|head)\s*\(\s*[`'"]https?:\/\//i },
|
|
32
|
+
{ name: "Firecrawl path", re: /\bfirecrawl\b/i },
|
|
33
|
+
];
|
|
34
|
+
|
|
35
|
+
function ext(path) {
|
|
36
|
+
const i = path.lastIndexOf(".");
|
|
37
|
+
return i >= 0 ? path.slice(i) : "";
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function walk(dir, out = []) {
|
|
41
|
+
for (const name of readdirSync(dir)) {
|
|
42
|
+
const abs = join(dir, name);
|
|
43
|
+
const r = relative(ROOT, abs);
|
|
44
|
+
if (SKIP_DIRS.has(name) || r.startsWith(".pi/harness/runs/") || r.startsWith(".pi/agents/") || r.startsWith(".pi/skills/") || r.startsWith(".pi/prompts/") || r.startsWith(".pi/harness/docs/")) continue;
|
|
45
|
+
const st = statSync(abs);
|
|
46
|
+
if (st.isDirectory()) walk(abs, out);
|
|
47
|
+
else if (TEXT_EXTS.has(ext(name)) || name.includes("template") || name.includes("example")) out.push(abs);
|
|
48
|
+
}
|
|
49
|
+
return out;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const hits = [];
|
|
53
|
+
for (const abs of walk(ROOT)) {
|
|
54
|
+
const r = relative(ROOT, abs);
|
|
55
|
+
if (ALLOWED_FILES.has(r)) continue;
|
|
56
|
+
let text = "";
|
|
57
|
+
try { text = readFileSync(abs, "utf8"); } catch { continue; }
|
|
58
|
+
for (const needle of NEEDLES) {
|
|
59
|
+
const m = text.match(needle.re);
|
|
60
|
+
if (m) hits.push({ file: r, rule: needle.name, match: m[0].slice(0, 140) });
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (hits.length) {
|
|
65
|
+
console.error(JSON.stringify({ ok: false, hits }, null, 2));
|
|
66
|
+
process.exit(1);
|
|
67
|
+
}
|
|
68
|
+
console.log(JSON.stringify({ ok: true, scanned_root: ROOT }, null, 2));
|
|
@@ -12,9 +12,9 @@ import { readYamlFile, writeYamlFile } from "../lib/harness-yaml.mjs";
|
|
|
12
12
|
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
13
13
|
|
|
14
14
|
const MINIMUMS = {
|
|
15
|
-
low: { phases: 2, work_items:
|
|
16
|
-
med: { phases: 3, work_items:
|
|
17
|
-
high: { phases: 4, work_items:
|
|
15
|
+
low: { phases: 2, work_items: 2, acceptance_checks: 3, risks: 0 },
|
|
16
|
+
med: { phases: 3, work_items: 4, acceptance_checks: 5, risks: 3 },
|
|
17
|
+
high: { phases: 4, work_items: 6, acceptance_checks: 8, risks: 3 },
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
function fail(msg) {
|
package/AGENTS.md
CHANGED
|
@@ -10,6 +10,7 @@ Created: 2026-05-14
|
|
|
10
10
|
- ./raw/ → Source documents for graphify ingestion
|
|
11
11
|
- docs/adr/ → Repo-level Architectural Decision Records
|
|
12
12
|
- .pi/harness/docs/adrs/ → Harness ADRs (team-shared; [index](.pi/harness/docs/adrs/README.md))
|
|
13
|
+
- .pi/harness/docs/practice-map.md → Phase → practice → agent spawn topology for `/harness-plan`, `/harness-run`, `/harness-review`
|
|
13
14
|
- .pi/skills/ → Agent skills
|
|
14
15
|
- .pi/agents/ → Specialized agents
|
|
15
16
|
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,17 @@ All notable changes to this project are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [v0.18.0] — 2026-05-23
|
|
8
|
+
|
|
9
|
+
### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **Harness:** Agent-native plan/review workflows (ADR 0042–0044) — path-first `approve_plan` / `create_plan` / `submit_*`, lakes on execution plans, `parallel_probes` debate, `review-outcome.yaml` routing, `/harness-steer` repair loop, `merge_harness_yaml`, and `harness_synthesize_repair_brief`.
|
|
12
|
+
- **Harness:** Practice map, planning-context and plan-synthesizer agents, PostHog bootstrap, graphify KB updater corpus, and Sentrux steward prompt.
|
|
13
|
+
|
|
14
|
+
### ✅ Tests
|
|
15
|
+
|
|
16
|
+
- Add harness tool-payload, post-run routing, subprocess bootstrap, artifact gate, and topology tests; extend smoke plan fixture for parallel probes.
|
|
17
|
+
|
|
7
18
|
## [v0.17.0] — 2026-05-22
|
|
8
19
|
|
|
9
20
|
### ✨ Features
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultimate-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.18.0",
|
|
4
4
|
"description": "Ultimate AI coding harness for pi.dev — extensible skills, Obsidian wiki knowledge layer, compressed context, deterministic output",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"pi-package",
|
|
@@ -74,7 +74,7 @@
|
|
|
74
74
|
"@earendil-works/pi-coding-agent": "*"
|
|
75
75
|
},
|
|
76
76
|
"scripts": {
|
|
77
|
-
"check:ts": "tsc --noEmit --target ES2023 --lib ES2023 --moduleResolution nodenext --module nodenext --skipLibCheck .pi/extensions/custom-system-prompt.ts .pi/lib/harness-run-context.ts .pi/lib/harness-ui-state.ts .pi/extensions/harness-run-context.ts .pi/extensions/lib/harness-vcc-settings.ts .pi/extensions/dotenv-loader.ts .pi/extensions/lib/posthog-node.d.ts .pi/extensions/lib/harness-posthog.ts .pi/extensions/lib/harness-paths.ts .pi/extensions/pi-model-router-harness.ts .pi/extensions/provider-payload-sanitize.ts .pi/extensions/harness-telemetry.ts .pi/extensions/harness-ask-user.ts .pi/extensions/harness-plan-approval.ts .pi/extensions/lib/ask-user/schema.ts .pi/extensions/lib/ask-user/types.ts .pi/extensions/lib/ask-user/validate.ts .pi/extensions/lib/ask-user/dialog.ts .pi/extensions/lib/ask-user/fallback.ts .pi/extensions/lib/ask-user/render.ts .pi/extensions/lib/plan-approval/types.ts .pi/extensions/lib/plan-approval/schema.ts .pi/extensions/lib/plan-approval/validate.ts .pi/extensions/lib/plan-approval/format-plan.ts .pi/extensions/lib/plan-approval/dialog.ts .pi/extensions/lib/plan-approval/render.ts .pi/extensions/lib/plan-approval/create-plan.ts .pi/extensions/harness-subagents.ts .pi/extensions/lib/harness-subagents-bridge.ts .pi/extensions/lib/harness-cocoindex-refresh.ts .pi/extensions/lib/harness-subagent-auth.ts .pi/extensions/lib/harness-subagent-policy.ts .pi/extensions/lib/harness-subagent-precheck.ts .pi/extensions/lib/harness-spawn-budget.ts .pi/extensions/lib/spawn-policy.ts vendor/pi-subagents/src/agents.ts vendor/pi-subagents/src/subagents.ts .pi/extensions/trace-recorder.ts .pi/extensions/observation-bus.ts .pi/extensions/drift-monitor.ts .pi/extensions/policy-gate.ts .pi/extensions/budget-guard.ts .pi/extensions/debate-orchestrator.ts .pi/extensions/harness-debate-tools.ts .pi/extensions/lib/debate-bus-core.ts .pi/extensions/lib/debate-bus-state.ts .pi/extensions/lib/plan-debate-gate.ts .pi/extensions/lib/plan-debate-id.ts .pi/extensions/lib/plan-messenger.ts .pi/extensions/lib/plan-debate-envelope.ts .pi/extensions/lib/plan-review-integrator-rules.ts .pi/extensions/lib/plan-scope-guard.ts .pi/extensions/lib/plan-debate-write-guard.ts .pi/extensions/lib/plan-debate-lane.ts .pi/extensions/lib/plan-debate-round-status.ts .pi/extensions/harness-live-widget.ts .pi/extensions/sentrux-rules-sync.ts .pi/extensions/custom-header.ts .pi/extensions/harness-web-tools.ts .pi/extensions/harness-web-guard.ts .pi/extensions/lib/harness-web/run-cli.ts",
|
|
77
|
+
"check:ts": "tsc --noEmit --target ES2023 --lib ES2023 --moduleResolution nodenext --module nodenext --skipLibCheck .pi/extensions/custom-system-prompt.ts .pi/lib/harness-run-context.ts .pi/lib/harness-context-mode-policy.ts .pi/lib/harness-ui-state.ts .pi/extensions/harness-run-context.ts .pi/extensions/lib/harness-vcc-settings.ts .pi/extensions/dotenv-loader.ts .pi/extensions/00-posthog-network-bootstrap.ts .pi/extensions/lib/posthog-client.ts .pi/extensions/lib/posthog-node.d.ts .pi/extensions/lib/harness-posthog.ts .pi/extensions/lib/harness-paths.ts .pi/extensions/pi-model-router-harness.ts .pi/extensions/provider-payload-sanitize.ts .pi/extensions/harness-telemetry.ts .pi/extensions/harness-ask-user.ts .pi/extensions/harness-plan-approval.ts .pi/extensions/lib/ask-user/schema.ts .pi/extensions/lib/ask-user/types.ts .pi/extensions/lib/ask-user/validate.ts .pi/extensions/lib/ask-user/dialog.ts .pi/extensions/lib/ask-user/fallback.ts .pi/extensions/lib/ask-user/render.ts .pi/extensions/lib/plan-approval/types.ts .pi/extensions/lib/plan-approval/schema.ts .pi/extensions/lib/plan-approval/validate.ts .pi/extensions/lib/plan-approval/format-plan.ts .pi/extensions/lib/plan-approval/dialog.ts .pi/extensions/lib/plan-approval/render.ts .pi/extensions/lib/plan-approval/create-plan.ts .pi/extensions/harness-subagents.ts .pi/extensions/lib/harness-subagents-bridge.ts .pi/extensions/lib/harness-cocoindex-refresh.ts .pi/extensions/lib/harness-subagent-auth.ts .pi/extensions/lib/harness-subagent-policy.ts .pi/extensions/lib/harness-subagent-precheck.ts .pi/extensions/lib/harness-spawn-budget.ts .pi/extensions/lib/spawn-policy.ts vendor/pi-subagents/src/agents.ts vendor/pi-subagents/src/subagents.ts .pi/extensions/trace-recorder.ts .pi/extensions/observation-bus.ts .pi/extensions/drift-monitor.ts .pi/extensions/policy-gate.ts .pi/extensions/budget-guard.ts .pi/extensions/debate-orchestrator.ts .pi/extensions/harness-debate-tools.ts .pi/extensions/lib/debate-bus-core.ts .pi/extensions/lib/debate-bus-state.ts .pi/extensions/lib/plan-debate-gate.ts .pi/extensions/lib/plan-debate-id.ts .pi/extensions/lib/plan-messenger.ts .pi/extensions/lib/plan-debate-envelope.ts .pi/extensions/lib/plan-review-integrator-rules.ts .pi/extensions/lib/plan-scope-guard.ts .pi/extensions/lib/plan-debate-write-guard.ts .pi/extensions/lib/plan-debate-lane.ts .pi/extensions/lib/plan-debate-round-status.ts .pi/extensions/harness-live-widget.ts .pi/extensions/sentrux-rules-sync.ts .pi/extensions/custom-header.ts .pi/extensions/harness-web-tools.ts .pi/extensions/harness-web-guard.ts .pi/extensions/lib/harness-web/run-cli.ts",
|
|
78
78
|
"vendor:sync-router": "bash .pi/scripts/vendor-sync-pi-model-router.sh",
|
|
79
79
|
"vendor:sync-vcc": "bash .pi/scripts/vendor-sync-pi-vcc.sh",
|
|
80
80
|
"vendor:sync-subagents": "bash .pi/scripts/vendor-sync-pi-subagents.sh",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"format": "biome format --write",
|
|
85
85
|
"format:check": "biome format",
|
|
86
86
|
"prepare": "lefthook install",
|
|
87
|
-
"test": "node --test test/harness-verify.test.mjs test/harness-ask-user.test.mjs test/harness-subagents-loader.test.mjs test/harness-subagent-precheck.test.mjs test/sentrux-rules-sync.test.mjs test/harness-budget-guard.test.mjs && node .pi/harness/evals/smoke/smoke-harness-plan.mjs --fixture && npx -y tsx --test test/harness-vcc-settings.test.ts test/harness-live-widget-status.test.ts test/harness-plan-phase-policy.test.mjs test/harness-subagent-policy.test.mjs test/harness-spawn-budget.test.mjs test/harness-spawn-parse.test.mjs test/harness-schema-validate.test.mjs test/harness-turn-routing.test.mjs test/harness-budget-enforce.test.mjs test/harness-submit-policy.test.mjs test/plan-approval-format.test.mjs test/plan-approval-dialog.test.mjs test/plan-approval-sync.test.mjs test/plan-create-plan.test.mjs test/plan-review-format.test.mjs test/debate-plan-phase.test.mjs test/plan-debate-eligibility.test.mjs test/plan-messenger-gate.test.mjs test/plan-debate-lane-apply.test.mjs",
|
|
87
|
+
"test": "node --test test/harness-verify.test.mjs test/posthog-client.test.mjs test/harness-ask-user.test.mjs test/harness-subagents-loader.test.mjs test/harness-subagent-precheck.test.mjs test/sentrux-rules-sync.test.mjs test/harness-budget-guard.test.mjs && node .pi/harness/evals/smoke/smoke-harness-plan.mjs --fixture && npx -y tsx --test test/harness-vcc-settings.test.ts test/harness-run-context-postrun.test.mjs test/harness-tool-payload.test.mjs test/harness-live-widget-status.test.ts test/harness-plan-phase-policy.test.mjs test/harness-context-mode-policy.test.mjs test/harness-subprocess-bootstrap.test.mjs test/harness-subagent-policy.test.mjs test/harness-subagent-precheck-topology.test.mjs test/plan-approval-readiness.test.mjs test/harness-spawn-budget.test.mjs test/harness-spawn-parse.test.mjs test/harness-schema-validate.test.mjs test/harness-turn-routing.test.mjs test/harness-budget-enforce.test.mjs test/harness-submit-policy.test.mjs test/plan-approval-format.test.mjs test/plan-approval-dialog.test.mjs test/plan-approval-sync.test.mjs test/plan-create-plan.test.mjs test/plan-review-format.test.mjs test/debate-plan-phase.test.mjs test/plan-debate-eligibility.test.mjs test/plan-messenger-gate.test.mjs test/plan-debate-lane-apply.test.mjs",
|
|
88
88
|
"test:vcc": "npx -y tsx --test vendor/pi-vcc/tests/*.test.ts",
|
|
89
89
|
"harness:sentrux-bootstrap": "node .pi/scripts/harness-sentrux-bootstrap.mjs",
|
|
90
90
|
"harness:sentrux-sync": "node .pi/scripts/sentrux-rules-sync.mjs --force",
|
|
@@ -108,7 +108,8 @@
|
|
|
108
108
|
"croner": "^9.0.0",
|
|
109
109
|
"jimp": "^1.6.1",
|
|
110
110
|
"nanoid": "^5.1.5",
|
|
111
|
-
"posthog-node": "^5.30.6"
|
|
111
|
+
"posthog-node": "^5.30.6",
|
|
112
|
+
"undici": "^7.16.0"
|
|
112
113
|
},
|
|
113
114
|
"overrides": {
|
|
114
115
|
"@mariozechner/pi-agent-core": "npm:@earendil-works/pi-agent-core@0.74.1",
|