Skip to content

feat(provider): add AdaL (SylphAI) as a built-in provider#1

Closed
chindris-mihai-alexandru wants to merge 21 commits intomainfrom
feat/adal-provider
Closed

feat(provider): add AdaL (SylphAI) as a built-in provider#1
chindris-mihai-alexandru wants to merge 21 commits intomainfrom
feat/adal-provider

Conversation

@chindris-mihai-alexandru
Copy link
Copy Markdown
Owner

Summary

Add AdaL (SylphAI) as a built-in provider in ForgeCode, enabling users to access AdaL's AI models directly from the ForgeCode terminal.

Motivation

AdaL CLI by SylphAI is a self-evolving coding agent that provides access to models from OpenAI, Anthropic, Google, and more. Adding AdaL as a provider in ForgeCode allows:

  • Cross-tool model access — ForgeCode users can use AdaL's model routing via forge provider login adal
  • Credential sharing — Developers using both tools can share their ADAL_API_KEY
  • Expanded ecosystem — Connects two popular AI development tools

Changes

crates/forge_repo/src/provider/provider.json

  • Added adal provider entry with OpenAI-compatible response type
  • API endpoint: https://api.sylph.ai/v1/chat/completions
  • Models endpoint: https://api.sylph.ai/v1/models (dynamic model fetching)
  • Auth: API key (ADAL_API_KEY)

crates/forge_domain/src/provider.rs

  • Added ProviderId::ADAL constant
  • Added "adal" to built_in_providers() list
  • Added "AdaL" display name mapping
  • Added "adal" to FromStr implementation

Testing

  • cargo check -p forge_domain passes
  • ✅ All 17 provider tests pass (cargo test -p forge_domain -- provider)
  • ✅ JSON validation passes

Related

🌸 Generated with AdaL

Alexx999 and others added 20 commits April 2, 2026 14:13
…2814)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
…#2813)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
tailcallhq#2762)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Amit Singh <amitksingh1490@gmail.com>
…ailcallhq#2790)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Amit Singh <amitksingh1490@gmail.com>
Co-authored-by: ForgeCode <noreply@forgecode.dev>
…ailcallhq#2783)

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Amit Singh <amitksingh1490@gmail.com>
…lcallhq#2837)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
…#2849)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
tailcallhq#2861)

Co-authored-by: ForgeCode <noreply@forgecode.dev>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
…dling (tailcallhq#2803)

Co-authored-by: Aiswarya Prakasan <aiswaryaprakasan@Aiswaryas-MacBook-Pro.local>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Co-authored-by: Tushar Mathur <tusharmath@gmail.com>
Co-authored-by: Amit Singh <amitksingh1490@gmail.com>
…ugh the stack (tailcallhq#2850)

Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
Copilot AI review requested due to automatic review settings April 6, 2026 18:23
@qodo-code-review
Copy link
Copy Markdown

Review Summary by Qodo

Eager config initialization, provider ecosystem expansion, and OAuth localhost callback support

✨ Enhancement 🐞 Bug fix 🧪 Tests 📝 Documentation

Grey Divider

Walkthroughs

Description
  **Major refactoring for eager configuration initialization and provider ecosystem expansion:**
• **Configuration threading**: Refactored config initialization from lazy (with silent errors) to
  eager at application startup, reading ForgeConfig once in main.rs and threading it through all
  layers (API, services, repositories, apps) to eliminate repeated disk reads and surface errors early
• **Provider ecosystem expansion**: Added GoogleAIStudio and AdaL (SylphAI) as built-in
  providers; refreshed Bedrock model catalog with 100+ new models across Google, Qwen, Mistral, Meta,
  DeepSeek, Anthropic, NVIDIA, MiniMax, OpenAI, Writer, and Zai; added inline provider definitions
  support via forge.toml
• **OAuth improvements**: Implemented LocalhostOAuthCallbackServer for capturing OAuth browser
  redirects on localhost with automatic redirect capture; added OpenID Connect support via id_token
  field; enhanced credential enrichment for Codex OAuth strategies
• **Tool call argument normalization**: Added NormalizeToolCallArguments transformer to detect and
  parse stringified JSON arguments (e.g., from kimi-k2p5-turbo API), converting to structured JSON
  with fallback to raw content for malformed data
• **Streaming usage fix**: Changed Anthropic streaming usage accumulation from sum to max-based
  merge strategy to fix double-counting where message_start and message_delta both report token
  counts
• **Bedrock improvements**: Added SanitizeToolIds transformer to replace invalid characters in
  tool call IDs with underscores (Bedrock requires ^[a-zA-Z0-9_-]+$); fixed prompt token calculation
  to use input_tokens instead of total_tokens
• **Conversation tracking**: Added initiator field to Context to track conversation origin
  ("user" or "agent"); persisted in conversation records for audit trails
• **File system improvements**: Added symlink exclusion from file discovery pipeline in both walker
  and fd modules; added is_symlink() helper function
• **Shell plugin refactoring**: Removed config-provider command; renamed model-reset to
  config-reload with expanded functionality to clear reasoning effort; updated variable declarations
  for better scoping
• **Dependency updates**: Updated rustyline (17→18), sha2 (0.10→0.11), similar (2.4→3.0),
  toml_edit (0.22→0.25), posthog-rs (0.4.7→0.5.0); added hex, tiny_http, and regex crates
• **Comprehensive test coverage**: Added tests for stringified tool call arguments, symlink
  exclusion, Anthropic usage merging, Bedrock token counting, OAuth callback validation, and provider
  configuration
Diagram
flowchart LR
  A["main.rs<br/>Read ForgeConfig"] -->|"pass config"| B["ForgeAPI"]
  B -->|"pass config"| C["ForgeInfra"]
  B -->|"pass config"| D["ForgeServices"]
  D -->|"pass config"| E["ForgeApp"]
  D -->|"pass config"| F["ToolRegistry"]
  E -->|"pass config"| G["Orchestrator"]
  F -->|"pass config"| H["ToolExecutor"]
  B -->|"pass config"| I["ForgeRepo"]
  I -->|"inline providers"| J["ProviderRepository"]
  K["OAuth Callback<br/>LocalhostServer"] -->|"capture redirect"| L["Browser"]
  M["Tool Arguments<br/>Stringified JSON"] -->|"normalize"| N["NormalizeToolCallArguments"]
  O["Streaming Usage<br/>Anthropic"] -->|"merge max"| P["Usage Accumulation"]
  Q["Bedrock Tool IDs"] -->|"sanitize"| R["SanitizeToolIds"]
Loading

Grey Divider

File Changes

1. crates/forge_main/src/ui.rs ✨ Enhancement +155/-87

Config caching and OAuth localhost callback improvements

• Refactored UI struct to accept ForgeConfig as a constructor parameter and store it as a field,
 eliminating repeated calls to api.get_config()
• Updated API factory closure signature from Fn() -> A to Fn(ForgeConfig) -> A to pass config on
 each new conversation
• Modified OAuth code flow handling to support localhost callback servers with automatic browser
 redirect capture
• Enhanced provider/model selection flow with atomic set_default_provider_and_model() calls and
 improved user cancellation handling
• Added workspace initialization consent prompt before syncing directory contents
• Fixed /commit command to reinitialize state before processing

crates/forge_main/src/ui.rs


2. crates/forge_main/src/oauth_callback.rs ✨ Enhancement +526/-0

Localhost OAuth callback server implementation

• New module implementing LocalhostOAuthCallbackServer for capturing OAuth browser redirects on
 localhost
• Validates redirect URIs, binds to loopback addresses, and waits for authorization code with
 5-minute timeout
• Includes HTML success/error pages and comprehensive request validation (loopback check, GET-only,
 state validation)
• Full test coverage for valid/invalid callbacks, state mismatches, and edge cases

crates/forge_main/src/oauth_callback.rs


3. crates/forge_repo/src/provider/openai.rs ✨ Enhancement +369/-0

GitHub Copilot optimization headers support

• Added GitHub Copilot-specific optimization headers (x-initiator, Openai-Intent,
 Copilot-Vision-Request, anthropic-beta)
• Detects request initiator (user vs agent) and vision content presence
• Injects anthropic-beta header when Copilot proxies Claude models
• Includes 7 new tests validating header injection for different scenarios

crates/forge_repo/src/provider/openai.rs


View more (80)
4. crates/forge_services/src/app_config.rs ✨ Enhancement +77/-59

Config caching with mutex-based synchronization

• Changed ForgeAppConfigService to accept and cache ForgeConfig in a Mutex instead of calling
 infra.get_config() repeatedly
• Updated set_default_provider() and set_default_model() to synchronously update the cached
 config
• Added new set_default_provider_and_model() method for atomic provider+model updates
• Removed get_config() method from MockInfra test fixture

crates/forge_services/src/app_config.rs


5. crates/forge_services/src/attachment.rs ✨ Enhancement +27/-39

Max read lines parameter injection

• Changed ForgeChatRequest to accept max_read_lines as a constructor parameter instead of
 fetching from config
• Updated all test instantiations to pass max_read_lines: 2000
• Removed get_config() method from test mock infrastructure

crates/forge_services/src/attachment.rs


6. crates/forge_domain/tests/test_stringified_tool_calls.rs 🧪 Tests +313/-0

Stringified tool call arguments roundtrip tests

• New integration test file verifying handling of stringified tool call arguments from APIs like
 kimi-k2p5-turbo
• Tests roundtrip conversion: API sends "arguments": "{...}" (string), should serialize back as
 JSON object
• Includes 4 test cases covering basic stringified args, patch tool scenario, multiple tool calls,
 and regular JSON objects

crates/forge_domain/tests/test_stringified_tool_calls.rs


7. crates/forge_repo/src/provider/bedrock_sanitize_ids.rs ✨ Enhancement +269/-0

Bedrock tool ID sanitization transformer

• New transformer SanitizeToolIds that replaces invalid characters in Bedrock tool call IDs with
 underscores
• Bedrock requires IDs matching pattern ^[a-zA-Z0-9_-]+$; replaces dots, colons, and special chars
• Processes both ToolUse and ToolResult content blocks in messages
• Includes 4 tests validating sanitization of invalid IDs and preservation of valid ones

crates/forge_repo/src/provider/bedrock_sanitize_ids.rs


8. crates/forge_domain/src/tools/call/args.rs 🐞 Bug fix +143/-3

Stringified JSON argument parsing and normalization

• Enhanced ToolCallArguments deserialization to detect and parse stringified JSON (e.g.,
 "{\"key\": \"value\"}"), converting to Parsed variant
• Added normalize() method to convert Unparsed strings to structured JSON, with fallback to
 _raw_content object for malformed JSON
• Updated deserialization tests and added 3 new tests for stringified JSON roundtrips and
 normalization

crates/forge_domain/src/tools/call/args.rs


9. crates/forge_infra/src/auth/strategy.rs ✨ Enhancement +84/-50

OAuth credential enrichment refactoring

• Moved extract_chatgpt_account_id() function earlier in file for reuse by both
 OAuthCodeStrategy and CodexDeviceStrategy
• Added enrich_codex_oauth_credential() helper to extract account ID from id_token (preferred)
 or access_token
• Updated both OAuth strategies to use the new enrichment function for consistent Codex credential
 handling

crates/forge_infra/src/auth/strategy.rs


10. crates/forge_domain/src/result_stream_ext.rs 🐞 Bug fix +85/-9

Streaming usage merge strategy fix

• Changed usage accumulation strategy from accumulate() (sum) to merge() (max) for partial
 streaming events
• Fixes double-counting in Anthropic streaming where message_start includes output_tokens=1 and
 message_delta has cumulative total
• Updated cost-only accumulation to properly sum costs instead of replacing
• Renamed test and added new test for zero output token edge case

crates/forge_domain/src/result_stream_ext.rs


11. crates/forge_app/src/orch.rs ✨ Enhancement +5/-1

Orchestrator config passing and tool argument normalization

• Added config field to Orchestrator struct and updated constructor to accept ForgeConfig
• Passes config to services.call() method for tool execution
• Added NormalizeToolCallArguments transformer to the default transformation pipeline

crates/forge_app/src/orch.rs


12. crates/forge_main/src/lib.rs ✨ Enhancement +1/-0

OAuth callback module export

• Added oauth_callback module declaration to expose the new localhost OAuth callback server
 functionality

crates/forge_main/src/lib.rs


13. crates/forge_repo/src/provider/provider_repo.rs ✨ Enhancement +81/-23

Support inline provider definitions from ForgeConfig

• Added From implementations to convert forge_config provider types to domain types
• Added config_providers field to ForgeProviderRepository to support inline provider definitions
• Implemented get_config_provider_configs() method to convert config entries to provider configs
• Updated get_merged_configs() to merge inline providers from ForgeConfig alongside embedded and
 custom providers
• Updated all test instantiations to pass empty config_providers vector

crates/forge_repo/src/provider/provider_repo.rs


14. crates/forge_domain/src/transformer/normalize_tool_args.rs ✨ Enhancement +195/-0

Add tool call argument normalization transformer

• New transformer to normalize tool call arguments from Unparsed to Parsed JSON
• Repairs assistant tool calls persisted with stringified JSON arguments
• Includes comprehensive tests for stringified arguments, already-parsed arguments, and messages
 without tool calls

crates/forge_domain/src/transformer/normalize_tool_args.rs


15. crates/forge_config/src/config.rs ✨ Enhancement +99/-0

Add provider configuration types and inline provider support

• Added ProviderResponseType enum for wire protocol types (OpenAI, Anthropic, Bedrock, Google,
 OpenCode)
• Added ProviderTypeEntry enum for provider categories (Llm, ContextEngine)
• Added ProviderAuthMethod enum for authentication methods (ApiKey, GoogleAdc)
• Added ProviderUrlParam struct for URL parameter variables with optional preset options
• Added ProviderEntry struct for inline provider definitions in forge.toml
• Added max_commit_count field to ForgeConfig for commit message generation context
• Added providers field to ForgeConfig for merging custom provider definitions

crates/forge_config/src/config.rs


16. crates/forge_domain/src/message.rs ✨ Enhancement +96/-2

Add usage merge method for cumulative token counts

• Added merge() method to Usage struct using "last non-zero wins" strategy for cumulative token
 counts
• Enhanced documentation for accumulate() method to clarify it's for independent requests
• Added comprehensive tests for Anthropic cumulative usage merging and cost preservation

crates/forge_domain/src/message.rs


17. crates/forge_domain/src/provider.rs ✨ Enhancement +17/-0

Add GoogleAIStudio and AdaL provider support

• Added GOOGLE_AI_STUDIO and ADAL provider ID constants
• Updated built_in_providers() to include new providers
• Added display name mappings for google_ai_studio and adal
• Added FromStr implementations for new provider IDs
• Added tests for GoogleAIStudio display name and from_str conversion

crates/forge_domain/src/provider.rs


18. crates/forge_infra/src/env.rs ✨ Enhancement +24/-31

Refactor environment infra to use pre-read configuration

• Made to_environment() function public for external use
• Changed ForgeEnvironmentInfra::new() to accept pre-read ForgeConfig and seed cache
• Updated cached_config() to return Result and re-read from disk if cache invalidated
• Removed read_from_disk() static method and error handling for missing config
• Updated documentation to reflect pre-seeded cache behavior

crates/forge_infra/src/env.rs


19. crates/forge_services/src/fd.rs ✨ Enhancement +90/-0

Exclude symlinks from file discovery pipeline

• Added is_symlink() helper function to detect symlinks without following them
• Updated filter_and_resolve() to exclude symlinks from file discovery
• Added comprehensive tests for symlink exclusion, dangling symlinks, and directory symlinks

crates/forge_services/src/fd.rs


20. crates/forge_api/src/forge_api.rs ✨ Enhancement +29/-13

Pass pre-read configuration through ForgeAPI

• Added config field to ForgeAPI struct to store pre-read configuration
• Updated new() constructor to accept ForgeConfig parameter
• Modified init() to accept config and services_url parameters
• Updated app() method to pass config to ForgeApp constructor
• Updated commit_message() to pass config to GitApp constructor
• Removed get_config() method that delegated to infra

crates/forge_api/src/forge_api.rs


21. crates/forge_app/src/app.rs ✨ Enhancement +24/-14

Thread configuration through ForgeApp and tool registry

• Added config field to ForgeApp struct
• Updated new() constructor to accept and store ForgeConfig
• Updated ToolRegistry instantiation to pass config
• Changed conversation lookup to use ? operator for error propagation
• Updated get_merged_configs() call to pass max_parallel_file_reads parameter
• Updated Orchestrator instantiation to pass config parameter
• Replaced services.get_config() calls with self.config.clone()

crates/forge_app/src/app.rs


22. crates/forge_app/src/changed_files.rs ✨ Enhancement +10/-14

Pass parallel file reads as parameter instead of fetching config

• Added parallel_file_reads parameter to update_file_stats() method
• Removed get_config() call from method body
• Updated all test calls to pass explicit parallel_file_reads value

crates/forge_app/src/changed_files.rs


23. crates/forge_infra/src/forge_infra.rs ✨ Enhancement +26/-15

Refactor ForgeInfra to accept pre-read configuration

• Updated new() constructor to accept config and services_url parameters
• Removed config reading from constructor; now uses pre-read config
• Added public config() method to access cached configuration with error handling
• Updated documentation to reflect pre-read configuration pattern

crates/forge_infra/src/forge_infra.rs


24. crates/forge_services/src/forge_services.rs ✨ Enhancement +24/-14

Thread configuration through service layer constructors

• Updated new() constructor to accept ForgeConfig parameter
• Passed config values to service constructors instead of having them read config internally
• Updated ForgeChatRequest, ForgeAuthService, ForgeAppConfigService, ForgeFsRead,
 ForgeImageRead, ForgeAgentRegistryService, and ForgeWorkspaceService instantiations with
 config parameters

crates/forge_services/src/forge_services.rs


25. crates/forge_app/src/git_app.rs ✨ Enhancement +15/-25

Add configuration support to GitApp for commit context

• Added config field to GitApp struct
• Updated new() constructor to accept ForgeConfig parameter
• Updated fetch_git_context() to use max_commit_count from config
• Replaced services.get_config() call with self.config reference
• Updated imports to use Services trait

crates/forge_app/src/git_app.rs


26. crates/forge_services/src/tool_services/fs_read.rs ✨ Enhancement +28/-11

Store file read limits as ForgeFsRead fields

• Refactored ForgeFsRead to store config values as fields instead of fetching from infra
• Added max_file_size_bytes, max_image_size_bytes, max_read_lines, and max_line_chars fields
• Updated new() constructor to accept these parameters
• Replaced all self.0.get_config() calls with direct field access

crates/forge_services/src/tool_services/fs_read.rs


27. crates/forge_app/src/tool_executor.rs ✨ Enhancement +9/-9

Add configuration field to ToolExecutor

• Added config field to ToolExecutor struct
• Updated new() constructor to accept ForgeConfig parameter
• Replaced services.get_config() calls with self.config field access

crates/forge_app/src/tool_executor.rs


28. crates/forge_main/src/main.rs ✨ Enhancement +29/-3

Read and validate configuration at application startup

• Refactored main() to call new run() async function with error handling
• Added configuration reading at startup with error context
• Added services URL validation at startup
• Updated ForgeAPI::init() call to pass pre-read config and validated services URL
• Updated UI::init() to accept config and pass it to ForgeAPI factory

crates/forge_main/src/main.rs


29. crates/forge_walker/src/walker.rs ✨ Enhancement +65/-0

Skip symlinks in file walker traversal

• Added symlink detection in walker loop to skip symlinks
• Added comprehensive tests for symlink exclusion, dangling symlinks, and symlink-to-directory cases

crates/forge_walker/src/walker.rs


30. crates/forge_repo/src/provider/bedrock.rs 🐞 Bug fix +8/-2

Fix Bedrock token counting and add tool ID sanitization

• Added SanitizeToolIds transformer to bedrock input pipeline
• Fixed prompt token calculation to use input_tokens instead of total_tokens
• Updated test fixtures to include initiator field in Context
• Updated test expectations for prompt token counts

crates/forge_repo/src/provider/bedrock.rs


31. crates/forge_app/src/tool_registry.rs ✨ Enhancement +8/-6

Add configuration field to ToolRegistry

• Added config field to ToolRegistry struct
• Updated new() constructor to accept ForgeConfig parameter
• Passed config to ToolExecutor and AgentExecutor constructors
• Replaced services.get_config() calls with self.config field access

crates/forge_app/src/tool_registry.rs


32. crates/forge_app/src/agent_executor.rs ✨ Enhancement +10/-4

Add configuration and initiator tracking to AgentExecutor

• Added config field to AgentExecutor struct
• Updated new() constructor to accept ForgeConfig parameter
• Added initiator field to context when spawning agent conversations
• Updated ForgeApp instantiation to pass config

crates/forge_app/src/agent_executor.rs


33. crates/forge_domain/src/context.rs ✨ Enhancement +19/-0

Add initiator tracking and token count comparison

• Added initiator field to Context struct to track conversation origin ("user" or "agent")
• Added max() method to TokenCount for comparing and selecting larger values
• Updated serialization to skip empty initiator values

crates/forge_domain/src/context.rs


34. crates/forge_api/src/api.rs ✨ Enhancement +9/-4

Remove get_config from API trait and add atomic provider/model setter

• Removed get_config() method from API trait
• Added set_default_provider_and_model() method to atomically update provider and model together

crates/forge_api/src/api.rs


35. crates/forge_services/src/agent_registry.rs ✨ Enhancement +11/-3

Store configuration in agent registry service

• Added config field to ForgeAgentRegistryService to store startup configuration
• Updated new() constructor to accept ForgeConfig parameter
• Updated load_agents() to use stored config instead of fetching from repository

crates/forge_services/src/agent_registry.rs


36. crates/forge_app/src/orch_spec/orch_runner.rs ✨ Enhancement +14/-7

Pass configuration to orchestrator and agent service

• Updated Orchestrator::new() call to pass setup.config parameter
• Updated AgentService::call_tool() signature to accept config parameter

crates/forge_app/src/orch_spec/orch_runner.rs


37. crates/forge_app/src/infra.rs 📝 Documentation +1/-10

Update EnvironmentInfra trait documentation

• Updated EnvironmentInfra trait documentation to clarify config storage
• Removed reference to get_config() method in trait documentation

crates/forge_app/src/infra.rs


38. crates/forge_services/src/tool_services/image_read.rs ✨ Enhancement +9/-7

Store image size limit as ForgeImageRead field

• Refactored ForgeImageRead to store max_image_size_bytes as field
• Updated new() constructor to accept max_image_size_bytes parameter
• Replaced self.0.get_config() call with direct field access

crates/forge_services/src/tool_services/image_read.rs


39. crates/forge_services/src/context_engine.rs ✨ Enhancement +6/-4

Store file read batch size in workspace service

• Added max_file_read_batch_size field to ForgeWorkspaceService
• Updated new() constructor to accept max_file_read_batch_size parameter
• Updated clone() implementation to include new field
• Replaced self.infra.get_config() calls with direct field access

crates/forge_services/src/context_engine.rs


40. crates/forge_repo/src/forge_repo.rs ✨ Enhancement +10/-7

Pass configuration to repository constructors

• Updated new() constructor to accept ForgeConfig parameter
• Passed config.providers to ForgeProviderRepository constructor
• Passed retry config and cache TTL to ForgeChatRepository constructor

crates/forge_repo/src/forge_repo.rs


41. crates/forge_app/src/dto/anthropic/response.rs 🧪 Tests +8/-6

Update Anthropic usage test to use merge semantics

• Updated test to use merge() instead of accumulate() for combining usage events
• Updated test expectations to reflect max-based merging instead of sum-based accumulation

crates/forge_app/src/dto/anthropic/response.rs


42. crates/forge_app/src/services.rs ✨ Enhancement +18/-0

Add atomic provider and model setter to AppConfigService

• Added set_default_provider_and_model() method to AppConfigService trait
• Implemented method in trait implementation to delegate to config service

crates/forge_app/src/services.rs


43. crates/forge_app/src/dto/openai/request.rs ✨ Enhancement +5/-0

Add initiator field to OpenAI request DTO

• Added initiator field to Request struct for tracking conversation origin
• Marked field with #[serde(skip_serializing)] to exclude from API requests
• Updated From<Context> implementation to populate initiator field

crates/forge_app/src/dto/openai/request.rs


44. crates/forge_services/src/auth.rs ✨ Enhancement +5/-7

Store services URL in auth service

• Added services_url field to ForgeAuthService struct
• Updated new() constructor to accept services_url parameter
• Replaced self.infra.get_config().services_url calls with self.services_url field

crates/forge_services/src/auth.rs


45. crates/forge_app/src/agent.rs ✨ Enhancement +3/-1

Add config parameter to AgentService trait

• Updated AgentService::call_tool() signature to accept config parameter
• Updated implementation to pass config to ToolRegistry constructor

crates/forge_app/src/agent.rs


46. crates/forge_repo/src/provider/chat.rs ✨ Enhancement +10/-4

Pass retry and cache config to chat repository

• Updated new() constructor to accept retry_config and model_cache_ttl_secs parameters
• Removed config reading from constructor body
• Replaced config.retry and config.model_cache_ttl_secs with constructor parameters

crates/forge_repo/src/provider/chat.rs


47. crates/forge_app/src/command_generator.rs 🧪 Tests +8/-7

Update command generator test mocks

• Removed get_config() method from mock infrastructure
• Added set_default_provider_and_model() method to mock service

crates/forge_app/src/command_generator.rs


48. shell-plugin/lib/actions/config.zsh ✨ Enhancement +15/-32

Refactor shell plugin config actions and session management

• Removed _forge_action_provider() function for provider selection
• Updated _forge_action_sync() and _forge_action_sync_init() to use _forge_exec_interactive
• Renamed _forge_action_model_reset() to _forge_action_config_reload() with expanded
 functionality
• Updated config reload to clear _FORGE_SESSION_REASONING_EFFORT in addition to model and provider

shell-plugin/lib/actions/config.zsh


49. shell-plugin/lib/config.zsh ✨ Enhancement +19/-17

Update shell plugin variable declarations and add reasoning effort

• Changed typeset -gh (global + hidden) to typeset -h (hidden only) for plugin variables
• Added _FORGE_SESSION_REASONING_EFFORT variable for session-scoped reasoning effort override

shell-plugin/lib/config.zsh


50. shell-plugin/forge.theme.zsh ✨ Enhancement +3/-5

Simplify zsh theme variable declarations

• Removed emulate -R zsh wrapper around setopt PROMPT_SUBST
• Removed typeset -g from RPROMPT assignment
• Added stderr redirection to suppress errors in prompt info function

shell-plugin/forge.theme.zsh


51. shell-plugin/lib/dispatcher.zsh ✨ Enhancement +3/-6

Update shell plugin command dispatcher

• Removed config-provider command handler
• Renamed model-reset to config-reload with alias cr
• Added provider as alias for provider-login command

shell-plugin/lib/dispatcher.zsh


52. forge.schema.json 📝 Documentation +147/-0

Add provider configuration schema definitions

• Added max_commit_count field schema for commit message generation context limit
• Added providers field schema for inline provider definitions
• Added schema definitions for ProviderEntry, ProviderResponseType, ProviderTypeEntry,
 ProviderAuthMethod, and ProviderUrlParam

forge.schema.json


53. Cargo.toml Dependencies +6/-4

Update dependencies and add new crates

• Updated rustyline from 17.0.2 to 18.0.0
• Updated sha2 from 0.10 to 0.11
• Updated similar from 2.4 to 3.0
• Updated toml_edit from 0.22 to 0.25
• Updated posthog-rs to 0.5.0
• Added hex crate 0.4.3 for hex encoding
• Added tiny_http crate 0.12.0

Cargo.toml


54. crates/forge_infra/src/auth/util.rs ✨ Enhancement +2/-0

Add id_token field to OAuth token responses

• Added id_token: None field to OAuth token response in into_domain() function
• Added id_token: None field to token response in build_token_response() function

crates/forge_infra/src/auth/util.rs


55. crates/forge_repo/src/conversation/conversation_record.rs ✨ Enhancement +4/-0

Persist initiator field in conversation records

• Added initiator field to ContextRecord struct
• Updated From<&Context> implementation to serialize initiator
• Updated TryFrom<ContextRecord> implementation to deserialize initiator

crates/forge_repo/src/conversation/conversation_record.rs


56. crates/forge_repo/src/skill.rs 🧪 Tests +8/-1

Update skill repository test fixture

• Updated test fixture to pass config and services_url to ForgeInfra::new()

crates/forge_repo/src/skill.rs


57. crates/forge_app/src/dto/anthropic/transforms/set_cache.rs 🧪 Tests +2/-0

Add initiator field to Anthropic test fixtures

• Added initiator: None field to test context fixtures

crates/forge_app/src/dto/anthropic/transforms/set_cache.rs


58. crates/forge_domain/src/transformer/mod.rs ✨ Enhancement +2/-0

Export normalize tool arguments transformer

• Added normalize_tool_args module import
• Re-exported NormalizeToolCallArguments transformer

crates/forge_domain/src/transformer/mod.rs


59. crates/forge_repo/src/provider/bedrock_cache.rs 🧪 Tests +3/-0

Add initiator field to Bedrock cache test fixtures

• Added initiator: None field to test context fixtures

crates/forge_repo/src/provider/bedrock_cache.rs


60. crates/forge_domain/src/auth/auth_token_response.rs ✨ Enhancement +4/-0

Add id_token field to OAuth token response

• Added id_token field to OAuthTokenResponse struct for OpenID Connect support

crates/forge_domain/src/auth/auth_token_response.rs


61. crates/forge_repo/src/provider/openai_responses/response.rs 📝 Documentation +4/-0

Document OpenAI Responses API usage timing

• Added documentation comment explaining usage event timing in OpenAI Responses API

crates/forge_repo/src/provider/openai_responses/response.rs


62. crates/forge_fs/src/lib.rs ✨ Enhancement +1/-1

Use hex crate for hash encoding

• Replaced manual hex formatting with hex::encode() function for SHA256 hash

crates/forge_fs/src/lib.rs


63. crates/forge_app/src/dto/openai/transformers/drop_tool_call.rs 🧪 Tests +1/-0

Add initiator field to OpenAI test fixture

• Added initiator: None field to test context fixture

crates/forge_app/src/dto/openai/transformers/drop_tool_call.rs


64. crates/forge_app/src/dto/openai/transformers/set_cache.rs 🧪 Tests +1/-0

Add initiator field to OpenAI cache test fixture

• Added initiator: None field to test context fixture

crates/forge_app/src/dto/openai/transformers/set_cache.rs


65. crates/forge_app/src/hooks/title_generation.rs 🧪 Tests +1/-0

Add config parameter to title generation test mock

• Added config parameter to AgentService::call_tool() mock implementation

crates/forge_app/src/hooks/title_generation.rs


66. crates/forge_app/src/utils.rs ✨ Enhancement +1/-1

Use hex crate for hash encoding

• Replaced manual hex formatting with hex::encode() function for SHA256 hash

crates/forge_app/src/utils.rs


67. crates/forge_app/src/dto/anthropic/transforms/auth_system_message.rs 🧪 Tests +1/-0

Add initiator field to Anthropic auth test fixture

• Added initiator: None field to test context fixture

crates/forge_app/src/dto/anthropic/transforms/auth_system_message.rs


68. crates/forge_repo/src/provider/mod.rs ✨ Enhancement +1/-0

Import Bedrock tool ID sanitization module

• Added bedrock_sanitize_ids module import

crates/forge_repo/src/provider/mod.rs


69. crates/forge_main/src/zsh/plugin.rs ✨ Enhancement +2/-6

Simplify zsh plugin variable declarations

• Removed typeset -g from _FORGE_PLUGIN_LOADED variable assignment
• Removed typeset -g from _FORGE_THEME_LOADED variable assignment

crates/forge_main/src/zsh/plugin.rs


70. crates/forge_main/src/built_in_commands.json ✨ Enhancement +3/-7

Update built-in commands list

• Removed config-provider command entry
• Renamed model-reset to config-reload with updated description
• Updated provider-login description to include provider alias

crates/forge_main/src/built_in_commands.json


71. crates/forge_main/Cargo.toml Dependencies +1/-0

Add tiny_http dependency to main crate

• Added tiny_http workspace dependency

crates/forge_main/Cargo.toml


72. crates/forge_tracker/Cargo.toml Dependencies +1/-1

Update posthog-rs dependency

• Updated posthog-rs from 0.4.7 to 0.5.0

crates/forge_tracker/Cargo.toml


73. crates/forge_fs/Cargo.toml Dependencies +1/-0

Add hex dependency to fs crate

• Added hex workspace dependency

crates/forge_fs/Cargo.toml


74. crates/forge_config/.forge.toml ⚙️ Configuration changes +1/-0

Add max commit count to default config

• Added max_commit_count = 20 configuration value

crates/forge_config/.forge.toml


75. crates/forge_repo/Cargo.toml Dependencies +1/-0

Add regex dependency to provider repository

• Added regex workspace dependency

crates/forge_repo/Cargo.toml


76. crates/forge_app/Cargo.toml Dependencies +1/-0

Add hex dependency to app crate

• Added hex workspace dependency

crates/forge_app/Cargo.toml


77. .gitattributes ⚙️ Configuration changes +1/-0

Configure line endings for zsh files

• Added .zsh file line ending configuration to use LF

.gitattributes


78. .github/ISSUE_TEMPLATE/bug_report.yml 📝 Documentation +4/-31

Simplify bug report issue template

• Removed "Error Logs" textarea field from bug report template
• Removed "Operating System" dropdown field
• Consolidated OS and version into single "Operating System & Version" input field

.github/ISSUE_TEMPLATE/bug_report.yml


79. crates/forge_repo/src/provider/provider.json ✨ Enhancement +666/-271

Add AdaL provider and refresh Bedrock/AI models catalog

• Replaced outdated Amazon Bedrock model entries with 100+ new models across multiple providers
 (Google, Qwen, Mistral, Meta, DeepSeek, Anthropic, NVIDIA, MiniMax, OpenAI, Writer, Zai)
• Updated model metadata including context lengths, tool support, parallel tool call capabilities,
 and input modalities (text, image, reasoning)
• Added new google_ai_studio provider entry with 7 Gemini models (3.1 Pro/Flash, 3 Pro/Flash, 2.5
 Pro/Flash, 2.0 Flash)
• Added OAuth code authentication method to OpenAI provider configuration
• Added adal provider (SylphAI) as a built-in provider with OpenAI-compatible response type and
 dynamic model fetching from https://api.sylph.ai/v1/models

crates/forge_repo/src/provider/provider.json


80. plans/2026-04-05-config-init-at-startup-v1.md 📝 Documentation +193/-0

Design config initialization refactor for error surfacing

• Comprehensive design document for refactoring config initialization from lazy (with silent errors)
 to eager (with error surfacing) at application startup
• Proposes reading ForgeConfig once in main.rs and threading it through the construction chain
 to eliminate silent failures
• Details 7-phase implementation plan including config reader fixes, propagation through infra
 layers, handling config freshness after updates, and trait removal
• Includes architecture diagrams, key invariants, verification criteria, risk mitigations, and
 alternative approaches

plans/2026-04-05-config-init-at-startup-v1.md


81. crates/forge_services/src/discovery.rs Additional files +0/-7

...

crates/forge_services/src/discovery.rs


82. crates/forge_services/src/tool_services/shell.rs Additional files +0/-7

...

crates/forge_services/src/tool_services/shell.rs


83. shell-plugin/lib/highlight.zsh Additional files +0/-8

...

shell-plugin/lib/highlight.zsh


Grey Divider

Qodo Logo

@qodo-code-review
Copy link
Copy Markdown

qodo-code-review Bot commented Apr 6, 2026

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (3) 📎 Requirement gaps (0) 🎨 UX Issues (0)

Grey Divider


Action required

1. ADAL const missing rustdoc 📘 Rule violation ⚙ Maintainability
Description
New public constants ProviderId::GOOGLE_AI_STUDIO and ProviderId::ADAL were added without ///
Rustdoc comments. This violates the requirement that all public Rust items must be documented.
Code

crates/forge_domain/src/provider.rs[R75-76]

+    pub const GOOGLE_AI_STUDIO: ProviderId = ProviderId(Cow::Borrowed("google_ai_studio"));
+    pub const ADAL: ProviderId = ProviderId(Cow::Borrowed("adal"));
Evidence
PR Compliance ID 4 requires Rustdoc comments for all public items. The added pub const items at
the cited lines have no preceding /// documentation.

AGENTS.md
crates/forge_domain/src/provider.rs[72-76]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
New public constants were added without required Rustdoc comments.
## Issue Context
Compliance requires `///` docs for all public Rust items (no code examples).
## Fix Focus Areas
- crates/forge_domain/src/provider.rs[72-76]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Integration tests not co-located 📘 Rule violation ⚙ Maintainability
Description
New tests were added under crates/forge_domain/tests/ instead of being co-located with the source
module via #[cfg(test)] mod tests. This violates the requirement to keep tests in the same file as
the code under test.
Code

crates/forge_domain/tests/test_stringified_tool_calls.rs[R1-21]

+//! Integration test for stringified tool call arguments fix
+//!
+//! This test verifies that when the API sends tool call arguments as a string
+//! containing JSON (like kimi-k2p5-turbo does), we properly parse it and
+//! serialize it back as a JSON object when sending to the API.
+
+use forge_domain::{Context, ContextMessage, Role};
+
+/// Test that stringified tool call arguments from API are properly handled
+///
+/// This simulates the exact scenario: API sends arguments as a string
+/// containing JSON, and when we serialize the conversation back to send to API,
+/// it should be a proper object.
+#[test]
+fn test_stringified_tool_call_arguments_roundtrip() {
+    // Simulate what kimi-k2p5-turbo sends: arguments as a string containing JSON
+    // Note: This is what the API sends us - a JSON string value containing JSON
+    // object
+    let conversation_json = r#"{
+        "messages": [
+            {
Evidence
PR Compliance ID 3 requires tests to live in the same Rust file as the source code under test. This
PR introduces a new separate test file containing #[test] functions.

AGENTS.md
crates/forge_domain/tests/test_stringified_tool_calls.rs[1-21]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Tests were added as a separate file under `crates/forge_domain/tests/`, but compliance requires unit tests to be co-located with the implementation.
## Issue Context
Refactor these tests into the relevant module file using `#[cfg(test)] mod tests { ... }`.
## Fix Focus Areas
- crates/forge_domain/tests/test_stringified_tool_calls.rs[1-313]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Legacy config lost on update 🐞 Bug ≡ Correctness
Description
ForgeEnvironmentInfra::update_environment rebuilds the writable config from defaults+global only and
writes it back, so any settings that exist only in the legacy ~/.forge/.config.json source will be
silently dropped when a config operation is applied. After cache invalidation, cached_config also
re-reads without legacy, making the session’s resolved config inconsistent with ForgeConfig::read().
Code

crates/forge_infra/src/env.rs[R151-154]

       let mut fc = ConfigReader::default()
Evidence
ForgeConfig::read() explicitly includes legacy config via ConfigReader::read_legacy(), but
ForgeEnvironmentInfra’s cached_config() and update_environment() re-read config without calling
read_legacy(), then update_environment writes that reduced view back to disk, causing data loss for
legacy-only settings.

crates/forge_infra/src/env.rs[118-130]
crates/forge_infra/src/env.rs[149-166]
crates/forge_config/src/config.rs[270-285]
crates/forge_config/src/reader.rs[114-123]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`ForgeEnvironmentInfra::cached_config()` and `ForgeEnvironmentInfra::update_environment()` re-read config without `read_legacy()`, even though `ForgeConfig::read()` includes it. This can drop legacy-only settings (from `~/.forge/.config.json`) when any config update is persisted.
### Issue Context
- `ForgeConfig::read()` uses `ConfigReader::read_legacy()`.
- `update_environment()` writes a new global config derived from defaults+global only.
### Fix Focus Areas
- crates/forge_infra/src/env.rs[118-130]
- crates/forge_infra/src/env.rs[149-166]
### Expected fix
- Prefer calling `ForgeConfig::read()` for the base config used by `cached_config()` when cache is empty.
- In `update_environment()`, build the base config using the same merged sources as `ForgeConfig::read()` (at least `read_legacy()+read_defaults()+read_global()`; include `read_env()` if you intend env overrides to participate in the computed writeback), so applying an operation does not discard legacy-only fields.
- Add/adjust a regression test that starts with a config coming only from legacy source, runs `update_environment`, and asserts unrelated legacy-derived fields are preserved.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


View more (2)
4. Config errors masked on /new 🐞 Bug ☼ Reliability
Description
UI::on_new uses ForgeConfig::read().unwrap_or_default(), so a malformed/invalid config silently
falls back to defaults after startup, undermining the PR’s “fail fast” config validation and causing
unexpected runtime behavior. This also makes debugging configuration problems much harder because
the error is discarded.
Code

crates/forge_main/src/ui.rs[R160-162]

+        let config = forge_config::ForgeConfig::read().unwrap_or_default();
+        self.config = config.clone();
+        self.api = Arc::new((self.new_api)(config));
Evidence
Startup now surfaces config read errors explicitly, but /new discards any read error and replaces
the active config/API with defaults, creating inconsistent behavior between initial startup and
subsequent /new resets.

crates/forge_main/src/ui.rs[159-163]
crates/forge_main/src/main.rs[104-114]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
`UI::on_new()` calls `ForgeConfig::read().unwrap_or_default()`, swallowing config read/parse errors and replacing config with defaults.
### Issue Context
The PR explicitly adds startup-time validation to avoid silent fallback; `/new` should not reintroduce silent fallback.
### Fix Focus Areas
- crates/forge_main/src/ui.rs[159-163]
### Expected fix
- Replace `unwrap_or_default()` with error propagation (e.g., `ForgeConfig::read()?` plus a helpful `context(...)`).
- Ensure the UI prints a clear error and keeps the existing API/config if the reload fails (instead of resetting to defaults).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


5. services_url never refreshes 🐞 Bug ≡ Correctness
Description
main.rs parses services_url once from the initial config and captures it in the UI API-factory
closure, so later config changes to services_url (followed by /new) cannot take effect because new
API instances still use the originally captured URL. This can route requests to the wrong workspace
server despite the user updating configuration.
Code

crates/forge_main/src/main.rs[R111-118]

+    let services_url: Url = config
+        .services_url
+        .parse()
+        .context("services_url in configuration must be a valid URL")?;
+
   // Handle worktree creation if specified
   let cwd: PathBuf = match (&cli.sandbox, &cli.directory) {
       (Some(sandbox), Some(cli)) => {
Evidence
services_url is derived from the initial config and then reused inside the closure passed to
UI::init, even though the closure receives a (potentially newer) ForgeConfig. UI::on_new reads
config again and re-invokes the factory, but the factory ignores the new config’s services_url
because it uses the captured Url value.

crates/forge_main/src/main.rs[111-133]
crates/forge_main/src/ui.rs[159-163]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The API factory closure captures `services_url` computed from the initial config, so subsequent `ForgeConfig` reloads (e.g. `/new`) cannot change which gRPC workspace server is used.
### Issue Context
`UI::init`’s factory closure receives a `ForgeConfig` argument explicitly to reflect config changes in new conversations.
### Fix Focus Areas
- crates/forge_main/src/main.rs[104-133]
### Expected fix
- Compute/validate `services_url` *inside* the factory closure from the passed-in `config.services_url` (and return an error instead of capturing a stale value).
- If the closure cannot return `Result`, consider changing `ForgeAPI::init` to accept only `ForgeConfig` and perform parsing/validation internally, or change the UI factory signature to return `Result<ForgeAPI>` so invalid URLs are surfaced without panics/silent fallback.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

6. assert_eq! lacks pretty_assertions 📘 Rule violation ⚙ Maintainability
Description
The newly added tests use assert_eq! without importing and using pretty_assertions for equality
comparisons. This violates the required Rust test assertion convention.
Code

crates/forge_domain/tests/test_stringified_tool_calls.rs[R67-75]

+    assert_eq!(tool_calls.len(), 1);
+
+    let tool_call = &tool_calls[0];
+    assert_eq!(tool_call.name.as_str(), "read");
+
+    // Verify arguments are parsed correctly (can access fields)
+    let parsed_args = tool_call.arguments.parse().expect("Should parse arguments");
+    assert_eq!(parsed_args["file_path"], "/test/path");
+    assert_eq!(parsed_args["start_line"], 1);
Evidence
PR Compliance ID 2 requires pretty_assertions for equality assertions. The new test file does not
import pretty_assertions::assert_eq but uses assert_eq! repeatedly.

AGENTS.md
crates/forge_domain/tests/test_stringified_tool_calls.rs[7-8]
crates/forge_domain/tests/test_stringified_tool_calls.rs[67-75]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
Equality assertions in the new tests are not using `pretty_assertions`.
## Issue Context
Compliance requires `pretty_assertions` to be used for equality comparisons to improve diff output.
## Fix Focus Areas
- crates/forge_domain/tests/test_stringified_tool_calls.rs[7-8]
- crates/forge_domain/tests/test_stringified_tool_calls.rs[67-75]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

ⓘ The new review experience is currently in Beta. Learn more

Grey Divider

Qodo Logo

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors configuration management to read settings at startup and propagate them through the system, eliminating lazy disk reads and silent failures. It introduces a localhost OAuth callback server for streamlined authentication and adds support for Google AI Studio and AdaL providers. Key improvements include normalizing stringified tool call arguments and sanitizing tool call IDs for Bedrock compatibility. Feedback highlighted that the JWT claim extraction for ChatGPT account IDs lacks signature verification and should be documented as an insecure method.

Comment on lines +65 to +93
fn extract_chatgpt_account_id(token: &str) -> Option<String> {
let parts: Vec<&str> = token.split('.').collect();
if parts.len() != 3 {
return None;
}
use base64::Engine;
let payload = base64::engine::general_purpose::URL_SAFE_NO_PAD
.decode(parts[1])
.ok()?;
let claims: serde_json::Value = serde_json::from_slice(&payload).ok()?;

// Try chatgpt_account_id first
if let Some(id) = claims["chatgpt_account_id"].as_str() {
return Some(id.to_string());
}
// Try nested auth claim
if let Some(id) = claims["https://api.openai.com/auth"]["chatgpt_account_id"].as_str() {
return Some(id.to_string());
}
// Fall back to organizations[0].id
if let Some(id) = claims["organizations"]
.as_array()
.and_then(|orgs| orgs.first())
.and_then(|org| org["id"].as_str())
{
return Some(id.to_string());
}
None
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The extract_chatgpt_account_id function performs base64 decoding and JSON parsing on a token without verifying the token's signature. While this is acceptable for extracting claims for non-security purposes, it should be clearly documented that this is an insecure extraction method.

Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR introduces AdaL (SylphAI) as a built-in provider and expands Forge’s provider/config system, alongside a broader refactor to read config eagerly at startup (and thread config through services) plus several related UX, provider, and tooling improvements.

Changes:

  • Added built-in provider IDs (incl. adal) and expanded provider configuration support (inline providers in forge.toml, schema updates).
  • Refactored config access to avoid EnvironmentInfra::get_config() and instead pass startup config into repos/services/tools; added config reload behavior in the shell plugin/UI.
  • Added OAuth localhost callback helper, symlink exclusion in discovery/walker paths, and several provider/request/usage normalization fixes (tool args, streaming usage merge, Bedrock tool id sanitization).

Reviewed changes

Copilot reviewed 83 out of 84 changed files in this pull request and generated 9 comments.

Show a summary per file
File Description
shell-plugin/lib/highlight.zsh Adjusts zsh-syntax-highlighting pattern setup
shell-plugin/lib/dispatcher.zsh Updates command dispatch aliases (config reload/provider login)
shell-plugin/lib/config.zsh Changes plugin variable scoping/visibility and adds session reasoning effort var
shell-plugin/lib/actions/config.zsh Removes provider picker action; adds config reload action; uses interactive exec for workspace ops
shell-plugin/forge.theme.zsh Simplifies prompt subst; suppresses prompt command stderr
plans/2026-04-05-config-init-at-startup-v1.md Design plan for eager config read / trait changes
forge.schema.json Adds schema for max_commit_count and inline providers
crates/forge_walker/src/walker.rs Skips symlinks during walk + adds tests
crates/forge_tracker/Cargo.toml Bumps posthog-rs dependency
crates/forge_services/src/tool_services/shell.rs Removes test mock get_config implementation
crates/forge_services/src/tool_services/image_read.rs Injects max image size via constructor instead of config lookup
crates/forge_services/src/tool_services/fs_read.rs Injects file/image/line limits via constructor instead of config lookup
crates/forge_services/src/forge_services.rs Threads config into services; removes EnvironmentInfra bound; config-driven construction
crates/forge_services/src/fd.rs Excludes symlinks from discovered files + adds tests
crates/forge_services/src/discovery.rs Removes test mock get_config implementation
crates/forge_services/src/context_engine.rs Injects batch size instead of reading config via infra
crates/forge_services/src/auth.rs Injects services_url instead of reading config via infra
crates/forge_services/src/attachment.rs Injects max_read_lines instead of reading config via infra
crates/forge_services/src/app_config.rs Stores/updates config in-memory mutex; adds atomic provider+model setter
crates/forge_services/src/agent_registry.rs Uses injected startup config for resolving defaults when loading agents
crates/forge_repo/src/skill.rs Updates tests to new ForgeInfra constructor
crates/forge_repo/src/provider/provider_repo.rs Merges inline providers; adds conversions from config provider entries
crates/forge_repo/src/provider/openai.rs Adds GitHub Copilot optimization headers + tests
crates/forge_repo/src/provider/openai_responses/response.rs Adds doc comment re: usage conversion
crates/forge_repo/src/provider/mod.rs Adds bedrock tool-id sanitization module
crates/forge_repo/src/provider/chat.rs Injects retry config + model cache TTL instead of reading config
crates/forge_repo/src/provider/bedrock.rs Adds tool-id sanitization; fixes usage mapping; updates tests for initiator field
crates/forge_repo/src/provider/bedrock_sanitize_ids.rs New transformer to sanitize Bedrock tool call IDs + tests
crates/forge_repo/src/provider/bedrock_cache.rs Updates tests for initiator field
crates/forge_repo/src/forge_repo.rs Threads config into repo; removes get_config delegation
crates/forge_repo/src/conversation/conversation_record.rs Persists initiator in context records
crates/forge_repo/Cargo.toml Adds regex dependency
crates/forge_main/src/zsh/plugin.rs Removes typeset -g when setting loaded markers
crates/forge_main/src/ui.rs Threads config into UI factory; refreshes config on /new; OAuth callback support; provider/model selection tweaks
crates/forge_main/src/oauth_callback.rs New localhost OAuth callback server helper
crates/forge_main/src/main.rs Eager config read + services_url validation; improved error printing
crates/forge_main/src/lib.rs Exposes oauth_callback module
crates/forge_main/src/built_in_commands.json Removes config-provider; replaces model-reset with config-reload; provider alias updates
crates/forge_main/Cargo.toml Adds tiny_http dependency
crates/forge_infra/src/forge_infra.rs New ForgeInfra constructor taking config + services_url; removes trait get_config
crates/forge_infra/src/env.rs Seeds config cache at construction; makes cached_config fallible; removes silent defaulting
crates/forge_infra/src/auth/util.rs Adds id_token field to token responses
crates/forge_infra/src/auth/strategy.rs Adds id_token support + Codex credential enrichment refactor
crates/forge_fs/src/lib.rs Uses hex::encode for hashing
crates/forge_fs/Cargo.toml Adds hex dependency
crates/forge_domain/tests/test_stringified_tool_calls.rs New integration tests for stringified tool args roundtrip
crates/forge_domain/src/transformer/normalize_tool_args.rs New transformer to normalize tool call args in context
crates/forge_domain/src/transformer/mod.rs Exports NormalizeToolCallArguments
crates/forge_domain/src/tools/call/args.rs Repairs stringified JSON tool args; adds normalize() + tests
crates/forge_domain/src/result_stream_ext.rs Changes streaming usage aggregation strategy (merge vs accumulate)
crates/forge_domain/src/provider.rs Adds ProviderId constants (incl. ADAL, Google AI Studio) and mappings/tests
crates/forge_domain/src/message.rs Adds Usage::merge and related tests
crates/forge_domain/src/context.rs Adds initiator to Context; adds TokenCount::max helper
crates/forge_domain/src/auth/auth_token_response.rs Adds id_token field to OAuthTokenResponse
crates/forge_config/src/config.rs Adds provider entry types; adds max_commit_count + inline providers
crates/forge_config/.forge.toml Sets max_commit_count default in embedded defaults
crates/forge_app/src/utils.rs Uses hex::encode for hashing
crates/forge_app/src/tool_registry.rs Threads config into tool registry/executors; removes config lookups from services
crates/forge_app/src/tool_executor.rs Uses injected config instead of services.get_config()
crates/forge_app/src/services.rs Adds AppConfigService atomic provider+model setter
crates/forge_app/src/orch.rs Threads config into orchestrator; adds NormalizeToolCallArguments to pipeline
crates/forge_app/src/orch_spec/orch_runner.rs Updates orchestrator construction and AgentService signature
crates/forge_app/src/infra.rs Removes get_config() from EnvironmentInfra trait
crates/forge_app/src/hooks/title_generation.rs Updates AgentService signature in tests
crates/forge_app/src/git_app.rs Threads config into GitApp; uses max_commit_count
crates/forge_app/src/dto/openai/transformers/set_cache.rs Updates tests for initiator field
crates/forge_app/src/dto/openai/transformers/drop_tool_call.rs Updates tests for initiator field
crates/forge_app/src/dto/openai/request.rs Adds non-serialized initiator field to OpenAI request DTO
crates/forge_app/src/dto/anthropic/transforms/set_cache.rs Updates tests for initiator field
crates/forge_app/src/dto/anthropic/transforms/auth_system_message.rs Updates tests for initiator field
crates/forge_app/src/dto/anthropic/response.rs Switches test expectations to merge semantics for usage
crates/forge_app/src/command_generator.rs Removes test mock get_config; updates AppConfigService mock
crates/forge_app/src/changed_files.rs Passes parallel read limit explicitly rather than reading config
crates/forge_app/src/app.rs Threads config into ForgeApp; improves conversation-not-found handling
crates/forge_app/src/agent.rs Threads config through AgentService tool call execution path
crates/forge_app/src/agent_executor.rs Sets context initiator=agent for spawned agent conversations; threads config into ForgeApp
crates/forge_app/Cargo.toml Adds hex dependency
crates/forge_api/src/forge_api.rs Threads config through ForgeAPI init; removes get_config; adds atomic provider+model setter
crates/forge_api/src/api.rs Removes get_config from API trait; adds atomic provider+model setter
Cargo.toml Dependency upgrades/additions (rustyline/sha2/similar/toml_edit/tiny_http/hex)
Cargo.lock Lockfile updates for dependency changes
.github/ISSUE_TEMPLATE/bug_report.yml Simplifies OS field; removes logs textarea and OS dropdown
.gitattributes Forces LF for *.zsh files
Comments suppressed due to low confidence (2)

crates/forge_services/src/agent_registry.rs:95

  • ForgeAgentRegistryService stores a startup ForgeConfig snapshot and uses it in load_agents(). After a user changes the default provider/model (e.g. via set_default_provider_and_model), reload_agents() will still reload using the stale snapshot, so agents without explicit provider/model won’t inherit the updated session defaults. Consider storing shared, updatable config (e.g. Arc<Mutex<ForgeConfig>> shared with ForgeAppConfigService), or re-reading config from disk inside load_agents() / reload_agents() before calling repository.get_agents(...).
    shell-plugin/lib/highlight.zsh:17
  • Without the global typeset -gA ZSH_HIGHLIGHT_PATTERNS / typeset -ga ZSH_HIGHLIGHT_HIGHLIGHTERS declarations, sourcing this file before zsh-syntax-highlighting initializes its globals can create the variables with the wrong type (e.g. indexed array instead of associative), breaking highlighting. Consider restoring the global type declarations (ideally guarded so they don’t override an existing correctly-typed var).

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +112 to 119
async fn set_default_provider_and_model(
&self,
provider_id: ProviderId,
model: ModelId,
) -> anyhow::Result<()> {
self.update(ConfigOperation::SetModel(provider_id, model))
.await
}
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

set_default_provider_and_model persists the change via update_environment, but it never updates the in-memory self.config cache. After calling this method, get_default_provider() / get_provider_model() will continue returning the previous values until the process is restarted or /new reloads config. Update the cached ForgeConfig (session.provider_id + session.model_id) after the operation succeeds, similar to set_default_provider and set_default_model.

Copilot uses AI. Check for mistakes.
Comment on lines 159 to 163
async fn on_new(&mut self) -> Result<()> {
self.api = Arc::new((self.new_api)());
let config = forge_config::ForgeConfig::read().unwrap_or_default();
self.config = config.clone();
self.api = Arc::new((self.new_api)(config));
self.init_state(false).await?;
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

on_new re-reads config using ForgeConfig::read().unwrap_or_default(), which silently falls back to defaults on parse/deserialization errors. This undermines the new “surface config errors” behavior added in main.rs. Prefer propagating the error to the UI (and keeping the existing config on failure) rather than defaulting silently.

Copilot uses AI. Check for mistakes.
Comment on lines +66 to +70
pub fn merge(mut self, other: &Usage) -> Self {
self.prompt_tokens = self.prompt_tokens.max(other.prompt_tokens);
self.completion_tokens = self.completion_tokens.max(other.completion_tokens);
self.total_tokens = self.total_tokens.max(other.total_tokens);
self.cached_tokens = self.cached_tokens.max(other.cached_tokens);
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Usage::merge currently takes max() for total_tokens, which can break the invariant that total_tokens == prompt_tokens + completion_tokens (see Anthropic conversion where total is computed as prompt+completion). With partial/cumulative streaming events, max(total) will undercount (e.g. prompt=1000, completion=50 => total should be 1050). Consider recomputing total_tokens from the merged prompt/completion (or otherwise ensuring consistency) instead of maxing the precomputed totals.

Copilot uses AI. Check for mistakes.
Comment on lines +4 to 14
# Using typeset to keep variables local to plugin scope and prevent public exposure

typeset -gh _FORGE_BIN="${FORGE_BIN:-forge}"
typeset -gh _FORGE_CONVERSATION_PATTERN=":"
typeset -gh _FORGE_MAX_COMMIT_DIFF="${FORGE_MAX_COMMIT_DIFF:-100000}"
typeset -gh _FORGE_DELIMITER='\s\s+'
typeset -gh _FORGE_PREVIEW_WINDOW="--preview-window=bottom:75%:wrap:border-sharp"
typeset -h _FORGE_BIN="${FORGE_BIN:-forge}"
typeset -h _FORGE_CONVERSATION_PATTERN=":"
typeset -h _FORGE_MAX_COMMIT_DIFF="${FORGE_MAX_COMMIT_DIFF:-100000}"
typeset -h _FORGE_DELIMITER='\s\s+'
typeset -h _FORGE_PREVIEW_WINDOW="--preview-window=bottom:75%:wrap:border-sharp"

# Detect fd command - Ubuntu/Debian use 'fdfind', others use 'fd'
typeset -gh _FORGE_FD_CMD="$(command -v fdfind 2>/dev/null || command -v fd 2>/dev/null || echo 'fd')"
typeset -h _FORGE_FD_CMD="$(command -v fdfind 2>/dev/null || command -v fd 2>/dev/null || echo 'fd')"

Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These plugin variables are declared with typeset -h inside a sourced script. If the plugin is sourced from within a function (common with lazy-loading plugin managers), typeset creates local variables unless -g is used, so _FORGE_* variables may disappear when the sourcing function returns. Restore global declarations (e.g. typeset -gh ...) to ensure the plugin works reliably under lazy loaders while still keeping variables hidden.

Copilot uses AI. Check for mistakes.
Comment on lines +141 to +176
#[test]
fn test_filter_and_resolve_excludes_symlinks() {
let dir = tempdir().unwrap();
let base = dir.path();

// Real file with an allowed extension.
let real_path = base.join("main.rs");
File::create(&real_path)
.unwrap()
.write_all(b"fn main() {}")
.unwrap();

// Symlink pointing to the real file (also carries an allowed extension).
let link_path = base.join("link.rs");
std::os::unix::fs::symlink(&real_path, &link_path).unwrap();

let paths = vec!["main.rs".to_string(), "link.rs".to_string()];
let actual = filter_and_resolve(base, paths).unwrap();

let expected = vec![base.join("main.rs")];
assert_eq!(actual, expected);
}

#[test]
fn test_filter_and_resolve_excludes_dangling_symlinks() {
let dir = tempdir().unwrap();
let base = dir.path();

// Real file with an allowed extension (keeps the result non-empty).
let real_path = base.join("lib.rs");
File::create(&real_path).unwrap().write_all(b"").unwrap();

// Dangling symlink — target does not exist.
let dangling = base.join("missing.rs");
std::os::unix::fs::symlink(base.join("nonexistent.rs"), &dangling).unwrap();

Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These tests use std::os::unix::fs::symlink, which will not compile/run on Windows targets. If the project CI includes Windows, gate the symlink-based tests with #[cfg(unix)] (or provide a Windows implementation using std::os::windows::fs::symlink_file/symlink_dir).

Copilot uses AI. Check for mistakes.
Comment on lines +625 to +635
#[tokio::test]
async fn test_walker_excludes_symlinks() {
let fixture = fixtures::Fixture::default();

// Real file that should appear in results.
fixture.add_file("real.txt", "content").unwrap();

// Symlink pointing to the real file — must be excluded.
let link_path = fixture.as_path().join("link.txt");
std::os::unix::fs::symlink(fixture.as_path().join("real.txt"), &link_path).unwrap();

Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test uses std::os::unix::fs::symlink, which will not compile/run on Windows targets. Gate with #[cfg(unix)] (or add a Windows symlink implementation) so the test suite remains portable.

Copilot uses AI. Check for mistakes.
Comment on lines 72 to +110
@@ -104,6 +106,8 @@ impl ProviderId {
ProviderId::OPENCODE_ZEN,
ProviderId::FIREWORKS_AI,
ProviderId::NOVITA,
ProviderId::GOOGLE_AI_STUDIO,
ProviderId::ADAL,
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The PR title/description focus on adding the AdaL provider, but this PR also includes a large config-loading refactor (removing get_config from EnvironmentInfra), OAuth callback server, symlink filtering changes, zsh plugin behavior changes, and multiple dependency upgrades. Consider updating the PR description to reflect the full scope, or splitting into smaller PRs to reduce review/rollback risk.

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch — the fork PR includes upstream merge commits which inflate the diff. The upstream PR (antinomyhq/forgecode#2868) shows only the 2-file, 34-line AdaL change.

Regarding the AdaL provider specifically: the updated commit now includes 3 unit tests (test_adal_from_str, test_adal_display_name, test_adal_in_built_in_providers) and also fixes 3 pre-existing missing FromStr arms for vertex_ai_anthropic, bedrock, and opencode_zen.

Comment on lines 297 to +306
fn fixture_skill_repo() -> (ForgeSkillRepository<ForgeInfra>, std::path::PathBuf) {
let skill_dir = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("src/fixtures/skills_with_resources");
let infra = Arc::new(ForgeInfra::new(std::env::current_dir().unwrap()));
let config = ForgeConfig::read().unwrap_or_default();
let services_url = config.services_url.parse().unwrap();
let infra = Arc::new(ForgeInfra::new(
std::env::current_dir().unwrap(),
config,
services_url,
));
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This test helper reads the user’s real ~/.forge.toml via ForgeConfig::read().unwrap_or_default(), making the unit test environment-dependent and potentially flaky (and it also silently swallows parse errors). Prefer using ForgeConfig::default() (or a minimal synthetic config with a known-valid services_url) so the test is deterministic and doesn’t depend on developer machine state.

Copilot uses AI. Check for mistakes.
Comment thread forge.schema.json
Comment on lines +70 to +76
"max_commit_count": {
"description": "Maximum number of recent commits included as context for commit message\ngeneration.",
"type": "integer",
"format": "uint",
"default": 0,
"minimum": 0
},
Copy link

Copilot AI Apr 6, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The schema lists max_commit_count.default as 0, but the embedded defaults file (crates/forge_config/.forge.toml) sets max_commit_count = 20, and runtime behavior (e.g. GitApp::fetch_git_context) depends on this being non-zero. Consider updating the schema default to match the actual default so validation/UI tooling doesn’t suggest an unusable value.

Copilot uses AI. Check for mistakes.
Comment on lines +75 to +76
pub const GOOGLE_AI_STUDIO: ProviderId = ProviderId(Cow::Borrowed("google_ai_studio"));
pub const ADAL: ProviderId = ProviderId(Cow::Borrowed("adal"));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. adal const missing rustdoc 📘 Rule violation ⚙ Maintainability

New public constants ProviderId::GOOGLE_AI_STUDIO and ProviderId::ADAL were added without ///
Rustdoc comments. This violates the requirement that all public Rust items must be documented.
Agent Prompt
## Issue description
New public constants were added without required Rustdoc comments.

## Issue Context
Compliance requires `///` docs for all public Rust items (no code examples).

## Fix Focus Areas
- crates/forge_domain/src/provider.rs[72-76]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the updated commit — added ProviderId::ADAL with full wiring:

  • built_in_providers() inclusion
  • display_name() mapping → "AdaL"
  • FromStr parsing for "adal"
  • Also fixed 3 pre-existing missing FromStr arms (vertex_ai_anthropic, bedrock, opencode_zen)
  • 3 new unit tests covering all of the above

All 20 provider tests pass ✅

Comment on lines +1 to +21
//! Integration test for stringified tool call arguments fix
//!
//! This test verifies that when the API sends tool call arguments as a string
//! containing JSON (like kimi-k2p5-turbo does), we properly parse it and
//! serialize it back as a JSON object when sending to the API.

use forge_domain::{Context, ContextMessage, Role};

/// Test that stringified tool call arguments from API are properly handled
///
/// This simulates the exact scenario: API sends arguments as a string
/// containing JSON, and when we serialize the conversation back to send to API,
/// it should be a proper object.
#[test]
fn test_stringified_tool_call_arguments_roundtrip() {
// Simulate what kimi-k2p5-turbo sends: arguments as a string containing JSON
// Note: This is what the API sends us - a JSON string value containing JSON
// object
let conversation_json = r#"{
"messages": [
{
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

2. Integration tests not co-located 📘 Rule violation ⚙ Maintainability

New tests were added under crates/forge_domain/tests/ instead of being co-located with the source
module via #[cfg(test)] mod tests. This violates the requirement to keep tests in the same file as
the code under test.
Agent Prompt
## Issue description
Tests were added as a separate file under `crates/forge_domain/tests/`, but compliance requires unit tests to be co-located with the implementation.

## Issue Context
Refactor these tests into the relevant module file using `#[cfg(test)] mod tests { ... }`.

## Fix Focus Areas
- crates/forge_domain/tests/test_stringified_tool_calls.rs[1-313]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +160 to +162
let config = forge_config::ForgeConfig::read().unwrap_or_default();
self.config = config.clone();
self.api = Arc::new((self.new_api)(config));
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

4. Config errors masked on /new 🐞 Bug ☼ Reliability

UI::on_new uses ForgeConfig::read().unwrap_or_default(), so a malformed/invalid config silently
falls back to defaults after startup, undermining the PR’s “fail fast” config validation and causing
unexpected runtime behavior. This also makes debugging configuration problems much harder because
the error is discarded.
Agent Prompt
### Issue description
`UI::on_new()` calls `ForgeConfig::read().unwrap_or_default()`, swallowing config read/parse errors and replacing config with defaults.

### Issue Context
The PR explicitly adds startup-time validation to avoid silent fallback; `/new` should not reintroduce silent fallback.

### Fix Focus Areas
- crates/forge_main/src/ui.rs[159-163]

### Expected fix
- Replace `unwrap_or_default()` with error propagation (e.g., `ForgeConfig::read()?` plus a helpful `context(...)`).
- Ensure the UI prints a clear error and keeps the existing API/config if the reload fails (instead of resetting to defaults).

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +111 to 118
let services_url: Url = config
.services_url
.parse()
.context("services_url in configuration must be a valid URL")?;

// Handle worktree creation if specified
let cwd: PathBuf = match (&cli.sandbox, &cli.directory) {
(Some(sandbox), Some(cli)) => {
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

5. Services_url never refreshes 🐞 Bug ≡ Correctness

main.rs parses services_url once from the initial config and captures it in the UI API-factory
closure, so later config changes to services_url (followed by /new) cannot take effect because new
API instances still use the originally captured URL. This can route requests to the wrong workspace
server despite the user updating configuration.
Agent Prompt
### Issue description
The API factory closure captures `services_url` computed from the initial config, so subsequent `ForgeConfig` reloads (e.g. `/new`) cannot change which gRPC workspace server is used.

### Issue Context
`UI::init`’s factory closure receives a `ForgeConfig` argument explicitly to reflect config changes in new conversations.

### Fix Focus Areas
- crates/forge_main/src/main.rs[104-133]

### Expected fix
- Compute/validate `services_url` *inside* the factory closure from the passed-in `config.services_url` (and return an error instead of capturing a stale value).
- If the closure cannot return `Result`, consider changing `ForgeAPI::init` to accept only `ForgeConfig` and perform parsing/validation internally, or change the UI factory signature to return `Result<ForgeAPI>` so invalid URLs are surfaced without panics/silent fallback.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


async fn update_environment(&self, ops: Vec<ConfigOperation>) -> anyhow::Result<()> {
// Load the global config (with defaults applied) for the update round-trip
let mut fc = ConfigReader::default()
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

3. Legacy config lost on update 🐞 Bug ≡ Correctness

ForgeEnvironmentInfra::update_environment rebuilds the writable config from defaults+global only and
writes it back, so any settings that exist only in the legacy ~/.forge/.config.json source will be
silently dropped when a config operation is applied. After cache invalidation, cached_config also
re-reads without legacy, making the session’s resolved config inconsistent with ForgeConfig::read().
Agent Prompt
### Issue description
`ForgeEnvironmentInfra::cached_config()` and `ForgeEnvironmentInfra::update_environment()` re-read config without `read_legacy()`, even though `ForgeConfig::read()` includes it. This can drop legacy-only settings (from `~/.forge/.config.json`) when any config update is persisted.

### Issue Context
- `ForgeConfig::read()` uses `ConfigReader::read_legacy()`.
- `update_environment()` writes a new global config derived from defaults+global only.

### Fix Focus Areas
- crates/forge_infra/src/env.rs[118-130]
- crates/forge_infra/src/env.rs[149-166]

### Expected fix
- Prefer calling `ForgeConfig::read()` for the base config used by `cached_config()` when cache is empty.
- In `update_environment()`, build the base config using the same merged sources as `ForgeConfig::read()` (at least `read_legacy()+read_defaults()+read_global()`; include `read_env()` if you intend env overrides to participate in the computed writeback), so applying an operation does not discard legacy-only fields.
- Add/adjust a regression test that starts with a config coming only from legacy source, runs `update_environment`, and asserts unrelated legacy-derived fields are preserved.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Add AdaL CLI by SylphAI as a new provider option in ForgeCode. AdaL is
registered as an OpenAI-compatible provider with api.sylph.ai endpoints,
enabling ForgeCode users to access AdaL's models via API key auth.

Changes:
- Add 'adal' entry to provider.json with OpenAI response type
- Add ProviderId::ADAL constant to forge_domain
- Wire up display_name ("AdaL"), FromStr, and built_in_providers()
- Add missing FromStr arms for vertex_ai_anthropic, bedrock, opencode_zen
- Add unit tests for ADAL display name, from_str, and built_in_providers

Co-Authored-By: ForgeCode <noreply@forgecode.dev>
@chindris-mihai-alexandru
Copy link
Copy Markdown
Owner Author

Closing — the upstream PR (tailcallhq#2868) is the canonical one. This fork PR was only needed to push the branch. Once upstream merges, changes will be pulled back via git fetch upstream.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

8 participants