@narumitw/pi-subagents 3.1.1 → 3.2.1

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
@@ -62,6 +62,12 @@ Call `subagent_spawn` with a self-contained task and only the work tools that ta
62
62
  The call returns a `jobId` immediately, and the job continues in the background.
63
63
  Continue useful main-agent work until the result is required or a completion arrives.
64
64
 
65
+ In TUI mode, each active-job row shows a preview of its existing `task` after the job ID; no new parameter is required.
66
+ The preview removes terminal controls, collapses whitespace, and uses at most 40 terminal columns, including an ellipsis when shortened.
67
+ It describes the assigned task, not live child activity; the full task sent to the child is unchanged.
68
+ Task text is visible in this local widget, but `subagent_inspect` continues to omit it.
69
+ Rows are clipped to the available terminal width, so trailing state, timing, or tools can be hidden on narrow terminals.
70
+
65
71
  Use messaging only when needed:
66
72
 
67
73
  - Call `subagent_send` with `recipient: jobId` to ask an active child a question.
@@ -125,7 +131,7 @@ The package intentionally registers and publishes no skill; the repository-only
125
131
  ```ts
126
132
  {
127
133
  task: string;
128
- tools?: Array<"read" | "bash" | "powershell" | "edit" | "write" | "grep" | "find" | "ls">;
134
+ tools?: Array<"read" | "bash" | "powershell" | "edit" | "write" | "grep" | "find" | "ls" | "codemode">;
129
135
  skills?: string[];
130
136
  extensions?: Array<{ path: string; tools: string[] }>;
131
137
  thinkingLevel?: "off" | "minimal" | "low" | "medium" | "high" | "xhigh" | "max";
@@ -133,11 +139,17 @@ The package intentionally registers and publishes no skill; the repository-only
133
139
  }
134
140
  ```
135
141
 
136
- The optional `tools` list selects Pi core work tools.
142
+ The optional `tools` list selects Pi core work tools and the built-in `codemode` tool.
137
143
  Omitting it selects `read`, `grep`, `find`, and `ls`, while an empty list gives the child no core work tools.
138
144
  The runtime always adds `subagent_send` and child `subagent_wait` and removes duplicate names.
139
145
  Adding `edit` or `write` lets the child modify files, while `bash` or `powershell` grants unrestricted command execution.
140
146
 
147
+ Add `"codemode"` explicitly to let the child run JavaScript that calls its selected tools and communication tools, including parallel calls and output filtering.
148
+ For example, `tools: ["read", "grep", "codemode"]` enables scripted read and search operations without shell or file-mutation tools.
149
+ The runtime loads Pi's built-in codemode extension and verifies tool readiness before sending the task; it does not enable automatic extension or MCP discovery.
150
+ Codemode follows Pi's effective `codemode` settings and also provides Pi's `models` API for classifier and image calls using child-visible credentials.
151
+ Those model calls can send data to providers and incur costs.
152
+
141
153
  The optional `skills` list attaches local Pi skills through progressive disclosure without injecting their full bodies or adding tools.
142
154
  Every attached skill path must contain at least one loadable skill, every non-ignored declared skill must load successfully, and skill names must be unique across attachments.
143
155
 
package/dist/index.ts CHANGED
@@ -156,6 +156,7 @@ function buildPiArgs(request) {
156
156
  "-e",
157
157
  childCommunicationBridgePath()
158
158
  ];
159
+ if (request.tools.includes("codemode")) args.push("-e", "builtin:codemode");
159
160
  for (const extension of request.extensions) args.push("-e", extension.path);
160
161
  if (requiresReadinessAttestation(request)) args.push("-e", childReadinessProbePath());
161
162
  for (const skill of request.skills) args.push("--skill", skill);
@@ -202,7 +203,7 @@ function childReadinessProbePath() {
202
203
  return fileURLToPath(new URL("./child-readiness-probe.ts", import.meta.url));
203
204
  }
204
205
  function requiresReadinessAttestation(request) {
205
- return request.extensions.length > 0;
206
+ return request.tools.includes("codemode") || request.extensions.length > 0;
206
207
  }
207
208
  function selectedChildTools(request) {
208
209
  return [
@@ -1200,7 +1201,17 @@ var TERMINAL_JOB_STATES = /* @__PURE__ */ new Set([
1200
1201
  "timed_out",
1201
1202
  "cancelled"
1202
1203
  ]);
1203
- var CHILD_CORE_TOOL_NAMES = ["read", "bash", "powershell", "edit", "write", "grep", "find", "ls"];
1204
+ var CHILD_CORE_TOOL_NAMES = [
1205
+ "read",
1206
+ "bash",
1207
+ "powershell",
1208
+ "edit",
1209
+ "write",
1210
+ "grep",
1211
+ "find",
1212
+ "ls",
1213
+ "codemode"
1214
+ ];
1204
1215
  var DEFAULT_SUBAGENT_TOOLS = ["read", "grep", "find", "ls"];
1205
1216
  var SUBAGENT_THINKING_LEVELS = [
1206
1217
  "off",
@@ -1762,6 +1773,7 @@ var SubagentRuntime = class {
1762
1773
  const now = this.now();
1763
1774
  return [...this.jobs.values()].filter((job) => !isTerminal(job.state)).sort((left, right) => left.createdAt - right.createdAt).map((job) => ({
1764
1775
  jobId: job.jobId,
1776
+ task: job.taskText,
1765
1777
  state: job.state,
1766
1778
  elapsedMs: Math.max(0, now - (job.startedAt ?? job.createdAt)),
1767
1779
  ...job.timeout !== void 0 ? { timeout: job.timeout } : {},
@@ -1803,6 +1815,7 @@ var SubagentRuntime = class {
1803
1815
  ...input.timeout !== void 0 ? { timeout: input.timeout } : {},
1804
1816
  controller,
1805
1817
  tools: effectiveTools,
1818
+ taskText: input.task,
1806
1819
  terminal,
1807
1820
  resolveTerminal,
1808
1821
  controlReady,
@@ -2282,7 +2295,7 @@ function registerSubagentTools(pi, dependencies = {}) {
2282
2295
  if (ctx.cwd !== cwd || ctx.isProjectTrusted() !== projectTrusted) {
2283
2296
  throw new Error("Subagent spawn context changed during attachment validation; retry the request.");
2284
2297
  }
2285
- if (attachments.extensions.length > 0) {
2298
+ if (tools.includes("codemode") || attachments.extensions.length > 0) {
2286
2299
  assertChildBootstrapCapacity([.../* @__PURE__ */ new Set([...attachments.effectiveTools, ...CHILD_COMMUNICATION_TOOL_NAMES])]);
2287
2300
  }
2288
2301
  const model = resolveChildModel(ctx, attachments.extensions.length > 0);
@@ -2518,10 +2531,11 @@ function toolResult(value) {
2518
2531
  }
2519
2532
 
2520
2533
  // src/widget.ts
2521
- import { truncateToWidth } from "@earendil-works/pi-tui";
2534
+ import { stripTerminalSequences, truncateToWidth, visibleWidth } from "@earendil-works/pi-tui";
2522
2535
  var SUBAGENT_WIDGET_KEY = "subagents";
2523
2536
  var SUBAGENT_WIDGET_REFRESH_INTERVAL_MS = 1e3;
2524
2537
  var WIDGET_OPTIONS = { placement: "aboveEditor" };
2538
+ var TASK_PREVIEW_WIDTH = 40;
2525
2539
  function createSubagentWidgetController(runtime) {
2526
2540
  let activeSession;
2527
2541
  let refreshTimer;
@@ -2590,19 +2604,30 @@ function renderJob(job, theme) {
2590
2604
  const symbol = theme.fg(running ? "accent" : "dim", running ? "\u25B6 " : "\u25CB ");
2591
2605
  const state = theme.fg(running ? "accent" : "muted", job.state);
2592
2606
  const jobId = sanitizeLabel(job.jobId);
2607
+ const taskText = sanitizeTask(job.task);
2608
+ const task = stripTerminalSequences(truncateToWidth(taskText, TASK_PREVIEW_WIDTH, "\u2026"));
2609
+ const preview = visibleWidth(task) > 0 ? ` \xB7 ${theme.fg("text", task)}` : "";
2593
2610
  const tools = job.tools.length > 0 ? job.tools.map(sanitizeLabel).join(", ") : "none";
2594
2611
  const timeout = job.timeout === void 0 ? "no timeout" : formatSeconds(job.timeout);
2595
2612
  const detail = ` \xB7 ${formatSeconds(Math.floor(job.elapsedMs / 1e3))} / ${timeout} \xB7 tools: ${tools}`;
2596
- return `${symbol}${theme.fg("text", jobId)} \xB7 ${state}${theme.fg("muted", detail)}`;
2613
+ return `${symbol}${theme.fg("text", jobId)}${preview} \xB7 ${state}${theme.fg("muted", detail)}`;
2597
2614
  }
2598
2615
  function widgetValue(jobs) {
2599
2616
  return jobs.map(
2600
- (job) => `${job.jobId}\0${job.state}\0${Math.floor(job.elapsedMs / 1e3)}\0${job.timeout ?? ""}\0${job.tools.join(",")}`
2617
+ (job) => `${job.jobId}\0${job.task}\0${job.state}\0${Math.floor(job.elapsedMs / 1e3)}\0${job.timeout ?? ""}\0${job.tools.join(",")}`
2601
2618
  ).join("\n");
2602
2619
  }
2603
2620
  function cloneDisplayJob(job) {
2604
2621
  return { ...job, tools: [...job.tools] };
2605
2622
  }
2623
+ function sanitizeTask(value) {
2624
+ const text = value.replace(
2625
+ // biome-ignore lint/suspicious/noControlCharactersInRegex: Remove untrusted terminal escape sequences before display.
2626
+ /(?:\u001b\[|\u009b)[0-?]*[ -/]*[@-~]|(?:\u001b[\]PX^_]|[\u0090\u0098\u009d-\u009f])[^\u0007\u001b\u0080-\u009f]*(?:\u0007|\u001b\\|\u009c)|\u001b[ -/]*[0-~]/gu,
2627
+ ""
2628
+ );
2629
+ return sanitizeLabel(text.replace(/\p{Bidi_Control}/gu, ""));
2630
+ }
2606
2631
  function sanitizeLabel(value) {
2607
2632
  return sanitizeTerminalText(value).replace(/\s+/gu, " ").trim();
2608
2633
  }