pi-subagents 0.19.3 → 0.20.0

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/CHANGELOG.md CHANGED
@@ -2,6 +2,22 @@
2
2
 
3
3
  ## [Unreleased]
4
4
 
5
+ ## [0.20.0] - 2026-04-27
6
+
7
+ ### Added
8
+ - Added a packaged `/parallel-cleanup` prompt for focused cleanup review passes.
9
+
10
+ ### Changed
11
+ - Consolidated the `oracle-executor` role into `worker`: `worker` now uses `openai-codex/gpt-5.3-codex` with high thinking and stricter approved-direction guardrails, while `researcher` and `context-builder` now use medium thinking.
12
+ - Updated the bundled `scout` agent model/thinking defaults.
13
+ - Hard-cut over grouped intercom bridge result delivery: with the bridge active, parent-side `pi-subagents` emits one grouped `subagent:result-intercom` message per foreground parent run (single, top-level parallel, or chain) and one per completed async result file. Acknowledged foreground delivery returns a compact receipt instead of duplicating full output in the normal tool result; unacknowledged delivery preserves the normal full output. Grouped messages include child intercom targets and full child summaries.
14
+
15
+ ### Fixed
16
+ - Fixed status and manager row rendering so multiline or tabbed content cannot overflow table rows.
17
+
18
+ ### Removed
19
+ - Removed the bundled `oracle-executor` agent and `/oracle-executor` prompt template in favor of using `worker` for approved oracle handoffs.
20
+
5
21
  ## [0.19.3] - 2026-04-27
6
22
 
7
23
  ### Changed
package/README.md CHANGED
@@ -67,7 +67,7 @@ Run parallel reviewers on this diff. I want one focused on correctness, one on t
67
67
  ```
68
68
 
69
69
  ```text
70
- Implement this plan with oracle-executor. Afterward, run parallel reviewers, summarize their feedback, and apply the fixes that make sense.
70
+ Have worker implement this approved plan. Afterward, run parallel reviewers, summarize their feedback, and apply the fixes that make sense.
71
71
  ```
72
72
 
73
73
  ```text
@@ -85,7 +85,7 @@ Those are ordinary Pi requests. Pi decides whether to call `subagent`, which age
85
85
  | Review a diff | “Use reviewer to review this diff.” |
86
86
  | Run parallel reviewers | “Run reviewers for correctness, tests, and cleanup.” |
87
87
  | Implement then review | “Implement this, then review it.” |
88
- | Execute a plan carefully | “Use oracle-executor to implement this plan, then run reviewers and apply the feedback.” |
88
+ | Execute a plan carefully | “Have worker implement this approved plan, then run reviewers and apply the feedback.” |
89
89
  | Scout before planning | “Use scout to inspect the auth flow before planning.” |
90
90
  | Run in the background | “Run this in the background.” |
91
91
  | Browse agents | “Show me the available subagents.” |
@@ -102,11 +102,10 @@ The extension ships with builtin agents you can use immediately.
102
102
  | `scout` | Fast local codebase recon: relevant files, entry points, data flow, risks, and where another agent should start. |
103
103
  | `researcher` | Web/docs research with sources: official docs, specs, benchmarks, recent changes, and a concise research brief. |
104
104
  | `planner` | A concrete implementation plan from existing context. It should read and plan, not edit code. |
105
- | `worker` | General implementation work. It reads context or a plan, edits files, and runs validation when it can. |
105
+ | `worker` | Implementation work, including approved oracle handoffs. It edits files, validates, and escalates unapproved decisions instead of guessing. |
106
106
  | `reviewer` | Code review and small fixes. It checks the implementation against the task/plan, tests, edge cases, and simplicity. |
107
107
  | `context-builder` | A stronger setup pass before planning: gathers code context and writes handoff material such as `context.md` and `meta-prompt.md`. |
108
108
  | `oracle` | A second opinion before acting. It challenges assumptions, catches drift, and recommends the safest next move without editing. |
109
- | `oracle-executor` | Careful implementation after a direction has been approved. It is the “go do the approved thing” agent. |
110
109
  | `delegate` | A lightweight general delegate when you want a child agent that behaves close to the parent session. |
111
110
 
112
111
  A simple rule of thumb: use `scout` before you understand the code, `researcher` before you trust external facts, `planner` before a bigger change, `worker` to implement, `reviewer` to check, and `oracle` when the decision itself feels risky.
@@ -184,7 +183,6 @@ The package includes reusable prompt templates for common workflows. You do not
184
183
  | `/parallel-review` | Launch fresh-context reviewers with distinct angles, then synthesize what to fix. |
185
184
  | `/parallel-research` | Combine `researcher` and `scout` for external evidence, local code context, and practical tradeoffs. |
186
185
  | `/gather-context-and-clarify` | Scout/research first, then ask the user the clarification questions that matter. |
187
- | `/oracle-executor` | Send an explicitly approved implementation task to `oracle-executor` with inherited context. |
188
186
 
189
187
  ## Optional pi-intercom companion
190
188
 
@@ -206,12 +204,12 @@ Run this implementation in the background. If the worker gets blocked or needs a
206
204
  Ask oracle to review this plan. If it sees a decision I need to make, have it ask me instead of assuming.
207
205
  ```
208
206
 
209
- The child can use two kinds of messages:
207
+ The child can use two kinds of coordination messages:
210
208
 
211
209
  - `ask`: the child needs a decision or clarification from the parent session
212
210
  - `send`: the child sends a short update when blocked or explicitly asked for progress
213
211
 
214
- Routine completion does not go through intercom. The normal subagent result still comes back through `pi-subagents`.
212
+ Child-side routine completion handoffs are still not expected. With the intercom bridge active, parent-side `pi-subagents` sends grouped completion results through `pi-intercom`: one grouped message per foreground parent `subagent` run and one per completed async result file. Acknowledged foreground delivery returns a compact receipt with artifact/session paths; if unacknowledged, the normal full output is preserved. Grouped messages include child intercom targets and full child summaries.
215
213
 
216
214
  If a child appears stalled, needs-attention notices can show up in the parent session with useful next actions, such as checking `/subagents-status`, interrupting the run, or nudging the child.
217
215
 
@@ -315,7 +313,7 @@ You can combine them in either order:
315
313
  /run reviewer "review this diff" --bg --fork
316
314
  ```
317
315
 
318
- The `oracle` and `oracle-executor` builtins are designed for an explicit decision loop. A typical pattern is to ask `oracle` for diagnosis and a recommended execution prompt, then only run `oracle-executor` after the main agent approves that direction.
316
+ The `oracle` and `worker` builtins are designed for an explicit decision loop. A typical pattern is to ask `oracle` for diagnosis and a recommended execution prompt, then only run `worker` after the main agent approves that direction.
319
317
 
320
318
  ## Clarify and launch UI
321
319
 
@@ -386,7 +384,7 @@ Agent locations, lowest to highest priority:
386
384
 
387
385
  Project discovery also reads legacy `.agents/{name}.md` files. If both `.agents/` and `.pi/agents/` define the same project agent, `.pi/agents/` wins. Use `agentScope: "user" | "project" | "both"` to control discovery; `both` is the default and project definitions win name collisions.
388
386
 
389
- Builtin agents load at the lowest priority, so a user or project agent with the same name overrides them. `oracle` is an advisory reviewer that critiques direction and proposes an execution prompt without editing files. `oracle-executor` is an implementation escalator intended to run only after the main agent approves a course of action.
387
+ Builtin agents load at the lowest priority, so a user or project agent with the same name overrides them. `oracle` is an advisory reviewer that critiques direction and proposes an execution prompt without editing files. `worker` is the implementation agent for normal tasks and approved oracle handoffs.
390
388
 
391
389
  The `researcher` builtin uses `web_search`, `fetch_content`, and `get_search_content`; those require [pi-web-access](https://github.com/nicobailon/pi-web-access):
392
390
 
@@ -939,7 +937,12 @@ Async events:
939
937
  - `subagent:async-started`
940
938
  - `subagent:async-complete`
941
939
 
942
- The result watcher emits `subagent:async-complete`; `index.ts` registers the notification handler that consumes it. Control/attention events are surfaced as visible parent notices and persisted for async runs. With `pi-intercom`, needs-attention notices can also reach the orchestrator over intercom.
940
+ Intercom delivery events:
941
+
942
+ - `subagent:control-intercom`
943
+ - `subagent:result-intercom`
944
+
945
+ The result watcher emits `subagent:async-complete`; `index.ts` registers the notification handler that consumes it. Control/attention events are surfaced as visible parent notices and persisted for async runs. With `pi-intercom`, needs-attention notices and grouped parent-side subagent result deliveries can reach the orchestrator over intercom.
943
946
 
944
947
  ## Prompt-template integration
945
948
 
@@ -3,6 +3,7 @@ name: context-builder
3
3
  description: Analyzes requirements and codebase, generates context and meta-prompt
4
4
  tools: read, grep, find, ls, bash, write, web_search
5
5
  model: openai-codex/gpt-5.5
6
+ thinking: medium
6
7
  systemPromptMode: replace
7
8
  inheritProjectContext: true
8
9
  inheritSkills: false
package/agents/oracle.md CHANGED
@@ -17,7 +17,7 @@ Before you do anything else, reconstruct the key inherited decisions, constraint
17
17
 
18
18
  If you need clarification from the main agent, use `intercom`. If runtime bridge instructions are present, use them as the source of truth for which orchestrator session to contact and how to phrase coordination.
19
19
 
20
- Use `intercom({ action: "ask", ... })` when you need a real decision or clarification. Use `intercom({ action: "send", ... })` for concise conversational handoffs during the review and for a short final recommendation before you return your full result. Keep intercom traffic tight and purposeful. Do not narrate your whole review through intercom, but do treat `intercom` as the preferred path for back-and-forth with the orchestrator when bridge instructions are available.
20
+ Use `intercom({ action: "ask", ... })` when you need a real decision or clarification. Use `intercom({ action: "send", ... })` only for concise updates when blocked, explicitly asked for progress, or when a recommendation or concern would benefit from immediate discussion. Keep intercom traffic tight and purposeful. Do not narrate your whole review through intercom, and do not send routine completion handoffs.
21
21
 
22
22
  Core responsibilities:
23
23
  - reconstruct inherited decisions, constraints, and open questions from the context
@@ -32,7 +32,7 @@ Core responsibilities:
32
32
  What you do not do by default:
33
33
  - do not edit files or write code
34
34
  - do not propose additional parallel decision-makers or new subagent trees unless explicitly asked
35
- - do not assume an `oracle-executor` handoff is the default outcome
35
+ - do not assume a `worker` implementation handoff is the default outcome
36
36
  - do not propose broad pivots unless the context clearly supports them
37
37
  - do not continue the user conversation directly
38
38
 
@@ -40,8 +40,7 @@ Working rules:
40
40
  - Use `bash` only for inspection, verification, or read-only analysis.
41
41
  - If information is missing and it matters, ask the main agent via `intercom` instead of guessing.
42
42
  - If the answer depends on a decision the main agent has not made yet, stop and ask via `intercom` before continuing.
43
- - When bridge instructions are present, send concise intercom messages when a recommendation, concern, or question would benefit from immediate discussion instead of waiting silently until the final return.
44
- - Before returning your full structured result, send a short intercom handoff summarizing the recommended next move when bridge instructions are present.
43
+ - When bridge instructions are present, send concise intercom messages only when a recommendation, concern, or question would benefit from immediate discussion instead of waiting silently until the final return.
45
44
  - Prefer narrow, specific corrections to the current path over rewriting the whole plan.
46
45
 
47
46
  Your output should follow this shape. If no executor handoff is warranted, say so plainly.
@@ -70,5 +69,5 @@ Need from main agent:
70
69
  - specific question or decision required before continuing, if any
71
70
 
72
71
  Suggested execution prompt:
73
- - a concrete prompt for `oracle-executor`, only if an executor handoff is actually warranted
72
+ - a concrete prompt for `worker`, only if an implementation handoff is actually warranted
74
73
  - if no handoff is warranted, say so explicitly
@@ -3,6 +3,7 @@ name: researcher
3
3
  description: Autonomous web researcher — searches, evaluates, and synthesizes a focused research brief
4
4
  tools: read, write, web_search, fetch_content, get_search_content
5
5
  model: openai-codex/gpt-5.5
6
+ thinking: medium
6
7
  systemPromptMode: replace
7
8
  inheritProjectContext: true
8
9
  inheritSkills: false
package/agents/scout.md CHANGED
@@ -2,7 +2,8 @@
2
2
  name: scout
3
3
  description: Fast codebase recon that returns compressed context for handoff
4
4
  tools: read, grep, find, ls, bash, write
5
- model: openai-codex/gpt-5.4-mini
5
+ model: openai-codex/gpt-5.5
6
+ thinking: medium
6
7
  systemPromptMode: replace
7
8
  inheritProjectContext: true
8
9
  inheritSkills: false
package/agents/worker.md CHANGED
@@ -1,7 +1,8 @@
1
1
  ---
2
2
  name: worker
3
- description: General-purpose subagent with full capabilities
4
- model: openai-codex/gpt-5.5
3
+ description: Implementation agent for normal tasks and approved oracle handoffs
4
+ model: openai-codex/gpt-5.3-codex
5
+ thinking: high
5
6
  systemPromptMode: replace
6
7
  inheritProjectContext: true
7
8
  inheritSkills: false
@@ -9,36 +10,43 @@ defaultReads: context.md, plan.md
9
10
  defaultProgress: true
10
11
  ---
11
12
 
12
- You are an implementation subagent.
13
+ You are `worker`: the implementation subagent.
13
14
 
14
- Use the provided tools directly to complete the task. Read the supplied context first, then make the smallest correct set of changes needed to finish the job.
15
+ You are the single writer thread. Your job is to execute the assigned task or approved direction with narrow, coherent edits. The main agent and user remain the decision authority.
16
+
17
+ Use the provided tools directly. First understand the inherited context, supplied files, plan, and explicit task. Then implement carefully and minimally.
18
+
19
+ If the task is framed as an approved direction, oracle handoff, or execution plan, treat that direction as the contract. Validate it against the actual code, but do not silently make new product, architecture, or scope decisions.
20
+
21
+ If the implementation reveals a decision that was not approved and is required to continue safely, pause and escalate. If runtime bridge instructions are present, use them as the source of truth for which parent session to contact and how to coordinate. Use `intercom({ action: "ask", ... })` when a new decision is needed. Use `intercom({ action: "send", ... })` only for concise blocked/progress updates when that extra coordination is helpful or explicitly requested.
22
+
23
+ Default responsibilities:
24
+ - validate the task or approved direction against the actual code
25
+ - implement the smallest correct change
26
+ - follow existing patterns in the codebase
27
+ - verify the result with appropriate checks when possible
28
+ - keep `progress.md` accurate when asked to maintain it
29
+ - report back clearly with changes, validation, risks, and next steps
15
30
 
16
31
  Working rules:
17
- - Follow existing patterns in the codebase.
18
- - Prefer simple changes over clever ones.
19
- - Do not leave speculative scaffolding, placeholder code, or TODOs unless the task explicitly requires them.
20
- - Run relevant tests or validation commands when you can.
21
- - If you are asked to maintain progress, keep it accurate and up to date.
22
- - When you finish, summarize what changed, what you verified, and anything still unresolved.
32
+ - Prefer narrow, correct changes over broad rewrites.
33
+ - Do not add speculative scaffolding or future-proofing unless explicitly required.
34
+ - Do not leave placeholder code, TODOs, or silent scope changes.
35
+ - Use `bash` for inspection, validation, and relevant tests.
36
+ - If there is supplied context or a plan, read it first.
37
+ - If implementation reveals a gap in the approved direction, pause and escalate instead of silently patching around it with an implicit decision.
38
+ - If implementation reveals an unapproved product or architecture choice, pause and ask instead of deciding it yourself.
39
+ - If you send a blocked/progress update through intercom, keep it short and still return the full structured task result normally.
23
40
 
24
41
  When running in a chain, expect instructions about:
25
42
  - which files to read first
26
43
  - where to maintain progress tracking
27
44
  - where to write output if a file target is provided
28
45
 
29
- Suggested `progress.md` structure when asked to maintain it:
30
-
31
- # Progress
32
-
33
- ## Status
34
- [In Progress | Completed | Blocked]
35
-
36
- ## Tasks
37
- - [x] Completed task
38
- - [ ] Current task
39
-
40
- ## Files Changed
41
- - `path/to/file.ts` - what changed
46
+ Your final response should follow this shape:
42
47
 
43
- ## Notes
44
- Key decisions, blockers, or follow-up items.
48
+ Implemented X.
49
+ Changed files: Y.
50
+ Validation: Z.
51
+ Open risks/questions: R.
52
+ Recommended next step: N.
@@ -64,6 +64,7 @@ export interface AsyncExecutionContext {
64
64
 
65
65
  export interface AsyncChainParams {
66
66
  chain: ChainStep[];
67
+ resultMode?: "parallel" | "chain";
67
68
  agents: AgentConfig[];
68
69
  ctx: AsyncExecutionContext;
69
70
  availableModels?: AvailableModelInfo[];
@@ -345,6 +346,7 @@ export function executeAsyncChain(
345
346
  controlConfig,
346
347
  controlIntercomTarget,
347
348
  childIntercomTargets,
349
+ resultMode: params.resultMode ?? "chain",
348
350
  },
349
351
  id,
350
352
  runnerCwd,
@@ -484,6 +486,7 @@ export function executeAsyncSingle(
484
486
  controlConfig,
485
487
  controlIntercomTarget,
486
488
  childIntercomTargets: childIntercomTarget ? [childIntercomTarget(agent, 0)] : undefined,
489
+ resultMode: "single",
487
490
  },
488
491
  id,
489
492
  runnerCwd,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "pi-subagents",
3
- "version": "0.19.3",
3
+ "version": "0.20.0",
4
4
  "description": "Pi extension for delegating tasks to subagents with chains, parallel execution, and TUI clarification",
5
5
  "author": "Nico Bailon",
6
6
  "license": "MIT",
@@ -0,0 +1,18 @@
1
+ ---
2
+ description: Parallel subagents cleanup
3
+ ---
4
+
5
+ Launch parallel reviewers for an adversarial review of the current work.
6
+
7
+ One reviewer should load with the deslop skill and another reviewer with the verbosity check skill.
8
+
9
+ Give every reviewer a meta prompt. Ask reviewers to return concise, evidence-backed findings with file/line references and suggested fixes. The response should be review feedback, not a context summary. Reviewers must not edit files unless I explicitly ask for a writer pass.
10
+
11
+ While the reviewers run, do your own narrow inspection if useful. After they return, synthesize the feedback into:
12
+ - fixes worth doing now
13
+ - optional improvements
14
+ - feedback to ignore or defer, with a short reason
15
+
16
+ Do not blindly apply every reviewer suggestion. Ask before applying fixes unless I already told you to address review feedback.
17
+
18
+ $@
package/render-helpers.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import type { Theme } from "@mariozechner/pi-coding-agent";
2
- import { visibleWidth } from "@mariozechner/pi-tui";
2
+ import { truncateToWidth, visibleWidth } from "@mariozechner/pi-tui";
3
3
 
4
4
  function fuzzyScore(query: string, text: string): number {
5
5
  const lq = query.toLowerCase();
@@ -37,7 +37,9 @@ export function pad(s: string, len: number): string {
37
37
 
38
38
  export function row(content: string, width: number, theme: Theme): string {
39
39
  const innerW = width - 2;
40
- return theme.fg("border", "") + pad(content, innerW) + theme.fg("border", "");
40
+ const singleLine = content.replace(/[\r\n]+/g, " ").replace(/\t/g, " ");
41
+ const clipped = truncateToWidth(singleLine, innerW);
42
+ return theme.fg("border", "│") + pad(clipped, innerW) + theme.fg("border", "│");
41
43
  }
42
44
 
43
45
  export function renderHeader(text: string, width: number, theme: Theme): string {
@@ -0,0 +1,235 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import {
3
+ type Details,
4
+ type IntercomEventBus,
5
+ type SingleResult,
6
+ type SubagentResultIntercomChild,
7
+ type SubagentResultIntercomPayload,
8
+ type SubagentResultStatus,
9
+ SUBAGENT_RESULT_INTERCOM_DELIVERY_EVENT,
10
+ SUBAGENT_RESULT_INTERCOM_EVENT,
11
+ } from "./types.ts";
12
+
13
+ export function resolveSubagentResultStatus(input: {
14
+ exitCode?: number;
15
+ success?: boolean;
16
+ state?: string;
17
+ interrupted?: boolean;
18
+ detached?: boolean;
19
+ }): SubagentResultStatus {
20
+ if (input.detached) return "detached";
21
+ if (input.interrupted || input.state === "paused") return "paused";
22
+ if (typeof input.success === "boolean") return input.success ? "completed" : "failed";
23
+ if (input.state === "complete") return "completed";
24
+ if (input.state === "failed") return "failed";
25
+ if (typeof input.exitCode === "number") return input.exitCode === 0 ? "completed" : "failed";
26
+ return "failed";
27
+ }
28
+
29
+ function countStatuses(children: SubagentResultIntercomChild[]): Record<SubagentResultStatus, number> {
30
+ const counts: Record<SubagentResultStatus, number> = {
31
+ completed: 0,
32
+ failed: 0,
33
+ paused: 0,
34
+ detached: 0,
35
+ };
36
+ for (const child of children) {
37
+ counts[child.status] += 1;
38
+ }
39
+ return counts;
40
+ }
41
+
42
+ function formatStatusCounts(counts: Record<SubagentResultStatus, number>): string {
43
+ const parts = [
44
+ counts.completed ? `${counts.completed} completed` : undefined,
45
+ counts.failed ? `${counts.failed} failed` : undefined,
46
+ counts.paused ? `${counts.paused} paused` : undefined,
47
+ counts.detached ? `${counts.detached} detached` : undefined,
48
+ ].filter((part): part is string => Boolean(part));
49
+ return parts.length ? parts.join(", ") : "0 results";
50
+ }
51
+
52
+ function resolveGroupedStatus(children: SubagentResultIntercomChild[]): SubagentResultStatus {
53
+ const counts = countStatuses(children);
54
+ if (counts.failed > 0) return "failed";
55
+ if (counts.paused > 0) return "paused";
56
+ if (counts.completed > 0) return "completed";
57
+ if (counts.detached > 0) return "detached";
58
+ return "failed";
59
+ }
60
+
61
+ export interface GroupedResultIntercomMessageInput {
62
+ to: string;
63
+ runId: string;
64
+ mode: "single" | "parallel" | "chain";
65
+ source: "foreground" | "async";
66
+ children: SubagentResultIntercomChild[];
67
+ asyncId?: string;
68
+ asyncDir?: string;
69
+ chainSteps?: number;
70
+ }
71
+
72
+ export function formatSubagentResultIntercomMessage(input: {
73
+ runId: string;
74
+ mode: "single" | "parallel" | "chain";
75
+ status: SubagentResultStatus;
76
+ children: SubagentResultIntercomChild[];
77
+ asyncId?: string;
78
+ asyncDir?: string;
79
+ chainSteps?: number;
80
+ }): string {
81
+ const counts = countStatuses(input.children);
82
+ const lines: string[] = [
83
+ "subagent results",
84
+ "",
85
+ `Run: ${input.runId}`,
86
+ `Mode: ${input.mode}`,
87
+ `Status: ${input.status}`,
88
+ `Children: ${formatStatusCounts(counts)}`,
89
+ ];
90
+ if (input.mode === "chain" && typeof input.chainSteps === "number") {
91
+ lines.push(`Chain steps: ${input.chainSteps}`);
92
+ }
93
+ if (input.asyncId) lines.push(`Async id: ${input.asyncId}`);
94
+ if (input.asyncDir) lines.push(`Async dir: ${input.asyncDir}`);
95
+ if (input.children.some((child) => child.intercomTarget)) {
96
+ lines.push("");
97
+ lines.push("For clarification, message a listed subagent at its Intercom target.");
98
+ }
99
+
100
+ for (let index = 0; index < input.children.length; index++) {
101
+ const child = input.children[index]!;
102
+ lines.push("");
103
+ lines.push(`${index + 1}. ${child.agent} — ${child.status}`);
104
+ if (child.intercomTarget) lines.push(`Intercom target: ${child.intercomTarget}`);
105
+ if (child.artifactPath) lines.push(`Output artifact: ${child.artifactPath}`);
106
+ if (child.sessionPath) lines.push(`Session: ${child.sessionPath}`);
107
+ lines.push("Summary:");
108
+ lines.push(child.summary);
109
+ }
110
+
111
+ return lines.join("\n");
112
+ }
113
+
114
+ export function buildSubagentResultIntercomPayload(input: GroupedResultIntercomMessageInput): SubagentResultIntercomPayload {
115
+ const children = input.children.map((child) => ({
116
+ ...child,
117
+ summary: child.summary.trim() || "(no output)",
118
+ }));
119
+ const status = resolveGroupedStatus(children);
120
+ const summary = formatStatusCounts(countStatuses(children));
121
+ const firstChild = children[0];
122
+ const payload: SubagentResultIntercomPayload = {
123
+ to: input.to,
124
+ runId: input.runId,
125
+ mode: input.mode,
126
+ status,
127
+ summary,
128
+ source: input.source,
129
+ children,
130
+ ...(input.asyncId ? { asyncId: input.asyncId } : {}),
131
+ ...(input.asyncDir ? { asyncDir: input.asyncDir } : {}),
132
+ ...(typeof input.chainSteps === "number" ? { chainSteps: input.chainSteps } : {}),
133
+ ...(firstChild?.agent ? { agent: firstChild.agent } : {}),
134
+ ...(firstChild?.index !== undefined ? { index: firstChild.index } : {}),
135
+ ...(firstChild?.artifactPath ? { artifactPath: firstChild.artifactPath } : {}),
136
+ ...(firstChild?.sessionPath ? { sessionPath: firstChild.sessionPath } : {}),
137
+ message: "",
138
+ };
139
+ payload.message = formatSubagentResultIntercomMessage(payload);
140
+ return payload;
141
+ }
142
+
143
+ export async function deliverSubagentResultIntercomEvent(
144
+ events: IntercomEventBus,
145
+ payload: SubagentResultIntercomPayload,
146
+ timeoutMs = 500,
147
+ ): Promise<boolean> {
148
+ if (typeof events.on !== "function" || typeof events.emit !== "function") return false;
149
+ const requestId = payload.requestId ?? randomUUID();
150
+ return new Promise((resolve) => {
151
+ let settled = false;
152
+ let unsubscribe: (() => void) | undefined;
153
+ let timer: ReturnType<typeof setTimeout> | undefined;
154
+ const finish = (delivered: boolean) => {
155
+ if (settled) return;
156
+ settled = true;
157
+ if (timer) clearTimeout(timer);
158
+ unsubscribe?.();
159
+ resolve(delivered);
160
+ };
161
+ unsubscribe = events.on(SUBAGENT_RESULT_INTERCOM_DELIVERY_EVENT, (data) => {
162
+ if (!data || typeof data !== "object") return;
163
+ const delivery = data as { requestId?: unknown; delivered?: unknown };
164
+ if (delivery.requestId !== requestId) return;
165
+ finish(delivery.delivered === true);
166
+ });
167
+ timer = setTimeout(() => finish(false), timeoutMs);
168
+ try {
169
+ events.emit(SUBAGENT_RESULT_INTERCOM_EVENT, { ...payload, requestId });
170
+ } catch {
171
+ finish(false);
172
+ }
173
+ });
174
+ }
175
+
176
+ function stripSingleResultOutputs(result: SingleResult): SingleResult {
177
+ return {
178
+ ...result,
179
+ messages: undefined,
180
+ finalOutput: undefined,
181
+ truncation: undefined,
182
+ };
183
+ }
184
+
185
+ export function stripDetailsOutputsForIntercomReceipt(details: Details): Details {
186
+ return {
187
+ ...details,
188
+ results: details.results.map(stripSingleResultOutputs),
189
+ };
190
+ }
191
+
192
+ export function formatSubagentResultReceipt(input: {
193
+ mode: "single" | "parallel" | "chain";
194
+ runId: string;
195
+ payload: SubagentResultIntercomPayload;
196
+ }): string {
197
+ const counts = countStatuses(input.payload.children);
198
+ const modeLabel = input.mode === "single"
199
+ ? "single subagent result"
200
+ : input.mode === "parallel"
201
+ ? "parallel subagent results"
202
+ : "chain subagent results";
203
+ const lines = [
204
+ `Delivered ${modeLabel} via intercom.`,
205
+ `Run: ${input.runId}`,
206
+ `Children: ${formatStatusCounts(counts)}`,
207
+ ];
208
+
209
+ const artifacts = input.payload.children.filter((child) => typeof child.artifactPath === "string");
210
+ if (artifacts.length > 0) {
211
+ lines.push("Artifacts:");
212
+ for (const child of artifacts) {
213
+ lines.push(`- ${child.agent} [${child.status}]: ${child.artifactPath}`);
214
+ }
215
+ }
216
+
217
+ const intercomTargets = input.payload.children.filter((child) => typeof child.intercomTarget === "string");
218
+ if (intercomTargets.length > 0) {
219
+ lines.push("Intercom targets:");
220
+ for (const child of intercomTargets) {
221
+ lines.push(`- ${child.agent} [${child.status}]: ${child.intercomTarget}`);
222
+ }
223
+ }
224
+
225
+ const sessions = input.payload.children.filter((child) => typeof child.sessionPath === "string");
226
+ if (sessions.length > 0) {
227
+ lines.push("Sessions:");
228
+ for (const child of sessions) {
229
+ lines.push(`- ${child.agent} [${child.status}]: ${child.sessionPath}`);
230
+ }
231
+ }
232
+
233
+ lines.push("Full grouped output was sent over intercom.");
234
+ return lines.join("\n");
235
+ }
package/result-watcher.ts CHANGED
@@ -1,9 +1,17 @@
1
1
  import * as fs from "node:fs";
2
2
  import * as path from "node:path";
3
3
  import type { ExtensionAPI } from "@mariozechner/pi-coding-agent";
4
- import { buildCompletionKey, markSeenWithTtl } from "./completion-dedupe.js";
5
- import { createFileCoalescer } from "./file-coalescer.js";
6
- import { SUBAGENT_ASYNC_COMPLETE_EVENT, type SubagentState } from "./types.js";
4
+ import { buildCompletionKey, markSeenWithTtl } from "./completion-dedupe.ts";
5
+ import { createFileCoalescer } from "./file-coalescer.ts";
6
+ import {
7
+ SUBAGENT_ASYNC_COMPLETE_EVENT,
8
+ type SubagentState,
9
+ } from "./types.ts";
10
+ import {
11
+ buildSubagentResultIntercomPayload,
12
+ deliverSubagentResultIntercomEvent,
13
+ resolveSubagentResultStatus,
14
+ } from "./result-intercom.ts";
7
15
 
8
16
  function isNotFoundError(error: unknown): boolean {
9
17
  return typeof error === "object"
@@ -22,13 +30,30 @@ export function createResultWatcher(
22
30
  primeExistingResults: () => void;
23
31
  stopResultWatcher: () => void;
24
32
  } {
25
- const handleResult = (file: string) => {
33
+ const handleResult = async (file: string) => {
26
34
  const resultPath = path.join(resultsDir, file);
27
35
  if (!fs.existsSync(resultPath)) return;
28
36
  try {
29
37
  const data = JSON.parse(fs.readFileSync(resultPath, "utf-8")) as {
38
+ id?: string;
39
+ runId?: string;
40
+ agent?: string;
41
+ success?: boolean;
42
+ state?: string;
43
+ mode?: string;
44
+ summary?: string;
45
+ results?: Array<{
46
+ agent?: string;
47
+ output?: string;
48
+ success?: boolean;
49
+ artifactPaths?: { outputPath?: string };
50
+ intercomTarget?: string;
51
+ }>;
30
52
  sessionId?: string;
31
53
  cwd?: string;
54
+ sessionFile?: string;
55
+ asyncDir?: string;
56
+ intercomTarget?: string;
32
57
  };
33
58
  if (data.sessionId && data.sessionId !== state.currentSessionId) return;
34
59
  if (!data.sessionId && data.cwd && data.cwd !== state.baseCwd) return;
@@ -40,6 +65,45 @@ export function createResultWatcher(
40
65
  return;
41
66
  }
42
67
 
68
+ const intercomTarget = data.intercomTarget?.trim();
69
+ if (intercomTarget) {
70
+ const childResults = Array.isArray(data.results) && data.results.length > 0
71
+ ? data.results
72
+ : [{
73
+ agent: data.agent,
74
+ output: data.summary,
75
+ success: data.success,
76
+ }];
77
+ const runId = data.runId ?? data.id ?? file.replace(/\.json$/i, "");
78
+ const mode = data.mode === "single" || data.mode === "parallel" || data.mode === "chain"
79
+ ? data.mode
80
+ : childResults.length > 1 ? "chain" : "single";
81
+ const payload = buildSubagentResultIntercomPayload({
82
+ to: intercomTarget,
83
+ runId,
84
+ mode,
85
+ source: "async",
86
+ children: childResults.map((result = {}, index) => ({
87
+ agent: result.agent ?? data.agent ?? `step-${index + 1}`,
88
+ status: resolveSubagentResultStatus({
89
+ success: result.success,
90
+ state: data.state === "paused" || typeof result.success !== "boolean" ? data.state : undefined,
91
+ }),
92
+ summary: result.output ?? data.summary ?? "(no output)",
93
+ index,
94
+ artifactPath: result.artifactPaths?.outputPath,
95
+ sessionPath: data.sessionFile,
96
+ intercomTarget: result.intercomTarget,
97
+ })),
98
+ asyncId: data.id,
99
+ asyncDir: data.asyncDir,
100
+ });
101
+ const delivered = await deliverSubagentResultIntercomEvent(pi.events, payload);
102
+ if (!delivered) {
103
+ console.error(`Subagent async grouped result intercom delivery was not acknowledged for '${resultPath}'.`);
104
+ }
105
+ }
106
+
43
107
  pi.events.emit(SUBAGENT_ASYNC_COMPLETE_EVENT, data);
44
108
  fs.unlinkSync(resultPath);
45
109
  } catch (error) {
@@ -48,7 +112,9 @@ export function createResultWatcher(
48
112
  }
49
113
  };
50
114
 
51
- state.resultFileCoalescer = createFileCoalescer(handleResult, 50);
115
+ state.resultFileCoalescer = createFileCoalescer((file) => {
116
+ void handleResult(file);
117
+ }, 50);
52
118
 
53
119
  const scheduleRestart = () => {
54
120
  state.watcherRestartTimer = setTimeout(() => {
@@ -16,7 +16,7 @@ agents into a workflow, or create/edit agents and chains on demand.
16
16
  ## When to Use
17
17
 
18
18
  - **Advisory review**: use fresh-context `reviewer` agents for adversarial code review, or fork to `oracle` when inherited decisions and drift matter
19
- - **Implementation handoff**: have `oracle` advise, then `oracle-executor` or `worker` implement only after an approved direction
19
+ - **Implementation handoff**: have `oracle` advise, then `worker` implement only after an approved direction
20
20
  - **Recon and planning**: use `scout` or `context-builder`, then `planner`
21
21
  - **Parallel exploration**: run multiple non-conflicting tasks concurrently
22
22
  - **Long-running work**: launch async/background runs and inspect them later
@@ -43,7 +43,6 @@ Packaged prompt shortcuts are also available for repeatable workflows:
43
43
  - `/parallel-review` — fresh-context reviewers with distinct review angles, then synthesis
44
44
  - `/parallel-research` — combine `researcher` and `scout` for external evidence plus local code context
45
45
  - `/gather-context-and-clarify` — scout/research first, then ask the user clarifying questions with `interview`
46
- - `/oracle-executor` — send an explicitly approved implementation task to `oracle-executor`
47
46
 
48
47
  ## Builtin Agents
49
48
 
@@ -54,13 +53,12 @@ and user/project agents override builtins with the same name.
54
53
  |-------|---------|-------|------------------------|
55
54
  | `scout` | Fast codebase recon | `openai-codex/gpt-5.4-mini` | Writes `context.md` handoff material |
56
55
  | `planner` | Creates implementation plans | `openai-codex/gpt-5.5` | Writes `plan.md` |
57
- | `worker` | General implementation | `openai-codex/gpt-5.5` | Edits code directly |
56
+ | `worker` | Implementation and approved oracle handoffs | `openai-codex/gpt-5.3-codex` | Single-writer implementation with decision escalation |
58
57
  | `reviewer` | Review-and-fix specialist | `openai-codex/gpt-5.5` | Can edit/fix reviewed code |
59
58
  | `context-builder` | Requirements/codebase handoff builder | `openai-codex/gpt-5.5` | Writes structured context files |
60
59
  | `researcher` | Web research brief generator | `openai-codex/gpt-5.5` | Writes `research.md` |
61
60
  | `delegate` | Lightweight generic delegate | inherits parent model | No fixed output; generic delegated work |
62
61
  | `oracle` | Decision-consistency advisory review | `openai-codex/gpt-5.5` | Advisory review, intercom coordination |
63
- | `oracle-executor` | Implementation after approval | `openai-codex/gpt-5.5` | Single-writer implementation after approval |
64
62
 
65
63
  Override builtin defaults before copying full agent files when a small tweak is enough.
66
64
 
@@ -282,7 +280,7 @@ The intended oracle loop is:
282
280
  2. `oracle` reviews direction, drift, assumptions, and risks
283
281
  3. `oracle` can coordinate back to the orchestrator via `intercom`
284
282
  4. the main agent decides what direction to approve
285
- 5. only then should `oracle-executor` implement
283
+ 5. only then should `worker` implement
286
284
 
287
285
  ```typescript
288
286
  // Advisory review in a branched thread
@@ -294,7 +292,7 @@ subagent({
294
292
 
295
293
  // Implementation only after explicit approval
296
294
  subagent({
297
- agent: "oracle-executor",
295
+ agent: "worker",
298
296
  task: "Implement the approved approach: ...",
299
297
  context: "fork"
300
298
  })
@@ -319,7 +317,7 @@ Use `intercom` when:
319
317
  Message conventions:
320
318
  - `ask` means the child needs a decision or clarification from the parent session.
321
319
  - `send` means a short blocked/progress update, only when blocked or explicitly asked.
322
- - Routine completion does not go through intercom. The normal subagent result still returns through `pi-subagents`.
320
+ - Child-side routine completion handoffs are not expected. With the intercom bridge active, parent-side `pi-subagents` sends grouped completion results through `pi-intercom`: one grouped message per foreground parent run and one per completed async result file. Acknowledged foreground delivery returns a compact receipt with artifact/session paths; if unacknowledged, the normal full output is preserved. Grouped messages include child intercom targets and full child summaries.
323
321
 
324
322
  If a bridge target is available, a child can ask:
325
323
 
@@ -426,9 +424,8 @@ copying a full builtin file.
426
424
  ## Prompt Template Integration
427
425
 
428
426
  The package includes prompt shortcuts for common workflows: `/parallel-review`,
429
- `/parallel-research`, `/gather-context-and-clarify`, and `/oracle-executor`.
430
- Use them when the user wants repeatable review, research, clarification, or
431
- approved execution patterns.
427
+ `/parallel-research`, and `/gather-context-and-clarify`. Use them when the user
428
+ wants repeatable review, research, or clarification patterns.
432
429
 
433
430
  If `pi-prompt-template-model` is installed, additional user prompt templates can delegate into
434
431
  `pi-subagents`. This is useful when a slash command should always run through a
@@ -453,8 +450,7 @@ particular agent or with forked context.
453
450
  ### Keep writes single-threaded by default
454
451
 
455
452
  A strong pattern is one main decision-maker plus advisory/research/review
456
- subagents around it. Use `oracle` for advice and `oracle-executor` or `worker`
457
- for the actual write path.
453
+ subagents around it. Use `oracle` for advice and `worker` for the actual write path.
458
454
 
459
455
  ### Use fork for branched advisory or execution threads
460
456
 
@@ -32,6 +32,13 @@ import { applyIntercomBridgeToAgent, INTERCOM_BRIDGE_MARKER, resolveIntercomBrid
32
32
  import { formatControlIntercomMessage, formatControlNoticeMessage, resolveControlConfig, shouldNotifyControlEvent } from "./subagent-control.ts";
33
33
  import { finalizeSingleOutput, injectSingleOutputInstruction, resolveSingleOutputPath } from "./single-output.ts";
34
34
  import { compactForegroundDetails, getSingleResultOutput, mapConcurrent, readStatus, resolveChildCwd } from "./utils.ts";
35
+ import {
36
+ buildSubagentResultIntercomPayload,
37
+ deliverSubagentResultIntercomEvent,
38
+ formatSubagentResultReceipt,
39
+ resolveSubagentResultStatus,
40
+ stripDetailsOutputsForIntercomReceipt,
41
+ } from "./result-intercom.ts";
35
42
  import { inspectSubagentStatus } from "./run-status.ts";
36
43
  import { applyForceTopLevelAsyncOverride } from "./top-level-async.ts";
37
44
  import {
@@ -261,6 +268,64 @@ function createForegroundControlNotifier(data: Pick<ExecutionContextData, "contr
261
268
  });
262
269
  }
263
270
 
271
+ async function emitForegroundResultIntercom(input: {
272
+ pi: ExtensionAPI;
273
+ intercomBridge: IntercomBridgeState;
274
+ runId: string;
275
+ mode: "single" | "parallel" | "chain";
276
+ results: SingleResult[];
277
+ chainSteps?: number;
278
+ }): Promise<ReturnType<typeof buildSubagentResultIntercomPayload> | null> {
279
+ if (!input.intercomBridge.active || !input.intercomBridge.orchestratorTarget) return null;
280
+ const children = input.results.flatMap((result, index) => result.detached ? [] : [{
281
+ agent: result.agent,
282
+ status: resolveSubagentResultStatus({
283
+ exitCode: result.exitCode,
284
+ interrupted: result.interrupted,
285
+ detached: result.detached,
286
+ }),
287
+ summary: getSingleResultOutput(result) || result.error || "(no output)",
288
+ index,
289
+ artifactPath: result.artifactPaths?.outputPath,
290
+ sessionPath: result.sessionFile,
291
+ intercomTarget: resolveSubagentIntercomTarget(input.runId, result.agent, index),
292
+ }]);
293
+ if (children.length === 0) return null;
294
+ const payload = buildSubagentResultIntercomPayload({
295
+ to: input.intercomBridge.orchestratorTarget,
296
+ runId: input.runId,
297
+ mode: input.mode,
298
+ source: "foreground",
299
+ children,
300
+ ...(typeof input.chainSteps === "number" ? { chainSteps: input.chainSteps } : {}),
301
+ });
302
+ const delivered = await deliverSubagentResultIntercomEvent(input.pi.events, payload);
303
+ if (!delivered) return null;
304
+ return payload;
305
+ }
306
+
307
+ async function maybeBuildForegroundIntercomReceipt(input: {
308
+ pi: ExtensionAPI;
309
+ intercomBridge: IntercomBridgeState;
310
+ runId: string;
311
+ mode: "single" | "parallel" | "chain";
312
+ details: Details;
313
+ }): Promise<{ text: string; details: Details } | null> {
314
+ const payload = await emitForegroundResultIntercom({
315
+ pi: input.pi,
316
+ intercomBridge: input.intercomBridge,
317
+ runId: input.runId,
318
+ mode: input.mode,
319
+ results: input.details.results,
320
+ ...(typeof input.details.totalSteps === "number" ? { chainSteps: input.details.totalSteps } : {}),
321
+ });
322
+ if (!payload) return null;
323
+ return {
324
+ text: formatSubagentResultReceipt({ mode: input.mode, runId: input.runId, payload }),
325
+ details: stripDetailsOutputsForIntercomReceipt(input.details),
326
+ };
327
+ }
328
+
264
329
  function validateExecutionInput(
265
330
  params: SubagentParamsLike,
266
331
  agents: AgentConfig[],
@@ -563,6 +628,7 @@ function runAsyncPath(data: ExecutionContextData, deps: ExecutorDeps): AgentTool
563
628
  concurrency: resolveTopLevelParallelConcurrency(params.concurrency, deps.config.parallel?.concurrency),
564
629
  worktree: params.worktree,
565
630
  }],
631
+ resultMode: "parallel",
566
632
  agents,
567
633
  ctx: asyncCtx,
568
634
  availableModels,
@@ -746,6 +812,24 @@ async function runChainPath(data: ExecutionContextData, deps: ExecutorDeps): Pro
746
812
  });
747
813
  }
748
814
 
815
+ const chainDetails = chainResult.details ? compactForegroundDetails(chainResult.details) : undefined;
816
+ const intercomReceipt = chainDetails && !chainDetails.results.some((result) => result.interrupted)
817
+ ? await maybeBuildForegroundIntercomReceipt({
818
+ pi: deps.pi,
819
+ intercomBridge: data.intercomBridge,
820
+ runId,
821
+ mode: "chain",
822
+ details: chainDetails,
823
+ })
824
+ : null;
825
+ if (intercomReceipt) {
826
+ return {
827
+ ...chainResult,
828
+ content: [{ type: "text", text: intercomReceipt.text }],
829
+ details: intercomReceipt.details,
830
+ };
831
+ }
832
+
749
833
  return chainResult;
750
834
  }
751
835
 
@@ -1120,6 +1204,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
1120
1204
  }));
1121
1205
  return executeAsyncChain(id, {
1122
1206
  chain: [{ parallel: parallelTasks, concurrency: parallelConcurrency, worktree: params.worktree }],
1207
+ resultMode: "parallel",
1123
1208
  agents,
1124
1209
  ctx: asyncCtx,
1125
1210
  availableModels,
@@ -1216,15 +1301,30 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
1216
1301
  }
1217
1302
 
1218
1303
  const interrupted = results.find((result) => result.interrupted);
1304
+ const details = compactForegroundDetails({
1305
+ mode: "parallel",
1306
+ results,
1307
+ progress: params.includeProgress ? allProgress : undefined,
1308
+ artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
1309
+ });
1219
1310
  if (interrupted) {
1220
1311
  return {
1221
1312
  content: [{ type: "text", text: `Parallel run paused after interrupt (${interrupted.agent}). Waiting for explicit next action.` }],
1222
- details: compactForegroundDetails({
1223
- mode: "parallel",
1224
- results,
1225
- progress: params.includeProgress ? allProgress : undefined,
1226
- artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
1227
- }),
1313
+ details,
1314
+ };
1315
+ }
1316
+
1317
+ const intercomReceipt = await maybeBuildForegroundIntercomReceipt({
1318
+ pi: deps.pi,
1319
+ intercomBridge: data.intercomBridge,
1320
+ runId,
1321
+ mode: "parallel",
1322
+ details,
1323
+ });
1324
+ if (intercomReceipt) {
1325
+ return {
1326
+ content: [{ type: "text", text: intercomReceipt.text }],
1327
+ details: intercomReceipt.details,
1228
1328
  };
1229
1329
  }
1230
1330
 
@@ -1248,12 +1348,7 @@ async function runParallelPath(data: ExecutionContextData, deps: ExecutorDeps):
1248
1348
 
1249
1349
  return {
1250
1350
  content: [{ type: "text", text: fullContent }],
1251
- details: compactForegroundDetails({
1252
- mode: "parallel",
1253
- results,
1254
- progress: params.includeProgress ? allProgress : undefined,
1255
- artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
1256
- }),
1351
+ details,
1257
1352
  };
1258
1353
  } finally {
1259
1354
  if (worktreeSetup) cleanupWorktrees(worktreeSetup);
@@ -1472,54 +1567,54 @@ async function runSinglePath(data: ExecutionContextData, deps: ExecutorDeps): Pr
1472
1567
  savedPath: r.savedOutputPath,
1473
1568
  saveError: r.outputSaveError,
1474
1569
  });
1570
+ const details = compactForegroundDetails({
1571
+ mode: "single",
1572
+ results: [r],
1573
+ progress: params.includeProgress ? allProgress : undefined,
1574
+ artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
1575
+ truncation: r.truncation,
1576
+ });
1577
+
1578
+ if (!r.detached && !r.interrupted) {
1579
+ const intercomReceipt = await maybeBuildForegroundIntercomReceipt({
1580
+ pi: deps.pi,
1581
+ intercomBridge: data.intercomBridge,
1582
+ runId,
1583
+ mode: "single",
1584
+ details,
1585
+ });
1586
+ if (intercomReceipt) {
1587
+ return {
1588
+ content: [{ type: "text", text: intercomReceipt.text }],
1589
+ details: intercomReceipt.details,
1590
+ ...(r.exitCode !== 0 ? { isError: true } : {}),
1591
+ };
1592
+ }
1593
+ }
1475
1594
 
1476
1595
  if (r.detached) {
1477
1596
  return {
1478
1597
  content: [{ type: "text", text: `Detached for intercom coordination: ${params.agent}` }],
1479
- details: compactForegroundDetails({
1480
- mode: "single",
1481
- results: [r],
1482
- progress: params.includeProgress ? allProgress : undefined,
1483
- artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
1484
- truncation: r.truncation,
1485
- }),
1598
+ details,
1486
1599
  };
1487
1600
  }
1488
1601
 
1489
1602
  if (r.interrupted) {
1490
1603
  return {
1491
1604
  content: [{ type: "text", text: `Run paused after interrupt (${params.agent}). Waiting for explicit next action.` }],
1492
- details: compactForegroundDetails({
1493
- mode: "single",
1494
- results: [r],
1495
- progress: params.includeProgress ? allProgress : undefined,
1496
- artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
1497
- truncation: r.truncation,
1498
- }),
1605
+ details,
1499
1606
  };
1500
1607
  }
1501
1608
 
1502
1609
  if (r.exitCode !== 0)
1503
1610
  return {
1504
1611
  content: [{ type: "text", text: r.error || "Failed" }],
1505
- details: compactForegroundDetails({
1506
- mode: "single",
1507
- results: [r],
1508
- progress: params.includeProgress ? allProgress : undefined,
1509
- artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
1510
- truncation: r.truncation,
1511
- }),
1612
+ details,
1512
1613
  isError: true,
1513
1614
  };
1514
1615
  return {
1515
1616
  content: [{ type: "text", text: finalizedOutput.displayOutput || "(no output)" }],
1516
- details: compactForegroundDetails({
1517
- mode: "single",
1518
- results: [r],
1519
- progress: params.includeProgress ? allProgress : undefined,
1520
- artifacts: allArtifactPaths.length ? { dir: artifactsDir, files: allArtifactPaths } : undefined,
1521
- truncation: r.truncation,
1522
- }),
1617
+ details,
1523
1618
  };
1524
1619
  }
1525
1620
 
@@ -75,6 +75,7 @@ interface SubagentRunConfig {
75
75
  controlConfig?: ResolvedControlConfig;
76
76
  controlIntercomTarget?: string;
77
77
  childIntercomTargets?: Array<string | undefined>;
78
+ resultMode?: "single" | "parallel" | "chain";
78
79
  }
79
80
 
80
81
  interface StepResult {
@@ -82,6 +83,7 @@ interface StepResult {
82
83
  output: string;
83
84
  success: boolean;
84
85
  skipped?: boolean;
86
+ intercomTarget?: string;
85
87
  model?: string;
86
88
  attemptedModels?: string[];
87
89
  modelAttempts?: ModelAttempt[];
@@ -542,6 +544,7 @@ async function runSingleStep(
542
544
  modelAttempts?: ModelAttempt[];
543
545
  artifactPaths?: ArtifactPaths;
544
546
  interrupted?: boolean;
547
+ intercomTarget?: string;
545
548
  }> {
546
549
  const placeholderRegex = new RegExp(ctx.placeholder.replace(/[.*+?^${}()|[\]\\]/g, "\\$&"), "g");
547
550
  const task = step.task.replace(placeholderRegex, () => ctx.previousOutput);
@@ -671,6 +674,7 @@ async function runSingleStep(
671
674
  output: outputForSummary,
672
675
  exitCode: finalResult?.exitCode ?? 1,
673
676
  error: finalResult?.error,
677
+ intercomTarget: ctx.childIntercomTarget,
674
678
  model: finalResult?.model,
675
679
  attemptedModels: attemptedModels.length > 0 ? attemptedModels : undefined,
676
680
  modelAttempts,
@@ -1147,6 +1151,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
1147
1151
  output: pr.output,
1148
1152
  success: pr.exitCode === 0,
1149
1153
  skipped: pr.skipped,
1154
+ intercomTarget: pr.intercomTarget,
1150
1155
  model: pr.model,
1151
1156
  attemptedModels: pr.attemptedModels,
1152
1157
  modelAttempts: pr.modelAttempts,
@@ -1225,6 +1230,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
1225
1230
  agent: singleResult.agent,
1226
1231
  output: singleResult.output,
1227
1232
  success: singleResult.exitCode === 0,
1233
+ intercomTarget: singleResult.intercomTarget,
1228
1234
  model: singleResult.model,
1229
1235
  attemptedModels: singleResult.attemptedModels,
1230
1236
  modelAttempts: singleResult.modelAttempts,
@@ -1386,6 +1392,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
1386
1392
  writeJson(resultPath, {
1387
1393
  id,
1388
1394
  agent: agentName,
1395
+ mode: config.resultMode ?? statusPayload.mode,
1389
1396
  success: !interrupted && results.every((r) => r.success),
1390
1397
  state: interrupted ? "paused" : results.every((r) => r.success) ? "complete" : "failed",
1391
1398
  summary: interrupted ? "Paused after interrupt. Waiting for explicit next action." : summary,
@@ -1394,6 +1401,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
1394
1401
  output: r.output,
1395
1402
  success: r.success,
1396
1403
  skipped: r.skipped || undefined,
1404
+ intercomTarget: r.intercomTarget,
1397
1405
  model: r.model,
1398
1406
  attemptedModels: r.attemptedModels,
1399
1407
  modelAttempts: r.modelAttempts,
@@ -1409,6 +1417,7 @@ async function runSubagent(config: SubagentRunConfig): Promise<void> {
1409
1417
  asyncDir,
1410
1418
  sessionId: config.sessionId,
1411
1419
  sessionFile: effectiveSessionFile,
1420
+ intercomTarget: config.controlIntercomTarget,
1412
1421
  shareUrl,
1413
1422
  gistUrl,
1414
1423
  shareError,
package/types.ts CHANGED
@@ -69,6 +69,37 @@ export interface ControlEvent {
69
69
  message: string;
70
70
  }
71
71
 
72
+ export type SubagentResultStatus = "completed" | "failed" | "paused" | "detached";
73
+
74
+ export interface SubagentResultIntercomChild {
75
+ agent: string;
76
+ status: SubagentResultStatus;
77
+ summary: string;
78
+ index?: number;
79
+ artifactPath?: string;
80
+ sessionPath?: string;
81
+ intercomTarget?: string;
82
+ }
83
+
84
+ export interface SubagentResultIntercomPayload {
85
+ to: string;
86
+ message: string;
87
+ requestId?: string;
88
+ runId: string;
89
+ mode: "single" | "parallel" | "chain";
90
+ status: SubagentResultStatus;
91
+ summary: string;
92
+ source: "foreground" | "async";
93
+ children: SubagentResultIntercomChild[];
94
+ asyncId?: string;
95
+ asyncDir?: string;
96
+ chainSteps?: number;
97
+ agent?: string;
98
+ index?: number;
99
+ artifactPath?: string;
100
+ sessionPath?: string;
101
+ }
102
+
72
103
  // ============================================================================
73
104
  // Progress Tracking
74
105
  // ============================================================================
@@ -310,6 +341,8 @@ export const SUBAGENT_ASYNC_STARTED_EVENT = "subagent:async-started";
310
341
  export const SUBAGENT_ASYNC_COMPLETE_EVENT = "subagent:async-complete";
311
342
  export const SUBAGENT_CONTROL_EVENT = "subagent:control-event";
312
343
  export const SUBAGENT_CONTROL_INTERCOM_EVENT = "subagent:control-intercom";
344
+ export const SUBAGENT_RESULT_INTERCOM_EVENT = "subagent:result-intercom";
345
+ export const SUBAGENT_RESULT_INTERCOM_DELIVERY_EVENT = "subagent:result-intercom-delivery";
313
346
 
314
347
  // ============================================================================
315
348
  // Execution Options
@@ -1,49 +0,0 @@
1
- ---
2
- name: oracle-executor
3
- description: High-context implementation agent that executes only after main-agent approval
4
- tools: read, grep, find, ls, bash, edit, write, intercom
5
- model: openai-codex/gpt-5.5
6
- thinking: high
7
- systemPromptMode: replace
8
- inheritProjectContext: true
9
- inheritSkills: false
10
- defaultProgress: true
11
- ---
12
-
13
- You are `oracle-executor`: a high-context implementation subagent.
14
-
15
- You are the single writer thread. Your job is to execute approved direction, not to make new architectural or product decisions.
16
-
17
- You are invoked after the main agent has already decided on a direction, often based on advice from `oracle`. You are allowed to act, but you are not the owner of product or architecture decisions. The main agent remains the final decision authority.
18
-
19
- If runtime bridge instructions are present, use them as the source of truth for which orchestrator session to contact and how to coordinate. Use `intercom({ action: "ask", ... })` when a new decision is needed to continue safely. Use `intercom({ action: "send", ... })` for concise progress or completion handoffs when that extra coordination is helpful.
20
-
21
- First understand the inherited context and the explicit task. Then execute carefully and minimally.
22
-
23
- If the task appears to require a new decision that has not clearly been approved by the main agent, stop and ask via `intercom` instead of making that decision yourself.
24
-
25
- Default responsibilities:
26
- - validate the approved direction against the actual code
27
- - implement the approved change with minimal, coherent edits
28
- - verify the result with appropriate checks
29
- - report back clearly, including risks and next steps
30
-
31
- Working rules:
32
- - Follow existing patterns in the codebase.
33
- - Prefer narrow, correct changes over broad rewrites.
34
- - Do not add speculative scaffolding or future-proofing unless explicitly required.
35
- - Use `bash` for inspection, validation, and relevant tests.
36
- - Escalate uncertainty to the main agent with `intercom` when needed.
37
- - If the implementation reveals a gap in the approved direction, pause and escalate via `intercom` rather than silently patching around it with an implicit decision.
38
- - If implementation reveals an unapproved product or architecture choice, pause and ask via `intercom` instead of deciding it yourself.
39
- - If you send a completion handoff through `intercom`, keep it short and still return the full structured task result normally.
40
- - Keep `progress.md` accurate when asked to maintain it.
41
- - Do not silently change the scope of the task.
42
-
43
- Your completion handoff should follow this exact shape:
44
-
45
- Implemented X.
46
- Changed files: Y.
47
- Validation: Z.
48
- Open risks/questions: R.
49
- Recommended next step: N.
@@ -1,9 +0,0 @@
1
- ---
2
- description: Send an explicitly approved task to oracle-executor with full inherited context.
3
- subagent: oracle-executor
4
- inheritContext: true
5
- ---
6
-
7
- Launch the oracle-executor subagent with a strict implementation meta prompt that points it at the plan and asks it to execute:
8
-
9
- $@