diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 8133334ed..b6799cfc2 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -34,7 +34,7 @@ jobs:
matrix:
check: [fmt, clippy, test]
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
@@ -110,10 +110,10 @@ jobs:
matrix:
check: [lint, typecheck, test, build]
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ env.NODE_VERSION }}
@@ -154,10 +154,10 @@ jobs:
matrix:
check: [typecheck]
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ env.NODE_VERSION }}
@@ -181,10 +181,10 @@ jobs:
matrix:
check: [typecheck, no-inline-edit]
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ env.NODE_VERSION }}
@@ -212,10 +212,10 @@ jobs:
matrix:
check: [lint, test]
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ env.NODE_VERSION }}
@@ -243,7 +243,7 @@ jobs:
matrix:
check: [analyze, test]
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Flutter
uses: subosito/flutter-action@1a449444c387b1966244ae4d4f8c696479add0b2 # v2
@@ -275,10 +275,10 @@ jobs:
matrix:
check: [lint]
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ env.NODE_VERSION }}
@@ -320,7 +320,7 @@ jobs:
name: Metal GPU tests (macOS)
runs-on: macos-latest
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
@@ -365,10 +365,10 @@ jobs:
matrix:
check: [build]
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Java
- uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
+ uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
with:
distribution: temurin
java-version: ${{ env.JAVA_VERSION }}
@@ -411,10 +411,10 @@ jobs:
matrix:
check: [build, test]
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Java
- uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
+ uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
with:
distribution: temurin
java-version: ${{ env.JAVA_VERSION }}
diff --git a/.github/workflows/pages.yml b/.github/workflows/pages.yml
index 31ab02407..d70540691 100644
--- a/.github/workflows/pages.yml
+++ b/.github/workflows/pages.yml
@@ -23,7 +23,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Ruby
uses: ruby/setup-ruby@v1
diff --git a/.github/workflows/pr-bot.yml b/.github/workflows/pr-bot.yml
index 78e3231fc..69bd25cf1 100644
--- a/.github/workflows/pr-bot.yml
+++ b/.github/workflows/pr-bot.yml
@@ -20,7 +20,7 @@ jobs:
steps:
- name: Checkout repository at PR head
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Check out the PR branch so the agent sees the PR's code
ref: refs/pull/${{ github.event.issue.number }}/head
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index b9f41acdb..731bd4bdc 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -31,7 +31,7 @@ jobs:
name: Security Audit
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
@@ -74,7 +74,7 @@ jobs:
needs: audit
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
@@ -98,7 +98,7 @@ jobs:
# TODO: pin to a verified SHA during Phase 3 hardening.
- name: Setup Node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "20"
@@ -121,7 +121,7 @@ jobs:
# TODO: pin to a verified SHA during Phase 3 hardening.
- name: Setup Python
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v7
with:
python-version: "3.12"
@@ -188,7 +188,7 @@ jobs:
archive: vibecli-x86_64-windows.zip
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
@@ -371,7 +371,7 @@ jobs:
args: ""
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
@@ -391,7 +391,7 @@ jobs:
xdg-utils
- name: Setup Node.js
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ env.NODE_VERSION }}
@@ -556,7 +556,7 @@ jobs:
args: ""
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
@@ -576,7 +576,7 @@ jobs:
xdg-utils
- name: Setup Node.js
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ env.NODE_VERSION }}
@@ -735,7 +735,7 @@ jobs:
args: ""
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
@@ -755,7 +755,7 @@ jobs:
xdg-utils
- name: Setup Node.js
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: ${{ env.NODE_VERSION }}
@@ -887,10 +887,10 @@ jobs:
needs: audit
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Java
- uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
+ uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
with:
distribution: temurin
java-version: ${{ env.JAVA_VERSION }}
@@ -940,7 +940,7 @@ jobs:
needs: audit
runs-on: macos-latest
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# Pin Xcode 26 — iOS 26 SDK is the required build SDK from April 28, 2026.
- name: Select Xcode 26
@@ -1092,10 +1092,10 @@ jobs:
needs: audit
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Java
- uses: actions/setup-java@ad2b38190b15e4d6bdf0c97fb4fca8412226d287 # v5.3.0
+ uses: actions/setup-java@b6effb05e454b25005698d916606bdc6ffcbf961 # v5.7.0
with:
distribution: temurin
java-version: ${{ env.JAVA_VERSION }}
@@ -1143,7 +1143,7 @@ jobs:
needs: audit
runs-on: macos-latest
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# Pin Xcode 26 so builds link against the watchOS 26 SDK — required
# for App Store Connect submissions from April 28, 2026 onward.
@@ -1241,7 +1241,7 @@ jobs:
echo "skip=false" >> "$GITHUB_OUTPUT"
fi
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
if: steps.apple-secrets.outputs.skip != 'true'
- name: Select Xcode 26
@@ -1381,7 +1381,7 @@ jobs:
runs-on: ubuntu-24.04
continue-on-error: true
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
@@ -1418,7 +1418,7 @@ jobs:
needs: audit
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
@@ -1451,7 +1451,7 @@ jobs:
contents: read
id-token: write # required for cosign keyless OIDC
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Set up Docker Buildx
uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
@@ -1521,7 +1521,7 @@ jobs:
}}
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Download all artifacts
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4
diff --git a/.github/workflows/security.yml b/.github/workflows/security.yml
index 840cbbb1b..4d0e5e6ec 100644
--- a/.github/workflows/security.yml
+++ b/.github/workflows/security.yml
@@ -33,7 +33,7 @@ jobs:
name: cargo audit (RUSTSEC advisories)
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
@@ -100,7 +100,7 @@ jobs:
name: cargo deny (licenses, sources, bans)
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Install Rust toolchain
uses: dtolnay/rust-toolchain@631a55b12751854ce901bb631d5902ceb48146f7 # stable
@@ -110,7 +110,7 @@ jobs:
# via tame-index 0.18.0 (release notes). Stuck on v2.0.4 made
# the job fail on any transitive dep that declared
# `edition = "2024"` — globset 0.4.18 in this repo, since 2026-04-xx.
- uses: EmbarkStudios/cargo-deny-action@bb137d7af7e4fb67e5f82a49c4fce4fad40782fe # v2.0.20
+ uses: EmbarkStudios/cargo-deny-action@3c6349835b2b7b196a839186cb8b78e02f7b5f25 # v2.1.1
with:
command: check
arguments: --all-features
@@ -127,10 +127,10 @@ jobs:
# carrying 8 high-severity advisories that nothing ever reported.
path: [vibecoder, vibeaichat, vibedesk, packages/agent-sdk, vscode-extension]
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Node
- uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0
+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: "20"
@@ -181,12 +181,12 @@ jobs:
name: pip-audit (vibe-rl-py)
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Setup Python
# TODO: pin to verified SHA during Phase 3 hardening (consistent with
# the rest of this workflow's pin policy).
- uses: actions/setup-python@v6
+ uses: actions/setup-python@v7
with:
python-version: "3.12"
@@ -255,7 +255,7 @@ jobs:
name: gitleaks (secret scanning)
runs-on: ubuntu-latest
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
# Full history so gitleaks can scan past commits, not just the diff.
# If the repo grows beyond ~5min scan time, switch to `fetch-depth: 1`
@@ -280,7 +280,7 @@ jobs:
container:
image: semgrep/semgrep
steps:
- - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
# Three rulesets:
# 1. OWASP Top 10 baseline (registry)
diff --git a/Cargo.lock b/Cargo.lock
index ffb7269d5..e0d4bb67c 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -33,10 +33,20 @@ version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d122413f284cf2d62fb1b7db97e02edb8cda96d769b16e443a4f6195e35662b0"
dependencies = [
- "crypto-common",
+ "crypto-common 0.1.7",
"generic-array",
]
+[[package]]
+name = "aead"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "1973cfbc1a2daf9cf550e74e1f088c28e7f7d8c1e1418fb6c9dc5184b7e84c99"
+dependencies = [
+ "crypto-common 0.2.2",
+ "inout 0.2.2",
+]
+
[[package]]
name = "aes"
version = "0.8.4"
@@ -44,19 +54,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b169f7a6d4742236a0a00c541b845991d0ac43e546831af1249753ab4c3aa3a0"
dependencies = [
"cfg-if",
- "cipher",
+ "cipher 0.4.4",
"cpufeatures 0.2.17",
]
+[[package]]
+name = "aes"
+version = "0.9.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f8eb277bec05f56a0e0591f155a484cbd0f4f07ff2905051a48c72f004f7ed58"
+dependencies = [
+ "cipher 0.5.2",
+ "cpubits",
+ "cpufeatures 0.3.0",
+]
+
[[package]]
name = "aes-gcm"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "831010a0f742e1209b3bcea8fab6a8e149051ba6099432c8cb2cc117dec3ead1"
dependencies = [
- "aead",
- "aes",
- "cipher",
+ "aead 0.5.2",
+ "aes 0.8.4",
+ "cipher 0.4.4",
"ctr",
"ghash",
"subtle",
@@ -161,7 +182,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "96401ca08501972288ecbcde33902fce858bf73fbcbdf91dab8c3a9544e106bb"
dependencies = [
"anstyle",
- "unicode-width 0.2.0",
+ "unicode-width",
]
[[package]]
@@ -200,7 +221,7 @@ version = "1.1.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
dependencies = [
- "windows-sys 0.61.2",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -211,7 +232,7 @@ checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
dependencies = [
"anstyle",
"once_cell_polyfill",
- "windows-sys 0.61.2",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -251,9 +272,6 @@ name = "arbitrary"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1"
-dependencies = [
- "derive_arbitrary",
-]
[[package]]
name = "arc-swap"
@@ -616,7 +634,7 @@ dependencies = [
"serde_json",
"serde_path_to_error",
"serde_urlencoded",
- "sha1",
+ "sha1 0.10.6",
"sync_wrapper",
"tokio",
"tokio-tungstenite 0.29.0",
@@ -647,9 +665,9 @@ dependencies = [
[[package]]
name = "base16ct"
-version = "0.2.0"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "4c7f02d4ea65f2c1853089ffd8d2787bdbc63de2f0d29dedbcf8ccdfa0ccd4cf"
+checksum = "fd307490d624467aa6f74b0eabb77633d1f758a7b25f12bceb0b22e08d9726f6"
[[package]]
name = "base64"
@@ -669,6 +687,12 @@ version = "0.22.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6"
+[[package]]
+name = "base64"
+version = "0.23.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ac07cdecf99051d9a5238b80f35af32cdeba5b336e55d957b318b50137e18da5"
+
[[package]]
name = "base64ct"
version = "1.8.3"
@@ -779,7 +803,7 @@ version = "0.10.6"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "46502ad458c9a52b69d4d4d32775c788b7a1b85e8bc9d482d92250fc0e3f8efe"
dependencies = [
- "digest",
+ "digest 0.10.7",
]
[[package]]
@@ -797,6 +821,16 @@ 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",
+ "zeroize",
+]
+
[[package]]
name = "block-padding"
version = "0.3.3"
@@ -953,21 +987,11 @@ dependencies = [
[[package]]
name = "bzip2"
-version = "0.5.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "49ecfb22d906f800d4fe833b6282cf4dc1c298f5057ca0b5445e5c209735ca47"
-dependencies = [
- "bzip2-sys",
-]
-
-[[package]]
-name = "bzip2-sys"
-version = "0.1.13+1.0.8"
+version = "0.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "225bff33b2141874fe80d71e07d6eec4f85c5c216453dd96388240f96e1acc14"
+checksum = "f3a53fac24f34a81bc9954b5d6cfce0c21e18ec6959f44f56e8e90e4bb7c346c"
dependencies = [
- "cc",
- "pkg-config",
+ "libbz2-rs-sys",
]
[[package]]
@@ -1065,7 +1089,7 @@ dependencies = [
"tokenizers 0.22.2",
"yoke 0.8.3",
"zerocopy",
- "zip 8.6.0",
+ "zip",
]
[[package]]
@@ -1190,7 +1214,7 @@ version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "26b52a9543ae338f279b96b0b9fed9c8093744685043739079ce85cd58f289a6"
dependencies = [
- "cipher",
+ "cipher 0.4.4",
]
[[package]]
@@ -1280,7 +1304,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3613f74bd2eac03dad61bd53dbe620703d4371614fe0bc3b9f04dd36fe4e818"
dependencies = [
"cfg-if",
- "cipher",
+ "cipher 0.4.4",
"cpufeatures 0.2.17",
]
@@ -1291,6 +1315,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "6f8d983286843e49675a4b7a2d174efe136dc93a18d69130dd18198a6c167601"
dependencies = [
"cfg-if",
+ "cipher 0.5.2",
"cpufeatures 0.3.0",
"rand_core 0.10.1",
]
@@ -1301,13 +1326,25 @@ version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "10cd79432192d1c0f4e1a0fef9527696cc039165d729fb41b3f4f4f354c2dc35"
dependencies = [
- "aead",
+ "aead 0.5.2",
"chacha20 0.9.1",
- "cipher",
- "poly1305",
+ "cipher 0.4.4",
+ "poly1305 0.8.0",
"zeroize",
]
+[[package]]
+name = "chacha20poly1305"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "9b89e1c441e926b9c82a8d023f6e1b7ae0adcfaa7d621814e4d60789bac751cb"
+dependencies = [
+ "aead 0.6.1",
+ "chacha20 0.10.0",
+ "cipher 0.5.2",
+ "poly1305 0.9.1",
+]
+
[[package]]
name = "chrono"
version = "0.4.45"
@@ -1355,11 +1392,22 @@ version = "0.4.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "773f3b9af64447d2ce9850330c473515014aa235e6a783b02db81ff39e4a3dad"
dependencies = [
- "crypto-common",
- "inout",
+ "crypto-common 0.1.7",
+ "inout 0.1.4",
"zeroize",
]
+[[package]]
+name = "cipher"
+version = "0.5.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "e8cf2a2c93cd704877c0858356ed03480ff301ee950b43f1cbe4573b088bfa6c"
+dependencies = [
+ "block-buffer 0.12.1",
+ "crypto-common 0.2.2",
+ "inout 0.2.2",
+]
+
[[package]]
name = "clang-sys"
version = "1.8.1"
@@ -1439,6 +1487,12 @@ dependencies = [
"cc",
]
+[[package]]
+name = "cmov"
+version = "0.5.4"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "0c9ea0ac24bc397ab3c98583a3c9ba74fa56b09a4449bbe172b9b1ddb016027a"
+
[[package]]
name = "cobs"
version = "0.3.0"
@@ -1503,7 +1557,7 @@ dependencies = [
"encode_unicode",
"libc",
"once_cell",
- "unicode-width 0.2.0",
+ "unicode-width",
"windows-sys 0.59.0",
]
@@ -1515,21 +1569,21 @@ checksum = "d64e8af5551369d19cf50138de61f1c42074ab970f74e99be916646777f8fc87"
dependencies = [
"encode_unicode",
"libc",
- "unicode-width 0.2.0",
+ "unicode-width",
"windows-sys 0.61.2",
]
[[package]]
name = "const-oid"
-version = "0.9.6"
+version = "0.10.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c2459377285ad874054d797f3ccebf984978aa39129f6eafde5cdc8315b612f8"
+checksum = "a6ef517f0926dd24a1582492c791b6a4818a4d94e789a334894aa15b0d12f55c"
[[package]]
name = "constant_time_eq"
-version = "0.3.1"
+version = "0.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7c74b8349d32d297c9134b8c88677813a227df8f779daa29bfc29c183fe3dca6"
+checksum = "3d52eff69cd5e647efe296129160853a42795992097e8af39800e1060caeea9b"
[[package]]
name = "convert_case"
@@ -1620,6 +1674,12 @@ dependencies = [
"cfg-if",
]
+[[package]]
+name = "cpubits"
+version = "0.1.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "15b85f9c39137c3a891689859392b1bd49812121d0d61c9caf00d46ed5ce06ae"
+
[[package]]
name = "cpufeatures"
version = "0.2.17"
@@ -1702,7 +1762,7 @@ dependencies = [
"rustc-hash 2.1.2",
"serde",
"serde_derive",
- "sha2",
+ "sha2 0.10.9",
"smallvec 1.15.1",
"target-lexicon 0.13.5",
"wasmtime-internal-core",
@@ -1944,12 +2004,16 @@ checksum = "460fbee9c2c2f33933d720630a6a0bac33ba7053db5344fac858d4b8952d77d5"
[[package]]
name = "crypto-bigint"
-version = "0.5.5"
+version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0dc92fb57ca44df6db8059111ab3af99a63d5d0f8375d9972e319a379c6bab76"
+checksum = "1a52aa3fcda4e6302a9f48734f234d35d4721b96f8fe07d073f07ce9df4f0271"
dependencies = [
- "generic-array",
- "rand_core 0.6.4",
+ "cpubits",
+ "ctutils",
+ "getrandom 0.4.3",
+ "hybrid-array",
+ "num-traits",
+ "rand_core 0.10.1",
"subtle",
"zeroize",
]
@@ -1961,10 +2025,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78c8292055d1c1df0cce5d180393dc8cce0abec0a7102adb6c7b1eef6016d60a"
dependencies = [
"generic-array",
- "rand_core 0.6.4",
"typenum",
]
+[[package]]
+name = "crypto-common"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ce6e4c961d6cd6c9a86db418387425e8bdeaf05b3c8bc1411e6dca4c252f1453"
+dependencies = [
+ "getrandom 0.4.3",
+ "hybrid-array",
+ "rand_core 0.10.1",
+]
+
[[package]]
name = "csscolorparser"
version = "0.6.2"
@@ -2041,7 +2115,17 @@ version = "0.9.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0369ee1ad671834580515889b80f2ea915f23b8be8d0daa4bbaf2ac5c7590835"
dependencies = [
- "cipher",
+ "cipher 0.4.4",
+]
+
+[[package]]
+name = "ctutils"
+version = "0.4.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7d5515a3834141de9eafb9717ad39eea8247b5674e6066c404e8c4b365d2a29e"
+dependencies = [
+ "cmov",
+ "subtle",
]
[[package]]
@@ -2114,7 +2198,7 @@ dependencies = [
"rayon",
"serde",
"serde_json",
- "sha2",
+ "sha2 0.10.9",
"thiserror 2.0.18",
"walkdir",
"which",
@@ -2345,9 +2429,9 @@ checksum = "5729f5117e208430e437df2f4843f5e5952997175992d1414f94c57d61e270b4"
[[package]]
name = "der"
-version = "0.7.10"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e7c1832837b905bbfb5101e07cc24c8deddf52f93225eee6ead5f4d63d53ddcb"
+checksum = "a69dedd701da44b0536442edf09c81a64b0ab97a7a4a5e3d1971f00027cbc63d"
dependencies = [
"const-oid",
"pem-rfc7468",
@@ -2389,17 +2473,6 @@ dependencies = [
"syn 2.0.117",
]
-[[package]]
-name = "derive_arbitrary"
-version = "1.4.2"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.117",
-]
-
[[package]]
name = "derive_builder"
version = "0.20.2"
@@ -2480,12 +2553,24 @@ version = "0.10.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9ed9a281f7bc9b7576e61468ba615a66a5c8cfdff42420a70aa82701a3b1e292"
dependencies = [
- "block-buffer",
- "const-oid",
- "crypto-common",
+ "block-buffer 0.10.4",
+ "crypto-common 0.1.7",
"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",
+ "const-oid",
+ "crypto-common 0.2.2",
+ "ctutils",
+ "zeroize",
+]
+
[[package]]
name = "directories-next"
version = "2.0.0"
@@ -2524,7 +2609,7 @@ dependencies = [
"libc",
"option-ext",
"redox_users 0.5.2",
- "windows-sys 0.61.2",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -2695,16 +2780,17 @@ checksum = "e1d926b4d407d372f141f93bb444696142c29d32962ccbd3531117cf3aa0bfa9"
[[package]]
name = "ecdsa"
-version = "0.16.9"
+version = "0.17.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "ee27f32b5c5292967d2d4a9d7f1e0b0aed2c15daded5a60300e4abb9d8020bca"
+checksum = "c0681a4fc24c767085329728d8dfba959af91228aa4610cca4f8ce317ba46ae0"
dependencies = [
"der",
- "digest",
+ "digest 0.11.3",
"elliptic-curve",
"rfc6979",
"signature",
"spki",
+ "zeroize",
]
[[package]]
@@ -2724,19 +2810,20 @@ dependencies = [
[[package]]
name = "elliptic-curve"
-version = "0.13.8"
+version = "0.14.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b5e6043086bf7973472e0c7dff2142ea0b680d30e18d9cc40f267efbf222bd47"
+checksum = "9d65aa39b3a5c1c9c1b745c9a019234bb7a21b77abcb4f4d266d706e2d577d65"
dependencies = [
"base16ct",
"crypto-bigint",
- "digest",
+ "crypto-common 0.2.2",
+ "digest 0.11.3",
"ff",
- "generic-array",
"group",
+ "hybrid-array",
"pem-rfc7468",
"pkcs8",
- "rand_core 0.6.4",
+ "rand_core 0.10.1",
"sec1",
"subtle",
"zeroize",
@@ -2899,7 +2986,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb"
dependencies = [
"libc",
- "windows-sys 0.61.2",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -3073,11 +3160,11 @@ dependencies = [
[[package]]
name = "ff"
-version = "0.13.1"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c0b50bfb653653f9ca9095b427bed08ab8d75a137839d9ad64eb11810d5b6393"
+checksum = "a1f686ab92a9fb0eaf188f6c6c87b89490baa6fdb0db4544ba4dc47f7942489f"
dependencies = [
- "rand_core 0.6.4",
+ "rand_core 0.10.1",
"subtle",
]
@@ -3140,6 +3227,7 @@ checksum = "843fba2746e448b37e26a819579957415c8cef339bf08564fe8b7ddbd959573c"
dependencies = [
"crc32fast",
"miniz_oxide",
+ "zlib-rs",
]
[[package]]
@@ -3827,7 +3915,6 @@ checksum = "85649ca51fd72272d7821adaf274ad91c288277713d9c18820d8499a7ff69e9a"
dependencies = [
"typenum",
"version_check",
- "zeroize",
]
[[package]]
@@ -3836,7 +3923,7 @@ version = "0.2.24"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "cfe4fbac503b8d1f88e6676011885f34b7174f46e59956bba534ba83abded4df"
dependencies = [
- "unicode-width 0.2.0",
+ "unicode-width",
]
[[package]]
@@ -3873,9 +3960,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "300e883d756b2e4ec94e02791f39b04b522276138852cfc41d9fb7e904106099"
dependencies = [
"cfg-if",
+ "js-sys",
"libc",
"r-efi 6.0.0",
"rand_core 0.10.1",
+ "wasm-bindgen",
]
[[package]]
@@ -4062,12 +4151,12 @@ dependencies = [
[[package]]
name = "group"
-version = "0.13.0"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f0f9ef7462f7c099f518d754361858f86d8a07af53ba9af0fe635bbccb151a63"
+checksum = "7fd1a1c7a5206c5b7a3f5a0d7ccd3ff85d0c8f5133d62a02680255b0004af5f4"
dependencies = [
"ff",
- "rand_core 0.6.4",
+ "rand_core 0.10.1",
"subtle",
]
@@ -4314,7 +4403,7 @@ version = "0.12.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7b5f8eb2ad728638ea2c7d47a21db23b7b58a72ed6a38256b8a1849f15fbbdf7"
dependencies = [
- "hmac",
+ "hmac 0.12.1",
]
[[package]]
@@ -4323,7 +4412,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 = "hmac"
+version = "0.13.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6303bc9732ae41b04cb554b844a762b4115a61bfaa81e3e83050991eeb56863f"
+dependencies = [
+ "digest 0.11.3",
]
[[package]]
@@ -4361,7 +4459,7 @@ dependencies = [
"html5ever 0.38.0",
"tendril 0.5.0",
"thiserror 2.0.18",
- "unicode-width 0.2.0",
+ "unicode-width",
]
[[package]]
@@ -4435,6 +4533,17 @@ version = "2.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "135b12329e5e3ce057a9f972339ea52bc954fe1e9358ef27f95e89716fbc5424"
+[[package]]
+name = "hybrid-array"
+version = "0.4.14"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "707114b52a152fa7bdb290cd7cd5912d9467273b6d74e21b8d81aca1f8533f6b"
+dependencies = [
+ "subtle",
+ "typenum",
+ "zeroize",
+]
+
[[package]]
name = "hyper"
version = "1.10.1"
@@ -4510,7 +4619,7 @@ dependencies = [
"js-sys",
"log",
"wasm-bindgen",
- "windows-core",
+ "windows-core 0.61.2",
]
[[package]]
@@ -4754,7 +4863,7 @@ dependencies = [
"console 0.15.11",
"number_prefix",
"portable-atomic",
- "unicode-width 0.2.0",
+ "unicode-width",
"web-time",
]
@@ -4767,7 +4876,7 @@ dependencies = [
"console 0.16.3",
"portable-atomic",
"rayon",
- "unicode-width 0.2.0",
+ "unicode-width",
"unit-prefix",
"web-time",
]
@@ -4826,6 +4935,15 @@ dependencies = [
"generic-array",
]
+[[package]]
+name = "inout"
+version = "0.2.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "4250ce6452e92010fdf7268ccc5d14faa80bb12fc741938534c58f16804e03c7"
+dependencies = [
+ "hybrid-array",
+]
+
[[package]]
name = "instability"
version = "0.3.12"
@@ -5159,7 +5277,7 @@ dependencies = [
"rusqlite",
"serde",
"serde_json",
- "sha2",
+ "sha2 0.10.9",
"tempfile",
"thiserror 1.0.69",
"tokio",
@@ -5252,6 +5370,12 @@ dependencies = [
"once_cell",
]
+[[package]]
+name = "libbz2-rs-sys"
+version = "0.2.5"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "34b357333733e8260735ba5894eb928c02ecc69c78715f01a8019e7fa7f2db4c"
+
[[package]]
name = "libc"
version = "0.2.189"
@@ -5475,24 +5599,12 @@ dependencies = [
]
[[package]]
-name = "lzma-rs"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "297e814c836ae64db86b36cf2a557ba54368d03f6afcd7d947c266692f71115e"
-dependencies = [
- "byteorder",
- "crc",
-]
-
-[[package]]
-name = "lzma-sys"
-version = "0.1.20"
+name = "lzma-rust2"
+version = "0.16.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "5fda04ab3764e6cde78b9974eec4f779acaba7c4e84b36eca3cf77c581b85d27"
+checksum = "ca93e534d1142d1d0dcca6d25fe302508a5dfb40b302802904577725ea0b695b"
dependencies = [
- "cc",
- "libc",
- "pkg-config",
+ "sha2 0.11.0",
]
[[package]]
@@ -5606,7 +5718,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d89e7ee0cfbedfc4da3340218492196241d89eefb6dab27de5df917a6d2e78cf"
dependencies = [
"cfg-if",
- "digest",
+ "digest 0.10.7",
]
[[package]]
@@ -5885,11 +5997,11 @@ dependencies = [
"serde-saphyr",
"serde_json",
"serde_plain",
- "sha2",
+ "sha2 0.10.9",
"statrs",
"strum 0.27.2",
"symphonia",
- "sysinfo",
+ "sysinfo 0.36.1",
"tempfile",
"thiserror 2.0.18",
"tokenizers 0.21.4",
@@ -6008,7 +6120,7 @@ dependencies = [
"safetensors 0.8.0",
"serde",
"serde_json",
- "sysinfo",
+ "sysinfo 0.36.1",
"thiserror 2.0.18",
"tokio",
"tracing",
@@ -6092,7 +6204,7 @@ dependencies = [
"png 0.18.1",
"serde",
"thiserror 2.0.18",
- "windows-sys 0.61.2",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -6176,6 +6288,18 @@ dependencies = [
"memoffset",
]
+[[package]]
+name = "nix"
+version = "0.30.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "74523f3a35e05aba87a1d978330aef40f67b0304ac79c1c00b294c9830543db6"
+dependencies = [
+ "bitflags 2.12.1",
+ "cfg-if",
+ "cfg_aliases 0.2.1",
+ "libc",
+]
+
[[package]]
name = "nix"
version = "0.31.3"
@@ -6282,7 +6406,7 @@ version = "0.50.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7957b9740744892f114936ab4a57b3f487491bbeafaf8083688b16841a4240e5"
dependencies = [
- "windows-sys 0.61.2",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -6611,6 +6735,17 @@ dependencies = [
"objc2-foundation",
]
+[[package]]
+name = "objc2-open-directory"
+version = "0.3.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "bb82bed227edf5201dfedf072bba4015a33d3d4a98519837295a90f0a23f676d"
+dependencies = [
+ "objc2",
+ "objc2-core-foundation",
+ "objc2-foundation",
+]
+
[[package]]
name = "objc2-quartz-core"
version = "0.3.2"
@@ -6766,8 +6901,8 @@ dependencies = [
"serde",
"serde_json",
"serde_with",
- "sha1",
- "sha2",
+ "sha1 0.10.6",
+ "sha2 0.10.9",
"thiserror 2.0.18",
]
@@ -6913,14 +7048,15 @@ dependencies = [
[[package]]
name = "p256"
-version = "0.13.2"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c9863ad85fa8f4460f9c48cb909d38a0d689dba1f6f6988a5e3e0d31071bcd4b"
+checksum = "d2c9239b2dbc807adbbe147e8cf72ea7450c3a0aabe62cb8e75ff4ec22e1f72a"
dependencies = [
"ecdsa",
"elliptic-curve",
+ "primefield",
"primeorder",
- "sha2",
+ "sha2 0.11.0",
]
[[package]]
@@ -7040,12 +7176,12 @@ checksum = "df94ce210e5bc13cb6651479fa48d14f601d9858cfe0467f43ae157023b938d3"
[[package]]
name = "pbkdf2"
-version = "0.12.2"
+version = "0.13.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8ed6a7761f76e3b9f92dfb0a60a6a6477c61024b775147ff0973a02653abaf2"
+checksum = "112d82ceb8c5bf524d9af484d4e4970c9fd5a0cc15ba14ad93dccd28873b0629"
dependencies = [
- "digest",
- "hmac",
+ "digest 0.11.3",
+ "hmac 0.13.0",
]
[[package]]
@@ -7087,9 +7223,9 @@ dependencies = [
[[package]]
name = "pem-rfc7468"
-version = "0.7.0"
+version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "88b39c9bfcfc231068454382784bb460aae594343fb030d46e9f50a645418412"
+checksum = "a6305423e0e7738146434843d1694d621cce767262b2a86910beab705e4493d9"
dependencies = [
"base64ct",
]
@@ -7140,7 +7276,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "89815c69d36021a140146f26659a81d6c2afa33d216d736dd4be5381a7362220"
dependencies = [
"pest",
- "sha2",
+ "sha2 0.10.9",
]
[[package]]
@@ -7310,9 +7446,9 @@ dependencies = [
[[package]]
name = "pkcs8"
-version = "0.10.2"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f950b2377845cebe5cf8b5165cb3cc1a5e0fa5cfa3e1f7f55707d8fd82e0a7b7"
+checksum = "451913da69c775a56034ea8d9003d27ee8948e12443eae7c038ba100a4f21cb7"
dependencies = [
"der",
"spki",
@@ -7419,7 +7555,17 @@ checksum = "8159bd90725d2df49889a078b54f4f79e87f1f8a8444194cdca81d38f5393abf"
dependencies = [
"cpufeatures 0.2.17",
"opaque-debug",
- "universal-hash",
+ "universal-hash 0.5.1",
+]
+
+[[package]]
+name = "poly1305"
+version = "0.9.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "6e2d0073b297041425c7c3df6eb4792d598a15323fe63346852b092eca02904c"
+dependencies = [
+ "cpufeatures 0.3.0",
+ "universal-hash 0.6.1",
]
[[package]]
@@ -7431,7 +7577,7 @@ dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
"opaque-debug",
- "universal-hash",
+ "universal-hash 0.5.1",
]
[[package]]
@@ -7488,6 +7634,12 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "439ee305def115ba05938db6eb1644ff94165c5ab5e9420d1c1bcedbba909391"
+[[package]]
+name = "ppmd-rust"
+version = "1.4.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "efca4c95a19a79d1c98f791f10aebd5c1363b473244630bb7dbde1dc98455a24"
+
[[package]]
name = "ppv-lite86"
version = "0.2.21"
@@ -7522,13 +7674,31 @@ dependencies = [
"num-integer",
]
+[[package]]
+name = "primefield"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "c555a6e4eb7d4e158fcb028c835c3b8642206ddc279b5c6b202ef9a8bdb592f4"
+dependencies = [
+ "crypto-bigint",
+ "crypto-common 0.2.2",
+ "ff",
+ "rand_core 0.10.1",
+ "subtle",
+ "zeroize",
+]
+
[[package]]
name = "primeorder"
-version = "0.13.6"
+version = "0.14.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "353e1ca18966c16d9deb1c69278edbc5f194139612772bd9537af60ac231e1e6"
+checksum = "5c9f42978c78a00e3d68f69fc03e57a234debae69da4020a4fb588fcdcd07b06"
dependencies = [
"elliptic-curve",
+ "once_cell",
+ "primefield",
+ "serdect",
+ "wnaf",
]
[[package]]
@@ -7961,7 +8131,7 @@ dependencies = [
"thiserror 2.0.18",
"unicode-segmentation",
"unicode-truncate",
- "unicode-width 0.2.0",
+ "unicode-width",
]
[[package]]
@@ -8024,7 +8194,7 @@ dependencies = [
"strum 0.28.0",
"time",
"unicode-segmentation",
- "unicode-width 0.2.0",
+ "unicode-width",
]
[[package]]
@@ -8360,12 +8530,12 @@ dependencies = [
[[package]]
name = "rfc6979"
-version = "0.4.0"
+version = "0.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f8dd2a808d456c4a54e300a23e9f5a67e122c3024119acbfd73e3bf664491cb2"
+checksum = "b4a459cddafb3fe76b31fd8f1108007566c40301feb64dc7b54656eb7388172b"
dependencies = [
- "hmac",
- "subtle",
+ "crypto-bigint",
+ "hmac 0.13.0",
]
[[package]]
@@ -8539,7 +8709,7 @@ dependencies = [
"errno",
"libc",
"linux-raw-sys",
- "windows-sys 0.61.2",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -8607,7 +8777,7 @@ dependencies = [
"security-framework",
"security-framework-sys",
"webpki-root-certs",
- "windows-sys 0.61.2",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -8636,9 +8806,9 @@ checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d"
[[package]]
name = "rustyline"
-version = "14.0.0"
+version = "17.0.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7803e8936da37efd9b6d4478277f4b2b9bb5cdb37a113e8d63222e58da647e63"
+checksum = "e902948a25149d50edc1a8e0141aad50f54e22ba83ff988cf8f7c9ef07f50564"
dependencies = [
"bitflags 2.12.1",
"cfg-if",
@@ -8648,20 +8818,20 @@ dependencies = [
"libc",
"log",
"memchr",
- "nix 0.28.0",
+ "nix 0.30.1",
"radix_trie 0.2.1",
"rustyline-derive",
"unicode-segmentation",
- "unicode-width 0.1.14",
+ "unicode-width",
"utf8parse",
- "windows-sys 0.52.0",
+ "windows-sys 0.60.2",
]
[[package]]
name = "rustyline-derive"
-version = "0.10.0"
+version = "0.11.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e5af959c8bf6af1aff6d2b463a57f71aae53d1332da58419e30ad8dc7011d951"
+checksum = "5d66de233f908aebf9cc30ac75ef9103185b4b715c6f2fb7a626aa5e5ede53ab"
dependencies = [
"proc-macro2",
"quote",
@@ -8833,14 +9003,14 @@ dependencies = [
[[package]]
name = "sec1"
-version = "0.7.3"
+version = "0.8.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d3e97a565f76233a6003f9f5c54be1d9c5bdfa3eccfb189469f11ec4901c47dc"
+checksum = "d56d437c2f19203ce5f7122e507831de96f3d2d4d3be5af44a0b0a09d8a80e4d"
dependencies = [
"base16ct",
+ "ctutils",
"der",
- "generic-array",
- "pkcs8",
+ "hybrid-array",
"subtle",
"zeroize",
]
@@ -8860,7 +9030,7 @@ version = "5.1.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9a62d7f86047af0077255a29494136b9aaaf697c76ff70b8e49cded4e2623c14"
dependencies = [
- "aes",
+ "aes 0.8.4",
"cbc",
"futures-util",
"generic-array",
@@ -8869,7 +9039,7 @@ dependencies = [
"num",
"once_cell",
"serde",
- "sha2",
+ "sha2 0.10.9",
"zbus",
]
@@ -9152,6 +9322,16 @@ dependencies = [
"unsafe-libyaml",
]
+[[package]]
+name = "serdect"
+version = "0.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "66cf8fedced2fcf12406bcb34223dffb92eaf34908ede12fed414c82b7f00b3e"
+dependencies = [
+ "base16ct",
+ "serde",
+]
+
[[package]]
name = "serial2"
version = "0.2.37"
@@ -9202,7 +9382,18 @@ checksum = "e3bf829a2d51ab4a5ddf1352d8470c140cadc8301b2ae1789db023f01cedd6ba"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
- "digest",
+ "digest 0.10.7",
+]
+
+[[package]]
+name = "sha1"
+version = "0.11.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "aacc4cc499359472b4abe1bf11d0b12e688af9a805fa5e3016f9a386dc2d0214"
+dependencies = [
+ "cfg-if",
+ "cpufeatures 0.3.0",
+ "digest 0.11.3",
]
[[package]]
@@ -9213,7 +9404,18 @@ checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283"
dependencies = [
"cfg-if",
"cpufeatures 0.2.17",
- "digest",
+ "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]]
@@ -9287,12 +9489,12 @@ dependencies = [
[[package]]
name = "signature"
-version = "2.2.0"
+version = "3.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "77549399552de45a898a580c1b41d445bf730df867cc44e6c0233bbc4b8329de"
+checksum = "28d567dcbaf0049cb8ac2608a76cd95ff9e4412e1899d389ee400918ca7537f5"
dependencies = [
- "digest",
- "rand_core 0.6.4",
+ "digest 0.11.3",
+ "rand_core 0.10.1",
]
[[package]]
@@ -9341,9 +9543,12 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e"
[[package]]
name = "similar"
-version = "2.7.0"
+version = "3.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bbbb5d9659141646ae647b42fe094daf6c6192d1620870b449d9557f748b2daa"
+checksum = "85ee016af5d736b69fc89e19254540fa4b5f5492853fb5503920f084011c78b6"
+dependencies = [
+ "bstr",
+]
[[package]]
name = "siphasher"
@@ -9449,12 +9654,12 @@ checksum = "599b506ccc4aff8cf7844bc42cf783009a434c1e26c964432560fb6d6ad02d82"
dependencies = [
"aes-gcm",
"blake2",
- "chacha20poly1305",
+ "chacha20poly1305 0.10.1",
"curve25519-dalek",
"getrandom 0.3.4",
"ring",
"rustc_version",
- "sha2",
+ "sha2 0.10.9",
"subtle",
]
@@ -9465,7 +9670,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c3d1e2c7f27f8d4cb10542a02c49005dbd6e93095799d6f3be745fae9f8fedd4"
dependencies = [
"libc",
- "windows-sys 0.61.2",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -9540,9 +9745,9 @@ dependencies = [
[[package]]
name = "spki"
-version = "0.7.3"
+version = "0.8.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d91ed6c858b01f942cd56b37a94b3e0a1798290327d1236e4d9cf4eaca44d29d"
+checksum = "1d9efca8738c78ee9484207732f728b1ef517bbb1833d6fc0879ca898a522f6f"
dependencies = [
"base64ct",
"der",
@@ -9609,7 +9814,7 @@ dependencies = [
"rustls",
"serde",
"serde_json",
- "sha2",
+ "sha2 0.10.9",
"smallvec 1.15.1",
"thiserror 2.0.18",
"tokio",
@@ -9647,7 +9852,7 @@ dependencies = [
"quote",
"serde",
"serde_json",
- "sha2",
+ "sha2 0.10.9",
"sqlx-core",
"sqlx-postgres",
"syn 2.0.117",
@@ -9673,7 +9878,7 @@ dependencies = [
"futures-util",
"hex",
"hkdf",
- "hmac",
+ "hmac 0.12.1",
"home",
"itoa",
"log",
@@ -9683,7 +9888,7 @@ dependencies = [
"rand 0.8.6",
"serde",
"serde_json",
- "sha2",
+ "sha2 0.10.9",
"smallvec 1.15.1",
"sqlx-core",
"stringprep",
@@ -10113,7 +10318,22 @@ dependencies = [
"ntapi",
"objc2-core-foundation",
"objc2-io-kit",
- "windows",
+ "windows 0.61.3",
+]
+
+[[package]]
+name = "sysinfo"
+version = "0.39.6"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "d2071df9448915b71c4fe6d25deaf1c22f12bd234f01540b77312bb8e41361e6"
+dependencies = [
+ "libc",
+ "memchr",
+ "ntapi",
+ "objc2-core-foundation",
+ "objc2-io-kit",
+ "objc2-open-directory",
+ "windows 0.62.2",
]
[[package]]
@@ -10184,8 +10404,8 @@ dependencies = [
"tao-macros",
"unicode-segmentation",
"url",
- "windows",
- "windows-core",
+ "windows 0.61.3",
+ "windows-core 0.61.2",
"windows-version",
"x11-dl",
]
@@ -10262,7 +10482,7 @@ dependencies = [
"webkit2gtk",
"webview2-com",
"window-vibrancy",
- "windows",
+ "windows 0.61.3",
]
[[package]]
@@ -10303,7 +10523,7 @@ dependencies = [
"semver",
"serde",
"serde_json",
- "sha2",
+ "sha2 0.10.9",
"syn 2.0.117",
"tauri-utils",
"thiserror 2.0.18",
@@ -10403,7 +10623,7 @@ dependencies = [
"tauri-plugin",
"thiserror 2.0.18",
"url",
- "windows",
+ "windows 0.61.3",
"zbus",
]
@@ -10429,7 +10649,7 @@ dependencies = [
"url",
"webkit2gtk",
"webview2-com",
- "windows",
+ "windows 0.61.3",
]
[[package]]
@@ -10454,7 +10674,7 @@ dependencies = [
"url",
"webkit2gtk",
"webview2-com",
- "windows",
+ "windows 0.61.3",
"wry",
]
@@ -10517,7 +10737,7 @@ dependencies = [
"getrandom 0.4.3",
"once_cell",
"rustix",
- "windows-sys 0.61.2",
+ "windows-sys 0.52.0",
]
[[package]]
@@ -10560,7 +10780,7 @@ dependencies = [
"parking_lot",
"rustix",
"signal-hook",
- "windows-sys 0.61.2",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -10570,7 +10790,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "230a1b821ccbd75b185820a1f1ff7b14d21da1e442e22c0863ea5f08771a8874"
dependencies = [
"rustix",
- "windows-sys 0.61.2",
+ "windows-sys 0.59.0",
]
[[package]]
@@ -10619,7 +10839,7 @@ dependencies = [
"pest",
"pest_derive",
"phf 0.11.3",
- "sha2",
+ "sha2 0.10.9",
"signal-hook",
"siphasher",
"terminfo",
@@ -10644,7 +10864,7 @@ checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057"
dependencies = [
"smawk",
"unicode-linebreak",
- "unicode-width 0.2.0",
+ "unicode-width",
]
[[package]]
@@ -10718,6 +10938,7 @@ checksum = "743bd48c283afc0388f9b8827b976905fb217ad9e647fae3a379a9283c4def2c"
dependencies = [
"deranged",
"itoa",
+ "js-sys",
"libc",
"num-conv",
"num_threads",
@@ -11345,14 +11566,14 @@ dependencies = [
"png 0.18.1",
"serde",
"thiserror 2.0.18",
- "windows-sys 0.61.2",
+ "windows-sys 0.60.2",
]
[[package]]
name = "tree-sitter"
-version = "0.25.10"
+version = "0.26.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "78f873475d258561b06f1c595d93308a7ed124d9977cb26b148c2084a4a3cc87"
+checksum = "83c567a8e18ae93f20982c90370b16fd24023aeaf52f6052b96957ab253a0fec"
dependencies = [
"cc",
"regex",
@@ -11426,7 +11647,7 @@ dependencies = [
"httparse",
"log",
"rand 0.9.4",
- "sha1",
+ "sha1 0.10.6",
"thiserror 2.0.18",
"utf-8",
]
@@ -11443,7 +11664,7 @@ dependencies = [
"httparse",
"log",
"rand 0.9.4",
- "sha1",
+ "sha1 0.10.6",
"thiserror 2.0.18",
]
@@ -11499,7 +11720,7 @@ checksum = "f2f6fb2847f6742cd76af783a2a2c49e9375d0a111c7bef6f71cd9e738c72d6e"
dependencies = [
"memoffset",
"tempfile",
- "windows-sys 0.61.2",
+ "windows-sys 0.60.2",
]
[[package]]
@@ -11659,15 +11880,9 @@ checksum = "16b380a1238663e5f8a691f9039c73e1cdae598a30e9855f541d29b08b53e9a5"
dependencies = [
"itertools 0.14.0",
"unicode-segmentation",
- "unicode-width 0.2.0",
+ "unicode-width",
]
-[[package]]
-name = "unicode-width"
-version = "0.1.14"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "7dd6e30e90baa6f72411720665d41d89b9a3d039dc45b8faea1ddd07f617f6af"
-
[[package]]
name = "unicode-width"
version = "0.2.0"
@@ -11698,10 +11913,20 @@ version = "0.5.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc1de2c688dc15305988b563c3854064043356019f97a4b46276fe734c4f07ea"
dependencies = [
- "crypto-common",
+ "crypto-common 0.1.7",
"subtle",
]
+[[package]]
+name = "universal-hash"
+version = "0.6.1"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f4987bdc12753382e0bec4a65c50738ffaabc998b9cdd1f952fb5f39b0048a96"
+dependencies = [
+ "crypto-common 0.2.2",
+ "ctutils",
+]
+
[[package]]
name = "unsafe-libyaml"
version = "0.2.11"
@@ -11886,12 +12111,12 @@ dependencies = [
"dotenvy",
"futures",
"hex",
- "hmac",
+ "hmac 0.13.0",
"regex",
"reqwest 0.12.28",
"serde",
"serde_json",
- "sha2",
+ "sha2 0.10.9",
"tempfile",
"thiserror 1.0.69",
"tokio",
@@ -11907,12 +12132,12 @@ name = "vibe-broker"
version = "0.5.8"
dependencies = [
"async-trait",
- "base64 0.22.1",
+ "base64 0.23.1",
"chrono",
"cucumber",
"futures",
"hex",
- "hmac",
+ "hmac 0.13.0",
"rcgen",
"reqwest 0.12.28",
"ring",
@@ -11920,7 +12145,7 @@ dependencies = [
"rustls-pemfile",
"serde",
"serde_json",
- "sha2",
+ "sha2 0.10.9",
"tempfile",
"thiserror 1.0.69",
"tokio",
@@ -11938,8 +12163,8 @@ dependencies = [
"anyhow",
"async-trait",
"axum",
- "base64 0.22.1",
- "chacha20poly1305",
+ "base64 0.23.1",
+ "chacha20poly1305 0.11.0",
"chrono",
"dirs",
"futures",
@@ -11952,8 +12177,8 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
- "sha2",
- "sysinfo",
+ "sha2 0.10.9",
+ "sysinfo 0.39.6",
"tauri",
"tauri-build",
"tauri-plugin-dialog",
@@ -12030,7 +12255,7 @@ dependencies = [
name = "vibe-desktop-voice"
version = "0.5.8"
dependencies = [
- "base64 0.22.1",
+ "base64 0.23.1",
"reqwest 0.12.28",
"serde_json",
"tauri",
@@ -12128,7 +12353,7 @@ dependencies = [
"anyhow",
"async-trait",
"bincode",
- "chacha20poly1305",
+ "chacha20poly1305 0.11.0",
"chrono",
"dirs",
"futures",
@@ -12138,7 +12363,7 @@ dependencies = [
"rusqlite",
"serde",
"serde_json",
- "sha2",
+ "sha2 0.10.9",
"tempfile",
"thiserror 1.0.69",
"tokio",
@@ -12153,12 +12378,12 @@ dependencies = [
name = "vibe-profile-store"
version = "0.5.8"
dependencies = [
- "chacha20poly1305",
+ "chacha20poly1305 0.11.0",
"hex",
"rand 0.10.2",
"rusqlite",
"serde_json",
- "sha2",
+ "sha2 0.10.9",
"tempfile",
]
@@ -12236,9 +12461,9 @@ dependencies = [
"anyhow",
"async-trait",
"axum",
- "base64 0.22.1",
+ "base64 0.23.1",
"bytes",
- "chacha20poly1305",
+ "chacha20poly1305 0.11.0",
"chrono",
"clap",
"clap_complete",
@@ -12253,7 +12478,7 @@ dependencies = [
"futures",
"handlebars",
"hex",
- "hmac",
+ "hmac 0.13.0",
"include_dir",
"keyring",
"kodegraph",
@@ -12273,7 +12498,7 @@ dependencies = [
"serde",
"serde_json",
"serde_yaml",
- "sha2",
+ "sha2 0.10.9",
"skilllensai-rs",
"skilloptai-rs",
"snow",
@@ -12284,7 +12509,7 @@ dependencies = [
"thiserror 1.0.69",
"tokio",
"tokio-stream",
- "tokio-tungstenite 0.28.0",
+ "tokio-tungstenite 0.29.0",
"toml 0.8.23",
"tower 0.4.13",
"tower-http 0.7.0",
@@ -12307,7 +12532,7 @@ dependencies = [
"vibe-sandbox-native",
"walkdir",
"whisper-rs",
- "zip 2.4.2",
+ "zip",
]
[[package]]
@@ -12633,7 +12858,7 @@ dependencies = [
"semver",
"serde",
"serde_derive",
- "sha2",
+ "sha2 0.10.9",
"smallvec 1.15.1",
"target-lexicon 0.13.5",
"wasm-encoder 0.252.0",
@@ -12656,7 +12881,7 @@ dependencies = [
"rustix",
"serde",
"serde_derive",
- "sha2",
+ "sha2 0.10.9",
"toml 0.9.12+spec-1.1.0",
"wasmtime-environ",
"windows-sys 0.61.2",
@@ -12808,7 +13033,7 @@ dependencies = [
"bumpalo",
"leb128fmt",
"memchr",
- "unicode-width 0.2.0",
+ "unicode-width",
"wasm-encoder 0.255.0",
]
@@ -12932,8 +13157,8 @@ checksum = "7130243a7a5b33c54a444e54842e6a9e133de08b5ad7b5861cd8ed9a6a5bc96a"
dependencies = [
"webview2-com-macros",
"webview2-com-sys",
- "windows",
- "windows-core",
+ "windows 0.61.3",
+ "windows-core 0.61.2",
"windows-implement",
"windows-interface",
]
@@ -12956,8 +13181,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "381336cfffd772377d291702245447a5251a2ffa5bad679c99e61bc48bacbf9c"
dependencies = [
"thiserror 2.0.18",
- "windows",
- "windows-core",
+ "windows 0.61.3",
+ "windows-core 0.61.2",
]
[[package]]
@@ -12984,7 +13209,7 @@ checksum = "692daff6d93d94e29e4114544ef6d5c942a7ed998b37abdc19b17136ea428eb7"
dependencies = [
"getrandom 0.3.4",
"mac_address",
- "sha2",
+ "sha2 0.10.9",
"thiserror 1.0.69",
"uuid 1.24.0",
]
@@ -13120,7 +13345,7 @@ version = "0.1.11"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22"
dependencies = [
- "windows-sys 0.61.2",
+ "windows-sys 0.48.0",
]
[[package]]
@@ -13150,11 +13375,23 @@ version = "0.61.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9babd3a767a4c1aef6900409f85f5d53ce2544ccdfaa86dad48c91782c6d6893"
dependencies = [
- "windows-collections",
- "windows-core",
- "windows-future",
+ "windows-collections 0.2.0",
+ "windows-core 0.61.2",
+ "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]]
@@ -13163,7 +13400,16 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
dependencies = [
- "windows-core",
+ "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]]
@@ -13179,15 +13425,39 @@ dependencies = [
"windows-strings 0.4.2",
]
+[[package]]
+name = "windows-core"
+version = "0.62.2"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb"
+dependencies = [
+ "windows-implement",
+ "windows-interface",
+ "windows-link 0.2.1",
+ "windows-result 0.4.1",
+ "windows-strings 0.5.1",
+]
+
[[package]]
name = "windows-future"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fc6a41e98427b19fe4b73c550f060b59fa592d7d686537eebf9385621bfbad8e"
dependencies = [
- "windows-core",
+ "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]]
@@ -13243,10 +13513,20 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
- "windows-core",
+ "windows-core 0.61.2",
"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.6.1"
@@ -13420,6 +13700,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"
@@ -13686,6 +13975,17 @@ dependencies = [
"wasmparser 0.252.0",
]
+[[package]]
+name = "wnaf"
+version = "0.14.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ab12e7090f27e2ffd9322651492942d50c2926094af30601e1964337db39daf1"
+dependencies = [
+ "ff",
+ "group",
+ "hybrid-array",
+]
+
[[package]]
name = "writeable"
version = "0.6.3"
@@ -13722,7 +14022,7 @@ dependencies = [
"once_cell",
"percent-encoding",
"raw-window-handle",
- "sha2",
+ "sha2 0.10.9",
"soup3",
"tao-macros",
"thiserror 2.0.18",
@@ -13730,8 +14030,8 @@ dependencies = [
"webkit2gtk",
"webkit2gtk-sys",
"webview2-com",
- "windows",
- "windows-core",
+ "windows 0.61.3",
+ "windows-core 0.61.2",
"windows-version",
"x11-dl",
]
@@ -13775,15 +14075,6 @@ dependencies = [
"time",
]
-[[package]]
-name = "xz2"
-version = "0.1.7"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "388c44dc09d76f1536602ead6d325eb532f5c122f17782bd57fb47baeeb767e2"
-dependencies = [
- "lzma-sys",
-]
-
[[package]]
name = "y4m"
version = "0.8.0"
@@ -13983,20 +14274,6 @@ name = "zeroize"
version = "1.8.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b97154e67e32c85465826e8bcc1c59429aaaf107c1e4a9e53c8d8ccd5eff88d0"
-dependencies = [
- "zeroize_derive",
-]
-
-[[package]]
-name = "zeroize_derive"
-version = "1.4.3"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "85a5b4158499876c763cb03bc4e49185d3cccbabb15b33c627f7884f43db852e"
-dependencies = [
- "proc-macro2",
- "quote",
- "syn 2.0.117",
-]
[[package]]
name = "zerotrie"
@@ -14033,45 +14310,36 @@ dependencies = [
[[package]]
name = "zip"
-version = "2.4.2"
+version = "8.6.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "fabe6324e908f85a1c52063ce7aa26b68dcb7eb6dbc83a2d148403c9bc3eba50"
+checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b"
dependencies = [
- "aes",
- "arbitrary",
+ "aes 0.9.2",
"bzip2",
"constant_time_eq",
"crc32fast",
- "crossbeam-utils",
"deflate64",
- "displaydoc",
"flate2",
- "getrandom 0.3.4",
- "hmac",
+ "getrandom 0.4.3",
+ "hmac 0.13.0",
"indexmap 2.14.0",
- "lzma-rs",
+ "lzma-rust2",
"memchr",
"pbkdf2",
- "sha1",
- "thiserror 2.0.18",
+ "ppmd-rust",
+ "sha1 0.11.0",
"time",
- "xz2",
+ "typed-path",
"zeroize",
"zopfli",
"zstd",
]
[[package]]
-name = "zip"
-version = "8.6.0"
+name = "zlib-rs"
+version = "0.6.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d04a6b5381502aa6087c94c669499eb1602eb9c5e8198e534de571f7154809b"
-dependencies = [
- "crc32fast",
- "indexmap 2.14.0",
- "memchr",
- "typed-path",
-]
+checksum = "34b31d188d9d685a4f9c7b46d6e36631b07058d2cfe190267adce54dc230bf12"
[[package]]
name = "zmij"
diff --git a/Cargo.toml b/Cargo.toml
index 090d36112..ef4d5739f 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -65,7 +65,7 @@ anyhow = "1.0"
thiserror = "1.0"
# Diff
-similar = { version = "2.6", features = ["inline"] }
+similar = { version = "3.1", features = ["inline"] }
# Git
git2 = { version = "0.21.0", features = ["vendored-openssl"] }
@@ -116,12 +116,12 @@ rusqlite = { version = "0.40", features = ["bundled"] }
# Crypto (AWS SigV4 signing)
sha2 = "0.10"
-hmac = "0.12"
-p256 = { version = "0.13", features = ["ecdsa"] }
+hmac = "0.13"
+p256 = { version = "0.14", features = ["ecdsa"] }
hex = "0.4"
# Code knowledge graph (kodegraph) — incremental AST-based symbol/edge graphs
-tree-sitter = "0.25"
+tree-sitter = "0.26"
tree-sitter-rust = "0.24"
tree-sitter-typescript = "0.23"
tree-sitter-python = "0.25"
diff --git a/crates/vibe-desktop-voice/Cargo.toml b/crates/vibe-desktop-voice/Cargo.toml
index c5b9d298b..18f20183f 100644
--- a/crates/vibe-desktop-voice/Cargo.toml
+++ b/crates/vibe-desktop-voice/Cargo.toml
@@ -10,7 +10,7 @@ description = "Voice-input Tauri commands shared by the VibeCody desktop shells
tauri = { version = "2" }
serde_json = { workspace = true }
reqwest = { workspace = true }
-base64 = "0.22"
+base64 = "0.23"
[dev-dependencies]
tokio = { workspace = true }
diff --git a/crates/vibe-profile-store/Cargo.toml b/crates/vibe-profile-store/Cargo.toml
index b886a8c0d..c8d60e93c 100644
--- a/crates/vibe-profile-store/Cargo.toml
+++ b/crates/vibe-profile-store/Cargo.toml
@@ -7,7 +7,7 @@ license.workspace = true
description = "Encrypted profile settings store (~/.vibecli/profile_settings.db) — API keys, provider config, and UI prefs."
[dependencies]
-chacha20poly1305 = "0.10"
+chacha20poly1305 = "0.11"
rand = "0.10"
rusqlite = { workspace = true }
serde_json = { workspace = true }
diff --git a/docs/_config.yml b/docs/_config.yml
index 70c19d778..7eee0243b 100644
--- a/docs/_config.yml
+++ b/docs/_config.yml
@@ -103,6 +103,9 @@ sidebar:
- title: Diffcomplete (⌘.)
icon: edit-3
url: /diffcomplete/
+ - title: 'Ghost text (⌥\)'
+ icon: zap
+ url: /ghost-text/
- title: BugBot
icon: bug
url: /bugbot/
diff --git a/docs/diffcomplete.md b/docs/diffcomplete.md
index 588c9bed8..2e1be928e 100644
--- a/docs/diffcomplete.md
+++ b/docs/diffcomplete.md
@@ -4,9 +4,11 @@ title: Diffcomplete (⌘.)
permalink: /diffcomplete/
---
-> AI editing without ghost-text. Press **⌘.** anywhere in the editor, describe the change you want, review the unified diff hunk-by-hunk, accept or reject what you like.
+> AI editing you review before it lands. Press **⌘.** anywhere in the editor, describe the change you want, review the unified diff hunk-by-hunk, accept or reject what you like.
-Diffcomplete is VibeCody's only AI code-editing surface. It deliberately replaces keystroke-driven inline completions with an explicit-trigger, diff-output flow — better for review and better for safety than FIM / next-edit / ghost-text systems.
+Diffcomplete is VibeCody's AI code-editing surface for changes worth reviewing. It deliberately replaces *keystroke-driven* inline completions with an explicit-trigger, diff-output flow — better for review and better for safety than FIM / next-edit / ghost-text systems.
+
+It is no longer the only AI editing surface: [Ghost text (⌥\)](/ghost-text/) adds a short inline completion at the cursor. Both are explicit-trigger — neither fires while you type — but they differ in output and review: diffcomplete returns a reviewable diff, ghost text returns a suggestion you accept with Tab.
---
@@ -20,7 +22,7 @@ Diffcomplete is VibeCody's only AI code-editing surface. It deliberately replace
6. Per hunk: **Accept** keeps the change, **Reject** drops it, **Edit** lets you tweak the proposed text in place.
7. Click **Apply** to write the accepted hunks back to the file.
-That's it. There is no other AI editing surface in VibeCody. If something looks like inline completion, it isn't — it's a static lint or formatting hint.
+That's it. The only other AI editing surface is [ghost text](/ghost-text/), which you must also press a key to summon. If a suggestion appears that you did *not* ask for, it isn't AI — it's LSP IntelliSense, a static lint, or a formatting hint.
---
@@ -210,12 +212,12 @@ User content (instruction text, file paths) is **not** logged at any level — o
For clarity (and because we removed these on purpose):
-- **Not** keystroke-driven ghost text. There is no `registerInlineCompletionsProvider` path in VibeCody.
+- **Not** ghost text of any kind. VibeCody does register an inline-completions provider (see [ghost text](/ghost-text/)), but it answers only the editor's *explicit* trigger kind — typing never reaches it.
- **Not** FIM (fill-in-middle). The model receives prefix + selection + suffix as discrete labeled regions, not a single FIM template.
- **Not** next-edit prediction. The model only sees the current file state, never your past edits.
- **Not** auto-retrieval. Files in context come from your explicit picker. There is no embedding search, no call-graph walker, no symbol-server probe.
-Diffcomplete is a deliberate, claim-distant alternative to those patterns. If you want a different shape of AI editing, that is a feature request — but the answer will not be re-introducing ghost text.
+Diffcomplete is a deliberate, claim-distant alternative to those patterns. Ghost text was later added back in an explicit-trigger form that keeps the same distinctions — no keystroke trigger, no edit history, no auto-retrieval — while restoring Tab-to-accept for short completions.
---
diff --git a/docs/ghost-text.md b/docs/ghost-text.md
new file mode 100644
index 000000000..5419de4a6
--- /dev/null
+++ b/docs/ghost-text.md
@@ -0,0 +1,114 @@
+---
+layout: page
+title: Ghost text (⌥\)
+permalink: /ghost-text/
+---
+
+> Inline AI completion that only appears when you ask for it. Press **⌥\\** at the cursor, read the grey suggestion, press **Tab** to accept or **Esc** to dismiss.
+
+Ghost text is VibeCody's short-form AI completion. It renders inline at the cursor and accepts with Tab — the ergonomics people expect from a modern editor — but it is **explicit-trigger only**: nothing is requested, and nothing appears, while you type.
+
+For a larger change you want to review hunk-by-hunk, use [Diffcomplete (⌘.)](/diffcomplete/) instead.
+
+---
+
+## Quick start
+
+1. Open a file in **VibeCoder**, or any file in **VS Code** with the VibeCLI extension installed.
+2. Put the cursor where the code should go.
+3. Press **⌥\\** (Alt-Backslash on every platform). Or run **AI: Inline Completion at Cursor** from the command palette.
+4. A grey suggestion appears inline.
+5. **Tab** accepts it. **Esc** dismisses it. Typing anything else dismisses it too.
+
+If nothing appears, the model decided nothing belonged at that cursor — that is a normal answer, not an error.
+
+---
+
+## Why it looks like other tools but isn't
+
+VibeCody removed its previous ghost-text implementation on purpose: that one ran on a debounce timer after every keystroke, kept a rolling buffer of your recent edits, and used them to predict the next one. This one keeps the presentation and drops all of that.
+
+| | Removed (keystroke-driven) | Current (explicit-trigger) |
+|---|---|---|
+| Trigger | debounce timer after each keystroke | **⌥\\ only** |
+| Sees your edit history | yes, a rolling buffer | **no — nothing is retained between requests** |
+| Auto-retrieval of other files | yes, embedding search | **no** |
+| Requests while you type | continuously | **zero** |
+| Accept | Tab | Tab |
+
+The guarantee is one line of code in each editor, and it is the same line conceptually: the inline-completion provider is consulted by the editor constantly, and it returns nothing unless the trigger kind is the explicit one.
+
+> **For contributors:** the enum is inverted between the two hosts. Monaco has `InlineCompletionTriggerKind = { Automatic: 0, Explicit: 1 }`; VS Code has `{ Invoke: 0, Automatic: 1 }`. The same names carry opposite numbers, so a literal comparison copied from one host to the other gates on exactly the wrong half and fires on every keystroke. Always compare against the named member. Each host has its own gate for this reason — `vibecoder/src/lib/ghostText.ts` and `vscode-extension/src/ghost-text.ts`.
+
+---
+
+## What gets sent
+
+Per press, one request carrying:
+
+- **Prefix** — up to 160 lines before the cursor.
+- **Suffix** — up to 60 lines after the cursor.
+- **File path and language**, for the prompt header.
+- **Project memory** — your author-written `VIBECLI.md` / `AGENTS.md` / `CLAUDE.md`, the same audit-restricted source diffcomplete uses. Never auto-extracted state.
+
+Nothing else. No edit history, no telemetry about what you accepted or rejected, no embedding search.
+
+The response is capped at 12 lines (`vibe_ai::ghost::MAX_COMPLETION_LINES`). When the cap clips a suggestion the editor says so — accept what you have and press ⌥\\ again to continue.
+
+---
+
+## Provider and model
+
+Ghost text is provider-agnostic and never defaults to a single vendor.
+
+- **VibeCoder** uses the provider selected in the toolbar, with that provider's registry default model. With no provider selected, ⌥\\ tells you to pick one rather than silently calling anything.
+- **VS Code** uses the `vibecli.provider` and `vibecli.model` settings. The daemon only honours the override when **both** are set — leave `vibecli.model` empty and the daemon uses whichever provider and model it was started with.
+
+---
+
+## Architecture
+
+```
+[ ⌥\ press ]
+ │
+ ▼
+editor.action.inlineSuggest.trigger (host built-in)
+ │
+ ▼
+inline-completion provider ── trigger kind is Automatic? ──► return nothing
+ │
+ │ explicit
+ ▼
+VibeCoder: ghost_complete (Tauri command)
+VS Code: POST /v1/ghost/complete (daemon, bearer auth)
+ │
+ ▼
+vibe_ai::ghost::generate()
+ │
+ ├─► system prompt demanding bare insertion text
+ ├─► prefix / cursor / suffix as labeled regions
+ ├─► project memory as a separate system message
+ │
+ ▼
+sanitize_completion() — unwraps stray code fences, caps at 12 lines,
+ preserves leading indentation
+```
+
+Leading whitespace is deliberately preserved: at a cursor sitting at column 0 of an indented block, the indentation *is* the first thing that belongs there.
+
+---
+
+## What ghost text is NOT
+
+- **Not** keystroke-driven. There is no debounce timer to tune and no on-type path to disable, because none is installed.
+- **Not** FIM. The model receives prefix and suffix as discrete labeled regions, not a single fill-in-middle template.
+- **Not** next-edit prediction. Each request is independent; nothing about your previous edits or previous suggestions is carried forward.
+- **Not** auto-retrieval. No embedding search, no call-graph walk, no symbol-server probe.
+
+---
+
+## Related
+
+- **Source:** [`vibecoder/crates/vibe-ai/src/ghost.rs`](https://github.com/TuringWorks/vibecody/blob/main/vibecoder/crates/vibe-ai/src/ghost.rs) · [`vibecoder/src/lib/ghostText.ts`](https://github.com/TuringWorks/vibecody/blob/main/vibecoder/src/lib/ghostText.ts) · [`vscode-extension/src/ghost-text.ts`](https://github.com/TuringWorks/vibecody/blob/main/vscode-extension/src/ghost-text.ts)
+- **Route:** `POST /v1/ghost/complete` (requires the daemon bearer token)
+- **Companion surface:** [Diffcomplete (⌘.)](/diffcomplete/)
diff --git a/jetbrains-plugin/gradle/wrapper/gradle-wrapper.jar b/jetbrains-plugin/gradle/wrapper/gradle-wrapper.jar
index b1b8ef56b..eddabd2ee 100644
Binary files a/jetbrains-plugin/gradle/wrapper/gradle-wrapper.jar and b/jetbrains-plugin/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/jetbrains-plugin/gradle/wrapper/gradle-wrapper.properties b/jetbrains-plugin/gradle/wrapper/gradle-wrapper.properties
index a351597e6..1e922f407 100644
--- a/jetbrains-plugin/gradle/wrapper/gradle-wrapper.properties
+++ b/jetbrains-plugin/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/vibe-memory/Cargo.toml b/vibe-memory/Cargo.toml
index b70892707..6c2d406f1 100644
--- a/vibe-memory/Cargo.toml
+++ b/vibe-memory/Cargo.toml
@@ -21,7 +21,7 @@ async-trait = "0.1"
rusqlite = { version = "0.40", features = ["bundled"], optional = true }
tokio = { version = "1", features = ["rt-multi-thread", "sync", "fs", "macros"] }
-chacha20poly1305 = { version = "0.10", optional = true }
+chacha20poly1305 = { version = "0.11", optional = true }
getrandom = { version = "0.4", optional = true }
sha2 = "0.10"
diff --git a/vibeaichat/package-lock.json b/vibeaichat/package-lock.json
index 43ab5af78..075b86c29 100644
--- a/vibeaichat/package-lock.json
+++ b/vibeaichat/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "vibeaichat",
- "version": "0.5.7",
+ "version": "0.5.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "vibeaichat",
- "version": "0.5.7",
+ "version": "0.5.8",
"dependencies": {
"@tauri-apps/api": "^2",
"@tauri-apps/plugin-opener": "^2",
@@ -21,8 +21,8 @@
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.4",
"@vitejs/plugin-react": "^6.0.5",
- "typescript": "^5.6.2",
- "vite": "^8.2.0"
+ "typescript": "^7.0.2",
+ "vite": "^8.2.1"
}
},
"node_modules/@oxc-project/types": {
@@ -622,6 +622,346 @@
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
"license": "MIT"
},
+ "node_modules/@typescript/typescript-aix-ppc64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-aix-ppc64/-/typescript-aix-ppc64-7.0.2.tgz",
+ "integrity": "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "aix"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-darwin-arm64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-arm64/-/typescript-darwin-arm64-7.0.2.tgz",
+ "integrity": "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-darwin-x64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-darwin-x64/-/typescript-darwin-x64-7.0.2.tgz",
+ "integrity": "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "darwin"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-freebsd-arm64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-arm64/-/typescript-freebsd-arm64-7.0.2.tgz",
+ "integrity": "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-freebsd-x64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-freebsd-x64/-/typescript-freebsd-x64-7.0.2.tgz",
+ "integrity": "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "freebsd"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-linux-arm": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm/-/typescript-linux-arm-7.0.2.tgz",
+ "integrity": "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==",
+ "cpu": [
+ "arm"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-linux-arm64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-arm64/-/typescript-linux-arm64-7.0.2.tgz",
+ "integrity": "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-linux-loong64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-loong64/-/typescript-linux-loong64-7.0.2.tgz",
+ "integrity": "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==",
+ "cpu": [
+ "loong64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-linux-mips64el": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-mips64el/-/typescript-linux-mips64el-7.0.2.tgz",
+ "integrity": "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==",
+ "cpu": [
+ "mips64el"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-linux-ppc64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-ppc64/-/typescript-linux-ppc64-7.0.2.tgz",
+ "integrity": "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==",
+ "cpu": [
+ "ppc64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-linux-riscv64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-riscv64/-/typescript-linux-riscv64-7.0.2.tgz",
+ "integrity": "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==",
+ "cpu": [
+ "riscv64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-linux-s390x": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-s390x/-/typescript-linux-s390x-7.0.2.tgz",
+ "integrity": "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==",
+ "cpu": [
+ "s390x"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-linux-x64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-linux-x64/-/typescript-linux-x64-7.0.2.tgz",
+ "integrity": "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "linux"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-netbsd-arm64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-arm64/-/typescript-netbsd-arm64-7.0.2.tgz",
+ "integrity": "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-netbsd-x64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-netbsd-x64/-/typescript-netbsd-x64-7.0.2.tgz",
+ "integrity": "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "netbsd"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-openbsd-arm64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-arm64/-/typescript-openbsd-arm64-7.0.2.tgz",
+ "integrity": "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-openbsd-x64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-openbsd-x64/-/typescript-openbsd-x64-7.0.2.tgz",
+ "integrity": "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "openbsd"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-sunos-x64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-sunos-x64/-/typescript-sunos-x64-7.0.2.tgz",
+ "integrity": "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "sunos"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-win32-arm64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-arm64/-/typescript-win32-arm64-7.0.2.tgz",
+ "integrity": "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==",
+ "cpu": [
+ "arm64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
+ "node_modules/@typescript/typescript-win32-x64": {
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/@typescript/typescript-win32-x64/-/typescript-win32-x64-7.0.2.tgz",
+ "integrity": "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==",
+ "cpu": [
+ "x64"
+ ],
+ "dev": true,
+ "license": "Apache-2.0",
+ "optional": true,
+ "os": [
+ "win32"
+ ],
+ "engines": {
+ "node": ">=16.20.0"
+ }
+ },
"node_modules/@ungap/structured-clone": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.3.tgz",
@@ -2452,17 +2792,38 @@
}
},
"node_modules/typescript": {
- "version": "5.9.3",
- "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz",
- "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==",
+ "version": "7.0.2",
+ "resolved": "https://registry.npmjs.org/typescript/-/typescript-7.0.2.tgz",
+ "integrity": "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==",
"dev": true,
"license": "Apache-2.0",
"bin": {
- "tsc": "bin/tsc",
- "tsserver": "bin/tsserver"
+ "tsc": "bin/tsc"
},
"engines": {
- "node": ">=14.17"
+ "node": ">=16.20.0"
+ },
+ "optionalDependencies": {
+ "@typescript/typescript-aix-ppc64": "7.0.2",
+ "@typescript/typescript-darwin-arm64": "7.0.2",
+ "@typescript/typescript-darwin-x64": "7.0.2",
+ "@typescript/typescript-freebsd-arm64": "7.0.2",
+ "@typescript/typescript-freebsd-x64": "7.0.2",
+ "@typescript/typescript-linux-arm": "7.0.2",
+ "@typescript/typescript-linux-arm64": "7.0.2",
+ "@typescript/typescript-linux-loong64": "7.0.2",
+ "@typescript/typescript-linux-mips64el": "7.0.2",
+ "@typescript/typescript-linux-ppc64": "7.0.2",
+ "@typescript/typescript-linux-riscv64": "7.0.2",
+ "@typescript/typescript-linux-s390x": "7.0.2",
+ "@typescript/typescript-linux-x64": "7.0.2",
+ "@typescript/typescript-netbsd-arm64": "7.0.2",
+ "@typescript/typescript-netbsd-x64": "7.0.2",
+ "@typescript/typescript-openbsd-arm64": "7.0.2",
+ "@typescript/typescript-openbsd-x64": "7.0.2",
+ "@typescript/typescript-sunos-x64": "7.0.2",
+ "@typescript/typescript-win32-arm64": "7.0.2",
+ "@typescript/typescript-win32-x64": "7.0.2"
}
},
"node_modules/unified": {
@@ -2581,16 +2942,16 @@
}
},
"node_modules/vite": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.0.tgz",
- "integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==",
+ "version": "8.2.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz",
+ "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==",
"dev": true,
"license": "MIT",
"dependencies": {
"lightningcss": "^1.33.0",
"picomatch": "^4.0.5",
- "postcss": "^8.5.23",
- "rolldown": "~1.2.0",
+ "postcss": "^8.5.25",
+ "rolldown": "~1.2.1",
"tinyglobby": "^0.2.17"
},
"bin": {
diff --git a/vibeaichat/package.json b/vibeaichat/package.json
index c3bba11ab..776fbfcfa 100644
--- a/vibeaichat/package.json
+++ b/vibeaichat/package.json
@@ -26,7 +26,7 @@
"@types/react": "^19.2.18",
"@types/react-dom": "^19.2.4",
"@vitejs/plugin-react": "^6.0.5",
- "typescript": "^5.6.2",
- "vite": "^8.2.0"
+ "typescript": "^7.0.2",
+ "vite": "^8.2.1"
}
}
diff --git a/vibeaichat/src/vite-env.d.ts b/vibeaichat/src/vite-env.d.ts
new file mode 100644
index 000000000..11f02fe2a
--- /dev/null
+++ b/vibeaichat/src/vite-env.d.ts
@@ -0,0 +1 @@
+///
diff --git a/vibecli/crates/vibe-broker/Cargo.toml b/vibecli/crates/vibe-broker/Cargo.toml
index 8884876e7..adec1670d 100644
--- a/vibecli/crates/vibe-broker/Cargo.toml
+++ b/vibecli/crates/vibe-broker/Cargo.toml
@@ -27,7 +27,7 @@ reqwest = { workspace = true }
# RS256 JWT signing. Already in the tree via rcgen + rustls, so this is a
# direct declaration of something we were compiling anyway, not a new dep.
ring = "0.17"
-base64 = "0.22"
+base64 = "0.23"
async-trait = { workspace = true }
[dev-dependencies]
diff --git a/vibecli/vibecli-cli/Cargo.toml b/vibecli/vibecli-cli/Cargo.toml
index 2f8266e1c..953eb6475 100644
--- a/vibecli/vibecli-cli/Cargo.toml
+++ b/vibecli/vibecli-cli/Cargo.toml
@@ -61,10 +61,10 @@ anyhow.workspace = true
serde_json.workspace = true
tracing.workspace = true
tracing-subscriber = { workspace = true, features = ["env-filter"] }
-rustyline = { version = "14.0", features = ["derive"] }
+rustyline = { version = "17.0", features = ["derive"] }
dirs = "6.0"
futures = "0.3"
-tokio-tungstenite = { version = "0.28", features = ["connect"] }
+tokio-tungstenite = { version = "0.29", features = ["connect"] }
crossterm = { workspace = true, features = ["event-stream"] }
serde = { workspace = true, features = ["derive"] }
toml = "0.8"
@@ -130,12 +130,12 @@ subtle = "2.5"
dashmap = "6"
hex.workspace = true
uuid.workspace = true
-base64 = "0.22"
+base64 = "0.23"
keyring = "4"
-chacha20poly1305 = "0.10"
+chacha20poly1305 = "0.11"
chrono = { version = "0.4", features = ["serde"] }
clap_complete = "4"
-zip = "2"
+zip = "8"
handlebars = "6"
socket2 = "0.6"
snow = "0.10"
diff --git a/vibecli/vibecli-cli/src/acp_stdio.rs b/vibecli/vibecli-cli/src/acp_stdio.rs
index bdc666847..e929d40c7 100644
--- a/vibecli/vibecli-cli/src/acp_stdio.rs
+++ b/vibecli/vibecli-cli/src/acp_stdio.rs
@@ -125,7 +125,14 @@ impl AcpResponse {
/// `prompt` slice.
#[derive(Debug, Clone, Default)]
pub struct AcpSession {
+ // Never read today: the session id is also the map key, and `mode` is
+ // written by `setSessionMode`, which is still `MethodNotImplemented`.
+ // Kept as targeted allows rather than a file-level one so genuinely new
+ // dead code still warns — the blanket allow on this module is exactly
+ // what let it sit unreachable for months.
+ #[allow(dead_code)]
pub id: String,
+ #[allow(dead_code)]
pub mode: String,
}
@@ -150,6 +157,7 @@ impl AcpServer {
}
/// Convenience for tests: how many sessions are currently held.
+ #[allow(dead_code)]
pub fn session_count(&self) -> usize {
self.state.lock_recover().sessions.len()
}
diff --git a/vibecli/vibecli-cli/src/serve.rs b/vibecli/vibecli-cli/src/serve.rs
index 45cc56f09..7ca5e16d1 100644
--- a/vibecli/vibecli-cli/src/serve.rs
+++ b/vibecli/vibecli-cli/src/serve.rs
@@ -6725,6 +6725,74 @@ async fn v1_resume_get(Path(handle): Path) -> (StatusCode, Json,
+ #[serde(default)]
+ model: Option,
+}
+
+async fn v1_ghost_complete_post(
+ State(state): State,
+ Json(req): Json,
+) -> (StatusCode, Json) {
+ if req.prefix.trim().is_empty() && req.suffix.trim().is_empty() {
+ return (
+ StatusCode::BAD_REQUEST,
+ Json(serde_json::json!({
+ "error": "prefix and suffix are both empty — nothing to complete"
+ })),
+ );
+ }
+
+ let provider = chat_provider_for(
+ &state.provider,
+ req.provider.as_deref(),
+ req.model.as_deref(),
+ );
+
+ let project_memory = crate::memory::ProjectMemory::load(&state.workspace_root).combined();
+
+ let request = vibe_ai::ghost::GhostRequest {
+ file_path: req.file_path,
+ language: req.language,
+ prefix: req.prefix,
+ suffix: req.suffix,
+ project_memory,
+ };
+
+ match vibe_ai::ghost::generate(provider, request).await {
+ Ok(res) => (
+ StatusCode::OK,
+ Json(serde_json::json!({
+ "completion": res.completion,
+ "model_name": res.model_name,
+ "truncated": res.truncated,
+ })),
+ ),
+ Err(e) => internal_error("ghost.generate", &e),
+ }
+}
+
// ── Recap & Resume — D1.1 /v1/diffcomplete/chains autosave route ───────────
//
// Persistence is *append-on-event* only. The modal posts here when a
@@ -7972,6 +8040,8 @@ pub(crate) fn build_router(state: ServeState, port: u16) -> Router {
// Writes happen only
// on discrete user-driven events posted by the modal.
.route("/v1/diffcomplete/chains", post(v1_diffcomplete_chains_post))
+ // Ghost text — explicit-trigger inline completion (VS Code extension).
+ .route("/v1/ghost/complete", post(v1_ghost_complete_post))
// Mobile Gateway — machine registration & dispatch (iOS/Android remote management)
.route("/mobile/machines", get(mobile_list_machines))
.route("/mobile/machines", post(mobile_register_machine))
@@ -12198,6 +12268,51 @@ mod tests {
// ── Auth: unauthenticated requests to protected routes → 401 ──
+ // ── /v1/ghost/complete ─────────────────────────────────────────
+ //
+ // Stops short of a real model call — that needs a configured provider
+ // a test machine cannot assume. What is tested is everything the
+ // handler decides *before* dispatching: auth and empty-window
+ // rejection.
+
+ #[tokio::test]
+ async fn ghost_complete_without_auth_returns_401() {
+ let (app, _tmp) = test_app("secret-token");
+ let req = Request::builder()
+ .method("POST")
+ .uri("/v1/ghost/complete")
+ .header("content-type", "application/json")
+ .body(Body::from(
+ r#"{"file_path":"a.rs","language":"rust","prefix":"fn f() {","suffix":"}"}"#,
+ ))
+ .unwrap();
+ let resp = app.oneshot(req).await.unwrap();
+ assert_eq!(
+ resp.status(),
+ StatusCode::UNAUTHORIZED,
+ "ghost completion is not in the public route list"
+ );
+ }
+
+ #[tokio::test]
+ async fn ghost_complete_rejects_an_empty_window() {
+ // Both sides blank means there is no cursor context at all; that is
+ // a client bug, and answering it would bill a model call for a
+ // prompt with nothing in it.
+ let (app, _tmp) = test_app("secret-token");
+ let req = Request::builder()
+ .method("POST")
+ .uri("/v1/ghost/complete")
+ .header("authorization", "Bearer secret-token")
+ .header("content-type", "application/json")
+ .body(Body::from(
+ r#"{"file_path":"a.rs","language":"rust","prefix":" ","suffix":""}"#,
+ ))
+ .unwrap();
+ let resp = app.oneshot(req).await.unwrap();
+ assert_eq!(resp.status(), StatusCode::BAD_REQUEST);
+ }
+
// ── /voice/transcribe + /voice/status ──────────────────────────
//
// These stop short of running a real engine: transcription needs either
diff --git a/vibecoder/crates/vibe-ai/src/ghost.rs b/vibecoder/crates/vibe-ai/src/ghost.rs
new file mode 100644
index 000000000..6e6e6cc51
--- /dev/null
+++ b/vibecoder/crates/vibe-ai/src/ghost.rs
@@ -0,0 +1,388 @@
+//! Explicit-trigger inline completion ("ghost text").
+//!
+//! # Why this module is not the surface that was removed
+//!
+//! Commit `5a7eef7c` deleted the previous ghost-text path (`SupercompleteEngine`
+//! + `request_inline_completion` + `predict_next_edit`) after an internal patent
+//! audit flagged it HIGH. That surface was **keystroke-driven**: an edit-history
+//! ring buffer fed a debounced FIM request on every pause in typing, and the
+//! model's output was inserted inline on a single keypress.
+//!
+//! This module deliberately keeps the useful half and drops the flagged half:
+//!
+//! - **Trigger**: explicit chord only. The hosts gate on the editor's own
+//! "explicit" trigger kind (`InlineCompletionTriggerKind::Explicit` in
+//! Monaco, `::Invoke` in VS Code) and return nothing for the automatic kind.
+//! There is no debounce timer and no on-type path to remove, because none
+//! is ever installed.
+//! - **Hidden state**: none. The request carries the prefix/suffix window
+//! around the cursor and nothing else — no edit-event history, no
+//! accepted/rejected telemetry, no automatic embedding retrieval. What the
+//! model sees is what is on screen.
+//! - **Output**: a plain continuation for the current cursor position, capped
+//! to a bounded number of lines.
+//!
+//! The one thing shared with the removed surface is inline presentation and
+//! Tab-to-accept, which the host provides natively.
+//!
+//! [`crate::diffcomplete`] remains the multi-line, review-before-apply surface;
+//! this one is for the short continuation where opening a modal is too heavy.
+
+use crate::provider::{AIProvider, Message, MessageRole};
+use anyhow::Result;
+use serde::{Deserialize, Serialize};
+use std::sync::Arc;
+
+/// How many lines of continuation we ask for, and enforce on the way out.
+///
+/// A cap is part of the design, not a performance tweak: an unbounded
+/// continuation is a code generator, and this surface is deliberately a
+/// completion of the line(s) under the cursor.
+pub const MAX_COMPLETION_LINES: usize = 12;
+
+/// A single explicit-trigger completion request.
+///
+/// `prefix` is the text before the cursor and `suffix` the text after it, each
+/// already windowed by the host. Splitting at the cursor rather than sending
+/// the whole file is what lets the model complete *at* a point instead of
+/// rewriting a region.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct GhostRequest {
+ pub file_path: String,
+ pub language: String,
+ /// Text before the cursor (windowed by the host).
+ pub prefix: String,
+ /// Text after the cursor (windowed by the host).
+ pub suffix: String,
+ /// Author-authored project memory, same audit-restricted source as
+ /// diffcomplete's. **MUST NOT** carry auto-extracted state.
+ #[serde(default)]
+ pub project_memory: Option,
+}
+
+/// A completion, ready for the host to render as ghost text at the cursor.
+#[derive(Debug, Clone, Serialize, Deserialize)]
+pub struct GhostResponse {
+ /// Text to insert verbatim at the cursor. Never empty on `Ok`.
+ pub completion: String,
+ pub model_name: String,
+ /// True when [`MAX_COMPLETION_LINES`] clipped the model's output. The host
+ /// surfaces this rather than pretending the suggestion was complete.
+ pub truncated: bool,
+}
+
+const SYSTEM_PROMPT: &str = "You are a code completion engine. The user gives \
+you a file split at the cursor into a PREFIX and a SUFFIX. Output the text that \
+belongs at the cursor, so that PREFIX + your output + SUFFIX is valid code.\n\
+\n\
+Rules:\n\
+- Output ONLY the insertion text. No prose, no explanation, no commentary.\n\
+- Do NOT repeat any part of the PREFIX or the SUFFIX.\n\
+- Do NOT wrap the output in a markdown code fence.\n\
+- Continue the prefix exactly where it stops, mid-token if that is where the \
+cursor is.\n\
+- Match the surrounding indentation, naming style, and language idiom.\n\
+- Keep it short: complete the current statement, expression, or block. Stop \
+when a reasonable suggestion ends.\n\
+- If nothing sensible belongs at the cursor, output nothing at all.";
+
+/// Build the message list for a request.
+///
+/// Mirrors [`crate::diffcomplete::build_messages`]: canonical system prompt
+/// first, then project memory as its own system message when present, then the
+/// user message. Memory is *context*, never folded into the instruction.
+pub fn build_messages(request: &GhostRequest) -> Vec {
+ let memory = request
+ .project_memory
+ .as_deref()
+ .map(str::trim)
+ .filter(|s| !s.is_empty());
+
+ let mut messages = Vec::with_capacity(if memory.is_some() { 3 } else { 2 });
+ messages.push(Message {
+ role: MessageRole::System,
+ content: SYSTEM_PROMPT.to_string(),
+ });
+ if let Some(mem) = memory {
+ messages.push(Message {
+ role: MessageRole::System,
+ content: format!(
+ "Project memory (author-authored, from VIBECLI.md / AGENTS.md / CLAUDE.md):\n\n{mem}"
+ ),
+ });
+ }
+ messages.push(Message {
+ role: MessageRole::User,
+ content: build_user_prompt(request),
+ });
+ messages
+}
+
+/// Build the user message.
+pub fn build_user_prompt(req: &GhostRequest) -> String {
+ let mut out = String::with_capacity(req.prefix.len() + req.suffix.len() + 256);
+ out.push_str("File: ");
+ out.push_str(&req.file_path);
+ out.push_str("\nLanguage: ");
+ out.push_str(&req.language);
+ out.push_str("\n\n=== PREFIX (text before the cursor) ===\n");
+ out.push_str(&req.prefix);
+ out.push_str("\n=== CURSOR ===\n=== SUFFIX (text after the cursor) ===\n");
+ out.push_str(&req.suffix);
+ out.push_str("\n\nOutput the insertion text for the cursor position:");
+ out
+}
+
+/// Strip the wrappers models add despite being told not to, and enforce the
+/// line cap.
+///
+/// Returns `(completion, truncated)`. An empty completion means the model
+/// declined — that is a valid answer here ("nothing belongs at the cursor"),
+/// and the caller reports it as such rather than as an error.
+///
+/// Trailing whitespace is trimmed but **leading whitespace is preserved**: at a
+/// cursor sitting at column 0 of an indented block, the indentation *is* the
+/// first thing that belongs at the cursor.
+pub fn sanitize_completion(raw: &str) -> (String, bool) {
+ let unfenced = strip_code_fence(raw);
+ let mut lines: Vec<&str> = unfenced.lines().collect();
+
+ // Drop trailing blank lines before counting, so a model that pads its
+ // answer doesn't burn the line budget or trip the truncation flag.
+ while lines.last().is_some_and(|l| l.trim().is_empty()) {
+ lines.pop();
+ }
+
+ let truncated = lines.len() > MAX_COMPLETION_LINES;
+ if truncated {
+ lines.truncate(MAX_COMPLETION_LINES);
+ }
+
+ let joined = lines.join("\n");
+ // `trim_end` only — see the doc comment on leading whitespace.
+ (joined.trim_end().to_string(), truncated)
+}
+
+/// Remove a single wrapping ``` fence if the whole response is one.
+///
+/// Only strips when the response *starts* with a fence, so a completion that
+/// legitimately contains a fence (writing a doc comment, say) is untouched.
+fn strip_code_fence(raw: &str) -> &str {
+ let trimmed = raw.trim_matches('\n');
+ let Some(rest) = trimmed.strip_prefix("```") else {
+ return raw;
+ };
+ // Drop the info string (```rust) on the opening fence.
+ let after_open = match rest.find('\n') {
+ Some(nl) => &rest[nl + 1..],
+ // A fence with no newline has no body.
+ None => return "",
+ };
+ after_open
+ .rfind("```")
+ .map_or(after_open, |close| &after_open[..close])
+}
+
+/// Generate a completion for the cursor position using the supplied provider.
+///
+/// Callers must have established that the user explicitly asked for this — the
+/// hosts do that by gating on the editor's explicit trigger kind. Nothing in
+/// this function can tell an explicit request from an automatic one, so the
+/// gate belongs at the edge and must not be relaxed there.
+pub async fn generate(provider: Arc, request: GhostRequest) -> Result {
+ let provider_name = provider.name().to_string();
+
+ tracing::debug!(
+ target: "vibecody::ghost",
+ provider = %provider_name,
+ language = %request.language,
+ file_path = %request.file_path,
+ prefix_len = request.prefix.len(),
+ suffix_len = request.suffix.len(),
+ "ghost completion requested"
+ );
+
+ if !provider.is_available().await {
+ tracing::warn!(
+ target: "vibecody::ghost",
+ provider = %provider_name,
+ "ghost provider unavailable"
+ );
+ anyhow::bail!("Provider {} is not available", provider_name);
+ }
+
+ let messages = build_messages(&request);
+
+ let raw = provider.chat(&messages, None).await.map_err(|e| {
+ tracing::warn!(
+ target: "vibecody::ghost",
+ provider = %provider_name,
+ error = %e,
+ "ghost provider chat call failed"
+ );
+ e
+ })?;
+
+ let (completion, truncated) = sanitize_completion(&raw);
+
+ tracing::info!(
+ target: "vibecody::ghost",
+ provider = %provider_name,
+ completion_len = completion.len(),
+ truncated,
+ "ghost completion generated"
+ );
+
+ Ok(GhostResponse {
+ completion,
+ model_name: provider_name,
+ truncated,
+ })
+}
+
+#[cfg(test)]
+mod tests {
+ use super::*;
+
+ fn request_stub() -> GhostRequest {
+ GhostRequest {
+ file_path: "src/lib.rs".to_string(),
+ language: "rust".to_string(),
+ prefix: "fn add(a: i32, b: i32) -> i32 {\n ".to_string(),
+ suffix: "\n}\n".to_string(),
+ project_memory: None,
+ }
+ }
+
+ // ── Prompt construction ──────────────────────────────────────────────
+
+ #[test]
+ fn build_messages_emits_only_system_and_user_when_memory_absent() {
+ let msgs = build_messages(&request_stub());
+ assert_eq!(msgs.len(), 2, "no memory → exactly 2 messages");
+ assert_eq!(msgs[0].role, MessageRole::System);
+ assert_eq!(msgs[1].role, MessageRole::User);
+ }
+
+ #[test]
+ fn build_messages_inserts_memory_as_second_system_message() {
+ let req = GhostRequest {
+ project_memory: Some("Always use anyhow::Result.".to_string()),
+ ..request_stub()
+ };
+ let msgs = build_messages(&req);
+ assert_eq!(msgs.len(), 3);
+ assert_eq!(msgs[1].role, MessageRole::System);
+ assert!(msgs[1].content.contains("Always use anyhow::Result."));
+ assert_eq!(msgs[2].role, MessageRole::User);
+ assert!(
+ !msgs[2].content.contains("Always use anyhow::Result."),
+ "memory is context, not instruction — it must not leak into the user message"
+ );
+ }
+
+ #[test]
+ fn whitespace_only_memory_is_treated_as_absent() {
+ let req = GhostRequest {
+ project_memory: Some(" \n ".to_string()),
+ ..request_stub()
+ };
+ assert_eq!(build_messages(&req).len(), 2);
+ }
+
+ #[test]
+ fn user_prompt_splits_at_the_cursor() {
+ let prompt = build_user_prompt(&request_stub());
+ let cursor = prompt.find("=== CURSOR ===").expect("cursor marker present");
+ let prefix_at = prompt.find("fn add(a: i32").expect("prefix present");
+ let suffix_at = prompt.rfind("\n}\n").expect("suffix present");
+ assert!(prefix_at < cursor, "prefix must precede the cursor marker");
+ assert!(suffix_at > cursor, "suffix must follow the cursor marker");
+ }
+
+ // ── Sanitising ───────────────────────────────────────────────────────
+
+ #[test]
+ fn plain_completion_passes_through() {
+ let (out, truncated) = sanitize_completion("a + b");
+ assert_eq!(out, "a + b");
+ assert!(!truncated);
+ }
+
+ #[test]
+ fn leading_indentation_is_preserved() {
+ // The cursor sits at column 0 of an indented block; the indentation is
+ // part of what belongs at the cursor. Trimming it would left-align the
+ // suggestion against its neighbours.
+ let (out, _) = sanitize_completion(" let x = 1;\n x + 1");
+ assert_eq!(out, " let x = 1;\n x + 1");
+ }
+
+ #[test]
+ fn fenced_response_is_unwrapped() {
+ let (out, _) = sanitize_completion("```rust\na + b\n```");
+ assert_eq!(out, "a + b");
+ }
+
+ #[test]
+ fn fenced_response_without_info_string_is_unwrapped() {
+ let (out, _) = sanitize_completion("```\na + b\n```");
+ assert_eq!(out, "a + b");
+ }
+
+ #[test]
+ fn interior_fence_is_left_alone() {
+ // A completion that writes a doc comment containing a fence must not
+ // be mangled — only a fence wrapping the *whole* response is stripped.
+ let raw = "/// ```\n/// let x = 1;\n/// ```\npub fn f() {}";
+ let (out, _) = sanitize_completion(raw);
+ assert_eq!(out, raw);
+ }
+
+ #[test]
+ fn declining_to_complete_yields_empty_not_error() {
+ let (out, truncated) = sanitize_completion(" \n\n ");
+ assert!(out.is_empty(), "an empty answer is a valid 'nothing fits here'");
+ assert!(!truncated);
+ }
+
+ #[test]
+ fn output_is_capped_and_flagged() {
+ let long = (0..MAX_COMPLETION_LINES + 5)
+ .map(|i| format!("line {i}"))
+ .collect::>()
+ .join("\n");
+ let (out, truncated) = sanitize_completion(&long);
+ assert!(truncated, "over-long output must report truncation");
+ assert_eq!(out.lines().count(), MAX_COMPLETION_LINES);
+ }
+
+ #[test]
+ fn trailing_blank_lines_do_not_trip_truncation() {
+ // Exactly at the cap plus padding: the padding is dropped before
+ // counting, so this is not a truncation.
+ let padded = format!(
+ "{}\n\n\n",
+ (0..MAX_COMPLETION_LINES)
+ .map(|i| format!("line {i}"))
+ .collect::>()
+ .join("\n")
+ );
+ let (out, truncated) = sanitize_completion(&padded);
+ assert!(!truncated);
+ assert_eq!(out.lines().count(), MAX_COMPLETION_LINES);
+ }
+
+ #[test]
+ fn unterminated_fence_still_unwraps() {
+ // Streaming responses get cut off mid-fence; the body is still usable.
+ let (out, _) = sanitize_completion("```rust\na + b");
+ assert_eq!(out, "a + b");
+ }
+
+ #[test]
+ fn bare_fence_yields_empty() {
+ let (out, _) = sanitize_completion("```");
+ assert!(out.is_empty());
+ }
+}
diff --git a/vibecoder/crates/vibe-ai/src/lib.rs b/vibecoder/crates/vibe-ai/src/lib.rs
index 19adb1e55..011effccf 100644
--- a/vibecoder/crates/vibe-ai/src/lib.rs
+++ b/vibecoder/crates/vibe-ai/src/lib.rs
@@ -7,6 +7,7 @@ pub mod catalog;
pub mod chat;
pub mod config;
pub mod diffcomplete;
+pub mod ghost;
pub mod hooks;
pub mod mcp;
pub mod multi_agent;
diff --git a/vibecoder/crates/vibe-core/Cargo.toml b/vibecoder/crates/vibe-core/Cargo.toml
index ad58a32be..3a7193232 100644
--- a/vibecoder/crates/vibe-core/Cargo.toml
+++ b/vibecoder/crates/vibe-core/Cargo.toml
@@ -29,7 +29,7 @@ regex = "1"
portable-pty = "0.9"
# Diff
-similar = { version = "2.6", features = ["inline"] }
+similar = { version = "3.1", features = ["inline"] }
# Crypto-safe random (temp file names, etc.)
rand = "0.10"
diff --git a/vibecoder/package-lock.json b/vibecoder/package-lock.json
index 63c07114c..bab25fd86 100644
--- a/vibecoder/package-lock.json
+++ b/vibecoder/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "tauri-app",
- "version": "0.5.7",
+ "version": "0.5.8",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "tauri-app",
- "version": "0.5.7",
+ "version": "0.5.8",
"hasInstallScript": true,
"dependencies": {
"@dagrejs/dagre": "^3.1.0",
@@ -17,7 +17,7 @@
"@xyflow/react": "^12.11.2",
"dompurify": "^3.4.13",
"fuse.js": "^7.5.0",
- "lucide-react": "^1.28.0",
+ "lucide-react": "^1.29.0",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-markdown": "^10.1.0",
@@ -25,8 +25,8 @@
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0",
"y-monaco": "^0.1.6",
- "y-websocket": "^3.0.0",
- "yjs": "^13.6.31"
+ "y-websocket": "^3.1.0",
+ "yjs": "^13.6.32"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
@@ -43,12 +43,12 @@
"eslint": "^10.8.0",
"eslint-plugin-no-unsanitized": "^4.1.2",
"eslint-plugin-react-hooks": "^7.0.1",
- "jsdom": "^29.0.0",
- "monaco-editor": "^0.55.1",
+ "jsdom": "^30.0.1",
+ "monaco-editor": "^0.56.0",
"patch-package": "^8.0.1",
"typescript": "~6.0.3",
"typescript-eslint": "^8.66.0",
- "vite": "^8.2.0",
+ "vite": "^8.2.1",
"vitest": "^4.1.10"
}
},
@@ -60,56 +60,58 @@
"license": "MIT"
},
"node_modules/@asamuzakjp/css-color": {
- "version": "5.1.11",
- "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-5.1.11.tgz",
- "integrity": "sha512-KVw6qIiCTUQhByfTd78h2yD1/00waTmm9uy/R7Ck/ctUyAPj+AEDLkQIdJW0T8+qGgj3j5bpNKK7Q3G+LedJWg==",
+ "version": "6.0.7",
+ "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-6.0.7.tgz",
+ "integrity": "sha512-vC/bk1Lz7Tn/EfU9/apOTBk80/8dyGyWMowPoV1tJ52muDGsDqt2HPT2klrFUiY60MQmQv9q8yIht15JnBgDGw==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@asamuzakjp/generational-cache": "^1.0.1",
- "@csstools/css-calc": "^3.2.0",
- "@csstools/css-color-parser": "^4.1.0",
+ "@csstools/css-calc": "^3.3.0",
+ "@csstools/css-color-parser": "^4.1.10",
"@csstools/css-parser-algorithms": "^4.0.0",
- "@csstools/css-tokenizer": "^4.0.0"
+ "@csstools/css-tokenizer": "^4.0.0",
+ "lru-cache": "^11.5.2"
},
"engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ "node": "^22.13.0 || >=24.0.0"
+ }
+ },
+ "node_modules/@asamuzakjp/css-color/node_modules/lru-cache": {
+ "version": "11.5.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz",
+ "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==",
+ "dev": true,
+ "license": "BlueOak-1.0.0",
+ "engines": {
+ "node": "20 || >=22"
}
},
"node_modules/@asamuzakjp/dom-selector": {
- "version": "7.1.1",
- "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-7.1.1.tgz",
- "integrity": "sha512-67RZDnYRc8H/8MLDgQCDE//zoqVFwajkepHZgmXrbwybzXOEwOWGPYGmALYl9J2DOLfFPPs6kKCqmbzV895hTQ==",
+ "version": "8.3.2",
+ "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-8.3.2.tgz",
+ "integrity": "sha512-93Z1N+BQNXysodoicpOIyNh2drHfz/CTf9nnT0FEx72GJcIiwgydD7tGAr78j41LsYn3hlRn+LdGPuBLn1Bl8Q==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@asamuzakjp/generational-cache": "^1.0.1",
- "@asamuzakjp/nwsapi": "^2.3.9",
"bidi-js": "^1.0.3",
"css-tree": "^3.2.1",
- "is-potential-custom-element-name": "^1.0.1"
+ "is-potential-custom-element-name": "^1.0.1",
+ "lru-cache": "^11.5.2"
},
"engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ "node": "^22.13.0 || >=24.0.0"
}
},
- "node_modules/@asamuzakjp/generational-cache": {
- "version": "1.0.1",
- "resolved": "https://registry.npmjs.org/@asamuzakjp/generational-cache/-/generational-cache-1.0.1.tgz",
- "integrity": "sha512-wajfB8KqzMCN2KGNFdLkReeHncd0AslUSrvHVvvYWuU8ghncRJoA50kT3zP9MVL0+9g4/67H+cdvBskj9THPzg==",
+ "node_modules/@asamuzakjp/dom-selector/node_modules/lru-cache": {
+ "version": "11.5.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz",
+ "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==",
"dev": true,
- "license": "MIT",
+ "license": "BlueOak-1.0.0",
"engines": {
- "node": "^20.19.0 || ^22.12.0 || >=24.0.0"
+ "node": "20 || >=22"
}
},
- "node_modules/@asamuzakjp/nwsapi": {
- "version": "2.3.9",
- "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz",
- "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==",
- "dev": true,
- "license": "MIT"
- },
"node_modules/@babel/code-frame": {
"version": "7.29.7",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz",
@@ -374,9 +376,9 @@
}
},
"node_modules/@csstools/color-helpers": {
- "version": "6.0.2",
- "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.0.2.tgz",
- "integrity": "sha512-LMGQLS9EuADloEFkcTBR3BwV/CGHV7zyDxVRtVDTwdI2Ca4it0CCVTT9wCkxSgokjE5Ho41hEPgb8OEUwoXr6Q==",
+ "version": "6.1.0",
+ "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-6.1.0.tgz",
+ "integrity": "sha512-064IFJdjTfUqnjpCVpMOdbr8FLQBhinbZj6yRv2An2E41O/pLEXqfFRWqGq/SxlE5PEUYTlvWsG2r8MswAVvkg==",
"dev": true,
"funding": [
{
@@ -394,9 +396,9 @@
}
},
"node_modules/@csstools/css-calc": {
- "version": "3.2.1",
- "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.2.1.tgz",
- "integrity": "sha512-DtdHlgXh5ZkA43cwBcAm+huzgJiwx3ZTWVjBs94kwz2xKqSimDA3lBgCjphYgwgVUMWatSM0pDd8TILB1yrVVg==",
+ "version": "3.3.0",
+ "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-3.3.0.tgz",
+ "integrity": "sha512-c5ihYsPkdG6JCkU2zTMm4+k6r7RXuGxtWYhu5DHMIiF1FHzrfmHL5so11AoFpUv/tu61xfcmT4AmKoFfMPoqdQ==",
"dev": true,
"funding": [
{
@@ -418,9 +420,9 @@
}
},
"node_modules/@csstools/css-color-parser": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.1.tgz",
- "integrity": "sha512-eZ5XOtyhK+mggRafYUWzA0tvaYOFgdY8AkgQiCJF9qNAePnUo/zmsqqYubBBb3sQ8uNUaSKTY9s9klfRaAXL0g==",
+ "version": "4.1.10",
+ "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-4.1.10.tgz",
+ "integrity": "sha512-UZhQLIUyJaaMepqehrCODwCg2KW25vFvLWBmqYFaPclYvvxzj/sG8LBOhBFCp11i9uE7t1EyS+RAoV9tztPFyw==",
"dev": true,
"funding": [
{
@@ -434,8 +436,8 @@
],
"license": "MIT",
"dependencies": {
- "@csstools/color-helpers": "^6.0.2",
- "@csstools/css-calc": "^3.2.1"
+ "@csstools/color-helpers": "^6.1.0",
+ "@csstools/css-calc": "^3.3.0"
},
"engines": {
"node": ">=20.19.0"
@@ -469,9 +471,9 @@
}
},
"node_modules/@csstools/css-syntax-patches-for-csstree": {
- "version": "1.1.4",
- "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.4.tgz",
- "integrity": "sha512-wgsqt92b7C7tQhIdPNxj0n9zuUbQlvAuI1exyzeNrOKOi62SD7ren8zqszmpVREjAOqg8cD2FqYhQfAuKjk4sw==",
+ "version": "1.1.7",
+ "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.1.7.tgz",
+ "integrity": "sha512-fQ+05118eQS1cofO3aJpB5efgpBZMvIzwr/sbC8kDLVA5XLG8q1kJV5yzrUAI1f7lvhPnm8fgIjzFB8/O/5Dig==",
"dev": true,
"funding": [
{
@@ -1636,92 +1638,6 @@
"integrity": "sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q==",
"license": "MIT"
},
- "node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.66.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.66.0.tgz",
- "integrity": "sha512-p088eaGrzYz1s+7cov0aMOCkNGTJlVxF4jgubf28c8L0Cv9Rloj8YBHnv4hXLq6IIEE1AsjNWavO+k+8kP2Y0A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/regexpp": "^4.12.2",
- "@typescript-eslint/scope-manager": "8.66.0",
- "@typescript-eslint/type-utils": "8.66.0",
- "@typescript-eslint/utils": "8.66.0",
- "@typescript-eslint/visitor-keys": "8.66.0",
- "ignore": "^7.0.5",
- "natural-compare": "^1.4.0",
- "ts-api-utils": "^2.5.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "@typescript-eslint/parser": "^8.66.0",
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz",
- "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/@typescript-eslint/parser": {
- "version": "8.66.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.66.0.tgz",
- "integrity": "sha512-X6ypGChaWYk6PBtUg2BwuTZEFFcHJAtGTVJ9/lCTOufhZ4i9fNolQNnktq+kkMCwMj7V8Svsq7+TxSDslmhE0g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/scope-manager": "8.66.0",
- "@typescript-eslint/types": "8.66.0",
- "@typescript-eslint/typescript-estree": "8.66.0",
- "@typescript-eslint/visitor-keys": "8.66.0",
- "debug": "^4.4.3"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
- "node_modules/@typescript-eslint/project-service": {
- "version": "8.66.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.66.0.tgz",
- "integrity": "sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.66.0",
- "@typescript-eslint/types": "^8.66.0",
- "debug": "^4.4.3"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.66.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.66.0.tgz",
@@ -1740,48 +1656,6 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
- "node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.66.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.66.0.tgz",
- "integrity": "sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
- "node_modules/@typescript-eslint/type-utils": {
- "version": "8.66.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.66.0.tgz",
- "integrity": "sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.66.0",
- "@typescript-eslint/typescript-estree": "8.66.0",
- "@typescript-eslint/utils": "8.66.0",
- "debug": "^4.4.3",
- "ts-api-utils": "^2.5.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
"node_modules/@typescript-eslint/types": {
"version": "8.66.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.66.0.tgz",
@@ -1796,71 +1670,6 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
- "node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.66.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.66.0.tgz",
- "integrity": "sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/project-service": "8.66.0",
- "@typescript-eslint/tsconfig-utils": "8.66.0",
- "@typescript-eslint/types": "8.66.0",
- "@typescript-eslint/visitor-keys": "8.66.0",
- "debug": "^4.4.3",
- "minimatch": "^10.2.2",
- "semver": "^7.7.3",
- "tinyglobby": "^0.2.15",
- "ts-api-utils": "^2.5.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
- "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": {
- "version": "7.8.5",
- "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
- "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
- "dev": true,
- "license": "ISC",
- "bin": {
- "semver": "bin/semver.js"
- },
- "engines": {
- "node": ">=10"
- }
- },
- "node_modules/@typescript-eslint/utils": {
- "version": "8.66.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.66.0.tgz",
- "integrity": "sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.9.1",
- "@typescript-eslint/scope-manager": "8.66.0",
- "@typescript-eslint/types": "8.66.0",
- "@typescript-eslint/typescript-estree": "8.66.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.66.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.66.0.tgz",
@@ -3611,39 +3420,39 @@
"license": "MIT"
},
"node_modules/jsdom": {
- "version": "29.1.1",
- "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-29.1.1.tgz",
- "integrity": "sha512-ECi4Fi2f7BdJtUKTflYRTiaMxIB0O6zfR1fX0GXpUrf6flp8QIYn1UT20YQqdSOfk2dfkCwS8LAFoJDEppNK5Q==",
+ "version": "30.0.1",
+ "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-30.0.1.tgz",
+ "integrity": "sha512-52v7mUVUfNQVYYqE1lcdaymWL0njO7lTLUog6ZvW2U5KsbiLk/GnZlVJ+qx0xfNJZ6Gn+KSpPNE52vurbxZwrA==",
"dev": true,
"license": "MIT",
"dependencies": {
- "@asamuzakjp/css-color": "^5.1.11",
- "@asamuzakjp/dom-selector": "^7.1.1",
+ "@asamuzakjp/css-color": "^6.0.5",
+ "@asamuzakjp/dom-selector": "^8.3.0",
"@bramus/specificity": "^2.4.2",
- "@csstools/css-syntax-patches-for-csstree": "^1.1.3",
- "@exodus/bytes": "^1.15.0",
+ "@csstools/css-syntax-patches-for-csstree": "^1.1.7",
+ "@exodus/bytes": "^1.15.1",
"css-tree": "^3.2.1",
"data-urls": "^7.0.0",
"decimal.js": "^10.6.0",
"html-encoding-sniffer": "^6.0.0",
"is-potential-custom-element-name": "^1.0.1",
- "lru-cache": "^11.3.5",
+ "lru-cache": "^11.5.2",
"parse5": "^8.0.1",
"saxes": "^6.0.0",
"symbol-tree": "^3.2.4",
- "tough-cookie": "^6.0.1",
- "undici": "^7.25.0",
+ "tough-cookie": "^6.0.2",
+ "undici": "^8.9.0",
"w3c-xmlserializer": "^5.0.0",
"webidl-conversions": "^8.0.1",
"whatwg-mimetype": "^5.0.0",
- "whatwg-url": "^16.0.1",
+ "whatwg-url": "^17.1.0",
"xml-name-validator": "^5.0.0"
},
"engines": {
- "node": "^20.19.0 || ^22.13.0 || >=24.0.0"
+ "node": "^22.22.2 || ^24.15.0 || >=26.0.0"
},
"peerDependencies": {
- "canvas": "^3.0.0"
+ "canvas": "^3.2.3"
},
"peerDependenciesMeta": {
"canvas": {
@@ -3652,15 +3461,30 @@
}
},
"node_modules/jsdom/node_modules/lru-cache": {
- "version": "11.5.1",
- "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.1.tgz",
- "integrity": "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A==",
+ "version": "11.5.2",
+ "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.2.tgz",
+ "integrity": "sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==",
"dev": true,
"license": "BlueOak-1.0.0",
"engines": {
"node": "20 || >=22"
}
},
+ "node_modules/jsdom/node_modules/whatwg-url": {
+ "version": "17.1.0",
+ "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-17.1.0.tgz",
+ "integrity": "sha512-3GeworPmc2ZfEEHP7lEbUfBX/L75wdEsi0rLNhXcXxnoN5jyq0SL5gCy06SGW2cyTIZdTvWIDQNQoza++vKeaw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@exodus/bytes": "^1.15.1",
+ "tr46": "^6.0.0",
+ "webidl-conversions": "^8.0.1"
+ },
+ "engines": {
+ "node": "^22.14.0 || >=24.0.0"
+ }
+ },
"node_modules/jsesc": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz",
@@ -4116,9 +3940,9 @@
}
},
"node_modules/lucide-react": {
- "version": "1.28.0",
- "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.28.0.tgz",
- "integrity": "sha512-fARAFJULsGuDDydjp6+6blekG/sBIM29TerzLjc9bQUKAcEfrSc4ZQKb25KRz4OMKd87cZTb5dgq0w/T6KufVg==",
+ "version": "1.31.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.31.0.tgz",
+ "integrity": "sha512-G8u2eEtoHUnUa9f8lbvqDhCiORMnYLdUEo06EEG9MQvHQrInKcX3Pa2TH39MM5qyzRcWETxB0+aOwAPI1g1kEg==",
"license": "ISC",
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
@@ -5079,12 +4903,12 @@
}
},
"node_modules/monaco-editor": {
- "version": "0.55.1",
- "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.55.1.tgz",
- "integrity": "sha512-jz4x+TJNFHwHtwuV9vA9rMujcZRb0CEilTEwG2rRSpe/A7Jdkuj8xPKttCgOh+v/lkHy7HsZ64oj+q3xoAFl9A==",
+ "version": "0.56.0",
+ "resolved": "https://registry.npmjs.org/monaco-editor/-/monaco-editor-0.56.0.tgz",
+ "integrity": "sha512-sXboRm3BeBeLm938eaiyLMe0OxzfXIlZvbv4ir/jVgQy1zDhWjgmny0WoN45fuDKhCCQsYMbBJrv/A6jd8aCUg==",
"license": "MIT",
"dependencies": {
- "dompurify": "3.2.7",
+ "dompurify": "3.4.8",
"marked": "14.0.0"
}
},
@@ -5911,22 +5735,22 @@
}
},
"node_modules/tldts": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.0.tgz",
- "integrity": "sha512-yHBe+zVfzNZ3QfTPW/Z6KK1G2t340gFjMHqI/4KKSt/abzYydzuCnpqdaF5gCCABby+9Yfbj59oR5F2Fd5CBzg==",
+ "version": "7.4.10",
+ "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.4.10.tgz",
+ "integrity": "sha512-GgouD1B+sWwvkaEq8vXC15DjQitxbvs12oIXELpconwm+Tg3zfcEv4jgzq3vtKverDXsg3VI8aRgNL2Nra0Iog==",
"dev": true,
"license": "MIT",
"dependencies": {
- "tldts-core": "^7.4.0"
+ "tldts-core": "^7.4.10"
},
"bin": {
"tldts": "bin/cli.js"
}
},
"node_modules/tldts-core": {
- "version": "7.4.0",
- "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.0.tgz",
- "integrity": "sha512-/mb9kRld+x1sIMXxWNOAp5m6C+D4GrAORWlJkOJ5dElvxdN1eutz/o7qHLp9gFvDF4Y3/L2xeScoxz6AbEo8rQ==",
+ "version": "7.4.10",
+ "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.4.10.tgz",
+ "integrity": "sha512-KnQjp53ZekKgm/r3l+u8kJGGzYgrWdP8+Mql7a4vijh2WE0IrZWspQj/TpTxDho/YxO+AnOZnIjQcCD+q6iJsw==",
"dev": true,
"license": "MIT"
},
@@ -5954,9 +5778,9 @@
}
},
"node_modules/tough-cookie": {
- "version": "6.0.1",
- "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.1.tgz",
- "integrity": "sha512-LktZQb3IeoUWB9lqR5EWTHgW/VTITCXg4D21M+lvybRVdylLrRMnqaIONLVb5mav8vM19m44HIcGq4qASeu2Qw==",
+ "version": "6.0.2",
+ "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.2.tgz",
+ "integrity": "sha512-exgYmnmL/sJpR3upZfXG5PoatXQii55xAiXGXzY+sROLZ/Y+SLcp9PgJNI9Vz37HpQ74WvDcLT8eqm+kV3FzrA==",
"dev": true,
"license": "BSD-3-Clause",
"dependencies": {
@@ -6063,14 +5887,207 @@
"typescript": ">=4.8.4 <6.1.0"
}
},
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.66.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.66.0.tgz",
+ "integrity": "sha512-p088eaGrzYz1s+7cov0aMOCkNGTJlVxF4jgubf28c8L0Cv9Rloj8YBHnv4hXLq6IIEE1AsjNWavO+k+8kP2Y0A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.66.0",
+ "@typescript-eslint/type-utils": "8.66.0",
+ "@typescript-eslint/utils": "8.66.0",
+ "@typescript-eslint/visitor-keys": "8.66.0",
+ "ignore": "^7.0.5",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.66.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": {
+ "version": "8.66.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.66.0.tgz",
+ "integrity": "sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.66.0",
+ "@typescript-eslint/typescript-estree": "8.66.0",
+ "@typescript-eslint/utils": "8.66.0",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": {
+ "version": "8.66.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.66.0.tgz",
+ "integrity": "sha512-X6ypGChaWYk6PBtUg2BwuTZEFFcHJAtGTVJ9/lCTOufhZ4i9fNolQNnktq+kkMCwMj7V8Svsq7+TxSDslmhE0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.66.0",
+ "@typescript-eslint/types": "8.66.0",
+ "@typescript-eslint/typescript-estree": "8.66.0",
+ "@typescript-eslint/visitor-keys": "8.66.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.66.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.66.0.tgz",
+ "integrity": "sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.66.0",
+ "@typescript-eslint/tsconfig-utils": "8.66.0",
+ "@typescript-eslint/types": "8.66.0",
+ "@typescript-eslint/visitor-keys": "8.66.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": {
+ "version": "8.66.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.66.0.tgz",
+ "integrity": "sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.66.0",
+ "@typescript-eslint/types": "^8.66.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.66.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.66.0.tgz",
+ "integrity": "sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": {
+ "version": "8.66.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.66.0.tgz",
+ "integrity": "sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.66.0",
+ "@typescript-eslint/types": "8.66.0",
+ "@typescript-eslint/typescript-estree": "8.66.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/ignore": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz",
+ "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/semver": {
+ "version": "7.8.5",
+ "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz",
+ "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==",
+ "dev": true,
+ "license": "ISC",
+ "bin": {
+ "semver": "bin/semver.js"
+ },
+ "engines": {
+ "node": ">=10"
+ }
+ },
"node_modules/undici": {
- "version": "7.29.0",
- "resolved": "https://registry.npmjs.org/undici/-/undici-7.29.0.tgz",
- "integrity": "sha512-IDxfleLmmbSskfWSUATiN1nfn2rDuvnMOqb5CWR92iIfojA0Ud+ulOAAEQ57LPr9rWmsreUyf5lwyao+7GNNVw==",
+ "version": "8.10.0",
+ "resolved": "https://registry.npmjs.org/undici/-/undici-8.10.0.tgz",
+ "integrity": "sha512-HvltHd7avK13QIw/oLe4qoOLyoVSoafqJ2jYOrtMRBkbYT31eiBQ8O0ehRKZiEZCMEyLFQNIADpgCWC5fALvYQ==",
"dev": true,
"license": "MIT",
"engines": {
- "node": ">=20.18.1"
+ "node": ">=22.19.0"
}
},
"node_modules/undici-types": {
@@ -6256,16 +6273,16 @@
}
},
"node_modules/vite": {
- "version": "8.2.0",
- "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.0.tgz",
- "integrity": "sha512-pn+CFpM0lwDeKwmOq1ZaBK/9sjorZcgqxki6MbY/jPEVd9vichIlmlD4HmQ5wdP5EgqQCFRaACBxMC7uEGc6lQ==",
+ "version": "8.2.1",
+ "resolved": "https://registry.npmjs.org/vite/-/vite-8.2.1.tgz",
+ "integrity": "sha512-EU/eS7BH3XROHh2YnBefjM6DBKA6ZeMZEYQbj7NLWg5wHYlhB8B/Mayd5XsgWq+NFYccDOTemRpdETWR6Ka/lw==",
"dev": true,
"license": "MIT",
"dependencies": {
"lightningcss": "^1.33.0",
"picomatch": "^4.0.5",
- "postcss": "^8.5.23",
- "rolldown": "~1.2.0",
+ "postcss": "^8.5.25",
+ "rolldown": "~1.2.1",
"tinyglobby": "^0.2.17"
},
"bin": {
@@ -6627,9 +6644,9 @@
}
},
"node_modules/y-websocket": {
- "version": "3.0.0",
- "resolved": "https://registry.npmjs.org/y-websocket/-/y-websocket-3.0.0.tgz",
- "integrity": "sha512-mUHy7AzkOZ834T/7piqtlA8Yk6AchqKqcrCXjKW8J1w2lPtRDjz8W5/CvXz9higKAHgKRKqpI3T33YkRFLkPtg==",
+ "version": "3.1.0",
+ "resolved": "https://registry.npmjs.org/y-websocket/-/y-websocket-3.1.0.tgz",
+ "integrity": "sha512-ZNzwH84Ysxv7zjpFNZHjTJvrBZgcAqMljTe+6zrWciAML9LQ18aVylyPNH9faxCXqEOV8I0JY4TGtrIHFX+Xwg==",
"license": "MIT",
"dependencies": {
"lib0": "^0.2.102",
@@ -6671,9 +6688,9 @@
}
},
"node_modules/yjs": {
- "version": "13.6.31",
- "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.31.tgz",
- "integrity": "sha512-Eq+5BRfbeGyqGVrTJL3bEcr8gKkxPuyuoHmAwpk52fDb8kOVMrfVSTRPd6yiGgX5Fskb96qCRjzjbRjrL4YEnw==",
+ "version": "13.6.32",
+ "resolved": "https://registry.npmjs.org/yjs/-/yjs-13.6.32.tgz",
+ "integrity": "sha512-lfiJIIC4Xayt5ItynE407ehlE03pCjeOc4hkR4yxxvvNJ4kuiN25B0g+Qp8XagYz361LLL7DCzR5bvFJ81QKtQ==",
"license": "MIT",
"dependencies": {
"lib0": "^0.2.99"
diff --git a/vibecoder/package.json b/vibecoder/package.json
index 71327e893..9e52cc3a7 100644
--- a/vibecoder/package.json
+++ b/vibecoder/package.json
@@ -27,7 +27,7 @@
"@xyflow/react": "^12.11.2",
"dompurify": "^3.4.13",
"fuse.js": "^7.5.0",
- "lucide-react": "^1.28.0",
+ "lucide-react": "^1.29.0",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-markdown": "^10.1.0",
@@ -35,8 +35,8 @@
"xterm": "^5.3.0",
"xterm-addon-fit": "^0.8.0",
"y-monaco": "^0.1.6",
- "y-websocket": "^3.0.0",
- "yjs": "^13.6.31"
+ "y-websocket": "^3.1.0",
+ "yjs": "^13.6.32"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
@@ -53,12 +53,12 @@
"eslint": "^10.8.0",
"eslint-plugin-no-unsanitized": "^4.1.2",
"eslint-plugin-react-hooks": "^7.0.1",
- "jsdom": "^29.0.0",
- "monaco-editor": "^0.55.1",
+ "jsdom": "^30.0.1",
+ "monaco-editor": "^0.56.0",
"patch-package": "^8.0.1",
"typescript": "~6.0.3",
"typescript-eslint": "^8.66.0",
- "vite": "^8.2.0",
+ "vite": "^8.2.1",
"vitest": "^4.1.10"
},
"overrides": {
diff --git a/vibecoder/src-tauri/Cargo.toml b/vibecoder/src-tauri/Cargo.toml
index a511c20a1..ee0023130 100644
--- a/vibecoder/src-tauri/Cargo.toml
+++ b/vibecoder/src-tauri/Cargo.toml
@@ -53,13 +53,13 @@ vibe-memory = { path = "../../vibe-memory" }
vibecli_cli = { package = "vibecli", path = "../../vibecli/vibecli-cli" }
# Voice input — the same daemon bridge VibeDesk and VibeAIChat use.
vibe-desktop-voice = { path = "../../crates/vibe-desktop-voice" }
-base64 = "0.22.1"
+base64 = "0.23.1"
chrono = "0.4.44"
rusqlite = { version = "0.40", features = ["bundled"] }
-chacha20poly1305 = "0.10"
+chacha20poly1305 = "0.11"
hostname = "0.4"
sha2 = "0.10"
-sysinfo = "0.36"
+sysinfo = "0.39"
tracing = "0.1"
[dev-dependencies]
diff --git a/vibecoder/src-tauri/src/commands.rs b/vibecoder/src-tauri/src/commands.rs
index 4c4527d63..bed6b544a 100644
--- a/vibecoder/src-tauri/src/commands.rs
+++ b/vibecoder/src-tauri/src/commands.rs
@@ -8508,6 +8508,7 @@ pub async fn diffcomplete_generate(
after_context: String,
instruction: String,
provider: String,
+ model: Option,
additional_files: Option>,
previous_diff: Option,
refinement: Option,
@@ -8535,16 +8536,7 @@ pub async fn diffcomplete_generate(
project_memory,
};
- let active = {
- let mut chat_engine = state.chat_engine.lock().await;
- if !provider.is_empty() {
- let _ = chat_engine.set_provider_by_name(&provider);
- }
- chat_engine
- .active_provider()
- .cloned()
- .ok_or_else(|| "No active AI provider configured".to_string())?
- };
+ let active = resolve_editing_provider(&state, &provider, model.as_deref()).await?;
let response = vibe_ai::diffcomplete::generate(active, request)
.await
@@ -8557,6 +8549,89 @@ pub async fn diffcomplete_generate(
})
}
+/// Resolve the provider for an explicit-trigger editing surface (⌘. and ghost
+/// text) from the toolbar's `provider` + `model` selection.
+///
+/// Built per request via [`build_temp_provider`] rather than by calling
+/// `set_provider_by_name` on the shared chat engine. The old path had two
+/// faults: it dropped `model` entirely, so the toolbar's model selection never
+/// reached the request, and `set_provider_by_name` mutates engine state — one
+/// ⌘. with a different provider silently re-pointed the whole chat session.
+///
+/// Falls back to the engine's active provider only when the caller sends no
+/// selection at all, which is the "no model selected" case the hosts already
+/// guard against.
+async fn resolve_editing_provider(
+ state: &tauri::State<'_, AppState>,
+ provider: &str,
+ model: Option<&str>,
+) -> Result, String> {
+ if !provider.is_empty() {
+ if let Some(model) = model.map(str::trim).filter(|m| !m.is_empty()) {
+ return build_temp_provider(provider, model).ok_or_else(|| {
+ format!("No API key configured for provider '{provider}'")
+ });
+ }
+ }
+
+ let chat_engine = state.chat_engine.lock().await;
+ chat_engine
+ .active_provider()
+ .cloned()
+ .ok_or_else(|| "No active AI provider configured".to_string())
+}
+
+// ── Ghost text — explicit-trigger inline completion ──────────────────────────
+
+/// Response for [`ghost_complete`].
+#[derive(serde::Serialize)]
+pub struct GhostResponseDto {
+ pub completion: String,
+ pub model_name: String,
+ pub truncated: bool,
+}
+
+/// Generate an inline completion for the cursor position.
+///
+/// **This command must only be invoked from an explicit user gesture.** It has
+/// no way to tell an explicit request from an automatic one; the gate lives in
+/// the host's inline-completion provider, which returns early for the editor's
+/// automatic trigger kind. See `vibe_ai::ghost` for why that boundary matters.
+#[tauri::command]
+pub async fn ghost_complete(
+ state: tauri::State<'_, AppState>,
+ file_path: String,
+ language: String,
+ prefix: String,
+ suffix: String,
+ provider: String,
+ model: Option,
+) -> Result {
+ let project_memory = read_active_workspace_path()
+ .as_deref()
+ .and_then(build_diffcomplete_project_memory);
+
+ let request = vibe_ai::ghost::GhostRequest {
+ file_path,
+ language,
+ prefix,
+ suffix,
+ project_memory,
+ };
+
+ let active = resolve_editing_provider(&state, &provider, model.as_deref()).await?;
+
+ let response = vibe_ai::ghost::generate(active, request)
+ .await
+ .map_err(|e| e.to_string())?;
+
+ Ok(GhostResponseDto {
+ completion: response.completion,
+ model_name: response.model_name,
+ truncated: response.truncated,
+ })
+}
+
// ─── Build System Commands ───────────────────────────────────────────────────
#[derive(Debug, Clone, Serialize, Deserialize)]
diff --git a/vibecoder/src-tauri/src/lib.rs b/vibecoder/src-tauri/src/lib.rs
index d347ae891..cc6594971 100644
--- a/vibecoder/src-tauri/src/lib.rs
+++ b/vibecoder/src-tauri/src/lib.rs
@@ -510,6 +510,7 @@ pub fn run() {
commands::get_sandbox_status,
commands::set_sandbox_enabled,
commands::diffcomplete_generate,
+ commands::ghost_complete,
// Build System commands
commands::list_workspace_subdirs,
commands::detect_build_system,
diff --git a/vibecoder/src/App.tsx b/vibecoder/src/App.tsx
index 1547cfbf0..42f226dc8 100644
--- a/vibecoder/src/App.tsx
+++ b/vibecoder/src/App.tsx
@@ -3,7 +3,8 @@ import { useToast } from "./hooks/useToast";
import { useNotifications } from "./hooks/useNotifications";
import { useApiKeyMonitor } from "./hooks/useApiKeyMonitor";
import { useDaemonMonitor } from "./hooks/useDaemonMonitor";
-import { probeAndCacheDefaultProvider } from "./hooks/useModelRegistry";
+import { probeAndCacheDefaultProvider, PROVIDER_DEFAULT_MODEL } from "./hooks/useModelRegistry";
+import { registerGhostText, type GhostTextHandle } from "./lib/ghostText";
import { Toaster } from "./components/Toaster";
import { NotificationCenter } from "./components/NotificationCenter";
import Editor, { DiffEditor, OnMount } from "@monaco-editor/react";
@@ -191,6 +192,14 @@ function App() {
selectedProviderRef.current = selectedProvider;
}, [selectedProvider]);
+ // The toolbar selects a provider; the model is that provider's registry
+ // default, the same resolution every other panel uses. Sent explicitly with
+ // each AI-editing request so the backend never has to guess one — and never
+ // re-points the shared chat engine to find out.
+ const selectedModel = PROVIDER_DEFAULT_MODEL[selectedProvider] ?? "";
+ const selectedModelRef = useRef(selectedModel);
+ selectedModelRef.current = selectedModel;
+
// Listen for file-tree refresh requests from child panels (e.g. Screenshot to App)
useEffect(() => {
const handler = () => { if (currentDirectory) loadDirectory(currentDirectory); };
@@ -658,6 +667,7 @@ function App() {
activeFilePathRef.current = activeFilePath;
const lspBridgeRef = useRef(null);
const lspNoticesRef = useRef(new Set());
+ const ghostTextRef = useRef(null);
/** Tell the user once per language why IntelliSense is quiet. */
const reportLspUnavailable = useCallback((support: LspLanguageSupport) => {
@@ -685,6 +695,10 @@ function App() {
useEffect(() => () => {
lspBridgeRef.current?.dispose();
lspBridgeRef.current = null;
+ // Registered on "*", so leaking it would leave a dead provider consulted
+ // on every keystroke for the rest of the process.
+ ghostTextRef.current?.dispose();
+ ghostTextRef.current = null;
}, []);
// Now that each file gets its own Monaco model (see the editor's `path`
@@ -736,6 +750,28 @@ function App() {
}
);
+ // ── Ctrl+Space (⌥\ on mac too): ghost text ──
+ // The provider answers only Monaco's `Explicit` trigger kind, so this
+ // chord is the sole path to a suggestion — typing never produces one.
+ // See `lib/ghostText.ts` for why that gate is the whole design.
+ const ghost = registerGhostText(monaco, {
+ invoke: (command: string, args?: Record) =>
+ invoke(command, args),
+ getProvider: () => selectedProviderRef.current,
+ getModel: () => selectedModelRef.current,
+ getFilePath: () => activeFilePathRef.current ?? "",
+ onError: (message) => toast.warn(message),
+ // The cap lives in `vibe_ai::ghost`; don't restate the number here, it
+ // would go stale silently. The backend reports *that* it clipped.
+ onTruncated: () =>
+ toast.info("Suggestion was clipped — accept it and re-trigger for more."),
+ });
+ ghostTextRef.current = ghost;
+ editor.addCommand(
+ monaco.KeyMod.Alt | monaco.KeyCode.Backslash,
+ () => ghost.trigger(editor),
+ );
+
// IntelliSense: completion, hover, go-to-definition, signature help and
// diagnostics, all driven from `lib/lsp.ts`. Providers are registered
// lazily, per language, the first time a file of that language is opened —
@@ -1017,6 +1053,19 @@ function App() {
action: handleNewFolder,
},
// Editor actions
+ {
+ id: 'editor.ghostText',
+ label: 'AI: Inline Completion at Cursor',
+ category: 'Editor',
+ icon: ,
+ shortcut: isMac ? '⌥\\' : 'Alt+\\',
+ action: () => {
+ const editor = editorRef.current;
+ if (!editor) return;
+ editor.focus();
+ ghostTextRef.current?.trigger(editor);
+ },
+ },
{
id: 'editor.toggleSidebar',
label: 'Toggle Sidebar',
@@ -2135,6 +2184,11 @@ function App() {
suggestOnTriggerCharacters: true,
parameterHints: { enabled: true },
tabCompletion: "on",
+ // Renders ghost text and binds Tab to accept it. The
+ // widget being enabled does NOT mean suggestions are
+ // requested while typing — the provider answers only
+ // the explicit trigger kind (see lib/ghostText.ts).
+ inlineSuggest: { enabled: true },
}}
/>
)
@@ -2159,6 +2213,7 @@ function App() {
const shift = isMac ? '⇧' : 'Shift+';
return [
[`${mod}.`, 'AI Edit (DiffComplete)'],
+ [isMac ? '⌥\\' : 'Alt+\\', 'AI Inline Completion'],
[`${mod}K`, 'Command Palette'],
[`${mod}${shift}P`, 'Command Palette'],
[`${mod}J`, 'Toggle AI Panel'],
@@ -2478,6 +2533,7 @@ function App() {
selectionStartLine={diffComplete.selectionStartLine}
selectionEndLine={diffComplete.selectionEndLine}
provider={selectedProvider}
+ model={selectedModel}
onApply={(modified) => {
if (modified === null) return;
const editor = editorRef.current;
diff --git a/vibecoder/src/components/DiffCompleteModal.tsx b/vibecoder/src/components/DiffCompleteModal.tsx
index 3a0679819..ccf531333 100644
--- a/vibecoder/src/components/DiffCompleteModal.tsx
+++ b/vibecoder/src/components/DiffCompleteModal.tsx
@@ -130,6 +130,13 @@ export interface DiffCompleteModalProps {
selectionEndLine: number;
/** Active provider id (e.g. "claude", "openai"). */
provider: string;
+ /**
+ * Model id for `provider`. Sent with the request so the backend uses the
+ * host's selection instead of re-pointing the shared chat engine to find a
+ * provider — which used to switch the model for the whole chat session as a
+ * side effect of a ⌘. press.
+ */
+ model: string;
/** Called with the modified file content on apply; null means cancelled. */
onApply: (modified: string | null) => void;
}
@@ -145,7 +152,7 @@ type ProviderStatus = "unknown" | "ready" | "no_providers";
export function DiffCompleteModal(props: DiffCompleteModalProps) {
const { open, onClose, filePath, language, originalContent, selectionText,
- selectionStartLine, selectionEndLine, provider, onApply } = props;
+ selectionStartLine, selectionEndLine, provider, model, onApply } = props;
const [phase, setPhase] = useState("prompt");
const [instruction, setInstruction] = useState("");
@@ -305,6 +312,7 @@ export function DiffCompleteModal(props: DiffCompleteModalProps) {
afterContext,
instruction: instruction.trim(),
provider,
+ model,
additionalFiles: additionalFiles.length ? additionalFiles : null,
previousDiff: opts.previousDiff && opts.previousDiff.length ? opts.previousDiff : null,
refinement: opts.refinement && opts.refinement.trim().length ? opts.refinement.trim() : null,
diff --git a/vibecoder/src/components/__tests__/DiffCompleteModal.test.tsx b/vibecoder/src/components/__tests__/DiffCompleteModal.test.tsx
index bad51407a..7cc431582 100644
--- a/vibecoder/src/components/__tests__/DiffCompleteModal.test.tsx
+++ b/vibecoder/src/components/__tests__/DiffCompleteModal.test.tsx
@@ -296,6 +296,7 @@ describe('DiffCompleteModal — flow', () => {
selectionStartLine: 0,
selectionEndLine: 0,
provider: "mock",
+ model: "mock-model",
onApply: vi.fn(),
};
@@ -337,6 +338,11 @@ describe('DiffCompleteModal — flow', () => {
language: "rust",
instruction: "rename line 2",
provider: "mock",
+ // The model must ride along with the provider. Without it the backend
+ // fell back to re-pointing the shared chat engine, which dropped the
+ // host's model choice and switched the whole chat session's provider
+ // as a side effect of a single ⌘. press.
+ model: "mock-model",
}));
});
@@ -717,6 +723,7 @@ describe('DiffCompleteModal — D1.2 autosave hooks', () => {
selectionStartLine: 2,
selectionEndLine: 2,
provider: "mock",
+ model: "mock-model",
onApply: vi.fn(),
};
diff --git a/vibecoder/src/lib/__tests__/ghostText.test.ts b/vibecoder/src/lib/__tests__/ghostText.test.ts
new file mode 100644
index 000000000..5b92fe86d
--- /dev/null
+++ b/vibecoder/src/lib/__tests__/ghostText.test.ts
@@ -0,0 +1,283 @@
+import { describe, it, expect, vi } from "vitest";
+import {
+ fitCompletionToLine,
+ windowContext,
+ registerGhostText,
+ PREFIX_LINES,
+ SUFFIX_LINES,
+ type GhostTextDeps,
+} from "../ghostText";
+
+// ── Monaco stand-ins ──────────────────────────────────────────────────────
+// Only the surface `registerGhostText` actually touches. Mirrors the real
+// enum's numbering: Automatic = 0, Explicit = 1. The VS Code enum is the
+// other way round, which is exactly why the gate must compare against a
+// named member — see ghostText.ts's header.
+const TRIGGER = { Automatic: 0, Explicit: 1 } as const;
+
+function fakeModel(lines: string[], languageId = "typescript") {
+ const lineAt = (n: number) => lines[n - 1] ?? "";
+ return {
+ getLineCount: () => lines.length,
+ getLanguageId: () => languageId,
+ getLineMaxColumn: (n: number) => lineAt(n).length + 1,
+ getValueInRange: (range: {
+ startLineNumber: number;
+ startColumn: number;
+ endLineNumber: number;
+ endColumn: number;
+ }) => {
+ const slice: string[] = [];
+ for (let n = range.startLineNumber; n <= range.endLineNumber; n++) {
+ const text = lineAt(n);
+ const from = n === range.startLineNumber ? range.startColumn - 1 : 0;
+ const to = n === range.endLineNumber ? range.endColumn - 1 : text.length;
+ slice.push(text.slice(from, to));
+ }
+ return slice.join("\n");
+ },
+ };
+}
+
+function fakeMonaco() {
+ const dispose = vi.fn();
+ let registered: {
+ provideInlineCompletions: (
+ model: unknown,
+ position: unknown,
+ context: { triggerKind: number },
+ token: { isCancellationRequested: boolean },
+ ) => Promise<{ items: unknown[] }>;
+ } | null = null;
+ return {
+ dispose,
+ get provider() {
+ if (!registered) throw new Error("provider was never registered");
+ return registered;
+ },
+ monaco: {
+ languages: {
+ InlineCompletionTriggerKind: TRIGGER,
+ registerInlineCompletionsProvider: (_selector: string, provider: never) => {
+ registered = provider;
+ return { dispose };
+ },
+ },
+ } as never,
+ };
+}
+
+function deps(overrides: Partial = {}): GhostTextDeps {
+ return {
+ invoke: vi.fn().mockResolvedValue({
+ completion: "return a + b;",
+ model_name: "test-model",
+ truncated: false,
+ }),
+ getProvider: () => "anthropic",
+ getModel: () => "claude-opus-5",
+ getFilePath: () => "/w/src/lib.ts",
+ onError: vi.fn(),
+ ...overrides,
+ };
+}
+
+const POSITION = { lineNumber: 2, column: 5 };
+const LIVE_TOKEN = { isCancellationRequested: false };
+
+describe("windowContext", () => {
+ it("splits the buffer exactly at the cursor", () => {
+ const model = fakeModel(["function f() {", " ", "}"]);
+ const { prefix, suffix } = windowContext(model, POSITION);
+ expect(prefix).toBe("function f() {\n ");
+ expect(suffix).toBe("\n}");
+ });
+
+ it("reports only the text between the cursor and end of line as restOfLine", () => {
+ const model = fakeModel(["const x = 1;", " foo(bar)", "}"]);
+ // Columns are 1-based: column 9 of " foo(bar)" sits just after the `(`.
+ const { restOfLine } = windowContext(model, { lineNumber: 2, column: 9 });
+ expect(restOfLine).toBe("bar)");
+ });
+
+ it("bounds the window rather than sending the whole file", () => {
+ const long = Array.from({ length: 1000 }, (_, i) => `line ${i}`);
+ const model = fakeModel(long);
+ const { prefix, suffix } = windowContext(model, { lineNumber: 500, column: 1 });
+ expect(prefix.split("\n").length).toBe(PREFIX_LINES + 1);
+ expect(suffix.split("\n").length).toBe(SUFFIX_LINES + 1);
+ });
+
+ it("clamps at the start and end of the buffer", () => {
+ const model = fakeModel(["a", "b"]);
+ const { prefix, suffix } = windowContext(model, { lineNumber: 1, column: 1 });
+ expect(prefix).toBe("");
+ expect(suffix).toBe("a\nb");
+ });
+});
+
+describe("fitCompletionToLine", () => {
+ it("keeps a multi-line completion when only whitespace follows the cursor", () => {
+ const fitted = fitCompletionToLine("if (x) {\n y();\n}", " ");
+ expect(fitted).toEqual({ text: "if (x) {\n y();\n}", extendToEndOfLine: true });
+ });
+
+ it("does not extend the range when the cursor is already at end of line", () => {
+ const fitted = fitCompletionToLine("a + b", "");
+ expect(fitted).toEqual({ text: "a + b", extendToEndOfLine: false });
+ });
+
+ it("clips to one line when real code follows the cursor", () => {
+ // Monaco requires a multi-line insertText to end its range at a line end,
+ // and we cannot extend over `)` without eating it.
+ const fitted = fitCompletionToLine("a + b\nmore()", ")");
+ expect(fitted).toEqual({ text: "a + b", extendToEndOfLine: false });
+ });
+
+ it("returns null when nothing renderable remains", () => {
+ expect(fitCompletionToLine("", "")).toBeNull();
+ expect(fitCompletionToLine("\nfoo()", ")")).toBeNull();
+ });
+});
+
+describe("registerGhostText — the explicit-trigger gate", () => {
+ it("returns nothing and calls no backend for an automatic trigger", async () => {
+ const fake = fakeMonaco();
+ const d = deps();
+ registerGhostText(fake.monaco, d);
+
+ const result = await fake.provider.provideInlineCompletions(
+ fakeModel(["a", "bcde", "f"]),
+ POSITION,
+ { triggerKind: TRIGGER.Automatic },
+ LIVE_TOKEN,
+ );
+
+ expect(result.items).toEqual([]);
+ expect(d.invoke).not.toHaveBeenCalled();
+ });
+
+ it("answers an explicit trigger with a suggestion at the cursor", async () => {
+ const fake = fakeMonaco();
+ const d = deps();
+ registerGhostText(fake.monaco, d);
+
+ const result = await fake.provider.provideInlineCompletions(
+ fakeModel(["function f() {", " ", "}"]),
+ POSITION,
+ { triggerKind: TRIGGER.Explicit },
+ LIVE_TOKEN,
+ );
+
+ expect(d.invoke).toHaveBeenCalledWith("ghost_complete", {
+ filePath: "/w/src/lib.ts",
+ language: "typescript",
+ prefix: "function f() {\n ",
+ suffix: "\n}",
+ provider: "anthropic",
+ model: "claude-opus-5",
+ });
+ expect(result.items).toHaveLength(1);
+ expect(result.items[0]).toMatchObject({ insertText: "return a + b;" });
+ });
+
+ it("refuses to guess a provider when the toolbar has no selection", async () => {
+ const fake = fakeMonaco();
+ const d = deps({ getProvider: () => "", getModel: () => "" });
+ registerGhostText(fake.monaco, d);
+
+ const result = await fake.provider.provideInlineCompletions(
+ fakeModel(["a", "bcde"]),
+ POSITION,
+ { triggerKind: TRIGGER.Explicit },
+ LIVE_TOKEN,
+ );
+
+ expect(result.items).toEqual([]);
+ expect(d.invoke).not.toHaveBeenCalled();
+ expect(d.onError).toHaveBeenCalledWith(
+ expect.stringContaining("Select a provider"),
+ );
+ });
+
+ it("treats an empty completion as the model declining, not an error", async () => {
+ const fake = fakeMonaco();
+ const d = deps({
+ invoke: vi.fn().mockResolvedValue({
+ completion: "",
+ model_name: "test-model",
+ truncated: false,
+ }),
+ });
+ registerGhostText(fake.monaco, d);
+
+ const result = await fake.provider.provideInlineCompletions(
+ fakeModel(["a", "bcde"]),
+ POSITION,
+ { triggerKind: TRIGGER.Explicit },
+ LIVE_TOKEN,
+ );
+
+ expect(result.items).toEqual([]);
+ expect(d.onError).not.toHaveBeenCalled();
+ });
+
+ it("surfaces a backend failure to the user", async () => {
+ const fake = fakeMonaco();
+ const d = deps({ invoke: vi.fn().mockRejectedValue(new Error("no API key")) });
+ registerGhostText(fake.monaco, d);
+
+ const result = await fake.provider.provideInlineCompletions(
+ fakeModel(["a", "bcde"]),
+ POSITION,
+ { triggerKind: TRIGGER.Explicit },
+ LIVE_TOKEN,
+ );
+
+ expect(result.items).toEqual([]);
+ expect(d.onError).toHaveBeenCalledWith(expect.stringContaining("no API key"));
+ });
+
+ it("drops a result whose request was cancelled mid-flight", async () => {
+ const fake = fakeMonaco();
+ registerGhostText(fake.monaco, deps());
+
+ const result = await fake.provider.provideInlineCompletions(
+ fakeModel(["a", "bcde"]),
+ POSITION,
+ { triggerKind: TRIGGER.Explicit },
+ { isCancellationRequested: true },
+ );
+
+ expect(result.items).toEqual([]);
+ });
+
+ it("reports truncation without claiming a specific line count", async () => {
+ const fake = fakeMonaco();
+ const onTruncated = vi.fn();
+ const d = deps({
+ onTruncated,
+ invoke: vi.fn().mockResolvedValue({
+ completion: "a();",
+ model_name: "test-model",
+ truncated: true,
+ }),
+ });
+ registerGhostText(fake.monaco, d);
+
+ await fake.provider.provideInlineCompletions(
+ fakeModel(["a", "bcde"]),
+ POSITION,
+ { triggerKind: TRIGGER.Explicit },
+ LIVE_TOKEN,
+ );
+
+ expect(onTruncated).toHaveBeenCalledOnce();
+ });
+
+ it("disposes its registration", () => {
+ const fake = fakeMonaco();
+ registerGhostText(fake.monaco, deps()).dispose();
+ expect(fake.dispose).toHaveBeenCalledOnce();
+ });
+});
diff --git a/vibecoder/src/lib/__tests__/lsp.test.ts b/vibecoder/src/lib/__tests__/lsp.test.ts
index 63ef4086c..93be4ce72 100644
--- a/vibecoder/src/lib/__tests__/lsp.test.ts
+++ b/vibecoder/src/lib/__tests__/lsp.test.ts
@@ -23,6 +23,7 @@ import {
parentDirectory,
parseInstallHint,
toLspPosition,
+ toLspCompletionContext,
toMonacoCompletionItem,
toMonacoCompletionKind,
toMonacoCompletionList,
@@ -1544,3 +1545,61 @@ describe("createLspBridge", () => {
warn.mockRestore();
});
});
+
+// ── Path B: LSP completion trigger context ────────────────────────────────
+// Monaco numbers these from 0, LSP from 1. The request used to be hardcoded
+// to `{ triggerKind: 1 }`, so every completion claimed to be a manual invoke.
+describe("toLspCompletionContext", () => {
+ // Monaco's real numbering.
+ const KINDS = {
+ Invoke: 0,
+ TriggerCharacter: 1,
+ TriggerForIncompleteCompletions: 2,
+ };
+
+ it("maps a manual invoke to LSP Invoked (1)", () => {
+ expect(toLspCompletionContext({ triggerKind: KINDS.Invoke }, KINDS)).toEqual({
+ triggerKind: 1,
+ });
+ });
+
+ it("maps a trigger character to LSP TriggerCharacter (2) and forwards it", () => {
+ expect(
+ toLspCompletionContext(
+ { triggerKind: KINDS.TriggerCharacter, triggerCharacter: "." },
+ KINDS,
+ ),
+ ).toEqual({ triggerKind: 2, triggerCharacter: "." });
+ });
+
+ it("maps an incomplete-refilter to LSP TriggerForIncompleteCompletions (3)", () => {
+ expect(
+ toLspCompletionContext(
+ { triggerKind: KINDS.TriggerForIncompleteCompletions },
+ KINDS,
+ ),
+ ).toEqual({ triggerKind: 3 });
+ });
+
+ it("omits triggerCharacter entirely when Monaco did not supply one", () => {
+ const ctx = toLspCompletionContext(
+ { triggerKind: KINDS.TriggerCharacter },
+ KINDS,
+ );
+ expect(ctx).toEqual({ triggerKind: 2 });
+ expect("triggerCharacter" in ctx).toBe(false);
+ });
+
+ it("falls back to Invoked when Monaco passes no context at all", () => {
+ expect(toLspCompletionContext(undefined, KINDS)).toEqual({ triggerKind: 1 });
+ });
+
+ it("never emits LSP's out-of-range 0", () => {
+ // Monaco's Invoke is 0 and LSP has no 0 — a naive pass-through would send
+ // it and some servers reject the request outright.
+ for (const kind of Object.values(KINDS)) {
+ expect(toLspCompletionContext({ triggerKind: kind }, KINDS).triggerKind)
+ .toBeGreaterThan(0);
+ }
+ });
+});
diff --git a/vibecoder/src/lib/ghostText.ts b/vibecoder/src/lib/ghostText.ts
new file mode 100644
index 000000000..4d8320806
--- /dev/null
+++ b/vibecoder/src/lib/ghostText.ts
@@ -0,0 +1,227 @@
+/**
+ * Ghost text — explicit-trigger inline completion for Monaco.
+ *
+ * # The gate
+ *
+ * The previous ghost-text surface was removed in `5a7eef7c` because it was
+ * keystroke-driven. This one is not, and the single line that guarantees it is
+ * the `triggerKind` check in `provideInlineCompletions`: Monaco calls the
+ * provider for *both* `Automatic` (typing) and `Explicit` (the user asked), and
+ * we answer only the second.
+ *
+ * `InlineCompletionTriggerKind` is `{ Automatic: 0, Explicit: 1 }` in Monaco and
+ * `{ Invoke: 0, Automatic: 1 }` in VS Code — the *same names mean opposite
+ * numbers*. Compare against the named enum member, never a literal, and never
+ * copy this check between the two hosts. The VS Code side lives in
+ * `vscode-extension/src/ghost-text.ts` and has its own gate.
+ *
+ * There is deliberately no debounce timer and no edit-history buffer here. The
+ * request carries the window around the cursor and nothing else.
+ */
+import type * as Monaco from "monaco-editor";
+
+/** Lines of context sent before the cursor. */
+export const PREFIX_LINES = 160;
+/** Lines of context sent after the cursor. */
+export const SUFFIX_LINES = 60;
+
+export interface GhostResponse {
+ completion: string;
+ model_name: string;
+ truncated: boolean;
+}
+
+export interface GhostTextDeps {
+ invoke: (command: string, args?: Record) => Promise;
+ /** Toolbar provider selection. Empty string means "nothing selected". */
+ getProvider: () => string;
+ /** Toolbar model selection. */
+ getModel: () => string;
+ /** Absolute path of the active file, for the prompt header. */
+ getFilePath: () => string;
+ /** Surfaced to the user; called for a failed request, not for an empty one. */
+ onError: (message: string) => void;
+ /** Called when the model's answer was clipped by the backend line cap. */
+ onTruncated?: () => void;
+}
+
+/** The prefix/suffix window around a cursor position. */
+export interface ContextWindow {
+ prefix: string;
+ suffix: string;
+ /** Text between the cursor and the end of its line. */
+ restOfLine: string;
+}
+
+/**
+ * Slice the bounded window around the cursor.
+ *
+ * Exported for tests: the window is the entire hidden state this surface has,
+ * so what goes in it is worth pinning.
+ */
+export function windowContext(
+ model: Pick,
+ position: Pick,
+): ContextWindow {
+ const lineCount = model.getLineCount();
+ const firstLine = Math.max(1, position.lineNumber - PREFIX_LINES);
+ const lastLine = Math.min(lineCount, position.lineNumber + SUFFIX_LINES);
+ const endColumn = model.getLineMaxColumn(lastLine);
+ const lineEndColumn = model.getLineMaxColumn(position.lineNumber);
+
+ return {
+ prefix: model.getValueInRange({
+ startLineNumber: firstLine,
+ startColumn: 1,
+ endLineNumber: position.lineNumber,
+ endColumn: position.column,
+ }),
+ suffix: model.getValueInRange({
+ startLineNumber: position.lineNumber,
+ startColumn: position.column,
+ endLineNumber: lastLine,
+ endColumn,
+ }),
+ restOfLine: model.getValueInRange({
+ startLineNumber: position.lineNumber,
+ startColumn: position.column,
+ endLineNumber: position.lineNumber,
+ endColumn: lineEndColumn,
+ }),
+ };
+}
+
+/** How a completion must be shaped to render correctly at the cursor. */
+export interface FittedCompletion {
+ text: string;
+ /**
+ * When true the replacement range runs to the end of the line, which is what
+ * Monaco requires of a multi-line `insertText`. Only safe when the text being
+ * swallowed is whitespace.
+ */
+ extendToEndOfLine: boolean;
+}
+
+/**
+ * Constrain a completion so Monaco can render it at the cursor.
+ *
+ * Monaco requires that a multi-line `insertText` end its range at the end of a
+ * line. When real code follows the cursor on the same line we cannot extend the
+ * range without eating it, so the suggestion is clipped to its first line.
+ * Returns `null` when nothing renderable remains.
+ */
+export function fitCompletionToLine(
+ completion: string,
+ restOfLine: string,
+): FittedCompletion | null {
+ if (completion.length === 0) return null;
+
+ // Trailing whitespace after the cursor can be swallowed; real text cannot.
+ if (restOfLine.trim().length === 0) {
+ return { text: completion, extendToEndOfLine: restOfLine.length > 0 };
+ }
+
+ const firstLine = completion.split("\n")[0];
+ if (firstLine.trim().length === 0) return null;
+ return { text: firstLine, extendToEndOfLine: false };
+}
+
+export interface GhostTextHandle {
+ dispose: () => void;
+ /**
+ * Ask Monaco for an explicit inline suggestion. This is the only path that
+ * reaches the provider with `Explicit`, so it is the only path that produces
+ * a suggestion.
+ */
+ trigger: (editor: Monaco.editor.ICodeEditor) => void;
+}
+
+/**
+ * Register the explicit-trigger inline completion provider for all languages.
+ *
+ * Registering on `"*"` is safe precisely because of the trigger gate: the
+ * provider is consulted constantly but answers only when asked.
+ */
+export function registerGhostText(
+ monaco: typeof Monaco,
+ deps: GhostTextDeps,
+): GhostTextHandle {
+ const registration = monaco.languages.registerInlineCompletionsProvider("*", {
+ provideInlineCompletions: async (model, position, context, token) => {
+ // ── The gate. See the module header before touching this. ──
+ if (
+ context.triggerKind !==
+ monaco.languages.InlineCompletionTriggerKind.Explicit
+ ) {
+ return { items: [] };
+ }
+
+ const provider = deps.getProvider();
+ const selectedModel = deps.getModel();
+ if (!provider || !selectedModel) {
+ // Matches the provider-agnostic rule: no toolbar selection means no
+ // request, never a silent default to one vendor.
+ deps.onError("Select a provider and model in the toolbar first.");
+ return { items: [] };
+ }
+
+ const { prefix, suffix, restOfLine } = windowContext(model, position);
+
+ let response: GhostResponse;
+ try {
+ response = await deps.invoke("ghost_complete", {
+ filePath: deps.getFilePath(),
+ language: model.getLanguageId(),
+ prefix,
+ suffix,
+ provider,
+ model: selectedModel,
+ });
+ } catch (error) {
+ deps.onError(
+ `Inline completion failed: ${
+ error instanceof Error ? error.message : String(error)
+ }`,
+ );
+ return { items: [] };
+ }
+
+ // The user moved on while the request was in flight.
+ if (token.isCancellationRequested) return { items: [] };
+
+ // An empty completion is the model declining, not a failure — say
+ // nothing rather than reporting an error the user cannot act on.
+ const fitted = fitCompletionToLine(response.completion, restOfLine);
+ if (!fitted) return { items: [] };
+
+ if (response.truncated) deps.onTruncated?.();
+
+ const endColumn = fitted.extendToEndOfLine
+ ? model.getLineMaxColumn(position.lineNumber)
+ : position.column;
+
+ return {
+ items: [
+ {
+ insertText: fitted.text,
+ range: {
+ startLineNumber: position.lineNumber,
+ startColumn: position.column,
+ endLineNumber: position.lineNumber,
+ endColumn,
+ },
+ },
+ ],
+ };
+ },
+ // Nothing is retained per request, so there is nothing to release.
+ disposeInlineCompletions: () => {},
+ });
+
+ return {
+ dispose: () => registration.dispose(),
+ trigger: (editor) => {
+ editor.trigger("ghost-text", "editor.action.inlineSuggest.trigger", {});
+ },
+ };
+}
diff --git a/vibecoder/src/lib/lsp.ts b/vibecoder/src/lib/lsp.ts
index 0adfe379c..0e8dc5acc 100644
--- a/vibecoder/src/lib/lsp.ts
+++ b/vibecoder/src/lib/lsp.ts
@@ -65,6 +65,11 @@ export interface MonacoLspEnums {
readonly completionKinds: MonacoCompletionKinds;
readonly insertAsSnippet: number;
readonly deprecatedTag: number;
+ readonly completionTriggerKinds: {
+ readonly Invoke: number;
+ readonly TriggerCharacter: number;
+ readonly TriggerForIncompleteCompletions: number;
+ };
readonly markerSeverity: {
readonly Error: number;
readonly Warning: number;
@@ -80,10 +85,56 @@ export function enumsFromMonaco(monaco: typeof Monaco): MonacoLspEnums {
insertAsSnippet:
monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet,
deprecatedTag: monaco.languages.CompletionItemTag.Deprecated,
+ completionTriggerKinds: monaco.languages.CompletionTriggerKind,
markerSeverity: monaco.MarkerSeverity,
};
}
+/**
+ * The `context` field of an LSP `textDocument/completion` request.
+ *
+ * LSP numbers these from 1 (`Invoked = 1`, `TriggerCharacter = 2`,
+ * `TriggerForIncompleteCompletions = 3`); Monaco numbers the same three from 0.
+ * They are *not* interchangeable, which is why the mapping below is explicit
+ * rather than `monacoKind + 1`.
+ */
+export interface LspCompletionContext {
+ triggerKind: 1 | 2 | 3;
+ triggerCharacter?: string;
+}
+
+/**
+ * Translate Monaco's completion context into LSP's.
+ *
+ * This used to be hardcoded to `{ triggerKind: 1 }` — every request claimed to
+ * be a manual invoke. Servers use this to decide what to offer: a member list
+ * after `.` is gated on `TriggerCharacter` by several servers, and a server
+ * that returned `isIncomplete` expects `TriggerForIncompleteCompletions` on the
+ * refilter rather than a fresh invoke.
+ */
+export function toLspCompletionContext(
+ context: { triggerKind: number; triggerCharacter?: string } | undefined,
+ kinds: MonacoLspEnums["completionTriggerKinds"],
+): LspCompletionContext {
+ if (!context) return { triggerKind: 1 };
+
+ if (context.triggerKind === kinds.TriggerCharacter) {
+ return {
+ triggerKind: 2,
+ // A TriggerCharacter request without the character is malformed; omit
+ // the field rather than send `triggerCharacter: undefined`, which some
+ // servers reject outright.
+ ...(context.triggerCharacter !== undefined
+ ? { triggerCharacter: context.triggerCharacter }
+ : {}),
+ };
+ }
+ if (context.triggerKind === kinds.TriggerForIncompleteCompletions) {
+ return { triggerKind: 3 };
+ }
+ return { triggerKind: 1 };
+}
+
// ── LSP wire types ──────────────────────────────────────────────────────────
export interface LspPosition {
@@ -1088,7 +1139,7 @@ export function createLspBridge(
const disposables: Monaco.IDisposable[] = [
monaco.languages.registerCompletionItemProvider(monacoLanguage, {
triggerCharacters: triggerList,
- provideCompletionItems: async (model, position) => {
+ provideCompletionItems: async (model, position, completionContext) => {
const document = documentFor(model);
if (!document) return { suggestions: [] };
// The edit that triggered this completion may still be queued; the
@@ -1111,7 +1162,10 @@ export function createLspBridge(
params: {
textDocument: { uri: document.uri },
position: toLspPosition(position),
- context: { triggerKind: 1 },
+ context: toLspCompletionContext(
+ completionContext,
+ enums.completionTriggerKinds,
+ ),
},
},
);
diff --git a/vibedesk/package-lock.json b/vibedesk/package-lock.json
index 350bfd46f..b737abe63 100644
--- a/vibedesk/package-lock.json
+++ b/vibedesk/package-lock.json
@@ -10,7 +10,7 @@
"dependencies": {
"@tauri-apps/api": "^2.11.1",
"@tauri-apps/plugin-dialog": "^2.7.2",
- "lucide-react": "^1.28.0",
+ "lucide-react": "^1.29.0",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-markdown": "^10.1.0",
@@ -310,9 +310,9 @@
}
},
"node_modules/@tauri-apps/cli": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.2.tgz",
- "integrity": "sha512-bk3HemqvGRoy+5D/dVMUQHKMYLglD0jVnMm/0iGMH6ufZ+p8r14m6BpIixwij3PBvZdvORUp1YifTD8QxVZ1Nw==",
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.11.4.tgz",
+ "integrity": "sha512-R8xGtMpwyetawSqm9kYOuMmEqkhUbvcUy8n0aNXIxollKBLESUu5f4Fx+64hgASYm1H+jSWq6jCW6zqTnH6hqQ==",
"dev": true,
"license": "Apache-2.0 OR MIT",
"bin": {
@@ -326,23 +326,23 @@
"url": "https://opencollective.com/tauri"
},
"optionalDependencies": {
- "@tauri-apps/cli-darwin-arm64": "2.11.2",
- "@tauri-apps/cli-darwin-x64": "2.11.2",
- "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.2",
- "@tauri-apps/cli-linux-arm64-gnu": "2.11.2",
- "@tauri-apps/cli-linux-arm64-musl": "2.11.2",
- "@tauri-apps/cli-linux-riscv64-gnu": "2.11.2",
- "@tauri-apps/cli-linux-x64-gnu": "2.11.2",
- "@tauri-apps/cli-linux-x64-musl": "2.11.2",
- "@tauri-apps/cli-win32-arm64-msvc": "2.11.2",
- "@tauri-apps/cli-win32-ia32-msvc": "2.11.2",
- "@tauri-apps/cli-win32-x64-msvc": "2.11.2"
+ "@tauri-apps/cli-darwin-arm64": "2.11.4",
+ "@tauri-apps/cli-darwin-x64": "2.11.4",
+ "@tauri-apps/cli-linux-arm-gnueabihf": "2.11.4",
+ "@tauri-apps/cli-linux-arm64-gnu": "2.11.4",
+ "@tauri-apps/cli-linux-arm64-musl": "2.11.4",
+ "@tauri-apps/cli-linux-riscv64-gnu": "2.11.4",
+ "@tauri-apps/cli-linux-x64-gnu": "2.11.4",
+ "@tauri-apps/cli-linux-x64-musl": "2.11.4",
+ "@tauri-apps/cli-win32-arm64-msvc": "2.11.4",
+ "@tauri-apps/cli-win32-ia32-msvc": "2.11.4",
+ "@tauri-apps/cli-win32-x64-msvc": "2.11.4"
}
},
"node_modules/@tauri-apps/cli-darwin-arm64": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.2.tgz",
- "integrity": "sha512-+4UZzLt+eOAEQCwgd+TqKgyUJMrvx+BgdXLLaqJYmPqzP+nE6YZr/hY6CWLYGQb8jFn99jEkmC6uA3tNvamA1w==",
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.11.4.tgz",
+ "integrity": "sha512-1ryOF3ZhpZ/nemHV5zVwBQBz9jDGKmKPvWPADOhc83ig0P4bMc2iER4NbC6r9sjeIZ6RVQ4g3RZIYvezhcl4TQ==",
"cpu": [
"arm64"
],
@@ -357,9 +357,9 @@
}
},
"node_modules/@tauri-apps/cli-darwin-x64": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.2.tgz",
- "integrity": "sha512-VjYYtZUPqDMLutSfJEyxFE3Bz+DPi7c8wC3imckgvciLDZLq4qwKJxBicg0BXGhXjJsl8vKWgWRFNMPELQ+Xyg==",
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.11.4.tgz",
+ "integrity": "sha512-uFsGQAAfuyz1k/yGLmkWfkBlgKAqZfxqlHmLWx81QU27RJWfmbNHCIq8T8w1e+VClleIuZUjpHWfoE4E3DLo3A==",
"cpu": [
"x64"
],
@@ -374,9 +374,9 @@
}
},
"node_modules/@tauri-apps/cli-linux-arm-gnueabihf": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.2.tgz",
- "integrity": "sha512-yMemD6f4i95AQriS8EazyOFzbE34yjnP16i3IOzpHGQvBoy2DjypFMFBq0NtPuITURv/cOGguRtHR5d79/9CSA==",
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.11.4.tgz",
+ "integrity": "sha512-IaHZn5CdBL21oUmjiVOS1ctw6Ip1O0pjp70FwOWmYz1myWe0SY96ZIj2FYf7pT0m8bI2h/hrs5ZbEXXh44/MkQ==",
"cpu": [
"arm"
],
@@ -391,9 +391,9 @@
}
},
"node_modules/@tauri-apps/cli-linux-arm64-gnu": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.2.tgz",
- "integrity": "sha512-cgI91D2wL8GSgoWwZXDqt+DwnuZCP2/bz03QAE4TrhgAKIsrB4hX26W/H1EONPUUNkqrsgeCD0wU6pcNjV/5kw==",
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.11.4.tgz",
+ "integrity": "sha512-N41/ukTRVe6XSuUTESuFdGeOW2i7k62tK+6gHK5Kd5/q5RPvvi19GaWAVPPb9u95HSGmTChSolBfzynUsssFaA==",
"cpu": [
"arm64"
],
@@ -411,9 +411,9 @@
}
},
"node_modules/@tauri-apps/cli-linux-arm64-musl": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.2.tgz",
- "integrity": "sha512-X1rm0BERqAAggtYTESSgXrS3sz4Sb/OiPiz54UqISlXW+GkR3vNIGnsy/lejNmoXGVqri3Q53BCfQiclOIyRPw==",
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.11.4.tgz",
+ "integrity": "sha512-v277UnT/fB64xAfSroL5N3Km3tLmvATWqJJw/wRI+g6o+HkeD0slyE7gOhNs1MbjE41R7bQOTxMVoL3aomUJmw==",
"cpu": [
"arm64"
],
@@ -431,9 +431,9 @@
}
},
"node_modules/@tauri-apps/cli-linux-riscv64-gnu": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.2.tgz",
- "integrity": "sha512-usbMLJbT3KtkOrBMDVeGYNM35aTHXx38SJSzTMSqqjeUIOQ+iVPjb2yAGNAE+KqmBbAx4FOFIyMeKXx2M/JKGQ==",
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.11.4.tgz",
+ "integrity": "sha512-qqgNkQ2u1yZHxjhxsZaxUtRDW8dIqIYm33rx/mzwQv0SfY9x1B+iraj8vWeFiXjjSVVhEMepXSOts1TqPzvXNQ==",
"cpu": [
"riscv64"
],
@@ -451,9 +451,9 @@
}
},
"node_modules/@tauri-apps/cli-linux-x64-gnu": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.2.tgz",
- "integrity": "sha512-Ru4gwJKPG0ctVGchRGpRup4Y4lW2SSfFnrbQcyHhCliKy4g8Qz97TrUgCur4CbWyAgKxvGh3SjrkA0LDYzDGiw==",
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.11.4.tgz",
+ "integrity": "sha512-2VRNWl84FOH0m2giiDkO2h0QXlcMJeX+zJDpI5kDIQAx6s+geF3v48F4DXfJez4GS/FdoDGnPnw1C2iYGbQ7bQ==",
"cpu": [
"x64"
],
@@ -471,9 +471,9 @@
}
},
"node_modules/@tauri-apps/cli-linux-x64-musl": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.2.tgz",
- "integrity": "sha512-eUm7T6clN1MMmNSRQ9gaWsQdyehQx2Gmn5hht/QUlqZQI/qcP2OJK5dnaxqwFzCr2HdsEo9ydxaqcS1oJzMvUw==",
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.11.4.tgz",
+ "integrity": "sha512-o9GyhYor/nc7xarmwDE3ka2szuW3uuZzXjHWh64Q8YX5AtSgxdQkFWzrY4O8KiGtVNvFBI14H3Q49Qj5TOIP/A==",
"cpu": [
"x64"
],
@@ -491,9 +491,9 @@
}
},
"node_modules/@tauri-apps/cli-win32-arm64-msvc": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.2.tgz",
- "integrity": "sha512-HeeZW80jU+gVTOEX4X/hC6NVSAdDVXajwP5fxIZ/3z9WvUC7qrudX2GMTilYq6Dg0e0sk0XgsAJD1hZ5wPBXUA==",
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.11.4.tgz",
+ "integrity": "sha512-ld5Ehb598m0VkYyylRPNeCFsBe/km0jxis6KgMpl3IGY6I/i1RwQXO05I1AsXUXO2WC6AvB/Lw4qTf/asiuEiQ==",
"cpu": [
"arm64"
],
@@ -508,9 +508,9 @@
}
},
"node_modules/@tauri-apps/cli-win32-ia32-msvc": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.2.tgz",
- "integrity": "sha512-YhjQNZcXfbkCLyazSv1nPnJ9iRFE1wm6kc51FDbU10/Dk09io+6PAGMLjkxnX2GdM0qMnDmTjstY8mTDVvtKeA==",
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.11.4.tgz",
+ "integrity": "sha512-12Hxi0XX/H5VFxO/bGgHkFWhml9VMgEOu9CidjeCeTNQ1l6fpUlbiGgSP7CLI3PFtW9/FfbeHieZ+kyWK5H7CA==",
"cpu": [
"ia32"
],
@@ -525,9 +525,9 @@
}
},
"node_modules/@tauri-apps/cli-win32-x64-msvc": {
- "version": "2.11.2",
- "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.2.tgz",
- "integrity": "sha512-d2JchlFIpZevZVReyqhQOekJmb1UH3rhZ5VX6sH3ty9ETE0TKQavpihvoScUXfKKpW6HZC0MrFGRU0ZtD+w3gA==",
+ "version": "2.11.4",
+ "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.11.4.tgz",
+ "integrity": "sha512-+vDiqBIU5dMISg/wNvX3sF+ZHfgJGJ5T0AcO+EHNXV9GGAG+P5fzodlDXD3QdKCRgZxMoCm5PPvj3BqLNjBthw==",
"cpu": [
"x64"
],
@@ -1600,9 +1600,9 @@
}
},
"node_modules/lucide-react": {
- "version": "1.28.0",
- "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.28.0.tgz",
- "integrity": "sha512-fARAFJULsGuDDydjp6+6blekG/sBIM29TerzLjc9bQUKAcEfrSc4ZQKb25KRz4OMKd87cZTb5dgq0w/T6KufVg==",
+ "version": "1.29.0",
+ "resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-1.29.0.tgz",
+ "integrity": "sha512-Xs9QFG5+9sNX04MdKVT4++umA+hJ2qsJVlRlRWHQ7qZobXgMiNHSpZ5eZm8JUoGCdNyoEdXoEwa8HVr0DNjOQg==",
"license": "ISC",
"peerDependencies": {
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
diff --git a/vibedesk/package.json b/vibedesk/package.json
index 6da829c5c..b6b903bb2 100644
--- a/vibedesk/package.json
+++ b/vibedesk/package.json
@@ -18,7 +18,7 @@
"dependencies": {
"@tauri-apps/api": "^2.11.1",
"@tauri-apps/plugin-dialog": "^2.7.2",
- "lucide-react": "^1.28.0",
+ "lucide-react": "^1.29.0",
"react": "^19.2.8",
"react-dom": "^19.2.8",
"react-markdown": "^10.1.0",
diff --git a/vibemobile/android/gradle/wrapper/gradle-wrapper.properties b/vibemobile/android/gradle/wrapper/gradle-wrapper.properties
index f98345f0f..c4a03016d 100644
--- a/vibemobile/android/gradle/wrapper/gradle-wrapper.properties
+++ b/vibemobile/android/gradle/wrapper/gradle-wrapper.properties
@@ -2,4 +2,4 @@ distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-all.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.0-all.zip
diff --git a/vibemobile/android/settings.gradle.kts b/vibemobile/android/settings.gradle.kts
index adb282659..69f4320c1 100644
--- a/vibemobile/android/settings.gradle.kts
+++ b/vibemobile/android/settings.gradle.kts
@@ -19,7 +19,7 @@ pluginManagement {
plugins {
id("dev.flutter.flutter-plugin-loader") version "1.0.0"
id("com.android.application") version "9.3.1" apply false
- id("org.jetbrains.kotlin.android") version "2.1.0" apply false
+ id("org.jetbrains.kotlin.android") version "2.4.10" apply false
}
include(":app")
diff --git a/vibemobile/pubspec.lock b/vibemobile/pubspec.lock
index 7b13349f7..9f3e16fe9 100644
--- a/vibemobile/pubspec.lock
+++ b/vibemobile/pubspec.lock
@@ -122,18 +122,18 @@ packages:
dependency: "direct main"
description:
name: flutter_secure_storage
- sha256: "7686b1d6a29985dcbb808c59518226e603e3bfa7c0ddfd1a0d00e4cda77c868e"
+ sha256: "15e8c8fe269fdf7d469b23008ab3df521c8b826ed345820532364c31bdebace6"
url: "https://pub.dev"
source: hosted
- version: "10.3.1"
+ version: "11.0.0"
flutter_secure_storage_darwin:
dependency: transitive
description:
name: flutter_secure_storage_darwin
- sha256: "82329fa5cdf343773b1b6897dea959105a29f092454259edff92f9f6637e8149"
+ sha256: ac6d76a752de0cd738334eb4b21743fc4943f449f5b6e308f18838b048c02ac0
url: "https://pub.dev"
source: hosted
- version: "0.3.2"
+ version: "0.4.0"
flutter_secure_storage_linux:
dependency: transitive
description:
diff --git a/vibemobile/pubspec.yaml b/vibemobile/pubspec.yaml
index 5e9d31e4d..168790849 100644
--- a/vibemobile/pubspec.yaml
+++ b/vibemobile/pubspec.yaml
@@ -13,7 +13,7 @@ dependencies:
http: ^1.4.0
provider: ^6.1.5
shared_preferences: ^2.5.3
- flutter_secure_storage: ^10.3.1
+ flutter_secure_storage: ^11.0.0
mobile_scanner: ^7.4.0
multicast_dns: ^0.3.2+1
speech_to_text: ^7.4.0
diff --git a/vibewatch/VibeCodyWear/gradle/wrapper/gradle-wrapper.jar b/vibewatch/VibeCodyWear/gradle/wrapper/gradle-wrapper.jar
index b1b8ef56b..eddabd2ee 100644
Binary files a/vibewatch/VibeCodyWear/gradle/wrapper/gradle-wrapper.jar and b/vibewatch/VibeCodyWear/gradle/wrapper/gradle-wrapper.jar differ
diff --git a/vibewatch/VibeCodyWear/gradle/wrapper/gradle-wrapper.properties b/vibewatch/VibeCodyWear/gradle/wrapper/gradle-wrapper.properties
index a351597e6..1e922f407 100644
--- a/vibewatch/VibeCodyWear/gradle/wrapper/gradle-wrapper.properties
+++ b/vibewatch/VibeCodyWear/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-9.6.1-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-9.7.0-bin.zip
networkTimeout=10000
validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME
diff --git a/vscode-extension/package-lock.json b/vscode-extension/package-lock.json
index 1b2b18024..0dc02d1ec 100644
--- a/vscode-extension/package-lock.json
+++ b/vscode-extension/package-lock.json
@@ -252,92 +252,6 @@
"dev": true,
"license": "MIT"
},
- "node_modules/@typescript-eslint/eslint-plugin": {
- "version": "8.66.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.66.0.tgz",
- "integrity": "sha512-p088eaGrzYz1s+7cov0aMOCkNGTJlVxF4jgubf28c8L0Cv9Rloj8YBHnv4hXLq6IIEE1AsjNWavO+k+8kP2Y0A==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/regexpp": "^4.12.2",
- "@typescript-eslint/scope-manager": "8.66.0",
- "@typescript-eslint/type-utils": "8.66.0",
- "@typescript-eslint/utils": "8.66.0",
- "@typescript-eslint/visitor-keys": "8.66.0",
- "ignore": "^7.0.5",
- "natural-compare": "^1.4.0",
- "ts-api-utils": "^2.5.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "@typescript-eslint/parser": "^8.66.0",
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
- "node_modules/@typescript-eslint/eslint-plugin/node_modules/ignore": {
- "version": "7.0.6",
- "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz",
- "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": ">= 4"
- }
- },
- "node_modules/@typescript-eslint/parser": {
- "version": "8.66.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.66.0.tgz",
- "integrity": "sha512-X6ypGChaWYk6PBtUg2BwuTZEFFcHJAtGTVJ9/lCTOufhZ4i9fNolQNnktq+kkMCwMj7V8Svsq7+TxSDslmhE0g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/scope-manager": "8.66.0",
- "@typescript-eslint/types": "8.66.0",
- "@typescript-eslint/typescript-estree": "8.66.0",
- "@typescript-eslint/visitor-keys": "8.66.0",
- "debug": "^4.4.3"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
- "node_modules/@typescript-eslint/project-service": {
- "version": "8.66.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.66.0.tgz",
- "integrity": "sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/tsconfig-utils": "^8.66.0",
- "@typescript-eslint/types": "^8.66.0",
- "debug": "^4.4.3"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
"node_modules/@typescript-eslint/scope-manager": {
"version": "8.66.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.66.0.tgz",
@@ -356,48 +270,6 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
- "node_modules/@typescript-eslint/tsconfig-utils": {
- "version": "8.66.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.66.0.tgz",
- "integrity": "sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==",
- "dev": true,
- "license": "MIT",
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
- "node_modules/@typescript-eslint/type-utils": {
- "version": "8.66.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.66.0.tgz",
- "integrity": "sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/types": "8.66.0",
- "@typescript-eslint/typescript-estree": "8.66.0",
- "@typescript-eslint/utils": "8.66.0",
- "debug": "^4.4.3",
- "ts-api-utils": "^2.5.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
"node_modules/@typescript-eslint/types": {
"version": "8.66.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.66.0.tgz",
@@ -412,58 +284,6 @@
"url": "https://opencollective.com/typescript-eslint"
}
},
- "node_modules/@typescript-eslint/typescript-estree": {
- "version": "8.66.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.66.0.tgz",
- "integrity": "sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@typescript-eslint/project-service": "8.66.0",
- "@typescript-eslint/tsconfig-utils": "8.66.0",
- "@typescript-eslint/types": "8.66.0",
- "@typescript-eslint/visitor-keys": "8.66.0",
- "debug": "^4.4.3",
- "minimatch": "^10.2.2",
- "semver": "^7.7.3",
- "tinyglobby": "^0.2.15",
- "ts-api-utils": "^2.5.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
- "node_modules/@typescript-eslint/utils": {
- "version": "8.66.0",
- "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.66.0.tgz",
- "integrity": "sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==",
- "dev": true,
- "license": "MIT",
- "dependencies": {
- "@eslint-community/eslint-utils": "^4.9.1",
- "@typescript-eslint/scope-manager": "8.66.0",
- "@typescript-eslint/types": "8.66.0",
- "@typescript-eslint/typescript-estree": "8.66.0"
- },
- "engines": {
- "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
- },
- "funding": {
- "type": "opencollective",
- "url": "https://opencollective.com/typescript-eslint"
- },
- "peerDependencies": {
- "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
- "typescript": ">=4.8.4 <6.1.0"
- }
- },
"node_modules/@typescript-eslint/visitor-keys": {
"version": "8.66.0",
"resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.66.0.tgz",
@@ -1211,6 +1031,186 @@
"typescript": ">=4.8.4 <6.1.0"
}
},
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin": {
+ "version": "8.66.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.66.0.tgz",
+ "integrity": "sha512-p088eaGrzYz1s+7cov0aMOCkNGTJlVxF4jgubf28c8L0Cv9Rloj8YBHnv4hXLq6IIEE1AsjNWavO+k+8kP2Y0A==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/regexpp": "^4.12.2",
+ "@typescript-eslint/scope-manager": "8.66.0",
+ "@typescript-eslint/type-utils": "8.66.0",
+ "@typescript-eslint/utils": "8.66.0",
+ "@typescript-eslint/visitor-keys": "8.66.0",
+ "ignore": "^7.0.5",
+ "natural-compare": "^1.4.0",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "@typescript-eslint/parser": "^8.66.0",
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": {
+ "version": "8.66.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.66.0.tgz",
+ "integrity": "sha512-LG2dWfjZQQp0ADtAu/EWJVayefGL2UEZ3CDeI44D9v3rXB/WYUqE/jpO28KrEKul5AySrmI+Zh1v6v+xW2U9+g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/types": "8.66.0",
+ "@typescript-eslint/typescript-estree": "8.66.0",
+ "@typescript-eslint/utils": "8.66.0",
+ "debug": "^4.4.3",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/parser": {
+ "version": "8.66.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.66.0.tgz",
+ "integrity": "sha512-X6ypGChaWYk6PBtUg2BwuTZEFFcHJAtGTVJ9/lCTOufhZ4i9fNolQNnktq+kkMCwMj7V8Svsq7+TxSDslmhE0g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/scope-manager": "8.66.0",
+ "@typescript-eslint/types": "8.66.0",
+ "@typescript-eslint/typescript-estree": "8.66.0",
+ "@typescript-eslint/visitor-keys": "8.66.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree": {
+ "version": "8.66.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.66.0.tgz",
+ "integrity": "sha512-8/x4INiiQb10jGgXYD7116/zQ+OL84ZIFn0za68wwFHCanT/VLbBEroWht8RV8fn0/ZCAoazHLQgwUC0UQcDfg==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/project-service": "8.66.0",
+ "@typescript-eslint/tsconfig-utils": "8.66.0",
+ "@typescript-eslint/types": "8.66.0",
+ "@typescript-eslint/visitor-keys": "8.66.0",
+ "debug": "^4.4.3",
+ "minimatch": "^10.2.2",
+ "semver": "^7.7.3",
+ "tinyglobby": "^0.2.15",
+ "ts-api-utils": "^2.5.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/project-service": {
+ "version": "8.66.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.66.0.tgz",
+ "integrity": "sha512-7MthGPTt4BP69lSryqpqq8HQqxuzynssckL/jyDyk3+TNMQ3y2jFWkptCrktWvBrP+EH787Nl5N5Qpw7WZg+5g==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@typescript-eslint/tsconfig-utils": "^8.66.0",
+ "@typescript-eslint/types": "^8.66.0",
+ "debug": "^4.4.3"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/typescript-estree/node_modules/@typescript-eslint/tsconfig-utils": {
+ "version": "8.66.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.66.0.tgz",
+ "integrity": "sha512-9D5gLYZG4rOjcoag8MQ/fWI8WqA9wcPDyOGyWtWFhvM1lHRbliqUSPIY5J3zqCU1tvSwzXxnnjhQhz5Ne7mJ4g==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": {
+ "version": "8.66.0",
+ "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.66.0.tgz",
+ "integrity": "sha512-jasearZPolBw5NJNYGMwxzHMF83niVWmMU1VdHzG1CyfI2VS7f7nZltnKtHcg20hW+7Uo5GfK4MeDPoU3qI8EA==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@eslint-community/eslint-utils": "^4.9.1",
+ "@typescript-eslint/scope-manager": "8.66.0",
+ "@typescript-eslint/types": "8.66.0",
+ "@typescript-eslint/typescript-estree": "8.66.0"
+ },
+ "engines": {
+ "node": "^18.18.0 || ^20.9.0 || >=21.1.0"
+ },
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/typescript-eslint"
+ },
+ "peerDependencies": {
+ "eslint": "^8.57.0 || ^9.0.0 || ^10.0.0",
+ "typescript": ">=4.8.4 <6.1.0"
+ }
+ },
+ "node_modules/typescript-eslint/node_modules/ignore": {
+ "version": "7.0.6",
+ "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.6.tgz",
+ "integrity": "sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==",
+ "dev": true,
+ "license": "MIT",
+ "engines": {
+ "node": ">= 4"
+ }
+ },
"node_modules/undici-types": {
"version": "8.3.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-8.3.0.tgz",
diff --git a/vscode-extension/package.json b/vscode-extension/package.json
index 51031ab58..4c372af59 100644
--- a/vscode-extension/package.json
+++ b/vscode-extension/package.json
@@ -19,6 +19,7 @@
{ "command": "vibecli.connectDaemon", "title": "VibeCLI: Connect to Daemon" },
{ "command": "vibecli.startDaemon", "title": "VibeCLI: Start Daemon" },
{ "command": "vibecli.inlineEdit", "title": "VibeCLI: Inline Edit (selection)" },
+ { "command": "vibecli.ghostComplete", "title": "VibeCLI: Inline Completion at Cursor" },
{ "command": "vibecli.viewJobs", "title": "VibeCLI: View Background Jobs" },
{ "command": "vibecli.sendSelection", "title": "VibeCLI: Send Selection to Agent" },
{ "command": "vibecli.dictate", "title": "VibeCLI: Dictate (voice input)" },
@@ -66,6 +67,12 @@
"key": "ctrl+shift+enter",
"mac": "cmd+shift+enter",
"when": "editorHasSelection"
+ },
+ {
+ "command": "vibecli.ghostComplete",
+ "key": "alt+\\",
+ "mac": "alt+\\",
+ "when": "editorTextFocus"
}
],
"configuration": {
@@ -104,6 +111,11 @@
],
"description": "AI provider to use. Must be a provider the daemon can build (matches vibe-ai::catalog / create_provider). Set the provider's API key via the app or the daemon; ollama needs no key."
},
+ "vibecli.model": {
+ "type": "string",
+ "default": "",
+ "description": "Model id for vibecli.provider (e.g. \"claude-opus-5\", \"gpt-5\"). The daemon only honours the provider override when a model is set too — leave this empty to use whichever provider and model the daemon itself was started with."
+ },
"vibecli.approval": {
"type": "string",
"default": "suggest",
diff --git a/vscode-extension/src/api-client.ts b/vscode-extension/src/api-client.ts
index f37e5790d..1e2244860 100644
--- a/vscode-extension/src/api-client.ts
+++ b/vscode-extension/src/api-client.ts
@@ -234,6 +234,24 @@ export interface IndexBuildResponse {
path: string;
}
+export interface GhostCompleteRequest {
+ filePath: string;
+ language: string;
+ /** Text before the cursor, already windowed by the caller. */
+ prefix: string;
+ /** Text after the cursor, already windowed by the caller. */
+ suffix: string;
+ provider?: string;
+ model?: string;
+}
+
+export interface GhostCompleteResponse {
+ completion: string;
+ model_name: string;
+ /** The daemon clipped the model's answer at its line cap. */
+ truncated: boolean;
+}
+
export class VibeCLIClient {
private baseUrl: string;
private explicitToken?: string;
@@ -280,6 +298,31 @@ export class VibeCLIClient {
}
}
+ /**
+ * Explicit-trigger inline completion for the cursor position.
+ *
+ * Called only from `ghost-text.ts`, which gates on VS Code's *Invoke*
+ * trigger kind. There is no automatic caller — see that file's header.
+ */
+ async ghostComplete(req: GhostCompleteRequest): Promise {
+ const res = await this.authedFetch(`${this.baseUrl}/v1/ghost/complete`, {
+ method: 'POST',
+ headers: { 'Content-Type': 'application/json' },
+ body: JSON.stringify({
+ file_path: req.filePath,
+ language: req.language,
+ prefix: req.prefix,
+ suffix: req.suffix,
+ provider: req.provider,
+ model: req.model,
+ }),
+ });
+ if (!res.ok) {
+ throw new Error(`Inline completion failed: ${res.status} ${await res.text()}`);
+ }
+ return await res.json() as GhostCompleteResponse;
+ }
+
/** Single-turn chat (non-streaming). */
async chat(messages: ChatMessage[]): Promise {
const res = await this.authedFetch(`${this.baseUrl}/chat`, {
diff --git a/vscode-extension/src/extension.ts b/vscode-extension/src/extension.ts
index 6671445e3..f2d7fc637 100644
--- a/vscode-extension/src/extension.ts
+++ b/vscode-extension/src/extension.ts
@@ -16,6 +16,7 @@ import {
type JobRecord,
} from './api-client';
import { GoalsTreeProvider, GoalTreeItem } from './goals-tree';
+import { registerGhostText } from './ghost-text';
import { gatePromptSubmission } from './hook-executor';
import { startRecording, type RecordingHandle } from './voice-capture';
@@ -42,6 +43,17 @@ export async function activate(context: vscode.ExtensionContext): Promise
// Try connecting to the daemon
await tryConnect();
+ // Ghost text — explicit-trigger inline completion (alt+\). Registers its
+ // own command; the provider answers only VS Code's Invoke trigger kind, so
+ // typing never issues a request. See ghost-text.ts.
+ registerGhostText(context, {
+ client: () => (daemonConnected ? client : null),
+ getProvider: () => vscode.workspace.getConfiguration('vibecli').get('provider', ''),
+ getModel: () => vscode.workspace.getConfiguration('vibecli').get('model', ''),
+ showError: (message) => vscode.window.showWarningMessage(`VibeCLI: ${message}`),
+ showInfo: (message) => vscode.window.setStatusBarMessage(`VibeCLI: ${message}`, 5000),
+ });
+
// Register commands
context.subscriptions.push(
vscode.commands.registerCommand('vibecli.connectDaemon', handleConnectDaemon),
diff --git a/vscode-extension/src/ghost-text.ts b/vscode-extension/src/ghost-text.ts
new file mode 100644
index 000000000..dd400b465
--- /dev/null
+++ b/vscode-extension/src/ghost-text.ts
@@ -0,0 +1,188 @@
+/**
+ * Ghost text — explicit-trigger inline completion for VS Code.
+ *
+ * # The gate
+ *
+ * VS Code calls an inline completion provider for both automatic (typing) and
+ * explicit (user gesture) triggers. This provider answers only the explicit
+ * one. That check is the entire reason this surface is not the keystroke-driven
+ * one removed in `5a7eef7c`, so it does not get relaxed.
+ *
+ * **The enum is inverted between our two editors.** VS Code has
+ * `InlineCompletionTriggerKind = { Invoke: 0, Automatic: 1 }`; Monaco has
+ * `{ Automatic: 0, Explicit: 1 }`. The same names carry opposite numbers, so a
+ * literal `=== 1` copied from the VibeCoder provider (`src/lib/ghostText.ts`)
+ * would gate on exactly the wrong half and fire on every keystroke. Always
+ * compare against the named member.
+ *
+ * There is no debounce timer and no edit-history buffer here — one request per
+ * user gesture, carrying only the window around the cursor.
+ */
+import * as vscode from 'vscode';
+import { VibeCLIClient } from './api-client';
+
+/** Lines of context sent before the cursor. */
+export const PREFIX_LINES = 160;
+/** Lines of context sent after the cursor. */
+export const SUFFIX_LINES = 60;
+
+/** The bounded window around a cursor position. */
+export interface ContextWindow {
+ prefix: string;
+ suffix: string;
+ /** Text between the cursor and the end of its line. */
+ restOfLine: string;
+}
+
+/**
+ * The slice of `vscode.TextDocument` this module needs. Narrowed so the pure
+ * helpers below can be tested without a live editor.
+ */
+export interface WindowableDocument {
+ lineCount: number;
+ getText(range?: vscode.Range): string;
+ lineAt(line: number): { range: vscode.Range; text: string };
+}
+
+/** Slice the bounded window around the cursor. */
+export function windowContext(
+ document: WindowableDocument,
+ position: vscode.Position,
+ makeRange: (sl: number, sc: number, el: number, ec: number) => vscode.Range,
+): ContextWindow {
+ const firstLine = Math.max(0, position.line - PREFIX_LINES);
+ const lastLine = Math.min(document.lineCount - 1, position.line + SUFFIX_LINES);
+ const lineEnd = document.lineAt(position.line).range.end;
+ const lastLineEnd = document.lineAt(lastLine).range.end;
+
+ return {
+ prefix: document.getText(
+ makeRange(firstLine, 0, position.line, position.character),
+ ),
+ suffix: document.getText(
+ makeRange(position.line, position.character, lastLine, lastLineEnd.character),
+ ),
+ restOfLine: document.getText(
+ makeRange(position.line, position.character, position.line, lineEnd.character),
+ ),
+ };
+}
+
+/**
+ * Constrain a completion so it renders correctly at the cursor.
+ *
+ * A multi-line suggestion cannot be shown when real code follows the cursor on
+ * the same line, so it is clipped to its first line. Returns `null` when
+ * nothing renderable remains.
+ */
+export function fitCompletionToLine(
+ completion: string,
+ restOfLine: string,
+): string | null {
+ if (completion.length === 0) return null;
+ if (restOfLine.trim().length === 0) return completion;
+
+ const firstLine = completion.split('\n')[0];
+ return firstLine.trim().length === 0 ? null : firstLine;
+}
+
+/** What the provider needs from the extension host. */
+export interface GhostTextDeps {
+ client: () => VibeCLIClient | null;
+ /** `vibecli.provider` / `vibecli.model` settings, empty when unset. */
+ getProvider: () => string;
+ getModel: () => string;
+ showError: (message: string) => void;
+ showInfo: (message: string) => void;
+}
+
+/**
+ * Build the provider. Exported separately from `registerGhostText` so tests
+ * can drive `provideInlineCompletionItems` directly.
+ */
+export function createGhostTextProvider(
+ deps: GhostTextDeps,
+): vscode.InlineCompletionItemProvider {
+ return {
+ async provideInlineCompletionItems(document, position, context, token) {
+ // ── The gate. Read this file's header before changing it. ──
+ if (context.triggerKind !== vscode.InlineCompletionTriggerKind.Invoke) {
+ return undefined;
+ }
+
+ const client = deps.client();
+ if (!client) {
+ deps.showError('Not connected to the VibeCLI daemon.');
+ return undefined;
+ }
+
+ const { prefix, suffix, restOfLine } = windowContext(
+ document,
+ position,
+ (sl, sc, el, ec) => new vscode.Range(sl, sc, el, ec),
+ );
+
+ let response;
+ try {
+ response = await client.ghostComplete({
+ filePath: document.uri.fsPath,
+ language: document.languageId,
+ prefix,
+ suffix,
+ provider: deps.getProvider() || undefined,
+ model: deps.getModel() || undefined,
+ });
+ } catch (error) {
+ deps.showError(
+ error instanceof Error ? error.message : String(error),
+ );
+ return undefined;
+ }
+
+ if (token.isCancellationRequested) return undefined;
+
+ // An empty completion is the model declining, not a failure.
+ const text = fitCompletionToLine(response.completion, restOfLine);
+ if (text === null) return undefined;
+
+ if (response.truncated) {
+ // The cap lives in `vibe_ai::ghost`; don't restate the number, it
+ // would go stale silently.
+ deps.showInfo('Suggestion was clipped — accept it and re-trigger for more.');
+ }
+
+ return [
+ new vscode.InlineCompletionItem(
+ text,
+ new vscode.Range(position, position),
+ ),
+ ];
+ },
+ };
+}
+
+/**
+ * Register the provider for every file and wire the explicit trigger command.
+ *
+ * Registering on `'*'` is safe precisely because of the trigger gate: the
+ * provider is consulted constantly and answers only when asked.
+ */
+export function registerGhostText(
+ context: vscode.ExtensionContext,
+ deps: GhostTextDeps,
+): void {
+ context.subscriptions.push(
+ vscode.languages.registerInlineCompletionItemProvider(
+ { pattern: '**' },
+ createGhostTextProvider(deps),
+ ),
+ vscode.commands.registerCommand('vibecli.ghostComplete', async () => {
+ // The built-in trigger action is what reaches the provider with
+ // `Invoke`; calling the provider ourselves would bypass VS Code's
+ // ghost-text rendering and its Tab-to-accept binding.
+ await vscode.commands.executeCommand(
+ 'editor.action.inlineSuggest.trigger',
+ );
+ }),
+ );
+}