@zachwill/pi-orchestrate 0.16.0 → 0.17.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/README.md +15 -17
- package/examples/workers/investigator.md +2 -1
- package/examples/workers/scout.md +1 -0
- package/examples/workers/web.md +2 -2
- package/examples/workers/worker.md +1 -0
- package/extension/index.ts +1 -1
- package/extension/orchestration/service.ts +6 -7
- package/extension/parent/contract.ts +19 -28
- package/extension/parent/delivery.ts +1 -1
- package/extension/parent/dispatch-policy.ts +12 -22
- package/extension/parent/worker-context.ts +2 -2
- package/extension/pi/tools.ts +26 -103
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,18 +1,26 @@
|
|
|
1
1
|
# Pi Orchestrate
|
|
2
2
|
|
|
3
|
-
[`@zachwill/pi-orchestrate`](https://www.npmjs.com/package/@zachwill/pi-orchestrate) lets a Pi session delegate work to direct child sessions.
|
|
3
|
+
[`@zachwill/pi-orchestrate`](https://www.npmjs.com/package/@zachwill/pi-orchestrate) lets a Pi session delegate work to direct child sessions.
|
|
4
4
|
|
|
5
5
|
- Each worker gets a focused brief and a separate conversation.
|
|
6
6
|
- Workers run independently and return their results to the parent.
|
|
7
7
|
- Only the parent can delegate; workers cannot create more workers.
|
|
8
8
|
|
|
9
|
+
Requires Pi 0.85.0 or newer.
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
pi install npm:@zachwill/pi-orchestrate
|
|
13
|
+
```
|
|
14
|
+
|
|
9
15
|
## The model
|
|
10
16
|
|
|
11
17
|
Each `orchestrate` dispatch creates a fresh worker session with its own transcript. The worker receives a complete brief from the parent but not the parent's conversation.
|
|
12
18
|
|
|
13
|
-
A worker definition is reusable configuration
|
|
19
|
+
A worker definition is reusable configuration for the worker's prompt, tools, lifecycle, and optional model settings. The parent remains responsible for the user's complete requested outcome across its own and worker work.
|
|
14
20
|
|
|
15
|
-
|
|
21
|
+
Workers run in the background. `orchestrate` and `interactive_send` return acceptance so the parent can continue useful independent work, including tool calls dispatched alongside the workers. Worker dispatches in the same response form one result group; dispatches in later responses form separate groups.
|
|
22
|
+
|
|
23
|
+
Results return only to the parent session that started the workers. Results that settle while the parent is busy are queued. After the parent run ends normally, individual results can enter its context as they arrive; a dispatch group resumes the parent after every admitted member settles. The parent can do useful independent work before ending its run, or end promptly when progress needs worker evidence. A rejected, failed, or aborted worker does not cancel its peers; stopping other active workers requires an explicit `worker_abort` request.
|
|
16
24
|
|
|
17
25
|
Workers have one of two lifecycles:
|
|
18
26
|
|
|
@@ -23,13 +31,7 @@ A **worker ID** identifies a worker session. A **run ID** identifies one generat
|
|
|
23
31
|
|
|
24
32
|
Interactive workers remain available across session switches and extension reloads within the same Pi process. Closing one releases its retained session; process shutdown releases any that remain.
|
|
25
33
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
Parent compaction does not stop workers. Results that settle during compaction remain queued for the owning session and resume automatic delivery when that session becomes idle, including after failed or cancelled compaction. Grouped work still triggers synthesis only after the group settles.
|
|
29
|
-
|
|
30
|
-
Before each parent model request, the extension adds a fresh, owner-scoped snapshot of active workers and ready interactive sessions. This transient context survives compaction by being rebuilt from live state; it is not appended to the transcript. The snapshot is capped at 12 KiB and reports omitted workers or truncated assignments, with `worker_status` available for recovery rather than polling.
|
|
31
|
-
|
|
32
|
-
This state belongs to the running Pi process. Compaction does not require restarting workers, and persisted transcripts do not restore running workers after a process restart.
|
|
34
|
+
Workers keep running when the parent conversation is compacted, and their results return automatically. Restarting Pi does not restore running workers.
|
|
33
35
|
|
|
34
36
|
## Agent interface
|
|
35
37
|
|
|
@@ -43,11 +45,11 @@ Pi Orchestrate gives the parent five model-facing tools:
|
|
|
43
45
|
| `worker_abort` | Stop active workers owned by the parent |
|
|
44
46
|
| `worker_status` | Inspect the trusted catalog and diagnose the parent's worker state |
|
|
45
47
|
|
|
46
|
-
The extension supplies the parent with
|
|
48
|
+
The extension supplies the parent with instructions for using these tools.
|
|
47
49
|
|
|
48
50
|
## Worker definitions
|
|
49
51
|
|
|
50
|
-
The package includes four fallback definitions in [`examples/workers/`](examples/workers/): `scout` for small factual probes, `investigator` for read-only cross-file research, `worker` for bounded implementation, and `web` for public web research. The
|
|
52
|
+
The package includes four fallback definitions in [`examples/workers/`](examples/workers/): `scout` for small factual probes, `investigator` for read-only cross-file research, `worker` for bounded implementation, and `web` for public web research. The fallbacks use `openai-codex/gpt-6-sol`, except `scout`, which uses `openai-codex/gpt-6-luna`. The `web` worker also requires an installed, authenticated Codex CLI.
|
|
51
53
|
|
|
52
54
|
Definitions are loaded by name in this precedence order:
|
|
53
55
|
|
|
@@ -85,8 +87,6 @@ The frontmatter is strict:
|
|
|
85
87
|
|
|
86
88
|
The Markdown body is the worker's nonempty system prompt. `tools` and `skills` accept either YAML arrays or comma-separated strings. Supported Pi tools are `read`, `bash`, `edit`, `write`, `grep`, `find`, and `ls`. Unknown fields and malformed definitions are rejected and appear in catalog diagnostics.
|
|
87
89
|
|
|
88
|
-
Each `orchestrate` call starts a new worker session from the selected definition. Only `interactive_send` continues an existing session.
|
|
89
|
-
|
|
90
90
|
## Trust boundary
|
|
91
91
|
|
|
92
92
|
Workers run in the parent process and are not security sandboxes. They share its filesystem and environment permissions.
|
|
@@ -95,6 +95,4 @@ Workers can use global Pi settings, authentication, packages, extensions, skills
|
|
|
95
95
|
|
|
96
96
|
A definition's `tools` field controls Pi's tool allowlist, not operating-system authority. A worker with `bash` can start external processes, including other agent CLIs. A read-only prompt also does not prevent writes when the worker has a write-capable tool.
|
|
97
97
|
|
|
98
|
-
Concurrent workers share the same working tree, so overlapping write scopes can collide.
|
|
99
|
-
|
|
100
|
-
Pi Orchestrate excludes itself from child sessions and keeps workers as direct Pi children.
|
|
98
|
+
Concurrent workers share the same working tree, so overlapping write scopes can collide.
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: investigator
|
|
3
3
|
description: Investigates cross-file questions through read-only inspection and evidence-based synthesis.
|
|
4
|
-
|
|
4
|
+
model: openai-codex/gpt-6-sol
|
|
5
|
+
thinking: high
|
|
5
6
|
tools: read, grep, find, ls, bash
|
|
6
7
|
lifecycle: one-shot
|
|
7
8
|
---
|
package/examples/workers/web.md
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: web
|
|
3
3
|
description: Researches the public web with one or more Codex searches and returns a source-grounded synthesis.
|
|
4
|
-
model: openai-codex/gpt-
|
|
4
|
+
model: openai-codex/gpt-6-sol
|
|
5
5
|
thinking: medium
|
|
6
6
|
tools: bash
|
|
7
7
|
skills: []
|
|
@@ -31,7 +31,7 @@ stderr_log="$work_dir/stderr.log"
|
|
|
31
31
|
|
|
32
32
|
codex exec - \
|
|
33
33
|
--ignore-user-config \
|
|
34
|
-
--model gpt-
|
|
34
|
+
--model gpt-6-sol \
|
|
35
35
|
-c 'model_reasoning_effort="medium"' \
|
|
36
36
|
-c 'web_search="cached"' \
|
|
37
37
|
--ephemeral \
|
package/extension/index.ts
CHANGED
|
@@ -84,7 +84,7 @@ export function createOrchestrationExtension(
|
|
|
84
84
|
orchestration: host.orchestration,
|
|
85
85
|
getCatalog: catalogFor,
|
|
86
86
|
getDispatchDecision: (toolCallId) =>
|
|
87
|
-
dispatchDecisions.get(toolCallId) ?? {
|
|
87
|
+
dispatchDecisions.get(toolCallId) ?? {},
|
|
88
88
|
});
|
|
89
89
|
hostAttachment ??= attachProcessHost(host);
|
|
90
90
|
|
|
@@ -376,14 +376,13 @@ class OrchestrationEngine implements OrchestrationService {
|
|
|
376
376
|
const runId = this.idFactories.runId();
|
|
377
377
|
const completion = yield* Deferred.make<CompletedRun>();
|
|
378
378
|
const now = this.clock.currentTimeMillisUnsafe();
|
|
379
|
-
const runRecord
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
379
|
+
const runRecord = makeRunRecord(
|
|
380
|
+
runId,
|
|
381
|
+
validatedWorkerId,
|
|
382
|
+
context,
|
|
383
383
|
mode,
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
};
|
|
384
|
+
now,
|
|
385
|
+
);
|
|
387
386
|
|
|
388
387
|
const admission = this.transact((draft) => {
|
|
389
388
|
const ready = readyInteractiveDecision(
|
|
@@ -96,44 +96,35 @@ function buildContract(catalog: WorkerCatalog): string {
|
|
|
96
96
|
return `${CONTRACT_START}
|
|
97
97
|
## Pi Orchestrate Contract
|
|
98
98
|
|
|
99
|
-
You
|
|
99
|
+
You own the user’s outcome across parent and worker work. Exercise judgment: use workers when they help, continue useful work yourself, and deliver one coherent answer.
|
|
100
100
|
|
|
101
|
-
###
|
|
101
|
+
### Outcome and scope
|
|
102
102
|
|
|
103
|
-
-
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
- Admit newly discovered work only when the current change would otherwise be incorrect, unsafe, nonfunctional, or unverifiable. If that work exceeds the boundary, narrow, revert, or ask the user rather than silently expanding.
|
|
107
|
-
- Do not introduce cross-feature policy, infrastructure, deployment, or compatibility work unless the request or an unavoidable requirement of the current change calls for it.
|
|
108
|
-
- Completed worker effort does not justify retaining an overgrown change set.
|
|
103
|
+
- Preserve the requested scope, completeness, and form. Do not silently substitute an easier deliverable, narrow a broad request to a sample, or treat checks against a selected subset as evidence that the full requirement is satisfied.
|
|
104
|
+
- Do the investigation and implementation needed for the outcome, but do not invent adjacent deliverables, policies, or cleanup. Ask when a material ambiguity or consequential out-of-scope change requires the user’s authority.
|
|
105
|
+
- Assign write ownership so concurrent scopes are disjoint. Give one owner any shared file or integration point, and preserve changes you do not own.
|
|
109
106
|
|
|
110
107
|
### Delegation
|
|
111
108
|
|
|
112
|
-
- Delegate
|
|
113
|
-
-
|
|
114
|
-
-
|
|
115
|
-
-
|
|
109
|
+
- Delegate separable work when the expected improvement in quality or latency is worth the coordination cost. Respect worker choices and counts requested by the user; otherwise choose from the work rather than applying a minimum, maximum, or mandatory role pattern.
|
|
110
|
+
- Give each worker enough context to own one outcome and scope boundary, including relevant constraints, owned paths, and consequential evidence checks. Workers do not receive the parent conversation. Have them report out-of-scope findings instead of fixing them.
|
|
111
|
+
- **Important:** When independent worker assignments are ready, dispatch them using one \`multi_tool_use.parallel\` call containing every \`functions.orchestrate\` call. Do not dispatch one worker first and batch the rest afterward.
|
|
112
|
+
- Each \`orchestrate\` call creates a fresh worker session. Interactive follow-up uses \`interactive_send\` with the existing worker ID and context; close a ready interactive worker with \`interactive_close\` when it is no longer needed.
|
|
116
113
|
|
|
117
|
-
###
|
|
114
|
+
### Dispatch and dependencies
|
|
118
115
|
|
|
119
|
-
-
|
|
120
|
-
-
|
|
121
|
-
-
|
|
122
|
-
-
|
|
123
|
-
-
|
|
116
|
+
- \`orchestrate\` and \`interactive_send\` start background work and return acceptance without ending the parent run, including alongside other tools. Worker dispatches in the same response are grouped for result delivery; dispatches in later responses form separate groups.
|
|
117
|
+
- After dispatch, continue useful independent work. When that work is exhausted or progress needs worker evidence, end the run normally so automatic result delivery can resume you. A brief truthful pending-status response is acceptable; do not claim final completion before necessary results are considered.
|
|
118
|
+
- Do not poll \`worker_status\`, sleep, duplicate active assignments, or invent work while results are pending. Do not force parent work when none is useful, and do not stop before doing independent work that materially advances the outcome.
|
|
119
|
+
- Results that settle while the parent is busy are queued and delivered when the parent run ends. Do not redispatch queued work.
|
|
120
|
+
- Compaction does not stop workers. Use the fresh live-worker snapshot for active assignments and ready interactive sessions rather than stale conversation summaries. Do not redispatch work because its dispatch was compacted away. Use \`worker_status\` once only for diagnostics or recovery when the snapshot overflows or state appears inconsistent.
|
|
124
121
|
|
|
125
122
|
### Completion
|
|
126
123
|
|
|
127
|
-
-
|
|
128
|
-
-
|
|
129
|
-
-
|
|
130
|
-
-
|
|
131
|
-
- Dispatch another wave only for admitted work inside the current change. Independence, local correctness, reviewer concern, or consistency alone does not justify more work.
|
|
132
|
-
- Inspect worker results and check the evidence behind consequential claims or changes. Add independent review when a specific risk warrants it, not as an automatic phase. Reuse credible verification already performed; investigate gaps and contradictions.
|
|
133
|
-
- Inspect the combined result and worker evidence, resolve disagreements, and accept, reduce, or discard the change. Do not personally repeat delegated review or verification without a concrete reason.
|
|
134
|
-
- Verification decides whether to accept the change; it is not a general source of new work. Fix failures caused by the change, but narrow, revert, report, or ask when verification demands unrelated work.
|
|
135
|
-
- Stop when the acceptance criteria pass. Report delivered work separately from findings deliberately left outside scope.
|
|
136
|
-
- Prefer one-shot workers. Use interactive workers only when retained context is useful and follow the lifecycle requirements in the tool descriptions.
|
|
124
|
+
- Treat worker reports as input, not the answer. Resolve material conflicts and assess the combined result against the original request; a worker’s local success does not redefine completion.
|
|
125
|
+
- Check consequential claims, changes, and failure modes with evidence suited to the task. Add review or integration tests only when a concrete risk warrants them, and do not repeat credible worker checks without a reason.
|
|
126
|
+
- Do not give the final answer until every worker result necessary to the outcome has been delivered and considered. Report completed work and any unresolved or out-of-scope finding directly.
|
|
127
|
+
- Use \`worker_abort\` only to stop active owned work. Ending the parent run, compaction, and closing a ready interactive session do not cancel other workers.
|
|
137
128
|
|
|
138
129
|
### Trusted worker catalog
|
|
139
130
|
|
|
@@ -7,7 +7,7 @@ export const MAX_WORKER_DELIVERY_MARKDOWN_BYTES = 16 * 1024;
|
|
|
7
7
|
export const DELIVERY_TRUNCATION_MARKER =
|
|
8
8
|
"\n\n[Worker result truncated for parent context. Full output remains in structured details.]";
|
|
9
9
|
export const DELIVERY_PARENT_INSTRUCTIONS =
|
|
10
|
-
"Parent:
|
|
10
|
+
"Parent: Use these results as input to the user's task. Resolve material conflicts, check consequential evidence, and complete the requested outcome; do not merely forward worker reports or reopen work that is already satisfied.";
|
|
11
11
|
|
|
12
12
|
export type ParentBindingGeneration = string | number | symbol;
|
|
13
13
|
export type ScheduleIdleRecheck = (recheck: () => void) => () => void;
|
|
@@ -6,7 +6,6 @@ export interface ParentToolCall {
|
|
|
6
6
|
}
|
|
7
7
|
|
|
8
8
|
export interface DispatchDecision {
|
|
9
|
-
readonly mode: "async" | "inline";
|
|
10
9
|
readonly synthesisGroup?: SynthesisGroup;
|
|
11
10
|
}
|
|
12
11
|
|
|
@@ -20,30 +19,21 @@ const DISPATCH_TOOL_NAMES: ReadonlySet<string> = new Set([
|
|
|
20
19
|
"interactive_send",
|
|
21
20
|
]);
|
|
22
21
|
|
|
23
|
-
//
|
|
24
|
-
//
|
|
25
|
-
//
|
|
26
|
-
// every admitted member has settled.
|
|
22
|
+
// Every public dispatch detaches so sibling parent tools can finish independently.
|
|
23
|
+
// Dispatches from one assistant response share a synthesis boundary; ordinary
|
|
24
|
+
// sibling tools are neither group members nor part of its expected size.
|
|
27
25
|
export function classifyParentDispatches(
|
|
28
26
|
toolCalls: readonly ParentToolCall[],
|
|
29
27
|
): readonly ClassifiedParentDispatch[] {
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
const synthesisGroup =
|
|
34
|
-
? { id: `
|
|
28
|
+
const dispatches = toolCalls.filter((toolCall) =>
|
|
29
|
+
DISPATCH_TOOL_NAMES.has(toolCall.name)
|
|
30
|
+
);
|
|
31
|
+
const synthesisGroup = dispatches.length > 1
|
|
32
|
+
? { id: `dispatch:${dispatches[0]!.id}`, size: dispatches.length }
|
|
35
33
|
: undefined;
|
|
36
34
|
|
|
37
|
-
return
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
decision: {
|
|
42
|
-
mode: isOrchestrateGroup || toolCalls.length === 1 ? "async" : "inline",
|
|
43
|
-
...(toolCall.name === "orchestrate" && synthesisGroup
|
|
44
|
-
? { synthesisGroup }
|
|
45
|
-
: {}),
|
|
46
|
-
},
|
|
47
|
-
}];
|
|
48
|
-
});
|
|
35
|
+
return dispatches.map((toolCall) => ({
|
|
36
|
+
toolCallId: toolCall.id,
|
|
37
|
+
decision: synthesisGroup ? { synthesisGroup } : {},
|
|
38
|
+
}));
|
|
49
39
|
}
|
|
@@ -146,7 +146,7 @@ function renderContext(
|
|
|
146
146
|
const guidance: string[] = [];
|
|
147
147
|
if (activeCount > 0) {
|
|
148
148
|
guidance.push(
|
|
149
|
-
"Do not duplicate active assignments.
|
|
149
|
+
"Do not duplicate active assignments. Continue useful independent work when it materially advances the outcome.",
|
|
150
150
|
);
|
|
151
151
|
}
|
|
152
152
|
if (readyCount > 0) {
|
|
@@ -155,7 +155,7 @@ function renderContext(
|
|
|
155
155
|
);
|
|
156
156
|
}
|
|
157
157
|
guidance.push(
|
|
158
|
-
"Use worker_status only for diagnostics or recovery when this snapshot reports overflow or state appears inconsistent.",
|
|
158
|
+
"Settled results are delivered automatically after the parent run ends. If progress depends on pending evidence, end the run truthfully rather than polling. Use worker_status only for diagnostics or recovery when this snapshot reports overflow or state appears inconsistent.",
|
|
159
159
|
);
|
|
160
160
|
sections.push(`Guidance:\n- ${guidance.join("\n- ")}`);
|
|
161
161
|
|
package/extension/pi/tools.ts
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import type { AgentToolResult } from "@earendil-works/pi-agent-core";
|
|
2
1
|
import type {
|
|
3
2
|
ExtensionAPI,
|
|
4
3
|
ExtensionContext,
|
|
@@ -28,10 +27,7 @@ import type {
|
|
|
28
27
|
} from "../orchestration/admission.ts";
|
|
29
28
|
import type {
|
|
30
29
|
AcceptedRun,
|
|
31
|
-
CompletedRun,
|
|
32
30
|
OwnerSnapshot,
|
|
33
|
-
SettlementListener,
|
|
34
|
-
WorkerRunResult,
|
|
35
31
|
} from "../orchestration/service.ts";
|
|
36
32
|
import type { DispatchDecision } from "../parent/dispatch-policy.ts";
|
|
37
33
|
import type { OrchestrationClient } from "../parent/process-host.ts";
|
|
@@ -42,11 +38,6 @@ import {
|
|
|
42
38
|
workerAbortToolRenderer,
|
|
43
39
|
workerStatusToolRenderer,
|
|
44
40
|
} from "./tool-renderer.ts";
|
|
45
|
-
import {
|
|
46
|
-
encodeInlineWorkerToolDetails,
|
|
47
|
-
type InlineWorkerSettlementDetails,
|
|
48
|
-
type WorkerSettlement,
|
|
49
|
-
} from "../orchestration/settlement.ts";
|
|
50
41
|
|
|
51
42
|
const STRICT_OBJECT = { additionalProperties: false } as const;
|
|
52
43
|
const shortTextSchema = Type.String({
|
|
@@ -114,45 +105,30 @@ export function registerOrchestrationTools(
|
|
|
114
105
|
name: "orchestrate",
|
|
115
106
|
label: "Orchestrate",
|
|
116
107
|
description:
|
|
117
|
-
"Dispatch fully briefed worker
|
|
118
|
-
promptSnippet: "Dispatch fully briefed
|
|
108
|
+
"Dispatch one fully briefed worker scope in the background. All orchestrate and interactive_send calls in the same assistant response form one result group; ordinary sibling tools execute concurrently and are not group members.",
|
|
109
|
+
promptSnippet: "Dispatch one fully briefed worker scope in the background",
|
|
119
110
|
promptGuidelines: [
|
|
120
|
-
"
|
|
121
|
-
"For an intended async wave of N workers, the next assistant response must contain exactly N separate, fully briefed orchestrate calls; one call is valid only when N=1. To run it asynchronously, include no other tool calls; harmless response text does not affect runtime classification.",
|
|
122
|
-
"When a parallel tool dispatcher is available, use it once with exactly N orchestrate entries and no other tools; for example, put N functions.orchestrate entries in multi_tool_use.parallel. Otherwise emit N native sibling orchestrate calls in one assistant response.",
|
|
123
|
-
"Form all N calls before emitting or finalizing the response. Never emit one call and wait for its result before forming the rest of the wave: a successfully admitted sole async orchestrate call returns terminate=true and ends the turn.",
|
|
111
|
+
"orchestrate starts background work and returns acceptance without ending the parent run. Dispatch siblings in one assistant response share a result group; ordinary sibling tools do not join that group.",
|
|
124
112
|
],
|
|
125
113
|
executionMode: "parallel",
|
|
126
114
|
parameters: taskSchema,
|
|
127
115
|
...orchestrateToolRenderer,
|
|
128
|
-
async execute(toolCallId, params, signal,
|
|
116
|
+
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
129
117
|
const decision = deps.getDispatchDecision(toolCallId);
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
"async",
|
|
137
|
-
signal,
|
|
138
|
-
);
|
|
139
|
-
const readable = acceptedRunSummary(acceptedRun);
|
|
140
|
-
return {
|
|
141
|
-
...readableToolResult(`Accepted async run ${readable.run_id}.`, readable),
|
|
142
|
-
terminate: true,
|
|
143
|
-
};
|
|
144
|
-
}
|
|
145
|
-
|
|
146
|
-
const completedRun = await deps.orchestration.orchestrate(
|
|
118
|
+
const orchestrationContext = buildOrchestrationContext(
|
|
119
|
+
ctx,
|
|
120
|
+
deps,
|
|
121
|
+
decision.synthesisGroup,
|
|
122
|
+
);
|
|
123
|
+
const acceptedRun = await deps.orchestration.orchestrate(
|
|
147
124
|
orchestrationContext,
|
|
148
125
|
params,
|
|
149
|
-
"
|
|
126
|
+
"async",
|
|
150
127
|
signal,
|
|
151
|
-
createInlineSettlementListener(onUpdate),
|
|
152
128
|
);
|
|
153
|
-
const readable =
|
|
129
|
+
const readable = acceptedRunSummary(acceptedRun);
|
|
154
130
|
return readableToolResult(
|
|
155
|
-
`
|
|
131
|
+
`Accepted async run ${readable.run_id}.`,
|
|
156
132
|
readable,
|
|
157
133
|
);
|
|
158
134
|
},
|
|
@@ -185,43 +161,31 @@ export function registerOrchestrationTools(
|
|
|
185
161
|
name: "interactive_send",
|
|
186
162
|
label: "Interactive Send",
|
|
187
163
|
description:
|
|
188
|
-
"Send follow-up instructions only to an owned lifecycle interactive worker whose status is ready.
|
|
164
|
+
"Send background follow-up instructions only to an owned lifecycle interactive worker whose status is ready. All interactive_send and orchestrate calls in the same assistant response form one result group; ordinary sibling tools execute concurrently and are not group members. Never use for one-shot or completed workers.",
|
|
189
165
|
promptSnippet: "Use only for an owned lifecycle interactive worker with status ready; never one-shot/completed because one-shot sessions terminate automatically",
|
|
190
166
|
promptGuidelines: [
|
|
191
|
-
"
|
|
167
|
+
"interactive_send starts background follow-up work and returns acceptance without ending the parent run. Dispatch siblings in one assistant response share a result group; ordinary sibling tools do not join that group.",
|
|
192
168
|
],
|
|
193
169
|
parameters: interactiveSendSchema,
|
|
194
170
|
...interactiveSendToolRenderer,
|
|
195
|
-
async execute(toolCallId, params, signal,
|
|
171
|
+
async execute(toolCallId, params, signal, _onUpdate, ctx) {
|
|
196
172
|
const workerId = params.worker_id;
|
|
197
|
-
const
|
|
198
|
-
const orchestrationContext = buildOrchestrationContext(
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
"async",
|
|
205
|
-
signal,
|
|
206
|
-
);
|
|
207
|
-
const readable = acceptedRunSummary(acceptedRun);
|
|
208
|
-
return {
|
|
209
|
-
...readableToolResult(`Accepted async run ${readable.run_id}.`, readable),
|
|
210
|
-
terminate: true,
|
|
211
|
-
};
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
const completedRun = await deps.orchestration.sendInteractive(
|
|
173
|
+
const decision = deps.getDispatchDecision(toolCallId);
|
|
174
|
+
const orchestrationContext = buildOrchestrationContext(
|
|
175
|
+
ctx,
|
|
176
|
+
deps,
|
|
177
|
+
decision.synthesisGroup,
|
|
178
|
+
);
|
|
179
|
+
const acceptedRun = await deps.orchestration.sendInteractive(
|
|
215
180
|
orchestrationContext,
|
|
216
181
|
workerId,
|
|
217
182
|
params.instructions,
|
|
218
|
-
"
|
|
183
|
+
"async",
|
|
219
184
|
signal,
|
|
220
|
-
createInlineSettlementListener(onUpdate),
|
|
221
185
|
);
|
|
222
|
-
const readable =
|
|
186
|
+
const readable = acceptedRunSummary(acceptedRun);
|
|
223
187
|
return readableToolResult(
|
|
224
|
-
`
|
|
188
|
+
`Accepted async run ${readable.run_id}.`,
|
|
225
189
|
readable,
|
|
226
190
|
);
|
|
227
191
|
},
|
|
@@ -289,20 +253,6 @@ function buildOrchestrationContext(
|
|
|
289
253
|
};
|
|
290
254
|
}
|
|
291
255
|
|
|
292
|
-
function createInlineSettlementListener(
|
|
293
|
-
onUpdate: ((result: AgentToolResult<unknown>) => void) | undefined,
|
|
294
|
-
): SettlementListener {
|
|
295
|
-
return (settlement) => {
|
|
296
|
-
onUpdate?.({
|
|
297
|
-
content: [{ type: "text", text: "Worker response received." }],
|
|
298
|
-
details: encodeInlineWorkerToolDetails({
|
|
299
|
-
mode: "inline",
|
|
300
|
-
result: inlineResultValue(settlement),
|
|
301
|
-
}),
|
|
302
|
-
});
|
|
303
|
-
};
|
|
304
|
-
}
|
|
305
|
-
|
|
306
256
|
function normalizeAbortTarget(params: {
|
|
307
257
|
worker_ids?: string[];
|
|
308
258
|
all?: boolean;
|
|
@@ -321,33 +271,6 @@ function acceptedRunSummary(run: AcceptedRun) {
|
|
|
321
271
|
};
|
|
322
272
|
}
|
|
323
273
|
|
|
324
|
-
function completedRunSummary(run: CompletedRun) {
|
|
325
|
-
return encodeInlineWorkerToolDetails({
|
|
326
|
-
mode: "inline",
|
|
327
|
-
runId: run.id,
|
|
328
|
-
ownerSessionId: run.ownerSessionId,
|
|
329
|
-
result: inlineResultValue(run.result),
|
|
330
|
-
});
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
function inlineResultValue(
|
|
334
|
-
result: WorkerRunResult | WorkerSettlement,
|
|
335
|
-
): InlineWorkerSettlementDetails {
|
|
336
|
-
return {
|
|
337
|
-
workerId: result.workerId,
|
|
338
|
-
worker: result.worker,
|
|
339
|
-
title: result.title,
|
|
340
|
-
status: result.status,
|
|
341
|
-
outcome: result.outcome,
|
|
342
|
-
usage: result.usage,
|
|
343
|
-
startedAt: result.startedAt,
|
|
344
|
-
settledAt: result.settledAt,
|
|
345
|
-
...(result.sessionFile === undefined
|
|
346
|
-
? {}
|
|
347
|
-
: { sessionFile: result.sessionFile }),
|
|
348
|
-
};
|
|
349
|
-
}
|
|
350
|
-
|
|
351
274
|
function statusSummary(catalog: WorkerCatalog, snapshot: OwnerSnapshot) {
|
|
352
275
|
return {
|
|
353
276
|
catalog: {
|