Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
115 changes: 99 additions & 16 deletions .github/workflows/desktop-build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,18 @@ name: desktop-build
#
# macOS signing/notarization is opt-in: if the APPLE_* / CSC_* secrets are present it
# signs, otherwise it builds an UNSIGNED package (testers right-click → Open to launch).
# Windows/Linux packages are unsigned here.
# Windows signing is likewise opt-in via the AZURE_TRUSTED_SIGNING_* secrets (W-c);
# Linux packages are unsigned here.

on:
push:
branches:
- main
# W-01 §2.2.6 trigger extension: exercise the Windows packaging path on
# the dev mainline too (windows targets only, no release publishing — the
# release job stays main-only), so NSIS + Trusted Signing + the packaged
# smoke regress on every dev push instead of only at release time.
- dev
workflow_dispatch:
inputs:
channel:
Expand Down Expand Up @@ -66,20 +72,31 @@ jobs:
shell: bash
env:
PLATFORMS: ${{ github.event.inputs.platforms || 'all' }}
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"}'
linux='{"host":"ubuntu-24.04","target":"linux-amd64","platform_flag":"--linux deb --x64","group":"linux"}'
win='{"host":"windows-2025","target":"win-x64","platform_flag":"--win nsis --x64","group":"win"}'
case "$PLATFORMS" in
mac) items="$mac" ;;
linux) items="$linux" ;;
windows) items="$win" ;;
all) items="$mac,$linux,$win" ;;
*) items="$mac,$linux,$win" ;;
esac
# 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"}'
# 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" ;;
linux) items="$linux" ;;
windows) items="$win" ;;
*) items="$mac,$linux,$win" ;;
esac
fi
echo "matrix={\"include\":[$items]}" >> "$GITHUB_OUTPUT"

build:
Expand All @@ -98,6 +115,11 @@ jobs:
# needs the Apple ID + app-specific password + team id. electron-builder.config.ts treats
# mac signing as available when all three are set.
HAS_APPLE_NOTARY: ${{ secrets.APPLE_ID != '' && secrets.APPLE_APP_SPECIFIC_PASSWORD != '' && secrets.APPLE_TEAM_ID != '' }}
# Windows signing uses Azure Trusted Signing (script/sign-windows.ps1, wired through
# electron-builder's per-file sign hook). The script authenticates with the Azure CLI,
# so the pipeline needs a federated service principal in addition to the endpoint /
# account / profile names. When any piece is missing the build proceeds unsigned.
HAS_TRUSTED_SIGNING: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT != '' && secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME != '' && secrets.AZURE_TRUSTED_SIGNING_CERTIFICATE_PROFILE != '' && secrets.AZURE_TRUSTED_SIGNING_CLIENT_ID != '' && secrets.AZURE_TRUSTED_SIGNING_TENANT_ID != '' && secrets.AZURE_TRUSTED_SIGNING_SUBSCRIPTION_ID != '' }}
steps:
- uses: actions/checkout@v4

Expand All @@ -116,6 +138,29 @@ jobs:
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}

# Trusted Signing authenticates through the Azure CLI credential chain
# (sign-windows.ps1 keeps only AzureCliCredential enabled), so log in with
# a federated workload identity before packaging.
- name: Azure login for Trusted Signing
if: matrix.group == 'win' && env.HAS_TRUSTED_SIGNING == 'true'
uses: azure/login@v2
with:
client-id: ${{ secrets.AZURE_TRUSTED_SIGNING_CLIENT_ID }}
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
Expand All @@ -140,8 +185,43 @@ jobs:
APPLE_ID: ${{ secrets.APPLE_ID }}
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
# Azure Trusted Signing (Windows). sign-windows.ps1 skips silently when
# these are unset, matching the opt-in unsigned behavior of the mac path.
AZURE_TRUSTED_SIGNING_ENDPOINT: ${{ secrets.AZURE_TRUSTED_SIGNING_ENDPOINT }}
AZURE_TRUSTED_SIGNING_ACCOUNT_NAME: ${{ secrets.AZURE_TRUSTED_SIGNING_ACCOUNT_NAME }}
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.
- name: Assert NSIS installers and signatures
if: matrix.group == 'win'
working-directory: packages/desktop
shell: pwsh
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 ', ')"
exit 1
}
if ($env:HAS_TRUSTED_SIGNING -eq "true") {
foreach ($installer in @($x64, $arm64)) {
$signature = Get-AuthenticodeSignature -FilePath $installer.FullName
if ($signature.Status -ne "Valid") {
Write-Error "$($installer.Name) signature status '$($signature.Status)': $($signature.StatusMessage)"
exit 1
}
Write-Host "$($installer.Name) signed by $($signature.SignerCertificate.Subject)"
}
} else {
Write-Host "Trusted Signing not configured; asserting installer presence only"
}

- name: Smoke test packaged macOS app
if: matrix.group == 'mac'
working-directory: packages/desktop
Expand All @@ -150,18 +230,21 @@ jobs:
test -n "$executable"
DEEPAGENT_CODE_DESKTOP_EXECUTABLE="$executable" bun run test:subagents-cold-start

# W9.3: first real Windows run of the local-first + WSL-fallback routing.
# Launches the packaged app and asserts GET /global/health with Basic auth
# (the desktop sidecar always configures a password, so the typed API route
# answers 401 to a credential-less curl — the auth'd fetch proves the full
# sidecar bootstrap path, not just a listening port).
# W9.3: first real Windows run of the native-only sidecar routing (local
# spawn, no platform fallback). Launches the packaged app and asserts
# GET /global/health with Basic auth (the desktop sidecar always
# configures a password, so the typed API route answers 401 to a
# credential-less curl — the auth'd fetch proves the full sidecar
# bootstrap path, not just a listening port).
- name: Smoke test packaged Windows sidecar health
if: matrix.group == 'win'
working-directory: packages/desktop
shell: pwsh
run: |
# Wildcard matches both prod ("DeepAgent Code.exe") and beta
# ("DeepAgent Code Beta.exe") artifact names so the beta build
# 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")
# 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
if (-not $exe) {
Expand Down
23 changes: 23 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,29 @@ jobs:
- name: Run test fixture gate
run: bun script/check-test-fixtures.ts

manifest-digest-gate:
name: manifest-digest-gate
# K-05/B-11: the C7-10 deterministic manifest must reproduce from the committed
# tree on any machine — byte-stable across runs, pinned to head-pin.ts, every
# digest input git-tracked, and git-ignored .artifacts/ residue unable to
# perturb the digest. Pure stateless gate, mirrors the test-fixture-gate pattern.
runs-on: blacksmith-4vcpu-ubuntu-2404
defaults:
run:
shell: bash
steps:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
with:
token: ${{ secrets.GITHUB_TOKEN }}

- name: Setup Bun
uses: ./.github/actions/setup-bun

- name: Run manifest digest reproducibility gate
working-directory: packages/core
run: bun run manifest:assert-reproducible

unit:
name: unit (${{ matrix.settings.name }})
strategy:
Expand Down
16 changes: 15 additions & 1 deletion .github/workflows/typecheck.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,21 @@ on:

jobs:
typecheck:
runs-on: blacksmith-4vcpu-ubuntu-2404
# W-01 §2.2.6: typecheck the Windows surface too — the win32 branches added
# by the native Windows work (shell dialect layer, data-directory split,
# registry env import, DPAPI, NSIS) must stay type-clean on the OS they
# target, and path/name casing errors only surface on a case-preserving
# filesystem.
name: typecheck (${{ matrix.settings.name }})
strategy:
fail-fast: false
matrix:
settings:
- name: linux
host: blacksmith-4vcpu-ubuntu-2404
- name: windows
host: blacksmith-4vcpu-windows-2025
runs-on: ${{ matrix.settings.host }}
steps:
- name: Checkout repository
uses: actions/checkout@34e114876b0b11c390a56381ad16ebd13914f8d5 # v4.3.1
Expand Down
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,9 @@ logs/
tsconfig.tsbuildinfo
/packages/*/.artifacts/
/script/live-llm.config.local.json
/script/live-llm.config.local.*.json
.tmp/
kimi-export-*.md

# Internal design and release artifacts are local-only. Stable public documentation belongs in
# README files, language documentation, or the reviewed design/ tree.
Expand Down
35 changes: 35 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,41 @@

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.1 / Desktop 2.0.1

Release labels: `core-v2.0.1` and `desktop-v2.0.1` (`2.0.1`).

### Windows native support

- Native Windows is now a first-class platform: data, caches and worktrees live under `%LOCALAPPDATA%`, config and credentials roam under `%APPDATA%`, and a one-time migration moves the legacy unified home on first start.
- Shell validation understands PowerShell semantics: commands run through a pwsh → powershell → cmd resolution chain, POSIX scripts resolve through Git Bash when present, and unrepresentable commands fail with typed guidance instead of a blanket platform refusal.
- The Windows desktop installer is a guided NSIS setup that adds the app to the user PATH (no system changes), and the desktop imports user/system environment variables from the registry at startup. The WSL requirement is removed across app, docs and packaging.
- Secrets on Windows are stored through DPAPI-backed encryption instead of plaintext fallbacks.

### Session migration and backup tooling

- Full-transcript Markdown export for every session, with a resumable batch exporter and a per-run content manifest.
- A guided V1→V2 migration flow: staged phases with progress in the maintenance shell, verified backups, a post-migration compliance report, backup retention governance, and an explicitly confirmed disk-reclaim step that never touches incident copies.

### Providers and models

- Custom config-defined providers now run on the V2 runtime end to end; undeclared pricing and limits surface as honestly unavailable instead of zero.
- Fixed custom providers being unresolvable in V2 sessions (origin-tracked catalog entries).

### Subagents and recovery

- Subagent permission inheritance keeps a restrictive parent from silently stripping child agents of every tool.
- Task fan-out accounting survives restarts; worktrees left by timed-out runs are reclaimed after a grace period while recovery-required runs are never touched.
- Provider recovery exposes one command surface across legacy and V2 receipts, including an evidence-bound confirm-settled exit; sessions blocked from redrive surface with explicit reasons.

### Fixes

- Tool result metadata (exit codes, truncation markers, output paths) is visible again on projected sessions.
- Bash results always end with a canonical `exit code: N` trailer; registry environment expansion cannot crash startup on circular values.
- Structured-output evidence from subagents is bound to the answer message as designed.
- Updated dependencies covering 14 published advisories (including a critical template RCE).
- `deepagent export --format md` writes a titled Markdown transcript next to the session.

## Core 2.0 / Desktop 2.0

Release labels: `core-v2.0` and `desktop-v2.0` (`2.0.0`).
Expand Down
Loading
Loading