@yusukeshib/pi-babysit 0.6.22 → 0.6.23
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 +29 -17
- package/index.ts +51 -19
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -50,10 +50,13 @@ at every depth.
|
|
|
50
50
|
Because sessions are real PTYs, the agent can also **drive interactive
|
|
51
51
|
programs** (installers, wizards, REPLs): type with `babysit_send`
|
|
52
52
|
(text or named keys) and read the rendered screen with
|
|
53
|
-
`babysit_check { screen: true }` — a capability neither retired extension had.
|
|
53
|
+
`babysit_check { description, screen: true }` — a capability neither retired extension had.
|
|
54
54
|
|
|
55
55
|
## Tools (LLM-callable)
|
|
56
56
|
|
|
57
|
+
All five tools require a short, non-blank `description` explaining the purpose
|
|
58
|
+
of that call.
|
|
59
|
+
|
|
57
60
|
| Tool | What it does |
|
|
58
61
|
| ---- | ------------ |
|
|
59
62
|
| `babysit_run` | Run any command (`description` + `command`, optional `name`/`pty`/`timeout`/`idleTimeout`/`retryOnWorkerDeath`/`notificationGroup`/`lifecycle`). Set `foreground: true` for one process or subagent whose result is needed in the same tool call; use `returnPattern`/`returnLines`/`maxBytes` to keep noisy process output bounded. Foreground and non-interactive process waits default to lifecycle `attached`, which terminates the process tree if that owning tool call is interrupted; background starts default to `detached`. Or start a named background subagent (`description`, `profile: "subagent"`, `task`, optional `name`/`agent`/`model`/`tools`/`maxDepth` and budget fields), then always collect it with `babysit_wait`. `maxDepth` defaults to 1. Subagent `continueAfterStart: true` is accepted as a compatibility alias for this default background behavior. Quick commands return inline; longer process runs notify in the background |
|
|
@@ -71,26 +74,35 @@ calls if another extension or preset re-enables `bash`, including shell
|
|
|
71
74
|
backgrounding (`… &`, `nohup`, `setsid`, `disown`). Set
|
|
72
75
|
`PI_BABYSIT_ALLOW_BASH=1` to retain direct `bash` explicitly.
|
|
73
76
|
|
|
74
|
-
## Describe every
|
|
77
|
+
## Describe every operation
|
|
75
78
|
|
|
76
|
-
Every
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
79
|
+
Every babysit tool call requires a non-blank `description`. Explain the purpose
|
|
80
|
+
briefly in language the user can understand. For `babysit_run`, `name` remains
|
|
81
|
+
a stable session identifier, while `command` / `task` contains the execution
|
|
82
|
+
instructions. Its description is trimmed and saved with the session. For
|
|
83
|
+
`babysit_check`, `babysit_send`, `babysit_wait`, and `babysit_kill`, the description
|
|
84
|
+
explains only that operation and never overwrites the session description.
|
|
85
|
+
Descriptions are display metadata, not shell code, stdin, or instructions sent
|
|
86
|
+
to the subagent.
|
|
81
87
|
|
|
82
88
|
```text
|
|
83
89
|
babysit_run { description: "Run regression tests", command: "bun test", foreground: true }
|
|
84
90
|
babysit_run { description: "Review the authentication changes", profile: "subagent", task: "Review the authentication diff for security regressions", maxTurns: 5 }
|
|
91
|
+
babysit_check { description: "Inspect build errors", id: "build", pattern: "ERROR" }
|
|
92
|
+
babysit_wait { description: "Wait for the build to finish", id: "build" }
|
|
93
|
+
babysit_send { description: "Ask the reviewer to check authorization too", id: "review", text: "Also check authorization." }
|
|
94
|
+
babysit_kill { description: "Stop the unused development server", id: "dev-server" }
|
|
85
95
|
```
|
|
86
96
|
|
|
87
97
|
In Pi's main transcript the description is the prominent yellow heading, with
|
|
88
|
-
the execution kind in muted gray. The full command or task
|
|
98
|
+
the tool name and execution kind in muted gray. The full command or task (or
|
|
99
|
+
all other operation arguments, including target IDs and input) is always visible
|
|
89
100
|
in gray beneath it, even when tool results are collapsed. Session lists, the
|
|
90
|
-
widget, and completion notifications
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
before
|
|
101
|
+
widget, and completion notifications show the session's original description.
|
|
102
|
+
Older saved sessions and tool calls without descriptions remain readable,
|
|
103
|
+
falling back to their command, task, or other arguments. Callers must supply a
|
|
104
|
+
description for every new call; blank or missing values are rejected before
|
|
105
|
+
execution.
|
|
94
106
|
|
|
95
107
|
## Remote processes over SSH
|
|
96
108
|
|
|
@@ -102,9 +114,9 @@ pi-babysit does not install remote software or copy credentials.
|
|
|
102
114
|
```text
|
|
103
115
|
babysit_run { description: "Run tests on Linux", host: "yusuke-t4", cwd: "/home/yusuke/project", command: "bun test", foreground: true, returnLines: 30 }
|
|
104
116
|
babysit_run { description: "Open a remote Python REPL", host: "user@server", command: "python3 -i", name: "remote-repl", continueAfterStart: true }
|
|
105
|
-
babysit_check { id: "remote-repl", screen: true }
|
|
106
|
-
babysit_send { id: "remote-repl", text: "print(42)" }
|
|
107
|
-
babysit_kill { id: "remote-repl" }
|
|
117
|
+
babysit_check { description: "Read the remote REPL screen", id: "remote-repl", screen: true }
|
|
118
|
+
babysit_send { description: "Evaluate a value in the remote REPL", id: "remote-repl", text: "print(42)" }
|
|
119
|
+
babysit_kill { description: "Close the remote REPL", id: "remote-repl" }
|
|
108
120
|
```
|
|
109
121
|
|
|
110
122
|
`host` omitted (or `"local"`) preserves local behavior. `cwd` is process-only:
|
|
@@ -204,8 +216,8 @@ pending for the next poll. Larger output stays out of model context. Inspect it
|
|
|
204
216
|
through the session id without creating another shell session:
|
|
205
217
|
|
|
206
218
|
```text
|
|
207
|
-
babysit_check { id: "cargo-test", lines: 50 }
|
|
208
|
-
babysit_check { id: "cargo-test", pattern: "FAIL|ERROR", lines: 50 }
|
|
219
|
+
babysit_check { description: "Inspect the recent test output", id: "cargo-test", lines: 50 }
|
|
220
|
+
babysit_check { description: "Find test failures", id: "cargo-test", pattern: "FAIL|ERROR", lines: 50 }
|
|
209
221
|
```
|
|
210
222
|
|
|
211
223
|
Tail and search results are capped at 200 lines and default to a 4 KB total
|
package/index.ts
CHANGED
|
@@ -51,6 +51,18 @@ import { Type, type TSchema } from "typebox";
|
|
|
51
51
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
52
52
|
import { type AgentConfig, type AgentScope, discoverAgents } from "./agents";
|
|
53
53
|
|
|
54
|
+
const descriptionSchema = Type.String({
|
|
55
|
+
minLength: 1,
|
|
56
|
+
pattern: "\\S",
|
|
57
|
+
description: "Required short human-readable purpose for this operation. Explain what it does and why; shown prominently to the user.",
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
function requireDescription(value: unknown): void {
|
|
61
|
+
if (typeof value !== "string" || !value.trim()) {
|
|
62
|
+
throw new Error("`description` is required and must be a non-blank string for every babysit tool call.");
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
54
66
|
// Dedicated babysit state root so pi-managed sessions never collide with the
|
|
55
67
|
// user's own manual `babysit` sessions. The base is namespaced per pi session
|
|
56
68
|
// on session_start (BABYSIT_DIR=<base>/<session-id>), so each pi session only
|
|
@@ -1373,6 +1385,17 @@ function escapeCommandForDisplay(command: string): string {
|
|
|
1373
1385
|
);
|
|
1374
1386
|
}
|
|
1375
1387
|
|
|
1388
|
+
function renderOperationCall(name: string, args: Record<string, unknown> | undefined, theme: Theme) {
|
|
1389
|
+
const { description, ...operationArgs } = args ?? {};
|
|
1390
|
+
const purpose = typeof description === "string" ? description.trim() : "";
|
|
1391
|
+
const title = theme.fg("muted", theme.bold(name));
|
|
1392
|
+
const heading = purpose
|
|
1393
|
+
? `${title} ${theme.fg("warning", theme.bold(escapeCommandForDisplay(purpose)))}`
|
|
1394
|
+
: title;
|
|
1395
|
+
const raw = Object.keys(operationArgs).length ? JSON.stringify(operationArgs) : "";
|
|
1396
|
+
return new Text(heading + (raw ? `\n${theme.fg("muted", escapeCommandForDisplay(raw))}` : ""), 0, 0);
|
|
1397
|
+
}
|
|
1398
|
+
|
|
1376
1399
|
export function readableLaunchCommand(command: string): string {
|
|
1377
1400
|
// Older sessions quoted every argv element, including safe paths and flags.
|
|
1378
1401
|
if (!command.startsWith("'") || !command.endsWith("'") || !command.includes("' '")) return command;
|
|
@@ -3286,7 +3309,7 @@ async function waitForTask(
|
|
|
3286
3309
|
ok: completed.ok,
|
|
3287
3310
|
text:
|
|
3288
3311
|
`Subagent ${id} finished its task (${stats}).\n` +
|
|
3289
|
-
`${subagentReuseHint(readMeta(id)?.reapAfter)} Follow-up: babysit_send { id: "${id}" }, ` +
|
|
3312
|
+
`${subagentReuseHint(readMeta(id)?.reapAfter)} Follow-up: babysit_send { description: "Assign a follow-up task", id: "${id}" }, ` +
|
|
3290
3313
|
`or babysit_kill when done.\n\n${completed.body}`,
|
|
3291
3314
|
status: st,
|
|
3292
3315
|
progress: prog,
|
|
@@ -3714,7 +3737,14 @@ export default function (pi: ExtensionAPI) {
|
|
|
3714
3737
|
const execute = tool.execute;
|
|
3715
3738
|
pi.registerTool({
|
|
3716
3739
|
...tool,
|
|
3740
|
+
description: tool.description + " Always provide a short human-readable `description` explaining the purpose of this operation.",
|
|
3741
|
+
promptGuidelines: [
|
|
3742
|
+
"Every babysit tool call requires a short description explaining its purpose for the user. Operation descriptions for check/send/wait/kill do not change the session's original description.",
|
|
3743
|
+
...(tool.promptGuidelines ?? []),
|
|
3744
|
+
],
|
|
3745
|
+
renderCall: tool.renderCall ?? ((args, theme) => renderOperationCall(tool.name, args as Record<string, unknown>, theme)),
|
|
3717
3746
|
async execute(toolCallId, params, signal, onUpdate, ctx) {
|
|
3747
|
+
requireDescription((params as { description?: unknown }).description);
|
|
3718
3748
|
const result = await execute(toolCallId, params, signal, onUpdate, ctx);
|
|
3719
3749
|
if ((result as typeof result & { isError?: boolean }).isError === true) {
|
|
3720
3750
|
declaredToolErrors.add(toolCallId);
|
|
@@ -4319,7 +4349,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4319
4349
|
block: true,
|
|
4320
4350
|
reason:
|
|
4321
4351
|
"Use babysit_run for shell commands so output is supervised and captured outside model context. " +
|
|
4322
|
-
"Inspect an existing session log with babysit_check { id, lines, pattern? }. " +
|
|
4352
|
+
"Inspect an existing session log with babysit_check { description, id, lines, pattern? }. " +
|
|
4323
4353
|
`Retry with a short purpose description as babysit_run({ description: "Explain the purpose of this command", command: ${JSON.stringify(command)} }).`,
|
|
4324
4354
|
};
|
|
4325
4355
|
});
|
|
@@ -4330,13 +4360,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
4330
4360
|
label: "Babysit: run",
|
|
4331
4361
|
description:
|
|
4332
4362
|
"Run a supervised shell command, or start a reusable pi subagent with `profile: \"subagent\"`. " +
|
|
4333
|
-
"Always provide a short human-readable `description` of what this run does and why. " +
|
|
4334
4363
|
"Use `foreground` for results needed now; otherwise long commands notify on exit. Full logs stay on disk. " +
|
|
4335
4364
|
"`returnPattern`/`returnLines` bound foreground output. Sessions support check, wait, send, and kill.",
|
|
4336
4365
|
promptSnippet: "Run supervised commands or bounded pi subagents with context-safe logs",
|
|
4337
4366
|
promptGuidelines: [
|
|
4338
4367
|
"Use babysit_run for shell commands and give meaningful sessions a stable name; bundle tiny related read-only observations into one command.",
|
|
4339
|
-
"Every babysit_run requires a short description explaining its purpose for the user, for both commands and subagents. Describe the actual work, not a generic 'run command' or 'start agent'.",
|
|
4340
4368
|
"Use babysit_run foreground mode for one process or subagent whose result is needed now; never issue sibling foreground runs in parallel. For parallel process checks, start background runs with continueAfterStart and collect them with one multi-session babysit_wait.",
|
|
4341
4369
|
"Use returnPattern/returnLines for noisy commands. During edit/fix loops run targeted checks first and one full validation suite at the end instead of repeating every full gate.",
|
|
4342
4370
|
"After a background process starts, stop the turn for its automatic notification; never poll or sleep. Use continueAfterStart only for specific non-polling process work.",
|
|
@@ -4350,11 +4378,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4350
4378
|
return prepareBabysitRunArguments(args) as never;
|
|
4351
4379
|
},
|
|
4352
4380
|
parameters: Type.Object({
|
|
4353
|
-
description:
|
|
4354
|
-
minLength: 1,
|
|
4355
|
-
pattern: "\\S",
|
|
4356
|
-
description: "Required short human-readable purpose for this command or subagent. Explain what it does and why; shown prominently to the user.",
|
|
4357
|
-
}),
|
|
4381
|
+
description: descriptionSchema,
|
|
4358
4382
|
host: Type.Optional(Type.String({ description: "Process SSH destination/config alias (default local). Remote subagents are unsupported." })),
|
|
4359
4383
|
cwd: Type.Optional(Type.String({ description: "Process working directory. Remote default is login home; never inferred from local cwd." })),
|
|
4360
4384
|
command: Type.Optional(
|
|
@@ -4485,9 +4509,6 @@ export default function (pi: ExtensionAPI) {
|
|
|
4485
4509
|
|
|
4486
4510
|
}),
|
|
4487
4511
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
4488
|
-
if (typeof params.description !== "string" || !params.description.trim()) {
|
|
4489
|
-
throw new Error("`description` is required and must be a non-blank string for both commands and subagents.");
|
|
4490
|
-
}
|
|
4491
4512
|
const description = params.description.trim();
|
|
4492
4513
|
await requireBabysit();
|
|
4493
4514
|
const isSubagent = params.profile === "subagent";
|
|
@@ -4702,8 +4723,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
4702
4723
|
text:
|
|
4703
4724
|
`${retried ? "Retried once after external worker death.\n" : ""}` +
|
|
4704
4725
|
`Process started (id: ${res.id}). ${NOTIFY_MARKER}\nDescription: ${escapeCommandForDisplay(description)}\nLog: ${logPath(res.id)}\n${nextStep}\n` +
|
|
4705
|
-
`Inspect: babysit_check { id: "${res.id}" } (screen: true for TUIs) · ` +
|
|
4706
|
-
`Wait: babysit_wait { id: "${res.id}" } · Kill: babysit_kill { id: "${res.id}" }\n` +
|
|
4726
|
+
`Inspect: babysit_check { description: "Inspect session output", id: "${res.id}" } (screen: true for TUIs) · ` +
|
|
4727
|
+
`Wait: babysit_wait { description: "Wait for completion", id: "${res.id}" } · Kill: babysit_kill { description: "Stop this session", id: "${res.id}" }\n` +
|
|
4707
4728
|
`Human can watch/take over: /babysit`,
|
|
4708
4729
|
},
|
|
4709
4730
|
],
|
|
@@ -4810,8 +4831,8 @@ export default function (pi: ExtensionAPI) {
|
|
|
4810
4831
|
text:
|
|
4811
4832
|
`Subagent started (id: ${res.id})${agent ? ` [agent: ${agent.name}]` : ""}${res.model ? ` [model: ${res.model}]` : ""} [depth: ${subagentNesting.childDepth}/${subagentNesting.maxDepth}].\n` +
|
|
4812
4833
|
`Description: ${escapeCommandForDisplay(description)}\nTask accepted — running in the background. You MUST collect it with babysit_wait before finishing the parent task; use foreground: true next time when no independent work is available.\n` +
|
|
4813
|
-
`Progress: babysit_check { id: "${res.id}" } (only when inspection is needed)\n` +
|
|
4814
|
-
`Collect: babysit_wait { id: "${res.id}" }\n` +
|
|
4834
|
+
`Progress: babysit_check { description: "Inspect agent progress", id: "${res.id}" } (only when inspection is needed)\n` +
|
|
4835
|
+
`Collect: babysit_wait { description: "Collect the agent result", id: "${res.id}" }\n` +
|
|
4815
4836
|
`Human can watch/steer: /babysit (pick ${res.id})`,
|
|
4816
4837
|
},
|
|
4817
4838
|
],
|
|
@@ -4895,6 +4916,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
4895
4916
|
"Do NOT poll this while merely waiting for a process to end — the exit notification is automatic.",
|
|
4896
4917
|
promptSnippet: "Check status/progress of babysit sessions (processes and subagents)",
|
|
4897
4918
|
parameters: Type.Object({
|
|
4919
|
+
description: descriptionSchema,
|
|
4898
4920
|
id: Type.Optional(Type.String({ description: "Session id. Omit to list sessions." })),
|
|
4899
4921
|
state: Type.Optional(
|
|
4900
4922
|
StringEnum(["running", "terminal", "all"] as const, {
|
|
@@ -5135,11 +5157,12 @@ export default function (pi: ExtensionAPI) {
|
|
|
5135
5157
|
description:
|
|
5136
5158
|
"Send input to a babysit session. Process: `text` types a line into its stdin (PTY), " +
|
|
5137
5159
|
"`keys` presses named keys (Enter, Tab, Esc, Up/Down/Left/Right, C-c, F1…) — use with " +
|
|
5138
|
-
"babysit_check { screen: true } to drive interactive programs. Subagent: `text` is " +
|
|
5160
|
+
"babysit_check { description, screen: true } to drive interactive programs. Subagent: `text` is " +
|
|
5139
5161
|
"STEERING while it works, or a NEW TASK after the current task settles (mode: auto/steer/task) — this " +
|
|
5140
5162
|
"is how you resume a finished subagent with full context.",
|
|
5141
5163
|
promptSnippet: "Send text/keys to a process, or steering/follow-up tasks to a subagent",
|
|
5142
5164
|
parameters: Type.Object({
|
|
5165
|
+
description: descriptionSchema,
|
|
5143
5166
|
id: Type.String({ description: "Session id." }),
|
|
5144
5167
|
text: Type.Optional(
|
|
5145
5168
|
Type.String({ description: "Text to send (a line for processes; a message for subagents)." }),
|
|
@@ -5211,7 +5234,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
5211
5234
|
content: [
|
|
5212
5235
|
{
|
|
5213
5236
|
type: "text",
|
|
5214
|
-
text: `Sent to ${params.id}. Read the reaction with babysit_check { id: "${params.id}"${params.keys?.length ? ", screen: true" : ""} } — don't expect the echo of your own input.`,
|
|
5237
|
+
text: `Sent to ${params.id}. Read the reaction with babysit_check { description: "Inspect the response to input", id: "${params.id}"${params.keys?.length ? ", screen: true" : ""} } — don't expect the echo of your own input.`,
|
|
5215
5238
|
},
|
|
5216
5239
|
],
|
|
5217
5240
|
details: { kind: "process" },
|
|
@@ -5328,6 +5351,11 @@ export default function (pi: ExtensionAPI) {
|
|
|
5328
5351
|
// New task → new bookkeeping window, so check/wait track THIS task.
|
|
5329
5352
|
writeMeta(params.id, {
|
|
5330
5353
|
kind: "subagent",
|
|
5354
|
+
name: meta?.name,
|
|
5355
|
+
description: meta?.description,
|
|
5356
|
+
launchCommand: meta?.launchCommand,
|
|
5357
|
+
reapAfter: meta?.reapAfter,
|
|
5358
|
+
startedAt: meta?.startedAt,
|
|
5331
5359
|
task: params.text,
|
|
5332
5360
|
// Start after the prompt-acceptance response. This excludes a
|
|
5333
5361
|
// preceding run that settled between `send` and RPC acceptance.
|
|
@@ -5389,6 +5417,7 @@ export default function (pi: ExtensionAPI) {
|
|
|
5389
5417
|
"the exit notification will resume you.",
|
|
5390
5418
|
promptSnippet: "Block until session(s) finish — process exit / output pattern / subagent task done",
|
|
5391
5419
|
parameters: Type.Object({
|
|
5420
|
+
description: descriptionSchema,
|
|
5392
5421
|
id: Type.Optional(Type.String({ description: "Session id (single wait)." })),
|
|
5393
5422
|
ids: Type.Optional(
|
|
5394
5423
|
Type.Array(Type.String(), {
|
|
@@ -5535,7 +5564,10 @@ export default function (pi: ExtensionAPI) {
|
|
|
5535
5564
|
label: "Babysit: kill",
|
|
5536
5565
|
description: "Terminate a babysit session (process or subagent).",
|
|
5537
5566
|
promptSnippet: "Terminate a babysit session",
|
|
5538
|
-
parameters: Type.Object({
|
|
5567
|
+
parameters: Type.Object({
|
|
5568
|
+
description: descriptionSchema,
|
|
5569
|
+
id: Type.String({ description: "Session id." }),
|
|
5570
|
+
}),
|
|
5539
5571
|
async execute(_id, params, _signal, _onUpdate, ctx) {
|
|
5540
5572
|
await requireBabysit();
|
|
5541
5573
|
// Prevent the exit poller racing a requested kill, but restore delivery
|