@yusukeshib/pi-babysit 0.6.13 → 0.6.14
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/README.md +7 -2
- package/index.ts +22 -4
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -210,8 +210,13 @@ because blindly rerunning an arbitrary command can duplicate side effects.
|
|
|
210
210
|
incrementally, which keeps follow-up tasks efficient.
|
|
211
211
|
|
|
212
212
|
Subagents load `self-reap.ts`, which exits an idle finished subagent after a
|
|
213
|
-
grace window
|
|
214
|
-
|
|
213
|
+
grace window using the same parked-turn rule, so a subagent waiting on a long
|
|
214
|
+
build is never false-killed. Callers can set `reapAfter: "5m"` on
|
|
215
|
+
`babysit_run { profile: "subagent", task, reapAfter: "5m" }`. Omission uses
|
|
216
|
+
`PI_BABYSIT_REAP_AFTER`, then `PI_SUBAGENT_REAP_AFTER`, then 120s.
|
|
217
|
+
`reapAfter: "none"` (also `"off"` or `"0"`) disables idle reaping; the absolute
|
|
218
|
+
`timeout` still applies. Follow-up tasks cancel the idle timer. This differs
|
|
219
|
+
from `idleTimeout`, which kills based on lack of output, even during work. Give bounded
|
|
215
220
|
recon/review tasks at least one cost, turn, tool-call, or token budget; omit
|
|
216
221
|
budgets only for intentionally open-ended work. Optional task budgets are
|
|
217
222
|
observed by the parent poller. `maxUsageTokens` counts cumulative input, output,
|
package/index.ts
CHANGED
|
@@ -464,6 +464,7 @@ interface Meta {
|
|
|
464
464
|
// subagent
|
|
465
465
|
task?: string;
|
|
466
466
|
launchCommand?: string;
|
|
467
|
+
reapAfter?: string;
|
|
467
468
|
promptOffset?: number;
|
|
468
469
|
model?: string;
|
|
469
470
|
tools?: string[];
|
|
@@ -1065,9 +1066,11 @@ const SUBAGENT_BUDGET_GRACE_MS =
|
|
|
1065
1066
|
parseDurMs(process.env.PI_BABYSIT_BUDGET_GRACE ?? "90s") ?? 90_000;
|
|
1066
1067
|
const SUBAGENT_REAP_AFTER =
|
|
1067
1068
|
process.env.PI_BABYSIT_REAP_AFTER ?? process.env.PI_SUBAGENT_REAP_AFTER ?? "120s";
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1069
|
+
export function subagentReuseHint(reapAfter = SUBAGENT_REAP_AFTER): string {
|
|
1070
|
+
return ["0", "off", "none"].includes(reapAfter)
|
|
1071
|
+
? "Session remains available until its absolute timeout."
|
|
1072
|
+
: `Session remains available for follow-ups during the ${reapAfter} idle grace.`;
|
|
1073
|
+
}
|
|
1071
1074
|
|
|
1072
1075
|
export function clip(s: string, maxBytes = TAIL_MAX_BYTES): string {
|
|
1073
1076
|
if (maxBytes <= 0) return "";
|
|
@@ -2101,6 +2104,7 @@ interface SubagentOpts {
|
|
|
2101
2104
|
depth: number;
|
|
2102
2105
|
maxDepth: number;
|
|
2103
2106
|
budget?: SubagentBudget;
|
|
2107
|
+
reapAfter?: string;
|
|
2104
2108
|
// Idle-timeout is OFF by default: an RPC-mode pi is silent while it works,
|
|
2105
2109
|
// so idle detection would false-kill a busy subagent. The absolute timeout
|
|
2106
2110
|
// is the safety valve instead.
|
|
@@ -2111,6 +2115,11 @@ interface SubagentOpts {
|
|
|
2111
2115
|
async function spawnSubagent(
|
|
2112
2116
|
opts: SubagentOpts,
|
|
2113
2117
|
): Promise<{ id: string; model?: string } | { error: string }> {
|
|
2118
|
+
const reapAfter = opts.reapAfter?.trim() ?? SUBAGENT_REAP_AFTER;
|
|
2119
|
+
if (opts.reapAfter !== undefined && !["0", "off", "none"].includes(reapAfter) &&
|
|
2120
|
+
(!/^\d+(ms|s|m|h)?$/.test(reapAfter) || !Number.isFinite(parseDurMs(reapAfter)))) {
|
|
2121
|
+
return { error: "Invalid reapAfter: use a duration such as 30s or 5m, or none/off/0 to disable." };
|
|
2122
|
+
}
|
|
2114
2123
|
// Long-lived RPC worker: the task is NOT passed as argv — it is injected
|
|
2115
2124
|
// below as an RPC `prompt` command, whose response we validate so spawn
|
|
2116
2125
|
// failures (bad model, missing API key) are loud instead of a silent exit=1.
|
|
@@ -2189,6 +2198,7 @@ async function spawnSubagent(
|
|
|
2189
2198
|
r = await bs(bsArgs, {
|
|
2190
2199
|
cwd: opts.cwd,
|
|
2191
2200
|
env: {
|
|
2201
|
+
PI_BABYSIT_REAP_AFTER: reapAfter,
|
|
2192
2202
|
[SUBAGENT_DEPTH_ENV]: String(opts.depth),
|
|
2193
2203
|
[SUBAGENT_MAX_DEPTH_ENV]: String(opts.maxDepth),
|
|
2194
2204
|
},
|
|
@@ -2221,6 +2231,7 @@ async function spawnSubagent(
|
|
|
2221
2231
|
depth: opts.depth,
|
|
2222
2232
|
maxDepth: opts.maxDepth,
|
|
2223
2233
|
budget: opts.budget,
|
|
2234
|
+
reapAfter,
|
|
2224
2235
|
});
|
|
2225
2236
|
|
|
2226
2237
|
// Wait for pi to boot (first JSON event in the log), then inject the task.
|
|
@@ -2314,6 +2325,7 @@ async function spawnSubagent(
|
|
|
2314
2325
|
depth: opts.depth,
|
|
2315
2326
|
maxDepth: opts.maxDepth,
|
|
2316
2327
|
budget: opts.budget,
|
|
2328
|
+
reapAfter,
|
|
2317
2329
|
startedAt: Date.now(),
|
|
2318
2330
|
});
|
|
2319
2331
|
return { id, model: resolvedModel };
|
|
@@ -2804,7 +2816,7 @@ async function waitForTask(
|
|
|
2804
2816
|
ok: completed.ok,
|
|
2805
2817
|
text:
|
|
2806
2818
|
`Subagent ${id} finished its task (${stats}).\n` +
|
|
2807
|
-
`${
|
|
2819
|
+
`${subagentReuseHint(readMeta(id)?.reapAfter)} Follow-up: babysit_send { id: "${id}" }, ` +
|
|
2808
2820
|
`or babysit_kill when done.\n\n${completed.body}`,
|
|
2809
2821
|
status: st,
|
|
2810
2822
|
progress: prog,
|
|
@@ -3901,6 +3913,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
3901
3913
|
"Absolute auto-kill after this long (e.g. 30m). Default: none for processes (dev servers may run forever), 15m for subagents. 'none' disables.",
|
|
3902
3914
|
}),
|
|
3903
3915
|
),
|
|
3916
|
+
reapAfter: Type.Optional(
|
|
3917
|
+
Type.String({
|
|
3918
|
+
description: "Subagent only: stop the worker after this long in task-complete IDLE (e.g. 30s, 5m). Defaults to PI_BABYSIT_REAP_AFTER, then PI_SUBAGENT_REAP_AFTER, then 120s. none/off/0 disables; absolute timeout still applies. Follow-up tasks cancel the idle timer.",
|
|
3919
|
+
}),
|
|
3920
|
+
),
|
|
3904
3921
|
idleTimeout: Type.Optional(
|
|
3905
3922
|
Type.String({
|
|
3906
3923
|
description:
|
|
@@ -4221,6 +4238,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4221
4238
|
maxUsageTokens: params.maxUsageTokens,
|
|
4222
4239
|
}
|
|
4223
4240
|
: undefined,
|
|
4241
|
+
reapAfter: params.reapAfter,
|
|
4224
4242
|
timeout: params.timeout ?? "15m",
|
|
4225
4243
|
idleTimeout: params.idleTimeout,
|
|
4226
4244
|
});
|