@zachwill/pi-orchestrate 0.2.1 → 0.4.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 +77 -48
- package/examples/workers/investigator.md +5 -31
- package/examples/workers/scout.md +5 -26
- package/examples/workers/web.md +84 -0
- package/examples/workers/worker.md +9 -35
- package/extension/catalog.ts +40 -22
- package/extension/contract.ts +4 -4
- package/extension/delivery.ts +72 -16
- package/extension/domain.ts +21 -55
- package/extension/host.ts +1 -37
- package/extension/index.ts +39 -11
- package/extension/presentation.ts +62 -158
- package/extension/runtime.ts +239 -331
- package/extension/tools.ts +206 -214
- package/extension/worker-session.ts +213 -50
- package/extension/worker-settlement.ts +106 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Pi Orchestrate
|
|
2
2
|
|
|
3
|
-
[`@zachwill/pi-orchestrate`](https://www.npmjs.com/package/@zachwill/pi-orchestrate) adds concurrent worker orchestration to [Pi](https://pi.dev).
|
|
3
|
+
[`@zachwill/pi-orchestrate`](https://www.npmjs.com/package/@zachwill/pi-orchestrate) adds concurrent worker orchestration to [Pi](https://pi.dev). A parent agent can delegate bounded work to isolated child sessions, run independent tasks concurrently, and synthesize the results.
|
|
4
4
|
|
|
5
5
|
## Install
|
|
6
6
|
|
|
@@ -8,60 +8,86 @@
|
|
|
8
8
|
pi install npm:@zachwill/pi-orchestrate
|
|
9
9
|
```
|
|
10
10
|
|
|
11
|
-
Pi packages
|
|
11
|
+
Pi packages run with your system permissions. Review this package and every worker definition you trust.
|
|
12
12
|
|
|
13
|
-
##
|
|
13
|
+
## Tools
|
|
14
14
|
|
|
15
15
|
Pi Orchestrate adds exactly five tools:
|
|
16
16
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
17
|
+
| Tool | Call | Purpose |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| `orchestrate` | `orchestrate({ worker, title, instructions })` | Start one worker task |
|
|
20
|
+
| `orchestration_status` | `orchestration_status({})` | Inspect the trusted catalog, diagnostics, runs, and worker states |
|
|
21
|
+
| `worker_send` | `worker_send({ worker_id, instructions })` | Send a follow-up to a ready reusable worker |
|
|
22
|
+
| `worker_abort` | `worker_abort({ worker_ids })` or `worker_abort({ all: true })` | Stop active owned work |
|
|
23
|
+
| `worker_close` | `worker_close({ worker_id })` | Close a ready reusable worker |
|
|
22
24
|
|
|
23
|
-
|
|
25
|
+
`title` is a label. `instructions` is the complete worker brief. Collapsed tool calls preview those instructions; expanded calls show them in full.
|
|
24
26
|
|
|
25
|
-
|
|
27
|
+
## Dispatch
|
|
26
28
|
|
|
27
|
-
|
|
29
|
+
Each `orchestrate` call validates its input, worker definition, and model before allocating IDs or starting a session. Calls are admitted independently: a rejected sibling does not block valid siblings. After admission, a startup or prompt failure settles only that worker as `failed`.
|
|
28
30
|
|
|
29
|
-
|
|
31
|
+
Pi executes sibling tool calls concurrently, with no extension-level group limit or hidden throttle. Send every known independent task as sibling `orchestrate` calls in one assistant message.
|
|
30
32
|
|
|
31
|
-
|
|
33
|
+
Execution mode depends on the complete tool-call group:
|
|
32
34
|
|
|
33
|
-
|
|
35
|
+
- One `orchestrate` call is asynchronous.
|
|
36
|
+
- A pure group of sibling `orchestrate` calls is asynchronous and concurrent.
|
|
37
|
+
- Mixing `orchestrate` with any other tool makes the orchestration calls inline and blocking.
|
|
38
|
+
- `worker_send` is asynchronous only when it is the sole tool call in the message.
|
|
34
39
|
|
|
35
|
-
|
|
40
|
+
Inline work follows the parent turn's cancellation signal. Accepted asynchronous work detaches from that signal and continues independently.
|
|
36
41
|
|
|
37
|
-
|
|
42
|
+
## Results and ownership
|
|
38
43
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
Asynchronous worker results enter the transcript individually. An ungrouped result starts a parent synthesis turn. Results from a sibling orchestration group share one final synthesis turn after every admitted member settles.
|
|
45
|
+
|
|
46
|
+
All state and delivery are owner-scoped. If an owning session is busy or inactive, completed results queue until that exact session is active and idle again. They are never delivered to another session.
|
|
47
|
+
|
|
48
|
+
`orchestration_status` is for diagnostics and recovery, not completion polling. It exposes bounded owner-scoped state without full task instructions or worker prompts.
|
|
49
|
+
|
|
50
|
+
The bottom widget shows active work only. Completed, failed, aborted, and reusable ready workers disappear immediately. Inline work shows its current response in the live tool output while it blocks.
|
|
51
|
+
|
|
52
|
+
## Lifecycle
|
|
53
|
+
|
|
54
|
+
A run represents one worker generation. A worker ID identifies the live worker session.
|
|
45
55
|
|
|
46
|
-
|
|
56
|
+
- A **one-shot** worker succeeds as `completed` and terminates.
|
|
57
|
+
- A **reusable** worker succeeds as `ready` and keeps the same worker ID.
|
|
58
|
+
- `worker_send` starts a new run on that ready reusable worker.
|
|
59
|
+
- `worker_close` closes a ready reusable worker.
|
|
60
|
+
- `worker_abort` stops active work only; `{ all: true }` does not close ready workers.
|
|
61
|
+
|
|
62
|
+
Workers, runs, and queued delivery survive extension reloads and session switches within the same Pi process. Reusable workers do not survive process exit, so close them when continuity is no longer needed.
|
|
63
|
+
|
|
64
|
+
## Parent contract
|
|
65
|
+
|
|
66
|
+
Pi Orchestrate injects the authoritative orchestration contract and trusted catalog into the parent system prompt. The parent remains responsible for the task end to end:
|
|
67
|
+
|
|
68
|
+
1. Keep trivial or tightly coupled work in the parent session.
|
|
69
|
+
2. Give every worker a complete brief: objective, scope and paths, forbidden actions, context, constraints, success criteria, checks, and expected output.
|
|
70
|
+
3. Dispatch independent scopes together, then yield after asynchronous acceptance.
|
|
71
|
+
4. Review evidence and changes, resolve conflicts, integrate deliberately, and verify the result.
|
|
72
|
+
5. Produce the final answer from the parent session.
|
|
47
73
|
|
|
48
|
-
|
|
74
|
+
Workers provide bounded evidence or changes. They do not replace parent judgment.
|
|
49
75
|
|
|
50
|
-
|
|
76
|
+
## Worker catalog
|
|
51
77
|
|
|
52
|
-
|
|
53
|
-
2. User definitions in `~/.pi/agent/pi-orchestrate/workers/*.md` override package fallbacks by name.
|
|
54
|
-
3. Project definitions in `<project>/.pi/pi-orchestrate/workers/*.md` override user and package definitions by name, but only after Pi trusts the project.
|
|
78
|
+
Definitions are loaded by name in this precedence order, from lowest to highest:
|
|
55
79
|
|
|
56
|
-
|
|
80
|
+
1. Package fallbacks in [`examples/workers/`](examples/workers/)
|
|
81
|
+
2. User definitions in `~/.pi/agent/pi-orchestrate/workers/*.md`
|
|
82
|
+
3. Project definitions in `<project>/.pi/pi-orchestrate/workers/*.md`, only after Pi trusts the project
|
|
57
83
|
|
|
58
|
-
|
|
84
|
+
A higher-precedence definition replaces a lower one with the same `name`. Pi performs no project-worker discovery for an untrusted project.
|
|
59
85
|
|
|
60
|
-
|
|
86
|
+
The package includes `scout`, `investigator`, `web`, and `worker` fallbacks. `scout`, `investigator`, and `worker` omit `model`, so they inherit the parent's active model at dispatch. `web` uses an installed, authenticated Codex CLI for public-web research and pins its Pi session and searches to `gpt-5.6-sol`. To customize one, copy its definition to the user or project directory and keep the same filename and `name`. Add an explicit model only when that worker needs one.
|
|
61
87
|
|
|
62
88
|
## Worker definitions
|
|
63
89
|
|
|
64
|
-
A worker is a
|
|
90
|
+
A worker is a regular Markdown file whose basename matches its `name`:
|
|
65
91
|
|
|
66
92
|
```md
|
|
67
93
|
---
|
|
@@ -71,29 +97,32 @@ tools: read, grep, find, ls, bash
|
|
|
71
97
|
lifecycle: reusable
|
|
72
98
|
---
|
|
73
99
|
|
|
74
|
-
|
|
100
|
+
Inspect the assigned scope and return concise findings with file paths.
|
|
75
101
|
```
|
|
76
102
|
|
|
77
|
-
|
|
103
|
+
| Field | Rule |
|
|
104
|
+
| --- | --- |
|
|
105
|
+
| `name` | Required; must match the filename |
|
|
106
|
+
| `description` | Required; used by the parent to choose a worker |
|
|
107
|
+
| `tools` | Required, nonempty list using `read`, `bash`, `edit`, `write`, `grep`, `find`, or `ls` |
|
|
108
|
+
| `lifecycle` | Required; exactly `one-shot` or `reusable` |
|
|
109
|
+
| `model` | Optional `provider/model`; omitted inherits the parent model |
|
|
110
|
+
| `thinking` | Optional Pi thinking level |
|
|
111
|
+
| `skills` | Optional; omitted uses normal discovery, a list is an exact allowlist, and `[]` disables skills |
|
|
112
|
+
| `compaction` | Optional worker compaction settings |
|
|
78
113
|
|
|
79
|
-
|
|
80
|
-
- `tools` and `lifecycle` are required. Grant the smallest useful tool set.
|
|
81
|
-
- `model` is optional. When omitted, the worker inherits the parent model active when dispatched.
|
|
82
|
-
- `thinking`, `skills`, and `compaction` are optional.
|
|
83
|
-
- Omitted `skills` uses Pi's normal discovered skills. A nonempty `skills` list is an exact name allowlist, and `skills: []` disables skills.
|
|
84
|
-
- `lifecycle` must be exactly `one-shot` or `reusable`.
|
|
85
|
-
- The Markdown body must be nonempty.
|
|
114
|
+
The Markdown body is the worker system prompt and must be nonempty. Unknown fields, invalid values, symlinks, and filename/name mismatches invalidate a definition.
|
|
86
115
|
|
|
87
|
-
|
|
116
|
+
Grant the smallest useful tool set. A read-only prompt is not enforcement when the worker has tools that can write.
|
|
88
117
|
|
|
89
|
-
##
|
|
118
|
+
## Trust and isolation
|
|
90
119
|
|
|
91
|
-
|
|
120
|
+
Workers receive fresh durable Pi session lineage without the parent's conversation. They still run in the parent process and are not security sandboxes: they share your filesystem and environment permissions.
|
|
92
121
|
|
|
93
|
-
|
|
122
|
+
Workers use normal global Pi settings, authentication, packages, extensions, skills, and context. A trusted project may also contribute project-scoped definitions, settings, extensions, skills, and context. An untrusted project contributes none of those project-scoped resources; global resources remain available.
|
|
94
123
|
|
|
95
|
-
|
|
124
|
+
Other configured extensions, including provider integrations such as `@benvargas/pi-claude-code-use`, load normally in worker sessions. Pi Orchestrate excludes itself, so workers remain direct Pi children. The injected boundary forbids recursive Pi Orchestrate delegation and descendant Pi worker sessions.
|
|
96
125
|
|
|
97
|
-
|
|
126
|
+
The worker definition controls the Pi tool allowlist, not operating-system authority. A trusted worker with `bash` can launch explicitly required external processes, including agent CLIs.
|
|
98
127
|
|
|
99
|
-
Pi Orchestrate performs no automatic filesystem writes.
|
|
128
|
+
Pi Orchestrate performs no automatic filesystem writes. Workers write only through their granted tools and instructions. Give concurrent workers non-overlapping write scopes, then inspect and verify their changes in the parent.
|
|
@@ -1,41 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: investigator
|
|
3
|
-
description:
|
|
3
|
+
description: Investigates cross-file questions and synthesizes grounded evidence.
|
|
4
4
|
thinking: medium
|
|
5
5
|
tools: read, grep, find, ls, bash
|
|
6
6
|
lifecycle: one-shot
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Investigate the assigned cross-file question through read-only inspection, comparison, and evidence synthesis.
|
|
10
10
|
|
|
11
|
-
Do not modify files
|
|
11
|
+
Do not modify files or run builds, tests, or commands that mutate state. Use bash only for read-only commands.
|
|
12
12
|
|
|
13
|
-
|
|
13
|
+
Ground each finding in file paths, line ranges, or symbols. Distinguish confirmed behavior from inference, connect evidence across files, and explain the resulting system shape or conclusion. Provide grounded recommendations when the assignment requests them.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
### Scope
|
|
18
|
-
|
|
19
|
-
- What you investigated
|
|
20
|
-
- What remained outside scope
|
|
21
|
-
|
|
22
|
-
### Findings
|
|
23
|
-
|
|
24
|
-
For each finding:
|
|
25
|
-
|
|
26
|
-
- `path/to/file#L10-L20` or `symbolName` in `path/to/file`
|
|
27
|
-
- Finding: what exists or happens
|
|
28
|
-
- Evidence: why it is confirmed
|
|
29
|
-
- Relevance: why it matters
|
|
30
|
-
|
|
31
|
-
### Synthesis
|
|
32
|
-
|
|
33
|
-
Explain the system shape or conclusion supported by the findings.
|
|
34
|
-
|
|
35
|
-
### Gaps
|
|
36
|
-
|
|
37
|
-
Include only unresolved questions that materially affect implementation or review.
|
|
38
|
-
|
|
39
|
-
### Start Here
|
|
40
|
-
|
|
41
|
-
Name the first files or symbols the parent should inspect next.
|
|
15
|
+
Return concise **Findings** and **Synthesis** sections. Add **Gaps** only for material unresolved questions and **Start Here** only when useful.
|
|
@@ -1,36 +1,15 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: scout
|
|
3
|
-
description:
|
|
3
|
+
description: Answers one small factual repository question with read-only evidence.
|
|
4
4
|
thinking: medium
|
|
5
5
|
tools: read, grep, find, ls, bash
|
|
6
6
|
lifecycle: one-shot
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Answer one small factual probe through fast, shallow, read-only repository inspection.
|
|
10
10
|
|
|
11
|
-
Do not modify files
|
|
11
|
+
Do not modify files or run builds, tests, or commands that mutate state. Use bash only for read-only commands.
|
|
12
12
|
|
|
13
|
-
Accept
|
|
13
|
+
Accept one path, symbol, command output, short inventory, direct comparison, or existence check. If the assignment requires broader investigation, synthesis, architecture judgment, planning, or implementation, stop concisely and recommend the investigator.
|
|
14
14
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
### Scope
|
|
18
|
-
|
|
19
|
-
- What you inspected
|
|
20
|
-
- What you did not inspect
|
|
21
|
-
|
|
22
|
-
### Findings
|
|
23
|
-
|
|
24
|
-
For each finding:
|
|
25
|
-
|
|
26
|
-
- `path/to/file#L10-L20` or `symbolName` in `path/to/file`
|
|
27
|
-
- Finding: concrete fact
|
|
28
|
-
- Relevance: why it matters
|
|
29
|
-
|
|
30
|
-
### Gaps
|
|
31
|
-
|
|
32
|
-
Include only unresolved questions that materially affect the parent task.
|
|
33
|
-
|
|
34
|
-
### Start Here
|
|
35
|
-
|
|
36
|
-
Name the first file or symbol the parent should inspect next.
|
|
15
|
+
Return a short **Answer** and **Evidence** grounded in paths, line ranges, symbols, or command output. Add **Gaps** only when material.
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: web
|
|
3
|
+
description: Researches the public web with one or more Codex searches and returns a source-grounded synthesis.
|
|
4
|
+
model: openai-codex/gpt-5.6-sol
|
|
5
|
+
thinking: medium
|
|
6
|
+
tools: bash
|
|
7
|
+
skills: []
|
|
8
|
+
lifecycle: one-shot
|
|
9
|
+
---
|
|
10
|
+
|
|
11
|
+
You are a web research worker. Understand the assigned research objective, choose an efficient search strategy, and return a useful source-grounded synthesis in the assignment's language.
|
|
12
|
+
|
|
13
|
+
Use the installed, authenticated `codex` CLI as your web-search backend. This external search process is explicitly part of your task; do not invoke Pi or other Pi workers. Do not modify project files or install anything. Use fresh temporary directories and clean them up.
|
|
14
|
+
|
|
15
|
+
## Strategy
|
|
16
|
+
|
|
17
|
+
Use your judgment:
|
|
18
|
+
|
|
19
|
+
- For a narrow lookup, run one focused Codex search.
|
|
20
|
+
- For independent entities, claims, or source families, run separate focused searches in parallel by issuing sibling bash calls in the same turn.
|
|
21
|
+
- For dependent questions, search serially so later work can use earlier evidence.
|
|
22
|
+
- Use a follow-up search only for a material gap, conflict, or verification need.
|
|
23
|
+
- Stop when the objective is adequately answered. Do not multiply searches for cosmetic coverage.
|
|
24
|
+
|
|
25
|
+
Tell each Codex process to use at most four actual web searches unless the assignment justifies a different bound. Use cached search for stable documentation or background and live search for current or time-sensitive questions.
|
|
26
|
+
|
|
27
|
+
## Codex Search
|
|
28
|
+
|
|
29
|
+
Use this command shape for each focused search, selecting `cached` or `live` and writing a complete prompt for that angle:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
work_dir="$(mktemp -d "${TMPDIR:-/tmp}/web-search.XXXXXX")" || exit 1
|
|
33
|
+
trap 'rm -rf "$work_dir"' EXIT HUP INT TERM
|
|
34
|
+
last_message="$work_dir/last-message.txt"
|
|
35
|
+
stdout_log="$work_dir/stdout.log"
|
|
36
|
+
stderr_log="$work_dir/stderr.log"
|
|
37
|
+
|
|
38
|
+
codex exec - \
|
|
39
|
+
--ignore-user-config \
|
|
40
|
+
--model gpt-5.6-sol \
|
|
41
|
+
-c 'model_reasoning_effort="medium"' \
|
|
42
|
+
-c 'web_search="cached"' \
|
|
43
|
+
--ephemeral \
|
|
44
|
+
--skip-git-repo-check \
|
|
45
|
+
--cd "$work_dir" \
|
|
46
|
+
--sandbox read-only \
|
|
47
|
+
--color never \
|
|
48
|
+
--output-last-message "$last_message" \
|
|
49
|
+
>"$stdout_log" 2>"$stderr_log" <<'CODEX_PROMPT'
|
|
50
|
+
[Research this focused angle. Include the assignment's relevant scope, exclusions, known facts, URLs, source priorities, freshness needs, and search bound. Require concise findings, exact source URLs, conflicts, gaps, and cautions. Stop when the angle is answered and return partial evidence if the bound is reached.]
|
|
51
|
+
CODEX_PROMPT
|
|
52
|
+
status=$?
|
|
53
|
+
|
|
54
|
+
if [ -s "$last_message" ]; then
|
|
55
|
+
command cat "$last_message"
|
|
56
|
+
fi
|
|
57
|
+
if [ "$status" -ne 0 ] || [ ! -s "$last_message" ]; then
|
|
58
|
+
printf '%s\n' '--- failure diagnostics ---'
|
|
59
|
+
command tail -n 80 "$stderr_log"
|
|
60
|
+
command tail -n 80 "$stdout_log"
|
|
61
|
+
fi
|
|
62
|
+
exit "$status"
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Never use `--dangerously-bypass-approvals-and-sandbox`. Retry only when diagnostics show a clear, mechanically correctable invocation failure.
|
|
66
|
+
|
|
67
|
+
## Research Standards
|
|
68
|
+
|
|
69
|
+
- Preserve quoted phrases, `site:` and `filetype:` constraints, known URLs, supplied facts, and assignment exclusions.
|
|
70
|
+
- Prefer official and primary sources. Verify important claims on source pages rather than relying on snippets or aggregators.
|
|
71
|
+
- Treat legal, discipline, injury, health, and rumor claims cautiously. Distinguish allegations, reporting, official records, and confirmed facts.
|
|
72
|
+
- Compare results across angles. Expose source conflicts, uncertainty, freshness limits, and weak coverage instead of guessing.
|
|
73
|
+
- Never invent sources or claims.
|
|
74
|
+
|
|
75
|
+
## Response
|
|
76
|
+
|
|
77
|
+
Return a concise synthesis that directly serves the assignment. Include:
|
|
78
|
+
|
|
79
|
+
- the answer or strongest supported conclusion;
|
|
80
|
+
- material findings and conflicts;
|
|
81
|
+
- source titles with exact URLs and relevance;
|
|
82
|
+
- unresolved gaps or cautions when they matter.
|
|
83
|
+
|
|
84
|
+
Do not dump search transcripts or raw temporary paths. If research fails, say what failed and return any useful partial evidence.
|
|
@@ -1,44 +1,18 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: worker
|
|
3
|
-
description:
|
|
3
|
+
description: Implements a bounded change within explicit scope and acceptance criteria.
|
|
4
4
|
thinking: medium
|
|
5
5
|
tools: read, bash, edit, write, grep, find, ls
|
|
6
6
|
lifecycle: one-shot
|
|
7
7
|
---
|
|
8
8
|
|
|
9
|
-
|
|
9
|
+
Implement the assigned change within its stated scope.
|
|
10
10
|
|
|
11
|
-
|
|
11
|
+
- Follow loaded project conventions. Inspect nearby code and reuse existing helpers and patterns before writing new code.
|
|
12
|
+
- Change only what the assignment requires. Do not fix, refactor, or investigate unrelated work.
|
|
13
|
+
- Do not commit, push, or perform destructive actions unless assigned.
|
|
14
|
+
- Stop and report a blocker rather than guessing when a required decision is unclear.
|
|
15
|
+
- Remove unused imports, dead code, debug output, and other leftovers from your changes.
|
|
16
|
+
- Run only the narrowest relevant verification permitted by the assignment and project conventions. Report pre-existing failures separately; fix only failures caused by your changes.
|
|
12
17
|
|
|
13
|
-
|
|
14
|
-
- Reuse existing helpers and patterns instead of duplicating them.
|
|
15
|
-
- Change only files required by the assignment.
|
|
16
|
-
- Keep the implementation direct and remove unused code introduced by your work.
|
|
17
|
-
- Do not commit, push, or perform destructive operations unless explicitly assigned.
|
|
18
|
-
- Stop and report a blocker when a required decision is unclear.
|
|
19
|
-
|
|
20
|
-
## Verification
|
|
21
|
-
|
|
22
|
-
Run the narrowest relevant lint, type-check, test, or build commands. Fix only failures caused by your changes and distinguish pre-existing failures with concrete evidence.
|
|
23
|
-
|
|
24
|
-
## Output
|
|
25
|
-
|
|
26
|
-
### Completed
|
|
27
|
-
|
|
28
|
-
Concise description of the result.
|
|
29
|
-
|
|
30
|
-
### Files Changed
|
|
31
|
-
|
|
32
|
-
- `path/to/file` — what changed
|
|
33
|
-
|
|
34
|
-
### Verification
|
|
35
|
-
|
|
36
|
-
Commands run and their results.
|
|
37
|
-
|
|
38
|
-
### Blockers
|
|
39
|
-
|
|
40
|
-
Include only when work could not be completed.
|
|
41
|
-
|
|
42
|
-
### Observations
|
|
43
|
-
|
|
44
|
-
Include only relevant out-of-scope issues that were not changed.
|
|
18
|
+
Return concise sections for **Completed**, **Files Changed**, and **Verification**. Add **Blockers** only when blocked and **Observations** only for directly relevant out-of-scope findings.
|
package/extension/catalog.ts
CHANGED
|
@@ -13,7 +13,11 @@ import type {
|
|
|
13
13
|
WorkerDefinition,
|
|
14
14
|
WorkerSourceKind,
|
|
15
15
|
} from "./domain.js";
|
|
16
|
-
import {
|
|
16
|
+
import {
|
|
17
|
+
createWorkerCatalog,
|
|
18
|
+
isSupportedToolName,
|
|
19
|
+
SUPPORTED_TOOL_NAMES,
|
|
20
|
+
} from "./domain.js";
|
|
17
21
|
|
|
18
22
|
const MAX_WORKER_BYTES = 64 * 1024;
|
|
19
23
|
const THINKING_LEVELS = ["off", "minimal", "low", "medium", "high", "xhigh", "max"] as const;
|
|
@@ -127,27 +131,34 @@ function isMissingPath(error: unknown): boolean {
|
|
|
127
131
|
return typeof error === "object" && error !== null && "code" in error && error.code === "ENOENT";
|
|
128
132
|
}
|
|
129
133
|
|
|
130
|
-
interface
|
|
134
|
+
interface IssuePath {
|
|
131
135
|
readonly path: readonly PropertyKey[];
|
|
136
|
+
readonly issue: SchemaIssue.Issue;
|
|
132
137
|
}
|
|
133
138
|
|
|
134
|
-
function
|
|
139
|
+
function collectIssuePaths(
|
|
135
140
|
issue: SchemaIssue.Issue,
|
|
136
141
|
parentPath: readonly PropertyKey[] = [],
|
|
137
|
-
):
|
|
142
|
+
): IssuePath[] {
|
|
138
143
|
switch (issue._tag) {
|
|
139
144
|
case "Pointer":
|
|
140
|
-
return
|
|
145
|
+
return collectIssuePaths(issue.issue, [...parentPath, ...issue.path]);
|
|
141
146
|
case "Composite":
|
|
147
|
+
return issue.issues.flatMap((child) => collectIssuePaths(child, parentPath));
|
|
142
148
|
case "AnyOf":
|
|
143
|
-
return issue.issues.
|
|
149
|
+
return issue.issues.length === 0
|
|
150
|
+
? [{ path: parentPath, issue }]
|
|
151
|
+
: issue.issues.flatMap((child) => collectIssuePaths(child, parentPath));
|
|
144
152
|
case "Encoding":
|
|
145
153
|
case "Filter":
|
|
146
|
-
return
|
|
154
|
+
return collectIssuePaths(issue.issue, parentPath);
|
|
155
|
+
case "InvalidType":
|
|
156
|
+
case "InvalidValue":
|
|
157
|
+
case "MissingKey":
|
|
147
158
|
case "UnexpectedKey":
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
return [];
|
|
159
|
+
case "Forbidden":
|
|
160
|
+
case "OneOf":
|
|
161
|
+
return [{ path: parentPath, issue }];
|
|
151
162
|
}
|
|
152
163
|
}
|
|
153
164
|
|
|
@@ -165,8 +176,9 @@ function listItems(value: unknown): readonly unknown[] {
|
|
|
165
176
|
return Array.isArray(value) ? value : [];
|
|
166
177
|
}
|
|
167
178
|
|
|
168
|
-
function schemaDiagnostic(
|
|
169
|
-
const
|
|
179
|
+
function schemaDiagnostic(issue: SchemaIssue.Issue, frontmatter: unknown): string {
|
|
180
|
+
const issuePaths = collectIssuePaths(issue);
|
|
181
|
+
const unexpected = issuePaths.filter(({ issue }) => issue._tag === "UnexpectedKey");
|
|
170
182
|
const frontmatterFields = unexpected
|
|
171
183
|
.filter(({ path }) => path.length === 1 && typeof path[0] === "string")
|
|
172
184
|
.map(({ path }) => String(path[0]))
|
|
@@ -196,8 +208,9 @@ function schemaDiagnostic(error: Schema.SchemaError, frontmatter: unknown): stri
|
|
|
196
208
|
"compaction",
|
|
197
209
|
"lifecycle",
|
|
198
210
|
];
|
|
199
|
-
const
|
|
200
|
-
|
|
211
|
+
const field = orderedFields.find((candidate) =>
|
|
212
|
+
issuePaths.some(({ path }) => path[0] === candidate)
|
|
213
|
+
);
|
|
201
214
|
const value = field === undefined ? undefined : fieldValue(frontmatter, field);
|
|
202
215
|
|
|
203
216
|
if (field === "name" || field === "description") {
|
|
@@ -234,13 +247,21 @@ function schemaDiagnostic(error: Schema.SchemaError, frontmatter: unknown): stri
|
|
|
234
247
|
if (compactionFields.length > 0) {
|
|
235
248
|
return `unknown compaction field${compactionFields.length === 1 ? "" : "s"}: ${compactionFields.join(", ")}`;
|
|
236
249
|
}
|
|
237
|
-
if (
|
|
250
|
+
if (issuePaths.some(({ path }) => path[0] === "compaction" && path[1] === "enabled")) {
|
|
238
251
|
return "frontmatter field 'compaction.enabled' must be a boolean";
|
|
239
252
|
}
|
|
240
|
-
if (
|
|
253
|
+
if (
|
|
254
|
+
issuePaths.some(({ path }) =>
|
|
255
|
+
path[0] === "compaction" && path[1] === "reserveTokens"
|
|
256
|
+
)
|
|
257
|
+
) {
|
|
241
258
|
return "frontmatter field 'compaction.reserveTokens' must be a non-negative integer";
|
|
242
259
|
}
|
|
243
|
-
if (
|
|
260
|
+
if (
|
|
261
|
+
issuePaths.some(({ path }) =>
|
|
262
|
+
path[0] === "compaction" && path[1] === "keepRecentTokens"
|
|
263
|
+
)
|
|
264
|
+
) {
|
|
244
265
|
return "frontmatter field 'compaction.keepRecentTokens' must be a non-negative integer";
|
|
245
266
|
}
|
|
246
267
|
return "frontmatter field 'compaction' must be a mapping";
|
|
@@ -266,7 +287,7 @@ function parseWorker(
|
|
|
266
287
|
const { frontmatter, body } = parsed;
|
|
267
288
|
const decoded = decodeWorkerFrontmatter(frontmatter);
|
|
268
289
|
if (Result.isFailure(decoded)) {
|
|
269
|
-
throw new Error(schemaDiagnostic(decoded.failure, frontmatter));
|
|
290
|
+
throw new Error(schemaDiagnostic(decoded.failure.issue, frontmatter));
|
|
270
291
|
}
|
|
271
292
|
|
|
272
293
|
const worker = decoded.success;
|
|
@@ -381,10 +402,7 @@ export function createWorkerCatalogDiscovery(fileSystem: CatalogFileSystem) {
|
|
|
381
402
|
diagnostics.push(...discovered.diagnostics);
|
|
382
403
|
}
|
|
383
404
|
|
|
384
|
-
|
|
385
|
-
compareText(left.name, right.name),
|
|
386
|
-
);
|
|
387
|
-
return { workers, diagnostics };
|
|
405
|
+
return createWorkerCatalog([...workersByName.values()], diagnostics);
|
|
388
406
|
};
|
|
389
407
|
}
|
|
390
408
|
|
package/extension/contract.ts
CHANGED
|
@@ -30,12 +30,12 @@ function buildContract(catalog: WorkerCatalog): string {
|
|
|
30
30
|
You are the parent orchestrator and own the task end to end.
|
|
31
31
|
|
|
32
32
|
- Keep trivial or tightly coupled work in the parent. Use as many useful workers as independent scopes justify.
|
|
33
|
-
- Delegate
|
|
33
|
+
- Delegate each independent scope with its own \`orchestrate\` call using \`{ worker, title, instructions }\`. Emit sibling \`orchestrate\` calls in one assistant message so Pi executes them concurrently.
|
|
34
34
|
- Give every worker a full brief: objective; paths/scope; forbidden actions; context; constraints; observable success; checks; expected output.
|
|
35
|
-
- Input, catalog, and model preflight is atomic before
|
|
36
|
-
-
|
|
35
|
+
- Input, catalog, and model preflight is atomic per call before that worker starts. Sibling calls are admitted independently, so one rejected call does not prevent valid siblings from starting.
|
|
36
|
+
- A sole \`orchestrate\` call or a pure group of sibling \`orchestrate\` calls runs asynchronously. Pi accepts a pure group concurrently, yields the parent turn, delivers each result as it settles, and starts synthesis only after the whole group settles. Mixing \`orchestrate\` with another tool makes it inline and blocking. \`worker_send\` is asynchronous only as the sole tool call in its assistant message.
|
|
37
37
|
- Exact worker instructions remain visible in the tool call and can be expanded; titles are labels, not substitutes for complete messages.
|
|
38
|
-
- After an accepted async
|
|
38
|
+
- After an accepted async run, yield the parent turn. Worker responses arrive individually as each worker settles, and the final response starts parent synthesis. Do not duplicate delegated work, poll \`orchestration_status\`, or use it as a normal completion mechanism.
|
|
39
39
|
- The active-work widget shows only workers currently starting, running, or stopping. Inline worker responses appear progressively in live tool output.
|
|
40
40
|
- The parent synthesizes worker results, reviews their evidence and changes, resolves conflicts, integrates the final result, and runs the relevant verification before declaring completion.
|
|
41
41
|
- Prefer one-shot workers. Use \`worker_send\` for follow-up work on a ready reusable worker, \`worker_close\` when that ready worker is finished, and \`worker_abort\` only when active work must stop.
|