@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
package/lib/session-reader.ts
CHANGED
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
} from "@earendil-works/pi-coding-agent";
|
|
7
7
|
import { closeSync, existsSync, openSync, readSync } from "fs";
|
|
8
8
|
import { readdir } from "fs/promises";
|
|
9
|
-
import { join as joinPath, normalize as normalizePath } from "path";
|
|
9
|
+
import { join as joinPath, basename as basenamePath, normalize as normalizePath } from "path";
|
|
10
10
|
import type { AgentMessage, SessionEntry, SessionHeader, SessionInfo, SessionContext } from "./types";
|
|
11
11
|
import { parseTodoSnapshot, TODO_STATE_CUSTOM_TYPE, type Todo } from "./todo-store";
|
|
12
12
|
import type { SessionEntry as PiSessionEntry } from "@earendil-works/pi-coding-agent";
|
|
@@ -15,7 +15,7 @@ import { normalizeToolCalls } from "./normalize";
|
|
|
15
15
|
import { projectIdentityKey } from "./project-identity";
|
|
16
16
|
import { sessionPathKey } from "./session-path";
|
|
17
17
|
import { resolveProject, type ProjectInfo } from "./worktree";
|
|
18
|
-
import {
|
|
18
|
+
import { scanOneSessionFile } from "./session-scanner";
|
|
19
19
|
import { ensureSessionIndexReady } from "./session-index-scanner";
|
|
20
20
|
import type { TurnIndexItem } from "./api-types";
|
|
21
21
|
|
|
@@ -23,7 +23,13 @@ export { getAgentDir };
|
|
|
23
23
|
|
|
24
24
|
type SessionMetaRow = Record<string, unknown>;
|
|
25
25
|
|
|
26
|
-
/**
|
|
26
|
+
/** session_meta 行的读取列(列表 / 任务区 / 看板摘要共用一套映射)。 */
|
|
27
|
+
const META_SELECT_COLUMNS =
|
|
28
|
+
"session_id, path, cwd, title, first_message, parent_id, created, modified, pinned, last_reply";
|
|
29
|
+
|
|
30
|
+
/** 单个 session_meta 行 → SessionInfo(聊天列表与任务列表共用的唯一映射)。
|
|
31
|
+
* lastReply 直接取库(v12 列:事件链路写真实回复、扫描器一次性回填存量);
|
|
32
|
+
* NULL(未回填)与 ''(回填过但无回复)在 UI 上都表现为空串。 */
|
|
27
33
|
function mapSessionMetaRow(r: SessionMetaRow): SessionInfo {
|
|
28
34
|
return {
|
|
29
35
|
path: (r.path as string) ?? "",
|
|
@@ -34,21 +40,22 @@ function mapSessionMetaRow(r: SessionMetaRow): SessionInfo {
|
|
|
34
40
|
modified: new Date((r.modified as number) ?? 0).toISOString(),
|
|
35
41
|
messageCount: 0,
|
|
36
42
|
firstMessage: (r.first_message as string | null) ?? "(no messages)",
|
|
43
|
+
lastReply: (r.last_reply as string | null) ?? "",
|
|
37
44
|
parentSessionId: (r.parent_id as string | null) ?? undefined,
|
|
38
45
|
pinned: Boolean((r.pinned as number) ?? 0),
|
|
39
46
|
};
|
|
40
47
|
}
|
|
41
48
|
|
|
42
49
|
/** 按 id 集合查 session_meta 映射 SessionInfo(返回顺序与入参一致,无行 id 跳过)。
|
|
43
|
-
* 任务列表详情与聊天列表 loadProjectSessions
|
|
44
|
-
*
|
|
50
|
+
* 任务列表详情与聊天列表 loadProjectSessions 完全同源——标题/首条消息/最后回复
|
|
51
|
+
* 全部由 session_meta 供给,读取路径不读任何文件。 */
|
|
45
52
|
export async function loadSessionDetailsFromMeta(ids: string[]): Promise<SessionInfo[]> {
|
|
46
53
|
if (ids.length === 0) return [];
|
|
47
54
|
let rows: Array<Record<string, unknown>>;
|
|
48
55
|
try {
|
|
49
56
|
rows = getDb()
|
|
50
57
|
.prepare(
|
|
51
|
-
`SELECT
|
|
58
|
+
`SELECT ${META_SELECT_COLUMNS}
|
|
52
59
|
FROM session_meta WHERE session_id IN (${ids.map(() => "?").join(",")})`,
|
|
53
60
|
)
|
|
54
61
|
.all(...ids) as Array<Record<string, unknown>>;
|
|
@@ -61,42 +68,16 @@ export async function loadSessionDetailsFromMeta(ids: string[]): Promise<Session
|
|
|
61
68
|
const row = byId.get(id);
|
|
62
69
|
if (row) sessions.push(mapSessionMetaRow(row));
|
|
63
70
|
}
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* lazy 回填:列表读取时对 first_message 为空的会话读文件头补真实首条消息
|
|
69
|
-
* (文件是内容事实源),并批量写回 session_meta(后台收敛,下次免读文件)。
|
|
70
|
-
* 真·空会话(文件无消息)保持 NULL → 显示 no messages。
|
|
71
|
-
* 列表(聊天/任务/全量)共用同一逻辑,保证各处标题一致。
|
|
72
|
-
*/
|
|
73
|
-
function fillFirstMessageFromFile(sessions: SessionInfo[]): SessionInfo[] {
|
|
74
|
-
const pending = sessions.filter((s) => s.firstMessage === "(no messages)" && s.path);
|
|
75
|
-
if (pending.length === 0) return sessions;
|
|
76
|
-
const updateStmt = getDb().prepare("UPDATE session_meta SET first_message = ? WHERE session_id = ?");
|
|
77
|
-
return sessions.map((s) => {
|
|
78
|
-
if (s.firstMessage !== "(no messages)" || !s.path) return s;
|
|
79
|
-
let firstMessage = s.firstMessage;
|
|
80
|
-
try {
|
|
81
|
-
const head = scanOneSessionHead(s.path);
|
|
82
|
-
if (head?.firstMessage) {
|
|
83
|
-
firstMessage = head.firstMessage;
|
|
84
|
-
updateStmt.run(firstMessage, s.id);
|
|
85
|
-
}
|
|
86
|
-
} catch {
|
|
87
|
-
// 文件不可读(丢失/权限)→ 保持 no messages
|
|
88
|
-
}
|
|
89
|
-
return firstMessage === s.firstMessage ? s : { ...s, firstMessage };
|
|
90
|
-
});
|
|
71
|
+
return sessions;
|
|
91
72
|
}
|
|
92
73
|
|
|
93
74
|
/**
|
|
94
75
|
* 当前项目聊天区会话(列表重构 v2 主读取路径)。
|
|
95
76
|
*
|
|
96
77
|
* 纯查 session_meta:按 project_key 过滤 + 排除任务会话(task_id 非空交任务区管),
|
|
97
|
-
* 置顶优先 + modified
|
|
98
|
-
*
|
|
99
|
-
*
|
|
78
|
+
* 置顶优先 + modified 降序。**不读任何文件**(用户决策 4):title 用 meta.title
|
|
79
|
+
* (本应用改名写库),无自定义名回退 first_message,最后一条消息用 last_reply
|
|
80
|
+
* (事件链路写入 + 扫描器一次性回填)。运行时/未落盘会话由调用方 union getRpcSessionInfos。
|
|
100
81
|
*/
|
|
101
82
|
export async function loadProjectSessions(projectKey: string): Promise<SessionInfo[]> {
|
|
102
83
|
if (!projectKey) return [];
|
|
@@ -105,7 +86,7 @@ export async function loadProjectSessions(projectKey: string): Promise<SessionIn
|
|
|
105
86
|
try {
|
|
106
87
|
rows = getDb()
|
|
107
88
|
.prepare(
|
|
108
|
-
`SELECT
|
|
89
|
+
`SELECT ${META_SELECT_COLUMNS}
|
|
109
90
|
FROM session_meta
|
|
110
91
|
WHERE project_key = ? AND task_id IS NULL
|
|
111
92
|
ORDER BY pinned DESC, modified DESC`,
|
|
@@ -115,20 +96,18 @@ export async function loadProjectSessions(projectKey: string): Promise<SessionIn
|
|
|
115
96
|
return [];
|
|
116
97
|
}
|
|
117
98
|
|
|
118
|
-
|
|
119
|
-
return attachSessionProjectInfo(sessions);
|
|
99
|
+
return attachSessionProjectInfo(rows.map(mapSessionMetaRow));
|
|
120
100
|
}
|
|
121
101
|
|
|
122
102
|
/** 全项目会话索引读取(无参 /api/sessions:跨项目统计/项目下拉/hydrate/红点清理用)。
|
|
123
|
-
* 纯查 session_meta
|
|
124
|
-
* lastReply 消费方已改走摘要点查,侧栏 allSessions 不需要它。 */
|
|
103
|
+
* 纯查 session_meta 全表(含任务会话、含各项目),不整盘扫文件、也不读文件尾。 */
|
|
125
104
|
export async function loadAllSessionIndex(): Promise<SessionInfo[]> {
|
|
126
105
|
await ensureSessionIndexReady();
|
|
127
106
|
let rows: Array<Record<string, unknown>>;
|
|
128
107
|
try {
|
|
129
108
|
rows = getDb()
|
|
130
109
|
.prepare(
|
|
131
|
-
`SELECT
|
|
110
|
+
`SELECT ${META_SELECT_COLUMNS}
|
|
132
111
|
FROM session_meta
|
|
133
112
|
ORDER BY modified DESC`,
|
|
134
113
|
)
|
|
@@ -137,8 +116,7 @@ export async function loadAllSessionIndex(): Promise<SessionInfo[]> {
|
|
|
137
116
|
return [];
|
|
138
117
|
}
|
|
139
118
|
|
|
140
|
-
|
|
141
|
-
return attachSessionProjectInfo(sessions);
|
|
119
|
+
return attachSessionProjectInfo(rows.map(mapSessionMetaRow));
|
|
142
120
|
}
|
|
143
121
|
|
|
144
122
|
export async function attachSessionProjectInfo(sessions: SessionInfo[]): Promise<SessionInfo[]> {
|
|
@@ -185,93 +163,68 @@ export function mergeSessionLists(
|
|
|
185
163
|
return [...byId.values()].sort((a, b) => b.modified.localeCompare(a.modified));
|
|
186
164
|
}
|
|
187
165
|
|
|
188
|
-
async function loadAllSessions(): Promise<SessionInfo[]> {
|
|
189
|
-
// 轻量扫描:只读每个会话文件的头部(header + 首条用户消息)与尾部(最新
|
|
190
|
-
// session_info 自定义名),最后活动时间用文件 mtime——列表只展示标题+时间,
|
|
191
|
-
// 不读取消息正文(SDK 的 listAll 会全量读每个 jsonl 并拼接全部文本,在大会话
|
|
192
|
-
// 文件上会把列表刷新拖到秒级甚至十几秒)。
|
|
193
|
-
const scanned = await sessionScanner.scan();
|
|
194
|
-
const pathToId = new Map<string, string>();
|
|
195
|
-
for (const s of scanned) pathToId.set(sessionPathKey(s.path), s.id);
|
|
196
|
-
|
|
197
|
-
const sessions = scanned.map((s) => {
|
|
198
|
-
cacheSessionPath(s.id, s.path);
|
|
199
|
-
return {
|
|
200
|
-
path: s.path,
|
|
201
|
-
id: s.id,
|
|
202
|
-
cwd: s.cwd,
|
|
203
|
-
name: s.name,
|
|
204
|
-
created: s.created.toISOString(),
|
|
205
|
-
modified: s.modified.toISOString(),
|
|
206
|
-
// 列表不消费消息数(精确值需要全量读,已移除);保留字段以兼容类型。
|
|
207
|
-
messageCount: 0,
|
|
208
|
-
firstMessage: s.firstMessage || "(no messages)",
|
|
209
|
-
lastReply: s.lastReply || "",
|
|
210
|
-
parentSessionId: s.parentSessionPath ? pathToId.get(sessionPathKey(s.parentSessionPath)) : undefined,
|
|
211
|
-
transient: false,
|
|
212
|
-
};
|
|
213
|
-
});
|
|
214
|
-
return attachSessionProjectInfo(sessions);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
166
|
/** 任务会话详情按需分页(侧栏任务区)。
|
|
218
167
|
*
|
|
219
|
-
*
|
|
220
|
-
*
|
|
221
|
-
*
|
|
168
|
+
* 成员与父子链全部来自 session_meta(库是唯一事实源):任务成员 = `task_id = 该任务`
|
|
169
|
+
* 的全部节点;**根** = `parent_id` 为空或父不在本任务成员集合内的节点。这里不 readdir、
|
|
170
|
+
* 不读 header(旧 buildTaskSessionIndex 每次请求全盘 readdir+读首行)。
|
|
222
171
|
*
|
|
223
|
-
*
|
|
224
|
-
*
|
|
225
|
-
*
|
|
172
|
+
* 每任务返回:置顶节点全量 + 非置顶根从 offset 起的 limit 个(含各自 fork 子树),
|
|
173
|
+
* 外加 rootTotal(根数,前端「加载更多」游标)与 sessionTotal(任务下全部节点数,
|
|
174
|
+
* 删除确认文案用)。响应字段与分页语义与旧实现一致,前端契约不变。
|
|
226
175
|
*/
|
|
227
176
|
export async function loadTaskSessionsPage(
|
|
228
177
|
taskId: string,
|
|
229
178
|
offset = 0,
|
|
230
179
|
limit = 5,
|
|
231
180
|
): Promise<{ sessions: SessionInfo[]; rootTotal: number; sessionTotal: number; pinnedSessionIds: string[] }> {
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
181
|
+
const { listPinnedTaskSessionIds } = await import("./task-store");
|
|
182
|
+
// 冷启动闸门(与同族 loadProjectSessions / loadAllSessionIndex 一致):任务成员来自
|
|
183
|
+
// 库,而库要等首轮扫描才把磁盘会话建行。少了这一句,冷启动时任务区可能读到
|
|
184
|
+
// 「任务在、会话 0」且之后无人重拉(30s 后才自愈)。
|
|
185
|
+
// 闸门抛错(扫描器故障 / 库异常)不能穿透成路由 500:库里有上一轮扫描的行,
|
|
186
|
+
// 照旧查库返回比空页 / 500 都好。这里的闸门只是「尽力补一轮」。
|
|
187
|
+
try {
|
|
188
|
+
await ensureSessionIndexReady();
|
|
189
|
+
} catch (error) {
|
|
190
|
+
// 文案保持中性:这里只知道「索引就绪检查失败」,库好不好是另一回事——库可用时下面
|
|
191
|
+
// 照常返回库内行,库不可用时下面的查库 catch 降级为空页。旧文案「(继续查库)」把
|
|
192
|
+
// 后一种情形说反了,会让排查者以为库没问题。
|
|
193
|
+
console.error(
|
|
194
|
+
"[pi-web] 任务区索引就绪检查失败(接着查库;库不可用则降级为空页):",
|
|
195
|
+
error instanceof Error ? error.message : String(error),
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
// 同族读取函数(loadProjectSessions / loadAllSessionIndex)在 db 不可用时返回空,
|
|
199
|
+
// 这里同样降级为空页(路由层不必因一次读不到库整个 /api/tasks 500)。
|
|
200
|
+
// ORDER BY:成员顺序确定,让 modified 相同的根在 slice(offset, offset+limit)
|
|
201
|
+
// 下分页稳定(rowid 升序=建行顺序;下面的 sort 是稳定排序,同键保持此顺序)。
|
|
202
|
+
let rows: Array<{ session_id: string; parent_id: string | null; modified: number | null }>;
|
|
203
|
+
let pinnedSessionIds: string[];
|
|
204
|
+
try {
|
|
205
|
+
rows = getDb()
|
|
206
|
+
.prepare("SELECT session_id, parent_id, modified FROM session_meta WHERE task_id = ? ORDER BY modified DESC, rowid")
|
|
207
|
+
.all(taskId) as Array<{ session_id: string; parent_id: string | null; modified: number | null }>;
|
|
208
|
+
pinnedSessionIds = listPinnedTaskSessionIds(taskId);
|
|
209
|
+
} catch {
|
|
210
|
+
return { sessions: [], rootTotal: 0, sessionTotal: 0, pinnedSessionIds: [] };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const memberIds = new Set(rows.map((r) => r.session_id));
|
|
214
|
+
const modifiedOf = new Map<string, number>();
|
|
249
215
|
const childrenOf = new Map<string, string[]>();
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
216
|
+
const roots: string[] = [];
|
|
217
|
+
for (const row of rows) {
|
|
218
|
+
modifiedOf.set(row.session_id, row.modified ?? 0);
|
|
219
|
+
if (row.parent_id && memberIds.has(row.parent_id)) {
|
|
220
|
+
const siblings = childrenOf.get(row.parent_id) ?? [];
|
|
221
|
+
siblings.push(row.session_id);
|
|
222
|
+
childrenOf.set(row.parent_id, siblings);
|
|
223
|
+
} else {
|
|
224
|
+
roots.push(row.session_id);
|
|
256
225
|
}
|
|
257
|
-
if (!parentPath) continue;
|
|
258
|
-
const parentMeta = metaByPath.get(sessionPathKey(parentPath));
|
|
259
|
-
if (!parentMeta) continue;
|
|
260
|
-
const arr = childrenOf.get(parentMeta.id) ?? [];
|
|
261
|
-
arr.push(m.id);
|
|
262
|
-
childrenOf.set(parentMeta.id, arr);
|
|
263
226
|
}
|
|
264
|
-
return { metaById, childrenOf };
|
|
265
|
-
}
|
|
266
227
|
|
|
267
|
-
/** 加载单个任务会话详情分页(复用外部已构建的任务索引,避免重复全量扫)。 */
|
|
268
|
-
export async function loadTaskSessionsPageWithIndex(
|
|
269
|
-
taskId: string,
|
|
270
|
-
index: { metaById: Map<string, { path: string; id: string; modified: Date }>; childrenOf: Map<string, string[]> },
|
|
271
|
-
offset = 0,
|
|
272
|
-
limit = 5,
|
|
273
|
-
): Promise<{ sessions: SessionInfo[]; rootTotal: number; sessionTotal: number; pinnedSessionIds: string[] }> {
|
|
274
|
-
const { metaById, childrenOf } = index;
|
|
275
228
|
const collectSubtree = (rootId: string): string[] => {
|
|
276
229
|
const out = [rootId];
|
|
277
230
|
const queue = [rootId];
|
|
@@ -285,71 +238,24 @@ export async function loadTaskSessionsPageWithIndex(
|
|
|
285
238
|
return out;
|
|
286
239
|
};
|
|
287
240
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
const
|
|
241
|
+
// 排序键:modified(最后活动时间;session_meta.updated 只是归属/置顶时间,
|
|
242
|
+
// 用它切页会把活跃会话切到后页而前端够不着)。
|
|
243
|
+
const byModifiedDesc = (a: string, b: string) => (modifiedOf.get(b) ?? 0) - (modifiedOf.get(a) ?? 0);
|
|
291
244
|
const pinnedSet = new Set(pinnedSessionIds);
|
|
245
|
+
const nonPinnedRoots = roots.filter((id) => !pinnedSet.has(id)).sort(byModifiedDesc);
|
|
292
246
|
|
|
293
|
-
//
|
|
294
|
-
//
|
|
295
|
-
|
|
296
|
-
const modifiedOf = (id: string): number => metaById.get(id)?.modified.getTime() ?? 0;
|
|
297
|
-
const byMtimeDesc = (a: string, b: string) => modifiedOf(b) - modifiedOf(a);
|
|
298
|
-
const pinnedRoots = [...pinnedSessionIds].sort(byMtimeDesc);
|
|
299
|
-
const nonPinnedRoots = rootIds.filter((id) => !pinnedSet.has(id)).sort(byMtimeDesc);
|
|
300
|
-
|
|
301
|
-
// 当前页根 = 置顶全量 + 非置顶 slice(offset, offset+limit);子树跟随根。
|
|
302
|
-
const pageRootIds = [...pinnedRoots, ...nonPinnedRoots.slice(offset, offset + limit)];
|
|
247
|
+
// 当前页根 = 置顶节点全量(不受 offset 影响)+ 非置顶根 slice(offset, offset+limit);子树跟随根。
|
|
248
|
+
// 排序只作用于页序,返回的 pinnedSessionIds 保持库内原序(与旧实现一致)。
|
|
249
|
+
const pageRootIds = [...[...pinnedSessionIds].sort(byModifiedDesc), ...nonPinnedRoots.slice(offset, offset + limit)];
|
|
303
250
|
const wantedIds = new Set<string>();
|
|
304
251
|
for (const rid of pageRootIds) {
|
|
305
252
|
for (const id of collectSubtree(rid)) wantedIds.add(id);
|
|
306
253
|
}
|
|
307
|
-
// 详情走 session_meta(与聊天列表 loadProjectSessions
|
|
308
|
-
//
|
|
254
|
+
// 详情走 session_meta(与聊天列表 loadProjectSessions 同源)——标题/最后回复由库供给,
|
|
255
|
+
// 不读文件;库内没有行的 id 自然跳过。
|
|
309
256
|
const sessions = await attachSessionProjectInfo(await loadSessionDetailsFromMeta([...wantedIds]));
|
|
310
257
|
|
|
311
|
-
|
|
312
|
-
const allIds = new Set<string>();
|
|
313
|
-
for (const rid of rootIds) for (const id of collectSubtree(rid)) allIds.add(id);
|
|
314
|
-
return { sessions, rootTotal: rootIds.length, sessionTotal: allIds.size, pinnedSessionIds };
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
export async function listAllSessions(options: { force?: boolean } = {}): Promise<SessionInfo[]> {
|
|
318
|
-
if (options.force) invalidateSessionListCache();
|
|
319
|
-
const generation = globalThis.__piSessionListGeneration ?? 0;
|
|
320
|
-
|
|
321
|
-
// Return cached result if still fresh (avoids re-scanning session files
|
|
322
|
-
// and re-spawning git processes on every page load).
|
|
323
|
-
if (globalThis.__piSessionListCache && Date.now() - globalThis.__piSessionListCache.ts < SESSION_LIST_CACHE_TTL_MS) {
|
|
324
|
-
return globalThis.__piSessionListCache.data;
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// Coalescing dedup: concurrent callers share the same in-flight promise
|
|
328
|
-
// only while it belongs to the current cache generation.
|
|
329
|
-
if (globalThis.__piSessionListPromise && globalThis.__piSessionListPromiseGeneration === generation) {
|
|
330
|
-
return globalThis.__piSessionListPromise;
|
|
331
|
-
}
|
|
332
|
-
|
|
333
|
-
const loadPromise = loadAllSessions().then((data) => {
|
|
334
|
-
// If a mutation invalidated this scan, make this caller join (or start) a
|
|
335
|
-
// scan for the current generation. Returning the stale result here made a
|
|
336
|
-
// refresh race indistinguishable from a successful refresh.
|
|
337
|
-
if ((globalThis.__piSessionListGeneration ?? 0) !== generation) {
|
|
338
|
-
return listAllSessions();
|
|
339
|
-
}
|
|
340
|
-
globalThis.__piSessionListCache = { data, ts: Date.now() };
|
|
341
|
-
return data;
|
|
342
|
-
});
|
|
343
|
-
const trackedPromise = loadPromise.finally(() => {
|
|
344
|
-
if (globalThis.__piSessionListPromise === trackedPromise) {
|
|
345
|
-
globalThis.__piSessionListPromise = undefined;
|
|
346
|
-
globalThis.__piSessionListPromiseGeneration = undefined;
|
|
347
|
-
}
|
|
348
|
-
});
|
|
349
|
-
|
|
350
|
-
globalThis.__piSessionListPromise = trackedPromise;
|
|
351
|
-
globalThis.__piSessionListPromiseGeneration = generation;
|
|
352
|
-
return trackedPromise;
|
|
258
|
+
return { sessions, rootTotal: roots.length, sessionTotal: rows.length, pinnedSessionIds };
|
|
353
259
|
}
|
|
354
260
|
|
|
355
261
|
// ============================================================================
|
|
@@ -358,17 +264,17 @@ export async function listAllSessions(options: { force?: boolean } = {}): Promis
|
|
|
358
264
|
declare global {
|
|
359
265
|
var __piSessionPathCache: Map<string, string> | undefined;
|
|
360
266
|
var __piPathToSessionIdCache: Map<string, string> | undefined;
|
|
361
|
-
var __piSessionListPromise: Promise<SessionInfo[]> | undefined;
|
|
362
|
-
var __piSessionListPromiseGeneration: number | undefined;
|
|
363
267
|
var __piSessionListGeneration: number | undefined;
|
|
364
|
-
var __piSessionListCache: { data: SessionInfo[]; ts: number } | undefined;
|
|
365
268
|
}
|
|
366
269
|
|
|
367
|
-
|
|
368
|
-
|
|
270
|
+
/** 写路径的「数据变了」信号:归属/pin/改名/新建/删除/事件都会调它。
|
|
271
|
+
*
|
|
272
|
+
* 列表已无缓存(读取 = 每次一次单表查询),所以目前**没有生产读者**:这里只保留
|
|
273
|
+
* generation 自增的既有行为(T1–T4b 的写路径与 auto-name / patch-write-failure 的
|
|
274
|
+
* 断言都依赖调用本身)。若要连函数一起删,需要同时改掉全部调用方与那两处断言——
|
|
275
|
+
* 属待清理项,本次不动。 */
|
|
369
276
|
export function invalidateSessionListCache(): void {
|
|
370
277
|
globalThis.__piSessionListGeneration = (globalThis.__piSessionListGeneration ?? 0) + 1;
|
|
371
|
-
globalThis.__piSessionListCache = undefined;
|
|
372
278
|
}
|
|
373
279
|
|
|
374
280
|
function getPathCache(): Map<string, string> {
|
|
@@ -382,13 +288,20 @@ function getPathToIdCache(): Map<string, string> {
|
|
|
382
288
|
}
|
|
383
289
|
|
|
384
290
|
/**
|
|
385
|
-
* Find one session's file
|
|
291
|
+
* Find one session's file by name, as a bounded fallback for sessions that are
|
|
292
|
+
* not in session_meta yet (a session an external tool just created).
|
|
386
293
|
*
|
|
387
294
|
* Session files are written as `<timestamp>_<id>.jsonl` under a per-project
|
|
388
295
|
* directory, so the id can be located by reading directory entries alone. The
|
|
389
296
|
* header is then parsed — bounded, first line only — to confirm the match
|
|
390
|
-
* rather than trusting the name. Returns null when nothing matches
|
|
391
|
-
*
|
|
297
|
+
* rather than trusting the name. Returns null when nothing matches; callers
|
|
298
|
+
* must not fall back to a full catalogue scan (that read path is gone).
|
|
299
|
+
*
|
|
300
|
+
* 决策 4 的代价(说得明白,免后人误以为是 bug):这里只平铺 readdir 每个项目目录的
|
|
301
|
+
* **第一层**,并依赖 `_<id>.jsonl` 命名约定。子目录里的会话(`<session>/forks/…`、
|
|
302
|
+
* `<session>/<runId>/run-0/session.jsonl`)与文件名不合约定的行因此解析不到——它们要等
|
|
303
|
+
* 扫描器把 path 写进 session_meta(≤30s)才能被 resolveSessionPath 命中。换来的是读
|
|
304
|
+
* 取路径不发全量扫盘。
|
|
392
305
|
*
|
|
393
306
|
* `sessionId` is only ever compared against names that came back from
|
|
394
307
|
* `readdir`, never joined into a path itself, so a separator or `..` inside it
|
|
@@ -424,7 +337,7 @@ async function findSessionPathByName(sessionId: string): Promise<string | null>
|
|
|
424
337
|
try {
|
|
425
338
|
if (readSessionHeader(candidate)?.id === sessionId) return candidate;
|
|
426
339
|
} catch {
|
|
427
|
-
//
|
|
340
|
+
// 读不出 header(不可读/截断)→ 不认这个候选;不回落全量扫盘(读取路径已无此退路)。
|
|
428
341
|
}
|
|
429
342
|
}
|
|
430
343
|
return null;
|
|
@@ -434,18 +347,28 @@ export async function resolveSessionPath(sessionId: string): Promise<string | nu
|
|
|
434
347
|
const cached = getPathCache().get(sessionId);
|
|
435
348
|
if (cached) return cached;
|
|
436
349
|
|
|
437
|
-
//
|
|
438
|
-
//
|
|
439
|
-
|
|
350
|
+
// 库是路径的事实源(扫描器已把每个磁盘会话的 path 写入 session_meta):
|
|
351
|
+
// 命中即用,不读目录、不扫盘。
|
|
352
|
+
try {
|
|
353
|
+
const row = getDb()
|
|
354
|
+
.prepare("SELECT path FROM session_meta WHERE session_id = ?")
|
|
355
|
+
.get(sessionId) as { path: string | null } | undefined;
|
|
356
|
+
if (row?.path && existsSync(row.path)) {
|
|
357
|
+
cacheSessionPath(sessionId, row.path);
|
|
358
|
+
return row.path;
|
|
359
|
+
}
|
|
360
|
+
} catch {
|
|
361
|
+
// db 不可用 → 走名字兜底
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
// 库内无行/路径已失效(外部工具刚建的会话)→ 按名字匹配兜底:每项目目录一次
|
|
365
|
+
// readdir + 一次 header 读,不再退化成全量头尾扫。
|
|
440
366
|
const direct = await findSessionPathByName(sessionId);
|
|
441
367
|
if (direct) {
|
|
442
368
|
cacheSessionPath(sessionId, direct);
|
|
443
369
|
return direct;
|
|
444
370
|
}
|
|
445
|
-
|
|
446
|
-
// Cache miss: scan all sessions to populate cache, then retry
|
|
447
|
-
await listAllSessions();
|
|
448
|
-
return getPathCache().get(sessionId) ?? null;
|
|
371
|
+
return null;
|
|
449
372
|
}
|
|
450
373
|
|
|
451
374
|
export async function resolveSessionIdByPath(filePath: string): Promise<string | undefined> {
|
|
@@ -453,8 +376,34 @@ export async function resolveSessionIdByPath(filePath: string): Promise<string |
|
|
|
453
376
|
const cached = getPathToIdCache().get(pathKey);
|
|
454
377
|
if (cached) return cached;
|
|
455
378
|
|
|
456
|
-
|
|
457
|
-
|
|
379
|
+
try {
|
|
380
|
+
const db = getDb();
|
|
381
|
+
// 精确命中(绝大多数调用:header.parentSession 与建行时写入的 path 同源)。
|
|
382
|
+
const exact = db
|
|
383
|
+
.prepare("SELECT session_id, path FROM session_meta WHERE path = ?")
|
|
384
|
+
.get(filePath) as { session_id: string; path: string } | undefined;
|
|
385
|
+
if (exact?.path) {
|
|
386
|
+
cacheSessionPath(exact.session_id, exact.path);
|
|
387
|
+
return exact.session_id;
|
|
388
|
+
}
|
|
389
|
+
// 归一化差异兜底(`/a/./b`、Windows 大小写):按文件名后缀粗筛,再用
|
|
390
|
+
// sessionPathKey 精确比较。`_` 在 LIKE 里是单字符通配符 → 只会多筛不会漏筛。
|
|
391
|
+
const base = basenamePath(filePath);
|
|
392
|
+
if (base) {
|
|
393
|
+
const candidates = db
|
|
394
|
+
.prepare("SELECT session_id, path FROM session_meta WHERE path LIKE ?")
|
|
395
|
+
.all(`%${base}`) as Array<{ session_id: string; path: string | null }>;
|
|
396
|
+
for (const row of candidates) {
|
|
397
|
+
if (row.path && sessionPathKey(row.path) === pathKey) {
|
|
398
|
+
cacheSessionPath(row.session_id, row.path);
|
|
399
|
+
return row.session_id;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
} catch {
|
|
404
|
+
// db 不可用 → 查不到(读取路径不再扫盘兜底)
|
|
405
|
+
}
|
|
406
|
+
return undefined;
|
|
458
407
|
}
|
|
459
408
|
|
|
460
409
|
export function cacheSessionPath(sessionId: string, filePath: string): void {
|
|
@@ -848,49 +797,64 @@ export function extractTurnIndex(entries: SessionEntry[], leafId: string | null)
|
|
|
848
797
|
/**
|
|
849
798
|
* 按 id 批量点查会话摘要(看板卡片轮询用,替代全量列表自筛)。
|
|
850
799
|
*
|
|
851
|
-
*
|
|
852
|
-
*
|
|
853
|
-
*
|
|
854
|
-
*
|
|
800
|
+
* 库优先(用户决策 4):title / last_reply / modified 全部取 session_meta,
|
|
801
|
+
* 一次点查不读任何文件(旧实现逐卡 scanOneSessionFile 读头尾)。只有
|
|
802
|
+
* **库里完全没有行**的 id(外部工具刚建、尚未入索引的会话)才回退到
|
|
803
|
+
* resolveSessionPath + 单文件详情读。查不到(id 不存在)跳过。
|
|
855
804
|
*/
|
|
856
805
|
export async function loadSessionSummariesByIds(ids: string[]): Promise<SessionInfo[]> {
|
|
857
806
|
const unique = [...new Set(ids.filter(Boolean))];
|
|
858
807
|
if (unique.length === 0) return [];
|
|
859
808
|
|
|
860
|
-
|
|
861
|
-
// title 优先用 meta(与列表同源:改名写 session_meta.title),文件尾 session_info 兜底——
|
|
862
|
-
// 避免改名后继续聊超 tail 上限(16MB)时看板卡片标题与侧栏不一致。
|
|
863
|
-
const rows = new Map<string, { path: string | null; title: string | null }>();
|
|
809
|
+
const rowById = new Map<string, SessionMetaRow>();
|
|
864
810
|
try {
|
|
865
811
|
const found = getDb()
|
|
866
|
-
.prepare(
|
|
867
|
-
.all(...unique) as Array<
|
|
868
|
-
for (const r of found)
|
|
812
|
+
.prepare(`SELECT ${META_SELECT_COLUMNS} FROM session_meta WHERE session_id IN (${unique.map(() => "?").join(",")})`)
|
|
813
|
+
.all(...unique) as Array<Record<string, unknown>>;
|
|
814
|
+
for (const r of found) rowById.set(r.session_id as string, r);
|
|
869
815
|
} catch {
|
|
870
|
-
// db 不可用 →
|
|
816
|
+
// db 不可用 → 全部走文件兜底
|
|
871
817
|
}
|
|
872
818
|
|
|
873
|
-
const
|
|
819
|
+
const byId = new Map<string, SessionInfo>();
|
|
820
|
+
const missing: string[] = [];
|
|
874
821
|
for (const id of unique) {
|
|
875
|
-
const
|
|
876
|
-
|
|
877
|
-
|
|
822
|
+
const row = rowById.get(id);
|
|
823
|
+
if (!row) {
|
|
824
|
+
missing.push(id);
|
|
825
|
+
continue;
|
|
826
|
+
}
|
|
827
|
+
if (row.path) cacheSessionPath(id, row.path as string);
|
|
828
|
+
byId.set(id, mapSessionMetaRow(row));
|
|
829
|
+
}
|
|
830
|
+
|
|
831
|
+
// 兜底:库内无行的 id(外部工具刚建的会话)—— 一次路径解析 + 文件头尾读。
|
|
832
|
+
for (const id of missing) {
|
|
833
|
+
const filePath = await resolveSessionPath(id);
|
|
878
834
|
if (!filePath) continue;
|
|
879
835
|
const scanned = scanOneSessionFile(filePath);
|
|
880
836
|
if (!scanned) continue;
|
|
881
837
|
cacheSessionPath(id, scanned.path);
|
|
882
|
-
|
|
838
|
+
// parentSessionId 与主路径同字段(会话 id,不是路径):磁盘上只有父路径,
|
|
839
|
+
// 经库反查一次;父也还没行就留 undefined(与主路径的 NULL 行为一致)。
|
|
840
|
+
const parentSessionId = scanned.parentSessionPath
|
|
841
|
+
? await resolveSessionIdByPath(scanned.parentSessionPath)
|
|
842
|
+
: undefined;
|
|
843
|
+
byId.set(id, {
|
|
883
844
|
path: scanned.path,
|
|
884
845
|
id: scanned.id,
|
|
885
846
|
cwd: scanned.cwd,
|
|
886
|
-
name:
|
|
847
|
+
name: scanned.name,
|
|
887
848
|
created: scanned.created.toISOString(),
|
|
888
849
|
modified: scanned.modified.toISOString(),
|
|
889
850
|
messageCount: 0,
|
|
890
851
|
firstMessage: scanned.firstMessage || "(no messages)",
|
|
891
852
|
lastReply: scanned.lastReply || "",
|
|
853
|
+
parentSessionId,
|
|
892
854
|
transient: false,
|
|
893
855
|
});
|
|
894
856
|
}
|
|
895
|
-
|
|
857
|
+
|
|
858
|
+
// 返回顺序与入参一致(命中集)。
|
|
859
|
+
return attachSessionProjectInfo(unique.map((id) => byId.get(id)).filter((s): s is SessionInfo => Boolean(s)));
|
|
896
860
|
}
|