ultimate-pi 0.11.0 → 0.13.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/ck-search/SKILL.md +11 -87
- package/.agents/skills/cocoindex-search/SKILL.md +35 -0
- 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/PACKAGING.md +1 -0
- package/.pi/SYSTEM.md +21 -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 +13 -5
- package/.pi/agents/harness/planning/scout-semantic.md +23 -11
- 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 +150 -5
- package/.pi/extensions/harness-subagents.ts +17 -6
- package/.pi/extensions/lib/harness-cocoindex-refresh.ts +49 -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} +8 -7
- package/.pi/extensions/lib/harness-subagent-precheck.ts +95 -0
- package/.pi/extensions/lib/harness-subagents-bridge.ts +122 -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 +2 -2
- 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 +83 -92
- 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 +30 -17
- package/.pi/prompts/harness-trace.md +2 -2
- package/.pi/scripts/README.md +1 -0
- package/.pi/scripts/harness-agents-manifest.mjs +1 -1
- package/.pi/scripts/harness-cli-verify.sh +24 -14
- package/.pi/scripts/harness-cocoindex-bootstrap.sh +182 -0
- package/.pi/scripts/harness-verify.mjs +38 -19
- package/.pi/scripts/validate-plan-dag.mjs +258 -0
- package/.pi/scripts/vendor-sync-pi-subagents.sh +19 -0
- package/.pi/skills/ast-grep/SKILL.md +2 -2
- package/.pi/skills/ccc/SKILL.md +142 -0
- package/.pi/skills/ccc/references/management.md +110 -0
- package/CHANGELOG.md +22 -0
- package/THIRD_PARTY_NOTICES.md +15 -0
- package/biome.json +2 -2
- package/package.json +7 -4
- package/vendor/pi-subagents/LICENSE +21 -0
- package/vendor/pi-subagents/UPSTREAM_PIN.md +11 -0
- package/vendor/pi-subagents/src/agents.ts +357 -0
- package/vendor/pi-subagents/src/subagents.ts +1463 -0
- 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
|
@@ -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
|
-
}
|
|
@@ -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
|
-
}
|