ultimate-pi 0.10.1 → 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 +3 -3
- package/.agents/skills/harness-orchestration/SKILL.md +59 -25
- package/.agents/skills/harness-plan/SKILL.md +16 -15
- 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 +83 -0
- 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 +89 -0
- package/.pi/agents/harness/planning/plan-adversary.md +18 -0
- 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 +54 -0
- package/.pi/agents/harness/planning/scout-semantic.md +47 -0
- package/.pi/agents/harness/planning/scout-structure.md +50 -0
- 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-ask-user.ts +5 -0
- package/.pi/extensions/harness-plan-approval.ts +137 -3
- package/.pi/extensions/harness-run-context.ts +146 -6
- package/.pi/extensions/harness-subagents.ts +10 -5
- package/.pi/extensions/harness-web-tools.ts +2 -0
- package/.pi/extensions/lib/extension-load-guard.ts +39 -0
- 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} +34 -9
- 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 +9 -7
- package/.pi/extensions/lib/plan-approval/plan-review.ts +393 -0
- package/.pi/extensions/lib/plan-approval/schema.ts +16 -1
- package/.pi/extensions/lib/plan-approval/types.ts +16 -0
- package/.pi/extensions/lib/plan-approval/validate.ts +2 -0
- package/.pi/extensions/lib/plan-debate-envelope.ts +84 -0
- package/.pi/extensions/lib/{harness-subagents/spawn-policy.ts → spawn-policy.ts} +2 -5
- package/.pi/extensions/policy-gate.ts +1 -1
- package/.pi/extensions/review-integrity.ts +48 -29
- package/.pi/extensions/ultimate-pi-vcc.ts +5 -0
- package/.pi/harness/agents.manifest.json +126 -82
- package/.pi/harness/docs/adrs/0032-harness-command-orchestration.md +7 -6
- package/.pi/harness/docs/adrs/0033-parent-orchestrated-planning.md +34 -0
- package/.pi/harness/docs/adrs/0034-darwin-plan-research-pipeline.md +41 -0
- package/.pi/harness/docs/adrs/0035-plan-phase-review-gate.md +27 -0
- package/.pi/harness/docs/adrs/README.md +2 -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/README.md +1 -1
- package/.pi/harness/specs/harness-posthog-event.schema.json +6 -1
- package/.pi/harness/specs/harness-spawn-context.schema.json +2 -1
- package/.pi/harness/specs/plan-adversary-brief.schema.json +45 -0
- package/.pi/harness/specs/plan-decomposition-brief.schema.json +108 -0
- 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-hypothesis-brief.schema.json +96 -0
- package/.pi/harness/specs/plan-hypothesis-eval.schema.json +61 -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 +76 -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 +106 -37
- 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-resolve-up-pkg.mjs +13 -0
- 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 +24 -0
- package/THIRD_PARTY_NOTICES.md +8 -0
- package/biome.json +4 -1
- package/package.json +6 -4
- package/.pi/agents/harness/planner.md +0 -54
- 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 -310
- package/.pi/extensions/lib/harness-subagents/parent-harness-ui-hooks.ts +0 -59
- 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 -684
- 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 -2494
- 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
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,30 @@ 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
|
+
|
|
19
|
+
## [v0.11.0] — 2026-05-17
|
|
20
|
+
|
|
21
|
+
### ✨ Features
|
|
22
|
+
|
|
23
|
+
- **Harness Darwin plan pipeline:** decomposition and hypothesis agents with plan-adversary, scouts, and structured plan brief schemas (ADR 0034).
|
|
24
|
+
- **Harness plan review:** `plan-review.md` for editor review; extension load guard.
|
|
25
|
+
- **Cursor Pi experts:** cursor-pi domain expert agents.
|
|
26
|
+
|
|
27
|
+
### 🔄 CI/CD
|
|
28
|
+
|
|
29
|
+
- **Biome:** ignore harness runtime JSON; format committed harness plan pipeline sources.
|
|
30
|
+
|
|
7
31
|
## [v0.10.1] — 2026-05-17
|
|
8
32
|
|
|
9
33
|
### 🐛 Fixes
|
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
|
@@ -12,7 +12,10 @@
|
|
|
12
12
|
"**/*.{ts,tsx,js,jsx,json,jsonc,css}",
|
|
13
13
|
"!graphify-out/**/*",
|
|
14
14
|
"!graphify-books-out/**/*",
|
|
15
|
-
"!vendor/**/*"
|
|
15
|
+
"!vendor/**/*",
|
|
16
|
+
"!.pi/harness/active-run.json",
|
|
17
|
+
"!.pi/harness/runs/**/run-context.yaml",
|
|
18
|
+
"!.pi/harness/runs/**/plan-packet.yaml"
|
|
16
19
|
]
|
|
17
20
|
},
|
|
18
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,54 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Harness planner that compiles strict PlanPacket contracts before execution.
|
|
3
|
-
tools: read, grep, find, ls, ask_user, approve_plan, create_plan
|
|
4
|
-
disallowed_tools: write, edit, bash
|
|
5
|
-
extensions: false
|
|
6
|
-
thinking: medium
|
|
7
|
-
max_turns: 20
|
|
8
|
-
inherit_context: false
|
|
9
|
-
---
|
|
10
|
-
|
|
11
|
-
You are the Harness Planner.
|
|
12
|
-
|
|
13
|
-
## Mission
|
|
14
|
-
|
|
15
|
-
Compile a strict, machine-readable `PlanPacket`, get user approval, and persist it with **`create_plan`**. You do **not** use `write` or `edit` — those are blocked. The parent orchestrator does not write `plan-packet.json`.
|
|
16
|
-
|
|
17
|
-
## Spawn context
|
|
18
|
-
|
|
19
|
-
Read the `HarnessSpawnContext` JSON in the spawn prompt (`schema_version`, `mode`, `task_summary`, `plan_packet_path`, `risk_level`, `quick`, etc.). Never set `inherit_context: true` on harness agents.
|
|
20
|
-
|
|
21
|
-
## Process
|
|
22
|
-
|
|
23
|
-
1. Use graphify context (`graphify-out/GRAPH_REPORT.md` or wiki) before claiming architecture — do not read harness spec JSON files from disk.
|
|
24
|
-
2. Parse task scope, constraints, and acceptance intent from spawn context.
|
|
25
|
-
3. **Greenfield** (`mode: create`) vs **revise** (`mode: revise`) — when revising, read the existing packet at `plan_packet_path` if present and amend.
|
|
26
|
-
4. `--quick` / `quick: true` narrows breadth, never safety or rollback requirements.
|
|
27
|
-
5. Build a complete `PlanPacket`: `plan_id`, `task_id`, `scope`, `assumptions`, `risk_level`, `acceptance_checks`, `rollback_plan` with `revert_command`, `revert_branch`, `patch_bundle`, `revert_commit_ready: true`.
|
|
28
|
-
6. Escalate `risk_level` to `high` for blast radius, uncertainty, or policy-sensitive surfaces.
|
|
29
|
-
7. If scope is ambiguous, call `ask_user` with structured options — do not return `needs_clarification` without trying `ask_user` first when options are clear.
|
|
30
|
-
8. Call **`approve_plan`** with the full `plan_packet` (and optional `human_summary`). The parent TUI shows a scrollable plan plus **Approve** / **Request changes** / **Cancel**. On Request changes, revise and call `approve_plan` again.
|
|
31
|
-
9. After the user selects **Approve**, call **`create_plan`** with the same `plan_packet` to write canonical `plan-packet.json` for this run.
|
|
32
|
-
|
|
33
|
-
## Guardrails
|
|
34
|
-
|
|
35
|
-
- Never call `write`, `edit`, or mutating `bash` — use **`create_plan`** only for the plan file.
|
|
36
|
-
- Never speculate about code you have not read.
|
|
37
|
-
- Do not execute or widen implementation scope.
|
|
38
|
-
|
|
39
|
-
## Output (required JSON block)
|
|
40
|
-
|
|
41
|
-
End with a single fenced `json` block the parent can parse:
|
|
42
|
-
|
|
43
|
-
```json
|
|
44
|
-
{
|
|
45
|
-
"status": "ready",
|
|
46
|
-
"plan_packet": { },
|
|
47
|
-
"human_summary": "…",
|
|
48
|
-
"clarification": null
|
|
49
|
-
}
|
|
50
|
-
```
|
|
51
|
-
|
|
52
|
-
Use `"status": "needs_clarification"` only when blocked after `ask_user` or user cancelled; include `clarification` when the parent must intervene without a live subagent.
|
|
53
|
-
|
|
54
|
-
When `create_plan` succeeds, set `status` to `"ready"` and confirm `plan_packet_path` was written.
|
|
@@ -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
|
-
}
|
|
@@ -1,119 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* agents.manifest.json drift detection (package agents vs installed hashes).
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { readFileSync } from "node:fs";
|
|
6
|
-
import { join } from "node:path";
|
|
7
|
-
import {
|
|
8
|
-
type DiscoveredAgentFile,
|
|
9
|
-
loadPackageAgentHashes,
|
|
10
|
-
sha256Content,
|
|
11
|
-
} from "./agent-loader.js";
|
|
12
|
-
|
|
13
|
-
export interface ManifestEntry {
|
|
14
|
-
path: string;
|
|
15
|
-
sha256: string;
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export interface AgentsManifest {
|
|
19
|
-
schema_version: string;
|
|
20
|
-
package: string;
|
|
21
|
-
package_version: string;
|
|
22
|
-
generated_at: string;
|
|
23
|
-
agents: Record<string, ManifestEntry>;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
export interface DriftItem {
|
|
27
|
-
id: string;
|
|
28
|
-
kind: "missing_in_manifest" | "hash_mismatch" | "missing_on_disk";
|
|
29
|
-
expected?: string;
|
|
30
|
-
actual?: string;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
export interface DriftReport {
|
|
34
|
-
ok: boolean;
|
|
35
|
-
packageVersion: string;
|
|
36
|
-
items: DriftItem[];
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function readPackageVersion(packageRoot: string): string {
|
|
40
|
-
try {
|
|
41
|
-
const pkg = JSON.parse(
|
|
42
|
-
readFileSync(join(packageRoot, "package.json"), "utf-8"),
|
|
43
|
-
) as { version?: string };
|
|
44
|
-
return pkg.version ?? "unknown";
|
|
45
|
-
} catch {
|
|
46
|
-
return "unknown";
|
|
47
|
-
}
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
export function readAgentsManifest(packageRoot: string): AgentsManifest | null {
|
|
51
|
-
const path = join(packageRoot, ".pi", "harness", "agents.manifest.json");
|
|
52
|
-
try {
|
|
53
|
-
return JSON.parse(readFileSync(path, "utf-8")) as AgentsManifest;
|
|
54
|
-
} catch {
|
|
55
|
-
return null;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
export function getDriftReport(packageRoot: string): DriftReport {
|
|
60
|
-
const manifest = readAgentsManifest(packageRoot);
|
|
61
|
-
const onDisk = loadPackageAgentHashes(packageRoot);
|
|
62
|
-
const packageVersion = readPackageVersion(packageRoot);
|
|
63
|
-
const items: DriftItem[] = [];
|
|
64
|
-
|
|
65
|
-
if (!manifest) {
|
|
66
|
-
return {
|
|
67
|
-
ok: false,
|
|
68
|
-
packageVersion,
|
|
69
|
-
items: [{ id: "*", kind: "missing_on_disk" }],
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
for (const [id, entry] of onDisk) {
|
|
74
|
-
const expected = manifest.agents[id];
|
|
75
|
-
if (!expected) {
|
|
76
|
-
items.push({ id, kind: "missing_in_manifest" });
|
|
77
|
-
continue;
|
|
78
|
-
}
|
|
79
|
-
if (expected.sha256 !== entry.sha256) {
|
|
80
|
-
items.push({
|
|
81
|
-
id,
|
|
82
|
-
kind: "hash_mismatch",
|
|
83
|
-
expected: expected.sha256,
|
|
84
|
-
actual: entry.sha256,
|
|
85
|
-
});
|
|
86
|
-
}
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
for (const id of Object.keys(manifest.agents)) {
|
|
90
|
-
if (!onDisk.has(id)) {
|
|
91
|
-
items.push({ id, kind: "missing_on_disk" });
|
|
92
|
-
}
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return { ok: items.length === 0, packageVersion, items };
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
export function buildManifestFromFiles(
|
|
99
|
-
files: Iterable<DiscoveredAgentFile>,
|
|
100
|
-
packageName: string,
|
|
101
|
-
packageVersion: string,
|
|
102
|
-
): AgentsManifest {
|
|
103
|
-
const agents: Record<string, ManifestEntry> = {};
|
|
104
|
-
for (const f of files) {
|
|
105
|
-
if (f.source !== "package") continue;
|
|
106
|
-
const relPath = `.pi/agents/${f.id}.md`;
|
|
107
|
-
agents[f.id] = {
|
|
108
|
-
path: relPath,
|
|
109
|
-
sha256: sha256Content(f.content),
|
|
110
|
-
};
|
|
111
|
-
}
|
|
112
|
-
return {
|
|
113
|
-
schema_version: "1.0.0",
|
|
114
|
-
package: packageName,
|
|
115
|
-
package_version: packageVersion,
|
|
116
|
-
generated_at: new Date().toISOString(),
|
|
117
|
-
agents,
|
|
118
|
-
};
|
|
119
|
-
}
|
|
@@ -1,87 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Parse harness agent .md files into AgentConfig (path id = posix relative path).
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
6
|
-
import { BUILTIN_TOOL_NAMES } from "./vendored/agent-types.js";
|
|
7
|
-
import type {
|
|
8
|
-
AgentConfig,
|
|
9
|
-
MemoryScope,
|
|
10
|
-
ThinkingLevel,
|
|
11
|
-
} from "./vendored/types.js";
|
|
12
|
-
|
|
13
|
-
function str(val: unknown): string | undefined {
|
|
14
|
-
return typeof val === "string" ? val : undefined;
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function nonNegativeInt(val: unknown): number | undefined {
|
|
18
|
-
return typeof val === "number" && val >= 0 ? val : undefined;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function parseCsvField(val: unknown): string[] | undefined {
|
|
22
|
-
if (val === undefined || val === null) return undefined;
|
|
23
|
-
const s = String(val).trim();
|
|
24
|
-
if (!s || s === "none") return undefined;
|
|
25
|
-
const items = s
|
|
26
|
-
.split(",")
|
|
27
|
-
.map((t) => t.trim())
|
|
28
|
-
.filter(Boolean);
|
|
29
|
-
return items.length > 0 ? items : undefined;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function csvList(val: unknown, defaults: string[]): string[] {
|
|
33
|
-
if (val === undefined || val === null) return defaults;
|
|
34
|
-
return parseCsvField(val) ?? [];
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function csvListOptional(val: unknown): string[] | undefined {
|
|
38
|
-
return parseCsvField(val);
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
function parseMemory(val: unknown): MemoryScope | undefined {
|
|
42
|
-
if (val === "user" || val === "project" || val === "local") return val;
|
|
43
|
-
return undefined;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
function inheritField(val: unknown): true | string[] | false {
|
|
47
|
-
if (val === undefined || val === null || val === true) return true;
|
|
48
|
-
if (val === false || val === "none") return false;
|
|
49
|
-
const items = csvList(val, []);
|
|
50
|
-
return items.length > 0 ? items : false;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function parseAgentMarkdown(
|
|
54
|
-
agentId: string,
|
|
55
|
-
content: string,
|
|
56
|
-
source: "package" | "project" | "global",
|
|
57
|
-
): AgentConfig {
|
|
58
|
-
const { frontmatter: fm, body } =
|
|
59
|
-
parseFrontmatter<Record<string, unknown>>(content);
|
|
60
|
-
|
|
61
|
-
const yamlName = str(fm.name);
|
|
62
|
-
const displayName = str(fm.display_name) ?? yamlName;
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
name: agentId,
|
|
66
|
-
displayName,
|
|
67
|
-
description: str(fm.description) ?? agentId,
|
|
68
|
-
builtinToolNames: csvList(fm.tools, BUILTIN_TOOL_NAMES),
|
|
69
|
-
disallowedTools: csvListOptional(fm.disallowed_tools),
|
|
70
|
-
extensions: inheritField(fm.extensions ?? fm.inherit_extensions),
|
|
71
|
-
skills: inheritField(fm.skills ?? fm.inherit_skills),
|
|
72
|
-
model: str(fm.model),
|
|
73
|
-
thinking: str(fm.thinking) as ThinkingLevel | undefined,
|
|
74
|
-
maxTurns: nonNegativeInt(fm.max_turns),
|
|
75
|
-
systemPrompt: body.trim(),
|
|
76
|
-
promptMode: fm.prompt_mode === "append" ? "append" : "replace",
|
|
77
|
-
inheritContext:
|
|
78
|
-
fm.inherit_context != null ? fm.inherit_context === true : undefined,
|
|
79
|
-
runInBackground:
|
|
80
|
-
fm.run_in_background != null ? fm.run_in_background === true : undefined,
|
|
81
|
-
isolated: fm.isolated != null ? fm.isolated === true : undefined,
|
|
82
|
-
memory: parseMemory(fm.memory),
|
|
83
|
-
isolation: fm.isolation === "worktree" ? "worktree" : undefined,
|
|
84
|
-
enabled: fm.enabled !== false,
|
|
85
|
-
source: source === "package" ? "global" : source,
|
|
86
|
-
};
|
|
87
|
-
}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Orchestrator blackboard tool (list/read/query/wait/delete).
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import { defineTool, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
6
|
-
import { Type } from "@sinclair/typebox";
|
|
7
|
-
import type { Blackboard } from "./blackboard.js";
|
|
8
|
-
import type { BlackboardQuery } from "./types-blackboard.js";
|
|
9
|
-
|
|
10
|
-
function textResult(text: string) {
|
|
11
|
-
return {
|
|
12
|
-
content: [{ type: "text" as const, text }],
|
|
13
|
-
details: {},
|
|
14
|
-
};
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
export function registerBlackboardTool(
|
|
18
|
-
pi: ExtensionAPI,
|
|
19
|
-
blackboard: Blackboard,
|
|
20
|
-
): void {
|
|
21
|
-
pi.registerTool(
|
|
22
|
-
defineTool({
|
|
23
|
-
name: "blackboard",
|
|
24
|
-
label: "Blackboard",
|
|
25
|
-
description:
|
|
26
|
-
"Shared knowledge store for harness orchestration. Actions: list, read, query, wait, delete. " +
|
|
27
|
-
"Use namespaced keys (e.g. scout:findings). Spawn context injection is capped at ~8k chars.",
|
|
28
|
-
parameters: Type.Object({
|
|
29
|
-
action: Type.Union([
|
|
30
|
-
Type.Literal("list"),
|
|
31
|
-
Type.Literal("read"),
|
|
32
|
-
Type.Literal("query"),
|
|
33
|
-
Type.Literal("wait"),
|
|
34
|
-
Type.Literal("delete"),
|
|
35
|
-
]),
|
|
36
|
-
key: Type.Optional(Type.String()),
|
|
37
|
-
pattern: Type.Optional(Type.String()),
|
|
38
|
-
agent_id: Type.Optional(Type.String()),
|
|
39
|
-
agent_name: Type.Optional(Type.String()),
|
|
40
|
-
category: Type.Optional(Type.String()),
|
|
41
|
-
timeout_ms: Type.Optional(
|
|
42
|
-
Type.Number({ description: "For wait action (default 30000)." }),
|
|
43
|
-
),
|
|
44
|
-
}),
|
|
45
|
-
execute: async (_id, params) => {
|
|
46
|
-
const action = params.action as string;
|
|
47
|
-
|
|
48
|
-
if (action === "list") {
|
|
49
|
-
return textResult(blackboard.serialize());
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (action === "read") {
|
|
53
|
-
const key = params.key as string | undefined;
|
|
54
|
-
if (!key) return textResult("read requires key.");
|
|
55
|
-
const entry = blackboard.get(key);
|
|
56
|
-
if (!entry) {
|
|
57
|
-
return textResult(`No entry for key "${key}".`);
|
|
58
|
-
}
|
|
59
|
-
return textResult(JSON.stringify(entry, null, 2));
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
if (action === "query") {
|
|
63
|
-
const q: BlackboardQuery = {};
|
|
64
|
-
if (params.pattern) q.pattern = params.pattern as string;
|
|
65
|
-
if (params.agent_id) q.agentId = params.agent_id as string;
|
|
66
|
-
if (params.agent_name) q.agentName = params.agent_name as string;
|
|
67
|
-
if (params.category) q.category = params.category as string;
|
|
68
|
-
if (params.key) q.keys = [params.key as string];
|
|
69
|
-
return textResult(JSON.stringify(blackboard.toJSON(q), null, 2));
|
|
70
|
-
}
|
|
71
|
-
|
|
72
|
-
if (action === "delete") {
|
|
73
|
-
const key = params.key as string | undefined;
|
|
74
|
-
if (!key) return textResult("delete requires key.");
|
|
75
|
-
const removed = blackboard.delete(key);
|
|
76
|
-
return textResult(
|
|
77
|
-
removed ? `Deleted "${key}".` : `Key "${key}" not found.`,
|
|
78
|
-
);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (action === "wait") {
|
|
82
|
-
const pattern = (params.pattern ?? params.key) as string | undefined;
|
|
83
|
-
if (!pattern) {
|
|
84
|
-
return textResult("wait requires pattern or key.");
|
|
85
|
-
}
|
|
86
|
-
const timeoutMs = (params.timeout_ms as number) ?? 30_000;
|
|
87
|
-
const start = Date.now();
|
|
88
|
-
while (Date.now() - start < timeoutMs) {
|
|
89
|
-
const matches = blackboard.query({ pattern });
|
|
90
|
-
if (matches.length > 0) {
|
|
91
|
-
return textResult(
|
|
92
|
-
JSON.stringify(blackboard.toJSON({ pattern }), null, 2),
|
|
93
|
-
);
|
|
94
|
-
}
|
|
95
|
-
await new Promise((r) => setTimeout(r, 200));
|
|
96
|
-
}
|
|
97
|
-
return textResult(`Timeout waiting for pattern "${pattern}".`);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
return textResult("Unknown action.");
|
|
101
|
-
},
|
|
102
|
-
}),
|
|
103
|
-
);
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
export function buildBlackboardContextInjection(
|
|
107
|
-
blackboard: Blackboard,
|
|
108
|
-
spec?: { agentId?: string; keys?: string[]; agentName?: string },
|
|
109
|
-
): string | undefined {
|
|
110
|
-
if (!spec) return undefined;
|
|
111
|
-
const q: BlackboardQuery = {};
|
|
112
|
-
if (spec.agentId) q.agentId = spec.agentId;
|
|
113
|
-
if (spec.agentName) q.agentName = spec.agentName;
|
|
114
|
-
if (spec.keys?.length) q.keys = spec.keys;
|
|
115
|
-
const serialized = blackboard.serialize(q);
|
|
116
|
-
if (serialized === "(blackboard is empty)") return undefined;
|
|
117
|
-
return serialized;
|
|
118
|
-
}
|