@rexvane/piweb 0.3.25 → 0.3.26
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 +6 -5
- package/.next/build-manifest.json +4 -4
- package/.next/next-server.js.nft.json +1 -1
- package/.next/prerender-manifest.json +4 -4
- package/.next/react-loadable-manifest.json +14 -26
- package/.next/required-server-files.js +26 -26
- package/.next/required-server-files.json +26 -26
- package/.next/routes-manifest.json +6 -0
- package/.next/server/app/_global-error/page.js +2 -2
- package/.next/server/app/_global-error/page.js.nft.json +1 -1
- 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.js.nft.json +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 +2 -2
- package/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +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]/events/route_client-reference-manifest.js +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/[id]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/[id]/tree/route.js +1 -1
- package/.next/server/app/api/agent/[id]/tree/route.js.nft.json +1 -1
- package/.next/server/app/api/agent/[id]/tree/route_client-reference-manifest.js +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/new/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/agent/resources/route.js +1 -0
- package/.next/server/app/api/agent/resources/route.js.nft.json +1 -0
- package/.next/server/app/api/agent/resources/route_client-reference-manifest.js +1 -0
- package/.next/server/app/api/config/route.js +1 -1
- package/.next/server/app/api/config/route.js.nft.json +1 -1
- package/.next/server/app/api/config/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/files/route.js +1 -1
- package/.next/server/app/api/files/route.js.nft.json +1 -1
- package/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/fs/route.js +1 -1
- package/.next/server/app/api/fs/route.js.nft.json +1 -1
- package/.next/server/app/api/fs/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/git/route.js +4 -4
- package/.next/server/app/api/git/route.js.nft.json +1 -1
- package/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/github/route.js +4 -4
- package/.next/server/app/api/github/route.js.nft.json +1 -1
- package/.next/server/app/api/github/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/growth/route.js +1 -1
- package/.next/server/app/api/growth/route.js.nft.json +1 -1
- package/.next/server/app/api/growth/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/health/route.js +1 -1
- package/.next/server/app/api/health/route.js.nft.json +1 -1
- package/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models/defaults/route.js +1 -1
- package/.next/server/app/api/models/defaults/route.js.nft.json +1 -1
- package/.next/server/app/api/models/defaults/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/models/route.js +1 -1
- package/.next/server/app/api/models/route.js.nft.json +1 -1
- package/.next/server/app/api/models/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/pi-settings/route.js +1 -1
- package/.next/server/app/api/pi-settings/route.js.nft.json +1 -1
- package/.next/server/app/api/pi-settings/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/plugins/route.js +1 -1
- package/.next/server/app/api/plugins/route.js.nft.json +1 -1
- package/.next/server/app/api/plugins/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/prompts/route.js +1 -1
- package/.next/server/app/api/prompts/route.js.nft.json +1 -1
- package/.next/server/app/api/prompts/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/remote/route.js +2 -2
- package/.next/server/app/api/remote/route.js.nft.json +1 -1
- package/.next/server/app/api/remote/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/security/route.js +1 -1
- package/.next/server/app/api/security/route.js.nft.json +1 -1
- package/.next/server/app/api/security/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/[id]/export/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/export/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/export/route_client-reference-manifest.js +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]/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/sessions/import/route.js +2 -2
- package/.next/server/app/api/sessions/import/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/import/route_client-reference-manifest.js +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/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/skills/route.js +2 -2
- package/.next/server/app/api/skills/route.js.nft.json +1 -1
- package/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/update/route.js +2 -2
- package/.next/server/app/api/update/route.js.nft.json +1 -1
- package/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/version/route.js +1 -1
- package/.next/server/app/api/version/route.js.nft.json +1 -1
- package/.next/server/app/api/version/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/web-auth/route.js +2 -2
- package/.next/server/app/api/web-auth/route.js.nft.json +1 -1
- package/.next/server/app/api/web-auth/route_client-reference-manifest.js +1 -1
- package/.next/server/app/api/workspaces/route.js +1 -1
- package/.next/server/app/api/workspaces/route.js.nft.json +1 -1
- package/.next/server/app/api/workspaces/route_client-reference-manifest.js +1 -1
- package/.next/server/app/icon.svg/route.js +1 -1
- package/.next/server/app/icon.svg/route.js.nft.json +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +5 -5
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +5 -5
- package/.next/server/app/index.segments/_full.segment.rsc +5 -5
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login/page.js +1 -1
- package/.next/server/app/login/page.js.nft.json +1 -1
- package/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/.next/server/app/login.html +1 -1
- package/.next/server/app/login.rsc +4 -4
- package/.next/server/app/login.segments/_full.segment.rsc +4 -4
- package/.next/server/app/login.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/login.segments/login/__PAGE__.segment.rsc +4 -4
- package/.next/server/app/page.js +1 -1
- package/.next/server/app/page.js.nft.json +1 -1
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +6 -5
- package/.next/server/chunks/{813.js → 1813.js} +1 -1
- package/.next/server/chunks/{430.js → 2430.js} +5 -5
- package/.next/server/chunks/{970.js → 2970.js} +1 -1
- package/.next/server/chunks/3924.js +6 -0
- package/.next/server/chunks/4807.js +1 -0
- package/.next/server/chunks/5393.js +1 -0
- package/.next/server/chunks/5802.js +2 -0
- package/.next/server/chunks/620.js +1 -1
- package/.next/server/chunks/6229.js +1 -0
- package/.next/server/chunks/7496.js +1 -0
- package/.next/server/chunks/7662.js +2 -0
- package/.next/server/chunks/8686.js +1 -0
- package/.next/server/chunks/{593.js → 974.js} +1 -1
- package/.next/server/chunks/{920.js → 9920.js} +1 -1
- package/.next/server/instrumentation.js +1 -1
- package/.next/server/instrumentation.js.nft.json +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 +5 -5
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +1 -1
- package/.next/server/preview-props.json +3 -3
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/server/webpack-runtime.js +1 -1
- package/.next/static/QJCtqgDKzGS2BdKABd9ch/_buildManifest.js +1 -0
- package/.next/static/chunks/2577.86c8538fbf05a8b2.js +1 -0
- package/.next/static/chunks/2716.fa561519675a3ff1.js +1 -0
- package/.next/static/chunks/2906.8a0e09bb47605cf1.js +1 -0
- package/.next/static/chunks/4673.ea36e55005c0f82f.js +1 -0
- package/.next/static/chunks/4686-4dc565817a160351.js +1 -0
- package/.next/static/chunks/4778.0d2c5d75b791d352.js +1 -0
- package/.next/static/chunks/app/_global-error/page-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/_not-found/page-2d40d1d7d916666a.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/events/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/agent/[id]/tree/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/agent/new/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/agent/resources/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/config/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/files/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/fs/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/git/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/github/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/growth/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/health/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/models/defaults/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/models/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/pi-settings/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/plugins/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/prompts/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/remote/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/security/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/export/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/sessions/[id]/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/sessions/import/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/sessions/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/skills/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/update/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/version/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/web-auth/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/api/workspaces/route-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/app/{layout-7f64fa67ea6ce980.js → layout-50d06e2078d6ce76.js} +1 -1
- package/.next/static/chunks/app/login/{page-00dbd2f395b364d5.js → page-5e276a1eeb8867df.js} +1 -1
- package/.next/static/chunks/app/page-71f2d3b3ed864b3d.js +1 -0
- package/.next/static/chunks/main-5403f0764579e5fe.js +5 -0
- package/.next/static/chunks/{main-app-56863ace24eb152f.js → main-app-e9b53f0e1c6591b2.js} +1 -1
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/global-error-a7e260f8cc2582d7.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-f2d93969c0bcf406.js +1 -0
- package/.next/static/chunks/webpack-ffc09b5fcc8aaa6d.js +1 -0
- package/.next/static/css/7e3caf797e4cb19a.css +1 -0
- package/.next/types/app/api/agent/[id]/events/route.ts +1 -1
- package/.next/types/app/api/agent/[id]/route.ts +1 -1
- package/.next/types/app/api/agent/[id]/tree/route.ts +1 -1
- package/.next/types/app/api/agent/new/route.ts +1 -1
- package/.next/types/app/api/agent/resources/route.ts +355 -0
- package/.next/types/app/api/config/route.ts +1 -1
- package/.next/types/app/api/files/route.ts +1 -1
- package/.next/types/app/api/fs/route.ts +1 -1
- package/.next/types/app/api/git/route.ts +1 -1
- package/.next/types/app/api/github/route.ts +1 -1
- package/.next/types/app/api/growth/route.ts +1 -1
- package/.next/types/app/api/health/route.ts +1 -1
- package/.next/types/app/api/models/defaults/route.ts +1 -1
- package/.next/types/app/api/models/route.ts +1 -1
- package/.next/types/app/api/pi-settings/route.ts +1 -1
- package/.next/types/app/api/plugins/route.ts +1 -1
- package/.next/types/app/api/prompts/route.ts +1 -1
- package/.next/types/app/api/remote/route.ts +1 -1
- package/.next/types/app/api/security/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/export/route.ts +1 -1
- package/.next/types/app/api/sessions/[id]/route.ts +1 -1
- package/.next/types/app/api/sessions/import/route.ts +1 -1
- package/.next/types/app/api/sessions/route.ts +1 -1
- package/.next/types/app/api/skills/route.ts +1 -1
- package/.next/types/app/api/update/route.ts +1 -1
- package/.next/types/app/api/version/route.ts +1 -1
- package/.next/types/app/api/web-auth/route.ts +1 -1
- package/.next/types/app/api/workspaces/route.ts +1 -1
- package/.next/types/app/layout.ts +1 -1
- package/.next/types/app/login/page.ts +1 -1
- package/.next/types/app/page.ts +1 -1
- package/.next/types/routes.d.ts +2 -1
- package/.next/types/validator.ts +10 -0
- package/LICENSE +1 -1
- package/README.md +1 -1
- package/bin/pi.js +0 -0
- package/bin/piweb.js +0 -0
- package/package.json +1 -1
- package/scripts/build-output.mjs +0 -0
- package/scripts/dev.mjs +0 -0
- package/scripts/entrypoint.mjs +0 -0
- package/scripts/install-build.mjs +0 -0
- package/scripts/launcher.mjs +0 -0
- package/scripts/pick-folder.ps1 +0 -0
- package/scripts/postinstall.mjs +0 -0
- package/scripts/process-runner.mjs +0 -0
- package/scripts/release.mjs +0 -0
- package/scripts/sync-file-icons.mjs +0 -0
- package/scripts/sync-provider-icons.mjs +0 -0
- package/src/app/api/agent/resources/route.ts +18 -0
- package/src/app/api/remote/route.ts +5 -7
- package/src/app/api/sessions/[id]/export/route.ts +3 -1
- package/src/app/globals.css +16 -0
- package/src/components/AppShell.tsx +68 -21
- package/src/components/ChatInput.tsx +98 -27
- package/src/components/ChatWindow.tsx +68 -11
- package/src/components/ContextMeter.tsx +24 -4
- package/src/components/ExtensionUI.tsx +9 -3
- package/src/components/FileViewer.tsx +24 -21
- package/src/components/GitPanel.tsx +449 -449
- package/src/components/GrowthTree.tsx +3 -1
- package/src/components/ImageViewer.tsx +72 -0
- package/src/components/MarkdownBlock.tsx +6 -3
- package/src/components/PreviewImage.tsx +33 -0
- package/src/components/ProjectPanel.tsx +5 -3
- package/src/components/PromptPanel.tsx +322 -313
- package/src/components/RemoteSection.tsx +2 -1
- package/src/components/SessionSidebar.tsx +22 -4
- package/src/components/SettingsPanel.tsx +2654 -2639
- package/src/components/composer/ComposerImages.tsx +1 -79
- package/src/hooks/usePiWeb.ts +47 -2
- package/src/i18n.tsx +24 -0
- package/src/lib/agent-manager.ts +275 -18
- package/src/lib/chat/skill-expansion.ts +10 -0
- package/src/lib/extension-ui.ts +25 -1
- package/src/lib/git-service.ts +291 -289
- package/src/lib/github-identity.ts +245 -245
- package/src/lib/growth-turns.ts +3 -1
- package/src/lib/session-reader.ts +25 -13
- package/src/lib/session-retention.ts +11 -8
- package/src/lib/slash-command-token.ts +14 -0
- package/src/lib/trajectory.ts +17 -6
- package/src/lib/types.ts +24 -2
- package/src/lib/workspace-store.ts +36 -1
- package/.next/diagnostics/build-diagnostics.json +0 -6
- package/.next/diagnostics/framework.json +0 -1
- package/.next/server/chunks/229.js +0 -1
- package/.next/server/chunks/393.js +0 -1
- package/.next/server/chunks/612.js +0 -1
- package/.next/server/chunks/662.js +0 -2
- package/.next/server/chunks/686.js +0 -1
- package/.next/server/chunks/802.js +0 -2
- package/.next/server/chunks/807.js +0 -1
- package/.next/server/chunks/924.js +0 -6
- package/.next/static/4snUelZTEIiQYCkB5nTW7/_buildManifest.js +0 -1
- package/.next/static/chunks/1966.1560d0f43ac9a41a.js +0 -1
- package/.next/static/chunks/2577.eebf2cf291a811ad.js +0 -1
- package/.next/static/chunks/2716.a5228b0eeedbe4c8.js +0 -1
- package/.next/static/chunks/2906.546a64377e12dfbc.js +0 -1
- package/.next/static/chunks/3899.55a0943bf5e7fec7.js +0 -1
- package/.next/static/chunks/4673.8eff281cb63e2799.js +0 -1
- package/.next/static/chunks/4686-f03275819a719fd8.js +0 -1
- package/.next/static/chunks/4778.2dae0c6a9e396326.js +0 -1
- package/.next/static/chunks/app/_global-error/page-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/_not-found/page-851dc3883a1937aa.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/events/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/agent/[id]/tree/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/agent/new/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/config/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/files/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/fs/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/git/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/github/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/growth/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/health/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/models/defaults/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/models/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/pi-settings/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/plugins/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/prompts/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/remote/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/security/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/export/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/sessions/[id]/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/sessions/import/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/sessions/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/skills/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/update/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/version/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/web-auth/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/api/workspaces/route-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/app/page-0159987118376f42.js +0 -1
- package/.next/static/chunks/main-22ccebc2da4d291e.js +0 -5
- package/.next/static/chunks/next/dist/client/components/builtin/app-error-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/forbidden-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/global-error-c37b7707077f50ce.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/not-found-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/next/dist/client/components/builtin/unauthorized-f977af7f858990a2.js +0 -1
- package/.next/static/chunks/webpack-c1b43c269867b64f.js +0 -1
- package/.next/static/css/93ed3daa6f3fbc35.css +0 -1
- package/.next/trace +0 -58
- package/.next/trace-build +0 -1
- /package/.next/static/{4snUelZTEIiQYCkB5nTW7 → QJCtqgDKzGS2BdKABd9ch}/_ssgManifest.js +0 -0
package/src/lib/agent-manager.ts
CHANGED
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
* agent-manager:AgentSession 池 + pi 事件 → Web 事件翻译 + SSE 订阅管理。
|
|
3
3
|
* 打开会话即惰性冷启动(createAgentSession),空闲回收,事件先快照后增量。
|
|
4
4
|
*/
|
|
5
|
-
import { existsSync, readFileSync, statSync } from "node:fs";
|
|
5
|
+
import { closeSync, existsSync, openSync, readFileSync, readSync, statSync } from "node:fs";
|
|
6
6
|
import { randomUUID } from "node:crypto";
|
|
7
7
|
import path from "node:path";
|
|
8
8
|
import type { AgentSession, AgentSessionEvent } from "@earendil-works/pi-coding-agent";
|
|
9
|
-
import { formatSkillsForPrompt, generateUnifiedPatch } from "@earendil-works/pi-coding-agent";
|
|
9
|
+
import { ExtensionRunner, ModelRegistry, formatSkillsForPrompt, generateUnifiedPatch } from "@earendil-works/pi-coding-agent";
|
|
10
10
|
import {
|
|
11
11
|
getResourceLoader,
|
|
12
12
|
createSessionResourceLoader,
|
|
@@ -56,6 +56,8 @@ const IDLE_MS = 10 * 60 * 1000;
|
|
|
56
56
|
const MAX_ACTIVE = 6;
|
|
57
57
|
const BUFFER_CAP = 400;
|
|
58
58
|
const SHUTDOWN_TIMEOUT_MS = 5_000;
|
|
59
|
+
/** 外来写入重载后,这么久之内连上的快照都带着提示 */
|
|
60
|
+
const EXTERNAL_RELOAD_NOTICE_MS = 60_000;
|
|
59
61
|
|
|
60
62
|
interface Managed {
|
|
61
63
|
viewerAvailable?: () => boolean;
|
|
@@ -106,6 +108,12 @@ interface Managed {
|
|
|
106
108
|
ui: ExtensionUiBridge | null;
|
|
107
109
|
/** 项目生长:影子仓库快照的触发器(首个会话事件时创建,dispose 时释放) */
|
|
108
110
|
growth: GrowthTracker | null;
|
|
111
|
+
/** JSONL 已核对到的字节位置(总在整行末尾);之后多出来的行要看是不是别的进程写的(见 externalEntryCount) */
|
|
112
|
+
diskSize: number;
|
|
113
|
+
/** 已发现、还没重载进来的外来条目数(终端 pi 同时在写这个会话) */
|
|
114
|
+
externalEntries: number;
|
|
115
|
+
/** 这一份是因外来写入从磁盘重载出来的:快照带上,前端提示一次 */
|
|
116
|
+
externalReload?: { entries: number; at: number };
|
|
109
117
|
}
|
|
110
118
|
|
|
111
119
|
const globalForAgentManager = globalThis as typeof globalThis & {
|
|
@@ -165,10 +173,95 @@ export function toWebMessage(m: any, entryId?: string): WebMessage {
|
|
|
165
173
|
encodingLoss: result.encodingLoss || undefined,
|
|
166
174
|
patch: patchOf(m.details),
|
|
167
175
|
}];
|
|
176
|
+
} else if (m.role === "compactionSummary" || m.role === "branchSummary") {
|
|
177
|
+
out.role = "summary";
|
|
178
|
+
out.summaryKind = m.role === "branchSummary" ? "branch" : "compaction";
|
|
179
|
+
out.summary = typeof m.summary === "string" ? m.summary : "";
|
|
180
|
+
if (typeof m.tokensBefore === "number" && Number.isFinite(m.tokensBefore)) out.tokensBefore = m.tokensBefore;
|
|
168
181
|
}
|
|
169
182
|
return out;
|
|
170
183
|
}
|
|
171
184
|
|
|
185
|
+
/** 会话条目 → 显示消息:消息照常投影,压缩 / 分支摘要投影成摘要卡,其余条目(切模型、自定义状态等)不显示 */
|
|
186
|
+
function entryToWebMessage(entry: any): WebMessage | null {
|
|
187
|
+
if (entry?.type === "message" && entry.message) return toWebMessage(entry.message, entry.id);
|
|
188
|
+
if (entry?.type !== "compaction" && !(entry?.type === "branch_summary" && entry.summary)) return null;
|
|
189
|
+
const timestamp = Date.parse(entry.timestamp);
|
|
190
|
+
return toWebMessage({
|
|
191
|
+
role: entry.type === "compaction" ? "compactionSummary" : "branchSummary",
|
|
192
|
+
summary: entry.summary,
|
|
193
|
+
tokensBefore: entry.tokensBefore,
|
|
194
|
+
timestamp: Number.isFinite(timestamp) ? timestamp : undefined,
|
|
195
|
+
}, entry.id);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* 被压缩掉的历史:当前分支上已不在模型上下文里的条目,按原顺序照常显示(可编辑、可分叉)。
|
|
200
|
+
* 每张压缩卡挪到它保留的第一条(firstKeptEntryId)前面,和上下文部分「摘要在前、保留消息在后」一致;
|
|
201
|
+
* 这样卡片以上就是模型只看得到摘要的内容。
|
|
202
|
+
*/
|
|
203
|
+
function compactedHistory(sm: Pick<SessionManager, "getBranch">, context: any[]): WebMessage[] {
|
|
204
|
+
if (context[0]?.type !== "compaction") return [];
|
|
205
|
+
const inContext = new Set(context.map((entry) => entry.id));
|
|
206
|
+
const entries = (sm.getBranch() as any[]).filter((entry) => !inContext.has(entry.id));
|
|
207
|
+
const position = new Map(entries.map((entry, index) => [entry.id, index]));
|
|
208
|
+
const markersBefore = new Map<string, WebMessage[]>();
|
|
209
|
+
const moved = new Set<string>();
|
|
210
|
+
entries.forEach((entry, index) => {
|
|
211
|
+
if (entry.type !== "compaction") return;
|
|
212
|
+
const at = position.get(entry.firstKeptEntryId);
|
|
213
|
+
if (at === undefined || at >= index) return;
|
|
214
|
+
const marker = entryToWebMessage(entry)!;
|
|
215
|
+
markersBefore.set(entry.firstKeptEntryId, [...(markersBefore.get(entry.firstKeptEntryId) ?? []), marker]);
|
|
216
|
+
moved.add(entry.id);
|
|
217
|
+
});
|
|
218
|
+
const out: WebMessage[] = [];
|
|
219
|
+
for (const entry of entries) {
|
|
220
|
+
out.push(...(markersBefore.get(entry.id) ?? []));
|
|
221
|
+
if (moved.has(entry.id)) continue;
|
|
222
|
+
const message = entryToWebMessage(entry);
|
|
223
|
+
if (message) out.push(message);
|
|
224
|
+
}
|
|
225
|
+
return out;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
/** 冷会话 / 只读接口的消息列表:被压缩掉的历史 + 当前上下文(最新一次压缩投影成分隔卡) */
|
|
229
|
+
export function sessionDisplayMessages(sm: Pick<SessionManager, "getBranch" | "buildContextEntries">): WebMessage[] {
|
|
230
|
+
const context = sm.buildContextEntries() as any[];
|
|
231
|
+
const current = context.flatMap((entry, index) => {
|
|
232
|
+
// 保留区里更早的压缩条目不进模型上下文(SDK 投影只取下标 0 的那次),这里同样跳过
|
|
233
|
+
if (entry.type === "compaction" && index > 0) return [];
|
|
234
|
+
const message = entryToWebMessage(entry);
|
|
235
|
+
return message ? [message] : [];
|
|
236
|
+
});
|
|
237
|
+
return [...compactedHistory(sm, context), ...current];
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* 压缩刚完成(SDK 先写条目再发 compaction_end):分隔卡 + 它该插在哪条消息前。
|
|
242
|
+
* 取保留下来的第一条用户 / 助手消息,和快照里「卡片在保留消息之前」的位置一致;没有保留消息就追加。
|
|
243
|
+
*/
|
|
244
|
+
function compactionMarker(m: Managed): { marker?: WebMessage; beforeId?: string; beforeStreamId?: string } {
|
|
245
|
+
try {
|
|
246
|
+
const context = m.sm.buildContextEntries() as any[];
|
|
247
|
+
if (context[0]?.type !== "compaction") return {};
|
|
248
|
+
const kept = context.find((entry) => entry.type === "message" && (entry.message?.role === "user" || entry.message?.role === "assistant"));
|
|
249
|
+
return {
|
|
250
|
+
marker: entryToWebMessage(context[0]) ?? undefined,
|
|
251
|
+
beforeId: kept?.id,
|
|
252
|
+
beforeStreamId: kept ? m.messageIds?.get(kept.message) : undefined,
|
|
253
|
+
};
|
|
254
|
+
} catch {
|
|
255
|
+
return {};
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/** 热会话里摘要消息(SDK 每次投影新建的对象)对应的条目 ID:压缩取上下文首条,分支摘要按 fromId + 时间匹配 */
|
|
260
|
+
function summaryEntryId(context: any[], message: any): string | undefined {
|
|
261
|
+
if (message?.role === "compactionSummary") return context[0]?.type === "compaction" ? context[0].id : undefined;
|
|
262
|
+
return context.find((entry) => entry.type === "branch_summary" && entry.fromId === message?.fromId && Date.parse(entry.timestamp) === message?.timestamp)?.id;
|
|
263
|
+
}
|
|
264
|
+
|
|
172
265
|
/** 消息对象 → 持久化条目 ID 的索引;条目数一变就重建(会话只追加,改写分支时条目数也会变) */
|
|
173
266
|
const entryIdIndexes = new WeakMap<SessionManager, { count: number; ids: WeakMap<object, string> }>();
|
|
174
267
|
|
|
@@ -346,11 +439,15 @@ function publishModelState(m: Managed, session: AgentSession): void {
|
|
|
346
439
|
});
|
|
347
440
|
}
|
|
348
441
|
|
|
349
|
-
/**
|
|
442
|
+
/**
|
|
443
|
+
* 切模型后恢复用户显式选过的级别(按新模型钳制)。
|
|
444
|
+
* 只记进本会话(SDK 会写 thinking_level_change 条目),不写全局默认:
|
|
445
|
+
* ~/.pi/agent/settings.json 与终端 pi 共用,在 PiWeb 里切一下档位不该改掉终端 pi 的默认值。
|
|
446
|
+
*/
|
|
350
447
|
function reapplyDesiredLevel(m: Managed, session: AgentSession): void {
|
|
351
448
|
if (!m.desiredThinkingLevel) return;
|
|
352
449
|
const target = clampThinkingLevel(session.model as any, m.desiredThinkingLevel as any);
|
|
353
|
-
if (target !== session.thinkingLevel) session.setThinkingLevel(target as never
|
|
450
|
+
if (target !== session.thinkingLevel) session.setThinkingLevel(target as never);
|
|
354
451
|
}
|
|
355
452
|
|
|
356
453
|
/**
|
|
@@ -658,6 +755,7 @@ function translate(m: Managed, evt: AgentSessionEvent): void {
|
|
|
658
755
|
phase: "end",
|
|
659
756
|
reason: (evt as any).reason,
|
|
660
757
|
errorMessage: (evt as any).errorMessage,
|
|
758
|
+
...((evt as any).result && !(evt as any).errorMessage ? compactionMarker(m) : {}),
|
|
661
759
|
ts: now,
|
|
662
760
|
});
|
|
663
761
|
publishUsage(m, true);
|
|
@@ -760,7 +858,8 @@ export function warmSessionResources(cwds: string[]): void {
|
|
|
760
858
|
|
|
761
859
|
export function getManaged(sessionPath: string): Managed {
|
|
762
860
|
let m = sessions.get(sessionPath);
|
|
763
|
-
if (
|
|
861
|
+
if (m) m = syncWithDisk(m);
|
|
862
|
+
else {
|
|
764
863
|
if (!existsSync(sessionPath)) throw new BoundaryError("session not found");
|
|
765
864
|
const sm = openSessionManager(sessionPath);
|
|
766
865
|
m = registerManaged(sessionPath, sm.getCwd() || process.cwd(), sm);
|
|
@@ -769,6 +868,115 @@ export function getManaged(sessionPath: string): Managed {
|
|
|
769
868
|
return m;
|
|
770
869
|
}
|
|
771
870
|
|
|
871
|
+
/**
|
|
872
|
+
* 数出 JSONL 里自上次核对以来、不是本进程写的新条目。
|
|
873
|
+
* 自己的条目在 SessionManager._appendEntry 里先进内存索引再落盘,所以索引里找不到 id 的行就是外来的
|
|
874
|
+
* (终端 pi 同时打开了这个会话)。只读新增的字节;文件变短(被整体重写)就从头核对;没写完的最后一行留到下次。
|
|
875
|
+
*/
|
|
876
|
+
function externalEntryCount(m: Managed): number {
|
|
877
|
+
let size: number;
|
|
878
|
+
try {
|
|
879
|
+
size = statSync(m.sessionPath).size;
|
|
880
|
+
} catch {
|
|
881
|
+
return 0; // 还没落盘
|
|
882
|
+
}
|
|
883
|
+
if (size === m.diskSize) return 0;
|
|
884
|
+
const from = size < m.diskSize ? 0 : m.diskSize;
|
|
885
|
+
let buf = Buffer.alloc(size - from);
|
|
886
|
+
let fd: number | undefined;
|
|
887
|
+
try {
|
|
888
|
+
fd = openSync(m.sessionPath, "r");
|
|
889
|
+
buf = buf.subarray(0, readSync(fd, buf, 0, buf.length, from));
|
|
890
|
+
} catch {
|
|
891
|
+
return 0;
|
|
892
|
+
} finally {
|
|
893
|
+
if (fd !== undefined) closeSync(fd);
|
|
894
|
+
}
|
|
895
|
+
const end = buf.lastIndexOf(0x0a);
|
|
896
|
+
m.diskSize = from + end + 1;
|
|
897
|
+
let count = 0;
|
|
898
|
+
for (const line of buf.subarray(0, Math.max(0, end)).toString("utf8").split("\n")) {
|
|
899
|
+
if (!line.trim()) continue;
|
|
900
|
+
try {
|
|
901
|
+
const entry = JSON.parse(line);
|
|
902
|
+
if (entry?.type !== "session" && typeof entry?.id === "string" && !m.sm.getEntry(entry.id)) count += 1;
|
|
903
|
+
} catch {
|
|
904
|
+
/* 从行中间读起的半行、写坏的行都不算 */
|
|
905
|
+
}
|
|
906
|
+
}
|
|
907
|
+
return count;
|
|
908
|
+
}
|
|
909
|
+
|
|
910
|
+
/**
|
|
911
|
+
* pi 终端和 PiWeb 共用同一份 JSONL,但各自在内存里记着会话树:一边写了,另一边不知道,
|
|
912
|
+
* 接着写就从自己的旧叶子分叉。打开会话、发消息都经过 getManaged,这里先核对磁盘:
|
|
913
|
+
* 有外来条目且本会话空闲,就丢掉内存里的旧副本从磁盘重开;正在跑就只提醒,空闲后的下一次核对再重载。
|
|
914
|
+
*/
|
|
915
|
+
function syncWithDisk(m: Managed): Managed {
|
|
916
|
+
const found = externalEntryCount(m);
|
|
917
|
+
if (!found && !m.externalEntries) return m;
|
|
918
|
+
m.externalEntries += found;
|
|
919
|
+
if (m.creating || isManagedBusy(m)) {
|
|
920
|
+
if (found) publish(m, { type: "external_write", entries: m.externalEntries, ts: Date.now() });
|
|
921
|
+
return m;
|
|
922
|
+
}
|
|
923
|
+
return reopenManaged(m);
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
/** 等同空闲回收后再打开:会话级的选择(工具档位、思考级别)带过去,正在看的浏览器立刻重连拿新快照 */
|
|
927
|
+
function reopenManaged(m: Managed): Managed {
|
|
928
|
+
let sm: SessionManager;
|
|
929
|
+
try {
|
|
930
|
+
sm = openSessionManager(m.sessionPath);
|
|
931
|
+
} catch {
|
|
932
|
+
return m; // 读不出来(正被写坏?)就先沿用旧副本,下次再试
|
|
933
|
+
}
|
|
934
|
+
const at = Date.now();
|
|
935
|
+
const reload = { entries: m.externalEntries, at };
|
|
936
|
+
// 直接写给订阅者:publish 可能排在队列里,晚于 dispose 就送不到了
|
|
937
|
+
const frame = JSON.stringify({ type: "external_reload", ...reload, ts: at });
|
|
938
|
+
for (const send of m.subscribers) {
|
|
939
|
+
try {
|
|
940
|
+
send(m.seq, frame);
|
|
941
|
+
} catch {
|
|
942
|
+
/* ignore */
|
|
943
|
+
}
|
|
944
|
+
}
|
|
945
|
+
disposeSession(m);
|
|
946
|
+
const fresh = registerManaged(m.sessionPath, sm.getCwd() || m.cwd, sm);
|
|
947
|
+
// 核对位置沿用旧的(整行末尾):打开时恰好没写完的那行,写完后还会被认作外来的
|
|
948
|
+
fresh.diskSize = m.diskSize;
|
|
949
|
+
fresh.toolPreset = m.toolPreset;
|
|
950
|
+
fresh.customActiveTools = m.customActiveTools;
|
|
951
|
+
fresh.desiredThinkingLevel = m.desiredThinkingLevel;
|
|
952
|
+
fresh.viewerGraceUntil = m.viewerGraceUntil;
|
|
953
|
+
fresh.externalReload = reload;
|
|
954
|
+
return fresh;
|
|
955
|
+
}
|
|
956
|
+
|
|
957
|
+
/**
|
|
958
|
+
* 侧栏每 3 秒拉一次会话列表,顺带核对所有开着的会话:终端写入几秒内就能提示 / 重载,
|
|
959
|
+
* 回合中途发现的外来写入也在回合结束后的下一次拉取时重载(不在 agent_settled 里做:
|
|
960
|
+
* SDK 收尾时还可能有延后的提问要开跑,那一刻回收会把它丢掉)。每个会话一次 stat,没变就不读文件。
|
|
961
|
+
*/
|
|
962
|
+
export function syncSessionsFromDisk(): void {
|
|
963
|
+
for (const m of [...sessions.values()]) {
|
|
964
|
+
try {
|
|
965
|
+
syncWithDisk(m);
|
|
966
|
+
} catch {
|
|
967
|
+
/* 核对失败只是晚一点发现,打开 / 发送时还会再查 */
|
|
968
|
+
}
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
function fileSize(file: string): number {
|
|
973
|
+
try {
|
|
974
|
+
return statSync(file).size;
|
|
975
|
+
} catch {
|
|
976
|
+
return 0;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
|
|
772
980
|
function registerManaged(sessionPath: string, cwd: string, sm: SessionManager): Managed {
|
|
773
981
|
let initialTrajectory: TrajEntry[] = [];
|
|
774
982
|
try {
|
|
@@ -800,6 +1008,8 @@ function registerManaged(sessionPath: string, cwd: string, sm: SessionManager):
|
|
|
800
1008
|
runActive: false,
|
|
801
1009
|
disposed: false,
|
|
802
1010
|
messageIds: new WeakMap(),
|
|
1011
|
+
diskSize: fileSize(sessionPath),
|
|
1012
|
+
externalEntries: 0,
|
|
803
1013
|
};
|
|
804
1014
|
sessions.set(sessionPath, m);
|
|
805
1015
|
return m;
|
|
@@ -1232,7 +1442,22 @@ startArchivedSessionRetention({ isActive: isSessionInUse, log: (message) => cons
|
|
|
1232
1442
|
|
|
1233
1443
|
type ResourceBundle = Pick<WebSnapshot, "skills" | "promptTemplates" | "extensionCommands" | "projectTrust" | "resourceDiagnostics">;
|
|
1234
1444
|
|
|
1235
|
-
|
|
1445
|
+
/**
|
|
1446
|
+
* 会话还不存在时也能发现命令:不绑定运行时、不发 session_start、不启动 MCP。
|
|
1447
|
+
* 新会话页和冷会话的斜杠补全都靠它,等第一次提问才建会话。
|
|
1448
|
+
*/
|
|
1449
|
+
export async function loadWorkspaceResources(cwd: string): Promise<ResourceBundle> {
|
|
1450
|
+
await refreshResourceLoaderIfStale(cwd);
|
|
1451
|
+
const modelRegistry = new ModelRegistry(await getModelRuntime());
|
|
1452
|
+
return withResourceLock(async () => {
|
|
1453
|
+
const loader = getResourceLoader(cwd);
|
|
1454
|
+
const { extensions, runtime } = loader.getExtensions();
|
|
1455
|
+
const runner = new ExtensionRunner(extensions, runtime, cwd, SessionManager.inMemory(cwd), modelRegistry);
|
|
1456
|
+
return collectResources({ cwd, session: null }, runner);
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1459
|
+
|
|
1460
|
+
function collectResources(m: Pick<Managed, "cwd" | "session">, commandRunner?: ExtensionRunner): ResourceBundle {
|
|
1236
1461
|
const loader = m.session?.resourceLoader ?? peekResourceLoader(m.cwd) ?? getResourceLoader(m.cwd);
|
|
1237
1462
|
const diagnostics: WebSnapshot["resourceDiagnostics"] = [];
|
|
1238
1463
|
let skills: WebSnapshot["skills"] = [];
|
|
@@ -1257,9 +1482,9 @@ function collectResources(m: Managed): ResourceBundle {
|
|
|
1257
1482
|
/* ignore */
|
|
1258
1483
|
}
|
|
1259
1484
|
let extensionCommands: WebSnapshot["extensionCommands"] = [];
|
|
1260
|
-
if (m.session) {
|
|
1485
|
+
if (m.session || commandRunner) {
|
|
1261
1486
|
try {
|
|
1262
|
-
const runner =
|
|
1487
|
+
const runner = commandRunner ?? m.session?.extensionRunner;
|
|
1263
1488
|
if (runner) {
|
|
1264
1489
|
extensionCommands = runner.getRegisteredCommands().map((c) => ({
|
|
1265
1490
|
name: c.invocationName,
|
|
@@ -1388,7 +1613,22 @@ export async function buildSnapshot(m: Managed): Promise<WebSnapshot> {
|
|
|
1388
1613
|
|
|
1389
1614
|
if (session) {
|
|
1390
1615
|
const hasInflightFinal = linkInflightFinal(m, session);
|
|
1391
|
-
|
|
1616
|
+
// session.messages 只是压缩后的模型上下文;前面补上被压缩掉的历史,压缩摘要投影成分隔卡
|
|
1617
|
+
let context: any[] = [];
|
|
1618
|
+
let history: WebMessage[] = [];
|
|
1619
|
+
try {
|
|
1620
|
+
context = m.sm.buildContextEntries() as any[];
|
|
1621
|
+
history = compactedHistory(m.sm, context);
|
|
1622
|
+
} catch {
|
|
1623
|
+
/* 条目读不到时只显示上下文 */
|
|
1624
|
+
}
|
|
1625
|
+
messages = [
|
|
1626
|
+
...history,
|
|
1627
|
+
...session.messages.map((message: any) =>
|
|
1628
|
+
message?.role === "compactionSummary" || message?.role === "branchSummary"
|
|
1629
|
+
? toWebMessage(message, summaryEntryId(context, message))
|
|
1630
|
+
: projectMessage(m, message)),
|
|
1631
|
+
];
|
|
1392
1632
|
// final 已被 SDK push 时只投影一次;延后的 end 使用相同 streamId 继续更新权威内容。
|
|
1393
1633
|
if (!hasInflightFinal && m.inflight) {
|
|
1394
1634
|
const partial = projectMessage(m, m.inflight);
|
|
@@ -1405,12 +1645,9 @@ export async function buildSnapshot(m: Managed): Promise<WebSnapshot> {
|
|
|
1405
1645
|
}
|
|
1406
1646
|
isStreaming = isManagedBusy(m);
|
|
1407
1647
|
} else {
|
|
1408
|
-
// 冷渲染:从 JSONL
|
|
1648
|
+
// 冷渲染:从 JSONL 还原消息(不启动 agent),压缩掉的历史也照常显示
|
|
1409
1649
|
try {
|
|
1410
|
-
|
|
1411
|
-
messages = entries
|
|
1412
|
-
.filter((e: any) => e.type === "message" && e.message)
|
|
1413
|
-
.map((e: any) => toWebMessage(e.message, e.id));
|
|
1650
|
+
messages = sessionDisplayMessages(m.sm);
|
|
1414
1651
|
} catch {
|
|
1415
1652
|
messages = [];
|
|
1416
1653
|
}
|
|
@@ -1481,7 +1718,8 @@ export async function buildSnapshot(m: Managed): Promise<WebSnapshot> {
|
|
|
1481
1718
|
}
|
|
1482
1719
|
|
|
1483
1720
|
// 斜杠命令数据源:技能 / 提示模板 / 扩展命令,加上项目信任与资源诊断
|
|
1484
|
-
|
|
1721
|
+
// 冷会话还没有 extensionRunner,但命令目录已经能发现;不要在第一次提问前推空列表。
|
|
1722
|
+
const resources = session && !m.creating ? collectResources(m) : await loadWorkspaceResources(m.cwd);
|
|
1485
1723
|
|
|
1486
1724
|
const baseTrajectory = m.ledger.entries.length ? m.ledger.entries : coldTrajectory(m);
|
|
1487
1725
|
const trajectory = [...environmentTrajectory(m, session, contextResourcesLoaded ? contextResources : undefined), ...baseTrajectory];
|
|
@@ -1513,6 +1751,8 @@ export async function buildSnapshot(m: Managed): Promise<WebSnapshot> {
|
|
|
1513
1751
|
queue,
|
|
1514
1752
|
trajectory,
|
|
1515
1753
|
isStreaming,
|
|
1754
|
+
// 重载时没人在看(打开会话那一刻才发现):靠快照把提示带给随后连上的浏览器,前端按 at 去重
|
|
1755
|
+
...(m.externalReload && Date.now() - m.externalReload.at < EXTERNAL_RELOAD_NOTICE_MS ? { externalReload: m.externalReload } : {}),
|
|
1516
1756
|
};
|
|
1517
1757
|
}
|
|
1518
1758
|
|
|
@@ -1594,6 +1834,17 @@ async function prepareGrowthBriefly(m: Managed): Promise<void> {
|
|
|
1594
1834
|
]);
|
|
1595
1835
|
}
|
|
1596
1836
|
|
|
1837
|
+
type PromptImages = NonNullable<NonNullable<Parameters<AgentSession["prompt"]>[1]>["images"]>;
|
|
1838
|
+
|
|
1839
|
+
/** 被编辑的那条用户消息里的图片:编辑只改文字,图片原样带进重发(编辑框里也照样显示它们) */
|
|
1840
|
+
function userMessageImages(session: AgentSession, entryId: string): PromptImages {
|
|
1841
|
+
const entry = session.sessionManager?.getEntry?.(entryId) as { type?: string; message?: { role?: string; content?: unknown } } | undefined;
|
|
1842
|
+
if (entry?.type !== "message" || entry.message?.role !== "user" || !Array.isArray(entry.message.content)) return [];
|
|
1843
|
+
return entry.message.content.filter(
|
|
1844
|
+
(c): c is PromptImages[number] => c?.type === "image" && typeof c.data === "string" && typeof c.mimeType === "string",
|
|
1845
|
+
);
|
|
1846
|
+
}
|
|
1847
|
+
|
|
1597
1848
|
/** 交给 SDK,并在「受理」那一刻返回,而不是等整轮跑完;受理之后的错误经 SSE 可见 */
|
|
1598
1849
|
function submitPrompt(
|
|
1599
1850
|
m: Managed,
|
|
@@ -1615,7 +1866,10 @@ function submitPrompt(
|
|
|
1615
1866
|
});
|
|
1616
1867
|
// 同步挂好 rejection handler;接受后的错误仍经 SSE 可见,不能成为未处理拒绝。
|
|
1617
1868
|
void run.then(() => {
|
|
1618
|
-
|
|
1869
|
+
// 上一轮的 agent_settled 还没发完时,SDK 不拒绝提问,而是把它挂进 deferredSettledActions,
|
|
1870
|
+
// 立即返回并在稍后用同一份 options 重发;这条路径不会调用 preflightResult。
|
|
1871
|
+
// 消息确实会发出去,不能报「未受理」——否则界面报错,消息随后照样出现。真正的拒绝只会是 rejection。
|
|
1872
|
+
if (!accepted) resolve({ ok: true, data: { accepted: true } });
|
|
1619
1873
|
}, (error) => {
|
|
1620
1874
|
const message = String(error?.message ?? error);
|
|
1621
1875
|
if (accepted) publish(m, { type: "error", message, ts: Date.now() });
|
|
@@ -1699,7 +1953,8 @@ export async function execute(m: Managed, cmd: AgentCommand): Promise<CommandRes
|
|
|
1699
1953
|
// 与 pi 终端一致:不支持的档位就近钳制而不是拒绝(非推理模型 → off),并把实际生效值回传
|
|
1700
1954
|
const effective = clampThinkingLevel(session.model as any, requested as any);
|
|
1701
1955
|
m.desiredThinkingLevel = requested;
|
|
1702
|
-
|
|
1956
|
+
// 不传 persist:与终端 pi 一样,聊天里切档位只影响本会话;全局默认仍以 settings.json 为准(见 reapplyDesiredLevel)
|
|
1957
|
+
session.setThinkingLevel(effective as never);
|
|
1703
1958
|
publishModelState(m, session);
|
|
1704
1959
|
return { ok: true, data: { requested, thinkingLevel: session.thinkingLevel, clamped: session.thinkingLevel !== requested } };
|
|
1705
1960
|
}
|
|
@@ -1777,13 +2032,15 @@ export async function execute(m: Managed, cmd: AgentCommand): Promise<CommandRes
|
|
|
1777
2032
|
}
|
|
1778
2033
|
}
|
|
1779
2034
|
if (m.disposed) throw new Error("session is disposed");
|
|
2035
|
+
// 回到消息之前先取它带的图片:客户端只发改过的文字,不取的话重发出去图就没了
|
|
2036
|
+
const images = userMessageImages(session, entryId);
|
|
1780
2037
|
const moved = await session.navigateTree(entryId);
|
|
1781
2038
|
if (moved?.cancelled) return { ok: false, error: "edit cancelled" };
|
|
1782
2039
|
await prepareGrowthBriefly(m);
|
|
1783
2040
|
if (m.disposed) throw new Error("session is disposed");
|
|
1784
2041
|
// 与普通 prompt 一样在受理时就返回:等整轮跑完的话,这期间 promptSubmitting 一直为真,
|
|
1785
2042
|
// 插话 / 排队 / 新消息全被拒,HTTP 请求也一直挂着
|
|
1786
|
-
return await submitPrompt(m, session, text);
|
|
2043
|
+
return await submitPrompt(m, session, text, images.length ? { images } : {});
|
|
1787
2044
|
} finally {
|
|
1788
2045
|
m.promptSubmitting = false;
|
|
1789
2046
|
if (!m.disposed) publish(m, { type: "status", isStreaming: isManagedBusy(m), state: isManagedBusy(m) ? "running" : "idle", ts: Date.now() });
|
|
@@ -39,3 +39,13 @@ export function parseExpandedSkill(text: string): ExpandedSkill | null {
|
|
|
39
39
|
// 参数在闭合标签之后,与技能正文同属一条用户消息(pi 不单独发)
|
|
40
40
|
return { name, args: trimmed.slice(close + "</skill>".length).trim() };
|
|
41
41
|
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* 拿用户消息当标题 / 摘要时用的文本:技能展开还原成用户当初打的 `/skill:name 参数`,
|
|
45
|
+
* 别的原样返回。会话列表、窗口标题、增长树的轮次标题都走它,不再露出 `<skill name=…>` 和 SKILL.md 正文。
|
|
46
|
+
*/
|
|
47
|
+
export function promptTitleText(text: string): string {
|
|
48
|
+
const skill = parseExpandedSkill(text);
|
|
49
|
+
if (!skill) return text;
|
|
50
|
+
return skill.args ? `/skill:${skill.name} ${skill.args}` : `/skill:${skill.name}`;
|
|
51
|
+
}
|
package/src/lib/extension-ui.ts
CHANGED
|
@@ -31,6 +31,28 @@ export interface ExtensionUiBridge {
|
|
|
31
31
|
|
|
32
32
|
const DEFAULT_TIMEOUT_MS = 10 * 60 * 1000;
|
|
33
33
|
|
|
34
|
+
const plainText = (text: unknown): string => (typeof text === "string" ? text : String(text ?? ""));
|
|
35
|
+
/**
|
|
36
|
+
* 扩展常写 `ctx.ui.theme.fg("accent", text)` 给状态文字上色;pi 的 noOp 上下文也带 theme。
|
|
37
|
+
* 缺了它扩展一调就抛 TypeError。Web 不渲染 ANSI,所以给一个原样返回文字、不加任何转义序列的主题。
|
|
38
|
+
*/
|
|
39
|
+
const PLAIN_THEME = {
|
|
40
|
+
name: "piweb-plain",
|
|
41
|
+
style: (text: string) => plainText(text),
|
|
42
|
+
fg: (_color: string, text: string) => plainText(text),
|
|
43
|
+
bg: (_color: string, text: string) => plainText(text),
|
|
44
|
+
bold: plainText,
|
|
45
|
+
italic: plainText,
|
|
46
|
+
underline: plainText,
|
|
47
|
+
inverse: plainText,
|
|
48
|
+
strikethrough: plainText,
|
|
49
|
+
getFgAnsi: () => "",
|
|
50
|
+
getBgAnsi: () => "",
|
|
51
|
+
getColorMode: () => "truecolor",
|
|
52
|
+
getThinkingBorderColor: () => plainText,
|
|
53
|
+
getBashModeBorderColor: () => plainText,
|
|
54
|
+
};
|
|
55
|
+
|
|
34
56
|
export function createExtensionUiBridge(opts: {
|
|
35
57
|
publish: (evt: WebEvent) => void;
|
|
36
58
|
hasViewers: () => boolean;
|
|
@@ -106,9 +128,11 @@ export function createExtensionUiBridge(opts: {
|
|
|
106
128
|
addAutocompleteProvider: () => {},
|
|
107
129
|
setEditorComponent: () => {},
|
|
108
130
|
getEditorComponent: () => undefined,
|
|
131
|
+
theme: PLAIN_THEME,
|
|
109
132
|
getAllThemes: () => [],
|
|
110
133
|
getTheme: () => undefined,
|
|
111
|
-
|
|
134
|
+
// 与 pi 的 noOp 同一返回形状:扩展会读 result.success,返回 undefined 会让它们解构时出错
|
|
135
|
+
setTheme: () => ({ success: false, error: "UI not available" }),
|
|
112
136
|
getToolsExpanded: () => false,
|
|
113
137
|
setToolsExpanded: () => {},
|
|
114
138
|
};
|