diff --git a/config/examples/article-review-pipeline.yaml b/config/examples/article-review-pipeline.yaml
index 17158c16..6f5f9244 100644
--- a/config/examples/article-review-pipeline.yaml
+++ b/config/examples/article-review-pipeline.yaml
@@ -16,13 +16,8 @@ Orchestration:
TriggerTurnCount: 20
KeepRecentTurns: 6
- # Isolation: Shared declared explicitly on every agent below to preserve this example's
- # original behavior — the default changed to Fresh (no SharedHistory) with the agent-isolation
- # protocol overhaul. See skills/craft-orchestration/references/schema-cheatsheet.md for what
- # Isolation: Fresh + Context: buys you and when it's safe to switch an agent over.
Agents:
- Name: Writer
- Isolation: Shared
Description: Technical writer who drafts or revises an article based on the task and any editor feedback.
Instructions: |
You are a technical writer.
@@ -54,7 +49,6 @@ Orchestration:
FunctionChoice: none
- Name: Editor
- Isolation: Shared
Description: Senior editor who evaluates drafts for quality, accuracy, and completeness.
Instructions: |
You are a senior editor.
@@ -96,7 +90,6 @@ Orchestration:
FunctionChoice: none
- Name: Publisher
- Isolation: Shared
Description: Publisher who saves the approved article to disk as a Markdown file.
Instructions: |
You are a content publisher.
diff --git a/config/examples/brownfield.yaml b/config/examples/brownfield.yaml
index 6b56e1fa..fb056757 100644
--- a/config/examples/brownfield.yaml
+++ b/config/examples/brownfield.yaml
@@ -130,13 +130,8 @@ Orchestration:
KeepRecentTurns: 10
Mode: lossless
- # Isolation: Shared declared explicitly on every agent below to preserve this example's
- # original behavior — the default changed to Fresh (no SharedHistory) with the agent-isolation
- # protocol overhaul. See skills/craft-orchestration/references/schema-cheatsheet.md for what
- # Isolation: Fresh + Context: buys you and when it's safe to switch an agent over.
Agents:
- Name: Archaeologist
- Isolation: Shared
Description: Recon agent that maps the codebase before any changes are made.
Instructions: |
You are a codebase archaeologist. Your job is reconnaissance — no code changes.
@@ -223,7 +218,6 @@ Orchestration:
Git: [read]
- Name: Planner
- Isolation: Shared
Description: Scopes the task to the Archaeologist's findings and writes brief.json.
Instructions: |
You are a technical project planner working on a brownfield codebase.
@@ -276,7 +270,6 @@ Orchestration:
- Handoff
- Name: Developer
- Isolation: Shared
Description: Senior engineer who implements within the enforced change envelope.
Instructions: |
You are an expert software developer working in a brownfield codebase.
@@ -328,7 +321,6 @@ Orchestration:
- Handoff
- Name: Tester
- Isolation: Shared
Description: QA engineer who runs targeted tests and verifies acceptance criteria.
Instructions: |
You are an expert QA engineer. Verify everything independently.
@@ -383,7 +375,6 @@ Orchestration:
- Handoff
- Name: Reviewer
- Isolation: Shared
Description: Tech lead who approves only after reading code and confirming conventions.
Instructions: |
You are a senior tech lead performing a final code review on a brownfield codebase.
@@ -442,7 +433,6 @@ Orchestration:
TextOnly: true
- Name: Verifier
- Isolation: Shared
Description: Evidence auditor who checks for inconsistencies between claims and actions.
Instructions: |
You are an evidence auditor. Detect inconsistencies between what agents claim
diff --git a/config/examples/cross-system-flow-analyzer.yaml b/config/examples/cross-system-flow-analyzer.yaml
index e92d3ed5..66c87593 100644
--- a/config/examples/cross-system-flow-analyzer.yaml
+++ b/config/examples/cross-system-flow-analyzer.yaml
@@ -111,17 +111,12 @@ Orchestration:
Mode: json
Path: .fuseraft/checkpoints
- # Isolation: Shared declared explicitly on every agent below to preserve this example's
- # original behavior — the default changed to Fresh (no SharedHistory) with the agent-isolation
- # protocol overhaul. See skills/craft-orchestration/references/schema-cheatsheet.md for what
- # Isolation: Fresh + Context: buys you and when it's safe to switch an agent over.
Agents:
## Phase 1: map the territory — orient without deep-reading files
## ── The only agent with hardcoded workspace paths. Update the "Workspace layout"
## block below; all downstream agents derive their paths from .fuseraft/recon.md.
- Name: Archaeologist
- Isolation: Shared
Instructions: |
You are a codebase cartographer. Your job is to map the territory, not read everything.
@@ -197,7 +192,6 @@ Orchestration:
## Phase 2: deep-read the SQL layer
## Paths come from recon.md — no workspace-specific edits needed here.
- Name: SQLAnalyst
- Isolation: Shared
Instructions: |
You are a SQL expert. Begin by reading .fuseraft/recon.md — it lists the exact
stored procedure paths and schema objects relevant to this flow. The database
@@ -274,7 +268,6 @@ Orchestration:
## Phase 3: deep-read the API layer, cross-reference SQL
## Paths come from recon.md — no workspace-specific edits needed here.
- Name: APIAnalyst
- Isolation: Shared
Instructions: |
You are a web API expert. Begin by reading .fuseraft/recon.md and
.fuseraft/sql-findings.md. The recon.md lists exact API route paths; the API
@@ -350,7 +343,6 @@ Orchestration:
## Phase 4: extract signal from binary docs
## Paths come from recon.md — no workspace-specific edits needed here.
- Name: DocAnalyst
- Isolation: Shared
Instructions: |
You are a technical documentation analyst. Begin by reading .fuseraft/recon.md
for the list of document paths. Use document_extract_text for each one
@@ -398,7 +390,6 @@ Orchestration:
## are hit here in practice, consider decomposing into EntityNormalizer →
## RelationshipNormalizer → AsyncFlowNormalizer → CanonicalAssembler.
- Name: Normalizer
- Isolation: Shared
Instructions: |
You are a data normalization specialist. Read all four findings files:
.fuseraft/recon.md
@@ -468,7 +459,6 @@ Orchestration:
## Phase 6: connect the dots — this agent sees only text, never tool frames
- Name: Synthesizer
- Isolation: Shared
Instructions: |
You are a systems integration architect. Read .fuseraft/canonical-model.json
(the normalized, deduplicated entity model) plus all findings files for prose detail:
@@ -578,7 +568,6 @@ Orchestration:
## Phase 7: produce the artifact
- Name: DiagramBuilder
- Isolation: Shared
Instructions: |
You are a DrawIO expert. Read .fuseraft/flow-model.json (machine-readable graph)
and .fuseraft/flow-model.md (supplemental annotation only).
@@ -635,7 +624,6 @@ Orchestration:
## Phase 8: validate all outputs for consistency
- Name: OutputValidator
- Isolation: Shared
Instructions: |
You are an output validator. Read:
.fuseraft/canonical-model.json
diff --git a/config/examples/dev-team-structured.yaml b/config/examples/dev-team-structured.yaml
index b690be53..1c4fec99 100644
--- a/config/examples/dev-team-structured.yaml
+++ b/config/examples/dev-team-structured.yaml
@@ -82,13 +82,8 @@ Orchestration:
KeepRecentTurns: 10
Mode: lossless
- # Isolation: Shared declared explicitly on every agent below to preserve this example's
- # original behavior — the default changed to Fresh (no SharedHistory) with the agent-isolation
- # protocol overhaul. See skills/craft-orchestration/references/schema-cheatsheet.md for what
- # Isolation: Fresh + Context: buys you and when it's safe to switch an agent over.
Agents:
- Name: Planner
- Isolation: Shared
Description: Session planner who reads the task and codebase to produce a focused brief.
Instructions: |
You are a technical project planner.
@@ -135,7 +130,6 @@ Orchestration:
- Handoff
- Name: Developer
- Isolation: Shared
Description: Senior software engineer who implements features using tools.
Instructions: |
You are an expert software developer.
@@ -173,7 +167,6 @@ Orchestration:
- Handoff
- Name: Tester
- Isolation: Shared
Description: QA engineer who verifies changes with real tool calls.
Instructions: |
You are an expert QA engineer. Verify everything independently.
@@ -223,7 +216,6 @@ Orchestration:
- Handoff
- Name: Reviewer
- Isolation: Shared
Description: Tech lead who approves only after reading code, running a spot-check, and confirming all criteria pass.
Instructions: |
You are a senior tech lead performing a final review.
@@ -278,7 +270,6 @@ Orchestration:
TextOnly: true
- Name: Verifier
- Isolation: Shared
Description: Evidence auditor who checks for inconsistencies between claims and recorded actions.
Instructions: |
You are an evidence auditor. Detect inconsistencies between what agents claim
diff --git a/config/examples/devops-team.yaml b/config/examples/devops-team.yaml
index 64d9b726..3657416e 100644
--- a/config/examples/devops-team.yaml
+++ b/config/examples/devops-team.yaml
@@ -44,13 +44,8 @@ Orchestration:
KeepRecentTurns: 8
Mode: lossless
- # Isolation: Shared declared explicitly on every agent below to preserve this example's
- # original behavior — the default changed to Fresh (no SharedHistory) with the agent-isolation
- # protocol overhaul. See skills/craft-orchestration/references/schema-cheatsheet.md for what
- # Isolation: Fresh + Context: buys you and when it's safe to switch an agent over.
Agents:
- Name: Architect
- Isolation: Shared
Description: Senior architect who analyses requirements and writes a concrete implementation plan to disk.
Instructions: |
You are a senior software architect.
@@ -84,7 +79,6 @@ Orchestration:
- Handoff
- Name: Engineer
- Isolation: Shared
Description: Full-stack engineer who executes the plan using tools — never describes changes without making them.
Instructions: |
You are a full-stack engineer executing the Architect's plan.
@@ -128,7 +122,6 @@ Orchestration:
- Handoff
- Name: Operator
- Isolation: Shared
Description: Site reliability engineer who executes the deployment and verifies success.
Instructions: |
You are a site reliability engineer.
diff --git a/config/examples/etl-pipeline.yaml b/config/examples/etl-pipeline.yaml
index 9a9c42f4..70a4b803 100644
--- a/config/examples/etl-pipeline.yaml
+++ b/config/examples/etl-pipeline.yaml
@@ -50,13 +50,8 @@ Orchestration:
- "output/**"
- ".fuseraft/artifacts/**"
- # Isolation: Shared declared explicitly on every agent below to preserve this example's
- # original behavior — the default changed to Fresh (no SharedHistory) with the agent-isolation
- # protocol overhaul. See skills/craft-orchestration/references/schema-cheatsheet.md for what
- # Isolation: Fresh + Context: buys you and when it's safe to switch an agent over.
Agents:
- Name: Extractor
- Isolation: Shared
Description: Reads and validates the raw input data named in the task.
Instructions: |
You extract and validate raw pipeline input. You never write files.
@@ -82,7 +77,6 @@ Orchestration:
FunctionChoice: required
- Name: Transformer
- Isolation: Shared
Description: Normalizes the extracted data and writes it to the output path.
Instructions: |
You transform validated pipeline data and write the result. You run
diff --git a/config/examples/magentic-team.yaml b/config/examples/magentic-team.yaml
index 2e1bc0b7..f71e1339 100644
--- a/config/examples/magentic-team.yaml
+++ b/config/examples/magentic-team.yaml
@@ -15,14 +15,9 @@ Orchestration:
Endpoint: https://api.openai.com/v1
ApiKeyEnvVar: OPENAI_API_KEY
- # Isolation: Shared declared explicitly on every agent below because Magentic's manager/
- # ledger loop structurally depends on shared visibility of progress across all participants —
- # the config loader rejects Isolation: Fresh (the default) under Selection.Type: magentic.
- # See skills/craft-orchestration/references/schema-cheatsheet.md for details.
Agents:
- Name: Researcher
Description: Gathers information, summarizes findings, and answers factual questions.
- Isolation: Shared
Instructions: |
You are a Researcher. Your job is to find information, analyze data, and produce
well-sourced summaries. When asked to investigate a topic, be thorough but concise.
@@ -36,7 +31,6 @@ Orchestration:
- Name: Developer
Description: Writes code, implements features, runs tests, and fixes bugs.
- Isolation: Shared
Instructions: |
You are a Developer. Your job is to write clean, working code that solves the
problem at hand. When implementing features, write the code first, then test it.
diff --git a/config/examples/open-webui.yaml b/config/examples/open-webui.yaml
index 910ecc61..a9a08e35 100644
--- a/config/examples/open-webui.yaml
+++ b/config/examples/open-webui.yaml
@@ -73,13 +73,8 @@ Orchestration:
KeepRecentTurns: 6
Mode: lossless
- # Isolation: Shared declared explicitly on every agent below to preserve this example's
- # original behavior — the default changed to Fresh (no SharedHistory) with the agent-isolation
- # protocol overhaul. See skills/craft-orchestration/references/schema-cheatsheet.md for what
- # Isolation: Fresh + Context: buys you and when it's safe to switch an agent over.
Agents:
- Name: Planner
- Isolation: Shared
Description: Session planner who reads the task and codebase to produce a focused brief for the team.
Instructions: |
You are a technical project planner.
@@ -132,7 +127,6 @@ Orchestration:
- Handoff
- Name: Developer
- Isolation: Shared
Description: Senior software engineer who implements features using tools.
Instructions: |
You are an expert software developer with access to filesystem, shell, and git tools.
@@ -174,7 +168,6 @@ Orchestration:
- Handoff
- Name: Tester
- Isolation: Shared
Description: QA engineer who independently verifies changes with real tool calls.
Instructions: |
You are an expert QA engineer. DO NOT trust the Developer's account — verify everything independently.
@@ -231,7 +224,6 @@ Orchestration:
- Handoff
- Name: Reviewer
- Isolation: Shared
Description: Tech lead who approves only after reading the code, running a spot-check, and confirming all acceptance criteria are verified passing.
Instructions: |
You are a senior tech lead performing a final review.
@@ -292,7 +284,6 @@ Orchestration:
TextOnly: true
- Name: Verifier
- Isolation: Shared
Description: Evidence auditor who checks for inconsistencies between claims and recorded actions.
Instructions: |
You are an evidence auditor. Detect inconsistencies between what agents claim
diff --git a/config/examples/orchestration.yaml b/config/examples/orchestration.yaml
index 96b6b5fe..15a96fd6 100644
--- a/config/examples/orchestration.yaml
+++ b/config/examples/orchestration.yaml
@@ -85,14 +85,9 @@ Orchestration:
KeepRecentTurns: 10
Mode: lossless
- # Isolation: Shared declared explicitly on every agent below to preserve this example's
- # original behavior — the default changed to Fresh (no SharedHistory) with the agent-isolation
- # protocol overhaul. See skills/craft-orchestration/references/schema-cheatsheet.md for what
- # Isolation: Fresh + Context: buys you and when it's safe to switch an agent over.
Agents:
- Name: Planner
Description: Session planner who reads the task and codebase to produce a focused brief.
- Isolation: Shared
Instructions: |
You are a technical project planner.
@@ -130,7 +125,6 @@ Orchestration:
- Name: Developer
Description: Senior software engineer who implements features using tools.
- Isolation: Shared
Instructions: |
You are an expert software developer.
@@ -158,7 +152,6 @@ Orchestration:
- Name: Tester
Description: QA engineer who runs tests and writes a structured report.
- Isolation: Shared
Instructions: |
You are a quality assurance engineer.
@@ -197,7 +190,6 @@ Orchestration:
- Name: Reviewer
Description: Tech lead who approves completed work or requests revisions.
- Isolation: Shared
Instructions: |
You are a senior tech lead performing a final code review.
@@ -227,7 +219,6 @@ Orchestration:
- Name: Verifier
Description: Evidence auditor who checks for inconsistencies between claims and recorded actions.
- Isolation: Fork
Instructions: |
You are an evidence auditor. Detect inconsistencies between what agents claim
and what is recorded in the change log.
diff --git a/config/examples/playwright-mcp.yaml b/config/examples/playwright-mcp.yaml
index a2ebb885..e4098121 100644
--- a/config/examples/playwright-mcp.yaml
+++ b/config/examples/playwright-mcp.yaml
@@ -23,13 +23,8 @@ Orchestration:
- "--browser"
- "chromium" # must match the browser installed via playwright-core's cli.js
- # Isolation: Shared declared explicitly on every agent below to preserve this example's
- # original behavior — the default changed to Fresh (no SharedHistory) with the agent-isolation
- # protocol overhaul. See skills/craft-orchestration/references/schema-cheatsheet.md for what
- # Isolation: Fresh + Context: buys you and when it's safe to switch an agent over.
Agents:
- Name: BrowserAgent
- Isolation: Shared
Description: Automates browser interactions using Playwright tools.
Instructions: |
You are a browser automation agent with access to Playwright tools.
diff --git a/config/examples/research-team.yaml b/config/examples/research-team.yaml
index 29393d2e..ea50f2af 100644
--- a/config/examples/research-team.yaml
+++ b/config/examples/research-team.yaml
@@ -38,13 +38,8 @@ Orchestration:
KeepRecentTurns: 6
Mode: lossless
- # Isolation: Shared declared explicitly on every agent below to preserve this example's
- # original behavior — the default changed to Fresh (no SharedHistory) with the agent-isolation
- # protocol overhaul. See skills/craft-orchestration/references/schema-cheatsheet.md for what
- # Isolation: Fresh + Context: buys you and when it's safe to switch an agent over.
Agents:
- Name: Researcher
- Isolation: Shared
Description: Data researcher who fetches real information using HTTP and filesystem tools.
Instructions: |
You are a research specialist.
@@ -79,7 +74,6 @@ Orchestration:
- Handoff
- Name: Writer
- Isolation: Shared
Description: Technical writer who synthesises research into a structured report.
Instructions: |
You are a technical writer.
diff --git a/config/orchestration.yaml b/config/orchestration.yaml
index 916bfcbb..804ed267 100644
--- a/config/orchestration.yaml
+++ b/config/orchestration.yaml
@@ -75,18 +75,9 @@ Orchestration:
KeepRecentTurns: 10
Mode: lossless
- # Isolation is explicit below on every agent because this config predates the Isolation:
- # field (default changed to Fresh — no SharedHistory unless an agent opts in). Every agent
- # here declares `Isolation: Shared` to preserve its exact original behavior: several agents'
- # instructions depend on prior conversation being visible (e.g. Planner step 3, "IF THIS IS
- # A RETRY: Reviewer feedback is present in context"). Migrating individual agents to
- # `Isolation: Fresh` + an explicit `Context:` block is a real option — most of them already
- # read their inputs from .fuseraft/artifacts/*.json by hand — but changes turn-by-turn
- # behavior and needs its own validation pass; not done wholesale here.
Agents:
- Name: Planner
Description: Session planner who reads the task and codebase to produce a focused brief for the team.
- Isolation: Shared
Instructions: |
You are a technical project planner.
@@ -146,7 +137,6 @@ Orchestration:
- Name: PlannerCritic
Description: Adversarially reviews the brief for completeness before the Developer starts.
- Isolation: Shared
Instructions: |
You are an adversarial brief reviewer. Find reasons the brief will FAIL — not reasons
it will succeed. A brief that passes goes directly to the Developer; one that fails
@@ -198,7 +188,6 @@ Orchestration:
- Name: Developer
Description: Senior software engineer who implements features using tools.
- Isolation: Shared
Instructions: |
You are an expert software developer with access to filesystem, shell, and git tools.
@@ -238,7 +227,6 @@ Orchestration:
- Name: Tester
Description: QA engineer who independently verifies changes with real tool calls and blocks promotion on any failure.
- Isolation: Shared
Instructions: |
You are an expert QA engineer. DO NOT trust the Developer's account — verify everything independently.
@@ -300,7 +288,6 @@ Orchestration:
- Name: Reviewer
Description: Tech lead who approves only after reading the code, running a spot-check, and confirming all acceptance criteria are verified passing.
- Isolation: Shared
Instructions: |
You are a senior tech lead performing a final review.
@@ -359,12 +346,6 @@ Orchestration:
- Name: Verifier
Description: Evidence auditor who checks for inconsistencies between claims and recorded actions.
- # Fork, not Shared: the Verifier's whole job is cross-checking claims made "in recent
- # conversation messages" against the change log — it needs the full transcript. Fork
- # additionally layers in a synthesized directive on the turns it's dispatched via a
- # handoff() call; EveryNTurns-triggered runs behave the same as Shared (no directive
- # available), so this is strictly no worse than Shared and correct for its stated role.
- Isolation: Fork
Instructions: |
You are an evidence auditor. Detect inconsistencies between what agents claim
and what is recorded in the change log.
diff --git a/config/security/red-team.yaml b/config/security/red-team.yaml
index cb2e4af3..2c1e1514 100644
--- a/config/security/red-team.yaml
+++ b/config/security/red-team.yaml
@@ -81,14 +81,9 @@ Orchestration:
Events:
Path: .fuseraft/red-team/events.jsonl
- # Isolation: Shared declared explicitly on every agent below to preserve this example's
- # original behavior — the default changed to Fresh (no SharedHistory) with the agent-isolation
- # protocol overhaul. See skills/craft-orchestration/references/schema-cheatsheet.md for what
- # Isolation: Fresh + Context: buys you and when it's safe to switch an agent over.
Agents:
## Phase 1: map the attack surface
- Name: Recon
- Isolation: Shared
Description: Maps fuseraft-cli's attack surface before adversarial probing begins.
TrustScore: 0.9
Instructions: |
@@ -154,7 +149,6 @@ Orchestration:
## Phase 2: static code analysis
- Name: StaticAttacker
- Isolation: Shared
Description: Red Team Alpha — reads source code and identifies implementation vulnerabilities.
TrustScore: 0.70
Instructions: |
@@ -265,7 +259,6 @@ Orchestration:
## Fix 1: ProbeWriter cannot execute anything. It only creates YAML files.
## Even if prompt-injected, it has no way to run a command.
- Name: ProbeWriter
- Isolation: Shared
Description: Red Team Bravo — writes malicious config files for the Prober to test. No shell access.
TrustScore: 0.70
Instructions: |
@@ -429,7 +422,6 @@ Orchestration:
## a Docker container with --network none. The validate step uses the Probe plugin
## (structured, no general-purpose scripting) rather than Shell.
- Name: Prober
- Isolation: Shared
Description: Red Team Bravo — runs fuseraft validate on each probe and analyses YAML parsing in Docker.
TrustScore: 0.75
Instructions: |
@@ -524,7 +516,6 @@ Orchestration:
## Phase 4: triage and report
- Name: Triage
- Isolation: Shared
Description: Deduplicates findings from both agents, scores by severity, and writes the security report.
TrustScore: 0.9
Instructions: |
diff --git a/docs/models.md b/docs/models.md
index c482cda2..2827beda 100644
--- a/docs/models.md
+++ b/docs/models.md
@@ -108,19 +108,12 @@ For any model not matching the table, specify `Provider`, `Endpoint`, and `ApiKe
{
"modelId": "anthropic.claude-sonnet-4-6-20250929-v1:0",
"endpoint": "http://localhost:3000/api/openai/v1",
- "apiKeyEnvVar": "OPENWEBUI_API_KEY",
- "replContextBudget": 400000
+ "apiKeyEnvVar": "OPENWEBUI_API_KEY"
}
```
Set this file via `fuseraft repl` or `fuseraft models` (the setup wizard runs automatically on first use) or edit it directly. Run `fuseraft models` to see all models available from the configured provider, or use `/models` inside a REPL session for the same list.
-### `replContextBudget` — REPL working-context override
-
-The REPL trims conversation history against a working-context-token budget (`ctx.ContextTokenBudget`, shown in `/context`), separate from `MaxContextTokens` above. By default this budget comes from a per-model-family heuristic (150K for 1M/128K+-class frontier models like `claude-*`/`gemini-*`/`grok-*`/`gpt-5*`, 100K for ~128K-class models like `gpt-4*`/`mistral-*`/`deepseek-*`, 80K otherwise) — deliberately conservative, since the REPL's char-based token estimate doesn't account for tool-schema tokens.
-
-Set `replContextBudget` in `~/.fuseraft/config` (a positive integer, in tokens) to override that heuristic for every model used in the REPL session, regardless of family. Leave it unset (or `0`) to keep the built-in heuristic. This is REPL-only and does not affect `MaxContextTokens` above (a separate per-agent hard ceiling enforced before each API call in non-REPL agent/orchestration contexts), nor the unrelated `ContextBudget` YAML block used in `orchestration.yaml` (warn/cutover/tool-result trimming for multi-agent orchestration runs) — the similarly-named `replContextBudget` field intentionally carries the `Repl` prefix to keep the two apart.
-
### OS keychain fallback
If an agent model has neither `ApiKey` nor `ApiKeyEnvVar` set after global defaults are applied, fuseraft retrieves the key stored in the OS keychain (set via `fuseraft key set` or the REPL wizard) and injects it as a literal `ApiKey`. This means the full auth resolution order for any agent model is:
diff --git a/skills/craft-orchestration/references/schema-cheatsheet.md b/skills/craft-orchestration/references/schema-cheatsheet.md
index 9c6d0824..5ccea9aa 100644
--- a/skills/craft-orchestration/references/schema-cheatsheet.md
+++ b/skills/craft-orchestration/references/schema-cheatsheet.md
@@ -123,10 +123,9 @@ Orchestration:
- FileSystem
- Shell
- Handoff
- Isolation: Fresh # Fresh (default) | Shared | Fork — see "Isolation" below
ContextWindow:
- TextOnly: true # strip tool-call results from context window; ignored when Isolation: Fresh
- Context: # this agent's own inputs — always used under Isolation: Fresh
+ TextOnly: true # strip tool-call results from context window
+ Context: # replaces ContextWindow when set; assembles from artifacts
- Source: session_context # handoff summary from session_context_write
- Source: changes_recent:5 # last 5 change-log entries
- Source: brief_field:test_targets # field from brief.json
@@ -143,49 +142,6 @@ Orchestration:
---
-## Isolation
-
-Controls whether an agent sees the shared session transcript other agents have been writing
-to, or only a synthesized handoff directive plus its own declared `Context:` sources — the
-same fresh-by-default, fork-by-explicit-choice split Claude Code uses for its own sub-agents.
-
-| Mode | What the agent receives | Use for |
-|---|---|---|
-| `Fresh` (default) | The synthesized `AgentDirective` (see below) + its own `Context:` sources only. Never `SharedHistory` — even with an empty/absent `Context:` block. | Most agents. No inherited reasoning, dead ends, or another agent's tool-call noise. |
-| `Shared` | `Context:` block if declared, else the windowed shared transcript (`ContextWindow`). Pre-overhaul behavior. | Conversational round-robin/keyword group chats; anything whose prompts assume prior turns are visible. |
-| `Fork` | `Shared` behavior **plus** the synthesized directive layered on top. | Meta-agents that genuinely need the full transcript AND a clear statement of what to do with it — a Verifier auditing the session, a RecoveryAgent diagnosing a failure. |
-
-**`Selection.Type: magentic` requires every agent to be `Shared` or `Fork`** — the manager's
-ledger loop depends on shared visibility of progress across all participants; config load fails
-with `Isolation: Fresh` under Magentic.
-
-A `Fresh` agent with no `Context:` sources at all still runs — it just receives nothing but the
-directive each turn. Fine for a terminal/leaf agent; a load-time warning flags this for anything
-that looks like it needs durable state.
-
-### The directive: how a `Fresh` agent learns what to do
-
-Extend the `handoff()` call with optional structured fields instead of leaving the receiving
-agent to infer intent from a bare routing keyword:
-
-```
-handoff(
- route_keyword: "HANDOFF TO DEVELOPER",
- goal: "Add pagination to GET /users.",
- background: "Explored the handler in src/api/users.py — no existing page param. " +
- "Auth middleware already extracts the caller; don't touch it.",
- constraints: "Do not change the existing response shape for callers that omit ?page."
-)
-```
-
-`goal`/`background`/`constraints` are optional — a bare `handoff(route_keyword: ...)` still
-works exactly as before. When present, they become the receiving agent's task message under
-`Isolation: Fresh` (and are layered onto the transcript under `Fork`). Write them the way you'd
-brief a colleague who wasn't in the room: state what's already been learned or ruled out, don't
-assume they can see your reasoning.
-
----
-
## All plugin names
| Plugin | What it provides |
@@ -269,10 +225,9 @@ Selection:
Signal: "HANDOFF TO TESTER"
Contract: ImplementationComplete
HandoffContext: # inject targeted artifacts when transition fires
- - Source: session_context # NOTE: only takes effect for Shared/Fork agents —
- - Source: changes_recent # a Fresh agent never reads SharedHistory, so this
- - Source: brief_field:test_targets # never reaches it. Put the same sources in the
- # target agent's own Context: block instead.
+ - Source: session_context
+ - Source: changes_recent
+ - Source: brief_field:test_targets
- To: Planning
Signal: "REPLAN REQUIRED"
@@ -480,7 +435,6 @@ Termination:
| `RequireReviewJudgement` | Reviewer → Done | Reviewer emitted `{"review":[...]}` with all PASS verdicts + shell run |
| `RequireRelatedTestsPass` | Developer → Tester | Targeted tests for changed files pass (needs `TestSelector`) |
| `RequireAcceptanceCriteriaPassedValidator` | Developer → Reviewer | Machine-testable criteria verified by real shell output |
-| `RequireSessionContextWrite` | Any route/edge/transition whose source agent is `Isolation: Fresh` | At least one `session_context_write` call this turn — not auto-attached; add it explicitly so a `Fresh` agent that forgets to write a summary fails loudly instead of silently handing the next agent nothing |
---
diff --git a/src/Cli/Commands/Repl/ModelContextWindow.cs b/src/Cli/Commands/Repl/ModelContextWindow.cs
index 0ecce616..2c95dff7 100644
--- a/src/Cli/Commands/Repl/ModelContextWindow.cs
+++ b/src/Cli/Commands/Repl/ModelContextWindow.cs
@@ -35,15 +35,8 @@ internal static class ModelContextWindow
/// so both bare model IDs (e.g. claude-sonnet-4-6) and provider-prefixed deployment
/// IDs (e.g. Bedrock's anthropic.claude-sonnet-4-6-20250929-v1:0) resolve correctly.
///
- /// The model ID whose family determines the heuristic budget.
- ///
- /// User-configured override ().
- /// When positive, takes precedence over the per-family heuristic below.
- ///
- internal static int GetBudget(string? modelId, int? overrideBudget = null)
+ internal static int GetBudget(string? modelId)
{
- if (overrideBudget is > 0) return overrideBudget.Value;
-
if (string.IsNullOrWhiteSpace(modelId)) return DefaultBudget;
if (LargeFamilyMarkers.Any(m => modelId.Contains(m, StringComparison.OrdinalIgnoreCase)))
diff --git a/src/Cli/Commands/Repl/ReplSessionContext.cs b/src/Cli/Commands/Repl/ReplSessionContext.cs
index 2aee84bc..682ed187 100644
--- a/src/Cli/Commands/Repl/ReplSessionContext.cs
+++ b/src/Cli/Commands/Repl/ReplSessionContext.cs
@@ -49,17 +49,15 @@ public string ModelId
set
{
_modelId = value;
- ContextTokenBudget = ModelContextWindow.GetBudget(value, UserCfg?.ReplContextBudget);
+ ContextTokenBudget = ModelContextWindow.GetBudget(value);
}
}
// Working token budget for history trimming (TrimHistory) and the /context, /compact,
- // and context-warning displays — derived from ModelId (and UserCfg.ReplContextBudget, if set)
- // so a large-context model isn't held to the same ceiling as a small-context local model.
- // Recomputed automatically whenever ModelId is (re)assigned, including on /provider setup,
- // /model switch, and session resume. Relies on UserCfg already being current at that point
- // — the constructor below sets UserCfg before ModelId for this reason, and every later
- // reassignment site that changes both (e.g. /provider setup) must preserve that order.
+ // and context-warning displays — derived from ModelId so a large-context model isn't
+ // held to the same ceiling as a small-context local model. Recomputed automatically
+ // whenever ModelId is (re)assigned, including on /provider setup, /model switch, and
+ // session resume.
public int ContextTokenBudget { get; private set; } = ModelContextWindow.DefaultBudget;
public ModelConfig ModelConfig { get; set; }
@@ -166,9 +164,9 @@ public ReplSessionContext(
Cwd = cwd;
SessionId = sessionId;
StartedAt = startedAt;
- UserCfg = userCfg;
ModelId = modelId;
ModelConfig = modelConfig;
+ UserCfg = userCfg;
Client = client;
Factory = factory;
KeyStore = keyStore;
diff --git a/src/Cli/Commands/ValidateConfigCommand.cs b/src/Cli/Commands/ValidateConfigCommand.cs
index 73a1ec95..d260d94c 100644
--- a/src/Cli/Commands/ValidateConfigCommand.cs
+++ b/src/Cli/Commands/ValidateConfigCommand.cs
@@ -120,9 +120,7 @@ protected override async Task ExecuteAsync(CommandContext context, Validate
issues.Add(("error", $"SystemPromptPath file not found: {promptPath}"));
}
- var magenticFreshViolations = OrchestratorConfigLoader.FindMagenticFreshIsolationViolations(config)
- .ToHashSet(StringComparer.OrdinalIgnoreCase);
- ValidateAgents(config, settings, issues, magenticFreshViolations);
+ ValidateAgents(config, settings, issues);
// Selection strategy
var selType = config.Selection.Type.ToLowerInvariant();
@@ -214,8 +212,7 @@ protected override async Task ExecuteAsync(CommandContext context, Validate
private void ValidateAgents(
OrchestrationConfig config,
ValidateConfigSettings settings,
- List<(string Level, string Message)> issues,
- HashSet magenticFreshViolations)
+ List<(string Level, string Message)> issues)
{
if (config.Agents.Count == 0)
{
@@ -286,16 +283,6 @@ private void ValidateAgents(
if (effort is not null && effort.Trim().Contains(' '))
issues.Add(("error", $"Agent '{agent.Name}': Model.ReasoningEffort '{effort}' looks malformed — expected a single token (e.g. none, low, medium, high, xhigh, max)."));
- // Magentic's manager/ledger loop depends on every participant sharing the
- // transcript — Isolation: Fresh (the default) would silently starve it. Uses
- // the same OrchestratorConfigLoader.FindMagenticFreshIsolationViolations the
- // real config loader hard-fails on, so this lint can't drift out of sync with it.
- if (magenticFreshViolations.Contains(agent.Name))
- issues.Add(("error", $"Agent '{agent.Name}': Isolation: Fresh is incompatible with Selection.Type 'magentic' — set 'Isolation: Shared' (or 'Fork')."));
- else if (agent.Isolation == fuseraft.Core.Models.Agents.AgentIsolation.Fresh
- && agent.Context is not { Count: > 0 })
- issues.Add(("warning", $"Agent '{agent.Name}': Isolation: Fresh (the default) with no Context: sources declared — it will receive only the synthesized handoff directive each turn. Fine for a terminal/leaf agent; otherwise add a Context: block or set 'Isolation: Shared'."));
-
if (settings.Strict)
{
var registered = pluginRegistry.RegisteredPlugins
diff --git a/src/Cli/OrchestratorBuilder.cs b/src/Cli/OrchestratorBuilder.cs
index bc5b83ee..b6773d3c 100644
--- a/src/Cli/OrchestratorBuilder.cs
+++ b/src/Cli/OrchestratorBuilder.cs
@@ -117,7 +117,6 @@ public static class OrchestratorBuilder
{
PropertyNameCaseInsensitive = true,
DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingNull,
- Converters = { new JsonStringEnumConverter() },
};
///
diff --git a/src/Cli/OrchestratorConfigLoader.cs b/src/Cli/OrchestratorConfigLoader.cs
index d7780c38..1b7f8a6c 100644
--- a/src/Cli/OrchestratorConfigLoader.cs
+++ b/src/Cli/OrchestratorConfigLoader.cs
@@ -5,7 +5,6 @@
using fuseraft.Core.Models;
using fuseraft.Infrastructure.KeyStore;
using fuseraft.Infrastructure.Plugins;
-using fuseraft.Orchestration;
namespace fuseraft.Cli;
@@ -51,8 +50,6 @@ public static class OrchestratorConfigLoader
if (config.Agents.Count == 0)
throw new InvalidOperationException("Config must define at least one agent.");
- ValidateIsolationConstraints(config, loggerFactory);
-
// Expand ${ENV_VAR} tokens in security and API profile config before use.
config = ExpandEnvVars(config);
@@ -460,53 +457,4 @@ private static void ValidateSchemaVersion(OrchestrationConfig config, ILoggerFac
else
logger.LogDebug("Config schema_version '{SchemaVersion}' is valid.", config.SchemaVersion);
}
-
- // Magentic's manager/ledger loop structurally depends on every participant seeing the same
- // shared transcript to coordinate — Isolation.Fresh (which never reads SharedHistory) would
- // silently starve the manager of the progress signal it needs. Reject rather than degrade
- // quietly; the fix (drop Isolation: Fresh or switch orchestrator type) is a one-line config
- // change, not a runtime workaround.
- //
- // Separately, warn (do not fail) when a Fresh agent — the default — declares no Context:
- // sources at all: such an agent receives only the synthesized handoff directive each turn,
- // which is fine for a terminal/leaf agent but likely a misconfiguration for one that needs
- // durable state (brief.json, prior changes, etc.) across turns.
- // Shared with ValidateConfigCommand's lint pass so the "magentic requires Shared/Fork"
- // rule can't drift out of sync between the lint-only check and this hard-throw one.
- internal static IReadOnlyList FindMagenticFreshIsolationViolations(OrchestrationConfig config)
- {
- if (!string.Equals(config.Selection.Type, OrchestratorTypes.Magentic, StringComparison.OrdinalIgnoreCase))
- return [];
-
- return config.Agents
- .Where(a => a.Isolation == AgentIsolation.Fresh)
- .Select(a => a.Name)
- .ToList();
- }
-
- internal static void ValidateIsolationConstraints(OrchestrationConfig config, ILoggerFactory loggerFactory)
- {
- var logger = loggerFactory.CreateLogger(nameof(OrchestratorBuilder));
-
- var freshAgents = FindMagenticFreshIsolationViolations(config);
- if (freshAgents.Count > 0)
- throw new InvalidOperationException(
- $"Selection.Type 'magentic' requires every agent to use Isolation: Shared or " +
- $"Isolation: Fork — the manager's ledger loop depends on shared visibility of " +
- $"progress across all participants. Agent(s) declaring Isolation: Fresh (the " +
- $"default): {string.Join(", ", freshAgents)}. Set 'Isolation: Shared' explicitly " +
- $"on these agents, or on the whole roster if none should isolate.");
-
- foreach (var agent in config.Agents)
- {
- if (agent.Isolation == AgentIsolation.Fresh && agent.Context is not { Count: > 0 })
- logger.LogWarning(
- "Agent '{Agent}' uses Isolation: Fresh (the default) with no Context: sources " +
- "declared — it will receive only the synthesized handoff directive each turn, " +
- "nothing else. This is fine for a terminal/leaf agent; otherwise declare a " +
- "Context: block (session_context, brief_field:*, changes_recent:N, own_history:N, " +
- "etc.) or set 'Isolation: Shared' if this agent needs the group transcript.",
- agent.Name);
- }
- }
}
diff --git a/src/Core/Models/Agents/AgentConfig.cs b/src/Core/Models/Agents/AgentConfig.cs
index 43dbd310..39025d00 100644
--- a/src/Core/Models/Agents/AgentConfig.cs
+++ b/src/Core/Models/Agents/AgentConfig.cs
@@ -100,17 +100,6 @@ public record AgentConfig
///
public List? Context { get; init; }
- ///
- /// Controls whether this agent sees the shared session transcript. Defaults to
- /// : the agent never reads SharedHistory — its
- /// context is built from the synthesized handoff plus whatever
- /// sources it declares. Set for
- /// orchestration styles that need shared visibility to coordinate (e.g. Magentic's
- /// manager/ledger loop), or for meta-agents (Verifier,
- /// RecoveryAgent) that need the full transcript plus an explicit directive.
- ///
- public AgentIsolation Isolation { get; init; } = AgentIsolation.Fresh;
-
///
/// When true, suppresses the automatic execution_state prepend that
/// OrchestratorBuilder injects for all state-machine agents. Set this when an
diff --git a/src/Core/Models/Agents/AgentDirective.cs b/src/Core/Models/Agents/AgentDirective.cs
deleted file mode 100644
index 9f8e85ee..00000000
--- a/src/Core/Models/Agents/AgentDirective.cs
+++ /dev/null
@@ -1,46 +0,0 @@
-using System.Text;
-
-namespace fuseraft.Core.Models.Agents;
-
-///
-/// Synthesized handoff payload passed to the next agent at a routing transition, replacing
-/// the historical pattern of injecting a bare [fuseraft: A → B] marker (or a heuristic
-/// excerpt of A's raw response) into the shared transcript. Populated from the optional
-/// goal/background/constraints arguments on handoff()
-/// ().
-///
-public sealed record AgentDirective
-{
- /// What the receiving agent must accomplish this turn.
- public required string Goal { get; init; }
-
- /// What the handing-off agent already learned, tried, or ruled out.
- public string? Background { get; init; }
-
- /// Explicit constraints the receiving agent must respect.
- public IReadOnlyList Constraints { get; init; } = [];
-
- /// Renders this directive as a single user-facing message body.
- public string Format()
- {
- var sb = new StringBuilder();
- sb.AppendLine(Goal);
-
- if (!string.IsNullOrWhiteSpace(Background))
- {
- sb.AppendLine();
- sb.AppendLine("Background:");
- sb.AppendLine(Background);
- }
-
- if (Constraints.Count > 0)
- {
- sb.AppendLine();
- sb.AppendLine("Constraints:");
- foreach (var c in Constraints)
- sb.AppendLine($"- {c}");
- }
-
- return sb.ToString().TrimEnd();
- }
-}
diff --git a/src/Core/Models/Agents/AgentExecutionRequest.cs b/src/Core/Models/Agents/AgentExecutionRequest.cs
index 75a1e4c5..f73bb701 100644
--- a/src/Core/Models/Agents/AgentExecutionRequest.cs
+++ b/src/Core/Models/Agents/AgentExecutionRequest.cs
@@ -17,16 +17,6 @@ public sealed record AgentExecutionRequest
/// The shared conversation history accumulated so far.
public required IReadOnlyList SharedHistory { get; init; }
- ///
- /// Synthesized handoff payload for this turn, built from the routing agent's
- /// handoff(goal, background, constraints) call. Used verbatim as the agent's task
- /// message when is
- /// ; layered on top of shared history when
- /// . Null when the routing agent's handoff call omitted
- /// the optional directive fields, or on the first turn of a session.
- ///
- public AgentDirective? Directive { get; init; }
-
/// Per-agent configuration (context window, knowledge weight, context sources, etc.).
public AgentConfig? AgentConfig { get; init; }
diff --git a/src/Core/Models/Agents/AgentIsolation.cs b/src/Core/Models/Agents/AgentIsolation.cs
deleted file mode 100644
index 651bf4bb..00000000
--- a/src/Core/Models/Agents/AgentIsolation.cs
+++ /dev/null
@@ -1,34 +0,0 @@
-namespace fuseraft.Core.Models.Agents;
-
-///
-/// Controls what context an agent receives at each invocation — specifically, whether it sees
-/// the shared session transcript other agents have been writing to, or only a synthesized
-/// directive plus its own declared sources.
-///
-public enum AgentIsolation
-{
- ///
- /// The agent never sees SharedHistory. Its context is built entirely from the
- /// incoming (goal/background/constraints synthesized at
- /// handoff time) plus its own declared sources, if any.
- /// This is the default: agents do not inherit another agent's reasoning, dead ends, or
- /// tool-call noise unless a Context: source explicitly names it.
- ///
- Fresh = 0,
-
- ///
- /// Legacy/pre-overhaul behavior: if declared, otherwise
- /// the windowed shared transcript (SharedHistoryFallback). Required for orchestration
- /// styles that depend on shared visibility to coordinate — e.g. MagenticOrchestrator's
- /// manager/ledger loop, or simple conversational round-robin/keyword group chats.
- ///
- Shared = 1,
-
- ///
- /// behavior plus the synthesized layered
- /// on top. For meta-agents that genuinely need the full transcript AND a clear statement of
- /// what to do with it — e.g. a Verifier auditing the session, or a RecoveryAgent diagnosing
- /// a failure.
- ///
- Fork = 2,
-}
diff --git a/src/Core/Models/Config/UserConfig.cs b/src/Core/Models/Config/UserConfig.cs
index 76ff4f9d..663f590f 100644
--- a/src/Core/Models/Config/UserConfig.cs
+++ b/src/Core/Models/Config/UserConfig.cs
@@ -19,18 +19,6 @@ public sealed class UserConfig
[JsonPropertyName("skillCuration")]
public SkillCurationConfig? SkillCuration { get; set; }
- ///
- /// Overrides the REPL's heuristic working-context-token budget ()
- /// used for history trimming and the /context, /compact, and context-warning displays.
- /// REPL-only — unrelated to the orchestration-level ContextBudgetConfig
- /// (warn/cutover/tool-result trimming for agent orchestration runs); the similar name is
- /// coincidental, hence the Repl prefix here to keep the two unambiguous.
- /// Applies to every model used in the REPL session, regardless of model family. Null or
- /// <= 0 falls back to the built-in per-family heuristic.
- ///
- [JsonPropertyName("replContextBudget")]
- public int? ReplContextBudget { get; set; }
-
// Never written to disk — populated at runtime from the OS keychain.
[JsonIgnore]
public string ApiKey { get; set; } = string.Empty;
diff --git a/src/Infrastructure/Plugins/HandoffPlugin.cs b/src/Infrastructure/Plugins/HandoffPlugin.cs
index e8b90607..e1052c83 100644
--- a/src/Infrastructure/Plugins/HandoffPlugin.cs
+++ b/src/Infrastructure/Plugins/HandoffPlugin.cs
@@ -23,16 +23,6 @@ namespace fuseraft.Infrastructure.Plugins;
/// The tool itself is a no-op: it returns verbatim so that
/// the legacy tool-result scanning paths also detect it as a fallback.
///
-///
-///
-/// The optional //
-/// arguments let the handing-off agent synthesize a self-contained directive for the receiving
-/// agent instead of relying on it to infer intent from the shared transcript. Orchestrators read
-/// these directly off the FunctionCallContent and build an
-/// for the next turn. When omitted, the
-/// receiving agent falls back to whatever its
-/// mode otherwise provides.
-///
///
public sealed class HandoffPlugin
{
@@ -45,20 +35,8 @@ public sealed class HandoffPlugin
/// The argument name the model must supply (route_keyword).
public const string ArgumentName = "route_keyword";
- /// The optional structured-directive argument names, for orchestrators reading raw FunctionCallContent.
- public const string GoalArgumentName = "goal";
- public const string BackgroundArgumentName = "background";
- public const string ConstraintsArgumentName = "constraints";
-
[Description("Signal completion and hand off to the next workflow step. Must be the last tool call.")]
public string Handoff(
- [Description("Exact routing keyword for the intended handoff.")]
- string route_keyword,
- [Description("What the receiving agent must accomplish this turn. Recommended: always set this — it becomes the receiving agent's task when it runs in isolated (Fresh) mode and cannot see this conversation.")]
- string? goal = null,
- [Description("What you already learned, tried, or ruled out that the receiving agent needs to know. Do not assume it can see your reasoning.")]
- string? background = null,
- [Description("Explicit constraints the receiving agent must respect, one per line.")]
- string? constraints = null)
+ [Description("Exact routing keyword for the intended handoff.")] string route_keyword)
=> route_keyword;
}
diff --git a/src/Infrastructure/Plugins/SandboxEnforcementFilter.cs b/src/Infrastructure/Plugins/SandboxEnforcementFilter.cs
index aa605650..2106b272 100644
--- a/src/Infrastructure/Plugins/SandboxEnforcementFilter.cs
+++ b/src/Infrastructure/Plugins/SandboxEnforcementFilter.cs
@@ -59,7 +59,6 @@ public sealed class SandboxEnforcementFilter
private readonly Matcher? _fsDenyMatcher;
private readonly Matcher? _fsReadMatcher;
private readonly Matcher? _fsWriteMatcher;
- private readonly IReadOnlyList _fsWritePatterns = [];
// Prefixes of OS directories that contain executables and shared libraries.
private static readonly string[] SystemPrefixes = OperatingSystem.IsWindows()
@@ -214,7 +213,6 @@ public SandboxEnforcementFilter(
{
_fsWriteMatcher = new Matcher(StringComparison.OrdinalIgnoreCase);
foreach (var p in write) _fsWriteMatcher.AddInclude(p);
- _fsWritePatterns = write;
}
}
@@ -332,15 +330,8 @@ public AIAgent WrapAgent(AIAgent agent) =>
bool applyWriteGlob = isWriteOp || (_fsWriteMatcher is not null && isMixedOp && isDestArg);
if (applyWriteGlob)
{
- // create_directory targets a directory, never a file, so it will never
- // literally match a file-shaped glob like "workspace/**" — only descendants
- // of "workspace" do. Allow it here when the requested directory is an
- // ancestor of (or equal to) an allowed write path, since creating it is a
- // prerequisite for writes the glob already permits.
- bool allowAncestor = string.Equals(functionName, "create_directory", StringComparison.OrdinalIgnoreCase);
var writeDenial = CheckGlob(raw, _fsWriteMatcher!, matchMeansDeny: false,
- "Path is outside the configured FileSystem write permissions.",
- allowAncestorOfWriteScope: allowAncestor);
+ "Path is outside the configured FileSystem write permissions.");
if (writeDenial is not null) return writeDenial;
}
@@ -536,10 +527,7 @@ private static bool TryGetArgString(object? val, out string str)
// Evaluates a glob matcher against a resolved relative path.
// When matchMeansDeny=true (deny list): returns a denial when the path matches.
// When matchMeansDeny=false (allow list): returns a denial when the path does NOT match.
- // allowAncestorOfWriteScope additionally passes a path that doesn't match the glob itself
- // but is an ancestor directory of an allowed write pattern (see IsAncestorOfWriteScope).
- private string? CheckGlob(string rawPath, Matcher matcher, bool matchMeansDeny, string reason,
- bool allowAncestorOfWriteScope = false)
+ private string? CheckGlob(string rawPath, Matcher matcher, bool matchMeansDeny, string reason)
{
string resolved;
try
@@ -554,51 +542,11 @@ private static bool TryGetArgString(object? val, out string str)
var relative = Path.GetRelativePath(_sandboxRoot, resolved).Replace('\\', '/');
bool matches = matcher.Match(relative).HasMatches;
- if (!matches && allowAncestorOfWriteScope && IsAncestorOfWriteScope(relative, _fsWritePatterns))
- matches = true;
-
return (matchMeansDeny ? matches : !matches)
? PluginResult.Denied($"[DENIED] '{relative}': {reason}")
: null;
}
- // True when `relative` is an ancestor directory of (or exactly equal to) the fixed,
- // non-wildcard prefix of at least one write pattern — e.g. "workspace" is an ancestor of
- // "workspace/**", and "src/gen" is an ancestor of "src/gen/*.g.cs". Lets create_directory
- // succeed for directories that only exist to hold files the write glob already allows.
- private static bool IsAncestorOfWriteScope(string relative, IReadOnlyList writePatterns)
- {
- var candidate = relative.Split('/', StringSplitOptions.RemoveEmptyEntries);
- if (candidate.Length == 0) return false;
-
- foreach (var pattern in writePatterns)
- {
- var segments = pattern.Split('/', StringSplitOptions.RemoveEmptyEntries);
-
- int fixedCount = 0;
- while (fixedCount < segments.Length && segments[fixedCount].IndexOfAny(['*', '?']) < 0)
- fixedCount++;
-
- // A fully-literal pattern (no wildcard segment) names a file, not a directory —
- // only its parent segments are directories a create_directory call could target.
- int ancestorDepth = fixedCount == segments.Length ? fixedCount - 1 : fixedCount;
- if (candidate.Length > ancestorDepth) continue;
-
- bool isPrefix = true;
- for (int i = 0; i < candidate.Length; i++)
- {
- if (!string.Equals(candidate[i], segments[i], StringComparison.OrdinalIgnoreCase))
- {
- isPrefix = false;
- break;
- }
- }
- if (isPrefix) return true;
- }
-
- return false;
- }
-
private string? CheckEnvelope(string rawPath)
{
string resolved;
diff --git a/src/Infrastructure/Storage/UserConfigStore.cs b/src/Infrastructure/Storage/UserConfigStore.cs
index be698825..8770a4c0 100644
--- a/src/Infrastructure/Storage/UserConfigStore.cs
+++ b/src/Infrastructure/Storage/UserConfigStore.cs
@@ -34,11 +34,10 @@ public static (UserConfig? Config, string? LegacyKey) Load()
var config = new UserConfig
{
- ModelId = onDisk.ModelId ?? string.Empty,
- Endpoint = onDisk.Endpoint ?? string.Empty,
- Provider = onDisk.Provider ?? string.Empty,
- ApiKeyEnvVar = onDisk.ApiKeyEnvVar ?? string.Empty,
- ReplContextBudget = onDisk.ReplContextBudget,
+ ModelId = onDisk.ModelId ?? string.Empty,
+ Endpoint = onDisk.Endpoint ?? string.Empty,
+ Provider = onDisk.Provider ?? string.Empty,
+ ApiKeyEnvVar = onDisk.ApiKeyEnvVar ?? string.Empty,
};
return (config, onDisk.ApiKey ?? legacyKeyFile);
}
@@ -68,11 +67,10 @@ public static void Save(UserConfig config)
Directory.CreateDirectory(ConfigDir);
var onDisk = new OnDiskConfig
{
- ModelId = config.ModelId,
- Endpoint = config.Endpoint,
- Provider = config.Provider,
- ApiKeyEnvVar = config.ApiKeyEnvVar,
- ReplContextBudget = config.ReplContextBudget,
+ ModelId = config.ModelId,
+ Endpoint = config.Endpoint,
+ Provider = config.Provider,
+ ApiKeyEnvVar = config.ApiKeyEnvVar,
};
File.WriteAllText(ConfigPath, JsonSerializer.Serialize(onDisk, JsonOptions));
}
@@ -93,9 +91,6 @@ private sealed class OnDiskConfig
[JsonPropertyName("apiKeyEnvVar")]
public string? ApiKeyEnvVar { get; set; }
- [JsonPropertyName("replContextBudget")]
- public int? ReplContextBudget { get; set; }
-
// Present only in configs created before keychain support was added.
[JsonPropertyName("apiKey")]
public string? ApiKey { get; set; }
diff --git a/src/Orchestration/AgentOrchestrator.cs b/src/Orchestration/AgentOrchestrator.cs
index be75b8c5..ef1e6af3 100644
--- a/src/Orchestration/AgentOrchestrator.cs
+++ b/src/Orchestration/AgentOrchestrator.cs
@@ -924,29 +924,11 @@ await EmitContextAssemblyAsync(eventEmitter, assembled.Metrics, turn,
if (memoryManager is not null)
instructions = await memoryManager.AugmentInstructionsAsync(agentName, instructions, cancellationToken);
- var isolation = agentCfg?.Isolation ?? AgentIsolation.Fresh;
- var directive = isolation is AgentIsolation.Fresh or AgentIsolation.Fork
- ? OrchestratorHelpers.FindLastDirective((IReadOnlyList)history)
- : null;
-
IReadOnlyList filtered;
- if (isolation == AgentIsolation.Fresh && contextAssembler is not null)
+ if (agentCfg?.Context is { Count: > 0 } agentContextSources && contextAssembler is not null)
{
filtered = (await contextAssembler.AssembleForAgentAsync(
- agentName, task, (IReadOnlyList?)agentCfg?.Context ?? [],
- history, directive, cancellationToken)).Messages;
- }
- else if (isolation == AgentIsolation.Fresh)
- {
- // No assembler configured — degrade to the directive/task alone rather than
- // falling back to the shared transcript.
- filtered = [new ChatMessage(ChatRole.User, directive?.Format() ?? task)];
- }
- else if (agentCfg?.Context is { Count: > 0 } agentContextSources && contextAssembler is not null)
- {
- filtered = (await contextAssembler.AssembleForAgentAsync(
- agentName, task, agentContextSources, history,
- isolation == AgentIsolation.Fork ? directive : null, cancellationToken)).Messages;
+ agentName, task, agentContextSources, history, cancellationToken)).Messages;
}
else
{
@@ -965,11 +947,6 @@ await EmitContextAssemblyAsync(eventEmitter, assembled.Metrics, turn,
else filtered = raw;
}
else filtered = raw;
-
- // Fork: layer the synthesized directive on top of the full shared transcript,
- // matching ContextAssemblyPipeline.AssembleAsync's equivalent branch.
- if (isolation == AgentIsolation.Fork && directive is not null)
- filtered = [.. filtered, new ChatMessage(ChatRole.User, directive.Format())];
}
context = (hasInstructions || memoryManager is not null) && instructions is not null
diff --git a/src/Orchestration/Context/ContextAssembler.cs b/src/Orchestration/Context/ContextAssembler.cs
index 61e95fdc..3b346bab 100644
--- a/src/Orchestration/Context/ContextAssembler.cs
+++ b/src/Orchestration/Context/ContextAssembler.cs
@@ -4,7 +4,6 @@
using Microsoft.Extensions.AI;
using fuseraft.Core;
using fuseraft.Core.Models;
-using fuseraft.Core.Models.Agents;
using fuseraft.Core.Models.Context;
using fuseraft.Infrastructure;
@@ -148,17 +147,13 @@ public async Task AssembleForAgentAsync(
string task,
IReadOnlyList sources,
IList sharedHistory,
- AgentDirective? directive = null,
CancellationToken ct = default)
{
var result = new List();
var emptySources = new List();
- // 1. Task message — the agent always needs to know what it's working on. When a
- // synthesized directive is available (handoff() goal/background/constraints), it
- // replaces the raw task string — this is what makes Fresh isolation self-contained
- // rather than just "an empty Context: block with no explanation of what to do".
- result.Add(new ChatMessage(ChatRole.User, directive?.Format() ?? task));
+ // 1. Task message — the agent always needs to know what it's working on.
+ result.Add(new ChatMessage(ChatRole.User, task));
// Separate own_history sources from artifact sources.
ContextSource? ownHistorySrc = null;
@@ -575,12 +570,8 @@ private static IReadOnlyList ExtractOwnHistory(
// ── Pending-correction extraction ───────────────────────────────────────
// Returns all correction messages in shared history that appear after the last
- // assistant turn by agentName AND are addressed to agentName specifically — i.e. the
- // nearest preceding assistant turn belongs to agentName. Corrections are injected
- // immediately after the turn that triggered them (a blocked handoff, a validation
- // failure) with no explicit "addressed to" field, so once other agents have taken turns
- // since agentName last spoke (e.g. a graph loop revisits agentName later), a correction
- // meant for one of those other agents must not be attributed to agentName here.
+ // assistant turn by agentName. These are unread corrections the agent has not yet
+ // acted on; they must be included in the assembled context so the agent sees them.
private static IReadOnlyList ExtractPendingCorrections(
string agentName,
IList history)
@@ -597,19 +588,9 @@ private static IReadOnlyList ExtractPendingCorrections(
}
var corrections = new List();
- // Corrections immediately following agentName's own last turn (before any other
- // agent's turn intervenes) are addressed to agentName by construction.
- string? precedingAuthor = lastOwnIdx >= 0 ? agentName : null;
for (int i = lastOwnIdx + 1; i < history.Count; i++)
{
- if (history[i].Role == ChatRole.Assistant)
- {
- precedingAuthor = history[i].AuthorName;
- continue;
- }
-
- if (ContextWindowFilter.IsCorrectionMessage(history[i]) &&
- string.Equals(precedingAuthor, agentName, StringComparison.OrdinalIgnoreCase))
+ if (ContextWindowFilter.IsCorrectionMessage(history[i]))
corrections.Add(history[i]);
}
return corrections;
diff --git a/src/Orchestration/Context/ContextAssemblyPipeline.cs b/src/Orchestration/Context/ContextAssemblyPipeline.cs
index c2fb5bea..373767bd 100644
--- a/src/Orchestration/Context/ContextAssemblyPipeline.cs
+++ b/src/Orchestration/Context/ContextAssemblyPipeline.cs
@@ -5,7 +5,6 @@
using fuseraft.Core;
using fuseraft.Core.Interfaces;
using fuseraft.Core.Models;
-using fuseraft.Core.Models.Agents;
using fuseraft.Infrastructure;
namespace fuseraft.Orchestration.Context;
@@ -126,50 +125,16 @@ public async Task AssembleAsync(
IReadOnlyList declaredSources = [];
IReadOnlyList emptySources = [];
- var isolation = agentCfg?.Isolation ?? AgentIsolation.Fresh;
- var directive = request.Directive
- ?? (isolation is AgentIsolation.Fresh or AgentIsolation.Fork
- ? OrchestratorHelpers.FindLastDirective(history)
- : null);
-
- if (isolation == AgentIsolation.Fresh)
- {
- // Fresh: never touch SharedHistory. Build from the synthesized directive (if any)
- // plus whatever Context: sources this agent declares — even when that list is empty,
- // this is NOT the SharedHistoryFallback path.
- var contextSources = (IReadOnlyList?)agentCfg?.Context ?? [];
- if (_contextAssembler is not null)
- {
- var assembled = await _contextAssembler.AssembleForAgentAsync(
- agentName, task, contextSources,
- history as IList ?? new List(history), directive, ct);
- baseMessages = assembled.Messages;
- historyChars = baseMessages.Sum(m => m.Text?.Length ?? 0);
- historyMessages = baseMessages;
- declaredSources = contextSources.Select(s => s.Source).ToList();
- emptySources = assembled.EmptySources;
- }
- else
- {
- // No assembler configured — degrade to the directive/task alone rather than
- // falling back to the shared transcript, preserving the Fresh isolation invariant.
- baseMessages = [new ChatMessage(ChatRole.User, directive?.Format() ?? task)];
- historyChars = baseMessages.Sum(m => m.Text?.Length ?? 0);
- historyMessages = baseMessages;
- }
- contextStrategy = ContextAssemblyMetrics.Strategies.ArtifactSpec;
- }
- else if (agentCfg?.Context is { Count: > 0 } contextSources2 && _contextAssembler is not null)
+ if (agentCfg?.Context is { Count: > 0 } contextSources && _contextAssembler is not null)
{
var assembled = await _contextAssembler.AssembleForAgentAsync(
- agentName, task, contextSources2,
- history as IList ?? new List(history),
- isolation == AgentIsolation.Fork ? directive : null, ct);
+ agentName, task, contextSources,
+ history as IList ?? new List(history), ct);
baseMessages = assembled.Messages;
historyChars = baseMessages.Sum(m => m.Text?.Length ?? 0);
historyMessages = baseMessages;
contextStrategy = ContextAssemblyMetrics.Strategies.ArtifactSpec;
- declaredSources = contextSources2.Select(s => s.Source).ToList();
+ declaredSources = contextSources.Select(s => s.Source).ToList();
emptySources = assembled.EmptySources;
}
else
@@ -185,10 +150,6 @@ public async Task AssembleAsync(
sessionContextChars = sessionCtx.Length;
baseMessages = BuildDefaultMessages(filtered, sessionCtx);
-
- // Fork: layer the synthesized directive on top of the full shared transcript.
- if (isolation == AgentIsolation.Fork && directive is not null)
- baseMessages = [.. baseMessages, new ChatMessage(ChatRole.User, directive.Format())];
}
// ── History breakdown (role + compaction) ────────────────────────────
diff --git a/src/Orchestration/Graph/SubGraphExecutor.cs b/src/Orchestration/Graph/SubGraphExecutor.cs
index 3e2b4b9f..2f91643d 100644
--- a/src/Orchestration/Graph/SubGraphExecutor.cs
+++ b/src/Orchestration/Graph/SubGraphExecutor.cs
@@ -66,8 +66,7 @@ await eventEmitter.EmitAsync(EventTypes.AgentStart,
?? (ILogger)Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance;
subOrchestrator = new MapReduceOrchestrator(
subConfig, services.AgentFactory, mrLogger,
- services.ChangeTracker, eventEmitter, services.GovernanceKernel,
- services.HumanApprovalService, services.ContextPipeline, services.RepositoryKnowledgeStore);
+ services.ChangeTracker, eventEmitter, services.GovernanceKernel);
}
else if (subSpec.IsScatterGather)
{
@@ -84,8 +83,7 @@ await eventEmitter.EmitAsync(EventTypes.AgentStart,
?? (ILogger)Microsoft.Extensions.Logging.Abstractions.NullLogger.Instance;
subOrchestrator = new ScatterGatherOrchestrator(
subConfig, services.AgentFactory, sgLogger,
- services.ChangeTracker, eventEmitter, services.GovernanceKernel,
- services.HumanApprovalService, services.ContextPipeline, services.RepositoryKnowledgeStore);
+ services.ChangeTracker, eventEmitter, services.GovernanceKernel);
}
else
{
diff --git a/src/Orchestration/OrchestratorHelpers.cs b/src/Orchestration/OrchestratorHelpers.cs
index f63680c5..d45c407b 100644
--- a/src/Orchestration/OrchestratorHelpers.cs
+++ b/src/Orchestration/OrchestratorHelpers.cs
@@ -3,9 +3,7 @@
using Microsoft.Extensions.AI;
using Microsoft.Extensions.Logging;
using fuseraft.Core.Models;
-using fuseraft.Core.Models.Agents;
using fuseraft.Infrastructure;
-using fuseraft.Infrastructure.Plugins;
namespace fuseraft.Orchestration;
@@ -100,64 +98,6 @@ internal static class OrchestratorHelpers
return val?.ToString();
}
- // Same lookup as GetArg, but against FunctionCallContent.Arguments' actual declared type
- // (IDictionary) — avoids an unchecked cast to IReadOnlyDictionary that
- // would throw InvalidCastException if a future Arguments implementation didn't also
- // implement IReadOnlyDictionary. Kept separate from GetArg (rather than overloaded) because
- // FunctionInvocationContext.Arguments is the concrete AIFunctionArguments type, which
- // implements both interfaces — an overload on IDictionary would make its call sites
- // ambiguous.
- private static string? GetHandoffArg(IDictionary? args, string key)
- {
- if (args is null || !args.TryGetValue(key, out var val)) return null;
- return val?.ToString();
- }
-
- // Builds an AgentDirective from a handoff() FunctionCallContent's optional structured
- // arguments (goal/background/constraints). Returns null when the call omitted `goal` —
- // callers fall back to legacy marker-message behavior in that case.
- internal static AgentDirective? TryExtractDirective(FunctionCallContent fc)
- {
- var args = fc.Arguments;
- var goal = GetHandoffArg(args, HandoffPlugin.GoalArgumentName);
- if (string.IsNullOrWhiteSpace(goal)) return null;
-
- var background = GetHandoffArg(args, HandoffPlugin.BackgroundArgumentName);
- var constraints = GetHandoffArg(args, HandoffPlugin.ConstraintsArgumentName);
-
- return new AgentDirective
- {
- Goal = goal.Trim(),
- Background = string.IsNullOrWhiteSpace(background) ? null : background.Trim(),
- Constraints = string.IsNullOrWhiteSpace(constraints)
- ? []
- : constraints.Split('\n', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries),
- };
- }
-
- // Scans the tail of history for the most recent handoff() call and extracts its directive,
- // if any. Used where a directive must be recovered after the fact (e.g. at context-assembly
- // time) rather than at the moment the FunctionCallContent is first observed.
- internal static AgentDirective? FindLastDirective(IReadOnlyList history, int lookback = AgentMessageLookback)
- {
- for (int i = history.Count - 1, scanned = 0; i >= 0 && scanned < lookback; i--)
- {
- foreach (var item in history[i].Contents)
- {
- if (item is FunctionCallContent fc &&
- string.Equals(fc.Name, HandoffPlugin.FunctionName, StringComparison.OrdinalIgnoreCase))
- {
- // Stop at the most recent handoff() call regardless of whether it carried a
- // directive (i.e. declared `goal`). An older handoff's goal/background was
- // addressed to a *different* recipient and must not be resurrected here.
- return TryExtractDirective(fc);
- }
- }
- if (history[i].Role == ChatRole.Assistant) scanned++;
- }
- return null;
- }
-
// Counts how many consecutive assistant turns from agentName appear at the tail of
// history, stopping at any user message or a different agent's turn.
internal static int CountConsecutiveAgentTurns(IList history, string agentName)
diff --git a/src/Orchestration/Strategies/StrategyFactory.cs b/src/Orchestration/Strategies/StrategyFactory.cs
index caaae2e5..f2ab7261 100644
--- a/src/Orchestration/Strategies/StrategyFactory.cs
+++ b/src/Orchestration/Strategies/StrategyFactory.cs
@@ -258,8 +258,7 @@ private static Dictionary BuildValidators(
provenanceRegistry: provenanceRegistry),
// Threshold defaults to 3; command pattern supplied per-route via RequiredCommandPattern.
[ValidatorNames.BlockOnConsecutiveFail] = new ConsecutiveShellFailValidator(
- changeLogPath: config?.ChangeLogPath),
- [ValidatorNames.RequireSessionContextWrite] = new RequireSessionContextWriteValidator(),
+ changeLogPath: config?.ChangeLogPath)
};
if (config is not null)
diff --git a/src/Orchestration/Validation/RequireSessionContextWriteValidator.cs b/src/Orchestration/Validation/RequireSessionContextWriteValidator.cs
deleted file mode 100644
index b1adbbf3..00000000
--- a/src/Orchestration/Validation/RequireSessionContextWriteValidator.cs
+++ /dev/null
@@ -1,55 +0,0 @@
-using Microsoft.Extensions.AI;
-using fuseraft.Core.Interfaces;
-
-namespace fuseraft.Orchestration.Validation;
-
-///
-/// Blocks a handoff route unless the source agent called session_context_write
-/// () during the
-/// current turn.
-///
-///
-/// Not auto-attached — opt in explicitly with Validators: [RequireSessionContextWrite]
-/// on a route/edge/transition whose source agent has
-/// isolation. A Fresh
-/// agent's own turn — tool calls, intermediate reasoning — never reaches the next agent;
-/// only its session_context_write summary and the synthesized
-/// do. Without this validator, an
-/// agent that forgets to write a summary silently hands the next agent nothing; attaching it
-/// turns that into a hard, visible failure at handoff time instead of a discovered-later
-/// context gap. See skills/craft-orchestration/references/schema-cheatsheet.md's "Built-in
-/// validators" table.
-///
-///
-public sealed class RequireSessionContextWriteValidator : IRoutingValidator
-{
- public Task ValidateAsync(
- IList history,
- CancellationToken cancellationToken = default)
- {
- for (int i = history.Count - 1; i >= 0; i--)
- {
- var msg = history[i];
-
- // User messages mark the turn boundary — stop here.
- if (msg.Role == ChatRole.User) break;
-
- if (msg.Role != ChatRole.Tool) continue;
-
- foreach (var item in msg.Contents)
- {
- if (item is not FunctionResultContent frc) continue;
- var funcName = HistoryHelpers.FindFunctionName(history, frc.CallId, i) ?? string.Empty;
- if (funcName.Equals("session_context_write", StringComparison.OrdinalIgnoreCase))
- return Task.FromResult(RoutingValidationResult.Pass());
- }
- }
-
- return Task.FromResult(RoutingValidationResult.Fail(
- "Handoff blocked: this agent runs in isolated (Fresh) context — the next agent will " +
- "not see this conversation, only what you write to session_context_write.\n\n" +
- " 1. Call session_context_write(summary: \"...\") — what you accomplished, files " +
- "changed, and anything the next agent needs to know.\n" +
- " 2. Emit the handoff keyword in the same response."));
- }
-}
diff --git a/src/Orchestration/Validation/ValidatorRegistry.cs b/src/Orchestration/Validation/ValidatorRegistry.cs
index 291c5dad..40b14f4d 100644
--- a/src/Orchestration/Validation/ValidatorRegistry.cs
+++ b/src/Orchestration/Validation/ValidatorRegistry.cs
@@ -69,8 +69,6 @@ public static IReadOnlyList BuildValidatorsFromNames(
sandboxRoot);
else if (name.Equals(ValidatorNames.ArchitectureValidator, StringComparison.OrdinalIgnoreCase))
v = new ArchitectureValidator(projectRoot: sandboxRoot);
- else if (name.Equals(ValidatorNames.RequireSessionContextWrite, StringComparison.OrdinalIgnoreCase))
- v = new RequireSessionContextWriteValidator();
if (v is not null)
result.Add(v);
diff --git a/src/Orchestration/ValidatorNames.cs b/src/Orchestration/ValidatorNames.cs
index d84018b6..315ec936 100644
--- a/src/Orchestration/ValidatorNames.cs
+++ b/src/Orchestration/ValidatorNames.cs
@@ -17,7 +17,6 @@ public static class ValidatorNames
public const string BlockOnConsecutiveFail = "BlockOnConsecutiveFail";
public const string TestReportValid = "TestReportValid";
public const string ArchitectureValidator = "ArchitectureValidator";
- public const string RequireSessionContextWrite = "RequireSessionContextWrite";
// Synthetic validator names emitted into ValidatorStuckException / event logs
public const string StructuredRouting = "StructuredRouting";
diff --git a/src/fuseraft.csproj b/src/fuseraft.csproj
index ca358187..51578c61 100644
--- a/src/fuseraft.csproj
+++ b/src/fuseraft.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/tests/FuseraftCli.Tests/ContextAssemblyPipelineIsolationTests.cs b/tests/FuseraftCli.Tests/ContextAssemblyPipelineIsolationTests.cs
deleted file mode 100644
index f6ab5a1b..00000000
--- a/tests/FuseraftCli.Tests/ContextAssemblyPipelineIsolationTests.cs
+++ /dev/null
@@ -1,145 +0,0 @@
-using Microsoft.Extensions.AI;
-using fuseraft.Core.Models.Agents;
-using fuseraft.Orchestration.Context;
-
-namespace FuseraftCli.Tests;
-
-///
-/// Tests for the Isolation-aware branch of —
-/// the core behavioral flip of the agent-isolation protocol overhaul:
-/// (the default) must never surface SharedHistory content, while
-/// preserves the pre-overhaul windowed-transcript fallback and layers
-/// a synthesized directive on top of the full transcript.
-///
-public sealed class ContextAssemblyPipelineIsolationTests
-{
- private static List SharedHistoryWithSecret() =>
- [
- new ChatMessage(ChatRole.User, "Investigate the outage.") { AuthorName = "Investigator" },
- new ChatMessage(ChatRole.Assistant, "SECRET_REASONING: tried X, ruled it out, tried Y.")
- { AuthorName = "Investigator" },
- ];
-
- [Fact]
- public async Task Fresh_agent_never_sees_shared_history_content()
- {
- var pipeline = new ContextAssemblyPipeline();
- var request = new AgentExecutionRequest
- {
- AgentName = "Fixer",
- Task = "Fix the outage.",
- SharedHistory = SharedHistoryWithSecret(),
- AgentConfig = new AgentConfig { Name = "Fixer", Isolation = AgentIsolation.Fresh },
- };
-
- var assembled = await pipeline.AssembleAsync(request);
-
- Assert.DoesNotContain(assembled.Messages, m =>
- m.Text?.Contains("SECRET_REASONING", StringComparison.Ordinal) == true);
- Assert.Equal(
- fuseraft.Core.Models.Context.ContextAssemblyMetrics.Strategies.ArtifactSpec,
- assembled.Metrics.ContextStrategy);
- }
-
- [Fact]
- public async Task Fresh_agent_uses_directive_as_task_message_when_supplied()
- {
- var pipeline = new ContextAssemblyPipeline();
- var directive = new AgentDirective
- {
- Goal = "Patch the null check in Parser.cs.",
- Background = "Root cause confirmed: line 42 dereferences before the null guard.",
- Constraints = ["Do not change the public API."],
- };
- var request = new AgentExecutionRequest
- {
- AgentName = "Fixer",
- Task = "(original session task — should not appear verbatim)",
- SharedHistory = SharedHistoryWithSecret(),
- Directive = directive,
- AgentConfig = new AgentConfig { Name = "Fixer", Isolation = AgentIsolation.Fresh },
- };
-
- var assembled = await pipeline.AssembleAsync(request);
-
- Assert.Contains(assembled.Messages, m =>
- m.Text?.Contains("Patch the null check in Parser.cs.", StringComparison.Ordinal) == true);
- Assert.Contains(assembled.Messages, m =>
- m.Text?.Contains("Do not change the public API.", StringComparison.Ordinal) == true);
- }
-
- [Fact]
- public async Task Fresh_agent_recovers_directive_from_last_handoff_call_when_not_supplied_directly()
- {
- var pipeline = new ContextAssemblyPipeline();
- var history = new List
- {
- new ChatMessage(ChatRole.User, "Investigate the outage.") { AuthorName = "Investigator" },
- new ChatMessage(ChatRole.Assistant,
- [
- new FunctionCallContent("call-1", "handoff", new Dictionary
- {
- ["route_keyword"] = "HANDOFF TO FIXER",
- ["goal"] = "Patch the parser null check.",
- ["background"] = "Root cause already confirmed.",
- }),
- ])
- { AuthorName = "Investigator" },
- };
- var request = new AgentExecutionRequest
- {
- AgentName = "Fixer",
- Task = "(original session task)",
- SharedHistory = history,
- AgentConfig = new AgentConfig { Name = "Fixer", Isolation = AgentIsolation.Fresh },
- };
-
- var assembled = await pipeline.AssembleAsync(request);
-
- Assert.Contains(assembled.Messages, m =>
- m.Text?.Contains("Patch the parser null check.", StringComparison.Ordinal) == true);
- }
-
- [Fact]
- public async Task Shared_agent_with_no_context_block_keeps_legacy_history_fallback()
- {
- var pipeline = new ContextAssemblyPipeline();
- var request = new AgentExecutionRequest
- {
- AgentName = "Investigator",
- Task = "Investigate the outage.",
- SharedHistory = SharedHistoryWithSecret(),
- AgentConfig = new AgentConfig { Name = "Investigator", Isolation = AgentIsolation.Shared },
- };
-
- var assembled = await pipeline.AssembleAsync(request);
-
- Assert.Contains(assembled.Messages, m =>
- m.Text?.Contains("SECRET_REASONING", StringComparison.Ordinal) == true);
- Assert.Equal(
- fuseraft.Core.Models.Context.ContextAssemblyMetrics.Strategies.SharedHistoryFallback,
- assembled.Metrics.ContextStrategy);
- }
-
- [Fact]
- public async Task Fork_agent_sees_full_history_plus_directive()
- {
- var pipeline = new ContextAssemblyPipeline();
- var directive = new AgentDirective { Goal = "Audit the session for inconsistencies." };
- var request = new AgentExecutionRequest
- {
- AgentName = "Verifier",
- Task = "Investigate the outage.",
- SharedHistory = SharedHistoryWithSecret(),
- Directive = directive,
- AgentConfig = new AgentConfig { Name = "Verifier", Isolation = AgentIsolation.Fork },
- };
-
- var assembled = await pipeline.AssembleAsync(request);
-
- Assert.Contains(assembled.Messages, m =>
- m.Text?.Contains("SECRET_REASONING", StringComparison.Ordinal) == true);
- Assert.Contains(assembled.Messages, m =>
- m.Text?.Contains("Audit the session for inconsistencies.", StringComparison.Ordinal) == true);
- }
-}
diff --git a/tests/FuseraftCli.Tests/OrchestratorConfigLoaderIsolationTests.cs b/tests/FuseraftCli.Tests/OrchestratorConfigLoaderIsolationTests.cs
deleted file mode 100644
index d43db242..00000000
--- a/tests/FuseraftCli.Tests/OrchestratorConfigLoaderIsolationTests.cs
+++ /dev/null
@@ -1,72 +0,0 @@
-using Microsoft.Extensions.Logging.Abstractions;
-using fuseraft.Cli;
-using fuseraft.Core.Models.Agents;
-using fuseraft.Core.Models.Orchestration;
-using fuseraft.Orchestration;
-
-namespace FuseraftCli.Tests;
-
-///
-/// Tests for — the guard
-/// that keeps Isolation: Fresh (the new default) from silently starving Magentic's
-/// manager/ledger loop, which structurally depends on every participant sharing the transcript.
-///
-public sealed class OrchestratorConfigLoaderIsolationTests
-{
- private static AgentConfig Agent(string name, AgentIsolation isolation) =>
- new() { Name = name, Isolation = isolation };
-
- [Fact]
- public void Magentic_config_with_a_fresh_agent_is_rejected()
- {
- var config = new OrchestrationConfig
- {
- Selection = new SelectionStrategyConfig { Type = OrchestratorTypes.Magentic },
- Agents =
- [
- Agent("Manager", AgentIsolation.Shared),
- Agent("Worker", AgentIsolation.Fresh),
- ],
- };
-
- var ex = Assert.Throws(() =>
- OrchestratorConfigLoader.ValidateIsolationConstraints(config, NullLoggerFactory.Instance));
-
- Assert.Contains("magentic", ex.Message, StringComparison.OrdinalIgnoreCase);
- Assert.Contains("Worker", ex.Message, StringComparison.Ordinal);
- }
-
- [Fact]
- public void Magentic_config_with_all_agents_shared_or_fork_is_accepted()
- {
- var config = new OrchestrationConfig
- {
- Selection = new SelectionStrategyConfig { Type = OrchestratorTypes.Magentic },
- Agents =
- [
- Agent("Manager", AgentIsolation.Shared),
- Agent("Worker", AgentIsolation.Fork),
- ],
- };
-
- var exception = Record.Exception(() =>
- OrchestratorConfigLoader.ValidateIsolationConstraints(config, NullLoggerFactory.Instance));
-
- Assert.Null(exception);
- }
-
- [Fact]
- public void Non_magentic_config_with_a_fresh_agent_is_accepted()
- {
- var config = new OrchestrationConfig
- {
- Selection = new SelectionStrategyConfig { Type = OrchestratorTypes.StateMachine },
- Agents = [Agent("Developer", AgentIsolation.Fresh)],
- };
-
- var exception = Record.Exception(() =>
- OrchestratorConfigLoader.ValidateIsolationConstraints(config, NullLoggerFactory.Instance));
-
- Assert.Null(exception);
- }
-}
diff --git a/tests/FuseraftCli.Tests/SandboxEnforcementFilterTests.cs b/tests/FuseraftCli.Tests/SandboxEnforcementFilterTests.cs
index 86d75aa1..48d2e2f9 100644
--- a/tests/FuseraftCli.Tests/SandboxEnforcementFilterTests.cs
+++ b/tests/FuseraftCli.Tests/SandboxEnforcementFilterTests.cs
@@ -133,50 +133,4 @@ public void ShellRun_SedRegexContainingSlashes_IsNotFalselyDeniedAsAPath()
Assert.Null(result);
}
-
- // ── create_directory vs. a file-shaped write glob ──────────────────────
-
- [Fact]
- public void CreateDirectory_BareAncestorOfWriteGlob_IsAllowed()
- {
- // "workspace/**" only matches files under workspace/, never the literal
- // "workspace" segment itself — but creating that directory is a prerequisite
- // for writes the glob already permits, so it must not be denied.
- var result = MakeFilter().Inspect("create_directory",
- new Dictionary { ["path"] = "workspace" });
-
- Assert.Null(result);
- }
-
- [Fact]
- public void CreateDirectory_NestedAncestorOfWriteGlob_IsAllowed()
- {
- var result = MakeFilter().Inspect("create_directory",
- new Dictionary { ["path"] = "workspace/src" });
-
- Assert.Null(result);
- }
-
- [Fact]
- public void CreateDirectory_OutsideWriteGlob_IsStillDenied()
- {
- var result = MakeFilter().Inspect("create_directory",
- new Dictionary { ["path"] = "other" });
-
- Assert.NotNull(result);
- Assert.Contains("DENIED", result);
- }
-
- [Fact]
- public void WriteFile_BareAncestorDirectory_IsNotAllowedByAncestorRule()
- {
- // The ancestor relaxation is scoped to create_directory only — write_file must
- // still match the glob on its own merits, so a bare "workspace" (not a file
- // under it) stays denied.
- var result = MakeFilter().Inspect("write_file",
- new Dictionary { ["path"] = "workspace" });
-
- Assert.NotNull(result);
- Assert.Contains("DENIED", result);
- }
}
diff --git a/tests/FuseraftCli.Tests/ValidateConfigCommandTests.cs b/tests/FuseraftCli.Tests/ValidateConfigCommandTests.cs
index b76690e8..217bef76 100644
--- a/tests/FuseraftCli.Tests/ValidateConfigCommandTests.cs
+++ b/tests/FuseraftCli.Tests/ValidateConfigCommandTests.cs
@@ -729,7 +729,7 @@ public async Task MagenticSelection_ValidConfig_Returns0()
var config = """
{
"Orchestration": {
- "Agents": [{"Name": "Worker", "Instructions": "do work", "Model": {"ModelId": "gpt-4o"}, "Isolation": "Shared"}],
+ "Agents": [{"Name": "Worker", "Instructions": "do work", "Model": {"ModelId": "gpt-4o"}}],
"Selection": {
"Type": "magentic",
"Magentic": {
@@ -796,7 +796,7 @@ public async Task MagenticSelection_ModelAlias_Resolves()
"ApiKeyEnvVar": "OPENAI_API_KEY"
}
},
- "Agents": [{"Name": "Worker", "Instructions": "ok", "Model": {"ModelId": "gpt-4o"}, "Isolation": "Shared"}],
+ "Agents": [{"Name": "Worker", "Instructions": "ok", "Model": {"ModelId": "gpt-4o"}}],
"Selection": {
"Type": "magentic",
"Magentic": {
@@ -824,7 +824,7 @@ public async Task MagenticSelection_TerminationConfigured_WarnsButPasses()
var config = """
{
"Orchestration": {
- "Agents": [{"Name": "Worker", "Instructions": "do work", "Model": {"ModelId": "gpt-4o"}, "Isolation": "Shared"}],
+ "Agents": [{"Name": "Worker", "Instructions": "do work", "Model": {"ModelId": "gpt-4o"}}],
"Selection": {
"Type": "magentic",
"Magentic": {"Model": {"ModelId": "gpt-4o"}}