feat(fusion): batch trim by turn policy, claims need evidence, worker limits and hand-back, reasoning caps, cloud worker cap, pinned request (F8, F9, F19–F22) - #434
Open
plombeer31 wants to merge 8 commits into
Conversation
Before choosing the survivor of an approval-gated batch, drop the calls plan mode or the fusion orchestrator gate would refuse; on an orchestrator turn prefer fusion.delegate over an earlier mutation. One shared predicate — wouldRefuse(toolName, ctx) on both gates — so the trim and the dispatch gates cannot disagree. The next-step notice names refused calls with their gate and separates them from the calls to retry; the batch_trimmed event and its trace row carry refused[].
(a) os.shell.run: a command that runs node --check / node -c over more than one path (globs expanded as the subshell would) prepends a line naming the one file node actually checked and asking for one command per file — exit 0 says nothing about the rest. (b) A final reply that claims a check ran (node --check, tests pass, verified, ran the tests, lint passes, builds cleanly) with no matching call this turn — a shell command containing the claimed check, or any verify.* call — is held once with a notice and one more step, the same shape as the invented-transcript rejection; the forced final step is exempt. A second claiming reply is delivered with details.unverifiedClaims on its result, which the trace records.
…honest /runmode status llm.runMode.fusion.cloudWorkers (default 4) bounds a fan-out whose worker leg has no slot pool; a higher maxWorkers is clamped and the result names the knob. SlotManager.observedPoolSize() reports the pool only once a /props answer sized it, and that number is threaded bootstrap -> loop deps -> step deps -> buildPrompt so the ### fusion machine facts state an external server's slots when observed, null only when nothing is known. describeRunMode takes the worker facts and /runmode status prints what will run: up to N local slots / up to N cloud workers.
The request the runtime already records per turn for the workers' briefs (pickOriginalRequest) now reaches the main loop's own prompt: RunTurnOptions.originalRequest -> step context -> BuildPromptInput. buildPrompt renders it as ### request (<= 16,000 chars) immediately before ### conversation, only when the packer has dropped the user turn that carried it — a combined follow-up record is matched by the earlier message it was taken from — and then takes its room out of the conversation cap with a second pack so the tail still fits. While the carrier is in view nothing is rendered.
…, spend in the status table
llm.runMode.fusion.workerReasoning (low|medium|high) and
workerMaxOutputTokens (both unset by default) reach every worker
completion: ResolvedRunMode -> runWorkerTasks -> RunTurnOptions
{reasoningEffort, maxOutputTokens} -> step context -> LlmStreamParams
-> CompletionRequest on both transports. The OpenAI body builder takes
the provider family: OpenRouter sends reasoning: {effort},
openai-compatible sends reasoning_effort, others omit it;
maxOutputTokens fills max_tokens under a per-step maxTokens and over
the provider ceiling, and caps llama-server's n_predict the same way.
The delegate result's head line states the fan-out's spend when the
worker model is priced (resolveModelPricingFor), never for a local leg.
… nothing is written LlamaServerClient keeps a rolling mean of timings.predicted_per_second (predicted_n / predicted_ms on an older server) as measuredTokensPerSecond(); fusion.delegate reads it per fan-out for a slot-affine leg and each local worker's time limit becomes clamp((briefChars/4 + 2000 x declaredFiles) / tokPerSec x 3, 10 min, workerTimeoutMs) — the configured value stays the ceiling and a cloud leg keeps it whole. D4: a task with declared files that has no successful os.fs.write/edit/patch by half its step budget or half its time limit is stopped and comes back needs_orchestrator with a forced summary (steps and minutes used, tool tally, last results), never as cancelled or failed.
…ng, workerMaxOutputTokens v66. All three are additive; an older file parses with them absent.
Bring the tree to the state the package has once the other packages are merged around it: shared seams (config version, prompt tail order, fusion facts) resolved the same way as in the integrated build.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Before: the batch trim kept the first approval-gated call in emit order without asking the fusion gate, so
[os.shell.run mkdir, fusion.delegate]kept the refusedmkdirand dropped the delegation (9 minutes of local generation redone); a reply could claim "node --check on all files (all passed)" after a one-file check or no check at all; every worker had 40 steps and 45 minutes regardless of model speed, and a worker that wrote nothing spent all of it; no reasoning effort or output cap was ever sent for workers (87 % of one fan-out's output was hidden reasoning); the cloud worker pool was infinite and/runmode statusprinted the default rather than what runs; an earlier task's request could fall out of the prompt during a repair.After:
wouldRefuse/refusedToolNameson the fusion gate drive the trim (refused calls dropped first,fusion.delegatepreferred in an orchestrator turn) and the notice names why each call was dropped; a shellnode --check a b cwarns that only the first file was checked, and a final reply claiming a check with no matching call gets one### noticeand one more step; the worker time limit is derived from measured tok/s and the declared output (clamped 10–45 min, cloud keeps 45), and a task with declared files and no write by half its budget returnsneeds_orchestratorwith what it found;llm.runMode.fusion.workerReasoningandworkerMaxOutputTokensreach the provider body (reasoning: {effort}on OpenRouter,reasoning_efforton openai-compatible) and the status table shows the fan-out's cloud spend;llm.runMode.fusion.cloudWorkerscaps a cloud fan-out, the machine facts use the live slot count in external mode, and/runmode statussays what will run; a### requestsection (≤ 16,000 chars) is rendered right before### conversationonce the packer has dropped the turn that carried it. Config bump for the new fields.Why
Design call D4 (early hand-back). Keep one shared predicate for the gate and the trim or they disagree and drop the wrong call. The claim check is one notice per turn, never a blocked reply, and matches only claims about this turn. Includes the reconcile commit that removes a duplicated
wouldRefuse/FusionGateContextdeclaration, a duplicatedreasoningEffortfield onCompletionRequest, and renames the OpenRouter subclass'skindoption toproviderKind.How it was verified
npm run lintcleannpx vitest run --minWorkers=1 --maxWorkers=3 src/agent src/config src/llm src/llm/provider src/llm/provider/openai src/llm/provider/openrouter src/llm/run-mode src/prompt src/runtime/bootstrap.test.ts src/runtime/llm-fallback-seam.test.ts src/runtime/llm-link-attempt.test.ts src/tools/fusion src/tools/os src/tracing/trace src/tui/agent-event-reducer.test.ts— 244 files / 3516 tests green, 3 skipped (baseline failures: none)needs_orchestratorinstead ofok(the F19 hand-back, later tuned in PR 14)Stacked on #433; merge in order.