chore: reformat codebase with oxfmt
Apply prettier-compatible formatting across the repo to match the incoming oxfmt configuration. Mechanical reformat only — no logic changes. Covers YAML quote normalization, package.json key sorting, Markdown/TOML formatting, and minor TS whitespace tweaks.
This commit is contained in:
@@ -3,10 +3,10 @@ name: Deploy App
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- "v*"
|
||||||
- '!v*-beta.*'
|
- "!v*-beta.*"
|
||||||
- 'app-v*'
|
- "app-v*"
|
||||||
- '!app-v*-beta.*'
|
- "!app-v*-beta.*"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -18,10 +18,10 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: "22"
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
registry-url: 'https://npm.pkg.github.com'
|
registry-url: "https://npm.pkg.github.com"
|
||||||
scope: '@boudra'
|
scope: "@boudra"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm install --workspace=@getpaseo/app --include-workspace-root
|
run: npm install --workspace=@getpaseo/app --include-workspace-root
|
||||||
|
|||||||
@@ -4,8 +4,8 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- 'packages/relay/**'
|
- "packages/relay/**"
|
||||||
- '.github/workflows/deploy-relay.yml'
|
- ".github/workflows/deploy-relay.yml"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
@@ -17,8 +17,8 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: "22"
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm install --workspace=@getpaseo/relay --include-workspace-root
|
run: npm install --workspace=@getpaseo/relay --include-workspace-root
|
||||||
@@ -30,4 +30,3 @@ jobs:
|
|||||||
run: cd packages/relay && npx wrangler deploy
|
run: cd packages/relay && npx wrangler deploy
|
||||||
env:
|
env:
|
||||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||||
|
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- 'CHANGELOG.md'
|
- "CHANGELOG.md"
|
||||||
- 'packages/website/**'
|
- "packages/website/**"
|
||||||
- 'package.json'
|
- "package.json"
|
||||||
- 'package-lock.json'
|
- "package-lock.json"
|
||||||
- 'patches/**'
|
- "patches/**"
|
||||||
- '.github/workflows/deploy-website.yml'
|
- ".github/workflows/deploy-website.yml"
|
||||||
release:
|
release:
|
||||||
types: [published]
|
types: [published]
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
@@ -24,8 +24,8 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: "22"
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
|
|
||||||
- name: Install dependencies
|
- name: Install dependencies
|
||||||
run: npm install --workspace=@getpaseo/website --include-workspace-root
|
run: npm install --workspace=@getpaseo/website --include-workspace-root
|
||||||
|
|||||||
@@ -3,21 +3,21 @@ name: Desktop Release
|
|||||||
on:
|
on:
|
||||||
push:
|
push:
|
||||||
tags:
|
tags:
|
||||||
- 'v*'
|
- "v*"
|
||||||
- 'desktop-v*'
|
- "desktop-v*"
|
||||||
- 'desktop-macos-v*'
|
- "desktop-macos-v*"
|
||||||
- 'desktop-linux-v*'
|
- "desktop-linux-v*"
|
||||||
- 'desktop-windows-v*'
|
- "desktop-windows-v*"
|
||||||
workflow_dispatch:
|
workflow_dispatch:
|
||||||
inputs:
|
inputs:
|
||||||
tag:
|
tag:
|
||||||
description: 'Existing tag to build (e.g. v0.1.0)'
|
description: "Existing tag to build (e.g. v0.1.0)"
|
||||||
required: true
|
required: true
|
||||||
type: string
|
type: string
|
||||||
platform:
|
platform:
|
||||||
description: 'Optional desktop platform to build.'
|
description: "Optional desktop platform to build."
|
||||||
required: false
|
required: false
|
||||||
default: 'all'
|
default: "all"
|
||||||
type: choice
|
type: choice
|
||||||
options:
|
options:
|
||||||
- all
|
- all
|
||||||
@@ -31,8 +31,8 @@ concurrency:
|
|||||||
|
|
||||||
env:
|
env:
|
||||||
SOURCE_TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref_name }}
|
SOURCE_TAG: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.tag || github.ref_name }}
|
||||||
DESKTOP_WORKSPACE: '@getpaseo/desktop'
|
DESKTOP_WORKSPACE: "@getpaseo/desktop"
|
||||||
DESKTOP_PACKAGE_PATH: 'packages/desktop'
|
DESKTOP_PACKAGE_PATH: "packages/desktop"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
create-release:
|
create-release:
|
||||||
@@ -100,11 +100,11 @@ jobs:
|
|||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: "22"
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
cache-dependency-path: package-lock.json
|
cache-dependency-path: package-lock.json
|
||||||
registry-url: 'https://npm.pkg.github.com'
|
registry-url: "https://npm.pkg.github.com"
|
||||||
scope: '@boudra'
|
scope: "@boudra"
|
||||||
|
|
||||||
- name: Install JS dependencies
|
- name: Install JS dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -278,11 +278,11 @@ jobs:
|
|||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: "22"
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
cache-dependency-path: package-lock.json
|
cache-dependency-path: package-lock.json
|
||||||
registry-url: 'https://npm.pkg.github.com'
|
registry-url: "https://npm.pkg.github.com"
|
||||||
scope: '@boudra'
|
scope: "@boudra"
|
||||||
|
|
||||||
- name: Install JS dependencies
|
- name: Install JS dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
@@ -346,11 +346,11 @@ jobs:
|
|||||||
- name: Setup Node
|
- name: Setup Node
|
||||||
uses: actions/setup-node@v4
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: "22"
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
cache-dependency-path: package-lock.json
|
cache-dependency-path: package-lock.json
|
||||||
registry-url: 'https://npm.pkg.github.com'
|
registry-url: "https://npm.pkg.github.com"
|
||||||
scope: '@boudra'
|
scope: "@boudra"
|
||||||
|
|
||||||
- name: Install JS dependencies
|
- name: Install JS dependencies
|
||||||
run: npm ci
|
run: npm ci
|
||||||
|
|||||||
@@ -4,12 +4,12 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- 'package.json'
|
- "package.json"
|
||||||
- 'package-lock.json'
|
- "package-lock.json"
|
||||||
pull_request:
|
pull_request:
|
||||||
paths:
|
paths:
|
||||||
- 'package.json'
|
- "package.json"
|
||||||
- 'package-lock.json'
|
- "package-lock.json"
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: write
|
contents: write
|
||||||
@@ -25,8 +25,8 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '22'
|
node-version: "22"
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
|
|
||||||
- uses: cachix/install-nix-action@v31
|
- uses: cachix/install-nix-action@v31
|
||||||
with:
|
with:
|
||||||
|
|||||||
@@ -4,27 +4,27 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- 'nix/**'
|
- "nix/**"
|
||||||
- 'flake.nix'
|
- "flake.nix"
|
||||||
- 'flake.lock'
|
- "flake.lock"
|
||||||
- 'package.json'
|
- "package.json"
|
||||||
- 'package-lock.json'
|
- "package-lock.json"
|
||||||
- 'packages/highlight/**'
|
- "packages/highlight/**"
|
||||||
- 'packages/server/**'
|
- "packages/server/**"
|
||||||
- 'packages/relay/**'
|
- "packages/relay/**"
|
||||||
- 'packages/cli/**'
|
- "packages/cli/**"
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- 'nix/**'
|
- "nix/**"
|
||||||
- 'flake.nix'
|
- "flake.nix"
|
||||||
- 'flake.lock'
|
- "flake.lock"
|
||||||
- 'package.json'
|
- "package.json"
|
||||||
- 'package-lock.json'
|
- "package-lock.json"
|
||||||
- 'packages/highlight/**'
|
- "packages/highlight/**"
|
||||||
- 'packages/server/**'
|
- "packages/server/**"
|
||||||
- 'packages/relay/**'
|
- "packages/relay/**"
|
||||||
- 'packages/cli/**'
|
- "packages/cli/**"
|
||||||
|
|
||||||
permissions:
|
permissions:
|
||||||
contents: read
|
contents: read
|
||||||
|
|||||||
@@ -4,17 +4,17 @@ on:
|
|||||||
push:
|
push:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- 'packages/server/**'
|
- "packages/server/**"
|
||||||
- 'package.json'
|
- "package.json"
|
||||||
- 'package-lock.json'
|
- "package-lock.json"
|
||||||
- '.github/workflows/server-ci.yml'
|
- ".github/workflows/server-ci.yml"
|
||||||
pull_request:
|
pull_request:
|
||||||
branches: [main]
|
branches: [main]
|
||||||
paths:
|
paths:
|
||||||
- 'packages/server/**'
|
- "packages/server/**"
|
||||||
- 'package.json'
|
- "package.json"
|
||||||
- 'package-lock.json'
|
- "package-lock.json"
|
||||||
- '.github/workflows/server-ci.yml'
|
- ".github/workflows/server-ci.yml"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
@@ -27,8 +27,8 @@ jobs:
|
|||||||
|
|
||||||
- uses: actions/setup-node@v4
|
- uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: '20'
|
node-version: "20"
|
||||||
cache: 'npm'
|
cache: "npm"
|
||||||
|
|
||||||
- name: Fetch origin/main (worktree tests)
|
- name: Fetch origin/main (worktree tests)
|
||||||
run: git fetch --no-tags origin main:refs/remotes/origin/main
|
run: git fetch --no-tags origin main:refs/remotes/origin/main
|
||||||
|
|||||||
+135
@@ -3,12 +3,15 @@
|
|||||||
## 0.1.62 - 2026-04-23
|
## 0.1.62 - 2026-04-23
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Sidebar warning when your app and daemon versions drift apart, with a shortcut to settings.
|
- Sidebar warning when your app and daemon versions drift apart, with a shortcut to settings.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Workspaces appear in the sidebar immediately on startup instead of waiting for git registration.
|
- Workspaces appear in the sidebar immediately on startup instead of waiting for git registration.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Pull request status resolves correctly for PRs opened from forks.
|
- Pull request status resolves correctly for PRs opened from forks.
|
||||||
- Installing the paseo CLI from the macOS desktop app now works in packaged builds.
|
- Installing the paseo CLI from the macOS desktop app now works in packaged builds.
|
||||||
- Agents launched from the desktop app no longer inherit Electron-only environment variables.
|
- Agents launched from the desktop app no longer inherit Electron-only environment variables.
|
||||||
@@ -16,10 +19,12 @@
|
|||||||
## 0.1.61 - 2026-04-23
|
## 0.1.61 - 2026-04-23
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- `additionalModels` option in provider config lets you add or relabel models without replacing the full list — entries merge with runtime-discovered models (ACP) or your static `models` list. See the [Providers docs](https://paseo.sh/docs/providers).
|
- `additionalModels` option in provider config lets you add or relabel models without replacing the full list — entries merge with runtime-discovered models (ACP) or your static `models` list. See the [Providers docs](https://paseo.sh/docs/providers).
|
||||||
- New [Providers docs page](https://paseo.sh/docs/providers) covering first-class providers and every custom provider config pattern in one place.
|
- New [Providers docs page](https://paseo.sh/docs/providers) covering first-class providers and every custom provider config pattern in one place.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Pi loads your installed extensions on startup so their models show up in the model picker.
|
- Pi loads your installed extensions on startup so their models show up in the model picker.
|
||||||
- Resizing the explorer sidebar no longer rerenders the rest of the workspace.
|
- Resizing the explorer sidebar no longer rerenders the rest of the workspace.
|
||||||
- Images in assistant messages (both file paths and inline data URLs) persist as local attachments and open in the file pane.
|
- Images in assistant messages (both file paths and inline data URLs) persist as local attachments and open in the file pane.
|
||||||
@@ -27,6 +32,7 @@
|
|||||||
## 0.1.60 - 2026-04-22
|
## 0.1.60 - 2026-04-22
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Scripts and services per worktree — define named commands in `paseo.json`, and long-running services get supervised with their own ports and nice proxy URLs like `http://web.my-app.localhost:6767`. See the [worktrees guide](https://paseo.sh/docs/worktrees).
|
- Scripts and services per worktree — define named commands in `paseo.json`, and long-running services get supervised with their own ports and nice proxy URLs like `http://web.my-app.localhost:6767`. See the [worktrees guide](https://paseo.sh/docs/worktrees).
|
||||||
- Launch scripts and services for a worktree directly from the workspace header.
|
- Launch scripts and services for a worktree directly from the workspace header.
|
||||||
- New Setup tab in every workspace showing setup, teardown, and script progress live.
|
- New Setup tab in every workspace showing setup, teardown, and script progress live.
|
||||||
@@ -41,6 +47,7 @@
|
|||||||
- New-workspace picker ranks branches by recency with fast search.
|
- New-workspace picker ranks branches by recency with fast search.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Workspace and tab switching are dramatically faster on desktop and mobile — you can keep many workspaces open in parallel without lag.
|
- Workspace and tab switching are dramatically faster on desktop and mobile — you can keep many workspaces open in parallel without lag.
|
||||||
- Agent streams render more smoothly during heavy tool output.
|
- Agent streams render more smoothly during heavy tool output.
|
||||||
- App startup routes through a stable connection and lands on the right screen without flicker.
|
- App startup routes through a stable connection and lands on the right screen without flicker.
|
||||||
@@ -55,6 +62,7 @@
|
|||||||
- GitHub branch and PR picker loads faster — queries are deferred until the picker opens.
|
- GitHub branch and PR picker loads faster — queries are deferred until the picker opens.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Composer textarea shrinks back down after sending on web.
|
- Composer textarea shrinks back down after sending on web.
|
||||||
- New workspace drafts clear after submit instead of sticking around.
|
- New workspace drafts clear after submit instead of sticking around.
|
||||||
- Replacing a running agent cleans up the previous one without leaving it behind.
|
- Replacing a running agent cleans up the previous one without leaving it behind.
|
||||||
@@ -76,18 +84,21 @@
|
|||||||
## 0.1.59 - 2026-04-16
|
## 0.1.59 - 2026-04-16
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Opus 4.7 in the Claude model picker, with a 1M-context variant.
|
- Opus 4.7 in the Claude model picker, with a 1M-context variant.
|
||||||
- Extra High reasoning effort for Opus 4.7, between High and Max.
|
- Extra High reasoning effort for Opus 4.7, between High and Max.
|
||||||
|
|
||||||
## 0.1.58 - 2026-04-16
|
## 0.1.58 - 2026-04-16
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Markdown files render as formatted markdown in the file pane. ([#427](https://github.com/getpaseo/paseo/pull/427) by [@aaronflorey](https://github.com/aaronflorey))
|
- Markdown files render as formatted markdown in the file pane. ([#427](https://github.com/getpaseo/paseo/pull/427) by [@aaronflorey](https://github.com/aaronflorey))
|
||||||
- Cmd+L (Ctrl+L on Windows/Linux) focuses the agent message input.
|
- Cmd+L (Ctrl+L on Windows/Linux) focuses the agent message input.
|
||||||
- Provider models refresh on a freshness TTL; Settings shows last-updated time and any fetch errors. ([#426](https://github.com/getpaseo/paseo/pull/426))
|
- Provider models refresh on a freshness TTL; Settings shows last-updated time and any fetch errors. ([#426](https://github.com/getpaseo/paseo/pull/426))
|
||||||
- `disallowedTools` option in provider config to block specific tools from an agent.
|
- `disallowedTools` option in provider config to block specific tools from an agent.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Windows: agents launch reliably from npm `.cmd` shims, paths with spaces, and JSON config args — fixes `spawn EINVAL` startup errors. ([#454](https://github.com/getpaseo/paseo/pull/454))
|
- Windows: agents launch reliably from npm `.cmd` shims, paths with spaces, and JSON config args — fixes `spawn EINVAL` startup errors. ([#454](https://github.com/getpaseo/paseo/pull/454))
|
||||||
- OpenCode permission prompts include the requesting tool's context. ([#398](https://github.com/getpaseo/paseo/pull/398) by [@aaronflorey](https://github.com/aaronflorey))
|
- OpenCode permission prompts include the requesting tool's context. ([#398](https://github.com/getpaseo/paseo/pull/398) by [@aaronflorey](https://github.com/aaronflorey))
|
||||||
- OpenCode todo and compaction events render in the timeline. ([#429](https://github.com/getpaseo/paseo/pull/429) by [@aaronflorey](https://github.com/aaronflorey))
|
- OpenCode todo and compaction events render in the timeline. ([#429](https://github.com/getpaseo/paseo/pull/429) by [@aaronflorey](https://github.com/aaronflorey))
|
||||||
@@ -97,6 +108,7 @@
|
|||||||
- Native scrollbars match the active theme across all web views. ([#399](https://github.com/getpaseo/paseo/pull/399) by [@ethersh](https://github.com/ethersh))
|
- Native scrollbars match the active theme across all web views. ([#399](https://github.com/getpaseo/paseo/pull/399) by [@ethersh](https://github.com/ethersh))
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Code file previews can be selected and copied on iOS. ([#447](https://github.com/getpaseo/paseo/pull/447) by [@muzhi1991](https://github.com/muzhi1991))
|
- Code file previews can be selected and copied on iOS. ([#447](https://github.com/getpaseo/paseo/pull/447) by [@muzhi1991](https://github.com/muzhi1991))
|
||||||
- File preview no longer shows stale content when reopening the same file. ([#411](https://github.com/getpaseo/paseo/pull/411) by [@muzhi1991](https://github.com/muzhi1991))
|
- File preview no longer shows stale content when reopening the same file. ([#411](https://github.com/getpaseo/paseo/pull/411) by [@muzhi1991](https://github.com/muzhi1991))
|
||||||
- File explorer reinitialises when the client reconnects after a page refresh. ([#442](https://github.com/getpaseo/paseo/pull/442) by [@1996fanrui](https://github.com/1996fanrui))
|
- File explorer reinitialises when the client reconnects after a page refresh. ([#442](https://github.com/getpaseo/paseo/pull/442) by [@1996fanrui](https://github.com/1996fanrui))
|
||||||
@@ -110,12 +122,14 @@
|
|||||||
## 0.1.56 - 2026-04-14
|
## 0.1.56 - 2026-04-14
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Projects with empty git repositories (no commits yet) no longer crash the app on startup.
|
- Projects with empty git repositories (no commits yet) no longer crash the app on startup.
|
||||||
- A single problematic project can no longer prevent the rest of your workspaces from loading.
|
- A single problematic project can no longer prevent the rest of your workspaces from loading.
|
||||||
|
|
||||||
## 0.1.55 - 2026-04-14
|
## 0.1.55 - 2026-04-14
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Provider profiles — define custom providers in your Paseo config that appear alongside built-ins. Override a built-in's binary, env, or models, or create entirely new providers. See the [configuration guide](https://github.com/getpaseo/paseo/blob/main/docs/CUSTOM-PROVIDERS.md).
|
- Provider profiles — define custom providers in your Paseo config that appear alongside built-ins. Override a built-in's binary, env, or models, or create entirely new providers. See the [configuration guide](https://github.com/getpaseo/paseo/blob/main/docs/CUSTOM-PROVIDERS.md).
|
||||||
- ACP agent support — add any ACP-compatible agent to Paseo with `extends: "acp"` in your provider config. No code changes needed.
|
- ACP agent support — add any ACP-compatible agent to Paseo with `extends: "acp"` in your provider config. No code changes needed.
|
||||||
- Choose provider and model when creating scheduled agents.
|
- Choose provider and model when creating scheduled agents.
|
||||||
@@ -123,12 +137,14 @@
|
|||||||
- Cmd+, (Ctrl+, on Windows/Linux) opens settings.
|
- Cmd+, (Ctrl+, on Windows/Linux) opens settings.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Git operations are dramatically faster — workspace status, PR checks, and branch data all use a shared cached snapshot service instead of shelling out to git on every request. Running 20+ workspaces simultaneously is now smooth.
|
- Git operations are dramatically faster — workspace status, PR checks, and branch data all use a shared cached snapshot service instead of shelling out to git on every request. Running 20+ workspaces simultaneously is now smooth.
|
||||||
- Windows support — the daemon and CLI run natively on Windows with proper shell quoting, executable resolution, and path handling.
|
- Windows support — the daemon and CLI run natively on Windows with proper shell quoting, executable resolution, and path handling.
|
||||||
- iPad and tablet layouts work correctly across all screen sizes.
|
- iPad and tablet layouts work correctly across all screen sizes.
|
||||||
- IME composition (Chinese, Japanese, Korean input) no longer submits prematurely when pressing Enter.
|
- IME composition (Chinese, Japanese, Korean input) no longer submits prematurely when pressing Enter.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Creating a worktree no longer briefly flashes it as a standalone project before placing it under the correct repository.
|
- Creating a worktree no longer briefly flashes it as a standalone project before placing it under the correct repository.
|
||||||
- Worktree creation spinner stays visible throughout the process instead of disappearing on mouse-out.
|
- Worktree creation spinner stays visible throughout the process instead of disappearing on mouse-out.
|
||||||
- Workspace navigation updates correctly when switching between workspaces in the same project.
|
- Workspace navigation updates correctly when switching between workspaces in the same project.
|
||||||
@@ -138,13 +154,16 @@
|
|||||||
## 0.1.54 - 2026-04-12
|
## 0.1.54 - 2026-04-12
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Inline image previews in agent messages — screenshots and images generated by agents render directly in the conversation instead of showing as raw markdown links.
|
- Inline image previews in agent messages — screenshots and images generated by agents render directly in the conversation instead of showing as raw markdown links.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Paseo tools are no longer injected into agents by default — opt in from Settings when you need agent-to-agent orchestration.
|
- Paseo tools are no longer injected into agents by default — opt in from Settings when you need agent-to-agent orchestration.
|
||||||
- Agent provider and mode are now resolved server-side, so CLI commands like `paseo run` use consistent defaults without client-side lookups.
|
- Agent provider and mode are now resolved server-side, so CLI commands like `paseo run` use consistent defaults without client-side lookups.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Shift+Enter now correctly inserts a newline in agent terminal input instead of submitting.
|
- Shift+Enter now correctly inserts a newline in agent terminal input instead of submitting.
|
||||||
- Windows: MCP configuration is no longer mangled when spawning Claude agents.
|
- Windows: MCP configuration is no longer mangled when spawning Claude agents.
|
||||||
- Branch ahead/behind count no longer errors for branches with no remote tracking branch.
|
- Branch ahead/behind count no longer errors for branches with no remote tracking branch.
|
||||||
@@ -152,6 +171,7 @@
|
|||||||
## 0.1.53 - 2026-04-12
|
## 0.1.53 - 2026-04-12
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Agents get Paseo tools automatically — every new agent gets access to terminals, schedules, worktrees, and other agents through MCP. Toggle it off in Settings under "Inject Paseo tools".
|
- Agents get Paseo tools automatically — every new agent gets access to terminals, schedules, worktrees, and other agents through MCP. Toggle it off in Settings under "Inject Paseo tools".
|
||||||
- Git pull — pull remote changes directly from the workspace header. Promoted to the primary action when your branch is behind origin.
|
- Git pull — pull remote changes directly from the workspace header. Promoted to the primary action when your branch is behind origin.
|
||||||
- Child agent notifications — parent agents are automatically notified when a child agent finishes, errors, or needs permission approval.
|
- Child agent notifications — parent agents are automatically notified when a child agent finishes, errors, or needs permission approval.
|
||||||
@@ -160,6 +180,7 @@
|
|||||||
- Keyboard shortcut to cycle themes.
|
- Keyboard shortcut to cycle themes.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Unavailable git actions now explain why in a toast instead of being silently greyed out.
|
- Unavailable git actions now explain why in a toast instead of being silently greyed out.
|
||||||
- Streaming markdown on mobile renders significantly faster.
|
- Streaming markdown on mobile renders significantly faster.
|
||||||
- Sidebar, branch switcher, and agent panel no longer re-render unnecessarily — noticeable on large workspaces.
|
- Sidebar, branch switcher, and agent panel no longer re-render unnecessarily — noticeable on large workspaces.
|
||||||
@@ -167,6 +188,7 @@
|
|||||||
- Relay and pairing URLs are stripped from daemon logs.
|
- Relay and pairing URLs are stripped from daemon logs.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Closed agent tabs no longer reappear after reconnecting.
|
- Closed agent tabs no longer reappear after reconnecting.
|
||||||
- Desktop notification badge counts match across all workspaces.
|
- Desktop notification badge counts match across all workspaces.
|
||||||
- Host switcher status syncs correctly when switching between hosts.
|
- Host switcher status syncs correctly when switching between hosts.
|
||||||
@@ -174,11 +196,13 @@
|
|||||||
## 0.1.52 - 2026-04-10
|
## 0.1.52 - 2026-04-10
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Theme selector — choose from six themes including Midnight, Claude, and Ghostty dark variants.
|
- Theme selector — choose from six themes including Midnight, Claude, and Ghostty dark variants.
|
||||||
- Branch switching — switch git branches directly from the workspace header, with automatic stash and restore for uncommitted changes.
|
- Branch switching — switch git branches directly from the workspace header, with automatic stash and restore for uncommitted changes.
|
||||||
- Auto-download updates — desktop updates download silently in the background so they're ready to install when you are.
|
- Auto-download updates — desktop updates download silently in the background so they're ready to install when you are.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Layout now responds correctly when resizing the window or rotating a tablet — previously the app could get stuck in mobile layout on a large screen.
|
- Layout now responds correctly when resizing the window or rotating a tablet — previously the app could get stuck in mobile layout on a large screen.
|
||||||
- Terminal no longer causes massive memory spikes from snapshot thrashing during heavy output.
|
- Terminal no longer causes massive memory spikes from snapshot thrashing during heavy output.
|
||||||
- Typing in the terminal works reliably — special keys, Ctrl combos, and paste are handled natively by the terminal emulator.
|
- Typing in the terminal works reliably — special keys, Ctrl combos, and paste are handled natively by the terminal emulator.
|
||||||
@@ -191,11 +215,13 @@
|
|||||||
## 0.1.51 - 2026-04-09
|
## 0.1.51 - 2026-04-09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Image attachments for OpenCode — attach screenshots and images to OpenCode agent prompts.
|
- Image attachments for OpenCode — attach screenshots and images to OpenCode agent prompts.
|
||||||
- WebStorm — added to the "Open in editor" list alongside Cursor, VS Code, and Zed.
|
- WebStorm — added to the "Open in editor" list alongside Cursor, VS Code, and Zed.
|
||||||
- Send behavior setting — choose whether pressing Enter while an agent is running interrupts immediately or queues your message.
|
- Send behavior setting — choose whether pressing Enter while an agent is running interrupts immediately or queues your message.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Model selector no longer crashes on iPad.
|
- Model selector no longer crashes on iPad.
|
||||||
- Pairing now uses the correct hostname, fixing connection failures on some network setups.
|
- Pairing now uses the correct hostname, fixing connection failures on some network setups.
|
||||||
- OpenCode agents show the correct terminal state and refresh models reliably.
|
- OpenCode agents show the correct terminal state and refresh models reliably.
|
||||||
@@ -207,6 +233,7 @@
|
|||||||
## 0.1.50 - 2026-04-07
|
## 0.1.50 - 2026-04-07
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Context window meter — see how much of the context window your agent has used, with color thresholds at 70% and 90%. Works with Claude Code, Codex, and OpenCode.
|
- Context window meter — see how much of the context window your agent has used, with color thresholds at 70% and 90%. Works with Claude Code, Codex, and OpenCode.
|
||||||
- Open in editor — jump from any workspace straight into Cursor, VS Code, Zed, or your file manager. Paseo remembers your choice.
|
- Open in editor — jump from any workspace straight into Cursor, VS Code, Zed, or your file manager. Paseo remembers your choice.
|
||||||
- Side-by-side diffs — toggle between unified and split-column diff views, with a whitespace visibility option.
|
- Side-by-side diffs — toggle between unified and split-column diff views, with a whitespace visibility option.
|
||||||
@@ -215,6 +242,7 @@
|
|||||||
- Background git fetch — ahead/behind counts in the Changes pane stay up to date automatically.
|
- Background git fetch — ahead/behind counts in the Changes pane stay up to date automatically.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Workspaces load instantly on connect instead of waiting for a full sync.
|
- Workspaces load instantly on connect instead of waiting for a full sync.
|
||||||
- File explorer and diff pane remember which folders are expanded when you switch tabs.
|
- File explorer and diff pane remember which folders are expanded when you switch tabs.
|
||||||
- Closing a workspace tab is now instant.
|
- Closing a workspace tab is now instant.
|
||||||
@@ -222,6 +250,7 @@
|
|||||||
- Reload agent moved away from the close button to prevent accidental taps.
|
- Reload agent moved away from the close button to prevent accidental taps.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Voice mode no longer drifts into false speech detection during long sessions.
|
- Voice mode no longer drifts into false speech detection during long sessions.
|
||||||
- Garbled overlapping text on plan cards.
|
- Garbled overlapping text on plan cards.
|
||||||
- Changes pane could show stale diffs when working with git worktrees.
|
- Changes pane could show stale diffs when working with git worktrees.
|
||||||
@@ -234,6 +263,7 @@
|
|||||||
## 0.1.49 - 2026-04-07
|
## 0.1.49 - 2026-04-07
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Models and providers now load reliably on first connect instead of requiring a manual refresh.
|
- Models and providers now load reliably on first connect instead of requiring a manual refresh.
|
||||||
- Model picker only shows models from the agent's own provider, not every provider on the server.
|
- Model picker only shows models from the agent's own provider, not every provider on the server.
|
||||||
- Model lists stay consistent regardless of which screen you open first.
|
- Model lists stay consistent regardless of which screen you open first.
|
||||||
@@ -241,17 +271,20 @@
|
|||||||
## 0.1.48 - 2026-04-05
|
## 0.1.48 - 2026-04-05
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Provider diagnostics — tap a provider in Settings to see binary path, version, model count, and status at a glance. Helps troubleshoot why an agent type isn't available.
|
- Provider diagnostics — tap a provider in Settings to see binary path, version, model count, and status at a glance. Helps troubleshoot why an agent type isn't available.
|
||||||
- Provider snapshot system — daemon now pushes real-time provider availability and model lists to the app, replacing the old poll-based approach. Models and modes update live as providers come online or go offline.
|
- Provider snapshot system — daemon now pushes real-time provider availability and model lists to the app, replacing the old poll-based approach. Models and modes update live as providers come online or go offline.
|
||||||
- Codex question handling — Codex agents can now ask the user questions mid-session (e.g. "which file?") and receive answers inline, matching the Claude Code question flow.
|
- Codex question handling — Codex agents can now ask the user questions mid-session (e.g. "which file?") and receive answers inline, matching the Claude Code question flow.
|
||||||
- Reload tab action — right-click a workspace tab to reload its agent list without restarting the app.
|
- Reload tab action — right-click a workspace tab to reload its agent list without restarting the app.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Model selector redesigned — grouped by provider with status badges, search, and better touch targets on mobile.
|
- Model selector redesigned — grouped by provider with status badges, search, and better touch targets on mobile.
|
||||||
- Enter key now submits question card answers and confirms dictation, matching the expected keyboard flow.
|
- Enter key now submits question card answers and confirms dictation, matching the expected keyboard flow.
|
||||||
- Removed noisy agent lifecycle toasts that fired on every state change.
|
- Removed noisy agent lifecycle toasts that fired on every state change.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Desktop app now resolves the user's full login shell environment at startup, fixing tools like `codex`, `node`, `bun`, and `direnv` not being found when Paseo is launched from Finder or Dock. Terminals spawned by Paseo now inherit the same PATH and environment variables as a normal terminal session. Approach adapted from VS Code's battle-tested shell environment resolution.
|
- Desktop app now resolves the user's full login shell environment at startup, fixing tools like `codex`, `node`, `bun`, and `direnv` not being found when Paseo is launched from Finder or Dock. Terminals spawned by Paseo now inherit the same PATH and environment variables as a normal terminal session. Approach adapted from VS Code's battle-tested shell environment resolution.
|
||||||
- Input field on running agent screens now correctly receives keyboard focus.
|
- Input field on running agent screens now correctly receives keyboard focus.
|
||||||
- Mobile model selector alignment and sizing.
|
- Mobile model selector alignment and sizing.
|
||||||
@@ -259,6 +292,7 @@
|
|||||||
## 0.1.47 - 2026-04-05
|
## 0.1.47 - 2026-04-05
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Voice TTS in Electron — sherpa now requests copied buffers and the voice MCP bridge sets `ELECTRON_RUN_AS_NODE`, preventing "external buffers not allowed" crashes.
|
- Voice TTS in Electron — sherpa now requests copied buffers and the voice MCP bridge sets `ELECTRON_RUN_AS_NODE`, preventing "external buffers not allowed" crashes.
|
||||||
- QR pairing in desktop — CLI JSON output parsing now tolerates Node deprecation warnings in stdout.
|
- QR pairing in desktop — CLI JSON output parsing now tolerates Node deprecation warnings in stdout.
|
||||||
- STT segment race condition — segment ID and audio buffer are snapshotted before the async transcription call, so rapid commits no longer interleave.
|
- STT segment race condition — segment ID and audio buffer are snapshotted before the async transcription call, so rapid commits no longer interleave.
|
||||||
@@ -267,6 +301,7 @@
|
|||||||
## 0.1.46 - 2026-04-04
|
## 0.1.46 - 2026-04-04
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Voice activation in packaged builds — Silero VAD model is now copied out of the Electron asar archive so native code can read it.
|
- Voice activation in packaged builds — Silero VAD model is now copied out of the Electron asar archive so native code can read it.
|
||||||
- App version sent in probe client hello so the daemon's version gate no longer hides Pi/Copilot from reconnected sessions.
|
- App version sent in probe client hello so the daemon's version gate no longer hides Pi/Copilot from reconnected sessions.
|
||||||
- `worktreeRoot` schema made backward-compatible for old clients and daemons that don't send the field.
|
- `worktreeRoot` schema made backward-compatible for old clients and daemons that don't send the field.
|
||||||
@@ -275,6 +310,7 @@
|
|||||||
## 0.1.45 - 2026-04-04
|
## 0.1.45 - 2026-04-04
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Pi (pi.dev) agent provider — connect Pi as a new ACP-based agent type with thinking levels and tool call support.
|
- Pi (pi.dev) agent provider — connect Pi as a new ACP-based agent type with thinking levels and tool call support.
|
||||||
- Copilot agent provider re-enabled after ACP compatibility fixes.
|
- Copilot agent provider re-enabled after ACP compatibility fixes.
|
||||||
- `paseo .` and `paseo <path>` open the desktop app with the given project, similar to `code .`.
|
- `paseo .` and `paseo <path>` open the desktop app with the given project, similar to `code .`.
|
||||||
@@ -287,11 +323,13 @@
|
|||||||
- Setup hint and paseo.sh link on the mobile welcome screen so new App Store users know what to do next.
|
- Setup hint and paseo.sh link on the mobile welcome screen so new App Store users know what to do next.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Desktop startup is faster — existing daemon connections are raced against bootstrap so the app is usable sooner.
|
- Desktop startup is faster — existing daemon connections are raced against bootstrap so the app is usable sooner.
|
||||||
- Settings sections reordered for better grouping (integrations and daemon together).
|
- Settings sections reordered for better grouping (integrations and daemon together).
|
||||||
- Sidebar projects and workspaces now persist across sessions, with a context menu to remove projects.
|
- Sidebar projects and workspaces now persist across sessions, with a context menu to remove projects.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Sidebar crash when switching iOS theme (Unistyles/Reanimated interaction).
|
- Sidebar crash when switching iOS theme (Unistyles/Reanimated interaction).
|
||||||
- Silero VAD crash caused by external buffer mode in CircularBuffer.
|
- Silero VAD crash caused by external buffer mode in CircularBuffer.
|
||||||
- Bulk close now correctly archives stored agents instead of leaving orphans.
|
- Bulk close now correctly archives stored agents instead of leaving orphans.
|
||||||
@@ -308,6 +346,7 @@
|
|||||||
## 0.1.44 - 2026-04-03
|
## 0.1.44 - 2026-04-03
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Desktop app now stops the daemon cleanly before auto-update restarts.
|
- Desktop app now stops the daemon cleanly before auto-update restarts.
|
||||||
- Disabled claude-acp and copilot providers from the agent registry.
|
- Disabled claude-acp and copilot providers from the agent registry.
|
||||||
- Keyboard focus scope resolution now checks multiple candidates for broader compatibility.
|
- Keyboard focus scope resolution now checks multiple candidates for broader compatibility.
|
||||||
@@ -317,16 +356,19 @@
|
|||||||
## 0.1.43 - 2026-04-02
|
## 0.1.43 - 2026-04-02
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Copilot agent support via ACP base provider — connect GitHub Copilot as a new agent type.
|
- Copilot agent support via ACP base provider — connect GitHub Copilot as a new agent type.
|
||||||
- Searchable model favorites — quickly find and pin preferred models.
|
- Searchable model favorites — quickly find and pin preferred models.
|
||||||
- Slash command support for OpenCode agents.
|
- Slash command support for OpenCode agents.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Refined model selector UX with better mobile sheet behavior.
|
- Refined model selector UX with better mobile sheet behavior.
|
||||||
- Workspace status now uses amber alert styling for "needs input" state.
|
- Workspace status now uses amber alert styling for "needs input" state.
|
||||||
- Themed scrollbar on message input for consistent styling.
|
- Themed scrollbar on message input for consistent styling.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Ctrl+C/V copy and paste now works correctly in the terminal on Windows and Linux.
|
- Ctrl+C/V copy and paste now works correctly in the terminal on Windows and Linux.
|
||||||
- Shell arguments with spaces are now properly quoted on Windows.
|
- Shell arguments with spaces are now properly quoted on Windows.
|
||||||
- Claude models with 1M context support are now correctly reported.
|
- Claude models with 1M context support are now correctly reported.
|
||||||
@@ -334,11 +376,13 @@
|
|||||||
## 0.1.42 - 2026-04-01
|
## 0.1.42 - 2026-04-01
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed Claude Code failing to launch on Windows when installed to a path with spaces (e.g. `C:\Program Files\...`).
|
- Fixed Claude Code failing to launch on Windows when installed to a path with spaces (e.g. `C:\Program Files\...`).
|
||||||
|
|
||||||
## 0.1.41 - 2026-04-01
|
## 0.1.41 - 2026-04-01
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed agent spawning on Windows — all providers (Claude, Codex, OpenCode) now use shell mode so npm shims and `.cmd` wrappers resolve correctly.
|
- Fixed agent spawning on Windows — all providers (Claude, Codex, OpenCode) now use shell mode so npm shims and `.cmd` wrappers resolve correctly.
|
||||||
- Fixed terminal creation on Windows defaulting to a Unix shell instead of `cmd.exe`.
|
- Fixed terminal creation on Windows defaulting to a Unix shell instead of `cmd.exe`.
|
||||||
- Fixed path handling across the app to support Windows drive-letter paths (`C:\...`) and UNC paths (`\\...`).
|
- Fixed path handling across the app to support Windows drive-letter paths (`C:\...`) and UNC paths (`\\...`).
|
||||||
@@ -350,18 +394,22 @@
|
|||||||
- Removed the 40-item cap on activity timeline output so long agent sessions display their full history.
|
- Removed the 40-item cap on activity timeline output so long agent sessions display their full history.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Improved light mode theming with dedicated workspace background, scrollbar handle colors, and lighter shadows.
|
- Improved light mode theming with dedicated workspace background, scrollbar handle colors, and lighter shadows.
|
||||||
- Window controls overlay on Windows/Linux reduced from 48px to 29px height for a more compact titlebar.
|
- Window controls overlay on Windows/Linux reduced from 48px to 29px height for a more compact titlebar.
|
||||||
|
|
||||||
## 0.1.40 - 2026-04-01
|
## 0.1.40 - 2026-04-01
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Workspace tabs can now be closed in batches.
|
- Workspace tabs can now be closed in batches.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Provider model lists are now cached per server and provider, reducing redundant model lookups in the UI.
|
- Provider model lists are now cached per server and provider, reducing redundant model lookups in the UI.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- OpenCode reasoning content no longer appears duplicated as assistant text.
|
- OpenCode reasoning content no longer appears duplicated as assistant text.
|
||||||
- Daemon no longer crashes when a Codex binary is missing or fails to spawn.
|
- Daemon no longer crashes when a Codex binary is missing or fails to spawn.
|
||||||
- Archive tab now correctly reconciles agent visibility after archiving.
|
- Archive tab now correctly reconciles agent visibility after archiving.
|
||||||
@@ -372,61 +420,73 @@
|
|||||||
## 0.1.39 - 2026-03-30
|
## 0.1.39 - 2026-03-30
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- **Terminal management from the CLI** — new `paseo terminal` command group lets you list, create, and interact with workspace terminals without leaving your terminal.
|
- **Terminal management from the CLI** — new `paseo terminal` command group lets you list, create, and interact with workspace terminals without leaving your terminal.
|
||||||
- **Material file icons in the explorer** — the file explorer tree now shows language-specific icons (TypeScript, JSON, Markdown, etc.) so you can spot files at a glance.
|
- **Material file icons in the explorer** — the file explorer tree now shows language-specific icons (TypeScript, JSON, Markdown, etc.) so you can spot files at a glance.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed iOS sidebar scroll flicker caused by redundant overflow clipping.
|
- Fixed iOS sidebar scroll flicker caused by redundant overflow clipping.
|
||||||
- Centralized window controls padding into a shared hook, eliminating layout inconsistencies across platforms.
|
- Centralized window controls padding into a shared hook, eliminating layout inconsistencies across platforms.
|
||||||
|
|
||||||
## 0.1.38 - 2026-03-30
|
## 0.1.38 - 2026-03-30
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed daemon startup race where the app could time out connecting on first launch because the PID file advertised a listen address before the server was ready.
|
- Fixed daemon startup race where the app could time out connecting on first launch because the PID file advertised a listen address before the server was ready.
|
||||||
- Fixed daemon log rotation losing startup traces — trace-level WebSocket logs no longer include full message payloads.
|
- Fixed daemon log rotation losing startup traces — trace-level WebSocket logs no longer include full message payloads.
|
||||||
|
|
||||||
## 0.1.37 - 2026-03-29
|
## 0.1.37 - 2026-03-29
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Custom window controls on Windows and Linux — the native titlebar is replaced with overlay controls that match the app's design.
|
- Custom window controls on Windows and Linux — the native titlebar is replaced with overlay controls that match the app's design.
|
||||||
- Desktop file logging with electron-log for easier debugging of daemon and app issues.
|
- Desktop file logging with electron-log for easier debugging of daemon and app issues.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed broken PATH propagation and Claude binary resolution on Windows.
|
- Fixed broken PATH propagation and Claude binary resolution on Windows.
|
||||||
- Dictation errors now show a visible toast instead of failing silently.
|
- Dictation errors now show a visible toast instead of failing silently.
|
||||||
|
|
||||||
## 0.1.36 - 2026-03-27
|
## 0.1.36 - 2026-03-27
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed Windows drive-letter path handling across the codebase.
|
- Fixed Windows drive-letter path handling across the codebase.
|
||||||
- Fixed stale Nix hash with automatic lockfile-change detection.
|
- Fixed stale Nix hash with automatic lockfile-change detection.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added metrics collection and terminal performance tests.
|
- Added metrics collection and terminal performance tests.
|
||||||
|
|
||||||
## 0.1.35 - 2026-03-26
|
## 0.1.35 - 2026-03-26
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Faster app startup by redirecting to the welcome screen immediately and showing host connection status inline.
|
- Faster app startup by redirecting to the welcome screen immediately and showing host connection status inline.
|
||||||
- Codex file deletions now display correctly as removed lines in diffs.
|
- Codex file deletions now display correctly as removed lines in diffs.
|
||||||
- OpenCode questions are now surfaced in the permission UI.
|
- OpenCode questions are now surfaced in the permission UI.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed queued prompt dispatch after idle transition.
|
- Fixed queued prompt dispatch after idle transition.
|
||||||
- Replaced bash-only `mapfile` with a portable `while-read` loop in the chat script.
|
- Replaced bash-only `mapfile` with a portable `while-read` loop in the chat script.
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added support for Nix and NixOS installation.
|
- Added support for Nix and NixOS installation.
|
||||||
|
|
||||||
## 0.1.34 - 2026-03-25
|
## 0.1.34 - 2026-03-25
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added `paseo archive` as a top-level alias for `paseo agent archive`.
|
- Added `paseo archive` as a top-level alias for `paseo agent archive`.
|
||||||
- Added the `PASEO_AGENT_ID` environment variable for Claude and Codex agents.
|
- Added the `PASEO_AGENT_ID` environment variable for Claude and Codex agents.
|
||||||
- Added a redesigned command autocomplete with a detail card and dropdown styling.
|
- Added a redesigned command autocomplete with a detail card and dropdown styling.
|
||||||
- Linked Android download surfaces to the Google Play Store.
|
- Linked Android download surfaces to the Google Play Store.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Autonomous turns now complete gracefully on interrupt instead of being canceled.
|
- Autonomous turns now complete gracefully on interrupt instead of being canceled.
|
||||||
- Thinking/model selection now always resolves to a real option instead of showing a generic Default choice.
|
- Thinking/model selection now always resolves to a real option instead of showing a generic Default choice.
|
||||||
- Restored per-provider form preferences and removed the Auto model fallback.
|
- Restored per-provider form preferences and removed the Auto model fallback.
|
||||||
@@ -435,6 +495,7 @@
|
|||||||
- Improved chat transcript readability.
|
- Improved chat transcript readability.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed `paseo send --no-wait` not taking effect.
|
- Fixed `paseo send --no-wait` not taking effect.
|
||||||
- Fixed stale abort results contaminating replacement turns after an interrupt.
|
- Fixed stale abort results contaminating replacement turns after an interrupt.
|
||||||
- Fixed Claude interrupt handling and autonomous wake reliability.
|
- Fixed Claude interrupt handling and autonomous wake reliability.
|
||||||
@@ -451,6 +512,7 @@
|
|||||||
## 0.1.33 - 2026-03-23
|
## 0.1.33 - 2026-03-23
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed the desktop app failing to reopen after closing on macOS — the daemon and agent processes were registering with Launch Services as instances of the main app, blocking subsequent launches.
|
- Fixed the desktop app failing to reopen after closing on macOS — the daemon and agent processes were registering with Launch Services as instances of the main app, blocking subsequent launches.
|
||||||
- Fixed dictation not working in the packaged desktop app — the microphone entitlement was missing from the hardened runtime configuration.
|
- Fixed dictation not working in the packaged desktop app — the microphone entitlement was missing from the hardened runtime configuration.
|
||||||
- Fixed leaked Claude Code child processes when agents were closed — the SDK query stream was not being properly shut down.
|
- Fixed leaked Claude Code child processes when agents were closed — the SDK query stream was not being properly shut down.
|
||||||
@@ -459,6 +521,7 @@
|
|||||||
## 0.1.32 - 2026-03-23
|
## 0.1.32 - 2026-03-23
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Fully rebindable keyboard shortcuts with chord support — all shortcuts are now declarative with proper Cmd (Mac) vs Ctrl (Windows/Linux) separation.
|
- Fully rebindable keyboard shortcuts with chord support — all shortcuts are now declarative with proper Cmd (Mac) vs Ctrl (Windows/Linux) separation.
|
||||||
- Migrated the desktop app from Tauri to Electron, with macOS notarization, code signing, and Linux Wayland support.
|
- Migrated the desktop app from Tauri to Electron, with macOS notarization, code signing, and Linux Wayland support.
|
||||||
- Added line numbers and word-wrap toggle to file previews.
|
- Added line numbers and word-wrap toggle to file previews.
|
||||||
@@ -468,6 +531,7 @@
|
|||||||
- Added status bar tooltips for project and agent status.
|
- Added status bar tooltips for project and agent status.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Redesigned the mobile tab switcher as a compact header row with quick access to new agents and terminals.
|
- Redesigned the mobile tab switcher as a compact header row with quick access to new agents and terminals.
|
||||||
- Streamlined workspace creation — worktrees are now created inline with a single action instead of a multi-step flow.
|
- Streamlined workspace creation — worktrees are now created inline with a single action instead of a multi-step flow.
|
||||||
- Agent history now streams from disk on reconnect, so you see past messages immediately instead of a blank screen.
|
- Agent history now streams from disk on reconnect, so you see past messages immediately instead of a blank screen.
|
||||||
@@ -482,6 +546,7 @@
|
|||||||
- Better error messages from the daemon — RPC errors now include the actual underlying details.
|
- Better error messages from the daemon — RPC errors now include the actual underlying details.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed user messages appearing as assistant output in the timeline when messages contained structured content blocks.
|
- Fixed user messages appearing as assistant output in the timeline when messages contained structured content blocks.
|
||||||
- Fixed archived workspace routing so navigating to an archived session no longer breaks the app.
|
- Fixed archived workspace routing so navigating to an archived session no longer breaks the app.
|
||||||
- Fixed Linux AppImage failing to launch on Wayland-only desktops.
|
- Fixed Linux AppImage failing to launch on Wayland-only desktops.
|
||||||
@@ -490,16 +555,19 @@
|
|||||||
## 0.1.30 - 2026-03-19
|
## 0.1.30 - 2026-03-19
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added terminal tabs, split pane controls, and drop previews for workspace layouts.
|
- Added terminal tabs, split pane controls, and drop previews for workspace layouts.
|
||||||
- Added a combined model selector and agent mode visuals across key UI surfaces.
|
- Added a combined model selector and agent mode visuals across key UI surfaces.
|
||||||
- Added Open Graph metadata improvements for richer website sharing previews.
|
- Added Open Graph metadata improvements for richer website sharing previews.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Improved workspace navigation with better active-workspace tracking and keyboard-driven pane interactions.
|
- Improved workspace navigation with better active-workspace tracking and keyboard-driven pane interactions.
|
||||||
- Improved terminal scrollbar behavior, pane focus handling, and status bar/message input spacing.
|
- Improved terminal scrollbar behavior, pane focus handling, and status bar/message input spacing.
|
||||||
- Improved project picker path display and general workspace UI polish.
|
- Improved project picker path display and general workspace UI polish.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed agent startup reliability by tightening PATH resolution and surfacing missing provider binaries in status.
|
- Fixed agent startup reliability by tightening PATH resolution and surfacing missing provider binaries in status.
|
||||||
- Fixed workspace route syncing, drag hit areas, and git diff panel header styling regressions.
|
- Fixed workspace route syncing, drag hit areas, and git diff panel header styling regressions.
|
||||||
- Fixed website mobile horizontal scrolling and ensured the workspace audio module builds during EAS installs.
|
- Fixed website mobile horizontal scrolling and ensured the workspace audio module builds during EAS installs.
|
||||||
@@ -507,30 +575,36 @@
|
|||||||
## 0.1.28 - 2026-03-15
|
## 0.1.28 - 2026-03-15
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added OpenCode build and plan modes.
|
- Added OpenCode build and plan modes.
|
||||||
- Added website landing pages for Claude Code, Codex, and OpenCode.
|
- Added website landing pages for Claude Code, Codex, and OpenCode.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Improved the git action menu for more reliable repository actions.
|
- Improved the git action menu for more reliable repository actions.
|
||||||
- Improved the mobile settings screen, workspace header actions, and welcome screen presentation.
|
- Improved the mobile settings screen, workspace header actions, and welcome screen presentation.
|
||||||
- Updated the website hero copy and added a sponsor callout section.
|
- Updated the website hero copy and added a sponsor callout section.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed assistant file links so they open the correct workspace files from chat.
|
- Fixed assistant file links so they open the correct workspace files from chat.
|
||||||
|
|
||||||
## 0.1.27 - 2026-03-13
|
## 0.1.27 - 2026-03-13
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added voice runtime with new audio engine architecture for voice interactions.
|
- Added voice runtime with new audio engine architecture for voice interactions.
|
||||||
- Added Grep tool support in Claude tool-call mapping.
|
- Added Grep tool support in Claude tool-call mapping.
|
||||||
- Added ability to open workspace files directly from agent chat messages.
|
- Added ability to open workspace files directly from agent chat messages.
|
||||||
- Added desktop notifications via a custom native bridge.
|
- Added desktop notifications via a custom native bridge.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Improved image picker, markdown rendering, and UI interactions.
|
- Improved image picker, markdown rendering, and UI interactions.
|
||||||
- Improved shell environment detection using shell-env.
|
- Improved shell environment detection using shell-env.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed platform-specific markdown link rendering.
|
- Fixed platform-specific markdown link rendering.
|
||||||
- Fixed Linux AppImage CLI resource paths.
|
- Fixed Linux AppImage CLI resource paths.
|
||||||
- Fixed Codex replacement stream being killed by stale turn notifications.
|
- Fixed Codex replacement stream being killed by stale turn notifications.
|
||||||
@@ -538,15 +612,18 @@
|
|||||||
## 0.1.26 - 2026-03-12
|
## 0.1.26 - 2026-03-12
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added single-instance desktop behavior, Android APK download access, and refreshed splash screen styling.
|
- Added single-instance desktop behavior, Android APK download access, and refreshed splash screen styling.
|
||||||
- Added bundled Codex and OpenCode binaries in the server so setup no longer depends on global installs.
|
- Added bundled Codex and OpenCode binaries in the server so setup no longer depends on global installs.
|
||||||
- Added Windows support with improved cross-platform shell execution.
|
- Added Windows support with improved cross-platform shell execution.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Improved desktop runtime behavior on Windows by suppressing console windows and defaulting app data to `~/.paseo`.
|
- Improved desktop runtime behavior on Windows by suppressing console windows and defaulting app data to `~/.paseo`.
|
||||||
- Added a Discord link to the website navigation.
|
- Added a Discord link to the website navigation.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed desktop Claude agent startup from the managed runtime and rotated logs correctly on restart.
|
- Fixed desktop Claude agent startup from the managed runtime and rotated logs correctly on restart.
|
||||||
- Fixed the home route to hide browser chrome when appropriate.
|
- Fixed the home route to hide browser chrome when appropriate.
|
||||||
- Fixed Expo Metro compatibility by updating the `exclusionList` import.
|
- Fixed Expo Metro compatibility by updating the `exclusionList` import.
|
||||||
@@ -556,61 +633,79 @@
|
|||||||
## 0.1.25 - 2026-03-11
|
## 0.1.25 - 2026-03-11
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed desktop app failing to start the built-in daemon on fresh macOS installs. The DMG was not notarized and code-signing stripped entitlements from the bundled Node runtime, causing Gatekeeper to block execution.
|
- Fixed desktop app failing to start the built-in daemon on fresh macOS installs. The DMG was not notarized and code-signing stripped entitlements from the bundled Node runtime, causing Gatekeeper to block execution.
|
||||||
- Fixed Linux AppImage build by restoring the AppImage bundle format and stripping CUDA dependencies from onnxruntime.
|
- Fixed Linux AppImage build by restoring the AppImage bundle format and stripping CUDA dependencies from onnxruntime.
|
||||||
|
|
||||||
## 0.1.24 - 2026-03-10
|
## 0.1.24 - 2026-03-10
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Improved command center keyboard navigation and new tab shortcut.
|
- Improved command center keyboard navigation and new tab shortcut.
|
||||||
- Simplified desktop release pipeline for faster and more reliable builds.
|
- Simplified desktop release pipeline for faster and more reliable builds.
|
||||||
|
|
||||||
## 0.1.21 - 2026-03-10
|
## 0.1.21 - 2026-03-10
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Improved desktop release reliability by fixing the Windows managed-runtime build path during GitHub Actions releases.
|
- Improved desktop release reliability by fixing the Windows managed-runtime build path during GitHub Actions releases.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed a desktop release CI failure caused by a Unix-only server build script on Windows runners.
|
- Fixed a desktop release CI failure caused by a Unix-only server build script on Windows runners.
|
||||||
- Fixed server CI to build the relay dependency before running tests, restoring relay E2EE test coverage on clean runners.
|
- Fixed server CI to build the relay dependency before running tests, restoring relay E2EE test coverage on clean runners.
|
||||||
- Fixed a Claude redesign test that depended on the local Claude CLI being installed.
|
- Fixed a Claude redesign test that depended on the local Claude CLI being installed.
|
||||||
|
|
||||||
## 0.1.20 - 2026-03-10
|
## 0.1.20 - 2026-03-10
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added workspace sidebar git actions with quick diff stats and archive controls.
|
- Added workspace sidebar git actions with quick diff stats and archive controls.
|
||||||
- Added refreshed website downloads and homepage presentation for desktop installs.
|
- Added refreshed website downloads and homepage presentation for desktop installs.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Desktop release packaging now rebuilds and validates the bundled managed runtime during CI, improving installer reliability for macOS users.
|
- Desktop release packaging now rebuilds and validates the bundled managed runtime during CI, improving installer reliability for macOS users.
|
||||||
- Improved desktop and web stream rendering, settings polish, and React 19.1.4 compatibility.
|
- Improved desktop and web stream rendering, settings polish, and React 19.1.4 compatibility.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed Claude interrupt/restart regressions and strengthened managed-daemon smoke coverage for desktop releases.
|
- Fixed Claude interrupt/restart regressions and strengthened managed-daemon smoke coverage for desktop releases.
|
||||||
|
|
||||||
## 0.1.19 - 2026-03-09
|
## 0.1.19 - 2026-03-09
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added a draft GitHub release flow so maintainers can upload and review desktop and Android release assets before publishing the final release.
|
- Added a draft GitHub release flow so maintainers can upload and review desktop and Android release assets before publishing the final release.
|
||||||
|
|
||||||
## 0.1.18 - 2026-03-06
|
## 0.1.18 - 2026-03-06
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added a desktop `Mod+W` shortcut to close the current tab.
|
- Added a desktop `Mod+W` shortcut to close the current tab.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- New and newly selected terminals now take focus automatically so you can type immediately.
|
- New and newly selected terminals now take focus automatically so you can type immediately.
|
||||||
- Kept newly created workspaces and projects in a more stable order in the sidebar.
|
- Kept newly created workspaces and projects in a more stable order in the sidebar.
|
||||||
- Improved project naming for GitHub remotes and expanded project icon discovery to Phoenix `priv/static` assets.
|
- Improved project naming for GitHub remotes and expanded project icon discovery to Phoenix `priv/static` assets.
|
||||||
- Updated the website desktop download link to use the universal macOS DMG.
|
- Updated the website desktop download link to use the universal macOS DMG.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Restored automatic agent metadata generation for Claude runs.
|
- Restored automatic agent metadata generation for Claude runs.
|
||||||
|
|
||||||
## 0.1.17 - 2026-03-06
|
## 0.1.17 - 2026-03-06
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- New workspace-first navigation model with workspace tabs, file tabs, and sortable tab groups.
|
- New workspace-first navigation model with workspace tabs, file tabs, and sortable tab groups.
|
||||||
- Keyboard shortcuts for workspace and tab navigation, with shortcut badges in the sidebar.
|
- Keyboard shortcuts for workspace and tab navigation, with shortcut badges in the sidebar.
|
||||||
- Workspace-level archive actions with improved worktree archiving flow and context menu support.
|
- Workspace-level archive actions with improved worktree archiving flow and context menu support.
|
||||||
- In-chat task notifications rendered as synthetic tool-call events for clearer status tracking.
|
- In-chat task notifications rendered as synthetic tool-call events for clearer status tracking.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Desktop builds now ship as a universal macOS binary (Apple Silicon + Intel).
|
- Desktop builds now ship as a universal macOS binary (Apple Silicon + Intel).
|
||||||
- More reliable workspace routing and tab identity handling across refreshes and deep links.
|
- More reliable workspace routing and tab identity handling across refreshes and deep links.
|
||||||
- Better sidebar drag-and-drop behavior with explicit drag handles and nested list interactions.
|
- Better sidebar drag-and-drop behavior with explicit drag handles and nested list interactions.
|
||||||
@@ -618,6 +713,7 @@
|
|||||||
- Stronger provider error surfacing and updated Claude model/runtime handling.
|
- Stronger provider error surfacing and updated Claude model/runtime handling.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed orphan workspace runs caused by non-canonical tab routes.
|
- Fixed orphan workspace runs caused by non-canonical tab routes.
|
||||||
- Fixed mobile terminal tab remount/routing restore issues.
|
- Fixed mobile terminal tab remount/routing restore issues.
|
||||||
- Fixed agent metadata title/branch update reliability.
|
- Fixed agent metadata title/branch update reliability.
|
||||||
@@ -625,7 +721,9 @@
|
|||||||
- Fixed reversed edge-wheel scroll behavior in chat/tool stream views.
|
- Fixed reversed edge-wheel scroll behavior in chat/tool stream views.
|
||||||
|
|
||||||
## 0.1.16 - 2026-02-22
|
## 0.1.16 - 2026-02-22
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Update the Paseo desktop app and local daemon directly from Settings.
|
- Update the Paseo desktop app and local daemon directly from Settings.
|
||||||
- Microphone and notification permission controls in Settings.
|
- Microphone and notification permission controls in Settings.
|
||||||
- Thinking/reasoning mode — agents can use extended thinking when the provider supports it.
|
- Thinking/reasoning mode — agents can use extended thinking when the provider supports it.
|
||||||
@@ -633,6 +731,7 @@
|
|||||||
- `paseo wait` now shows a snapshot of recent agent activity while you wait.
|
- `paseo wait` now shows a snapshot of recent agent activity while you wait.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Smoother streaming with less UI flicker and scroll jumping during long agent runs.
|
- Smoother streaming with less UI flicker and scroll jumping during long agent runs.
|
||||||
- Faster agent sidebar list rendering.
|
- Faster agent sidebar list rendering.
|
||||||
- Archiving an agent now stops it first instead of archiving a half-running session.
|
- Archiving an agent now stops it first instead of archiving a half-running session.
|
||||||
@@ -640,16 +739,20 @@
|
|||||||
- More reliable relay connections.
|
- More reliable relay connections.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed Claude background tasks desyncing the chat.
|
- Fixed Claude background tasks desyncing the chat.
|
||||||
- Fixed duplicate user messages appearing in the timeline.
|
- Fixed duplicate user messages appearing in the timeline.
|
||||||
- Fixed a startup crash caused by an OpenCode SDK update.
|
- Fixed a startup crash caused by an OpenCode SDK update.
|
||||||
- Fixed spurious "needs attention" notifications from background agent activity.
|
- Fixed spurious "needs attention" notifications from background agent activity.
|
||||||
|
|
||||||
## 0.1.15 - 2026-02-19
|
## 0.1.15 - 2026-02-19
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added a public changelog page on the website so users can browse release notes.
|
- Added a public changelog page on the website so users can browse release notes.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Redesigned the website get-started experience into a clearer two-step flow.
|
- Redesigned the website get-started experience into a clearer two-step flow.
|
||||||
- Simplified website GitHub navigation and changelog headings.
|
- Simplified website GitHub navigation and changelog headings.
|
||||||
- Improved app draft/new-agent UX with clearer working directory placeholder and empty-state messaging.
|
- Improved app draft/new-agent UX with clearer working directory placeholder and empty-state messaging.
|
||||||
@@ -657,11 +760,14 @@
|
|||||||
- Hid empty filter groups in the left sidebar.
|
- Hid empty filter groups in the left sidebar.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed archived-agent navigation by redirecting archived agent routes to draft.
|
- Fixed archived-agent navigation by redirecting archived agent routes to draft.
|
||||||
- Fixed duplicate `/rewind` user-message behavior.
|
- Fixed duplicate `/rewind` user-message behavior.
|
||||||
|
|
||||||
## 0.1.14 - 2026-02-19
|
## 0.1.14 - 2026-02-19
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added Claude `/rewind` command support.
|
- Added Claude `/rewind` command support.
|
||||||
- Added slash command access in the draft agent composer.
|
- Added slash command access in the draft agent composer.
|
||||||
- Added `@` workspace file autocomplete in chat prompts.
|
- Added `@` workspace file autocomplete in chat prompts.
|
||||||
@@ -670,6 +776,7 @@
|
|||||||
- Added shared desktop/web overlay scroll handles, including file preview panes.
|
- Added shared desktop/web overlay scroll handles, including file preview panes.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Improved worktree flow after shipping, including better merged PR detection.
|
- Improved worktree flow after shipping, including better merged PR detection.
|
||||||
- Improved draft workflow by enabling the explorer sidebar immediately after CWD selection.
|
- Improved draft workflow by enabling the explorer sidebar immediately after CWD selection.
|
||||||
- Improved new worktree-agent defaults by prefilling CWD to the main repository.
|
- Improved new worktree-agent defaults by prefilling CWD to the main repository.
|
||||||
@@ -679,6 +786,7 @@
|
|||||||
- Improved scrollbar visibility, drag interactions, tracking, and animation timing on web/desktop.
|
- Improved scrollbar visibility, drag interactions, tracking, and animation timing on web/desktop.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed worktree archive/setup lifecycle issues, including terminal cleanup and archive timing.
|
- Fixed worktree archive/setup lifecycle issues, including terminal cleanup and archive timing.
|
||||||
- Fixed worktree path collisions by hashing CWD for collision-safe worktree roots.
|
- Fixed worktree path collisions by hashing CWD for collision-safe worktree roots.
|
||||||
- Fixed terminal sizing when switching back to an agent session.
|
- Fixed terminal sizing when switching back to an agent session.
|
||||||
@@ -692,25 +800,32 @@
|
|||||||
- Fixed daemon version badge visibility in settings when daemon version data is unavailable.
|
- Fixed daemon version badge visibility in settings when daemon version data is unavailable.
|
||||||
|
|
||||||
## 0.1.9 - 2026-02-17
|
## 0.1.9 - 2026-02-17
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Unified structured-output generation through a single shared schema-validation and retry pipeline.
|
- Unified structured-output generation through a single shared schema-validation and retry pipeline.
|
||||||
- Reused provider availability checks for structured generation fallback selection.
|
- Reused provider availability checks for structured generation fallback selection.
|
||||||
- Added structured generation waterfall ordering for internal metadata and git text generation: Claude Haiku, then Codex, then OpenCode.
|
- Added structured generation waterfall ordering for internal metadata and git text generation: Claude Haiku, then Codex, then OpenCode.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed CLI `run --output-schema` to use the shared structured-output path instead of ad-hoc JSON parsing.
|
- Fixed CLI `run --output-schema` to use the shared structured-output path instead of ad-hoc JSON parsing.
|
||||||
- Fixed `run --output-schema` failures where providers returned empty `lastMessage` by recovering from timeline assistant output.
|
- Fixed `run --output-schema` failures where providers returned empty `lastMessage` by recovering from timeline assistant output.
|
||||||
- Fixed internal commit message, pull request text, and agent metadata generation to follow one consistent structured pipeline.
|
- Fixed internal commit message, pull request text, and agent metadata generation to follow one consistent structured pipeline.
|
||||||
|
|
||||||
## 0.1.8 - 2026-02-17
|
## 0.1.8 - 2026-02-17
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added a cross-platform confirm dialog flow for daemon restarts.
|
- Added a cross-platform confirm dialog flow for daemon restarts.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Simplified local speech bootstrap and daemon startup locking behavior.
|
- Simplified local speech bootstrap and daemon startup locking behavior.
|
||||||
- Updated website hero copy to emphasize local execution.
|
- Updated website hero copy to emphasize local execution.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed stuck "send while running" recovery across app and server session handling.
|
- Fixed stuck "send while running" recovery across app and server session handling.
|
||||||
- Fixed Claude session identity preservation when reloading existing agents.
|
- Fixed Claude session identity preservation when reloading existing agents.
|
||||||
- Fixed combobox option behavior and related interactions.
|
- Fixed combobox option behavior and related interactions.
|
||||||
@@ -718,73 +833,93 @@
|
|||||||
- Fixed web tool-detail wheel event routing at scroll edges.
|
- Fixed web tool-detail wheel event routing at scroll edges.
|
||||||
|
|
||||||
## 0.1.7 - 2026-02-16
|
## 0.1.7 - 2026-02-16
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Improved agent workspace flows with better directory suggestions.
|
- Improved agent workspace flows with better directory suggestions.
|
||||||
- Added iOS TestFlight and Android app access request forms on the website.
|
- Added iOS TestFlight and Android app access request forms on the website.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Unified daemon startup behavior between dev and CLI paths for more predictable local runs.
|
- Unified daemon startup behavior between dev and CLI paths for more predictable local runs.
|
||||||
- Improved website app download and update guidance.
|
- Improved website app download and update guidance.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Prevented an initial desktop combobox `0,0` position flash.
|
- Prevented an initial desktop combobox `0,0` position flash.
|
||||||
- Fixed CLI version output issues.
|
- Fixed CLI version output issues.
|
||||||
- Hardened server runtime loading for local speech dependencies.
|
- Hardened server runtime loading for local speech dependencies.
|
||||||
|
|
||||||
## 0.1.6 - 2026-02-16
|
## 0.1.6 - 2026-02-16
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
- No major visible product changes in this patch release.
|
- No major visible product changes in this patch release.
|
||||||
|
|
||||||
## 0.1.5 - 2026-02-16
|
## 0.1.5 - 2026-02-16
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added terminal reattach support and better worktree terminal handling.
|
- Added terminal reattach support and better worktree terminal handling.
|
||||||
- Added global keyboard shortcut help in the app.
|
- Added global keyboard shortcut help in the app.
|
||||||
- Added sidebar host filtering and improved agent workflow controls.
|
- Added sidebar host filtering and improved agent workflow controls.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Improved worktree setup visibility by streaming setup progress.
|
- Improved worktree setup visibility by streaming setup progress.
|
||||||
- Improved terminal streaming reliability and lifecycle handling.
|
- Improved terminal streaming reliability and lifecycle handling.
|
||||||
- Preserved explorer tab state so context survives navigation better.
|
- Preserved explorer tab state so context survives navigation better.
|
||||||
|
|
||||||
## 0.1.4 - 2026-02-14
|
## 0.1.4 - 2026-02-14
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added voice capability status reporting in the client.
|
- Added voice capability status reporting in the client.
|
||||||
- Added background local speech model downloads with runtime gating.
|
- Added background local speech model downloads with runtime gating.
|
||||||
- Added adaptive dictation finish timing based on server-provided budgets.
|
- Added adaptive dictation finish timing based on server-provided budgets.
|
||||||
- Added relay reconnect behavior with grace periods and branch suggestions.
|
- Added relay reconnect behavior with grace periods and branch suggestions.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Improved connection selection and agent hydration reliability.
|
- Improved connection selection and agent hydration reliability.
|
||||||
- Improved timeline loading with cursor-based fetch behavior.
|
- Improved timeline loading with cursor-based fetch behavior.
|
||||||
- Improved first-run experience by bootstrapping a default localhost connection.
|
- Improved first-run experience by bootstrapping a default localhost connection.
|
||||||
- Improved inline code rendering by auto-linkifying URLs.
|
- Improved inline code rendering by auto-linkifying URLs.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed Linux checkout diff watch behavior to avoid recursive watches.
|
- Fixed Linux checkout diff watch behavior to avoid recursive watches.
|
||||||
- Fixed stale relay client timer behavior.
|
- Fixed stale relay client timer behavior.
|
||||||
- Fixed unnecessary git diff header auto-scroll on collapse.
|
- Fixed unnecessary git diff header auto-scroll on collapse.
|
||||||
|
|
||||||
## 0.1.3 - 2026-02-12
|
## 0.1.3 - 2026-02-12
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Added CLI onboarding command.
|
- Added CLI onboarding command.
|
||||||
- Added CLI `--output-schema` support for structured agent output.
|
- Added CLI `--output-schema` support for structured agent output.
|
||||||
- Added CLI agent metadata update support for names and labels.
|
- Added CLI agent metadata update support for names and labels.
|
||||||
- Added provider availability detection with normalization of legacy default model IDs.
|
- Added provider availability detection with normalization of legacy default model IDs.
|
||||||
|
|
||||||
### Improved
|
### Improved
|
||||||
|
|
||||||
- Improved file explorer refresh feedback and unresolved checkout fallback handling.
|
- Improved file explorer refresh feedback and unresolved checkout fallback handling.
|
||||||
- Added better voice interrupt handling with a speech-start grace period.
|
- Added better voice interrupt handling with a speech-start grace period.
|
||||||
- Improved CLI defaults to list all non-archived agents by default.
|
- Improved CLI defaults to list all non-archived agents by default.
|
||||||
- Improved website UX with clearer install CTA and privacy policy access.
|
- Improved website UX with clearer install CTA and privacy policy access.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
- Fixed dev runner entry issues and sherpa TTS initialization behavior.
|
- Fixed dev runner entry issues and sherpa TTS initialization behavior.
|
||||||
|
|
||||||
## 0.1.2 - 2026-02-11
|
## 0.1.2 - 2026-02-11
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
- No major visible product changes in this patch release.
|
- No major visible product changes in this patch release.
|
||||||
|
|
||||||
## 0.1.1 - 2026-02-11
|
## 0.1.1 - 2026-02-11
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
- Initial `0.1.x` release line.
|
- Initial `0.1.x` release line.
|
||||||
|
|||||||
@@ -17,17 +17,17 @@ This is an npm workspace monorepo:
|
|||||||
|
|
||||||
## Documentation
|
## Documentation
|
||||||
|
|
||||||
| Doc | What's in it |
|
| Doc | What's in it |
|
||||||
|---|---|
|
| ---------------------------------------------------- | --------------------------------------------------------------------------------- |
|
||||||
| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | System design, package layering, WebSocket protocol, agent lifecycle, data flow |
|
| [docs/ARCHITECTURE.md](docs/ARCHITECTURE.md) | System design, package layering, WebSocket protocol, agent lifecycle, data flow |
|
||||||
| [docs/CODING_STANDARDS.md](docs/CODING_STANDARDS.md) | Type hygiene, error handling, state design, React patterns, file organization |
|
| [docs/CODING_STANDARDS.md](docs/CODING_STANDARDS.md) | Type hygiene, error handling, state design, React patterns, file organization |
|
||||||
| [docs/TESTING.md](docs/TESTING.md) | TDD workflow, determinism, real dependencies over mocks, test organization |
|
| [docs/TESTING.md](docs/TESTING.md) | TDD workflow, determinism, real dependencies over mocks, test organization |
|
||||||
| [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) | Dev server, build sync gotchas, CLI reference, agent state, Playwright MCP |
|
| [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md) | Dev server, build sync gotchas, CLI reference, agent state, Playwright MCP |
|
||||||
| [docs/RELEASE.md](docs/RELEASE.md) | Release playbook, draft releases, completion checklist |
|
| [docs/RELEASE.md](docs/RELEASE.md) | Release playbook, draft releases, completion checklist |
|
||||||
| [docs/CUSTOM-PROVIDERS.md](docs/CUSTOM-PROVIDERS.md) | Custom provider config: Z.AI, Alibaba/Qwen, ACP agents, profiles, custom binaries |
|
| [docs/CUSTOM-PROVIDERS.md](docs/CUSTOM-PROVIDERS.md) | Custom provider config: Z.AI, Alibaba/Qwen, ACP agents, profiles, custom binaries |
|
||||||
| [docs/ANDROID.md](docs/ANDROID.md) | App variants, local/cloud builds, EAS workflows |
|
| [docs/ANDROID.md](docs/ANDROID.md) | App variants, local/cloud builds, EAS workflows |
|
||||||
| [docs/DESIGN.md](docs/DESIGN.md) | How to design features before implementation |
|
| [docs/DESIGN.md](docs/DESIGN.md) | How to design features before implementation |
|
||||||
| [SECURITY.md](SECURITY.md) | Relay threat model, E2E encryption, DNS rebinding, agent auth |
|
| [SECURITY.md](SECURITY.md) | Relay threat model, E2E encryption, DNS rebinding, agent auth |
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
@@ -68,23 +68,23 @@ The app runs on iOS, Android, web (browser), and web (Electron desktop). Code is
|
|||||||
|
|
||||||
### The four gates
|
### The four gates
|
||||||
|
|
||||||
| Gate | Type | When to use |
|
| Gate | Type | When to use |
|
||||||
|---|---|---|
|
| -------------------------- | --------- | --------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `isWeb` | constant | DOM APIs — `document`, `window`, `<div>`, `addEventListener`, `ResizeObserver`. This is the **exception**, not the default. |
|
| `isWeb` | constant | DOM APIs — `document`, `window`, `<div>`, `addEventListener`, `ResizeObserver`. This is the **exception**, not the default. |
|
||||||
| `isNative` | constant | Native-only APIs — Haptics, `StatusBar.currentHeight`, push tokens, camera/scanner, `expo-av`. |
|
| `isNative` | constant | Native-only APIs — Haptics, `StatusBar.currentHeight`, push tokens, camera/scanner, `expo-av`. |
|
||||||
| `getIsElectron()` | cached fn | Desktop wrapper features — file dialogs, titlebar drag region, daemon management, app updates, dock badges. |
|
| `getIsElectron()` | cached fn | Desktop wrapper features — file dialogs, titlebar drag region, daemon management, app updates, dock badges. |
|
||||||
| `useIsCompactFormFactor()` | hook | Layout decisions — sidebar overlay vs pinned, modal vs full screen, single-panel vs split. From `@/constants/layout`. |
|
| `useIsCompactFormFactor()` | hook | Layout decisions — sidebar overlay vs pinned, modal vs full screen, single-panel vs split. From `@/constants/layout`. |
|
||||||
|
|
||||||
### Decision matrix
|
### Decision matrix
|
||||||
|
|
||||||
| I need to... | Use |
|
| I need to... | Use |
|
||||||
|---|---|
|
| -------------------------------------------------------------- | ------------------------------------------------------------------------- |
|
||||||
| Access DOM (`document`, `window`, `<div>`, `addEventListener`) | `if (isWeb)` |
|
| Access DOM (`document`, `window`, `<div>`, `addEventListener`) | `if (isWeb)` |
|
||||||
| Use a native-only API (Haptics, push tokens, camera) | `if (isNative)` |
|
| Use a native-only API (Haptics, push tokens, camera) | `if (isNative)` |
|
||||||
| Use an Electron bridge (file dialog, titlebar, updates) | `if (getIsElectron())` |
|
| Use an Electron bridge (file dialog, titlebar, updates) | `if (getIsElectron())` |
|
||||||
| Switch layout between phone and tablet/desktop | `useIsCompactFormFactor()` |
|
| Switch layout between phone and tablet/desktop | `useIsCompactFormFactor()` |
|
||||||
| Show something on hover, always-visible on native | `isHovered \|\| isNative \|\| isCompact` (hover only works on web) |
|
| Show something on hover, always-visible on native | `isHovered \|\| isNative \|\| isCompact` (hover only works on web) |
|
||||||
| Gate to iOS or Android specifically | `Platform.OS === "ios"` / `Platform.OS === "android"` (rare, keep inline) |
|
| Gate to iOS or Android specifically | `Platform.OS === "ios"` / `Platform.OS === "android"` (rare, keep inline) |
|
||||||
|
|
||||||
### Rules
|
### Rules
|
||||||
|
|
||||||
@@ -104,5 +104,4 @@ The app runs on iOS, Android, web (browser), and web (Electron desktop). Code is
|
|||||||
|
|
||||||
## Debugging
|
## Debugging
|
||||||
|
|
||||||
|
|
||||||
Find the complete daemon logs and traces in the $PASEO_HOME/daemon.log
|
Find the complete daemon logs and traces in the $PASEO_HOME/daemon.log
|
||||||
|
|||||||
@@ -69,6 +69,7 @@ paseo
|
|||||||
This shows a QR code in the terminal. Connect from any client. This path is useful for servers and remote machines.
|
This shows a QR code in the terminal. Connect from any client. This path is useful for servers and remote machines.
|
||||||
|
|
||||||
For full setup and configuration, see:
|
For full setup and configuration, see:
|
||||||
|
|
||||||
- [Docs](https://paseo.sh/docs)
|
- [Docs](https://paseo.sh/docs)
|
||||||
- [Configuration reference](https://paseo.sh/docs/configuration)
|
- [Configuration reference](https://paseo.sh/docs/configuration)
|
||||||
|
|
||||||
@@ -116,6 +117,7 @@ Then use them in any agent conversation:
|
|||||||
## Development
|
## Development
|
||||||
|
|
||||||
Quick monorepo package map:
|
Quick monorepo package map:
|
||||||
|
|
||||||
- `packages/server`: Paseo daemon (agent process orchestration, WebSocket API, MCP server)
|
- `packages/server`: Paseo daemon (agent process orchestration, WebSocket API, MCP server)
|
||||||
- `packages/app`: Expo client (iOS, Android, web)
|
- `packages/app`: Expo client (iOS, Android, web)
|
||||||
- `packages/cli`: `paseo` CLI for daemon and agent workflows
|
- `packages/cli`: `paseo` CLI for daemon and agent workflows
|
||||||
|
|||||||
@@ -58,6 +58,7 @@ await rm(staticDir, { recursive: true, force: true });
|
|||||||
```
|
```
|
||||||
|
|
||||||
Run with:
|
Run with:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npx tsx packages/server/src/server/your-script.ts
|
npx tsx packages/server/src/server/your-script.ts
|
||||||
```
|
```
|
||||||
@@ -107,6 +108,7 @@ const updated = await client.waitForAgentUpsert(agent.id, (s) => s.status === "i
|
|||||||
The daemon hides non-legacy providers (anything other than claude, codex, opencode) from clients that don't send an `appVersion >= 0.1.45`. The `DaemonClient` sends no version by default, so custom providers like ACP-based ones will be invisible in snapshot responses.
|
The daemon hides non-legacy providers (anything other than claude, codex, opencode) from clients that don't send an `appVersion >= 0.1.45`. The `DaemonClient` sends no version by default, so custom providers like ACP-based ones will be invisible in snapshot responses.
|
||||||
|
|
||||||
Always pass `appVersion`:
|
Always pass `appVersion`:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
const client = new DaemonClient({
|
const client = new DaemonClient({
|
||||||
url: `ws://127.0.0.1:${port}/ws`,
|
url: `ws://127.0.0.1:${port}/ws`,
|
||||||
|
|||||||
+3
-3
@@ -4,9 +4,9 @@
|
|||||||
|
|
||||||
Controlled by `APP_VARIANT` in `packages/app/app.config.js` (vanilla Expo, no custom Gradle plugin):
|
Controlled by `APP_VARIANT` in `packages/app/app.config.js` (vanilla Expo, no custom Gradle plugin):
|
||||||
|
|
||||||
| Variant | App name | Package ID |
|
| Variant | App name | Package ID |
|
||||||
|---|---|---|
|
| ------------- | ----------- | ---------------- |
|
||||||
| `production` | Paseo | `sh.paseo` |
|
| `production` | Paseo | `sh.paseo` |
|
||||||
| `development` | Paseo Debug | `sh.paseo.debug` |
|
| `development` | Paseo Debug | `sh.paseo.debug` |
|
||||||
|
|
||||||
EAS profiles: `development`, `production`, and `production-apk` in `packages/app/eas.json`.
|
EAS profiles: `development`, `production`, and `production-apk` in `packages/app/eas.json`.
|
||||||
|
|||||||
+18
-16
@@ -53,17 +53,17 @@ The heart of Paseo. A Node.js process that:
|
|||||||
|
|
||||||
**Key modules:**
|
**Key modules:**
|
||||||
|
|
||||||
| Module | Responsibility |
|
| Module | Responsibility |
|
||||||
|---|---|
|
| ------------------------- | ----------------------------------------------------------------------------- |
|
||||||
| `bootstrap.ts` | Daemon initialization: HTTP server, WS server, agent manager, storage, relay |
|
| `bootstrap.ts` | Daemon initialization: HTTP server, WS server, agent manager, storage, relay |
|
||||||
| `websocket-server.ts` | WebSocket connection management, hello/welcome handshake, binary multiplexing |
|
| `websocket-server.ts` | WebSocket connection management, hello/welcome handshake, binary multiplexing |
|
||||||
| `session.ts` | Per-client session state, timeline subscriptions, terminal operations |
|
| `session.ts` | Per-client session state, timeline subscriptions, terminal operations |
|
||||||
| `agent/agent-manager.ts` | Agent lifecycle state machine, timeline tracking, subscriber management |
|
| `agent/agent-manager.ts` | Agent lifecycle state machine, timeline tracking, subscriber management |
|
||||||
| `agent/agent-storage.ts` | File-backed JSON persistence at `$PASEO_HOME/agents/` |
|
| `agent/agent-storage.ts` | File-backed JSON persistence at `$PASEO_HOME/agents/` |
|
||||||
| `agent/mcp-server.ts` | MCP server for sub-agent creation, permissions, timeouts |
|
| `agent/mcp-server.ts` | MCP server for sub-agent creation, permissions, timeouts |
|
||||||
| `providers/` | Provider adapters: Claude (Agent SDK), Codex (AppServer), OpenCode |
|
| `providers/` | Provider adapters: Claude (Agent SDK), Codex (AppServer), OpenCode |
|
||||||
| `relay-transport.ts` | Outbound relay connection with E2E encryption |
|
| `relay-transport.ts` | Outbound relay connection with E2E encryption |
|
||||||
| `client/daemon-client.ts` | Client library for connecting to the daemon (used by CLI and app) |
|
| `client/daemon-client.ts` | Client library for connecting to the daemon (used by CLI and app) |
|
||||||
|
|
||||||
### `packages/app` — Mobile + web client (Expo)
|
### `packages/app` — Mobile + web client (Expo)
|
||||||
|
|
||||||
@@ -132,6 +132,7 @@ Server → Client: WSWelcomeMessage { clientId, daemonVersion, sessionId, capab
|
|||||||
**Binary multiplexing:**
|
**Binary multiplexing:**
|
||||||
|
|
||||||
Terminal I/O and agent streaming share the same connection via `BinaryMuxFrame`:
|
Terminal I/O and agent streaming share the same connection via `BinaryMuxFrame`:
|
||||||
|
|
||||||
- Channel 0: control messages
|
- Channel 0: control messages
|
||||||
- Channel 1: terminal data
|
- Channel 1: terminal data
|
||||||
- 1-byte channel ID + 1-byte flags + variable payload
|
- 1-byte channel ID + 1-byte flags + variable payload
|
||||||
@@ -153,13 +154,14 @@ initializing → idle → running → idle (or error → closed)
|
|||||||
|
|
||||||
Each provider implements a common `AgentClient` interface:
|
Each provider implements a common `AgentClient` interface:
|
||||||
|
|
||||||
| Provider | Wraps | Session format |
|
| Provider | Wraps | Session format |
|
||||||
|---|---|---|
|
| -------- | ------------------- | -------------------------------------------------- |
|
||||||
| Claude | Anthropic Agent SDK | `~/.claude/projects/{cwd}/{session-id}.jsonl` |
|
| Claude | Anthropic Agent SDK | `~/.claude/projects/{cwd}/{session-id}.jsonl` |
|
||||||
| Codex | CodexAppServer | `~/.codex/sessions/{date}/rollout-{ts}-{id}.jsonl` |
|
| Codex | CodexAppServer | `~/.codex/sessions/{date}/rollout-{ts}-{id}.jsonl` |
|
||||||
| OpenCode | OpenCode CLI | Provider-managed |
|
| OpenCode | OpenCode CLI | Provider-managed |
|
||||||
|
|
||||||
All providers:
|
All providers:
|
||||||
|
|
||||||
- Handle their own authentication (Paseo does not manage API keys)
|
- Handle their own authentication (Paseo does not manage API keys)
|
||||||
- Support session resume via persistence handles
|
- Support session resume via persistence handles
|
||||||
- Map tool calls to a normalized `ToolCallDetail` type
|
- Map tool calls to a normalized `ToolCallDetail` type
|
||||||
|
|||||||
@@ -40,7 +40,10 @@ No complex inline types in public function signatures.
|
|||||||
function enqueueJob(input: { userId: string; priority: "low" | "normal" | "high" }) {}
|
function enqueueJob(input: { userId: string; priority: "low" | "normal" | "high" }) {}
|
||||||
|
|
||||||
// Good
|
// Good
|
||||||
interface EnqueueJobInput { userId: string; priority: "low" | "normal" | "high" }
|
interface EnqueueJobInput {
|
||||||
|
userId: string;
|
||||||
|
priority: "low" | "normal" | "high";
|
||||||
|
}
|
||||||
function enqueueJob(input: EnqueueJobInput) {}
|
function enqueueJob(input: EnqueueJobInput) {}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -53,7 +56,11 @@ If a function needs more than one argument, use a single object parameter.
|
|||||||
function createToolCall(provider: string, toolName: string, payload: unknown) {}
|
function createToolCall(provider: string, toolName: string, payload: unknown) {}
|
||||||
|
|
||||||
// Good: object param
|
// Good: object param
|
||||||
interface CreateToolCallInput { provider: string; toolName: string; payload: unknown }
|
interface CreateToolCallInput {
|
||||||
|
provider: string;
|
||||||
|
toolName: string;
|
||||||
|
payload: unknown;
|
||||||
|
}
|
||||||
function createToolCall(input: CreateToolCallInput) {}
|
function createToolCall(input: CreateToolCallInput) {}
|
||||||
```
|
```
|
||||||
|
|
||||||
@@ -78,7 +85,11 @@ Use discriminated unions instead of bags of booleans and optionals.
|
|||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Bad
|
// Bad
|
||||||
interface FetchState { isLoading: boolean; error?: Error; data?: Data }
|
interface FetchState {
|
||||||
|
isLoading: boolean;
|
||||||
|
error?: Error;
|
||||||
|
data?: Data;
|
||||||
|
}
|
||||||
|
|
||||||
// Good
|
// Good
|
||||||
type FetchState =
|
type FetchState =
|
||||||
@@ -136,7 +147,10 @@ Avoid packing branching, lookup, and transformation into single dense expression
|
|||||||
// Bad: nested ternaries + inline lookups
|
// Bad: nested ternaries + inline lookups
|
||||||
const billing = shouldUseLegacy(account)
|
const billing = shouldUseLegacy(account)
|
||||||
? getLegacy(account)
|
? getLegacy(account)
|
||||||
: buildBilling(account, rates.find((r) => r.region === account.region));
|
: buildBilling(
|
||||||
|
account,
|
||||||
|
rates.find((r) => r.region === account.region),
|
||||||
|
);
|
||||||
|
|
||||||
// Good: named steps, then assemble
|
// Good: named steps, then assemble
|
||||||
const rate = rates.find((r) => r.region === account.region);
|
const rate = rates.find((r) => r.region === account.region);
|
||||||
|
|||||||
+47
-51
@@ -55,6 +55,7 @@ Use `extends` to create a new provider entry that inherits from a built-in provi
|
|||||||
```
|
```
|
||||||
|
|
||||||
Required fields for custom providers:
|
Required fields for custom providers:
|
||||||
|
|
||||||
- `extends` — which built-in provider to inherit from (or `"acp"`)
|
- `extends` — which built-in provider to inherit from (or `"acp"`)
|
||||||
- `label` — display name in the UI
|
- `label` — display name in the UI
|
||||||
|
|
||||||
@@ -95,12 +96,12 @@ Required fields for custom providers:
|
|||||||
|
|
||||||
### Available models
|
### Available models
|
||||||
|
|
||||||
| Model | Tier |
|
| Model | Tier |
|
||||||
|---|---|
|
| ------------- | ------------------- |
|
||||||
| `glm-5.1` | Advanced (flagship) |
|
| `glm-5.1` | Advanced (flagship) |
|
||||||
| `glm-5-turbo` | Advanced |
|
| `glm-5-turbo` | Advanced |
|
||||||
| `glm-4.7` | Standard |
|
| `glm-4.7` | Standard |
|
||||||
| `glm-4.5-air` | Lightweight |
|
| `glm-4.5-air` | Lightweight |
|
||||||
|
|
||||||
### Notes
|
### Notes
|
||||||
|
|
||||||
@@ -148,10 +149,10 @@ Required fields for custom providers:
|
|||||||
|
|
||||||
### API endpoints
|
### API endpoints
|
||||||
|
|
||||||
| Mode | Base URL |
|
| Mode | Base URL |
|
||||||
|---|---|
|
| ------------------------------- | ----------------------------------------------------------- |
|
||||||
| Coding plan (subscription) | `https://coding-intl.dashscope.aliyuncs.com/apps/anthropic` |
|
| Coding plan (subscription) | `https://coding-intl.dashscope.aliyuncs.com/apps/anthropic` |
|
||||||
| Pay-as-you-go (no subscription) | `https://dashscope-intl.aliyuncs.com/apps/anthropic` |
|
| Pay-as-you-go (no subscription) | `https://dashscope-intl.aliyuncs.com/apps/anthropic` |
|
||||||
|
|
||||||
For pay-as-you-go, use `ANTHROPIC_API_KEY` with a standard Model Studio key (`sk-xxxxx`) instead of `ANTHROPIC_AUTH_TOKEN`.
|
For pay-as-you-go, use `ANTHROPIC_API_KEY` with a standard Model Studio key (`sk-xxxxx`) instead of `ANTHROPIC_AUTH_TOKEN`.
|
||||||
|
|
||||||
@@ -159,13 +160,13 @@ For pay-as-you-go, use `ANTHROPIC_API_KEY` with a standard Model Studio key (`sk
|
|||||||
|
|
||||||
**Recommended for coding plan:**
|
**Recommended for coding plan:**
|
||||||
|
|
||||||
| Model | Notes |
|
| Model | Notes |
|
||||||
|---|---|
|
| ------------------ | --------------------------- |
|
||||||
| `qwen3.5-plus` | Vision capable, recommended |
|
| `qwen3.5-plus` | Vision capable, recommended |
|
||||||
| `qwen3-coder-next` | Optimized for coding |
|
| `qwen3-coder-next` | Optimized for coding |
|
||||||
| `kimi-k2.5` | Vision capable |
|
| `kimi-k2.5` | Vision capable |
|
||||||
| `glm-5` | Zhipu GLM |
|
| `glm-5` | Zhipu GLM |
|
||||||
| `MiniMax-M2.5` | MiniMax |
|
| `MiniMax-M2.5` | MiniMax |
|
||||||
|
|
||||||
**Additional models (pay-as-you-go):**
|
**Additional models (pay-as-you-go):**
|
||||||
`qwen3-max`, `qwen3.5-flash`, `qwen3-coder-plus`, `qwen3-coder-flash`, `qwen3-vl-plus`, `qwen3-vl-flash`
|
`qwen3-max`, `qwen3.5-flash`, `qwen3-coder-plus`, `qwen3-coder-flash`, `qwen3-vl-plus`, `qwen3-vl-flash`
|
||||||
@@ -221,16 +222,12 @@ You can also combine profiles with model overrides to pin specific models per pr
|
|||||||
"claude-fast": {
|
"claude-fast": {
|
||||||
"extends": "claude",
|
"extends": "claude",
|
||||||
"label": "Claude (Fast)",
|
"label": "Claude (Fast)",
|
||||||
"models": [
|
"models": [{ "id": "claude-sonnet-4-6", "label": "Sonnet 4.6", "isDefault": true }]
|
||||||
{ "id": "claude-sonnet-4-6", "label": "Sonnet 4.6", "isDefault": true }
|
|
||||||
]
|
|
||||||
},
|
},
|
||||||
"claude-smart": {
|
"claude-smart": {
|
||||||
"extends": "claude",
|
"extends": "claude",
|
||||||
"label": "Claude (Smart)",
|
"label": "Claude (Smart)",
|
||||||
"models": [
|
"models": [{ "id": "claude-opus-4-6", "label": "Opus 4.6", "isDefault": true }]
|
||||||
{ "id": "claude-opus-4-6", "label": "Opus 4.6", "isDefault": true }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -338,6 +335,7 @@ Set `extends: "acp"` and provide a `command`:
|
|||||||
```
|
```
|
||||||
|
|
||||||
Required fields for ACP providers:
|
Required fields for ACP providers:
|
||||||
|
|
||||||
- `extends: "acp"`
|
- `extends: "acp"`
|
||||||
- `label`
|
- `label`
|
||||||
- `command` — the command to spawn the agent process (must support ACP over stdio)
|
- `command` — the command to spawn the agent process (must support ACP over stdio)
|
||||||
@@ -455,9 +453,7 @@ Example: relabel a discovered model without replacing the full list:
|
|||||||
"extends": "acp",
|
"extends": "acp",
|
||||||
"label": "My Agent",
|
"label": "My Agent",
|
||||||
"command": ["my-agent", "--acp"],
|
"command": ["my-agent", "--acp"],
|
||||||
"additionalModels": [
|
"additionalModels": [{ "id": "provider/model-id", "label": "My Preferred Label" }]
|
||||||
{ "id": "provider/model-id", "label": "My Preferred Label" }
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -472,39 +468,39 @@ When an `additionalModels` entry has the same `id` as a discovered model, it upd
|
|||||||
|
|
||||||
Every entry under `agents.providers` accepts these fields:
|
Every entry under `agents.providers` accepts these fields:
|
||||||
|
|
||||||
| Field | Type | Required | Description |
|
| Field | Type | Required | Description |
|
||||||
|---|---|---|---|
|
| ------------------ | ------------------------ | ----------------- | ------------------------------------------------------------------ |
|
||||||
| `extends` | `string` | Yes (custom only) | Built-in provider ID to inherit from, or `"acp"` |
|
| `extends` | `string` | Yes (custom only) | Built-in provider ID to inherit from, or `"acp"` |
|
||||||
| `label` | `string` | Yes (custom only) | Display name in the UI |
|
| `label` | `string` | Yes (custom only) | Display name in the UI |
|
||||||
| `description` | `string` | No | Short description shown in the UI |
|
| `description` | `string` | No | Short description shown in the UI |
|
||||||
| `command` | `string[]` | Yes (ACP only) | Command to spawn the agent process |
|
| `command` | `string[]` | Yes (ACP only) | Command to spawn the agent process |
|
||||||
| `env` | `Record<string, string>` | No | Environment variables to set for the agent process |
|
| `env` | `Record<string, string>` | No | Environment variables to set for the agent process |
|
||||||
| `models` | `ProviderProfileModel[]` | No | Static model list (overrides runtime discovery) |
|
| `models` | `ProviderProfileModel[]` | No | Static model list (overrides runtime discovery) |
|
||||||
| `additionalModels` | `ProviderProfileModel[]` | No | Static model additions (merged with runtime discovery or `models`) |
|
| `additionalModels` | `ProviderProfileModel[]` | No | Static model additions (merged with runtime discovery or `models`) |
|
||||||
| `disallowedTools` | `string[]` | No | Tool names to disable for this provider (e.g. `["WebSearch"]`) |
|
| `disallowedTools` | `string[]` | No | Tool names to disable for this provider (e.g. `["WebSearch"]`) |
|
||||||
| `enabled` | `boolean` | No | Set to `false` to hide the provider (default: `true`) |
|
| `enabled` | `boolean` | No | Set to `false` to hide the provider (default: `true`) |
|
||||||
| `order` | `number` | No | Sort order in the provider list |
|
| `order` | `number` | No | Sort order in the provider list |
|
||||||
|
|
||||||
### Model definition
|
### Model definition
|
||||||
|
|
||||||
Each entry in the `models` array:
|
Each entry in the `models` array:
|
||||||
|
|
||||||
| Field | Type | Required | Description |
|
| Field | Type | Required | Description |
|
||||||
|---|---|---|---|
|
| ----------------- | ------------------ | -------- | ------------------------------------- |
|
||||||
| `id` | `string` | Yes | Model identifier sent to the provider |
|
| `id` | `string` | Yes | Model identifier sent to the provider |
|
||||||
| `label` | `string` | Yes | Display name in the UI |
|
| `label` | `string` | Yes | Display name in the UI |
|
||||||
| `description` | `string` | No | Short description |
|
| `description` | `string` | No | Short description |
|
||||||
| `isDefault` | `boolean` | No | Mark as the default model selection |
|
| `isDefault` | `boolean` | No | Mark as the default model selection |
|
||||||
| `thinkingOptions` | `ThinkingOption[]` | No | Available thinking/reasoning levels |
|
| `thinkingOptions` | `ThinkingOption[]` | No | Available thinking/reasoning levels |
|
||||||
|
|
||||||
### Thinking option
|
### Thinking option
|
||||||
|
|
||||||
| Field | Type | Required | Description |
|
| Field | Type | Required | Description |
|
||||||
|---|---|---|---|
|
| ------------- | --------- | -------- | ----------------------------------- |
|
||||||
| `id` | `string` | Yes | Thinking option identifier |
|
| `id` | `string` | Yes | Thinking option identifier |
|
||||||
| `label` | `string` | Yes | Display name |
|
| `label` | `string` | Yes | Display name |
|
||||||
| `description` | `string` | No | Short description |
|
| `description` | `string` | No | Short description |
|
||||||
| `isDefault` | `boolean` | No | Mark as the default thinking option |
|
| `isDefault` | `boolean` | No | Mark as the default thinking option |
|
||||||
|
|
||||||
### Gotcha: `extends: "claude"` with third-party endpoints
|
### Gotcha: `extends: "claude"` with third-party endpoints
|
||||||
|
|
||||||
|
|||||||
+200
-200
@@ -34,88 +34,88 @@ $PASEO_HOME/
|
|||||||
|
|
||||||
Each agent is stored as a separate JSON file, grouped by project directory.
|
Each agent is stored as a separate JSON file, grouped by project directory.
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
| -------------------- | ---------------------------------------- | ---------------------------------------------------------------------- |
|
||||||
| `id` | `string` | UUID, primary key |
|
| `id` | `string` | UUID, primary key |
|
||||||
| `provider` | `string` | Agent provider (`"claude"`, `"codex"`, `"opencode"`, etc.) |
|
| `provider` | `string` | Agent provider (`"claude"`, `"codex"`, `"opencode"`, etc.) |
|
||||||
| `cwd` | `string` | Working directory the agent operates in |
|
| `cwd` | `string` | Working directory the agent operates in |
|
||||||
| `createdAt` | `string` (ISO 8601) | Creation timestamp |
|
| `createdAt` | `string` (ISO 8601) | Creation timestamp |
|
||||||
| `updatedAt` | `string` (ISO 8601) | Last update timestamp |
|
| `updatedAt` | `string` (ISO 8601) | Last update timestamp |
|
||||||
| `lastActivityAt` | `string?` (ISO 8601) | Last activity timestamp |
|
| `lastActivityAt` | `string?` (ISO 8601) | Last activity timestamp |
|
||||||
| `lastUserMessageAt` | `string?` (ISO 8601) | Last user message timestamp |
|
| `lastUserMessageAt` | `string?` (ISO 8601) | Last user message timestamp |
|
||||||
| `title` | `string?` | User-visible title |
|
| `title` | `string?` | User-visible title |
|
||||||
| `labels` | `Record<string, string>` | Key-value labels (default `{}`) |
|
| `labels` | `Record<string, string>` | Key-value labels (default `{}`) |
|
||||||
| `lastStatus` | `AgentStatus` | One of: `"initializing"`, `"idle"`, `"running"`, `"error"`, `"closed"` |
|
| `lastStatus` | `AgentStatus` | One of: `"initializing"`, `"idle"`, `"running"`, `"error"`, `"closed"` |
|
||||||
| `lastModeId` | `string?` | Last active mode ID |
|
| `lastModeId` | `string?` | Last active mode ID |
|
||||||
| `config` | `SerializableConfig?` | Agent session configuration (see below) |
|
| `config` | `SerializableConfig?` | Agent session configuration (see below) |
|
||||||
| `runtimeInfo` | `RuntimeInfo?` | Live runtime state (see below) |
|
| `runtimeInfo` | `RuntimeInfo?` | Live runtime state (see below) |
|
||||||
| `features` | `AgentFeature[]?` | Provider-reported features (toggles/selects) |
|
| `features` | `AgentFeature[]?` | Provider-reported features (toggles/selects) |
|
||||||
| `persistence` | `PersistenceHandle?` | Handle for resuming sessions |
|
| `persistence` | `PersistenceHandle?` | Handle for resuming sessions |
|
||||||
| `requiresAttention` | `boolean?` | Whether the agent needs user attention |
|
| `requiresAttention` | `boolean?` | Whether the agent needs user attention |
|
||||||
| `attentionReason` | `"finished" \| "error" \| "permission"?` | Why attention is needed |
|
| `attentionReason` | `"finished" \| "error" \| "permission"?` | Why attention is needed |
|
||||||
| `attentionTimestamp` | `string?` (ISO 8601) | When attention was flagged |
|
| `attentionTimestamp` | `string?` (ISO 8601) | When attention was flagged |
|
||||||
| `internal` | `boolean?` | Whether this is a system-internal agent (loop workers, etc.) |
|
| `internal` | `boolean?` | Whether this is a system-internal agent (loop workers, etc.) |
|
||||||
| `archivedAt` | `string?` (ISO 8601) | Soft-delete timestamp |
|
| `archivedAt` | `string?` (ISO 8601) | Soft-delete timestamp |
|
||||||
|
|
||||||
### Nested: SerializableConfig
|
### Nested: SerializableConfig
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
| ------------------ | -------------------------- | ---------------------------- |
|
||||||
| `title` | `string?` | Configured title |
|
| `title` | `string?` | Configured title |
|
||||||
| `modeId` | `string?` | Configured mode |
|
| `modeId` | `string?` | Configured mode |
|
||||||
| `model` | `string?` | Configured model |
|
| `model` | `string?` | Configured model |
|
||||||
| `thinkingOptionId` | `string?` | Thinking/reasoning level |
|
| `thinkingOptionId` | `string?` | Thinking/reasoning level |
|
||||||
| `featureValues` | `Record<string, unknown>?` | Feature preference overrides |
|
| `featureValues` | `Record<string, unknown>?` | Feature preference overrides |
|
||||||
| `extra` | `Record<string, any>?` | Provider-specific config |
|
| `extra` | `Record<string, any>?` | Provider-specific config |
|
||||||
| `systemPrompt` | `string?` | Custom system prompt |
|
| `systemPrompt` | `string?` | Custom system prompt |
|
||||||
| `mcpServers` | `Record<string, any>?` | MCP server configurations |
|
| `mcpServers` | `Record<string, any>?` | MCP server configurations |
|
||||||
|
|
||||||
### Nested: RuntimeInfo
|
### Nested: RuntimeInfo
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
| ------------------ | -------------------------- | ------------------------------ |
|
||||||
| `provider` | `string` | Active provider |
|
| `provider` | `string` | Active provider |
|
||||||
| `sessionId` | `string?` | Active session ID |
|
| `sessionId` | `string?` | Active session ID |
|
||||||
| `model` | `string?` | Active model |
|
| `model` | `string?` | Active model |
|
||||||
| `thinkingOptionId` | `string?` | Active thinking option |
|
| `thinkingOptionId` | `string?` | Active thinking option |
|
||||||
| `modeId` | `string?` | Active mode |
|
| `modeId` | `string?` | Active mode |
|
||||||
| `extra` | `Record<string, unknown>?` | Provider-specific runtime data |
|
| `extra` | `Record<string, unknown>?` | Provider-specific runtime data |
|
||||||
|
|
||||||
### Nested: PersistenceHandle
|
### Nested: PersistenceHandle
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
| -------------- | ---------------------- | --------------------------------------------------------------------- |
|
||||||
| `provider` | `string` | Provider that owns the session |
|
| `provider` | `string` | Provider that owns the session |
|
||||||
| `sessionId` | `string` | Session ID for resumption |
|
| `sessionId` | `string` | Session ID for resumption |
|
||||||
| `nativeHandle` | `any?` | Provider-specific handle (Codex thread ID, Claude resume token, etc.) |
|
| `nativeHandle` | `any?` | Provider-specific handle (Codex thread ID, Claude resume token, etc.) |
|
||||||
| `metadata` | `Record<string, any>?` | Extra metadata |
|
| `metadata` | `Record<string, any>?` | Extra metadata |
|
||||||
|
|
||||||
### Nested: AgentFeature (discriminated union on `type`)
|
### Nested: AgentFeature (discriminated union on `type`)
|
||||||
|
|
||||||
**Toggle:**
|
**Toggle:**
|
||||||
|
|
||||||
| Field | Type |
|
| Field | Type |
|
||||||
|---|---|
|
| ------------- | ---------- |
|
||||||
| `type` | `"toggle"` |
|
| `type` | `"toggle"` |
|
||||||
| `id` | `string` |
|
| `id` | `string` |
|
||||||
| `label` | `string` |
|
| `label` | `string` |
|
||||||
| `description` | `string?` |
|
| `description` | `string?` |
|
||||||
| `tooltip` | `string?` |
|
| `tooltip` | `string?` |
|
||||||
| `icon` | `string?` |
|
| `icon` | `string?` |
|
||||||
| `value` | `boolean` |
|
| `value` | `boolean` |
|
||||||
|
|
||||||
**Select:**
|
**Select:**
|
||||||
|
|
||||||
| Field | Type |
|
| Field | Type |
|
||||||
|---|---|
|
| ------------- | --------------------- |
|
||||||
| `type` | `"select"` |
|
| `type` | `"select"` |
|
||||||
| `id` | `string` |
|
| `id` | `string` |
|
||||||
| `label` | `string` |
|
| `label` | `string` |
|
||||||
| `description` | `string?` |
|
| `description` | `string?` |
|
||||||
| `tooltip` | `string?` |
|
| `tooltip` | `string?` |
|
||||||
| `icon` | `string?` |
|
| `icon` | `string?` |
|
||||||
| `value` | `string?` |
|
| `value` | `string?` |
|
||||||
| `options` | `AgentSelectOption[]` |
|
| `options` | `AgentSelectOption[]` |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -172,22 +172,22 @@ All fields are optional with sensible defaults.
|
|||||||
|
|
||||||
One file per schedule. ID is 8 hex characters.
|
One file per schedule. ID is 8 hex characters.
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
| ----------- | ------------------------------------- | -------------------------------- |
|
||||||
| `id` | `string` | 8-char hex ID |
|
| `id` | `string` | 8-char hex ID |
|
||||||
| `name` | `string?` | Human-readable name |
|
| `name` | `string?` | Human-readable name |
|
||||||
| `prompt` | `string` | The prompt to send |
|
| `prompt` | `string` | The prompt to send |
|
||||||
| `cadence` | `ScheduleCadence` | Timing (see below) |
|
| `cadence` | `ScheduleCadence` | Timing (see below) |
|
||||||
| `target` | `ScheduleTarget` | What to run (see below) |
|
| `target` | `ScheduleTarget` | What to run (see below) |
|
||||||
| `status` | `"active" \| "paused" \| "completed"` | Current state |
|
| `status` | `"active" \| "paused" \| "completed"` | Current state |
|
||||||
| `createdAt` | `string` (ISO 8601) | |
|
| `createdAt` | `string` (ISO 8601) | |
|
||||||
| `updatedAt` | `string` (ISO 8601) | |
|
| `updatedAt` | `string` (ISO 8601) | |
|
||||||
| `nextRunAt` | `string?` (ISO 8601) | Next scheduled execution |
|
| `nextRunAt` | `string?` (ISO 8601) | Next scheduled execution |
|
||||||
| `lastRunAt` | `string?` (ISO 8601) | Last execution time |
|
| `lastRunAt` | `string?` (ISO 8601) | Last execution time |
|
||||||
| `pausedAt` | `string?` (ISO 8601) | When paused |
|
| `pausedAt` | `string?` (ISO 8601) | When paused |
|
||||||
| `expiresAt` | `string?` (ISO 8601) | Auto-expire time |
|
| `expiresAt` | `string?` (ISO 8601) | Auto-expire time |
|
||||||
| `maxRuns` | `number?` | Max executions before completing |
|
| `maxRuns` | `number?` | Max executions before completing |
|
||||||
| `runs` | `ScheduleRun[]` | Execution history |
|
| `runs` | `ScheduleRun[]` | Execution history |
|
||||||
|
|
||||||
### Nested: ScheduleCadence (discriminated union on `type`)
|
### Nested: ScheduleCadence (discriminated union on `type`)
|
||||||
|
|
||||||
@@ -201,16 +201,16 @@ One file per schedule. ID is 8 hex characters.
|
|||||||
|
|
||||||
### Nested: ScheduleRun
|
### Nested: ScheduleRun
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
| -------------- | -------------------------------------- | ----------------------- |
|
||||||
| `id` | `string` | Run ID |
|
| `id` | `string` | Run ID |
|
||||||
| `scheduledFor` | `string` (ISO 8601) | Intended execution time |
|
| `scheduledFor` | `string` (ISO 8601) | Intended execution time |
|
||||||
| `startedAt` | `string` (ISO 8601) | |
|
| `startedAt` | `string` (ISO 8601) | |
|
||||||
| `endedAt` | `string?` (ISO 8601) | |
|
| `endedAt` | `string?` (ISO 8601) | |
|
||||||
| `status` | `"running" \| "succeeded" \| "failed"` | |
|
| `status` | `"running" \| "succeeded" \| "failed"` | |
|
||||||
| `agentId` | `string?` (UUID) | Agent used for this run |
|
| `agentId` | `string?` (UUID) | Agent used for this run |
|
||||||
| `output` | `string?` | Agent output text |
|
| `output` | `string?` | Agent output text |
|
||||||
| `error` | `string?` | Error message if failed |
|
| `error` | `string?` | Error message if failed |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -229,25 +229,25 @@ Single file containing all rooms and messages.
|
|||||||
|
|
||||||
### ChatRoom
|
### ChatRoom
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
| ----------- | ------------------- | ----------------------------------- |
|
||||||
| `id` | `string` (UUID) | |
|
| `id` | `string` (UUID) | |
|
||||||
| `name` | `string` | Unique room name (case-insensitive) |
|
| `name` | `string` | Unique room name (case-insensitive) |
|
||||||
| `purpose` | `string?` | Room description |
|
| `purpose` | `string?` | Room description |
|
||||||
| `createdAt` | `string` (ISO 8601) | |
|
| `createdAt` | `string` (ISO 8601) | |
|
||||||
| `updatedAt` | `string` (ISO 8601) | Updated on each new message |
|
| `updatedAt` | `string` (ISO 8601) | Updated on each new message |
|
||||||
|
|
||||||
### ChatMessage
|
### ChatMessage
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
| ------------------ | ------------------- | ----------------------------------- |
|
||||||
| `id` | `string` (UUID) | |
|
| `id` | `string` (UUID) | |
|
||||||
| `roomId` | `string` | FK to ChatRoom.id |
|
| `roomId` | `string` | FK to ChatRoom.id |
|
||||||
| `authorAgentId` | `string` | Agent ID of the author |
|
| `authorAgentId` | `string` | Agent ID of the author |
|
||||||
| `body` | `string` | Message text (supports `@mentions`) |
|
| `body` | `string` | Message text (supports `@mentions`) |
|
||||||
| `replyToMessageId` | `string?` | FK to another ChatMessage.id |
|
| `replyToMessageId` | `string?` | FK to another ChatMessage.id |
|
||||||
| `mentionAgentIds` | `string[]` | Extracted `@mention` agent IDs |
|
| `mentionAgentIds` | `string[]` | Extracted `@mention` agent IDs |
|
||||||
| `createdAt` | `string` (ISO 8601) | |
|
| `createdAt` | `string` (ISO 8601) | |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -257,84 +257,84 @@ Single file containing all rooms and messages.
|
|||||||
|
|
||||||
Single file containing an array of all loop records.
|
Single file containing an array of all loop records.
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
| ----------------------- | --------------------------------------------------- | ------------------------------------------ |
|
||||||
| `id` | `string` | 8-char UUID prefix |
|
| `id` | `string` | 8-char UUID prefix |
|
||||||
| `name` | `string?` | Human-readable name |
|
| `name` | `string?` | Human-readable name |
|
||||||
| `prompt` | `string` | Worker prompt |
|
| `prompt` | `string` | Worker prompt |
|
||||||
| `cwd` | `string` | Working directory |
|
| `cwd` | `string` | Working directory |
|
||||||
| `provider` | `string` | Default provider |
|
| `provider` | `string` | Default provider |
|
||||||
| `model` | `string?` | Default model |
|
| `model` | `string?` | Default model |
|
||||||
| `workerProvider` | `string?` | Override provider for workers |
|
| `workerProvider` | `string?` | Override provider for workers |
|
||||||
| `workerModel` | `string?` | Override model for workers |
|
| `workerModel` | `string?` | Override model for workers |
|
||||||
| `verifierProvider` | `string?` | Override provider for verifiers |
|
| `verifierProvider` | `string?` | Override provider for verifiers |
|
||||||
| `verifierModel` | `string?` | Override model for verifiers |
|
| `verifierModel` | `string?` | Override model for verifiers |
|
||||||
| `verifyPrompt` | `string?` | LLM verification prompt |
|
| `verifyPrompt` | `string?` | LLM verification prompt |
|
||||||
| `verifyChecks` | `string[]` | Shell commands to run as checks |
|
| `verifyChecks` | `string[]` | Shell commands to run as checks |
|
||||||
| `archive` | `boolean` | Whether to archive worker agents after use |
|
| `archive` | `boolean` | Whether to archive worker agents after use |
|
||||||
| `sleepMs` | `number` | Delay between iterations (ms) |
|
| `sleepMs` | `number` | Delay between iterations (ms) |
|
||||||
| `maxIterations` | `number?` | Cap on iterations |
|
| `maxIterations` | `number?` | Cap on iterations |
|
||||||
| `maxTimeMs` | `number?` | Total time budget (ms) |
|
| `maxTimeMs` | `number?` | Total time budget (ms) |
|
||||||
| `status` | `"running" \| "succeeded" \| "failed" \| "stopped"` | |
|
| `status` | `"running" \| "succeeded" \| "failed" \| "stopped"` | |
|
||||||
| `createdAt` | `string` (ISO 8601) | |
|
| `createdAt` | `string` (ISO 8601) | |
|
||||||
| `updatedAt` | `string` (ISO 8601) | |
|
| `updatedAt` | `string` (ISO 8601) | |
|
||||||
| `startedAt` | `string` (ISO 8601) | |
|
| `startedAt` | `string` (ISO 8601) | |
|
||||||
| `completedAt` | `string?` (ISO 8601) | |
|
| `completedAt` | `string?` (ISO 8601) | |
|
||||||
| `stopRequestedAt` | `string?` (ISO 8601) | |
|
| `stopRequestedAt` | `string?` (ISO 8601) | |
|
||||||
| `iterations` | `LoopIteration[]` | |
|
| `iterations` | `LoopIteration[]` | |
|
||||||
| `logs` | `LoopLogEntry[]` | |
|
| `logs` | `LoopLogEntry[]` | |
|
||||||
| `nextLogSeq` | `number` | Monotonic log sequence counter |
|
| `nextLogSeq` | `number` | Monotonic log sequence counter |
|
||||||
| `activeIteration` | `number?` | Currently executing iteration index |
|
| `activeIteration` | `number?` | Currently executing iteration index |
|
||||||
| `activeWorkerAgentId` | `string?` | Currently running worker agent |
|
| `activeWorkerAgentId` | `string?` | Currently running worker agent |
|
||||||
| `activeVerifierAgentId` | `string?` | Currently running verifier agent |
|
| `activeVerifierAgentId` | `string?` | Currently running verifier agent |
|
||||||
|
|
||||||
### Nested: LoopIteration
|
### Nested: LoopIteration
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
| ------------------- | --------------------------------------------------- | ------------------------ |
|
||||||
| `index` | `number` | 1-based iteration index |
|
| `index` | `number` | 1-based iteration index |
|
||||||
| `workerAgentId` | `string?` | Agent ID of the worker |
|
| `workerAgentId` | `string?` | Agent ID of the worker |
|
||||||
| `workerStartedAt` | `string` (ISO 8601) | |
|
| `workerStartedAt` | `string` (ISO 8601) | |
|
||||||
| `workerCompletedAt` | `string?` (ISO 8601) | |
|
| `workerCompletedAt` | `string?` (ISO 8601) | |
|
||||||
| `verifierAgentId` | `string?` | Agent ID of the verifier |
|
| `verifierAgentId` | `string?` | Agent ID of the verifier |
|
||||||
| `status` | `"running" \| "succeeded" \| "failed" \| "stopped"` | |
|
| `status` | `"running" \| "succeeded" \| "failed" \| "stopped"` | |
|
||||||
| `workerOutcome` | `"completed" \| "failed" \| "canceled"?` | |
|
| `workerOutcome` | `"completed" \| "failed" \| "canceled"?` | |
|
||||||
| `failureReason` | `string?` | |
|
| `failureReason` | `string?` | |
|
||||||
| `verifyChecks` | `LoopVerifyCheckResult[]` | Shell check results |
|
| `verifyChecks` | `LoopVerifyCheckResult[]` | Shell check results |
|
||||||
| `verifyPrompt` | `LoopVerifyPromptResult?` | LLM verification result |
|
| `verifyPrompt` | `LoopVerifyPromptResult?` | LLM verification result |
|
||||||
|
|
||||||
### Nested: LoopLogEntry
|
### Nested: LoopLogEntry
|
||||||
|
|
||||||
| Field | Type |
|
| Field | Type |
|
||||||
|---|---|
|
| ----------- | ---------------------------------------------------- |
|
||||||
| `seq` | `number` (monotonic) |
|
| `seq` | `number` (monotonic) |
|
||||||
| `timestamp` | `string` (ISO 8601) |
|
| `timestamp` | `string` (ISO 8601) |
|
||||||
| `iteration` | `number?` |
|
| `iteration` | `number?` |
|
||||||
| `source` | `"loop" \| "worker" \| "verifier" \| "verify-check"` |
|
| `source` | `"loop" \| "worker" \| "verifier" \| "verify-check"` |
|
||||||
| `level` | `"info" \| "error"` |
|
| `level` | `"info" \| "error"` |
|
||||||
| `text` | `string` |
|
| `text` | `string` |
|
||||||
|
|
||||||
### Nested: LoopVerifyCheckResult
|
### Nested: LoopVerifyCheckResult
|
||||||
|
|
||||||
| Field | Type |
|
| Field | Type |
|
||||||
|---|---|
|
| ------------- | ------------------- |
|
||||||
| `command` | `string` |
|
| `command` | `string` |
|
||||||
| `exitCode` | `number` |
|
| `exitCode` | `number` |
|
||||||
| `passed` | `boolean` |
|
| `passed` | `boolean` |
|
||||||
| `stdout` | `string` |
|
| `stdout` | `string` |
|
||||||
| `stderr` | `string` |
|
| `stderr` | `string` |
|
||||||
| `startedAt` | `string` (ISO 8601) |
|
| `startedAt` | `string` (ISO 8601) |
|
||||||
| `completedAt` | `string` (ISO 8601) |
|
| `completedAt` | `string` (ISO 8601) |
|
||||||
|
|
||||||
### Nested: LoopVerifyPromptResult
|
### Nested: LoopVerifyPromptResult
|
||||||
|
|
||||||
| Field | Type |
|
| Field | Type |
|
||||||
|---|---|
|
| ----------------- | ------------------- |
|
||||||
| `passed` | `boolean` |
|
| `passed` | `boolean` |
|
||||||
| `reason` | `string` |
|
| `reason` | `string` |
|
||||||
| `verifierAgentId` | `string?` |
|
| `verifierAgentId` | `string?` |
|
||||||
| `startedAt` | `string` (ISO 8601) |
|
| `startedAt` | `string` (ISO 8601) |
|
||||||
| `completedAt` | `string` (ISO 8601) |
|
| `completedAt` | `string` (ISO 8601) |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -344,15 +344,15 @@ Single file containing an array of all loop records.
|
|||||||
|
|
||||||
Array of project records.
|
Array of project records.
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
| ------------- | -------------------- | ------------------------------ |
|
||||||
| `projectId` | `string` | Primary key |
|
| `projectId` | `string` | Primary key |
|
||||||
| `rootPath` | `string` | Filesystem root of the project |
|
| `rootPath` | `string` | Filesystem root of the project |
|
||||||
| `kind` | `"git" \| "non_git"` | |
|
| `kind` | `"git" \| "non_git"` | |
|
||||||
| `displayName` | `string` | |
|
| `displayName` | `string` | |
|
||||||
| `createdAt` | `string` (ISO 8601) | |
|
| `createdAt` | `string` (ISO 8601) | |
|
||||||
| `updatedAt` | `string` (ISO 8601) | |
|
| `updatedAt` | `string` (ISO 8601) | |
|
||||||
| `archivedAt` | `string?` (ISO 8601) | Soft-delete timestamp |
|
| `archivedAt` | `string?` (ISO 8601) | Soft-delete timestamp |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -362,16 +362,16 @@ Array of project records.
|
|||||||
|
|
||||||
Array of workspace records. A workspace is a specific working directory within a project.
|
Array of workspace records. A workspace is a specific working directory within a project.
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
| ------------- | ----------------------------------------------- | ----------------------- |
|
||||||
| `workspaceId` | `string` | Primary key |
|
| `workspaceId` | `string` | Primary key |
|
||||||
| `projectId` | `string` | FK to Project.projectId |
|
| `projectId` | `string` | FK to Project.projectId |
|
||||||
| `cwd` | `string` | Filesystem path |
|
| `cwd` | `string` | Filesystem path |
|
||||||
| `kind` | `"local_checkout" \| "worktree" \| "directory"` | |
|
| `kind` | `"local_checkout" \| "worktree" \| "directory"` | |
|
||||||
| `displayName` | `string` | |
|
| `displayName` | `string` | |
|
||||||
| `createdAt` | `string` (ISO 8601) | |
|
| `createdAt` | `string` (ISO 8601) | |
|
||||||
| `updatedAt` | `string` (ISO 8601) | |
|
| `updatedAt` | `string` (ISO 8601) | |
|
||||||
| `archivedAt` | `string?` (ISO 8601) | Soft-delete timestamp |
|
| `archivedAt` | `string?` (ISO 8601) | Soft-delete timestamp |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -417,12 +417,12 @@ Stores binary attachment blobs keyed by attachment ID.
|
|||||||
|
|
||||||
### AttachmentMetadata
|
### AttachmentMetadata
|
||||||
|
|
||||||
| Field | Type | Description |
|
| Field | Type | Description |
|
||||||
|---|---|---|
|
| ------------- | --------- | ------------------------------ |
|
||||||
| `id` | `string` | Unique attachment ID |
|
| `id` | `string` | Unique attachment ID |
|
||||||
| `mimeType` | `string` | MIME type |
|
| `mimeType` | `string` | MIME type |
|
||||||
| `storageType` | `string` | Storage backend identifier |
|
| `storageType` | `string` | Storage backend identifier |
|
||||||
| `storageKey` | `string` | Key within the storage backend |
|
| `storageKey` | `string` | Key within the storage backend |
|
||||||
| `createdAt` | `number` | Epoch ms |
|
| `createdAt` | `number` | Epoch ms |
|
||||||
| `fileName` | `string?` | Original filename |
|
| `fileName` | `string?` | Original filename |
|
||||||
| `byteSize` | `number?` | Size in bytes |
|
| `byteSize` | `number?` | Size in bytes |
|
||||||
|
|||||||
+11
-6
@@ -31,6 +31,7 @@ PASEO_HOME=~/.paseo-blue npm run dev
|
|||||||
### Default ports
|
### Default ports
|
||||||
|
|
||||||
In the main checkout:
|
In the main checkout:
|
||||||
|
|
||||||
- Daemon: `localhost:6767`
|
- Daemon: `localhost:6767`
|
||||||
- Expo app: `localhost:8081`
|
- Expo app: `localhost:8081`
|
||||||
|
|
||||||
@@ -63,13 +64,13 @@ Pass either a DB directory or a `paseo.sqlite` file to `--db`. The script opens
|
|||||||
|
|
||||||
Every `scripts` entry with `"type": "service"` receives these environment variables:
|
Every `scripts` entry with `"type": "service"` receives these environment variables:
|
||||||
|
|
||||||
| Variable | Value |
|
| Variable | Value |
|
||||||
|---|---|
|
| --------------------------- | ------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `PASEO_SERVICE_<NAME>_URL` | Proxied daemon URL for a declared peer service. Prefer this for peer discovery; it survives peer restarts. |
|
| `PASEO_SERVICE_<NAME>_URL` | Proxied daemon URL for a declared peer service. Prefer this for peer discovery; it survives peer restarts. |
|
||||||
| `PASEO_SERVICE_<NAME>_PORT` | Raw ephemeral port for a declared peer service. Use only as a bypass escape hatch; it can go stale if that peer restarts. |
|
| `PASEO_SERVICE_<NAME>_PORT` | Raw ephemeral port for a declared peer service. Use only as a bypass escape hatch; it can go stale if that peer restarts. |
|
||||||
| `PASEO_URL` | Self alias for `PASEO_SERVICE_<SELF>_URL`. |
|
| `PASEO_URL` | Self alias for `PASEO_SERVICE_<SELF>_URL`. |
|
||||||
| `PASEO_PORT` | Self alias for `PASEO_SERVICE_<SELF>_PORT`. |
|
| `PASEO_PORT` | Self alias for `PASEO_SERVICE_<SELF>_PORT`. |
|
||||||
| `HOST` | Bind host for the service process. |
|
| `HOST` | Bind host for the service process. |
|
||||||
|
|
||||||
`<NAME>` is normalized from the script name by uppercasing it, replacing each run of non-`A-Z0-9` characters with `_`, and trimming leading or trailing `_`. For example, `app-server` and `app.server` both normalize to `APP_SERVER`; that collision fails at spawn time with an actionable error.
|
`<NAME>` is normalized from the script name by uppercasing it, replacing each run of non-`A-Z0-9` characters with `_`, and trimming leading or trailing `_`. For example, `app-server` and `app.server` both normalize to `APP_SERVER`; that collision fails at spawn time with an actionable error.
|
||||||
|
|
||||||
@@ -135,11 +136,13 @@ $PASEO_HOME/agents/{cwd-with-dashes}/{agent-id}.json
|
|||||||
```
|
```
|
||||||
|
|
||||||
Find an agent by ID:
|
Find an agent by ID:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
find $PASEO_HOME/agents -name "{agent-id}.json"
|
find $PASEO_HOME/agents -name "{agent-id}.json"
|
||||||
```
|
```
|
||||||
|
|
||||||
Find by content:
|
Find by content:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
rg -l "some title text" $PASEO_HOME/agents/
|
rg -l "some title text" $PASEO_HOME/agents/
|
||||||
```
|
```
|
||||||
@@ -149,11 +152,13 @@ rg -l "some title text" $PASEO_HOME/agents/
|
|||||||
Get the session ID from the agent JSON (`persistence.sessionId`), then:
|
Get the session ID from the agent JSON (`persistence.sessionId`), then:
|
||||||
|
|
||||||
**Claude:**
|
**Claude:**
|
||||||
|
|
||||||
```
|
```
|
||||||
~/.claude/projects/{cwd-with-dashes}/{session-id}.jsonl
|
~/.claude/projects/{cwd-with-dashes}/{session-id}.jsonl
|
||||||
```
|
```
|
||||||
|
|
||||||
**Codex:**
|
**Codex:**
|
||||||
|
|
||||||
```
|
```
|
||||||
~/.codex/sessions/{YYYY}/{MM}/{DD}/rollout-{timestamp}-{session-id}.jsonl
|
~/.codex/sessions/{YYYY}/{MM}/{DD}/rollout-{timestamp}-{session-id}.jsonl
|
||||||
```
|
```
|
||||||
|
|||||||
+56
-56
@@ -35,8 +35,8 @@ cat node_modules/material-icon-theme/icons/ICON_NAME.svg
|
|||||||
```
|
```
|
||||||
|
|
||||||
3. Add two things to `material-file-icons.ts`:
|
3. Add two things to `material-file-icons.ts`:
|
||||||
|
|
||||||
- The SVG string in `SVG_ICONS`:
|
- The SVG string in `SVG_ICONS`:
|
||||||
|
|
||||||
```ts
|
```ts
|
||||||
"icon_name": `<svg ...>...</svg>`,
|
"icon_name": `<svg ...>...</svg>`,
|
||||||
```
|
```
|
||||||
@@ -52,58 +52,58 @@ cat node_modules/material-icon-theme/icons/ICON_NAME.svg
|
|||||||
|
|
||||||
53 unique icons covering these extensions:
|
53 unique icons covering these extensions:
|
||||||
|
|
||||||
| Extension(s) | Icon |
|
| Extension(s) | Icon |
|
||||||
|---|---|
|
| ------------------------------------------ | ----------- |
|
||||||
| `ts` | typescript |
|
| `ts` | typescript |
|
||||||
| `tsx` | react_ts |
|
| `tsx` | react_ts |
|
||||||
| `js` | javascript |
|
| `js` | javascript |
|
||||||
| `jsx` | react |
|
| `jsx` | react |
|
||||||
| `py` | python |
|
| `py` | python |
|
||||||
| `go` | go |
|
| `go` | go |
|
||||||
| `rs` | rust |
|
| `rs` | rust |
|
||||||
| `rb` | ruby |
|
| `rb` | ruby |
|
||||||
| `java` | java |
|
| `java` | java |
|
||||||
| `kt` | kotlin |
|
| `kt` | kotlin |
|
||||||
| `c` | c |
|
| `c` | c |
|
||||||
| `cpp` | cpp |
|
| `cpp` | cpp |
|
||||||
| `h` | h |
|
| `h` | h |
|
||||||
| `hpp` | hpp |
|
| `hpp` | hpp |
|
||||||
| `cs` | csharp |
|
| `cs` | csharp |
|
||||||
| `swift` | swift |
|
| `swift` | swift |
|
||||||
| `dart` | dart |
|
| `dart` | dart |
|
||||||
| `ex`, `exs` | elixir |
|
| `ex`, `exs` | elixir |
|
||||||
| `erl` | erlang |
|
| `erl` | erlang |
|
||||||
| `hs` | haskell |
|
| `hs` | haskell |
|
||||||
| `clj` | clojure |
|
| `clj` | clojure |
|
||||||
| `scala` | scala |
|
| `scala` | scala |
|
||||||
| `ml` | ocaml |
|
| `ml` | ocaml |
|
||||||
| `r` | r |
|
| `r` | r |
|
||||||
| `lua` | lua |
|
| `lua` | lua |
|
||||||
| `zig` | zig |
|
| `zig` | zig |
|
||||||
| `nix` | nix |
|
| `nix` | nix |
|
||||||
| `php` | php |
|
| `php` | php |
|
||||||
| `html` | html |
|
| `html` | html |
|
||||||
| `css` | css |
|
| `css` | css |
|
||||||
| `scss` | sass |
|
| `scss` | sass |
|
||||||
| `less` | less |
|
| `less` | less |
|
||||||
| `json` | json |
|
| `json` | json |
|
||||||
| `yml`, `yaml` | yaml |
|
| `yml`, `yaml` | yaml |
|
||||||
| `xml` | xml |
|
| `xml` | xml |
|
||||||
| `toml` | toml |
|
| `toml` | toml |
|
||||||
| `md`, `markdown` | markdown |
|
| `md`, `markdown` | markdown |
|
||||||
| `sql` | database |
|
| `sql` | database |
|
||||||
| `graphql`, `gql` | graphql |
|
| `graphql`, `gql` | graphql |
|
||||||
| `sh`, `bash` | console |
|
| `sh`, `bash` | console |
|
||||||
| `tf` | terraform |
|
| `tf` | terraform |
|
||||||
| `hcl` | hcl |
|
| `hcl` | hcl |
|
||||||
| `vue` | vue |
|
| `vue` | vue |
|
||||||
| `svelte` | svelte |
|
| `svelte` | svelte |
|
||||||
| `astro` | astro |
|
| `astro` | astro |
|
||||||
| `wasm` | webassembly |
|
| `wasm` | webassembly |
|
||||||
| `svg` | svg |
|
| `svg` | svg |
|
||||||
| `png`, `jpg`, `jpeg`, `gif`, `webp`, `ico` | image |
|
| `png`, `jpg`, `jpeg`, `gif`, `webp`, `ico` | image |
|
||||||
| `txt` | document |
|
| `txt` | document |
|
||||||
| `conf`, `cfg`, `ini` | settings |
|
| `conf`, `cfg`, `ini` | settings |
|
||||||
| `lock` | lock |
|
| `lock` | lock |
|
||||||
| `groovy` | groovy |
|
| `groovy` | groovy |
|
||||||
| `gradle` | gradle |
|
| `gradle` | gradle |
|
||||||
|
|||||||
@@ -223,7 +223,7 @@ const styles = StyleSheet.create((theme) => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
<Animated.View style={[styles.sidebar, animatedStyle]} />
|
<Animated.View style={[styles.sidebar, animatedStyle]} />;
|
||||||
```
|
```
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
@@ -244,7 +244,7 @@ const { theme } = useUnistyles();
|
|||||||
|
|
||||||
<Animated.View
|
<Animated.View
|
||||||
style={[staticStyles.sidebar, animatedStyle, { backgroundColor: theme.colors.surfaceSidebar }]}
|
style={[staticStyles.sidebar, animatedStyle, { backgroundColor: theme.colors.surfaceSidebar }]}
|
||||||
/>
|
/>;
|
||||||
```
|
```
|
||||||
|
|
||||||
Regular `View` components can safely use Unistyles dynamic styles — the conflict is specific to `Animated.View`.
|
Regular `View` components can safely use Unistyles dynamic styles — the conflict is specific to `Animated.View`.
|
||||||
|
|||||||
@@ -30,6 +30,7 @@ Each project opens as a workspace. For git projects, the default workspace is th
|
|||||||
### Inside a workspace
|
### Inside a workspace
|
||||||
|
|
||||||
A workspace is a flexible canvas:
|
A workspace is a flexible canvas:
|
||||||
|
|
||||||
- Launch multiple agents side by side in split panes
|
- Launch multiple agents side by side in split panes
|
||||||
- Open terminals alongside agents
|
- Open terminals alongside agents
|
||||||
- Mix and match providers within the same workspace
|
- Mix and match providers within the same workspace
|
||||||
@@ -39,6 +40,7 @@ A workspace is a flexible canvas:
|
|||||||
Paseo is a client-server system. The daemon (Node.js) runs on your machine, manages agent processes, and streams output in real time over WebSocket. Clients connect to the daemon — locally or remotely.
|
Paseo is a client-server system. The daemon (Node.js) runs on your machine, manages agent processes, and streams output in real time over WebSocket. Clients connect to the daemon — locally or remotely.
|
||||||
|
|
||||||
This architecture means:
|
This architecture means:
|
||||||
|
|
||||||
- The daemon can run on any machine: laptop, VM, remote server
|
- The daemon can run on any machine: laptop, VM, remote server
|
||||||
- Multiple clients can connect simultaneously
|
- Multiple clients can connect simultaneously
|
||||||
- Agents keep running when you close the app
|
- Agents keep running when you close the app
|
||||||
|
|||||||
+14
-7
@@ -58,10 +58,10 @@ type MyProviderClientOptions = {
|
|||||||
export class MyProviderACPAgentClient extends ACPAgentClient {
|
export class MyProviderACPAgentClient extends ACPAgentClient {
|
||||||
constructor(options: MyProviderClientOptions) {
|
constructor(options: MyProviderClientOptions) {
|
||||||
super({
|
super({
|
||||||
provider: "my-provider", // Must match the ID used everywhere else
|
provider: "my-provider", // Must match the ID used everywhere else
|
||||||
logger: options.logger,
|
logger: options.logger,
|
||||||
runtimeSettings: options.runtimeSettings,
|
runtimeSettings: options.runtimeSettings,
|
||||||
defaultCommand: ["my-agent-binary", "--acp"], // CLI command to spawn
|
defaultCommand: ["my-agent-binary", "--acp"], // CLI command to spawn
|
||||||
defaultModes: MY_PROVIDER_MODES,
|
defaultModes: MY_PROVIDER_MODES,
|
||||||
capabilities: MY_PROVIDER_CAPABILITIES,
|
capabilities: MY_PROVIDER_CAPABILITIES,
|
||||||
});
|
});
|
||||||
@@ -70,7 +70,7 @@ export class MyProviderACPAgentClient extends ACPAgentClient {
|
|||||||
// Override isAvailable() if the provider needs specific auth/env vars
|
// Override isAvailable() if the provider needs specific auth/env vars
|
||||||
override async isAvailable(): Promise<boolean> {
|
override async isAvailable(): Promise<boolean> {
|
||||||
if (!(await super.isAvailable())) {
|
if (!(await super.isAvailable())) {
|
||||||
return false; // Binary not found
|
return false; // Binary not found
|
||||||
}
|
}
|
||||||
return Boolean(process.env["MY_PROVIDER_API_KEY"]);
|
return Boolean(process.env["MY_PROVIDER_API_KEY"]);
|
||||||
}
|
}
|
||||||
@@ -212,8 +212,8 @@ export const agentConfigs = {
|
|||||||
provider: "my-provider",
|
provider: "my-provider",
|
||||||
model: "default-model-id",
|
model: "default-model-id",
|
||||||
modes: {
|
modes: {
|
||||||
full: "autonomous", // Mode with no permission prompts
|
full: "autonomous", // Mode with no permission prompts
|
||||||
ask: "default", // Mode that requires permission approval
|
ask: "default", // Mode that requires permission approval
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
} as const satisfies Record<string, AgentTestConfig>;
|
} as const satisfies Record<string, AgentTestConfig>;
|
||||||
@@ -264,8 +264,15 @@ If your agent does not speak ACP, implement the interfaces from `agent-sdk-types
|
|||||||
interface AgentClient {
|
interface AgentClient {
|
||||||
readonly provider: AgentProvider;
|
readonly provider: AgentProvider;
|
||||||
readonly capabilities: AgentCapabilityFlags;
|
readonly capabilities: AgentCapabilityFlags;
|
||||||
createSession(config: AgentSessionConfig, launchContext?: AgentLaunchContext): Promise<AgentSession>;
|
createSession(
|
||||||
resumeSession(handle: AgentPersistenceHandle, overrides?: Partial<AgentSessionConfig>, launchContext?: AgentLaunchContext): Promise<AgentSession>;
|
config: AgentSessionConfig,
|
||||||
|
launchContext?: AgentLaunchContext,
|
||||||
|
): Promise<AgentSession>;
|
||||||
|
resumeSession(
|
||||||
|
handle: AgentPersistenceHandle,
|
||||||
|
overrides?: Partial<AgentSessionConfig>,
|
||||||
|
launchContext?: AgentLaunchContext,
|
||||||
|
): Promise<AgentSession>;
|
||||||
listModels(options?: ListModelsOptions): Promise<AgentModelDefinition[]>;
|
listModels(options?: ListModelsOptions): Promise<AgentModelDefinition[]>;
|
||||||
isAvailable(): Promise<boolean>;
|
isAvailable(): Promise<boolean>;
|
||||||
// Optional:
|
// Optional:
|
||||||
|
|||||||
+4
-1
@@ -79,7 +79,9 @@ const realSender: EmailSender = { send: sendgrid.send };
|
|||||||
function createTestEmailSender() {
|
function createTestEmailSender() {
|
||||||
const sent: Array<{ to: string; body: string }> = [];
|
const sent: Array<{ to: string; body: string }> = [];
|
||||||
return {
|
return {
|
||||||
send: async (to: string, body: string) => { sent.push({ to, body }); },
|
send: async (to: string, body: string) => {
|
||||||
|
sent.push({ to, body });
|
||||||
|
},
|
||||||
sent,
|
sent,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -115,6 +117,7 @@ The test output is the source of truth, not your reading of the code.
|
|||||||
## Design for testability
|
## Design for testability
|
||||||
|
|
||||||
If code isn't testable, refactor it. Signs:
|
If code isn't testable, refactor it. Signs:
|
||||||
|
|
||||||
- You want to reach for a mock
|
- You want to reach for a mock
|
||||||
- You can't inject a dependency
|
- You can't inject a dependency
|
||||||
- You need to test private internals
|
- You need to test private internals
|
||||||
|
|||||||
+21
-23
@@ -19,7 +19,7 @@ The important detail: the automatic native path tracks `props.style`. It does no
|
|||||||
Avoid this pattern when the style depends on the theme:
|
Avoid this pattern when the style depends on the theme:
|
||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
<ScrollView contentContainerStyle={styles.container} />
|
<ScrollView contentContainerStyle={styles.container} />;
|
||||||
|
|
||||||
const styles = StyleSheet.create((theme) => ({
|
const styles = StyleSheet.create((theme) => ({
|
||||||
container: {
|
container: {
|
||||||
@@ -39,10 +39,8 @@ Preferred pattern: put themed backgrounds on a normal wrapper view, and keep `co
|
|||||||
|
|
||||||
```tsx
|
```tsx
|
||||||
<View style={styles.container}>
|
<View style={styles.container}>
|
||||||
<ScrollView contentContainerStyle={styles.contentContainer}>
|
<ScrollView contentContainerStyle={styles.contentContainer}>{children}</ScrollView>
|
||||||
{children}
|
</View>;
|
||||||
</ScrollView>
|
|
||||||
</View>
|
|
||||||
|
|
||||||
const styles = StyleSheet.create((theme) => ({
|
const styles = StyleSheet.create((theme) => ({
|
||||||
container: {
|
container: {
|
||||||
@@ -66,10 +64,7 @@ import { StyleSheet, withUnistyles } from "react-native-unistyles";
|
|||||||
|
|
||||||
const ThemedScrollView = withUnistyles(ScrollView);
|
const ThemedScrollView = withUnistyles(ScrollView);
|
||||||
|
|
||||||
<ThemedScrollView
|
<ThemedScrollView style={styles.scrollView} contentContainerStyle={styles.contentContainer} />;
|
||||||
style={styles.scrollView}
|
|
||||||
contentContainerStyle={styles.contentContainer}
|
|
||||||
/>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
`withUnistyles` extracts dependency metadata from both `style` and `contentContainerStyle`, subscribes to the relevant theme/runtime changes, and re-renders only that wrapped component when needed. Its [auto-mapping behavior for `style` and `contentContainerStyle`](https://www.unistyl.es/v3/references/with-unistyles#auto-mapping-for-style-and-contentcontainerstyle-props) is the reason it fixes themed `ScrollView` content containers. Reach for it when wrapper-view layout would be awkward or when a third-party component needs theme-aware non-`style` props mapped through Unistyles.
|
`withUnistyles` extracts dependency metadata from both `style` and `contentContainerStyle`, subscribes to the relevant theme/runtime changes, and re-renders only that wrapped component when needed. Its [auto-mapping behavior for `style` and `contentContainerStyle`](https://www.unistyl.es/v3/references/with-unistyles#auto-mapping-for-style-and-contentcontainerstyle-props) is the reason it fixes themed `ScrollView` content containers. Reach for it when wrapper-view layout would be awkward or when a third-party component needs theme-aware non-`style` props mapped through Unistyles.
|
||||||
@@ -80,11 +75,8 @@ The smallest escape hatch is to use `useUnistyles()` and pass an inline value th
|
|||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
|
|
||||||
<ScrollView
|
<ScrollView
|
||||||
contentContainerStyle={[
|
contentContainerStyle={[styles.contentContainer, { backgroundColor: theme.colors.surface0 }]}
|
||||||
styles.contentContainer,
|
/>;
|
||||||
{ backgroundColor: theme.colors.surface0 },
|
|
||||||
]}
|
|
||||||
/>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Use this sparingly. It works because React re-renders the prop, but it gives up the main Unistyles native-update path for that value.
|
Use this sparingly. It works because React re-renders the prop, but it gives up the main Unistyles native-update path for that value.
|
||||||
@@ -98,8 +90,14 @@ The sharp edge: Unistyles hashes styles by value. If `withUnistyles` receives a
|
|||||||
Concrete regression we hit: `welcome-screen.tsx` had `const ThemedScrollView = withUnistyles(ScrollView)` with `style={{ flex: 1, backgroundColor: theme.colors.surface0 }}`. `agent-panel.tsx` had `root` and `container` styles with the exact same value. All three collided on class `unistyles_j2k2iilhfz`, so the browser stylesheet contained:
|
Concrete regression we hit: `welcome-screen.tsx` had `const ThemedScrollView = withUnistyles(ScrollView)` with `style={{ flex: 1, backgroundColor: theme.colors.surface0 }}`. `agent-panel.tsx` had `root` and `container` styles with the exact same value. All three collided on class `unistyles_j2k2iilhfz`, so the browser stylesheet contained:
|
||||||
|
|
||||||
```css
|
```css
|
||||||
.unistyles_j2k2iilhfz { flex: 1 1 0%; background-color: var(--colors-surface0); }
|
.unistyles_j2k2iilhfz {
|
||||||
.unistyles_j2k2iilhfz > * { flex: 1 1 0%; background-color: var(--colors-surface0); }
|
flex: 1 1 0%;
|
||||||
|
background-color: var(--colors-surface0);
|
||||||
|
}
|
||||||
|
.unistyles_j2k2iilhfz > * {
|
||||||
|
flex: 1 1 0%;
|
||||||
|
background-color: var(--colors-surface0);
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
The child-selector rule forced `flex:1` and `background-color: surface0` onto the Composer's outer `Animated.View` (a direct child of `container`), stretching it to fill remaining space and leaving a large empty gap between the composer UI and the bottom of the screen. It also painted a `surface0` band behind the scroll-to-bottom button. The bug only appeared in the browser — Electron skips `WelcomeScreen` after pairing, so the `> *` rule was never injected there.
|
The child-selector rule forced `flex:1` and `background-color: surface0` onto the Composer's outer `Animated.View` (a direct child of `container`), stretching it to fill remaining space and leaving a large empty gap between the composer UI and the bottom of the screen. It also painted a `surface0` band behind the scroll-to-bottom button. The bug only appeared in the browser — Electron skips `WelcomeScreen` after pairing, so the `> *` rule was never injected there.
|
||||||
@@ -113,8 +111,10 @@ Symptoms to watch for:
|
|||||||
Quick confirmation in DevTools console:
|
Quick confirmation in DevTools console:
|
||||||
|
|
||||||
```js
|
```js
|
||||||
[...document.styleSheets].flatMap(s => [...(s.cssRules || [])])
|
[...document.styleSheets]
|
||||||
.map(r => r.cssText).filter(t => t.includes("unistyles") && t.includes("> *"));
|
.flatMap((s) => [...(s.cssRules || [])])
|
||||||
|
.map((r) => r.cssText)
|
||||||
|
.filter((t) => t.includes("unistyles") && t.includes("> *"));
|
||||||
```
|
```
|
||||||
|
|
||||||
Any match beyond benign `r-pointerEvents-* > *` rules from react-native-web is a leak.
|
Any match beyond benign `r-pointerEvents-* > *` rules from react-native-web is a leak.
|
||||||
@@ -130,9 +130,7 @@ We saw this in `AdaptiveModalSheet`: the body text and buttons were dark-theme-c
|
|||||||
```tsx
|
```tsx
|
||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
|
|
||||||
<Text style={[styles.title, { color: theme.colors.foreground }]}>
|
<Text style={[styles.title, { color: theme.colors.foreground }]}>{title}</Text>;
|
||||||
{title}
|
|
||||||
</Text>
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Keep layout and typography in `StyleSheet.create`; move only the stale theme-dependent value through React. If a larger subtree shows the same behavior, consider remounting the sheet on theme changes or moving the themed paint onto a wrapper that is mounted with the visible content.
|
Keep layout and typography in `StyleSheet.create`; move only the stale theme-dependent value through React. If a larger subtree shows the same behavior, consider remounting the sheet on theme changes or moving the themed paint onto a wrapper that is mounted with the visible content.
|
||||||
@@ -170,7 +168,7 @@ Use `useUnistyles()` inside the component instead:
|
|||||||
```tsx
|
```tsx
|
||||||
const { theme } = useUnistyles();
|
const { theme } = useUnistyles();
|
||||||
|
|
||||||
<ChevronDown size={theme.iconSize.md} color={theme.colors.foregroundMuted} />
|
<ChevronDown size={theme.iconSize.md} color={theme.colors.foregroundMuted} />;
|
||||||
```
|
```
|
||||||
|
|
||||||
Importing `baseColors`, theme-name constants, or `type Theme` is fine when the value is intentionally static or type-only.
|
Importing `baseColors`, theme-name constants, or `type Theme` is fine when the value is intentionally static or type-only.
|
||||||
@@ -233,4 +231,4 @@ The welcome-screen investigation used this approach to prove the white layer was
|
|||||||
- [GitHub issue #550: ScrollView sticky-header theme updates](https://github.com/jpudysz/react-native-unistyles/issues/550)
|
- [GitHub issue #550: ScrollView sticky-header theme updates](https://github.com/jpudysz/react-native-unistyles/issues/550)
|
||||||
- [GitHub issue #817: `UnistylesRuntime.themeName` does not re-render](https://github.com/jpudysz/react-native-unistyles/issues/817)
|
- [GitHub issue #817: `UnistylesRuntime.themeName` does not re-render](https://github.com/jpudysz/react-native-unistyles/issues/817)
|
||||||
- [GitHub issue #1030: `Image.tintColor` and native style update edge case](https://github.com/jpudysz/react-native-unistyles/issues/1030)
|
- [GitHub issue #1030: `Image.tintColor` and native style update edge case](https://github.com/jpudysz/react-native-unistyles/issues/1030)
|
||||||
- [Local research note: welcome theme split](</Users/moboudra/.paseo/notes/welcome-theme-split-research.md>)
|
- [Local research note: welcome theme split](/Users/moboudra/.paseo/notes/welcome-theme-split-research.md)
|
||||||
|
|||||||
@@ -92,9 +92,7 @@ type DaemonClientConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function loadDaemonClientConstructor(): Promise<
|
async function loadDaemonClientConstructor(): Promise<
|
||||||
new (
|
new (config: DaemonClientConfig) => DaemonClientInstance
|
||||||
config: DaemonClientConfig,
|
|
||||||
) => DaemonClientInstance
|
|
||||||
> {
|
> {
|
||||||
const repoRoot = path.resolve(__dirname, "../../../../");
|
const repoRoot = path.resolve(__dirname, "../../../../");
|
||||||
const moduleUrl = pathToFileURL(
|
const moduleUrl = pathToFileURL(
|
||||||
|
|||||||
@@ -81,9 +81,7 @@ type ArchiveTabDaemonClientConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function loadDaemonClientConstructor(): Promise<
|
async function loadDaemonClientConstructor(): Promise<
|
||||||
new (
|
new (config: ArchiveTabDaemonClientConfig) => ArchiveTabDaemonClient
|
||||||
config: ArchiveTabDaemonClientConfig,
|
|
||||||
) => ArchiveTabDaemonClient
|
|
||||||
> {
|
> {
|
||||||
const repoRoot = path.resolve(__dirname, "../../../../");
|
const repoRoot = path.resolve(__dirname, "../../../../");
|
||||||
const moduleUrl = pathToFileURL(
|
const moduleUrl = pathToFileURL(
|
||||||
|
|||||||
@@ -49,9 +49,7 @@ function getDaemonWsUrl(): string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function loadDaemonClientConstructor(): Promise<
|
async function loadDaemonClientConstructor(): Promise<
|
||||||
new (
|
new (config: NewWorkspaceDaemonClientConfig) => NewWorkspaceDaemonClient
|
||||||
config: NewWorkspaceDaemonClientConfig,
|
|
||||||
) => NewWorkspaceDaemonClient
|
|
||||||
> {
|
> {
|
||||||
const repoRoot = path.resolve(__dirname, "../../../../");
|
const repoRoot = path.resolve(__dirname, "../../../../");
|
||||||
const moduleUrl = pathToFileURL(
|
const moduleUrl = pathToFileURL(
|
||||||
|
|||||||
@@ -63,9 +63,7 @@ type TerminalPerfDaemonClientConfig = {
|
|||||||
};
|
};
|
||||||
|
|
||||||
async function loadDaemonClientConstructor(): Promise<
|
async function loadDaemonClientConstructor(): Promise<
|
||||||
new (
|
new (config: TerminalPerfDaemonClientConfig) => TerminalPerfDaemonClient
|
||||||
config: TerminalPerfDaemonClientConfig,
|
|
||||||
) => TerminalPerfDaemonClient
|
|
||||||
> {
|
> {
|
||||||
const repoRoot = path.resolve(__dirname, "../../../../");
|
const repoRoot = path.resolve(__dirname, "../../../../");
|
||||||
const moduleUrl = pathToFileURL(
|
const moduleUrl = pathToFileURL(
|
||||||
|
|||||||
@@ -24,4 +24,3 @@ appId: sh.paseo
|
|||||||
commands:
|
commands:
|
||||||
- tapOn:
|
- tapOn:
|
||||||
point: "50%,20%"
|
point: "50%,20%"
|
||||||
|
|
||||||
|
|||||||
@@ -3,4 +3,3 @@ appId: sh.paseo
|
|||||||
- launchApp:
|
- launchApp:
|
||||||
clearState: true
|
clearState: true
|
||||||
- runFlow: flows/dev-client.yaml
|
- runFlow: flows/dev-client.yaml
|
||||||
|
|
||||||
|
|||||||
@@ -1,8 +1,8 @@
|
|||||||
{
|
{
|
||||||
"name": "@getpaseo/app",
|
"name": "@getpaseo/app",
|
||||||
"main": "index.ts",
|
|
||||||
"version": "0.1.62",
|
"version": "0.1.62",
|
||||||
"private": true,
|
"private": true,
|
||||||
|
"main": "index.ts",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "APP_VARIANT=development expo start",
|
"start": "APP_VARIANT=development expo start",
|
||||||
"reset-project": "node ./scripts/reset-project.js",
|
"reset-project": "node ./scripts/reset-project.js",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html lang="%LANG_ISO_CODE%">
|
<html lang="%LANG_ISO_CODE%">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="utf-8" />
|
<meta charset="utf-8" />
|
||||||
@@ -21,8 +21,9 @@
|
|||||||
}
|
}
|
||||||
/* Prevent white flash before React mounts */
|
/* Prevent white flash before React mounts */
|
||||||
@media (prefers-color-scheme: dark) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html, body {
|
html,
|
||||||
background-color: #181B1A;
|
body {
|
||||||
|
background-color: #181b1a;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/* These styles make the root element full-height */
|
/* These styles make the root element full-height */
|
||||||
@@ -38,17 +39,17 @@
|
|||||||
input,
|
input,
|
||||||
textarea,
|
textarea,
|
||||||
select,
|
select,
|
||||||
[role='button'],
|
[role="button"],
|
||||||
[role='link'],
|
[role="link"],
|
||||||
[role='textbox'],
|
[role="textbox"],
|
||||||
[role='combobox'],
|
[role="combobox"],
|
||||||
[role='tab'],
|
[role="tab"],
|
||||||
[role='switch'],
|
[role="switch"],
|
||||||
[role='checkbox'],
|
[role="checkbox"],
|
||||||
[role='slider'],
|
[role="slider"],
|
||||||
[role='menuitem'],
|
[role="menuitem"],
|
||||||
[tabindex],
|
[tabindex],
|
||||||
[contenteditable='true'] {
|
[contenteditable="true"] {
|
||||||
-webkit-app-region: no-drag !important;
|
-webkit-app-region: no-drag !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -59,7 +60,7 @@
|
|||||||
outline: none;
|
outline: none;
|
||||||
}
|
}
|
||||||
*:focus-visible {
|
*:focus-visible {
|
||||||
outline: 2px solid #20744A;
|
outline: 2px solid #20744a;
|
||||||
outline-offset: 2px;
|
outline-offset: 2px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -7,9 +7,8 @@ let attachmentStorePromise: Promise<AttachmentStore> | null = null;
|
|||||||
async function createAttachmentStore(): Promise<AttachmentStore> {
|
async function createAttachmentStore(): Promise<AttachmentStore> {
|
||||||
if (isWeb) {
|
if (isWeb) {
|
||||||
if (isElectronRuntime()) {
|
if (isElectronRuntime()) {
|
||||||
const { createDesktopAttachmentStore } = await import(
|
const { createDesktopAttachmentStore } =
|
||||||
"../desktop/attachments/desktop-attachment-store"
|
await import("../desktop/attachments/desktop-attachment-store");
|
||||||
);
|
|
||||||
return createDesktopAttachmentStore();
|
return createDesktopAttachmentStore();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -82,13 +82,7 @@ vi.mock("expo-image", () => ({
|
|||||||
|
|
||||||
vi.mock("react-native", async () => {
|
vi.mock("react-native", async () => {
|
||||||
const actual = await vi.importActual<Record<string, unknown>>("react-native");
|
const actual = await vi.importActual<Record<string, unknown>>("react-native");
|
||||||
const Modal = ({
|
const Modal = ({ visible = true, children }: { visible?: boolean; children?: React.ReactNode }) =>
|
||||||
visible = true,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
visible?: boolean;
|
|
||||||
children?: React.ReactNode;
|
|
||||||
}) =>
|
|
||||||
visible ? React.createElement("div", { "data-testid": "lightbox-modal" }, children) : null;
|
visible ? React.createElement("div", { "data-testid": "lightbox-modal" }, children) : null;
|
||||||
return { ...actual, Modal };
|
return { ...actual, Modal };
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -285,13 +285,7 @@ vi.mock("expo-image", () => ({
|
|||||||
|
|
||||||
vi.mock("react-native", async () => {
|
vi.mock("react-native", async () => {
|
||||||
const actual = await vi.importActual<Record<string, unknown>>("react-native");
|
const actual = await vi.importActual<Record<string, unknown>>("react-native");
|
||||||
const Modal = ({
|
const Modal = ({ visible = true, children }: { visible?: boolean; children?: React.ReactNode }) =>
|
||||||
visible = true,
|
|
||||||
children,
|
|
||||||
}: {
|
|
||||||
visible?: boolean;
|
|
||||||
children?: React.ReactNode;
|
|
||||||
}) =>
|
|
||||||
visible ? React.createElement("div", { "data-testid": "lightbox-modal" }, children) : null;
|
visible ? React.createElement("div", { "data-testid": "lightbox-modal" }, children) : null;
|
||||||
return { ...actual, Modal };
|
return { ...actual, Modal };
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -1954,8 +1954,8 @@ export function SidebarWorkspaceList({
|
|||||||
select: toProjectIconDataUri,
|
select: toProjectIconDataUri,
|
||||||
enabled: Boolean(
|
enabled: Boolean(
|
||||||
getHostRuntimeStore().getClient(request.serverId) &&
|
getHostRuntimeStore().getClient(request.serverId) &&
|
||||||
isHostRuntimeConnected(getHostRuntimeStore().getSnapshot(request.serverId)) &&
|
isHostRuntimeConnected(getHostRuntimeStore().getSnapshot(request.serverId)) &&
|
||||||
request.cwd,
|
request.cwd,
|
||||||
),
|
),
|
||||||
staleTime: Infinity,
|
staleTime: Infinity,
|
||||||
gcTime: 1000 * 60 * 60,
|
gcTime: 1000 * 60 * 60,
|
||||||
|
|||||||
@@ -134,18 +134,17 @@ interface SplitNodeViewProps extends Omit<SplitContainerProps, "layout"> {
|
|||||||
tabDropPreview: TabDropPreview | null;
|
tabDropPreview: TabDropPreview | null;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface SplitPaneViewProps
|
interface SplitPaneViewProps extends Omit<
|
||||||
extends Omit<
|
SplitNodeViewProps,
|
||||||
SplitNodeViewProps,
|
| "node"
|
||||||
| "node"
|
| "workspaceKey"
|
||||||
| "workspaceKey"
|
| "focusedPaneId"
|
||||||
| "focusedPaneId"
|
| "activeDragTabId"
|
||||||
| "activeDragTabId"
|
| "showDropZones"
|
||||||
| "showDropZones"
|
| "dropPreview"
|
||||||
| "dropPreview"
|
| "onMoveTabToPane"
|
||||||
| "onMoveTabToPane"
|
| "onResizeSplit"
|
||||||
| "onResizeSplit"
|
> {
|
||||||
> {
|
|
||||||
pane: SplitPane;
|
pane: SplitPane;
|
||||||
uiTabs: WorkspaceTab[];
|
uiTabs: WorkspaceTab[];
|
||||||
isFocused: boolean;
|
isFocused: boolean;
|
||||||
|
|||||||
@@ -237,23 +237,24 @@ describe("providers snapshot hook cache scope", () => {
|
|||||||
it.each([
|
it.each([
|
||||||
{ name: "missing", entries: [] },
|
{ name: "missing", entries: [] },
|
||||||
{ name: "loading", entries: [codexEntry("loading")] },
|
{ name: "loading", entries: [codexEntry("loading")] },
|
||||||
])("ensures a selected provider snapshot on selector open when it is $name", async ({
|
])(
|
||||||
entries,
|
"ensures a selected provider snapshot on selector open when it is $name",
|
||||||
}) => {
|
async ({ entries }) => {
|
||||||
enableProvidersSnapshot();
|
enableProvidersSnapshot();
|
||||||
mockClient.getProvidersSnapshot
|
mockClient.getProvidersSnapshot
|
||||||
.mockResolvedValueOnce(providersSnapshot(entries))
|
.mockResolvedValueOnce(providersSnapshot(entries))
|
||||||
.mockResolvedValueOnce(providersSnapshot([codexEntry("ready", [readyCodexModel])]));
|
.mockResolvedValueOnce(providersSnapshot([codexEntry("ready", [readyCodexModel])]));
|
||||||
|
|
||||||
const { result } = renderProvidersSnapshotHook();
|
const { result } = renderProvidersSnapshotHook();
|
||||||
|
|
||||||
await waitForSnapshotEntries(result, entries);
|
await waitForSnapshotEntries(result, entries);
|
||||||
await openSelectorForSelectedProvider(result);
|
await openSelectorForSelectedProvider(result);
|
||||||
await waitForSnapshotReads(2);
|
await waitForSnapshotReads(2);
|
||||||
|
|
||||||
expect(mockClient.getProvidersSnapshot).toHaveBeenLastCalledWith({});
|
expect(mockClient.getProvidersSnapshot).toHaveBeenLastCalledWith({});
|
||||||
expect(mockClient.refreshProvidersSnapshot).not.toHaveBeenCalled();
|
expect(mockClient.refreshProvidersSnapshot).not.toHaveBeenCalled();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
it("does not ensure a selected provider snapshot on selector open when the provider is ready with no models", async () => {
|
it("does not ensure a selected provider snapshot on selector open when the provider is ready with no models", async () => {
|
||||||
enableProvidersSnapshot();
|
enableProvidersSnapshot();
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ export function resolveKeyboardFocusScope(input: {
|
|||||||
candidates.some((element) =>
|
candidates.some((element) =>
|
||||||
Boolean(
|
Boolean(
|
||||||
element.closest("[data-testid='command-center-panel']") ||
|
element.closest("[data-testid='command-center-panel']") ||
|
||||||
element.closest("[data-testid='command-center-input']"),
|
element.closest("[data-testid='command-center-input']"),
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -289,23 +289,19 @@ describe("keyboard-shortcuts", () => {
|
|||||||
},
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
it.each(matchingCases)("$name", ({
|
it.each(matchingCases)(
|
||||||
event,
|
"$name",
|
||||||
context,
|
({ event, context, action, payload, preventDefault, stopPropagation }) => {
|
||||||
action,
|
expectShortcutResolution({
|
||||||
payload,
|
event,
|
||||||
preventDefault,
|
context,
|
||||||
stopPropagation,
|
action,
|
||||||
}) => {
|
...(payload !== undefined ? { payload } : {}),
|
||||||
expectShortcutResolution({
|
...(preventDefault !== undefined ? { preventDefault } : {}),
|
||||||
event,
|
...(stopPropagation !== undefined ? { stopPropagation } : {}),
|
||||||
context,
|
});
|
||||||
action,
|
},
|
||||||
...(payload !== undefined ? { payload } : {}),
|
);
|
||||||
...(preventDefault !== undefined ? { preventDefault } : {}),
|
|
||||||
...(stopPropagation !== undefined ? { stopPropagation } : {}),
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
const nonMatchingCases: NonMatchingShortcutCase[] = [
|
const nonMatchingCases: NonMatchingShortcutCase[] = [
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -491,10 +491,7 @@ export class HostRuntimeController {
|
|||||||
private probeRequestVersion = 0;
|
private probeRequestVersion = 0;
|
||||||
private probeCycleInFlight: Promise<void> | null = null;
|
private probeCycleInFlight: Promise<void> | null = null;
|
||||||
|
|
||||||
constructor(input: {
|
constructor(input: { host: HostProfile; deps?: HostRuntimeControllerDeps }) {
|
||||||
host: HostProfile;
|
|
||||||
deps?: HostRuntimeControllerDeps;
|
|
||||||
}) {
|
|
||||||
this.host = input.host;
|
this.host = input.host;
|
||||||
this.deps = input.deps ?? createDefaultDeps();
|
this.deps = input.deps ?? createDefaultDeps();
|
||||||
this.connectionMachineState = {
|
this.connectionMachineState = {
|
||||||
@@ -1155,9 +1152,7 @@ export class HostRuntimeStore {
|
|||||||
private storageLoaded = false;
|
private storageLoaded = false;
|
||||||
private bootstrapAttempted = false;
|
private bootstrapAttempted = false;
|
||||||
|
|
||||||
constructor(input?: {
|
constructor(input?: { deps?: HostRuntimeControllerDeps }) {
|
||||||
deps?: HostRuntimeControllerDeps;
|
|
||||||
}) {
|
|
||||||
this.deps = input?.deps ?? createDefaultDeps();
|
this.deps = input?.deps ?? createDefaultDeps();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -230,11 +230,11 @@ export function WorkspaceDraftAgentTab({
|
|||||||
|
|
||||||
const isReadyForPendingAutoSubmit = Boolean(
|
const isReadyForPendingAutoSubmit = Boolean(
|
||||||
pendingAutoSubmit &&
|
pendingAutoSubmit &&
|
||||||
draftInput.isHydrated &&
|
draftInput.isHydrated &&
|
||||||
workspaceDirectory &&
|
workspaceDirectory &&
|
||||||
client &&
|
client &&
|
||||||
!isSubmitting &&
|
!isSubmitting &&
|
||||||
!composerState.isModelLoading,
|
!composerState.isModelLoading,
|
||||||
);
|
);
|
||||||
const autoSubmitKeyRef = useRef<string | null>(null);
|
const autoSubmitKeyRef = useRef<string | null>(null);
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -36,10 +36,10 @@ export function hasMeaningfulToolCallDetail(detail: ToolCallDetail | undefined):
|
|||||||
case "search":
|
case "search":
|
||||||
return Boolean(
|
return Boolean(
|
||||||
detail.query.trim().length > 0 ||
|
detail.query.trim().length > 0 ||
|
||||||
detail.content ||
|
detail.content ||
|
||||||
(detail.filePaths && detail.filePaths.length > 0) ||
|
(detail.filePaths && detail.filePaths.length > 0) ||
|
||||||
(detail.webResults && detail.webResults.length > 0) ||
|
(detail.webResults && detail.webResults.length > 0) ||
|
||||||
(detail.annotations && detail.annotations.length > 0),
|
(detail.annotations && detail.annotations.length > 0),
|
||||||
);
|
);
|
||||||
case "fetch":
|
case "fetch":
|
||||||
return Boolean(detail.url || detail.result || detail.codeText);
|
return Boolean(detail.url || detail.result || detail.codeText);
|
||||||
|
|||||||
@@ -2,17 +2,17 @@
|
|||||||
"name": "@getpaseo/cli",
|
"name": "@getpaseo/cli",
|
||||||
"version": "0.1.62",
|
"version": "0.1.62",
|
||||||
"description": "Paseo CLI - control your AI coding agents from the command line",
|
"description": "Paseo CLI - control your AI coding agents from the command line",
|
||||||
"type": "module",
|
"bin": {
|
||||||
|
"paseo": "bin/paseo"
|
||||||
|
},
|
||||||
"files": [
|
"files": [
|
||||||
"bin",
|
"bin",
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
|
"type": "module",
|
||||||
"publishConfig": {
|
"publishConfig": {
|
||||||
"access": "public"
|
"access": "public"
|
||||||
},
|
},
|
||||||
"bin": {
|
|
||||||
"paseo": "bin/paseo"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node -e \"require('node:fs').rmSync('dist',{ recursive: true, force: true })\" && tsc -p tsconfig.json --incremental false",
|
"build": "node -e \"require('node:fs').rmSync('dist',{ recursive: true, force: true })\" && tsc -p tsconfig.json --incremental false",
|
||||||
"prepack": "npm run build",
|
"prepack": "npm run build",
|
||||||
|
|||||||
@@ -3,6 +3,16 @@
|
|||||||
"version": "0.1.62",
|
"version": "0.1.62",
|
||||||
"private": true,
|
"private": true,
|
||||||
"description": "Paseo desktop app (Electron wrapper)",
|
"description": "Paseo desktop app (Electron wrapper)",
|
||||||
|
"homepage": "https://paseo.sh",
|
||||||
|
"license": "AGPL-3.0-or-later",
|
||||||
|
"author": {
|
||||||
|
"name": "Mohamed Boudra",
|
||||||
|
"email": "hello@moboudra.com"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/getpaseo/paseo.git"
|
||||||
|
},
|
||||||
"main": "dist/main.js",
|
"main": "dist/main.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "npm --prefix ../.. run build:daemon && npm run build:main && electron-builder --config electron-builder.yml",
|
"build": "npm --prefix ../.. run build:daemon && npm run build:main && electron-builder --config electron-builder.yml",
|
||||||
@@ -27,15 +37,5 @@
|
|||||||
"typescript": "5.9.3",
|
"typescript": "5.9.3",
|
||||||
"unzip-crx-3": "^0.2.0",
|
"unzip-crx-3": "^0.2.0",
|
||||||
"wait-on": "8.0.5"
|
"wait-on": "8.0.5"
|
||||||
},
|
}
|
||||||
"homepage": "https://paseo.sh",
|
|
||||||
"repository": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://github.com/getpaseo/paseo.git"
|
|
||||||
},
|
|
||||||
"author": {
|
|
||||||
"name": "Mohamed Boudra",
|
|
||||||
"email": "hello@moboudra.com"
|
|
||||||
},
|
|
||||||
"license": "AGPL-3.0-or-later"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,23 +17,23 @@ diverse, inclusive, and healthy community.
|
|||||||
Examples of behavior that contributes to a positive environment for our
|
Examples of behavior that contributes to a positive environment for our
|
||||||
community include:
|
community include:
|
||||||
|
|
||||||
* Demonstrating empathy and kindness toward other people
|
- Demonstrating empathy and kindness toward other people
|
||||||
* Being respectful of differing opinions, viewpoints, and experiences
|
- Being respectful of differing opinions, viewpoints, and experiences
|
||||||
* Giving and gracefully accepting constructive feedback
|
- Giving and gracefully accepting constructive feedback
|
||||||
* Accepting responsibility and apologizing to those affected by our mistakes,
|
- Accepting responsibility and apologizing to those affected by our mistakes,
|
||||||
and learning from the experience
|
and learning from the experience
|
||||||
* Focusing on what is best not just for us as individuals, but for the
|
- Focusing on what is best not just for us as individuals, but for the
|
||||||
overall community
|
overall community
|
||||||
|
|
||||||
Examples of unacceptable behavior include:
|
Examples of unacceptable behavior include:
|
||||||
|
|
||||||
* The use of sexualized language or imagery, and sexual attention or
|
- The use of sexualized language or imagery, and sexual attention or
|
||||||
advances of any kind
|
advances of any kind
|
||||||
* Trolling, insulting or derogatory comments, and personal or political attacks
|
- Trolling, insulting or derogatory comments, and personal or political attacks
|
||||||
* Public or private harassment
|
- Public or private harassment
|
||||||
* Publishing others' private information, such as a physical or email
|
- Publishing others' private information, such as a physical or email
|
||||||
address, without their explicit permission
|
address, without their explicit permission
|
||||||
* Other conduct which could reasonably be considered inappropriate in a
|
- Other conduct which could reasonably be considered inappropriate in a
|
||||||
professional setting
|
professional setting
|
||||||
|
|
||||||
## Enforcement Responsibilities
|
## Enforcement Responsibilities
|
||||||
@@ -106,7 +106,7 @@ Violating these terms may lead to a permanent ban.
|
|||||||
### 4. Permanent Ban
|
### 4. Permanent Ban
|
||||||
|
|
||||||
**Community Impact**: Demonstrating a pattern of violation of community
|
**Community Impact**: Demonstrating a pattern of violation of community
|
||||||
standards, including sustained inappropriate behavior, harassment of an
|
standards, including sustained inappropriate behavior, harassment of an
|
||||||
individual, or aggression toward or disparagement of classes of individuals.
|
individual, or aggression toward or disparagement of classes of individuals.
|
||||||
|
|
||||||
**Consequence**: A permanent ban from any sort of public interaction within
|
**Consequence**: A permanent ban from any sort of public interaction within
|
||||||
@@ -125,4 +125,4 @@ enforcement ladder](https://github.com/mozilla/diversity).
|
|||||||
|
|
||||||
For answers to common questions about this code of conduct, see the FAQ at
|
For answers to common questions about this code of conduct, see the FAQ at
|
||||||
https://www.contributor-covenant.org/faq. Translations are available at
|
https://www.contributor-covenant.org/faq. Translations are available at
|
||||||
https://www.contributor-covenant.org/translations.
|
https://www.contributor-covenant.org/translations.
|
||||||
|
|||||||
@@ -18,18 +18,20 @@ This example showcases the basic functions of the expo-two-way-audio module:
|
|||||||
In the `examples/basic-usage` directory:
|
In the `examples/basic-usage` directory:
|
||||||
|
|
||||||
1. Install dependencies:
|
1. Install dependencies:
|
||||||
```sh
|
|
||||||
npm install
|
```sh
|
||||||
```
|
npm install
|
||||||
|
```
|
||||||
|
|
||||||
2. Run the app:
|
2. Run the app:
|
||||||
```sh
|
|
||||||
# Run iOS
|
|
||||||
npm run ios
|
|
||||||
|
|
||||||
# Run Android
|
```sh
|
||||||
npm run android
|
# Run iOS
|
||||||
```
|
npm run ios
|
||||||
|
|
||||||
|
# Run Android
|
||||||
|
npm run android
|
||||||
|
```
|
||||||
|
|
||||||
## Notes
|
## Notes
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "expo-two-way-audio-example",
|
"name": "expo-two-way-audio-example",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "expo/AppEntry.js",
|
"main": "expo/AppEntry.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "expo start",
|
"start": "expo start",
|
||||||
@@ -11,17 +12,16 @@
|
|||||||
"dependencies": {
|
"dependencies": {
|
||||||
"buffer": "^6.0.3",
|
"buffer": "^6.0.3",
|
||||||
"expo": "^52.0.0",
|
"expo": "^52.0.0",
|
||||||
"react": "18.3.1",
|
"expo-build-properties": "~0.13.1",
|
||||||
"react-native": "0.76.2",
|
|
||||||
"expo-dev-client": "~5.0.3",
|
"expo-dev-client": "~5.0.3",
|
||||||
"expo-build-properties": "~0.13.1"
|
"react": "18.3.1",
|
||||||
|
"react-native": "0.76.2"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@babel/core": "^7.20.0",
|
"@babel/core": "^7.20.0",
|
||||||
"@types/react": "~18.3.12",
|
"@types/react": "~18.3.12",
|
||||||
"typescript": "^5.1.3"
|
"typescript": "^5.1.3"
|
||||||
},
|
},
|
||||||
"private": true,
|
|
||||||
"expo": {
|
"expo": {
|
||||||
"autolinking": {
|
"autolinking": {
|
||||||
"nativeModulesDir": "../.."
|
"nativeModulesDir": "../.."
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"name": "expo-two-way-audio-flow-example",
|
"name": "expo-two-way-audio-flow-example",
|
||||||
"version": "1.0.0",
|
"version": "1.0.0",
|
||||||
|
"private": true,
|
||||||
"main": "expo/AppEntry.js",
|
"main": "expo/AppEntry.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"start": "expo start",
|
"start": "expo start",
|
||||||
@@ -25,7 +26,6 @@
|
|||||||
"@types/react": "~18.3.12",
|
"@types/react": "~18.3.12",
|
||||||
"typescript": "^5.1.3"
|
"typescript": "^5.1.3"
|
||||||
},
|
},
|
||||||
"private": true,
|
|
||||||
"expo": {
|
"expo": {
|
||||||
"autolinking": {
|
"autolinking": {
|
||||||
"nativeModulesDir": "../.."
|
"nativeModulesDir": "../.."
|
||||||
|
|||||||
@@ -1,10 +1,10 @@
|
|||||||
{
|
{
|
||||||
"name": "@getpaseo/highlight",
|
"name": "@getpaseo/highlight",
|
||||||
"version": "0.1.62",
|
"version": "0.1.62",
|
||||||
|
"files": [
|
||||||
|
"dist"
|
||||||
|
],
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
},
|
|
||||||
"main": "./dist/index.js",
|
"main": "./dist/index.js",
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
@@ -14,9 +14,9 @@
|
|||||||
"default": "./dist/index.js"
|
"default": "./dist/index.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"files": [
|
"publishConfig": {
|
||||||
"dist"
|
"access": "public"
|
||||||
],
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node -e \"require('node:fs').rmSync('dist',{ recursive: true, force: true })\" && tsc -p tsconfig.json --incremental false",
|
"build": "node -e \"require('node:fs').rmSync('dist',{ recursive: true, force: true })\" && tsc -p tsconfig.json --incremental false",
|
||||||
"prepack": "npm run build",
|
"prepack": "npm run build",
|
||||||
|
|||||||
@@ -2,14 +2,11 @@
|
|||||||
"name": "@getpaseo/relay",
|
"name": "@getpaseo/relay",
|
||||||
"version": "0.1.62",
|
"version": "0.1.62",
|
||||||
"description": "Paseo relay for bridging daemon and client connections",
|
"description": "Paseo relay for bridging daemon and client connections",
|
||||||
"type": "module",
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
},
|
|
||||||
"types": "./dist/index.d.ts",
|
|
||||||
"files": [
|
"files": [
|
||||||
"dist"
|
"dist"
|
||||||
],
|
],
|
||||||
|
"type": "module",
|
||||||
|
"types": "./dist/index.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"types": "./dist/index.d.ts",
|
"types": "./dist/index.d.ts",
|
||||||
@@ -30,6 +27,9 @@
|
|||||||
"default": "./src/cloudflare-adapter.ts"
|
"default": "./src/cloudflare-adapter.ts"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "node -e \"require('node:fs').rmSync('dist',{ recursive: true, force: true })\" && tsc -p tsconfig.json --incremental false",
|
"build": "node -e \"require('node:fs').rmSync('dist',{ recursive: true, force: true })\" && tsc -p tsconfig.json --incremental false",
|
||||||
"prepack": "npm run build",
|
"prepack": "npm run build",
|
||||||
|
|||||||
+146
-139
@@ -199,169 +199,176 @@ async function stopRelayProcess(relayProcess: ChildProcess): Promise<void> {
|
|||||||
}
|
}
|
||||||
}, SHUTDOWN_TIMEOUT_MS);
|
}, SHUTDOWN_TIMEOUT_MS);
|
||||||
|
|
||||||
it("full flow: daemon and client exchange encrypted messages through relay", {
|
it(
|
||||||
timeout: 90_000,
|
"full flow: daemon and client exchange encrypted messages through relay",
|
||||||
}, async () => {
|
{
|
||||||
const serverId = "test-session-" + Date.now();
|
timeout: 90_000,
|
||||||
const connectionId = "clt_test_" + Date.now() + "_" + Math.random().toString(36).slice(2);
|
},
|
||||||
|
async () => {
|
||||||
|
const serverId = "test-session-" + Date.now();
|
||||||
|
const connectionId = "clt_test_" + Date.now() + "_" + Math.random().toString(36).slice(2);
|
||||||
|
|
||||||
// === DAEMON SIDE ===
|
// === DAEMON SIDE ===
|
||||||
// Generate keypair (public key goes in QR)
|
// Generate keypair (public key goes in QR)
|
||||||
const daemonKeyPair = await generateKeyPair();
|
const daemonKeyPair = await generateKeyPair();
|
||||||
const daemonPubKeyB64 = await exportPublicKey(daemonKeyPair.publicKey);
|
const daemonPubKeyB64 = await exportPublicKey(daemonKeyPair.publicKey);
|
||||||
|
|
||||||
// QR would contain: { serverId, daemonPubKeyB64, relay: { endpoint } }
|
// QR would contain: { serverId, daemonPubKeyB64, relay: { endpoint } }
|
||||||
|
|
||||||
// Daemon connects to relay as "server" control role
|
// Daemon connects to relay as "server" control role
|
||||||
const daemonControlWs = new WebSocket(
|
const daemonControlWs = new WebSocket(
|
||||||
`ws://127.0.0.1:${relayPort}/ws?serverId=${serverId}&role=server&v=2`,
|
`ws://127.0.0.1:${relayPort}/ws?serverId=${serverId}&role=server&v=2`,
|
||||||
);
|
);
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
daemonControlWs.on("open", resolve);
|
daemonControlWs.on("open", resolve);
|
||||||
daemonControlWs.on("error", reject);
|
daemonControlWs.on("error", reject);
|
||||||
});
|
});
|
||||||
|
|
||||||
// === CLIENT SIDE ===
|
// === CLIENT SIDE ===
|
||||||
// Client scans QR, gets daemon's public key and session ID
|
// Client scans QR, gets daemon's public key and session ID
|
||||||
// Client generates own keypair
|
// Client generates own keypair
|
||||||
const clientKeyPair = await generateKeyPair();
|
const clientKeyPair = await generateKeyPair();
|
||||||
const clientPubKeyB64 = await exportPublicKey(clientKeyPair.publicKey);
|
const clientPubKeyB64 = await exportPublicKey(clientKeyPair.publicKey);
|
||||||
|
|
||||||
// Client imports daemon's public key and derives shared secret
|
// Client imports daemon's public key and derives shared secret
|
||||||
const daemonPubKeyOnClient = await importPublicKey(daemonPubKeyB64);
|
const daemonPubKeyOnClient = await importPublicKey(daemonPubKeyB64);
|
||||||
const clientSharedKey = await deriveSharedKey(clientKeyPair.secretKey, daemonPubKeyOnClient);
|
const clientSharedKey = await deriveSharedKey(clientKeyPair.secretKey, daemonPubKeyOnClient);
|
||||||
|
|
||||||
const waitForClientSeen = new Promise<void>((resolve, reject) => {
|
const waitForClientSeen = new Promise<void>((resolve, reject) => {
|
||||||
const timeout = setTimeout(() => reject(new Error("timed out waiting for connected")), 5000);
|
const timeout = setTimeout(
|
||||||
const onMessage = (raw: unknown) => {
|
() => reject(new Error("timed out waiting for connected")),
|
||||||
try {
|
5000,
|
||||||
const text =
|
);
|
||||||
typeof raw === "string"
|
const onMessage = (raw: unknown) => {
|
||||||
? raw
|
try {
|
||||||
: raw && typeof (raw as any).toString === "function"
|
const text =
|
||||||
? (raw as any).toString()
|
typeof raw === "string"
|
||||||
: "";
|
? raw
|
||||||
const msg = JSON.parse(text);
|
: raw && typeof (raw as any).toString === "function"
|
||||||
if (msg?.type === "connected" && msg.connectionId === connectionId) {
|
? (raw as any).toString()
|
||||||
clearTimeout(timeout);
|
: "";
|
||||||
daemonControlWs.off("message", onMessage);
|
const msg = JSON.parse(text);
|
||||||
resolve();
|
if (msg?.type === "connected" && msg.connectionId === connectionId) {
|
||||||
return;
|
clearTimeout(timeout);
|
||||||
|
daemonControlWs.off("message", onMessage);
|
||||||
|
resolve();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (
|
||||||
|
msg?.type === "sync" &&
|
||||||
|
Array.isArray(msg.connectionIds) &&
|
||||||
|
msg.connectionIds.includes(connectionId)
|
||||||
|
) {
|
||||||
|
clearTimeout(timeout);
|
||||||
|
daemonControlWs.off("message", onMessage);
|
||||||
|
resolve();
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
// ignore
|
||||||
}
|
}
|
||||||
if (
|
};
|
||||||
msg?.type === "sync" &&
|
daemonControlWs.on("message", onMessage);
|
||||||
Array.isArray(msg.connectionIds) &&
|
});
|
||||||
msg.connectionIds.includes(connectionId)
|
|
||||||
) {
|
|
||||||
clearTimeout(timeout);
|
|
||||||
daemonControlWs.off("message", onMessage);
|
|
||||||
resolve();
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// ignore
|
|
||||||
}
|
|
||||||
};
|
|
||||||
daemonControlWs.on("message", onMessage);
|
|
||||||
});
|
|
||||||
|
|
||||||
// Client connects to relay as "client" role (must include connectionId)
|
// Client connects to relay as "client" role (must include connectionId)
|
||||||
const clientWs = new WebSocket(
|
const clientWs = new WebSocket(
|
||||||
`ws://127.0.0.1:${relayPort}/ws?serverId=${serverId}&role=client&connectionId=${connectionId}&v=2`,
|
`ws://127.0.0.1:${relayPort}/ws?serverId=${serverId}&role=client&connectionId=${connectionId}&v=2`,
|
||||||
);
|
);
|
||||||
|
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
clientWs.on("open", resolve);
|
clientWs.on("open", resolve);
|
||||||
clientWs.on("error", reject);
|
clientWs.on("error", reject);
|
||||||
});
|
});
|
||||||
|
|
||||||
await waitForClientSeen;
|
await waitForClientSeen;
|
||||||
|
|
||||||
const daemonWs = new WebSocket(
|
const daemonWs = new WebSocket(
|
||||||
`ws://127.0.0.1:${relayPort}/ws?serverId=${serverId}&role=server&connectionId=${connectionId}&v=2`,
|
`ws://127.0.0.1:${relayPort}/ws?serverId=${serverId}&role=server&connectionId=${connectionId}&v=2`,
|
||||||
);
|
);
|
||||||
await new Promise<void>((resolve, reject) => {
|
await new Promise<void>((resolve, reject) => {
|
||||||
daemonWs.on("open", resolve);
|
daemonWs.on("open", resolve);
|
||||||
daemonWs.on("error", reject);
|
daemonWs.on("error", reject);
|
||||||
});
|
});
|
||||||
|
|
||||||
// Client sends hello with its public key (this message is NOT encrypted - it's the handshake)
|
// Client sends hello with its public key (this message is NOT encrypted - it's the handshake)
|
||||||
const helloMsg = JSON.stringify({ type: "hello", key: clientPubKeyB64 });
|
const helloMsg = JSON.stringify({ type: "hello", key: clientPubKeyB64 });
|
||||||
clientWs.send(helloMsg);
|
clientWs.send(helloMsg);
|
||||||
|
|
||||||
// === DAEMON RECEIVES HELLO ===
|
// === DAEMON RECEIVES HELLO ===
|
||||||
const daemonReceivedHello = await new Promise<string>((resolve) => {
|
const daemonReceivedHello = await new Promise<string>((resolve) => {
|
||||||
daemonWs.once("message", (data) => resolve(data.toString()));
|
daemonWs.once("message", (data) => resolve(data.toString()));
|
||||||
});
|
});
|
||||||
|
|
||||||
const hello = JSON.parse(daemonReceivedHello);
|
const hello = JSON.parse(daemonReceivedHello);
|
||||||
expect(hello.type).toBe("hello");
|
expect(hello.type).toBe("hello");
|
||||||
expect(hello.key).toBe(clientPubKeyB64);
|
expect(hello.key).toBe(clientPubKeyB64);
|
||||||
|
|
||||||
// Daemon imports client's public key and derives shared secret
|
// Daemon imports client's public key and derives shared secret
|
||||||
const clientPubKeyOnDaemon = await importPublicKey(hello.key);
|
const clientPubKeyOnDaemon = await importPublicKey(hello.key);
|
||||||
const daemonSharedKey = await deriveSharedKey(daemonKeyPair.secretKey, clientPubKeyOnDaemon);
|
const daemonSharedKey = await deriveSharedKey(daemonKeyPair.secretKey, clientPubKeyOnDaemon);
|
||||||
|
|
||||||
// === VERIFY BOTH HAVE SAME KEY - Exchange encrypted messages ===
|
// === VERIFY BOTH HAVE SAME KEY - Exchange encrypted messages ===
|
||||||
|
|
||||||
// Daemon sends encrypted "ready" message
|
// Daemon sends encrypted "ready" message
|
||||||
const readyPlaintext = JSON.stringify({ type: "ready" });
|
const readyPlaintext = JSON.stringify({ type: "ready" });
|
||||||
const readyCiphertext = await encrypt(daemonSharedKey, readyPlaintext);
|
const readyCiphertext = await encrypt(daemonSharedKey, readyPlaintext);
|
||||||
daemonWs.send(Buffer.from(readyCiphertext));
|
daemonWs.send(Buffer.from(readyCiphertext));
|
||||||
|
|
||||||
// Client receives and decrypts
|
// Client receives and decrypts
|
||||||
const clientReceivedReady = await new Promise<Buffer>((resolve) => {
|
const clientReceivedReady = await new Promise<Buffer>((resolve) => {
|
||||||
clientWs.once("message", (data) => resolve(data as Buffer));
|
clientWs.once("message", (data) => resolve(data as Buffer));
|
||||||
});
|
});
|
||||||
const decryptedReady = await decrypt(
|
const decryptedReady = await decrypt(
|
||||||
clientSharedKey,
|
clientSharedKey,
|
||||||
clientReceivedReady.buffer.slice(
|
clientReceivedReady.buffer.slice(
|
||||||
clientReceivedReady.byteOffset,
|
clientReceivedReady.byteOffset,
|
||||||
clientReceivedReady.byteOffset + clientReceivedReady.byteLength,
|
clientReceivedReady.byteOffset + clientReceivedReady.byteLength,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(JSON.parse(decryptedReady as string)).toEqual({ type: "ready" });
|
expect(JSON.parse(decryptedReady as string)).toEqual({ type: "ready" });
|
||||||
|
|
||||||
// Client sends encrypted message
|
// Client sends encrypted message
|
||||||
const clientMessage = "Hello from client!";
|
const clientMessage = "Hello from client!";
|
||||||
const clientCiphertext = await encrypt(clientSharedKey, clientMessage);
|
const clientCiphertext = await encrypt(clientSharedKey, clientMessage);
|
||||||
clientWs.send(Buffer.from(clientCiphertext));
|
clientWs.send(Buffer.from(clientCiphertext));
|
||||||
|
|
||||||
// Daemon receives and decrypts
|
// Daemon receives and decrypts
|
||||||
const daemonReceivedMsg = await new Promise<Buffer>((resolve) => {
|
const daemonReceivedMsg = await new Promise<Buffer>((resolve) => {
|
||||||
daemonWs.once("message", (data) => resolve(data as Buffer));
|
daemonWs.once("message", (data) => resolve(data as Buffer));
|
||||||
});
|
});
|
||||||
const decryptedClientMsg = await decrypt(
|
const decryptedClientMsg = await decrypt(
|
||||||
daemonSharedKey,
|
daemonSharedKey,
|
||||||
daemonReceivedMsg.buffer.slice(
|
daemonReceivedMsg.buffer.slice(
|
||||||
daemonReceivedMsg.byteOffset,
|
daemonReceivedMsg.byteOffset,
|
||||||
daemonReceivedMsg.byteOffset + daemonReceivedMsg.byteLength,
|
daemonReceivedMsg.byteOffset + daemonReceivedMsg.byteLength,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(decryptedClientMsg).toBe(clientMessage);
|
expect(decryptedClientMsg).toBe(clientMessage);
|
||||||
|
|
||||||
// Daemon sends encrypted response
|
// Daemon sends encrypted response
|
||||||
const daemonMessage = "Hello from daemon!";
|
const daemonMessage = "Hello from daemon!";
|
||||||
const daemonCiphertext = await encrypt(daemonSharedKey, daemonMessage);
|
const daemonCiphertext = await encrypt(daemonSharedKey, daemonMessage);
|
||||||
daemonWs.send(Buffer.from(daemonCiphertext));
|
daemonWs.send(Buffer.from(daemonCiphertext));
|
||||||
|
|
||||||
// Client receives and decrypts
|
// Client receives and decrypts
|
||||||
const clientReceivedMsg = await new Promise<Buffer>((resolve) => {
|
const clientReceivedMsg = await new Promise<Buffer>((resolve) => {
|
||||||
clientWs.once("message", (data) => resolve(data as Buffer));
|
clientWs.once("message", (data) => resolve(data as Buffer));
|
||||||
});
|
});
|
||||||
const decryptedDaemonMsg = await decrypt(
|
const decryptedDaemonMsg = await decrypt(
|
||||||
clientSharedKey,
|
clientSharedKey,
|
||||||
clientReceivedMsg.buffer.slice(
|
clientReceivedMsg.buffer.slice(
|
||||||
clientReceivedMsg.byteOffset,
|
clientReceivedMsg.byteOffset,
|
||||||
clientReceivedMsg.byteOffset + clientReceivedMsg.byteLength,
|
clientReceivedMsg.byteOffset + clientReceivedMsg.byteLength,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
expect(decryptedDaemonMsg).toBe(daemonMessage);
|
expect(decryptedDaemonMsg).toBe(daemonMessage);
|
||||||
|
|
||||||
// Cleanup
|
// Cleanup
|
||||||
daemonWs.close();
|
daemonWs.close();
|
||||||
clientWs.close();
|
clientWs.close();
|
||||||
});
|
},
|
||||||
|
);
|
||||||
|
|
||||||
it("relay only sees opaque bytes after handshake", { timeout: 90_000 }, async () => {
|
it("relay only sees opaque bytes after handshake", { timeout: 90_000 }, async () => {
|
||||||
const serverId = "opaque-test-" + Date.now();
|
const serverId = "opaque-test-" + Date.now();
|
||||||
|
|||||||
@@ -1,8 +1,4 @@
|
|||||||
export {
|
export { createClientChannel, createDaemonChannel, EncryptedChannel } from "./encrypted-channel.js";
|
||||||
createClientChannel,
|
|
||||||
createDaemonChannel,
|
|
||||||
EncryptedChannel,
|
|
||||||
} from "./encrypted-channel.js";
|
|
||||||
export type { Transport, EncryptedChannelEvents } from "./encrypted-channel.js";
|
export type { Transport, EncryptedChannelEvents } from "./encrypted-channel.js";
|
||||||
|
|
||||||
export {
|
export {
|
||||||
|
|||||||
@@ -1,7 +1,4 @@
|
|||||||
export type {
|
export type { ConnectionRole, RelaySessionAttachment } from "./types.js";
|
||||||
ConnectionRole,
|
|
||||||
RelaySessionAttachment,
|
|
||||||
} from "./types.js";
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
generateKeyPair,
|
generateKeyPair,
|
||||||
@@ -12,9 +9,5 @@ export {
|
|||||||
decrypt,
|
decrypt,
|
||||||
} from "./crypto.js";
|
} from "./crypto.js";
|
||||||
|
|
||||||
export {
|
export { createClientChannel, createDaemonChannel, EncryptedChannel } from "./encrypted-channel.js";
|
||||||
createClientChannel,
|
|
||||||
createDaemonChannel,
|
|
||||||
EncryptedChannel,
|
|
||||||
} from "./encrypted-channel.js";
|
|
||||||
export type { Transport, EncryptedChannelEvents } from "./encrypted-channel.js";
|
export type { Transport, EncryptedChannelEvents } from "./encrypted-channel.js";
|
||||||
|
|||||||
@@ -3,9 +3,7 @@ account_id = "10ed39a1dbf316e30abd0c409bed40d6"
|
|||||||
main = "src/cloudflare-adapter.ts"
|
main = "src/cloudflare-adapter.ts"
|
||||||
compatibility_date = "2024-12-01"
|
compatibility_date = "2024-12-01"
|
||||||
|
|
||||||
routes = [
|
routes = [{ pattern = "relay.paseo.sh", custom_domain = true }]
|
||||||
{ pattern = "relay.paseo.sh", custom_domain = true }
|
|
||||||
]
|
|
||||||
|
|
||||||
[observability]
|
[observability]
|
||||||
enabled = true
|
enabled = true
|
||||||
|
|||||||
+31
-10
@@ -11,6 +11,7 @@ Paseo is a mobile + CLI app for monitoring and controlling local AI coding agent
|
|||||||
## Build / Lint / Test Commands
|
## Build / Lint / Test Commands
|
||||||
|
|
||||||
### Root (monorepo)
|
### Root (monorepo)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run dev # Start daemon + Expo in Tmux
|
npm run dev # Start daemon + Expo in Tmux
|
||||||
npm run build:daemon # Build: highlight + relay + server + cli
|
npm run build:daemon # Build: highlight + relay + server + cli
|
||||||
@@ -20,6 +21,7 @@ npm run format # Format with Biome (in-place)
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Server package (`packages/server`)
|
### Server package (`packages/server`)
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run dev # Start dev daemon (tsx watch)
|
npm run dev # Start dev daemon (tsx watch)
|
||||||
npm run build # Build lib + scripts to dist/
|
npm run build # Build lib + scripts to dist/
|
||||||
@@ -45,6 +47,7 @@ npm run test:ui # Vitest UI at localhost:51204
|
|||||||
```
|
```
|
||||||
|
|
||||||
### Other useful commands
|
### Other useful commands
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm run build --workspace=@getpaseo/relay # Rebuild relay before daemon
|
npm run build --workspace=@getpaseo/relay # Rebuild relay before daemon
|
||||||
npm run build --workspace=@getpaseo/server # Rebuild server
|
npm run build --workspace=@getpaseo/server # Rebuild server
|
||||||
@@ -58,13 +61,22 @@ npm run cli -- daemon status # Check daemon status
|
|||||||
## Code Style
|
## Code Style
|
||||||
|
|
||||||
### Biome (formatting only, no linting)
|
### Biome (formatting only, no linting)
|
||||||
|
|
||||||
```json
|
```json
|
||||||
{ "indentStyle": "space", "indentWidth": 2, "lineWidth": 100, "quoteStyle": "double", "trailingCommas": "all", "semicolons": "always" }
|
{
|
||||||
|
"indentStyle": "space",
|
||||||
|
"indentWidth": 2,
|
||||||
|
"lineWidth": 100,
|
||||||
|
"quoteStyle": "double",
|
||||||
|
"trailingCommas": "all",
|
||||||
|
"semicolons": "always"
|
||||||
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
### TypeScript
|
### TypeScript
|
||||||
|
|
||||||
- **Fully strict** — no `any`, no implicit `any`
|
- **Fully strict** — no `any`, no implicit `any`
|
||||||
- **`interface`** over `type`** when possible
|
- **`interface`** over `type`\*\* when possible
|
||||||
- **`function` declarations** over arrow function assignments
|
- **`function` declarations** over arrow function assignments
|
||||||
- **Named types** — no complex inline types in public signatures
|
- **Named types** — no complex inline types in public signatures
|
||||||
- **Object parameters** — use single object param when >1 argument
|
- **Object parameters** — use single object param when >1 argument
|
||||||
@@ -72,15 +84,18 @@ npm run cli -- daemon status # Check daemon status
|
|||||||
- `noUnusedLocals: true`, `noUnusedParameters: true`, `noFallthroughCasesInSwitch: true`
|
- `noUnusedLocals: true`, `noUnusedParameters: true`, `noFallthroughCasesInSwitch: true`
|
||||||
|
|
||||||
### Imports
|
### Imports
|
||||||
|
|
||||||
- Use path alias `@server/*` in server package (maps to `./src/`)
|
- Use path alias `@server/*` in server package (maps to `./src/`)
|
||||||
- No barrel `index.ts` re-exports — they create unnecessary indirection
|
- No barrel `index.ts` re-exports — they create unnecessary indirection
|
||||||
|
|
||||||
### Naming
|
### Naming
|
||||||
|
|
||||||
- Files: `kebab-case.ts` named after the main export (`create-tool-call.ts`)
|
- Files: `kebab-case.ts` named after the main export (`create-tool-call.ts`)
|
||||||
- Tests: collocated with implementation (`thing.test.ts`)
|
- Tests: collocated with implementation (`thing.test.ts`)
|
||||||
- No prefixes like `RpcX`, `DbX`, `UiX` — keep one canonical type per concept
|
- No prefixes like `RpcX`, `DbX`, `UiX` — keep one canonical type per concept
|
||||||
|
|
||||||
### Error Handling
|
### Error Handling
|
||||||
|
|
||||||
- **Fail explicitly** — throw instead of silently returning defaults
|
- **Fail explicitly** — throw instead of silently returning defaults
|
||||||
- **Typed domain errors** — extend `Error` with structured metadata
|
- **Typed domain errors** — extend `Error` with structured metadata
|
||||||
|
|
||||||
@@ -97,10 +112,16 @@ class TimeoutError extends Error {
|
|||||||
```
|
```
|
||||||
|
|
||||||
### State Design
|
### State Design
|
||||||
|
|
||||||
Discriminated unions over bags of booleans/optionals:
|
Discriminated unions over bags of booleans/optionals:
|
||||||
|
|
||||||
```typescript
|
```typescript
|
||||||
// Bad
|
// Bad
|
||||||
interface FetchState { isLoading: boolean; error?: Error; data?: Data }
|
interface FetchState {
|
||||||
|
isLoading: boolean;
|
||||||
|
error?: Error;
|
||||||
|
data?: Data;
|
||||||
|
}
|
||||||
|
|
||||||
// Good
|
// Good
|
||||||
type FetchState =
|
type FetchState =
|
||||||
@@ -168,10 +189,10 @@ npm run db:query -- "SELECT * FROM agent_timeline_rows..."
|
|||||||
|
|
||||||
## Relevant Docs
|
## Relevant Docs
|
||||||
|
|
||||||
| File | What it covers |
|
| File | What it covers |
|
||||||
|---|---|
|
| ---------------------------------------------------------- | ------------------------------------------------ |
|
||||||
| [../CLAUDE.md](../CLAUDE.md) | Repository overview, critical rules, quick start |
|
| [../CLAUDE.md](../CLAUDE.md) | Repository overview, critical rules, quick start |
|
||||||
| [../docs/ARCHITECTURE.md](../docs/ARCHITECTURE.md) | System design, WebSocket protocol, data flow |
|
| [../docs/ARCHITECTURE.md](../docs/ARCHITECTURE.md) | System design, WebSocket protocol, data flow |
|
||||||
| [../docs/CODING_STANDARDS.md](../docs/CODING_STANDARDS.md) | Type hygiene, error handling, React patterns |
|
| [../docs/CODING_STANDARDS.md](../docs/CODING_STANDARDS.md) | Type hygiene, error handling, React patterns |
|
||||||
| [../docs/TESTING.md](../docs/TESTING.md) | TDD workflow, determinism, real deps over mocks |
|
| [../docs/TESTING.md](../docs/TESTING.md) | TDD workflow, determinism, real deps over mocks |
|
||||||
| [../SECURITY.md](../SECURITY.md) | Relay threat model, E2E encryption |
|
| [../SECURITY.md](../SECURITY.md) | Relay threat model, E2E encryption |
|
||||||
|
|||||||
@@ -49,15 +49,18 @@ npm start
|
|||||||
## Project Status
|
## Project Status
|
||||||
|
|
||||||
**✅ Completed** (Phases 1-2):
|
**✅ Completed** (Phases 1-2):
|
||||||
|
|
||||||
- Package setup and configuration
|
- Package setup and configuration
|
||||||
- Express server with WebSocket
|
- Express server with WebSocket
|
||||||
- React UI with Vite
|
- React UI with Vite
|
||||||
- WebSocket client with ping/pong testing
|
- WebSocket client with ping/pong testing
|
||||||
|
|
||||||
**⏳ In Progress** (Phase 3):
|
**⏳ In Progress** (Phase 3):
|
||||||
|
|
||||||
- Terminal control (tmux integration)
|
- Terminal control (tmux integration)
|
||||||
|
|
||||||
**📋 Planned** (Phases 4-9):
|
**📋 Planned** (Phases 4-9):
|
||||||
|
|
||||||
- LLM integration (OpenAI GPT-4)
|
- LLM integration (OpenAI GPT-4)
|
||||||
- Agent orchestrator
|
- Agent orchestrator
|
||||||
- Speech-to-Text (Deepgram)
|
- Speech-to-Text (Deepgram)
|
||||||
@@ -95,6 +98,7 @@ PASEO_HOME=~/.paseo-blue PASEO_LISTEN=127.0.0.1:7777 npm run dev
|
|||||||
## Testing
|
## Testing
|
||||||
|
|
||||||
Currently manual testing via:
|
Currently manual testing via:
|
||||||
|
|
||||||
1. Start servers: `npm run dev`
|
1. Start servers: `npm run dev`
|
||||||
2. Open http://localhost:5173
|
2. Open http://localhost:5173
|
||||||
3. Test WebSocket connection (green status indicator)
|
3. Test WebSocket connection (green status indicator)
|
||||||
|
|||||||
@@ -2,11 +2,6 @@
|
|||||||
"name": "@getpaseo/server",
|
"name": "@getpaseo/server",
|
||||||
"version": "0.1.62",
|
"version": "0.1.62",
|
||||||
"description": "Paseo backend server",
|
"description": "Paseo backend server",
|
||||||
"type": "module",
|
|
||||||
"publishConfig": {
|
|
||||||
"access": "public"
|
|
||||||
},
|
|
||||||
"types": "./dist/server/server/exports.d.ts",
|
|
||||||
"files": [
|
"files": [
|
||||||
"dist/server",
|
"dist/server",
|
||||||
"dist/src",
|
"dist/src",
|
||||||
@@ -15,6 +10,8 @@
|
|||||||
"README.md",
|
"README.md",
|
||||||
".env.example"
|
".env.example"
|
||||||
],
|
],
|
||||||
|
"type": "module",
|
||||||
|
"types": "./dist/server/server/exports.d.ts",
|
||||||
"exports": {
|
"exports": {
|
||||||
".": {
|
".": {
|
||||||
"types": "./dist/server/server/exports.d.ts",
|
"types": "./dist/server/server/exports.d.ts",
|
||||||
@@ -27,6 +24,9 @@
|
|||||||
"default": "./dist/server/utils/tool-call-parsers.js"
|
"default": "./dist/server/utils/tool-call-parsers.js"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"publishConfig": {
|
||||||
|
"access": "public"
|
||||||
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "cross-env NODE_ENV=development node --import tsx scripts/dev-runner.ts",
|
"dev": "cross-env NODE_ENV=development node --import tsx scripts/dev-runner.ts",
|
||||||
"dev:tsx": "cross-env NODE_ENV=development tsx watch --ignore '**/*.timestamp-*' src/server/index.ts",
|
"dev:tsx": "cross-env NODE_ENV=development tsx watch --ignore '**/*.timestamp-*' src/server/index.ts",
|
||||||
|
|||||||
@@ -232,11 +232,10 @@ export interface CreateAgentRequestOptions extends AgentConfigOverrides {
|
|||||||
labels?: Record<string, string>;
|
labels?: Record<string, string>;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface CreatePaseoWorktreeInput
|
export interface CreatePaseoWorktreeInput extends Pick<
|
||||||
extends Pick<
|
CreatePaseoWorktreeRequest,
|
||||||
CreatePaseoWorktreeRequest,
|
"cwd" | "worktreeSlug" | "attachments" | "refName" | "action" | "githubPrNumber"
|
||||||
"cwd" | "worktreeSlug" | "attachments" | "refName" | "action" | "githubPrNumber"
|
> {}
|
||||||
> {}
|
|
||||||
|
|
||||||
type CheckoutStatusPayload = CheckoutStatusResponse["payload"];
|
type CheckoutStatusPayload = CheckoutStatusResponse["payload"];
|
||||||
type SubscribeCheckoutDiffPayload = Extract<
|
type SubscribeCheckoutDiffPayload = Extract<
|
||||||
|
|||||||
@@ -130,9 +130,7 @@ function buildZodValidator<T>(schema: z.ZodTypeAny, schemaName: string): SchemaV
|
|||||||
|
|
||||||
function buildJsonSchemaValidator<T>(schema: JsonSchema): SchemaValidator<T> {
|
function buildJsonSchemaValidator<T>(schema: JsonSchema): SchemaValidator<T> {
|
||||||
const AjvConstructor = Ajv as unknown as {
|
const AjvConstructor = Ajv as unknown as {
|
||||||
new (
|
new (options?: AjvOptions): {
|
||||||
options?: AjvOptions,
|
|
||||||
): {
|
|
||||||
compile: (input: JsonSchema) => ((value: unknown) => boolean) & {
|
compile: (input: JsonSchema) => ((value: unknown) => boolean) & {
|
||||||
errors?: ErrorObject[] | null;
|
errors?: ErrorObject[] | null;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1203,9 +1203,7 @@ export async function createAgentMcpServer(options: AgentMcpServerOptions): Prom
|
|||||||
? (
|
? (
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
terminalManager.listDirectories().map(async (directory) =>
|
terminalManager.listDirectories().map(async (directory) =>
|
||||||
(
|
(await terminalManager.getTerminals(directory)).map((terminal) => ({
|
||||||
await terminalManager.getTerminals(directory)
|
|
||||||
).map((terminal) => ({
|
|
||||||
id: terminal.id,
|
id: terminal.id,
|
||||||
name: terminal.name,
|
name: terminal.name,
|
||||||
cwd: terminal.cwd,
|
cwd: terminal.cwd,
|
||||||
|
|||||||
@@ -8,4 +8,3 @@
|
|||||||
* This file is kept as a stub to document the migration.
|
* This file is kept as a stub to document the migration.
|
||||||
* It can be deleted once the refactoring is confirmed working.
|
* It can be deleted once the refactoring is confirmed working.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|||||||
@@ -49,9 +49,7 @@ export class ClaudeSidechainTracker {
|
|||||||
private readonly activeSidechains = new Map<string, SubAgentActivityState>();
|
private readonly activeSidechains = new Map<string, SubAgentActivityState>();
|
||||||
private readonly getToolInput: (toolUseId: string) => AgentMetadata | null | undefined;
|
private readonly getToolInput: (toolUseId: string) => AgentMetadata | null | undefined;
|
||||||
|
|
||||||
constructor(input: {
|
constructor(input: { getToolInput: (toolUseId: string) => AgentMetadata | null | undefined }) {
|
||||||
getToolInput: (toolUseId: string) => AgentMetadata | null | undefined;
|
|
||||||
}) {
|
|
||||||
this.getToolInput = input.getToolInput;
|
this.getToolInput = input.getToolInput;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -291,11 +291,7 @@ export class MockLoadTestAgentSession implements AgentSession {
|
|||||||
private modeId: string | null;
|
private modeId: string | null;
|
||||||
private modelId: string | null;
|
private modelId: string | null;
|
||||||
|
|
||||||
constructor(options: {
|
constructor(options: { config: AgentSessionConfig; sessionId: string; logger?: Logger }) {
|
||||||
config: AgentSessionConfig;
|
|
||||||
sessionId: string;
|
|
||||||
logger?: Logger;
|
|
||||||
}) {
|
|
||||||
this.id = options.sessionId;
|
this.id = options.sessionId;
|
||||||
this.logger = options.logger;
|
this.logger = options.logger;
|
||||||
this.modeId = options.config.modeId ?? MOCK_LOAD_TEST_MODE_ID;
|
this.modeId = options.config.modeId ?? MOCK_LOAD_TEST_MODE_ID;
|
||||||
|
|||||||
@@ -192,26 +192,23 @@ describe.runIf(process.platform === "win32")("Windows provider launch parity", (
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
test.each(
|
test.each(providerLaunchCases)(
|
||||||
providerLaunchCases,
|
"$provider launches a cmd shim from a path with spaces through spawnProcess",
|
||||||
)("$provider launches a cmd shim from a path with spaces through spawnProcess", async ({
|
async ({ binaryName, args, shell }) => {
|
||||||
binaryName,
|
const fixture = makeFixture(binaryName, args);
|
||||||
args,
|
|
||||||
shell,
|
|
||||||
}) => {
|
|
||||||
const fixture = makeFixture(binaryName, args);
|
|
||||||
|
|
||||||
const result = await runProviderFixture({
|
const result = await runProviderFixture({
|
||||||
command: fixture.shim,
|
command: fixture.shim,
|
||||||
args: fixture.expectedArgs,
|
args: fixture.expectedArgs,
|
||||||
expectedArgs: fixture.expectedArgs,
|
expectedArgs: fixture.expectedArgs,
|
||||||
shell,
|
shell,
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(result.error).toBeNull();
|
expect(result.error).toBeNull();
|
||||||
expect(result.code).toBe(0);
|
expect(result.code).toBe(0);
|
||||||
expect(result.signal).toBeNull();
|
expect(result.signal).toBeNull();
|
||||||
expect(result.stderr).toBe("");
|
expect(result.stderr).toBe("");
|
||||||
expect(result.stdout.trim()).toBe("ARGV_OK");
|
expect(result.stdout.trim()).toBe("ARGV_OK");
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -78,52 +78,51 @@ describe("daemon E2E", () => {
|
|||||||
await ctx.cleanup();
|
await ctx.cleanup();
|
||||||
}, 60000);
|
}, 60000);
|
||||||
|
|
||||||
describe.each([
|
describe.each(["claude", "codex", "opencode"] as const)(
|
||||||
"claude",
|
"live model switching (%s)",
|
||||||
"codex",
|
(provider) => {
|
||||||
"opencode",
|
const shouldRun =
|
||||||
] as const)("live model switching (%s)", (provider) => {
|
provider === "claude" ||
|
||||||
const shouldRun =
|
(provider === "codex" && hasCodex) ||
|
||||||
provider === "claude" ||
|
(provider === "opencode" && hasOpenCode);
|
||||||
(provider === "codex" && hasCodex) ||
|
|
||||||
(provider === "opencode" && hasOpenCode);
|
|
||||||
|
|
||||||
test.runIf(shouldRun)(
|
test.runIf(shouldRun)(
|
||||||
"updates agent model without restarting",
|
"updates agent model without restarting",
|
||||||
async () => {
|
async () => {
|
||||||
const cwd = tmpCwd();
|
const cwd = tmpCwd();
|
||||||
try {
|
try {
|
||||||
const modelList = await ctx.client.listProviderModels(provider);
|
const modelList = await ctx.client.listProviderModels(provider);
|
||||||
if (!modelList.models || modelList.models.length === 0) {
|
if (!modelList.models || modelList.models.length === 0) {
|
||||||
throw new Error(`No models returned for provider ${provider}`);
|
throw new Error(`No models returned for provider ${provider}`);
|
||||||
|
}
|
||||||
|
const [modelA, modelB] = pickTwoDistinctModels(modelList.models);
|
||||||
|
|
||||||
|
const agent = await ctx.client.createAgent({
|
||||||
|
provider,
|
||||||
|
cwd,
|
||||||
|
title: `Model Switch (${provider})`,
|
||||||
|
model: modelA,
|
||||||
|
});
|
||||||
|
|
||||||
|
const startIndex = messages.length;
|
||||||
|
await ctx.client.setAgentModel(agent.id, modelB);
|
||||||
|
|
||||||
|
const updated = await waitForAgentUpdate(
|
||||||
|
messages,
|
||||||
|
startIndex,
|
||||||
|
(a) => a.id === agent.id && a.model === modelB,
|
||||||
|
{ timeoutMs: 20000 },
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(updated.model).toBe(modelB);
|
||||||
|
} finally {
|
||||||
|
rmSync(cwd, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
const [modelA, modelB] = pickTwoDistinctModels(modelList.models);
|
},
|
||||||
|
180000,
|
||||||
const agent = await ctx.client.createAgent({
|
);
|
||||||
provider,
|
},
|
||||||
cwd,
|
);
|
||||||
title: `Model Switch (${provider})`,
|
|
||||||
model: modelA,
|
|
||||||
});
|
|
||||||
|
|
||||||
const startIndex = messages.length;
|
|
||||||
await ctx.client.setAgentModel(agent.id, modelB);
|
|
||||||
|
|
||||||
const updated = await waitForAgentUpdate(
|
|
||||||
messages,
|
|
||||||
startIndex,
|
|
||||||
(a) => a.id === agent.id && a.model === modelB,
|
|
||||||
{ timeoutMs: 20000 },
|
|
||||||
);
|
|
||||||
|
|
||||||
expect(updated.model).toBe(modelB);
|
|
||||||
} finally {
|
|
||||||
rmSync(cwd, { recursive: true, force: true });
|
|
||||||
}
|
|
||||||
},
|
|
||||||
180000,
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test("live thinking switching works for Claude (off -> on)", async () => {
|
test("live thinking switching works for Claude (off -> on)", async () => {
|
||||||
const cwd = tmpCwd();
|
const cwd = tmpCwd();
|
||||||
|
|||||||
@@ -35,11 +35,7 @@ export {
|
|||||||
type ProviderOverride,
|
type ProviderOverride,
|
||||||
type ProviderProfileModel,
|
type ProviderProfileModel,
|
||||||
} from "./agent/provider-launch-config.js";
|
} from "./agent/provider-launch-config.js";
|
||||||
export {
|
export { findExecutable, quoteWindowsArgument, quoteWindowsCommand } from "../utils/executable.js";
|
||||||
findExecutable,
|
|
||||||
quoteWindowsArgument,
|
|
||||||
quoteWindowsCommand,
|
|
||||||
} from "../utils/executable.js";
|
|
||||||
export { execCommand, spawnProcess } from "../utils/spawn.js";
|
export { execCommand, spawnProcess } from "../utils/spawn.js";
|
||||||
|
|
||||||
// Provider manifest (source of truth for provider definitions)
|
// Provider manifest (source of truth for provider definitions)
|
||||||
|
|||||||
@@ -1776,9 +1776,9 @@ describe("workspace aggregation", () => {
|
|||||||
rmSync(tempDir, { recursive: true, force: true });
|
rmSync(tempDir, { recursive: true, force: true });
|
||||||
}
|
}
|
||||||
|
|
||||||
const response = emitted.find((message) => message.type === "create_paseo_worktree_response") as
|
const response = emitted.find(
|
||||||
| { type: "create_paseo_worktree_response"; payload: any }
|
(message) => message.type === "create_paseo_worktree_response",
|
||||||
| undefined;
|
) as { type: "create_paseo_worktree_response"; payload: any } | undefined;
|
||||||
|
|
||||||
expect(response?.payload.error).toBeNull();
|
expect(response?.payload.error).toBeNull();
|
||||||
expect(response?.payload.workspace).toMatchObject({
|
expect(response?.payload.workspace).toMatchObject({
|
||||||
|
|||||||
@@ -7,8 +7,5 @@ export {
|
|||||||
type DaemonEvent,
|
type DaemonEvent,
|
||||||
type DaemonEventHandler,
|
type DaemonEventHandler,
|
||||||
} from "./daemon-client.js";
|
} from "./daemon-client.js";
|
||||||
export {
|
export { createDaemonTestContext, type DaemonTestContext } from "./daemon-test-context.js";
|
||||||
createDaemonTestContext,
|
|
||||||
type DaemonTestContext,
|
|
||||||
} from "./daemon-test-context.js";
|
|
||||||
export { useTempClaudeConfigDir } from "./claude-config.js";
|
export { useTempClaudeConfigDir } from "./claude-config.js";
|
||||||
|
|||||||
@@ -172,20 +172,19 @@ describe("pull request timeline message schemas", () => {
|
|||||||
expect(parsed.payload.error).toEqual({ kind: "unknown", message: "x" });
|
expect(parsed.payload.error).toEqual({ kind: "unknown", message: "x" });
|
||||||
});
|
});
|
||||||
|
|
||||||
test.each([
|
test.each(["rate_limited", "", undefined])(
|
||||||
"rate_limited",
|
"normalizes malformed error kind %j to unknown",
|
||||||
"",
|
(kind) => {
|
||||||
undefined,
|
const parsed = PullRequestTimelineResponseSchema.parse({
|
||||||
])("normalizes malformed error kind %j to unknown", (kind) => {
|
type: "pull_request_timeline_response",
|
||||||
const parsed = PullRequestTimelineResponseSchema.parse({
|
payload: {
|
||||||
type: "pull_request_timeline_response",
|
error: { kind },
|
||||||
payload: {
|
},
|
||||||
error: { kind },
|
});
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
expect(parsed.payload.error).toEqual({ kind: "unknown", message: "" });
|
expect(parsed.payload.error).toEqual({ kind: "unknown", message: "" });
|
||||||
});
|
},
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("checkout PR status compatibility", () => {
|
describe("checkout PR status compatibility", () => {
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ main = "@tanstack/react-start/server-entry"
|
|||||||
|
|
||||||
routes = [
|
routes = [
|
||||||
{ pattern = "paseo.sh", custom_domain = true },
|
{ pattern = "paseo.sh", custom_domain = true },
|
||||||
{ pattern = "www.paseo.sh", custom_domain = true }
|
{ pattern = "www.paseo.sh", custom_domain = true },
|
||||||
]
|
]
|
||||||
|
|
||||||
[observability]
|
[observability]
|
||||||
|
|||||||
@@ -19,6 +19,7 @@ Load the **Paseo skill** first if you need CLI guidance for launching or messagi
|
|||||||
## Rules
|
## Rules
|
||||||
|
|
||||||
When using chat:
|
When using chat:
|
||||||
|
|
||||||
- create a room with `paseo chat create` if you need a new room
|
- create a room with `paseo chat create` if you need a new room
|
||||||
- inspect available rooms with `paseo chat ls` and `paseo chat inspect`
|
- inspect available rooms with `paseo chat ls` and `paseo chat inspect`
|
||||||
- post with `paseo chat post`
|
- post with `paseo chat post`
|
||||||
@@ -27,6 +28,7 @@ When using chat:
|
|||||||
- check chat often while working
|
- check chat often while working
|
||||||
|
|
||||||
Mentions are active:
|
Mentions are active:
|
||||||
|
|
||||||
- write mentions inline in the message body as `@<agent-id>` to notify a specific agent immediately
|
- write mentions inline in the message body as `@<agent-id>` to notify a specific agent immediately
|
||||||
- use `@everyone` to notify all non-archived, non-internal agents
|
- use `@everyone` to notify all non-archived, non-internal agents
|
||||||
- notifications are sent to the target agent without blocking the chat post
|
- notifications are sent to the target agent without blocking the chat post
|
||||||
@@ -99,10 +101,12 @@ paseo chat wait issue-456 --timeout 60s
|
|||||||
## Defaults
|
## Defaults
|
||||||
|
|
||||||
When creating a room:
|
When creating a room:
|
||||||
|
|
||||||
- choose a short slug: `issue-456`, `pr-143-review`, `relay-cleanup`
|
- choose a short slug: `issue-456`, `pr-143-review`, `relay-cleanup`
|
||||||
- give it a clear purpose
|
- give it a clear purpose
|
||||||
|
|
||||||
When using a room:
|
When using a room:
|
||||||
|
|
||||||
- read only a bounded window before acting
|
- read only a bounded window before acting
|
||||||
- post updates when they would help another agent or your future self
|
- post updates when they would help another agent or your future self
|
||||||
- use `--reply-to` when responding to a specific message
|
- use `--reply-to` when responding to a specific message
|
||||||
@@ -112,6 +116,7 @@ When using a room:
|
|||||||
- your own agent ID is available via `$PASEO_AGENT_ID`
|
- your own agent ID is available via `$PASEO_AGENT_ID`
|
||||||
|
|
||||||
Typical things to post:
|
Typical things to post:
|
||||||
|
|
||||||
- status updates
|
- status updates
|
||||||
- blockers
|
- blockers
|
||||||
- handoffs
|
- handoffs
|
||||||
|
|||||||
@@ -30,13 +30,13 @@ Parse `$ARGUMENTS` to determine:
|
|||||||
|
|
||||||
### Provider Resolution
|
### Provider Resolution
|
||||||
|
|
||||||
| User says | --provider | Mode |
|
| User says | --provider | Mode |
|
||||||
|---|---|---|
|
| ----------- | --------------- | ------------- |
|
||||||
| *(nothing)* | `codex/gpt-5.4` | `full-access` |
|
| _(nothing)_ | `codex/gpt-5.4` | `full-access` |
|
||||||
| `codex` | `codex/gpt-5.4` | `full-access` |
|
| `codex` | `codex/gpt-5.4` | `full-access` |
|
||||||
| `claude` | `claude/opus` | `bypass` |
|
| `claude` | `claude/opus` | `bypass` |
|
||||||
| `opus` | `claude/opus` | `bypass` |
|
| `opus` | `claude/opus` | `bypass` |
|
||||||
| `sonnet` | `claude/sonnet` | `bypass` |
|
| `sonnet` | `claude/sonnet` | `bypass` |
|
||||||
|
|
||||||
Default is **Codex** with `gpt-5.4`.
|
Default is **Codex** with `gpt-5.4`.
|
||||||
|
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ paseo loop stop <id> # Stop a running loop
|
|||||||
### Worker prompt
|
### Worker prompt
|
||||||
|
|
||||||
The worker prompt must be:
|
The worker prompt must be:
|
||||||
|
|
||||||
- self-contained
|
- self-contained
|
||||||
- concrete about commands, files, branches, tests, PRs, or systems to inspect
|
- concrete about commands, files, branches, tests, PRs, or systems to inspect
|
||||||
- explicit about what counts as progress this iteration
|
- explicit about what counts as progress this iteration
|
||||||
@@ -115,6 +116,7 @@ The worker prompt must be:
|
|||||||
### Verifier prompt
|
### Verifier prompt
|
||||||
|
|
||||||
The verifier prompt should:
|
The verifier prompt should:
|
||||||
|
|
||||||
- check facts, not offer fixes
|
- check facts, not offer fixes
|
||||||
- cite commands, outputs, or file evidence
|
- cite commands, outputs, or file evidence
|
||||||
- be specific about what "done" means
|
- be specific about what "done" means
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ You are an end-to-end implementation orchestrator. You take a task from understa
|
|||||||
## Prerequisites
|
## Prerequisites
|
||||||
|
|
||||||
Load these skills before proceeding:
|
Load these skills before proceeding:
|
||||||
|
|
||||||
1. **e2e-playwright** — if the task involves frontend/UI work
|
1. **e2e-playwright** — if the task involves frontend/UI work
|
||||||
|
|
||||||
## Guard
|
## Guard
|
||||||
@@ -46,13 +47,13 @@ Merge with defaults for any missing fields. The file maps role categories to `<a
|
|||||||
- The part before `/` is the `agentType` (e.g., `codex`, `claude`, `opencode`)
|
- The part before `/` is the `agentType` (e.g., `codex`, `claude`, `opencode`)
|
||||||
- The part after `/` is the `model` (e.g., `gpt-5.4`, `opus`)
|
- The part after `/` is the `model` (e.g., `gpt-5.4`, `opus`)
|
||||||
|
|
||||||
| Category | Roles covered | Default |
|
| Category | Roles covered | Default |
|
||||||
|----------|--------------|---------|
|
| ---------- | --------------------------------- | --------------- |
|
||||||
| `impl` | impl, tester, refactorer | `codex/gpt-5.4` |
|
| `impl` | impl, tester, refactorer | `codex/gpt-5.4` |
|
||||||
| `ui` | impl agents doing UI/styling work | `claude/opus` |
|
| `ui` | impl agents doing UI/styling work | `claude/opus` |
|
||||||
| `research` | researcher | `codex/gpt-5.4` |
|
| `research` | researcher | `codex/gpt-5.4` |
|
||||||
| `planning` | planner, plan-reviewer | `codex/gpt-5.4` |
|
| `planning` | planner, plan-reviewer | `codex/gpt-5.4` |
|
||||||
| `audit` | auditor, qa | `codex/gpt-5.4` |
|
| `audit` | auditor, qa | `codex/gpt-5.4` |
|
||||||
|
|
||||||
The file also has a `preferences` array of freeform natural language strings. Read these at startup and weave them into your behavior contextually. When the user says "store my preference: X", update the file.
|
The file also has a `preferences` array of freeform natural language strings. Read these at startup and weave them into your behavior contextually. When the user says "store my preference: X", update the file.
|
||||||
|
|
||||||
@@ -90,11 +91,13 @@ To archive: Paseo **archive agent**.
|
|||||||
### How to Write Agent Prompts
|
### How to Write Agent Prompts
|
||||||
|
|
||||||
**Describe the problem, not the solution.** Your prompt should tell the agent:
|
**Describe the problem, not the solution.** Your prompt should tell the agent:
|
||||||
|
|
||||||
- What's wrong or what needs to be built (the goal)
|
- What's wrong or what needs to be built (the goal)
|
||||||
- How it currently fails (error output, test output, user-visible behavior)
|
- How it currently fails (error output, test output, user-visible behavior)
|
||||||
- The acceptance criteria (what "done" looks like)
|
- The acceptance criteria (what "done" looks like)
|
||||||
|
|
||||||
**Do NOT tell the agent:**
|
**Do NOT tell the agent:**
|
||||||
|
|
||||||
- Which specific lines to change
|
- Which specific lines to change
|
||||||
- What code to write
|
- What code to write
|
||||||
- Which functions to call or which patterns to use
|
- Which functions to call or which patterns to use
|
||||||
@@ -141,43 +144,43 @@ State the order and briefly why: "Order 3 — touches server session management
|
|||||||
|
|
||||||
**Order 1 — Single file, single concern.** A contained change: fix a bug in one function, add a field to one type, update one component.
|
**Order 1 — Single file, single concern.** A contained change: fix a bug in one function, add a field to one type, update one component.
|
||||||
|
|
||||||
| Phase | Agents |
|
| Phase | Agents |
|
||||||
|-------|--------|
|
| --------- | ----------------------------- |
|
||||||
| Research | 1 researcher |
|
| Research | 1 researcher |
|
||||||
| Planning | 0 — orchestrator plans inline |
|
| Planning | 0 — orchestrator plans inline |
|
||||||
| Implement | 1 impl |
|
| Implement | 1 impl |
|
||||||
| Verify | 1-2 auditors |
|
| Verify | 1-2 auditors |
|
||||||
| Cleanup | 0-1 refactorer |
|
| Cleanup | 0-1 refactorer |
|
||||||
|
|
||||||
**Order 2 — Single module, few files.** A feature or fix within one package that touches 3-8 files.
|
**Order 2 — Single module, few files.** A feature or fix within one package that touches 3-8 files.
|
||||||
|
|
||||||
| Phase | Agents |
|
| Phase | Agents |
|
||||||
|-------|--------|
|
| --------- | ---------------- |
|
||||||
| Research | 2 researchers |
|
| Research | 2 researchers |
|
||||||
| Planning | 1 planner |
|
| Planning | 1 planner |
|
||||||
| Implement | 1 impl per phase |
|
| Implement | 1 impl per phase |
|
||||||
| Verify | 2-3 auditors |
|
| Verify | 2-3 auditors |
|
||||||
| Cleanup | 1 refactorer |
|
| Cleanup | 1 refactorer |
|
||||||
|
|
||||||
**Order 3 — Cross-module, multiple packages.** A feature that spans packages.
|
**Order 3 — Cross-module, multiple packages.** A feature that spans packages.
|
||||||
|
|
||||||
| Phase | Agents |
|
| Phase | Agents |
|
||||||
|-------|--------|
|
| --------- | ---------------------------- |
|
||||||
| Research | 3-4 researchers |
|
| Research | 3-4 researchers |
|
||||||
| Planning | 2 planners + 1 plan-reviewer |
|
| Planning | 2 planners + 1 plan-reviewer |
|
||||||
| Implement | 1-2 impl agents per phase |
|
| Implement | 1-2 impl agents per phase |
|
||||||
| Verify | 3-4 auditors |
|
| Verify | 3-4 auditors |
|
||||||
| Cleanup | 1-2 refactorers |
|
| Cleanup | 1-2 refactorers |
|
||||||
|
|
||||||
**Order 4 — Architectural, system-wide.** A new subsystem, major refactor, or system-wide change.
|
**Order 4 — Architectural, system-wide.** A new subsystem, major refactor, or system-wide change.
|
||||||
|
|
||||||
| Phase | Agents |
|
| Phase | Agents |
|
||||||
|-------|--------|
|
| --------- | ------------------------------ |
|
||||||
| Research | 5+ researchers |
|
| Research | 5+ researchers |
|
||||||
| Planning | 2+ planners + 2 plan-reviewers |
|
| Planning | 2+ planners + 2 plan-reviewers |
|
||||||
| Implement | 2+ impl agents per phase |
|
| Implement | 2+ impl agents per phase |
|
||||||
| Verify | Full auditor suite per phase |
|
| Verify | Full auditor suite per phase |
|
||||||
| Cleanup | 2+ refactorers |
|
| Cleanup | 2+ refactorers |
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
@@ -250,6 +253,7 @@ Deploy planners to create an implementation plan informed by research findings.
|
|||||||
Every planner prompt must emphasize this: the default agent instinct is to bolt new code on top of existing code. Resist this.
|
Every planner prompt must emphasize this: the default agent instinct is to bolt new code on top of existing code. Resist this.
|
||||||
|
|
||||||
The right approach:
|
The right approach:
|
||||||
|
|
||||||
1. Study the existing code — understand why it's shaped the way it is
|
1. Study the existing code — understand why it's shaped the way it is
|
||||||
2. Design the target shape — what would the code look like if this feature had always existed?
|
2. Design the target shape — what would the code look like if this feature had always existed?
|
||||||
3. Identify the refactoring gap — what needs to change so the new feature slots in cleanly?
|
3. Identify the refactoring gap — what needs to change so the new feature slots in cleanly?
|
||||||
@@ -344,6 +348,7 @@ Present the plan to the user. Wait for explicit confirmation before proceeding.
|
|||||||
Persist the plan to disk and set up the heartbeat:
|
Persist the plan to disk and set up the heartbeat:
|
||||||
|
|
||||||
Use the Paseo **create schedule** tool with:
|
Use the Paseo **create schedule** tool with:
|
||||||
|
|
||||||
- `name`: `"heartbeat-<task-slug>"`
|
- `name`: `"heartbeat-<task-slug>"`
|
||||||
- `target`: `"self"`
|
- `target`: `"self"`
|
||||||
- `every`: `"5m"`
|
- `every`: `"5m"`
|
||||||
@@ -394,6 +399,7 @@ Do the following steps in order:
|
|||||||
## Phase 7: Implement
|
## Phase 7: Implement
|
||||||
|
|
||||||
Execute phases from the plan sequentially. For each phase:
|
Execute phases from the plan sequentially. For each phase:
|
||||||
|
|
||||||
1. Launch impl agent(s) with `background: true, notifyOnFinish: true`
|
1. Launch impl agent(s) with `background: true, notifyOnFinish: true`
|
||||||
2. Wait for notification
|
2. Wait for notification
|
||||||
3. Verify (Phase 8)
|
3. Verify (Phase 8)
|
||||||
@@ -406,6 +412,7 @@ UI passes use `providers.ui` from preferences. All other impl work uses `provide
|
|||||||
### TDD — Not Optional
|
### TDD — Not Optional
|
||||||
|
|
||||||
Every impl agent works TDD:
|
Every impl agent works TDD:
|
||||||
|
|
||||||
1. Write a failing test that defines the expected behavior
|
1. Write a failing test that defines the expected behavior
|
||||||
2. Make it pass
|
2. Make it pass
|
||||||
3. Refactor if needed
|
3. Refactor if needed
|
||||||
@@ -462,6 +469,7 @@ Run typecheck when done. Do NOT commit."
|
|||||||
### Handling Blockers
|
### Handling Blockers
|
||||||
|
|
||||||
If an impl agent reports a blocker:
|
If an impl agent reports a blocker:
|
||||||
|
|
||||||
- Do NOT ask the user (in either mode)
|
- Do NOT ask the user (in either mode)
|
||||||
- Spin up a researcher to investigate
|
- Spin up a researcher to investigate
|
||||||
- Spin up an impl agent to fix it
|
- Spin up an impl agent to fix it
|
||||||
@@ -477,13 +485,13 @@ After every implementation phase, deploy auditors to verify the work. Auditors a
|
|||||||
|
|
||||||
### Which Auditors to Deploy
|
### Which Auditors to Deploy
|
||||||
|
|
||||||
| Phase type | Auditors |
|
| Phase type | Auditors |
|
||||||
|-----------|----------|
|
| ------------------ | ------------------------------------------------------ |
|
||||||
| Refactor | `parity`, `regression`, `types` |
|
| Refactor | `parity`, `regression`, `types` |
|
||||||
| Feature (backend) | `overeng`, `tests`, `regression`, `types` |
|
| Feature (backend) | `overeng`, `tests`, `regression`, `types` |
|
||||||
| Feature (frontend) | `overeng`, `tests`, `types`, `browser` (if applicable) |
|
| Feature (frontend) | `overeng`, `tests`, `types`, `browser` (if applicable) |
|
||||||
| UI pass | `overeng`, `browser` (if applicable) |
|
| UI pass | `overeng`, `browser` (if applicable) |
|
||||||
| Test-only | `regression` |
|
| Test-only | `regression` |
|
||||||
|
|
||||||
Deploy all relevant auditors in parallel — they're read-only so they don't conflict.
|
Deploy all relevant auditors in parallel — they're read-only so they don't conflict.
|
||||||
|
|
||||||
@@ -622,6 +630,7 @@ Do NOT edit files."
|
|||||||
### Interpreting Findings
|
### Interpreting Findings
|
||||||
|
|
||||||
If any auditor reports issues:
|
If any auditor reports issues:
|
||||||
|
|
||||||
1. Check the auditor's activity with Paseo **get agent activity** for details
|
1. Check the auditor's activity with Paseo **get agent activity** for details
|
||||||
2. Direct the impl agent to fix them via Paseo **send agent prompt**, or launch a new impl agent if the old one is stale
|
2. Direct the impl agent to fix them via Paseo **send agent prompt**, or launch a new impl agent if the old one is stale
|
||||||
3. Re-deploy the same auditor after fixes
|
3. Re-deploy the same auditor after fixes
|
||||||
@@ -841,16 +850,16 @@ Every agent has exactly one role. The role determines what the agent does, wheth
|
|||||||
|
|
||||||
**Naming:** `<role>-<scope>[-<specialization>]` in kebab-case.
|
**Naming:** `<role>-<scope>[-<specialization>]` in kebab-case.
|
||||||
|
|
||||||
| Role | Job | Edits? | Prompt emphasis |
|
| Role | Job | Edits? | Prompt emphasis |
|
||||||
|------|-----|--------|----------------|
|
| --------------- | ------------------------------------------ | ------ | --------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||||
| `researcher` | Gathers info: codebase, docs, web, scripts | No | "Report what you find. Do not suggest solutions. Do not edit files." |
|
| `researcher` | Gathers info: codebase, docs, web, scripts | No | "Report what you find. Do not suggest solutions. Do not edit files." |
|
||||||
| `planner` | Creates implementation plan from research | No | "Think refactor-first. Design the target shape, not the steps." |
|
| `planner` | Creates implementation plan from research | No | "Think refactor-first. Design the target shape, not the steps." |
|
||||||
| `plan-reviewer` | Adversarially challenges a plan | No | "Challenge the plan. Find what's wrong, missing, or over-engineered." |
|
| `plan-reviewer` | Adversarially challenges a plan | No | "Challenge the plan. Find what's wrong, missing, or over-engineered." |
|
||||||
| `impl` | Writes code, works TDD | Yes | "Work TDD. Reshape existing code. Run typecheck AND run any tests you modified. Both must pass. Do NOT commit." |
|
| `impl` | Writes code, works TDD | Yes | "Work TDD. Reshape existing code. Run typecheck AND run any tests you modified. Both must pass. Do NOT commit." |
|
||||||
| `tester` | Writes/fixes tests | Yes | "Verify behavior, not implementation. Run every test you modified and confirm it passes. A test change without running the test is not done." |
|
| `tester` | Writes/fixes tests | Yes | "Verify behavior, not implementation. Run every test you modified and confirm it passes. A test change without running the test is not done." |
|
||||||
| `auditor` | Read-only verification | No | "Check [specialization]. Report YES/NO with evidence. Do NOT edit files." |
|
| `auditor` | Read-only verification | No | "Check [specialization]. Report YES/NO with evidence. Do NOT edit files." |
|
||||||
| `refactorer` | Targeted cleanup | Yes | "Fix [specialization] only. Run typecheck and any tests you touch. Do NOT commit." |
|
| `refactorer` | Targeted cleanup | Yes | "Fix [specialization] only. Run typecheck and any tests you touch. Do NOT commit." |
|
||||||
| `qa` | End-to-end QA, browser testing | No | "Test the actual user experience. Report with evidence." |
|
| `qa` | End-to-end QA, browser testing | No | "Test the actual user experience. Report with evidence." |
|
||||||
|
|
||||||
Auditor specializations: `overeng`, `dry`, `tests`, `regression`, `types`, `browser`, `parity`
|
Auditor specializations: `overeng`, `dry`, `tests`, `regression`, `types`, `browser`, `parity`
|
||||||
Refactorer specializations: `dry`, `dead-code`, `naming`
|
Refactorer specializations: `dry`, `dead-code`, `naming`
|
||||||
|
|||||||
@@ -81,6 +81,7 @@ Do NOT edit files."
|
|||||||
## If Issues Are Found
|
## If Issues Are Found
|
||||||
|
|
||||||
If any final QA agent reports issues:
|
If any final QA agent reports issues:
|
||||||
|
|
||||||
1. Launch an impl or refactorer agent to fix them
|
1. Launch an impl or refactorer agent to fix them
|
||||||
2. Re-run the specific QA check that failed
|
2. Re-run the specific QA check that failed
|
||||||
3. Repeat until all checks pass
|
3. Repeat until all checks pass
|
||||||
|
|||||||
@@ -5,6 +5,7 @@ Deploy impl agents to execute the plan phase by phase. Each phase is independent
|
|||||||
## TDD — Not Optional
|
## TDD — Not Optional
|
||||||
|
|
||||||
Every impl agent works TDD:
|
Every impl agent works TDD:
|
||||||
|
|
||||||
1. Write a failing test that defines the expected behavior
|
1. Write a failing test that defines the expected behavior
|
||||||
2. Make it pass
|
2. Make it pass
|
||||||
3. Refactor if needed
|
3. Refactor if needed
|
||||||
@@ -17,6 +18,7 @@ If an impl agent finds a broken test, it fixes it. No "pre-existing failures." N
|
|||||||
Execute phases sequentially from the plan. Refactoring phases first, then feature phases, then UI passes.
|
Execute phases sequentially from the plan. Refactoring phases first, then feature phases, then UI passes.
|
||||||
|
|
||||||
After each phase:
|
After each phase:
|
||||||
|
|
||||||
1. Verify (see verification.md)
|
1. Verify (see verification.md)
|
||||||
2. Fix any issues found
|
2. Fix any issues found
|
||||||
3. Re-verify
|
3. Re-verify
|
||||||
@@ -29,11 +31,13 @@ Use the Paseo **create agent** tool with `background: true` and `notifyOnFinish:
|
|||||||
### How to describe the work
|
### How to describe the work
|
||||||
|
|
||||||
**Describe the problem, not the solution.** Your prompt should tell the agent:
|
**Describe the problem, not the solution.** Your prompt should tell the agent:
|
||||||
|
|
||||||
- What's wrong or what needs to be built (the goal)
|
- What's wrong or what needs to be built (the goal)
|
||||||
- How it currently fails (error output, test output, user-visible behavior)
|
- How it currently fails (error output, test output, user-visible behavior)
|
||||||
- The acceptance criteria (what "done" looks like)
|
- The acceptance criteria (what "done" looks like)
|
||||||
|
|
||||||
**Do NOT tell the agent:**
|
**Do NOT tell the agent:**
|
||||||
|
|
||||||
- Which specific lines to change
|
- Which specific lines to change
|
||||||
- What code to write
|
- What code to write
|
||||||
- Which functions to call or which patterns to use
|
- Which functions to call or which patterns to use
|
||||||
@@ -95,6 +99,7 @@ Run typecheck when done. Do NOT commit."
|
|||||||
## Handling Blockers
|
## Handling Blockers
|
||||||
|
|
||||||
If an impl agent reports a blocker:
|
If an impl agent reports a blocker:
|
||||||
|
|
||||||
- Do NOT ask the user (in either mode)
|
- Do NOT ask the user (in either mode)
|
||||||
- Spin up a researcher to investigate
|
- Spin up a researcher to investigate
|
||||||
- Spin up an impl agent to fix it
|
- Spin up an impl agent to fix it
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ Deploy planners to create an implementation plan informed by research findings.
|
|||||||
Every planner prompt must emphasize this: the default agent instinct is to bolt new code on top of existing code. Resist this.
|
Every planner prompt must emphasize this: the default agent instinct is to bolt new code on top of existing code. Resist this.
|
||||||
|
|
||||||
The right approach:
|
The right approach:
|
||||||
|
|
||||||
1. Study the existing code — understand why it's shaped the way it is
|
1. Study the existing code — understand why it's shaped the way it is
|
||||||
2. Design the target shape — what would the code look like if this feature had always existed?
|
2. Design the target shape — what would the code look like if this feature had always existed?
|
||||||
3. Identify the refactoring gap — what needs to change so the new feature slots in cleanly?
|
3. Identify the refactoring gap — what needs to change so the new feature slots in cleanly?
|
||||||
@@ -65,6 +66,7 @@ Challenge the plan:
|
|||||||
## Multiple Planners (Order 3+)
|
## Multiple Planners (Order 3+)
|
||||||
|
|
||||||
For cross-module tasks, deploy planners focusing on different slices:
|
For cross-module tasks, deploy planners focusing on different slices:
|
||||||
|
|
||||||
- One for backend phases
|
- One for backend phases
|
||||||
- One for frontend phases
|
- One for frontend phases
|
||||||
- One for test strategy
|
- One for test strategy
|
||||||
@@ -74,6 +76,7 @@ Then deploy a plan-reviewer to challenge the combined plan.
|
|||||||
## Iteration
|
## Iteration
|
||||||
|
|
||||||
If the plan-reviewer finds significant issues, either:
|
If the plan-reviewer finds significant issues, either:
|
||||||
|
|
||||||
1. Send follow-up instructions via the Paseo **send agent prompt** tool to the planner
|
1. Send follow-up instructions via the Paseo **send agent prompt** tool to the planner
|
||||||
2. Launch a new planner if the original is stale
|
2. Launch a new planner if the original is stale
|
||||||
|
|
||||||
|
|||||||
@@ -24,15 +24,16 @@ Maps role categories to `<agent-type>/<model>` strings. These map directly to th
|
|||||||
- The part before `/` is the `agentType` (e.g., `codex`, `claude`, `opencode`)
|
- The part before `/` is the `agentType` (e.g., `codex`, `claude`, `opencode`)
|
||||||
- The part after `/` is the `model` (e.g., `gpt-5.4`, `opus`)
|
- The part after `/` is the `model` (e.g., `gpt-5.4`, `opus`)
|
||||||
|
|
||||||
| Category | Roles covered |
|
| Category | Roles covered |
|
||||||
|----------|--------------|
|
| ---------- | --------------------------------- |
|
||||||
| `impl` | impl, tester, refactorer |
|
| `impl` | impl, tester, refactorer |
|
||||||
| `ui` | impl agents doing UI/styling work |
|
| `ui` | impl agents doing UI/styling work |
|
||||||
| `research` | researcher |
|
| `research` | researcher |
|
||||||
| `planning` | planner, plan-reviewer |
|
| `planning` | planner, plan-reviewer |
|
||||||
| `audit` | auditor, qa |
|
| `audit` | auditor, qa |
|
||||||
|
|
||||||
If a category is missing, use these defaults:
|
If a category is missing, use these defaults:
|
||||||
|
|
||||||
- `impl` -> `codex/gpt-5.4`
|
- `impl` -> `codex/gpt-5.4`
|
||||||
- `ui` -> `claude/opus`
|
- `ui` -> `claude/opus`
|
||||||
- `research` -> `codex/gpt-5.4`
|
- `research` -> `codex/gpt-5.4`
|
||||||
@@ -44,6 +45,7 @@ If a category is missing, use these defaults:
|
|||||||
Freeform array of natural language strings. The user states preferences and the orchestrator appends them here. Read these at startup and weave them into your behavior contextually.
|
Freeform array of natural language strings. The user states preferences and the orchestrator appends them here. Read these at startup and weave them into your behavior contextually.
|
||||||
|
|
||||||
Examples:
|
Examples:
|
||||||
|
|
||||||
- "Prefer small, focused PRs over large bundled ones"
|
- "Prefer small, focused PRs over large bundled ones"
|
||||||
- "Run E2E tests with Maestro, not Playwright"
|
- "Run E2E tests with Maestro, not Playwright"
|
||||||
- "Always check mobile responsiveness"
|
- "Always check mobile responsiveness"
|
||||||
|
|||||||
@@ -18,46 +18,46 @@ State the order and briefly why: "Order 3 — touches server session management
|
|||||||
|
|
||||||
A contained change: fix a bug in one function, add a field to one type, update one component.
|
A contained change: fix a bug in one function, add a field to one type, update one component.
|
||||||
|
|
||||||
| Phase | Agents |
|
| Phase | Agents |
|
||||||
|-------|--------|
|
| --------- | ----------------------------- |
|
||||||
| Research | 1 researcher |
|
| Research | 1 researcher |
|
||||||
| Planning | 0 — orchestrator plans inline |
|
| Planning | 0 — orchestrator plans inline |
|
||||||
| Implement | 1 impl |
|
| Implement | 1 impl |
|
||||||
| Verify | 1-2 auditors |
|
| Verify | 1-2 auditors |
|
||||||
| Cleanup | 0-1 refactorer |
|
| Cleanup | 0-1 refactorer |
|
||||||
|
|
||||||
### Order 2 — Single module, few files
|
### Order 2 — Single module, few files
|
||||||
|
|
||||||
A feature or fix within one package that touches 3-8 files. Might involve new types, new tests, a few component changes.
|
A feature or fix within one package that touches 3-8 files. Might involve new types, new tests, a few component changes.
|
||||||
|
|
||||||
| Phase | Agents |
|
| Phase | Agents |
|
||||||
|-------|--------|
|
| --------- | ---------------- |
|
||||||
| Research | 2 researchers |
|
| Research | 2 researchers |
|
||||||
| Planning | 1 planner |
|
| Planning | 1 planner |
|
||||||
| Implement | 1 impl per phase |
|
| Implement | 1 impl per phase |
|
||||||
| Verify | 2-3 auditors |
|
| Verify | 2-3 auditors |
|
||||||
| Cleanup | 1 refactorer |
|
| Cleanup | 1 refactorer |
|
||||||
|
|
||||||
### Order 3 — Cross-module, multiple packages
|
### Order 3 — Cross-module, multiple packages
|
||||||
|
|
||||||
A feature that spans packages (e.g., server + app, or CLI + server). Multiple concerns, multiple file groups, likely needs interface changes between layers.
|
A feature that spans packages (e.g., server + app, or CLI + server). Multiple concerns, multiple file groups, likely needs interface changes between layers.
|
||||||
|
|
||||||
| Phase | Agents |
|
| Phase | Agents |
|
||||||
|-------|--------|
|
| --------- | ----------------------------------------------------------------------- |
|
||||||
| Research | 3-4 researchers (one per area: backend, frontend, tests, external docs) |
|
| Research | 3-4 researchers (one per area: backend, frontend, tests, external docs) |
|
||||||
| Planning | 2 planners + 1 plan-reviewer |
|
| Planning | 2 planners + 1 plan-reviewer |
|
||||||
| Implement | 1-2 impl agents per phase |
|
| Implement | 1-2 impl agents per phase |
|
||||||
| Verify | 3-4 auditors (overeng, tests, regression, types) |
|
| Verify | 3-4 auditors (overeng, tests, regression, types) |
|
||||||
| Cleanup | 1-2 refactorers |
|
| Cleanup | 1-2 refactorers |
|
||||||
|
|
||||||
### Order 4 — Architectural, system-wide
|
### Order 4 — Architectural, system-wide
|
||||||
|
|
||||||
A new subsystem, major refactor, or change that touches most of the codebase. New abstractions, new patterns, potentially breaking changes that need migration.
|
A new subsystem, major refactor, or change that touches most of the codebase. New abstractions, new patterns, potentially breaking changes that need migration.
|
||||||
|
|
||||||
| Phase | Agents |
|
| Phase | Agents |
|
||||||
|-------|--------|
|
| --------- | --------------------------------------------------- |
|
||||||
| Research | 5+ researchers across all relevant areas |
|
| Research | 5+ researchers across all relevant areas |
|
||||||
| Planning | 2+ planners (one per major area) + 2 plan-reviewers |
|
| Planning | 2+ planners (one per major area) + 2 plan-reviewers |
|
||||||
| Implement | 2+ impl agents per phase, sequenced carefully |
|
| Implement | 2+ impl agents per phase, sequenced carefully |
|
||||||
| Verify | Full auditor suite per phase |
|
| Verify | Full auditor suite per phase |
|
||||||
| Cleanup | 2+ refactorers with different specializations |
|
| Cleanup | 2+ refactorers with different specializations |
|
||||||
|
|||||||
@@ -6,13 +6,13 @@ After every implementation phase, deploy auditors to verify the work. Auditors a
|
|||||||
|
|
||||||
Not every phase needs every auditor. Match auditors to the work:
|
Not every phase needs every auditor. Match auditors to the work:
|
||||||
|
|
||||||
| Phase type | Auditors |
|
| Phase type | Auditors |
|
||||||
|-----------|----------|
|
| ------------------ | ------------------------------------------------------ |
|
||||||
| Refactor | `parity`, `regression`, `types` |
|
| Refactor | `parity`, `regression`, `types` |
|
||||||
| Feature (backend) | `overeng`, `tests`, `regression`, `types` |
|
| Feature (backend) | `overeng`, `tests`, `regression`, `types` |
|
||||||
| Feature (frontend) | `overeng`, `tests`, `types`, `browser` (if applicable) |
|
| Feature (frontend) | `overeng`, `tests`, `types`, `browser` (if applicable) |
|
||||||
| UI pass | `overeng`, `browser` (if applicable) |
|
| UI pass | `overeng`, `browser` (if applicable) |
|
||||||
| Test-only | `regression` |
|
| Test-only | `regression` |
|
||||||
|
|
||||||
Deploy all relevant auditors in parallel — they're read-only so they don't conflict.
|
Deploy all relevant auditors in parallel — they're read-only so they don't conflict.
|
||||||
|
|
||||||
@@ -151,6 +151,7 @@ Do NOT edit files."
|
|||||||
## Interpreting Findings
|
## Interpreting Findings
|
||||||
|
|
||||||
If any auditor reports issues:
|
If any auditor reports issues:
|
||||||
|
|
||||||
1. Check the auditor's activity with the Paseo **get agent activity** tool for details
|
1. Check the auditor's activity with the Paseo **get agent activity** tool for details
|
||||||
2. Direct the impl agent to fix them via the Paseo **send agent prompt** tool, or launch a new impl agent if the old one is stale
|
2. Direct the impl agent to fix them via the Paseo **send agent prompt** tool, or launch a new impl agent if the old one is stale
|
||||||
3. Re-deploy the same auditor after fixes
|
3. Re-deploy the same auditor after fixes
|
||||||
|
|||||||
@@ -193,6 +193,7 @@ paseo terminal send-keys <terminal-id> --literal "raw text" # No special token
|
|||||||
`Enter`, `Tab`, `Escape`, `Space`, `BSpace`, `C-c`, `C-d`, `C-z`, `C-l`, `C-a`, `C-e`
|
`Enter`, `Tab`, `Escape`, `Space`, `BSpace`, `C-c`, `C-d`, `C-z`, `C-l`, `C-a`, `C-e`
|
||||||
|
|
||||||
**Common pattern — launch a process and interact with it:**
|
**Common pattern — launch a process and interact with it:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
id=$(paseo terminal create --name "my-shell" -q)
|
id=$(paseo terminal create --name "my-shell" -q)
|
||||||
paseo terminal send-keys "$id" "claude" Enter
|
paseo terminal send-keys "$id" "claude" Enter
|
||||||
@@ -208,11 +209,13 @@ paseo terminal kill "$id"
|
|||||||
## Available Models
|
## Available Models
|
||||||
|
|
||||||
**Claude (default provider):**
|
**Claude (default provider):**
|
||||||
|
|
||||||
- `--provider claude/haiku` — Fast/cheap, ONLY for tests (not for real work)
|
- `--provider claude/haiku` — Fast/cheap, ONLY for tests (not for real work)
|
||||||
- `--provider claude/sonnet` — Good for most tasks
|
- `--provider claude/sonnet` — Good for most tasks
|
||||||
- `--provider claude/opus` — For harder reasoning, complex debugging
|
- `--provider claude/opus` — For harder reasoning, complex debugging
|
||||||
|
|
||||||
**Codex:**
|
**Codex:**
|
||||||
|
|
||||||
- `--provider codex/gpt-5.4` — Latest frontier agentic coding model (preferred for all engineering tasks)
|
- `--provider codex/gpt-5.4` — Latest frontier agentic coding model (preferred for all engineering tasks)
|
||||||
- `--provider codex/gpt-5.4-mini` — Cheaper, faster, but less capable
|
- `--provider codex/gpt-5.4-mini` — Cheaper, faster, but less capable
|
||||||
|
|
||||||
@@ -236,6 +239,7 @@ Both `paseo run` and `paseo wait` block until the agent completes. Trust them.
|
|||||||
`paseo run` blocks by default and `--output-schema` returns structured JSON, making it easy to compose agents in bash loops and pipelines.
|
`paseo run` blocks by default and `--output-schema` returns structured JSON, making it easy to compose agents in bash loops and pipelines.
|
||||||
|
|
||||||
**Detach + wait pattern for parallel work:**
|
**Detach + wait pattern for parallel work:**
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
api_id=$(paseo run -d --name "impl-api" "implement the API" -q)
|
api_id=$(paseo run -d --name "impl-api" "implement the API" -q)
|
||||||
ui_id=$(paseo run -d --name "impl-ui" "implement the UI" -q)
|
ui_id=$(paseo run -d --name "impl-ui" "implement the UI" -q)
|
||||||
|
|||||||
Reference in New Issue
Block a user