pi-better-harness 0.44.0 → 0.45.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/CHANGELOG.md +6 -0
- package/node_modules/pi-better-subagents/docs/usage.md +14 -0
- package/node_modules/pi-better-subagents/historical-attention.ts +35 -0
- package/node_modules/pi-better-subagents/index.ts +73 -35
- package/node_modules/pi-better-subagents/package.json +1 -1
- package/node_modules/pi-better-subagents/worker-waits.ts +14 -13
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [0.45.0] - 2026-11-12
|
|
4
|
+
|
|
5
|
+
### Changed
|
|
6
|
+
|
|
7
|
+
- Bundle Subagents 0.32.0 for session-scoped historical wait warnings, original-session inspection, durable cancellation, and dismissal of unchanged notices. (#719)
|
|
8
|
+
|
|
3
9
|
## [0.44.0] - 2026-11-12
|
|
4
10
|
|
|
5
11
|
### Changed
|
|
@@ -1085,3 +1085,17 @@ Near-term:
|
|
|
1085
1085
|
twice ([#2](https://github.com/1aboveio/pi-better-subagents/issues/2)) — **done**.
|
|
1086
1086
|
- Named agent-definition files (per-agent system prompt + tool allowlist) and
|
|
1087
1087
|
chain/parallel orchestration.
|
|
1088
|
+
# Historical Waits
|
|
1089
|
+
|
|
1090
|
+
`/subagents history` lists retained waits from other sessions in this workspace.
|
|
1091
|
+
Each entry identifies its originating session and offers inspection or explicit
|
|
1092
|
+
cancellation. Inspection does not adopt the assignment, acknowledge its evidence,
|
|
1093
|
+
or start a continuation. Cancellation confirms the durable result; it does not
|
|
1094
|
+
claim that an uncertain writer has stopped. Failures leave the wait available
|
|
1095
|
+
for inspection and retry.
|
|
1096
|
+
|
|
1097
|
+
`/subagents history dismiss` durably dismisses the current historical-attention
|
|
1098
|
+
notice without changing any wait or evidence. A changed wait generation, phase,
|
|
1099
|
+
publication or attention reason can produce a new notice. Ordinary task navigation
|
|
1100
|
+
remains scoped to the active session.
|
|
1101
|
+
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { createHash, randomUUID } from "node:crypto";
|
|
2
|
+
import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
4
|
+
import { type RunCallbackOrigin } from "./registry.ts";
|
|
5
|
+
import { durableSubagentRoot } from "./session-store.ts";
|
|
6
|
+
import { parkedWaits, type ParkedWait } from "./worker-waits.ts";
|
|
7
|
+
|
|
8
|
+
/** Discovery is not adoption: the captured owner and wait evidence stay untouched. */
|
|
9
|
+
export function historicalWaits(origin: RunCallbackOrigin | undefined): ParkedWait[] {
|
|
10
|
+
if (!origin) return [];
|
|
11
|
+
return parkedWaits().filter(({ record }) => record.ownerWorkspace === origin.cwd
|
|
12
|
+
&& record.ownerSessionId !== origin.sessionId);
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export function attentionRevision(waits: ParkedWait[]): string {
|
|
16
|
+
return createHash("sha256").update(JSON.stringify(waits.map(({ runId, record }) => [
|
|
17
|
+
runId, record.waitId, record.generation, record.phase, record.heldReason,
|
|
18
|
+
record.cancelReason, record.resumedBy, record.resumeLaunchToken,
|
|
19
|
+
]).sort((a, b) => String(a[0]).localeCompare(String(b[0]))))).digest("hex");
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function receiptPath(workspace: string): string {
|
|
23
|
+
return join(durableSubagentRoot(), `historical-attention-${createHash("sha256").update(workspace).digest("hex")}.json`);
|
|
24
|
+
}
|
|
25
|
+
export function attentionDismissed(workspace: string, revision: string): boolean {
|
|
26
|
+
try { return JSON.parse(readFileSync(receiptPath(workspace), "utf8")).revision === revision; }
|
|
27
|
+
catch { return false; }
|
|
28
|
+
}
|
|
29
|
+
export function dismissAttention(workspace: string, revision: string): void {
|
|
30
|
+
mkdirSync(durableSubagentRoot(), { recursive: true });
|
|
31
|
+
const path = receiptPath(workspace);
|
|
32
|
+
const temp = `${path}.${randomUUID()}.tmp`;
|
|
33
|
+
writeFileSync(temp, JSON.stringify({ version: 1, revision }), { mode: 0o600 });
|
|
34
|
+
renameSync(temp, path);
|
|
35
|
+
}
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { assignmentFor } from "./assignment-projection.mjs";
|
|
15
|
+
import { historicalWaits, attentionRevision, attentionDismissed, dismissAttention } from "./historical-attention.ts";
|
|
15
16
|
import { registerAssignmentAttempt, commitAssignmentTerminal, readOutcome, receiptOutcome, outcomeTranscriptReceipt, inspectOutcome, createOutcomeHost, closeOutcomeHost, claimOutcome, type AssignmentBinding } from "./assignment-outcomes.ts";
|
|
16
17
|
import { currentSandboxPermissions } from "./permission-policy.ts";
|
|
17
18
|
import { requestResumeAdmission } from "./worker-waits.ts";
|
|
@@ -238,6 +239,8 @@ import { defaultUserRoot, type CatalogSnapshot } from "./catalog-store.ts";
|
|
|
238
239
|
import { restrictContextFilesToWorktree } from "./shared-worktree-context.ts";
|
|
239
240
|
|
|
240
241
|
const SUBAGENT_MODE_ACTIONS: readonly AutocompleteItem[] = [
|
|
242
|
+
{ value: "history", label: "history", description: "Inspect or cancel waits from other sessions" },
|
|
243
|
+
{ value: "history dismiss", label: "history dismiss", description: "Dismiss unchanged historical attention notices" },
|
|
241
244
|
{ value: "settings", label: "settings", description: "Open delegation, concurrency, and timing settings" },
|
|
242
245
|
{ value: "cap", label: "cap <number>", description: "Set a positive concurrent subagent limit for this session" },
|
|
243
246
|
{ value: "tool-wait", label: "tool-wait <minutes>", description: "Set the session tool-wait alert time; 0 turns it off" },
|
|
@@ -1438,20 +1441,22 @@ function navigatorDetail(id: string, now: number = Date.now()) {
|
|
|
1438
1441
|
* Set by the active extension instance: cancel a parked assignment's pending
|
|
1439
1442
|
* wait. Returns false when the run is not parked, so the ordinary stop path runs.
|
|
1440
1443
|
*/
|
|
1441
|
-
let cancelParkedForUi: ((id: string, reason: string) => boolean) | undefined;
|
|
1444
|
+
let cancelParkedForUi: ((id: string, reason: string) => Promise<boolean>) | undefined;
|
|
1442
1445
|
|
|
1443
1446
|
/** Shared #44 stop+dismiss path used by navigator Close (#47). */
|
|
1444
1447
|
function navigatorCloseRun(id: string) {
|
|
1445
1448
|
// A parked attempt's child already exited cleanly, so "close" must cancel the
|
|
1446
1449
|
// pending automatic resume instead of pretending to stop a live process.
|
|
1447
|
-
if (
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1450
|
+
if (parkedWaitForRun(id) && cancelParkedForUi) {
|
|
1451
|
+
void cancelParkedForUi(id, "closed from the navigator; the automatic resume is cancelled")
|
|
1452
|
+
.then((cancelled) => {
|
|
1453
|
+
if (!cancelled) throw new Error("The pending wait changed; inspect it again.");
|
|
1454
|
+
dismissRun(id);
|
|
1455
|
+
uiCtx?.ui.notify(`Cancelled the pending wait of ${id}. Writer termination may remain unproven.`, "info");
|
|
1456
|
+
})
|
|
1457
|
+
.catch((error) => uiCtx?.ui.notify(`Cancellation failed for ${id}: ${String(error)}. The wait remains available for inspection and retry.`, "warning"));
|
|
1458
|
+
// The synchronous navigator must retain the row until the durable operation settles.
|
|
1459
|
+
return { action: "missing", id };
|
|
1455
1460
|
}
|
|
1456
1461
|
const outcome = executeNavigatorClose(id, {
|
|
1457
1462
|
readMeta,
|
|
@@ -2300,18 +2305,21 @@ export default function (pi: ExtensionAPI) {
|
|
|
2300
2305
|
childExtensionPath: childBridgeExtensionPath(selfDir()),
|
|
2301
2306
|
});
|
|
2302
2307
|
let resumeOrchestration: ResumeOrchestration | undefined;
|
|
2308
|
+
const notifyOwnedWait = (message: string, level: "warning" | "info" = "warning") => {
|
|
2309
|
+
try { uiCtx?.ui?.notify?.(message, level); } catch { /* no UI */ }
|
|
2310
|
+
};
|
|
2303
2311
|
/**
|
|
2304
2312
|
* Stop a continuation attempt only when this parent owns it and the token
|
|
2305
2313
|
* published with it matches. A pid is never signalled on a guess.
|
|
2306
2314
|
*/
|
|
2307
2315
|
const stopPublishedAttempt = createOwnedAttemptStopper();
|
|
2308
|
-
cancelParkedForUi = (id, reason) => {
|
|
2316
|
+
cancelParkedForUi = async (id, reason) => {
|
|
2309
2317
|
const parked = parkedWaitForRun(id);
|
|
2310
2318
|
if (!parked) return false;
|
|
2311
|
-
|
|
2312
|
-
.
|
|
2313
|
-
.
|
|
2314
|
-
|
|
2319
|
+
try {
|
|
2320
|
+
await cancelParkedWait(workerOperations.service, parked, reason, { stopAttempt: stopPublishedAttempt });
|
|
2321
|
+
if (parked.meta.assignment) enqueueCompletionCallback(pi, id);
|
|
2322
|
+
} finally { syncHealthTicker(); }
|
|
2315
2323
|
return true;
|
|
2316
2324
|
};
|
|
2317
2325
|
const effectiveSettings = () => {
|
|
@@ -3482,9 +3490,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
3482
3490
|
capacity: resumeCapacity(),
|
|
3483
3491
|
launch: launchParkedResume,
|
|
3484
3492
|
stopAttempt: stopPublishedAttempt,
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
},
|
|
3493
|
+
notifyWhen: (meta) => Boolean(activeCallbackOrigin && belongsToOrigin(meta, activeCallbackOrigin)),
|
|
3494
|
+
notify: notifyOwnedWait,
|
|
3488
3495
|
});
|
|
3489
3496
|
return resumeOrchestration;
|
|
3490
3497
|
}
|
|
@@ -3509,10 +3516,13 @@ export default function (pi: ExtensionAPI) {
|
|
|
3509
3516
|
try {
|
|
3510
3517
|
await reconcileParkedWaits(service, {
|
|
3511
3518
|
resume: (park) => orchestration.resume(park),
|
|
3512
|
-
|
|
3513
|
-
|
|
3514
|
-
},
|
|
3519
|
+
notifyWhen: (meta) => Boolean(activeCallbackOrigin && belongsToOrigin(meta, activeCallbackOrigin)),
|
|
3520
|
+
notify: notifyOwnedWait,
|
|
3515
3521
|
});
|
|
3522
|
+
const historical = historicalWaits(activeCallbackOrigin);
|
|
3523
|
+
if (historical.length && activeCallbackOrigin && !attentionDismissed(activeCallbackOrigin.cwd, attentionRevision(historical))) {
|
|
3524
|
+
uiCtx?.ui?.notify?.(`${historical.length} historical subagent waits remain. Inspect or cancel with /subagents history; dismiss this notice with /subagents history dismiss.`, "warning");
|
|
3525
|
+
}
|
|
3516
3526
|
} catch { /* reconciliation retries on the next session start */ }
|
|
3517
3527
|
}
|
|
3518
3528
|
|
|
@@ -3990,23 +4000,25 @@ export default function (pi: ExtensionAPI) {
|
|
|
3990
4000
|
enqueueCompletionCallback(pi, id);
|
|
3991
4001
|
} });
|
|
3992
4002
|
acceptanceStopToolRef = acceptanceStopTool;
|
|
3993
|
-
// A parked
|
|
3994
|
-
//
|
|
3995
|
-
// be continued manually. Without this, stop would report "not running" and
|
|
3996
|
-
// leave the automatic resume armed for the same assignment.
|
|
4003
|
+
// A parked attempt may have exited, but its automatic resume is still armed.
|
|
4004
|
+
// Explicit stop must confirm durable cancellation, not just stop a process.
|
|
3997
4005
|
const acceptanceStopToolWithParked = {
|
|
3998
4006
|
...acceptanceStopTool,
|
|
3999
4007
|
async execute(toolCallId: string, params: unknown, signal?: AbortSignal, onUpdate?: unknown, ctx?: unknown) {
|
|
4000
4008
|
const target = (params as { id?: unknown } | undefined)?.id;
|
|
4001
|
-
|
|
4002
|
-
|
|
4003
|
-
|
|
4004
|
-
|
|
4005
|
-
|
|
4006
|
-
|
|
4007
|
-
|
|
4008
|
-
|
|
4009
|
-
|
|
4009
|
+
try {
|
|
4010
|
+
if (typeof target === "string" && await cancelParkedForUi?.(target, "cancelled by subagent_stop; the automatic resume is cancelled")) {
|
|
4011
|
+
renderWidget();
|
|
4012
|
+
syncHealthTicker();
|
|
4013
|
+
return {
|
|
4014
|
+
content: [{
|
|
4015
|
+
type: "text",
|
|
4016
|
+
text: `Durably cancelled the pending wait of parked subagent ${target}. Writer termination may remain unproven; ordinary continuation admission still applies.`,
|
|
4017
|
+
}],
|
|
4018
|
+
};
|
|
4019
|
+
}
|
|
4020
|
+
} catch (error) {
|
|
4021
|
+
return { isError: true, content: [{ type: "text", text: `Cancellation failed for ${target}: ${String(error)}. The retained wait remains available for inspection and retry.` }] };
|
|
4010
4022
|
}
|
|
4011
4023
|
return (acceptanceStopTool.execute as (
|
|
4012
4024
|
id: string, p: unknown, s?: AbortSignal, u?: unknown, c?: unknown,
|
|
@@ -4100,10 +4112,36 @@ export default function (pi: ExtensionAPI) {
|
|
|
4100
4112
|
if (typeof pi.registerCommand === "function") {
|
|
4101
4113
|
agentOperations.registerCommands(pi);
|
|
4102
4114
|
pi.registerCommand("subagents", {
|
|
4103
|
-
description: "[settings|mode manual|adaptive|coordinator|cap <number>|tool-wait <minutes>|stuck <minutes>|save|reset] — Subagent
|
|
4115
|
+
description: "[history [dismiss]|settings|mode manual|adaptive|coordinator|cap <number>|tool-wait <minutes>|stuck <minutes>|save|reset] — Subagent management",
|
|
4104
4116
|
getArgumentCompletions: subagentsArgumentCompletions,
|
|
4105
4117
|
async handler(args, ctx) {
|
|
4106
4118
|
const tokens = args.trim().split(/\s+/).filter(Boolean);
|
|
4119
|
+
if (tokens[0] === "history") {
|
|
4120
|
+
try {
|
|
4121
|
+
const origin = { cwd: ctx.cwd, sessionId: ctx.sessionManager.getSessionId() };
|
|
4122
|
+
const waits = historicalWaits(origin);
|
|
4123
|
+
if (tokens[1] === "dismiss") {
|
|
4124
|
+
dismissAttention(ctx.cwd, attentionRevision(waits));
|
|
4125
|
+
ctx.ui.notify("Historical attention notice dismissed. Waits and evidence are unchanged.", "info");
|
|
4126
|
+
return;
|
|
4127
|
+
}
|
|
4128
|
+
const labels = waits.map((p) => `${p.runId} · session ${p.record.ownerSessionId ?? "unknown"} · ${p.phase}`);
|
|
4129
|
+
const selected = await ctx.ui.select("Historical subagent waits", labels);
|
|
4130
|
+
const park = waits[labels.indexOf(selected ?? "")];
|
|
4131
|
+
if (!park) return;
|
|
4132
|
+
const action = await ctx.ui.select(`${park.runId} · session ${park.record.ownerSessionId ?? "unknown"}`, ["Inspect", "Cancel pending wait"]);
|
|
4133
|
+
if (action === "Inspect") {
|
|
4134
|
+
ctx.ui.notify(JSON.stringify({ runId: park.runId, originatingSession: park.record.ownerSessionId,
|
|
4135
|
+
workspace: park.record.ownerWorkspace, waitId: park.waitId, generation: park.generation,
|
|
4136
|
+
phase: park.phase, reason: park.record.heldReason, cancellation: park.record.cancelReason,
|
|
4137
|
+
resumedBy: park.record.resumedBy, log: park.meta.logPath }), "info");
|
|
4138
|
+
} else if (action === "Cancel pending wait" && await ctx.ui.confirm("Cancel pending wait?", `${park.runId} belongs to session ${park.record.ownerSessionId ?? "unknown"}. This does not prove its writer has stopped.`)) {
|
|
4139
|
+
if (!await cancelParkedForUi?.(park.runId, "explicit cancellation from historical management")) throw new Error("The pending wait changed; inspect it again.");
|
|
4140
|
+
ctx.ui.notify(`Durably cancelled the pending wait of ${park.runId}. Writer termination may remain unproven.`, "info");
|
|
4141
|
+
}
|
|
4142
|
+
} catch (error) { ctx.ui.notify(`Historical wait management failed: ${String(error)}. Inspect or retry with /subagents history.`, "warning"); }
|
|
4143
|
+
return;
|
|
4144
|
+
}
|
|
4107
4145
|
if (tokens.length === 0 || (tokens.length === 1 && tokens[0] === "settings")) {
|
|
4108
4146
|
await openSettings(ctx);
|
|
4109
4147
|
return;
|
|
@@ -4153,7 +4191,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4153
4191
|
changeTiming(key, minutes);
|
|
4154
4192
|
ctx.ui.notify(`${tokens[0] === "tool-wait" ? "Tool-wait" : "No-progress"} alert: ${minutes} min (current session). Running subagents are unchanged.`, "info");
|
|
4155
4193
|
} else {
|
|
4156
|
-
ctx.ui.notify("Usage: /subagents [settings|mode manual|adaptive|coordinator|cap <number>|tool-wait <minutes>|stuck <minutes>|save|reset]", "warning");
|
|
4194
|
+
ctx.ui.notify("Usage: /subagents [history [dismiss]|settings|mode manual|adaptive|coordinator|cap <number>|tool-wait <minutes>|stuck <minutes>|save|reset]", "warning");
|
|
4157
4195
|
}
|
|
4158
4196
|
refreshBackgroundWorkNavigator(ctx);
|
|
4159
4197
|
},
|
|
@@ -1670,6 +1670,7 @@ export interface ResumeOrchestrationOptions {
|
|
|
1670
1670
|
/** Follow a cancelled generation to the attempt it already published. */
|
|
1671
1671
|
stopAttempt?: StopPublishedAttempt;
|
|
1672
1672
|
notify?(message: string, level: "info" | "warning"): void;
|
|
1673
|
+
notifyWhen?(meta: RunMeta): boolean;
|
|
1673
1674
|
now?: () => number;
|
|
1674
1675
|
}
|
|
1675
1676
|
|
|
@@ -1872,7 +1873,7 @@ export function createResumeOrchestration(options: ResumeOrchestrationOptions):
|
|
|
1872
1873
|
} catch { /* the park record still shows the hold */ }
|
|
1873
1874
|
if (park.meta.assignment) commitAssignmentTerminal({ ...park.meta, status: "failed", failureReason: reason }, true);
|
|
1874
1875
|
try {
|
|
1875
|
-
options.notify?.(`Subagent ${park.runId} wait ${park.waitId} is held: ${reason}`, "warning");
|
|
1876
|
+
if (!options.notifyWhen || options.notifyWhen(park.meta)) options.notify?.(`Subagent ${park.runId} wait ${park.waitId} is held: ${reason}`, "warning");
|
|
1876
1877
|
} catch { /* ignore UI failures */ }
|
|
1877
1878
|
return "held";
|
|
1878
1879
|
};
|
|
@@ -2117,7 +2118,8 @@ export async function cancelParkedWait(
|
|
|
2117
2118
|
const scope = backgroundScopeForMeta(park.meta, park.record);
|
|
2118
2119
|
const at = (options.now ?? Date.now)();
|
|
2119
2120
|
if (park.meta.assignment) {
|
|
2120
|
-
commitAssignmentTerminal({ ...park.meta, status: "killed" });
|
|
2121
|
+
const outcome = commitAssignmentTerminal({ ...park.meta, status: "killed" });
|
|
2122
|
+
if (outcome?.outcome !== "cancelled") throw new Error("The assignment already has a non-cancelled terminal outcome; cancellation was not committed.");
|
|
2121
2123
|
const cancellation = await stopPublishedAttempt(park, reason, options.stopAttempt, options.now);
|
|
2122
2124
|
updateParkRecord(park.runId, { phase: "cancelled", cancelReason: cancellation === "foreign" ? `${reason} (published writer termination remains unproven)` : reason, cancelledAt: at });
|
|
2123
2125
|
return;
|
|
@@ -2130,10 +2132,10 @@ export async function cancelParkedWait(
|
|
|
2130
2132
|
updateParkRecord(park.runId, {
|
|
2131
2133
|
phase: "held",
|
|
2132
2134
|
cancelReason: `${reason} (the durable cancellation failed: ${reasonOf(error)})`,
|
|
2133
|
-
cancelledAt: at,
|
|
2134
2135
|
});
|
|
2135
|
-
|
|
2136
|
+
throw new Error(`Durable cancellation failed: ${reasonOf(error)}`);
|
|
2136
2137
|
}
|
|
2138
|
+
if (view.phase !== "cancelled") throw new Error(`Durable cancellation was not confirmed (wait is ${view.phase}).`);
|
|
2137
2139
|
const settled = readParkRecord(park.runId);
|
|
2138
2140
|
if (settled?.phase === "cancelled" && settled.cancelReason) return;
|
|
2139
2141
|
const cancellation = await stopPublishedAttempt(park, reason, options.stopAttempt, options.now);
|
|
@@ -2148,14 +2150,7 @@ export async function cancelParkedWait(
|
|
|
2148
2150
|
});
|
|
2149
2151
|
return;
|
|
2150
2152
|
}
|
|
2151
|
-
|
|
2152
|
-
updateParkRecord(park.runId, {
|
|
2153
|
-
phase: "cancelled",
|
|
2154
|
-
cancelReason: cancellation === "foreign"
|
|
2155
|
-
? `${reason} (the published continuation could not be attributed to this parent and was left running)`
|
|
2156
|
-
: reason,
|
|
2157
|
-
cancelledAt: at,
|
|
2158
|
-
});
|
|
2153
|
+
throw new Error("Durable cancellation unavailable: the original worker scope and observed-waits service are required.");
|
|
2159
2154
|
}
|
|
2160
2155
|
|
|
2161
2156
|
/**
|
|
@@ -2493,17 +2488,22 @@ export async function settleResumedAttemptExit(input: ResumedAttemptExitInput):
|
|
|
2493
2488
|
|
|
2494
2489
|
export async function reconcileParkedWaits(
|
|
2495
2490
|
service: WorkerOperationService | undefined,
|
|
2496
|
-
|
|
2491
|
+
outerOptions: {
|
|
2497
2492
|
resume(park: ParkedWait): Promise<"resumed" | "held" | "skipped">;
|
|
2498
2493
|
notify?(message: string, level: "warning"): void;
|
|
2494
|
+
notifyWhen?(meta: RunMeta): boolean;
|
|
2499
2495
|
},
|
|
2500
2496
|
): Promise<{ resumed: number; held: number; skipped: number }> {
|
|
2501
2497
|
let resumed = 0;
|
|
2502
2498
|
let held = 0;
|
|
2503
2499
|
let skipped = 0;
|
|
2500
|
+
const options = outerOptions;
|
|
2501
|
+
const scopedOptions = (meta: RunMeta) => ({ ...outerOptions,
|
|
2502
|
+
notify: outerOptions.notifyWhen && !outerOptions.notifyWhen(meta) ? undefined : outerOptions.notify });
|
|
2504
2503
|
if (service?.outageReconciliation === 1) {
|
|
2505
2504
|
try {
|
|
2506
2505
|
for (const report of pendingObservations()) {
|
|
2506
|
+
const options = scopedOptions(report.meta);
|
|
2507
2507
|
try {
|
|
2508
2508
|
const scope = backgroundScopeForMeta(report.meta, report.record);
|
|
2509
2509
|
if (!scope) continue;
|
|
@@ -2569,6 +2569,7 @@ export async function reconcileParkedWaits(
|
|
|
2569
2569
|
}
|
|
2570
2570
|
}
|
|
2571
2571
|
for (const park of parkedWaits()) {
|
|
2572
|
+
const options = scopedOptions(park.meta);
|
|
2572
2573
|
try {
|
|
2573
2574
|
if (park.record.phase === "resuming") {
|
|
2574
2575
|
const attemptId = park.record.resumedBy ?? "";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-better-harness",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.45.0",
|
|
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",
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"pi-ollama-cloud": "0.13.1",
|
|
65
65
|
"pi-better-sandbox": "0.14.1",
|
|
66
66
|
"pi-better-ssh": "0.1.5",
|
|
67
|
-
"pi-better-subagents": "0.
|
|
67
|
+
"pi-better-subagents": "0.32.0",
|
|
68
68
|
"proper-lockfile": "^4.1.2",
|
|
69
69
|
"shell-quote": "1.12.0",
|
|
70
70
|
"smol-toml": "1.9.0",
|