@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/task-store.test.mjs
CHANGED
|
@@ -1,8 +1,18 @@
|
|
|
1
1
|
import assert from "node:assert/strict";
|
|
2
|
-
import
|
|
2
|
+
import { mkdirSync, mkdtempSync, rmSync } from "node:fs";
|
|
3
|
+
import { tmpdir } from "node:os";
|
|
4
|
+
import { join } from "node:path";
|
|
5
|
+
import test, { after } from "node:test";
|
|
3
6
|
import { DatabaseSync } from "node:sqlite";
|
|
4
7
|
import { createJiti } from "jiti";
|
|
5
8
|
|
|
9
|
+
// getAgentDir() 每次调用都读环境变量 → 指向临时树,测试绝不碰 ~/.pi/agent。
|
|
10
|
+
// sessions 子目录先建好:无行会话补行时会按 id 在这里找文件,miss 不回落扫开发机真实目录。
|
|
11
|
+
const agentDir = mkdtempSync(join(tmpdir(), "pi-web-task-store-"));
|
|
12
|
+
mkdirSync(join(agentDir, "sessions"), { recursive: true });
|
|
13
|
+
process.env.PI_CODING_AGENT_DIR = agentDir;
|
|
14
|
+
after(() => rmSync(agentDir, { recursive: true, force: true }));
|
|
15
|
+
|
|
6
16
|
const jiti = createJiti(import.meta.url, { interopDefault: true, moduleCache: false });
|
|
7
17
|
const { getDb, setDbForTesting } = await jiti.import("./sqlite-db.ts");
|
|
8
18
|
const { createTask, deleteTask, listTasks, updateTask, taskForSession, assignSessionToTask, reorderTasks, setSessionTitle } = await jiti.import("./task-store.ts");
|
|
@@ -112,26 +122,26 @@ test("updateTask returns null for unknown id and rejects empty rename", () => {
|
|
|
112
122
|
assert.throws(() => updateTask(a.id, { name: " " }), /name must not be empty/);
|
|
113
123
|
});
|
|
114
124
|
|
|
115
|
-
test("assignSessionToTask attaches a session atomically and bumps the task's updated", () => {
|
|
125
|
+
test("assignSessionToTask attaches a session atomically and bumps the task's updated", async () => {
|
|
116
126
|
freshDb();
|
|
117
127
|
const a = createTask("p", "A");
|
|
118
128
|
seedSession("s1", null); // temp
|
|
119
129
|
seedSession("s2", a.id); // already a member
|
|
120
130
|
|
|
121
|
-
assert.equal(assignSessionToTask("s1", a.id), true);
|
|
131
|
+
assert.equal(await assignSessionToTask("s1", a.id), true);
|
|
122
132
|
assert.equal(taskForSession("s1"), a.id);
|
|
123
133
|
// 重复归属幂等:已是成员再归一次仍成功且不重复。
|
|
124
|
-
assert.equal(assignSessionToTask("s2", a.id), true);
|
|
134
|
+
assert.equal(await assignSessionToTask("s2", a.id), true);
|
|
125
135
|
|
|
126
136
|
const task = listTasks("p")[0];
|
|
127
137
|
assert.deepEqual([...task.sessionIds].sort(), ["s1", "s2"]);
|
|
128
138
|
assert.ok(task.updated >= task.created, "归属会刷新任务的 updated(排序用)");
|
|
129
139
|
});
|
|
130
140
|
|
|
131
|
-
test("assignSessionToTask returns false for a missing task without writing anything", () => {
|
|
141
|
+
test("assignSessionToTask returns false for a missing task without writing anything", async () => {
|
|
132
142
|
freshDb();
|
|
133
143
|
seedSession("s1", null);
|
|
134
|
-
assert.equal(assignSessionToTask("s1", "no-such-task"), false);
|
|
144
|
+
assert.equal(await assignSessionToTask("s1", "no-such-task"), false);
|
|
135
145
|
assert.equal(taskForSession("s1"), null);
|
|
136
146
|
assert.equal(listTasks("p").length, 0);
|
|
137
147
|
});
|
|
@@ -177,18 +187,38 @@ test("updateTask supports sortOrder patch", () => {
|
|
|
177
187
|
assert.equal(b.sortOrder < 0, true);
|
|
178
188
|
});
|
|
179
189
|
|
|
180
|
-
test("assignSessionToTask moves a session out of its previous task", () => {
|
|
190
|
+
test("assignSessionToTask moves a session out of its previous task", async () => {
|
|
181
191
|
freshDb();
|
|
182
192
|
const a = createTask("p", "A");
|
|
183
193
|
const b = createTask("p", "B");
|
|
184
194
|
seedSession("s1", null);
|
|
185
|
-
assignSessionToTask("s1", a.id);
|
|
186
|
-
assignSessionToTask("s1", b.id); // 移动到 B
|
|
195
|
+
await assignSessionToTask("s1", a.id);
|
|
196
|
+
await assignSessionToTask("s1", b.id); // 移动到 B
|
|
187
197
|
assert.equal(taskForSession("s1"), b.id);
|
|
188
198
|
const taskA = listTasks("p").find((t) => t.id === a.id);
|
|
189
199
|
assert.deepEqual(taskA.sessionIds, []);
|
|
190
200
|
});
|
|
191
201
|
|
|
202
|
+
test("updateTask 不再为无行会话插缺列局部行(加侧只 UPDATE)", () => {
|
|
203
|
+
freshDb();
|
|
204
|
+
const a = createTask("p", "A");
|
|
205
|
+
updateTask(a.id, { sessionIds: ["ghost-no-row"] });
|
|
206
|
+
// 行由路由层 updateTask 之前调 ensureSessionRows 建(那里才能 await 文件 I/O)。
|
|
207
|
+
assert.equal(
|
|
208
|
+
getDb().prepare("SELECT COUNT(*) c FROM session_meta WHERE session_id='ghost-no-row'").get().c,
|
|
209
|
+
0,
|
|
210
|
+
);
|
|
211
|
+
assert.deepEqual(listTasks("p")[0].sessionIds, []);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
test("updateTask 对已有行的成员照常写归属(加侧 UPDATE 不丢写)", () => {
|
|
215
|
+
freshDb();
|
|
216
|
+
const a = createTask("p", "A");
|
|
217
|
+
seedSession("s1", null);
|
|
218
|
+
updateTask(a.id, { sessionIds: ["s1"] });
|
|
219
|
+
assert.equal(taskForSession("s1"), a.id);
|
|
220
|
+
});
|
|
221
|
+
|
|
192
222
|
test("deleteTask cascades to its task board (nodes/edges/view gone)", () => {
|
|
193
223
|
freshDb();
|
|
194
224
|
const a = createTask("p", "A");
|
|
@@ -218,21 +248,17 @@ test("updateTask rename syncs task board name", () => {
|
|
|
218
248
|
updateTask(b.id, { name: "B2" });
|
|
219
249
|
});
|
|
220
250
|
|
|
221
|
-
test("setSessionTitle
|
|
251
|
+
test("setSessionTitle:已有行只改名(不覆盖归属/索引列)", async () => {
|
|
222
252
|
freshDb();
|
|
223
253
|
// 场景1:已有归属行(任务会话),改名写 title 保留 task_id
|
|
224
254
|
seedSession("s-task", "task-1");
|
|
225
|
-
setSessionTitle("s-task", "新名字");
|
|
255
|
+
await setSessionTitle("s-task", "新名字");
|
|
226
256
|
let row = getDb().prepare("SELECT title, task_id FROM session_meta WHERE session_id='s-task'").get();
|
|
227
257
|
assert.equal(row.title, "新名字");
|
|
228
258
|
assert.equal(row.task_id, "task-1", "不覆盖已有归属");
|
|
229
|
-
// 场景2
|
|
230
|
-
setSessionTitle("s-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
assert.equal(
|
|
234
|
-
assert.equal(row.pinned, 0);
|
|
235
|
-
// 场景3:再次改名覆盖
|
|
236
|
-
setSessionTitle("s-orphan", "再改");
|
|
237
|
-
assert.equal(getDb().prepare("SELECT title FROM session_meta WHERE session_id='s-orphan'").get().title, "再改");
|
|
259
|
+
// 场景2:再次改名覆盖
|
|
260
|
+
await setSessionTitle("s-task", "再改");
|
|
261
|
+
assert.equal(getDb().prepare("SELECT title FROM session_meta WHERE session_id='s-task'").get().title, "再改");
|
|
262
|
+
// 场景3(无行 + 磁盘无文件 → 不写缺列局部行)在 task-store.partial-row.test.mjs 覆盖。
|
|
263
|
+
assert.equal(getDb().prepare("SELECT COUNT(*) c FROM session_meta").get().c, 1);
|
|
238
264
|
});
|
package/lib/task-store.ts
CHANGED
|
@@ -1,6 +1,9 @@
|
|
|
1
1
|
import { randomUUID } from "crypto";
|
|
2
2
|
import { getDb } from "./sqlite-db";
|
|
3
3
|
import { deleteBoardCascade, renameTaskBoard } from "./board-store";
|
|
4
|
+
import { projectIdentityKey } from "./project-identity";
|
|
5
|
+
import { scanOneSessionHead } from "./session-scanner";
|
|
6
|
+
import { resolveProject } from "./worktree";
|
|
4
7
|
|
|
5
8
|
export interface Task {
|
|
6
9
|
id: string;
|
|
@@ -60,6 +63,60 @@ export function listAllTaskSessionIds(): Set<string> {
|
|
|
60
63
|
return new Set(rows.map((r) => r.session_id));
|
|
61
64
|
}
|
|
62
65
|
|
|
66
|
+
/** 一次全表读出的父子链(parentOf 上溯、childrenOf 下探)。
|
|
67
|
+
* updateTask 的多个根共用同一份快照——否则每个入参 id 都调一次 listDescendantIds,
|
|
68
|
+
* 每次一条全表 SELECT(O(成员数 × 全表))。 */
|
|
69
|
+
function readParentLinks(): { parentOf: Map<string, string | null>; childrenOf: Map<string, string[]> } {
|
|
70
|
+
const parentOf = new Map<string, string | null>();
|
|
71
|
+
const childrenOf = new Map<string, string[]>();
|
|
72
|
+
for (const r of getDb().prepare("SELECT session_id, parent_id FROM session_meta").all() as Array<{ session_id: string; parent_id: string | null }>) {
|
|
73
|
+
parentOf.set(r.session_id, r.parent_id);
|
|
74
|
+
if (r.parent_id !== null) {
|
|
75
|
+
const arr = childrenOf.get(r.parent_id) ?? [];
|
|
76
|
+
arr.push(r.session_id);
|
|
77
|
+
childrenOf.set(r.parent_id, arr);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return { parentOf, childrenOf };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** 按 childrenOf 递归收集后代 id(不含自身)。广度优先、visited 去重防环。 */
|
|
84
|
+
function collectDescendants(childrenOf: Map<string, string[]>, sessionId: string): string[] {
|
|
85
|
+
const out: string[] = [];
|
|
86
|
+
const seen = new Set<string>([sessionId]);
|
|
87
|
+
const queue = [...(childrenOf.get(sessionId) ?? [])];
|
|
88
|
+
while (queue.length) {
|
|
89
|
+
const cur = queue.shift()!;
|
|
90
|
+
if (seen.has(cur)) continue;
|
|
91
|
+
seen.add(cur);
|
|
92
|
+
out.push(cur);
|
|
93
|
+
queue.push(...(childrenOf.get(cur) ?? []));
|
|
94
|
+
}
|
|
95
|
+
return out;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** 按 parent_id 递归收集后代 id(不含自身)。 */
|
|
99
|
+
export function listDescendantIds(sessionId: string): string[] {
|
|
100
|
+
return collectDescendants(readParentLinks().childrenOf, sessionId);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
/** 成员集合归一化:若某成员在库里还有祖先、而该祖先不在集合里,则它也不能留
|
|
104
|
+
* (归属按整棵子树存,父子不跨任务)。循环到不动点,兼容多层子树。
|
|
105
|
+
* 祖先行已不存在(外部删除留下的悬空 parent_id)不算「祖先在库内」——此时它
|
|
106
|
+
* 就是本任务的根,剪掉会把合法成员误踢出任务。就地修改传入的集合。 */
|
|
107
|
+
function normalizeMemberSet(members: Set<string>, parentOf: Map<string, string | null>): void {
|
|
108
|
+
let pruned = true;
|
|
109
|
+
while (pruned) {
|
|
110
|
+
pruned = false;
|
|
111
|
+
for (const sessionId of members) {
|
|
112
|
+
const parent = parentOf.get(sessionId);
|
|
113
|
+
if (!parent || members.has(parent) || !parentOf.has(parent)) continue;
|
|
114
|
+
members.delete(sessionId);
|
|
115
|
+
pruned = true;
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
63
120
|
function getTaskRow(id: string): TaskRow | undefined {
|
|
64
121
|
return getDb()
|
|
65
122
|
.prepare(
|
|
@@ -120,10 +177,20 @@ export function createTask(projectKey: string, name: string): Task {
|
|
|
120
177
|
}
|
|
121
178
|
|
|
122
179
|
/**
|
|
123
|
-
* Rename and/or replace task membership. `sessionIds` is a full-replace list
|
|
124
|
-
*
|
|
125
|
-
*
|
|
126
|
-
*
|
|
180
|
+
* Rename and/or replace task membership. `sessionIds` is a full-replace list of
|
|
181
|
+
* subtree roots: each id and all of its descendants (session_meta.parent_id) are
|
|
182
|
+
* added; members whose ancestor falls outside that closure fall back to temp
|
|
183
|
+
* (NULL). Membership is stored per subtree, so a PATCH whose roots close over
|
|
184
|
+
* another task's nodes moves them here (子树压过单节点归属):两个任务的闭包重叠时,
|
|
185
|
+
* 以最后一个 PATCH 的闭包为准,而不是「永不碰其它任务」。
|
|
186
|
+
*
|
|
187
|
+
* 不变量:子会话不能单独移出任务——祖先仍是本任务成员时,子会话就保持在任务内
|
|
188
|
+
* (提交 [根] 时闭包会连带子孙;宁可不对「单独移出子会话」作出响应,也不让聊天区
|
|
189
|
+
* 出现父不在、子单飞的孤儿根)。
|
|
190
|
+
*
|
|
191
|
+
* 「移出任务」由这里的成员规范化承担:提交的剩余成员列表里,祖先已不在集合的成员
|
|
192
|
+
* 一律被剪掉(整棵子树一起走),不需要单独的 unassign 子树入口。
|
|
193
|
+
* Returns null when the task does not exist.
|
|
127
194
|
*/
|
|
128
195
|
export function updateTask(
|
|
129
196
|
id: string,
|
|
@@ -157,22 +224,26 @@ export function updateTask(
|
|
|
157
224
|
);
|
|
158
225
|
}
|
|
159
226
|
if (patch.sessionIds !== undefined) {
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
const
|
|
163
|
-
const
|
|
164
|
-
"INSERT INTO session_meta (session_id, task_id, updated) VALUES (?, ?, ?) " +
|
|
165
|
-
"ON CONFLICT(session_id) DO UPDATE SET task_id = excluded.task_id, updated = excluded.updated",
|
|
166
|
-
);
|
|
167
|
-
const unassign = db.prepare(
|
|
168
|
-
"UPDATE session_meta SET task_id = NULL, updated = ? WHERE session_id = ?",
|
|
169
|
-
);
|
|
227
|
+
// 入参是「根 id 列表」的闭包视图:加/减都以整棵子树为单位,避免子会话留在任务里当孤儿根。
|
|
228
|
+
// 父子链只读一次(多个根的展开 + 归一化共用同一份快照)。
|
|
229
|
+
const { parentOf, childrenOf } = readParentLinks();
|
|
230
|
+
const next = new Set<string>();
|
|
170
231
|
for (const sessionId of patch.sessionIds) {
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
for (const sessionId of current) {
|
|
174
|
-
if (!next.has(sessionId)) unassign.run(ts, sessionId);
|
|
232
|
+
next.add(sessionId);
|
|
233
|
+
for (const d of collectDescendants(childrenOf, sessionId)) next.add(d);
|
|
175
234
|
}
|
|
235
|
+
// 归一化:入参里「祖先已被剔除」的成员不能留。前端拖出父会话时提交的是
|
|
236
|
+
// flat 剩余成员列表(仍含子会话 id),不归一化就会父走子留、子会话在任务区当根。
|
|
237
|
+
normalizeMemberSet(next, parentOf);
|
|
238
|
+
const current = new Set(listTaskSessionIds(id));
|
|
239
|
+
const ts = now();
|
|
240
|
+
// 加侧只 UPDATE:行由创建链路(ensureSessionMetaRow)/扫描器/路由层
|
|
241
|
+
// (ensureSessionRows)建全列行。此处不能 INSERT——同步事务里 await 不了文件 I/O,
|
|
242
|
+
// 而插缺列局部行(缺 path/project_key/created)会让会话从聊天区消失、任务区显示 1970。
|
|
243
|
+
const assign = db.prepare("UPDATE session_meta SET task_id = ?, updated = ? WHERE session_id = ?");
|
|
244
|
+
const unassign = db.prepare("UPDATE session_meta SET task_id = NULL, updated = ? WHERE session_id = ?");
|
|
245
|
+
for (const sessionId of next) if (!current.has(sessionId)) assign.run(id, ts, sessionId);
|
|
246
|
+
for (const sessionId of current) if (!next.has(sessionId)) unassign.run(ts, sessionId);
|
|
176
247
|
db.prepare("UPDATE tasks SET updated = ? WHERE id = ?").run(ts, id);
|
|
177
248
|
}
|
|
178
249
|
db.exec("COMMIT");
|
|
@@ -256,15 +327,13 @@ export function taskNameForSession(sessionId: string): string | null {
|
|
|
256
327
|
return row?.name ?? null;
|
|
257
328
|
}
|
|
258
329
|
|
|
259
|
-
/**
|
|
260
|
-
* 把一个会话原子归属到任务(任务不存在返回 false)。会刷新任务的 updated
|
|
261
|
-
* 使置顶/最近排序生效;会话原本在其他任务下则移动(ON CONFLICT 更新)。
|
|
262
|
-
* 会话创建完成时由服务端调用,避免前端两跳 PATCH 造成的“先临时区后任务”窗口。
|
|
263
|
-
*/
|
|
264
330
|
/** 新建会话落盘即建全列索引行(先于会话真正运行)。
|
|
265
331
|
* 调用方拿到的 path/cwd/project_key/parent 等此时全部已知——会话从出生就是
|
|
266
332
|
* 完整索引行,读取(列表/任务区/看板)不再依赖扫描器补列,也不退化扫盘。
|
|
267
|
-
* ON CONFLICT 只补索引列、不碰 task_id/pinned/title
|
|
333
|
+
* ON CONFLICT 只补索引列、不碰 task_id/pinned/title(归属/置顶/改名各自管);
|
|
334
|
+
* `first_message` 只补空(COALESCE)——调用方不传就保留库里已有的首条消息。
|
|
335
|
+
* `taskId` 只在 INSERT 时写入(归属继承用):已存在的行归属各自管,upsert 不覆盖。
|
|
336
|
+
* INSERT 列清单显式列全 13 列(`last_reply` 显式 NULL)——不依赖建表默认值。 */
|
|
268
337
|
export function ensureSessionMetaRow(sessionId: string, row: {
|
|
269
338
|
path: string;
|
|
270
339
|
cwd: string;
|
|
@@ -272,46 +341,154 @@ export function ensureSessionMetaRow(sessionId: string, row: {
|
|
|
272
341
|
parentId?: string;
|
|
273
342
|
firstMessage?: string;
|
|
274
343
|
title?: string;
|
|
344
|
+
taskId?: string | null;
|
|
275
345
|
}): void {
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
346
|
+
getDb()
|
|
347
|
+
.prepare(
|
|
348
|
+
`INSERT INTO session_meta
|
|
349
|
+
(session_id, task_id, updated, pinned, path, cwd, project_key, title, first_message, parent_id, created, modified, last_reply)
|
|
350
|
+
VALUES (?, ?, ?, 0, ?, ?, ?, ?, ?, ?, ?, ?, NULL)
|
|
351
|
+
ON CONFLICT(session_id) DO UPDATE SET
|
|
352
|
+
path = excluded.path,
|
|
353
|
+
cwd = excluded.cwd,
|
|
354
|
+
project_key = excluded.project_key,
|
|
355
|
+
-- first_message 只补空:调用方不传(undefined)时不再把已有的首条消息擦成 NULL。
|
|
356
|
+
first_message = COALESCE(session_meta.first_message, excluded.first_message),
|
|
357
|
+
parent_id = excluded.parent_id,
|
|
358
|
+
created = excluded.created,
|
|
359
|
+
modified = excluded.modified`,
|
|
360
|
+
)
|
|
361
|
+
.run(
|
|
362
|
+
sessionId,
|
|
363
|
+
row.taskId ?? null,
|
|
364
|
+
now(),
|
|
365
|
+
row.path,
|
|
366
|
+
row.cwd,
|
|
367
|
+
row.projectKey,
|
|
368
|
+
row.title ?? null,
|
|
369
|
+
row.firstMessage ?? null,
|
|
370
|
+
row.parentId ?? null,
|
|
371
|
+
now(),
|
|
372
|
+
now(),
|
|
373
|
+
);
|
|
301
374
|
invalidateSessionListCache();
|
|
302
375
|
}
|
|
303
376
|
|
|
304
|
-
|
|
377
|
+
/**
|
|
378
|
+
* 给「会话已经在磁盘上、库里却还没行」的场景补一条**全列**行:定位文件 → 读 header → upsert。
|
|
379
|
+
* 先查行(有行直接返回,完全不碰文件);无行才按 id 解析文件。
|
|
380
|
+
*
|
|
381
|
+
* 为什么不能退回插「局部行」:只写 session_id/updated/title(pinned) 的行缺 path/project_key/created,
|
|
382
|
+
* 会被聊天区的 `project_key = ?` 过滤掉(会话消失到下一轮扫描)、任务区显示 1970-01-01(V4)。
|
|
383
|
+
* 同时补 `parent_id`(磁盘 header.parentSession 反查):归属守卫(hasForeignTaskAncestor)
|
|
384
|
+
* 只看库内 parent_id——补行时不写父,「库里还没行」的会话在守卫眼里就是无祖先的顶层行
|
|
385
|
+
* (带上跨任务祖先拖入别的任务会被错误放行)。反查不到父(父行未建/父文件已消失)则留空。
|
|
386
|
+
* 文件也找不到时返回 false 且报错可见——调用方保持原语义(不抛错),但绝不写半行。
|
|
387
|
+
*/
|
|
388
|
+
async function ensureRowForSession(sessionId: string): Promise<boolean> {
|
|
389
|
+
if (getDb().prepare("SELECT 1 FROM session_meta WHERE session_id = ?").get(sessionId)) return true;
|
|
390
|
+
// 动态导入避开 session-reader ↔ task-store 的循环加载(session-reader 反向动态引本模块)。
|
|
391
|
+
const { resolveSessionPath, resolveSessionIdByPath } = await import("./session-reader");
|
|
392
|
+
const filePath = await resolveSessionPath(sessionId);
|
|
393
|
+
if (!filePath) {
|
|
394
|
+
console.error(`[pi-web] 会话行补建失败:找不到会话文件 session=${sessionId}`);
|
|
395
|
+
return false;
|
|
396
|
+
}
|
|
397
|
+
const head = scanOneSessionHead(filePath);
|
|
398
|
+
if (!head) {
|
|
399
|
+
console.error(`[pi-web] 会话行补建失败:会话文件无有效 header session=${sessionId} path=${filePath}`);
|
|
400
|
+
return false;
|
|
401
|
+
}
|
|
402
|
+
const project = await resolveProject(head.cwd);
|
|
403
|
+
const parentId = head.parentSessionPath
|
|
404
|
+
? ((await resolveSessionIdByPath(head.parentSessionPath)) ?? undefined)
|
|
405
|
+
: undefined;
|
|
406
|
+
ensureSessionMetaRow(sessionId, {
|
|
407
|
+
path: filePath,
|
|
408
|
+
cwd: head.cwd,
|
|
409
|
+
projectKey: projectIdentityKey(project?.projectRoot ?? head.cwd),
|
|
410
|
+
parentId,
|
|
411
|
+
firstMessage: head.firstMessage || undefined,
|
|
412
|
+
});
|
|
413
|
+
return true;
|
|
414
|
+
}
|
|
415
|
+
|
|
416
|
+
/** 批量为「库内无行」的会话补全列行(PATCH 全量替换前由路由层调用)。
|
|
417
|
+
* 为什么在事务外:建行要读会话文件(`resolveSessionPath` miss 时会目录扫描),
|
|
418
|
+
* 在 `updateTask` 的事务里 await 文件 I/O 会长时间持写锁。
|
|
419
|
+
* 单个 id 失败不抛错也不阻塞其余 id(失败已由 ensureRowForSession 报错可见)。 */
|
|
420
|
+
export async function ensureSessionRows(sessionIds: string[]): Promise<void> {
|
|
421
|
+
for (const sessionId of sessionIds) {
|
|
422
|
+
try {
|
|
423
|
+
await ensureRowForSession(sessionId);
|
|
424
|
+
} catch (error) {
|
|
425
|
+
console.error(
|
|
426
|
+
`[pi-web] 会话行补建异常 session=${sessionId}:`,
|
|
427
|
+
error instanceof Error ? error.message : String(error),
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
/** 归属写入前的祖先守卫:该会话沿 session_meta.parent_id 上溯的任一祖先,是否带着
|
|
434
|
+
* **非空且不属于 taskId** 的归属。
|
|
435
|
+
*
|
|
436
|
+
* 为什么归属入口必须查:「归属按整棵子树存」是写库侧的不变量,但把「父在任务 A 的
|
|
437
|
+
* 会话」归到任务 B 会造出跨任务父子——而 updateTask 的成员规范化与扫描器的归属收敛
|
|
438
|
+
* 会反过来把它剪掉/拉回,两个方向互相纠正、永不收敛(看板画布落点、聊天区拖拽都是
|
|
439
|
+
* 可达入口)。上溯时用 visited 防脏环;祖先行不在库里(悬空 parent_id)不算冲突,
|
|
440
|
+
* 它就是本任务的根。
|
|
441
|
+
* 导出供路由在 assign* 拒绝后给出准确的状态码/原因(409 vs 404)。 */
|
|
442
|
+
export function hasForeignTaskAncestor(sessionId: string, taskId: string): boolean {
|
|
443
|
+
// 一次全表读同时拿 parent 链与归属(本函数在每次归属写入前跑,别读两遍库)。
|
|
444
|
+
const parentOf = new Map<string, string | null>();
|
|
445
|
+
const taskOf = new Map<string, string | null>();
|
|
446
|
+
for (const r of getDb().prepare("SELECT session_id, parent_id, task_id FROM session_meta").all() as Array<{ session_id: string; parent_id: string | null; task_id: string | null }>) {
|
|
447
|
+
parentOf.set(r.session_id, r.parent_id);
|
|
448
|
+
taskOf.set(r.session_id, r.task_id);
|
|
449
|
+
}
|
|
450
|
+
const seen = new Set<string>([sessionId]);
|
|
451
|
+
let current = parentOf.get(sessionId) ?? null;
|
|
452
|
+
while (current && !seen.has(current)) {
|
|
453
|
+
seen.add(current);
|
|
454
|
+
const ancestorTask = taskOf.get(current) ?? null;
|
|
455
|
+
if (ancestorTask !== null && ancestorTask !== taskId) return true;
|
|
456
|
+
current = parentOf.get(current) ?? null;
|
|
457
|
+
}
|
|
458
|
+
return false;
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
/**
|
|
462
|
+
* 把一个会话原子归属到任务(任务不存在返回 false)。会刷新任务的 updated
|
|
463
|
+
* 使置顶/最近排序生效;会话原本在其他任务下则移动。
|
|
464
|
+
* 会话创建完成时由服务端调用,避免前端两跳 PATCH 造成的“先临时区后任务”窗口。
|
|
465
|
+
*
|
|
466
|
+
* 无行会话先在**同一调用内**补全列行再归属(见 ensureRowForSession);补不出行返回 false,
|
|
467
|
+
* 保证「返回 true」永远等价于「归属已落库」。
|
|
468
|
+
* 祖先已属于其它任务时拒绝(返回 false + console.error,不写任何行):见 hasForeignTaskAncestor。
|
|
469
|
+
* 补行**先于**守卫:库里原本没行的会话,其父链只能靠补行(读磁盘 header)写进 parent_id,
|
|
470
|
+
* 顺序反了守卫就是盲的(无行会话会被当成无祖先的顶层行放行)。
|
|
471
|
+
*/
|
|
472
|
+
export async function assignSessionToTask(sessionId: string, taskId: string): Promise<boolean> {
|
|
305
473
|
const db = getDb();
|
|
306
474
|
const task = getTaskRow(taskId);
|
|
307
475
|
if (!task) return false;
|
|
476
|
+
// 归属不能靠「先插行为空、等扫描补列」:那正是 V4 的缺列局部行,也是 V1 的病灶。
|
|
477
|
+
if (!(await ensureRowForSession(sessionId))) return false;
|
|
478
|
+
if (hasForeignTaskAncestor(sessionId, taskId)) {
|
|
479
|
+
console.error(
|
|
480
|
+
`[pi-web] 归属被拒绝:祖先属于其它任务 session=${sessionId} task=${taskId}`,
|
|
481
|
+
);
|
|
482
|
+
return false;
|
|
483
|
+
}
|
|
308
484
|
const ts = now();
|
|
309
485
|
db.exec("BEGIN");
|
|
310
486
|
try {
|
|
311
|
-
db.prepare(
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
487
|
+
db.prepare("UPDATE session_meta SET task_id = ?, updated = ? WHERE session_id = ?").run(
|
|
488
|
+
taskId,
|
|
489
|
+
ts,
|
|
490
|
+
sessionId,
|
|
491
|
+
);
|
|
315
492
|
db.prepare("UPDATE tasks SET updated = ? WHERE id = ?").run(ts, taskId);
|
|
316
493
|
db.exec("COMMIT");
|
|
317
494
|
} catch (error) {
|
|
@@ -322,24 +499,79 @@ export function assignSessionToTask(sessionId: string, taskId: string): boolean
|
|
|
322
499
|
return true;
|
|
323
500
|
}
|
|
324
501
|
|
|
325
|
-
/**
|
|
326
|
-
|
|
502
|
+
/** 归属一条会话及其整棵子树(写库先于任何 UI 刷新;一次事务)。
|
|
503
|
+
* 子孙一律跟随父:否则「父在任务、子 task_id 为空」会让子会话同时出现在
|
|
504
|
+
* 任务区(按树渲染)与聊天区(按 task_id 过滤),即 V1 的两区重复。
|
|
505
|
+
* 根会话无行时同样先补全列行(UPDATE 对无行会话影响 0 行 = 静默写 0 行)。
|
|
506
|
+
* 根的祖先已属于其它任务时拒绝(返回 false + console.error,不写任何行):否则会造出
|
|
507
|
+
* 跨任务父子,被规范化/收敛反向拉回。补行先于守卫(与 assignSessionToTask 同规则):
|
|
508
|
+
* 无行会话的父链只能靠补行写进 parent_id,守卫才有得看。 */
|
|
509
|
+
export async function assignSessionSubtreeToTask(sessionId: string, taskId: string): Promise<boolean> {
|
|
510
|
+
if (!getTaskRow(taskId)) return false;
|
|
511
|
+
if (!(await ensureRowForSession(sessionId))) return false;
|
|
512
|
+
if (hasForeignTaskAncestor(sessionId, taskId)) {
|
|
513
|
+
console.error(
|
|
514
|
+
`[pi-web] 子树归属被拒绝:祖先属于其它任务 session=${sessionId} task=${taskId}`,
|
|
515
|
+
);
|
|
516
|
+
return false;
|
|
517
|
+
}
|
|
518
|
+
const ts = now();
|
|
327
519
|
const db = getDb();
|
|
328
|
-
db.
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
520
|
+
db.exec("BEGIN");
|
|
521
|
+
try {
|
|
522
|
+
const stmt = db.prepare("UPDATE session_meta SET task_id = ?, updated = ? WHERE session_id = ?");
|
|
523
|
+
for (const id of [sessionId, ...listDescendantIds(sessionId)]) stmt.run(taskId, ts, id);
|
|
524
|
+
db.prepare("UPDATE tasks SET updated = ? WHERE id = ?").run(ts, taskId);
|
|
525
|
+
db.exec("COMMIT");
|
|
526
|
+
} catch (error) {
|
|
527
|
+
db.exec("ROLLBACK");
|
|
528
|
+
throw error;
|
|
529
|
+
}
|
|
332
530
|
invalidateSessionListCache();
|
|
531
|
+
return true;
|
|
333
532
|
}
|
|
334
533
|
|
|
335
|
-
/**
|
|
336
|
-
*
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
534
|
+
/** 库内直接子行在磁盘上的文件路径(只用非空 path)。
|
|
535
|
+
* 给「删父会话时同步改写子行磁盘 header」用:子会话文件可能在子目录里
|
|
536
|
+
* (`<proj>/<session>/forks/*.jsonl` 等),只平铺一层 `readdirSync(dirname(父文件))`
|
|
537
|
+
* 会漏掉它们,漏斗就会「库已写祖父、磁盘仍指已删父」——下一轮扫描按磁盘把它改回 NULL。
|
|
538
|
+
* 库里有父链与 path,路径就是事实(不新写目录递归)。 */
|
|
539
|
+
export function listChildPaths(sessionId: string): string[] {
|
|
540
|
+
const rows = getDb()
|
|
541
|
+
.prepare("SELECT path FROM session_meta WHERE parent_id = ? AND path IS NOT NULL AND path != ''")
|
|
542
|
+
.all(sessionId) as Array<{ path: string }>;
|
|
543
|
+
return rows.map((row) => row.path);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/** 会话被删除时,把库内直接子行的 parent_id 改写为祖父(拿不到写入 NULL)。
|
|
547
|
+
* 为什么必须同请求写库:T5 之后「根」判定全来自 session_meta.parent_id——只管磁盘
|
|
548
|
+
* header 的话,库里子行仍指向已删 id,子会话会以根行呈现(≤30s 的窗口期)。 */
|
|
549
|
+
export function reparentSessionChildren(sessionId: string, newParentId: string | null): void {
|
|
550
|
+
getDb()
|
|
551
|
+
.prepare("UPDATE session_meta SET parent_id = ? WHERE parent_id = ?")
|
|
552
|
+
.run(newParentId, sessionId);
|
|
553
|
+
invalidateSessionListCache();
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
/** Pin / unpin a session (region-relative: inside its task group, or in chat).
|
|
557
|
+
* 无行老会话先补全列行(缺列行会被聊天区 project_key 过滤掉),补不出行则不写。 */
|
|
558
|
+
export async function setSessionPinned(sessionId: string, pinned: boolean): Promise<void> {
|
|
559
|
+
if (!(await ensureRowForSession(sessionId))) return;
|
|
560
|
+
// 只写 pinned,不刷 updated——与原先 ON CONFLICT SET pinned 的语义一致
|
|
561
|
+
// (updated 是扫描器「年轻行保护」的依据,置顶不该给幽灵行续命)。
|
|
562
|
+
getDb()
|
|
563
|
+
.prepare("UPDATE session_meta SET pinned = ? WHERE session_id = ?")
|
|
564
|
+
.run(pinned ? 1 : 0, sessionId);
|
|
565
|
+
invalidateSessionListCache();
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
/** 改名同步写 session_meta.title(列表索引的标题源)。无行老会话先补全列行
|
|
569
|
+
* (path/project_key/created 齐全),补不出行则只报错不写半行。 */
|
|
570
|
+
export async function setSessionTitle(sessionId: string, title: string): Promise<void> {
|
|
571
|
+
if (!(await ensureRowForSession(sessionId))) return;
|
|
572
|
+
getDb()
|
|
573
|
+
.prepare("UPDATE session_meta SET title = ?, updated = ? WHERE session_id = ?")
|
|
574
|
+
.run(title, now(), sessionId);
|
|
343
575
|
invalidateSessionListCache();
|
|
344
576
|
}
|
|
345
577
|
|
|
@@ -347,4 +579,48 @@ export function setSessionTitle(sessionId: string, title: string): void {
|
|
|
347
579
|
export function unassignSession(sessionId: string): void {
|
|
348
580
|
getDb().prepare("DELETE FROM session_meta WHERE session_id = ?").run(sessionId);
|
|
349
581
|
invalidateSessionListCache();
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
// ── 事件链路写入(agent_start / agent_settled)───────────────────────────────
|
|
585
|
+
// 三个函数都**只 UPDATE、不 INSERT**:会话行由创建链路(ensureSessionMetaRow)
|
|
586
|
+
// 或扫描器建立,事件不负责补行——否则会留下缺列局部行(path/project_key 为空的
|
|
587
|
+
// 1970 行),读取期就得再回退扫盘。库里没行时静默 no-op(UPDATE 影响 0 行)。
|
|
588
|
+
|
|
589
|
+
/** last_reply 入库上限(与 types.ts 的「截断」语义一致,防大消息撑爆行)。 */
|
|
590
|
+
export const LAST_REPLY_MAX = 4000;
|
|
591
|
+
|
|
592
|
+
/** 会话活跃时间前移(agent_start:运行中会话立刻浮顶)。 */
|
|
593
|
+
export function touchSessionActivity(sessionId: string, at: number = now()): void {
|
|
594
|
+
getDb().prepare("UPDATE session_meta SET modified = ? WHERE session_id = ?").run(at, sessionId);
|
|
595
|
+
invalidateSessionListCache();
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
/**
|
|
599
|
+
* 一轮循环真正结束(agent_settled,含用户取消)时落最后一条 assistant 文本 + 活跃时间。
|
|
600
|
+
* 空文本(本轮只有工具调用 / 刚发出就被取消)**不清库**:退化为只刷 modified,
|
|
601
|
+
* 保留库里已有的 last_reply——空字符串不是「一条消息」。
|
|
602
|
+
*/
|
|
603
|
+
export function recordSessionOutcome(sessionId: string, outcome: { lastReply: string; at: number }): void {
|
|
604
|
+
const text = outcome.lastReply.trim();
|
|
605
|
+
if (!text) {
|
|
606
|
+
touchSessionActivity(sessionId, outcome.at);
|
|
607
|
+
return;
|
|
608
|
+
}
|
|
609
|
+
const stored = text.length > LAST_REPLY_MAX ? text.slice(0, LAST_REPLY_MAX) : text;
|
|
610
|
+
getDb()
|
|
611
|
+
.prepare("UPDATE session_meta SET last_reply = ?, modified = ? WHERE session_id = ?")
|
|
612
|
+
.run(stored, outcome.at, sessionId);
|
|
613
|
+
invalidateSessionListCache();
|
|
614
|
+
}
|
|
615
|
+
|
|
616
|
+
/** 首条用户消息回填(读取路径不再读文件,改由事件补齐;只补空行)。 */
|
|
617
|
+
export function fillFirstMessageIfEmpty(sessionId: string, firstMessage: string): void {
|
|
618
|
+
const text = firstMessage.trim();
|
|
619
|
+
if (!text) return;
|
|
620
|
+
getDb()
|
|
621
|
+
.prepare(
|
|
622
|
+
"UPDATE session_meta SET first_message = ? WHERE session_id = ? AND (first_message IS NULL OR first_message = '')",
|
|
623
|
+
)
|
|
624
|
+
.run(text, sessionId);
|
|
625
|
+
invalidateSessionListCache();
|
|
350
626
|
}
|
package/lib/tool-presets.ts
CHANGED
|
@@ -2,6 +2,8 @@ export interface ToolEntry {
|
|
|
2
2
|
name: string;
|
|
3
3
|
description: string;
|
|
4
4
|
active: boolean;
|
|
5
|
+
parameters?: Record<string, unknown>;
|
|
6
|
+
promptGuidelines?: string[];
|
|
5
7
|
}
|
|
6
8
|
|
|
7
9
|
export const TOOL_PRESET_VALUES = ["none", "read-only", "default", "full"] as const;
|
|
@@ -11,6 +13,8 @@ export const PRESET_NONE: string[] = [];
|
|
|
11
13
|
export const PRESET_READ_ONLY: string[] = ["read", "grep", "find", "ls"];
|
|
12
14
|
export const PRESET_DEFAULT: string[] = ["read", "bash", "edit", "write"];
|
|
13
15
|
export const PRESET_FULL: string[] = ["bash", "read", "edit", "write", "grep", "find", "ls"];
|
|
16
|
+
/** 子代理默认工具集(subagents.ts 与 AgentsConfig 共用,客户端安全模块)。 */
|
|
17
|
+
export const DEFAULT_TOOLS: string[] = ["read", "bash", "edit", "write", "grep", "find", "ls"];
|
|
14
18
|
|
|
15
19
|
const BUILTIN_TOOL_NAMES = new Set(PRESET_FULL);
|
|
16
20
|
|
package/lib/types.ts
CHANGED
|
@@ -271,6 +271,15 @@ export interface SessionInfoEntry extends SessionEntryBase {
|
|
|
271
271
|
name?: string;
|
|
272
272
|
}
|
|
273
273
|
|
|
274
|
+
export type SubagentSessionStatus =
|
|
275
|
+
| "starting"
|
|
276
|
+
| "queued"
|
|
277
|
+
| "running"
|
|
278
|
+
| "completed"
|
|
279
|
+
| "failed"
|
|
280
|
+
| "aborted"
|
|
281
|
+
| "interrupted";
|
|
282
|
+
|
|
274
283
|
export type SessionEntry =
|
|
275
284
|
| SessionMessageEntry
|
|
276
285
|
| ThinkingLevelChangeEntry
|
|
@@ -309,6 +318,16 @@ export interface SessionInfo {
|
|
|
309
318
|
/** 最后一条 assistant 回复文本(看板卡片中间区展示用,截断),无则为空串 */
|
|
310
319
|
lastReply?: string;
|
|
311
320
|
parentSessionId?: string; // set if this session was forked from another
|
|
321
|
+
/** 会话间关系:fork 在 UI 保持顶层;仅 subagent 关系形成可见的父子树。 */
|
|
322
|
+
relation?:
|
|
323
|
+
| { kind: "fork"; originSessionId?: string }
|
|
324
|
+
| {
|
|
325
|
+
kind: "subagent";
|
|
326
|
+
parentSessionId: string;
|
|
327
|
+
profile: string;
|
|
328
|
+
description: string;
|
|
329
|
+
status: SubagentSessionStatus;
|
|
330
|
+
};
|
|
312
331
|
/** Main repo root shared by all worktrees of this cwd (cwd itself for non-git dirs).
|
|
313
332
|
* Always set by the server; optional because the client builds transient
|
|
314
333
|
* SessionInfo objects before the first refresh. Fall back to cwd. */
|