diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index e06c8b39..effec3f9 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -17,6 +17,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive # Xcode 26.5 is temporarily exposed as a beta app on macos-26 runners: https://github.com/actions/runner-images/issues/14108 - name: Select Xcode 26.5 @@ -100,6 +101,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive - name: Install Nix uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # was v22 @@ -179,6 +181,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive - name: Install Nix uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # was v22 @@ -204,6 +207,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive - name: Install Nix uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # was v22 @@ -230,6 +234,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive - name: Setup Bun uses: oven-sh/setup-bun@f4d14e03ff726c06358e5557344e1da148b56cf7 # was v1 @@ -239,7 +244,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # was stable with: - toolchain: 1.88.0 + toolchain: 1.91.1 - name: Install sccache shell: bash @@ -352,6 +357,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive - name: Install Nix uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # was v22 diff --git a/.github/workflows/desktop-pr-build.yml b/.github/workflows/desktop-pr-build.yml index 494678bd..dbdb2c6c 100644 --- a/.github/workflows/desktop-pr-build.yml +++ b/.github/workflows/desktop-pr-build.yml @@ -14,6 +14,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive # Xcode 26.5 is temporarily exposed as a beta app on macos-26 runners: https://github.com/actions/runner-images/issues/14108 - name: Select Xcode 26.5 @@ -72,6 +73,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive - name: Install Nix uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # was v22 @@ -129,6 +131,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive - name: Setup Bun uses: oven-sh/setup-bun@f4d14e03ff726c06358e5557344e1da148b56cf7 # was v1 @@ -138,7 +141,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # was stable with: - toolchain: 1.88.0 + toolchain: 1.91.1 - name: Install sccache shell: bash @@ -196,6 +199,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive - name: Install Nix uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # was v22 diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 236301f6..f2e6a955 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -56,6 +56,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive # Xcode 26.5 is temporarily exposed as a beta app on macos-26 runners: https://github.com/actions/runner-images/issues/14108 - name: Select Xcode 26.5 @@ -182,6 +183,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive - name: Setup Bun uses: oven-sh/setup-bun@f4d14e03ff726c06358e5557344e1da148b56cf7 # was v1 @@ -191,7 +193,7 @@ jobs: - name: Install Rust uses: dtolnay/rust-toolchain@29eef336d9b2848a0b548edc03f92a220660cdb8 # was stable with: - toolchain: 1.88.0 + toolchain: 1.91.1 - name: Install sccache shell: bash @@ -592,6 +594,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive - name: Install Nix uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # was v22 @@ -618,6 +621,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive # Xcode 26.5 is temporarily exposed as a beta app on macos-26 runners: https://github.com/actions/runner-images/issues/14108 - name: Select Xcode 26.5 @@ -663,6 +667,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive - name: Install Nix uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # was v22 diff --git a/.github/workflows/rust-tests.yml b/.github/workflows/rust-tests.yml index 41a427e0..82738a8e 100644 --- a/.github/workflows/rust-tests.yml +++ b/.github/workflows/rust-tests.yml @@ -16,6 +16,7 @@ jobs: - uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # was v4 with: persist-credentials: false + submodules: recursive - name: Install Nix uses: DeterminateSystems/nix-installer-action@ef8a148080ab6020fd15196c2084a2eea5ff2d25 # was v22 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..a0a12958 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "ThirdParties/goose"] + path = ThirdParties/goose + url = https://github.com/AnthonyRonning/goose.git diff --git a/ThirdParties/goose b/ThirdParties/goose new file mode 160000 index 00000000..1e03dbd4 --- /dev/null +++ b/ThirdParties/goose @@ -0,0 +1 @@ +Subproject commit 1e03dbd4bea9eda111c661de0ed6a01460a6f4bf diff --git a/docs/goose-embedded-readiness-feedback.md b/docs/goose-embedded-readiness-feedback.md new file mode 100644 index 00000000..c544ad33 --- /dev/null +++ b/docs/goose-embedded-readiness-feedback.md @@ -0,0 +1,506 @@ +# Goose Embedded Runtime Readiness Feedback + +This note is feedback from Maple's Agent Mode proof of concept. Maple embeds +Goose inside the Tauri desktop process and uses Maple's local OpenAI-compatible +proxy as the model provider. The PoC now works, but the path is not clean enough +to ship without either carrying Goose-specific integration debt or getting a +more explicit embedded-runtime surface from Goose. + +The scope here is intentionally narrow: + +- This is about embedding Goose directly in Maple's Rust/Tauri desktop runtime. +- This is not a critique of ACP as a protocol or of remote ACP interoperability. +- This is not about Maple frontend UX. +- This does not cover broad provider compatibility. The provider-specific fixes + Maple may need should stay Maple's responsibility unless they expose a concrete + Goose parser/runtime bug. + +## Current Maple Shape + +The current PR embeds Goose by depending on a forked submodule: + +- Submodule: `ThirdParties/goose` +- Rust dependency: `goose = { path = "../../ThirdParties/goose/crates/goose", default-features = false }` +- Goose runtime entrypoint used by Maple: + - `goose::acp::server_factory::{AcpServer, AcpServerFactoryConfig}` + - `goose::acp::transport::create_router` + - `goose::agents::GoosePlatform` + - `goose::config::Config::global()` + +Maple starts Goose inside Tauri by: + +1. Ensuring Maple's local proxy is running. +2. Creating a Maple-owned Goose root under Maple's app config directory. +3. Mutating process environment variables used by Goose. +4. Writing Goose provider/model/key settings through `Config::global()`. +5. Creating an `AcpServer`. +6. Wrapping Goose's router in a local Axum server. +7. Returning the local endpoint to the Maple frontend. + +That is viable for a PoC, but the host app is reaching into internal runtime +details, global config, process environment, and a forked source checkout. + +## P0: Stable Embeddable Runtime Crate + +### Problem + +Maple currently imports the parent `goose` crate from a Git submodule and calls +APIs that appear designed primarily for Goose's own desktop/server binaries. +That creates several shipping problems: + +- Maple needs a fork/submodule instead of a normal published crate dependency. +- The integration depends on internal module layout staying stable. +- Maple's reproducible builds now depend on a nested source checkout. +- It is unclear which APIs Goose considers supported for external embedders. + +### Current Workaround + +Maple carries: + +```toml +[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))'.dependencies] +goose = { path = "../../ThirdParties/goose/crates/goose", default-features = false } +``` + +### Requested Goose Improvement + +Expose and support a small embeddable runtime crate, for example `goose-runtime` +or a GDK crate, with a semver-stable API for: + +- Constructing a Goose runtime from host-owned config. +- Starting and stopping the runtime. +- Selecting built-in tool groups. +- Configuring provider/model settings. +- Setting data/config/state/log directories. +- Receiving structured lifecycle and diagnostics events. + +This does not need to expose every Goose internal. The important line is that +Maple should be able to depend on a normal crate and avoid reaching through the +same entrypoints used by Goose's own binaries. + +## P0: Host-Owned Runtime Configuration + +### Problem + +The current embedding path is too global. Maple has to configure Goose by +setting process environment variables, mutating `Config::global()`, and writing +Goose config/secrets files: + +```rust +std::env::set_var("GOOSE_PATH_ROOT", goose_path_root); +std::env::set_var("GOOSE_DISABLE_KEYRING", "true"); +std::env::remove_var("GOOSE_MAX_TOKENS"); + +let config = goose::config::Config::global(); +goose::config::set_active_provider(config, "openai", model)?; +config.set_param("GOOSE_FAST_MODEL", model)?; +config.set_param("GOOSE_MODE", mode)?; +config.set_param("OPENAI_BASE_URL", format!("{maple_proxy_base_url}/v1"))?; +config.set_secret("OPENAI_API_KEY", &proxy_api_key)?; +``` + +This is risky for an embedded desktop app: + +- Process-global environment is hard to test and hard to reason about. +- Two runtimes in one process would conflict. +- Host apps cannot cleanly separate user config from runtime config. +- Secrets flow through Goose's default config path even when the host already + owns secret storage. +- The host has to know which global keys to delete, such as `GOOSE_MAX_TOKENS`, + to avoid stale behavior. + +### Current Workaround + +Maple creates `config`, `data`, and `state` directories under its own app config +root, points `GOOSE_PATH_ROOT` there, disables keyring use, writes Goose config +values, and locks down file permissions after writing `config.yaml` and +`secrets.yaml`. + +### Requested Goose Improvement + +Provide a runtime config builder that does not require process-global mutation: + +```rust +let runtime = GooseRuntime::builder() + .path_root(goose_path_root) + .config_dir(goose_config_dir) + .data_dir(goose_data_dir) + .state_dir(goose_state_dir) + .disable_keyring(true) + .provider(OpenAiCompatibleProvider { + base_url: maple_proxy_base_url, + api_key: SecretString::new(proxy_api_key), + model, + fast_model: Some(model), + }) + .mode(mode) + .builtins(["developer"]) + .build()?; +``` + +The exact names do not matter. The important requirement is that an embedded +host can configure Goose with normal Rust values, without environment variables +or global singleton config. + +## P0: Upstream the Streaming Tool-Call Delta Fix + +### Problem + +The PoC exposed a concrete Goose parser bug in OpenAI-compatible streaming tool +calls. Some providers stream multiple `tool_calls` entries for the same index in +one chunk. One entry can carry the tool id/name while another same-index entry +carries only argument fragments. + +Before the patch, Goose could drop the argument-only fragment. The remaining +arguments then looked truncated, and Goose surfaced a misleading parse failure +similar to: + +```text +The model's response was truncated -- it hit the output token limit while generating this tool call. +``` + +The tool never executed. The model had actually produced a valid tool call, but +Goose's stream merger lost part of the argument JSON. + +### Current Workaround + +Maple's Goose submodule points at a fork containing: + +- `ThirdParties/goose/crates/goose-provider-types/src/formats/openai.rs` +- commit: `1e03dbd Merge streaming tool call deltas by index` + +The patch merges streaming tool-call deltas by `index`, preserving id, function +name, argument fragments, and extra metadata. + +### Requested Goose Improvement + +Upstream this parser behavior and keep a regression test covering: + +- Multiple tool-call deltas with the same `index` in a single stream chunk. +- id/name arriving separately from arguments. +- Argument fragments being appended in arrival order. +- No false "output token limit" error when the merged argument JSON is complete. + +This is not a broad provider-compatibility ask. It is a concrete streaming +parser correctness issue that directly affected embedding Goose with Maple's +local OpenAI-compatible proxy. + +## P0: Embedding-Friendly Dependency and MSRV Boundary + +### Problem + +Embedding Goose changed Maple's desktop build surface. Two concrete issues came +up: + +- Goose v1.41.0 declared `rmcp = "1.4"` in its workspace, but the embedded build + did not compile against the current 1.x API without pinning `rmcp = "=1.4.0"`. +- Maple had to align desktop CI around Rust 1.91.1 after Goose entered the + desktop dependency graph. + +Those may be reasonable requirements, but embedders need them to be explicit and +stable. A desktop app with reproducible builds needs to know the exact supported +dependency and toolchain boundary before shipping. + +### Current Workaround + +Maple pins `rmcp` next to the Goose path dependency: + +```toml +rmcp = { version = "=1.4.0", default-features = false } +``` + +Maple also limits Goose to desktop targets only, so web/iOS/Android do not pull +the embedded runtime. + +### Requested Goose Improvement + +For embedders, Goose should publish one of: + +- A crate with dependency constraints that compile correctly without Goose's + repository lockfile. +- A documented lockfile/vendor strategy for external hosts. +- A minimal embedded feature set that avoids pulling CLI/TUI/desktop-only + surfaces when the host only needs the agent runtime. + +Also document: + +- Supported Rust MSRV. +- Supported desktop platforms. +- Required native libraries or build-time assumptions. +- Which Cargo features are intended for embedded host apps. + +## P1: Runtime Lifecycle Handle + +### Problem + +Maple currently wraps Goose's router in its own Axum server task and implements +readiness, shutdown, and status tracking around it. That is manageable, but the +host app is inventing lifecycle semantics around a runtime it does not fully +own. + +### Current Workaround + +Maple: + +- Finds an available local port. +- Creates a token. +- Builds a local URL. +- Spawns `axum::serve(listener, router)`. +- Polls `/status` until ready. +- Stores its own `shutdown_tx`. +- Tracks runtime status separately for the frontend. + +### Requested Goose Improvement + +Expose a typed embedded runtime handle: + +```rust +let handle = runtime.start().await?; +handle.wait_until_ready().await?; +handle.status().await?; +handle.stop().await?; +handle.cancel_session(session_id).await?; +``` + +The host can still choose whether to expose ACP, a local socket, or a direct +Rust API. The embedded runtime should own lifecycle semantics and provide typed +errors when startup fails. + +## P1: Host-Visible Diagnostics Hooks + +### Problem + +When the initial tool-call failures happened, Maple did not have enough +host-visible evidence from inside Goose to distinguish: + +- A model output issue. +- A provider/proxy cap. +- A Goose stream parser bug. +- A malformed tool-call argument. + +Maple eventually added proxy-side raw LLM logging and better error-body logging +to investigate. That was useful, but it is outside Goose. For an embedded +runtime, the host app needs diagnostics hooks near the point where Goose sends a +provider request, consumes the stream, parses tool calls, and decides whether to +execute a tool. + +### Current Workaround + +Maple writes several logs: + +- Tauri app logs. +- Goose runtime logs. +- Maple proxy raw request/response logs. +- Maple Agent Mode session JSONL. + +That gives enough evidence now, but it required Maple-specific logging around +Goose instead of a structured embedded diagnostics API. + +### Requested Goose Improvement + +Expose opt-in diagnostics hooks with redaction controls: + +- Provider request metadata and redacted request bodies. +- Raw stream chunks before normalization, when enabled. +- Normalized stream events. +- Tool-call parse success/failure details. +- Tool name, tool id, arguments, and parser error context. +- Finish reason and upstream usage details when available. +- Retry attempts and retry reasons. + +This should not require changing ACP. It can be a Rust callback, event channel, +or tracing layer attached to the embedded runtime. + +## P1: Runtime Tool and Permission Policy Callback + +### Problem + +Maple can rely on Goose defaults for the PoC, but a shippable embedded desktop +agent needs a host-owned policy boundary. The host app should be able to decide +which tools are enabled and when to ask the user before running a tool. + +Some of this can be represented over ACP, but the embedded Goose concern is +lower-level: Maple needs a Rust-side way to apply policy before Goose executes +tools. + +### Current Workaround + +Maple selects Goose's `developer` builtins and configures Goose mode. More +specific permission behavior remains Goose-owned. + +### Requested Goose Improvement + +Expose a host policy callback before tool execution: + +```rust +runtime.set_tool_policy(|request| async move { + match request.tool_name.as_str() { + "shell" if request.is_destructive() => ToolDecision::AskUser, + "text_editor" => ToolDecision::Allow, + _ => ToolDecision::Default, + } +}); +``` + +The callback should include: + +- Tool name and id. +- Parsed arguments. +- Working directory. +- Session id. +- Whether Goose believes the operation is read-only or mutating, if known. +- A way to allow, deny, or defer to a host UI prompt. + +## P1: Documented Storage Ownership + +### Problem + +This is not asking Goose to solve ACP session sync. Maple may build its own +remote backup/sync layer later. + +The embedded runtime issue is simpler: Maple needs to know what Goose stores +under `config`, `data`, and `state`, which files are stable, which are cache, +which contain secrets, and what can be safely deleted, migrated, exported, or +backed up. + +### Current Workaround + +Maple creates a Goose root under its own app config directory and separately +keeps Agent Mode session JSONL for the UI. Goose still owns its own config, +data, and state files beneath that root. + +### Requested Goose Improvement + +Document storage ownership for embedded hosts: + +- Which directories Goose needs. +- Which files contain secrets. +- Which files are durable session state. +- Which files are cache/logs. +- How to safely delete a session. +- How to migrate or compact state across Goose versions. +- Whether a host can export/import a Goose session without relying on ACP. + +If Goose wants embedders to treat the whole root as opaque, that is acceptable, +but it should be documented with backup/delete expectations. + +## P1: First-Class OpenAI-Compatible Provider Config + +### Problem + +Maple's model path is intentionally simple: the desktop app ships a local +OpenAI-compatible proxy, and Goose should call that proxy. Today Maple gets +there by configuring Goose's `openai` provider name and writing +`OPENAI_BASE_URL` / `OPENAI_API_KEY`. + +That works, but it is awkward for an embedded app because the host already owns +the local proxy lifecycle and credential generation. + +### Current Workaround + +Maple starts the proxy, creates/loads a local proxy API key, and writes that key +into Goose's config as `OPENAI_API_KEY`. + +### Requested Goose Improvement + +Allow embedded hosts to pass a provider object directly: + +```rust +ProviderConfig::OpenAiCompatible { + base_url, + api_key, + model, + headers, + timeout, +} +``` + +This should avoid requiring env-style key names or writing provider secrets to a +Goose secrets file when the host already has a secret manager. + +## P2: Clearer Parser Error Wording + +### Problem + +The tool-call parser error said the model hit an output token limit. In the case +Maple debugged, that was not the real root cause. The root cause was lost +streamed argument fragments before JSON parsing. + +### Requested Goose Improvement + +When Goose reports tool-call truncation, include the evidence that led to that +conclusion: + +- Upstream finish reason, if known. +- Whether the JSON was incomplete at end of stream. +- Number of received argument characters. +- Tool id/name/index. +- Last argument fragment. + +If the upstream finish reason is not a length/token-limit reason, prefer wording +like "tool-call arguments ended before valid JSON was complete" instead of +asserting a token limit. + +## P2: Minimal Embedded Example + +### Problem + +Most of the integration time was spent discovering the right entrypoint, +configuration keys, directory layout, and startup lifecycle. + +### Requested Goose Improvement + +Add a small embedded Rust example that: + +- Starts Goose from another Rust process. +- Uses an OpenAI-compatible local endpoint. +- Sets model, mode, config/data/state dirs, and builtins. +- Installs a diagnostics hook. +- Starts and stops cleanly. + +A Tauri-specific example would be useful, but a plain Rust example is enough if +the runtime API is explicit. + +## Workarounds Maple Should Remove Before Shipping + +These are the concrete PoC compromises Maple should not carry long term: + +- Forked Goose submodule in `ThirdParties/goose`. +- Path dependency on `../../ThirdParties/goose/crates/goose`. +- Direct imports from Goose modules that are not clearly an embedded public API. +- `rmcp = "=1.4.0"` pin required by embedding outside Goose's lockfile. +- Process-global `GOOSE_PATH_ROOT`, `GOOSE_DISABLE_KEYRING`, and + `GOOSE_MAX_TOKENS` mutation. +- Runtime writes to Goose `config.yaml` and `secrets.yaml` for provider config. +- Host-side deletion of stale Goose config keys. +- Host-wrapped Axum server lifecycle and readiness polling. +- Maple proxy-side raw LLM logging as the only way to inspect provider stream + failures before Goose normalization. + +## Suggested Upstream Patch Order + +1. Upstream the OpenAI-compatible streaming tool-call merge fix and regression + test. +2. Document the current best supported embedded entrypoint, even if it is not + final GDK shape yet. +3. Add a host-owned runtime config builder that avoids env/global config. +4. Publish or stabilize a minimal embedded runtime crate/feature set. +5. Add diagnostics hooks for provider requests, raw stream chunks, parser + failures, and tool execution decisions. +6. Document Goose storage directories and lifecycle expectations for embedded + hosts. + +## Success Criteria For Maple + +Maple can seriously consider shipping embedded Goose when: + +- Goose can be consumed as a normal crate without a forked submodule. +- Maple can configure Goose with Rust values instead of global env/config + mutation. +- Maple can start, stop, and observe Goose through a typed runtime handle. +- The streaming tool-call parser fix is upstream. +- The desktop build footprint, MSRV, and features are documented and stable. +- Maple can attach host diagnostics and tool policy hooks without patching Goose. + +At that point Maple can keep Goose as the built-in agent harness while still +leaving ACP support for external harnesses as a separate integration layer. diff --git a/flake.nix b/flake.nix index 67e5ae16..3d811e16 100644 --- a/flake.nix +++ b/flake.nix @@ -30,7 +30,7 @@ versions = { bun = "1.3.5"; - rust = "1.88.0"; + rust = "1.91.1"; jdk = "21"; xcode = "26.5"; android = { diff --git a/frontend/bun.lock b/frontend/bun.lock index 95f097c1..bf731dd2 100644 --- a/frontend/bun.lock +++ b/frontend/bun.lock @@ -5,6 +5,7 @@ "": { "name": "maple", "dependencies": { + "@agentclientprotocol/sdk": "0.19.0", "@opensecret/react": "3.2.0", "@radix-ui/react-alert-dialog": "^1.1.15", "@radix-ui/react-avatar": "^1.1.11", @@ -86,6 +87,8 @@ "yaml": "^2.8.3", }, "packages": { + "@agentclientprotocol/sdk": ["@agentclientprotocol/sdk@0.19.0", "", { "peerDependencies": { "zod": "^3.25.0 || ^4.0.0" } }, "sha512-U9I8ws9WTOk6jCBAWpXefGSDgVXn14/kV6HFzwWGcstQ02mOQgClMAROHmoIn9GqZbDBDEOkdIbP4P4TEMQdug=="], + "@alloc/quick-lru": ["@alloc/quick-lru@5.2.0", "", {}, "sha512-UrcABB+4bUrFABwbluTIBErXwvbsU/V7TZWfmbgJfbkwiBuziS9gxdODUyuiecfdGQ85jglMW6juS3+z5TsKLw=="], "@babel/code-frame": ["@babel/code-frame@7.29.0", "", { "dependencies": { "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" } }, "sha512-9NhCeYjq9+3uxgdtp20LSiJXJvN0FeCtNGpJxuMFZ1Kv3cWUNb6DOhJwUvcVCzKGR66cw4njwM6hrJLqgOwbcw=="], @@ -1212,6 +1215,8 @@ "zwitch": ["zwitch@2.0.4", "", {}, "sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A=="], + "@agentclientprotocol/sdk/zod": ["zod@3.25.76", "", {}, "sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ=="], + "@babel/generator/@jridgewell/gen-mapping": ["@jridgewell/gen-mapping@0.3.13", "", { "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA=="], "@babel/helper-compilation-targets/browserslist": ["browserslist@4.24.4", "", { "dependencies": { "caniuse-lite": "^1.0.30001688", "electron-to-chromium": "^1.5.73", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.1" }, "bin": { "browserslist": "cli.js" } }, "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A=="], diff --git a/frontend/package.json b/frontend/package.json index d2cb5284..1a7fe1ca 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -31,6 +31,7 @@ "yaml": "^2.8.3" }, "dependencies": { + "@agentclientprotocol/sdk": "0.19.0", "@opensecret/react": "3.2.0", "@radix-ui/react-alert-dialog": "^1.1.15", "@radix-ui/react-avatar": "^1.1.11", diff --git a/frontend/src-tauri/Cargo.lock b/frontend/src-tauri/Cargo.lock index 937928d6..110a208d 100644 --- a/frontend/src-tauri/Cargo.lock +++ b/frontend/src-tauri/Cargo.lock @@ -23,7 +23,7 @@ version = "0.5.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0" dependencies = [ - "crypto-common", + "crypto-common 0.1.6", "generic-array", ] @@ -35,7 +35,7 @@ checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", ] [[package]] @@ -52,6 +52,71 @@ dependencies = [ "subtle", ] +[[package]] +name = "agent-client-protocol" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "16302d16c7531355db16593d99c38c8297db0c4653aa7dd80c3556bb17f4cd8c" +dependencies = [ + "agent-client-protocol-derive", + "agent-client-protocol-schema", + "async-process", + "blocking", + "futures", + "futures-concurrency", + "rustc-hash", + "schemars 1.0.5", + "serde", + "serde_json", + "shell-words", + "tracing", + "uuid", + "windows-sys 0.61.2", +] + +[[package]] +name = "agent-client-protocol-derive" +version = "1.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dd5ca63f112bd2459bcaf9eda0683b9ba95fc3b5e5fdd9036ca941c6a09345b1" +dependencies = [ + "quote", + "syn 2.0.108", +] + +[[package]] +name = "agent-client-protocol-http" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "31ff55efe7a6bb92a5d0226f6055c2f277b7591060d5095c7764f445289ebda3" +dependencies = [ + "agent-client-protocol", + "async-stream", + "axum", + "futures", + "serde_json", + "tokio", + "tower-http 0.7.0", + "tracing", + "uuid", +] + +[[package]] +name = "agent-client-protocol-schema" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ac542aba230234b1591ace7286a47c0514fe3efc3037d43296bde31ba7ee5728" +dependencies = [ + "anyhow", + "derive_more", + "schemars 1.0.5", + "serde", + "serde_json", + "serde_with", + "strum 0.28.0", + "tracing", +] + [[package]] name = "ahash" version = "0.7.8" @@ -63,6 +128,20 @@ dependencies = [ "version_check", ] +[[package]] +name = "ahash" +version = "0.8.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a15f179cd60c4584b8a8c596927aadc462e27f2ca70c04e0071964a73ba7a75" +dependencies = [ + "cfg-if", + "getrandom 0.3.4", + "once_cell", + "serde", + "version_check", + "zerocopy", +] + [[package]] name = "aho-corasick" version = "1.1.4" @@ -87,6 +166,12 @@ dependencies = [ "alloc-no-stdlib", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "android_log-sys" version = "0.3.2" @@ -165,9 +250,9 @@ dependencies = [ [[package]] name = "anyhow" -version = "1.0.100" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" +checksum = "2a4385e2e34eb35d6b3efe798b9eb88096925d87726c0798709bf56d9ed84af3" [[package]] name = "arbitrary" @@ -178,6 +263,29 @@ dependencies = [ "derive_arbitrary", ] +[[package]] +name = "arboard" +version = "3.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0348a1c054491f4bfe6ab86a7b6ab1e44e45d899005de92f58b3df180b36ddaf" +dependencies = [ + "clipboard-win", + "log", + "objc2 0.6.4", + "objc2-app-kit", + "objc2-foundation 0.3.2", + "parking_lot", + "percent-encoding", + "windows-sys 0.60.2", + "x11rb", +] + +[[package]] +name = "arrayref" +version = "0.3.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76a2e8124351fda1ef8aaaa3bbd7ebbcb486bbcd4225aca0aa0d84bb2db8fecb" + [[package]] name = "arrayvec" version = "0.7.6" @@ -247,6 +355,18 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "async-compression" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e79b3f8a79cccc2898f31920fc69f304859b3bd567490f75ebf51ae1c792a9ac" +dependencies = [ + "compression-codecs", + "compression-core", + "pin-project-lite", + "tokio", +] + [[package]] name = "async-executor" version = "1.13.3" @@ -399,6 +519,15 @@ dependencies = [ "system-deps", ] +[[package]] +name = "atoi" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f28d99ec8bfea296261ca1af174f24225171fea9664ba9003cbebee704810528" +dependencies = [ + "num-traits", +] + [[package]] name = "atomic-waker" version = "1.1.2" @@ -441,6 +570,7 @@ checksum = "8a18ed336352031311f4e0b4dd2ff392d4fbb370777c9d18d7fc9d7359f73871" dependencies = [ "axum-core", "axum-macros", + "base64 0.22.1", "bytes", "form_urlencoded", "futures-util", @@ -459,8 +589,10 @@ dependencies = [ "serde_json", "serde_path_to_error", "serde_urlencoded", + "sha1", "sync_wrapper", "tokio", + "tokio-tungstenite", "tower", "tower-layer", "tower-service", @@ -563,6 +695,20 @@ dependencies = [ "wyz", ] +[[package]] +name = "blake3" +version = "1.8.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa83c34e62843d924f905e0f5c866eb1dd6545fc4d719e803d9ba6030371fce" +dependencies = [ + "arrayref", + "arrayvec", + "cc", + "cfg-if", + "constant_time_eq", + "cpufeatures 0.3.0", +] + [[package]] name = "block-buffer" version = "0.10.4" @@ -572,6 +718,15 @@ dependencies = [ "generic-array", ] +[[package]] +name = "block-buffer" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d2f6c7dbe95a6ed67ad9f18e57daf93a2f034c524b99fd2b76d18fdfeb6660aa" +dependencies = [ + "hybrid-array", +] + [[package]] name = "block2" version = "0.5.1" @@ -603,6 +758,12 @@ dependencies = [ "piper", ] +[[package]] +name = "borrow-or-share" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c" + [[package]] name = "borsh" version = "1.5.7" @@ -647,6 +808,26 @@ 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.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5cee35f73844aa3014bb606320a6c1f010249dbdf43342fe54b5a4f6a8ed4b79" +dependencies = [ + "memchr", + "regex-automata", + "serde_core", +] + [[package]] name = "bumpalo" version = "3.19.0" @@ -686,6 +867,12 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "bytecount" +version = "0.6.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175812e0be2bccb6abe50bb8d566126198344f707e304f45c648fd8f2cc0365e" + [[package]] name = "bytemuck" version = "1.24.0" @@ -732,6 +919,16 @@ dependencies = [ "system-deps", ] +[[package]] +name = "calendrical_calculations" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5abbd6eeda6885048d357edc66748eea6e0268e3dd11f326fff5bd248d779c26" +dependencies = [ + "core_maths", + "displaydoc", +] + [[package]] name = "camino" version = "1.2.1" @@ -761,7 +958,7 @@ dependencies = [ "semver", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -833,7 +1030,18 @@ checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818" dependencies = [ "cfg-if", "cipher", - "cpufeatures", + "cpufeatures 0.2.17", +] + +[[package]] +name = "chacha20" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d524456ba66e72eb8b115ff89e01e497f8e6d11d78b70b1aa13c0fbd97540a81" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "rand_core 0.10.1", ] [[package]] @@ -843,7 +1051,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35" dependencies = [ "aead", - "chacha20", + "chacha20 0.9.1", "cipher", "poly1305", "zeroize", @@ -851,9 +1059,9 @@ dependencies = [ [[package]] name = "chrono" -version = "0.4.42" +version = "0.4.45" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +checksum = "1aa79e62e7697b8e29b513a68abacf485adcd1fe8284a4316c5ae868e6633327" dependencies = [ "iana-time-zone", "js-sys", @@ -863,6 +1071,16 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "chrono-tz" +version = "0.10.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a6139a8597ed92cf816dfb33f5dd6cf0bb93a6adc938f11039f371bc5bcd26c3" +dependencies = [ + "chrono", + "phf 0.12.1", +] + [[package]] name = "ciborium" version = "0.2.2" @@ -896,7 +1114,7 @@ version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad" dependencies = [ - "crypto-common", + "crypto-common 0.1.6", "inout", "zeroize", ] @@ -941,6 +1159,15 @@ version = "0.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d" +[[package]] +name = "clipboard-win" +version = "5.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bde03770d3df201d4fb868f2c9c59e66a3e4e2bd06692a0fe701e7103c7e84d4" +dependencies = [ + "error-code", +] + [[package]] name = "cmake" version = "0.1.58" @@ -950,6 +1177,12 @@ dependencies = [ "cc", ] +[[package]] +name = "color_quant" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b" + [[package]] name = "colorchoice" version = "1.0.4" @@ -966,6 +1199,26 @@ dependencies = [ "memchr", ] +[[package]] +name = "compression-codecs" +version = "0.4.38" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce2548391e9c1929c21bf6aa2680af86fe4c1b33e6cea9ac1cfeec0bd11218cf" +dependencies = [ + "brotli", + "compression-core", + "flate2", + "memchr", + "zstd", + "zstd-safe", +] + +[[package]] +name = "compression-core" +version = "0.4.32" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc14f565cf027a105f7a44ccf9e5b424348421a1d8952a8fc9d499d313107789" + [[package]] name = "concurrent-queue" version = "2.5.0" @@ -1001,16 +1254,50 @@ dependencies = [ "tiny-keccak", ] +[[package]] +name = "constant_time_eq" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b" + +[[package]] +name = "convert_case" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "633458d4ef8c78b72454de2d54fd6ab2e60f9e02be22f3c6104cdc8a4e0fceb9" +dependencies = [ + "unicode-segmentation", +] + [[package]] name = "cookie" version = "0.18.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4ddef33a339a91ea89fb53151bd0a4689cfce27055c291dfa69945475d22c747" dependencies = [ + "percent-encoding", "time", "version_check", ] +[[package]] +name = "cookie_store" +version = "0.22.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15b2c103cf610ec6cae3da84a766285b42fd16aad564758459e6ecf128c75206" +dependencies = [ + "cookie", + "document-features", + "idna", + "log", + "publicsuffix", + "serde", + "serde_derive", + "serde_json", + "time", + "url", +] + [[package]] name = "core-foundation" version = "0.9.4" @@ -1074,6 +1361,15 @@ dependencies = [ "libc", ] +[[package]] +name = "core_maths" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77745e017f5edba1a9c1d854f6f3a52dac8a12dd5af5d2f54aecf61e43d80d30" +dependencies = [ + "libm", +] + [[package]] name = "cpufeatures" version = "0.2.17" @@ -1083,6 +1379,30 @@ dependencies = [ "libc", ] +[[package]] +name = "cpufeatures" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8b2a41393f66f16b0823bb79094d54ac5fbd34ab292ddafb9a0456ac9f87d201" +dependencies = [ + "libc", +] + +[[package]] +name = "crc" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" +dependencies = [ + "crc-catalog", +] + +[[package]] +name = "crc-catalog" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "217698eaf96b4a3f0bc4f3662aaa55bdf913cd54d7204591faa790070c6d0853" + [[package]] name = "crc32fast" version = "1.5.0" @@ -1092,6 +1412,17 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "croner" +version = "3.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4aa42bcd3d846ebf66e15bd528d1087f75d1c6c1c66ebff626178a106353c576" +dependencies = [ + "chrono", + "derive_builder", + "strum 0.27.2", +] + [[package]] name = "crossbeam-channel" version = "0.5.15" @@ -1120,6 +1451,15 @@ dependencies = [ "crossbeam-utils", ] +[[package]] +name = "crossbeam-queue" +version = "0.3.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "803d13fb3b09d88be9f4dbc29062c66b19bf7170867ceb746d2a8689bf6c7a26" +dependencies = [ + "crossbeam-utils", +] + [[package]] name = "crossbeam-utils" version = "0.8.21" @@ -1155,6 +1495,15 @@ dependencies = [ "typenum", ] +[[package]] +name = "crypto-common" +version = "0.2.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453" +dependencies = [ + "hybrid-array", +] + [[package]] name = "cssparser" version = "0.36.0" @@ -1164,7 +1513,7 @@ dependencies = [ "cssparser-macros", "dtoa-short", "itoa", - "phf", + "phf 0.13.1", "smallvec 1.15.1", ] @@ -1210,7 +1559,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97fb8b7c4503de7d6ae7b42ab72a5a59857b4c937ec27a3d4539dba95b5ab2be" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "curve25519-dalek-derive", "fiat-crypto", "rustc_version", @@ -1231,19 +1580,29 @@ dependencies = [ [[package]] name = "darling" -version = "0.21.3" +version = "0.20.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cdf337090841a411e2a7f3deb9187445851f91b309c0c0a29e05f74a00a48c0" +checksum = "fc7f46116c46ff9ab3eb1597a45688b6715c6e628b5c133e288e709a29bcb4ee" dependencies = [ - "darling_core", - "darling_macro", + "darling_core 0.20.11", + "darling_macro 0.20.11", ] [[package]] -name = "darling_core" -version = "0.21.3" +name = "darling" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1247195ecd7e3c85f83c8d2a366e4210d588e802133e1e355180a9870b517ea4" +checksum = "25ae13da2f202d56bd7f91c25fba009e7717a1e4a1cc98a76d844b65ae912e9d" +dependencies = [ + "darling_core 0.23.0", + "darling_macro 0.23.0", +] + +[[package]] +name = "darling_core" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d00b9596d185e565c2207a0b01f8bd1a135483d02d9b7b0a54b11da8d53412e" dependencies = [ "fnv", "ident_case", @@ -1253,13 +1612,37 @@ dependencies = [ "syn 2.0.108", ] +[[package]] +name = "darling_core" +version = "0.23.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9865a50f7c335f53564bb694ef660825eb8610e0a53d3e11bf1b0d3df31e03b0" +dependencies = [ + "ident_case", + "proc-macro2", + "quote", + "strsim", + "syn 2.0.108", +] + [[package]] name = "darling_macro" -version = "0.21.3" +version = "0.20.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fc34b93ccb385b40dc71c6fceac4b2ad23662c7eeb248cf10d529b7e055b6ead" +dependencies = [ + "darling_core 0.20.11", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "darling_macro" +version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d38308df82d1080de0afee5d069fa14b0326a88c14f15c5ccda35b4a6c414c81" +checksum = "ac3984ec7bd6cfa798e62b4a642426a5be0e68f9401cfc2a01e3fa9ea2fcdb8d" dependencies = [ - "darling_core", + "darling_core 0.23.0", "quote", "syn 2.0.108", ] @@ -1327,6 +1710,37 @@ dependencies = [ "syn 2.0.108", ] +[[package]] +name = "derive_builder" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "507dfb09ea8b7fa618fcf76e953f4f5e192547945816d5358edffe39f6f94947" +dependencies = [ + "derive_builder_macro", +] + +[[package]] +name = "derive_builder_core" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2d5bcf7b024d6835cfb3d473887cd966994907effbe9227e8c8219824d06c4e8" +dependencies = [ + "darling 0.20.11", + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "derive_builder_macro" +version = "0.20.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab63b0e2bf4d5928aff72e83a7dace85d7bba5fe12dcc3c5a572d78caffd3f3c" +dependencies = [ + "derive_builder_core", + "syn 2.0.108", +] + [[package]] name = "derive_more" version = "2.1.1" @@ -1342,10 +1756,12 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "799a97264921d8623a957f6c3b9011f3b5492f557bbb7a5a19b7fa6d06ba8dcb" dependencies = [ + "convert_case", "proc-macro2", "quote", "rustc_version", "syn 2.0.108", + "unicode-xid", ] [[package]] @@ -1354,12 +1770,22 @@ version = "0.10.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292" dependencies = [ - "block-buffer", + "block-buffer 0.10.4", "const-oid", - "crypto-common", + "crypto-common 0.1.6", "subtle", ] +[[package]] +name = "digest" +version = "0.11.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1dd6dbb5841937940781866fa1281a1ff7bd3bf827091440879f9994983d5c2" +dependencies = [ + "block-buffer 0.12.1", + "crypto-common 0.2.2", +] + [[package]] name = "dirs" version = "6.0.0" @@ -1436,6 +1862,15 @@ dependencies = [ "const-random", ] +[[package]] +name = "document-features" +version = "0.2.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d4b8a88685455ed29a21542a33abd9cb6510b6b129abadabdcef0f4c55bc8f61" +dependencies = [ + "litrs", +] + [[package]] name = "dom_query" version = "0.27.0" @@ -1444,7 +1879,7 @@ checksum = "521e380c0c8afb8d9a1e83a1822ee03556fc3e3e7dbc1fd30be14e37f9cb3f89" dependencies = [ "bit-set", "cssparser", - "foldhash", + "foldhash 0.2.0", "html5ever", "precomputed-hash", "selectors", @@ -1515,7 +1950,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca" dependencies = [ "der", - "digest", + "digest 0.10.7", "elliptic-curve", "rfc6979", "signature", @@ -1527,6 +1962,9 @@ name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" +dependencies = [ + "serde", +] [[package]] name = "elliptic-curve" @@ -1536,7 +1974,7 @@ checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47" dependencies = [ "base16ct", "crypto-bigint", - "digest", + "digest 0.10.7", "ff", "generic-array", "group", @@ -1549,6 +1987,15 @@ dependencies = [ "zeroize", ] +[[package]] +name = "email_address" +version = "0.2.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e079f19b08ca6239f47f8ba8509c11cf3ea30095831f7fed61441475edd8c449" +dependencies = [ + "serde", +] + [[package]] name = "embed-resource" version = "3.0.6" @@ -1642,6 +2089,33 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "error-code" +version = "3.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dea2df4cf52843e0452895c455a1a2cfbb842a1e7329671acf418fdc53ed4c59" + +[[package]] +name = "etcetera" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "136d1b5283a1ab77bd9257427ffd09d8667ced0570b6f938942bc7568ed5b943" +dependencies = [ + "cfg-if", + "home", + "windows-sys 0.48.0", +] + +[[package]] +name = "etcetera" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "de48cc4d1c1d97a20fd819def54b890cadde72ed3ad0c614822a0a433361be96" +dependencies = [ + "cfg-if", + "windows-sys 0.61.2", +] + [[package]] name = "euclid" version = "0.20.14" @@ -1683,6 +2157,28 @@ dependencies = [ "pin-project-lite", ] +[[package]] +name = "fancy-regex" +version = "0.14.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e24cb5a94bcae1e5408b0effca5cd7172ea3c5755049c5f3af4cd283a165298" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + +[[package]] +name = "fancy-regex" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72cf461f865c862bb7dc573f643dd6a2b6842f7c30b07882b56bd148cc2761b8" +dependencies = [ + "bit-set", + "regex-automata", + "regex-syntax", +] + [[package]] name = "fastrand" version = "2.3.0" @@ -1751,6 +2247,12 @@ version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" +[[package]] +name = "fixedbitset" +version = "0.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d674e81391d1e1ab681a28d99df07927c6d4aa5b027d7da16ba32d1d21ecd99" + [[package]] name = "flate2" version = "1.1.5" @@ -1761,12 +2263,40 @@ dependencies = [ "miniz_oxide", ] +[[package]] +name = "fluent-uri" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1918b65d96df47d3591bed19c5cca17e3fa5d0707318e4b5ef2eae01764df7e5" +dependencies = [ + "borrow-or-share", + "ref-cast", + "serde", +] + +[[package]] +name = "flume" +version = "0.11.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "da0e4dd2a88388a1f4ccc7c9ce104604dab68d9f408dc34cd45823d5a9069095" +dependencies = [ + "futures-core", + "futures-sink", + "spin", +] + [[package]] name = "fnv" version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1" +[[package]] +name = "foldhash" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" + [[package]] name = "foldhash" version = "0.2.0" @@ -1824,6 +2354,35 @@ dependencies = [ "percent-encoding", ] +[[package]] +name = "fraction" +version = "0.15.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e076045bb43dac435333ed5f04caf35c7463631d0dae2deb2638d94dd0a5b872" +dependencies = [ + "lazy_static", + "num", +] + +[[package]] +name = "fs-err" +version = "3.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b91aa448ca50d7e79433bdf3ee8d99215430d2ec02ade5aefab2a073a1822e8a" +dependencies = [ + "autocfg", +] + +[[package]] +name = "fs2" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9564fc758e15025b46aa6643b1b77d047d1a56a1aea6e01002ac0c7026876213" +dependencies = [ + "libc", + "winapi", +] + [[package]] name = "fs_extra" version = "1.3.0" @@ -1838,9 +2397,9 @@ checksum = "e6d5a32815ae3f33302d95fdcb2ce17862f8c65363dcfd29360480ba1001fc9c" [[package]] name = "futures" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" +checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", @@ -1853,36 +2412,60 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" +checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", ] +[[package]] +name = "futures-concurrency" +version = "7.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "175cd8cca9e1d45b87f18ffa75088f2099e3c4fe5e2f83e42de112560bea8ea6" +dependencies = [ + "fixedbitset", + "futures-core", + "futures-lite", + "pin-project", + "smallvec 1.15.1", +] + [[package]] name = "futures-core" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" +checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-executor" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" +checksum = "baf29c38818342a3b26b5b923639e7b1f4a61fc5e76102d4b1981c6dc7a7579d" dependencies = [ "futures-core", "futures-task", "futures-util", ] +[[package]] +name = "futures-intrusive" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d930c203dd0b6ff06e0201a4a2fe9149b43c684fd4420555b26d21b1a02956f" +dependencies = [ + "futures-core", + "lock_api", + "parking_lot", +] + [[package]] name = "futures-io" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" +checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-lite" @@ -1899,9 +2482,9 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" +checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", @@ -1910,21 +2493,21 @@ dependencies = [ [[package]] name = "futures-sink" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" +checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" +checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" -version = "0.3.31" +version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" +checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", @@ -1934,7 +2517,6 @@ dependencies = [ "futures-task", "memchr", "pin-project-lite", - "pin-utils", "slab", ] @@ -2080,11 +2662,23 @@ dependencies = [ "cfg-if", "js-sys", "libc", - "r-efi", + "r-efi 5.3.0", "wasip2", "wasm-bindgen", ] +[[package]] +name = "getrandom" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099" +dependencies = [ + "cfg-if", + "libc", + "r-efi 6.0.0", + "rand_core 0.10.1", +] + [[package]] name = "ghash" version = "0.5.1" @@ -2095,6 +2689,16 @@ dependencies = [ "polyval", ] +[[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 = "gio" version = "0.18.4" @@ -2180,6 +2784,19 @@ version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" +[[package]] +name = "globset" +version = "0.4.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "52dfc19153a48bde0cbd630453615c8151bce3a5adfac7a0aebfbf0a1e1f57e3" +dependencies = [ + "aho-corasick", + "bstr", + "log", + "regex-automata", + "regex-syntax", +] + [[package]] name = "gobject-sys" version = "0.18.0" @@ -2192,40 +2809,215 @@ dependencies = [ ] [[package]] -name = "group" -version = "0.13.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +name = "goose" +version = "1.41.0" dependencies = [ - "ff", - "rand_core 0.6.4", + "agent-client-protocol", + "agent-client-protocol-http", + "agent-client-protocol-schema", + "anyhow", + "arboard", + "async-stream", + "async-trait", + "axum", + "base64 0.22.1", + "blake3", + "chrono", + "clap", + "dirs", + "etcetera 0.11.0", + "fs-err", + "fs2", + "futures", + "gethostname", + "goose-acp-macros", + "goose-download-manager", + "goose-providers", + "goose-sdk-types", + "icu_calendar", + "icu_locale", + "ignore", + "image", + "include_dir", + "indexmap 2.12.0", + "indoc", + "jsonschema", + "jsonwebtoken", + "libc", + "lru", + "minijinja", + "nanoid", + "oauth2", + "once_cell", + "pastey", + "process-wrap", + "pulldown-cmark", + "rand 0.10.2", + "rayon", + "regex", + "reqwest 0.13.2", + "rmcp", + "schemars 1.0.5", + "serde", + "serde_json", + "serde_path_to_error", + "serde_urlencoded", + "serde_yaml", + "sha2 0.11.0", + "shell-words", + "shellexpand", + "sqlx", + "strum 0.28.0", "subtle", + "sys-info", + "tempfile", + "thiserror 2.0.18", + "tiktoken-rs", + "tokio", + "tokio-cron-scheduler", + "tokio-stream", + "tokio-util", + "tower-http 0.7.0", + "tracing", + "tracing-appender", + "tracing-futures", + "tracing-subscriber", + "tree-sitter", + "tree-sitter-go", + "tree-sitter-java", + "tree-sitter-javascript", + "tree-sitter-kotlin-ng", + "tree-sitter-python", + "tree-sitter-ruby", + "tree-sitter-rust", + "tree-sitter-swift", + "tree-sitter-typescript", + "unicode-normalization", + "url", + "urlencoding", + "utoipa", + "uuid", + "v_htmlescape", + "webbrowser", + "which", + "winapi", ] [[package]] -name = "gtk" -version = "0.18.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +name = "goose-acp-macros" +version = "1.41.0" dependencies = [ - "atk", - "cairo-rs", - "field-offset", - "futures-channel", - "gdk", - "gdk-pixbuf", - "gio", - "glib", - "gtk-sys", - "gtk3-macros", - "libc", - "pango", - "pkg-config", + "quote", + "syn 2.0.108", ] [[package]] -name = "gtk-sys" -version = "0.18.2" +name = "goose-download-manager" +version = "1.41.0" +dependencies = [ + "anyhow", + "once_cell", + "reqwest 0.13.2", + "serde", + "tokio", + "tracing", + "utoipa", +] + +[[package]] +name = "goose-provider-types" +version = "1.41.0" +dependencies = [ + "anyhow", + "async-stream", + "async-trait", + "base64 0.22.1", + "chrono", + "futures", + "once_cell", + "rand 0.10.2", + "regex", + "reqwest 0.13.2", + "rmcp", + "serde", + "serde_json", + "strum 0.28.0", + "thiserror 2.0.18", + "tokio", + "tracing", + "unicode-normalization", + "utoipa", + "uuid", +] + +[[package]] +name = "goose-providers" +version = "1.41.0" +dependencies = [ + "anyhow", + "async-stream", + "async-trait", + "chrono", + "futures", + "goose-provider-types", + "reqwest 0.13.2", + "rmcp", + "serde", + "serde_json", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", + "url", + "utoipa", +] + +[[package]] +name = "goose-sdk-types" +version = "1.41.0" +dependencies = [ + "agent-client-protocol", + "agent-client-protocol-schema", + "schemars 1.0.5", + "serde", + "serde_json", +] + +[[package]] +name = "group" +version = "0.13.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63" +dependencies = [ + "ff", + "rand_core 0.6.4", + "subtle", +] + +[[package]] +name = "gtk" +version = "0.18.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fd56fb197bfc42bd5d2751f4f017d44ff59fbb58140c6b49f9b3b2bdab08506a" +dependencies = [ + "atk", + "cairo-rs", + "field-offset", + "futures-channel", + "gdk", + "gdk-pixbuf", + "gio", + "glib", + "gtk-sys", + "gtk3-macros", + "libc", + "pango", + "pkg-config", +] + +[[package]] +name = "gtk-sys" +version = "0.18.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8f29a1c21c59553eb7dd40e918be54dccd60c52b049b75119d5d96ce6b624414" dependencies = [ @@ -2290,7 +3082,7 @@ version = "0.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" dependencies = [ - "ahash", + "ahash 0.7.8", ] [[package]] @@ -2299,12 +3091,32 @@ version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +[[package]] +name = "hashbrown" +version = "0.15.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" +dependencies = [ + "allocator-api2", + "equivalent", + "foldhash 0.1.5", +] + [[package]] name = "hashbrown" version = "0.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5419bdc4f6a9207fbeba6d11b604d481addf78ecd10c11ad51e76c2f6482748d" +[[package]] +name = "hashlink" +version = "0.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" +dependencies = [ + "hashbrown 0.15.5", +] + [[package]] name = "hdrhistogram" version = "7.5.4" @@ -2354,7 +3166,16 @@ version = "0.12.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c49c37c09c17a53d937dfbb742eb3a961d65a994e6bcdcf37e7399d0cc8ab5e" dependencies = [ - "digest", + "digest 0.10.7", +] + +[[package]] +name = "home" +version = "0.5.12" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cc627f471c528ff0c4a49e1d5e60450c8f6461dd6d10ba9dcd3a61d3dff7728d" +dependencies = [ + "windows-sys 0.61.2", ] [[package]] @@ -2419,6 +3240,15 @@ version = "1.0.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "df3b46402a9d5adb4c86a0cf463f42e19994e3ee891101b1841f30a545cb49a9" +[[package]] +name = "hybrid-array" +version = "0.4.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "818356c5132c1fede50f837ca96afbe78ff42413047f4abb886217845e1b6c8c" +dependencies = [ + "typenum", +] + [[package]] name = "hyper" version = "1.7.0" @@ -2519,6 +3349,20 @@ dependencies = [ "png 0.17.16", ] +[[package]] +name = "icu_calendar" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6f0e52e009b6b16ba9c0693578796f2dd4aaa59a7f8f920423706714a89ac4e" +dependencies = [ + "calendrical_calculations", + "displaydoc", + "icu_locale_core", + "icu_provider", + "tinystr", + "zerovec", +] + [[package]] name = "icu_collections" version = "2.1.1" @@ -2532,6 +3376,20 @@ dependencies = [ "zerovec", ] +[[package]] +name = "icu_locale" +version = "2.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "532b11722e350ab6bf916ba6eb0efe3ee54b932666afec989465f9243fe6dd60" +dependencies = [ + "icu_collections", + "icu_locale_core", + "icu_provider", + "potential_utf", + "tinystr", + "zerovec", +] + [[package]] name = "icu_locale_core" version = "2.1.1" @@ -2540,6 +3398,7 @@ checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", + "serde", "tinystr", "writeable", "zerovec", @@ -2593,6 +3452,8 @@ checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", + "serde", + "stable_deref_trait", "writeable", "yoke", "zerofrom", @@ -2627,6 +3488,56 @@ dependencies = [ "icu_properties", ] +[[package]] +name = "ignore" +version = "0.4.27" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fe112b004901c62c2faa11f4f75e9864e0cc5af8da71c9115d184a3aa888749f" +dependencies = [ + "crossbeam-deque", + "globset", + "log", + "memchr", + "regex-automata", + "same-file", + "walkdir", + "winapi-util", +] + +[[package]] +name = "image" +version = "0.24.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5690139d2f55868e080017335e4b94cb7414274c74f1669c84fb5feba2c9f69d" +dependencies = [ + "bytemuck", + "byteorder", + "color_quant", + "gif", + "jpeg-decoder", + "num-traits", + "png 0.17.16", +] + +[[package]] +name = "include_dir" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "923d117408f1e49d914f1a379a309cffe4f18c05cf4e3d12e613a15fc81bd0dd" +dependencies = [ + "include_dir_macros", +] + +[[package]] +name = "include_dir_macros" +version = "0.7.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7cab85a7ed0bd5f0e76d93846e0147172bed2e2d3f859bcc33a8d9699cad1a75" +dependencies = [ + "proc-macro2", + "quote", +] + [[package]] name = "indexmap" version = "1.9.3" @@ -2650,6 +3561,15 @@ dependencies = [ "serde_core", ] +[[package]] +name = "indoc" +version = "2.0.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "79cf5c93f93228cf8efb3ba362535fb11199ac548a09ce117c9b1adc3030d706" +dependencies = [ + "rustversion", +] + [[package]] name = "infer" version = "0.19.0" @@ -2747,19 +3667,68 @@ dependencies = [ "cesu8", "cfg-if", "combine", - "jni-sys", + "jni-sys 0.3.0", "log", "thiserror 1.0.69", "walkdir", "windows-sys 0.45.0", ] +[[package]] +name = "jni" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5efd9a482cf3a427f00d6b35f14332adc7902ce91efb778580e180ff90fa3498" +dependencies = [ + "cfg-if", + "combine", + "jni-macros", + "jni-sys 0.4.1", + "log", + "simd_cesu8", + "thiserror 2.0.18", + "walkdir", + "windows-link 0.2.1", +] + +[[package]] +name = "jni-macros" +version = "0.22.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a00109accc170f0bdb141fed3e393c565b6f5e072365c3bd58f5b062591560a3" +dependencies = [ + "proc-macro2", + "quote", + "rustc_version", + "simd_cesu8", + "syn 2.0.108", +] + [[package]] name = "jni-sys" version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" +[[package]] +name = "jni-sys" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c6377a88cb3910bee9b0fa88d4f42e1d2da8e79915598f65fb0c7ee14c878af2" +dependencies = [ + "jni-sys-macros", +] + +[[package]] +name = "jni-sys-macros" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38c0b942f458fe50cdac086d2f946512305e5631e720728f2a61aabcd47a6264" +dependencies = [ + "quote", + "syn 2.0.108", +] + [[package]] name = "jobserver" version = "0.1.34" @@ -2770,6 +3739,12 @@ dependencies = [ "libc", ] +[[package]] +name = "jpeg-decoder" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "00810f1d8b74be64b13dbf3db89ac67740615d6c891f0e7b6179326533011a07" + [[package]] name = "js-sys" version = "0.3.95" @@ -2804,6 +3779,49 @@ dependencies = [ "serde_json", ] +[[package]] +name = "jsonschema" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1b46a0365a611fbf1d2143104dcf910aada96fafd295bab16c60b802bf6fa1d" +dependencies = [ + "ahash 0.8.12", + "base64 0.22.1", + "bytecount", + "email_address", + "fancy-regex 0.14.0", + "fraction", + "idna", + "itoa", + "num-cmp", + "num-traits", + "once_cell", + "percent-encoding", + "referencing", + "regex", + "regex-syntax", + "serde", + "serde_json", + "uuid-simd", +] + +[[package]] +name = "jsonwebtoken" +version = "10.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eba32bfb4ffdeaca3e34431072faf01745c9b26d25504aa7a6cf5684334fc4fc" +dependencies = [ + "base64 0.22.1", + "getrandom 0.2.16", + "js-sys", + "pem", + "serde", + "serde_json", + "signature", + "simple_asn1", + "zeroize", +] + [[package]] name = "keyboard-types" version = "0.7.0" @@ -2832,6 +3850,9 @@ name = "lazy_static" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" +dependencies = [ + "spin", +] [[package]] name = "libappindicator" @@ -2859,9 +3880,9 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.177" +version = "0.2.186" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" +checksum = "68ab91017fe16c622486840e4c83c9a37afeff978bd239b5293d61ece587de66" [[package]] name = "libdbus-sys" @@ -2909,6 +3930,17 @@ dependencies = [ "redox_syscall", ] +[[package]] +name = "libsqlite3-sys" +version = "0.30.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e99fb7a497b1e3339bc746195567ed8d3e24945ecd636e3619d20b9de9e9149" +dependencies = [ + "cc", + "pkg-config", + "vcpkg", +] + [[package]] name = "linux-raw-sys" version = "0.11.0" @@ -2922,7 +3954,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] -name = "lock_api" +name = "litrs" +version = "1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "11d3d7f243d5c5a8b9bb5d6dd2b1602c0cb0b9db1621bafc7ed66e35ff9fe092" + +[[package]] +name = "lock_api" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" @@ -2957,6 +3995,12 @@ dependencies = [ "weezl", ] +[[package]] +name = "lru" +version = "0.18.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8a860605968fce16869fd239cf4237a82f3ac470723415db603b0e8b6c8d4fb9" + [[package]] name = "lru-slab" version = "0.1.2" @@ -2971,7 +4015,9 @@ dependencies = [ "axum", "base64 0.22.1", "dirs", + "futures", "futures-util", + "goose", "hound", "keyring", "log", @@ -2986,13 +4032,15 @@ dependencies = [ "rand_distr", "regex", "reqwest 0.13.2", + "rmcp", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "tauri", "tauri-build", "tauri-plugin", "tauri-plugin-deep-link", + "tauri-plugin-dialog", "tauri-plugin-fs", "tauri-plugin-log", "tauri-plugin-opener", @@ -3022,7 +4070,7 @@ dependencies = [ "serde_json", "tokio", "tower", - "tower-http", + "tower-http 0.6.8", "tracing", "tracing-subscriber", ] @@ -3070,7 +4118,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf" dependencies = [ "cfg-if", - "digest", + "digest 0.10.7", ] [[package]] @@ -3079,6 +4127,12 @@ version = "2.7.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" +[[package]] +name = "memo-map" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "38d1115007560874e373613744c6fba374c17688327a71c1476d1a5954cc857b" + [[package]] name = "memoffset" version = "0.9.1" @@ -3094,6 +4148,26 @@ version = "0.3.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" +[[package]] +name = "mime_guess" +version = "2.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f7c44f8e672c00fe5308fa235f821cb4198414e1c77935c1ab6948d3fd78550e" +dependencies = [ + "mime", + "unicase", +] + +[[package]] +name = "minijinja" +version = "2.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb3d648e68cea56d9858d535ee28f9538404e2dd8cb08ed0bd05dca379477f39" +dependencies = [ + "memo-map", + "serde", +] + [[package]] name = "minimal-lexical" version = "0.2.1" @@ -3118,9 +4192,9 @@ dependencies = [ [[package]] name = "mio" -version = "1.1.0" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +checksum = "02bd0af71c67b473010cbbc60715ee815645a4dc942899111f494b4b737d6fda" dependencies = [ "libc", "wasi", @@ -3144,10 +4218,19 @@ dependencies = [ "once_cell", "png 0.18.1", "serde", - "thiserror 2.0.17", + "thiserror 2.0.18", "windows-sys 0.61.2", ] +[[package]] +name = "nanoid" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628de41fe064cc3f0cf07f3d299ee3e73521adaff72278731d5c8cae3797873" +dependencies = [ + "rand 0.9.4", +] + [[package]] name = "native-tls" version = "0.2.14" @@ -3188,7 +4271,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c3f42e7bbe13d351b6bead8286a43aac9534b82bd3cc43e47037f012ebfd62d4" dependencies = [ "bitflags 2.10.0", - "jni-sys", + "jni-sys 0.3.0", "log", "ndk-sys", "num_enum", @@ -3196,13 +4279,19 @@ dependencies = [ "thiserror 1.0.69", ] +[[package]] +name = "ndk-context" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "27b02d87554356db9e9a873add8782d4ea6e3e58ea071a9adb9a2e8ddb884a8b" + [[package]] name = "ndk-sys" version = "0.6.0+11769913" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ee6cda3051665f1fb8d9e08fc35c96d5a244fb1be711a03b71118828afc9a873" dependencies = [ - "jni-sys", + "jni-sys 0.3.0", ] [[package]] @@ -3224,6 +4313,18 @@ dependencies = [ "memoffset", ] +[[package]] +name = "nix" +version = "0.31.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cf20d2fde8ff38632c426f1165ed7436270b44f199fc55284c38276f9db47c3d" +dependencies = [ + "bitflags 2.10.0", + "cfg-if", + "cfg_aliases", + "libc", +] + [[package]] name = "nom" version = "7.1.3" @@ -3243,6 +4344,20 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "num" +version = "0.4.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "35bd024e8b2ff75562e5f34e7f4905839deb4b22955ef5e73d2fea1b9813cb23" +dependencies = [ + "num-bigint", + "num-complex", + "num-integer", + "num-iter", + "num-rational", + "num-traits", +] + [[package]] name = "num-bigint" version = "0.4.6" @@ -3253,6 +4368,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-bigint-dig" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e661dda6640fad38e827a6d4a310ff4763082116fe217f279885c97f511bb0b7" +dependencies = [ + "lazy_static", + "libm", + "num-integer", + "num-iter", + "num-traits", + "rand 0.8.6", + "smallvec 1.15.1", + "zeroize", +] + +[[package]] +name = "num-cmp" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63335b2e2c34fae2fb0aa2cecfd9f0832a1e24b3b32ecec612c3426d46dc8aaa" + [[package]] name = "num-complex" version = "0.4.6" @@ -3268,6 +4405,17 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c6673768db2d862beb9b39a78fdcb1a69439615d5794a1be50caa9bc92c81967" +[[package]] +name = "num-derive" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed3955f1a9c7c0c15e092f9c887db08b1fc683305fdf6eb6684f22555355e202" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.108", +] + [[package]] name = "num-integer" version = "0.1.46" @@ -3277,6 +4425,28 @@ dependencies = [ "num-traits", ] +[[package]] +name = "num-iter" +version = "0.1.45" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1429034a0490724d0075ebb2bc9e875d6503c3cf69e235a8941aa757d83ef5bf" +dependencies = [ + "autocfg", + "num-integer", + "num-traits", +] + +[[package]] +name = "num-rational" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f83d14da390562dca69fc84082e73e548e1ad308d24accdedd2720017cb37824" +dependencies = [ + "num-bigint", + "num-integer", + "num-traits", +] + [[package]] name = "num-traits" version = "0.2.19" @@ -3318,6 +4488,25 @@ dependencies = [ "libc", ] +[[package]] +name = "oauth2" +version = "5.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "51e219e79014df21a225b1860a479e2dcd7cbd9130f4defd4bd0e191ea31d67d" +dependencies = [ + "base64 0.22.1", + "chrono", + "getrandom 0.2.16", + "http", + "rand 0.8.6", + "serde", + "serde_json", + "serde_path_to_error", + "sha2 0.10.9", + "thiserror 1.0.69", + "url", +] + [[package]] name = "objc-sys" version = "0.3.5" @@ -3354,6 +4543,7 @@ dependencies = [ "block2 0.6.2", "objc2 0.6.4", "objc2-core-foundation", + "objc2-core-graphics", "objc2-foundation 0.3.2", ] @@ -3643,8 +4833,8 @@ dependencies = [ "ring", "serde", "serde_json", - "sha2", - "thiserror 2.0.17", + "sha2 0.10.9", + "thiserror 2.0.18", "tokio", "tracing", "uuid", @@ -3759,7 +4949,7 @@ checksum = "e2aba9f5c7c479925205799216e7e5d07cc1d4fa76ea8058c60a9a30f6a4e890" dependencies = [ "flate2", "pkg-config", - "sha2", + "sha2 0.10.9", "tar", "ureq", ] @@ -3787,9 +4977,15 @@ dependencies = [ "objc2-osa-kit", "serde", "serde_json", - "thiserror 2.0.17", + "thiserror 2.0.18", ] +[[package]] +name = "outref" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a80800c0488c3a21695ea981a54918fbb37abf04f4d0720c453632255e2ff0e" + [[package]] name = "p256" version = "0.13.2" @@ -3799,7 +4995,7 @@ dependencies = [ "ecdsa", "elliptic-curve", "primeorder", - "sha2", + "sha2 0.10.9", ] [[package]] @@ -3856,6 +5052,12 @@ dependencies = [ "windows-link 0.2.1", ] +[[package]] +name = "pastey" +version = "0.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2ee67f1008b1ba2321834326597b8e186293b049a023cdef258527550b9935b4" + [[package]] name = "pathdiff" version = "0.2.3" @@ -3877,6 +5079,16 @@ dependencies = [ "unicode-normalization", ] +[[package]] +name = "pem" +version = "3.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d30c53c26bc5b31a98cd02d20f25a7c8567146caf63ed593a9d87b2775291be" +dependencies = [ + "base64 0.22.1", + "serde_core", +] + [[package]] name = "pem-rfc7468" version = "0.7.0" @@ -3892,6 +5104,15 @@ version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" +[[package]] +name = "phf" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "913273894cec178f401a31ec4b656318d95473527be05c0752cc41cdc32be8b7" +dependencies = [ + "phf_shared 0.12.1", +] + [[package]] name = "phf" version = "0.13.1" @@ -3899,7 +5120,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c1562dc717473dbaa4c1f85a36410e03c047b2e7df7f45ee938fbef64ae7fadf" dependencies = [ "phf_macros", - "phf_shared", + "phf_shared 0.13.1", "serde", ] @@ -3910,7 +5131,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "49aa7f9d80421bca176ca8dbfebe668cc7a2684708594ec9f3c0db0805d5d6e1" dependencies = [ "phf_generator", - "phf_shared", + "phf_shared 0.13.1", ] [[package]] @@ -3920,7 +5141,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "135ace3a761e564ec88c03a77317a7c6b80bb7f7135ef2544dbe054243b89737" dependencies = [ "fastrand", - "phf_shared", + "phf_shared 0.13.1", ] [[package]] @@ -3930,12 +5151,21 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "812f032b54b1e759ccd5f8b6677695d5268c588701effba24601f6932f8269ef" dependencies = [ "phf_generator", - "phf_shared", + "phf_shared 0.13.1", "proc-macro2", "quote", "syn 2.0.108", ] +[[package]] +name = "phf_shared" +version = "0.12.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "06005508882fb681fd97892ecff4b7fd0fee13ef1aa569f8695dae7ab9099981" +dependencies = [ + "siphasher", +] + [[package]] name = "phf_shared" version = "0.13.1" @@ -3988,6 +5218,17 @@ dependencies = [ "futures-io", ] +[[package]] +name = "pkcs1" +version = "0.7.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8ffb9f10fa047879315e6625af03c164b16962a5368d724ed16323b68ace47f" +dependencies = [ + "der", + "pkcs8", + "spki", +] + [[package]] name = "pkcs8" version = "0.10.2" @@ -4063,7 +5304,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf" dependencies = [ - "cpufeatures", + "cpufeatures 0.2.17", "opaque-debug", "universal-hash", ] @@ -4075,7 +5316,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9d1fe60d06143b2430aa532c94cfe9e29783047f06c0d7fd359a9a51b729fa25" dependencies = [ "cfg-if", - "cpufeatures", + "cpufeatures 0.2.17", "opaque-debug", "universal-hash", ] @@ -4113,6 +5354,8 @@ version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ + "serde_core", + "writeable", "zerovec", ] @@ -4208,6 +5451,26 @@ dependencies = [ "unicode-ident", ] +[[package]] +name = "process-wrap" +version = "9.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2e842efad9119158434d193c6682e2ebee4b44d6ad801d7b349623b3f57cdf55" +dependencies = [ + "futures", + "indexmap 2.12.0", + "nix 0.31.3", + "tokio", + "tracing", + "windows 0.62.2", +] + +[[package]] +name = "psl-types" +version = "2.0.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33cb294fe86a74cbcf50d4445b37da762029549ebeea341421c7c70370f86cac" + [[package]] name = "ptr_meta" version = "0.1.4" @@ -4228,6 +5491,27 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "publicsuffix" +version = "2.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6f42ea446cab60335f76979ec15e12619a2165b5ae2c12166bef27d283a9fadf" +dependencies = [ + "idna", + "psl-types", +] + +[[package]] +name = "pulldown-cmark" +version = "0.13.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e9f068eba8e7071c5f9511831b44f32c740d5adf574e990f946ddb53db2f314e" +dependencies = [ + "bitflags 2.10.0", + "memchr", + "unicase", +] + [[package]] name = "quick-xml" version = "0.38.3" @@ -4251,7 +5535,7 @@ dependencies = [ "rustc-hash", "rustls", "socket2", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tracing", "web-time", @@ -4273,7 +5557,7 @@ dependencies = [ "rustls", "rustls-pki-types", "slab", - "thiserror 2.0.17", + "thiserror 2.0.18", "tinyvec", "tracing", "web-time", @@ -4308,6 +5592,12 @@ version = "5.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" +[[package]] +name = "r-efi" +version = "6.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f8dcc9c7d52a811697d2151c701e0d08956f92b0e24136cf4cf27b57a6a0d9bf" + [[package]] name = "radium" version = "0.7.0" @@ -4335,6 +5625,17 @@ dependencies = [ "rand_core 0.9.5", ] +[[package]] +name = "rand" +version = "0.10.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c7f5fa3a058cd35567ef9bfa5e75732bee0f9e4c55fa90477bef2dfcdbc4be80" +dependencies = [ + "chacha20 0.10.1", + "getrandom 0.4.3", + "rand_core 0.10.1", +] + [[package]] name = "rand_chacha" version = "0.3.1" @@ -4373,6 +5674,12 @@ dependencies = [ "getrandom 0.3.4", ] +[[package]] +name = "rand_core" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "63b8176103e19a2643978565ca18b50549f6101881c443590420e4dc998a3c69" + [[package]] name = "rand_distr" version = "0.4.3" @@ -4438,7 +5745,7 @@ checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" dependencies = [ "getrandom 0.2.16", "libredox", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -4461,11 +5768,25 @@ dependencies = [ "syn 2.0.108", ] +[[package]] +name = "referencing" +version = "0.30.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8eff4fa778b5c2a57e85c5f2fe3a709c52f0e60d23146e2151cbef5893f420e" +dependencies = [ + "ahash 0.8.12", + "fluent-uri", + "once_cell", + "parking_lot", + "percent-encoding", + "serde_json", +] + [[package]] name = "regex" -version = "1.12.2" +version = "1.12.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" +checksum = "f1292b7759ae1cb9ec195452d1390a074f0cd8541ab7a5a8c31cd6db45d4a6ba" dependencies = [ "aho-corasick", "memchr", @@ -4486,9 +5807,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.8" +version = "0.8.11" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" +checksum = "d6f6ff9a378485b298a5286656da665ba74413d36db0979633275d2e708145d4" [[package]] name = "rend" @@ -4533,7 +5854,7 @@ dependencies = [ "tokio-rustls", "tokio-util", "tower", - "tower-http", + "tower-http 0.6.8", "tower-service", "url", "wasm-bindgen", @@ -4551,7 +5872,10 @@ checksum = "ab3f43e3283ab1488b624b44b0e988d0acea0b3214e694730a055cb6b2efa801" dependencies = [ "base64 0.22.1", "bytes", + "cookie", + "cookie_store", "encoding_rs", + "futures-channel", "futures-core", "futures-util", "h2", @@ -4564,6 +5888,7 @@ dependencies = [ "js-sys", "log", "mime", + "mime_guess", "percent-encoding", "pin-project-lite", "quinn", @@ -4572,12 +5897,13 @@ dependencies = [ "rustls-platform-verifier", "serde", "serde_json", + "serde_urlencoded", "sync_wrapper", "tokio", "tokio-rustls", "tokio-util", "tower", - "tower-http", + "tower-http 0.6.8", "tower-service", "url", "wasm-bindgen", @@ -4596,6 +5922,30 @@ dependencies = [ "subtle", ] +[[package]] +name = "rfd" +version = "0.16.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a15ad77d9e70a92437d8f74c35d99b4e4691128df018833e99f90bcd36152672" +dependencies = [ + "block2 0.6.2", + "dispatch2", + "glib-sys", + "gobject-sys", + "gtk-sys", + "js-sys", + "log", + "objc2 0.6.4", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-foundation 0.3.2", + "raw-window-handle", + "wasm-bindgen", + "wasm-bindgen-futures", + "web-sys", + "windows-sys 0.60.2", +] + [[package]] name = "ring" version = "0.17.14" @@ -4639,6 +5989,71 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "rmcp" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f542f74cf247da16f19bbc87e298cd201e912314f4083e88cdd671f44f5fcb53" +dependencies = [ + "async-trait", + "bytes", + "chrono", + "futures", + "http", + "http-body-util", + "hyper", + "hyper-util", + "oauth2", + "pastey", + "pin-project-lite", + "process-wrap", + "reqwest 0.13.2", + "rmcp-macros", + "schemars 1.0.5", + "serde", + "serde_json", + "sse-stream", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tokio-util", + "tracing", + "url", +] + +[[package]] +name = "rmcp-macros" +version = "1.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1aad0035b69380782d78ea95b508327e6deaa2235909053e596eea8f27b5e1d5" +dependencies = [ + "darling 0.23.0", + "proc-macro2", + "quote", + "serde_json", + "syn 2.0.108", +] + +[[package]] +name = "rsa" +version = "0.9.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8573f03f5883dcaebdfcf4725caa1ecb9c15b2ef50c43a07b816e06799bb12d" +dependencies = [ + "const-oid", + "digest 0.10.7", + "num-bigint-dig", + "num-integer", + "num-traits", + "pkcs1", + "pkcs8", + "rand_core 0.6.4", + "signature", + "spki", + "subtle", + "zeroize", +] + [[package]] name = "rust-ini" version = "0.21.3" @@ -4747,7 +6162,7 @@ checksum = "1d99feebc72bae7ab76ba994bb5e121b8d83d910ca40b36e0921f53becc41784" dependencies = [ "core-foundation 0.10.1", "core-foundation-sys", - "jni", + "jni 0.21.1", "log", "once_cell", "rustls", @@ -4816,7 +6231,7 @@ checksum = "3fbf2ae1b8bc8e02df939598064d22402220cd5bbcca1c76f7d6a310974d5615" dependencies = [ "dyn-clone", "indexmap 1.9.3", - "schemars_derive", + "schemars_derive 0.8.22", "serde", "serde_json", "url", @@ -4841,8 +6256,10 @@ version = "1.0.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1317c3bf3e7df961da95b0a56a172a02abead31276215a0497241a7624b487ce" dependencies = [ + "chrono", "dyn-clone", "ref-cast", + "schemars_derive 1.0.5", "serde", "serde_json", ] @@ -4859,6 +6276,18 @@ dependencies = [ "syn 2.0.108", ] +[[package]] +name = "schemars_derive" +version = "1.0.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5f760a6150d45dd66ec044983c124595ae76912e77ed0b44124cb3e415cce5d9" +dependencies = [ + "proc-macro2", + "quote", + "serde_derive_internals", + "syn 2.0.108", +] + [[package]] name = "scopeguard" version = "1.2.0" @@ -4932,7 +6361,7 @@ dependencies = [ "derive_more", "log", "new_debug_unreachable", - "phf", + "phf 0.13.1", "phf_codegen", "precomputed-hash", "rustc-hash", @@ -5009,6 +6438,7 @@ version = "1.0.145" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "402a6f66d8c709116cf22f558eab210f5a50187f702eb4d7e5ef38d9a7f1c79c" dependencies = [ + "indexmap 2.12.0", "itoa", "memchr", "ryu", @@ -5070,11 +6500,12 @@ dependencies = [ [[package]] name = "serde_with" -version = "3.15.1" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa66c845eee442168b2c8134fec70ac50dc20e760769c8ba0ad1319ca1959b04" +checksum = "76a5c54c7310e7b8b9577c286d7e399ddd876c3e12b3ed917a8aabc4b96e9e8c" dependencies = [ "base64 0.22.1", + "bs58", "chrono", "hex", "indexmap 1.9.3", @@ -5089,16 +6520,29 @@ dependencies = [ [[package]] name = "serde_with_macros" -version = "3.15.1" +version = "3.21.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b91a903660542fced4e99881aa481bdbaec1634568ee02e0b8bd57c64cb38955" +checksum = "84d57bc0c8b9a17920c178daa6bb924850d54a9c97ab45194bb8c17ad66bb660" dependencies = [ - "darling", + "darling 0.23.0", "proc-macro2", "quote", "syn 2.0.108", ] +[[package]] +name = "serde_yaml" +version = "0.9.34+deprecated" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6a8b1a1a2ebf674015cc02edccce75287f1a0130d394307b36743c2f5d504b47" +dependencies = [ + "indexmap 2.12.0", + "itoa", + "ryu", + "serde", + "unsafe-libyaml", +] + [[package]] name = "serialize-to-javascript" version = "0.1.2" @@ -5130,6 +6574,17 @@ dependencies = [ "stable_deref_trait", ] +[[package]] +name = "sha1" +version = "0.10.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba" +dependencies = [ + "cfg-if", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + [[package]] name = "sha2" version = "0.10.9" @@ -5137,8 +6592,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", - "cpufeatures", - "digest", + "cpufeatures 0.2.17", + "digest 0.10.7", +] + +[[package]] +name = "sha2" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "446ba717509524cb3f22f17ecc096f10f4822d76ab5c0b9822c5f9c284e825f4" +dependencies = [ + "cfg-if", + "cpufeatures 0.3.0", + "digest 0.11.3", ] [[package]] @@ -5150,6 +6616,21 @@ dependencies = [ "lazy_static", ] +[[package]] +name = "shell-words" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc6fe69c597f9c37bfeeeeeb33da3530379845f10be461a66d16d03eca2ded77" + +[[package]] +name = "shellexpand" +version = "3.1.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32824fab5e16e6c4d86dc1ba84489390419a39f97699852b66480bb87d297ed8" +dependencies = [ + "dirs", +] + [[package]] name = "shlex" version = "1.3.0" @@ -5171,7 +6652,7 @@ version = "2.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de" dependencies = [ - "digest", + "digest 0.10.7", "rand_core 0.6.4", ] @@ -5181,12 +6662,34 @@ version = "0.3.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe" +[[package]] +name = "simd_cesu8" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "94f90157bb87cddf702797c5dadfa0be7d266cdf49e22da2fcaa32eff75b2c33" +dependencies = [ + "rustc_version", + "simdutf8", +] + [[package]] name = "simdutf8" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" +[[package]] +name = "simple_asn1" +version = "0.6.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0d585997b0ac10be3c5ee635f1bab02d512760d14b7c468801ac8a01d9ae5f1d" +dependencies = [ + "num-bigint", + "num-traits", + "thiserror 2.0.18", + "time", +] + [[package]] name = "siphasher" version = "1.0.1" @@ -5204,6 +6707,9 @@ name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" +dependencies = [ + "serde", +] [[package]] name = "smallvec" @@ -5213,12 +6719,12 @@ checksum = "51d44cfb396c3caf6fbfd0ab422af02631b69ddd96d2eff0b0f0724f9024051b" [[package]] name = "socket2" -version = "0.6.1" +version = "0.6.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +checksum = "52d1cfed4120b4d927bf7c0f86d2087a4a7d6027c906d9f9d525a80573b9be51" dependencies = [ "libc", - "windows-sys 0.60.2", + "windows-sys 0.61.2", ] [[package]] @@ -5280,6 +6786,15 @@ dependencies = [ "system-deps", ] +[[package]] +name = "spin" +version = "0.9.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67" +dependencies = [ + "lock_api", +] + [[package]] name = "spki" version = "0.7.3" @@ -5290,6 +6805,211 @@ dependencies = [ "der", ] +[[package]] +name = "sqlx" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fefb893899429669dcdd979aff487bd78f4064e5e7907e4269081e0ef7d97dc" +dependencies = [ + "sqlx-core", + "sqlx-macros", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", +] + +[[package]] +name = "sqlx-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ee6798b1838b6a0f69c007c133b8df5866302197e404e8b6ee8ed3e3a5e68dc6" +dependencies = [ + "base64 0.22.1", + "bytes", + "chrono", + "crc", + "crossbeam-queue", + "either", + "event-listener", + "futures-core", + "futures-intrusive", + "futures-io", + "futures-util", + "hashbrown 0.15.5", + "hashlink", + "indexmap 2.12.0", + "log", + "memchr", + "once_cell", + "percent-encoding", + "serde", + "serde_json", + "sha2 0.10.9", + "smallvec 1.15.1", + "thiserror 2.0.18", + "tokio", + "tokio-stream", + "tracing", + "url", +] + +[[package]] +name = "sqlx-macros" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a2d452988ccaacfbf5e0bdbc348fb91d7c8af5bee192173ac3636b5fb6e6715d" +dependencies = [ + "proc-macro2", + "quote", + "sqlx-core", + "sqlx-macros-core", + "syn 2.0.108", +] + +[[package]] +name = "sqlx-macros-core" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "19a9c1841124ac5a61741f96e1d9e2ec77424bf323962dd894bdb93f37d5219b" +dependencies = [ + "dotenvy", + "either", + "heck 0.5.0", + "hex", + "once_cell", + "proc-macro2", + "quote", + "serde", + "serde_json", + "sha2 0.10.9", + "sqlx-core", + "sqlx-mysql", + "sqlx-postgres", + "sqlx-sqlite", + "syn 2.0.108", + "tokio", + "url", +] + +[[package]] +name = "sqlx-mysql" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "aa003f0038df784eb8fecbbac13affe3da23b45194bd57dba231c8f48199c526" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags 2.10.0", + "byteorder", + "bytes", + "chrono", + "crc", + "digest 0.10.7", + "dotenvy", + "either", + "futures-channel", + "futures-core", + "futures-io", + "futures-util", + "generic-array", + "hex", + "hkdf", + "hmac", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "percent-encoding", + "rand 0.8.6", + "rsa", + "serde", + "sha1", + "sha2 0.10.9", + "smallvec 1.15.1", + "sqlx-core", + "stringprep", + "thiserror 2.0.18", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-postgres" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db58fcd5a53cf07c184b154801ff91347e4c30d17a3562a635ff028ad5deda46" +dependencies = [ + "atoi", + "base64 0.22.1", + "bitflags 2.10.0", + "byteorder", + "chrono", + "crc", + "dotenvy", + "etcetera 0.8.0", + "futures-channel", + "futures-core", + "futures-util", + "hex", + "hkdf", + "hmac", + "home", + "itoa", + "log", + "md-5", + "memchr", + "once_cell", + "rand 0.8.6", + "serde", + "serde_json", + "sha2 0.10.9", + "smallvec 1.15.1", + "sqlx-core", + "stringprep", + "thiserror 2.0.18", + "tracing", + "whoami", +] + +[[package]] +name = "sqlx-sqlite" +version = "0.8.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2d12fe70b2c1b4401038055f90f151b78208de1f9f89a7dbfd41587a10c3eea" +dependencies = [ + "atoi", + "chrono", + "flume", + "futures-channel", + "futures-core", + "futures-executor", + "futures-intrusive", + "futures-util", + "libsqlite3-sys", + "log", + "percent-encoding", + "serde", + "serde_urlencoded", + "sqlx-core", + "thiserror 2.0.18", + "tracing", + "url", +] + +[[package]] +name = "sse-stream" +version = "0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "39f24a9b78c40b90817bbcd1821c74ddfd74916aadd29403d001532a9195532d" +dependencies = [ + "bytes", + "futures-util", + "http-body", + "http-body-util", + "pin-project-lite", +] + [[package]] name = "stable_deref_trait" version = "1.2.1" @@ -5300,7 +7020,13 @@ checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" name = "static_assertions" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" +checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f" + +[[package]] +name = "streaming-iterator" +version = "0.1.9" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b2231b7c3057d5e4ad0156fb3dc807d900806020c5ffa3ee6ff2c8c76fb8520" [[package]] name = "string_cache" @@ -5310,7 +7036,7 @@ checksum = "a18596f8c785a729f2819c0f6a7eae6ebeebdfffbfe4214ae6b087f690e31901" dependencies = [ "new_debug_unreachable", "parking_lot", - "phf_shared", + "phf_shared 0.13.1", "precomputed-hash", ] @@ -5321,17 +7047,70 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "585635e46db231059f76c5849798146164652513eb9e8ab2685939dd90f29b69" dependencies = [ "phf_generator", - "phf_shared", + "phf_shared 0.13.1", "proc-macro2", "quote", ] +[[package]] +name = "stringprep" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7b4df3d392d81bd458a8a621b8bffbd2302a12ffe288a9d931670948749463b1" +dependencies = [ + "unicode-bidi", + "unicode-normalization", + "unicode-properties", +] + [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" +[[package]] +name = "strum" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af23d6f6c1a224baef9d3f61e287d2761385a5b88fdab4eb4c6f11aeb54c4bcf" +dependencies = [ + "strum_macros 0.27.2", +] + +[[package]] +name = "strum" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9628de9b8791db39ceda2b119bbe13134770b56c138ec1d3af810d045c04f9bd" +dependencies = [ + "strum_macros 0.28.0", +] + +[[package]] +name = "strum_macros" +version = "0.27.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7695ce3845ea4b33927c055a39dc438a45b059f7c1b3d91d38d10355fb8cbca7" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.108", +] + +[[package]] +name = "strum_macros" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ab85eea0270ee17587ed4156089e10b9e6880ee688791d45a905f5b1ca36f664" +dependencies = [ + "heck 0.5.0", + "proc-macro2", + "quote", + "syn 2.0.108", +] + [[package]] name = "subtle" version = "2.6.1" @@ -5349,6 +7128,12 @@ dependencies = [ "serde_json", ] +[[package]] +name = "symlink" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a7973cce6668464ea31f176d85b13c7ab3bba2cb3b77a2ed26abd7801688010a" + [[package]] name = "syn" version = "1.0.109" @@ -5391,6 +7176,16 @@ dependencies = [ "syn 2.0.108", ] +[[package]] +name = "sys-info" +version = "0.9.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b3a0d0aba8bf96a0e1ddfdc352fc53b3df7f39318c71854910c3c4b024ae52c" +dependencies = [ + "cc", + "libc", +] + [[package]] name = "sys-locale" version = "0.3.2" @@ -5450,7 +7245,7 @@ dependencies = [ "gdkwayland-sys", "gdkx11-sys", "gtk", - "jni", + "jni 0.21.1", "libc", "log", "ndk", @@ -5466,7 +7261,7 @@ dependencies = [ "tao-macros", "unicode-segmentation", "url", - "windows", + "windows 0.61.3", "windows-core 0.61.2", "windows-version", "x11-dl", @@ -5523,7 +7318,7 @@ dependencies = [ "gtk", "heck 0.5.0", "http", - "jni", + "jni 0.21.1", "libc", "log", "mime", @@ -5547,14 +7342,14 @@ dependencies = [ "tauri-runtime", "tauri-runtime-wry", "tauri-utils", - "thiserror 2.0.17", + "thiserror 2.0.18", "tokio", "tray-icon", "url", "webkit2gtk", "webview2-com", "window-vibrancy", - "windows", + "windows 0.61.3", ] [[package]] @@ -5595,10 +7390,10 @@ dependencies = [ "semver", "serde", "serde_json", - "sha2", + "sha2 0.10.9", "syn 2.0.108", "tauri-utils", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", "url", "uuid", @@ -5649,13 +7444,31 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-utils", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "url", "windows-registry", "windows-result 0.3.4", ] +[[package]] +name = "tauri-plugin-dialog" +version = "2.7.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "65981abb771e74e571a38196c3baa11c459379164791eba0e67abc1a5fac9884" +dependencies = [ + "log", + "raw-window-handle", + "rfd", + "serde", + "serde_json", + "tauri", + "tauri-plugin", + "tauri-plugin-fs", + "thiserror 2.0.18", + "url", +] + [[package]] name = "tauri-plugin-fs" version = "2.5.1" @@ -5675,7 +7488,7 @@ dependencies = [ "tauri", "tauri-plugin", "tauri-utils", - "thiserror 2.0.17", + "thiserror 2.0.18", "toml 1.1.0+spec-1.1.0", "url", ] @@ -5698,7 +7511,7 @@ dependencies = [ "swift-rs", "tauri", "tauri-plugin", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", ] @@ -5718,9 +7531,9 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin", - "thiserror 2.0.17", + "thiserror 2.0.18", "url", - "windows", + "windows 0.61.3", "zbus", ] @@ -5739,7 +7552,7 @@ dependencies = [ "sys-locale", "tauri", "tauri-plugin", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -5751,7 +7564,7 @@ dependencies = [ "serde", "tauri", "tauri-plugin", - "thiserror 2.0.17", + "thiserror 2.0.18", ] [[package]] @@ -5764,7 +7577,7 @@ dependencies = [ "serde_json", "tauri", "tauri-plugin-deep-link", - "thiserror 2.0.17", + "thiserror 2.0.18", "tracing", "windows-sys 0.60.2", "zbus", @@ -5795,7 +7608,7 @@ dependencies = [ "tauri", "tauri-plugin", "tempfile", - "thiserror 2.0.17", + "thiserror 2.0.18", "time", "tokio", "url", @@ -5813,7 +7626,7 @@ dependencies = [ "dpi", "gtk", "http", - "jni", + "jni 0.21.1", "objc2 0.6.4", "objc2-ui-kit", "objc2-web-kit", @@ -5821,11 +7634,11 @@ dependencies = [ "serde", "serde_json", "tauri-utils", - "thiserror 2.0.17", + "thiserror 2.0.18", "url", "webkit2gtk", "webview2-com", - "windows", + "windows 0.61.3", ] [[package]] @@ -5836,7 +7649,7 @@ checksum = "b83849ee63ecb27a8e8d0fe51915ca215076914aca43f96db1179f0f415f6cd9" dependencies = [ "gtk", "http", - "jni", + "jni 0.21.1", "log", "objc2 0.6.4", "objc2-app-kit", @@ -5850,7 +7663,7 @@ dependencies = [ "url", "webkit2gtk", "webview2-com", - "windows", + "windows 0.61.3", "wry", ] @@ -5872,7 +7685,7 @@ dependencies = [ "json-patch", "log", "memchr", - "phf", + "phf 0.13.1", "plist", "proc-macro2", "quote", @@ -5884,7 +7697,7 @@ dependencies = [ "serde_json", "serde_with", "swift-rs", - "thiserror 2.0.17", + "thiserror 2.0.18", "toml 1.1.0+spec-1.1.0", "url", "urlpattern", @@ -5936,11 +7749,11 @@ dependencies = [ [[package]] name = "thiserror" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f63587ca0f12b72a0600bcba1d40081f830876000bb46dd2337a3051618f4fc8" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" dependencies = [ - "thiserror-impl 2.0.17", + "thiserror-impl 2.0.18", ] [[package]] @@ -5956,9 +7769,9 @@ dependencies = [ [[package]] name = "thiserror-impl" -version = "2.0.17" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3ff15c8ecd7de3849db632e14d18d2571fa09dfc5ed93479bc4485c7a517c913" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", @@ -5974,6 +7787,21 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "tiktoken-rs" +version = "0.12.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "027853bbf8c7763b77c5c595f1c271c7d536ced7d6f83452911b944621e57fc2" +dependencies = [ + "anyhow", + "base64 0.22.1", + "bstr", + "fancy-regex 0.17.0", + "lazy_static", + "regex", + "rustc-hash", +] + [[package]] name = "time" version = "0.3.47" @@ -6023,6 +7851,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", + "serde_core", "zerovec", ] @@ -6043,9 +7872,9 @@ checksum = "1f3ccbac311fea05f86f61904b462b55fb3df8837a366dfc601a0161d0532f20" [[package]] name = "tokio" -version = "1.48.0" +version = "1.52.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +checksum = "8fc7f01b389ac15039e4dc9531aa973a135d7a4135281b12d7c1bc79fd57fffe" dependencies = [ "bytes", "libc", @@ -6058,11 +7887,27 @@ dependencies = [ "windows-sys 0.61.2", ] +[[package]] +name = "tokio-cron-scheduler" +version = "0.15.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1f50e41f200fd8ed426489bd356910ede4f053e30cebfbd59ef0f856f0d7432a" +dependencies = [ + "chrono", + "chrono-tz", + "croner", + "num-derive", + "num-traits", + "tokio", + "tracing", + "uuid", +] + [[package]] name = "tokio-macros" -version = "2.6.0" +version = "2.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +checksum = "385a6cb71ab9ab790c5fe8d67f1645e6c450a7ce006a33de03daa956cf70a496" dependencies = [ "proc-macro2", "quote", @@ -6079,6 +7924,29 @@ dependencies = [ "tokio", ] +[[package]] +name = "tokio-stream" +version = "0.1.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" +dependencies = [ + "futures-core", + "pin-project-lite", + "tokio", +] + +[[package]] +name = "tokio-tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d25a406cddcc431a75d3d9afc6a7c0f7428d4891dd973e4d54c56b46127bf857" +dependencies = [ + "futures-util", + "log", + "tokio", + "tungstenite", +] + [[package]] name = "tokio-util" version = "0.7.17" @@ -6087,6 +7955,7 @@ checksum = "2efa149fe76073d6e8fd97ef4f4eca7b67f599660115591483572e406e165594" dependencies = [ "bytes", "futures-core", + "futures-io", "futures-sink", "pin-project-lite", "tokio", @@ -6238,19 +8107,39 @@ version = "0.6.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d4e6559d53cc268e5031cd8429d05415bc4cb4aefc4aa5d6cc35fbf5b924a1f8" dependencies = [ + "async-compression", "bitflags 2.10.0", "bytes", + "futures-core", "futures-util", "http", "http-body", + "http-body-util", "iri-string", "pin-project-lite", + "tokio", + "tokio-util", "tower", "tower-layer", "tower-service", "tracing", ] +[[package]] +name = "tower-http" +version = "0.7.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b11f75e912b0c2be01b63d8cf8057b8c3f97cf34abb3d431a3a4c8675498e233" +dependencies = [ + "bitflags 2.10.0", + "bytes", + "http", + "percent-encoding", + "pin-project-lite", + "tower-layer", + "tower-service", +] + [[package]] name = "tower-layer" version = "0.3.3" @@ -6265,9 +8154,9 @@ checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" -version = "0.1.41" +version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "784e0ac535deb450455cbfa28a6f0df145ea1bb7ae51b821cf5e7927fdcfbdd0" +checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "log", "pin-project-lite", @@ -6275,11 +8164,24 @@ dependencies = [ "tracing-core", ] +[[package]] +name = "tracing-appender" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "050686193eb999b4bb3bc2acfa891a13da00f79734704c4b8b4ef1a10b368a3c" +dependencies = [ + "crossbeam-channel", + "symlink", + "thiserror 2.0.18", + "time", + "tracing-subscriber", +] + [[package]] name = "tracing-attributes" -version = "0.1.30" +version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "81383ab64e72a7a8b8e13130c49e3dab29def6d0c7d76a03087b3cf71c5c6903" +checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", @@ -6287,64 +8189,200 @@ dependencies = [ ] [[package]] -name = "tracing-core" -version = "0.1.34" +name = "tracing-core" +version = "0.1.36" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" +dependencies = [ + "once_cell", + "valuable", +] + +[[package]] +name = "tracing-futures" +version = "0.2.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "97d095ae15e245a057c8e8451bab9b3ee1e1f68e9ba2b4fbc18d0ac5237835f2" +dependencies = [ + "futures", + "futures-task", + "pin-project", + "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-serde" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "704b1aeb7be0d0a84fc9828cae51dab5970fee5088f83d1dd7ee6f6246fc6ff1" +dependencies = [ + "serde", + "tracing-core", +] + +[[package]] +name = "tracing-subscriber" +version = "0.3.23" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb7f578e5945fb242538965c2d0b04418d38ec25c79d160cd279bf0731c8d319" +dependencies = [ + "matchers", + "nu-ansi-term", + "once_cell", + "regex-automata", + "serde", + "serde_json", + "sharded-slab", + "smallvec 1.15.1", + "thread_local", + "time", + "tracing", + "tracing-core", + "tracing-log", + "tracing-serde", +] + +[[package]] +name = "tray-icon" +version = "0.23.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "15edbb0d80583e85ee8df283410038e17314df5cba30da2087a54a85216c0773" +dependencies = [ + "crossbeam-channel", + "dirs", + "libappindicator", + "muda", + "objc2 0.6.4", + "objc2-app-kit", + "objc2-core-foundation", + "objc2-core-graphics", + "objc2-foundation 0.3.2", + "once_cell", + "png 0.18.1", + "serde", + "thiserror 2.0.18", + "windows-sys 0.61.2", +] + +[[package]] +name = "tree-sitter" +version = "0.26.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3c343ed63e3f5c64d1acdecb5d2c13d4e169cb5fde0052106ebaa6c6f27f9e55" +dependencies = [ + "cc", + "regex", + "regex-syntax", + "serde_json", + "streaming-iterator", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-go" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c8560a4d2f835cc0d4d2c2e03cbd0dde2f6114b43bc491164238d333e28b16ea" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-java" +version = "0.23.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0aa6cbcdc8c679b214e616fd3300da67da0e492e066df01bcf5a5921a71e90d6" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-javascript" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68204f2abc0627a90bdf06e605f5c470aa26fdcb2081ea553a04bdad756693f5" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-kotlin-ng" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e800ebbda938acfbf224f4d2c34947a31994b1295ee6e819b65226c7b51b4450" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-language" +version = "0.1.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "009994f150cc0cd50ff54917d5bc8bffe8cad10ca10d81c34da2ec421ae61782" + +[[package]] +name = "tree-sitter-python" +version = "0.25.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6bf85fd39652e740bf60f46f4cda9492c3a9ad75880575bf14960f775cb74a1c" +dependencies = [ + "cc", + "tree-sitter-language", +] + +[[package]] +name = "tree-sitter-ruby" +version = "0.23.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b9d12581f227e93f094d3af2ae690a574abb8a2b9b7a96e7cfe9647b2b617678" +checksum = "be0484ea4ef6bb9c575b4fdabde7e31340a8d2dbc7d52b321ac83da703249f95" dependencies = [ - "once_cell", - "valuable", + "cc", + "tree-sitter-language", ] [[package]] -name = "tracing-log" -version = "0.2.0" +name = "tree-sitter-rust" +version = "0.24.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ee855f1f400bd0e5c02d150ae5de3840039a3f54b025156404e34c23c03f47c3" +checksum = "439e577dbe07423ec2582ac62c7531120dbfccfa6e5f92406f93dd271a120e45" dependencies = [ - "log", - "once_cell", - "tracing-core", + "cc", + "tree-sitter-language", ] [[package]] -name = "tracing-subscriber" -version = "0.3.20" +name = "tree-sitter-swift" +version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2054a14f5307d601f88daf0553e1cbf472acc4f2c51afab632431cdcd72124d5" +checksum = "fe36052155b9dd69ca82b3b8f1b4ccfb2d867125ac1a4db1dd7331829242668c" dependencies = [ - "matchers", - "nu-ansi-term", - "once_cell", - "regex-automata", - "sharded-slab", - "smallvec 1.15.1", - "thread_local", - "tracing", - "tracing-core", - "tracing-log", + "cc", + "tree-sitter-language", ] [[package]] -name = "tray-icon" -version = "0.23.1" +name = "tree-sitter-typescript" +version = "0.23.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "15edbb0d80583e85ee8df283410038e17314df5cba30da2087a54a85216c0773" +checksum = "6c5f76ed8d947a75cc446d5fccd8b602ebf0cde64ccf2ffa434d873d7a575eff" dependencies = [ - "crossbeam-channel", - "dirs", - "libappindicator", - "muda", - "objc2 0.6.4", - "objc2-app-kit", - "objc2-core-foundation", - "objc2-core-graphics", - "objc2-foundation 0.3.2", - "once_cell", - "png 0.18.1", - "serde", - "thiserror 2.0.17", - "windows-sys 0.61.2", + "cc", + "tree-sitter-language", ] [[package]] @@ -6353,6 +8391,23 @@ version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e421abadd41a4225275504ea4d6566923418b7f05506fbc9c0fe86ba7396114b" +[[package]] +name = "tungstenite" +version = "0.28.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8628dcc84e5a09eb3d8423d6cb682965dea9133204e8fb3efee74c2a0c259442" +dependencies = [ + "bytes", + "data-encoding", + "http", + "httparse", + "log", + "rand 0.9.4", + "sha1", + "thiserror 2.0.18", + "utf-8", +] + [[package]] name = "type1-encoding-parser" version = "0.1.0" @@ -6370,9 +8425,9 @@ checksum = "bc7d623258602320d5c55d1bc22793b57daff0ec7efc270ea7d55ce1d5f5471c" [[package]] name = "typenum" -version = "1.19.0" +version = "1.20.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "562d481066bde0658276a35467c4af00bdc6ee726305698a55b86e61d7ad82bb" +checksum = "b6f5e870be6c3b371b77fe0ee0bafb859fa4964b4404c27de1d380043c4dda20" [[package]] name = "uds_windows" @@ -6426,6 +8481,18 @@ dependencies = [ "unic-common", ] +[[package]] +name = "unicase" +version = "2.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dbc4bc3a9f746d862c45cb89d705aa10f187bb96c76001afab07a0d35ce60142" + +[[package]] +name = "unicode-bidi" +version = "0.3.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c1cb5db39152898a79168971543b1cb5020dff7fe43c8dc468b0885f5e29df5" + [[package]] name = "unicode-ident" version = "1.0.22" @@ -6441,22 +8508,40 @@ dependencies = [ "tinyvec", ] +[[package]] +name = "unicode-properties" +version = "0.1.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7df058c713841ad818f1dc5d3fd88063241cc61f49f5fbea4b951e8cf5a8d71d" + [[package]] name = "unicode-segmentation" version = "1.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f6ccf251212114b54433ec949fd6a7841275f9ada20dddd2f29e9ceea4501493" +[[package]] +name = "unicode-xid" +version = "0.2.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" + [[package]] name = "universal-hash" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea" dependencies = [ - "crypto-common", + "crypto-common 0.1.6", "subtle", ] +[[package]] +name = "unsafe-libyaml" +version = "0.2.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "673aac59facbab8a9007c7f6108d11f63b603f7cabff99fabf650fea5c32b861" + [[package]] name = "untrusted" version = "0.9.0" @@ -6505,6 +8590,12 @@ dependencies = [ "serde", ] +[[package]] +name = "urlencoding" +version = "2.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "daf8dba3b7eb870caf1ddeed7bc9d2a049f3cfdfae7cb521b087cc33ae4c49da" + [[package]] name = "urlpattern" version = "0.3.0" @@ -6541,18 +8632,68 @@ version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" +[[package]] +name = "utoipa" +version = "4.2.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c5afb1a60e207dca502682537fefcfd9921e71d0b83e9576060f09abc6efab23" +dependencies = [ + "indexmap 2.12.0", + "serde", + "serde_json", + "utoipa-gen", +] + +[[package]] +name = "utoipa-gen" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "20c24e8ab68ff9ee746aad22d39b5535601e6416d1b0feeabf78be986a5c4392" +dependencies = [ + "proc-macro-error", + "proc-macro2", + "quote", + "syn 2.0.108", +] + [[package]] name = "uuid" -version = "1.18.1" +version = "1.23.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f87b8aa10b915a06587d0dec516c282ff295b475d94abf425d62b57710070a2" +checksum = "bf80a72845275afea99e7f2b434723d3bc7e38470fcd1c7ed39a599c73319a53" dependencies = [ - "getrandom 0.3.4", + "getrandom 0.4.3", "js-sys", - "serde", + "serde_core", "wasm-bindgen", ] +[[package]] +name = "uuid-simd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b082222b4f6619906941c17eb2297fff4c2fb96cb60164170522942a200bd8" +dependencies = [ + "outref", + "uuid", + "vsimd", +] + +[[package]] +name = "v_escape-base" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1212fce830b75af194b578e55b3db9049f2c8c45f58d397fb25602fdb50fb3d" + +[[package]] +name = "v_htmlescape" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "befb3d53c9e3ec641417685896cbc8cc5bd264d6a2e190c56aaef1af24740d99" +dependencies = [ + "v_escape-base", +] + [[package]] name = "valuable" version = "0.1.1" @@ -6583,6 +8724,12 @@ version = "0.9.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0b928f33d975fc6ad9f86c8f283853ad26bdd5b10b7f1542aa2fa15e2289105a" +[[package]] +name = "vsimd" +version = "0.8.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5c3082ca00d5a5ef149bb8b555a72ae84c9c59f7250f013ac822ac2e49b19c64" + [[package]] name = "vswhom" version = "0.1.0" @@ -6637,6 +8784,12 @@ dependencies = [ "wit-bindgen", ] +[[package]] +name = "wasite" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" + [[package]] name = "wasm-bindgen" version = "0.2.118" @@ -6744,12 +8897,28 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d7cff6eef815df1834fd250e3a2ff436044d82a9f1bc1980ca1dbdf07effc538" dependencies = [ - "phf", + "phf 0.13.1", "phf_codegen", "string_cache", "string_cache_codegen", ] +[[package]] +name = "webbrowser" +version = "1.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fc95580916af1e68ff6a7be07446fc5db73ebf71cf092de939bbf5f7e189f72" +dependencies = [ + "core-foundation 0.10.1", + "jni 0.22.4", + "log", + "ndk-context", + "objc2 0.6.4", + "objc2-foundation 0.3.2", + "url", + "web-sys", +] + [[package]] name = "webkit2gtk" version = "2.0.2" @@ -6820,7 +8989,7 @@ checksum = "d4ba622a989277ef3886dd5afb3e280e3dd6d974b766118950a08f8f678ad6a4" dependencies = [ "webview2-com-macros", "webview2-com-sys", - "windows", + "windows 0.61.3", "windows-core 0.61.2", "windows-implement", "windows-interface", @@ -6843,8 +9012,8 @@ version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36695906a1b53a3bf5c4289621efedac12b73eeb0b89e7e1a89b517302d5d75c" dependencies = [ - "thiserror 2.0.17", - "windows", + "thiserror 2.0.18", + "windows 0.61.3", "windows-core 0.61.2", ] @@ -6854,6 +9023,25 @@ version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a751b3277700db47d3e574514de2eced5e54dc8a5436a3bf7a0b248b2cee16f3" +[[package]] +name = "which" +version = "8.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "48d7cd18d4acb58fb3cdfe9ea54e6cd96a4e7d4cc45c56338b236e82dad47248" +dependencies = [ + "libc", +] + +[[package]] +name = "whoami" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" +dependencies = [ + "libredox", + "wasite", +] + [[package]] name = "winapi" version = "0.3.9" @@ -6906,11 +9094,23 @@ version = "0.61.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893" dependencies = [ - "windows-collections", + "windows-collections 0.2.0", "windows-core 0.61.2", - "windows-future", + "windows-future 0.2.1", "windows-link 0.1.3", - "windows-numerics", + "windows-numerics 0.2.0", +] + +[[package]] +name = "windows" +version = "0.62.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "527fadee13e0c05939a6a05d5bd6eec6cd2e3dbd648b9f8e447c6518133d8580" +dependencies = [ + "windows-collections 0.3.2", + "windows-core 0.62.2", + "windows-future 0.3.2", + "windows-numerics 0.3.1", ] [[package]] @@ -6922,6 +9122,15 @@ dependencies = [ "windows-core 0.61.2", ] +[[package]] +name = "windows-collections" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "23b2d95af1a8a14a3c7367e1ed4fc9c20e0a26e79551b1454d72583c97cc6610" +dependencies = [ + "windows-core 0.62.2", +] + [[package]] name = "windows-core" version = "0.61.2" @@ -6956,7 +9165,18 @@ checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e" dependencies = [ "windows-core 0.61.2", "windows-link 0.1.3", - "windows-threading", + "windows-threading 0.1.0", +] + +[[package]] +name = "windows-future" +version = "0.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e1d6f90251fe18a279739e78025bd6ddc52a7e22f921070ccdc67dde84c605cb" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", + "windows-threading 0.2.1", ] [[package]] @@ -7003,6 +9223,16 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-numerics" +version = "0.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6e2e40844ac143cdb44aead537bbf727de9b044e107a0f1220392177d15b0f26" +dependencies = [ + "windows-core 0.62.2", + "windows-link 0.2.1", +] + [[package]] name = "windows-registry" version = "0.5.3" @@ -7059,6 +9289,15 @@ dependencies = [ "windows-targets 0.42.2", ] +[[package]] +name = "windows-sys" +version = "0.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "677d2418bec65e3338edb076e806bc1ec15693c5d0104683f2efe857f61056a9" +dependencies = [ + "windows-targets 0.48.5", +] + [[package]] name = "windows-sys" version = "0.52.0" @@ -7110,6 +9349,21 @@ dependencies = [ "windows_x86_64_msvc 0.42.2", ] +[[package]] +name = "windows-targets" +version = "0.48.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a2fa6e2155d7247be68c096456083145c183cbbbc2764150dda45a87197940c" +dependencies = [ + "windows_aarch64_gnullvm 0.48.5", + "windows_aarch64_msvc 0.48.5", + "windows_i686_gnu 0.48.5", + "windows_i686_msvc 0.48.5", + "windows_x86_64_gnu 0.48.5", + "windows_x86_64_gnullvm 0.48.5", + "windows_x86_64_msvc 0.48.5", +] + [[package]] name = "windows-targets" version = "0.52.6" @@ -7152,6 +9406,15 @@ dependencies = [ "windows-link 0.1.3", ] +[[package]] +name = "windows-threading" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3949bd5b99cafdf1c7ca86b43ca564028dfe27d66958f2470940f73d86d75b37" +dependencies = [ + "windows-link 0.2.1", +] + [[package]] name = "windows-version" version = "0.1.7" @@ -7167,6 +9430,12 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" + [[package]] name = "windows_aarch64_gnullvm" version = "0.52.6" @@ -7185,6 +9454,12 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" + [[package]] name = "windows_aarch64_msvc" version = "0.52.6" @@ -7203,6 +9478,12 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" + [[package]] name = "windows_i686_gnu" version = "0.52.6" @@ -7233,6 +9514,12 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" + [[package]] name = "windows_i686_msvc" version = "0.52.6" @@ -7251,6 +9538,12 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" + [[package]] name = "windows_x86_64_gnu" version = "0.52.6" @@ -7269,6 +9562,12 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" + [[package]] name = "windows_x86_64_gnullvm" version = "0.52.6" @@ -7287,6 +9586,12 @@ 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" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" + [[package]] name = "windows_x86_64_msvc" version = "0.52.6" @@ -7363,7 +9668,7 @@ dependencies = [ "gtk", "http", "javascriptcore-rs", - "jni", + "jni 0.21.1", "libc", "ndk", "objc2 0.6.4", @@ -7375,15 +9680,15 @@ dependencies = [ "once_cell", "percent-encoding", "raw-window-handle", - "sha2", + "sha2 0.10.9", "soup3", "tao-macros", - "thiserror 2.0.17", + "thiserror 2.0.18", "url", "webkit2gtk", "webkit2gtk-sys", "webview2-com", - "windows", + "windows 0.61.3", "windows-core 0.61.2", "windows-version", "x11-dl", @@ -7419,6 +9724,23 @@ dependencies = [ "pkg-config", ] +[[package]] +name = "x11rb" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9993aa5be5a26815fe2c3eacfc1fde061fc1a1f094bf1ad2a18bf9c495dd7414" +dependencies = [ + "gethostname", + "rustix", + "x11rb-protocol", +] + +[[package]] +name = "x11rb-protocol" +version = "0.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ea6fc2961e4ef194dcbfe56bb845534d0dc8098940c7e5c012a258bfec6701bd" + [[package]] name = "x25519-dalek" version = "2.0.1" @@ -7507,7 +9829,7 @@ dependencies = [ "futures-core", "futures-lite", "hex", - "nix", + "nix 0.30.1", "ordered-stream", "serde", "serde_repr", @@ -7626,6 +9948,7 @@ version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ + "serde", "yoke", "zerofrom", "zerovec-derive", @@ -7654,6 +9977,34 @@ dependencies = [ "memchr", ] +[[package]] +name = "zstd" +version = "0.13.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e91ee311a569c327171651566e07972200e76fcfe2242a4fa446149a3881c08a" +dependencies = [ + "zstd-safe", +] + +[[package]] +name = "zstd-safe" +version = "7.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8f49c4d5f0abb602a93fb8736af2a4f4dd9512e36f7f570d66e65ff867ed3b9d" +dependencies = [ + "zstd-sys", +] + +[[package]] +name = "zstd-sys" +version = "2.0.16+zstd.1.5.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "91e19ebc2adc8f83e43039e79776e3fda8ca919132d68a1fed6a5faca2683748" +dependencies = [ + "cc", + "pkg-config", +] + [[package]] name = "zvariant" version = "5.8.0" diff --git a/frontend/src-tauri/Cargo.toml b/frontend/src-tauri/Cargo.toml index 7f41a9ec..dd768820 100644 --- a/frontend/src-tauri/Cargo.toml +++ b/frontend/src-tauri/Cargo.toml @@ -6,7 +6,7 @@ authors = ["tony@trymaple.ai"] license = "MIT" repository = "https://github.com/OpenSecretCloud/Maple" edition = "2021" -rust-version = "1.88.0" +rust-version = "1.91.1" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html @@ -33,6 +33,7 @@ tauri-plugin-deep-link = "=2.4.7" tauri-plugin-single-instance = { version = "=2.4.0", features = ["deep-link"] } tauri-plugin-opener = "2.5.4" tauri-plugin-os = "2.3.2" +tauri-plugin-dialog = "2.4.1" tauri-plugin-sign-in-with-apple = "1.0.2" tokio = { version = "1.0", features = ["net", "sync", "rt-multi-thread", "macros", "time"] } once_cell = "1.18.0" @@ -40,9 +41,18 @@ maple-proxy = "0.1.8" tauri-plugin-fs = "2.5.1" anyhow = "1.0" axum = "0.8" +futures = "0.3" pdf-extract = "0.7" base64 = "0.22" +# BEGIN MAPLE_DESKTOP_GOOSE_DEPENDENCIES +[target.'cfg(any(target_os = "macos", target_os = "windows", target_os = "linux"))'.dependencies] +goose = { path = "../../ThirdParties/goose/crates/goose", default-features = false } +# Goose v1.41.0 declares `rmcp = "1.4"` in its workspace, but it does not +# compile against the current 1.x API when embedded outside Goose's lockfile. +rmcp = { version = "=1.4.0", default-features = false } +# END MAPLE_DESKTOP_GOOSE_DEPENDENCIES + [target.'cfg(any(target_os = "macos", target_os = "windows"))'.dependencies] # TTS dependencies (Supertonic) - desktop only ort = "2.0.0-rc.10" diff --git a/frontend/src-tauri/capabilities/default.json b/frontend/src-tauri/capabilities/default.json index 8695013b..459ee757 100644 --- a/frontend/src-tauri/capabilities/default.json +++ b/frontend/src-tauri/capabilities/default.json @@ -8,6 +8,7 @@ "permissions": [ "core:default", "updater:default", + "dialog:default", "fs:default", { "identifier": "fs:allow-read-file", diff --git a/frontend/src-tauri/src/agent.rs b/frontend/src-tauri/src/agent.rs new file mode 100644 index 00000000..2acd3627 --- /dev/null +++ b/frontend/src-tauri/src/agent.rs @@ -0,0 +1,808 @@ +use crate::proxy; +use axum::http::HeaderValue; +use goose::acp::server_factory::{AcpServer, AcpServerFactoryConfig}; +use goose::acp::transport::create_router as create_goose_acp_router; +use goose::agents::GoosePlatform; +use goose::config::ConfigError; +use rand::{rngs::OsRng, RngCore}; +use serde::{Deserialize, Serialize}; +use serde_json::Value; +use std::fs::{self, OpenOptions}; +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::sync::Arc; +use std::time::{Duration, SystemTime, UNIX_EPOCH}; +use tauri::{AppHandle, Manager, State}; +use tokio::sync::{oneshot, Mutex}; +use tokio::task::JoinHandle; + +const DEFAULT_AGENT_MODEL: &str = "auto:powerful"; +const DEFAULT_GOOSE_MODE: &str = "approve"; +const READINESS_TIMEOUT: Duration = Duration::from_secs(30); +const READINESS_INTERVAL: Duration = Duration::from_millis(100); +const SHUTDOWN_TIMEOUT: Duration = Duration::from_secs(5); + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AgentConfig { + pub default_project_root: Option, + pub default_model: String, + #[serde(default = "default_runtime_kind")] + pub runtime_kind: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub external_acp_url: Option, +} + +fn default_runtime_kind() -> String { + "goose".to_string() +} + +impl Default for AgentConfig { + fn default() -> Self { + Self { + default_project_root: None, + default_model: DEFAULT_AGENT_MODEL.to_string(), + runtime_kind: default_runtime_kind(), + external_acp_url: None, + } + } +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct AgentStartRequest { + pub project_root: Option, + pub model: Option, + // Kept so older frontend state or dev tools can pass the old field without + // breaking deserialization. Built-in Goose now runs in-process. + pub goose_binary: Option, + pub mode: Option, +} + +#[derive(Debug, Clone, Serialize)] +#[serde(rename_all = "camelCase")] +pub struct AgentRuntimeStatus { + pub running: bool, + pub acp_url: Option, + pub redacted_acp_url: Option, + pub http_base_url: Option, + pub status_url: Option, + pub project_root: Option, + pub goose_binary: Option, + pub pid: Option, + pub model: Option, + pub mode: Option, + pub maple_proxy_base_url: Option, + pub config_dir: String, + pub goose_path_root: Option, + pub log_path: Option, + pub llm_log_dir: Option, + pub latest_llm_log_path: Option, + pub proxy_llm_log_dir: Option, + pub latest_proxy_llm_log_path: Option, + pub error: Option, +} + +#[derive(Debug, Clone, Serialize, Deserialize)] +#[serde(rename_all = "camelCase")] +pub struct RecentProjectRoot { + pub path: String, + pub name: String, + pub last_used_ms: u128, +} + +struct AgentRuntime { + shutdown: Option>, + server_task: JoinHandle>, + acp_url: String, + redacted_acp_url: String, + http_base_url: String, + status_url: String, + project_root: PathBuf, + model: String, + mode: String, + maple_proxy_base_url: String, + config_dir: PathBuf, + goose_path_root: PathBuf, + log_path: PathBuf, +} + +impl AgentRuntime { + fn status(&self, running: bool, error: Option) -> AgentRuntimeStatus { + AgentRuntimeStatus { + running, + acp_url: if running { + Some(self.acp_url.clone()) + } else { + None + }, + redacted_acp_url: Some(self.redacted_acp_url.clone()), + http_base_url: Some(self.http_base_url.clone()), + status_url: Some(self.status_url.clone()), + project_root: Some(path_string(&self.project_root)), + goose_binary: None, + pid: None, + model: Some(self.model.clone()), + mode: Some(self.mode.clone()), + maple_proxy_base_url: Some(self.maple_proxy_base_url.clone()), + config_dir: path_string(&self.config_dir), + goose_path_root: Some(path_string(&self.goose_path_root)), + log_path: Some(path_string(&self.log_path)), + llm_log_dir: Some(path_string(&goose_llm_log_dir(&self.goose_path_root))), + latest_llm_log_path: Some(path_string(&latest_goose_llm_log_path( + &self.goose_path_root, + ))), + proxy_llm_log_dir: Some(path_string(&proxy_llm_log_dir(&self.config_dir))), + latest_proxy_llm_log_path: latest_proxy_llm_log_path(&self.config_dir), + error, + } + } +} + +pub struct AgentRuntimeState { + inner: Arc>>, + session_log: Arc>, +} + +impl AgentRuntimeState { + pub fn new() -> Self { + Self { + inner: Arc::new(Mutex::new(None)), + session_log: Arc::new(Mutex::new(())), + } + } +} + +#[tauri::command] +pub async fn agent_get_runtime_status( + app_handle: AppHandle, + state: State<'_, AgentRuntimeState>, +) -> Result { + let config_dir = agent_config_dir(&app_handle).map_err(|e| e.to_string())?; + let mut runtime = state.inner.lock().await; + + if let Some(current) = runtime.as_mut() { + if !current.server_task.is_finished() { + return Ok(current.status(true, None)); + } + } + + if let Some(current) = runtime.take() { + drop(runtime); + let error = match current.server_task.await { + Ok(Ok(())) => Some("Goose ACP server stopped".to_string()), + Ok(Err(error)) => Some(error), + Err(error) => Some(format!("Goose ACP server task failed: {error}")), + }; + return Ok(stopped_status(config_dir, error)); + } + + Ok(stopped_status(config_dir, None)) +} + +#[tauri::command] +pub async fn agent_start_runtime( + app_handle: AppHandle, + state: State<'_, AgentRuntimeState>, + proxy_state: State<'_, proxy::ProxyState>, + request: Option, +) -> Result { + clear_exited_runtime(&state).await; + + { + let runtime = state.inner.lock().await; + if let Some(current) = runtime.as_ref() { + return Ok(current.status(true, None)); + } + } + + let agent_config = load_agent_config_inner(&app_handle).unwrap_or_default(); + let request = request.unwrap_or(AgentStartRequest { + project_root: None, + model: None, + goose_binary: None, + mode: None, + }); + + log::info!("Agent Mode requested embedded Goose runtime start"); + let proxy_status = proxy::ensure_proxy_running(app_handle.clone(), proxy_state).await?; + let proxy_config = proxy_status.config; + let proxy_host = if proxy_config.host == "0.0.0.0" { + "127.0.0.1".to_string() + } else { + proxy_config.host.clone() + }; + let maple_proxy_base_url = format!("http://{}:{}", proxy_host, proxy_config.port); + + let project_root = resolve_project_root(request.project_root.as_deref(), &agent_config) + .map_err(|e| format!("Failed to resolve Agent Mode project root: {e}"))?; + let model = request + .model + .or_else(|| std::env::var("MAPLE_GOOSE_MODEL").ok()) + .unwrap_or(agent_config.default_model); + let mode = request + .mode + .or_else(|| std::env::var("MAPLE_GOOSE_MODE").ok()) + .unwrap_or_else(|| DEFAULT_GOOSE_MODE.to_string()); + + let config_dir = agent_config_dir(&app_handle).map_err(|e| e.to_string())?; + let goose_path_root = std::env::var("MAPLE_GOOSE_PATH_ROOT") + .map(PathBuf::from) + .unwrap_or_else(|_| config_dir.join("goose")); + let log_dir = config_dir.join("logs"); + fs::create_dir_all(&log_dir).map_err(|e| format!("Failed to create log dir: {e}"))?; + fs::create_dir_all(&goose_path_root) + .map_err(|e| format!("Failed to create Goose data dir: {e}"))?; + let log_path = log_dir.join("goose-embedded.log"); + let llm_log_dir = goose_llm_log_dir(&goose_path_root); + let latest_llm_log_path = latest_goose_llm_log_path(&goose_path_root); + let proxy_llm_log_dir = proxy_llm_log_dir(&config_dir); + + let port = find_available_port().map_err(|e| format!("Failed to allocate Goose port: {e}"))?; + let token = secure_token(); + let http_base_url = format!("http://127.0.0.1:{port}"); + let status_url = format!("{http_base_url}/status"); + let acp_url = format!("ws://127.0.0.1:{port}/acp?token={token}"); + let redacted_acp_url = format!("ws://127.0.0.1:{port}/acp?token=REDACTED"); + let allowed_origins = tauri_acp_allowed_origins(); + let allowed_origin_log = allowed_origins + .iter() + .filter_map(|origin| origin.to_str().ok()) + .collect::>() + .join(", "); + + configure_embedded_goose( + &goose_path_root, + &model, + &mode, + &maple_proxy_base_url, + &proxy_config.api_key, + )?; + + let listener = tokio::net::TcpListener::bind(("127.0.0.1", port)) + .await + .map_err(|e| format!("Failed to bind Goose ACP listener: {e}"))?; + let acp_server = Arc::new(AcpServer::new(AcpServerFactoryConfig { + builtins: vec!["developer".to_string()], + data_dir: goose_path_root.join("data"), + config_dir: goose_path_root.join("config"), + goose_platform: GoosePlatform::GooseDesktop, + additional_source_roots: Vec::new(), + scheduler: None, + })); + let router = create_goose_acp_router(acp_server, token, true, allowed_origins); + let (shutdown_tx, shutdown_rx) = oneshot::channel::<()>(); + + log::info!( + "Starting embedded Goose ACP runtime in {} on {} with allowed origins: {}", + project_root.display(), + redacted_acp_url, + allowed_origin_log + ); + append_runtime_log( + &log_path, + &format!( + "starting embedded Goose ACP runtime: project_root={}, acp={}, proxy=http://{}:{}, llm_log_dir={}, latest_llm_log={}, proxy_llm_log_dir={}, allowed_origins={}", + project_root.display(), + redacted_acp_url, + proxy_host, + proxy_config.port, + llm_log_dir.display(), + latest_llm_log_path.display(), + proxy_llm_log_dir.display(), + allowed_origin_log + ), + ); + + let server_task = tokio::spawn(async move { + axum::serve(listener, router) + .with_graceful_shutdown(async { + let _ = shutdown_rx.await; + }) + .await + .map_err(|e| format!("Goose ACP server stopped with error: {e}")) + }); + + if let Err(e) = wait_for_goose_ready(&status_url).await { + append_runtime_log( + &log_path, + &format!("embedded Goose ACP runtime readiness failed: {e}"), + ); + let _ = shutdown_tx.send(()); + server_task.abort(); + return Err(format!("{e}. See {}", log_path.display())); + } + + append_runtime_log( + &log_path, + &format!("embedded Goose ACP runtime ready: status={status_url}, acp={redacted_acp_url}"), + ); + log::info!("Embedded Goose ACP runtime ready on {redacted_acp_url}"); + + let runtime = AgentRuntime { + shutdown: Some(shutdown_tx), + server_task, + acp_url, + redacted_acp_url, + http_base_url, + status_url, + project_root: project_root.clone(), + model: model.clone(), + mode, + maple_proxy_base_url, + config_dir: config_dir.clone(), + goose_path_root, + log_path, + }; + + let status = runtime.status(true, None); + { + let mut guard = state.inner.lock().await; + *guard = Some(runtime); + } + + let _ = save_recent_project_root_inner(&app_handle, &project_root); + let mut next_config = load_agent_config_inner(&app_handle).unwrap_or_default(); + next_config.default_project_root = Some(path_string(&project_root)); + next_config.default_model = model; + let _ = save_agent_config_inner(&app_handle, &next_config); + + Ok(status) +} + +#[tauri::command] +pub async fn agent_stop_runtime( + app_handle: AppHandle, + state: State<'_, AgentRuntimeState>, +) -> Result { + let config_dir = agent_config_dir(&app_handle).map_err(|e| e.to_string())?; + stop_runtime_inner(&state).await?; + Ok(stopped_status(config_dir, None)) +} + +#[tauri::command] +pub async fn agent_restart_runtime( + app_handle: AppHandle, + state: State<'_, AgentRuntimeState>, + proxy_state: State<'_, proxy::ProxyState>, + request: Option, +) -> Result { + stop_runtime_inner(&state).await?; + agent_start_runtime(app_handle, state, proxy_state, request).await +} + +#[tauri::command] +pub async fn agent_load_config(app_handle: AppHandle) -> Result { + load_agent_config_inner(&app_handle).map_err(|e| e.to_string()) +} + +#[tauri::command] +pub async fn agent_save_config(app_handle: AppHandle, config: AgentConfig) -> Result<(), String> { + save_agent_config_inner(&app_handle, &config).map_err(|e| e.to_string()) +} + +#[tauri::command] +pub async fn agent_list_recent_project_roots( + app_handle: AppHandle, +) -> Result, String> { + load_recent_project_roots_inner(&app_handle).map_err(|e| e.to_string()) +} + +#[tauri::command] +pub async fn agent_save_recent_project_root( + app_handle: AppHandle, + path: String, +) -> Result, String> { + let project_root = normalize_project_root(Path::new(&path))?; + save_recent_project_root_inner(&app_handle, &project_root).map_err(|e| e.to_string()) +} + +#[tauri::command] +pub async fn agent_append_session_event( + app_handle: AppHandle, + state: State<'_, AgentRuntimeState>, + session_id: String, + event: Value, +) -> Result<(), String> { + let _guard = state.session_log.lock().await; + append_session_event_inner(&app_handle, &session_id, event).map_err(|e| e.to_string()) +} + +#[tauri::command] +pub async fn agent_append_runtime_log( + app_handle: AppHandle, + message: String, +) -> Result<(), String> { + let log_path = agent_config_dir(&app_handle) + .map_err(|e| e.to_string())? + .join("logs") + .join("goose-embedded.log"); + log::info!("[Agent Mode] {message}"); + append_runtime_log(&log_path, &format!("frontend {}", message)); + Ok(()) +} + +async fn clear_exited_runtime(state: &State<'_, AgentRuntimeState>) { + let mut runtime = state.inner.lock().await; + if runtime + .as_ref() + .map(|current| current.server_task.is_finished()) + .unwrap_or(false) + { + *runtime = None; + } +} + +async fn stop_runtime_inner(state: &State<'_, AgentRuntimeState>) -> Result<(), String> { + let mut runtime = state.inner.lock().await; + if let Some(mut current) = runtime.take() { + log::info!("Stopping embedded Goose ACP runtime"); + append_runtime_log(¤t.log_path, "stopping embedded Goose ACP runtime"); + if let Some(shutdown) = current.shutdown.take() { + let _ = shutdown.send(()); + } + let mut server_task = current.server_task; + tokio::select! { + result = &mut server_task => { + match result { + Ok(Ok(())) => {} + Ok(Err(error)) => log::warn!("{error}"), + Err(error) => log::warn!("Goose ACP server task failed: {error}"), + } + } + _ = tokio::time::sleep(SHUTDOWN_TIMEOUT) => { + server_task.abort(); + let _ = server_task.await; + log::warn!("Timed out stopping Goose ACP server; aborted task"); + } + } + } + Ok(()) +} + +async fn wait_for_goose_ready(status_url: &str) -> Result<(), String> { + let client = reqwest::Client::builder() + .timeout(Duration::from_secs(1)) + .build() + .map_err(|e| format!("Failed to build readiness client: {e}"))?; + let deadline = std::time::Instant::now() + READINESS_TIMEOUT; + + while std::time::Instant::now() < deadline { + if let Ok(response) = client.get(status_url).send().await { + if response.status().is_success() { + return Ok(()); + } + } + + tokio::time::sleep(READINESS_INTERVAL).await; + } + + Err(format!("Goose did not become ready on {status_url}")) +} + +fn configure_embedded_goose( + goose_path_root: &Path, + model: &str, + mode: &str, + maple_proxy_base_url: &str, + proxy_api_key: &str, +) -> Result<(), String> { + fs::create_dir_all(goose_path_root.join("config")) + .map_err(|e| format!("Failed to create Goose config dir: {e}"))?; + fs::create_dir_all(goose_path_root.join("data")) + .map_err(|e| format!("Failed to create Goose data dir: {e}"))?; + fs::create_dir_all(goose_path_root.join("state")) + .map_err(|e| format!("Failed to create Goose state dir: {e}"))?; + + std::env::set_var("GOOSE_PATH_ROOT", goose_path_root); + std::env::set_var("GOOSE_DISABLE_KEYRING", "true"); + std::env::remove_var("GOOSE_MAX_TOKENS"); + + let config = goose::config::Config::global(); + delete_goose_config_key(config, "GOOSE_MAX_TOKENS")?; + goose::config::set_active_provider(config, "openai", model) + .map_err(|e| format!("Failed to configure Goose provider: {e}"))?; + config + .set_param("GOOSE_FAST_MODEL", model) + .map_err(|e| format!("Failed to configure Goose fast model: {e}"))?; + config + .set_param("GOOSE_MODE", mode) + .map_err(|e| format!("Failed to configure Goose mode: {e}"))?; + config + .set_param("OPENAI_BASE_URL", format!("{maple_proxy_base_url}/v1")) + .map_err(|e| format!("Failed to configure Goose OpenAI base URL: {e}"))?; + config + .set_param("GOOSE_DISABLE_KEYRING", true) + .map_err(|e| format!("Failed to configure Goose keyring mode: {e}"))?; + config + .set_secret("OPENAI_API_KEY", &proxy_api_key) + .map_err(|e| format!("Failed to configure Goose OpenAI API key: {e}"))?; + goose::providers::utils::init_goose_request_log() + .map_err(|e| format!("Failed to initialize Goose LLM request logging: {e}"))?; + + set_owner_only_permissions(&goose_path_root.join("config").join("config.yaml")); + set_owner_only_permissions(&goose_path_root.join("config").join("secrets.yaml")); + Ok(()) +} + +fn delete_goose_config_key(config: &goose::config::Config, key: &str) -> Result<(), String> { + match config.delete(key) { + Ok(()) | Err(ConfigError::NotFound(_)) => Ok(()), + Err(e) => Err(format!("Failed to clear Goose config key {key}: {e}")), + } +} + +fn stopped_status(config_dir: PathBuf, error: Option) -> AgentRuntimeStatus { + AgentRuntimeStatus { + running: false, + acp_url: None, + redacted_acp_url: None, + http_base_url: None, + status_url: None, + project_root: None, + goose_binary: None, + pid: None, + model: None, + mode: None, + maple_proxy_base_url: None, + config_dir: path_string(&config_dir), + goose_path_root: None, + log_path: None, + llm_log_dir: None, + latest_llm_log_path: None, + proxy_llm_log_dir: Some(path_string(&proxy_llm_log_dir(&config_dir))), + latest_proxy_llm_log_path: latest_proxy_llm_log_path(&config_dir), + error, + } +} + +fn goose_llm_log_dir(goose_path_root: &Path) -> PathBuf { + goose_path_root.join("state").join("logs") +} + +fn latest_goose_llm_log_path(goose_path_root: &Path) -> PathBuf { + goose_llm_log_dir(goose_path_root).join("llm_request.0.jsonl") +} + +fn proxy_llm_log_dir(agent_config_dir: &Path) -> PathBuf { + agent_config_dir.join("proxy-llm-logs") +} + +fn latest_proxy_llm_log_path(agent_config_dir: &Path) -> Option { + let dir = proxy_llm_log_dir(agent_config_dir); + let entries = fs::read_dir(dir).ok()?; + entries + .filter_map(|entry| { + let entry = entry.ok()?; + let path = entry.path(); + if path.extension().and_then(|value| value.to_str()) != Some("jsonl") { + return None; + } + let modified = entry.metadata().ok()?.modified().ok()?; + Some((modified, path)) + }) + .max_by_key(|(modified, _)| *modified) + .map(|(_, path)| path_string(&path)) +} + +fn resolve_project_root(requested: Option<&str>, config: &AgentConfig) -> Result { + if let Some(path) = requested.filter(|value| !value.trim().is_empty()) { + return normalize_project_root(Path::new(path)); + } + + if let Some(path) = config + .default_project_root + .as_deref() + .filter(|value| !value.trim().is_empty()) + { + if let Ok(root) = normalize_project_root(Path::new(path)) { + return Ok(root); + } + } + + std::env::current_dir() + .map_err(|e| format!("Failed to read current directory: {e}")) + .and_then(|path| normalize_project_root(&path)) +} + +fn normalize_project_root(path: &Path) -> Result { + let canonical = path + .canonicalize() + .map_err(|e| format!("{}: {e}", path.display()))?; + if !canonical.is_dir() { + return Err(format!("{} is not a folder", canonical.display())); + } + Ok(canonical) +} + +fn find_available_port() -> std::io::Result { + let listener = std::net::TcpListener::bind(("127.0.0.1", 0))?; + Ok(listener.local_addr()?.port()) +} + +fn secure_token() -> String { + let mut bytes = [0u8; 32]; + OsRng.fill_bytes(&mut bytes); + bytes.iter().map(|byte| format!("{byte:02x}")).collect() +} + +fn tauri_acp_allowed_origins() -> Vec { + let mut origins = vec![ + HeaderValue::from_static("http://localhost:5173"), + HeaderValue::from_static("http://127.0.0.1:5173"), + HeaderValue::from_static("http://tauri.localhost"), + HeaderValue::from_static("https://tauri.localhost"), + HeaderValue::from_static("tauri://localhost"), + HeaderValue::from_static("null"), + HeaderValue::from_static("file://"), + ]; + + if let Ok(extra_origins) = std::env::var("MAPLE_GOOSE_ACP_ALLOWED_ORIGINS") { + for origin in extra_origins + .split(',') + .map(str::trim) + .filter(|s| !s.is_empty()) + { + match HeaderValue::from_str(origin) { + Ok(header) if !origins.contains(&header) => origins.push(header), + Ok(_) => {} + Err(error) => { + log::warn!("Ignoring invalid MAPLE_GOOSE_ACP_ALLOWED_ORIGINS entry {origin:?}: {error}"); + } + } + } + } + + origins +} + +fn agent_config_dir(app_handle: &AppHandle) -> Result { + let base = if cfg!(target_os = "windows") { + app_handle + .path() + .app_config_dir() + .map_err(|e| anyhow::anyhow!("Failed to resolve app config dir: {e}"))? + } else { + let home = + std::env::var("HOME").map_err(|_| anyhow::anyhow!("Failed to get home directory"))?; + PathBuf::from(home).join(".config").join("maple") + }; + let path = base.join("agent"); + fs::create_dir_all(&path)?; + Ok(path) +} + +fn load_agent_config_inner(app_handle: &AppHandle) -> Result { + let path = agent_config_dir(app_handle)?.join("config.json"); + if !path.exists() { + return Ok(AgentConfig::default()); + } + let contents = fs::read_to_string(path)?; + Ok(serde_json::from_str(&contents)?) +} + +fn save_agent_config_inner( + app_handle: &AppHandle, + config: &AgentConfig, +) -> Result<(), anyhow::Error> { + let path = agent_config_dir(app_handle)?.join("config.json"); + write_json_file(&path, config) +} + +fn load_recent_project_roots_inner( + app_handle: &AppHandle, +) -> Result, anyhow::Error> { + let path = agent_config_dir(app_handle)?.join("recent_roots.json"); + if !path.exists() { + return Ok(Vec::new()); + } + let contents = fs::read_to_string(path)?; + Ok(serde_json::from_str(&contents)?) +} + +fn save_recent_project_root_inner( + app_handle: &AppHandle, + project_root: &Path, +) -> Result, anyhow::Error> { + let mut roots = load_recent_project_roots_inner(app_handle).unwrap_or_default(); + let path = path_string(project_root); + roots.retain(|root| root.path != path); + roots.insert( + 0, + RecentProjectRoot { + name: project_root + .file_name() + .and_then(|value| value.to_str()) + .unwrap_or(&path) + .to_string(), + path, + last_used_ms: unix_ms(), + }, + ); + roots.truncate(20); + + let file_path = agent_config_dir(app_handle)?.join("recent_roots.json"); + write_json_file(&file_path, &roots)?; + Ok(roots) +} + +fn append_session_event_inner( + app_handle: &AppHandle, + session_id: &str, + event: Value, +) -> Result<(), anyhow::Error> { + let sessions_dir = agent_config_dir(app_handle)?.join("sessions"); + fs::create_dir_all(&sessions_dir)?; + let path = sessions_dir.join(format!("{}.jsonl", sanitize_file_id(session_id))); + let mut file = OpenOptions::new().create(true).append(true).open(&path)?; + set_owner_only_permissions(&path); + let row = serde_json::json!({ + "ts": unix_ms(), + "event": event, + }); + serde_json::to_writer(&mut file, &row)?; + file.write_all(b"\n")?; + Ok(()) +} + +fn append_runtime_log(path: &Path, message: &str) { + let result = (|| -> Result<(), anyhow::Error> { + if let Some(parent) = path.parent() { + fs::create_dir_all(parent)?; + } + let mut file = OpenOptions::new().create(true).append(true).open(path)?; + set_owner_only_permissions(path); + writeln!(file, "{} {}", unix_ms(), message)?; + Ok(()) + })(); + if let Err(error) = result { + log::warn!("Failed to write Goose runtime log: {error}"); + } +} + +fn write_json_file(path: &Path, value: &T) -> Result<(), anyhow::Error> { + if let Some(parent) = path.parent() { + fs::create_dir_all(parent)?; + } + fs::write(path, serde_json::to_string_pretty(value)?)?; + set_owner_only_permissions(path); + Ok(()) +} + +#[cfg(unix)] +fn set_owner_only_permissions(path: &Path) { + use std::os::unix::fs::PermissionsExt; + let _ = fs::set_permissions(path, fs::Permissions::from_mode(0o600)); +} + +#[cfg(not(unix))] +fn set_owner_only_permissions(_path: &Path) {} + +fn sanitize_file_id(value: &str) -> String { + let sanitized: String = value + .chars() + .map(|ch| { + if ch.is_ascii_alphanumeric() || ch == '-' || ch == '_' { + ch + } else { + '_' + } + }) + .collect(); + if sanitized.is_empty() { + "session".to_string() + } else { + sanitized + } +} + +fn unix_ms() -> u128 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_millis()) + .unwrap_or_default() +} + +fn path_string(path: &Path) -> String { + path.to_string_lossy().to_string() +} diff --git a/frontend/src-tauri/src/lib.rs b/frontend/src-tauri/src/lib.rs index 1749ede5..0a49bf80 100644 --- a/frontend/src-tauri/src/lib.rs +++ b/frontend/src-tauri/src/lib.rs @@ -1,6 +1,8 @@ use tauri::Emitter; use tauri_plugin_deep_link::DeepLinkExt; +#[cfg(desktop)] +mod agent; mod pdf_extractor; mod proxy; // TTS is available on desktop and iOS (not Android) @@ -33,12 +35,24 @@ pub fn run() { })) .plugin(tauri_plugin_log::Builder::default().level(log::LevelFilter::Info).build()) .plugin(tauri_plugin_deep_link::init()) + .plugin(tauri_plugin_dialog::init()) .plugin(tauri_plugin_opener::init()) .plugin(tauri_plugin_os::init()) .plugin(tauri_plugin_fs::init()) + .manage(agent::AgentRuntimeState::new()) .manage(proxy::ProxyState::new()) .manage(tts::TTSState::new()) .invoke_handler(tauri::generate_handler![ + agent::agent_get_runtime_status, + agent::agent_start_runtime, + agent::agent_stop_runtime, + agent::agent_restart_runtime, + agent::agent_load_config, + agent::agent_save_config, + agent::agent_list_recent_project_roots, + agent::agent_save_recent_project_root, + agent::agent_append_session_event, + agent::agent_append_runtime_log, proxy::start_proxy, proxy::stop_proxy, proxy::get_proxy_status, diff --git a/frontend/src-tauri/src/proxy.rs b/frontend/src-tauri/src/proxy.rs index 0eb6345c..26a20843 100644 --- a/frontend/src-tauri/src/proxy.rs +++ b/frontend/src-tauri/src/proxy.rs @@ -1,12 +1,29 @@ use anyhow::{anyhow, Result}; +use axum::{ + body::{to_bytes, Body, Bytes}, + extract::{Request, State as AxumState}, + http::{Method, StatusCode}, + middleware::{self, Next}, + response::{IntoResponse, Response}, + Router, +}; +use futures::{Stream, StreamExt}; use maple_proxy::{create_app, Config}; use serde::{Deserialize, Serialize}; -use std::path::PathBuf; -use std::sync::Arc; +use serde_json::Value; +use std::fs::{self, File, OpenOptions}; +use std::io::Write; +use std::path::{Path, PathBuf}; +use std::pin::Pin; +use std::sync::atomic::{AtomicU64, Ordering}; +use std::sync::{Arc, Mutex as StdMutex}; +use std::time::{SystemTime, UNIX_EPOCH}; use tauri::{AppHandle, Emitter, Manager, State}; use tokio::net::TcpListener; use tokio::sync::Mutex; +static PROXY_LLM_LOG_COUNTER: AtomicU64 = AtomicU64::new(0); + #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ProxyConfig { pub host: String, @@ -52,6 +69,18 @@ pub struct ProxyState { running: Arc>, } +#[derive(Clone)] +struct LoggedProxyState { + llm_log_dir: PathBuf, +} + +#[derive(Clone)] +struct ProxyLlmLog { + request_id: String, + path: PathBuf, + file: Arc>, +} + impl ProxyState { pub fn new() -> Self { Self { @@ -60,6 +89,14 @@ impl ProxyState { running: Arc::new(Mutex::new(false)), } } + + pub async fn status(&self) -> ProxyStatus { + ProxyStatus { + running: *self.running.lock().await, + config: self.config.lock().await.clone(), + error: None, + } + } } // On Windows the proxy config lives in the roaming %APPDATA% profile, so a @@ -184,8 +221,13 @@ pub async fn start_proxy( } }; - // Create the app - let app = create_app(proxy_config); + let llm_log_dir = proxy_llm_log_dir(&app_handle) + .map_err(|e| format!("Failed to create proxy LLM log dir: {e}"))?; + log::info!("Maple proxy LLM logs enabled at {}", llm_log_dir.display()); + + // Create the app. This mirrors maple-proxy's OpenAI-compatible routes, but + // tees chat completion traffic into local JSONL logs before Goose parses it. + let app = create_logged_proxy_app(proxy_config, llm_log_dir); // Spawn the proxy server let handle = tokio::spawn(async move { @@ -245,14 +287,7 @@ pub async fn stop_proxy(state: State<'_, ProxyState>) -> Result) -> Result { - let running = *state.running.lock().await; - let config = state.config.lock().await.clone(); - - Ok(ProxyStatus { - running, - config, - error: None, - }) + Ok(state.status().await) } #[tauri::command] @@ -285,6 +320,268 @@ pub async fn test_proxy_port(host: String, port: u16) -> Result { } } +fn create_logged_proxy_app(config: Config, llm_log_dir: PathBuf) -> Router { + create_app(config).layer(middleware::from_fn_with_state( + Arc::new(LoggedProxyState { llm_log_dir }), + log_chat_completion_traffic, + )) +} + +async fn log_chat_completion_traffic( + AxumState(state): AxumState>, + request: Request, + next: Next, +) -> Response { + if request.method() != Method::POST || request.uri().path() != "/v1/chat/completions" { + return next.run(request).await; + } + + let request_id = next_proxy_llm_request_id(); + let llm_log = create_proxy_llm_log(&state.llm_log_dir, &request_id); + let (parts, body) = request.into_parts(); + let path = parts.uri.path().to_string(); + + let body_bytes = match to_bytes(body, usize::MAX).await { + Ok(bytes) => bytes, + Err(error) => { + append_proxy_llm_log_row( + llm_log.as_ref(), + serde_json::json!({ + "type": "request_read_error", + "request_id": request_id, + "ts": unix_ms(), + "path": path, + "error": error.to_string(), + }), + ); + return (StatusCode::BAD_REQUEST, "Failed to read proxy request body").into_response(); + } + }; + + let raw_body = parse_body_json(&body_bytes); + let model = raw_body + .get("model") + .and_then(Value::as_str) + .unwrap_or(""); + let is_streaming = raw_body + .get("stream") + .and_then(Value::as_bool) + .unwrap_or(false); + + append_proxy_llm_log_row( + llm_log.as_ref(), + serde_json::json!({ + "type": "request", + "request_id": request_id, + "ts": unix_ms(), + "path": path, + "model": model, + "stream": is_streaming, + "tools_len": raw_body.get("tools").and_then(Value::as_array).map(|tools| tools.len()).unwrap_or(0), + "raw_has_max_tokens": raw_body.get("max_tokens").is_some(), + "raw_has_max_completion_tokens": raw_body.get("max_completion_tokens").is_some(), + "raw_has_max_output_tokens": raw_body.get("max_output_tokens").is_some(), + "raw_body": raw_body, + "note": "Logged in Maple proxy middleware before maple-proxy parses the request; response_chunk and response_error_body rows are raw SSE/HTTP body bytes returned by maple-proxy to Goose.", + }), + ); + + log::info!( + "Proxy LLM request {request_id} model={} stream={} log={}", + model, + is_streaming, + llm_log + .as_ref() + .map(|log| log.path.display().to_string()) + .unwrap_or_else(|| "unavailable".to_string()) + ); + + let request = Request::from_parts(parts, Body::from(body_bytes)); + let response = next.run(request).await; + let (response_parts, response_body) = response.into_parts(); + let status = response_parts.status; + + append_proxy_llm_log_row( + llm_log.as_ref(), + serde_json::json!({ + "type": "response_start", + "request_id": request_id, + "ts": unix_ms(), + "status": status.as_u16(), + }), + ); + + if !status.is_success() { + let error_body = match to_bytes(response_body, usize::MAX).await { + Ok(bytes) => bytes, + Err(error) => { + append_proxy_llm_log_row( + llm_log.as_ref(), + serde_json::json!({ + "type": "response_body_error", + "request_id": request_id, + "ts": unix_ms(), + "error": error.to_string(), + }), + ); + return Response::from_parts(response_parts, Body::empty()); + } + }; + + append_proxy_llm_log_row( + llm_log.as_ref(), + serde_json::json!({ + "type": "response_error_body", + "request_id": request_id, + "ts": unix_ms(), + "bytes_len": error_body.len(), + "body_utf8_lossy": String::from_utf8_lossy(&error_body), + }), + ); + append_proxy_llm_log_row( + llm_log.as_ref(), + serde_json::json!({ + "type": "response_done", + "request_id": request_id, + "ts": unix_ms(), + }), + ); + return Response::from_parts(response_parts, Body::from(error_body)); + } + + let response_stream = response_body.into_data_stream(); + let logged_stream = log_response_body_stream(response_stream, request_id, llm_log); + Response::from_parts(response_parts, Body::from_stream(logged_stream)) +} + +fn parse_body_json(body: &Bytes) -> Value { + serde_json::from_slice::(body).unwrap_or_else(|error| { + serde_json::json!({ + "_parse_error": error.to_string(), + "_raw_utf8_lossy": String::from_utf8_lossy(body), + }) + }) +} + +fn log_response_body_stream( + stream: S, + request_id: String, + llm_log: Option, +) -> impl Stream> + Send +where + S: Stream> + Send + 'static, +{ + type BoxedBodyStream = Pin> + Send>>; + let stream: BoxedBodyStream = Box::pin(stream); + + futures::stream::unfold((stream, llm_log), move |(mut stream, llm_log)| { + let request_id = request_id.clone(); + async move { + match stream.next().await { + Some(Ok(bytes)) => { + append_proxy_llm_log_row( + llm_log.as_ref(), + serde_json::json!({ + "type": "response_chunk", + "request_id": request_id, + "ts": unix_ms(), + "bytes_len": bytes.len(), + "body_utf8_lossy": String::from_utf8_lossy(&bytes), + }), + ); + Some((Ok(bytes), (stream, llm_log))) + } + Some(Err(error)) => { + append_proxy_llm_log_row( + llm_log.as_ref(), + serde_json::json!({ + "type": "response_body_error", + "request_id": request_id, + "ts": unix_ms(), + "error": error.to_string(), + }), + ); + Some((Err(error), (stream, llm_log))) + } + None => { + append_proxy_llm_log_row( + llm_log.as_ref(), + serde_json::json!({ + "type": "response_done", + "request_id": request_id, + "ts": unix_ms(), + }), + ); + None + } + } + } + }) +} + +fn create_proxy_llm_log(log_dir: &Path, request_id: &str) -> Option { + let path = log_dir.join(format!("{request_id}.jsonl")); + match OpenOptions::new().create(true).append(true).open(&path) { + Ok(file) => { + set_owner_only_permissions(&path); + Some(ProxyLlmLog { + request_id: request_id.to_string(), + path, + file: Arc::new(StdMutex::new(file)), + }) + } + Err(error) => { + log::warn!("Failed to create proxy LLM log {}: {error}", path.display()); + None + } + } +} + +fn append_proxy_llm_log_row(log: Option<&ProxyLlmLog>, mut row: Value) { + let Some(log) = log else { + return; + }; + if let Value::Object(ref mut object) = row { + object + .entry("request_id") + .or_insert_with(|| Value::String(log.request_id.clone())); + } + + let result = (|| -> Result<()> { + let mut file = log + .file + .lock() + .map_err(|_| anyhow!("proxy LLM log lock poisoned"))?; + serde_json::to_writer(&mut *file, &row)?; + file.write_all(b"\n")?; + file.flush()?; + Ok(()) + })(); + + if let Err(error) = result { + log::warn!( + "Failed to write proxy LLM log {}: {error}", + log.path.display() + ); + } +} + +fn proxy_llm_log_dir(app_handle: &AppHandle) -> Result { + let base = if cfg!(target_os = "windows") { + app_handle + .path() + .app_config_dir() + .map_err(|e| anyhow!("Failed to resolve app config dir: {e}"))? + } else { + let home = std::env::var("HOME").map_err(|_| anyhow!("Failed to get home directory"))?; + PathBuf::from(home).join(".config").join("maple") + }; + let path = base.join("agent").join("proxy-llm-logs"); + fs::create_dir_all(&path)?; + set_owner_only_dir_permissions(&path); + Ok(path) +} + // Helper functions for config persistence. // Windows uses the Tauri-managed app_config_dir() (%APPDATA%); macOS/Linux keep // the historical ~/.config/maple/ location. The Windows API key is additionally @@ -354,7 +651,7 @@ async fn save_proxy_config(app_handle: &AppHandle, config: &ProxyConfig) -> Resu Ok(()) } -async fn load_saved_proxy_config(app_handle: &AppHandle) -> Result { +pub async fn load_saved_proxy_config(app_handle: &AppHandle) -> Result { let path = get_config_path(app_handle).await?; if !path.exists() { @@ -377,6 +674,37 @@ async fn load_saved_proxy_config(app_handle: &AppHandle) -> Result Ok(config) } +pub async fn ensure_proxy_running( + app_handle: AppHandle, + state: State<'_, ProxyState>, +) -> Result { + let current = state.status().await; + if current.running { + log::info!( + "Maple proxy already running on {}:{}", + current.config.host, + current.config.port + ); + return Ok(current); + } + + let config = load_saved_proxy_config(&app_handle) + .await + .map_err(|e| format!("Failed to load proxy config: {e}"))?; + + if config.api_key.trim().is_empty() { + log::warn!("Maple proxy cannot auto-start because saved config has no API key"); + return Err("Maple proxy is not configured with an API key yet".to_string()); + } + + log::info!( + "Starting Maple proxy from saved config on {}:{}", + config.host, + config.port + ); + start_proxy(app_handle, state, config).await +} + // Initialize proxy on app startup if auto_start is enabled pub async fn init_proxy_on_startup_simple(app_handle: AppHandle) -> Result<()> { // Load saved config @@ -412,3 +740,33 @@ pub async fn init_proxy_on_startup_simple(app_handle: AppHandle) -> Result<()> { Ok(()) } + +fn next_proxy_llm_request_id() -> String { + let counter = PROXY_LLM_LOG_COUNTER.fetch_add(1, Ordering::Relaxed); + format!("proxy_llm_{}_{}", unix_ms(), counter) +} + +fn unix_ms() -> u128 { + SystemTime::now() + .duration_since(UNIX_EPOCH) + .map(|duration| duration.as_millis()) + .unwrap_or_default() +} + +#[cfg(unix)] +fn set_owner_only_permissions(path: &Path) { + use std::os::unix::fs::PermissionsExt; + let _ = fs::set_permissions(path, fs::Permissions::from_mode(0o600)); +} + +#[cfg(not(unix))] +fn set_owner_only_permissions(_path: &Path) {} + +#[cfg(unix)] +fn set_owner_only_dir_permissions(path: &Path) { + use std::os::unix::fs::PermissionsExt; + let _ = fs::set_permissions(path, fs::Permissions::from_mode(0o700)); +} + +#[cfg(not(unix))] +fn set_owner_only_dir_permissions(_path: &Path) {} diff --git a/frontend/src-tauri/tauri.conf.json b/frontend/src-tauri/tauri.conf.json index d57d19f0..e946be22 100644 --- a/frontend/src-tauri/tauri.conf.json +++ b/frontend/src-tauri/tauri.conf.json @@ -58,7 +58,7 @@ } ], "security": { - "csp": "default-src 'self'; connect-src 'self' https://opensecret.cloud https://*.opensecret.cloud https://trymaple.ai https://*.trymaple.ai https://secretgpt.ai https://*.secretgpt.ai https://*.maple-ca8.pages.dev https://raw.githubusercontent.com localhost:* http://localhost:* http://0.0.0.0:* http://127.0.0.1:*; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; worker-src 'self' blob:; media-src 'self' blob: mediastream:" + "csp": "default-src 'self'; connect-src 'self' https://opensecret.cloud https://*.opensecret.cloud https://trymaple.ai https://*.trymaple.ai https://secretgpt.ai https://*.secretgpt.ai https://*.maple-ca8.pages.dev https://raw.githubusercontent.com localhost:* http://localhost:* http://0.0.0.0:* http://127.0.0.1:* ws://localhost:* ws://127.0.0.1:*; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; img-src 'self' data: blob:; font-src 'self' data:; worker-src 'self' blob:; media-src 'self' blob: mediastream:" } }, "bundle": { diff --git a/frontend/src/components/AgentMode.tsx b/frontend/src/components/AgentMode.tsx new file mode 100644 index 00000000..47f02cab --- /dev/null +++ b/frontend/src/components/AgentMode.tsx @@ -0,0 +1,1164 @@ +import { useCallback, useEffect, useMemo, useRef, useState } from "react"; +import { useOpenSecret } from "@opensecret/react"; +import { + AlertCircle, + Bot, + ChevronRight, + Check, + Circle, + FolderOpen, + Loader2, + Play, + RotateCcw, + Send, + Square, + Terminal, + X +} from "lucide-react"; +import { + type ContentBlock, + type PlanEntry, + type SessionNotification, + type ToolCall, + type ToolCallContent, + type ToolCallLocation, + type ToolCallUpdate, + type UsageUpdate +} from "@agentclientprotocol/sdk"; +import { Button } from "@/components/ui/button"; +import { Textarea } from "@/components/ui/textarea"; +import { Badge } from "@/components/ui/badge"; +import { Markdown } from "@/components/markdown"; +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue +} from "@/components/ui/select"; +import { Input } from "@/components/ui/input"; +import { Sidebar, SidebarToggle } from "@/components/Sidebar"; +import { MapleWordmark } from "@/components/MapleWordmark"; +import { + AgentAcpClient, + type PermissionDecision, + type PermissionRequestHandle +} from "@/services/agentAcpClient"; +import { + agentRuntimeService, + type AgentRuntimeStatus, + type RecentProjectRoot +} from "@/services/agentRuntimeService"; +import { proxyService } from "@/services/proxyService"; +import { SIDEBAR_GRID_COLUMNS_CLASS, getSidebarLayoutStyle } from "@/constants/layout"; +import { cn, useIsLandscapeMobile, useIsMobile } from "@/utils/utils"; +import { isTauriDesktop } from "@/utils/platform"; + +type AgentMessageRole = "user" | "assistant" | "thought" | "system"; + +interface AgentMessageItem { + type: "message"; + id: string; + role: AgentMessageRole; + text: string; +} + +interface AgentToolItem { + type: "tool"; + id: string; + title: string; + kind?: string; + status?: string; + input?: unknown; + output?: unknown; + content?: ToolCallContent[]; + locations?: ToolCallLocation[]; + meta?: Record | null; +} + +type AgentTimelineItem = AgentMessageItem | AgentToolItem; + +const DEFAULT_MODEL = "auto:powerful"; + +export function AgentMode() { + const { createApiKey } = useOpenSecret(); + const isMobile = useIsMobile(); + const isLandscapeMobile = useIsLandscapeMobile(); + const isCompactLayout = isMobile || isLandscapeMobile; + const [isSidebarOpen, setIsSidebarOpen] = useState(!isCompactLayout); + const [runtimeStatus, setRuntimeStatus] = useState(null); + const [recentRoots, setRecentRoots] = useState([]); + const [projectRoot, setProjectRoot] = useState(""); + const [model, setModel] = useState(DEFAULT_MODEL); + const [sessionId, setSessionId] = useState(null); + const [timelineItems, setTimelineItems] = useState([]); + const [planEntries, setPlanEntries] = useState([]); + const [usage, setUsage] = useState(null); + const [pendingPermissions, setPendingPermissions] = useState([]); + const [input, setInput] = useState(""); + const [error, setError] = useState(null); + const [isStarting, setIsStarting] = useState(false); + const [isSending, setIsSending] = useState(false); + const clientRef = useRef(null); + const sessionIdRef = useRef(null); + const didRunInitialProxyInitRef = useRef(false); + const messagesEndRef = useRef(null); + + useEffect(() => { + setIsSidebarOpen(!isCompactLayout); + }, [isCompactLayout]); + + useEffect(() => { + sessionIdRef.current = sessionId; + }, [sessionId]); + + useEffect(() => { + messagesEndRef.current?.scrollIntoView({ block: "end" }); + }, [timelineItems, pendingPermissions, planEntries]); + + const activeRootLabel = useMemo(() => { + if (!projectRoot) return "Select folder"; + return recentRoots.find((root) => root.path === projectRoot)?.name || basename(projectRoot); + }, [projectRoot, recentRoots]); + + const toggleSidebar = useCallback(() => setIsSidebarOpen((prev) => !prev), []); + + const appendRuntimeLog = useCallback((message: string) => { + if (!isTauriDesktop()) return; + void agentRuntimeService.appendRuntimeLog(message).catch(() => { + // Logging must never block Agent Mode interaction. + }); + }, []); + + const ensureMapleProxyReady = useCallback(async () => { + appendRuntimeLog("Ensuring Maple proxy is ready for Agent Mode"); + const status = await proxyService.ensureProxyReady(async (name) => { + appendRuntimeLog(`Creating Agent Mode proxy API key ${name}`); + const response = await createApiKey(name); + return response.key; + }); + appendRuntimeLog( + `Maple proxy ready for Agent Mode on ${status.config.host}:${status.config.port} backend=${status.config.backend_url || "default"}` + ); + return status; + }, [appendRuntimeLog, createApiKey]); + + useEffect(() => { + let cancelled = false; + async function loadInitialState() { + if (!isTauriDesktop()) return; + try { + const [status, config, roots] = await Promise.all([ + agentRuntimeService.getRuntimeStatus(), + agentRuntimeService.loadConfig(), + agentRuntimeService.listRecentProjectRoots() + ]); + if (cancelled) return; + setRuntimeStatus(status); + setRecentRoots(roots); + const root = status.projectRoot || config.defaultProjectRoot || roots[0]?.path || ""; + setProjectRoot(root); + setModel(status.model || config.defaultModel || DEFAULT_MODEL); + + if (!didRunInitialProxyInitRef.current) { + didRunInitialProxyInitRef.current = true; + await ensureMapleProxyReady(); + } + } catch (loadError) { + if (!cancelled) setError(errorMessage(loadError)); + } + } + void loadInitialState(); + return () => { + cancelled = true; + clientRef.current?.close(); + clientRef.current = null; + }; + }, [ensureMapleProxyReady]); + + const handleSessionUpdate = useCallback( + (notification: SessionNotification) => { + const update = notification.update; + void agentRuntimeService.appendSessionEvent(notification.sessionId, { + type: "sessionUpdate", + update + }); + + switch (update.sessionUpdate) { + case "agent_message_chunk": + appendMessageChunk( + "assistant", + messageIdFromUpdate(update), + contentBlockText(update.content) + ); + break; + case "agent_thought_chunk": + appendMessageChunk( + "thought", + messageIdFromUpdate(update), + contentBlockText(update.content) + ); + break; + case "tool_call": + mergeToolCall(update); + if (update.status === "failed") { + appendRuntimeLog(`[ACP tool failed] ${toolFailureSummary(update)}`); + } + break; + case "tool_call_update": + mergeToolCall(update); + if (update.status === "failed") { + appendRuntimeLog(`[ACP tool failed] ${toolFailureSummary(update)}`); + } + break; + case "plan": + setPlanEntries(update.entries); + break; + case "usage_update": + setUsage(update); + break; + case "user_message_chunk": + case "available_commands_update": + case "current_mode_update": + case "config_option_update": + case "session_info_update": + break; + } + }, + [appendRuntimeLog] + ); + + const createClient = useCallback(() => { + return new AgentAcpClient({ + onSessionUpdate: handleSessionUpdate, + onPermissionRequest: (request) => { + setPendingPermissions((current) => [...current, request]); + }, + onDiagnostic: (diagnostic) => { + appendRuntimeLog(`[ACP ${diagnostic.phase}] ${diagnostic.message}`); + }, + onClosed: () => { + clientRef.current = null; + } + }); + }, [appendRuntimeLog, handleSessionUpdate]); + + const refreshRuntimeStatus = useCallback(async () => { + if (!isTauriDesktop()) return; + const status = await agentRuntimeService.getRuntimeStatus(); + setRuntimeStatus(status); + }, []); + + const chooseProjectRoot = useCallback(async () => { + if (!isTauriDesktop()) return; + try { + const { open } = await import("@tauri-apps/plugin-dialog"); + const selected = await open({ + directory: true, + multiple: false, + title: "Select project folder" + }); + if (typeof selected === "string") { + setProjectRoot(selected); + const roots = await agentRuntimeService.saveRecentProjectRoot(selected); + setRecentRoots(roots); + } + } catch (chooseError) { + setError(errorMessage(chooseError)); + } + }, []); + + const ensureConnectedSession = useCallback(async () => { + if (!projectRoot) { + throw new Error("Select a project folder first"); + } + + await ensureMapleProxyReady(); + + let status = await agentRuntimeService.getRuntimeStatus(); + if (!status.running || !status.acpUrl) { + setIsStarting(true); + try { + status = await agentRuntimeService.startRuntime({ + projectRoot, + model: model || DEFAULT_MODEL, + mode: "approve" + }); + setRuntimeStatus(status); + setRecentRoots(await agentRuntimeService.listRecentProjectRoots()); + } finally { + setIsStarting(false); + } + } + + if (!status.acpUrl) { + throw new Error("ACP endpoint is not available"); + } + + if (!clientRef.current) { + const nextClient = createClient(); + await nextClient.connect(status.acpUrl); + clientRef.current = nextClient; + } + + let activeSessionId = sessionIdRef.current; + if (!activeSessionId) { + activeSessionId = await clientRef.current.newSession(projectRoot); + setSessionId(activeSessionId); + sessionIdRef.current = activeSessionId; + await agentRuntimeService.appendSessionEvent(activeSessionId, { + type: "sessionStarted", + projectRoot, + model: model || DEFAULT_MODEL + }); + } + + return { client: clientRef.current, sessionId: activeSessionId }; + }, [createClient, ensureMapleProxyReady, model, projectRoot]); + + const startRuntime = useCallback(async () => { + setError(null); + setIsStarting(true); + try { + if (!projectRoot) { + throw new Error("Select a project folder first"); + } + await ensureMapleProxyReady(); + const status = await agentRuntimeService.startRuntime({ + projectRoot: projectRoot || null, + model: model || DEFAULT_MODEL, + mode: "approve" + }); + setRuntimeStatus(status); + setProjectRoot(status.projectRoot || projectRoot); + setModel(status.model || model || DEFAULT_MODEL); + setRecentRoots(await agentRuntimeService.listRecentProjectRoots()); + } catch (startError) { + setError(errorMessage(startError)); + } finally { + setIsStarting(false); + } + }, [ensureMapleProxyReady, model, projectRoot]); + + const restartRuntime = useCallback(async () => { + setError(null); + setIsStarting(true); + clientRef.current?.close(); + clientRef.current = null; + setSessionId(null); + setTimelineItems([]); + setPlanEntries([]); + setUsage(null); + setPendingPermissions([]); + try { + await ensureMapleProxyReady(); + const status = await agentRuntimeService.restartRuntime({ + projectRoot: projectRoot || null, + model: model || DEFAULT_MODEL, + mode: "approve" + }); + setRuntimeStatus(status); + setProjectRoot(status.projectRoot || projectRoot); + setModel(status.model || model || DEFAULT_MODEL); + } catch (restartError) { + setError(errorMessage(restartError)); + } finally { + setIsStarting(false); + } + }, [ensureMapleProxyReady, model, projectRoot]); + + const stopRuntime = useCallback(async () => { + clientRef.current?.close(); + clientRef.current = null; + setSessionId(null); + setPendingPermissions([]); + setRuntimeStatus(await agentRuntimeService.stopRuntime()); + }, []); + + const sendMessage = useCallback(async () => { + const text = input.trim(); + if (!text || isSending) return; + + setError(null); + setInput(""); + setIsSending(true); + setPlanEntries([]); + setUsage(null); + setPendingPermissions([]); + const userMessage: AgentMessageItem = { + type: "message", + id: crypto.randomUUID(), + role: "user", + text + }; + setTimelineItems((current) => [...current, userMessage]); + + try { + const active = await ensureConnectedSession(); + await agentRuntimeService.appendSessionEvent(active.sessionId, { + type: "userPrompt", + text + }); + await active.client.prompt(active.sessionId, text); + } catch (sendError) { + setError(errorMessage(sendError)); + setTimelineItems((current) => [ + ...current, + { + type: "message", + id: crypto.randomUUID(), + role: "system", + text: errorMessage(sendError) + } + ]); + } finally { + setIsSending(false); + } + }, [ensureConnectedSession, input, isSending]); + + const cancelPrompt = useCallback(async () => { + if (!sessionId || !clientRef.current) return; + try { + await clientRef.current.cancel(sessionId); + } catch (cancelError) { + setError(errorMessage(cancelError)); + } + }, [sessionId]); + + const respondToPermission = useCallback((id: string, decision: PermissionDecision) => { + setPendingPermissions((current) => { + const request = current.find((candidate) => candidate.id === id); + request?.decide(decision); + return current.filter((candidate) => candidate.id !== id); + }); + }, []); + + const cancelPermission = useCallback((id: string) => { + setPendingPermissions((current) => { + const request = current.find((candidate) => candidate.id === id); + request?.cancel(); + return current.filter((candidate) => candidate.id !== id); + }); + }, []); + + const handleKeyDown = useCallback( + (event: React.KeyboardEvent) => { + if (event.key === "Enter" && !event.shiftKey && !isCompactLayout) { + event.preventDefault(); + void sendMessage(); + } + }, + [isCompactLayout, sendMessage] + ); + + const sidebarLayoutStyle = getSidebarLayoutStyle({ offsetContent: isSidebarOpen }); + + if (!isTauriDesktop()) { + return ( +
+
+ +

Agent Mode is available in Maple Desktop.

+
+
+ ); + } + + return ( +
+ + +
+ {!isSidebarOpen && ( +
+ + +
+ )} + +
+
+
+ +
+
+
+

Agent Mode

+ +
+

+ {projectRoot ? projectRoot : "No project folder selected"} +

+
+
+
+ + {runtimeStatus?.running ? ( + + ) : ( + + )} +
+
+ + {error && ( +
+
+ + {error} +
+
+ )} + +
+
+
+ {timelineItems.length === 0 ? ( +
+
+

+ Work in a folder... +

+ +
+
+ ) : ( + <> + + {planEntries.length > 0 && } + {pendingPermissions.length > 0 && ( + + )} + {usage && } + + )} +
+
+
+ + {timelineItems.length > 0 ? ( +
+
+ +
+
+ ) : null} +
+
+
+ ); + + function appendMessageChunk( + role: AgentMessageRole, + messageId: string | null | undefined, + text: string + ) { + if (!text) return; + setTimelineItems((current) => { + const timelineMessageId = messageId ? `${messageId}:${role}` : undefined; + const index = messageId + ? current.findIndex((item) => item.type === "message" && item.id === timelineMessageId) + : current.length - 1; + const existing = index >= 0 ? current[index] : null; + if (!messageId && (existing?.type !== "message" || existing.role !== role)) { + return [...current, { type: "message", id: `${role}-${crypto.randomUUID()}`, role, text }]; + } + if (index >= 0) { + const next = [...current]; + const message = next[index]; + if (message.type === "message") { + next[index] = { ...message, text: message.text + text }; + } + return next; + } + return [ + ...current, + { + type: "message", + id: timelineMessageId || `${role}-${crypto.randomUUID()}`, + role, + text + } + ]; + }); + } + + function mergeToolCall(update: (ToolCall | ToolCallUpdate) & { sessionUpdate: string }) { + setTimelineItems((current) => { + const index = current.findIndex( + (item) => item.type === "tool" && item.id === update.toolCallId + ); + const previous = index >= 0 ? current[index] : null; + const nextTool: AgentToolItem = { + type: "tool", + id: update.toolCallId, + title: + update.title ?? (previous?.type === "tool" ? previous.title : undefined) ?? "Tool call", + kind: update.kind ?? (previous?.type === "tool" ? previous.kind : undefined), + status: update.status ?? (previous?.type === "tool" ? previous.status : undefined), + input: update.rawInput ?? (previous?.type === "tool" ? previous.input : undefined), + output: update.rawOutput ?? (previous?.type === "tool" ? previous.output : undefined), + content: update.content ?? (previous?.type === "tool" ? previous.content : undefined), + locations: update.locations ?? (previous?.type === "tool" ? previous.locations : undefined), + meta: update._meta ?? (previous?.type === "tool" ? previous.meta : undefined) + }; + + if (index >= 0) { + const next = [...current]; + next[index] = nextTool; + return next; + } + return [...current, nextTool]; + }); + } +} + +interface AgentComposerProps { + activeRootLabel: string; + input: string; + isSending: boolean; + isStarting: boolean; + model: string; + projectRoot: string; + recentRoots: RecentProjectRoot[]; + onCancelPrompt: () => void; + onChooseProjectRoot: () => void; + onInputChange: (value: string) => void; + onKeyDown: (event: React.KeyboardEvent) => void; + onModelChange: (value: string) => void; + onProjectRootChange: (value: string) => void; + onRestartRuntime: () => void; + onSendMessage: () => void; +} + +function AgentComposer({ + activeRootLabel, + input, + isSending, + isStarting, + model, + projectRoot, + recentRoots, + onCancelPrompt, + onChooseProjectRoot, + onInputChange, + onKeyDown, + onModelChange, + onProjectRootChange, + onRestartRuntime, + onSendMessage +}: AgentComposerProps) { + return ( +
+
+ + + onModelChange(event.target.value)} + className="h-8 w-[11rem] rounded-md border-0 bg-muted/60 text-xs" + aria-label="Agent model" + /> + +
+
+