From d625e58215eacaef6139488353ab6617dd977fb6 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 23 May 2026 14:40:13 -0400 Subject: [PATCH 01/66] chore(deps): bump agent-client-protocol from 0.11.1 to 0.12.1 (#9381) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> Co-authored-by: Jack Amadeo --- Cargo.lock | 179 +++++++++++++++++++++++++++++++++++++++++++++++------ Cargo.toml | 4 +- 2 files changed, 162 insertions(+), 21 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index e1423fe66404..3a37d53c72cf 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,13 +40,14 @@ dependencies = [ [[package]] name = "agent-client-protocol" -version = "0.11.1" +version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2af62fb84df2af0f933d8f5fd78b843fa5eb0ec5a48fa1b528c41951d0bbe36c" +checksum = "4361ba6627e51de955b10f3c77fb9eb959c85191a236c1c2c84e32f4ff240faf" dependencies = [ "agent-client-protocol-derive", "agent-client-protocol-schema", - "anyhow", + "async-process", + "blocking", "futures", "futures-concurrency", "jsonrpcmsg", @@ -55,7 +56,7 @@ dependencies = [ "schemars 1.2.1", "serde", "serde_json", - "thiserror 2.0.18", + "shell-words", "tokio", "tokio-util", "tracing", @@ -64,20 +65,19 @@ dependencies = [ [[package]] name = "agent-client-protocol-derive" -version = "0.11.0" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ce42c2d3c048c12897eef2e577dfff1e3355c632c9f1625cc953b9df48b44631" +checksum = "cabdc9d845d08ec7ed2d0c9de1ae4a1b198301407d55855261572761be90ec9f" dependencies = [ - "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "agent-client-protocol-schema" -version = "0.12.0" +version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49bae57dad1c28a362fbdcf7bab0583316a02b45a70792109fced55780a3b63c" +checksum = "b957d8391ac3933e2a940446171c508d2b8ffc386d8fa7d0b9c936a2575b463e" dependencies = [ "anyhow", "derive_more", @@ -338,6 +338,18 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "async-channel" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" +dependencies = [ + "concurrent-queue", + "event-listener-strategy", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-compression" version = "0.4.41" @@ -350,12 +362,77 @@ dependencies = [ "tokio", ] +[[package]] +name = "async-io" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" +dependencies = [ + "autocfg", + "cfg-if", + "concurrent-queue", + "futures-io", + "futures-lite", + "parking", + "polling", + "rustix 1.1.4", + "slab", + "windows-sys 0.61.2", +] + +[[package]] +name = "async-lock" +version = "3.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" +dependencies = [ + "event-listener", + "event-listener-strategy", + "pin-project-lite", +] + [[package]] name = "async-once-cell" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" +[[package]] +name = "async-process" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" +dependencies = [ + "async-channel", + "async-io", + "async-lock", + "async-signal", + "async-task", + "blocking", + "cfg-if", + "event-listener", + "futures-lite", + "rustix 1.1.4", +] + +[[package]] +name = "async-signal" +version = "0.2.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" +dependencies = [ + "async-io", + "async-lock", + "atomic-waker", + "cfg-if", + "futures-core", + "futures-io", + "rustix 1.1.4", + "signal-hook-registry", + "slab", + "windows-sys 0.61.2", +] + [[package]] name = "async-stream" version = "0.3.6" @@ -378,6 +455,12 @@ dependencies = [ "syn 2.0.117", ] +[[package]] +name = "async-task" +version = "4.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" + [[package]] name = "async-trait" version = "0.1.89" @@ -1548,6 +1631,19 @@ dependencies = [ "objc2", ] +[[package]] +name = "blocking" +version = "1.6.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" +dependencies = [ + "async-channel", + "async-task", + "futures-io", + "futures-lite", + "piper", +] + [[package]] name = "borrow-or-share" version = "0.2.4" @@ -1605,6 +1701,15 @@ dependencies = [ "alloc-stdlib", ] +[[package]] +name = "bs58" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bf88ba1141d185c399bee5288d850d63b8369520c1eafc32a0430b5b6c287bf4" +dependencies = [ + "tinyvec", +] + [[package]] name = "bstr" version = "1.12.1" @@ -3773,6 +3878,16 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "event-listener-strategy" +version = "0.5.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" +dependencies = [ + "event-listener", + "pin-project-lite", +] + [[package]] name = "exr" version = "1.74.0" @@ -6368,9 +6483,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.1" +version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" +checksum = "50b7e5b27aa02a74bac8c3f23f448f8d87ff11f92d3aac1a6ed369ee08cc56c1" dependencies = [ "libc", "wasi", @@ -7577,6 +7692,17 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" +[[package]] +name = "piper" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" +dependencies = [ + "atomic-waker", + "fastrand", + "futures-io", +] + [[package]] name = "pkcs1" version = "0.7.5" @@ -7649,6 +7775,20 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "polling" +version = "3.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +dependencies = [ + "cfg-if", + "concurrent-queue", + "hermit-abi", + "pin-project-lite", + "rustix 1.1.4", + "windows-sys 0.61.2", +] + [[package]] name = "poly1305" version = "0.8.0" @@ -9157,11 +9297,12 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.18.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd5414fad8e6907dbdd5bc441a50ae8d6e26151a03b1de04d89a5576de61d01f" +checksum = "e72c1c2cb7b223fafb600a619537a871c2818583d619401b785e7c0b746ccde2" dependencies = [ "base64 0.22.1", + "bs58", "chrono", "hex", "indexmap 1.9.3", @@ -9176,9 +9317,9 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.18.0" +version = "3.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d3db8978e608f1fe7357e211969fd9abdcae80bac1ba7a3369bb7eb6b404eb65" +checksum = "b90c488738ecb4fb0262f41f43bc40efc5868d9fb744319ddf5f5317f417bfac" dependencies = [ "darling 0.23.0", "proc-macro2", @@ -11111,9 +11252,9 @@ dependencies = [ [[package]] name = "tokio" -version = "1.50.0" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -11144,9 +11285,9 @@ dependencies = [ [[package]] name = "tokio-macros" -version = "2.6.1" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", diff --git a/Cargo.toml b/Cargo.toml index 6b09a19e4dbe..b0f737d3ecfe 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -21,8 +21,8 @@ string_slice = "warn" [workspace.dependencies] rmcp = { version = "1.7.0", features = ["schemars", "auth"] } -agent-client-protocol-schema = { version = "0.12", features = ["unstable"] } -agent-client-protocol = "0.11" +agent-client-protocol-schema = { version = "0.13", features = ["unstable"] } +agent-client-protocol = "0.12" arboard = "3" anyhow = "1.0" async-stream = "0.3" From 8689fdf33f20032afb8e21a9b033499943b56ed4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Sat, 23 May 2026 18:40:20 +0000 Subject: [PATCH 02/66] chore(deps): bump image from 0.24.9 to 0.25.10 (#9383) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- Cargo.lock | 367 +++++++++++++++++++++++++++++------- crates/goose-mcp/Cargo.toml | 2 +- 2 files changed, 301 insertions(+), 68 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 3a37d53c72cf..ba22bd09efe8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -112,6 +112,24 @@ dependencies = [ "memchr", ] +[[package]] +name = "aligned" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" +dependencies = [ + "as-slice", +] + +[[package]] +name = "aligned-vec" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" +dependencies = [ + "equator", +] + [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -232,7 +250,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" dependencies = [ "clipboard-win", - "image 0.25.10", + "image", "log", "objc2", "objc2-app-kit", @@ -254,6 +272,17 @@ dependencies = [ "rustversion", ] +[[package]] +name = "arg_enum_proc_macro" +version = "0.3.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "arraydeque" version = "0.5.1" @@ -272,6 +301,15 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" +[[package]] +name = "as-slice" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" +dependencies = [ + "stable_deref_trait", +] + [[package]] name = "ascii" version = "1.1.0" @@ -530,6 +568,49 @@ version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" +[[package]] +name = "av-scenechange" +version = "0.14.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" +dependencies = [ + "aligned", + "anyhow", + "arg_enum_proc_macro", + "arrayvec", + "log", + "num-rational", + "num-traits", + "pastey 0.1.1", + "rayon", + "thiserror 2.0.18", + "v_frame", + "y4m", +] + +[[package]] +name = "av1-grain" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" +dependencies = [ + "anyhow", + "arrayvec", + "log", + "nom 8.0.0", + "num-rational", + "v_frame", +] + +[[package]] +name = "avif-serialize" +version = "0.8.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e7178fe5f7d460b13895ebb9dcb28a3a6216d2df2574a0806cb51b555d297f38" +dependencies = [ + "arrayvec", +] + [[package]] name = "aws-config" version = "1.8.16" @@ -1563,6 +1644,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "bitstream-io" +version = "4.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" +dependencies = [ + "no_std_io2", +] + [[package]] name = "bitvec" version = "1.0.1" @@ -1721,6 +1811,12 @@ dependencies = [ "serde", ] +[[package]] +name = "built" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9" + [[package]] name = "bumpalo" version = "3.20.2" @@ -3568,7 +3664,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed73cbf5e1c37baa23f4132569ac1187829f03922c206bd68fe109e3001a343d" dependencies = [ "base64 0.22.1", - "image 0.25.10", + "image", "quick-xml 0.36.2", "serde", "serde_json", @@ -3807,6 +3903,26 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" +[[package]] +name = "equator" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" +dependencies = [ + "equator-macro", +] + +[[package]] +name = "equator-macro" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "equivalent" version = "1.0.2" @@ -4636,16 +4752,6 @@ dependencies = [ "wasip3", ] -[[package]] -name = "gif" -version = "0.13.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" -dependencies = [ - "color_quant", - "weezl", -] - [[package]] name = "gif" version = "0.14.1" @@ -4758,7 +4864,7 @@ dependencies = [ "opentelemetry-otlp", "opentelemetry-stdout", "opentelemetry_sdk", - "pastey", + "pastey 0.2.3", "pctx_code_mode", "pem", "pkcs1", @@ -4893,7 +4999,7 @@ dependencies = [ "chrono", "docx-rs", "etcetera 0.11.0", - "image 0.24.9", + "image", "include_dir", "indoc", "lopdf", @@ -5615,38 +5721,36 @@ dependencies = [ [[package]] name = "image" -version = "0.24.9" +version = "0.25.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" dependencies = [ "bytemuck", - "byteorder", + "byteorder-lite", "color_quant", "exr", - "gif 0.13.3", - "jpeg-decoder", + "gif", + "image-webp", + "moxcms", "num-traits", - "png 0.17.16", + "png", "qoi", - "tiff 0.9.1", + "ravif", + "rayon", + "rgb", + "tiff", + "zune-core", + "zune-jpeg", ] [[package]] -name = "image" -version = "0.25.10" +name = "image-webp" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" dependencies = [ - "bytemuck", "byteorder-lite", - "color_quant", - "gif 0.14.1", - "moxcms", - "num-traits", - "png 0.18.1", - "tiff 0.11.3", - "zune-core", - "zune-jpeg", + "quick-error", ] [[package]] @@ -5655,6 +5759,12 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c" +[[package]] +name = "imgref" +version = "1.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2" + [[package]] name = "import_map" version = "0.24.0" @@ -5770,6 +5880,17 @@ dependencies = [ "web-sys", ] +[[package]] +name = "interpolate_name" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.117", +] + [[package]] name = "ipnet" version = "2.12.0" @@ -5968,15 +6089,6 @@ dependencies = [ "libc", ] -[[package]] -name = "jpeg-decoder" -version = "0.3.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" -dependencies = [ - "rayon", -] - [[package]] name = "js-sys" version = "0.3.91" @@ -6152,6 +6264,16 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "libfuzzer-sys" +version = "0.4.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" +dependencies = [ + "arbitrary", + "cc", +] + [[package]] name = "libloading" version = "0.8.9" @@ -6290,6 +6412,15 @@ version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" +[[package]] +name = "loop9" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" +dependencies = [ + "imgref", +] + [[package]] name = "lopdf" version = "0.40.0" @@ -6383,6 +6514,16 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" +[[package]] +name = "maybe-rayon" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" +dependencies = [ + "cfg-if", + "rayon", +] + [[package]] name = "md-5" version = "0.10.6" @@ -6621,6 +6762,15 @@ dependencies = [ "libc", ] +[[package]] +name = "no_std_io2" +version = "0.9.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" +dependencies = [ + "memchr", +] + [[package]] name = "node_resolver" version = "0.65.0" @@ -6682,6 +6832,12 @@ dependencies = [ "nom 8.0.0", ] +[[package]] +name = "noop_proc_macro" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" + [[package]] name = "nostr" version = "0.44.3" @@ -7342,6 +7498,12 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" +[[package]] +name = "pastey" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" + [[package]] name = "pastey" version = "0.2.3" @@ -7749,19 +7911,6 @@ dependencies = [ "time", ] -[[package]] -name = "png" -version = "0.17.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" -dependencies = [ - "bitflags 1.3.2", - "crc32fast", - "fdeflate", - "flate2", - "miniz_oxide", -] - [[package]] name = "png" version = "0.18.1" @@ -7952,6 +8101,25 @@ dependencies = [ "windows 0.62.2", ] +[[package]] +name = "profiling" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" +dependencies = [ + "profiling-procmacros", +] + +[[package]] +name = "profiling-procmacros" +version = "1.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" +dependencies = [ + "quote", + "syn 2.0.117", +] + [[package]] name = "prost" version = "0.14.3" @@ -8301,6 +8469,56 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" +[[package]] +name = "rav1e" +version = "0.8.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" +dependencies = [ + "aligned-vec", + "arbitrary", + "arg_enum_proc_macro", + "arrayvec", + "av-scenechange", + "av1-grain", + "bitstream-io", + "built", + "cfg-if", + "interpolate_name", + "itertools 0.14.0", + "libc", + "libfuzzer-sys", + "log", + "maybe-rayon", + "new_debug_unreachable", + "noop_proc_macro", + "num-derive", + "num-traits", + "paste", + "profiling", + "rand 0.9.4", + "rand_chacha 0.9.0", + "simd_helpers", + "thiserror 2.0.18", + "v_frame", + "wasm-bindgen", +] + +[[package]] +name = "ravif" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" +dependencies = [ + "avif-serialize", + "imgref", + "loop9", + "quick-error", + "rav1e", + "rayon", + "rgb", +] + [[package]] name = "raw-cpuid" version = "11.6.0" @@ -8633,7 +8851,7 @@ dependencies = [ "hyper", "hyper-util", "oauth2", - "pastey", + "pastey 0.2.3", "pin-project-lite", "process-wrap", "rand 0.10.0", @@ -9630,6 +9848,15 @@ dependencies = [ "simdutf8", ] +[[package]] +name = "simd_helpers" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" +dependencies = [ + "quote", +] + [[package]] name = "simdutf8" version = "0.1.5" @@ -11045,17 +11272,6 @@ dependencies = [ "cfg-if", ] -[[package]] -name = "tiff" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" -dependencies = [ - "flate2", - "jpeg-decoder", - "weezl", -] - [[package]] name = "tiff" version = "0.11.3" @@ -12222,6 +12438,17 @@ dependencies = [ "which 6.0.3", ] +[[package]] +name = "v_frame" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" +dependencies = [ + "aligned-vec", + "num-traits", + "wasm-bindgen", +] + [[package]] name = "v_htmlescape" version = "0.15.8" @@ -13270,6 +13497,12 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7008a9d8ba97a7e47d9b2df63fcdb8dade303010c5a7cd5bf2469d4da6eba673" +[[package]] +name = "y4m" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" + [[package]] name = "yaml-rust2" version = "0.11.0" diff --git a/crates/goose-mcp/Cargo.toml b/crates/goose-mcp/Cargo.toml index f028f094b2ac..340a14c32726 100644 --- a/crates/goose-mcp/Cargo.toml +++ b/crates/goose-mcp/Cargo.toml @@ -36,7 +36,7 @@ include_dir = { workspace = true } once_cell = { workspace = true } lopdf = "0.40.0" docx-rs = "0.4.20" -image = { version = "0.24.9", features = ["jpeg"] } +image = { version = "0.25.10", features = ["jpeg"] } umya-spreadsheet = "2.2.3" shell-words = { workspace = true } process-wrap = { version = "9.1.0", features = ["std"] } From ce004f7475e3eaa370684c304f100f50b12053fa Mon Sep 17 00:00:00 2001 From: fre$h Date: Sat, 23 May 2026 14:42:32 -0400 Subject: [PATCH 03/66] fix(agents): serialize per-session agent creation to stop duplicate MCP init (#9357) Signed-off-by: fresh3nough --- crates/goose/src/execution/manager.rs | 255 +++++++++++++++++++++++++- 1 file changed, 245 insertions(+), 10 deletions(-) diff --git a/crates/goose/src/execution/manager.rs b/crates/goose/src/execution/manager.rs index d590ca53a51b..302dd7d9571d 100644 --- a/crates/goose/src/execution/manager.rs +++ b/crates/goose/src/execution/manager.rs @@ -10,7 +10,7 @@ use lru::LruCache; use std::collections::HashMap; use std::num::NonZeroUsize; use std::sync::Arc; -use tokio::sync::{OnceCell, RwLock}; +use tokio::sync::{Mutex, OnceCell, RwLock}; use tokio_util::sync::CancellationToken; use tracing::{debug, info}; @@ -25,6 +25,15 @@ pub struct AgentManager { default_provider: Arc>>>, default_mode: GooseMode, cancel_tokens: Arc>>, + /// Per-session creation locks. When `get_or_create_agent` misses the + /// `sessions` cache it acquires the per-session lock before doing the + /// expensive work (provider restore, MCP extension initialization) so + /// concurrent callers for the same session never race into doing the + /// work twice. Entries are inserted on demand and pruned when the + /// session is removed *or* evicted by the LRU; the underlying + /// `Arc>` stays alive as long as any caller still holds it, + /// even after the HashMap entry is removed. + creation_locks: Arc>>>>, } impl AgentManager { @@ -46,6 +55,7 @@ impl AgentManager { default_provider: Arc::new(RwLock::new(None)), default_mode, cancel_tokens: Arc::new(RwLock::new(HashMap::new())), + creation_locks: Arc::new(Mutex::new(HashMap::new())), }; Ok(manager) @@ -89,6 +99,7 @@ impl AgentManager { } pub async fn get_or_create_agent(&self, session_id: String) -> Result> { + // Fast path: agent already cached. { let mut sessions = self.sessions.write().await; if let Some(existing) = sessions.get(&session_id) { @@ -96,10 +107,62 @@ impl AgentManager { } } + // Slow path: serialize creation per session so concurrent callers + // (e.g. start_agent's background extension-loading task and a + // resume_agent request racing through the frontend) cannot each + // construct their own Agent and independently send `initialize` to + // every MCP server. See issue #9031. + let creation_lock = { + let mut locks = self.creation_locks.lock().await; + Arc::clone( + locks + .entry(session_id.clone()) + .or_insert_with(|| Arc::new(Mutex::new(()))), + ) + }; + let creation_guard = creation_lock.lock().await; + + // Funnel the fallible work through a helper so we can prune the + // per-session creation lock on every error exit. Without this + // the provider-setup path (update_provider / update_mode) could + // bail out via `?`, leaving a permanent `creation_locks` entry + // for a session that never made it into the LRU cache and that + // no one will ever call `remove_session` on. + let result = self.create_agent_locked(&session_id).await; + + if result.is_err() { + // Release BOTH the guard and our local Arc clone of the + // creation lock before pruning. `prune_creation_lock` + // gates removal on `Arc::strong_count == 1`; if we kept + // `creation_lock` alive the count would still be at least + // two (HashMap + this local) and the failed session would + // leak its lock entry forever. In-flight waiters keep the + // Arc alive on their own and prune correctly skips while + // they hold it. + drop(creation_guard); + drop(creation_lock); + self.prune_creation_lock(&session_id).await; + } + + result + } + + /// Slow-path body for `get_or_create_agent`. Must be called with the + /// per-session creation lock held by the caller. + async fn create_agent_locked(&self, session_id: &str) -> Result> { + // Re-check under the creation lock: another caller may have + // finished creating the agent while we were waiting. + { + let mut sessions = self.sessions.write().await; + if let Some(existing) = sessions.get(session_id) { + return Ok(Arc::clone(existing)); + } + } + let mut mode = self.default_mode; let permission_manager = PermissionManager::instance(); - if let Ok(session) = self.session_manager.get_session(&session_id, false).await { + if let Ok(session) = self.session_manager.get_session(session_id, false).await { mode = session.goose_mode; info!(goose_mode = %mode, session_id = %session_id, "Session loaded"); } @@ -116,7 +179,7 @@ impl AgentManager { ); let agent = Arc::new(Agent::with_config(config)); - if let Ok(session) = self.session_manager.get_session(&session_id, false).await { + if let Ok(session) = self.session_manager.get_session(session_id, false).await { if session.provider_name.is_some() { info!( "Restoring evicted session {} (provider: {:?})", @@ -136,21 +199,53 @@ impl AgentManager { if agent.provider().await.is_err() { if let Some(provider) = &*self.default_provider.read().await { agent - .update_provider(Arc::clone(provider), &session_id) + .update_provider(Arc::clone(provider), session_id) .await?; provider - .update_mode(&session_id, mode) + .update_mode(session_id, mode) .await .map_err(|e| anyhow::anyhow!("Failed to propagate mode to provider: {}", e))?; } } let mut sessions = self.sessions.write().await; - if let Some(existing) = sessions.get(&session_id) { - Ok(Arc::clone(existing)) - } else { - sessions.put(session_id, agent.clone()); - Ok(agent) + if let Some(existing) = sessions.get(session_id) { + return Ok(Arc::clone(existing)); + } + // `push` returns the LRU-evicted entry when the cache is at + // capacity, which `put` does not surface. We need the evicted + // key so we can also drop its creation lock below, otherwise the + // `creation_locks` HashMap would grow without bound in long-lived + // processes that churn through many sessions. + let evicted = sessions + .push(session_id.to_string(), agent.clone()) + .map(|(k, _)| k); + drop(sessions); + + if let Some(evicted_id) = evicted { + self.prune_creation_lock(&evicted_id).await; + } + + Ok(agent) + } + + /// Drop the per-session creation lock for `session_id` if no other + /// caller is currently holding a clone of its `Arc`. Holding the + /// `creation_locks` mutex while we both check `Arc::strong_count` and + /// remove guarantees no new waiter can race in between the check and + /// the removal: any new caller would need to acquire the outer mutex + /// first to clone the inner `Arc`. + /// + /// If a waiter is still in flight (strong_count > 1) we leave the + /// entry in place so the in-flight callers continue to serialize + /// through the same lock; a later removal or eviction will sweep it. + async fn prune_creation_lock(&self, session_id: &str) { + let mut locks = self.creation_locks.lock().await; + let in_use = locks + .get(session_id) + .is_some_and(|lock| Arc::strong_count(lock) > 1); + if !in_use { + locks.remove(session_id); } } @@ -162,6 +257,12 @@ impl AgentManager { sessions .pop(session_id) .ok_or_else(|| anyhow::anyhow!("Session {} not found", session_id))?; + drop(sessions); + // Best-effort prune of the per-session creation lock so the + // HashMap doesn't grow unbounded. Any caller still holding a + // clone of the Arc keeps the underlying Mutex alive until it + // releases its guard. + self.prune_creation_lock(session_id).await; info!("Removed session {}", session_id); Ok(()) } @@ -428,6 +529,140 @@ mod tests { assert!(result.unwrap_err().to_string().contains("not found")); } + #[tokio::test] + async fn test_remove_session_prunes_creation_lock() { + // remove_session must drop the per-session creation lock so the + // HashMap doesn't grow unboundedly. + let temp_dir = TempDir::new().unwrap(); + let manager = create_test_manager(&temp_dir).await; + let session = String::from("to-be-removed"); + + manager.get_or_create_agent(session.clone()).await.unwrap(); + assert_eq!(manager.creation_locks.lock().await.len(), 1); + + manager.remove_session(&session).await.unwrap(); + assert!( + manager.creation_locks.lock().await.is_empty(), + "remove_session must prune the creation lock for the removed session" + ); + } + + #[tokio::test] + async fn test_failed_creation_prunes_creation_lock() { + // Regression test for the Codex review note on PR #9357: when the + // provider-setup path in `create_agent_locked` returns Err, the + // outer `get_or_create_agent` must also drop its local Arc clone + // of the creation lock before pruning. Otherwise + // `Arc::strong_count` stays > 1 and the failed session leaks a + // permanent entry in `creation_locks`. + use async_trait::async_trait; + use rmcp::model::Tool; + + use crate::conversation::message::Message; + use crate::model::ModelConfig; + use crate::providers::base::{MessageStream, Provider, ProviderUsage, Usage}; + use crate::providers::errors::ProviderError; + + struct FailingProvider; + + #[async_trait] + impl Provider for FailingProvider { + fn get_name(&self) -> &str { + "failing-test-provider" + } + + fn get_model_config(&self) -> ModelConfig { + ModelConfig::new_or_fail("test-model") + } + + async fn stream( + &self, + _model_config: &ModelConfig, + _session_id: &str, + _system: &str, + _messages: &[Message], + _tools: &[Tool], + ) -> std::result::Result { + Ok(crate::providers::base::stream_from_single_message( + Message::assistant().with_text("unused"), + ProviderUsage::new("failing-test-provider".into(), Usage::default()), + )) + } + + async fn update_mode( + &self, + _session_id: &str, + _mode: GooseMode, + ) -> std::result::Result<(), ProviderError> { + Err(ProviderError::ExecutionError( + "intentional failure for test".into(), + )) + } + } + + let temp_dir = TempDir::new().unwrap(); + let manager = create_test_manager(&temp_dir).await; + manager + .set_default_provider(Arc::new(FailingProvider)) + .await; + + let session_id = String::from("failed-creation-test"); + let result = manager.get_or_create_agent(session_id.clone()).await; + + assert!( + result.is_err(), + "expected provider mode-update failure to propagate" + ); + assert!( + manager.creation_locks.lock().await.is_empty(), + "creation_locks must be empty after a failed agent creation" + ); + assert!( + !manager.has_session(&session_id).await, + "failed creation must not insert into the LRU cache" + ); + } + + #[tokio::test] + async fn test_lru_eviction_prunes_creation_lock() { + // Sessions can disappear from the LRU cache without going through + // remove_session. When that happens the matching creation lock + // must also be pruned, otherwise long-lived processes that churn + // through many session IDs would accumulate stale lock entries + // even though only `max_sessions` agents remain cached. + let temp_dir = TempDir::new().unwrap(); + let session_manager = Arc::new(SessionManager::new(temp_dir.path().to_path_buf())); + let schedule_path = temp_dir.path().join("schedule.json"); + let manager = AgentManager::new( + session_manager, + schedule_path, + Some(2), + GooseMode::default(), + ) + .await + .unwrap(); + + manager.get_or_create_agent("a".into()).await.unwrap(); + manager.get_or_create_agent("b".into()).await.unwrap(); + assert_eq!(manager.creation_locks.lock().await.len(), 2); + + // Inserting a third session evicts the LRU entry ("a"). + manager.get_or_create_agent("c".into()).await.unwrap(); + + let locks = manager.creation_locks.lock().await; + assert_eq!( + locks.len(), + 2, + "creation_locks must stay bounded by max_sessions after LRU eviction" + ); + assert!( + !locks.contains_key("a"), + "LRU-evicted session's creation lock should be pruned" + ); + assert!(locks.contains_key("b")); + assert!(locks.contains_key("c")); + } + #[test_case(GooseMode::Approve ; "approve")] #[test_case(GooseMode::Chat ; "chat")] #[test_case(GooseMode::SmartApprove ; "smart_approve")] From c4d64d1a8395b3acfdff2e3264a08ab6a9ade753 Mon Sep 17 00:00:00 2001 From: Angie Jones Date: Sun, 24 May 2026 18:33:44 -0500 Subject: [PATCH 04/66] Fix desktop chat search session limiting (#9366) Signed-off-by: Angie Jones --- crates/goose-server/src/routes/session.rs | 24 +- .../goose/src/session/chat_history_search.rs | 134 +++++++++ crates/goose/src/session/session_manager.rs | 269 ++++++++++++++++++ 3 files changed, 406 insertions(+), 21 deletions(-) diff --git a/crates/goose-server/src/routes/session.rs b/crates/goose-server/src/routes/session.rs index 5b0d89a88abe..0ae6f9a75f74 100644 --- a/crates/goose-server/src/routes/session.rs +++ b/crates/goose-server/src/routes/session.rs @@ -685,9 +685,9 @@ async fn search_sessions( .and_then(|s| chrono::DateTime::parse_from_rfc3339(&s).ok()) .map(|dt| dt.with_timezone(&chrono::Utc)); - let search_results = state + let sessions = state .session_manager() - .search_chat_history( + .search_chat_sessions( query, Some(limit), after_date, @@ -698,23 +698,5 @@ async fn search_sessions( .await .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - // Get full Session objects for matching session IDs - let session_ids: Vec = search_results - .results - .into_iter() - .map(|r| r.session_id) - .collect(); - - let all_sessions = state - .session_manager() - .list_sessions() - .await - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; - - let matching_sessions: Vec = all_sessions - .into_iter() - .filter(|s| session_ids.contains(&s.id)) - .collect(); - - Ok(Json(matching_sessions)) + Ok(Json(sessions)) } diff --git a/crates/goose/src/session/chat_history_search.rs b/crates/goose/src/session/chat_history_search.rs index c06abb846459..22a89807e792 100644 --- a/crates/goose/src/session/chat_history_search.rs +++ b/crates/goose/src/session/chat_history_search.rs @@ -302,3 +302,137 @@ impl<'a> ChatHistorySearch<'a> { } } } + +pub(crate) struct ChatSessionSearch<'a> { + pool: &'a Pool, + query: &'a str, + limit: usize, + after_date: Option>, + before_date: Option>, + exclude_session_id: Option, + session_types: Vec, +} + +impl<'a> ChatSessionSearch<'a> { + pub fn new( + pool: &'a Pool, + query: &'a str, + limit: Option, + after_date: Option>, + before_date: Option>, + exclude_session_id: Option, + session_types: Vec, + ) -> Self { + Self { + pool, + query, + limit: limit.unwrap_or(10), + after_date, + before_date, + exclude_session_id, + session_types, + } + } + + pub async fn execute(self) -> Result> { + let keywords = self.parse_keywords(); + if keywords.is_empty() { + return Ok(Vec::new()); + } + + let sql = self.build_sql(&keywords); + let mut query_builder = sqlx::query_scalar::<_, String>(&sql); + + for keyword in &keywords { + query_builder = query_builder.bind(keyword); + } + + if let Some(after) = self.after_date { + query_builder = query_builder.bind(after); + } + if let Some(before) = self.before_date { + query_builder = query_builder.bind(before); + } + + if let Some(exclude_id) = &self.exclude_session_id { + query_builder = query_builder.bind(exclude_id); + } + + for t in &self.session_types { + query_builder = query_builder.bind(t.to_string()); + } + + query_builder = query_builder.bind(self.limit as i64); + + Ok(query_builder.fetch_all(self.pool).await?) + } + + fn parse_keywords(&self) -> Vec { + self.query + .split_whitespace() + .map(|word| format!("%{}%", word.to_lowercase())) + .collect() + } + + fn build_sql(&self, keywords: &[String]) -> String { + let mut sql = String::from( + r#" + SELECT s.id + FROM sessions s + WHERE EXISTS ( + SELECT 1 + FROM messages m + WHERE m.session_id = s.id + AND EXISTS ( + SELECT 1 FROM json_each(m.content_json) + WHERE json_extract(value, '$.type') = 'text' + AND ( + "#, + ); + + for (i, _) in keywords.iter().enumerate() { + if i > 0 { + sql.push_str(" OR "); + } + sql.push_str("LOWER(json_extract(value, '$.text')) LIKE ?"); + } + + sql.push_str( + r#" + ) + ) + "#, + ); + + if self.after_date.is_some() { + sql.push_str(" AND m.timestamp >= ?"); + } + if self.before_date.is_some() { + sql.push_str(" AND m.timestamp <= ?"); + } + + sql.push_str( + r#" + ) + "#, + ); + + if self.exclude_session_id.is_some() { + sql.push_str(" AND s.id != ?"); + } + + if !self.session_types.is_empty() { + let placeholders: String = self + .session_types + .iter() + .map(|_| "?") + .collect::>() + .join(", "); + sql.push_str(&format!(" AND s.session_type IN ({})", placeholders)); + } + + sql.push_str(" ORDER BY s.updated_at DESC, s.id DESC LIMIT ?"); + + sql + } +} diff --git a/crates/goose/src/session/session_manager.rs b/crates/goose/src/session/session_manager.rs index 6b04183d3ed5..20caf461d609 100644 --- a/crates/goose/src/session/session_manager.rs +++ b/crates/goose/src/session/session_manager.rs @@ -472,6 +472,27 @@ impl SessionManager { .await } + pub async fn search_chat_sessions( + &self, + query: &str, + limit: Option, + after_date: Option>, + before_date: Option>, + exclude_session_id: Option, + session_types: Vec, + ) -> Result> { + self.storage + .search_chat_sessions( + query, + limit, + after_date, + before_date, + exclude_session_id, + session_types, + ) + .await + } + pub async fn update_message_metadata(id: &str, message_id: &str, f: F) -> Result<()> where F: FnOnce( @@ -1845,6 +1866,41 @@ impl SessionStorage { .await } + async fn search_chat_sessions( + &self, + query: &str, + limit: Option, + after_date: Option>, + before_date: Option>, + exclude_session_id: Option, + session_types: Vec, + ) -> Result> { + use crate::session::chat_history_search::ChatSessionSearch; + + let pool = self.pool().await?; + let session_ids = ChatSessionSearch::new( + pool, + query, + limit, + after_date, + before_date, + exclude_session_id, + session_types, + ) + .execute() + .await?; + + let mut sessions = Vec::with_capacity(session_ids.len()); + for session_id in session_ids { + match self.get_session(&session_id, false).await { + Ok(session) => sessions.push(session), + Err(err) if err.to_string() == "Session not found" => continue, + Err(err) => return Err(err), + } + } + Ok(sessions) + } + async fn update_message_metadata( &self, session_id: &str, @@ -2015,6 +2071,219 @@ mod tests { } } + async fn add_message_at(sm: &SessionManager, session_id: &str, text: &str, timestamp: &str) { + sm.add_message(session_id, &Message::user().with_text(text)) + .await + .unwrap(); + + let pool = sm.storage().pool().await.unwrap(); + let timestamp = chrono::DateTime::parse_from_rfc3339(timestamp).unwrap(); + let timestamp_string = timestamp.format("%Y-%m-%d %H:%M:%S").to_string(); + + sqlx::query( + "UPDATE messages SET timestamp = ?, created_timestamp = ? WHERE id = (SELECT MAX(id) FROM messages WHERE session_id = ?)", + ) + .bind(×tamp_string) + .bind(timestamp.timestamp()) + .bind(session_id) + .execute(pool) + .await + .unwrap(); + } + + async fn create_search_session( + sm: &SessionManager, + name: &str, + session_type: SessionType, + updated_at: &str, + messages: &[(&str, &str)], + ) -> String { + let session = sm + .create_session( + PathBuf::from("/tmp/search-test"), + name.to_string(), + session_type, + GooseMode::default(), + ) + .await + .unwrap(); + + for (text, timestamp) in messages { + add_message_at(sm, &session.id, text, timestamp).await; + } + set_sessions_updated_at(sm, std::slice::from_ref(&session.id), updated_at).await; + + session.id + } + + #[tokio::test] + async fn test_search_chat_history_preserves_message_limited_behavior() { + let temp_dir = TempDir::new().unwrap(); + let sm = SessionManager::new(temp_dir.path().to_path_buf()); + + let _older_target = create_search_session( + &sm, + "Older target", + SessionType::User, + "2026-05-01T00:00:00Z", + &[( + "does Acme have an email address for John Doe", + "2026-05-01T00:00:00Z", + )], + ) + .await; + + let newer_noise = create_search_session( + &sm, + "Newer noise", + SessionType::User, + "2026-05-22T00:00:00Z", + &[ + ("Acme person name looking for Acme", "2026-05-22T00:00:00Z"), + ( + "another Acme person name looking for Acme", + "2026-05-22T00:01:00Z", + ), + ], + ) + .await; + + let results = sm + .search_chat_history("Acme", Some(2), None, None, None, vec![SessionType::User]) + .await + .unwrap(); + + assert_eq!(results.results.len(), 1); + assert_eq!(results.results[0].session_id, newer_noise); + assert_eq!(results.results[0].messages.len(), 2); + } + + #[tokio::test] + async fn test_search_chat_sessions_limits_distinct_sessions() { + let temp_dir = TempDir::new().unwrap(); + let sm = SessionManager::new(temp_dir.path().to_path_buf()); + + let older_target = create_search_session( + &sm, + "Older target", + SessionType::User, + "2026-05-01T00:00:00Z", + &[( + "does Acme have an email address for John Doe", + "2026-05-01T00:00:00Z", + )], + ) + .await; + + let newer_noise = create_search_session( + &sm, + "Newer noise", + SessionType::User, + "2026-05-22T00:00:00Z", + &[ + ("Acme person name looking for Acme", "2026-05-22T00:00:00Z"), + ( + "another Acme person name looking for Acme", + "2026-05-22T00:01:00Z", + ), + ], + ) + .await; + + let results = sm + .search_chat_sessions("Acme", Some(2), None, None, None, vec![SessionType::User]) + .await + .unwrap(); + let ids = results + .iter() + .map(|session| session.id.clone()) + .collect::>(); + + assert_eq!(ids, vec![newer_noise, older_target]); + } + + #[tokio::test] + async fn test_search_chat_sessions_applies_all_filters() { + let temp_dir = TempDir::new().unwrap(); + let sm = SessionManager::new(temp_dir.path().to_path_buf()); + + let excluded = create_search_session( + &sm, + "Excluded user", + SessionType::User, + "2026-05-20T00:00:00Z", + &[("Acme John excluded session", "2026-05-15T00:00:00Z")], + ) + .await; + + let scheduled_target = create_search_session( + &sm, + "Scheduled target", + SessionType::Scheduled, + "2026-05-19T00:00:00Z", + &[( + "John appears in scheduled Acme work", + "2026-05-16T00:00:00Z", + )], + ) + .await; + + let user_target = create_search_session( + &sm, + "User target", + SessionType::User, + "2026-05-18T00:00:00Z", + &[( + "Acme has an email address question for John Doe", + "2026-05-14T00:00:00Z", + )], + ) + .await; + + let _before_window = create_search_session( + &sm, + "Before window", + SessionType::User, + "2026-05-17T00:00:00Z", + &[("Acme John before date window", "2026-05-09T00:00:00Z")], + ) + .await; + + let _wrong_type = create_search_session( + &sm, + "ACP target", + SessionType::Acp, + "2026-05-16T00:00:00Z", + &[("Acme John wrong session type", "2026-05-15T00:00:00Z")], + ) + .await; + + let after = chrono::DateTime::parse_from_rfc3339("2026-05-10T00:00:00Z") + .unwrap() + .with_timezone(&chrono::Utc); + let before = chrono::DateTime::parse_from_rfc3339("2026-05-17T00:00:00Z") + .unwrap() + .with_timezone(&chrono::Utc); + + let results = sm + .search_chat_sessions( + "Acme John", + Some(10), + Some(after), + Some(before), + Some(excluded), + vec![SessionType::User, SessionType::Scheduled], + ) + .await + .unwrap(); + let ids = results + .iter() + .map(|session| session.id.clone()) + .collect::>(); + + assert_eq!(ids, vec![scheduled_target, user_target]); + } + async fn expected_session_list_ids(sm: &SessionManager, session_ids: &[String]) -> Vec { let mut sessions = Vec::new(); for session_id in session_ids { From e1cc44f7ec827150784b5e934d8882c355cfc1b8 Mon Sep 17 00:00:00 2001 From: Douwe Osinga Date: Sun, 24 May 2026 20:55:58 -0400 Subject: [PATCH 05/66] Build summon instructions per turn (#9329) Signed-off-by: Douwe Osinga Co-authored-by: Douwe Osinga --- .../goose/src/agents/platform_extensions/summon.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/goose/src/agents/platform_extensions/summon.rs b/crates/goose/src/agents/platform_extensions/summon.rs index e1edf2607a39..2c67d38dd716 100644 --- a/crates/goose/src/agents/platform_extensions/summon.rs +++ b/crates/goose/src/agents/platform_extensions/summon.rs @@ -428,11 +428,8 @@ impl Drop for SummonClient { impl SummonClient { pub fn new(context: PlatformExtensionContext) -> Result { - let instructions = build_subagent_instructions(context.session.as_deref()); - let info = InitializeResult::new(ServerCapabilities::builder().enable_tools().build()) - .with_server_info(Implementation::new(EXTENSION_NAME, "1.0.0").with_title("Summon")) - .with_instructions(instructions); + .with_server_info(Implementation::new(EXTENSION_NAME, "1.0.0").with_title("Summon")); Ok(Self { info, @@ -1744,6 +1741,15 @@ impl McpClientTrait for SummonClient { Some(&self.info) } + fn get_instructions(&self) -> Option { + let instructions = build_subagent_instructions(self.context.session.as_deref()); + if instructions.is_empty() { + None + } else { + Some(instructions) + } + } + async fn subscribe(&self) -> mpsc::Receiver { let (tx, rx) = mpsc::channel(16); self.notification_subscribers.lock().await.push(tx); From ba16de9738ec5bc319adbddeb9dd6523475fc7c0 Mon Sep 17 00:00:00 2001 From: Angie Jones Date: Sun, 24 May 2026 21:38:39 -0500 Subject: [PATCH 06/66] docs: stats update (#9410) --- documentation/src/pages/index.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/documentation/src/pages/index.tsx b/documentation/src/pages/index.tsx index 051eff70e599..5db2d352e209 100644 --- a/documentation/src/pages/index.tsx +++ b/documentation/src/pages/index.tsx @@ -36,12 +36,12 @@ function HeroSection() {
- 38k+ + 45k+ GitHub stars
- 400+ + 500+ Contributors
From a11843a1e8bb8939bde36c2ca01077a3d7f4c21a Mon Sep 17 00:00:00 2001 From: Douwe Osinga Date: Tue, 26 May 2026 09:49:18 -0400 Subject: [PATCH 07/66] Simplify UI customization (#9353) Signed-off-by: Douwe Osinga Co-authored-by: Douwe Osinga --- ui/desktop/src/components/BaseChat.tsx | 22 +- ui/desktop/src/components/ChatInput.tsx | 506 ++++++++---------- ui/desktop/src/components/ChatInputCard.tsx | 24 + ui/desktop/src/components/Hub.tsx | 166 +++--- .../src/components/Layout/AppLayout.tsx | 300 ++--------- .../components/Layout/CondensedRenderer.tsx | 377 ------------- .../components/Layout/ExpandedRenderer.tsx | 341 ------------ .../components/Layout/NavigationContext.tsx | 191 +------ .../src/components/Layout/NavigationPanel.tsx | 368 +++++++------ ui/desktop/src/components/Layout/constants.ts | 14 +- .../navigation/ChatSessionsDropdown.tsx | 118 ---- .../Layout/navigation/NavigationOverlay.tsx | 52 -- .../Layout/navigation/SessionsList.tsx | 162 ------ .../src/components/Layout/navigation/index.ts | 3 - .../src/components/Layout/navigation/types.ts | 54 -- .../bottom_menu/ContextWindowIndicator.tsx | 26 +- .../components/bottom_menu/CostTracker.tsx | 69 +-- .../components/bottom_menu/DirSwitcher.tsx | 4 +- ui/desktop/src/components/common/Greeting.tsx | 132 ----- .../conversation/ChatHistorySearch.tsx | 393 -------------- .../components/sessions/SessionsInsights.tsx | 391 -------------- .../src/components/sessions/SessionsView.tsx | 2 +- .../settings/app/AppSettingsSection.tsx | 96 +--- .../app/NavigationCustomizationSettings.tsx | 202 ------- .../settings/app/NavigationModeSelector.tsx | 78 --- .../app/NavigationPositionSelector.tsx | 64 --- .../settings/app/NavigationStyleSelector.tsx | 78 --- ui/desktop/src/hooks/use-text-animator.tsx | 245 --------- ui/desktop/src/hooks/useNavigationItems.ts | 52 +- ui/desktop/src/hooks/useNavigationSessions.ts | 2 +- ui/desktop/src/i18n/messages/en.json | 215 +------- ui/desktop/src/i18n/messages/zh-CN.json | 184 +------ ui/desktop/src/main.ts | 10 +- ui/desktop/src/utils/settings.ts | 2 - 34 files changed, 757 insertions(+), 4186 deletions(-) create mode 100644 ui/desktop/src/components/ChatInputCard.tsx delete mode 100644 ui/desktop/src/components/Layout/CondensedRenderer.tsx delete mode 100644 ui/desktop/src/components/Layout/ExpandedRenderer.tsx delete mode 100644 ui/desktop/src/components/Layout/navigation/ChatSessionsDropdown.tsx delete mode 100644 ui/desktop/src/components/Layout/navigation/NavigationOverlay.tsx delete mode 100644 ui/desktop/src/components/Layout/navigation/SessionsList.tsx delete mode 100644 ui/desktop/src/components/Layout/navigation/index.ts delete mode 100644 ui/desktop/src/components/Layout/navigation/types.ts delete mode 100644 ui/desktop/src/components/common/Greeting.tsx delete mode 100644 ui/desktop/src/components/conversation/ChatHistorySearch.tsx delete mode 100644 ui/desktop/src/components/sessions/SessionsInsights.tsx delete mode 100644 ui/desktop/src/components/settings/app/NavigationCustomizationSettings.tsx delete mode 100644 ui/desktop/src/components/settings/app/NavigationModeSelector.tsx delete mode 100644 ui/desktop/src/components/settings/app/NavigationPositionSelector.tsx delete mode 100644 ui/desktop/src/components/settings/app/NavigationStyleSelector.tsx delete mode 100644 ui/desktop/src/hooks/use-text-animator.tsx diff --git a/ui/desktop/src/components/BaseChat.tsx b/ui/desktop/src/components/BaseChat.tsx index c973507d31c9..bee64543e95d 100644 --- a/ui/desktop/src/components/BaseChat.tsx +++ b/ui/desktop/src/components/BaseChat.tsx @@ -13,6 +13,7 @@ import LoadingGoose from './LoadingGoose'; import ProgressiveMessageList from './ProgressiveMessageList'; import { MainPanelLayout } from './Layout/MainPanelLayout'; import ChatInput from './ChatInput'; +import { ChatInputCard } from './ChatInputCard'; import { ScrollArea, ScrollAreaHandle } from './ui/scroll-area'; import { useFileDrop } from '../hooks/useFileDrop'; import { Message } from '../api'; @@ -364,13 +365,13 @@ export default function BaseChat({ return (
{renderHeader && renderHeader()} -
-
+
+

{intl.formatMessage(i18n.failedToLoadSession)}

@@ -395,7 +396,7 @@ export default function BaseChat({ return (
@@ -403,7 +404,7 @@ export default function BaseChat({ {renderHeader && renderHeader()} {/* Chat container with sticky recipe header */} -
+
{/* Goose watermark - top right */}
-
-
+ {recipe && isActiveSession && ( diff --git a/ui/desktop/src/components/ChatInput.tsx b/ui/desktop/src/components/ChatInput.tsx index e75d5b8e97e7..2e5eb0e71451 100644 --- a/ui/desktop/src/components/ChatInput.tsx +++ b/ui/desktop/src/components/ChatInput.tsx @@ -1,18 +1,18 @@ import { AppEvents } from '../constants/events'; import React, { useRef, useState, useEffect, useMemo, useCallback } from 'react'; -import { Bug, ChefHat, ScrollText } from 'lucide-react'; +import { ArrowUp, Bug, ScrollText } from 'lucide-react'; import { Tooltip, TooltipContent, TooltipTrigger } from './ui/Tooltip'; import { Button } from './ui/button'; import type { View } from '../utils/navigationUtils'; import Stop from './ui/Stop'; -import { Attach, Send, Close, Microphone } from './icons'; +import { Attach, Close, Microphone } from './icons'; import { ChatState } from '../types/chatState'; import debounce from 'lodash/debounce'; import { LocalMessageStorage } from '../utils/localMessageStorage'; import { DirSwitcher } from './bottom_menu/DirSwitcher'; import ModelsBottomBar from './settings/models/bottom_bar/ModelsBottomBar'; -import { BottomMenuModeSelection } from './bottom_menu/BottomMenuModeSelection'; import { BottomMenuExtensionSelection } from './bottom_menu/BottomMenuExtensionSelection'; +import { cn } from '../utils'; import { AlertType, useAlerts } from './alerts'; import { useConfig } from './ConfigContext'; import { useModelAndProvider } from './ModelAndProviderContext'; @@ -28,16 +28,12 @@ import { MessageQueue, QueuedMessage } from './MessageQueue'; import { detectInterruption } from '../utils/interruptionDetector'; import { DiagnosticsModal } from './ui/Diagnostics'; import { getSession, Message } from '../api'; -import CreateRecipeFromSessionModal from './recipes/CreateRecipeFromSessionModal'; -import CreateEditRecipeModal from './recipes/CreateEditRecipeModal'; import { getInitialWorkingDir } from '../utils/workingDir'; import { getPredefinedModelsFromEnv } from './settings/models/predefinedModelsUtils'; import { trackFileAttached, trackVoiceDictation, trackDiagnosticsOpened, - trackCreateRecipeOpened, - trackEditRecipeOpened, } from '../utils/analytics'; import { getNavigationShortcutText } from '../utils/keyboardShortcuts'; import { UserInput, ImageData } from '../types/message'; @@ -224,8 +220,8 @@ export default function ChatInput({ accumulatedCost, messages = [], disableAnimation = false, - recipe, - recipeId, + recipe: _recipe, + recipeId: _recipeId, recipeAccepted, initialPrompt, toolCount, @@ -307,10 +303,23 @@ export default function ChatInput({ const [tokenLimit, setTokenLimit] = useState(TOKEN_LIMIT_DEFAULT); const [isTokenLimitLoaded, setIsTokenLimitLoaded] = useState(false); const [diagnosticsOpen, setDiagnosticsOpen] = useState(false); - const [showCreateRecipeModal, setShowCreateRecipeModal] = useState(false); - const [showEditRecipeModal, setShowEditRecipeModal] = useState(false); const [sessionWorkingDir, setSessionWorkingDir] = useState(null); + // Hide non-essential bottom-bar controls when the chat input is narrow. + // Only the model selector, mic, and send button remain visible. + const bottomBarRef = useRef(null); + const [isBottomBarNarrow, setIsBottomBarNarrow] = useState(false); + useEffect(() => { + const el = bottomBarRef.current; + if (!el) return; + const observer = new ResizeObserver((entries) => { + const width = entries[0]?.contentRect.width ?? 0; + setIsBottomBarNarrow(width < 480); + }); + observer.observe(el); + return () => observer.disconnect(); + }, []); + useEffect(() => { if (!sessionId) { return; @@ -1193,7 +1202,7 @@ export default function ChatInput({ } }; - const onFormSubmit = (e: React.FormEvent) => { + const onFormSubmit = (e: React.FormEvent | React.MouseEvent) => { e.preventDefault(); if (isLoading && hasSubmittableContent) { handleInterruptionAndQueue(); @@ -1396,7 +1405,7 @@ export default function ChatInput({ isFocused ? 'border-border-secondary hover:border-border-secondary' : 'border-border-primary hover:border-border-primary' - } bg-background-primary z-10 rounded-t-2xl`} + } bg-background-primary z-10`} data-drop-zone="true" onDrop={handleLocalDrop} onDragOver={handleLocalDragOver} @@ -1446,154 +1455,30 @@ export default function ChatInput({ minHeight: `${minTextareaHeight}px`, maxHeight: `${maxHeight}px`, overflowY: 'auto', - paddingRight: dictationProvider ? '180px' : '120px', }} className="w-full outline-none border-none focus:ring-0 bg-transparent px-3 pt-3 pb-1.5 text-sm resize-none text-text-primary placeholder:text-text-secondary" /> - {/* Inline action buttons - absolutely positioned on the right */} -
- {/* Microphone button - show only if provider is selected */} - {dictationProvider && ( - <> - {!isEnabled ? ( - - - - - - - - {dictationProvider === 'openai' ? ( -

- OpenAI API key is not configured. Set it up in Settings {'>'}{' '} - Models. -

- ) : dictationProvider === 'elevenlabs' ? ( -

- ElevenLabs API key is not configured. Set it up in Settings {'>'}{' '} - Chat {'>'} Voice Dictation. -

- ) : dictationProvider === 'local' ? ( -

- Local Whisper model not found. Download a model in{' '} - Settings > Dictation > Local (Offline) -

- ) : ( -

Dictation provider is not properly configured.

- )} -
-
- ) : ( - - - - - -

- Voice dictation - {isRecording ? '' : ' • Say "submit" to send'} -

-
-
+ {/* Recording/transcribing status indicator (floats above the bottom bar) */} + {(isRecording || isTranscribing) && ( +
+ + {isRecording && ( + + + Listening + )} - - )} - - {/* Send/Stop button */} - {isLoading && !hasSubmittableContent ? ( - - ) : ( - - - - + {isRecording && isTranscribing && } + {isTranscribing && ( + + + Transcribing - - -

{getSubmitButtonTooltip()}

-
-
- )} - - {/* Recording/transcribing status indicator - positioned above the button row */} - {(isRecording || isTranscribing) && ( -
- - {isRecording && ( - - - Listening - - )} - {isRecording && isTranscribing && } - {isTranscribing && ( - - - Transcribing - - )} - -
- )} -
+ )} + +
+ )}
@@ -1698,129 +1583,206 @@ export default function ChatInput({
)} - {/* Secondary actions and controls row below input */} -
- { - setSessionWorkingDir(newDir); - if (onWorkingDirChange) { - onWorkingDirChange(newDir); - } - }} - onRestartStart={() => setChatState?.(ChatState.RestartingAgent)} - onRestartEnd={() => setChatState?.(ChatState.Idle)} - /> -
+ {/* Bottom action bar. Single flat row; no dividers. Left side: model + + working dir. Right side (after spacer): context indicator, + extensions, diagnostics, attach, mic, send. When the bar is narrow + (e.g. on a small window), the secondary controls drop out so the + model selector + send button always stay visible. */} +
+ {/* Left: model selector */} - - - - Attach file +
+ +
-
- {/* Model selector, mode selector, alerts, summarize button */} -
- {/* Cost Tracker */} - {COST_TRACKING_ENABLED && ( - <> -
- -
- - )} - { + setSessionWorkingDir(newDir); + if (onWorkingDirChange) { + onWorkingDirChange(newDir); + } + }} + onRestartStart={() => setChatState?.(ChatState.RestartingAgent)} + onRestartEnd={() => setChatState?.(ChatState.Idle)} /> - -
- + + {!isBottomBarNarrow && ( + <> + {/* Right: cost tracker (when enabled) */} + {COST_TRACKING_ENABLED && ( + -
-
-
- -
- - {sessionId && messages.length > 0 && ( - <> -
-
- - - - - - {recipe - ? intl.formatMessage(i18n.viewEditRecipe) - : intl.formatMessage(i18n.createRecipeFromSession)} - - -
- - )} - {sessionId && ( + )} + + {/* Right: context window indicator */} + + + {/* Right: extension selector */} + + + {/* Right: diagnostics */} + {sessionId && ( + + + + + Generate diagnostics bundle + + )} + + {/* Right: attach */} - Generate diagnostics bundle + Attach file - )} -
+ + )} + + {/* Right: mic — ghost icon, no background when idle */} + {dictationProvider && ( + + + + + + {!isEnabled ? ( +

Dictation not configured (Settings)

+ ) : ( +

Voice dictation{isRecording ? '' : ' • Say "submit" to send'}

+ )} +
+
+ )} + + {/* Right: send / stop — soft gray circle with up-arrow */} + {isLoading && !hasSubmittableContent ? ( + + ) : ( + + + + + + + +

{getSubmitButtonTooltip()}

+
+
+ )} {sessionId && diagnosticsOpen && ( - {sessionId && showCreateRecipeModal && ( - setShowCreateRecipeModal(false)} - sessionId={sessionId} - /> - )} - - {recipe && showEditRecipeModal && ( - setShowEditRecipeModal(false)} - recipe={recipe} - recipeId={recipeId} - /> - )}
); diff --git a/ui/desktop/src/components/ChatInputCard.tsx b/ui/desktop/src/components/ChatInputCard.tsx new file mode 100644 index 000000000000..f632f4af296b --- /dev/null +++ b/ui/desktop/src/components/ChatInputCard.tsx @@ -0,0 +1,24 @@ +import React from 'react'; +import { cn } from '../utils'; + +/** + * Shared visual wrapper for the ChatInput. + * + * Both the Hub (empty-chat landing) and the BaseChat (active session) + * present ChatInput as a floating rounded outlined card on the canvas. + * Centralizing it here keeps the look in sync and gives a single place + * to tweak the recipe. + */ +export const ChatInputCard: React.FC<{ + className?: string; + children: React.ReactNode; +}> = ({ className, children }) => ( +
+ {children} +
+); diff --git a/ui/desktop/src/components/Hub.tsx b/ui/desktop/src/components/Hub.tsx index 3cdeb21aa6fa..0fd38f32d892 100644 --- a/ui/desktop/src/components/Hub.tsx +++ b/ui/desktop/src/components/Hub.tsx @@ -1,47 +1,70 @@ -import { AppEvents } from '../constants/events'; /** * Hub Component * - * The Hub is the main landing page and entry point for the Goose Desktop application. - * It serves as the welcome screen where users can start new conversations. - * - * Key Responsibilities: - * - Displays SessionInsights to show session statistics and recent chats - * - Provides a ChatInput for users to start new conversations - * - Creates a new session and navigates to Pair with the session ID - * - Shows loading state while session is being created - * - * Navigation Flow: - * Hub (input submission) → Create Session → Pair (with session ID and initial message) + * The empty-chat landing screen. Visually it's "Pair with no messages yet" — + * a large time + greeting above a centered, narrower ChatInput. Submitting + * creates a session and navigates to /pair so the rest of the chat lifecycle + * lives there. */ -import { useEffect, useRef, useState } from 'react'; -import { SessionInsights } from './sessions/SessionsInsights'; +import { useEffect, useMemo, useRef, useState } from 'react'; +import { defineMessages, useIntl } from '../i18n'; +import { AppEvents } from '../constants/events'; import ChatInput from './ChatInput'; +import { ChatInputCard } from './ChatInputCard'; import { ChatState } from '../types/chatState'; import 'react-toastify/dist/ReactToastify.css'; import { View, ViewOptions } from '../utils/navigationUtils'; import { useConfig } from './ConfigContext'; import { - getExtensionConfigsWithOverrides, clearExtensionOverrides, + getExtensionConfigsWithOverrides, } from '../store/extensionOverrides'; import { getInitialWorkingDir } from '../utils/workingDir'; import { createSession } from '../sessions'; import LoadingGoose from './LoadingGoose'; import { UserInput } from '../types/message'; +const i18n = defineMessages({ + goodMorning: { id: 'hub.goodMorning', defaultMessage: 'Good morning' }, + goodAfternoon: { id: 'hub.goodAfternoon', defaultMessage: 'Good afternoon' }, + goodEvening: { id: 'hub.goodEvening', defaultMessage: 'Good evening' }, +}); + +function useClock(): { time: string; meridiem: string; hour: number } { + const [now, setNow] = useState(() => new Date()); + useEffect(() => { + const interval = setInterval(() => setNow(new Date()), 30_000); + return () => clearInterval(interval); + }, []); + + const hour = now.getHours(); + const minutes = now.getMinutes(); + const meridiem = hour >= 12 ? 'PM' : 'AM'; + const displayHour = ((hour + 11) % 12) + 1; + const time = `${displayHour}:${String(minutes).padStart(2, '0')}`; + return { time, meridiem, hour }; +} + export default function Hub({ setView, }: { setView: (view: View, viewOptions?: ViewOptions) => void; }) { + const intl = useIntl(); const { extensionsList } = useConfig(); const [workingDir, setWorkingDir] = useState(getInitialWorkingDir()); const [isCreatingSession, setIsCreatingSession] = useState(false); const inputRef = useRef(null); + const { time, meridiem, hour } = useClock(); - // rAF is more reliable than autoFocus across async render boundaries (Suspense, OnboardingGuard, etc.) + const greeting = useMemo(() => { + if (hour < 12) return intl.formatMessage(i18n.goodMorning); + if (hour < 18) return intl.formatMessage(i18n.goodAfternoon); + return intl.formatMessage(i18n.goodEvening); + }, [intl, hour]); + + // rAF is more reliable than autoFocus across async render boundaries. useEffect(() => { const frameId = requestAnimationFrame(() => { inputRef.current?.focus(); @@ -51,67 +74,74 @@ export default function Hub({ const handleSubmit = async (input: UserInput) => { const { msg: userMessage, images } = input; - if ((images.length > 0 || userMessage.trim()) && !isCreatingSession) { - const extensionConfigs = getExtensionConfigsWithOverrides(extensionsList); - clearExtensionOverrides(); - setIsCreatingSession(true); + if (!(images.length > 0 || userMessage.trim()) || isCreatingSession) return; + + const extensionConfigs = getExtensionConfigsWithOverrides(extensionsList); + clearExtensionOverrides(); + setIsCreatingSession(true); - try { - const session = await createSession(workingDir, { - extensionConfigs, - allExtensions: extensionConfigs.length > 0 ? undefined : extensionsList, - }); + try { + const session = await createSession(workingDir, { + extensionConfigs, + allExtensions: extensionConfigs.length > 0 ? undefined : extensionsList, + }); - window.dispatchEvent(new CustomEvent(AppEvents.SESSION_CREATED)); - window.dispatchEvent( - new CustomEvent(AppEvents.ADD_ACTIVE_SESSION, { - detail: { sessionId: session.id, initialMessage: { msg: userMessage, images } }, - }) - ); + window.dispatchEvent(new CustomEvent(AppEvents.SESSION_CREATED)); + window.dispatchEvent( + new CustomEvent(AppEvents.ADD_ACTIVE_SESSION, { + detail: { sessionId: session.id, initialMessage: { msg: userMessage, images } }, + }) + ); - setView('pair', { - disableAnimation: true, - resumeSessionId: session.id, - initialMessage: { msg: userMessage, images }, - }); - } catch (error) { - console.error('Failed to create session:', error); - setIsCreatingSession(false); - } + setView('pair', { + disableAnimation: true, + resumeSessionId: session.id, + initialMessage: { msg: userMessage, images }, + }); + } catch (error) { + console.error('Failed to create session:', error); + setIsCreatingSession(false); } }; return ( -
-
- - {isCreatingSession && ( -
- -
- )} -
+
+
+
+ + {time} + + {meridiem} +
+

{greeting}

-
- {}} - initialValue="" - setView={setView} - totalTokens={0} - accumulatedInputTokens={0} - accumulatedOutputTokens={0} - droppedFiles={[]} - onFilesProcessed={() => {}} - messages={[]} - disableAnimation={false} - toolCount={0} - onWorkingDirChange={setWorkingDir} - inputRef={inputRef} - /> + + {}} + initialValue="" + setView={setView} + totalTokens={0} + accumulatedInputTokens={0} + accumulatedOutputTokens={0} + droppedFiles={[]} + onFilesProcessed={() => {}} + messages={[]} + disableAnimation={false} + toolCount={0} + onWorkingDirChange={setWorkingDir} + inputRef={inputRef} + /> +
+ + {isCreatingSession && ( +
+ +
+ )}
); } diff --git a/ui/desktop/src/components/Layout/AppLayout.tsx b/ui/desktop/src/components/Layout/AppLayout.tsx index 845d525ae9b6..e28a47eb2274 100644 --- a/ui/desktop/src/components/Layout/AppLayout.tsx +++ b/ui/desktop/src/components/Layout/AppLayout.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useRef, useState } from 'react'; +import React, { useEffect, useState } from 'react'; import { IpcRendererEvent } from 'electron'; import { Outlet, useLocation } from 'react-router-dom'; import { motion } from 'framer-motion'; @@ -14,10 +14,6 @@ import { cn } from '../../utils'; import { UserInput } from '../../types/message'; const i18n = defineMessages({ - closeNavigation: { - id: 'appLayout.closeNavigation', - defaultMessage: 'Close navigation', - }, openNavigation: { id: 'appLayout.openNavigation', defaultMessage: 'Open navigation', @@ -53,91 +49,7 @@ const AppLayoutContent: React.FC = ({ activeSessions }) = return () => window.electron.off('fullscreen-change', handler); }, [safeIsMacOS]); - const { - isNavExpanded, - setIsNavExpanded, - effectiveNavigationMode, - effectiveNavigationStyle, - navigationPosition, - isHorizontalNav, - isCondensedIconOnly, - } = useNavigationContext(); - - const [navWidth, setNavWidth] = useState(null); - const navWidthRef = useRef(null); - - useEffect(() => { - window.electron.getSetting('navExpandedWidth').then((delta) => { - if (delta !== null) { - setNavWidth( - Math.min( - NAV_DIMENSIONS.MAX_NAV_WIDTH, - Math.max(NAV_DIMENSIONS.MIN_NAV_WIDTH, NAV_DIMENSIONS.CONDENSED_WIDTH + delta) - ) - ); - } - }); - }, []); - - const isResizable = - !isHorizontalNav && !isCondensedIconOnly && effectiveNavigationMode === 'push' && isNavExpanded; - - const dragStateRef = useRef<{ startX: number; startWidth: number; direction: 1 | -1 } | null>( - null - ); - const navRef = useRef(null); - - const onMouseMove = useCallback((e: MouseEvent) => { - if (!dragStateRef.current) return; - const delta = (e.clientX - dragStateRef.current.startX) * dragStateRef.current.direction; - const newWidth = Math.min( - NAV_DIMENSIONS.MAX_NAV_WIDTH, - Math.max(NAV_DIMENSIONS.MIN_NAV_WIDTH, dragStateRef.current.startWidth + delta) - ); - navWidthRef.current = newWidth; - setNavWidth(newWidth); - }, []); - - const onMouseUp = useCallback(() => { - dragStateRef.current = null; - document.body.style.cursor = ''; - document.body.style.userSelect = ''; - window.removeEventListener('mousemove', onMouseMove); - window.removeEventListener('mouseup', onMouseUp); - if (navWidthRef.current !== null) { - window.electron.setSetting( - 'navExpandedWidth', - navWidthRef.current - NAV_DIMENSIONS.CONDENSED_WIDTH - ); - } - }, [onMouseMove]); - - const onHandleMouseDown = useCallback( - (e: React.MouseEvent) => { - e.preventDefault(); - const currentWidth = - navRef.current?.getBoundingClientRect().width ?? NAV_DIMENSIONS.CONDENSED_WIDTH; - dragStateRef.current = { - startX: e.clientX, - startWidth: currentWidth, - direction: navigationPosition === 'right' ? -1 : 1, - }; - document.body.style.cursor = 'col-resize'; - document.body.style.userSelect = 'none'; - window.addEventListener('mousemove', onMouseMove); - window.addEventListener('mouseup', onMouseUp); - }, - [navigationPosition, onMouseMove, onMouseUp] - ); - - useEffect(() => { - return () => { - window.removeEventListener('mousemove', onMouseMove); - window.removeEventListener('mouseup', onMouseUp); - document.body.style.cursor = ''; - document.body.style.userSelect = ''; - }; - }, [onMouseMove, onMouseUp]); + const { isNavExpanded, setIsNavExpanded } = useNavigationContext(); if (!chatContext) { throw new Error('AppLayoutContent must be used within ChatProvider'); @@ -145,181 +57,57 @@ const AppLayoutContent: React.FC = ({ activeSessions }) = const { setChat } = chatContext; - // Hide the titlebar drag region when nav is at the top in push mode, - // since the nav occupies that space and the drag region blocks interactions - const isPushTopNav = - effectiveNavigationMode === 'push' && navigationPosition === 'top' && isNavExpanded; - React.useEffect(() => { - const dragRegion = document.querySelector('.titlebar-drag-region') as HTMLElement | null; - if (!dragRegion) return; - if (isPushTopNav) { - dragRegion.style.display = 'none'; - } else { - dragRegion.style.display = ''; - } - return () => { - dragRegion.style.display = ''; - }; - }, [isPushTopNav]); - const needsTrafficLightInset = safeIsMacOS && !isFullScreen; const headerPadding = needsTrafficLightInset ? 'pl-[96px]' : 'pl-4'; const headerTop = needsTrafficLightInset ? 'top-[15px]' : 'top-[11px]'; - // Determine flex direction based on navigation position (for push mode) - const getLayoutClass = () => { - if (effectiveNavigationMode === 'overlay') { - return 'flex-row'; - } - - switch (navigationPosition) { - case 'top': - return 'flex-col'; - case 'bottom': - return 'flex-col-reverse'; - case 'left': - return 'flex-row'; - case 'right': - return 'flex-row-reverse'; - default: - return 'flex-row'; - } - }; - - // Main content area - const mainContent = ( -
-
- - {/* Always render ChatSessionsContainer to keep SSE connections alive. - When navigating away from /pair, hide it with CSS */} -
- -
-
-
- ); - return ( -
- {/* Header controls */} -
- {/* Navigation trigger */} - -
- - {/* Main content with navigation */} -
- {/* Push mode navigation (inline) with animation */} - {effectiveNavigationMode === 'push' && ( - setIsNavExpanded(true)} + className="no-drag hover:!bg-background-tertiary" + variant="ghost" + size="xs" + title={intl.formatMessage(i18n.openNavigation)} > -
- -
- {isResizable && ( -
-
-
- )} - - )} + + +
+ )} - {/* Main content */} - {mainContent} + {/* Main content with navigation. Shared white canvas; the sidebar is a + rounded outlined card floating on it with breathing room. */} +
+ +
+ +
+
+ + {/* Main content — no border / no card; just flows on the canvas. */} +
+ + {/* Always render ChatSessionsContainer to keep SSE connections alive. + When navigating away from /pair, hide it with CSS */} +
+ +
+
- - {/* Overlay mode navigation */} - {effectiveNavigationMode === 'overlay' && }
); }; diff --git a/ui/desktop/src/components/Layout/CondensedRenderer.tsx b/ui/desktop/src/components/Layout/CondensedRenderer.tsx deleted file mode 100644 index f6f7feb3c9b4..000000000000 --- a/ui/desktop/src/components/Layout/CondensedRenderer.tsx +++ /dev/null @@ -1,377 +0,0 @@ -import React, { useState } from 'react'; -import { GripVertical, ChevronDown, ChevronRight, Plus } from 'lucide-react'; -import { motion } from 'framer-motion'; -import { defineMessages, useIntl } from '../../i18n'; -import { cn } from '../../utils'; -import { DropdownMenu, DropdownMenuTrigger } from '../ui/dropdown-menu'; -import { ChatSessionsDropdown, SessionsList } from './navigation'; -import { ChatHistorySearch } from '../conversation/ChatHistorySearch'; -import type { NavigationRendererProps } from './navigation/types'; -import { getNavItemLabel } from '../../hooks/useNavigationItems'; - -const i18n = defineMessages({ - newChat: { - id: 'condensedRenderer.newChat', - defaultMessage: 'New Chat', - }, -}); - -export const CondensedRenderer: React.FC = ({ - isOverlayMode, - navigationPosition, - isCondensedIconOnly, - className, - visibleItems, - isActive, - recentSessions, - activeSessionId, - onNavClick, - onNewChat, - onSessionClick, - onFetchSessions, - getSessionStatus, - clearUnread, - isChatExpanded, - onToggleChatExpanded, - drag, - navFocusRef, -}) => { - const intl = useIntl(); - const [chatPopoverOpen, setChatPopoverOpen] = useState(false); - - const isVertical = navigationPosition === 'left' || navigationPosition === 'right'; - const isTopPosition = navigationPosition === 'top'; - const isBottomPosition = navigationPosition === 'bottom'; - - return ( - - {/* Top spacer (vertical only) */} - {isVertical && ( -
- )} - - {/* Left spacer (horizontal top position only) */} - {!isVertical && isTopPosition && ( -
- )} - - {/* Search bar — skip mount entirely in icon-only mode so the - document-level Cmd/Ctrl+K handler inside ChatHistorySearch - does not intercept the shortcut when no UI is visible. */} - {!isCondensedIconOnly && ( -
- -
- )} - - {/* Navigation items */} - {isVertical ? ( -
- {visibleItems.map((item, index) => { - const Icon = item.icon; - const active = isActive(item.path); - const isDragging = drag.draggedItem === item.id; - const isDragOver = drag.dragOverItem === item.id; - const isChatItem = item.id === 'chat'; - - return ( - drag.onDragStart(e as unknown as React.DragEvent, item.id)} - onDragOver={(e) => drag.onDragOver(e as unknown as React.DragEvent, item.id)} - onDrop={(e) => drag.onDrop(e as unknown as React.DragEvent, item.id)} - onDragEnd={drag.onDragEnd} - initial={{ opacity: 0 }} - animate={{ opacity: isDragging ? 0.5 : 1 }} - transition={{ duration: 0.15, delay: index * 0.02 }} - className={cn( - 'relative cursor-move group', - isCondensedIconOnly ? 'flex-shrink-0' : 'w-full flex-shrink-0', - isDragOver && 'ring-2 ring-blue-500 rounded-lg', - isChatItem && !isCondensedIconOnly && 'overflow-visible' - )} - > -
- {/* Chat item with dropdown in icon-only mode */} - {isChatItem && isCondensedIconOnly ? ( - - - - - onNavClick('/sessions')} - /> - - ) : ( - <> - {isChatItem && !isCondensedIconOnly ? ( -
- -
- -
- - - {getNavItemLabel(item, intl)} - -
- {isChatExpanded ? ( - - ) : ( - - )} -
-
- {!isChatExpanded && ( - { - e.stopPropagation(); - onNewChat(); - }} - whileHover={{ scale: 1.1 }} - whileTap={{ scale: 0.95 }} - className={cn( - 'absolute -right-9 top-1/2 -translate-y-1/2 p-1.5 rounded-md z-10', - 'opacity-0 group-hover:opacity-100 transition-opacity', - 'bg-background-tertiary hover:bg-background-inverse hover:text-text-inverse', - 'flex items-center justify-center' - )} - title={intl.formatMessage(i18n.newChat)} - > - - - )} -
- ) : ( - onNavClick(item.path)} - whileHover={{ scale: 1.02 }} - whileTap={{ scale: 0.98 }} - className={cn( - 'flex flex-row items-center gap-2', - 'relative rounded-lg transition-colors duration-200 no-drag', - isCondensedIconOnly - ? 'justify-center p-2.5' - : 'w-full pl-2 pr-4 py-2.5', - active - ? 'bg-background-inverse text-text-inverse' - : 'bg-background-primary hover:bg-background-tertiary' - )} - > - {!isCondensedIconOnly && ( -
- -
- )} - - {!isCondensedIconOnly && ( - - {getNavItemLabel(item, intl)} - - )} - {!isCondensedIconOnly && item.getTag && ( -
- - {item.getTag()} - -
- )} -
- )} - - )} - {isChatItem && !isCondensedIconOnly && ( - onNavClick('/sessions')} - /> - )} -
-
- ); - })} - -
-
- ) : ( - /* Horizontal navigation items */ - visibleItems.map((item, index) => { - const Icon = item.icon; - const active = isActive(item.path); - const isDragging = drag.draggedItem === item.id; - const isDragOver = drag.dragOverItem === item.id; - const isChatItem = item.id === 'chat'; - - return ( - drag.onDragStart(e as unknown as React.DragEvent, item.id)} - onDragOver={(e) => drag.onDragOver(e as unknown as React.DragEvent, item.id)} - onDrop={(e) => drag.onDrop(e as unknown as React.DragEvent, item.id)} - onDragEnd={drag.onDragEnd} - initial={{ opacity: 0 }} - animate={{ opacity: isDragging ? 0.5 : 1 }} - transition={{ duration: 0.15, delay: index * 0.02 }} - className={cn( - 'relative cursor-move group flex-shrink-0', - isDragOver && 'ring-2 ring-blue-500 rounded-lg', - isChatItem && !isCondensedIconOnly && 'overflow-visible' - )} - > -
- {isChatItem ? ( - - - - - - {getNavItemLabel(item, intl)} - - - - onNavClick('/sessions')} - /> - - ) : ( - onNavClick(item.path)} - whileHover={{ scale: 1.02 }} - whileTap={{ scale: 0.98 }} - className={cn( - 'flex flex-row items-center gap-2 px-3 py-2.5', - 'relative rounded-lg transition-colors duration-200 no-drag', - active - ? 'bg-background-inverse text-text-inverse' - : 'bg-background-primary hover:bg-background-tertiary' - )} - > - - - {getNavItemLabel(item, intl)} - - - )} -
-
- ); - }) - )} - - {/* Right spacer (horizontal only) */} - {!isVertical && ( -
- )} - - ); -}; diff --git a/ui/desktop/src/components/Layout/ExpandedRenderer.tsx b/ui/desktop/src/components/Layout/ExpandedRenderer.tsx deleted file mode 100644 index d69a169437bb..000000000000 --- a/ui/desktop/src/components/Layout/ExpandedRenderer.tsx +++ /dev/null @@ -1,341 +0,0 @@ -import React, { useState, useEffect, useRef } from 'react'; -import { GripVertical } from 'lucide-react'; -import { motion, AnimatePresence } from 'framer-motion'; -import { Z_INDEX } from './constants'; -import { cn } from '../../utils'; -import { DropdownMenu, DropdownMenuTrigger } from '../ui/dropdown-menu'; -import { ChatSessionsDropdown } from './navigation'; -import { ChatHistorySearch } from '../conversation/ChatHistorySearch'; -import type { NavigationRendererProps } from './navigation/types'; -import { useIntl } from '../../i18n'; -import { getNavItemLabel } from '../../hooks/useNavigationItems'; - -export const ExpandedRenderer: React.FC = ({ - isNavExpanded, - isOverlayMode, - navigationPosition, - onClose, - className, - visibleItems, - isActive, - recentSessions, - activeSessionId, - onNavClick, - onNewChat, - onSessionClick, - getSessionStatus, - clearUnread, - drag, - navFocusRef, -}) => { - const intl = useIntl(); - const [chatDropdownOpen, setChatDropdownOpen] = useState(false); - const [gridColumns, setGridColumns] = useState(2); - const [gridMeasured, setGridMeasured] = useState(false); - const [tilesReady, setTilesReady] = useState(false); - const [isClosing, setIsClosing] = useState(false); - const prevIsNavExpandedRef = useRef(isNavExpanded); - const gridRef = useRef(null); - - // Detect when nav is closing - useEffect(() => { - if (prevIsNavExpandedRef.current && !isNavExpanded) { - setIsClosing(true); - setTilesReady(false); - } else if (!prevIsNavExpandedRef.current && isNavExpanded) { - setIsClosing(false); - } - prevIsNavExpandedRef.current = isNavExpanded; - }, [isNavExpanded]); - - // Delay tiles animation until panel opens - useEffect(() => { - if (!isNavExpanded) { - setTilesReady(false); - return; - } - const timeoutId = setTimeout(() => setTilesReady(true), 150); - return () => clearTimeout(timeoutId); - }, [isNavExpanded]); - - // Track grid columns for spacer tiles - useEffect(() => { - if (!isNavExpanded) { - setGridMeasured(false); - return; - } - - setGridMeasured(false); - let rafId: number; - - const updateGridColumns = () => { - if (!gridRef.current) return; - const parent = gridRef.current.parentElement; - if (!parent) return; - - const parentStyle = window.getComputedStyle(parent); - const availableWidth = - parent.clientWidth - - parseFloat(parentStyle.paddingLeft) - - parseFloat(parentStyle.paddingRight); - - const minSize = navigationPosition === 'left' || navigationPosition === 'right' ? 140 : 160; - const gap = isOverlayMode ? 12 : 2; - const cols = Math.max(1, Math.floor((availableWidth + gap) / (minSize + gap))); - - setGridColumns(cols); - setGridMeasured(true); - }; - - const timeoutId = setTimeout(() => { - rafId = requestAnimationFrame(updateGridColumns); - }, 100); - - const resizeObserver = new ResizeObserver(() => { - cancelAnimationFrame(rafId); - rafId = requestAnimationFrame(updateGridColumns); - }); - - const parent = gridRef.current?.parentElement; - if (parent) resizeObserver.observe(parent); - - return () => { - clearTimeout(timeoutId); - cancelAnimationFrame(rafId); - resizeObserver.disconnect(); - }; - }, [isNavExpanded, navigationPosition, isOverlayMode]); - - const isPushTopNav = !isOverlayMode && navigationPosition === 'top'; - const dragStyle = isPushTopNav ? ({ WebkitAppRegion: 'drag' } as React.CSSProperties) : undefined; - const showContent = !isClosing || isOverlayMode; - - const navContent = ( - - {showContent ? ( -
- {/* Search bar spanning full width */} -
- -
- - {visibleItems.map((item, index) => { - const Icon = item.icon; - const active = isActive(item.path); - const isDragging = drag.draggedItem === item.id; - const isDragOver = drag.dragOverItem === item.id; - const isChatItem = item.id === 'chat'; - - if (isChatItem) { - return ( - - drag.onDragStart(e as unknown as React.DragEvent, item.id)} - onDragOver={(e) => drag.onDragOver(e as unknown as React.DragEvent, item.id)} - onDrop={(e) => drag.onDrop(e as unknown as React.DragEvent, item.id)} - onDragEnd={drag.onDragEnd} - initial={{ opacity: 0 }} - animate={{ opacity: tilesReady ? (isDragging ? 0.5 : 1) : 0 }} - transition={{ duration: 0.15, delay: tilesReady ? index * 0.03 : 0 }} - className={cn( - 'relative cursor-move group', - isDragOver && 'ring-2 ring-blue-500 rounded-lg' - )} - > -
- - -
-
- -
- {item.getTag && ( -
- - {item.getTag()} - -
- )} -
- -

{getNavItemLabel(item, intl)}

-
-
-
-
-
- onNavClick('/sessions')} - /> -
-
- ); - } - - return ( - drag.onDragStart(e as unknown as React.DragEvent, item.id)} - onDragOver={(e) => drag.onDragOver(e as unknown as React.DragEvent, item.id)} - onDrop={(e) => drag.onDrop(e as unknown as React.DragEvent, item.id)} - onDragEnd={drag.onDragEnd} - initial={{ opacity: 0 }} - animate={{ opacity: tilesReady ? (isDragging ? 0.5 : 1) : 0 }} - transition={{ duration: 0.15, delay: tilesReady ? index * 0.03 : 0 }} - className={cn( - 'relative cursor-move group', - isDragOver && 'ring-2 ring-blue-500 rounded-lg' - )} - > - - - - - ); - })} - - {/* Spacer tiles */} - {!isOverlayMode && - gridMeasured && - gridColumns >= 2 && - Array.from({ - length: - navigationPosition === 'left' || navigationPosition === 'right' - ? ((gridColumns - (visibleItems.length % gridColumns)) % gridColumns) + - gridColumns * 6 - : (gridColumns - (visibleItems.length % gridColumns)) % gridColumns, - }).map((_, index) => ( -
-
-
- ))} -
- ) : null} - - ); - - // Expanded overlay uses its own AnimatePresence - if (isOverlayMode) { - return ( - - {isNavExpanded && ( -
- -
-
-
{navContent}
-
-
-
- )} -
- ); - } - - return navContent; -}; diff --git a/ui/desktop/src/components/Layout/NavigationContext.tsx b/ui/desktop/src/components/Layout/NavigationContext.tsx index 37d33a4ed6a2..0a14d7d63c38 100644 --- a/ui/desktop/src/components/Layout/NavigationContext.tsx +++ b/ui/desktop/src/components/Layout/NavigationContext.tsx @@ -8,48 +8,16 @@ import React, { useState, } from 'react'; -export type NavigationMode = 'push' | 'overlay'; -export type NavigationStyle = 'expanded' | 'condensed'; -export type NavigationPosition = 'top' | 'bottom' | 'left' | 'right'; - -export interface NavigationPreferences { - itemOrder: string[]; - enabledItems: string[]; -} - -export const DEFAULT_ITEM_ORDER = [ - 'home', - 'chat', - 'recipes', - 'skills', - 'apps', - 'scheduler', - 'extensions', - 'settings', -]; - -export const DEFAULT_ENABLED_ITEMS = [...DEFAULT_ITEM_ORDER]; - -const RESPONSIVE_BREAKPOINT = 700; +/** + * When the window is narrower than this many CSS pixels, we auto-collapse + * the sidebar. The user can re-expand it via the menu button; it will only + * auto-collapse again if they go below the threshold from above. + */ +const NARROW_WINDOW_THRESHOLD = 700; interface NavigationContextValue { isNavExpanded: boolean; setIsNavExpanded: (expanded: boolean) => void; - navigationMode: NavigationMode; - setNavigationMode: (mode: NavigationMode) => void; - effectiveNavigationMode: NavigationMode; - navigationStyle: NavigationStyle; - setNavigationStyle: (style: NavigationStyle) => void; - effectiveNavigationStyle: NavigationStyle; - navigationPosition: NavigationPosition; - setNavigationPosition: (position: NavigationPosition) => void; - preferences: NavigationPreferences; - updatePreferences: (prefs: NavigationPreferences) => void; - isHorizontalNav: boolean; - isCondensedIconOnly: boolean; - isOverlayMode: boolean; - isChatExpanded: boolean; - setIsChatExpanded: (expanded: boolean) => void; } const NavigationContext = createContext(null); @@ -76,97 +44,11 @@ export const NavigationProvider: React.FC = ({ children return stored !== 'false'; }); - const [isBelowBreakpoint, setIsBelowBreakpoint] = useState( - () => window.innerWidth < RESPONSIVE_BREAKPOINT - ); - - const [navigationMode, setNavigationModeState] = useState(() => { - const stored = localStorage.getItem('navigation_mode'); - return (stored as NavigationMode) || 'push'; - }); - - const [navigationStyle, setNavigationStyleState] = useState(() => { - const stored = localStorage.getItem('navigation_style'); - return (stored as NavigationStyle) || 'condensed'; - }); - - const [navigationPosition, setNavigationPositionState] = useState(() => { - const stored = localStorage.getItem('navigation_position'); - return (stored as NavigationPosition) || 'left'; - }); - - const [preferences, setPreferences] = useState(() => { - const stored = localStorage.getItem('navigation_preferences'); - if (stored) { - try { - const parsed = JSON.parse(stored); - // Only backfill truly new default IDs (not previously known to the user). - // Using itemOrder as the source of truth ensures items the user - // intentionally disabled stay disabled. - const newIds = DEFAULT_ITEM_ORDER.filter( - (id) => !parsed.itemOrder?.includes(id) - ); - return { - itemOrder: [...(parsed.itemOrder ?? []), ...newIds], - enabledItems: [...(parsed.enabledItems ?? []), ...newIds], - }; - } catch { - console.error('Failed to parse navigation preferences'); - } - } - return { - itemOrder: DEFAULT_ITEM_ORDER, - enabledItems: DEFAULT_ENABLED_ITEMS, - }; - }); - - const [isChatExpanded, setIsChatExpandedState] = useState(() => { - const stored = localStorage.getItem('navigation_chat_expanded'); - return stored !== 'false'; - }); - - useEffect(() => { - const mql = window.matchMedia(`(max-width: ${RESPONSIVE_BREAKPOINT - 1}px)`); - const onChange = () => setIsBelowBreakpoint(window.innerWidth < RESPONSIVE_BREAKPOINT); - mql.addEventListener('change', onChange); - setIsBelowBreakpoint(window.innerWidth < RESPONSIVE_BREAKPOINT); - return () => mql.removeEventListener('change', onChange); - }, []); - const setIsNavExpanded = useCallback((expanded: boolean) => { setIsNavExpandedState(expanded); localStorage.setItem('navigation_expanded', String(expanded)); }, []); - const setNavigationMode = useCallback((mode: NavigationMode) => { - setNavigationModeState(mode); - localStorage.setItem('navigation_mode', mode); - window.dispatchEvent(new CustomEvent('navigation-mode-changed', { detail: { mode } })); - }, []); - - const setNavigationStyle = useCallback((style: NavigationStyle) => { - setNavigationStyleState(style); - localStorage.setItem('navigation_style', style); - window.dispatchEvent(new CustomEvent('navigation-style-changed', { detail: { style } })); - }, []); - - const setNavigationPosition = useCallback((position: NavigationPosition) => { - setNavigationPositionState(position); - localStorage.setItem('navigation_position', position); - window.dispatchEvent(new CustomEvent('navigation-position-changed', { detail: { position } })); - }, []); - - const updatePreferences = useCallback((newPrefs: NavigationPreferences) => { - setPreferences(newPrefs); - localStorage.setItem('navigation_preferences', JSON.stringify(newPrefs)); - window.dispatchEvent(new CustomEvent('navigation-preferences-updated', { detail: newPrefs })); - }, []); - - const setIsChatExpanded = useCallback((expanded: boolean) => { - setIsChatExpandedState(expanded); - localStorage.setItem('navigation_chat_expanded', String(expanded)); - }, []); - const isNavExpandedRef = useRef(isNavExpanded); useEffect(() => { isNavExpandedRef.current = isNavExpanded; @@ -182,53 +64,32 @@ export const NavigationProvider: React.FC = ({ children }; }, [setIsNavExpanded]); + // Auto-collapse the sidebar when the window becomes narrow. Track the + // previous width so we only fire on the downward crossing — the user can + // re-expand it manually without us fighting them on the next resize. useEffect(() => { - const handleModeChange = (e: Event) => setNavigationModeState((e as CustomEvent).detail.mode); - const handleStyleChange = (e: Event) => - setNavigationStyleState((e as CustomEvent).detail.style); - const handlePositionChange = (e: Event) => - setNavigationPositionState((e as CustomEvent).detail.position); - const handlePrefsChange = (e: Event) => setPreferences((e as CustomEvent).detail); - - window.addEventListener('navigation-mode-changed', handleModeChange); - window.addEventListener('navigation-style-changed', handleStyleChange); - window.addEventListener('navigation-position-changed', handlePositionChange); - window.addEventListener('navigation-preferences-updated', handlePrefsChange); - - return () => { - window.removeEventListener('navigation-mode-changed', handleModeChange); - window.removeEventListener('navigation-style-changed', handleStyleChange); - window.removeEventListener('navigation-position-changed', handlePositionChange); - window.removeEventListener('navigation-preferences-updated', handlePrefsChange); + let lastWidth = window.innerWidth; + if (lastWidth < NARROW_WINDOW_THRESHOLD && isNavExpandedRef.current) { + setIsNavExpanded(false); + } + const onResize = () => { + const width = window.innerWidth; + if ( + width < NARROW_WINDOW_THRESHOLD && + lastWidth >= NARROW_WINDOW_THRESHOLD && + isNavExpandedRef.current + ) { + setIsNavExpanded(false); + } + lastWidth = width; }; - }, []); - - const isHorizontalNav = navigationPosition === 'top' || navigationPosition === 'bottom'; - const effectiveNavigationMode: NavigationMode = - navigationStyle === 'expanded' && isBelowBreakpoint ? 'overlay' : navigationMode; - const effectiveNavigationStyle: NavigationStyle = - navigationMode === 'overlay' ? 'expanded' : navigationStyle; - const isCondensedIconOnly = !isHorizontalNav && isBelowBreakpoint; - const isOverlayMode = effectiveNavigationMode === 'overlay'; + window.addEventListener('resize', onResize); + return () => window.removeEventListener('resize', onResize); + }, [setIsNavExpanded]); const value: NavigationContextValue = { isNavExpanded, setIsNavExpanded, - navigationMode, - setNavigationMode, - effectiveNavigationMode, - navigationStyle, - setNavigationStyle, - effectiveNavigationStyle, - navigationPosition, - setNavigationPosition, - preferences, - updatePreferences, - isHorizontalNav, - isCondensedIconOnly, - isOverlayMode, - isChatExpanded, - setIsChatExpanded, }; return {children}; diff --git a/ui/desktop/src/components/Layout/NavigationPanel.tsx b/ui/desktop/src/components/Layout/NavigationPanel.tsx index f8e1b63012ba..bce83155ac5c 100644 --- a/ui/desktop/src/components/Layout/NavigationPanel.tsx +++ b/ui/desktop/src/components/Layout/NavigationPanel.tsx @@ -1,44 +1,139 @@ -import React, { useState, useEffect, useRef, useCallback, useMemo } from 'react'; +import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'; import { useLocation } from 'react-router-dom'; +import { ChevronDown, ChevronRight, PanelLeft } from 'lucide-react'; +import { motion } from 'framer-motion'; import { useNavigationContext } from './NavigationContext'; import { useConfig } from '../ConfigContext'; -import { useNavigationSessions } from '../../hooks/useNavigationSessions'; -import { getNavItemById, type NavItem } from '../../hooks/useNavigationItems'; +import { useNavigationSessions, getSessionDisplayName } from '../../hooks/useNavigationSessions'; +import { + NAV_ITEMS, + SETTINGS_NAV_ITEM, + getNavItemLabel, + type NavItem, +} from '../../hooks/useNavigationItems'; import { AppEvents } from '../../constants/events'; -import { CondensedRenderer } from './CondensedRenderer'; -import { ExpandedRenderer } from './ExpandedRenderer'; -import { NavigationOverlay } from './navigation'; -import type { SessionStatus, DragHandlers } from './navigation/types'; +import { Goose } from '../icons/Goose'; +import { InlineEditText } from '../common/InlineEditText'; +import { SessionIndicators } from '../SessionIndicators'; +import { updateSessionName, type Session } from '../../api'; +import { cn } from '../../utils'; +import { defineMessages, useIntl } from '../../i18n'; -export const Navigation: React.FC<{ className?: string }> = ({ className }) => { - const { - isNavExpanded, - setIsNavExpanded, - navigationPosition, - preferences, - updatePreferences, - isCondensedIconOnly, - isOverlayMode, - effectiveNavigationStyle, - isChatExpanded, - setIsChatExpanded, - } = useNavigationContext(); +type StreamState = 'idle' | 'loading' | 'streaming' | 'error'; + +interface SessionStatus { + streamState: StreamState; + hasUnreadActivity: boolean; +} + +const i18n = defineMessages({ + chats: { + id: 'navigationPanel.chats', + defaultMessage: 'Chats', + }, + noChats: { + id: 'navigationPanel.noChats', + defaultMessage: 'No recent chats', + }, + untitledSession: { + id: 'navigationPanel.untitledSession', + defaultMessage: 'Untitled session', + }, + collapseSidebar: { + id: 'navigationPanel.collapseSidebar', + defaultMessage: 'Collapse sidebar', + }, +}); + +const navItemClass = (active: boolean) => + cn( + 'flex flex-row items-center gap-3 outline-none no-drag w-full', + 'rounded-full px-3 py-2 text-sm font-medium transition-colors', + active + ? 'bg-background-tertiary text-text-primary' + : 'text-text-primary hover:bg-background-tertiary/60' + ); + +interface NavRowProps { + item: NavItem; + active: boolean; + onClick: () => void; +} + +const NavRow: React.FC = ({ item, active, onClick }) => { + const intl = useIntl(); + const Icon = item.icon; + return ( + + ); +}; + +interface SessionRowProps { + session: Session; + active: boolean; + status: SessionStatus | undefined; + onClick: () => void; + onRenamed: () => void; +} + +const SessionRow: React.FC = ({ session, active, status, onClick, onRenamed }) => { + const intl = useIntl(); + const [isEditing, setIsEditing] = useState(false); + const isStreaming = status?.streamState === 'streaming'; + const hasError = status?.streamState === 'error'; + const hasUnread = status?.hasUnreadActivity ?? false; + return ( +
!isEditing && onClick()} + className={cn( + 'flex items-center gap-2 px-3 py-1.5 rounded-full cursor-pointer text-sm', + 'hover:bg-background-tertiary/60 transition-colors', + active && 'bg-background-tertiary' + )} + > + { + await updateSessionName({ + path: { session_id: session.id }, + body: { name: newName }, + }); + onRenamed(); + }} + placeholder={intl.formatMessage(i18n.untitledSession)} + disabled={isStreaming} + singleClickEdit={false} + className="truncate text-text-primary flex-1 !px-0 !py-0 hover:bg-transparent" + editClassName="!text-sm" + onEditStart={() => setIsEditing(true)} + onEditEnd={() => setIsEditing(false)} + /> + +
+ ); +}; + +export const Navigation: React.FC<{ className?: string }> = ({ className }) => { + const intl = useIntl(); + const { isNavExpanded, setIsNavExpanded } = useNavigationContext(); const location = useLocation(); const { extensionsList } = useConfig(); const appsExtensionEnabled = !!extensionsList?.find((ext) => ext.name === 'apps')?.enabled; - const visibleItems = useMemo(() => { - return preferences.itemOrder - .filter((id) => preferences.enabledItems.includes(id)) - .map((id) => getNavItemById(id)) - .filter((item): item is NavItem => item !== undefined) - .filter((item) => { - if (item.path === '/apps') return appsExtensionEnabled; - return true; - }); - }, [preferences.itemOrder, preferences.enabledItems, appsExtensionEnabled]); + const visibleItems = useMemo(() => { + return NAV_ITEMS.filter((item) => { + if (item.path === '/apps') return appsExtensionEnabled; + return true; + }); + }, [appsExtensionEnabled]); const isActive = useCallback((path: string) => location.pathname === path, [location.pathname]); @@ -47,61 +142,8 @@ export const Navigation: React.FC<{ className?: string }> = ({ className }) => { activeSessionId, fetchSessions, handleNavClick, - handleNewChat, handleSessionClick, - } = useNavigationSessions({ - onNavigate: isOverlayMode ? () => setIsNavExpanded(false) : undefined, - }); - - const [draggedItem, setDraggedItem] = useState(null); - const [dragOverItem, setDragOverItem] = useState(null); - - const onDragStart = useCallback((e: React.DragEvent, itemId: string) => { - setDraggedItem(itemId); - e.dataTransfer.effectAllowed = 'move'; - }, []); - - const onDragOver = useCallback( - (e: React.DragEvent, itemId: string) => { - e.preventDefault(); - if (draggedItem && draggedItem !== itemId) setDragOverItem(itemId); - }, - [draggedItem] - ); - - const onDrop = useCallback( - (e: React.DragEvent, dropItemId: string) => { - e.preventDefault(); - if (!draggedItem || draggedItem === dropItemId) return; - - const newOrder = [...preferences.itemOrder]; - const draggedIndex = newOrder.indexOf(draggedItem); - const dropIndex = newOrder.indexOf(dropItemId); - if (draggedIndex === -1 || dropIndex === -1) return; - - newOrder.splice(draggedIndex, 1); - newOrder.splice(dropIndex, 0, draggedItem); - updatePreferences({ ...preferences, itemOrder: newOrder }); - - setDraggedItem(null); - setDragOverItem(null); - }, - [draggedItem, preferences, updatePreferences] - ); - - const onDragEnd = useCallback(() => { - setDraggedItem(null); - setDragOverItem(null); - }, []); - - const drag: DragHandlers = { - draggedItem, - dragOverItem, - onDragStart, - onDragOver, - onDrop, - onDragEnd, - }; + } = useNavigationSessions(); const [sessionStatuses, setSessionStatuses] = useState>(new Map()); @@ -124,11 +166,6 @@ export const Navigation: React.FC<{ className?: string }> = ({ className }) => { return () => window.removeEventListener(AppEvents.SESSION_STATUS_UPDATE, handleStatusUpdate); }, []); - const getSessionStatus = useCallback( - (sessionId: string) => sessionStatuses.get(sessionId), - [sessionStatuses] - ); - const clearUnread = useCallback((sessionId: string) => { setSessionStatuses((prev) => { const status = prev.get(sessionId); @@ -141,20 +178,6 @@ export const Navigation: React.FC<{ className?: string }> = ({ className }) => { }); }, []); - useEffect(() => { - if (!(isOverlayMode && isNavExpanded)) return; - - const handleKeyDown = (e: KeyboardEvent) => { - if (e.key === 'Escape') { - e.preventDefault(); - setIsNavExpanded(false); - } - }; - - document.addEventListener('keydown', handleKeyDown, { capture: true }); - return () => document.removeEventListener('keydown', handleKeyDown, { capture: true }); - }, [isNavExpanded, isOverlayMode, setIsNavExpanded]); - const navFocusRef = useRef(null); useEffect(() => { @@ -164,58 +187,93 @@ export const Navigation: React.FC<{ className?: string }> = ({ className }) => { } }, [isNavExpanded, fetchSessions]); - const onToggleChatExpanded = useCallback(() => { - setIsChatExpanded(!isChatExpanded); - }, [isChatExpanded, setIsChatExpanded]); - - const onClose = useCallback(() => setIsNavExpanded(false), [setIsNavExpanded]); - - const rendererProps = { - isNavExpanded, - isOverlayMode, - navigationPosition, - isCondensedIconOnly, - onClose, - className, - visibleItems, - isActive, - recentSessions, - activeSessionId, - onNavClick: handleNavClick, - onNewChat: handleNewChat, - onSessionClick: handleSessionClick, - onFetchSessions: fetchSessions, - getSessionStatus, - clearUnread, - isChatExpanded, - onToggleChatExpanded, - drag, - navFocusRef, - }; - - const content = - effectiveNavigationStyle === 'expanded' ? ( - - ) : ( - - ); - - if (isOverlayMode) { - if (effectiveNavigationStyle === 'expanded') { - // Expanded overlay uses its own AnimatePresence layout - return content; - } - return ( - setIsNavExpanded(false)} - > - {content} - - ); - } + const [isChatsExpanded, setIsChatsExpanded] = useState(true); if (!isNavExpanded) return null; - return content; + + return ( + + {/* Header: logo + collapse button. Top padding clears the macOS traffic lights. */} +
+ + +
+ + {/* Nav items */} +
+ {visibleItems.map((item) => ( + handleNavClick(item.path)} + /> + ))} +
+ + {/* Chats section — takes remaining vertical space */} +
+ + {isChatsExpanded && ( +
+ {recentSessions.length === 0 ? ( +
+ {intl.formatMessage(i18n.noChats)} +
+ ) : ( + recentSessions.map((session) => ( + { + clearUnread(session.id); + handleSessionClick(session.id); + }} + onRenamed={fetchSessions} + /> + )) + )} +
+ )} +
+ + {/* Settings pinned to bottom */} +
+ handleNavClick(SETTINGS_NAV_ITEM.path)} + /> +
+
+ ); }; diff --git a/ui/desktop/src/components/Layout/constants.ts b/ui/desktop/src/components/Layout/constants.ts index 81d3acdf9d43..41c525ea541b 100644 --- a/ui/desktop/src/components/Layout/constants.ts +++ b/ui/desktop/src/components/Layout/constants.ts @@ -1,16 +1,6 @@ export const NAV_DIMENSIONS = { - /** Width of condensed navigation in icon-only mode */ - CONDENSED_ICON_ONLY_WIDTH: 44, - /** Width of condensed navigation with labels */ - CONDENSED_WIDTH: 200, - /** Height of expanded navigation (horizontal mode) */ - EXPANDED_HEIGHT: 180, - /** Height of condensed navigation (horizontal mode) */ - CONDENSED_HEIGHT: 46, - /** Minimum width when resizing the navigation panel */ - MIN_NAV_WIDTH: 200, - /** Maximum width when resizing the navigation panel */ - MAX_NAV_WIDTH: 600, + /** Width of the navigation sidebar */ + NAV_WIDTH: 240, } as const; export const Z_INDEX = { diff --git a/ui/desktop/src/components/Layout/navigation/ChatSessionsDropdown.tsx b/ui/desktop/src/components/Layout/navigation/ChatSessionsDropdown.tsx deleted file mode 100644 index 2f2006a7afea..000000000000 --- a/ui/desktop/src/components/Layout/navigation/ChatSessionsDropdown.tsx +++ /dev/null @@ -1,118 +0,0 @@ -import React from 'react'; -import { MessageSquare, History, Plus, ChefHat } from 'lucide-react'; -import { - DropdownMenuContent, - DropdownMenuItem, - DropdownMenuSeparator, -} from '../../ui/dropdown-menu'; -import { SessionIndicators } from '../../SessionIndicators'; -import { cn } from '../../../utils'; -import { getSessionDisplayName, truncateMessage } from '../../../hooks/useNavigationSessions'; -import { defineMessages, useIntl } from '../../../i18n'; -import type { Session } from '../../../api'; -import type { SessionStatus } from './types'; - -const i18n = defineMessages({ - newChat: { - id: 'chatSessionsDropdown.newChat', - defaultMessage: 'New Chat', - }, - showAll: { - id: 'chatSessionsDropdown.showAll', - defaultMessage: 'Show All', - }, -}); - -interface ChatSessionsDropdownProps { - sessions: Session[]; - activeSessionId?: string; - side?: 'top' | 'bottom' | 'left' | 'right'; - zIndex?: number; - getSessionStatus: (sessionId: string) => SessionStatus | undefined; - clearUnread: (sessionId: string) => void; - onNewChat: () => void; - onSessionClick: (sessionId: string) => void; - onShowAll: () => void; -} - -export const ChatSessionsDropdown: React.FC = ({ - sessions, - activeSessionId, - side = 'right', - zIndex, - getSessionStatus, - clearUnread, - onNewChat, - onSessionClick, - onShowAll, -}) => { - const intl = useIntl(); - return ( - - - - {intl.formatMessage(i18n.newChat)} - - - {sessions.length > 0 && } - - {sessions.map((session) => { - const status = getSessionStatus(session.id); - const isStreaming = status?.streamState === 'streaming'; - const hasError = status?.streamState === 'error'; - const hasUnread = status?.hasUnreadActivity ?? false; - const isActiveSession = session.id === activeSessionId; - - return ( - { - clearUnread(session.id); - onSessionClick(session.id); - }} - className={cn( - 'flex items-center gap-2 px-3 py-2 text-sm rounded-lg cursor-pointer', - isActiveSession && 'bg-background-tertiary' - )} - > - {session.recipe ? ( - - ) : ( - - )} - - {truncateMessage(getSessionDisplayName(session), 30)} - - - - ); - })} - - {sessions.length > 0 && ( - <> - - - - {intl.formatMessage(i18n.showAll)} - - - )} - - ); -}; diff --git a/ui/desktop/src/components/Layout/navigation/NavigationOverlay.tsx b/ui/desktop/src/components/Layout/navigation/NavigationOverlay.tsx deleted file mode 100644 index e9ac6d34f7c6..000000000000 --- a/ui/desktop/src/components/Layout/navigation/NavigationOverlay.tsx +++ /dev/null @@ -1,52 +0,0 @@ -import React from 'react'; -import { motion, AnimatePresence } from 'framer-motion'; -import { cn } from '../../../utils'; -import { Z_INDEX } from '../constants'; - -type NavigationPosition = 'top' | 'bottom' | 'left' | 'right'; - -interface NavigationOverlayProps { - isOpen: boolean; - position: NavigationPosition; - onClose: () => void; - children: React.ReactNode; -} - -export const NavigationOverlay: React.FC = ({ - isOpen, - position, - onClose, - children, -}) => { - return ( - - {isOpen && ( -
- {/* Backdrop */} - - - {/* Scrollable container for navigation panel */} -
-
-
{children}
-
-
-
- )} -
- ); -}; diff --git a/ui/desktop/src/components/Layout/navigation/SessionsList.tsx b/ui/desktop/src/components/Layout/navigation/SessionsList.tsx deleted file mode 100644 index 42705d24e410..000000000000 --- a/ui/desktop/src/components/Layout/navigation/SessionsList.tsx +++ /dev/null @@ -1,162 +0,0 @@ -import React, { useState, useCallback } from 'react'; -import { MessageSquare, ChefHat, Plus, History } from 'lucide-react'; -import { motion, AnimatePresence } from 'framer-motion'; -import { SessionIndicators } from '../../SessionIndicators'; -import { InlineEditText } from '../../common/InlineEditText'; -import { cn } from '../../../utils'; -import { getSessionDisplayName } from '../../../hooks/useNavigationSessions'; -import { updateSessionName } from '../../../api'; -import type { Session } from '../../../api'; -import type { SessionStatus } from './types'; -import { defineMessages, useIntl } from '../../../i18n'; - -const i18n = defineMessages({ - startNewChat: { - id: 'sessionsList.startNewChat', - defaultMessage: 'Start New Chat', - }, - untitledSession: { - id: 'sessionsList.untitledSession', - defaultMessage: 'Untitled session', - }, - showAll: { - id: 'sessionsList.showAll', - defaultMessage: 'Show All', - }, -}); - -interface SessionsListProps { - sessions: Session[]; - activeSessionId?: string; - isExpanded: boolean; - getSessionStatus: (sessionId: string) => SessionStatus | undefined; - clearUnread: (sessionId: string) => void; - onSessionClick: (sessionId: string) => void; - onSessionRenamed?: () => void; - onNewChat?: () => void; - onShowAll?: () => void; -} - -export const SessionsList: React.FC = ({ - sessions, - activeSessionId, - isExpanded, - getSessionStatus, - clearUnread, - onSessionClick, - onSessionRenamed, - onNewChat, - onShowAll, -}) => { - const intl = useIntl(); - const [editingSessionId, setEditingSessionId] = useState(null); - - const handleSaveSessionName = useCallback( - async (sessionId: string, newName: string) => { - await updateSessionName({ - path: { session_id: sessionId }, - body: { name: newName }, - }); - onSessionRenamed?.(); - }, - [onSessionRenamed] - ); - - return ( - - {isExpanded && ( - -
- {/* New Chat button as first item */} - {onNewChat && ( -
-
- - {intl.formatMessage(i18n.startNewChat)} -
- )} - - {sessions.map((session) => { - const status = getSessionStatus(session.id); - const isStreaming = status?.streamState === 'streaming'; - const hasError = status?.streamState === 'error'; - const hasUnread = status?.hasUnreadActivity ?? false; - const isActiveSession = session.id === activeSessionId; - const isEditing = editingSessionId === session.id; - - return ( -
{ - if (!isEditing) { - clearUnread(session.id); - onSessionClick(session.id); - } - }} - className={cn( - 'w-full text-left py-1.5 px-2 text-xs rounded-md', - 'hover:bg-background-tertiary transition-colors', - 'flex items-center gap-2 cursor-pointer', - isActiveSession && 'bg-background-tertiary' - )} - > -
- {session.recipe ? ( - - ) : ( - - )} - handleSaveSessionName(session.id, newName)} - placeholder={intl.formatMessage(i18n.untitledSession)} - disabled={isStreaming} - singleClickEdit={false} - className="truncate text-text-primary flex-1 !px-0 !py-0 hover:bg-transparent" - editClassName="!text-xs" - onEditStart={() => setEditingSessionId(session.id)} - onEditEnd={() => setEditingSessionId(null)} - /> - -
- ); - })} - - {/* Show All button at bottom */} - {onShowAll && sessions.length > 0 && ( -
-
- - {intl.formatMessage(i18n.showAll)} -
- )} -
- - )} - - ); -}; diff --git a/ui/desktop/src/components/Layout/navigation/index.ts b/ui/desktop/src/components/Layout/navigation/index.ts deleted file mode 100644 index 63a77a7b82ca..000000000000 --- a/ui/desktop/src/components/Layout/navigation/index.ts +++ /dev/null @@ -1,3 +0,0 @@ -export { ChatSessionsDropdown } from './ChatSessionsDropdown'; -export { NavigationOverlay } from './NavigationOverlay'; -export { SessionsList } from './SessionsList'; diff --git a/ui/desktop/src/components/Layout/navigation/types.ts b/ui/desktop/src/components/Layout/navigation/types.ts deleted file mode 100644 index bca2cf5bbb9a..000000000000 --- a/ui/desktop/src/components/Layout/navigation/types.ts +++ /dev/null @@ -1,54 +0,0 @@ -import type { NavItem } from '../../../hooks/useNavigationItems'; -import type { Session } from '../../../api'; -import type { NavigationPosition } from '../NavigationContext'; - -export type StreamState = 'idle' | 'loading' | 'streaming' | 'error'; - -export interface SessionStatus { - streamState: StreamState; - hasUnreadActivity: boolean; -} - -export interface DragHandlers { - draggedItem: string | null; - dragOverItem: string | null; - onDragStart: (e: React.DragEvent, itemId: string) => void; - onDragOver: (e: React.DragEvent, itemId: string) => void; - onDrop: (e: React.DragEvent, dropItemId: string) => void; - onDragEnd: () => void; -} - -export interface NavigationRendererProps { - isNavExpanded: boolean; - isOverlayMode: boolean; - navigationPosition: NavigationPosition; - isCondensedIconOnly: boolean; - onClose: () => void; - className?: string; - - // Items - visibleItems: NavItem[]; - isActive: (path: string) => boolean; - - // Sessions - recentSessions: Session[]; - activeSessionId?: string; - onNavClick: (path: string) => void; - onNewChat: () => void; - onSessionClick: (sessionId: string) => void; - onFetchSessions: () => void; - - // Session status - getSessionStatus: (sessionId: string) => SessionStatus | undefined; - clearUnread: (sessionId: string) => void; - - // Chat expand (condensed only, but simpler to keep uniform) - isChatExpanded: boolean; - onToggleChatExpanded: () => void; - - // Drag and drop - drag: DragHandlers; - - // Ref for focus management - navFocusRef: React.RefObject; -} diff --git a/ui/desktop/src/components/bottom_menu/ContextWindowIndicator.tsx b/ui/desktop/src/components/bottom_menu/ContextWindowIndicator.tsx index 1e74de093527..f734d1b0ee1e 100644 --- a/ui/desktop/src/components/bottom_menu/ContextWindowIndicator.tsx +++ b/ui/desktop/src/components/bottom_menu/ContextWindowIndicator.tsx @@ -8,13 +8,8 @@ interface ContextWindowIndicatorProps { } const formatTokenCount = (count: number): string => { - if (count >= 1000000) { - const millions = count / 1000000; - return millions % 1 === 0 ? `${millions.toFixed(0)}M` : `${millions.toFixed(1)}M`; - } else if (count >= 1000) { - const thousands = count / 1000; - return thousands % 1 === 0 ? `${thousands.toFixed(0)}k` : `${thousands.toFixed(1)}k`; - } + if (count >= 1_000_000) return `${Math.round(count / 1_000_000)}M`; + if (count >= 1_000) return `${Math.round(count / 1_000)}k`; return count.toString(); }; @@ -35,15 +30,12 @@ export function ContextWindowIndicator({ const colorClass = getProgressColor(percentage); return ( - <> -
- - - {formatTokenCount(totalTokens)} / {formatTokenCount(tokenLimit)} - - -
-
- +
+ + + {formatTokenCount(totalTokens)} / {formatTokenCount(tokenLimit)} + + +
); } diff --git a/ui/desktop/src/components/bottom_menu/CostTracker.tsx b/ui/desktop/src/components/bottom_menu/CostTracker.tsx index 1ade649f544e..323beb3ee4d6 100644 --- a/ui/desktop/src/components/bottom_menu/CostTracker.tsx +++ b/ui/desktop/src/components/bottom_menu/CostTracker.tsx @@ -99,10 +99,7 @@ export function CostTracker({ return accumulatedCost ?? 0; }; - const formatCost = (cost: number): string => { - // Always show 4 decimal places for consistency - return cost.toFixed(4); - }; + const formatCost = (cost: number): string => cost.toFixed(2); // Show loading state or when we don't have model/provider info if (!currentModel || !currentProvider) { @@ -112,12 +109,9 @@ export function CostTracker({ // If still loading, show a placeholder if (isLoading) { return ( - <> -
- ... -
-
- +
+ ... +
); } @@ -129,14 +123,11 @@ export function CostTracker({ const freeProviders = ['ollama', 'local', 'localhost']; if (freeProviders.includes(currentProvider.toLowerCase())) { return ( - <> -
- - {inputTokens.toLocaleString()}↑ {outputTokens.toLocaleString()}↓ - -
-
- +
+ + {inputTokens.toLocaleString()}↑ {outputTokens.toLocaleString()}↓ + +
); } @@ -153,18 +144,15 @@ export function CostTracker({ }; return ( - <> - - -
- - 0.0000 -
-
- {getUnavailableTooltip()} -
-
- + + +
+ + 0.0000 +
+
+ {getUnavailableTooltip()} +
); } @@ -199,17 +187,14 @@ export function CostTracker({ }; return ( - <> - - -
- - {formatCost(totalCost)} -
-
- {getTooltipContent()} -
-
- + + +
+ + {formatCost(totalCost)} +
+
+ {getTooltipContent()} +
); } diff --git a/ui/desktop/src/components/bottom_menu/DirSwitcher.tsx b/ui/desktop/src/components/bottom_menu/DirSwitcher.tsx index 13b1b7a06566..10b24c886af1 100644 --- a/ui/desktop/src/components/bottom_menu/DirSwitcher.tsx +++ b/ui/desktop/src/components/bottom_menu/DirSwitcher.tsx @@ -188,7 +188,9 @@ export const DirSwitcher: React.FC = ({ disabled={isDirectoryChooserOpen} > -
{workingDir}
+
+ {workingDir.replace(/\/+$/, '').split('/').pop() || workingDir} +
diff --git a/ui/desktop/src/components/common/Greeting.tsx b/ui/desktop/src/components/common/Greeting.tsx deleted file mode 100644 index 66bc99633051..000000000000 --- a/ui/desktop/src/components/common/Greeting.tsx +++ /dev/null @@ -1,132 +0,0 @@ -import { useState } from 'react'; -import { useTextAnimator } from '../../hooks/use-text-animator'; -import { defineMessages, useIntl } from '../../i18n'; - -const i18n = defineMessages({ - readyToGetStarted: { - id: 'greeting.readyToGetStarted', - defaultMessage: 'Ready to get started?', - }, - whatToWorkOn: { - id: 'greeting.whatToWorkOn', - defaultMessage: 'What would you like to work on?', - }, - readyToBuild: { - id: 'greeting.readyToBuild', - defaultMessage: 'Ready to build something amazing?', - }, - whatToExplore: { - id: 'greeting.whatToExplore', - defaultMessage: 'What would you like to explore?', - }, - whatsOnYourMind: { - id: 'greeting.whatsOnYourMind', - defaultMessage: "What's on your mind?", - }, - whatShallWeCreate: { - id: 'greeting.whatShallWeCreate', - defaultMessage: 'What shall we create today?', - }, - whatProjectNeedsAttention: { - id: 'greeting.whatProjectNeedsAttention', - defaultMessage: 'What project needs attention?', - }, - whatToTackle: { - id: 'greeting.whatToTackle', - defaultMessage: 'What would you like to tackle?', - }, - whatNeedsToBeDone: { - id: 'greeting.whatNeedsToBeDone', - defaultMessage: 'What needs to be done?', - }, - whatsThePlan: { - id: 'greeting.whatsThePlan', - defaultMessage: "What's the plan for today?", - }, - readyToCreateGreat: { - id: 'greeting.readyToCreateGreat', - defaultMessage: 'Ready to create something great?', - }, - whatCanBeBuilt: { - id: 'greeting.whatCanBeBuilt', - defaultMessage: 'What can be built today?', - }, - whatsNextChallenge: { - id: 'greeting.whatsNextChallenge', - defaultMessage: "What's the next challenge?", - }, - whatProgress: { - id: 'greeting.whatProgress', - defaultMessage: 'What progress can be made?', - }, - whatToAccomplish: { - id: 'greeting.whatToAccomplish', - defaultMessage: 'What would you like to accomplish?', - }, - whatTaskAwaits: { - id: 'greeting.whatTaskAwaits', - defaultMessage: 'What task awaits?', - }, - whatsTheMission: { - id: 'greeting.whatsTheMission', - defaultMessage: "What's the mission today?", - }, - whatCanBeAchieved: { - id: 'greeting.whatCanBeAchieved', - defaultMessage: 'What can be achieved?', - }, - whatProjectReadyToBegin: { - id: 'greeting.whatProjectReadyToBegin', - defaultMessage: 'What project is ready to begin?', - }, -}); - -interface GreetingProps { - className?: string; - forceRefresh?: boolean; -} - -export function Greeting({ - className = 'mt-1 text-4xl font-light animate-in fade-in duration-300', - forceRefresh = false, -}: GreetingProps) { - const intl = useIntl(); - - const messageDescriptors = [ - i18n.readyToGetStarted, - i18n.whatToWorkOn, - i18n.readyToBuild, - i18n.whatToExplore, - i18n.whatsOnYourMind, - i18n.whatShallWeCreate, - i18n.whatProjectNeedsAttention, - i18n.whatToTackle, - i18n.whatToExplore, - i18n.whatNeedsToBeDone, - i18n.whatsThePlan, - i18n.readyToCreateGreat, - i18n.whatCanBeBuilt, - i18n.whatsNextChallenge, - i18n.whatProgress, - i18n.whatToAccomplish, - i18n.whatTaskAwaits, - i18n.whatsTheMission, - i18n.whatCanBeAchieved, - i18n.whatProjectReadyToBegin, - ]; - - // Using lazy initializer to generate random greeting on each component instance - const greeting = useState(() => { - const randomMessageIndex = Math.floor(Math.random() * messageDescriptors.length); - return messageDescriptors[randomMessageIndex]; - })[0]; - - const greetingText = intl.formatMessage(greeting); - const messageRef = useTextAnimator({ text: greetingText }); - - return ( -

- {greetingText} -

- ); -} diff --git a/ui/desktop/src/components/conversation/ChatHistorySearch.tsx b/ui/desktop/src/components/conversation/ChatHistorySearch.tsx deleted file mode 100644 index 5d332e7408f3..000000000000 --- a/ui/desktop/src/components/conversation/ChatHistorySearch.tsx +++ /dev/null @@ -1,393 +0,0 @@ -import React, { useState, useCallback, useEffect, useRef } from 'react'; -import { Search, X, MessageSquare, ChefHat, Clock } from 'lucide-react'; -import { AnimatePresence, motion } from 'framer-motion'; -import { defineMessages, useIntl } from '../../i18n'; -import { searchSessions } from '../../api'; -import { cn } from '../../utils'; -import { ScrollArea } from '../ui/scroll-area'; -import { Skeleton } from '../ui/skeleton'; -import { SessionIndicators } from '../SessionIndicators'; -import { getSessionDisplayName, truncateMessage } from '../../hooks/useNavigationSessions'; -import type { Session } from '../../api'; -import type { SessionStatus } from '../Layout/navigation/types'; - -const i18n = defineMessages({ - searchPlaceholder: { - id: 'chatHistorySearch.searchPlaceholder', - defaultMessage: 'Search chats…', - }, - noResults: { - id: 'chatHistorySearch.noResults', - defaultMessage: 'No chats found', - }, - searchError: { - id: 'chatHistorySearch.searchError', - defaultMessage: 'Search failed', - }, - messageCount: { - id: 'chatHistorySearch.messageCount', - defaultMessage: '{count, plural, one {# message} other {# messages}}', - }, - keyboardHintMac: { - id: 'chatHistorySearch.keyboardHintMac', - defaultMessage: '⌘K', - }, - keyboardHintOther: { - id: 'chatHistorySearch.keyboardHintOther', - defaultMessage: 'Ctrl+K', - }, -}); - -function formatRelativeTime(dateStr: string): string { - const date = new Date(dateStr); - const now = new Date(); - const diffMs = now.getTime() - date.getTime(); - const diffMin = Math.floor(diffMs / 60000); - const diffHr = Math.floor(diffMs / 3600000); - const diffDay = Math.floor(diffMs / 86400000); - - if (diffMin < 1) return 'just now'; - if (diffMin < 60) return `${diffMin}m ago`; - if (diffHr < 24) return `${diffHr}h ago`; - if (diffDay < 7) return `${diffDay}d ago`; - return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }); -} - -interface ChatHistorySearchProps { - onSessionClick: (sessionId: string) => void; - getSessionStatus: (sessionId: string) => SessionStatus | undefined; - clearUnread: (sessionId: string) => void; - activeSessionId?: string; - className?: string; -} - -const SEARCH_RESULTS_LIMIT = 8; - -export const ChatHistorySearch: React.FC = ({ - onSessionClick, - getSessionStatus, - clearUnread, - activeSessionId, - className, -}) => { - const intl = useIntl(); - const [query, setQuery] = useState(''); - const [results, setResults] = useState([]); - const [isSearching, setIsSearching] = useState(false); - const [hasError, setHasError] = useState(false); - const [isFocused, setIsFocused] = useState(false); - const [highlightedIndex, setHighlightedIndex] = useState(-1); - const containerRef = useRef(null); - const inputRef = useRef(null); - const searchTimeoutRef = useRef | undefined>(undefined); - const latestRequestIdRef = useRef(0); - - const showDropdown = - isFocused && (query.trim().length > 0 || isSearching || hasError || results.length > 0); - - const performSearch = useCallback(async (searchQuery: string) => { - if (!searchQuery.trim()) { - latestRequestIdRef.current += 1; - setResults([]); - setHasError(false); - return; - } - - const requestId = ++latestRequestIdRef.current; - setIsSearching(true); - setHasError(false); - try { - const response = await searchSessions({ - query: { query: searchQuery, limit: SEARCH_RESULTS_LIMIT }, - throwOnError: false, - client: undefined, - }); - - if (requestId !== latestRequestIdRef.current) return; - - if (response.error || !response.data) { - setResults([]); - setHasError(true); - } else { - setResults(response.data); - } - } catch { - if (requestId !== latestRequestIdRef.current) return; - setResults([]); - setHasError(true); - } finally { - if (requestId === latestRequestIdRef.current) { - setIsSearching(false); - setHighlightedIndex(-1); - } - } - }, []); - - useEffect(() => { - if (searchTimeoutRef.current) { - clearTimeout(searchTimeoutRef.current); - } - - // Invalidate any in-flight request and reset selection so Enter - // cannot pick a stale result during the debounce window. - latestRequestIdRef.current += 1; - setHighlightedIndex(-1); - - if (query.trim()) { - // Drop previous results and flip to the searching state immediately - // so (a) an item from the previous query cannot be clicked during the - // 250ms debounce window and (b) the empty-state does not flicker - // before the new request actually runs. - setResults([]); - setHasError(false); - setIsSearching(true); - searchTimeoutRef.current = setTimeout(() => { - performSearch(query); - }, 250); - } else { - setResults([]); - setHasError(false); - setIsSearching(false); - } - - return () => { - if (searchTimeoutRef.current) { - clearTimeout(searchTimeoutRef.current); - } - }; - }, [query, performSearch]); - - const handleClear = useCallback(() => { - setQuery(''); - setResults([]); - setHasError(false); - setHighlightedIndex(-1); - inputRef.current?.focus(); - }, []); - - const handleSelectSession = useCallback( - (sessionId: string) => { - clearUnread(sessionId); - onSessionClick(sessionId); - setQuery(''); - setResults([]); - setHighlightedIndex(-1); - inputRef.current?.blur(); - }, - [onSessionClick, clearUnread] - ); - - const handleContainerBlur = useCallback((event: React.FocusEvent) => { - const nextFocusedElement = event.relatedTarget; - if (!containerRef.current || !nextFocusedElement) { - setIsFocused(false); - return; - } - - if (!containerRef.current.contains(nextFocusedElement)) { - setIsFocused(false); - } - }, []); - - useEffect(() => { - const handleClickOutside = (event: MouseEvent) => { - if (containerRef.current && !containerRef.current.contains(event.target as Node)) { - setIsFocused(false); - } - }; - document.addEventListener('mousedown', handleClickOutside); - return () => document.removeEventListener('mousedown', handleClickOutside); - }, []); - - useEffect(() => { - const isMac = window.electron?.platform === 'darwin'; - const handleKeyDown = (e: KeyboardEvent) => { - if ((isMac ? e.metaKey : e.ctrlKey) && !e.shiftKey && !e.altKey && e.key === 'k') { - e.preventDefault(); - inputRef.current?.focus(); - setIsFocused(true); - } - }; - document.addEventListener('keydown', handleKeyDown); - return () => document.removeEventListener('keydown', handleKeyDown); - }, []); - - const handleInputKeyDown = useCallback( - (e: React.KeyboardEvent) => { - if (!showDropdown) return; - - switch (e.key) { - case 'ArrowDown': - e.preventDefault(); - setHighlightedIndex((prev) => (prev < results.length - 1 ? prev + 1 : 0)); - break; - case 'ArrowUp': - e.preventDefault(); - setHighlightedIndex((prev) => (prev > 0 ? prev - 1 : results.length - 1)); - break; - case 'Enter': - e.preventDefault(); - // Ignore Enter while a search is pending/in-flight so we don't - // select a result from the previous query. - if (isSearching) break; - if (highlightedIndex >= 0 && highlightedIndex < results.length) { - handleSelectSession(results[highlightedIndex].id); - } - break; - case 'Escape': - e.preventDefault(); - setIsFocused(false); - inputRef.current?.blur(); - break; - } - }, - [showDropdown, results, highlightedIndex, isSearching, handleSelectSession] - ); - - return ( -
-
- - setQuery(e.target.value)} - onFocus={() => setIsFocused(true)} - onKeyDown={handleInputKeyDown} - placeholder={intl.formatMessage(i18n.searchPlaceholder)} - aria-label={intl.formatMessage(i18n.searchPlaceholder)} - aria-expanded={showDropdown} - aria-autocomplete="list" - aria-controls="chat-search-results" - role="combobox" - className={cn( - 'w-full pl-8 pr-14 py-1.5 text-sm', - 'bg-background-secondary border border-border-primary rounded-lg', - 'text-text-primary placeholder-text-secondary', - 'focus:outline-none focus:ring-1 focus:ring-border-tertiary focus:border-border-tertiary', - 'transition-all duration-150' - )} - /> - {query ? ( - - ) : ( - - {intl.formatMessage( - window.electron?.platform === 'darwin' ? i18n.keyboardHintMac : i18n.keyboardHintOther - )} - - )} -
- - - {showDropdown && ( - - {isSearching ? ( -
- {Array.from({ length: 3 }).map((_, i) => ( -
- -
- - -
-
- ))} -
- ) : hasError ? ( -
- {intl.formatMessage(i18n.searchError)} -
- ) : results.length > 0 ? ( - -
- {results.map((session, index) => { - const status = getSessionStatus(session.id); - const isStreaming = status?.streamState === 'streaming'; - const hasSessionError = status?.streamState === 'error'; - const hasUnread = status?.hasUnreadActivity ?? false; - const isActive = session.id === activeSessionId; - const isHighlighted = index === highlightedIndex; - const displayName = truncateMessage(getSessionDisplayName(session), 40); - const isRecipe = !!session.recipe; - - return ( - - ); - })} -
-
- ) : query.trim() ? ( -
- {intl.formatMessage(i18n.noResults)} -
- ) : null} -
- )} -
-
- ); -}; diff --git a/ui/desktop/src/components/sessions/SessionsInsights.tsx b/ui/desktop/src/components/sessions/SessionsInsights.tsx deleted file mode 100644 index 7d92267efb5f..000000000000 --- a/ui/desktop/src/components/sessions/SessionsInsights.tsx +++ /dev/null @@ -1,391 +0,0 @@ -import { useEffect, useState } from 'react'; -import { defineMessages, useIntl } from '../../i18n'; -import { errorMessage } from '../../utils/conversionUtils'; -import { Card, CardContent, CardDescription } from '../ui/card'; -import { Greeting } from '../common/Greeting'; -import { useNavigate } from 'react-router-dom'; -import { Button } from '../ui/button'; -import { ChatSmart } from '../icons/'; -import { Goose } from '../icons/Goose'; -import { Skeleton } from '../ui/skeleton'; -import { - getSessionInsights, - listSessions, - Session, - SessionInsights as ApiSessionInsights, -} from '../../api'; -import { resumeSession } from '../../sessions'; -import { useNavigation } from '../../hooks/useNavigation'; - -const i18n = defineMessages({ - totalSessions: { - id: 'sessionsInsights.totalSessions', - defaultMessage: 'Total sessions', - }, - totalTokens: { - id: 'sessionsInsights.totalTokens', - defaultMessage: 'Total tokens', - }, - recentChats: { - id: 'sessionsInsights.recentChats', - defaultMessage: 'Recent chats', - }, - seeAll: { - id: 'sessionsInsights.seeAll', - defaultMessage: 'See all', - }, - noRecentChats: { - id: 'sessionsInsights.noRecentChats', - defaultMessage: 'No recent chat sessions found.', - }, - failedToLoadInsights: { - id: 'sessionsInsights.failedToLoad', - defaultMessage: 'Failed to load insights', - }, -}); - -export function SessionInsights() { - const intl = useIntl(); - const [insights, setInsights] = useState(null); - const [error, setError] = useState(null); - const [recentSessions, setRecentSessions] = useState([]); - const [isLoading, setIsLoading] = useState(true); - const [isLoadingSessions, setIsLoadingSessions] = useState(true); - const navigate = useNavigate(); - const setView = useNavigation(); - - useEffect(() => { - let loadingTimeout: ReturnType; - - const loadInsights = async () => { - try { - const response = await getSessionInsights({ throwOnError: true }); - setInsights(response.data); - setError(null); - } catch (error) { - console.error('Failed to load insights:', error); - setError(errorMessage(error, 'Failed to load insights')); - setInsights({ - totalSessions: 0, - totalTokens: 0, - }); - } finally { - setIsLoading(false); - } - }; - - const loadRecentSessions = async () => { - try { - const response = await listSessions({ throwOnError: true }); - setRecentSessions(response.data.sessions.slice(0, 3)); - } finally { - setIsLoadingSessions(false); - } - }; - - // Set a maximum loading time to prevent infinite skeleton - loadingTimeout = setTimeout(() => { - // Only apply fallback if we still don't have insights data - setInsights((currentInsights) => { - if (!currentInsights) { - console.warn('Loading timeout reached, showing fallback content'); - setError('Failed to load insights'); - setIsLoading(false); - return { - totalSessions: 0, - mostActiveDirs: [], - avgSessionDuration: 0, - totalTokens: 0, - recentActivity: [], - }; - } - // If we already have insights, just make sure loading is false - setIsLoading(false); - return currentInsights; - }); - }, 10000); // 10 second timeout - - loadInsights(); - loadRecentSessions(); - - return () => { - if (loadingTimeout) { - window.clearTimeout(loadingTimeout); - } - }; - }, []); - - const handleSessionClick = async (session: Session) => { - try { - resumeSession(session, setView); - } catch (error) { - console.error('Failed to start session:', error); - navigate('/sessions', { - state: { selectedSessionId: session.id }, - replace: true, - }); - } - }; - - const navigateToSessionHistory = () => { - navigate('/sessions'); - }; - - // Format date to show only the date part (without time) - const formatDateOnly = (dateStr: string) => { - const date = new Date(dateStr); - return date - .toLocaleDateString('en-US', { month: '2-digit', day: '2-digit', year: 'numeric' }) - .replace(/\//g, '/'); - }; - - const formatTokens = (tokens: number | undefined): string => { - return new Intl.NumberFormat('en', { notation: 'compact', maximumFractionDigits: 2 }).format( - tokens || 0 - ); - }; - - // Render skeleton loader while data is loading - const renderSkeleton = () => ( -
- {/* Header container with rounded bottom */} -
-
-
- -
- -
-
- - {/* Stats containers - full bleed with 2px gaps */} -
- {/* Top row with three equal columns */} -
- {/* Total Sessions Card Skeleton */} - - -
- - {intl.formatMessage(i18n.totalSessions)} -
-
-
- - {/* Total Tokens Card Skeleton */} - - -
- - {intl.formatMessage(i18n.totalTokens)} -
-
-
-
- - {/* Recent Chats Card Skeleton */} -
- - -
- - {intl.formatMessage(i18n.recentChats)} - - -
-
- {/* Skeleton chat items */} -
-
- - -
- -
-
-
- - -
- -
-
-
- - -
- -
-
-
-
-
- - {/* Filler container - extends to fill remaining space */} -
-
-
- ); - - // Show skeleton while loading, then show actual content - if (isLoading) { - return renderSkeleton(); - } - - return ( -
- {/* Header container with rounded bottom */} -
-
-
- -
- -
-
- - {/* Stats containers - full bleed with 2px gaps */} -
- {/* Error notice if insights failed to load */} - {error && ( -
-
-
- - {intl.formatMessage(i18n.failedToLoadInsights)} - -
-
- )} - - {/* Top row with three equal columns */} -
- {/* Total Sessions Card */} - - -
-

- {Math.max(insights?.totalSessions ?? 0, 0)} -

- {intl.formatMessage(i18n.totalSessions)} -
-
-
- - {/* Average Duration Card */} - {/**/} - {/* */} - {/*
*/} - {/*

*/} - {/* {insights?.avgSessionDuration*/} - {/* ? `${insights.avgSessionDuration.toFixed(1)}m`*/} - {/* : '0.0m'}*/} - {/*

*/} - {/* Avg. chat length*/} - {/*
*/} - {/*
*/} - {/*
*/} - - {/* Total Tokens Card */} - - -
-

- {formatTokens(insights?.totalTokens)} -

- {intl.formatMessage(i18n.totalTokens)} -
-
-
-
- - {/* Recent Chats Card */} -
- {/* Recent Chats Card */} - - -
- - {intl.formatMessage(i18n.recentChats)} - - -
-
- {isLoadingSessions ? ( - <> -
-
- - -
- -
-
-
- - -
- -
-
-
- - -
- -
- - ) : recentSessions.length > 0 ? ( - recentSessions.map((session, index) => ( -
handleSessionClick(session)} - role="button" - tabIndex={0} - style={{ animationDelay: `${index * 0.1}s` }} - onKeyDown={async (e) => { - if (e.key === 'Enter' || e.key === ' ') { - await handleSessionClick(session); - } - }} - > -
- - {session.name} -
- - {formatDateOnly(session.updated_at)} - -
- )) - ) : ( -
- {intl.formatMessage(i18n.noRecentChats)} -
- )} -
-
-
-
- - {/* Filler container - extends to fill remaining space */} -
-
-
- ); -} diff --git a/ui/desktop/src/components/sessions/SessionsView.tsx b/ui/desktop/src/components/sessions/SessionsView.tsx index a3e8c19a82d7..a7eb513604e4 100644 --- a/ui/desktop/src/components/sessions/SessionsView.tsx +++ b/ui/desktop/src/components/sessions/SessionsView.tsx @@ -59,7 +59,7 @@ const SessionsView: React.FC = () => { [setView] ); - // Check if a session ID was passed in the location state (from SessionsInsights) + // Check if a session ID was passed in the location state. useEffect(() => { const state = location.state as { selectedSessionId?: string } | null; if (state?.selectedSessionId) { diff --git a/ui/desktop/src/components/settings/app/AppSettingsSection.tsx b/ui/desktop/src/components/settings/app/AppSettingsSection.tsx index bc3f5e8126d8..82d8683b6f9a 100644 --- a/ui/desktop/src/components/settings/app/AppSettingsSection.tsx +++ b/ui/desktop/src/components/settings/app/AppSettingsSection.tsx @@ -2,7 +2,7 @@ import { useState, useEffect, useRef } from 'react'; import { defineMessages, useIntl } from '../../../i18n'; import { Switch } from '../../ui/switch'; import { Button } from '../../ui/button'; -import { Settings, ChevronDown, ChevronUp } from 'lucide-react'; +import { Settings } from 'lucide-react'; import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle } from '../../ui/dialog'; import UpdateSection from './UpdateSection'; @@ -13,11 +13,6 @@ import BlockLogoBlack from './icons/block-lockup_black.png'; import BlockLogoWhite from './icons/block-lockup_white.png'; import TelemetrySettings from './TelemetrySettings'; import { trackSettingToggled } from '../../../utils/analytics'; -import { NavigationModeSelector } from './NavigationModeSelector'; -import { NavigationStyleSelector } from './NavigationStyleSelector'; -import { NavigationPositionSelector } from './NavigationPositionSelector'; -import { NavigationCustomizationSettings } from './NavigationCustomizationSettings'; -import { NavigationProvider, useNavigationContextSafe } from '../../Layout/NavigationContext'; const i18n = defineMessages({ appearanceTitle: { id: 'settings.appearance.title', defaultMessage: 'Appearance' }, @@ -63,15 +58,6 @@ const i18n = defineMessages({ id: 'settings.theme.description', defaultMessage: 'Customize the look and feel of goose', }, - navigationTitle: { id: 'settings.navigation.title', defaultMessage: 'Navigation' }, - navigationDesc: { - id: 'settings.navigation.description', - defaultMessage: 'Customize navigation layout and behavior', - }, - navMode: { id: 'settings.navigation.mode', defaultMessage: 'Mode' }, - navStyle: { id: 'settings.navigation.style', defaultMessage: 'Style' }, - navPosition: { id: 'settings.navigation.position', defaultMessage: 'Position' }, - navCustomize: { id: 'settings.navigation.customize', defaultMessage: 'Customize Items' }, helpTitle: { id: 'settings.help.title', defaultMessage: 'Help & feedback' }, helpDesc: { id: 'settings.help.description', @@ -136,83 +122,6 @@ interface AppSettingsSectionProps { scrollToSection?: string; } -const NavigationSettingsContent: React.FC = () => { - const [isExpanded, setIsExpanded] = useState(false); - const navContext = useNavigationContextSafe(); - const isOverlayMode = navContext?.navigationMode === 'overlay'; - const intl = useIntl(); - - return ( - - - - - {isExpanded && ( - -
-

- {intl.formatMessage(i18n.navMode)} -

- -
- {!isOverlayMode && ( -
-

- {intl.formatMessage(i18n.navStyle)} -

- -
- )} - {!isOverlayMode && ( -
-

- {intl.formatMessage(i18n.navPosition)} -

- -
- )} -
-

- {intl.formatMessage(i18n.navCustomize)} -

- -
-
- )} -
- ); -}; - -// Navigation Settings Card - wrapped in its own provider for settings page -const NavigationSettingsCard: React.FC = () => { - const navContext = useNavigationContextSafe(); - - // If already in a NavigationProvider context, render directly - if (navContext) { - return ; - } - - // Otherwise wrap with provider - return ( - - - - ); -}; - export default function AppSettingsSection({ scrollToSection }: AppSettingsSectionProps) { const [menuBarIconEnabled, setMenuBarIconEnabled] = useState(true); const [dockIconEnabled, setDockIconEnabled] = useState(true); @@ -491,9 +400,6 @@ export default function AppSettingsSection({ scrollToSection }: AppSettingsSecti - {/* Navigation Settings */} - - diff --git a/ui/desktop/src/components/settings/app/NavigationCustomizationSettings.tsx b/ui/desktop/src/components/settings/app/NavigationCustomizationSettings.tsx deleted file mode 100644 index c52998612a1d..000000000000 --- a/ui/desktop/src/components/settings/app/NavigationCustomizationSettings.tsx +++ /dev/null @@ -1,202 +0,0 @@ -import React, { useState } from 'react'; -import { GripVertical, Eye, EyeOff } from 'lucide-react'; -import { defineMessages, useIntl } from '../../../i18n'; -import { - useNavigationContext, - DEFAULT_ITEM_ORDER, - DEFAULT_ENABLED_ITEMS, -} from '../../Layout/NavigationContext'; -import { cn } from '../../../utils'; - -const i18n = defineMessages({ - dragInstructions: { - id: 'navigationCustomization.dragInstructions', - defaultMessage: 'Drag to reorder, click the eye icon to show/hide items', - }, - resetToDefaults: { - id: 'navigationCustomization.resetToDefaults', - defaultMessage: 'Reset to defaults', - }, - hideItem: { - id: 'navigationCustomization.hideItem', - defaultMessage: 'Hide item', - }, - showItem: { - id: 'navigationCustomization.showItem', - defaultMessage: 'Show item', - }, - itemHome: { - id: 'navigationCustomization.itemHome', - defaultMessage: 'Home', - }, - itemChat: { - id: 'navigationCustomization.itemChat', - defaultMessage: 'Chat', - }, - itemRecipes: { - id: 'navigationCustomization.itemRecipes', - defaultMessage: 'Recipes', - }, - itemApps: { - id: 'navigationCustomization.itemApps', - defaultMessage: 'Apps', - }, - itemScheduler: { - id: 'navigationCustomization.itemScheduler', - defaultMessage: 'Scheduler', - }, - itemExtensions: { - id: 'navigationCustomization.itemExtensions', - defaultMessage: 'Extensions', - }, - itemSettings: { - id: 'navigationCustomization.itemSettings', - defaultMessage: 'Settings', - }, -}); - -const ITEM_LABEL_KEYS: Record = { - home: 'itemHome', - chat: 'itemChat', - recipes: 'itemRecipes', - apps: 'itemApps', - scheduler: 'itemScheduler', - extensions: 'itemExtensions', - settings: 'itemSettings', -}; - -interface NavigationCustomizationSettingsProps { - className?: string; -} - -export const NavigationCustomizationSettings: React.FC = ({ - className, -}) => { - const { preferences, updatePreferences } = useNavigationContext(); - const [draggedItem, setDraggedItem] = useState(null); - const [dragOverItem, setDragOverItem] = useState(null); - const intl = useIntl(); - - const handleDragStart = (e: React.DragEvent, itemId: string) => { - setDraggedItem(itemId); - e.dataTransfer.effectAllowed = 'move'; - }; - - const handleDragOver = (e: React.DragEvent, itemId: string) => { - e.preventDefault(); - if (draggedItem && draggedItem !== itemId) { - setDragOverItem(itemId); - } - }; - - const handleDrop = (e: React.DragEvent, dropItemId: string) => { - e.preventDefault(); - if (!draggedItem || draggedItem === dropItemId) return; - - const newOrder = [...preferences.itemOrder]; - const draggedIndex = newOrder.indexOf(draggedItem); - const dropIndex = newOrder.indexOf(dropItemId); - - if (draggedIndex === -1 || dropIndex === -1) return; - - newOrder.splice(draggedIndex, 1); - newOrder.splice(dropIndex, 0, draggedItem); - - updatePreferences({ - ...preferences, - itemOrder: newOrder, - }); - - setDraggedItem(null); - setDragOverItem(null); - }; - - const handleDragEnd = () => { - setDraggedItem(null); - setDragOverItem(null); - }; - - const toggleItemEnabled = (itemId: string) => { - const newEnabledItems = preferences.enabledItems.includes(itemId) - ? preferences.enabledItems.filter((id) => id !== itemId) - : [...preferences.enabledItems, itemId]; - - updatePreferences({ - ...preferences, - enabledItems: newEnabledItems, - }); - }; - - const resetToDefaults = () => { - updatePreferences({ - itemOrder: DEFAULT_ITEM_ORDER, - enabledItems: DEFAULT_ENABLED_ITEMS, - }); - }; - - const getItemLabel = (itemId: string): string => { - const key = ITEM_LABEL_KEYS[itemId]; - if (key) { - return intl.formatMessage(i18n[key]); - } - return itemId; - }; - - return ( -
-
-
-

- {intl.formatMessage(i18n.dragInstructions)} -

- -
- - {preferences.itemOrder.map((itemId) => { - const isEnabled = preferences.enabledItems.includes(itemId); - const isDragging = draggedItem === itemId; - const isDragOver = dragOverItem === itemId; - const label = getItemLabel(itemId); - - return ( -
handleDragStart(e, itemId)} - onDragOver={(e) => handleDragOver(e, itemId)} - onDrop={(e) => handleDrop(e, itemId)} - onDragEnd={handleDragEnd} - className={cn( - 'flex items-center gap-3 p-3 rounded-lg border transition-all', - isDragging && 'opacity-50', - isDragOver - ? 'border-border-primary bg-background-tertiary' - : 'border-border-secondary bg-background-primary', - !isEnabled && 'opacity-50' - )} - > - - {label} - -
- ); - })} -
-
- ); -}; diff --git a/ui/desktop/src/components/settings/app/NavigationModeSelector.tsx b/ui/desktop/src/components/settings/app/NavigationModeSelector.tsx deleted file mode 100644 index 7206965e3abc..000000000000 --- a/ui/desktop/src/components/settings/app/NavigationModeSelector.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React from 'react'; -import { Columns2, Layers } from 'lucide-react'; -import { defineMessages, useIntl } from '../../../i18n'; -import { useNavigationContext, NavigationMode } from '../../Layout/NavigationContext'; -import { cn } from '../../../utils'; - -const i18n = defineMessages({ - pushLabel: { - id: 'navigationModeSelector.pushLabel', - defaultMessage: 'Push', - }, - pushDescription: { - id: 'navigationModeSelector.pushDescription', - defaultMessage: 'Navigation pushes content', - }, - overlayLabel: { - id: 'navigationModeSelector.overlayLabel', - defaultMessage: 'Overlay', - }, - overlayDescription: { - id: 'navigationModeSelector.overlayDescription', - defaultMessage: 'Full-screen overlay', - }, -}); - -interface NavigationModeSelectorProps { - className?: string; -} - -export const NavigationModeSelector: React.FC = ({ className }) => { - const { navigationMode, setNavigationMode } = useNavigationContext(); - const intl = useIntl(); - - const modes: { - value: NavigationMode; - label: string; - icon: React.ReactNode; - description: string; - }[] = [ - { - value: 'push', - label: intl.formatMessage(i18n.pushLabel), - icon: , - description: intl.formatMessage(i18n.pushDescription), - }, - { - value: 'overlay', - label: intl.formatMessage(i18n.overlayLabel), - icon: , - description: intl.formatMessage(i18n.overlayDescription), - }, - ]; - - return ( -
-
- {modes.map((mode) => ( - - ))} -
-
- ); -}; diff --git a/ui/desktop/src/components/settings/app/NavigationPositionSelector.tsx b/ui/desktop/src/components/settings/app/NavigationPositionSelector.tsx deleted file mode 100644 index fee468685f5c..000000000000 --- a/ui/desktop/src/components/settings/app/NavigationPositionSelector.tsx +++ /dev/null @@ -1,64 +0,0 @@ -import React from 'react'; -import { ArrowUp, ArrowDown, ArrowLeft, ArrowRight } from 'lucide-react'; -import { defineMessages, useIntl } from '../../../i18n'; -import { useNavigationContext, NavigationPosition } from '../../Layout/NavigationContext'; -import { cn } from '../../../utils'; - -const i18n = defineMessages({ - topLabel: { - id: 'navigationPositionSelector.topLabel', - defaultMessage: 'Top', - }, - bottomLabel: { - id: 'navigationPositionSelector.bottomLabel', - defaultMessage: 'Bottom', - }, - leftLabel: { - id: 'navigationPositionSelector.leftLabel', - defaultMessage: 'Left', - }, - rightLabel: { - id: 'navigationPositionSelector.rightLabel', - defaultMessage: 'Right', - }, -}); - -interface NavigationPositionSelectorProps { - className?: string; -} - -export const NavigationPositionSelector: React.FC = ({ - className, -}) => { - const { navigationPosition, setNavigationPosition } = useNavigationContext(); - const intl = useIntl(); - - const positions: { value: NavigationPosition; label: string; icon: React.ReactNode }[] = [ - { value: 'top', label: intl.formatMessage(i18n.topLabel), icon: }, - { value: 'bottom', label: intl.formatMessage(i18n.bottomLabel), icon: }, - { value: 'left', label: intl.formatMessage(i18n.leftLabel), icon: }, - { value: 'right', label: intl.formatMessage(i18n.rightLabel), icon: }, - ]; - - return ( -
-
- {positions.map((position) => ( - - ))} -
-
- ); -}; diff --git a/ui/desktop/src/components/settings/app/NavigationStyleSelector.tsx b/ui/desktop/src/components/settings/app/NavigationStyleSelector.tsx deleted file mode 100644 index cd5b79c3b841..000000000000 --- a/ui/desktop/src/components/settings/app/NavigationStyleSelector.tsx +++ /dev/null @@ -1,78 +0,0 @@ -import React from 'react'; -import { LayoutGrid, List } from 'lucide-react'; -import { defineMessages, useIntl } from '../../../i18n'; -import { useNavigationContext, NavigationStyle } from '../../Layout/NavigationContext'; -import { cn } from '../../../utils'; - -const i18n = defineMessages({ - tileLabel: { - id: 'navigationStyleSelector.tileLabel', - defaultMessage: 'Tile', - }, - tileDescription: { - id: 'navigationStyleSelector.tileDescription', - defaultMessage: 'Enlarged tile view', - }, - listLabel: { - id: 'navigationStyleSelector.listLabel', - defaultMessage: 'List', - }, - listDescription: { - id: 'navigationStyleSelector.listDescription', - defaultMessage: 'Classic condensed view', - }, -}); - -interface NavigationStyleSelectorProps { - className?: string; -} - -export const NavigationStyleSelector: React.FC = ({ className }) => { - const { navigationStyle, setNavigationStyle } = useNavigationContext(); - const intl = useIntl(); - - const styles: { - value: NavigationStyle; - label: string; - icon: React.ReactNode; - description: string; - }[] = [ - { - value: 'expanded', - label: intl.formatMessage(i18n.tileLabel), - icon: , - description: intl.formatMessage(i18n.tileDescription), - }, - { - value: 'condensed', - label: intl.formatMessage(i18n.listLabel), - icon: , - description: intl.formatMessage(i18n.listDescription), - }, - ]; - - return ( -
-
- {styles.map((style) => ( - - ))} -
-
- ); -}; diff --git a/ui/desktop/src/hooks/use-text-animator.tsx b/ui/desktop/src/hooks/use-text-animator.tsx deleted file mode 100644 index d3002d0d37bc..000000000000 --- a/ui/desktop/src/hooks/use-text-animator.tsx +++ /dev/null @@ -1,245 +0,0 @@ -import SplitType from 'split-type'; -import { useEffect, useRef } from 'react'; - -interface TextSplitterOptions { - resizeCallback?: () => void; - splitTypeTypes?: ('lines' | 'words' | 'chars')[]; -} - -export class TextSplitter { - textElement: HTMLElement; - onResize: (() => void) | null; - splitText: SplitType; - previousContainerWidth: number | null = null; - - constructor(textElement: HTMLElement, options: TextSplitterOptions = {}) { - if (!textElement || !(textElement instanceof HTMLElement)) { - throw new Error('Invalid text element provided.'); - } - - const { resizeCallback, splitTypeTypes } = options; - this.textElement = textElement; - this.onResize = typeof resizeCallback === 'function' ? resizeCallback : null; - - const splitOptions = splitTypeTypes ? { types: splitTypeTypes } : {}; - this.splitText = new SplitType(this.textElement, splitOptions); - - if (this.onResize) { - this.initResizeObserver(); - } - } - - initResizeObserver() { - const resizeObserver = new ResizeObserver(() => { - if (this.textElement) { - const currentWidth = Math.floor(this.textElement.getBoundingClientRect().width); - - if (this.previousContainerWidth && this.previousContainerWidth !== currentWidth) { - this.splitText.split({ types: ['chars'] }); - this.onResize?.(); - } - - this.previousContainerWidth = currentWidth; - } - }); - - resizeObserver.observe(this.textElement); - } - - getLines(): HTMLElement[] { - return this.splitText.lines ?? []; - } - - getChars(): HTMLElement[] { - return this.splitText.chars ?? []; - } -} - -// Text animation class for hover effects -const lettersAndSymbols = [ - 'a', - 'b', - 'c', - 'd', - 'e', - 'f', - 'g', - 'h', - 'i', - 'j', - 'k', - 'l', - 'm', - 'n', - 'o', - 'p', - 'q', - 'r', - 's', - 't', - 'u', - 'v', - 'w', - 'x', - 'y', - 'z', - '!', - '@', - '#', - '$', - '%', - '^', - '&', - '*', - '-', - '_', - '+', - '=', - ';', - ':', - '<', - '>', - ',', -]; - -export class TextAnimator { - textElement: HTMLElement; - splitter!: TextSplitter; - originalChars!: string[]; - activeAnimations: globalThis.Animation[] = []; - activeTimeouts: ReturnType[] = []; - - constructor(textElement: HTMLElement) { - if (!textElement || !(textElement instanceof HTMLElement)) { - throw new Error('Invalid text element provided.'); - } - - this.textElement = textElement; - this.splitText(); - } - - private splitText() { - this.splitter = new TextSplitter(this.textElement, { - splitTypeTypes: ['words', 'chars'], - }); - this.originalChars = this.splitter.getChars().map((char) => char.textContent || ''); - } - - animate() { - this.reset(); - - const chars = this.splitter.getChars(); - - chars.forEach((char, position) => { - const initialText = char.textContent || ''; - - char.style.opacity = '1'; - char.style.display = 'inline-block'; - char.style.position = 'relative'; - - const animation = char.animate( - [ - { - opacity: 1, - color: '#666', - fontFamily: 'Cash Sans Mono', - fontWeight: '300', - }, - { - opacity: 0.5, - color: '#999', - }, - { - opacity: 1, - color: 'inherit', - fontFamily: 'inherit', - fontWeight: 'inherit', - }, - ], - { - duration: 300, // Total duration for all iterations - easing: 'ease-in-out', - delay: position * 30, // Stagger the start of each animation - iterations: 1, - } - ); - - this.activeAnimations.push(animation); - - let iteration = 0; - const maxIterations = 2; - - const animateCharacterChange = () => { - if (iteration < maxIterations) { - char.textContent = - lettersAndSymbols[Math.floor(Math.random() * lettersAndSymbols.length)]; - const timeoutId = setTimeout(animateCharacterChange, 100); - this.activeTimeouts.push(timeoutId); - iteration++; - } else { - char.textContent = initialText; - } - }; - - const timeoutId = setTimeout(animateCharacterChange, position * 30); - this.activeTimeouts.push(timeoutId); - - animation.onfinish = () => { - char.textContent = initialText; - char.style.color = ''; - char.style.fontFamily = ''; - char.style.opacity = '1'; - }; - }); - } - - reset() { - this.activeTimeouts.forEach((timeoutId) => clearTimeout(timeoutId)); - this.activeTimeouts = []; - this.activeAnimations.forEach((animation) => animation.cancel()); - this.activeAnimations = []; - - const chars = this.splitter.getChars(); - chars.forEach((char, index) => { - if (this.originalChars[index]) { - char.textContent = this.originalChars[index]; - } - }); - } -} - -interface UseTextAnimatorProps { - text: string; -} - -function prefersReducedMotion(): boolean { - return window.matchMedia('(prefers-reduced-motion: reduce)').matches; -} - -export function useTextAnimator({ text }: UseTextAnimatorProps) { - const elementRef = useRef(null); - const animator = useRef(null); - - useEffect(() => { - if (!elementRef.current) return; - - if (prefersReducedMotion()) { - return; - } - - animator.current = new TextAnimator(elementRef.current); - - const timeoutId = setTimeout(() => { - animator.current?.animate(); - }, 100); - - return () => { - window.clearTimeout(timeoutId); - if (animator.current) { - animator.current.reset(); - } - }; - }, [text]); - - return elementRef; -} diff --git a/ui/desktop/src/hooks/useNavigationItems.ts b/ui/desktop/src/hooks/useNavigationItems.ts index 8715c44f28cb..e6283baead90 100644 --- a/ui/desktop/src/hooks/useNavigationItems.ts +++ b/ui/desktop/src/hooks/useNavigationItems.ts @@ -1,9 +1,9 @@ import { - Home, - MessageSquare, - FileText, AppWindow, Clock, + FileText, + History, + MessageSquarePlus, Puzzle, Settings, Zap, @@ -18,54 +18,60 @@ export interface NavItem { icon: LucideIcon; getTag?: () => string; tagAlign?: 'left' | 'right'; - hasSubItems?: boolean; } +/** Top-level nav items (excluding Settings which is pinned to the bottom). */ export const NAV_ITEMS: NavItem[] = [ - { id: 'home', path: '/', label: 'Home', icon: Home }, - { id: 'chat', path: '/pair', label: 'Chat', icon: MessageSquare, hasSubItems: true }, + { id: 'home', path: '/', label: 'New Chat', icon: MessageSquarePlus }, { id: 'recipes', path: '/recipes', label: 'Recipes', icon: FileText }, { id: 'skills', path: '/skills', label: 'Skills', icon: Zap }, { id: 'apps', path: '/apps', label: 'Apps', icon: AppWindow }, { id: 'scheduler', path: '/schedules', label: 'Scheduler', icon: Clock }, { id: 'extensions', path: '/extensions', label: 'Extensions', icon: Puzzle }, - { id: 'settings', path: '/settings', label: 'Settings', icon: Settings }, + { id: 'sessions', path: '/sessions', label: 'Session History', icon: History }, ]; +/** Settings is rendered separately, pinned to the bottom of the sidebar. */ +export const SETTINGS_NAV_ITEM: NavItem = { + id: 'settings', + path: '/settings', + label: 'Settings', + icon: Settings, +}; + // Translation descriptors for nav labels. Kept here next to NAV_ITEMS so the two -// stay in sync. Reuses the existing `navigationCustomization.item*` ids that are -// also used by the "Customize Navigation" settings screen. +// stay in sync. const navItemMessages = defineMessages({ home: { - id: 'navigationCustomization.itemHome', - defaultMessage: 'Home', - }, - chat: { - id: 'navigationCustomization.itemChat', - defaultMessage: 'Chat', + id: 'navigation.itemHome', + defaultMessage: 'New Chat', }, recipes: { - id: 'navigationCustomization.itemRecipes', + id: 'navigation.itemRecipes', defaultMessage: 'Recipes', }, skills: { - id: 'navigationCustomization.itemSkills', + id: 'navigation.itemSkills', defaultMessage: 'Skills', }, apps: { - id: 'navigationCustomization.itemApps', + id: 'navigation.itemApps', defaultMessage: 'Apps', }, scheduler: { - id: 'navigationCustomization.itemScheduler', + id: 'navigation.itemScheduler', defaultMessage: 'Scheduler', }, extensions: { - id: 'navigationCustomization.itemExtensions', + id: 'navigation.itemExtensions', defaultMessage: 'Extensions', }, + sessions: { + id: 'navigation.itemSessions', + defaultMessage: 'Session History', + }, settings: { - id: 'navigationCustomization.itemSettings', + id: 'navigation.itemSettings', defaultMessage: 'Settings', }, }); @@ -77,7 +83,3 @@ export function getNavItemLabel(item: NavItem, intl: IntlShape): string { const descriptor = NAV_ITEM_MESSAGES[item.id]; return descriptor ? intl.formatMessage(descriptor) : item.label; } - -export function getNavItemById(id: string): NavItem | undefined { - return NAV_ITEMS.find((item) => item.id === id); -} diff --git a/ui/desktop/src/hooks/useNavigationSessions.ts b/ui/desktop/src/hooks/useNavigationSessions.ts index bbbf7306f035..4bd86aed7615 100644 --- a/ui/desktop/src/hooks/useNavigationSessions.ts +++ b/ui/desktop/src/hooks/useNavigationSessions.ts @@ -9,7 +9,7 @@ import { getInitialWorkingDir } from '../utils/workingDir'; import { AppEvents } from '../constants/events'; import type { Session } from '../api'; -const MAX_RECENT_SESSIONS = 5; +const MAX_RECENT_SESSIONS = 25; interface UseNavigationSessionsOptions { onNavigate?: () => void; diff --git a/ui/desktop/src/i18n/messages/en.json b/ui/desktop/src/i18n/messages/en.json index 50f1406e4996..40007a5d4a17 100644 --- a/ui/desktop/src/i18n/messages/en.json +++ b/ui/desktop/src/i18n/messages/en.json @@ -11,9 +11,6 @@ "announcementModal.gotIt": { "defaultMessage": "Got it!" }, - "appLayout.closeNavigation": { - "defaultMessage": "Close navigation" - }, "appLayout.openNavigation": { "defaultMessage": "Open navigation" }, @@ -119,24 +116,6 @@ "chat.notification.taskComplete.title": { "defaultMessage": "Goose finished the task." }, - "chatHistorySearch.keyboardHintMac": { - "defaultMessage": "⌘K" - }, - "chatHistorySearch.keyboardHintOther": { - "defaultMessage": "Ctrl+K" - }, - "chatHistorySearch.messageCount": { - "defaultMessage": "{count,plural,one{# message} other{# messages}}" - }, - "chatHistorySearch.noResults": { - "defaultMessage": "No chats found" - }, - "chatHistorySearch.searchError": { - "defaultMessage": "Search failed" - }, - "chatHistorySearch.searchPlaceholder": { - "defaultMessage": "Search chats…" - }, "chatInput.contextWindow": { "defaultMessage": "Context window" }, @@ -191,12 +170,6 @@ "chatInput.waitingForImages": { "defaultMessage": "Waiting for images to save..." }, - "chatSessionsDropdown.newChat": { - "defaultMessage": "New Chat" - }, - "chatSessionsDropdown.showAll": { - "defaultMessage": "Show All" - }, "chatSettings.modeDescription": { "defaultMessage": "Configure how Goose interacts with tools and extensions" }, @@ -209,9 +182,6 @@ "chatSettings.responseStylesTitle": { "defaultMessage": "Response Styles" }, - "condensedRenderer.newChat": { - "defaultMessage": "New Chat" - }, "configSettings.configReset": { "defaultMessage": "Configuration Reset" }, @@ -1391,63 +1361,6 @@ "goosehintsSection.title": { "defaultMessage": "Project Hints (.goosehints)" }, - "greeting.readyToBuild": { - "defaultMessage": "Ready to build something amazing?" - }, - "greeting.readyToCreateGreat": { - "defaultMessage": "Ready to create something great?" - }, - "greeting.readyToGetStarted": { - "defaultMessage": "Ready to get started?" - }, - "greeting.whatCanBeAchieved": { - "defaultMessage": "What can be achieved?" - }, - "greeting.whatCanBeBuilt": { - "defaultMessage": "What can be built today?" - }, - "greeting.whatNeedsToBeDone": { - "defaultMessage": "What needs to be done?" - }, - "greeting.whatProgress": { - "defaultMessage": "What progress can be made?" - }, - "greeting.whatProjectNeedsAttention": { - "defaultMessage": "What project needs attention?" - }, - "greeting.whatProjectReadyToBegin": { - "defaultMessage": "What project is ready to begin?" - }, - "greeting.whatShallWeCreate": { - "defaultMessage": "What shall we create today?" - }, - "greeting.whatTaskAwaits": { - "defaultMessage": "What task awaits?" - }, - "greeting.whatToAccomplish": { - "defaultMessage": "What would you like to accomplish?" - }, - "greeting.whatToExplore": { - "defaultMessage": "What would you like to explore?" - }, - "greeting.whatToTackle": { - "defaultMessage": "What would you like to tackle?" - }, - "greeting.whatToWorkOn": { - "defaultMessage": "What would you like to work on?" - }, - "greeting.whatsNextChallenge": { - "defaultMessage": "What's the next challenge?" - }, - "greeting.whatsOnYourMind": { - "defaultMessage": "What's on your mind?" - }, - "greeting.whatsTheMission": { - "defaultMessage": "What's the mission today?" - }, - "greeting.whatsThePlan": { - "defaultMessage": "What's the plan for today?" - }, "groupedExtensionLoadingToast.askGoose": { "defaultMessage": "Ask goose" }, @@ -1508,6 +1421,15 @@ "headersSection.value": { "defaultMessage": "Value" }, + "hub.goodAfternoon": { + "defaultMessage": "Good afternoon" + }, + "hub.goodEvening": { + "defaultMessage": "Good evening" + }, + "hub.goodMorning": { + "defaultMessage": "Good morning" + }, "huggingFaceModelSearch.download": { "defaultMessage": "Download" }, @@ -2402,77 +2324,41 @@ "modelsSection.resetTitle": { "defaultMessage": "Reset Provider and Model" }, - "navigationCustomization.dragInstructions": { - "defaultMessage": "Drag to reorder, click the eye icon to show/hide items" - }, - "navigationCustomization.hideItem": { - "defaultMessage": "Hide item" - }, - "navigationCustomization.itemApps": { + "navigation.itemApps": { "defaultMessage": "Apps" }, - "navigationCustomization.itemChat": { - "defaultMessage": "Chat" - }, - "navigationCustomization.itemExtensions": { + "navigation.itemExtensions": { "defaultMessage": "Extensions" }, - "navigationCustomization.itemHome": { - "defaultMessage": "Home" + "navigation.itemHome": { + "defaultMessage": "New Chat" }, - "navigationCustomization.itemRecipes": { + "navigation.itemRecipes": { "defaultMessage": "Recipes" }, - "navigationCustomization.itemScheduler": { + "navigation.itemScheduler": { "defaultMessage": "Scheduler" }, - "navigationCustomization.itemSettings": { + "navigation.itemSessions": { + "defaultMessage": "Session History" + }, + "navigation.itemSettings": { "defaultMessage": "Settings" }, - "navigationCustomization.itemSkills": { + "navigation.itemSkills": { "defaultMessage": "Skills" }, - "navigationCustomization.resetToDefaults": { - "defaultMessage": "Reset to defaults" - }, - "navigationCustomization.showItem": { - "defaultMessage": "Show item" - }, - "navigationModeSelector.overlayDescription": { - "defaultMessage": "Full-screen overlay" - }, - "navigationModeSelector.overlayLabel": { - "defaultMessage": "Overlay" + "navigationPanel.chats": { + "defaultMessage": "Chats" }, - "navigationModeSelector.pushDescription": { - "defaultMessage": "Navigation pushes content" + "navigationPanel.collapseSidebar": { + "defaultMessage": "Collapse sidebar" }, - "navigationModeSelector.pushLabel": { - "defaultMessage": "Push" + "navigationPanel.noChats": { + "defaultMessage": "No recent chats" }, - "navigationPositionSelector.bottomLabel": { - "defaultMessage": "Bottom" - }, - "navigationPositionSelector.leftLabel": { - "defaultMessage": "Left" - }, - "navigationPositionSelector.rightLabel": { - "defaultMessage": "Right" - }, - "navigationPositionSelector.topLabel": { - "defaultMessage": "Top" - }, - "navigationStyleSelector.listDescription": { - "defaultMessage": "Classic condensed view" - }, - "navigationStyleSelector.listLabel": { - "defaultMessage": "List" - }, - "navigationStyleSelector.tileDescription": { - "defaultMessage": "Enlarged tile view" - }, - "navigationStyleSelector.tileLabel": { - "defaultMessage": "Tile" + "navigationPanel.untitledSession": { + "defaultMessage": "Untitled session" }, "onboardingGuard.checkProviderErrorDescription": { "defaultMessage": "The server may be starting up or temporarily unavailable." @@ -4016,33 +3902,6 @@ "sessions.toast.updated": { "defaultMessage": "Session description updated successfully" }, - "sessionsInsights.failedToLoad": { - "defaultMessage": "Failed to load insights" - }, - "sessionsInsights.noRecentChats": { - "defaultMessage": "No recent chat sessions found." - }, - "sessionsInsights.recentChats": { - "defaultMessage": "Recent chats" - }, - "sessionsInsights.seeAll": { - "defaultMessage": "See all" - }, - "sessionsInsights.totalSessions": { - "defaultMessage": "Total sessions" - }, - "sessionsInsights.totalTokens": { - "defaultMessage": "Total tokens" - }, - "sessionsList.showAll": { - "defaultMessage": "Show All" - }, - "sessionsList.startNewChat": { - "defaultMessage": "Start New Chat" - }, - "sessionsList.untitledSession": { - "defaultMessage": "Untitled session" - }, "sessionsView.error.failedToLoad": { "defaultMessage": "Failed to load session details. Please try again later." }, @@ -4088,24 +3947,6 @@ "settings.menuBarIcon.title": { "defaultMessage": "Menu bar icon" }, - "settings.navigation.customize": { - "defaultMessage": "Customize Items" - }, - "settings.navigation.description": { - "defaultMessage": "Customize navigation layout and behavior" - }, - "settings.navigation.mode": { - "defaultMessage": "Mode" - }, - "settings.navigation.position": { - "defaultMessage": "Position" - }, - "settings.navigation.style": { - "defaultMessage": "Style" - }, - "settings.navigation.title": { - "defaultMessage": "Navigation" - }, "settings.notifications.configGuide": { "defaultMessage": "Configuration guide" }, diff --git a/ui/desktop/src/i18n/messages/zh-CN.json b/ui/desktop/src/i18n/messages/zh-CN.json index c3615acdd072..6374d6940e8c 100644 --- a/ui/desktop/src/i18n/messages/zh-CN.json +++ b/ui/desktop/src/i18n/messages/zh-CN.json @@ -11,9 +11,6 @@ "announcementModal.gotIt": { "defaultMessage": "知道了!" }, - "appLayout.closeNavigation": { - "defaultMessage": "收起导航" - }, "appLayout.openNavigation": { "defaultMessage": "展开导航" }, @@ -173,12 +170,6 @@ "chatInput.waitingForImages": { "defaultMessage": "正在等待图片保存…" }, - "chatSessionsDropdown.newChat": { - "defaultMessage": "新建聊天" - }, - "chatSessionsDropdown.showAll": { - "defaultMessage": "显示全部" - }, "chatSettings.modeDescription": { "defaultMessage": "配置 Goose 与工具和扩展的交互方式" }, @@ -191,9 +182,6 @@ "chatSettings.responseStylesTitle": { "defaultMessage": "回复风格" }, - "condensedRenderer.newChat": { - "defaultMessage": "新建聊天" - }, "configSettings.configReset": { "defaultMessage": "配置已重置" }, @@ -296,9 +284,6 @@ "costTracker.pricingUnavailable": { "defaultMessage": "无法获取 {model} 的定价数据" }, - "costTracker.sessionCostBreakdown": { - "defaultMessage": "本次会话费用明细:" - }, "costTracker.totalSessionCost": { "defaultMessage": "本次会话总费用:{cost}" }, @@ -1334,63 +1319,6 @@ "goosehintsSection.title": { "defaultMessage": "项目提示(.goosehints)" }, - "greeting.readyToBuild": { - "defaultMessage": "准备好构建点酷炫的东西了吗?" - }, - "greeting.readyToCreateGreat": { - "defaultMessage": "准备好创造点了不起的东西了吗?" - }, - "greeting.readyToGetStarted": { - "defaultMessage": "准备好开始了吗?" - }, - "greeting.whatCanBeAchieved": { - "defaultMessage": "可以实现什么?" - }, - "greeting.whatCanBeBuilt": { - "defaultMessage": "今天可以构建什么?" - }, - "greeting.whatNeedsToBeDone": { - "defaultMessage": "有什么需要做的?" - }, - "greeting.whatProgress": { - "defaultMessage": "可以取得什么进展?" - }, - "greeting.whatProjectNeedsAttention": { - "defaultMessage": "哪个项目需要关注?" - }, - "greeting.whatProjectReadyToBegin": { - "defaultMessage": "哪个项目准备好开始了?" - }, - "greeting.whatShallWeCreate": { - "defaultMessage": "今天我们来创造点什么?" - }, - "greeting.whatTaskAwaits": { - "defaultMessage": "有什么任务在等着?" - }, - "greeting.whatToAccomplish": { - "defaultMessage": "你想完成什么?" - }, - "greeting.whatToExplore": { - "defaultMessage": "你想探索什么?" - }, - "greeting.whatToTackle": { - "defaultMessage": "你想解决什么?" - }, - "greeting.whatToWorkOn": { - "defaultMessage": "你想在什么上工作?" - }, - "greeting.whatsNextChallenge": { - "defaultMessage": "下一个挑战是什么?" - }, - "greeting.whatsOnYourMind": { - "defaultMessage": "你在想什么?" - }, - "greeting.whatsTheMission": { - "defaultMessage": "今天的任务是什么?" - }, - "greeting.whatsThePlan": { - "defaultMessage": "今天的计划是什么?" - }, "groupedExtensionLoadingToast.askGoose": { "defaultMessage": "问问 goose" }, @@ -2345,78 +2273,27 @@ "modelsSection.resetTitle": { "defaultMessage": "重置提供商和模型" }, - "navigationCustomization.dragInstructions": { - "defaultMessage": "拖动重新排序,点击眼睛图标显示/隐藏项" - }, - "navigationCustomization.hideItem": { - "defaultMessage": "隐藏项" - }, - "navigationCustomization.itemApps": { + "navigation.itemApps": { "defaultMessage": "应用" }, - "navigationCustomization.itemChat": { - "defaultMessage": "聊天" - }, - "navigationCustomization.itemExtensions": { + "navigation.itemExtensions": { "defaultMessage": "扩展" }, - "navigationCustomization.itemHome": { - "defaultMessage": "首页" + "navigation.itemHome": { + "defaultMessage": "新对话" }, - "navigationCustomization.itemRecipes": { + "navigation.itemRecipes": { "defaultMessage": "配方" }, - "navigationCustomization.itemScheduler": { + "navigation.itemScheduler": { "defaultMessage": "调度" }, - "navigationCustomization.itemSettings": { + "navigation.itemSettings": { "defaultMessage": "设置" }, - "navigationCustomization.itemSkills": { + "navigation.itemSkills": { "defaultMessage": "技能" }, - "navigationCustomization.resetToDefaults": { - "defaultMessage": "重置为默认" - }, - "navigationCustomization.showItem": { - "defaultMessage": "显示项" - }, - "navigationModeSelector.overlayDescription": { - "defaultMessage": "全屏悬浮" - }, - "navigationModeSelector.overlayLabel": { - "defaultMessage": "悬浮" - }, - "navigationModeSelector.pushDescription": { - "defaultMessage": "导航会推动内容" - }, - "navigationModeSelector.pushLabel": { - "defaultMessage": "推动" - }, - "navigationPositionSelector.bottomLabel": { - "defaultMessage": "底部" - }, - "navigationPositionSelector.leftLabel": { - "defaultMessage": "左侧" - }, - "navigationPositionSelector.rightLabel": { - "defaultMessage": "右侧" - }, - "navigationPositionSelector.topLabel": { - "defaultMessage": "顶部" - }, - "navigationStyleSelector.listDescription": { - "defaultMessage": "经典的紧凑视图" - }, - "navigationStyleSelector.listLabel": { - "defaultMessage": "列表" - }, - "navigationStyleSelector.tileDescription": { - "defaultMessage": "放大的瓷砖视图" - }, - "navigationStyleSelector.tileLabel": { - "defaultMessage": "瓷砖" - }, "onboardingGuard.checkProviderErrorDescription": { "defaultMessage": "服务器可能正在启动或暂时不可用。" }, @@ -3923,33 +3800,6 @@ "sessions.toast.updated": { "defaultMessage": "会话描述更新成功" }, - "sessionsInsights.failedToLoad": { - "defaultMessage": "加载概览失败" - }, - "sessionsInsights.noRecentChats": { - "defaultMessage": "未找到近期聊天会话。" - }, - "sessionsInsights.recentChats": { - "defaultMessage": "近期聊天" - }, - "sessionsInsights.seeAll": { - "defaultMessage": "查看全部" - }, - "sessionsInsights.totalSessions": { - "defaultMessage": "会话总数" - }, - "sessionsInsights.totalTokens": { - "defaultMessage": "令牌总数" - }, - "sessionsList.showAll": { - "defaultMessage": "显示全部" - }, - "sessionsList.startNewChat": { - "defaultMessage": "开始新聊天" - }, - "sessionsList.untitledSession": { - "defaultMessage": "未命名会话" - }, "sessionsView.error.failedToLoad": { "defaultMessage": "加载会话详情失败,请稍后重试。" }, @@ -3995,24 +3845,6 @@ "settings.menuBarIcon.title": { "defaultMessage": "菜单栏图标" }, - "settings.navigation.customize": { - "defaultMessage": "自定义项" - }, - "settings.navigation.description": { - "defaultMessage": "自定义导航布局和行为" - }, - "settings.navigation.mode": { - "defaultMessage": "模式" - }, - "settings.navigation.position": { - "defaultMessage": "位置" - }, - "settings.navigation.style": { - "defaultMessage": "样式" - }, - "settings.navigation.title": { - "defaultMessage": "导航" - }, "settings.notifications.configGuide": { "defaultMessage": "配置指南" }, diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts index e089699274f7..5bcecd18d9f0 100644 --- a/ui/desktop/src/main.ts +++ b/ui/desktop/src/main.ts @@ -545,7 +545,10 @@ app.on('open-url', async (_event, url) => { if (process.platform !== 'win32') { const parsedUrl = new URL(url); - log.info('[Main] Received open-url event:', url.includes('key=') ? url.replace(/key=[^&]+/, 'key=REDACTED') : url); + log.info( + '[Main] Received open-url event:', + url.includes('key=') ? url.replace(/key=[^&]+/, 'key=REDACTED') : url + ); await app.whenReady(); @@ -869,7 +872,8 @@ const createChat = async (app: App, options: CreateChatOptions = {}) => { y: mainWindowState.y, width: mainWindowState.width, height: mainWindowState.height, - minWidth: 450, + minWidth: 480, + minHeight: 400, resizable: true, useContentSize: true, icon: path.join(__dirname, '../images/icon.icns'), @@ -1197,7 +1201,6 @@ const createChat = async (app: App, options: CreateChatOptions = {}) => { } windowPowerSaveBlockers.delete(windowId); } - }); return mainWindow; }; @@ -1614,7 +1617,6 @@ const validSettingKeys: Set = new Set([ 'showPricing', 'sessionSharing', 'seenAnnouncementIds', - 'navExpandedWidth', ]); ipcMain.handle('set-setting', (_event, key: SettingKey, value: unknown) => { diff --git a/ui/desktop/src/utils/settings.ts b/ui/desktop/src/utils/settings.ts index 4afefd5a538e..5f50fc6e0790 100644 --- a/ui/desktop/src/utils/settings.ts +++ b/ui/desktop/src/utils/settings.ts @@ -46,7 +46,6 @@ export interface Settings { showPricing: boolean; sessionSharing: SessionSharingConfig; seenAnnouncementIds: string[]; - navExpandedWidth: number | null; } export type SettingKey = keyof Settings; @@ -89,7 +88,6 @@ export const defaultSettings: Settings = { baseUrl: '', }, seenAnnouncementIds: [], - navExpandedWidth: null, }; export function getKeyboardShortcuts(settings: Settings): KeyboardShortcuts { From 6d544e7b55915b2332cbb511bbaf16427f7ea504 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 11:52:38 -0400 Subject: [PATCH 08/66] chore(deps): bump qs from 6.14.2 to 6.15.2 in /evals/open-model-gym/mcp-harness (#9395) Signed-off-by: dependabot[bot] Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com> --- evals/open-model-gym/mcp-harness/package-lock.json | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/evals/open-model-gym/mcp-harness/package-lock.json b/evals/open-model-gym/mcp-harness/package-lock.json index 0abb8b0f26d1..845f45748a2d 100644 --- a/evals/open-model-gym/mcp-harness/package-lock.json +++ b/evals/open-model-gym/mcp-harness/package-lock.json @@ -846,9 +846,9 @@ } }, "node_modules/qs": { - "version": "6.14.2", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.2.tgz", - "integrity": "sha512-V/yCWTTF7VJ9hIh18Ugr2zhJMP01MY7c5kh4J870L7imm6/DIzBsNLTXzMwUA3yZ5b/KBqLx8Kp3uRvd7xSe3Q==", + "version": "6.15.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz", + "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==", "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" From bf0da953d5d0528703aa5cb8a4eeaf931a20b3ff Mon Sep 17 00:00:00 2001 From: seneroner77-cmd Date: Tue, 26 May 2026 18:53:19 +0300 Subject: [PATCH 09/66] Add Turkish desktop locale (#9392) Signed-off-by: dejavu Co-authored-by: dejavu --- ui/desktop/src/i18n/i18n.test.ts | 11 + ui/desktop/src/i18n/index.ts | 2 +- ui/desktop/src/i18n/messages/tr.json | 4811 ++++++++++++++++++++++++++ 3 files changed, 4823 insertions(+), 1 deletion(-) create mode 100644 ui/desktop/src/i18n/messages/tr.json diff --git a/ui/desktop/src/i18n/i18n.test.ts b/ui/desktop/src/i18n/i18n.test.ts index 808883e32e39..abd5b0458285 100644 --- a/ui/desktop/src/i18n/i18n.test.ts +++ b/ui/desktop/src/i18n/i18n.test.ts @@ -52,6 +52,17 @@ describe('getLocale', () => { expect(getLocale()).toEqual({ locale: 'en-GB', messageLocale: 'en' }); }); + it('supports Turkish from navigator.languages', () => { + vi.stubGlobal('navigator', { languages: ['tr-TR'] }); + expect(getLocale()).toEqual({ locale: 'tr-TR', messageLocale: 'tr' }); + }); + + it('supports explicit Turkish locale', () => { + mockAppConfig({ GOOSE_LOCALE: 'tr' }); + vi.stubGlobal('navigator', { languages: ['xx-XX'] }); + expect(getLocale()).toEqual({ locale: 'tr', messageLocale: 'tr' }); + }); + it('falls back to base language when locale tag is invalid BCP 47', () => { // "en-" is not a valid BCP 47 tag and would cause RangeError in Intl APIs mockAppConfig({ GOOSE_LOCALE: 'en-' }); diff --git a/ui/desktop/src/i18n/index.ts b/ui/desktop/src/i18n/index.ts index 07a6d23c828d..b13271d1d015 100644 --- a/ui/desktop/src/i18n/index.ts +++ b/ui/desktop/src/i18n/index.ts @@ -15,7 +15,7 @@ export { defineMessages, useIntl } from 'react-intl'; /** The set of locales that have translation catalogs. */ -const SUPPORTED_LOCALES = new Set(['en', 'zh-CN']); +const SUPPORTED_LOCALES = new Set(['en', 'tr', 'zh-CN']); /** * Map Simplified Chinese aliases (zh, zh-Hans*, zh-SG, zh-MY) to "zh-CN". diff --git a/ui/desktop/src/i18n/messages/tr.json b/ui/desktop/src/i18n/messages/tr.json new file mode 100644 index 000000000000..c33c4f63fd45 --- /dev/null +++ b/ui/desktop/src/i18n/messages/tr.json @@ -0,0 +1,4811 @@ +{ + "alertBox.autoCompactAt": { + "defaultMessage": "Otomatik sıkıştırma eşiği" + }, + "alertBox.compactNow": { + "defaultMessage": "Şimdi sıkıştır" + }, + "alertBox.failedToSaveThreshold": { + "defaultMessage": "Eşik kaydedilemedi: {error}" + }, + "announcementModal.gotIt": { + "defaultMessage": "Anladım!" + }, + "appLayout.closeNavigation": { + "defaultMessage": "Gezinmeyi kapat" + }, + "appLayout.openNavigation": { + "defaultMessage": "Gezinmeyi aç" + }, + "appsView.customApp": { + "defaultMessage": "Özel uygulama" + }, + "appsView.description": { + "defaultMessage": "MCP sunucularınızdaki uygulamalar ve goose tarafından oluşturulan Uygulamalar. Sohbet arayüzü aracılığıyla yeni uygulamalar oluşturmasını isteyebilirsiniz; bunlar burada görünecektir." + }, + "appsView.errorLoading": { + "defaultMessage": "Uygulamalar yüklenirken hata oluştu: {error}" + }, + "appsView.importApp": { + "defaultMessage": "Uygulamayı İçe Aktar" + }, + "appsView.launch": { + "defaultMessage": "Başlat" + }, + "appsView.loading": { + "defaultMessage": "Uygulamalar yükleniyor..." + }, + "appsView.noAppsDescription": { + "defaultMessage": "Bir sohbet açın ve istediğiniz uygulamayı goose'a sorun. Sizin için bir uygulama oluşturabilir ve bu uygulama burada görünür. Birisi bir uygulama paylaştıysa yukarıdaki düğmeyi kullanarak içe aktarabilirsiniz." + }, + "appsView.noAppsTitle": { + "defaultMessage": "Kullanılabilir uygulama yok" + }, + "appsView.retry": { + "defaultMessage": "Yeniden dene" + }, + "appsView.title": { + "defaultMessage": "Uygulamalar" + }, + "backButton.back": { + "defaultMessage": "Geri" + }, + "baseChat.failedToLoadSession": { + "defaultMessage": "Oturum Yüklenemedi" + }, + "baseChat.goHome": { + "defaultMessage": "Ana sayfaya git" + }, + "baseChat.noSession": { + "defaultMessage": "Oturum Yok" + }, + "baseChat.recipeCreatedMessage": { + "defaultMessage": "\"{title}\" kaydedildi ve kullanıma hazır." + }, + "baseChat.recipeCreatedTitle": { + "defaultMessage": "Tarif başarıyla oluşturuldu!" + }, + "bottomMenuExtensionSelection.extensionToggleError": { + "defaultMessage": "Uzantı Geçiş Hatası" + }, + "bottomMenuExtensionSelection.extensionUpdated": { + "defaultMessage": "Uzantı Güncellendi" + }, + "bottomMenuExtensionSelection.extensionWillBeDisabled": { + "defaultMessage": "{name} yeni sohbetlerde devre dışı bırakılacak" + }, + "bottomMenuExtensionSelection.extensionWillBeEnabled": { + "defaultMessage": "{name} yeni sohbetlerde etkinleştirilecek" + }, + "bottomMenuExtensionSelection.extensionsForNewChats": { + "defaultMessage": "Yeni sohbetler için uzantılar" + }, + "bottomMenuExtensionSelection.extensionsForThisSession": { + "defaultMessage": "Bu sohbet oturumunun uzantıları" + }, + "bottomMenuExtensionSelection.manageExtensions": { + "defaultMessage": "uzantıları yönet" + }, + "bottomMenuExtensionSelection.noActiveSession": { + "defaultMessage": "Aktif oturum bulunamadı. Lütfen önce bir sohbet oturumu başlatın." + }, + "bottomMenuExtensionSelection.noExtensionsAvailable": { + "defaultMessage": "Uzantı yok" + }, + "bottomMenuExtensionSelection.noExtensionsFound": { + "defaultMessage": "uzantı bulunamadı" + }, + "bottomMenuExtensionSelection.searchExtensions": { + "defaultMessage": "uzantıları ara..." + }, + "bottomMenuModeSelection.autoFallback": { + "defaultMessage": "otomatik" + }, + "bottomMenuModeSelection.automaticModeDescription": { + "defaultMessage": "Otomatik mod seçimi" + }, + "bottomMenuModeSelection.currentModeTitle": { + "defaultMessage": "Geçerli mod: {label} - {description}" + }, + "cardButtons.configure": { + "defaultMessage": "Yapılandır" + }, + "cardButtons.launch": { + "defaultMessage": "Başlat" + }, + "chat.notification.taskComplete.body": { + "defaultMessage": "Goose'yi tekrar odağa almak için buraya tıklayın." + }, + "chat.notification.taskComplete.title": { + "defaultMessage": "Goose görevi tamamladı." + }, + "chatHistorySearch.keyboardHintMac": { + "defaultMessage": "⌘K" + }, + "chatHistorySearch.keyboardHintOther": { + "defaultMessage": "Ctrl+K" + }, + "chatHistorySearch.messageCount": { + "defaultMessage": "{count,plural,one{# mesaj} other{# mesaj}}" + }, + "chatHistorySearch.noResults": { + "defaultMessage": "Sohbet bulunamadı" + }, + "chatHistorySearch.searchError": { + "defaultMessage": "Arama başarısız oldu" + }, + "chatHistorySearch.searchPlaceholder": { + "defaultMessage": "Sohbetlerde ara…" + }, + "chatInput.contextWindow": { + "defaultMessage": "Bağlam penceresi" + }, + "chatInput.createRecipeFromSession": { + "defaultMessage": "Oturumdan Tarif Oluştur" + }, + "chatInput.dictationError": { + "defaultMessage": "Dikte Hatası" + }, + "chatInput.failedToReadImage": { + "defaultMessage": "Resim dosyası okunamadı" + }, + "chatInput.navigationShortcut": { + "defaultMessage": "Mesajlarda gezinmek için {prefix}↑/{prefix}↓" + }, + "chatInput.processingDroppedFiles": { + "defaultMessage": "Bırakılan dosyalar işleniyor..." + }, + "chatInput.recording": { + "defaultMessage": "Kaydediliyor..." + }, + "chatInput.removeFile": { + "defaultMessage": "Dosyayı kaldır" + }, + "chatInput.removeImage": { + "defaultMessage": "Resmi kaldır" + }, + "chatInput.restartingSession": { + "defaultMessage": "Oturum yeniden başlatılıyor..." + }, + "chatInput.send": { + "defaultMessage": "Gönder" + }, + "chatInput.tooManyTools": { + "defaultMessage": "Çok fazla araç performansı düşürebilir. Takım sayısı: {toolCount} (önerilen: {recommended})" + }, + "chatInput.transcribing": { + "defaultMessage": "Metne dönüştürülüyor..." + }, + "chatInput.typeMessage": { + "defaultMessage": "Göndermek için bir mesaj yazın" + }, + "chatInput.unknownType": { + "defaultMessage": "Bilinmeyen tür" + }, + "chatInput.viewEditRecipe": { + "defaultMessage": "Tarifi Görüntüle/Düzenle" + }, + "chatInput.viewExtensions": { + "defaultMessage": "Uzantıları görüntüle" + }, + "chatInput.waitingForImages": { + "defaultMessage": "Resimlerin kaydedilmesi bekleniyor..." + }, + "chatSessionsDropdown.newChat": { + "defaultMessage": "Yeni Sohbet" + }, + "chatSessionsDropdown.showAll": { + "defaultMessage": "Tümünü Göster" + }, + "chatSettings.modeDescription": { + "defaultMessage": "Goose'nin araçlar ve uzantılarla nasıl etkileşimde bulunacağını yapılandırın" + }, + "chatSettings.modeTitle": { + "defaultMessage": "Mod" + }, + "chatSettings.responseStylesDescription": { + "defaultMessage": "Goose'nin yanıtlarını nasıl biçimlendireceğini ve stillendireceğini seçin" + }, + "chatSettings.responseStylesTitle": { + "defaultMessage": "Yanıt Stilleri" + }, + "condensedRenderer.newChat": { + "defaultMessage": "Yeni Sohbet" + }, + "configSettings.configReset": { + "defaultMessage": "Yapılandırma Sıfırlama" + }, + "configSettings.configResetMsg": { + "defaultMessage": "Tüm değişiklikler geri alındı" + }, + "configSettings.configUpdated": { + "defaultMessage": "Yapılandırma Güncellendi" + }, + "configSettings.configUpdatedMsg": { + "defaultMessage": "\"{name}\" başarıyla kaydedildi" + }, + "configSettings.configurationEditor": { + "defaultMessage": "Yapılandırma Düzenleyici" + }, + "configSettings.description": { + "defaultMessage": "goose yapılandırma ayarlarınızı düzenleyin" + }, + "configSettings.descriptionWithProvider": { + "defaultMessage": "goose yapılandırma ayarlarınızı düzenleyin ({provider} için geçerli ayarlar)" + }, + "configSettings.done": { + "defaultMessage": "Bitti" + }, + "configSettings.editConfiguration": { + "defaultMessage": "Yapılandırmayı Düzenle" + }, + "configSettings.enterValue": { + "defaultMessage": "{name}'yi girin" + }, + "configSettings.noSettings": { + "defaultMessage": "Hiçbir yapılandırma ayarı bulunamadı." + }, + "configSettings.resetChanges": { + "defaultMessage": "Değişiklikleri Sıfırla" + }, + "configSettings.saveFailed": { + "defaultMessage": "Kaydetme Başarısız Oldu" + }, + "configSettings.saveFailedMsg": { + "defaultMessage": "\"{name}\" kaydedilemedi" + }, + "configSettings.saving": { + "defaultMessage": "Kaydediliyor..." + }, + "configSettings.title": { + "defaultMessage": "Yapılandırma" + }, + "configureApproveMode.cancel": { + "defaultMessage": "İptal" + }, + "configureApproveMode.description": { + "defaultMessage": "Onay istekleri tüm araç isteklerine verilebilir veya hangi eylemlerin entegrasyona ihtiyaç duyabileceği belirlenebilir" + }, + "configureApproveMode.manualApproval": { + "defaultMessage": "Manuel onay" + }, + "configureApproveMode.manualApprovalDescription": { + "defaultMessage": "Tüm araçlar, uzantılar ve dosya değişiklikleri insan onayı gerektirecektir" + }, + "configureApproveMode.save": { + "defaultMessage": "Kaydet" + }, + "configureApproveMode.saving": { + "defaultMessage": "Kaydediliyor..." + }, + "configureApproveMode.smartApproval": { + "defaultMessage": "Akıllı onay" + }, + "configureApproveMode.smartApprovalDescription": { + "defaultMessage": "Risk düzeyine göre hangi eylemlerin onaylanması gerektiğini akıllıca belirleyin" + }, + "configureApproveMode.title": { + "defaultMessage": "Onay modunu yapılandırma" + }, + "confirmationModal.defaultCancel": { + "defaultMessage": "Hayır" + }, + "confirmationModal.defaultConfirm": { + "defaultMessage": "Evet" + }, + "confirmationModal.processing": { + "defaultMessage": "İşleniyor..." + }, + "conversationLimitsDropdown.conversationLimits": { + "defaultMessage": "Konuşma Sınırları" + }, + "conversationLimitsDropdown.maxTurns": { + "defaultMessage": "Maksimum Dönüş" + }, + "conversationLimitsDropdown.maxTurnsDescription": { + "defaultMessage": "Goose kullanıcı girişi istemeden önce maksimum aracı dönüşü" + }, + "costTracker.costUnavailable": { + "defaultMessage": "{model} için maliyet verileri mevcut değil ({inputTokens} girişi, {outputTokens} çıkış jetonları)" + }, + "costTracker.inputOutputTooltip": { + "defaultMessage": "Girdi: {inputTokens} belirteçleri ({inputCost}) | Çıktı: {outputTokens} belirteçleri ({outputCost})" + }, + "costTracker.pricingUnavailable": { + "defaultMessage": "{model} için fiyatlandırma verileri mevcut değil" + }, + "costTracker.totalSessionCost": { + "defaultMessage": "Toplam oturum maliyeti: {cost}" + }, + "createEditRecipe.clickToGenerateDeeplink": { + "defaultMessage": "Derin bağlantı oluşturmak için tıklayın" + }, + "createEditRecipe.close": { + "defaultMessage": "Kapat" + }, + "createEditRecipe.copied": { + "defaultMessage": "Kopyalandı!" + }, + "createEditRecipe.copy": { + "defaultMessage": "Kopyala" + }, + "createEditRecipe.copyLinkDescription": { + "defaultMessage": "Arkadaşlarınızla paylaşmak için bu bağlantıyı kopyalayın veya açmak için doğrudan Chrome'ye yapıştırın" + }, + "createEditRecipe.createRecipeTitle": { + "defaultMessage": "Tarif Oluştur" + }, + "createEditRecipe.createSubtitle": { + "defaultMessage": "Yeniden kullanılabilir sohbet oturumlarına yönelik temsilci davranışını ve yeteneklerini tanımlamak için yeni bir tarif oluşturun." + }, + "createEditRecipe.editSubtitle": { + "defaultMessage": "Yeni bir oturumda temsilcinin davranışını değiştirmek için aşağıdaki tarifi düzenleyebilirsiniz." + }, + "createEditRecipe.generatingDeeplink": { + "defaultMessage": "Derin bağlantı oluşturuluyor..." + }, + "createEditRecipe.learnMore": { + "defaultMessage": "Daha fazla bilgi edinin" + }, + "createEditRecipe.recipeSavedAndLaunchedMsg": { + "defaultMessage": "Tarif başarıyla kaydedildi ve başlatıldı" + }, + "createEditRecipe.recipeSavedMsg": { + "defaultMessage": "Tarif başarıyla kaydedildi" + }, + "createEditRecipe.saveAndRunFailed": { + "defaultMessage": "Kaydetme ve Çalıştırma Başarısız Oldu" + }, + "createEditRecipe.saveAndRunFailedMsg": { + "defaultMessage": "Tarif kaydedilip çalıştırılamadı: {error}" + }, + "createEditRecipe.saveAndRunRecipe": { + "defaultMessage": "Tarifi Kaydet ve Çalıştır" + }, + "createEditRecipe.saveFailed": { + "defaultMessage": "Kaydetme Başarısız Oldu" + }, + "createEditRecipe.saveFailedMsg": { + "defaultMessage": "Tarif kaydedilemedi: {error}" + }, + "createEditRecipe.saveRecipe": { + "defaultMessage": "Tarifi Kaydet" + }, + "createEditRecipe.saving": { + "defaultMessage": "Kaydediliyor..." + }, + "createEditRecipe.validationFailed": { + "defaultMessage": "Doğrulama Başarısız" + }, + "createEditRecipe.validationMsg": { + "defaultMessage": "Lütfen gerekli tüm alanları doldurun ve JSON şemasının geçerli olduğundan emin olun." + }, + "createEditRecipe.viewEditRecipeTitle": { + "defaultMessage": "Tarifi görüntüle/düzenle" + }, + "createRecipeFromSession.analyzingTitle": { + "defaultMessage": "Konuşmanızı analiz etme" + }, + "createRecipeFromSession.cancel": { + "defaultMessage": "İptal" + }, + "createRecipeFromSession.createAndRunRecipe": { + "defaultMessage": "Tarif Oluştur ve Çalıştır" + }, + "createRecipeFromSession.createRecipe": { + "defaultMessage": "Tarif Oluştur" + }, + "createRecipeFromSession.creating": { + "defaultMessage": "Oluşturuluyor..." + }, + "createRecipeFromSession.extractingInsights": { + "defaultMessage": "Sohbetinizden içgörüler çıkarma" + }, + "createRecipeFromSession.failedToCreateDefaultMsg": { + "defaultMessage": "Tarif oluşturulurken beklenmeyen bir hata oluştu. Lütfen tekrar deneyin." + }, + "createRecipeFromSession.failedToCreateTitle": { + "defaultMessage": "Tarif oluşturulamadı" + }, + "createRecipeFromSession.stageComplete": { + "defaultMessage": "Tamamla!" + }, + "createRecipeFromSession.stageExtracting": { + "defaultMessage": "Ana konular çıkarılıyor..." + }, + "createRecipeFromSession.stageFinalizing": { + "defaultMessage": "Ayrıntılar tamamlanıyor..." + }, + "createRecipeFromSession.stageGenerating": { + "defaultMessage": "Tarif yapısı oluşturuluyor..." + }, + "createRecipeFromSession.stageIdentifying": { + "defaultMessage": "Anahtar kalıpları belirlemek..." + }, + "createRecipeFromSession.stageReading": { + "defaultMessage": "Konuşmanızı okuyorum..." + }, + "createRecipeFromSession.subtitle": { + "defaultMessage": "Mevcut konuşmanıza göre yeniden kullanılabilir bir tarif oluşturun." + }, + "createRecipeFromSession.title": { + "defaultMessage": "Oturumdan Tarif Oluştur" + }, + "createSubRecipeInline.cancel": { + "defaultMessage": "İptal" + }, + "createSubRecipeInline.closeModal": { + "defaultMessage": "Alt tarif modelini oluşturmayı kapat" + }, + "createSubRecipeInline.createAndAdd": { + "defaultMessage": "Alt Tarif Oluştur ve Ekle" + }, + "createSubRecipeInline.createdSuccess": { + "defaultMessage": "Alt tarif başarıyla oluşturuldu" + }, + "createSubRecipeInline.creating": { + "defaultMessage": "Oluşturuluyor..." + }, + "createSubRecipeInline.duplicateName": { + "defaultMessage": "Yinelenen Ad" + }, + "createSubRecipeInline.duplicateNameMsg": { + "defaultMessage": "\"{name}\" adlı bir alt tarif zaten mevcut. Lütfen benzersiz bir ad kullanın." + }, + "createSubRecipeInline.instructionsLabel": { + "defaultMessage": "Talimatlar" + }, + "createSubRecipeInline.instructionsPlaceholder": { + "defaultMessage": "Bu alt tarif çağrıldığında AI için talimatlar..." + }, + "createSubRecipeInline.nameHint": { + "defaultMessage": "Araç adını oluşturmak için kullanılan benzersiz tanımlayıcı" + }, + "createSubRecipeInline.nameLabel": { + "defaultMessage": "İsim" + }, + "createSubRecipeInline.namePlaceholder": { + "defaultMessage": "ör. güvenlik_tarama" + }, + "createSubRecipeInline.preconfiguredValues": { + "defaultMessage": "Önceden Yapılandırılmış Değerler" + }, + "createSubRecipeInline.preconfiguredValuesHint": { + "defaultMessage": "Her zaman alt tarife aktarılan isteğe bağlı parametre değerleri" + }, + "createSubRecipeInline.recipeDescriptionLabel": { + "defaultMessage": "Tarif Açıklaması" + }, + "createSubRecipeInline.recipeDescriptionPlaceholder": { + "defaultMessage": "Bu tarif uygulandığında ne yapar?" + }, + "createSubRecipeInline.recipeTitleLabel": { + "defaultMessage": "Tarif Başlığı" + }, + "createSubRecipeInline.recipeTitlePlaceholder": { + "defaultMessage": "örneğin, Güvenlik Analizi Aracı" + }, + "createSubRecipeInline.saveFailed": { + "defaultMessage": "Kaydetme Başarısız Oldu" + }, + "createSubRecipeInline.saveFailedMsg": { + "defaultMessage": "Alt tarif kaydedilemedi: {error}" + }, + "createSubRecipeInline.sequentialHint": { + "defaultMessage": "(Birden fazla örneğin sıralı yürütülmesini zorlar)" + }, + "createSubRecipeInline.sequentialLabel": { + "defaultMessage": "Tekrarlandığında sıralı" + }, + "createSubRecipeInline.subtitle": { + "defaultMessage": "Ana tarifinizde çağrılabilir bir araç olarak kullanmak için basit bir tarif oluşturun" + }, + "createSubRecipeInline.title": { + "defaultMessage": "Yeni Alt Tarif Oluştur" + }, + "createSubRecipeInline.toolDescriptionLabel": { + "defaultMessage": "Araç Açıklaması" + }, + "createSubRecipeInline.toolDescriptionPlaceholder": { + "defaultMessage": "Bu bir araç olarak adlandırıldığında gösterilen isteğe bağlı açıklama" + }, + "createSubRecipeInline.validationFailed": { + "defaultMessage": "Doğrulama Başarısız" + }, + "createSubRecipeInline.validationMsg": { + "defaultMessage": "Ad, başlık, tarif açıklaması ve talimatlar gereklidir." + }, + "creditsExhaustedNotification.addCredits": { + "defaultMessage": "Kredi ekle" + }, + "creditsExhaustedNotification.insufficientCredits": { + "defaultMessage": "Yetersiz Kredi" + }, + "cronPicker.april": { + "defaultMessage": "Nisan" + }, + "cronPicker.at": { + "defaultMessage": "en" + }, + "cronPicker.atMinute": { + "defaultMessage": "dakikada" + }, + "cronPicker.atSecond": { + "defaultMessage": "ikinci sırada" + }, + "cronPicker.august": { + "defaultMessage": "Ağustos" + }, + "cronPicker.cronExpression": { + "defaultMessage": "Cron ifadesi" + }, + "cronPicker.custom": { + "defaultMessage": "Özel cron" + }, + "cronPicker.day": { + "defaultMessage": "Gün" + }, + "cronPicker.december": { + "defaultMessage": "Aralık" + }, + "cronPicker.emptyCronError": { + "defaultMessage": "Cron ifadesi boş olamaz" + }, + "cronPicker.every": { + "defaultMessage": "Her" + }, + "cronPicker.february": { + "defaultMessage": "Şubat" + }, + "cronPicker.friday": { + "defaultMessage": "Cuma" + }, + "cronPicker.hour": { + "defaultMessage": "Saat" + }, + "cronPicker.inMonth": { + "defaultMessage": "içinde" + }, + "cronPicker.invalidDayOfMonth": { + "defaultMessage": "Gün 1 ile {max} arasında olmalıdır" + }, + "cronPicker.january": { + "defaultMessage": "Ocak" + }, + "cronPicker.july": { + "defaultMessage": "Temmuz" + }, + "cronPicker.june": { + "defaultMessage": "Haziran" + }, + "cronPicker.march": { + "defaultMessage": "Mart" + }, + "cronPicker.may": { + "defaultMessage": "Mayıs" + }, + "cronPicker.minute": { + "defaultMessage": "Dakika" + }, + "cronPicker.mode": { + "defaultMessage": "Mod" + }, + "cronPicker.monday": { + "defaultMessage": "Pazartesi" + }, + "cronPicker.month": { + "defaultMessage": "Ay" + }, + "cronPicker.november": { + "defaultMessage": "Kasım" + }, + "cronPicker.october": { + "defaultMessage": "Ekim" + }, + "cronPicker.on": { + "defaultMessage": "üzerinde" + }, + "cronPicker.onDay": { + "defaultMessage": "gününde" + }, + "cronPicker.quarter": { + "defaultMessage": "Çeyrek" + }, + "cronPicker.saturday": { + "defaultMessage": "cumartesi" + }, + "cronPicker.september": { + "defaultMessage": "Eylül" + }, + "cronPicker.startingMonth": { + "defaultMessage": "başlangıç ayı" + }, + "cronPicker.sunday": { + "defaultMessage": "Pazar" + }, + "cronPicker.thursday": { + "defaultMessage": "perşembe" + }, + "cronPicker.tuesday": { + "defaultMessage": "Salı" + }, + "cronPicker.wednesday": { + "defaultMessage": "Çarşamba" + }, + "cronPicker.week": { + "defaultMessage": "Hafta" + }, + "cronPicker.year": { + "defaultMessage": "Yıl" + }, + "customProviderForm.add": { + "defaultMessage": "Ekle" + }, + "customProviderForm.anthropicCompatible": { + "defaultMessage": "Anthropic Uyumlu" + }, + "customProviderForm.apiBasePath": { + "defaultMessage": "API Temel Yol (isteğe bağlı)" + }, + "customProviderForm.apiBasePathHint": { + "defaultMessage": "Varsayılan API yolunu geçersiz kılın. Sağlayıcının varsayılan yolunu kullanmak için boş bırakın." + }, + "customProviderForm.apiBasePathPlaceholder": { + "defaultMessage": "ör. v1/sohbet/tamamlamalar veya proje_id/v1" + }, + "customProviderForm.apiKey": { + "defaultMessage": "API Anahtarı" + }, + "customProviderForm.apiKeyPlaceholderExisting": { + "defaultMessage": "Mevcut anahtarı korumak için boş bırakın" + }, + "customProviderForm.apiKeyPlaceholderNew": { + "defaultMessage": "API anahtarınız" + }, + "customProviderForm.apiKeyRequired": { + "defaultMessage": "API anahtarı gerekli" + }, + "customProviderForm.apiUrl": { + "defaultMessage": "API URL'si" + }, + "customProviderForm.apiUrlPlaceholder": { + "defaultMessage": "https://api.example.com" + }, + "customProviderForm.apiUrlRequired": { + "defaultMessage": "API URL'si gerekli" + }, + "customProviderForm.attachments": { + "defaultMessage": "Ekler" + }, + "customProviderForm.authHint": { + "defaultMessage": "Ollama gibi yerel LLM'ler genellikle API anahtarı gerektirmez." + }, + "customProviderForm.authentication": { + "defaultMessage": "Kimlik doğrulama" + }, + "customProviderForm.availableModels": { + "defaultMessage": "Mevcut Modeller (virgülle ayrılmış)" + }, + "customProviderForm.back": { + "defaultMessage": "← Geri" + }, + "customProviderForm.cancel": { + "defaultMessage": "İptal" + }, + "customProviderForm.cannotDeleteActive": { + "defaultMessage": "Bu sağlayıcıyı şu anda kullanımdayken silemezsiniz. Lütfen önce farklı bir modele geçin." + }, + "customProviderForm.chooseSetup": { + "defaultMessage": "Sağlayıcınızı nasıl ayarlamak istediğinizi seçin." + }, + "customProviderForm.clear": { + "defaultMessage": "Temizle" + }, + "customProviderForm.configureManually": { + "defaultMessage": "Manuel olarak yapılandır" + }, + "customProviderForm.configureManuallyDesc": { + "defaultMessage": "Tüm sağlayıcı ayrıntılarını kendiniz girin" + }, + "customProviderForm.confirmDelete": { + "defaultMessage": "Silmeyi Onayla" + }, + "customProviderForm.createProvider": { + "defaultMessage": "Sağlayıcı Oluştur" + }, + "customProviderForm.customHeaders": { + "defaultMessage": "Özel Başlıklar" + }, + "customProviderForm.customHeadersHint": { + "defaultMessage": "Sağlayıcıya gönderilen isteklere dahil etmek için özel HTTP üstbilgileri ekleyin. Her iki alanı da doldurduktan sonra eklemek için \"+\" butonuna tıklayın." + }, + "customProviderForm.deleteConfirmation": { + "defaultMessage": "Bu özel sağlayıcıyı silmek istediğinizden emin misiniz? Bu, sağlayıcıyı ve depolanan API anahtarını kalıcı olarak kaldıracaktır. Bu eylem geri alınamaz." + }, + "customProviderForm.deleteProvider": { + "defaultMessage": "Sağlayıcıyı Sil" + }, + "customProviderForm.displayName": { + "defaultMessage": "Görünen Ad" + }, + "customProviderForm.displayNamePlaceholder": { + "defaultMessage": "Sağlayıcı Adınız" + }, + "customProviderForm.displayNameRequired": { + "defaultMessage": "Görünen ad gerekli" + }, + "customProviderForm.docs": { + "defaultMessage": "Dokümanlar" + }, + "customProviderForm.headerBothRequired": { + "defaultMessage": "Hem başlık adı hem de değer girilmelidir" + }, + "customProviderForm.headerDuplicate": { + "defaultMessage": "Bu ada sahip bir başlık zaten mevcut" + }, + "customProviderForm.headerNamePlaceholder": { + "defaultMessage": "Başlık adı" + }, + "customProviderForm.headerNoSpaces": { + "defaultMessage": "Başlık adı boşluk içeremez" + }, + "customProviderForm.modelsPlaceholder": { + "defaultMessage": "model-a, model-b, model-c" + }, + "customProviderForm.modelsRequired": { + "defaultMessage": "En az bir model gerekli" + }, + "customProviderForm.ollamaCompatible": { + "defaultMessage": "Ollama Uyumlu" + }, + "customProviderForm.openaiCompatible": { + "defaultMessage": "OpenAI Uyumlu" + }, + "customProviderForm.providerType": { + "defaultMessage": "Sağlayıcı Türü" + }, + "customProviderForm.reasoning": { + "defaultMessage": "muhakeme" + }, + "customProviderForm.requiresApiKey": { + "defaultMessage": "Bu sağlayıcı bir API anahtarı gerektiriyor" + }, + "customProviderForm.startFromTemplate": { + "defaultMessage": "Sağlayıcı şablonundan başlayın" + }, + "customProviderForm.startFromTemplateDesc": { + "defaultMessage": "Bilinen bir sağlayıcı seçin, yapılandırmayı otomatik olarak dolduralım" + }, + "customProviderForm.submitError": { + "defaultMessage": "Sağlayıcı kaydedilemedi. Lütfen yapılandırmanızı kontrol edip tekrar deneyin." + }, + "customProviderForm.supportsStreaming": { + "defaultMessage": "Sağlayıcı akış yanıtlarını destekler" + }, + "customProviderForm.toolCalling": { + "defaultMessage": "Takım çağırma" + }, + "customProviderForm.updateProvider": { + "defaultMessage": "Sağlayıcıyı Güncelle" + }, + "customProviderForm.usingTemplate": { + "defaultMessage": "Şablonu kullanma: {name}" + }, + "customProviderForm.valuePlaceholder": { + "defaultMessage": "Değer" + }, + "defaultCardButtons.configureSettings": { + "defaultMessage": "{name} ayarlarını yapılandırın" + }, + "defaultCardButtons.deleteSettings": { + "defaultMessage": "{name} ayarlarını silin" + }, + "defaultCardButtons.editSettings": { + "defaultMessage": "{name} ayarlarını düzenleyin" + }, + "defaultCardButtons.getStarted": { + "defaultMessage": "goose'yi kullanmaya başlayın!" + }, + "defaultProviderSetupForm.apiHostLabel": { + "defaultMessage": "API Ana Bilgisayar" + }, + "defaultProviderSetupForm.apiHostPlaceholder": { + "defaultMessage": "https://api.example.com" + }, + "defaultProviderSetupForm.apiKeyLabel": { + "defaultMessage": "API Anahtarı" + }, + "defaultProviderSetupForm.apiKeyPlaceholder": { + "defaultMessage": "API anahtarınız" + }, + "defaultProviderSetupForm.hideOptions": { + "defaultMessage": "{count} seçeneklerini gizle" + }, + "defaultProviderSetupForm.loadingConfig": { + "defaultMessage": "Yapılandırma değerleri yükleniyor..." + }, + "defaultProviderSetupForm.modelsLabel": { + "defaultMessage": "Modeller" + }, + "defaultProviderSetupForm.modelsPlaceholder": { + "defaultMessage": "model-a, model-b" + }, + "defaultProviderSetupForm.noConfigParameters": { + "defaultMessage": "Bu sağlayıcı için yapılandırma parametresi yok." + }, + "defaultProviderSetupForm.showOptions": { + "defaultMessage": "{count} seçeneklerini göster" + }, + "diagnosticsModal.attachHint": { + "defaultMessage": "Bir hata bildirirseniz, teşhis raporunu buna eklemeyi düşünün." + }, + "diagnosticsModal.cancel": { + "defaultMessage": "İptal" + }, + "diagnosticsModal.configSettings": { + "defaultMessage": "Yapılandırma ayarları" + }, + "diagnosticsModal.description": { + "defaultMessage": "Ekiple paylaşmak için bir tanılama zip dosyası indirebilir veya sistem ayrıntılarınızı önceden doldurarak doğrudan GitHub üzerinden bir hata dosyası oluşturabilirsiniz. Bir teşhis raporu aşağıdakileri içerir:" + }, + "diagnosticsModal.diagnosticsErrorMsg": { + "defaultMessage": "Teşhis indirilemedi" + }, + "diagnosticsModal.diagnosticsErrorTitle": { + "defaultMessage": "Teşhis Hatası" + }, + "diagnosticsModal.download": { + "defaultMessage": "İndir" + }, + "diagnosticsModal.downloading": { + "defaultMessage": "İndiriliyor..." + }, + "diagnosticsModal.fileBug": { + "defaultMessage": "GitHub'de Dosya Hatası" + }, + "diagnosticsModal.logFiles": { + "defaultMessage": "Son günlük dosyaları" + }, + "diagnosticsModal.opening": { + "defaultMessage": "Açılıyor..." + }, + "diagnosticsModal.reportProblem": { + "defaultMessage": "Sorun Bildir" + }, + "diagnosticsModal.sensitiveWarning": { + "defaultMessage": "Oturumunuz hassas bilgiler içeriyorsa teşhis dosyasını herkese açık olarak paylaşmayın." + }, + "diagnosticsModal.sessionMessages": { + "defaultMessage": "Mevcut oturum mesajlarınız" + }, + "diagnosticsModal.systemInfo": { + "defaultMessage": "Temel sistem bilgisi" + }, + "diagnosticsModal.systemInfoErrorMsg": { + "defaultMessage": "Sistem bilgisi alınamadı" + }, + "diagnosticsModal.systemInfoErrorTitle": { + "defaultMessage": "Hata" + }, + "dialog.close": { + "defaultMessage": "Kapat" + }, + "dictationSettings.addApiKey": { + "defaultMessage": "API Anahtarını Ekle" + }, + "dictationSettings.apiKey": { + "defaultMessage": "API Anahtarı" + }, + "dictationSettings.cancel": { + "defaultMessage": "İptal" + }, + "dictationSettings.chooseVoiceConversion": { + "defaultMessage": "Sesin metne nasıl dönüştürüleceğini seçin" + }, + "dictationSettings.configureApiKey": { + "defaultMessage": "{settingsPath}'de API anahtarını yapılandırın" + }, + "dictationSettings.configured": { + "defaultMessage": "(Yapılandırılmış)" + }, + "dictationSettings.configuredIn": { + "defaultMessage": "✓ {settingsPath}'de yapılandırılmıştır" + }, + "dictationSettings.disabled": { + "defaultMessage": "Devre dışı" + }, + "dictationSettings.enterApiKey": { + "defaultMessage": "API anahtarınızı girin" + }, + "dictationSettings.notConfigured": { + "defaultMessage": "(yapılandırılmamış)" + }, + "dictationSettings.removeApiKey": { + "defaultMessage": "API Anahtarını Kaldır" + }, + "dictationSettings.requiredForTranscription": { + "defaultMessage": "Transkripsiyon için gerekli" + }, + "dictationSettings.save": { + "defaultMessage": "Kaydet" + }, + "dictationSettings.updateApiKey": { + "defaultMessage": "API Anahtarını Güncelle" + }, + "dictationSettings.voiceDictationProvider": { + "defaultMessage": "Ses Dikte Sağlayıcısı" + }, + "dirSwitcher.chooseDirectory": { + "defaultMessage": "Dizin seç…" + }, + "dirSwitcher.currentDirectory": { + "defaultMessage": "Geçerli dizin" + }, + "dirSwitcher.failedToUpdateWorkingDir": { + "defaultMessage": "Çalışma dizini güncellenemedi" + }, + "dirSwitcher.gitWorktrees": { + "defaultMessage": "Git çalışma ağaçları" + }, + "dirSwitcher.noWorktreesFound": { + "defaultMessage": "Hiçbir çalışma ağacı bulunamadı" + }, + "dirSwitcher.openInFinder": { + "defaultMessage": "Dosya yöneticisinde aç" + }, + "dirSwitcher.recentDirectories": { + "defaultMessage": "Son dizinler" + }, + "elicitationRequest.accept": { + "defaultMessage": "Kabul et" + }, + "elicitationRequest.cancelled": { + "defaultMessage": "Bilgi talebi iptal edildi." + }, + "elicitationRequest.defaultMessage": { + "defaultMessage": "Goose'nin sizden bazı bilgilere ihtiyacı var." + }, + "elicitationRequest.expired": { + "defaultMessage": "Bu isteğin süresi doldu. Uzantının tekrar sorması gerekecek." + }, + "elicitationRequest.submit": { + "defaultMessage": "Gönder" + }, + "elicitationRequest.submitted": { + "defaultMessage": "Bilgiler gönderildi" + }, + "elicitationRequest.waitingForResponse": { + "defaultMessage": "Yanıtınızı bekliyorum ({timeRemaining} kaldı)" + }, + "envVarsSection.add": { + "defaultMessage": "Ekle" + }, + "envVarsSection.bothRequired": { + "defaultMessage": "Hem değişken adı hem de değer girilmelidir" + }, + "envVarsSection.envVarsDescription": { + "defaultMessage": "Ortam değişkenleri için anahtar/değer çiftleri ekleyin. Her iki alanı da doldurduktan sonra eklemek için \"+\" butonuna tıklayın. Mevcut gizli değerleri değiştirmek için düzenle düğmesine tıklayın." + }, + "envVarsSection.environmentVariables": { + "defaultMessage": "Ortam Değişkenleri" + }, + "envVarsSection.noSpaces": { + "defaultMessage": "Değişken adı boşluk içeremez" + }, + "envVarsSection.value": { + "defaultMessage": "Değer" + }, + "envVarsSection.variableName": { + "defaultMessage": "Değişken adı" + }, + "environmentBadge.dev": { + "defaultMessage": "Geliştirici" + }, + "errorBoundary.errorGeneric": { + "defaultMessage": "Bir hata oluştu." + }, + "errorBoundary.errorWithVersion": { + "defaultMessage": "Goose v{version}'de bir hata oluştu." + }, + "errorBoundary.heading": { + "defaultMessage": "Korna!" + }, + "errorBoundary.reload": { + "defaultMessage": "Yeniden yükle" + }, + "extensionConfigFields.commandLabel": { + "defaultMessage": "Komut" + }, + "extensionConfigFields.commandPlaceholder": { + "defaultMessage": "örneğin npx -y @modelcontextprotocol/uzantımım [dosya yolu]" + }, + "extensionConfigFields.commandRequired": { + "defaultMessage": "Komut gerekli" + }, + "extensionConfigFields.endpointLabel": { + "defaultMessage": "Uç nokta" + }, + "extensionConfigFields.endpointPlaceholder": { + "defaultMessage": "Uç nokta URL'sini girin..." + }, + "extensionConfigFields.endpointRequired": { + "defaultMessage": "Uç nokta URL'si gerekli" + }, + "extensionInfoFields.descriptionLabel": { + "defaultMessage": "Açıklama" + }, + "extensionInfoFields.descriptionPlaceholder": { + "defaultMessage": "İsteğe bağlı açıklama..." + }, + "extensionInfoFields.extensionName": { + "defaultMessage": "Uzantı Adı" + }, + "extensionInfoFields.extensionNamePlaceholder": { + "defaultMessage": "Uzantı adını girin..." + }, + "extensionInfoFields.nameRequired": { + "defaultMessage": "Ad gerekli" + }, + "extensionInfoFields.typeHttp": { + "defaultMessage": "HTTP" + }, + "extensionInfoFields.typeLabel": { + "defaultMessage": "Tür" + }, + "extensionInfoFields.typeSseUnsupported": { + "defaultMessage": "SSE (desteklenmiyor)" + }, + "extensionInfoFields.typeStandardIo": { + "defaultMessage": "Standart IO (STDIO)" + }, + "extensionInfoFields.typeStdio": { + "defaultMessage": "STDIO" + }, + "extensionInfoFields.typeStreamableHttp": { + "defaultMessage": "Yayınlanabilir HTTP" + }, + "extensionInstallModal.alreadyInstalledMessage": { + "defaultMessage": "''{name}'' uzantısı zaten başarıyla yüklendi. Kullanmak için yeni bir sohbet oturumu başlatın." + }, + "extensionInstallModal.alreadyInstalledTitle": { + "defaultMessage": "''{name}'' Uzantısı Zaten Yüklü" + }, + "extensionInstallModal.blockedMessage": { + "defaultMessage": "Bu uzantı komutu izin verilenler listesinde değil ve kurulumu engellendi. Uzantı: {name} Komut: {command} Bu uzantının onayını istemek için yöneticinizle iletişime geçin." + }, + "extensionInstallModal.blockedTitle": { + "defaultMessage": "Uzantı Kurulumu Engellendi" + }, + "extensionInstallModal.cancel": { + "defaultMessage": "İptal" + }, + "extensionInstallModal.installAnyway": { + "defaultMessage": "Yine de Yükle" + }, + "extensionInstallModal.installing": { + "defaultMessage": "Yükleniyor..." + }, + "extensionInstallModal.no": { + "defaultMessage": "Hayır" + }, + "extensionInstallModal.ok": { + "defaultMessage": "tamam" + }, + "extensionInstallModal.trustedMessage": { + "defaultMessage": "{name} uzantısını yüklemek istediğinizden emin misiniz? Komut: {command}" + }, + "extensionInstallModal.trustedTitle": { + "defaultMessage": "Uzantı Kurulumunu Onaylayın" + }, + "extensionInstallModal.unknownCommand": { + "defaultMessage": "Bilinmeyen Komut" + }, + "extensionInstallModal.untrustedMessageWithCommand": { + "defaultMessage": "{securityMessage} Uzantı: {name} Komut: {command} Bu konuda emin değilseniz yöneticinizle iletişime geçin." + }, + "extensionInstallModal.untrustedMessageWithUrl": { + "defaultMessage": "{securityMessage} Uzantı: {name} URL: {url} Bu konuda emin değilseniz yöneticinizle iletişime geçin." + }, + "extensionInstallModal.untrustedSecurityMessage": { + "defaultMessage": "Bu uzantı komutu izin verilenler listesinde değil ve görüşmelerinize erişebilecek ve ek işlevsellik sağlayabilecek. Güvenilmeyen kaynaklardan gelen uzantıların yüklenmesi güvenlik riskleri oluşturabilir." + }, + "extensionInstallModal.untrustedTitle": { + "defaultMessage": "Güvenilmeyen Uzantı Yüklensin mi?" + }, + "extensionInstallModal.yes": { + "defaultMessage": "Evet" + }, + "extensionItem.configureExtension": { + "defaultMessage": "{name} Uzantısını Yapılandırın" + }, + "extensionItem.toggleExtension": { + "defaultMessage": "{name} uzantısını Açık veya Kapalı duruma getirin" + }, + "extensionList.availableExtensions": { + "defaultMessage": "Kullanılabilir Uzantılar ({count})" + }, + "extensionList.builtInExtension": { + "defaultMessage": "Yerleşik uzantı" + }, + "extensionList.defaultExtensions": { + "defaultMessage": "Varsayılan Uzantılar ({count})" + }, + "extensionList.noExtensions": { + "defaultMessage": "Uzantı yok" + }, + "extensionModal.cancel": { + "defaultMessage": "İptal" + }, + "extensionModal.closeWithoutSaving": { + "defaultMessage": "Kaydetmeden Kapat" + }, + "extensionModal.confirmRemoval": { + "defaultMessage": "Kaldırma işlemini onaylayın" + }, + "extensionModal.deleteDescription": { + "defaultMessage": "Bu işlem, bu uzantıyı ve tüm ayarlarını kalıcı olarak kaldıracaktır." + }, + "extensionModal.deleteExtensionTitle": { + "defaultMessage": "\"{name}\" Uzantısını Sil" + }, + "extensionModal.installationNotes": { + "defaultMessage": "Kurulum Notları" + }, + "extensionModal.removeExtension": { + "defaultMessage": "Uzantıyı kaldır" + }, + "extensionModal.unsavedChangesMessage": { + "defaultMessage": "Uzantı yapılandırmasında kaydedilmemiş değişiklikleriniz var. Kaydetmeden kapatmak istediğinizden emin misiniz?" + }, + "extensionModal.unsavedChangesTitle": { + "defaultMessage": "Kaydedilmemiş Değişiklikler" + }, + "extensionTimeoutField.timeoutLabel": { + "defaultMessage": "Zaman aşımı" + }, + "extensionsSection.addCustomExtension": { + "defaultMessage": "Özel uzantı ekle" + }, + "extensionsSection.addExtension": { + "defaultMessage": "Uzantı Ekle" + }, + "extensionsSection.browseExtensions": { + "defaultMessage": "Uzantılara göz atın" + }, + "extensionsSection.saveChanges": { + "defaultMessage": "Değişiklikleri Kaydet" + }, + "extensionsSection.updateExtension": { + "defaultMessage": "Uzantıyı Güncelle" + }, + "extensionsView.addCustomExtension": { + "defaultMessage": "Özel uzantı ekle" + }, + "extensionsView.addExtension": { + "defaultMessage": "Uzantı Ekle" + }, + "extensionsView.browseExtensions": { + "defaultMessage": "Uzantılara göz atın" + }, + "extensionsView.defaultNote": { + "defaultMessage": "Burada etkinleştirilen uzantılar, yeni sohbetler için varsayılan olarak kullanılır. Ayrıca sohbet sırasında etkin uzantıları da değiştirebilirsiniz." + }, + "extensionsView.description": { + "defaultMessage": "Bu uzantılar Model Bağlam Protokolünü (MCP) kullanır. Üç ana bileşeni kullanarak Goose'nin yeteneklerini genişletebilirler: İstemler, Kaynaklar ve Araçlar. Aramak için {searchShortcut}." + }, + "extensionsView.heading": { + "defaultMessage": "Uzantılar" + }, + "extensionsView.searchPlaceholder": { + "defaultMessage": "Uzantıları ara..." + }, + "externalBackendSection.certFingerprint": { + "defaultMessage": "Sertifika Parmak İzi (isteğe bağlı)" + }, + "externalBackendSection.certFingerprintHelp": { + "defaultMessage": "Belirli bir TLS sertifikası parmak izini sabitleyin. Atlanırsa, sertifikaya ilk kullanımda güvenilir (TOFU)." + }, + "externalBackendSection.certFingerprintPlaceholder": { + "defaultMessage": "AA:BB:CC:... veya sha256/base64" + }, + "externalBackendSection.description": { + "defaultMessage": "Varsayılan olarak goose sizin için bir sunucu başlatır; harici bir goose sunucusuna bağlanmak için bunu kullanın" + }, + "externalBackendSection.restartNote": { + "defaultMessage": "Değişikliklerin etkili olması için Goose'nin yeniden başlatılması gerekir. Yeni sohbet pencereleri harici sunucuya bağlanacaktır." + }, + "externalBackendSection.secretKey": { + "defaultMessage": "Gizli Anahtar" + }, + "externalBackendSection.secretKeyHelp": { + "defaultMessage": "goosed sunucusunda yapılandırılan gizli anahtar (GOOSE_SERVER__SECRET_KEY)" + }, + "externalBackendSection.secretKeyPlaceholder": { + "defaultMessage": "Sunucunun gizli anahtarını girin" + }, + "externalBackendSection.serverUrl": { + "defaultMessage": "Sunucu URL'si" + }, + "externalBackendSection.title": { + "defaultMessage": "Goose Sunucu" + }, + "externalBackendSection.urlFormatError": { + "defaultMessage": "Geçersiz URL biçimi" + }, + "externalBackendSection.urlProtocolError": { + "defaultMessage": "URL, http veya https protokolünü kullanmalıdır" + }, + "externalBackendSection.useExternalServer": { + "defaultMessage": "Harici sunucu kullan" + }, + "externalBackendSection.useExternalServerDescription": { + "defaultMessage": "Başka bir yerde çalışan bir goose sunucusuna bağlanın (uygulamanın yeniden başlatılmasını gerektirir)" + }, + "freeOptionCards.chooseOption": { + "defaultMessage": "Başlamak için bir seçenek seçin." + }, + "freeOptionCards.freeAndPrivate": { + "defaultMessage": "Ücretsiz ve Özel" + }, + "freeOptionCards.localModelDescription": { + "defaultMessage": "Bir model indirin ve tamamen makinenizde çalıştırın. API anahtarı yok, hesap yok." + }, + "freeOptionCards.localModelTitle": { + "defaultMessage": "Yerel Bir Model Kullanın" + }, + "freeOptionCards.nanogptDescription": { + "defaultMessage": "7 gün boyunca 60 milyon ücretsiz jeton almak için kaydolun." + }, + "freeOptionCards.nanogptTitle": { + "defaultMessage": "NanoGPT" + }, + "freeOptionCards.retry": { + "defaultMessage": "Yeniden dene" + }, + "freeOptionCards.tetrateDescription": { + "defaultMessage": "Otomatik kurulumla birden fazla yapay zeka modeline erişin. 10$ kredi almak için kaydolun." + }, + "freeOptionCards.tetrateTitle": { + "defaultMessage": "Tetrate'den Ajan Yönlendirici" + }, + "freeOptionCards.unexpectedError": { + "defaultMessage": "Kurulum sırasında beklenmeyen bir hata oluştu." + }, + "gatewaySettings.botFatherInstructions": { + "defaultMessage": "Telefonunuzda @BotFather'ı açın, /newbot gönderin ve botunuza isim vermek için talimatları izleyin. BotFather bir API jetonuyla yanıt verecektir; bunu aşağıya yapıştırın." + }, + "gatewaySettings.close": { + "defaultMessage": "Kapat" + }, + "gatewaySettings.expiresIn": { + "defaultMessage": "Süresi {time}'de doluyor" + }, + "gatewaySettings.failedToGeneratePairingCode": { + "defaultMessage": "Eşleştirme kodu oluşturulamadı" + }, + "gatewaySettings.failedToRemove": { + "defaultMessage": "Kaldırılamadı" + }, + "gatewaySettings.failedToStart": { + "defaultMessage": "Başlatılamadı" + }, + "gatewaySettings.failedToStop": { + "defaultMessage": "Durdurulamadı" + }, + "gatewaySettings.failedToUnpairUser": { + "defaultMessage": "Kullanıcının eşlemesi kaldırılamadı" + }, + "gatewaySettings.loading": { + "defaultMessage": "Yükleniyor..." + }, + "gatewaySettings.pairDevice": { + "defaultMessage": "Cihazı Eşleştir" + }, + "gatewaySettings.pairedUsers": { + "defaultMessage": "Eşlenen Kullanıcılar" + }, + "gatewaySettings.pairingCode": { + "defaultMessage": "Eşleştirme Kodu" + }, + "gatewaySettings.pasteBotToken": { + "defaultMessage": "Bot jetonunu buraya yapıştırın" + }, + "gatewaySettings.remove": { + "defaultMessage": "Kaldır" + }, + "gatewaySettings.running": { + "defaultMessage": "Koşu" + }, + "gatewaySettings.sendCodeToPair": { + "defaultMessage": "Eşleştirmek için bu kodu {gatewayType} botunuza gönderin." + }, + "gatewaySettings.start": { + "defaultMessage": "Başlat" + }, + "gatewaySettings.stop": { + "defaultMessage": "Durdur" + }, + "gatewaySettings.stopped": { + "defaultMessage": "Durduruldu" + }, + "gatewaySettings.telegram": { + "defaultMessage": "Telgraf" + }, + "goosehintsModal.close": { + "defaultMessage": "Kapat" + }, + "goosehintsModal.developer": { + "defaultMessage": "Geliştirici" + }, + "goosehintsModal.dialogDescription": { + "defaultMessage": "Goose ile iletişimi geliştirmek için projeniz hakkında ek bağlam sağlayın" + }, + "goosehintsModal.dialogTitle": { + "defaultMessage": "Proje İpuçlarını Yapılandırma (.goosehints)" + }, + "goosehintsModal.errorReading": { + "defaultMessage": ".goosehints dosyası okunurken hata oluştu: {error}" + }, + "goosehintsModal.failedToAccess": { + "defaultMessage": ".goosehints dosyasına erişilemedi" + }, + "goosehintsModal.failedToSave": { + "defaultMessage": ".goosehints dosyası kaydedilemedi" + }, + "goosehintsModal.fileCreating": { + "defaultMessage": "Şu konumda yeni.goosehints dosyası oluşturuluyor: {filePath}" + }, + "goosehintsModal.fileFound": { + "defaultMessage": ".goosehints dosyası şu konumda bulundu: {filePath}" + }, + "goosehintsModal.helpText1": { + "defaultMessage": ".goosehints, projeniz hakkında ek bağlam sağlamak ve Goose ile iletişimi geliştirmek için kullanılan bir metin dosyasıdır." + }, + "goosehintsModal.helpText2": { + "defaultMessage": "Lütfen uzantılar sayfasında {bold} uzantısının etkinleştirildiğinden emin olun. Bu uzantı.goosehints'i kullanmak için gereklidir..goosehints güncellemelerinin etkili olması için oturumunuzu yeniden başlatmanız gerekir." + }, + "goosehintsModal.helpText3": { + "defaultMessage": "Daha fazla bilgi için {link}'ye bakın." + }, + "goosehintsModal.helpTextLink": { + "defaultMessage": ".goosehints'i kullanma" + }, + "goosehintsModal.placeholder": { + "defaultMessage": "Proje ipuçlarını buraya girin..." + }, + "goosehintsModal.save": { + "defaultMessage": "Kaydet" + }, + "goosehintsModal.savedSuccessfully": { + "defaultMessage": "Başarıyla kaydedildi" + }, + "goosehintsModal.saving": { + "defaultMessage": "Kaydediliyor..." + }, + "goosehintsSection.configure": { + "defaultMessage": "Yapılandır" + }, + "goosehintsSection.description": { + "defaultMessage": "Goose'a ek bağlam sağlamak için projenizin.goosehints dosyasını yapılandırın" + }, + "goosehintsSection.title": { + "defaultMessage": "Proje İpuçları (.goosehints)" + }, + "greeting.readyToBuild": { + "defaultMessage": "Harika bir şey inşa etmeye hazır mısınız?" + }, + "greeting.readyToCreateGreat": { + "defaultMessage": "Harika bir şey yaratmaya hazır mısınız?" + }, + "greeting.readyToGetStarted": { + "defaultMessage": "Başlamaya hazır mısınız?" + }, + "greeting.whatCanBeAchieved": { + "defaultMessage": "Ne başarılabilir?" + }, + "greeting.whatCanBeBuilt": { + "defaultMessage": "Bugün ne inşa edilebilir?" + }, + "greeting.whatNeedsToBeDone": { + "defaultMessage": "Ne yapılması gerekiyor?" + }, + "greeting.whatProgress": { + "defaultMessage": "Hangi ilerleme kaydedilebilir?" + }, + "greeting.whatProjectNeedsAttention": { + "defaultMessage": "Hangi projenin ilgiye ihtiyacı var?" + }, + "greeting.whatProjectReadyToBegin": { + "defaultMessage": "Hangi proje başlamaya hazır?" + }, + "greeting.whatShallWeCreate": { + "defaultMessage": "Bugün ne yaratacağız?" + }, + "greeting.whatTaskAwaits": { + "defaultMessage": "Hangi görev bekliyor?" + }, + "greeting.whatToAccomplish": { + "defaultMessage": "Neyi başarmak istersiniz?" + }, + "greeting.whatToExplore": { + "defaultMessage": "Neyi keşfetmek istersiniz?" + }, + "greeting.whatToTackle": { + "defaultMessage": "Neyle uğraşmak istersin?" + }, + "greeting.whatToWorkOn": { + "defaultMessage": "Ne üzerinde çalışmak istersin?" + }, + "greeting.whatsNextChallenge": { + "defaultMessage": "Bir sonraki zorluk nedir?" + }, + "greeting.whatsOnYourMind": { + "defaultMessage": "Aklında ne var?" + }, + "greeting.whatsTheMission": { + "defaultMessage": "Bugünkü görev nedir?" + }, + "greeting.whatsThePlan": { + "defaultMessage": "Bugünkü planın ne?" + }, + "groupedExtensionLoadingToast.askGoose": { + "defaultMessage": "goose'a sorun" + }, + "groupedExtensionLoadingToast.collapseDetails": { + "defaultMessage": "Ayrıntıları daralt" + }, + "groupedExtensionLoadingToast.copied": { + "defaultMessage": "Kopyalandı!" + }, + "groupedExtensionLoadingToast.copyError": { + "defaultMessage": "Kopyalama hatası" + }, + "groupedExtensionLoadingToast.expandDetails": { + "defaultMessage": "Ayrıntıları genişlet" + }, + "groupedExtensionLoadingToast.failedToAddExtension": { + "defaultMessage": "Uzantı eklenemedi" + }, + "groupedExtensionLoadingToast.failedToLoad": { + "defaultMessage": "{count,plural,one{# uzantı yüklenemedi} other{# uzantı yüklenemedi}}" + }, + "groupedExtensionLoadingToast.loadingExtensions": { + "defaultMessage": "{count,plural,one{# uzantı yükleniyor...} other{# uzantı yükleniyor...}}" + }, + "groupedExtensionLoadingToast.partiallyLoaded": { + "defaultMessage": "{totalCount,plural,one{{successCount}/# uzantı yüklendi} other{{successCount}/# uzantı yüklendi}}" + }, + "groupedExtensionLoadingToast.showDetails": { + "defaultMessage": "Ayrıntıları göster" + }, + "groupedExtensionLoadingToast.showLess": { + "defaultMessage": "Daha az göster" + }, + "groupedExtensionLoadingToast.successfullyLoaded": { + "defaultMessage": "{count,plural,one{# uzantı başarıyla yüklendi} other{# uzantı başarıyla yüklendi}}" + }, + "headersSection.add": { + "defaultMessage": "Ekle" + }, + "headersSection.bothRequired": { + "defaultMessage": "Hem başlık adı hem de değer girilmelidir" + }, + "headersSection.duplicateHeader": { + "defaultMessage": "Bu ada sahip bir başlık zaten mevcut" + }, + "headersSection.headerName": { + "defaultMessage": "Başlık adı" + }, + "headersSection.headersDescription": { + "defaultMessage": "MCP sunucusuna yapılan isteklere dahil etmek için özel HTTP üstbilgileri ekleyin. Her iki alanı da doldurduktan sonra eklemek için \"+\" butonuna tıklayın." + }, + "headersSection.noSpaces": { + "defaultMessage": "Başlık adı boşluk içeremez" + }, + "headersSection.requestHeaders": { + "defaultMessage": "Başlıkları Talep Et" + }, + "headersSection.value": { + "defaultMessage": "Değer" + }, + "huggingFaceModelSearch.download": { + "defaultMessage": "İndir" + }, + "huggingFaceModelSearch.downloaded": { + "defaultMessage": "İndirildi" + }, + "huggingFaceModelSearch.downloading": { + "defaultMessage": "İndiriliyor…" + }, + "huggingFaceModelSearch.loadingVariants": { + "defaultMessage": "Çeşitler yükleniyor..." + }, + "huggingFaceModelSearch.noGgufModels": { + "defaultMessage": "Bu sorgu için GGUF modeli bulunamadı." + }, + "huggingFaceModelSearch.recommended": { + "defaultMessage": "Önerilen" + }, + "huggingFaceModelSearch.searchError": { + "defaultMessage": "Arama hatası: {details}" + }, + "huggingFaceModelSearch.searchFailed": { + "defaultMessage": "Arama başarısız oldu. Lütfen tekrar deneyin." + }, + "huggingFaceModelSearch.searchHuggingFace": { + "defaultMessage": "Arama HuggingFace" + }, + "huggingFaceModelSearch.searchNoData": { + "defaultMessage": "Arama hiçbir veri döndürmedi." + }, + "huggingFaceModelSearch.searchPlaceholder": { + "defaultMessage": "GGUF modellerini arayın..." + }, + "huggingFaceModelSearch.tooLarge": { + "defaultMessage": "Belleğe sığmayabilir ({size} modeli, {available} mevcuttur)" + }, + "imagePreview.altText": { + "defaultMessage": "goose resmi" + }, + "imagePreview.clickToCollapse": { + "defaultMessage": "Daraltmak için tıklayın" + }, + "imagePreview.clickToExpand": { + "defaultMessage": "Genişletmek için tıklayın" + }, + "imagePreview.unableToLoad": { + "defaultMessage": "Resim yüklenemiyor" + }, + "importRecipeForm.cancel": { + "defaultMessage": "İptal" + }, + "importRecipeForm.deeplinkHint": { + "defaultMessage": "\"goose://recipe?config=\" ile başlayan bir tarif derin bağlantısı yapıştırın" + }, + "importRecipeForm.deeplinkPlaceholder": { + "defaultMessage": "goose://recipe?config=... derin bağlantınızı buraya yapıştırın" + }, + "importRecipeForm.example": { + "defaultMessage": "örnek" + }, + "importRecipeForm.expectedRecipeStructure": { + "defaultMessage": "Beklenen Tarif Yapısı" + }, + "importRecipeForm.importRecipeButton": { + "defaultMessage": "Tarifi İçe Aktar" + }, + "importRecipeForm.importRecipeTitle": { + "defaultMessage": "Tarifi İçe Aktar" + }, + "importRecipeForm.importing": { + "defaultMessage": "İçe aktarılıyor..." + }, + "importRecipeForm.or": { + "defaultMessage": "VEYA" + }, + "importRecipeForm.recipeDeeplinkLabel": { + "defaultMessage": "Tarif Derin Bağlantısı" + }, + "importRecipeForm.recipeFileHint": { + "defaultMessage": "Tarif yapısını içeren bir YAML veya JSON dosyası yükleyin" + }, + "importRecipeForm.recipeFileLabel": { + "defaultMessage": "Tarif Dosyası" + }, + "importRecipeForm.reviewWarning": { + "defaultMessage": "Tarif dosyalarının içeriğini goose arayüzünüze eklemeden önce incelediğinizden emin olun." + }, + "importRecipeForm.schemaDescription": { + "defaultMessage": "YAML veya JSON dosyanız bu yapıyı takip etmelidir. Zorunlu alanlar şunlardır: başlık, açıklama ve talimatlar veya bilgi istemi." + }, + "inlineEditText.clickToEdit": { + "defaultMessage": "Düzenlemek için tıklayın" + }, + "inlineEditText.doubleClickToEdit": { + "defaultMessage": "Düzenlemek için çift tıklayın" + }, + "inlineEditText.enterText": { + "defaultMessage": "Metni girin" + }, + "inlineEditText.failedToSave": { + "defaultMessage": "Kaydedilemedi" + }, + "instructionsEditor.cancel": { + "defaultMessage": "İptal" + }, + "instructionsEditor.insertExample": { + "defaultMessage": "Örnek Ekle" + }, + "instructionsEditor.label": { + "defaultMessage": "Talimatlar" + }, + "instructionsEditor.placeholder": { + "defaultMessage": "Kullanıcıdan gizlenen yapay zeka için ayrıntılı talimatlar" + }, + "instructionsEditor.save": { + "defaultMessage": "Talimatları Kaydet" + }, + "instructionsEditor.syntaxHelp": { + "defaultMessage": "Kullanıcıların doldurabileceği parametreleri tanımlamak için {code} sözdizimini kullanın" + }, + "instructionsEditor.title": { + "defaultMessage": "Talimat Düzenleyicisi" + }, + "jsonSchemaEditor.cancel": { + "defaultMessage": "İptal" + }, + "jsonSchemaEditor.description": { + "defaultMessage": "JSON Şema formatını kullanarak yapay zekanın yanıtının beklenen yapısını tanımlayın" + }, + "jsonSchemaEditor.insertExample": { + "defaultMessage": "Örnek Ekle" + }, + "jsonSchemaEditor.invalidJson": { + "defaultMessage": "Geçersiz JSON biçimi" + }, + "jsonSchemaEditor.label": { + "defaultMessage": "Yanıt JSON Şeması" + }, + "jsonSchemaEditor.save": { + "defaultMessage": "Şemayı Kaydet" + }, + "jsonSchemaEditor.title": { + "defaultMessage": "JSON Şema Düzenleyici" + }, + "jsonSchemaForm.cancel": { + "defaultMessage": "İptal" + }, + "jsonSchemaForm.fieldRequired": { + "defaultMessage": "Bu alan zorunludur" + }, + "jsonSchemaForm.maxLength": { + "defaultMessage": "Maksimum uzunluk {maxLength}'dir" + }, + "jsonSchemaForm.maxValue": { + "defaultMessage": "Maksimum değer {maximum}'dir" + }, + "jsonSchemaForm.minLength": { + "defaultMessage": "Minimum uzunluk {minLength}'dir" + }, + "jsonSchemaForm.minValue": { + "defaultMessage": "Minimum değer {minimum}'dir" + }, + "jsonSchemaForm.noFields": { + "defaultMessage": "Görüntülenecek alan yok" + }, + "jsonSchemaForm.selectPlaceholder": { + "defaultMessage": "Seç..." + }, + "jsonSchemaForm.submit": { + "defaultMessage": "Gönder" + }, + "keyValueEditor.addValue": { + "defaultMessage": "Önceden yapılandırılmış değer ekleyin" + }, + "keyValueEditor.defaultKeyPlaceholder": { + "defaultMessage": "Parametre adı..." + }, + "keyValueEditor.defaultValuePlaceholder": { + "defaultMessage": "Parametre değeri..." + }, + "keyValueEditor.removeValue": { + "defaultMessage": "Önceden yapılandırılmış değeri kaldır {key}" + }, + "keyboardShortcuts.alwaysOnTopDescription": { + "defaultMessage": "Pencereyi her zaman üstte değiştir" + }, + "keyboardShortcuts.alwaysOnTopLabel": { + "defaultMessage": "Her Zaman Zirvede" + }, + "keyboardShortcuts.cancel": { + "defaultMessage": "İptal" + }, + "keyboardShortcuts.categoryApplication": { + "defaultMessage": "Uygulama Kısayolları" + }, + "keyboardShortcuts.categoryApplicationDescription": { + "defaultMessage": "Bu kısayollar Goose etkin uygulama olduğunda çalışır" + }, + "keyboardShortcuts.categoryGlobal": { + "defaultMessage": "Genel Kısayollar" + }, + "keyboardShortcuts.categoryGlobalDescription": { + "defaultMessage": "Bu kısayollar, Goose odaklanmadığında bile sistem genelinde çalışır" + }, + "keyboardShortcuts.categorySearch": { + "defaultMessage": "Arama Kısayolları" + }, + "keyboardShortcuts.categorySearchDescription": { + "defaultMessage": "Bu kısayollar bir görüşmede arama yaparken çalışır" + }, + "keyboardShortcuts.categoryWindow": { + "defaultMessage": "Pencere Kısayolları" + }, + "keyboardShortcuts.categoryWindowDescription": { + "defaultMessage": "Bu kısayollar pencere davranışını kontrol eder" + }, + "keyboardShortcuts.change": { + "defaultMessage": "Değiştir" + }, + "keyboardShortcuts.disabled": { + "defaultMessage": "Devre dışı" + }, + "keyboardShortcuts.dismiss": { + "defaultMessage": "Reddet" + }, + "keyboardShortcuts.findDescription": { + "defaultMessage": "Görüşmede aramayı aç" + }, + "keyboardShortcuts.findLabel": { + "defaultMessage": "Bul" + }, + "keyboardShortcuts.findNextDescription": { + "defaultMessage": "Sonraki arama sonucuna atla" + }, + "keyboardShortcuts.findNextLabel": { + "defaultMessage": "Sonrakini Bul" + }, + "keyboardShortcuts.findPreviousDescription": { + "defaultMessage": "Önceki arama sonucuna atla" + }, + "keyboardShortcuts.findPreviousLabel": { + "defaultMessage": "Öncekini Bul" + }, + "keyboardShortcuts.focusWindowDescription": { + "defaultMessage": "Goose penceresini istediğiniz yerden öne getirin" + }, + "keyboardShortcuts.focusWindowLabel": { + "defaultMessage": "Odak Goose Penceresi" + }, + "keyboardShortcuts.loading": { + "defaultMessage": "Yükleniyor..." + }, + "keyboardShortcuts.newChatDescription": { + "defaultMessage": "Geçerli pencerede yeni bir sohbet oluşturun" + }, + "keyboardShortcuts.newChatLabel": { + "defaultMessage": "Yeni Sohbet" + }, + "keyboardShortcuts.newChatWindowDescription": { + "defaultMessage": "Yeni bir Goose penceresi aç" + }, + "keyboardShortcuts.newChatWindowLabel": { + "defaultMessage": "Yeni Sohbet Penceresi" + }, + "keyboardShortcuts.openDirectoryDescription": { + "defaultMessage": "Dizin seçimi iletişim kutusunu aç" + }, + "keyboardShortcuts.openDirectoryLabel": { + "defaultMessage": "Dizini Aç" + }, + "keyboardShortcuts.quickLauncherDescription": { + "defaultMessage": "Hızlı başlatıcı katmanını aç" + }, + "keyboardShortcuts.quickLauncherLabel": { + "defaultMessage": "Hızlı Başlatıcı" + }, + "keyboardShortcuts.reassignShortcut": { + "defaultMessage": "Kısayolu Yeniden Ata" + }, + "keyboardShortcuts.resetAllShortcuts": { + "defaultMessage": "Tüm Kısayolları Sıfırla" + }, + "keyboardShortcuts.resetShortcutsDetail": { + "defaultMessage": "Bu, tüm kısayolları orijinal yapılandırmalarına geri yükleyecektir." + }, + "keyboardShortcuts.resetShortcutsMessage": { + "defaultMessage": "Tüm klavye kısayolları varsayılan değerlerine sıfırlansın mı?" + }, + "keyboardShortcuts.resetShortcutsTitle": { + "defaultMessage": "Klavye Kısayollarını Sıfırla" + }, + "keyboardShortcuts.resetToDefaultsDescription": { + "defaultMessage": "Tüm klavye kısayollarını orijinal yapılandırmalarına geri yükleyin" + }, + "keyboardShortcuts.resetToDefaultsHeading": { + "defaultMessage": "Varsayılanlara Sıfırla" + }, + "keyboardShortcuts.restartDescription": { + "defaultMessage": "Uygulama kısayollarında yapılan değişikliklerin (Yeni Sohbet, Ayarlar vb.) etkili olması için Goose'nin yeniden başlatılması gerekir. Genel kısayollar (Odak Penceresi, Hızlı Başlatıcı) hemen çalışır." + }, + "keyboardShortcuts.restartRequired": { + "defaultMessage": "Yeniden Başlatma Gerekli" + }, + "keyboardShortcuts.settingsDescription": { + "defaultMessage": "Ayarlar panelini aç" + }, + "keyboardShortcuts.settingsLabel": { + "defaultMessage": "Ayarlar" + }, + "keyboardShortcuts.shortcutConflictSaveDetail": { + "defaultMessage": "Bunu kaydetmek, kısayolu \"{conflictLabel}\"den kaldıracak ve \"{targetLabel}\"ye atayacaktır. Devam etmek istiyor musun?" + }, + "keyboardShortcuts.shortcutConflictTitle": { + "defaultMessage": "Kısayol Çakışması" + }, + "keyboardShortcuts.shortcutConflictToggleDetail": { + "defaultMessage": "Bunun etkinleştirilmesi \"{conflictLabel}\" kısayolunu kaldıracak ve \"{targetLabel}\" öğesine atayacaktır. Devam etmek istiyor musun?" + }, + "keyboardShortcuts.shortcutConflictToggleMessage": { + "defaultMessage": "{shortcut} kısayolu zaten \"{conflictLabel}\" öğesine atanmıştır." + }, + "keyboardShortcuts.toggleNavigationDescription": { + "defaultMessage": "Gezinme menüsünü göster veya gizle" + }, + "keyboardShortcuts.toggleNavigationLabel": { + "defaultMessage": "Gezinmeyi Değiştir" + }, + "launcher.placeholder": { + "defaultMessage": "goose'a herhangi bir şey sorun..." + }, + "loadingGoose.compacting": { + "defaultMessage": "goose konuşmayı sıkıştırıyor..." + }, + "loadingGoose.idle": { + "defaultMessage": "goose bunun üzerinde çalışıyor…" + }, + "loadingGoose.loadingConversation": { + "defaultMessage": "konuşma yükleniyor..." + }, + "loadingGoose.restartingAgent": { + "defaultMessage": "oturum yeniden başlatılıyor..." + }, + "loadingGoose.streaming": { + "defaultMessage": "goose bunun üzerinde çalışıyor…" + }, + "loadingGoose.thinking": { + "defaultMessage": "goose düşünüyor…" + }, + "loadingGoose.waiting": { + "defaultMessage": "goose bekliyor…" + }, + "localInferenceSettings.deleteConfirm": { + "defaultMessage": "Bu model silinsin mi? Daha sonra tekrar indirebilirsiniz." + }, + "localInferenceSettings.description": { + "defaultMessage": "API anahtarları olmadan çıkarım yapmak için yerel LLM modellerini indirin ve yönetin. Herhangi bir GGUF modeli için HuggingFace'yi arayın veya aşağıdaki öne çıkan seçimleri kullanın." + }, + "localInferenceSettings.download": { + "defaultMessage": "İndir" + }, + "localInferenceSettings.downloadFailed": { + "defaultMessage": "İndirme başarısız oldu" + }, + "localInferenceSettings.downloadProgress": { + "defaultMessage": "{downloaded} / {total} (%{percent})" + }, + "localInferenceSettings.downloadedModels": { + "defaultMessage": "İndirilen Modeller" + }, + "localInferenceSettings.downloading": { + "defaultMessage": "İndiriliyor" + }, + "localInferenceSettings.featuredModels": { + "defaultMessage": "Öne Çıkan Modeller" + }, + "localInferenceSettings.modelSettings": { + "defaultMessage": "Modeli Ayarları" + }, + "localInferenceSettings.modelSettingsTitle": { + "defaultMessage": "Modeli ayarları" + }, + "localInferenceSettings.noModels": { + "defaultMessage": "Mevcut model yok" + }, + "localInferenceSettings.recommended": { + "defaultMessage": "Önerilen" + }, + "localInferenceSettings.remaining": { + "defaultMessage": "{time} kaldı" + }, + "localInferenceSettings.showAllFeatured": { + "defaultMessage": "Tüm öne çıkanları göster ({count} daha fazlası)" + }, + "localInferenceSettings.showRecommendedOnly": { + "defaultMessage": "Yalnızca önerilenleri göster" + }, + "localInferenceSettings.title": { + "defaultMessage": "Yerel Çıkarım Modelleri" + }, + "localInferenceSettings.vision": { + "defaultMessage": "Vizyon" + }, + "localInferenceSettings.visionEncoderDownloading": { + "defaultMessage": "Vision enkoder indiriliyor…" + }, + "localInferenceSettings.visionEncoderNotDownloaded": { + "defaultMessage": "Görüntü kodlayıcı indirilmedi" + }, + "localModelManager.active": { + "defaultMessage": "Aktif" + }, + "localModelManager.deleteConfirm": { + "defaultMessage": "Bu model silinsin mi? Daha sonra tekrar indirebilirsiniz." + }, + "localModelManager.download": { + "defaultMessage": "İndir" + }, + "localModelManager.downloaded": { + "defaultMessage": "İndirildi" + }, + "localModelManager.gpuAcceleration": { + "defaultMessage": "GPU hızlandırmayı destekler (NVIDIA için CUDA, Apple Silicon için Metal). Donanım hızlandırma için derleme sırasında GPU özelliklerinin etkinleştirilmesi gerekir." + }, + "localModelManager.noModels": { + "defaultMessage": "Mevcut model yok" + }, + "localModelManager.recommended": { + "defaultMessage": "Önerilen" + }, + "localModelManager.recommendedForHardware": { + "defaultMessage": "Donanımınız için önerilir" + }, + "localModelManager.showAllModels": { + "defaultMessage": "Tüm modelleri göster ({count} daha fazlası)" + }, + "localModelManager.showRecommendedOnly": { + "defaultMessage": "Yalnızca önerilenleri göster" + }, + "localModelPicker.back": { + "defaultMessage": "Geri" + }, + "localModelPicker.bestForMachine": { + "defaultMessage": "Makineniz için en iyisi" + }, + "localModelPicker.cancelDownload": { + "defaultMessage": "İndirmeyi İptal Et" + }, + "localModelPicker.checkingModels": { + "defaultMessage": "Mevcut modeller kontrol ediliyor..." + }, + "localModelPicker.downloadModel": { + "defaultMessage": "{modelId}'yi ({size}) indirin" + }, + "localModelPicker.downloading": { + "defaultMessage": "{modelId} indiriliyor" + }, + "localModelPicker.failedToLoad": { + "defaultMessage": "Mevcut modeller yüklenemedi. Lütfen tekrar deneyin." + }, + "localModelPicker.failedToStartDownload": { + "defaultMessage": "İndirme işlemi başlatılamadı. Lütfen tekrar deneyin." + }, + "localModelPicker.hideOtherSizes": { + "defaultMessage": "Diğer boyutları gizle" + }, + "localModelPicker.localModelsNote": { + "defaultMessage": "Yerel modeller, tam gizlilik için her şeyi makinenizde tutar. Performans ve bağlam penceresi boyutu, donanım ve model boyutunuza bağlı olarak bulut sağlayıcılara göre farklılık gösterebilir." + }, + "localModelPicker.lostConnection": { + "defaultMessage": "İndirmek için bağlantı kesildi. Lütfen tekrar deneyin." + }, + "localModelPicker.modelNotFound": { + "defaultMessage": "Model bulunamadı" + }, + "localModelPicker.ready": { + "defaultMessage": "Hazır" + }, + "localModelPicker.selectModel": { + "defaultMessage": "Bir model seçin" + }, + "localModelPicker.showOtherSizes": { + "defaultMessage": "{count} diğer boyutları göster" + }, + "localModelPicker.startingDownload": { + "defaultMessage": "İndirme başlatılıyor..." + }, + "localModelPicker.tryAgain": { + "defaultMessage": "Tekrar Deneyin" + }, + "localModelPicker.useModel": { + "defaultMessage": "{modelId}'yi kullanın" + }, + "markdownContent.cancel": { + "defaultMessage": "İptal" + }, + "markdownContent.copyCode": { + "defaultMessage": "Kodu kopyala" + }, + "markdownContent.failedToOpenLink": { + "defaultMessage": "Bağlantı Açılamadı" + }, + "markdownContent.noApplicationFound": { + "defaultMessage": "Bu bağlantıyı açacak uygulama bulunamadı." + }, + "markdownContent.open": { + "defaultMessage": "Açık" + }, + "markdownContent.openExternalLink": { + "defaultMessage": "Dış Bağlantıyı Aç" + }, + "markdownContent.openProtocolLink": { + "defaultMessage": "{protocol} bağlantısı açılsın mı?" + }, + "markdownContent.thisWillOpen": { + "defaultMessage": "Bu açılacak: {href}" + }, + "mcpAppRenderer.appFallbackTitle": { + "defaultMessage": "Uygulama" + }, + "mcpAppRenderer.cancelButton": { + "defaultMessage": "İptal" + }, + "mcpAppRenderer.close": { + "defaultMessage": "Kapat" + }, + "mcpAppRenderer.exitFullscreen": { + "defaultMessage": "Tam ekrandan çık" + }, + "mcpAppRenderer.exitFullscreenTitle": { + "defaultMessage": "Tam ekrandan çık (Esc)" + }, + "mcpAppRenderer.failedToInitSandbox": { + "defaultMessage": "Korumalı alan proxy'si başlatılamadı" + }, + "mcpAppRenderer.failedToLoadResource": { + "defaultMessage": "Kaynak yüklenemedi" + }, + "mcpAppRenderer.fullscreen": { + "defaultMessage": "Tam ekran" + }, + "mcpAppRenderer.invalidUrl": { + "defaultMessage": "Geçersiz URL" + }, + "mcpAppRenderer.movePipWindow": { + "defaultMessage": "Resim İçinde Resim penceresini taşıyın (ok tuşlarını kullanın)" + }, + "mcpAppRenderer.openButton": { + "defaultMessage": "Açık" + }, + "mcpAppRenderer.openExternalLinkTitle": { + "defaultMessage": "Dış Bağlantıyı Aç" + }, + "mcpAppRenderer.openLinkDetail": { + "defaultMessage": "Bu açılacak: {url}" + }, + "mcpAppRenderer.openProtocolLink": { + "defaultMessage": "{protocol} bağlantısı açılsın mı?" + }, + "mcpAppRenderer.pictureInPicture": { + "defaultMessage": "Resim İçinde Resim" + }, + "mcpAppRenderer.playingInPip": { + "defaultMessage": "Resim İçinde Resim'de Oynatma" + }, + "mcpUIResourceRenderer.cancelButton": { + "defaultMessage": "İptal" + }, + "mcpUIResourceRenderer.openButton": { + "defaultMessage": "Açık" + }, + "mcpUIResourceRenderer.openExternalLinkTitle": { + "defaultMessage": "Dış Bağlantıyı Aç" + }, + "mcpUIResourceRenderer.openLinkDetail": { + "defaultMessage": "Bu açılacak: {url}" + }, + "mcpUIResourceRenderer.openProtocolLink": { + "defaultMessage": "{protocol} bağlantısı açılsın mı?" + }, + "mcpUIResourceRenderer.toastMessageReceived": { + "defaultMessage": "{message} için mesaj alındı." + }, + "mcpUIResourceRenderer.toastTitle": { + "defaultMessage": "MCP-UI {messageType} mesajı" + }, + "mcpUIResourceRenderer.toastUnsupported": { + "defaultMessage": "{message} için mesaj alındı. {messageType} mesajları henüz desteklenmiyor; daha fazla ayrıntı için konsola bakın." + }, + "mentionPopover.itemsFound": { + "defaultMessage": "{count,plural,one{# öğe bulundu} other{# öğe bulundu}}" + }, + "mentionPopover.loadingCommands": { + "defaultMessage": "Komutlar yükleniyor..." + }, + "mentionPopover.noCommandsFound": { + "defaultMessage": "\"{query}\" ile eşleşen komut bulunamadı" + }, + "mentionPopover.noItemsFound": { + "defaultMessage": "\"{query}\" ile eşleşen öğe bulunamadı" + }, + "mentionPopover.scanningFiles": { + "defaultMessage": "Dosyalar taranıyor..." + }, + "messageCopyLink.copied": { + "defaultMessage": "Kopyalandı!" + }, + "messageCopyLink.copy": { + "defaultMessage": "Kopyala" + }, + "messageQueue.cancel": { + "defaultMessage": "İptal" + }, + "messageQueue.cannotSendWhileEditing": { + "defaultMessage": "Düzenleme sırasında gönderilemiyor" + }, + "messageQueue.clearAll": { + "defaultMessage": "Tümünü Temizle" + }, + "messageQueue.clickToEdit": { + "defaultMessage": "{content} (Düzenlemek için tıklayın)" + }, + "messageQueue.collapseQueue": { + "defaultMessage": "Sırayı daralt" + }, + "messageQueue.dragToReorder": { + "defaultMessage": "Önceliği yeniden sıralamak için mesajları sürükleyin" + }, + "messageQueue.expandQueue": { + "defaultMessage": "Sırayı genişlet" + }, + "messageQueue.messageCount": { + "defaultMessage": "{count,plural,one{# mesaj {status}} other{# mesaj {status}}}" + }, + "messageQueue.messageQueue": { + "defaultMessage": "Mesaj Sırası" + }, + "messageQueue.next": { + "defaultMessage": "Sonraki" + }, + "messageQueue.paused": { + "defaultMessage": "Duraklatıldı" + }, + "messageQueue.queuePaused": { + "defaultMessage": "Sıra Duraklatıldı" + }, + "messageQueue.queuePausedCompact": { + "defaultMessage": "Sıra duraklatıldı - devam etmek için \"Gönder\"i tıklayın veya yeni mesaj ekleyin" + }, + "messageQueue.queuePausedExpanded": { + "defaultMessage": "Kuyruk kesinti nedeniyle duraklatıldı. Devam etmek için \"Şimdi Gönder\" seçeneğini kullanın veya yeni bir mesaj ekleyin." + }, + "messageQueue.queued": { + "defaultMessage": "sıraya alınmış" + }, + "messageQueue.removeFromQueue": { + "defaultMessage": "Bu mesajı sıradan kaldır" + }, + "messageQueue.save": { + "defaultMessage": "Kaydet" + }, + "messageQueue.sendNow": { + "defaultMessage": "Bu mesajı şimdi gönder" + }, + "messageQueue.stopAndSend": { + "defaultMessage": "Mevcut işlemeyi durdurun ve bu mesajı hemen gönderin" + }, + "messageQueue.waiting": { + "defaultMessage": "beklemek" + }, + "microphoneSelector.chooseDescription": { + "defaultMessage": "Dikte için hangi mikrofonun kullanılacağını seçin" + }, + "microphoneSelector.grantAccess": { + "defaultMessage": "Erişim Ver" + }, + "microphoneSelector.grantAccessDescription": { + "defaultMessage": "Kullanılabilir mikrofonları görmek için erişim izni verin" + }, + "microphoneSelector.microphone": { + "defaultMessage": "Mikrofon" + }, + "microphoneSelector.microphoneLabel": { + "defaultMessage": "Mikrofon {index}" + }, + "microphoneSelector.selectedMicrophone": { + "defaultMessage": "Seçilen Mikrofon" + }, + "microphoneSelector.speakToTest": { + "defaultMessage": "Mikrofonunuzu test etmek için konuşun ({seconds}'ler)" + }, + "microphoneSelector.stop": { + "defaultMessage": "Durdur" + }, + "microphoneSelector.systemDefault": { + "defaultMessage": "Sistem Varsayılanı" + }, + "microphoneSelector.test": { + "defaultMessage": "testi" + }, + "modeSelectionItem.autonomousDescription": { + "defaultMessage": "Tam dosya değiştirme yetenekleri, dosyaları özgürce düzenleyin, oluşturun ve silin." + }, + "modeSelectionItem.autonomousLabel": { + "defaultMessage": "Özerk" + }, + "modeSelectionItem.chatOnlyDescription": { + "defaultMessage": "Araç veya uzantı kullanmadan seçilen sağlayıcıyla etkileşime geçin." + }, + "modeSelectionItem.chatOnlyLabel": { + "defaultMessage": "Yalnızca sohbet" + }, + "modeSelectionItem.manualDescription": { + "defaultMessage": "Tüm araçlar, uzantılar ve dosya değişiklikleri insan onayı gerektirecektir" + }, + "modeSelectionItem.manualLabel": { + "defaultMessage": "Manuel" + }, + "modeSelectionItem.smartDescription": { + "defaultMessage": "Risk düzeyine göre hangi eylemlerin onaylanması gerektiğini akıllıca belirleyin" + }, + "modeSelectionItem.smartLabel": { + "defaultMessage": "Akıllı" + }, + "modelAndProviderContext.modelChangeFailed": { + "defaultMessage": "{provider}/{model} başarısız oldu" + }, + "modelAndProviderContext.modelChangedTitle": { + "defaultMessage": "Model değiştirildi" + }, + "modelAndProviderContext.selectModel": { + "defaultMessage": "Modeli Seçin" + }, + "modelAndProviderContext.switchModelSuccess": { + "defaultMessage": "{provider}'den {model} kullanılarak modeller başarıyla değiştirildi" + }, + "modelAndProviderContext.unknownProviderMsg": { + "defaultMessage": "Config'de bilinmeyen sağlayıcı -- lütfen config.yaml dosyanızı inceleyin" + }, + "modelAndProviderContext.unknownProviderTitle": { + "defaultMessage": "Sağlayıcı adı araması" + }, + "modelSettingsButtons.configureProviders": { + "defaultMessage": "Sağlayıcıları yapılandırma" + }, + "modelSettingsButtons.switchModels": { + "defaultMessage": "Modelleri değiştir" + }, + "modelSettingsPanel.batchSize": { + "defaultMessage": "Parti boyutu" + }, + "modelSettingsPanel.batchSizeDescription": { + "defaultMessage": "Hızlı işlem toplu" + }, + "modelSettingsPanel.contextAndGeneration": { + "defaultMessage": "Bağlam ve Üretim" + }, + "modelSettingsPanel.contextSize": { + "defaultMessage": "Bağlam boyutu" + }, + "modelSettingsPanel.contextSizeDescription": { + "defaultMessage": "Maksimum bağlam penceresi (0 = model varsayılanı)" + }, + "modelSettingsPanel.etaLearningRate": { + "defaultMessage": "Eta (öğrenme oranı)" + }, + "modelSettingsPanel.flashAttention": { + "defaultMessage": "Flaş dikkat" + }, + "modelSettingsPanel.flashAttentionDescription": { + "defaultMessage": "Flaş dikkati optimizasyonunu etkinleştir" + }, + "modelSettingsPanel.frequencyPenalty": { + "defaultMessage": "Frekans cezası" + }, + "modelSettingsPanel.frequencyPenaltyDescription": { + "defaultMessage": "0,0 = kapalı" + }, + "modelSettingsPanel.gpuLayers": { + "defaultMessage": "GPU katmanları" + }, + "modelSettingsPanel.gpuLayersDescription": { + "defaultMessage": "GPU'ya aktarılacak katmanlar" + }, + "modelSettingsPanel.loadingSettings": { + "defaultMessage": "Ayarlar yükleniyor..." + }, + "modelSettingsPanel.lockModelInRam": { + "defaultMessage": "Modeli RAM'e kilitle (mlock)" + }, + "modelSettingsPanel.lockModelInRamDescription": { + "defaultMessage": "Modelin diske değiştirilmesini önleyin" + }, + "modelSettingsPanel.maxOutputTokens": { + "defaultMessage": "Maksimum çıkış jetonları" + }, + "modelSettingsPanel.maxOutputTokensDescription": { + "defaultMessage": "Oluşturulan jetonların üst sınırı" + }, + "modelSettingsPanel.minP": { + "defaultMessage": "Min P" + }, + "modelSettingsPanel.nativeToolCalling": { + "defaultMessage": "Yerel araç çağrısı" + }, + "modelSettingsPanel.nativeToolCallingDescription": { + "defaultMessage": "Kabuk komutu emülatörü yerine modelin yerleşik araç çağrısı formatını kullanın. Araç çağırmayı güvenilir şekilde destekleyen büyük modeller için etkinleştirin." + }, + "modelSettingsPanel.performance": { + "defaultMessage": "Performans" + }, + "modelSettingsPanel.presencePenalty": { + "defaultMessage": "Varlık cezası" + }, + "modelSettingsPanel.presencePenaltyDescription": { + "defaultMessage": "0,0 = kapalı" + }, + "modelSettingsPanel.repeatPenalty": { + "defaultMessage": "Cezayı tekrarla" + }, + "modelSettingsPanel.repeatPenaltyDescription": { + "defaultMessage": "1,0 = kapalı" + }, + "modelSettingsPanel.repeatWindow": { + "defaultMessage": "Pencereyi tekrarla" + }, + "modelSettingsPanel.repeatWindowDescription": { + "defaultMessage": "Geriye bakılacak jetonlar" + }, + "modelSettingsPanel.repetitionPenalty": { + "defaultMessage": "Tekrarlama Cezası" + }, + "modelSettingsPanel.reset": { + "defaultMessage": "Sıfırla" + }, + "modelSettingsPanel.resetToDefaults": { + "defaultMessage": "Varsayılanlara sıfırla" + }, + "modelSettingsPanel.samplingStrategy": { + "defaultMessage": "Örnekleme Stratejisi" + }, + "modelSettingsPanel.saving": { + "defaultMessage": "Kaydediliyor..." + }, + "modelSettingsPanel.seed": { + "defaultMessage": "tohum" + }, + "modelSettingsPanel.tauTargetEntropy": { + "defaultMessage": "Tau (hedef entropisi)" + }, + "modelSettingsPanel.temperature": { + "defaultMessage": "Sıcaklık" + }, + "modelSettingsPanel.threads": { + "defaultMessage": "Konular" + }, + "modelSettingsPanel.threadsDescription": { + "defaultMessage": "Nesil için CPU iş parçacıkları" + }, + "modelSettingsPanel.toolCalling": { + "defaultMessage": "Takım Çağırma" + }, + "modelSettingsPanel.topK": { + "defaultMessage": "En İyi K" + }, + "modelSettingsPanel.topP": { + "defaultMessage": "Üst P" + }, + "modelsBottomBar.changeModel": { + "defaultMessage": "Modeli Değiştir" + }, + "modelsBottomBar.currentModel": { + "defaultMessage": "Mevcut model" + }, + "modelsBottomBar.loadingModel": { + "defaultMessage": "Model yükleniyor..." + }, + "modelsBottomBar.localModelSettings": { + "defaultMessage": "Yerel Model Ayarları" + }, + "modelsBottomBar.localModelSettingsTitle": { + "defaultMessage": "Yerel Model Ayarları — {modelName}" + }, + "modelsBottomBar.resolvedModel": { + "defaultMessage": "Çözümlenen model" + }, + "modelsBottomBar.selectModel": { + "defaultMessage": "Modeli Seçin" + }, + "modelsSection.resetDescription": { + "defaultMessage": "Yeni bir başlangıç yapmak için seçtiğiniz model ve sağlayıcı ayarlarını temizleyin" + }, + "modelsSection.resetTitle": { + "defaultMessage": "Sağlayıcıyı ve Modeli Sıfırla" + }, + "navigationCustomization.dragInstructions": { + "defaultMessage": "Yeniden sıralamak için sürükleyin, öğeleri göstermek/gizlemek için göz simgesini tıklayın" + }, + "navigationCustomization.hideItem": { + "defaultMessage": "Öğeyi gizle" + }, + "navigationCustomization.itemApps": { + "defaultMessage": "Uygulamalar" + }, + "navigationCustomization.itemChat": { + "defaultMessage": "Sohbet" + }, + "navigationCustomization.itemExtensions": { + "defaultMessage": "Uzantılar" + }, + "navigationCustomization.itemHome": { + "defaultMessage": "Ana sayfa" + }, + "navigationCustomization.itemRecipes": { + "defaultMessage": "Tarifler" + }, + "navigationCustomization.itemScheduler": { + "defaultMessage": "Zamanlayıcı" + }, + "navigationCustomization.itemSettings": { + "defaultMessage": "Ayarlar" + }, + "navigationCustomization.itemSkills": { + "defaultMessage": "Beceriler" + }, + "navigationCustomization.resetToDefaults": { + "defaultMessage": "Varsayılanlara sıfırla" + }, + "navigationCustomization.showItem": { + "defaultMessage": "Öğeyi göster" + }, + "navigationModeSelector.overlayDescription": { + "defaultMessage": "Tam ekran yer paylaşımı" + }, + "navigationModeSelector.overlayLabel": { + "defaultMessage": "Kaplama" + }, + "navigationModeSelector.pushDescription": { + "defaultMessage": "Gezinme içeriği zorlar" + }, + "navigationModeSelector.pushLabel": { + "defaultMessage": "İt" + }, + "navigationPositionSelector.bottomLabel": { + "defaultMessage": "Alt" + }, + "navigationPositionSelector.leftLabel": { + "defaultMessage": "Sol" + }, + "navigationPositionSelector.rightLabel": { + "defaultMessage": "Sağ" + }, + "navigationPositionSelector.topLabel": { + "defaultMessage": "Üst" + }, + "navigationStyleSelector.listDescription": { + "defaultMessage": "Klasik yoğunlaştırılmış görünüm" + }, + "navigationStyleSelector.listLabel": { + "defaultMessage": "Liste" + }, + "navigationStyleSelector.tileDescription": { + "defaultMessage": "Büyütülmüş döşeme görünümü" + }, + "navigationStyleSelector.tileLabel": { + "defaultMessage": "Fayans" + }, + "onboardingGuard.checkProviderErrorDescription": { + "defaultMessage": "Sunucu başlatılıyor veya geçici olarak kullanılamıyor olabilir." + }, + "onboardingGuard.checkProviderErrorTitle": { + "defaultMessage": "Goose sunucusuna bağlanılamıyor" + }, + "onboardingGuard.retry": { + "defaultMessage": "Yeniden dene" + }, + "onboardingGuard.welcomeDescription": { + "defaultMessage": "Yerel AI temsilciniz. Başlamak için bir AI model sağlayıcısına bağlanın." + }, + "onboardingGuard.welcomeTitle": { + "defaultMessage": "goose'a hoş geldiniz" + }, + "onboardingSuccess.allSet": { + "defaultMessage": "goose'yi kullanmaya başlamaya hazırsınız." + }, + "onboardingSuccess.connectedTo": { + "defaultMessage": "{providerName}'ye bağlanıldı" + }, + "onboardingSuccess.getStarted": { + "defaultMessage": "Başlayın" + }, + "onboardingSuccess.learnMore": { + "defaultMessage": "Daha fazla bilgi edinin" + }, + "onboardingSuccess.localModelReady": { + "defaultMessage": "Yerli model hazır" + }, + "onboardingSuccess.privacyDescription": { + "defaultMessage": "Anonim kullanım verileri goose'nin iyileştirilmesine yardımcı olur. Konuşmalarınızı, kodlarınızı veya kişisel verilerinizi asla toplamıyoruz." + }, + "onboardingSuccess.privacyTitle": { + "defaultMessage": "Gizlilik" + }, + "onboardingSuccess.shareUsageData": { + "defaultMessage": "Anonim kullanım verilerini paylaşın" + }, + "parameterInput.defaultValue": { + "defaultMessage": "Varsayılan Değer" + }, + "parameterInput.defaultValuePlaceholder": { + "defaultMessage": "Varsayılan değeri girin" + }, + "parameterInput.deleteParameter": { + "defaultMessage": "Parametreyi sil: {key}" + }, + "parameterInput.description": { + "defaultMessage": "açıklama" + }, + "parameterInput.descriptionHelp": { + "defaultMessage": "Bu, son kullanıcının göreceği mesajdır." + }, + "parameterInput.descriptionPlaceholder": { + "defaultMessage": "Örneğin, \"Yeni bileşenin adını girin\"" + }, + "parameterInput.inputType": { + "defaultMessage": "Giriş Türü" + }, + "parameterInput.optional": { + "defaultMessage": "İsteğe bağlı" + }, + "parameterInput.optionsHelp": { + "defaultMessage": "Her seçeneği yeni bir satıra girin. Bunlar açılır seçenekler olarak gösterilecektir." + }, + "parameterInput.optionsLabel": { + "defaultMessage": "Seçenekler (her satıra bir tane)" + }, + "parameterInput.optionsPlaceholder": { + "defaultMessage": "Seçenek 1 Seçenek 2 Seçenek 3" + }, + "parameterInput.required": { + "defaultMessage": "Gerekli" + }, + "parameterInput.requirement": { + "defaultMessage": "Gereksinim" + }, + "parameterInput.typeBoolean": { + "defaultMessage": "Boolean" + }, + "parameterInput.typeNumber": { + "defaultMessage": "Sayı" + }, + "parameterInput.typeSelect": { + "defaultMessage": "Seç" + }, + "parameterInput.typeString": { + "defaultMessage": "Dize" + }, + "parameterInput.unused": { + "defaultMessage": "Kullanılmayan" + }, + "parameterInput.unusedWarningTitle": { + "defaultMessage": "Bu parametre talimatlarda veya istemde kullanılmaz. Manuel giriş için mevcut olacaktır ancak gerekli olmayabilir." + }, + "parameterInputModal.backToForm": { + "defaultMessage": "Parametre Formuna Geri Dön" + }, + "parameterInputModal.cancel": { + "defaultMessage": "İptal" + }, + "parameterInputModal.cancelRecipeSetup": { + "defaultMessage": "Tarif Kurulumunu İptal Et" + }, + "parameterInputModal.enterValue": { + "defaultMessage": "{key} için değer girin..." + }, + "parameterInputModal.false": { + "defaultMessage": "Yanlış" + }, + "parameterInputModal.recipeParameters": { + "defaultMessage": "Tarif Parametreleri" + }, + "parameterInputModal.select": { + "defaultMessage": "Seç..." + }, + "parameterInputModal.selectOption": { + "defaultMessage": "Bir seçenek seçin..." + }, + "parameterInputModal.startNewChat": { + "defaultMessage": "Yeni Sohbet Başlat (Tarif Yok)" + }, + "parameterInputModal.startRecipe": { + "defaultMessage": "Tarifi Başlat" + }, + "parameterInputModal.true": { + "defaultMessage": "Doğru" + }, + "parameterInputModal.whatToDo": { + "defaultMessage": "Ne yapmak istersin?" + }, + "permissionModal.alwaysAllow": { + "defaultMessage": "Her zaman izin ver" + }, + "permissionModal.askBefore": { + "defaultMessage": "Daha önce sor" + }, + "permissionModal.cancel": { + "defaultMessage": "İptal" + }, + "permissionModal.close": { + "defaultMessage": "Kapat" + }, + "permissionModal.failedToLoadTools": { + "defaultMessage": "Araçlar yüklenemedi" + }, + "permissionModal.failedToLoadToolsDescription": { + "defaultMessage": "Bu uzantı için araçlar yüklenemedi. Uzantı geçerli oturumda yüklenmemiş olabilir." + }, + "permissionModal.neverAllow": { + "defaultMessage": "Asla izin verme" + }, + "permissionModal.noActiveSession": { + "defaultMessage": "Aktif oturum yok" + }, + "permissionModal.noActiveSessionDescription": { + "defaultMessage": "Bu uzantının araç izinlerini yapılandırmak için önce bir sohbet oturumu başlatın. Araç izinleri etkin oturumun uzantılarından yüklenir." + }, + "permissionModal.noToolsAvailable": { + "defaultMessage": "Bu uzantı için kullanılabilir araç yok." + }, + "permissionModal.saveChanges": { + "defaultMessage": "Değişiklikleri Kaydet" + }, + "permissionRulesModal.description": { + "defaultMessage": "Uzantıların sisteminizle nasıl etkileşime gireceğini kontrol etmek için araç izinlerini yapılandırın." + }, + "permissionRulesModal.extensionRules": { + "defaultMessage": "Uzantı kuralları" + }, + "permissionRulesModal.title": { + "defaultMessage": "İzin Kuralları" + }, + "permissionSetting.extensionRules": { + "defaultMessage": "Uzantı kuralları" + }, + "permissionSetting.permissionRules": { + "defaultMessage": "İzin Kuralları" + }, + "permissionSetting.permissionRulesDescription": { + "defaultMessage": "Yanıtlarınızı yönlendirmeye ve bağlam eklemeye yardımcı olmak için sağlayıcıya iletilecek gizli talimatlar." + }, + "privacyInfoModal.collectErrors": { + "defaultMessage": "Hata türleri (ör. \"rate_limit\", \"auth\" - ayrıntı yok)" + }, + "privacyInfoModal.collectExtensions": { + "defaultMessage": "Uzantılar ve araç kullanım sayıları (yalnızca adlar)" + }, + "privacyInfoModal.collectOs": { + "defaultMessage": "İşletim sistemi, sürümü ve mimarisi" + }, + "privacyInfoModal.collectProvider": { + "defaultMessage": "Sağlayıcı ve kullanılan model" + }, + "privacyInfoModal.collectSession": { + "defaultMessage": "Oturum metrikleri (süre, etkileşim sayısı, belirteç kullanımı)" + }, + "privacyInfoModal.collectVersion": { + "defaultMessage": "goose sürümü ve yükleme yöntemi" + }, + "privacyInfoModal.description": { + "defaultMessage": "Anonim kullanım verileri, goose'nin nasıl kullanıldığını anlamamıza ve iyileştirilecek alanları belirlememize yardımcı olur." + }, + "privacyInfoModal.neverCollect": { + "defaultMessage": "Konuşmalarınızı, kodunuzu, araç argümanlarınızı, hata mesajlarınızı veya herhangi bir kişisel verinizi asla toplamıyoruz. Bu ayarı istediğiniz zaman Ayarlar'dan değiştirebilirsiniz." + }, + "privacyInfoModal.title": { + "defaultMessage": "Gizlilik ayrıntıları" + }, + "privacyInfoModal.whatWeCollect": { + "defaultMessage": "Ne topluyoruz:" + }, + "progressiveMessageList.loadingMessages": { + "defaultMessage": "Mesajlar yükleniyor... ({renderedCount}/{totalCount})" + }, + "progressiveMessageList.modelChanged": { + "defaultMessage": "Model değişti: {previousModel} → {currentModel}" + }, + "progressiveMessageList.searchHint": { + "defaultMessage": "Arama amacıyla tüm mesajları anında yüklemek için Cmd/Ctrl+F tuşlarına basın" + }, + "promptsSettings.allPromptsReset": { + "defaultMessage": "Tüm istemler varsayılanlara sıfırlandı" + }, + "promptsSettings.backToList": { + "defaultMessage": "Listeye Geri Dön" + }, + "promptsSettings.confirmReplaceWithDefault": { + "defaultMessage": "Mevcut içerik varsayılanla değiştirilsin mi? Değişiklikleriniz kaybolacak." + }, + "promptsSettings.confirmResetAll": { + "defaultMessage": "Tüm istemleri varsayılan değerlerine sıfırlamak istediğinizden emin misiniz? Bu geri alınamaz." + }, + "promptsSettings.confirmResetOne": { + "defaultMessage": "Bu istemi varsayılan değerine sıfırlamak istediğinizden emin misiniz? Bu geri alınamaz." + }, + "promptsSettings.confirmUnsavedBack": { + "defaultMessage": "Kaydedilmemiş değişiklikleriniz var. Geri dönmek istediğinden emin misin?" + }, + "promptsSettings.customized": { + "defaultMessage": "Özelleştirilmiş" + }, + "promptsSettings.edit": { + "defaultMessage": "Düzenle" + }, + "promptsSettings.editPromptTitle": { + "defaultMessage": "Düzenleme: {name}" + }, + "promptsSettings.editingLabel": { + "defaultMessage": "Düzenleme: {name}" + }, + "promptsSettings.enterPromptContent": { + "defaultMessage": "Bilgi istemi içeriğini girin..." + }, + "promptsSettings.failedToLoadPrompt": { + "defaultMessage": "İstem yüklenemedi" + }, + "promptsSettings.failedToLoadPrompts": { + "defaultMessage": "İstemler yüklenemedi" + }, + "promptsSettings.failedToResetPrompt": { + "defaultMessage": "İstem sıfırlanamadı" + }, + "promptsSettings.failedToResetPrompts": { + "defaultMessage": "İstemler sıfırlanamadı" + }, + "promptsSettings.failedToSavePrompt": { + "defaultMessage": "İstem kaydedilemedi" + }, + "promptsSettings.promptEditingDescription": { + "defaultMessage": "goose'nin farklı bağlamlardaki davranışını tanımlayan istemleri özelleştirin. Bu istemler Jinja2 şablon oluşturma sözdizimini kullanır. Yanlış değişiklikler işlevselliği bozabileceğinden şablon değişkenlerini değiştirirken dikkatli olun. Lütfen iyileştirmeleri toplulukla paylaşın." + }, + "promptsSettings.promptEditingTitle": { + "defaultMessage": "İstemi Düzenleme" + }, + "promptsSettings.promptResetToDefault": { + "defaultMessage": "İstemi varsayılana sıfırlama" + }, + "promptsSettings.promptSaved": { + "defaultMessage": "İstem kaydedildi" + }, + "promptsSettings.resetAll": { + "defaultMessage": "Tümünü Sıfırla" + }, + "promptsSettings.resetToDefault": { + "defaultMessage": "Varsayılana Sıfırla" + }, + "promptsSettings.restoreDefault": { + "defaultMessage": "Varsayılanı Geri Yükle" + }, + "promptsSettings.save": { + "defaultMessage": "Kaydet" + }, + "promptsSettings.templateTip": { + "defaultMessage": "{extensionsExample} veya {forExample} gibi şablon değişkenleri çalışma zamanında gerçek değerlerle değiştirilir. Gerekli değişkenleri kaldırmamaya dikkat edin." + }, + "promptsSettings.unsavedChanges": { + "defaultMessage": "Kaydedilmemiş değişiklikleriniz var" + }, + "providerCard.noMetadata": { + "defaultMessage": "ProviderCard hatası: Meta veri sağlanmadı" + }, + "providerCard.unknownProvider": { + "defaultMessage": "Bilinmeyen Sağlayıcı" + }, + "providerCatalogPicker.anthropicCompatible": { + "defaultMessage": "Anthropic Uyumlu" + }, + "providerCatalogPicker.apiFormat": { + "defaultMessage": "API Biçimi" + }, + "providerCatalogPicker.cancel": { + "defaultMessage": "İptal" + }, + "providerCatalogPicker.chooseProvider": { + "defaultMessage": "Sağlayıcıyı Seçin" + }, + "providerCatalogPicker.errorPrefix": { + "defaultMessage": "Hata: {error}" + }, + "providerCatalogPicker.loadingProviders": { + "defaultMessage": "Sağlayıcılar yükleniyor..." + }, + "providerCatalogPicker.modelsAvailable": { + "defaultMessage": "{count} modelleri mevcut" + }, + "providerCatalogPicker.noProvidersAvailable": { + "defaultMessage": "Sağlayıcı yok" + }, + "providerCatalogPicker.noProvidersFound": { + "defaultMessage": "\"{query}\" için sağlayıcı bulunamadı" + }, + "providerCatalogPicker.openaiCompatible": { + "defaultMessage": "OpenAI Uyumlu" + }, + "providerCatalogPicker.requiresEnvVar": { + "defaultMessage": "• {envVar} gerektirir" + }, + "providerCatalogPicker.searchProviders": { + "defaultMessage": "Sağlayıcıları arayın..." + }, + "providerCatalogPicker.selectFormatDescription": { + "defaultMessage": "Bir API formatı ve sağlayıcı seçin. Yapılandırmayı sizin için otomatik olarak dolduracağız." + }, + "providerConfigForm.browserWindowOpen": { + "defaultMessage": "Oturum açma işlemini tamamlamanız için bir tarayıcı penceresi açılacaktır." + }, + "providerConfigForm.configuring": { + "defaultMessage": "Yapılandırılıyor..." + }, + "providerConfigForm.continue": { + "defaultMessage": "Devam et" + }, + "providerConfigForm.deviceCodeFlowHint": { + "defaultMessage": "Bir tarayıcı penceresi açılacak ve doğrulama kodu panonuza kopyalanacaktır. Oturum açmayı tamamlamak için tarayıcıya yapıştırın." + }, + "providerConfigForm.noApiKey": { + "defaultMessage": "API anahtarınız yok mu?" + }, + "providerConfigForm.signInWith": { + "defaultMessage": "{providerName} ile oturum açın" + }, + "providerConfigForm.signingIn": { + "defaultMessage": "Oturum açılıyor..." + }, + "providerConfigurationModal.addApiKeyDescription": { + "defaultMessage": "Bu sağlayıcının goose'a entegre olması için API anahtarlarınızı ekleyin" + }, + "providerConfigurationModal.browserWindowHint": { + "defaultMessage": "Oturum açma işlemini tamamlamanız için bir tarayıcı penceresi açılacaktır." + }, + "providerConfigurationModal.cancel": { + "defaultMessage": "İptal" + }, + "providerConfigurationModal.cannotDeleteActive": { + "defaultMessage": "Bu sağlayıcıyı şu anda kullanımdayken silemezsiniz. Lütfen önce farklı bir modele geçin." + }, + "providerConfigurationModal.checkConfigAgain": { + "defaultMessage": "Bu sağlayıcıyı kullanmak için yapılandırmanızı tekrar kontrol edin." + }, + "providerConfigurationModal.close": { + "defaultMessage": "Kapat" + }, + "providerConfigurationModal.configureHeader": { + "defaultMessage": "{providerName}'yi yapılandırın" + }, + "providerConfigurationModal.deleteConfigHeader": { + "defaultMessage": "{providerName} için yapılandırmayı sil" + }, + "providerConfigurationModal.deleteConfirmation": { + "defaultMessage": "Bu, mevcut sağlayıcı yapılandırmasını kalıcı olarak silecektir." + }, + "providerConfigurationModal.deviceCodeFlowHint": { + "defaultMessage": "Bir tarayıcı penceresi açılacak ve doğrulama kodu panonuza kopyalanacaktır. Oturum açmayı tamamlamak için tarayıcıya yapıştırın." + }, + "providerConfigurationModal.errorCheckingConfig": { + "defaultMessage": "Bu sağlayıcı yapılandırması kontrol edilirken bir hata oluştu." + }, + "providerConfigurationModal.errorTitle": { + "defaultMessage": "Hata" + }, + "providerConfigurationModal.externalSetupIntro": { + "defaultMessage": "Bu sağlayıcı goose dışında yapılandırılmıştır. Şu adımları izleyin:" + }, + "providerConfigurationModal.goBack": { + "defaultMessage": "Geri Dön" + }, + "providerConfigurationModal.oauthLoginFailed": { + "defaultMessage": "OAuth oturum açma işlemi başarısız oldu: {error}" + }, + "providerConfigurationModal.oauthSignInDescription": { + "defaultMessage": "Bu sağlayıcıyı kullanmak için {providerName} hesabınızla oturum açın" + }, + "providerConfigurationModal.parameterRequired": { + "defaultMessage": "{paramName} gereklidir" + }, + "providerConfigurationModal.removeConfiguration": { + "defaultMessage": "Yapılandırmayı Kaldır" + }, + "providerConfigurationModal.seeDocumentation": { + "defaultMessage": "Daha fazla ayrıntı için belgelere bakın." + }, + "providerConfigurationModal.signInWith": { + "defaultMessage": "{providerName} ile oturum açın" + }, + "providerConfigurationModal.signingIn": { + "defaultMessage": "Oturum açılıyor..." + }, + "providerGrid.addProvider": { + "defaultMessage": "Sağlayıcı Ekle" + }, + "providerGrid.addProviderTitle": { + "defaultMessage": "Sağlayıcı Ekle" + }, + "providerGrid.chooseModel": { + "defaultMessage": "Modeli Seçin" + }, + "providerGrid.configureProvider": { + "defaultMessage": "Sağlayıcıyı Yapılandır" + }, + "providerGrid.editProvider": { + "defaultMessage": "Sağlayıcıyı Düzenle" + }, + "providerGrid.fromTemplateOrManual": { + "defaultMessage": "Şablondan veya manuel kurulumdan" + }, + "providerLogo.alt": { + "defaultMessage": "{providerName} logosu" + }, + "providerSelector.addCustomProvider": { + "defaultMessage": "Özel bir sağlayıcı ekleyin" + }, + "providerSelector.addCustomProviderTitle": { + "defaultMessage": "Özel Sağlayıcı Ekle" + }, + "providerSelector.connectProvider": { + "defaultMessage": "Bir Sağlayıcıya Bağlan" + }, + "providerSelector.connectProviderDescription": { + "defaultMessage": "OpenAI, Anthropic, Google, vb.'yi bağlayın" + }, + "providerSelector.freeLocalDescription": { + "defaultMessage": "Yerel bir model veya ücretsiz kredili bir sağlayıcı kullanın" + }, + "providerSelector.selectProvider": { + "defaultMessage": "Bir sağlayıcı seçin" + }, + "providerSelector.useFreeLocal": { + "defaultMessage": "Ücretsiz/Yerel Sağlayıcıları Kullanın" + }, + "providerSettings.configurationSettings": { + "defaultMessage": "Sağlayıcı Yapılandırma Ayarları" + }, + "providerSettings.loadingProviders": { + "defaultMessage": "Sağlayıcılar yükleniyor..." + }, + "providerSettings.onboardingDescription": { + "defaultMessage": "goose'yi kullanmaya başlamak için bir yapay zeka modeli sağlayıcısı seçin. Her sağlayıcı tarafından oluşturulan ve yerel olarak şifrelenecek ve saklanacak API anahtarlarını kullanmanız gerekecektir. Sağlayıcınızı istediğiniz zaman ayarlardan değiştirebilirsiniz." + }, + "providerSettings.otherProviders": { + "defaultMessage": "Diğer sağlayıcılar" + }, + "providerSetupActions.cancel": { + "defaultMessage": "İptal" + }, + "providerSetupActions.cannotDeleteActive": { + "defaultMessage": "Şu anda kullanımdayken {providerName}'yi silemezsiniz. Bu sağlayıcıyı silmeden önce lütfen farklı bir modele geçin." + }, + "providerSetupActions.confirmDelete": { + "defaultMessage": "Silmeyi Onayla" + }, + "providerSetupActions.confirmDeleteMessage": { + "defaultMessage": "{providerName} için yapılandırma parametrelerini silmek istediğinizden emin misiniz? Bu eylem geri alınamaz." + }, + "providerSetupActions.deleteProvider": { + "defaultMessage": "Sağlayıcıyı Sil" + }, + "providerSetupActions.enableProvider": { + "defaultMessage": "Sağlayıcıyı Etkinleştir" + }, + "providerSetupActions.ok": { + "defaultMessage": "Tamam" + }, + "providerSetupActions.submit": { + "defaultMessage": "Gönder" + }, + "recipeActivityEditor.activitiesDescription": { + "defaultMessage": "Tarif sohbeti penceresinde görüntülenecek üst satırdaki istemler ve etkinlik düğmeleri." + }, + "recipeActivityEditor.activitiesLabel": { + "defaultMessage": "Faaliyetler" + }, + "recipeActivityEditor.activityButtonsDescription": { + "defaultMessage": "Kullanıcıların tarifinizle etkileşime geçmesine yardımcı olmak için mesajın altında görünecek tıklanabilir düğmeler." + }, + "recipeActivityEditor.activityButtonsLabel": { + "defaultMessage": "Etkinlik Düğmeleri" + }, + "recipeActivityEditor.addActivity": { + "defaultMessage": "Etkinlik ekle" + }, + "recipeActivityEditor.addNewActivityPlaceholder": { + "defaultMessage": "Yeni etkinlik ekle..." + }, + "recipeActivityEditor.messageDescription": { + "defaultMessage": "Tarifin üst kısmında görünecek biçimlendirilmiş bir mesaj. Markdown biçimlendirmesini destekler." + }, + "recipeActivityEditor.messageLabel": { + "defaultMessage": "Mesaj" + }, + "recipeActivityEditor.messagePlaceholder": { + "defaultMessage": "Tarifiniz için kullanıcıya yönelik bir tanıtım mesajı girin (**kalın**, *italik*, `kod' vb. destekler)" + }, + "recipeExtensionSelector.description": { + "defaultMessage": "Bu tarifi çalıştırırken hangi uzantıların mevcut olacağını seçin. Varsayılan uzantıları kullanmak için boş bırakın." + }, + "recipeExtensionSelector.extensionsSelected": { + "defaultMessage": "{count,plural,one{# uzantı seçildi} other{# uzantı seçildi}}" + }, + "recipeExtensionSelector.label": { + "defaultMessage": "Uzantılar (İsteğe bağlı)" + }, + "recipeExtensionSelector.noExtensionsAvailable": { + "defaultMessage": "Uzantı yok" + }, + "recipeExtensionSelector.noExtensionsFound": { + "defaultMessage": "Uzantı bulunamadı" + }, + "recipeExtensionSelector.searchPlaceholder": { + "defaultMessage": "Uzantıları ara..." + }, + "recipeFormFields.addParameter": { + "defaultMessage": "Parametre ekle" + }, + "recipeFormFields.advancedOptions": { + "defaultMessage": "Gelişmiş Seçenekler" + }, + "recipeFormFields.advancedOptionsHint": { + "defaultMessage": "Faaliyetler, parametreler, model, uzantılar, yanıt şeması, alt tarifler" + }, + "recipeFormFields.descriptionLabel": { + "defaultMessage": "Açıklama" + }, + "recipeFormFields.descriptionPlaceholder": { + "defaultMessage": "Bu tarifin ne işe yaradığının kısa açıklaması" + }, + "recipeFormFields.enterValueFor": { + "defaultMessage": "{key} için değer girin" + }, + "recipeFormFields.initialPrompt": { + "defaultMessage": "İlk İstem" + }, + "recipeFormFields.instructionsLabel": { + "defaultMessage": "Talimatlar" + }, + "recipeFormFields.instructionsPlaceholder": { + "defaultMessage": "Kullanıcıdan gizlenen yapay zeka için ayrıntılı talimatlar" + }, + "recipeFormFields.openEditor": { + "defaultMessage": "Düzenleyiciyi Aç" + }, + "recipeFormFields.parameterNamePlaceholder": { + "defaultMessage": "Parametre adını girin..." + }, + "recipeFormFields.parametersDescription": { + "defaultMessage": "Parametreler, talimatlar/istem/aktivitelerdeki '{{parameter_name}}' sözdiziminden otomatik olarak algılanacaktır veya bunları aşağıda manuel olarak ekleyebilirsiniz." + }, + "recipeFormFields.parametersLabel": { + "defaultMessage": "Parametreler" + }, + "recipeFormFields.promptOptionalHint": { + "defaultMessage": "(İsteğe bağlı - Talimatlar veya İstem gereklidir)" + }, + "recipeFormFields.promptPlaceholder": { + "defaultMessage": "Tarif başladığında önceden doldurulmuş bilgi istemi" + }, + "recipeFormFields.responseJsonSchema": { + "defaultMessage": "Yanıt JSON Şeması" + }, + "recipeFormFields.responseJsonSchemaDescription": { + "defaultMessage": "JSON Şema formatını kullanarak yapay zekanın yanıtının beklenen yapısını tanımlayın" + }, + "recipeFormFields.templateVarHint": { + "defaultMessage": "Tarifi çalıştırırken doldurulabilecek parametreleri tanımlamak için '{{parameter_name}}' kullanın." + }, + "recipeFormFields.titleLabel": { + "defaultMessage": "Başlık" + }, + "recipeFormFields.titlePlaceholder": { + "defaultMessage": "Tarif başlığı" + }, + "recipeHeader.recipeLabel": { + "defaultMessage": "Tarif" + }, + "recipeModelSelector.backToModelList": { + "defaultMessage": "Model listesine geri dön" + }, + "recipeModelSelector.enterCustomModel": { + "defaultMessage": "Özel model adını girin" + }, + "recipeModelSelector.enterModelNotListed": { + "defaultMessage": "Listelenmeyen bir model girin..." + }, + "recipeModelSelector.fetchError": { + "defaultMessage": "Modeller getirilemedi. Lütfen daha sonra tekrar deneyin." + }, + "recipeModelSelector.loadingModels": { + "defaultMessage": "Modeller yükleniyor…" + }, + "recipeModelSelector.modelHint": { + "defaultMessage": "Seçilen sağlayıcı için varsayılan modeli kullanmak için boş bırakın" + }, + "recipeModelSelector.modelLabel": { + "defaultMessage": "Modeli (İsteğe bağlı)" + }, + "recipeModelSelector.providerHint": { + "defaultMessage": "Ayarlarda yapılandırılan varsayılan sağlayıcıyı kullanmak için boş bırakın" + }, + "recipeModelSelector.providerLabel": { + "defaultMessage": "Sağlayıcı (İsteğe bağlı)" + }, + "recipeModelSelector.selectModel": { + "defaultMessage": "Bir model seçin" + }, + "recipeModelSelector.selectProvider": { + "defaultMessage": "Sağlayıcı seçin" + }, + "recipeModelSelector.useDefaultProvider": { + "defaultMessage": "Varsayılan sağlayıcıyı kullan" + }, + "recipeNameField.defaultLabel": { + "defaultMessage": "Tarif Adı" + }, + "recipeNameField.formatHint": { + "defaultMessage": "Otomatik olarak biçimlendirilecektir (küçük harf, boşluklar için kısa çizgiler)" + }, + "recipeWarningModal.cancel": { + "defaultMessage": "İptal" + }, + "recipeWarningModal.descriptionLabel": { + "defaultMessage": "Açıklama:" + }, + "recipeWarningModal.firstTimeDescription": { + "defaultMessage": "Daha önce çalıştırmadığınız bir tarifi uygulamak üzeresiniz." + }, + "recipeWarningModal.hiddenCharsWarning": { + "defaultMessage": "Bu tarif, kötü amaçlarla kullanılabilecekleri için güvenliğiniz açısından göz ardı edilecek gizli karakterler içermektedir." + }, + "recipeWarningModal.instructionsLabel": { + "defaultMessage": "Talimatlar:" + }, + "recipeWarningModal.newRecipeWarningTitle": { + "defaultMessage": "⚠️ Yeni Tarif Uyarısı" + }, + "recipeWarningModal.recipePreview": { + "defaultMessage": "Tarif Önizlemesi:" + }, + "recipeWarningModal.securityWarningTitle": { + "defaultMessage": "⚠️ Güvenlik Uyarısı" + }, + "recipeWarningModal.titleLabel": { + "defaultMessage": "Başlık:" + }, + "recipeWarningModal.trustAndExecute": { + "defaultMessage": "Güvenin ve Uygulayın" + }, + "recipeWarningModal.trustSource": { + "defaultMessage": "Yalnızca bu tarifin kaynağına güveniyorsanız devam edin." + }, + "recipesView.addSchedule": { + "defaultMessage": "Program ekle" + }, + "recipesView.addSlashCommand": { + "defaultMessage": "Eğik çizgi komutu ekle" + }, + "recipesView.adjustSearchTerms": { + "defaultMessage": "Arama terimlerinizi ayarlamayı deneyin" + }, + "recipesView.allFiles": { + "defaultMessage": "Tüm Dosyalar" + }, + "recipesView.cancel": { + "defaultMessage": "İptal" + }, + "recipesView.copyDeeplink": { + "defaultMessage": "Derin Bağlantıyı Kopyala" + }, + "recipesView.copyDeeplinkFailedMsg": { + "defaultMessage": "Derin bağlantı panoya kopyalanamadı" + }, + "recipesView.copyFailedTitle": { + "defaultMessage": "Kopyalama başarısız oldu" + }, + "recipesView.copyYaml": { + "defaultMessage": "YAML'yi kopyala" + }, + "recipesView.copyYamlFailedMsg": { + "defaultMessage": "YAML tarifi panoya kopyalanamadı" + }, + "recipesView.createRecipe": { + "defaultMessage": "Tarif Oluştur" + }, + "recipesView.deeplinkCopiedMsg": { + "defaultMessage": "Tarif derin bağlantısı panoya kopyalandı" + }, + "recipesView.deeplinkCopiedTitle": { + "defaultMessage": "Derin bağlantı kopyalandı" + }, + "recipesView.deleteRecipe": { + "defaultMessage": "Tarifi sil" + }, + "recipesView.deleteRecipeConfirm": { + "defaultMessage": "\"{title}\"yi silmek istediğinizden emin misiniz?" + }, + "recipesView.deleteRecipeDetail": { + "defaultMessage": "Tarif dosyası silinecek." + }, + "recipesView.deleteRecipeTitle": { + "defaultMessage": "Tarifi Sil" + }, + "recipesView.editRecipe": { + "defaultMessage": "Tarifi düzenle" + }, + "recipesView.editSchedule": { + "defaultMessage": "Programı düzenle" + }, + "recipesView.editSlashCommand": { + "defaultMessage": "Eğik çizgi komutunu düzenle" + }, + "recipesView.errorLoadingRecipes": { + "defaultMessage": "Tarifler Yüklenirken Hata Oluştu" + }, + "recipesView.exportFailedMsg": { + "defaultMessage": "Tarif dosyaya aktarılamadı" + }, + "recipesView.exportFailedTitle": { + "defaultMessage": "Dışa aktarma başarısız oldu" + }, + "recipesView.exportRecipeDialogTitle": { + "defaultMessage": "Tarifi Dışa Aktar" + }, + "recipesView.exportToFile": { + "defaultMessage": "Dosyaya Aktar" + }, + "recipesView.noMatchingRecipes": { + "defaultMessage": "Eşleşen tarif bulunamadı" + }, + "recipesView.noSavedRecipes": { + "defaultMessage": "Kayıtlı tarif yok" + }, + "recipesView.noSavedRecipesDescription": { + "defaultMessage": "Sohbetlerden kaydedilen tarif burada görünecek." + }, + "recipesView.openInNewWindow": { + "defaultMessage": "Yeni pencerede aç" + }, + "recipesView.recipeDeletedSuccess": { + "defaultMessage": "Tarif başarıyla silindi" + }, + "recipesView.recipeExportedMsg": { + "defaultMessage": "Tarif {filePath}'ye kaydedildi" + }, + "recipesView.recipeExportedTitle": { + "defaultMessage": "Tarif dışa aktarıldı" + }, + "recipesView.recipesDescription": { + "defaultMessage": "Önceden tanımlanmış konfigürasyonlarla yeni oturumları hızlı bir şekilde başlatmak için kayıtlı tariflerinizi görüntüleyin ve yönetin. Aramak için {shortcut}." + }, + "recipesView.recipesTitle": { + "defaultMessage": "Tarifler" + }, + "recipesView.remove": { + "defaultMessage": "Kaldır" + }, + "recipesView.removeSchedule": { + "defaultMessage": "Programı Kaldır" + }, + "recipesView.runs": { + "defaultMessage": "{schedule}'yi çalıştırır" + }, + "recipesView.save": { + "defaultMessage": "Kaydet" + }, + "recipesView.scheduleDialogTitle": { + "defaultMessage": "{action} Programı" + }, + "recipesView.scheduleRemovedMsg": { + "defaultMessage": "Tarif artık otomatik olarak çalışmayacak" + }, + "recipesView.scheduleRemovedTitle": { + "defaultMessage": "Program kaldırıldı" + }, + "recipesView.scheduleSavedMsg": { + "defaultMessage": "Tarif {schedule}'yi çalıştıracak" + }, + "recipesView.scheduleSavedTitle": { + "defaultMessage": "Program kaydedildi" + }, + "recipesView.searchRecipesPlaceholder": { + "defaultMessage": "Tarif ara..." + }, + "recipesView.shareRecipe": { + "defaultMessage": "Tarifi paylaş" + }, + "recipesView.slashCommandDescription": { + "defaultMessage": "Bu tarifi herhangi bir sohbetten hızlı bir şekilde çalıştırmak için bir eğik çizgi komutu ayarlayın" + }, + "recipesView.slashCommandPlaceholder": { + "defaultMessage": "komut adı" + }, + "recipesView.slashCommandRemovedMsg": { + "defaultMessage": "Tarif eğik çizgi komutu kaldırıldı" + }, + "recipesView.slashCommandRemovedTitle": { + "defaultMessage": "Eğik çizgi komutu kaldırıldı" + }, + "recipesView.slashCommandSavedMsg": { + "defaultMessage": "Bu tarifi çalıştırmak için /{command} kullanın" + }, + "recipesView.slashCommandSavedTitle": { + "defaultMessage": "Eğik çizgi komutu kaydedildi" + }, + "recipesView.slashCommandTitle": { + "defaultMessage": "Eğik Çizgi Komutu" + }, + "recipesView.slashCommandUsageHint": { + "defaultMessage": "Bu tarifi çalıştırmak için herhangi bir sohbette /{command} kullanın" + }, + "recipesView.tryAgain": { + "defaultMessage": "Tekrar Deneyin" + }, + "recipesView.useRecipe": { + "defaultMessage": "Tarifi kullan" + }, + "recipesView.yamlCopiedMsg": { + "defaultMessage": "Tarif YAML panoya kopyalandı" + }, + "recipesView.yamlCopiedTitle": { + "defaultMessage": "YAML kopyalandı" + }, + "recipesView.yamlFiles": { + "defaultMessage": "YAML Dosyaları" + }, + "resetProviderSection.resetButton": { + "defaultMessage": "Sağlayıcıyı ve Modeli Sıfırla" + }, + "resetProviderSection.resetDescription": { + "defaultMessage": "Bu, seçtiğiniz modeli ve sağlayıcı ayarlarını temizleyecektir. Hiçbir varsayılan ayar yoksa, bunları yeniden ayarlamak için karşılama ekranına yönlendirileceksiniz." + }, + "responseStyle.conciseDescription": { + "defaultMessage": "Araç çağrıları varsayılan olarak kapalıdır ve yalnızca kullanılan aracı gösterir" + }, + "responseStyle.conciseLabel": { + "defaultMessage": "Kısa" + }, + "responseStyle.detailedDescription": { + "defaultMessage": "Araç çağrıları varsayılan olarak ayrıntıları ortaya çıkarmak için açık olarak gösterilir" + }, + "responseStyle.detailedLabel": { + "defaultMessage": "Detaylı" + }, + "scheduleDetailView.actions": { + "defaultMessage": "Eylemler" + }, + "scheduleDetailView.cannotModifyRunning": { + "defaultMessage": "Zaten çalışmakta olan bir program tetiklenemez veya değiştirilemez." + }, + "scheduleDetailView.created": { + "defaultMessage": "Oluşturuldu: {date}" + }, + "scheduleDetailView.cronExpression": { + "defaultMessage": "Cron İfadesi:" + }, + "scheduleDetailView.currentSession": { + "defaultMessage": "Mevcut Oturum:" + }, + "scheduleDetailView.currentlyRunning": { + "defaultMessage": "Şu anda Çalışıyor" + }, + "scheduleDetailView.dir": { + "defaultMessage": "Yön: {path}" + }, + "scheduleDetailView.editSchedule": { + "defaultMessage": "Programı Düzenle" + }, + "scheduleDetailView.errorPrefix": { + "defaultMessage": "Hata: {error}" + }, + "scheduleDetailView.failedToLoadSession": { + "defaultMessage": "Oturum yüklenemedi" + }, + "scheduleDetailView.idLabel": { + "defaultMessage": "Kimlik:" + }, + "scheduleDetailView.inspectJobError": { + "defaultMessage": "İş Hatasını İnceleyin" + }, + "scheduleDetailView.inspectNoInfo": { + "defaultMessage": "Detaylı bilgi mevcut değil" + }, + "scheduleDetailView.inspectRunningJob": { + "defaultMessage": "Çalışan İşi İncele" + }, + "scheduleDetailView.jobCancelled": { + "defaultMessage": "İş İptal Edildi" + }, + "scheduleDetailView.jobCancelledMsg": { + "defaultMessage": "Başlatma sırasında iş iptal edildi." + }, + "scheduleDetailView.jobInspection": { + "defaultMessage": "İş Denetimi" + }, + "scheduleDetailView.jobKilled": { + "defaultMessage": "İş Öldürüldü" + }, + "scheduleDetailView.killJobError": { + "defaultMessage": "İş Hatasını Öldür" + }, + "scheduleDetailView.killRunningJob": { + "defaultMessage": "Çalışan İşi Öldür" + }, + "scheduleDetailView.lastRun": { + "defaultMessage": "Son Çalıştırma:" + }, + "scheduleDetailView.loadingSchedule": { + "defaultMessage": "Program yükleniyor..." + }, + "scheduleDetailView.loadingSessions": { + "defaultMessage": "Oturumlar yükleniyor..." + }, + "scheduleDetailView.messages": { + "defaultMessage": "Mesajlar: {count}" + }, + "scheduleDetailView.newSession": { + "defaultMessage": "Yeni oturum: {sessionId}" + }, + "scheduleDetailView.noScheduleId": { + "defaultMessage": "Program kimliği sağlanmadı. Program listesine geri dönün." + }, + "scheduleDetailView.noSessions": { + "defaultMessage": "Bu program için oturum bulunamadı." + }, + "scheduleDetailView.pauseSchedule": { + "defaultMessage": "Programı Duraklat" + }, + "scheduleDetailView.pauseUnpauseError": { + "defaultMessage": "Duraklatma/Duraklatmayı Kaldırma Hatası" + }, + "scheduleDetailView.paused": { + "defaultMessage": "Duraklatıldı" + }, + "scheduleDetailView.pausedMsg": { + "defaultMessage": "\"{id}\" duraklatıldı" + }, + "scheduleDetailView.pausedWarning": { + "defaultMessage": "Bu program duraklatıldı ve otomatik olarak çalışmayacak. Manuel olarak tetiklemek için \"Şimdi Programlamayı Çalıştır\" seçeneğini kullanın veya otomatik yürütmeye devam etmek için duraklatmayı kaldırın." + }, + "scheduleDetailView.processStarted": { + "defaultMessage": "İşlem Başlatıldı:" + }, + "scheduleDetailView.recentSessions": { + "defaultMessage": "Son Oturumlar" + }, + "scheduleDetailView.recipeSource": { + "defaultMessage": "Tarif Kaynağı:" + }, + "scheduleDetailView.runScheduleError": { + "defaultMessage": "Zamanlama Hatasını Çalıştır" + }, + "scheduleDetailView.runScheduleNow": { + "defaultMessage": "Şimdi Planla'yı Çalıştır" + }, + "scheduleDetailView.scheduleDetails": { + "defaultMessage": "Program Ayrıntıları" + }, + "scheduleDetailView.scheduleInformation": { + "defaultMessage": "Program Bilgileri" + }, + "scheduleDetailView.scheduleLabel": { + "defaultMessage": "Program:" + }, + "scheduleDetailView.scheduleNotFound": { + "defaultMessage": "Program Bulunamadı" + }, + "scheduleDetailView.scheduleNotFoundError": { + "defaultMessage": "Program bulunamadı" + }, + "scheduleDetailView.schedulePaused": { + "defaultMessage": "Plan Duraklatıldı" + }, + "scheduleDetailView.scheduleTriggered": { + "defaultMessage": "Zamanlama Tetiklendi" + }, + "scheduleDetailView.scheduleUnpaused": { + "defaultMessage": "Duraklatmayı Kaldırmayı Planla" + }, + "scheduleDetailView.scheduleUpdated": { + "defaultMessage": "Program Güncellendi" + }, + "scheduleDetailView.sessionId": { + "defaultMessage": "Oturum Kimliği: {id}" + }, + "scheduleDetailView.tokens": { + "defaultMessage": "Jetonlar: {count}" + }, + "scheduleDetailView.unpauseSchedule": { + "defaultMessage": "Programı Duraklatmayı Kaldır" + }, + "scheduleDetailView.unpausedMsg": { + "defaultMessage": "Duraklatılmamış \"{id}\"" + }, + "scheduleDetailView.updateScheduleError": { + "defaultMessage": "Güncelleme Zamanlaması Hatası" + }, + "scheduleDetailView.updatedMsg": { + "defaultMessage": "\"{id}\" güncellendi" + }, + "scheduleDetailView.viewingScheduleId": { + "defaultMessage": "Program Kimliğini Görüntüleme: {id}" + }, + "scheduleModal.browseYaml": { + "defaultMessage": "YAML dosyasına göz atın..." + }, + "scheduleModal.cancel": { + "defaultMessage": "İptal" + }, + "scheduleModal.createNewSchedule": { + "defaultMessage": "Yeni Program Oluştur" + }, + "scheduleModal.createSchedule": { + "defaultMessage": "Program Oluştur" + }, + "scheduleModal.creating": { + "defaultMessage": "Oluşturuluyor..." + }, + "scheduleModal.deepLink": { + "defaultMessage": "Derin bağlantı" + }, + "scheduleModal.deepLinkPlaceholder": { + "defaultMessage": "goose://tarif bağlantısını buraya yapıştırın..." + }, + "scheduleModal.editSchedule": { + "defaultMessage": "Programı Düzenle" + }, + "scheduleModal.failedParseRecipe": { + "defaultMessage": "Dosyadan tarif ayrıştırılamadı." + }, + "scheduleModal.failedReadFile": { + "defaultMessage": "Seçilen dosya okunamadı." + }, + "scheduleModal.invalidDeepLink": { + "defaultMessage": "Geçersiz derin bağlantı. Lütfen goose://tarif bağlantısını kullanın." + }, + "scheduleModal.invalidFileType": { + "defaultMessage": "Geçersiz dosya türü: Lütfen bir YAML dosyası seçin (.yaml veya.yml)" + }, + "scheduleModal.nameLabel": { + "defaultMessage": "İsim:" + }, + "scheduleModal.namePlaceholder": { + "defaultMessage": "örneğin günlük özet işi" + }, + "scheduleModal.provideValidRecipe": { + "defaultMessage": "Lütfen geçerli bir tarif kaynağı sağlayın." + }, + "scheduleModal.recipeDescription": { + "defaultMessage": "Açıklama: {description}" + }, + "scheduleModal.recipeParsed": { + "defaultMessage": "Tarif başarıyla ayrıştırıldı" + }, + "scheduleModal.recipeTitle": { + "defaultMessage": "Başlık: {title}" + }, + "scheduleModal.scheduleIdRequired": { + "defaultMessage": "Program kimliği gerekli." + }, + "scheduleModal.scheduleLabel": { + "defaultMessage": "Program:" + }, + "scheduleModal.selected": { + "defaultMessage": "Seçili: {path}" + }, + "scheduleModal.sourceLabel": { + "defaultMessage": "Kaynak:" + }, + "scheduleModal.updateSchedule": { + "defaultMessage": "Programı Güncelle" + }, + "scheduleModal.updating": { + "defaultMessage": "Güncelleniyor..." + }, + "scheduleModal.yaml": { + "defaultMessage": "YAML" + }, + "schedulesView.confirmDelete": { + "defaultMessage": "\"{id}\" programı kaldırılsın mı? Tarif saklanacak." + }, + "schedulesView.createSchedule": { + "defaultMessage": "Program Oluştur" + }, + "schedulesView.description": { + "defaultMessage": "Tarifleri belirli zamanlarda otomatik olarak çalıştırmak için zamanlanmış görevler oluşturun ve yönetin." + }, + "schedulesView.edit": { + "defaultMessage": "Düzenle" + }, + "schedulesView.errorPrefix": { + "defaultMessage": "Hata: {error}" + }, + "schedulesView.inspect": { + "defaultMessage": "İncele" + }, + "schedulesView.inspectError": { + "defaultMessage": "İş Hatasını İnceleyin" + }, + "schedulesView.inspectNoInfo": { + "defaultMessage": "Bu iş için ayrıntılı bilgi mevcut değil" + }, + "schedulesView.jobInspection": { + "defaultMessage": "İş Denetimi" + }, + "schedulesView.jobKilled": { + "defaultMessage": "İş Öldürüldü" + }, + "schedulesView.kill": { + "defaultMessage": "Öldür" + }, + "schedulesView.killError": { + "defaultMessage": "İş Hatasını Öldür" + }, + "schedulesView.lastRun": { + "defaultMessage": "Son çalıştırma: {date}" + }, + "schedulesView.noSchedules": { + "defaultMessage": "Henüz program yok" + }, + "schedulesView.pause": { + "defaultMessage": "Duraklat" + }, + "schedulesView.pauseError": { + "defaultMessage": "Zamanlamayı Duraklat Hatası" + }, + "schedulesView.paused": { + "defaultMessage": "Duraklatıldı" + }, + "schedulesView.refresh": { + "defaultMessage": "Yenile" + }, + "schedulesView.refreshing": { + "defaultMessage": "Yenileniyor..." + }, + "schedulesView.resume": { + "defaultMessage": "Devam et" + }, + "schedulesView.running": { + "defaultMessage": "Koşu" + }, + "schedulesView.schedulePaused": { + "defaultMessage": "Plan Duraklatıldı" + }, + "schedulesView.schedulePausedMsg": { + "defaultMessage": "\"{id}\" programı başarıyla duraklatıldı" + }, + "schedulesView.scheduleUnpaused": { + "defaultMessage": "Duraklatmayı Kaldırmayı Planla" + }, + "schedulesView.scheduleUnpausedMsg": { + "defaultMessage": "\"{id}\" programının duraklatılması başarıyla kaldırıldı" + }, + "schedulesView.scheduleUpdated": { + "defaultMessage": "Program Güncellendi" + }, + "schedulesView.scheduleUpdatedMsg": { + "defaultMessage": "\"{id}\" programı başarıyla güncellendi" + }, + "schedulesView.scheduler": { + "defaultMessage": "Zamanlayıcı" + }, + "schedulesView.unpauseError": { + "defaultMessage": "Duraklatmayı Kaldırma Programı Hatası" + }, + "searchBar.caseSensitive": { + "defaultMessage": "Büyük/küçük harfe duyarlı" + }, + "searchBar.close": { + "defaultMessage": "Kapat ({shortcut})" + }, + "searchBar.next": { + "defaultMessage": "Sonraki ({shortcut})" + }, + "searchBar.placeholder": { + "defaultMessage": "Konuşmayı ara..." + }, + "searchBar.previous": { + "defaultMessage": "Önceki ({shortcut})" + }, + "secureStorageNotice.defaultMessage": { + "defaultMessage": "Anahtarlar anahtarlıkta güvenli bir şekilde saklanır" + }, + "securityToggle.apiTokenDescription": { + "defaultMessage": "Sınıflandırma hizmeti için kimlik doğrulama belirteci" + }, + "securityToggle.apiTokenOptional": { + "defaultMessage": "API Jetonu (İsteğe bağlı)" + }, + "securityToggle.classificationEndpoint": { + "defaultMessage": "Sınıflandırma Uç Noktası" + }, + "securityToggle.classificationEndpointDescription": { + "defaultMessage": "Sınıflandırma hizmetinizin tam URL'sini girin" + }, + "securityToggle.commandClassifierActive": { + "defaultMessage": "Komut sınıflandırıcı etkin (ortamdan otomatik olarak yapılandırılır)" + }, + "securityToggle.commandEndpointDescription": { + "defaultMessage": "Komut ekleme sınıflandırma hizmetinizin tam URL'sini girin" + }, + "securityToggle.commandInjectionDescription": { + "defaultMessage": "Kötü amaçlı kabuk komutlarını tespit etmek için ML modellerini kullanın" + }, + "securityToggle.detectionModel": { + "defaultMessage": "Algılama Modeli" + }, + "securityToggle.detectionModelDescription": { + "defaultMessage": "Hızlı enjeksiyon tespiti için hangi ML modelinin kullanılacağını seçin" + }, + "securityToggle.detectionThreshold": { + "defaultMessage": "Tespit Eşiği" + }, + "securityToggle.enableCommandInjection": { + "defaultMessage": "Command Injection ML Algılamayı Etkinleştir" + }, + "securityToggle.enablePromptInjection": { + "defaultMessage": "İstem Ekleme Algılamayı Etkinleştir" + }, + "securityToggle.enablePromptInjectionMl": { + "defaultMessage": "Prompt Injection ML Algılamayı Etkinleştir" + }, + "securityToggle.mlEndpointDescription": { + "defaultMessage": "ML sınıflandırma hizmetinizin tam URL'sini girin (model tanımlayıcı dahil)" + }, + "securityToggle.mlTokenDescription": { + "defaultMessage": "ML hizmeti için kimlik doğrulama belirteci (ör. HuggingFace belirteci)" + }, + "securityToggle.promptInjectionDescription": { + "defaultMessage": "Potansiyel anlık enjeksiyon saldırılarını tespit edin ve önleyin" + }, + "securityToggle.promptInjectionMlDescription": { + "defaultMessage": "Sohbetinize olası istem eklemeyi tespit etmek için makine öğrenimi modellerini kullanın" + }, + "securityToggle.thresholdDescription": { + "defaultMessage": "Daha yüksek değerler daha katıdır (0,01 = çok yumuşak, 1,0 = maksimum katı)" + }, + "sessionHistory.cancel": { + "defaultMessage": "İptal" + }, + "sessionHistory.copy": { + "defaultMessage": "Kopyala" + }, + "sessionHistory.empty.description": { + "defaultMessage": "Bu oturum herhangi bir mesaj içermiyor" + }, + "sessionHistory.empty.title": { + "defaultMessage": "Mesaj bulunamadı" + }, + "sessionHistory.error.loading": { + "defaultMessage": "Oturum Ayrıntıları Yüklenirken Hata Oluştu" + }, + "sessionHistory.error.tryAgain": { + "defaultMessage": "Tekrar Deneyin" + }, + "sessionHistory.loading": { + "defaultMessage": "Oturum ayrıntıları yükleniyor..." + }, + "sessionHistory.resume": { + "defaultMessage": "Devam et" + }, + "sessionHistory.searchPlaceholder": { + "defaultMessage": "Arama geçmişi..." + }, + "sessionHistory.share": { + "defaultMessage": "Paylaş" + }, + "sessionHistory.shareModal.description": { + "defaultMessage": "Başkalarına goose sohbetinizin salt okunur görünümünü vermek için bu oturum bağlantısını paylaşın." + }, + "sessionHistory.shareModal.title": { + "defaultMessage": "Oturumu Paylaş (beta)" + }, + "sessionHistory.shareTooltip": { + "defaultMessage": "Oturum paylaşımını etkinleştirmek için Ayarlar > Oturum > Oturum Paylaşımı bölümüne gidin." + }, + "sessionHistory.sharing": { + "defaultMessage": "Paylaşılıyor..." + }, + "sessionHistory.toast.copyFailed": { + "defaultMessage": "Bağlantı panoya kopyalanamadı" + }, + "sessionHistory.toast.launchFailed": { + "defaultMessage": "Oturum başlatılamadı: {error}" + }, + "sessionHistory.toast.shareFailed": { + "defaultMessage": "Oturum paylaşılamadı: {error}" + }, + "sessionIndicators.error": { + "defaultMessage": "Oturum bir hatayla karşılaştı" + }, + "sessionIndicators.newActivity": { + "defaultMessage": "Yeni aktivite var" + }, + "sessionIndicators.streaming": { + "defaultMessage": "Akış" + }, + "sessionItem.messageCount": { + "defaultMessage": "{count} mesajları" + }, + "sessionSharingSection.alreadyConfigured": { + "defaultMessage": "Oturum paylaşımı zaten yapılandırılmış" + }, + "sessionSharingSection.baseUrl": { + "defaultMessage": "Temel URL" + }, + "sessionSharingSection.connectionFailed": { + "defaultMessage": "Bağlantı başarısız oldu." + }, + "sessionSharingSection.connectionSuccess": { + "defaultMessage": "Bağlantı başarılı!" + }, + "sessionSharingSection.connectionTimedOut": { + "defaultMessage": "Bağlantı zaman aşımına uğradı. Sunucu yavaş olabilir veya erişilemez olabilir." + }, + "sessionSharingSection.descriptionConfigured": { + "defaultMessage": "Oturum paylaşımı yapılandırılmıştır ancak tamamen etkinleştirilmiştir; oturumlarınız yalnızca paylaş düğmesini açıkça tıklattığınızda paylaşılır." + }, + "sessionSharingSection.descriptionDefault": { + "defaultMessage": "Oturumlarınızı başkalarıyla paylaşmak için oturum paylaşımını etkinleştirebilirsiniz." + }, + "sessionSharingSection.enableSharing": { + "defaultMessage": "Oturum paylaşımını etkinleştir" + }, + "sessionSharingSection.invalidUrl": { + "defaultMessage": "Geçersiz URL biçimi. Lütfen geçerli bir URL girin (ör. https://example.com/api)." + }, + "sessionSharingSection.serverError": { + "defaultMessage": "Sunucu hatası: HTTP {status}. Sunucu doğru yapılandırılmamış olabilir." + }, + "sessionSharingSection.testConnection": { + "defaultMessage": "Bağlantıyı Test Et" + }, + "sessionSharingSection.testing": { + "defaultMessage": "Test ediliyor..." + }, + "sessionSharingSection.testingConnection": { + "defaultMessage": "Bağlantı test ediliyor..." + }, + "sessionSharingSection.title": { + "defaultMessage": "Oturum Paylaşımı" + }, + "sessionSharingSection.unknownError": { + "defaultMessage": "Bilinmeyen bir hata oluştu." + }, + "sessionSharingSection.unreachableServer": { + "defaultMessage": "Sunucuya ulaşılamıyor. Lütfen URL'yi ve ağ bağlantınızı kontrol edin." + }, + "sessionSharingSection.urlPlaceholder": { + "defaultMessage": "https://example.com/api" + }, + "sessionViewComponents.empty.description": { + "defaultMessage": "Bu oturum herhangi bir mesaj içermiyor" + }, + "sessionViewComponents.empty.title": { + "defaultMessage": "Mesaj bulunamadı" + }, + "sessionViewComponents.error.loading": { + "defaultMessage": "Oturum Ayrıntıları Yüklenirken Hata Oluştu" + }, + "sessionViewComponents.error.tryAgain": { + "defaultMessage": "Tekrar Deneyin" + }, + "sessionViewComponents.role.assistant": { + "defaultMessage": "Goose" + }, + "sessionViewComponents.role.user": { + "defaultMessage": "sen" + }, + "sessions.action.delete": { + "defaultMessage": "Oturumu sil" + }, + "sessions.action.duplicate": { + "defaultMessage": "Yinelenen oturum" + }, + "sessions.action.editName": { + "defaultMessage": "Oturum adını düzenleyin" + }, + "sessions.action.export": { + "defaultMessage": "Oturumu dışa aktar" + }, + "sessions.action.openNewWindow": { + "defaultMessage": "Yeni pencerede aç" + }, + "sessions.action.shareNostr": { + "defaultMessage": "Şifrelenmiş Nostr bağlantısını paylaşın" + }, + "sessions.cancel": { + "defaultMessage": "İptal" + }, + "sessions.chatHistory": { + "defaultMessage": "Sohbet geçmişi" + }, + "sessions.chatHistoryDesc": { + "defaultMessage": "Goose ile geçmiş konuşmalarınızı görüntüleyin ve arayın. Aramak için {shortcut}." + }, + "sessions.close": { + "defaultMessage": "Kapat" + }, + "sessions.delete.message": { + "defaultMessage": "\"{name}\" oturumunu silmek istediğinizden emin misiniz? Bu eylem geri alınamaz." + }, + "sessions.delete.title": { + "defaultMessage": "Oturumu Sil" + }, + "sessions.edit.placeholder": { + "defaultMessage": "Oturum açıklamasını girin" + }, + "sessions.edit.title": { + "defaultMessage": "Oturum Açıklamasını Düzenle" + }, + "sessions.empty.description": { + "defaultMessage": "Sohbet geçmişiniz burada görünecek" + }, + "sessions.empty.title": { + "defaultMessage": "Sohbet oturumu bulunamadı" + }, + "sessions.error.loading": { + "defaultMessage": "Oturumlar Yüklenirken Hata Oluştu" + }, + "sessions.error.tryAgain": { + "defaultMessage": "Tekrar Deneyin" + }, + "sessions.extensions": { + "defaultMessage": "Uzantılar:" + }, + "sessions.import": { + "defaultMessage": "Oturumu İçe Aktar" + }, + "sessions.importNostr": { + "defaultMessage": "Bağlantıyı İçe Aktar" + }, + "sessions.importNostr.description": { + "defaultMessage": "Oturumu almak, şifresini çözmek ve içe aktarmak için bir Goose Nostr paylaşım bağlantısını yapıştırın." + }, + "sessions.importNostr.placeholder": { + "defaultMessage": "goose://sessions/nostr?nevent=...&key=..." + }, + "sessions.importNostr.title": { + "defaultMessage": "Nostr Oturumunu İçe Aktar" + }, + "sessions.importing": { + "defaultMessage": "İçe aktarılıyor..." + }, + "sessions.loadingMore": { + "defaultMessage": "Daha fazla oturum yükleniyor..." + }, + "sessions.save": { + "defaultMessage": "Kaydet" + }, + "sessions.saving": { + "defaultMessage": "Kaydediliyor..." + }, + "sessions.search.noResults": { + "defaultMessage": "Eşleşen oturum bulunamadı" + }, + "sessions.search.noResultsDesc": { + "defaultMessage": "Arama terimlerinizi ayarlamayı deneyin" + }, + "sessions.searchPlaceholder": { + "defaultMessage": "Arama geçmişi..." + }, + "sessions.shareNostr.description": { + "defaultMessage": "Bu bağlantıya sahip olan herkes oturumu alıp şifresini çözebilir. Buna bir sırmış gibi davran." + }, + "sessions.shareNostr.title": { + "defaultMessage": "Şifreli Nostr Paylaşım Bağlantısı" + }, + "sessions.toast.copied": { + "defaultMessage": "Panoya kopyalandı" + }, + "sessions.toast.deleteFailed": { + "defaultMessage": "\"{name}\" oturumu silinemedi: {error}" + }, + "sessions.toast.deleted": { + "defaultMessage": "Oturum başarıyla silindi" + }, + "sessions.toast.duplicateFailed": { + "defaultMessage": "Oturum kopyalanamadı: {error}" + }, + "sessions.toast.duplicated": { + "defaultMessage": "\"{name}\" oturumu başarıyla kopyalandı" + }, + "sessions.toast.exported": { + "defaultMessage": "Oturum başarıyla dışa aktarıldı" + }, + "sessions.toast.importFailed": { + "defaultMessage": "Oturum içe aktarılamadı: {error}" + }, + "sessions.toast.imported": { + "defaultMessage": "Oturum başarıyla içe aktarıldı" + }, + "sessions.toast.shareNostr": { + "defaultMessage": "Şifrelenmiş Nostr paylaşım bağlantısı oluşturuldu" + }, + "sessions.toast.shareNostrFailed": { + "defaultMessage": "Nostr paylaşım bağlantısı oluşturulamadı: {error}" + }, + "sessions.toast.updateFailed": { + "defaultMessage": "Oturum açıklaması güncellenemedi: {error}" + }, + "sessions.toast.updated": { + "defaultMessage": "Oturum açıklaması başarıyla güncellendi" + }, + "sessionsInsights.failedToLoad": { + "defaultMessage": "Analizler yüklenemedi" + }, + "sessionsInsights.noRecentChats": { + "defaultMessage": "Yeni sohbet oturumu bulunamadı." + }, + "sessionsInsights.recentChats": { + "defaultMessage": "Son sohbetler" + }, + "sessionsInsights.seeAll": { + "defaultMessage": "Tümünü gör" + }, + "sessionsInsights.totalSessions": { + "defaultMessage": "Toplam oturumlar" + }, + "sessionsInsights.totalTokens": { + "defaultMessage": "Toplam jeton" + }, + "sessionsList.showAll": { + "defaultMessage": "Tümünü Göster" + }, + "sessionsList.startNewChat": { + "defaultMessage": "Yeni Sohbet Başlat" + }, + "sessionsList.untitledSession": { + "defaultMessage": "Başlıksız oturum" + }, + "sessionsView.error.failedToLoad": { + "defaultMessage": "Oturum ayrıntıları yüklenemedi. Lütfen daha sonra tekrar deneyin." + }, + "sessionsView.loading": { + "defaultMessage": "Yükleniyor..." + }, + "settings.appearance.description": { + "defaultMessage": "goose'nin sisteminizde nasıl görüneceğini yapılandırın" + }, + "settings.appearance.title": { + "defaultMessage": "Görünüm" + }, + "settings.close": { + "defaultMessage": "Kapat" + }, + "settings.costTracking.description": { + "defaultMessage": "Model fiyatlandırmasını ve kullanım maliyetlerini göster" + }, + "settings.costTracking.title": { + "defaultMessage": "Maliyet Takibi" + }, + "settings.dockIcon.description": { + "defaultMessage": "Dock'ta goose'yi göster" + }, + "settings.dockIcon.title": { + "defaultMessage": "Bağlantı noktası simgesi" + }, + "settings.help.description": { + "defaultMessage": "Sorunları bildirerek veya yeni özellikler talep ederek goose'yi geliştirmemize yardımcı olun" + }, + "settings.help.reportBug": { + "defaultMessage": "Hata Bildir" + }, + "settings.help.requestFeature": { + "defaultMessage": "Özellik Talep Edin" + }, + "settings.help.title": { + "defaultMessage": "Yardım ve geri bildirim" + }, + "settings.menuBarIcon.description": { + "defaultMessage": "Menü çubuğunda goose'yi göster" + }, + "settings.menuBarIcon.title": { + "defaultMessage": "Menü çubuğu simgesi" + }, + "settings.navigation.customize": { + "defaultMessage": "Öğeleri Özelleştir" + }, + "settings.navigation.description": { + "defaultMessage": "Gezinme düzenini ve davranışını özelleştirin" + }, + "settings.navigation.mode": { + "defaultMessage": "Mod" + }, + "settings.navigation.position": { + "defaultMessage": "Pozisyon" + }, + "settings.navigation.style": { + "defaultMessage": "Stil" + }, + "settings.navigation.title": { + "defaultMessage": "Navigasyon" + }, + "settings.notifications.configGuide": { + "defaultMessage": "Yapılandırma kılavuzu" + }, + "settings.notifications.description": { + "defaultMessage": "Bildirimler işletim sisteminiz tarafından yönetilir - {link}" + }, + "settings.notifications.modal.macInstructions": { + "defaultMessage": "MacOS'ta bildirimleri etkinleştirmek için:" + }, + "settings.notifications.modal.macStep1": { + "defaultMessage": "Sistem Tercihlerini Aç" + }, + "settings.notifications.modal.macStep2": { + "defaultMessage": "Bildirimler'e tıklayın" + }, + "settings.notifications.modal.macStep3": { + "defaultMessage": "Uygulama listesinde goose'yi bulun ve seçin" + }, + "settings.notifications.modal.macStep4": { + "defaultMessage": "Bildirimleri etkinleştirin ve ayarları istediğiniz gibi yapın" + }, + "settings.notifications.modal.title": { + "defaultMessage": "Bildirimler Nasıl Etkinleştirilir" + }, + "settings.notifications.modal.winInstructions": { + "defaultMessage": "Windows'ta bildirimleri etkinleştirmek için:" + }, + "settings.notifications.modal.winStep1": { + "defaultMessage": "Ayarları Aç" + }, + "settings.notifications.modal.winStep2": { + "defaultMessage": "Sistem > Bildirimler'e gidin" + }, + "settings.notifications.modal.winStep3": { + "defaultMessage": "Uygulama listesinde goose'yi bulun ve seçin" + }, + "settings.notifications.modal.winStep4": { + "defaultMessage": "Bildirimleri açın ve ayarları istediğiniz gibi yapın" + }, + "settings.notifications.openSettings": { + "defaultMessage": "Ayarları Aç" + }, + "settings.notifications.task.description": { + "defaultMessage": "Pencere arka plandayken Goose bir görevi tamamladığında bildirim al" + }, + "settings.notifications.task.title": { + "defaultMessage": "Görev tamamlama bildirimleri" + }, + "settings.notifications.title": { + "defaultMessage": "Bildirimler" + }, + "settings.preventSleep.description": { + "defaultMessage": "goose bir görevi çalıştırırken bilgisayarınızı uyanık tutun (ekran hala kilitlenebilir)" + }, + "settings.preventSleep.title": { + "defaultMessage": "Uykuyu Önleyin" + }, + "settings.theme.description": { + "defaultMessage": "goose'nin görünümünü ve hissini özelleştirin" + }, + "settings.theme.title": { + "defaultMessage": "Tema" + }, + "settings.updates.description": { + "defaultMessage": "goose'nin en iyi şekilde çalışmasını sağlamak için güncellemeleri kontrol edin ve yükleyin" + }, + "settings.updates.title": { + "defaultMessage": "Güncellemeler" + }, + "settings.version.title": { + "defaultMessage": "Sürüm" + }, + "settingsView.tabApp": { + "defaultMessage": "Uygulama" + }, + "settingsView.tabChat": { + "defaultMessage": "Sohbet" + }, + "settingsView.tabKeyboard": { + "defaultMessage": "Klavye" + }, + "settingsView.tabLocalInference": { + "defaultMessage": "Yerel Çıkarım" + }, + "settingsView.tabModels": { + "defaultMessage": "Modeller" + }, + "settingsView.tabPrompts": { + "defaultMessage": "İstemler" + }, + "settingsView.tabSession": { + "defaultMessage": "Oturum" + }, + "settingsView.title": { + "defaultMessage": "Ayarlar" + }, + "sharedSession.loading": { + "defaultMessage": "Oturum ayrıntıları yükleniyor..." + }, + "sharedSession.title": { + "defaultMessage": "Paylaşılan Oturum" + }, + "sheet.close": { + "defaultMessage": "Kapat" + }, + "shortcutRecorder.cancel": { + "defaultMessage": "İptal" + }, + "shortcutRecorder.clickToRecord": { + "defaultMessage": "Kaydetmek için tıklayın..." + }, + "shortcutRecorder.conflictWarning": { + "defaultMessage": "Bu kısayol zaten {label} tarafından kullanılıyor. Kaydetme işlemi onu bu eyleme yeniden atayacaktır." + }, + "shortcutRecorder.pressShortcut": { + "defaultMessage": "Kısayol tuşuna basın..." + }, + "shortcutRecorder.save": { + "defaultMessage": "Kaydet" + }, + "skillsView.addSkill": { + "defaultMessage": "Beceri Ekle" + }, + "skillsView.adjustSearchTerms": { + "defaultMessage": "Arama terimlerinizi ayarlamayı deneyin" + }, + "skillsView.comingSoon": { + "defaultMessage": "Yakında" + }, + "skillsView.errorLoadingSkills": { + "defaultMessage": "Beceriler Yüklenirken Hata Oluştu" + }, + "skillsView.noMatchingSkills": { + "defaultMessage": "Eşleşen beceri bulunamadı" + }, + "skillsView.noSkillsDescription": { + "defaultMessage": "Beceriler ~/.config/agents/skills/,.goose/skills/ veya desteklenen diğer dizinlerdeki SKILL.md dosyalarından yüklenir." + }, + "skillsView.noSkillsInstalled": { + "defaultMessage": "Hiçbir beceri yüklü değil" + }, + "skillsView.searchSkillsPlaceholder": { + "defaultMessage": "Arama becerileri..." + }, + "skillsView.skillsDescription": { + "defaultMessage": "Goose yeteneklerini genişleten yüklü becerileri görüntüleyin. Aramak için {shortcut}." + }, + "skillsView.skillsTitle": { + "defaultMessage": "Beceriler" + }, + "skillsView.tryAgain": { + "defaultMessage": "Tekrar Deneyin" + }, + "spellcheckToggle.description": { + "defaultMessage": "Sohbet girişinde yazımı kontrol edin. Etkili olması için yeniden başlatma gerekir." + }, + "spellcheckToggle.title": { + "defaultMessage": "Yazım Denetimini Etkinleştir" + }, + "standaloneAppView.failedToLoad": { + "defaultMessage": "Uygulama Yüklenemedi" + }, + "standaloneAppView.initializing": { + "defaultMessage": "Uygulama başlatılıyor..." + }, + "standaloneAppView.missingParams": { + "defaultMessage": "Gerekli parametreler eksik" + }, + "stringUtils.configuredProvider": { + "defaultMessage": "{name} sağlayıcısı yapılandırıldı" + }, + "stringUtils.ollamaApp": { + "defaultMessage": "Ollama uygulaması" + }, + "stringUtils.ollamaNotConfiguredPrefix": { + "defaultMessage": "Kullanmak için ya" + }, + "stringUtils.ollamaNotConfiguredSuffix": { + "defaultMessage": "makinenizde kurulu ve açık olmalı veya OLLAMA_HOST için bir değer girmelisiniz." + }, + "subRecipeEditor.addExisting": { + "defaultMessage": "Mevcut Ekle" + }, + "subRecipeEditor.createNew": { + "defaultMessage": "Yeni Alt Tarif Oluştur" + }, + "subRecipeEditor.deleteSubrecipe": { + "defaultMessage": "{name} alt tarifini sil" + }, + "subRecipeEditor.description": { + "defaultMessage": "Alt tarifler, uygulama sırasında araç olarak adlandırılabilecek tariflerdir. Çok adımlı iş akışlarına ve yeniden kullanılabilir bileşenlere olanak tanırlar." + }, + "subRecipeEditor.duplicateName": { + "defaultMessage": "Yinelenen Ad" + }, + "subRecipeEditor.duplicateNameMsg": { + "defaultMessage": "\"{name}\" adlı bir alt tarif zaten mevcut. Lütfen benzersiz bir ad kullanın." + }, + "subRecipeEditor.editSubrecipe": { + "defaultMessage": "{name} alt tarifini düzenleyin" + }, + "subRecipeEditor.label": { + "defaultMessage": "Alt tarifler" + }, + "subRecipeEditor.preconfiguredValues": { + "defaultMessage": "Önceden yapılandırılmış değerler:" + }, + "subRecipeEditor.sequential": { + "defaultMessage": "Sıralı" + }, + "subRecipeModal.addTitle": { + "defaultMessage": "Alt Tarif Ekle" + }, + "subRecipeModal.apply": { + "defaultMessage": "Uygula" + }, + "subRecipeModal.browse": { + "defaultMessage": "Göz at" + }, + "subRecipeModal.cancel": { + "defaultMessage": "İptal" + }, + "subRecipeModal.closeModal": { + "defaultMessage": "Alt tarif modelini kapat" + }, + "subRecipeModal.configureTitle": { + "defaultMessage": "Alt Tarifi Yapılandır" + }, + "subRecipeModal.descriptionLabel": { + "defaultMessage": "Açıklama" + }, + "subRecipeModal.descriptionPlaceholder": { + "defaultMessage": "Bu alt tarifin ne işe yaradığına ilişkin isteğe bağlı açıklama..." + }, + "subRecipeModal.invalidFile": { + "defaultMessage": "Geçersiz Dosya" + }, + "subRecipeModal.invalidFileMsg": { + "defaultMessage": "Lütfen bir YAML dosyası seçin (.yaml veya.yml)." + }, + "subRecipeModal.nameHint": { + "defaultMessage": "Araç adını oluşturmak için kullanılan benzersiz tanımlayıcı" + }, + "subRecipeModal.nameLabel": { + "defaultMessage": "İsim" + }, + "subRecipeModal.namePlaceholder": { + "defaultMessage": "ör. güvenlik_tarama" + }, + "subRecipeModal.pathHint": { + "defaultMessage": "Mevcut bir tarif dosyasına göz atın veya manuel olarak bir yol girin" + }, + "subRecipeModal.pathLabel": { + "defaultMessage": "Yol" + }, + "subRecipeModal.pathPlaceholder": { + "defaultMessage": "ör.,./subrecipes/security-analiz.yaml" + }, + "subRecipeModal.preconfiguredValues": { + "defaultMessage": "Önceden Yapılandırılmış Değerler" + }, + "subRecipeModal.preconfiguredValuesHint": { + "defaultMessage": "Her zaman alt tarife aktarılan isteğe bağlı parametre değerleri" + }, + "subRecipeModal.sequentialHint": { + "defaultMessage": "(Birden fazla alt tarif örneğinin sıralı olarak yürütülmesini zorlar)" + }, + "subRecipeModal.sequentialLabel": { + "defaultMessage": "Tekrarlandığında sıralı" + }, + "subRecipeModal.subtitle": { + "defaultMessage": "Tarif yürütme sırasında araç olarak çağrılabilecek bir alt tarif yapılandırın" + }, + "switchModelModal.backToModelList": { + "defaultMessage": "Model listesine geri dön" + }, + "switchModelModal.cancel": { + "defaultMessage": "İptal" + }, + "switchModelModal.checkProviderConfig": { + "defaultMessage": "Ayarlar → Sağlayıcılar bölümünde sağlayıcı yapılandırmanızı kontrol edin" + }, + "switchModelModal.chooseModel": { + "defaultMessage": "Bir model seçin:" + }, + "switchModelModal.claudeAdaptive": { + "defaultMessage": "Uyarlanabilir - Claude ne zaman ve ne kadar düşüneceğine karar verir" + }, + "switchModelModal.claudeDisabled": { + "defaultMessage": "Engelli - Uzun süreli düşünme yok" + }, + "switchModelModal.claudeEffortHigh": { + "defaultMessage": "Yüksek - Derin muhakeme (varsayılan)" + }, + "switchModelModal.claudeEffortLow": { + "defaultMessage": "Düşük - Minimal düşünme, en hızlı tepkiler" + }, + "switchModelModal.claudeEffortMax": { + "defaultMessage": "Max - Düşünme derinliğinde kısıtlama yok" + }, + "switchModelModal.claudeEffortMedium": { + "defaultMessage": "Orta - Orta düzeyde düşünme" + }, + "switchModelModal.claudeEnabled": { + "defaultMessage": "Etkin - Düşünmek için sabit jeton bütçesi" + }, + "switchModelModal.couldNotContactProvider": { + "defaultMessage": "Sağlayıcıyla iletişim kurulamadı" + }, + "switchModelModal.customModelName": { + "defaultMessage": "Özel model adı" + }, + "switchModelModal.description": { + "defaultMessage": "Görüşmeleriniz için kullanılacak sağlayıcıyı ve modeli seçin." + }, + "switchModelModal.enterModelNotListed": { + "defaultMessage": "Listelenmeyen bir model girin..." + }, + "switchModelModal.extendedThinking": { + "defaultMessage": "Genişletilmiş Düşünme" + }, + "switchModelModal.geminiOnly": { + "defaultMessage": "(Yalnızca Gemini 3 modelleri)" + }, + "switchModelModal.goToSettings": { + "defaultMessage": "Ayarlar'a gidin" + }, + "switchModelModal.loadingModels": { + "defaultMessage": "Modeller yükleniyor…" + }, + "switchModelModal.localModelsDescription": { + "defaultMessage": "Yerel çıkarımı kullanmak için öncelikle bilgisayarınıza bir model indirmeniz gerekir. Yerel modelleri yönetmek için Ayarlar → Modeller'e gidin." + }, + "switchModelModal.localModelsTitle": { + "defaultMessage": "Önce yerel modellerin indirilmesi gerekiyor" + }, + "switchModelModal.providerPlaceholder": { + "defaultMessage": "Sağlayıcı, aramak için yazın" + }, + "switchModelModal.quickStartGuide": { + "defaultMessage": "Hızlı başlangıç kılavuzu" + }, + "switchModelModal.recommended": { + "defaultMessage": "Önerilen" + }, + "switchModelModal.selectEffortLevel": { + "defaultMessage": "Çaba düzeyini seçin" + }, + "switchModelModal.selectModel": { + "defaultMessage": "Lütfen bir model seçin" + }, + "switchModelModal.selectModelButton": { + "defaultMessage": "Modeli seçin" + }, + "switchModelModal.selectModelPlaceholder": { + "defaultMessage": "Bir model seçin, aramak için yazın" + }, + "switchModelModal.selectOrEnterModel": { + "defaultMessage": "Lütfen bir model seçin veya girin" + }, + "switchModelModal.selectProvider": { + "defaultMessage": "Lütfen bir sağlayıcı seçin" + }, + "switchModelModal.selectThinkingLevel": { + "defaultMessage": "Düşünme düzeyini seçin" + }, + "switchModelModal.selectThinkingMode": { + "defaultMessage": "Düşünme modunu seçin" + }, + "switchModelModal.thinkingBudget": { + "defaultMessage": "Düşünme Bütçesi (jetonlar)" + }, + "switchModelModal.thinkingEffort": { + "defaultMessage": "Düşünme Çabası" + }, + "switchModelModal.thinkingEffortOff": { + "defaultMessage": "Kapalı - Uzun süreli düşünme yok" + }, + "switchModelModal.thinkingLevel": { + "defaultMessage": "Düşünme Seviyesi" + }, + "switchModelModal.thinkingLevelHigh": { + "defaultMessage": "Yüksek - Daha derin muhakeme, daha yüksek gecikme" + }, + "switchModelModal.thinkingLevelLow": { + "defaultMessage": "Düşük - Daha iyi gecikme, daha hafif muhakeme" + }, + "switchModelModal.title": { + "defaultMessage": "Modelleri değiştir" + }, + "switchModelModal.typeModelName": { + "defaultMessage": "Model adını buraya yazın" + }, + "switchModelModal.useOtherProvider": { + "defaultMessage": "Başka sağlayıcı kullan" + }, + "telemetryConsentPrompt.description": { + "defaultMessage": "goose'nin iyileştirilmesine yardımcı olmak için anonim kullanım verilerini paylaşmak ister misiniz? Konuşmalarınızı, kodlarınızı veya kişisel verilerinizi asla toplamıyoruz." + }, + "telemetryConsentPrompt.heading": { + "defaultMessage": "goose'nin iyileştirilmesine yardımcı olun" + }, + "telemetryConsentPrompt.learnMore": { + "defaultMessage": "Daha fazla bilgi edinin" + }, + "telemetryConsentPrompt.optIn": { + "defaultMessage": "Evet, anonim kullanım verilerini paylaş" + }, + "telemetryConsentPrompt.optOut": { + "defaultMessage": "Hayır teşekkürler" + }, + "telemetrySettings.configErrorTitle": { + "defaultMessage": "Yapılandırma Hatası" + }, + "telemetrySettings.description": { + "defaultMessage": "Verilerinizin nasıl kullanıldığını kontrol edin" + }, + "telemetrySettings.learnMore": { + "defaultMessage": "Daha fazla bilgi edinin" + }, + "telemetrySettings.loadError": { + "defaultMessage": "Telemetri ayarları yüklenemedi." + }, + "telemetrySettings.title": { + "defaultMessage": "Gizlilik" + }, + "telemetrySettings.toggleDescription": { + "defaultMessage": "Anonim kullanım istatistiklerini paylaşarak goose'nin geliştirilmesine yardımcı olun." + }, + "telemetrySettings.toggleLabel": { + "defaultMessage": "Anonim kullanım verileri" + }, + "telemetrySettings.updateError": { + "defaultMessage": "Telemetri ayarları güncellenemedi." + }, + "themeSelector.dark": { + "defaultMessage": "Karanlık" + }, + "themeSelector.light": { + "defaultMessage": "Işık" + }, + "themeSelector.system": { + "defaultMessage": "Sistem" + }, + "themeSelector.theme": { + "defaultMessage": "Tema" + }, + "toolApprovalButtons.allowOnce": { + "defaultMessage": "Bir Kez İzin Ver" + }, + "toolApprovalButtons.allowedOnce": { + "defaultMessage": "Bir kez izin verildi" + }, + "toolApprovalButtons.alwaysAllow": { + "defaultMessage": "Her Zaman İzin Ver" + }, + "toolApprovalButtons.alwaysAllowed": { + "defaultMessage": "Her zaman izin verilir" + }, + "toolApprovalButtons.cancelled": { + "defaultMessage": "İptal edildi" + }, + "toolApprovalButtons.denied": { + "defaultMessage": "Reddedildi" + }, + "toolApprovalButtons.deniedOnce": { + "defaultMessage": "Bir kez reddedildi" + }, + "toolApprovalButtons.deny": { + "defaultMessage": "Reddet" + }, + "toolCallStatusIndicator.toolStatus": { + "defaultMessage": "Takım durumu: {status}" + }, + "toolCallWithResponse.activityCount": { + "defaultMessage": "Etkinlik ({count})" + }, + "toolCallWithResponse.code": { + "defaultMessage": "Kod" + }, + "toolCallWithResponse.loadingSpinner": { + "defaultMessage": "Döndürücü yükleniyor" + }, + "toolCallWithResponse.logs": { + "defaultMessage": "Günlükler" + }, + "toolCallWithResponse.mcpUiExperimental": { + "defaultMessage": "MCP Kullanıcı arayüzü deneyseldir ve herhangi bir zamanda değişebilir." + }, + "toolCallWithResponse.output": { + "defaultMessage": "Çıkış" + }, + "toolCallWithResponse.toolDetails": { + "defaultMessage": "Araç Ayrıntıları" + }, + "toolCallWithResponse.toolResultAlt": { + "defaultMessage": "Araç sonucu" + }, + "toolCallWithResponse.viewSubagentSession": { + "defaultMessage": "Alt temsilci oturumunu görüntüle" + }, + "toolConfirmation.allowToolCallWithName": { + "defaultMessage": "{toolName}'ye izin verilsin mi?" + }, + "toolConfirmation.gooseWouldLikeToCallWithName": { + "defaultMessage": "Goose, {toolName}'yi aramak istiyor. İzin vermek?" + }, + "tunnelSection.appStoreQrInstructions": { + "defaultMessage": "App Store'dan goose mobil uygulamasını yüklemek için bu QR kodunu iPhone kameranızla tarayın" + }, + "tunnelSection.close": { + "defaultMessage": "Kapat" + }, + "tunnelSection.connectionDetails": { + "defaultMessage": "Bağlantı Detayları" + }, + "tunnelSection.downloadIosApp": { + "defaultMessage": "goose iOS Uygulamasını İndirin" + }, + "tunnelSection.failedToLoadStatus": { + "defaultMessage": "Tünel durumu yüklenemedi" + }, + "tunnelSection.failedToStartTunnel": { + "defaultMessage": "Tünel başlatılamadı" + }, + "tunnelSection.failedToStopTunnel": { + "defaultMessage": "Tünel durdurulamadı" + }, + "tunnelSection.getIosApp": { + "defaultMessage": "iOS uygulamasını edinin" + }, + "tunnelSection.mobileApp": { + "defaultMessage": "Mobil Uygulama" + }, + "tunnelSection.mobileAppConnection": { + "defaultMessage": "Mobil Uygulama Bağlantısı" + }, + "tunnelSection.openInAppStore": { + "defaultMessage": "App Store'da aç" + }, + "tunnelSection.or": { + "defaultMessage": "veya" + }, + "tunnelSection.previewDescription": { + "defaultMessage": "Güvenli tünellemeyi kullanarak mobil cihazlardan goose'a uzaktan erişimi etkinleştirin." + }, + "tunnelSection.previewFeature": { + "defaultMessage": "Önizleme özelliği:" + }, + "tunnelSection.qrCodeInstructions": { + "defaultMessage": "Bu QR kodunu goose mobil uygulamasıyla tarayın. Bu kodu kişisel erişiminiz için olduğundan başkasıyla paylaşmayınız." + }, + "tunnelSection.retry": { + "defaultMessage": "Yeniden dene" + }, + "tunnelSection.scanQrCode": { + "defaultMessage": "QR kodunu tara" + }, + "tunnelSection.secretKey": { + "defaultMessage": "Gizli Anahtar" + }, + "tunnelSection.showQrCode": { + "defaultMessage": "QR Kodunu Göster" + }, + "tunnelSection.startTunnel": { + "defaultMessage": "Tüneli Başlat" + }, + "tunnelSection.starting": { + "defaultMessage": "Başlıyor..." + }, + "tunnelSection.statusDisabled": { + "defaultMessage": "Tünel devre dışı" + }, + "tunnelSection.statusError": { + "defaultMessage": "Tünel bir hatayla karşılaştı" + }, + "tunnelSection.statusIdle": { + "defaultMessage": "Tünel çalışmıyor" + }, + "tunnelSection.statusRunning": { + "defaultMessage": "Tünel aktif" + }, + "tunnelSection.statusStarting": { + "defaultMessage": "Tünel başlatılıyor..." + }, + "tunnelSection.stopTunnel": { + "defaultMessage": "Tüneli Durdur" + }, + "tunnelSection.tunnelStatus": { + "defaultMessage": "Tünel Durumu" + }, + "tunnelSection.tunnelUrl": { + "defaultMessage": "Tünel URL'si" + }, + "tunnelSection.url": { + "defaultMessage": "URL'si:" + }, + "updateSection.autoDownload": { + "defaultMessage": "Güncelleme arka planda otomatik olarak indirilecektir." + }, + "updateSection.autoInstallNote": { + "defaultMessage": "Uygulamadan çıktığınızda güncelleme otomatik olarak yüklenecektir." + }, + "updateSection.checkForUpdates": { + "defaultMessage": "Güncellemeleri Kontrol Et" + }, + "updateSection.checking": { + "defaultMessage": "Güncellemeler kontrol ediliyor..." + }, + "updateSection.currentVersion": { + "defaultMessage": "Güncel sürüm" + }, + "updateSection.downloadReady": { + "defaultMessage": "Güncelleme indirildi ve kuruluma hazır!" + }, + "updateSection.downloadingProgress": { + "defaultMessage": "Güncelleme indiriliyor... {percent}%" + }, + "updateSection.downloadingUpdate": { + "defaultMessage": "Güncelleme indiriliyor..." + }, + "updateSection.installAndRestart": { + "defaultMessage": "Yükle ve Yeniden Başlat" + }, + "updateSection.installNowHint": { + "defaultMessage": "Veya şimdi güncellemek için \"Yükle ve Yeniden Başlat\"ı tıklayın." + }, + "updateSection.latestVersion": { + "defaultMessage": "En son sürümü çalıştırıyorsunuz!" + }, + "updateSection.loading": { + "defaultMessage": "Yükleniyor..." + }, + "updateSection.manualInstallNote": { + "defaultMessage": "İndirdikten sonra güncellemeyi manuel olarak yüklemeniz gerekecektir." + }, + "updateSection.manualInstallRequired": { + "defaultMessage": "Bu güncelleme yöntemi için manuel kurulum gereklidir." + }, + "updateSection.readyInstallAuto": { + "defaultMessage": "✓ Güncelleme hazır! Goose'den çıktığınızda yüklenecektir." + }, + "updateSection.readyInstallManual": { + "defaultMessage": "✓ Güncelleme hazır! Kurulum talimatları için \"Yükle ve Yeniden Başlat\"a tıklayın." + }, + "updateSection.upToDate": { + "defaultMessage": "(güncel)" + }, + "updateSection.updateAvailable": { + "defaultMessage": "Güncelleme mevcut!" + }, + "updateSection.versionAvailable": { + "defaultMessage": "→ {version} mevcut" + }, + "updateSection.versionIsAvailable": { + "defaultMessage": "Sürüm {version} mevcut" + }, + "userMessage.cancel": { + "defaultMessage": "İptal" + }, + "userMessage.cancelAriaLabel": { + "defaultMessage": "Düzenlemeyi iptal et" + }, + "userMessage.editAriaLabel": { + "defaultMessage": "Mesaj içeriğini düzenle" + }, + "userMessage.editButton": { + "defaultMessage": "Düzenle" + }, + "userMessage.editInPlace": { + "defaultMessage": "Yerinde Düzenle" + }, + "userMessage.editInPlaceAriaLabel": { + "defaultMessage": "Mesajı yerinde düzenleyin" + }, + "userMessage.editInPlaceDescription": { + "defaultMessage": "Edit in Place bu oturumu günceller • Fork Session yeni bir oturum oluşturur" + }, + "userMessage.editInPlaceTitle": { + "defaultMessage": "Bu oturumdaki mesajı güncelleyin" + }, + "userMessage.editMessageAriaLabel": { + "defaultMessage": "Mesajı düzenle: {preview}" + }, + "userMessage.editMessageTitle": { + "defaultMessage": "Mesajı düzenle" + }, + "userMessage.editPlaceholder": { + "defaultMessage": "Mesajınızı düzenleyin..." + }, + "userMessage.emptyError": { + "defaultMessage": "Mesaj boş olamaz" + }, + "userMessage.forkSession": { + "defaultMessage": "Çatal Oturumu" + }, + "userMessage.forkSessionAriaLabel": { + "defaultMessage": "Düzenlenmiş mesajla oturumu çatallayın" + }, + "userMessage.forkSessionTitle": { + "defaultMessage": "Düzenlenen mesajla yeni bir oturum oluşturun" + } +} From 27dc0d5f83c5e47c87bdf6f66ce6ff23138900c8 Mon Sep 17 00:00:00 2001 From: jh-block Date: Tue, 26 May 2026 18:09:03 +0200 Subject: [PATCH 10/66] Improve dependency hygiene (#9360) Signed-off-by: jh-block --- Cargo.lock | 2011 +++++------------ Cargo.toml | 168 +- Justfile | 2 +- crates/goose-acp-macros/Cargo.toml | 4 +- crates/goose-cli/Cargo.toml | 43 +- crates/goose-cli/src/cli.rs | 3 + crates/goose-cli/src/commands/mod.rs | 1 + crates/goose-cli/src/commands/session.rs | 19 +- crates/goose-mcp/Cargo.toml | 13 +- crates/goose-server/Cargo.toml | 34 +- crates/goose-server/src/routes/agent.rs | 2 + crates/goose-server/src/routes/session.rs | 65 +- crates/goose-server/src/routes/telemetry.rs | 20 +- crates/goose-server/src/state.rs | 4 +- crates/goose/Cargo.toml | 158 +- crates/goose/src/lib.rs | 3 - crates/goose/src/otel/otlp.rs | 4 +- crates/goose/src/providers/api_client.rs | 26 +- crates/goose/src/providers/bedrock.rs | 4 +- crates/goose/src/providers/chatgpt_codex.rs | 2 +- crates/goose/src/providers/gemini_oauth.rs | 2 +- crates/goose/src/providers/sagemaker_tgi.rs | 6 +- crates/goose/src/session/mod.rs | 1 + .../tests/session_id_propagation_test.rs | 6 +- 24 files changed, 956 insertions(+), 1645 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index ba22bd09efe8..d528758ef4e8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -40,23 +40,22 @@ dependencies = [ [[package]] name = "agent-client-protocol" -version = "0.12.1" +version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4361ba6627e51de955b10f3c77fb9eb959c85191a236c1c2c84e32f4ff240faf" +checksum = "2af62fb84df2af0f933d8f5fd78b843fa5eb0ec5a48fa1b528c41951d0bbe36c" dependencies = [ "agent-client-protocol-derive", "agent-client-protocol-schema", - "async-process", - "blocking", + "anyhow", "futures", "futures-concurrency", "jsonrpcmsg", "rmcp", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "schemars 1.2.1", "serde", "serde_json", - "shell-words", + "thiserror 2.0.18", "tokio", "tokio-util", "tracing", @@ -75,9 +74,9 @@ dependencies = [ [[package]] name = "agent-client-protocol-schema" -version = "0.13.2" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b957d8391ac3933e2a940446171c508d2b8ffc386d8fa7d0b9c936a2575b463e" +checksum = "49bae57dad1c28a362fbdcf7bab0583316a02b45a70792109fced55780a3b63c" dependencies = [ "anyhow", "derive_more", @@ -112,24 +111,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "aligned" -version = "0.4.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee4508988c62edf04abd8d92897fca0c2995d907ce1dfeaf369dac3716a40685" -dependencies = [ - "as-slice", -] - -[[package]] -name = "aligned-vec" -version = "0.6.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc890384c8602f339876ded803c97ad529f3842aba97f6392b3dba0dd171769b" -dependencies = [ - "equator", -] - [[package]] name = "alloc-no-stdlib" version = "2.0.4" @@ -250,12 +231,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" dependencies = [ "clipboard-win", - "image", "log", "objc2", "objc2-app-kit", - "objc2-core-foundation", - "objc2-core-graphics", "objc2-foundation", "parking_lot", "percent-encoding", @@ -265,30 +243,13 @@ dependencies = [ [[package]] name = "arc-swap" -version = "1.9.0" +version = "1.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a07d1f37ff60921c83bdfc7407723bdefe89b44b98a9b772f225c8f9d67141a6" +checksum = "6a3a1fd6f75306b68087b831f025c712524bcb19aad54e557b1129cfa0a2b207" dependencies = [ "rustversion", ] -[[package]] -name = "arg_enum_proc_macro" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0ae92a5119aa49cdbcf6b9f893fe4e1d98b04ccbf82ee0584ad948a44a734dea" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "arraydeque" -version = "0.5.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7d902e3d592a523def97af8f317b08ce16b7ab854c1985a0c671e6f15cebc236" - [[package]] name = "arrayref" version = "0.3.9" @@ -301,15 +262,6 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7c02d123df017efcdfbd739ef81735b36c5ba83ec3c59c80a9d7ecc718f92e50" -[[package]] -name = "as-slice" -version = "0.2.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "516b6b4f0e40d50dcda9365d53964ec74560ad4284da2e7fc97122cd83174516" -dependencies = [ - "stable_deref_trait", -] - [[package]] name = "ascii" version = "1.1.0" @@ -318,9 +270,9 @@ checksum = "d92bec98840b8f03a5ff5413de5293bfcd8bf96467cf5452609f939ec6f5de16" [[package]] name = "asn1-rs" -version = "0.7.1" +version = "0.7.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "56624a96882bb8c26d61312ae18cb45868e5a9992ea73c58e45c3101e56a1e60" +checksum = "b7f43a50ac4fdca5df8e885c21b835997f0a1cdee65494a6847694a98652d9d8" dependencies = [ "asn1-rs-derive", "asn1-rs-impl", @@ -376,23 +328,11 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "async-channel" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "924ed96dd52d1b75e9c1a3e6275715fd320f5f9439fb5a4a11fa51f4221158d2" -dependencies = [ - "concurrent-queue", - "event-listener-strategy", - "futures-core", - "pin-project-lite", -] - [[package]] name = "async-compression" -version = "0.4.41" +version = "0.4.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d0f9ee0f6e02ffd7ad5816e9464499fba7b3effd01123b515c41d1697c43dad1" +checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" dependencies = [ "compression-codecs", "compression-core", @@ -400,77 +340,12 @@ dependencies = [ "tokio", ] -[[package]] -name = "async-io" -version = "2.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "456b8a8feb6f42d237746d4b3e9a178494627745c3c56c6ea55d92ba50d026fc" -dependencies = [ - "autocfg", - "cfg-if", - "concurrent-queue", - "futures-io", - "futures-lite", - "parking", - "polling", - "rustix 1.1.4", - "slab", - "windows-sys 0.61.2", -] - -[[package]] -name = "async-lock" -version = "3.4.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "290f7f2596bd5b78a9fec8088ccd89180d7f9f55b94b0576823bbbdc72ee8311" -dependencies = [ - "event-listener", - "event-listener-strategy", - "pin-project-lite", -] - [[package]] name = "async-once-cell" version = "0.5.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4288f83726785267c6f2ef073a3d83dc3f9b81464e9f99898240cced85fce35a" -[[package]] -name = "async-process" -version = "2.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fc50921ec0055cdd8a16de48773bfeec5c972598674347252c0399676be7da75" -dependencies = [ - "async-channel", - "async-io", - "async-lock", - "async-signal", - "async-task", - "blocking", - "cfg-if", - "event-listener", - "futures-lite", - "rustix 1.1.4", -] - -[[package]] -name = "async-signal" -version = "0.2.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52b5aaafa020cf5053a01f2a60e8ff5dccf550f0f77ec54a4e47285ac2bab485" -dependencies = [ - "async-io", - "async-lock", - "atomic-waker", - "cfg-if", - "futures-core", - "futures-io", - "rustix 1.1.4", - "signal-hook-registry", - "slab", - "windows-sys 0.61.2", -] - [[package]] name = "async-stream" version = "0.3.6" @@ -493,12 +368,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "async-task" -version = "4.7.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" - [[package]] name = "async-trait" version = "0.1.89" @@ -564,58 +433,15 @@ checksum = "1505bd5d3d116872e7271a6d4e16d81d0c8570876c8de68093a09ac269d8aac0" [[package]] name = "autocfg" -version = "1.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" - -[[package]] -name = "av-scenechange" -version = "0.14.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f321d77c20e19b92c39e7471cf986812cbb46659d2af674adc4331ef3f18394" -dependencies = [ - "aligned", - "anyhow", - "arg_enum_proc_macro", - "arrayvec", - "log", - "num-rational", - "num-traits", - "pastey 0.1.1", - "rayon", - "thiserror 2.0.18", - "v_frame", - "y4m", -] - -[[package]] -name = "av1-grain" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8cfddb07216410377231960af4fcab838eaa12e013417781b78bd95ee22077f8" -dependencies = [ - "anyhow", - "arrayvec", - "log", - "nom 8.0.0", - "num-rational", - "v_frame", -] - -[[package]] -name = "avif-serialize" -version = "0.8.9" +version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e7178fe5f7d460b13895ebb9dcb28a3a6216d2df2574a0806cb51b555d297f38" -dependencies = [ - "arrayvec", -] +checksum = "f2032f911046de80f0a198e0901378627c33f59ea0ac00e363d481118bd70a53" [[package]] name = "aws-config" -version = "1.8.16" +version = "1.8.17" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50f156acdd2cf55f5aa53ee416c4ac851cf1222694506c0b1f78c85695e9ca9d" +checksum = "517aa062d8bd9015ee23d6daa5e1c1372328412fdae4e6c4c1be9b69c6ad37a2" dependencies = [ "aws-credential-types", "aws-runtime", @@ -627,12 +453,13 @@ dependencies = [ "aws-smithy-json", "aws-smithy-runtime", "aws-smithy-runtime-api", + "aws-smithy-schema", "aws-smithy-types", "aws-types", "bytes", "fastrand", "hex", - "http 1.4.0", + "http 1.4.1", "sha1", "time", "tokio", @@ -694,7 +521,7 @@ dependencies = [ "bytes", "bytes-utils", "fastrand", - "http 1.4.0", + "http 1.4.1", "http-body 1.0.1", "percent-encoding", "pin-project-lite", @@ -723,7 +550,7 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", - "http 1.4.0", + "http 1.4.1", "http-body-util", "regex-lite", "tracing", @@ -749,16 +576,16 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", - "http 1.4.0", + "http 1.4.1", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-sso" -version = "1.98.0" +version = "1.99.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d69c77aafa20460c68b6b3213c84f6423b6e76dbf89accd3e1789a686ffd9489" +checksum = "9f4055e6099b2ec264abdc0d9bbfffce306c1601809275c861594779a0b04b45" dependencies = [ "aws-credential-types", "aws-runtime", @@ -773,16 +600,16 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", - "http 1.4.0", + "http 1.4.1", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-ssooidc" -version = "1.100.0" +version = "1.101.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1c7e7b09346d5ca22a2a08267555843a6a0127fb20d8964cb6ecfb8fdb190225" +checksum = "02f009ba0284c5d696425fd7b4dcc5b189f5726f4041b7a5794daecb3a68d598" dependencies = [ "aws-credential-types", "aws-runtime", @@ -797,16 +624,16 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", - "http 1.4.0", + "http 1.4.1", "regex-lite", "tracing", ] [[package]] name = "aws-sdk-sts" -version = "1.103.0" +version = "1.104.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c2249b81a2e73a8027c41c378463a81ec39b8510f184f2caab87de912af0f49b" +checksum = "6aa6622798e19e6a76b690562085dd4771c736cd48343464a53ab4ae2f2c9f84" dependencies = [ "aws-credential-types", "aws-runtime", @@ -822,7 +649,7 @@ dependencies = [ "aws-types", "fastrand", "http 0.2.12", - "http 1.4.0", + "http 1.4.1", "regex-lite", "tracing", ] @@ -843,7 +670,7 @@ dependencies = [ "hex", "hmac 0.13.0", "http 0.2.12", - "http 1.4.0", + "http 1.4.1", "percent-encoding", "sha2 0.11.0", "time", @@ -885,7 +712,7 @@ dependencies = [ "bytes-utils", "futures-core", "futures-util", - "http 1.4.0", + "http 1.4.1", "http-body 1.0.1", "http-body-util", "percent-encoding", @@ -894,30 +721,6 @@ dependencies = [ "tracing", ] -[[package]] -name = "aws-smithy-http-client" -version = "1.1.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a2f165a7feee6f263028b899d0a181987f4fa7179a6411a32a439fba7c5f769" -dependencies = [ - "aws-smithy-async", - "aws-smithy-runtime-api", - "aws-smithy-types", - "h2", - "http 1.4.0", - "hyper", - "hyper-rustls", - "hyper-util", - "pin-project-lite", - "rustls", - "rustls-native-certs", - "rustls-pki-types", - "tokio", - "tokio-rustls", - "tower", - "tracing", -] - [[package]] name = "aws-smithy-json" version = "0.62.6" @@ -956,7 +759,6 @@ checksum = "b8e6f5caf6fea86f8c2206541ab5857cfcda9013426cdbe8fa0098b9e2d32182" dependencies = [ "aws-smithy-async", "aws-smithy-http", - "aws-smithy-http-client", "aws-smithy-observability", "aws-smithy-runtime-api", "aws-smithy-schema", @@ -964,7 +766,7 @@ dependencies = [ "bytes", "fastrand", "http 0.2.12", - "http 1.4.0", + "http 1.4.1", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -985,7 +787,7 @@ dependencies = [ "aws-smithy-types", "bytes", "http 0.2.12", - "http 1.4.0", + "http 1.4.1", "pin-project-lite", "tokio", "tracing", @@ -1011,7 +813,7 @@ checksum = "7442cb268338f0eb8278140a107c046756aa01093d8ef5e99628d34ae09c94f5" dependencies = [ "aws-smithy-runtime-api", "aws-smithy-types", - "http 1.4.0", + "http 1.4.1", ] [[package]] @@ -1025,7 +827,7 @@ dependencies = [ "bytes-utils", "futures-core", "http 0.2.12", - "http 1.4.0", + "http 1.4.1", "http-body 0.4.6", "http-body 1.0.1", "http-body-util", @@ -1076,7 +878,7 @@ dependencies = [ "bytes", "form_urlencoded", "futures-util", - "http 1.4.0", + "http 1.4.1", "http-body 1.0.1", "http-body-util", "hyper", @@ -1098,7 +900,6 @@ dependencies = [ "tower", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -1109,7 +910,7 @@ checksum = "08c78f31d7b1291f7ee735c1c6780ccde7785daae9a9206026862dab7d8792d1" dependencies = [ "bytes", "futures-core", - "http 1.4.0", + "http 1.4.1", "http-body 1.0.1", "http-body-util", "mime", @@ -1117,7 +918,6 @@ dependencies = [ "sync_wrapper", "tower-layer", "tower-service", - "tracing", ] [[package]] @@ -1141,7 +941,7 @@ dependencies = [ "bytes", "either", "fs-err", - "http 1.4.0", + "http 1.4.1", "http-body 1.0.1", "hyper", "hyper-util", @@ -1294,7 +1094,7 @@ version = "0.72.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "993776b509cfb49c750f11b8f07a46fa23e0a1386ffc01fb1e7d343efc387895" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "cexpr", "clang-sys", "itertools 0.13.0", @@ -1303,8 +1103,8 @@ dependencies = [ "proc-macro2", "quote", "regex", - "rustc-hash 2.1.1", - "shlex 1.3.0", + "rustc-hash 2.1.2", + "shlex", "syn 2.0.117", ] @@ -1334,7 +1134,7 @@ dependencies = [ "biome_json_parser", "biome_json_syntax", "biome_rowan", - "bitflags 2.11.0", + "bitflags 2.11.1", "indexmap 1.9.3", "serde", "serde_json", @@ -1354,7 +1154,7 @@ dependencies = [ "biome_rowan", "biome_text_edit", "biome_text_size", - "bitflags 2.11.0", + "bitflags 2.11.1", "bpaf", "serde", "termcolor", @@ -1447,7 +1247,7 @@ dependencies = [ "biome_js_unicode_table", "biome_parser", "biome_rowan", - "bitflags 2.11.0", + "bitflags 2.11.1", "cfg-if", "drop_bomb", "indexmap 1.9.3", @@ -1532,7 +1332,7 @@ dependencies = [ "biome_console", "biome_diagnostics", "biome_rowan", - "bitflags 2.11.0", + "bitflags 2.11.1", "drop_bomb", ] @@ -1637,22 +1437,13 @@ checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" [[package]] name = "bitflags" -version = "2.11.0" +version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" +checksum = "c4512299f36f043ab09a583e57bceb5a5aab7a73db1805848e8fef3c9e8c78b3" dependencies = [ "serde_core", ] -[[package]] -name = "bitstream-io" -version = "4.10.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7eff00be299a18769011411c9def0d827e8f2d7bf0c3dbf53633147a8867fd1f" -dependencies = [ - "no_std_io2", -] - [[package]] name = "bitvec" version = "1.0.1" @@ -1721,19 +1512,6 @@ dependencies = [ "objc2", ] -[[package]] -name = "blocking" -version = "1.6.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e83f8d02be6967315521be875afa792a316e28d57b5a2d401897e2a7921b7f21" -dependencies = [ - "async-channel", - "async-task", - "futures-io", - "futures-lite", - "piper", -] - [[package]] name = "borrow-or-share" version = "0.2.4" @@ -1752,18 +1530,18 @@ dependencies = [ [[package]] name = "bpaf" -version = "0.9.24" +version = "0.9.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2435ff2f08be8436bdcd06a3de2bd7696fd10e45eb630ecfc09af7fbfa3e69a" +checksum = "0b86829876e7e200161a5aa6ea688d46c32d64d70ee3100127790dde84688d6e" dependencies = [ "bpaf_derive", ] [[package]] name = "bpaf_derive" -version = "0.5.23" +version = "0.5.26" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "549ca9c364fdc06f9f36d1356980193d930abc80db848193c2dd4d0e9a83de1b" +checksum = "2f7e98cee839b19076cb3ce1afdb62bb182e04ff5f71f70188827002fae91094" dependencies = [ "proc-macro2", "quote", @@ -1811,17 +1589,11 @@ dependencies = [ "serde", ] -[[package]] -name = "built" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c0e531d93d39c34eef561e929e8a7f86d77a5af08aac4f6d6e39976c51858e9" - [[package]] name = "bumpalo" -version = "3.20.2" +version = "3.20.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" +checksum = "72f5acc6cb2ba439de613abc23857ec3d78374d8ed5ac84e9d11336e87da8649" dependencies = [ "allocator-api2", ] @@ -1913,9 +1685,9 @@ checksum = "1bf2a5fb3207c12b5d208ebc145f967fea5cac41a021c37417ccc31ba40f39ee" [[package]] name = "calendrical_calculations" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a0b39595c6ee54a8d0900204ba4c401d0ab4eb45adaf07178e8d017541529e7" +checksum = "5abbd6eeda6885048d357edc66748eea6e0268e3dd11f326fff5bd248d779c26" dependencies = [ "core_maths", "displaydoc", @@ -1940,7 +1712,7 @@ dependencies = [ "candle-kernels", "candle-metal-kernels", "candle-ug", - "cudarc 0.19.4", + "cudarc 0.19.7", "float8", "gemm 0.19.0", "half", @@ -1956,7 +1728,7 @@ dependencies = [ "safetensors 0.7.0", "thiserror 2.0.18", "tokenizers 0.22.2", - "yoke 0.8.1", + "yoke 0.8.2", "zip 7.2.0", ] @@ -2074,22 +1846,16 @@ dependencies = [ [[package]] name = "cc" -version = "1.2.61" +version = "1.2.62" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d16d90359e986641506914ba71350897565610e87ce0ad9e6f28569db3dd5c6d" +checksum = "a1dce859f0832a7d088c4f1119888ab94ef4b5d6795d1ce05afb7fe159d79f98" dependencies = [ "find-msvc-tools", "jobserver", "libc", - "shlex 1.3.0", + "shlex", ] -[[package]] -name = "cesu8" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" - [[package]] name = "cexpr" version = "0.6.0" @@ -2141,7 +1907,7 @@ checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601" dependencies = [ "cfg-if", "cpufeatures 0.3.0", - "rand_core 0.10.0", + "rand_core 0.10.1", ] [[package]] @@ -2264,9 +2030,9 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9" [[package]] name = "clap_mangen" -version = "0.3.0" +version = "0.2.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d82842b45bf9f6a3be090dd860095ac30728042c08e0d6261ca7259b5d850f07" +checksum = "7e30ffc187e2e3aeafcd1c6e2aa416e29739454c0ccaa419226d5ecd181f2d78" dependencies = [ "clap", "roff", @@ -2307,9 +2073,9 @@ dependencies = [ [[package]] name = "cmake" -version = "0.1.57" +version = "0.1.58" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75443c44cd6b379beb8c5b45d85d0773baf31cce901fe7bb252f4eff3008ef7d" +checksum = "c0f78a02292a74a88ac736019ab962ece0bc380e3f977bf72e376c5d78ff0678" dependencies = [ "cc", ] @@ -2372,7 +2138,6 @@ version = "7.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "958c5d6ecf1f214b4c2bbbbf6ab9523a864bd136dcf71a7e8904799acfe1ad47" dependencies = [ - "crossterm", "unicode-segmentation", "unicode-width 0.2.2", ] @@ -2407,9 +2172,9 @@ dependencies = [ [[package]] name = "compression-codecs" -version = "0.4.37" +version = "0.4.38" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb7b51a7d9c967fc26773061ba86150f19c50c0d65c887cb1fbe295fd16619b7" +checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" dependencies = [ "brotli", "compression-core", @@ -2421,9 +2186,9 @@ dependencies = [ [[package]] name = "compression-core" -version = "0.4.31" +version = "0.4.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75984efb6ed102a0d42db99afb6c1948f0380d1d91808d5529916e6c08b49d8d" +checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" [[package]] name = "concurrent-queue" @@ -2440,18 +2205,10 @@ version = "0.15.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f316c6237b2d38be61949ecd15268a4c6ca32570079394a2444d9ce2c72a72d8" dependencies = [ - "async-trait", - "convert_case 0.6.0", - "json5", "pathdiff", - "ron", - "rust-ini", - "serde-untagged", "serde_core", - "serde_json", - "toml 1.1.0+spec-1.1.0", - "winnow 1.0.0", - "yaml-rust2", + "toml 1.1.2+spec-1.1.0", + "winnow 1.0.3", ] [[package]] @@ -2478,26 +2235,6 @@ version = "0.10.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c" -[[package]] -name = "const-random" -version = "0.1.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87e00182fe74b066627d63b85fd550ac2998d4b0bd86bfed477a0ae4c7c71359" -dependencies = [ - "const-random-macro", -] - -[[package]] -name = "const-random-macro" -version = "0.1.16" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9d839f2a20b0aee515dc581a6172f2321f96cab76c1a38a4c584a194955390e" -dependencies = [ - "getrandom 0.2.17", - "once_cell", - "tiny-keccak", -] - [[package]] name = "constant_time_eq" version = "0.4.2" @@ -2513,15 +2250,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "convert_case" -version = "0.6.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ec182b0ca2f35d8fc196cf3404988fd8b8c739a4d270ff118a398feb0cbec1ca" -dependencies = [ - "unicode-segmentation", -] - [[package]] name = "convert_case" version = "0.10.0" @@ -2647,9 +2375,9 @@ dependencies = [ [[package]] name = "crc-catalog" -version = "2.4.0" +version = "2.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" [[package]] name = "crc32fast" @@ -2726,29 +2454,6 @@ version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" -[[package]] -name = "crossterm" -version = "0.29.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d8b9f2e4c67f833b660cdb0a3523065869fb35570177239812ed4c905aeff87b" -dependencies = [ - "bitflags 2.11.0", - "crossterm_winapi", - "document-features", - "parking_lot", - "rustix 1.1.4", - "winapi", -] - -[[package]] -name = "crossterm_winapi" -version = "0.9.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b" -dependencies = [ - "winapi", -] - [[package]] name = "crunchy" version = "0.2.4" @@ -2780,21 +2485,21 @@ dependencies = [ [[package]] name = "crypto-common" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "77727bb15fa921304124b128af125e7e3b968275d1b108b379190264f4423710" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" dependencies = [ "hybrid-array", ] [[package]] name = "ctor" -version = "1.0.6" +version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d765eb1c0bda10d31e0ea185f5ee15da532d60b0912d2bd1441783439e749c5" +checksum = "32a2785755761f3ddc1492979ce1e48d2c00d09311c39e4466429188f3dd6501" dependencies = [ - "link-section", - "linktime-proc-macro", + "quote", + "syn 2.0.117", ] [[package]] @@ -2836,9 +2541,9 @@ dependencies = [ [[package]] name = "cudarc" -version = "0.19.4" +version = "0.19.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f071cd6a7b5d51607df76aa2d426aaabc7a74bc6bdb885b8afa63a880572ad9b" +checksum = "1cea5f10a99e025c1b44ae2354c2d8326b25ddbd0baf76bde8e55cfd4018a2cc" dependencies = [ "float8", "half", @@ -2943,9 +2648,9 @@ dependencies = [ [[package]] name = "dary_heap" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "06d2e3287df1c007e74221c49ca10a95d557349e54b3a75dc2fb14712c751f04" +checksum = "8b1e3a325bc115f096c8b77bbf027a7c2592230e70be2d985be950d3d5e60ebe" dependencies = [ "serde", ] @@ -2979,9 +2684,9 @@ dependencies = [ [[package]] name = "data-encoding" -version = "2.10.0" +version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d7a1e2f27636f116493b8b860f5546edb47c8d8f8ea73e1d2a20be88e28d1fea" +checksum = "a4ae5f15dda3c708c0ade84bfee31ccab44a3da4f88015ed22f63732abe300c8" [[package]] name = "data-url" @@ -2991,13 +2696,13 @@ checksum = "be1e0bca6c3637f992fc1cc7cbc52a78c1ef6db076dbf1059c4323d6a2048376" [[package]] name = "dbus" -version = "0.9.10" +version = "0.9.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21b3aa68d7e7abee336255bd7248ea965cc393f3e70411135a6f6a4b651345d4" +checksum = "b942602992bb7acfd1f51c49811c58a610ef9181b6e66f3e519d79b540a3bf73" dependencies = [ "libc", "libdbus-sys", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -3097,7 +2802,7 @@ dependencies = [ "deno_error", "deno_media_type", "deno_path_util", - "http 1.4.0", + "http 1.4.1", "indexmap 2.14.0", "log", "once_cell", @@ -3494,7 +3199,7 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ - "convert_case 0.10.0", + "convert_case", "proc-macro2", "quote", "rustc_version", @@ -3528,7 +3233,7 @@ checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" dependencies = [ "block-buffer 0.12.0", "const-oid 0.10.2", - "crypto-common 0.2.1", + "crypto-common 0.2.2", "ctutils", ] @@ -3612,7 +3317,7 @@ version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e0e367e4e7da84520dedcac1901e4da967309406d1e51017ae1abfb97adbd38" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "objc2", ] @@ -3633,15 +3338,6 @@ version = "1.0.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "59f8e79d1fbf76bdfbde321e902714bf6c49df88a7dda6fc682fc2979226962d" -[[package]] -name = "dlv-list" -version = "0.5.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "442039f5147480ba31067cb00ada1adae6892028e40e45fc5de7b7df6dcc1b5f" -dependencies = [ - "const-random", -] - [[package]] name = "doc-comment" version = "0.3.4" @@ -3664,7 +3360,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed73cbf5e1c37baa23f4132569ac1187829f03922c206bd68fe109e3001a343d" dependencies = [ "base64 0.22.1", - "image", + "image 0.25.10", "quick-xml 0.36.2", "serde", "serde_json", @@ -3691,7 +3387,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33175ddb7a6d418589cab2966bd14a710b3b1139459d3d5ca9edf783c4833f4c" dependencies = [ "num-bigint", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "swc_atoms", "swc_common", "swc_ecma_ast", @@ -3801,9 +3497,9 @@ dependencies = [ [[package]] name = "either" -version = "1.15.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +checksum = "91622ff5e7162018101f2fea40d6ebf4a78bbe5a49736a2020649edf9693679e" dependencies = [ "serde", ] @@ -3904,50 +3600,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c7f84e12ccf0a7ddc17a6c41c93326024c42920d7ee630d04950e6926645c0fe" [[package]] -name = "equator" -version = "0.4.2" +name = "equivalent" +version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4711b213838dfee0117e3be6ac926007d7f433d7bbe33595975d4190cb07e6fc" -dependencies = [ - "equator-macro", -] +checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] -name = "equator-macro" -version = "0.4.2" +name = "errno" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44f23cf4b44bfce11a86ace86f8a73ffdec849c9fd00a386a53d278bd9e81fb3" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - -[[package]] -name = "equivalent" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" - -[[package]] -name = "erased-serde" -version = "0.4.10" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2add8a07dd6a8d93ff627029c51de145e12686fbc36ecb298ac22e74cf02dec" -dependencies = [ - "serde", - "serde_core", - "typeid", -] - -[[package]] -name = "errno" -version = "0.3.14" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" -dependencies = [ - "libc", - "windows-sys 0.61.2", + "libc", + "windows-sys 0.61.2", ] [[package]] @@ -3994,16 +3659,6 @@ dependencies = [ "pin-project-lite", ] -[[package]] -name = "event-listener-strategy" -version = "0.5.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8be9f3dfaaffdae2972880079a491a1a8bb7cbed0b8dd7a347f668b4150a3b93" -dependencies = [ - "event-listener", - "pin-project-lite", -] - [[package]] name = "exr" version = "1.74.0" @@ -4049,29 +3704,15 @@ dependencies = [ [[package]] name = "fastrand" -version = "2.3.0" +version = "2.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "37909eebbb50d72f9059c3b6d82c0463f2ff062c9e95845c43a6c9c0355411be" +checksum = "9f1f227452a390804cdb637b74a86990f2a7d7ba4b7d5693aac9b4dd6defd8d6" [[package]] name = "fax" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f05de7d48f37cd6730705cbca900770cab77a89f413d23e100ad7fad7795a0ab" -dependencies = [ - "fax_derive", -] - -[[package]] -name = "fax_derive" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0aca10fb742cb43f9e7bb8467c91aa9bcb8e3ffbc6a6f7389bb93ffc920577d" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] +checksum = "caf1079563223d5d59d83c85886a56e586cfd5c1a26292e971a0fa266531ac5a" [[package]] name = "fdeflate" @@ -4100,13 +3741,12 @@ checksum = "28dea519a9695b9977216879a3ebfddf92f1c08c05d984f8996aecd6ecdc811d" [[package]] name = "filetime" -version = "0.2.27" +version = "0.2.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f98844151eee8917efc50bd9e8318cb963ae8b297431495d3f758616ea5c57db" +checksum = "5c287a33c7f0a620c38e641e7f60827713987b3c0f26e8ddc9462cc69cf75759" dependencies = [ "cfg-if", "libc", - "libredox", ] [[package]] @@ -4252,9 +3892,9 @@ dependencies = [ [[package]] name = "fraction" -version = "0.15.3" +version = "0.15.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0f158e3ff0a1b334408dc9fb811cd99b446986f4d8b741bb08f9df1604085ae7" +checksum = "e076045bb43dac435333ed5f04caf35c7463631d0dae2deb2638d94dd0a5b872" dependencies = [ "lazy_static", "num", @@ -4262,9 +3902,12 @@ dependencies = [ [[package]] name = "fragile" -version = "2.0.1" +version = "2.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "28dd6caf6059519a65843af8fe2a3ae298b14b80179855aeb4adc2c1934ee619" +checksum = "8878864ba14bb86e818a412bfd6f18f9eabd4ec0f008a28e8f7eb61db532fcf9" +dependencies = [ + "futures-core", +] [[package]] name = "from_variant" @@ -4702,15 +4345,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "getopts" -version = "0.2.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df" -dependencies = [ - "unicode-width 0.2.2", -] - [[package]] name = "getrandom" version = "0.2.17" @@ -4747,16 +4381,26 @@ dependencies = [ "cfg-if", "libc", "r-efi 6.0.0", - "rand_core 0.10.0", + "rand_core 0.10.1", "wasip2", "wasip3", ] [[package]] name = "gif" -version = "0.14.1" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4ae047235e33e2829703574b54fdec96bfbad892062d97fed2f76022287de61b" +dependencies = [ + "color_quant", + "weezl", +] + +[[package]] +name = "gif" +version = "0.14.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f5df2ba84018d80c213569363bdcd0c64e6933c67fe4c1d60ecf822971a3c35e" +checksum = "ee8cfcc411d9adbbaba82fb72661cc1bcca13e8bba98b364e62b2dba8f960159" dependencies = [ "color_quant", "weezl", @@ -4839,8 +4483,10 @@ dependencies = [ "goose-mcp", "goose-sdk", "goose-test-support", - "http 1.4.0", + "http 1.4.1", "http-body-util", + "icu_calendar", + "icu_locale", "ignore", "include_dir", "indexmap 2.14.0", @@ -4851,7 +4497,7 @@ dependencies = [ "keyring", "libc", "llama-cpp-2", - "lru 0.18.0", + "lru", "minijinja", "mockall", "nanoid", @@ -4859,22 +4505,22 @@ dependencies = [ "nostr-sdk", "oauth2", "once_cell", - "opentelemetry", + "opentelemetry 0.32.0", "opentelemetry-appender-tracing", - "opentelemetry-otlp", + "opentelemetry-otlp 0.32.0", "opentelemetry-stdout", - "opentelemetry_sdk", - "pastey 0.2.3", + "opentelemetry_sdk 0.32.0", + "pastey", "pctx_code_mode", "pem", "pkcs1", "pkcs8", "process-wrap", "pulldown-cmark", - "rand 0.8.5", + "rand 0.8.6", "rayon", "regex", - "reqwest 0.13.3", + "reqwest 0.13.4", "rmcp", "rubato", "rustls", @@ -4888,8 +4534,9 @@ dependencies = [ "sha2 0.10.9", "shell-words", "shellexpand", + "smithy-transport-reqwest", "sqlx", - "strum 0.28.0", + "strum 0.27.2", "symphonia", "sys-info", "tempfile", @@ -4964,18 +4611,18 @@ dependencies = [ "goose-mcp", "indicatif", "open", - "rand 0.8.5", + "rand 0.8.6", "regex", - "reqwest 0.13.3", + "reqwest 0.13.4", "rmcp", "rustyline", "serde", "serde_json", "serde_yaml", "sha2 0.10.9", - "shlex 2.0.1", + "shlex", "sigstore-verify", - "strum 0.28.0", + "strum 0.27.2", "tar", "tempfile", "test-case", @@ -4999,13 +4646,13 @@ dependencies = [ "chrono", "docx-rs", "etcetera 0.11.0", - "image", + "image 0.24.9", "include_dir", "indoc", "lopdf", "once_cell", "process-wrap", - "reqwest 0.13.3", + "reqwest 0.13.4", "rmcp", "schemars 1.2.1", "serde", @@ -5051,12 +4698,12 @@ dependencies = [ "goose", "goose-mcp", "hex", - "http 1.4.0", + "http 1.4.1", "openssl", "pem", - "rand 0.8.5", + "rand 0.8.6", "rcgen", - "reqwest 0.13.3", + "reqwest 0.13.4", "rmcp", "rustls", "serde", @@ -5094,7 +4741,7 @@ version = "1.35.0" dependencies = [ "axum", "env-lock", - "opentelemetry", + "opentelemetry 0.32.0", "rmcp", "serde_json", "tokio", @@ -5113,25 +4760,25 @@ dependencies = [ [[package]] name = "gzip-header" -version = "1.0.0" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95cc527b92e6029a62960ad99aa8a6660faa4555fe5f731aab13aa6a921795a2" +checksum = "86848f4fd157d91041a62c78046fb7b248bcc2dce78376d436a1756e9a038577" dependencies = [ "crc32fast", ] [[package]] name = "h2" -version = "0.4.13" +version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f44da3a8150a6703ed5d34e164b875fd14c2cdab9af1252a9a1020bde2bdc54" +checksum = "171fefbc92fe4a4de27e0698d6a5b392d6a0e333506bc49133760b3bcf948733" dependencies = [ "atomic-waker", "bytes", "fnv", "futures-core", "futures-sink", - "http 1.4.0", + "http 1.4.1", "indexmap 2.14.0", "slab", "tokio", @@ -5156,9 +4803,9 @@ dependencies = [ [[package]] name = "handlebars" -version = "6.4.0" +version = "6.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9b3f9296c208515b87bd915a2f5d1163d4b3f863ba83337d7713cf478055948e" +checksum = "d43ccdfe15a81ab0a8af639e90254227c9a46afd9c5f5b6ec7efaa345c4b0f00" dependencies = [ "derive_builder", "log", @@ -5212,14 +4859,9 @@ dependencies = [ [[package]] name = "hashbrown" -version = "0.17.0" +version = "0.17.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" -dependencies = [ - "allocator-api2", - "equivalent", - "foldhash 0.2.0", -] +checksum = "ed5909b6e89a2db4456e54cd5f673791d7eca6732202bbf2a9cc504fe2f9b84a" [[package]] name = "hashlink" @@ -5230,15 +4872,6 @@ dependencies = [ "hashbrown 0.15.5", ] -[[package]] -name = "hashlink" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ea0b22561a9c04a7cb1a302c013e0259cd3b4bb619f145b32f72b8b4bcbed230" -dependencies = [ - "hashbrown 0.16.1", -] - [[package]] name = "heck" version = "0.5.0" @@ -5315,14 +4948,14 @@ dependencies = [ [[package]] name = "hstr" -version = "3.0.4" +version = "3.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "faa57007c3c9dab34df2fa4c1fb52fe9c34ec5a27ed9d8edea53254b50cd7887" +checksum = "c1b94e40256e78ddd4e30490aa931bec17e65e9413a6ad11f64ec67815da9323" dependencies = [ "hashbrown 0.14.5", "new_debug_unreachable", "once_cell", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "serde", "triomphe", ] @@ -5355,9 +4988,9 @@ dependencies = [ [[package]] name = "http" -version = "1.4.0" +version = "1.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +checksum = "8be7462df143984c4598a256ef469b251d7d7f9e271135073e78fc535414f3d0" dependencies = [ "bytes", "itoa", @@ -5381,7 +5014,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" dependencies = [ "bytes", - "http 1.4.0", + "http 1.4.1", ] [[package]] @@ -5392,7 +5025,7 @@ checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" dependencies = [ "bytes", "futures-core", - "http 1.4.0", + "http 1.4.1", "http-body 1.0.1", "pin-project-lite", ] @@ -5420,22 +5053,21 @@ dependencies = [ [[package]] name = "hyper" -version = "1.8.1" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2ab2d4f250c3d7b1c9fcdff1cece94ea4e2dfbec68614f7b87cb205f24ca9d11" +checksum = "6299f016b246a94207e63da54dbe807655bf9e00044f73ded42c3ac5305fbcca" dependencies = [ "atomic-waker", "bytes", "futures-channel", "futures-core", "h2", - "http 1.4.0", + "http 1.4.1", "http-body 1.0.1", "httparse", "httpdate", "itoa", "pin-project-lite", - "pin-utils", "smallvec", "tokio", "want", @@ -5443,20 +5075,18 @@ dependencies = [ [[package]] name = "hyper-rustls" -version = "0.27.7" +version = "0.27.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3c93eb611681b207e1fe55d5a71ecf91572ec8a6705cdb6857f7d8d5242cf58" +checksum = "33ca68d021ef39cf6463ab54c1d0f5daf03377b70561305bb89a8f83aab66e0f" dependencies = [ - "http 1.4.0", + "http 1.4.1", "hyper", "hyper-util", "rustls", - "rustls-native-certs", - "rustls-pki-types", "tokio", "tokio-rustls", "tower-service", - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] @@ -5498,7 +5128,7 @@ dependencies = [ "bytes", "futures-channel", "futures-util", - "http 1.4.0", + "http 1.4.1", "http-body 1.0.1", "hyper", "ipnet", @@ -5567,7 +5197,7 @@ checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", - "yoke 0.8.1", + "yoke 0.8.2", "zerofrom", "zerovec", ] @@ -5589,9 +5219,9 @@ dependencies = [ [[package]] name = "icu_locale_core" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" +checksum = "92219b62b3e2b4d88ac5119f8904c10f8f61bf7e95b640d25ba3075e6cac2c29" dependencies = [ "displaydoc", "litemap", @@ -5649,16 +5279,16 @@ checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" -version = "2.1.1" +version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" +checksum = "139c4cf31c8b5f33d7e199446eff9c1e02decfc2f0eec2c8d71f65befa45b421" dependencies = [ "displaydoc", "icu_locale_core", "serde", "stable_deref_trait", "writeable", - "yoke 0.8.1", + "yoke 0.8.2", "zerofrom", "zerotrie", "zerovec", @@ -5721,36 +5351,37 @@ dependencies = [ [[package]] name = "image" -version = "0.25.10" +version = "0.24.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" dependencies = [ "bytemuck", - "byteorder-lite", + "byteorder", "color_quant", "exr", - "gif", - "image-webp", - "moxcms", + "gif 0.13.3", + "jpeg-decoder", "num-traits", - "png", - "qoi", - "ravif", - "rayon", - "rgb", - "tiff", - "zune-core", - "zune-jpeg", + "png 0.17.16", + "tiff 0.9.1", ] [[package]] -name = "image-webp" -version = "0.2.4" +name = "image" +version = "0.25.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "525e9ff3e1a4be2fbea1fdf0e98686a6d98b4d8f937e1bf7402245af1909e8c3" +checksum = "85ab80394333c02fe689eaf900ab500fbd0c2213da414687ebf995a65d5a6104" dependencies = [ + "bytemuck", "byteorder-lite", - "quick-error", + "color_quant", + "gif 0.14.2", + "moxcms", + "num-traits", + "png 0.18.1", + "tiff 0.11.3", + "zune-core", + "zune-jpeg", ] [[package]] @@ -5759,12 +5390,6 @@ version = "0.14.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "09e54e57b4c48b40f7aec75635392b12b3421fa26fe8b4332e63138ed278459c" -[[package]] -name = "imgref" -version = "1.12.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "40fac9d56ed6437b198fddba683305e8e2d651aa42647f00f5ae542e7f5c94a2" - [[package]] name = "import_map" version = "0.24.0" @@ -5819,7 +5444,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d466e9454f08e4a911e14806c24e16fba1b4c121d1ea474396f396069cf949d9" dependencies = [ "equivalent", - "hashbrown 0.17.0", + "hashbrown 0.17.1", "serde", "serde_core", ] @@ -5862,7 +5487,6 @@ version = "1.47.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7b4a6248eb93a4401ed2f37dfe8ea592d3cf05b7cf4f8efa867b6895af7e094e" dependencies = [ - "console", "once_cell", "similar", "tempfile", @@ -5880,17 +5504,6 @@ dependencies = [ "web-sys", ] -[[package]] -name = "interpolate_name" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c34819042dc3d3971c46c2190835914dfbe0c3c13f61449b2997f4e9722dfa60" -dependencies = [ - "proc-macro2", - "quote", - "syn 2.0.117", -] - [[package]] name = "ipnet" version = "2.12.0" @@ -5960,15 +5573,15 @@ checksum = "8f42a60cbdf9a97f5d2305f08a87dc4e09308d1276d28c869c684d7777685682" [[package]] name = "ixdtf" -version = "0.6.4" +version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "84de9d95a6d2547d9b77ee3f25fa0ee32e3c3a6484d47a55adebc0439c077992" +checksum = "2ceaf4c6c48465bead8cb6a0b7c4ee0c86ecbb31239032b9c66ab9a08d2f3ee1" [[package]] name = "jiff" -version = "0.2.23" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a3546dc96b6d42c5f24902af9e2538e82e39ad350b0c766eb3fbf2d8f3d8359" +checksum = "392c70591e8749fe235ddaf513e6f58b26bce3dcc16524cecc8936f75afa161e" dependencies = [ "jiff-static", "jiff-tzdb-platform", @@ -5976,14 +5589,14 @@ dependencies = [ "portable-atomic", "portable-atomic-util", "serde_core", - "windows-sys 0.61.2", + "windows-link", ] [[package]] name = "jiff-static" -version = "0.2.23" +version = "0.2.27" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a8c8b344124222efd714b73bb41f8b5120b27a7cc1c75593a6ff768d9d05aa4" +checksum = "47b605b0c050d845fc355bb11eb3f9a8deddc218ea60c76e61aa1f2adfb2c96a" dependencies = [ "proc-macro2", "quote", @@ -6005,22 +5618,6 @@ dependencies = [ "jiff-tzdb", ] -[[package]] -name = "jni" -version = "0.21.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" -dependencies = [ - "cesu8", - "cfg-if", - "combine", - "jni-sys 0.3.1", - "log", - "thiserror 1.0.69", - "walkdir", - "windows-sys 0.45.0", -] - [[package]] name = "jni" version = "0.22.4" @@ -6030,7 +5627,7 @@ dependencies = [ "cfg-if", "combine", "jni-macros", - "jni-sys 0.4.1", + "jni-sys", "log", "simd_cesu8", "thiserror 2.0.18", @@ -6051,15 +5648,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "jni-sys" -version = "0.3.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "41a652e1f9b6e0275df1f15b32661cf0d4b78d4d87ddec5e0c3c20f097433258" -dependencies = [ - "jni-sys 0.4.1", -] - [[package]] name = "jni-sys" version = "0.4.1" @@ -6090,24 +5678,24 @@ dependencies = [ ] [[package]] -name = "js-sys" -version = "0.3.91" +name = "jpeg-decoder" +version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" dependencies = [ - "once_cell", - "wasm-bindgen", + "rayon", ] [[package]] -name = "json5" -version = "0.4.1" +name = "js-sys" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "96b0db21af676c1ce64250b5f40f3ce2cf27e4e47cb91ed91eb6fe9350b430c1" +checksum = "142bc4740e452c1e57ade0cbc129f139c9093e354346f0872ef985f4f5cf5f11" dependencies = [ - "pest", - "pest_derive", - "serde", + "cfg-if", + "futures-util", + "once_cell", + "wasm-bindgen", ] [[package]] @@ -6150,7 +5738,6 @@ dependencies = [ "referencing", "regex", "regex-syntax", - "reqwest 0.12.28", "serde", "serde_json", "uuid-simd", @@ -6171,7 +5758,7 @@ dependencies = [ "p256", "p384", "pem", - "rand 0.8.5", + "rand 0.8.6", "rsa", "serde", "serde_json", @@ -6244,9 +5831,9 @@ checksum = "7a79a3332a6609480d7d0c9eab957bca6b455b91bb84e66d19f5ff66294b85b8" [[package]] name = "libbz2-rs-sys" -version = "0.2.3" +version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b3a6a8c165077efc8f3a971534c50ea6a1a18b329ef4a66e897a7e3a1494565f" +checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c" [[package]] name = "libc" @@ -6264,16 +5851,6 @@ dependencies = [ "pkg-config", ] -[[package]] -name = "libfuzzer-sys" -version = "0.4.12" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f12a681b7dd8ce12bff52488013ba614b869148d54dd79836ab85aafdd53f08d" -dependencies = [ - "arbitrary", - "cc", -] - [[package]] name = "libloading" version = "0.8.9" @@ -6302,14 +5879,14 @@ checksum = "b6d2cec3eae94f9f509c767b45932f1ada8350c4bdb85af2fcab4a3c14807981" [[package]] name = "libredox" -version = "0.1.15" +version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7ddbf48fd451246b1f8c2610bd3b4ac0cc6e149d89832867093ab69a17194f08" +checksum = "e02f3bb43d335493c96bf3fd3a321600bf6bd07ed34bc64118e9293bdffea46c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "libc", "plain", - "redox_syscall 0.7.3", + "redox_syscall 0.7.5", ] [[package]] @@ -6323,12 +5900,6 @@ dependencies = [ "vcpkg", ] -[[package]] -name = "link-section" -version = "0.17.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4d1e908a416d6e9f725743b84a36feea40c4c131e805fbc26d61f9f451f36080" - [[package]] name = "linktime-proc-macro" version = "0.1.0" @@ -6341,7 +5912,7 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83270a18e9f90d0707c41e9f35efada77b64c0e6f3f1810e71c8368a864d5590" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "libc", ] @@ -6359,9 +5930,9 @@ checksum = "32a66949e030da00e8c7d4434b251670a91556f4144941d37452769c25d58a53" [[package]] name = "litemap" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" +checksum = "92daf443525c4cce67b150400bc2316076100ce0b3686209eb8cf3c31612e6f0" [[package]] name = "litrs" @@ -6408,18 +5979,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.29" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" - -[[package]] -name = "loop9" -version = "0.1.5" +version = "0.4.30" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0fae87c125b03c1d2c0150c90365d7d6bcc53fb73a9acaef207d2d065860f062" -dependencies = [ - "imgref", -] +checksum = "616ec5685824bcc94416c6d4a7a446eea774a31efd7062c8480ba6fd06d7a6e5" [[package]] name = "lopdf" @@ -6428,27 +5990,23 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "73fdcbab5b237a03984f83b1394dc534e0b1960675c7f3ec4d04dccc9032b56d" dependencies = [ "aes", - "bitflags 2.11.0", + "bitflags 2.11.1", "cbc", - "chrono", "ecb", "encoding_rs", "flate2", "getrandom 0.4.2", "indexmap 2.14.0", "itoa", - "jiff", "log", "md-5", "nom 8.0.0", "nom_locate", - "rand 0.10.0", + "rand 0.10.1", "rangemap", - "rayon", "sha2 0.10.9", "stringprep", "thiserror 2.0.18", - "time", "ttf-parser", "weezl", ] @@ -6459,15 +6017,6 @@ version = "0.16.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f66e8d5d03f609abc3a39e6f08e4164ebf1447a732906d39eb9b99b7919ef39" -[[package]] -name = "lru" -version = "0.18.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a860605968fce16869fd239cf4237a82f3ac470723415db603b0e8b6c8d4fb9" -dependencies = [ - "hashbrown 0.17.0", -] - [[package]] name = "lru-slab" version = "0.1.2" @@ -6514,16 +6063,6 @@ version = "0.8.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47e1ffaa40ddd1f3ed91f717a33c8c0ee23fff369e3aa8772b9605cc1d22f4c3" -[[package]] -name = "maybe-rayon" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8ea1f30cedd69f0a2954655f7188c6a834246d2bcf1e315e2ac40c4b24dc9519" -dependencies = [ - "cfg-if", - "rayon", -] - [[package]] name = "md-5" version = "0.10.6" @@ -6571,7 +6110,7 @@ version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ecfd3296f8c56b7c1f6fbac3c71cefa9d78ce009850c45000015f206dc7fa21" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block", "core-graphics-types", "foreign-types 0.5.0", @@ -6635,9 +6174,9 @@ dependencies = [ [[package]] name = "mockall" -version = "0.14.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f58d964098a5f9c6b63d0798e5372fd04708193510a7af313c22e9f29b7b620b" +checksum = "39a6bfcc6c8c7eed5ee98b9c3e33adc726054389233e201c95dab2d41a3839d2" dependencies = [ "cfg-if", "downcast", @@ -6649,9 +6188,9 @@ dependencies = [ [[package]] name = "mockall_derive" -version = "0.14.0" +version = "0.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ca41ce716dda6a9be188b385aa78ee5260fc25cd3802cb2a8afdc6afbe6b6dbf" +checksum = "25ca3004c2efe9011bd4e461bd8256445052b9615405b4f7ea43fc8ca5c20898" dependencies = [ "cfg-if", "proc-macro2", @@ -6752,25 +6291,16 @@ dependencies = [ [[package]] name = "nix" -version = "0.31.2" +version = "0.31.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d6d0705320c1e6ba1d912b5e37cf18071b6c2e9b7fa8215a1e8a7651966f5d3" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "cfg-if", "cfg_aliases", "libc", ] -[[package]] -name = "no_std_io2" -version = "0.9.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "418abd1b6d34fbf6cae440dc874771b0525a604428704c76e48b29a5e67b8003" -dependencies = [ - "memchr", -] - [[package]] name = "node_resolver" version = "0.65.0" @@ -6832,12 +6362,6 @@ dependencies = [ "nom 8.0.0", ] -[[package]] -name = "noop_proc_macro" -version = "0.3.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0676bb32a98c1a483ce53e500a81ad9c3d5b3f7c920c28c24e9cb0980d0b5bc8" - [[package]] name = "nostr" version = "0.44.3" @@ -6868,7 +6392,7 @@ version = "0.44.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7462c9d8ae5ef6a28d66a192d399ad2530f1f2130b13186296dbb11bdef5b3d1" dependencies = [ - "lru 0.16.4", + "lru", "nostr", "tokio", ] @@ -6884,15 +6408,15 @@ dependencies = [ [[package]] name = "nostr-relay-pool" -version = "0.44.0" +version = "0.44.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4b1073ccfbaea5549fb914a9d52c68dab2aecda61535e5143dd73e95445a804b" +checksum = "91b2c039df4f96c4bf7dae52a74fd5516ad6dda83a11c0c69dea91b5255a4f37" dependencies = [ "async-utility", "async-wsocket", "atomic-destructor", "hex", - "lru 0.16.4", + "lru", "negentropy", "nostr", "nostr-database", @@ -6946,7 +6470,7 @@ checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "serde", ] @@ -6961,7 +6485,7 @@ dependencies = [ "num-integer", "num-iter", "num-traits", - "rand 0.8.5", + "rand 0.8.6", "smallvec", "zeroize", ] @@ -6984,9 +6508,9 @@ dependencies = [ [[package]] name = "num-conv" -version = "0.2.1" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +checksum = "521739c6d2bac4aa25192232afe6841231376b2b26d4d9fae5ecf8ca5772e441" [[package]] name = "num-derive" @@ -7074,8 +6598,8 @@ dependencies = [ "base64 0.22.1", "chrono", "getrandom 0.2.17", - "http 1.4.0", - "rand 0.8.5", + "http 1.4.1", + "rand 0.8.6", "reqwest 0.12.28", "serde", "serde_json", @@ -7109,7 +6633,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d49e936b501e5c5bf01fda3a9452ff86dc3ea98ad5f283e1455153142d97518c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "objc2", "objc2-core-graphics", "objc2-foundation", @@ -7121,7 +6645,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a180dd8642fa45cdb7dd721cd4c11b1cadd4929ce112ebd8b9f5803cc79d536" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "dispatch2", "objc2", ] @@ -7132,7 +6656,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e022c9d066895efa1345f8e33e584b9f958da2fd4cd116792e15e07e4720a807" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "dispatch2", "objc2", "objc2-core-foundation", @@ -7151,7 +6675,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3e0adef53c21f888deb4fa59fc59f7eb17404926ee8a6f59f5df0fd7f9f3272" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block2", "libc", "objc2", @@ -7164,7 +6688,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "180788110936d59bab6bd83b6060ffdfffb3b922ba1396b312ae795e1de9d81d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "objc2", "objc2-core-foundation", ] @@ -7175,7 +6699,7 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a0125f776a10d00af4152d74616409f0d4a2053a6f57fa5b7d6aa2854ac04794" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "block2", "dispatch2", "objc2", @@ -7215,11 +6739,11 @@ checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "onig" -version = "6.5.1" +version = "6.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "336b9c63443aceef14bea841b899035ae3abe89b7c486aaf4c5bd8aafedac3f0" +checksum = "0cc3cbf698f9438986c11a880c90a6d04b9de27575afd28bbf45b154b6c709e2" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "libc", "once_cell", "onig_sys", @@ -7227,9 +6751,9 @@ dependencies = [ [[package]] name = "onig_sys" -version = "69.9.1" +version = "69.9.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f86c6eef3d6df15f23bcfb6af487cbd2fed4e5581d58d5bf1f5f8b7f6727dc" +checksum = "1e68317604e77e53b85896388e1a803c1d21b74c899ec9e5e1112db90735edd7" dependencies = [ "cc", "pkg-config", @@ -7258,7 +6782,7 @@ version = "0.10.80" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a45fa2aa886c42762255da344f0a0d313e254066c46aad76f300c3d3da62d967" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "cfg-if", "foreign-types 0.3.2", "libc", @@ -7285,9 +6809,9 @@ checksum = "7c87def4c32ab89d880effc9e097653c8da5d6ef28e6b539d313baaacfbafcbe" [[package]] name = "openssl-src" -version = "300.5.5+3.5.5" +version = "300.6.0+3.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3f1787d533e03597a7934fd0a765f0d28e94ecc5fb7789f8053b1e699a56f709" +checksum = "a8e8cbfd3a4a8c8f089147fd7aaa33cf8c7450c4d09f8f80698a0cf093abeff4" dependencies = [ "cc", ] @@ -7308,7 +6832,22 @@ dependencies = [ [[package]] name = "opentelemetry" version = "0.31.0" -source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=345cd74a#345cd74a9c88ad1a47435d3d063c12d47235e803" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b84bcd6ae87133e903af7ef497404dda70c60d0ea14895fc8a5e6722754fc2a0" +dependencies = [ + "futures-core", + "futures-sink", + "js-sys", + "pin-project-lite", + "thiserror 2.0.18", + "tracing", +] + +[[package]] +name = "opentelemetry" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b0142c63252a9e054e68a4c61a5778f7b14f576274d593f8ce883d191a099682" dependencies = [ "futures-core", "futures-sink", @@ -7320,10 +6859,11 @@ dependencies = [ [[package]] name = "opentelemetry-appender-tracing" -version = "0.31.1" -source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=345cd74a#345cd74a9c88ad1a47435d3d063c12d47235e803" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2c0080f0dc1d7c786f467cd85a4e395fcab11ee852004f39a29a18ab7c25d837" dependencies = [ - "opentelemetry", + "opentelemetry 0.32.0", "tracing", "tracing-core", "tracing-subscriber", @@ -7332,64 +6872,124 @@ dependencies = [ [[package]] name = "opentelemetry-http" version = "0.31.0" -source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=345cd74a#345cd74a9c88ad1a47435d3d063c12d47235e803" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d7a6d09a73194e6b66df7c8f1b680f156d916a1a942abf2de06823dd02b7855d" dependencies = [ "async-trait", "bytes", - "http 1.4.0", - "opentelemetry", - "reqwest 0.13.3", + "http 1.4.1", + "opentelemetry 0.31.0", + "reqwest 0.12.28", +] + +[[package]] +name = "opentelemetry-http" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5683015d09e2df236ef005b17f6f196f0d5f6313c4fa43a7b6a53b52776e4331" +dependencies = [ + "async-trait", + "bytes", + "http 1.4.1", + "opentelemetry 0.32.0", + "reqwest 0.13.4", ] [[package]] name = "opentelemetry-otlp" -version = "0.31.0" -source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=345cd74a#345cd74a9c88ad1a47435d3d063c12d47235e803" +version = "0.31.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f69cd6acbb9af919df949cd1ec9e5e7fdc2ef15d234b6b795aaa525cc02f71f" dependencies = [ - "http 1.4.0", - "opentelemetry", - "opentelemetry-http", - "opentelemetry-proto", - "opentelemetry_sdk", + "http 1.4.1", + "opentelemetry 0.31.0", + "opentelemetry-http 0.31.0", + "opentelemetry-proto 0.31.0", + "opentelemetry_sdk 0.31.0", "prost", - "reqwest 0.13.3", + "reqwest 0.12.28", "thiserror 2.0.18", "tokio", "tonic", - "tonic-types", + "tracing", +] + +[[package]] +name = "opentelemetry-otlp" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9966929966d17620d7c316c643ba62631826e10021409357772d5eea84f62c35" +dependencies = [ + "http 1.4.1", + "opentelemetry 0.32.0", + "opentelemetry-http 0.32.0", + "opentelemetry-proto 0.32.0", + "opentelemetry_sdk 0.32.0", + "prost", + "reqwest 0.13.4", + "thiserror 2.0.18", ] [[package]] name = "opentelemetry-proto" version = "0.31.0" -source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=345cd74a#345cd74a9c88ad1a47435d3d063c12d47235e803" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7175df06de5eaee9909d4805a3d07e28bb752c34cab57fa9cff549da596b30f" dependencies = [ - "opentelemetry", - "opentelemetry_sdk", + "opentelemetry 0.31.0", + "opentelemetry_sdk 0.31.0", "prost", "tonic", "tonic-prost", ] +[[package]] +name = "opentelemetry-proto" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56d658ba1faf63f7b9c492cfbe6e0ec365440a16132d3270c1065f7b33f1b638" +dependencies = [ + "opentelemetry 0.32.0", + "opentelemetry_sdk 0.32.0", + "prost", +] + [[package]] name = "opentelemetry-stdout" -version = "0.31.0" -source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=345cd74a#345cd74a9c88ad1a47435d3d063c12d47235e803" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1b1c6a247d79091f0062a5f4bd058589525cf987a8d4c169440d9c1be72f0ad" dependencies = [ "chrono", - "opentelemetry", - "opentelemetry_sdk", + "opentelemetry 0.32.0", + "opentelemetry_sdk 0.32.0", ] [[package]] name = "opentelemetry_sdk" version = "0.31.0" -source = "git+https://github.com/open-telemetry/opentelemetry-rust?rev=345cd74a#345cd74a9c88ad1a47435d3d063c12d47235e803" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e14ae4f5991976fd48df6d843de219ca6d31b01daaab2dad5af2badeded372bd" dependencies = [ "futures-channel", "futures-executor", "futures-util", - "opentelemetry", + "opentelemetry 0.31.0", + "percent-encoding", + "rand 0.9.4", + "thiserror 2.0.18", +] + +[[package]] +name = "opentelemetry_sdk" +version = "0.32.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "368afaed344110f40b179bb8fbe54bc52d98f9bd2b281799ef32487c2650c956" +dependencies = [ + "futures-channel", + "futures-executor", + "futures-util", + "opentelemetry 0.32.0", "percent-encoding", "portable-atomic", "rand 0.9.4", @@ -7403,16 +7003,6 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" -[[package]] -name = "ordered-multimap" -version = "0.7.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "49203cdcae0030493bad186b28da2fa25645fa276a51b6fec8010d281e02ef79" -dependencies = [ - "dlv-list", - "hashbrown 0.14.5", -] - [[package]] name = "outref" version = "0.5.2" @@ -7498,12 +7088,6 @@ version = "1.0.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57c0d7b74b563b49d38dae00a0c37d4d6de9b432382b2892f0574ddcae73fd0a" -[[package]] -name = "pastey" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "35fb2e5f958ec131621fdd531e9fc186ed768cbe395337403ae56c17a74c68ec" - [[package]] name = "pastey" version = "0.2.3" @@ -7578,7 +7162,7 @@ dependencies = [ "thiserror 2.0.18", "tokio", "tracing", - "utoipa 5.4.0", + "utoipa 5.5.0", ] [[package]] @@ -7612,16 +7196,16 @@ dependencies = [ "anyhow", "base64 0.22.1", "camino", - "http 1.4.0", + "http 1.4.1", "indexmap 2.14.0", "keyring", - "opentelemetry-otlp", - "opentelemetry_sdk", - "reqwest 0.13.3", + "opentelemetry-otlp 0.31.1", + "opentelemetry_sdk 0.31.0", + "reqwest 0.13.4", "rmcp", "serde", "serde_json", - "shlex 1.3.0", + "shlex", "thiserror 2.0.18", "tokio", "tonic", @@ -7788,7 +7372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3c80231409c20246a13fddb31776fb942c38553c51e871f8cbd687a4cfb5843d" dependencies = [ "phf_shared 0.11.3", - "rand 0.8.5", + "rand 0.8.6", ] [[package]] @@ -7810,7 +7394,7 @@ version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67eabc2ef2a60eb7faa00097bd1ffdb5bd28e62bf39990626a582201b7a754e5" dependencies = [ - "siphasher 1.0.2", + "siphasher 1.0.3", ] [[package]] @@ -7819,23 +7403,23 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" dependencies = [ - "siphasher 1.0.2", + "siphasher 1.0.3", ] [[package]] name = "pin-project" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f1749c7ed4bcaf4c3d0a3efc28538844fb29bcdd7d2b67b2be7e20ba861ff517" +checksum = "2466b2336ed02bcdca6b294417127b90ec92038d1d5c4fbeac971a922e0e0924" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.11" +version = "1.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d9b20ed30f105399776b9c883e68e536ef602a16ae6f596d2c473591d6ad64c6" +checksum = "c96395f0a926bc13b1c17622aaddda1ecb55d49c8f1bf9777e4d877800a43f8b" dependencies = [ "proc-macro2", "quote", @@ -7854,17 +7438,6 @@ version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b870d8c151b6f2fb93e84a13146138f05d02ed11c7e7c54f8826aaaf7c9f184" -[[package]] -name = "piper" -version = "0.2.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c835479a4443ded371d6c535cbfd8d31ad92c5d23ae9770a61bc155e4992a3c1" -dependencies = [ - "atomic-waker", - "fastrand", - "futures-io", -] - [[package]] name = "pkcs1" version = "0.7.5" @@ -7888,9 +7461,9 @@ dependencies = [ [[package]] name = "pkg-config" -version = "0.3.32" +version = "0.3.33" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7edddbd0b52d732b21ad9a5fab5c704c14cd949e5e9a1ec5929a24fded1b904c" +checksum = "19f132c84eca552bf34cab8ec81f1c1dcc229b811638f9d283dceabe58c5569e" [[package]] name = "plain" @@ -7900,24 +7473,24 @@ checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "plist" -version = "1.8.0" +version = "1.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "740ebea15c5d1428f910cd1a5f52cebf8d25006245ed8ade92702f4943d91e07" +checksum = "092791278e026273c1b65bbdcfbba3a300f2994c896bd01ab01da613c29c46f1" dependencies = [ "base64 0.22.1", "indexmap 2.14.0", - "quick-xml 0.38.4", + "quick-xml 0.39.4", "serde", "time", ] [[package]] name = "png" -version = "0.18.1" +version = "0.17.16" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" +checksum = "82151a2fc869e011c153adc57cf2789ccb8d9906ce52c0b39a6b5697749d7526" dependencies = [ - "bitflags 2.11.0", + "bitflags 1.3.2", "crc32fast", "fdeflate", "flate2", @@ -7925,17 +7498,16 @@ dependencies = [ ] [[package]] -name = "polling" -version = "3.11.0" +name = "png" +version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5d0e4f59085d47d8241c88ead0f274e8a0cb551f3625263c05eb8dd897c34218" +checksum = "60769b8b31b2a9f263dae2776c37b1b28ae246943cf719eb6946a1db05128a61" dependencies = [ - "cfg-if", - "concurrent-queue", - "hermit-abi", - "pin-project-lite", - "rustix 1.1.4", - "windows-sys 0.61.2", + "bitflags 2.11.1", + "crc32fast", + "fdeflate", + "flate2", + "miniz_oxide", ] [[package]] @@ -7957,18 +7529,18 @@ checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "portable-atomic-util" -version = "0.2.6" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "091397be61a01d4be58e7841595bd4bfedb15f1cd54977d79b8271e94ed799a3" +checksum = "c2a106d1259c23fac8e543272398ae0e3c0b8d33c88ed73d0cc71b0f1d902618" dependencies = [ "portable-atomic", ] [[package]] name = "potential_utf" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" +checksum = "0103b1cef7ec0cf76490e969665504990193874ea05c85ff9bab8b911d0a0564" dependencies = [ "serde_core", "writeable", @@ -8036,15 +7608,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "primal-check" -version = "0.3.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dc0d895b311e3af9902528fbb8f928688abbd95872819320517cc24ca6b2bd08" -dependencies = [ - "num-integer", -] - [[package]] name = "primeorder" version = "0.13.6" @@ -8101,25 +7664,6 @@ dependencies = [ "windows 0.62.2", ] -[[package]] -name = "profiling" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d595e54a326bc53c1c197b32d295e14b169e3cfeaa8dc82b529f947fba6bcf5" -dependencies = [ - "profiling-procmacros", -] - -[[package]] -name = "profiling-procmacros" -version = "1.0.18" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4488a4a36b9a4ba6b9334a32a39971f77c1436ec82c38707bce707699cc3bbcb" -dependencies = [ - "quote", - "syn 2.0.117", -] - [[package]] name = "prost" version = "0.14.3" @@ -8143,15 +7687,6 @@ dependencies = [ "syn 2.0.117", ] -[[package]] -name = "prost-types" -version = "0.14.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8991c4cbdb8bc5b11f0b074ffe286c30e523de90fee5ba8132f1399f23cb3dd7" -dependencies = [ - "prost", -] - [[package]] name = "psl-types" version = "2.0.11" @@ -8160,9 +7695,9 @@ checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" [[package]] name = "psm" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3852766467df634d74f0b2d7819bf8dc483a0eb2e3b0f50f756f9cfe8b0d18d8" +checksum = "645dbe486e346d9b5de3ef16ede18c26e6c70ad97418f4874b8b1889d6e761ea" dependencies = [ "ar_archive_writer", "cc", @@ -8184,19 +7719,11 @@ version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" dependencies = [ - "bitflags 2.11.0", - "getopts", + "bitflags 2.11.1", "memchr", - "pulldown-cmark-escape", "unicase", ] -[[package]] -name = "pulldown-cmark-escape" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "007d8adb5ddab6f8e3f491ac63566a7d5002cc7ed73901f72057943fa71ae1ae" - [[package]] name = "pulp" version = "0.21.5" @@ -8236,18 +7763,9 @@ checksum = "40e24eee682d89fb193496edf918a7f407d30175b2e785fe057e4392dfd182e0" [[package]] name = "pxfm" -version = "0.1.28" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b5a041e753da8b807c9255f28de81879c78c876392ff2469cde94799b2896b9d" - -[[package]] -name = "qoi" -version = "0.4.1" +version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001" -dependencies = [ - "bytemuck", -] +checksum = "e0c5ccf5294c6ccd63a74f1565028353830a9c2f5eb0c682c355c471726a6e3f" [[package]] name = "quick-error" @@ -8277,9 +7795,9 @@ dependencies = [ [[package]] name = "quick-xml" -version = "0.38.4" +version = "0.39.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b66c2058c55a409d601666cffe35f04333cf1013010882cec174a7467cd4e21c" +checksum = "cdcc8dd4e2f670d309a5f0e83fe36dfdc05af317008fea29144da1a2ac858e5e" dependencies = [ "memchr", ] @@ -8295,7 +7813,7 @@ dependencies = [ "pin-project-lite", "quinn-proto", "quinn-udp", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "rustls", "socket2", "thiserror 2.0.18", @@ -8316,7 +7834,7 @@ dependencies = [ "lru-slab", "rand 0.9.4", "ring", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "rustls", "rustls-pki-types", "slab", @@ -8379,9 +7897,9 @@ dependencies = [ [[package]] name = "rand" -version = "0.8.5" +version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404" +checksum = "5ca0ecfa931c29007047d1bc58e623ab12e5590e8c7cc53200d5202b69266d8a" dependencies = [ "libc", "rand_chacha 0.3.1", @@ -8400,13 +7918,13 @@ dependencies = [ [[package]] name = "rand" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc266eb313df6c5c09c1c7b1fbe2510961e5bcd3add930c1e31f7ed9da0feff8" +checksum = "d2e8e8bcc7961af1fdac401278c6a831614941f6164ee3bf4ce61b7edb162207" dependencies = [ "chacha20 0.10.0", "getrandom 0.4.2", - "rand_core 0.10.0", + "rand_core 0.10.1", ] [[package]] @@ -8449,9 +7967,9 @@ dependencies = [ [[package]] name = "rand_core" -version = "0.10.0" +version = "0.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0c8d0fd677905edcbeedbf2edb6494d676f0e98d54d5cf9bda0b061cb8fb8aba" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" [[package]] name = "rand_distr" @@ -8469,63 +7987,13 @@ version = "1.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "973443cf09a9c8656b574a866ab68dfa19f0867d0340648c7d2f6a71b8a8ea68" -[[package]] -name = "rav1e" -version = "0.8.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "43b6dd56e85d9483277cde964fd1bdb0428de4fec5ebba7540995639a21cb32b" -dependencies = [ - "aligned-vec", - "arbitrary", - "arg_enum_proc_macro", - "arrayvec", - "av-scenechange", - "av1-grain", - "bitstream-io", - "built", - "cfg-if", - "interpolate_name", - "itertools 0.14.0", - "libc", - "libfuzzer-sys", - "log", - "maybe-rayon", - "new_debug_unreachable", - "noop_proc_macro", - "num-derive", - "num-traits", - "paste", - "profiling", - "rand 0.9.4", - "rand_chacha 0.9.0", - "simd_helpers", - "thiserror 2.0.18", - "v_frame", - "wasm-bindgen", -] - -[[package]] -name = "ravif" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e52310197d971b0f5be7fe6b57530dcd27beb35c1b013f29d66c1ad73fbbcc45" -dependencies = [ - "avif-serialize", - "imgref", - "loop9", - "quick-error", - "rav1e", - "rayon", - "rgb", -] - [[package]] name = "raw-cpuid" version = "11.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "498cd0dc59d73224351ee52a95fee0f1a617a2eae0e7d9d720cc622c73a54186" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] @@ -8565,23 +8033,14 @@ version = "0.14.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "57f6d249aad744e274e682777a50283a225a32705394ee6d5fcc01efa25e4055" dependencies = [ + "aws-lc-rs", "pem", - "ring", "rustls-pki-types", "time", "x509-parser", "yasna", ] -[[package]] -name = "realfft" -version = "3.5.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f821338fddb99d089116342c46e9f1fbf3828dba077674613e734e01d6ea8677" -dependencies = [ - "rustfft", -] - [[package]] name = "reborrow" version = "0.5.5" @@ -8594,16 +8053,16 @@ version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] name = "redox_syscall" -version = "0.7.3" +version = "0.7.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" +checksum = "4666a1a60d8412eab19d94f6d13dcc9cea0a5ef4fdf6a5db306537413c661b1b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", ] [[package]] @@ -8708,7 +8167,7 @@ dependencies = [ "futures-channel", "futures-core", "futures-util", - "http 1.4.0", + "http 1.4.1", "http-body 1.0.1", "http-body-util", "hyper", @@ -8737,14 +8196,14 @@ dependencies = [ "wasm-bindgen", "wasm-bindgen-futures", "web-sys", - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] name = "reqwest" -version = "0.13.3" +version = "0.13.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62e0021ea2c22aed41653bc7e1419abb2c97e038ff2c33d0e1309e49a97deec0" +checksum = "219c5811de6525e5416c7d5d53bb656d3afdbc6c5af816e0802bcfa42dbdc1c3" dependencies = [ "base64 0.22.1", "bytes", @@ -8755,7 +8214,7 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http 1.4.0", + "http 1.4.1", "http-body 1.0.1", "http-body-util", "hyper", @@ -8793,9 +8252,9 @@ dependencies = [ [[package]] name = "resb" -version = "0.1.1" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6a067ab3b5ca3b4dc307d0de9cf75f9f5e6ca9717b192b2f28a36c83e5de9e76" +checksum = "22d392791f3c6802a1905a509e9d1a6039cbbcb5e9e00e5a6d3661f7c874f390" dependencies = [ "potential_utf", "serde_core", @@ -8845,17 +8304,17 @@ dependencies = [ "bytes", "chrono", "futures", - "http 1.4.0", + "http 1.4.1", "http-body 1.0.1", "http-body-util", "hyper", "hyper-util", "oauth2", - "pastey 0.2.3", + "pastey", "pin-project-lite", "process-wrap", - "rand 0.10.0", - "reqwest 0.13.3", + "rand 0.10.1", + "reqwest 0.13.4", "rmcp-macros", "schemars 1.2.1", "serde", @@ -8882,27 +8341,13 @@ dependencies = [ "quote", "serde_json", "syn 2.0.117", -] - -[[package]] -name = "roff" -version = "1.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dbf2048e0e979efb2ca7b91c4f1a8d77c91853e9b987c94c555668a8994915ad" +] [[package]] -name = "ron" -version = "0.12.0" +name = "roff" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd490c5b18261893f14449cbd28cb9c0b637aebf161cd77900bfdedaff21ec32" -dependencies = [ - "bitflags 2.11.0", - "once_cell", - "serde", - "serde_derive", - "typeid", - "unicode-ident", -] +checksum = "323c417e1d9665a65b263ec744ba09030cfb277e9daa0b018a4ab62e57bc8189" [[package]] name = "rsa" @@ -8930,20 +8375,8 @@ version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5258099699851cfd0082aeb645feb9c084d9a5e1f1b8d5372086b989fc5e56a1" dependencies = [ - "num-complex", "num-integer", "num-traits", - "realfft", -] - -[[package]] -name = "rust-ini" -version = "0.21.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "796e8d2b6696392a43bea58116b667fb4c29727dc5abd27d6acf338bb4f688c7" -dependencies = [ - "cfg-if", - "ordered-multimap", ] [[package]] @@ -8960,9 +8393,9 @@ checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" -version = "2.1.1" +version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +checksum = "94300abf3f1ae2e2b8ffb7b58043de3d399c73fa6f4b73826402a5c457614dbe" [[package]] name = "rustc_version" @@ -8973,20 +8406,6 @@ dependencies = [ "semver", ] -[[package]] -name = "rustfft" -version = "6.4.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "21db5f9893e91f41798c88680037dba611ca6674703c1a18601b01a72c8adb89" -dependencies = [ - "num-complex", - "num-integer", - "num-traits", - "primal-check", - "strength_reduce", - "transpose", -] - [[package]] name = "rusticata-macros" version = "4.1.0" @@ -9002,7 +8421,7 @@ version = "0.38.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys 0.4.15", @@ -9015,7 +8434,7 @@ version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6fe4565b9518b83ef4f91bb47ce29620ca828bd32cb7e408f0062e9930ba190" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "errno", "libc", "linux-raw-sys 0.12.1", @@ -9029,7 +8448,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef86cd5876211988985292b91c96a8f2d298df24e75989a43a3c73f2d4d8168b" dependencies = [ "aws-lc-rs", - "log", "once_cell", "ring", "rustls-pki-types", @@ -9052,9 +8470,9 @@ dependencies = [ [[package]] name = "rustls-pki-types" -version = "1.14.0" +version = "1.14.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be040f8b0a225e40375822a563fa9524378b9d63112f53e19ffff34df5d33fdd" +checksum = "30a7197ae7eb376e574fe940d068c30fe0462554a3ddbe4eca7838e049c937a9" dependencies = [ "web-time", "zeroize", @@ -9062,13 +8480,13 @@ dependencies = [ [[package]] name = "rustls-platform-verifier" -version = "0.6.2" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" +checksum = "26d1e2536ce4f35f4846aa13bff16bd0ff40157cdb14cc056c7b14ba41233ba0" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", - "jni 0.21.1", + "jni", "log", "once_cell", "rustls", @@ -9111,7 +8529,7 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4a990b25f351b25139ddc7f21ee3f6f56f86d6846b74ac8fad3a719a287cd4a0" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "cfg-if", "clipboard-win", "home", @@ -9308,7 +8726,7 @@ version = "0.29.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9465315bc9d4566e1724f0fffcbcc446268cb522e60f9a27bcded6b19c108113" dependencies = [ - "rand 0.8.5", + "rand 0.8.6", "secp256k1-sys", "serde", ] @@ -9328,7 +8746,7 @@ version = "2.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "897b2245f0b511c87893af39b033e5ca9cce68824c4d7e7630b5a1d339658d02" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "core-foundation 0.9.4", "core-foundation-sys", "libc", @@ -9341,7 +8759,7 @@ version = "3.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7f4bc775c73d9a02cde8bf7b2ec4c9d12743edf609006c7facc23998404cd1d" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "core-foundation 0.10.1", "core-foundation-sys", "libc", @@ -9360,9 +8778,9 @@ dependencies = [ [[package]] name = "semver" -version = "1.0.27" +version = "1.0.28" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2" +checksum = "8a7852d02fc848982e0c167ef163aaff9cd91dc640ba85e263cb1ce46fae51cd" [[package]] name = "seq-macro" @@ -9380,18 +8798,6 @@ dependencies = [ "serde_derive", ] -[[package]] -name = "serde-untagged" -version = "0.1.9" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f9faf48a4a2d2693be24c6289dbe26552776eb7737074e6722891fadbe6c5058" -dependencies = [ - "erased-serde", - "serde", - "serde_core", - "typeid", -] - [[package]] name = "serde_bytes" version = "0.11.19" @@ -9480,9 +8886,9 @@ dependencies = [ [[package]] name = "serde_spanned" -version = "1.1.0" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "876ac351060d4f882bb1032b6369eb0aef79ad9df1ea8bc404874d8cc3d0cd98" +checksum = "6662b5879511e06e8999a8a235d848113e942c9124f211511b16466ee2995f26" dependencies = [ "serde_core", ] @@ -9564,9 +8970,6 @@ version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "911bd979bf1070a3f3aa7b691a3b3e9968f339ceeec89e08c280a8a22207a32f" dependencies = [ - "futures-executor", - "futures-util", - "log", "once_cell", "parking_lot", "scc", @@ -9647,12 +9050,6 @@ version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" -[[package]] -name = "shlex" -version = "2.0.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8fadd59c855ef2080decdef8ff161eb6661b86933c9d82e5ba29dc602a55aba" - [[package]] name = "signal-hook-registry" version = "1.4.8" @@ -9734,7 +9131,7 @@ checksum = "fbe0bf948de89bed9b3b5c9d62940264a4da60db3518006b952b107d0e71926f" dependencies = [ "base64 0.22.1", "hex", - "reqwest 0.13.3", + "reqwest 0.13.4", "serde", "serde_json", "sigstore-crypto", @@ -9777,7 +9174,7 @@ dependencies = [ "hex", "jiff", "rand 0.9.4", - "reqwest 0.13.3", + "reqwest 0.13.4", "rustls-pki-types", "rustls-webpki", "sigstore-crypto", @@ -9834,9 +9231,9 @@ dependencies = [ [[package]] name = "simd-adler32" -version = "0.3.8" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" +checksum = "703d5c7ef118737c72f1af64ad2f6f8c5e1921f818cdcb97b8fe6fc69bf66214" [[package]] name = "simd_cesu8" @@ -9848,15 +9245,6 @@ dependencies = [ "simdutf8", ] -[[package]] -name = "simd_helpers" -version = "0.1.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "95890f873bec569a0362c235787f3aca6e1e887302ba4840839bcc6459c42da6" -dependencies = [ - "quote", -] - [[package]] name = "simdutf8" version = "0.1.5" @@ -9893,9 +9281,9 @@ checksum = "38b58827f4464d87d377d175e90bf58eb00fd8716ff0a62f80356b5e61555d0d" [[package]] name = "siphasher" -version = "1.0.2" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b2aa850e253778c88a04c3d7323b043aeda9d3e30d5971937c1855769763678e" +checksum = "8ee5873ec9cce0195efcb7a4e9507a04cd49aec9c83d0389df45b1ef7ba2e649" [[package]] name = "slab" @@ -9929,6 +9317,19 @@ version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" +[[package]] +name = "smithy-transport-reqwest" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "566dc85be03a09c384f77a122188d9af000e1f1bd23551b346a9b555838da7e1" +dependencies = [ + "aws-smithy-runtime-api", + "aws-smithy-types", + "http 1.4.1", + "parking_lot", + "reqwest 0.13.4", +] + [[package]] name = "socket2" version = "0.6.3" @@ -9950,7 +9351,7 @@ dependencies = [ "data-encoding", "debugid", "if_chain", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "serde", "serde_json", "unicode-id-start", @@ -10025,7 +9426,7 @@ dependencies = [ "futures-io", "futures-util", "hashbrown 0.15.5", - "hashlink 0.10.0", + "hashlink", "indexmap 2.14.0", "log", "memchr", @@ -10091,7 +9492,7 @@ checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.11.0", + "bitflags 2.11.1", "byteorder", "bytes", "chrono", @@ -10113,7 +9514,7 @@ dependencies = [ "memchr", "once_cell", "percent-encoding", - "rand 0.8.5", + "rand 0.8.6", "rsa", "serde", "sha1", @@ -10134,7 +9535,7 @@ checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" dependencies = [ "atoi", "base64 0.22.1", - "bitflags 2.11.0", + "bitflags 2.11.1", "byteorder", "chrono", "crc", @@ -10152,7 +9553,7 @@ dependencies = [ "md-5", "memchr", "once_cell", - "rand 0.8.5", + "rand 0.8.6", "serde", "serde_json", "sha2 0.10.9", @@ -10191,9 +9592,9 @@ dependencies = [ [[package]] name = "sse-stream" -version = "0.2.1" +version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eb4dc4d33c68ec1f27d386b5610a351922656e1fdf5c05bbaad930cd1519479a" +checksum = "f3962b63f038885f15bce2c6e02c0e7925c072f1ac86bb60fd44c5c6b762fb72" dependencies = [ "bytes", "futures-util", @@ -10210,15 +9611,15 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "stacker" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "08d74a23609d509411d10e2176dc2a4346e3b4aea2e7b1869f19fdedbc71c013" +checksum = "640c8cdd92b6b12f5bcb1803ca3bbf5ab96e5e6b6b96b9ab77dabe9e880b3190" dependencies = [ "cc", "cfg-if", "libc", "psm", - "windows-sys 0.59.0", + "windows-sys 0.61.2", ] [[package]] @@ -10248,12 +9649,6 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" -[[package]] -name = "strength_reduce" -version = "0.2.4" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fe895eb47f22e2ddd4dabc02bce419d2e643c8e3b585c78158b349195bc24d82" - [[package]] name = "string_enum" version = "1.0.2" @@ -10345,7 +9740,7 @@ dependencies = [ "allocator-api2", "bumpalo", "hashbrown 0.14.5", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", ] [[package]] @@ -10374,7 +9769,7 @@ dependencies = [ "new_debug_unreachable", "num-bigint", "once_cell", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "serde", "siphasher 0.3.11", "swc_atoms", @@ -10418,12 +9813,12 @@ version = "18.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a573a0c72850dec8d4d8085f152d5778af35a2520c3093b242d2d1d50776da7c" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "is-macro", "num-bigint", "once_cell", "phf 0.11.3", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "serde", "string_enum", "swc_atoms", @@ -10444,7 +9839,7 @@ dependencies = [ "num-bigint", "once_cell", "regex", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "ryu-js", "serde", "swc_allocator", @@ -10473,10 +9868,10 @@ version = "26.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e82f7747e052c6ff6e111fa4adeb14e33b46ee6e94fe5ef717601f651db48fc" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "either", "num-bigint", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "seq-macro", "serde", "smallvec", @@ -10497,7 +9892,7 @@ checksum = "fbcababb48f0d46587a0a854b2c577eb3a56fa99687de558338021e93cd2c8f5" dependencies = [ "anyhow", "pathdiff", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "serde", "swc_atoms", "swc_common", @@ -10510,11 +9905,11 @@ version = "27.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f1a51af1a92cd4904c073b293e491bbc0918400a45d58227b34c961dd6f52d7" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "either", "num-bigint", "phf 0.11.3", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "seq-macro", "serde", "smartstring", @@ -10536,7 +9931,7 @@ dependencies = [ "once_cell", "par-core", "phf 0.11.3", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "serde", "swc_atoms", "swc_common", @@ -10579,7 +9974,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c2d7748d4112c87ce1885260035e4a43cebfe7661a40174b7d77a0a04760a257" dependencies = [ "either", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "serde", "swc_atoms", "swc_common", @@ -10600,7 +9995,7 @@ dependencies = [ "bytes-str", "indexmap 2.14.0", "once_cell", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "serde", "sha1", "string_enum", @@ -10621,7 +10016,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4408800fdeb541fabf3659db622189a0aeb386f57b6103f9294ff19dfde4f7b0" dependencies = [ "bytes-str", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "serde", "swc_atoms", "swc_common", @@ -10642,7 +10037,7 @@ dependencies = [ "num_cpus", "once_cell", "par-core", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "ryu-js", "swc_atoms", "swc_common", @@ -10700,7 +10095,7 @@ dependencies = [ "data-encoding", "debugid", "if_chain", - "rustc-hash 2.1.1", + "rustc-hash 2.1.2", "serde", "serde_json", "unicode-id-start", @@ -10730,7 +10125,6 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5773a4c030a19d9bfaa090f49746ff35c75dfddfa700df7a5939d5e076a57039" dependencies = [ "lazy_static", - "symphonia-bundle-flac", "symphonia-bundle-mp3", "symphonia-codec-aac", "symphonia-codec-adpcm", @@ -10738,26 +10132,12 @@ dependencies = [ "symphonia-codec-pcm", "symphonia-codec-vorbis", "symphonia-core", - "symphonia-format-caf", "symphonia-format-isomp4", "symphonia-format-mkv", - "symphonia-format-ogg", "symphonia-format-riff", "symphonia-metadata", ] -[[package]] -name = "symphonia-bundle-flac" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c91565e180aea25d9b80a910c546802526ffd0072d0b8974e3ebe59b686c9976" -dependencies = [ - "log", - "symphonia-core", - "symphonia-metadata", - "symphonia-utils-xiph", -] - [[package]] name = "symphonia-bundle-mp3" version = "0.5.5" @@ -10835,17 +10215,6 @@ dependencies = [ "log", ] -[[package]] -name = "symphonia-format-caf" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b8faf379316b6b6e6bbc274d00e7a592e0d63ff1a7e182ce8ba25e24edd3d096" -dependencies = [ - "log", - "symphonia-core", - "symphonia-metadata", -] - [[package]] name = "symphonia-format-isomp4" version = "0.5.5" @@ -10872,18 +10241,6 @@ dependencies = [ "symphonia-utils-xiph", ] -[[package]] -name = "symphonia-format-ogg" -version = "0.5.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2b4955c67c1ed3aa8ae8428d04ca8397fbef6a19b2b051e73b5da8b1435639cb" -dependencies = [ - "log", - "symphonia-core", - "symphonia-metadata", - "symphonia-utils-xiph", -] - [[package]] name = "symphonia-format-riff" version = "0.5.5" @@ -11025,7 +10382,7 @@ version = "0.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "01198a2debb237c62b6826ec7081082d951f46dbb64b0e8c7649a452230d1dfc" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "byteorder", "enum-as-inner", "libc", @@ -11039,7 +10396,7 @@ version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a13f3d0daba03132c0aa9767f98351b3488edc2c100cda2d2ec2b04f3d8d3c8b" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "core-foundation 0.9.4", "system-configuration-sys", ] @@ -11068,7 +10425,6 @@ checksum = "3f6221d9a6003c78398e3b239969f352578258df48c8eb051caadae0015bc840" dependencies = [ "filetime", "libc", - "xattr", ] [[package]] @@ -11213,9 +10569,9 @@ dependencies = [ [[package]] name = "thin-vec" -version = "0.2.16" +version = "0.2.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "259cdf8ed4e4aca6f1e9d011e10bd53f524a2d0637d7b28450f6c64ac298c4c6" +checksum = "b0f7e269b48f0a7dd0146680fa24b50cc67fc0373f086a5b2f99bd084639b482" [[package]] name = "thiserror" @@ -11272,6 +10628,17 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "tiff" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba1310fcea54c6a9a4fd1aad794ecc02c31682f6bfbecdf460bf19533eed1e3e" +dependencies = [ + "flate2", + "jpeg-decoder", + "weezl", +] + [[package]] name = "tiff" version = "0.11.3" @@ -11344,20 +10711,11 @@ dependencies = [ "zoneinfo64", ] -[[package]] -name = "tiny-keccak" -version = "2.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2c9d3793400a45f954c52e73d068316d76b6f4e36977e3fcebb13a2721e80237" -dependencies = [ - "crunchy", -] - [[package]] name = "tinystr" -version = "0.8.2" +version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" +checksum = "c8323304221c2a851516f22236c5722a72eaa19749016521d6dff0824447d96d" dependencies = [ "displaydoc", "serde_core", @@ -11630,15 +10988,15 @@ dependencies = [ [[package]] name = "toml" -version = "1.1.0+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f8195ca05e4eb728f4ba94f3e3291661320af739c4e43779cbdfae82ab239fcc" +checksum = "81f3d15e84cbcd896376e6730314d59fb5a87f31e4b038454184435cd57defee" dependencies = [ "serde_core", "serde_spanned", - "toml_datetime 1.1.0+spec-1.1.0", + "toml_datetime 1.1.1+spec-1.1.0", "toml_parser", - "winnow 1.0.0", + "winnow 1.0.3", ] [[package]] @@ -11652,40 +11010,40 @@ dependencies = [ [[package]] name = "toml_datetime" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97251a7c317e03ad83774a8752a7e81fb6067740609f75ea2b585b569a59198f" +checksum = "3165f65f62e28e0115a00b2ebdd37eb6f3b641855f9d636d3cd4103767159ad7" dependencies = [ "serde_core", ] [[package]] name = "toml_parser" -version = "1.1.0+spec-1.1.0" +version = "1.1.2+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2334f11ee363607eb04df9b8fc8a13ca1715a72ba8662a26ac285c98aabb4011" +checksum = "a2abe9b86193656635d2411dc43050282ca48aa31c2451210f4202550afb7526" dependencies = [ - "winnow 1.0.0", + "winnow 1.0.3", ] [[package]] name = "toml_writer" -version = "1.1.0+spec-1.1.0" +version = "1.1.1+spec-1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d282ade6016312faf3e41e57ebbba0c073e4056dab1232ab1cb624199648f8ed" +checksum = "756daf9b1013ebe47a8776667b466417e2d4c5679d441c26230efd9ef78692db" [[package]] name = "tonic" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fec7c61a0695dc1887c1b53952990f3ad2e3a31453e1f49f10e75424943a93ec" +checksum = "ac2a5518c70fa84342385732db33fb3f44bc4cc748936eb5833d2df34d6445ef" dependencies = [ "async-trait", "axum", "base64 0.22.1", "bytes", "h2", - "http 1.4.0", + "http 1.4.1", "http-body 1.0.1", "http-body-util", "hyper", @@ -11705,26 +11063,15 @@ dependencies = [ [[package]] name = "tonic-prost" -version = "0.14.5" +version = "0.14.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a55376a0bbaa4975a3f10d009ad763d8f4108f067c7c2e74f3001fb49778d309" +checksum = "50849f68853be452acf590cde0b146665b8d507b3b8af17261df47e02c209ea0" dependencies = [ "bytes", "prost", "tonic", ] -[[package]] -name = "tonic-types" -version = "0.14.5" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a875a902255423d34c1f20838ab374126db8eb41625b7947a1d54113b0b7399" -dependencies = [ - "prost", - "prost-types", - "tonic", -] - [[package]] name = "tower" version = "0.5.3" @@ -11751,11 +11098,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4cfcf7e2740e6fc6d4d688b4ef00650406bb94adf4731e43c096c3a19fe40840" dependencies = [ "async-compression", - "bitflags 2.11.0", + "bitflags 2.11.1", "bytes", "futures-core", "futures-util", - "http 1.4.0", + "http 1.4.1", "http-body 1.0.1", "http-body-util", "pin-project-lite", @@ -11837,29 +11184,17 @@ dependencies = [ "tracing", ] -[[package]] -name = "tracing-log" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" -dependencies = [ - "log", - "once_cell", - "tracing-core", -] - [[package]] name = "tracing-opentelemetry" -version = "0.32.1" +version = "0.33.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ac28f2d093c6c477eaa76b23525478f38de514fa9aeb1285738d4b97a9552fc" +checksum = "adbc64cba7137545b8044cb1fe9814f7aacf3c6b5f9b45be8bb5db538befdb26" dependencies = [ "js-sys", - "opentelemetry", + "opentelemetry 0.32.0", "smallvec", "tracing", "tracing-core", - "tracing-log", "tracing-subscriber", "web-time", ] @@ -11887,25 +11222,13 @@ dependencies = [ "serde", "serde_json", "sharded-slab", - "smallvec", "thread_local", "time", "tracing", "tracing-core", - "tracing-log", "tracing-serde", ] -[[package]] -name = "transpose" -version = "0.2.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1ad61aed86bc3faea4300c7aee358b4c6d0c8d6ccc36524c96e4c92ccf26e77e" -dependencies = [ - "num-integer", - "strength_reduce", -] - [[package]] name = "tree-sitter" version = "0.26.9" @@ -12046,7 +11369,7 @@ checksum = "4793cb5e56680ecbb1d843515b23b6de9a75eb04b66643e256a396d43be33c13" dependencies = [ "bytes", "data-encoding", - "http 1.4.0", + "http 1.4.1", "httparse", "log", "rand 0.9.4", @@ -12065,7 +11388,7 @@ checksum = "6c01152af293afb9c7c2a57e4b559c5620b421f6d133261c60dd2d0cdb38e6b8" dependencies = [ "bytes", "data-encoding", - "http 1.4.0", + "http 1.4.1", "httparse", "log", "native-tls", @@ -12088,12 +11411,6 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8e28f89b80c87b8fb0cf04ab448d5dd0dd0ade2f8891bae878de66a75a28600e" -[[package]] -name = "typeid" -version = "1.0.3" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" - [[package]] name = "typenum" version = "1.20.0" @@ -12358,14 +11675,14 @@ dependencies = [ [[package]] name = "utoipa" -version = "5.4.0" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2fcc29c80c21c31608227e0912b2d7fddba57ad76b606890627ba8ee7964e993" +checksum = "8bde15df68e80b16c7d16b9616e80770ad158988daa56a27dccd1e55558b0160" dependencies = [ "indexmap 2.14.0", "serde", "serde_json", - "utoipa-gen 5.4.0", + "utoipa-gen 5.5.0", ] [[package]] @@ -12383,9 +11700,9 @@ dependencies = [ [[package]] name = "utoipa-gen" -version = "5.4.0" +version = "5.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6d79d08d92ab8af4c5e8a6da20c47ae3f61a0f1dabc1997cdf2d082b757ca08b" +checksum = "6ba0b99ee52df3028635d93840c797102da61f8a7bb3cf751032455895b52ef8" dependencies = [ "proc-macro2", "quote", @@ -12428,7 +11745,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f84468f393984251db025e944544590a8fb429d5088b78102bd72f09232f0da" dependencies = [ "bindgen", - "bitflags 2.11.0", + "bitflags 2.11.1", "fslock", "gzip-header", "home", @@ -12439,21 +11756,19 @@ dependencies = [ ] [[package]] -name = "v_frame" -version = "0.3.9" +name = "v_escape-base" +version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "666b7727c8875d6ab5db9533418d7c764233ac9c0cff1d469aec8fa127597be2" -dependencies = [ - "aligned-vec", - "num-traits", - "wasm-bindgen", -] +checksum = "f1212fce830b75af194b578e55b3db9049f2c8c45f58d397fb25602fdb50fb3d" [[package]] name = "v_htmlescape" -version = "0.15.8" +version = "0.17.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e8257fbc510f0a46eb602c10215901938b5c2a7d5e70fc11483b1d3c9b5b18c" +checksum = "befb3d53c9e3ec641417685896cbc8cc5bd264d6a2e190c56aaef1af24740d99" +dependencies = [ + "v_escape-base", +] [[package]] name = "valuable" @@ -12506,11 +11821,11 @@ checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasip2" -version = "1.0.2+wasi-0.2.9" +version = "1.0.3+wasi-0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9517f9239f02c069db75e65f174b3da828fe5f5b945c4dd26bd25d89c03ebcf5" +checksum = "20064672db26d7cdc89c7798c48a0fdfac8213434a1186e5ef29fd560ae223d6" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.57.1", ] [[package]] @@ -12519,7 +11834,7 @@ version = "0.4.0+wasi-0.3.0-rc-2026-01-06" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5428f8bf88ea5ddc08faddef2ac4a67e390b88186c703ce6dbd955e1c145aca5" dependencies = [ - "wit-bindgen", + "wit-bindgen 0.51.0", ] [[package]] @@ -12530,9 +11845,9 @@ checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" -version = "0.2.114" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" +checksum = "3ed04576f974d2b2fba0f38c51dbc5518011e38c36bf1143164be765528fd409" dependencies = [ "cfg-if", "once_cell", @@ -12543,23 +11858,19 @@ dependencies = [ [[package]] name = "wasm-bindgen-futures" -version = "0.4.64" +version = "0.4.72" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e9c5522b3a28661442748e09d40924dfb9ca614b21c00d3fd135720e48b67db8" +checksum = "9473dbd2991ae90b6291c3c32c30c6187ac49aa32f9905d1cce280ec1e110b0f" dependencies = [ - "cfg-if", - "futures-util", "js-sys", - "once_cell", "wasm-bindgen", - "web-sys", ] [[package]] name = "wasm-bindgen-macro" -version = "0.2.114" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" +checksum = "916151b09da36bd82f6615cbf3a419e2f0ba23a03c6160e8e92eb6bd4aa1dec6" dependencies = [ "quote", "wasm-bindgen-macro-support", @@ -12567,9 +11878,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-macro-support" -version = "0.2.114" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" +checksum = "299047362ccbfce148b67ab7e73349f77748e00c8296f9542adfad2ad82c5c5e" dependencies = [ "bumpalo", "proc-macro2", @@ -12580,9 +11891,9 @@ dependencies = [ [[package]] name = "wasm-bindgen-shared" -version = "0.2.114" +version = "0.2.122" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" +checksum = "9a929b2c61f11ba3e9bc35b50c1f25cb38e0e892c0c231ae2b8cf78d5dad4437" dependencies = [ "unicode-ident", ] @@ -12638,7 +11949,7 @@ version = "0.244.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "47b807c72e1bac69382b3a6fb3dbe8ea4c0ed87ff5629b8685ae6b9a611028fe" dependencies = [ - "bitflags 2.11.0", + "bitflags 2.11.1", "hashbrown 0.15.5", "indexmap 2.14.0", "semver", @@ -12646,9 +11957,9 @@ dependencies = [ [[package]] name = "web-sys" -version = "0.3.91" +version = "0.3.99" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "854ba17bb104abfb26ba36da9729addc7ce7f06f5c0f90f3c391f8461cca21f9" +checksum = "6d621441cfc37b84979402712047321980c178f299193a3589d05b99e8763436" dependencies = [ "js-sys", "wasm-bindgen", @@ -12671,7 +11982,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" dependencies = [ "core-foundation 0.10.1", - "jni 0.22.4", + "jni", "log", "ndk-context", "objc2", @@ -12682,9 +11993,9 @@ dependencies = [ [[package]] name = "webpki-root-certs" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "804f18a4ac2676ffb4e8b5b5fa9ae38af06df08162314f96a68d2a363e21a8ca" +checksum = "f31141ce3fc3e300ae89b78c0dd67f9708061d1d2eda54b8209346fd6be9a92c" dependencies = [ "rustls-pki-types", ] @@ -12695,14 +12006,14 @@ version = "0.26.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "521bc38abb08001b01866da9f51eb7c5d647a19260e00054a8c7fd5f9e57f7a9" dependencies = [ - "webpki-roots 1.0.6", + "webpki-roots 1.0.7", ] [[package]] name = "webpki-roots" -version = "1.0.6" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "22cfaf3c063993ff62e73cb4311efde4db1efb31ab78a3e5c457939ad5cc0bed" +checksum = "52f5ee44c96cf55f1b349600768e3ece3a8f26010c05265ab73f945bb1a2eb9d" dependencies = [ "rustls-pki-types", ] @@ -12952,15 +12263,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-sys" -version = "0.45.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" -dependencies = [ - "windows-targets 0.42.2", -] - [[package]] name = "windows-sys" version = "0.48.0" @@ -13006,21 +12308,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows-targets" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" -dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", -] - [[package]] name = "windows-targets" version = "0.48.5" @@ -13078,12 +12365,6 @@ dependencies = [ "windows-link", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.48.5" @@ -13102,12 +12383,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.48.5" @@ -13126,12 +12401,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.48.5" @@ -13162,12 +12431,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.48.5" @@ -13186,12 +12449,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.48.5" @@ -13210,12 +12467,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.48.5" @@ -13234,12 +12485,6 @@ version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.48.5" @@ -13266,9 +12511,9 @@ checksum = "df79d97927682d2fd8adb29682d1140b343be4ac0f08fd68b7765d9c059d3945" [[package]] name = "winnow" -version = "1.0.0" +version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a90e88e4667264a994d34e6d1ab2d26d398dcdca8b7f52bec8668957517fc7d8" +checksum = "0592e1c9d151f854e6fd382574c3a0855250e1d9b2f99d9281c6e6391af352f1" dependencies = [ "memchr", ] @@ -13299,7 +12544,7 @@ dependencies = [ "base64 0.22.1", "deadpool", "futures", - "http 1.4.0", + "http 1.4.1", "http-body-util", "hyper", "hyper-util", @@ -13321,6 +12566,12 @@ dependencies = [ "wit-bindgen-rust-macro", ] +[[package]] +name = "wit-bindgen" +version = "0.57.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1ebf944e87a7c253233ad6766e082e3cd714b5d03812acc24c318f549614536e" + [[package]] name = "wit-bindgen-core" version = "0.51.0" @@ -13370,7 +12621,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d66ea20e9553b30172b5e831994e35fbde2d165325bec84fc43dbf6f4eb9cb2" dependencies = [ "anyhow", - "bitflags 2.11.0", + "bitflags 2.11.1", "indexmap 2.14.0", "log", "serde", @@ -13402,9 +12653,9 @@ dependencies = [ [[package]] name = "writeable" -version = "0.6.2" +version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" +checksum = "1ffae5123b2d3fc086436f8834ae3ab053a283cfac8fe0a0b8eaae044768a4c4" [[package]] name = "wyz" @@ -13453,12 +12704,12 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d43b0f71ce057da06bc0851b23ee24f3f86190b07203dd8f567d0b706a185202" dependencies = [ "asn1-rs", + "aws-lc-rs", "data-encoding", "der-parser", "lazy_static", "nom 7.1.3", "oid-registry", - "ring", "rusticata-macros", "thiserror 2.0.18", "time", @@ -13475,16 +12726,6 @@ dependencies = [ "der", ] -[[package]] -name = "xattr" -version = "1.6.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "32e45ad4206f6d2479085147f02bc2ef834ac85886624a23575ae137c8aa8156" -dependencies = [ - "libc", - "rustix 1.1.4", -] - [[package]] name = "xmlparser" version = "0.13.6" @@ -13497,23 +12738,6 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7008a9d8ba97a7e47d9b2df63fcdb8dade303010c5a7cd5bf2469d4da6eba673" -[[package]] -name = "y4m" -version = "0.8.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a5a4b21e1a62b67a2970e6831bc091d7b87e119e7f9791aef9702e3bef04448" - -[[package]] -name = "yaml-rust2" -version = "0.11.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "631a50d867fafb7093e709d75aaee9e0e0d5deb934021fcea25ac2fe09edc51e" -dependencies = [ - "arraydeque", - "encoding_rs", - "hashlink 0.11.0", -] - [[package]] name = "yansi" version = "1.0.1" @@ -13544,12 +12768,12 @@ dependencies = [ [[package]] name = "yoke" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" +checksum = "abe8c5fda708d9ca3df187cae8bfb9ceda00dd96231bed36e445a1a48e66f9ca" dependencies = [ "stable_deref_trait", - "yoke-derive 0.8.1", + "yoke-derive 0.8.2", "zerofrom", ] @@ -13567,9 +12791,9 @@ dependencies = [ [[package]] name = "yoke-derive" -version = "0.8.1" +version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" +checksum = "de844c262c8848816172cef550288e7dc6c7b7814b4ee56b3e1553f275f1858e" dependencies = [ "proc-macro2", "quote", @@ -13579,18 +12803,18 @@ dependencies = [ [[package]] name = "zerocopy" -version = "0.8.47" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "efbb2a062be311f2ba113ce66f697a4dc589f85e78a4aea276200804cea0ed87" +checksum = "eed437bf9d6692032087e337407a86f04cd8d6a16a37199ed57949d415bd68e9" dependencies = [ "zerocopy-derive", ] [[package]] name = "zerocopy-derive" -version = "0.8.47" +version = "0.8.48" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e8bc7269b54418e7aeeef514aa68f8690b8c0489a06b0136e5f57c4c5ccab89" +checksum = "70e3cd084b1788766f53af483dd21f93881ff30d7320490ec3ef7526d203bad4" dependencies = [ "proc-macro2", "quote", @@ -13599,18 +12823,18 @@ dependencies = [ [[package]] name = "zerofrom" -version = "0.1.6" +version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" +checksum = "0ec05a11813ea801ff6d75110ad09cd0824ddba17dfe17128ea0d5f68e6c5272" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" -version = "0.1.6" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" +checksum = "11532158c46691caf0f2593ea8358fed6bbf68a0315e80aae9bd41fbade684a1" dependencies = [ "proc-macro2", "quote", @@ -13640,32 +12864,33 @@ dependencies = [ [[package]] name = "zerotrie" -version = "0.2.3" +version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" +checksum = "0f9152d31db0792fa83f70fb2f83148effb5c1f5b8c7686c3459e361d9bc20bf" dependencies = [ "displaydoc", - "yoke 0.8.1", + "yoke 0.8.2", "zerofrom", + "zerovec", ] [[package]] name = "zerovec" -version = "0.11.5" +version = "0.11.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" +checksum = "90f911cbc359ab6af17377d242225f4d75119aec87ea711a880987b18cd7b239" dependencies = [ "serde", - "yoke 0.8.1", + "yoke 0.8.2", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" -version = "0.11.2" +version = "0.11.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" +checksum = "625dc425cab0dca6dc3c3319506e6593dcb08a9f387ea3b284dbd52a92c40555" dependencies = [ "proc-macro2", "quote", @@ -13809,9 +13034,9 @@ dependencies = [ [[package]] name = "zune-jpeg" -version = "0.5.14" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0b7a1c0af6e5d8d1363f4994b7a091ccf963d8b694f7da5b0b9cceb82da2c0a6" +checksum = "27bc9d5b815bc103f142aa054f561d9187d191692ec7c2d1e2b4737f8dbd7296" dependencies = [ "zune-core", ] diff --git a/Cargo.toml b/Cargo.toml index b0f737d3ecfe..d33b88d695fc 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -20,88 +20,96 @@ uninlined_format_args = "allow" string_slice = "warn" [workspace.dependencies] -rmcp = { version = "1.7.0", features = ["schemars", "auth"] } -agent-client-protocol-schema = { version = "0.13", features = ["unstable"] } -agent-client-protocol = "0.12" -arboard = "3" -anyhow = "1.0" -async-stream = "0.3" -async-trait = "0.1" -axum = "0.8" -base64 = "0.22.1" -bytes = "1" -chrono = { version = "0.4", features = ["serde"] } -clap = { version = "4", features = ["derive"] } -dirs = "5.0" -dotenvy = "0.15" -env-lock = "1.0.1" -etcetera = "0.11.0" -fs2 = "0.4" -futures = "0.3" -http = "1.0" -ignore = "0.4.25" -include_dir = "0.7.4" -indoc = "2.0" -lru = "0.18" -once_cell = "1.20" -rand = "0.8" -regex = "1.12" -reqwest = { version = "0.13", default-features = false, features = ["multipart", "form"] } -schemars = { default-features = false, version = "1.0" } -serde = { version = "1.0", features = ["derive"] } -serde_json = "1.0" -serde_yaml = "0.9" -shellexpand = "3.1" -strum = { version = "0.28", features = ["derive"] } -tempfile = "3" -thiserror = "1.0" -tokio = { version = "1.49", features = ["full"] } -tokio-stream = "0.1" -tokio-util = "0.7" -tower-http = "0.6.11" -tracing = "0.1" -tracing-appender = "0.2" -tracing-subscriber = "0.3" -urlencoding = "2.1" -utoipa = "4.1" -uuid = { version = "1.23", features = ["v4"] } -webbrowser = "1.2" -which = "8.0.0" -winapi = { version = "0.3", features = ["wincred"] } -wiremock = "0.6" -zip = { version = "^8.6", default-features = false, features = ["deflate"] } -serial_test = "3.2.0" -sha2 = "0.10" -shell-words = "1.1.1" -test-case = "3.3.1" -url = "2.5.8" -opentelemetry = "0.31" -opentelemetry_sdk = { version = "0.31", features = ["metrics"] } -opentelemetry-otlp = "0.31" -opentelemetry-appender-tracing = { version = "0.31", features = ["experimental_span_attributes"] } -opentelemetry-stdout = { version = "0.31", features = ["trace", "metrics", "logs"] } -tracing-futures = { version = "0.2", features = ["futures-03"] } -tracing-opentelemetry = "0.32" +rmcp = { version = "1.4", default-features = false, features = ["schemars", "auth"] } +agent-client-protocol-schema = { version = "0.12", default-features = false, features = ["unstable"] } +agent-client-protocol = { version = "0.11", default-features = false } +arboard = { version = "3", default-features = false } +anyhow = { version = "1.0.102", default-features = false, features = ["std"] } +async-stream = { version = "0.3.6", default-features = false } +async-trait = { version = "0.1.89", default-features = false } +axum = { version = "0.8", default-features = false, features = ["http1", "http2", "json", "tokio", "query"] } +base64 = { version = "0.22.1", default-features = false, features = ["std"] } +bytes = { version = "1.10.1", default-features = false, features = ["std"] } +candle-core = { version = "0.10", default-features = false } +candle-nn = { version = "0.10", default-features = false } +chrono = { version = "0.4.44", default-features = false, features = ["serde", "std"] } +clap = { version = "4.1.14", default-features = false, features = ["derive", "std", "help", "suggestions", "usage", "color", "error-context"] } +dirs = { version = "5", default-features = false } +dotenvy = { version = "0.15.7", default-features = false } +env-lock = { version = "1", default-features = false } +etcetera = { version = "0.11", default-features = false } +fs2 = { version = "0.4", default-features = false } +futures = { version = "0.3.32", default-features = false, features = ["std"] } +http = { version = "1.1", default-features = false, features = ["std"] } +ignore = { version = "0.4.12", default-features = false } +include_dir = { version = "0.7", default-features = false } +indoc = { version = "2", default-features = false } +keyring = { version = "3.6.3", default-features = false, features = ["vendored"] } +lru = { version = "0.16", default-features = false } +once_cell = { version = "1.21.3", default-features = false, features = ["std"] } +rand = { version = "0.8.5", default-features = false, features = ["std"] } +regex = { version = "1.12.3", default-features = false, features = ["std"] } +reqwest = { version = "0.13.2", default-features = false, features = ["multipart", "form"] } +rustls = { version = "0.23.31", default-features = false, features = ["aws_lc_rs", "std"] } +schemars = { version = "1.0.2", default-features = false, features = ["std"] } +serde = { version = "1.0.228", default-features = false, features = ["derive", "std"] } +serde_json = { version = "1.0.145", default-features = false, features = ["std"] } +serde_yaml = { version = "0.9.32", default-features = false } +shellexpand = { version = "3", default-features = false, features = ["base-0", "tilde"] } +strum = { version = "0.27.1", default-features = false, features = ["derive", "std"] } +tempfile = { version = "3.10.1", default-features = false } +thiserror = { version = "1.0.49", default-features = false } +tokio = { version = "1.48", default-features = false } +tokio-stream = { version = "0.1.16", default-features = false } +tokio-util = { version = "0.7.12", default-features = false } +tower-http = { version = "0.6.8", default-features = false } +tracing = { version = "0.1.43", default-features = false, features = ["std"] } +tracing-appender = { version = "0.2.1", default-features = false } +tracing-futures = { version = "0.2.4", default-features = false, features = ["futures-03", "std", "std-future"] } +tracing-subscriber = { version = "0.3.22", default-features = false, features = ["std"] } +urlencoding = { version = "2.1", default-features = false } +utoipa = { version = "4.2", default-features = false } +uuid = { version = "1.18", default-features = false, features = ["v4", "std"] } +webbrowser = { version = "1", default-features = false } +which = { version = "8", default-features = false, features = ["real-sys"] } +winapi = { version = "0.3.9", default-features = false, features = ["wincred", "std"] } +wiremock = { version = "0.6", default-features = false } +zip = { version = "8", default-features = false, features = ["deflate"] } +serial_test = { version = "3", default-features = false } +sha2 = { version = "0.10.8", default-features = false, features = ["std"] } +shell-words = { version = "1", default-features = false, features = ["std"] } +test-case = { version = "3", default-features = false } +url = { version = "2.5.4", default-features = false, features = ["std"] } +opentelemetry = { version = "0.32", default-features = false } +opentelemetry_sdk = { version = "0.32", default-features = false, features = ["metrics"] } +opentelemetry-http = { version = "0.32", default-features = false, features = ["internal-logs", "reqwest"] } +opentelemetry-otlp = { version = "0.32", default-features = false, features = ["http-proto", "internal-logs", "logs", "metrics", "reqwest-client", "trace"] } +opentelemetry-appender-tracing = { version = "0.32", default-features = false, features = ["experimental_span_attributes"] } +opentelemetry-stdout = { version = "0.32", default-features = false, features = ["trace", "metrics", "logs"] } +tracing-opentelemetry = { version = "0.33", default-features = false, features = ["metrics"] } -rayon = "1.12" -tree-sitter = "0.26" -tree-sitter-go = "0.25" -tree-sitter-java = "0.23" -tree-sitter-javascript = "0.25" -tree-sitter-kotlin-ng = "1.1" -tree-sitter-python = "0.25" -tree-sitter-ruby = "0.23" -tree-sitter-rust = "0.24" -tree-sitter-swift = "0.7" -tree-sitter-typescript = "0.23" +rayon = { version = "1.10", default-features = false } +tree-sitter = { version = "0.26", default-features = false, features = ["std"] } +tree-sitter-go = { version = "0.25", default-features = false } +tree-sitter-java = { version = "0.23", default-features = false } +tree-sitter-javascript = { version = "0.25", default-features = false } +tree-sitter-kotlin-ng = { version = "1", default-features = false } +tree-sitter-python = { version = "0.25", default-features = false } +tree-sitter-ruby = { version = "0.23", default-features = false } +tree-sitter-rust = { version = "0.24", default-features = false } +tree-sitter-swift = { version = "0.7", default-features = false } +tree-sitter-typescript = { version = "0.23", default-features = false } + +# llama-cpp-2 doesn't pin the version of its sys crate, so we do it here. it also has breaking changes in patch releases, so we use an exact version pin +llama-cpp-2 = { version = "=0.1.146", default-features = false, features = ["sampler", "mtmd"] } +llama-cpp-sys-2 = { version = "=0.1.146", default-features = false } + +# These are needed because temporal_rs 0.1 (a transitive dep via PCTX) enables unstable features on icu_calendar without pinning the dependency version +# A fix is available in temporal_rs 0.2 but PCTX has not updated +# They are just here to pin the version, and can be removed if PCTX updates temporal_rs +icu_calendar = { version = "=2.1.1", default-features = false } +icu_locale = { version = "=2.1.1", default-features = false } [patch.crates-io] v8 = { path = "vendor/v8" } cudaforge = { git = "https://github.com/jbg/cudaforge", rev = "e7c1967340e40673db98dc9e17da0f04834a456f" } -# TODO: switch to released version in opentelemetry 0.32.0 -# https://github.com/open-telemetry/opentelemetry-rust/issues/3408 -opentelemetry = { git = "https://github.com/open-telemetry/opentelemetry-rust", rev = "345cd74a" } -opentelemetry_sdk = { git = "https://github.com/open-telemetry/opentelemetry-rust", rev = "345cd74a" } -opentelemetry-appender-tracing = { git = "https://github.com/open-telemetry/opentelemetry-rust", rev = "345cd74a" } -opentelemetry-otlp = { git = "https://github.com/open-telemetry/opentelemetry-rust", rev = "345cd74a" } -opentelemetry-stdout = { git = "https://github.com/open-telemetry/opentelemetry-rust", rev = "345cd74a" } diff --git a/Justfile b/Justfile index f615a25ce3e6..8e8f6e49f354 100644 --- a/Justfile +++ b/Justfile @@ -188,7 +188,7 @@ check-acp-schema: generate-acp-types # Generate ACP JSON schema from Rust types generate-acp-schema: @echo "Generating ACP schema..." - cd crates/goose && cargo run --bin generate-acp-schema + cd crates/goose && cargo run --features code-mode,local-inference,aws-providers,telemetry,otel,rustls-tls,system-keyring --bin generate-acp-schema @echo "ACP schema generated: crates/goose/acp-schema.json, crates/goose/acp-meta.json" # Generate ACP TypeScript types from JSON schema (requires generate-acp-schema first) diff --git a/crates/goose-acp-macros/Cargo.toml b/crates/goose-acp-macros/Cargo.toml index 982f87638d2b..760397ddcdb2 100644 --- a/crates/goose-acp-macros/Cargo.toml +++ b/crates/goose-acp-macros/Cargo.toml @@ -12,8 +12,8 @@ description.workspace = true proc-macro = true [dependencies] -quote = "1" -syn = { version = "2", features = ["full", "extra-traits"] } +quote = { version = "1.0.40", default-features = false } +syn = { version = "2.0.104", default-features = false, features = ["full", "extra-traits"] } [lints] workspace = true diff --git a/crates/goose-cli/Cargo.toml b/crates/goose-cli/Cargo.toml index 479957527031..0a93aa2191fc 100644 --- a/crates/goose-cli/Cargo.toml +++ b/crates/goose-cli/Cargo.toml @@ -20,15 +20,15 @@ name = "generate_manpages" path = "src/bin/generate_manpages.rs" [dependencies] -clap_mangen = "0.3.0" +clap_mangen = { version = "0.2", default-features = false } goose = { path = "../goose", default-features = false } -goose-mcp = { path = "../goose-mcp" } +goose-mcp = { path = "../goose-mcp", default-features = false } rmcp = { workspace = true } clap = { workspace = true } -cliclack = "0.5.4" -console = "0.16.1" +cliclack = { version = "0.5", default-features = false } +console = { version = "0.16", default-features = false, features = ["std"] } dotenvy = { workspace = true } -bat = { version = "0.26.1", default-features = false, features = ["regex-onig"] } +bat = { version = "0.26", default-features = false, features = ["regex-onig"] } anyhow = { workspace = true } serde_json = { workspace = true } tokio = { workspace = true } @@ -39,33 +39,34 @@ strum = { workspace = true } tempfile = { workspace = true } etcetera = { workspace = true } rand = { workspace = true } -rustyline = "18.0.0" +rustyline = { version = "18", default-features = false, features = ["custom-bindings", "with-dirs", "with-file-history"] } tracing = { workspace = true } chrono = { workspace = true } tracing-subscriber = { workspace = true, features = ["env-filter", "fmt", "json", "time"] } -shlex = "2.0.1" +shlex = { version = "1.3", default-features = false, features = ["std"] } async-trait = { workspace = true } base64 = { workspace = true } regex = { workspace = true } -tar = "0.4.46" -reqwest = { workspace = true, features = ["blocking"], default-features = false } +tar = { version = "0.4.46", default-features = false } +reqwest = { workspace = true, features = ["blocking"] } zip = { workspace = true } -bzip2 = "0.6" +bzip2 = { version = "0.6", default-features = false, features = ["default"] } webbrowser = { workspace = true } -indicatif = "0.18.1" +indicatif = { version = "0.18", default-features = false } tokio-util = { workspace = true, features = ["compat", "rt"] } -anstream = "1.0.0" -open = "5.3.5" +anstream = { version = "1", default-features = false, features = ["auto"] } +open = { version = "5", default-features = false } url = { workspace = true } urlencoding = { workspace = true } -clap_complete = "4.6.5" -comfy-table = "7.2.2" +clap_complete = { version = "4", default-features = false } +comfy-table = { version = "7", default-features = false } sha2 = { workspace = true } -sigstore-verify = { version = "=0.8.0", default-features = false } +sigstore-verify = { version = "0.8", default-features = false, optional = true } axum.workspace = true -clap_complete_nushell = "4.6.0" +clap_complete_nushell = { version = "4", default-features = false } [target.'cfg(target_os = "windows")'.dependencies] +anstream = { version = "1", default-features = false, features = ["wincon"] } winapi = { workspace = true } [features] @@ -74,9 +75,11 @@ default = [ "local-inference", "aws-providers", "telemetry", + "nostr", "otel", "rustls-tls", "system-keyring", + "update", ] code-mode = ["goose/code-mode"] local-inference = ["goose/local-inference"] @@ -84,20 +87,22 @@ aws-providers = ["goose/aws-providers"] cuda = ["goose/cuda", "local-inference"] vulkan = ["goose/vulkan", "local-inference"] telemetry = ["goose/telemetry"] +nostr = ["goose/nostr"] otel = ["goose/otel"] system-keyring = ["goose/system-keyring"] +update = ["dep:sigstore-verify"] portable-default = ["rustls-tls", "aws-providers", "telemetry", "otel"] # disables the update command disable-update = [] rustls-tls = [ "reqwest/rustls", - "sigstore-verify/rustls", + "sigstore-verify?/rustls", "goose/rustls-tls", "goose-mcp/rustls-tls", ] native-tls = [ "reqwest/native-tls", - "sigstore-verify/native-tls", + "sigstore-verify?/native-tls", "goose/native-tls", "goose-mcp/native-tls", ] diff --git a/crates/goose-cli/src/cli.rs b/crates/goose-cli/src/cli.rs index abc08a7406a4..0615d8ac3806 100644 --- a/crates/goose-cli/src/cli.rs +++ b/crates/goose-cli/src/cli.rs @@ -935,6 +935,7 @@ enum Command { }, /// Update the goose CLI version + #[cfg(feature = "update")] #[command(about = "Update the goose CLI version")] Update { /// Update to canary version @@ -1269,6 +1270,7 @@ fn get_command_name(command: &Option) -> &'static str { Some(Command::Run { .. }) => "run", Some(Command::Gateway { .. }) => "gateway", Some(Command::Schedule { .. }) => "schedule", + #[cfg(feature = "update")] Some(Command::Update { .. }) => "update", Some(Command::Recipe { .. }) => "recipe", Some(Command::Plugin { .. }) => "plugin", @@ -2099,6 +2101,7 @@ pub async fn cli() -> anyhow::Result<()> { } Some(Command::Gateway { command }) => handle_gateway_command(command).await, Some(Command::Schedule { command }) => handle_schedule_command(command).await, + #[cfg(feature = "update")] Some(Command::Update { canary, reconfigure, diff --git a/crates/goose-cli/src/commands/mod.rs b/crates/goose-cli/src/commands/mod.rs index 76d4d79cbcc8..09102bc72c1f 100644 --- a/crates/goose-cli/src/commands/mod.rs +++ b/crates/goose-cli/src/commands/mod.rs @@ -10,4 +10,5 @@ pub mod schedule; pub mod session; pub mod term; pub mod tui; +#[cfg(feature = "update")] pub mod update; diff --git a/crates/goose-cli/src/commands/session.rs b/crates/goose-cli/src/commands/session.rs index 9990d376e8cb..01efebf21a82 100644 --- a/crates/goose-cli/src/commands/session.rs +++ b/crates/goose-cli/src/commands/session.rs @@ -3,8 +3,11 @@ use anyhow::{Context, Result}; use cliclack::{confirm, multiselect, select}; use etcetera::home_dir; +#[cfg(feature = "nostr")] use goose::config::Config; -use goose::session::{generate_diagnostics, nostr_share, Session, SessionManager, SessionType}; +#[cfg(feature = "nostr")] +use goose::session::nostr_share; +use goose::session::{generate_diagnostics, Session, SessionManager, SessionType}; use goose::utils::safe_truncate; use regex::Regex; use std::fs; @@ -218,7 +221,7 @@ pub async fn handle_session_export( output_path: Option, format: String, nostr: bool, - relays: Vec, + #[cfg_attr(not(feature = "nostr"), allow(unused_variables))] relays: Vec, ) -> Result<()> { let session_manager = SessionManager::instance(); let session = match session_manager.get_session(&session_id, true).await { @@ -244,6 +247,7 @@ pub async fn handle_session_export( _ => return Err(anyhow::anyhow!("Unsupported format: {}", format)), }; + #[cfg(feature = "nostr")] if nostr { if format != "json" { return Err(anyhow::anyhow!( @@ -266,6 +270,10 @@ pub async fn handle_session_export( println!("{}", share.deeplink); return Ok(()); } + #[cfg(not(feature = "nostr"))] + if nostr { + return Err(anyhow::anyhow!("goose was not built with nostr support")); + } if let Some(output_path) = output_path { fs::write(&output_path, output).with_context(|| { @@ -281,7 +289,12 @@ pub async fn handle_session_export( pub async fn handle_session_import(input: String, nostr: bool) -> Result<()> { let json = if nostr || input.starts_with("goose://sessions/nostr") { - nostr_share::import_session_json_from_deeplink(&input).await? + #[cfg(feature = "nostr")] + { + nostr_share::import_session_json_from_deeplink(&input).await? + } + #[cfg(not(feature = "nostr"))] + return Err(anyhow::anyhow!("goose was not built with nostr support")); } else { fs::read_to_string(&input) .with_context(|| format!("Failed to read session import file: {input}"))? diff --git a/crates/goose-mcp/Cargo.toml b/crates/goose-mcp/Cargo.toml index 340a14c32726..610e9986b68c 100644 --- a/crates/goose-mcp/Cargo.toml +++ b/crates/goose-mcp/Cargo.toml @@ -12,6 +12,7 @@ description.workspace = true workspace = true [features] +default = [] rustls-tls = ["reqwest/rustls"] native-tls = ["reqwest/native-tls"] @@ -28,15 +29,15 @@ serde = { workspace = true } serde_json = { workspace = true } schemars = { workspace = true } indoc = { workspace = true } -reqwest = { workspace = true, features = ["json", "system-proxy"], default-features = false } +reqwest = { workspace = true, features = ["json", "system-proxy"] } chrono = { workspace = true } etcetera = { workspace = true } tempfile = { workspace = true } include_dir = { workspace = true } once_cell = { workspace = true } -lopdf = "0.40.0" -docx-rs = "0.4.20" -image = { version = "0.25.10", features = ["jpeg"] } -umya-spreadsheet = "2.2.3" +lopdf = { version = "0.40", default-features = false } +docx-rs = { version = "0.4.18", default-features = false, features = ["image"] } +image = { version = "0.24.4", default-features = false, features = ["bmp", "dds", "dxt", "farbfeld", "gif", "hdr", "ico", "jpeg", "jpeg_rayon", "openexr", "png", "pnm", "tga", "tiff", "webp"] } +umya-spreadsheet = { version = "2", default-features = false } shell-words = { workspace = true } -process-wrap = { version = "9.1.0", features = ["std"] } +process-wrap = { version = "9", default-features = false, features = ["std"] } diff --git a/crates/goose-server/Cargo.toml b/crates/goose-server/Cargo.toml index 991e5f475bce..c1f04fc6dddf 100644 --- a/crates/goose-server/Cargo.toml +++ b/crates/goose-server/Cargo.toml @@ -17,6 +17,7 @@ default = [ "local-inference", "aws-providers", "telemetry", + "nostr", "otel", "rustls-tls", "system-keyring", @@ -27,6 +28,7 @@ aws-providers = ["goose/aws-providers"] cuda = ["goose/cuda", "local-inference"] vulkan = ["goose/vulkan", "local-inference"] telemetry = ["goose/telemetry"] +nostr = ["goose/nostr"] otel = ["goose/otel"] system-keyring = ["goose/system-keyring"] portable-default = ["rustls-tls", "aws-providers", "telemetry", "otel"] @@ -50,7 +52,7 @@ native-tls = [ [dependencies] goose = { path = "../goose", default-features = false } -goose-mcp = { path = "../goose-mcp" } +goose-mcp = { path = "../goose-mcp", default-features = false } rmcp = { workspace = true } axum = { workspace = true, features = ["ws", "macros"] } tokio = { workspace = true } @@ -66,31 +68,31 @@ anyhow = { workspace = true } bytes = { workspace = true } http = { workspace = true } base64 = { workspace = true } -config = { version = "0.15.23", features = ["toml"] } +config = { version = "0.15", default-features = false, features = ["toml"] } thiserror = { workspace = true } clap = { workspace = true } serde_yaml = { workspace = true } utoipa = { workspace = true, features = ["axum_extras", "chrono"] } -reqwest = { workspace = true, features = ["json", "blocking", "multipart", "system-proxy"], default-features = false } +reqwest = { workspace = true, features = ["json", "blocking", "multipart", "system-proxy"] } tokio-util = { workspace = true } -serde_path_to_error = "0.1.20" -tokio-tungstenite = { version = "0.29.0" } +serde_path_to_error = { version = "0.1.8", default-features = false } +tokio-tungstenite = { version = "0.29", default-features = false, features = ["connect"] } url = { workspace = true } rand = { workspace = true } -hex = "0.4.3" -subtle = "2.6" -socket2 = "0.6.1" +hex = { version = "0.4.3", default-features = false, features = ["std"] } +subtle = { version = "2.5", default-features = false, features = ["std"] } +socket2 = { version = "0.6", default-features = false } fs2 = { workspace = true } -rustls = { version = "0.23", features = ["aws_lc_rs"], optional = true } +rustls = { workspace = true, optional = true } uuid = { workspace = true } -rcgen = "0.14" -axum-server = { version = "0.8.0" } -aws-lc-rs = { version = "1.17.0", optional = true } -openssl = { version = "0.10", optional = true } -pem = "3.0.6" +rcgen = { version = "0.14", default-features = false, features = ["aws_lc_rs", "crypto", "pem"] } +axum-server = { version = "0.8", default-features = false } +aws-lc-rs = { version = "1.17", default-features = false, optional = true } +openssl = { version = "0.10.66", default-features = false, optional = true } +pem = { version = "3.0.2", default-features = false, features = ["std"] } [target.'cfg(windows)'.dependencies] -winreg = { version = "0.56.0" } +winreg = { version = "0.56", default-features = false } [[bin]] name = "goosed" @@ -101,7 +103,7 @@ name = "generate_schema" path = "src/bin/generate_schema.rs" [dev-dependencies] -tower = "0.5.2" +tower = { version = "0.5.2", default-features = false } [package.metadata.cargo-machete] ignored = [ diff --git a/crates/goose-server/src/routes/agent.rs b/crates/goose-server/src/routes/agent.rs index 1b9f83913b76..f69f8a386625 100644 --- a/crates/goose-server/src/routes/agent.rs +++ b/crates/goose-server/src/routes/agent.rs @@ -708,7 +708,9 @@ async fn agent_add_extension( State(state): State>, Json(request): Json, ) -> Result { + #[cfg(feature = "telemetry")] let extension_name = request.config.name(); + let agent = state.get_agent(request.session_id.clone()).await?; agent diff --git a/crates/goose-server/src/routes/session.rs b/crates/goose-server/src/routes/session.rs index 0ae6f9a75f74..96bbc9590c52 100644 --- a/crates/goose-server/src/routes/session.rs +++ b/crates/goose-server/src/routes/session.rs @@ -11,6 +11,7 @@ use axum::{ }; use goose::agents::ExtensionConfig; use goose::recipe::Recipe; +#[cfg(feature = "nostr")] use goose::session::nostr_share; use goose::session::session_manager::{SessionInsights, SessionType}; use goose::session::{EnabledExtensionsState, Session}; @@ -51,6 +52,7 @@ pub struct ImportSessionRequest { json: String, } +#[cfg_attr(not(feature = "nostr"), allow(dead_code))] #[derive(Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct ShareSessionNostrRequest { @@ -67,6 +69,7 @@ pub struct ShareSessionNostrResponse { relays: Vec, } +#[cfg_attr(not(feature = "nostr"), allow(dead_code))] #[derive(Deserialize, ToSchema)] #[serde(rename_all = "camelCase")] pub struct ImportSessionNostrRequest { @@ -387,6 +390,7 @@ async fn import_session( Ok(Json(session)) } +#[cfg_attr(not(feature = "nostr"), allow(unused_variables))] #[utoipa::path( post, path = "/sessions/{session_id}/share/nostr", @@ -410,25 +414,32 @@ async fn share_session_nostr( Path(session_id): Path, Json(request): Json, ) -> Result, StatusCode> { - let exported = state - .session_manager() - .export_session(&session_id) - .await - .map_err(|_| StatusCode::NOT_FOUND)?; + #[cfg(feature = "nostr")] + { + let exported = state + .session_manager() + .export_session(&session_id) + .await + .map_err(|_| StatusCode::NOT_FOUND)?; - let relays = nostr_share::resolve_relays(request.relays, goose::config::Config::global()); - let share = nostr_share::publish_session_json(&exported, relays) - .await - .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + let relays = nostr_share::resolve_relays(request.relays, goose::config::Config::global()); + let share = nostr_share::publish_session_json(&exported, relays) + .await + .map_err(|_| StatusCode::INTERNAL_SERVER_ERROR)?; + + Ok(Json(ShareSessionNostrResponse { + deeplink: share.deeplink, + nevent: share.nevent, + event_id: share.event_id, + relays: share.relays, + })) + } - Ok(Json(ShareSessionNostrResponse { - deeplink: share.deeplink, - nevent: share.nevent, - event_id: share.event_id, - relays: share.relays, - })) + #[cfg(not(feature = "nostr"))] + Err(StatusCode::NOT_FOUND) } +#[cfg_attr(not(feature = "nostr"), allow(unused_variables))] #[utoipa::path( post, path = "/sessions/import/nostr", @@ -448,16 +459,22 @@ async fn import_session_nostr( State(state): State>, Json(request): Json, ) -> Result, StatusCode> { - let json = nostr_share::import_session_json_from_deeplink(&request.deeplink) - .await - .map_err(|_| StatusCode::BAD_REQUEST)?; - let session = state - .session_manager() - .import_session(&json, Some(SessionType::User)) - .await - .map_err(|_| StatusCode::BAD_REQUEST)?; + #[cfg(feature = "nostr")] + { + let json = nostr_share::import_session_json_from_deeplink(&request.deeplink) + .await + .map_err(|_| StatusCode::BAD_REQUEST)?; + let session = state + .session_manager() + .import_session(&json, Some(SessionType::User)) + .await + .map_err(|_| StatusCode::BAD_REQUEST)?; - Ok(Json(session)) + Ok(Json(session)) + } + + #[cfg(not(feature = "nostr"))] + Err(StatusCode::NOT_FOUND) } #[utoipa::path( diff --git a/crates/goose-server/src/routes/telemetry.rs b/crates/goose-server/src/routes/telemetry.rs index 697f579cf049..222449ecbf3a 100644 --- a/crates/goose-server/src/routes/telemetry.rs +++ b/crates/goose-server/src/routes/telemetry.rs @@ -8,6 +8,7 @@ use utoipa::ToSchema; use crate::state::AppState; +#[cfg_attr(not(feature = "telemetry"), allow(dead_code))] #[derive(Debug, Deserialize, ToSchema)] pub struct TelemetryEventRequest { pub event_name: String, @@ -15,6 +16,7 @@ pub struct TelemetryEventRequest { pub properties: HashMap, } +#[cfg_attr(not(feature = "telemetry"), allow(unused_variables))] #[utoipa::path( post, path = "/telemetry/event", @@ -27,15 +29,17 @@ async fn send_telemetry_event( State(_state): State>, Json(request): Json, ) -> StatusCode { - let event_name = request.event_name; - let properties = request.properties; - #[cfg(feature = "telemetry")] - tokio::spawn(async move { - if let Err(e) = emit_event(&event_name, properties).await { - tracing::debug!("Failed to send telemetry event: {}", e); - } - }); + { + let event_name = request.event_name; + let properties = request.properties; + + tokio::spawn(async move { + if let Err(e) = emit_event(&event_name, properties).await { + tracing::debug!("Failed to send telemetry event: {}", e); + } + }); + } StatusCode::ACCEPTED } diff --git a/crates/goose-server/src/state.rs b/crates/goose-server/src/state.rs index 3be8934adbce..a9dee95f65ce 100644 --- a/crates/goose-server/src/state.rs +++ b/crates/goose-server/src/state.rs @@ -5,7 +5,9 @@ use goose::scheduler_trait::SchedulerTrait; use goose::session::SessionManager; use std::collections::{HashMap, HashSet}; use std::path::PathBuf; -use std::sync::{Arc, OnceLock}; +use std::sync::Arc; +#[cfg(feature = "local-inference")] +use std::sync::OnceLock; use tokio::sync::Mutex; use tokio::task::JoinHandle; diff --git a/crates/goose/Cargo.toml b/crates/goose/Cargo.toml index e3d5b42efe7d..7e391afcdb8d 100644 --- a/crates/goose/Cargo.toml +++ b/crates/goose/Cargo.toml @@ -9,15 +9,7 @@ repository.workspace = true description.workspace = true [features] -default = [ - "code-mode", - "local-inference", - "aws-providers", - "telemetry", - "otel", - "rustls-tls", - "system-keyring", -] +default = [] telemetry = [] otel = [ "dep:tracing-opentelemetry", @@ -43,6 +35,7 @@ aws-providers = [ "dep:aws-smithy-types", "dep:aws-sdk-bedrockruntime", "dep:aws-sdk-sagemakerruntime", + "dep:smithy-transport-reqwest", ] cuda = ["local-inference", "candle-core/cuda", "candle-nn/cuda", "llama-cpp-2/cuda"] vulkan = ["local-inference", "llama-cpp-2/vulkan"] @@ -50,6 +43,7 @@ rustls-tls = [ "dep:rustls", "reqwest/rustls", "rmcp/reqwest", + "smithy-transport-reqwest?/rustls", "sqlx/runtime-tokio-rustls", "jsonwebtoken/aws_lc_rs", "oauth2/reqwest", @@ -62,6 +56,7 @@ native-tls = [ "dep:sec1", "reqwest/native-tls", "rmcp/reqwest-native-tls", + "smithy-transport-reqwest?/native-tls", "sqlx/runtime-tokio-native-tls", "jsonwebtoken/rust_crypto", "oauth2/reqwest", @@ -69,7 +64,7 @@ native-tls = [ ] system-keyring = ["dep:keyring"] portable-default = ["rustls-tls", "aws-providers", "telemetry", "otel"] - +nostr = ["dep:nostr", "dep:nostr-sdk"] [lints] workspace = true @@ -89,30 +84,30 @@ anyhow = { workspace = true } thiserror = { workspace = true } futures = { workspace = true } dirs = { workspace = true } -reqwest = { workspace = true, features = ["json", "cookies", "gzip", "brotli", "deflate", "zstd", "charset", "http2", "stream", "blocking", "multipart", "system-proxy"], default-features = false } +reqwest = { workspace = true, features = ["json", "cookies", "gzip", "brotli", "deflate", "zstd", "charset", "http2", "stream", "blocking", "multipart", "system-proxy"] } tokio = { workspace = true } serde = { workspace = true } serde_json = { workspace = true } -serde_urlencoded = "0.7" -jsonschema = "0.30.0" +serde_urlencoded = { version = "0.7.1", default-features = false } +jsonschema = { version = "0.30", default-features = false } uuid = { workspace = true, features = ["v7"] } regex = { workspace = true } async-trait = { workspace = true } async-stream = { workspace = true } -minijinja = { version = "2.20.0", features = ["loader"] } +minijinja = { version = "2.18", default-features = false, features = ["loader", "multi_template", "serde"] } include_dir = { workspace = true } -tiktoken-rs = "0.11.0" +tiktoken-rs = { version = "0.11", default-features = false } chrono = { workspace = true } clap = { workspace = true } indoc = { workspace = true } -nanoid = "0.5" +nanoid = { version = "0.5", default-features = false } sha2 = { workspace = true } base64 = { workspace = true } url = { workspace = true } axum = { workspace = true, features = ["ws"] } webbrowser = { workspace = true } tracing = { workspace = true } -tracing-subscriber = { workspace = true, features = ["env-filter", "fmt", "json", "time"] } +tracing-subscriber = { workspace = true, features = ["ansi", "env-filter", "fmt", "json", "time"] } tracing-futures = { workspace = true } tracing-opentelemetry = { workspace = true, optional = true } opentelemetry = { workspace = true, optional = true } @@ -120,19 +115,19 @@ opentelemetry_sdk = { workspace = true, optional = true } opentelemetry-appender-tracing = { workspace = true, optional = true } opentelemetry-otlp = { workspace = true, optional = true } opentelemetry-stdout = { workspace = true, optional = true } -keyring = { version = "3.6.2", features = ["vendored"], optional = true } +keyring = { workspace = true, optional = true } serde_yaml = { workspace = true } strum = { workspace = true } once_cell = { workspace = true } etcetera = { workspace = true } -fs-err = "3" -goose-sdk = { path = "../goose-sdk" } +fs-err = { version = "3.1", default-features = false } +goose-sdk = { path = "../goose-sdk", default-features = false } rand = { workspace = true } utoipa = { workspace = true, features = ["chrono"] } -tokio-cron-scheduler = "0.15.1" +tokio-cron-scheduler = { version = "0.15", default-features = false } urlencoding = { workspace = true } -v_htmlescape = "0.15" -sqlx = { version = "0.8", default-features = false, features = [ +v_htmlescape = { version = "0.17", default-features = false, features = ["std"] } +sqlx = { version = "0.8.5", default-features = false, features = [ "sqlite", "chrono", "json", @@ -141,43 +136,46 @@ sqlx = { version = "0.8", default-features = false, features = [ ] } # For Bedrock provider (optional, behind "aws-providers" feature) -aws-config = { version = "=1.8.16", features = ["behavior-version-latest"], optional = true } -aws-smithy-types = { version = "=1.4.8", optional = true } -aws-sdk-bedrockruntime = { version = "=1.131.0", default-features = false, features = ["default-https-client", "rt-tokio"], optional = true } +aws-config = { version = "1.6", default-features = false, features = ["credentials-process", "rt-tokio", "sso", "behavior-version-latest"], optional = true } +aws-smithy-types = { version = "1.3.4", default-features = false, features = ["rt-tokio"], optional = true } +aws-sdk-bedrockruntime = { version = "1.119", default-features = false, features = ["rt-tokio"], optional = true } +smithy-transport-reqwest = { version = "0.1", default-features = false, features = ["http2", "system-proxy"], optional = true } # For SageMaker TGI provider (optional, behind "aws-providers" feature) -aws-sdk-sagemakerruntime = { version = "1.102.0", default-features = false, features = ["default-https-client", "rt-tokio"], optional = true } +aws-sdk-sagemakerruntime = { version = "1.64", default-features = false, features = ["rt-tokio"], optional = true } # For GCP Vertex AI provider auth -jsonwebtoken = { version = "10.4.0", default-features = false, features = ["use_pem"] } +jsonwebtoken = { version = "10.2", default-features = false, features = ["use_pem"] } -blake3 = "1.8" +blake3 = { version = "1", default-features = false, features = ["std"] } fs2 = { workspace = true } tokio-stream = { workspace = true, features = ["io-util"] } -tempfile.workspace = true -dashmap = "6.2" -ahash = "0.8" +tempfile = { workspace = true } +dashmap = { version = "6", default-features = false } +ahash = { version = "0.8.11", default-features = false, features = ["std"] } tokio-util = { workspace = true, features = ["compat"] } agent-client-protocol-schema = { workspace = true } agent-client-protocol = { workspace = true, features = ["unstable"] } -unicode-normalization = "0.1" +unicode-normalization = { version = "0.1.22", default-features = false, features = ["std"] } # For local Whisper transcription (optional, behind "local-inference" feature) -candle-core = { version = "0.10.2", default-features = false, optional = true } -candle-nn = { version = "0.10.2", default-features = false, optional = true } -candle-transformers = { version = "0.10.2", default-features = false, optional = true } -byteorder = { version = "1.5.0", optional = true } -tokenizers = { version = "0.21.0", default-features = false, features = ["onig"], optional = true } -symphonia = { version = "0.5", features = ["all"], optional = true } -rubato = { version = "0.16", optional = true } +candle-core = { workspace = true, optional = true } +candle-nn = { workspace = true, optional = true } +candle-transformers = { version = "0.10", default-features = false, optional = true } +byteorder = { version = "1.5", default-features = false, features = ["std"], optional = true } +tokenizers = { version = "0.21", default-features = false, features = ["onig"], optional = true } +symphonia = { version = "0.5", default-features = false, features = ["aac", "adpcm", "alac", "isomp4", "mkv", "mp3", "pcm", "vorbis", "wav"], optional = true } +rubato = { version = "0.16", default-features = false, optional = true } zip = { workspace = true } -sys-info = "0.9" +sys-info = { version = "0.9", default-features = false } + +llama-cpp-2 = { workspace = true, optional = true } schemars = { workspace = true, features = [ "derive", ] } shellexpand = { workspace = true } -indexmap = "2.14.0" +indexmap = { version = "2.9", default-features = false, features = ["std"] } ignore = { workspace = true } rayon = { workspace = true } tree-sitter = { workspace = true } @@ -191,59 +189,64 @@ tree-sitter-rust = { workspace = true } tree-sitter-swift = { workspace = true } tree-sitter-typescript = { workspace = true } which = { workspace = true } -pctx_code_mode = { version = "^0.3.0", optional = true } -pulldown-cmark = "0.13.4" -llama-cpp-2 = { version = "0.1.145", features = ["sampler", "mtmd"], optional = true } -encoding_rs = "0.8.35" -pastey = "0.2.3" +pulldown-cmark = { version = "0.13", default-features = false } +encoding_rs = { version = "0.8.35", default-features = false } +pastey = { version = "0.2", default-features = false } shell-words = { workspace = true } -pem = { version = "3", optional = true } -pkcs1 = { version = "0.7", default-features = false, features = ["pkcs8"], optional = true } -pkcs8 = { version = "0.10", default-features = false, features = ["alloc"], optional = true } -sec1 = { version = "0.7", default-features = false, features = ["der", "pkcs8"], optional = true } -goose-acp-macros = { path = "../goose-acp-macros" } +pem = { version = "3.0.2", default-features = false, features = ["std"], optional = true } +pkcs1 = { version = "0.7.5", default-features = false, features = ["pkcs8", "std"], optional = true } +pkcs8 = { version = "0.10.2", default-features = false, features = ["alloc", "std"], optional = true } +sec1 = { version = "0.7", default-features = false, features = ["der", "pkcs8", "std"], optional = true } +goose-acp-macros = { path = "../goose-acp-macros", default-features = false } tower-http = { workspace = true, features = ["cors"] } -http-body-util = "0.1.3" -tracing-appender.workspace = true -process-wrap = { version = "9.1.0", features = ["std"] } -nostr = { version = "0.44.3", features = ["nip44"] } -nostr-sdk = { version = "0.44.1", features = ["nip44"] } -rustls = { version = "0.23", features = ["aws_lc_rs"], optional = true } +http-body-util = { version = "0.1.2", default-features = false } +tracing-appender = { workspace = true } +process-wrap = { version = "9", default-features = false, features = ["std"] } +nostr = { version = "0.44", default-features = false, features = ["nip44", "std"], optional = true } +nostr-sdk = { version = "0.44", default-features = false, features = ["nip44"], optional = true } +rustls = { workspace = true, optional = true } +pctx_code_mode = { version = "0.3", default-features = false, optional = true } + +# These are needed because temporal_rs 0.1 (a transitive dep via PCTX) enables unstable features on icu_calendar without pinning the dependency version +# A fix is available in temporal_rs 0.2 but PCTX has not updated +# They are just here to pin the version, and can be removed if PCTX updates temporal_rs +icu_calendar = { version = "=2.1.1", default-features = false } +icu_locale = { version = "=2.1.1", default-features = false } [target.'cfg(target_os = "windows")'.dependencies] winapi = { workspace = true } -keyring = { version = "3.6.2", features = ["windows-native"], optional = true } +keyring = { workspace = true, features = ["windows-native"], optional = true } # Platform-specific GPU acceleration for Whisper and local inference [target.'cfg(target_os = "macos")'.dependencies] -candle-core = { version = "0.10.2", default-features = false, features = ["metal"], optional = true } -candle-nn = { version = "0.10.2", default-features = false, features = ["metal"], optional = true } -llama-cpp-2 = { version = "0.1.145", features = ["sampler", "metal", "mtmd"], optional = true } -keyring = { version = "3.6.2", features = ["apple-native"], optional = true } +candle-core = { workspace = true, features = ["metal"], optional = true } +candle-nn = { workspace = true, features = ["metal"], optional = true } +llama-cpp-2 = { workspace = true, features = ["sampler", "metal", "mtmd"], optional = true } +keyring = { workspace = true, features = ["apple-native"], optional = true } [target.'cfg(target_os = "linux")'.dependencies] -keyring = { version = "3.6.2", features = ["sync-secret-service"], optional = true } -libc = "0.2.186" +keyring = { workspace = true, features = ["sync-secret-service"], optional = true } +libc = { version = "0.2.182", default-features = false, features = ["std"] } [dev-dependencies] serial_test = { workspace = true } -mockall = "0.14.0" +mockall = { version = "0.13", default-features = false } wiremock = { workspace = true } tokio = { workspace = true } tokio-util = { workspace = true, features = ["compat"] } dotenvy = { workspace = true } -ctor = "1.0.6" +ctor = { version = "0.2", default-features = false } test-case = { workspace = true } env-lock = { workspace = true } rmcp = { workspace = true, features = ["transport-streamable-http-server"] } opentelemetry_sdk = { workspace = true, features = ["testing"] } -goose-test-support = { path = "../goose-test-support" } -bytes.workspace = true -http.workspace = true -goose-mcp = { path = "../goose-mcp" } -insta = "1.47.2" -dtor = "1.0.3" +goose-test-support = { path = "../goose-test-support", default-features = false } +bytes = { workspace = true } +http = { workspace = true } +goose-mcp = { path = "../goose-mcp", default-features = false } +insta = { version = "1", default-features = false } +dtor = { version = "1.0.3", default-features = false, features = ["proc_macro"] } [[example]] name = "agent" @@ -271,12 +274,11 @@ name = "generate-acp-schema" path = "src/bin/generate_acp_schema.rs" [package.metadata.cargo-machete] - ignored = [ # Used only on windows "winapi", - # Used to provide extras imports for agent-client-protocol - "agent-client-protocol-schema", - # Used via http transport - "http-body-util", + + # Included only to pin version + "icu_calendar", + "icu_locale", ] diff --git a/crates/goose/src/lib.rs b/crates/goose/src/lib.rs index 02696312c942..aaee33764927 100644 --- a/crates/goose/src/lib.rs +++ b/crates/goose/src/lib.rs @@ -1,6 +1,3 @@ -#[cfg(not(any(feature = "rustls-tls", feature = "native-tls")))] -compile_error!("At least one of `rustls-tls` or `native-tls` features must be enabled"); - #[cfg(all(feature = "rustls-tls", feature = "native-tls"))] compile_error!("Features `rustls-tls` and `native-tls` are mutually exclusive"); diff --git a/crates/goose/src/otel/otlp.rs b/crates/goose/src/otel/otlp.rs index 234617941ec9..c93049679b65 100644 --- a/crates/goose/src/otel/otlp.rs +++ b/crates/goose/src/otel/otlp.rs @@ -1,6 +1,6 @@ use opentelemetry::trace::TracerProvider; use opentelemetry::{global, KeyValue}; -use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge; +use opentelemetry_appender_tracing::layer::{OpenTelemetryTracingBridge, TracingSpanAttributes}; use opentelemetry_sdk::logs::{SdkLogger, SdkLoggerProvider}; use opentelemetry_sdk::metrics::{SdkMeterProvider, Temporality}; use opentelemetry_sdk::propagation::TraceContextPropagator; @@ -242,7 +242,7 @@ fn create_otlp_logs_layer() -> OtlpResult { }; let bridge = OpenTelemetryTracingBridge::builder(&logger_provider) - .with_span_attribute_allowlist(["session.id"]) + .with_tracing_span_attributes(TracingSpanAttributes::allowlist(["session.id"])) .build(); *LOGGER_PROVIDER.lock().unwrap_or_else(|e| e.into_inner()) = Some(logger_provider); diff --git a/crates/goose/src/providers/api_client.rs b/crates/goose/src/providers/api_client.rs index 86109073ad86..f6bc62d47083 100644 --- a/crates/goose/src/providers/api_client.rs +++ b/crates/goose/src/providers/api_client.rs @@ -4,10 +4,13 @@ use anyhow::Result; use async_trait::async_trait; use reqwest::{ header::{HeaderMap, HeaderName, HeaderValue}, - Certificate, Client, Identity, Response, StatusCode, + Client, Response, StatusCode, }; +#[cfg(any(feature = "rustls-tls", feature = "native-tls"))] +use reqwest::{Certificate, Identity}; use serde_json::Value; use std::fmt; +#[cfg(any(feature = "rustls-tls", feature = "native-tls"))] use std::fs::read_to_string; use std::path::PathBuf; use std::time::Duration; @@ -113,7 +116,8 @@ impl TlsConfig { self.client_identity.is_some() || self.ca_cert_path.is_some() } - pub fn load_identity(&self) -> Result> { + #[cfg(any(feature = "rustls-tls", feature = "native-tls"))] + fn load_identity(&self) -> Result> { if let Some(cert_key_pair) = &self.client_identity { let cert_pem = read_to_string(&cert_key_pair.cert_path) .map_err(|e| anyhow::anyhow!("Failed to read client certificate: {}", e))?; @@ -142,7 +146,8 @@ impl TlsConfig { } } - pub fn load_ca_certificates(&self) -> Result> { + #[cfg(any(feature = "rustls-tls", feature = "native-tls"))] + fn load_ca_certificates(&self) -> Result> { match &self.ca_cert_path { Some(ca_path) => { let ca_pem = read_to_string(ca_path) @@ -323,6 +328,7 @@ impl ApiClient { } /// Configure TLS settings on a reqwest ClientBuilder + #[cfg(any(feature = "rustls-tls", feature = "native-tls"))] fn configure_tls( mut client_builder: reqwest::ClientBuilder, tls_config: &TlsConfig, @@ -342,6 +348,20 @@ impl ApiClient { Ok(client_builder) } + /// Reject custom TLS settings when goose is compiled without a TLS backend. + #[cfg(not(any(feature = "rustls-tls", feature = "native-tls")))] + fn configure_tls( + client_builder: reqwest::ClientBuilder, + tls_config: &TlsConfig, + ) -> Result { + if tls_config.is_configured() { + return Err(anyhow::anyhow!( + "Custom TLS configuration requires the `rustls-tls` or `native-tls` feature" + )); + } + Ok(client_builder) + } + pub fn with_headers(mut self, headers: HeaderMap) -> Result { self.default_headers = headers; self.rebuild_client()?; diff --git a/crates/goose/src/providers/bedrock.rs b/crates/goose/src/providers/bedrock.rs index 4a5a733ba741..13be0e79554d 100644 --- a/crates/goose/src/providers/bedrock.rs +++ b/crates/goose/src/providers/bedrock.rs @@ -17,6 +17,7 @@ use futures::future::BoxFuture; use reqwest::header::HeaderValue; use rmcp::model::Tool; use serde_json::Value; +use smithy_transport_reqwest::ReqwestHttpClient; use super::formats::bedrock::{ from_bedrock_message, from_bedrock_usage, to_bedrock_message_with_caching, @@ -98,7 +99,8 @@ impl BedrockProvider { }; // Use load_defaults() which supports AWS SSO, profiles, and environment variables - let mut loader = aws_config::defaults(aws_config::BehaviorVersion::latest()); + let mut loader = aws_config::defaults(aws_config::BehaviorVersion::latest()) + .http_client(ReqwestHttpClient::new()); if let Ok(profile_name) = config.get_param::("AWS_PROFILE") { if !profile_name.is_empty() { diff --git a/crates/goose/src/providers/chatgpt_codex.rs b/crates/goose/src/providers/chatgpt_codex.rs index 1aa29862b01b..17cd31bc07fe 100644 --- a/crates/goose/src/providers/chatgpt_codex.rs +++ b/crates/goose/src/providers/chatgpt_codex.rs @@ -624,7 +624,7 @@ fn html_success() -> String { } fn html_error(error: &str) -> String { - let safe_error = v_htmlescape::escape(error).to_string(); + let safe_error = v_htmlescape::escape_fmt(error); format!( r#" diff --git a/crates/goose/src/providers/gemini_oauth.rs b/crates/goose/src/providers/gemini_oauth.rs index cb4ef5b4b6c5..2126c624f09f 100644 --- a/crates/goose/src/providers/gemini_oauth.rs +++ b/crates/goose/src/providers/gemini_oauth.rs @@ -531,7 +531,7 @@ fn html_success() -> String { } fn html_error(error: &str) -> String { - let safe_error = v_htmlescape::escape(error).to_string(); + let safe_error = v_htmlescape::escape_fmt(error); format!( r#" diff --git a/crates/goose/src/providers/sagemaker_tgi.rs b/crates/goose/src/providers/sagemaker_tgi.rs index 89fe5424cf2d..d05e15113170 100644 --- a/crates/goose/src/providers/sagemaker_tgi.rs +++ b/crates/goose/src/providers/sagemaker_tgi.rs @@ -8,6 +8,7 @@ use aws_sdk_bedrockruntime::config::ProvideCredentials; use aws_sdk_sagemakerruntime::Client as SageMakerClient; use rmcp::model::Tool; use serde_json::{json, Value}; +use smithy_transport_reqwest::ReqwestHttpClient; use super::base::{ ConfigKey, MessageStream, Provider, ProviderDef, ProviderMetadata, ProviderUsage, Usage, @@ -61,7 +62,10 @@ impl SageMakerTgiProvider { set_aws_env_vars(config.all_values()); set_aws_env_vars(config.all_secrets()); - let aws_config = aws_config::load_from_env().await; + let aws_config = aws_config::from_env() + .http_client(ReqwestHttpClient::new()) + .load() + .await; // Validate credentials aws_config diff --git a/crates/goose/src/session/mod.rs b/crates/goose/src/session/mod.rs index bf58fa8970db..8f4bb474cf12 100644 --- a/crates/goose/src/session/mod.rs +++ b/crates/goose/src/session/mod.rs @@ -2,6 +2,7 @@ mod chat_history_search; mod diagnostics; pub mod extension_data; mod legacy; +#[cfg(feature = "nostr")] pub mod nostr_share; pub mod session_manager; diff --git a/crates/goose/tests/session_id_propagation_test.rs b/crates/goose/tests/session_id_propagation_test.rs index be26868475d1..c8c069f154a6 100644 --- a/crates/goose/tests/session_id_propagation_test.rs +++ b/crates/goose/tests/session_id_propagation_test.rs @@ -161,7 +161,9 @@ async fn make_request(provider: &dyn Provider, session_id: &str) { async fn test_session_id_propagates_to_log_records() { use opentelemetry::logs::AnyValue; use opentelemetry::Key; - use opentelemetry_appender_tracing::layer::OpenTelemetryTracingBridge; + use opentelemetry_appender_tracing::layer::{ + OpenTelemetryTracingBridge, TracingSpanAttributes, + }; use opentelemetry_sdk::logs::{InMemoryLogExporterBuilder, SdkLoggerProvider}; use tracing_subscriber::prelude::*; @@ -171,7 +173,7 @@ async fn test_session_id_propagates_to_log_records() { .build(); let layer = OpenTelemetryTracingBridge::builder(&provider) - .with_span_attribute_allowlist(["session.id"]) + .with_tracing_span_attributes(TracingSpanAttributes::allowlist(["session.id"])) .build(); let subscriber = tracing_subscriber::registry().with(layer); let _guard = tracing::subscriber::set_default(subscriber); From dcdc7f645b51ed160da5a983c65c90d0ebdf55d5 Mon Sep 17 00:00:00 2001 From: Asish Kumar <87874775+officialasishkumar@users.noreply.github.com> Date: Tue, 26 May 2026 21:49:22 +0530 Subject: [PATCH 11/66] fix(desktop): stop the main window growing taller on every launch (#9409) Signed-off-by: Asish Kumar --- ui/desktop/src/main.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/ui/desktop/src/main.ts b/ui/desktop/src/main.ts index 5bcecd18d9f0..ecf3fe740ec4 100644 --- a/ui/desktop/src/main.ts +++ b/ui/desktop/src/main.ts @@ -868,6 +868,10 @@ const createChat = async (app: App, options: CreateChatOptions = {}) => { trafficLightPosition: process.platform === 'darwin' ? { x: 20, y: 16 } : undefined, vibrancy: process.platform === 'darwin' ? 'window' : undefined, frame: process.platform !== 'darwin', + // windowStateKeeper persists the outer window bounds (getBounds), so the + // window must be restored by outer bounds too. With useContentSize the saved + // outer height is reapplied as the content height, growing the window by the + // frame height on every launch on framed platforms (#9363). x: mainWindowState.x, y: mainWindowState.y, width: mainWindowState.width, @@ -875,7 +879,6 @@ const createChat = async (app: App, options: CreateChatOptions = {}) => { minWidth: 480, minHeight: 400, resizable: true, - useContentSize: true, icon: path.join(__dirname, '../images/icon.icns'), webPreferences: { spellcheck: settings.spellcheckEnabled ?? true, From b332f509b2d2e64ccee05e5991b244e124e1d451 Mon Sep 17 00:00:00 2001 From: Dmitry Beskov <43372966+besdar@users.noreply.github.com> Date: Tue, 26 May 2026 20:46:23 +0300 Subject: [PATCH 12/66] Russian language support (#9406) Co-authored-by: Jack Amadeo --- ui/desktop/src/i18n/i18n.test.ts | 10 + ui/desktop/src/i18n/index.ts | 2 +- ui/desktop/src/i18n/messages/ru.json | 4652 ++++++++++++++++++++++++++ 3 files changed, 4663 insertions(+), 1 deletion(-) create mode 100644 ui/desktop/src/i18n/messages/ru.json diff --git a/ui/desktop/src/i18n/i18n.test.ts b/ui/desktop/src/i18n/i18n.test.ts index abd5b0458285..1e8940941376 100644 --- a/ui/desktop/src/i18n/i18n.test.ts +++ b/ui/desktop/src/i18n/i18n.test.ts @@ -52,6 +52,16 @@ describe('getLocale', () => { expect(getLocale()).toEqual({ locale: 'en-GB', messageLocale: 'en' }); }); + it('returns Russian when navigator.languages contains ru', () => { + vi.stubGlobal('navigator', { languages: ['ru'] }); + expect(getLocale()).toEqual({ locale: 'ru', messageLocale: 'ru' }); + }); + + it('preserves Russian regional tag for formatting', () => { + vi.stubGlobal('navigator', { languages: ['ru-RU'] }); + expect(getLocale()).toEqual({ locale: 'ru-RU', messageLocale: 'ru' }); + }); + it('supports Turkish from navigator.languages', () => { vi.stubGlobal('navigator', { languages: ['tr-TR'] }); expect(getLocale()).toEqual({ locale: 'tr-TR', messageLocale: 'tr' }); diff --git a/ui/desktop/src/i18n/index.ts b/ui/desktop/src/i18n/index.ts index b13271d1d015..c8cac14d8d96 100644 --- a/ui/desktop/src/i18n/index.ts +++ b/ui/desktop/src/i18n/index.ts @@ -15,7 +15,7 @@ export { defineMessages, useIntl } from 'react-intl'; /** The set of locales that have translation catalogs. */ -const SUPPORTED_LOCALES = new Set(['en', 'tr', 'zh-CN']); +const SUPPORTED_LOCALES = new Set(['en', 'ru', 'tr', 'zh-CN']); /** * Map Simplified Chinese aliases (zh, zh-Hans*, zh-SG, zh-MY) to "zh-CN". diff --git a/ui/desktop/src/i18n/messages/ru.json b/ui/desktop/src/i18n/messages/ru.json new file mode 100644 index 000000000000..611bee5e0d67 --- /dev/null +++ b/ui/desktop/src/i18n/messages/ru.json @@ -0,0 +1,4652 @@ +{ + "alertBox.autoCompactAt": { + "defaultMessage": "Автосжимать при" + }, + "alertBox.compactNow": { + "defaultMessage": "Сжать сейчас" + }, + "alertBox.failedToSaveThreshold": { + "defaultMessage": "Не удалось сохранить пороговое значение: {error}" + }, + "announcementModal.gotIt": { + "defaultMessage": "Понятно!" + }, + "appLayout.openNavigation": { + "defaultMessage": "Открыть навигацию" + }, + "appsView.customApp": { + "defaultMessage": "Пользовательское приложение" + }, + "appsView.description": { + "defaultMessage": "Приложения с ваших MCP-серверов и приложения, созданные самим goose. Вы можете попросить его создать новые приложения через чат, и они появятся здесь." + }, + "appsView.errorLoading": { + "defaultMessage": "Ошибка загрузки приложений: {error}" + }, + "appsView.importApp": { + "defaultMessage": "Импортировать приложение" + }, + "appsView.launch": { + "defaultMessage": "Запустить" + }, + "appsView.loading": { + "defaultMessage": "Загрузка приложений..." + }, + "appsView.noAppsDescription": { + "defaultMessage": "Откройте чат и попросите goose создать нужное приложение. Он может собрать его для вас, после чего оно появится здесь. Если кто-то поделился приложением, импортируйте его кнопкой выше." + }, + "appsView.noAppsTitle": { + "defaultMessage": "Нет доступных приложений" + }, + "appsView.retry": { + "defaultMessage": "Повторить" + }, + "appsView.title": { + "defaultMessage": "Приложения" + }, + "backButton.back": { + "defaultMessage": "Назад" + }, + "baseChat.failedToLoadSession": { + "defaultMessage": "Не удалось загрузить сеанс" + }, + "baseChat.goHome": { + "defaultMessage": "На главную" + }, + "baseChat.noSession": { + "defaultMessage": "Нет сеанса" + }, + "baseChat.recipeCreatedMessage": { + "defaultMessage": "«{title}» сохранено и готово к использованию." + }, + "baseChat.recipeCreatedTitle": { + "defaultMessage": "Рецепт успешно создан!" + }, + "bottomMenuExtensionSelection.extensionToggleError": { + "defaultMessage": "Ошибка переключения расширения" + }, + "bottomMenuExtensionSelection.extensionUpdated": { + "defaultMessage": "Расширение обновлено" + }, + "bottomMenuExtensionSelection.extensionWillBeDisabled": { + "defaultMessage": "{name} будет отключено в новых чатах" + }, + "bottomMenuExtensionSelection.extensionWillBeEnabled": { + "defaultMessage": "{name} будет включено в новых чатах" + }, + "bottomMenuExtensionSelection.extensionsForNewChats": { + "defaultMessage": "Расширения для новых чатов" + }, + "bottomMenuExtensionSelection.extensionsForThisSession": { + "defaultMessage": "Расширения для этого чата" + }, + "bottomMenuExtensionSelection.manageExtensions": { + "defaultMessage": "управлять расширениями" + }, + "bottomMenuExtensionSelection.noActiveSession": { + "defaultMessage": "Активный сеанс не найден. Сначала начните чат." + }, + "bottomMenuExtensionSelection.noExtensionsAvailable": { + "defaultMessage": "нет доступных расширений" + }, + "bottomMenuExtensionSelection.noExtensionsFound": { + "defaultMessage": "расширения не найдены" + }, + "bottomMenuExtensionSelection.searchExtensions": { + "defaultMessage": "поиск расширений..." + }, + "bottomMenuModeSelection.autoFallback": { + "defaultMessage": "авто" + }, + "bottomMenuModeSelection.automaticModeDescription": { + "defaultMessage": "Автоматический выбор режима" + }, + "bottomMenuModeSelection.currentModeTitle": { + "defaultMessage": "Текущий режим: {label} - {description}" + }, + "cardButtons.configure": { + "defaultMessage": "Настроить" + }, + "cardButtons.launch": { + "defaultMessage": "Запустить" + }, + "chat.notification.taskComplete.body": { + "defaultMessage": "Нажмите здесь, чтобы сфокусироваться на Goose." + }, + "chat.notification.taskComplete.title": { + "defaultMessage": "Goose завершил задачу." + }, + "chatInput.contextWindow": { + "defaultMessage": "Контекстное окно" + }, + "chatInput.createRecipeFromSession": { + "defaultMessage": "Создать рецепт из сеанса" + }, + "chatInput.dictationError": { + "defaultMessage": "Ошибка диктовки" + }, + "chatInput.failedToReadImage": { + "defaultMessage": "Не удалось прочитать файл изображения" + }, + "chatInput.navigationShortcut": { + "defaultMessage": "{prefix}↑/{prefix}↓ для навигации по сообщениям" + }, + "chatInput.processingDroppedFiles": { + "defaultMessage": "Обработка перетащенных файлов..." + }, + "chatInput.recording": { + "defaultMessage": "Запись..." + }, + "chatInput.removeFile": { + "defaultMessage": "Удалить файл" + }, + "chatInput.removeImage": { + "defaultMessage": "Удалить изображение" + }, + "chatInput.restartingSession": { + "defaultMessage": "Перезапуск сеанса..." + }, + "chatInput.send": { + "defaultMessage": "Отправить" + }, + "chatInput.tooManyTools": { + "defaultMessage": "Слишком много инструментов может снизить производительность. Количество инструментов: {toolCount} (рекомендуется: {recommended})" + }, + "chatInput.transcribing": { + "defaultMessage": "Расшифровка..." + }, + "chatInput.typeMessage": { + "defaultMessage": "Введите сообщение для отправки" + }, + "chatInput.unknownType": { + "defaultMessage": "Неизвестный тип" + }, + "chatInput.viewEditRecipe": { + "defaultMessage": "Просмотреть/изменить рецепт" + }, + "chatInput.viewExtensions": { + "defaultMessage": "Просмотреть расширения" + }, + "chatInput.waitingForImages": { + "defaultMessage": "Ожидание сохранения изображений..." + }, + "chatSettings.modeDescription": { + "defaultMessage": "Настройте как Goose взаимодействует с инструментами и расширениями" + }, + "chatSettings.modeTitle": { + "defaultMessage": "Режим" + }, + "chatSettings.responseStylesDescription": { + "defaultMessage": "Выберите как Goose должен форматировать и оформлять ответы" + }, + "chatSettings.responseStylesTitle": { + "defaultMessage": "Стили ответов" + }, + "configSettings.configReset": { + "defaultMessage": "Конфигурация сброшена" + }, + "configSettings.configResetMsg": { + "defaultMessage": "Все изменения отменены" + }, + "configSettings.configUpdated": { + "defaultMessage": "Конфигурация обновлена" + }, + "configSettings.configUpdatedMsg": { + "defaultMessage": "«{name}» успешно сохранено" + }, + "configSettings.configurationEditor": { + "defaultMessage": "Редактор конфигурации" + }, + "configSettings.description": { + "defaultMessage": "Измените настройки конфигурации goose" + }, + "configSettings.descriptionWithProvider": { + "defaultMessage": "Измените настройки конфигурации goose (текущие настройки для {provider})" + }, + "configSettings.done": { + "defaultMessage": "Готово" + }, + "configSettings.editConfiguration": { + "defaultMessage": "Изменить конфигурацию" + }, + "configSettings.enterValue": { + "defaultMessage": "Введите {name}" + }, + "configSettings.noSettings": { + "defaultMessage": "Настройки конфигурации не найдены." + }, + "configSettings.resetChanges": { + "defaultMessage": "Сбросить изменения" + }, + "configSettings.saveFailed": { + "defaultMessage": "Не удалось сохранить" + }, + "configSettings.saveFailedMsg": { + "defaultMessage": "Не удалось сохранить «{name}»" + }, + "configSettings.saving": { + "defaultMessage": "Сохранение..." + }, + "configSettings.title": { + "defaultMessage": "Конфигурация" + }, + "configureApproveMode.cancel": { + "defaultMessage": "Отмена" + }, + "configureApproveMode.description": { + "defaultMessage": "Запросы на подтверждение могут быть даны на все запросы, либо определяться в зависимости от действий, которые могут требовать человеческого вмешательства" + }, + "configureApproveMode.manualApproval": { + "defaultMessage": "Ручное подтверждение" + }, + "configureApproveMode.manualApprovalDescription": { + "defaultMessage": "Все инструменты, расширения и изменения файлов будут требовать подтверждения человеком" + }, + "configureApproveMode.save": { + "defaultMessage": "Сохранить" + }, + "configureApproveMode.saving": { + "defaultMessage": "Сохранение..." + }, + "configureApproveMode.smartApproval": { + "defaultMessage": "Умное подтверждение" + }, + "configureApproveMode.smartApprovalDescription": { + "defaultMessage": "Интеллектуально определять, какие действия требуют подтверждения, исходя из уровня риска" + }, + "configureApproveMode.title": { + "defaultMessage": "Настроить режим подтверждения" + }, + "confirmationModal.defaultCancel": { + "defaultMessage": "Нет" + }, + "confirmationModal.defaultConfirm": { + "defaultMessage": "Да" + }, + "confirmationModal.processing": { + "defaultMessage": "Обработка..." + }, + "conversationLimitsDropdown.conversationLimits": { + "defaultMessage": "Ограничения разговора" + }, + "conversationLimitsDropdown.maxTurns": { + "defaultMessage": "Максимум ходов" + }, + "conversationLimitsDropdown.maxTurnsDescription": { + "defaultMessage": "Максимальное число ходов агента до запроса ввода от пользователя" + }, + "costTracker.costUnavailable": { + "defaultMessage": "Данные о стоимости недоступны для {model} ({inputTokens} входных, {outputTokens} выходных токенов)" + }, + "costTracker.inputOutputTooltip": { + "defaultMessage": "Ввод: {inputTokens} токенов ({inputCost}) | Вывод: {outputTokens} токенов ({outputCost})" + }, + "costTracker.pricingUnavailable": { + "defaultMessage": "Данные о ценах недоступны для {model}" + }, + "costTracker.totalSessionCost": { + "defaultMessage": "Итоговая стоимость сеанса: {cost}" + }, + "createEditRecipe.clickToGenerateDeeplink": { + "defaultMessage": "Нажмите, чтобы создать диплинк" + }, + "createEditRecipe.close": { + "defaultMessage": "Закрыть" + }, + "createEditRecipe.copied": { + "defaultMessage": "Скопировано!" + }, + "createEditRecipe.copy": { + "defaultMessage": "Копировать" + }, + "createEditRecipe.copyLinkDescription": { + "defaultMessage": "Скопируйте эту ссылку, чтобы поделиться с друзьями, или вставьте ее напрямую в Chrome для открытия" + }, + "createEditRecipe.createRecipeTitle": { + "defaultMessage": "Создать рецепт" + }, + "createEditRecipe.createSubtitle": { + "defaultMessage": "Создайте новый рецепт, чтобы задать поведение и возможности агента для повторно используемых сеансов чата." + }, + "createEditRecipe.editSubtitle": { + "defaultMessage": "Вы можете изменить рецепт ниже, чтобы изменить поведение агента в новом сеансе." + }, + "createEditRecipe.generatingDeeplink": { + "defaultMessage": "Создание диплинка..." + }, + "createEditRecipe.learnMore": { + "defaultMessage": "Подробнее" + }, + "createEditRecipe.recipeSavedAndLaunchedMsg": { + "defaultMessage": "Рецепт успешно сохранен и запущен" + }, + "createEditRecipe.recipeSavedMsg": { + "defaultMessage": "Рецепт успешно сохранен" + }, + "createEditRecipe.saveAndRunFailed": { + "defaultMessage": "Не удалось сохранить и запустить" + }, + "createEditRecipe.saveAndRunFailedMsg": { + "defaultMessage": "Не удалось сохранить и запустить рецепт: {error}" + }, + "createEditRecipe.saveAndRunRecipe": { + "defaultMessage": "Сохранить и запустить рецепт" + }, + "createEditRecipe.saveFailed": { + "defaultMessage": "Не удалось сохранить" + }, + "createEditRecipe.saveFailedMsg": { + "defaultMessage": "Не удалось сохранить рецепт: {error}" + }, + "createEditRecipe.saveRecipe": { + "defaultMessage": "Сохранить рецепт" + }, + "createEditRecipe.saving": { + "defaultMessage": "Сохранение..." + }, + "createEditRecipe.validationFailed": { + "defaultMessage": "Проверка не пройдена" + }, + "createEditRecipe.validationMsg": { + "defaultMessage": "Заполните все обязательные поля и убедитесь, что JSON-схема корректна." + }, + "createEditRecipe.viewEditRecipeTitle": { + "defaultMessage": "Просмотреть/изменить рецепт" + }, + "createRecipeFromSession.analyzingTitle": { + "defaultMessage": "Анализ вашего разговора" + }, + "createRecipeFromSession.cancel": { + "defaultMessage": "Отмена" + }, + "createRecipeFromSession.createAndRunRecipe": { + "defaultMessage": "Создать и запустить рецепт" + }, + "createRecipeFromSession.createRecipe": { + "defaultMessage": "Создать рецепт" + }, + "createRecipeFromSession.creating": { + "defaultMessage": "Создание..." + }, + "createRecipeFromSession.extractingInsights": { + "defaultMessage": "Извлечение сведений из вашего чата" + }, + "createRecipeFromSession.failedToCreateDefaultMsg": { + "defaultMessage": "При создании рецепта произошла непредвиденная ошибка. Повторите попытку." + }, + "createRecipeFromSession.failedToCreateTitle": { + "defaultMessage": "Не удалось создать рецепт" + }, + "createRecipeFromSession.stageComplete": { + "defaultMessage": "Готово!" + }, + "createRecipeFromSession.stageExtracting": { + "defaultMessage": "Извлечение основных тем..." + }, + "createRecipeFromSession.stageFinalizing": { + "defaultMessage": "Завершение деталей..." + }, + "createRecipeFromSession.stageGenerating": { + "defaultMessage": "Создание структуры рецепта..." + }, + "createRecipeFromSession.stageIdentifying": { + "defaultMessage": "Определение ключевых шаблонов..." + }, + "createRecipeFromSession.stageReading": { + "defaultMessage": "Чтение вашего разговора..." + }, + "createRecipeFromSession.subtitle": { + "defaultMessage": "Создайте повторно используемый рецепт на основе текущего разговора." + }, + "createRecipeFromSession.title": { + "defaultMessage": "Создать рецепт из сеанса" + }, + "createSubRecipeInline.cancel": { + "defaultMessage": "Отмена" + }, + "createSubRecipeInline.closeModal": { + "defaultMessage": "Закрыть окно создания подрецепта" + }, + "createSubRecipeInline.createAndAdd": { + "defaultMessage": "Создать и добавить подрецепт" + }, + "createSubRecipeInline.createdSuccess": { + "defaultMessage": "Подрецепт успешно создан" + }, + "createSubRecipeInline.creating": { + "defaultMessage": "Создание..." + }, + "createSubRecipeInline.duplicateName": { + "defaultMessage": "Повторяющееся имя" + }, + "createSubRecipeInline.duplicateNameMsg": { + "defaultMessage": "Подрецепт с именем «{name}» уже существует. Используйте уникальное имя." + }, + "createSubRecipeInline.instructionsLabel": { + "defaultMessage": "Инструкции" + }, + "createSubRecipeInline.instructionsPlaceholder": { + "defaultMessage": "Инструкции для ИИ при вызове этого подрецепта..." + }, + "createSubRecipeInline.nameHint": { + "defaultMessage": "Уникальный идентификатор, используемый для создания имени инструмента" + }, + "createSubRecipeInline.nameLabel": { + "defaultMessage": "Имя" + }, + "createSubRecipeInline.namePlaceholder": { + "defaultMessage": "например, security_scan" + }, + "createSubRecipeInline.preconfiguredValues": { + "defaultMessage": "Предварительно настроенные значения" + }, + "createSubRecipeInline.preconfiguredValuesHint": { + "defaultMessage": "Необязательные значения параметров, которые всегда передаются подрецепту" + }, + "createSubRecipeInline.recipeDescriptionLabel": { + "defaultMessage": "Описание рецепта" + }, + "createSubRecipeInline.recipeDescriptionPlaceholder": { + "defaultMessage": "Что делает этот рецепт при выполнении" + }, + "createSubRecipeInline.recipeTitleLabel": { + "defaultMessage": "Название рецепта" + }, + "createSubRecipeInline.recipeTitlePlaceholder": { + "defaultMessage": "например, Инструмент анализа безопасности" + }, + "createSubRecipeInline.saveFailed": { + "defaultMessage": "Не удалось сохранить" + }, + "createSubRecipeInline.saveFailedMsg": { + "defaultMessage": "Не удалось сохранить подрецепт: {error}" + }, + "createSubRecipeInline.sequentialHint": { + "defaultMessage": "(Принудительно выполняет несколько экземпляров последовательно)" + }, + "createSubRecipeInline.sequentialLabel": { + "defaultMessage": "Последовательно при повторении" + }, + "createSubRecipeInline.subtitle": { + "defaultMessage": "Создайте простой рецепт для использования как вызываемый инструмент в основном рецепте" + }, + "createSubRecipeInline.title": { + "defaultMessage": "Создать новый подрецепт" + }, + "createSubRecipeInline.toolDescriptionLabel": { + "defaultMessage": "Описание инструмента" + }, + "createSubRecipeInline.toolDescriptionPlaceholder": { + "defaultMessage": "Необязательное описание, показываемое при вызове как инструмента" + }, + "createSubRecipeInline.validationFailed": { + "defaultMessage": "Проверка не пройдена" + }, + "createSubRecipeInline.validationMsg": { + "defaultMessage": "Имя, название, описание рецепта и инструкции обязательны." + }, + "creditsExhaustedNotification.addCredits": { + "defaultMessage": "Добавить кредиты" + }, + "creditsExhaustedNotification.insufficientCredits": { + "defaultMessage": "Недостаточно кредитов" + }, + "cronPicker.april": { + "defaultMessage": "Апрель" + }, + "cronPicker.at": { + "defaultMessage": "в" + }, + "cronPicker.atMinute": { + "defaultMessage": "на минуте" + }, + "cronPicker.atSecond": { + "defaultMessage": "на секунде" + }, + "cronPicker.august": { + "defaultMessage": "Август" + }, + "cronPicker.cronExpression": { + "defaultMessage": "Cron-выражение" + }, + "cronPicker.custom": { + "defaultMessage": "Пользовательский cron" + }, + "cronPicker.day": { + "defaultMessage": "День" + }, + "cronPicker.december": { + "defaultMessage": "Декабрь" + }, + "cronPicker.emptyCronError": { + "defaultMessage": "Cron-выражение не может быть пустым" + }, + "cronPicker.every": { + "defaultMessage": "Каждый" + }, + "cronPicker.february": { + "defaultMessage": "Февраль" + }, + "cronPicker.friday": { + "defaultMessage": "Пятница" + }, + "cronPicker.hour": { + "defaultMessage": "Час" + }, + "cronPicker.inMonth": { + "defaultMessage": "в" + }, + "cronPicker.invalidDayOfMonth": { + "defaultMessage": "День должен быть от 1 до {max}" + }, + "cronPicker.january": { + "defaultMessage": "Январь" + }, + "cronPicker.july": { + "defaultMessage": "Июль" + }, + "cronPicker.june": { + "defaultMessage": "Июнь" + }, + "cronPicker.march": { + "defaultMessage": "Март" + }, + "cronPicker.may": { + "defaultMessage": "Май" + }, + "cronPicker.minute": { + "defaultMessage": "Минута" + }, + "cronPicker.mode": { + "defaultMessage": "Режим" + }, + "cronPicker.monday": { + "defaultMessage": "Понедельник" + }, + "cronPicker.month": { + "defaultMessage": "Месяц" + }, + "cronPicker.november": { + "defaultMessage": "Ноябрь" + }, + "cronPicker.october": { + "defaultMessage": "Октябрь" + }, + "cronPicker.on": { + "defaultMessage": "в" + }, + "cronPicker.onDay": { + "defaultMessage": "в день" + }, + "cronPicker.quarter": { + "defaultMessage": "Квартал" + }, + "cronPicker.saturday": { + "defaultMessage": "Суббота" + }, + "cronPicker.september": { + "defaultMessage": "Сентябрь" + }, + "cronPicker.startingMonth": { + "defaultMessage": "начальный месяц" + }, + "cronPicker.sunday": { + "defaultMessage": "Воскресенье" + }, + "cronPicker.thursday": { + "defaultMessage": "Четверг" + }, + "cronPicker.tuesday": { + "defaultMessage": "Вторник" + }, + "cronPicker.wednesday": { + "defaultMessage": "Среда" + }, + "cronPicker.week": { + "defaultMessage": "Неделя" + }, + "cronPicker.year": { + "defaultMessage": "Год" + }, + "customProviderForm.add": { + "defaultMessage": "Добавить" + }, + "customProviderForm.anthropicCompatible": { + "defaultMessage": "Совместимо с Anthropic" + }, + "customProviderForm.apiBasePath": { + "defaultMessage": "Базовый путь API (необязательно)" + }, + "customProviderForm.apiBasePathHint": { + "defaultMessage": "Переопределите путь API по умолчанию. Оставьте пустым, чтобы использовать путь провайдера по умолчанию." + }, + "customProviderForm.apiBasePathPlaceholder": { + "defaultMessage": "например, v1/chat/completions или project_id/v1" + }, + "customProviderForm.apiKey": { + "defaultMessage": "API-ключ" + }, + "customProviderForm.apiKeyPlaceholderExisting": { + "defaultMessage": "Оставьте пустым, чтобы сохранить существующий ключ" + }, + "customProviderForm.apiKeyPlaceholderNew": { + "defaultMessage": "Ваш API-ключ" + }, + "customProviderForm.apiKeyRequired": { + "defaultMessage": "Требуется API-ключ" + }, + "customProviderForm.apiUrl": { + "defaultMessage": "URL API" + }, + "customProviderForm.apiUrlPlaceholder": { + "defaultMessage": "https://api.example.com" + }, + "customProviderForm.apiUrlRequired": { + "defaultMessage": "Требуется URL API" + }, + "customProviderForm.attachments": { + "defaultMessage": "Вложения" + }, + "customProviderForm.authHint": { + "defaultMessage": "Локальные LLM, такие как Ollama, обычно не требуют API-ключа." + }, + "customProviderForm.authentication": { + "defaultMessage": "Аутентификация" + }, + "customProviderForm.availableModels": { + "defaultMessage": "Доступные модели (через запятую)" + }, + "customProviderForm.back": { + "defaultMessage": "← Назад" + }, + "customProviderForm.cancel": { + "defaultMessage": "Отмена" + }, + "customProviderForm.cannotDeleteActive": { + "defaultMessage": "Нельзя удалить этого провайдера, пока он используется. Сначала переключитесь на другую модель." + }, + "customProviderForm.chooseSetup": { + "defaultMessage": "Выберите способ настройки провайдера." + }, + "customProviderForm.clear": { + "defaultMessage": "Очистить" + }, + "customProviderForm.configureManually": { + "defaultMessage": "Настроить вручную" + }, + "customProviderForm.configureManuallyDesc": { + "defaultMessage": "Введите все данные провайдера самостоятельно" + }, + "customProviderForm.confirmDelete": { + "defaultMessage": "Подтвердить удаление" + }, + "customProviderForm.createProvider": { + "defaultMessage": "Создать провайдера" + }, + "customProviderForm.customHeaders": { + "defaultMessage": "Пользовательские заголовки" + }, + "customProviderForm.customHeadersHint": { + "defaultMessage": "Добавьте пользовательские HTTP-заголовки для запросов к провайдеру. После заполнения обоих полей нажмите кнопку «+», чтобы добавить." + }, + "customProviderForm.deleteConfirmation": { + "defaultMessage": "Удалить этого пользовательского провайдера? Провайдер и сохраненный API-ключ будут удалены навсегда. Это действие нельзя отменить." + }, + "customProviderForm.deleteProvider": { + "defaultMessage": "Удалить провайдера" + }, + "customProviderForm.displayName": { + "defaultMessage": "Отображаемое имя" + }, + "customProviderForm.displayNamePlaceholder": { + "defaultMessage": "Имя вашего провайдера" + }, + "customProviderForm.displayNameRequired": { + "defaultMessage": "Требуется отображаемое имя" + }, + "customProviderForm.docs": { + "defaultMessage": "Документация" + }, + "customProviderForm.headerBothRequired": { + "defaultMessage": "Необходимо ввести имя и заголовок" + }, + "customProviderForm.headerDuplicate": { + "defaultMessage": "Заголовок с таким именем уже существует" + }, + "customProviderForm.headerNamePlaceholder": { + "defaultMessage": "Имя заголовка" + }, + "customProviderForm.headerNoSpaces": { + "defaultMessage": "Имя заголовка не может содержать пробелы" + }, + "customProviderForm.modelsPlaceholder": { + "defaultMessage": "model-a, model-b, model-c" + }, + "customProviderForm.modelsRequired": { + "defaultMessage": "Требуется хотя бы одна модель" + }, + "customProviderForm.ollamaCompatible": { + "defaultMessage": "Совместимо с Ollama" + }, + "customProviderForm.openaiCompatible": { + "defaultMessage": "Совместимо с OpenAI" + }, + "customProviderForm.providerType": { + "defaultMessage": "Тип провайдера" + }, + "customProviderForm.reasoning": { + "defaultMessage": "Рассуждение" + }, + "customProviderForm.requiresApiKey": { + "defaultMessage": "Этот провайдер требует API-ключ" + }, + "customProviderForm.startFromTemplate": { + "defaultMessage": "Начать с шаблона провайдера" + }, + "customProviderForm.startFromTemplateDesc": { + "defaultMessage": "Выберите известного провайдера, и мы автоматически заполним конфигурацию" + }, + "customProviderForm.submitError": { + "defaultMessage": "Не удалось сохранить провайдера. Проверьте конфигурацию и повторите попытку." + }, + "customProviderForm.supportsStreaming": { + "defaultMessage": "Провайдер поддерживает потоковые ответы" + }, + "customProviderForm.toolCalling": { + "defaultMessage": "Вызов инструментов" + }, + "customProviderForm.updateProvider": { + "defaultMessage": "Обновить провайдера" + }, + "customProviderForm.usingTemplate": { + "defaultMessage": "Используется шаблон: {name}" + }, + "customProviderForm.valuePlaceholder": { + "defaultMessage": "Значение" + }, + "defaultCardButtons.configureSettings": { + "defaultMessage": "Настроить параметры {name}" + }, + "defaultCardButtons.deleteSettings": { + "defaultMessage": "Удалить параметры {name}" + }, + "defaultCardButtons.editSettings": { + "defaultMessage": "Изменить параметры {name}" + }, + "defaultCardButtons.getStarted": { + "defaultMessage": "Начните работу с goose!" + }, + "defaultProviderSetupForm.apiHostLabel": { + "defaultMessage": "Хост API" + }, + "defaultProviderSetupForm.apiHostPlaceholder": { + "defaultMessage": "https://api.example.com" + }, + "defaultProviderSetupForm.apiKeyLabel": { + "defaultMessage": "API-ключ" + }, + "defaultProviderSetupForm.apiKeyPlaceholder": { + "defaultMessage": "Ваш API-ключ" + }, + "defaultProviderSetupForm.hideOptions": { + "defaultMessage": "Скрыть {count} параметров" + }, + "defaultProviderSetupForm.loadingConfig": { + "defaultMessage": "Загрузка значений конфигурации..." + }, + "defaultProviderSetupForm.modelsLabel": { + "defaultMessage": "Модели" + }, + "defaultProviderSetupForm.modelsPlaceholder": { + "defaultMessage": "model-a, model-b" + }, + "defaultProviderSetupForm.noConfigParameters": { + "defaultMessage": "У этого провайдера нет параметров конфигурации." + }, + "defaultProviderSetupForm.showOptions": { + "defaultMessage": "Показать {count} параметров" + }, + "diagnosticsModal.attachHint": { + "defaultMessage": "Если вы создаете отчет об ошибке, приложите к нему диагностический отчет." + }, + "diagnosticsModal.cancel": { + "defaultMessage": "Отмена" + }, + "diagnosticsModal.configSettings": { + "defaultMessage": "Настройки конфигурации" + }, + "diagnosticsModal.description": { + "defaultMessage": "Вы можете скачать ZIP-файл диагностики, чтобы поделиться им с командой, или сразу создать ошибку на GitHub с предзаполненными сведениями о системе. Диагностический отчет содержит:" + }, + "diagnosticsModal.diagnosticsErrorMsg": { + "defaultMessage": "Не удалось скачать диагностику" + }, + "diagnosticsModal.diagnosticsErrorTitle": { + "defaultMessage": "Ошибка диагностики" + }, + "diagnosticsModal.download": { + "defaultMessage": "Скачать" + }, + "diagnosticsModal.downloading": { + "defaultMessage": "Загрузка..." + }, + "diagnosticsModal.fileBug": { + "defaultMessage": "Сообщить об ошибке на GitHub" + }, + "diagnosticsModal.logFiles": { + "defaultMessage": "Недавние файлы журналов" + }, + "diagnosticsModal.opening": { + "defaultMessage": "Открытие..." + }, + "diagnosticsModal.reportProblem": { + "defaultMessage": "Сообщить о проблеме" + }, + "diagnosticsModal.sensitiveWarning": { + "defaultMessage": "Если ваш сеанс содержит конфиденциальную информацию, не публикуйте файл диагностики." + }, + "diagnosticsModal.sessionMessages": { + "defaultMessage": "Сообщения текущего сеанса" + }, + "diagnosticsModal.systemInfo": { + "defaultMessage": "Основная информация о системе" + }, + "diagnosticsModal.systemInfoErrorMsg": { + "defaultMessage": "Не удалось получить информацию о системе" + }, + "diagnosticsModal.systemInfoErrorTitle": { + "defaultMessage": "Ошибка" + }, + "dialog.close": { + "defaultMessage": "Закрыть" + }, + "dictationSettings.addApiKey": { + "defaultMessage": "Добавить API-ключ" + }, + "dictationSettings.apiKey": { + "defaultMessage": "API-ключ" + }, + "dictationSettings.cancel": { + "defaultMessage": "Отмена" + }, + "dictationSettings.chooseVoiceConversion": { + "defaultMessage": "Выберите, как голос преобразуется в текст" + }, + "dictationSettings.configureApiKey": { + "defaultMessage": "Настройте API-ключ в {settingsPath}" + }, + "dictationSettings.configured": { + "defaultMessage": "(Настроено)" + }, + "dictationSettings.configuredIn": { + "defaultMessage": "✓ Настроено в {settingsPath}" + }, + "dictationSettings.disabled": { + "defaultMessage": "Отключено" + }, + "dictationSettings.enterApiKey": { + "defaultMessage": "Введите API-ключ" + }, + "dictationSettings.notConfigured": { + "defaultMessage": "(не настроено)" + }, + "dictationSettings.removeApiKey": { + "defaultMessage": "Удалить API-ключ" + }, + "dictationSettings.requiredForTranscription": { + "defaultMessage": "Требуется для транскрипции" + }, + "dictationSettings.save": { + "defaultMessage": "Сохранить" + }, + "dictationSettings.updateApiKey": { + "defaultMessage": "Обновить API-ключ" + }, + "dictationSettings.voiceDictationProvider": { + "defaultMessage": "Провайдер голосовой диктовки" + }, + "dirSwitcher.chooseDirectory": { + "defaultMessage": "Выбрать каталог…" + }, + "dirSwitcher.currentDirectory": { + "defaultMessage": "Текущий каталог" + }, + "dirSwitcher.failedToUpdateWorkingDir": { + "defaultMessage": "Не удалось обновить рабочий каталог" + }, + "dirSwitcher.gitWorktrees": { + "defaultMessage": "Git worktrees" + }, + "dirSwitcher.noWorktreesFound": { + "defaultMessage": "Git worktrees не найдены" + }, + "dirSwitcher.openInFinder": { + "defaultMessage": "Открыть в файловом менеджере" + }, + "dirSwitcher.recentDirectories": { + "defaultMessage": "Недавние каталоги" + }, + "elicitationRequest.accept": { + "defaultMessage": "Принять" + }, + "elicitationRequest.cancelled": { + "defaultMessage": "Запрос на информацию был отменен." + }, + "elicitationRequest.defaultMessage": { + "defaultMessage": "Goose нужна от вас дополнительная информация." + }, + "elicitationRequest.expired": { + "defaultMessage": "Срок действия запроса истек. Расширению нужно будет запросить его снова." + }, + "elicitationRequest.submit": { + "defaultMessage": "Отправить" + }, + "elicitationRequest.submitted": { + "defaultMessage": "Информация отправлена" + }, + "elicitationRequest.waitingForResponse": { + "defaultMessage": "Ожидание вашего ответа (осталось {timeRemaining})" + }, + "envVarsSection.add": { + "defaultMessage": "Добавить" + }, + "envVarsSection.bothRequired": { + "defaultMessage": "Необходимо ввести имя переменной и значение" + }, + "envVarsSection.envVarsDescription": { + "defaultMessage": "Добавьте пары ключ-значение для переменных окружения. После заполнения обоих полей нажмите «+», чтобы добавить. Для существующих секретных значений нажмите кнопку редактирования." + }, + "envVarsSection.environmentVariables": { + "defaultMessage": "Переменные окружения" + }, + "envVarsSection.noSpaces": { + "defaultMessage": "Имя переменной не может содержать пробелы" + }, + "envVarsSection.value": { + "defaultMessage": "Значение" + }, + "envVarsSection.variableName": { + "defaultMessage": "Имя переменной" + }, + "environmentBadge.dev": { + "defaultMessage": "Разработка" + }, + "errorBoundary.errorGeneric": { + "defaultMessage": "Произошла ошибка." + }, + "errorBoundary.errorWithVersion": { + "defaultMessage": "Произошла ошибка в Goose v{version}." + }, + "errorBoundary.heading": { + "defaultMessage": "Га-га!" + }, + "errorBoundary.reload": { + "defaultMessage": "Перезагрузить" + }, + "extensionConfigFields.commandLabel": { + "defaultMessage": "Команда" + }, + "extensionConfigFields.commandPlaceholder": { + "defaultMessage": "e.g. npx -y @modelcontextprotocol/my-extension [filepath]" + }, + "extensionConfigFields.commandRequired": { + "defaultMessage": "Требуется команда" + }, + "extensionConfigFields.endpointLabel": { + "defaultMessage": "Эндпоинт" + }, + "extensionConfigFields.endpointPlaceholder": { + "defaultMessage": "Введите URL эндпоинта..." + }, + "extensionConfigFields.endpointRequired": { + "defaultMessage": "Требуется URL эндпоинта" + }, + "extensionInfoFields.descriptionLabel": { + "defaultMessage": "Описание" + }, + "extensionInfoFields.descriptionPlaceholder": { + "defaultMessage": "Необязательное описание..." + }, + "extensionInfoFields.extensionName": { + "defaultMessage": "Имя расширения" + }, + "extensionInfoFields.extensionNamePlaceholder": { + "defaultMessage": "Введите имя расширения..." + }, + "extensionInfoFields.nameRequired": { + "defaultMessage": "Требуется имя" + }, + "extensionInfoFields.typeHttp": { + "defaultMessage": "HTTP" + }, + "extensionInfoFields.typeLabel": { + "defaultMessage": "Тип" + }, + "extensionInfoFields.typeSseUnsupported": { + "defaultMessage": "SSE (не поддерживается)" + }, + "extensionInfoFields.typeStandardIo": { + "defaultMessage": "Стандартный ввод-вывод (STDIO)" + }, + "extensionInfoFields.typeStdio": { + "defaultMessage": "STDIO" + }, + "extensionInfoFields.typeStreamableHttp": { + "defaultMessage": "Потоковый HTTP" + }, + "extensionInstallModal.alreadyInstalledMessage": { + "defaultMessage": "Расширение ''{name}'' уже успешно установлено. Начните новый чат, чтобы использовать расширение." + }, + "extensionInstallModal.alreadyInstalledTitle": { + "defaultMessage": "Расширение ''{name}'' уже установлено" + }, + "extensionInstallModal.blockedMessage": { + "defaultMessage": "Команда этого расширения не входит в список разрешенных, поэтому установка заблокирована. Расширение: {name} Команда: {command} Обратитесь к администратору, чтобы запросить одобрение этого расширения." + }, + "extensionInstallModal.blockedTitle": { + "defaultMessage": "Установка расширения заблокирована" + }, + "extensionInstallModal.cancel": { + "defaultMessage": "Отмена" + }, + "extensionInstallModal.installAnyway": { + "defaultMessage": "Все равно установить" + }, + "extensionInstallModal.installing": { + "defaultMessage": "Установка..." + }, + "extensionInstallModal.no": { + "defaultMessage": "Нет" + }, + "extensionInstallModal.ok": { + "defaultMessage": "OK" + }, + "extensionInstallModal.trustedMessage": { + "defaultMessage": "Установить расширение {name}? Команда: {command}" + }, + "extensionInstallModal.trustedTitle": { + "defaultMessage": "Подтвердить установку расширения" + }, + "extensionInstallModal.unknownCommand": { + "defaultMessage": "Неизвестная команда" + }, + "extensionInstallModal.untrustedMessageWithCommand": { + "defaultMessage": "{securityMessage} Расширение: {name} Команда: {command} Если вы не уверены, обратитесь к администратору." + }, + "extensionInstallModal.untrustedMessageWithUrl": { + "defaultMessage": "{securityMessage} Расширение: {name} URL: {url} Если вы не уверены, обратитесь к администратору." + }, + "extensionInstallModal.untrustedSecurityMessage": { + "defaultMessage": "Команда этого расширения не входит в список разрешенных и сможет получать доступ к вашим разговорам и предоставлять дополнительную функциональность. Установка расширений из ненадежных источников может создавать риски безопасности." + }, + "extensionInstallModal.untrustedTitle": { + "defaultMessage": "Установить недоверенное расширение?" + }, + "extensionInstallModal.yes": { + "defaultMessage": "Да" + }, + "extensionItem.configureExtension": { + "defaultMessage": "Настроить расширение {name}" + }, + "extensionItem.toggleExtension": { + "defaultMessage": "Включить или отключить расширение {name}" + }, + "extensionList.availableExtensions": { + "defaultMessage": "Доступные расширения ({count})" + }, + "extensionList.builtInExtension": { + "defaultMessage": "Встроенное расширение" + }, + "extensionList.defaultExtensions": { + "defaultMessage": "Расширения по умолчанию ({count})" + }, + "extensionList.noExtensions": { + "defaultMessage": "Нет доступных расширений" + }, + "extensionModal.cancel": { + "defaultMessage": "Отмена" + }, + "extensionModal.closeWithoutSaving": { + "defaultMessage": "Закрыть без сохранения" + }, + "extensionModal.confirmRemoval": { + "defaultMessage": "Подтвердить удаление" + }, + "extensionModal.deleteDescription": { + "defaultMessage": "Это навсегда удалит расширение и все его настройки." + }, + "extensionModal.deleteExtensionTitle": { + "defaultMessage": "Удалить расширение «{name}»" + }, + "extensionModal.installationNotes": { + "defaultMessage": "Примечания по установке" + }, + "extensionModal.removeExtension": { + "defaultMessage": "Удалить расширение" + }, + "extensionModal.unsavedChangesMessage": { + "defaultMessage": "У вас есть несохраненные изменения конфигурации расширения. Закрыть без сохранения?" + }, + "extensionModal.unsavedChangesTitle": { + "defaultMessage": "Несохраненные изменения" + }, + "extensionTimeoutField.timeoutLabel": { + "defaultMessage": "Тайм-аут" + }, + "extensionsSection.addCustomExtension": { + "defaultMessage": "Добавить пользовательское расширение" + }, + "extensionsSection.addExtension": { + "defaultMessage": "Добавить расширение" + }, + "extensionsSection.browseExtensions": { + "defaultMessage": "Просмотреть расширения" + }, + "extensionsSection.saveChanges": { + "defaultMessage": "Сохранить изменения" + }, + "extensionsSection.updateExtension": { + "defaultMessage": "Обновить расширение" + }, + "extensionsView.addCustomExtension": { + "defaultMessage": "Добавить пользовательское расширение" + }, + "extensionsView.addExtension": { + "defaultMessage": "Добавить расширение" + }, + "extensionsView.browseExtensions": { + "defaultMessage": "Просмотреть расширения" + }, + "extensionsView.defaultNote": { + "defaultMessage": "Расширения, включенные здесь, используются по умолчанию для новых чатов. Активные расширения также можно переключать во время чата." + }, + "extensionsView.description": { + "defaultMessage": "Эти расширения используют Model Context Protocol (MCP). Они расширяют возможности Goose с помощью трех основных компонентов: промптов, ресурсов и инструментов. {searchShortcut} для поиска." + }, + "extensionsView.heading": { + "defaultMessage": "Расширения" + }, + "extensionsView.searchPlaceholder": { + "defaultMessage": "Поиск расширений..." + }, + "externalBackendSection.certFingerprint": { + "defaultMessage": "Отпечаток сертификата (необязательно)" + }, + "externalBackendSection.certFingerprintHelp": { + "defaultMessage": "Закрепите конкретный отпечаток TLS-сертификата. Если не указано, сертификат будет доверен при первом использовании (TOFU)." + }, + "externalBackendSection.certFingerprintPlaceholder": { + "defaultMessage": "AA:BB:CC:... or sha256/base64" + }, + "externalBackendSection.description": { + "defaultMessage": "По умолчанию goose запускает сервер за вас; используйте это для подключения к внешнему серверу goose" + }, + "externalBackendSection.restartNote": { + "defaultMessage": "Чтобы изменения вступили в силу, требуется перезапуск Goose. Новые окна чата будут подключаться к внешнему серверу." + }, + "externalBackendSection.secretKey": { + "defaultMessage": "Секретный ключ" + }, + "externalBackendSection.secretKeyHelp": { + "defaultMessage": "Секретный ключ, настроенный на сервере goosed (GOOSE_SERVER__SECRET_KEY)" + }, + "externalBackendSection.secretKeyPlaceholder": { + "defaultMessage": "Введите секретный ключ сервера" + }, + "externalBackendSection.serverUrl": { + "defaultMessage": "URL сервера" + }, + "externalBackendSection.title": { + "defaultMessage": "Сервер Goose" + }, + "externalBackendSection.urlFormatError": { + "defaultMessage": "Неверный формат URL" + }, + "externalBackendSection.urlProtocolError": { + "defaultMessage": "URL должен использовать протокол http или https" + }, + "externalBackendSection.useExternalServer": { + "defaultMessage": "Использовать внешний сервер" + }, + "externalBackendSection.useExternalServerDescription": { + "defaultMessage": "Подключиться к серверу goose, запущенному в другом месте (требуется перезапуск приложения)" + }, + "freeOptionCards.chooseOption": { + "defaultMessage": "Выберите вариант, чтобы начать." + }, + "freeOptionCards.freeAndPrivate": { + "defaultMessage": "Бесплатно и приватно" + }, + "freeOptionCards.localModelDescription": { + "defaultMessage": "Скачайте модель и запускайте её полностью на своем компьютере. Без API-ключей и учетных записей." + }, + "freeOptionCards.localModelTitle": { + "defaultMessage": "Использовать локальную модель" + }, + "freeOptionCards.nanogptDescription": { + "defaultMessage": "Зарегистрируйтесь, чтобы получить 60 млн бесплатных токенов на 7 дней." + }, + "freeOptionCards.nanogptTitle": { + "defaultMessage": "NanoGPT" + }, + "freeOptionCards.retry": { + "defaultMessage": "Повторить" + }, + "freeOptionCards.tetrateDescription": { + "defaultMessage": "Получите доступ к нескольким ИИ-моделям с автоматической настройкой. Зарегистрируйтесь, чтобы получить кредит $10." + }, + "freeOptionCards.tetrateTitle": { + "defaultMessage": "Agent Router by Tetrate" + }, + "freeOptionCards.unexpectedError": { + "defaultMessage": "Во время настройки произошла непредвиденная ошибка." + }, + "gatewaySettings.botFatherInstructions": { + "defaultMessage": "Откройте @BotFather на телефоне, отправьте /newbot и следуйте подсказкам, чтобы назвать бота. BotFather ответит API-токеном — вставьте его ниже." + }, + "gatewaySettings.close": { + "defaultMessage": "Закрыть" + }, + "gatewaySettings.expiresIn": { + "defaultMessage": "Истекает через {time}" + }, + "gatewaySettings.failedToGeneratePairingCode": { + "defaultMessage": "Не удалось создать код подключения" + }, + "gatewaySettings.failedToRemove": { + "defaultMessage": "Не удалось удалить" + }, + "gatewaySettings.failedToStart": { + "defaultMessage": "Не удалось запустить" + }, + "gatewaySettings.failedToStop": { + "defaultMessage": "Не удалось остановить" + }, + "gatewaySettings.failedToUnpairUser": { + "defaultMessage": "Не удалось отменить подключение пользователя" + }, + "gatewaySettings.loading": { + "defaultMessage": "Загрузка..." + }, + "gatewaySettings.pairDevice": { + "defaultMessage": "Подключить устройство" + }, + "gatewaySettings.pairedUsers": { + "defaultMessage": "Подключенные пользователи" + }, + "gatewaySettings.pairingCode": { + "defaultMessage": "Код подключения" + }, + "gatewaySettings.pasteBotToken": { + "defaultMessage": "Вставьте токен бота здесь" + }, + "gatewaySettings.remove": { + "defaultMessage": "Удалить" + }, + "gatewaySettings.running": { + "defaultMessage": "Выполняется" + }, + "gatewaySettings.sendCodeToPair": { + "defaultMessage": "Отправьте этот код вашему боту {gatewayType} для подключения." + }, + "gatewaySettings.start": { + "defaultMessage": "Запустить" + }, + "gatewaySettings.stop": { + "defaultMessage": "Остановить" + }, + "gatewaySettings.stopped": { + "defaultMessage": "Остановлено" + }, + "gatewaySettings.telegram": { + "defaultMessage": "Telegram" + }, + "goosehintsModal.close": { + "defaultMessage": "Закрыть" + }, + "goosehintsModal.developer": { + "defaultMessage": "Разработчик" + }, + "goosehintsModal.dialogDescription": { + "defaultMessage": "Предоставьте дополнительный контекст о проекте, чтобы улучшить взаимодействие с Goose" + }, + "goosehintsModal.dialogTitle": { + "defaultMessage": "Настроить подсказки проекта (.goosehints)" + }, + "goosehintsModal.errorReading": { + "defaultMessage": "Ошибка чтения файла .goosehints: {error}" + }, + "goosehintsModal.failedToAccess": { + "defaultMessage": "Не удалось получить доступ к файлу .goosehints" + }, + "goosehintsModal.failedToSave": { + "defaultMessage": "Не удалось сохранить файл .goosehints" + }, + "goosehintsModal.fileCreating": { + "defaultMessage": "Создание нового файла .goosehints по пути: {filePath}" + }, + "goosehintsModal.fileFound": { + "defaultMessage": "Файл .goosehints найден по пути: {filePath}" + }, + "goosehintsModal.helpText1": { + "defaultMessage": ".goosehints — это текстовый файл для дополнительного контекста о проекте и улучшения взаимодействия с Goose." + }, + "goosehintsModal.helpText2": { + "defaultMessage": "Убедитесь, что расширение {bold} включено на странице расширений. Оно необходимо для использования .goosehints. Чтобы обновления .goosehints вступили в силу, перезапустите сеанс." + }, + "goosehintsModal.helpText3": { + "defaultMessage": "Подробнее см. {link}." + }, + "goosehintsModal.helpTextLink": { + "defaultMessage": "использование .goosehints" + }, + "goosehintsModal.placeholder": { + "defaultMessage": "Введите подсказки проекта здесь..." + }, + "goosehintsModal.save": { + "defaultMessage": "Сохранить" + }, + "goosehintsModal.savedSuccessfully": { + "defaultMessage": "Успешно сохранено" + }, + "goosehintsModal.saving": { + "defaultMessage": "Сохранение..." + }, + "goosehintsSection.configure": { + "defaultMessage": "Настроить" + }, + "goosehintsSection.description": { + "defaultMessage": "Настройте файл .goosehints вашего проекта, чтобы предоставить Goose дополнительный контекст" + }, + "goosehintsSection.title": { + "defaultMessage": "Подсказки проекта (.goosehints)" + }, + "groupedExtensionLoadingToast.askGoose": { + "defaultMessage": "Спросить goose" + }, + "groupedExtensionLoadingToast.collapseDetails": { + "defaultMessage": "Свернуть детали" + }, + "groupedExtensionLoadingToast.copied": { + "defaultMessage": "Скопировано!" + }, + "groupedExtensionLoadingToast.copyError": { + "defaultMessage": "Скопировать ошибку" + }, + "groupedExtensionLoadingToast.expandDetails": { + "defaultMessage": "Развернуть детали" + }, + "groupedExtensionLoadingToast.failedToAddExtension": { + "defaultMessage": "Не удалось добавить расширение" + }, + "groupedExtensionLoadingToast.failedToLoad": { + "defaultMessage": "{count, plural, one {# расширение не загрузилось} few {# расширения не загрузились} many {# расширений не загрузилось} other {# расширения не загрузились}}" + }, + "groupedExtensionLoadingToast.loadingExtensions": { + "defaultMessage": "{count, plural, one {Загрузка # расширения...} few {Загрузка # расширений...} many {Загрузка # расширений...} other {Загрузка # расширения...}}" + }, + "groupedExtensionLoadingToast.partiallyLoaded": { + "defaultMessage": "{totalCount, plural, one {Загружено {successCount}/# расширение} few {Загружено {successCount}/# расширения} many {Загружено {successCount}/# расширений} other {Загружено {successCount}/# расширения}}" + }, + "groupedExtensionLoadingToast.showDetails": { + "defaultMessage": "Показать детали" + }, + "groupedExtensionLoadingToast.showLess": { + "defaultMessage": "Показать меньше" + }, + "groupedExtensionLoadingToast.successfullyLoaded": { + "defaultMessage": "{count, plural, one {Успешно загружено # расширение} few {Успешно загружено # расширения} many {Успешно загружено # расширений} other {Успешно загружено # расширения}}" + }, + "headersSection.add": { + "defaultMessage": "Добавить" + }, + "headersSection.bothRequired": { + "defaultMessage": "Необходимо ввести имя и значение заголовка" + }, + "headersSection.duplicateHeader": { + "defaultMessage": "Заголовок с таким именем уже существует" + }, + "headersSection.headerName": { + "defaultMessage": "Имя заголовка" + }, + "headersSection.headersDescription": { + "defaultMessage": "Добавьте пользовательские HTTP-заголовки для запросов к MCP-серверу. После заполнения обоих полей нажмите «+», чтобы добавить." + }, + "headersSection.noSpaces": { + "defaultMessage": "Имя заголовка не может содержать пробелы" + }, + "headersSection.requestHeaders": { + "defaultMessage": "Заголовки запроса" + }, + "headersSection.value": { + "defaultMessage": "Значение" + }, + "hub.goodAfternoon": { + "defaultMessage": "Добрый день" + }, + "hub.goodEvening": { + "defaultMessage": "Добрый вечер" + }, + "hub.goodMorning": { + "defaultMessage": "Доброе утро" + }, + "huggingFaceModelSearch.download": { + "defaultMessage": "Скачать" + }, + "huggingFaceModelSearch.downloaded": { + "defaultMessage": "Скачано" + }, + "huggingFaceModelSearch.downloading": { + "defaultMessage": "Скачивание…" + }, + "huggingFaceModelSearch.loadingVariants": { + "defaultMessage": "Загрузка вариантов..." + }, + "huggingFaceModelSearch.noGgufModels": { + "defaultMessage": "По этому запросу модели GGUF не найдены." + }, + "huggingFaceModelSearch.recommended": { + "defaultMessage": "Рекомендуется" + }, + "huggingFaceModelSearch.searchError": { + "defaultMessage": "Ошибка поиска: {details}" + }, + "huggingFaceModelSearch.searchFailed": { + "defaultMessage": "Поиск не удался. Повторите попытку." + }, + "huggingFaceModelSearch.searchHuggingFace": { + "defaultMessage": "Поиск в HuggingFace" + }, + "huggingFaceModelSearch.searchNoData": { + "defaultMessage": "Поиск не вернул данных." + }, + "huggingFaceModelSearch.searchPlaceholder": { + "defaultMessage": "Поиск моделей GGUF..." + }, + "huggingFaceModelSearch.tooLarge": { + "defaultMessage": "Может не поместиться в память (модель {size}, доступно {available})" + }, + "imagePreview.altText": { + "defaultMessage": "изображение goose" + }, + "imagePreview.clickToCollapse": { + "defaultMessage": "Нажмите, чтобы свернуть" + }, + "imagePreview.clickToExpand": { + "defaultMessage": "Нажмите, чтобы развернуть" + }, + "imagePreview.unableToLoad": { + "defaultMessage": "Не удалось загрузить изображение" + }, + "importRecipeForm.cancel": { + "defaultMessage": "Отмена" + }, + "importRecipeForm.deeplinkHint": { + "defaultMessage": "Вставьте диплинк рецепта, начинающийся с «goose://recipe?config=»" + }, + "importRecipeForm.deeplinkPlaceholder": { + "defaultMessage": "Вставьте сюда ваш диплинк goose://recipe?config=..." + }, + "importRecipeForm.example": { + "defaultMessage": "пример" + }, + "importRecipeForm.expectedRecipeStructure": { + "defaultMessage": "Ожидаемая структура рецепта" + }, + "importRecipeForm.importRecipeButton": { + "defaultMessage": "Импортировать рецепт" + }, + "importRecipeForm.importRecipeTitle": { + "defaultMessage": "Импортировать рецепт" + }, + "importRecipeForm.importing": { + "defaultMessage": "Импорт..." + }, + "importRecipeForm.or": { + "defaultMessage": "ИЛИ" + }, + "importRecipeForm.recipeDeeplinkLabel": { + "defaultMessage": "Диплинк рецепта" + }, + "importRecipeForm.recipeFileHint": { + "defaultMessage": "Загрузите YAML- или JSON-файл со структурой рецепта" + }, + "importRecipeForm.recipeFileLabel": { + "defaultMessage": "Файл рецепта" + }, + "importRecipeForm.reviewWarning": { + "defaultMessage": "Проверяйте содержимое файлов рецептов перед добавлением в интерфейс goose." + }, + "importRecipeForm.schemaDescription": { + "defaultMessage": "Ваш YAML- или JSON-файл должен соответствовать этой структуре. Обязательные поля: title, description и instructions или prompt." + }, + "inlineEditText.clickToEdit": { + "defaultMessage": "Нажмите, чтобы изменить" + }, + "inlineEditText.doubleClickToEdit": { + "defaultMessage": "Дважды нажмите, чтобы изменить" + }, + "inlineEditText.enterText": { + "defaultMessage": "Введите текст" + }, + "inlineEditText.failedToSave": { + "defaultMessage": "Не удалось сохранить" + }, + "instructionsEditor.cancel": { + "defaultMessage": "Отмена" + }, + "instructionsEditor.insertExample": { + "defaultMessage": "Вставить пример" + }, + "instructionsEditor.label": { + "defaultMessage": "Инструкции" + }, + "instructionsEditor.placeholder": { + "defaultMessage": "Подробные инструкции для ИИ, скрытые от пользователя" + }, + "instructionsEditor.save": { + "defaultMessage": "Сохранить инструкции" + }, + "instructionsEditor.syntaxHelp": { + "defaultMessage": "Используйте синтаксис {code}, чтобы определить параметры, которые пользователи смогут заполнить" + }, + "instructionsEditor.title": { + "defaultMessage": "Редактор инструкций" + }, + "jsonSchemaEditor.cancel": { + "defaultMessage": "Отмена" + }, + "jsonSchemaEditor.description": { + "defaultMessage": "Определите ожидаемую структуру ответа ИИ в формате JSON Schema" + }, + "jsonSchemaEditor.insertExample": { + "defaultMessage": "Вставить пример" + }, + "jsonSchemaEditor.invalidJson": { + "defaultMessage": "Неверный формат JSON" + }, + "jsonSchemaEditor.label": { + "defaultMessage": "JSON-схема ответа" + }, + "jsonSchemaEditor.save": { + "defaultMessage": "Сохранить схему" + }, + "jsonSchemaEditor.title": { + "defaultMessage": "Редактор JSON Schema" + }, + "jsonSchemaForm.cancel": { + "defaultMessage": "Отмена" + }, + "jsonSchemaForm.fieldRequired": { + "defaultMessage": "Это поле обязательно" + }, + "jsonSchemaForm.maxLength": { + "defaultMessage": "Максимальная длина: {maxLength}" + }, + "jsonSchemaForm.maxValue": { + "defaultMessage": "Максимальное значение: {maximum}" + }, + "jsonSchemaForm.minLength": { + "defaultMessage": "Минимальная длина: {minLength}" + }, + "jsonSchemaForm.minValue": { + "defaultMessage": "Минимальное значение: {minimum}" + }, + "jsonSchemaForm.noFields": { + "defaultMessage": "Нет полей для отображения" + }, + "jsonSchemaForm.selectPlaceholder": { + "defaultMessage": "Выберите..." + }, + "jsonSchemaForm.submit": { + "defaultMessage": "Отправить" + }, + "keyValueEditor.addValue": { + "defaultMessage": "Добавить предварительно настроенное значение" + }, + "keyValueEditor.defaultKeyPlaceholder": { + "defaultMessage": "Имя параметра..." + }, + "keyValueEditor.defaultValuePlaceholder": { + "defaultMessage": "Значение параметра..." + }, + "keyValueEditor.removeValue": { + "defaultMessage": "Удалить предварительно настроенное значение {key}" + }, + "keyboardShortcuts.alwaysOnTopDescription": { + "defaultMessage": "Переключить окно поверх всех" + }, + "keyboardShortcuts.alwaysOnTopLabel": { + "defaultMessage": "Поверх всех окон" + }, + "keyboardShortcuts.cancel": { + "defaultMessage": "Отмена" + }, + "keyboardShortcuts.categoryApplication": { + "defaultMessage": "Горячие клавиши приложения" + }, + "keyboardShortcuts.categoryApplicationDescription": { + "defaultMessage": "Эти сочетания работают, когда Goose является активным приложением" + }, + "keyboardShortcuts.categoryGlobal": { + "defaultMessage": "Глобальные горячие клавиши" + }, + "keyboardShortcuts.categoryGlobalDescription": { + "defaultMessage": "Эти сочетания работают во всей системе, даже когда Goose не в фокусе" + }, + "keyboardShortcuts.categorySearch": { + "defaultMessage": "Горячие клавиши поиска" + }, + "keyboardShortcuts.categorySearchDescription": { + "defaultMessage": "Эти сочетания работают при поиске в разговоре" + }, + "keyboardShortcuts.categoryWindow": { + "defaultMessage": "Горячие клавиши окна" + }, + "keyboardShortcuts.categoryWindowDescription": { + "defaultMessage": "Эти сочетания управляют поведением окна" + }, + "keyboardShortcuts.change": { + "defaultMessage": "Изменить" + }, + "keyboardShortcuts.disabled": { + "defaultMessage": "Отключено" + }, + "keyboardShortcuts.dismiss": { + "defaultMessage": "Закрыть" + }, + "keyboardShortcuts.findDescription": { + "defaultMessage": "Открыть поиск в разговоре" + }, + "keyboardShortcuts.findLabel": { + "defaultMessage": "Найти" + }, + "keyboardShortcuts.findNextDescription": { + "defaultMessage": "Перейти к следующему результату поиска" + }, + "keyboardShortcuts.findNextLabel": { + "defaultMessage": "Найти следующее" + }, + "keyboardShortcuts.findPreviousDescription": { + "defaultMessage": "Перейти к предыдущему результату поиска" + }, + "keyboardShortcuts.findPreviousLabel": { + "defaultMessage": "Найти предыдущее" + }, + "keyboardShortcuts.focusWindowDescription": { + "defaultMessage": "Вывести окно Goose на передний план из любого места" + }, + "keyboardShortcuts.focusWindowLabel": { + "defaultMessage": "Сфокусировать окно Goose" + }, + "keyboardShortcuts.loading": { + "defaultMessage": "Загрузка..." + }, + "keyboardShortcuts.newChatDescription": { + "defaultMessage": "Создать новый чат в текущем окне" + }, + "keyboardShortcuts.newChatLabel": { + "defaultMessage": "Новый чат" + }, + "keyboardShortcuts.newChatWindowDescription": { + "defaultMessage": "Открыть новое окно Goose" + }, + "keyboardShortcuts.newChatWindowLabel": { + "defaultMessage": "Новое окно чата" + }, + "keyboardShortcuts.openDirectoryDescription": { + "defaultMessage": "Открыть диалог выбора каталога" + }, + "keyboardShortcuts.openDirectoryLabel": { + "defaultMessage": "Открыть каталог" + }, + "keyboardShortcuts.quickLauncherDescription": { + "defaultMessage": "Открыть всплывающее окно быстрого запуска" + }, + "keyboardShortcuts.quickLauncherLabel": { + "defaultMessage": "Быстрый запуск" + }, + "keyboardShortcuts.reassignShortcut": { + "defaultMessage": "Переназначить сочетание" + }, + "keyboardShortcuts.resetAllShortcuts": { + "defaultMessage": "Сбросить все сочетания" + }, + "keyboardShortcuts.resetShortcutsDetail": { + "defaultMessage": "Это восстановит исходную конфигурацию всех сочетаний." + }, + "keyboardShortcuts.resetShortcutsMessage": { + "defaultMessage": "Сбросить все сочетания клавиш к значениям по умолчанию?" + }, + "keyboardShortcuts.resetShortcutsTitle": { + "defaultMessage": "Сбросить сочетания клавиш" + }, + "keyboardShortcuts.resetToDefaultsDescription": { + "defaultMessage": "Восстановить исходную конфигурацию всех сочетаний клавиш" + }, + "keyboardShortcuts.resetToDefaultsHeading": { + "defaultMessage": "Сбросить к значениям по умолчанию" + }, + "keyboardShortcuts.restartDescription": { + "defaultMessage": "Чтобы изменения сочетаний приложения (например, Новый чат, Настройки и т. д.) вступили в силу, требуется перезапуск Goose. Глобальные сочетания (Фокус окна, Быстрый запуск) работают сразу." + }, + "keyboardShortcuts.restartRequired": { + "defaultMessage": "Требуется перезапуск" + }, + "keyboardShortcuts.settingsDescription": { + "defaultMessage": "Открыть панель настроек" + }, + "keyboardShortcuts.settingsLabel": { + "defaultMessage": "Настройки" + }, + "keyboardShortcuts.shortcutConflictSaveDetail": { + "defaultMessage": "Сохранение удалит сочетание из «{conflictLabel}» и назначит его «{targetLabel}». Продолжить?" + }, + "keyboardShortcuts.shortcutConflictTitle": { + "defaultMessage": "Конфликт сочетаний" + }, + "keyboardShortcuts.shortcutConflictToggleDetail": { + "defaultMessage": "Включение удалит сочетание из «{conflictLabel}» и назначит его «{targetLabel}». Продолжить?" + }, + "keyboardShortcuts.shortcutConflictToggleMessage": { + "defaultMessage": "Сочетание {shortcut} уже назначено «{conflictLabel}»." + }, + "keyboardShortcuts.toggleNavigationDescription": { + "defaultMessage": "Показать или скрыть меню навигации" + }, + "keyboardShortcuts.toggleNavigationLabel": { + "defaultMessage": "Переключить навигацию" + }, + "launcher.placeholder": { + "defaultMessage": "Спросите goose о чем угодно..." + }, + "loadingGoose.compacting": { + "defaultMessage": "goose сжимает разговор..." + }, + "loadingGoose.idle": { + "defaultMessage": "goose работает над этим…" + }, + "loadingGoose.loadingConversation": { + "defaultMessage": "загрузка разговора..." + }, + "loadingGoose.restartingAgent": { + "defaultMessage": "перезапуск сеанса..." + }, + "loadingGoose.streaming": { + "defaultMessage": "goose работает над этим…" + }, + "loadingGoose.thinking": { + "defaultMessage": "goose думает…" + }, + "loadingGoose.waiting": { + "defaultMessage": "goose ждет…" + }, + "localInferenceSettings.deleteConfirm": { + "defaultMessage": "Удалить эту модель? Позже ее можно скачать снова." + }, + "localInferenceSettings.description": { + "defaultMessage": "Скачивайте и управляйте локальными LLM-моделями для инференса без API-ключей. Ищите любые GGUF-модели на HuggingFace или используйте подборку ниже." + }, + "localInferenceSettings.download": { + "defaultMessage": "Скачать" + }, + "localInferenceSettings.downloadFailed": { + "defaultMessage": "Скачивание не удалось" + }, + "localInferenceSettings.downloadProgress": { + "defaultMessage": "{downloaded} / {total} ({percent}%)" + }, + "localInferenceSettings.downloadedModels": { + "defaultMessage": "Скачанные модели" + }, + "localInferenceSettings.downloading": { + "defaultMessage": "Скачивание" + }, + "localInferenceSettings.featuredModels": { + "defaultMessage": "Рекомендуемые модели" + }, + "localInferenceSettings.modelSettings": { + "defaultMessage": "Настройки модели" + }, + "localInferenceSettings.modelSettingsTitle": { + "defaultMessage": "Настройки модели" + }, + "localInferenceSettings.noModels": { + "defaultMessage": "Нет доступных моделей" + }, + "localInferenceSettings.recommended": { + "defaultMessage": "Рекомендуется" + }, + "localInferenceSettings.remaining": { + "defaultMessage": "осталось {time}" + }, + "localInferenceSettings.showAllFeatured": { + "defaultMessage": "Показать все рекомендуемые (ещё {count})" + }, + "localInferenceSettings.showRecommendedOnly": { + "defaultMessage": "Показывать только рекомендуемые" + }, + "localInferenceSettings.title": { + "defaultMessage": "Локальные модели инференса" + }, + "localInferenceSettings.vision": { + "defaultMessage": "Зрение" + }, + "localInferenceSettings.visionEncoderDownloading": { + "defaultMessage": "Скачивание визуального кодировщика…" + }, + "localInferenceSettings.visionEncoderNotDownloaded": { + "defaultMessage": "Визуальный кодировщик не скачан" + }, + "localModelManager.active": { + "defaultMessage": "Активно" + }, + "localModelManager.deleteConfirm": { + "defaultMessage": "Удалить эту модель? Позже её можно скачать снова." + }, + "localModelManager.download": { + "defaultMessage": "Скачать" + }, + "localModelManager.downloaded": { + "defaultMessage": "Скачано" + }, + "localModelManager.gpuAcceleration": { + "defaultMessage": "Поддерживает ускорение GPU (CUDA для NVIDIA, Metal для Apple Silicon). Для аппаратного ускорения функции GPU должны быть включены при сборке." + }, + "localModelManager.noModels": { + "defaultMessage": "Нет доступных моделей" + }, + "localModelManager.recommended": { + "defaultMessage": "Рекомендуется" + }, + "localModelManager.recommendedForHardware": { + "defaultMessage": "Рекомендуется для вашего оборудования" + }, + "localModelManager.showAllModels": { + "defaultMessage": "Показать все модели (ещё {count})" + }, + "localModelManager.showRecommendedOnly": { + "defaultMessage": "Показывать только рекомендуемые" + }, + "localModelPicker.back": { + "defaultMessage": "Назад" + }, + "localModelPicker.bestForMachine": { + "defaultMessage": "Лучше всего для вашего компьютера" + }, + "localModelPicker.cancelDownload": { + "defaultMessage": "Отменить скачивание" + }, + "localModelPicker.checkingModels": { + "defaultMessage": "Проверка доступных моделей..." + }, + "localModelPicker.downloadModel": { + "defaultMessage": "Скачать {modelId} ({size})" + }, + "localModelPicker.downloading": { + "defaultMessage": "Скачивание {modelId}" + }, + "localModelPicker.failedToLoad": { + "defaultMessage": "Не удалось загрузить доступные модели. Повторите попытку." + }, + "localModelPicker.failedToStartDownload": { + "defaultMessage": "Не удалось начать скачивание. Повторите попытку." + }, + "localModelPicker.hideOtherSizes": { + "defaultMessage": "Скрыть другие размеры" + }, + "localModelPicker.localModelsNote": { + "defaultMessage": "Локальные модели хранят всё на вашем компьютере для полной приватности. Производительность и размер контекстного окна могут отличаться от облачных провайдеров в зависимости от оборудования и размера модели." + }, + "localModelPicker.lostConnection": { + "defaultMessage": "Соединение для скачивания потеряно. Повторите попытку." + }, + "localModelPicker.modelNotFound": { + "defaultMessage": "Модель не найдена" + }, + "localModelPicker.ready": { + "defaultMessage": "Готово" + }, + "localModelPicker.selectModel": { + "defaultMessage": "Выберите модель" + }, + "localModelPicker.showOtherSizes": { + "defaultMessage": "Показать другие размеры ({count})" + }, + "localModelPicker.startingDownload": { + "defaultMessage": "Начало скачивания..." + }, + "localModelPicker.tryAgain": { + "defaultMessage": "Повторить попытку" + }, + "localModelPicker.useModel": { + "defaultMessage": "Использовать {modelId}" + }, + "markdownContent.cancel": { + "defaultMessage": "Отмена" + }, + "markdownContent.copyCode": { + "defaultMessage": "Копировать код" + }, + "markdownContent.failedToOpenLink": { + "defaultMessage": "Не удалось открыть ссылку" + }, + "markdownContent.noApplicationFound": { + "defaultMessage": "Не найдено приложение для открытия этой ссылки." + }, + "markdownContent.open": { + "defaultMessage": "Открыть" + }, + "markdownContent.openExternalLink": { + "defaultMessage": "Открыть внешнюю ссылку" + }, + "markdownContent.openProtocolLink": { + "defaultMessage": "Открыть ссылку {protocol}?" + }, + "markdownContent.thisWillOpen": { + "defaultMessage": "Будет открыто: {href}" + }, + "mcpAppRenderer.appFallbackTitle": { + "defaultMessage": "Приложение" + }, + "mcpAppRenderer.cancelButton": { + "defaultMessage": "Отмена" + }, + "mcpAppRenderer.close": { + "defaultMessage": "Закрыть" + }, + "mcpAppRenderer.exitFullscreen": { + "defaultMessage": "Выйти из полноэкранного режима" + }, + "mcpAppRenderer.exitFullscreenTitle": { + "defaultMessage": "Выйти из полноэкранного режима (Esc)" + }, + "mcpAppRenderer.failedToInitSandbox": { + "defaultMessage": "Не удалось инициализировать прокси песочницы" + }, + "mcpAppRenderer.failedToLoadResource": { + "defaultMessage": "Не удалось загрузить ресурс" + }, + "mcpAppRenderer.fullscreen": { + "defaultMessage": "Полный экран" + }, + "mcpAppRenderer.invalidUrl": { + "defaultMessage": "Неверный URL" + }, + "mcpAppRenderer.movePipWindow": { + "defaultMessage": "Переместить окно «картинка в картинке» (используйте стрелки)" + }, + "mcpAppRenderer.openButton": { + "defaultMessage": "Открыть" + }, + "mcpAppRenderer.openExternalLinkTitle": { + "defaultMessage": "Открыть внешнюю ссылку" + }, + "mcpAppRenderer.openLinkDetail": { + "defaultMessage": "Будет открыто: {url}" + }, + "mcpAppRenderer.openProtocolLink": { + "defaultMessage": "Открыть ссылку {protocol}?" + }, + "mcpAppRenderer.pictureInPicture": { + "defaultMessage": "Картинка в картинке" + }, + "mcpAppRenderer.playingInPip": { + "defaultMessage": "Воспроизведение в режиме «картинка в картинке»" + }, + "mcpUIResourceRenderer.cancelButton": { + "defaultMessage": "Отмена" + }, + "mcpUIResourceRenderer.openButton": { + "defaultMessage": "Открыть" + }, + "mcpUIResourceRenderer.openExternalLinkTitle": { + "defaultMessage": "Открыть внешнюю ссылку" + }, + "mcpUIResourceRenderer.openLinkDetail": { + "defaultMessage": "Будет открыто: {url}" + }, + "mcpUIResourceRenderer.openProtocolLink": { + "defaultMessage": "Открыть ссылку {protocol}?" + }, + "mcpUIResourceRenderer.toastMessageReceived": { + "defaultMessage": "Получено сообщение для {message}." + }, + "mcpUIResourceRenderer.toastTitle": { + "defaultMessage": "Сообщение MCP-UI {messageType}" + }, + "mcpUIResourceRenderer.toastUnsupported": { + "defaultMessage": "Получено сообщение для {message}. Сообщения {messageType} пока не поддерживаются; подробности смотрите в консоли." + }, + "mentionPopover.itemsFound": { + "defaultMessage": "{count, plural, one {Найден # элемент} few {Найдено # элемента} many {Найдено # элементов} other {Найдено # элемента}}" + }, + "mentionPopover.loadingCommands": { + "defaultMessage": "Загрузка команд..." + }, + "mentionPopover.noCommandsFound": { + "defaultMessage": "Команды по запросу «{query}» не найдены" + }, + "mentionPopover.noItemsFound": { + "defaultMessage": "Элементы по запросу «{query}» не найдены" + }, + "mentionPopover.scanningFiles": { + "defaultMessage": "Сканирование файлов..." + }, + "messageCopyLink.copied": { + "defaultMessage": "Скопировано!" + }, + "messageCopyLink.copy": { + "defaultMessage": "Копировать" + }, + "messageQueue.cancel": { + "defaultMessage": "Отмена" + }, + "messageQueue.cannotSendWhileEditing": { + "defaultMessage": "Нельзя отправить во время редактирования" + }, + "messageQueue.clearAll": { + "defaultMessage": "Очистить всё" + }, + "messageQueue.clickToEdit": { + "defaultMessage": "{content} (нажмите, чтобы изменить)" + }, + "messageQueue.collapseQueue": { + "defaultMessage": "Свернуть очередь" + }, + "messageQueue.dragToReorder": { + "defaultMessage": "Перетащите сообщения, чтобы изменить приоритет" + }, + "messageQueue.expandQueue": { + "defaultMessage": "Развернуть очередь" + }, + "messageQueue.messageCount": { + "defaultMessage": "{count, plural, one {# сообщение {status}} few {# сообщения {status}} many {# сообщений {status}} other {# сообщения {status}}}" + }, + "messageQueue.messageQueue": { + "defaultMessage": "Очередь сообщений" + }, + "messageQueue.next": { + "defaultMessage": "Далее" + }, + "messageQueue.paused": { + "defaultMessage": "На паузе" + }, + "messageQueue.queuePaused": { + "defaultMessage": "Очередь приостановлена" + }, + "messageQueue.queuePausedCompact": { + "defaultMessage": "Очередь приостановлена - нажмите «Отправить» или добавьте новое сообщение, чтобы продолжить" + }, + "messageQueue.queuePausedExpanded": { + "defaultMessage": "Очередь приостановлена из-за прерывания. Используйте «Отправить сейчас» или добавьте новое сообщение, чтобы продолжить." + }, + "messageQueue.queued": { + "defaultMessage": "в очереди" + }, + "messageQueue.removeFromQueue": { + "defaultMessage": "Удалить это сообщение из очереди" + }, + "messageQueue.save": { + "defaultMessage": "Сохранить" + }, + "messageQueue.sendNow": { + "defaultMessage": "Отправить это сообщение сейчас" + }, + "messageQueue.stopAndSend": { + "defaultMessage": "Остановить текущую обработку и отправить это сообщение сейчас" + }, + "messageQueue.waiting": { + "defaultMessage": "ожидание" + }, + "microphoneSelector.chooseDescription": { + "defaultMessage": "Выберите микрофон для диктовки" + }, + "microphoneSelector.grantAccess": { + "defaultMessage": "Предоставить доступ" + }, + "microphoneSelector.grantAccessDescription": { + "defaultMessage": "Предоставьте доступ, чтобы увидеть доступные микрофоны" + }, + "microphoneSelector.microphone": { + "defaultMessage": "Микрофон" + }, + "microphoneSelector.microphoneLabel": { + "defaultMessage": "Микрофон {index}" + }, + "microphoneSelector.selectedMicrophone": { + "defaultMessage": "Выбранный микрофон" + }, + "microphoneSelector.speakToTest": { + "defaultMessage": "Говорите, чтобы проверить микрофон ({seconds}с)" + }, + "microphoneSelector.stop": { + "defaultMessage": "Остановить" + }, + "microphoneSelector.systemDefault": { + "defaultMessage": "Системный по умолчанию" + }, + "microphoneSelector.test": { + "defaultMessage": "Проверить" + }, + "modeSelectionItem.autonomousDescription": { + "defaultMessage": "Полные возможности изменения файлов: свободное редактирование, создание и удаление файлов." + }, + "modeSelectionItem.autonomousLabel": { + "defaultMessage": "Автономный" + }, + "modeSelectionItem.chatOnlyDescription": { + "defaultMessage": "Работать с выбранным провайдером без использования инструментов или расширений." + }, + "modeSelectionItem.chatOnlyLabel": { + "defaultMessage": "Только чат" + }, + "modeSelectionItem.manualDescription": { + "defaultMessage": "Все инструменты, расширения и изменения файлов будут требовать подтверждения человеком" + }, + "modeSelectionItem.manualLabel": { + "defaultMessage": "Вручную" + }, + "modeSelectionItem.smartDescription": { + "defaultMessage": "Интеллектуально определять, какие действия требуют подтверждения, исходя из уровня риска" + }, + "modeSelectionItem.smartLabel": { + "defaultMessage": "Умный" + }, + "modelAndProviderContext.modelChangeFailed": { + "defaultMessage": "{provider}/{model} не удалось" + }, + "modelAndProviderContext.modelChangedTitle": { + "defaultMessage": "Модель изменена" + }, + "modelAndProviderContext.selectModel": { + "defaultMessage": "Выбрать модель" + }, + "modelAndProviderContext.switchModelSuccess": { + "defaultMessage": "Модель успешно переключена -- используется {model} от {provider}" + }, + "modelAndProviderContext.unknownProviderMsg": { + "defaultMessage": "Неизвестный провайдер в конфигурации -- проверьте config.yaml" + }, + "modelAndProviderContext.unknownProviderTitle": { + "defaultMessage": "Поиск имени провайдера" + }, + "modelSettingsButtons.configureProviders": { + "defaultMessage": "Настроить провайдеров" + }, + "modelSettingsButtons.switchModels": { + "defaultMessage": "Переключить модели" + }, + "modelSettingsPanel.batchSize": { + "defaultMessage": "Размер пакета" + }, + "modelSettingsPanel.batchSizeDescription": { + "defaultMessage": "Пакет обработки промпта" + }, + "modelSettingsPanel.contextAndGeneration": { + "defaultMessage": "Контекст и генерация" + }, + "modelSettingsPanel.contextSize": { + "defaultMessage": "Размер контекста" + }, + "modelSettingsPanel.contextSizeDescription": { + "defaultMessage": "Максимальное контекстное окно (0 = по умолчанию модели)" + }, + "modelSettingsPanel.etaLearningRate": { + "defaultMessage": "Eta (скорость обучения)" + }, + "modelSettingsPanel.flashAttention": { + "defaultMessage": "Flash attention" + }, + "modelSettingsPanel.flashAttentionDescription": { + "defaultMessage": "Включить оптимизацию flash attention" + }, + "modelSettingsPanel.frequencyPenalty": { + "defaultMessage": "Штраф частоты" + }, + "modelSettingsPanel.frequencyPenaltyDescription": { + "defaultMessage": "0.0 = выкл." + }, + "modelSettingsPanel.gpuLayers": { + "defaultMessage": "Слои GPU" + }, + "modelSettingsPanel.gpuLayersDescription": { + "defaultMessage": "Слои для выгрузки на GPU" + }, + "modelSettingsPanel.loadingSettings": { + "defaultMessage": "Загрузка настроек..." + }, + "modelSettingsPanel.lockModelInRam": { + "defaultMessage": "Закрепить модель в RAM (mlock)" + }, + "modelSettingsPanel.lockModelInRamDescription": { + "defaultMessage": "Не допускать выгрузки модели на диск" + }, + "modelSettingsPanel.maxOutputTokens": { + "defaultMessage": "Максимум выходных токенов" + }, + "modelSettingsPanel.maxOutputTokensDescription": { + "defaultMessage": "Ограничение сгенерированных токенов" + }, + "modelSettingsPanel.minP": { + "defaultMessage": "Min P" + }, + "modelSettingsPanel.nativeToolCalling": { + "defaultMessage": "Нативный вызов инструментов" + }, + "modelSettingsPanel.nativeToolCallingDescription": { + "defaultMessage": "Использовать встроенный формат вызова инструментов модели вместо эмулятора shell-команд. Включайте для крупных моделей, надежно поддерживающих вызов tool calling." + }, + "modelSettingsPanel.performance": { + "defaultMessage": "Производительность" + }, + "modelSettingsPanel.presencePenalty": { + "defaultMessage": "Штраф присутствия" + }, + "modelSettingsPanel.presencePenaltyDescription": { + "defaultMessage": "0.0 = выкл." + }, + "modelSettingsPanel.repeatPenalty": { + "defaultMessage": "Штраф повторов" + }, + "modelSettingsPanel.repeatPenaltyDescription": { + "defaultMessage": "1.0 = выкл." + }, + "modelSettingsPanel.repeatWindow": { + "defaultMessage": "Окно повторов" + }, + "modelSettingsPanel.repeatWindowDescription": { + "defaultMessage": "Количество токенов для просмотра назад" + }, + "modelSettingsPanel.repetitionPenalty": { + "defaultMessage": "Штраф повторения" + }, + "modelSettingsPanel.reset": { + "defaultMessage": "Сбросить" + }, + "modelSettingsPanel.resetToDefaults": { + "defaultMessage": "Сбросить к значениям по умолчанию" + }, + "modelSettingsPanel.samplingStrategy": { + "defaultMessage": "Стратегия сэмплирования" + }, + "modelSettingsPanel.saving": { + "defaultMessage": "Сохранение..." + }, + "modelSettingsPanel.seed": { + "defaultMessage": "Seed" + }, + "modelSettingsPanel.tauTargetEntropy": { + "defaultMessage": "Tau (целевая энтропия)" + }, + "modelSettingsPanel.temperature": { + "defaultMessage": "Температура" + }, + "modelSettingsPanel.threads": { + "defaultMessage": "Потоки" + }, + "modelSettingsPanel.threadsDescription": { + "defaultMessage": "Потоки CPU для генерации" + }, + "modelSettingsPanel.toolCalling": { + "defaultMessage": "Вызов инструментов" + }, + "modelSettingsPanel.topK": { + "defaultMessage": "Top K" + }, + "modelSettingsPanel.topP": { + "defaultMessage": "Top P" + }, + "modelsBottomBar.changeModel": { + "defaultMessage": "Изменить модель" + }, + "modelsBottomBar.currentModel": { + "defaultMessage": "Текущая модель" + }, + "modelsBottomBar.loadingModel": { + "defaultMessage": "Загрузка модели..." + }, + "modelsBottomBar.localModelSettings": { + "defaultMessage": "Настройки локальной модели" + }, + "modelsBottomBar.localModelSettingsTitle": { + "defaultMessage": "Настройки локальной модели — {modelName}" + }, + "modelsBottomBar.resolvedModel": { + "defaultMessage": "Разрешенная модель" + }, + "modelsBottomBar.selectModel": { + "defaultMessage": "Выбрать модель" + }, + "modelsSection.resetDescription": { + "defaultMessage": "Очистить выбранную модель и настройки провайдера, чтобы начать заново" + }, + "modelsSection.resetTitle": { + "defaultMessage": "Сбросить провайдера и модель" + }, + "navigation.itemApps": { + "defaultMessage": "Приложения" + }, + "navigation.itemExtensions": { + "defaultMessage": "Расширения" + }, + "navigation.itemHome": { + "defaultMessage": "Новый чат" + }, + "navigation.itemRecipes": { + "defaultMessage": "Рецепты" + }, + "navigation.itemScheduler": { + "defaultMessage": "Планировщик" + }, + "navigation.itemSessions": { + "defaultMessage": "История сеансов" + }, + "navigation.itemSettings": { + "defaultMessage": "Настройки" + }, + "navigation.itemSkills": { + "defaultMessage": "Навыки" + }, + "navigationPanel.chats": { + "defaultMessage": "Чаты" + }, + "navigationPanel.collapseSidebar": { + "defaultMessage": "Свернуть боковую панель" + }, + "navigationPanel.noChats": { + "defaultMessage": "Нет недавних чатов" + }, + "navigationPanel.untitledSession": { + "defaultMessage": "Безымянный сеанс" + }, + "onboardingGuard.checkProviderErrorDescription": { + "defaultMessage": "Сервер может запускаться или быть временно недоступен." + }, + "onboardingGuard.checkProviderErrorTitle": { + "defaultMessage": "Не удалось подключиться к серверу Goose" + }, + "onboardingGuard.retry": { + "defaultMessage": "Повторить" + }, + "onboardingGuard.welcomeDescription": { + "defaultMessage": "Ваш локальный ИИ-агент. Подключите провайдера ИИ-модели, чтобы начать." + }, + "onboardingGuard.welcomeTitle": { + "defaultMessage": "Добро пожаловать в goose" + }, + "onboardingSuccess.allSet": { + "defaultMessage": "Все готово для начала работы с goose." + }, + "onboardingSuccess.connectedTo": { + "defaultMessage": "Подключено к {providerName}" + }, + "onboardingSuccess.getStarted": { + "defaultMessage": "Начать" + }, + "onboardingSuccess.learnMore": { + "defaultMessage": "Подробнее" + }, + "onboardingSuccess.localModelReady": { + "defaultMessage": "Локальная модель готова" + }, + "onboardingSuccess.privacyDescription": { + "defaultMessage": "Анонимные данные об использовании помогают улучшать goose. Мы никогда не собираем ваши разговоры, код или персональные данные." + }, + "onboardingSuccess.privacyTitle": { + "defaultMessage": "Конфиденциальность" + }, + "onboardingSuccess.shareUsageData": { + "defaultMessage": "Поделиться анонимными данными об использовании" + }, + "parameterInput.defaultValue": { + "defaultMessage": "Значение по умолчанию" + }, + "parameterInput.defaultValuePlaceholder": { + "defaultMessage": "Введите значение по умолчанию" + }, + "parameterInput.deleteParameter": { + "defaultMessage": "Удалить параметр: {key}" + }, + "parameterInput.description": { + "defaultMessage": "описание" + }, + "parameterInput.descriptionHelp": { + "defaultMessage": "Это сообщение увидит конечный пользователь." + }, + "parameterInput.descriptionPlaceholder": { + "defaultMessage": "Например: «Введите имя нового компонента»" + }, + "parameterInput.inputType": { + "defaultMessage": "Тип ввода" + }, + "parameterInput.optional": { + "defaultMessage": "Необязательно" + }, + "parameterInput.optionsHelp": { + "defaultMessage": "Введите каждый вариант с новой строки. Они будут показаны как пункты выпадающего списка." + }, + "parameterInput.optionsLabel": { + "defaultMessage": "Варианты (по одному на строку)" + }, + "parameterInput.optionsPlaceholder": { + "defaultMessage": "Вариант 1 Вариант 2 Вариант 3" + }, + "parameterInput.required": { + "defaultMessage": "Обязательно" + }, + "parameterInput.requirement": { + "defaultMessage": "Требование" + }, + "parameterInput.typeBoolean": { + "defaultMessage": "Булево" + }, + "parameterInput.typeNumber": { + "defaultMessage": "Число" + }, + "parameterInput.typeSelect": { + "defaultMessage": "Селект" + }, + "parameterInput.typeString": { + "defaultMessage": "Строка" + }, + "parameterInput.unused": { + "defaultMessage": "Не используется" + }, + "parameterInput.unusedWarningTitle": { + "defaultMessage": "Этот параметр не используется в инструкциях или промпте. Он будет доступен для ручного ввода, но может быть не нужен." + }, + "parameterInputModal.backToForm": { + "defaultMessage": "Вернуться к форме параметров" + }, + "parameterInputModal.cancel": { + "defaultMessage": "Отмена" + }, + "parameterInputModal.cancelRecipeSetup": { + "defaultMessage": "Отменить настройку рецепта" + }, + "parameterInputModal.enterValue": { + "defaultMessage": "Введите значение для {key}..." + }, + "parameterInputModal.false": { + "defaultMessage": "False" + }, + "parameterInputModal.recipeParameters": { + "defaultMessage": "Параметры рецепта" + }, + "parameterInputModal.select": { + "defaultMessage": "Выберите..." + }, + "parameterInputModal.selectOption": { + "defaultMessage": "Выберите вариант..." + }, + "parameterInputModal.startNewChat": { + "defaultMessage": "Начать новый чат (без рецепта)" + }, + "parameterInputModal.startRecipe": { + "defaultMessage": "Запустить рецепт" + }, + "parameterInputModal.true": { + "defaultMessage": "True" + }, + "parameterInputModal.whatToDo": { + "defaultMessage": "Что вы хотите сделать?" + }, + "permissionModal.alwaysAllow": { + "defaultMessage": "Всегда разрешать" + }, + "permissionModal.askBefore": { + "defaultMessage": "Спрашивать заранее" + }, + "permissionModal.cancel": { + "defaultMessage": "Отмена" + }, + "permissionModal.close": { + "defaultMessage": "Закрыть" + }, + "permissionModal.failedToLoadTools": { + "defaultMessage": "Не удалось загрузить инструменты" + }, + "permissionModal.failedToLoadToolsDescription": { + "defaultMessage": "Не удалось загрузить инструменты для этого расширения. Возможно, расширение не загружено в текущем сеансе." + }, + "permissionModal.neverAllow": { + "defaultMessage": "Никогда не разрешать" + }, + "permissionModal.noActiveSession": { + "defaultMessage": "Нет активного сеанса" + }, + "permissionModal.noActiveSessionDescription": { + "defaultMessage": "Сначала начните чат, чтобы настроить разрешения на инструменты для этого расширения. Разрешения на инструменты загружаются из расширений активного сеанса." + }, + "permissionModal.noToolsAvailable": { + "defaultMessage": "Для этого расширения нет доступных инструментов." + }, + "permissionModal.saveChanges": { + "defaultMessage": "Сохранить изменения" + }, + "permissionRulesModal.description": { + "defaultMessage": "Настройте разрешения на инструменты для расширений, чтобы управлять их взаимодействием с системой." + }, + "permissionRulesModal.extensionRules": { + "defaultMessage": "Правила расширений" + }, + "permissionRulesModal.title": { + "defaultMessage": "Правила разрешений" + }, + "permissionSetting.extensionRules": { + "defaultMessage": "Правила расширений" + }, + "permissionSetting.permissionRules": { + "defaultMessage": "Правила разрешений" + }, + "permissionSetting.permissionRulesDescription": { + "defaultMessage": "Скрытые инструкции, которые будут переданы провайдеру, чтобы направлять ответы и добавлять к ним контекст." + }, + "privacyInfoModal.collectErrors": { + "defaultMessage": "Типы ошибок (например, «rate_limit», «auth» - без подробностей)" + }, + "privacyInfoModal.collectExtensions": { + "defaultMessage": "Расширения и счетчики использования инструментов (только имена)" + }, + "privacyInfoModal.collectOs": { + "defaultMessage": "Операционная система, версия и архитектура" + }, + "privacyInfoModal.collectProvider": { + "defaultMessage": "Используемые провайдер и модель" + }, + "privacyInfoModal.collectSession": { + "defaultMessage": "Метрики сеанса (длительность, число взаимодействий, использование токенов)" + }, + "privacyInfoModal.collectVersion": { + "defaultMessage": "Версия goose и способ установки" + }, + "privacyInfoModal.description": { + "defaultMessage": "Анонимные данные об использовании помогают понять как используется goose и найти области для улучшения." + }, + "privacyInfoModal.neverCollect": { + "defaultMessage": "Мы никогда не собираем ваши разговоры, код, аргументы инструментов, сообщения об ошибках или персональные данные. Эту настройку можно изменить в любое время в настройках." + }, + "privacyInfoModal.title": { + "defaultMessage": "Сведения о конфиденциальности" + }, + "privacyInfoModal.whatWeCollect": { + "defaultMessage": "Что мы собираем:" + }, + "progressiveMessageList.loadingMessages": { + "defaultMessage": "Загрузка сообщений... ({renderedCount}/{totalCount})" + }, + "progressiveMessageList.modelChanged": { + "defaultMessage": "Модель изменена: {previousModel} → {currentModel}" + }, + "progressiveMessageList.searchHint": { + "defaultMessage": "Нажмите Cmd/Ctrl+F, чтобы сразу загрузить все сообщения для поиска" + }, + "promptsSettings.allPromptsReset": { + "defaultMessage": "Все промпты сброшены к значениям по умолчанию" + }, + "promptsSettings.backToList": { + "defaultMessage": "Назад к списку" + }, + "promptsSettings.confirmReplaceWithDefault": { + "defaultMessage": "Заменить текущее содержимое значением по умолчанию? Ваши изменения будут потеряны." + }, + "promptsSettings.confirmResetAll": { + "defaultMessage": "Сбросить все промпты к значениям по умолчанию? Это нельзя отменить." + }, + "promptsSettings.confirmResetOne": { + "defaultMessage": "Сбросить этот промпт к значению по умолчанию? Это нельзя отменить." + }, + "promptsSettings.confirmUnsavedBack": { + "defaultMessage": "У вас есть несохраненные изменения. Вернуться назад?" + }, + "promptsSettings.customized": { + "defaultMessage": "Настроено" + }, + "promptsSettings.edit": { + "defaultMessage": "Изменить" + }, + "promptsSettings.editPromptTitle": { + "defaultMessage": "Изменить: {name}" + }, + "promptsSettings.editingLabel": { + "defaultMessage": "Редактирование: {name}" + }, + "promptsSettings.enterPromptContent": { + "defaultMessage": "Введите содержимое промпта..." + }, + "promptsSettings.failedToLoadPrompt": { + "defaultMessage": "Не удалось загрузить промпт" + }, + "promptsSettings.failedToLoadPrompts": { + "defaultMessage": "Не удалось загрузить промпты" + }, + "promptsSettings.failedToResetPrompt": { + "defaultMessage": "Не удалось сбросить промпт" + }, + "promptsSettings.failedToResetPrompts": { + "defaultMessage": "Не удалось сбросить промпты" + }, + "promptsSettings.failedToSavePrompt": { + "defaultMessage": "Не удалось сохранить промпт" + }, + "promptsSettings.promptEditingDescription": { + "defaultMessage": "Настраивайте промпты, определяющие поведение goose в разных контекстах. Эти промпты используют синтаксис шаблонов Jinja2. Будьте осторожны при изменении переменных шаблона: неверные изменения могут нарушить работу. Делитесь улучшениями с сообществом." + }, + "promptsSettings.promptEditingTitle": { + "defaultMessage": "Редактирование промптов" + }, + "promptsSettings.promptResetToDefault": { + "defaultMessage": "Промпт сброшен к значению по умолчанию" + }, + "promptsSettings.promptSaved": { + "defaultMessage": "Промпт сохранен" + }, + "promptsSettings.resetAll": { + "defaultMessage": "Сбросить все" + }, + "promptsSettings.resetToDefault": { + "defaultMessage": "Сбросить к значению по умолчанию" + }, + "promptsSettings.restoreDefault": { + "defaultMessage": "Восстановить по умолчанию" + }, + "promptsSettings.save": { + "defaultMessage": "Сохранить" + }, + "promptsSettings.templateTip": { + "defaultMessage": "Переменные шаблона, такие как {extensionsExample} или {forExample}, заменяются фактическими значениями во время выполнения. Не удаляйте обязательные переменные." + }, + "promptsSettings.unsavedChanges": { + "defaultMessage": "У вас есть несохраненные изменения" + }, + "providerCard.noMetadata": { + "defaultMessage": "Ошибка ProviderCard: метаданные не предоставлены" + }, + "providerCard.unknownProvider": { + "defaultMessage": "Неизвестный провайдер" + }, + "providerCatalogPicker.anthropicCompatible": { + "defaultMessage": "Совместимо с Anthropic" + }, + "providerCatalogPicker.apiFormat": { + "defaultMessage": "Формат API" + }, + "providerCatalogPicker.cancel": { + "defaultMessage": "Отмена" + }, + "providerCatalogPicker.chooseProvider": { + "defaultMessage": "Выберите провайдера" + }, + "providerCatalogPicker.errorPrefix": { + "defaultMessage": "Ошибка: {error}" + }, + "providerCatalogPicker.loadingProviders": { + "defaultMessage": "Загрузка провайдеров..." + }, + "providerCatalogPicker.modelsAvailable": { + "defaultMessage": "Доступно моделей: {count}" + }, + "providerCatalogPicker.noProvidersAvailable": { + "defaultMessage": "Нет доступных провайдеров" + }, + "providerCatalogPicker.noProvidersFound": { + "defaultMessage": "Провайдеры по запросу «{query}» не найдены" + }, + "providerCatalogPicker.openaiCompatible": { + "defaultMessage": "Совместимо с OpenAI" + }, + "providerCatalogPicker.requiresEnvVar": { + "defaultMessage": "• Требуется {envVar}" + }, + "providerCatalogPicker.searchProviders": { + "defaultMessage": "Поиск провайдеров..." + }, + "providerCatalogPicker.selectFormatDescription": { + "defaultMessage": "Выберите формат API и провайдера. Мы автоматически заполним конфигурацию." + }, + "providerConfigForm.browserWindowOpen": { + "defaultMessage": "Откроется окно браузера для завершения входа." + }, + "providerConfigForm.configuring": { + "defaultMessage": "Настройка..." + }, + "providerConfigForm.continue": { + "defaultMessage": "Продолжить" + }, + "providerConfigForm.deviceCodeFlowHint": { + "defaultMessage": "Откроется окно браузера, а код подтверждения будет скопирован в буфер обмена. Вставьте его в браузере, чтобы завершить вход." + }, + "providerConfigForm.noApiKey": { + "defaultMessage": "Нет API-ключа?" + }, + "providerConfigForm.signInWith": { + "defaultMessage": "Войти через {providerName}" + }, + "providerConfigForm.signingIn": { + "defaultMessage": "Вход..." + }, + "providerConfigurationModal.addApiKeyDescription": { + "defaultMessage": "Добавьте API-ключи для этого провайдера, чтобы интегрировать его в goose" + }, + "providerConfigurationModal.browserWindowHint": { + "defaultMessage": "Откроется окно браузера для завершения входа." + }, + "providerConfigurationModal.cancel": { + "defaultMessage": "Отмена" + }, + "providerConfigurationModal.cannotDeleteActive": { + "defaultMessage": "Нельзя удалить этого провайдера, пока он используется. Сначала переключитесь на другую модель." + }, + "providerConfigurationModal.checkConfigAgain": { + "defaultMessage": "Проверьте конфигурацию еще раз, чтобы использовать этого провайдера." + }, + "providerConfigurationModal.close": { + "defaultMessage": "Закрыть" + }, + "providerConfigurationModal.configureHeader": { + "defaultMessage": "Настроить {providerName}" + }, + "providerConfigurationModal.deleteConfigHeader": { + "defaultMessage": "Удалить конфигурацию для {providerName}" + }, + "providerConfigurationModal.deleteConfirmation": { + "defaultMessage": "Это навсегда удалит текущую конфигурацию провайдера." + }, + "providerConfigurationModal.deviceCodeFlowHint": { + "defaultMessage": "Откроется окно браузера, а код подтверждения будет скопирован в буфер обмена. Вставьте его в браузере, чтобы завершить вход." + }, + "providerConfigurationModal.errorCheckingConfig": { + "defaultMessage": "При проверке конфигурации этого провайдера произошла ошибка." + }, + "providerConfigurationModal.errorTitle": { + "defaultMessage": "Ошибка" + }, + "providerConfigurationModal.externalSetupIntro": { + "defaultMessage": "Этот провайдер настраивается вне goose. Выполните следующие шаги:" + }, + "providerConfigurationModal.goBack": { + "defaultMessage": "Назад" + }, + "providerConfigurationModal.oauthLoginFailed": { + "defaultMessage": "Вход OAuth не удался: {error}" + }, + "providerConfigurationModal.oauthSignInDescription": { + "defaultMessage": "Войдите в аккаунт {providerName}, чтобы использовать этого провайдера" + }, + "providerConfigurationModal.parameterRequired": { + "defaultMessage": "Требуется {paramName}" + }, + "providerConfigurationModal.removeConfiguration": { + "defaultMessage": "Удалить конфигурацию" + }, + "providerConfigurationModal.seeDocumentation": { + "defaultMessage": "Подробнее см. в документации." + }, + "providerConfigurationModal.signInWith": { + "defaultMessage": "Войти через {providerName}" + }, + "providerConfigurationModal.signingIn": { + "defaultMessage": "Вход..." + }, + "providerGrid.addProvider": { + "defaultMessage": "Добавить провайдера" + }, + "providerGrid.addProviderTitle": { + "defaultMessage": "Добавить провайдера" + }, + "providerGrid.chooseModel": { + "defaultMessage": "Выбрать модель" + }, + "providerGrid.configureProvider": { + "defaultMessage": "Настроить провайдера" + }, + "providerGrid.editProvider": { + "defaultMessage": "Изменить провайдера" + }, + "providerGrid.fromTemplateOrManual": { + "defaultMessage": "Из шаблона или вручную" + }, + "providerLogo.alt": { + "defaultMessage": "Логотип {providerName}" + }, + "providerSelector.addCustomProvider": { + "defaultMessage": "Добавить пользовательского провайдера" + }, + "providerSelector.addCustomProviderTitle": { + "defaultMessage": "Добавить пользовательского провайдера" + }, + "providerSelector.connectProvider": { + "defaultMessage": "Подключиться к провайдеру" + }, + "providerSelector.connectProviderDescription": { + "defaultMessage": "Подключите OpenAI, Anthropic, Google и т. д." + }, + "providerSelector.freeLocalDescription": { + "defaultMessage": "Используйте локальную модель или провайдера с бесплатными кредитами" + }, + "providerSelector.selectProvider": { + "defaultMessage": "Выберите провайдера" + }, + "providerSelector.useFreeLocal": { + "defaultMessage": "Использовать бесплатных/локальных провайдеров" + }, + "providerSettings.configurationSettings": { + "defaultMessage": "Настройки конфигурации провайдера" + }, + "providerSettings.loadingProviders": { + "defaultMessage": "Загрузка провайдеров..." + }, + "providerSettings.onboardingDescription": { + "defaultMessage": "Выберите провайдера ИИ-модели, чтобы начать работу с goose. Понадобятся API-ключи, созданные каждым провайдером; они будут зашифрованы и сохранены локально. Провайдера можно изменить в настройках в любое время." + }, + "providerSettings.otherProviders": { + "defaultMessage": "Другие провайдеры" + }, + "providerSetupActions.cancel": { + "defaultMessage": "Отмена" + }, + "providerSetupActions.cannotDeleteActive": { + "defaultMessage": "Нельзя удалить {providerName}, пока он используется. Перед удалением провайдера переключитесь на другую модель." + }, + "providerSetupActions.confirmDelete": { + "defaultMessage": "Подтвердить удаление" + }, + "providerSetupActions.confirmDeleteMessage": { + "defaultMessage": "Удалить параметры конфигурации для {providerName}? Это действие нельзя отменить." + }, + "providerSetupActions.deleteProvider": { + "defaultMessage": "Удалить провайдера" + }, + "providerSetupActions.enableProvider": { + "defaultMessage": "Включить провайдера" + }, + "providerSetupActions.ok": { + "defaultMessage": "ОК" + }, + "providerSetupActions.submit": { + "defaultMessage": "Отправить" + }, + "recipeActivityEditor.activitiesDescription": { + "defaultMessage": "Верхнеуровневые промпты и кнопки активностей, которые будут отображаться в окне чата рецепта." + }, + "recipeActivityEditor.activitiesLabel": { + "defaultMessage": "Действия" + }, + "recipeActivityEditor.activityButtonsDescription": { + "defaultMessage": "Кликабельные кнопки, которые появятся под сообщением, чтобы помочь пользователям взаимодействовать с рецептом." + }, + "recipeActivityEditor.activityButtonsLabel": { + "defaultMessage": "Кнопки действий" + }, + "recipeActivityEditor.addActivity": { + "defaultMessage": "Добавить действие" + }, + "recipeActivityEditor.addNewActivityPlaceholder": { + "defaultMessage": "Добавить новое действие..." + }, + "recipeActivityEditor.messageDescription": { + "defaultMessage": "Форматированное сообщение, которое появится вверху рецепта. Поддерживает форматирование Markdown." + }, + "recipeActivityEditor.messageLabel": { + "defaultMessage": "Сообщение" + }, + "recipeActivityEditor.messagePlaceholder": { + "defaultMessage": "Введите вступительное сообщение для пользователя вашего рецепта (поддерживает **жирный**, *курсив*, `код` и т. д.)" + }, + "recipeExtensionSelector.description": { + "defaultMessage": "Выберите расширения, доступные при запуске этого рецепта. Оставьте пустым, чтобы использовать расширения по умолчанию." + }, + "recipeExtensionSelector.extensionsSelected": { + "defaultMessage": "{count, plural, one {Выбрано # расширение} few {Выбрано # расширения} many {Выбрано # расширений} other {Выбрано # расширения}}" + }, + "recipeExtensionSelector.label": { + "defaultMessage": "Расширения (необязательно)" + }, + "recipeExtensionSelector.noExtensionsAvailable": { + "defaultMessage": "Нет доступных расширений" + }, + "recipeExtensionSelector.noExtensionsFound": { + "defaultMessage": "Расширения не найдены" + }, + "recipeExtensionSelector.searchPlaceholder": { + "defaultMessage": "Поиск расширений..." + }, + "recipeFormFields.addParameter": { + "defaultMessage": "Добавить параметр" + }, + "recipeFormFields.advancedOptions": { + "defaultMessage": "Дополнительные параметры" + }, + "recipeFormFields.advancedOptionsHint": { + "defaultMessage": "Действия, параметры, модель, расширения, схема ответа, подрецепты" + }, + "recipeFormFields.descriptionLabel": { + "defaultMessage": "Описание" + }, + "recipeFormFields.descriptionPlaceholder": { + "defaultMessage": "Краткое описание того, что делает этот рецепт" + }, + "recipeFormFields.enterValueFor": { + "defaultMessage": "Введите значение для {key}" + }, + "recipeFormFields.initialPrompt": { + "defaultMessage": "Начальный промпт" + }, + "recipeFormFields.instructionsLabel": { + "defaultMessage": "Инструкции" + }, + "recipeFormFields.instructionsPlaceholder": { + "defaultMessage": "Подробные инструкции для ИИ, скрытые от пользователя" + }, + "recipeFormFields.openEditor": { + "defaultMessage": "Открыть редактор" + }, + "recipeFormFields.parameterNamePlaceholder": { + "defaultMessage": "Введите имя параметра..." + }, + "recipeFormFields.parametersDescription": { + "defaultMessage": "Параметры будут автоматически обнаружены по синтаксису '{{parameter_name}}' в инструкциях/промпте/действиях, либо их можно добавить вручную ниже." + }, + "recipeFormFields.parametersLabel": { + "defaultMessage": "Параметры" + }, + "recipeFormFields.promptOptionalHint": { + "defaultMessage": "(Необязательно - требуются инструкции или промпт)" + }, + "recipeFormFields.promptPlaceholder": { + "defaultMessage": "Предзаполненный промпт при запуске рецепта" + }, + "recipeFormFields.responseJsonSchema": { + "defaultMessage": "JSON-схема ответа" + }, + "recipeFormFields.responseJsonSchemaDescription": { + "defaultMessage": "Определите ожидаемую структуру ответа ИИ в формате JSON Schema" + }, + "recipeFormFields.templateVarHint": { + "defaultMessage": "Используйте '{{parameter_name}}', чтобы определить параметры, которые можно заполнить при запуске рецепта." + }, + "recipeFormFields.titleLabel": { + "defaultMessage": "Название" + }, + "recipeFormFields.titlePlaceholder": { + "defaultMessage": "Название рецепта" + }, + "recipeHeader.recipeLabel": { + "defaultMessage": "Рецепт" + }, + "recipeModelSelector.backToModelList": { + "defaultMessage": "Назад к списку моделей" + }, + "recipeModelSelector.enterCustomModel": { + "defaultMessage": "Введите имя пользовательской модели" + }, + "recipeModelSelector.enterModelNotListed": { + "defaultMessage": "Введите модель, которой нет в списке..." + }, + "recipeModelSelector.fetchError": { + "defaultMessage": "Не удалось получить модели. Повторите попытку позже." + }, + "recipeModelSelector.loadingModels": { + "defaultMessage": "Загрузка моделей…" + }, + "recipeModelSelector.modelHint": { + "defaultMessage": "Оставьте пустым, чтобы использовать модель по умолчанию для выбранного провайдера" + }, + "recipeModelSelector.modelLabel": { + "defaultMessage": "Модель (необязательно)" + }, + "recipeModelSelector.providerHint": { + "defaultMessage": "Оставьте пустым, чтобы использовать провайдера по умолчанию из настроек" + }, + "recipeModelSelector.providerLabel": { + "defaultMessage": "Провайдер (необязательно)" + }, + "recipeModelSelector.selectModel": { + "defaultMessage": "Выберите модель" + }, + "recipeModelSelector.selectProvider": { + "defaultMessage": "Выберите провайдера" + }, + "recipeModelSelector.useDefaultProvider": { + "defaultMessage": "Использовать провайдера по умолчанию" + }, + "recipeNameField.defaultLabel": { + "defaultMessage": "Имя рецепта" + }, + "recipeNameField.formatHint": { + "defaultMessage": "Будет автоматически отформатировано (нижний регистр, дефисы вместо пробелов)" + }, + "recipeWarningModal.cancel": { + "defaultMessage": "Отмена" + }, + "recipeWarningModal.descriptionLabel": { + "defaultMessage": "Описание:" + }, + "recipeWarningModal.firstTimeDescription": { + "defaultMessage": "Вы собираетесь выполнить рецепт, который еще не запускали." + }, + "recipeWarningModal.hiddenCharsWarning": { + "defaultMessage": "Этот рецепт содержит скрытые символы, которые будут проигнорированы для вашей безопасности, так как их могут использовать во вредоносных целях." + }, + "recipeWarningModal.instructionsLabel": { + "defaultMessage": "Инструкции:" + }, + "recipeWarningModal.newRecipeWarningTitle": { + "defaultMessage": "⚠️ Предупреждение о новом рецепте" + }, + "recipeWarningModal.recipePreview": { + "defaultMessage": "Предпросмотр рецепта:" + }, + "recipeWarningModal.securityWarningTitle": { + "defaultMessage": "⚠️ Предупреждение безопасности" + }, + "recipeWarningModal.titleLabel": { + "defaultMessage": "Название:" + }, + "recipeWarningModal.trustAndExecute": { + "defaultMessage": "Доверять и выполнить" + }, + "recipeWarningModal.trustSource": { + "defaultMessage": "Продолжайте только если доверяете источнику этого рецепта." + }, + "recipesView.addSchedule": { + "defaultMessage": "Добавить расписание" + }, + "recipesView.addSlashCommand": { + "defaultMessage": "Добавить slash-команду" + }, + "recipesView.adjustSearchTerms": { + "defaultMessage": "Попробуйте изменить поисковый запрос" + }, + "recipesView.allFiles": { + "defaultMessage": "Все файлы" + }, + "recipesView.cancel": { + "defaultMessage": "Отмена" + }, + "recipesView.copyDeeplink": { + "defaultMessage": "Копировать диплинк" + }, + "recipesView.copyDeeplinkFailedMsg": { + "defaultMessage": "Не удалось скопировать диплинк в буфер обмена" + }, + "recipesView.copyFailedTitle": { + "defaultMessage": "Копирование не удалось" + }, + "recipesView.copyYaml": { + "defaultMessage": "Копировать YAML" + }, + "recipesView.copyYamlFailedMsg": { + "defaultMessage": "Не удалось скопировать YAML рецепта в буфер обмена" + }, + "recipesView.createRecipe": { + "defaultMessage": "Создать рецепт" + }, + "recipesView.deeplinkCopiedMsg": { + "defaultMessage": "Диплинк рецепта скопирован в буфер обмена" + }, + "recipesView.deeplinkCopiedTitle": { + "defaultMessage": "Диплинк скопирован" + }, + "recipesView.deleteRecipe": { + "defaultMessage": "Удалить рецепт" + }, + "recipesView.deleteRecipeConfirm": { + "defaultMessage": "Удалить «{title}»?" + }, + "recipesView.deleteRecipeDetail": { + "defaultMessage": "Файл рецепта будет удален." + }, + "recipesView.deleteRecipeTitle": { + "defaultMessage": "Удалить рецепт" + }, + "recipesView.editRecipe": { + "defaultMessage": "Изменить рецепт" + }, + "recipesView.editSchedule": { + "defaultMessage": "Изменить расписание" + }, + "recipesView.editSlashCommand": { + "defaultMessage": "Изменить slash-команду" + }, + "recipesView.errorLoadingRecipes": { + "defaultMessage": "Ошибка загрузки рецептов" + }, + "recipesView.exportFailedMsg": { + "defaultMessage": "Не удалось экспортировать рецепт в файл" + }, + "recipesView.exportFailedTitle": { + "defaultMessage": "Экспорт не удался" + }, + "recipesView.exportRecipeDialogTitle": { + "defaultMessage": "Экспортировать рецепт" + }, + "recipesView.exportToFile": { + "defaultMessage": "Экспортировать в файл" + }, + "recipesView.noMatchingRecipes": { + "defaultMessage": "Подходящие рецепты не найдены" + }, + "recipesView.noSavedRecipes": { + "defaultMessage": "Нет сохраненных рецептов" + }, + "recipesView.noSavedRecipesDescription": { + "defaultMessage": "Рецепты, сохраненные из чатов, появятся здесь." + }, + "recipesView.openInNewWindow": { + "defaultMessage": "Открыть в новом окне" + }, + "recipesView.recipeDeletedSuccess": { + "defaultMessage": "Рецепт успешно удален" + }, + "recipesView.recipeExportedMsg": { + "defaultMessage": "Рецепт сохранен в {filePath}" + }, + "recipesView.recipeExportedTitle": { + "defaultMessage": "Рецепт экспортирован" + }, + "recipesView.recipesDescription": { + "defaultMessage": "Просматривайте и управляйте сохраненными рецептами, чтобы быстро запускать новые сеансы с предустановленными конфигурациями. {shortcut} для поиска." + }, + "recipesView.recipesTitle": { + "defaultMessage": "Рецепты" + }, + "recipesView.remove": { + "defaultMessage": "Удалить" + }, + "recipesView.removeSchedule": { + "defaultMessage": "Удалить расписание" + }, + "recipesView.runs": { + "defaultMessage": "Запускается {schedule}" + }, + "recipesView.save": { + "defaultMessage": "Сохранить" + }, + "recipesView.scheduleDialogTitle": { + "defaultMessage": "{action} расписание" + }, + "recipesView.scheduleRemovedMsg": { + "defaultMessage": "Рецепт больше не будет запускаться автоматически" + }, + "recipesView.scheduleRemovedTitle": { + "defaultMessage": "Расписание удалено" + }, + "recipesView.scheduleSavedMsg": { + "defaultMessage": "Рецепт будет запускаться {schedule}" + }, + "recipesView.scheduleSavedTitle": { + "defaultMessage": "Расписание сохранено" + }, + "recipesView.searchRecipesPlaceholder": { + "defaultMessage": "Поиск рецептов..." + }, + "recipesView.shareRecipe": { + "defaultMessage": "Поделиться рецептом" + }, + "recipesView.slashCommandDescription": { + "defaultMessage": "Задайте slash-команду, чтобы быстро запускать этот рецепт из любого чата" + }, + "recipesView.slashCommandPlaceholder": { + "defaultMessage": "command-name" + }, + "recipesView.slashCommandRemovedMsg": { + "defaultMessage": "Slash-команда рецепта удалена" + }, + "recipesView.slashCommandRemovedTitle": { + "defaultMessage": "Slash-команда удалена" + }, + "recipesView.slashCommandSavedMsg": { + "defaultMessage": "Используйте /{command}, чтобы запустить этот рецепт" + }, + "recipesView.slashCommandSavedTitle": { + "defaultMessage": "Slash-команда сохранена" + }, + "recipesView.slashCommandTitle": { + "defaultMessage": "Slash-команда" + }, + "recipesView.slashCommandUsageHint": { + "defaultMessage": "Используйте /{command} в любом чате, чтобы запустить этот рецепт" + }, + "recipesView.tryAgain": { + "defaultMessage": "Повторить попытку" + }, + "recipesView.useRecipe": { + "defaultMessage": "Использовать рецепт" + }, + "recipesView.yamlCopiedMsg": { + "defaultMessage": "YAML рецепта скопирован в буфер обмена" + }, + "recipesView.yamlCopiedTitle": { + "defaultMessage": "YAML скопирован" + }, + "recipesView.yamlFiles": { + "defaultMessage": "Файлы YAML" + }, + "resetProviderSection.resetButton": { + "defaultMessage": "Сбросить провайдера и модель" + }, + "resetProviderSection.resetDescription": { + "defaultMessage": "Это очистит выбранную модель и настройки провайдера. Если значения по умолчанию недоступны, вы перейдете на экран приветствия для повторной настройки." + }, + "responseStyle.conciseDescription": { + "defaultMessage": "Вызовы инструментов по умолчанию закрыты и показывают только использованный инструмент" + }, + "responseStyle.conciseLabel": { + "defaultMessage": "Краткий" + }, + "responseStyle.detailedDescription": { + "defaultMessage": "Вызовы инструментов по умолчанию раскрыты, чтобы показать детали" + }, + "responseStyle.detailedLabel": { + "defaultMessage": "Подробный" + }, + "scheduleDetailView.actions": { + "defaultMessage": "Действия" + }, + "scheduleDetailView.cannotModifyRunning": { + "defaultMessage": "Нельзя запускать или изменять расписание, пока оно выполняется." + }, + "scheduleDetailView.created": { + "defaultMessage": "Создано: {date}" + }, + "scheduleDetailView.cronExpression": { + "defaultMessage": "Cron-выражение:" + }, + "scheduleDetailView.currentSession": { + "defaultMessage": "Текущий сеанс:" + }, + "scheduleDetailView.currentlyRunning": { + "defaultMessage": "Сейчас выполняется" + }, + "scheduleDetailView.dir": { + "defaultMessage": "Каталог: {path}" + }, + "scheduleDetailView.editSchedule": { + "defaultMessage": "Изменить расписание" + }, + "scheduleDetailView.errorPrefix": { + "defaultMessage": "Ошибка: {error}" + }, + "scheduleDetailView.failedToLoadSession": { + "defaultMessage": "Не удалось загрузить сеанс" + }, + "scheduleDetailView.idLabel": { + "defaultMessage": "ID:" + }, + "scheduleDetailView.inspectJobError": { + "defaultMessage": "Ошибка проверки задания" + }, + "scheduleDetailView.inspectNoInfo": { + "defaultMessage": "Подробная информация недоступна" + }, + "scheduleDetailView.inspectRunningJob": { + "defaultMessage": "Проверить выполняющуюся задачу" + }, + "scheduleDetailView.jobCancelled": { + "defaultMessage": "Задача отменена" + }, + "scheduleDetailView.jobCancelledMsg": { + "defaultMessage": "Задача была отменена во время запуска." + }, + "scheduleDetailView.jobInspection": { + "defaultMessage": "Проверка задачи" + }, + "scheduleDetailView.jobKilled": { + "defaultMessage": "Задача принудительно остановлена" + }, + "scheduleDetailView.killJobError": { + "defaultMessage": "Ошибка остановки задачи" + }, + "scheduleDetailView.killRunningJob": { + "defaultMessage": "Остановить выполняющуюся задачу" + }, + "scheduleDetailView.lastRun": { + "defaultMessage": "Последний запуск:" + }, + "scheduleDetailView.loadingSchedule": { + "defaultMessage": "Загрузка расписания..." + }, + "scheduleDetailView.loadingSessions": { + "defaultMessage": "Загрузка сеансов..." + }, + "scheduleDetailView.messages": { + "defaultMessage": "Сообщений: {count}" + }, + "scheduleDetailView.newSession": { + "defaultMessage": "Новый сеанс: {sessionId}" + }, + "scheduleDetailView.noScheduleId": { + "defaultMessage": "ID расписания не указан. Вернитесь к списку расписаний." + }, + "scheduleDetailView.noSessions": { + "defaultMessage": "Сеансы для этого расписания не найдены." + }, + "scheduleDetailView.pauseSchedule": { + "defaultMessage": "Приостановить расписание" + }, + "scheduleDetailView.pauseUnpauseError": { + "defaultMessage": "Ошибка паузы/возобновления" + }, + "scheduleDetailView.paused": { + "defaultMessage": "На паузе" + }, + "scheduleDetailView.pausedMsg": { + "defaultMessage": "«{id}» приостановлено" + }, + "scheduleDetailView.pausedWarning": { + "defaultMessage": "Это расписание приостановлено и не будет запускаться автоматически. Используйте «Запустить расписание сейчас», чтобы запустить вручную, или снимите паузу для возобновления автоматического выполнения." + }, + "scheduleDetailView.processStarted": { + "defaultMessage": "Процесс запущен:" + }, + "scheduleDetailView.recentSessions": { + "defaultMessage": "Недавние сеансы" + }, + "scheduleDetailView.recipeSource": { + "defaultMessage": "Источник рецепта:" + }, + "scheduleDetailView.runScheduleError": { + "defaultMessage": "Ошибка запуска расписания" + }, + "scheduleDetailView.runScheduleNow": { + "defaultMessage": "Запустить расписание сейчас" + }, + "scheduleDetailView.scheduleDetails": { + "defaultMessage": "Детали расписания" + }, + "scheduleDetailView.scheduleInformation": { + "defaultMessage": "Информация о расписании" + }, + "scheduleDetailView.scheduleLabel": { + "defaultMessage": "Расписание:" + }, + "scheduleDetailView.scheduleNotFound": { + "defaultMessage": "Расписание не найдено" + }, + "scheduleDetailView.scheduleNotFoundError": { + "defaultMessage": "Расписание не найдено" + }, + "scheduleDetailView.schedulePaused": { + "defaultMessage": "Расписание приостановлено" + }, + "scheduleDetailView.scheduleTriggered": { + "defaultMessage": "Расписание запущено" + }, + "scheduleDetailView.scheduleUnpaused": { + "defaultMessage": "Расписание возобновлено" + }, + "scheduleDetailView.scheduleUpdated": { + "defaultMessage": "Расписание обновлено" + }, + "scheduleDetailView.sessionId": { + "defaultMessage": "ID сеанса: {id}" + }, + "scheduleDetailView.tokens": { + "defaultMessage": "Токенов: {count}" + }, + "scheduleDetailView.unpauseSchedule": { + "defaultMessage": "Возобновить расписание" + }, + "scheduleDetailView.unpausedMsg": { + "defaultMessage": "«{id}» возобновлено" + }, + "scheduleDetailView.updateScheduleError": { + "defaultMessage": "Ошибка обновления расписания" + }, + "scheduleDetailView.updatedMsg": { + "defaultMessage": "«{id}» обновлено" + }, + "scheduleDetailView.viewingScheduleId": { + "defaultMessage": "Просмотр ID расписания: {id}" + }, + "scheduleModal.browseYaml": { + "defaultMessage": "Выбрать YAML-файл..." + }, + "scheduleModal.cancel": { + "defaultMessage": "Отмена" + }, + "scheduleModal.createNewSchedule": { + "defaultMessage": "Создать новое расписание" + }, + "scheduleModal.createSchedule": { + "defaultMessage": "Создать расписание" + }, + "scheduleModal.creating": { + "defaultMessage": "Создание..." + }, + "scheduleModal.deepLink": { + "defaultMessage": "Диплинк" + }, + "scheduleModal.deepLinkPlaceholder": { + "defaultMessage": "Вставьте ссылку goose://recipe сюда..." + }, + "scheduleModal.editSchedule": { + "defaultMessage": "Изменить расписание" + }, + "scheduleModal.failedParseRecipe": { + "defaultMessage": "Не удалось разобрать рецепт из файла." + }, + "scheduleModal.failedReadFile": { + "defaultMessage": "Не удалось прочитать выбранный файл." + }, + "scheduleModal.invalidDeepLink": { + "defaultMessage": "Неверный диплинк. Используйте ссылку goose://recipe." + }, + "scheduleModal.invalidFileType": { + "defaultMessage": "Неверный тип файла: выберите YAML-файл (.yaml или .yml)" + }, + "scheduleModal.nameLabel": { + "defaultMessage": "Имя:" + }, + "scheduleModal.namePlaceholder": { + "defaultMessage": "например, daily-summary-job" + }, + "scheduleModal.provideValidRecipe": { + "defaultMessage": "Укажите корректный источник рецепта." + }, + "scheduleModal.recipeDescription": { + "defaultMessage": "Описание: {description}" + }, + "scheduleModal.recipeParsed": { + "defaultMessage": "Рецепт успешно разобран" + }, + "scheduleModal.recipeTitle": { + "defaultMessage": "Название: {title}" + }, + "scheduleModal.scheduleIdRequired": { + "defaultMessage": "Требуется ID расписания." + }, + "scheduleModal.scheduleLabel": { + "defaultMessage": "Расписание:" + }, + "scheduleModal.selected": { + "defaultMessage": "Выбрано: {path}" + }, + "scheduleModal.sourceLabel": { + "defaultMessage": "Источник:" + }, + "scheduleModal.updateSchedule": { + "defaultMessage": "Обновить расписание" + }, + "scheduleModal.updating": { + "defaultMessage": "Обновление..." + }, + "scheduleModal.yaml": { + "defaultMessage": "YAML" + }, + "schedulesView.confirmDelete": { + "defaultMessage": "Удалить расписание «{id}»? Рецепт будет сохранен." + }, + "schedulesView.createSchedule": { + "defaultMessage": "Создать расписание" + }, + "schedulesView.description": { + "defaultMessage": "Создавайте и управляйте запланированными задачами для автоматического запуска рецептов в заданное время." + }, + "schedulesView.edit": { + "defaultMessage": "Изменить" + }, + "schedulesView.errorPrefix": { + "defaultMessage": "Ошибка: {error}" + }, + "schedulesView.inspect": { + "defaultMessage": "Проверить" + }, + "schedulesView.inspectError": { + "defaultMessage": "Ошибка проверки задания" + }, + "schedulesView.inspectNoInfo": { + "defaultMessage": "Подробная информация для этого задания недоступна" + }, + "schedulesView.jobInspection": { + "defaultMessage": "Проверка задания" + }, + "schedulesView.jobKilled": { + "defaultMessage": "Задание принудительно остановлено" + }, + "schedulesView.kill": { + "defaultMessage": "Остановить" + }, + "schedulesView.killError": { + "defaultMessage": "Ошибка остановки задания" + }, + "schedulesView.lastRun": { + "defaultMessage": "Последний запуск: {date}" + }, + "schedulesView.noSchedules": { + "defaultMessage": "Расписаний пока нет" + }, + "schedulesView.pause": { + "defaultMessage": "Пауза" + }, + "schedulesView.pauseError": { + "defaultMessage": "Ошибка приостановки расписания" + }, + "schedulesView.paused": { + "defaultMessage": "На паузе" + }, + "schedulesView.refresh": { + "defaultMessage": "Обновить" + }, + "schedulesView.refreshing": { + "defaultMessage": "Обновление..." + }, + "schedulesView.resume": { + "defaultMessage": "Продолжить" + }, + "schedulesView.running": { + "defaultMessage": "Выполняется" + }, + "schedulesView.schedulePaused": { + "defaultMessage": "Расписание приостановлено" + }, + "schedulesView.schedulePausedMsg": { + "defaultMessage": "Расписание «{id}» успешно приостановлено" + }, + "schedulesView.scheduleUnpaused": { + "defaultMessage": "Расписание возобновлено" + }, + "schedulesView.scheduleUnpausedMsg": { + "defaultMessage": "Расписание «{id}» успешно возобновлено" + }, + "schedulesView.scheduleUpdated": { + "defaultMessage": "Расписание обновлено" + }, + "schedulesView.scheduleUpdatedMsg": { + "defaultMessage": "Расписание «{id}» успешно обновлено" + }, + "schedulesView.scheduler": { + "defaultMessage": "Планировщик" + }, + "schedulesView.unpauseError": { + "defaultMessage": "Ошибка возобновления расписания" + }, + "searchBar.caseSensitive": { + "defaultMessage": "С учетом регистра" + }, + "searchBar.close": { + "defaultMessage": "Закрыть ({shortcut})" + }, + "searchBar.next": { + "defaultMessage": "Следующее ({shortcut})" + }, + "searchBar.placeholder": { + "defaultMessage": "Поиск в разговоре..." + }, + "searchBar.previous": { + "defaultMessage": "Предыдущее ({shortcut})" + }, + "secureStorageNotice.defaultMessage": { + "defaultMessage": "Ключи надежно хранятся в связке ключей" + }, + "securityToggle.apiTokenDescription": { + "defaultMessage": "Токен аутентификации для сервиса классификации" + }, + "securityToggle.apiTokenOptional": { + "defaultMessage": "API-токен (необязательно)" + }, + "securityToggle.classificationEndpoint": { + "defaultMessage": "Эндпоинт классификации" + }, + "securityToggle.classificationEndpointDescription": { + "defaultMessage": "Введите полный URL сервиса классификации" + }, + "securityToggle.commandClassifierActive": { + "defaultMessage": "Классификатор команд активен (автоматически настроен из окружения)" + }, + "securityToggle.commandEndpointDescription": { + "defaultMessage": "Введите полный URL сервиса классификации command injection" + }, + "securityToggle.commandInjectionDescription": { + "defaultMessage": "Использовать ML-модели для обнаружения вредоносных shell-команд" + }, + "securityToggle.detectionModel": { + "defaultMessage": "Модель обнаружения" + }, + "securityToggle.detectionModelDescription": { + "defaultMessage": "Выберите ML-модель для обнаружения prompt injection" + }, + "securityToggle.detectionThreshold": { + "defaultMessage": "Порог обнаружения" + }, + "securityToggle.enableCommandInjection": { + "defaultMessage": "Включить ML-обнаружение command injection" + }, + "securityToggle.enablePromptInjection": { + "defaultMessage": "Включить обнаружение prompt injection" + }, + "securityToggle.enablePromptInjectionMl": { + "defaultMessage": "Включить ML-обнаружение prompt injection" + }, + "securityToggle.mlEndpointDescription": { + "defaultMessage": "Введите полный URL ML-сервиса классификации (включая идентификатор модели)" + }, + "securityToggle.mlTokenDescription": { + "defaultMessage": "Токен аутентификации для ML-сервиса (например, токен HuggingFace)" + }, + "securityToggle.promptInjectionDescription": { + "defaultMessage": "Обнаруживать и предотвращать потенциальные атаки prompt injection" + }, + "securityToggle.promptInjectionMlDescription": { + "defaultMessage": "Использовать ML-модели для обнаружения потенциального prompt injection в чате" + }, + "securityToggle.thresholdDescription": { + "defaultMessage": "Чем выше значение, тем строже проверка (0.01 = очень мягко, 1.0 = максимально строго)" + }, + "sessionHistory.cancel": { + "defaultMessage": "Отмена" + }, + "sessionHistory.copy": { + "defaultMessage": "Копировать" + }, + "sessionHistory.empty.description": { + "defaultMessage": "Этот сеанс не содержит сообщений" + }, + "sessionHistory.empty.title": { + "defaultMessage": "Сообщения не найдены" + }, + "sessionHistory.error.loading": { + "defaultMessage": "Ошибка загрузки сведений о сеансе" + }, + "sessionHistory.error.tryAgain": { + "defaultMessage": "Повторить попытку" + }, + "sessionHistory.loading": { + "defaultMessage": "Загрузка сведений о сеансе..." + }, + "sessionHistory.resume": { + "defaultMessage": "Продолжить" + }, + "sessionHistory.searchPlaceholder": { + "defaultMessage": "Поиск по истории..." + }, + "sessionHistory.share": { + "defaultMessage": "Поделиться" + }, + "sessionHistory.shareModal.description": { + "defaultMessage": "Поделитесь ссылкой на этот сеанс, чтобы другие могли просматривать ваш чат goose только для чтения." + }, + "sessionHistory.shareModal.title": { + "defaultMessage": "Поделиться сеансом (бета)" + }, + "sessionHistory.shareTooltip": { + "defaultMessage": "Чтобы включить общий доступ к сеансам, перейдите в Настройки > Сеанс > Общий доступ к сеансам." + }, + "sessionHistory.sharing": { + "defaultMessage": "Публикация..." + }, + "sessionHistory.toast.copyFailed": { + "defaultMessage": "Не удалось скопировать ссылку в буфер обмена" + }, + "sessionHistory.toast.launchFailed": { + "defaultMessage": "Не удалось запустить сеанс: {error}" + }, + "sessionHistory.toast.shareFailed": { + "defaultMessage": "Не удалось поделиться сеансом: {error}" + }, + "sessionIndicators.error": { + "defaultMessage": "В сеансе возникла ошибка" + }, + "sessionIndicators.newActivity": { + "defaultMessage": "Есть новая активность" + }, + "sessionIndicators.streaming": { + "defaultMessage": "Потоковая передача" + }, + "sessionItem.messageCount": { + "defaultMessage": "Сообщений: {count}" + }, + "sessionSharingSection.alreadyConfigured": { + "defaultMessage": "Общий доступ к сеансам уже настроен" + }, + "sessionSharingSection.baseUrl": { + "defaultMessage": "Базовый URL" + }, + "sessionSharingSection.connectionFailed": { + "defaultMessage": "Соединение не удалось." + }, + "sessionSharingSection.connectionSuccess": { + "defaultMessage": "Соединение успешно!" + }, + "sessionSharingSection.connectionTimedOut": { + "defaultMessage": "Время соединения истекло. Сервер может быть медленным или недоступным." + }, + "sessionSharingSection.descriptionConfigured": { + "defaultMessage": "Общий доступ к сеансам настроен, но полностью доброволен — сеансы публикуются только когда вы явно нажимаете кнопку «Поделиться»." + }, + "sessionSharingSection.descriptionDefault": { + "defaultMessage": "Вы можете включить общий доступ к сеансам, чтобы делиться ими с другими." + }, + "sessionSharingSection.enableSharing": { + "defaultMessage": "Включить общий доступ к сеансам" + }, + "sessionSharingSection.invalidUrl": { + "defaultMessage": "Неверный формат URL. Введите корректный URL (например, https://example.com/api)." + }, + "sessionSharingSection.serverError": { + "defaultMessage": "Ошибка сервера: HTTP {status}. Возможно, сервер настроен неверно." + }, + "sessionSharingSection.testConnection": { + "defaultMessage": "Проверить соединение" + }, + "sessionSharingSection.testing": { + "defaultMessage": "Проверка..." + }, + "sessionSharingSection.testingConnection": { + "defaultMessage": "Проверка соединения..." + }, + "sessionSharingSection.title": { + "defaultMessage": "Общий доступ к сеансам" + }, + "sessionSharingSection.unknownError": { + "defaultMessage": "Произошла неизвестная ошибка." + }, + "sessionSharingSection.unreachableServer": { + "defaultMessage": "Не удалось связаться с сервером. Проверьте URL и сетевое подключение." + }, + "sessionSharingSection.urlPlaceholder": { + "defaultMessage": "https://example.com/api" + }, + "sessionViewComponents.empty.description": { + "defaultMessage": "Этот сеанс не содержит сообщений" + }, + "sessionViewComponents.empty.title": { + "defaultMessage": "Сообщения не найдены" + }, + "sessionViewComponents.error.loading": { + "defaultMessage": "Ошибка загрузки сведений о сеансе" + }, + "sessionViewComponents.error.tryAgain": { + "defaultMessage": "Повторить попытку" + }, + "sessionViewComponents.role.assistant": { + "defaultMessage": "Goose" + }, + "sessionViewComponents.role.user": { + "defaultMessage": "Вы" + }, + "sessions.action.delete": { + "defaultMessage": "Удалить сеанс" + }, + "sessions.action.duplicate": { + "defaultMessage": "Дублировать сеанс" + }, + "sessions.action.editName": { + "defaultMessage": "Изменить имя сеанса" + }, + "sessions.action.export": { + "defaultMessage": "Экспортировать сеанс" + }, + "sessions.action.openNewWindow": { + "defaultMessage": "Открыть в новом окне" + }, + "sessions.action.shareNostr": { + "defaultMessage": "Поделиться зашифрованной ссылкой Nostr" + }, + "sessions.cancel": { + "defaultMessage": "Отмена" + }, + "sessions.chatHistory": { + "defaultMessage": "История чатов" + }, + "sessions.chatHistoryDesc": { + "defaultMessage": "Просматривайте и ищите прошлые разговоры с Goose. {shortcut} для поиска." + }, + "sessions.close": { + "defaultMessage": "Закрыть" + }, + "sessions.delete.message": { + "defaultMessage": "Удалить сеанс «{name}»? Это действие нельзя отменить." + }, + "sessions.delete.title": { + "defaultMessage": "Удалить сеанс" + }, + "sessions.edit.placeholder": { + "defaultMessage": "Введите описание сеанса" + }, + "sessions.edit.title": { + "defaultMessage": "Изменить описание сеанса" + }, + "sessions.empty.description": { + "defaultMessage": "История чатов появится здесь" + }, + "sessions.empty.title": { + "defaultMessage": "Сеансы чата не найдены" + }, + "sessions.error.loading": { + "defaultMessage": "Ошибка загрузки сеансов" + }, + "sessions.error.tryAgain": { + "defaultMessage": "Повторить попытку" + }, + "sessions.extensions": { + "defaultMessage": "Расширения:" + }, + "sessions.import": { + "defaultMessage": "Импортировать сеанс" + }, + "sessions.importNostr": { + "defaultMessage": "Импортировать ссылку" + }, + "sessions.importNostr.description": { + "defaultMessage": "Вставьте ссылку общего доступа Goose Nostr, чтобы получить, расшифровать и импортировать сеанс." + }, + "sessions.importNostr.placeholder": { + "defaultMessage": "goose://sessions/nostr?nevent=...&key=..." + }, + "sessions.importNostr.title": { + "defaultMessage": "Импортировать сеанс Nostr" + }, + "sessions.importing": { + "defaultMessage": "Импорт..." + }, + "sessions.loadingMore": { + "defaultMessage": "Загрузка дополнительных сеансов..." + }, + "sessions.save": { + "defaultMessage": "Сохранить" + }, + "sessions.saving": { + "defaultMessage": "Сохранение..." + }, + "sessions.search.noResults": { + "defaultMessage": "Подходящие сеансы не найдены" + }, + "sessions.search.noResultsDesc": { + "defaultMessage": "Попробуйте изменить поисковый запрос" + }, + "sessions.searchPlaceholder": { + "defaultMessage": "Поиск по истории..." + }, + "sessions.shareNostr.description": { + "defaultMessage": "Любой, у кого есть эта ссылка, сможет получить и расшифровать ваш сеанс. Обращайтесь с ней как с секретом." + }, + "sessions.shareNostr.title": { + "defaultMessage": "Зашифрованная ссылка общего доступа Nostr" + }, + "sessions.toast.copied": { + "defaultMessage": "Скопировано в буфер обмена" + }, + "sessions.toast.deleteFailed": { + "defaultMessage": "Не удалось удалить сеанс «{name}»: {error}" + }, + "sessions.toast.deleted": { + "defaultMessage": "Сеанс успешно удален" + }, + "sessions.toast.duplicateFailed": { + "defaultMessage": "Не удалось дублировать сеанс: {error}" + }, + "sessions.toast.duplicated": { + "defaultMessage": "Сеанс «{name}» успешно дублирован" + }, + "sessions.toast.exported": { + "defaultMessage": "Сеанс успешно экспортирован" + }, + "sessions.toast.importFailed": { + "defaultMessage": "Не удалось импортировать сеанс: {error}" + }, + "sessions.toast.imported": { + "defaultMessage": "Сеанс успешно импортирован" + }, + "sessions.toast.shareNostr": { + "defaultMessage": "Зашифрованная ссылка Nostr создана" + }, + "sessions.toast.shareNostrFailed": { + "defaultMessage": "Не удалось создать ссылку Nostr: {error}" + }, + "sessions.toast.updateFailed": { + "defaultMessage": "Не удалось обновить описание сеанса: {error}" + }, + "sessions.toast.updated": { + "defaultMessage": "Описание сеанса успешно обновлено" + }, + "sessionsView.error.failedToLoad": { + "defaultMessage": "Не удалось загрузить сведения о сеансе. Повторите попытку позже." + }, + "sessionsView.loading": { + "defaultMessage": "Загрузка..." + }, + "settings.appearance.description": { + "defaultMessage": "Настройте, как goose отображается в вашей системе" + }, + "settings.appearance.title": { + "defaultMessage": "Внешний вид" + }, + "settings.close": { + "defaultMessage": "Закрыть" + }, + "settings.costTracking.description": { + "defaultMessage": "Показывать цены моделей и стоимость использования" + }, + "settings.costTracking.title": { + "defaultMessage": "Отслеживание стоимости" + }, + "settings.dockIcon.description": { + "defaultMessage": "Показывать goose в Dock" + }, + "settings.dockIcon.title": { + "defaultMessage": "Значок Dock" + }, + "settings.help.description": { + "defaultMessage": "Помогите улучшить goose: сообщайте о проблемах или запрашивайте новые функции" + }, + "settings.help.reportBug": { + "defaultMessage": "Сообщить об ошибке" + }, + "settings.help.requestFeature": { + "defaultMessage": "Запросить функцию" + }, + "settings.help.title": { + "defaultMessage": "Помощь и отзывы" + }, + "settings.menuBarIcon.description": { + "defaultMessage": "Показывать goose в строке меню" + }, + "settings.menuBarIcon.title": { + "defaultMessage": "Значок строки меню" + }, + "settings.notifications.configGuide": { + "defaultMessage": "Руководство по настройке" + }, + "settings.notifications.description": { + "defaultMessage": "Уведомления управляются вашей ОС - {link}" + }, + "settings.notifications.modal.macInstructions": { + "defaultMessage": "Чтобы включить уведомления в macOS:" + }, + "settings.notifications.modal.macStep1": { + "defaultMessage": "Откройте «Системные настройки»" + }, + "settings.notifications.modal.macStep2": { + "defaultMessage": "Нажмите «Уведомления»" + }, + "settings.notifications.modal.macStep3": { + "defaultMessage": "Найдите и выберите goose в списке приложений" + }, + "settings.notifications.modal.macStep4": { + "defaultMessage": "Включите уведомления и настройте параметры по желанию" + }, + "settings.notifications.modal.title": { + "defaultMessage": "Как включить уведомления" + }, + "settings.notifications.modal.winInstructions": { + "defaultMessage": "Чтобы включить уведомления в Windows:" + }, + "settings.notifications.modal.winStep1": { + "defaultMessage": "Откройте «Параметры»" + }, + "settings.notifications.modal.winStep2": { + "defaultMessage": "Перейдите в Система > Уведомления" + }, + "settings.notifications.modal.winStep3": { + "defaultMessage": "Найдите и выберите goose в списке приложений" + }, + "settings.notifications.modal.winStep4": { + "defaultMessage": "Включите уведомления и настройте параметры по желанию" + }, + "settings.notifications.openSettings": { + "defaultMessage": "Откройте «Параметры»" + }, + "settings.notifications.task.description": { + "defaultMessage": "Уведомлять, когда Goose завершает задачу, пока окно в фоне" + }, + "settings.notifications.task.title": { + "defaultMessage": "Уведомления о завершении задач" + }, + "settings.notifications.title": { + "defaultMessage": "Уведомления" + }, + "settings.preventSleep.description": { + "defaultMessage": "Не давать компьютеру засыпать, пока goose выполняет задачу (экран все равно может блокироваться)" + }, + "settings.preventSleep.title": { + "defaultMessage": "Запретить сон" + }, + "settings.theme.description": { + "defaultMessage": "Настройте внешний вид и ощущения goose" + }, + "settings.theme.title": { + "defaultMessage": "Тема" + }, + "settings.updates.description": { + "defaultMessage": "Проверяйте и устанавливайте обновления, чтобы goose работал оптимально" + }, + "settings.updates.title": { + "defaultMessage": "Обновления" + }, + "settings.version.title": { + "defaultMessage": "Версия" + }, + "settingsView.tabApp": { + "defaultMessage": "Приложение" + }, + "settingsView.tabChat": { + "defaultMessage": "Чат" + }, + "settingsView.tabKeyboard": { + "defaultMessage": "Клавиатура" + }, + "settingsView.tabLocalInference": { + "defaultMessage": "Локальный инференс" + }, + "settingsView.tabModels": { + "defaultMessage": "Модели" + }, + "settingsView.tabPrompts": { + "defaultMessage": "Промпты" + }, + "settingsView.tabSession": { + "defaultMessage": "Сеанс" + }, + "settingsView.title": { + "defaultMessage": "Настройки" + }, + "sharedSession.loading": { + "defaultMessage": "Загрузка сведений о сеансе..." + }, + "sharedSession.title": { + "defaultMessage": "Общий сеанс" + }, + "sheet.close": { + "defaultMessage": "Закрыть" + }, + "shortcutRecorder.cancel": { + "defaultMessage": "Отмена" + }, + "shortcutRecorder.clickToRecord": { + "defaultMessage": "Нажмите для записи..." + }, + "shortcutRecorder.conflictWarning": { + "defaultMessage": "Это сочетание уже используется для {label}. При сохранении оно будет переназначено этому действию." + }, + "shortcutRecorder.pressShortcut": { + "defaultMessage": "Нажмите сочетание..." + }, + "shortcutRecorder.save": { + "defaultMessage": "Сохранить" + }, + "skillsView.addSkill": { + "defaultMessage": "Добавить навык" + }, + "skillsView.adjustSearchTerms": { + "defaultMessage": "Попробуйте изменить поисковый запрос" + }, + "skillsView.comingSoon": { + "defaultMessage": "Скоро" + }, + "skillsView.errorLoadingSkills": { + "defaultMessage": "Ошибка загрузки навыков" + }, + "skillsView.noMatchingSkills": { + "defaultMessage": "Подходящие навыки не найдены" + }, + "skillsView.noSkillsDescription": { + "defaultMessage": "Навыки загружаются из файлов SKILL.md в ~/.config/agents/skills/, .goose/skills/ или других поддерживаемых каталогах." + }, + "skillsView.noSkillsInstalled": { + "defaultMessage": "Навыки не установлены" + }, + "skillsView.searchSkillsPlaceholder": { + "defaultMessage": "Поиск навыков..." + }, + "skillsView.skillsDescription": { + "defaultMessage": "Просматривайте установленные навыки, расширяющие возможности Goose. {shortcut} для поиска." + }, + "skillsView.skillsTitle": { + "defaultMessage": "Навыки" + }, + "skillsView.tryAgain": { + "defaultMessage": "Повторить попытку" + }, + "spellcheckToggle.description": { + "defaultMessage": "Проверять орфографию в поле ввода чата. Для применения требуется перезапуск." + }, + "spellcheckToggle.title": { + "defaultMessage": "Включить проверку орфографии" + }, + "standaloneAppView.failedToLoad": { + "defaultMessage": "Не удалось загрузить приложение" + }, + "standaloneAppView.initializing": { + "defaultMessage": "Инициализация приложения..." + }, + "standaloneAppView.missingParams": { + "defaultMessage": "Отсутствуют обязательные параметры" + }, + "stringUtils.configuredProvider": { + "defaultMessage": "Провайдер {name} настроен" + }, + "stringUtils.ollamaApp": { + "defaultMessage": "Приложение Ollama" + }, + "stringUtils.ollamaNotConfiguredPrefix": { + "defaultMessage": "Для использования либо" + }, + "stringUtils.ollamaNotConfiguredSuffix": { + "defaultMessage": "должно быть установлено на вашем компьютере и открыто, либо нужно ввести значение для OLLAMA_HOST." + }, + "subRecipeEditor.addExisting": { + "defaultMessage": "Добавить существующий" + }, + "subRecipeEditor.createNew": { + "defaultMessage": "Создать новый подрецепт" + }, + "subRecipeEditor.deleteSubrecipe": { + "defaultMessage": "Удалить подрецепт {name}" + }, + "subRecipeEditor.description": { + "defaultMessage": "Подрецепты — это рецепты, которые можно вызывать как инструменты во время выполнения. Они позволяют создавать многошаговые процессы и переиспользуемые компоненты." + }, + "subRecipeEditor.duplicateName": { + "defaultMessage": "Повторяющееся имя" + }, + "subRecipeEditor.duplicateNameMsg": { + "defaultMessage": "Подрецепт с именем «{name}» уже существует. Используйте уникальное имя." + }, + "subRecipeEditor.editSubrecipe": { + "defaultMessage": "Изменить подрецепт {name}" + }, + "subRecipeEditor.label": { + "defaultMessage": "Подрецепты" + }, + "subRecipeEditor.preconfiguredValues": { + "defaultMessage": "Предварительно настроенные значения:" + }, + "subRecipeEditor.sequential": { + "defaultMessage": "Последовательно" + }, + "subRecipeModal.addTitle": { + "defaultMessage": "Добавить подрецепт" + }, + "subRecipeModal.apply": { + "defaultMessage": "Применить" + }, + "subRecipeModal.browse": { + "defaultMessage": "Обзор" + }, + "subRecipeModal.cancel": { + "defaultMessage": "Отмена" + }, + "subRecipeModal.closeModal": { + "defaultMessage": "Закрыть окно подрецепта" + }, + "subRecipeModal.configureTitle": { + "defaultMessage": "Настроить подрецепт" + }, + "subRecipeModal.descriptionLabel": { + "defaultMessage": "Описание" + }, + "subRecipeModal.descriptionPlaceholder": { + "defaultMessage": "Необязательное описание того, что делает этот подрецепт..." + }, + "subRecipeModal.invalidFile": { + "defaultMessage": "Неверный файл" + }, + "subRecipeModal.invalidFileMsg": { + "defaultMessage": "Выберите YAML-файл (.yaml или .yml)." + }, + "subRecipeModal.nameHint": { + "defaultMessage": "Уникальный идентификатор, используемый для создания имени инструмента" + }, + "subRecipeModal.nameLabel": { + "defaultMessage": "Имя" + }, + "subRecipeModal.namePlaceholder": { + "defaultMessage": "например, security_scan" + }, + "subRecipeModal.pathHint": { + "defaultMessage": "Выберите существующий файл рецепта или введите путь вручную" + }, + "subRecipeModal.pathLabel": { + "defaultMessage": "Путь" + }, + "subRecipeModal.pathPlaceholder": { + "defaultMessage": "например, ./subrecipes/security-analysis.yaml" + }, + "subRecipeModal.preconfiguredValues": { + "defaultMessage": "Предварительно настроенные значения" + }, + "subRecipeModal.preconfiguredValuesHint": { + "defaultMessage": "Необязательные значения параметров, которые всегда передаются подрецепту" + }, + "subRecipeModal.sequentialHint": { + "defaultMessage": "(Принудительно выполняет несколько экземпляров подрецепта последовательно)" + }, + "subRecipeModal.sequentialLabel": { + "defaultMessage": "Последовательно при повторении" + }, + "subRecipeModal.subtitle": { + "defaultMessage": "Настройте подрецепт, который можно вызывать как инструмент при выполнении рецепта" + }, + "switchModelModal.backToModelList": { + "defaultMessage": "Назад к списку моделей" + }, + "switchModelModal.cancel": { + "defaultMessage": "Отмена" + }, + "switchModelModal.checkProviderConfig": { + "defaultMessage": "Проверьте конфигурацию провайдера в Настройки → Провайдеры" + }, + "switchModelModal.chooseModel": { + "defaultMessage": "Выберите модель:" + }, + "switchModelModal.claudeAdaptive": { + "defaultMessage": "Адаптивно - Claude решает, когда и сколько думать" + }, + "switchModelModal.claudeDisabled": { + "defaultMessage": "Отключено - без расширенного мышления" + }, + "switchModelModal.claudeEffortHigh": { + "defaultMessage": "Высокий - глубокое рассуждение (по умолчанию)" + }, + "switchModelModal.claudeEffortLow": { + "defaultMessage": "Низкий - минимальное размышление, самые быстрые ответы" + }, + "switchModelModal.claudeEffortMax": { + "defaultMessage": "Макс. - без ограничений глубины размышления" + }, + "switchModelModal.claudeEffortMedium": { + "defaultMessage": "Средний - умеренное размышление" + }, + "switchModelModal.claudeEnabled": { + "defaultMessage": "Включено - фиксированный бюджет токенов для размышления" + }, + "switchModelModal.couldNotContactProvider": { + "defaultMessage": "Не удалось связаться с провайдером" + }, + "switchModelModal.customModelName": { + "defaultMessage": "Имя пользовательской модели" + }, + "switchModelModal.description": { + "defaultMessage": "Выберите провайдера и модель для ваших разговоров." + }, + "switchModelModal.enterModelNotListed": { + "defaultMessage": "Введите модель, которой нет в списке..." + }, + "switchModelModal.extendedThinking": { + "defaultMessage": "Расширенное размышление" + }, + "switchModelModal.geminiOnly": { + "defaultMessage": "(только модели Gemini 3)" + }, + "switchModelModal.goToSettings": { + "defaultMessage": "Перейти в настройки" + }, + "switchModelModal.loadingModels": { + "defaultMessage": "Загрузка моделей…" + }, + "switchModelModal.localModelsDescription": { + "defaultMessage": "Чтобы использовать локальный инференс, сначала скачайте модель на компьютер. Перейдите в Настройки → Модели для управления локальными моделями." + }, + "switchModelModal.localModelsTitle": { + "defaultMessage": "Сначала нужно скачать локальные модели" + }, + "switchModelModal.providerPlaceholder": { + "defaultMessage": "Провайдер, введите для поиска" + }, + "switchModelModal.quickStartGuide": { + "defaultMessage": "Краткое руководство" + }, + "switchModelModal.recommended": { + "defaultMessage": "Рекомендуется" + }, + "switchModelModal.selectEffortLevel": { + "defaultMessage": "Выберите уровень усилия" + }, + "switchModelModal.selectModel": { + "defaultMessage": "Выберите модель" + }, + "switchModelModal.selectModelButton": { + "defaultMessage": "Выбрать модель" + }, + "switchModelModal.selectModelPlaceholder": { + "defaultMessage": "Выберите модель, введите для поиска" + }, + "switchModelModal.selectOrEnterModel": { + "defaultMessage": "Выберите или введите модель" + }, + "switchModelModal.selectProvider": { + "defaultMessage": "Выберите провайдера" + }, + "switchModelModal.selectThinkingLevel": { + "defaultMessage": "Выберите уровень размышления" + }, + "switchModelModal.selectThinkingMode": { + "defaultMessage": "Выберите режим размышления" + }, + "switchModelModal.thinkingBudget": { + "defaultMessage": "Бюджет размышления (токены)" + }, + "switchModelModal.thinkingEffort": { + "defaultMessage": "Усилие размышления" + }, + "switchModelModal.thinkingEffortOff": { + "defaultMessage": "Выкл. - без расширенного размышления" + }, + "switchModelModal.thinkingLevel": { + "defaultMessage": "Уровень размышления" + }, + "switchModelModal.thinkingLevelHigh": { + "defaultMessage": "Высокий - более глубокое рассуждение, выше задержка" + }, + "switchModelModal.thinkingLevelLow": { + "defaultMessage": "Низкий - ниже задержка, более легкое рассуждение" + }, + "switchModelModal.title": { + "defaultMessage": "Переключить модели" + }, + "switchModelModal.typeModelName": { + "defaultMessage": "Введите имя модели здесь" + }, + "switchModelModal.useOtherProvider": { + "defaultMessage": "Использовать другого провайдера" + }, + "telemetryConsentPrompt.description": { + "defaultMessage": "Хотите поделиться анонимными данными об использовании, чтобы помочь улучшить goose? Мы никогда не собираем ваши разговоры, код или персональные данные." + }, + "telemetryConsentPrompt.heading": { + "defaultMessage": "Помогите улучшить goose" + }, + "telemetryConsentPrompt.learnMore": { + "defaultMessage": "Подробнее" + }, + "telemetryConsentPrompt.optIn": { + "defaultMessage": "Да, делиться анонимными данными об использовании" + }, + "telemetryConsentPrompt.optOut": { + "defaultMessage": "Нет, спасибо" + }, + "telemetrySettings.configErrorTitle": { + "defaultMessage": "Ошибка конфигурации" + }, + "telemetrySettings.description": { + "defaultMessage": "Управляйте использованием ваших данных" + }, + "telemetrySettings.learnMore": { + "defaultMessage": "Подробнее" + }, + "telemetrySettings.loadError": { + "defaultMessage": "Не удалось загрузить настройки телеметрии." + }, + "telemetrySettings.title": { + "defaultMessage": "Конфиденциальность" + }, + "telemetrySettings.toggleDescription": { + "defaultMessage": "Помогите улучшить goose, делясь анонимной статистикой использования." + }, + "telemetrySettings.toggleLabel": { + "defaultMessage": "Анонимные данные об использовании" + }, + "telemetrySettings.updateError": { + "defaultMessage": "Не удалось обновить настройки телеметрии." + }, + "themeSelector.dark": { + "defaultMessage": "Темная" + }, + "themeSelector.light": { + "defaultMessage": "Светлая" + }, + "themeSelector.system": { + "defaultMessage": "Системная" + }, + "themeSelector.theme": { + "defaultMessage": "Тема" + }, + "toolApprovalButtons.allowOnce": { + "defaultMessage": "Разрешить один раз" + }, + "toolApprovalButtons.allowedOnce": { + "defaultMessage": "Разрешено один раз" + }, + "toolApprovalButtons.alwaysAllow": { + "defaultMessage": "Всегда разрешать" + }, + "toolApprovalButtons.alwaysAllowed": { + "defaultMessage": "Всегда разрешено" + }, + "toolApprovalButtons.cancelled": { + "defaultMessage": "Отменено" + }, + "toolApprovalButtons.denied": { + "defaultMessage": "Отклонено" + }, + "toolApprovalButtons.deniedOnce": { + "defaultMessage": "Отклонено один раз" + }, + "toolApprovalButtons.deny": { + "defaultMessage": "Отклонить" + }, + "toolCallStatusIndicator.toolStatus": { + "defaultMessage": "Статус инструмента: {status}" + }, + "toolCallWithResponse.activityCount": { + "defaultMessage": "Активность ({count})" + }, + "toolCallWithResponse.code": { + "defaultMessage": "Код" + }, + "toolCallWithResponse.loadingSpinner": { + "defaultMessage": "Индикатор загрузки" + }, + "toolCallWithResponse.logs": { + "defaultMessage": "Журналы" + }, + "toolCallWithResponse.mcpUiExperimental": { + "defaultMessage": "MCP UI является экспериментальным и может измениться в любой момент." + }, + "toolCallWithResponse.output": { + "defaultMessage": "Вывод" + }, + "toolCallWithResponse.toolDetails": { + "defaultMessage": "Сведения об инструменте" + }, + "toolCallWithResponse.toolResultAlt": { + "defaultMessage": "Результат инструмента" + }, + "toolCallWithResponse.viewSubagentSession": { + "defaultMessage": "Просмотреть сеанс субагента" + }, + "toolConfirmation.allowToolCallWithName": { + "defaultMessage": "Разрешить {toolName}?" + }, + "toolConfirmation.gooseWouldLikeToCallWithName": { + "defaultMessage": "Goose хочет вызвать {toolName}. Разрешить?" + }, + "tunnelSection.appStoreQrInstructions": { + "defaultMessage": "Отсканируйте этот QR-код камерой iPhone, чтобы установить мобильное приложение goose из App Store" + }, + "tunnelSection.close": { + "defaultMessage": "Закрыть" + }, + "tunnelSection.connectionDetails": { + "defaultMessage": "Сведения о подключении" + }, + "tunnelSection.downloadIosApp": { + "defaultMessage": "Скачать приложение goose для iOS" + }, + "tunnelSection.failedToLoadStatus": { + "defaultMessage": "Не удалось загрузить статус туннеля" + }, + "tunnelSection.failedToStartTunnel": { + "defaultMessage": "Не удалось запустить туннель" + }, + "tunnelSection.failedToStopTunnel": { + "defaultMessage": "Не удалось остановить туннель" + }, + "tunnelSection.getIosApp": { + "defaultMessage": "Получить приложение iOS" + }, + "tunnelSection.mobileApp": { + "defaultMessage": "Мобильное приложение" + }, + "tunnelSection.mobileAppConnection": { + "defaultMessage": "Подключение мобильного приложения" + }, + "tunnelSection.openInAppStore": { + "defaultMessage": "Открыть в App Store" + }, + "tunnelSection.or": { + "defaultMessage": "или" + }, + "tunnelSection.previewDescription": { + "defaultMessage": "Включите удаленный доступ к goose с мобильных устройств через защищенное туннелирование." + }, + "tunnelSection.previewFeature": { + "defaultMessage": "Предварительная функция:" + }, + "tunnelSection.qrCodeInstructions": { + "defaultMessage": "Отсканируйте этот QR-код мобильным приложением goose. Не делитесь этим кодом: он предназначен для вашего личного доступа." + }, + "tunnelSection.retry": { + "defaultMessage": "Повторить" + }, + "tunnelSection.scanQrCode": { + "defaultMessage": "сканировать QR-код" + }, + "tunnelSection.secretKey": { + "defaultMessage": "Секретный ключ" + }, + "tunnelSection.showQrCode": { + "defaultMessage": "Показать QR-код" + }, + "tunnelSection.startTunnel": { + "defaultMessage": "Запустить туннель" + }, + "tunnelSection.starting": { + "defaultMessage": "Запуск..." + }, + "tunnelSection.statusDisabled": { + "defaultMessage": "Туннель отключен" + }, + "tunnelSection.statusError": { + "defaultMessage": "В туннеле возникла ошибка" + }, + "tunnelSection.statusIdle": { + "defaultMessage": "Туннель не запущен" + }, + "tunnelSection.statusRunning": { + "defaultMessage": "Туннель активен" + }, + "tunnelSection.statusStarting": { + "defaultMessage": "Запуск туннеля..." + }, + "tunnelSection.stopTunnel": { + "defaultMessage": "Остановить туннель" + }, + "tunnelSection.tunnelStatus": { + "defaultMessage": "Статус туннеля" + }, + "tunnelSection.tunnelUrl": { + "defaultMessage": "URL туннеля" + }, + "tunnelSection.url": { + "defaultMessage": "URL:" + }, + "updateSection.autoDownload": { + "defaultMessage": "Обновление будет автоматически скачано в фоновом режиме." + }, + "updateSection.autoInstallNote": { + "defaultMessage": "Обновление будет установлено автоматически при выходе из приложения." + }, + "updateSection.checkForUpdates": { + "defaultMessage": "Проверить обновления" + }, + "updateSection.checking": { + "defaultMessage": "Проверка обновлений..." + }, + "updateSection.currentVersion": { + "defaultMessage": "Текущая версия" + }, + "updateSection.downloadReady": { + "defaultMessage": "Обновление скачано и готово к установке!" + }, + "updateSection.downloadingProgress": { + "defaultMessage": "Скачивание обновления... {percent}%" + }, + "updateSection.downloadingUpdate": { + "defaultMessage": "Скачивание обновления..." + }, + "updateSection.installAndRestart": { + "defaultMessage": "Установить и перезапустить" + }, + "updateSection.installNowHint": { + "defaultMessage": "Или нажмите «Установить и перезапустить», чтобы обновиться сейчас." + }, + "updateSection.latestVersion": { + "defaultMessage": "У вас установлена последняя версия!" + }, + "updateSection.loading": { + "defaultMessage": "Загрузка..." + }, + "updateSection.manualInstallNote": { + "defaultMessage": "После скачивания нужно будет установить обновление вручную." + }, + "updateSection.manualInstallRequired": { + "defaultMessage": "Для этого способа обновления требуется ручная установка." + }, + "updateSection.readyInstallAuto": { + "defaultMessage": "✓ Обновление готово! Оно будет установлено при выходе из Goose." + }, + "updateSection.readyInstallManual": { + "defaultMessage": "✓ Обновление готово! Нажмите «Установить и перезапустить», чтобы получить инструкции по установке." + }, + "updateSection.upToDate": { + "defaultMessage": "(актуально)" + }, + "updateSection.updateAvailable": { + "defaultMessage": "Доступно обновление!" + }, + "updateSection.versionAvailable": { + "defaultMessage": "→ доступна {version}" + }, + "updateSection.versionIsAvailable": { + "defaultMessage": "Доступна версия {version}" + }, + "userMessage.cancel": { + "defaultMessage": "Отмена" + }, + "userMessage.cancelAriaLabel": { + "defaultMessage": "Отменить редактирование" + }, + "userMessage.editAriaLabel": { + "defaultMessage": "Изменить содержимое сообщения" + }, + "userMessage.editButton": { + "defaultMessage": "Изменить" + }, + "userMessage.editInPlace": { + "defaultMessage": "Изменить на месте" + }, + "userMessage.editInPlaceAriaLabel": { + "defaultMessage": "Изменить сообщение на месте" + }, + "userMessage.editInPlaceDescription": { + "defaultMessage": "Изменить на месте обновляет этот сеанс • Ответвить сеанс создает новый сеанс" + }, + "userMessage.editInPlaceTitle": { + "defaultMessage": "Обновить сообщение в этом сеансе" + }, + "userMessage.editMessageAriaLabel": { + "defaultMessage": "Изменить сообщение: {preview}" + }, + "userMessage.editMessageTitle": { + "defaultMessage": "Изменить сообщение" + }, + "userMessage.editPlaceholder": { + "defaultMessage": "Измените сообщение..." + }, + "userMessage.emptyError": { + "defaultMessage": "Сообщение не может быть пустым" + }, + "userMessage.forkSession": { + "defaultMessage": "Форкнуть сеанс" + }, + "userMessage.forkSessionAriaLabel": { + "defaultMessage": "Форкнуть сеанс с измененным сообщением" + }, + "userMessage.forkSessionTitle": { + "defaultMessage": "Создать новый сеанс с измененным сообщением" + } +} From b0cd61aa424e9f75a215bd6915d17e5e44024698 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" <41898282+github-actions[bot]@users.noreply.github.com> Date: Tue, 26 May 2026 18:02:17 +0000 Subject: [PATCH 13/66] chore(release): bump version to 1.36.0 (minor) (#9417) Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- Cargo.lock | 16 +- Cargo.toml | 2 +- .../canonical/data/canonical_models.json | 5667 ++++++++++------- .../canonical/data/provider_metadata.json | 72 +- ui/desktop/openapi.json | 2 +- ui/desktop/package.json | 2 +- 6 files changed, 3567 insertions(+), 2194 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index d528758ef4e8..f3e267cfb612 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4445,7 +4445,7 @@ dependencies = [ [[package]] name = "goose" -version = "1.35.0" +version = "1.36.0" dependencies = [ "agent-client-protocol", "agent-client-protocol-schema", @@ -4579,7 +4579,7 @@ dependencies = [ [[package]] name = "goose-acp-macros" -version = "1.35.0" +version = "1.36.0" dependencies = [ "quote", "syn 2.0.117", @@ -4587,7 +4587,7 @@ dependencies = [ [[package]] name = "goose-cli" -version = "1.35.0" +version = "1.36.0" dependencies = [ "anstream", "anyhow", @@ -4639,7 +4639,7 @@ dependencies = [ [[package]] name = "goose-mcp" -version = "1.35.0" +version = "1.36.0" dependencies = [ "anyhow", "base64 0.22.1", @@ -4669,7 +4669,7 @@ dependencies = [ [[package]] name = "goose-sdk" -version = "1.35.0" +version = "1.36.0" dependencies = [ "agent-client-protocol", "agent-client-protocol-schema", @@ -4682,7 +4682,7 @@ dependencies = [ [[package]] name = "goose-server" -version = "1.35.0" +version = "1.36.0" dependencies = [ "anyhow", "aws-lc-rs", @@ -4729,7 +4729,7 @@ dependencies = [ [[package]] name = "goose-test" -version = "1.35.0" +version = "1.36.0" dependencies = [ "clap", "serde_json", @@ -4737,7 +4737,7 @@ dependencies = [ [[package]] name = "goose-test-support" -version = "1.35.0" +version = "1.36.0" dependencies = [ "axum", "env-lock", diff --git a/Cargo.toml b/Cargo.toml index d33b88d695fc..27e84329fd57 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ resolver = "2" [workspace.package] edition = "2021" -version = "1.35.0" +version = "1.36.0" rust-version = "1.91.1" authors = ["AAIF "] license = "Apache-2.0" diff --git a/crates/goose/src/providers/canonical/data/canonical_models.json b/crates/goose/src/providers/canonical/data/canonical_models.json index 38969d61a536..b1cdba60b302 100644 --- a/crates/goose/src/providers/canonical/data/canonical_models.json +++ b/crates/goose/src/providers/canonical/data/canonical_models.json @@ -6562,7 +6562,7 @@ "cost": { "input": 0.14, "output": 0.28, - "cache_read": 0.028 + "cache_read": 0.0028 }, "limit": { "context": 1000000, @@ -6590,9 +6590,9 @@ }, "open_weights": true, "cost": { - "input": 1.74, - "output": 3.48, - "cache_read": 0.145 + "input": 0.435, + "output": 0.87, + "cache_read": 0.003625 }, "limit": { "context": 1000000, @@ -8260,6 +8260,37 @@ "output": 65536 } }, + { + "id": "alibaba-cn/qwen3.6-flash", + "name": "Qwen3.6 Flash", + "family": "qwen3.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-04-27", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.1875, + "output": 1.125, + "cache_write": 0.234375 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, { "id": "alibaba-cn/qwen3.6-max-preview", "name": "Qwen3.6 Max Preview", @@ -8322,6 +8353,36 @@ "output": 65536 } }, + { + "id": "alibaba-cn/qwen3.7-max", + "name": "Qwen3.7 Max", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-21", + "last_updated": "2026-05-21", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 2.5, + "output": 7.5, + "cache_read": 0.5, + "cache_write": 3.125 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, { "id": "alibaba-cn/qwq-32b", "name": "QwQ 32B", @@ -8769,140 +8830,15 @@ } }, { - "id": "alibaba-coding-plan-cn/qwen3.6-plus", - "name": "Qwen3.6 Plus", - "family": "qwen", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-04-02", - "last_updated": "2026-04-02", - "modalities": { - "input": [ - "text", - "image", - "video" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.0, - "output": 0.0, - "cache_read": 0.0, - "cache_write": 0.0 - }, - "limit": { - "context": 1000000, - "output": 65536 - } - }, - { - "id": "alibaba-coding-plan/MiniMax-M2.5", - "name": "MiniMax-M2.5", - "family": "minimax", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2026-02-12", - "last_updated": "2026-02-12", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.0, - "output": 0.0, - "cache_read": 0.0, - "cache_write": 0.0 - }, - "limit": { - "context": 196608, - "output": 24576 - } - }, - { - "id": "alibaba-coding-plan/glm-4.7", - "name": "GLM-4.7", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.0, - "output": 0.0, - "cache_read": 0.0, - "cache_write": 0.0 - }, - "limit": { - "context": 202752, - "output": 16384 - } - }, - { - "id": "alibaba-coding-plan/glm-5", - "name": "GLM-5", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.0, - "output": 0.0, - "cache_read": 0.0, - "cache_write": 0.0 - }, - "limit": { - "context": 202752, - "output": 16384 - } - }, - { - "id": "alibaba-coding-plan/kimi-k2.5", - "name": "Kimi K2.5", - "family": "kimi", + "id": "alibaba-coding-plan-cn/qwen3.6-flash", + "name": "Qwen3.6 Flash", + "family": "qwen3.6", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", + "release_date": "2026-04-27", + "last_updated": "2026-04-27", "modalities": { "input": [ "text", @@ -8913,73 +8849,11 @@ "text" ] }, - "open_weights": true, - "cost": { - "input": 0.0, - "output": 0.0, - "cache_read": 0.0, - "cache_write": 0.0 - }, - "limit": { - "context": 262144, - "output": 32768 - } - }, - { - "id": "alibaba-coding-plan/qwen3-coder-next", - "name": "Qwen3 Coder Next", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2026-02-03", - "last_updated": "2026-02-03", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.0, - "output": 0.0, - "cache_read": 0.0, - "cache_write": 0.0 - }, - "limit": { - "context": 262144, - "output": 65536 - } - }, - { - "id": "alibaba-coding-plan/qwen3-coder-plus", - "name": "Qwen3 Coder Plus", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-23", - "last_updated": "2025-07-23", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, + "open_weights": false, "cost": { - "input": 0.0, - "output": 0.0, - "cache_read": 0.0, - "cache_write": 0.0 + "input": 0.1875, + "output": 1.125, + "cache_write": 0.234375 }, "limit": { "context": 1000000, @@ -8987,47 +8861,16 @@ } }, { - "id": "alibaba-coding-plan/qwen3-max", - "name": "Qwen3 Max", - "family": "qwen", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2026-01-23", - "last_updated": "2026-01-23", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.0, - "output": 0.0, - "cache_read": 0.0, - "cache_write": 0.0 - }, - "limit": { - "context": 262144, - "output": 32768 - } - }, - { - "id": "alibaba-coding-plan/qwen3.5-plus", - "name": "Qwen3.5 Plus", + "id": "alibaba-coding-plan-cn/qwen3.6-plus", + "name": "Qwen3.6 Plus", "family": "qwen", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2025-04", - "release_date": "2026-02-16", - "last_updated": "2026-02-16", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", "modalities": { "input": [ "text", @@ -9050,6 +8893,316 @@ "output": 65536 } }, + { + "id": "alibaba-coding-plan-cn/qwen3.7-max", + "name": "Qwen3.7 Max", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-21", + "last_updated": "2026-05-21", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 2.5, + "output": 7.5, + "cache_read": 0.5, + "cache_write": 3.125 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, + { + "id": "alibaba-coding-plan/MiniMax-M2.5", + "name": "MiniMax-M2.5", + "family": "minimax", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.0, + "output": 0.0, + "cache_read": 0.0, + "cache_write": 0.0 + }, + "limit": { + "context": 196608, + "output": 24576 + } + }, + { + "id": "alibaba-coding-plan/glm-4.7", + "name": "GLM-4.7", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.0, + "output": 0.0, + "cache_read": 0.0, + "cache_write": 0.0 + }, + "limit": { + "context": 202752, + "output": 16384 + } + }, + { + "id": "alibaba-coding-plan/glm-5", + "name": "GLM-5", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.0, + "output": 0.0, + "cache_read": 0.0, + "cache_write": 0.0 + }, + "limit": { + "context": 202752, + "output": 16384 + } + }, + { + "id": "alibaba-coding-plan/kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.0, + "output": 0.0, + "cache_read": 0.0, + "cache_write": 0.0 + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + { + "id": "alibaba-coding-plan/qwen3-coder-next", + "name": "Qwen3 Coder Next", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-03", + "last_updated": "2026-02-03", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.0, + "output": 0.0, + "cache_read": 0.0, + "cache_write": 0.0 + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, + { + "id": "alibaba-coding-plan/qwen3-coder-plus", + "name": "Qwen3 Coder Plus", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-07-23", + "last_updated": "2025-07-23", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.0, + "output": 0.0, + "cache_read": 0.0, + "cache_write": 0.0 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, + { + "id": "alibaba-coding-plan/qwen3-max", + "name": "Qwen3 Max", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-01-23", + "last_updated": "2026-01-23", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.0, + "output": 0.0, + "cache_read": 0.0, + "cache_write": 0.0 + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, + { + "id": "alibaba-coding-plan/qwen3.5-plus", + "name": "Qwen3.5 Plus", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-02-16", + "last_updated": "2026-02-16", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.0, + "output": 0.0, + "cache_read": 0.0, + "cache_write": 0.0 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, + { + "id": "alibaba-coding-plan/qwen3.6-flash", + "name": "Qwen3.6 Flash", + "family": "qwen3.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-04-27", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.1875, + "output": 1.125, + "cache_write": 0.234375 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, { "id": "alibaba-coding-plan/qwen3.6-plus", "name": "Qwen3.6 Plus", @@ -9083,6 +9236,36 @@ "output": 65536 } }, + { + "id": "alibaba-coding-plan/qwen3.7-max", + "name": "Qwen3.7 Max", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-21", + "last_updated": "2026-05-21", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 2.5, + "output": 7.5, + "cache_read": 0.5, + "cache_write": 3.125 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, { "id": "alibaba/qvq-max", "name": "QVQ Max", @@ -10434,6 +10617,37 @@ "output": 65536 } }, + { + "id": "alibaba/qwen3.6-flash", + "name": "Qwen3.6 Flash", + "family": "qwen3.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-27", + "last_updated": "2026-04-27", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.1875, + "output": 1.125, + "cache_write": 0.234375 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, { "id": "alibaba/qwen3.6-max-preview", "name": "Qwen3.6 Max Preview", @@ -10498,6 +10712,36 @@ "output": 65536 } }, + { + "id": "alibaba/qwen3.7-max", + "name": "Qwen3.7 Max", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-21", + "last_updated": "2026-05-21", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 2.5, + "output": 7.5, + "cache_read": 0.5, + "cache_write": 3.125 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, { "id": "alibaba/qwq-plus", "name": "QwQ Plus", @@ -14182,12 +14426,13 @@ "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-05-01", - "last_updated": "2026-05-01", + "release_date": "2026-04-17", + "last_updated": "2026-04-17", "modalities": { "input": [ "text", - "image" + "image", + "pdf" ], "output": [ "text" @@ -24631,6 +24876,90 @@ "output": 131072 } }, + { + "id": "cloudflare-workers-ai/@cf/aisingapore/gemma-sea-lion-v4-27b-it", + "name": "Gemma Sea Lion V4 27B It", + "family": "gemma", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-09-23", + "last_updated": "2025-09-23", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.351, + "output": 0.555 + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + { + "id": "cloudflare-workers-ai/@cf/deepseek-ai/deepseek-r1-distill-qwen-32b", + "name": "Deepseek R1 Distill Qwen 32B", + "family": "deepseek", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-01-22", + "last_updated": "2025-01-22", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.497, + "output": 4.881 + }, + "limit": { + "context": 80000, + "output": 80000 + } + }, + { + "id": "cloudflare-workers-ai/@cf/google/gemma-3-12b-it", + "name": "Gemma 3 12B It", + "family": "gemma", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-03-18", + "last_updated": "2025-03-18", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.345, + "output": 0.556 + }, + "limit": { + "context": 80000, + "output": 80000 + } + }, { "id": "cloudflare-workers-ai/@cf/google/gemma-4-26b-a4b-it", "name": "Gemma 4 26B A4B IT", @@ -24660,6 +24989,287 @@ "output": 16384 } }, + { + "id": "cloudflare-workers-ai/@cf/ibm-granite/granite-4.0-h-micro", + "name": "Granite 4.0 H Micro", + "family": "granite", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-10-07", + "last_updated": "2025-10-07", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.017, + "output": 0.112 + }, + "limit": { + "context": 131000, + "output": 131000 + } + }, + { + "id": "cloudflare-workers-ai/@cf/meta/llama-2-7b-chat-fp16", + "name": "Llama 2 7B Chat fp16", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2023-11-07", + "last_updated": "2023-11-07", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.556, + "output": 6.667 + }, + "limit": { + "context": 4096, + "output": 4096 + } + }, + { + "id": "cloudflare-workers-ai/@cf/meta/llama-3-8b-instruct", + "name": "Llama 3 8B Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-04-18", + "last_updated": "2024-04-18", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.282, + "output": 0.827 + }, + "limit": { + "context": 7968, + "output": 7968 + } + }, + { + "id": "cloudflare-workers-ai/@cf/meta/llama-3-8b-instruct-awq", + "name": "Llama 3 8B Instruct Awq", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-05-09", + "last_updated": "2024-05-09", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.123, + "output": 0.266 + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + { + "id": "cloudflare-workers-ai/@cf/meta/llama-3.1-8b-instruct-awq", + "name": "Llama 3.1 8B Instruct Awq", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-07-25", + "last_updated": "2024-07-25", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.123, + "output": 0.266 + }, + "limit": { + "context": 8192, + "output": 8192 + } + }, + { + "id": "cloudflare-workers-ai/@cf/meta/llama-3.1-8b-instruct-fp8", + "name": "Llama 3.1 8B Instruct fp8", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-07-25", + "last_updated": "2024-07-25", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.152, + "output": 0.287 + }, + "limit": { + "context": 32000, + "output": 32000 + } + }, + { + "id": "cloudflare-workers-ai/@cf/meta/llama-3.2-11b-vision-instruct", + "name": "Llama 3.2 11B Vision Instruct", + "family": "llama", + "attachment": true, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.0485, + "output": 0.676 + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, + { + "id": "cloudflare-workers-ai/@cf/meta/llama-3.2-1b-instruct", + "name": "Llama 3.2 1B Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.027, + "output": 0.201 + }, + "limit": { + "context": 60000, + "output": 60000 + } + }, + { + "id": "cloudflare-workers-ai/@cf/meta/llama-3.2-3b-instruct", + "name": "Llama 3.2 3B Instruct", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2024-09-25", + "last_updated": "2024-09-25", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.0509, + "output": 0.335 + }, + "limit": { + "context": 80000, + "output": 80000 + } + }, + { + "id": "cloudflare-workers-ai/@cf/meta/llama-3.3-70b-instruct-fp8-fast", + "name": "Llama 3.3 70B Instruct fp8 Fast", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.293, + "output": 2.253 + }, + "limit": { + "context": 24000, + "output": 24000 + } + }, { "id": "cloudflare-workers-ai/@cf/meta/llama-4-scout-17b-16e-instruct", "name": "Llama 4 Scout 17B 16E Instruct", @@ -24685,10 +25295,94 @@ "output": 0.85 }, "limit": { - "context": 128000, + "context": 131000, "output": 16384 } }, + { + "id": "cloudflare-workers-ai/@cf/meta/llama-guard-3-8b", + "name": "Llama Guard 3 8B", + "family": "llama", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-01-22", + "last_updated": "2025-01-22", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.484, + "output": 0.03 + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + { + "id": "cloudflare-workers-ai/@cf/mistral/mistral-7b-instruct-v0.1", + "name": "Mistral 7B Instruct V0.1", + "family": "mistral", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2023-11-07", + "last_updated": "2023-11-07", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.11, + "output": 0.19 + }, + "limit": { + "context": 2824, + "output": 2824 + } + }, + { + "id": "cloudflare-workers-ai/@cf/mistralai/mistral-small-3.1-24b-instruct", + "name": "Mistral Small 3.1 24B Instruct", + "family": "mistral-small", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-03-18", + "last_updated": "2025-03-18", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.351, + "output": 0.555 + }, + "limit": { + "context": 128000, + "output": 128000 + } + }, { "id": "cloudflare-workers-ai/@cf/moonshotai/kimi-k2.5", "name": "Kimi K2.5", @@ -24747,7 +25441,7 @@ "cache_read": 0.16 }, "limit": { - "context": 256000, + "context": 262144, "output": 256000 } }, @@ -24782,6 +25476,7 @@ { "id": "cloudflare-workers-ai/@cf/openai/gpt-oss-120b", "name": "GPT OSS 120B", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, @@ -24809,6 +25504,7 @@ { "id": "cloudflare-workers-ai/@cf/openai/gpt-oss-20b", "name": "GPT OSS 20B", + "family": "gpt-oss", "attachment": false, "reasoning": true, "tool_call": true, @@ -24833,6 +25529,90 @@ "output": 16384 } }, + { + "id": "cloudflare-workers-ai/@cf/qwen/qwen2.5-coder-32b-instruct", + "name": "Qwen2.5 Coder 32B Instruct", + "family": "qwen", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2025-02-27", + "last_updated": "2025-02-27", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.66, + "output": 1.0 + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + { + "id": "cloudflare-workers-ai/@cf/qwen/qwen3-30b-a3b-fp8", + "name": "Qwen3 30B A3b fp8", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2025-04-30", + "last_updated": "2025-04-30", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.0509, + "output": 0.335 + }, + "limit": { + "context": 32768, + "output": 32768 + } + }, + { + "id": "cloudflare-workers-ai/@cf/qwen/qwq-32b", + "name": "Qwq 32B", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": false, + "temperature": true, + "release_date": "2025-03-05", + "last_updated": "2025-03-05", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.66, + "output": 1.0 + }, + "limit": { + "context": 24000, + "output": 24000 + } + }, { "id": "cloudflare-workers-ai/@cf/zai-org/glm-4.7-flash", "name": "GLM-4.7-Flash", @@ -24854,7 +25634,7 @@ }, "open_weights": true, "cost": { - "input": 0.06, + "input": 0.0605, "output": 0.4 }, "limit": { @@ -25552,7 +26332,7 @@ "cost": { "input": 0.133, "output": 0.266, - "cache_read": 0.028 + "cache_read": 0.0028 }, "limit": { "context": 1048576, @@ -25582,7 +26362,7 @@ "cost": { "input": 1.553, "output": 3.106, - "cache_read": 0.145 + "cache_read": 0.003625 }, "limit": { "context": 1048576, @@ -26630,6 +27410,644 @@ "output": 250000 } }, + { + "id": "crof/deepseek-v3.2", + "name": "DeepSeek V3.2", + "family": "deepseek", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2025-07-22", + "last_updated": "2025-07-22", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.28, + "output": 0.38, + "cache_read": 0.06 + }, + "limit": { + "context": 163840, + "output": 163840 + } + }, + { + "id": "crof/deepseek-v4-flash", + "name": "DeepSeek V4 Flash", + "family": "deepseek-flash", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.12, + "output": 0.21, + "cache_read": 0.02 + }, + "limit": { + "context": 1000000, + "output": 131072 + } + }, + { + "id": "crof/deepseek-v4-pro", + "name": "DeepSeek V4 Pro", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-05", + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.4, + "output": 0.85, + "cache_read": 0.003 + }, + "limit": { + "context": 1000000, + "output": 131072 + } + }, + { + "id": "crof/deepseek-v4-pro-precision", + "name": "DeepSeek V4 Pro (Precision)", + "family": "deepseek-thinking", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-24", + "last_updated": "2026-04-24", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.1 + }, + "limit": { + "context": 1000000, + "output": 131072 + } + }, + { + "id": "crof/gemma-4-31b-it", + "name": "Gemma 4 31B IT", + "family": "gemma", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.02 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + { + "id": "crof/glm-4.7", + "name": "GLM-4.7", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2025-12-22", + "last_updated": "2025-12-22", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.25, + "output": 1.1, + "cache_read": 0.05, + "cache_write": 0.0 + }, + "limit": { + "context": 202752, + "output": 202752 + } + }, + { + "id": "crof/glm-4.7-flash", + "name": "GLM-4.7-Flash", + "family": "glm-flash", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-01-19", + "last_updated": "2026-01-19", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.04, + "output": 0.3, + "cache_read": 0.008, + "cache_write": 0.0 + }, + "limit": { + "context": 200000, + "output": 131072 + } + }, + { + "id": "crof/glm-5", + "name": "GLM-5", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-11", + "last_updated": "2026-02-11", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.48, + "output": 1.9, + "cache_read": 0.1, + "cache_write": 0.0 + }, + "limit": { + "context": 202752, + "output": 202752 + } + }, + { + "id": "crof/glm-5.1", + "name": "GLM-5.1", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.45, + "output": 2.1, + "cache_read": 0.09, + "cache_write": 0.0 + }, + "limit": { + "context": 202752, + "output": 202752 + } + }, + { + "id": "crof/glm-5.1-precision", + "name": "GLM 5.1 (Precision)", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.75, + "output": 2.9, + "cache_read": 0.15 + }, + "limit": { + "context": 202752, + "output": 202752 + } + }, + { + "id": "crof/greg", + "name": "Experiment!: Greg", + "attachment": false, + "reasoning": false, + "tool_call": false, + "temperature": true, + "release_date": "2026-01-27", + "last_updated": "2026-01-27", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.1, + "output": 0.2, + "cache_read": 0.02 + }, + "limit": { + "context": 229376, + "output": 229376 + } + }, + { + "id": "crof/kimi-k2.5", + "name": "Kimi K2.5", + "family": "kimi-k2.5", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2026-01", + "last_updated": "2026-01", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.35, + "output": 1.7, + "cache_read": 0.07 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + { + "id": "crof/kimi-k2.5-lightning", + "name": "Kimi K2.5 (Lightning)", + "family": "kimi-k2.5", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": false, + "release_date": "2026-02-06", + "last_updated": "2026-02-06", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 1.0, + "output": 3.0, + "cache_read": 0.2 + }, + "limit": { + "context": 131072, + "output": 32768 + } + }, + { + "id": "crof/kimi-k2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.5, + "output": 1.99, + "cache_read": 0.1 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + { + "id": "crof/kimi-k2.6-precision", + "name": "Kimi K2.6 (Precision)", + "family": "kimi-k2.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.55, + "output": 2.7, + "cache_read": 0.11 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + { + "id": "crof/mimo-v2.5-pro", + "name": "MiMo-V2.5-Pro", + "family": "mimo", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2024-12", + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.5, + "output": 1.5, + "cache_read": 0.1 + }, + "limit": { + "context": 1048576, + "output": 131072 + } + }, + { + "id": "crof/mimo-v2.5-pro-precision", + "name": "MiMo-V2.5-Pro (Precision)", + "family": "mimo", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.8, + "output": 2.5, + "cache_read": 0.16 + }, + "limit": { + "context": 1000000, + "output": 131072 + } + }, + { + "id": "crof/minimax-m2.5", + "name": "MiniMax-M2.5", + "family": "minimax", + "attachment": false, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.11, + "output": 0.95, + "cache_read": 0.02, + "cache_write": 0.375 + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + { + "id": "crof/qwen3.5-397b-a17b", + "name": "Qwen3.5 397B-A17B", + "family": "qwen", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-15", + "last_updated": "2026-02-15", + "modalities": { + "input": [ + "text", + "image", + "video", + "audio" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.35, + "output": 1.75, + "cache_read": 0.07 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + { + "id": "crof/qwen3.5-9b", + "name": "Qwen3.5 9B", + "family": "qwen", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-13", + "last_updated": "2026-03-13", + "modalities": { + "input": [ + "text", + "image", + "video", + "audio" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.04, + "output": 0.15, + "cache_read": 0.008 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + { + "id": "crof/qwen3.6-27b", + "name": "Qwen3.6 27B", + "family": "qwen", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-22", + "last_updated": "2026-04-22", + "modalities": { + "input": [ + "text", + "image", + "video", + "audio" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.2, + "output": 1.5, + "cache_read": 0.04 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, { "id": "databricks/databricks-claude-haiku-4.5", "name": "Claude Haiku 4.5 (latest)", @@ -27026,8 +28444,8 @@ "cache_read": 0.2 }, "limit": { - "context": 1000000, - "output": 64000 + "context": 1048576, + "output": 65536 } }, { @@ -27815,7 +29233,7 @@ "cost": { "input": 0.14, "output": 0.28, - "cache_read": 0.028 + "cache_read": 0.0028 }, "limit": { "context": 1000000, @@ -27843,9 +29261,9 @@ }, "open_weights": true, "cost": { - "input": 1.74, - "output": 3.48, - "cache_read": 0.145 + "input": 0.435, + "output": 0.87, + "cache_read": 0.003625 }, "limit": { "context": 65536, @@ -27854,9 +29272,9 @@ }, { "id": "deepinfra/google/gemma-4-26B-A4B-it", - "name": "Gemma 4 26B", + "name": "Gemma 4 26B A4B IT", "family": "gemma", - "attachment": false, + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, @@ -27877,15 +29295,15 @@ "output": 0.34 }, "limit": { - "context": 256000, - "output": 8192 + "context": 262144, + "output": 32768 } }, { "id": "deepinfra/google/gemma-4-31B-it", - "name": "Gemma 4 31B", + "name": "Gemma 4 31B IT", "family": "gemma", - "attachment": false, + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, @@ -27906,8 +29324,8 @@ "output": 0.38 }, "limit": { - "context": 256000, - "output": 8192 + "context": 262144, + "output": 32768 } }, { @@ -28633,7 +30051,7 @@ "cost": { "input": 0.14, "output": 0.28, - "cache_read": 0.028 + "cache_read": 0.0028 }, "limit": { "context": 1000000, @@ -28661,9 +30079,9 @@ }, "open_weights": true, "cost": { - "input": 1.74, - "output": 3.48, - "cache_read": 0.145 + "input": 0.435, + "output": 0.87, + "cache_read": 0.003625 }, "limit": { "context": 1000000, @@ -31983,65 +33401,6 @@ "output": 262000 } }, - { - "id": "fireworks-ai/accounts/fireworks/models/deepseek-v3p1", - "name": "DeepSeek V3.1", - "family": "deepseek", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-08-21", - "last_updated": "2025-08-21", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.56, - "output": 1.68 - }, - "limit": { - "context": 163840, - "output": 163840 - } - }, - { - "id": "fireworks-ai/accounts/fireworks/models/deepseek-v3p2", - "name": "DeepSeek V3.2", - "family": "deepseek", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-09", - "release_date": "2025-12-01", - "last_updated": "2025-12-01", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.56, - "output": 1.68, - "cache_read": 0.28 - }, - "limit": { - "context": 160000, - "output": 160000 - } - }, { "id": "fireworks-ai/accounts/fireworks/models/deepseek-v4-flash", "name": "DeepSeek V4 Flash", @@ -32095,130 +33454,13 @@ "cost": { "input": 1.74, "output": 3.48, - "cache_read": 0.15 + "cache_read": 0.145 }, "limit": { "context": 1000000, "output": 384000 } }, - { - "id": "fireworks-ai/accounts/fireworks/models/glm-4p5", - "name": "GLM 4.5", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-07-29", - "last_updated": "2025-07-29", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.55, - "output": 2.19 - }, - "limit": { - "context": 131072, - "output": 131072 - } - }, - { - "id": "fireworks-ai/accounts/fireworks/models/glm-4p5-air", - "name": "GLM 4.5 Air", - "family": "glm-air", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-08-01", - "last_updated": "2025-08-01", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.22, - "output": 0.88 - }, - "limit": { - "context": 131072, - "output": 131072 - } - }, - { - "id": "fireworks-ai/accounts/fireworks/models/glm-4p7", - "name": "GLM 4.7", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-04", - "release_date": "2025-12-22", - "last_updated": "2025-12-22", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.6, - "output": 2.2, - "cache_read": 0.3 - }, - "limit": { - "context": 198000, - "output": 198000 - } - }, - { - "id": "fireworks-ai/accounts/fireworks/models/glm-5", - "name": "GLM 5", - "family": "glm", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2026-02-11", - "last_updated": "2026-02-11", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 1.0, - "output": 3.2, - "cache_read": 0.5 - }, - "limit": { - "context": 202752, - "output": 131072 - } - }, { "id": "fireworks-ai/accounts/fireworks/models/glm-5p1", "name": "GLM 5.1", @@ -32269,7 +33511,8 @@ "open_weights": true, "cost": { "input": 0.15, - "output": 0.6 + "output": 0.6, + "cache_read": 0.015 }, "limit": { "context": 131072, @@ -32296,72 +33539,15 @@ }, "open_weights": true, "cost": { - "input": 0.05, - "output": 0.2 + "input": 0.07, + "output": 0.3, + "cache_read": 0.035 }, "limit": { "context": 131072, "output": 32768 } }, - { - "id": "fireworks-ai/accounts/fireworks/models/kimi-k2-instruct", - "name": "Kimi K2 Instruct", - "family": "kimi", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-11", - "last_updated": "2025-07-11", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 1.0, - "output": 3.0 - }, - "limit": { - "context": 128000, - "output": 16384 - } - }, - { - "id": "fireworks-ai/accounts/fireworks/models/kimi-k2-thinking", - "name": "Kimi K2 Thinking", - "family": "kimi-thinking", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-11-06", - "last_updated": "2025-11-06", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.6, - "output": 2.5, - "cache_read": 0.3 - }, - "limit": { - "context": 256000, - "output": 256000 - } - }, { "id": "fireworks-ai/accounts/fireworks/models/kimi-k2p5", "name": "Kimi K2.5", @@ -32424,35 +33610,6 @@ "output": 262000 } }, - { - "id": "fireworks-ai/accounts/fireworks/models/minimax-m2p1", - "name": "MiniMax-M2.1", - "family": "minimax", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "release_date": "2025-12-23", - "last_updated": "2025-12-23", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.3, - "output": 1.2, - "cache_read": 0.03 - }, - "limit": { - "context": 200000, - "output": 200000 - } - }, { "id": "fireworks-ai/accounts/fireworks/models/minimax-m2p5", "name": "MiniMax-M2.5", @@ -32504,7 +33661,7 @@ "cost": { "input": 0.3, "output": 1.2, - "cache_read": 0.03 + "cache_read": 0.06 }, "limit": { "context": 196608, @@ -32542,16 +33699,44 @@ } }, { - "id": "fireworks-ai/accounts/fireworks/routers/kimi-k2p5-turbo", - "name": "Kimi K2.5 Turbo", + "id": "fireworks-ai/accounts/fireworks/routers/glm-5p1-fast", + "name": "GLM 5.1 Fast", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-01", + "last_updated": "2026-04-01", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 2.8, + "output": 8.8, + "cache_read": 0.52 + }, + "limit": { + "context": 202800, + "output": 131072 + } + }, + { + "id": "fireworks-ai/accounts/fireworks/routers/kimi-k2p6-turbo", + "name": "Kimi K2.6 Turbo", "family": "kimi-thinking", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", + "release_date": "2026-04-17", + "last_updated": "2026-04-17", "modalities": { "input": [ "text", @@ -32563,13 +33748,13 @@ }, "open_weights": true, "cost": { - "input": 0.0, - "output": 0.0, - "cache_read": 0.0 + "input": 2.0, + "output": 8.0, + "cache_read": 0.3 }, "limit": { - "context": 256000, - "output": 256000 + "context": 262000, + "output": 262000 } }, { @@ -33828,22 +35013,84 @@ } }, { - "id": "github-copilot/gemini-3-flash-preview", - "name": "Gemini 3 Flash", - "family": "gemini-flash", + "id": "github-copilot/gemini-3-flash-preview", + "name": "Gemini 3 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-12-17", + "last_updated": "2025-12-17", + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.0, + "output": 0.0 + }, + "limit": { + "context": 128000, + "output": 64000 + } + }, + { + "id": "github-copilot/gemini-3-pro-preview", + "name": "Gemini 3 Pro Preview", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.0, + "output": 0.0 + }, + "limit": { + "context": 128000, + "output": 64000 + } + }, + { + "id": "github-copilot/gemini-3.1-pro-preview", + "name": "Gemini 3.1 Pro Preview", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2025-01", - "release_date": "2025-12-17", - "last_updated": "2025-12-17", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", "modalities": { "input": [ "text", - "image", - "audio", - "video" + "image" ], "output": [ "text" @@ -33860,16 +35107,16 @@ } }, { - "id": "github-copilot/gemini-3-pro-preview", - "name": "Gemini 3 Pro Preview", - "family": "gemini-pro", + "id": "github-copilot/gemini-3.5-flash", + "name": "Gemini 3.5 Flash", + "family": "gemini-flash", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2025-01", - "release_date": "2025-11-18", - "last_updated": "2025-11-18", + "release_date": "2026-05-19", + "last_updated": "2026-05-19", "modalities": { "input": [ "text", @@ -33891,36 +35138,6 @@ "output": 64000 } }, - { - "id": "github-copilot/gemini-3.1-pro-preview", - "name": "Gemini 3.1 Pro Preview", - "family": "gemini-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-02-19", - "last_updated": "2026-02-19", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.0, - "output": 0.0 - }, - "limit": { - "context": 128000, - "output": 64000 - } - }, { "id": "github-copilot/gpt-4.1", "name": "GPT-4.1", @@ -37060,8 +38277,8 @@ "cache_write": 3.75 }, "limit": { - "context": 200000, - "output": 64000 + "context": 1000000, + "output": 128000 } }, { @@ -37456,8 +38673,8 @@ "cache_write": 3.75 }, "limit": { - "context": 200000, - "output": 64000 + "context": 1000000, + "output": 128000 } }, { @@ -37555,7 +38772,7 @@ }, { "id": "google-vertex/gemini-2.0-flash-lite", - "name": "Gemini 2.0 Flash Lite", + "name": "Gemini 2.0 Flash-Lite", "family": "gemini-flash-lite", "attachment": true, "reasoning": false, @@ -37623,7 +38840,7 @@ }, { "id": "google-vertex/gemini-2.5-flash-lite", - "name": "Gemini 2.5 Flash Lite", + "name": "Gemini 2.5 Flash-Lite", "family": "gemini-flash-lite", "attachment": true, "reasoning": true, @@ -37689,108 +38906,6 @@ "output": 65536 } }, - { - "id": "google-vertex/gemini-2.5-flash-lite-preview-09", - "name": "Gemini 2.5 Flash Lite Preview 09-25", - "family": "gemini-flash-lite", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.1, - "output": 0.4, - "cache_read": 0.025 - }, - "limit": { - "context": 1048576, - "output": 65536 - } - }, - { - "id": "google-vertex/gemini-2.5-flash-preview-04-17", - "name": "Gemini 2.5 Flash Preview 04-17", - "family": "gemini-flash", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-04-17", - "last_updated": "2025-04-17", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.15, - "output": 0.6, - "cache_read": 0.0375 - }, - "limit": { - "context": 1048576, - "output": 65536 - } - }, - { - "id": "google-vertex/gemini-2.5-flash-preview-05-20", - "name": "Gemini 2.5 Flash Preview 05-20", - "family": "gemini-flash", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.15, - "output": 0.6, - "cache_read": 0.0375 - }, - "limit": { - "context": 1048576, - "output": 65536 - } - }, { "id": "google-vertex/gemini-2.5-flash-preview-09", "name": "Gemini 2.5 Flash Preview 09-25", @@ -37860,74 +38975,6 @@ "output": 65536 } }, - { - "id": "google-vertex/gemini-2.5-pro-preview-05-06", - "name": "Gemini 2.5 Pro Preview 05-06", - "family": "gemini-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-05-06", - "last_updated": "2025-05-06", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 1.25, - "output": 10.0, - "cache_read": 0.31 - }, - "limit": { - "context": 1048576, - "output": 65536 - } - }, - { - "id": "google-vertex/gemini-2.5-pro-preview-06-05", - "name": "Gemini 2.5 Pro Preview 06-05", - "family": "gemini-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-05", - "last_updated": "2025-06-05", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 1.25, - "output": 10.0, - "cache_read": 0.31 - }, - "limit": { - "context": 1048576, - "output": 65536 - } - }, { "id": "google-vertex/gemini-3-flash-preview", "name": "Gemini 3 Flash Preview", @@ -38132,6 +39179,40 @@ "output": 65536 } }, + { + "id": "google-vertex/gemini-3.5-flash", + "name": "Gemini 3.5 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-05-19", + "last_updated": "2026-05-19", + "modalities": { + "input": [ + "text", + "image", + "video", + "audio", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.5, + "output": 9.0, + "cache_read": 0.15 + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, { "id": "google-vertex/gemini-embedding-001", "name": "Gemini Embedding 001", @@ -38158,7 +39239,7 @@ }, "limit": { "context": 2048, - "output": 3072 + "output": 1 } }, { @@ -38461,105 +39542,6 @@ "output": 131072 } }, - { - "id": "google/gemini-1.5-flash", - "name": "Gemini 1.5 Flash", - "family": "gemini-flash", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-05-14", - "last_updated": "2024-05-14", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.075, - "output": 0.3, - "cache_read": 0.01875 - }, - "limit": { - "context": 1000000, - "output": 8192 - } - }, - { - "id": "google/gemini-1.5-flash-8b", - "name": "Gemini 1.5 Flash-8B", - "family": "gemini-flash", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-10-03", - "last_updated": "2024-10-03", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.0375, - "output": 0.15, - "cache_read": 0.01 - }, - "limit": { - "context": 1000000, - "output": 8192 - } - }, - { - "id": "google/gemini-1.5-pro", - "name": "Gemini 1.5 Pro", - "family": "gemini-pro", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-04", - "release_date": "2024-02-15", - "last_updated": "2024-02-15", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 1.25, - "output": 5.0, - "cache_read": 0.3125 - }, - "limit": { - "context": 1000000, - "output": 8192 - } - }, { "id": "google/gemini-2.0-flash", "name": "Gemini 2.0 Flash", @@ -38596,7 +39578,7 @@ }, { "id": "google/gemini-2.0-flash-lite", - "name": "Gemini 2.0 Flash Lite", + "name": "Gemini 2.0 Flash-Lite", "family": "gemini-flash-lite", "attachment": true, "reasoning": false, @@ -38663,39 +39645,7 @@ }, { "id": "google/gemini-2.5-flash-image", - "name": "Gemini 2.5 Flash Image", - "family": "gemini-flash", - "attachment": true, - "reasoning": true, - "tool_call": false, - "temperature": true, - "knowledge": "2025-06", - "release_date": "2025-08-26", - "last_updated": "2025-08-26", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text", - "image" - ] - }, - "open_weights": false, - "cost": { - "input": 0.3, - "output": 30.0, - "cache_read": 0.075 - }, - "limit": { - "context": 32768, - "output": 32768 - } - }, - { - "id": "google/gemini-2.5-flash-image-preview", - "name": "Gemini 2.5 Flash Image (Preview)", + "name": "Nano Banana", "family": "gemini-flash", "attachment": true, "reasoning": true, @@ -38727,7 +39677,7 @@ }, { "id": "google/gemini-2.5-flash-lite", - "name": "Gemini 2.5 Flash Lite", + "name": "Gemini 2.5 Flash-Lite", "family": "gemini-flash-lite", "attachment": true, "reasoning": true, @@ -38759,176 +39709,6 @@ "output": 65536 } }, - { - "id": "google/gemini-2.5-flash-lite-preview-06-17", - "name": "Gemini 2.5 Flash Lite Preview 06-17", - "family": "gemini-flash-lite", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.1, - "output": 0.4, - "cache_read": 0.025 - }, - "limit": { - "context": 1048576, - "output": 65536 - } - }, - { - "id": "google/gemini-2.5-flash-lite-preview-09", - "name": "Gemini 2.5 Flash Lite Preview 09-25", - "family": "gemini-flash-lite", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.1, - "output": 0.4, - "cache_read": 0.025 - }, - "limit": { - "context": 1048576, - "output": 65536 - } - }, - { - "id": "google/gemini-2.5-flash-preview-04-17", - "name": "Gemini 2.5 Flash Preview 04-17", - "family": "gemini-flash", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-04-17", - "last_updated": "2025-04-17", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.15, - "output": 0.6, - "cache_read": 0.0375 - }, - "limit": { - "context": 1048576, - "output": 65536 - } - }, - { - "id": "google/gemini-2.5-flash-preview-05-20", - "name": "Gemini 2.5 Flash Preview 05-20", - "family": "gemini-flash", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.15, - "output": 0.6, - "cache_read": 0.0375 - }, - "limit": { - "context": 1048576, - "output": 65536 - } - }, - { - "id": "google/gemini-2.5-flash-preview-09", - "name": "Gemini 2.5 Flash Preview 09-25", - "family": "gemini-flash", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.3, - "output": 2.5, - "cache_read": 0.075 - }, - "limit": { - "context": 1048576, - "output": 65536 - } - }, { "id": "google/gemini-2.5-flash-preview-tts", "name": "Gemini 2.5 Flash Preview TTS", @@ -38936,7 +39716,7 @@ "attachment": false, "reasoning": false, "tool_call": false, - "temperature": false, + "temperature": true, "knowledge": "2025-01", "release_date": "2025-05-01", "last_updated": "2025-05-01", @@ -38954,8 +39734,8 @@ "output": 10.0 }, "limit": { - "context": 8000, - "output": 16000 + "context": 8192, + "output": 16384 } }, { @@ -38992,74 +39772,6 @@ "output": 65536 } }, - { - "id": "google/gemini-2.5-pro-preview-05-06", - "name": "Gemini 2.5 Pro Preview 05-06", - "family": "gemini-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-05-06", - "last_updated": "2025-05-06", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 1.25, - "output": 10.0, - "cache_read": 0.31 - }, - "limit": { - "context": 1048576, - "output": 65536 - } - }, - { - "id": "google/gemini-2.5-pro-preview-06-05", - "name": "Gemini 2.5 Pro Preview 06-05", - "family": "gemini-pro", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-06-05", - "last_updated": "2025-06-05", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 1.25, - "output": 10.0, - "cache_read": 0.31 - }, - "limit": { - "context": 1048576, - "output": 65536 - } - }, { "id": "google/gemini-2.5-pro-preview-tts", "name": "Gemini 2.5 Pro Preview TTS", @@ -39067,7 +39779,7 @@ "attachment": false, "reasoning": false, "tool_call": false, - "temperature": false, + "temperature": true, "knowledge": "2025-01", "release_date": "2025-05-01", "last_updated": "2025-05-01", @@ -39085,8 +39797,8 @@ "output": 20.0 }, "limit": { - "context": 8000, - "output": 16000 + "context": 8192, + "output": 16384 } }, { @@ -39153,13 +39865,13 @@ "cache_read": 0.2 }, "limit": { - "context": 1000000, - "output": 64000 + "context": 1048576, + "output": 65536 } }, { "id": "google/gemini-3.1-flash-image-preview", - "name": "Gemini 3.1 Flash Image (Preview)", + "name": "Nano Banana 2", "family": "gemini-flash", "attachment": true, "reasoning": true, @@ -39185,8 +39897,8 @@ "output": 60.0 }, "limit": { - "context": 131072, - "output": 32768 + "context": 65536, + "output": 65536 } }, { @@ -39385,7 +40097,7 @@ }, "limit": { "context": 2048, - "output": 3072 + "output": 1 } }, { @@ -39413,43 +40125,9 @@ }, "open_weights": false, "cost": { - "input": 0.3, - "output": 2.5, - "cache_read": 0.075 - }, - "limit": { - "context": 1048576, - "output": 65536 - } - }, - { - "id": "google/gemini-flash-lite", - "name": "Gemini Flash-Lite Latest", - "family": "gemini-flash-lite", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.1, - "output": 0.4, - "cache_read": 0.025 + "input": 0.3, + "output": 2.5, + "cache_read": 0.075 }, "limit": { "context": 1048576, @@ -39457,223 +40135,44 @@ } }, { - "id": "google/gemini-live-2.5-flash", - "name": "Gemini Live 2.5 Flash", - "family": "gemini-flash", + "id": "google/gemini-flash-lite", + "name": "Gemini Flash-Lite Latest", + "family": "gemini-flash-lite", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2025-01", - "release_date": "2025-09-01", - "last_updated": "2025-09-01", + "release_date": "2025-09-25", + "last_updated": "2025-09-25", "modalities": { "input": [ "text", "image", "audio", - "video" - ], - "output": [ - "text", - "audio" - ] - }, - "open_weights": false, - "cost": { - "input": 0.5, - "output": 2.0 - }, - "limit": { - "context": 128000, - "output": 8000 - } - }, - { - "id": "google/gemini-live-2.5-flash-preview-native-audio", - "name": "Gemini Live 2.5 Flash Preview Native Audio", - "family": "gemini-flash", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": false, - "knowledge": "2025-01", - "release_date": "2025-06-17", - "last_updated": "2025-09-18", - "modalities": { - "input": [ - "text", - "audio", - "video" + "video", + "pdf" ], "output": [ - "text", - "audio" + "text" ] }, "open_weights": false, "cost": { - "input": 0.5, - "output": 2.0 + "input": 0.1, + "output": 0.4, + "cache_read": 0.025 }, "limit": { - "context": 131072, + "context": 1048576, "output": 65536 } }, - { - "id": "google/gemma-3-12b-it", - "name": "Gemma 3 12B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.0, - "output": 0.0 - }, - "limit": { - "context": 32768, - "output": 8192 - } - }, - { - "id": "google/gemma-3-27b-it", - "name": "Gemma 3 27B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-12", - "last_updated": "2025-03-12", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.0, - "output": 0.0 - }, - "limit": { - "context": 131072, - "output": 8192 - } - }, - { - "id": "google/gemma-3-4b-it", - "name": "Gemma 3 4B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.0, - "output": 0.0 - }, - "limit": { - "context": 32768, - "output": 8192 - } - }, - { - "id": "google/gemma-3n-e2b-it", - "name": "Gemma 3n 2B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.0, - "output": 0.0 - }, - "limit": { - "context": 8192, - "output": 2000 - } - }, - { - "id": "google/gemma-3n-e4b-it", - "name": "Gemma 3n 4B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.0, - "output": 0.0 - }, - "limit": { - "context": 8192, - "output": 2000 - } - }, { "id": "google/gemma-4-26b-a4b-it", - "name": "Gemma 4 26B", + "name": "Gemma 4 26B A4B IT", "family": "gemma", - "attachment": false, + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, @@ -39691,15 +40190,15 @@ "open_weights": true, "cost": {}, "limit": { - "context": 256000, - "output": 8192 + "context": 262144, + "output": 32768 } }, { "id": "google/gemma-4-31b-it", - "name": "Gemma 4 31B", + "name": "Gemma 4 31B IT", "family": "gemma", - "attachment": false, + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, @@ -39717,8 +40216,8 @@ "open_weights": true, "cost": {}, "limit": { - "context": 256000, - "output": 8192 + "context": 262144, + "output": 32768 } }, { @@ -43609,9 +44108,9 @@ }, "open_weights": true, "cost": { - "input": 1.74, - "output": 3.48, - "cache_read": 0.145 + "input": 0.435, + "output": 0.87, + "cache_read": 0.003625 }, "limit": { "context": 1048576, @@ -44294,6 +44793,129 @@ "output": 8192 } }, + { + "id": "inceptron/MiniMaxAI/MiniMax-M2.5", + "name": "MiniMax M2.5", + "family": "minimax", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-02-12", + "last_updated": "2026-02-12", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.24, + "output": 0.9, + "cache_read": 0.03, + "cache_write": 0.0 + }, + "limit": { + "context": 196608, + "output": 196608 + } + }, + { + "id": "inceptron/moonshotai/Kimi-K2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.78, + "output": 3.5, + "cache_read": 0.2, + "cache_write": 0.0 + }, + "limit": { + "context": 262144, + "output": 262144 + } + }, + { + "id": "inceptron/nvidia/llama-3.3-70b-instruct-fp8", + "name": "Llama 3.3 70B Instruct", + "family": "llama", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "knowledge": "2023-12", + "release_date": "2024-12-06", + "last_updated": "2024-12-06", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.12, + "output": 0.38, + "cache_read": 0.0, + "cache_write": 0.0 + }, + "limit": { + "context": 131072, + "output": 131072 + } + }, + { + "id": "inceptron/zai-org/GLM-5.1-FP8", + "name": "GLM 5.1", + "family": "glm", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-27", + "last_updated": "2026-03-27", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.4, + "output": 4.4, + "cache_read": 0.26, + "cache_write": 0.0 + }, + "limit": { + "context": 202752, + "output": 202752 + } + }, { "id": "inference/google/gemma-3", "name": "Google Gemma 3", @@ -57120,7 +57742,7 @@ "cost": { "input": 0.14, "output": 0.28, - "cache_read": 0.028 + "cache_read": 0.0028 }, "limit": { "context": 1000000, @@ -57148,9 +57770,9 @@ }, "open_weights": true, "cost": { - "input": 1.74, - "output": 3.48, - "cache_read": 0.145 + "input": 0.435, + "output": 0.87, + "cache_read": 0.003625 }, "limit": { "context": 1000000, @@ -57251,7 +57873,7 @@ }, { "id": "llmgateway/gemini-2.0-flash-lite", - "name": "Gemini 2.0 Flash Lite", + "name": "Gemini 2.0 Flash-Lite", "family": "gemini-flash-lite", "attachment": true, "reasoning": false, @@ -57318,7 +57940,7 @@ }, { "id": "llmgateway/gemini-2.5-flash-lite", - "name": "Gemini 2.5 Flash Lite", + "name": "Gemini 2.5 Flash-Lite", "family": "gemini-flash-lite", "attachment": true, "reasoning": true, @@ -57350,40 +57972,6 @@ "output": 65536 } }, - { - "id": "llmgateway/gemini-2.5-flash-lite-preview-09", - "name": "Gemini 2.5 Flash Lite Preview 09-25", - "family": "gemini-flash-lite", - "attachment": true, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-01", - "release_date": "2025-09-25", - "last_updated": "2025-09-25", - "modalities": { - "input": [ - "text", - "image", - "audio", - "video", - "pdf" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 0.1, - "output": 0.4, - "cache_read": 0.025 - }, - "limit": { - "context": 1048576, - "output": 65536 - } - }, { "id": "llmgateway/gemini-2.5-pro", "name": "Gemini 2.5 Pro", @@ -57554,6 +58142,40 @@ "output": 65536 } }, + { + "id": "llmgateway/gemini-3.5-flash", + "name": "Gemini 3.5 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-05-19", + "last_updated": "2026-05-19", + "modalities": { + "input": [ + "text", + "image", + "video", + "audio", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.5, + "output": 9.0, + "cache_read": 0.15 + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, { "id": "llmgateway/gemini-pro", "name": "Gemini Pro Latest", @@ -57612,36 +58234,6 @@ "output": 16384 } }, - { - "id": "llmgateway/gemma-3-12b-it", - "name": "Gemma 3 12B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.0, - "output": 0.0 - }, - "limit": { - "context": 32768, - "output": 8192 - } - }, { "id": "llmgateway/gemma-3-1b-it", "name": "Gemma 3 1B IT", @@ -57699,94 +58291,6 @@ "output": 16384 } }, - { - "id": "llmgateway/gemma-3-4b-it", - "name": "Gemma 3 4B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-03-13", - "last_updated": "2025-03-13", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.0, - "output": 0.0 - }, - "limit": { - "context": 32768, - "output": 8192 - } - }, - { - "id": "llmgateway/gemma-3n-e2b-it", - "name": "Gemma 3n 2B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-07-09", - "last_updated": "2025-07-09", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.0, - "output": 0.0 - }, - "limit": { - "context": 8192, - "output": 2000 - } - }, - { - "id": "llmgateway/gemma-3n-e4b-it", - "name": "Gemma 3n 4B", - "family": "gemma", - "attachment": true, - "reasoning": false, - "tool_call": false, - "temperature": true, - "knowledge": "2024-10", - "release_date": "2025-05-20", - "last_updated": "2025-05-20", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.0, - "output": 0.0 - }, - "limit": { - "context": 8192, - "output": 2000 - } - }, { "id": "llmgateway/glm-4-32b-0414-128k", "name": "GLM-4 32B (0414-128k)", @@ -59281,6 +59785,37 @@ "output": 256000 } }, + { + "id": "llmgateway/grok-4-20", + "name": "Grok 4.20 (Reasoning)", + "family": "grok", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-09", + "last_updated": "2026-03-09", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.25, + "output": 2.5, + "cache_read": 0.2 + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, { "id": "llmgateway/grok-4-20-beta", "name": "Grok 4.20 (Reasoning)", @@ -59294,7 +59829,8 @@ "modalities": { "input": [ "text", - "image" + "image", + "pdf" ], "output": [ "text" @@ -59302,8 +59838,8 @@ }, "open_weights": false, "cost": { - "input": 2.0, - "output": 6.0, + "input": 1.25, + "output": 2.5, "cache_read": 0.2 }, "limit": { @@ -59324,7 +59860,8 @@ "modalities": { "input": [ "text", - "image" + "image", + "pdf" ], "output": [ "text" @@ -59332,8 +59869,39 @@ }, "open_weights": false, "cost": { - "input": 2.0, - "output": 6.0, + "input": 1.25, + "output": 2.5, + "cache_read": 0.2 + }, + "limit": { + "context": 2000000, + "output": 30000 + } + }, + { + "id": "llmgateway/grok-4-20-non", + "name": "Grok 4.20 (Non-Reasoning)", + "family": "grok", + "attachment": true, + "reasoning": false, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-09", + "last_updated": "2026-03-09", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.25, + "output": 2.5, "cache_read": 0.2 }, "limit": { @@ -59409,12 +59977,13 @@ "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-05-01", - "last_updated": "2026-05-01", + "release_date": "2026-04-17", + "last_updated": "2026-04-17", "modalities": { "input": [ "text", - "image" + "image", + "pdf" ], "output": [ "text" @@ -61722,6 +62291,36 @@ "output": 65536 } }, + { + "id": "llmgateway/qwen3.7-max", + "name": "Qwen3.7 Max", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-21", + "last_updated": "2026-05-21", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 2.5, + "output": 7.5, + "cache_read": 0.5, + "cache_write": 3.125 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, { "id": "llmgateway/qwen35-397b-a17b", "name": "Qwen3.5 397B-A17B", @@ -79097,6 +79696,35 @@ "output": 32768 } }, + { + "id": "nearai/Qwen/Qwen3.6-35B-A3B-FP8", + "name": "Qwen 3.6 35B A3B FP8", + "family": "qwen", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-17", + "last_updated": "2026-04-17", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.17, + "output": 1.1, + "cache_read": 0.056 + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, { "id": "nearai/anthropic/claude-haiku-4.5", "name": "Claude Haiku 4.5 (latest)", @@ -79327,7 +79955,7 @@ }, { "id": "nearai/google/gemini-2.5-flash-lite", - "name": "Gemini 2.5 Flash Lite", + "name": "Gemini 2.5 Flash-Lite", "family": "gemini-flash-lite", "attachment": true, "reasoning": true, @@ -79393,6 +80021,40 @@ "output": 65536 } }, + { + "id": "nearai/google/gemini-3-pro", + "name": "Gemini 3 Pro Preview", + "family": "gemini-pro", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2025-11-18", + "last_updated": "2025-11-18", + "modalities": { + "input": [ + "text", + "image", + "video", + "audio", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.25, + "output": 15.0, + "cache_read": 0.0 + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, { "id": "nearai/google/gemini-3.1-flash-lite", "name": "Gemini 3.1 Flash Lite", @@ -79427,6 +80089,69 @@ "output": 65536 } }, + { + "id": "nearai/google/gemini-3.5-flash", + "name": "Gemini 3.5 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-05-19", + "last_updated": "2026-05-19", + "modalities": { + "input": [ + "text", + "image", + "video", + "audio", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.5, + "output": 9.0, + "cache_read": 0.15 + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, + { + "id": "nearai/google/gemma-4-31B-it", + "name": "Gemma 4 31B IT", + "family": "gemma", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.13, + "output": 0.4, + "cache_read": 0.026 + }, + "limit": { + "context": 262144, + "output": 32768 + } + }, { "id": "nearai/openai/gpt-4.1", "name": "GPT-4.1", @@ -80989,8 +81714,8 @@ }, "open_weights": true, "cost": { - "input": 0.05, - "output": 0.1 + "input": 0.29, + "output": 1.15 }, "limit": { "context": 131056, @@ -81275,8 +82000,8 @@ }, "open_weights": true, "cost": { - "input": 0.05, - "output": 0.1 + "input": 0.29, + "output": 1.15 }, "limit": { "context": 131056, @@ -84480,7 +85205,7 @@ "cost": { "input": 0.14, "output": 0.28, - "cache_read": 0.028 + "cache_read": 0.0028 }, "limit": { "context": 1048576, @@ -84508,9 +85233,9 @@ }, "open_weights": true, "cost": { - "input": 1.74, - "output": 3.48, - "cache_read": 0.145 + "input": 0.435, + "output": 0.87, + "cache_read": 0.003625 }, "limit": { "context": 1048576, @@ -89685,7 +90410,7 @@ }, "limit": { "context": 200000, - "output": 128000 + "output": 32000 } }, { @@ -90117,6 +90842,40 @@ "output": 65536 } }, + { + "id": "opencode/gemini-3.5-flash", + "name": "Gemini 3.5 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-05-19", + "last_updated": "2026-05-19", + "modalities": { + "input": [ + "text", + "image", + "video", + "audio", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.5, + "output": 9.0, + "cache_read": 0.15 + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, { "id": "opencode/glm-4.6", "name": "GLM-4.6", @@ -90831,6 +91590,36 @@ "output": 128000 } }, + { + "id": "opencode/grok-build-0.1", + "name": "Grok Build 0.1", + "family": "grok-build", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-20", + "last_updated": "2026-05-20", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.0, + "output": 2.0, + "cache_read": 0.2 + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, { "id": "opencode/grok-code", "name": "Grok Code Fast 1", @@ -91703,36 +92492,6 @@ "output": 4096 } }, - { - "id": "openrouter/alibaba/tongyi-deepresearch-30b-a3b", - "name": "Tongyi DeepResearch 30B A3B", - "family": "yi", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2025-03-31", - "release_date": "2025-09-18", - "last_updated": "2025-09-18", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.09, - "output": 0.45, - "cache_read": 0.09 - }, - "limit": { - "context": 131072, - "output": 131072 - } - }, { "id": "openrouter/allenai/olmo-3-32b-think", "name": "Olmo 3 32B Think", @@ -92005,7 +92764,7 @@ }, { "id": "openrouter/anthropic/claude-haiku-4.5", - "name": "Claude Haiku 4.5", + "name": "Claude Haiku 4.5 (latest)", "family": "claude-haiku", "attachment": true, "reasoning": true, @@ -92071,13 +92830,13 @@ }, { "id": "openrouter/anthropic/claude-opus-4.1", - "name": "Claude Opus 4.1", + "name": "Claude Opus 4.1 (latest)", "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-31", + "knowledge": "2025-03-31", "release_date": "2025-08-05", "last_updated": "2025-08-05", "modalities": { @@ -92104,13 +92863,13 @@ }, { "id": "openrouter/anthropic/claude-opus-4.5", - "name": "Claude Opus 4.5", + "name": "Claude Opus 4.5 (latest)", "family": "claude-opus", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-05-30", + "knowledge": "2025-03-31", "release_date": "2025-11-24", "last_updated": "2025-11-24", "modalities": { @@ -92144,8 +92903,8 @@ "tool_call": true, "temperature": true, "knowledge": "2025-05-31", - "release_date": "2026-02-04", - "last_updated": "2026-02-04", + "release_date": "2026-02-05", + "last_updated": "2026-03-13", "modalities": { "input": [ "text", @@ -92300,13 +93059,13 @@ }, { "id": "openrouter/anthropic/claude-sonnet-4.5", - "name": "Claude Sonnet 4.5", + "name": "Claude Sonnet 4.5 (latest)", "family": "claude-sonnet", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-31", + "knowledge": "2025-07-31", "release_date": "2025-09-29", "last_updated": "2025-09-29", "modalities": { @@ -92341,7 +93100,7 @@ "temperature": true, "knowledge": "2025-08-31", "release_date": "2026-02-17", - "last_updated": "2026-02-17", + "last_updated": "2026-03-13", "modalities": { "input": [ "text", @@ -92449,34 +93208,6 @@ "output": 65537 } }, - { - "id": "openrouter/arcee-ai/trinity-large-preview", - "name": "Trinity Large Preview", - "family": "trinity", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "release_date": "2026-01-27", - "last_updated": "2026-01-27", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.15, - "output": 0.45 - }, - "limit": { - "context": 131000, - "output": 131000 - } - }, { "id": "openrouter/arcee-ai/trinity-large-thinking", "name": "Trinity Large Thinking", @@ -93002,13 +93733,13 @@ }, { "id": "openrouter/cohere/command-r-08", - "name": "Command R (08-2024)", + "name": "Command R", "family": "command-r", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-03-31", + "knowledge": "2024-06-01", "release_date": "2024-08-30", "last_updated": "2024-08-30", "modalities": { @@ -93019,7 +93750,7 @@ "text" ] }, - "open_weights": false, + "open_weights": true, "cost": { "input": 0.15, "output": 0.6 @@ -93031,13 +93762,13 @@ }, { "id": "openrouter/cohere/command-r-plus-08", - "name": "Command R+ (08-2024)", + "name": "Command R+", "family": "command-r", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-03-31", + "knowledge": "2024-06-01", "release_date": "2024-08-30", "last_updated": "2024-08-30", "modalities": { @@ -93048,7 +93779,7 @@ "text" ] }, - "open_weights": false, + "open_weights": true, "cost": { "input": 2.5, "output": 10.0 @@ -93060,15 +93791,15 @@ }, { "id": "openrouter/cohere/command-r7b-12", - "name": "Command R7B (12-2024)", + "name": "Command R7B", "family": "command-r", "attachment": false, "reasoning": false, "tool_call": false, "temperature": true, - "knowledge": "2024-08-31", - "release_date": "2024-12-14", - "last_updated": "2024-12-14", + "knowledge": "2024-06-01", + "release_date": "2024-02-27", + "last_updated": "2024-02-27", "modalities": { "input": [ "text" @@ -93077,7 +93808,7 @@ "text" ] }, - "open_weights": false, + "open_weights": true, "cost": { "input": 0.0375, "output": 0.15 @@ -93117,15 +93848,15 @@ }, { "id": "openrouter/deepseek/deepseek-chat", - "name": "DeepSeek V3", + "name": "DeepSeek Chat", "family": "deepseek", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-07-31", - "release_date": "2024-12-26", - "last_updated": "2024-12-26", + "knowledge": "2025-09", + "release_date": "2025-12-01", + "last_updated": "2026-02-28", "modalities": { "input": [ "text" @@ -93136,12 +93867,12 @@ }, "open_weights": true, "cost": { - "input": 0.32, - "output": 0.89 + "input": 0.2288, + "output": 0.9144 }, "limit": { - "context": 163840, - "output": 16384 + "context": 128000, + "output": 16000 } }, { @@ -93414,11 +94145,12 @@ { "id": "openrouter/deepseek/deepseek-v4-flash", "name": "DeepSeek V4 Flash", - "family": "deepseek", + "family": "deepseek-flash", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, + "knowledge": "2025-05", "release_date": "2026-04-24", "last_updated": "2026-04-24", "modalities": { @@ -93431,23 +94163,24 @@ }, "open_weights": true, "cost": { - "input": 0.112, - "output": 0.224, - "cache_read": 0.022 + "input": 0.1, + "output": 0.2, + "cache_read": 0.02 }, "limit": { - "context": 1048575, - "output": 131072 + "context": 1048576, + "output": 16384 } }, { "id": "openrouter/deepseek/deepseek-v4-flash:free", "name": "DeepSeek V4 Flash (free)", - "family": "deepseek", + "family": "deepseek-flash", "attachment": false, "reasoning": true, "tool_call": true, "temperature": false, + "knowledge": "2025-05", "release_date": "2026-04-24", "last_updated": "2026-04-24", "modalities": { @@ -93471,11 +94204,12 @@ { "id": "openrouter/deepseek/deepseek-v4-pro", "name": "DeepSeek V4 Pro", - "family": "deepseek", + "family": "deepseek-thinking", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, + "knowledge": "2025-05", "release_date": "2026-04-24", "last_updated": "2026-04-24", "modalities": { @@ -93556,7 +94290,7 @@ "cache_write": 0.083333 }, "limit": { - "context": 1048576, + "context": 1000000, "output": 8192 } }, @@ -93601,9 +94335,9 @@ "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-31", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", "modalities": { "input": [ "pdf", @@ -93630,15 +94364,15 @@ }, { "id": "openrouter/google/gemini-2.5-flash-image", - "name": "Nano Banana (Gemini 2.5 Flash Image)", - "family": "gemini", + "name": "Nano Banana", + "family": "gemini-flash", "attachment": true, "reasoning": false, "tool_call": false, "temperature": true, - "knowledge": "2025-01-31", - "release_date": "2025-10-07", - "last_updated": "2025-10-07", + "knowledge": "2025-06", + "release_date": "2025-08-26", + "last_updated": "2025-08-26", "modalities": { "input": [ "image", @@ -93663,15 +94397,15 @@ }, { "id": "openrouter/google/gemini-2.5-flash-lite", - "name": "Gemini 2.5 Flash Lite", + "name": "Gemini 2.5 Flash-Lite", "family": "gemini-flash-lite", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-31", - "release_date": "2025-07-22", - "last_updated": "2025-07-22", + "knowledge": "2025-01", + "release_date": "2025-06-17", + "last_updated": "2025-06-17", "modalities": { "input": [ "text", @@ -93734,14 +94468,14 @@ { "id": "openrouter/google/gemini-2.5-pro", "name": "Gemini 2.5 Pro", - "family": "gemini", + "family": "gemini-pro", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01-31", - "release_date": "2025-06-17", - "last_updated": "2025-06-17", + "knowledge": "2025-01", + "release_date": "2025-03-20", + "last_updated": "2025-06-05", "modalities": { "input": [ "text", @@ -93904,7 +94638,7 @@ }, { "id": "openrouter/google/gemini-3.1-flash-image-preview", - "name": "Nano Banana 2 (Gemini 3.1 Flash Image Preview)", + "name": "Nano Banana 2", "family": "gemini-flash", "attachment": true, "reasoning": true, @@ -93936,11 +94670,12 @@ { "id": "openrouter/google/gemini-3.1-flash-lite", "name": "Gemini 3.1 Flash Lite", - "family": "gemini", + "family": "gemini-flash-lite", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, + "knowledge": "2025-01", "release_date": "2026-05-07", "last_updated": "2026-05-07", "modalities": { @@ -93975,6 +94710,7 @@ "reasoning": true, "tool_call": true, "temperature": true, + "knowledge": "2025-01", "release_date": "2026-03-03", "last_updated": "2026-03-03", "modalities": { @@ -94045,8 +94781,8 @@ "tool_call": true, "temperature": true, "knowledge": "2025-01", - "release_date": "2026-02-25", - "last_updated": "2026-02-25", + "release_date": "2026-02-19", + "last_updated": "2026-02-19", "modalities": { "input": [ "text", @@ -94071,6 +94807,41 @@ "output": 65536 } }, + { + "id": "openrouter/google/gemini-3.5-flash", + "name": "Gemini 3.5 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-05-19", + "last_updated": "2026-05-19", + "modalities": { + "input": [ + "text", + "image", + "video", + "pdf", + "audio" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.5, + "output": 9.0, + "cache_read": 0.15, + "cache_write": 0.083333 + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, { "id": "openrouter/google/gemma-2-27b-it", "name": "Gemma 2 27B", @@ -94221,15 +94992,14 @@ }, { "id": "openrouter/google/gemma-4-26b-a4b-it", - "name": "Gemma 4 26B A4B ", + "name": "Gemma 4 26B A4B IT", "family": "gemma", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-04-03", - "last_updated": "2026-04-03", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", "modalities": { "input": [ "image", @@ -94258,9 +95028,8 @@ "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", - "release_date": "2026-04-03", - "last_updated": "2026-04-03", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", "modalities": { "input": [ "image", @@ -94283,13 +95052,12 @@ }, { "id": "openrouter/google/gemma-4-31b-it", - "name": "Gemma 4 31B", + "name": "Gemma 4 31B IT", "family": "gemma", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", "release_date": "2026-04-02", "last_updated": "2026-04-02", "modalities": { @@ -94320,7 +95088,6 @@ "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-01", "release_date": "2026-04-02", "last_updated": "2026-04-02", "modalities": { @@ -94537,9 +95304,9 @@ }, "open_weights": false, "cost": { - "input": 0.3, - "output": 2.5, - "cache_read": 0.06 + "input": 0.075, + "output": 0.625, + "cache_read": 0.015 }, "limit": { "context": 262144, @@ -95039,13 +95806,13 @@ }, { "id": "openrouter/meta-llama/llama-3.3-70b-instruct", - "name": "Llama 3.3 70B Instruct", + "name": "Llama-3.3-70B-Instruct", "family": "llama", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-12-31", + "knowledge": "2023-12", "release_date": "2024-12-06", "last_updated": "2024-12-06", "modalities": { @@ -95074,7 +95841,7 @@ "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-12-31", + "knowledge": "2023-12", "release_date": "2024-12-06", "last_updated": "2024-12-06", "modalities": { @@ -95361,14 +96128,14 @@ }, { "id": "openrouter/minimax/minimax-m2", - "name": "MiniMax M2", + "name": "MiniMax-M2", "family": "minimax", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-10-23", - "last_updated": "2025-10-23", + "release_date": "2025-10-27", + "last_updated": "2025-10-27", "modalities": { "input": [ "text" @@ -95419,7 +96186,7 @@ }, { "id": "openrouter/minimax/minimax-m2.1", - "name": "MiniMax M2.1", + "name": "MiniMax-M2.1", "family": "minimax", "attachment": false, "reasoning": true, @@ -95448,7 +96215,7 @@ }, { "id": "openrouter/minimax/minimax-m2.5", - "name": "MiniMax M2.5", + "name": "MiniMax-M2.5", "family": "minimax", "attachment": false, "reasoning": true, @@ -95504,7 +96271,7 @@ }, { "id": "openrouter/minimax/minimax-m2.7", - "name": "MiniMax M2.7", + "name": "MiniMax-M2.7", "family": "minimax", "attachment": false, "reasoning": true, @@ -95563,7 +96330,7 @@ }, { "id": "openrouter/mistralai/devstral", - "name": "Devstral 2 2512", + "name": "Devstral 2", "family": "devstral", "attachment": true, "reasoning": false, @@ -95775,15 +96542,15 @@ }, { "id": "openrouter/mistralai/mistral-large", - "name": "Mistral Large", + "name": "Mistral Large 2.1", "family": "mistral-large", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-11-30", - "release_date": "2024-02-26", - "last_updated": "2024-02-26", + "knowledge": "2024-11", + "release_date": "2024-11-01", + "last_updated": "2024-11-04", "modalities": { "input": [ "text", @@ -95793,15 +96560,15 @@ "text" ] }, - "open_weights": false, + "open_weights": true, "cost": { "input": 2.0, "output": 6.0, "cache_read": 0.2 }, "limit": { - "context": 128000, - "output": 128000 + "context": 131072, + "output": 131072 } }, { @@ -95906,9 +96673,9 @@ "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-04-30", - "release_date": "2024-07-19", - "last_updated": "2024-07-19", + "knowledge": "2024-07", + "release_date": "2024-07-01", + "last_updated": "2024-07-01", "modalities": { "input": [ "text" @@ -96233,14 +97000,14 @@ { "id": "openrouter/moonshotai/kimi-k2.5", "name": "Kimi K2.5", - "family": "kimi", + "family": "kimi-k2.5", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2025-01", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", + "release_date": "2026-01", + "last_updated": "2026-01", "modalities": { "input": [ "text", @@ -96264,13 +97031,14 @@ { "id": "openrouter/moonshotai/kimi-k2.6", "name": "Kimi K2.6", - "family": "kimi", + "family": "kimi-k2.6", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-04-20", - "last_updated": "2026-04-20", + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", "modalities": { "input": [ "text", @@ -96815,15 +97583,15 @@ }, { "id": "openrouter/openai/gpt-3.5-turbo", - "name": "GPT-3.5 Turbo (older v0613)", + "name": "GPT-3.5-turbo", "family": "gpt", "attachment": false, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2021-09-30", - "release_date": "2024-01-25", - "last_updated": "2024-01-25", + "knowledge": "2021-09-01", + "release_date": "2023-03-01", + "last_updated": "2023-11-06", "modalities": { "input": [ "text" @@ -96834,11 +97602,11 @@ }, "open_weights": false, "cost": { - "input": 1.0, - "output": 2.0 + "input": 0.5, + "output": 1.5 }, "limit": { - "context": 4095, + "context": 16385, "output": 4096 } }, @@ -96908,9 +97676,9 @@ "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2021-09-30", - "release_date": "2023-05-28", - "last_updated": "2023-05-28", + "knowledge": "2023-11", + "release_date": "2023-11-06", + "last_updated": "2024-04-09", "modalities": { "input": [ "text" @@ -96966,8 +97734,8 @@ "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-12-31", - "release_date": "2024-04-09", + "knowledge": "2023-12", + "release_date": "2023-11-06", "last_updated": "2024-04-09", "modalities": { "input": [ @@ -97025,7 +97793,7 @@ "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-06-30", + "knowledge": "2024-04", "release_date": "2025-04-14", "last_updated": "2025-04-14", "modalities": { @@ -97051,13 +97819,13 @@ }, { "id": "openrouter/openai/gpt-4.1-mini", - "name": "GPT-4.1 Mini", + "name": "GPT-4.1 mini", "family": "gpt-mini", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-06-30", + "knowledge": "2024-04", "release_date": "2025-04-14", "last_updated": "2025-04-14", "modalities": { @@ -97083,13 +97851,13 @@ }, { "id": "openrouter/openai/gpt-4.1-nano", - "name": "GPT-4.1 Nano", - "family": "gpt", + "name": "GPT-4.1 nano", + "family": "gpt-nano", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2024-06-30", + "knowledge": "2024-04", "release_date": "2025-04-14", "last_updated": "2025-04-14", "modalities": { @@ -97121,7 +97889,7 @@ "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-10-31", + "knowledge": "2023-09", "release_date": "2024-08-06", "last_updated": "2024-08-06", "modalities": { @@ -97178,13 +97946,13 @@ }, { "id": "openrouter/openai/gpt-4o-mini", - "name": "GPT-4o-mini (2024-07-18)", - "family": "o-mini", + "name": "GPT-4o mini", + "family": "gpt-mini", "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-10-31", + "knowledge": "2023-09", "release_date": "2024-07-18", "last_updated": "2024-07-18", "modalities": { @@ -97332,15 +98100,15 @@ }, { "id": "openrouter/openai/gpt-5-codex", - "name": "GPT-5 Codex", + "name": "GPT-5-Codex", "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, "temperature": false, "knowledge": "2024-09-30", - "release_date": "2025-09-23", - "last_updated": "2025-09-23", + "release_date": "2025-09-15", + "last_updated": "2025-09-15", "modalities": { "input": [ "text", @@ -97434,7 +98202,7 @@ "reasoning": true, "tool_call": true, "temperature": false, - "knowledge": "2024-05-31", + "knowledge": "2024-05-30", "release_date": "2025-08-07", "last_updated": "2025-08-07", "modalities": { @@ -97466,7 +98234,7 @@ "reasoning": true, "tool_call": true, "temperature": false, - "knowledge": "2024-05-31", + "knowledge": "2024-05-30", "release_date": "2025-08-07", "last_updated": "2025-08-07", "modalities": { @@ -97587,7 +98355,7 @@ }, { "id": "openrouter/openai/gpt-5.1-codex", - "name": "GPT-5.1-Codex", + "name": "GPT-5.1 Codex", "family": "gpt-codex", "attachment": true, "reasoning": true, @@ -97618,15 +98386,15 @@ }, { "id": "openrouter/openai/gpt-5.1-codex-max", - "name": "GPT-5.1-Codex-Max", + "name": "GPT-5.1 Codex Max", "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, "temperature": false, "knowledge": "2024-09-30", - "release_date": "2025-12-04", - "last_updated": "2025-12-04", + "release_date": "2025-11-13", + "last_updated": "2025-11-13", "modalities": { "input": [ "text", @@ -97649,7 +98417,7 @@ }, { "id": "openrouter/openai/gpt-5.1-codex-mini", - "name": "GPT-5.1-Codex-Mini", + "name": "GPT-5.1 Codex mini", "family": "gpt-codex", "attachment": true, "reasoning": true, @@ -97687,8 +98455,8 @@ "tool_call": true, "temperature": false, "knowledge": "2025-08-31", - "release_date": "2025-12-10", - "last_updated": "2025-12-10", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", "modalities": { "input": [ "pdf", @@ -97744,15 +98512,15 @@ }, { "id": "openrouter/openai/gpt-5.2-codex", - "name": "GPT-5.2-Codex", + "name": "GPT-5.2 Codex", "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, "temperature": false, "knowledge": "2025-08-31", - "release_date": "2026-01-14", - "last_updated": "2026-01-14", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", "modalities": { "input": [ "text", @@ -97782,8 +98550,8 @@ "tool_call": true, "temperature": false, "knowledge": "2025-08-31", - "release_date": "2025-12-10", - "last_updated": "2025-12-10", + "release_date": "2025-12-11", + "last_updated": "2025-12-11", "modalities": { "input": [ "image", @@ -97837,15 +98605,15 @@ }, { "id": "openrouter/openai/gpt-5.3-codex", - "name": "GPT-5.3-Codex", + "name": "GPT-5.3 Codex", "family": "gpt-codex", "attachment": true, "reasoning": true, "tool_call": true, "temperature": false, "knowledge": "2025-08-31", - "release_date": "2026-02-24", - "last_updated": "2026-02-24", + "release_date": "2026-02-05", + "last_updated": "2026-02-05", "modalities": { "input": [ "text", @@ -97875,6 +98643,7 @@ "reasoning": true, "tool_call": true, "temperature": false, + "knowledge": "2025-08-31", "release_date": "2026-03-05", "last_updated": "2026-03-05", "modalities": { @@ -97932,7 +98701,7 @@ }, { "id": "openrouter/openai/gpt-5.4-mini", - "name": "GPT-5.4 Mini", + "name": "GPT-5.4 mini", "family": "gpt-mini", "attachment": true, "reasoning": true, @@ -97964,7 +98733,7 @@ }, { "id": "openrouter/openai/gpt-5.4-nano", - "name": "GPT-5.4 Nano", + "name": "GPT-5.4 nano", "family": "gpt-nano", "attachment": true, "reasoning": true, @@ -98034,8 +98803,8 @@ "tool_call": true, "temperature": false, "knowledge": "2025-12-01", - "release_date": "2026-04-24", - "last_updated": "2026-04-24", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", "modalities": { "input": [ "pdf", @@ -98060,14 +98829,14 @@ { "id": "openrouter/openai/gpt-5.5-pro", "name": "GPT-5.5 Pro", - "family": "gpt", + "family": "gpt-pro", "attachment": true, "reasoning": true, "tool_call": true, "temperature": false, "knowledge": "2025-12-01", - "release_date": "2026-04-24", - "last_updated": "2026-04-24", + "release_date": "2026-04-23", + "last_updated": "2026-04-23", "modalities": { "input": [ "pdf", @@ -98332,9 +99101,9 @@ "reasoning": true, "tool_call": true, "temperature": false, - "knowledge": "2023-10-31", - "release_date": "2024-12-17", - "last_updated": "2024-12-17", + "knowledge": "2023-09", + "release_date": "2024-12-05", + "last_updated": "2024-12-05", "modalities": { "input": [ "text", @@ -98359,12 +99128,12 @@ { "id": "openrouter/openai/o1-pro", "name": "o1-pro", - "family": "o", + "family": "o-pro", "attachment": true, "reasoning": true, "tool_call": false, "temperature": false, - "knowledge": "2023-10-31", + "knowledge": "2023-09", "release_date": "2025-03-19", "last_updated": "2025-03-19", "modalities": { @@ -98395,7 +99164,7 @@ "reasoning": true, "tool_call": true, "temperature": false, - "knowledge": "2024-06-30", + "knowledge": "2024-05", "release_date": "2025-04-16", "last_updated": "2025-04-16", "modalities": { @@ -98421,14 +99190,15 @@ }, { "id": "openrouter/openai/o3-deep-research", - "name": "o3 Deep Research", + "name": "o3-deep-research", "family": "o", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-10-10", - "last_updated": "2025-10-10", + "knowledge": "2024-05", + "release_date": "2024-06-26", + "last_updated": "2024-06-26", "modalities": { "input": [ "image", @@ -98452,15 +99222,15 @@ }, { "id": "openrouter/openai/o3-mini", - "name": "o3 Mini", - "family": "o", + "name": "o3-mini", + "family": "o-mini", "attachment": true, "reasoning": true, "tool_call": true, "temperature": false, - "knowledge": "2023-10-31", - "release_date": "2025-01-31", - "last_updated": "2025-01-31", + "knowledge": "2024-05", + "release_date": "2024-12-20", + "last_updated": "2025-01-29", "modalities": { "input": [ "text", @@ -98514,13 +99284,13 @@ }, { "id": "openrouter/openai/o3-pro", - "name": "o3 Pro", - "family": "o", + "name": "o3-pro", + "family": "o-pro", "attachment": true, "reasoning": true, "tool_call": true, "temperature": false, - "knowledge": "2024-06-30", + "knowledge": "2024-05", "release_date": "2025-06-10", "last_updated": "2025-06-10", "modalities": { @@ -98545,13 +99315,13 @@ }, { "id": "openrouter/openai/o4-mini", - "name": "o4 Mini", + "name": "o4-mini", "family": "o-mini", "attachment": true, "reasoning": true, "tool_call": true, "temperature": false, - "knowledge": "2024-06-30", + "knowledge": "2024-05", "release_date": "2025-04-16", "last_updated": "2025-04-16", "modalities": { @@ -98577,14 +99347,15 @@ }, { "id": "openrouter/openai/o4-mini-deep-research", - "name": "o4 Mini Deep Research", - "family": "o", + "name": "o4-mini-deep-research", + "family": "o-mini", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-10-10", - "last_updated": "2025-10-10", + "knowledge": "2024-05", + "release_date": "2024-06-26", + "last_updated": "2024-06-26", "modalities": { "input": [ "pdf", @@ -99999,7 +100770,7 @@ }, "limit": { "context": 262144, - "output": 65536 + "output": 262144 } }, { @@ -100054,9 +100825,8 @@ }, "open_weights": true, "cost": { - "input": 0.14, - "output": 1.0, - "cache_read": 0.05 + "input": 0.139, + "output": 1.0 }, "limit": { "context": 262144, @@ -100087,8 +100857,7 @@ "open_weights": true, "cost": { "input": 0.39, - "output": 2.34, - "cache_read": 0.195 + "output": 2.34 }, "limit": { "context": 262144, @@ -100240,12 +101009,12 @@ }, "open_weights": true, "cost": { - "input": 0.32, + "input": 0.3, "output": 3.2 }, "limit": { "context": 262144, - "output": 81920 + "output": 262144 } }, { @@ -100271,12 +101040,11 @@ "open_weights": true, "cost": { "input": 0.15, - "output": 1.0, - "cache_read": 0.05 + "output": 1.0 }, "limit": { - "context": 262144, - "output": 262144 + "context": 262140, + "output": 262140 } }, { @@ -100371,6 +101139,35 @@ "output": 65536 } }, + { + "id": "openrouter/qwen/qwen3.7-max", + "name": "Qwen3.7 Max", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-21", + "last_updated": "2026-05-21", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 2.5, + "output": 7.5, + "cache_write": 3.125 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, { "id": "openrouter/rekaai/reka-edge", "name": "Reka Edge", @@ -100650,12 +101447,13 @@ }, "open_weights": true, "cost": { - "input": 0.1, - "output": 0.3 + "input": 0.09, + "output": 0.3, + "cache_read": 0.02 }, "limit": { "context": 262144, - "output": 65536 + "output": 16384 } }, { @@ -101014,8 +101812,8 @@ "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-04-30", - "last_updated": "2026-04-30", + "release_date": "2026-04-17", + "last_updated": "2026-04-17", "modalities": { "input": [ "text", @@ -101036,6 +101834,36 @@ "output": 1000000 } }, + { + "id": "openrouter/x-ai/grok-build-0.1", + "name": "Grok Build 0.1", + "family": "grok-build", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-16", + "last_updated": "2026-04-16", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.0, + "output": 2.0, + "cache_read": 0.2 + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, { "id": "openrouter/xiaomi/mimo-v2-flash", "name": "MiMo-V2-Flash", @@ -101044,8 +101872,9 @@ "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2025-12-14", - "last_updated": "2025-12-14", + "knowledge": "2024-12-01", + "release_date": "2025-12-16", + "last_updated": "2026-02-04", "modalities": { "input": [ "text" @@ -101068,11 +101897,12 @@ { "id": "openrouter/xiaomi/mimo-v2-omni", "name": "MiMo-V2-Omni", - "family": "mimo-v2-omni", + "family": "mimo", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, + "knowledge": "2024-12", "release_date": "2026-03-18", "last_updated": "2026-03-18", "modalities": { @@ -101100,11 +101930,12 @@ { "id": "openrouter/xiaomi/mimo-v2-pro", "name": "MiMo-V2-Pro", - "family": "mimo-v2-pro", + "family": "mimo", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, + "knowledge": "2024-12", "release_date": "2026-03-18", "last_updated": "2026-03-18", "modalities": { @@ -101129,11 +101960,12 @@ { "id": "openrouter/xiaomi/mimo-v2.5", "name": "MiMo-V2.5", - "family": "mimo-v2.5", + "family": "mimo", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, + "knowledge": "2024-12", "release_date": "2026-04-22", "last_updated": "2026-04-22", "modalities": { @@ -101161,11 +101993,12 @@ { "id": "openrouter/xiaomi/mimo-v2.5-pro", "name": "MiMo-V2.5-Pro", - "family": "mimo-v2.5-pro", + "family": "mimo", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, + "knowledge": "2024-12", "release_date": "2026-04-22", "last_updated": "2026-04-22", "modalities": { @@ -101218,15 +102051,15 @@ }, { "id": "openrouter/z-ai/glm-4.5", - "name": "GLM 4.5", + "name": "GLM-4.5", "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-12-31", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", "modalities": { "input": [ "text" @@ -101248,15 +102081,15 @@ }, { "id": "openrouter/z-ai/glm-4.5-air", - "name": "GLM 4.5 Air", + "name": "GLM-4.5-Air", "family": "glm-air", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-12-31", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", "modalities": { "input": [ "text" @@ -101284,9 +102117,9 @@ "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-12-31", - "release_date": "2025-07-25", - "last_updated": "2025-07-25", + "knowledge": "2025-04", + "release_date": "2025-07-28", + "last_updated": "2025-07-28", "modalities": { "input": [ "text" @@ -101307,13 +102140,13 @@ }, { "id": "openrouter/z-ai/glm-4.5v", - "name": "GLM 4.5V", + "name": "GLM-4.5V", "family": "glm", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-12-31", + "knowledge": "2025-04", "release_date": "2025-08-11", "last_updated": "2025-08-11", "modalities": { @@ -101338,13 +102171,13 @@ }, { "id": "openrouter/z-ai/glm-4.6", - "name": "GLM 4.6", + "name": "GLM-4.6", "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-03-31", + "knowledge": "2025-04", "release_date": "2025-09-30", "last_updated": "2025-09-30", "modalities": { @@ -101368,12 +102201,13 @@ }, { "id": "openrouter/z-ai/glm-4.6v", - "name": "GLM 4.6V", + "name": "GLM-4.6V", "family": "glm", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, + "knowledge": "2025-04", "release_date": "2025-12-08", "last_updated": "2025-12-08", "modalities": { @@ -101399,7 +102233,7 @@ }, { "id": "openrouter/z-ai/glm-4.7", - "name": "GLM 4.7", + "name": "GLM-4.7", "family": "glm", "attachment": false, "reasoning": true, @@ -101429,12 +102263,13 @@ }, { "id": "openrouter/z-ai/glm-4.7-flash", - "name": "GLM 4.7 Flash", - "family": "glm", + "name": "GLM-4.7-Flash", + "family": "glm-flash", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, + "knowledge": "2025-04", "release_date": "2026-01-19", "last_updated": "2026-01-19", "modalities": { @@ -101458,7 +102293,7 @@ }, { "id": "openrouter/z-ai/glm-5", - "name": "GLM 5", + "name": "GLM-5", "family": "glm", "attachment": false, "reasoning": true, @@ -101487,14 +102322,14 @@ }, { "id": "openrouter/z-ai/glm-5-turbo", - "name": "GLM 5 Turbo", + "name": "GLM-5-Turbo", "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-03-15", - "last_updated": "2026-03-15", + "release_date": "2026-03-16", + "last_updated": "2026-03-16", "modalities": { "input": [ "text" @@ -101516,14 +102351,14 @@ }, { "id": "openrouter/z-ai/glm-5.1", - "name": "GLM 5.1", + "name": "GLM-5.1", "family": "glm", "attachment": false, "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-04-07", - "last_updated": "2026-04-07", + "release_date": "2026-03-27", + "last_updated": "2026-03-27", "modalities": { "input": [ "text" @@ -101532,7 +102367,7 @@ "text" ] }, - "open_weights": true, + "open_weights": false, "cost": { "input": 0.98, "output": 3.08, @@ -101540,12 +102375,12 @@ }, "limit": { "context": 202752, - "output": 131072 + "output": 202800 } }, { "id": "openrouter/z-ai/glm-5v-turbo", - "name": "GLM 5V Turbo", + "name": "GLM-5V-Turbo", "family": "glm", "attachment": true, "reasoning": true, @@ -101678,15 +102513,16 @@ "reasoning": true, "tool_call": true, "temperature": true, + "knowledge": "2025-01-01", "release_date": "2026-04-27", "last_updated": "2026-04-27", "modalities": { "input": [ "text", "image", + "video", "pdf", - "audio", - "video" + "audio" ], "output": [ "text" @@ -101694,9 +102530,9 @@ }, "open_weights": false, "cost": { - "input": 0.5, - "output": 3.0, - "cache_read": 0.05, + "input": 1.5, + "output": 9.0, + "cache_read": 0.15, "cache_write": 0.083333 }, "limit": { @@ -102212,7 +103048,7 @@ "cost": { "input": 0.19, "output": 0.37, - "cache_read": 0.028 + "cache_read": 0.0028 }, "limit": { "context": 1000000, @@ -102242,7 +103078,7 @@ "cost": { "input": 0.56, "output": 1.12, - "cache_read": 0.145 + "cache_read": 0.003625 }, "limit": { "context": 1000000, @@ -102285,7 +103121,7 @@ }, { "id": "orcarouter/google/gemini-2.5-flash-lite", - "name": "Gemini 2.5 Flash Lite", + "name": "Gemini 2.5 Flash-Lite", "family": "gemini-flash-lite", "attachment": true, "reasoning": true, @@ -102415,8 +103251,8 @@ "cache_read": 0.2 }, "limit": { - "context": 1000000, - "output": 64000 + "context": 1048576, + "output": 65536 } }, { @@ -102591,9 +103427,9 @@ }, { "id": "orcarouter/google/gemma-4-26b-a4b-it", - "name": "Gemma 4 26B", + "name": "Gemma 4 26B A4B IT", "family": "gemma", - "attachment": false, + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, @@ -102614,15 +103450,15 @@ "output": 0.33 }, "limit": { - "context": 256000, - "output": 8192 + "context": 262144, + "output": 32768 } }, { "id": "orcarouter/google/gemma-4-31b-it", - "name": "Gemma 4 31B", + "name": "Gemma 4 31B IT", "family": "gemma", - "attachment": false, + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, @@ -102643,8 +103479,8 @@ "output": 0.38 }, "limit": { - "context": 256000, - "output": 8192 + "context": 262144, + "output": 32768 } }, { @@ -102655,12 +103491,13 @@ "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-05-01", - "last_updated": "2026-05-01", + "release_date": "2026-04-17", + "last_updated": "2026-04-17", "modalities": { "input": [ "text", - "image" + "image", + "pdf" ], "output": [ "text" @@ -106278,6 +107115,38 @@ "output": 65536 } }, + { + "id": "poe/google/gemini-3.5-flash", + "name": "Gemini-3.5-Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": false, + "knowledge": "2025-01", + "release_date": "2026-05-19", + "last_updated": "2026-05-19", + "modalities": { + "input": [ + "text", + "image", + "audio" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.5152, + "output": 9.0909, + "cache_read": 0.1515 + }, + "limit": { + "context": 1048576, + "output": 65536 + } + }, { "id": "poe/google/gemini-deep-research", "name": "gemini-deep-research", @@ -109055,6 +109924,64 @@ "output": 128000 } }, + { + "id": "poolside/poolside/laguna-m.1", + "name": "Laguna M.1", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-28", + "last_updated": "2026-04-28", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 0.0, + "output": 0.0, + "cache_read": 0.0, + "cache_write": 0.0 + }, + "limit": { + "context": 131040, + "output": 8192 + } + }, + { + "id": "poolside/poolside/laguna-xs.2", + "name": "Laguna XS.2", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-04-28", + "last_updated": "2026-04-28", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.0, + "output": 0.0, + "cache_read": 0.0, + "cache_write": 0.0 + }, + "limit": { + "context": 131040, + "output": 8192 + } + }, { "id": "privatemode-ai/gemma-3-27b", "name": "Gemma 3 27B", @@ -113298,7 +114225,7 @@ "cost": { "input": 0.4928, "output": 0.7392, - "cache_read": 0.028 + "cache_read": 0.0028 }, "limit": { "context": 1000000, @@ -113328,7 +114255,7 @@ "cost": { "input": 0.4928, "output": 0.7392, - "cache_read": 0.028 + "cache_read": 0.0028 }, "limit": { "context": 1000000, @@ -113358,7 +114285,7 @@ "cost": { "input": 0.4928, "output": 0.7392, - "cache_read": 0.145 + "cache_read": 0.003625 }, "limit": { "context": 1000000, @@ -113388,7 +114315,7 @@ "cost": { "input": 0.4928, "output": 0.7392, - "cache_read": 0.145 + "cache_read": 0.003625 }, "limit": { "context": 1000000, @@ -114830,35 +115757,6 @@ "output": 3072 } }, - { - "id": "scaleway/deepseek-r1-distill-llama-70b", - "name": "DeepSeek R1 Distill Llama 70B", - "family": "deepseek-thinking", - "attachment": false, - "reasoning": true, - "tool_call": true, - "temperature": true, - "knowledge": "2024-07", - "release_date": "2025-01-20", - "last_updated": "2026-03-17", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": true, - "cost": { - "input": 0.9, - "output": 0.9 - }, - "limit": { - "context": 32000, - "output": 8196 - } - }, { "id": "scaleway/devstral-2-123b-instruct", "name": "Devstral 2 123B Instruct (2512)", @@ -114867,6 +115765,7 @@ "reasoning": false, "tool_call": true, "temperature": true, + "knowledge": "2025-12", "release_date": "2026-01-07", "last_updated": "2026-03-17", "modalities": { @@ -114918,18 +115817,20 @@ } }, { - "id": "scaleway/gpt-oss-120b", - "name": "GPT-OSS 120B", - "family": "gpt-oss", + "id": "scaleway/gemma-4-26b-a4b-it", + "name": "Gemma 4 26B A4B IT", + "family": "gemma", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-01-01", - "last_updated": "2026-03-17", + "knowledge": "2025-04", + "release_date": "2026-04-01", + "last_updated": "2026-05-22", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" @@ -114937,24 +115838,23 @@ }, "open_weights": true, "cost": { - "input": 0.15, - "output": 0.6 + "input": 0.25, + "output": 0.5 }, "limit": { - "context": 128000, - "output": 32768 + "context": 256000, + "output": 16384 } }, { - "id": "scaleway/llama-3.1-8b-instruct", - "name": "Llama 3.1 8B Instruct", - "family": "llama", - "attachment": false, + "id": "scaleway/gpt-oss-120b", + "name": "GPT-OSS 120B", + "family": "gpt-oss", + "attachment": true, "reasoning": false, "tool_call": true, "temperature": true, - "knowledge": "2023-12", - "release_date": "2025-01-01", + "release_date": "2024-01-01", "last_updated": "2026-03-17", "modalities": { "input": [ @@ -114966,12 +115866,12 @@ }, "open_weights": true, "cost": { - "input": 0.2, - "output": 0.2 + "input": 0.15, + "output": 0.6 }, "limit": { "context": 128000, - "output": 16384 + "output": 32768 } }, { @@ -115004,18 +115904,19 @@ } }, { - "id": "scaleway/mistral-nemo-instruct", - "name": "Mistral Nemo Instruct 2407", - "family": "mistral-nemo", + "id": "scaleway/mistral-medium-3.5-128b", + "name": "Mistral Medium 3.5 128B", + "family": "mistral-medium", "attachment": true, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2024-07-25", - "last_updated": "2026-03-17", + "release_date": "2026-04-29", + "last_updated": "2026-04-29", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" @@ -115023,12 +115924,12 @@ }, "open_weights": true, "cost": { - "input": 0.2, - "output": 0.2 + "input": 1.5, + "output": 7.5 }, "limit": { - "context": 128000, - "output": 8192 + "context": 256000, + "output": 16384 } }, { @@ -115039,6 +115940,7 @@ "reasoning": false, "tool_call": true, "temperature": true, + "knowledge": "2025-03", "release_date": "2025-06-20", "last_updated": "2026-03-17", "modalities": { @@ -115068,6 +115970,7 @@ "reasoning": false, "tool_call": true, "temperature": true, + "knowledge": "2024-09", "release_date": "2024-09-25", "last_updated": "2026-03-17", "modalities": { @@ -115093,10 +115996,11 @@ "id": "scaleway/qwen3-235b-a22b-instruct", "name": "Qwen3 235B A22B Instruct 2507", "family": "qwen", - "attachment": true, - "reasoning": false, + "attachment": false, + "reasoning": true, "tool_call": true, "temperature": true, + "knowledge": "2025-04", "release_date": "2025-07-01", "last_updated": "2026-03-17", "modalities": { @@ -115178,7 +116082,7 @@ "id": "scaleway/qwen3.5-397b-a17b", "name": "Qwen3.5 397B A17B", "family": "qwen", - "attachment": false, + "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, @@ -115205,6 +116109,36 @@ "output": 16384 } }, + { + "id": "scaleway/qwen3.6-35b-a3b", + "name": "Qwen3.6 35B A3B", + "family": "qwen", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-05-01", + "last_updated": "2026-05-22", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.25, + "output": 1.5 + }, + "limit": { + "context": 128000, + "output": 16384 + } + }, { "id": "scaleway/voxtral-small-24b", "name": "Voxtral Small 24B 2507", @@ -119928,6 +120862,35 @@ "output": 8192 } }, + { + "id": "stepfun-ai/step-3.5-flash", + "name": "Step 3.5 Flash 2603", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-04-02", + "last_updated": "2026-04-02", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.1, + "output": 0.3, + "cache_read": 0.02 + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, { "id": "stepfun/step-1-32k", "name": "Step 1 (32K)", @@ -121442,6 +122405,152 @@ "output": 64000 } }, + { + "id": "the-grid-ai/agent-max", + "name": "Agent Max", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-04", + "last_updated": "2026-05-19", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": {}, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + { + "id": "the-grid-ai/agent-prime", + "name": "Agent Prime", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-04", + "last_updated": "2026-05-19", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": {}, + "limit": { + "context": 128000, + "output": 64000 + } + }, + { + "id": "the-grid-ai/agent-standard", + "name": "Agent Standard", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-04", + "last_updated": "2026-05-19", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": {}, + "limit": { + "context": 128000, + "output": 16000 + } + }, + { + "id": "the-grid-ai/code-max", + "name": "Code Max", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-04", + "last_updated": "2026-05-19", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": {}, + "limit": { + "context": 1000000, + "output": 128000 + } + }, + { + "id": "the-grid-ai/code-prime", + "name": "Code Prime", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-04", + "last_updated": "2026-05-19", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": {}, + "limit": { + "context": 128000, + "output": 64000 + } + }, + { + "id": "the-grid-ai/code-standard", + "name": "Code Standard", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-04", + "last_updated": "2026-05-19", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": {}, + "limit": { + "context": 128000, + "output": 16000 + } + }, { "id": "the-grid-ai/text-max", "name": "Text Max", @@ -121450,10 +122559,11 @@ "tool_call": true, "temperature": true, "release_date": "2026-03-24", - "last_updated": "2026-03-24", + "last_updated": "2026-05-19", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" @@ -121474,7 +122584,7 @@ "tool_call": true, "temperature": true, "release_date": "2026-02-26", - "last_updated": "2026-02-26", + "last_updated": "2026-05-19", "modalities": { "input": [ "text" @@ -121498,7 +122608,7 @@ "tool_call": true, "temperature": true, "release_date": "2026-02-26", - "last_updated": "2026-02-26", + "last_updated": "2026-05-19", "modalities": { "input": [ "text" @@ -121716,6 +122826,34 @@ "output": 500000 } }, + { + "id": "togetherai/Qwen/Qwen3.7-Max", + "name": "Qwen3.7 Max", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-21", + "last_updated": "2026-05-21", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 2.5, + "output": 7.5 + }, + "limit": { + "context": 1000000, + "output": 500000 + } + }, { "id": "togetherai/deepseek-ai/DeepSeek-R1", "name": "DeepSeek R1", @@ -122803,6 +123941,39 @@ "output": 32768 } }, + { + "id": "venice/gemini-3.5-flash", + "name": "Gemini 3.5 Flash", + "family": "gemini-flash", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-22", + "last_updated": "2026-05-25", + "modalities": { + "input": [ + "text", + "image", + "audio", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.55, + "output": 9.45, + "cache_read": 0.155, + "cache_write": 0.086 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, { "id": "venice/gemma-4-uncensored", "name": "Gemma 4 Uncensored", @@ -122901,7 +124072,7 @@ "tool_call": true, "temperature": true, "release_date": "2026-04-03", - "last_updated": "2026-04-12", + "last_updated": "2026-05-25", "modalities": { "input": [ "text", @@ -122914,8 +124085,8 @@ }, "open_weights": true, "cost": { - "input": 0.175, - "output": 0.5 + "input": 0.155, + "output": 0.44 }, "limit": { "context": 256000, @@ -123013,16 +124184,15 @@ } }, { - "id": "venice/grok-41-fast", - "name": "Grok 4.1 Fast", - "family": "grok", + "id": "venice/grok-build-0.1", + "name": "Grok Build 0.1", + "family": "grok-build", "attachment": true, "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2025-07", - "release_date": "2025-12-01", - "last_updated": "2026-04-09", + "release_date": "2026-05-21", + "last_updated": "2026-05-22", "modalities": { "input": [ "text", @@ -123034,13 +124204,13 @@ }, "open_weights": false, "cost": { - "input": 0.23, - "output": 0.57, - "cache_read": 0.06 + "input": 1.0, + "output": 2.0, + "cache_read": 0.2 }, "limit": { - "context": 1000000, - "output": 30000 + "context": 256000, + "output": 65536 } }, { @@ -123778,6 +124948,36 @@ "output": 65536 } }, + { + "id": "venice/qwen-3.7-max", + "name": "Qwen 3.7 Max", + "family": "qwen", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-22", + "last_updated": "2026-05-25", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 2.7, + "output": 8.05, + "cache_read": 0.27, + "cache_write": 3.35 + }, + "limit": { + "context": 1000000, + "output": 65536 + } + }, { "id": "venice/qwen3-235b-a22b-instruct", "name": "Qwen 3 235B A22B Instruct 2507", @@ -123845,7 +125045,7 @@ "tool_call": true, "temperature": true, "release_date": "2026-02-25", - "last_updated": "2026-04-16", + "last_updated": "2026-05-25", "modalities": { "input": [ "text", @@ -123864,7 +125064,7 @@ }, "limit": { "context": 256000, - "output": 65536 + "output": 16384 } }, { @@ -125045,6 +126245,38 @@ "output": 64000 } }, + { + "id": "vercel/alibaba/qwen3.7-max", + "name": "Qwen 3.7 Max", + "family": "qwen", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-21", + "last_updated": "2026-05-21", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 2.5, + "output": 7.5, + "cache_read": 0.5, + "cache_write": 3.125 + }, + "limit": { + "context": 991000, + "output": 64000 + } + }, { "id": "vercel/amazon/nova-2-lite", "name": "Nova 2 Lite", @@ -126286,7 +127518,7 @@ }, { "id": "vercel/google/gemini-2.0-flash-lite", - "name": "Gemini 2.0 Flash Lite", + "name": "Gemini 2.0 Flash-Lite", "family": "gemini-flash-lite", "attachment": true, "reasoning": false, @@ -126768,6 +128000,37 @@ "output": 64000 } }, + { + "id": "vercel/google/gemini-3.5-flash", + "name": "Gemini 3.5 Flash", + "family": "gemini", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-19", + "last_updated": "2026-05-21", + "modalities": { + "input": [ + "text", + "image", + "pdf" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.5, + "output": 9.0, + "cache_read": 0.15 + }, + "limit": { + "context": 1000000, + "output": 64000 + } + }, { "id": "vercel/google/gemini-embedding-001", "name": "Gemini Embedding 001", @@ -128084,6 +129347,34 @@ "output": 64000 } }, + { + "id": "vercel/mistral/mistral-medium-3.5", + "name": "Mistral Medium Latest", + "family": "mistral-medium", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-21", + "last_updated": "2026-05-21", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.5, + "output": 7.5 + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, { "id": "vercel/mistral/mistral-nemo", "name": "Mistral Nemo", @@ -130473,37 +131764,6 @@ "output": 1536 } }, - { - "id": "vercel/xai/grok-2-vision", - "name": "Grok 2 Vision", - "family": "grok", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-08-20", - "last_updated": "2024-08-20", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 2.0, - "output": 10.0, - "cache_read": 2.0 - }, - "limit": { - "context": 8192, - "output": 4096 - } - }, { "id": "vercel/xai/grok-4-fast", "name": "Grok 4 Fast Reasoning", @@ -130807,6 +132067,36 @@ "output": 1000000 } }, + { + "id": "vercel/xai/grok-build-0.1", + "name": "Grok Build 0.1", + "family": "grok-build", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-05-20", + "last_updated": "2026-05-21", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": false, + "cost": { + "input": 1.0, + "output": 2.0, + "cache_read": 0.19999999999999998 + }, + "limit": { + "context": 256000, + "output": 256000 + } + }, { "id": "vercel/xai/grok-imagine-image", "name": "Grok Imagine Image", @@ -131413,9 +132703,9 @@ }, "open_weights": true, "cost": { - "input": 1.74, - "output": 3.48, - "cache_read": 0.145 + "input": 0.435, + "output": 0.87, + "cache_read": 0.003625 }, "limit": { "context": 1000000, @@ -131775,11 +133065,68 @@ } }, { - "id": "vultr/DeepSeek-V3.2", + "id": "vultr/MiniMaxAI/MiniMax-M2.7", + "name": "MiniMax-M2.7", + "family": "minimax", + "attachment": false, + "reasoning": true, + "tool_call": true, + "temperature": true, + "release_date": "2026-03-18", + "last_updated": "2026-03-18", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.3, + "output": 1.2 + }, + "limit": { + "context": 204800, + "output": 131072 + } + }, + { + "id": "vultr/moonshotai/Kimi-K2.6", + "name": "Kimi K2.6", + "family": "kimi-k2.6", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-04-21", + "last_updated": "2026-04-21", + "modalities": { + "input": [ + "text" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.15, + "output": 0.6 + }, + "limit": { + "context": 262144, + "output": 131072 + } + }, + { + "id": "vultr/nvidia/DeepSeek-V3.2-NVFP4", "name": "DeepSeek V3.2", "family": "deepseek", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, "knowledge": "2024-07", @@ -131799,21 +133146,21 @@ "output": 1.65 }, "limit": { - "context": 127000, - "output": 4096 + "context": 131072, + "output": 131072 } }, { - "id": "vultr/GLM-5-FP8", - "name": "GLM 5 FP8", - "family": "glm", + "id": "vultr/nvidia/Llama-3.1-Nemotron-Safety-Guard-8B-v3", + "name": "Llama 3.1 Nemotron Safety Guard", + "family": "llama", "attachment": false, "reasoning": false, - "tool_call": true, + "tool_call": false, "temperature": true, - "knowledge": "2025-05", - "release_date": "2026-02-11", - "last_updated": "2026-02-11", + "knowledge": "2023-12", + "release_date": "2025-10-28", + "last_updated": "2025-10-28", "modalities": { "input": [ "text" @@ -131824,25 +133171,25 @@ }, "open_weights": true, "cost": { - "input": 0.85, - "output": 3.1 + "input": 0.01, + "output": 0.01 }, "limit": { - "context": 200000, - "output": 131072 + "context": 8192, + "output": 4096 } }, { - "id": "vultr/Kimi-K2.5", - "name": "Kimi K2 Instruct", - "family": "kimi", + "id": "vultr/nvidia/Nemotron-3-Nano-Omni-30B-A3B-Reasoning-BF16", + "name": "NVIDIA Nemotron 3 Nano Omni", + "family": "nemotron", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-04", - "release_date": "2026-01-27", - "last_updated": "2026-01-27", + "knowledge": "2025-05", + "release_date": "2026-04-28", + "last_updated": "2026-04-28", "modalities": { "input": [ "text" @@ -131853,25 +133200,25 @@ }, "open_weights": true, "cost": { - "input": 0.55, - "output": 2.75 + "input": 0.13, + "output": 0.38 }, "limit": { - "context": 254000, - "output": 32768 + "context": 262144, + "output": 131072 } }, { - "id": "vultr/MiniMax-M2.5", - "name": "MiniMax M2.5", - "family": "minimax", + "id": "vultr/nvidia/Nemotron-Cascade-2-30B-A3B", + "name": "NVIDIA Nemotron Cascade 2", + "family": "nemotron", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-09", - "release_date": "2025-02-11", - "last_updated": "2025-02-11", + "knowledge": "2024-07", + "release_date": "2025-12-01", + "last_updated": "2025-12-01", "modalities": { "input": [ "text" @@ -131882,25 +133229,24 @@ }, "open_weights": true, "cost": { - "input": 0.3, - "output": 1.2 + "input": 0.15, + "output": 0.6 }, "limit": { - "context": 194000, - "output": 4096 + "context": 262144, + "output": 131072 } }, { - "id": "vultr/gpt-oss-120b", - "name": "GPT OSS 120B", - "family": "gpt-oss", + "id": "vultr/zai-org/GLM-5.1-FP8", + "name": "GLM-5.1", + "family": "glm", "attachment": false, - "reasoning": false, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-06", - "release_date": "2025-08-05", - "last_updated": "2025-08-05", + "release_date": "2026-03-27", + "last_updated": "2026-03-27", "modalities": { "input": [ "text" @@ -131909,14 +133255,14 @@ "text" ] }, - "open_weights": true, + "open_weights": false, "cost": { - "input": 0.15, - "output": 0.6 + "input": 0.85, + "output": 3.1 }, "limit": { - "context": 129000, - "output": 4096 + "context": 200000, + "output": 131072 } }, { @@ -131950,6 +133296,38 @@ "output": 131072 } }, + { + "id": "wafer.ai/Kimi-K2.6", + "name": "Kimi K2.6", + "family": "kimi", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-01", + "release_date": "2026-05-13", + "last_updated": "2026-05-13", + "modalities": { + "input": [ + "text", + "image" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 1.1, + "output": 4.8, + "cache_read": 0.11, + "cache_write": 0.0 + }, + "limit": { + "context": 262144, + "output": 65536 + } + }, { "id": "wafer.ai/Qwen3.5-397B-A17B", "name": "Qwen3.5 397B A17B", @@ -131983,6 +133361,39 @@ "output": 65536 } }, + { + "id": "wafer.ai/Qwen3.6-35B-A3B", + "name": "Qwen3.6 35B A3B", + "family": "qwen", + "attachment": true, + "reasoning": true, + "tool_call": true, + "temperature": true, + "knowledge": "2025-04", + "release_date": "2026-05-11", + "last_updated": "2026-05-11", + "modalities": { + "input": [ + "text", + "image", + "video" + ], + "output": [ + "text" + ] + }, + "open_weights": true, + "cost": { + "input": 0.19, + "output": 1.25, + "cache_read": 0.02, + "cache_write": 0.0 + }, + "limit": { + "context": 32768, + "output": 16384 + } + }, { "id": "wandb/MiniMaxAI/MiniMax-M2.5", "name": "MiniMax M2.5", @@ -132498,67 +133909,6 @@ "output": 131072 } }, - { - "id": "x-ai/grok-2", - "name": "Grok 2 Latest", - "family": "grok", - "attachment": false, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-08-20", - "last_updated": "2024-12-12", - "modalities": { - "input": [ - "text" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 2.0, - "output": 10.0, - "cache_read": 2.0 - }, - "limit": { - "context": 131072, - "output": 8192 - } - }, - { - "id": "x-ai/grok-2-vision", - "name": "Grok 2 Vision", - "family": "grok", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-08-20", - "last_updated": "2024-08-20", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 2.0, - "output": 10.0, - "cache_read": 2.0 - }, - "limit": { - "context": 8192, - "output": 4096 - } - }, { "id": "x-ai/grok-4.20", "name": "Grok 4.20 (Reasoning)", @@ -132572,7 +133922,8 @@ "modalities": { "input": [ "text", - "image" + "image", + "pdf" ], "output": [ "text" @@ -132580,8 +133931,8 @@ }, "open_weights": false, "cost": { - "input": 2.0, - "output": 6.0, + "input": 1.25, + "output": 2.5, "cache_read": 0.2 }, "limit": { @@ -132602,7 +133953,8 @@ "modalities": { "input": [ "text", - "image" + "image", + "pdf" ], "output": [ "text" @@ -132610,8 +133962,8 @@ }, "open_weights": false, "cost": { - "input": 2.0, - "output": 6.0, + "input": 1.25, + "output": 2.5, "cache_read": 0.2 }, "limit": { @@ -132632,7 +133984,8 @@ "modalities": { "input": [ "text", - "image" + "image", + "pdf" ], "output": [ "text" @@ -132640,8 +133993,8 @@ }, "open_weights": false, "cost": { - "input": 2.0, - "output": 6.0, + "input": 1.25, + "output": 2.5, "cache_read": 0.2 }, "limit": { @@ -132657,12 +134010,13 @@ "reasoning": true, "tool_call": true, "temperature": true, - "release_date": "2026-05-01", - "last_updated": "2026-05-01", + "release_date": "2026-04-17", + "last_updated": "2026-04-17", "modalities": { "input": [ "text", - "image" + "image", + "pdf" ], "output": [ "text" @@ -132680,19 +134034,20 @@ } }, { - "id": "x-ai/grok-beta", - "name": "Grok Beta", - "family": "grok-beta", - "attachment": false, - "reasoning": false, + "id": "x-ai/grok-build-0.1", + "name": "Grok Build 0.1", + "family": "grok-build", + "attachment": true, + "reasoning": true, "tool_call": true, "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-11-01", - "last_updated": "2024-11-01", + "release_date": "2026-04-16", + "last_updated": "2026-04-16", "modalities": { "input": [ - "text" + "text", + "image", + "pdf" ], "output": [ "text" @@ -132700,13 +134055,13 @@ }, "open_weights": false, "cost": { - "input": 5.0, - "output": 15.0, - "cache_read": 5.0 + "input": 1.0, + "output": 2.0, + "cache_read": 0.2 }, "limit": { - "context": 131072, - "output": 4096 + "context": 256000, + "output": 256000 } }, { @@ -132717,21 +134072,23 @@ "reasoning": false, "tool_call": false, "temperature": false, - "release_date": "2026-03", - "last_updated": "2026-05-16", + "release_date": "2026-01-28", + "last_updated": "2026-01-28", "modalities": { "input": [ "text", - "image" + "image", + "pdf" ], "output": [ - "image" + "image", + "pdf" ] }, "open_weights": false, "cost": {}, "limit": { - "context": 1024, + "context": 8000, "output": 0 } }, @@ -132743,21 +134100,23 @@ "reasoning": false, "tool_call": false, "temperature": false, - "release_date": "2026-04", - "last_updated": "2026-05-16", + "release_date": "2026-04-03", + "last_updated": "2026-04-03", "modalities": { "input": [ "text", - "image" + "image", + "pdf" ], "output": [ - "image" + "image", + "pdf" ] }, "open_weights": false, "cost": {}, "limit": { - "context": 1024, + "context": 8000, "output": 0 } }, @@ -132769,13 +134128,13 @@ "reasoning": false, "tool_call": false, "temperature": false, - "release_date": "2026-03", - "last_updated": "2026-05-16", + "release_date": "2026-01-28", + "last_updated": "2026-01-28", "modalities": { "input": [ "text", "image", - "video" + "pdf" ], "output": [ "video" @@ -132788,37 +134147,6 @@ "output": 0 } }, - { - "id": "x-ai/grok-vision-beta", - "name": "Grok Vision Beta", - "family": "grok-vision", - "attachment": true, - "reasoning": false, - "tool_call": true, - "temperature": true, - "knowledge": "2024-08", - "release_date": "2024-11-01", - "last_updated": "2024-11-01", - "modalities": { - "input": [ - "text", - "image" - ], - "output": [ - "text" - ] - }, - "open_weights": false, - "cost": { - "input": 5.0, - "output": 15.0, - "cache_read": 5.0 - }, - "limit": { - "context": 8192, - "output": 4096 - } - }, { "id": "xiaomi-token-plan-ams/mimo-v2-flash", "name": "MiMo-V2-Flash", @@ -133537,10 +134865,11 @@ "temperature": false, "knowledge": "2025-12-30", "release_date": "2026-05-01", - "last_updated": "2026-05-15", + "last_updated": "2026-05-25", "modalities": { "input": [ - "text" + "text", + "image" ], "output": [ "text" @@ -133553,8 +134882,8 @@ "cache_read": 0.15 }, "limit": { - "context": 400000, - "output": 128000 + "context": 1000000, + "output": 384000 } }, { @@ -134600,7 +135929,7 @@ "cost": { "input": 0.14, "output": 0.28, - "cache_read": 0.028 + "cache_read": 0.0028 }, "limit": { "context": 1000000, @@ -134628,9 +135957,9 @@ }, "open_weights": true, "cost": { - "input": 1.74, - "output": 3.48, - "cache_read": 0.145 + "input": 0.435, + "output": 0.87, + "cache_read": 0.003625 }, "limit": { "context": 1000000, diff --git a/crates/goose/src/providers/canonical/data/provider_metadata.json b/crates/goose/src/providers/canonical/data/provider_metadata.json index c19ab1449c78..d16fc1bf045f 100644 --- a/crates/goose/src/providers/canonical/data/provider_metadata.json +++ b/crates/goose/src/providers/canonical/data/provider_metadata.json @@ -164,6 +164,17 @@ ], "model_count": 8 }, + { + "id": "crof", + "display_name": "CrofAI", + "npm": "@ai-sdk/openai-compatible", + "api": "https://crof.ai/v1", + "doc": "https://crof.ai/docs", + "env": [ + "CROF_API_KEY" + ], + "model_count": 21 + }, { "id": "ambient", "display_name": "Ambient", @@ -195,7 +206,7 @@ "env": [ "THEGRIDAI_API_KEY" ], - "model_count": 3 + "model_count": 9 }, { "id": "fastrouter", @@ -306,7 +317,7 @@ "CLOUDFLARE_ACCOUNT_ID", "CLOUDFLARE_API_KEY" ], - "model_count": 8 + "model_count": 27 }, { "id": "lmstudio", @@ -350,7 +361,7 @@ "env": [ "NEARAI_API_KEY" ], - "model_count": 33 + "model_count": 37 }, { "id": "abacus", @@ -439,7 +450,7 @@ "env": [ "VULTR_API_KEY" ], - "model_count": 5 + "model_count": 7 }, { "id": "kuae-cloud-coding-plan", @@ -582,7 +593,7 @@ "env": [ "LLMGATEWAY_API_KEY" ], - "model_count": 189 + "model_count": 188 }, { "id": "poe", @@ -593,7 +604,7 @@ "env": [ "POE_API_KEY" ], - "model_count": 135 + "model_count": 136 }, { "id": "minimax", @@ -682,7 +693,7 @@ "env": [ "ALIBABA_CODING_PLAN_API_KEY" ], - "model_count": 9 + "model_count": 11 }, { "id": "minimax-cn", @@ -792,7 +803,7 @@ "env": [ "OPENCODE_API_KEY" ], - "model_count": 60 + "model_count": 62 }, { "id": "mixlayer", @@ -836,7 +847,7 @@ "env": [ "ALIBABA_CODING_PLAN_API_KEY" ], - "model_count": 9 + "model_count": 11 }, { "id": "meganova", @@ -860,6 +871,17 @@ ], "model_count": 33 }, + { + "id": "inceptron", + "display_name": "Inceptron", + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.inceptron.io/v1", + "doc": "https://docs.inceptron.io", + "env": [ + "INCEPTRON_API_KEY" + ], + "model_count": 4 + }, { "id": "minimax-coding-plan", "display_name": "MiniMax Token Plan (minimax.io)", @@ -935,7 +957,7 @@ "env": [ "WAFER_API_KEY" ], - "model_count": 2 + "model_count": 4 }, { "id": "evroc", @@ -968,7 +990,7 @@ "env": [ "FIREWORKS_API_KEY" ], - "model_count": 20 + "model_count": 12 }, { "id": "alibaba", @@ -979,7 +1001,7 @@ "env": [ "DASHSCOPE_API_KEY" ], - "model_count": 48 + "model_count": 50 }, { "id": "302ai", @@ -1078,7 +1100,7 @@ "env": [ "DASHSCOPE_API_KEY" ], - "model_count": 80 + "model_count": 82 }, { "id": "drun", @@ -1166,7 +1188,18 @@ "env": [ "GITHUB_TOKEN" ], - "model_count": 27 + "model_count": 28 + }, + { + "id": "stepfun-ai", + "display_name": "StepFun", + "npm": "@ai-sdk/openai-compatible", + "api": "https://api.stepfun.ai/step_plan/v1", + "doc": "https://platform.stepfun.ai/docs/en/step-plan/integrations/open-code", + "env": [ + "STEPFUN_API_KEY" + ], + "model_count": 2 }, { "id": "inference", @@ -1178,5 +1211,16 @@ "INFERENCE_API_KEY" ], "model_count": 9 + }, + { + "id": "poolside", + "display_name": "Poolside", + "npm": "@ai-sdk/openai-compatible", + "api": "https://inference.poolside.ai/v1", + "doc": "https://platform.poolside.ai", + "env": [ + "POOLSIDE_API_KEY" + ], + "model_count": 2 } ] \ No newline at end of file diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index f2ac75833f65..7e2f3bcd321c 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -10,7 +10,7 @@ "license": { "name": "Apache-2.0" }, - "version": "1.35.0" + "version": "1.36.0" }, "paths": { "/action-required/tool-confirmation": { diff --git a/ui/desktop/package.json b/ui/desktop/package.json index d8aefa6dbd65..629bdd4992c5 100644 --- a/ui/desktop/package.json +++ b/ui/desktop/package.json @@ -1,7 +1,7 @@ { "name": "goose-app", "productName": "Goose", - "version": "1.35.0", + "version": "1.36.0", "description": "Goose App", "engines": { "node": "^24.10.0", From 7dc904e1e2389ed376b2357534b86a981da73be4 Mon Sep 17 00:00:00 2001 From: Bradley Axen Date: Tue, 26 May 2026 15:45:01 -0700 Subject: [PATCH 14/66] add databricks ai gateway provider (#9274) Signed-off-by: Bradley Axen --- crates/goose/src/providers/catalog.rs | 30 ++ crates/goose/src/providers/databricks.rs | 72 +-- crates/goose/src/providers/databricks_auth.rs | 70 +++ crates/goose/src/providers/databricks_v2.rs | 499 ++++++++++++++++++ crates/goose/src/providers/init.rs | 6 + crates/goose/src/providers/mod.rs | 2 + ui/desktop/src/utils/configUtils.ts | 1 + 7 files changed, 610 insertions(+), 70 deletions(-) create mode 100644 crates/goose/src/providers/databricks_auth.rs create mode 100644 crates/goose/src/providers/databricks_v2.rs diff --git a/crates/goose/src/providers/catalog.rs b/crates/goose/src/providers/catalog.rs index e1dfe6d0f526..2cfe698de5d5 100644 --- a/crates/goose/src/providers/catalog.rs +++ b/crates/goose/src/providers/catalog.rs @@ -495,6 +495,36 @@ const SETUP_METADATA: &[CuratedSetupMetadata] = &[ }, ], }, + CuratedSetupMetadata { + provider_id: "databricks_v2", + category: ProviderSetupCategory::Model, + setup_method: ProviderSetupMethod::HostWithOauthFallback, + group: ProviderSetupGroup::Additional, + display_name: Some("Databricks AI Gateway"), + description: Some("Models on Databricks AI Gateway v2"), + docs_url: Some("https://docs.databricks.com/en/generative-ai/ai-gateway/"), + aliases: &["databricks_ai_gateway"], + native_connect_query: None, + binary_name: None, + setup_capabilities: setup_capabilities(false, true, false), + show_only_when_installed: false, + synthetic: false, + secret_field_default: None, + field_overrides: &[ + CuratedFieldMetadata { + key: "DATABRICKS_HOST", + label: "Host URL", + placeholder: Some("https://dbc-...cloud.databricks.com"), + default_value: None, + }, + CuratedFieldMetadata { + key: "DATABRICKS_TOKEN", + label: "Access Token", + placeholder: Some("Paste your access token"), + default_value: None, + }, + ], + }, CuratedSetupMetadata { provider_id: "github_copilot", category: ProviderSetupCategory::Model, diff --git a/crates/goose/src/providers/databricks.rs b/crates/goose/src/providers/databricks.rs index 0eff8b1963d3..76b9bc45942a 100644 --- a/crates/goose/src/providers/databricks.rs +++ b/crates/goose/src/providers/databricks.rs @@ -1,23 +1,22 @@ use anyhow::Result; use async_trait::async_trait; use futures::future::BoxFuture; -use serde::{Deserialize, Serialize}; use serde_json::Value; use std::collections::HashSet; use std::sync::LazyLock; use std::sync::{Arc, Mutex}; use std::time::{Duration, Instant}; -use super::api_client::{ApiClient, AuthMethod, AuthProvider}; +use super::api_client::{ApiClient, AuthMethod}; use super::base::{ ConfigKey, MessageStream, ModelInfo, Provider, ProviderDef, ProviderMetadata, DEFAULT_PROVIDER_TIMEOUT_SECS, }; +use super::databricks_auth::{DatabricksAuth, DatabricksAuthProvider}; use super::embedding::EmbeddingCapable; use super::errors::ProviderError; use super::formats::databricks::create_request; use super::formats::openai_responses::create_responses_request; -use super::oauth; use super::openai_compatible::{ handle_response_openai_compat, handle_status, map_http_error_to_provider_error, sanitize_url, stream_openai_compat, stream_responses_compat, @@ -55,10 +54,6 @@ struct CachedDatabricksEndpointInfo { fetched_at: Instant, } -const DEFAULT_CLIENT_ID: &str = "databricks-cli"; -const DEFAULT_REDIRECT_URL: &str = "http://localhost"; -const DEFAULT_SCOPES: &[&str] = &["all-apis", "offline_access"]; - const DATABRICKS_PROVIDER_NAME: &str = "databricks"; const DATABRICKS_ENDPOINT_METADATA_TTL_SECS: u64 = 60; static DATABRICKS_ENDPOINT_INFO_CACHE: LazyLock< @@ -75,69 +70,6 @@ pub const DATABRICKS_KNOWN_MODELS: &[&str] = &[ pub const DATABRICKS_DOC_URL: &str = "https://docs.databricks.com/en/generative-ai/external-models/index.html"; -#[derive(Debug, Clone, Serialize, Deserialize)] -pub enum DatabricksAuth { - Token(String), - OAuth { - host: String, - client_id: String, - redirect_url: String, - scopes: Vec, - }, -} - -impl DatabricksAuth { - pub fn oauth(host: String) -> Self { - Self::OAuth { - host, - client_id: DEFAULT_CLIENT_ID.to_string(), - redirect_url: DEFAULT_REDIRECT_URL.to_string(), - scopes: DEFAULT_SCOPES.iter().map(|s| s.to_string()).collect(), - } - } - - pub fn token(token: String) -> Self { - Self::Token(token) - } -} - -struct DatabricksAuthProvider { - auth: DatabricksAuth, - token_cache: Arc>>, -} - -#[async_trait] -impl AuthProvider for DatabricksAuthProvider { - async fn get_auth_header(&self) -> Result<(String, String)> { - let token = match &self.auth { - DatabricksAuth::Token(original) => { - let cached = self.token_cache.lock().unwrap().clone(); - match cached { - Some(t) => t, - None => { - // Cache was cleared by refresh_credentials(); re-read - // from config which may have a sidecar-rotated token. - // Fall back to the constructor-provided token if config - // lookup fails (e.g. from_params usage). - let fresh = crate::config::Config::global() - .get_secret::("DATABRICKS_TOKEN") - .unwrap_or_else(|_| original.clone()); - *self.token_cache.lock().unwrap() = Some(fresh.clone()); - fresh - } - } - } - DatabricksAuth::OAuth { - host, - client_id, - redirect_url, - scopes, - } => oauth::get_oauth_token_async(host, client_id, redirect_url, scopes).await?, - }; - Ok(("Authorization".to_string(), format!("Bearer {}", token))) - } -} - #[derive(Debug, serde::Serialize)] pub struct DatabricksProvider { #[serde(skip)] diff --git a/crates/goose/src/providers/databricks_auth.rs b/crates/goose/src/providers/databricks_auth.rs new file mode 100644 index 000000000000..e2795e0ef501 --- /dev/null +++ b/crates/goose/src/providers/databricks_auth.rs @@ -0,0 +1,70 @@ +use anyhow::Result; +use async_trait::async_trait; +use serde::{Deserialize, Serialize}; +use std::sync::{Arc, Mutex}; + +use super::api_client::AuthProvider; +use super::oauth; + +const DEFAULT_CLIENT_ID: &str = "databricks-cli"; +const DEFAULT_REDIRECT_URL: &str = "http://localhost"; +const DEFAULT_SCOPES: &[&str] = &["all-apis", "offline_access"]; + +#[derive(Debug, Clone, Serialize, Deserialize)] +pub enum DatabricksAuth { + Token(String), + OAuth { + host: String, + client_id: String, + redirect_url: String, + scopes: Vec, + }, +} + +impl DatabricksAuth { + pub fn oauth(host: String) -> Self { + Self::OAuth { + host, + client_id: DEFAULT_CLIENT_ID.to_string(), + redirect_url: DEFAULT_REDIRECT_URL.to_string(), + scopes: DEFAULT_SCOPES.iter().map(|s| s.to_string()).collect(), + } + } + + pub fn token(token: String) -> Self { + Self::Token(token) + } +} + +pub(crate) struct DatabricksAuthProvider { + pub auth: DatabricksAuth, + pub token_cache: Arc>>, +} + +#[async_trait] +impl AuthProvider for DatabricksAuthProvider { + async fn get_auth_header(&self) -> Result<(String, String)> { + let token = match &self.auth { + DatabricksAuth::Token(original) => { + let cached = self.token_cache.lock().unwrap().clone(); + match cached { + Some(t) => t, + None => { + let fresh = crate::config::Config::global() + .get_secret::("DATABRICKS_TOKEN") + .unwrap_or_else(|_| original.clone()); + *self.token_cache.lock().unwrap() = Some(fresh.clone()); + fresh + } + } + } + DatabricksAuth::OAuth { + host, + client_id, + redirect_url, + scopes, + } => oauth::get_oauth_token_async(host, client_id, redirect_url, scopes).await?, + }; + Ok(("Authorization".to_string(), format!("Bearer {}", token))) + } +} diff --git a/crates/goose/src/providers/databricks_v2.rs b/crates/goose/src/providers/databricks_v2.rs new file mode 100644 index 000000000000..2385ae9bde8c --- /dev/null +++ b/crates/goose/src/providers/databricks_v2.rs @@ -0,0 +1,499 @@ +use anyhow::Result; +use async_stream::try_stream; +use async_trait::async_trait; +use futures::future::BoxFuture; +use futures::TryStreamExt; +use serde::Serialize; +use serde_json::Value; +use std::io; +use std::sync::{Arc, Mutex}; +use std::time::Duration; +use tokio::pin; +use tokio_util::io::StreamReader; + +use super::api_client::{ApiClient, AuthMethod}; +use super::base::{ + ConfigKey, MessageStream, Provider, ProviderDef, ProviderMetadata, + DEFAULT_PROVIDER_TIMEOUT_SECS, +}; +use super::databricks_auth::{DatabricksAuth, DatabricksAuthProvider}; +use super::errors::ProviderError; +use super::formats::{anthropic, openai, openai_responses}; +use super::openai_compatible::{handle_status, stream_openai_compat, stream_responses_compat}; +use super::retry::ProviderRetry; +use super::utils::{extract_reasoning_effort, is_openai_responses_model, ImageFormat, RequestLog}; +use crate::config::ConfigError; +use crate::conversation::message::Message; +use crate::model::ModelConfig; +use crate::providers::retry::{ + RetryConfig, DEFAULT_BACKOFF_MULTIPLIER, DEFAULT_INITIAL_RETRY_INTERVAL_MS, + DEFAULT_MAX_RETRIES, DEFAULT_MAX_RETRY_INTERVAL_MS, +}; +use rmcp::model::Tool; + +const DATABRICKS_V2_PROVIDER_NAME: &str = "databricks_v2"; +const DATABRICKS_V2_LIST_ENDPOINTS_PATH: &str = "api/ai-gateway/v2/endpoints"; +pub const DATABRICKS_V2_DEFAULT_MODEL: &str = "databricks-gpt-5-5"; +pub const DATABRICKS_V2_KNOWN_MODELS: &[&str] = + &["databricks-gpt-5-5", "databricks-claude-opus-4-7"]; + +pub const DATABRICKS_V2_DOC_URL: &str = "https://docs.databricks.com/en/generative-ai/ai-gateway/"; + +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +enum DatabricksV2Route { + OpenAiResponses, + AnthropicMessages, + MlflowChatCompletions, +} + +#[derive(Debug, Serialize)] +pub struct DatabricksV2Provider { + #[serde(skip)] + api_client: ApiClient, + model: ModelConfig, + #[serde(skip)] + retry_config: RetryConfig, + #[serde(skip)] + name: String, + #[serde(skip)] + token_cache: Arc>>, +} + +impl DatabricksV2Provider { + pub async fn cleanup() -> Result<()> { + super::oauth::cleanup_oauth_cache() + } + + pub async fn from_env(model: ModelConfig) -> Result { + let config = crate::config::Config::global(); + + let mut host: Result = config.get_param("DATABRICKS_HOST"); + if host.is_err() { + host = config.get_secret("DATABRICKS_HOST") + } + + if host.is_err() { + return Err(ConfigError::NotFound( + "Did not find DATABRICKS_HOST in either config file or keyring".to_string(), + ) + .into()); + } + + let host = host?; + let retry_config = Self::load_retry_config(config); + + let auth = if let Ok(api_key) = config.get_secret("DATABRICKS_TOKEN") { + DatabricksAuth::token(api_key) + } else { + DatabricksAuth::oauth(host.clone()) + }; + + Self::new(host, auth, model, retry_config) + } + + pub fn from_params(host: String, api_key: String, model: ModelConfig) -> Result { + Self::new( + host, + DatabricksAuth::token(api_key), + model, + RetryConfig::default(), + ) + } + + fn new( + host: String, + auth: DatabricksAuth, + model: ModelConfig, + retry_config: RetryConfig, + ) -> Result { + let token_cache = Arc::new(Mutex::new(match &auth { + DatabricksAuth::Token(t) => Some(t.clone()), + _ => None, + })); + + let auth_method = AuthMethod::Custom(Box::new(DatabricksAuthProvider { + auth: auth.clone(), + token_cache: token_cache.clone(), + })); + + let api_client = ApiClient::with_timeout( + host, + auth_method, + Duration::from_secs(DEFAULT_PROVIDER_TIMEOUT_SECS), + )?; + + Ok(Self { + api_client, + model, + retry_config, + name: DATABRICKS_V2_PROVIDER_NAME.to_string(), + token_cache, + }) + } + + fn load_retry_config(config: &crate::config::Config) -> RetryConfig { + let max_retries = config + .get_param("DATABRICKS_MAX_RETRIES") + .ok() + .and_then(|v: String| v.parse::().ok()) + .unwrap_or(DEFAULT_MAX_RETRIES); + + let initial_interval_ms = config + .get_param("DATABRICKS_INITIAL_RETRY_INTERVAL_MS") + .ok() + .and_then(|v: String| v.parse::().ok()) + .unwrap_or(DEFAULT_INITIAL_RETRY_INTERVAL_MS); + + let backoff_multiplier = config + .get_param("DATABRICKS_BACKOFF_MULTIPLIER") + .ok() + .and_then(|v: String| v.parse::().ok()) + .unwrap_or(DEFAULT_BACKOFF_MULTIPLIER); + + let max_interval_ms = config + .get_param("DATABRICKS_MAX_RETRY_INTERVAL_MS") + .ok() + .and_then(|v: String| v.parse::().ok()) + .unwrap_or(DEFAULT_MAX_RETRY_INTERVAL_MS); + + RetryConfig::new( + max_retries, + initial_interval_ms, + backoff_multiplier, + max_interval_ms, + ) + } + + fn route_for_model(model_name: &str) -> DatabricksV2Route { + let (clean_name, _) = extract_reasoning_effort(model_name); + let lower = clean_name.to_lowercase(); + + if is_openai_responses_model(&clean_name) || Self::looks_like_gpt5(&lower) { + DatabricksV2Route::OpenAiResponses + } else if Self::is_claude_model(&lower) { + DatabricksV2Route::AnthropicMessages + } else { + DatabricksV2Route::MlflowChatCompletions + } + } + + fn looks_like_gpt5(model_name: &str) -> bool { + model_name.contains("gpt-5") || model_name.contains("gpt5") + } + + fn is_claude_model(model_name: &str) -> bool { + model_name.contains("claude") + } + + fn parse_list_endpoints_response(json: &Value) -> Result, ProviderError> { + let endpoints = json + .get("endpoints") + .and_then(|v| v.as_array()) + .ok_or_else(|| { + ProviderError::RequestFailed( + "Unexpected response format from Databricks AI Gateway endpoints API" + .to_string(), + ) + })?; + + let mut models: Vec = endpoints + .iter() + .filter_map(|endpoint| { + endpoint + .get("name") + .and_then(|v| v.as_str()) + .map(str::to_string) + }) + .collect(); + models.sort(); + Ok(models) + } + + async fn stream_openai_responses( + &self, + model_config: &ModelConfig, + session_id: &str, + system: &str, + messages: &[Message], + tools: &[Tool], + ) -> Result { + let mut payload = + openai_responses::create_responses_request(model_config, system, messages, tools)?; + payload["stream"] = Value::Bool(true); + let mut log = RequestLog::start(model_config, &payload)?; + + let response = self + .with_retry(|| async { + let resp = self + .api_client + .response_post(Some(session_id), "ai-gateway/openai/v1/responses", &payload) + .await?; + handle_status(resp).await + }) + .await + .inspect_err(|e| { + let _ = log.error(e); + })?; + + stream_responses_compat(response, log) + } + + async fn stream_mlflow_chat_completions( + &self, + model_config: &ModelConfig, + session_id: &str, + system: &str, + messages: &[Message], + tools: &[Tool], + ) -> Result { + let mut payload = openai::create_request( + model_config, + system, + messages, + tools, + &ImageFormat::OpenAi, + true, + )?; + if payload.get("max_tokens").is_none() { + payload["max_tokens"] = Value::from(model_config.max_output_tokens()); + } + let mut log = RequestLog::start(model_config, &payload)?; + + let response = self + .with_retry(|| async { + let resp = self + .api_client + .response_post( + Some(session_id), + "ai-gateway/mlflow/v1/chat/completions", + &payload, + ) + .await?; + handle_status(resp).await + }) + .await + .inspect_err(|e| { + let _ = log.error(e); + })?; + + stream_openai_compat(response, log) + } + + async fn stream_anthropic_messages( + &self, + model_config: &ModelConfig, + session_id: &str, + system: &str, + messages: &[Message], + tools: &[Tool], + ) -> Result { + let mut payload = anthropic::create_request(model_config, system, messages, tools)?; + payload["stream"] = Value::Bool(true); + let mut log = RequestLog::start(model_config, &payload)?; + + let response = self + .with_retry(|| async { + let resp = self + .api_client + .response_post( + Some(session_id), + "ai-gateway/anthropic/v1/messages", + &payload, + ) + .await?; + handle_status(resp).await + }) + .await + .inspect_err(|e| { + let _ = log.error(e); + })?; + + let stream = response.bytes_stream().map_err(io::Error::other); + + Ok(Box::pin(try_stream! { + let stream_reader = StreamReader::new(stream); + let framed = tokio_util::codec::FramedRead::new(stream_reader, tokio_util::codec::LinesCodec::new()) + .map_err(anyhow::Error::from); + + let message_stream = anthropic::response_to_streaming_message(framed); + pin!(message_stream); + while let Some(message) = futures::StreamExt::next(&mut message_stream).await { + let (message, usage) = message.map_err(|e| ProviderError::RequestFailed(format!("Stream decode error: {e}")))?; + log.write(&message, usage.as_ref().map(|f| f.usage).as_ref())?; + yield (message, usage); + } + })) + } +} + +impl ProviderDef for DatabricksV2Provider { + type Provider = Self; + + fn metadata() -> ProviderMetadata { + ProviderMetadata::new( + DATABRICKS_V2_PROVIDER_NAME, + "Databricks AI Gateway", + "Models on Databricks AI Gateway v2", + DATABRICKS_V2_DEFAULT_MODEL, + DATABRICKS_V2_KNOWN_MODELS.to_vec(), + DATABRICKS_V2_DOC_URL, + vec![ + ConfigKey::new("DATABRICKS_HOST", true, false, None, true), + ConfigKey::new("DATABRICKS_TOKEN", false, true, None, true), + ], + ) + } + + fn from_env( + model: ModelConfig, + _extensions: Vec, + ) -> BoxFuture<'static, Result> { + Box::pin(Self::from_env(model)) + } + + fn supports_inventory_refresh() -> bool { + true + } +} + +#[async_trait] +impl Provider for DatabricksV2Provider { + fn get_name(&self) -> &str { + &self.name + } + + fn retry_config(&self) -> RetryConfig { + self.retry_config.clone() + } + + async fn refresh_credentials(&self) -> Result<(), ProviderError> { + crate::config::Config::global().invalidate_secrets_cache(); + *self.token_cache.lock().unwrap() = None; + Ok(()) + } + + fn get_model_config(&self) -> ModelConfig { + self.model.clone() + } + + async fn stream( + &self, + model_config: &ModelConfig, + session_id: &str, + system: &str, + messages: &[Message], + tools: &[Tool], + ) -> Result { + match Self::route_for_model(&model_config.model_name) { + DatabricksV2Route::OpenAiResponses => { + self.stream_openai_responses(model_config, session_id, system, messages, tools) + .await + } + DatabricksV2Route::AnthropicMessages => { + self.stream_anthropic_messages(model_config, session_id, system, messages, tools) + .await + } + DatabricksV2Route::MlflowChatCompletions => { + self.stream_mlflow_chat_completions( + model_config, + session_id, + system, + messages, + tools, + ) + .await + } + } + } + + async fn fetch_supported_models(&self) -> Result, ProviderError> { + let response = self + .api_client + .response_get(None, DATABRICKS_V2_LIST_ENDPOINTS_PATH) + .await + .map_err(|e| { + ProviderError::RequestFailed(format!( + "Failed to fetch Databricks AI Gateway endpoints: {e}" + )) + })?; + + if !response.status().is_success() { + let status = response.status(); + let detail = response.text().await.unwrap_or_default(); + return Err(ProviderError::RequestFailed(format!( + "Failed to fetch Databricks AI Gateway endpoints: {status} {detail}" + ))); + } + + let json: Value = response.json().await.map_err(|e| { + ProviderError::RequestFailed(format!( + "Failed to parse Databricks AI Gateway endpoints response: {e}" + )) + })?; + + Self::parse_list_endpoints_response(&json) + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn routes_known_model_families() { + for model in ["databricks-gpt-5-5", "databricks-gpt5"] { + assert_eq!( + DatabricksV2Provider::route_for_model(model), + DatabricksV2Route::OpenAiResponses, + "unexpected route for {model}" + ); + } + + for model in ["databricks-claude-opus-4-7", "databricks-claude-sonnet-4-6"] { + assert_eq!( + DatabricksV2Provider::route_for_model(model), + DatabricksV2Route::AnthropicMessages, + "unexpected route for {model}" + ); + } + + assert_eq!( + DatabricksV2Provider::route_for_model("custom-model"), + DatabricksV2Route::MlflowChatCompletions + ); + } + + #[test] + fn parses_list_endpoints_response() { + let json = serde_json::json!({ + "endpoints": [ + {"name": "databricks-claude-opus-4-7"}, + {"name": "databricks-gpt-5-5"}, + {"name": "custom-model"} + ] + }); + + let models = DatabricksV2Provider::parse_list_endpoints_response(&json).unwrap(); + + assert_eq!( + models, + vec![ + "custom-model".to_string(), + "databricks-claude-opus-4-7".to_string(), + "databricks-gpt-5-5".to_string(), + ] + ); + } + + #[test] + fn errors_when_list_endpoints_response_has_no_endpoints_array() { + let json = serde_json::json!({"data": []}); + + let error = DatabricksV2Provider::parse_list_endpoints_response(&json).unwrap_err(); + + assert!(matches!(error, ProviderError::RequestFailed(_))); + assert!(error + .to_string() + .contains("Unexpected response format from Databricks AI Gateway endpoints API")); + } +} diff --git a/crates/goose/src/providers/init.rs b/crates/goose/src/providers/init.rs index 24364399ccf1..cc4c20b6d29a 100644 --- a/crates/goose/src/providers/init.rs +++ b/crates/goose/src/providers/init.rs @@ -21,6 +21,7 @@ use super::{ copilot_acp::CopilotAcpProvider, cursor_agent::CursorAgentProvider, databricks::DatabricksProvider, + databricks_v2::DatabricksV2Provider, gcpvertexai::GcpVertexAIProvider, gemini_cli::GeminiCliProvider, gemini_oauth::GeminiOAuthProvider, @@ -68,6 +69,7 @@ async fn init_registry() -> RwLock { registry.register::(true); registry.register::(false); registry.register::(true); + registry.register::(false); registry.register::(false); registry.register::(false); registry.register::(true); @@ -95,6 +97,10 @@ async fn init_registry() -> RwLock { "databricks", Arc::new(|| Box::pin(DatabricksProvider::cleanup())), ); + registry.set_cleanup( + "databricks_v2", + Arc::new(|| Box::pin(DatabricksV2Provider::cleanup())), + ); registry.set_cleanup( "kimi_code", Arc::new(|| Box::pin(KimiCodeProvider::cleanup())), diff --git a/crates/goose/src/providers/mod.rs b/crates/goose/src/providers/mod.rs index 44f7dd0a96a5..f5e4f952001d 100644 --- a/crates/goose/src/providers/mod.rs +++ b/crates/goose/src/providers/mod.rs @@ -19,6 +19,8 @@ pub mod codex_acp; pub mod copilot_acp; pub mod cursor_agent; pub mod databricks; +pub mod databricks_auth; +pub mod databricks_v2; pub mod embedding; pub mod errors; pub mod formats; diff --git a/ui/desktop/src/utils/configUtils.ts b/ui/desktop/src/utils/configUtils.ts index 9e0a5e556cd6..58f866e552fd 100644 --- a/ui/desktop/src/utils/configUtils.ts +++ b/ui/desktop/src/utils/configUtils.ts @@ -79,6 +79,7 @@ export const providerPrefixes: Record = { google: ['GOOGLE_'], groq: ['GROQ_'], databricks: ['DATABRICKS_'], + databricks_v2: ['DATABRICKS_'], openrouter: ['OPENROUTER_'], ollama: ['OLLAMA_'], azure_openai: ['AZURE_'], From 17493540e1e5cd1f0849994f0e14bb5f1a716a8d Mon Sep 17 00:00:00 2001 From: Bradley Axen Date: Tue, 26 May 2026 18:32:32 -0700 Subject: [PATCH 15/66] Prefer goose aliases for Databricks v2 inventory (#9430) Signed-off-by: Bradley Axen --- crates/goose/src/providers/inventory/mod.rs | 24 +++++++++++++++++++-- 1 file changed, 22 insertions(+), 2 deletions(-) diff --git a/crates/goose/src/providers/inventory/mod.rs b/crates/goose/src/providers/inventory/mod.rs index 3b76a59c4f7b..dae24e7bb0fe 100644 --- a/crates/goose/src/providers/inventory/mod.rs +++ b/crates/goose/src/providers/inventory/mod.rs @@ -881,10 +881,10 @@ fn enrich_model_ids_with_canonical( models.push(model); } - // For databricks, prefer goose- prefixed model_ids when there are duplicates. + // For Databricks providers, prefer goose- prefixed model_ids when there are duplicates. // Re-scan: if a later model_id with "goose-" prefix maps to the same display name, // swap it in. - if provider_family == "databricks" { + if matches!(provider_family, "databricks" | "databricks_v2") { let mut name_to_idx: HashMap = HashMap::new(); for (idx, model) in models.iter().enumerate() { name_to_idx.insert(model.name.clone(), idx); @@ -1178,6 +1178,26 @@ mod tests { assert!(models[0].name.contains("Claude")); } + #[test] + fn databricks_v2_inventory_prefers_goose_model_ids_for_duplicate_names() { + let models = enrich_model_ids_with_canonical( + "databricks_v2", + &[ + "databricks-gpt-5-5".to_string(), + "goose-gpt-5-5".to_string(), + ], + ); + + assert!( + models.iter().any(|model| model.id == "goose-gpt-5-5"), + "expected goose-gpt-5-5 to win duplicate canonical-name tie, got {models:?}" + ); + assert!( + !models.iter().any(|model| model.id == "databricks-gpt-5-5"), + "expected databricks-gpt-5-5 to be replaced by goose-gpt-5-5, got {models:?}" + ); + } + #[test] fn inventory_uses_configured_models_before_first_successful_refresh() { let configured_models = [ModelInfo::new("claude-sonnet-4-5", 0)]; From 794402d932257446598ecac3cd4a920c4bf0485c Mon Sep 17 00:00:00 2001 From: 88plug <19512127+88plug@users.noreply.github.com> Date: Wed, 27 May 2026 05:58:46 -0700 Subject: [PATCH 16/66] fix(ci): build linux x86_64 standard inside manylinux_2_28 for glibc 2.28+ compat (#9415) Signed-off-by: Andrew Mello Co-authored-by: Alex Hancock Co-authored-by: jh-block <255854896+jh-block@users.noreply.github.com> --- .github/workflows/build-cli.yml | 60 +++++++++++++++++++++++++++++---- 1 file changed, 53 insertions(+), 7 deletions(-) diff --git a/.github/workflows/build-cli.yml b/.github/workflows/build-cli.yml index e1889daad243..0e3f898864da 100644 --- a/.github/workflows/build-cli.yml +++ b/.github/workflows/build-cli.yml @@ -4,7 +4,7 @@ # - canary.yml # # Platform Build Strategy: -# - Linux standard: Uses native Ubuntu 22.04 runners to keep glibc compatibility with Ubuntu 22.04 LTS +# - Linux standard (x86_64 + aarch64): Builds inside manylinux_2_28 container for glibc 2.28+ compat # - Linux Vulkan: Uses native Ubuntu 24.04 runners for newer Vulkan headers/tooling # - Linux musl: Uses native Ubuntu 22.04 runners with reduced features for musl compatibility # - macOS: Uses native macOS runners for each architecture @@ -27,6 +27,7 @@ jobs: build-cli: name: Build CLI runs-on: ${{ matrix.build-on }} + container: ${{ matrix.container }} env: MACOSX_DEPLOYMENT_TARGET: "12.0" strategy: @@ -37,11 +38,15 @@ jobs: architecture: x86_64 target-suffix: unknown-linux-gnu build-on: ubuntu-22.04 + # Pinned by digest for reproducible builds; bump explicitly when newer manylinux_2_28 images ship. + container: quay.io/pypa/manylinux_2_28_x86_64@sha256:441c35fdc6ee809ff9260894f8468ab4fea8c15dc880f8700a3f81b7922c1cda variant: standard - platform: linux architecture: aarch64 target-suffix: unknown-linux-gnu build-on: ubuntu-22.04-arm + # Pinned by digest for reproducible builds; bump explicitly when newer manylinux_2_28 images ship. + container: quay.io/pypa/manylinux_2_28_aarch64@sha256:8b5f2b4e8c072ae5aefeb659f22c03e1ff46e6a82f154b6c904b106c87e65ff7 variant: standard - platform: linux architecture: x86_64 @@ -97,8 +102,8 @@ jobs: sed -i.bak 's/^version = ".*"/version = "'${{ inputs.version }}'"/' Cargo.toml rm -f Cargo.toml.bak - - name: Install Linux build dependencies - if: matrix.platform == 'linux' + - name: Install Linux build dependencies (host runner) + if: matrix.platform == 'linux' && matrix.container == '' run: | sudo apt-get update sudo apt-get install -y \ @@ -119,11 +124,28 @@ jobs: sudo apt-get install -y musl-tools fi + - name: Install Linux build dependencies (manylinux container) + if: matrix.platform == 'linux' && matrix.container != '' + run: | + # perl-core provides FindBin, File::Compare, etc. that openssl-sys's + # vendored openssl build needs; in AlmaLinux 8 these aren't standalone packages. + # clang provides libclang.so for bindgen (used by llama-cpp-sys-2). + # Defensive: avoid actions/checkout falling back to a tarball download if base image changes. + dnf install -y --setopt=install_weak_deps=False \ + openssl-devel \ + dbus-devel \ + libxcb-devel \ + cmake \ + perl-core \ + clang \ + git \ + tar + - name: Cache Cargo artifacts (Linux/macOS) if: matrix.platform != 'windows' uses: Swatinem/rust-cache@c19371144df3bb44fab255c43d04cbc2ab54d1c4 # v2.9.1 with: - key: ${{ matrix.architecture }}-${{ matrix.target-suffix }}-${{ matrix.build-on }}-native-macos-deployment-target-12 + key: ${{ matrix.architecture }}-${{ matrix.target-suffix }}-${{ matrix.build-on }}-${{ matrix.container || 'native' }}-macos-deployment-target-12 - name: Cache Cargo artifacts (Windows) if: matrix.platform == 'windows' @@ -131,8 +153,8 @@ jobs: with: key: windows-msvc-cli-${{ matrix.variant }} - - name: Build CLI (Linux/macOS) - if: matrix.platform != 'windows' + - name: Build CLI (Linux/macOS host runner) + if: matrix.platform != 'windows' && matrix.container == '' env: RUST_LOG: debug RUST_BACKTRACE: 1 @@ -157,6 +179,27 @@ jobs: cargo build --release --target ${TARGET} -p goose-cli "${FEATURE_ARGS[@]}" fi + - name: Build CLI (manylinux container) + if: matrix.platform == 'linux' && matrix.container != '' + env: + RUST_BACKTRACE: 1 + run: | + # Hermit's tool cache is host-runner-scoped; inside the container we + # bootstrap rustup directly and let rust-toolchain.toml pin the channel. + curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \ + | sh -s -- -y --default-toolchain none --profile minimal --no-modify-path + export PATH="$HOME/.cargo/bin:$PATH" + TARGET="${{ matrix.architecture }}-${{ matrix.target-suffix }}" + RUST_CHANNEL=$(grep '^channel' rust-toolchain.toml | cut -d'"' -f2) + if [ -z "$RUST_CHANNEL" ]; then + echo "Could not parse channel from rust-toolchain.toml" >&2 + exit 1 + fi + rustup toolchain install "$RUST_CHANNEL" --profile minimal \ + --component rustc,cargo --target "$TARGET" + rustup show + cargo build --release --target "$TARGET" -p goose-cli + - name: Setup Rust (Windows) if: matrix.platform == 'windows' shell: bash @@ -215,7 +258,10 @@ jobs: - name: Package CLI (Linux/macOS) if: matrix.platform != 'windows' run: | - source ./bin/activate-hermit + # Hermit isn't installed in the manylinux container; tar is all this step needs. + if [ "${{ matrix.container }}" = '' ]; then + source ./bin/activate-hermit + fi export TARGET="${{ matrix.architecture }}-${{ matrix.target-suffix }}" export VARIANT_SUFFIX="" if [ "${{ matrix.variant }}" = "vulkan" ]; then From d90b349a693e7cf799229af8f83304a3fed784a2 Mon Sep 17 00:00:00 2001 From: Bradley Axen Date: Wed, 27 May 2026 06:38:59 -0700 Subject: [PATCH 17/66] feat: add /model slash command to CLI for session model switching (#8747) Signed-off-by: Bradley Axen Signed-off-by: Douwe Osinga Co-authored-by: Douwe Osinga --- Cargo.lock | 1 + crates/goose-cli/Cargo.toml | 1 + crates/goose-cli/src/session/completion.rs | 25 +++- crates/goose-cli/src/session/input.rs | 32 +++++ crates/goose-cli/src/session/mod.rs | 156 +++++++++++++++++++++ 5 files changed, 214 insertions(+), 1 deletion(-) diff --git a/Cargo.lock b/Cargo.lock index f3e267cfb612..9bb2adeeed1d 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4605,6 +4605,7 @@ dependencies = [ "comfy-table", "console", "dotenvy", + "env-lock", "etcetera 0.11.0", "futures", "goose", diff --git a/crates/goose-cli/Cargo.toml b/crates/goose-cli/Cargo.toml index 0a93aa2191fc..ca7ecd213d9d 100644 --- a/crates/goose-cli/Cargo.toml +++ b/crates/goose-cli/Cargo.toml @@ -108,6 +108,7 @@ native-tls = [ ] [dev-dependencies] +env-lock.workspace = true tempfile = { workspace = true } test-case = { workspace = true } tokio = { workspace = true } diff --git a/crates/goose-cli/src/session/completion.rs b/crates/goose-cli/src/session/completion.rs index f9c3d2bf6795..69bffe1a7d87 100644 --- a/crates/goose-cli/src/session/completion.rs +++ b/crates/goose-cli/src/session/completion.rs @@ -129,7 +129,6 @@ impl GooseCompleter { let skills = list_installed_skills(Some(&cwd)); let skill_names: Vec = skills.iter().map(|s| s.name.clone()).collect(); - // Complete the last letter being typed (e.g. "/skills coding in") let last = line.rsplit_once(' ').map_or("", |(_, w)| w); let pos = line.len() - last.len(); @@ -146,6 +145,11 @@ impl GooseCompleter { Ok((pos, candidates)) } + /// Complete model names for the /model command. + fn complete_model_names(&self, line: &str) -> Result<(usize, Vec)> { + Ok((line.len(), vec![])) + } + /// Complete slash commands fn complete_slash_commands(&self, line: &str) -> Result<(usize, Vec)> { // Define available slash commands @@ -160,6 +164,7 @@ impl GooseCompleter { "/prompts", "/prompt", "/mode", + "/model", "/recipe", "/skills", ]; @@ -396,6 +401,10 @@ impl Completer for GooseCompleter { } } + if line.starts_with("/model") { + return self.complete_model_names(line); + } + if line.starts_with("/mode") { return self.complete_mode_flags(line); } @@ -574,6 +583,20 @@ mod tests { assert_eq!(candidates.len(), 0); } + #[test] + fn test_complete_model_names() { + let cache = create_test_cache(); + let completer = GooseCompleter::new(cache); + + let (pos, candidates) = completer.complete_model_names("/model ").unwrap(); + assert_eq!(pos, "/model ".len()); + assert!(candidates.is_empty()); + + let (pos, candidates) = completer.complete_model_names("/model gpt").unwrap(); + assert_eq!(pos, "/model gpt".len()); + assert!(candidates.is_empty()); + } + #[test] fn test_complete_prompt_names() { let cache = create_test_cache(); diff --git a/crates/goose-cli/src/session/input.rs b/crates/goose-cli/src/session/input.rs index ba60ea763d08..729428b29413 100644 --- a/crates/goose-cli/src/session/input.rs +++ b/crates/goose-cli/src/session/input.rs @@ -20,6 +20,7 @@ pub enum InputResult { ListPrompts(Option), PromptCommand(PromptCommandOptions), GooseMode(String), + Model(Option), Plan(PlanCommandOptions), EndPlan, Clear, @@ -196,6 +197,8 @@ fn handle_slash_command(input: &str) -> Option { const CMD_EXTENSION: &str = "/extension "; const CMD_BUILTIN: &str = "/builtin "; const CMD_MODE: &str = "/mode "; + const CMD_MODEL: &str = "/model"; + const CMD_MODEL_WITH_SPACE: &str = "/model "; const CMD_PLAN: &str = "/plan"; const CMD_ENDPLAN: &str = "/endplan"; const CMD_CLEAR: &str = "/clear"; @@ -261,6 +264,19 @@ fn handle_slash_command(input: &str) -> Option { s if s.starts_with(CMD_MODE) => Some(InputResult::GooseMode( s.get(CMD_MODE.len()..).unwrap_or("").to_string(), )), + s if s == CMD_MODEL => Some(InputResult::Model(None)), + s if s.starts_with(CMD_MODEL_WITH_SPACE) => { + let model = s + .get(CMD_MODEL_WITH_SPACE.len()..) + .unwrap_or("") + .trim() + .to_string(); + if model.is_empty() { + Some(InputResult::Model(None)) + } else { + Some(InputResult::Model(Some(model))) + } + } s if s.starts_with(CMD_PLAN) => { parse_plan_command(s.get(CMD_PLAN.len()..).unwrap_or("").trim().to_string()) } @@ -399,6 +415,7 @@ fn print_help() { /prompts [--extension ] - List all available prompts, optionally filtered by extension /prompt [--info] [key=value...] - Get prompt info or execute a prompt /mode - Set the goose mode to use ({modes}) +/model [name] - Show the current model, or switch models for this session while keeping the same provider /plan - Enters 'plan' mode with optional message. Create a plan based on the current messages and asks user if they want to act on it. If user acts on the plan, goose mode is set to 'auto' and returns to 'normal' goose mode. To warm up goose before using '/plan', we recommend setting '/mode approve' & putting appropriate context into goose. @@ -498,6 +515,21 @@ mod tests { panic!("Expected AddBuiltin"); } + // Test model command + assert!(matches!( + handle_slash_command("/model"), + Some(InputResult::Model(None)) + )); + assert!(matches!( + handle_slash_command("/model "), + Some(InputResult::Model(None)) + )); + if let Some(InputResult::Model(Some(model))) = handle_slash_command("/model gpt-4.1") { + assert_eq!(model, "gpt-4.1"); + } else { + panic!("Expected Model"); + } + // Test unknown commands assert!(handle_slash_command("/unknown").is_none()); } diff --git a/crates/goose-cli/src/session/mod.rs b/crates/goose-cli/src/session/mod.rs index 11713ed9f520..cd7e63b74098 100644 --- a/crates/goose-cli/src/session/mod.rs +++ b/crates/goose-cli/src/session/mod.rs @@ -608,6 +608,10 @@ impl CliSession { history.save(editor); self.handle_goose_mode(&mode).await?; } + InputResult::Model(model) => { + history.save(editor); + self.handle_model(model.as_deref()).await?; + } InputResult::Plan(options) => { self.handle_plan_mode(options).await?; } @@ -795,6 +799,73 @@ impl CliSession { Ok(()) } + async fn handle_model(&self, model: Option<&str>) -> Result<()> { + let provider = self.agent.provider().await?; + let current_provider_name = provider.get_name().to_string(); + let current_model_config = provider.get_model_config(); + let current_model_name = current_model_config.model_name.clone(); + + if model.is_none() { + output::goose_mode_message(&format!( + "Current session model: '{}' (provider '{}')", + current_model_name, current_provider_name + )); + return Ok(()); + } + + let model_name = model.unwrap_or_default().trim(); + if model_name.is_empty() { + output::render_error("Model name cannot be empty"); + return Ok(()); + } + + if current_provider_name.ends_with("-acp") { + output::render_error( + "Session model switching is not supported for ACP providers in the CLI.", + ); + return Ok(()); + } + + if provider.manages_own_context() { + output::render_error(&format!( + "Session model switching is not supported for provider '{}' because it manages its own conversation context.", + current_provider_name + )); + return Ok(()); + } + + let new_model_config = + build_switched_model_config(¤t_provider_name, model_name, ¤t_model_config)?; + + if new_model_config.model_name == current_model_config.model_name + && new_model_config.thinking_effort() == current_model_config.thinking_effort() + { + output::goose_mode_message(&format!( + "Session already using model '{}' for provider '{}'", + current_model_name, current_provider_name + )); + return Ok(()); + } + + let extensions = self.agent.get_extension_configs().await; + let new_provider = + goose::providers::create(¤t_provider_name, new_model_config, extensions) + .await + .map_err(|e| anyhow::anyhow!("Failed to create provider: {e}"))?; + + self.agent + .update_provider(new_provider, &self.session_id) + .await?; + + let mode = self.agent.goose_mode().await; + self.agent.update_goose_mode(mode, &self.session_id).await?; + output::goose_mode_message(&format!( + "Session model switched from '{}' to '{}' for provider '{}'", + current_model_name, model_name, current_provider_name + )); + Ok(()) + } + async fn handle_plan_mode(&mut self, options: input::PlanCommandOptions) -> Result<()> { self.run_mode = RunMode::Plan; output::render_enter_plan_mode(); @@ -2091,11 +2162,28 @@ fn format_elapsed_time(duration: std::time::Duration) -> String { } } +fn build_switched_model_config( + provider_name: &str, + model_name: &str, + current_model_config: &goose::model::ModelConfig, +) -> Result { + goose::model::ModelConfig::new(model_name) + .map(|config| { + config + .with_canonical_limits(provider_name) + .with_temperature(current_model_config.temperature) + .with_toolshim(current_model_config.toolshim) + .with_toolshim_model(current_model_config.toolshim_model.clone()) + }) + .map_err(|e| anyhow::anyhow!("Failed to create model configuration: {e}")) +} + #[cfg(test)] mod tests { use super::*; use goose::agents::extension::Envs; use goose::config::ExtensionConfig; + use std::collections::HashMap; use std::time::Duration; use test_case::test_case; @@ -2219,6 +2307,74 @@ mod tests { assert!(CliSession::parse_stdio_extension("").is_err()); } + #[test] + fn test_build_switched_model_config_rebuilds_target_model_settings() { + let _guard = env_lock::lock_env([ + ("GOOSE_MAX_TOKENS", None::<&str>), + ("GOOSE_TEMPERATURE", None::<&str>), + ("GOOSE_CONTEXT_LIMIT", None::<&str>), + ("GOOSE_TOOLSHIM", None::<&str>), + ("GOOSE_TOOLSHIM_OLLAMA_MODEL", None::<&str>), + ]); + + let current_model_config = goose::model::ModelConfig { + model_name: "gpt-4o".to_string(), + context_limit: Some(128_000), + temperature: Some(0.25), + max_tokens: Some(16_384), + toolshim: true, + toolshim_model: Some("qwen2.5-coder".to_string()), + fast_model_config: None, + request_params: Some(HashMap::from([( + "anthropic_beta".to_string(), + serde_json::json!(["output-128k-2025-02-19"]), + )])), + reasoning: Some(false), + }; + + let switched = + build_switched_model_config("openai", "gpt-5.4", ¤t_model_config).unwrap(); + let expected = goose::model::ModelConfig::new_or_fail("gpt-5.4") + .with_canonical_limits("openai") + .with_temperature(Some(0.25)) + .with_toolshim(true) + .with_toolshim_model(Some("qwen2.5-coder".to_string())); + + assert_eq!(switched.model_name, expected.model_name); + assert_eq!(switched.context_limit, expected.context_limit); + assert_eq!(switched.max_tokens, expected.max_tokens); + assert_eq!(switched.request_params, expected.request_params); + assert_eq!(switched.reasoning, expected.reasoning); + assert_eq!(switched.temperature, Some(0.25)); + assert!(switched.toolshim); + assert_eq!(switched.toolshim_model.as_deref(), Some("qwen2.5-coder")); + } + + #[test] + fn test_build_switched_model_config_detects_effort_suffix_change() { + let _guard = env_lock::lock_env([ + ("GOOSE_MAX_TOKENS", None::<&str>), + ("GOOSE_TEMPERATURE", None::<&str>), + ("GOOSE_CONTEXT_LIMIT", None::<&str>), + ("GOOSE_TOOLSHIM", None::<&str>), + ("GOOSE_TOOLSHIM_OLLAMA_MODEL", None::<&str>), + ("GOOSE_THINKING_EFFORT", None::<&str>), + ]); + + let current = + goose::model::ModelConfig::new_or_fail("gpt-5.4-high").with_canonical_limits("openai"); + assert_eq!(current.model_name, "gpt-5.4"); + assert_eq!( + current.thinking_effort(), + Some(goose::model::ThinkingEffort::High) + ); + + let switched = build_switched_model_config("openai", "gpt-5.4", ¤t).unwrap(); + + assert_eq!(switched.model_name, current.model_name); + assert_ne!(switched.thinking_effort(), current.thinking_effort()); + } + #[test] fn test_split_command_args_windows_paths() { assert_eq!( From 27b41d93f5af39e765a217f44dd4864fdeed7c93 Mon Sep 17 00:00:00 2001 From: jh-block Date: Wed, 27 May 2026 20:00:30 +0200 Subject: [PATCH 18/66] local inference: stricter GGUF requirements, auto detection of tool calling support, fixed thinking output parsing (#9442) Signed-off-by: jh-block --- Cargo.lock | 1 + crates/goose-cli/src/cli.rs | 35 +- crates/goose-server/src/openapi.rs | 3 + .../src/routes/local_inference.rs | 193 +++++-- crates/goose/Cargo.toml | 2 + crates/goose/src/providers/base.rs | 13 + crates/goose/src/providers/local_inference.rs | 49 +- .../providers/local_inference/hf_models.rs | 182 ++++++- .../llamacpp/inference_emulated_tools.rs | 182 +++++-- .../llamacpp/inference_engine.rs | 277 +++++++++- .../llamacpp/inference_native_tools.rs | 210 +++----- .../providers/local_inference/llamacpp/mod.rs | 486 +++++++++++++++--- .../local_inference/local_model_registry.rs | 131 ++--- ui/desktop/openapi.json | 100 +++- ui/desktop/src/api/index.ts | 4 +- ui/desktop/src/api/sdk.gen.ts | 4 +- ui/desktop/src/api/types.gen.ts | 34 +- .../localInference/ModelSettingsPanel.tsx | 215 +++++++- ui/desktop/src/i18n/messages/en.json | 47 +- ui/desktop/src/i18n/messages/ru.json | 51 +- ui/desktop/src/i18n/messages/tr.json | 51 +- ui/desktop/src/i18n/messages/zh-CN.json | 51 +- ui/desktop/src/types/message.ts | 15 - 23 files changed, 1844 insertions(+), 492 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 9bb2adeeed1d..c5afe245e33c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4497,6 +4497,7 @@ dependencies = [ "keyring", "libc", "llama-cpp-2", + "llama-cpp-sys-2", "lru", "minijinja", "mockall", diff --git a/crates/goose-cli/src/cli.rs b/crates/goose-cli/src/cli.rs index 0615d8ac3806..78c374db33cb 100644 --- a/crates/goose-cli/src/cli.rs +++ b/crates/goose-cli/src/cli.rs @@ -1816,7 +1816,7 @@ async fn handle_term_subcommand(command: TermCommand) -> Result<()> { async fn handle_local_models_command(command: LocalModelsCommand) -> Result<()> { use goose::providers::local_inference::hf_models; use goose::providers::local_inference::local_model_registry::{ - get_registry, model_id_from_repo, LocalModelEntry, + get_registry, mmproj_local_path, model_id_from_repo, LocalModelEntry, }; match command { @@ -1853,10 +1853,28 @@ async fn handle_local_models_command(command: LocalModelsCommand) -> Result<()> } LocalModelsCommand::Download { spec } => { println!("Resolving {}...", spec); - let (repo_id, file) = hf_models::resolve_model_spec(&spec).await?; + let (repo_id, resolved) = hf_models::resolve_model_spec_full(&spec).await?; + if resolved.files.len() > 1 { + anyhow::bail!( + "Model '{}' is sharded ({} files) — download it from the desktop UI", + spec, + resolved.files.len() + ); + } + let mmproj = resolved.mmproj; + let file = resolved.files.into_iter().next().unwrap(); let model_id = model_id_from_repo(&repo_id, &file.quantization); let local_path = goose::config::paths::Paths::in_data_dir("models").join(&file.filename); + let mmproj_path = mmproj + .as_ref() + .map(|mmproj| mmproj_local_path(&repo_id, &mmproj.filename)); + let mmproj_source_url = mmproj.as_ref().map(|mmproj| mmproj.download_url.clone()); + let mmproj_size_bytes = mmproj.as_ref().map_or(0, |mmproj| mmproj.size_bytes); + let mut download_files = vec![(file.download_url.clone(), local_path.clone())]; + if let Some(mmproj) = mmproj { + download_files.push((mmproj.download_url, mmproj_path.clone().unwrap())); + } println!( "Downloading {} ({})...", @@ -1881,9 +1899,10 @@ async fn handle_local_models_command(command: LocalModelsCommand) -> Result<()> source_url: file.download_url.clone(), settings: Default::default(), size_bytes: file.size_bytes, - mmproj_path: None, - mmproj_source_url: None, - mmproj_size_bytes: 0, + mmproj_path, + mmproj_source_url, + mmproj_size_bytes, + mmproj_checked: true, shard_files: vec![], }; @@ -1897,10 +1916,10 @@ async fn handle_local_models_command(command: LocalModelsCommand) -> Result<()> // Download let manager = goose::download_manager::get_download_manager(); manager - .download_model( + .download_model_sharded( format!("{}-model", model_id), - file.download_url, - local_path, + download_files, + file.size_bytes + mmproj_size_bytes, None, ) .await?; diff --git a/crates/goose-server/src/openapi.rs b/crates/goose-server/src/openapi.rs index 79d2301b87a1..5749b6440a3b 100644 --- a/crates/goose-server/src/openapi.rs +++ b/crates/goose-server/src/openapi.rs @@ -683,6 +683,7 @@ pub struct ApiDoc; super::routes::local_inference::list_local_models, super::routes::local_inference::sync_featured_models, super::routes::local_inference::search_hf_models, + super::routes::local_inference::list_builtin_chat_templates, super::routes::local_inference::get_repo_files, super::routes::local_inference::download_hf_model, super::routes::local_inference::get_local_model_download_progress, @@ -701,7 +702,9 @@ pub struct ApiDoc; goose::providers::local_inference::hf_models::HfQuantVariant, super::routes::local_inference::RepoVariantsResponse, goose::providers::local_inference::local_model_registry::ModelSettings, + goose::providers::local_inference::local_model_registry::ChatTemplate, goose::providers::local_inference::local_model_registry::SamplingConfig, + goose::providers::local_inference::local_model_registry::ToolCallingMode, )) )] pub struct LocalInferenceApiDoc; diff --git a/crates/goose-server/src/routes/local_inference.rs b/crates/goose-server/src/routes/local_inference.rs index b7a7dfbde6ab..8ee9bc4eebcf 100644 --- a/crates/goose-server/src/routes/local_inference.rs +++ b/crates/goose-server/src/routes/local_inference.rs @@ -13,10 +13,10 @@ use goose::config::paths::Paths; use goose::download_manager::{get_download_manager, DownloadProgress}; use goose::providers::local_inference::hf_models::{self, HfModelInfo, HfQuantVariant}; use goose::providers::local_inference::{ - available_inference_memory_bytes, - hf_models::{resolve_model_spec, resolve_model_spec_full, HfGgufFile}, + available_inference_memory_bytes, builtin_chat_template_names, + hf_models::{resolve_model_spec_full, HfGgufFile}, local_model_registry::{ - default_settings_for_model, featured_mmproj_spec, get_registry, is_featured_model, + default_settings_for_model, get_registry, is_featured_model, mmproj_local_path, model_id_from_repo, LocalModelEntry, ModelDownloadStatus as RegistryDownloadStatus, ModelSettings, ShardFile, FEATURED_MODELS, }, @@ -79,26 +79,18 @@ async fn ensure_featured_models_in_registry() -> Result<(), ErrorResponse> { .lock() .map_err(|_| ErrorResponse::internal("Failed to acquire registry lock"))?; if let Some(existing) = registry.get_model(&model_id) { - let needs_backfill = existing.mmproj_path.is_none() && featured.mmproj.is_some(); - let needs_download = existing.is_downloaded() - && featured.mmproj.is_some() - && !existing.mmproj_path.as_ref().is_some_and(|p| p.exists()); - - if needs_download { - if let Some(mmproj) = featured.mmproj.as_ref() { - let path = mmproj.local_path(); - let url = format!( - "https://huggingface.co/{}/resolve/main/{}", - mmproj.repo, mmproj.filename - ); - mmproj_downloads_needed.push((model_id.clone(), url, path)); + if let Some(path) = &existing.mmproj_path { + if existing.is_downloaded() && !path.exists() { + if let Some(url) = &existing.mmproj_source_url { + mmproj_downloads_needed.push(( + model_id.clone(), + url.clone(), + path.clone(), + )); + } } - } - - if !needs_backfill { continue; } - // Fall through to resolve for backfill } } @@ -110,36 +102,45 @@ async fn ensure_featured_models_in_registry() -> Result<(), ErrorResponse> { }); } - let resolved: Vec<(PendingResolve, HfGgufFile)> = + let resolved: Vec<(PendingResolve, HfGgufFile, Option)> = join_all(to_resolve.into_iter().map(|pending| async move { - let hf_file = match resolve_model_spec(pending.spec).await { - Ok((_repo, file)) => file, + let (hf_file, mmproj) = match resolve_model_spec_full(pending.spec).await { + Ok((_repo, resolved)) => (resolved.files[0].clone(), resolved.mmproj), Err(_) => { let filename = format!( "{}-{}.gguf", pending.repo_id.split('/').next_back().unwrap_or("model"), pending.quantization ); - HfGgufFile { - filename: filename.clone(), - size_bytes: 0, - quantization: pending.quantization.to_string(), - download_url: format!( - "https://huggingface.co/{}/resolve/main/{}", - pending.repo_id, filename - ), - } + ( + HfGgufFile { + filename: filename.clone(), + size_bytes: 0, + quantization: pending.quantization.to_string(), + download_url: format!( + "https://huggingface.co/{}/resolve/main/{}", + pending.repo_id, filename + ), + }, + None, + ) } }; - (pending, hf_file) + (pending, hf_file, mmproj) })) .await; let entries_to_add: Vec = resolved .into_iter() - .map(|(pending, hf_file)| { + .map(|(pending, hf_file, mmproj)| { let local_path = Paths::in_data_dir("models").join(&hf_file.filename); let settings = default_settings_for_model(&pending.model_id); + let mmproj_path = mmproj + .as_ref() + .map(|mmproj| mmproj_local_path(&pending.repo_id, &mmproj.filename)); + let mmproj_source_url = mmproj.as_ref().map(|mmproj| mmproj.download_url.clone()); + let mmproj_size_bytes = mmproj.as_ref().map_or(0, |mmproj| mmproj.size_bytes); + let mmproj_checked = mmproj.is_some(); LocalModelEntry { id: pending.model_id, repo_id: pending.repo_id, @@ -149,9 +150,10 @@ async fn ensure_featured_models_in_registry() -> Result<(), ErrorResponse> { source_url: hf_file.download_url, settings, size_bytes: hf_file.size_bytes, - mmproj_path: None, - mmproj_source_url: None, - mmproj_size_bytes: 0, + mmproj_path, + mmproj_source_url, + mmproj_size_bytes, + mmproj_checked, shard_files: vec![], } }) @@ -165,20 +167,80 @@ async fn ensure_featured_models_in_registry() -> Result<(), ErrorResponse> { if !entries_to_add.is_empty() { registry.sync_with_featured(entries_to_add); } + } + + let to_backfill: Vec<(String, String, String)> = { + let registry = get_registry() + .lock() + .map_err(|_| ErrorResponse::internal("Failed to acquire registry lock"))?; + + registry + .list_models() + .iter() + .filter(|model| model.is_downloaded()) + .filter(|model| model.mmproj_path.is_none()) + .filter(|model| !model.mmproj_checked) + .map(|model| { + ( + model.id.clone(), + model.repo_id.clone(), + model.quantization.clone(), + ) + }) + .collect() + }; + + let mmproj_backfills: Vec<(String, String, Option>)> = join_all( + to_backfill + .into_iter() + .map(|(id, repo_id, quantization)| async move { + let spec = format!("{repo_id}:{quantization}"); + let mmproj = resolve_model_spec_full(&spec) + .await + .ok() + .map(|(_, resolved)| resolved.mmproj); + (id, repo_id, mmproj) + }), + ) + .await; + + { + let mut registry = get_registry() + .lock() + .map_err(|_| ErrorResponse::internal("Failed to acquire registry lock"))?; + + for (model_id, repo_id, mmproj_result) in mmproj_backfills { + if let Some(model) = registry + .list_models_mut() + .iter_mut() + .find(|model| model.id == model_id) + { + let Some(mmproj) = mmproj_result else { + continue; + }; + + model.mmproj_checked = true; + if let Some(mmproj) = mmproj { + model.mmproj_path = Some(mmproj_local_path(&repo_id, &mmproj.filename)); + model.mmproj_source_url = Some(mmproj.download_url); + model.mmproj_size_bytes = mmproj.size_bytes; + } + model.refresh_mmproj_metadata(); + } + } - // Backfill mmproj data for all registry models and collect any - // needed mmproj downloads for models already on disk. for model in registry.list_models_mut() { - model.enrich_with_featured_mmproj(); + model.refresh_mmproj_metadata(); if model.is_downloaded() { - if let Some(mmproj) = featured_mmproj_spec(&model.id) { - let path = mmproj.local_path(); + if let Some(path) = &model.mmproj_path { if !path.exists() { - let url = format!( - "https://huggingface.co/{}/resolve/main/{}", - mmproj.repo, mmproj.filename - ); - mmproj_downloads_needed.push((model.id.clone(), url, path)); + if let Some(url) = &model.mmproj_source_url { + mmproj_downloads_needed.push(( + model.id.clone(), + url.clone(), + path.clone(), + )); + } } } } @@ -431,6 +493,20 @@ pub async fn download_hf_model( vec![] }; + let mmproj_path = resolved + .mmproj + .as_ref() + .map(|mmproj| mmproj_local_path(&repo_id, &mmproj.filename)); + let mmproj_source_url = resolved + .mmproj + .as_ref() + .map(|mmproj| mmproj.download_url.clone()); + let mmproj_size_bytes = resolved + .mmproj + .as_ref() + .map_or(0, |mmproj| mmproj.size_bytes); + let mmproj_checked = true; + let entry = LocalModelEntry { id: model_id.clone(), repo_id, @@ -440,13 +516,13 @@ pub async fn download_hf_model( source_url: first_file.download_url.clone(), settings: default_settings_for_model(&model_id), size_bytes: resolved.total_size, - mmproj_path: None, - mmproj_source_url: None, - mmproj_size_bytes: 0, + mmproj_path, + mmproj_source_url, + mmproj_size_bytes, + mmproj_checked, shard_files: shard_files.clone(), }; - // add_model enriches the entry with mmproj metadata from the featured table let mmproj_path = { let mut registry = get_registry() .lock() @@ -649,6 +725,17 @@ pub async fn update_model_settings( Ok(Json(settings)) } +#[utoipa::path( + get, + path = "/local-inference/chat-templates/builtin", + responses( + (status = 200, description = "llama.cpp built-in chat template names", body = Vec) + ) +)] +pub async fn list_builtin_chat_templates() -> Json> { + Json(builtin_chat_template_names()) +} + pub fn routes(state: Arc) -> Router { let registered_paths: std::collections::HashSet = get_registry() .lock() @@ -672,6 +759,10 @@ pub fn routes(state: Arc) -> Router { .route("/local-inference/models", get(list_local_models)) .route("/local-inference/sync-featured", post(sync_featured_models)) .route("/local-inference/search", get(search_hf_models)) + .route( + "/local-inference/chat-templates/builtin", + get(list_builtin_chat_templates), + ) .route( "/local-inference/repo/{author}/{repo}/files", get(get_repo_files), diff --git a/crates/goose/Cargo.toml b/crates/goose/Cargo.toml index 7e391afcdb8d..fccd7dbbc6d8 100644 --- a/crates/goose/Cargo.toml +++ b/crates/goose/Cargo.toml @@ -25,6 +25,7 @@ local-inference = [ "dep:candle-nn", "dep:candle-transformers", "dep:llama-cpp-2", + "dep:llama-cpp-sys-2", "dep:tokenizers", "dep:symphonia", "dep:rubato", @@ -213,6 +214,7 @@ pctx_code_mode = { version = "0.3", default-features = false, optional = true } # They are just here to pin the version, and can be removed if PCTX updates temporal_rs icu_calendar = { version = "=2.1.1", default-features = false } icu_locale = { version = "=2.1.1", default-features = false } +llama-cpp-sys-2 = { workspace = true, optional = true } [target.'cfg(target_os = "windows")'.dependencies] winapi = { workspace = true } diff --git a/crates/goose/src/providers/base.rs b/crates/goose/src/providers/base.rs index 0737cbed6b95..f79a3a3f3fb2 100644 --- a/crates/goose/src/providers/base.rs +++ b/crates/goose/src/providers/base.rs @@ -1419,6 +1419,19 @@ mod tests { assert_eq!(out.thinking, "unfinished"); } + #[test] + fn test_think_filter_tracks_generation_prompt_open_block() { + let mut filter = ThinkFilter::new(); + let _ = filter.push("<|assistant|>\n"); + let mut out = filter.push("hidden reasoningvisible answer"); + let final_out = filter.finish(); + out.content.push_str(&final_out.content); + out.thinking.push_str(&final_out.thinking); + + assert_eq!(out.content, "visible answer"); + assert_eq!(out.thinking, "hidden reasoning"); + } + #[test] fn test_think_filter_preserves_tags_with_think_prefix() { for input in [ diff --git a/crates/goose/src/providers/local_inference.rs b/crates/goose/src/providers/local_inference.rs index 970a35311fa2..e7229f5fdd8f 100644 --- a/crates/goose/src/providers/local_inference.rs +++ b/crates/goose/src/providers/local_inference.rs @@ -19,6 +19,7 @@ use async_trait::async_trait; use backend::{BackendLoadedModel, LocalInferenceBackend}; use futures::future::BoxFuture; use llamacpp::{LlamaCppBackend, LLAMACPP_BACKEND_ID}; +use local_model_registry::ChatTemplate; use rmcp::model::Tool; use serde_json::{json, Value}; use std::collections::HashMap; @@ -33,13 +34,19 @@ type ModelSlot = Arc>>>; struct ModelCacheKey { backend_id: &'static str, model_id: String, + chat_template: ChatTemplate, } impl ModelCacheKey { - fn new(backend_id: &'static str, model_id: impl Into) -> Self { + fn new( + backend_id: &'static str, + model_id: impl Into, + chat_template: ChatTemplate, + ) -> Self { Self { backend_id, model_id: model_id.into(), + chat_template, } } } @@ -49,6 +56,10 @@ pub struct InferenceRuntime { backends: HashMap<&'static str, Arc>, } +pub fn builtin_chat_template_names() -> Vec { + llamacpp::builtin_chat_template_names() +} + /// Global weak reference used to share a single `InferenceRuntime` across /// all providers and server routes. Only a `Weak` is stored — strong `Arc`s /// live in providers and `AppState`. When all strong refs drop (normal @@ -123,20 +134,12 @@ pub(super) struct ResolvedModelPaths { /// Resolve model path, context limit, settings, and mmproj path for a model ID from the registry. fn resolve_model_path(model_id: &str) -> Option { - use crate::providers::local_inference::local_model_registry::{ - default_settings_for_model, get_registry, - }; + use crate::providers::local_inference::local_model_registry::get_registry; if let Ok(registry) = get_registry().lock() { if let Some(entry) = registry.get_model(model_id) { let ctx = entry.settings.context_size.unwrap_or(0) as usize; let mut settings = entry.settings.clone(); - // Capability flags are inherent to the model family, not user-configurable. - // Re-derive them so that registry entries persisted before a model was - // recognized (or with a different quantization) still get the right behavior. - let defaults = default_settings_for_model(model_id); - settings.native_tool_calling = defaults.native_tool_calling; - settings.vision_capable = defaults.vision_capable; settings.mmproj_size_bytes = entry.mmproj_size_bytes; let mmproj_path = entry.mmproj_path.as_ref().filter(|p| p.exists()).cloned(); return Some(ResolvedModelPaths { @@ -195,6 +198,22 @@ fn build_openai_messages_json(system: &str, messages: &[Message]) -> String { serde_json::to_string(&arr).unwrap_or_else(|_| "[]".to_string()) } +fn build_openai_text_messages_json(system: &str, messages: &[Message]) -> String { + let mut arr: Vec = vec![json!({"role": "system", "content": system})]; + arr.extend(messages.iter().filter_map(|m| { + let content = extract_text_content(m); + if content.trim().is_empty() { + return None; + } + let role = match m.role { + rmcp::model::Role::User => "user", + rmcp::model::Role::Assistant => "assistant", + }; + Some(json!({"role": role, "content": content})) + })); + serde_json::to_string(&arr).unwrap_or_else(|_| "[]".to_string()) +} + /// Remove `image_url` content parts from OpenAI-format messages JSON, replacing /// each with a text note. This prevents an FFI crash in llama.cpp which does not /// accept `image_url` content-part types. @@ -422,7 +441,11 @@ impl Provider for LocalInferenceProvider { let backend = self.runtime.backend_for_model(&resolved)?; let model_context_limit = resolved.context_limit; let model_settings = resolved.settings.clone(); - let cache_key = ModelCacheKey::new(backend.id(), model_config.model_name.clone()); + let cache_key = ModelCacheKey::new( + backend.id(), + model_config.model_name.clone(), + model_settings.chat_template.clone(), + ); let model_slot = self.runtime.get_or_create_model_slot(cache_key.clone()); // Ensure model is loaded — unload any other models first to free memory. @@ -477,8 +500,8 @@ impl Provider for LocalInferenceProvider { }).collect::>(), "tools": tools.iter().map(|t| &t.name).collect::>(), "settings": { - "use_jinja": settings.use_jinja, - "native_tool_calling": settings.native_tool_calling, + "tool_calling": settings.tool_calling, + "chat_template": settings.chat_template, "context_size": settings.context_size, "sampling": settings.sampling, }, diff --git a/crates/goose/src/providers/local_inference/hf_models.rs b/crates/goose/src/providers/local_inference/hf_models.rs index 510cd63812ac..5767193ae2ea 100644 --- a/crates/goose/src/providers/local_inference/hf_models.rs +++ b/crates/goose/src/providers/local_inference/hf_models.rs @@ -42,6 +42,7 @@ pub struct HfQuantVariant { pub struct ResolvedModel { pub files: Vec, pub total_size: u64, + pub mmproj: Option, } #[derive(Debug, Deserialize)] @@ -183,6 +184,24 @@ fn parse_quantization(filename: &str) -> String { "unknown".to_string() } +fn quant_bits(quantization: &str) -> u8 { + let digits: String = quantization + .chars() + .skip_while(|c| !c.is_ascii_digit()) + .take_while(|c| c.is_ascii_digit()) + .collect(); + digits.parse().unwrap_or(0) +} + +fn mmproj_precision_preference(quantization: &str) -> u8 { + match quantization.to_uppercase().as_str() { + "BF16" => 3, + "F16" => 2, + "F32" => 1, + _ => 0, + } +} + fn looks_like_quant(s: &str) -> bool { let upper = s.to_uppercase(); upper.starts_with("Q") @@ -226,6 +245,64 @@ fn build_download_url(repo_id: &str, filename: &str) -> String { format!("{}/{}/resolve/main/{}", HF_DOWNLOAD_BASE, repo_id, filename) } +fn parent_components(filename: &str) -> Vec<&str> { + filename.rsplit_once('/').map_or(Vec::new(), |(parent, _)| { + parent.split('/').filter(|part| !part.is_empty()).collect() + }) +} + +fn is_prefix(prefix: &[&str], parts: &[&str]) -> bool { + prefix.len() <= parts.len() && prefix.iter().zip(parts).all(|(a, b)| a == b) +} + +fn select_best_mmproj( + repo_id: &str, + siblings: &[HfApiSibling], + model_filename: &str, + model_quantization: &str, +) -> Option { + let model_dir = parent_components(model_filename); + let model_bits = quant_bits(model_quantization); + + siblings + .iter() + .filter(|s| { + let lowercase = s.rfilename.to_lowercase(); + lowercase.ends_with(".gguf") && lowercase.contains("mmproj") + }) + .filter_map(|s| { + let mmproj_dir = parent_components(&s.rfilename); + if !is_prefix(&mmproj_dir, &model_dir) { + return None; + } + + let quantization = parse_quantization(&s.rfilename); + let bits = quant_bits(&quantization); + let diff = bits.abs_diff(model_bits); + let proximity = u8::MAX - diff; + + Some(( + mmproj_dir.len(), + proximity, + mmproj_precision_preference(&quantization), + s, + quantization, + )) + }) + .max_by(|a, b| { + a.0.cmp(&b.0) + .then_with(|| a.1.cmp(&b.1)) + .then_with(|| a.2.cmp(&b.2)) + .then_with(|| b.3.rfilename.cmp(&a.3.rfilename)) + }) + .map(|(_, _, _, sibling, quantization)| HfGgufFile { + filename: sibling.rfilename.clone(), + size_bytes: sibling.size.unwrap_or(0), + quantization, + download_url: build_download_url(repo_id, &sibling.rfilename), + }) +} + /// Derive the expected model filename stem from a repo_id. /// e.g. "unsloth/gemma-4-26B-A4B-it-GGUF" → "gemma-4-26b-a4b-it" (lowercased) fn model_stem_from_repo(repo_id: &str) -> String { @@ -500,7 +577,7 @@ pub async fn resolve_model_spec_full(spec: &str) -> Result<(String, ResolvedMode // Collect all GGUF files matching the quantization let matching: Vec<_> = siblings - .into_iter() + .iter() .filter(|s| { s.rfilename.ends_with(".gguf") && is_model_file(&s.rfilename, &stem) @@ -519,7 +596,7 @@ pub async fn resolve_model_spec_full(spec: &str) -> Result<(String, ResolvedMode // Separate single files from shards let mut single_files: Vec<&HfApiSibling> = Vec::new(); let mut shard_files: Vec<&HfApiSibling> = Vec::new(); - for f in &matching { + for &f in &matching { if is_shard_file(&f.rfilename) { shard_files.push(f); } else { @@ -529,6 +606,7 @@ pub async fn resolve_model_spec_full(spec: &str) -> Result<(String, ResolvedMode // Prefer single file if available if let Some(single) = single_files.first() { + let mmproj = select_best_mmproj(&repo_id, &siblings, &single.rfilename, &quant); let file = HfGgufFile { filename: single.rfilename.clone(), size_bytes: single.size.unwrap_or(0), @@ -541,6 +619,7 @@ pub async fn resolve_model_spec_full(spec: &str) -> Result<(String, ResolvedMode ResolvedModel { files: vec![file], total_size, + mmproj, }, )); } @@ -600,7 +679,16 @@ pub async fn resolve_model_spec_full(spec: &str) -> Result<(String, ResolvedMode .collect(); let total_size: u64 = files.iter().map(|f| f.size_bytes).sum(); - Ok((repo_id, ResolvedModel { files, total_size })) + let mmproj = select_best_mmproj(&repo_id, &siblings, &files[0].filename, &quant); + + Ok(( + repo_id, + ResolvedModel { + files, + total_size, + mmproj, + }, + )) } /// Resolve a model spec to a specific GGUF file from the repo. @@ -816,4 +904,92 @@ mod tests { assert_eq!(variants[1].quantization, "Q4_K_M"); assert_eq!(variants[2].quantization, "IQ1_S"); } + + #[test] + fn test_select_best_mmproj_prefers_closest_precision() { + let files = vec![ + HfApiSibling { + rfilename: "mmproj-F32.gguf".into(), + size: Some(3_000), + }, + HfApiSibling { + rfilename: "mmproj-BF16.gguf".into(), + size: Some(2_000), + }, + ]; + + let mmproj = + select_best_mmproj("someone/model-GGUF", &files, "model-Q4_K_M.gguf", "Q4_K_M") + .unwrap(); + + assert_eq!(mmproj.filename, "mmproj-BF16.gguf"); + assert_eq!(mmproj.quantization, "BF16"); + } + + #[test] + fn test_select_best_mmproj_prefers_bf16_over_f16_tie() { + let files = vec![ + HfApiSibling { + rfilename: "mmproj-F16.gguf".into(), + size: Some(2_000), + }, + HfApiSibling { + rfilename: "mmproj-BF16.gguf".into(), + size: Some(2_000), + }, + ]; + + let mmproj = + select_best_mmproj("someone/model-GGUF", &files, "model-Q8_0.gguf", "Q8_0").unwrap(); + + assert_eq!(mmproj.filename, "mmproj-BF16.gguf"); + } + + #[test] + fn test_select_best_mmproj_prefers_nearest_directory() { + let files = vec![ + HfApiSibling { + rfilename: "mmproj-BF16.gguf".into(), + size: Some(2_000), + }, + HfApiSibling { + rfilename: "Q4_K_M/mmproj-F32.gguf".into(), + size: Some(3_000), + }, + ]; + + let mmproj = select_best_mmproj( + "someone/model-GGUF", + &files, + "Q4_K_M/model-Q4_K_M.gguf", + "Q4_K_M", + ) + .unwrap(); + + assert_eq!(mmproj.filename, "Q4_K_M/mmproj-F32.gguf"); + } + + #[test] + fn test_select_best_mmproj_ignores_sibling_directories() { + let files = vec![ + HfApiSibling { + rfilename: "Q8_0/mmproj-BF16.gguf".into(), + size: Some(2_000), + }, + HfApiSibling { + rfilename: "mmproj-F32.gguf".into(), + size: Some(3_000), + }, + ]; + + let mmproj = select_best_mmproj( + "someone/model-GGUF", + &files, + "Q4_K_M/model-Q4_K_M.gguf", + "Q4_K_M", + ) + .unwrap(); + + assert_eq!(mmproj.filename, "mmproj-F32.gguf"); + } } diff --git a/crates/goose/src/providers/local_inference/llamacpp/inference_emulated_tools.rs b/crates/goose/src/providers/local_inference/llamacpp/inference_emulated_tools.rs index 1f18612a68a3..bcde00435989 100644 --- a/crates/goose/src/providers/local_inference/llamacpp/inference_emulated_tools.rs +++ b/crates/goose/src/providers/local_inference/llamacpp/inference_emulated_tools.rs @@ -22,7 +22,6 @@ use crate::conversation::message::{Message, MessageContent}; use crate::providers::errors::ProviderError; -use llama_cpp_2::model::AddBos; use rmcp::model::{CallToolRequestParams, Tool}; use serde_json::json; use std::borrow::Cow; @@ -30,8 +29,8 @@ use uuid::Uuid; use super::super::{finalize_usage, StreamSender}; use super::inference_engine::{ - create_and_prefill_context, create_and_prefill_multimodal, generation_loop, - validate_and_compute_context, GenerationContext, TokenAction, + generation_loop, prepare_generation, GenerationContext, StopSuffixTrimmer, + ThinkingOutputFilter, TokenAction, }; const SHELL_TOOL: &str = "developer__shell"; @@ -355,56 +354,26 @@ fn send_emulator_action( pub(super) fn generate_with_emulated_tools( ctx: &mut GenerationContext<'_>, code_mode_enabled: bool, + oai_messages_json: &str, ) -> Result<(), ProviderError> { - // Use oaicompat variant — its C++ wrapper catches exceptions that would - // otherwise abort the process when other native libs disturb the C++ ABI. - let prompt = ctx - .loaded - .model - .apply_chat_template_with_tools_oaicompat( - &ctx.loaded.template, - ctx.chat_messages, - None, // no tools for emulated path - None, // no json_schema - true, // add_generation_prompt - ) - .map(|r| r.prompt) - .map_err(|e| { - ProviderError::ExecutionError(format!("Failed to apply chat template: {}", e)) - })?; - - let (mut llama_ctx, prompt_token_count, effective_ctx) = if !ctx.images.is_empty() { - create_and_prefill_multimodal( - ctx.loaded, - ctx.backend, - &prompt, - ctx.images, - ctx.context_limit, - ctx.settings, - )? - } else { - let tokens = ctx - .loaded - .model - .str_to_token(&prompt, AddBos::Never) - .map_err(|e| ProviderError::ExecutionError(e.to_string()))?; - let (ptc, ectx) = validate_and_compute_context( - ctx.loaded, - ctx.backend, - tokens.len(), - ctx.context_limit, - ctx.settings, - )?; - let lctx = - create_and_prefill_context(ctx.loaded, ctx.backend, &tokens, ectx, ctx.settings)?; - (lctx, ptc, ectx) - }; + let prepared = prepare_generation(ctx, oai_messages_json, None, None)?; + let template_result = prepared.template_result; + let mut llama_ctx = prepared.llama_ctx; + let prompt_token_count = prepared.prompt_token_count; + let effective_ctx = prepared.effective_ctx; let message_id = ctx.message_id; let tx = ctx.tx; let mut emulator_parser = StreamingEmulatorParser::new(code_mode_enabled); + let mut output_filter = ThinkingOutputFilter::new( + ctx.settings.enable_thinking, + &template_result.generation_prompt, + ); + let mut stop_trimmer = StopSuffixTrimmer::new(&template_result.additional_stops); + let mut generated_text = String::new(); let mut tool_call_emitted = false; let mut send_failed = false; + let mut stop_string_emitted = false; let output_token_count = generation_loop( &ctx.loaded.model, @@ -413,7 +382,10 @@ pub(super) fn generate_with_emulated_tools( prompt_token_count, effective_ctx, |piece| { - let actions = emulator_parser.process_chunk(piece); + generated_text.push_str(piece); + let filtered = output_filter.push_text(piece); + let (content, stop_seen) = stop_trimmer.push(&filtered.content); + let actions = emulator_parser.process_chunk(&content); for action in actions { match send_emulator_action(&action, message_id, tx) { Ok(is_tool) => { @@ -429,12 +401,47 @@ pub(super) fn generate_with_emulated_tools( } if tool_call_emitted { Ok(TokenAction::Stop) + } else if stop_seen + || template_result + .additional_stops + .iter() + .any(|stop| generated_text.ends_with(stop)) + { + stop_string_emitted = true; + Ok(TokenAction::Stop) } else { Ok(TokenAction::Continue) } }, )?; + if !send_failed { + let filtered = output_filter.finish(); + if !filtered.thinking.is_empty() { + let mut message = Message::assistant().with_thinking(filtered.thinking, ""); + message.id = Some(message_id.to_string()); + send_failed = tx.blocking_send(Ok((Some(message), None))).is_err(); + } + if !send_failed { + let content = if stop_string_emitted { + String::new() + } else { + let (content, stop_seen) = stop_trimmer.push(&filtered.content); + let mut content = content; + if !stop_seen { + content.push_str(&stop_trimmer.finish()); + } + content + }; + for action in emulator_parser.process_chunk(&content) { + if send_emulator_action(&action, message_id, tx).is_err() { + send_failed = true; + break; + } + } + } + } + if !send_failed { for action in emulator_parser.flush() { if send_emulator_action(&action, message_id, tx).is_err() { @@ -474,6 +481,50 @@ mod tests { parse_chunks(&[input], code_mode) } + fn trim_chunks(chunks: &[&str], stops: &[String]) -> (String, bool) { + let mut trimmer = StopSuffixTrimmer::new(stops); + let mut output = String::new(); + let mut stopped = false; + + for chunk in chunks { + let (content, stop_seen) = trimmer.push(chunk); + output.push_str(&content); + if stop_seen { + stopped = true; + break; + } + } + + if !stopped { + output.push_str(&trimmer.finish()); + } + + (output, stopped) + } + + fn parse_with_seeded_thinking( + chunks: &[&str], + code_mode: bool, + ) -> (String, Vec) { + let mut output_filter = ThinkingOutputFilter::new(true, "<|assistant|>\n"); + let mut parser = StreamingEmulatorParser::new(code_mode); + let mut thinking = String::new(); + let mut actions = Vec::new(); + + for chunk in chunks { + let filtered = output_filter.push_text(chunk); + thinking.push_str(&filtered.thinking); + actions.extend(parser.process_chunk(&filtered.content)); + } + + let filtered = output_filter.finish(); + thinking.push_str(&filtered.thinking); + actions.extend(parser.process_chunk(&filtered.content)); + actions.extend(parser.flush()); + + (thinking, actions) + } + fn assert_text(action: &EmulatorAction, expected: &str) { match action { EmulatorAction::Text(t) => assert_eq!(t.trim(), expected.trim(), "text mismatch"), @@ -507,6 +558,24 @@ mod tests { } } + #[test] + fn stop_suffix_trimmer_strips_split_stop() { + let stops = vec!["<|eom_id|>".to_string()]; + let (content, stopped) = trim_chunks(&["The answer", "<|e", "om_id|>"], &stops); + + assert!(stopped); + assert_eq!(content, "The answer"); + } + + #[test] + fn stop_suffix_trimmer_flushes_partial_non_stop() { + let stops = vec!["<|eom_id|>".to_string()]; + let (content, stopped) = trim_chunks(&["Use the <", " symbol"], &stops); + + assert!(!stopped); + assert_eq!(content, "Use the < symbol"); + } + #[test] fn plain_text_no_tools() { let actions = parse_all("Hello, world!", false); @@ -667,6 +736,25 @@ mod tests { assert_shell(shells[0], "echo hello"); } + #[test] + fn thinking_seeded_from_generation_prompt_is_not_emulated_text() { + let (thinking, actions) = + parse_with_seeded_thinking(&["reasoning\n$ echo hidden\nThe answer."], false); + + assert_eq!(thinking.trim(), "reasoning\n$ echo hidden"); + assert!(actions + .iter() + .all(|action| matches!(action, EmulatorAction::Text(_)))); + let text: String = actions + .iter() + .filter_map(|action| match action { + EmulatorAction::Text(text) => Some(text.as_str()), + _ => None, + }) + .collect(); + assert_eq!(text.trim(), "The answer."); + } + #[test] fn execute_block_with_multiline_code() { let input = "```execute_typescript\nasync function run() {\n const r = await Developer.shell({ command: \"ls\" });\n return r;\n}\n```\n"; diff --git a/crates/goose/src/providers/local_inference/llamacpp/inference_engine.rs b/crates/goose/src/providers/local_inference/llamacpp/inference_engine.rs index 86d2988c05bc..2a19f5fe7595 100644 --- a/crates/goose/src/providers/local_inference/llamacpp/inference_engine.rs +++ b/crates/goose/src/providers/local_inference/llamacpp/inference_engine.rs @@ -1,3 +1,4 @@ +use crate::providers::base::{FilterOut, ThinkFilter}; use crate::providers::errors::ProviderError; use crate::providers::local_inference::backend::LocalInferenceBackend; use crate::providers::local_inference::local_model_registry::ModelSettings; @@ -5,8 +6,9 @@ use crate::providers::local_inference::multimodal::ExtractedImage; use crate::providers::utils::RequestLog; use llama_cpp_2::context::params::LlamaContextParams; use llama_cpp_2::llama_batch::LlamaBatch; -use llama_cpp_2::model::{LlamaChatMessage, LlamaChatTemplate, LlamaModel}; +use llama_cpp_2::model::{AddBos, ChatTemplateResult, LlamaChatTemplate, LlamaModel}; use llama_cpp_2::mtmd::{MtmdBitmap, MtmdContext, MtmdInputText}; +use llama_cpp_2::openai::OpenAIChatTemplateParams; use llama_cpp_2::sampling::LlamaSampler; use std::num::NonZeroU32; @@ -16,7 +18,7 @@ use super::LlamaCppBackend; pub(super) struct GenerationContext<'a> { pub loaded: &'a LoadedModel, pub backend: &'a LlamaCppBackend, - pub chat_messages: &'a [LlamaChatMessage], + pub template: &'a LlamaChatTemplate, pub settings: &'a ModelSettings, pub context_limit: usize, pub model_name: String, @@ -28,11 +30,165 @@ pub(super) struct GenerationContext<'a> { pub(super) struct LoadedModel { pub model: LlamaModel, - pub template: LlamaChatTemplate, + pub templates: LoadedChatTemplates, /// Multimodal context for vision models. None for text-only models. pub mtmd_ctx: Option, } +pub(super) struct LoadedChatTemplates { + pub default: Option, + pub tool_use: Option, + pub force_default: bool, +} + +pub(super) struct PreparedGeneration<'model> { + pub template_result: ChatTemplateResult, + pub llama_ctx: llama_cpp_2::context::LlamaContext<'model>, + pub prompt_token_count: usize, + pub effective_ctx: usize, +} + +pub(super) struct ThinkingOutputFilter { + enabled: bool, + saw_structured_reasoning: bool, + think_filter: ThinkFilter, + pending_inline_thinking: String, + accumulated_thinking: String, +} + +impl ThinkingOutputFilter { + pub(super) fn new(enable_thinking: bool, generation_prompt: &str) -> Self { + let mut think_filter = ThinkFilter::new(); + if enable_thinking && !generation_prompt.is_empty() { + let _ = think_filter.push(generation_prompt); + } + + Self { + enabled: enable_thinking, + saw_structured_reasoning: false, + think_filter, + pending_inline_thinking: String::new(), + accumulated_thinking: String::new(), + } + } + + pub(super) fn push_structured_reasoning(&mut self, reasoning: &str) -> Option { + if reasoning.is_empty() { + return None; + } + + self.saw_structured_reasoning = true; + self.pending_inline_thinking.clear(); + self.think_filter = ThinkFilter::new(); + self.accumulated_thinking.push_str(reasoning); + Some(reasoning.to_string()) + } + + pub(super) fn push_text(&mut self, text: &str) -> FilterOut { + if !self.enabled { + return FilterOut { + content: text.to_string(), + thinking: String::new(), + }; + } + + let mut filtered = self.think_filter.push(text); + if self.saw_structured_reasoning { + filtered.thinking.clear(); + } else if !filtered.thinking.is_empty() { + self.pending_inline_thinking.push_str(&filtered.thinking); + filtered.thinking.clear(); + } + filtered + } + + pub(super) fn finish(&mut self) -> FilterOut { + let mut filtered = if self.enabled && !self.saw_structured_reasoning { + std::mem::take(&mut self.think_filter).finish() + } else { + FilterOut::default() + }; + + if !self.saw_structured_reasoning { + let mut thinking = std::mem::take(&mut self.pending_inline_thinking); + thinking.push_str(&filtered.thinking); + if !thinking.is_empty() { + self.accumulated_thinking.push_str(&thinking); + } + filtered.thinking = thinking; + } else { + filtered.thinking.clear(); + } + + filtered + } + + pub(super) fn accumulated_thinking(&self) -> &str { + &self.accumulated_thinking + } +} + +pub(super) struct StopSuffixTrimmer { + pending: String, + stops: Vec, +} + +impl StopSuffixTrimmer { + pub(super) fn new(stops: &[String]) -> Self { + Self { + pending: String::new(), + stops: stops + .iter() + .filter(|stop| !stop.is_empty()) + .cloned() + .collect(), + } + } + + pub(super) fn push(&mut self, chunk: &str) -> (String, bool) { + if self.stops.is_empty() { + return (chunk.to_string(), false); + } + + self.pending.push_str(chunk); + + if let Some(stop) = self + .stops + .iter() + .filter(|stop| self.pending.ends_with(stop.as_str())) + .max_by_key(|stop| stop.len()) + { + let emit_len = self.pending.len() - stop.len(); + let _stop = self.pending.split_off(emit_len); + let emit = std::mem::take(&mut self.pending); + return (emit, true); + } + + let hold_len = self + .pending + .char_indices() + .map(|(idx, _)| idx) + .chain(std::iter::once(self.pending.len())) + .filter(|idx| { + self.pending + .get(*idx..) + .is_some_and(|suffix| self.stops.iter().any(|stop| stop.starts_with(suffix))) + }) + .map(|idx| self.pending.len() - idx) + .max() + .unwrap_or(0); + + let emit_len = self.pending.len() - hold_len; + let keep = self.pending.split_off(emit_len); + let emit = std::mem::replace(&mut self.pending, keep); + (emit, false) + } + + pub(super) fn finish(&mut self) -> String { + std::mem::take(&mut self.pending) + } +} + /// Estimate the maximum context length that can fit in available accelerator/CPU /// memory based on the model's KV cache requirements. /// @@ -349,6 +505,121 @@ pub(super) fn create_and_prefill_multimodal<'model>( Ok((llama_ctx, prompt_token_count, effective_ctx)) } +pub(super) fn prepare_generation<'model>( + ctx: &mut GenerationContext<'model>, + oai_messages_json: &str, + full_tools_json: Option<&str>, + compact_tools_json: Option<&str>, +) -> Result, ProviderError> { + let apply_template = |tools: Option<&str>| { + let params = OpenAIChatTemplateParams { + messages_json: oai_messages_json, + tools_json: tools, + tool_choice: None, + json_schema: None, + grammar: None, + reasoning_format: if ctx.settings.enable_thinking { + Some("auto") + } else { + None + }, + chat_template_kwargs: None, + add_generation_prompt: true, + use_jinja: true, + parallel_tool_calls: false, + enable_thinking: ctx.settings.enable_thinking, + add_bos: false, + add_eos: false, + parse_tool_calls: true, + }; + ctx.loaded + .model + .apply_chat_template_oaicompat(ctx.template, ¶ms) + }; + + let min_generation_headroom = 512; + let n_ctx_train = ctx.loaded.model.n_ctx_train() as usize; + let mmproj_overhead = if ctx.loaded.mtmd_ctx.is_some() { + ctx.settings.mmproj_size_bytes + } else { + 0 + }; + let memory_max_ctx = + estimate_max_context_for_memory(&ctx.loaded.model, ctx.backend, mmproj_overhead); + let cap = context_cap(ctx.settings, ctx.context_limit, n_ctx_train, memory_max_ctx); + let token_budget = cap.saturating_sub(min_generation_headroom); + let estimated_image_tokens = ctx.images.len() * ctx.settings.image_token_estimate; + + let template_result = match apply_template(full_tools_json) { + Ok(r) => { + let token_count = ctx + .loaded + .model + .str_to_token(&r.prompt, AddBos::Never) + .map(|t| t.len()) + .unwrap_or(0); + if token_count + estimated_image_tokens > token_budget { + apply_template(compact_tools_json).unwrap_or(r) + } else { + r + } + } + Err(e) => { + tracing::warn!( + error = %e, + "Failed to apply llama.cpp OpenAI-compatible chat template" + ); + match apply_template(compact_tools_json) { + Ok(r) => r, + Err(compact_err) => { + return Err(ProviderError::ExecutionError(format!( + "Failed to apply chat template with llama.cpp's Jinja renderer. This usually means the selected built-in template name does not exist, the embedded or custom template is invalid, or the template is incompatible with the current message shape. Select a valid llama.cpp built-in template name, configure a custom inline Jinja template, or use a GGUF with valid tokenizer.chat_template metadata. Full tools error: {e}; compact tools error: {compact_err}" + ))); + } + } + } + }; + + let _ = ctx.log.write( + &serde_json::json!({"applied_prompt": &template_result.prompt}), + None, + ); + + let (llama_ctx, prompt_token_count, effective_ctx) = if !ctx.images.is_empty() { + create_and_prefill_multimodal( + ctx.loaded, + ctx.backend, + &template_result.prompt, + ctx.images, + ctx.context_limit, + ctx.settings, + )? + } else { + let tokens = ctx + .loaded + .model + .str_to_token(&template_result.prompt, AddBos::Never) + .map_err(|e| ProviderError::ExecutionError(e.to_string()))?; + let (ptc, ectx) = validate_and_compute_context( + ctx.loaded, + ctx.backend, + tokens.len(), + ctx.context_limit, + ctx.settings, + )?; + let lctx = + create_and_prefill_context(ctx.loaded, ctx.backend, &tokens, ectx, ctx.settings)?; + (lctx, ptc, ectx) + }; + + Ok(PreparedGeneration { + template_result, + llama_ctx, + prompt_token_count, + effective_ctx, + }) +} + /// Action to take after processing a generated token piece. pub(super) enum TokenAction { Continue, diff --git a/crates/goose/src/providers/local_inference/llamacpp/inference_native_tools.rs b/crates/goose/src/providers/local_inference/llamacpp/inference_native_tools.rs index 662b80244549..501ad6b8497c 100644 --- a/crates/goose/src/providers/local_inference/llamacpp/inference_native_tools.rs +++ b/crates/goose/src/providers/local_inference/llamacpp/inference_native_tools.rs @@ -1,7 +1,5 @@ use crate::conversation::message::{Message, MessageContent}; use crate::providers::errors::ProviderError; -use llama_cpp_2::model::AddBos; -use llama_cpp_2::openai::OpenAIChatTemplateParams; use rmcp::model::CallToolRequestParams; use serde_json::Value; use std::borrow::Cow; @@ -9,121 +7,27 @@ use uuid::Uuid; use super::super::finalize_usage; use super::inference_engine::{ - context_cap, create_and_prefill_context, create_and_prefill_multimodal, - estimate_max_context_for_memory, generation_loop, validate_and_compute_context, - GenerationContext, TokenAction, + generation_loop, prepare_generation, GenerationContext, StopSuffixTrimmer, + ThinkingOutputFilter, TokenAction, }; pub(super) fn generate_with_native_tools( ctx: &mut GenerationContext<'_>, - oai_messages_json: &Option, + oai_messages_json: &str, full_tools_json: Option<&str>, compact_tools: Option<&str>, ) -> Result<(), ProviderError> { - let min_generation_headroom = 512; - let n_ctx_train = ctx.loaded.model.n_ctx_train() as usize; - let mmproj_overhead = if ctx.loaded.mtmd_ctx.is_some() { - ctx.settings.mmproj_size_bytes - } else { - 0 - }; - let memory_max_ctx = - estimate_max_context_for_memory(&ctx.loaded.model, ctx.backend, mmproj_overhead); - let cap = context_cap(ctx.settings, ctx.context_limit, n_ctx_train, memory_max_ctx); - let token_budget = cap.saturating_sub(min_generation_headroom); - - let apply_template = |tools: Option<&str>| { - if let Some(ref messages_json) = oai_messages_json { - let params = OpenAIChatTemplateParams { - messages_json: messages_json.as_str(), - tools_json: tools, - tool_choice: None, - json_schema: None, - grammar: None, - reasoning_format: if ctx.settings.enable_thinking { - Some("auto") - } else { - None - }, - chat_template_kwargs: None, - add_generation_prompt: true, - use_jinja: true, - parallel_tool_calls: false, - enable_thinking: ctx.settings.enable_thinking, - add_bos: false, - add_eos: false, - parse_tool_calls: true, - }; - ctx.loaded - .model - .apply_chat_template_oaicompat(&ctx.loaded.template, ¶ms) - } else { - ctx.loaded.model.apply_chat_template_with_tools_oaicompat( - &ctx.loaded.template, - ctx.chat_messages, - tools, - None, - true, - ) - } - }; - - let estimated_image_tokens = ctx.images.len() * ctx.settings.image_token_estimate; - - let template_result = match apply_template(full_tools_json) { - Ok(r) => { - let token_count = ctx - .loaded - .model - .str_to_token(&r.prompt, AddBos::Never) - .map(|t| t.len()) - .unwrap_or(0); - if token_count + estimated_image_tokens > token_budget { - apply_template(compact_tools).unwrap_or(r) - } else { - r - } - } - Err(_) => apply_template(compact_tools).map_err(|e| { - ProviderError::ExecutionError(format!("Failed to apply chat template: {}", e)) - })?, - }; - - let _ = ctx.log.write( - &serde_json::json!({"applied_prompt": &template_result.prompt}), - None, - ); - - let (mut llama_ctx, prompt_token_count, effective_ctx) = if !ctx.images.is_empty() { - create_and_prefill_multimodal( - ctx.loaded, - ctx.backend, - &template_result.prompt, - ctx.images, - ctx.context_limit, - ctx.settings, - )? - } else { - let tokens = ctx - .loaded - .model - .str_to_token(&template_result.prompt, AddBos::Never) - .map_err(|e| ProviderError::ExecutionError(e.to_string()))?; - let (ptc, ectx) = validate_and_compute_context( - ctx.loaded, - ctx.backend, - tokens.len(), - ctx.context_limit, - ctx.settings, - )?; - let lctx = - create_and_prefill_context(ctx.loaded, ctx.backend, &tokens, ectx, ctx.settings)?; - (lctx, ptc, ectx) - }; + let prepared = prepare_generation(ctx, oai_messages_json, full_tools_json, compact_tools)?; + let template_result = prepared.template_result; + let mut llama_ctx = prepared.llama_ctx; + let prompt_token_count = prepared.prompt_token_count; + let effective_ctx = prepared.effective_ctx; let message_id = ctx.message_id; let tx = ctx.tx; let mut generated_text = String::new(); + let mut stop_trimmer = StopSuffixTrimmer::new(&template_result.additional_stops); + let mut stop_string_emitted = false; // Initialize streaming parser — handles thinking tokens, tool calls, etc. let mut stream_parser = template_result.streaming_state_oaicompat().map_err(|e| { @@ -141,7 +45,10 @@ pub(super) fn generate_with_native_tools( // Accumulate thinking/reasoning across the entire generation so we can // attach it to the final tool-call message (mirroring what the OpenAI // streaming path does). Streaming chunks are still sent for UI display. - let mut accumulated_thinking = String::new(); + let mut output_filter = ThinkingOutputFilter::new( + ctx.settings.enable_thinking, + &template_result.generation_prompt, + ); let output_token_count = generation_loop( &ctx.loaded.model, @@ -151,6 +58,7 @@ pub(super) fn generate_with_native_tools( effective_ctx, |piece| { generated_text.push_str(piece); + let mut stop_seen = false; // Feed the new piece to the streaming parser match stream_parser.update(piece, true) { @@ -161,9 +69,10 @@ pub(super) fn generate_with_native_tools( if let Some(reasoning) = delta.get("reasoning_content").and_then(|v| v.as_str()) { - if !reasoning.is_empty() { - accumulated_thinking.push_str(reasoning); - let mut msg = Message::assistant().with_thinking(reasoning, ""); + if let Some(thinking) = + output_filter.push_structured_reasoning(reasoning) + { + let mut msg = Message::assistant().with_thinking(thinking, ""); msg.id = Some(message_id.to_string()); if tx.blocking_send(Ok((Some(msg), None))).is_err() { return Ok(TokenAction::Stop); @@ -173,10 +82,15 @@ pub(super) fn generate_with_native_tools( // Stream content text to the UI if let Some(content) = delta.get("content").and_then(|v| v.as_str()) { if !content.is_empty() { - let mut msg = Message::assistant().with_text(content); - msg.id = Some(message_id.to_string()); - if tx.blocking_send(Ok((Some(msg), None))).is_err() { - return Ok(TokenAction::Stop); + let filtered = output_filter.push_text(content); + let (content, seen) = stop_trimmer.push(&filtered.content); + stop_seen |= seen; + if !content.is_empty() { + let mut msg = Message::assistant().with_text(content); + msg.id = Some(message_id.to_string()); + if tx.blocking_send(Ok((Some(msg), None))).is_err() { + return Ok(TokenAction::Stop); + } } } } @@ -193,19 +107,26 @@ pub(super) fn generate_with_native_tools( } Err(e) => { tracing::warn!("Streaming parser error: {}", e); - let mut msg = Message::assistant().with_text(piece); - msg.id = Some(message_id.to_string()); - if tx.blocking_send(Ok((Some(msg), None))).is_err() { - return Ok(TokenAction::Stop); + let filtered = output_filter.push_text(piece); + let (content, seen) = stop_trimmer.push(&filtered.content); + stop_seen |= seen; + if !content.is_empty() { + let mut msg = Message::assistant().with_text(content); + msg.id = Some(message_id.to_string()); + if tx.blocking_send(Ok((Some(msg), None))).is_err() { + return Ok(TokenAction::Stop); + } } } } - let should_stop = template_result - .additional_stops - .iter() - .any(|stop| generated_text.ends_with(stop)); + let should_stop = stop_seen + || template_result + .additional_stops + .iter() + .any(|stop| generated_text.ends_with(stop)); if should_stop { + stop_string_emitted = true; Ok(TokenAction::Stop) } else { Ok(TokenAction::Continue) @@ -218,18 +139,22 @@ pub(super) fn generate_with_native_tools( for delta_json in final_deltas { if let Ok(delta) = serde_json::from_str::(&delta_json) { if let Some(reasoning) = delta.get("reasoning_content").and_then(|v| v.as_str()) { - if !reasoning.is_empty() { - accumulated_thinking.push_str(reasoning); - let mut msg = Message::assistant().with_thinking(reasoning, ""); + if let Some(thinking) = output_filter.push_structured_reasoning(reasoning) { + let mut msg = Message::assistant().with_thinking(thinking, ""); msg.id = Some(message_id.to_string()); let _ = tx.blocking_send(Ok((Some(msg), None))); } } if let Some(content) = delta.get("content").and_then(|v| v.as_str()) { if !content.is_empty() { - let mut msg = Message::assistant().with_text(content); - msg.id = Some(message_id.to_string()); - let _ = tx.blocking_send(Ok((Some(msg), None))); + let filtered = output_filter.push_text(content); + let (content, stop_seen) = stop_trimmer.push(&filtered.content); + stop_string_emitted |= stop_seen; + if !content.is_empty() { + let mut msg = Message::assistant().with_text(content); + msg.id = Some(message_id.to_string()); + let _ = tx.blocking_send(Ok((Some(msg), None))); + } } } if let Some(tool_calls) = delta.get("tool_calls").and_then(|v| v.as_array()) { @@ -241,6 +166,28 @@ pub(super) fn generate_with_native_tools( } } + let filtered = output_filter.finish(); + if !filtered.thinking.is_empty() { + let mut msg = Message::assistant().with_thinking(&filtered.thinking, ""); + msg.id = Some(message_id.to_string()); + let _ = tx.blocking_send(Ok((Some(msg), None))); + } + let content = if stop_string_emitted { + String::new() + } else { + let (content, stop_seen) = stop_trimmer.push(&filtered.content); + let mut content = content; + if !stop_seen { + content.push_str(&stop_trimmer.finish()); + } + content + }; + if !content.is_empty() { + let mut msg = Message::assistant().with_text(content); + msg.id = Some(message_id.to_string()); + let _ = tx.blocking_send(Ok((Some(msg), None))); + } + // Build a single message combining thinking + all tool calls, mirroring // the structure produced by the OpenAI streaming path. The agent relies // on this combined message to: @@ -250,8 +197,11 @@ pub(super) fn generate_with_native_tools( let tool_call_contents = extract_oai_tool_call_contents(&accumulated_tool_calls); if !tool_call_contents.is_empty() { let mut contents: Vec = Vec::new(); - if !accumulated_thinking.is_empty() { - contents.push(MessageContent::thinking(&accumulated_thinking, "")); + if !output_filter.accumulated_thinking().is_empty() { + contents.push(MessageContent::thinking( + output_filter.accumulated_thinking(), + "", + )); } contents.extend(tool_call_contents); let mut msg = Message::new( diff --git a/crates/goose/src/providers/local_inference/llamacpp/mod.rs b/crates/goose/src/providers/local_inference/llamacpp/mod.rs index 0b4141ad72fc..4e076f459325 100644 --- a/crates/goose/src/providers/local_inference/llamacpp/mod.rs +++ b/crates/goose/src/providers/local_inference/llamacpp/mod.rs @@ -3,35 +3,312 @@ mod inference_engine; mod inference_native_tools; use std::any::Any; +use std::ffi::CStr; use std::path::PathBuf; use anyhow::Result; use llama_cpp_2::llama_backend::LlamaBackend; use llama_cpp_2::model::params::LlamaModelParams; -use llama_cpp_2::model::{LlamaChatMessage, LlamaChatTemplate, LlamaModel}; +use llama_cpp_2::model::{ChatTemplateResult, LlamaChatTemplate, LlamaModel}; +use llama_cpp_2::openai::OpenAIChatTemplateParams; use llama_cpp_2::{list_llama_ggml_backend_devices, LlamaBackendDeviceType, LogOptions}; -use rmcp::model::Role; use self::inference_emulated_tools::{ build_emulator_tool_description, generate_with_emulated_tools, load_tiny_model_prompt, }; -use self::inference_engine::{GenerationContext, LoadedModel}; +use self::inference_engine::{GenerationContext, LoadedChatTemplates, LoadedModel}; use self::inference_native_tools::generate_with_native_tools; use crate::providers::errors::ProviderError; use crate::providers::formats::openai::format_tools; use crate::providers::local_inference::backend::{ BackendLoadedModel, LocalGenerationRequest, LocalInferenceBackend, }; +use crate::providers::local_inference::local_model_registry::{ + ChatTemplate, ModelSettings, ToolCallingMode, +}; use crate::providers::local_inference::multimodal::ExtractedImage; use crate::providers::local_inference::tool_parsing::compact_tools_json; use crate::providers::local_inference::{ - build_openai_messages_json, extract_text_content, ResolvedModelPaths, + build_openai_messages_json, build_openai_text_messages_json, ResolvedModelPaths, }; pub(super) const LLAMACPP_BACKEND_ID: &str = "llamacpp"; const CODE_EXECUTION_TOOL: &str = "code_execution__execute_typescript"; +pub(super) fn builtin_chat_template_names() -> Vec { + let count = unsafe { llama_cpp_sys_2::llama_chat_builtin_templates(std::ptr::null_mut(), 0) }; + if count <= 0 { + return Vec::new(); + } + + let mut templates = vec![std::ptr::null(); count as usize]; + let written = unsafe { + llama_cpp_sys_2::llama_chat_builtin_templates(templates.as_mut_ptr(), templates.len()) + }; + templates.truncate(written.max(0) as usize); + + templates + .into_iter() + .filter(|ptr| !ptr.is_null()) + .filter_map(|ptr| { + unsafe { CStr::from_ptr(ptr) } + .to_str() + .ok() + .map(str::to_string) + }) + .collect() +} + +fn template_result_supports_native_tool_calling(result: &ChatTemplateResult) -> bool { + result.parse_tool_calls + && result + .parser + .as_deref() + .is_some_and(|parser| !parser.trim().is_empty()) +} + +fn supports_native_tool_calling( + loaded: &LoadedModel, + settings: &ModelSettings, + template: &LlamaChatTemplate, + oai_messages_json: &str, + tools_json: Option<&str>, +) -> bool { + let Some(tools_json) = tools_json.filter(|tools| !tools.trim().is_empty()) else { + return false; + }; + + // llama.cpp exposes common_chat_templates_get_caps in C++, but llama-cpp-2 + // 0.1.146 does not bind it yet. Replace this dry-run with that capability + // map once it is available through the Rust wrapper. + let params = OpenAIChatTemplateParams { + messages_json: oai_messages_json, + tools_json: Some(tools_json), + tool_choice: None, + json_schema: None, + grammar: None, + reasoning_format: if settings.enable_thinking { + Some("auto") + } else { + None + }, + chat_template_kwargs: None, + add_generation_prompt: true, + use_jinja: true, + parallel_tool_calls: false, + enable_thinking: settings.enable_thinking, + add_bos: false, + add_eos: false, + parse_tool_calls: true, + }; + + match loaded + .model + .apply_chat_template_oaicompat(template, ¶ms) + { + Ok(result) => template_result_supports_native_tool_calling(&result), + Err(e) => { + tracing::debug!( + error = %e, + "llama.cpp chat template dry-run did not support native tool calling" + ); + false + } + } +} + +fn should_use_native_tool_calling( + mode: ToolCallingMode, + has_tools: bool, + template_supports_native: bool, +) -> bool { + has_tools + && match mode { + ToolCallingMode::Auto => template_supports_native, + ToolCallingMode::ForceNative => true, + ToolCallingMode::ForceEmulated => false, + } +} + +fn is_legacy_builtin_template_name(template: &str) -> bool { + matches!( + template.trim(), + "bailing" + | "bailing-think" + | "bailing2" + | "chatglm3" + | "chatglm4" + | "command-r" + | "deepseek" + | "deepseek-ocr" + | "deepseek2" + | "deepseek3" + | "exaone-moe" + | "exaone3" + | "exaone4" + | "falcon3" + | "gemma" + | "gigachat" + | "glmedge" + | "gpt-oss" + | "granite" + | "granite-4.0" + | "grok-2" + | "hunyuan-dense" + | "hunyuan-moe" + | "hunyuan-ocr" + | "kimi-k2" + | "llama2" + | "llama2-sys" + | "llama2-sys-bos" + | "llama2-sys-strip" + | "llama3" + | "llama4" + | "megrez" + | "minicpm" + | "mistral-v1" + | "mistral-v3" + | "mistral-v3-tekken" + | "mistral-v7" + | "mistral-v7-tekken" + | "monarch" + | "openchat" + | "orion" + | "pangu-embedded" + | "phi3" + | "phi4" + | "rwkv-world" + | "seed_oss" + | "smolvlm" + | "solar-open" + | "vicuna" + | "vicuna-orca" + | "yandex" + | "zephyr" + ) +} + +fn missing_chat_template_error( + model_id: &str, + architecture: Option<&str>, + context: &str, + has_tool_use_template: bool, +) -> ProviderError { + let architecture = architecture + .map(str::trim) + .filter(|arch| !arch.is_empty()) + .map(|arch| format!(" Detected GGUF general.architecture={arch}.")) + .unwrap_or_default(); + let tool_use_note = if has_tool_use_template { + " A named tool_use chat template is present, but that template is only used for native tool calls with tools present." + } else { + "" + }; + + ProviderError::ExecutionError(format!( + "Model {model_id} does not contain GGUF tokenizer.chat_template metadata required for {context}.{architecture}{tool_use_note} \ + Goose cannot safely infer the correct prompt format from architecture alone. Select a \ + llama.cpp built-in chat template name, configure a custom inline chat template containing \ + the full Jinja template source, or use a GGUF that includes tokenizer.chat_template metadata." + )) +} + +fn load_chat_templates( + model: &LlamaModel, + settings: &ModelSettings, +) -> Result { + match &settings.chat_template { + ChatTemplate::Embedded => Ok(LoadedChatTemplates { + default: model.chat_template(None).ok(), + tool_use: model.chat_template(Some("tool_use")).ok(), + force_default: false, + }), + ChatTemplate::Builtin { name } => { + let trimmed = name.trim(); + if trimmed.is_empty() { + return Err(ProviderError::ExecutionError( + "Built-in chat template name is empty. Enter a llama.cpp built-in template name such as 'chatml', or use embedded chat template metadata.".to_string(), + )); + } + LlamaChatTemplate::new(trimmed) + .map_err(|e| { + ProviderError::ExecutionError(format!( + "Built-in chat template name contains an invalid NUL byte: {e}" + )) + }) + .map(|template| LoadedChatTemplates { + default: Some(template), + tool_use: None, + force_default: true, + }) + } + ChatTemplate::CustomInline { template } => { + let trimmed = template.trim(); + if trimmed.is_empty() { + return Err(ProviderError::ExecutionError( + "Custom inline chat template is empty. Paste the full Jinja chat template source, use a llama.cpp built-in template name, or use embedded chat template metadata.".to_string(), + )); + } + if trimmed == "chatml" || is_legacy_builtin_template_name(trimmed) { + return Err(ProviderError::ExecutionError(format!( + "Custom inline chat template is set to '{trimmed}', which is a llama.cpp template name rather than Jinja template source. Paste the full Jinja chat template source instead, or select Built-in and enter '{trimmed}' if that built-in template is intended." + ))); + } + LlamaChatTemplate::new(template) + .map_err(|e| { + ProviderError::ExecutionError(format!( + "Custom inline chat template contains an invalid NUL byte: {e}" + )) + }) + .map(|template| LoadedChatTemplates { + default: Some(template), + tool_use: None, + force_default: true, + }) + } + } +} + +fn select_generation_template<'a>( + model_id: &str, + model: &LlamaModel, + templates: &'a LoadedChatTemplates, + native_tool_calling: bool, + has_tools: bool, +) -> Result<&'a LlamaChatTemplate, ProviderError> { + if templates.force_default { + return templates.default.as_ref().ok_or_else(|| { + ProviderError::ExecutionError( + "Configured chat template was not loaded correctly".to_string(), + ) + }); + } + + if native_tool_calling && has_tools { + if let Some(template) = templates.tool_use.as_ref() { + return Ok(template); + } + } + + templates.default.as_ref().ok_or_else(|| { + let architecture = model.meta_val_str("general.architecture").ok(); + let context = if has_tools && native_tool_calling { + "native tool calling because no tool_use template is available" + } else if has_tools { + "emulated tool calling" + } else { + "chat without tools" + }; + missing_chat_template_error( + model_id, + architecture.as_deref(), + context, + templates.tool_use.is_some(), + ) + }) +} + pub(super) struct LlamaCppBackend { backend: LlamaBackend, } @@ -134,18 +411,7 @@ impl LocalInferenceBackend for LlamaCppBackend { let model = LlamaModel::load_from_file(&self.backend, model_path, ¶ms) .map_err(|e| ProviderError::ExecutionError(e.to_string()))?; - let template = match model.chat_template(None) { - Ok(t) => t, - Err(_) => { - tracing::warn!("Model has no embedded chat template, falling back to chatml"); - LlamaChatTemplate::new("chatml").map_err(|e| { - ProviderError::ExecutionError(format!( - "Failed to create fallback chat template: {}", - e - )) - })? - } - }; + let templates = load_chat_templates(&model, settings)?; let mtmd_ctx = Self::init_mtmd_context(&model, &resolved.mmproj_path, settings); @@ -157,7 +423,7 @@ impl LocalInferenceBackend for LlamaCppBackend { Ok(Box::new(LoadedModel { model, - template, + templates, mtmd_ctx, })) } @@ -174,14 +440,6 @@ impl LocalInferenceBackend for LlamaCppBackend { ProviderError::ExecutionError("Loaded model backend mismatch".to_string()) })?; - let native_tool_calling = request.settings.native_tool_calling; - let use_emulator = !native_tool_calling && !request.tools.is_empty(); - let system_prompt = if use_emulator { - load_tiny_model_prompt() - } else { - request.system.to_string() - }; - let has_vision = request.resolved_model.mmproj_path.is_some(); let marker = llama_cpp_2::mtmd::mtmd_default_marker(); let (images, vision_messages): (Vec, Option>) = if has_vision { @@ -193,45 +451,8 @@ impl LocalInferenceBackend for LlamaCppBackend { }; let effective_messages = vision_messages.as_deref().unwrap_or(request.messages); - let mut chat_messages = - vec![ - LlamaChatMessage::new("system".to_string(), system_prompt.clone()).map_err( - |e| { - ProviderError::ExecutionError(format!( - "Failed to create system message: {}", - e - )) - }, - )?, - ]; - let code_mode_enabled = request.tools.iter().any(|t| t.name == CODE_EXECUTION_TOOL); - - if use_emulator && !request.tools.is_empty() { - let tool_desc = build_emulator_tool_description(request.tools, code_mode_enabled); - chat_messages = vec![LlamaChatMessage::new( - "system".to_string(), - format!("{}{}", system_prompt, tool_desc), - ) - .map_err(|e| { - ProviderError::ExecutionError(format!("Failed to create system message: {}", e)) - })?]; - } - - for msg in effective_messages { - let role = match msg.role { - Role::User => "user", - Role::Assistant => "assistant", - }; - let content = extract_text_content(msg); - if !content.trim().is_empty() { - chat_messages.push(LlamaChatMessage::new(role.to_string(), content).map_err( - |e| ProviderError::ExecutionError(format!("Failed to create message: {}", e)), - )?); - } - } - - let (full_tools_json, compact_tools) = if !use_emulator && !request.tools.is_empty() { + let (full_tools_json, compact_tools) = if !request.tools.is_empty() { let full = format_tools(request.tools) .ok() .and_then(|spec| serde_json::to_string(&spec).ok()); @@ -241,13 +462,53 @@ impl LocalInferenceBackend for LlamaCppBackend { (None, None) }; - let oai_messages_json = if request.settings.use_jinja || native_tool_calling { - Some(build_openai_messages_json( - &system_prompt, - effective_messages, - )) + let has_native_tool_payload = full_tools_json + .as_deref() + .is_some_and(|tools| !tools.trim().is_empty()); + let template_supports_native = + if matches!(request.settings.tool_calling, ToolCallingMode::Auto) + && has_native_tool_payload + { + let messages_json = build_openai_messages_json(request.system, effective_messages); + if let Some(template) = loaded.templates.tool_use.as_ref() { + supports_native_tool_calling( + loaded, + request.settings, + template, + &messages_json, + full_tools_json.as_deref(), + ) + } else { + loaded.templates.default.as_ref().is_some_and(|template| { + supports_native_tool_calling( + loaded, + request.settings, + template, + &messages_json, + full_tools_json.as_deref(), + ) + }) + } + } else { + false + }; + let native_tool_calling = should_use_native_tool_calling( + request.settings.tool_calling, + !request.tools.is_empty(), + template_supports_native, + ); + let use_emulator = !native_tool_calling && !request.tools.is_empty(); + let system_prompt = if use_emulator { + let tool_desc = build_emulator_tool_description(request.tools, code_mode_enabled); + format!("{}{}", load_tiny_model_prompt(), tool_desc) } else { - None + request.system.to_string() + }; + + let oai_messages_json = if use_emulator { + build_openai_text_messages_json(&system_prompt, effective_messages) + } else { + build_openai_messages_json(&system_prompt, effective_messages) }; if !images.is_empty() && loaded.mtmd_ctx.is_none() { @@ -258,10 +519,18 @@ impl LocalInferenceBackend for LlamaCppBackend { ); } + let template = select_generation_template( + &request.model_name, + &loaded.model, + &loaded.templates, + native_tool_calling, + !request.tools.is_empty(), + )?; + let mut gen_ctx = GenerationContext { loaded, backend: self, - chat_messages: &chat_messages, + template, settings: request.settings, context_limit: request.context_limit, model_name: request.model_name, @@ -272,7 +541,7 @@ impl LocalInferenceBackend for LlamaCppBackend { }; if use_emulator { - generate_with_emulated_tools(&mut gen_ctx, code_mode_enabled) + generate_with_emulated_tools(&mut gen_ctx, code_mode_enabled, &oai_messages_json) } else { generate_with_native_tools( &mut gen_ctx, @@ -353,3 +622,78 @@ fn log_inference_backend_devices() { ); } } + +#[cfg(test)] +mod tests { + use super::*; + + fn template_result(parser: Option<&str>, parse_tool_calls: bool) -> ChatTemplateResult { + ChatTemplateResult { + prompt: String::new(), + grammar: None, + grammar_lazy: false, + grammar_triggers: Vec::new(), + preserved_tokens: Vec::new(), + additional_stops: Vec::new(), + chat_format: 0, + parser: parser.map(str::to_string), + generation_prompt: String::new(), + parse_tool_calls, + } + } + + #[test] + fn native_tool_calling_requires_generated_parser() { + assert!(template_result_supports_native_tool_calling( + &template_result(Some("parser"), true) + )); + assert!(!template_result_supports_native_tool_calling( + &template_result(None, true) + )); + assert!(!template_result_supports_native_tool_calling( + &template_result(Some("parser"), false) + )); + assert!(!template_result_supports_native_tool_calling( + &template_result(Some(" "), true) + )); + } + + #[test] + fn tool_calling_mode_controls_path_selection() { + assert!(should_use_native_tool_calling( + ToolCallingMode::Auto, + true, + true + )); + assert!(!should_use_native_tool_calling( + ToolCallingMode::Auto, + true, + false + )); + assert!(should_use_native_tool_calling( + ToolCallingMode::ForceNative, + true, + false + )); + assert!(!should_use_native_tool_calling( + ToolCallingMode::ForceEmulated, + true, + true + )); + assert!(!should_use_native_tool_calling( + ToolCallingMode::ForceNative, + false, + true + )); + } + + #[test] + fn rejects_legacy_builtin_names_as_inline_templates() { + assert!(is_legacy_builtin_template_name("gemma")); + assert!(is_legacy_builtin_template_name("llama3")); + assert!(!is_legacy_builtin_template_name("chatml")); + assert!(!is_legacy_builtin_template_name( + "{% for message in messages %}{{ message.content }}{% endfor %}" + )); + } +} diff --git a/crates/goose/src/providers/local_inference/local_model_registry.rs b/crates/goose/src/providers/local_inference/local_model_registry.rs index b6a4a6f306f7..abe9ee635568 100644 --- a/crates/goose/src/providers/local_inference/local_model_registry.rs +++ b/crates/goose/src/providers/local_inference/local_model_registry.rs @@ -36,6 +36,29 @@ impl Default for SamplingConfig { } } +#[derive(Debug, Clone, Copy, Default, PartialEq, Eq, Serialize, Deserialize, ToSchema)] +#[serde(rename_all = "snake_case")] +pub enum ToolCallingMode { + #[default] + Auto, + ForceNative, + ForceEmulated, +} + +#[derive(Debug, Clone, Default, Hash, PartialEq, Eq, Serialize, Deserialize, ToSchema)] +#[serde(tag = "type", rename_all = "snake_case")] +pub enum ChatTemplate { + #[serde(alias = "auto")] + #[default] + Embedded, + Builtin { + name: String, + }, + CustomInline { + template: String, + }, +} + #[derive(Debug, Clone, Serialize, Deserialize, ToSchema)] pub struct ModelSettings { pub context_size: Option, @@ -57,13 +80,13 @@ pub struct ModelSettings { pub flash_attention: Option, pub n_threads: Option, #[serde(default)] - pub native_tool_calling: bool, + pub tool_calling: ToolCallingMode, #[serde(default)] - pub use_jinja: bool, + pub chat_template: ChatTemplate, #[serde(default = "default_true")] pub enable_thinking: bool, /// Whether this model architecture supports vision input. - /// Derived from the featured model table, not user-configurable. + /// Derived from associated mmproj metadata, not user-configurable. #[serde(default)] pub vision_capable: bool, /// Estimated tokens per image for budget planning before mtmd tokenization. @@ -106,8 +129,8 @@ impl Default for ModelSettings { use_mlock: false, flash_attention: None, n_threads: None, - native_tool_calling: false, - use_jinja: false, + tool_calling: ToolCallingMode::Auto, + chat_template: ChatTemplate::Embedded, enable_thinking: true, vision_capable: false, image_token_estimate: default_image_token_estimate(), @@ -116,100 +139,43 @@ impl Default for ModelSettings { } } -/// HuggingFace repo + filename for multimodal projection weights (vision encoder). -pub struct MmprojSpec { - pub repo: &'static str, - pub filename: &'static str, -} - -impl MmprojSpec { - /// Local path for this mmproj, namespaced by repo to avoid collisions - /// between different models that use the same filename. - pub fn local_path(&self) -> std::path::PathBuf { - let repo_name = self.repo.split('/').next_back().unwrap_or(self.repo); - Paths::in_data_dir("models") - .join(repo_name) - .join(self.filename) - } -} - pub struct FeaturedModel { /// HuggingFace spec in "author/repo-GGUF:quantization" format. pub spec: &'static str, - /// Whether this model's GGUF template supports native tool calling via llama.cpp. - pub native_tool_calling: bool, - /// Multimodal projection weights spec. None for text-only models. - pub mmproj: Option, } pub const FEATURED_MODELS: &[FeaturedModel] = &[ FeaturedModel { spec: "bartowski/Llama-3.2-1B-Instruct-GGUF:Q4_K_M", - native_tool_calling: false, - mmproj: None, }, FeaturedModel { spec: "bartowski/Llama-3.2-3B-Instruct-GGUF:Q4_K_M", - native_tool_calling: false, - mmproj: None, }, FeaturedModel { spec: "bartowski/Hermes-2-Pro-Mistral-7B-GGUF:Q4_K_M", - native_tool_calling: false, - mmproj: None, }, FeaturedModel { spec: "bartowski/Mistral-Small-24B-Instruct-2501-GGUF:Q4_K_M", - native_tool_calling: false, - mmproj: None, }, FeaturedModel { spec: "unsloth/gemma-4-E4B-it-GGUF:Q4_K_M", - native_tool_calling: true, - mmproj: Some(MmprojSpec { - repo: "unsloth/gemma-4-E4B-it-GGUF", - filename: "mmproj-BF16.gguf", - }), }, FeaturedModel { spec: "unsloth/gemma-4-26B-A4B-it-GGUF:Q4_K_M", - native_tool_calling: true, - mmproj: Some(MmprojSpec { - repo: "unsloth/gemma-4-26B-A4B-it-GGUF", - filename: "mmproj-BF16.gguf", - }), }, ]; -pub fn default_settings_for_model(model_id: &str) -> ModelSettings { - use super::hf_models::parse_model_spec; - let model_repo = model_id.split(':').next().unwrap_or(model_id); - let featured = FEATURED_MODELS.iter().find(|m| { - if let Ok((repo_id, _quant)) = parse_model_spec(m.spec) { - repo_id == model_repo - } else { - false - } - }); +pub fn default_settings_for_model(_model_id: &str) -> ModelSettings { ModelSettings { - native_tool_calling: featured.is_some_and(|m| m.native_tool_calling), - vision_capable: featured.is_some_and(|m| m.mmproj.is_some()), ..ModelSettings::default() } } -/// Look up the `MmprojSpec` for a featured model by its model ID. -pub fn featured_mmproj_spec(model_id: &str) -> Option<&'static MmprojSpec> { - use super::hf_models::parse_model_spec; - let model_repo = model_id.split(':').next().unwrap_or(model_id); - FEATURED_MODELS.iter().find_map(|m| { - if let Ok((repo_id, _quant)) = parse_model_spec(m.spec) { - if repo_id == model_repo { - return m.mmproj.as_ref(); - } - } - None - }) +/// Local path for an mmproj file, namespaced by repo to avoid collisions +/// between different models that use the same filename. +pub fn mmproj_local_path(repo_id: &str, filename: &str) -> PathBuf { + let repo_name = repo_id.split('/').next_back().unwrap_or(repo_id); + Paths::in_data_dir("models").join(repo_name).join(filename) } /// Check if a model ID corresponds to a featured model. @@ -263,32 +229,27 @@ pub struct LocalModelEntry { #[serde(default)] pub mmproj_size_bytes: u64, #[serde(default)] + pub mmproj_checked: bool, + #[serde(default)] pub shard_files: Vec, } impl LocalModelEntry { - /// Populate mmproj metadata and vision settings from the featured model - /// table if this model's repo has a known vision encoder. - pub fn enrich_with_featured_mmproj(&mut self) { - if let Some(mmproj) = featured_mmproj_spec(&self.id) { - let path = mmproj.local_path(); - if self.mmproj_path.as_ref() != Some(&path) { - self.mmproj_path = Some(path.clone()); - self.mmproj_source_url = Some(format!( - "https://huggingface.co/{}/resolve/main/{}", - mmproj.repo, mmproj.filename - )); - } + pub fn refresh_mmproj_metadata(&mut self) { + self.settings.vision_capable = self.mmproj_path.is_some(); + if let Some(path) = &self.mmproj_path { + self.mmproj_checked = true; self.settings.vision_capable = true; if self.mmproj_size_bytes == 0 || self.settings.mmproj_size_bytes == 0 { - if let Ok(meta) = std::fs::metadata(&path) { + if let Ok(meta) = std::fs::metadata(path) { self.mmproj_size_bytes = meta.len(); self.settings.mmproj_size_bytes = meta.len(); } } + } else { + self.mmproj_size_bytes = 0; + self.settings.mmproj_size_bytes = 0; } - let defaults = default_settings_for_model(&self.id); - self.settings.native_tool_calling = defaults.native_tool_calling; } pub fn is_downloaded(&self) -> bool { @@ -436,7 +397,7 @@ impl LocalModelRegistry { for mut entry in featured_entries { if !self.models.iter().any(|m| m.id == entry.id) { - entry.enrich_with_featured_mmproj(); + entry.refresh_mmproj_metadata(); self.models.push(entry); changed = true; } @@ -455,7 +416,7 @@ impl LocalModelRegistry { } pub fn add_model(&mut self, mut entry: LocalModelEntry) -> Result<()> { - entry.enrich_with_featured_mmproj(); + entry.refresh_mmproj_metadata(); if let Some(existing) = self.models.iter_mut().find(|m| m.id == entry.id) { *existing = entry; } else { diff --git a/ui/desktop/openapi.json b/ui/desktop/openapi.json index 7e2f3bcd321c..a67d6b62fd56 100644 --- a/ui/desktop/openapi.json +++ b/ui/desktop/openapi.json @@ -1993,6 +1993,29 @@ } } }, + "/local-inference/chat-templates/builtin": { + "get": { + "tags": [ + "super::routes::local_inference" + ], + "operationId": "list_builtin_chat_templates", + "responses": { + "200": { + "description": "llama.cpp built-in chat template names", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } + } + } + }, "/local-inference/download": { "post": { "tags": [ @@ -4260,6 +4283,63 @@ } } }, + "ChatTemplate": { + "oneOf": [ + { + "type": "object", + "required": [ + "type" + ], + "properties": { + "type": { + "type": "string", + "enum": [ + "embedded" + ] + } + } + }, + { + "type": "object", + "required": [ + "name", + "type" + ], + "properties": { + "name": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "builtin" + ] + } + } + }, + { + "type": "object", + "required": [ + "template", + "type" + ], + "properties": { + "template": { + "type": "string" + }, + "type": { + "type": "string", + "enum": [ + "custom_inline" + ] + } + } + } + ], + "discriminator": { + "propertyName": "type" + } + }, "CheckProviderRequest": { "type": "object", "required": [ @@ -6716,6 +6796,9 @@ "ModelSettings": { "type": "object", "properties": { + "chat_template": { + "$ref": "#/components/schemas/ChatTemplate" + }, "context_size": { "type": "integer", "format": "int32", @@ -6766,9 +6849,6 @@ "format": "int32", "nullable": true }, - "native_tool_calling": { - "type": "boolean" - }, "presence_penalty": { "type": "number", "format": "float" @@ -6784,15 +6864,15 @@ "sampling": { "$ref": "#/components/schemas/SamplingConfig" }, - "use_jinja": { - "type": "boolean" + "tool_calling": { + "$ref": "#/components/schemas/ToolCallingMode" }, "use_mlock": { "type": "boolean" }, "vision_capable": { "type": "boolean", - "description": "Whether this model architecture supports vision input.\nDerived from the featured model table, not user-configurable." + "description": "Whether this model architecture supports vision input.\nDerived from associated mmproj metadata, not user-configurable." } } }, @@ -8797,6 +8877,14 @@ } } }, + "ToolCallingMode": { + "type": "string", + "enum": [ + "auto", + "force_native", + "force_emulated" + ] + }, "ToolConfirmationRequest": { "type": "object", "required": [ diff --git a/ui/desktop/src/api/index.ts b/ui/desktop/src/api/index.ts index d42bc29fc7f3..871f52ded6bf 100644 --- a/ui/desktop/src/api/index.ts +++ b/ui/desktop/src/api/index.ts @@ -1,4 +1,4 @@ // This file is auto-generated by @hey-api/openapi-ts -export { addExtension, agentAddExtension, agentRemoveExtension, callTool, cancelDownload, cancelLocalModelDownload, checkProvider, cleanupProviderCache, configureProviderOauth, confirmToolAction, createCustomProvider, createRecipe, createSchedule, decodeRecipe, deleteLocalModel, deleteModel, deleteRecipe, deleteSchedule, deleteSession, diagnostics, downloadHfModel, downloadModel, encodeRecipe, exportApp, exportSession, forkSession, getCanonicalModelInfo, getCustomProvider, getDictationConfig, getDownloadProgress, getExtensions, getFeatures, getLocalModelDownloadProgress, getModelSettings, getPrompt, getPrompts, getProviderCatalog, getProviderCatalogTemplate, getProviderModelInfo, getProviderModels, getRepoFiles, getSession, getSessionExtensions, getSessionInsights, getSlashCommands, getTools, getTunnelStatus, importApp, importSession, importSessionNostr, inspectRunningJob, killRunningJob, listApps, listLocalModels, listModels, listRecipes, listSchedules, listSessions, mcpUiProxy, type Options, parseRecipe, pauseSchedule, providers, readAllConfig, readConfig, readResource, recipeToYaml, removeConfig, removeCustomProvider, removeExtension, reply, resetPrompt, restartAgent, resumeAgent, runNowHandler, savePrompt, saveRecipe, scanRecipe, scheduleRecipe, searchHfModels, searchSessions, sendTelemetryEvent, sessionCancel, sessionEvents, sessionReply, sessionsHandler, setConfigProvider, setRecipeSlashCommand, shareSessionNostr, startAgent, startNanogptSetup, startOpenrouterSetup, startTetrateSetup, startTunnel, status, stopAgent, stopTunnel, syncFeaturedModels, systemInfo, transcribeDictation, unpauseSchedule, updateAgentProvider, updateCustomProvider, updateFromSession, updateModelSettings, updateSchedule, updateSession, updateSessionName, updateSessionUserRecipeValues, updateWorkingDir, upsertConfig, upsertPermissions, validateConfig } from './sdk.gen'; -export type { ActionRequired, ActionRequiredData, AddExtensionData, AddExtensionErrors, AddExtensionRequest, AddExtensionResponse, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponse, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponse, AgentRemoveExtensionResponses, Annotations, Author, AuthorRequest, CallToolData, CallToolError, CallToolErrors, CallToolRequest, CallToolResponse, CallToolResponse2, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CancelLocalModelDownloadData, CancelLocalModelDownloadErrors, CancelLocalModelDownloadResponses, CancelRequest, ChatRequest, CheckProviderData, CheckProviderRequest, CleanupProviderCacheData, CleanupProviderCacheErrors, CleanupProviderCacheResponse, CleanupProviderCacheResponses, ClientOptions, CommandType, ConfigKey, ConfigKeyQuery, ConfigResponse, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionRequest, ConfirmToolActionResponses, Content, ContentBlock, Conversation, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponse, CreateCustomProviderResponse2, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeRequest, CreateRecipeResponse, CreateRecipeResponse2, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleRequest, CreateScheduleResponse, CreateScheduleResponses, CspMetadata, DeclarativeProviderConfig, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeRequest, DecodeRecipeResponse, DecodeRecipeResponse2, DecodeRecipeResponses, DeleteLocalModelData, DeleteLocalModelErrors, DeleteLocalModelResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeRequest, DeleteRecipeResponse, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponse, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponse, DiagnosticsResponses, DictationProvider, DictationProviderStatus, DownloadHfModelData, DownloadHfModelErrors, DownloadHfModelResponse, DownloadHfModelResponses, DownloadModelData, DownloadModelErrors, DownloadModelRequest, DownloadModelResponses, DownloadProgress, DownloadStatus, EmbeddedResource, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeRequest, EncodeRecipeResponse, EncodeRecipeResponse2, EncodeRecipeResponses, Envs, EnvVarConfig, ErrorResponse, ExportAppData, ExportAppError, ExportAppErrors, ExportAppResponse, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponse, ExportSessionResponses, ExtensionConfig, ExtensionData, ExtensionEntry, ExtensionLoadResult, ExtensionQuery, ExtensionResponse, FeaturesResponse, ForkRequest, ForkResponse, ForkSessionData, ForkSessionErrors, ForkSessionResponse, ForkSessionResponses, FrontendToolRequest, GetCanonicalModelInfoData, GetCanonicalModelInfoResponse, GetCanonicalModelInfoResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponse, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponse, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponse, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponse, GetExtensionsResponses, GetFeaturesData, GetFeaturesResponse, GetFeaturesResponses, GetLocalModelDownloadProgressData, GetLocalModelDownloadProgressErrors, GetLocalModelDownloadProgressResponse, GetLocalModelDownloadProgressResponses, GetModelSettingsData, GetModelSettingsErrors, GetModelSettingsResponse, GetModelSettingsResponses, GetPromptData, GetPromptErrors, GetPromptResponse, GetPromptResponses, GetPromptsData, GetPromptsResponse, GetPromptsResponses, GetProviderCatalogData, GetProviderCatalogErrors, GetProviderCatalogResponse, GetProviderCatalogResponses, GetProviderCatalogTemplateData, GetProviderCatalogTemplateErrors, GetProviderCatalogTemplateResponse, GetProviderCatalogTemplateResponses, GetProviderModelInfoData, GetProviderModelInfoErrors, GetProviderModelInfoResponse, GetProviderModelInfoResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponse, GetProviderModelsResponses, GetRepoFilesData, GetRepoFilesResponse, GetRepoFilesResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponse, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponse, GetSessionInsightsResponses, GetSessionResponse, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponse, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsQuery, GetToolsResponse, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponse, GetTunnelStatusResponses, GooseApp, GooseMode, HfGgufFile, HfModelInfo, HfQuantVariant, Icon, IconTheme, ImageContent, ImportAppData, ImportAppError, ImportAppErrors, ImportAppRequest, ImportAppResponse, ImportAppResponse2, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionNostrData, ImportSessionNostrErrors, ImportSessionNostrRequest, ImportSessionNostrResponse, ImportSessionNostrResponses, ImportSessionRequest, ImportSessionResponse, ImportSessionResponses, InferenceMetadata, InspectJobResponse, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponse, InspectRunningJobResponses, JsonObject, KillJobResponse, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsError, ListAppsErrors, ListAppsRequest, ListAppsResponse, ListAppsResponse2, ListAppsResponses, ListLocalModelsData, ListLocalModelsResponse, ListLocalModelsResponses, ListModelsData, ListModelsResponse, ListModelsResponses, ListRecipeResponse, ListRecipesData, ListRecipesErrors, ListRecipesResponse, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponse, ListSchedulesResponse2, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponse, ListSessionsResponses, LoadedProvider, LocalModelResponse, McpAppResource, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, Message, MessageContent, MessageEvent, MessageMetadata, ModelCapabilities, ModelConfig, ModelDownloadStatus, ModelInfo, ModelInfoData, ModelInfoQuery, ModelInfoResponse, ModelSettings, ModelTemplate, ParseRecipeData, ParseRecipeError, ParseRecipeErrors, ParseRecipeRequest, ParseRecipeResponse, ParseRecipeResponse2, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponse, PauseScheduleResponses, Permission, PermissionLevel, PermissionsMetadata, PrincipalType, PromptContentResponse, PromptsListResponse, ProviderCatalogEntry, ProviderDetails, ProviderEngine, ProviderMetadata, ProviderModelInfoQuery, ProvidersData, ProvidersResponse, ProvidersResponse2, ProvidersResponses, ProviderTemplate, ProviderType, RawAudioContent, RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ReadAllConfigData, ReadAllConfigResponse, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceRequest, ReadResourceResponse, ReadResourceResponse2, ReadResourceResponses, Recipe, RecipeManifest, RecipeParameter, RecipeParameterInputType, RecipeParameterRequirement, RecipeToYamlData, RecipeToYamlError, RecipeToYamlErrors, RecipeToYamlRequest, RecipeToYamlResponse, RecipeToYamlResponse2, RecipeToYamlResponses, RedactedThinkingContent, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponse, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponse, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionRequest, RemoveExtensionResponse, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponse, ReplyResponses, RepoVariantsResponse, ResetPromptData, ResetPromptErrors, ResetPromptResponse, ResetPromptResponses, ResourceContents, ResourceMetadata, Response, RestartAgentData, RestartAgentErrors, RestartAgentRequest, RestartAgentResponse, RestartAgentResponse2, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentRequest, ResumeAgentResponse, ResumeAgentResponse2, ResumeAgentResponses, RetryConfig, Role, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponse, RunNowHandlerResponses, RunNowResponse, SamplingConfig, SavePromptData, SavePromptErrors, SavePromptRequest, SavePromptResponse, SavePromptResponses, SaveRecipeData, SaveRecipeError, SaveRecipeErrors, SaveRecipeRequest, SaveRecipeResponse, SaveRecipeResponse2, SaveRecipeResponses, ScanRecipeData, ScanRecipeRequest, ScanRecipeResponse, ScanRecipeResponse2, ScanRecipeResponses, ScheduledJob, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeRequest, ScheduleRecipeResponses, SearchHfModelsData, SearchHfModelsErrors, SearchHfModelsResponse, SearchHfModelsResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponse, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, Session, SessionCancelData, SessionCancelResponses, SessionDisplayInfo, SessionEventsData, SessionEventsErrors, SessionEventsResponse, SessionEventsResponses, SessionExtensionsResponse, SessionInsights, SessionListResponse, SessionReplyData, SessionReplyErrors, SessionReplyRequest, SessionReplyResponse, SessionReplyResponse2, SessionReplyResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponse, SessionsHandlerResponses, SessionsQuery, SessionType, SetConfigProviderData, SetProviderRequest, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, SetSlashCommandRequest, Settings, SetupResponse, ShareSessionNostrData, ShareSessionNostrErrors, ShareSessionNostrRequest, ShareSessionNostrResponse, ShareSessionNostrResponse2, ShareSessionNostrResponses, SlashCommand, SlashCommandsResponse, StartAgentData, StartAgentError, StartAgentErrors, StartAgentRequest, StartAgentResponse, StartAgentResponses, StartNanogptSetupData, StartNanogptSetupResponse, StartNanogptSetupResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponse, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponse, StartTetrateSetupResponses, StartTunnelData, StartTunnelError, StartTunnelErrors, StartTunnelResponse, StartTunnelResponses, StatusData, StatusResponse, StatusResponses, StopAgentData, StopAgentErrors, StopAgentRequest, StopAgentResponse, StopAgentResponses, StopTunnelData, StopTunnelError, StopTunnelErrors, StopTunnelResponses, SubRecipe, SuccessCheck, SyncFeaturedModelsData, SyncFeaturedModelsResponses, SystemInfo, SystemInfoData, SystemInfoResponse, SystemInfoResponses, SystemNotificationContent, SystemNotificationType, TaskSupport, TelemetryEventRequest, Template, TextContent, ThinkingContent, ThinkingEffort, TokenState, Tool, ToolAnnotations, ToolConfirmationRequest, ToolExecution, ToolInfo, ToolPermission, ToolRequest, ToolResponse, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponse, TranscribeDictationResponses, TranscribeRequest, TranscribeResponse, TunnelInfo, TunnelState, UiMetadata, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponse, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderRequest, UpdateCustomProviderResponse, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionRequest, UpdateFromSessionResponses, UpdateModelSettingsData, UpdateModelSettingsErrors, UpdateModelSettingsResponse, UpdateModelSettingsResponses, UpdateProviderRequest, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleRequest, UpdateScheduleResponse, UpdateScheduleResponses, UpdateSessionData, UpdateSessionErrors, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameRequest, UpdateSessionNameResponses, UpdateSessionRequest, UpdateSessionResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesError, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesRequest, UpdateSessionUserRecipeValuesResponse, UpdateSessionUserRecipeValuesResponse2, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirRequest, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigQuery, UpsertConfigResponse, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsQuery, UpsertPermissionsResponse, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponse, ValidateConfigResponses, WhisperModelResponse, WindowProps } from './types.gen'; +export { addExtension, agentAddExtension, agentRemoveExtension, callTool, cancelDownload, cancelLocalModelDownload, checkProvider, cleanupProviderCache, configureProviderOauth, confirmToolAction, createCustomProvider, createRecipe, createSchedule, decodeRecipe, deleteLocalModel, deleteModel, deleteRecipe, deleteSchedule, deleteSession, diagnostics, downloadHfModel, downloadModel, encodeRecipe, exportApp, exportSession, forkSession, getCanonicalModelInfo, getCustomProvider, getDictationConfig, getDownloadProgress, getExtensions, getFeatures, getLocalModelDownloadProgress, getModelSettings, getPrompt, getPrompts, getProviderCatalog, getProviderCatalogTemplate, getProviderModelInfo, getProviderModels, getRepoFiles, getSession, getSessionExtensions, getSessionInsights, getSlashCommands, getTools, getTunnelStatus, importApp, importSession, importSessionNostr, inspectRunningJob, killRunningJob, listApps, listBuiltinChatTemplates, listLocalModels, listModels, listRecipes, listSchedules, listSessions, mcpUiProxy, type Options, parseRecipe, pauseSchedule, providers, readAllConfig, readConfig, readResource, recipeToYaml, removeConfig, removeCustomProvider, removeExtension, reply, resetPrompt, restartAgent, resumeAgent, runNowHandler, savePrompt, saveRecipe, scanRecipe, scheduleRecipe, searchHfModels, searchSessions, sendTelemetryEvent, sessionCancel, sessionEvents, sessionReply, sessionsHandler, setConfigProvider, setRecipeSlashCommand, shareSessionNostr, startAgent, startNanogptSetup, startOpenrouterSetup, startTetrateSetup, startTunnel, status, stopAgent, stopTunnel, syncFeaturedModels, systemInfo, transcribeDictation, unpauseSchedule, updateAgentProvider, updateCustomProvider, updateFromSession, updateModelSettings, updateSchedule, updateSession, updateSessionName, updateSessionUserRecipeValues, updateWorkingDir, upsertConfig, upsertPermissions, validateConfig } from './sdk.gen'; +export type { ActionRequired, ActionRequiredData, AddExtensionData, AddExtensionErrors, AddExtensionRequest, AddExtensionResponse, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponse, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponse, AgentRemoveExtensionResponses, Annotations, Author, AuthorRequest, CallToolData, CallToolError, CallToolErrors, CallToolRequest, CallToolResponse, CallToolResponse2, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CancelLocalModelDownloadData, CancelLocalModelDownloadErrors, CancelLocalModelDownloadResponses, CancelRequest, ChatRequest, ChatTemplate, CheckProviderData, CheckProviderRequest, CleanupProviderCacheData, CleanupProviderCacheErrors, CleanupProviderCacheResponse, CleanupProviderCacheResponses, ClientOptions, CommandType, ConfigKey, ConfigKeyQuery, ConfigResponse, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionRequest, ConfirmToolActionResponses, Content, ContentBlock, Conversation, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponse, CreateCustomProviderResponse2, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeRequest, CreateRecipeResponse, CreateRecipeResponse2, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleRequest, CreateScheduleResponse, CreateScheduleResponses, CspMetadata, DeclarativeProviderConfig, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeRequest, DecodeRecipeResponse, DecodeRecipeResponse2, DecodeRecipeResponses, DeleteLocalModelData, DeleteLocalModelErrors, DeleteLocalModelResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeRequest, DeleteRecipeResponse, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponse, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponse, DiagnosticsResponses, DictationProvider, DictationProviderStatus, DownloadHfModelData, DownloadHfModelErrors, DownloadHfModelResponse, DownloadHfModelResponses, DownloadModelData, DownloadModelErrors, DownloadModelRequest, DownloadModelResponses, DownloadProgress, DownloadStatus, EmbeddedResource, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeRequest, EncodeRecipeResponse, EncodeRecipeResponse2, EncodeRecipeResponses, Envs, EnvVarConfig, ErrorResponse, ExportAppData, ExportAppError, ExportAppErrors, ExportAppResponse, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponse, ExportSessionResponses, ExtensionConfig, ExtensionData, ExtensionEntry, ExtensionLoadResult, ExtensionQuery, ExtensionResponse, FeaturesResponse, ForkRequest, ForkResponse, ForkSessionData, ForkSessionErrors, ForkSessionResponse, ForkSessionResponses, FrontendToolRequest, GetCanonicalModelInfoData, GetCanonicalModelInfoResponse, GetCanonicalModelInfoResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponse, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponse, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponse, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponse, GetExtensionsResponses, GetFeaturesData, GetFeaturesResponse, GetFeaturesResponses, GetLocalModelDownloadProgressData, GetLocalModelDownloadProgressErrors, GetLocalModelDownloadProgressResponse, GetLocalModelDownloadProgressResponses, GetModelSettingsData, GetModelSettingsErrors, GetModelSettingsResponse, GetModelSettingsResponses, GetPromptData, GetPromptErrors, GetPromptResponse, GetPromptResponses, GetPromptsData, GetPromptsResponse, GetPromptsResponses, GetProviderCatalogData, GetProviderCatalogErrors, GetProviderCatalogResponse, GetProviderCatalogResponses, GetProviderCatalogTemplateData, GetProviderCatalogTemplateErrors, GetProviderCatalogTemplateResponse, GetProviderCatalogTemplateResponses, GetProviderModelInfoData, GetProviderModelInfoErrors, GetProviderModelInfoResponse, GetProviderModelInfoResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponse, GetProviderModelsResponses, GetRepoFilesData, GetRepoFilesResponse, GetRepoFilesResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponse, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponse, GetSessionInsightsResponses, GetSessionResponse, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponse, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsQuery, GetToolsResponse, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponse, GetTunnelStatusResponses, GooseApp, GooseMode, HfGgufFile, HfModelInfo, HfQuantVariant, Icon, IconTheme, ImageContent, ImportAppData, ImportAppError, ImportAppErrors, ImportAppRequest, ImportAppResponse, ImportAppResponse2, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionNostrData, ImportSessionNostrErrors, ImportSessionNostrRequest, ImportSessionNostrResponse, ImportSessionNostrResponses, ImportSessionRequest, ImportSessionResponse, ImportSessionResponses, InferenceMetadata, InspectJobResponse, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponse, InspectRunningJobResponses, JsonObject, KillJobResponse, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsError, ListAppsErrors, ListAppsRequest, ListAppsResponse, ListAppsResponse2, ListAppsResponses, ListBuiltinChatTemplatesData, ListBuiltinChatTemplatesResponse, ListBuiltinChatTemplatesResponses, ListLocalModelsData, ListLocalModelsResponse, ListLocalModelsResponses, ListModelsData, ListModelsResponse, ListModelsResponses, ListRecipeResponse, ListRecipesData, ListRecipesErrors, ListRecipesResponse, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponse, ListSchedulesResponse2, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponse, ListSessionsResponses, LoadedProvider, LocalModelResponse, McpAppResource, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, Message, MessageContent, MessageEvent, MessageMetadata, ModelCapabilities, ModelConfig, ModelDownloadStatus, ModelInfo, ModelInfoData, ModelInfoQuery, ModelInfoResponse, ModelSettings, ModelTemplate, ParseRecipeData, ParseRecipeError, ParseRecipeErrors, ParseRecipeRequest, ParseRecipeResponse, ParseRecipeResponse2, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponse, PauseScheduleResponses, Permission, PermissionLevel, PermissionsMetadata, PrincipalType, PromptContentResponse, PromptsListResponse, ProviderCatalogEntry, ProviderDetails, ProviderEngine, ProviderMetadata, ProviderModelInfoQuery, ProvidersData, ProvidersResponse, ProvidersResponse2, ProvidersResponses, ProviderTemplate, ProviderType, RawAudioContent, RawEmbeddedResource, RawImageContent, RawResource, RawTextContent, ReadAllConfigData, ReadAllConfigResponse, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceRequest, ReadResourceResponse, ReadResourceResponse2, ReadResourceResponses, Recipe, RecipeManifest, RecipeParameter, RecipeParameterInputType, RecipeParameterRequirement, RecipeToYamlData, RecipeToYamlError, RecipeToYamlErrors, RecipeToYamlRequest, RecipeToYamlResponse, RecipeToYamlResponse2, RecipeToYamlResponses, RedactedThinkingContent, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponse, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponse, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionRequest, RemoveExtensionResponse, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponse, ReplyResponses, RepoVariantsResponse, ResetPromptData, ResetPromptErrors, ResetPromptResponse, ResetPromptResponses, ResourceContents, ResourceMetadata, Response, RestartAgentData, RestartAgentErrors, RestartAgentRequest, RestartAgentResponse, RestartAgentResponse2, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentRequest, ResumeAgentResponse, ResumeAgentResponse2, ResumeAgentResponses, RetryConfig, Role, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponse, RunNowHandlerResponses, RunNowResponse, SamplingConfig, SavePromptData, SavePromptErrors, SavePromptRequest, SavePromptResponse, SavePromptResponses, SaveRecipeData, SaveRecipeError, SaveRecipeErrors, SaveRecipeRequest, SaveRecipeResponse, SaveRecipeResponse2, SaveRecipeResponses, ScanRecipeData, ScanRecipeRequest, ScanRecipeResponse, ScanRecipeResponse2, ScanRecipeResponses, ScheduledJob, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeRequest, ScheduleRecipeResponses, SearchHfModelsData, SearchHfModelsErrors, SearchHfModelsResponse, SearchHfModelsResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponse, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, Session, SessionCancelData, SessionCancelResponses, SessionDisplayInfo, SessionEventsData, SessionEventsErrors, SessionEventsResponse, SessionEventsResponses, SessionExtensionsResponse, SessionInsights, SessionListResponse, SessionReplyData, SessionReplyErrors, SessionReplyRequest, SessionReplyResponse, SessionReplyResponse2, SessionReplyResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponse, SessionsHandlerResponses, SessionsQuery, SessionType, SetConfigProviderData, SetProviderRequest, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, SetSlashCommandRequest, Settings, SetupResponse, ShareSessionNostrData, ShareSessionNostrErrors, ShareSessionNostrRequest, ShareSessionNostrResponse, ShareSessionNostrResponse2, ShareSessionNostrResponses, SlashCommand, SlashCommandsResponse, StartAgentData, StartAgentError, StartAgentErrors, StartAgentRequest, StartAgentResponse, StartAgentResponses, StartNanogptSetupData, StartNanogptSetupResponse, StartNanogptSetupResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponse, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponse, StartTetrateSetupResponses, StartTunnelData, StartTunnelError, StartTunnelErrors, StartTunnelResponse, StartTunnelResponses, StatusData, StatusResponse, StatusResponses, StopAgentData, StopAgentErrors, StopAgentRequest, StopAgentResponse, StopAgentResponses, StopTunnelData, StopTunnelError, StopTunnelErrors, StopTunnelResponses, SubRecipe, SuccessCheck, SyncFeaturedModelsData, SyncFeaturedModelsResponses, SystemInfo, SystemInfoData, SystemInfoResponse, SystemInfoResponses, SystemNotificationContent, SystemNotificationType, TaskSupport, TelemetryEventRequest, Template, TextContent, ThinkingContent, ThinkingEffort, TokenState, Tool, ToolAnnotations, ToolCallingMode, ToolConfirmationRequest, ToolExecution, ToolInfo, ToolPermission, ToolRequest, ToolResponse, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponse, TranscribeDictationResponses, TranscribeRequest, TranscribeResponse, TunnelInfo, TunnelState, UiMetadata, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponse, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderRequest, UpdateCustomProviderResponse, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionRequest, UpdateFromSessionResponses, UpdateModelSettingsData, UpdateModelSettingsErrors, UpdateModelSettingsResponse, UpdateModelSettingsResponses, UpdateProviderRequest, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleRequest, UpdateScheduleResponse, UpdateScheduleResponses, UpdateSessionData, UpdateSessionErrors, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameRequest, UpdateSessionNameResponses, UpdateSessionRequest, UpdateSessionResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesError, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesRequest, UpdateSessionUserRecipeValuesResponse, UpdateSessionUserRecipeValuesResponse2, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirRequest, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigQuery, UpsertConfigResponse, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsQuery, UpsertPermissionsResponse, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponse, ValidateConfigResponses, WhisperModelResponse, WindowProps } from './types.gen'; diff --git a/ui/desktop/src/api/sdk.gen.ts b/ui/desktop/src/api/sdk.gen.ts index c98c91640854..081dfb57fcd5 100644 --- a/ui/desktop/src/api/sdk.gen.ts +++ b/ui/desktop/src/api/sdk.gen.ts @@ -2,7 +2,7 @@ import type { Client, Options as Options2, TDataShape } from './client'; import { client } from './client.gen'; -import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, CallToolData, CallToolErrors, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CancelLocalModelDownloadData, CancelLocalModelDownloadErrors, CancelLocalModelDownloadResponses, CheckProviderData, CleanupProviderCacheData, CleanupProviderCacheErrors, CleanupProviderCacheResponses, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteLocalModelData, DeleteLocalModelErrors, DeleteLocalModelResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, DownloadHfModelData, DownloadHfModelErrors, DownloadHfModelResponses, DownloadModelData, DownloadModelErrors, DownloadModelResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportAppData, ExportAppErrors, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, ForkSessionData, ForkSessionErrors, ForkSessionResponses, GetCanonicalModelInfoData, GetCanonicalModelInfoResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetFeaturesData, GetFeaturesResponses, GetLocalModelDownloadProgressData, GetLocalModelDownloadProgressErrors, GetLocalModelDownloadProgressResponses, GetModelSettingsData, GetModelSettingsErrors, GetModelSettingsResponses, GetPromptData, GetPromptErrors, GetPromptResponses, GetPromptsData, GetPromptsResponses, GetProviderCatalogData, GetProviderCatalogErrors, GetProviderCatalogResponses, GetProviderCatalogTemplateData, GetProviderCatalogTemplateErrors, GetProviderCatalogTemplateResponses, GetProviderModelInfoData, GetProviderModelInfoErrors, GetProviderModelInfoResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetRepoFilesData, GetRepoFilesResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportAppData, ImportAppErrors, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionNostrData, ImportSessionNostrErrors, ImportSessionNostrResponses, ImportSessionResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsErrors, ListAppsResponses, ListLocalModelsData, ListLocalModelsResponses, ListModelsData, ListModelsResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, RecipeToYamlData, RecipeToYamlErrors, RecipeToYamlResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResetPromptData, ResetPromptErrors, ResetPromptResponses, RestartAgentData, RestartAgentErrors, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SavePromptData, SavePromptErrors, SavePromptResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SearchHfModelsData, SearchHfModelsErrors, SearchHfModelsResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionCancelData, SessionCancelResponses, SessionEventsData, SessionEventsErrors, SessionEventsResponses, SessionReplyData, SessionReplyErrors, SessionReplyResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, ShareSessionNostrData, ShareSessionNostrErrors, ShareSessionNostrResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartNanogptSetupData, StartNanogptSetupResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopAgentData, StopAgentErrors, StopAgentResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, SyncFeaturedModelsData, SyncFeaturedModelsResponses, SystemInfoData, SystemInfoResponses, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateModelSettingsData, UpdateModelSettingsErrors, UpdateModelSettingsResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionData, UpdateSessionErrors, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen'; +import type { AddExtensionData, AddExtensionErrors, AddExtensionResponses, AgentAddExtensionData, AgentAddExtensionErrors, AgentAddExtensionResponses, AgentRemoveExtensionData, AgentRemoveExtensionErrors, AgentRemoveExtensionResponses, CallToolData, CallToolErrors, CallToolResponses, CancelDownloadData, CancelDownloadErrors, CancelDownloadResponses, CancelLocalModelDownloadData, CancelLocalModelDownloadErrors, CancelLocalModelDownloadResponses, CheckProviderData, CleanupProviderCacheData, CleanupProviderCacheErrors, CleanupProviderCacheResponses, ConfigureProviderOauthData, ConfigureProviderOauthErrors, ConfigureProviderOauthResponses, ConfirmToolActionData, ConfirmToolActionErrors, ConfirmToolActionResponses, CreateCustomProviderData, CreateCustomProviderErrors, CreateCustomProviderResponses, CreateRecipeData, CreateRecipeErrors, CreateRecipeResponses, CreateScheduleData, CreateScheduleErrors, CreateScheduleResponses, DecodeRecipeData, DecodeRecipeErrors, DecodeRecipeResponses, DeleteLocalModelData, DeleteLocalModelErrors, DeleteLocalModelResponses, DeleteModelData, DeleteModelErrors, DeleteModelResponses, DeleteRecipeData, DeleteRecipeErrors, DeleteRecipeResponses, DeleteScheduleData, DeleteScheduleErrors, DeleteScheduleResponses, DeleteSessionData, DeleteSessionErrors, DeleteSessionResponses, DiagnosticsData, DiagnosticsErrors, DiagnosticsResponses, DownloadHfModelData, DownloadHfModelErrors, DownloadHfModelResponses, DownloadModelData, DownloadModelErrors, DownloadModelResponses, EncodeRecipeData, EncodeRecipeErrors, EncodeRecipeResponses, ExportAppData, ExportAppErrors, ExportAppResponses, ExportSessionData, ExportSessionErrors, ExportSessionResponses, ForkSessionData, ForkSessionErrors, ForkSessionResponses, GetCanonicalModelInfoData, GetCanonicalModelInfoResponses, GetCustomProviderData, GetCustomProviderErrors, GetCustomProviderResponses, GetDictationConfigData, GetDictationConfigResponses, GetDownloadProgressData, GetDownloadProgressErrors, GetDownloadProgressResponses, GetExtensionsData, GetExtensionsErrors, GetExtensionsResponses, GetFeaturesData, GetFeaturesResponses, GetLocalModelDownloadProgressData, GetLocalModelDownloadProgressErrors, GetLocalModelDownloadProgressResponses, GetModelSettingsData, GetModelSettingsErrors, GetModelSettingsResponses, GetPromptData, GetPromptErrors, GetPromptResponses, GetPromptsData, GetPromptsResponses, GetProviderCatalogData, GetProviderCatalogErrors, GetProviderCatalogResponses, GetProviderCatalogTemplateData, GetProviderCatalogTemplateErrors, GetProviderCatalogTemplateResponses, GetProviderModelInfoData, GetProviderModelInfoErrors, GetProviderModelInfoResponses, GetProviderModelsData, GetProviderModelsErrors, GetProviderModelsResponses, GetRepoFilesData, GetRepoFilesResponses, GetSessionData, GetSessionErrors, GetSessionExtensionsData, GetSessionExtensionsErrors, GetSessionExtensionsResponses, GetSessionInsightsData, GetSessionInsightsErrors, GetSessionInsightsResponses, GetSessionResponses, GetSlashCommandsData, GetSlashCommandsResponses, GetToolsData, GetToolsErrors, GetToolsResponses, GetTunnelStatusData, GetTunnelStatusResponses, ImportAppData, ImportAppErrors, ImportAppResponses, ImportSessionData, ImportSessionErrors, ImportSessionNostrData, ImportSessionNostrErrors, ImportSessionNostrResponses, ImportSessionResponses, InspectRunningJobData, InspectRunningJobErrors, InspectRunningJobResponses, KillRunningJobData, KillRunningJobResponses, ListAppsData, ListAppsErrors, ListAppsResponses, ListBuiltinChatTemplatesData, ListBuiltinChatTemplatesResponses, ListLocalModelsData, ListLocalModelsResponses, ListModelsData, ListModelsResponses, ListRecipesData, ListRecipesErrors, ListRecipesResponses, ListSchedulesData, ListSchedulesErrors, ListSchedulesResponses, ListSessionsData, ListSessionsErrors, ListSessionsResponses, McpUiProxyData, McpUiProxyErrors, McpUiProxyResponses, ParseRecipeData, ParseRecipeErrors, ParseRecipeResponses, PauseScheduleData, PauseScheduleErrors, PauseScheduleResponses, ProvidersData, ProvidersResponses, ReadAllConfigData, ReadAllConfigResponses, ReadConfigData, ReadConfigErrors, ReadConfigResponses, ReadResourceData, ReadResourceErrors, ReadResourceResponses, RecipeToYamlData, RecipeToYamlErrors, RecipeToYamlResponses, RemoveConfigData, RemoveConfigErrors, RemoveConfigResponses, RemoveCustomProviderData, RemoveCustomProviderErrors, RemoveCustomProviderResponses, RemoveExtensionData, RemoveExtensionErrors, RemoveExtensionResponses, ReplyData, ReplyErrors, ReplyResponses, ResetPromptData, ResetPromptErrors, ResetPromptResponses, RestartAgentData, RestartAgentErrors, RestartAgentResponses, ResumeAgentData, ResumeAgentErrors, ResumeAgentResponses, RunNowHandlerData, RunNowHandlerErrors, RunNowHandlerResponses, SavePromptData, SavePromptErrors, SavePromptResponses, SaveRecipeData, SaveRecipeErrors, SaveRecipeResponses, ScanRecipeData, ScanRecipeResponses, ScheduleRecipeData, ScheduleRecipeErrors, ScheduleRecipeResponses, SearchHfModelsData, SearchHfModelsErrors, SearchHfModelsResponses, SearchSessionsData, SearchSessionsErrors, SearchSessionsResponses, SendTelemetryEventData, SendTelemetryEventResponses, SessionCancelData, SessionCancelResponses, SessionEventsData, SessionEventsErrors, SessionEventsResponses, SessionReplyData, SessionReplyErrors, SessionReplyResponses, SessionsHandlerData, SessionsHandlerErrors, SessionsHandlerResponses, SetConfigProviderData, SetRecipeSlashCommandData, SetRecipeSlashCommandErrors, SetRecipeSlashCommandResponses, ShareSessionNostrData, ShareSessionNostrErrors, ShareSessionNostrResponses, StartAgentData, StartAgentErrors, StartAgentResponses, StartNanogptSetupData, StartNanogptSetupResponses, StartOpenrouterSetupData, StartOpenrouterSetupResponses, StartTetrateSetupData, StartTetrateSetupResponses, StartTunnelData, StartTunnelErrors, StartTunnelResponses, StatusData, StatusResponses, StopAgentData, StopAgentErrors, StopAgentResponses, StopTunnelData, StopTunnelErrors, StopTunnelResponses, SyncFeaturedModelsData, SyncFeaturedModelsResponses, SystemInfoData, SystemInfoResponses, TranscribeDictationData, TranscribeDictationErrors, TranscribeDictationResponses, UnpauseScheduleData, UnpauseScheduleErrors, UnpauseScheduleResponses, UpdateAgentProviderData, UpdateAgentProviderErrors, UpdateAgentProviderResponses, UpdateCustomProviderData, UpdateCustomProviderErrors, UpdateCustomProviderResponses, UpdateFromSessionData, UpdateFromSessionErrors, UpdateFromSessionResponses, UpdateModelSettingsData, UpdateModelSettingsErrors, UpdateModelSettingsResponses, UpdateScheduleData, UpdateScheduleErrors, UpdateScheduleResponses, UpdateSessionData, UpdateSessionErrors, UpdateSessionNameData, UpdateSessionNameErrors, UpdateSessionNameResponses, UpdateSessionResponses, UpdateSessionUserRecipeValuesData, UpdateSessionUserRecipeValuesErrors, UpdateSessionUserRecipeValuesResponses, UpdateWorkingDirData, UpdateWorkingDirErrors, UpdateWorkingDirResponses, UpsertConfigData, UpsertConfigErrors, UpsertConfigResponses, UpsertPermissionsData, UpsertPermissionsErrors, UpsertPermissionsResponses, ValidateConfigData, ValidateConfigErrors, ValidateConfigResponses } from './types.gen'; export type Options = Options2 & { /** @@ -321,6 +321,8 @@ export const startOpenrouterSetup = (optio export const startTetrateSetup = (options?: Options) => (options?.client ?? client).post({ url: '/handle_tetrate', ...options }); +export const listBuiltinChatTemplates = (options?: Options) => (options?.client ?? client).get({ url: '/local-inference/chat-templates/builtin', ...options }); + export const downloadHfModel = (options: Options) => (options.client ?? client).post({ url: '/local-inference/download', ...options, diff --git a/ui/desktop/src/api/types.gen.ts b/ui/desktop/src/api/types.gen.ts index b5535a239fb6..a4d110403e56 100644 --- a/ui/desktop/src/api/types.gen.ts +++ b/ui/desktop/src/api/types.gen.ts @@ -76,6 +76,16 @@ export type ChatRequest = { user_message: Message; }; +export type ChatTemplate = { + type: 'embedded'; +} | { + name: string; + type: 'builtin'; +} | { + template: string; + type: 'custom_inline'; +}; + export type CheckProviderRequest = { provider: string; }; @@ -863,6 +873,7 @@ export type ModelInfoResponse = { }; export type ModelSettings = { + chat_template?: ChatTemplate; context_size?: number | null; enable_thinking?: boolean; flash_attention?: boolean | null; @@ -880,16 +891,15 @@ export type ModelSettings = { n_batch?: number | null; n_gpu_layers?: number | null; n_threads?: number | null; - native_tool_calling?: boolean; presence_penalty?: number; repeat_last_n?: number; repeat_penalty?: number; sampling?: SamplingConfig; - use_jinja?: boolean; + tool_calling?: ToolCallingMode; use_mlock?: boolean; /** * Whether this model architecture supports vision input. - * Derived from the featured model table, not user-configurable. + * Derived from associated mmproj metadata, not user-configurable. */ vision_capable?: boolean; }; @@ -1544,6 +1554,8 @@ export type ToolAnnotations = { title?: string; }; +export type ToolCallingMode = 'auto' | 'force_native' | 'force_emulated'; + export type ToolConfirmationRequest = { arguments: JsonObject; id: string; @@ -3241,6 +3253,22 @@ export type StartTetrateSetupResponses = { export type StartTetrateSetupResponse = StartTetrateSetupResponses[keyof StartTetrateSetupResponses]; +export type ListBuiltinChatTemplatesData = { + body?: never; + path?: never; + query?: never; + url: '/local-inference/chat-templates/builtin'; +}; + +export type ListBuiltinChatTemplatesResponses = { + /** + * llama.cpp built-in chat template names + */ + 200: Array; +}; + +export type ListBuiltinChatTemplatesResponse = ListBuiltinChatTemplatesResponses[keyof ListBuiltinChatTemplatesResponses]; + export type DownloadHfModelData = { body: DownloadModelRequest; path?: never; diff --git a/ui/desktop/src/components/settings/localInference/ModelSettingsPanel.tsx b/ui/desktop/src/components/settings/localInference/ModelSettingsPanel.tsx index 8887ad65d5ce..e1b0661a798d 100644 --- a/ui/desktop/src/components/settings/localInference/ModelSettingsPanel.tsx +++ b/ui/desktop/src/components/settings/localInference/ModelSettingsPanel.tsx @@ -4,9 +4,12 @@ import { Button } from '../../ui/button'; import { Switch } from '../../ui/switch'; import { getModelSettings, + listBuiltinChatTemplates, updateModelSettings, + type ChatTemplate, type ModelSettings, type SamplingConfig, + type ToolCallingMode, } from '../../../api'; import { defineMessages, useIntl } from '../../../i18n'; @@ -161,16 +164,59 @@ const i18n = defineMessages({ }, toolCalling: { id: 'modelSettingsPanel.toolCalling', - defaultMessage: 'Tool Calling', + defaultMessage: 'Tool calling', }, - nativeToolCalling: { - id: 'modelSettingsPanel.nativeToolCalling', - defaultMessage: 'Native tool calling', + toolCallingDescription: { + id: 'modelSettingsPanel.toolCallingDescription', + defaultMessage: 'Choose how local models select native or emulated tool calling', }, - nativeToolCallingDescription: { - id: 'modelSettingsPanel.nativeToolCallingDescription', - defaultMessage: - "Use the model's built-in tool-call format instead of the shell-command emulator. Enable for large models that reliably support tool calling.", + toolCallingAuto: { + id: 'modelSettingsPanel.toolCallingAuto', + defaultMessage: 'Auto', + }, + toolCallingForceNative: { + id: 'modelSettingsPanel.toolCallingForceNative', + defaultMessage: 'Force native', + }, + toolCallingForceEmulated: { + id: 'modelSettingsPanel.toolCallingForceEmulated', + defaultMessage: 'Force emulated', + }, + chatTemplate: { + id: 'modelSettingsPanel.chatTemplate', + defaultMessage: 'Chat template', + }, + chatTemplateDescription: { + id: 'modelSettingsPanel.chatTemplateDescription', + defaultMessage: 'Use embedded GGUF metadata, a llama.cpp built-in template, or inline Jinja', + }, + chatTemplateEmbedded: { + id: 'modelSettingsPanel.chatTemplateEmbedded', + defaultMessage: 'Embedded', + }, + chatTemplateBuiltin: { + id: 'modelSettingsPanel.chatTemplateBuiltin', + defaultMessage: 'Built-in', + }, + chatTemplateCustomInline: { + id: 'modelSettingsPanel.chatTemplateCustomInline', + defaultMessage: 'Custom inline', + }, + builtinChatTemplate: { + id: 'modelSettingsPanel.builtinChatTemplate', + defaultMessage: 'Built-in template', + }, + builtinChatTemplateDescription: { + id: 'modelSettingsPanel.builtinChatTemplateDescription', + defaultMessage: 'Select a llama.cpp built-in template name', + }, + customChatTemplate: { + id: 'modelSettingsPanel.customChatTemplate', + defaultMessage: 'Custom chat template', + }, + customChatTemplateDescription: { + id: 'modelSettingsPanel.customChatTemplateDescription', + defaultMessage: 'Paste the full Jinja chat template source', }, }); @@ -194,10 +240,12 @@ const DEFAULT_SETTINGS: ModelSettings = { use_mlock: false, flash_attention: null, n_threads: null, - native_tool_calling: false, + tool_calling: 'auto', + chat_template: { type: 'embedded' }, }; type SamplingType = SamplingConfig['type']; +type ChatTemplateMode = 'embedded' | 'builtin' | 'custom_inline'; function NumberField({ label, @@ -302,16 +350,59 @@ function SelectField({ ); } +function TextAreaField({ + label, + description, + value, + onChange, + onBlur, +}: { + label: string; + description?: string; + value: string; + onChange: (v: string) => void; + onBlur: () => void; +}) { + return ( +
+ + {description && {description}} +