@baique/pi-web-sky 0.1.55 → 0.1.57
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.next/BUILD_ID +1 -1
- package/.next/app-path-routes-manifest.json +8 -4
- package/.next/build-manifest.json +3 -3
- package/.next/prerender-manifest.json +2 -2
- package/.next/react-loadable-manifest.json +2 -2
- package/.next/required-server-files.js +3 -2
- package/.next/required-server-files.json +3 -2
- package/.next/routes-manifest.json +28 -0
- package/.next/server/app/_global-error/page.js +2 -2
- package/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_global-error.html +1 -1
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/server/app/_not-found/page.js +1 -1
- package/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/server/app/_not-found.html +1 -1
- package/.next/server/app/_not-found.rsc +4 -3
- package/.next/server/app/_not-found.segments/_full.segment.rsc +4 -3
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +4 -3
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +3 -2
- package/.next/server/app/api/agent/[id]/bash-output/route.js +2 -2
- package/.next/server/app/api/agent/[id]/events/route.js +2 -2
- package/.next/server/app/api/agent/[id]/events/route.js.nft.json +1 -1
- package/.next/server/app/api/agent/[id]/route.js +1 -1
- package/.next/server/app/api/agent/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/agent/new/route.js +1 -1
- package/.next/server/app/api/agent/new/route.js.nft.json +1 -1
- package/.next/server/app/api/agent/running/route.js +1 -1
- package/.next/server/app/api/agent/running/route.js.nft.json +1 -1
- package/.next/server/app/api/app-update/route.js +1 -1
- package/.next/server/app/api/auth/all-providers/route.js +1 -1
- package/.next/server/app/api/auth/api-key/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/login/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/logout/[provider]/route.js +1 -1
- package/.next/server/app/api/auth/providers/route.js +1 -1
- package/.next/server/app/api/board-assets/[...path]/route.js +2 -2
- package/.next/server/app/api/board-assets/route.js +2 -2
- package/.next/server/app/api/boards/[id]/add-session/route.js +1 -0
- package/.next/server/app/api/boards/[id]/add-session/route.js.nft.json +1 -0
- package/.next/server/app/api/boards/[id]/add-session/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/boards/[id]/reconcile/route.js +1 -1
- package/.next/server/app/api/boards/[id]/route.js +1 -1
- package/.next/server/app/api/boards/reorder/route.js +2 -2
- package/.next/server/app/api/boards/route.js +1 -1
- package/.next/server/app/api/cwd/browse/route.js +1 -1
- package/.next/server/app/api/cwd/validate/route.js +2 -2
- package/.next/server/app/api/default-cwd/route.js +2 -2
- package/.next/server/app/api/file-index/route.js +2 -2
- package/.next/server/app/api/files/[...path]/route.js +3 -3
- package/.next/server/app/api/git/diff/route.js +1 -1
- package/.next/server/app/api/git/status/route.js +1 -1
- package/.next/server/app/api/home/route.js +1 -1
- package/.next/server/app/api/mcp/route.js +2 -2
- package/.next/server/app/api/models/route.js +2 -2
- package/.next/server/app/api/models-config/catalog/route.js +1 -1
- package/.next/server/app/api/models-config/discover/route.js +1 -1
- package/.next/server/app/api/models-config/route.js +1 -1
- package/.next/server/app/api/models-config/test/route.js +1 -1
- package/.next/server/app/api/plugins/route.js +2 -2
- package/.next/server/app/api/project-trust/route.js +1 -1
- package/.next/server/app/api/project-trust/route.js.nft.json +1 -1
- package/.next/server/app/api/quota/route.js +1 -1
- package/.next/server/app/api/search/route.js +3 -3
- package/.next/server/app/api/sessions/[id]/auto-name/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/auto-name/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/context/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/context/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/entries/[entryId]/thinking/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/export/route.js +2 -2
- package/.next/server/app/api/sessions/[id]/minimap/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/minimap/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/state/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/state/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/todos/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/todos/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/route.js +1 -1
- package/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/summary/route.js +2 -2
- package/.next/server/app/api/skills/check/route.js +1 -1
- package/.next/server/app/api/skills/install/route.js +2 -2
- package/.next/server/app/api/skills/route.js +1 -1
- package/.next/server/app/api/skills/search/route.js +1 -1
- package/.next/server/app/api/skills/update/route.js +1 -1
- package/.next/server/app/api/subagents/[id]/route.js +1 -0
- package/.next/server/app/api/subagents/[id]/route.js.nft.json +1 -0
- package/.next/server/app/api/subagents/[id]/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/subagents/profiles/route.js +30 -0
- package/.next/server/app/api/subagents/profiles/route.js.nft.json +1 -0
- package/.next/server/app/api/subagents/profiles/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/subagents/settings/route.js +1 -0
- package/.next/server/app/api/subagents/settings/route.js.nft.json +1 -0
- package/.next/server/app/api/subagents/settings/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/task-card-questions/[id]/answer/route.js +2 -2
- package/.next/server/app/api/task-card-questions/route.js +2 -2
- package/.next/server/app/api/task-cards/[id]/route.js +1 -1
- package/.next/server/app/api/task-cards/route.js +1 -1
- package/.next/server/app/api/task-cards/unbind/route.js +3 -3
- package/.next/server/app/api/task-scheduler/status/route.js +1 -1
- package/.next/server/app/api/task-scheduler/status/route.js.nft.json +1 -1
- package/.next/server/app/api/tasks/[id]/assign-session/route.js +2 -12
- package/.next/server/app/api/tasks/[id]/board/route.js +1 -1
- package/.next/server/app/api/tasks/[id]/route.js +1 -1
- package/.next/server/app/api/tasks/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/tasks/reorder/route.js +2 -12
- package/.next/server/app/api/tasks/route.js +2 -12
- package/.next/server/app/api/terminal/[id]/events/route.js +2 -2
- package/.next/server/app/api/terminal/[id]/route.js +2 -2
- package/.next/server/app/api/terminal/route.js +2 -2
- package/.next/server/app/api/worktrees/route.js +2 -2
- package/.next/server/app/api/yjs-version/route.js +1 -1
- package/.next/server/app/favicon.ico/route.js +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +6 -5
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +6 -5
- package/.next/server/app/index.segments/_full.segment.rsc +6 -5
- package/.next/server/app/index.segments/_tree.segment.rsc +3 -2
- package/.next/server/app/manifest.webmanifest/route.js +2 -2
- package/.next/server/app/page.js +175 -8
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +8 -4
- package/.next/server/chunks/1937.js +14 -13
- package/.next/server/chunks/2524.js +2 -2
- package/.next/server/chunks/3379.js +1 -1
- package/.next/server/chunks/3500.js +1 -1
- package/.next/server/chunks/402.js +28 -7
- package/.next/server/chunks/426.js +14 -0
- package/.next/server/chunks/4367.js +1 -1
- package/.next/server/chunks/4457.js +3 -13
- package/.next/server/chunks/4980.js +1 -1
- package/.next/server/chunks/542.js +23 -4
- package/.next/server/chunks/5794.js +2 -2
- package/.next/server/chunks/5940.js +1 -1
- package/.next/server/chunks/6337.js +1 -1
- package/.next/server/chunks/6429.js +15 -2
- package/.next/server/chunks/6525.js +25 -4
- package/.next/server/chunks/6583.js +12 -11
- package/.next/server/chunks/7048.js +6 -0
- package/.next/server/instrumentation.js +1 -1
- package/.next/server/middleware-build-manifest.js +1 -1
- package/.next/server/middleware-react-loadable-manifest.js +1 -1
- package/.next/server/middleware.js +3 -3
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/static/MPBMmQqmDG8zo8tM_bkDy/_buildManifest.js +1 -0
- package/.next/static/chunks/752.bafff304e6b00987.js +1 -0
- package/.next/static/chunks/8088.39866dc5f1d15868.js +1 -0
- package/.next/static/chunks/app/_global-error/page-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/bash-output/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/events/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/agent/new/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/agent/running/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/app-update/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/auth/all-providers/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/auth/api-key/[provider]/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/auth/login/[provider]/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/auth/logout/[provider]/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/auth/providers/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/board-assets/[...path]/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/board-assets/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/boards/[id]/add-session/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/boards/[id]/reconcile/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/boards/[id]/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/boards/reorder/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/boards/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/cwd/browse/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/cwd/validate/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/default-cwd/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/file-index/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/files/[...path]/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/git/diff/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/git/status/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/home/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/mcp/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/models/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/models-config/catalog/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/models-config/discover/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/models-config/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/models-config/test/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/plugins/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/project-trust/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/quota/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/search/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/context/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/export/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/minimap/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/state/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/todos/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/sessions/summary/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/skills/check/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/skills/install/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/skills/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/skills/search/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/skills/update/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/subagents/[id]/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/subagents/profiles/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/subagents/settings/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/task-card-questions/[id]/answer/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/task-card-questions/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/task-cards/[id]/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/task-cards/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/task-cards/unbind/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/task-scheduler/status/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/tasks/[id]/assign-session/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/tasks/[id]/board/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/tasks/[id]/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/tasks/reorder/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/tasks/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/terminal/[id]/events/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/terminal/[id]/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/terminal/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/worktrees/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/api/yjs-version/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/layout-fae2b226b3c78448.js +1 -0
- package/.next/static/chunks/app/manifest.webmanifest/route-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/app/page-d5af3bd535589daf.js +256 -0
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-ad6e695deb9de65c.js +1 -0
- package/.next/static/chunks/{webpack-b0effde51c1651b8.js → webpack-1ce35e1249cfd931.js} +1 -1
- package/.next/static/css/bb9361c7f832a89d.css +1 -0
- package/.next/static/css/c7d127e07b1a786c.css +1 -0
- package/.next/trace +8 -7
- package/.next/trace-build +1 -1
- package/.next/types/app/api/boards/[id]/add-session/route.ts +353 -0
- package/.next/types/app/api/subagents/[id]/route.ts +353 -0
- package/.next/types/app/api/subagents/profiles/route.ts +353 -0
- package/.next/types/app/api/subagents/settings/route.ts +353 -0
- package/.next/types/routes.d.ts +5 -1
- package/.next/types/validator.ts +36 -0
- package/lib/api-types.ts +10 -0
- package/lib/board-reconcile.test.mjs +46 -8
- package/lib/board-reconcile.ts +57 -1
- package/lib/board-store.test.mjs +2 -2
- package/lib/chat-lazy-load.test.mjs +0 -62
- package/lib/chat-lazy-load.ts +0 -13
- package/lib/chat-only.ts +23 -0
- package/lib/draft-stash.ts +4 -5
- package/lib/file-types.ts +16 -1
- package/lib/i18n/messages/en.ts +91 -0
- package/lib/i18n/messages/zh-CN.ts +91 -0
- package/lib/id.test.mjs +38 -0
- package/lib/id.ts +17 -0
- package/lib/ime-guard.test.mjs +52 -0
- package/lib/ime-guard.ts +54 -0
- package/lib/message-text.ts +24 -0
- package/lib/models-cache.ts +1 -1
- package/lib/pi-types.ts +19 -1
- package/lib/pin-store.ts +85 -0
- package/lib/powershell-settings.ts +106 -0
- package/lib/quoted-selection.ts +4 -0
- package/lib/rpc-manager.activity.test.mjs +223 -0
- package/lib/rpc-manager.test.mjs +22 -0
- package/lib/rpc-manager.title.test.mjs +152 -0
- package/lib/rpc-manager.ts +301 -28
- package/lib/session-activity.test.mjs +123 -0
- package/lib/session-activity.ts +77 -0
- package/lib/session-delete.test.mjs +45 -41
- package/lib/session-delete.ts +8 -34
- package/lib/session-family.test.mjs +49 -0
- package/lib/session-family.ts +80 -0
- package/lib/session-index-scanner.test.mjs +364 -2
- package/lib/session-index-scanner.ts +199 -55
- package/lib/session-reader.noscan.test.mjs +371 -0
- package/lib/session-reader.project.test.mjs +30 -52
- package/lib/session-reader.test.mjs +2 -65
- package/lib/session-reader.ts +177 -213
- package/lib/session-scanner.test.mjs +151 -38
- package/lib/session-scanner.ts +140 -61
- package/lib/session-search.ts +6 -5
- package/lib/session-time-group.test.mjs +38 -0
- package/lib/session-time-group.ts +55 -0
- package/lib/settings-navigation.ts +124 -0
- package/lib/sqlite-db.test.mjs +26 -0
- package/lib/sqlite-db.ts +10 -1
- package/lib/subagent-extension.test.mjs +424 -0
- package/lib/subagent-extension.ts +309 -0
- package/lib/subagent-input.test.mjs +63 -0
- package/lib/subagent-input.ts +71 -0
- package/lib/subagent-profile-precedence.ts +20 -0
- package/lib/subagent-prompt.test.mjs +64 -0
- package/lib/subagent-prompt.ts +31 -0
- package/lib/subagent-queue.test.mjs +35 -0
- package/lib/subagent-queue.ts +74 -0
- package/lib/subagent-runtime.test.mjs +150 -0
- package/lib/subagent-runtime.ts +629 -0
- package/lib/subagent-session-title.test.mjs +120 -0
- package/lib/subagent-settings.test.mjs +50 -0
- package/lib/subagent-settings.ts +93 -0
- package/lib/subagents.test.mjs +601 -0
- package/lib/subagents.ts +602 -0
- package/lib/task-scheduler.test.mjs +27 -0
- package/lib/task-scheduler.ts +8 -1
- package/lib/task-store.activity.test.mjs +141 -0
- package/lib/task-store.meta-row.test.mjs +25 -5
- package/lib/task-store.partial-row.test.mjs +198 -0
- package/lib/task-store.subtree.test.mjs +172 -0
- package/lib/task-store.test.mjs +46 -20
- package/lib/task-store.ts +344 -68
- package/lib/tool-presets.ts +4 -0
- package/lib/types.ts +19 -0
- package/lib/worktree-isolation.test.mjs +51 -0
- package/next.config.ts +3 -1
- package/package.json +1 -1
- package/.next/static/bsW3fla0-BlBHVMKtAM5I/_buildManifest.js +0 -1
- package/.next/static/chunks/752.0ad92735d5f38143.js +0 -1
- package/.next/static/chunks/8088.53d9cdaf15634d4b.js +0 -1
- package/.next/static/chunks/app/_global-error/page-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/bash-output/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/events/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/agent/new/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/agent/running/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/app-update/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/auth/all-providers/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/auth/api-key/[provider]/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/auth/login/[provider]/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/auth/logout/[provider]/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/auth/providers/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/board-assets/[...path]/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/board-assets/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/boards/[id]/reconcile/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/boards/[id]/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/boards/reorder/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/boards/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/cwd/browse/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/cwd/validate/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/default-cwd/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/file-index/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/files/[...path]/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/git/diff/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/git/status/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/home/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/mcp/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/models/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/models-config/catalog/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/models-config/discover/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/models-config/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/models-config/test/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/plugins/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/project-trust/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/quota/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/search/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/auto-name/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/context/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/entries/[entryId]/thinking/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/export/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/minimap/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/state/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/todos/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/sessions/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/sessions/summary/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/skills/check/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/skills/install/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/skills/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/skills/search/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/skills/update/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/task-card-questions/[id]/answer/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/task-card-questions/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/task-cards/[id]/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/task-cards/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/task-cards/unbind/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/task-scheduler/status/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/tasks/[id]/assign-session/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/tasks/[id]/board/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/tasks/[id]/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/tasks/reorder/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/tasks/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/terminal/[id]/events/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/terminal/[id]/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/terminal/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/worktrees/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/api/yjs-version/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/layout-dace09825b2d3857.js +0 -1
- package/.next/static/chunks/app/manifest.webmanifest/route-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/app/page-64b9f4b3567a2f73.js +0 -89
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-2d5f6296c7e693c6.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-2d5f6296c7e693c6.js +0 -1
- package/.next/static/css/fd25673802257262.css +0 -1
- package/lib/extension-command.test.mjs +0 -63
- package/lib/extension-command.ts +0 -81
- package/lib/subagent-widget.test.mjs +0 -87
- package/lib/subagent-widget.ts +0 -109
- /package/.next/static/{bsW3fla0-BlBHVMKtAM5I → MPBMmQqmDG8zo8tM_bkDy}/_ssgManifest.js +0 -0
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 会话列表的时间分组(今天 / 昨天 / 本周 / 近一月 / 更久之前)。
|
|
3
|
+
*
|
|
4
|
+
* 只做分类,不排序:调用方(侧栏聊天区)已按 modified 倒序排好,
|
|
5
|
+
* 这里只负责给每条会话贴上它属于哪一段。
|
|
6
|
+
*/
|
|
7
|
+
export type SessionTimeGroup = "today" | "yesterday" | "thisWeek" | "thisMonth" | "older";
|
|
8
|
+
|
|
9
|
+
/** 分组展示顺序(由近及远)。 */
|
|
10
|
+
export const SESSION_TIME_GROUP_ORDER: readonly SessionTimeGroup[] = [
|
|
11
|
+
"today",
|
|
12
|
+
"yesterday",
|
|
13
|
+
"thisWeek",
|
|
14
|
+
"thisMonth",
|
|
15
|
+
"older",
|
|
16
|
+
];
|
|
17
|
+
|
|
18
|
+
/** 近一月的窗口长度(含今天),滚动 30 天而非自然月。 */
|
|
19
|
+
const RECENT_MONTH_DAYS = 30;
|
|
20
|
+
|
|
21
|
+
function startOfDay(ms: number): number {
|
|
22
|
+
const d = new Date(ms);
|
|
23
|
+
d.setHours(0, 0, 0, 0);
|
|
24
|
+
return d.getTime();
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/** 加减自然日(走 Date,跨夏令时不会少/多一小时)。 */
|
|
28
|
+
function addDays(ms: number, days: number): number {
|
|
29
|
+
const d = new Date(ms);
|
|
30
|
+
d.setDate(d.getDate() + days);
|
|
31
|
+
return d.getTime();
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** 本周起始(周一 00:00),与中文习惯一致。 */
|
|
35
|
+
function startOfWeek(ms: number): number {
|
|
36
|
+
const day = new Date(ms).getDay(); // 0 = 周日
|
|
37
|
+
return addDays(startOfDay(ms), -((day + 6) % 7));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* 判断一条会话的修改时间落在哪个时间分组。
|
|
42
|
+
* @param modified 会话修改时间(ISO 字符串或毫秒时间戳)
|
|
43
|
+
* @param now 当前时间(毫秒),用于测试注入
|
|
44
|
+
* @returns 时间分组;时间不可解析时按「更久之前」处理
|
|
45
|
+
*/
|
|
46
|
+
export function sessionTimeGroup(modified: string | number, now: number = Date.now()): SessionTimeGroup {
|
|
47
|
+
const ms = typeof modified === "number" ? modified : Date.parse(modified);
|
|
48
|
+
if (!Number.isFinite(ms)) return "older";
|
|
49
|
+
// 未来时间(客户端与服务端时钟漂移)归今天,别把刚写的会话沉到底部。
|
|
50
|
+
if (ms >= startOfDay(now)) return "today";
|
|
51
|
+
if (ms >= addDays(startOfDay(now), -1)) return "yesterday";
|
|
52
|
+
if (ms >= startOfWeek(now)) return "thisWeek";
|
|
53
|
+
if (ms >= addDays(startOfDay(now), -(RECENT_MONTH_DAYS - 1))) return "thisMonth";
|
|
54
|
+
return "older";
|
|
55
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
export const SETTINGS_SECTION_VALUES = [
|
|
2
|
+
"general",
|
|
3
|
+
"models",
|
|
4
|
+
"skills",
|
|
5
|
+
"agents",
|
|
6
|
+
"plugins",
|
|
7
|
+
] as const;
|
|
8
|
+
|
|
9
|
+
export type SettingsSection = (typeof SETTINGS_SECTION_VALUES)[number];
|
|
10
|
+
export type SettingsDetailSection = Exclude<SettingsSection, "general">;
|
|
11
|
+
|
|
12
|
+
const STORAGE_KEY = "pi-web:settings-navigation";
|
|
13
|
+
const PROJECT_SECTIONS = new Set<SettingsSection>(["skills", "agents", "plugins"]);
|
|
14
|
+
|
|
15
|
+
interface StorageLike {
|
|
16
|
+
getItem(key: string): string | null;
|
|
17
|
+
setItem(key: string, value: string): void;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface SettingsNavigationState {
|
|
21
|
+
section?: string;
|
|
22
|
+
selections?: Record<string, string>;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function getBrowserStorage(): StorageLike | null {
|
|
26
|
+
if (typeof window === "undefined") return null;
|
|
27
|
+
try {
|
|
28
|
+
return window.localStorage;
|
|
29
|
+
} catch {
|
|
30
|
+
return null;
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function isSettingsSection(value: unknown): value is SettingsSection {
|
|
35
|
+
return typeof value === "string"
|
|
36
|
+
&& SETTINGS_SECTION_VALUES.includes(value as SettingsSection);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function readState(storage: StorageLike): SettingsNavigationState {
|
|
40
|
+
const raw = storage.getItem(STORAGE_KEY);
|
|
41
|
+
if (!raw) return {};
|
|
42
|
+
try {
|
|
43
|
+
const parsed: unknown = JSON.parse(raw);
|
|
44
|
+
if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {};
|
|
45
|
+
const state = parsed as SettingsNavigationState;
|
|
46
|
+
return {
|
|
47
|
+
section: typeof state.section === "string" ? state.section : undefined,
|
|
48
|
+
selections: state.selections !== null
|
|
49
|
+
&& typeof state.selections === "object"
|
|
50
|
+
&& !Array.isArray(state.selections)
|
|
51
|
+
? state.selections
|
|
52
|
+
: undefined,
|
|
53
|
+
};
|
|
54
|
+
} catch {
|
|
55
|
+
return {};
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function selectionKey(section: SettingsDetailSection, cwd?: string | null): string | null {
|
|
60
|
+
if (section === "models") return section;
|
|
61
|
+
return cwd ? JSON.stringify([section, cwd]) : null;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function getLastSettingsSection(
|
|
65
|
+
cwd: string | null,
|
|
66
|
+
storage: StorageLike | null = getBrowserStorage(),
|
|
67
|
+
): SettingsSection {
|
|
68
|
+
if (!storage) return "general";
|
|
69
|
+
try {
|
|
70
|
+
const section = readState(storage).section;
|
|
71
|
+
if (!isSettingsSection(section)) return "general";
|
|
72
|
+
return PROJECT_SECTIONS.has(section) && !cwd ? "general" : section;
|
|
73
|
+
} catch {
|
|
74
|
+
return "general";
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
export function setLastSettingsSection(
|
|
79
|
+
section: SettingsSection,
|
|
80
|
+
storage: StorageLike | null = getBrowserStorage(),
|
|
81
|
+
): void {
|
|
82
|
+
if (!storage) return;
|
|
83
|
+
try {
|
|
84
|
+
const state = readState(storage);
|
|
85
|
+
state.section = section;
|
|
86
|
+
storage.setItem(STORAGE_KEY, JSON.stringify(state));
|
|
87
|
+
} catch {
|
|
88
|
+
// Browser storage is best-effort.
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export function getLastSettingsSelection(
|
|
93
|
+
section: SettingsDetailSection,
|
|
94
|
+
cwd?: string | null,
|
|
95
|
+
storage: StorageLike | null = getBrowserStorage(),
|
|
96
|
+
): string | null {
|
|
97
|
+
if (!storage) return null;
|
|
98
|
+
const key = selectionKey(section, cwd);
|
|
99
|
+
if (!key) return null;
|
|
100
|
+
try {
|
|
101
|
+
const value = readState(storage).selections?.[key];
|
|
102
|
+
return typeof value === "string" && value.length > 0 ? value : null;
|
|
103
|
+
} catch {
|
|
104
|
+
return null;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function setLastSettingsSelection(
|
|
109
|
+
section: SettingsDetailSection,
|
|
110
|
+
value: string,
|
|
111
|
+
cwd?: string | null,
|
|
112
|
+
storage: StorageLike | null = getBrowserStorage(),
|
|
113
|
+
): void {
|
|
114
|
+
if (!storage || !value) return;
|
|
115
|
+
const key = selectionKey(section, cwd);
|
|
116
|
+
if (!key) return;
|
|
117
|
+
try {
|
|
118
|
+
const state = readState(storage);
|
|
119
|
+
state.selections = { ...state.selections, [key]: value };
|
|
120
|
+
storage.setItem(STORAGE_KEY, JSON.stringify(state));
|
|
121
|
+
} catch {
|
|
122
|
+
// Browser storage is best-effort.
|
|
123
|
+
}
|
|
124
|
+
}
|
package/lib/sqlite-db.test.mjs
CHANGED
|
@@ -189,3 +189,29 @@ test("v10 database upgrades to v11 (session_meta index columns + project index)"
|
|
|
189
189
|
assert.equal(row.task_id, "task-1");
|
|
190
190
|
assert.equal(row.pinned, 1);
|
|
191
191
|
});
|
|
192
|
+
|
|
193
|
+
test("schema v12:老库升级后 session_meta 有 last_reply 列,新库直接具备", () => {
|
|
194
|
+
const old = new DatabaseSync(":memory:");
|
|
195
|
+
old.exec("PRAGMA user_version = 11");
|
|
196
|
+
old.exec(
|
|
197
|
+
"CREATE TABLE session_meta (session_id TEXT PRIMARY KEY, task_id TEXT, updated INTEGER NOT NULL, pinned INTEGER NOT NULL DEFAULT 0, path TEXT, cwd TEXT, project_key TEXT, title TEXT, first_message TEXT, parent_id TEXT, created INTEGER, modified INTEGER)",
|
|
198
|
+
);
|
|
199
|
+
old.prepare(
|
|
200
|
+
"INSERT INTO session_meta (session_id, updated, modified) VALUES ('s-old', 1, 2)",
|
|
201
|
+
).run();
|
|
202
|
+
|
|
203
|
+
initSchema(old);
|
|
204
|
+
|
|
205
|
+
const cols = old.prepare("PRAGMA table_info(session_meta)").all().map((c) => c.name);
|
|
206
|
+
assert.ok(cols.includes("last_reply"), "v12 adds session_meta.last_reply");
|
|
207
|
+
assert.equal(old.prepare("PRAGMA user_version").get().user_version, 12);
|
|
208
|
+
// 老行保留,新列默认 NULL("还没有最后一条消息")
|
|
209
|
+
const kept = old.prepare("SELECT session_id, last_reply FROM session_meta WHERE session_id='s-old'").get();
|
|
210
|
+
assert.equal(kept.session_id, "s-old");
|
|
211
|
+
assert.equal(kept.last_reply, null);
|
|
212
|
+
// 新库第一次建表就该有该列(老库升级与新库同构)
|
|
213
|
+
const fresh = memoryDb();
|
|
214
|
+
const freshCols = fresh.prepare("PRAGMA table_info(session_meta)").all().map((c) => c.name);
|
|
215
|
+
assert.ok(freshCols.includes("last_reply"));
|
|
216
|
+
assert.equal(fresh.prepare("PRAGMA user_version").get().user_version, 12);
|
|
217
|
+
});
|
package/lib/sqlite-db.ts
CHANGED
|
@@ -93,7 +93,7 @@ export function initSchema(db: DatabaseSync): void {
|
|
|
93
93
|
* 老库打开时自动按序补齐缺失的迁移(每个迁移一个事务,成功后推进版本号),
|
|
94
94
|
* 新库建表后从 v0 一路迁到 SCHEMA_VERSION。重复打开不再执行已完成的迁移。
|
|
95
95
|
*/
|
|
96
|
-
export const SCHEMA_VERSION =
|
|
96
|
+
export const SCHEMA_VERSION = 12;
|
|
97
97
|
|
|
98
98
|
interface Migration {
|
|
99
99
|
version: number;
|
|
@@ -219,6 +219,15 @@ const MIGRATIONS: Migration[] = [
|
|
|
219
219
|
"CREATE INDEX IF NOT EXISTS idx_meta_project_modified ON session_meta(project_key, modified DESC);",
|
|
220
220
|
],
|
|
221
221
|
},
|
|
222
|
+
{
|
|
223
|
+
version: 12,
|
|
224
|
+
name: "session_meta.last_reply(最后一条消息入库,列表零扫盘)",
|
|
225
|
+
statements: [
|
|
226
|
+
// 「最后一条消息」原先靠读取期读文件尾(滞后且每次列表都要摸盘)。改由事件
|
|
227
|
+
// 链路在一轮循环结束时写入(agent_settled,含取消),读取路径不再读文件。
|
|
228
|
+
"ALTER TABLE session_meta ADD COLUMN last_reply TEXT;",
|
|
229
|
+
],
|
|
230
|
+
},
|
|
222
231
|
];
|
|
223
232
|
|
|
224
233
|
function migrate(db: DatabaseSync): void {
|
|
@@ -0,0 +1,424 @@
|
|
|
1
|
+
import assert from "node:assert/strict";
|
|
2
|
+
import test from "node:test";
|
|
3
|
+
import { createJiti } from "jiti";
|
|
4
|
+
|
|
5
|
+
const {
|
|
6
|
+
createSubagentExtension,
|
|
7
|
+
preferPiWebSubagentExtension,
|
|
8
|
+
} = await createJiti(import.meta.url).import("./subagent-extension.ts");
|
|
9
|
+
|
|
10
|
+
async function loadTools(runtime, getProfiles = () => [], isEnabled = () => true) {
|
|
11
|
+
const tools = new Map();
|
|
12
|
+
const sent = [];
|
|
13
|
+
const extension = createSubagentExtension(runtime, getProfiles, isEnabled);
|
|
14
|
+
await extension.factory({
|
|
15
|
+
registerTool(tool) { tools.set(tool.name, tool); },
|
|
16
|
+
sendMessage(message, options) { sent.push({ message, options }); },
|
|
17
|
+
});
|
|
18
|
+
return { tools, sent };
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function run(overrides = {}) {
|
|
22
|
+
return {
|
|
23
|
+
sessionId: "child-session",
|
|
24
|
+
sessionPath: "/tmp/child.jsonl",
|
|
25
|
+
parentSessionId: "parent-session",
|
|
26
|
+
parentToolCallId: "tool-call",
|
|
27
|
+
profile: "explore",
|
|
28
|
+
description: "Find parser",
|
|
29
|
+
task: "Find the parser",
|
|
30
|
+
runInBackground: false,
|
|
31
|
+
status: "running",
|
|
32
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
33
|
+
...overrides,
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
test("integrated extension exposes the legacy-compatible tool names", async () => {
|
|
38
|
+
const tools = new Map();
|
|
39
|
+
const extension = createSubagentExtension({
|
|
40
|
+
async start(request) {
|
|
41
|
+
const running = {
|
|
42
|
+
sessionId: "child-session",
|
|
43
|
+
sessionPath: "/tmp/child.jsonl",
|
|
44
|
+
parentSessionId: "parent-session",
|
|
45
|
+
parentToolCallId: request.parentToolCallId,
|
|
46
|
+
profile: request.profile,
|
|
47
|
+
description: request.description,
|
|
48
|
+
task: request.task,
|
|
49
|
+
runInBackground: true,
|
|
50
|
+
status: "running",
|
|
51
|
+
createdAt: "2026-01-01T00:00:00.000Z",
|
|
52
|
+
};
|
|
53
|
+
return {
|
|
54
|
+
run: running,
|
|
55
|
+
completion: Promise.resolve({
|
|
56
|
+
...running,
|
|
57
|
+
status: "completed",
|
|
58
|
+
completedAt: "2026-01-01T00:01:00.000Z",
|
|
59
|
+
result: "Done",
|
|
60
|
+
}),
|
|
61
|
+
};
|
|
62
|
+
},
|
|
63
|
+
async get() { return null; },
|
|
64
|
+
async steer() {},
|
|
65
|
+
async notifyParent() {},
|
|
66
|
+
}, () => []);
|
|
67
|
+
await extension.factory({
|
|
68
|
+
registerTool(tool) { tools.set(tool.name, tool); },
|
|
69
|
+
sendMessage() {},
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
assert.deepEqual([...tools.keys()], ["Agent", "get_subagent_result", "steer_subagent"]);
|
|
73
|
+
const result = await tools.get("Agent").execute(
|
|
74
|
+
"tool-call",
|
|
75
|
+
{
|
|
76
|
+
subagent_type: "explore",
|
|
77
|
+
prompt: "Find the parser",
|
|
78
|
+
input_files: ["docs/parser.md"],
|
|
79
|
+
description: "Find parser",
|
|
80
|
+
run_in_background: true,
|
|
81
|
+
},
|
|
82
|
+
undefined,
|
|
83
|
+
undefined,
|
|
84
|
+
{ sessionManager: { getSessionId: () => "parent-session" } },
|
|
85
|
+
);
|
|
86
|
+
assert.equal(result.details.kind, "pi-web-subagent");
|
|
87
|
+
assert.equal(result.details.sessionId, "child-session");
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
test("integrated extension registers no tools while its feature is disabled", async () => {
|
|
91
|
+
let enabled = false;
|
|
92
|
+
const runtime = {
|
|
93
|
+
async start() { throw new Error("unused"); },
|
|
94
|
+
async get() { return null; },
|
|
95
|
+
async steer() {},
|
|
96
|
+
async notifyParent() {},
|
|
97
|
+
};
|
|
98
|
+
const extension = createSubagentExtension(runtime, () => [], () => enabled);
|
|
99
|
+
const disabledTools = new Map();
|
|
100
|
+
await extension.factory({ registerTool(tool) { disabledTools.set(tool.name, tool); } });
|
|
101
|
+
assert.deepEqual([...disabledTools], []);
|
|
102
|
+
|
|
103
|
+
enabled = true;
|
|
104
|
+
const enabledTools = new Map();
|
|
105
|
+
await extension.factory({ registerTool(tool) { enabledTools.set(tool.name, tool); } });
|
|
106
|
+
assert.deepEqual([...enabledTools.keys()], ["Agent", "get_subagent_result", "steer_subagent"]);
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
test("Agent tool description lists enabled effective profiles and refreshes when its factory reloads", async () => {
|
|
110
|
+
const runtime = {
|
|
111
|
+
async start() { throw new Error("unused"); },
|
|
112
|
+
async get() { return null; },
|
|
113
|
+
async steer() {},
|
|
114
|
+
async notifyParent() {},
|
|
115
|
+
};
|
|
116
|
+
let profiles = [
|
|
117
|
+
{
|
|
118
|
+
name: "explore",
|
|
119
|
+
displayName: "Explore",
|
|
120
|
+
description: "Inspect the codebase",
|
|
121
|
+
systemPrompt: "Explore",
|
|
122
|
+
tools: ["read", "grep"],
|
|
123
|
+
inheritContext: false,
|
|
124
|
+
runInBackground: false,
|
|
125
|
+
enabled: true,
|
|
126
|
+
scope: "builtin",
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
name: "disabled-agent",
|
|
130
|
+
displayName: "Disabled agent",
|
|
131
|
+
description: "Must stay hidden",
|
|
132
|
+
systemPrompt: "Disabled",
|
|
133
|
+
tools: [],
|
|
134
|
+
model: "provider/hidden",
|
|
135
|
+
inheritContext: false,
|
|
136
|
+
runInBackground: false,
|
|
137
|
+
enabled: false,
|
|
138
|
+
scope: "project",
|
|
139
|
+
},
|
|
140
|
+
];
|
|
141
|
+
const extension = createSubagentExtension(runtime, () => profiles);
|
|
142
|
+
|
|
143
|
+
const firstTools = new Map();
|
|
144
|
+
await extension.factory({ registerTool(tool) { firstTools.set(tool.name, tool); }, sendMessage() {} });
|
|
145
|
+
const first = firstTools.get("Agent");
|
|
146
|
+
assert.match(first.description, /- explore: Inspect the codebase \(Tools: read, grep\)/);
|
|
147
|
+
assert.doesNotMatch(first.description, /disabled-agent|Must stay hidden/);
|
|
148
|
+
assert.match(first.parameters.properties.subagent_type.description, /Available types: explore/);
|
|
149
|
+
|
|
150
|
+
profiles = [{
|
|
151
|
+
name: "reviewer",
|
|
152
|
+
displayName: "Reviewer",
|
|
153
|
+
description: "Review a change",
|
|
154
|
+
systemPrompt: "Review",
|
|
155
|
+
tools: [],
|
|
156
|
+
model: "provider/review-model",
|
|
157
|
+
inheritContext: false,
|
|
158
|
+
runInBackground: false,
|
|
159
|
+
enabled: true,
|
|
160
|
+
scope: "project",
|
|
161
|
+
}];
|
|
162
|
+
const reloadedTools = new Map();
|
|
163
|
+
await extension.factory({ registerTool(tool) { reloadedTools.set(tool.name, tool); }, sendMessage() {} });
|
|
164
|
+
const reloaded = reloadedTools.get("Agent");
|
|
165
|
+
assert.match(reloaded.description, /- reviewer: Review a change \(Tools: none; Model: provider\/review-model\)/);
|
|
166
|
+
assert.doesNotMatch(reloaded.description, /explore/);
|
|
167
|
+
assert.match(reloaded.parameters.properties.subagent_type.description, /Available types: reviewer/);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
test("integrated extension removes a legacy extension that owns the same tools", () => {
|
|
171
|
+
const host = {
|
|
172
|
+
path: "<inline:pi-web-subagents>",
|
|
173
|
+
tools: new Map([["Agent", {}], ["get_subagent_result", {}], ["steer_subagent", {}]]),
|
|
174
|
+
};
|
|
175
|
+
const legacy = {
|
|
176
|
+
path: "/tmp/pi-subagents/index.ts",
|
|
177
|
+
tools: new Map([["Agent", {}], ["get_subagent_result", {}], ["steer_subagent", {}]]),
|
|
178
|
+
};
|
|
179
|
+
const other = { path: "/tmp/other.ts", tools: new Map([["search", {}]]) };
|
|
180
|
+
const lookalike = {
|
|
181
|
+
path: "/tmp/unrelated/index.ts",
|
|
182
|
+
tools: new Map([["Agent", {}], ["get_subagent_result", {}], ["steer_subagent", {}], ["custom", {}]]),
|
|
183
|
+
};
|
|
184
|
+
const result = preferPiWebSubagentExtension({
|
|
185
|
+
extensions: [legacy, lookalike, host, other],
|
|
186
|
+
errors: [{ path: legacy.path, error: "duplicate" }, { path: other.path, error: "other" }],
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
assert.deepEqual(result.extensions.map((extension) => extension.path), [lookalike.path, host.path, other.path]);
|
|
190
|
+
assert.deepEqual(result.errors.map((error) => error.path), [other.path]);
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
test("integrated extension removes recognized legacy extensions with any reserved tool", () => {
|
|
194
|
+
const host = {
|
|
195
|
+
path: "<inline:pi-web-subagents>",
|
|
196
|
+
tools: new Map([["Agent", {}], ["get_subagent_result", {}], ["steer_subagent", {}]]),
|
|
197
|
+
};
|
|
198
|
+
for (const toolName of ["Agent", "get_subagent_result", "steer_subagent"]) {
|
|
199
|
+
const legacy = {
|
|
200
|
+
path: "/tmp/pi-subagents/index.ts",
|
|
201
|
+
tools: new Map([[toolName, {}], ["other", {}]]),
|
|
202
|
+
};
|
|
203
|
+
const result = preferPiWebSubagentExtension({
|
|
204
|
+
extensions: [legacy, host],
|
|
205
|
+
errors: [{
|
|
206
|
+
path: host.path,
|
|
207
|
+
error: `Tool "${toolName}" conflicts with ${legacy.path}`,
|
|
208
|
+
}],
|
|
209
|
+
});
|
|
210
|
+
assert.deepEqual(result.extensions, [host]);
|
|
211
|
+
assert.deepEqual(result.errors, []);
|
|
212
|
+
}
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
test("foreground Agent streams updates and returns the completed result", async () => {
|
|
216
|
+
const updates = [];
|
|
217
|
+
let request;
|
|
218
|
+
const { tools } = await loadTools({
|
|
219
|
+
async start(value) {
|
|
220
|
+
request = value;
|
|
221
|
+
value.onUpdate(run({ status: "running" }));
|
|
222
|
+
return {
|
|
223
|
+
run: run(),
|
|
224
|
+
completion: Promise.resolve(run({
|
|
225
|
+
status: "completed",
|
|
226
|
+
completedAt: "2026-01-01T00:01:00.000Z",
|
|
227
|
+
result: "Parser is in lib/parser.ts",
|
|
228
|
+
})),
|
|
229
|
+
};
|
|
230
|
+
},
|
|
231
|
+
async get() { return null; },
|
|
232
|
+
async steer() {},
|
|
233
|
+
});
|
|
234
|
+
const result = await tools.get("Agent").execute(
|
|
235
|
+
"tool-call",
|
|
236
|
+
{
|
|
237
|
+
subagent_type: "explore",
|
|
238
|
+
prompt: "Find the parser",
|
|
239
|
+
input_files: ["docs/parser.md"],
|
|
240
|
+
description: "Find parser",
|
|
241
|
+
},
|
|
242
|
+
undefined,
|
|
243
|
+
(update) => updates.push(update),
|
|
244
|
+
{ sessionManager: { getSessionId: () => "parent-session" } },
|
|
245
|
+
);
|
|
246
|
+
|
|
247
|
+
assert.equal(request.parentToolCallId, "tool-call");
|
|
248
|
+
assert.equal(request.task, "Find the parser");
|
|
249
|
+
assert.deepEqual(request.inputFiles, ["docs/parser.md"]);
|
|
250
|
+
assert.equal(updates[0].details.status, "running");
|
|
251
|
+
assert.equal(result.content[0].text, "Parser is in lib/parser.ts");
|
|
252
|
+
assert.equal(result.details.status, "completed");
|
|
253
|
+
assert.equal(result.isError, undefined);
|
|
254
|
+
});
|
|
255
|
+
|
|
256
|
+
test("foreground Agent reports startup and execution failures as tool errors", async () => {
|
|
257
|
+
const { tools: startFailureTools } = await loadTools({
|
|
258
|
+
async start() { throw new Error("profile missing"); },
|
|
259
|
+
async get() { return null; },
|
|
260
|
+
async steer() {},
|
|
261
|
+
});
|
|
262
|
+
const startup = await startFailureTools.get("Agent").execute(
|
|
263
|
+
"tool-call",
|
|
264
|
+
{ prompt: "task", description: "desc" },
|
|
265
|
+
undefined,
|
|
266
|
+
undefined,
|
|
267
|
+
{ sessionManager: { getSessionId: () => "parent-session" } },
|
|
268
|
+
);
|
|
269
|
+
assert.equal(startup.isError, true);
|
|
270
|
+
assert.equal(startup.content[0].text, "profile missing");
|
|
271
|
+
|
|
272
|
+
const { tools: runFailureTools } = await loadTools({
|
|
273
|
+
async start() {
|
|
274
|
+
return {
|
|
275
|
+
run: run(),
|
|
276
|
+
completion: Promise.resolve(run({ status: "failed", error: "model failed" })),
|
|
277
|
+
};
|
|
278
|
+
},
|
|
279
|
+
async get() { return null; },
|
|
280
|
+
async steer() {},
|
|
281
|
+
});
|
|
282
|
+
const failure = await runFailureTools.get("Agent").execute(
|
|
283
|
+
"tool-call",
|
|
284
|
+
{ prompt: "task", description: "desc" },
|
|
285
|
+
undefined,
|
|
286
|
+
undefined,
|
|
287
|
+
{ sessionManager: { getSessionId: () => "parent-session" } },
|
|
288
|
+
);
|
|
289
|
+
assert.equal(failure.isError, true);
|
|
290
|
+
assert.match(failure.content[0].text, /model failed/);
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
test("Agent resume routes the prompt to the existing-session runtime", async () => {
|
|
294
|
+
let resumed;
|
|
295
|
+
const { tools } = await loadTools({
|
|
296
|
+
async start() { throw new Error("must use resume"); },
|
|
297
|
+
async resume(request) {
|
|
298
|
+
resumed = request;
|
|
299
|
+
return { run: run({ sessionId: "existing", runInBackground: false }), completion: Promise.resolve(run({ sessionId: "existing", status: "completed", result: "continued" })) };
|
|
300
|
+
},
|
|
301
|
+
async get() { return null; },
|
|
302
|
+
async steer() {},
|
|
303
|
+
});
|
|
304
|
+
const result = await tools.get("Agent").execute(
|
|
305
|
+
"tool-call",
|
|
306
|
+
{ resume: "existing", prompt: "Continue the review", description: "Continue review" },
|
|
307
|
+
undefined,
|
|
308
|
+
undefined,
|
|
309
|
+
{ sessionManager: { getSessionId: () => "parent-session" } },
|
|
310
|
+
);
|
|
311
|
+
assert.equal(resumed.sessionId, "existing");
|
|
312
|
+
assert.equal(resumed.task, "Continue the review");
|
|
313
|
+
assert.equal(result.content[0].text, "continued");
|
|
314
|
+
});
|
|
315
|
+
|
|
316
|
+
test("background Agent returns immediately and delegates one completion notification", async () => {
|
|
317
|
+
let finish;
|
|
318
|
+
const notifications = [];
|
|
319
|
+
const completion = new Promise((resolve) => { finish = resolve; });
|
|
320
|
+
const { tools, sent } = await loadTools({
|
|
321
|
+
async start() {
|
|
322
|
+
return { run: run({ runInBackground: true }), completion };
|
|
323
|
+
},
|
|
324
|
+
async get() { return null; },
|
|
325
|
+
async steer() {},
|
|
326
|
+
async notifyParent(completed) { notifications.push(completed); },
|
|
327
|
+
});
|
|
328
|
+
const result = await tools.get("Agent").execute(
|
|
329
|
+
"tool-call",
|
|
330
|
+
{ prompt: "task", description: "desc", run_in_background: true },
|
|
331
|
+
undefined,
|
|
332
|
+
undefined,
|
|
333
|
+
{ sessionManager: { getSessionId: () => "parent-session" } },
|
|
334
|
+
);
|
|
335
|
+
assert.match(result.content[0].text, /started in background/);
|
|
336
|
+
assert.equal(sent.length, 0);
|
|
337
|
+
|
|
338
|
+
finish(run({ runInBackground: true, status: "completed", result: "done" }));
|
|
339
|
+
await completion;
|
|
340
|
+
await Promise.resolve();
|
|
341
|
+
assert.equal(sent.length, 0);
|
|
342
|
+
assert.equal(notifications.length, 1);
|
|
343
|
+
assert.equal(notifications[0].result, "done");
|
|
344
|
+
});
|
|
345
|
+
|
|
346
|
+
test("get_subagent_result covers missing, running, completed, failed, and aborted runs", async () => {
|
|
347
|
+
let current = null;
|
|
348
|
+
const { tools } = await loadTools({
|
|
349
|
+
async start() { throw new Error("unused"); },
|
|
350
|
+
async get() { return current; },
|
|
351
|
+
async steer() {},
|
|
352
|
+
});
|
|
353
|
+
const tool = tools.get("get_subagent_result");
|
|
354
|
+
|
|
355
|
+
let result = await tool.execute("call", { agent_id: "missing" });
|
|
356
|
+
assert.equal(result.isError, true);
|
|
357
|
+
assert.match(result.content[0].text, /not found/);
|
|
358
|
+
|
|
359
|
+
current = run();
|
|
360
|
+
result = await tool.execute("call", { agent_id: "child-session" });
|
|
361
|
+
assert.equal(result.details.status, "running");
|
|
362
|
+
assert.equal(result.content[0].text, "Subagent child-session is running.");
|
|
363
|
+
|
|
364
|
+
current = run({ status: "completed", result: "done" });
|
|
365
|
+
result = await tool.execute("call", { agent_id: "child-session" });
|
|
366
|
+
assert.equal(result.content[0].text, "done");
|
|
367
|
+
|
|
368
|
+
current = run({ status: "failed", error: "boom" });
|
|
369
|
+
result = await tool.execute("call", { agent_id: "child-session" });
|
|
370
|
+
assert.match(result.content[0].text, /boom/);
|
|
371
|
+
assert.equal(result.isError, true);
|
|
372
|
+
|
|
373
|
+
current = run({ status: "aborted" });
|
|
374
|
+
result = await tool.execute("call", { agent_id: "child-session" });
|
|
375
|
+
assert.match(result.content[0].text, /was stopped/);
|
|
376
|
+
|
|
377
|
+
current = run({ status: "interrupted" });
|
|
378
|
+
result = await tool.execute("call", { agent_id: "child-session" });
|
|
379
|
+
assert.match(result.content[0].text, /interrupted before completion/);
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
test("get_subagent_result wait honors abort signals", async () => {
|
|
383
|
+
const { tools } = await loadTools({
|
|
384
|
+
async start() { throw new Error("unused"); },
|
|
385
|
+
async get() { return run(); },
|
|
386
|
+
async steer() {},
|
|
387
|
+
});
|
|
388
|
+
const controller = new AbortController();
|
|
389
|
+
controller.abort();
|
|
390
|
+
await assert.rejects(
|
|
391
|
+
tools.get("get_subagent_result").execute("call", { agent_id: "child-session", wait: true }, controller.signal),
|
|
392
|
+
/Result wait aborted/,
|
|
393
|
+
);
|
|
394
|
+
});
|
|
395
|
+
|
|
396
|
+
test("steer_subagent returns success and runtime failures", async () => {
|
|
397
|
+
const calls = [];
|
|
398
|
+
const { tools } = await loadTools({
|
|
399
|
+
async start() { throw new Error("unused"); },
|
|
400
|
+
async get() { return null; },
|
|
401
|
+
async steer(id, message) {
|
|
402
|
+
calls.push([id, message]);
|
|
403
|
+
if (message === "fail") throw new Error("not running");
|
|
404
|
+
},
|
|
405
|
+
});
|
|
406
|
+
const tool = tools.get("steer_subagent");
|
|
407
|
+
const success = await tool.execute("call", { agent_id: "child-session", message: "focus" });
|
|
408
|
+
assert.deepEqual(calls[0], ["child-session", "focus"]);
|
|
409
|
+
assert.match(success.content[0].text, /sent/);
|
|
410
|
+
|
|
411
|
+
const failure = await tool.execute("call", { agent_id: "child-session", message: "fail" });
|
|
412
|
+
assert.equal(failure.isError, true);
|
|
413
|
+
assert.equal(failure.content[0].text, "not running");
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
test("legacy preference leaves unrelated and partial-overlap extensions intact", () => {
|
|
417
|
+
const host = { path: "<inline:pi-web-subagents>", tools: new Map() };
|
|
418
|
+
const partial = { path: "/tmp/partial.ts", tools: new Map([["Agent", {}]]) };
|
|
419
|
+
const base = { extensions: [host, partial], errors: [] };
|
|
420
|
+
assert.equal(preferPiWebSubagentExtension(base), base);
|
|
421
|
+
|
|
422
|
+
const noHost = { extensions: [partial], errors: [] };
|
|
423
|
+
assert.equal(preferPiWebSubagentExtension(noHost), noHost);
|
|
424
|
+
});
|