pi-better-harness 0.1.18 → 0.1.19
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.
|
@@ -164,7 +164,7 @@ export default function (pi: ExtensionAPI): void {
|
|
|
164
164
|
let continuationQueuedFor: string | null = null;
|
|
165
165
|
let idleContinuationTimer: ReturnType<typeof setTimeout> | undefined;
|
|
166
166
|
let idleContinuationSignature = "";
|
|
167
|
-
let refreshGoalWidget: (() => void) | undefined;
|
|
167
|
+
let refreshGoalWidget: ((force?: boolean) => void) | undefined;
|
|
168
168
|
let lastAgentEvidence: ContinuationEvidence | null = null;
|
|
169
169
|
|
|
170
170
|
const getGoal = (ctx: ExtensionContext): GoalSnapshot | null => currentGoalSnapshot(ctx);
|
|
@@ -180,21 +180,53 @@ export default function (pi: ExtensionAPI): void {
|
|
|
180
180
|
appendContinuationState(createContinuationState(goal.goalId));
|
|
181
181
|
};
|
|
182
182
|
|
|
183
|
+
const isForegroundBusy = (ctx: ExtensionContext): boolean =>
|
|
184
|
+
foregroundRunning || !ctx.isIdle();
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Chat `notify` appends lines above the Working/bash status region. While the
|
|
188
|
+
* agent is streaming, that height change desyncs Pi main-screen differential
|
|
189
|
+
* rendering and stacks "Working..." / "Elapsed Xs" into scrollback. Keep
|
|
190
|
+
* mid-stream feedback out of the transcript; use the footer status instead.
|
|
191
|
+
*/
|
|
192
|
+
const notifyGoal = (
|
|
193
|
+
ctx: ExtensionContext,
|
|
194
|
+
message: string,
|
|
195
|
+
type: "info" | "warning" | "error" = "info",
|
|
196
|
+
): void => {
|
|
197
|
+
if (!ctx.hasUI) {
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
if (isForegroundBusy(ctx) && type !== "error") {
|
|
201
|
+
try {
|
|
202
|
+
ctx.ui.setStatus(EXTENSION_NAME, message);
|
|
203
|
+
} catch {
|
|
204
|
+
// Footer status is best-effort only.
|
|
205
|
+
}
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
ctx.ui.notify(message, type);
|
|
209
|
+
};
|
|
210
|
+
|
|
183
211
|
const setGoal = (goal: GoalSnapshot, ctx: ExtensionContext, source: GoalEntrySource): void => {
|
|
212
|
+
const previous = getGoal(ctx);
|
|
213
|
+
const wasVisible = previous !== null && isGoalClockVisible(previous);
|
|
184
214
|
pi.appendEntry(EXTENSION_NAME, goalSetEntry(goal, source));
|
|
185
215
|
continuationQueuedFor = null;
|
|
186
216
|
backgroundDrainTracker = null;
|
|
187
217
|
clearIdleContinuation();
|
|
188
|
-
|
|
218
|
+
// Force a full redraw when the dock height changes (absent ↔ visible clock).
|
|
219
|
+
refreshGoalWidget?.(!wasVisible || !isGoalClockVisible(goal));
|
|
189
220
|
};
|
|
190
221
|
|
|
191
222
|
const clearGoal = (ctx: ExtensionContext, source: GoalEntrySource): void => {
|
|
192
223
|
const current = getGoal(ctx);
|
|
224
|
+
const wasVisible = current !== null && isGoalClockVisible(current);
|
|
193
225
|
pi.appendEntry(EXTENSION_NAME, goalClearEntry(current?.goalId ?? null, source));
|
|
194
226
|
continuationQueuedFor = null;
|
|
195
227
|
backgroundDrainTracker = null;
|
|
196
228
|
clearIdleContinuation();
|
|
197
|
-
refreshGoalWidget?.();
|
|
229
|
+
refreshGoalWidget?.(wasVisible);
|
|
198
230
|
};
|
|
199
231
|
|
|
200
232
|
const queueGoalContinuation = (goal: GoalSnapshot): void => {
|
|
@@ -395,8 +427,24 @@ export default function (pi: ExtensionAPI): void {
|
|
|
395
427
|
ctx.ui.setWidget(
|
|
396
428
|
EXTENSION_NAME,
|
|
397
429
|
(tui, theme) => {
|
|
398
|
-
const
|
|
399
|
-
|
|
430
|
+
const requestRender = (force = false): void => {
|
|
431
|
+
try {
|
|
432
|
+
tui.requestRender(force);
|
|
433
|
+
} catch {
|
|
434
|
+
// UI paint is best-effort only.
|
|
435
|
+
}
|
|
436
|
+
};
|
|
437
|
+
const scheduler = createRenderScheduler(() => requestRender(false));
|
|
438
|
+
const refresh = (force = false): void => {
|
|
439
|
+
if (force) {
|
|
440
|
+
scheduler.cancel();
|
|
441
|
+
// Height transitions must reset main-screen differential state so
|
|
442
|
+
// Working/Elapsed lines keep rewriting in place instead of stacking.
|
|
443
|
+
requestRender(true);
|
|
444
|
+
return;
|
|
445
|
+
}
|
|
446
|
+
scheduler.request();
|
|
447
|
+
};
|
|
400
448
|
refreshGoalWidget = refresh;
|
|
401
449
|
|
|
402
450
|
return {
|
|
@@ -444,49 +492,53 @@ export default function (pi: ExtensionAPI): void {
|
|
|
444
492
|
|
|
445
493
|
if (!trimmed) {
|
|
446
494
|
const continuation = current ? currentContinuationState(ctx, current.goalId) : null;
|
|
495
|
+
// Inspection is always explicit user intent; show the full summary even while busy.
|
|
447
496
|
ctx.ui.notify(formatGoal(current, continuation, observeGoalStall(current, continuation, { foregroundRunning })), "info");
|
|
448
497
|
return;
|
|
449
498
|
}
|
|
450
499
|
|
|
451
500
|
if (trimmed === "pause") {
|
|
452
501
|
if (!current || current.status !== "active") {
|
|
453
|
-
ctx
|
|
502
|
+
notifyGoal(ctx, "Only active goals can be paused.", "warning");
|
|
454
503
|
return;
|
|
455
504
|
}
|
|
456
505
|
setGoal(goalWithStatus(current, "paused"), ctx, "command");
|
|
457
|
-
ctx
|
|
506
|
+
notifyGoal(ctx, "Goal paused.");
|
|
458
507
|
return;
|
|
459
508
|
}
|
|
460
509
|
|
|
461
510
|
if (trimmed === "resume") {
|
|
462
511
|
if (!current || current.status !== "paused") {
|
|
463
|
-
ctx
|
|
512
|
+
notifyGoal(ctx, "Only paused goals can be resumed.", "warning");
|
|
464
513
|
return;
|
|
465
514
|
}
|
|
466
515
|
const goal = goalWithStatus(current, "active");
|
|
467
516
|
setGoal(goal, ctx, "command");
|
|
468
517
|
queueGoalContinuation(goal);
|
|
469
|
-
ctx
|
|
518
|
+
notifyGoal(ctx, "Goal resumed.");
|
|
470
519
|
return;
|
|
471
520
|
}
|
|
472
521
|
|
|
473
522
|
if (trimmed === "clear") {
|
|
474
523
|
clearGoal(ctx, "command");
|
|
475
|
-
ctx
|
|
524
|
+
notifyGoal(ctx, "Goal cleared.");
|
|
476
525
|
return;
|
|
477
526
|
}
|
|
478
527
|
|
|
479
528
|
if (trimmed === "complete") {
|
|
480
529
|
if (!current) {
|
|
481
|
-
ctx
|
|
530
|
+
notifyGoal(ctx, "No goal is set.", "warning");
|
|
482
531
|
return;
|
|
483
532
|
}
|
|
484
533
|
setGoal(goalWithStatus(current, "complete"), ctx, "command");
|
|
485
|
-
ctx
|
|
534
|
+
notifyGoal(ctx, "Goal marked complete.");
|
|
486
535
|
return;
|
|
487
536
|
}
|
|
488
537
|
|
|
489
|
-
|
|
538
|
+
// Confirm dialogs swap the editor and reflow the dock. While the agent is
|
|
539
|
+
// streaming that desyncs main-screen differential paints, so replace
|
|
540
|
+
// silently mid-turn and only confirm when idle.
|
|
541
|
+
if (current && current.status !== "complete" && ctx.hasUI && !isForegroundBusy(ctx)) {
|
|
490
542
|
const replace = await ctx.ui.confirm(
|
|
491
543
|
"Replace active goal?",
|
|
492
544
|
`Current goal: ${current.objective}`,
|
|
@@ -498,9 +550,9 @@ export default function (pi: ExtensionAPI): void {
|
|
|
498
550
|
|
|
499
551
|
try {
|
|
500
552
|
const goal = startOrReplaceGoal(trimmed, null, ctx, "command");
|
|
501
|
-
ctx
|
|
553
|
+
notifyGoal(ctx, `Goal set: ${goal.objective}`);
|
|
502
554
|
} catch (error) {
|
|
503
|
-
ctx
|
|
555
|
+
notifyGoal(ctx, error instanceof Error ? error.message : String(error), "error");
|
|
504
556
|
}
|
|
505
557
|
},
|
|
506
558
|
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-better-harness",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.19",
|
|
4
4
|
"description": "Pi extension bundle for subagents, durable background tasks, and goal tracking.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"dependencies": {
|
|
46
46
|
"pi-better-background-tasks": "0.2.0",
|
|
47
|
-
"pi-better-goal": "0.1.
|
|
47
|
+
"pi-better-goal": "0.1.21",
|
|
48
48
|
"pi-better-subagents": "0.1.19"
|
|
49
49
|
},
|
|
50
50
|
"bundledDependencies": [
|