@arhen/pi-core-subagent 1.3.45 → 1.3.47

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 CHANGED
@@ -93,11 +93,10 @@ Define agents inline per call, or reference a named agent file (see [Agent files
93
93
  }
94
94
  ```
95
95
 
96
- Parallel — mixed toolsets, siblings can talk via mailbox:
96
+ Parallel — mixed toolsets, siblings can talk via mailbox (intercom is always on):
97
97
 
98
98
  ```json
99
99
  {
100
- "allowIntercom": true,
101
100
  "tasks": [
102
101
  { "agent": "researcher", "prompt": "You find facts. Cite paths.", "task": "Map the auth flow", "write": false },
103
102
  { "agent": "implementer", "prompt": "You make minimal changes.", "task": "Implement POST /api/upload", "write": true }
@@ -272,14 +271,13 @@ flowchart LR
272
271
 
273
272
  And the rule that keeps this from becoming ceremony: **zero `needs` anywhere = plain parallel.** No waves, no gates, no graph vocabulary imposed on flat work.
274
273
 
275
- Background (default) + intercom — the run returns a runId immediately; you stay steerable while it works:
274
+ Background (default) + intercom — the run returns a runId immediately; you stay steerable while it works. Children can always ask you questions and message each other:
276
275
 
277
276
  ```json
278
277
  {
279
278
  "agent": "auditor",
280
279
  "prompt": "You audit dependencies.",
281
- "task": "Audit package.json for outdated deps",
282
- "allowIntercom": true
280
+ "task": "Audit package.json for outdated deps"
283
281
  }
284
282
  ```
285
283
 
@@ -289,7 +287,7 @@ Background (default) + intercom — the run returns a runId immediately; you sta
289
287
 
290
288
  | Tool | Purpose |
291
289
  |---|---|
292
- | `subagent` | single / `tasks` (parallel or graph via `needs`) / `chain` (`{previous}`); every run is background — returns a runId, completion notifies you; `autoAwait:true` parks the call until the run finishes and returns the final result inline; `allowIntercom:true` enables child talk tools; `notifyPerTask` (default true) wakes you as each task completes |
290
+ | `subagent` | single / `tasks` (parallel or graph via `needs`) / `chain` (`{previous}`); every run is background — returns a runId, completion notifies you; `autoAwait:true` parks the call until the run finishes and returns the final result inline; children always carry talk tools (ask/notify/mailbox); `notifyPerTask` (default true) wakes you as each task completes |
293
291
  | `subagent_status` | live per-task snapshot (non-blocking), including each child's session file path |
294
292
  | `subagent_result` | full output of a run or one task |
295
293
  | `await_subagent` | block until a run finishes (optional `timeoutMs`) |
@@ -299,9 +297,9 @@ Background (default) + intercom — the run returns a runId immediately; you sta
299
297
 
300
298
  ### Per-task fields
301
299
 
302
- `agent` (name you invent — required), `task` (required), `prompt` (system prompt, optional — minimal default used), `write` (toolset, default read-only), plus optional `model` (`provider/model-id`), `thinking` (validated enum: `off|minimal|low|medium|high|xhigh|max`), `tools` (explicit allowlist), `cwd`, `maxRuntimeMs`, `id`, `needs` (dependency edges — see [Graph mode](#graph-mode--needs)). Top-level only: `autoAwait`, `notifyPerTask`, `allowIntercom`, `concurrency`.
300
+ `agent` (name you invent — required), `task` (required), `prompt` (system prompt, optional — minimal default used), `write` (toolset, default read-only), plus optional `model` (`provider/model-id`), `thinking` (validated enum: `off|minimal|low|medium|high|xhigh|max`), `tools` (explicit allowlist), `cwd`, `maxRuntimeMs`, `id`, `needs` (dependency edges — see [Graph mode](#graph-mode--needs)). Top-level only: `autoAwait`, `notifyPerTask`, `concurrency`.
303
301
 
304
- ### Child talk tools (when `allowIntercom: true`)
302
+ ### Child talk tools (always on)
305
303
 
306
304
  | Tool | Meaning |
307
305
  |---|---|
@@ -315,7 +313,7 @@ Background (default) + intercom — the run returns a runId immediately; you sta
315
313
  ## Commands
316
314
 
317
315
  - `/subagents` — list runs; `/subagents peek` (or `ctrl+shift+a`) — browsable pane
318
- - `/subagents auto-limit on|off` — toggle leader-imposed `maxRuntimeMs` caps (persists to `~/.pi/agent/subagents-config.json`; default on). `off` strips ALL task timeouts: tasks run unlimited until done, stalled, or aborted — only for runs where a hard bound is genuinely required is a cap kept (none, when off). Bare `/subagents auto-limit` shows the current state.
316
+ - `/subagents auto-limit on|off` — toggle leader-imposed `maxRuntimeMs` caps (persists to `~/.pi/agent/subagents-config.json`; default **off**). `on` gives tasks without an explicit `maxRuntimeMs` the 1 h default ceiling; `off` raises the ceiling to 6 h (still a ceiling — an unbounded child would pin the run forever). Bare `/subagents auto-limit` shows the current state.
319
317
 
320
318
  ## Peek — `/subagents peek` or `ctrl+shift+a`
321
319
 
@@ -356,7 +354,7 @@ The extension has no multiplexer integration and does not want one: it exposes t
356
354
 
357
355
  - Parent tools: 6 schemas with short descriptions. **No catalog, no context hook** — nothing injected per request.
358
356
  - Background completion: 3-line notice. Full text only via `subagent_result`.
359
- - Children: isolated sessions; talk tools injected only when `allowIntercom`; each child's prompt states its own task id and its siblings' so mailbox addressing works. Model resolution: explicit `provider/model-id` or bare id via the pi model registry → the parent's current model → settings default. Thinking levels validated against the resolved model's `thinkingLevelMap`.
357
+ - Children: isolated sessions; talk tools always injected; each child's prompt states its own task id and its siblings' so mailbox addressing works. Model resolution: explicit `provider/model-id` or bare id via the pi model registry → the parent's current model → settings default. Thinking levels validated against the resolved model's `thinkingLevelMap`.
360
358
 
361
359
  ## What this is built on
362
360
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arhen/pi-core-subagent",
3
- "version": "1.3.45",
3
+ "version": "1.3.47",
4
4
  "type": "module",
5
5
  "description": "pi extension: fast in-process subagents with a dependency-graph scheduler (needs edges gate tasks and carry upstream output into dependent prompts), plus background runs, intercom and agent-to-agent mailbox. Leader defines agents inline.",
6
6
  "license": "MIT",
package/src/child.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  /**
2
2
  * Child-session tools.
3
3
  *
4
- * When `allowIntercom: true`, children get four talk tools:
4
+ * Every child gets four talk tools:
5
5
  * - ask_parent blocking Q&A with the leader (parent)
6
6
  * - notify_parent one-way message to the leader
7
7
  * - send_agent_message one-way message to another subagent's mailbox
package/src/index.ts CHANGED
@@ -81,12 +81,12 @@ export default function (pi: ExtensionAPI) {
81
81
  if (value === "on" || value === "off") {
82
82
  const next = manager.setAutoLimit(value === "on");
83
83
  ctx.ui.notify(
84
- `auto-limit ${next ? "on" : "off"} — ${next ? "leader-imposed" : "no"} maxRuntimeMs caps apply to tasks (off = unlimited until done).`,
84
+ `auto-limit ${next ? "on" : "off"} — ${next ? "tasks without an explicit maxRuntimeMs get the 1 h default ceiling" : "raised 6 h ceiling applies (no 1 h cap)"}.`,
85
85
  "info",
86
86
  );
87
87
  } else {
88
88
  ctx.ui.notify(
89
- `auto-limit is ${manager.autoLimitOn ? "on" : "off"} — use \`/subagents auto-limit on|off\` (off = tasks run unlimited until done).`,
89
+ `auto-limit is ${manager.autoLimitOn ? "on (1 h default ceiling)" : "off (6 h ceiling)"} — use \`/subagents auto-limit on|off\`.`,
90
90
  "info",
91
91
  );
92
92
  }
@@ -157,7 +157,7 @@ export default function (pi: ExtensionAPI) {
157
157
  // ponytail: this string is billed on every request. No example block — an example
158
158
  // biases the model toward one shape; guidelines + JSON schema describe all of them.
159
159
  description:
160
- "Run isolated subagents (own context, own session). You invent each agent: name, optional system prompt, toolset (read-only default, write:true to edit). Use `agent`+`task` for one, `tasks` for many. `needs` declares dependency edges: a task waits for its needs and receives their outputs prepended to its prompt. If a user agent file in `.agents/agents`, `.claude/agents`, or `.pi/agents` (project dirs, then home) has a `description` matching the spawn goal (name + task), that file is authoritative: body = system prompt, frontmatter `model`/`tools` apply, inline prompt/model/tools ignored. No match → the inline definition stands. Write agents run in an isolated git worktree: on completion the result reports the branch + changed files — review, then merge with `git merge --no-ff <branch>` (merged branches are cleaned automatically). Every run is background: the call returns a runId immediately and completion notifies you — do NOT park waiting on it. If you have no other work, end your turn; the completion notice wakes you with the results. Set autoAwait:true only when the very next step in the SAME turn consumes the result. allowIntercom:true lets children talk to you and each other.",
160
+ "Run isolated subagents (own context, own session). You invent each agent: name, optional system prompt, toolset (read-only default, write:true to edit). Use `agent`+`task` for one, `tasks` for many. `needs` declares dependency edges: a task waits for its needs and receives their outputs prepended to its prompt. If a user agent file in `.agents/agents`, `.claude/agents`, or `.pi/agents` (project dirs, then home) has a `description` matching the spawn goal (name + task), that file is authoritative: body = system prompt, frontmatter `model`/`tools` apply, inline prompt/model/tools ignored. No match → the inline definition stands. Write agents run in an isolated git worktree: on completion the result reports the branch + changed files — review, then merge with `git merge --no-ff <branch>` (merged branches are cleaned automatically). Every run is background: the call returns a runId immediately and completion notifies you — do NOT park waiting on it. If you have no other work, end your turn; the completion notice wakes you with the results. Set autoAwait:true only when the very next step in the SAME turn consumes the result. Children always carry talk tools: they can ask you questions, notify you, and message siblings.",
161
161
  promptSnippet: "Define and delegate work to specialized subagents.",
162
162
  promptGuidelines: [
163
163
  "Use subagent when independent review, testing, research, or parallel analysis improves quality.",
@@ -172,7 +172,6 @@ export default function (pi: ExtensionAPI) {
172
172
  "Never block with nothing to do: if you have no work left after spawning, end your turn. Task completion notifies you and wakes a fresh turn with the results — await_subagent/autoAwait in that situation only burns time and tokens.",
173
173
  "autoAwait:true only when the same turn must consume the result immediately (e.g. you spawn a reviewer and then must act on its verdict before replying). Otherwise spawn background and read results from the completion notice, or subagent_result when you come back.",
174
174
  "await_subagent is for the rare case where you have parallel work of your own and need to sync at a specific point — not the default follow-up to a spawn.",
175
- "allowIntercom:true only when a child may need to ask you something.",
176
175
  ],
177
176
  parameters: SubagentParams,
178
177
  executionMode: "parallel", // sibling subagent calls run concurrently, not serialized
@@ -237,7 +236,7 @@ export default function (pi: ExtensionAPI) {
237
236
  : args.agent
238
237
  ? `single ${args.agent}`
239
238
  : "preparing…";
240
- const flags = [args.autoAwait ? "await" : "bg", args.allowIntercom ? "a2a" : ""].filter(Boolean).join(" · ");
239
+ const flags = args.autoAwait ? "await" : "bg";
241
240
  // Params used, dimmed: model, thinking, toolset, per-task write count.
242
241
  const tasks = args.tasks ?? args.chain ?? [];
243
242
  const writeCount = tasks.filter((t) => t.write).length;
@@ -269,7 +268,7 @@ export default function (pi: ExtensionAPI) {
269
268
  })
270
269
  .join("");
271
270
  return new Text(
272
- `${theme.fg("toolTitle", theme.bold("subagent"))} ${theme.fg("accent", mode)}${flags ? ` ${theme.fg("muted", `[${flags}]`)}` : ""}${params}${graphLine}${plan}`,
271
+ `${theme.fg("toolTitle", theme.bold("subagent"))} ${theme.fg("accent", mode)} ${theme.fg("muted", `[${flags}]`)}${params}${graphLine}${plan}`,
273
272
  0,
274
273
  0,
275
274
  );
package/src/manager.ts CHANGED
@@ -321,8 +321,8 @@ export class SubagentManager {
321
321
  /** Set by clearRuns — blocks late persists from erasing the sidecar. */
322
322
  private cleared = false;
323
323
 
324
- /** When false, strip leader-imposed maxRuntimeMs so tasks run unlimited — toggle via `/subagents auto-limit on|off`. */
325
- private autoLimit = true;
324
+ /** When true, tasks without an explicit maxRuntimeMs get the 1 h default ceiling; when false (default) the raised 6 h ceiling applies — toggle via `/subagents auto-limit on|off`. */
325
+ private autoLimit = false;
326
326
 
327
327
  turnActivity = false;
328
328
 
@@ -850,7 +850,7 @@ export class SubagentManager {
850
850
  const allowedTools = input.write ? WRITE_TOOLS : READONLY_TOOLS;
851
851
  const fileTools = file?.tools?.filter((t) => allowedTools.includes(t));
852
852
  const baseTools = fileTools?.length ? fileTools : (input.tools ?? allowedTools);
853
- const tools = [...baseTools, ...(run.allowIntercom ? CHILD_TALK_TOOLS : [])];
853
+ const tools = [...baseTools, ...CHILD_TALK_TOOLS];
854
854
  // Isolation follows the DELIVERED toolset, never the raw request: explicit
855
855
  // tools: [bash] without write:true still gets a worktree, and a file that
856
856
  // narrowed the child to read-only never gets the commit/merge ceremony.
@@ -990,9 +990,7 @@ export class SubagentManager {
990
990
  const worktreeNote = wt
991
991
  ? ` You work in an isolated git worktree (branch ${wt.branch})${task.stackedOn ? `, stacked on ${task.stackedOn} (its changes are already in your tree)` : ""}. Never run git commands that switch branches, create branches, or move the worktree (git switch/checkout/branch/worktree). The extension commits your changes when you finish. git status/diff are fine for inspecting your own changes. node_modules is a SHARED symlink to the main checkout: never install, upgrade, or delete dependencies (no npm/bun/yarn/pnpm install, no \`rm -rf node_modules\`) — those writes escape your worktree and damage the user's project. If the task truly needs a dependency change, edit the manifest only and say so in your answer.`
992
992
  : "";
993
- const subagentInstruction = run.allowIntercom
994
- ? `You are running as a subagent. Your bash tool already executes in the project working directory — never prefix commands with \`cd\`. Do not call subagent/delegation tools unless the parent explicitly asks. Return a concise final answer. You MAY use ask_parent only when truly blocked on information only the parent has; notify_parent for one-way updates; send_agent_message/poll_agent_messages to coordinate with siblings. Your mailbox address and siblings: ${task.roster ?? "(none)"}. Use the exact task ids (e.g. task_2) as send_agent_message targets. Siblings run independently and may start late or finish early — never block indefinitely on their replies: poll at most 5 times, then proceed with your best judgment. A gated sibling (marked ↳ waits in the graph) may not be running yet; do not wait for it. Stalled waits get the whole run killed. When your work is done, call notify_parent ONCE with a concise result summary — key findings, verdicts, file:line evidence — so the leader can start consuming your output before the run finishes.${worktreeNote}`
995
- : `You are running as a subagent. Your bash tool already executes in the project working directory — never prefix commands with \`cd\`. Do not call subagent/delegation tools unless the parent explicitly asks. Return a concise final answer for the parent agent.${worktreeNote}`;
993
+ const subagentInstruction = `You are running as a subagent. Your bash tool already executes in the project working directory — never prefix commands with \`cd\`. Do not call subagent/delegation tools unless the parent explicitly asks. Return a concise final answer. You MAY use ask_parent only when truly blocked on information only the parent has; notify_parent for one-way updates; send_agent_message/poll_agent_messages to coordinate with siblings. Your mailbox address and siblings: ${task.roster ?? "(none)"}. Use the exact task ids (e.g. task_2) as send_agent_message targets. Siblings run independently and may start late or finish early — never block indefinitely on their replies: poll at most 5 times, then proceed with your best judgment. A gated sibling (marked ↳ waits in the graph) may not be running yet; do not wait for it. Stalled waits get the whole run killed. When your work is done, call notify_parent ONCE with a concise result summary — key findings, verdicts, file:line evidence — so the leader can start consuming your output before the run finishes.${worktreeNote}`;
996
994
 
997
995
  const loader = new DefaultResourceLoader({
998
996
  cwd: childCwd,
@@ -1005,9 +1003,7 @@ export class SubagentManager {
1005
1003
  });
1006
1004
  await loader.reload();
1007
1005
 
1008
- const customTools: ToolDefinition[] = run.allowIntercom
1009
- ? createChildTools(task.id, this.makeChildHandlers(run, task, ctx))
1010
- : [];
1006
+ const customTools: ToolDefinition[] = createChildTools(task.id, this.makeChildHandlers(run, task, ctx));
1011
1007
 
1012
1008
  const created = await createAgentSession({
1013
1009
  cwd: childCwd,
@@ -1324,7 +1320,6 @@ export class SubagentManager {
1324
1320
  id: newId("run"),
1325
1321
  mode,
1326
1322
  status: "queued",
1327
- allowIntercom: Boolean(params.allowIntercom),
1328
1323
  notifyPerTask: params.notifyPerTask ?? true,
1329
1324
  createdAt: Date.now(),
1330
1325
  concurrency: Math.max(1, Math.min(params.concurrency ?? DEFAULT_CONCURRENCY, MAX_CONCURRENCY)),
package/src/peek.ts CHANGED
@@ -116,7 +116,8 @@ function eventLines(raw: string): PeekLine[] {
116
116
  return out;
117
117
  }
118
118
 
119
- function tailLines(path: string, max: number): PeekLine[] {
119
+ /** The whole readable tail, unsliced — the viewport decides what is shown. */
120
+ function tailLines(path: string): PeekLine[] {
120
121
  let text: string;
121
122
  try {
122
123
  text = readTail(path);
@@ -126,7 +127,7 @@ function tailLines(path: string, max: number): PeekLine[] {
126
127
  const lines: PeekLine[] = [];
127
128
  // First line of a mid-file read is usually a fragment — drop it.
128
129
  for (const raw of text.split("\n").slice(1)) lines.push(...eventLines(raw));
129
- return lines.slice(-max);
130
+ return lines;
130
131
  }
131
132
 
132
133
  /** Gutter colour carries the line's role, so the eye can skip to failures without
@@ -172,9 +173,32 @@ export function createPeekPane(
172
173
  let selected = 0;
173
174
  let tailing = false;
174
175
  let confirming = false;
176
+ /** Rows scrolled back from the newest line. 0 = pinned to live output.
177
+ * A tail that keeps jumping to the bottom while you read is unusable, so any
178
+ * scroll detaches from live and the 700ms poll stops moving the view. */
179
+ let scrollback = 0;
180
+ /** Content rows the last render could show — page keys need the real viewport,
181
+ * which only render() knows (it is given the width and derives the height). */
182
+ let viewport = TAIL_ROWS;
183
+ /** Tail length at the previous render, to keep a scrolled-back view pinned to
184
+ * the SAME lines as the child appends new ones. Without this the window is
185
+ * anchored to the end of a growing list, so the text you are reading crawls
186
+ * upward every poll. */
187
+ let lastTotal = 0;
175
188
  const timer = setInterval(requestRender, POLL_MS);
176
189
 
177
190
  const clamp = (n: number, len: number) => (len === 0 ? 0 : Math.max(0, Math.min(len - 1, n)));
191
+ /** Scrolling past either end is a no-op, never a wrap: the newest line is a
192
+ * hard floor, the oldest readable line a hard ceiling. */
193
+ const scrollBy = (rows: number) => {
194
+ const task = getTasks()[selected];
195
+ if (!tailing || !task?.sessionFile) return;
196
+ const total = tailLines(task.sessionFile).length;
197
+ // Anchor here as well as in render(): a scroll before the next render would
198
+ // otherwise be read as "the file grew by its whole length" and jump the view.
199
+ lastTotal = total;
200
+ scrollback = Math.max(0, Math.min(Math.max(0, total - viewport), scrollback + rows));
201
+ };
178
202
 
179
203
  /**
180
204
  * Every row is a full-width bordered line: │ edge, one space of padding, the
@@ -208,7 +232,12 @@ export function createPeekPane(
208
232
  const task = tasks[selected]!;
209
233
  const hint = confirming
210
234
  ? theme.fg("error", `abort ${task.agent}? y / n`)
211
- : theme.fg("dim", tailing ? "esc back · x abort" : "shift+↑↓ / jk move · enter tail · x abort · esc close");
235
+ : theme.fg(
236
+ "dim",
237
+ tailing
238
+ ? "↑↓ / jk scroll · ⇧ page · g/G top·live · esc back · x abort"
239
+ : "shift+↑↓ / jk move · enter tail · x abort · esc close",
240
+ );
212
241
  // Breadcrumb, not a bare name: while tailing, WHERE you are is the thing you
213
242
  // lose track of first, and the child's status belongs next to its name.
214
243
  const crumb = tailing
@@ -226,9 +255,23 @@ export function createPeekPane(
226
255
  lines.push(row(theme.fg("dim", "(no session file — agent has not started yet)"), width));
227
256
  } else {
228
257
  // ponytail: re-reads the tail each render (700ms poll). A watcher only pays off for files far bigger than a child session.
229
- const tail = tailLines(task.sessionFile, TAIL_ROWS);
230
- if (tail.length === 0) lines.push(row(theme.fg("dim", "(no activity yet)"), width));
231
- for (const line of tail) lines.push(row(renderLine(line, theme), width));
258
+ const tail = tailLines(task.sessionFile);
259
+ viewport = TAIL_ROWS;
260
+ if (scrollback > 0 && lastTotal > 0 && tail.length > lastTotal) scrollback += tail.length - lastTotal;
261
+ lastTotal = tail.length;
262
+ // Clamp here too: the readable tail is a sliding 64KB window, so old lines
263
+ // fall off the front and a deep scrollback would render a blank window.
264
+ scrollback = Math.max(0, Math.min(Math.max(0, tail.length - viewport), scrollback));
265
+ const end = tail.length - scrollback;
266
+ const window = tail.slice(Math.max(0, end - viewport), end);
267
+ if (window.length === 0) lines.push(row(theme.fg("dim", "(no activity yet)"), width));
268
+ for (const line of window) lines.push(row(renderLine(line, theme), width));
269
+ // Scrolled-back views must say so: an unmoving tail is otherwise
270
+ // indistinguishable from a stalled child.
271
+ if (scrollback > 0) {
272
+ lines.push(edgeRow(width, "├", "┤"));
273
+ lines.push(row(theme.fg("warning", `↑ ${scrollback} older · G / end → live`), width));
274
+ }
232
275
  }
233
276
  lines.push(edgeRow(width, "╰", "╯"));
234
277
  return lines;
@@ -249,17 +292,32 @@ export function createPeekPane(
249
292
  if (data === "x" || data === "X") {
250
293
  if (tasks[selected]?.running) confirming = true;
251
294
  } else if (matchesKey(data, Key.escape)) {
252
- if (tailing) tailing = false;
253
- else close();
295
+ if (tailing) {
296
+ tailing = false;
297
+ scrollback = 0; // leaving the tail forgets where you were reading
298
+ } else close();
254
299
  } else if (matchesKey(data, Key.enter) || matchesKey(data, Key.right)) {
255
300
  tailing = true;
301
+ scrollback = 0; // a freshly opened tail always starts live
256
302
  } else if (matchesKey(data, Key.left)) {
257
303
  tailing = false;
304
+ scrollback = 0;
305
+ } else if (matchesKey(data, Key.pageUp)) {
306
+ scrollBy(viewport);
307
+ } else if (matchesKey(data, Key.pageDown)) {
308
+ scrollBy(-viewport);
309
+ } else if (data === "g") {
310
+ scrollBy(Number.MAX_SAFE_INTEGER); // oldest readable line
311
+ } else if (data === "G" || matchesKey(data, Key.end)) {
312
+ scrollback = 0; // back to live
258
313
  } else if (matchesKey(data, "shift+up") || matchesKey(data, Key.up) || data === "k") {
314
+ // In the tail the same keys scroll; in the list they move between agents.
259
315
  // shift+↑↓ and j/k are the reliable pair: bare arrows can be eaten by prompt history.
260
- selected = clamp(selected - 1, len);
316
+ if (tailing) scrollBy(1);
317
+ else selected = clamp(selected - 1, len);
261
318
  } else if (matchesKey(data, "shift+down") || matchesKey(data, Key.down) || data === "j") {
262
- selected = clamp(selected + 1, len);
319
+ if (tailing) scrollBy(-1);
320
+ else selected = clamp(selected + 1, len);
263
321
  }
264
322
  requestRender();
265
323
  },
package/src/schemas.ts CHANGED
@@ -67,9 +67,6 @@ export const SubagentParams = Type.Object({
67
67
  default: true,
68
68
  }),
69
69
  ),
70
- allowIntercom: Type.Optional(
71
- Type.Boolean({ description: "Let children ask you questions, notify you, and message sibling subagents" }),
72
- ),
73
70
  });
74
71
 
75
72
  /** Derived from the schemas — single source of truth, no hand-maintained mirror. */
package/src/types.ts CHANGED
@@ -68,7 +68,6 @@ export interface RunSnapshot {
68
68
  id: string;
69
69
  mode: RunMode;
70
70
  status: RunStatus;
71
- allowIntercom: boolean;
72
71
  notifyPerTask: boolean;
73
72
  createdAt: number;
74
73
  startedAt?: number;