pi-better-harness 0.40.1 → 0.40.2

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.
@@ -1,20 +1,60 @@
1
- /** Harness-wide scheduling guidance, vendored so standalone extensions share it. */
2
- const EXECUTION_POLICY = [
1
+ /**
2
+ * Harness-wide scheduling guidance, vendored so standalone extensions share it.
3
+ *
4
+ * Published as a named system-prompt section, never as appended text. A
5
+ * `before_agent_start` handler that returns `systemPrompt` sets
6
+ * `forceSystemPrompt`, and a forced prompt is opaque to pi: `buildSystemPromptState`
7
+ * returns `{ content }` with no sections while `diffSystemPromptSections` keeps
8
+ * diffing the sections the request therefore never shows. The request carries a
9
+ * rendered snapshot, so any section published after that snapshot never reaches
10
+ * it, so every package that speaks here publishes a section.
11
+ */
12
+ export const EXECUTION_POLICY_SECTION = "harness_execution_policy";
13
+
14
+ /**
15
+ * The minimum shape this module needs. `sections` stays optional because this
16
+ * repo typechecks against older pi typings whose event options predate sections;
17
+ * the shipped runtime always supplies the map.
18
+ */
19
+ export interface PromptSectionTarget {
20
+ sections?: Record<string, string>;
21
+ }
22
+
23
+ export const EXECUTION_POLICY_TEXT: string = [
3
24
  "Harness execution policy: concurrency is separate from subagent delegation.",
4
- "Before multi-unit work, identify ready, authorized, independent units, actual prerequisites, shared mutable resources, and capacity or availability constraints. Represent independently verifiable deliverables (such as each host migration) separately in the plan; do not hide them in one serial rollout step or create dependencies merely from list order.",
25
+ "Before multi-unit work, identify ready, authorized, independent units, actual prerequisites, shared mutable resources, and capacity or availability constraints. Represent each independently verifiable deliverable as its own plan step; do not create dependencies from list order.",
5
26
  "Start all ready, authorized, non-conflicting units concurrently within known resource limits before waiting for any one to finish. Reassess ready work after launches and results; continue unblocked foreground work instead of polling running tasks. Do not equate one background launch with scheduling all ready work.",
6
27
  "Serialize only for a concrete dependency, conflicting writes or ownership, resource or concurrency limit, documented rollout gate, or explicit availability requirement. Record the reason in the plan or explain it to the user. Do not invent a canary or host-by-host availability requirement.",
7
- "If concurrency raises an unresolved material operational trade-off (for example, draining all hosts could leave no available runners), ask the user once before the affected operations. Continue independent preparation meanwhile. Existing authorization, safety checks, workflow prerequisites, and human-approved rollout constraints remain binding; urgency does not waive them.",
8
- "Choose the execution mechanism after deciding what can run together: foreground tools for small, tightly coupled, or interactive work; subagents for bounded independent reasoning or implementation when the active delegation mode permits; background tasks for independent long-running commands or waits. Manual delegation forbids proactive subagents, not concurrent foreground tools or background commands. Use available batch or parallel tool interfaces where appropriate and inspect every launch receipt for failures or skipped work.",
9
- "Established command-only jobs and external waits belong to background operations, including in coordinator mode. Use bg_task_spawn for a long command and bg_task_watch for a repeated check; use watch_delivery for supported merge delivery so a stateful domain watcher observes the queue. A subagent assignment must require reasoning or implementation beyond launching and waiting. Workers retain their assigned validation: use supervised background tools and wait_for_operations to park, then resume the same conversation to interpret and acknowledge the terminal result. A linked external wait must have a registered observer and an execution owner. Continue independent work while it runs; a task launch or a completed parked attempt is not completion of the assignment.",
10
- "If a worker hands back a command, the coordinator launches it through background tools with the supplied cwd and inspects its terminal result before claiming verification. Workers may run focused implementation checks; moving a command to a worker does not make it a background task.",
28
+ "If concurrency raises an unresolved material operational trade-off, ask the user once before the affected operations and continue independent preparation meanwhile. Existing authorization, safety checks, workflow prerequisites, and human-approved rollout constraints remain binding; urgency does not waive them.",
29
+ "Choose the execution mechanism after deciding what can run together: foreground tools for small, tightly coupled, or interactive work; subagents for bounded independent reasoning or implementation when the active delegation mode permits; background tasks for independent long-running commands or waits. Manual delegation forbids proactive subagents, not concurrent foreground tools or background commands. Inspect every launch receipt for failures or skipped work.",
30
+ "Established command-only jobs and external waits belong to background operations, including in coordinator mode. Use bg_task_spawn for a long command, bg_task_watch for a repeated check, and watch_delivery for supported merge delivery. A subagent assignment must require reasoning or implementation beyond launching and waiting. Workers retain their assigned validation: launch or watch, continue independent work, then park with wait_for_operations and inspect the terminal evidence on continuation. Continue independent work while it runs; a task launch is not completion of the assignment.",
31
+ "If a worker hands back a command, launch it through bg_task_spawn or bg_task_watch with the supplied cwd, and inspect its terminal result before claiming verification.",
11
32
  "A unit waiting or failing does not block other ready independent units unless the evidence shows a shared blocker. Preserve each unit's guards and rollback boundary. Before completing verification, the plan, or the goal, inspect and integrate every relevant terminal result or failure; launch is not completion.",
12
33
  ].join("\n");
13
34
 
14
- export function appendExecutionPolicy(systemPrompt: string): string {
15
- // Inspect the prompt, not module-global state: vendored copies have separate
16
- // module identities, and every fresh turn or reload needs the policy again.
17
- return systemPrompt.includes(EXECUTION_POLICY)
18
- ? systemPrompt
19
- : `${systemPrompt}\n\n${EXECUTION_POLICY}`;
20
- }
35
+ /**
36
+ * True when this pi runtime renders custom prompt sections.
37
+ *
38
+ * pi gained `systemPromptOptions.sections` after 0.84.4, and the packages here
39
+ * declare support back to 0.84.4. A section-only publisher is a silent no-op on
40
+ * an older host: the handler runs, the map is populated, and the prompt the model
41
+ * receives is unchanged. Callers must fall back to appending text instead.
42
+ */
43
+ export function supportsPromptSections(options: unknown): boolean {
44
+ const sections = (options as { sections?: unknown } | undefined)?.sections;
45
+ return typeof sections === "object" && sections !== null;
46
+ }
47
+
48
+ /**
49
+ * Publish the policy into `options.sections`.
50
+ *
51
+ * Assignment, not concatenation: two packages publishing the same section write
52
+ * identical text, so load order cannot duplicate or drop it, and a repeated turn
53
+ * rewrites the same value so pi's section diff reports no change.
54
+ */
55
+ export function publishExecutionPolicy<T extends object>(options: T): T {
56
+ const target = options as PromptSectionTarget;
57
+ const sections = (target.sections ??= {});
58
+ sections[EXECUTION_POLICY_SECTION] = EXECUTION_POLICY_TEXT;
59
+ return options;
60
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-background-tasks",
3
- "version": "0.14.1",
3
+ "version": "0.14.2",
4
4
  "description": "Pi extension for durable background shell tasks, watchers, logs, and status inspection.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -8,7 +8,7 @@ import { createObservedWaitHost } from "./observed-waits.js";
8
8
  import { getCallbackBatcher } from "./shared-callback-batcher.js";
9
9
  import { observeForegroundSandboxPolicy } from "./sandbox.js";
10
10
  import { registerTools } from "./tools.js";
11
- import { appendExecutionPolicy } from "./shared-execution-policy.js";
11
+ import { deliverBackgroundTasksPrompt } from "./prompt.js";
12
12
 
13
13
  export default function backgroundTasksExtension(pi: ExtensionAPI): void {
14
14
  // Created before registerTools so the tools can resolve it lazily; the durable
@@ -36,7 +36,10 @@ export default function backgroundTasksExtension(pi: ExtensionAPI): void {
36
36
  },
37
37
  });
38
38
  initializeNavigatorPanels(pi);
39
- pi.on("before_agent_start", (event) => ({ systemPrompt: appendExecutionPolicy(event.systemPrompt) }));
39
+ // Sections on a host that renders them; text only where it renders none.
40
+ // Returning a prompt on modern pi would set forceSystemPrompt, and the request
41
+ // would then carry a snapshot, so sections published after it never arrive.
42
+ pi.on("before_agent_start", (event) => deliverBackgroundTasksPrompt(event));
40
43
  registerBackgroundTasksGoalProvider(pi);
41
44
  ensureBackgroundTasksNavigatorProvider(pi);
42
45
  // Subscribed at load so a sandbox extension that publishes later is heard, and
@@ -0,0 +1,72 @@
1
+ /**
2
+ * The operational prompt this package owns: how to wait, and how to launch.
3
+ *
4
+ * Published as a named section so pi can diff it. It exists because the tool
5
+ * descriptions cannot carry it: a description explains one call, while the
6
+ * failure this prevents is choosing `sleep` over a watcher in the first place.
7
+ *
8
+ * Instructions only. No rationale, no worked examples, and no defaults that the
9
+ * tool schemas already state: the model has every schema in context, and repeated
10
+ * reference just spends prefix tokens on text it can already read.
11
+ */
12
+ export const BACKGROUND_TASKS_SECTION = "background_tasks";
13
+
14
+ import { EXECUTION_POLICY_TEXT, publishExecutionPolicy, supportsPromptSections } from "./shared-execution-policy.js";
15
+
16
+ /** `sections` is optional because the repo typechecks against older pi typings. */
17
+ export interface PromptSectionTarget {
18
+ sections?: Record<string, string>;
19
+ }
20
+
21
+ export const BACKGROUND_TASKS_PROMPT: string = [
22
+ "Background tasks: never wait in the foreground.",
23
+ "",
24
+ "- Never use foreground `sleep` to wait or poll, whether alone, before another command, or inside a loop. The sole permitted foreground sleep is at most 3 s immediately after `pkill`, `kill`, or `systemctl stop`, while the process exits.",
25
+ "- Use `bg_task_spawn` and `bg_task_watch` instead of `nohup`, `&`, or `disown`.",
26
+ "- Treat the wait, not the check, as the long part. Waiting for `gh pr checks` to pass is a `bg_task_watch`, not `sleep 300; gh pr checks`.",
27
+ "- Waiting on an external system (`gh run view`, `gh pr checks`, `mergify queue show`, a redeploy, a dev server, `cloud-init`): `bg_task_watch` with the check command and `success_when`. For a PR you just enqueued, `watch_delivery`.",
28
+ "- Waiting on your own long command (tests, build, migration): `bg_task_spawn`, then `wait_for_operations`. Do not sleep and re-read the log.",
29
+ "- Waiting for a server you just started: `bg_task_spawn` the server, then `bg_task_watch` on a readiness check (`curl -fsS ...`).",
30
+ "- Retry until a condition with one `bg_task_watch`, never `for i in ...; do ...; sleep N; done`.",
31
+ "- If a dependency is pending and no watcher is running, start `bg_task_watch`; do not loop short sleeps.",
32
+ "- Nothing here automatically diverts a foreground command. A sleep or monitor loop submitted through foreground `bash` blocks the turn.",
33
+ "- Omit `timeout_seconds` on `bg_task_spawn` unless you need a bound. Raise it on `bg_task_watch` for a wait longer than 15 minutes, and leave `callback` alone: do not poll.",
34
+ "- Write the check so a broken check is visible; do not end it with `exit 0` or `|| true`.",
35
+ "- Stop tasks you started for a turn-scoped purpose before finishing the turn.",
36
+ ].join("\n");
37
+
38
+ /** Publish the guidance into `options.sections`; assignment keeps it idempotent across turns. */
39
+ export function publishBackgroundTasksGuidance<T extends object>(options: T): T {
40
+ const target = options as PromptSectionTarget;
41
+ const sections = (target.sections ??= {});
42
+ sections[BACKGROUND_TASKS_SECTION] = BACKGROUND_TASKS_PROMPT;
43
+ return options;
44
+ }
45
+
46
+ /** What a handler receives; only these two fields are used. */
47
+ export interface BeforeAgentStartEvent {
48
+ systemPrompt: string;
49
+ systemPromptOptions?: unknown;
50
+ }
51
+
52
+ /**
53
+ * Deliver this package's guidance on both kinds of host.
54
+ *
55
+ * Modern pi renders custom sections, so publishing keeps the prompt structured
56
+ * and lets pi diff it. Older pi (the packages declare support back to 0.84.4)
57
+ * renders no sections at all, so publishing there would silently deliver nothing;
58
+ * returning the text is the behaviour those hosts already had.
59
+ */
60
+ export function deliverBackgroundTasksPrompt(event: BeforeAgentStartEvent): { systemPrompt: string } | undefined {
61
+ if (!supportsPromptSections(event.systemPromptOptions)) {
62
+ // Several packages carry the same policy, so appending blindly would print it
63
+ // once per publisher. The text path stays idempotent exactly as it used to be.
64
+ const policy = event.systemPrompt.includes(EXECUTION_POLICY_TEXT) ? "" : `${EXECUTION_POLICY_TEXT}\n\n`;
65
+ const guidance = event.systemPrompt.includes(BACKGROUND_TASKS_PROMPT) ? "" : BACKGROUND_TASKS_PROMPT;
66
+ return { systemPrompt: `${event.systemPrompt}\n\n${policy}${guidance}` };
67
+ }
68
+ const options = event.systemPromptOptions as { sections?: Record<string, string> };
69
+ publishExecutionPolicy(options);
70
+ publishBackgroundTasksGuidance(options);
71
+ return undefined;
72
+ }
@@ -1,21 +1,61 @@
1
1
  // Generated from packages/pi-better-harness/extensions/execution-policy.ts. Do not edit directly.
2
- /** Harness-wide scheduling guidance, vendored so standalone extensions share it. */
3
- const EXECUTION_POLICY = [
2
+ /**
3
+ * Harness-wide scheduling guidance, vendored so standalone extensions share it.
4
+ *
5
+ * Published as a named system-prompt section, never as appended text. A
6
+ * `before_agent_start` handler that returns `systemPrompt` sets
7
+ * `forceSystemPrompt`, and a forced prompt is opaque to pi: `buildSystemPromptState`
8
+ * returns `{ content }` with no sections while `diffSystemPromptSections` keeps
9
+ * diffing the sections the request therefore never shows. The request carries a
10
+ * rendered snapshot, so any section published after that snapshot never reaches
11
+ * it, so every package that speaks here publishes a section.
12
+ */
13
+ export const EXECUTION_POLICY_SECTION = "harness_execution_policy";
14
+
15
+ /**
16
+ * The minimum shape this module needs. `sections` stays optional because this
17
+ * repo typechecks against older pi typings whose event options predate sections;
18
+ * the shipped runtime always supplies the map.
19
+ */
20
+ export interface PromptSectionTarget {
21
+ sections?: Record<string, string>;
22
+ }
23
+
24
+ export const EXECUTION_POLICY_TEXT: string = [
4
25
  "Harness execution policy: concurrency is separate from subagent delegation.",
5
- "Before multi-unit work, identify ready, authorized, independent units, actual prerequisites, shared mutable resources, and capacity or availability constraints. Represent independently verifiable deliverables (such as each host migration) separately in the plan; do not hide them in one serial rollout step or create dependencies merely from list order.",
26
+ "Before multi-unit work, identify ready, authorized, independent units, actual prerequisites, shared mutable resources, and capacity or availability constraints. Represent each independently verifiable deliverable as its own plan step; do not create dependencies from list order.",
6
27
  "Start all ready, authorized, non-conflicting units concurrently within known resource limits before waiting for any one to finish. Reassess ready work after launches and results; continue unblocked foreground work instead of polling running tasks. Do not equate one background launch with scheduling all ready work.",
7
28
  "Serialize only for a concrete dependency, conflicting writes or ownership, resource or concurrency limit, documented rollout gate, or explicit availability requirement. Record the reason in the plan or explain it to the user. Do not invent a canary or host-by-host availability requirement.",
8
- "If concurrency raises an unresolved material operational trade-off (for example, draining all hosts could leave no available runners), ask the user once before the affected operations. Continue independent preparation meanwhile. Existing authorization, safety checks, workflow prerequisites, and human-approved rollout constraints remain binding; urgency does not waive them.",
9
- "Choose the execution mechanism after deciding what can run together: foreground tools for small, tightly coupled, or interactive work; subagents for bounded independent reasoning or implementation when the active delegation mode permits; background tasks for independent long-running commands or waits. Manual delegation forbids proactive subagents, not concurrent foreground tools or background commands. Use available batch or parallel tool interfaces where appropriate and inspect every launch receipt for failures or skipped work.",
10
- "Established command-only jobs and external waits belong to background operations, including in coordinator mode. Use bg_task_spawn for a long command and bg_task_watch for a repeated check; use watch_delivery for supported merge delivery so a stateful domain watcher observes the queue. A subagent assignment must require reasoning or implementation beyond launching and waiting. Workers retain their assigned validation: use supervised background tools and wait_for_operations to park, then resume the same conversation to interpret and acknowledge the terminal result. A linked external wait must have a registered observer and an execution owner. Continue independent work while it runs; a task launch or a completed parked attempt is not completion of the assignment.",
11
- "If a worker hands back a command, the coordinator launches it through background tools with the supplied cwd and inspects its terminal result before claiming verification. Workers may run focused implementation checks; moving a command to a worker does not make it a background task.",
29
+ "If concurrency raises an unresolved material operational trade-off, ask the user once before the affected operations and continue independent preparation meanwhile. Existing authorization, safety checks, workflow prerequisites, and human-approved rollout constraints remain binding; urgency does not waive them.",
30
+ "Choose the execution mechanism after deciding what can run together: foreground tools for small, tightly coupled, or interactive work; subagents for bounded independent reasoning or implementation when the active delegation mode permits; background tasks for independent long-running commands or waits. Manual delegation forbids proactive subagents, not concurrent foreground tools or background commands. Inspect every launch receipt for failures or skipped work.",
31
+ "Established command-only jobs and external waits belong to background operations, including in coordinator mode. Use bg_task_spawn for a long command, bg_task_watch for a repeated check, and watch_delivery for supported merge delivery. A subagent assignment must require reasoning or implementation beyond launching and waiting. Workers retain their assigned validation: launch or watch, continue independent work, then park with wait_for_operations and inspect the terminal evidence on continuation. Continue independent work while it runs; a task launch is not completion of the assignment.",
32
+ "If a worker hands back a command, launch it through bg_task_spawn or bg_task_watch with the supplied cwd, and inspect its terminal result before claiming verification.",
12
33
  "A unit waiting or failing does not block other ready independent units unless the evidence shows a shared blocker. Preserve each unit's guards and rollback boundary. Before completing verification, the plan, or the goal, inspect and integrate every relevant terminal result or failure; launch is not completion.",
13
34
  ].join("\n");
14
35
 
15
- export function appendExecutionPolicy(systemPrompt: string): string {
16
- // Inspect the prompt, not module-global state: vendored copies have separate
17
- // module identities, and every fresh turn or reload needs the policy again.
18
- return systemPrompt.includes(EXECUTION_POLICY)
19
- ? systemPrompt
20
- : `${systemPrompt}\n\n${EXECUTION_POLICY}`;
21
- }
36
+ /**
37
+ * True when this pi runtime renders custom prompt sections.
38
+ *
39
+ * pi gained `systemPromptOptions.sections` after 0.84.4, and the packages here
40
+ * declare support back to 0.84.4. A section-only publisher is a silent no-op on
41
+ * an older host: the handler runs, the map is populated, and the prompt the model
42
+ * receives is unchanged. Callers must fall back to appending text instead.
43
+ */
44
+ export function supportsPromptSections(options: unknown): boolean {
45
+ const sections = (options as { sections?: unknown } | undefined)?.sections;
46
+ return typeof sections === "object" && sections !== null;
47
+ }
48
+
49
+ /**
50
+ * Publish the policy into `options.sections`.
51
+ *
52
+ * Assignment, not concatenation: two packages publishing the same section write
53
+ * identical text, so load order cannot duplicate or drop it, and a repeated turn
54
+ * rewrites the same value so pi's section diff reports no change.
55
+ */
56
+ export function publishExecutionPolicy<T extends object>(options: T): T {
57
+ const target = options as PromptSectionTarget;
58
+ const sections = (target.sections ??= {});
59
+ sections[EXECUTION_POLICY_SECTION] = EXECUTION_POLICY_TEXT;
60
+ return options;
61
+ }
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-goal",
3
- "version": "0.12.0",
3
+ "version": "0.12.1",
4
4
  "description": "Pi extension for goal tracking with background-aware continuation.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -30,6 +30,7 @@ import {
30
30
  } from "./goal-state.js";
31
31
  import { continuationEvidence, type ContinuationEvidence } from "./continuation.js";
32
32
  import { currentPermissionHold, logicalPermissionBlockerKey, MAX_PERMISSION_BLOCKERS, MAX_PERMISSION_RECORDS, permissionHoldRecord, permissionInstruction, permissionRecord, reportedPermissionBlockers, reportedPermissionGap, type PermissionHold } from "./permission-hold.js";
33
+ import { publishGoalGuidance } from "./prompt.js";
33
34
  import { observeGoalStall } from "./stall.js";
34
35
  import {
35
36
  flattenObjectiveForRail,
@@ -1360,12 +1361,12 @@ export default function (pi: ExtensionAPI): void {
1360
1361
  (permissions.blockers.length === 0 ? "\nBlocker evidence is unavailable. Stay held; do not invent a retry scope." : "") : "";
1361
1362
  // A held goal overrides workflow ownership without invoking or invalidating that workflow.
1362
1363
  if (permissionPrompt && goal?.status === "paused") {
1363
- return { systemPrompt: `${event.systemPrompt}\n\n${permissionPrompt}` };
1364
+ return publishGoalGuidance(event, permissionPrompt);
1364
1365
  }
1365
1366
  const pausedInstruction = permissionPrompt || (goal?.status === "paused" && (goal.pauseReason === "interrupt" || goal.pauseReason === "handback")
1366
1367
  ? pausedGoalPrompt(goal, preferences.conversationalResume, !harnessPresent) : "");
1367
1368
  if (!isPokeable(goal) && !owner) {
1368
- return pausedInstruction ? { systemPrompt: `${event.systemPrompt}\n\n${pausedInstruction}` } : undefined;
1369
+ return publishGoalGuidance(event, pausedInstruction);
1369
1370
  }
1370
1371
 
1371
1372
  const questionInstruction = snapshot.backgroundRunning
@@ -1379,31 +1380,28 @@ export default function (pi: ExtensionAPI): void {
1379
1380
  if (!workflowAvailable(owner)) {
1380
1381
  if (isPokeable(goal)) setGoal(goalWithStatus(goal, "paused"), ctx, "runtime");
1381
1382
  recordWorkflow(null);
1382
- return { systemPrompt: `${event.systemPrompt}\n\nWorkflow ${owner.name} is no longer a registered, valid skill. Stop work and ask the user to reinvoke the skill.` };
1383
+ return publishGoalGuidance(event, `Workflow ${owner.name} is no longer a registered, valid skill. Stop work and ask the user to reinvoke the skill.`);
1383
1384
  }
1384
1385
  let instructions: string;
1385
1386
  try {
1386
1387
  instructions = readFileSync(owner.path, "utf8");
1387
1388
  } catch {
1388
1389
  if (isPokeable(goal)) setGoal(goalWithStatus(goal, "paused"), ctx, "runtime");
1389
- return { systemPrompt: `${event.systemPrompt}\n\nWorkflow ${owner.name} is unavailable. Stop work and ask the user to restore or reinvoke the skill.` };
1390
+ return publishGoalGuidance(event, `Workflow ${owner.name} is unavailable. Stop work and ask the user to restore or reinvoke the skill.`);
1390
1391
  }
1391
- return {
1392
- systemPrompt: `${event.systemPrompt}\n\n` +
1393
- (pausedInstruction
1394
- ? `${pausedInstruction}\n` + (permissionPrompt
1395
- ? "These permission constraints override the workflow instructions below.\n\n"
1396
- : "While the goal is paused, this overrides the workflow instructions below: do not advance the workflow until the goal is resumed.\n\n")
1397
- : "") +
1398
- `Active workflow: ${owner.name} (${owner.path}). Its task plan owns planning and the parent is a coordinator, not a product-code implementer. Follow the workflow instructions below, including on resumed turns:\n\n${instructions}` +
1399
- (isPokeable(goal)
1400
- ? harnessPresent
1401
- ? "\n\nComplete the goal only after the workflow completion audit."
1402
- : `\n\nActive objective: ${goal.objective}. Complete it only after the workflow completion audit.`
1403
- : "") +
1404
- (questionInstruction ? `\n\n${questionInstruction.trim()}` : "") +
1405
- (heldInstruction ? `\n\n${heldInstruction}` : ""),
1406
- };
1392
+ return publishGoalGuidance(
1393
+ event,
1394
+ pausedInstruction &&
1395
+ `${pausedInstruction}\n` + (permissionPrompt
1396
+ ? "These permission constraints override the workflow instructions below.\n\n"
1397
+ : "While the goal is paused, this overrides the workflow instructions below: do not advance the workflow until the goal is resumed.\n\n"),
1398
+ `Active workflow: ${owner.name} (${owner.path}). Its task plan owns planning and the parent is a coordinator, not a product-code implementer. Follow the workflow instructions below, including on resumed turns:\n\n${instructions}`,
1399
+ isPokeable(goal) && (harnessPresent
1400
+ ? "Complete the goal only after the workflow completion audit."
1401
+ : `Active objective: ${goal.objective}. Complete it only after the workflow completion audit.`),
1402
+ questionInstruction && questionInstruction.trim(),
1403
+ heldInstruction,
1404
+ );
1407
1405
  }
1408
1406
 
1409
1407
  if (!isPokeable(goal)) return;
@@ -1413,18 +1411,19 @@ export default function (pi: ExtensionAPI): void {
1413
1411
  if (harnessPresent) {
1414
1412
  // Harness owns the outcome text. Retain only the Goal-specific guidance
1415
1413
  // that the Outcome & Todo prompt cannot replace.
1416
- const retained = [heldInstruction, backgroundInstruction.trim(), questionInstruction.trim(), permissionPrompt]
1417
- .filter((part) => part.length > 0);
1418
- return retained.length > 0
1419
- ? { systemPrompt: `${event.systemPrompt}\n\n${retained.join("\n\n")}` }
1420
- : undefined;
1421
- }
1422
- return {
1423
- systemPrompt:
1424
- `${event.systemPrompt}\n\n` +
1425
- `Pi Better Goal active objective: ${goal.objective}. ${heldInstruction || "Keep working through clear low-risk next steps, and mark complete only after an evidence-backed completion audit."}${backgroundInstruction}${questionInstruction}` +
1426
- (permissionPrompt ? `\n\n${permissionPrompt}` : ""),
1427
- };
1414
+ return publishGoalGuidance(
1415
+ event,
1416
+ heldInstruction,
1417
+ backgroundInstruction.trim(),
1418
+ questionInstruction.trim(),
1419
+ permissionPrompt,
1420
+ );
1421
+ }
1422
+ return publishGoalGuidance(
1423
+ event,
1424
+ `Pi Better Goal active objective: ${goal.objective}. ${heldInstruction || "Keep working through clear low-risk next steps, and mark complete only after an evidence-backed completion audit."}${backgroundInstruction}${questionInstruction}`,
1425
+ permissionPrompt,
1426
+ );
1428
1427
  });
1429
1428
 
1430
1429
  pi.on("agent_start", async (_event, ctx) => {
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Goal-owned system-prompt guidance.
3
+ *
4
+ * Published as a named section on hosts that render sections, never as a returned
5
+ * `systemPrompt` there: returning text sets `forceSystemPrompt`, and the request
6
+ * then carries an opaque rendered snapshot, so a section published afterwards
7
+ * never reaches the model.
8
+ *
9
+ * On older hosts (this package declares support back to pi 0.84.4) custom sections
10
+ * do not render at all, so the same text is appended instead. Publishing alone
11
+ * would silently deliver nothing on those versions.
12
+ */
13
+ export const GOAL_SECTION = "goal";
14
+
15
+ /** The minimum shape this module needs; both the event options and a test double satisfy it. */
16
+ export interface PromptSectionTarget {
17
+ sections?: Record<string, string>;
18
+ }
19
+
20
+ /** What a handler receives; only these two fields are used. */
21
+ export interface BeforeAgentStartEvent {
22
+ systemPrompt: string;
23
+ systemPromptOptions?: unknown;
24
+ }
25
+
26
+ /** Mirrors `supportsPromptSections` in the vendored execution policy. */
27
+ function rendersSections(options: unknown): boolean {
28
+ const sections = (options as { sections?: unknown } | undefined)?.sections;
29
+ return typeof sections === "object" && sections !== null;
30
+ }
31
+
32
+ /**
33
+ * Deliver guidance, choosing the channel this host understands.
34
+ *
35
+ * Appending within one turn is safe: pi normalizes a fresh options object for
36
+ * every `before_agent_start`, so this never accumulates between requests, and
37
+ * within a turn the parts belong together and keep their order.
38
+ */
39
+ export function publishGoalGuidance(
40
+ event: BeforeAgentStartEvent,
41
+ ...parts: Array<string | undefined | false>
42
+ ): { systemPrompt: string } | undefined {
43
+ const text = parts.filter((part): part is string => typeof part === "string" && part.trim().length > 0).join("\n\n");
44
+ if (!text) return undefined;
45
+ if (!rendersSections(event.systemPromptOptions)) {
46
+ return { systemPrompt: `${event.systemPrompt}\n\n${text}` };
47
+ }
48
+ const target = event.systemPromptOptions as PromptSectionTarget;
49
+ const sections = (target.sections ??= {});
50
+ const existing = sections[GOAL_SECTION];
51
+ sections[GOAL_SECTION] = existing ? `${existing}\n\n${text}` : text;
52
+ return undefined;
53
+ }
@@ -1,8 +1,10 @@
1
1
  import type { ExtensionAPI } from "@earendil-works/pi-coding-agent";
2
2
  import { parse } from "shell-quote";
3
3
  import { STEER_FILE_ENV } from "./child-steer.ts";
4
+ import { supportsPromptSections } from "./shared-execution-policy.ts";
4
5
 
5
6
  export const WORKER_HANDOFF = "Worker ownership: complete your assigned implementation or diagnosis, run focused checks, push only when authorized, write the requested result, then return. Pending CI is a handoff, not a failed implementation or a CI pass. The coordinator owns CI/PR monitoring, review scheduling, readiness, enqueueing and merge. Do not start external delivery watchers or background monitoring. Short status reads and failed-log retrieval for diagnosis are allowed. Legitimate local tests and builds may run to completion within their command limits. Use Bash for short diagnostic commands and focused checks needed to implement the change. When supervised background tools are available, retain ownership of assigned validation: launch a known long command with bg_task_spawn or repeated observation with bg_task_watch, continue independent work, then call wait_for_operations to park when no independent work remains. On continuation, inspect the operation's terminal evidence and acknowledge its interpretation before continuing the assignment. Parking is a saved-conversation dependency, not a request for the parent model to relay commands or results. An explicit restricted tool loadout may omit background support; return the exact command, cwd, required environment variable names (never secret values), current evidence and remaining validation to the coordinator. Do not emulate background execution with polling, sleep loops, nohup or shell detachment. Established command-only operations and external delivery monitoring belong to the coordinator's background tools.";
7
+ export const WORKER_HANDOFF_SECTION = "worker_handoff";
6
8
  const REFUSAL = "Worker ownership: CI/PR monitoring belongs to the coordinator. Return the PR/run ID, pushed HEAD and current evidence with CI pending; do not retry the watcher through another tool or script. Local tests/builds and short diagnostic status/log reads remain allowed.";
7
9
  const WATCH_SCRIPTS = new Set(["watch-pr-delivery.mjs", "watch-delivery.mjs", "watch-candidate-delivery.mjs"]);
8
10
  const nameOf = (value: string) => value.replaceAll("\\", "/").split("/").at(-1)!.replace(/\.exe$/i, "");
@@ -133,7 +135,18 @@ export function workerMonitoringRefusal(toolName: string, input: unknown): strin
133
135
  export default function childOwnership(pi: Pick<ExtensionAPI, "on">, env: NodeJS.ProcessEnv = process.env): void {
134
136
  // This marker is supplied by the parent on every child launch, including clean/unsandboxed runs.
135
137
  if (!env[STEER_FILE_ENV]) return;
136
- pi.on("before_agent_start", (event) => ({ systemPrompt: `${event.systemPrompt}\n\n${WORKER_HANDOFF}` }));
138
+ // A section on hosts that render them; appended text on hosts that do not.
139
+ // Returning a prompt on a modern host would set forceSystemPrompt, and the
140
+ // request would carry a snapshot, so later sections never arrive.
141
+ pi.on("before_agent_start", (event) => {
142
+ if (!supportsPromptSections(event.systemPromptOptions)) {
143
+ return { systemPrompt: `${event.systemPrompt}\n\n${WORKER_HANDOFF}` };
144
+ }
145
+ const options = event.systemPromptOptions as { sections?: Record<string, string> };
146
+ const sections = (options.sections ??= {});
147
+ sections[WORKER_HANDOFF_SECTION] = WORKER_HANDOFF;
148
+ return undefined;
149
+ });
137
150
  pi.on("tool_call", (event) => {
138
151
  const reason = workerMonitoringRefusal(event.toolName, event.input);
139
152
  if (reason) return { block: true, reason };
@@ -1,6 +1,8 @@
1
1
  import { createHash } from "node:crypto";
2
2
  import { delegationPrompt, type DelegationMode } from "./delegation.ts";
3
3
 
4
+ import { EXECUTION_POLICY_TEXT, publishExecutionPolicy, supportsPromptSections } from "./shared-execution-policy.ts";
5
+
4
6
  export const SUBAGENT_SETTINGS_CONTEXT = "pi-better-subagents-settings";
5
7
 
6
8
  export interface EffectiveSubagentSettings {
@@ -48,18 +50,42 @@ export function formatSubagentSettings(settings: EffectiveSubagentSettings): str
48
50
  }
49
51
 
50
52
  /**
51
- * Append the delegation policy to the end of the system prompt.
53
+ * Publish the delegation policy as its own prompt section.
52
54
  *
53
- * The system prompt is not part of the compaction window, so the policy survives
54
- * a summary that rewrites the conversation. It is also part of the cached
55
- * prefix, so it costs nothing per request while it stays unchanged.
55
+ * A section survives a summary that rewrites the conversation without being
56
+ * appended text, and it is part of the cached prefix, so it costs nothing per
57
+ * request while it stays unchanged. Assignment is idempotent: a repeated turn
58
+ * rewrites the same value and pi's section diff reports no change.
56
59
  *
57
60
  * Nothing volatile goes in here: live occupancy would rewrite the cached prefix
58
61
  * on every run, and admission rechecks capacity at spawn anyway.
59
62
  */
60
- export function appendSubagentSettings(systemPrompt: string, settings: EffectiveSubagentSettings): string {
61
- const policy = formatSubagentSettings(settings);
62
- return systemPrompt.includes(policy) ? systemPrompt : `${systemPrompt}\n\n${policy}`;
63
+ export const SUBAGENT_SETTINGS_SECTION = "subagents";
64
+
65
+ /** `sections` is optional because the repo typechecks against older pi typings. */
66
+ export interface PromptSectionTarget {
67
+ sections?: Record<string, string>;
68
+ }
69
+
70
+ /**
71
+ * Published with the settings block: the block is the authority on delegation, so
72
+ * the sentence that says so has to sit beside it rather than in a separate section
73
+ * whose position would depend on package load order.
74
+ *
75
+ * The note must not say the block is at the end of the prompt. Custom sections
76
+ * render in publication order, so Background Tasks and Goal publish after this one;
77
+ * naming its own section keeps the sentence true whatever the load order.
78
+ */
79
+ const SUBAGENT_DELEGATION_NOTE = "Treat the Subagent settings block in this section as the authoritative delegation mode and cap. Its delegation guidance replaces earlier mode-specific guidance, including guidance in a plan. Live occupancy is not pushed into context: inspect subagent_settings when it matters, and never treat the concurrency limit as a target number of agents.";
80
+
81
+ export function publishSubagentSettings<T extends object>(options: T, settings: EffectiveSubagentSettings): T {
82
+ // Note first, block last inside the section: the note introduces the block it
83
+ // describes. Section order in the prompt is a separate concern, decided by
84
+ // publication order, so the note never claims a position in the whole prompt.
85
+ const target = options as PromptSectionTarget;
86
+ const sections = (target.sections ??= {});
87
+ sections[SUBAGENT_SETTINGS_SECTION] = `${SUBAGENT_DELEGATION_NOTE}\n\n${formatSubagentSettings(settings)}`;
88
+ return options;
63
89
  }
64
90
 
65
91
  export function subagentSettingsTool(Type: any, read: () => EffectiveSubagentSettings) {
@@ -81,4 +107,31 @@ export function subagentSettingsTool(Type: any, read: () => EffectiveSubagentSet
81
107
  details: settings, structuredContent: settings };
82
108
  },
83
109
  };
84
- }
110
+ }
111
+ /** What a handler receives; only these two fields are used. */
112
+ export interface BeforeAgentStartEvent {
113
+ systemPrompt: string;
114
+ systemPromptOptions?: unknown;
115
+ }
116
+
117
+ /**
118
+ * Deliver the policy and the settings on both kinds of host.
119
+ *
120
+ * pi before 0.84.4+ renders no custom sections, so publishing alone would
121
+ * deliver nothing on a host this package still declares support for. There the
122
+ * guidance is appended as text, which is what those hosts did before sections.
123
+ */
124
+ export function deliverSubagentPrompt(event: BeforeAgentStartEvent, settings: EffectiveSubagentSettings): { systemPrompt: string } | undefined {
125
+ if (!supportsPromptSections(event.systemPromptOptions)) {
126
+ // Both packages carry this policy, so appending blindly would print it
127
+ // once per publisher; keep the text path idempotent as it used to be.
128
+ const policy = event.systemPrompt.includes(EXECUTION_POLICY_TEXT) ? "" : `${EXECUTION_POLICY_TEXT}\n\n`;
129
+ const block = `${SUBAGENT_DELEGATION_NOTE}\n\n${formatSubagentSettings(settings)}`;
130
+ const guidance = event.systemPrompt.includes(block) ? "" : block;
131
+ return { systemPrompt: `${event.systemPrompt}\n\n${policy}${guidance}` };
132
+ }
133
+ const options = event.systemPromptOptions as PromptSectionTarget;
134
+ publishExecutionPolicy(options);
135
+ publishSubagentSettings(options, settings);
136
+ return undefined;
137
+ }
@@ -38,7 +38,7 @@ import { selectSessionByName } from "./session-selection.ts";
38
38
  import { isWithinWorkspace } from "./workspace-scope.ts";
39
39
  import { loadConfig, writeDelegationMode, writeSubagentSettings, isConcurrencyCap, normalizeConcurrencyCap, normalizeTools, resolveExtensionPath, configPath, selfDir, SAFE_DEFAULT_TOOLS, SAFE_CLEAN_TOOLS } from "./config.ts";
40
40
  import { createSubagentSettingsPage } from "./settings-page.ts";
41
- import { appendSubagentSettings, effectiveSubagentSettings, formatSubagentSettings, subagentSettingsTool, SUBAGENT_SETTINGS_CONTEXT } from "./effective-settings.ts";
41
+ import { deliverSubagentPrompt, effectiveSubagentSettings, formatSubagentSettings, subagentSettingsTool, SUBAGENT_SETTINGS_CONTEXT } from "./effective-settings.ts";
42
42
  import { STEER_FILE_ENV, readSteerReceipt } from "./child-steer.ts";
43
43
  import {
44
44
  decideTiming,
@@ -56,7 +56,6 @@ import {
56
56
  } from "./timing.ts";
57
57
  import { readAppendedLines, type LogCursor } from "./log-cursor.ts";
58
58
  import { DELEGATION_MODES, DELEGATION_MODE_REQUEST, isDelegationMode, normalizeDelegationMode, type DelegationMode } from "./delegation.ts";
59
- import { appendExecutionPolicy } from "./shared-execution-policy.ts";
60
59
  import { resolveChildClosure, resolveChildLaunch, configSnapshot } from "./child-dependencies.mjs";
61
60
  import { PREFLIGHT_ENV } from "./child-preflight.mjs";
62
61
  import { resolveExtensions, extensionArgs } from "./extensions.ts";
@@ -3602,12 +3601,10 @@ export default function (pi: ExtensionAPI) {
3602
3601
  const discoveryTool = agentOperations.createDiscoveryTool(Type as never);
3603
3602
  pi.registerTool(discoveryTool as Parameters<ExtensionAPI["registerTool"]>[0]);
3604
3603
 
3605
- pi.on("before_agent_start", (event) => ({
3606
- systemPrompt: appendSubagentSettings(
3607
- `${appendExecutionPolicy(event.systemPrompt)}\n\nTreat the Subagent settings block at the end of this prompt as the authoritative delegation mode and cap. Its delegation guidance replaces earlier mode-specific guidance, including guidance in a plan. Live occupancy is not pushed into context: inspect subagent_settings when it matters, and never treat the concurrency limit as a target number of agents.`,
3608
- effectiveSettings(),
3609
- ),
3610
- }));
3604
+ // Sections on a host that renders them; text only where it renders none.
3605
+ // Returning a prompt on modern pi would set forceSystemPrompt, and the request
3606
+ // would then carry a snapshot, so sections published after it never arrive.
3607
+ pi.on("before_agent_start", (event) => deliverSubagentPrompt(event, effectiveSettings()));
3611
3608
 
3612
3609
  // ---- live-status lifecycle -----------------------------------------
3613
3610
  pi.on("agent_start", async (_event, ctx) => {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-subagents",
3
- "version": "0.27.1",
3
+ "version": "0.27.2",
4
4
  "description": "Pi extension for detached, sandboxed subagent runs that keep the foreground session free.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -1,21 +1,61 @@
1
1
  // Generated from packages/pi-better-harness/extensions/execution-policy.ts. Do not edit directly.
2
- /** Harness-wide scheduling guidance, vendored so standalone extensions share it. */
3
- const EXECUTION_POLICY = [
2
+ /**
3
+ * Harness-wide scheduling guidance, vendored so standalone extensions share it.
4
+ *
5
+ * Published as a named system-prompt section, never as appended text. A
6
+ * `before_agent_start` handler that returns `systemPrompt` sets
7
+ * `forceSystemPrompt`, and a forced prompt is opaque to pi: `buildSystemPromptState`
8
+ * returns `{ content }` with no sections while `diffSystemPromptSections` keeps
9
+ * diffing the sections the request therefore never shows. The request carries a
10
+ * rendered snapshot, so any section published after that snapshot never reaches
11
+ * it, so every package that speaks here publishes a section.
12
+ */
13
+ export const EXECUTION_POLICY_SECTION = "harness_execution_policy";
14
+
15
+ /**
16
+ * The minimum shape this module needs. `sections` stays optional because this
17
+ * repo typechecks against older pi typings whose event options predate sections;
18
+ * the shipped runtime always supplies the map.
19
+ */
20
+ export interface PromptSectionTarget {
21
+ sections?: Record<string, string>;
22
+ }
23
+
24
+ export const EXECUTION_POLICY_TEXT: string = [
4
25
  "Harness execution policy: concurrency is separate from subagent delegation.",
5
- "Before multi-unit work, identify ready, authorized, independent units, actual prerequisites, shared mutable resources, and capacity or availability constraints. Represent independently verifiable deliverables (such as each host migration) separately in the plan; do not hide them in one serial rollout step or create dependencies merely from list order.",
26
+ "Before multi-unit work, identify ready, authorized, independent units, actual prerequisites, shared mutable resources, and capacity or availability constraints. Represent each independently verifiable deliverable as its own plan step; do not create dependencies from list order.",
6
27
  "Start all ready, authorized, non-conflicting units concurrently within known resource limits before waiting for any one to finish. Reassess ready work after launches and results; continue unblocked foreground work instead of polling running tasks. Do not equate one background launch with scheduling all ready work.",
7
28
  "Serialize only for a concrete dependency, conflicting writes or ownership, resource or concurrency limit, documented rollout gate, or explicit availability requirement. Record the reason in the plan or explain it to the user. Do not invent a canary or host-by-host availability requirement.",
8
- "If concurrency raises an unresolved material operational trade-off (for example, draining all hosts could leave no available runners), ask the user once before the affected operations. Continue independent preparation meanwhile. Existing authorization, safety checks, workflow prerequisites, and human-approved rollout constraints remain binding; urgency does not waive them.",
9
- "Choose the execution mechanism after deciding what can run together: foreground tools for small, tightly coupled, or interactive work; subagents for bounded independent reasoning or implementation when the active delegation mode permits; background tasks for independent long-running commands or waits. Manual delegation forbids proactive subagents, not concurrent foreground tools or background commands. Use available batch or parallel tool interfaces where appropriate and inspect every launch receipt for failures or skipped work.",
10
- "Established command-only jobs and external waits belong to background operations, including in coordinator mode. Use bg_task_spawn for a long command and bg_task_watch for a repeated check; use watch_delivery for supported merge delivery so a stateful domain watcher observes the queue. A subagent assignment must require reasoning or implementation beyond launching and waiting. Workers retain their assigned validation: use supervised background tools and wait_for_operations to park, then resume the same conversation to interpret and acknowledge the terminal result. A linked external wait must have a registered observer and an execution owner. Continue independent work while it runs; a task launch or a completed parked attempt is not completion of the assignment.",
11
- "If a worker hands back a command, the coordinator launches it through background tools with the supplied cwd and inspects its terminal result before claiming verification. Workers may run focused implementation checks; moving a command to a worker does not make it a background task.",
29
+ "If concurrency raises an unresolved material operational trade-off, ask the user once before the affected operations and continue independent preparation meanwhile. Existing authorization, safety checks, workflow prerequisites, and human-approved rollout constraints remain binding; urgency does not waive them.",
30
+ "Choose the execution mechanism after deciding what can run together: foreground tools for small, tightly coupled, or interactive work; subagents for bounded independent reasoning or implementation when the active delegation mode permits; background tasks for independent long-running commands or waits. Manual delegation forbids proactive subagents, not concurrent foreground tools or background commands. Inspect every launch receipt for failures or skipped work.",
31
+ "Established command-only jobs and external waits belong to background operations, including in coordinator mode. Use bg_task_spawn for a long command, bg_task_watch for a repeated check, and watch_delivery for supported merge delivery. A subagent assignment must require reasoning or implementation beyond launching and waiting. Workers retain their assigned validation: launch or watch, continue independent work, then park with wait_for_operations and inspect the terminal evidence on continuation. Continue independent work while it runs; a task launch is not completion of the assignment.",
32
+ "If a worker hands back a command, launch it through bg_task_spawn or bg_task_watch with the supplied cwd, and inspect its terminal result before claiming verification.",
12
33
  "A unit waiting or failing does not block other ready independent units unless the evidence shows a shared blocker. Preserve each unit's guards and rollback boundary. Before completing verification, the plan, or the goal, inspect and integrate every relevant terminal result or failure; launch is not completion.",
13
34
  ].join("\n");
14
35
 
15
- export function appendExecutionPolicy(systemPrompt: string): string {
16
- // Inspect the prompt, not module-global state: vendored copies have separate
17
- // module identities, and every fresh turn or reload needs the policy again.
18
- return systemPrompt.includes(EXECUTION_POLICY)
19
- ? systemPrompt
20
- : `${systemPrompt}\n\n${EXECUTION_POLICY}`;
21
- }
36
+ /**
37
+ * True when this pi runtime renders custom prompt sections.
38
+ *
39
+ * pi gained `systemPromptOptions.sections` after 0.84.4, and the packages here
40
+ * declare support back to 0.84.4. A section-only publisher is a silent no-op on
41
+ * an older host: the handler runs, the map is populated, and the prompt the model
42
+ * receives is unchanged. Callers must fall back to appending text instead.
43
+ */
44
+ export function supportsPromptSections(options: unknown): boolean {
45
+ const sections = (options as { sections?: unknown } | undefined)?.sections;
46
+ return typeof sections === "object" && sections !== null;
47
+ }
48
+
49
+ /**
50
+ * Publish the policy into `options.sections`.
51
+ *
52
+ * Assignment, not concatenation: two packages publishing the same section write
53
+ * identical text, so load order cannot duplicate or drop it, and a repeated turn
54
+ * rewrites the same value so pi's section diff reports no change.
55
+ */
56
+ export function publishExecutionPolicy<T extends object>(options: T): T {
57
+ const target = options as PromptSectionTarget;
58
+ const sections = (target.sections ??= {});
59
+ sections[EXECUTION_POLICY_SECTION] = EXECUTION_POLICY_TEXT;
60
+ return options;
61
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-harness",
3
- "version": "0.40.1",
3
+ "version": "0.40.2",
4
4
  "description": "Pi extension bundle for a write sandbox, subagents, background tasks, SSH, goals, model routing, and Outcome & Todo.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -55,13 +55,13 @@
55
55
  "test": "node --import tsx --import ../../scripts/isolate-registry.mjs --test test/*.test.mjs test/*.test.ts test/task-context/*.test.ts"
56
56
  },
57
57
  "dependencies": {
58
- "pi-better-background-tasks": "0.14.1",
59
- "pi-better-goal": "0.12.0",
58
+ "pi-better-background-tasks": "0.14.2",
59
+ "pi-better-goal": "0.12.1",
60
60
  "pi-better-model-router": "0.1.2",
61
61
  "pi-ollama-cloud": "0.13.1",
62
62
  "pi-better-sandbox": "0.14.1",
63
63
  "pi-better-ssh": "0.1.4",
64
- "pi-better-subagents": "0.27.1",
64
+ "pi-better-subagents": "0.27.2",
65
65
  "proper-lockfile": "^4.1.2",
66
66
  "shell-quote": "1.12.0",
67
67
  "smol-toml": "1.9.0",
@@ -1,19 +0,0 @@
1
- // Generated from packages/pi-better-harness/extensions/execution-policy.ts. Do not edit directly.
2
- /** Harness-wide scheduling guidance, vendored so standalone extensions share it. */
3
- const EXECUTION_POLICY = [
4
- "Harness execution policy: concurrency is separate from subagent delegation.",
5
- "Before multi-unit work, identify ready, authorized, independent units, actual prerequisites, shared mutable resources, and capacity or availability constraints. Represent independently verifiable deliverables (such as each host migration) separately in the plan; do not hide them in one serial rollout step or create dependencies merely from list order.",
6
- "Start all ready, authorized, non-conflicting units concurrently within known resource limits before waiting for any one to finish. Reassess ready work after launches and results; continue unblocked foreground work instead of polling running tasks. Do not equate one background launch with scheduling all ready work.",
7
- "Serialize only for a concrete dependency, conflicting writes or ownership, resource or concurrency limit, documented rollout gate, or explicit availability requirement. Record the reason in the plan or explain it to the user. Do not invent a canary or host-by-host availability requirement.",
8
- "If concurrency raises an unresolved material operational trade-off (for example, draining all hosts could leave no available runners), ask the user once before the affected operations. Continue independent preparation meanwhile. Existing authorization, safety checks, workflow prerequisites, and human-approved rollout constraints remain binding; urgency does not waive them.",
9
- "Choose the execution mechanism after deciding what can run together: foreground tools for small, tightly coupled, or interactive work; subagents for bounded independent reasoning or implementation when the active delegation mode permits; background tasks for independent long-running commands or waits. Manual delegation forbids proactive subagents, not concurrent foreground tools or background commands. Use available batch or parallel tool interfaces where appropriate and inspect every launch receipt for failures or skipped work.",
10
- "A unit waiting or failing does not block other ready independent units unless the evidence shows a shared blocker. Preserve each unit's guards and rollback boundary. Before completing verification, the plan, or the goal, inspect and integrate every relevant terminal result or failure; launch is not completion.",
11
- ].join("\n");
12
-
13
- export function appendExecutionPolicy(systemPrompt: string): string {
14
- // Inspect the prompt, not module-global state: vendored copies have separate
15
- // module identities, and every fresh turn or reload needs the policy again.
16
- return systemPrompt.includes(EXECUTION_POLICY)
17
- ? systemPrompt
18
- : `${systemPrompt}\n\n${EXECUTION_POLICY}`;
19
- }