mirror of
https://github.com/getpaseo/paseo.git
synced 2026-10-28 04:03:05 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
74b56cbfe8 | ||
|
|
c67b7158b4 | ||
|
|
91dc927335 | ||
|
|
49f9cec6be | ||
|
|
e07da55f81 |
@@ -1,5 +1,65 @@
|
||||
# Changelog
|
||||
|
||||
## 0.9.2 - 2026-09-24
|
||||
|
||||
### Added
|
||||
|
||||
- Added structured Claude Code launch arguments for session configuration and plugins ([#5206](https://github.com/getpaseo/paseo/pull/5206))
|
||||
|
||||
### Improved
|
||||
|
||||
- Reduced background Git polling for repositories the watcher cannot observe ([#5170](https://github.com/getpaseo/paseo/pull/5170))
|
||||
- Reduced Add Project directory search time on large home directories ([#5190](https://github.com/getpaseo/paseo/pull/5190))
|
||||
|
||||
### Fixed
|
||||
|
||||
- Fixed the daemon becoming unresponsive when ignored directories appear after a workspace opens ([c3e1e08](https://github.com/getpaseo/paseo/commit/c3e1e084a068e5895710c43b034ced8ebef256e8) by [@Marcus172](https://github.com/Marcus172))
|
||||
- Fixed daemon memory growing after client connections close ([9978988](https://github.com/getpaseo/paseo/commit/9978988e35409a018a52d0d7646f18b51e562346))
|
||||
- Fixed a project folder ending in a space crashing the app on load ([#5205](https://github.com/getpaseo/paseo/pull/5205) by [@L4XB](https://github.com/L4XB))
|
||||
- Fixed workspace labels missing when an agent screen opens before the sidebar ([#5079](https://github.com/getpaseo/paseo/pull/5079) by [@morven-ai](https://github.com/morven-ai))
|
||||
- Fixed workspaces disappearing when their disk or network share is unavailable ([#5227](https://github.com/getpaseo/paseo/pull/5227))
|
||||
- Fixed a failed session import making an archived worktree impossible to restore ([#5238](https://github.com/getpaseo/paseo/pull/5238))
|
||||
- Fixed archived agent logs failing after Paseo removes their worktree ([#5229](https://github.com/getpaseo/paseo/pull/5229))
|
||||
- Fixed a newly created branch pushing to the default branch through an inherited upstream ([#5249](https://github.com/getpaseo/paseo/pull/5249))
|
||||
- Fixed fork checkout pull requests disappearing from the workspace after refresh ([#5221](https://github.com/getpaseo/paseo/pull/5221))
|
||||
- Fixed agent-created local workspaces accepting a missing path or file ([#5322](https://github.com/getpaseo/paseo/pull/5322))
|
||||
- Fixed chat uploads replacing valid characters in original file names with underscores ([#5317](https://github.com/getpaseo/paseo/pull/5317))
|
||||
- Fixed multi-select questions dropping checked options or a typed Other answer ([#5320](https://github.com/getpaseo/paseo/pull/5320) by [@ThePharmer](https://github.com/ThePharmer))
|
||||
- Fixed Android Back leaving the screen while a bottom sheet is open ([#5245](https://github.com/getpaseo/paseo/pull/5245))
|
||||
- Fixed voice mode playing the thinking tone between spoken reply segments ([#5281](https://github.com/getpaseo/paseo/pull/5281))
|
||||
- Fixed a Claude slash command sent with an attachment reaching Claude as plain text ([#5240](https://github.com/getpaseo/paseo/pull/5240) by [@joecorkerton](https://github.com/joecorkerton))
|
||||
- Fixed Claude rewind after a turn that received no response ([#5285](https://github.com/getpaseo/paseo/pull/5285))
|
||||
- Fixed Claude rewind after a turn containing subagent messages ([#5289](https://github.com/getpaseo/paseo/pull/5289))
|
||||
- Fixed repeated agent history after refresh when a durable timeline store is configured ([#5286](https://github.com/getpaseo/paseo/pull/5286))
|
||||
- Fixed an OMP custom message ending a turn before the provider finished ([#3258](https://github.com/getpaseo/paseo/pull/3258))
|
||||
- Fixed a stopped OMP turn appearing as a failed turn ([#5243](https://github.com/getpaseo/paseo/pull/5243))
|
||||
- Fixed Stop refusing to settle a Pi or OMP agent whose runtime has exited ([#5235](https://github.com/getpaseo/paseo/pull/5235))
|
||||
- Fixed OpenCode agents ignoring their configured permission rules ([#5296](https://github.com/getpaseo/paseo/pull/5296) by [@gurvancampion](https://github.com/gurvancampion))
|
||||
- Fixed Codex Default and Read-only modes sending approval requests to Auto-review ([#5239](https://github.com/getpaseo/paseo/pull/5239) by [@HMWCS](https://github.com/HMWCS))
|
||||
- Fixed Cursor agent creation after switching from a Fast model to one without Fast ([#5274](https://github.com/getpaseo/paseo/pull/5274) by [@gengjiawen](https://github.com/gengjiawen))
|
||||
- Fixed the Fast control missing for GPT-6 Sol and GPT-6 Luna in Codex ([#5273](https://github.com/getpaseo/paseo/pull/5273) by [@basilk15](https://github.com/basilk15), [@colonelpanic8](https://github.com/colonelpanic8))
|
||||
- Fixed Claude's configured Fable model missing from the model picker ([#5326](https://github.com/getpaseo/paseo/pull/5326) by [@noahg9](https://github.com/noahg9))
|
||||
- Fixed ACP terminals started by the daemon using the wrong agent identity ([#5248](https://github.com/getpaseo/paseo/pull/5248))
|
||||
- Fixed an agent's completed plugin session appearing failed after a daemon restart ([#5253](https://github.com/getpaseo/paseo/pull/5253))
|
||||
- Fixed plugin reload crashing its subprocess while a provider session closes ([#5231](https://github.com/getpaseo/paseo/pull/5231))
|
||||
- Fixed a failed plugin provider request crashing the daemon ([#5298](https://github.com/getpaseo/paseo/pull/5298))
|
||||
- Fixed Hub executions being unable to title workspaces they create ([#5302](https://github.com/getpaseo/paseo/pull/5302))
|
||||
- Fixed an invalid schedule file preventing the daemon from starting ([#5301](https://github.com/getpaseo/paseo/pull/5301))
|
||||
- Fixed an empty `paseo.pid` preventing the daemon from starting ([#5306](https://github.com/getpaseo/paseo/pull/5306))
|
||||
- Fixed a recycled daemon PID preventing startup after a reboot ([#5277](https://github.com/getpaseo/paseo/pull/5277))
|
||||
- Fixed daemon startup failing on `config.json` saved with a UTF-8 byte order mark ([#5315](https://github.com/getpaseo/paseo/pull/5315))
|
||||
- Fixed background daemon startup errors missing from the reported log file ([#5332](https://github.com/getpaseo/paseo/pull/5332))
|
||||
- Fixed CLI errors for invalid `config.json` omitting the file and failing field ([#5337](https://github.com/getpaseo/paseo/pull/5337))
|
||||
- Fixed the CLI suggesting daemon startup after a password rejection ([#5310](https://github.com/getpaseo/paseo/pull/5310))
|
||||
- Fixed Open in editor missing for a password-protected desktop daemon ([#5335](https://github.com/getpaseo/paseo/pull/5335))
|
||||
- Fixed `paseo permit ls --json` shortening request IDs needed by `permit allow` and `permit deny` ([#5305](https://github.com/getpaseo/paseo/pull/5305))
|
||||
- Fixed a replica cache read spinning when its store keeps rejecting writes ([#5290](https://github.com/getpaseo/paseo/pull/5290))
|
||||
- Fixed the MiniMax card showing a raw error for an inactive token subscription ([#5258](https://github.com/getpaseo/paseo/pull/5258))
|
||||
- Fixed modified Backspace being rejected as a custom shortcut ([#5224](https://github.com/getpaseo/paseo/pull/5224))
|
||||
- Fixed multi-step shortcuts failing when the app updates between steps ([#5255](https://github.com/getpaseo/paseo/pull/5255) by [@colonelpanic8](https://github.com/colonelpanic8))
|
||||
- Fixed multi-step shortcuts failing when the second step holds a modifier ([#5272](https://github.com/getpaseo/paseo/pull/5272))
|
||||
- Fixed rebound pane-focus shortcuts failing while typing ([#5287](https://github.com/getpaseo/paseo/pull/5287))
|
||||
|
||||
## 0.9.1 - 2026-09-22
|
||||
|
||||
### Added
|
||||
|
||||
@@ -0,0 +1,12 @@
|
||||
Added
|
||||
- Added structured Claude Code launch arguments for session configuration and plugins
|
||||
|
||||
Improved
|
||||
- Reduced background Git polling for repositories the watcher cannot observe
|
||||
- Reduced Add Project directory search time on large home directories
|
||||
|
||||
Fixed
|
||||
- Fixed the daemon becoming unresponsive when ignored directories appear after a workspace opens
|
||||
- Fixed daemon memory growing after client connections close (9978988)
|
||||
|
||||
Full notes: https://paseo.sh/changelog
|
||||
@@ -0,0 +1,12 @@
|
||||
Added
|
||||
- Added structured Claude Code launch arguments for session configuration and plugins
|
||||
|
||||
Improved
|
||||
- Reduced background Git polling for repositories the watcher cannot observe
|
||||
- Reduced Add Project directory search time on large home directories
|
||||
|
||||
Fixed
|
||||
- Fixed the daemon becoming unresponsive when ignored directories appear after a workspace opens
|
||||
- Fixed daemon memory growing after client connections close (9978988)
|
||||
|
||||
Full notes: https://paseo.sh/changelog
|
||||
@@ -0,0 +1,12 @@
|
||||
Added
|
||||
- Added structured Claude Code launch arguments for session configuration and plugins
|
||||
|
||||
Improved
|
||||
- Reduced background Git polling for repositories the watcher cannot observe
|
||||
- Reduced Add Project directory search time on large home directories
|
||||
|
||||
Fixed
|
||||
- Fixed the daemon becoming unresponsive when ignored directories appear after a workspace opens
|
||||
- Fixed daemon memory growing after client connections close (9978988)
|
||||
|
||||
Full notes: https://paseo.sh/changelog
|
||||
@@ -0,0 +1,12 @@
|
||||
Added
|
||||
- Added structured Claude Code launch arguments for session configuration and plugins
|
||||
|
||||
Improved
|
||||
- Reduced background Git polling for repositories the watcher cannot observe
|
||||
- Reduced Add Project directory search time on large home directories
|
||||
|
||||
Fixed
|
||||
- Fixed the daemon becoming unresponsive when ignored directories appear after a workspace opens
|
||||
- Fixed daemon memory growing after client connections close (9978988)
|
||||
|
||||
Full notes: https://paseo.sh/changelog
|
||||
Generated
+27
-27
@@ -1,12 +1,12 @@
|
||||
{
|
||||
"name": "paseo",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "paseo",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"hasInstallScript": true,
|
||||
"license": "Apache-2.0",
|
||||
"workspaces": [
|
||||
@@ -36196,7 +36196,7 @@
|
||||
},
|
||||
"packages/app": {
|
||||
"name": "@getpaseo/app",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"dependencies": {
|
||||
"@codemirror/commands": "6.10.4",
|
||||
"@codemirror/language": "6.12.4",
|
||||
@@ -37262,12 +37262,12 @@
|
||||
},
|
||||
"packages/cli": {
|
||||
"name": "@getpaseo/cli",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"dependencies": {
|
||||
"@clack/prompts": "^1.0.0",
|
||||
"@getpaseo/client": "0.9.1",
|
||||
"@getpaseo/protocol": "0.9.1",
|
||||
"@getpaseo/server": "0.9.1",
|
||||
"@getpaseo/client": "0.9.2",
|
||||
"@getpaseo/protocol": "0.9.2",
|
||||
"@getpaseo/server": "0.9.2",
|
||||
"chalk": "^5.3.0",
|
||||
"commander": "^12.0.0",
|
||||
"mime-types": "^2.1.35",
|
||||
@@ -37514,10 +37514,10 @@
|
||||
},
|
||||
"packages/client": {
|
||||
"name": "@getpaseo/client",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"dependencies": {
|
||||
"@getpaseo/protocol": "0.9.1",
|
||||
"@getpaseo/relay": "0.9.1",
|
||||
"@getpaseo/protocol": "0.9.2",
|
||||
"@getpaseo/relay": "0.9.2",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
@@ -37528,7 +37528,7 @@
|
||||
},
|
||||
"packages/desktop": {
|
||||
"name": "@getpaseo/desktop",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"@getpaseo/cli": "*",
|
||||
@@ -37772,7 +37772,7 @@
|
||||
},
|
||||
"packages/expo-two-way-audio": {
|
||||
"name": "@getpaseo/expo-two-way-audio",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"license": "MIT",
|
||||
"devDependencies": {
|
||||
"@types/jest": "^29.5.14",
|
||||
@@ -38668,7 +38668,7 @@
|
||||
},
|
||||
"packages/highlight": {
|
||||
"name": "@getpaseo/highlight",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"dependencies": {
|
||||
"@codemirror/language": "6.12.4",
|
||||
"@codemirror/legacy-modes": "^6.5.3",
|
||||
@@ -38697,14 +38697,14 @@
|
||||
},
|
||||
"packages/plugin": {
|
||||
"name": "@getpaseo/plugin",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^1.4.0",
|
||||
"use-sync-external-store": "^1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@getpaseo/client": "0.9.1",
|
||||
"@getpaseo/protocol": "0.9.1",
|
||||
"@getpaseo/client": "0.9.2",
|
||||
"@getpaseo/protocol": "0.9.2",
|
||||
"@types/node": "^20.9.0",
|
||||
"@types/react": "~19.2.0",
|
||||
"react": "19.1.0",
|
||||
@@ -38714,8 +38714,8 @@
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@getpaseo/client": "0.9.1",
|
||||
"@getpaseo/protocol": "0.9.1",
|
||||
"@getpaseo/client": "0.9.2",
|
||||
"@getpaseo/protocol": "0.9.2",
|
||||
"react": "~19.1.0",
|
||||
"react-native": ">=0.81.5",
|
||||
"zod": "^4.4.3"
|
||||
@@ -38737,7 +38737,7 @@
|
||||
},
|
||||
"packages/protocol": {
|
||||
"name": "@getpaseo/protocol",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"dependencies": {
|
||||
"semver": "^7.8.5",
|
||||
"zod": "^4.4.3"
|
||||
@@ -38764,7 +38764,7 @@
|
||||
},
|
||||
"packages/relay": {
|
||||
"name": "@getpaseo/relay",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"dependencies": {
|
||||
"base64-js": "^1.5.1",
|
||||
"tweetnacl": "^1.0.3",
|
||||
@@ -38982,16 +38982,16 @@
|
||||
},
|
||||
"packages/server": {
|
||||
"name": "@getpaseo/server",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"dependencies": {
|
||||
"@agentclientprotocol/sdk": "^0.17.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.246",
|
||||
"@anthropic-ai/sdk": "^0.104.2",
|
||||
"@getpaseo/client": "0.9.1",
|
||||
"@getpaseo/highlight": "0.9.1",
|
||||
"@getpaseo/plugin": "0.9.1",
|
||||
"@getpaseo/protocol": "0.9.1",
|
||||
"@getpaseo/relay": "0.9.1",
|
||||
"@getpaseo/client": "0.9.2",
|
||||
"@getpaseo/highlight": "0.9.2",
|
||||
"@getpaseo/plugin": "0.9.2",
|
||||
"@getpaseo/protocol": "0.9.2",
|
||||
"@getpaseo/relay": "0.9.2",
|
||||
"@isaacs/ttlcache": "^2.1.4",
|
||||
"@modelcontextprotocol/sdk": "1.29.0",
|
||||
"@opencode-ai/sdk": "1.14.46",
|
||||
@@ -39895,7 +39895,7 @@
|
||||
},
|
||||
"packages/website": {
|
||||
"name": "@getpaseo/website",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"dependencies": {
|
||||
"@cloudflare/vite-plugin": "^1.29.1",
|
||||
"@cloudflare/workers-types": "^4.20260317.1",
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "paseo",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"private": true,
|
||||
"description": "Paseo: voice-controlled development environment for local AI coding agents",
|
||||
"keywords": [
|
||||
|
||||
@@ -19,7 +19,8 @@
|
||||
"APP_VARIANT": "production"
|
||||
},
|
||||
"android": {
|
||||
"resourceClass": "large"
|
||||
"resourceClass": "large",
|
||||
"gradleCommand": ":app:bundleRelease -x lint -x lintVitalAnalyzeRelease -x lintVitalRelease -x generateReleaseLintModel -x generateReleaseLintVitalModel"
|
||||
}
|
||||
},
|
||||
"production-apk": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@getpaseo/app",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"private": true,
|
||||
"main": "index.ts",
|
||||
"scripts": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@getpaseo/cli",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"description": "Paseo CLI - control your AI coding agents from the command line",
|
||||
"bin": {
|
||||
"paseo": "bin/paseo"
|
||||
@@ -28,9 +28,9 @@
|
||||
},
|
||||
"dependencies": {
|
||||
"@clack/prompts": "^1.0.0",
|
||||
"@getpaseo/client": "0.9.1",
|
||||
"@getpaseo/protocol": "0.9.1",
|
||||
"@getpaseo/server": "0.9.1",
|
||||
"@getpaseo/client": "0.9.2",
|
||||
"@getpaseo/protocol": "0.9.2",
|
||||
"@getpaseo/server": "0.9.2",
|
||||
"chalk": "^5.3.0",
|
||||
"commander": "^12.0.0",
|
||||
"mime-types": "^2.1.35",
|
||||
|
||||
@@ -5,6 +5,7 @@ import net from "node:net";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
import { fileURLToPath, pathToFileURL } from "node:url";
|
||||
import { hashDaemonPassword } from "@getpaseo/server/auth";
|
||||
import { startDaemonInstance, readDaemonInstance } from "@getpaseo/server/daemon-control";
|
||||
import { expect, test } from "vitest";
|
||||
import { connectToDaemon } from "../../utils/client.js";
|
||||
@@ -196,6 +197,26 @@ test("removed flags and ambiguous targets fail before side effects; observation
|
||||
}
|
||||
}, 30_000);
|
||||
|
||||
test("status reports the server id of a password-protected daemon to a caller without the password", async () => {
|
||||
const f = await fixture();
|
||||
const home = f.homes[0]!;
|
||||
try {
|
||||
await f.configure(home, `127.0.0.1:${await port()}`);
|
||||
const configPath = path.join(home, "config.json");
|
||||
const config = JSON.parse(await readFile(configPath, "utf8"));
|
||||
config.daemon.auth = { password: await hashDaemonPassword("secret") };
|
||||
await writeFile(configPath, JSON.stringify(config));
|
||||
await f.ok(["start", "--home", home, "--timeout", "30"]);
|
||||
const authenticated = await f.liveStatus(home, { PASEO_PASSWORD: "secret" });
|
||||
expect(await f.ok(["daemon", "status", "--home", home])).toMatchObject({
|
||||
connectedDaemon: "auth_required",
|
||||
serverId: authenticated.serverId,
|
||||
});
|
||||
} finally {
|
||||
await f.close();
|
||||
}
|
||||
}, 60_000);
|
||||
|
||||
test("an occupied initial or replacement address fails without false readiness or killing its owner", async () => {
|
||||
const f = await fixture();
|
||||
const [a, b] = f.homes as [string, string];
|
||||
|
||||
@@ -124,6 +124,7 @@ function localStatus(home: string, instance: Awaited<ReturnType<typeof readDaemo
|
||||
pid: instance?.pid ?? null,
|
||||
startedAt: instance?.startedAt ?? null,
|
||||
listen: instance?.listen ?? null,
|
||||
serverId: instance?.serverId ?? undefined,
|
||||
hostname: instance?.hostname ?? null,
|
||||
configuredListen: config.listen,
|
||||
localDaemon,
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@getpaseo/client",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"description": "Paseo client SDK package",
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -39,8 +39,8 @@
|
||||
"test": "vitest run"
|
||||
},
|
||||
"dependencies": {
|
||||
"@getpaseo/protocol": "0.9.1",
|
||||
"@getpaseo/relay": "0.9.1",
|
||||
"@getpaseo/protocol": "0.9.2",
|
||||
"@getpaseo/relay": "0.9.2",
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@getpaseo/desktop",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"private": true,
|
||||
"description": "Paseo desktop app (Electron wrapper)",
|
||||
"homepage": "https://paseo.sh",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@getpaseo/expo-two-way-audio",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"description": "Native module for two way audio streaming",
|
||||
"keywords": [
|
||||
"ExpoTwoWayAudio",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@getpaseo/highlight",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"files": [
|
||||
"dist",
|
||||
"!dist/**/*.map",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@getpaseo/plugin",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"description": "Paseo plugin SDK package",
|
||||
"files": [
|
||||
"dist",
|
||||
@@ -65,8 +65,8 @@
|
||||
"use-sync-external-store": "^1.6.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@getpaseo/client": "0.9.1",
|
||||
"@getpaseo/protocol": "0.9.1",
|
||||
"@getpaseo/client": "0.9.2",
|
||||
"@getpaseo/protocol": "0.9.2",
|
||||
"@types/node": "^20.9.0",
|
||||
"@types/react": "~19.2.0",
|
||||
"react": "19.1.0",
|
||||
@@ -76,8 +76,8 @@
|
||||
"zod": "^4.4.3"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@getpaseo/client": "0.9.1",
|
||||
"@getpaseo/protocol": "0.9.1",
|
||||
"@getpaseo/client": "0.9.2",
|
||||
"@getpaseo/protocol": "0.9.2",
|
||||
"react": "~19.1.0",
|
||||
"react-native": ">=0.81.5",
|
||||
"zod": "^4.4.3"
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@getpaseo/protocol",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"description": "Paseo shared protocol schemas and wire types",
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@getpaseo/relay",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"description": "Paseo relay for bridging daemon and client connections",
|
||||
"files": [
|
||||
"dist",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@getpaseo/server",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"description": "Paseo backend server",
|
||||
"files": [
|
||||
"dist/server",
|
||||
@@ -107,11 +107,11 @@
|
||||
"@agentclientprotocol/sdk": "^0.17.1",
|
||||
"@anthropic-ai/claude-agent-sdk": "0.3.246",
|
||||
"@anthropic-ai/sdk": "^0.104.2",
|
||||
"@getpaseo/client": "0.9.1",
|
||||
"@getpaseo/highlight": "0.9.1",
|
||||
"@getpaseo/plugin": "0.9.1",
|
||||
"@getpaseo/protocol": "0.9.1",
|
||||
"@getpaseo/relay": "0.9.1",
|
||||
"@getpaseo/client": "0.9.2",
|
||||
"@getpaseo/highlight": "0.9.2",
|
||||
"@getpaseo/plugin": "0.9.2",
|
||||
"@getpaseo/protocol": "0.9.2",
|
||||
"@getpaseo/relay": "0.9.2",
|
||||
"@isaacs/ttlcache": "^2.1.4",
|
||||
"@modelcontextprotocol/sdk": "1.29.0",
|
||||
"@opencode-ai/sdk": "1.14.46",
|
||||
|
||||
@@ -172,10 +172,11 @@ async function main(): Promise<void> {
|
||||
: undefined,
|
||||
restartOnCrash: true,
|
||||
logFile: supervisorLogFile,
|
||||
onWorkerReady: async ({ listen }) => {
|
||||
await updatePidLock(paseoHome, { listen }, { ownerPid: process.pid });
|
||||
onWorkerReady: async ({ listen, serverId }) => {
|
||||
await updatePidLock(paseoHome, { listen, serverId }, { ownerPid: process.pid });
|
||||
},
|
||||
onWorkerExit: () => updatePidLock(paseoHome, { listen: null }, { ownerPid: process.pid }),
|
||||
onWorkerExit: () =>
|
||||
updatePidLock(paseoHome, { listen: null, serverId: null }, { ownerPid: process.pid }),
|
||||
onSupervisorExit: releaseLock,
|
||||
});
|
||||
requestSupervisorShutdown = supervisor.requestShutdown;
|
||||
|
||||
@@ -38,7 +38,7 @@ async function runSupervisorFixture(options: {
|
||||
|
||||
await writeFile(
|
||||
workerPath,
|
||||
`process.send?.({ type: "paseo:ready", listen: "fixture" });\n${options.workerSource}`,
|
||||
`process.send?.({ type: "paseo:ready", listen: "fixture", serverId: "srv_fixture" });\n${options.workerSource}`,
|
||||
);
|
||||
await writeFile(
|
||||
runnerPath,
|
||||
|
||||
@@ -92,7 +92,7 @@ test("a replacement worker must independently reach ready", async () => {
|
||||
process.on("message", message => {
|
||||
if (message.type === "paseo:graceful-shutdown") process.exit(0);
|
||||
});
|
||||
process.send({ type: "paseo:ready", listen: "test-endpoint" });
|
||||
process.send({ type: "paseo:ready", listen: "test-endpoint", serverId: "srv_test" });
|
||||
process.send({ type: "paseo:restart" });
|
||||
`);
|
||||
expect(result.code).toBe(1);
|
||||
@@ -102,7 +102,7 @@ test("a replacement worker must independently reach ready", async () => {
|
||||
test("exit clears publication after an in-flight ready write", async () => {
|
||||
const result = await runWorker(
|
||||
`
|
||||
process.send({ type: "paseo:ready", listen: "test-endpoint" }, () => process.exit(0));
|
||||
process.send({ type: "paseo:ready", listen: "test-endpoint", serverId: "srv_test" }, () => process.exit(0));
|
||||
`,
|
||||
100,
|
||||
);
|
||||
|
||||
@@ -23,6 +23,7 @@ type WorkerLifecycleMessage =
|
||||
| {
|
||||
type: "paseo:ready";
|
||||
listen: string;
|
||||
serverId: string;
|
||||
}
|
||||
| {
|
||||
type: "paseo:restart";
|
||||
@@ -50,7 +51,7 @@ interface SupervisorOptions {
|
||||
args: string[];
|
||||
env?: NodeJS.ProcessEnv;
|
||||
} | null;
|
||||
onWorkerReady?: (message: { listen: string }) => Promise<void> | void;
|
||||
onWorkerReady?: (message: { listen: string; serverId: string }) => Promise<void> | void;
|
||||
onWorkerExit?: () => Promise<void> | void;
|
||||
restartOnCrash?: boolean;
|
||||
onSupervisorExit?: () => Promise<void> | void;
|
||||
@@ -78,11 +79,14 @@ function parseLifecycleMessage(msg: unknown): WorkerLifecycleMessage | null {
|
||||
};
|
||||
}
|
||||
if (type === "paseo:ready") {
|
||||
const listen = (msg as { listen?: unknown }).listen;
|
||||
const { listen, serverId } = msg as { listen?: unknown; serverId?: unknown };
|
||||
if (typeof listen !== "string" || listen.trim().length === 0) {
|
||||
return null;
|
||||
}
|
||||
return { type: "paseo:ready", listen };
|
||||
if (typeof serverId !== "string" || serverId.trim().length === 0) {
|
||||
return null;
|
||||
}
|
||||
return { type: "paseo:ready", listen, serverId };
|
||||
}
|
||||
if (type === "paseo:restart") {
|
||||
const reason = (msg as { reason?: unknown }).reason;
|
||||
@@ -289,7 +293,12 @@ export function runSupervisor(options: SupervisorOptions): SupervisorController
|
||||
reachedReady = true;
|
||||
writeLifecycleLog("Worker ready", { listen: lifecycleMessage.listen });
|
||||
publication = publication
|
||||
.then(() => options.onWorkerReady?.({ listen: lifecycleMessage.listen }))
|
||||
.then(() =>
|
||||
options.onWorkerReady?.({
|
||||
listen: lifecycleMessage.listen,
|
||||
serverId: lifecycleMessage.serverId,
|
||||
}),
|
||||
)
|
||||
.catch((error) => {
|
||||
lifecycleFailed = true;
|
||||
const message = error instanceof Error ? error.message : String(error);
|
||||
|
||||
@@ -471,6 +471,7 @@ export interface PaseoDaemon {
|
||||
start(): Promise<void>;
|
||||
stop(): Promise<void>;
|
||||
getListenTarget(): ListenTarget | null;
|
||||
getServerId(): string;
|
||||
}
|
||||
|
||||
export interface PaseoDaemonDependencies {
|
||||
@@ -1831,6 +1832,7 @@ export async function createPaseoDaemon(
|
||||
start,
|
||||
stop,
|
||||
getListenTarget: () => boundListenTarget,
|
||||
getServerId: () => serverId,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
import { expect, test } from "vitest";
|
||||
import { mkdtempSync, rmSync } from "node:fs";
|
||||
import { tmpdir } from "node:os";
|
||||
import path from "node:path";
|
||||
|
||||
import { createTestPaseoDaemon } from "../test-utils/paseo-daemon.js";
|
||||
import { DaemonClient } from "../test-utils/daemon-client.js";
|
||||
|
||||
// Repro for #4427: SDK, CLI, and MCP callers create agents with a prompt but no
|
||||
// clientMessageId. The fake provider does not echo user messages, so the prompt
|
||||
// only reaches the timeline if the daemon records it when the turn starts.
|
||||
for (const clientMessageId of [undefined, "create-msg-1"]) {
|
||||
test(`initial prompt reaches the timeline without a provider echo (clientMessageId: ${
|
||||
clientMessageId ?? "none"
|
||||
})`, async () => {
|
||||
const cwd = mkdtempSync(path.join(tmpdir(), "initial-prompt-timeline-"));
|
||||
const daemon = await createTestPaseoDaemon();
|
||||
const client = new DaemonClient({
|
||||
url: `ws://127.0.0.1:${daemon.port}/ws`,
|
||||
});
|
||||
try {
|
||||
await client.connect();
|
||||
await client.fetchAgents({ subscribe: {} });
|
||||
const agent = await client.createAgent({
|
||||
provider: "codex",
|
||||
cwd,
|
||||
initialPrompt: "hello from create",
|
||||
...(clientMessageId ? { clientMessageId } : {}),
|
||||
});
|
||||
await client.waitForFinish(agent.id, 10_000);
|
||||
|
||||
const timeline = await client.fetchAgentTimeline(agent.id, {
|
||||
direction: "tail",
|
||||
limit: 0,
|
||||
});
|
||||
const userMessages = timeline.entries.flatMap((entry) =>
|
||||
entry.item.type === "user_message" ? [entry.item.text] : []
|
||||
);
|
||||
expect(userMessages).toEqual(["hello from create"]);
|
||||
} finally {
|
||||
await client.close().catch(() => undefined);
|
||||
await daemon.close();
|
||||
rmSync(cwd, { recursive: true, force: true });
|
||||
}
|
||||
}, 30_000);
|
||||
}
|
||||
@@ -17,6 +17,7 @@ type SupervisorLifecycleMessage =
|
||||
| {
|
||||
type: "paseo:ready";
|
||||
listen: string;
|
||||
serverId: string;
|
||||
}
|
||||
| {
|
||||
type: "paseo:restart";
|
||||
@@ -327,7 +328,11 @@ async function main() {
|
||||
if (!listen) {
|
||||
throw new Error("Daemon did not expose a listen target after startup");
|
||||
}
|
||||
sendSupervisorLifecycleMessage({ type: "paseo:ready", listen });
|
||||
sendSupervisorLifecycleMessage({
|
||||
type: "paseo:ready",
|
||||
listen,
|
||||
serverId: daemon.getServerId(),
|
||||
});
|
||||
} catch (err) {
|
||||
logger.fatal({ err }, "Daemon failed to start listening");
|
||||
throw err;
|
||||
|
||||
@@ -765,6 +765,34 @@ describe("config.json saved with a UTF-8 byte order mark", () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe("readPersistedConfig with an unreadable config.json", () => {
|
||||
test("names the file when it is not valid JSON", () => {
|
||||
const home = createTempHome();
|
||||
const configPath = path.join(home, "config.json");
|
||||
try {
|
||||
writeFileSync(configPath, '{"version":1,');
|
||||
|
||||
expect(() => readPersistedConfig(home)).toThrow(`[Config] Invalid JSON in ${configPath}: `);
|
||||
} finally {
|
||||
rmSync(home, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
test("names the file and the field when it does not match the schema", () => {
|
||||
const home = createTempHome();
|
||||
const configPath = path.join(home, "config.json");
|
||||
try {
|
||||
writeFileSync(configPath, '{"daemon":{"listen":5}}');
|
||||
|
||||
expect(() => readPersistedConfig(home)).toThrow(
|
||||
`[Config] Invalid config in ${configPath}:\n - daemon.listen: `,
|
||||
);
|
||||
} finally {
|
||||
rmSync(home, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
describe.skipIf(process.platform === "win32")("persisted config file permissions", () => {
|
||||
test("initializes config.json with private permissions", () => {
|
||||
const home = createTempHome();
|
||||
|
||||
@@ -436,6 +436,29 @@ export function loadPersistedConfig(paseoHome: string, logger?: LoggerLike): Per
|
||||
});
|
||||
}
|
||||
|
||||
const config = parseConfigFile(configPath, raw);
|
||||
log?.info(`Loaded from ${configPath}`);
|
||||
return config;
|
||||
}
|
||||
|
||||
/** Observe the file without initializing a home, identity, or default configuration. */
|
||||
export function readPersistedConfig(
|
||||
paseoHome: string,
|
||||
options: { defaultsIfMissing?: boolean } = {},
|
||||
): PersistedConfig {
|
||||
const configPath = getConfigPath(paseoHome);
|
||||
let raw: string;
|
||||
try {
|
||||
raw = readFileSync(configPath, "utf8");
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === "ENOENT")
|
||||
return options.defaultsIfMissing ? structuredClone(DEFAULT_PERSISTED_CONFIG) : {};
|
||||
throw error;
|
||||
}
|
||||
return parseConfigFile(configPath, raw);
|
||||
}
|
||||
|
||||
function parseConfigFile(configPath: string, raw: string): PersistedConfig {
|
||||
let parsed: unknown;
|
||||
try {
|
||||
parsed = parseConfigText(raw);
|
||||
@@ -446,37 +469,16 @@ export function loadPersistedConfig(paseoHome: string, logger?: LoggerLike): Per
|
||||
});
|
||||
}
|
||||
|
||||
const migrated = stripRemovedConfigFields(parsed);
|
||||
const result = PersistedConfigSchema.safeParse(migrated);
|
||||
const result = PersistedConfigSchema.safeParse(stripRemovedConfigFields(parsed));
|
||||
if (!result.success) {
|
||||
const issues = result.error.issues
|
||||
.map((i) => ` - ${i.path.join(".")}: ${i.message}`)
|
||||
.join("\n");
|
||||
throw new Error(`[Config] Invalid config in ${configPath}:\n${issues}`);
|
||||
}
|
||||
|
||||
log?.info(`Loaded from ${configPath}`);
|
||||
return result.data as PersistedConfig;
|
||||
}
|
||||
|
||||
/** Observe the file without initializing a home, identity, or default configuration. */
|
||||
export function readPersistedConfig(
|
||||
paseoHome: string,
|
||||
options: { defaultsIfMissing?: boolean } = {},
|
||||
): PersistedConfig {
|
||||
let raw: string;
|
||||
try {
|
||||
raw = readFileSync(getConfigPath(paseoHome), "utf8");
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === "ENOENT")
|
||||
return options.defaultsIfMissing ? structuredClone(DEFAULT_PERSISTED_CONFIG) : {};
|
||||
throw error;
|
||||
}
|
||||
return PersistedConfigSchema.parse(
|
||||
stripRemovedConfigFields(parseConfigText(raw)),
|
||||
) as PersistedConfig;
|
||||
}
|
||||
|
||||
/** Editors such as Windows Notepad save UTF-8 with a byte order mark, which JSON.parse rejects. */
|
||||
function parseConfigText(raw: string): unknown {
|
||||
return JSON.parse(raw.replace(/^\uFEFF/, ""));
|
||||
|
||||
@@ -11,6 +11,7 @@ export const pidLockInfoSchema = z.object({
|
||||
hostname: z.string(),
|
||||
uid: z.number(),
|
||||
listen: z.string().nullable(),
|
||||
serverId: z.string().nullable().optional(),
|
||||
desktopManaged: z.boolean().optional(),
|
||||
heartbeat: z.literal(true).optional(),
|
||||
});
|
||||
@@ -319,7 +320,7 @@ export function startPidLockHeartbeat(
|
||||
|
||||
export async function updatePidLock(
|
||||
paseoHome: string,
|
||||
patch: { listen: string | null },
|
||||
patch: { listen: string; serverId: string } | { listen: null; serverId: null },
|
||||
options?: { ownerPid?: number },
|
||||
): Promise<void> {
|
||||
const pidPath = getPidFilePath(paseoHome);
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@getpaseo/website",
|
||||
"version": "0.9.1",
|
||||
"version": "0.9.2",
|
||||
"private": true,
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
|
||||
Reference in New Issue
Block a user