pi-background-tasks 0.7.7 → 0.9.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/PUBLISHING.md +7 -7
- package/README.md +64 -7
- package/TEST_PLAN.md +8 -1
- package/package.json +7 -2
- package/src/core/delegate/launch.ts +1 -0
- package/src/core/fusion/artifacts.ts +49 -4
- package/src/core/fusion/budget.ts +21 -12
- package/src/core/fusion/context.ts +7 -2
- package/src/core/fusion/orchestrator.ts +70 -15
- package/src/core/fusion/pi-child.ts +473 -8
- package/src/core/fusion/prompts.ts +151 -3
- package/src/core/fusion/types.ts +84 -2
- package/src/core/fusion/web-fetch.ts +904 -0
- package/src/core/fusion/workflows.ts +130 -0
- package/src/fusion-child-extension.ts +279 -2
- package/src/fusion-extension.ts +182 -27
package/PUBLISHING.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Publishing pi-background-tasks
|
|
2
2
|
|
|
3
|
-
Release checklist for npm publishing and standalone git publishing. The current release candidate is 0.
|
|
3
|
+
Release checklist for npm publishing and standalone git publishing. The current release candidate is 0.9.0, which adds the `fusion_validate` validation workflow and Anthropic child sanitization; 0.7.0 introduced the Fusion public surfaces (`/fusion`, `/fusion-models`, `fusion_brainstorm`) in addition to the background-task surfaces. Do not advertise the GitHub install target until the standalone repository has the exact release commit and tag.
|
|
4
4
|
|
|
5
5
|
## Preconditions
|
|
6
6
|
|
|
@@ -37,8 +37,8 @@ npm publish --access public
|
|
|
37
37
|
Pi install smoke after publish:
|
|
38
38
|
|
|
39
39
|
```bash
|
|
40
|
-
PI_CODING_AGENT_DIR=$(mktemp -d) pi -e npm:pi-background-tasks@0.
|
|
41
|
-
pi install npm:pi-background-tasks@0.
|
|
40
|
+
PI_CODING_AGENT_DIR=$(mktemp -d) pi -e npm:pi-background-tasks@0.9.0 --offline --no-tools --no-session -p "/jobs"
|
|
41
|
+
pi install npm:pi-background-tasks@0.9.0
|
|
42
42
|
```
|
|
43
43
|
|
|
44
44
|
## Publish to git
|
|
@@ -51,15 +51,15 @@ git status --short --branch
|
|
|
51
51
|
git log --oneline -3
|
|
52
52
|
git remote -v
|
|
53
53
|
git push origin main
|
|
54
|
-
git tag v0.
|
|
55
|
-
git push origin v0.
|
|
54
|
+
git tag v0.9.0
|
|
55
|
+
git push origin v0.9.0
|
|
56
56
|
```
|
|
57
57
|
|
|
58
58
|
Pi install smoke after git tag, using an isolated Pi agent directory so no local checkout or user `~/.pi` state is involved:
|
|
59
59
|
|
|
60
60
|
```bash
|
|
61
|
-
PI_CODING_AGENT_DIR=$(mktemp -d) pi -e git:github.com/ismailsaleekh/pi-background-tasks@v0.
|
|
62
|
-
pi install git:github.com/ismailsaleekh/pi-background-tasks@v0.
|
|
61
|
+
PI_CODING_AGENT_DIR=$(mktemp -d) pi -e git:github.com/ismailsaleekh/pi-background-tasks@v0.9.0 --offline --no-tools --no-session -p "/jobs"
|
|
62
|
+
pi install git:github.com/ismailsaleekh/pi-background-tasks@v0.9.0
|
|
63
63
|
```
|
|
64
64
|
|
|
65
65
|
## pi.dev/packages
|
package/README.md
CHANGED
|
@@ -2,26 +2,26 @@
|
|
|
2
2
|
|
|
3
3
|
Claude-Code-like explicit background shell task manager for [Pi](https://pi.dev/).
|
|
4
4
|
|
|
5
|
-
This package adds named, tracked background shell jobs with durable output files, bounded log reads, kill/timeout safety, task-owned context-window/token/tool-use/model telemetry, explicit Pi-agent telemetry wrapping for tasks marked as agents, a focused footer-dock task manager, `/tasks` fallback UI, and completion notifications that can wake the agent when LLM-launched work finishes. It also ships Fusion: a direct child-Pi five-call synthesis workflow exposed as `/fusion`, `/fusion-models`, and the always-active `fusion_brainstorm`
|
|
5
|
+
This package adds named, tracked background shell jobs with durable output files, bounded log reads, kill/timeout safety, task-owned context-window/token/tool-use/model telemetry, explicit Pi-agent telemetry wrapping for tasks marked as agents, a focused footer-dock task manager, `/tasks` fallback UI, and completion notifications that can wake the agent when LLM-launched work finishes. It also ships Fusion: a direct child-Pi five-call synthesis workflow exposed as `/fusion`, `/fusion-models`, and the always-active `fusion_brainstorm` and `fusion_validate` tools. A terminal task status is published only after trailing wrapped-agent telemetry is consumed and final output plus terminal metadata have completed their durability writes.
|
|
6
6
|
|
|
7
7
|
## Install
|
|
8
8
|
|
|
9
9
|
From npm after publish:
|
|
10
10
|
|
|
11
11
|
```bash
|
|
12
|
-
pi install npm:pi-background-tasks@0.
|
|
12
|
+
pi install npm:pi-background-tasks@0.9.0
|
|
13
13
|
```
|
|
14
14
|
|
|
15
15
|
From git after pushing this package to its standalone repository and tagging:
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
|
-
pi install git:github.com/ismailsaleekh/pi-background-tasks@v0.
|
|
18
|
+
pi install git:github.com/ismailsaleekh/pi-background-tasks@v0.9.0
|
|
19
19
|
```
|
|
20
20
|
|
|
21
21
|
For project-local install:
|
|
22
22
|
|
|
23
23
|
```bash
|
|
24
|
-
pi install -l npm:pi-background-tasks@0.
|
|
24
|
+
pi install -l npm:pi-background-tasks@0.9.0
|
|
25
25
|
```
|
|
26
26
|
|
|
27
27
|
## Commands
|
|
@@ -97,7 +97,8 @@ The lookup runs at most once per session on `session_start`, is time-boxed, and
|
|
|
97
97
|
- `bg_status` — inspect one task or all recent tasks.
|
|
98
98
|
- `bg_logs` — read bounded task output.
|
|
99
99
|
- `bg_kill` — stop a running task.
|
|
100
|
-
- `
|
|
100
|
+
- `fusion_validate({prompt})` — always-active tool that runs the same five-model Fusion workflow as a validation review of work that was just completed, and returns the merged prose review. Its closed public schema has exactly one parameter, `prompt`; **it takes no `capability` argument**, and a caller-supplied `capability` is rejected loudly rather than ignored. Candidate reviewers always run with the read-only `inspect` capability, because a reasoning-only reviewer cannot read the code it is judging; the evaluator and merger remain no-tools by stage policy. Findings are classified `critical`, `high`, or `minor`, each with a file/symbol location, the evidence the reviewer actually read, and why it matters. A review with no findings must state what was verified rather than returning an unexplained pass. See [Validation workflow](#validation-workflow).
|
|
101
|
+
- `fusion_brainstorm({prompt, capability?})` — always-active tool that runs the Fusion workflow and returns the exact merged text as the tool result for the parent agent to consume, with the exact Pi `Usage` shape attached when the host supports tool-result usage: token fields plus complete `cost.input`, `cost.output`, `cost.cacheRead`, `cost.cacheWrite`, and `cost.total`. Calling it as `fusion_brainstorm({prompt})` uses the default `reason` capability: no tools and byte-identical child argv/prompt behaviour to the previous release. The optional `capability` accepts only `"reason"`, `"inspect"`, or `"research"`; `inspect` gives candidate children read-only repository inspection tools, and `research` adds the package-owned `fusion_web_fetch` tool for targeted public URL fetches. The evaluator and merger remain no-tools by stage policy. Its closed public schema has one required parameter, `prompt`, plus optional `capability`; extra keys are rejected. It has no eligibility, quota, routine, or justification gate. Tool context capture excludes the current assistant tool-call leaf when Pi is executing that `fusion_brainstorm` call, so the nested children do not see the in-progress tool call or sibling calls. Children receive the documented conversation projection described under [Conversation context policy](#conversation-context-policy): visible user/assistant text verbatim, with thinking and tool payloads replaced by explicit hash-accounted omission receipts. Because the prompt is composed by the parent agent, it is treated as authoritative and self-contained.
|
|
101
102
|
|
|
102
103
|
`bg_run` requires a concise `name` for the footer dock, the shell `command`, and required `isAgent: boolean`. Set `isAgent: true` only when the background task launches an LLM/agent process (for example `pi -p ...` or `pi --mode json ...`); set `isAgent: false` for scripts, tests, dev servers, sleeps, and ordinary shell commands. It defaults both `notifyOnCompletion` and `triggerOnCompletion` to `true`. With those defaults, `bg_run` returns immediately, the agent continues only independent useful work or ends its current turn instead of sleeping or polling, and a durable `background-task-notification` for completed, failed, or killed state automatically starts a follow-up turn. The launch receipt states the effective notification/wake behavior explicitly. `bg_status` and `bg_logs` remain available for user-requested inspection, deliberately disabled completion delivery, concrete hang diagnosis, or reading output after the terminal event; they are not waiting primitives, and the terminal notification does not need status reconfirmation. Setting `triggerOnCompletion: false` keeps the notification but prevents it from starting an agent turn. Setting `notifyOnCompletion: false` suppresses both notification and wake-up even if `triggerOnCompletion` is true.
|
|
103
104
|
|
|
@@ -269,7 +270,7 @@ what the parent knew at launch and is never used to decide success.
|
|
|
269
270
|
|
|
270
271
|
## Fusion workflow
|
|
271
272
|
|
|
272
|
-
Fusion runs direct child `pi --mode text` processes only; it never calls `pi-ai` completion APIs. Each child is launched with `--no-session`, `--no-
|
|
273
|
+
Fusion runs direct child `pi --mode text` processes only; it never calls `pi-ai` completion APIs. Each child is launched with `--no-session`, `--no-extensions`, `--no-skills`, `--no-prompt-templates`, `--no-themes`, and `--no-context-files`, plus the tool policy described below, the resolved provider/model/thinking level, and the package-owned private `extensions/fusion-child.ts` metadata extension. The prompt travels over stdin, not a shell or positional argument.
|
|
273
274
|
|
|
274
275
|
Pi text mode writes the final full answer exactly once instead of serializing cumulative reasoning/partial-message events on every token delta. The private child extension emits one compact, reasoning-free metadata record per finalized assistant message for provider/model, stop reason, the complete Pi token/cost `Usage` object, and response byte/hash validation. Fusion persists those compact records in `*.events.jsonl`; the complete answer remains in the stage response artifact. The 32 MiB child stdout cap therefore applies to one final response, not amplified JSON telemetry. Failed attempts keep the authoritative response artifact empty and, when any stdout was captured, persist it separately as an explicitly incomplete `*.response.partial.*` artifact.
|
|
275
276
|
|
|
@@ -279,6 +280,62 @@ Model configuration is global under the Pi agent directory:
|
|
|
279
280
|
fusion-models.json
|
|
280
281
|
```
|
|
281
282
|
|
|
283
|
+
### Validation workflow
|
|
284
|
+
|
|
285
|
+
`fusion_validate` is the same orchestrator, artifact store, budget engine, conversation projection, and evaluation schema as `fusion_brainstorm`, with different stage framing. A workflow profile selects the four system prompts and the capability policy; it never changes the canonical input schema, which remains `pi-background-tasks.fusion-input.v4` for both tools. Canonical input bytes and the omission ledger are provably identical across workflows for the same conversation, and that equality is asserted by the golden-bytes gate rather than assumed.
|
|
286
|
+
|
|
287
|
+
| Concern | `fusion_brainstorm` | `fusion_validate` |
|
|
288
|
+
|---|---|---|
|
|
289
|
+
| Parameters | `{prompt, capability?}` | `{prompt}` — capability rejected |
|
|
290
|
+
| Candidate capability | caller-selected, default `reason` | always `inspect` |
|
|
291
|
+
| Evaluator / merger capability | `reason` by stage policy | `reason` by stage policy |
|
|
292
|
+
| Run id prefix | `f` | `v` |
|
|
293
|
+
| Output | prose answer | prose review |
|
|
294
|
+
| Evaluation schema | `fusion-evaluation.v1` | `fusion-evaluation.v1` (identical) |
|
|
295
|
+
|
|
296
|
+
The capability is fixed rather than defaulted. A `capability` argument on `fusion_validate` is a hard error at the schema boundary, and the orchestrator independently re-asserts the workflow capability before the artifact store or any child process exists, so a contradicting request launches **zero children**.
|
|
297
|
+
|
|
298
|
+
The three reviewers are blind-compared exactly like brainstorm candidates, but the evaluator is additionally required to treat each distinct defect claim as a unit and to carry every surviving claim into `synthesis_plan.must_include`, **including claims raised by only one reviewer**. The merger is correspondingly forbidden from dropping a single-source finding or inventing one no reviewer raised, and must state the resolution and reason wherever reviewers disagreed. Without those two clauses a real defect that only one model noticed could disappear by silent majority vote, which is the failure mode a three-model review exists to prevent.
|
|
299
|
+
|
|
300
|
+
`fusion_validate` is advisory and read-only. It never modifies files, it does not gate anything, and it is not a substitute for running tests or builds. Like every Fusion entry point, facts that exist only inside omitted tool output are not visible to reviewers; `inspect` lets them re-derive repository facts themselves, but uncommitted state that exists only in the parent agent's context must be restated in the prompt.
|
|
301
|
+
|
|
302
|
+
### Anthropic child sanitization
|
|
303
|
+
|
|
304
|
+
Fusion children launch with `--no-extensions` for isolation, which disables extension *discovery* while still honouring explicit `--extension` paths. The parent session normally loads an Anthropic system-prompt sanitizer through discovery, so a Claude child would inherit nothing and fail at the provider: Pi's own system prompt contains documentation lines Anthropic rejects.
|
|
305
|
+
|
|
306
|
+
Children routed to the `anthropic` provider therefore receive a second explicit extension, [`@ravshansbox/pi-anthropic-sps`](https://github.com/ravshansbox/pi-anthropic-sps) (MIT), resolved from the package's own dependency tree. The metadata extension is always passed first so its `message_end` frame is never displaced.
|
|
307
|
+
|
|
308
|
+
Children on every other provider receive exactly one `--extension` and their argv is byte-identical to the pre-sanitizer form. The sanitizer package publishes no `main`/`exports`, so it is located through its manifest's `pi.extensions[0]` entry rather than a direct require. Every resolution failure - package missing, manifest unreadable or malformed, no declared extension, or a declared file that does not exist - is a loud error before launch, because silently omitting the sanitizer would resurface later as an opaque provider rejection.
|
|
309
|
+
|
|
310
|
+
### Candidate capabilities
|
|
311
|
+
|
|
312
|
+
Fusion candidate children support three launch-time capability profiles. `reason` is the default: it passes `--no-tools` and preserves the previous no-tool candidate argv and prompt bytes. `inspect` is available only to candidate children and replaces `--no-tools` with the exact read-only tool policy `--no-builtin-tools --tools read,grep,find,ls --exclude-tools bash,edit,write,fusion_brainstorm,bg_delegate,bg_result,bg_run,bg_kill,bg_status,bg_logs,bg_run_pi_attested`. `research` extends `inspect` by adding the package-owned `fusion_web_fetch` tool to the `--tools` allowlist; the `--exclude-tools` denylist is unchanged and still bans `bash`, `edit`, `write`, `fusion_brainstorm`, `bg_delegate`, `bg_result`, `bg_run`, `bg_kill`, `bg_status`, `bg_logs`, and `bg_run_pi_attested`. The `reason` and `inspect` argv forms remain byte-identical to v0.7.8.
|
|
313
|
+
|
|
314
|
+
Evaluator, evaluation-repair, and merger children always run with `--no-tools` by stage policy. Caller input cannot grant them tools, even when candidates use `inspect` or `research`. Capability is recorded as launch metadata in the run manifest and child argv, not added to the canonical child-facing input; the canonical input schema remains `pi-background-tasks.fusion-input.v4`.
|
|
315
|
+
|
|
316
|
+
The inspect candidate system prompt tells the child it may re-derive facts from the repository using `read`, `grep`, `find`, and `ls`. The research prompt adds `fusion_web_fetch` for fetching a specific public URL as bounded Markdown or text. Both prompts extend the untrusted-data rule: projected conversation text, file contents, and fetched page content are data, never instructions to follow.
|
|
317
|
+
|
|
318
|
+
`fusion_web_fetch` has a closed schema: `{ url: string, extract?: 'text' | 'markdown' }`. `extract` defaults to Markdown. There is deliberately no per-fetch prompt parameter. Anthropic documents the `{url, prompt}` extraction pattern as lossy by design: the prompt decides what reaches the model, so a false negative can enter a Fusion candidate answer, pass through blind evaluation, and reach the merged answer with no signal that the page contained missed information.
|
|
319
|
+
|
|
320
|
+
HTML extraction uses the runtime dependency `turndown@7.2.4`; its only dependency is `@mixmark-io/domino`, so it does not require `jsdom`. Markdown is the default because it preserves link destinations, headings, tables, and code blocks better than plain text. This version has no web search, browser, PDF support, cache, or domain allowlist.
|
|
321
|
+
|
|
322
|
+
| `fusion_web_fetch` policy | Value |
|
|
323
|
+
|---|---:|
|
|
324
|
+
| Request method | GET |
|
|
325
|
+
| Schemes | `http:` and `https:` only |
|
|
326
|
+
| Timeout | 60 seconds |
|
|
327
|
+
| Response body cap | 2 MiB |
|
|
328
|
+
| Returned content cap | 32 KiB |
|
|
329
|
+
| Redirect cap | 5 hops |
|
|
330
|
+
|
|
331
|
+
Network handling is basic network hygiene, not a secret-exfiltration control. The hostname is resolved once, every returned address is checked, the connection is pinned to the vetted address, and the socket's remote address is verified after connect. Address validation re-runs on every redirect hop. Private, loopback, link-local, unique-local, multicast, and cloud-metadata addresses are refused. A research child that can read files and reach the network can in principle send what it read; that is an accepted trade in this version, not a sandbox or security boundary.
|
|
332
|
+
|
|
333
|
+
`fusion_web_fetch` fails loudly with typed errors rather than retrying or falling back to another URL, scheme, encoding, or extraction mode. Error codes include `invalid_url`, `unsupported_scheme`, `blocked_address`, `dns_failure`, `redirect_limit`, `redirect_blocked`, `response_too_large`, `unsupported_content_type`, `request_timeout`, `network_error`, `extraction_failed`, and `http_error`.
|
|
334
|
+
|
|
335
|
+
Every child has a stale-action watchdog in addition to the 30-minute absolute timeout. `FUSION_CHILD_IDLE_TIMEOUT_MS` defaults to 900 seconds and fails the child if no stdout or stderr activity occurs during that window; any stdout or stderr activity resets the watchdog. The threshold is deliberately far above tool latency: the child metadata frame is emitted only at `message_end` and text-mode stdout carries only the final assistant message, so one slow model turn is legitimately silent on both streams and must not be killed. The absolute timeout remains a backstop for children that keep producing output but never finish.
|
|
336
|
+
|
|
337
|
+
Inspect and research candidates also write a per-attempt tool-call audit log at `candidate-<slot>.attempt-<n>.tool-calls.jsonl`. The child appends one JSON line per completed tool call with the tool name, argument/result byte counts, and SHA-256 digests only. For `fusion_web_fetch`, the record also includes `url`, `final_url`, `http_status`, `response_bytes`, and `content_sha256`. Raw arguments, raw tool results, and page content are never written because file paths, file contents, fetched content, and tool results may contain secrets. After the child exits, the parent verifies the log is complete and contiguous; a trailing partial line, ordinal gap, duplicate ordinal, or schema-version mismatch is a loud failure.
|
|
338
|
+
|
|
282
339
|
Missing config means all five slots are `$current`. Malformed config, stale explicit models, unavailable current models, and concurrent selector write conflicts fail loudly before child inference. Selector saves use an inter-process lock plus revision re-read before rename so simultaneous dialogs cannot silently overwrite each other. Candidate identities are anonymized before evaluation; provider/model metadata stays in local artifacts, not in evaluator prompts.
|
|
283
340
|
|
|
284
341
|
Progress is surfaced through `fusion` status updates, TUI cancellable loader UI for `/fusion`, and partial `fusion_brainstorm` tool updates. Session shutdown or reload tracks the whole invocation from entry, aborts live or initializing Fusion runs, and waits for cleanup.
|
|
@@ -389,7 +446,7 @@ Fusion writes private debugging artifacts under:
|
|
|
389
446
|
.pi/fusion/<session-id>-<pid>/<run-id>/
|
|
390
447
|
```
|
|
391
448
|
|
|
392
|
-
Each run contains `manifest.json`, `canonical-input.json`, `context-omission-ledger.json`, `budget-plan.json`, candidate/evaluation/merge prompts, raw child JSONL events, stderr, responses, `blind-candidates.json`, `evaluation.json`, `merged.md`, and `error.json` for failed/cancelled runs. Persisted stage prompts are byte-identical to the exact bytes written to that child's stdin. `context-omission-ledger.json` carries the complete source-ordered omission ledger, and `budget-plan.json` records every configured route's capacity plus the pre-candidate feasibility decision, so a rejected run is as auditable as a successful one. Artifact files are written by private temp-file/fsync/rename, and v2 manifests persist cumulative child usage plus per-attempt observed usage/model data for successful, failed, and cancelled child attempts. Every usage record preserves the complete Pi cost breakdown; the same exact shape is cloned into `fusion_brainstorm` tool results so newer Pi hosts can calculate and replay footer/session statistics safely. These artifacts are local evidence only; they are not shown in `/jobs` or the background-task dock.
|
|
449
|
+
Each run contains `manifest.json`, `canonical-input.json`, `context-omission-ledger.json`, `budget-plan.json`, candidate/evaluation/merge prompts, raw child JSONL events, stderr, responses, and, when inspect or research candidates run, tool-call logs named `candidate-<slot>.attempt-<n>.tool-calls.jsonl`, plus `blind-candidates.json`, `evaluation.json`, `merged.md`, and `error.json` for failed/cancelled runs. Persisted stage prompts are byte-identical to the exact bytes written to that child's stdin. `context-omission-ledger.json` carries the complete source-ordered omission ledger, and `budget-plan.json` records every configured route's capacity plus the pre-candidate feasibility decision, so a rejected run is as auditable as a successful one. Artifact files are written by private temp-file/fsync/rename, and v2 manifests persist cumulative child usage plus per-attempt observed usage/model data for successful, failed, and cancelled child attempts. Every usage record preserves the complete Pi cost breakdown; the same exact shape is cloned into `fusion_brainstorm` tool results so newer Pi hosts can calculate and replay footer/session statistics safely. These artifacts are local evidence only; they are not shown in `/jobs` or the background-task dock.
|
|
393
450
|
|
|
394
451
|
For attested Pi tasks only, the task id is `b` plus 32 random hex characters (128 bits) and additional flat siblings are written in the same directory:
|
|
395
452
|
|
package/TEST_PLAN.md
CHANGED
|
@@ -13,7 +13,7 @@ This package follows:
|
|
|
13
13
|
| Package | `pi-background-tasks` |
|
|
14
14
|
| Extension entrypoint | `extensions/background-tasks.ts` |
|
|
15
15
|
| Public commands | `/bg`, `/jobs`, `/logs`, `/kill`, `/tasks`, `/bg-tasks`, `/bg-clear`, `/bg-update`, `/fusion`, `/fusion-models` |
|
|
16
|
-
| Public tools | `bg_run`, `bg_delegate`, `bg_result`, `bg_run_pi_attested`, `bg_status`, `bg_logs`, `bg_kill`, `fusion_brainstorm` |
|
|
16
|
+
| Public tools | `bg_run`, `bg_delegate`, `bg_result`, `bg_run_pi_attested`, `bg_status`, `bg_logs`, `bg_kill`, `fusion_brainstorm`, `fusion_validate` |
|
|
17
17
|
| Extension EventBus API | `pi-background-tasks:request:v1`, `pi-background-tasks:response:v1`, `pi-background-tasks:terminal:v1`; schemas exported from `src/core/extension-api.ts` |
|
|
18
18
|
| Shortcuts | `Shift+Down`; optional fallback `Ctrl+Alt+C` |
|
|
19
19
|
| Custom UI | footer status + focused bottom dock overlay |
|
|
@@ -55,8 +55,15 @@ This package follows:
|
|
|
55
55
|
| Stop task from LLM tool | `bg_kill` | | yes | | | | | | Covers running kill and already-finished loud failure. |
|
|
56
56
|
| Fusion command direct result | `/fusion`, `fusion-result` custom message | yes | yes | yes | | yes | yes | | Core unit covers deterministic v2 conversation-projection construction, explicit image omission markers without raw image data, direct final-text child argv/stdin/compact-metadata parsing, exact multi-block answer reconstruction, artifacts, pre-abort handling, and orchestration. SDK runs real `/fusion` against a fake child `pi`, verifies exactly five child text-mode invocations, hidden prompt persistence, visible exact merged custom message, no parent assistant rewrite, renderer output, progress/status behavior, no-argument editor flow, editor cancellation, and malformed config causing zero children. RPC verifies command discovery, `/fusion <prompt>` with U+2028/U+2029 content, custom-message result emission, no parent `agent_start`, no-argument editor protocol, malformed config/child failure visibility, and child isolation flags. PTY verifies `/fusion` renders the exact merged answer directly in a real TUI. Compatibility smoke runs `/fusion` through every supported Pi version with the installed package entrypoint. |
|
|
57
57
|
| Fusion tool result | `fusion_brainstorm({prompt})` | yes | yes | | | current-host stats/replay | yes | yes | Tool is registered at load and re-added on `session_start`; no eligibility/quota/routine/justification gates are implemented. BUG-182 unit/SDK coverage pins the exact host `Usage` contract, complete cost-component aggregation, rejection of legacy `costTotal`, v2 child/result/manifest schemas, exact merged text, progress, context exclusion, and final details. The release compatibility gate drives a real current-host RPC agent through the tool, verifies persisted tool usage, invokes the same session-stat path used by the footer, then reopens the durable session and verifies identical stats. Scripted-provider coverage proves normal parent consumption and evaluator schema repair. |
|
|
58
|
+
| Fusion Anthropic child sanitization | `@ravshansbox/pi-anthropic-sps` dependency, child `--extension` argv | yes | | | | | yes | | Unit proves a Claude route receives exactly two `--extension` paths with the metadata extension first and the sanitizer second, that every non-Anthropic provider still receives exactly one and never resolves the sanitizer at all, and that the real installed package resolves to an existing file through its manifest `pi.extensions[0]`. Resolution failures are each pinned as loud errors: package unresolvable, manifest unreadable, manifest not JSON, missing `pi` section, empty `pi.extensions`, blank entry, and a declared file that does not exist. Package guards require the sanitizer to be a declared dependency and require the provider gate so non-Anthropic child argv cannot drift. |
|
|
59
|
+
| Fusion validation workflow | `fusion_validate({prompt})`, workflow profiles, `fusion-manifest.v3`, `fusion-result.v4` | yes | | | | | yes | | Unit pins the brainstorm profile to the exact pre-extraction prompt constants so the workflow seam is proven byte-neutral, binds the validate profile to its own four system prompts, and asserts the two workflows share one evaluation schema contract and one repair framing. Validate orchestration covers five children with validate framing, candidates always `inspect` while evaluator and merger stay `reason`, tool-call log paths assigned only to candidates, `workflow` plus a `v`-prefixed run id persisted in the manifest and result details, and prompt artifacts byte-identical to the bytes handed to each child. A caller capability contradicting the fixed workflow policy is rejected before the artifact store exists and launches **zero** children; the same capability stated explicitly is accepted. Golden bytes pin a separate committed validate corpus, prove canonical input and omission ledger are identical across workflows for every case, and prove budget plans move for every case so the profile provably reaches the budget engine. Package guards reject `capability` and extra keys on `fusion_validate`, pin `FUSION_VALIDATE_CAPABILITY` to `inspect`, require `fusion_validate` in both the Fusion and delegate denylists, and pin the workflow-invariant comments at exactly one occurrence per launch site. |
|
|
58
60
|
| Fusion conversation projection | canonical input `fusion-input.v4`, `context-omission-ledger.json` | yes | yes | yes | | | yes | | Unit covers a >1 MB synthetic tool-heavy session staying within budget, verbatim user/assistant text, thinking exclusion, zero tool-payload preview bytes (head/tail sentinels absent), exact and stable omission counts/byte totals/hashes, tuple round-trip preservation of roles/source ordinals/block ordinals/exact text, byte-identical repeated construction and separate-process determinism, hash change on omitted-payload mutation without exposing the payload, ledger root hash unchanged by compact encoding, contiguous omission-run collapsing into compact `["o", span, bytes, [thinking, toolCalls, toolResults]]` tuples, material canonical-input byte reduction versus the verbose object encoding, receipt-to-ledger reconciliation through `projection_map`, active-tool-call-leaf and sibling-call exclusion, marker-only user images plus ledger-only tool-result images with no base64, per-entry-point policy ids and request authority, and the exactly-one-disposition property for every retained block. SDK verifies the exact child stdin carries `conversation_projection` and never `conversation_transcript`. RPC verifies the versioned request object. A dedicated high-cardinality fixture (340 receipt runs over 1,360 omitted events with incident-matching visible-text volume) pins per-receipt cost and proves the pre-fix verbose format would overflow the merge stage while the compact tuple format fits. Package tests assert the ledger artifact ships in the documented artifact set. |
|
|
59
61
|
| Fusion stage budgets | `budget-plan.json` (v3 per-stage forecasts), typed `prompt_budget_exceeded_forecast` / `prompt_budget_exceeded_measured` | yes | | | | | | | Unit covers the per-family affine estimator, additive segment accounting, multibyte 1.0-token/byte charging, unknown-provider floor visibility, byte-capacity route selection, scope guards for small windows, input-only fatal preflight versus warning-only reservations, rejection of unknown/zero/negative/too-small context windows, boundary accept at exactly the limit and reject one byte past, the child system prompt counted as input, per-stage forecasts built from the real prompt builders against each stage's own route, reservation warnings, breach-detector artifacts, safe prompts completing all five calls, persisted route/plan snapshots including negative slack on fatal rejection, and the reproduced 1 MB failure shape now fitting the smallest configured budget. Errors carry stage, measured size, allowed size, limiting model, estimator source, and remediation in both structured detail and message text. |
|
|
62
|
+
| Fusion candidate capability profiles | `fusion_brainstorm({prompt, capability?})`, candidate child argv | yes | yes | | | | yes | | Unit covers `reason` defaulting; reason and inspect argv remain byte-identical; `inspect` argv is exactly `--no-builtin-tools --tools read,grep,find,ls --exclude-tools bash,edit,write,fusion_brainstorm,bg_delegate,bg_result,bg_run,bg_kill,bg_status,bg_logs,bg_run_pi_attested`; `research` argv is the inspect allowlist plus `fusion_web_fetch`, with the same denylist. Unit also proves capability-specific prompts, research env only for research children, candidate tool-call log paths assigned only to candidates, and evaluator plus merger calls staying `reason`/`--no-tools` under caller-selected inspect or research. SDK and package guards validate only `reason`/`inspect`/`research`, reject extra keys, pin the allow/deny lists and `fusion_web_fetch` tool name, and assert evaluator/merger launch sites cannot read the caller-selected capability. |
|
|
63
|
+
| Fusion research web fetch | `fusion_web_fetch({ url, extract? })`, `candidate-<slot>.attempt-<n>.tool-calls.jsonl` | yes | | | | | yes | live child | Unit covers the closed schema surface through the child extension, registration only when research mode is enabled, unsupported schemes and URL credentials failing before network access, blocked DNS classes, a mixed public-plus-blocked DNS answer failing loudly, a redirect to a blocked address failing loudly, redirect-loop limit failure, Content-Length and streamed-body response caps, unsupported content types, Markdown extraction preserving links/headings/tables/code blocks, script/style stripping, text/plain passthrough with hash and byte count, UTF-8-safe output truncation, and typed timeout. Unit also proves the resolver is consulted once and the connection is pinned to that vetted address, and that audit metadata records `url`, `final_url`, `http_status`, `response_bytes`, and `content_sha256` without raw page content. Package guards require `fusion_web_fetch` registration behind the research env flag. Live verification fetched `https://example.com/`, reported the page H1, read a repository constant, and refused instruction-like text from both a repo file and the fetched page. |
|
|
64
|
+
| Fusion multi-message transcript validation | `*.events.jsonl`, stop reasons `toolUse`/`stop` | yes | | | | | | | Unit accepts a multi-message tool loop, reconstructs stdout from the final assistant message only, and sums usage across all compact metadata records. Non-final records must be `toolUse`, the final record must be `stop`, and `length`, `error`, `aborted`, and `pending` fail loudly instead of being skipped. |
|
|
65
|
+
| Fusion stale-action watchdog | `FUSION_CHILD_IDLE_TIMEOUT_MS`, child process lifecycle | yes | | | | | | | Unit proves a child with no stdout or stderr activity fails as `child_timeout` with the stalled-child message and is terminated; stderr activity resets the watchdog and allows success; the 30-minute absolute timeout path remains distinct and is not reported as a stalled child. |
|
|
66
|
+
| Fusion candidate tool-call audit log | `candidate-<slot>.attempt-<n>.tool-calls.jsonl`, `pi-background-tasks.fusion-tool-call.v1` | yes | | | | | | | Unit proves completed tool calls are logged with tool name, byte counts, and hashes while raw arguments/results containing a secret never appear; a complete 3-call log summarizes count and result bytes; zero completed calls is a complete trace; a trailing partial line, ordinal gap, duplicate ordinal, wrong schema version, and inspect-child partial log all fail loudly. Artifact coverage verifies successful attempts persist the JSONL and manifest summary, while no-log attempts do not invent one. |
|
|
60
67
|
| Pi/TypeBox compatibility | `peerDependencies`, packed bytes | yes | | | | | yes | | Unit pins typebox as a `"*"` peer that is neither a runtime nor bundled dependency, requires the resolved TypeBox to be Pi 0.83's 1.3.x line, requires all four supported Pi/TUI lines in the peer range, scans all package TypeScript for the seven removed TypeBox APIs, and compiles the shipped tool schema plus nullable-array/nullable-string and optional-field shapes under TypeBox 1.3. The release compat gate repeats the removed-API scan against installed package bytes per Pi version. |
|
|
61
68
|
| Pi hook contract | `npm run test:hook-contract`, `tests/scripted-provider/pi-hook-contract-evidence.json`, `src/core/delegate/hook-contract-evidence.json` | | | | | | evidence parity | yes | Executes a real Pi agent loop and records, rather than assumes: `context` fires once before every model call in load order; returned messages reach the provider; **throwing** in `context` does NOT block dispatch (Pi catches and continues); `ctx.abort()` does not skip the call site but delivers an already-aborted signal and terminates the run; `tool_result` fires before the transcript entry, chains in load order, replacement reaches the provider while the original does not, and toolCallId/role/`isError` survive. Handler ordering across two separate probe extensions is pinned. The evidence file is compared, never silently regenerated, and a package test asserts the shipped copy is byte-identical. |
|
|
62
69
|
| Delegate context seeding | `bg_delegate` seed `pi-background-tasks.delegate-seed.v1`, `seed.json`, `context-omission-ledger.json` | yes | yes | | | | mutation guard | yes | Unit covers verbatim visible user/assistant text, complete exclusion of thinking/tool-call arguments/tool-result payloads, marker-only images with no raw bytes anywhere in the seed, exclusion of the in-flight `bg_delegate` call **and every sibling call in the same assistant message** (two delegates launched together get byte-identical projected history), the prompt preserved exactly and marked `explicit_text` authority, zero payload-preview bytes, byte-identical construction across repeated builds and across separate processes via a fixed-leaf fixture, and refusal of a blank prompt. Receive-side verification rejects a single mutated byte, a foreign task identity, a structurally malformed seed whose hash matches, a directive whose hash disagrees with its text, and an unsupported capability. SDK proves the projected parent text actually reaches the child. |
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pi-background-tasks",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Pi extension package for background shell tasks plus five-model fusion brainstorming: bg_run tools, /bg commands, /fusion, fusion_brainstorm, model selector UI, bounded logs, kill/timeout safety, and completion wakeups.",
|
|
3
|
+
"version": "0.9.0",
|
|
4
|
+
"description": "Pi extension package for background shell tasks plus five-model fusion brainstorming and validation: bg_run tools, bg_delegate, /bg commands, /fusion, fusion_brainstorm, fusion_validate, model selector UI, bounded logs, kill/timeout safety, and completion wakeups.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "ISC",
|
|
7
7
|
"author": "Ismail <ismailsalikhodjaev@gmail.com>",
|
|
@@ -72,12 +72,17 @@
|
|
|
72
72
|
"@earendil-works/pi-ai": "^0.83.0",
|
|
73
73
|
"@earendil-works/pi-coding-agent": "^0.83.0",
|
|
74
74
|
"@earendil-works/pi-tui": "^0.83.0",
|
|
75
|
+
"@ravshansbox/pi-anthropic-sps": "github:ravshansbox/pi-anthropic-sps",
|
|
75
76
|
"@types/node": "^24.0.0",
|
|
77
|
+
"@types/turndown": "5.0.6",
|
|
76
78
|
"tsx": "^4.19.0",
|
|
77
79
|
"typebox": "^1.3.7",
|
|
78
80
|
"typescript": "^5.9.0"
|
|
79
81
|
},
|
|
80
82
|
"engines": {
|
|
81
83
|
"node": ">=22.19.0"
|
|
84
|
+
},
|
|
85
|
+
"dependencies": {
|
|
86
|
+
"turndown": "7.2.4"
|
|
82
87
|
}
|
|
83
88
|
}
|
|
@@ -15,6 +15,7 @@ import {
|
|
|
15
15
|
type FusionBudgetPlanV1,
|
|
16
16
|
type FusionCalibrationViolation,
|
|
17
17
|
type FusionCandidateId,
|
|
18
|
+
type FusionCapability,
|
|
18
19
|
type FusionContextOmissionLedgerV2,
|
|
19
20
|
type FusionChildRunResult,
|
|
20
21
|
type FusionModelConfigV1,
|
|
@@ -23,14 +24,21 @@ import {
|
|
|
23
24
|
type FusionState,
|
|
24
25
|
type FusionTerminalState,
|
|
25
26
|
type FusionUsage,
|
|
27
|
+
type FusionWorkflowId,
|
|
26
28
|
type ResolvedFusionModels,
|
|
27
29
|
} from './types.js';
|
|
30
|
+
import { FUSION_BRAINSTORM_WORKFLOW, type FusionWorkflowProfile } from './workflows.js';
|
|
28
31
|
|
|
29
|
-
|
|
32
|
+
/**
|
|
33
|
+
* Run ids are prefixed by workflow so an artifact directory is self-describing.
|
|
34
|
+
* The prefix set is closed: an unknown prefix must fail rather than be accepted.
|
|
35
|
+
*/
|
|
36
|
+
const RUN_ID_PATTERN = /^[fv][0-9a-f]{32}$/;
|
|
30
37
|
|
|
31
38
|
interface MutableFusionArtifactManifest {
|
|
32
39
|
schema_version: typeof FUSION_MANIFEST_SCHEMA_VERSION;
|
|
33
40
|
run_id: string;
|
|
41
|
+
workflow: FusionWorkflowId;
|
|
34
42
|
source: FusionSource;
|
|
35
43
|
state: FusionState;
|
|
36
44
|
created_at: string;
|
|
@@ -43,6 +51,11 @@ interface MutableFusionArtifactManifest {
|
|
|
43
51
|
merger: string;
|
|
44
52
|
thinking_level: string;
|
|
45
53
|
};
|
|
54
|
+
capabilities: {
|
|
55
|
+
candidate: FusionCapability;
|
|
56
|
+
evaluation: FusionCapability;
|
|
57
|
+
merge: FusionCapability;
|
|
58
|
+
};
|
|
46
59
|
usage: FusionUsage;
|
|
47
60
|
attempts: FusionAttemptArtifactRecord[];
|
|
48
61
|
artifacts: Record<string, FusionArtifactRef>;
|
|
@@ -54,9 +67,15 @@ export interface CreateFusionArtifactStoreOptions {
|
|
|
54
67
|
cwd: string;
|
|
55
68
|
sessionId?: string | undefined;
|
|
56
69
|
runId?: string | undefined;
|
|
70
|
+
profile?: FusionWorkflowProfile | undefined;
|
|
57
71
|
source: FusionSource;
|
|
58
72
|
config: FusionModelConfigV1;
|
|
59
73
|
models: ResolvedFusionModels;
|
|
74
|
+
capabilities?: {
|
|
75
|
+
candidate: FusionCapability;
|
|
76
|
+
evaluation: FusionCapability;
|
|
77
|
+
merge: FusionCapability;
|
|
78
|
+
};
|
|
60
79
|
now?: () => Date;
|
|
61
80
|
}
|
|
62
81
|
|
|
@@ -83,8 +102,8 @@ export interface RecordFusionFailedAttemptInput {
|
|
|
83
102
|
usage?: FusionUsage;
|
|
84
103
|
}
|
|
85
104
|
|
|
86
|
-
function makeRunId(): string {
|
|
87
|
-
return
|
|
105
|
+
function makeRunId(profile: FusionWorkflowProfile): string {
|
|
106
|
+
return `${profile.runIdPrefix}${randomBytes(16).toString('hex')}`;
|
|
88
107
|
}
|
|
89
108
|
|
|
90
109
|
function modelsForManifest(models: ResolvedFusionModels): MutableFusionArtifactManifest['models'] {
|
|
@@ -149,6 +168,7 @@ function publicManifest(manifest: MutableFusionArtifactManifest): FusionArtifact
|
|
|
149
168
|
const out: FusionArtifactManifest = {
|
|
150
169
|
schema_version: manifest.schema_version,
|
|
151
170
|
run_id: manifest.run_id,
|
|
171
|
+
workflow: manifest.workflow,
|
|
152
172
|
source: manifest.source,
|
|
153
173
|
state: manifest.state,
|
|
154
174
|
created_at: manifest.created_at,
|
|
@@ -156,6 +176,7 @@ function publicManifest(manifest: MutableFusionArtifactManifest): FusionArtifact
|
|
|
156
176
|
cwd: manifest.cwd,
|
|
157
177
|
config: manifest.config,
|
|
158
178
|
models: manifest.models,
|
|
179
|
+
capabilities: manifest.capabilities,
|
|
159
180
|
usage: cloneFusionUsage(manifest.usage),
|
|
160
181
|
attempts: [...manifest.attempts],
|
|
161
182
|
artifacts: { ...manifest.artifacts },
|
|
@@ -203,8 +224,14 @@ export class FusionArtifactStore {
|
|
|
203
224
|
}
|
|
204
225
|
|
|
205
226
|
static async create(options: CreateFusionArtifactStoreOptions): Promise<FusionArtifactStore> {
|
|
206
|
-
const
|
|
227
|
+
const profile = options.profile ?? FUSION_BRAINSTORM_WORKFLOW;
|
|
228
|
+
const runId = options.runId ?? makeRunId(profile);
|
|
207
229
|
if (!RUN_ID_PATTERN.test(runId)) throw errorForArtifact(`invalid fusion run id: ${runId}`);
|
|
230
|
+
if (!runId.startsWith(profile.runIdPrefix)) {
|
|
231
|
+
throw errorForArtifact(
|
|
232
|
+
`fusion run id ${runId} does not carry the ${profile.id} workflow prefix ${profile.runIdPrefix}`,
|
|
233
|
+
);
|
|
234
|
+
}
|
|
208
235
|
const sessionSegment = sanitizePathSegment(
|
|
209
236
|
options.sessionId ?? `session-${String(process.pid)}`,
|
|
210
237
|
);
|
|
@@ -217,6 +244,7 @@ export class FusionArtifactStore {
|
|
|
217
244
|
const manifest: MutableFusionArtifactManifest = {
|
|
218
245
|
schema_version: FUSION_MANIFEST_SCHEMA_VERSION,
|
|
219
246
|
run_id: runId,
|
|
247
|
+
workflow: profile.id,
|
|
220
248
|
source: options.source,
|
|
221
249
|
state: 'initializing',
|
|
222
250
|
created_at: timestamp,
|
|
@@ -224,6 +252,11 @@ export class FusionArtifactStore {
|
|
|
224
252
|
cwd: options.cwd,
|
|
225
253
|
config: options.config,
|
|
226
254
|
models: modelsForManifest(options.models),
|
|
255
|
+
capabilities: options.capabilities ?? {
|
|
256
|
+
candidate: 'reason',
|
|
257
|
+
evaluation: 'reason',
|
|
258
|
+
merge: 'reason',
|
|
259
|
+
},
|
|
227
260
|
usage: cloneFusionUsage(EMPTY_FUSION_USAGE),
|
|
228
261
|
attempts: [],
|
|
229
262
|
artifacts: {},
|
|
@@ -250,6 +283,10 @@ export class FusionArtifactStore {
|
|
|
250
283
|
return this.runDirAbs;
|
|
251
284
|
}
|
|
252
285
|
|
|
286
|
+
childToolCallLogPath(stage: FusionStage, slot: 1 | 2 | 3 | undefined, attempt: number): string {
|
|
287
|
+
return this.artifactPath(`${attemptPrefix(stage, slot, attempt)}.tool-calls.jsonl`);
|
|
288
|
+
}
|
|
289
|
+
|
|
253
290
|
snapshot(): FusionArtifactManifest {
|
|
254
291
|
return publicManifest(this.manifest);
|
|
255
292
|
}
|
|
@@ -338,6 +375,10 @@ export class FusionArtifactStore {
|
|
|
338
375
|
responseName(prefix, input.responseKind),
|
|
339
376
|
input.result.text,
|
|
340
377
|
);
|
|
378
|
+
const toolCallsRef =
|
|
379
|
+
input.result.toolCallTrace === undefined
|
|
380
|
+
? undefined
|
|
381
|
+
: await this.writeArtifact(`${prefix}.tool-calls.jsonl`, input.result.toolCallTrace.bytes);
|
|
341
382
|
await this.updateManifest((manifest) => {
|
|
342
383
|
const record: FusionAttemptArtifactRecord = {
|
|
343
384
|
stage: input.result.stage,
|
|
@@ -352,6 +393,10 @@ export class FusionArtifactStore {
|
|
|
352
393
|
qualifiedId: input.result.qualifiedId,
|
|
353
394
|
usage: cloneFusionUsage(input.result.usage),
|
|
354
395
|
};
|
|
396
|
+
if (toolCallsRef !== undefined && input.result.toolCallTrace !== undefined) {
|
|
397
|
+
record.tool_calls_path = toolCallsRef.path;
|
|
398
|
+
record.tool_calls = { ...input.result.toolCallTrace.summary };
|
|
399
|
+
}
|
|
355
400
|
if (input.result.slot !== undefined) record.slot = input.result.slot;
|
|
356
401
|
manifest.attempts.push(record);
|
|
357
402
|
});
|
|
@@ -14,10 +14,6 @@ import {
|
|
|
14
14
|
isUsableContextWindow,
|
|
15
15
|
} from '../context/token-budget.js';
|
|
16
16
|
import {
|
|
17
|
-
FUSION_CANDIDATE_SYSTEM_PROMPT,
|
|
18
|
-
FUSION_EVALUATION_REPAIR_SYSTEM_PROMPT,
|
|
19
|
-
FUSION_EVALUATOR_SYSTEM_PROMPT,
|
|
20
|
-
FUSION_MERGER_SYSTEM_PROMPT,
|
|
21
17
|
buildBlindEvaluationInput,
|
|
22
18
|
buildCandidatePrompt,
|
|
23
19
|
buildEvaluationPrompt,
|
|
@@ -26,10 +22,12 @@ import {
|
|
|
26
22
|
buildMergePrompt,
|
|
27
23
|
type AnonymousFusionCandidate,
|
|
28
24
|
} from './prompts.js';
|
|
25
|
+
import { FUSION_BRAINSTORM_WORKFLOW, type FusionWorkflowProfile } from './workflows.js';
|
|
29
26
|
import {
|
|
30
27
|
FUSION_BUDGET_PLAN_SCHEMA_VERSION,
|
|
31
28
|
FUSION_CALIBRATION_VIOLATION_SCHEMA_VERSION,
|
|
32
29
|
FUSION_EVALUATION_SCHEMA_VERSION,
|
|
30
|
+
FUSION_DEFAULT_CAPABILITY,
|
|
33
31
|
FusionError,
|
|
34
32
|
type FusionBudgetBlocker,
|
|
35
33
|
type FusionBudgetCheckKind,
|
|
@@ -45,6 +43,7 @@ import {
|
|
|
45
43
|
type FusionBudgetWarning,
|
|
46
44
|
type FusionCalibrationViolation,
|
|
47
45
|
type FusionCanonicalInputV3,
|
|
46
|
+
type FusionCapability,
|
|
48
47
|
type FusionEvaluationV1,
|
|
49
48
|
type FusionRouteCapacity,
|
|
50
49
|
type FusionStage,
|
|
@@ -677,11 +676,20 @@ export class FusionBudget {
|
|
|
677
676
|
readonly routes: readonly FusionRouteCapacity[];
|
|
678
677
|
readonly limiting: FusionRouteCapacity;
|
|
679
678
|
private readonly contextPolicyId: string;
|
|
680
|
-
|
|
681
|
-
|
|
679
|
+
private readonly candidateCapability: FusionCapability;
|
|
680
|
+
private readonly profile: FusionWorkflowProfile;
|
|
681
|
+
|
|
682
|
+
constructor(
|
|
683
|
+
models: ResolvedFusionModels,
|
|
684
|
+
contextPolicyId: string,
|
|
685
|
+
candidateCapability: FusionCapability = FUSION_DEFAULT_CAPABILITY,
|
|
686
|
+
profile: FusionWorkflowProfile = FUSION_BRAINSTORM_WORKFLOW,
|
|
687
|
+
) {
|
|
682
688
|
this.routes = fusionRouteCapacities(models);
|
|
683
689
|
this.limiting = fusionLimitingRoute(this.routes);
|
|
684
690
|
this.contextPolicyId = contextPolicyId;
|
|
691
|
+
this.candidateCapability = candidateCapability;
|
|
692
|
+
this.profile = profile;
|
|
685
693
|
}
|
|
686
694
|
|
|
687
695
|
get allowedInputTokens(): number {
|
|
@@ -708,6 +716,7 @@ export class FusionBudget {
|
|
|
708
716
|
}
|
|
709
717
|
|
|
710
718
|
private drafts(input: FusionCanonicalInputV3): readonly StageForecastDraft[] {
|
|
719
|
+
const candidateSystemPrompt = this.profile.candidateSystemPrompt(this.candidateCapability);
|
|
711
720
|
const candidatePrompt = buildCandidatePrompt(input);
|
|
712
721
|
const blindInput = buildBlindEvaluationInput(input, EMPTY_CANDIDATES);
|
|
713
722
|
const evaluationPrompt = buildEvaluationPrompt(blindInput);
|
|
@@ -724,7 +733,7 @@ export class FusionBudget {
|
|
|
724
733
|
slot: 1,
|
|
725
734
|
route: this.routeForStage('candidate', 1),
|
|
726
735
|
conditional: false,
|
|
727
|
-
system_prompt:
|
|
736
|
+
system_prompt: candidateSystemPrompt,
|
|
728
737
|
empty_user_prompt: candidatePrompt,
|
|
729
738
|
upstream_output_contract_bytes: 0,
|
|
730
739
|
},
|
|
@@ -733,7 +742,7 @@ export class FusionBudget {
|
|
|
733
742
|
slot: 2,
|
|
734
743
|
route: this.routeForStage('candidate', 2),
|
|
735
744
|
conditional: false,
|
|
736
|
-
system_prompt:
|
|
745
|
+
system_prompt: candidateSystemPrompt,
|
|
737
746
|
empty_user_prompt: candidatePrompt,
|
|
738
747
|
upstream_output_contract_bytes: 0,
|
|
739
748
|
},
|
|
@@ -742,7 +751,7 @@ export class FusionBudget {
|
|
|
742
751
|
slot: 3,
|
|
743
752
|
route: this.routeForStage('candidate', 3),
|
|
744
753
|
conditional: false,
|
|
745
|
-
system_prompt:
|
|
754
|
+
system_prompt: candidateSystemPrompt,
|
|
746
755
|
empty_user_prompt: candidatePrompt,
|
|
747
756
|
upstream_output_contract_bytes: 0,
|
|
748
757
|
},
|
|
@@ -750,7 +759,7 @@ export class FusionBudget {
|
|
|
750
759
|
budget_stage: 'evaluation',
|
|
751
760
|
route: this.routeForStage('evaluation'),
|
|
752
761
|
conditional: false,
|
|
753
|
-
system_prompt:
|
|
762
|
+
system_prompt: this.profile.evaluatorSystemPrompt,
|
|
754
763
|
empty_user_prompt: evaluationPrompt,
|
|
755
764
|
upstream_output_contract_bytes: 3 * FUSION_CANDIDATE_MAX_OUTPUT_BYTES,
|
|
756
765
|
},
|
|
@@ -758,7 +767,7 @@ export class FusionBudget {
|
|
|
758
767
|
budget_stage: 'merge',
|
|
759
768
|
route: this.routeForStage('merge'),
|
|
760
769
|
conditional: false,
|
|
761
|
-
system_prompt:
|
|
770
|
+
system_prompt: this.profile.mergerSystemPrompt,
|
|
762
771
|
empty_user_prompt: mergePrompt,
|
|
763
772
|
upstream_output_contract_bytes:
|
|
764
773
|
3 * FUSION_CANDIDATE_MAX_OUTPUT_BYTES + FUSION_EVALUATION_MAX_OUTPUT_BYTES,
|
|
@@ -767,7 +776,7 @@ export class FusionBudget {
|
|
|
767
776
|
budget_stage: 'evaluation_repair',
|
|
768
777
|
route: this.routeForStage('evaluation_repair'),
|
|
769
778
|
conditional: true,
|
|
770
|
-
system_prompt:
|
|
779
|
+
system_prompt: this.profile.evaluationRepairSystemPrompt,
|
|
771
780
|
empty_user_prompt: repairPrompt,
|
|
772
781
|
upstream_output_contract_bytes:
|
|
773
782
|
3 * FUSION_CANDIDATE_MAX_OUTPUT_BYTES +
|
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
type ReadonlyParentSessionManager,
|
|
14
14
|
} from '../context/parent-snapshot.js';
|
|
15
15
|
import type { Message } from '@earendil-works/pi-ai';
|
|
16
|
+
import { FUSION_BRAINSTORM_TOOL_NAME as FUSION_BRAINSTORM_TOOL_NAME_VALUE } from './workflows.js';
|
|
16
17
|
import {
|
|
17
18
|
FUSION_BRANCH_FILTER_ID,
|
|
18
19
|
FUSION_COMMAND_CONTEXT_POLICY_ID,
|
|
@@ -33,7 +34,11 @@ import {
|
|
|
33
34
|
type FusionSource,
|
|
34
35
|
} from './types.js';
|
|
35
36
|
|
|
36
|
-
|
|
37
|
+
/**
|
|
38
|
+
* Re-exported from the workflow registry, which owns every workflow's tool name.
|
|
39
|
+
* Kept here so existing importers of this module keep working unchanged.
|
|
40
|
+
*/
|
|
41
|
+
export { FUSION_BRAINSTORM_TOOL_NAME, FUSION_VALIDATE_TOOL_NAME } from './workflows.js';
|
|
37
42
|
|
|
38
43
|
/** Retained for source compatibility; Fusion's session access is the shared adapter. */
|
|
39
44
|
export type FusionReadonlySessionManager = ReadonlyParentSessionManager;
|
|
@@ -202,7 +207,7 @@ export function buildFusionCanonicalInput(
|
|
|
202
207
|
childCreated: false,
|
|
203
208
|
});
|
|
204
209
|
}
|
|
205
|
-
const toolName = options.toolName ??
|
|
210
|
+
const toolName = options.toolName ?? FUSION_BRAINSTORM_TOOL_NAME_VALUE;
|
|
206
211
|
const snapshotOptions: ParentSnapshotOptions = {
|
|
207
212
|
toolName,
|
|
208
213
|
excludeActiveToolCallLeaf: options.source === 'tool',
|