diff --git a/.github/workflows/release-policy.yml b/.github/workflows/release-policy.yml
new file mode 100644
index 00000000000..8014145939b
--- /dev/null
+++ b/.github/workflows/release-policy.yml
@@ -0,0 +1,98 @@
+name: Release Policy
+
+on:
+ release:
+ types:
+ - published
+ - edited
+
+permissions:
+ contents: write
+
+concurrency:
+ group: release-policy
+ cancel-in-progress: false
+
+jobs:
+ enforce:
+ if: github.repository == 'stablyai/orca'
+ runs-on: ubuntu-latest
+ timeout-minutes: 5
+ steps:
+ - name: Enforce release policy
+ uses: actions/github-script@v8
+ with:
+ script: |
+ const release = context.payload.release;
+ const tag = release.tag_name;
+ const author = release.author?.login;
+ const number = "(?:0|[1-9][0-9]*)";
+ const version = `${number}\\.${number}\\.${number}`;
+ const stableTag = new RegExp(`^v${version}$`);
+ const prereleaseTag = new RegExp(
+ `^(?:v${version}-rc\\.${number}(?:\\.[0-9A-Za-z]+)?|mobile(?:-android)?-v${version})$`,
+ );
+ const expectedPrerelease = prereleaseTag.test(tag);
+ const allowed = author === "github-actions[bot]" &&
+ (stableTag.test(tag) || expectedPrerelease);
+ const { owner, repo } = context.repo;
+
+ async function restoreLatestStable() {
+ const releases = await github.paginate(github.rest.repos.listReleases, {
+ owner,
+ repo,
+ per_page: 100,
+ });
+ const stable = releases
+ .filter((candidate) =>
+ !candidate.draft &&
+ !candidate.prerelease &&
+ candidate.author?.login === "github-actions[bot]" &&
+ stableTag.test(candidate.tag_name),
+ )
+ .sort((left, right) => {
+ const a = left.tag_name.slice(1).split(".").map(Number);
+ const b = right.tag_name.slice(1).split(".").map(Number);
+ return a.reduce((result, part, index) => result || part - b[index], 0);
+ })
+ .at(-1);
+ if (stable) {
+ await github.rest.repos.updateRelease({
+ owner,
+ repo,
+ release_id: stable.id,
+ make_latest: "true",
+ });
+ }
+ }
+
+ if (allowed) {
+ if (release.prerelease !== expectedPrerelease) {
+ await github.rest.repos.updateRelease({
+ owner,
+ repo,
+ release_id: release.id,
+ prerelease: expectedPrerelease,
+ make_latest: expectedPrerelease ? "false" : "legacy",
+ });
+ }
+ await restoreLatestStable();
+ return;
+ }
+
+ await github.rest.repos.updateRelease({
+ owner,
+ repo,
+ release_id: release.id,
+ draft: true,
+ prerelease: true,
+ make_latest: "false",
+ });
+ await restoreLatestStable();
+ await github.rest.repos.deleteRelease({ owner, repo, release_id: release.id });
+ try {
+ await github.rest.git.deleteRef({ owner, repo, ref: `tags/${tag}` });
+ } catch (error) {
+ if (error.status !== 404) throw error;
+ }
+ core.warning(`Deleted unauthorized release ${tag} created by ${author || "unknown"}.`);
diff --git a/README.md b/README.md
index 670b54e2835..b679cb95c3d 100644
--- a/README.md
+++ b/README.md
@@ -36,7 +36,7 @@
Monitor and steer your agents from your phone — get notified when an agent finishes and send follow-ups from anywhere.
-[iOS App Store](https://apps.apple.com/us/app/orca-ide/id6766130217) · [TestFlight](https://testflight.apple.com/join/YjeGMQBA) · [Android APK 0.0.37](https://github.com/stablyai/orca/releases/download/mobile-android-v0.0.37/app-release.apk) · [Docs →](https://www.onorca.dev/docs/mobile)
+[iOS App Store](https://apps.apple.com/us/app/orca-ide/id6766130217) · [TestFlight](https://testflight.apple.com/join/YjeGMQBA) · [Android APK 0.0.42](https://github.com/stablyai/orca/releases/download/mobile-android-v0.0.42/app-release.apk) · [Docs →](https://www.onorca.dev/docs/mobile)
@@ -230,7 +230,7 @@ yay -S stably-orca-bin
Pair with your desktop app to monitor and steer your agents from your phone.
- **iOS:** [Download on the App Store](https://apps.apple.com/us/app/orca-ide/id6766130217) or [join TestFlight](https://testflight.apple.com/join/YjeGMQBA)
-- **Android:** [Download APK 0.0.37](https://github.com/stablyai/orca/releases/download/mobile-android-v0.0.37/app-release.apk)
+- **Android:** [Download APK 0.0.42](https://github.com/stablyai/orca/releases/download/mobile-android-v0.0.42/app-release.apk)
---
diff --git a/config/localization-coverage-allowlist.json b/config/localization-coverage-allowlist.json
index b37d3374bf5..d3c9f3b0864 100644
--- a/config/localization-coverage-allowlist.json
+++ b/config/localization-coverage-allowlist.json
@@ -7,7 +7,7 @@
"count": 1
},
{
- "filePath": "src/renderer/src/components/sidebar/WorktreeCard.tsx",
+ "filePath": "src/renderer/src/components/sidebar/worktree-card-meta-row.tsx",
"kind": "jsx-attribute:label",
"text": "sidebar",
"dynamic": false,
diff --git a/config/max-lines-baseline.txt b/config/max-lines-baseline.txt
index 3275e33987a..8710b4e82e3 100644
--- a/config/max-lines-baseline.txt
+++ b/config/max-lines-baseline.txt
@@ -235,7 +235,6 @@ inline src/renderer/src/components/settings/Settings.tsx
inline src/renderer/src/components/settings/SettingsFormControls.tsx
inline src/renderer/src/components/sidebar/RemoteFileBrowser.tsx
inline src/renderer/src/components/sidebar/WorkspaceKanbanDrawer.tsx
-inline src/renderer/src/components/sidebar/WorktreeCard.tsx
inline src/renderer/src/components/sidebar/WorktreeContextMenu.tsx
inline src/renderer/src/components/sidebar/WorktreeList.lineage-child-card.test.ts
inline src/renderer/src/components/sidebar/WorktreeList.tsx
@@ -270,7 +269,6 @@ inline src/renderer/src/hooks/useComposerState.ts
inline src/renderer/src/hooks/useEditorExternalWatch.ts
inline src/renderer/src/hooks/useIpcEvents.test.ts
inline src/renderer/src/hooks/useIpcEvents.ts
-inline src/renderer/src/hooks/useIssueMetadata.ts
inline src/renderer/src/hooks/useSettingsNavigationMetadata.ts
inline src/renderer/src/lib/active-agent-note-send.test.ts
inline src/renderer/src/lib/file-type-icons.ts
@@ -342,7 +340,6 @@ mobile-config app/h/*/source-control/*.tsx
mobile-config app/h/*/tasks.tsx
mobile-config app/index.tsx
mobile-config app/pair-scan.tsx
-mobile-config app/terminal-settings.tsx
mobile-config app/troubleshoot.tsx
mobile-config scripts/mock-server.ts
mobile-config scripts/repro-terminal-colors.ts
diff --git a/config/reliability-gates.jsonc b/config/reliability-gates.jsonc
index 16e479b2666..f5e5a39caeb 100644
--- a/config/reliability-gates.jsonc
+++ b/config/reliability-gates.jsonc
@@ -1969,7 +1969,7 @@
"invariant": "Close permanently removes the owned provider session, agent descendants, and resume authority even when no TerminalPane is mounted; a terminating id remains reserved through natural exit, duplicate callers await the same completion, and immediate teardown upgrades any graceful request without signalling a recycled PID or a descendant tree after root ownership is lost; process-table work is locale-stable, bounded, fresh for each post-start request, same-turn coalesced, and begins within the requesting caller's deadline, including bulk worktree cleanup; detach and park preserve ownership; aliases prevent a detached agent's immutable physical pane key from being retired with its former tab.",
"oracle": "Capture the exact PTY before parking, prove it remains listed while the view is absent, close through the product state boundary, and poll the provider inventory until that exact ID disappears; an agent-marked PTY's detached-pgid child is alive before close and absent afterward; unit tests keep a naturally exited id reserved without re-killing its PID or signalling its captured tree, upgrade pending and post-snapshot graceful kills to immediate, force ps into the C locale, coalesce each bounded bulk-shutdown batch, share duplicate teardown completion, coalesce 20 same-turn process-table requests, start one shared successor without waiting for the prior scan, terminate cyclic-looking traversal, retain the source scan's timestamp, bound both read phases, avoid ambiguous SIGKILL, and assert canonical owner dedupe, exact pane tombstones, chained detach transfer, and restart alias restoration.",
"commands": [
- "pnpm dlx node@24 ./node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/agent-hooks/server-pane-authority.test.ts src/main/ipc/agent-hooks.test.ts src/main/ipc/agent-pane-authority-ownership.test.ts src/main/ipc/pty-management.test.ts src/main/persistence.test.ts src/renderer/src/store/slices/agent-pane-authority.test.ts src/renderer/src/store/slices/terminal-pane-detach-agent-identity.test.ts src/renderer/src/store/slices/terminal-tab-retirement.test.ts src/renderer/src/store/slices/terminal-tab-retirement-store.test.ts src/renderer/src/components/shared/kill-all-terminal-surfaces.test.ts",
+ "pnpm dlx node@24 ./node_modules/vitest/vitest.mjs run --config config/vitest.config.ts src/main/agent-hooks/server-pane-authority.test.ts src/main/ipc/agent-hooks.test.ts src/main/ipc/agent-pane-authority-ownership.test.ts src/main/ipc/pty-management.test.ts src/main/persistence.test.ts src/renderer/src/store/slices/agent-pane-authority.test.ts src/renderer/src/store/slices/terminal-pane-detach-agent-identity.test.ts src/renderer/src/store/slices/terminal-tab-retirement.test.ts src/renderer/src/store/slices/terminal-tab-retirement-store.test.ts tests/e2e/completed-worker-retirement-resume.unit.test.ts src/renderer/src/components/shared/kill-all-terminal-surfaces.test.ts",
"pnpm exec vitest run --config config/vitest.config.ts src/main/pty-descendant-termination.test.ts src/main/daemon/session.test.ts src/main/daemon/terminal-host.test.ts src/main/providers/local-pty-provider.test.ts src/main/runtime/worktree-teardown.test.ts",
"pnpm run test:e2e -- tests/e2e/terminal-parked-close-retirement.spec.ts --workers=1",
"pnpm run test:e2e -- tests/e2e/agent-descendant-process-kill.spec.ts --workers=1"
@@ -1989,11 +1989,18 @@
"src/renderer/src/store/slices/terminal-pane-detach-agent-identity.test.ts",
"src/renderer/src/store/slices/terminal-tab-retirement.test.ts",
"src/renderer/src/store/slices/terminal-tab-retirement-store.test.ts",
+ "tests/e2e/completed-worker-retirement-resume.unit.test.ts",
"src/renderer/src/components/shared/kill-all-terminal-surfaces.test.ts",
"tests/e2e/terminal-parked-close-retirement.spec.ts",
"tests/e2e/agent-descendant-process-kill.spec.ts"
],
"assertionRefs": [
+ {
+ "file": "tests/e2e/completed-worker-retirement-resume.unit.test.ts",
+ "assertions": [
+ "explicit close revokes the exact live recovery record both before and after PTY exit removes the tab, while PTY exit alone preserves interrupted-session recovery"
+ ]
+ },
{
"file": "tests/e2e/terminal-parked-close-retirement.spec.ts",
"assertions": [
@@ -2711,11 +2718,13 @@
"oracle": "Renderer-state tests assert provider-session ownership across preserved and queued panes. Main/provider contracts assert atomic attach-only adoption, stable host/worktree/tab/leaf identity, no fresh spawn on adoption, and safe paired-runtime cancellation. The Electron oracle creates inactive runtime-owned Codex and Setup PTYs plus an unrelated canary, seeds an exact resumable provider session, removes only the target renderer projections, and activates the workspace. It requires byte-stable handle, PTY, incarnation, tab, leaf, process PID, renderer graph, persisted binding, runtime id, graph epoch, and daemon PID across first mount and reload; PID-specific DOM keyboard I/O must remain live with one launch, zero resume argv, zero signals, zero interruption text, and no canary mutation.",
"commands": [
"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/resume-sleeping-agent-session.test.ts",
+ "pnpm exec vitest run --config config/vitest.config.ts tests/e2e/completed-worker-retirement-resume.unit.test.ts --reporter=verbose",
"pnpm exec vitest run --config config/vitest.config.ts src/renderer/src/lib/resume-sleeping-agent-session.test.ts src/main/providers/local-pty-provider.test.ts src/main/daemon/terminal-host.test.ts src/main/daemon/daemon-pty-adapter.test.ts src/main/ipc/pty.test.ts src/main/runtime/orca-runtime.test.ts src/renderer/src/lib/pane-manager/pane-fit.test.ts src/renderer/src/components/terminal-pane/pty-connection.test.ts src/renderer/src/components/terminal-pane/pty-transport.test.ts",
"pnpm exec electron-vite build --mode e2e && SKIP_BUILD=1 pnpm exec playwright test tests/e2e/live-background-terminal-mount-authority.spec.ts --config tests/playwright.config.ts --project electron-headless --workers=1"
],
"testFiles": [
"src/renderer/src/lib/resume-sleeping-agent-session.test.ts",
+ "tests/e2e/completed-worker-retirement-resume.unit.test.ts",
"src/main/providers/local-pty-provider.test.ts",
"src/main/daemon/terminal-host.test.ts",
"src/main/daemon/daemon-pty-adapter.test.ts",
@@ -2727,6 +2736,13 @@
"tests/e2e/live-background-terminal-mount-authority.spec.ts"
],
"assertionRefs": [
+ {
+ "file": "tests/e2e/completed-worker-retirement-resume.unit.test.ts",
+ "assertions": [
+ "completed and explicitly retired workers retain no provider-resume authority on first workspace activation",
+ "the original tab, leaf, PTY, provider session, release state, replacement count, queued resume argv, emitted restored banner, and unrelated canary remain exact across restart"
+ ]
+ },
{
"file": "src/renderer/src/lib/resume-sleeping-agent-session.test.ts",
"assertions": [
@@ -7867,16 +7883,24 @@
"invariant": "A settled Dispatch may close only its one coordinator-created terminal lease. Explicit reuse, real user input, retain, identity or host change, ambiguity, and another resource for the same exact host/pane/process must fence closure. Output preservation and the requested-to-releasing transition are atomic, archives remain readable without the provider file, retries resume idempotently, and orchestration reset removes archive and authority state.",
"oracle": "Record release intent for a settled owner, attempt exact reuse before close, and require worker-start to fail with terminal_release_in_progress while the terminal stays open; then release the original owner exactly once. Race retain and real user input against a controlled archive promise and require no committed archive or close. Change host or process identity and inject duplicate resource evidence to require retention. Freeze a structured transcript, delete its source file, and require archived worker-read to return the same bounded redacted messages. Restart a pending mutation, reset orchestration state, and create 50 resources while asserting replay convergence, zero orphan rows, two-query worker listing, and no unrelated close.",
"commands": [
- "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/rpc/methods/orchestration-worker-release.test.ts src/main/runtime/rpc/methods/orchestration-worker-release-recovery.test.ts src/main/runtime/rpc/orchestration-mutation-ledger.test.ts src/main/runtime/orchestration/worker-transcript-read.test.ts src/renderer/src/lib/worker-terminal-takeover-report.test.ts --reporter=dot"
+ "pnpm exec vitest run --config config/vitest.config.ts src/main/runtime/rpc/methods/orchestration-worker-release.test.ts src/main/runtime/rpc/methods/orchestration-worker-release-recovery.test.ts src/main/runtime/rpc/orchestration-mutation-ledger.test.ts src/main/runtime/orchestration/worker-transcript-read.test.ts src/renderer/src/lib/worker-terminal-takeover-report.test.ts --reporter=dot",
+ "pnpm exec vitest run --config config/vitest.config.ts tests/e2e/completed-worker-retirement-resume.unit.test.ts --reporter=verbose"
],
"testFiles": [
"src/main/runtime/rpc/methods/orchestration-worker-release.test.ts",
"src/main/runtime/rpc/methods/orchestration-worker-release-recovery.test.ts",
"src/main/runtime/rpc/orchestration-mutation-ledger.test.ts",
"src/main/runtime/orchestration/worker-transcript-read.test.ts",
- "src/renderer/src/lib/worker-terminal-takeover-report.test.ts"
+ "src/renderer/src/lib/worker-terminal-takeover-report.test.ts",
+ "tests/e2e/completed-worker-retirement-resume.unit.test.ts"
],
"assertionRefs": [
+ {
+ "file": "tests/e2e/completed-worker-retirement-resume.unit.test.ts",
+ "assertions": [
+ "an already-exited succeeded Dispatch reaches released while its late exact close revokes the missing tab's stale resume record"
+ ]
+ },
{
"file": "src/main/runtime/rpc/methods/orchestration-worker-release.test.ts",
"assertions": [
diff --git a/config/scripts/locale-ko-key-overrides.json b/config/scripts/locale-ko-key-overrides.json
index e8d13941ffa..bd0c3c15aee 100644
--- a/config/scripts/locale-ko-key-overrides.json
+++ b/config/scripts/locale-ko-key-overrides.json
@@ -1058,36 +1058,6 @@
"auto.components.feature.wall.EditorAnimatedVisual.e16479c1c5": {
"ko": "[data-slash-menu] [data-slash-row].slash-active { background: rgba(24,24,27,0.07); box-shadow: inset 0 0 0 1px rgba(24,24,27,0.06); } [data-md-active-line][data-role=\"active\"] { color: rgb(113 113 122); font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12.5px; } [data-md-active-line][data-role=\"h1\"] { color: inherit; font-family: inherit; font-size: 18px; font-weight: 700; letter-spacing: -0.01em; line-height: 1.2; margin-top: 6px; } [data-md-caret] { display: inline-block; width: 1.5px; height: 1em; background: currentColor; vertical-align: -2px; margin-left: 1px; animation: md-caret-blink 1.05s steps(1) infinite; } @keyframes md-caret-blink { 0%, 50% { opacity: 1 } 51%, 100% { opacity: 0 } } @keyframes md-block-in { from { opacity: 0; transform: translateY(-2px); } to { opacity: 1; transform: none; } } @keyframes md-cursor-ripple { 0% { transform: scale(0.4); opacity: 0.9; } 100% { transform: scale(1.4); opacity: 0; } } [data-clicking=\"1\"] [data-cursor-ripple] { animation: md-cursor-ripple 460ms ease-out forwards; }"
},
- "auto.components.feature.wall.FeatureTourPreview.04d54d50ec": {
- "ko": "orca · zsh"
- },
- "auto.components.feature.wall.FeatureTourPreview.304ad0dfc1": {
- "ko": "생각 중..."
- },
- "auto.components.feature.wall.FeatureTourPreview.3822d8d14b": {
- "ko": "fix/worktree-picker-truncates"
- },
- "auto.components.feature.wall.FeatureTourPreview.47f16ecf34": {
- "ko": "여러 작업을 동시에 진행하세요. 각 워크스페이스는 브랜치, terminal, agent 활동을 함께 보관합니다."
- },
- "auto.components.feature.wall.FeatureTourPreview.5d6ee181b6": {
- "ko": "워크스페이스를 열어 terminal로 돌아가고, 테스트와 로그, agents 용으로 패널을 나누세요."
- },
- "auto.components.feature.wall.FeatureTourPreview.6218a9014d": {
- "ko": "pnpm playwright test"
- },
- "auto.components.feature.wall.FeatureTourPreview.6ed43cb0e0": {
- "ko": "dashboard.spec.ts"
- },
- "auto.components.feature.wall.FeatureTourPreview.70aa182266": {
- "ko": "목표를 넘겨두고 자리를 비우세요. 코디네이터 agent가 작업을 나누어 병렬 PR을 만듭니다."
- },
- "auto.components.feature.wall.FeatureTourPreview.f10c14dd9d": {
- "ko": "탭을 오갈 필요 없이 GitHub 또는 Linear 백로그에서 선택하고 한 번의 클릭으로 워크스페이스를 시작하세요."
- },
- "auto.components.feature.wall.FeatureTourWorkspaceCard.9220794f6a": {
- "ko": "작업 중"
- },
"auto.components.feature.wall.FeatureWallSetupChecklist.713cc529a5": {
"ko": "마일스톤"
},
@@ -1502,12 +1472,6 @@
"auto.components.new.workspace.ProjectCombobox.search": {
"ko": "프로젝트 검색..."
},
- "auto.components.new.workspace.ProjectHostSetupCombobox.empty": {
- "ko": "이 프로젝트에 준비된 호스트가 없습니다."
- },
- "auto.components.new.workspace.ProjectHostSetupCombobox.placeholder": {
- "ko": "호스트 선택"
- },
"auto.components.notification.sound.options.0acd3d384e": {
"ko": "클랙"
},
@@ -1520,9 +1484,6 @@
"auto.components.notification.sound.options.86af8d938c": {
"ko": "봉 소리"
},
- "auto.components.onboarding.AgentFeatureSetupStep.97dcdc010f": {
- "ko": "기능 활성화"
- },
"auto.components.onboarding.AgentStep.1eee1c7bd8": {
"ko": "PATH에서 agents를 찾지 못했습니다. 나중에 설치할 항목을 선택하거나 빈 terminal로 계속 진행하세요."
},
@@ -1538,12 +1499,6 @@
"auto.components.onboarding.AgentStep.yoloPermissionsTooltip": {
"ko": "중단을 줄이기 위해 agents 권한 확인을 건너뜁니다."
},
- "auto.components.onboarding.FeatureSetupChecklist.01426f3a23": {
- "ko": "Agents가 사이트를 탐색하고, 페이지를 검사하고, 브라우저 작업을 처리할 수 있습니다."
- },
- "auto.components.onboarding.FeatureSetupChecklist.c5292c409d": {
- "ko": "Agents는 요청 시 앱 창을 검사하고 로컬 앱을 작동할 수 있습니다."
- },
"auto.components.onboarding.IntegrationsStep.0b4a7d23ab": {
"ko": "로그인 중"
},
@@ -1556,18 +1511,6 @@
"auto.components.onboarding.OnboardingFlow.windowsTerminalSubtitle": {
"ko": "새 창에서 사용할 기본 셸과 terminal에서 오른쪽 클릭이 동작하는 방식을 선택하세요."
},
- "auto.components.onboarding.RepoStep.466108ab89": {
- "ko": "선택한 호스트에 존재하는 경로를 입력하세요."
- },
- "auto.components.onboarding.RepoStep.6558d50c69": {
- "ko": "한 번에 여러 repos를 가져오고 싶으신가요? 상위 폴더를 선택하세요."
- },
- "auto.components.onboarding.RepoStep.c33b190ca3": {
- "ko": "호스트 경로만"
- },
- "auto.components.onboarding.RepoStep.cf23006ba7": {
- "ko": "선택한 호스트"
- },
"auto.components.onboarding.ThemeStep.7ee9234e54": {
"ko": "Ghostty 설정을 찾았습니다."
},
@@ -4976,9 +4919,6 @@
"auto.components.ui.color.picker.faa855a582": {
"ko": "Hex"
},
- "auto.components.ui.team.multi.combobox.18ec58881e": {
- "ko": "팀 검색..."
- },
"auto.components.workspace.cleanup.WorkspaceCleanupDialog.1b18868569": {
"ko": "리뷰 필요"
},
diff --git a/config/scripts/verify-computer-native.mjs b/config/scripts/verify-computer-native.mjs
index 91c27e03a5b..ba23a49cdd0 100644
--- a/config/scripts/verify-computer-native.mjs
+++ b/config/scripts/verify-computer-native.mjs
@@ -27,6 +27,12 @@ const checks = [
],
enabled: true
},
+ {
+ name: 'Linux snapshot renderer tests',
+ command: 'python3',
+ args: ['native/computer-use-linux/runtime_render_test.py'],
+ enabled: true
+ },
{
name: 'native provider argument guardrails',
run: verifyNativeArgumentGuardrails,
@@ -69,6 +75,19 @@ const checks = [
run: verifyWindowsProviderHandshake,
enabled: process.platform === 'win32'
},
+ {
+ name: 'Windows snapshot renderer tests',
+ command: 'powershell.exe',
+ args: [
+ '-NoLogo',
+ '-NoProfile',
+ '-ExecutionPolicy',
+ 'Bypass',
+ '-File',
+ 'native/computer-use-windows/runtime-render.test.ps1'
+ ],
+ enabled: process.platform === 'win32'
+ },
{
name: 'macOS helper app bundle and signature',
run: verifyMacOSHelperApp,
diff --git a/config/tsconfig.cli.json b/config/tsconfig.cli.json
index 14295cb475a..747780e416b 100644
--- a/config/tsconfig.cli.json
+++ b/config/tsconfig.cli.json
@@ -12,6 +12,7 @@
"../src/main/agent-hooks/local-agent-cli-presence.ts",
"../src/main/agent-hooks/managed-agent-hook-controls.ts",
"../src/main/agent-hooks/managed-agent-hook-registry.ts",
+ "../src/main/agent-hooks/managed-hook-script-refresh.ts",
"../src/main/amp/hook-service.ts",
"../src/main/antigravity/hook-service.ts",
"../src/main/claude/hook-settings.ts",
diff --git a/config/vitest.config.ts b/config/vitest.config.ts
index 735e1b2a0af..d23bee128c9 100644
--- a/config/vitest.config.ts
+++ b/config/vitest.config.ts
@@ -16,7 +16,8 @@ export default defineConfig({
test: {
environment: 'node',
// Why: Node 26's undefined Web Storage globals prevent Vitest from installing happy-dom's.
- execArgv: ['--no-experimental-webstorage'],
+ // Why --expose-gc: retention tests need a deterministic collection point to measure what a queue really holds.
+ execArgv: ['--no-experimental-webstorage', '--expose-gc'],
// Why: happy-dom drops MutationObserver callbacks on GC; keep them alive like a browser does.
setupFiles: [resolve('config/scripts/happy-dom-mutation-observer-retention.ts')],
include: [
diff --git a/docs/STYLEGUIDE.md b/docs/STYLEGUIDE.md
index d795f74039a..c21496b40bc 100644
--- a/docs/STYLEGUIDE.md
+++ b/docs/STYLEGUIDE.md
@@ -110,7 +110,7 @@ Don't add a fourth level. If something needs more emphasis than "floating," you'
Use the shadcn primitives in `src/renderer/src/components/ui/` before writing anything custom. The shadcn-style wrappers in this folder follow a consistent pattern:
-- Most carry a `data-slot=""` attribute on their root for CSS targeting — do not strip it. (The non-shadcn helpers in this folder — `sonner`, `repo-multi-combobox`, `team-multi-combobox` — don't follow this pattern and shouldn't be modeled when adding new primitives that should.)
+- Most carry a `data-slot=""` attribute on their root for CSS targeting — do not strip it. (The non-shadcn helpers in this folder — `sonner` and `repo-multi-combobox` — don't follow this pattern and shouldn't be modeled when adding new primitives that should.)
- Use `cn()` for class merging. Pass user `className` last so callers can override.
- Use `class-variance-authority` (CVA) for variants when there are multiple.
@@ -148,7 +148,7 @@ When a control has multiple plausible primitives, use this fork:
| Drawer / panel sliding in from an edge | `Sheet` | `Dialog` centered |
| Single choice from a known list | `Select` | Custom listbox |
| Single choice with search / fuzzy filtering | `Command` inside `Popover` | `Select` (no search) |
-| Multi-select with search | `repo-multi-combobox` / `team-multi-combobox` (mirror their pattern) | Roll a new one |
+| Multi-select with search | `repo-multi-combobox` (mirror its pattern) | Roll a new one |
| Transient confirmation ("Saved", "Copied") | `sonner` toast | `Dialog`, inline banner |
| Persistent inline status ("3 errors") | inline text + `Badge` | toast (toasts disappear) |
diff --git a/docs/assets/readme-downloads.svg b/docs/assets/readme-downloads.svg
index 7414e4cea7a..a3074230367 100644
--- a/docs/assets/readme-downloads.svg
+++ b/docs/assets/readme-downloads.svg
@@ -1,5 +1,5 @@
- |