ultimate-pi 0.3.0 → 0.3.1
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.
|
@@ -178,6 +178,24 @@ export default function policyGate(pi: ExtensionAPI) {
|
|
|
178
178
|
pi.on("before_agent_start", async (event) => {
|
|
179
179
|
const bootstrapPrompt = isBootstrapPrompt(event.prompt);
|
|
180
180
|
const abortSignal = hasAbortSignal(event.prompt);
|
|
181
|
+
|
|
182
|
+
// /harness-setup instructions mention `harness-plan` (e.g. gh label text). That
|
|
183
|
+
// substring must not force inferPhase() to "plan" or bootstrap stays blocked.
|
|
184
|
+
if (bootstrapPrompt) {
|
|
185
|
+
state.phase = "execute";
|
|
186
|
+
state.approvedPlan = true;
|
|
187
|
+
state.planId = null;
|
|
188
|
+
state.budgetBypass = true;
|
|
189
|
+
state.aborted = false;
|
|
190
|
+
state.abortReason = null;
|
|
191
|
+
state.abortedAt = null;
|
|
192
|
+
state.updatedAt = nowIso();
|
|
193
|
+
pi.appendEntry("harness-policy-state", state);
|
|
194
|
+
return {
|
|
195
|
+
systemPrompt: `${event.systemPrompt}\n\n[PolicyGate]\nPhase=${state.phase}; ApprovedPlan=${state.approvedPlan}; PlanId=${state.planId ?? "none"}; Aborted=${state.aborted}; Bootstrap=harness-setup.`,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
|
|
181
199
|
if (abortSignal) {
|
|
182
200
|
state.phase = "plan";
|
|
183
201
|
state.approvedPlan = false;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Strip provider-specific fields from LLM request payloads before HTTP send.
|
|
3
|
+
*
|
|
4
|
+
* Strict OpenAI-compatible gateways return 400 when assistant history includes
|
|
5
|
+
* a top-level `reasoning` key (Cursor/thinking transcripts). Pi builds clean
|
|
6
|
+
* chat params; this is a safety net for any extra fields that slip through.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type {
|
|
10
|
+
BeforeProviderRequestEvent,
|
|
11
|
+
ExtensionAPI,
|
|
12
|
+
} from "@mariozechner/pi-coding-agent";
|
|
13
|
+
|
|
14
|
+
const CHAT_MESSAGE_EXTRA_KEYS = [
|
|
15
|
+
"reasoning",
|
|
16
|
+
"reasoning_text",
|
|
17
|
+
"chain_of_thought",
|
|
18
|
+
"chainOfThought",
|
|
19
|
+
"thinking",
|
|
20
|
+
"thought",
|
|
21
|
+
] as const;
|
|
22
|
+
|
|
23
|
+
function stripExtraChatFields(message: unknown): unknown {
|
|
24
|
+
if (
|
|
25
|
+
message === null ||
|
|
26
|
+
typeof message !== "object" ||
|
|
27
|
+
Array.isArray(message)
|
|
28
|
+
) {
|
|
29
|
+
return message;
|
|
30
|
+
}
|
|
31
|
+
const m = message as Record<string, unknown>;
|
|
32
|
+
if (typeof m.role !== "string") {
|
|
33
|
+
return message;
|
|
34
|
+
}
|
|
35
|
+
let touched = false;
|
|
36
|
+
const next = { ...m };
|
|
37
|
+
for (const k of CHAT_MESSAGE_EXTRA_KEYS) {
|
|
38
|
+
if (k in next) {
|
|
39
|
+
delete next[k];
|
|
40
|
+
touched = true;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return touched ? next : message;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function sanitizePayload(payload: unknown): unknown {
|
|
47
|
+
if (payload === null || typeof payload !== "object") {
|
|
48
|
+
return payload;
|
|
49
|
+
}
|
|
50
|
+
const body = payload as Record<string, unknown>;
|
|
51
|
+
const rawMessages = body.messages;
|
|
52
|
+
if (Array.isArray(rawMessages)) {
|
|
53
|
+
const messages = rawMessages.map(stripExtraChatFields);
|
|
54
|
+
if (messages.some((m, i) => m !== rawMessages[i])) {
|
|
55
|
+
return { ...body, messages };
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return payload;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
export default function providerPayloadSanitize(pi: ExtensionAPI) {
|
|
63
|
+
pi.on("before_provider_request", (event: BeforeProviderRequestEvent) => {
|
|
64
|
+
return sanitizePayload(event.payload);
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -59,6 +59,10 @@ Run from the **project root** (the external repo root, not ultimate-pi unless th
|
|
|
59
59
|
```bash
|
|
60
60
|
mkdir -p ./raw .pi/harness/specs .pi/harness/runs .pi/harness/incidents .pi/harness/debates
|
|
61
61
|
|
|
62
|
+
# Copy JSON schemas + specs README from the package so plan-packet.schema.json exists
|
|
63
|
+
# in the target repo immediately (before graphify or policy-gated planning).
|
|
64
|
+
node "$UP_PKG/.pi/scripts/harness-seed-project-contracts.mjs" "$(pwd)"
|
|
65
|
+
|
|
62
66
|
# Bundled with ultimate-pi harness; $UP_PKG is set in Step 0
|
|
63
67
|
bash "$UP_PKG/.pi/scripts/harness-graphify-bootstrap.sh"
|
|
64
68
|
# Developing ultimate-pi from repo root: UP_PKG="$(pwd)" then same command
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Copy harness JSON contracts (and specs README) from the installed ultimate-pi
|
|
4
|
+
* package into the current project. External repos get `.pi/harness/specs/` before
|
|
5
|
+
* graphify or /harness-plan so paths like plan-packet.schema.json resolve locally.
|
|
6
|
+
*
|
|
7
|
+
* Usage:
|
|
8
|
+
* node "$UP_PKG/.pi/scripts/harness-seed-project-contracts.mjs" [PROJECT_ROOT]
|
|
9
|
+
*
|
|
10
|
+
* PROJECT_ROOT defaults to process.cwd(). Package root is derived from this file
|
|
11
|
+
* (the script always lives under the shipped ultimate-pi package).
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
import { copyFile, mkdir, readdir } from "node:fs/promises";
|
|
15
|
+
import { join, dirname } from "node:path";
|
|
16
|
+
import { fileURLToPath } from "node:url";
|
|
17
|
+
|
|
18
|
+
const SCRIPT_DIR = dirname(fileURLToPath(import.meta.url));
|
|
19
|
+
const UP_PKG = join(SCRIPT_DIR, "..", "..");
|
|
20
|
+
const SPEC_SRC = join(UP_PKG, ".pi", "harness", "specs");
|
|
21
|
+
|
|
22
|
+
const projectRoot = process.argv[2] || process.cwd();
|
|
23
|
+
const specDest = join(projectRoot, ".pi", "harness", "specs");
|
|
24
|
+
|
|
25
|
+
async function main() {
|
|
26
|
+
const names = await readdir(SPEC_SRC);
|
|
27
|
+
const toCopy = names.filter(
|
|
28
|
+
(n) => n.endsWith(".schema.json") || n === "README.md",
|
|
29
|
+
);
|
|
30
|
+
if (toCopy.length === 0) {
|
|
31
|
+
console.error(
|
|
32
|
+
"harness-seed-project-contracts: no schema files under",
|
|
33
|
+
SPEC_SRC,
|
|
34
|
+
);
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
37
|
+
await mkdir(specDest, { recursive: true });
|
|
38
|
+
for (const name of toCopy) {
|
|
39
|
+
await copyFile(join(SPEC_SRC, name), join(specDest, name));
|
|
40
|
+
}
|
|
41
|
+
console.log(
|
|
42
|
+
`harness-seed-project-contracts: copied ${toCopy.length} file(s) -> ${specDest}`,
|
|
43
|
+
);
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
main().catch((err) => {
|
|
47
|
+
console.error(err);
|
|
48
|
+
process.exit(1);
|
|
49
|
+
});
|
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,14 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to this project are documented in this file.
|
|
4
4
|
|
|
5
|
+
## [v0.3.1] — 2026-05-15
|
|
6
|
+
|
|
7
|
+
### 🐛 Fixes
|
|
8
|
+
|
|
9
|
+
- **External `/harness-setup`**: policy gate no longer forces **plan** phase because the setup doc mentions `harness-plan` (e.g. `gh label create "harness-plan"`).
|
|
10
|
+
- **Harness specs in consumer repos**: copy `*.schema.json` and specs `README` from the package via `harness-seed-project-contracts.mjs` as part of setup (so `plan-packet.schema.json` exists before planning).
|
|
11
|
+
- **Strict LLM gateways**: new `provider-payload-sanitize` extension removes disallowed top-level fields (`reasoning`, etc.) from `messages` before provider requests (avoids 400 “Extra inputs … reasoning” on some OpenAI-compatible APIs).
|
|
12
|
+
|
|
5
13
|
## [v0.3.0] — 2026-05-15
|
|
6
14
|
|
|
7
15
|
### 📦 Release
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ultimate-pi",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.1",
|
|
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",
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
"@mariozechner/pi-coding-agent": "*"
|
|
76
76
|
},
|
|
77
77
|
"scripts": {
|
|
78
|
-
"check:ts": "tsc --noEmit --target ES2023 --lib ES2023 --moduleResolution nodenext --module nodenext --skipLibCheck .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/harness-telemetry.ts .pi/extensions/trace-recorder.ts .pi/extensions/observation-bus.ts .pi/extensions/drift-monitor.ts .pi/extensions/sentrux-rules-sync.ts .pi/extensions/custom-header.ts",
|
|
78
|
+
"check:ts": "tsc --noEmit --target ES2023 --lib ES2023 --moduleResolution nodenext --module nodenext --skipLibCheck .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/trace-recorder.ts .pi/extensions/observation-bus.ts .pi/extensions/drift-monitor.ts .pi/extensions/sentrux-rules-sync.ts .pi/extensions/custom-header.ts",
|
|
79
79
|
"vendor:sync-router": "bash .pi/scripts/vendor-sync-pi-model-router.sh",
|
|
80
80
|
"lint": "biome check",
|
|
81
81
|
"lint:fix": "biome check --fix",
|