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
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Shared blackboard for orchestrator ↔ subagent handoffs (~8k injection cap).
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type {
|
|
6
|
-
BlackboardEntry,
|
|
7
|
-
BlackboardQuery,
|
|
8
|
-
PostMetadata,
|
|
9
|
-
} from "./types-blackboard.js";
|
|
10
|
-
|
|
11
|
-
const MAX_VALUE_DISPLAY_CHARS = 500;
|
|
12
|
-
export const MAX_SERIALIZE_TOTAL_CHARS = 8_000;
|
|
13
|
-
|
|
14
|
-
function truncateForDisplay(value: unknown): string {
|
|
15
|
-
const str =
|
|
16
|
-
typeof value === "string" ? value : JSON.stringify(value, null, 2);
|
|
17
|
-
if (str.length <= MAX_VALUE_DISPLAY_CHARS) return str;
|
|
18
|
-
return `${str.slice(0, MAX_VALUE_DISPLAY_CHARS)}...(truncated)`;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
type PostHandler = (key: string, entry: BlackboardEntry) => void;
|
|
22
|
-
|
|
23
|
-
export class Blackboard {
|
|
24
|
-
private entries = new Map<string, BlackboardEntry>();
|
|
25
|
-
private postHandlers: PostHandler[] = [];
|
|
26
|
-
|
|
27
|
-
post(
|
|
28
|
-
namespacedKey: string,
|
|
29
|
-
value: unknown,
|
|
30
|
-
agentId: string,
|
|
31
|
-
agentName: string,
|
|
32
|
-
metadata?: PostMetadata,
|
|
33
|
-
): void {
|
|
34
|
-
if (metadata?.supersedes) {
|
|
35
|
-
this.entries.delete(metadata.supersedes);
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const entry: BlackboardEntry = {
|
|
39
|
-
key: namespacedKey,
|
|
40
|
-
value,
|
|
41
|
-
agentId,
|
|
42
|
-
agentName,
|
|
43
|
-
timestamp: Date.now(),
|
|
44
|
-
metadata,
|
|
45
|
-
};
|
|
46
|
-
|
|
47
|
-
this.entries.set(namespacedKey, entry);
|
|
48
|
-
|
|
49
|
-
for (const handler of this.postHandlers) {
|
|
50
|
-
try {
|
|
51
|
-
handler(namespacedKey, entry);
|
|
52
|
-
} catch {
|
|
53
|
-
/* ignore */
|
|
54
|
-
}
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
get(key: string): BlackboardEntry | undefined {
|
|
59
|
-
return this.entries.get(key);
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
getAll(): Map<string, BlackboardEntry> {
|
|
63
|
-
return new Map(this.entries);
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
query(query: BlackboardQuery): BlackboardEntry[] {
|
|
67
|
-
let results = [...this.entries.values()];
|
|
68
|
-
|
|
69
|
-
if (query.keys?.length) {
|
|
70
|
-
results = results.filter((e) => query.keys?.includes(e.key));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
if (query.pattern) {
|
|
74
|
-
const pat = query.pattern;
|
|
75
|
-
if (pat instanceof RegExp) {
|
|
76
|
-
results = results.filter((e) => pat.test(e.key));
|
|
77
|
-
} else {
|
|
78
|
-
const escaped = pat.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
|
|
79
|
-
const re = new RegExp(`^${escaped.replace(/\*/g, ".*")}$`);
|
|
80
|
-
results = results.filter((e) => re.test(e.key) || e.key.includes(pat));
|
|
81
|
-
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (query.agentId) {
|
|
85
|
-
results = results.filter((e) => e.agentId === query.agentId);
|
|
86
|
-
}
|
|
87
|
-
if (query.agentName) {
|
|
88
|
-
results = results.filter((e) => e.agentName === query.agentName);
|
|
89
|
-
}
|
|
90
|
-
if (query.category) {
|
|
91
|
-
results = results.filter((e) => e.metadata?.category === query.category);
|
|
92
|
-
}
|
|
93
|
-
if (query.after !== undefined) {
|
|
94
|
-
results = results.filter((e) => e.timestamp > query.after!);
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
return results;
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
serialize(query?: BlackboardQuery): string {
|
|
101
|
-
const entries = query ? this.query(query) : [...this.entries.values()];
|
|
102
|
-
|
|
103
|
-
if (entries.length === 0) return "(blackboard is empty)";
|
|
104
|
-
|
|
105
|
-
const lines: string[] = [`Blackboard (${entries.length} entries):`];
|
|
106
|
-
let totalChars = lines[0].length;
|
|
107
|
-
|
|
108
|
-
for (const entry of entries) {
|
|
109
|
-
const summary = entry.metadata?.summary
|
|
110
|
-
? entry.metadata.summary
|
|
111
|
-
: truncateForDisplay(entry.value);
|
|
112
|
-
const category = entry.metadata?.category
|
|
113
|
-
? ` [${entry.metadata.category}]`
|
|
114
|
-
: "";
|
|
115
|
-
const ts = new Date(entry.timestamp).toISOString().slice(11, 19);
|
|
116
|
-
const keyLine = ` ${entry.key}${category} (${entry.agentName} @ ${ts})`;
|
|
117
|
-
const valLine = ` ${summary}`;
|
|
118
|
-
const entryChars = keyLine.length + valLine.length + 2;
|
|
119
|
-
|
|
120
|
-
if (totalChars + entryChars > MAX_SERIALIZE_TOTAL_CHARS) {
|
|
121
|
-
lines.push(
|
|
122
|
-
" ... (more entries truncated — use blackboard query with specific keys)",
|
|
123
|
-
);
|
|
124
|
-
break;
|
|
125
|
-
}
|
|
126
|
-
lines.push(keyLine, valLine);
|
|
127
|
-
totalChars += entryChars;
|
|
128
|
-
}
|
|
129
|
-
|
|
130
|
-
return lines.join("\n");
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
toJSON(query?: BlackboardQuery): object {
|
|
134
|
-
const entries = query ? this.query(query) : [...this.entries.values()];
|
|
135
|
-
return {
|
|
136
|
-
count: entries.length,
|
|
137
|
-
entries: entries.map((e) => ({
|
|
138
|
-
key: e.key,
|
|
139
|
-
agentId: e.agentId,
|
|
140
|
-
agentName: e.agentName,
|
|
141
|
-
timestamp: e.timestamp,
|
|
142
|
-
summary: e.metadata?.summary ?? null,
|
|
143
|
-
category: e.metadata?.category ?? null,
|
|
144
|
-
value: e.value,
|
|
145
|
-
})),
|
|
146
|
-
};
|
|
147
|
-
}
|
|
148
|
-
|
|
149
|
-
clear(): void {
|
|
150
|
-
this.entries.clear();
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
restore(data: {
|
|
154
|
-
key: string;
|
|
155
|
-
value: unknown;
|
|
156
|
-
agentId: string;
|
|
157
|
-
agentName: string;
|
|
158
|
-
timestamp: number;
|
|
159
|
-
metadata?: PostMetadata;
|
|
160
|
-
}): void {
|
|
161
|
-
this.entries.set(data.key, { ...data });
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
delete(key: string): boolean {
|
|
165
|
-
return this.entries.delete(key);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
get size(): number {
|
|
169
|
-
return this.entries.size;
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
on(event: "post", handler: PostHandler): void {
|
|
173
|
-
if (event === "post") this.postHandlers.push(handler);
|
|
174
|
-
}
|
|
175
|
-
}
|
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* @deprecated Import from parent-harness-ui-bridge.js — kept for stable import paths.
|
|
3
|
-
*/
|
|
4
|
-
export {
|
|
5
|
-
agentTypeAllowsParentAskUser,
|
|
6
|
-
agentTypeAllowsParentHarnessUi,
|
|
7
|
-
createParentAskUserBridgeFactory,
|
|
8
|
-
createParentHarnessUiBridgeFactory,
|
|
9
|
-
type ParentHarnessUiHooks,
|
|
10
|
-
} from "./parent-harness-ui-bridge.js";
|
|
@@ -1,310 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Registers ask_user and approve_plan in subagent sessions, delegating UI to the parent harness session.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
import type {
|
|
6
|
-
ExtensionAPI,
|
|
7
|
-
ExtensionContext,
|
|
8
|
-
} from "@earendil-works/pi-coding-agent";
|
|
9
|
-
import { Text } from "@earendil-works/pi-tui";
|
|
10
|
-
import { Type } from "@sinclair/typebox";
|
|
11
|
-
import type {
|
|
12
|
-
PlanPacketLike,
|
|
13
|
-
PlanUserApproval,
|
|
14
|
-
} from "../../../lib/harness-run-context.js";
|
|
15
|
-
import { parsePlanApprovalFromMessage } from "../../../lib/harness-run-context.js";
|
|
16
|
-
import { runAskDialog } from "../ask-user/dialog.js";
|
|
17
|
-
import { runAskFallback } from "../ask-user/fallback.js";
|
|
18
|
-
import { renderAskCall, renderAskResult } from "../ask-user/render.js";
|
|
19
|
-
import {
|
|
20
|
-
PROMPT_GUIDELINES as ASK_PROMPT_GUIDELINES,
|
|
21
|
-
PROMPT_SNIPPET as ASK_PROMPT_SNIPPET,
|
|
22
|
-
AskUserParamsSchema,
|
|
23
|
-
} from "../ask-user/schema.js";
|
|
24
|
-
import type { AskUserParams, DialogResult } from "../ask-user/types.js";
|
|
25
|
-
import {
|
|
26
|
-
formatResultText,
|
|
27
|
-
toToolDetails,
|
|
28
|
-
validateAskParams,
|
|
29
|
-
} from "../ask-user/validate.js";
|
|
30
|
-
import {
|
|
31
|
-
CREATE_PLAN_GUIDELINES,
|
|
32
|
-
CREATE_PLAN_SNIPPET,
|
|
33
|
-
executeCreatePlan,
|
|
34
|
-
formatCreatePlanResultText,
|
|
35
|
-
} from "../plan-approval/create-plan.js";
|
|
36
|
-
import { runPlanApprovalDialog } from "../plan-approval/dialog.js";
|
|
37
|
-
import { runPlanApprovalFallback } from "../plan-approval/fallback.js";
|
|
38
|
-
import {
|
|
39
|
-
renderApprovePlanCall,
|
|
40
|
-
renderApprovePlanResult,
|
|
41
|
-
} from "../plan-approval/render.js";
|
|
42
|
-
import {
|
|
43
|
-
ApprovePlanParamsSchema,
|
|
44
|
-
PROMPT_GUIDELINES as PLAN_PROMPT_GUIDELINES,
|
|
45
|
-
PROMPT_SNIPPET as PLAN_PROMPT_SNIPPET,
|
|
46
|
-
} from "../plan-approval/schema.js";
|
|
47
|
-
import type { ApprovePlanParams } from "../plan-approval/types.js";
|
|
48
|
-
import {
|
|
49
|
-
formatApprovePlanResultText,
|
|
50
|
-
toApprovePlanToolDetails,
|
|
51
|
-
validateApprovePlanParams,
|
|
52
|
-
} from "../plan-approval/validate.js";
|
|
53
|
-
|
|
54
|
-
const HARNESS_UI_AGENT_TYPES = new Set([
|
|
55
|
-
"harness/planner",
|
|
56
|
-
"harness/evaluator",
|
|
57
|
-
"harness/adversary",
|
|
58
|
-
"harness/tie-breaker",
|
|
59
|
-
]);
|
|
60
|
-
|
|
61
|
-
export interface ParentHarnessUiHooks {
|
|
62
|
-
projectRoot?: string;
|
|
63
|
-
getParentEntries?: () => unknown[];
|
|
64
|
-
getParentRunContext?: () =>
|
|
65
|
-
| import("../../../lib/harness-run-context.js").HarnessRunContext
|
|
66
|
-
| null;
|
|
67
|
-
onPlanApproval?: (approval: PlanUserApproval) => void;
|
|
68
|
-
appendPlanDraft?: (draft: {
|
|
69
|
-
plan_packet: PlanPacketLike;
|
|
70
|
-
human_summary?: string;
|
|
71
|
-
}) => void;
|
|
72
|
-
onPlanCommitted?: (
|
|
73
|
-
runCtx: import("../../../lib/harness-run-context.js").HarnessRunContext,
|
|
74
|
-
packet: PlanPacketLike,
|
|
75
|
-
planPath: string,
|
|
76
|
-
) => void;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const CreatePlanParamsSchema = Type.Object({
|
|
80
|
-
plan_packet: Type.Object(
|
|
81
|
-
{},
|
|
82
|
-
{
|
|
83
|
-
description:
|
|
84
|
-
"Approved PlanPacket to persist (same object as approve_plan).",
|
|
85
|
-
},
|
|
86
|
-
),
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
const PLANNER_ONLY_AGENT = "harness/planner";
|
|
90
|
-
|
|
91
|
-
export function agentTypeAllowsParentHarnessUi(agentType: string): boolean {
|
|
92
|
-
return HARNESS_UI_AGENT_TYPES.has(agentType);
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
/** @deprecated Use agentTypeAllowsParentHarnessUi */
|
|
96
|
-
export function agentTypeAllowsParentAskUser(agentType: string): boolean {
|
|
97
|
-
return agentTypeAllowsParentHarnessUi(agentType);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function notifyPlanApproval(
|
|
101
|
-
hooks: ParentHarnessUiHooks | undefined,
|
|
102
|
-
details: unknown,
|
|
103
|
-
toolName: "ask_user" | "approve_plan",
|
|
104
|
-
): void {
|
|
105
|
-
if (!hooks?.onPlanApproval) return;
|
|
106
|
-
const approval = parsePlanApprovalFromMessage({ toolName, details });
|
|
107
|
-
if (approval) hooks.onPlanApproval(approval);
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function createParentHarnessUiBridgeFactory(
|
|
111
|
-
parentCtx: ExtensionContext,
|
|
112
|
-
agentType: string,
|
|
113
|
-
hooks?: ParentHarnessUiHooks,
|
|
114
|
-
): ((pi: ExtensionAPI) => void) | null {
|
|
115
|
-
if (!agentTypeAllowsParentHarnessUi(agentType)) {
|
|
116
|
-
return null;
|
|
117
|
-
}
|
|
118
|
-
return (pi: ExtensionAPI) => {
|
|
119
|
-
pi.registerTool({
|
|
120
|
-
name: "ask_user",
|
|
121
|
-
label: "Ask User",
|
|
122
|
-
description:
|
|
123
|
-
"Ask the user a structured question (parent session UI). Use for clarification — not final plan approval (use approve_plan).",
|
|
124
|
-
promptSnippet: ASK_PROMPT_SNIPPET,
|
|
125
|
-
promptGuidelines: ASK_PROMPT_GUIDELINES,
|
|
126
|
-
parameters: AskUserParamsSchema,
|
|
127
|
-
async execute(_toolCallId, params, _signal, _onUpdate) {
|
|
128
|
-
const validated = validateAskParams(params as AskUserParams);
|
|
129
|
-
if (typeof validated === "string") {
|
|
130
|
-
return {
|
|
131
|
-
content: [{ type: "text", text: validated }],
|
|
132
|
-
details: {
|
|
133
|
-
question: params.question ?? "",
|
|
134
|
-
options: [],
|
|
135
|
-
response: null,
|
|
136
|
-
cancelled: true,
|
|
137
|
-
},
|
|
138
|
-
};
|
|
139
|
-
}
|
|
140
|
-
let outcome: DialogResult;
|
|
141
|
-
if (parentCtx.hasUI) {
|
|
142
|
-
outcome = await runAskDialog(parentCtx.ui, validated);
|
|
143
|
-
} else {
|
|
144
|
-
outcome = await runAskFallback(parentCtx.ui, validated);
|
|
145
|
-
}
|
|
146
|
-
const details = toToolDetails(
|
|
147
|
-
validated,
|
|
148
|
-
outcome.response,
|
|
149
|
-
outcome.cancelled,
|
|
150
|
-
);
|
|
151
|
-
notifyPlanApproval(hooks, details, "ask_user");
|
|
152
|
-
const text = formatResultText(outcome.response, outcome.cancelled);
|
|
153
|
-
return {
|
|
154
|
-
content: [{ type: "text", text }],
|
|
155
|
-
details,
|
|
156
|
-
};
|
|
157
|
-
},
|
|
158
|
-
renderCall(args, theme) {
|
|
159
|
-
return renderAskCall(args, theme);
|
|
160
|
-
},
|
|
161
|
-
renderResult(result, options, theme) {
|
|
162
|
-
return renderAskResult(result, options, theme);
|
|
163
|
-
},
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
if (agentType !== PLANNER_ONLY_AGENT) {
|
|
167
|
-
return;
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
pi.registerTool({
|
|
171
|
-
name: "approve_plan",
|
|
172
|
-
label: "Approve Plan",
|
|
173
|
-
description:
|
|
174
|
-
"Present the full PlanPacket for user approval in the parent TUI (scrollable overlay).",
|
|
175
|
-
promptSnippet: PLAN_PROMPT_SNIPPET,
|
|
176
|
-
promptGuidelines: PLAN_PROMPT_GUIDELINES,
|
|
177
|
-
parameters: ApprovePlanParamsSchema,
|
|
178
|
-
async execute(_toolCallId, params, _signal, _onUpdate) {
|
|
179
|
-
const validated = validateApprovePlanParams(
|
|
180
|
-
params as ApprovePlanParams,
|
|
181
|
-
);
|
|
182
|
-
if (typeof validated === "string") {
|
|
183
|
-
return {
|
|
184
|
-
content: [{ type: "text", text: validated }],
|
|
185
|
-
details: {
|
|
186
|
-
plan_packet: (params as ApprovePlanParams).plan_packet ?? {},
|
|
187
|
-
options: [],
|
|
188
|
-
response: null,
|
|
189
|
-
cancelled: true,
|
|
190
|
-
},
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
hooks?.appendPlanDraft?.({
|
|
195
|
-
plan_packet: validated.plan_packet,
|
|
196
|
-
human_summary: validated.human_summary,
|
|
197
|
-
});
|
|
198
|
-
|
|
199
|
-
let outcome: DialogResult;
|
|
200
|
-
if (parentCtx.hasUI) {
|
|
201
|
-
outcome = await runPlanApprovalDialog(parentCtx.ui, validated, {
|
|
202
|
-
onMounted: () => {
|
|
203
|
-
pi.events.emit("plan-approval:mounted", {});
|
|
204
|
-
},
|
|
205
|
-
});
|
|
206
|
-
} else {
|
|
207
|
-
outcome = await runPlanApprovalFallback(parentCtx.ui, validated);
|
|
208
|
-
}
|
|
209
|
-
const details = toApprovePlanToolDetails(
|
|
210
|
-
validated,
|
|
211
|
-
outcome.response,
|
|
212
|
-
outcome.cancelled,
|
|
213
|
-
);
|
|
214
|
-
notifyPlanApproval(hooks, details, "approve_plan");
|
|
215
|
-
const text = formatApprovePlanResultText(
|
|
216
|
-
outcome.response,
|
|
217
|
-
outcome.cancelled,
|
|
218
|
-
);
|
|
219
|
-
return {
|
|
220
|
-
content: [{ type: "text", text }],
|
|
221
|
-
details,
|
|
222
|
-
};
|
|
223
|
-
},
|
|
224
|
-
renderCall(args, theme) {
|
|
225
|
-
return renderApprovePlanCall(args, theme);
|
|
226
|
-
},
|
|
227
|
-
renderResult(result, options, theme) {
|
|
228
|
-
return renderApprovePlanResult(result, options, theme);
|
|
229
|
-
},
|
|
230
|
-
});
|
|
231
|
-
|
|
232
|
-
pi.registerTool({
|
|
233
|
-
name: "create_plan",
|
|
234
|
-
label: "Create Plan",
|
|
235
|
-
description:
|
|
236
|
-
"Write the approved PlanPacket to the canonical plan-packet.json for this harness run. Requires approve_plan Approve first. Do not use write/edit.",
|
|
237
|
-
promptSnippet: CREATE_PLAN_SNIPPET,
|
|
238
|
-
promptGuidelines: CREATE_PLAN_GUIDELINES,
|
|
239
|
-
parameters: CreatePlanParamsSchema,
|
|
240
|
-
async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
|
|
241
|
-
const validated = validateApprovePlanParams(
|
|
242
|
-
params as ApprovePlanParams,
|
|
243
|
-
);
|
|
244
|
-
if (typeof validated === "string") {
|
|
245
|
-
return {
|
|
246
|
-
content: [{ type: "text", text: validated }],
|
|
247
|
-
details: { error: validated },
|
|
248
|
-
};
|
|
249
|
-
}
|
|
250
|
-
const projectRoot = hooks?.projectRoot ?? parentCtx.cwd;
|
|
251
|
-
const parentEntries = hooks?.getParentEntries?.() ?? [];
|
|
252
|
-
const subEntries = ctx.sessionManager.getEntries();
|
|
253
|
-
const result = await executeCreatePlan(validated.plan_packet, {
|
|
254
|
-
projectRoot,
|
|
255
|
-
getParentEntries: () => parentEntries,
|
|
256
|
-
getSubagentEntries: () => subEntries,
|
|
257
|
-
getParentRunContext: () => hooks?.getParentRunContext?.() ?? null,
|
|
258
|
-
onCommitted: (runCtx, packet, planPath) => {
|
|
259
|
-
hooks?.onPlanCommitted?.(runCtx, packet, planPath);
|
|
260
|
-
},
|
|
261
|
-
});
|
|
262
|
-
const text = formatCreatePlanResultText(result);
|
|
263
|
-
return {
|
|
264
|
-
content: [{ type: "text", text }],
|
|
265
|
-
details: result.ok
|
|
266
|
-
? {
|
|
267
|
-
plan_path: result.planPath,
|
|
268
|
-
plan_id: result.planId,
|
|
269
|
-
}
|
|
270
|
-
: { error: result.error },
|
|
271
|
-
isError: !result.ok,
|
|
272
|
-
};
|
|
273
|
-
},
|
|
274
|
-
renderCall(args, theme) {
|
|
275
|
-
const packet = (args as { plan_packet?: PlanPacketLike }).plan_packet;
|
|
276
|
-
const id = packet?.plan_id ?? "?";
|
|
277
|
-
return new Text(theme.fg("accent", `create_plan: ${id}`), 0, 0);
|
|
278
|
-
},
|
|
279
|
-
renderResult(result, _options, theme) {
|
|
280
|
-
const details = result.details as
|
|
281
|
-
| { plan_path?: string; error?: string }
|
|
282
|
-
| undefined;
|
|
283
|
-
if (details?.error) {
|
|
284
|
-
return new Text(
|
|
285
|
-
theme.fg("error", details.error ?? "create_plan failed"),
|
|
286
|
-
0,
|
|
287
|
-
0,
|
|
288
|
-
);
|
|
289
|
-
}
|
|
290
|
-
return new Text(
|
|
291
|
-
theme.fg(
|
|
292
|
-
"success",
|
|
293
|
-
`Wrote ${details?.plan_path ?? "plan-packet.json"}`,
|
|
294
|
-
),
|
|
295
|
-
0,
|
|
296
|
-
0,
|
|
297
|
-
);
|
|
298
|
-
},
|
|
299
|
-
});
|
|
300
|
-
};
|
|
301
|
-
}
|
|
302
|
-
|
|
303
|
-
/** @deprecated Use createParentHarnessUiBridgeFactory */
|
|
304
|
-
export function createParentAskUserBridgeFactory(
|
|
305
|
-
parentCtx: ExtensionContext,
|
|
306
|
-
agentType: string,
|
|
307
|
-
hooks?: ParentHarnessUiHooks,
|
|
308
|
-
): ((pi: ExtensionAPI) => void) | null {
|
|
309
|
-
return createParentHarnessUiBridgeFactory(parentCtx, agentType, hooks);
|
|
310
|
-
}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
2
|
-
import {
|
|
3
|
-
appendPlanApprovalIfNew,
|
|
4
|
-
getLatestRunContext,
|
|
5
|
-
type HarnessRunContext,
|
|
6
|
-
nowIso,
|
|
7
|
-
type PlanUserApproval,
|
|
8
|
-
planPacketSummary,
|
|
9
|
-
} from "../../../lib/harness-run-context.js";
|
|
10
|
-
import type { ParentHarnessUiHooks } from "./parent-harness-ui-bridge.js";
|
|
11
|
-
|
|
12
|
-
function persistRunContext(pi: ExtensionAPI, runCtx: HarnessRunContext): void {
|
|
13
|
-
pi.appendEntry("harness-run-context", runCtx);
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
export function createParentHarnessUiHooks(
|
|
17
|
-
pi: ExtensionAPI,
|
|
18
|
-
getParentEntries: () => unknown[],
|
|
19
|
-
projectRoot: string,
|
|
20
|
-
): ParentHarnessUiHooks {
|
|
21
|
-
return {
|
|
22
|
-
projectRoot,
|
|
23
|
-
getParentEntries,
|
|
24
|
-
getParentRunContext: () => getLatestRunContext(getParentEntries()),
|
|
25
|
-
appendPlanDraft: (draft) => {
|
|
26
|
-
const planId = String(draft.plan_packet.plan_id ?? "plan");
|
|
27
|
-
const summary =
|
|
28
|
-
draft.human_summary?.trim() || `Plan ${planId} — pending your approval`;
|
|
29
|
-
pi.sendMessage({
|
|
30
|
-
customType: "harness-plan-draft",
|
|
31
|
-
content: summary,
|
|
32
|
-
display: true,
|
|
33
|
-
details: {
|
|
34
|
-
schema_version: "1.0.0",
|
|
35
|
-
plan_packet: draft.plan_packet,
|
|
36
|
-
human_summary: draft.human_summary ?? null,
|
|
37
|
-
shown_at: nowIso(),
|
|
38
|
-
},
|
|
39
|
-
});
|
|
40
|
-
},
|
|
41
|
-
onPlanApproval: (approval: PlanUserApproval) => {
|
|
42
|
-
const entries = getParentEntries();
|
|
43
|
-
const runCtx = getLatestRunContext(entries);
|
|
44
|
-
appendPlanApprovalIfNew(
|
|
45
|
-
(type, data) => pi.appendEntry(type, data),
|
|
46
|
-
entries,
|
|
47
|
-
approval,
|
|
48
|
-
runCtx,
|
|
49
|
-
);
|
|
50
|
-
},
|
|
51
|
-
onPlanCommitted: (runCtx, packet, planPath) => {
|
|
52
|
-
persistRunContext(pi, runCtx);
|
|
53
|
-
pi.appendEntry(
|
|
54
|
-
"harness-plan-packet",
|
|
55
|
-
planPacketSummary(packet, planPath, "ready"),
|
|
56
|
-
);
|
|
57
|
-
},
|
|
58
|
-
};
|
|
59
|
-
}
|
|
@@ -1,27 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Blackboard types (ported from subagent-v2 reference; MIT design reference).
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
export interface PostMetadata {
|
|
6
|
-
summary?: string;
|
|
7
|
-
category?: string;
|
|
8
|
-
supersedes?: string;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
export interface BlackboardEntry {
|
|
12
|
-
key: string;
|
|
13
|
-
value: unknown;
|
|
14
|
-
agentId: string;
|
|
15
|
-
agentName: string;
|
|
16
|
-
timestamp: number;
|
|
17
|
-
metadata?: PostMetadata;
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
export interface BlackboardQuery {
|
|
21
|
-
keys?: string[];
|
|
22
|
-
pattern?: string | RegExp;
|
|
23
|
-
agentId?: string;
|
|
24
|
-
agentName?: string;
|
|
25
|
-
category?: string;
|
|
26
|
-
after?: number;
|
|
27
|
-
}
|