pi-better-harness 0.3.6 → 0.3.8

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,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-background-tasks",
3
- "version": "0.2.11",
3
+ "version": "0.2.12",
4
4
  "description": "Pi extension for durable background shell tasks, watchers, logs, and status inspection.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -103,6 +103,12 @@ const StatusActionParams = Type.Object({
103
103
  verbose: Type.Optional(Type.Boolean()),
104
104
  });
105
105
 
106
+ const BACKGROUND_ORCHESTRATION_GUIDELINES = [
107
+ "Use background tasks for genuinely long-running processes or repeated checks. Run short commands in the foreground.",
108
+ "When a structured plan is active, keep it as the coordinator ledger: launch relevant background work early, continue unblocked foreground work without polling, and update the plan after inspecting each terminal result or failure.",
109
+ "Do not treat launch as completion of the parent milestone; relevant background work must be terminal, inspected, and integrated before verification or completion.",
110
+ ];
111
+
106
112
  export function registerTools(pi: ExtensionAPI): void {
107
113
  let activeSession: BackgroundTaskCallbackOrigin | undefined;
108
114
  const getActiveSession = () => activeSession;
@@ -125,6 +131,7 @@ export function registerTools(pi: ExtensionAPI): void {
125
131
  name: "bg_task_spawn",
126
132
  label: "BG Spawn",
127
133
  description: "Start a long-running background process and return immediately with its task id. For remote work, prefer structured ssh: pass ssh:{host,user} and put the remote command in command; spawn defaults to a remote tmux session with durable local logs and real remote stop. For short synchronous remote commands that should return output now, use remote_bash from pi-better-ssh. If tmux is missing, the preset attempts to install tmux non-interactively and fails closed with operator guidance when setup cannot proceed. Explicit remote.session=direct skips tmux, but direct mode has weaker stop semantics and may leave the remote process running. Never wait or poll in the foreground.",
134
+ promptGuidelines: BACKGROUND_ORCHESTRATION_GUIDELINES,
128
135
  parameters: SpawnParams,
129
136
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
130
137
  activeSession = getCallbackOrigin(ctx);
@@ -138,6 +145,7 @@ export function registerTools(pi: ExtensionAPI): void {
138
145
  name: "bg_task_watch",
139
146
  label: "BG Watch",
140
147
  description: "Poll a command in the background until success_when, failure_when, or timeout matches. For remote work, prefer structured ssh: pass ssh:{host,user} and provide the remote command in command; each interval opens a direct one-shot SSH poll without tmux installation. For short synchronous remote commands that should return output now, use remote_bash from pi-better-ssh. Returns immediately with its task id. Default timeout 900 seconds; pass timeout_seconds:0 to disable.",
148
+ promptGuidelines: BACKGROUND_ORCHESTRATION_GUIDELINES,
141
149
  parameters: WatchParams,
142
150
  async execute(_toolCallId, params, _signal, _onUpdate, ctx) {
143
151
  activeSession = getCallbackOrigin(ctx);
@@ -198,6 +206,7 @@ export function registerTools(pi: ExtensionAPI): void {
198
206
  name: "bg_task",
199
207
  label: "BG Task",
200
208
  description: "Action wrapper for background tasks: spawn, watch, list, status, log, stop, or clear. For remote work, prefer structured ssh: pass ssh:{host,user} and provide the remote command in command. For short synchronous remote commands that should return output now, use remote_bash from pi-better-ssh. SSH spawn defaults to durable tmux; SSH watches use direct one-shot polls without tmux installation; remote.session=direct is a weaker-stop spawn escape hatch. Spawn/watch return immediately; do not poll in foreground. For action:status, default compact output and use verbose:true only for full metadata. For action:log, default compact tail and use tail_lines:0 only for explicit full logs.",
209
+ promptGuidelines: BACKGROUND_ORCHESTRATION_GUIDELINES,
201
210
  parameters: ActionParams,
202
211
  renderResult(result: unknown, options: unknown, theme: unknown) {
203
212
  return renderBackgroundTaskLogDisplay(result, options, theme);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-goal",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Pi extension for goal tracking with background-aware continuation.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -248,6 +248,15 @@ export default function (pi: ExtensionAPI): void {
248
248
  refreshGoalWidget?.(wasVisible);
249
249
  };
250
250
 
251
+ const pauseGoalOnInterrupt = (ctx: ExtensionContext): void => {
252
+ const goal = getGoal(ctx);
253
+ if (!isPokeable(goal)) {
254
+ return;
255
+ }
256
+ setGoal(goalWithStatus(goal, "paused"), ctx, "runtime");
257
+ notifyGoal(ctx, "Goal paused (interrupted).");
258
+ };
259
+
251
260
  const queueGoalContinuation = (goal: GoalSnapshot): void => {
252
261
  if (!isPokeable(goal) || continuationQueuedFor === goal.goalId) {
253
262
  return;
@@ -713,7 +722,7 @@ export default function (pi: ExtensionAPI): void {
713
722
  }
714
723
 
715
724
  const backgroundInstruction = snapshot.backgroundRunning
716
- ? ` The goal still has delegated background work running (${summarizeActiveBackground(snapshot)}). Foreground idleness alone is not goal completion; do not mark the goal complete until delegated background work reaches a terminal state and its result or failure has been inspected.`
725
+ ? ` The goal still has delegated background work running (${summarizeActiveBackground(snapshot)}). Foreground idleness alone is not goal completion; keep any structured plan current and do not mark verification, the plan, or the goal complete until every relevant delegated task reaches a terminal state and its result or failure has been inspected and integrated.`
717
726
  : "";
718
727
  return {
719
728
  systemPrompt:
@@ -728,6 +737,14 @@ export default function (pi: ExtensionAPI): void {
728
737
  clearIdleContinuation();
729
738
  continuationQueuedFor = null;
730
739
  lastAgentEvidence = null;
740
+ const turnSignal = ctx.signal;
741
+ if (turnSignal) {
742
+ if (turnSignal.aborted) {
743
+ pauseGoalOnInterrupt(ctx);
744
+ } else {
745
+ turnSignal.addEventListener("abort", () => pauseGoalOnInterrupt(ctx), { once: true });
746
+ }
747
+ }
731
748
  await publishSnapshot(ctx);
732
749
  });
733
750
 
@@ -738,13 +755,7 @@ export default function (pi: ExtensionAPI): void {
738
755
  // aborted (escape / ctrl+c while streaming), pause the active goal so it does
739
756
  // not auto-continue after the user stopped the agent.
740
757
  if (wasTurnAborted(event.messages)) {
741
- const goal = getGoal(ctx);
742
- if (isPokeable(goal)) {
743
- setGoal(goalWithStatus(goal, "paused"), ctx, "runtime");
744
- if (ctx.hasUI) {
745
- ctx.ui.notify("Goal paused (interrupted).", "info");
746
- }
747
- }
758
+ pauseGoalOnInterrupt(ctx);
748
759
  }
749
760
  });
750
761
 
@@ -12,6 +12,12 @@
12
12
 
13
13
  Plan progress is checklist progress, not an estimate of effort. The extension never infers completion from prose or successful tool calls.
14
14
 
15
+ ## Coordinating Delegated Work
16
+
17
+ Use the plan as the foreground coordinator's milestone ledger. Delegate independent, sufficiently substantial work early with subagents, and use background tasks for long-running processes or repeated checks. Keep doing unblocked foreground work after launch; do not poll workers.
18
+
19
+ Concurrent workers belong under one `in_progress` coordinator step rather than one active plan step per worker. Worker tools and the background-work navigator own individual run status. Complete verification and the plan only after every relevant delegated task is terminal and its result or failure has been inspected and integrated.
20
+
15
21
  ## Install
16
22
 
17
23
  ```sh
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-plan",
3
- "version": "0.1.4",
3
+ "version": "0.1.5",
4
4
  "description": "Structured execution plans with persistent progress for Pi.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -152,6 +152,8 @@ export default function planExtension(pi: ExtensionAPI): void {
152
152
  promptGuidelines: [
153
153
  "Use update_plan for work with three or more meaningful steps, and update it immediately when a step completes, becomes blocked, or scope changes.",
154
154
  "Keep at most one update_plan step in_progress and do not mark a step completed until its required verification succeeds.",
155
+ "Use the plan as the foreground coordinator's milestone ledger. Delegate independent, sufficiently substantial work early when subagent or background-task tools are available, and continue unblocked foreground work instead of waiting or polling.",
156
+ "Represent concurrent delegated work under one in_progress coordinator step. Before completing verification or the plan, ensure every relevant delegated task is terminal, then inspect and integrate its result or failure.",
155
157
  ],
156
158
  parameters: UpdatePlanSchema,
157
159
  async execute(_toolCallId, params) {
@@ -270,6 +272,8 @@ function planPrompt(plan: PlanSnapshot): string {
270
272
  return [
271
273
  "Current structured execution plan:",
272
274
  ...plan.steps.map((item, index) => `${index + 1}. [${item.status}] ${item.step}`),
273
- "Use update_plan immediately when a step completes, becomes blocked, or the scope changes. Completion must be explicit and evidence-backed.",
275
+ "Use update_plan immediately when a step completes, becomes blocked, or the scope changes.",
276
+ "Treat the plan as the foreground coordinator's milestone ledger: delegate independent, sufficiently substantial work early, keep one coordinator step in_progress across concurrent workers, and continue unblocked foreground work instead of waiting or polling.",
277
+ "Do not complete verification or the plan until relevant delegated work is terminal, its results or failures have been inspected and integrated, and the outcome is evidence-backed.",
274
278
  ].join("\n");
275
279
  }
@@ -127,6 +127,11 @@ const SUBAGENT_TOOLS = [
127
127
  "subagent_result",
128
128
  ];
129
129
 
130
+ const SUBAGENT_ORCHESTRATION_GUIDELINES = [
131
+ "When a structured plan is active, keep it as the parent-owned coordinator ledger: delegate bounded independent work, continue unblocked foreground work, and update the plan after integrating each result or failure.",
132
+ "Do not treat launching a subagent as completion of the parent milestone; relevant terminal results must be inspected and integrated before verification or completion.",
133
+ ];
134
+
130
135
  const GOAL_READY_EVENT = "pi-better-goal:ready";
131
136
  const GOAL_REGISTER_PROVIDER_EVENT = "pi-better-goal:register-provider";
132
137
  const goalReadySubscriptions = new WeakSet<ExtensionAPI>();
@@ -1267,7 +1272,8 @@ export default function (pi: ExtensionAPI) {
1267
1272
  promptGuidelines: [
1268
1273
  "Use subagent_spawn for independent work the user should not have to wait on. It returns at once with a run id; that return IS the deliverable — report the id to the user and continue.",
1269
1274
  "After subagent_spawn, do NOT call subagent_output or subagent_result in a loop to wait for the result, and do NOT sleep. The run completes on its own and reports back on the next turn.",
1270
- "Only call subagent_result / subagent_output when the user explicitly asks how a run is going or for its result.",
1275
+ "Call subagent_result after a completion or attention callback, or when the user explicitly asks for the result. Use subagent_output only when the user explicitly asks how a run is progressing; never use either tool to poll.",
1276
+ ...SUBAGENT_ORCHESTRATION_GUIDELINES,
1271
1277
  "The tools param is both the tool allowlist AND what determines which extensions load in the child (e.g. tools='read,bash,web_fetch' loads only the web-tools package). Ask for the tools the task needs and nothing more; clean:true gives a built-ins-only child. Pick a model with the model param (e.g. 'xai/grok-4.5@high'); providerless model patterns are resolved by Pi, while provider/model is deterministic and loads mapped provider extensions.",
1272
1278
  "By default the subagent is sandboxed (writes confined to its working dir, reads and network open) and triggers completion here on finish. Set callback:false to finish quietly — then read the result on demand via subagent_result.",
1273
1279
  "Use git_clone_workspace:true when the subagent will mutate Git in a sandbox. The parent prepares a disposable, self-contained clone with a real .git/ directory inside the sandbox root, so linked-worktree metadata outside the sandbox cannot stall the child.",
@@ -1336,6 +1342,7 @@ export default function (pi: ExtensionAPI) {
1336
1342
  "Use subagent_spawn_batch when you have several independent tasks to delegate. It returns immediately with a batch id and one run id per launched job.",
1337
1343
  "Each job is a normal subagent run; use subagent_result / subagent_output / subagent_stop with the individual run ids just like subagent_spawn.",
1338
1344
  "Do NOT poll for results. Each job reports back on its own when it finishes.",
1345
+ ...SUBAGENT_ORCHESTRATION_GUIDELINES,
1339
1346
  "By default the whole batch is rejected if there is not enough capacity. Set onCapacity to 'launch-available' to launch as many as fit and report the rest as skipped.",
1340
1347
  ],
1341
1348
  parameters: Type.Object({
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-subagents",
3
- "version": "0.1.26",
3
+ "version": "0.1.27",
4
4
  "description": "Pi extension for detached, sandboxed subagent runs that keep the foreground session free.",
5
5
  "license": "MIT",
6
6
  "type": "module",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-better-harness",
3
- "version": "0.3.6",
3
+ "version": "0.3.8",
4
4
  "description": "Pi extension bundle for a write sandbox, subagents, background tasks, SSH, goals, and structured plans.",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -50,12 +50,12 @@
50
50
  "test": "node --test test/*.test.mjs"
51
51
  },
52
52
  "dependencies": {
53
- "pi-better-background-tasks": "0.2.11",
54
- "pi-better-goal": "0.2.1",
55
- "pi-better-plan": "0.1.4",
53
+ "pi-better-background-tasks": "0.2.12",
54
+ "pi-better-goal": "0.2.3",
55
+ "pi-better-plan": "0.1.5",
56
56
  "pi-better-sandbox": "0.3.0",
57
57
  "pi-better-ssh": "0.1.1",
58
- "pi-better-subagents": "0.1.26"
58
+ "pi-better-subagents": "0.1.27"
59
59
  },
60
60
  "bundledDependencies": [
61
61
  "pi-better-background-tasks",