diff --git a/.github/workflows/desktop-build.yml b/.github/workflows/desktop-build.yml index df039d90e..e95d8e742 100644 --- a/.github/workflows/desktop-build.yml +++ b/.github/workflows/desktop-build.yml @@ -75,26 +75,20 @@ jobs: EVENT_NAME: ${{ github.event_name }} GIT_REF: ${{ github.ref }} run: | - # Keep one artifact per OS to stay under the repo's tight artifact quota. - # macOS arm64 only - Apple Silicon covers current Macs. The Intel (macos-13) - # runner is queue-starved and the x64 cross-compile is unreliable, so it is dropped. - mac='{"host":"macos-14","target":"darwin-arm64","platform_flag":"--mac dmg zip --arm64","group":"mac"}' + mac_arm64='{"host":"macos-14","target":"darwin-arm64","platform_flag":"--mac dmg zip --arm64","group":"mac"}' + mac_x64='{"host":"macos-26-intel","target":"darwin-x64","platform_flag":"--mac dmg zip --x64","group":"mac"}' linux='{"host":"ubuntu-24.04","target":"linux-amd64","platform_flag":"--linux deb --x64","group":"linux"}' - # W-01 §2.2.6 win-arm64: both Windows arches are packaged in ONE electron-builder - # invocation so a single latest.yml lists both installers — electron-updater then - # serves x64 and arm64 machines from the same feed (a per-arch matrix split would - # produce two latest.yml files and silently drop one arch from the update feed). - win='{"host":"windows-2025","target":"win-x64-arm64","platform_flag":"--win nsis --x64 --arm64","group":"win"}' + win='{"host":"windows-2025","target":"win-x64","platform_flag":"--win nsis --x64","group":"win"}' # Dev mainline pushes exercise only the Windows packaging path (the newest and # least-runner-covered one); everything else follows the platforms selection. if [ "$EVENT_NAME" = "push" ] && [ "$GIT_REF" = "refs/heads/dev" ]; then items="$win" else case "$PLATFORMS" in - mac) items="$mac" ;; + mac) items="$mac_arm64,$mac_x64" ;; linux) items="$linux" ;; windows) items="$win" ;; - *) items="$mac,$linux,$win" ;; + *) items="$mac_arm64,$mac_x64,$linux,$win" ;; esac fi echo "matrix={\"include\":[$items]}" >> "$GITHUB_OUTPUT" @@ -149,18 +143,6 @@ jobs: tenant-id: ${{ secrets.AZURE_TRUSTED_SIGNING_TENANT_ID }} subscription-id: ${{ secrets.AZURE_TRUSTED_SIGNING_SUBSCRIPTION_ID }} - # Cross-arch packaging: bun only installs the runner's own platform binaries, - # so the arm64 app asar would silently miss its prebuilt native modules - # (@lydell/node-pty-win32-arm64 for the terminal, @parcel/watcher-win32-arm64 - # for file watching). electron-builder filters node_modules by the target - # os/cpu when copying, so installing both here yields each artifact its - # matching set. Versions must match packages/desktop/package.json - # optionalDependencies. - - name: Install arm64 Windows platform packages - if: matrix.group == 'win' - shell: pwsh - run: npm install --no-save --no-package-lock --no-audit --no-fund "@lydell/node-pty-win32-arm64@1.2.0-beta.12" "@parcel/watcher-win32-arm64@2.5.1" - # bun run build invokes the package prebuild lifecycle before electron-vite. - name: Build renderer working-directory: packages/desktop @@ -192,11 +174,8 @@ jobs: AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE: ${{ secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }} run: npx electron-builder ${{ matrix.platform_flag }} --publish never --config electron-builder.config.ts - # W-01 §2.2.6 Windows smoke floor: the packaged exe launch below proves the - # x64 app boots, but NSIS-specific regressions (installer script, per-arch - # payload, signing hook) would otherwise only surface on a physical machine. - # Assert both per-arch installers exist, and when Trusted Signing ran, - # assert the installer signature actually validates on Windows. + # Assert that packaging produced the supported x64 installer and, when + # Trusted Signing ran, that its signature validates on Windows. - name: Assert NSIS installers and signatures if: matrix.group == 'win' working-directory: packages/desktop @@ -204,13 +183,12 @@ jobs: run: | $installers = Get-ChildItem -Path dist -Filter "deepagent-code-desktop-win-*.exe" -File $x64 = $installers | Where-Object { $_.Name -eq "deepagent-code-desktop-win-x64.exe" } - $arm64 = $installers | Where-Object { $_.Name -eq "deepagent-code-desktop-win-arm64.exe" } - if (-not $x64 -or -not $arm64) { - Write-Error "NSIS installers missing: expected win-x64 and win-arm64, found: $($installers.Name -join ', ')" + if (-not $x64 -or $installers.Count -ne 1) { + Write-Error "NSIS installers mismatch: expected only win-x64, found: $($installers.Name -join ', ')" exit 1 } if ($env:HAS_TRUSTED_SIGNING -eq "true") { - foreach ($installer in @($x64, $arm64)) { + foreach ($installer in @($x64)) { $signature = Get-AuthenticodeSignature -FilePath $installer.FullName if ($signature.Status -ne "Valid") { Write-Error "$($installer.Name) signature status '$($signature.Status)': $($signature.StatusMessage)" @@ -226,7 +204,7 @@ jobs: if: matrix.group == 'mac' working-directory: packages/desktop run: | - executable=$(find dist/mac-arm64 -maxdepth 4 -type f -path '*/Contents/MacOS/DeepAgent Code*' -perm +111 -print -quit) + executable=$(find dist -maxdepth 5 -type f -path '*/Contents/MacOS/DeepAgent Code*' -perm +111 -print -quit) test -n "$executable" DEEPAGENT_CODE_DESKTOP_EXECUTABLE="$executable" bun run test:subagents-cold-start @@ -241,9 +219,7 @@ jobs: working-directory: packages/desktop shell: pwsh run: | - # win-unpacked holds the x64 app (the arch the x64 runner can execute; - # arm64 lands in win-arm64-unpacked and is covered by the installer - # assertion above). Wildcard matches both prod ("DeepAgent Code.exe") + # win-unpacked holds the x64 app. Wildcard matches both prod ("DeepAgent Code.exe") # and beta ("DeepAgent Code Beta.exe") artifact names so the beta build # cannot silently fail this smoke because of an exact name filter. $exe = Get-ChildItem -Path "dist/win-unpacked" -Filter "*DeepAgent Code*.exe" -Recurse -File | Select-Object -First 1 @@ -277,12 +253,26 @@ jobs: steps: - uses: actions/checkout@v4 + - uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 + with: + bun-version-file: package.json + - uses: actions/download-artifact@v4 with: pattern: deepagent-code-desktop-* path: release-assets merge-multiple: true + - uses: actions/download-artifact@v4 + with: + name: deepagent-code-desktop-darwin-arm64 + path: /tmp/latest-yml-aarch64-apple-darwin + + - uses: actions/download-artifact@v4 + with: + name: deepagent-code-desktop-darwin-x64 + path: /tmp/latest-yml-x86_64-apple-darwin + - name: Publish latest desktop release env: GH_TOKEN: ${{ github.token }} @@ -293,6 +283,13 @@ jobs: version=$(node -p 'require("./packages/desktop/package.json").version.replace(/-.*/, "")') tag="app-v${version}-main.${GITHUB_RUN_NUMBER}" + # The two native Mac jobs each write latest-mac.yml; publish one feed + # containing both supported architectures instead of whichever artifact + # happened to win the merge-multiple download. + LATEST_YML_DIR=/tmp GH_REPO="$REPOSITORY" DEEPAGENT_CODE_VERSION="$version" \ + RUNNER_TEMP="$RUNNER_TEMP" bun packages/desktop/scripts/finalize-latest-yml.ts --dry-run + cp "$RUNNER_TEMP/latest-mac.yml" release-assets/latest-mac.yml + mapfile -t assets < <(find release-assets -type f \( -name 'latest*.yml' -o -name '*.blockmap' -o -name '*.dmg' -o -name '*.zip' -o -name '*.deb' -o -name '*.exe' \) | sort) if [ "${#assets[@]}" -eq 0 ]; then echo "No desktop release assets found" diff --git a/.github/workflows/publish.yml b/.github/workflows/publish.yml index 48f85d503..96ee12779 100644 --- a/.github/workflows/publish.yml +++ b/.github/workflows/publish.yml @@ -23,10 +23,15 @@ on: description: "Override version (optional)" required: false type: string + qualification_run_id: + description: "Completed candidate-SHA qualification run carrying release-qualification-v2 (required for release GO)" + required: false + type: string concurrency: ${{ github.workflow }}-${{ github.ref }}-${{ inputs.version || inputs.bump }} permissions: + actions: read id-token: write contents: write packages: write @@ -60,6 +65,7 @@ jobs: GH_TOKEN: ${{ steps.committer.outputs.token }} DEEPAGENT_CODE_BUMP: ${{ inputs.bump }} DEEPAGENT_CODE_VERSION: ${{ inputs.version }} + DEEPAGENT_CODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || ((inputs.bump || inputs.version) && 'latest') || github.ref_name }} DEEPAGENT_CODE_API_KEY: ${{ secrets.DEEPAGENT_CODE_API_KEY }} GH_REPO: ${{ (github.ref_name == 'beta' && 'deepagent-ltd/deepagent-code-beta') || github.repository }} outputs: @@ -67,6 +73,9 @@ jobs: release: ${{ steps.version.outputs.release }} tag: ${{ steps.version.outputs.tag }} repo: ${{ steps.version.outputs.repo }} + candidate_commit: ${{ steps.version.outputs.candidate_commit }} + candidate_tree: ${{ steps.version.outputs.candidate_tree }} + channel: ${{ steps.version.outputs.channel }} build-cli: needs: version @@ -75,6 +84,7 @@ jobs: steps: - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 with: + ref: ${{ needs.version.outputs.candidate_commit }} fetch-tags: true - uses: ./.github/actions/setup-bun @@ -86,23 +96,6 @@ jobs: deepagent-code-app-id: ${{ vars.DEEPAGENT_CODE_APP_ID }} deepagent-code-app-secret: ${{ secrets.DEEPAGENT_CODE_APP_SECRET }} - - name: Build (release identity pinned) - id: build - run: | - ./packages/deepagent-code/script/build.ts ${{ (github.ref_name == 'beta' && '--sourcemaps') || '' }} - ./packages/cli/script/build.ts ${{ (github.ref_name == 'beta' && '--sourcemaps') || '' }} - # Ship the authorization row at every binary's appRoot (dirname of the executable — - # V2OwnerSeed.defaultOwnerAuthorizationAppRoot) so default installs self-qualify. - for dir in packages/deepagent-code/dist/deepagent-code-*/bin packages/cli/dist/*/bin; do - [ -d "$dir" ] && cp "$RUNNER_TEMP/owner-authorization.json" "$dir/owner-authorization.json" - done - env: - DEEPAGENT_CODE_VERSION: ${{ needs.version.outputs.version }} - DEEPAGENT_CODE_RELEASE: ${{ needs.version.outputs.release }} - DEEPAGENT_CODE_RELEASE_OWNER_PUBLIC_KEY: ${{ runner.temp }}/release-owner-public-key.pem - GH_REPO: ${{ needs.version.outputs.repo }} - GH_TOKEN: ${{ steps.committer.outputs.token }} - # Owner authorization (2026-09-11 ruling): mint with a ONE-TIME Ed25519 pair BEFORE the # binaries build — the public half is pinned into this exact build via the # DEEPAGENT_CODE_RELEASE_OWNER_PUBLIC_KEY define and the private half dies with the mint @@ -122,23 +115,61 @@ jobs: jq -r '.public_key_pem' "$RUNNER_TEMP/mint-result.json" > "$RUNNER_TEMP/release-owner-public-key.pem" test -s "$RUNNER_TEMP/release-owner-public-key.pem" + - name: Build (release identity pinned) + id: build + run: | + ./packages/deepagent-code/script/build.ts --exclude-win32 ${{ (needs.version.outputs.channel == 'beta' && '--sourcemaps') || '' }} + ./packages/cli/script/build.ts ${{ (needs.version.outputs.channel == 'beta' && '--sourcemaps') || '' }} + # Ship the authorization row at every binary's appRoot (dirname of the executable — + # V2OwnerSeed.defaultOwnerAuthorizationAppRoot) so default installs self-qualify. + for dir in packages/deepagent-code/dist/deepagent-code-*/bin packages/cli/dist/*/bin; do + [ -d "$dir" ] && cp "$RUNNER_TEMP/owner-authorization.json" "$dir/owner-authorization.json" + done + env: + DEEPAGENT_CODE_VERSION: ${{ needs.version.outputs.version }} + DEEPAGENT_CODE_CHANNEL: ${{ needs.version.outputs.channel }} + DEEPAGENT_CODE_RELEASE: ${{ needs.version.outputs.release }} + DEEPAGENT_CODE_RELEASE_OWNER_AUTHORIZATION_FILE: ${{ runner.temp }}/owner-authorization.json + DEEPAGENT_CODE_RELEASE_OWNER_PUBLIC_KEY: ${{ runner.temp }}/release-owner-public-key.pem + GH_REPO: ${{ needs.version.outputs.repo }} + GH_TOKEN: ${{ steps.committer.outputs.token }} + + - name: Verify frozen CLI release candidate + if: needs.version.outputs.release + env: + GH_TOKEN: ${{ steps.committer.outputs.token }} + run: | + bun script/assert-release-candidate.ts \ + --commit "${{ needs.version.outputs.candidate_commit }}" \ + --tree "${{ needs.version.outputs.candidate_tree }}" \ + --tag "v${{ needs.version.outputs.version }}" \ + --release-id "${{ needs.version.outputs.release }}" \ + --release-repo "${{ needs.version.outputs.repo }}" \ + --package-dir packages/deepagent-code/dist/deepagent-code-linux-x64 + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: owner-authorization path: ${{ runner.temp }}/owner-authorization.json if-no-files-found: warn + # Public half of the ephemeral release owner key: the native Windows build job pins the + # same key into its binaries via DEEPAGENT_CODE_RELEASE_OWNER_PUBLIC_KEY, keeping one + # build identity across every platform artifact. Public material — safe as an artifact. + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: release-owner-public-key + path: ${{ runner.temp }}/release-owner-public-key.pem + if-no-files-found: warn + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: deepagent-code-cli path: | packages/deepagent-code/dist/deepagent-code-darwin* packages/deepagent-code/dist/deepagent-code-linux* - - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 - with: - name: deepagent-code-cli-windows - path: packages/deepagent-code/dist/deepagent-code-windows* + packages/deepagent-code/dist/deepagent-code-darwin*.zip + packages/deepagent-code/dist/deepagent-code-linux*.tar.gz - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: @@ -148,9 +179,67 @@ jobs: outputs: version: ${{ needs.version.outputs.version }} - sign-cli-windows: + # Windows binaries build NATIVELY on a Windows runner: bun cross-compilation with the large + # embedded web UI produced non-starting Windows PEs when the build host was macOS (verified on + # the acceptance box), and native builds have been validated end-to-end there dozens of times. + # The build script's own smoke test actually executes the exe here, so a broken build fails CI. + build-cli-windows: needs: + - version - build-cli + runs-on: blacksmith-4vcpu-windows-2025 + if: github.repository == 'deepagent-ltd/deepagent-code' + steps: + - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + with: + ref: ${{ needs.version.outputs.candidate_commit }} + fetch-tags: true + + - uses: ./.github/actions/setup-bun + + - name: Setup git committer + id: committer + uses: ./.github/actions/setup-git-committer + with: + deepagent-code-app-id: ${{ vars.DEEPAGENT_CODE_APP_ID }} + deepagent-code-app-secret: ${{ secrets.DEEPAGENT_CODE_APP_SECRET }} + + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: owner-authorization + path: ${{ runner.temp }}/owner-auth + + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: release-owner-public-key + path: ${{ runner.temp }}/owner-key + + - name: Build (release identity pinned, native) + shell: bash + run: | + bun install + cd packages/deepagent-code + bun script/build.ts --single --baseline ${{ (needs.version.outputs.channel == 'beta' && '--sourcemaps') || '' }} + for dir in dist/deepagent-code-*/bin; do + [ -d "$dir" ] && cp "$RUNNER_TEMP/owner-auth/owner-authorization.json" "$dir/owner-authorization.json" + done + env: + DEEPAGENT_CODE_VERSION: ${{ needs.version.outputs.version }} + DEEPAGENT_CODE_CHANNEL: ${{ needs.version.outputs.channel }} + DEEPAGENT_CODE_RELEASE: ${{ needs.version.outputs.release }} + DEEPAGENT_CODE_RELEASE_OWNER_AUTHORIZATION_FILE: ${{ runner.temp }}/owner-auth/owner-authorization.json + DEEPAGENT_CODE_RELEASE_OWNER_PUBLIC_KEY: ${{ runner.temp }}/owner-key/release-owner-public-key.pem + GH_REPO: ${{ needs.version.outputs.repo }} + GH_TOKEN: ${{ steps.committer.outputs.token }} + + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: deepagent-code-cli-windows + path: packages/deepagent-code/dist/deepagent-code-windows* + + sign-cli-windows: + needs: + - build-cli-windows - version runs-on: blacksmith-4vcpu-windows-2025 if: github.repository == 'deepagent-ltd/deepagent-code' @@ -163,6 +252,8 @@ jobs: AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }} steps: - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + with: + ref: ${{ needs.version.outputs.candidate_commit }} - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: @@ -189,7 +280,6 @@ jobs: signing-account-name: ${{ env.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }} certificate-profile-name: ${{ env.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE }} files: | - ${{ github.workspace }}\packages\deepagent-code\dist\deepagent-code-windows-arm64\bin\deepagent-code.exe ${{ github.workspace }}\packages\deepagent-code\dist\deepagent-code-windows-x64\bin\deepagent-code.exe ${{ github.workspace }}\packages\deepagent-code\dist\deepagent-code-windows-x64-baseline\bin\deepagent-code.exe exclude-environment-credential: true @@ -207,7 +297,6 @@ jobs: shell: pwsh run: | $files = @( - "${{ github.workspace }}\packages\deepagent-code\dist\deepagent-code-windows-arm64\bin\deepagent-code.exe", "${{ github.workspace }}\packages\deepagent-code\dist\deepagent-code-windows-x64\bin\deepagent-code.exe", "${{ github.workspace }}\packages\deepagent-code\dist\deepagent-code-windows-x64-baseline\bin\deepagent-code.exe" ) @@ -223,34 +312,26 @@ jobs: working-directory: packages/deepagent-code/dist shell: pwsh run: | - Compress-Archive -Path "deepagent-code-windows-arm64\bin\*" -DestinationPath "deepagent-code-windows-arm64.zip" -Force Compress-Archive -Path "deepagent-code-windows-x64\bin\*" -DestinationPath "deepagent-code-windows-x64.zip" -Force Compress-Archive -Path "deepagent-code-windows-x64-baseline\bin\*" -DestinationPath "deepagent-code-windows-x64-baseline.zip" -Force - - name: Upload signed Windows CLI release assets - if: needs.version.outputs.release != '' - shell: pwsh - env: - GH_TOKEN: ${{ steps.committer.outputs.token }} - run: | - gh release upload "v${{ needs.version.outputs.version }}" ` - "${{ github.workspace }}\packages\deepagent-code\dist\deepagent-code-windows-arm64.zip" ` - "${{ github.workspace }}\packages\deepagent-code\dist\deepagent-code-windows-x64.zip" ` - "${{ github.workspace }}\packages\deepagent-code\dist\deepagent-code-windows-x64-baseline.zip" ` - --clobber ` - --repo "${{ needs.version.outputs.repo }}" - - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: deepagent-code-cli-signed-windows path: | - packages/deepagent-code/dist/deepagent-code-windows-arm64 packages/deepagent-code/dist/deepagent-code-windows-x64 packages/deepagent-code/dist/deepagent-code-windows-x64-baseline + - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + if: needs.version.outputs.release + with: + name: deepagent-code-cli-signed-windows-archives + path: packages/deepagent-code/dist/deepagent-code-windows*.zip + build-electron: needs: - build-cli + - build-cli-windows - version if: github.repository == 'deepagent-ltd/deepagent-code' continue-on-error: false @@ -273,22 +354,17 @@ jobs: target: aarch64-apple-darwin platform_flag: --mac --arm64 bun_install_flags: --os=darwin --cpu=arm64 - # github-hosted: blacksmith lacks ARM64 MSVC cross-compilation toolchain - - host: "windows-2025" - target: aarch64-pc-windows-msvc - platform_flag: --win --arm64 - host: "blacksmith-4vcpu-windows-2025" target: x86_64-pc-windows-msvc platform_flag: --win - host: "blacksmith-4vcpu-ubuntu-2404" target: x86_64-unknown-linux-gnu platform_flag: --linux - - host: "blacksmith-4vcpu-ubuntu-2404-arm" - target: aarch64-unknown-linux-gnu - platform_flag: --linux --arm64 runs-on: ${{ matrix.settings.host }} steps: - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + with: + ref: ${{ needs.version.outputs.candidate_commit }} - uses: apple-actions/import-codesign-certs@8f3fb608891dd2244cdab3d69cd68c0d37a7fe93 # v2.0.0 if: runner.os == 'macOS' @@ -346,7 +422,7 @@ jobs: working-directory: packages/desktop env: DEEPAGENT_CODE_VERSION: ${{ needs.version.outputs.version }} - DEEPAGENT_CODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} + DEEPAGENT_CODE_CHANNEL: ${{ (needs.version.outputs.channel == 'beta' && 'beta') || 'prod' }} DEEPAGENT_CODE_CLI_ARTIFACT: ${{ (runner.os == 'Windows' && 'deepagent-code-cli-windows') || 'deepagent-code-cli' }} RUST_TARGET: ${{ matrix.settings.target }} GH_TOKEN: ${{ github.token }} @@ -356,7 +432,7 @@ jobs: run: bun run build working-directory: packages/desktop env: - DEEPAGENT_CODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} + DEEPAGENT_CODE_CHANNEL: ${{ (needs.version.outputs.channel == 'beta' && 'beta') || 'prod' }} SENTRY_AUTH_TOKEN: ${{ secrets.SENTRY_AUTH_TOKEN }} SENTRY_ORG: ${{ vars.SENTRY_ORG }} SENTRY_PROJECT: ${{ vars.WEB_SENTRY_PROJECT }} @@ -371,7 +447,7 @@ jobs: working-directory: packages/desktop timeout-minutes: 60 env: - DEEPAGENT_CODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} + DEEPAGENT_CODE_CHANNEL: ${{ (needs.version.outputs.channel == 'beta' && 'beta') || 'prod' }} GH_TOKEN: ${{ steps.committer.outputs.token }} CSC_LINK: ${{ secrets.APPLE_CERTIFICATE }} CSC_KEY_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }} @@ -385,7 +461,7 @@ jobs: working-directory: packages/desktop timeout-minutes: 60 env: - DEEPAGENT_CODE_CHANNEL: ${{ (github.ref_name == 'beta' && 'beta') || 'prod' }} + DEEPAGENT_CODE_CHANNEL: ${{ (needs.version.outputs.channel == 'beta' && 'beta') || 'prod' }} - name: Create macOS .app.tar.gz if: runner.os == 'macOS' && needs.version.outputs.release @@ -439,12 +515,21 @@ jobs: needs: - version - build-cli + - build-cli-windows - sign-cli-windows - build-electron - if: always() && !failure() && !cancelled() + if: always() && !failure() && !cancelled() && needs.version.outputs.channel != 'beta' runs-on: blacksmith-4vcpu-ubuntu-2404 + env: + GH_REPO: ${{ needs.version.outputs.repo }} + DEEPAGENT_CODE_VERSION: ${{ needs.version.outputs.version }} + DEEPAGENT_CODE_RELEASE: ${{ needs.version.outputs.release }} + DEEPAGENT_CODE_CANDIDATE_COMMIT: ${{ needs.version.outputs.candidate_commit }} + DEEPAGENT_CODE_CANDIDATE_TREE: ${{ needs.version.outputs.candidate_tree }} steps: - uses: actions/checkout@f43a0e5ff2bd294095638e18286ca9a3d1956744 # v3.6.0 + with: + ref: ${{ needs.version.outputs.candidate_commit }} - uses: ./.github/actions/setup-bun @@ -481,6 +566,12 @@ jobs: name: deepagent-code-cli-signed-windows path: packages/deepagent-code/dist + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + if: needs.version.outputs.release + with: + name: deepagent-code-cli-signed-windows-archives + path: /tmp/signed-cli-windows + - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: name: deepagent-code-preview-cli @@ -512,6 +603,65 @@ jobs: deepagent-code-app-id: ${{ vars.DEEPAGENT_CODE_APP_ID }} deepagent-code-app-secret: ${{ secrets.DEEPAGENT_CODE_APP_SECRET }} + - name: Build SDK from frozen release candidate + if: needs.version.outputs.release + env: + GH_TOKEN: ${{ steps.committer.outputs.token }} + run: | + ./packages/sdk/js/script/build.ts + bun packages/sdk/js/script/verify-build.ts + bun script/assert-release-candidate.ts \ + --commit "${{ needs.version.outputs.candidate_commit }}" \ + --tree "${{ needs.version.outputs.candidate_tree }}" \ + --tag "v${{ needs.version.outputs.version }}" \ + --release-id "${{ needs.version.outputs.release }}" \ + --release-repo "${{ needs.version.outputs.repo }}" \ + --package-dir packages/deepagent-code/dist/deepagent-code-linux-x64 + + - name: Probe frozen packaged runtime + if: needs.version.outputs.release + run: | + mkdir -p "$RUNNER_TEMP/ri51-probe" + DEEPAGENT_CODE_OWNER_AUTHORIZATION="$PWD/packages/deepagent-code/dist/deepagent-code-linux-x64/bin/owner-authorization.json" \ + bun packages/core/script/evidence-ledger/probe-packaged-runs.ts \ + "$PWD/packages/deepagent-code/dist/deepagent-code-linux-x64/bin/deepagent-code" \ + "$RUNNER_TEMP/ri51-probe/runs.json" \ + "$RUNNER_TEMP/ri51-probe/home" \ + "$RUNNER_TEMP/ri51-probe/evidence.json" + + - name: Download reviewed G0-G8 qualification + if: needs.version.outputs.release && inputs.qualification_run_id != '' + env: + GH_TOKEN: ${{ github.token }} + QUALIFICATION_RUN_ID: ${{ inputs.qualification_run_id }} + GH_REPO: ${{ needs.version.outputs.repo }} + CANDIDATE_COMMIT: ${{ needs.version.outputs.candidate_commit }} + CANDIDATE_TREE: ${{ needs.version.outputs.candidate_tree }} + run: | + [[ "$QUALIFICATION_RUN_ID" =~ ^[0-9]+$ ]] + mkdir -p "$RUNNER_TEMP/ri51-qualification" + gh run download "$QUALIFICATION_RUN_ID" --repo "$GH_REPO" \ + --name release-qualification-v2 --dir "$RUNNER_TEMP/ri51-qualification" + gh api "repos/$GH_REPO/actions/runs/$QUALIFICATION_RUN_ID" \ + > "$RUNNER_TEMP/ri51-qualification/source-run.json" + bun packages/core/script/evidence-ledger/verify-qualification-bundle.ts \ + --dir "$RUNNER_TEMP/ri51-qualification" \ + --commit "$CANDIDATE_COMMIT" --tree "$CANDIDATE_TREE" \ + --run-id "$QUALIFICATION_RUN_ID" --repo "$GH_REPO" + + - name: Stage and verify final release archives + if: needs.version.outputs.release + run: | + bun script/release-assets.ts \ + --commit "${{ needs.version.outputs.candidate_commit }}" \ + --tree "${{ needs.version.outputs.candidate_tree }}" \ + --version "${{ needs.version.outputs.version }}" \ + --cli-dist "$PWD/packages/deepagent-code/dist" \ + --windows-archives /tmp/signed-cli-windows \ + --desktop-dir /tmp/desktop \ + --assets-dir "$RUNNER_TEMP/release-assets" \ + --manifest "$RUNNER_TEMP/ri51-probe/release-assets.json" + - name: Cache apt packages (AUR) uses: actions/cache@0057852bfaa89a56745cba8c7296529d2fc39830 # v4.3.0 with: @@ -537,31 +687,83 @@ jobs: # gates are pending and the release is refused — fail-closed by construction. - name: RI-51 authoritative ledger release gate if: needs.version.outputs.release + env: + QUALIFICATION_RUN_ID: ${{ inputs.qualification_run_id }} + GH_REPO: ${{ needs.version.outputs.repo }} + GH_TOKEN: ${{ steps.committer.outputs.token }} run: | + bun script/assert-release-candidate.ts \ + --commit "${{ needs.version.outputs.candidate_commit }}" \ + --tree "${{ needs.version.outputs.candidate_tree }}" \ + --tag "v${{ needs.version.outputs.version }}" \ + --release-id "${{ needs.version.outputs.release }}" \ + --release-repo "${{ needs.version.outputs.repo }}" \ + --package-dir packages/deepagent-code/dist/deepagent-code-linux-x64 mkdir -p /tmp/authoritative-ledger - (cd packages/core && bun run script/evidence-ledger/release-gate.ts --out /tmp/authoritative-ledger/ledger.json) + args=(--out /tmp/authoritative-ledger/ledger.json \ + --package-dir "$PWD/packages/deepagent-code/dist/deepagent-code-linux-x64" \ + --runs "$RUNNER_TEMP/ri51-probe/runs.json" \ + --evidence "$RUNNER_TEMP/ri51-probe/evidence.json" \ + --asset-manifest "$RUNNER_TEMP/ri51-probe/release-assets.json" \ + --assets-dir "$RUNNER_TEMP/release-assets") + if [[ -n "$QUALIFICATION_RUN_ID" ]]; then + args+=(--qualification-dir "$RUNNER_TEMP/ri51-qualification" \ + --qualification-run-id "$QUALIFICATION_RUN_ID" --qualification-repo "$GH_REPO") + fi + (cd packages/core && bun run script/evidence-ledger/release-gate.ts "${args[@]}") - name: Upload authoritative ledger release asset - if: needs.version.outputs.release && !cancelled() + if: needs.version.outputs.release && success() env: GH_TOKEN: ${{ steps.committer.outputs.token }} run: | - if [ -f /tmp/authoritative-ledger/ledger.json ]; then - gh release upload "v${{ needs.version.outputs.version }}" /tmp/authoritative-ledger/ledger.json --clobber --repo "${{ needs.version.outputs.repo }}" || echo "::warning::ledger asset upload skipped" - fi + bun script/assert-release-candidate.ts \ + --commit "${{ needs.version.outputs.candidate_commit }}" \ + --tree "${{ needs.version.outputs.candidate_tree }}" \ + --tag "v${{ needs.version.outputs.version }}" \ + --release-id "${{ needs.version.outputs.release }}" \ + --release-repo "${{ needs.version.outputs.repo }}" + bun packages/core/script/evidence-ledger/verify-ledger-products.ts \ + --ledger /tmp/authoritative-ledger/ledger.json \ + --artifact-dir /tmp/authoritative-ledger/release-evidence-products + tar -czf /tmp/authoritative-ledger/release-evidence-products.tar.gz \ + -C /tmp/authoritative-ledger \ + ledger.json release-evidence-manifest.json release-evidence-products release-evidence + bun script/upload-release-asset.ts \ + /tmp/authoritative-ledger/ledger.json \ + /tmp/authoritative-ledger/release-evidence-products.tar.gz + + - name: Upload CLI release assets + if: needs.version.outputs.release + env: + GH_TOKEN: ${{ steps.committer.outputs.token }} + run: | + bun script/assert-release-candidate.ts \ + --commit "${{ needs.version.outputs.candidate_commit }}" \ + --tree "${{ needs.version.outputs.candidate_tree }}" \ + --tag "v${{ needs.version.outputs.version }}" \ + --release-id "${{ needs.version.outputs.release }}" \ + --release-repo "${{ needs.version.outputs.repo }}" + mapfile -t names < <(jq -r '.assets[] | select(.kind == "cli") | .name' "$RUNNER_TEMP/ri51-probe/release-assets.json") + files=() + for name in "${names[@]}"; do files+=("$RUNNER_TEMP/release-assets/$name"); done + bun script/upload-release-asset.ts "${files[@]}" - name: Upload desktop release assets if: needs.version.outputs.release env: GH_TOKEN: ${{ steps.committer.outputs.token }} run: | - shopt -s nullglob - files=(/tmp/desktop/*.{exe,blockmap,dmg,zip,AppImage,deb,rpm} /tmp/desktop/*.app.tar.gz) - if (( ${#files[@]} == 0 )); then - echo "No desktop release assets found" - exit 1 - fi - gh release upload "v${{ needs.version.outputs.version }}" "${files[@]}" --clobber --repo "${{ needs.version.outputs.repo }}" + bun script/assert-release-candidate.ts \ + --commit "${{ needs.version.outputs.candidate_commit }}" \ + --tree "${{ needs.version.outputs.candidate_tree }}" \ + --tag "v${{ needs.version.outputs.version }}" \ + --release-id "${{ needs.version.outputs.release }}" \ + --release-repo "${{ needs.version.outputs.repo }}" + mapfile -t names < <(jq -r '.assets[] | select(.kind == "desktop") | .name' "$RUNNER_TEMP/ri51-probe/release-assets.json") + files=() + for name in "${names[@]}"; do files+=("$RUNNER_TEMP/release-assets/$name"); done + bun script/upload-release-asset.ts "${files[@]}" # W0.5: deliver the signed owner-authorization.json with the release artifacts. The runtime # seed (v2-owner-seed.ts) reads it from the install root; if it is missing the release still @@ -571,20 +773,57 @@ jobs: env: GH_TOKEN: ${{ steps.committer.outputs.token }} run: | - if [ -f /tmp/owner-authorization/owner-authorization.json ]; then - gh release upload "v${{ needs.version.outputs.version }}" /tmp/owner-authorization/owner-authorization.json --clobber --repo "${{ needs.version.outputs.repo }}" - else - echo "::warning::owner-authorization.json was not minted for this release — operations must mint and deliver it before the default install can pass owner qualification" - fi + bun script/assert-release-candidate.ts \ + --commit "${{ needs.version.outputs.candidate_commit }}" \ + --tree "${{ needs.version.outputs.candidate_tree }}" \ + --tag "v${{ needs.version.outputs.version }}" \ + --release-id "${{ needs.version.outputs.release }}" \ + --release-repo "${{ needs.version.outputs.repo }}" + bun script/upload-release-asset.ts "$RUNNER_TEMP/release-assets/owner-authorization.json" + + - name: Read back final release asset bytes + if: needs.version.outputs.release + env: + GH_TOKEN: ${{ steps.committer.outputs.token }} + run: | + bun script/assert-release-candidate.ts \ + --commit "${{ needs.version.outputs.candidate_commit }}" \ + --tree "${{ needs.version.outputs.candidate_tree }}" \ + --tag "v${{ needs.version.outputs.version }}" \ + --release-id "${{ needs.version.outputs.release }}" \ + --release-repo "${{ needs.version.outputs.repo }}" + mkdir -p "$RUNNER_TEMP/release-readback" + gh release download "v${{ needs.version.outputs.version }}" \ + --repo "${{ needs.version.outputs.repo }}" \ + --dir "$RUNNER_TEMP/release-readback" --pattern '*' + bun script/release-assets.ts --verify --allow-ledger-assets \ + --manifest "$RUNNER_TEMP/ri51-probe/release-assets.json" \ + --assets-dir "$RUNNER_TEMP/release-readback" \ + --commit "${{ needs.version.outputs.candidate_commit }}" \ + --tree "${{ needs.version.outputs.candidate_tree }}" + cmp -s "$RUNNER_TEMP/release-readback/ledger.json" /tmp/authoritative-ledger/ledger.json + cmp -s "$RUNNER_TEMP/release-readback/release-evidence-products.tar.gz" \ + /tmp/authoritative-ledger/release-evidence-products.tar.gz + bun script/assert-release-candidate.ts \ + --commit "${{ needs.version.outputs.candidate_commit }}" \ + --tree "${{ needs.version.outputs.candidate_tree }}" \ + --tag "v${{ needs.version.outputs.version }}" \ + --release-id "${{ needs.version.outputs.release }}" \ + --release-repo "${{ needs.version.outputs.repo }}" - run: ./script/publish.ts env: DEEPAGENT_CODE_VERSION: ${{ needs.version.outputs.version }} + DEEPAGENT_CODE_CHANNEL: ${{ needs.version.outputs.channel }} DEEPAGENT_CODE_RELEASE: ${{ needs.version.outputs.release }} + DEEPAGENT_CODE_CANDIDATE_COMMIT: ${{ needs.version.outputs.candidate_commit }} + DEEPAGENT_CODE_CANDIDATE_TREE: ${{ needs.version.outputs.candidate_tree }} AUR_KEY: ${{ secrets.AUR_KEY }} GITHUB_TOKEN: ${{ steps.committer.outputs.token }} GH_REPO: ${{ needs.version.outputs.repo }} NPM_CONFIG_PROVENANCE: false LATEST_YML_DIR: /tmp/latest-yml + RELEASE_ASSETS_DIR: ${{ runner.temp }}/release-assets + RELEASE_LEDGER_PATH: /tmp/authoritative-ledger/ledger.json TAURI_SIGNING_PRIVATE_KEY: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY }} TAURI_SIGNING_PRIVATE_KEY_PASSWORD: ${{ secrets.TAURI_SIGNING_PRIVATE_KEY_PASSWORD }} diff --git a/.github/workflows/release-qualification-v2.yml b/.github/workflows/release-qualification-v2.yml new file mode 100644 index 000000000..4211b4029 --- /dev/null +++ b/.github/workflows/release-qualification-v2.yml @@ -0,0 +1,116 @@ +name: release-qualification-v2 + +on: + workflow_dispatch: + inputs: + candidate_commit: + description: "Exact SHA at release-candidates/v" + required: true + type: string + candidate_tree: + description: "Exact tree SHA for candidate_commit" + required: true + type: string + review_ref: + description: "Optional refs/heads/release-evidence/... branch containing release-qualification/review.json and evidence/" + required: false + type: string + review_commit: + description: "Exact SHA at review_ref (required with review_ref)" + required: false + type: string + +permissions: + contents: read + actions: read + +jobs: + qualification: + if: github.repository == 'deepagent-ltd/deepagent-code' + runs-on: blacksmith-4vcpu-ubuntu-2404 + timeout-minutes: 120 + environment: release-qualification + steps: + - name: Checkout exact dispatch ref + uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1 + with: + fetch-depth: 0 + persist-credentials: true + + - name: Assert frozen candidate and review ref + env: + CANDIDATE_COMMIT: ${{ inputs.candidate_commit }} + CANDIDATE_TREE: ${{ inputs.candidate_tree }} + REVIEW_REF: ${{ inputs.review_ref }} + REVIEW_COMMIT: ${{ inputs.review_commit }} + run: | + set -euo pipefail + [[ "$GITHUB_REF" =~ ^refs/heads/release-candidates/v[0-9]+\.[0-9]+\.[0-9]+([.-][A-Za-z0-9.-]+)?$ ]] + [[ "$CANDIDATE_COMMIT" =~ ^[0-9a-f]{40}$ ]] + [[ "$CANDIDATE_TREE" =~ ^[0-9a-f]{40}$ ]] + [[ "$GITHUB_SHA" == "$CANDIDATE_COMMIT" ]] + [[ "$(git rev-parse HEAD)" == "$CANDIDATE_COMMIT" ]] + [[ "$(git rev-parse 'HEAD^{tree}')" == "$CANDIDATE_TREE" ]] + [[ "$(git ls-remote origin "$GITHUB_REF" | cut -f1)" == "$CANDIDATE_COMMIT" ]] + [[ -z "$(git status --porcelain --untracked-files=all)" ]] + if [[ -n "$REVIEW_REF" || -n "$REVIEW_COMMIT" ]]; then + [[ "$REVIEW_REF" =~ ^refs/heads/release-evidence/[A-Za-z0-9._/-]+$ ]] + [[ "$REVIEW_COMMIT" =~ ^[0-9a-f]{40}$ ]] + [[ "$(git ls-remote origin "$REVIEW_REF" | cut -f1)" == "$REVIEW_COMMIT" ]] + git fetch --no-tags origin "$REVIEW_REF" + [[ "$(git rev-parse FETCH_HEAD)" == "$REVIEW_COMMIT" ]] + mkdir -p "$RUNNER_TEMP/ri51-review" + git archive "$REVIEW_COMMIT" release-qualification | tar -xf - -C "$RUNNER_TEMP/ri51-review" + fi + + - name: Setup Bun + uses: ./.github/actions/setup-bun + with: + install-flags: --frozen-lockfile + + - name: Verify protected reviewer configuration + if: inputs.review_ref != '' || inputs.review_commit != '' + env: + GH_TOKEN: ${{ secrets.RELEASE_QUALIFICATION_REVIEW_TOKEN || github.token }} + GH_REPO: ${{ github.repository }} + run: | + set -euo pipefail + gh api "repos/$GH_REPO/environments/release-qualification" > "$RUNNER_TEMP/ri51-environment.json" + bun packages/core/script/evidence-ledger/produce-qualification-bundle.ts \ + --assert-environment "$RUNNER_TEMP/ri51-environment.json" + + - name: Capture real static checks + env: + CANDIDATE_COMMIT: ${{ inputs.candidate_commit }} + CANDIDATE_TREE: ${{ inputs.candidate_tree }} + run: | + bun packages/core/script/evidence-ledger/run-qualification-checks.ts \ + --commit "$CANDIDATE_COMMIT" --tree "$CANDIDATE_TREE" \ + --out "$RUNNER_TEMP/ri51-checks.json" + + - name: Build content-addressed qualification bundle + env: + CANDIDATE_COMMIT: ${{ inputs.candidate_commit }} + CANDIDATE_TREE: ${{ inputs.candidate_tree }} + REVIEW_REF: ${{ inputs.review_ref }} + REVIEW_COMMIT: ${{ inputs.review_commit }} + run: | + set -euo pipefail + args=() + if [[ -n "$REVIEW_REF" ]]; then + args+=(--review-dir "$RUNNER_TEMP/ri51-review/release-qualification" \ + --review-ref "$REVIEW_REF" --review-commit "$REVIEW_COMMIT") + fi + bun packages/core/script/evidence-ledger/produce-qualification-bundle.ts \ + --out "$RUNNER_TEMP/ri51-qualification" \ + --commit "$CANDIDATE_COMMIT" --tree "$CANDIDATE_TREE" \ + --run-id "$GITHUB_RUN_ID" --repo "$GITHUB_REPOSITORY" \ + --checks "$RUNNER_TEMP/ri51-checks.json" "${args[@]}" + + - name: Upload reviewed qualification + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: release-qualification-v2 + path: ${{ runner.temp }}/ri51-qualification/ + if-no-files-found: error + retention-days: 30 diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 9254e556f..29e8034a0 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -72,10 +72,13 @@ jobs: settings: - name: linux host: blacksmith-4vcpu-ubuntu-2404 + timeout: 20 - name: windows host: blacksmith-4vcpu-windows-2025 + timeout: 20 - name: macos host: macos-14 + timeout: 120 runs-on: ${{ matrix.settings.host }} defaults: run: @@ -109,7 +112,7 @@ jobs: turbo-${{ runner.os }}- - name: Run unit tests - timeout-minutes: 20 + timeout-minutes: ${{ matrix.settings.timeout }} run: bun turbo test:ci --log-order=stream --log-prefix=task env: DEEPAGENT_CODE_EXPERIMENTAL_DISABLE_FILEWATCHER: ${{ runner.os == 'Windows' && 'true' || 'false' }} diff --git a/CHANGELOG.md b/CHANGELOG.md index c655a657b..ef72bc508 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,66 @@ This changelog contains public, user-facing product changes. Internal incident identifiers, local paths, private environment topology, test credentials, release-gate evidence, and operational measurements are intentionally excluded. +## Core 2.0.2 / Desktop 2.0.2 + +Release labels: `core-v2.0.2` and `desktop-v2.0.2` (`2.0.2`). + +### Windows + +- Fixed a startup crash that hit Windows installs on every launch after the one-time data migration had completed, as well as in the migration-guarded and migration-failed paths. +- The interactive TUI now starts on Windows; it previously crashed during the first draw. +- Hardened the CLI runtime and file-search picker against two crash classes found in short-lived Windows commands. + +### Terminal (web & desktop) + +- The terminal panel no longer sticks on "Loading terminal..." when a session opens in a fresh page load: terminals now start automatically within seconds, and a failed readiness probe retries instead of deadlocking the panel. + +### Security and configuration + +- Plaintext MCP server secrets found in config files are now erased exactly where they came from: migration rewrites the originating file in place (comments preserved) after moving each secret into the OS credential store (DPAPI on Windows), transactionally and idempotently — the plaintext no longer survives on disk. +- Instance-level configuration updates made through the HTTP config API now persist to the project config file that is actually loaded, instead of a file nothing reads back. + +### Release engineering + +- Windows release binaries are now built natively on Windows runners in CI instead of cross-compiled; the build pipeline executes the produced executable as a smoke test, so a non-starting Windows build fails the release instead of shipping. +- Embedded web UI builds start from a clean output directory so stale chunks from an interrupted build cannot ship inside the binary. + +### Sessions and history + +- Legacy sessions no longer accept writes through the old surfaces: editing or deleting historical message parts returns typed guidance to adopt the session instead of silently writing the legacy projection, and sharing actions validate session state first, with conflicts surfaced honestly in the UI. +- Manual compaction is honest about outcomes: over-budget summaries are refused with an explicit result instead of being reported as compacted, and a completed run keeps a verifiable receipt. +- Stalled-session challenges survive restarts and resume durably; an expired permission owner rotates instead of deadlocking the session. +- A delegated task's merged PR can be undone through an audited rollback that reverts the review and merge records together with the workspace. + +### Sharing + +- Public web sharing is off by default in this release; it will return in a later version. +- Local session ZIP export/import remains available and now scrubs credential material — cookies, basic-auth headers, and encoded or serialized secret forms — from exported data. + +### Server mode + +- Added a tenant-gated transparent LLM proxy for server deployments: provider traffic flows through per-tenant lanes bound to tool policy, with durable audit events, admission-serialized token accounting, and per-provider-turn usage records. +- The gateway binds its service inventory to the running composition, so config drift cannot serve stale tool surfaces. + +### Agents and integrations + +- Agent pushes deliver to bound Slack channels: bindings are fail-closed (empty, incomplete, or revoked bindings never send), pushes respect rate limits and quiet hours, and retries never double-send. +- `@agent` and `@reference` mentions in prompts render into provider requests through the durable wire path. +- Configured instruction files load into the system context. + +### Providers + +- Kimi connects again after upstream catalog renames: official provider ids are bridged to their renamed catalog entries end to end. +- Mixed-generation config files (V1 `provider` keys alongside the V2 `providers` catalog) no longer break config loading. + +### Desktop fixes + +- An unreachable server now shows a real offline state — naming the server, re-probing automatically, and offering one-click switching to another configured server — instead of a mislabeled maintenance banner over an indefinitely loading app; a server that drops mid-session keeps already-loaded content behind a reconnecting banner and recovers automatically when it returns. +- Intelligence draft preparation that fails on the server degrades visibly: the message is sent directly with a clear notice instead of silently locking the composer. +- The settings dialog always offers a close button, and Escape reliably closes and reopens dialogs. +- New V2 chats resolve the model before the first question, live replies render in directory sessions, and chat lists refresh from the directory journal. +- Tool artifacts use Node file APIs in the packaged desktop app; packaged builds are located on both macOS architectures; plugin settings read from the server context; provider-recovery lists retry transient failures instead of erroring the whole app. + ## Core 2.0.1 / Desktop 2.0.1 Release labels: `core-v2.0.1` and `desktop-v2.0.1` (`2.0.1`). diff --git a/bun.lock b/bun.lock index 331c38401..af820157c 100644 --- a/bun.lock +++ b/bun.lock @@ -30,7 +30,7 @@ }, "packages/app": { "name": "@deepagent-code/app", - "version": "2.0.1", + "version": "2.0.2", "dependencies": { "@codemirror/autocomplete": "6", "@codemirror/commands": "6", @@ -104,7 +104,7 @@ }, "packages/cli": { "name": "@deepagent-code/cli", - "version": "2.0.1", + "version": "2.0.2", "bin": { "lildax": "./bin/lildax.cjs", }, @@ -129,7 +129,7 @@ }, "packages/core": { "name": "@deepagent-code/core", - "version": "2.0.1", + "version": "2.0.2", "dependencies": { "@ai-sdk/alibaba": "1.0.17", "@ai-sdk/amazon-bedrock": "4.0.112", @@ -220,7 +220,7 @@ }, "packages/deepagent-code": { "name": "deepagent-code", - "version": "2.0.1", + "version": "2.0.2", "bin": { "deepagent-code": "./bin/deepagent", "deepagent": "./bin/deepagent", @@ -335,6 +335,7 @@ "@types/yargs": "17.0.33", "@typescript/native-preview": "catalog:", "drizzle-orm": "catalog:", + "openai": "6.39.1", "prettier": "3.6.2", "typescript": "catalog:", "vscode-languageserver-types": "3.17.5", @@ -343,7 +344,7 @@ }, "packages/desktop": { "name": "@deepagent-code/desktop", - "version": "2.0.1", + "version": "2.0.2", "dependencies": { "@lydell/node-pty": "catalog:", "@zip.js/zip.js": "2.7.62", @@ -427,7 +428,7 @@ }, "packages/function": { "name": "@deepagent-code/function", - "version": "2.0.1", + "version": "2.0.2", "dependencies": { "@hono/node-server": "2.0.10", "@octokit/auth-app": "8.0.1", @@ -483,7 +484,7 @@ }, "packages/plugin": { "name": "@deepagent-code/plugin", - "version": "2.0.1", + "version": "2.0.2", "dependencies": { "@deepagent-code/sdk": "workspace:*", "effect": "catalog:", @@ -499,9 +500,9 @@ "typescript": "catalog:", }, "peerDependencies": { - "@opentui/core": ">=0.3.2", - "@opentui/keymap": ">=0.3.2", - "@opentui/solid": ">=0.3.2", + "@opentui/core": ">=0.5.12", + "@opentui/keymap": ">=0.5.12", + "@opentui/solid": ">=0.5.12", }, "optionalPeers": [ "@opentui/core", @@ -536,7 +537,7 @@ }, "packages/server": { "name": "@deepagent-code/server", - "version": "2.0.1", + "version": "2.0.2", "dependencies": { "@deepagent-code/core": "workspace:*", "drizzle-orm": "catalog:", @@ -613,7 +614,7 @@ }, "packages/ui": { "name": "@deepagent-code/ui", - "version": "2.0.1", + "version": "2.0.2", "dependencies": { "@deepagent-code/core": "workspace:*", "@deepagent-code/sdk": "workspace:*", @@ -662,7 +663,7 @@ }, "packages/web": { "name": "@deepagent-code/web", - "version": "2.0.1", + "version": "2.0.2", "dependencies": { "@astrojs/markdown-remark": "7.3.1", "@astrojs/node": "11.1.6", @@ -733,9 +734,9 @@ "@npmcli/arborist": "9.4.0", "@octokit/rest": "22.0.0", "@openauthjs/openauth": "0.0.0-20250322224806", - "@opentui/core": "0.3.2", - "@opentui/keymap": "0.3.2", - "@opentui/solid": "0.3.2", + "@opentui/core": "0.5.12", + "@opentui/keymap": "0.5.12", + "@opentui/solid": "0.5.12", "@pierre/diffs": "1.1.0-beta.18", "@playwright/test": "1.59.1", "@sentry/solid": "10.36.0", @@ -1113,8 +1114,6 @@ "@clack/prompts": ["@clack/prompts@1.0.0-alpha.1", "", { "dependencies": { "@clack/core": "1.0.0-alpha.1", "picocolors": "^1.0.0", "sisteransi": "^1.0.5" } }, "sha512-07MNT0OsxjKOcyVfX8KhXBhJiyUbDP1vuIAcHc+nx5v93MJO23pX3X/k3bWz6T3rpM9dgWPq90i4Jq7gZAyMbw=="], - "@cloudflare/workers-types": ["@cloudflare/workers-types@4.20251008.0", "", {}, "sha512-dZLkO4PbCL0qcCSKzuW7KE4GYe49lI12LCfQ5y9XeSwgYBoAUbwH4gmJ6A0qUIURiTJTkGkRkhVPqpq2XNgYRA=="], - "@codemirror/autocomplete": ["@codemirror/autocomplete@6.20.3", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.0.0", "@codemirror/view": "^6.17.0", "@lezer/common": "^1.0.0" } }, "sha512-tlosUqb+3BbxCxZdu4tKeRghPFC+QM7q4X5YhKV2eCmPG+1r2F3f4AaSz5sCrFqUtX4Jh20VFTKecl16MgiV9g=="], "@codemirror/commands": ["@codemirror/commands@6.10.4", "", { "dependencies": { "@codemirror/language": "^6.0.0", "@codemirror/state": "^6.7.0", "@codemirror/view": "^6.27.0", "@lezer/common": "^1.1.0" } }, "sha512-Ryk9y9T0FFVF0cUGhAknveAyUOl/A1qReTFi+qPKtOh2Z9F4AUBz3XOrYD4ZEgZirdugVzHvd/2/Wcwy5OliTg=="], @@ -1241,10 +1240,6 @@ "@emnapi/wasi-threads": ["@emnapi/wasi-threads@1.2.1", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w=="], - "@emotion/is-prop-valid": ["@emotion/is-prop-valid@0.8.8", "", { "dependencies": { "@emotion/memoize": "0.7.4" } }, "sha512-u5WtneEAr5IDG2Wv65yhunPSMLIpuKsbuOktRojfrEiEvRyC85LgPMZI63cr7NUqT8ZIGdSVg8ZKGxIug4lXcA=="], - - "@emotion/memoize": ["@emotion/memoize@0.7.4", "", {}, "sha512-Ja/Vfqe3HpuzRsG1oBtWTHk2PGZ7GR+2Vz5iYGelAw8dx32K0y7PjVuxK6z1nMpZOqAFsRUPCkK1YjJ56qJlgw=="], - "@esbuild/aix-ppc64": ["@esbuild/aix-ppc64@0.25.12", "", { "os": "aix", "cpu": "ppc64" }, "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA=="], "@esbuild/android-arm": ["@esbuild/android-arm@0.25.12", "", { "os": "android", "cpu": "arm" }, "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg=="], @@ -1429,8 +1424,6 @@ "@jridgewell/resolve-uri": ["@jridgewell/resolve-uri@3.1.2", "", {}, "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw=="], - "@jridgewell/source-map": ["@jridgewell/source-map@0.3.11", "", { "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.25" } }, "sha512-ZMp1V8ZFcPG5dIWnQLr3NSI1MiCU7UETdS/A0G8V/XWHvJv3ZsFqutJn1Y5RPmAPX6F3BiE397OqveU/9NCuIA=="], - "@jridgewell/sourcemap-codec": ["@jridgewell/sourcemap-codec@1.5.5", "", {}, "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og=="], "@jridgewell/trace-mapping": ["@jridgewell/trace-mapping@0.3.31", "", { "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw=="], @@ -1471,32 +1464,6 @@ "@lezer/rust": ["@lezer/rust@1.0.2", "", { "dependencies": { "@lezer/common": "^1.2.0", "@lezer/highlight": "^1.0.0", "@lezer/lr": "^1.0.0" } }, "sha512-Lz5sIPBdF2FUXcWeCu1//ojFAZqzTQNRga0aYv6dYXqJqPfMdCAI0NzajWUd4Xijj1IKJLtjoXRPMvTKWBcqKg=="], - "@libsql/client": ["@libsql/client@0.18.0", "", { "dependencies": { "@libsql/core": "^0.18.0", "@libsql/hrana-client": "^0.10.0", "js-base64": "^3.7.5", "libsql": "^0.5.28", "promise-limit": "^2.7.0" } }, "sha512-zMCCo58vv7w27j7+cnt/GW+X6/Rih6cHZ86PKi0AxCMOqMfrVgG9OZ5lW2bDMJhSeOjsv2szH2nJF3A9l36UmA=="], - - "@libsql/core": ["@libsql/core@0.18.0", "", { "dependencies": { "js-base64": "^3.7.5" } }, "sha512-N8RR2CIpcgtbKZnXs2KVpw2lJQfinUj3I56e7qRDRpK18Hty0Rr8nr00VmFb4R/DsqOanPB/TaP3+QAQLZeXMg=="], - - "@libsql/darwin-arm64": ["@libsql/darwin-arm64@0.5.29", "", { "os": "darwin", "cpu": "arm64" }, "sha512-K+2RIB1OGFPYQbfay48GakLhqf3ArcbHqPFu7EZiaUcRgFcdw8RoltsMyvbj5ix2fY0HV3Q3Ioa/ByvQdaSM0A=="], - - "@libsql/darwin-x64": ["@libsql/darwin-x64@0.5.29", "", { "os": "darwin", "cpu": "x64" }, "sha512-OtT+KFHsKFy1R5FVadr8FJ2Bb1mghtXTyJkxv0trocq7NuHntSki1eUbxpO5ezJesDvBlqFjnWaYYY516QNLhQ=="], - - "@libsql/hrana-client": ["@libsql/hrana-client@0.10.0", "", { "dependencies": { "@libsql/isomorphic-ws": "^0.1.5", "js-base64": "^3.7.5" } }, "sha512-OoA4EMqRAC7kn7V2P6EQqRcpZf2W+AjsNIyCizBg339Tq/aMC7sRnzs3SklderhmQWAqEzvv8A2vhxVmWpkVvw=="], - - "@libsql/isomorphic-ws": ["@libsql/isomorphic-ws@0.1.5", "", { "dependencies": { "@types/ws": "^8.5.4", "ws": "^8.13.0" } }, "sha512-DtLWIH29onUYR00i0GlQ3UdcTRC6EP4u9w/h9LxpUZJWRMARk6dQwZ6Jkd+QdwVpuAOrdxt18v0K2uIYR3fwFg=="], - - "@libsql/linux-arm-gnueabihf": ["@libsql/linux-arm-gnueabihf@0.5.29", "", { "os": "linux", "cpu": "arm" }, "sha512-CD4n4zj7SJTHso4nf5cuMoWoMSS7asn5hHygsDuhRl8jjjCTT3yE+xdUvI4J7zsyb53VO5ISh4cwwOtf6k2UhQ=="], - - "@libsql/linux-arm-musleabihf": ["@libsql/linux-arm-musleabihf@0.5.29", "", { "os": "linux", "cpu": "arm" }, "sha512-2Z9qBVpEJV7OeflzIR3+l5yAd4uTOLxklScYTwpZnkm2vDSGlC1PRlueLaufc4EFITkLKXK2MWBpexuNJfMVcg=="], - - "@libsql/linux-arm64-gnu": ["@libsql/linux-arm64-gnu@0.5.29", "", { "os": "linux", "cpu": "arm64" }, "sha512-gURBqaiXIGGwFNEaUj8Ldk7Hps4STtG+31aEidCk5evMMdtsdfL3HPCpvys+ZF/tkOs2MWlRWoSq7SOuCE9k3w=="], - - "@libsql/linux-arm64-musl": ["@libsql/linux-arm64-musl@0.5.29", "", { "os": "linux", "cpu": "arm64" }, "sha512-fwgYZ0H8mUkyVqXZHF3mT/92iIh1N94Owi/f66cPVNsk9BdGKq5gVpoKO+7UxaNzuEH1roJp2QEwsCZMvBLpqg=="], - - "@libsql/linux-x64-gnu": ["@libsql/linux-x64-gnu@0.5.29", "", { "os": "linux", "cpu": "x64" }, "sha512-y14V0vY0nmMC6G0pHeJcEarcnGU2H6cm21ZceRkacWHvQAEhAG0latQkCtoS2njFOXiYIg+JYPfAoWKbi82rkg=="], - - "@libsql/linux-x64-musl": ["@libsql/linux-x64-musl@0.5.29", "", { "os": "linux", "cpu": "x64" }, "sha512-gquqwA/39tH4pFl+J9n3SOMSymjX+6kZ3kWgY3b94nXFTwac9bnFNMffIomgvlFaC4ArVqMnOZD3nuJ3H3VO1w=="], - - "@libsql/win32-x64-msvc": ["@libsql/win32-x64-msvc@0.5.29", "", { "os": "win32", "cpu": "x64" }, "sha512-4/0CvEdhi6+KjMxMaVbFM2n2Z44escBRoEYpR+gZg64DdetzGnYm8mcNLcoySaDJZNaBd6wz5DNdgRmcI4hXcg=="], - "@lukeed/ms": ["@lukeed/ms@2.0.2", "", {}, "sha512-9I2Zn6+NJLfaGoz9jN3lpwDgAYvfGeNYdbAIjJOqzs4Tpc+VU3Jqq4IofSUBKajiDS8k9fZIg18/z13mpk1bsA=="], "@lydell/node-pty": ["@lydell/node-pty@1.2.0-beta.12", "", { "optionalDependencies": { "@lydell/node-pty-darwin-arm64": "1.2.0-beta.12", "@lydell/node-pty-darwin-x64": "1.2.0-beta.12", "@lydell/node-pty-linux-arm64": "1.2.0-beta.12", "@lydell/node-pty-linux-x64": "1.2.0-beta.12", "@lydell/node-pty-win32-arm64": "1.2.0-beta.12", "@lydell/node-pty-win32-x64": "1.2.0-beta.12" } }, "sha512-qIK890UwPupoj07osVvgOIa++1mxeHbcGry4PKRHhNVNs81V2SCG34eJr46GybiOmBtc8Sj5PB1/GGM5PL549g=="], @@ -1541,8 +1508,6 @@ "@napi-rs/wasm-runtime": ["@napi-rs/wasm-runtime@1.1.4", "", { "dependencies": { "@tybys/wasm-util": "^0.10.1" }, "peerDependencies": { "@emnapi/core": "^1.7.1", "@emnapi/runtime": "^1.7.1" } }, "sha512-3NQNNgA1YSlJb/kMH1ildASP9HW7/7kYnRI2szWJaofaS1hWmbGI4H+d3+22aGzXXN9IJ+n+GiFVcGipJP18ow=="], - "@neon-rs/load": ["@neon-rs/load@0.0.4", "", {}, "sha512-kTPhdZyTQxB+2wpiRcFWrDcejc4JI6tkPuS7UZCG4l6Zvc5kU/gGQ/ozvHTh1XR5tS+UlfAfGuPajjzQjCiHCw=="], - "@nodable/entities": ["@nodable/entities@2.1.1", "", {}, "sha512-Pig3HxDIoMgjdEH8OCf/dkcTmLFjJRjWuq8jSnklu284/TKOPibSRERmOykiwmyXTtv61mP+44f3GMx0tLAyjg=="], "@npm/types": ["@npm/types@1.0.2", "", {}, "sha512-KXZccTDEnWqNrrx6JjpJKU/wJvNeg9BDgjS0XhmlZab7br921HtyVbsYzJr4L+xIvjdJ20Wh9dgxgCI2a5CEQw=="], @@ -1647,27 +1612,27 @@ "@opentelemetry/semantic-conventions": ["@opentelemetry/semantic-conventions@1.41.1", "", {}, "sha512-/UhIkaZgPutTFmQ7RnIJGgDXZmtEJ7Dvi86xNTFWcnRxVRNk/aotsqDJYeEvDP+FSMB2SdW+pQzNMcWP0rwuNA=="], - "@opentui/core": ["@opentui/core@0.3.2", "", { "dependencies": { "bun-ffi-structs": "0.2.2", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2", "yoga-layout": "3.2.1" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.3.2", "@opentui/core-darwin-x64": "0.3.2", "@opentui/core-linux-arm64": "0.3.2", "@opentui/core-linux-arm64-musl": "0.3.2", "@opentui/core-linux-x64": "0.3.2", "@opentui/core-linux-x64-musl": "0.3.2", "@opentui/core-win32-arm64": "0.3.2", "@opentui/core-win32-x64": "0.3.2" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-5rCVS/3Obb3iLqg/egLCRArt7hAu3lX/9PWVHqUlnJylCT6b5NYDFljt0r3x8v3VG98LB71UzpnDv7DgmGKATw=="], + "@opentui/core": ["@opentui/core@0.5.12", "", { "dependencies": { "bun-ffi-structs": "0.3.1", "diff": "9.0.0", "marked": "17.0.1", "string-width": "7.2.0", "strip-ansi": "7.1.2" }, "optionalDependencies": { "@opentui/core-darwin-arm64": "0.5.12", "@opentui/core-darwin-x64": "0.5.12", "@opentui/core-linux-arm64": "0.5.12", "@opentui/core-linux-arm64-musl": "0.5.12", "@opentui/core-linux-x64": "0.5.12", "@opentui/core-linux-x64-musl": "0.5.12", "@opentui/core-win32-arm64": "0.5.12", "@opentui/core-win32-x64": "0.5.12" }, "peerDependencies": { "web-tree-sitter": "0.25.10" } }, "sha512-ZXBE5gmvdovmV8zJQrOQf6E44v1tJRDEgrM2MYhEglzgXZ+smIUp95O8zeRYGsuIzQIiMPMgQqKtTJuzvAb7BQ=="], - "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.3.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-rFnGfqqEOGiUTbxglpiDA500KeRqcI1ukemhNfDrEzx3imAArS8mFZSuUG7ib31P5EpX+PXuvg0G9/3YXfgWeQ=="], + "@opentui/core-darwin-arm64": ["@opentui/core-darwin-arm64@0.5.12", "", { "os": "darwin", "cpu": "arm64" }, "sha512-YdVnP0tAyerBNl0mIcmQEOotPeZzW1VnSXKBl5cyZ5e6nDd2Y+ui/8eRPpn1oqcamf1NCnzS4ohMgejOvna8Zg=="], - "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.3.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-Z+/GxKvB3NzMDSwuyWR7HDStbaNRf5a09lt5W9b4BGmCAFW/mbX0Tuh3kloubcMgiq5vLnVaNzY19hrIgJrjGQ=="], + "@opentui/core-darwin-x64": ["@opentui/core-darwin-x64@0.5.12", "", { "os": "darwin", "cpu": "x64" }, "sha512-uRrQJdHmLUSj3PV23QPi3WSimYTTxcXnVouxF6U4xMXlOv4N3SxnHfVwMRQkPqbGOfvVWHeLE6FdK4C+ubU0sQ=="], - "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-0TRuGNR+2GGk0rQuDaIxkIa/3Ty/XySfeOQLAUX4Jqaifky04As69fYT17yOhHqg5viCJUAGG/SdW8IH6C2osg=="], + "@opentui/core-linux-arm64": ["@opentui/core-linux-arm64@0.5.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-XeKhuIaEtgipvuPHbl4qPOBj+Ut+2zObmsxMVM1jDcjz/FatG9PGeGQPx1G1SnvH2AgpT4K+eCu7DUF0+yIqoQ=="], - "@opentui/core-linux-arm64-musl": ["@opentui/core-linux-arm64-musl@0.3.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-noDKfwYUjutQUx5rtoyKrBIYaeSCmAmtxOSJdnKecyWEhMygtdHp4ssPtxzsZMuQAliHogCmD7vUG/pGMgcXMQ=="], + "@opentui/core-linux-arm64-musl": ["@opentui/core-linux-arm64-musl@0.5.12", "", { "os": "linux", "cpu": "arm64" }, "sha512-VZ2sNMw1d/r1SLPjUbOP9LKscKz1CQjID8adTL6gG8Lrrq+mYcIUxutyB+P/eG0J/7oRZLPR6OMt7dUOap6RTg=="], - "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-EZp+Lg9eZwzwNny4l2ACHdC95JbEKYbor1WImnm6IEo1e2Fgl9mltYv2J+i+Ea+dXQbrkK/MRIw7CRusxfqFFA=="], + "@opentui/core-linux-x64": ["@opentui/core-linux-x64@0.5.12", "", { "os": "linux", "cpu": "x64" }, "sha512-eZiCjEzwbb6qClPPfk32Nha9xmr9obt69Xj0+9SKsXxWLBKkjQEGOMRoh/R9ObaQF4aq8If1xV3VEY0sD9W9vg=="], - "@opentui/core-linux-x64-musl": ["@opentui/core-linux-x64-musl@0.3.2", "", { "os": "linux", "cpu": "x64" }, "sha512-/FNGeJYhCHcIE3qBIo+nl8014NZ8u/XXUwQY2RjuWhMnzK9kQUfZ3cW4FP6FkOA/k4jGvByya47193II6djFtw=="], + "@opentui/core-linux-x64-musl": ["@opentui/core-linux-x64-musl@0.5.12", "", { "os": "linux", "cpu": "x64" }, "sha512-WWW0hVBoSYZ3D6AgZ4u2Y5/u/IyIq2pDb+4yI3WgJ70Wyt6ofHy+6kRGRgbXFn1p+rPInAHjCXD2v6C7iEKSrA=="], - "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.3.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-/zb5nCZKDgBS1UEQXrzTYUbbTPFMygbiZ/BfNWjEDIbm63gVzQ7pVouYbGP+88CRXIJtwT6LeoVPgp9nmOrDWQ=="], + "@opentui/core-win32-arm64": ["@opentui/core-win32-arm64@0.5.12", "", { "os": "win32", "cpu": "arm64" }, "sha512-aLbm6870Ybls6CYL4zMOCImTBPLZHZMUXJFGqMI44lIWxitkAtT6zg5lYA4oRqFRzzryDclxr29+hDgT3p3Blw=="], - "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.3.2", "", { "os": "win32", "cpu": "x64" }, "sha512-q8xqMhW1jlJVzos+A5+FXRquH01j1ZHmrNi/9++W1Ebz3LQYn+8Z8j7rcV/meIiDuo9nyRHigQQT+cy9xV4N2g=="], + "@opentui/core-win32-x64": ["@opentui/core-win32-x64@0.5.12", "", { "os": "win32", "cpu": "x64" }, "sha512-KTwtwpfd2zF9opVh3SyRJYDd1o3Xv4XL8OZb8Zi+CqWUel6Y2IDCiVivCv8fGJt3J7wOIXXtuZI9ZUkLyKJCiQ=="], - "@opentui/keymap": ["@opentui/keymap@0.3.2", "", { "dependencies": { "@opentui/core": "0.3.2" }, "peerDependencies": { "@opentui/react": "0.3.2", "@opentui/solid": "0.3.2", "react": ">=19.2.0", "solid-js": "1.9.12" }, "optionalPeers": ["@opentui/react", "@opentui/solid", "react", "solid-js"] }, "sha512-y+IPBagxPvVrKIISmlfvO7ScvVIXGMmV92x0O+WzQ2vPf6fi5xgJc4YNmgEACfGdnM3ub14MkgEXqMOeUN7ZQA=="], + "@opentui/keymap": ["@opentui/keymap@0.5.12", "", { "dependencies": { "@opentui/core": "0.5.12" }, "peerDependencies": { "@opentui/react": "0.5.12", "@opentui/solid": "0.5.12", "react": ">=19.2.0", "solid-js": "1.9.12" }, "optionalPeers": ["@opentui/react", "@opentui/solid", "react", "solid-js"] }, "sha512-yWPvJjRhJTRoRSUucQq9Ua8ZW7n/2YQ/j6JxWq5Qekm4WuFiTplEkebR/Aj2/xA8tX68NOE5qv1LrY0Jk3NLNQ=="], - "@opentui/solid": ["@opentui/solid@0.3.2", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.3.2", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-Yff0gSwIY/o0XeMciYeAUkQtea8bWzR0UjjVglmcBe13hWuJZt/GfjbDMdNNQ8zCrLubLEh04an5fYXCd7NMYQ=="], + "@opentui/solid": ["@opentui/solid@0.5.12", "", { "dependencies": { "@babel/core": "7.28.0", "@babel/preset-typescript": "7.27.1", "@opentui/core": "0.5.12", "babel-plugin-module-resolver": "5.0.2", "babel-preset-solid": "1.9.12", "entities": "7.0.1", "s-js": "^0.4.9" }, "peerDependencies": { "solid-js": "1.9.12" } }, "sha512-hAiVlVMtT7AkHGblKwcW1YAuXtxkSy1XSf/RRc4j3IlG3mTNX0bhJdnGOo3Xw14EqeZMp41Mcp5WzHAzMm/DzA=="], "@oslojs/asn1": ["@oslojs/asn1@1.0.0", "", { "dependencies": { "@oslojs/binary": "1.0.0" } }, "sha512-zw/wn0sj0j0QKbIXfIlnEcTviaCzYOY3V5rAyjR6YtOByFtJiT574+8p9Wlach0lZH9fddD4yb9laEAIl4vXQA=="], @@ -1859,8 +1824,6 @@ "@pkgjs/parseargs": ["@pkgjs/parseargs@0.11.0", "", {}, "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg=="], - "@planetscale/database": ["@planetscale/database@1.19.0", "", {}, "sha512-Tv4jcFUFAFjOWrGSio49H6R2ijALv0ZzVBfJKIdm+kl9X046Fh4LLawrF9OMsglVbK6ukqMJsUCeucGAFTBcMA=="], - "@playwright/test": ["@playwright/test@1.59.1", "", { "dependencies": { "playwright": "1.59.1" }, "bin": { "playwright": "cli.js" } }, "sha512-PG6q63nQg5c9rIi4/Z5lR5IVF7yU5MqmKaPOe0HSc0O2cX1fPi96sUQu5j7eo4gKCkB2AnNGoWt7y4/Xx3Kcqg=="], "@protobuf-ts/plugin": ["@protobuf-ts/plugin@2.11.1", "", { "dependencies": { "@bufbuild/protobuf": "^2.4.0", "@bufbuild/protoplugin": "^2.4.0", "@protobuf-ts/protoc": "^2.11.1", "@protobuf-ts/runtime": "^2.11.1", "@protobuf-ts/runtime-rpc": "^2.11.1", "typescript": "^3.9" }, "bin": { "protoc-gen-ts": "bin/protoc-gen-ts", "protoc-gen-dump": "bin/protoc-gen-dump" } }, "sha512-HyuprDcw0bEEJqkOWe1rnXUP0gwYLij8YhPuZyZk6cJbIgc/Q0IFgoHQxOXNIXAcXM4Sbehh6kjVnCzasElw1A=="], @@ -2437,25 +2400,17 @@ "@ungap/structured-clone": ["@ungap/structured-clone@1.3.1", "", {}, "sha512-mUFwbeTqrVgDQxFveS+df2yfap6iuP20NAKAsBt5jDEoOTDew+zwLAOilHCeQJOVSvmgCX4ogqIrA0mnyr08yQ=="], - "@upstash/redis": ["@upstash/redis@1.38.0", "", { "dependencies": { "uncrypto": "^0.1.3" } }, "sha512-wu+dZBptlLy0+MCUEoHmzrY/TnmgDey3+c7EbIGwrLqAvkP8yi5MWZHYGIFtAygmL4Bkz2TdFu+eU0vFPncIcg=="], - "@valibot/to-json-schema": ["@valibot/to-json-schema@1.6.0", "", { "peerDependencies": { "valibot": "^1.3.0" } }, "sha512-d6rYyK5KVa2XdqamWgZ4/Nr+cXhxjy7lmpe6Iajw15J/jmU+gyxl2IEd1Otg1d7Rl3gOQL5reulnSypzBtYy1A=="], "@vercel/oidc": ["@vercel/oidc@3.2.0", "", {}, "sha512-UycprH3T6n3jH0k44NHMa7pnFHGu/N05MjojYr+Mc6I7obkoLIJujSWwin1pCvdy/eOxrI/l3uDLQsmcrOb4ug=="], "@vitest/expect": ["@vitest/expect@3.2.4", "", { "dependencies": { "@types/chai": "^5.2.2", "@vitest/spy": "3.2.4", "@vitest/utils": "3.2.4", "chai": "^5.2.0", "tinyrainbow": "^2.0.0" } }, "sha512-Io0yyORnB6sikFlt8QW5K7slY4OjqNX9jmJQ02QDda8lyM6B5oNgVWoSoKPac8/kgnCUzuHQKrSLtu/uOqqrig=="], - "@vitest/mocker": ["@vitest/mocker@4.1.7", "", { "dependencies": { "@vitest/spy": "4.1.7", "estree-walker": "^3.0.3", "magic-string": "^0.30.21" }, "peerDependencies": { "msw": "^2.4.9", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["msw", "vite"] }, "sha512-vY7nuamKgfvpA1Koa3oYIw/k7D6kZnpGyNMZW8loow2bsBYla1TFdqTaXncWdRn4pgwNs+90RhnXhJScDwQeJA=="], - - "@vitest/pretty-format": ["@vitest/pretty-format@4.1.7", "", { "dependencies": { "tinyrainbow": "^3.1.0" } }, "sha512-umgCarTOYQWIaDMvGDRZij+6b9oVeLIyJzfN+AS88e0ZOU3QTgNNSTtjQOpcvWr3np1N0j4WgZj+sb3oYBDscw=="], - - "@vitest/runner": ["@vitest/runner@4.1.7", "", { "dependencies": { "@vitest/utils": "4.1.7", "pathe": "^2.0.3" } }, "sha512-BapjmAQ2aI78WdMEfeUWivnfVzB+VPGwWRQcJE0OUq7qEeEcBsCSf+0T5iREBNE5nBb4wA5Ya0W6IA+sghdEFw=="], - - "@vitest/snapshot": ["@vitest/snapshot@4.1.7", "", { "dependencies": { "@vitest/pretty-format": "4.1.7", "@vitest/utils": "4.1.7", "magic-string": "^0.30.21", "pathe": "^2.0.3" } }, "sha512-ZacLzja+TmJeZ1h14xW2FB/WpeimUD3haBXQPyJqxvo8jQTmfeA8zv58mtjN2C7EHXZDYVcVYdYmAxjkWVvKCw=="], + "@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], "@vitest/spy": ["@vitest/spy@3.2.4", "", { "dependencies": { "tinyspy": "^4.0.3" } }, "sha512-vAfasCOe6AIK70iP5UD11Ac4siNUNJ9i/9PZ3NKx07sG6sUxeag1LWdNrMWeKKYBLlzuK+Gn65Yd5nyL6ds+nw=="], - "@vitest/utils": ["@vitest/utils@4.1.7", "", { "dependencies": { "@vitest/pretty-format": "4.1.7", "convert-source-map": "^2.0.0", "tinyrainbow": "^3.1.0" } }, "sha512-T532WBu791cBxJlCl6SO+J14l81DQx6uQHm1bQbmCDY7nqlEIgkza/UFnSBNaUtSf41unldDFjdOBYEQC4b5Hw=="], + "@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], "@volar/kit": ["@volar/kit@2.4.28", "", { "dependencies": { "@volar/language-service": "2.4.28", "@volar/typescript": "2.4.28", "typesafe-path": "^0.2.2", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "typescript": "*" } }, "sha512-cKX4vK9dtZvDRaAzeoUdaAJEew6IdxHNCRrdp5Kvcl6zZOqb6jTOfk3kXkIkG3T7oTFXguEMt5+9ptyqYR84Pg=="], @@ -2567,8 +2522,6 @@ "available-typed-arrays": ["available-typed-arrays@1.0.7", "", { "dependencies": { "possible-typed-array-names": "^1.0.0" } }, "sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ=="], - "aws-ssl-profiles": ["aws-ssl-profiles@1.1.2", "", {}, "sha512-NZKeq9AfyQvEeNlN0zSYAaWrmBffJh3IELMZfRpJVWgrpEbtEpnjvzqBPf+mxoI287JohRDoa+/nsfqqiZmF6g=="], - "aws4fetch": ["aws4fetch@1.0.20", "", {}, "sha512-/djoAN709iY65ETD6LKCtyyEI04XIBP5xVvfmNxsEP0uJB5tyaGBztSryRr4HqMStr9R06PisQE7m9zDTXKu6g=="], "axe-core": ["axe-core@4.11.4", "", {}, "sha512-KunSNx+TVpkAw/6ULfhnx+HWRecjqZGTOyquAoWHYLRSdK1tB5Ihce1ZW+UY3fj33bYAFWPu7W/GRSmmrCGuxA=="], @@ -2651,7 +2604,7 @@ "builder-util-runtime": ["builder-util-runtime@9.5.1", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ=="], - "bun-ffi-structs": ["bun-ffi-structs@0.2.2", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-N/ZWtyN0piZlrXQT7TO0V+q952orYqkfhXRXM1Hcbb+R3QSiBH4vLnib187Mrs1H7pWIYECAmPeapGYDOMCl+w=="], + "bun-ffi-structs": ["bun-ffi-structs@0.3.1", "", { "peerDependencies": { "typescript": "^5" } }, "sha512-3gM7PpVWLyrwxWjcilSiGuhWanhZivvo6l0u573NziPH6f/gwk6McbaYgn7oJWov6pKGRTDbrg94W5DcJsKTtQ=="], "bun-pty": ["bun-pty@0.4.8", "", {}, "sha512-rO70Mrbr13+jxHHHu2YBkk2pNqrJE5cJn29WE++PUr+GFA0hq/VgtQPZANJ8dJo6d7XImvBk37Innt8GM7O28w=="], @@ -2803,8 +2756,6 @@ "data-view-byte-offset": ["data-view-byte-offset@1.0.1", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "is-data-view": "^1.0.1" } }, "sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ=="], - "db0": ["db0@0.3.4", "", { "peerDependencies": { "@electric-sql/pglite": "*", "@libsql/client": "*", "better-sqlite3": "*", "drizzle-orm": "*", "mysql2": "*", "sqlite3": "*" }, "optionalPeers": ["@electric-sql/pglite", "@libsql/client", "better-sqlite3", "drizzle-orm", "mysql2", "sqlite3"] }, "sha512-RiXXi4WaNzPTHEOu8UPQKMooIbqOEyqA1t7Z6MsdxSCeb8iUC9ko3LcmsLmeUt2SM5bctfArZKkRQggKZz7JNw=="], - "debounce-fn": ["debounce-fn@6.0.0", "", { "dependencies": { "mimic-function": "^5.0.0" } }, "sha512-rBMW+F2TXryBwB54Q0d8drNEI+TfoS9JpNTAoVpukbWEhjXQq4rySFYLaqXMFXwdv61Zb2OHtj5bviSoimqxRQ=="], "debug": ["debug@4.4.3", "", { "dependencies": { "ms": "^2.1.3" }, "peerDependencies": { "supports-color": "*" }, "optionalPeers": ["supports-color"] }, "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA=="], @@ -3017,8 +2968,6 @@ "execa": ["execa@8.0.1", "", { "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^8.0.1", "human-signals": "^5.0.0", "is-stream": "^3.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^5.1.0", "onetime": "^6.0.0", "signal-exit": "^4.1.0", "strip-final-newline": "^3.0.0" } }, "sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg=="], - "expect-type": ["expect-type@1.3.0", "", {}, "sha512-knvyeauYhqjOYvQ66MznSMs83wmHrCycNEN6Ao+2AeYEfxUIkuiVxdEa1qlGEPK+We3n0THiDciYSsCcgW/DoA=="], - "exponential-backoff": ["exponential-backoff@3.1.3", "", {}, "sha512-ZgEeZXj30q+I0EN+CbSSpIyPaJ5HVQD18Z1m+u1FXbAeT94mr1zw50q4q6jiiC447Nl/YTcIYSAftiGqetwXCA=="], "express": ["express@4.22.2", "", { "dependencies": { "accepts": "~1.3.8", "array-flatten": "1.1.1", "body-parser": "~1.20.5", "content-disposition": "~0.5.4", "content-type": "~1.0.4", "cookie": "~0.7.1", "cookie-signature": "~1.0.6", "debug": "2.6.9", "depd": "2.0.0", "encodeurl": "~2.0.0", "escape-html": "~1.0.3", "etag": "~1.8.1", "finalhandler": "~1.3.1", "fresh": "~0.5.2", "http-errors": "~2.0.0", "merge-descriptors": "1.0.3", "methods": "~1.1.2", "on-finished": "~2.4.1", "parseurl": "~1.3.3", "path-to-regexp": "~0.1.12", "proxy-addr": "~2.0.7", "qs": "~6.15.1", "range-parser": "~1.2.1", "safe-buffer": "5.2.1", "send": "~0.19.0", "serve-static": "~1.16.2", "setprototypeof": "1.2.0", "statuses": "~2.0.1", "type-is": "~1.6.18", "utils-merge": "1.0.1", "vary": "~1.1.2" } }, "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q=="], @@ -3121,8 +3070,6 @@ "gcp-metadata": ["gcp-metadata@8.1.2", "", { "dependencies": { "gaxios": "^7.0.0", "google-logging-utils": "^1.0.0", "json-bigint": "^1.0.0" } }, "sha512-zV/5HKTfCeKWnxG0Dmrw51hEWFGfcF2xiXqcA3+J90WDuP0SvoiSO5ORvcBsifmx/FoIjgQN3oNOGaQ5PhLFkg=="], - "generate-function": ["generate-function@2.3.1", "", { "dependencies": { "is-property": "^1.0.2" } }, "sha512-eeB5GfMNeevm/GRYq20ShmsaGcmI81kIX2K9XQx5miC8KdHaC6Jm0qQ8ZNeGOi7wYB8OsdxKs+Y2oVuTFuVwKQ=="], - "generator-function": ["generator-function@2.0.1", "", {}, "sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g=="], "gensync": ["gensync@1.0.0-beta.2", "", {}, "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg=="], @@ -3271,7 +3218,7 @@ "iconv-corefoundation": ["iconv-corefoundation@1.1.7", "", { "dependencies": { "cli-truncate": "^2.1.0", "node-addon-api": "^1.6.3" }, "os": "darwin" }, "sha512-T10qvkw0zz4wnm560lOEg0PovVqUXuOFhhHAkixw8/sycy7TJt7v/RrkEKEQnAw2viPSJu6iAkErxnzR0g8PpQ=="], - "iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + "iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], "ieee754": ["ieee754@1.2.1", "", {}, "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA=="], @@ -3361,8 +3308,6 @@ "is-promise": ["is-promise@4.0.0", "", {}, "sha512-hvpoI6korhJMnej285dSg6nu1+e6uxs7zG3BYAm5byqDsgJNWwxzM6z6iZiAgQR4TJ30JmBTOwqZUw3WlyH3AQ=="], - "is-property": ["is-property@1.0.2", "", {}, "sha512-Ks/IoX00TtClbGQr4TWXemAnktAQvYB7HzcCxDGqEZU6oCmb2INHuOoKxbtR+HFkmYWBKv/dOZtGRiAjDhj92g=="], - "is-regex": ["is-regex@1.2.1", "", { "dependencies": { "call-bound": "^1.0.2", "gopd": "^1.2.0", "has-tostringtag": "^1.0.2", "hasown": "^2.0.2" } }, "sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g=="], "is-set": ["is-set@2.0.3", "", {}, "sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg=="], @@ -3479,8 +3424,6 @@ "lazystream": ["lazystream@1.0.1", "", { "dependencies": { "readable-stream": "^2.0.5" } }, "sha512-b94GiNHQNy6JNTrt5w6zNyffMrNkXZb3KTkCZJb2V1xaEGCk093vkZ2jk3tpaeP33/OiXC+WvK9AxUebnf5nbw=="], - "libsql": ["libsql@0.5.29", "", { "dependencies": { "@neon-rs/load": "^0.0.4", "detect-libc": "2.0.2" }, "optionalDependencies": { "@libsql/darwin-arm64": "0.5.29", "@libsql/darwin-x64": "0.5.29", "@libsql/linux-arm-gnueabihf": "0.5.29", "@libsql/linux-arm-musleabihf": "0.5.29", "@libsql/linux-arm64-gnu": "0.5.29", "@libsql/linux-arm64-musl": "0.5.29", "@libsql/linux-x64-gnu": "0.5.29", "@libsql/linux-x64-musl": "0.5.29", "@libsql/win32-x64-msvc": "0.5.29" }, "os": [ "linux", "win32", "darwin", ], "cpu": [ "arm", "x64", "arm64", ] }, "sha512-8lMP8iMgiBzzoNbAPQ59qdVcj6UaE/Vnm+fiwX4doX4Narook0a4GPKWBEv+CR8a1OwbfkgL18uBfBjWdF0Fzg=="], - "lightningcss": ["lightningcss@1.30.1", "", { "dependencies": { "detect-libc": "^2.0.3" }, "optionalDependencies": { "lightningcss-darwin-arm64": "1.30.1", "lightningcss-darwin-x64": "1.30.1", "lightningcss-freebsd-x64": "1.30.1", "lightningcss-linux-arm-gnueabihf": "1.30.1", "lightningcss-linux-arm64-gnu": "1.30.1", "lightningcss-linux-arm64-musl": "1.30.1", "lightningcss-linux-x64-gnu": "1.30.1", "lightningcss-linux-x64-musl": "1.30.1", "lightningcss-win32-arm64-msvc": "1.30.1", "lightningcss-win32-x64-msvc": "1.30.1" } }, "sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg=="], "lightningcss-android-arm64": ["lightningcss-android-arm64@1.33.0", "", { "os": "android", "cpu": "arm64" }, "sha512-gEpRTalKdosp4Bb8qWtc2iOgE5SeIHlpS1up9bFq2wAyYhl1UdTObYiHe98zEM9SQvSoqQZ1IQD0JNpg3Ml5pg=="], @@ -3539,8 +3482,6 @@ "lru-cache": ["lru-cache@11.5.1", "", {}, "sha512-RPimw/7aMdv2oqRrxKwvZXcPfwBrn/JZ2xYcY9Hus/6LaS3VOAKVWKWgNLCFSiOm1ESXinjsDlidVU7JlnCN2A=="], - "lru.min": ["lru.min@1.1.4", "", {}, "sha512-DqC6n3QQ77zdFpCMASA1a3Jlb64Hv2N2DciFGkO/4L9+q/IpIAuRlKOvCXabtRW6cQf8usbmM6BE/TOPysCdIA=="], - "lru_map": ["lru_map@0.4.1", "", {}, "sha512-I+lBvqMMFfqaV8CJCISjI3wbjmwVu/VyOoU7+qtu9d7ioW5klMgsTTiUOUp+DJvfTTzKXoPbyC6YfgkNcyPSOg=="], "luxon": ["luxon@3.6.1", "", {}, "sha512-tJLxrKJhO2ukZ5z0gyjY1zPh3Rh88Ej9P7jNrZiHMUXHae1yvI2imgOZtL1TO8TW6biMMKfTtAOoEJANgtWBMQ=="], @@ -3745,10 +3686,6 @@ "multipasta": ["multipasta@0.2.7", "", {}, "sha512-KPA58d68KgGil15oDqXjkUBEBYc00XvbPj5/X+dyzeo/lWm9Nc25pQRlf1D+gv4OpK7NM0J1odrbu9JNNGvynA=="], - "mysql2": ["mysql2@3.14.4", "", { "dependencies": { "aws-ssl-profiles": "^1.1.1", "denque": "^2.1.0", "generate-function": "^2.3.1", "iconv-lite": "^0.7.0", "long": "^5.2.1", "lru.min": "^1.0.0", "named-placeholders": "^1.1.3", "seq-queue": "^0.0.5", "sqlstring": "^2.3.2" } }, "sha512-Cs/jx3WZPNrYHVz+Iunp9ziahaG5uFMvD2R8Zlmc194AqXNxt9HBNu7ZsPYrUtmJsF0egETCWIdMIYAwOGjL1w=="], - - "named-placeholders": ["named-placeholders@1.1.6", "", { "dependencies": { "lru.min": "^1.1.0" } }, "sha512-Tz09sEL2EEuv5fFowm419c1+a/jSMiBjI9gHxVLrVdbUkkNUUfjsVYs9pVZu5oCon/kmRh9TfLEObFtkVxmY0w=="], - "nanoevents": ["nanoevents@7.0.1", "", {}, "sha512-o6lpKiCxLeijK4hgsqfR6CNToPyRU3keKyyI6uwuHRvpRTbZ0wXw51WRgyldVugZqoJfkGFrjrIenYH3bfEO3Q=="], "nanoid": ["nanoid@3.3.12", "", { "bin": { "nanoid": "bin/nanoid.cjs" } }, "sha512-ZB9RH/39qpq5Vu6Y+NmUaFhQR6pp+M2Xt76XBnEwDaGcVAqhlvxrl3B2bKS5D3NH3QR76v3aSrKaF/Kiy7lEtQ=="], @@ -3941,8 +3878,6 @@ "postcss-selector-parser": ["postcss-selector-parser@7.1.1", "", { "dependencies": { "cssesc": "^3.0.0", "util-deprecate": "^1.0.2" } }, "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg=="], - "postgres": ["postgres@3.4.7", "", {}, "sha512-Jtc2612XINuBjIl/QTWsV5UvE8UHuNblcO3vVADSrKsrc6RqGX6lOW1cEo3CM2v0XG4Nat8nI+YM7/f26VxXLw=="], - "postject": ["postject@1.0.0-alpha.6", "", { "dependencies": { "commander": "^9.4.0" }, "bin": { "postject": "dist/cli.js" } }, "sha512-b9Eb8h2eVqNE8edvKdwqkrY6O7kAwmI8kcnBv1NScolYJbo59XUF0noFq+lxbC1yN20bmC0WBEbDC5H/7ASb0A=="], "powershell-utils": ["powershell-utils@0.1.0", "", {}, "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A=="], @@ -3969,8 +3904,6 @@ "promise-call-limit": ["promise-call-limit@3.0.2", "", {}, "sha512-mRPQO2T1QQVw11E7+UdCJu7S61eJVWknzml9sC1heAdj1jxl0fWMBypIt9ZOcLFf8FkG995ZD7RnVk7HH72fZw=="], - "promise-limit": ["promise-limit@2.7.0", "", {}, "sha512-7nJ6v5lnJsXwGprnGXga4wx6d1POjvi5Qmf1ivTRxTjH4Z/9Czja/UCMLVmB9N93GeWOU93XaFaEt6jbuoagNw=="], - "promise-retry": ["promise-retry@2.0.1", "", { "dependencies": { "err-code": "^2.0.2", "retry": "^0.12.0" } }, "sha512-y+WKFlBR8BGXnsNlIHFGPZmyDf3DFMoLhaflAnyZgV6rG6xu+JwesTo2Q9R6XwYmtmwAFCkAk3e35jEdoeh/3g=="], "promise.allsettled": ["promise.allsettled@1.0.7", "", { "dependencies": { "array.prototype.map": "^1.0.5", "call-bind": "^1.0.2", "define-properties": "^1.2.0", "es-abstract": "^1.22.1", "get-intrinsic": "^1.2.1", "iterate-value": "^1.0.2" } }, "sha512-hezvKvQQmsFkOdrZfYxUxkyxl8mgFQeT259Ajj9PXdbg9VzBCWrItOev72JyWxkCD5VSSqAeHmlN3tWx4DlmsA=="], @@ -4145,8 +4078,6 @@ "send": ["send@1.2.1", "", { "dependencies": { "debug": "^4.4.3", "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "etag": "^1.8.1", "fresh": "^2.0.0", "http-errors": "^2.0.1", "mime-types": "^3.0.2", "ms": "^2.1.3", "on-finished": "^2.4.1", "range-parser": "^1.2.1", "statuses": "^2.0.2" } }, "sha512-1gnZf7DFcoIcajTjTwjwuDjzuz4PPcY2StKPlsGAQ1+YH20IRVrBaXSWmdjowTJ6u8Rc01PoYOGHXfP1mYcZNQ=="], - "seq-queue": ["seq-queue@0.0.5", "", {}, "sha512-hr3Wtp/GZIc/6DAGPDcV4/9WoZhjrkXsi5B/07QgX8tsdc6ilr7BFM6PM6rbdAX1kFSDYeZGLipIZZKyQP0O5Q=="], - "serialize-error": ["serialize-error@7.0.1", "", { "dependencies": { "type-fest": "^0.13.1" } }, "sha512-8I8TjW5KMOKsZQTvoxjuSIa7foAwPWGOts+6o7sgjz41/qMD9VQHEDxi6PBvK2l0MXUmqZyNpUK+T2tQaaElvw=="], "seroval": ["seroval@1.5.6", "", {}, "sha512-rVQVWjjSvlINzaQPZH5JFqsqEsIWdTxY3iJZCnTL/5gQbXIRooVZKI60tVCkOVfzcRPejboxO2t0P89dg5mQaA=="], @@ -4181,8 +4112,6 @@ "side-channel-weakmap": ["side-channel-weakmap@1.0.2", "", { "dependencies": { "call-bound": "^1.0.2", "es-errors": "^1.3.0", "get-intrinsic": "^1.2.5", "object-inspect": "^1.13.3", "side-channel-map": "^1.0.1" } }, "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A=="], - "siginfo": ["siginfo@2.0.0", "", {}, "sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g=="], - "signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], "sigstore": ["sigstore@4.1.1", "", { "dependencies": { "@sigstore/bundle": "^4.0.0", "@sigstore/core": "^3.2.1", "@sigstore/protobuf-specs": "^0.5.0", "@sigstore/sign": "^4.1.1", "@sigstore/tuf": "^4.0.2", "@sigstore/verify": "^3.1.1" } }, "sha512-endqECJkfhozrXMK5ngu/UAA0xVcVEFdnHJCElGaExypjW+HK5i6zu3NteLoaX/iFbRUbC3+DjttQs0GARr+5w=="], @@ -4237,8 +4166,6 @@ "sprintf-js": ["sprintf-js@1.0.3", "", {}, "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g=="], - "sqlstring": ["sqlstring@2.3.3", "", {}, "sha512-qC9iz2FlN7DQl3+wjwn3802RTyjCx7sDvfQEXchwa6CWOx07/WVfh91gBmQ9fahw8snwGEWU3xGzOt4tFyHLxg=="], - "ssri": ["ssri@13.0.1", "", { "dependencies": { "minipass": "^7.0.3" } }, "sha512-QUiRf1+u9wPTL/76GTYlKttDEBWV1ga9ZXW8BG6kfdeyyM8LGPix9gROyg9V2+P0xNyF3X2Go526xKFdMZrHSQ=="], "sst": ["sst@4.13.1", "", { "dependencies": { "@aws/durable-execution-sdk-js": "1.0.2", "aws4fetch": "1.0.18", "jose": "5.2.3", "openid-client": "5.6.4" }, "optionalDependencies": { "sst-darwin-arm64": "4.13.1", "sst-darwin-x64": "4.13.1", "sst-linux-arm64": "4.13.1", "sst-linux-x64": "4.13.1", "sst-linux-x86": "4.13.1", "sst-win32-arm64": "4.13.1", "sst-win32-x64": "4.13.1", "sst-win32-x86": "4.13.1" }, "bin": { "sst": "bin/sst.mjs" } }, "sha512-FKzhWuqV5NPhFWH7b+Ktf5+Mvox6DCMc8iOrjaS3mkjnnmyEcOZq7HNx2/ZlIcBwgOZE9sCPR3ot1zXtXkXzZg=="], @@ -4259,16 +4186,12 @@ "sst-win32-x86": ["sst-win32-x86@4.13.1", "", { "os": "win32", "cpu": "none" }, "sha512-YPxBVdac/MsrzwlC6pF0NrrvMcmfdBLYjv7MbzHc5jNh1FQ1WPh6bdWQqgv0KD9EQTNLLEkej0beydgUvcCWJg=="], - "stackback": ["stackback@0.0.2", "", {}, "sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw=="], - "standard-as-callback": ["standard-as-callback@2.1.0", "", {}, "sha512-qoRRSyROncaz1z0mvYqIE4lCd9p2R90i6GxW3uZv5ucSu8tU7B5HXUP1gG8pVZsYNVaXjk8ClXHPttLyxAL48A=="], "stat-mode": ["stat-mode@1.0.0", "", {}, "sha512-jH9EhtKIjuXZ2cWxmXS8ZP80XyC3iasQxMDV8jzhNJpfDb7VbQLVW4Wvsxz9QZvzV+G4YoSfBUVKDOyxLzi/sg=="], "statuses": ["statuses@2.0.2", "", {}, "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw=="], - "std-env": ["std-env@4.1.0", "", {}, "sha512-Rq7ybcX2RuC55r9oaPVEW7/xu3tj8u4GeBYHBWCychFtzMIr86A7e3PPEBPT37sHStKX3+TiX/Fr/ACmJLVlLQ=="], - "stop-iteration-iterator": ["stop-iteration-iterator@1.1.0", "", { "dependencies": { "es-errors": "^1.3.0", "internal-slot": "^1.1.0" } }, "sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ=="], "storybook": ["storybook@10.4.1", "", { "dependencies": { "@storybook/global": "^5.0.0", "@storybook/icons": "^2.0.2", "@testing-library/jest-dom": "^6.9.1", "@testing-library/user-event": "^14.6.1", "@vitest/expect": "3.2.4", "@vitest/spy": "3.2.4", "@webcontainer/env": "^1.1.1", "esbuild": "^0.18.0 || ^0.19.0 || ^0.20.0 || ^0.21.0 || ^0.22.0 || ^0.23.0 || ^0.24.0 || ^0.25.0 || ^0.26.0 || ^0.27.0", "open": "^10.2.0", "oxc-parser": "^0.127.0", "oxc-resolver": "^11.19.1", "recast": "^0.23.5", "semver": "^7.7.3", "use-sync-external-store": "^1.5.0", "ws": "^8.18.0" }, "peerDependencies": { "@types/react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0", "prettier": "^2 || ^3", "vite-plus": "^0.1.15" }, "optionalPeers": ["@types/react", "prettier", "vite-plus"], "bin": "./dist/bin/dispatcher.js" }, "sha512-V1Zd2e+gBFufqAQVZ1JR8KLqALsEZ3JYSBnWwQbKa6zCfWWanR6AFMyuOkLt2gZOgGp3h2Riuz88pGNVTQSG0A=="], @@ -4341,8 +4264,6 @@ "temp-file": ["temp-file@3.4.0", "", { "dependencies": { "async-exit-hook": "^2.0.1", "fs-extra": "^10.0.0" } }, "sha512-C5tjlC/HCtVUOi3KWVokd4vHVViOmGjtLwIh4MuzPo/nMYTV/p1urt3RnMz2IWXDdKEGJH3k5+KPxtqRsUYGtg=="], - "terser": ["terser@5.48.0", "", { "dependencies": { "@jridgewell/source-map": "^0.3.3", "acorn": "^8.15.0", "commander": "^2.20.0", "source-map-support": "~0.5.20" }, "bin": { "terser": "bin/terser" } }, "sha512-J/9An6vs9Us6wKRriSFXBWdRZapREHqFzdNUKk0pmu804EMR6dr6winwo7e5JDxN4xahxQsuysyYFwlwj4XN/Q=="], - "text-decoder": ["text-decoder@1.2.7", "", { "dependencies": { "b4a": "^1.6.4" } }, "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ=="], "thunky": ["thunky@1.1.0", "", {}, "sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA=="], @@ -4355,15 +4276,13 @@ "tiny-typed-emitter": ["tiny-typed-emitter@2.1.0", "", {}, "sha512-qVtvMxeXbVej0cQWKqVSSAHmKZEHAvxdF8HEUBFWts8h+xEo5m/lEiPakuyZ3BnCBjOD8i24kzNOiOLLgsSxhA=="], - "tinybench": ["tinybench@2.9.0", "", {}, "sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg=="], - "tinyclip": ["tinyclip@0.1.15", "", {}, "sha512-uo33abH+Ays0xYaDysoBt494Hb3hsEczMpcC0MwFl773pazORx4fmvKhclhR1wonUbB6vvpRsvVMwnhfqeMc+A=="], "tinyexec": ["tinyexec@0.3.2", "", {}, "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA=="], "tinyglobby": ["tinyglobby@0.2.17", "", { "dependencies": { "fdir": "^6.5.0", "picomatch": "^4.0.4" } }, "sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g=="], - "tinyrainbow": ["tinyrainbow@3.1.0", "", {}, "sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw=="], + "tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], "tinyspy": ["tinyspy@4.0.4", "", {}, "sha512-azl+t0z7pw/z958Gy9svOTuzqIk6xq+NSheJzn5MMWtWTFywIacg2wUlzKFGtt3cthx0r2SxMK0yzJOR0IES7Q=="], @@ -4529,8 +4448,6 @@ "vitefu": ["vitefu@1.1.3", "", { "peerDependencies": { "vite": "^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0" }, "optionalPeers": ["vite"] }, "sha512-ub4okH7Z5KLjb6hDyjqrGXqWtWvoYdU3IGm/NorpgHncKoLTCfRIbvlhBm7r0YstIaQRYlp4yEbFqDcKSzXSSg=="], - "vitest": ["vitest@4.1.7", "", { "dependencies": { "@vitest/expect": "4.1.7", "@vitest/mocker": "4.1.7", "@vitest/pretty-format": "4.1.7", "@vitest/runner": "4.1.7", "@vitest/snapshot": "4.1.7", "@vitest/spy": "4.1.7", "@vitest/utils": "4.1.7", "es-module-lexer": "^2.0.0", "expect-type": "^1.3.0", "magic-string": "^0.30.21", "obug": "^2.1.1", "pathe": "^2.0.3", "picomatch": "^4.0.3", "std-env": "^4.0.0-rc.1", "tinybench": "^2.9.0", "tinyexec": "^1.0.2", "tinyglobby": "^0.2.15", "tinyrainbow": "^3.1.0", "vite": "^6.0.0 || ^7.0.0 || ^8.0.0", "why-is-node-running": "^2.3.0" }, "peerDependencies": { "@edge-runtime/vm": "*", "@opentelemetry/api": "^1.9.0", "@types/node": "^20.0.0 || ^22.0.0 || >=24.0.0", "@vitest/browser-playwright": "4.1.7", "@vitest/browser-preview": "4.1.7", "@vitest/browser-webdriverio": "4.1.7", "@vitest/coverage-istanbul": "4.1.7", "@vitest/coverage-v8": "4.1.7", "@vitest/ui": "4.1.7", "happy-dom": "*", "jsdom": "*" }, "optionalPeers": ["@edge-runtime/vm", "@opentelemetry/api", "@types/node", "@vitest/browser-playwright", "@vitest/browser-preview", "@vitest/browser-webdriverio", "@vitest/coverage-istanbul", "@vitest/coverage-v8", "@vitest/ui", "happy-dom", "jsdom"], "bin": { "vitest": "vitest.mjs" } }, "sha512-flYyaFd2CgoCoU+0UKt3pxksgC+S02iTDN0n3LtqaMeXsI9SBcdNujc2k0DeFLzUn/0k538yNjOSdwgCqcrwJA=="], - "volar-service-css": ["volar-service-css@0.0.70", "", { "dependencies": { "vscode-css-languageservice": "^6.3.0", "vscode-languageserver-textdocument": "^1.0.11", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-K1qyOvBpE3rzdAv3e4/6Rv5yizrYPy5R/ne3IWCAzLBuMO4qBMV3kSqWzj6KUVe6S0AnN6wxF7cRkiaKfYMYJw=="], "volar-service-emmet": ["volar-service-emmet@0.0.70", "", { "dependencies": { "@emmetio/css-parser": "^0.4.1", "@emmetio/html-matcher": "^1.3.0", "@vscode/emmet-helper": "^2.9.3", "vscode-uri": "^3.0.8" }, "peerDependencies": { "@volar/language-service": "~2.4.0" }, "optionalPeers": ["@volar/language-service"] }, "sha512-xi5bC4m/VyE3zy/n2CXspKeDZs3qA41tHLTw275/7dNWM/RqE2z3BnDICQybHIVp/6G1iOQj5c1qXMgQC08TNg=="], @@ -4635,8 +4552,6 @@ "yocto-queue": ["yocto-queue@1.2.2", "", {}, "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ=="], - "yoga-layout": ["yoga-layout@3.2.1", "", {}, "sha512-0LPOt3AxKqMdFBZA3HBAt/t/8vIKq7VaQYbuA8WxCgung+p9TVyKRYdpvCb80HcdTN2NkbIKbhNwKUfm3tQywQ=="], - "zip-stream": ["zip-stream@6.0.1", "", { "dependencies": { "archiver-utils": "^5.0.0", "compress-commons": "^6.0.2", "readable-stream": "^4.0.0" } }, "sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA=="], "zod": ["zod@4.1.8", "", {}, "sha512-5R1P+WwQqmmMIEACyzSvo4JXHY5WiAFHRMg+zBZKgKS+Q1viRa0C1hmUKtHltoIFKtIdki3pRxkmpP74jnNYHQ=="], @@ -5025,12 +4940,6 @@ "@types/plist/xmlbuilder": ["xmlbuilder@15.1.1", "", {}, "sha512-yMqGBqtXyeN1e3TGYvgNgDVZ3j84W4cwkOXQswghol6APgZWaff9lnbvN7MHYJOiXsvGPXtjTYJEiC9J2wv9Eg=="], - "@vitest/expect/@vitest/utils": ["@vitest/utils@3.2.4", "", { "dependencies": { "@vitest/pretty-format": "3.2.4", "loupe": "^3.1.4", "tinyrainbow": "^2.0.0" } }, "sha512-fB2V0JFrQSMsCo9HiSq3Ezpdv4iYaXRG1Sx8edX3MwxfyNn83mKiGzOcH+Fkxt4MHxr3y42fQi1oeAInqgX2QA=="], - - "@vitest/expect/tinyrainbow": ["tinyrainbow@2.0.0", "", {}, "sha512-op4nsTR47R6p0vMUUoYl/a+ljLFVtlfaXkLQmqfLR1qHma1h/ysYk4hEXZ880bf2CYgTskvTa/e196Vd5dDQXw=="], - - "@vitest/mocker/@vitest/spy": ["@vitest/spy@4.1.7", "", {}, "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q=="], - "@vscode/emmet-helper/jsonc-parser": ["jsonc-parser@2.3.1", "", {}, "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg=="], "accepts/mime-types": ["mime-types@2.1.35", "", { "dependencies": { "mime-db": "1.52.0" } }, "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw=="], @@ -5125,8 +5034,6 @@ "dir-compare/p-limit": ["p-limit@3.1.0", "", { "dependencies": { "yocto-queue": "^0.1.0" } }, "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ=="], - "dmg-builder/iconv-lite": ["iconv-lite@0.6.3", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw=="], - "dmg-builder/js-yaml": ["js-yaml@4.1.1", "", { "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA=="], "dmg-license/ajv": ["ajv@6.15.0", "", { "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" } }, "sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw=="], @@ -5187,14 +5094,14 @@ "lazystream/readable-stream": ["readable-stream@2.3.8", "", { "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } }, "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA=="], - "libsql/detect-libc": ["detect-libc@2.0.2", "", {}, "sha512-UX6sGumvvqSaXgdKGUsgZWqcUyIXZ/vZTrlRT/iobiKhGL0zL4d3osHj3uqllWJK+i+sixDS/3COVEOFbupFyw=="], - "lightningcss/detect-libc": ["detect-libc@2.1.2", "", {}, "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ=="], "matcher/escape-string-regexp": ["escape-string-regexp@4.0.0", "", {}, "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA=="], "micromatch/picomatch": ["picomatch@2.3.2", "", {}, "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA=="], + "minipass-fetch/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + "minipass-flush/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], "minipass-pipeline/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], @@ -5291,8 +5198,6 @@ "tar/yallist": ["yallist@5.0.0", "", {}, "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw=="], - "terser/commander": ["commander@2.20.3", "", {}, "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ=="], - "tiny-async-pool/semver": ["semver@5.7.2", "", { "bin": { "semver": "bin/semver" } }, "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g=="], "tree-sitter-bash/node-addon-api": ["node-addon-api@8.8.0", "", {}, "sha512-c5Ko1fZJIJmzhFIkhRN76WTq+fC6tWnGy9CXA0fA+XygsWZmEwG8vmbkNqxMyoaa0Tin4djul49NzdVcJJcjeA=="], @@ -5311,16 +5216,6 @@ "vite-plugin-solid/@babel/core": ["@babel/core@7.28.4", "", { "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.3", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.28.3", "@babel/helpers": "^7.28.4", "@babel/parser": "^7.28.4", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.4", "@babel/types": "^7.28.4", "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" } }, "sha512-2BCOP7TN8M+gVDj7/ht3hsaO/B/n5oDbiAyyvnRlNOs+u1o+JWNYTQrmpuNp1/Wq2gcFrI01JAW+paEKDMx/CA=="], - "vitest/@vitest/expect": ["@vitest/expect@4.1.7", "", { "dependencies": { "@standard-schema/spec": "^1.1.0", "@types/chai": "^5.2.2", "@vitest/spy": "4.1.7", "@vitest/utils": "4.1.7", "chai": "^6.2.2", "tinyrainbow": "^3.1.0" } }, "sha512-1R+tw0ortHEbZDGMymm+pN7/AFQ/RkFFdtd7EN+VBpynKmLbP8A3rpEXdshBJ7+8hQ9zBJh/i1s0yKNtxAnU7w=="], - - "vitest/@vitest/spy": ["@vitest/spy@4.1.7", "", {}, "sha512-kbkI5LMWakyuTIvs6fUJ5qdIVb1XVKsYJAT4OJ938cHMROYMSfmoQdZy0aaAnjbbc8F61vkoTqz/Az+/HiIu5Q=="], - - "vitest/tinyexec": ["tinyexec@1.2.4", "", {}, "sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg=="], - - "vitest/vite": ["vite@7.1.10", "", { "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.5.0", "picomatch": "^4.0.3", "postcss": "^8.5.6", "rollup": "^4.43.0", "tinyglobby": "^0.2.15" }, "optionalDependencies": { "fsevents": "~2.3.3" }, "peerDependencies": { "@types/node": "^20.19.0 || >=22.12.0", "jiti": ">=1.21.0", "less": "^4.0.0", "lightningcss": "^1.21.0", "sass": "^1.70.0", "sass-embedded": "^1.70.0", "stylus": ">=0.54.8", "sugarss": "^5.0.0", "terser": "^5.16.0", "tsx": "^4.8.1", "yaml": "^2.4.2" }, "optionalPeers": ["@types/node", "jiti", "less", "lightningcss", "sass", "sass-embedded", "stylus", "sugarss", "terser", "tsx", "yaml"], "bin": { "vite": "bin/vite.js" } }, "sha512-CmuvUBzVJ/e3HGxhg6cYk88NGgTnBoOo7ogtfJJ0fefUWAxN/WDSUa50o+oVBxuIhO8FoEZW0j2eW7sfjs5EtA=="], - - "vitest/why-is-node-running": ["why-is-node-running@2.3.0", "", { "dependencies": { "siginfo": "^2.0.0", "stackback": "0.0.2" }, "bin": { "why-is-node-running": "cli.js" } }, "sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w=="], - "vscode-languageserver-protocol/vscode-jsonrpc": ["vscode-jsonrpc@8.2.0", "", {}, "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA=="], "wrap-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], @@ -5467,6 +5362,8 @@ "@modelcontextprotocol/sdk/express/type-is": ["type-is@2.1.0", "", { "dependencies": { "content-type": "^2.0.0", "media-typer": "^1.1.0", "mime-types": "^3.0.0" } }, "sha512-faYHw0anBbc/kWF3zFTEnxSFOAGUX9GFbOBthvDdLsIlEoWOFOtS0zgCiQYwIskL9iGXZL3kAXD8OoZ4GmMATA=="], + "@modelcontextprotocol/sdk/raw-body/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + "@octokit/auth-app/@octokit/request/@octokit/endpoint": ["@octokit/endpoint@11.0.3", "", { "dependencies": { "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" } }, "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag=="], "@octokit/auth-app/@octokit/request/@octokit/types": ["@octokit/types@16.0.0", "", { "dependencies": { "@octokit/openapi-types": "^27.0.0" } }, "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg=="], @@ -5593,8 +5490,6 @@ "@tailwindcss/oxide-wasm32-wasi/@napi-rs/wasm-runtime/@tybys/wasm-util": ["@tybys/wasm-util@0.10.2", "", { "dependencies": { "tslib": "^2.4.0" } }, "sha512-RoBvJ2X0wuKlWFIjrwffGw1IqZHKQqzIchKaadZZfnNpsAYp2mM0h36JtPCjNDAHGgYez/15uMBpfGwchhiMgg=="], - "@vitest/expect/@vitest/utils/@vitest/pretty-format": ["@vitest/pretty-format@3.2.4", "", { "dependencies": { "tinyrainbow": "^2.0.0" } }, "sha512-IVNZik8IVRJRTr9fxlitMKeJeXFFFN0JaB9PHPGQ8NKQbGpfjlTx9zO4RefN8gp7eqjNy8nyK3NZmBzOPeIxtA=="], - "accepts/mime-types/mime-db": ["mime-db@1.52.0", "", {}, "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg=="], "app-builder-lib/@electron/get/env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], @@ -5773,10 +5668,6 @@ "vite-plugin-solid/@babel/core/semver": ["semver@6.3.1", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA=="], - "vitest/@vitest/expect/@standard-schema/spec": ["@standard-schema/spec@1.1.0", "", {}, "sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w=="], - - "vitest/@vitest/expect/chai": ["chai@6.2.2", "", {}, "sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg=="], - "wrap-ansi-cjs/string-width/emoji-regex": ["emoji-regex@8.0.0", "", {}, "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A=="], "wrap-ansi-cjs/strip-ansi/ansi-regex": ["ansi-regex@5.0.1", "", {}, "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ=="], @@ -5853,6 +5744,8 @@ "@electron/universal/minimatch/brace-expansion/balanced-match": ["balanced-match@1.0.2", "", {}, "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw=="], + "@modelcontextprotocol/sdk/express/body-parser/iconv-lite": ["iconv-lite@0.7.2", "", { "dependencies": { "safer-buffer": ">= 2.1.2 < 3.0.0" } }, "sha512-im9DjEDQ55s9fL4EYzOAv0yMqmMBSZp6G0VvFyTMPKWxiSBHUj9NW/qqLmXUwXrrM7AvqSlTCfvqRb0cM8yYqw=="], + "@modelcontextprotocol/sdk/express/type-is/content-type": ["content-type@2.0.0", "", {}, "sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ=="], "@modelcontextprotocol/sdk/express/type-is/media-typer": ["media-typer@1.1.0", "", {}, "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw=="], diff --git a/install b/install index c50a9a154..245775e58 100755 --- a/install +++ b/install @@ -128,7 +128,7 @@ else combo="$os-$arch" case "$combo" in - linux-x64|linux-arm64|darwin-x64|darwin-arm64|windows-x64) + linux-x64|darwin-x64|darwin-arm64|windows-x64) ;; *) echo -e "${RED}Unsupported OS/Arch: $os/$arch${NC}" diff --git a/package.json b/package.json index fa4f49ab7..628e36de2 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,7 @@ "description": "AI-powered development tool", "private": true, "type": "module", - "packageManager": "bun@1.3.14", + "packageManager": "bun@1.4.2", "scripts": { "dev": "bun run --cwd packages/deepagent-code --conditions=browser src/index.ts", "dev:desktop": "bun --cwd packages/desktop dev", @@ -41,9 +41,9 @@ "@octokit/rest": "22.0.0", "@hono/standard-validator": "0.2.0", "@hono/zod-validator": "0.4.2", - "@opentui/core": "0.3.2", - "@opentui/keymap": "0.3.2", - "@opentui/solid": "0.3.2", + "@opentui/core": "0.5.12", + "@opentui/keymap": "0.5.12", + "@opentui/solid": "0.5.12", "ulid": "3.0.1", "@kobalte/core": "0.13.11", "@types/luxon": "3.7.1", diff --git a/packages/app/e2e/regression/panel-hosts.spec.ts b/packages/app/e2e/regression/panel-hosts.spec.ts index cadb924fe..f552f5383 100644 --- a/packages/app/e2e/regression/panel-hosts.spec.ts +++ b/packages/app/e2e/regression/panel-hosts.spec.ts @@ -104,11 +104,13 @@ test("Bottom Panel, movable views, Problems, and mobile reachability", async ({ await expect(page.locator("[data-terminal-pane]")).toHaveCount(0) await page.screenshot({ path: "e2e/test-results/panel-bottom-problems.png", fullPage: true }) - await bottom.getByRole("button", { name: "Move to Right Sidebar" }).click() + await page.getByRole("button", { name: "Panel Views" }).click() + await page.getByRole("button", { name: "Move to Right Sidebar: Problems" }).click() await expect(bottom.getByText("Type mismatch")).toBeHidden() const side = page.locator("#review-panel") await expect(side.getByText("Type mismatch")).toBeVisible() - await side.getByRole("button", { name: "Move to bottom dock" }).click() + await page.getByRole("button", { name: "Panel Views" }).click() + await page.getByRole("button", { name: "Move to Bottom Panel: Problems" }).click() await expect(bottom.getByText("Type mismatch")).toBeVisible() await expect(page.locator("[data-terminal-pane]")).toHaveCount(0) @@ -135,45 +137,24 @@ test("Bottom Panel, movable views, Problems, and mobile reachability", async ({ await bottom.getByText("Type mismatch").click() await expect(page.getByText("app.ts").first()).toBeVisible() - for (const view of ["Debug Console", "Terminal"]) { + for (const view of ["Debug Console"]) { await bottom.getByRole("tab", { name: view, exact: true }).click() - await bottom.getByRole("button", { name: "Move to Right Sidebar" }).click() + await page.getByRole("button", { name: "Panel Views" }).click() + await page.getByRole("button", { name: `Move to Right Sidebar: ${view}` }).click() await expect(side.getByText(view).first()).toBeVisible() - if (view === "Terminal") { - await expectTerminalPaneInHost(page, "side") - const actionBoxes = await Promise.all( - [ - side.getByLabel("Split terminal"), - side.getByLabel("New terminal"), - side.getByLabel("Move to bottom dock"), - side.getByRole("button", { name: "Close", exact: true }), - ].map(async (control) => { - await expect(control).toBeVisible() - return control.boundingBox() - }), - ) - const boxes = actionBoxes.filter((box): box is NonNullable => box !== null) - expect(boxes).toHaveLength(4) - expect(boxes.every((box) => Math.abs(box.y - boxes[0].y) <= 1)).toBe(true) - expect(boxes.every((box, index) => index === 0 || box.x > boxes[index - 1].x)).toBe(true) - await side.screenshot({ path: "e2e/test-results/panel-side-terminal-toolbar.png" }) - } - await side.getByRole("button", { name: "Move to bottom dock" }).click() + await page.getByRole("button", { name: "Panel Views" }).click() + await page.getByRole("button", { name: `Move to Bottom Panel: ${view}` }).click() await expect(bottom.getByRole("tab", { name: view, exact: true })).toBeVisible() - if (view === "Terminal") { - await bottom.getByRole("tab", { name: "Terminal", exact: true }).click() - await expectTerminalPaneInHost(page, "bottom") - } } await bottom.getByRole("tab", { name: "Problems", exact: true }).click() - for (const view of ["Terminal", "Debug Console", "Problems"]) { + for (const view of ["Debug Console", "Problems"]) { await page.getByRole("button", { name: "Panel Views" }).click() await page.getByRole("button", { name: `Move to Right Sidebar: ${view}` }).click() } - const unavailableBottomToggle = page.getByRole("button", { name: "Move a Panel View to the Bottom Panel first." }) - await expect(unavailableBottomToggle).toBeDisabled() - await expect(bottom).toHaveCSS("height", "0px") + await expect(bottom.getByRole("tab", { name: "Terminal", exact: true })).toBeVisible() + await bottom.getByRole("tab", { name: "Terminal", exact: true }).click() + await expectTerminalPaneInHost(page, "bottom") await page.getByRole("button", { name: "Panel Views" }).click() await page.getByRole("button", { name: "Move to Bottom Panel: Problems" }).click() await expect(bottom.getByText("Type mismatch")).toBeVisible() @@ -241,9 +222,7 @@ test("Terminal keeps one visible host and supports tabs plus atomic splits", asy await expect(page.locator("[data-terminal-pane]")).toHaveCount(0) await bottom.getByRole("tab", { name: "Terminal", exact: true }).click() await expect(page.locator("[data-terminal-pane]")).toHaveCount(4) - - await bottom.getByRole("button", { name: "Move to Right Sidebar" }).click() - await expect(page.locator('[data-terminal-host="bottom"]')).toHaveCount(0) - await expect(page.locator('[data-terminal-host="side"] [data-terminal-pane]')).toHaveCount(4) + await expect(page.locator('[data-terminal-host="bottom"] [data-terminal-pane]')).toHaveCount(4) + await expect(page.locator('[data-terminal-host="side"]')).toHaveCount(0) await expect(page.locator("[data-terminal-pane]")).toHaveCount(4) }) diff --git a/packages/app/e2e/regression/prompt-thinking-level.spec.ts b/packages/app/e2e/regression/prompt-thinking-level.spec.ts index 2aa4325b6..ed27a86db 100644 --- a/packages/app/e2e/regression/prompt-thinking-level.spec.ts +++ b/packages/app/e2e/regression/prompt-thinking-level.spec.ts @@ -1,4 +1,4 @@ -import { expect, test, type Page } from "@playwright/test" +import { expect, test } from "@playwright/test" import { base64Encode } from "@deepagent-code/core/util/encode" import { mockDeepAgentCodeServer } from "../utils/mock-server" import { expectAppVisible } from "../utils/waits" @@ -54,15 +54,11 @@ test("shows the V2 thinking level control while relevant", async ({ page }) => { }) await page.goto(`/${base64Encode(directory)}/session/${sessionID}`) - const composer = page.locator('[data-component="session-composer"]') + const composer = page.locator('[data-component="session-prompt-dock"]') const input = composer.locator('[data-component="prompt-input"]') const control = composer.locator('[data-component="prompt-variant-control"]') await expectAppVisible(composer) - await idleComposer(page) - await expect(control).toBeHidden() - - await composer.hover() await expect(control).toBeVisible() await control.locator('[data-action="prompt-model-variant"]').click() @@ -73,15 +69,11 @@ test("shows the V2 thinking level control while relevant", async ({ page }) => { await expect(high).toBeVisible() await high.click() - await idleComposer(page) await input.focus() await expect(control).toBeVisible() - await idleComposer(page) + await input.press("!") + await expect(control).toHaveCount(0) + await input.press("Backspace") await expect(control).toBeVisible() }) - -async function idleComposer(page: Page) { - await page.mouse.move(0, 0) - await page.evaluate(() => (document.activeElement as HTMLElement | null)?.blur()) -} diff --git a/packages/app/e2e/regression/session-list-path-loading.spec.ts b/packages/app/e2e/regression/session-list-path-loading.spec.ts index d9080b9d4..2d18cffa1 100644 --- a/packages/app/e2e/regression/session-list-path-loading.spec.ts +++ b/packages/app/e2e/regression/session-list-path-loading.spec.ts @@ -1,4 +1,4 @@ -import { test } from "@playwright/test" +import { expect, test } from "@playwright/test" import { fixture, pageMessages } from "../smoke/session-timeline.fixture" import { mockDeepAgentCodeServer } from "../utils/mock-server" import { expectAppVisible } from "../utils/waits" @@ -13,11 +13,13 @@ test("shows loaded sessions before the directory path request resolves", async ( }) let releasePath!: () => void + let pathRequests = 0 const pathBlocked = new Promise((resolve) => { releasePath = resolve }) await page.route("**/path?*", async (route) => { if (!new URL(route.request().url()).searchParams.has("directory")) return route.fallback() + pathRequests += 1 await pathBlocked return route.fallback() }) @@ -34,6 +36,8 @@ test("shows loaded sessions before the directory path request resolves", async ( await page.goto("/") try { + await page.getByRole("button", { name: /SmokeProject/ }).click() + await expect.poll(() => pathRequests).toBeGreaterThan(0) await expectAppVisible(page.getByText(fixture.expected.sourceTitle).first()) } finally { releasePath() diff --git a/packages/app/e2e/smoke/session-timeline.spec.ts b/packages/app/e2e/smoke/session-timeline.spec.ts index b04ee6609..0f1d038ce 100644 --- a/packages/app/e2e/smoke/session-timeline.spec.ts +++ b/packages/app/e2e/smoke/session-timeline.spec.ts @@ -207,10 +207,10 @@ async function expectCanScrollToStart( collectSeen(current, seenParts, seenMessages) samples.push(sampleTraversal(current, seenParts.size, seenMessages.size)) expectNoSmokeErrors(errors, current.errorToasts, current.forbiddenText) - expectOrderedIDs(expectedPartIDs, current.ids, "mounted part") - expectOrderedIDs(expectedPartIDs, current.visibleIds, "visible part") - expectOrderedIDs(expectedMessageIDs, unique(current.messageIds), "mounted message") - expectOrderedIDs(expectedMessageIDs, unique(current.visibleMessageIds), "visible message") + expectKnownIDs(expectedPartIDs, current.ids, "mounted part") + expectKnownIDs(expectedPartIDs, current.visibleIds, "visible part") + expectKnownIDs(expectedMessageIDs, unique(current.messageIds), "mounted message") + expectKnownIDs(expectedMessageIDs, unique(current.visibleMessageIds), "visible message") if ( current.scrollTop <= 1 && @@ -308,16 +308,15 @@ async function scrollTimelineUp(page: Page, before: SmokeState) { ) } -function expectOrderedIDs(expected: string[], actual: string[], label: string) { +function expectKnownIDs(expected: string[], actual: string[], label: string) { expect(actual.length, `${label} ids should not be empty`).toBeGreaterThan(0) - // Order-independent membership: the timeline may group part categories (e.g. - // tools before reasoning summaries) without changing what is mounted; the smoke - // contract is "every expected id is present", not a fixed visual order. - const actualSet = new Set(actual) - expect( - [...actualSet].filter((id) => expected.includes(id)).sort(), - `${label} ids`, - ).toEqual([...new Set(expected)].sort()) + // Virtualized rows expose only a window and may briefly mount the same logical + // row twice. Assistant parts within a turn may be grouped out of ID order, but + // turns must remain chronological while paging. + const mounted = unique(actual) + expect(mounted.filter((id) => !expected.includes(id)), `${label} ids`).toEqual([]) + const turns = mounted.map((id) => Number(id.match(/_smoke_(\d+)$/)?.[1])) + expect(turns, `${label} turn order`).toEqual(turns.slice().sort((a, b) => a - b)) } function unique(values: string[]) { @@ -388,10 +387,10 @@ async function expectSessionTimelineReady( for (const text of forbiddenText) await expect(page.getByText(text)).toHaveCount(0) const currentState = await timelineState(page) expectNoSmokeErrors(errors, currentState.errorToasts, currentState.forbiddenText) - expectOrderedIDs(expectedPartIDs, currentState.ids, "mounted part") - expectOrderedIDs(expectedPartIDs, currentState.visibleIds, "visible part") - expectOrderedIDs(expectedMessageIDs, unique(currentState.messageIds), "mounted message") - expectOrderedIDs(expectedMessageIDs, unique(currentState.visibleMessageIds), "visible message") + expectKnownIDs(expectedPartIDs, currentState.ids, "mounted part") + expectKnownIDs(expectedPartIDs, currentState.visibleIds, "visible part") + expectKnownIDs(expectedMessageIDs, unique(currentState.messageIds), "mounted message") + expectKnownIDs(expectedMessageIDs, unique(currentState.visibleMessageIds), "visible message") } function expectCompleteScroll( @@ -407,6 +406,7 @@ function expectCompleteScroll( expectedPartIDs.filter((id) => !seenParts.has(id)), `missing visible timeline parts\n${sampleSummary(samples)}`, ).toEqual([]) + expect([...seenParts].filter((id) => !expectedPartIDs.includes(id)), "unexpected visible timeline parts").toEqual([]) expect( expectedMessageIDs.filter((id) => !seenMessages.has(id)), `missing visible messages\n${sampleSummary(samples)}`, diff --git a/packages/app/package.json b/packages/app/package.json index d71b984ee..8a6097463 100644 --- a/packages/app/package.json +++ b/packages/app/package.json @@ -1,6 +1,6 @@ { "name": "@deepagent-code/app", - "version": "2.0.1", + "version": "2.0.2", "description": "", "type": "module", "exports": { diff --git a/packages/app/src/components/directory-picker.test.ts b/packages/app/src/components/directory-picker.test.ts index 98cf605c6..4e6d479a4 100644 --- a/packages/app/src/components/directory-picker.test.ts +++ b/packages/app/src/components/directory-picker.test.ts @@ -3,7 +3,6 @@ import { directoryPickerKind } from "./directory-picker-policy" const local = { type: "sidecar", - variant: "base", http: { url: "http://localhost:4096" }, } as const const remote = { diff --git a/packages/app/src/components/prompt-input.tsx b/packages/app/src/components/prompt-input.tsx index ee9cbf34a..985c688ee 100644 --- a/packages/app/src/components/prompt-input.tsx +++ b/packages/app/src/components/prompt-input.tsx @@ -273,6 +273,13 @@ export const PromptInput: Component = (props) => { requestAnimationFrame(() => { if (draftReview() || draftPreview()) return draftPreparePrompt = undefined + // D1: prepare locked the editor (blur + cursor-wait). When no draft surface remains — + // including a server-side degrade that ends preparation without ever producing a draft — + // hand focus back so the composer is usable again instead of looking stuck. + if (!props.disabled) { + editorRef.focus() + queueScroll() + } }) } diff --git a/packages/app/src/components/prompt-input/submit.test.ts b/packages/app/src/components/prompt-input/submit.test.ts index f47f53bf5..893658b17 100644 --- a/packages/app/src/components/prompt-input/submit.test.ts +++ b/packages/app/src/components/prompt-input/submit.test.ts @@ -1,5 +1,6 @@ import { beforeAll, beforeEach, describe, expect, mock, test } from "bun:test" import type { ContextItem, Prompt } from "@/context/prompt" +import { toV2Prompt } from "@deepagent-code/sdk/client" let createPromptSubmit: typeof import("./submit").createPromptSubmit @@ -33,13 +34,14 @@ const sentPromptAsync: Array<{ directory: string metadata?: unknown text?: string - parts?: Array<{ id?: string; type: string; text?: string }> + parts?: unknown[] messageID?: string intentID?: string intentSource?: string intentVariant?: string }> = [] const promptPrepareEvents: string[] = [] +const toastCalls: Array<{ variant?: string; title?: string; description?: string }> = [] const promptPrepareProgress: string[] = [] let params: { id?: string } = {} @@ -74,30 +76,31 @@ const clientFor = (directory: string) => { return { data: undefined } }, prompt: async () => ({ data: undefined }), - promptAsync: async (payload?: { - metadata?: unknown - parts?: Array<{ type: string; text?: string }> - messageID?: string - intentID?: string - intentSource?: string - intentVariant?: string + promptAsync: async () => { throw new Error("legacy prompt path") }, + command: async () => ({ data: undefined }), + abort: async () => ({ data: undefined }), + }, + v2: { session: { + prompt: async (payload: { + id?: string + prompt: { text: string; metadata?: unknown; files?: unknown[]; agents?: unknown[]; intent?: { id?: string; source?: string; variant?: string } } }) => { const sent = { directory, - metadata: payload?.metadata, - text: payload?.parts?.find((part) => part.type === "text")?.text, - parts: payload?.parts, - messageID: payload?.messageID, - intentID: payload?.intentID, - intentSource: payload?.intentSource, - intentVariant: payload?.intentVariant, + metadata: payload.prompt.metadata, + text: payload.prompt.text, + parts: [{ type: "text", text: payload.prompt.text }, ...(payload.prompt.files ?? []), ...(payload.prompt.agents ?? [])], + messageID: payload.id, + intentID: payload.prompt.intent?.id, + intentSource: payload.prompt.intent?.source, + intentVariant: payload.prompt.intent?.variant, } sentPromptAsync.push(sent) if ( - (sent.text === "receipt lost" || sent.text === "Edited retry goal") && - !rejectedAdmissionReceipts.has(sent.text) + (sent.text?.includes("receipt lost") || sent.text?.includes("Edited retry goal")) && + !rejectedAdmissionReceipts.has(sent.text ?? "") ) { - rejectedAdmissionReceipts.add(sent.text) + rejectedAdmissionReceipts.add(sent.text ?? "") throw new Error("connection closed after durable admission") } if (sent.text === "prompt waits after admission") { @@ -105,11 +108,9 @@ const clientFor = (directory: string) => { releaseDelayedPrompt = resolve }) } - return { data: { messageID: "msg_server_admitted", delivery: "steer" } } + return { data: { data: { id: "msg_server_admitted", delivery: "steer" } } } }, - command: async () => ({ data: undefined }), - abort: async () => ({ data: undefined }), - }, + } }, client: { request: async (payload: { url?: string @@ -140,6 +141,41 @@ const clientFor = (directory: string) => { }, }) } + if (text === "prepare degrades") { + const degradedResult = { + prompt_draft_id: "", + context_plan_id: "", + state: "general_ready", + mode: "intelligence", + route: "general", + goal: text, + preview: text, + intent_id: payload.body?.intent_id, + degraded: true, + } + return { + data: new ReadableStream({ + start(controller) { + controller.enqueue( + new TextEncoder().encode(`data: ${JSON.stringify({ type: "result", result: degradedResult })}\n\n`), + ) + controller.close() + }, + }), + } + } + if (text === "prepare stalls") { + return { + data: new ReadableStream({ + start(controller) { + // Emit one progress delta, then never the terminal event and never close: + // the exact hung-stream shape the D1 timeout must recover from. + controller.enqueue(new TextEncoder().encode(`data: ${JSON.stringify({ type: "progress", preview: "Partial" })}\n\n`)) + payload.signal?.addEventListener("abort", () => controller.close(), { once: true }) + }, + }), + } + } if (text === "prepare waits") { return { data: new ReadableStream({ @@ -196,6 +232,7 @@ beforeAll(async () => { })) mock.module("@deepagent-code/sdk/client", () => ({ + toV2Prompt, createDeepAgentCodeClient: (input: { directory: string }) => { createdClients.push(input.directory) return clientFor(input.directory) @@ -204,7 +241,10 @@ beforeAll(async () => { mock.module("@deepagent-code/ui/toast", () => ({ Toast: { Region: () => null }, - showToast: () => 0, + showToast: (options: { variant?: string; title?: string; description?: string }) => { + toastCalls.push(options) + return 0 + }, })) mock.module("@deepagent-code/core/util/encode", () => ({ @@ -352,6 +392,7 @@ beforeAll(async () => { const mod = await import("./submit") createPromptSubmit = mod.createPromptSubmit + mod.setPrepareStreamIdleTimeoutMs(200) }) beforeEach(() => { @@ -369,6 +410,7 @@ beforeEach(() => { preparedIntents.length = 0 sentPromptAsync.length = 0 promptPrepareEvents.length = 0 + toastCalls.length = 0 promptPrepareProgress.length = 0 promptContextItems.length = 0 promptValue[0] = { type: "text", content: "ls", start: 0, end: 2 } @@ -705,6 +747,117 @@ describe("prompt submit worktree selection", () => { promptValue[0] = { type: "text", content: "ls", start: 0, end: 2 } }) + // D1: the W1-3 degrade must also be VISIBLE — a toast accompanies the direct send. + test("shows a degrade toast when intelligence prompt preparation fails", async () => { + params = { id: "session-1" } + promptMode = "intelligence" + promptValue[0] = { type: "text", content: "prepare fails", start: 0, end: 13 } + + const submit = createPromptSubmit({ + info: () => ({ id: "session-1" }), + imageAttachments: () => [], + commentCount: () => 0, + autoAccept: () => false, + mode: () => "normal", + working: () => false, + editor: () => undefined, + queueScroll: () => undefined, + promptLength: (value) => value.reduce((sum, part) => sum + ("content" in part ? part.content.length : 0), 0), + addToHistory: () => undefined, + resetHistoryNavigation: () => undefined, + setMode: () => undefined, + setPopover: () => undefined, + onSubmit: () => undefined, + }) + + await submit.handleSubmit({ preventDefault: () => undefined } as unknown as Event) + await flushAsyncSubmit() + + expect(sentPromptAsync[0]?.text).toBe("prepare fails") + expect(toastCalls.length).toBe(1) + expect(toastCalls[0]?.title).toContain("Intelligence unavailable") + }) + + // D1: the server's own fail-soft degrade arrives as a NORMAL result event (route:general + + // degraded:true) — the catch path never fires, so the toast must key off the flag. + test("shows a degrade toast when the server marks the result as degraded", async () => { + params = { id: "session-1" } + promptMode = "intelligence" + promptValue[0] = { type: "text", content: "prepare degrades", start: 0, end: 16 } + + const submit = createPromptSubmit({ + info: () => ({ id: "session-1" }), + imageAttachments: () => [], + commentCount: () => 0, + autoAccept: () => false, + mode: () => "normal", + working: () => false, + editor: () => undefined, + queueScroll: () => undefined, + promptLength: (value) => value.reduce((sum, part) => sum + ("content" in part ? part.content.length : 0), 0), + addToHistory: () => undefined, + resetHistoryNavigation: () => undefined, + setMode: () => undefined, + setPopover: () => undefined, + onSubmit: () => undefined, + }) + + await submit.handleSubmit({ preventDefault: () => undefined } as unknown as Event) + await flushAsyncSubmit() + + expect(sentPromptAsync[0]?.text).toBe("prepare degrades") + expect(sentPromptAsync[0]?.metadata).toEqual({ + deepagent: { + agent_mode_override: "general", + prompt_pipeline: { + mode: "direct_override", + }, + }, + }) + expect(toastCalls.length).toBe(1) + expect(toastCalls[0]?.title).toContain("Intelligence unavailable") + }) + + // D1: a prepare stream that never emits its terminal event (hung refinement / dropped body) + // must time out and degrade to direct instead of locking the composer forever. + test("times out a stalled prepare stream and degrades to direct", async () => { + params = { id: "session-1" } + promptMode = "intelligence" + promptValue[0] = { type: "text", content: "prepare stalls", start: 0, end: 14 } + + const submit = createPromptSubmit({ + info: () => ({ id: "session-1" }), + imageAttachments: () => [], + commentCount: () => 0, + autoAccept: () => false, + mode: () => "normal", + working: () => false, + editor: () => undefined, + queueScroll: () => undefined, + promptLength: (value) => value.reduce((sum, part) => sum + ("content" in part ? part.content.length : 0), 0), + addToHistory: () => undefined, + resetHistoryNavigation: () => undefined, + setMode: () => undefined, + setPopover: () => undefined, + onSubmit: () => undefined, + }) + + await submit.handleSubmit({ preventDefault: () => undefined } as unknown as Event) + // The stall is recovered by the 200ms idle deadline (shrunk in beforeAll), so wait past it. + await new Promise((resolve) => setTimeout(resolve, 300)) + await flushAsyncSubmit() + + expect(sentPromptAsync[0]?.text).toBe("prepare stalls") + expect(sentPromptAsync[0]?.metadata).toEqual({ + deepagent: { + agent_mode_override: "general", + prompt_pipeline: { + mode: "direct_override", + }, + }, + }) + }) + test("stops intelligence prompt preparation without submitting", async () => { params = { id: "session-1" } promptMode = "intelligence" diff --git a/packages/app/src/components/prompt-input/submit.ts b/packages/app/src/components/prompt-input/submit.ts index ec82b4e53..d58658e6f 100644 --- a/packages/app/src/components/prompt-input/submit.ts +++ b/packages/app/src/components/prompt-input/submit.ts @@ -1,4 +1,5 @@ import type { Message, Part, Session } from "@deepagent-code/sdk/client" +import { toV2Prompt } from "@deepagent-code/sdk/client" import { showToast } from "@/utils/toast" import { base64Encode } from "@deepagent-code/core/util/encode" import { Binary } from "@deepagent-code/core/util/binary" @@ -29,6 +30,13 @@ type PendingPrompt = { const pending = new Map() +// D1: idle deadline for the prepare SSE stream (see prepareDeepAgentPromptDraft). Exported +// mutable so tests can shrink it instead of waiting the real 90s. +export let prepareStreamIdleTimeoutMs = 90_000 +export const setPrepareStreamIdleTimeoutMs = (ms: number) => { + prepareStreamIdleTimeoutMs = ms +} + export type FollowupDraft = { sessionID: string sessionDirectory: string @@ -58,6 +66,9 @@ export type DeepAgentPromptPrepareResult = { goal: string preview: string intent_id?: string + // D1: server-side fail-soft degrade marker (refinement failed; raw input goes out directly). + // Absent on a plain general-chat classification, which is the same route:"general" shape. + degraded?: boolean } export type DeepAgentPromptConfirmResult = { editedGoal: string } @@ -118,6 +129,7 @@ type FollowupSendInput = { onPromptPrepareProgress?: (preview: string) => void onPromptPrepareEnd?: () => void onPromptPrepareDiscard?: () => void + suppressPrepareDegradeToast?: boolean promptPrepareSignal?: AbortSignal promptOutputLanguage?: DeepAgentPromptOutputLanguage confirmPromptDraft?: (draft: DeepAgentPromptPrepareResult) => Promise @@ -205,17 +217,42 @@ async function prepareDeepAgentPromptDraft(input: { prepared = event.result } - while (true) { - const part = await reader.read() - if (part.done) break - buffer += decoder.decode(part.value, { stream: true }) - const blocks = buffer.split("\n\n") - buffer = blocks.pop() ?? "" - blocks.forEach(readEvent) + // D1 guard: the terminal SSE event must arrive for the stream to end. A server that never + // emits one (hung refinement, a dropped connection that keeps the body open) would leave the + // composer in its locked preparing state forever. Race each read against an idle deadline so + // the failure surfaces to the W1-3 degrade path instead. The timer is cleared per iteration + // (no leak across chunks) and the reader is cancelled on ANY early exit so the underlying + // connection does not stay pinned. + const readChunk = async () => { + let timer: ReturnType | undefined + const deadline = new Promise((_, reject) => { + timer = setTimeout(() => reject(new Error("Prompt draft prepare stream timed out")), prepareStreamIdleTimeoutMs) + }) + try { + return await Promise.race([reader.read(), deadline]) + } finally { + clearTimeout(timer) + } + } + try { + while (true) { + const part = await readChunk() + if (part.done) break + buffer += decoder.decode(part.value, { stream: true }) + const blocks = buffer.split("\n\n") + buffer = blocks.pop() ?? "" + blocks.forEach(readEvent) + } + buffer += decoder.decode() + if (buffer.trim()) readEvent(buffer) + } catch (err) { + await reader.cancel().catch(() => undefined) + throw err + } + if (!prepared) { + await reader.cancel().catch(() => undefined) + throw new Error("Prompt draft prepare returned no result") } - buffer += decoder.decode() - if (buffer.trim()) readEvent(buffer) - if (!prepared) throw new Error("Prompt draft prepare returned no result") return prepared } @@ -347,11 +384,30 @@ export async function sendFollowupDraft(input: FollowupSendInput) { // W1-3 — degrade to the direct path instead of blocking the send: refinement is an // enhancement, not a gate. V2-only servers already degrade server-side (W0-3a); this // covers older servers and transient prepare failures so the raw input still goes out. - void err + // D1: the degrade must be VISIBLE — a silent fallthrough reads as "the button did + // nothing" when the composer unlocks with the draft still parked in the editor. + if (!input.suppressPrepareDegradeToast) { + showToast({ + variant: "error", + title: "Intelligence unavailable — sent directly", + description: err instanceof Error ? err.message : String(err), + }) + } degradedToDirect = true } input.onPromptPrepareEnd?.() if (degradedToDirect || !prepared || prepared.route === "general") { + // D1: the most common real-world failure is the server's own fail-soft degrade — a NORMAL + // result event (route:"general") that the catch branch above never sees. The server marks + // those with degraded:true; distinguish them from a plain general-chat classification + // (same wire shape, no flag) and surface the same toast as the client-side degrade. + if (!degradedToDirect && prepared?.degraded && !input.suppressPrepareDegradeToast) { + showToast({ + variant: "error", + title: "Intelligence unavailable — sent directly", + description: "Prompt preparation failed on the server; your message was sent as a direct prompt.", + }) + } input.onPromptPrepareDiscard?.() metadata = { deepagent: { @@ -440,11 +496,15 @@ export async function sendFollowupDraft(input: FollowupSendInput) { metadata, } input.onPromptInput?.({ promptInput, optimisticParts: submittedParts.optimisticParts }) - const admission = await input.client.session.promptAsync(promptInput) - if (!admission.data?.messageID) throw new Error("Prompt admission returned no durable receipt") + const admission = await input.client.v2.session.prompt({ + sessionID: promptInput.sessionID, + id: promptInput.messageID, + prompt: toV2Prompt(promptInput), + }) + if (!admission.data?.data.id) throw new Error("Prompt admission returned no durable receipt") // A chat steer is only projected into canonical history at the next provider boundary. Keep the // client-keyed placeholder visible until that correlated message.updated event replaces it. - if (admission.data.messageID !== messageID && admission.data.delivery !== "steer") remove() + if (admission.data.data.id !== messageID && admission.data.data.delivery !== "steer") remove() return true } catch (err) { batch(() => { @@ -479,6 +539,8 @@ type PromptSubmitInput = { onPromptPrepareProgress?: (preview: string) => void onPromptPrepareEnd?: () => void onPromptPrepareDiscard?: () => void + // D1: tests pass true to keep the visible-degrade toast out of unit assertions. + suppressPrepareDegradeToast?: boolean confirmPromptDraft?: (draft: DeepAgentPromptPrepareResult) => Promise } diff --git a/packages/app/src/components/review/dialog-review-contract.test.ts b/packages/app/src/components/review/dialog-review-contract.test.ts index 049fb47fe..d3fd6af89 100644 --- a/packages/app/src/components/review/dialog-review-contract.test.ts +++ b/packages/app/src/components/review/dialog-review-contract.test.ts @@ -1,5 +1,5 @@ import { describe, expect, test } from "bun:test" -import { listPending, reviewSummary, setStatus, listEnvFacts, decideEnvFact, modifyEnvFact } from "./dialog-review.api" +import { listPending, reviewSummary, setStatus, listEnvFacts, decideEnvFact, modifyEnvFact, groupPendingByReason, type KnowledgeItem } from "./dialog-review.api" // P1-C route contract: the V3.1 self-learning Review dialog talks to the raw-request escape-hatch // routes (NOT the generated SDK). These assertions lock the exact method/url/body so a backend @@ -20,6 +20,39 @@ function client(calls: Recorded[], data: unknown) { } describe("DeepAgent review dialog route contract", () => { + test("pending inbox candidates group by reason within their authority scope", () => { + const item: KnowledgeItem = { + sourceStore: "project", + id: "knowledge:one", + version: 1, + hash: "hash", + candidateId: "candidate-one", + fingerprint: "fingerprint", + governanceRevision: "governance", + type: "memory", + summary: "one", + evidence_strength: "weak", + evidence_refs: [], + approval_status: "pending", + scope: "durable:project:one", + inboxID: "inbox:candidate-one", + reasonGroup: "manual review policy", + } + const groups = groupPendingByReason([ + item, + { ...item, id: "knowledge:two", candidateId: "candidate-two", inboxID: "inbox:candidate-two" }, + { ...item, id: "knowledge:three", candidateId: "candidate-three", reasonGroup: "sensitive" }, + { ...item, id: "knowledge:global", sourceStore: "user_global", scope: "durable" }, + { ...item, id: "knowledge:approved", approval_status: "approved" }, + ]) + expect(groups.map((group) => [group.scope, group.reason, group.items.length])).toEqual([ + ["project", "manual review policy", 2], + ["project", "sensitive", 1], + ["global", "manual review policy", 1], + ]) + expect(groups[0]?.items.map((entry) => entry.inboxID)).toEqual(["inbox:candidate-one", "inbox:candidate-two"]) + }) + test("listPending GETs /deepagent/knowledge/pending and unwraps items", async () => { const calls: Recorded[] = [] const items = [ diff --git a/packages/app/src/components/review/dialog-review.api.ts b/packages/app/src/components/review/dialog-review.api.ts index 1a99c2e28..9e41863fc 100644 --- a/packages/app/src/components/review/dialog-review.api.ts +++ b/packages/app/src/components/review/dialog-review.api.ts @@ -19,6 +19,9 @@ export type KnowledgeItem = { // Storage scope, for grouping by project vs global: "durable" (global) or // "durable:project:". Absent from older servers → treated as global. scope?: string + inboxID?: string + reviewReason?: string + reasonGroup?: string } type RawSdkClient = { @@ -82,6 +85,22 @@ export const reviewAuthorityKey = (item: KnowledgeItem) => item.governanceRevision, ]) +export const groupPendingByReason = (items: readonly KnowledgeItem[]) => { + const groups = new Map() + items.filter((item) => item.approval_status === "pending").forEach((item) => { + const scope = item.scope?.startsWith("durable:project:") ? "project" : "global" + const reason = item.reasonGroup ?? "other" + const key = JSON.stringify([scope, reason]) + const group = groups.get(key) + if (group) group.items.push(item) + else groups.set(key, { scope, reason, items: [item] }) + }) + return [...groups.values()].sort((a, b) => + (a.scope === "project" ? 0 : 1) - (b.scope === "project" ? 0 : 1) || + a.reason.localeCompare(b.reason), + ) +} + // V3.8.1 §G environment-fact use-gate. Provisional user-global environment facts surface here so the // user decides, per project, whether to adopt them (§G.5). Credentials never appear — only secret_ref // pointers. `degraded` marks a fact whose last connection attempt failed (§G.6). diff --git a/packages/app/src/components/review/dialog-review.tsx b/packages/app/src/components/review/dialog-review.tsx index 599158f58..f357963e3 100644 --- a/packages/app/src/components/review/dialog-review.tsx +++ b/packages/app/src/components/review/dialog-review.tsx @@ -11,6 +11,7 @@ import { decideEnvFact, modifyEnvFact, reviewAuthorityKey, + groupPendingByReason, type KnowledgeItem, type ReviewClient, type EnvFactItem, @@ -26,6 +27,7 @@ export { decideEnvFact, modifyEnvFact, reviewAuthorityKey, + groupPendingByReason, type KnowledgeItem, type ReviewClient, type EnvFactBody, @@ -153,32 +155,24 @@ export const ReviewPanel: Component<{ client: ReviewClient }> = (props) => { const pending = createMemo(() => (items() ?? []).filter((i) => i.approval_status === "pending" && matchesQuery(i))) const approved = createMemo(() => (items() ?? []).filter((i) => i.approval_status === "approved" && matchesQuery(i))) - // Group by SCOPE, not by type: the governance view shows the user's learned facts split into - // "this project" vs "global". A doc scoped `durable:project:` is project-local; anything else - // (`durable`, or legacy untagged) is global. (Skills + domain-pack seed docs are already excluded - // server-side, so every item here is a human-readable, user-learned fact.) - const scopeOf = (item: KnowledgeItem): "project" | "global" => - item.scope?.startsWith("durable:project:") ? "project" : "global" const scopeLabel = (scope: string) => scope === "project" ? language.t("review.scope.project") : language.t("review.scope.global") - // Project bucket first (most specific to what the user is working on), then global. - const SCOPE_ORDER = ["project", "global"] as const - const pendingGroups = createMemo(() => { - const byScope = new Map() - for (const item of pending()) { - const key = scopeOf(item) - const list = byScope.get(key) - if (list) list.push(item) - else byScope.set(key, [item]) - } - const ordered: Array<{ scope: string; items: KnowledgeItem[] }> = [] - for (const scope of SCOPE_ORDER) { - const list = byScope.get(scope) - if (list?.length) ordered.push({ scope, items: list }) - } - return ordered - }) + const pendingGroups = createMemo(() => groupPendingByReason(pending())) + const reasonLabel = (reason: string) => { + const key = { + "manual review policy": "review.reason.manual", + "reviewer unavailable": "review.reason.reviewerUnavailable", + sensitive: "review.reason.sensitive", + contradiction: "review.reason.contradiction", + pack_promotion: "review.reason.packPromotion", + global_promotion: "review.reason.globalPromotion", + "near-duplicate merge requires review": "review.reason.nearDuplicate", + "confidence below automatic admission floor": "review.reason.lowConfidence", + other: "review.reason.other", + }[reason] + return key ? language.t(key) : reason + } // Which boxes are expanded. Default: all pending type boxes open, approved collapsed. const [collapsed, setCollapsed] = createSignal>(new Set(["approved"])) @@ -236,6 +230,7 @@ export const ReviewPanel: Component<{ client: ReviewClient }> = (props) => {