@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.
Files changed (3) hide show
  1. package/README.md +7 -2
  2. package/index.ts +22 -4
  3. 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 (`PI_BABYSIT_REAP_AFTER`, default 120s) using the same parked-turn
214
- rule, so a subagent waiting on a long build is never false-killed. Give bounded
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
- const SUBAGENT_REUSE_HINT = ["0", "off", "none"].includes(SUBAGENT_REAP_AFTER)
1069
- ? "Session remains available until its absolute timeout."
1070
- : `Session remains available for follow-ups during the ${SUBAGENT_REAP_AFTER} idle grace.`;
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
- `${SUBAGENT_REUSE_HINT} Follow-up: babysit_send { id: "${id}" }, ` +
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
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@yusukeshib/pi-babysit",
3
- "version": "0.6.13",
3
+ "version": "0.6.14",
4
4
  "description": "Run any shell command and pi subagents under babysit, with context-safe captured output.",
5
5
  "keywords": [
6
6
  "pi-package",