@pi-ohm/subagents 0.6.4-dev.22132543465.1.e4e3071 → 0.6.4-dev.22169815567.1.cdde4e8

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
@@ -27,7 +27,8 @@ from profile IDs with deterministic collision handling.
27
27
  - direct-tool execution and task-routed execution share the same runtime/result envelope
28
28
 
29
29
  The orchestration tool name is **`task`**. Async orchestration lifecycle
30
- operations (`start/status/wait/send/cancel`) are exposed through this tool.
30
+ operations (`start/status/wait/send/cancel`) are exposed through this tool, but
31
+ default execution is sync (`async:false`). Use `async:true` only for background/long tasks.
31
32
 
32
33
  ## Task tool (current)
33
34
 
@@ -36,8 +37,38 @@ Current behavior:
36
37
  - supports `op: "start"` for a single task payload (sync + `async:true`)
37
38
  - supports batched `op: "start"` payloads via `tasks[]` with optional `parallel:true`
38
39
  - supports lifecycle operations: `status`, `wait`, `send`, `cancel`
39
- - compatibility aliases: `status`/`wait` accept `id` or `ids`; `op:"result"` is normalized to `status`
40
+ - input normalization: `status`/`wait` accept `id` or `ids`; `op:"result"` is normalized to `status`
41
+ - non-debug result text renders Amp-style inline message trees (prompt -> tool calls -> result)
42
+ - running background updates use minimal inline progress lines
40
43
  - returns `task_id`, status, and deterministic task details
44
+ - includes explicit wait/cancel ergonomics fields:
45
+ - `wait_status` (`completed|timeout|aborted`)
46
+ - `done` (boolean completion flag for `wait`)
47
+ - `cancel_applied`
48
+ - `prior_status`
49
+ - includes batch acceptance accounting for `start` with `tasks[]`:
50
+ - `total_count`
51
+ - `accepted_count`
52
+ - `rejected_count`
53
+ - `batch_status` (`accepted|partial|completed|rejected`)
54
+ - includes structured output metadata in details/items:
55
+ - `output_available`
56
+ - `output_truncated`
57
+ - `output_total_chars`
58
+ - `output_returned_chars`
59
+ - includes structured SDK-derived tool transcript rows in details/items when available:
60
+ - `tool_rows` (deterministic per-tool lifecycle rows)
61
+ - `event_count` (captured structured event count)
62
+ - `assistant_text` (event-derived assistant transcript tail)
63
+ - includes machine marker on every tool details payload:
64
+ - `contract_version: "task.v1"`
65
+ - includes observability fields on details/items:
66
+ - `provider`
67
+ - `model`
68
+ - `runtime`
69
+ - `route`
70
+ - collection lifecycle ops (`status`/`wait`) aggregate observability from task items
71
+ (so top-level `runtime`/`route` align with per-item metadata when present)
41
72
  - persists task registry snapshots to disk for resume/reload behavior
42
73
  - enforces terminal-task retention expiry with explicit `task_expired` lookup errors
43
74
  - validates all payloads with TypeBox boundary schema + typed Result errors
@@ -48,11 +79,34 @@ Runtime backend is selected from `subagentBackend` config:
48
79
 
49
80
  - `interactive-shell` (default): executes a real nested `pi` run for subagent prompts
50
81
  using built-in tools (`read,bash,edit,write,grep,find,ls`)
82
+ - `interactive-sdk` (opt-in): executes subagent prompts through in-process Pi SDK
83
+ sessions with in-memory session/settings managers
51
84
  - `none`: uses deterministic scaffold backend (echo-style debug output)
52
85
  - `custom-plugin`: currently returns `unsupported_subagent_backend`
53
86
 
87
+ Optional safety fallback:
88
+
89
+ - set `OHM_SUBAGENTS_SDK_FALLBACK_TO_CLI=true` to fallback from `interactive-sdk` to
90
+ `interactive-shell` when SDK bootstrap/execution fails (`task_backend_execution_failed`)
91
+
54
92
  If output appears like prompt regurgitation, verify `subagentBackend` is not set to `none`.
55
93
 
94
+ Nested interactive-shell outputs are sanitized to strip runtime metadata lines (`backend:`,
95
+ `provider:`, `model:`) before surfacing task output.
96
+
97
+ For unknown tasks/expired tasks, error categorization is explicit: `error_category: "not_found"`.
98
+
99
+ ### Output truncation policy
100
+
101
+ Task output returned in tool payloads is capped to prevent oversized context injection.
102
+
103
+ - env override: `OHM_SUBAGENTS_OUTPUT_MAX_CHARS`
104
+ - default cap: `8000` chars
105
+ - when truncation occurs, payloads include:
106
+ - `output_truncated: true`
107
+ - `output_total_chars`
108
+ - `output_returned_chars`
109
+
56
110
  Example payload:
57
111
 
58
112
  ```jsonc
@@ -69,6 +123,8 @@ Batch execution notes:
69
123
  - aggregate item order is deterministic (input order)
70
124
  - bounded parallelism is enforced by `subagents.taskMaxConcurrency` (default `3`)
71
125
  - task failures are isolated; one failed batch item does not abort siblings
126
+ - async mixed-validity batch starts no longer collapse to top-level failure when tasks were accepted;
127
+ use acceptance counters + `batch_status` to decide polling behavior
72
128
 
73
129
  ## Task permission policy
74
130
 
@@ -121,38 +177,68 @@ Existing slash commands remain unchanged:
121
177
  - `/ohm-subagents`
122
178
  - `/ohm-subagent <id>`
123
179
 
180
+ ## Invocation mode behavior
181
+
182
+ `task-routed` and `primary-tool` invocation paths share one runtime/result envelope.
183
+
184
+ Current shared fields:
185
+
186
+ - `contract_version`
187
+ - `status`
188
+ - `subagent_type`
189
+ - `description`
190
+ - `backend`
191
+ - `provider`
192
+ - `model`
193
+ - `runtime`
194
+ - `route`
195
+ - `output_available`
196
+ - `output` (subject to truncation policy)
197
+
198
+ Invocation mode differences are intentional and explicit via `invocation`:
199
+
200
+ - `task-routed` for non-primary profiles
201
+ - `primary-tool` for direct primary profile calls
202
+
124
203
  ## Live TUI feedback
125
204
 
126
- `@pi-ohm/subagents` uses `@mariozechner/pi-tui` for task runtime visuals.
205
+ `@pi-ohm/subagents` uses shared component `@pi-ohm/tui` (`SubagentTaskTreeComponent`) for task runtime visuals.
206
+ Live bottom widget mode now defaults to `off`; inline tool-result updates are the primary UX.
127
207
 
128
208
  Baseline running-task display includes:
129
209
 
130
210
  - spinner
131
- - task description (from original `task` start payload)
132
- - in-flight tool call count
133
- - elapsed time (`mm:ss`)
211
+ - prompt line from task start payload (main-agent instruction)
212
+ - best-effort parsed tool-call rows
213
+ - terminal/final result row
134
214
 
135
215
  Runtime UI surfaces are synchronized from one task snapshot model:
136
216
 
137
217
  - footer status (`setStatus`) with running/active counters
138
- - widget task list (`setWidget`) using two-line per-task renderer
218
+ - widget task tree (`setWidget`) using Amp-style tree component
139
219
  - headless fallback `onUpdate` text with equivalent description/tool-count/elapsed info
140
220
 
141
221
  Example running block:
142
222
 
143
223
  ```bash
144
- [finder] Auth flow scan
145
- Tools 3/3 · Elapsed 00:18
224
+ Finder · Auth flow scan
225
+ ├── Trace auth validation
226
+ ├── ✓ Read packages/subagents/src
227
+ ├── ✓ Grep auth|token in packages/subagents/src
228
+ ╰── Working...
146
229
  ```
147
230
 
148
231
  Terminal examples:
149
232
 
150
233
  ```bash
151
- [finder] Auth flow scan
152
- Tools 5/5 · Elapsed 00:42
153
-
154
- [finder] Auth flow scan
155
- Tools 2/3 · Elapsed 00:11
234
+ Finder · Auth flow scan
235
+ ├── Trace auth validation
236
+ ├── ✓ Read packages/subagents/src
237
+ ╰── Auth validation path uses task permission policy + runtime store transitions.
238
+
239
+ ✕ Finder · Auth flow scan
240
+ ├── Trace auth validation
241
+ ╰── Task failed: backend timeout while reading repository files.
156
242
  ```
157
243
 
158
244
  ## Error handling
@@ -167,3 +253,23 @@ Commands:
167
253
 
168
254
  - `/ohm-subagents`
169
255
  - `/ohm-subagent <id>`
256
+
257
+ ## Primary tool input schemas
258
+
259
+ For profiles marked `primary:true`, direct tool input schema is subagent-specific:
260
+
261
+ - `librarian`
262
+ - required: `query`
263
+ - optional: `context`, `async`, `description`
264
+ - `oracle`
265
+ - required: `task`
266
+ - optional: `context`, `files[]`, `async`, `description`
267
+ - `finder`
268
+ - required: `query`
269
+ - optional: `async`, `description`
270
+
271
+ Normalization behavior:
272
+
273
+ - `context` is forwarded in a dedicated prompt section (`Context:`)
274
+ - oracle `files[]` is forwarded in a dedicated prompt block (`Files:` + bullet paths)
275
+ - task lifecycle/result payload remains the same shape after primary normalization
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pi-ohm/subagents",
3
- "version": "0.6.4-dev.22132543465.1.e4e3071",
3
+ "version": "0.6.4-dev.22169815567.1.cdde4e8",
4
4
  "homepage": "https://github.com/pi-ohm/pi-ohm/tree/dev/packages/subagents#readme",
5
5
  "repository": {
6
6
  "type": "git",
@@ -20,7 +20,8 @@
20
20
  },
21
21
  "dependencies": {
22
22
  "@mariozechner/pi-coding-agent": "catalog:pi",
23
- "@pi-ohm/config": "0.6.4-dev.22132543465.1.e4e3071",
23
+ "@pi-ohm/config": "0.6.4-dev.22169815567.1.cdde4e8",
24
+ "@pi-ohm/tui": "0.6.4-dev.22169815567.1.cdde4e8",
24
25
  "better-result": "catalog:",
25
26
  "zod": "catalog:"
26
27
  },
@@ -2,8 +2,10 @@ import assert from "node:assert/strict";
2
2
  import test from "node:test";
3
3
  import type { OhmRuntimeConfig } from "@pi-ohm/config";
4
4
  import { getSubagentById } from "./catalog";
5
+ import { getTaskLiveUiMode, setTaskLiveUiMode } from "./runtime/live-ui";
5
6
  import {
6
7
  buildSubagentDetailText,
8
+ resolveSubagentsLiveUiModeCommand,
7
9
  buildSubagentsOverviewText,
8
10
  getSubagentInvocationMode,
9
11
  normalizeCommandArgs,
@@ -135,3 +137,23 @@ defineTest("buildSubagentDetailText preserves detailed subagent view", () => {
135
137
  assert.match(text, /When to use:/);
136
138
  assert.match(text, /Scaffold prompt:/);
137
139
  });
140
+
141
+ defineTest("resolveSubagentsLiveUiModeCommand sets requested mode", () => {
142
+ setTaskLiveUiMode("compact");
143
+
144
+ const result = resolveSubagentsLiveUiModeCommand(["verbose"]);
145
+
146
+ assert.equal(result.ok, true);
147
+ assert.equal(result.mode, "verbose");
148
+ assert.equal(getTaskLiveUiMode(), "verbose");
149
+ });
150
+
151
+ defineTest("resolveSubagentsLiveUiModeCommand rejects invalid mode values", () => {
152
+ setTaskLiveUiMode("compact");
153
+
154
+ const result = resolveSubagentsLiveUiModeCommand(["loud"]);
155
+
156
+ assert.equal(result.ok, false);
157
+ assert.match(result.message, /off\|compact\|verbose/);
158
+ assert.equal(getTaskLiveUiMode(), "compact");
159
+ });
package/src/extension.ts CHANGED
@@ -2,6 +2,12 @@ import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
2
2
  import { loadOhmRuntimeConfig, registerOhmSettings, type OhmRuntimeConfig } from "@pi-ohm/config";
3
3
  import { getSubagentById, OHM_SUBAGENT_CATALOG } from "./catalog";
4
4
  import { isSubagentVisibleInTaskRoster } from "./policy";
5
+ import {
6
+ getTaskLiveUiMode,
7
+ parseTaskLiveUiModeInput,
8
+ setTaskLiveUiMode,
9
+ type TaskLiveUiMode,
10
+ } from "./runtime/live-ui";
5
11
  import { registerPrimarySubagentTools } from "./tools/primary";
6
12
  import { createDefaultTaskToolDependencies, registerTaskTool } from "./tools/task";
7
13
 
@@ -107,6 +113,47 @@ export function buildSubagentDetailText(input: {
107
113
  ].join("\n");
108
114
  }
109
115
 
116
+ export interface ResolveSubagentsLiveUiModeResult {
117
+ readonly ok: boolean;
118
+ readonly mode: TaskLiveUiMode;
119
+ readonly message: string;
120
+ }
121
+
122
+ export function resolveSubagentsLiveUiModeCommand(args: unknown): ResolveSubagentsLiveUiModeResult {
123
+ const [requestedModeRaw] = normalizeCommandArgs(args);
124
+ const currentMode = getTaskLiveUiMode();
125
+
126
+ if (!requestedModeRaw) {
127
+ return {
128
+ ok: true,
129
+ mode: currentMode,
130
+ message: [
131
+ `subagents live ui mode: ${currentMode}`,
132
+ "Usage: /ohm-subagents-live <off|compact|verbose>",
133
+ ].join("\n"),
134
+ };
135
+ }
136
+
137
+ const parsedMode = parseTaskLiveUiModeInput(requestedModeRaw);
138
+ if (!parsedMode) {
139
+ return {
140
+ ok: false,
141
+ mode: currentMode,
142
+ message: [`Invalid mode '${requestedModeRaw}'.`, "Use one of: off|compact|verbose"].join(
143
+ "\n",
144
+ ),
145
+ };
146
+ }
147
+
148
+ setTaskLiveUiMode(parsedMode);
149
+
150
+ return {
151
+ ok: true,
152
+ mode: parsedMode,
153
+ message: `subagents live ui mode set to '${parsedMode}'`,
154
+ };
155
+ }
156
+
110
157
  export function registerSubagentTools(pi: Pick<ExtensionAPI, "registerTool">): {
111
158
  readonly primaryToolCount: number;
112
159
  readonly diagnosticsCount: number;
@@ -189,4 +236,25 @@ export default function registerSubagentsExtension(pi: ExtensionAPI): void {
189
236
  await ctx.ui.editor(`pi-ohm ${match.id} subagent`, text);
190
237
  },
191
238
  });
239
+
240
+ pi.registerCommand("ohm-subagents-live", {
241
+ description: "Set subagents live UI mode (off|compact|verbose)",
242
+ handler: async (args, ctx) => {
243
+ const result = resolveSubagentsLiveUiModeCommand(args);
244
+
245
+ if (!ctx.hasUI) {
246
+ console.log(result.message);
247
+ return;
248
+ }
249
+
250
+ if (result.mode === "off") {
251
+ ctx.ui.setStatus("ohm-subagents", undefined);
252
+ ctx.ui.setWidget("ohm-subagents", undefined, { placement: "belowEditor" });
253
+ } else {
254
+ ctx.ui.setStatus("ohm-subagents", `subagents live ui: ${result.mode}`);
255
+ }
256
+
257
+ await ctx.ui.editor("pi-ohm subagents live", result.message);
258
+ },
259
+ });
192
260
  }