From 57ac2a004790a32d3a8945a4bff8d594c75b1bd8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chindri=C8=99=20Mihai=20-=20Alexandru?= Date: Mon, 6 Apr 2026 21:19:03 +0300 Subject: [PATCH 1/5] feat(provider): add AdaL (SylphAI) as a built-in provider 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 --- crates/forge_domain/src/provider.rs | 25 ++++++++++++++++++++ crates/forge_repo/src/provider/provider.json | 9 +++++++ 2 files changed, 34 insertions(+) diff --git a/crates/forge_domain/src/provider.rs b/crates/forge_domain/src/provider.rs index a65b43e416..832ec98064 100644 --- a/crates/forge_domain/src/provider.rs +++ b/crates/forge_domain/src/provider.rs @@ -73,6 +73,7 @@ impl ProviderId { pub const FIREWORKS_AI: ProviderId = ProviderId(Cow::Borrowed("fireworks-ai")); pub const NOVITA: ProviderId = ProviderId(Cow::Borrowed("novita")); pub const GOOGLE_AI_STUDIO: ProviderId = ProviderId(Cow::Borrowed("google_ai_studio")); + pub const ADAL: ProviderId = ProviderId(Cow::Borrowed("adal")); /// Returns all built-in provider IDs /// @@ -106,6 +107,7 @@ impl ProviderId { ProviderId::FIREWORKS_AI, ProviderId::NOVITA, ProviderId::GOOGLE_AI_STUDIO, + ProviderId::ADAL, ] } @@ -132,6 +134,7 @@ impl ProviderId { "fireworks-ai" => "FireworksAI".to_string(), "novita" => "Novita".to_string(), "google_ai_studio" => "GoogleAIStudio".to_string(), + "adal" => "AdaL".to_string(), _ => { // For other providers, use UpperCamelCase conversion use convert_case::{Case, Casing}; @@ -176,7 +179,11 @@ impl std::str::FromStr for ProviderId { "codex" => ProviderId::CODEX, "fireworks-ai" => ProviderId::FIREWORKS_AI, "novita" => ProviderId::NOVITA, + "vertex_ai_anthropic" => ProviderId::VERTEX_AI_ANTHROPIC, + "bedrock" => ProviderId::BEDROCK, + "opencode_zen" => ProviderId::OPENCODE_ZEN, "google_ai_studio" => ProviderId::GOOGLE_AI_STUDIO, + "adal" => ProviderId::ADAL, // For custom providers, use Cow::Owned to avoid memory leaks custom => ProviderId(Cow::Owned(custom.to_string())), }; @@ -581,6 +588,24 @@ mod tests { assert_eq!(actual, expected); } + #[test] + fn test_adal_from_str() { + let actual = ProviderId::from_str("adal").unwrap(); + let expected = ProviderId::ADAL; + assert_eq!(actual, expected); + } + + #[test] + fn test_adal_display_name() { + assert_eq!(ProviderId::ADAL.to_string(), "AdaL"); + } + + #[test] + fn test_adal_in_built_in_providers() { + let built_in = ProviderId::built_in_providers(); + assert!(built_in.contains(&ProviderId::ADAL)); + } + #[test] fn test_io_intelligence() { let fixture = "test_key"; diff --git a/crates/forge_repo/src/provider/provider.json b/crates/forge_repo/src/provider/provider.json index 16dc7899f6..56434a6800 100644 --- a/crates/forge_repo/src/provider/provider.json +++ b/crates/forge_repo/src/provider/provider.json @@ -3099,5 +3099,14 @@ "input_modalities": ["text"] } ] + }, + { + "id": "adal", + "api_key_vars": "ADAL_API_KEY", + "url_param_vars": [], + "response_type": "OpenAI", + "url": "https://api.sylph.ai/v1/chat/completions", + "models": "https://api.sylph.ai/v1/models", + "auth_methods": ["api_key"] } ] From f2f5f551e3d0adfb1fb5d194329d567e4b79c30d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chindri=C8=99=20Mihai=20-=20Alexandru?= Date: Mon, 6 Apr 2026 22:08:18 +0300 Subject: [PATCH 2/5] feat(provider): add Modal as a built-in provider for GLM-5 Add Modal as a new provider in ForgeCode, enabling access to Z.ai's GLM-5 745B parameter model hosted on Modal's infrastructure. Modal offers an OpenAI-compatible API endpoint with free GLM-5 access until April 30th, 2026. Changes: - Add 'modal' entry to provider.json with GLM-5-FP8 model - Add ProviderId::MODAL constant to forge_domain - Wire up display_name ("Modal"), FromStr, and built_in_providers() - Add unit tests for MODAL display name, from_str, and built_in_providers Co-Authored-By: ForgeCode --- crates/forge_domain/src/provider.rs | 22 ++++++++++++++++++++ crates/forge_repo/src/provider/provider.json | 20 ++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/crates/forge_domain/src/provider.rs b/crates/forge_domain/src/provider.rs index a65b43e416..be7498c7e2 100644 --- a/crates/forge_domain/src/provider.rs +++ b/crates/forge_domain/src/provider.rs @@ -73,6 +73,7 @@ impl ProviderId { pub const FIREWORKS_AI: ProviderId = ProviderId(Cow::Borrowed("fireworks-ai")); pub const NOVITA: ProviderId = ProviderId(Cow::Borrowed("novita")); pub const GOOGLE_AI_STUDIO: ProviderId = ProviderId(Cow::Borrowed("google_ai_studio")); + pub const MODAL: ProviderId = ProviderId(Cow::Borrowed("modal")); /// Returns all built-in provider IDs /// @@ -106,6 +107,7 @@ impl ProviderId { ProviderId::FIREWORKS_AI, ProviderId::NOVITA, ProviderId::GOOGLE_AI_STUDIO, + ProviderId::MODAL, ] } @@ -132,6 +134,7 @@ impl ProviderId { "fireworks-ai" => "FireworksAI".to_string(), "novita" => "Novita".to_string(), "google_ai_studio" => "GoogleAIStudio".to_string(), + "modal" => "Modal".to_string(), _ => { // For other providers, use UpperCamelCase conversion use convert_case::{Case, Casing}; @@ -177,6 +180,7 @@ impl std::str::FromStr for ProviderId { "fireworks-ai" => ProviderId::FIREWORKS_AI, "novita" => ProviderId::NOVITA, "google_ai_studio" => ProviderId::GOOGLE_AI_STUDIO, + "modal" => ProviderId::MODAL, // For custom providers, use Cow::Owned to avoid memory leaks custom => ProviderId(Cow::Owned(custom.to_string())), }; @@ -581,6 +585,24 @@ mod tests { assert_eq!(actual, expected); } + #[test] + fn test_modal_from_str() { + let actual = ProviderId::from_str("modal").unwrap(); + let expected = ProviderId::MODAL; + assert_eq!(actual, expected); + } + + #[test] + fn test_modal_display_name() { + assert_eq!(ProviderId::MODAL.to_string(), "Modal"); + } + + #[test] + fn test_modal_in_built_in_providers() { + let built_in = ProviderId::built_in_providers(); + assert!(built_in.contains(&ProviderId::MODAL)); + } + #[test] fn test_io_intelligence() { let fixture = "test_key"; diff --git a/crates/forge_repo/src/provider/provider.json b/crates/forge_repo/src/provider/provider.json index 16dc7899f6..e0f6f9d24d 100644 --- a/crates/forge_repo/src/provider/provider.json +++ b/crates/forge_repo/src/provider/provider.json @@ -3099,5 +3099,25 @@ "input_modalities": ["text"] } ] + }, + { + "id": "modal", + "api_key_vars": "MODAL_API_KEY", + "url_param_vars": [], + "response_type": "OpenAI", + "url": "https://api.us-west-2.modal.direct/v1/chat/completions", + "models": [ + { + "id": "zai-org/GLM-5-FP8", + "name": "GLM-5", + "description": "Z.ai's 745B parameter flagship open-source MoE model for long-horizon agents and systems engineering, hosted on Modal", + "context_length": 192000, + "tools_supported": true, + "supports_parallel_tool_calls": true, + "supports_reasoning": true, + "input_modalities": ["text"] + } + ], + "auth_methods": ["api_key"] } ] From 36d4a24cb4bf2c91cd074dfbeee973f23e3c2603 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chindri=C8=99=20Mihai=20-=20Alexandru?= Date: Fri, 17 Apr 2026 22:44:58 +0300 Subject: [PATCH 3/5] Add ACP integration tracking notes --- ACP_TRACKING.md | 31 +++++++++++++++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 ACP_TRACKING.md diff --git a/ACP_TRACKING.md b/ACP_TRACKING.md new file mode 100644 index 0000000000..9c56d3f9f6 --- /dev/null +++ b/ACP_TRACKING.md @@ -0,0 +1,31 @@ +# ACP Integration Tracking + +> Personal reminder for when ForgeCode ACP support lands. + +## Upstream References + +- **Feature request**: [tailcallhq/forgecode#2968](https://github.com/tailcallhq/forgecode/issues/2968) — ACP support +- **PR (open)**: [tailcallhq/forgecode#2858](https://github.com/tailcallhq/forgecode/pull/2858) — Machine stdio transport for ACP +- **PR (draft)**: [tailcallhq/forgecode#2371](https://github.com/tailcallhq/forgecode/pull/2371) — ACP phase 1 testing +- **ACP spec**: https://zed.dev/acp + +## Action Items + +- [ ] Watch/subscribe to the above PRs and issue on GitHub +- [ ] When ACP PRs merge into upstream `main`: + - [ ] Fetch and merge upstream into this fork's `main` + - [ ] Rebuild local Forge binary: `cd /Volumes/990Pro2TB/OtherProjects/forgecode-fork && cargo build` + - [ ] Test ACP transport: `forge --acp` or equivalent flag +- [ ] Once ForgeCode ACP is stable, test with Zed: + - [ ] Verify ForgeCode appears in Zed's agent panel + - [ ] End-to-end test: open a ForgeCode session from Zed via ACP +- [ ] Close/remove this tracking file once ACP is fully integrated and working locally + +## Related + +- Zed `--wait` bug (filed): [zed-industries/zed#54203](https://github.com/zed-industries/zed/issues/54203) + +## Context + +ACP (Agent Client Protocol) enables ForgeCode to run as an agent inside Zed and other ACP-compatible editors. +This completes the **ForgeCode + Warp + Zed** workflow by letting Zed invoke ForgeCode directly. \ No newline at end of file From 174266e2403ca73b764e99bb3dd67156b31d0475 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chindri=C8=99=20Mihai=20-=20Alexandru?= Date: Sat, 18 Apr 2026 01:01:51 +0300 Subject: [PATCH 4/5] fix(paste): preserve large pasted prompts as plain text Co-Authored-By: ForgeCode --- crates/forge_main/src/zsh/paste.rs | 38 +++++++++++++++++++++++++++++- 1 file changed, 37 insertions(+), 1 deletion(-) diff --git a/crates/forge_main/src/zsh/paste.rs b/crates/forge_main/src/zsh/paste.rs index e82b800f4f..8efee53e28 100644 --- a/crates/forge_main/src/zsh/paste.rs +++ b/crates/forge_main/src/zsh/paste.rs @@ -7,6 +7,8 @@ use std::path::Path; +const MAX_INLINE_PATH_WRAP_BYTES: usize = 512; + /// Transforms pasted text by wrapping bare file paths in `@[...]` syntax. /// /// Called when a bracketed-paste event is received. The pasted content is @@ -42,10 +44,24 @@ pub fn wrap_pasted_text(pasted: &str) -> String { return format!("{leading}@[{resolved}]{trailing}"); } - // Scan token by token, wrapping any absolute paths that exist on disk + // Arbitrary pasted prompts often contain absolute paths inside prose, + // logs, stack traces, or code snippets. Auto-wrapping those paths turns + // plain text into attachments and can surface confusing binary-file errors + // when the referenced path happens to be an executable. Restrict token-level + // path wrapping to smaller single-line pastes that more closely resemble a + // drag-and-drop or an intentional short request. + if should_preserve_plain_paste(trimmed) { + return normalised; + } + + // Scan token by token, wrapping any absolute paths that exist on disk. wrap_tokens(&normalised) } +fn should_preserve_plain_paste(trimmed: &str) -> bool { + trimmed.contains('\n') || trimmed.len() > MAX_INLINE_PATH_WRAP_BYTES +} + /// Strips surrounding single or double quotes that some terminals add /// when dragging files with spaces in their names. fn strip_surrounding_quotes(s: &str) -> &str { @@ -224,6 +240,26 @@ mod tests { assert_eq!(actual, expected); } + #[test] + fn test_wrap_pasted_text_multiline_prompt_preserves_plain_text_paths() { + let fixture = "Please review this snippet:\n/usr/bin/env\n/tmp\nAnd explain what it does."; + let actual = wrap_pasted_text(fixture); + let expected = fixture; + assert_eq!(actual, expected); + } + + #[test] + fn test_wrap_pasted_text_large_single_line_prompt_preserves_plain_text_paths() { + let fixture = format!( + "{} /usr/bin/env {}", + "context".repeat(60), + "details".repeat(30) + ); + let actual = wrap_pasted_text(&fixture); + let expected = fixture; + assert_eq!(actual, expected); + } + #[test] fn test_wrap_pasted_text_existing_file() { // /usr/bin/env exists on macOS/Linux From 1c990617b33a94c4384e13986e2f61afb2746522 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Chindri=C8=99=20Mihai=20-=20Alexandru?= Date: Sat, 18 Apr 2026 02:02:33 +0300 Subject: [PATCH 5/5] fix(paste): address Copilot review feedback Co-Authored-By: ForgeCode --- ACP_TRACKING.md | 31 ------------------------------ crates/forge_main/src/zsh/paste.rs | 9 ++++----- 2 files changed, 4 insertions(+), 36 deletions(-) delete mode 100644 ACP_TRACKING.md diff --git a/ACP_TRACKING.md b/ACP_TRACKING.md deleted file mode 100644 index 9c56d3f9f6..0000000000 --- a/ACP_TRACKING.md +++ /dev/null @@ -1,31 +0,0 @@ -# ACP Integration Tracking - -> Personal reminder for when ForgeCode ACP support lands. - -## Upstream References - -- **Feature request**: [tailcallhq/forgecode#2968](https://github.com/tailcallhq/forgecode/issues/2968) — ACP support -- **PR (open)**: [tailcallhq/forgecode#2858](https://github.com/tailcallhq/forgecode/pull/2858) — Machine stdio transport for ACP -- **PR (draft)**: [tailcallhq/forgecode#2371](https://github.com/tailcallhq/forgecode/pull/2371) — ACP phase 1 testing -- **ACP spec**: https://zed.dev/acp - -## Action Items - -- [ ] Watch/subscribe to the above PRs and issue on GitHub -- [ ] When ACP PRs merge into upstream `main`: - - [ ] Fetch and merge upstream into this fork's `main` - - [ ] Rebuild local Forge binary: `cd /Volumes/990Pro2TB/OtherProjects/forgecode-fork && cargo build` - - [ ] Test ACP transport: `forge --acp` or equivalent flag -- [ ] Once ForgeCode ACP is stable, test with Zed: - - [ ] Verify ForgeCode appears in Zed's agent panel - - [ ] End-to-end test: open a ForgeCode session from Zed via ACP -- [ ] Close/remove this tracking file once ACP is fully integrated and working locally - -## Related - -- Zed `--wait` bug (filed): [zed-industries/zed#54203](https://github.com/zed-industries/zed/issues/54203) - -## Context - -ACP (Agent Client Protocol) enables ForgeCode to run as an agent inside Zed and other ACP-compatible editors. -This completes the **ForgeCode + Warp + Zed** workflow by letting Zed invoke ForgeCode directly. \ No newline at end of file diff --git a/crates/forge_main/src/zsh/paste.rs b/crates/forge_main/src/zsh/paste.rs index 8efee53e28..d714d85e43 100644 --- a/crates/forge_main/src/zsh/paste.rs +++ b/crates/forge_main/src/zsh/paste.rs @@ -250,11 +250,10 @@ mod tests { #[test] fn test_wrap_pasted_text_large_single_line_prompt_preserves_plain_text_paths() { - let fixture = format!( - "{} /usr/bin/env {}", - "context".repeat(60), - "details".repeat(30) - ); + let mut fixture = String::from("context /usr/bin/env"); + while fixture.len() <= MAX_INLINE_PATH_WRAP_BYTES { + fixture.push('x'); + } let actual = wrap_pasted_text(&fixture); let expected = fixture; assert_eq!(actual, expected);