ultimate-pi 0.11.0 → 0.12.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-debate-plan/SKILL.md +44 -0
- package/.agents/skills/harness-decisions/SKILL.md +1 -1
- package/.agents/skills/harness-orchestration/SKILL.md +54 -28
- package/.agents/skills/harness-plan/SKILL.md +15 -20
- package/.pi/agents/harness/adversary.md +0 -1
- package/.pi/agents/harness/evaluator.md +0 -1
- package/.pi/agents/harness/executor.md +1 -2
- package/.pi/agents/harness/incident-recorder.md +0 -1
- package/.pi/agents/harness/meta-optimizer.md +0 -1
- package/.pi/agents/harness/planning/decompose.md +3 -4
- package/.pi/agents/harness/planning/execution-plan-author.md +30 -0
- package/.pi/agents/harness/planning/hypothesis-validator.md +23 -0
- package/.pi/agents/harness/planning/hypothesis.md +3 -4
- package/.pi/agents/harness/planning/plan-adversary.md +10 -42
- package/.pi/agents/harness/planning/plan-evaluator.md +18 -0
- package/.pi/agents/harness/planning/review-integrator.md +23 -0
- package/.pi/agents/harness/planning/scout-graphify.md +11 -5
- package/.pi/agents/harness/planning/scout-semantic.md +11 -6
- package/.pi/agents/harness/planning/scout-structure.md +12 -6
- package/.pi/agents/harness/planning/sprint-contract-auditor.md +18 -0
- package/.pi/agents/harness/planning/stack-researcher.md +24 -0
- package/.pi/agents/harness/tie-breaker.md +0 -1
- package/.pi/agents/harness/trace-librarian.md +0 -1
- package/.pi/extensions/debate-orchestrator.ts +90 -53
- package/.pi/extensions/harness-plan-approval.ts +2 -2
- package/.pi/extensions/harness-run-context.ts +145 -5
- package/.pi/extensions/harness-subagents.ts +2 -2
- package/.pi/extensions/lib/harness-posthog.ts +6 -1
- package/.pi/extensions/lib/harness-spawn-budget.ts +75 -0
- package/.pi/extensions/lib/harness-subagent-auth.ts +123 -0
- package/.pi/extensions/lib/{harness-subagents/harness-subagent-policy.ts → harness-subagent-policy.ts} +3 -6
- package/.pi/extensions/lib/harness-subagent-precheck.ts +95 -0
- package/.pi/extensions/lib/harness-subagents-bridge.ts +176 -0
- package/.pi/extensions/lib/plan-approval/create-plan.ts +4 -7
- package/.pi/extensions/lib/plan-approval/plan-review.ts +1 -1
- package/.pi/extensions/lib/plan-approval/types.ts +7 -1
- package/.pi/extensions/lib/plan-debate-envelope.ts +84 -0
- package/.pi/extensions/lib/{harness-subagents/spawn-policy.ts → spawn-policy.ts} +1 -0
- package/.pi/extensions/policy-gate.ts +1 -1
- package/.pi/extensions/review-integrity.ts +48 -29
- package/.pi/harness/agents.manifest.json +37 -25
- package/.pi/harness/docs/adrs/0032-harness-command-orchestration.md +4 -3
- package/.pi/harness/docs/adrs/0033-parent-orchestrated-planning.md +1 -1
- package/.pi/harness/docs/adrs/0035-plan-phase-review-gate.md +27 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/artifacts/review-round-r1.yaml +25 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/artifacts/review-round-r4.yaml +26 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/artifacts/sprint-audit-r4.yaml +5 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/plan-packet.yaml +196 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/plan-review.md +14 -0
- package/.pi/harness/evals/smoke/fixtures/plan-phase/minimal-med/research-brief.yaml +32 -0
- package/.pi/harness/evals/smoke/run-context.fixture.json +1 -1
- package/.pi/harness/evals/smoke/smoke-harness-plan.mjs +88 -0
- package/.pi/harness/specs/harness-posthog-event.schema.json +6 -1
- package/.pi/harness/specs/plan-execution-plan-brief.schema.json +13 -0
- package/.pi/harness/specs/plan-execution-plan.schema.json +255 -0
- package/.pi/harness/specs/plan-packet.schema.json +14 -5
- package/.pi/harness/specs/plan-review-round-draft.schema.json +68 -0
- package/.pi/harness/specs/plan-sprint-audit-turn.schema.json +29 -0
- package/.pi/harness/specs/plan-stack-brief.schema.json +65 -0
- package/.pi/harness/specs/plan-validation-turn.schema.json +42 -0
- package/.pi/harness/specs/round-result.schema.json +16 -9
- package/.pi/lib/debate-orchestrator-types.ts +38 -0
- package/.pi/lib/harness-agent-discovery.mjs +81 -0
- package/.pi/lib/harness-run-context.ts +64 -38
- package/.pi/lib/harness-yaml.mjs +73 -0
- package/.pi/lib/harness-yaml.ts +90 -0
- package/.pi/prompts/harness-auto.md +13 -11
- package/.pi/prompts/harness-critic.md +2 -2
- package/.pi/prompts/harness-eval.md +3 -3
- package/.pi/prompts/harness-incident.md +2 -2
- package/.pi/prompts/harness-plan.md +79 -93
- package/.pi/prompts/harness-review.md +2 -2
- package/.pi/prompts/harness-router-tune.md +1 -1
- package/.pi/prompts/harness-run.md +2 -2
- package/.pi/prompts/harness-setup.md +15 -6
- package/.pi/prompts/harness-trace.md +2 -2
- package/.pi/scripts/harness-agents-manifest.mjs +1 -1
- package/.pi/scripts/harness-verify.mjs +28 -19
- package/.pi/scripts/validate-plan-dag.mjs +258 -0
- package/.pi/scripts/vendor-sync-pi-subagents.sh +19 -0
- package/CHANGELOG.md +12 -0
- package/THIRD_PARTY_NOTICES.md +8 -0
- package/biome.json +2 -2
- package/package.json +6 -4
- package/.pi/agents/harness/planner.md +0 -13
- package/.pi/agents/harness/planning/hypothesis-eval.md +0 -59
- package/.pi/agents/harness/planning/planner.md +0 -20
- package/.pi/extensions/lib/harness-subagents/agent-loader.ts +0 -126
- package/.pi/extensions/lib/harness-subagents/agent-manifest.ts +0 -119
- package/.pi/extensions/lib/harness-subagents/agent-parser.ts +0 -87
- package/.pi/extensions/lib/harness-subagents/blackboard-tool.ts +0 -118
- package/.pi/extensions/lib/harness-subagents/blackboard.ts +0 -175
- package/.pi/extensions/lib/harness-subagents/parent-ask-user-bridge.ts +0 -10
- package/.pi/extensions/lib/harness-subagents/parent-harness-ui-bridge.ts +0 -137
- package/.pi/extensions/lib/harness-subagents/parent-harness-ui-hooks.ts +0 -77
- package/.pi/extensions/lib/harness-subagents/types-blackboard.ts +0 -27
- package/.pi/extensions/lib/harness-subagents/vendored/agent-manager.ts +0 -558
- package/.pi/extensions/lib/harness-subagents/vendored/agent-runner.ts +0 -666
- package/.pi/extensions/lib/harness-subagents/vendored/agent-types.ts +0 -175
- package/.pi/extensions/lib/harness-subagents/vendored/context.ts +0 -59
- package/.pi/extensions/lib/harness-subagents/vendored/cross-extension-rpc.ts +0 -134
- package/.pi/extensions/lib/harness-subagents/vendored/custom-agents.ts +0 -5
- package/.pi/extensions/lib/harness-subagents/vendored/default-agents.ts +0 -123
- package/.pi/extensions/lib/harness-subagents/vendored/env.ts +0 -43
- package/.pi/extensions/lib/harness-subagents/vendored/group-join.ts +0 -144
- package/.pi/extensions/lib/harness-subagents/vendored/index.ts +0 -2460
- package/.pi/extensions/lib/harness-subagents/vendored/invocation-config.ts +0 -52
- package/.pi/extensions/lib/harness-subagents/vendored/memory.ts +0 -182
- package/.pi/extensions/lib/harness-subagents/vendored/model-resolver.ts +0 -92
- package/.pi/extensions/lib/harness-subagents/vendored/output-file.ts +0 -115
- package/.pi/extensions/lib/harness-subagents/vendored/prompts.ts +0 -103
- package/.pi/extensions/lib/harness-subagents/vendored/schedule-store.ts +0 -177
- package/.pi/extensions/lib/harness-subagents/vendored/schedule.ts +0 -416
- package/.pi/extensions/lib/harness-subagents/vendored/settings.ts +0 -210
- package/.pi/extensions/lib/harness-subagents/vendored/skill-loader.ts +0 -108
- package/.pi/extensions/lib/harness-subagents/vendored/types.ts +0 -187
- package/.pi/extensions/lib/harness-subagents/vendored/ui/agent-widget.ts +0 -639
- package/.pi/extensions/lib/harness-subagents/vendored/ui/conversation-viewer.ts +0 -324
- package/.pi/extensions/lib/harness-subagents/vendored/ui/schedule-menu.ts +0 -110
- package/.pi/extensions/lib/harness-subagents/vendored/usage.ts +0 -71
- package/.pi/extensions/lib/harness-subagents/vendored/worktree.ts +0 -195
- /package/.pi/extensions/{00-ultimate-pi-system-prompt.ts → custom-system-prompt.ts} +0 -0
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* validate-plan-dag — deterministic ExecutionPlan DAG checks (YAML packet in).
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { access } from "node:fs/promises";
|
|
7
|
+
import { constants } from "node:fs";
|
|
8
|
+
import { dirname, join, resolve } from "node:path";
|
|
9
|
+
import { fileURLToPath } from "node:url";
|
|
10
|
+
import { readYamlFile, writeYamlFile } from "../lib/harness-yaml.mjs";
|
|
11
|
+
|
|
12
|
+
const ROOT = join(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
13
|
+
|
|
14
|
+
const MINIMUMS = {
|
|
15
|
+
low: { phases: 2, work_items: 5, acceptance_checks: 3, risks: 0 },
|
|
16
|
+
med: { phases: 3, work_items: 8, acceptance_checks: 5, risks: 3 },
|
|
17
|
+
high: { phases: 4, work_items: 12, acceptance_checks: 8, risks: 3 },
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function fail(msg) {
|
|
21
|
+
console.error(`validate-plan-dag: FAIL: ${msg}`);
|
|
22
|
+
process.exit(1);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function ok(msg) {
|
|
26
|
+
console.log(` ✓ ${msg}`);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function topoSort(workItems) {
|
|
30
|
+
const ids = new Set(workItems.map((w) => w.work_item_id));
|
|
31
|
+
const adj = new Map();
|
|
32
|
+
for (const w of workItems) {
|
|
33
|
+
adj.set(w.work_item_id, (w.depends_on ?? []).filter((d) => ids.has(d)));
|
|
34
|
+
}
|
|
35
|
+
const visited = new Set();
|
|
36
|
+
const stack = new Set();
|
|
37
|
+
const order = [];
|
|
38
|
+
const cycles = [];
|
|
39
|
+
|
|
40
|
+
function dfs(n, path) {
|
|
41
|
+
if (stack.has(n)) {
|
|
42
|
+
cycles.push([...path, n]);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (visited.has(n)) return;
|
|
46
|
+
visited.add(n);
|
|
47
|
+
stack.add(n);
|
|
48
|
+
for (const d of adj.get(n) ?? []) dfs(d, [...path, n]);
|
|
49
|
+
stack.delete(n);
|
|
50
|
+
order.push(n);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
for (const id of ids) dfs(id, []);
|
|
54
|
+
order.reverse();
|
|
55
|
+
return { order, cycles };
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function computeCriticalPath(workItems) {
|
|
59
|
+
const ids = new Set(workItems.map((w) => w.work_item_id));
|
|
60
|
+
const len = new Map();
|
|
61
|
+
for (const w of workItems) len.set(w.work_item_id, 0);
|
|
62
|
+
let changed = true;
|
|
63
|
+
while (changed) {
|
|
64
|
+
changed = false;
|
|
65
|
+
for (const w of workItems) {
|
|
66
|
+
const deps = (w.depends_on ?? []).filter((d) => ids.has(d));
|
|
67
|
+
const base = deps.length === 0 ? 0 : Math.max(...deps.map((d) => len.get(d) ?? 0)) + 1;
|
|
68
|
+
if (base > (len.get(w.work_item_id) ?? 0)) {
|
|
69
|
+
len.set(w.work_item_id, base);
|
|
70
|
+
changed = true;
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
const maxLen = Math.max(0, ...len.values());
|
|
75
|
+
const end = workItems.filter((w) => len.get(w.work_item_id) === maxLen).map((w) => w.work_item_id);
|
|
76
|
+
// Backtrack one longest path
|
|
77
|
+
const path = [];
|
|
78
|
+
let cur = end[0];
|
|
79
|
+
if (!cur) return [];
|
|
80
|
+
const byId = new Map(workItems.map((w) => [w.work_item_id, w]));
|
|
81
|
+
while (cur) {
|
|
82
|
+
path.unshift(cur);
|
|
83
|
+
const w = byId.get(cur);
|
|
84
|
+
const deps = (w?.depends_on ?? []).filter((d) => ids.has(d));
|
|
85
|
+
if (deps.length === 0) break;
|
|
86
|
+
cur = deps.reduce((a, b) => ((len.get(a) ?? 0) >= (len.get(b) ?? 0) ? a : b));
|
|
87
|
+
}
|
|
88
|
+
return path;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function validateExecutionPlan(packet, projectRoot = ROOT) {
|
|
92
|
+
const errors = [];
|
|
93
|
+
const ep = packet.execution_plan;
|
|
94
|
+
if (!ep) {
|
|
95
|
+
errors.push("execution_plan required");
|
|
96
|
+
return { status: "fail", errors, report: null };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
const risk = packet.risk_level ?? "med";
|
|
100
|
+
const min = MINIMUMS[risk] ?? MINIMUMS.med;
|
|
101
|
+
const phases = ep.phases ?? [];
|
|
102
|
+
const workItems = ep.work_items ?? [];
|
|
103
|
+
const conflicts = [];
|
|
104
|
+
|
|
105
|
+
if (phases.length < min.phases) {
|
|
106
|
+
errors.push(`need >= ${min.phases} phases for risk ${risk}`);
|
|
107
|
+
}
|
|
108
|
+
if (workItems.length < min.work_items) {
|
|
109
|
+
errors.push(`need >= ${min.work_items} work_items for risk ${risk}`);
|
|
110
|
+
}
|
|
111
|
+
const ac = packet.acceptance_checks ?? [];
|
|
112
|
+
if (ac.length < min.acceptance_checks) {
|
|
113
|
+
errors.push(`need >= ${min.acceptance_checks} acceptance_checks`);
|
|
114
|
+
}
|
|
115
|
+
if ((ep.risk_register ?? []).length < min.risks) {
|
|
116
|
+
errors.push(`need >= ${min.risks} risks for risk ${risk}`);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const phaseIds = new Set(phases.map((p) => p.phase_id));
|
|
120
|
+
const phaseIndex = new Map(phases.map((p, i) => [p.phase_id, i]));
|
|
121
|
+
const wiIds = new Set(workItems.map((w) => w.work_item_id));
|
|
122
|
+
|
|
123
|
+
for (const p of phases) {
|
|
124
|
+
if (!p.exit_criteria?.length) errors.push(`phase ${p.phase_id} missing exit_criteria`);
|
|
125
|
+
if (!p.work_item_ids?.length) errors.push(`phase ${p.phase_id} has no work items`);
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
const wiInPhase = new Set();
|
|
129
|
+
for (const w of workItems) {
|
|
130
|
+
if (!phaseIds.has(w.phase_id)) {
|
|
131
|
+
errors.push(`work_item ${w.work_item_id} unknown phase_id`);
|
|
132
|
+
}
|
|
133
|
+
wiInPhase.add(w.work_item_id);
|
|
134
|
+
for (const d of w.depends_on ?? []) {
|
|
135
|
+
if (!wiIds.has(d)) errors.push(`work_item ${w.work_item_id} depends_on missing ${d}`);
|
|
136
|
+
}
|
|
137
|
+
if (!w.non_code && (!w.files || w.files.length === 0)) {
|
|
138
|
+
errors.push(`work_item ${w.work_item_id} needs files[] or non_code: true`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
for (const p of phases) {
|
|
143
|
+
for (const wid of p.work_item_ids ?? []) {
|
|
144
|
+
if (!wiIds.has(wid)) errors.push(`phase ${p.phase_id} references missing ${wid}`);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
const { order, cycles } = topoSort(workItems);
|
|
149
|
+
if (cycles.length) errors.push(`cycle detected: ${JSON.stringify(cycles[0])}`);
|
|
150
|
+
|
|
151
|
+
// File conflicts
|
|
152
|
+
for (let i = 0; i < workItems.length; i++) {
|
|
153
|
+
for (let j = i + 1; j < workItems.length; j++) {
|
|
154
|
+
const a = workItems[i];
|
|
155
|
+
const b = workItems[j];
|
|
156
|
+
const filesA = new Set(a.files ?? []);
|
|
157
|
+
const overlap = (b.files ?? []).filter((f) => filesA.has(f));
|
|
158
|
+
if (overlap.length === 0) continue;
|
|
159
|
+
const reachable = (from, to, seen = new Set()) => {
|
|
160
|
+
if (from === to) return true;
|
|
161
|
+
if (seen.has(from)) return false;
|
|
162
|
+
seen.add(from);
|
|
163
|
+
const w = workItems.find((x) => x.work_item_id === from);
|
|
164
|
+
for (const d of w?.depends_on ?? []) {
|
|
165
|
+
if (reachable(d, to, seen)) return true;
|
|
166
|
+
}
|
|
167
|
+
return false;
|
|
168
|
+
};
|
|
169
|
+
if (!reachable(a.work_item_id, b.work_item_id) && !reachable(b.work_item_id, a.work_item_id)) {
|
|
170
|
+
if ((phaseIndex.get(a.phase_id) ?? 0) === (phaseIndex.get(b.phase_id) ?? 0)) {
|
|
171
|
+
conflicts.push(
|
|
172
|
+
`file overlap ${overlap.join(",")} between ${a.work_item_id} and ${b.work_item_id} without dependency`,
|
|
173
|
+
);
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
const computedCp = computeCriticalPath(workItems);
|
|
180
|
+
const authorCp = ep.schedule_metadata?.critical_path_work_item_ids ?? [];
|
|
181
|
+
if (computedCp.length >= 3 && authorCp.length) {
|
|
182
|
+
const same =
|
|
183
|
+
authorCp.length === computedCp.length &&
|
|
184
|
+
authorCp.every((id, i) => id === computedCp[i]);
|
|
185
|
+
if (!same) {
|
|
186
|
+
errors.push(
|
|
187
|
+
`critical_path mismatch author=${authorCp.join("→")} computed=${computedCp.join("→")}`,
|
|
188
|
+
);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
const acIds = new Set(
|
|
193
|
+
ac.map((c) => (typeof c === "string" ? c : c.id)).filter(Boolean),
|
|
194
|
+
);
|
|
195
|
+
for (const w of workItems) {
|
|
196
|
+
for (const acid of w.acceptance_check_ids ?? []) {
|
|
197
|
+
if (!acIds.has(acid)) errors.push(`${w.work_item_id} references orphan ${acid}`);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
for (const acid of acIds) {
|
|
201
|
+
const used = workItems.some((w) => (w.acceptance_check_ids ?? []).includes(acid));
|
|
202
|
+
if (!used) errors.push(`orphan acceptance check ${acid}`);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
const status = errors.length === 0 && conflicts.length === 0 ? "pass" : "fail";
|
|
206
|
+
const report = {
|
|
207
|
+
status,
|
|
208
|
+
topological_order: order,
|
|
209
|
+
cycles,
|
|
210
|
+
conflicts: [...conflicts, ...errors],
|
|
211
|
+
};
|
|
212
|
+
return { status, errors: [...errors, ...conflicts], report };
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
async function main() {
|
|
216
|
+
const args = process.argv.slice(2);
|
|
217
|
+
let packetPath = null;
|
|
218
|
+
let writeBack = false;
|
|
219
|
+
for (let i = 0; i < args.length; i++) {
|
|
220
|
+
if (args[i] === "--packet" && args[i + 1]) packetPath = args[++i];
|
|
221
|
+
else if (args[i] === "--write") writeBack = true;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
if (!packetPath) {
|
|
225
|
+
console.error("Usage: validate-plan-dag.mjs --packet <plan-packet.yaml> [--write]");
|
|
226
|
+
process.exit(2);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
const abs = resolve(packetPath);
|
|
230
|
+
try {
|
|
231
|
+
await access(abs, constants.R_OK);
|
|
232
|
+
} catch {
|
|
233
|
+
fail(`cannot read ${abs}`);
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const packet = await readYamlFile(abs);
|
|
237
|
+
const { status, errors, report } = validateExecutionPlan(packet, dirname(abs));
|
|
238
|
+
|
|
239
|
+
if (writeBack && report && packet.execution_plan) {
|
|
240
|
+
packet.execution_plan.dag_validation = {
|
|
241
|
+
status: report.status,
|
|
242
|
+
topological_order: report.topological_order,
|
|
243
|
+
cycles: report.cycles,
|
|
244
|
+
conflicts: report.conflicts,
|
|
245
|
+
};
|
|
246
|
+
await writeYamlFile(abs, packet);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if (status !== "pass") {
|
|
250
|
+
for (const e of errors) console.error(` - ${e}`);
|
|
251
|
+
fail("validation failed");
|
|
252
|
+
}
|
|
253
|
+
ok(`DAG validation pass (${report.topological_order.length} work items)`);
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
if (process.argv[1] && fileURLToPath(import.meta.url) === resolve(process.argv[1])) {
|
|
257
|
+
main();
|
|
258
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# Re-fetch upstream pi-subagents from narumiruna/pi-extensions.
|
|
3
|
+
set -euo pipefail
|
|
4
|
+
ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
|
|
5
|
+
VEND="$ROOT/vendor/pi-subagents"
|
|
6
|
+
BASE="https://raw.githubusercontent.com/narumiruna/pi-extensions/main/extensions/pi-subagents"
|
|
7
|
+
|
|
8
|
+
mkdir -p "$VEND/src"
|
|
9
|
+
curl -fsSL "$BASE/LICENSE" -o "$VEND/LICENSE"
|
|
10
|
+
curl -fsSL "$BASE/src/subagents.ts" -o "$VEND/src/subagents.upstream.ts"
|
|
11
|
+
|
|
12
|
+
# Preserve ultimate-pi harness extensions (agents.ts, harness patches applied to subagents.ts manually or via merge).
|
|
13
|
+
if [[ ! -f "$VEND/src/agents.ts" ]]; then
|
|
14
|
+
curl -fsSL "$BASE/src/agents.ts" -o "$VEND/src/agents.ts"
|
|
15
|
+
fi
|
|
16
|
+
|
|
17
|
+
sed -i 's/from "typebox"/from "@sinclair\/typebox"/g' "$VEND/src/subagents.upstream.ts" 2>/dev/null || true
|
|
18
|
+
|
|
19
|
+
echo "Fetched upstream into $VEND/src/subagents.upstream.ts — merge harness changes into subagents.ts before commit."
|
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,18 @@ All notable changes to this project are documented in this file.
|
|
|
4
4
|
|
|
5
5
|
## [Unreleased]
|
|
6
6
|
|
|
7
|
+
## [v0.12.0] — 2026-05-18
|
|
8
|
+
|
|
9
|
+
### ✨ Features
|
|
10
|
+
|
|
11
|
+
- **Harness subagents:** vendor `pi-subagents`; spawn budget, precheck, and policy bridge.
|
|
12
|
+
- **Router spawn auth:** forward concrete provider/model + API key to subprocesses using `--no-extensions` (fixes planning scout 401 with `router/auto`).
|
|
13
|
+
- **Plan review gate:** debate orchestration, execution-plan schemas, `write_harness_yaml`, plan-phase smoke fixture (ADR 0035).
|
|
14
|
+
|
|
15
|
+
### 🔧 Chores
|
|
16
|
+
|
|
17
|
+
- Stop tracking harness runtime and local graphify artifacts in git.
|
|
18
|
+
|
|
7
19
|
## [v0.11.0] — 2026-05-17
|
|
8
20
|
|
|
9
21
|
### ✨ Features
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -14,3 +14,11 @@
|
|
|
14
14
|
- **License:** MIT (see upstream repository)
|
|
15
15
|
- **Pinned revision:** See [vendor/pi-vcc/UPSTREAM_PIN.md](vendor/pi-vcc/UPSTREAM_PIN.md)
|
|
16
16
|
- ultimate-pi loads it from [`vendor/pi-vcc`](vendor/pi-vcc) via [`.pi/extensions/ultimate-pi-vcc.ts`](.pi/extensions/ultimate-pi-vcc.ts). Harness configuration is env-only: `HARNESS_VCC_COMPACTION`, `HARNESS_VCC_DEBUG` ([`.pi/extensions/lib/harness-vcc-settings.ts`](.pi/extensions/lib/harness-vcc-settings.ts)). Maintainer refresh: `npm run vendor:sync-vcc`.
|
|
17
|
+
|
|
18
|
+
## pi-subagents (vendored)
|
|
19
|
+
|
|
20
|
+
- **Project:** https://github.com/narumiruna/pi-extensions (`extensions/pi-subagents`)
|
|
21
|
+
- **npm:** `@narumitw/pi-subagents@0.1.26`
|
|
22
|
+
- **License:** MIT ([vendor/pi-subagents/LICENSE](vendor/pi-subagents/LICENSE))
|
|
23
|
+
- **Pinned revision:** See [vendor/pi-subagents/UPSTREAM_PIN.md](vendor/pi-subagents/UPSTREAM_PIN.md)
|
|
24
|
+
- ultimate-pi loads it from [`vendor/pi-subagents`](vendor/pi-subagents) via [`.pi/extensions/harness-subagents.ts`](.pi/extensions/harness-subagents.ts) with harness discovery, spawn gates, and subprocess env. Maintainer refresh: `npm run vendor:sync-subagents`.
|
package/biome.json
CHANGED
|
@@ -14,8 +14,8 @@
|
|
|
14
14
|
"!graphify-books-out/**/*",
|
|
15
15
|
"!vendor/**/*",
|
|
16
16
|
"!.pi/harness/active-run.json",
|
|
17
|
-
"!.pi/harness/runs/**/run-context.
|
|
18
|
-
"!.pi/harness/runs/**/plan-packet.
|
|
17
|
+
"!.pi/harness/runs/**/run-context.yaml",
|
|
18
|
+
"!.pi/harness/runs/**/plan-packet.yaml"
|
|
19
19
|
]
|
|
20
20
|
},
|
|
21
21
|
"formatter": {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultimate-pi",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.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",
|
|
@@ -73,16 +73,17 @@
|
|
|
73
73
|
"@earendil-works/pi-coding-agent": "*"
|
|
74
74
|
},
|
|
75
75
|
"scripts": {
|
|
76
|
-
"check:ts": "tsc --noEmit --target ES2023 --lib ES2023 --moduleResolution nodenext --module nodenext --skipLibCheck .pi/extensions/
|
|
76
|
+
"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/fallback.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-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-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
77
|
"vendor:sync-router": "bash .pi/scripts/vendor-sync-pi-model-router.sh",
|
|
78
78
|
"vendor:sync-vcc": "bash .pi/scripts/vendor-sync-pi-vcc.sh",
|
|
79
|
+
"vendor:sync-subagents": "bash .pi/scripts/vendor-sync-pi-subagents.sh",
|
|
79
80
|
"release": "bash .pi/scripts/release.sh",
|
|
80
81
|
"lint": "biome check",
|
|
81
82
|
"lint:fix": "biome check --fix",
|
|
82
83
|
"format": "biome format --write",
|
|
83
84
|
"format:check": "biome format",
|
|
84
85
|
"prepare": "lefthook install",
|
|
85
|
-
"test": "node --test test/harness-verify.test.mjs test/harness-ask-user.test.mjs test/harness-subagents-loader.test.mjs test/harness-
|
|
86
|
+
"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-plan-phase-policy.test.mjs test/harness-subagent-policy.test.mjs test/harness-spawn-budget.test.mjs test/harness-turn-routing.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",
|
|
86
87
|
"test:vcc": "npx -y tsx --test vendor/pi-vcc/tests/*.test.ts",
|
|
87
88
|
"harness:sentrux-bootstrap": "node .pi/scripts/harness-sentrux-bootstrap.mjs",
|
|
88
89
|
"harness:sentrux-sync": "node .pi/scripts/sentrux-rules-sync.mjs --force",
|
|
@@ -96,7 +97,8 @@
|
|
|
96
97
|
"@earendil-works/pi-tui": "0.74.1",
|
|
97
98
|
"@sinclair/typebox": "^0.34.49",
|
|
98
99
|
"lefthook": "2.1.6",
|
|
99
|
-
"typescript": "^6.0.3"
|
|
100
|
+
"typescript": "^6.0.3",
|
|
101
|
+
"yaml": "^2.8.0"
|
|
100
102
|
},
|
|
101
103
|
"dependencies": {
|
|
102
104
|
"@posthog/pi": "latest",
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "DEPRECATED — relocated to harness/planning/. Do not spawn harness/planner."
|
|
3
|
-
tools: read
|
|
4
|
-
extensions: false
|
|
5
|
-
max_turns: 1
|
|
6
|
-
inherit_context: false
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
**Relocated:** plan-phase agents live under `harness/planning/` (scouts, plan-adversary).
|
|
10
|
-
|
|
11
|
-
Use `/harness-plan` in the parent session — do **not** spawn `harness/planner` or `harness/planning/planner`.
|
|
12
|
-
|
|
13
|
-
See `.pi/agents/harness/planning/` and `.pi/prompts/harness-plan.md`.
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Plan-phase blind hypothesis self-evaluation (read-only).
|
|
3
|
-
tools: read, grep, find, ls
|
|
4
|
-
disallowed_tools: write, edit, bash, ask_user, approve_plan, create_plan, Agent
|
|
5
|
-
extensions: false
|
|
6
|
-
thinking: medium
|
|
7
|
-
max_turns: 12
|
|
8
|
-
inherit_context: false
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
You are the **Harness hypothesis evaluator** — blind self-evaluation only.
|
|
12
|
-
|
|
13
|
-
## Mission
|
|
14
|
-
|
|
15
|
-
Score the hypothesis brief on research quality dimensions. You do **not** revise the hypothesis, build PlanPacket, or mutate anything.
|
|
16
|
-
|
|
17
|
-
## Input (strict)
|
|
18
|
-
|
|
19
|
-
You receive **only**:
|
|
20
|
-
|
|
21
|
-
- Original task statement
|
|
22
|
-
- `PlanHypothesisBrief` JSON
|
|
23
|
-
|
|
24
|
-
You must **not** use decomposition, scout findings, PlanPacket, or adversary output even if present in the prompt — ignore them.
|
|
25
|
-
|
|
26
|
-
## Scoring rubric
|
|
27
|
-
|
|
28
|
-
| Dimension | 90+ | 70–89 | <50 |
|
|
29
|
-
|-----------|-----|-------|--------|
|
|
30
|
-
| Novelty | Reframes problem | Novel combo | Known approach |
|
|
31
|
-
| Coherence | Implementation-ready | Minor gaps | Vague |
|
|
32
|
-
| Testability | Fully specified experiment | Clear direction | Unfalsifiable |
|
|
33
|
-
| Impact | Field-changing | Meaningful | Incremental |
|
|
34
|
-
|
|
35
|
-
**Relevance**: Does the primary hypothesis address the original task? (`passes` true/false + rationale).
|
|
36
|
-
|
|
37
|
-
Set `revision_recommended: true` when **testability** score < 70 or **relevance.passes** is false.
|
|
38
|
-
|
|
39
|
-
## Output (required JSON block)
|
|
40
|
-
|
|
41
|
-
```json
|
|
42
|
-
{
|
|
43
|
-
"schema_version": "1.0.0",
|
|
44
|
-
"dimensions": {
|
|
45
|
-
"novelty": { "score": 75, "rationale": "…" },
|
|
46
|
-
"coherence": { "score": 80, "rationale": "…" },
|
|
47
|
-
"testability": { "score": 85, "rationale": "…" },
|
|
48
|
-
"impact": { "score": 70, "rationale": "…" }
|
|
49
|
-
},
|
|
50
|
-
"relevance": {
|
|
51
|
-
"passes": true,
|
|
52
|
-
"rationale": "…"
|
|
53
|
-
},
|
|
54
|
-
"revision_recommended": false,
|
|
55
|
-
"human_summary": "…"
|
|
56
|
-
}
|
|
57
|
-
```
|
|
58
|
-
|
|
59
|
-
Match `PlanHypothesisEval` (`.pi/harness/specs/plan-hypothesis-eval.schema.json`).
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: "DEPRECATED — do not spawn. Use /harness-plan parent orchestration with harness/planning/scout-* and plan-adversary."
|
|
3
|
-
tools: read
|
|
4
|
-
extensions: false
|
|
5
|
-
max_turns: 1
|
|
6
|
-
inherit_context: false
|
|
7
|
-
---
|
|
8
|
-
|
|
9
|
-
This agent is **deprecated**. `/harness-plan` no longer spawns `harness/planning/planner`.
|
|
10
|
-
|
|
11
|
-
The parent orchestrator runs:
|
|
12
|
-
|
|
13
|
-
- `harness/planning/scout-graphify`
|
|
14
|
-
- `harness/planning/scout-structure`
|
|
15
|
-
- `harness/planning/scout-semantic` (skipped when `--quick`)
|
|
16
|
-
- `harness/planning/plan-adversary`
|
|
17
|
-
|
|
18
|
-
Then the parent calls `ask_user`, `approve_plan`, and `create_plan` in the main session.
|
|
19
|
-
|
|
20
|
-
Do not use this file except for manifest compatibility or project overrides.
|
|
@@ -1,126 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Recursive discovery: $UP_PKG/.pi/agents/** + project .pi/agents/** overrides.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { createHash } from "node:crypto";
|
|
6
|
-
import { type Dirent, existsSync, readdirSync, readFileSync } from "node:fs";
|
|
7
|
-
import { join, relative } from "node:path";
|
|
8
|
-
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
9
|
-
import { parseAgentMarkdown } from "./agent-parser.js";
|
|
10
|
-
import type { AgentConfig } from "./vendored/types.js";
|
|
11
|
-
|
|
12
|
-
export type AgentSource = "package" | "project" | "global";
|
|
13
|
-
|
|
14
|
-
export interface DiscoveredAgentFile {
|
|
15
|
-
id: string;
|
|
16
|
-
path: string;
|
|
17
|
-
source: AgentSource;
|
|
18
|
-
content: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
/** Reject path traversal and unsafe ids. */
|
|
22
|
-
export function isSafeAgentId(id: string): boolean {
|
|
23
|
-
if (!id || id.includes("..") || id.startsWith("/") || id.includes("\\")) {
|
|
24
|
-
return false;
|
|
25
|
-
}
|
|
26
|
-
return /^[a-zA-Z0-9][a-zA-Z0-9/_-]*$/.test(id);
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
function walkAgentsDir(
|
|
30
|
-
rootDir: string,
|
|
31
|
-
source: AgentSource,
|
|
32
|
-
out: Map<string, DiscoveredAgentFile>,
|
|
33
|
-
): void {
|
|
34
|
-
if (!existsSync(rootDir)) return;
|
|
35
|
-
|
|
36
|
-
const stack: string[] = [rootDir];
|
|
37
|
-
while (stack.length > 0) {
|
|
38
|
-
const dir = stack.pop()!;
|
|
39
|
-
let entries: Dirent[];
|
|
40
|
-
try {
|
|
41
|
-
entries = readdirSync(dir, { withFileTypes: true });
|
|
42
|
-
} catch {
|
|
43
|
-
continue;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
for (const entry of entries) {
|
|
47
|
-
const full = join(dir, entry.name);
|
|
48
|
-
if (entry.isDirectory()) {
|
|
49
|
-
stack.push(full);
|
|
50
|
-
continue;
|
|
51
|
-
}
|
|
52
|
-
if (!entry.name.endsWith(".md")) continue;
|
|
53
|
-
|
|
54
|
-
const rel = relative(rootDir, full).replace(/\\/g, "/");
|
|
55
|
-
const id = rel.replace(/\.md$/i, "");
|
|
56
|
-
if (!isSafeAgentId(id)) continue;
|
|
57
|
-
|
|
58
|
-
let content: string;
|
|
59
|
-
try {
|
|
60
|
-
content = readFileSync(full, "utf-8");
|
|
61
|
-
} catch {
|
|
62
|
-
continue;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
out.set(id, { id, path: full, source, content });
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
/**
|
|
71
|
-
* Discover agent files from package, global, and project (low → high priority).
|
|
72
|
-
*/
|
|
73
|
-
export function discoverAgentFiles(
|
|
74
|
-
cwd: string,
|
|
75
|
-
packageRoot: string,
|
|
76
|
-
): Map<string, DiscoveredAgentFile> {
|
|
77
|
-
const files = new Map<string, DiscoveredAgentFile>();
|
|
78
|
-
|
|
79
|
-
const packageAgents = join(packageRoot, ".pi", "agents");
|
|
80
|
-
const globalDir = join(getAgentDir(), "agents");
|
|
81
|
-
const projectDir = join(cwd, ".pi", "agents");
|
|
82
|
-
|
|
83
|
-
walkAgentsDir(packageAgents, "package", files);
|
|
84
|
-
walkAgentsDir(globalDir, "global", files);
|
|
85
|
-
walkAgentsDir(projectDir, "project", files);
|
|
86
|
-
|
|
87
|
-
return files;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
/** Load merged AgentConfig map (project overrides package for same id). */
|
|
91
|
-
export function loadHarnessAgents(
|
|
92
|
-
cwd: string,
|
|
93
|
-
packageRoot: string,
|
|
94
|
-
): Map<string, AgentConfig> {
|
|
95
|
-
const agents = new Map<string, AgentConfig>();
|
|
96
|
-
for (const file of discoverAgentFiles(cwd, packageRoot).values()) {
|
|
97
|
-
agents.set(file.id, parseAgentMarkdown(file.id, file.content, file.source));
|
|
98
|
-
}
|
|
99
|
-
return agents;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
export function sha256Content(content: string): string {
|
|
103
|
-
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
/** Package-only manifest entries (path → hash). */
|
|
107
|
-
export function loadPackageAgentHashes(
|
|
108
|
-
packageRoot: string,
|
|
109
|
-
): Map<string, { path: string; sha256: string }> {
|
|
110
|
-
const packageAgents = join(packageRoot, ".pi", "agents");
|
|
111
|
-
const out = new Map<string, { path: string; sha256: string }>();
|
|
112
|
-
const files = new Map<string, DiscoveredAgentFile>();
|
|
113
|
-
walkAgentsDir(packageAgents, "package", files);
|
|
114
|
-
for (const f of files.values()) {
|
|
115
|
-
out.set(f.id, { path: f.path, sha256: sha256Content(f.content) });
|
|
116
|
-
}
|
|
117
|
-
return out;
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
/** Legacy hook used by pi-subagents custom-agents.ts replacement. */
|
|
121
|
-
export function loadCustomAgents(
|
|
122
|
-
cwd: string,
|
|
123
|
-
packageRoot: string,
|
|
124
|
-
): Map<string, AgentConfig> {
|
|
125
|
-
return loadHarnessAgents(cwd, packageRoot);
|
|
126
|
-
}
|