Enhance validators and agent defaults with global config support#10
Merged
Conversation
added 14 commits
May 11, 2026 11:56
…dator patch_file is a legitimate file-writing tool call (surgical edit) but was invisible to HandoffToTesterValidator, causing agents that use patch_file to be permanently blocked at the handoff gate. Also generalized the error message away from "HANDOFF TO TESTER blocked" and Developer-specific build instructions — this validator backs RequireWriteFile which is used on edges beyond Developer→Tester (e.g. Archaeologist's RECON COMPLETE edge), so the old message misdirected agents into irrelevant recovery steps.
Agents generated by templates had no Endpoint or ApiKeyEnvVar, forcing users to manually add them to every agent file even when ~/.fuseraft/config already declared a provider URL. ApplyGlobalDefaults now runs after config binding (in both BuildAsync and LoadConfig) and fills in missing Endpoint and ApiKeyEnvVar on every agent model, named alias, and selection/magentic model. Explicit per-agent values always win; global values only fill empty fields. Also adds ApiKeyEnvVar to UserConfig and UserConfigStore so it can be set once in the global config and inherited everywhere.
…yEnvVar When a user stores their provider key in the OS keychain (via `fuseraft key set`) and no agent file declares ApiKey or ApiKeyEnvVar, the key was silently unused at run time and auth failed. ApplyKeychainKeyAsync now runs after ApplyGlobalDefaults in BuildAsync: it retrieves the keychain key once and injects it as a literal ApiKey on every model config that still has neither field set. Models with either field already populated are untouched. LoadConfig (used for display and validation) is deliberately kept sync and unchanged since the literal key is only needed at runtime, not for config inspection.
validators.md — RequireWriteFile now accepts patch_file alongside write_file; update the "used on" header (no longer specific to HANDOFF TO TESTER), the pass/fail descriptions, the ShellFallbackPattern prose, and the error-message example to match the new generic message. models.md — add "Global config defaults" section documenting that endpoint and apiKeyEnvVar in ~/.fuseraft/config are applied to any agent model that omits them, and documenting the full auth priority chain (explicit ApiKey → ApiKeyEnvVar → global apiKeyEnvVar → OS keychain). Update the ModelConfig table rows for Endpoint and ApiKeyEnvVar to note the global-config fallback. cli-reference.md — note that the --endpoint flag value is also inherited by agents at run time; clarify that keychain-only agents skip the ApiKeyEnvVar env-var check in static validation.
…v-independent LoadConfig is used for static inspection and validation — it should check the config as-written, not as-runtime-resolved. Applying global defaults there made ValidateConfigCommand pass configs that are actually incomplete (e.g. unknown model prefix with no Endpoint), because ~/.fuseraft/config's endpoint silently filled the gap. Global defaults now apply only in BuildAsync where agents are actually invoked. Fixes ModelWithoutPrefix_NoEndpoint_Errors test.
…th set The short-circuit that bypasses model-ID prefix detection only fired when both Endpoint and a literal ApiKey were present. When global config supplied Endpoint and ApiKeyEnvVar (env-var reference instead of literal key), the model fell through to DetectFromPrefix, which doesn't recognise AWS Bedrock-style IDs like anthropic.claude-sonnet-4-5-20250929-v1:0, throwing "Cannot determine the LLM provider". Extend the short-circuit to cover any form of auth (ApiKey OR ApiKeyEnvVar) — if the caller supplied their own endpoint and auth, treat as OpenAI-compatible and skip prefix detection entirely.
…tection fails When a model ID doesn't match any known prefix (e.g. AWS Bedrock-style IDs like anthropic.claude-sonnet-4-5-20250929-v1:0 used through Open WebUI), and an Endpoint is set (either inline or injected from ~/.fuseraft/config), Resolve was still throwing "Cannot determine the LLM provider". A custom Endpoint is an unambiguous signal that the caller knows which provider to use — fall back to openai-compatible instead of throwing, consistent with how the REPL wizard treats enterprise/custom endpoints.
… it blank ApplyGlobalDefaults already propagated Endpoint and ApiKeyEnvVar from the global config; extend it to cover ModelId too. This completes the contract: fuseraft run will not fail due to a missing connection field as long as ~/.fuseraft/config supplies the defaults — agent files only need to override what differs from the global config (e.g. a per-agent MaxTokens or a different model for one role).
… in SDK ApiKeyCredential throws "Value cannot be an empty string" when given an empty key, producing a cryptic stack trace from deep in System.ClientModel. Guard both the openai-compatible and azure branches with an explicit check before the constructor call, surfacing a message that names the model, the endpoint, and the two ways to fix it (fuseraft key set or apiKeyEnvVar in global config).
…fuseraft key set'
…c catch in SessionRunner The generic catch block called AnsiConsole.WriteException (printing the full stack trace) and also set errorMessage, which RenderSummary then printed again as '✗ Error: ...'. The message already surfaces clearly through RenderSummary; the stack trace added noise without value for user-facing operational errors.
Add docs/context-management.md — a unified overview of all four context layers (context store, persistent memory, ContextWindow filtering, compaction). Covers TextOnly, ExcludeAgents, MaxTurnAge, MaxTailMessages, ContextCapFraction, replay truncation, all five compaction modes, IncludeReasoning, IncludeSymbolGraph, change log grounding, and the head+tail pinning behaviour. Includes a flow diagram and opinionated strategy guide. Rename docs/context.md → docs/context-store.md so the file name matches what it actually documents (the file-import CLI, not conversation context). Update mkdocs.yml nav and index.md guide table accordingly.
…ontext import Add DocumentTextExtractor (PDF via PdfPig, DOCX/PPTX/XLSX via DocumentFormat.OpenXml) — fully cross-platform pure-.NET extraction, no shell tools required. Add DocumentPlugin with four read-only tools: document_extract_text — full text from PDF, DOCX, PPTX, or XLSX document_get_info — format/size metadata without full extraction document_list_sheets — sheet names from an XLSX workbook document_get_sheet — single sheet as pipe-delimited text table Fix ContextStore.AddAsync: binary documents (.pdf, .docx, .pptx, .xlsx) are now extracted to .txt at import time so agents can access them via read_file. Falls back to storing the binary with a warning on extraction failure. ExtractionInfo recorded on ContextItem; notes surfaced in CLI output. Register Document in PluginRegistry (default + sandboxed); add capability entries (all read) to PluginCapabilityMap. Update plugins.md and context-store.md.
… Document plugin - configuration.md: add Document to the capability tag table - cli-reference.md: document binary extraction behaviour in fuseraft context add - context-management.md: update Layer 1 to mention document formats and cross-link Document plugin
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.
No description provided.