ultimate-pi 0.7.0 → 0.8.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-decisions/SKILL.md +20 -1
- package/.agents/skills/harness-eval/SKILL.md +11 -13
- package/.agents/skills/harness-orchestration/SKILL.md +36 -30
- package/.agents/skills/harness-plan/SKILL.md +13 -18
- package/.pi/PACKAGING.md +1 -1
- package/.pi/agents/harness/adversary.md +20 -12
- package/.pi/agents/harness/evaluator.md +25 -14
- package/.pi/agents/harness/executor.md +27 -16
- package/.pi/agents/harness/incident-recorder.md +37 -0
- package/.pi/agents/harness/meta-optimizer.md +18 -15
- package/.pi/agents/harness/planner.md +27 -30
- package/.pi/agents/harness/tie-breaker.md +4 -2
- package/.pi/agents/harness/trace-librarian.md +18 -11
- package/.pi/agents/pi-pi/ext-expert.md +1 -1
- package/.pi/agents/pi-pi/keybinding-expert.md +1 -1
- package/.pi/agents/pi-pi/tui-expert.md +3 -3
- package/.pi/extensions/00-ultimate-pi-system-prompt.ts +2 -2
- package/.pi/extensions/budget-guard.ts +1 -1
- package/.pi/extensions/custom-footer.ts +8 -3
- package/.pi/extensions/custom-header.ts +2 -2
- package/.pi/extensions/debate-orchestrator.ts +1 -1
- package/.pi/extensions/dotenv-loader.ts +1 -1
- package/.pi/extensions/drift-monitor.ts +1 -1
- package/.pi/extensions/harness-ask-user.ts +1 -1
- package/.pi/extensions/harness-live-widget.ts +1 -1
- package/.pi/extensions/harness-run-context.ts +52 -10
- package/.pi/extensions/harness-telemetry.ts +1 -1
- package/.pi/extensions/harness-web-guard.ts +1 -1
- package/.pi/extensions/harness-web-tools.ts +1 -1
- package/.pi/extensions/lib/ask-user/dialog.ts +2 -2
- package/.pi/extensions/lib/ask-user/fallback.ts +1 -1
- package/.pi/extensions/lib/ask-user/render.ts +3 -3
- package/.pi/extensions/lib/harness-subagents/agent-loader.ts +1 -1
- package/.pi/extensions/lib/harness-subagents/agent-parser.ts +1 -1
- package/.pi/extensions/lib/harness-subagents/blackboard-tool.ts +1 -1
- package/.pi/extensions/lib/harness-subagents/harness-subagent-policy.ts +134 -0
- package/.pi/extensions/lib/harness-subagents/vendored/agent-manager.ts +2 -2
- package/.pi/extensions/lib/harness-subagents/vendored/agent-runner.ts +9 -5
- package/.pi/extensions/lib/harness-subagents/vendored/context.ts +1 -1
- package/.pi/extensions/lib/harness-subagents/vendored/env.ts +1 -1
- package/.pi/extensions/lib/harness-subagents/vendored/index.ts +2 -2
- package/.pi/extensions/lib/harness-subagents/vendored/output-file.ts +1 -1
- package/.pi/extensions/lib/harness-subagents/vendored/schedule.ts +1 -1
- package/.pi/extensions/lib/harness-subagents/vendored/settings.ts +1 -1
- package/.pi/extensions/lib/harness-subagents/vendored/skill-loader.ts +1 -1
- package/.pi/extensions/lib/harness-subagents/vendored/types.ts +2 -2
- package/.pi/extensions/lib/harness-subagents/vendored/ui/agent-widget.ts +1 -1
- package/.pi/extensions/lib/harness-subagents/vendored/ui/conversation-viewer.ts +2 -2
- package/.pi/extensions/lib/harness-subagents/vendored/ui/schedule-menu.ts +1 -1
- package/.pi/extensions/observation-bus.ts +1 -1
- package/.pi/extensions/pi-model-router-harness.ts +1 -1
- package/.pi/extensions/policy-gate.ts +86 -16
- package/.pi/extensions/provider-payload-sanitize.ts +1 -1
- package/.pi/extensions/review-integrity.ts +76 -22
- package/.pi/extensions/sentrux-rules-sync.ts +1 -1
- package/.pi/extensions/soundboard.ts +1 -1
- package/.pi/extensions/test-diff-integrity.ts +1 -1
- package/.pi/extensions/trace-recorder.ts +1 -1
- package/.pi/extensions/ultimate-pi-vcc.ts +1 -1
- package/.pi/harness/agents.manifest.json +16 -12
- package/.pi/harness/docs/adrs/0031-harness-run-context.md +5 -2
- package/.pi/harness/docs/adrs/0032-harness-command-orchestration.md +37 -0
- package/.pi/harness/docs/adrs/README.md +1 -0
- package/.pi/harness/specs/harness-spawn-context.schema.json +65 -0
- package/.pi/lib/harness-agent-output.ts +41 -0
- package/.pi/lib/harness-run-context.ts +352 -7
- package/.pi/lib/harness-ui-state.ts +1 -1
- package/.pi/prompts/harness-auto.md +36 -61
- package/.pi/prompts/harness-critic.md +15 -28
- package/.pi/prompts/harness-eval.md +19 -27
- package/.pi/prompts/harness-incident.md +15 -34
- package/.pi/prompts/harness-plan.md +31 -50
- package/.pi/prompts/harness-review.md +16 -30
- package/.pi/prompts/harness-router-tune.md +16 -38
- package/.pi/prompts/harness-run.md +21 -38
- package/.pi/prompts/harness-setup.md +2 -0
- package/.pi/prompts/harness-trace.md +13 -30
- package/.pi/scripts/harness-generate-model-router.mjs +16 -13
- package/.pi/scripts/harness-verify.mjs +16 -0
- package/.pi/scripts/vendor-sync-pi-model-router.sh +10 -10
- package/CHANGELOG.md +19 -1
- package/README.md +4 -5
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/package.json +13 -8
- package/vendor/pi-model-router/UPSTREAM_PIN.md +1 -1
- package/vendor/pi-model-router/extensions/commands.ts +2 -2
- package/vendor/pi-model-router/extensions/config.ts +2 -2
- package/vendor/pi-model-router/extensions/index.ts +1 -1
- package/vendor/pi-model-router/extensions/provider.ts +2 -2
- package/vendor/pi-model-router/extensions/routing.ts +2 -2
- package/vendor/pi-model-router/extensions/types.ts +1 -1
- package/vendor/pi-model-router/extensions/ui.ts +1 -1
- package/vendor/pi-model-router/package.json +4 -4
- package/vendor/pi-vcc/index.ts +1 -1
- package/vendor/pi-vcc/package.json +1 -1
- package/vendor/pi-vcc/src/commands/pi-vcc.ts +1 -1
- package/vendor/pi-vcc/src/commands/vcc-recall.ts +1 -1
- package/vendor/pi-vcc/src/core/content.ts +1 -1
- package/vendor/pi-vcc/src/core/load-messages.ts +1 -1
- package/vendor/pi-vcc/src/core/normalize.ts +1 -1
- package/vendor/pi-vcc/src/core/render-entries.ts +1 -1
- package/vendor/pi-vcc/src/core/report.ts +1 -1
- package/vendor/pi-vcc/src/core/search-entries.ts +1 -1
- package/vendor/pi-vcc/src/core/summarize.ts +1 -1
- package/vendor/pi-vcc/src/hooks/before-compact.ts +2 -2
- package/vendor/pi-vcc/src/tools/recall.ts +1 -1
- package/vendor/pi-vcc/src/types.ts +1 -1
- package/vendor/pi-vcc/tests/fixtures.ts +1 -1
- package/vendor/pi-vcc/tests/render-entries.test.ts +1 -1
- package/vendor/pi-vcc/tests/search-entries.test.ts +1 -1
- package/vendor/pi-vcc/tests/support/load-session.ts +2 -2
|
@@ -19,7 +19,7 @@ You are an extensions expert for the Pi coding agent. You know EVERYTHING about
|
|
|
19
19
|
- Flags via `pi.registerFlag()`
|
|
20
20
|
- State management via tool result details and `pi.appendEntry()`
|
|
21
21
|
- Custom rendering via renderCall/renderResult
|
|
22
|
-
- Available imports: `@
|
|
22
|
+
- Available imports: `@earendil-works/pi-coding-agent`, `@sinclair/typebox`, `@earendil-works/pi-ai` (StringEnum), `@earendil-works/pi-tui`
|
|
23
23
|
- System prompt override via before_agent_start
|
|
24
24
|
- Context manipulation via context event
|
|
25
25
|
- Tool blocking and result modification
|
|
@@ -87,7 +87,7 @@ This is CRITICAL for building extensions that work on macOS:
|
|
|
87
87
|
- When a reserved action is remapped away from a key, that key becomes available for extensions
|
|
88
88
|
- The conflict check uses EFFECTIVE keybindings (after user remaps), not defaults
|
|
89
89
|
|
|
90
|
-
### Key Helper (from @
|
|
90
|
+
### Key Helper (from @earendil-works/pi-tui)
|
|
91
91
|
|
|
92
92
|
- `Key.ctrl("x")` → `"ctrl+x"`
|
|
93
93
|
- `Key.shift("tab")` → `"shift+tab"`
|
|
@@ -19,7 +19,7 @@ You are a TUI (Terminal User Interface) expert for the Pi coding agent. You know
|
|
|
19
19
|
- wantsKeyRelease? — for Kitty protocol key release events
|
|
20
20
|
- invalidate() — clear cached render state
|
|
21
21
|
|
|
22
|
-
### Built-in Components (from @
|
|
22
|
+
### Built-in Components (from @earendil-works/pi-tui)
|
|
23
23
|
|
|
24
24
|
- Text: multi-line text with word wrapping, paddingX, paddingY, background function
|
|
25
25
|
- Box: container with padding and background color
|
|
@@ -30,7 +30,7 @@ You are a TUI (Terminal User Interface) expert for the Pi coding agent. You know
|
|
|
30
30
|
- SelectList: selection dialog with theme, onSelect/onCancel
|
|
31
31
|
- SettingsList: toggle settings with theme
|
|
32
32
|
|
|
33
|
-
### From @
|
|
33
|
+
### From @earendil-works/pi-coding-agent
|
|
34
34
|
|
|
35
35
|
- DynamicBorder: border with color function — ALWAYS type the param: `(s: string) => theme.fg("accent", s)`
|
|
36
36
|
- BorderedLoader: spinner with abort support
|
|
@@ -93,7 +93,7 @@ Then read the fetched file. Also search the local codebase for existing TUI comp
|
|
|
93
93
|
## How to Respond
|
|
94
94
|
|
|
95
95
|
- Provide COMPLETE, WORKING component code
|
|
96
|
-
- Include all imports from @
|
|
96
|
+
- Include all imports from @earendil-works/pi-tui and @earendil-works/pi-coding-agent
|
|
97
97
|
- Show the ctx.ui.custom() wrapper for interactive components
|
|
98
98
|
- Handle invalidation properly for theme changes
|
|
99
99
|
- Include keyboard input handling where relevant
|
|
@@ -13,8 +13,8 @@ import { join } from "node:path";
|
|
|
13
13
|
import type {
|
|
14
14
|
BuildSystemPromptOptions,
|
|
15
15
|
ExtensionAPI,
|
|
16
|
-
} from "@
|
|
17
|
-
import { formatSkillsForPrompt } from "@
|
|
16
|
+
} from "@earendil-works/pi-coding-agent";
|
|
17
|
+
import { formatSkillsForPrompt } from "@earendil-works/pi-coding-agent";
|
|
18
18
|
import { resolveHarnessAsset } from "./lib/harness-paths.js";
|
|
19
19
|
|
|
20
20
|
// @ts-expect-error pi extensions run as ESM
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
|
|
8
8
|
import { appendFile, mkdir, readFile } from "node:fs/promises";
|
|
9
9
|
import { join } from "node:path";
|
|
10
|
-
import type { ExtensionAPI } from "@
|
|
10
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
11
11
|
import { getRunIdFromSession } from "../lib/harness-run-context.js";
|
|
12
12
|
|
|
13
13
|
type HarnessPhase = "plan" | "execute" | "evaluate" | "adversary" | "merge";
|
|
@@ -9,9 +9,14 @@ import type {
|
|
|
9
9
|
ExtensionAPI,
|
|
10
10
|
ExtensionContext,
|
|
11
11
|
ThemeColor,
|
|
12
|
-
} from "@
|
|
13
|
-
import type { TUI } from "@
|
|
14
|
-
import {
|
|
12
|
+
} from "@earendil-works/pi-coding-agent";
|
|
13
|
+
import type { TUI } from "@earendil-works/pi-tui";
|
|
14
|
+
import {
|
|
15
|
+
Box,
|
|
16
|
+
Text,
|
|
17
|
+
truncateToWidth,
|
|
18
|
+
visibleWidth,
|
|
19
|
+
} from "@earendil-works/pi-tui";
|
|
15
20
|
|
|
16
21
|
// ── router decision reader ──────────────────────────────────────────
|
|
17
22
|
|
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
* doubling vertical resolution in the same terminal footprint.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import type { ExtensionAPI } from "@
|
|
10
|
-
import { truncateToWidth } from "@
|
|
9
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
10
|
+
import { truncateToWidth } from "@earendil-works/pi-tui";
|
|
11
11
|
import * as JimpModule from "jimp";
|
|
12
12
|
import { resolveHarnessAsset } from "./lib/harness-paths.js";
|
|
13
13
|
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
|
|
17
17
|
import { appendFile, mkdir, readFile, writeFile } from "node:fs/promises";
|
|
18
18
|
import { join } from "node:path";
|
|
19
|
-
import type { ExtensionAPI } from "@
|
|
19
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
20
20
|
import { getRunIdFromSession } from "../lib/harness-run-context.js";
|
|
21
21
|
|
|
22
22
|
type DebateParticipant =
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
import { existsSync, readFileSync } from "node:fs";
|
|
15
15
|
import { resolve } from "node:path";
|
|
16
|
-
import type { ExtensionAPI } from "@
|
|
16
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
17
17
|
|
|
18
18
|
// ── .env parser ──────────────────────────────────────────────────
|
|
19
19
|
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* Emits harness-drift-report custom entries for harness-telemetry + observation bus.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
|
-
import type { ExtensionAPI } from "@
|
|
7
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
8
8
|
|
|
9
9
|
type HarnessPhase = "plan" | "execute" | "evaluate" | "adversary" | "merge";
|
|
10
10
|
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Design references: pi-ask-user, @pi-unipi/ask-user, rpiv-ask-user-question (not vendored).
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import type { ExtensionAPI } from "@
|
|
6
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
7
7
|
import { runAskDialog } from "./lib/ask-user/dialog.js";
|
|
8
8
|
import { runAskFallback } from "./lib/ask-user/fallback.js";
|
|
9
9
|
import { renderAskCall, renderAskResult } from "./lib/ask-user/render.js";
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* in before_agent_start so trace-recorder reuses it on agent_start.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
import type { ExtensionAPI } from "@
|
|
8
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import {
|
|
10
10
|
canonicalPlanPath,
|
|
11
11
|
createFreshRunContext,
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
getPolicyTransitionBlock,
|
|
19
19
|
type HarnessRunContext,
|
|
20
20
|
hasHarnessAbortSignal,
|
|
21
|
+
hasPlanUserApproval,
|
|
21
22
|
isAmendPlanAllowed,
|
|
22
23
|
isHarnessBootstrapPrompt,
|
|
23
24
|
isHarnessSlashCommand,
|
|
@@ -26,7 +27,9 @@ import {
|
|
|
26
27
|
loadProjectActiveRun,
|
|
27
28
|
loadRunContextFromDisk,
|
|
28
29
|
nextStepAfterOutcome,
|
|
30
|
+
nowIso,
|
|
29
31
|
type PlanPacketSummary,
|
|
32
|
+
parseAskUserApprovalFromMessage,
|
|
30
33
|
parseHarnessSlashCommand,
|
|
31
34
|
planPacketSummary,
|
|
32
35
|
readPlanPacketFromPath,
|
|
@@ -70,16 +73,17 @@ function syncPolicyFromPlan(
|
|
|
70
73
|
entries: unknown[],
|
|
71
74
|
planId: string,
|
|
72
75
|
phase: HarnessRunContext["phase"],
|
|
76
|
+
approvedPlan: boolean,
|
|
73
77
|
): void {
|
|
74
78
|
let prior: Record<string, unknown> = {
|
|
75
79
|
phase,
|
|
76
|
-
approvedPlan
|
|
80
|
+
approvedPlan,
|
|
77
81
|
planId,
|
|
78
82
|
budgetBypass: false,
|
|
79
83
|
aborted: false,
|
|
80
84
|
abortReason: null,
|
|
81
85
|
abortedAt: null,
|
|
82
|
-
updatedAt:
|
|
86
|
+
updatedAt: nowIso(),
|
|
83
87
|
};
|
|
84
88
|
for (let i = entries.length - 1; i >= 0; i--) {
|
|
85
89
|
const entry = entries[i] as SessionEntryLike;
|
|
@@ -204,6 +208,7 @@ export default function harnessRunContext(pi: ExtensionAPI) {
|
|
|
204
208
|
entries,
|
|
205
209
|
activeCtx.plan_id ?? "plan-pending",
|
|
206
210
|
"plan",
|
|
211
|
+
false,
|
|
207
212
|
);
|
|
208
213
|
persistContext(pi, activeCtx);
|
|
209
214
|
return {
|
|
@@ -321,15 +326,18 @@ export default function harnessRunContext(pi: ExtensionAPI) {
|
|
|
321
326
|
const task = extractTaskSummary(userPrompt);
|
|
322
327
|
activeCtx = createFreshRunContext(sessionId, projectRoot, task);
|
|
323
328
|
}
|
|
324
|
-
|
|
325
|
-
activeCtx.plan_ready = false;
|
|
326
|
-
}
|
|
329
|
+
activeCtx.plan_ready = false;
|
|
327
330
|
activeCtx.phase = "plan";
|
|
328
331
|
activeCtx.status = "active";
|
|
329
332
|
if (command === "harness-plan") {
|
|
330
333
|
const task = extractTaskSummary(userPrompt);
|
|
331
334
|
if (task) activeCtx.task_summary = task;
|
|
332
335
|
}
|
|
336
|
+
pi.appendEntry("harness-plan-attempt", {
|
|
337
|
+
run_id: activeCtx.run_id,
|
|
338
|
+
command,
|
|
339
|
+
started_at: nowIso(),
|
|
340
|
+
});
|
|
333
341
|
} else if (
|
|
334
342
|
activeCtx &&
|
|
335
343
|
shouldReuseHarnessRunId(userPrompt, activeCtx, command)
|
|
@@ -519,15 +527,31 @@ export default function harnessRunContext(pi: ExtensionAPI) {
|
|
|
519
527
|
) {
|
|
520
528
|
const packet = await readPlanPacketFromPath(activeCtx.plan_packet_path);
|
|
521
529
|
const validation = validatePlanPacket(packet);
|
|
522
|
-
|
|
523
|
-
|
|
530
|
+
const approved = hasPlanUserApproval(entries, {
|
|
531
|
+
sincePlanCommand: true,
|
|
532
|
+
planId: packet?.plan_id ?? null,
|
|
533
|
+
});
|
|
534
|
+
planReady = validation.valid && approved;
|
|
535
|
+
if (validation.valid && !approved) {
|
|
536
|
+
activeCtx.last_outcome = "needs_clarification";
|
|
537
|
+
activeCtx.last_completed_step = "plan";
|
|
538
|
+
const msg =
|
|
539
|
+
"Plan file exists but user approval was not recorded. Present the full plan and call ask_user (Approve) before writing plan-packet.json.";
|
|
540
|
+
if (ctx.hasUI) ctx.ui.notify(msg, "warning");
|
|
541
|
+
else
|
|
542
|
+
pi.sendMessage({
|
|
543
|
+
customType: "harness-plan-packet",
|
|
544
|
+
content: msg,
|
|
545
|
+
display: true,
|
|
546
|
+
});
|
|
547
|
+
} else if (planReady && packet?.plan_id) {
|
|
524
548
|
activeCtx.plan_id = packet.plan_id;
|
|
525
|
-
syncPolicyFromPlan(pi, entries, packet.plan_id, "plan");
|
|
549
|
+
syncPolicyFromPlan(pi, entries, packet.plan_id, "plan", true);
|
|
526
550
|
const summary = planPacketSummary(packet, activeCtx.plan_packet_path);
|
|
527
551
|
pi.appendEntry("harness-plan-packet", summary);
|
|
528
552
|
activeCtx.last_completed_step = "plan";
|
|
529
553
|
activeCtx.last_outcome = summary.plan_status;
|
|
530
|
-
} else {
|
|
554
|
+
} else if (!validation.valid) {
|
|
531
555
|
activeCtx.last_outcome = "needs_clarification";
|
|
532
556
|
activeCtx.last_completed_step = "plan";
|
|
533
557
|
}
|
|
@@ -578,6 +602,24 @@ export default function harnessRunContext(pi: ExtensionAPI) {
|
|
|
578
602
|
}
|
|
579
603
|
});
|
|
580
604
|
|
|
605
|
+
pi.on("tool_result", async (event, ctx) => {
|
|
606
|
+
if (event.toolName !== "ask_user" || event.isError) return;
|
|
607
|
+
const approval = parseAskUserApprovalFromMessage({
|
|
608
|
+
toolName: "ask_user",
|
|
609
|
+
details: event.details,
|
|
610
|
+
content: event.content,
|
|
611
|
+
});
|
|
612
|
+
if (!approval) return;
|
|
613
|
+
const entries = getEntries(ctx);
|
|
614
|
+
const runCtx = getLatestRunContext(entries) ?? activeCtx;
|
|
615
|
+
if (!runCtx) return;
|
|
616
|
+
pi.appendEntry("harness-plan-approval", {
|
|
617
|
+
plan_id: approval.plan_id ?? runCtx.plan_id,
|
|
618
|
+
approved_at: approval.approved_at,
|
|
619
|
+
source: "ask_user",
|
|
620
|
+
});
|
|
621
|
+
});
|
|
622
|
+
|
|
581
623
|
pi.on("tool_call", async (event) => {
|
|
582
624
|
if (!activeCtx?.plan_packet_path) return undefined;
|
|
583
625
|
const phase = activeCtx.phase;
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* harness-web-guard — block bash that bypasses web_search / web_fetch tools.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { ExtensionAPI } from "@
|
|
5
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
|
|
7
7
|
const BLOCK_REASON =
|
|
8
8
|
"harness-web-guard: use web_search (SERP) or web_fetch (page content) instead of raw curl/wget/firecrawl/scrapling fetch. " +
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* harness-web-tools — web_search + web_fetch pi tools wrapping harness-web.py.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { ExtensionAPI } from "@
|
|
5
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { Type } from "@sinclair/typebox";
|
|
7
7
|
import {
|
|
8
8
|
harnessWebContextLine,
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { ExtensionUIContext } from "@
|
|
1
|
+
import type { ExtensionUIContext } from "@earendil-works/pi-coding-agent";
|
|
2
2
|
import {
|
|
3
3
|
Editor,
|
|
4
4
|
type EditorTheme,
|
|
5
5
|
Key,
|
|
6
6
|
matchesKey,
|
|
7
7
|
truncateToWidth,
|
|
8
|
-
} from "@
|
|
8
|
+
} from "@earendil-works/pi-tui";
|
|
9
9
|
import type { AskResponse, DialogResult, ValidatedAskParams } from "./types.js";
|
|
10
10
|
|
|
11
11
|
type DisplayOption = {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import type { AgentToolResult } from "@
|
|
1
|
+
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
2
2
|
import type {
|
|
3
3
|
Theme,
|
|
4
4
|
ToolRenderResultOptions,
|
|
5
|
-
} from "@
|
|
6
|
-
import { Text } from "@
|
|
5
|
+
} from "@earendil-works/pi-coding-agent";
|
|
6
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
7
7
|
import type { AskToolDetails } from "./types.js";
|
|
8
8
|
|
|
9
9
|
export function renderAskCall(
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import { createHash } from "node:crypto";
|
|
6
6
|
import { type Dirent, existsSync, readdirSync, readFileSync } from "node:fs";
|
|
7
7
|
import { join, relative } from "node:path";
|
|
8
|
-
import { getAgentDir } from "@
|
|
8
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
9
9
|
import { parseAgentMarkdown } from "./agent-parser.js";
|
|
10
10
|
import type { AgentConfig } from "./vendored/types.js";
|
|
11
11
|
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Parse harness agent .md files into AgentConfig (path id = posix relative path).
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { parseFrontmatter } from "@
|
|
5
|
+
import { parseFrontmatter } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { BUILTIN_TOOL_NAMES } from "./vendored/agent-types.js";
|
|
7
7
|
import type {
|
|
8
8
|
AgentConfig,
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Orchestrator blackboard tool (list/read/query/wait/delete).
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import { defineTool, type ExtensionAPI } from "@
|
|
5
|
+
import { defineTool, type ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import { Type } from "@sinclair/typebox";
|
|
7
7
|
import type { Blackboard } from "./blackboard.js";
|
|
8
8
|
import type { BlackboardQuery } from "./types-blackboard.js";
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Per-agent tool policy for harness/* subagents (defense in depth with frontmatter).
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import {
|
|
6
|
+
evaluateSubagentToolCall,
|
|
7
|
+
type ToolCallDecision,
|
|
8
|
+
} from "./spawn-policy.js";
|
|
9
|
+
|
|
10
|
+
export type HarnessAgentKind =
|
|
11
|
+
| "planner"
|
|
12
|
+
| "executor"
|
|
13
|
+
| "evaluator"
|
|
14
|
+
| "adversary"
|
|
15
|
+
| "tie_breaker"
|
|
16
|
+
| "meta"
|
|
17
|
+
| "trace"
|
|
18
|
+
| "incident"
|
|
19
|
+
| "other";
|
|
20
|
+
|
|
21
|
+
const MUTATING_TOOLS = new Set(["write", "edit"]);
|
|
22
|
+
|
|
23
|
+
const BASH_MUTATION_PATTERNS = [
|
|
24
|
+
/\brm\s+-/i,
|
|
25
|
+
/\bmv\s+/i,
|
|
26
|
+
/\bcp\s+/i,
|
|
27
|
+
/\btouch\s+/i,
|
|
28
|
+
/\bmkdir\s+/i,
|
|
29
|
+
/\btee\s+/i,
|
|
30
|
+
/\bgit\s+(add|commit|push|reset|checkout|merge|rebase|cherry-pick|apply)\b/i,
|
|
31
|
+
/\bnpm\s+(install|uninstall|ci)\b/i,
|
|
32
|
+
/\bpnpm\s+(add|install|remove)\b/i,
|
|
33
|
+
/\byarn\s+(add|install|remove)\b/i,
|
|
34
|
+
/\bsed\s+-i\b/i,
|
|
35
|
+
/\bperl\s+-i\b/i,
|
|
36
|
+
];
|
|
37
|
+
|
|
38
|
+
const READ_ONLY_KINDS = new Set<HarnessAgentKind>([
|
|
39
|
+
"planner",
|
|
40
|
+
"evaluator",
|
|
41
|
+
"adversary",
|
|
42
|
+
"tie_breaker",
|
|
43
|
+
"trace",
|
|
44
|
+
"incident",
|
|
45
|
+
"meta",
|
|
46
|
+
]);
|
|
47
|
+
|
|
48
|
+
export function classifyHarnessAgent(agentType: string): HarnessAgentKind {
|
|
49
|
+
const id = agentType.replace(/^harness\//, "");
|
|
50
|
+
switch (id) {
|
|
51
|
+
case "planner":
|
|
52
|
+
return "planner";
|
|
53
|
+
case "executor":
|
|
54
|
+
return "executor";
|
|
55
|
+
case "evaluator":
|
|
56
|
+
return "evaluator";
|
|
57
|
+
case "adversary":
|
|
58
|
+
return "adversary";
|
|
59
|
+
case "tie-breaker":
|
|
60
|
+
return "tie_breaker";
|
|
61
|
+
case "meta-optimizer":
|
|
62
|
+
return "meta";
|
|
63
|
+
case "trace-librarian":
|
|
64
|
+
return "trace";
|
|
65
|
+
case "incident-recorder":
|
|
66
|
+
return "incident";
|
|
67
|
+
default:
|
|
68
|
+
return agentType.startsWith("harness/") ? "other" : "other";
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function isMutatingBash(command: string): boolean {
|
|
73
|
+
return BASH_MUTATION_PATTERNS.some((pattern) => pattern.test(command));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export function isHarnessPackageAgent(agentType: string): boolean {
|
|
77
|
+
return agentType.startsWith("harness/");
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function evaluateHarnessSubagentToolCall(
|
|
81
|
+
toolName: string,
|
|
82
|
+
input: Record<string, unknown> | undefined,
|
|
83
|
+
agentType: string,
|
|
84
|
+
): ToolCallDecision {
|
|
85
|
+
const base = evaluateSubagentToolCall(toolName);
|
|
86
|
+
if (base.action === "block") {
|
|
87
|
+
return base;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
if (!isHarnessPackageAgent(agentType)) {
|
|
91
|
+
return { action: "allow" };
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const kind = classifyHarnessAgent(agentType);
|
|
95
|
+
if (!READ_ONLY_KINDS.has(kind)) {
|
|
96
|
+
return { action: "allow" };
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (MUTATING_TOOLS.has(toolName)) {
|
|
100
|
+
return {
|
|
101
|
+
action: "block",
|
|
102
|
+
reason: `harness-subagent-policy: ${toolName} blocked for harness/${kind} (read-only phase agent).`,
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (toolName === "bash") {
|
|
107
|
+
const command = String(input?.command ?? "");
|
|
108
|
+
if (command && isMutatingBash(command)) {
|
|
109
|
+
return {
|
|
110
|
+
action: "block",
|
|
111
|
+
reason: `harness-subagent-policy: mutating bash blocked for harness/${kind}.`,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return { action: "allow" };
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Policy phase hint seeded into subagent system prompt appendix when extensions load policy-gate. */
|
|
120
|
+
export function harnessSubagentPhaseHint(agentType: string): string | null {
|
|
121
|
+
const kind = classifyHarnessAgent(agentType);
|
|
122
|
+
switch (kind) {
|
|
123
|
+
case "planner":
|
|
124
|
+
return "plan";
|
|
125
|
+
case "executor":
|
|
126
|
+
return "execute";
|
|
127
|
+
case "evaluator":
|
|
128
|
+
return "evaluate";
|
|
129
|
+
case "adversary":
|
|
130
|
+
return "adversary";
|
|
131
|
+
default:
|
|
132
|
+
return null;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { randomUUID } from "node:crypto";
|
|
10
|
-
import type { Model } from "@
|
|
10
|
+
import type { Model } from "@earendil-works/pi-ai";
|
|
11
11
|
import type {
|
|
12
12
|
AgentSession,
|
|
13
13
|
ExtensionAPI,
|
|
14
14
|
ExtensionContext,
|
|
15
|
-
} from "@
|
|
15
|
+
} from "@earendil-works/pi-coding-agent";
|
|
16
16
|
import { resumeAgent, runAgent, type ToolActivity } from "./agent-runner.js";
|
|
17
17
|
import type {
|
|
18
18
|
AgentInvocation,
|
|
@@ -5,8 +5,8 @@
|
|
|
5
5
|
import * as fs from "node:fs";
|
|
6
6
|
import * as os from "node:os";
|
|
7
7
|
import * as path from "node:path";
|
|
8
|
-
import type { Model } from "@
|
|
9
|
-
import type { ExtensionContext } from "@
|
|
8
|
+
import type { Model } from "@earendil-works/pi-ai";
|
|
9
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
10
10
|
import {
|
|
11
11
|
type AgentSession,
|
|
12
12
|
type AgentSessionEvent,
|
|
@@ -16,8 +16,8 @@ import {
|
|
|
16
16
|
getAgentDir,
|
|
17
17
|
SessionManager,
|
|
18
18
|
SettingsManager,
|
|
19
|
-
} from "@
|
|
20
|
-
import {
|
|
19
|
+
} from "@earendil-works/pi-coding-agent";
|
|
20
|
+
import { evaluateHarnessSubagentToolCall } from "../harness-subagent-policy.js";
|
|
21
21
|
import {
|
|
22
22
|
getAgentConfig,
|
|
23
23
|
getConfig,
|
|
@@ -331,7 +331,11 @@ export async function runAgent(
|
|
|
331
331
|
const extensionFactories: Array<(pi: ExtensionAPI) => void> = [
|
|
332
332
|
(pi) => {
|
|
333
333
|
pi.on("tool_call", (event) => {
|
|
334
|
-
const decision =
|
|
334
|
+
const decision = evaluateHarnessSubagentToolCall(
|
|
335
|
+
event.toolName,
|
|
336
|
+
event.input as Record<string, unknown> | undefined,
|
|
337
|
+
type,
|
|
338
|
+
);
|
|
335
339
|
if (decision.action === "block") {
|
|
336
340
|
return { block: true, reason: decision.reason };
|
|
337
341
|
}
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* context.ts — Extract parent conversation context for subagent inheritance.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { ExtensionContext } from "@
|
|
5
|
+
import type { ExtensionContext } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
|
|
7
7
|
/** Extract text from a message content block array. */
|
|
8
8
|
export function extractText(content: unknown[]): string {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* env.ts — Detect environment info (git, platform) for subagent system prompts.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { ExtensionAPI } from "@
|
|
5
|
+
import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
|
|
6
6
|
import type { EnvInfo } from "./types.js";
|
|
7
7
|
|
|
8
8
|
export async function detectEnv(
|
|
@@ -14,8 +14,8 @@ import {
|
|
|
14
14
|
type ExtensionCommandContext,
|
|
15
15
|
type ExtensionContext,
|
|
16
16
|
getAgentDir,
|
|
17
|
-
} from "@
|
|
18
|
-
import { Text } from "@
|
|
17
|
+
} from "@earendil-works/pi-coding-agent";
|
|
18
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
19
19
|
import { Type } from "@sinclair/typebox";
|
|
20
20
|
import { getDriftReport } from "../agent-manifest.js";
|
|
21
21
|
import { Blackboard } from "../blackboard.js";
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
import type {
|
|
19
19
|
ExtensionAPI,
|
|
20
20
|
ExtensionContext,
|
|
21
|
-
} from "@
|
|
21
|
+
} from "@earendil-works/pi-coding-agent";
|
|
22
22
|
import { Cron } from "croner";
|
|
23
23
|
import { nanoid } from "nanoid";
|
|
24
24
|
import type { AgentManager } from "./agent-manager.js";
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
6
6
|
import { dirname, join } from "node:path";
|
|
7
|
-
import { getAgentDir } from "@
|
|
7
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
8
8
|
import type { JoinMode } from "./types.js";
|
|
9
9
|
|
|
10
10
|
export interface SubagentsSettings {
|
|
@@ -22,7 +22,7 @@ import type { Dirent } from "node:fs";
|
|
|
22
22
|
import { existsSync, readdirSync } from "node:fs";
|
|
23
23
|
import { homedir } from "node:os";
|
|
24
24
|
import { join } from "node:path";
|
|
25
|
-
import { getAgentDir } from "@
|
|
25
|
+
import { getAgentDir } from "@earendil-works/pi-coding-agent";
|
|
26
26
|
import { isSymlink, isUnsafeName, safeReadFile } from "./memory.js";
|
|
27
27
|
|
|
28
28
|
export interface PreloadedSkill {
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
* types.ts — Type definitions for the subagent system.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
-
import type { ThinkingLevel } from "@
|
|
6
|
-
import type { AgentSession } from "@
|
|
5
|
+
import type { ThinkingLevel } from "@earendil-works/pi-agent-core";
|
|
6
|
+
import type { AgentSession } from "@earendil-works/pi-coding-agent";
|
|
7
7
|
import type { LifetimeUsage } from "./usage.js";
|
|
8
8
|
|
|
9
9
|
export type { ThinkingLevel };
|