vibepulse 0.2.1 → 0.3.0-beta.0
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 +1 -0
- package/.next/build-manifest.json +2 -2
- package/.next/cache/.previewinfo +1 -1
- package/.next/cache/.rscinfo +1 -1
- package/.next/cache/.tsbuildinfo +1 -1
- package/.next/cache/config.json +3 -3
- package/.next/fallback-build-manifest.json +2 -2
- package/.next/prerender-manifest.json +3 -3
- package/.next/routes-manifest.json +8 -0
- package/.next/server/app/_global-error/page.js +1 -1
- package/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/server/app/_global-error.html +2 -2
- package/.next/server/app/_global-error.rsc +1 -1
- package/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/server/app/_global-error.segments/_index.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/_head.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/api/node/sessions/[id]/open-editor/route.js.nft.json +1 -1
- package/.next/server/app/api/node/sessions/route.js +5 -3
- package/.next/server/app/api/node/sessions/route.js.nft.json +1 -1
- package/.next/server/app/api/opencode-config/route.js.nft.json +1 -1
- package/.next/server/app/api/opencode-config/status/route.js.nft.json +1 -1
- package/.next/server/app/api/profiles/[id]/apply/route.js.nft.json +1 -1
- package/.next/server/app/api/profiles/[id]/export/route.js.nft.json +1 -1
- package/.next/server/app/api/profiles/[id]/route.js.nft.json +1 -1
- package/.next/server/app/api/profiles/import/route.js.nft.json +1 -1
- package/.next/server/app/api/profiles/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/archive/route.js +3 -2
- package/.next/server/app/api/sessions/[id]/archive/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/delete/route.js +3 -2
- package/.next/server/app/api/sessions/[id]/delete/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/open-editor/route.js +1 -1
- package/.next/server/app/api/sessions/[id]/open-editor/route.js.nft.json +1 -1
- package/.next/server/app/api/sessions/[id]/restore/route/app-paths-manifest.json +3 -0
- package/.next/server/app/api/sessions/[id]/restore/route/build-manifest.json +11 -0
- package/.next/server/app/api/sessions/[id]/restore/route/server-reference-manifest.json +4 -0
- package/.next/server/app/api/sessions/[id]/restore/route.js +8 -0
- package/.next/server/app/api/sessions/[id]/restore/route.js.map +5 -0
- package/.next/server/app/api/sessions/[id]/restore/route.js.nft.json +1 -0
- package/.next/server/app/api/sessions/[id]/restore/route_client-reference-manifest.js +2 -0
- package/.next/server/app/api/sessions/route.js +5 -3
- package/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/server/app/index.html +1 -1
- package/.next/server/app/index.rsc +3 -3
- package/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/server/app-paths-manifest.json +1 -0
- package/.next/server/chunks/[root-of-the-server]__31d19c5c._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__31d19c5c._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__56690af0._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__56690af0._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__56f5f249._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__56f5f249._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__59175de4._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__59175de4._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__5e0a0e38._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__5e0a0e38._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__64fffc02._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__64fffc02._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__89c5eeab._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__89c5eeab._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__8da6c5a8._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__8da6c5a8._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__98073dd6._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__98073dd6._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__b796d06c._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__b796d06c._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__b7b717eb._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__b7b717eb._.js.map +1 -0
- package/.next/server/chunks/[root-of-the-server]__c2ce5c0f._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__c2ce5c0f._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__d8e61048._.js +1 -1
- package/.next/server/chunks/[root-of-the-server]__d8e61048._.js.map +1 -1
- package/.next/server/chunks/[root-of-the-server]__f441109e._.js +3 -0
- package/.next/server/chunks/[root-of-the-server]__f441109e._.js.map +1 -0
- package/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_restore_route_actions_af7d6b6c.js +3 -0
- package/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_restore_route_actions_af7d6b6c.js.map +1 -0
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_2edc9589.js +3 -0
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_2edc9589.js.map +1 -0
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7f178d4a.js +3 -0
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7f178d4a.js.map +1 -0
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_aca45402.js +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_aca45402.js.map +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js.map +1 -1
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_d0c0f338.js +3 -0
- package/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_d0c0f338.js.map +1 -0
- package/.next/server/chunks/src_lib_session-providers_claudeCode_ts_0f9590ed._.js +3 -0
- package/.next/server/chunks/src_lib_session-providers_claudeCode_ts_0f9590ed._.js.map +1 -0
- package/.next/server/chunks/ssr/{[root-of-the-server]__631e12d0._.js → [root-of-the-server]__c91a8380._.js} +2 -2
- package/.next/server/chunks/ssr/{[root-of-the-server]__631e12d0._.js.map → [root-of-the-server]__c91a8380._.js.map} +1 -1
- package/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js +4 -4
- package/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js.map +1 -1
- package/.next/server/pages/404.html +1 -1
- package/.next/server/pages/500.html +2 -2
- package/.next/server/server-reference-manifest.js +1 -1
- package/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +1 -0
- package/.next/standalone/.next/build-manifest.json +2 -2
- package/.next/standalone/.next/prerender-manifest.json +3 -3
- package/.next/standalone/.next/routes-manifest.json +8 -0
- package/.next/standalone/.next/server/app/_global-error/page.js +1 -1
- package/.next/standalone/.next/server/app/_global-error/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +2 -2
- package/.next/standalone/.next/server/app/_global-error.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js +1 -1
- package/.next/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +1 -1
- package/.next/standalone/.next/server/app/_not-found.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/node/sessions/[id]/open-editor/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/node/sessions/route.js +5 -3
- package/.next/standalone/.next/server/app/api/node/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/opencode-config/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/opencode-config/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/profiles/[id]/apply/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/profiles/[id]/export/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/profiles/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/profiles/import/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/profiles/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/archive/route.js +3 -2
- package/.next/standalone/.next/server/app/api/sessions/[id]/archive/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/delete/route.js +3 -2
- package/.next/standalone/.next/server/app/api/sessions/[id]/delete/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route.js +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/open-editor/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route/app-paths-manifest.json +3 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route/build-manifest.json +11 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route/server-reference-manifest.json +4 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route.js +8 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route.js.map +5 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route.js.nft.json +1 -0
- package/.next/standalone/.next/server/app/api/sessions/[id]/restore/route_client-reference-manifest.js +2 -0
- package/.next/standalone/.next/server/app/api/sessions/route.js +5 -3
- package/.next/standalone/.next/server/app/api/sessions/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/index.html +1 -1
- package/.next/standalone/.next/server/app/index.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_full.segment.rsc +3 -3
- package/.next/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/.next/standalone/.next/server/app/index.segments/_index.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +1 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__31d19c5c._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__56690af0._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__56f5f249._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__59175de4._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__5e0a0e38._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__64fffc02._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__89c5eeab._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__8da6c5a8._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__98073dd6._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b796d06c._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__b7b717eb._.js +3 -0
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__c2ce5c0f._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__d8e61048._.js +1 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__f441109e._.js +3 -0
- package/.next/standalone/.next/server/chunks/_next-internal_server_app_api_sessions_[id]_restore_route_actions_af7d6b6c.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_2edc9589.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_7f178d4a.js +3 -0
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_aca45402.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_b054aff3.js +1 -1
- package/.next/standalone/.next/server/chunks/node_modules_next_dist_esm_build_templates_app-route_d0c0f338.js +3 -0
- package/.next/standalone/.next/server/chunks/src_lib_session-providers_claudeCode_ts_0f9590ed._.js +3 -0
- package/.next/standalone/.next/server/chunks/ssr/{[root-of-the-server]__631e12d0._.js → [root-of-the-server]__c91a8380._.js} +2 -2
- package/.next/standalone/.next/server/chunks/ssr/src_app_page_tsx_a7111f3e._.js +4 -4
- package/.next/standalone/.next/server/pages/404.html +1 -1
- package/.next/standalone/.next/server/pages/500.html +2 -2
- package/.next/standalone/.next/server/server-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/b3bc362202331708.css +3 -0
- package/.next/standalone/.next/static/chunks/c1294e057d8d4681.js +13 -0
- package/.next/standalone/AGENTS.md +2 -2
- package/.next/standalone/README.md +30 -7
- package/.next/standalone/docs/session-status-detection.md +36 -0
- package/.next/standalone/docs/superpowers/plans/2026-04-05-oh-my-openagent-migration.md +57 -0
- package/.next/standalone/docs/superpowers/specs/2026-04-09-claude-capability-alignment-design.md +39 -0
- package/.next/standalone/package-lock.json +2 -2
- package/.next/standalone/package.json +1 -1
- package/.next/standalone/src/app/api/AGENTS.md +2 -2
- package/.next/standalone/src/app/api/node/sessions/[id]/archive/route.test.ts +60 -1
- package/.next/standalone/src/app/api/node/sessions/[id]/archive/route.ts +77 -22
- package/.next/standalone/src/app/api/node/sessions/route.test.ts +282 -0
- package/.next/standalone/src/app/api/node/sessions/route.ts +141 -17
- package/.next/standalone/src/app/api/opencode-config/status/route.ts +6 -7
- package/.next/standalone/src/app/api/opencode-events/route.test.ts +3 -1
- package/.next/standalone/src/app/api/sessions/[id]/archive/route.test.ts +101 -0
- package/.next/standalone/src/app/api/sessions/[id]/archive/route.ts +47 -12
- package/.next/standalone/src/app/api/sessions/[id]/delete/route.test.ts +92 -0
- package/.next/standalone/src/app/api/sessions/[id]/delete/route.ts +45 -10
- package/.next/standalone/src/app/api/sessions/[id]/open-editor/route.test.ts +74 -0
- package/.next/standalone/src/app/api/sessions/[id]/open-editor/route.ts +22 -2
- package/.next/standalone/src/app/api/sessions/[id]/restore/route.test.ts +186 -0
- package/.next/standalone/src/app/api/sessions/[id]/restore/route.ts +184 -0
- package/.next/standalone/src/app/api/sessions/route.test.ts +1889 -107
- package/.next/standalone/src/app/api/sessions/route.ts +365 -981
- package/.next/standalone/src/components/AGENTS.md +2 -2
- package/.next/standalone/src/components/KanbanBoard.test.tsx +307 -1
- package/.next/standalone/src/components/KanbanBoard.tsx +105 -18
- package/.next/standalone/src/components/ProjectCard.test.tsx +416 -2
- package/.next/standalone/src/components/ProjectCard.tsx +238 -86
- package/.next/standalone/src/components/SessionCard.test.tsx +253 -2
- package/.next/standalone/src/components/SessionCard.tsx +182 -76
- package/.next/standalone/src/components/opencode-config/AgentConfigForm.test.tsx +28 -0
- package/.next/standalone/src/components/opencode-config/AgentConfigForm.tsx +9 -9
- package/.next/standalone/src/components/opencode-config/ConfigButton.tsx +4 -4
- package/.next/standalone/src/components/opencode-config/categories/CategoriesList.tsx +10 -8
- package/.next/standalone/src/components/opencode-config/categories/CategoriesManager.test.tsx +29 -1
- package/.next/standalone/src/components/opencode-config/categories/CategoryConfigForm.test.tsx +49 -0
- package/.next/standalone/src/components/opencode-config/categories/CategoryConfigForm.tsx +6 -6
- package/.next/standalone/src/hooks/useOpencodeSync.test.ts +321 -1
- package/.next/standalone/src/hooks/useOpencodeSync.ts +16 -12
- package/.next/standalone/src/index.ts +1 -1
- package/.next/standalone/src/lib/claudeSessionOverrides.test.ts +75 -0
- package/.next/standalone/src/lib/claudeSessionOverrides.ts +169 -0
- package/.next/standalone/src/lib/opencodeConfig.test.ts +53 -4
- package/.next/standalone/src/lib/opencodeConfig.ts +24 -12
- package/.next/standalone/src/lib/profiles/storage.test.ts +38 -1
- package/.next/standalone/src/lib/profiles/storage.ts +17 -17
- package/.next/standalone/src/lib/session-providers/claudeCode.test.ts +2288 -0
- package/.next/standalone/src/lib/session-providers/claudeCode.ts +1083 -0
- package/.next/standalone/src/lib/session-providers/localAggregator.test.ts +322 -0
- package/.next/standalone/src/lib/session-providers/localAggregator.ts +302 -0
- package/.next/standalone/src/lib/session-providers/opencodeProvider.ts +723 -0
- package/.next/standalone/src/lib/session-providers/providerIds.test.ts +337 -0
- package/.next/standalone/src/lib/session-providers/providerIds.ts +176 -0
- package/.next/standalone/src/lib/session-providers/types.ts +131 -0
- package/.next/standalone/src/lib/transform.test.ts +253 -0
- package/.next/standalone/src/lib/transform.ts +96 -37
- package/.next/standalone/src/types/index.ts +23 -17
- package/.next/standalone/src/types/opencodeConfig.ts +1 -9
- package/.next/static/chunks/b3bc362202331708.css +3 -0
- package/.next/static/chunks/c1294e057d8d4681.js +13 -0
- package/.next/trace +1 -1
- package/.next/trace-build +1 -1
- package/.next/types/routes.d.ts +2 -1
- package/.next/types/validator.ts +9 -0
- package/README.md +30 -7
- package/package.json +1 -1
- package/.next/server/chunks/[root-of-the-server]__2f981540._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__2f981540._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__3745b314._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__3745b314._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__6c428a24._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__6c428a24._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__73a00b88._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__73a00b88._.js.map +0 -1
- package/.next/server/chunks/[root-of-the-server]__db285678._.js +0 -3
- package/.next/server/chunks/[root-of-the-server]__db285678._.js.map +0 -1
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__2f981540._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__3745b314._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__6c428a24._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__73a00b88._.js +0 -3
- package/.next/standalone/.next/server/chunks/[root-of-the-server]__db285678._.js +0 -3
- package/.next/standalone/.next/static/chunks/7ac19aaef01f4a03.js +0 -13
- package/.next/standalone/.next/static/chunks/f42202943f6742e5.css +0 -3
- package/.next/static/chunks/7ac19aaef01f4a03.js +0 -13
- package/.next/static/chunks/f42202943f6742e5.css +0 -3
- /package/.next/standalone/.next/static/{Fw2R3y-fHX4B2SWxNy_4X → bsWNvgDS7Zp38Yt9q0DUg}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{Fw2R3y-fHX4B2SWxNy_4X → bsWNvgDS7Zp38Yt9q0DUg}/_clientMiddlewareManifest.json +0 -0
- /package/.next/standalone/.next/static/{Fw2R3y-fHX4B2SWxNy_4X → bsWNvgDS7Zp38Yt9q0DUg}/_ssgManifest.js +0 -0
- /package/.next/static/{Fw2R3y-fHX4B2SWxNy_4X → bsWNvgDS7Zp38Yt9q0DUg}/_buildManifest.js +0 -0
- /package/.next/static/{Fw2R3y-fHX4B2SWxNy_4X → bsWNvgDS7Zp38Yt9q0DUg}/_clientMiddlewareManifest.json +0 -0
- /package/.next/static/{Fw2R3y-fHX4B2SWxNy_4X → bsWNvgDS7Zp38Yt9q0DUg}/_ssgManifest.js +0 -0
|
@@ -13,13 +13,19 @@ vi.mock('@/lib/sessionArchiveOverrides', () => ({
|
|
|
13
13
|
markSessionStickyStatusBlocked: vi.fn(),
|
|
14
14
|
}));
|
|
15
15
|
|
|
16
|
+
vi.mock('@/lib/claudeSessionOverrides', () => ({
|
|
17
|
+
markClaudeSessionArchived: vi.fn(),
|
|
18
|
+
}));
|
|
19
|
+
|
|
16
20
|
import { discoverOpencodePortsWithMeta } from '@/lib/opencodeDiscovery';
|
|
17
21
|
import { listNodeRecords } from '@/lib/nodeRegistry';
|
|
22
|
+
import { markClaudeSessionArchived } from '@/lib/claudeSessionOverrides';
|
|
18
23
|
|
|
19
24
|
import { POST } from './route';
|
|
20
25
|
|
|
21
26
|
const mockDiscoverPortsWithMeta: any = discoverOpencodePortsWithMeta;
|
|
22
27
|
const mockListNodeRecords: any = listNodeRecords;
|
|
28
|
+
const mockMarkClaudeSessionArchived: any = markClaudeSessionArchived;
|
|
23
29
|
|
|
24
30
|
describe('/api/sessions/[id]/archive', () => {
|
|
25
31
|
beforeEach(() => {
|
|
@@ -40,6 +46,23 @@ describe('/api/sessions/[id]/archive', () => {
|
|
|
40
46
|
expect(mockFetch).toHaveBeenCalledWith('http://localhost:7777/session/abc', expect.objectContaining({ method: 'PATCH' }));
|
|
41
47
|
});
|
|
42
48
|
|
|
49
|
+
it('treats UUID-like local ids without claude namespace as opencode sessions', async () => {
|
|
50
|
+
const opencodeUuid = '550e8400-e29b-41d4-a716-446655440000';
|
|
51
|
+
const mockFetch = vi.fn(async () => new Response(JSON.stringify({ error: 'missing' }), { status: 404 }));
|
|
52
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
53
|
+
|
|
54
|
+
const response = await POST(new Request(`http://localhost/api/sessions/local:${opencodeUuid}/archive`, { method: 'POST' }), {
|
|
55
|
+
params: Promise.resolve({ id: `local:${opencodeUuid}` }),
|
|
56
|
+
});
|
|
57
|
+
const data = await response.json();
|
|
58
|
+
|
|
59
|
+
expect(response.status).toBe(404);
|
|
60
|
+
expect(data).toEqual({ error: 'Session not found', reason: 'session_not_found' });
|
|
61
|
+
expect(mockMarkClaudeSessionArchived).not.toHaveBeenCalled();
|
|
62
|
+
expect(mockDiscoverPortsWithMeta).toHaveBeenCalled();
|
|
63
|
+
expect(mockFetch).toHaveBeenCalledWith(`http://localhost:7777/session/${opencodeUuid}`, expect.objectContaining({ method: 'PATCH' }));
|
|
64
|
+
});
|
|
65
|
+
|
|
43
66
|
it('forwards remote archive ids to the matching node endpoint', async () => {
|
|
44
67
|
mockListNodeRecords.mockResolvedValue([
|
|
45
68
|
{
|
|
@@ -98,6 +121,84 @@ describe('/api/sessions/[id]/archive', () => {
|
|
|
98
121
|
expect(data).toEqual({ error: 'Session not found', reason: 'session_not_found' });
|
|
99
122
|
});
|
|
100
123
|
|
|
124
|
+
it('archives Claude sessions through local override storage before any OpenCode execution', async () => {
|
|
125
|
+
const mockFetch = vi.fn();
|
|
126
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
127
|
+
|
|
128
|
+
const response = await POST(new Request('http://localhost/api/sessions/local:claude~550e8400-e29b-41d4-a716-446655440000/archive', { method: 'POST' }), {
|
|
129
|
+
params: Promise.resolve({ id: 'local:claude~550e8400-e29b-41d4-a716-446655440000' }),
|
|
130
|
+
});
|
|
131
|
+
const data = await response.json();
|
|
132
|
+
|
|
133
|
+
expect(response.status).toBe(200);
|
|
134
|
+
expect(data).toEqual({ success: true });
|
|
135
|
+
expect(mockMarkClaudeSessionArchived).toHaveBeenCalledWith('550e8400-e29b-41d4-a716-446655440000');
|
|
136
|
+
expect(mockDiscoverPortsWithMeta).not.toHaveBeenCalled();
|
|
137
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('archives scoped Claude sidechain sessions through local override storage', async () => {
|
|
141
|
+
const scopedSessionId = '550e8400-e29b-41d4-a716-446655440000__agent-a123';
|
|
142
|
+
const mockFetch = vi.fn();
|
|
143
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
144
|
+
|
|
145
|
+
const response = await POST(new Request(`http://localhost/api/sessions/local:${scopedSessionId}/archive`, { method: 'POST' }), {
|
|
146
|
+
params: Promise.resolve({ id: `local:${scopedSessionId}` }),
|
|
147
|
+
});
|
|
148
|
+
const data = await response.json();
|
|
149
|
+
|
|
150
|
+
expect(response.status).toBe(200);
|
|
151
|
+
expect(data).toEqual({ success: true });
|
|
152
|
+
expect(mockMarkClaudeSessionArchived).toHaveBeenCalledWith(scopedSessionId);
|
|
153
|
+
expect(mockDiscoverPortsWithMeta).not.toHaveBeenCalled();
|
|
154
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('rejects remote Claude archive requests before local override or node execution', async () => {
|
|
158
|
+
const mockFetch = vi.fn();
|
|
159
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
160
|
+
|
|
161
|
+
const response = await POST(new Request('http://localhost/api/sessions/node-1:claude~550e8400-e29b-41d4-a716-446655440000/archive', { method: 'POST' }), {
|
|
162
|
+
params: Promise.resolve({ id: 'node-1:claude~550e8400-e29b-41d4-a716-446655440000' }),
|
|
163
|
+
});
|
|
164
|
+
const data = await response.json();
|
|
165
|
+
|
|
166
|
+
expect(response.status).toBe(403);
|
|
167
|
+
expect(data).toEqual({
|
|
168
|
+
error: 'Session action not supported by provider',
|
|
169
|
+
reason: 'provider_capability_unsupported',
|
|
170
|
+
provider: 'claude-code',
|
|
171
|
+
capability: 'archive',
|
|
172
|
+
});
|
|
173
|
+
expect(mockMarkClaudeSessionArchived).not.toHaveBeenCalled();
|
|
174
|
+
expect(mockListNodeRecords).not.toHaveBeenCalled();
|
|
175
|
+
expect(mockDiscoverPortsWithMeta).not.toHaveBeenCalled();
|
|
176
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
it('rejects remote scoped Claude sidechain archive requests before node execution', async () => {
|
|
180
|
+
const scopedSessionId = '550e8400-e29b-41d4-a716-446655440000__agent-a123';
|
|
181
|
+
const mockFetch = vi.fn();
|
|
182
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
183
|
+
|
|
184
|
+
const response = await POST(new Request(`http://localhost/api/sessions/node-1:${scopedSessionId}/archive`, { method: 'POST' }), {
|
|
185
|
+
params: Promise.resolve({ id: `node-1:${scopedSessionId}` }),
|
|
186
|
+
});
|
|
187
|
+
const data = await response.json();
|
|
188
|
+
|
|
189
|
+
expect(response.status).toBe(403);
|
|
190
|
+
expect(data).toEqual({
|
|
191
|
+
error: 'Session action not supported by provider',
|
|
192
|
+
reason: 'provider_capability_unsupported',
|
|
193
|
+
provider: 'claude-code',
|
|
194
|
+
capability: 'archive',
|
|
195
|
+
});
|
|
196
|
+
expect(mockMarkClaudeSessionArchived).not.toHaveBeenCalled();
|
|
197
|
+
expect(mockListNodeRecords).not.toHaveBeenCalled();
|
|
198
|
+
expect(mockDiscoverPortsWithMeta).not.toHaveBeenCalled();
|
|
199
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
200
|
+
});
|
|
201
|
+
|
|
101
202
|
it('does not misclassify non-404 local archive failures as session_not_found', async () => {
|
|
102
203
|
const mockFetch = vi.fn(async () => new Response(JSON.stringify({ error: 'boom' }), { status: 500 }));
|
|
103
204
|
vi.stubGlobal('fetch', mockFetch);
|
|
@@ -1,11 +1,13 @@
|
|
|
1
1
|
import { discoverOpencodePortsWithMeta } from '@/lib/opencodeDiscovery';
|
|
2
|
-
import { parseActionSessionReference, resolveLocalActionSessionId } from '@/lib/hostIdentity';
|
|
2
|
+
import { ActionSessionReference, parseActionSessionReference, resolveLocalActionSessionId } from '@/lib/hostIdentity';
|
|
3
3
|
import { listNodeRecords } from '@/lib/nodeRegistry';
|
|
4
4
|
import { createNodeRequestHeaders } from '@/lib/nodeProtocol';
|
|
5
|
+
import { detectProviderFromRawId, extractProviderRawId, getDefaultProviderContext } from '@/lib/session-providers/providerIds';
|
|
5
6
|
import {
|
|
6
|
-
|
|
7
|
-
|
|
7
|
+
clearSessionForceUnarchived,
|
|
8
|
+
markSessionStickyStatusBlocked,
|
|
8
9
|
} from '@/lib/sessionArchiveOverrides';
|
|
10
|
+
import { markClaudeSessionArchived } from '@/lib/claudeSessionOverrides';
|
|
9
11
|
|
|
10
12
|
const REMOTE_NODE_ACTION_TIMEOUT_MS = 5_000;
|
|
11
13
|
|
|
@@ -23,6 +25,20 @@ function createSessionNotFoundResponse() {
|
|
|
23
25
|
);
|
|
24
26
|
}
|
|
25
27
|
|
|
28
|
+
function createUnsupportedCapabilityResponse(capability: 'archive', sessionId: string) {
|
|
29
|
+
const provider = detectProviderFromRawId(sessionId);
|
|
30
|
+
|
|
31
|
+
return Response.json(
|
|
32
|
+
{
|
|
33
|
+
error: 'Session action not supported by provider',
|
|
34
|
+
reason: 'provider_capability_unsupported',
|
|
35
|
+
provider,
|
|
36
|
+
capability,
|
|
37
|
+
},
|
|
38
|
+
{ status: 403 }
|
|
39
|
+
);
|
|
40
|
+
}
|
|
41
|
+
|
|
26
42
|
function createArchiveFailureResponse(
|
|
27
43
|
status: number,
|
|
28
44
|
message?: string,
|
|
@@ -87,17 +103,36 @@ async function forwardRemoteArchive(hostId: string, sessionId: string): Promise<
|
|
|
87
103
|
|
|
88
104
|
export async function POST(_: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
89
105
|
const { id } = await params;
|
|
90
|
-
|
|
106
|
+
let actionTarget: ActionSessionReference;
|
|
91
107
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
108
|
+
try {
|
|
109
|
+
actionTarget = parseActionSessionReference(id);
|
|
110
|
+
} catch {
|
|
111
|
+
return createInvalidActionSessionIdResponse();
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
const provider = detectProviderFromRawId(actionTarget.sessionId);
|
|
115
|
+
if (!getDefaultProviderContext(provider).capabilities.archive) {
|
|
116
|
+
return createUnsupportedCapabilityResponse('archive', actionTarget.sessionId);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (provider === 'claude-code' && actionTarget.isRemote) {
|
|
120
|
+
return createUnsupportedCapabilityResponse('archive', actionTarget.sessionId);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if (provider === 'claude-code') {
|
|
124
|
+
await markClaudeSessionArchived(extractProviderRawId(actionTarget.sessionId));
|
|
125
|
+
const localSessionId = resolveLocalActionSessionId(id);
|
|
126
|
+
if (localSessionId) {
|
|
127
|
+
clearSessionForceUnarchived(localSessionId);
|
|
128
|
+
markSessionStickyStatusBlocked(localSessionId);
|
|
100
129
|
}
|
|
130
|
+
return Response.json({ success: true });
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
const sessionId = resolveLocalActionSessionId(id);
|
|
134
|
+
if (!sessionId && actionTarget.isRemote) {
|
|
135
|
+
return forwardRemoteArchive(actionTarget.hostId, actionTarget.sessionId);
|
|
101
136
|
}
|
|
102
137
|
|
|
103
138
|
if (!sessionId) {
|
|
@@ -17,15 +17,21 @@ vi.mock('@/lib/sessionArchiveOverrides', () => ({
|
|
|
17
17
|
clearSessionStickyStatusBlocked: vi.fn(),
|
|
18
18
|
}));
|
|
19
19
|
|
|
20
|
+
vi.mock('@/lib/claudeSessionOverrides', () => ({
|
|
21
|
+
markClaudeSessionDeleted: vi.fn(),
|
|
22
|
+
}));
|
|
23
|
+
|
|
20
24
|
import { createOpencodeClient } from '@opencode-ai/sdk';
|
|
21
25
|
import { discoverOpencodePortsWithMeta } from '@/lib/opencodeDiscovery';
|
|
22
26
|
import { listNodeRecords } from '@/lib/nodeRegistry';
|
|
27
|
+
import { markClaudeSessionDeleted } from '@/lib/claudeSessionOverrides';
|
|
23
28
|
|
|
24
29
|
import { POST } from './route';
|
|
25
30
|
|
|
26
31
|
const mockCreateOpencodeClient: any = createOpencodeClient;
|
|
27
32
|
const mockDiscoverPortsWithMeta: any = discoverOpencodePortsWithMeta;
|
|
28
33
|
const mockListNodeRecords: any = listNodeRecords;
|
|
34
|
+
const mockMarkClaudeSessionDeleted: any = markClaudeSessionDeleted;
|
|
29
35
|
const mockSessionDelete = vi.fn();
|
|
30
36
|
|
|
31
37
|
describe('/api/sessions/[id]/delete', () => {
|
|
@@ -110,4 +116,90 @@ describe('/api/sessions/[id]/delete', () => {
|
|
|
110
116
|
message: '404 not found',
|
|
111
117
|
});
|
|
112
118
|
});
|
|
119
|
+
|
|
120
|
+
it('deletes Claude sessions through local override storage before any OpenCode execution', async () => {
|
|
121
|
+
const mockFetch = vi.fn();
|
|
122
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
123
|
+
|
|
124
|
+
const response = await POST(new Request('http://localhost/api/sessions/local:claude~550e8400-e29b-41d4-a716-446655440000/delete', { method: 'POST' }), {
|
|
125
|
+
params: Promise.resolve({ id: 'local:claude~550e8400-e29b-41d4-a716-446655440000' }),
|
|
126
|
+
});
|
|
127
|
+
const data = await response.json();
|
|
128
|
+
|
|
129
|
+
expect(response.status).toBe(200);
|
|
130
|
+
expect(data).toEqual({ success: true });
|
|
131
|
+
expect(mockMarkClaudeSessionDeleted).toHaveBeenCalledWith('550e8400-e29b-41d4-a716-446655440000');
|
|
132
|
+
expect(mockDiscoverPortsWithMeta).not.toHaveBeenCalled();
|
|
133
|
+
expect(mockCreateOpencodeClient).not.toHaveBeenCalled();
|
|
134
|
+
expect(mockSessionDelete).not.toHaveBeenCalled();
|
|
135
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
it('deletes scoped Claude sidechain sessions through local override storage', async () => {
|
|
139
|
+
const scopedSessionId = '550e8400-e29b-41d4-a716-446655440000__agent-a123';
|
|
140
|
+
const mockFetch = vi.fn();
|
|
141
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
142
|
+
|
|
143
|
+
const response = await POST(new Request(`http://localhost/api/sessions/local:${scopedSessionId}/delete`, { method: 'POST' }), {
|
|
144
|
+
params: Promise.resolve({ id: `local:${scopedSessionId}` }),
|
|
145
|
+
});
|
|
146
|
+
const data = await response.json();
|
|
147
|
+
|
|
148
|
+
expect(response.status).toBe(200);
|
|
149
|
+
expect(data).toEqual({ success: true });
|
|
150
|
+
expect(mockMarkClaudeSessionDeleted).toHaveBeenCalledWith(scopedSessionId);
|
|
151
|
+
expect(mockDiscoverPortsWithMeta).not.toHaveBeenCalled();
|
|
152
|
+
expect(mockCreateOpencodeClient).not.toHaveBeenCalled();
|
|
153
|
+
expect(mockSessionDelete).not.toHaveBeenCalled();
|
|
154
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('rejects remote Claude delete requests before local override or node execution', async () => {
|
|
158
|
+
const mockFetch = vi.fn();
|
|
159
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
160
|
+
|
|
161
|
+
const response = await POST(new Request('http://localhost/api/sessions/node-1:claude~550e8400-e29b-41d4-a716-446655440000/delete', { method: 'POST' }), {
|
|
162
|
+
params: Promise.resolve({ id: 'node-1:claude~550e8400-e29b-41d4-a716-446655440000' }),
|
|
163
|
+
});
|
|
164
|
+
const data = await response.json();
|
|
165
|
+
|
|
166
|
+
expect(response.status).toBe(403);
|
|
167
|
+
expect(data).toEqual({
|
|
168
|
+
error: 'Session action not supported by provider',
|
|
169
|
+
reason: 'provider_capability_unsupported',
|
|
170
|
+
provider: 'claude-code',
|
|
171
|
+
capability: 'delete',
|
|
172
|
+
});
|
|
173
|
+
expect(mockMarkClaudeSessionDeleted).not.toHaveBeenCalled();
|
|
174
|
+
expect(mockListNodeRecords).not.toHaveBeenCalled();
|
|
175
|
+
expect(mockDiscoverPortsWithMeta).not.toHaveBeenCalled();
|
|
176
|
+
expect(mockCreateOpencodeClient).not.toHaveBeenCalled();
|
|
177
|
+
expect(mockSessionDelete).not.toHaveBeenCalled();
|
|
178
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
it('rejects remote scoped Claude sidechain deletes before node execution', async () => {
|
|
182
|
+
const scopedSessionId = '550e8400-e29b-41d4-a716-446655440000__agent-a123';
|
|
183
|
+
const mockFetch = vi.fn();
|
|
184
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
185
|
+
|
|
186
|
+
const response = await POST(new Request(`http://localhost/api/sessions/node-1:${scopedSessionId}/delete`, { method: 'POST' }), {
|
|
187
|
+
params: Promise.resolve({ id: `node-1:${scopedSessionId}` }),
|
|
188
|
+
});
|
|
189
|
+
const data = await response.json();
|
|
190
|
+
|
|
191
|
+
expect(response.status).toBe(403);
|
|
192
|
+
expect(data).toEqual({
|
|
193
|
+
error: 'Session action not supported by provider',
|
|
194
|
+
reason: 'provider_capability_unsupported',
|
|
195
|
+
provider: 'claude-code',
|
|
196
|
+
capability: 'delete',
|
|
197
|
+
});
|
|
198
|
+
expect(mockMarkClaudeSessionDeleted).not.toHaveBeenCalled();
|
|
199
|
+
expect(mockListNodeRecords).not.toHaveBeenCalled();
|
|
200
|
+
expect(mockDiscoverPortsWithMeta).not.toHaveBeenCalled();
|
|
201
|
+
expect(mockCreateOpencodeClient).not.toHaveBeenCalled();
|
|
202
|
+
expect(mockSessionDelete).not.toHaveBeenCalled();
|
|
203
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
204
|
+
});
|
|
113
205
|
});
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { createOpencodeClient } from '@opencode-ai/sdk';
|
|
2
2
|
import { discoverOpencodePortsWithMeta } from '@/lib/opencodeDiscovery';
|
|
3
|
-
import { parseActionSessionReference, resolveLocalActionSessionId } from '@/lib/hostIdentity';
|
|
3
|
+
import { ActionSessionReference, parseActionSessionReference, resolveLocalActionSessionId } from '@/lib/hostIdentity';
|
|
4
4
|
import { listNodeRecords } from '@/lib/nodeRegistry';
|
|
5
5
|
import { createNodeRequestHeaders } from '@/lib/nodeProtocol';
|
|
6
|
+
import { detectProviderFromRawId, extractProviderRawId, getDefaultProviderContext } from '@/lib/session-providers/providerIds';
|
|
6
7
|
import {
|
|
7
8
|
clearSessionForceUnarchived,
|
|
8
9
|
clearSessionStickyStatusBlocked,
|
|
9
10
|
} from '@/lib/sessionArchiveOverrides';
|
|
11
|
+
import { markClaudeSessionDeleted } from '@/lib/claudeSessionOverrides';
|
|
10
12
|
|
|
11
13
|
const REMOTE_NODE_ACTION_TIMEOUT_MS = 5_000;
|
|
12
14
|
|
|
@@ -28,6 +30,20 @@ function createSessionNotFoundResponse(message?: string) {
|
|
|
28
30
|
);
|
|
29
31
|
}
|
|
30
32
|
|
|
33
|
+
function createUnsupportedCapabilityResponse(capability: 'delete', sessionId: string) {
|
|
34
|
+
const provider = detectProviderFromRawId(sessionId);
|
|
35
|
+
|
|
36
|
+
return Response.json(
|
|
37
|
+
{
|
|
38
|
+
error: 'Session action not supported by provider',
|
|
39
|
+
reason: 'provider_capability_unsupported',
|
|
40
|
+
provider,
|
|
41
|
+
capability,
|
|
42
|
+
},
|
|
43
|
+
{ status: 403 }
|
|
44
|
+
);
|
|
45
|
+
}
|
|
46
|
+
|
|
31
47
|
async function forwardRemoteDelete(hostId: string, sessionId: string): Promise<Response> {
|
|
32
48
|
const nodeRecords = await listNodeRecords();
|
|
33
49
|
const nodeRecord = nodeRecords.find((node) => node.nodeId === hostId);
|
|
@@ -77,17 +93,36 @@ async function forwardRemoteDelete(hostId: string, sessionId: string): Promise<R
|
|
|
77
93
|
|
|
78
94
|
export async function POST(_: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
79
95
|
const { id } = await params;
|
|
80
|
-
|
|
96
|
+
let actionTarget: ActionSessionReference;
|
|
81
97
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
98
|
+
try {
|
|
99
|
+
actionTarget = parseActionSessionReference(id);
|
|
100
|
+
} catch {
|
|
101
|
+
return createInvalidActionSessionIdResponse();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const provider = detectProviderFromRawId(actionTarget.sessionId);
|
|
105
|
+
if (!getDefaultProviderContext(provider).capabilities.delete) {
|
|
106
|
+
return createUnsupportedCapabilityResponse('delete', actionTarget.sessionId);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (provider === 'claude-code' && actionTarget.isRemote) {
|
|
110
|
+
return createUnsupportedCapabilityResponse('delete', actionTarget.sessionId);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if (provider === 'claude-code') {
|
|
114
|
+
await markClaudeSessionDeleted(extractProviderRawId(actionTarget.sessionId));
|
|
115
|
+
const localSessionId = resolveLocalActionSessionId(id);
|
|
116
|
+
if (localSessionId) {
|
|
117
|
+
clearSessionForceUnarchived(localSessionId);
|
|
118
|
+
clearSessionStickyStatusBlocked(localSessionId);
|
|
90
119
|
}
|
|
120
|
+
return Response.json({ success: true });
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const sessionId = resolveLocalActionSessionId(id);
|
|
124
|
+
if (!sessionId && actionTarget.isRemote) {
|
|
125
|
+
return forwardRemoteDelete(actionTarget.hostId, actionTarget.sessionId);
|
|
91
126
|
}
|
|
92
127
|
|
|
93
128
|
if (!sessionId) {
|
|
@@ -215,4 +215,78 @@ describe('/api/sessions/[id]/open-editor', () => {
|
|
|
215
215
|
expect(response.status).toBe(400);
|
|
216
216
|
expect(data).toEqual({ error: 'Invalid action session id', reason: 'invalid_action_session_id' });
|
|
217
217
|
});
|
|
218
|
+
|
|
219
|
+
it('rejects unsupported Claude open-editor requests before remote node execution', async () => {
|
|
220
|
+
const mockFetch = vi.fn();
|
|
221
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
222
|
+
|
|
223
|
+
const response = await POST(
|
|
224
|
+
new Request('http://localhost/api/sessions/node-1:claude~550e8400-e29b-41d4-a716-446655440000/open-editor', {
|
|
225
|
+
method: 'POST',
|
|
226
|
+
headers: { 'Content-Type': 'application/json' },
|
|
227
|
+
body: JSON.stringify({ tool: 'vscode' }),
|
|
228
|
+
}),
|
|
229
|
+
{ params: Promise.resolve({ id: 'node-1:claude~550e8400-e29b-41d4-a716-446655440000' }) }
|
|
230
|
+
);
|
|
231
|
+
const data = await response.json();
|
|
232
|
+
|
|
233
|
+
expect(response.status).toBe(403);
|
|
234
|
+
expect(data).toEqual({
|
|
235
|
+
error: 'Session action not supported by provider',
|
|
236
|
+
reason: 'provider_capability_unsupported',
|
|
237
|
+
provider: 'claude-code',
|
|
238
|
+
capability: 'openEditor',
|
|
239
|
+
});
|
|
240
|
+
expect(mockListNodeRecords).not.toHaveBeenCalled();
|
|
241
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('rejects unsupported scoped Claude sidechain open-editor requests before remote node execution', async () => {
|
|
245
|
+
const scopedSessionId = '550e8400-e29b-41d4-a716-446655440000__agent-a123';
|
|
246
|
+
const mockFetch = vi.fn();
|
|
247
|
+
vi.stubGlobal('fetch', mockFetch);
|
|
248
|
+
|
|
249
|
+
const response = await POST(
|
|
250
|
+
new Request(`http://localhost/api/sessions/node-1:${scopedSessionId}/open-editor`, {
|
|
251
|
+
method: 'POST',
|
|
252
|
+
headers: { 'Content-Type': 'application/json' },
|
|
253
|
+
body: JSON.stringify({ tool: 'vscode' }),
|
|
254
|
+
}),
|
|
255
|
+
{ params: Promise.resolve({ id: `node-1:${scopedSessionId}` }) }
|
|
256
|
+
);
|
|
257
|
+
const data = await response.json();
|
|
258
|
+
|
|
259
|
+
expect(response.status).toBe(403);
|
|
260
|
+
expect(data).toEqual({
|
|
261
|
+
error: 'Session action not supported by provider',
|
|
262
|
+
reason: 'provider_capability_unsupported',
|
|
263
|
+
provider: 'claude-code',
|
|
264
|
+
capability: 'openEditor',
|
|
265
|
+
});
|
|
266
|
+
expect(mockListNodeRecords).not.toHaveBeenCalled();
|
|
267
|
+
expect(mockFetch).not.toHaveBeenCalled();
|
|
268
|
+
});
|
|
269
|
+
|
|
270
|
+
it('rejects local scoped Claude sidechain open-editor requests as unsupported capability', async () => {
|
|
271
|
+
const scopedSessionId = '550e8400-e29b-41d4-a716-446655440000__agent-a123';
|
|
272
|
+
|
|
273
|
+
const response = await POST(
|
|
274
|
+
new Request(`http://localhost/api/sessions/local:${scopedSessionId}/open-editor`, {
|
|
275
|
+
method: 'POST',
|
|
276
|
+
headers: { 'Content-Type': 'application/json' },
|
|
277
|
+
body: JSON.stringify({ tool: 'vscode' }),
|
|
278
|
+
}),
|
|
279
|
+
{ params: Promise.resolve({ id: `local:${scopedSessionId}` }) }
|
|
280
|
+
);
|
|
281
|
+
const data = await response.json();
|
|
282
|
+
|
|
283
|
+
expect(response.status).toBe(403);
|
|
284
|
+
expect(data).toEqual({
|
|
285
|
+
error: 'Session action not supported by provider',
|
|
286
|
+
reason: 'provider_capability_unsupported',
|
|
287
|
+
provider: 'claude-code',
|
|
288
|
+
capability: 'openEditor',
|
|
289
|
+
});
|
|
290
|
+
expect(mockListNodeRecords).not.toHaveBeenCalled();
|
|
291
|
+
});
|
|
218
292
|
});
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { parseActionSessionReference } from '@/lib/hostIdentity';
|
|
1
|
+
import { ActionSessionReference, parseActionSessionReference } from '@/lib/hostIdentity';
|
|
2
2
|
import { listNodeRecords } from '@/lib/nodeRegistry';
|
|
3
3
|
import { createNodeRequestHeaders } from '@/lib/nodeProtocol';
|
|
4
|
+
import { detectProviderFromRawId, extractProviderRawId, getDefaultProviderContext } from '@/lib/session-providers/providerIds';
|
|
4
5
|
|
|
5
6
|
const REMOTE_NODE_ACTION_TIMEOUT_MS = 5_000;
|
|
6
7
|
|
|
@@ -18,16 +19,35 @@ function createSessionNotFoundResponse() {
|
|
|
18
19
|
);
|
|
19
20
|
}
|
|
20
21
|
|
|
22
|
+
function createUnsupportedCapabilityResponse(capability: 'openEditor', sessionId: string) {
|
|
23
|
+
const provider = detectProviderFromRawId(sessionId);
|
|
24
|
+
|
|
25
|
+
return Response.json(
|
|
26
|
+
{
|
|
27
|
+
error: 'Session action not supported by provider',
|
|
28
|
+
reason: 'provider_capability_unsupported',
|
|
29
|
+
provider,
|
|
30
|
+
capability,
|
|
31
|
+
},
|
|
32
|
+
{ status: 403 }
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
21
36
|
export async function POST(request: Request, { params }: { params: Promise<{ id: string }> }) {
|
|
22
37
|
const { id } = await params;
|
|
23
38
|
|
|
24
|
-
let actionTarget;
|
|
39
|
+
let actionTarget: ActionSessionReference;
|
|
25
40
|
try {
|
|
26
41
|
actionTarget = parseActionSessionReference(id);
|
|
27
42
|
} catch {
|
|
28
43
|
return createInvalidActionSessionIdResponse();
|
|
29
44
|
}
|
|
30
45
|
|
|
46
|
+
const provider = detectProviderFromRawId(actionTarget.sessionId);
|
|
47
|
+
if (!getDefaultProviderContext(provider).capabilities.openEditor) {
|
|
48
|
+
return createUnsupportedCapabilityResponse('openEditor', actionTarget.sessionId);
|
|
49
|
+
}
|
|
50
|
+
|
|
31
51
|
if (!actionTarget.isRemote) {
|
|
32
52
|
return createInvalidActionSessionIdResponse();
|
|
33
53
|
}
|