@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 +120 -14
- package/package.json +3 -2
- package/src/extension.test.ts +22 -0
- package/src/extension.ts +68 -0
- package/src/runtime/backend.test.ts +501 -1
- package/src/runtime/backend.ts +592 -26
- package/src/runtime/events.test.ts +101 -0
- package/src/runtime/events.ts +198 -0
- package/src/runtime/live-ui.test.ts +272 -0
- package/src/runtime/live-ui.ts +273 -0
- package/src/runtime/tasks.test.ts +184 -0
- package/src/runtime/tasks.ts +378 -8
- package/src/runtime/ui.test.ts +125 -5
- package/src/runtime/ui.ts +165 -14
- package/src/tools/primary.test.ts +121 -4
- package/src/tools/primary.ts +248 -29
- package/src/tools/task.test.ts +934 -4
- package/src/tools/task.ts +1183 -33
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
|
-
-
|
|
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 `@
|
|
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
|
-
-
|
|
132
|
-
-
|
|
133
|
-
-
|
|
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
|
|
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
|
-
⠋
|
|
145
|
-
|
|
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
|
-
✓
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
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.
|
|
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.
|
|
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
|
},
|
package/src/extension.test.ts
CHANGED
|
@@ -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
|
}
|