From 2fa7d44199b957113fb88df3b8cc8b4b3c0c0fac Mon Sep 17 00:00:00 2001 From: Igor Warzocha Date: Wed, 17 Jun 2026 19:42:18 +0100 Subject: [PATCH 1/4] Bump Pi SDK packages to 0.79.6 --- bun.lock | 42 ++++++++++++++----- desktop/runtime-host/live-runtime-factory.ts | 4 +- desktop/runtime-host/skill-creator-service.ts | 4 +- desktop/runtime/composer-state.ts | 10 ++--- .../runtime/isolated-settings-manager.test.ts | 14 +++---- desktop/runtime/isolated-settings-manager.ts | 8 ++-- desktop/runtime/runtime-registry.ts | 4 +- package.json | 8 ++-- 8 files changed, 58 insertions(+), 36 deletions(-) diff --git a/bun.lock b/bun.lock index 6ed945140..9837b282a 100644 --- a/bun.lock +++ b/bun.lock @@ -9,10 +9,10 @@ "@dnd-kit/modifiers": "^9.0.0", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", - "@earendil-works/pi-agent-core": "0.79.1", - "@earendil-works/pi-ai": "0.79.1", - "@earendil-works/pi-coding-agent": "0.79.1", - "@earendil-works/pi-tui": "0.79.1", + "@earendil-works/pi-agent-core": "0.79.6", + "@earendil-works/pi-ai": "0.79.6", + "@earendil-works/pi-coding-agent": "0.79.6", + "@earendil-works/pi-tui": "0.79.6", "@fontsource-variable/geist-mono": "^5.2.8", "@fontsource-variable/inter": "^5.2.8", "@mdxeditor/editor": "4.0.2", @@ -255,13 +255,13 @@ "@dnd-kit/utilities": ["@dnd-kit/utilities@3.2.2", "", { "dependencies": { "tslib": "^2.0.0" }, "peerDependencies": { "react": ">=16.8.0" } }, "sha512-+MKAJEOfaBe5SmV6t34p80MMKhjvUz0vRrvVJbPT0WElzaOJ/1xs+D+KDv+tD/NE5ujfrChEcshd4fLn0wpiqg=="], - "@earendil-works/pi-agent-core": ["@earendil-works/pi-agent-core@0.79.1", "", { "dependencies": { "@earendil-works/pi-ai": "^0.79.1", "ignore": "7.0.5", "typebox": "1.1.38", "yaml": "2.9.0" } }, "sha512-PBPjBa2YBm9jauiLtHAKaSfVJ4Dvm3/nK/bR/oHebLjwBCS2tGx3aQDX7MSGAOXi6BejlhzbB/z82BkyAyNjjQ=="], + "@earendil-works/pi-agent-core": ["@earendil-works/pi-agent-core@0.79.6", "", { "dependencies": { "@earendil-works/pi-ai": "^0.79.6", "ignore": "7.0.5", "typebox": "1.1.38", "yaml": "2.9.0" } }, "sha512-SXZc4rQI+3zgUmAJDbU0GzFEHCPHbhItjN7QLsahj3TKfQAon4guwCqsrwZBLH5lPcub2+evTojPNrPItL62tA=="], - "@earendil-works/pi-ai": ["@earendil-works/pi-ai@0.79.1", "", { "dependencies": { "@anthropic-ai/sdk": "0.91.1", "@aws-sdk/client-bedrock-runtime": "3.1048.0", "@google/genai": "1.52.0", "@mistralai/mistralai": "2.2.1", "@smithy/node-http-handler": "4.7.3", "http-proxy-agent": "7.0.2", "https-proxy-agent": "7.0.6", "openai": "6.26.0", "partial-json": "0.1.7", "typebox": "1.1.38" }, "bin": { "pi-ai": "dist/cli.js" } }, "sha512-UnORwrcsTNLm4StEvoM8iEom0u87Te7BXEWxhec3iNXygWD6eEBosUoq9ddcveqtj/QpUZBMPWUu81cCtZxzkQ=="], + "@earendil-works/pi-ai": ["@earendil-works/pi-ai@0.79.6", "", { "dependencies": { "@anthropic-ai/sdk": "0.91.1", "@aws-sdk/client-bedrock-runtime": "3.1048.0", "@google/genai": "1.52.0", "@mistralai/mistralai": "2.2.1", "@smithy/node-http-handler": "4.7.3", "http-proxy-agent": "7.0.2", "https-proxy-agent": "7.0.6", "openai": "6.26.0", "partial-json": "0.1.7", "typebox": "1.1.38" }, "bin": { "pi-ai": "dist/cli.js" } }, "sha512-KGepEdgEeWDs7Imwlp96tBsO8TjSIpcDBvazsCDtHRa81+uwJI/YGetTegI52pMlKhVpJFLIGajRi4PCGC5MUg=="], - "@earendil-works/pi-coding-agent": ["@earendil-works/pi-coding-agent@0.79.1", "", { "dependencies": { "@earendil-works/pi-agent-core": "^0.79.1", "@earendil-works/pi-ai": "^0.79.1", "@earendil-works/pi-tui": "^0.79.1", "@silvia-odwyer/photon-node": "0.3.4", "chalk": "5.6.2", "cross-spawn": "7.0.6", "diff": "8.0.4", "glob": "13.0.6", "highlight.js": "10.7.3", "hosted-git-info": "9.0.3", "ignore": "7.0.5", "jiti": "2.7.0", "minimatch": "10.2.5", "proper-lockfile": "4.1.2", "typebox": "1.1.38", "undici": "8.3.0", "yaml": "2.9.0" }, "optionalDependencies": { "@mariozechner/clipboard": "0.3.9" }, "bin": { "pi": "dist/cli.js" } }, "sha512-dLnje4U5H3/ZytJpvhjhPINeDT/yvx85e4OH/ziMQRLpPlfNP12/peY9jRQd4W11Xth2+y2xGAFwS+NeVf2ZwA=="], + "@earendil-works/pi-coding-agent": ["@earendil-works/pi-coding-agent@0.79.6", "", { "dependencies": { "@earendil-works/pi-agent-core": "^0.79.6", "@earendil-works/pi-ai": "^0.79.6", "@earendil-works/pi-tui": "^0.79.6", "@silvia-odwyer/photon-node": "0.3.4", "chalk": "5.6.2", "cross-spawn": "7.0.6", "diff": "8.0.4", "glob": "13.0.6", "highlight.js": "10.7.3", "hosted-git-info": "9.0.3", "ignore": "7.0.5", "jiti": "2.7.0", "minimatch": "10.2.5", "proper-lockfile": "4.1.2", "semver": "7.8.0", "typebox": "1.1.38", "undici": "8.3.0", "yaml": "2.9.0" }, "optionalDependencies": { "@mariozechner/clipboard": "0.3.9" }, "bin": { "pi": "dist/cli.js" } }, "sha512-xPQDoA3+4Q8UsInST8OGFHPHyi7JQIbx51ku5kf2VuhXrrTv/npjVTXYD3A3D5xs6QRebV0B2mQqHfXaxQAplw=="], - "@earendil-works/pi-tui": ["@earendil-works/pi-tui@0.79.1", "", { "dependencies": { "get-east-asian-width": "1.6.0", "marked": "15.0.12" } }, "sha512-YvZCMfSE0YDSLNklAwMY6LC6SyEgnP0zMOoioTLNnXFNdexrCexMJdee7iDJsNcFlKt7+DVLccomuURtZS1C6g=="], + "@earendil-works/pi-tui": ["@earendil-works/pi-tui@0.79.6", "", { "dependencies": { "get-east-asian-width": "1.6.0", "marked": "18.0.5" } }, "sha512-6JCq780X0UuqvJsUDSJmi4V54ObB8qSwFQiBOI1jhPpC+Ydusd8SEXn2HtyIqve/utMgwcZT9aOyZM72m26A0w=="], "@effect/platform-node-shared": ["@effect/platform-node-shared@4.0.0-beta.70", "", { "dependencies": { "@types/ws": "^8.18.1", "ws": "^8.20.0" }, "peerDependencies": { "effect": "^4.0.0-beta.70" } }, "sha512-3VXuL63IDmq13We+ApRKn2JW3Rb9g5gj1YEmfb8u2b73norur1VsIJ/pRE4qjShevg19dQYi2JsLawSZ6gApug=="], @@ -1677,7 +1677,7 @@ "markdown-table": ["markdown-table@3.0.4", "", {}, "sha512-wiYz4+JrLyb/DqW2hkFJxP7Vd7JuTDm77fvbM8VfEQdmSMqcImWeeRbHwZjBjIFki/VaMK2BhFi7oUUZeM5bqw=="], - "marked": ["marked@15.0.12", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA=="], + "marked": ["marked@18.0.5", "", { "bin": { "marked": "bin/marked.js" } }, "sha512-S6GcvALHg6K4ohtu4E7x0a1AqhAjp6cV8KhLSyN9qVapnzJkusVBxZRcIU9AeYsbe6P1hKDusSbEOzGyyuce6w=="], "matcher": ["matcher@3.0.0", "", { "dependencies": { "escape-string-regexp": "^4.0.0" } }, "sha512-OkeDaAZ/bQCxeFAozM55PKcKU0yJMPGifLwV4Qgjitu+5MoAfSQN4lsLJeXZ1b8w0x+/Emda6MZgXS1jvsapng=="], @@ -2085,7 +2085,7 @@ "scheduler": ["scheduler@0.27.0", "", {}, "sha512-eNv+WrVbKu1f3vbYJT/xtiF5syA5HPIMtf9IgY/nKg0sWqzAUEvqY/xm7OcZc/qafLx/iO9FgOmeSAp4v5ti/Q=="], - "semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "semver": ["semver@7.8.0", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-AcM7dV/5ul4EekoQ29Agm5vri8JNqRyj39o0qpX6vDF2GZrtutZl5RwgD1XnZjiTAfncsJhMI48QQH3sN87YNA=="], "semver-compare": ["semver-compare@1.0.0", "", {}, "sha512-YM3/ITh2MJ5MtzaM429anh+x2jiLVjqILF4m4oyQB18W7Ggea7BfqdH/wGMK7dDiMghv/6WG7znWMwUDzJiXow=="], @@ -2429,6 +2429,8 @@ "@electron/fuses/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], + "@electron/get/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "@electron/get/undici": ["undici@7.27.2", "", {}, "sha512-uZsKNuzQxDMUY6M3pIMvy5tvlGmtq8XJ2oLAkfRKGNu+1VQAIvLy2xIVG5ATZl5wDXl/tddByAWCizRbOme+TA=="], "@electron/notarize/fs-extra": ["fs-extra@9.1.0", "", { "dependencies": { "at-least-node": "^1.0.0", "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", "universalify": "^2.0.0" } }, "sha512-hcg3ZmepS30/7BSFqRvoo3DOMQu7IjqxO5nCDt+zM9XWjb33Wg7ziNT+Qvqbuc3+gWpzO02JubVyk2G4Zvo1OQ=="], @@ -2453,6 +2455,8 @@ "@npmcli/agent/lru-cache": ["lru-cache@10.4.3", "", {}, "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ=="], + "@npmcli/fs/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "@pierre/diffs/diff": ["diff@8.0.3", "", {}, "sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ=="], "@tailwindcss/oxide-wasm32-wasi/@emnapi/core": ["@emnapi/core@1.10.0", "", { "dependencies": { "@emnapi/wasi-threads": "1.2.1", "tslib": "^2.4.0" }, "bundled": true }, "sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw=="], @@ -2489,6 +2493,8 @@ "app-builder-lib/hosted-git-info": ["hosted-git-info@4.1.0", "", { "dependencies": { "lru-cache": "^6.0.0" } }, "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA=="], + "app-builder-lib/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "app-builder-lib/tar": ["tar@7.5.15", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ=="], "bindings/file-uri-to-path": ["file-uri-to-path@1.0.0", "", {}, "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw=="], @@ -2511,6 +2517,8 @@ "clone-response/mimic-response": ["mimic-response@1.0.1", "", {}, "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ=="], + "conf/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "cross-spawn/which": ["which@2.0.2", "", { "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "./bin/node-which" } }, "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA=="], "dir-compare/minimatch": ["minimatch@3.1.5", "", { "dependencies": { "brace-expansion": "^1.1.7" } }, "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w=="], @@ -2543,6 +2551,8 @@ "foreground-child/signal-exit": ["signal-exit@4.1.0", "", {}, "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw=="], + "global-agent/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "iconv-corefoundation/cli-truncate": ["cli-truncate@2.1.0", "", { "dependencies": { "slice-ansi": "^3.0.0", "string-width": "^4.2.0" } }, "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg=="], "iconv-corefoundation/node-addon-api": ["node-addon-api@1.7.2", "", {}, "sha512-ibPK3iA+vaY1eEjESkQkM0BbCqFOaZMiXRTtdB0u7b4djtY6JnsjvPdUHVMg6xQt3B8fpTTWHI9A+ADjM9frzg=="], @@ -2563,8 +2573,14 @@ "minipass-sized/minipass": ["minipass@3.3.6", "", { "dependencies": { "yallist": "^4.0.0" } }, "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw=="], + "node-abi/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + + "node-api-version/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "node-gyp/env-paths": ["env-paths@2.2.1", "", {}, "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A=="], + "node-gyp/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "node-gyp/tar": ["tar@7.5.15", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-dzGK0boVlC4W5QFuQN1EFSl3bIDYsk7Tj40U6eIBnK2k/8ml7TZ5agbI5j5+qnoVcAA+rNtBml8SEiLxZpNqRQ=="], "node-gyp/undici": ["undici@6.26.0", "", {}, "sha512-4yqz8a3n5HmGTlsbADNtr/dJlhkh/55Rq798G6ibiULcXbDtaLpTl1pvdqcbFfeoj3iSi52lePFM7h9H21cw/A=="], @@ -2597,6 +2613,8 @@ "serialize-error/type-fest": ["type-fest@0.13.1", "", {}, "sha512-34R7HTnG0XIJcBSn5XhDd7nNFPRcXYRZrBB2O2jdKqYODldSzBAqzsWoZYYvduky73toYS/ESqxPvkDf/F0XMg=="], + "simple-update-notifier/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "slice-ansi/ansi-styles": ["ansi-styles@6.2.3", "", {}, "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg=="], "slice-ansi/is-fullwidth-code-point": ["is-fullwidth-code-point@5.1.0", "", { "dependencies": { "get-east-asian-width": "^1.3.1" } }, "sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ=="], @@ -2641,6 +2659,8 @@ "@electron/fuses/chalk/supports-color": ["supports-color@7.2.0", "", { "dependencies": { "has-flag": "^4.0.0" } }, "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw=="], + "@electron/rebuild/node-abi/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "@electron/universal/minimatch/brace-expansion": ["brace-expansion@2.1.0", "", { "dependencies": { "balanced-match": "^1.0.0" } }, "sha512-TN1kCZAgdgweJhWWpgKYrQaMNHcDULHkWwQIspdtjV4Y5aurRdZpjAqn6yX3FPqTA9ngHCc4hJxMAMgGfve85w=="], "@isaacs/cliui/string-width/emoji-regex": ["emoji-regex@9.2.2", "", {}, "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg=="], @@ -2703,6 +2723,8 @@ "electron-builder-squirrel-windows/app-builder-lib/jiti": ["jiti@2.6.1", "", { "bin": { "jiti": "lib/jiti-cli.mjs" } }, "sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ=="], + "electron-builder-squirrel-windows/app-builder-lib/semver": ["semver@7.7.4", "", { "bin": { "semver": "bin/semver.js" } }, "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA=="], + "electron-builder-squirrel-windows/app-builder-lib/tar": ["tar@7.5.13", "", { "dependencies": { "@isaacs/fs-minipass": "^4.0.0", "chownr": "^3.0.0", "minipass": "^7.1.2", "minizlib": "^3.1.0", "yallist": "^5.0.0" } }, "sha512-tOG/7GyXpFevhXVh8jOPJrmtRpOTsYqUIkVdVooZYJS/z8WhfQUX8RJILmeuJNinGAMSu1veBr4asSHFt5/hng=="], "electron-builder-squirrel-windows/builder-util/builder-util-runtime": ["builder-util-runtime@9.5.1", "", { "dependencies": { "debug": "^4.3.4", "sax": "^1.2.4" } }, "sha512-qt41tMfgHTllhResqM5DcnHyDIWNgzHvuY2jDcYP9iaGpkWxTUzV6GQjDeLnlR1/DtdlcsWQbA7sByMpmJFTLQ=="], diff --git a/desktop/runtime-host/live-runtime-factory.ts b/desktop/runtime-host/live-runtime-factory.ts index 15ac0b8ad..53e2f8ea4 100644 --- a/desktop/runtime-host/live-runtime-factory.ts +++ b/desktop/runtime-host/live-runtime-factory.ts @@ -46,7 +46,7 @@ export async function createLiveRuntime( ProjectTrustStore, createAgentSession, getAgentDir, - hasProjectTrustInputs, + hasTrustRequiringProjectResources, } = await getPiModule() const agentDir = getAgentDir() const defaultProjectTrust = getRuntimeDefaultProjectTrust({ @@ -59,7 +59,7 @@ export async function createLiveRuntime( agentDir, cwd: options.cwd, defaultProjectTrust, - hasProjectTrustInputs, + hasTrustRequiringProjectResources, settingsCwd: options.settingsCwd, }) const authStorage = AuthStorage.create() diff --git a/desktop/runtime-host/skill-creator-service.ts b/desktop/runtime-host/skill-creator-service.ts index bbe82eee4..a2531ef35 100644 --- a/desktop/runtime-host/skill-creator-service.ts +++ b/desktop/runtime-host/skill-creator-service.ts @@ -168,7 +168,7 @@ async function createSkillCreatorSession( ProjectTrustStore, createAgentSession, getAgentDir, - hasProjectTrustInputs, + hasTrustRequiringProjectResources, } = await getPiModule() const agentDir = getAgentDir() const trustCwd = projectPath ?? cwd @@ -182,7 +182,7 @@ async function createSkillCreatorSession( agentDir, cwd: trustCwd, defaultProjectTrust, - hasProjectTrustInputs, + hasTrustRequiringProjectResources, }) const authStorage = AuthStorage.create() const modelRegistry = normalizeModelRegistryContextWindows( diff --git a/desktop/runtime/composer-state.ts b/desktop/runtime/composer-state.ts index 4d939fc14..0ae5aed11 100644 --- a/desktop/runtime/composer-state.ts +++ b/desktop/runtime/composer-state.ts @@ -233,7 +233,7 @@ export async function createComposerSnapshotSession(request: ComposerStateReques ProjectTrustStore, createAgentSession, getAgentDir, - hasProjectTrustInputs, + hasTrustRequiringProjectResources, } = await getPiModule() const cwd = persistedSessionPath ? SessionManager.open(persistedSessionPath).getCwd() @@ -245,7 +245,7 @@ export async function createComposerSnapshotSession(request: ComposerStateReques agentDir, cwd, defaultProjectTrust, - hasProjectTrustInputs, + hasTrustRequiringProjectResources, settingsCwd: request.composerSessionDir, }) const authStorage = AuthStorage.create() @@ -284,7 +284,7 @@ export async function createComposerSnapshotSession(request: ComposerStateReques return { cwd, - projectTrustServices: { ProjectTrustStore, agentDir, hasProjectTrustInputs }, + projectTrustServices: { ProjectTrustStore, agentDir, hasTrustRequiringProjectResources }, session, } } @@ -331,7 +331,7 @@ export async function buildComposerState( runtime: PiRuntime, options: BuildComposerStateOptions = {}, ): Promise { - const { ProjectTrustStore, SettingsManager, getAgentDir, hasProjectTrustInputs } = + const { ProjectTrustStore, SettingsManager, getAgentDir, hasTrustRequiringProjectResources } = await getPiModule() const agentDir = getAgentDir() const defaultProjectTrust = getRuntimeDefaultProjectTrust({ @@ -362,7 +362,7 @@ export async function buildComposerState( agentDir, cwd: runtime.cwd, defaultProjectTrust, - hasProjectTrustInputs, + hasTrustRequiringProjectResources, }), contextUsage: getContextUsageForComposerState(runtime.session, options), isCompacting: runtime.session.isCompacting, diff --git a/desktop/runtime/isolated-settings-manager.test.ts b/desktop/runtime/isolated-settings-manager.test.ts index 4e84f73dc..033fc767d 100644 --- a/desktop/runtime/isolated-settings-manager.test.ts +++ b/desktop/runtime/isolated-settings-manager.test.ts @@ -79,7 +79,7 @@ describe('resolveRuntimeProjectTrust', () => { ProjectTrustStore: createTrustStoreFactory(false), agentDir: '/agent', cwd: '/repo', - hasProjectTrustInputs: () => false, + hasTrustRequiringProjectResources: () => false, }), ).toBe(true) }) @@ -90,7 +90,7 @@ describe('resolveRuntimeProjectTrust', () => { ProjectTrustStore: createTrustStoreFactory(true), agentDir: '/agent', cwd: '/repo', - hasProjectTrustInputs: () => true, + hasTrustRequiringProjectResources: () => true, }), ).toBe(true) @@ -99,7 +99,7 @@ describe('resolveRuntimeProjectTrust', () => { ProjectTrustStore: createTrustStoreFactory(false), agentDir: '/agent', cwd: '/repo', - hasProjectTrustInputs: () => true, + hasTrustRequiringProjectResources: () => true, }), ).toBe(false) }) @@ -111,7 +111,7 @@ describe('resolveRuntimeProjectTrust', () => { agentDir: '/agent', cwd: '/repo', defaultProjectTrust: 'ask', - hasProjectTrustInputs: () => true, + hasTrustRequiringProjectResources: () => true, }), ).toBe(false) }) @@ -123,7 +123,7 @@ describe('resolveRuntimeProjectTrust', () => { agentDir: '/agent', cwd: '/repo', defaultProjectTrust: 'always', - hasProjectTrustInputs: () => true, + hasTrustRequiringProjectResources: () => true, }), ).toBe(true) @@ -133,7 +133,7 @@ describe('resolveRuntimeProjectTrust', () => { agentDir: '/agent', cwd: '/repo', defaultProjectTrust: 'never', - hasProjectTrustInputs: () => true, + hasTrustRequiringProjectResources: () => true, }), ).toBe(false) }) @@ -144,7 +144,7 @@ describe('resolveRuntimeProjectTrust', () => { ProjectTrustStore: createTrustStoreFactory({ '/home/igorw': true }), agentDir: '/agent', cwd: '/home/igorw/Work/howcode', - hasProjectTrustInputs: () => true, + hasTrustRequiringProjectResources: () => true, }), ).toBe(true) }) diff --git a/desktop/runtime/isolated-settings-manager.ts b/desktop/runtime/isolated-settings-manager.ts index 7d6e59f22..a179013fd 100644 --- a/desktop/runtime/isolated-settings-manager.ts +++ b/desktop/runtime/isolated-settings-manager.ts @@ -147,11 +147,11 @@ export function resolveRuntimeProjectTrust(options: { agentDir: string cwd: string defaultProjectTrust?: PiDefaultProjectTrust | undefined - hasProjectTrustInputs: (cwd: string) => boolean + hasTrustRequiringProjectResources: (cwd: string) => boolean settingsCwd?: string | null | undefined }) { const trustCwd = options.cwd - if (!options.hasProjectTrustInputs(trustCwd)) return true + if (!options.hasTrustRequiringProjectResources(trustCwd)) return true const storedDecision = getStoredProjectTrustDecision(options) if (storedDecision !== null) return storedDecision @@ -163,9 +163,9 @@ export function getRuntimeProjectTrustRequest(options: { agentDir: string cwd: string defaultProjectTrust?: PiDefaultProjectTrust | undefined - hasProjectTrustInputs: (cwd: string) => boolean + hasTrustRequiringProjectResources: (cwd: string) => boolean }) { - if (!options.hasProjectTrustInputs(options.cwd)) return null + if (!options.hasTrustRequiringProjectResources(options.cwd)) return null const storedDecision = getStoredProjectTrustDecision(options) return storedDecision === null && (options.defaultProjectTrust ?? 'ask') === 'ask' ? { cwd: options.cwd } diff --git a/desktop/runtime/runtime-registry.ts b/desktop/runtime/runtime-registry.ts index 6d389afe4..3294d0953 100644 --- a/desktop/runtime/runtime-registry.ts +++ b/desktop/runtime/runtime-registry.ts @@ -125,7 +125,7 @@ async function createRuntime(options: { ProjectTrustStore, createAgentSession, getAgentDir, - hasProjectTrustInputs, + hasTrustRequiringProjectResources, } = await getPiModule() const agentDir = getAgentDir() const defaultProjectTrust = getRuntimeDefaultProjectTrust({ @@ -138,7 +138,7 @@ async function createRuntime(options: { agentDir, cwd: options.cwd, defaultProjectTrust, - hasProjectTrustInputs, + hasTrustRequiringProjectResources, settingsCwd: options.settingsCwd, }) const authStorage = AuthStorage.create() diff --git a/package.json b/package.json index 65ec8f005..d75c454db 100644 --- a/package.json +++ b/package.json @@ -60,10 +60,10 @@ "@dnd-kit/modifiers": "^9.0.0", "@dnd-kit/sortable": "^10.0.0", "@dnd-kit/utilities": "^3.2.2", - "@earendil-works/pi-agent-core": "0.79.1", - "@earendil-works/pi-ai": "0.79.1", - "@earendil-works/pi-coding-agent": "0.79.1", - "@earendil-works/pi-tui": "0.79.1", + "@earendil-works/pi-agent-core": "0.79.6", + "@earendil-works/pi-ai": "0.79.6", + "@earendil-works/pi-coding-agent": "0.79.6", + "@earendil-works/pi-tui": "0.79.6", "@fontsource-variable/geist-mono": "^5.2.8", "@fontsource-variable/inter": "^5.2.8", "@mdxeditor/editor": "4.0.2", From 397a928775ee9b0643e143e9106cfc12e5011b55 Mon Sep 17 00:00:00 2001 From: Igor Warzocha Date: Wed, 17 Jun 2026 19:59:07 +0100 Subject: [PATCH 2/4] Add session rename support --- desktop/pi-desktop-runtime.ts | 3 +- desktop/pi-threads/session-index.ts | 2 +- desktop/pi-threads/thread-actions.ts | 20 +++ desktop/runtime-host/host-service.ts | 6 +- desktop/runtime-host/live-thread-publisher.ts | 1 + desktop/runtime-host/protocol.ts | 2 + desktop/runtime-host/request-handlers.ts | 2 + .../runtime-host/runtime-session-events.ts | 4 + .../runtime-host/thread-preview-service.ts | 1 + .../runtime-host/thread-snapshot-service.ts | 28 +++++ desktop/runtime/runtime-session-events.ts | 5 + desktop/runtime/thread-publisher.ts | 4 +- desktop/thread-state-db.ts | 1 + desktop/thread-state-db/thread-writes.ts | 16 +++ desktop/thread-state-db/writes.ts | 1 + shared/desktop-action-contracts.ts | 7 ++ shared/desktop-action-coverage.ts | 1 + shared/desktop-actions.ts | 1 + shared/thread-data.ts | 13 +- .../controller-optimistic-updates.ts | 8 ++ .../controller-post-action-effects.ts | 108 ++++++++++++++++ src/app/app-shell/optimistic-projects.ts | 25 ++++ src/app/app-shell/useDesktopActionHandlers.ts | 5 + .../sidebar/chat/chat-thread-drop-item.tsx | 8 ++ .../project-tree/project-threads-list.tsx | 8 ++ .../project-work/project-work-thread-row.tsx | 8 ++ .../sidebar/thread-row/thread-row.tsx | 115 ++++++++++++++++-- src/styles/sidebar/thread-rows.css | 21 ++++ 28 files changed, 406 insertions(+), 18 deletions(-) diff --git a/desktop/pi-desktop-runtime.ts b/desktop/pi-desktop-runtime.ts index 9383d6628..136707d3b 100644 --- a/desktop/pi-desktop-runtime.ts +++ b/desktop/pi-desktop-runtime.ts @@ -113,7 +113,8 @@ async function persistHostThreadUpdate(event: Extract 0), ) diff --git a/desktop/pi-threads/session-index.ts b/desktop/pi-threads/session-index.ts index 7e573a06d..ff86168ff 100644 --- a/desktop/pi-threads/session-index.ts +++ b/desktop/pi-threads/session-index.ts @@ -219,7 +219,7 @@ export function mapSessionSummaryToRecord(cwd: string, session: SessionSummary) id: session.id, cwd: session.cwd || cwd, sessionPath: session.path, - title: normalizeThreadTitle(session.firstMessage || session.name), + title: normalizeThreadTitle(session.name || session.firstMessage), lastModifiedMs: session.modified.getTime(), } } diff --git a/desktop/pi-threads/thread-actions.ts b/desktop/pi-threads/thread-actions.ts index b84ec52d0..9d0f76061 100644 --- a/desktop/pi-threads/thread-actions.ts +++ b/desktop/pi-threads/thread-actions.ts @@ -14,6 +14,7 @@ import { import { deleteArtifactsForConversation } from '../artifact-state-db.ts' import { deleteChatThread } from '../chat-state-db.ts' import { openThreadRuntime, startNewThread } from '../pi-desktop-runtime.ts' +import { invokeRuntimeHost } from '../runtime-host/client-bridge.ts' import { addProjectUsageTotals, archiveThread, @@ -26,6 +27,7 @@ import { getThreadDeletionSnapshot, getThreadSessionPath, markInboxThreadRead, + renameThreadTitle, restoreThread, restoreThreads, toggleThreadPinned, @@ -201,6 +203,23 @@ async function deleteManyThreadsFromPayload(payload: AnyDesktopActionPayload) { return handledAction({ deletedThreadIds: deleteResult.deletedThreadIds }) } +function getRenameValue(payload: AnyDesktopActionPayload) { + return typeof payload.value === 'string' ? payload.value.trim() : '' +} + +async function renameThreadFromPayload(payload: AnyDesktopActionPayload) { + const threadId = getThreadId(payload) + const name = getRenameValue(payload) + if (!(threadId && name)) return handledAction() + + const sessionPath = getSessionPath(payload) ?? getThreadSessionPath(threadId) + if (!sessionPath) return handledAction({ error: 'Thread session not found.' }) + + const result = await invokeRuntimeHost('renameThreadSession', { sessionPath, name }) + renameThreadTitle(threadId, result.title) + return handledAction({ ...result, sessionPath, threadId }) +} + const threadActionHandlers = { 'thread.pin': (payload) => { const threadId = getThreadId(payload) @@ -246,6 +265,7 @@ const threadActionHandlers = { return handledAction() }, 'thread.delete-many': deleteManyThreadsFromPayload, + 'thread.rename': renameThreadFromPayload, 'thread.new': async (payload) => { const result = await startNewThread(getComposerRequest(payload)) const branchName = getBranchName(payload) diff --git a/desktop/runtime-host/host-service.ts b/desktop/runtime-host/host-service.ts index c4c4bf855..df0268e01 100644 --- a/desktop/runtime-host/host-service.ts +++ b/desktop/runtime-host/host-service.ts @@ -43,4 +43,8 @@ export { } from './skill-creator-service.ts' export { loadPiThemeStateInHost as loadPiThemeState } from './theme-service.ts' export { loadThreadPreviewAtEntry } from './thread-preview-service.ts' -export { loadThreadSnapshot, searchThreadSnapshot } from './thread-snapshot-service.ts' +export { + loadThreadSnapshot, + renameThreadSession, + searchThreadSnapshot, +} from './thread-snapshot-service.ts' diff --git a/desktop/runtime-host/live-thread-publisher.ts b/desktop/runtime-host/live-thread-publisher.ts index 95047364a..675c50f06 100644 --- a/desktop/runtime-host/live-thread-publisher.ts +++ b/desktop/runtime-host/live-thread-publisher.ts @@ -40,6 +40,7 @@ function buildLiveThreadData(runtime: PiRuntime) { return buildThreadData({ sessionPath, sourceMessages, + sessionName: runtime.session.sessionManager.getSessionName(), previousMessageCount: historySlice.previousMessageCount, isStreaming: runtime.session.isStreaming, isCompacting: runtime.session.isCompacting, diff --git a/desktop/runtime-host/protocol.ts b/desktop/runtime-host/protocol.ts index 3941d4692..a12f664e3 100644 --- a/desktop/runtime-host/protocol.ts +++ b/desktop/runtime-host/protocol.ts @@ -96,6 +96,7 @@ export type RuntimeHostRequestMap = { historyCompactions?: number | undefined } searchThreadSnapshot: { sessionPath: string; query: string } + renameThreadSession: { sessionPath: string; name: string } startSkillCreatorSession: { prompt: string local?: boolean | undefined @@ -188,6 +189,7 @@ export type RuntimeHostResponseMap = { thread: ThreadData } searchThreadSnapshot: ThreadSearchResult + renameThreadSession: { projectId: string; threadId: string; title: string } startSkillCreatorSession: SkillCreatorSessionState continueSkillCreatorSession: SkillCreatorSessionState closeSkillCreatorSession: { ok: boolean } diff --git a/desktop/runtime-host/request-handlers.ts b/desktop/runtime-host/request-handlers.ts index 76b8ac1ee..7d44b72c7 100644 --- a/desktop/runtime-host/request-handlers.ts +++ b/desktop/runtime-host/request-handlers.ts @@ -25,6 +25,7 @@ import { openThreadRuntime, removePiPackage, removePiSkill, + renameThreadSession, searchThreadSnapshot, selectProjectRuntime, sendComposerPrompt, @@ -75,6 +76,7 @@ const runtimeHostRequestHandlers = { openThreadRuntime: (payload) => openThreadRuntime(payload.request), removePiPackage: (payload) => removePiPackage(payload), removePiSkill: (payload) => removePiSkill(payload), + renameThreadSession: (payload) => renameThreadSession(payload), searchThreadSnapshot: (payload) => searchThreadSnapshot(payload), selectProjectRuntime: (payload) => selectProjectRuntime(payload.request), sendComposerPrompt: (payload) => sendComposerPrompt(payload), diff --git a/desktop/runtime-host/runtime-session-events.ts b/desktop/runtime-host/runtime-session-events.ts index 05926a635..2990a79a4 100644 --- a/desktop/runtime-host/runtime-session-events.ts +++ b/desktop/runtime-host/runtime-session-events.ts @@ -105,6 +105,10 @@ export function handleRuntimeSessionEvent( }) }, 0) return + case 'session_info_changed': + cancelLiveThreadUpdate(runtime) + void publishThreadUpdate(runtime, 'update') + return case 'tool_execution_start': case 'tool_execution_update': case 'tool_execution_end': diff --git a/desktop/runtime-host/thread-preview-service.ts b/desktop/runtime-host/thread-preview-service.ts index 2174d1fe0..9aac20fc6 100644 --- a/desktop/runtime-host/thread-preview-service.ts +++ b/desktop/runtime-host/thread-preview-service.ts @@ -33,6 +33,7 @@ export async function loadThreadPreviewAtEntry(request: { thread: buildThreadData({ sessionPath: request.sessionPath, sourceMessages: historySlice.sourceMessages, + sessionName: manager.getSessionName(), previousMessageCount: historySlice.previousMessageCount, isStreaming: false, isCompacting: false, diff --git a/desktop/runtime-host/thread-snapshot-service.ts b/desktop/runtime-host/thread-snapshot-service.ts index 5ce924b33..6789560ab 100644 --- a/desktop/runtime-host/thread-snapshot-service.ts +++ b/desktop/runtime-host/thread-snapshot-service.ts @@ -1,7 +1,9 @@ +import { normalizeThreadTitle } from '../../shared/pi-message-mapper.ts' import { buildThreadData } from '../../shared/thread-data.ts' import { buildThreadHistorySlice, type SessionPathEntry } from '../../shared/thread-history.ts' import { searchThreadData } from '../../shared/thread-search.ts' import { getPiModule } from '../pi-module.ts' +import { getCachedRuntimeForSessionPath } from './live-runtime-registry.ts' export async function loadThreadSnapshot(request: { sessionPath: string @@ -20,6 +22,7 @@ export async function loadThreadSnapshot(request: { thread: buildThreadData({ sessionPath: request.sessionPath, sourceMessages: historySlice.sourceMessages, + sessionName: manager.getSessionName(), previousMessageCount: historySlice.previousMessageCount, isStreaming: false, isCompacting: false, @@ -27,6 +30,30 @@ export async function loadThreadSnapshot(request: { } } +export async function renameThreadSession(request: { sessionPath: string; name: string }) { + const name = request.name.trim() + if (!name) throw new Error('Session name is required.') + + const runtime = await getCachedRuntimeForSessionPath(request.sessionPath) + if (runtime) { + runtime.session.setSessionName(name) + return { + projectId: runtime.cwd, + threadId: runtime.session.sessionId, + title: normalizeThreadTitle(runtime.session.sessionManager.getSessionName() ?? name), + } + } + + const { SessionManager } = await getPiModule() + const manager = SessionManager.open(request.sessionPath) + manager.appendSessionInfo(name) + return { + projectId: manager.getCwd(), + threadId: manager.getSessionId(), + title: normalizeThreadTitle(manager.getSessionName() ?? name), + } +} + export async function searchThreadSnapshot(request: { sessionPath: string; query: string }) { const { SessionManager } = await getPiModule() const manager = SessionManager.open(request.sessionPath) @@ -34,6 +61,7 @@ export async function searchThreadSnapshot(request: { sessionPath: string; query const thread = buildThreadData({ sessionPath: request.sessionPath, sourceMessages: buildThreadHistorySlice(pathEntries, -1).sourceMessages, + sessionName: manager.getSessionName(), previousMessageCount: 0, isStreaming: false, isCompacting: false, diff --git a/desktop/runtime/runtime-session-events.ts b/desktop/runtime/runtime-session-events.ts index b157d2e66..97cd78368 100644 --- a/desktop/runtime/runtime-session-events.ts +++ b/desktop/runtime/runtime-session-events.ts @@ -149,6 +149,11 @@ export function handleRuntimeSessionEvent( } if (event.type === 'compaction_end') return handleRuntimeCompactionEnd(runtime, runtimeKey, handlers) + if (event.type === 'session_info_changed') { + cancelLiveThreadUpdate(runtime) + void handlers.publishThreadUpdate(runtime, 'update') + return + } if ( event.type === 'tool_execution_start' || event.type === 'tool_execution_update' || diff --git a/desktop/runtime/thread-publisher.ts b/desktop/runtime/thread-publisher.ts index a027fddc6..8dd954b5b 100644 --- a/desktop/runtime/thread-publisher.ts +++ b/desktop/runtime/thread-publisher.ts @@ -55,6 +55,7 @@ function buildLiveThreadData(runtime: PiRuntime) { return buildThreadData({ sessionPath, sourceMessages, + sessionName: runtime.session.sessionManager.getSessionName(), previousMessageCount: historySlice.previousMessageCount, isStreaming: runtime.session.isStreaming, isCompacting: runtime.session.isCompacting, @@ -154,7 +155,8 @@ function updateThreadRunningState( ) { setThreadRunningState( sessionPath, - reason === 'update' || + thread.isStreaming || + thread.isCompacting || reason === 'compaction-start' || (reason === 'start' && thread.messages.length > 0), ) diff --git a/desktop/thread-state-db.ts b/desktop/thread-state-db.ts index 0979f052d..f3f1ef059 100644 --- a/desktop/thread-state-db.ts +++ b/desktop/thread-state-db.ts @@ -48,6 +48,7 @@ export { hideProject, markInboxThreadRead, renameProject, + renameThreadTitle, restoreThread, restoreThreads, setProjectCollapsed, diff --git a/desktop/thread-state-db/thread-writes.ts b/desktop/thread-state-db/thread-writes.ts index f8985c9ce..ee78eba08 100644 --- a/desktop/thread-state-db/thread-writes.ts +++ b/desktop/thread-state-db/thread-writes.ts @@ -79,6 +79,22 @@ export function toggleThreadPinned(threadId: string) { ).run(threadId) } +export function renameThreadTitle(threadId: string, title: string) { + const normalizedTitle = title.trim() + if (!normalizedTitle) return false + const db = getThreadStateDatabase() + const result = db + .prepare( + ` + UPDATE threads + SET title = ?, updated_at = CURRENT_TIMESTAMP + WHERE id = ? + `, + ) + .run(normalizedTitle, threadId) as { changes: number } + return result.changes > 0 +} + export function assignThreadBranch(threadId: string, branchName: string | null) { return assignThreadToProjectBranch(threadId, branchName) } diff --git a/desktop/thread-state-db/writes.ts b/desktop/thread-state-db/writes.ts index 123913223..124e3a5bc 100644 --- a/desktop/thread-state-db/writes.ts +++ b/desktop/thread-state-db/writes.ts @@ -32,6 +32,7 @@ export { assignThreadToProjectBranch, deleteThreadRecord, deleteThreadRecordsBySessionPaths, + renameThreadTitle, restoreThread, restoreThreads, setThreadDiffPreferences, diff --git a/shared/desktop-action-contracts.ts b/shared/desktop-action-contracts.ts index c295d0e22..f1f988969 100644 --- a/shared/desktop-action-contracts.ts +++ b/shared/desktop-action-contracts.ts @@ -190,6 +190,12 @@ export type DesktopActionPayloadMap = { 'thread.delete': { threadId: string } 'thread.delete-many': { threadIds: string[]; projectIds?: string[] | undefined } 'thread.pin': { threadId: string; projectId?: string | undefined | null | undefined } + 'thread.rename': { + threadId: string + projectId?: string | undefined | null | undefined + sessionPath?: string | undefined | null | undefined + value: string + } 'workspace.commit': { projectId?: string | undefined | null | undefined sessionPath?: string | undefined | null | undefined @@ -390,6 +396,7 @@ export type DesktopActionResultData = { pushFailed?: boolean | undefined repoProjectCount?: number | undefined sessionPath?: string | undefined | null | undefined + title?: string | undefined threadId?: string | undefined } diff --git a/shared/desktop-action-coverage.ts b/shared/desktop-action-coverage.ts index 51a3fbd7c..d602edb28 100644 --- a/shared/desktop-action-coverage.ts +++ b/shared/desktop-action-coverage.ts @@ -27,6 +27,7 @@ export const implementedDesktopActions = [ 'thread.delete', 'thread.delete-many', 'thread.new', + 'thread.rename', 'composer.model', 'composer.thinking', 'composer.send', diff --git a/shared/desktop-actions.ts b/shared/desktop-actions.ts index b81c7adbd..dcd448734 100644 --- a/shared/desktop-actions.ts +++ b/shared/desktop-actions.ts @@ -25,6 +25,7 @@ export const desktopActions = [ 'thread.delete', 'thread.delete-many', 'thread.pin', + 'thread.rename', 'workspace.commit', 'workspace.commit-options', 'workspace.diff-preferences', diff --git a/shared/thread-data.ts b/shared/thread-data.ts index 25a52a848..db7a27893 100644 --- a/shared/thread-data.ts +++ b/shared/thread-data.ts @@ -1,10 +1,15 @@ import type { AgentMessage } from '@earendil-works/pi-agent-core' import type { ThreadCustomMessageRecord, ThreadData } from './desktop-contracts' -import { getFirstUserTurnTitle, mapAgentMessagesToUiMessages } from './pi-message-mapper' +import { + getFirstUserTurnTitle, + mapAgentMessagesToUiMessages, + normalizeThreadTitle, +} from './pi-message-mapper' type BuildThreadDataInput = { sessionPath: string sourceMessages: readonly AgentMessage[] + sessionName?: string | undefined previousMessageCount: number isStreaming: boolean isCompacting?: boolean | undefined @@ -37,16 +42,20 @@ function getThreadCustomMessages(sourceMessages: readonly AgentMessage[]) { export function buildThreadData({ sessionPath, + sessionName, sourceMessages, previousMessageCount, isStreaming, isCompacting = false, }: BuildThreadDataInput): ThreadData { const messages = mapAgentMessagesToUiMessages([...sourceMessages]) + const trimmedSessionName = sessionName?.trim() return { sessionPath, - title: getFirstUserTurnTitle(messages), + title: trimmedSessionName + ? normalizeThreadTitle(trimmedSessionName) + : getFirstUserTurnTitle(messages), messages, customMessages: getThreadCustomMessages(sourceMessages), previousMessageCount, diff --git a/src/app/app-shell/controller-optimistic-updates.ts b/src/app/app-shell/controller-optimistic-updates.ts index 414f9f74d..419e286bc 100644 --- a/src/app/app-shell/controller-optimistic-updates.ts +++ b/src/app/app-shell/controller-optimistic-updates.ts @@ -7,6 +7,7 @@ import { getOptimisticallyUpdatedPiSettingsState } from './optimistic-pi-setting import { getOptimisticallyPinnedShellState, getOptimisticallyRenamedShellState, + getOptimisticallyRenamedThreadShellState, } from './optimistic-projects' import { getOptimisticallyUpdatedShellState } from './optimistic-settings' @@ -14,6 +15,7 @@ export { getOptimisticallyUpdatedPiSettingsState } from './optimistic-pi-setting export { getOptimisticallyPinnedShellState, getOptimisticallyRenamedShellState, + getOptimisticallyRenamedThreadShellState, } from './optimistic-projects' export { getOptimisticallyUpdatedShellState } from './optimistic-settings' @@ -35,6 +37,12 @@ export function applyOptimisticProjectRename(queryClient: QueryClient, payload: ) } +export function applyOptimisticThreadRename(queryClient: QueryClient, payload: ActionPayload) { + queryClient.setQueryData(desktopQueryKeys.shellState(), (currentState) => + getOptimisticallyRenamedThreadShellState(currentState ?? null, payload), + ) +} + export function applyOptimisticPinUpdate( queryClient: QueryClient, action: DesktopAction, diff --git a/src/app/app-shell/controller-post-action-effects.ts b/src/app/app-shell/controller-post-action-effects.ts index bc1b208a6..7d948e1a3 100644 --- a/src/app/app-shell/controller-post-action-effects.ts +++ b/src/app/app-shell/controller-post-action-effects.ts @@ -43,6 +43,7 @@ export { applyOptimisticPiSettingsUpdate, applyOptimisticProjectRename, applyOptimisticSettingsUpdate, + applyOptimisticThreadRename, getOptimisticallyPinnedShellState, getOptimisticallyRenamedShellState, getOptimisticallyUpdatedPiSettingsState, @@ -132,6 +133,112 @@ function applyPiThemeUpdate(ctx: PostEffectsContext) { ) } +function getThreadRenameContext(ctx: PostEffectsContext) { + const result = ctx.actionResult?.result + return { + projectId: + typeof result?.projectId === 'string' + ? result.projectId + : typeof ctx.contextualPayload.projectId === 'string' + ? ctx.contextualPayload.projectId + : null, + sessionPath: + typeof result?.sessionPath === 'string' + ? result.sessionPath + : typeof ctx.contextualPayload.sessionPath === 'string' + ? ctx.contextualPayload.sessionPath + : null, + threadId: + typeof result?.threadId === 'string' + ? result.threadId + : typeof ctx.contextualPayload.threadId === 'string' + ? ctx.contextualPayload.threadId + : null, + title: + typeof result?.title === 'string' + ? result.title + : typeof ctx.contextualPayload.value === 'string' + ? ctx.contextualPayload.value.trim() + : '', + } +} + +function isRenamedThread( + thread: { id: string; sessionPath?: string | null | undefined }, + input: { threadId: string; sessionPath: string | null }, +) { + return thread.id === input.threadId || thread.sessionPath === input.sessionPath +} + +function renameCachedProjectThreads( + ctx: PostEffectsContext, + input: { projectId: string; sessionPath: string | null; threadId: string; title: string }, +) { + for (const chat of [false, true]) { + ctx.queryClient.setQueryData<{ id: string; sessionPath?: string | null; title: string }[]>( + desktopQueryKeys.projectThreads(input.projectId, chat), + (current) => + current?.map((thread) => + isRenamedThread(thread, input) ? { ...thread, title: input.title } : thread, + ), + ) + } +} + +async function handleThreadRenameEffects(ctx: PostEffectsContext) { + const { projectId, sessionPath, threadId, title } = getThreadRenameContext(ctx) + if (hasActionError(ctx.actionResult)) { + if (projectId) { + await Promise.all([ + ctx.loadProjectThreads(projectId), + ctx.loadProjectThreads(projectId, { chat: true }), + ]) + } + return + } + if (!(threadId && title)) return + + if (projectId) { + updateShellProject(ctx.queryClient, projectId, (project) => ({ + ...project, + threads: project.threads.map((thread) => + thread.id === threadId ? { ...thread, title } : thread, + ), + })) + renameCachedProjectThreads(ctx, { projectId, sessionPath, threadId, title }) + } + + ctx.setChatSidebarState((currentState) => + currentState + ? { + ...currentState, + ungroupedThreads: currentState.ungroupedThreads.map((thread) => + isRenamedThread(thread, { threadId, sessionPath }) ? { ...thread, title } : thread, + ), + groups: currentState.groups.map((group) => ({ + ...group, + threads: group.threads.map((thread) => + isRenamedThread(thread, { threadId, sessionPath }) ? { ...thread, title } : thread, + ), + })), + } + : currentState, + ) + + ctx.setLiveThreadData((current) => + current && (!sessionPath || current.sessionPath === sessionPath) + ? { ...current, title } + : current, + ) + if (sessionPath) { + ctx.queryClient.setQueriesData( + { queryKey: desktopQueryKeys.threadPrefix(sessionPath) }, + (current) => (current ? { ...current, title } : current), + ) + } + await ctx.invalidateInboxThreads() +} + async function handleNewThreadOrProjectEffects(ctx: PostEffectsContext) { if (ctx.action !== 'thread.new' && ctx.action !== 'project.add') return await applyNewThreadPostEffect({ @@ -275,6 +382,7 @@ const postEffectHandlers: PostEffectHandler[] = [ await refreshArchivedIfVisible(getThreadLifecycleInput(ctx)) }, }, + { matches: (ctx) => ctx.action === 'thread.rename', run: handleThreadRenameEffects }, { matches: (ctx) => ctx.action === 'project.refresh-repo-origin', run: (ctx) => { diff --git a/src/app/app-shell/optimistic-projects.ts b/src/app/app-shell/optimistic-projects.ts index b047fe354..b9c5ac624 100644 --- a/src/app/app-shell/optimistic-projects.ts +++ b/src/app/app-shell/optimistic-projects.ts @@ -31,6 +31,31 @@ export function getOptimisticallyRenamedShellState( } satisfies ShellState } +export function getOptimisticallyRenamedThreadShellState( + currentState: ShellState | null, + payload: ActionPayload, +) { + if (!currentState) return null + const projectId = getPayloadProjectId(payload) + const threadId = getPayloadThreadId(payload) + const title = typeof payload.value === 'string' ? payload.value.trim() : '' + if (!(projectId && threadId && title)) return currentState + + return { + ...currentState, + projects: currentState.projects.map((project) => + project.id === projectId + ? { + ...project, + threads: project.threads.map((thread) => + thread.id === threadId ? { ...thread, title } : thread, + ), + } + : project, + ), + } satisfies ShellState +} + export function getOptimisticallyPinnedShellState( currentState: ShellState | null, action: DesktopAction, diff --git a/src/app/app-shell/useDesktopActionHandlers.ts b/src/app/app-shell/useDesktopActionHandlers.ts index bfe7e4ddb..84e375781 100644 --- a/src/app/app-shell/useDesktopActionHandlers.ts +++ b/src/app/app-shell/useDesktopActionHandlers.ts @@ -24,6 +24,7 @@ import { applyOptimisticPiSettingsUpdate, applyOptimisticProjectRename, applyOptimisticSettingsUpdate, + applyOptimisticThreadRename, runPostDesktopActionEffects, } from './controller-post-action-effects' import { applySwitchBranchPostEffect } from './post-effects/workspace' @@ -289,6 +290,10 @@ export function useDesktopActionHandlers({ applyOptimisticProjectRename(queryClient, payload) } + if (action === 'thread.rename') { + applyOptimisticThreadRename(queryClient, payload) + } + if (action === 'thread.pin' || action === 'project.pin') { applyOptimisticPinUpdate(queryClient, action, payload) } diff --git a/src/app/components/sidebar/chat/chat-thread-drop-item.tsx b/src/app/components/sidebar/chat/chat-thread-drop-item.tsx index e41706983..30169cace 100644 --- a/src/app/components/sidebar/chat/chat-thread-drop-item.tsx +++ b/src/app/components/sidebar/chat/chat-thread-drop-item.tsx @@ -52,6 +52,14 @@ export function ChatThreadDropItem({ onThreadOpen(thread.projectId, thread.id, thread.sessionPath, 'chat') } onPin={() => void onAction('thread.pin', { threadId: thread.id }).then(onRefresh)} + onRename={(title) => + void onAction('thread.rename', { + projectId: thread.projectId, + sessionPath: thread.sessionPath, + threadId: thread.id, + value: title, + }) + } /> ) diff --git a/src/app/components/sidebar/project-tree/project-threads-list.tsx b/src/app/components/sidebar/project-tree/project-threads-list.tsx index 6a8112048..602193e57 100644 --- a/src/app/components/sidebar/project-tree/project-threads-list.tsx +++ b/src/app/components/sidebar/project-tree/project-threads-list.tsx @@ -190,6 +190,14 @@ export function ProjectThreadsList({ threadId: thread.id, }) } + onRename={(title) => + onAction('thread.rename', { + projectId: project.id, + sessionPath: thread.sessionPath, + threadId: thread.id, + value: title, + }) + } /> ) diff --git a/src/app/components/sidebar/project-work/project-work-thread-row.tsx b/src/app/components/sidebar/project-work/project-work-thread-row.tsx index f4c7edb9b..47e00b60c 100644 --- a/src/app/components/sidebar/project-work/project-work-thread-row.tsx +++ b/src/app/components/sidebar/project-work/project-work-thread-row.tsx @@ -65,6 +65,14 @@ export function ProjectWorkThreadRow({ threadId: thread.id, }) } + onRename={(title) => + onAction('thread.rename', { + projectId: project.id, + sessionPath: thread.sessionPath, + threadId: thread.id, + value: title, + }) + } /> ) } diff --git a/src/app/components/sidebar/thread-row/thread-row.tsx b/src/app/components/sidebar/thread-row/thread-row.tsx index ca353247f..7b06dd99a 100644 --- a/src/app/components/sidebar/thread-row/thread-row.tsx +++ b/src/app/components/sidebar/thread-row/thread-row.tsx @@ -1,7 +1,7 @@ import { ActivitySpinner } from '@howcode/common/activity-spinner' import { Tooltip } from '@howcode/common/tooltip' -import { GitBranch, SquareTerminal, Star, Trash2 } from 'lucide-react' -import { useState } from 'react' +import { GitBranch, Pencil, SquareTerminal, Star, Trash2 } from 'lucide-react' +import { useEffect, useRef, useState } from 'react' import { cn } from '../../../utils/cn' import { SidebarInlineConfirmPopunder } from '../sidebar-inline-confirm-popunder' @@ -18,6 +18,7 @@ type ThreadRowProps = { onAssignToBranch?: (() => void) | undefined onOpen: () => void onPin: () => void + onRename?: ((title: string) => unknown) | undefined confirmDelete?: boolean | undefined } @@ -61,6 +62,7 @@ function ThreadMetaSlot({ confirmDelete, onDelete, onAssignToBranch, + onRenameStart, terminalRunning, }: Pick< ThreadRowProps, @@ -70,9 +72,9 @@ function ThreadMetaSlot({ | 'onDelete' | 'onAssignToBranch' | 'terminalRunning' ->) { +> & { onRenameStart?: (() => void) | undefined }) { const metaValue = terminalRunning ? : age - const actionCount = onAssignToBranch ? 2 : 1 + const actionCount = 1 + (onAssignToBranch ? 1 : 0) + (onRenameStart ? 1 : 0) const [confirmDeleteOpen, setConfirmDeleteOpen] = useState(false) const handleDeleteClick = () => { if (!confirmDelete) { @@ -88,6 +90,25 @@ function ThreadMetaSlot({ {metaValue} + {onRenameStart ? ( + + + + ) : null} {onAssignToBranch ? ( (null) + const skipBlurSubmitRef = useRef(false) + + useEffect(() => { + if (!editing) setDraft(title) + }, [editing, title]) + + useEffect(() => { + if (!editing) return + inputRef.current?.focus() + }, [editing]) + + const startRename = () => { + setDraft('') + setEditing(true) + } + + const cancelRename = () => { + skipBlurSubmitRef.current = true + setDraft(title) + setEditing(false) + } + + const submitRename = () => { + const nextTitle = draft.trim() + if (!nextTitle || nextTitle === title) { + cancelRename() + return + } + skipBlurSubmitRef.current = true + setEditing(false) + void onRename?.(nextTitle) + } + return (
- + {editing ? ( +
{ + event.preventDefault() + submitRename() + }} + > + { + if (skipBlurSubmitRef.current) { + skipBlurSubmitRef.current = false + return + } + submitRename() + }} + onChange={(event) => setDraft(event.target.value)} + onKeyDown={(event) => { + if (event.key === 'Escape') { + event.preventDefault() + cancelRename() + } + }} + aria-label="Session name" + /> +
+ ) : ( + + )}
diff --git a/src/styles/sidebar/thread-rows.css b/src/styles/sidebar/thread-rows.css index ed20c889b..334336866 100644 --- a/src/styles/sidebar/thread-rows.css +++ b/src/styles/sidebar/thread-rows.css @@ -131,6 +131,15 @@ white-space: nowrap; } +.sidebar-thread-title-input { + min-width: 0; + width: 100%; + border: 0; + background: transparent; + color: var(--text); + outline: none; +} + .sidebar-thread-meta-slot { position: relative; display: inline-flex; @@ -202,6 +211,18 @@ grid-template-columns: var(--sidebar-thread-action-size); } +.sidebar-thread-meta-actions[data-action-count="3"] { + width: calc( + var(--sidebar-thread-action-size) * + 3 + + var(--sidebar-thread-action-gap) * + 2 + + var(--sidebar-thread-action-pad) * + 2 + ); + grid-template-columns: repeat(3, var(--sidebar-thread-action-size)); +} + .sidebar-thread-row:hover .sidebar-thread-meta-actions, .sidebar-thread-row:focus-within .sidebar-thread-meta-actions { pointer-events: auto; From 4a974db51020f5ceb2b343b04eeaed94940205d8 Mon Sep 17 00:00:00 2001 From: Igor Warzocha Date: Wed, 17 Jun 2026 20:18:25 +0100 Subject: [PATCH 3/4] Keep compaction pill above composer overlays --- src/app/thread/thread-timeline.tsx | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/app/thread/thread-timeline.tsx b/src/app/thread/thread-timeline.tsx index 4cbc7e06c..e44f136c7 100644 --- a/src/app/thread/thread-timeline.tsx +++ b/src/app/thread/thread-timeline.tsx @@ -393,7 +393,14 @@ export function ThreadTimeline({ {isCompacting ? ( -
+
0 + ? { bottom: `calc(1rem + ${composerOverlayHeight}px)` } + : undefined + } + >
Date: Wed, 17 Jun 2026 20:20:47 +0100 Subject: [PATCH 4/4] Update changelog for SDK bump and session rename --- docs/changelog.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/docs/changelog.md b/docs/changelog.md index dcddafc3f..c28d584cb 100644 --- a/docs/changelog.md +++ b/docs/changelog.md @@ -15,11 +15,13 @@ - Extension widgets no longer shake the thread view. - Inbox keeps branch-assigned threads in Code context. - Thread URLs no longer stick to local draft ids. +- Sidebar sessions can be renamed inline now. - Rebuilt the workspace rails around the composer/thread. Better responsiveness. - Fixed the empty composer being taller than a composer with text in it. +- Compaction status now stays above composer widgets. - Fixed Past sessions count alignment in the sidebar. - Fixed macOS window dragging and native Cmd+A select-all. -- Updated Pi SDK/runtime packages to 0.79.1. +- Updated Pi SDK/runtime packages to 0.79.6. - Bumped app/build dependencies. Snapshot: 12 June 2026.