@geminilight/mindos 0.6.61 → 0.6.65
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/_standalone/.antigravity/mcp_config.json +14 -0
- package/_standalone/.mindos-build-version +1 -1
- package/_standalone/.next/BUILD_ID +1 -1
- package/_standalone/.next/app-path-routes-manifest.json +23 -23
- package/_standalone/.next/build-manifest.json +2 -2
- package/_standalone/.next/cache/.previewinfo +1 -1
- package/_standalone/.next/cache/.rscinfo +1 -1
- package/_standalone/.next/cache/config.json +3 -3
- package/_standalone/.next/prerender-manifest.json +3 -3
- package/_standalone/.next/server/app/.well-known/agent-card.json/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/_global-error.html +2 -2
- package/_standalone/.next/server/app/_global-error.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/_standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/_standalone/.next/server/app/_not-found/page.js +1 -1
- package/_standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/[agentKey]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/agents/page.js +1 -1
- package/_standalone/.next/server/app/agents/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/agents/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/delegations/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/discover/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/a2a/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/config/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/detect/route.js +1 -1
- package/_standalone/.next/server/app/api/acp/detect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/registry/route.js +1 -1
- package/_standalone/.next/server/app/api/acp/registry/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/acp/session/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agent-activity/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/copy-skill/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/agents/copy-skill/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/custom/detect/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/agents/custom/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask/route.js +53 -47
- package/_standalone/.next/server/app/api/ask/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/ask/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/ask-sessions/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/auth/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/backlinks/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/backlinks/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/bootstrap/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/changes/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/changes/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/export/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/export/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/extract-pdf/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/import/route.js +1 -1
- package/_standalone/.next/server/app/api/file/import/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/file/import/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/raw/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/file/raw/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/file/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/file/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/files/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/git/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/git/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/graph/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/graph/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/health/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/inbox/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/inbox/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/init/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/init/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/agents/route.js +1 -1
- package/_standalone/.next/server/app/api/mcp/agents/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/mcp/agents/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/install-skill/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/mcp/uninstall/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/monitoring/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/monitoring/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/recent-files/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/recent-files/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/restart/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/search/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/search/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/list-models/route.js +1 -1
- package/_standalone/.next/server/app/api/settings/list-models/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/reset-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/route.js +1 -1
- package/_standalone/.next/server/app/api/settings/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/settings/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/settings/test-key/route.js +1 -1
- package/_standalone/.next/server/app/api/settings/test-key/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-path/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/check-port/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/generate-token/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/ls/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/setup/route.js +1 -1
- package/_standalone/.next/server/app/api/setup/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/skills/route.js +1 -1
- package/_standalone/.next/server/app/api/skills/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/sync/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/tree-version/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/tree-version/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/uninstall/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-check/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/update-status/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/api/workflows/route.js.nft.json +1 -1
- package/_standalone/.next/server/app/api/workflows/route_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/changes/page.js +1 -1
- package/_standalone/.next/server/app/changes/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/changes/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page.js +2 -2
- package/_standalone/.next/server/app/echo/[segment]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/[segment]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/echo/page.js +1 -1
- package/_standalone/.next/server/app/echo/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/echo/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/explore/page.js +1 -1
- package/_standalone/.next/server/app/explore/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/explore/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/help/page.js +1 -1
- package/_standalone/.next/server/app/help/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/help/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/inbox/history/page.js +1 -1
- package/_standalone/.next/server/app/inbox/history/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/inbox/history/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/login/page.js +1 -1
- package/_standalone/.next/server/app/login/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/login/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/page.js +1 -1
- package/_standalone/.next/server/app/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/setup/page.js +2 -2
- package/_standalone/.next/server/app/setup/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/trash/page.js +3 -3
- package/_standalone/.next/server/app/trash/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/trash/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/view/[...path]/page.js +2 -2
- package/_standalone/.next/server/app/view/[...path]/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/view/[...path]/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app/wiki/page.js +1 -1
- package/_standalone/.next/server/app/wiki/page.js.nft.json +1 -1
- package/_standalone/.next/server/app/wiki/page_client-reference-manifest.js +1 -1
- package/_standalone/.next/server/app-paths-manifest.json +23 -23
- package/_standalone/.next/server/chunks/122.js +222 -0
- package/_standalone/.next/server/chunks/1550.js +1 -1
- package/_standalone/.next/server/chunks/1750.js +1 -1
- package/_standalone/.next/server/chunks/3113.js +52 -0
- package/_standalone/.next/server/chunks/6539.js +1 -1
- package/_standalone/.next/server/chunks/8388.js +3 -3
- package/_standalone/.next/server/chunks/953.js +3 -3
- package/_standalone/.next/server/pages/500.html +2 -2
- package/_standalone/.next/server/server-reference-manifest.js +1 -1
- package/_standalone/.next/server/server-reference-manifest.json +1 -1
- package/_standalone/.next/static/chunks/1001-99da82ec8d8c136f.js +1 -0
- package/_standalone/.next/static/chunks/1088-77544af0a50cb7a4.js +1 -0
- package/_standalone/.next/static/chunks/1467-87dde7eed498806f.js +1 -0
- package/_standalone/.next/static/chunks/5149-4d828886dda479fa.js +1 -0
- package/_standalone/.next/static/chunks/5581-c671163a2fe1b312.js +29 -0
- package/_standalone/.next/static/chunks/{7266-bb7be1128eccd48e.js → 5718-3837c3210a0e175f.js} +2 -2
- package/_standalone/.next/static/chunks/6636-53238eff89503f03.js +6 -0
- package/_standalone/.next/static/chunks/6757-1c1a89720fdda8f0.js +1 -0
- package/_standalone/.next/static/chunks/7129-20e9d2463a9da646.js +1 -0
- package/_standalone/.next/static/chunks/7294-cac25d97869afadc.js +1 -0
- package/_standalone/.next/static/chunks/8225-21e5cebc3731ddf0.js +1 -0
- package/_standalone/.next/static/chunks/8520-b51810e66293ceb8.js +22 -0
- package/_standalone/.next/static/chunks/9207-dc9c31b351a2ed78.js +1 -0
- package/_standalone/.next/static/chunks/app/agents/[agentKey]/page-2f5cf97e03dc1cc9.js +1 -0
- package/_standalone/.next/static/chunks/app/agents/page-50eac58d511dcc6e.js +1 -0
- package/_standalone/.next/static/chunks/app/echo/[segment]/page-2a00f4686adf3885.js +11 -0
- package/_standalone/.next/static/chunks/app/layout-2cb7a6602d2e5d5f.js +168 -0
- package/_standalone/.next/static/chunks/app/{page-6a1f8d21c12b829e.js → page-5ab911b2226f6ff7.js} +1 -1
- package/_standalone/.next/static/chunks/app/setup/page-907b7c57fad2292b.js +1 -0
- package/_standalone/.next/static/chunks/app/trash/page-11a511b065ea84c2.js +1 -0
- package/_standalone/.next/static/chunks/app/view/[...path]/page-26e47dd4c533a58c.js +12 -0
- package/_standalone/.next/static/chunks/app/wiki/page-dce495b9048022fb.js +1 -0
- package/_standalone/.next/static/css/67e7918f5ed7d147.css +1 -0
- package/_standalone/.next/trace +65 -65
- package/_standalone/__tests__/acp/registry.test.ts +30 -20
- package/_standalone/__tests__/api/ask-attachments.test.ts +194 -0
- package/_standalone/__tests__/api/mcp-install.test.ts +49 -2
- package/_standalone/__tests__/api/settings.test.ts +16 -12
- package/_standalone/__tests__/api/setup.test.ts +11 -9
- package/_standalone/__tests__/api/test-key.test.ts +0 -10
- package/_standalone/__tests__/components/UpdateToast.test.ts +344 -0
- package/_standalone/__tests__/core/context.test.ts +48 -426
- package/_standalone/__tests__/lib/pi-skills.test.ts +4 -4
- package/_standalone/__tests__/lib/settings-ai-client.test.ts +32 -12
- package/_standalone/__tests__/setup.ts +5 -5
- package/_standalone/app/globals.css +4 -4
- package/_standalone/components/ActivityBar.tsx +17 -6
- package/_standalone/components/Panel.tsx +24 -6
- package/_standalone/components/SidebarLayout.tsx +36 -8
- package/_standalone/components/agents/AgentsMcpSection.tsx +2 -2
- package/_standalone/components/agents/AgentsOverviewSection.tsx +5 -1
- package/_standalone/components/agents/AgentsPanelA2aTab.tsx +173 -113
- package/_standalone/components/agents/AgentsSkillsSection.tsx +2 -2
- package/_standalone/components/ask/AskContent.tsx +83 -44
- package/_standalone/components/ask/AskHeader.tsx +8 -1
- package/_standalone/components/ask/MessageList.tsx +37 -3
- package/_standalone/components/ask/ProviderModelCapsule.tsx +444 -174
- package/_standalone/components/home/InboxSection.tsx +25 -25
- package/_standalone/components/settings/AiTab.tsx +353 -298
- package/_standalone/components/settings/CustomProviderFields.tsx +121 -0
- package/_standalone/components/settings/CustomProvidersCard.tsx +154 -0
- package/_standalone/components/settings/KnowledgeTab.tsx +6 -20
- package/_standalone/components/settings/McpAgentInstall.tsx +7 -2
- package/_standalone/components/settings/Primitives.tsx +48 -104
- package/_standalone/components/settings/ProviderModal.tsx +87 -0
- package/_standalone/components/settings/SettingsContent.tsx +2 -5
- package/_standalone/components/settings/TestButton.tsx +64 -0
- package/_standalone/components/settings/types.ts +3 -9
- package/_standalone/components/settings/useCustomProviderForm.ts +132 -0
- package/_standalone/components/setup/StepAI.tsx +12 -5
- package/_standalone/components/shared/ModelInput.tsx +220 -0
- package/_standalone/components/shared/ProviderSelect.tsx +126 -36
- package/_standalone/hooks/useAskChat.ts +100 -13
- package/_standalone/hooks/useAskPanel.ts +17 -1
- package/_standalone/lib/settings-ai-client.ts +17 -8
- package/_standalone/tsconfig.tsbuildinfo +1 -1
- package/app/.antigravity/mcp_config.json +14 -0
- package/app/app/api/ask/route.ts +154 -45
- package/app/app/api/mcp/agents/route.ts +3 -3
- package/app/app/api/settings/list-models/route.ts +36 -9
- package/app/app/api/settings/route.ts +14 -42
- package/app/app/api/settings/test-key/route.ts +78 -2
- package/app/app/api/setup/route.ts +36 -18
- package/app/app/api/skills/route.ts +1 -1
- package/app/app/globals.css +4 -4
- package/app/app/layout.tsx +5 -3
- package/app/app/view/[...path]/page.tsx +5 -0
- package/app/components/ActivityBar.tsx +17 -6
- package/app/components/HomeContent.tsx +11 -0
- package/app/components/InboxView.tsx +656 -0
- package/app/components/Panel.tsx +24 -6
- package/app/components/SidebarLayout.tsx +36 -8
- package/app/components/UpdateToast.tsx +255 -0
- package/app/components/agents/AgentDetailContent.tsx +8 -8
- package/app/components/agents/AgentsMcpSection.tsx +2 -2
- package/app/components/agents/AgentsOverviewSection.tsx +5 -1
- package/app/components/agents/AgentsPanelA2aTab.tsx +173 -113
- package/app/components/agents/AgentsSkillsSection.tsx +2 -2
- package/app/components/ask/AskContent.tsx +83 -44
- package/app/components/ask/AskHeader.tsx +8 -1
- package/app/components/ask/MessageList.tsx +37 -3
- package/app/components/ask/ProviderModelCapsule.tsx +444 -174
- package/app/components/home/InboxSection.tsx +25 -25
- package/app/components/settings/AiTab.tsx +353 -298
- package/app/components/settings/CustomProviderFields.tsx +121 -0
- package/app/components/settings/CustomProvidersCard.tsx +154 -0
- package/app/components/settings/KnowledgeTab.tsx +6 -20
- package/app/components/settings/McpAgentInstall.tsx +7 -2
- package/app/components/settings/Primitives.tsx +48 -104
- package/app/components/settings/ProviderModal.tsx +87 -0
- package/app/components/settings/SettingsContent.tsx +2 -5
- package/app/components/settings/TestButton.tsx +64 -0
- package/app/components/settings/types.ts +3 -9
- package/app/components/settings/useCustomProviderForm.ts +132 -0
- package/app/components/setup/StepAI.tsx +12 -5
- package/app/components/shared/ModelInput.tsx +220 -0
- package/app/components/shared/ProviderSelect.tsx +126 -36
- package/app/hooks/useAskChat.ts +100 -13
- package/app/hooks/useAskPanel.ts +17 -1
- package/app/lib/acp/registry.ts +92 -10
- package/app/lib/agent/context.ts +65 -0
- package/app/lib/agent/providers.ts +25 -0
- package/app/lib/agent/tools.ts +1 -1
- package/app/lib/custom-endpoints.ts +160 -0
- package/app/lib/fs.ts +8 -1
- package/app/lib/i18n/modules/ai-chat.ts +6 -0
- package/app/lib/i18n/modules/knowledge.ts +16 -0
- package/app/lib/i18n/modules/onboarding.ts +4 -0
- package/app/lib/i18n/modules/settings.ts +88 -2
- package/app/lib/mcp-agents.ts +11 -0
- package/app/lib/pi-integration/skills.ts +16 -4
- package/app/lib/settings-ai-client.ts +17 -8
- package/app/lib/settings.ts +68 -72
- package/app/lib/types.ts +4 -0
- package/bin/lib/mcp-agents.js +11 -0
- package/bin/lib/mcp-install.js +71 -7
- package/package.json +1 -1
- package/_standalone/.next/server/chunks/530.js +0 -218
- package/_standalone/.next/server/chunks/8955.js +0 -52
- package/_standalone/.next/static/chunks/1369-7d0ac5d1564eed1e.js +0 -1
- package/_standalone/.next/static/chunks/3427-2e61a5df1f5e55fb.js +0 -1
- package/_standalone/.next/static/chunks/5581-0c700c20718bd916.js +0 -29
- package/_standalone/.next/static/chunks/6297-085daa21037d5f81.js +0 -1
- package/_standalone/.next/static/chunks/6636-9bbc90fb3b8731fe.js +0 -6
- package/_standalone/.next/static/chunks/7637-904b0a381dc3ec02.js +0 -1
- package/_standalone/.next/static/chunks/8520-76d1b05072178b43.js +0 -22
- package/_standalone/.next/static/chunks/8658-16ff58b75ae37fbb.js +0 -1
- package/_standalone/.next/static/chunks/9905-a19d379cb225246e.js +0 -1
- package/_standalone/.next/static/chunks/app/agents/[agentKey]/page-0ea3571c8fbae823.js +0 -1
- package/_standalone/.next/static/chunks/app/agents/page-66858acbcd1d4bf8.js +0 -1
- package/_standalone/.next/static/chunks/app/echo/[segment]/page-bf5c290fa3ccff09.js +0 -11
- package/_standalone/.next/static/chunks/app/layout-a5d5925b47e87cc3.js +0 -164
- package/_standalone/.next/static/chunks/app/setup/page-821714e7477be46c.js +0 -1
- package/_standalone/.next/static/chunks/app/trash/page-40bc7316806acd62.js +0 -1
- package/_standalone/.next/static/chunks/app/view/[...path]/page-6fbb14b8f322d0f0.js +0 -12
- package/_standalone/.next/static/chunks/app/wiki/page-ba36eccf4fe62cfe.js +0 -1
- package/_standalone/.next/static/css/b57c4eb3cc88308b.css +0 -1
- package/_standalone/lib/agent/context.ts +0 -403
- /package/_standalone/.next/static/{5GmVArEG8OX03azKICsGq → eIlwbGas1iRGonlPyEwj7}/_buildManifest.js +0 -0
- /package/_standalone/.next/static/{5GmVArEG8OX03azKICsGq → eIlwbGas1iRGonlPyEwj7}/_ssgManifest.js +0 -0
package/app/app/api/ask/route.ts
CHANGED
|
@@ -22,7 +22,9 @@ import { getFileContent, getMindRoot, collectAllFiles } from '@/lib/fs';
|
|
|
22
22
|
import { getModelConfig, hasImages } from '@/lib/agent/model';
|
|
23
23
|
import { isProviderId, type ProviderId, toPiProvider } from '@/lib/agent/providers';
|
|
24
24
|
import { getRequestScopedTools, getOrganizeTools, getChatTools, WRITE_TOOLS, truncate } from '@/lib/agent/tools';
|
|
25
|
+
import { isCustomProviderId, findCustomProvider } from '@/lib/custom-endpoints';
|
|
25
26
|
import { AGENT_SYSTEM_PROMPT, ORGANIZE_SYSTEM_PROMPT, CHAT_SYSTEM_PROMPT } from '@/lib/agent/prompt';
|
|
27
|
+
import { estimateStringTokens, getOllamaContextWindow } from '@/lib/agent/context';
|
|
26
28
|
import type { AskModeApi } from '@/lib/types';
|
|
27
29
|
import { toAgentMessages } from '@/lib/agent/to-agent-messages';
|
|
28
30
|
import { logAgentOp } from '@/lib/agent/log';
|
|
@@ -38,6 +40,48 @@ import type { Message as FrontendMessage } from '@/lib/types';
|
|
|
38
40
|
|
|
39
41
|
const MAX_DIR_FILES = 30;
|
|
40
42
|
|
|
43
|
+
/**
|
|
44
|
+
* Load attached and current files into context parts for the system prompt.
|
|
45
|
+
* Returns the context parts array and a list of file paths that failed to load.
|
|
46
|
+
* Deduplicates files and logs failures with the given mode label.
|
|
47
|
+
*/
|
|
48
|
+
function loadAttachedFileContext(
|
|
49
|
+
attachedFiles: string[] | undefined,
|
|
50
|
+
currentFile: string | undefined,
|
|
51
|
+
mode: string,
|
|
52
|
+
): { contextParts: string[]; failedFiles: string[] } {
|
|
53
|
+
const contextParts: string[] = [];
|
|
54
|
+
const failedFiles: string[] = [];
|
|
55
|
+
const seen = new Set<string>();
|
|
56
|
+
|
|
57
|
+
if (Array.isArray(attachedFiles) && attachedFiles.length > 0) {
|
|
58
|
+
for (const filePath of attachedFiles) {
|
|
59
|
+
if (seen.has(filePath)) continue;
|
|
60
|
+
seen.add(filePath);
|
|
61
|
+
try {
|
|
62
|
+
const content = truncate(getFileContent(filePath));
|
|
63
|
+
contextParts.push(`## Attached: ${filePath}\n\n${content}`);
|
|
64
|
+
} catch (err) {
|
|
65
|
+
console.warn(`[ask] ${mode}: failed to read attached file "${filePath}":`, err instanceof Error ? err.message : err);
|
|
66
|
+
failedFiles.push(filePath);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (currentFile && !seen.has(currentFile)) {
|
|
72
|
+
seen.add(currentFile);
|
|
73
|
+
try {
|
|
74
|
+
const content = truncate(getFileContent(currentFile));
|
|
75
|
+
contextParts.push(`## Current file: ${currentFile}\n\n${content}`);
|
|
76
|
+
} catch (err) {
|
|
77
|
+
console.warn(`[ask] ${mode}: failed to read currentFile "${currentFile}":`, err instanceof Error ? err.message : err);
|
|
78
|
+
failedFiles.push(currentFile);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { contextParts, failedFiles };
|
|
83
|
+
}
|
|
84
|
+
|
|
41
85
|
/** Expand attachedFiles entries: directory paths (trailing /) become individual file paths. */
|
|
42
86
|
function expandAttachedFiles(raw: string[]): string[] {
|
|
43
87
|
const result: string[] = [];
|
|
@@ -706,6 +750,8 @@ export async function POST(req: NextRequest) {
|
|
|
706
750
|
selectedAcpAgent?: { id: string; name: string } | null;
|
|
707
751
|
/** Per-request provider override from the chat panel capsule */
|
|
708
752
|
providerOverride?: string;
|
|
753
|
+
/** Per-request model override from the inline model picker */
|
|
754
|
+
modelOverride?: string;
|
|
709
755
|
};
|
|
710
756
|
try {
|
|
711
757
|
body = await req.json();
|
|
@@ -719,6 +765,11 @@ export async function POST(req: NextRequest) {
|
|
|
719
765
|
: body.mode === 'chat' ? 'chat'
|
|
720
766
|
: 'agent';
|
|
721
767
|
|
|
768
|
+
// Diagnostic: log attached files so silent failures are visible
|
|
769
|
+
if (Array.isArray(attachedFiles) && attachedFiles.length > 0) {
|
|
770
|
+
console.log(`[ask] mode=${askMode} attachedFiles=${JSON.stringify(attachedFiles)} currentFile=${currentFile ?? 'none'}`);
|
|
771
|
+
}
|
|
772
|
+
|
|
722
773
|
// Read agent config from settings
|
|
723
774
|
const serverSettings = readSettings();
|
|
724
775
|
const agentConfig = serverSettings.agent ?? {};
|
|
@@ -755,7 +806,7 @@ export async function POST(req: NextRequest) {
|
|
|
755
806
|
let systemPrompt: string;
|
|
756
807
|
|
|
757
808
|
if (askMode === 'organize') {
|
|
758
|
-
// Organize mode: minimal prompt — only KB structure + uploaded files
|
|
809
|
+
// Organize mode: minimal prompt — only KB structure + attached/uploaded files
|
|
759
810
|
const promptParts: string[] = [ORGANIZE_SYSTEM_PROMPT];
|
|
760
811
|
|
|
761
812
|
promptParts.push(`---\n\nmind_root=${getMindRoot()}`);
|
|
@@ -766,6 +817,15 @@ export async function POST(req: NextRequest) {
|
|
|
766
817
|
promptParts.push(`---\n\n## Knowledge Base Structure\n\n${bootstrapIndex.content}`);
|
|
767
818
|
}
|
|
768
819
|
|
|
820
|
+
// Include attached KB files (@ mentions) — same pattern as chat/agent modes
|
|
821
|
+
const { contextParts, failedFiles } = loadAttachedFileContext(attachedFiles, currentFile, 'organize');
|
|
822
|
+
if (contextParts.length > 0) {
|
|
823
|
+
promptParts.push(`---\n\nThe user is currently viewing these files:\n\n${contextParts.join('\n\n---\n\n')}`);
|
|
824
|
+
}
|
|
825
|
+
if (failedFiles.length > 0) {
|
|
826
|
+
promptParts.push(`---\n\n⚠️ The following attached files could not be read: ${failedFiles.join(', ')}. Inform the user that these files were not loaded.`);
|
|
827
|
+
}
|
|
828
|
+
|
|
769
829
|
if (uploadedParts.length > 0) {
|
|
770
830
|
promptParts.push(
|
|
771
831
|
`---\n\n## ⚠️ USER-UPLOADED FILES\n\n` +
|
|
@@ -791,28 +851,13 @@ export async function POST(req: NextRequest) {
|
|
|
791
851
|
const now = new Date();
|
|
792
852
|
promptParts.push(`---\n\n## Current Time Context\n- Current UTC Time: ${now.toISOString()}\n- System Local Time: ${new Intl.DateTimeFormat('en-US', { dateStyle: 'full', timeStyle: 'long' }).format(now)}`);
|
|
793
853
|
|
|
794
|
-
const contextParts
|
|
795
|
-
const seen = new Set<string>();
|
|
796
|
-
if (Array.isArray(attachedFiles) && attachedFiles.length > 0) {
|
|
797
|
-
for (const filePath of attachedFiles!) {
|
|
798
|
-
if (seen.has(filePath)) continue;
|
|
799
|
-
seen.add(filePath);
|
|
800
|
-
try {
|
|
801
|
-
const content = truncate(getFileContent(filePath));
|
|
802
|
-
contextParts.push(`## Attached: ${filePath}\n\n${content}`);
|
|
803
|
-
} catch { /* ignore missing files */ }
|
|
804
|
-
}
|
|
805
|
-
}
|
|
806
|
-
if (currentFile && !seen.has(currentFile)) {
|
|
807
|
-
seen.add(currentFile);
|
|
808
|
-
try {
|
|
809
|
-
const content = truncate(getFileContent(currentFile));
|
|
810
|
-
contextParts.push(`## Current file: ${currentFile}\n\n${content}`);
|
|
811
|
-
} catch { /* ignore */ }
|
|
812
|
-
}
|
|
854
|
+
const { contextParts, failedFiles } = loadAttachedFileContext(attachedFiles, currentFile, 'chat');
|
|
813
855
|
if (contextParts.length > 0) {
|
|
814
856
|
promptParts.push(`---\n\nThe user is currently viewing these files:\n\n${contextParts.join('\n\n---\n\n')}`);
|
|
815
857
|
}
|
|
858
|
+
if (failedFiles.length > 0) {
|
|
859
|
+
promptParts.push(`---\n\n⚠️ The following attached files could not be read: ${failedFiles.join(', ')}. Inform the user that these files were not loaded.`);
|
|
860
|
+
}
|
|
816
861
|
|
|
817
862
|
if (uploadedParts.length > 0) {
|
|
818
863
|
promptParts.push(
|
|
@@ -924,28 +969,7 @@ export async function POST(req: NextRequest) {
|
|
|
924
969
|
if (bootstrap.target_config_json?.ok) initContextBlocks.push(`## bootstrap_target_config_json\n\n${bootstrap.target_config_json.content}`);
|
|
925
970
|
|
|
926
971
|
// Build initial context from attached/current files
|
|
927
|
-
const contextParts
|
|
928
|
-
const seen = new Set<string>();
|
|
929
|
-
const hasAttached = Array.isArray(attachedFiles) && attachedFiles.length > 0;
|
|
930
|
-
|
|
931
|
-
if (hasAttached) {
|
|
932
|
-
for (const filePath of attachedFiles!) {
|
|
933
|
-
if (seen.has(filePath)) continue;
|
|
934
|
-
seen.add(filePath);
|
|
935
|
-
try {
|
|
936
|
-
const content = truncate(getFileContent(filePath));
|
|
937
|
-
contextParts.push(`## Attached: ${filePath}\n\n${content}`);
|
|
938
|
-
} catch { /* ignore missing files */ }
|
|
939
|
-
}
|
|
940
|
-
}
|
|
941
|
-
|
|
942
|
-
if (currentFile && !seen.has(currentFile)) {
|
|
943
|
-
seen.add(currentFile);
|
|
944
|
-
try {
|
|
945
|
-
const content = truncate(getFileContent(currentFile));
|
|
946
|
-
contextParts.push(`## Current file: ${currentFile}\n\n${content}`);
|
|
947
|
-
} catch { /* ignore */ }
|
|
948
|
-
}
|
|
972
|
+
const { contextParts, failedFiles } = loadAttachedFileContext(attachedFiles, currentFile, 'agent');
|
|
949
973
|
|
|
950
974
|
const now = new Date();
|
|
951
975
|
const timeContext = `## Current Time Context
|
|
@@ -970,6 +994,9 @@ export async function POST(req: NextRequest) {
|
|
|
970
994
|
if (contextParts.length > 0) {
|
|
971
995
|
promptParts.push(`---\n\nThe user is currently viewing these files:\n\n${contextParts.join('\n\n---\n\n')}`);
|
|
972
996
|
}
|
|
997
|
+
if (failedFiles.length > 0) {
|
|
998
|
+
promptParts.push(`---\n\n⚠️ The following attached files could not be read: ${failedFiles.join(', ')}. Inform the user that these files were not loaded.`);
|
|
999
|
+
}
|
|
973
1000
|
|
|
974
1001
|
if (uploadedParts.length > 0) {
|
|
975
1002
|
promptParts.push(
|
|
@@ -984,15 +1011,97 @@ export async function POST(req: NextRequest) {
|
|
|
984
1011
|
systemPrompt = promptParts.join('\n\n');
|
|
985
1012
|
}
|
|
986
1013
|
|
|
1014
|
+
// Log system prompt size for diagnosing context truncation issues (e.g. Ollama)
|
|
1015
|
+
console.log(`[ask] mode=${askMode} systemPrompt=${systemPrompt.length} chars (~${Math.ceil(systemPrompt.length / 4)} tokens)`);
|
|
1016
|
+
|
|
987
1017
|
try {
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
1018
|
+
let provOverride: ProviderId | undefined;
|
|
1019
|
+
let customProviderConfig: { apiKey: string; model: string; baseUrl: string } | undefined;
|
|
1020
|
+
|
|
1021
|
+
// Handle custom provider (cp_*) or built-in provider override
|
|
1022
|
+
if (body.providerOverride) {
|
|
1023
|
+
if (isCustomProviderId(body.providerOverride)) {
|
|
1024
|
+
const settings = readSettings();
|
|
1025
|
+
const customProvider = findCustomProvider(settings.ai.providers ?? [], body.providerOverride);
|
|
1026
|
+
if (!customProvider) {
|
|
1027
|
+
return apiError(ErrorCodes.INVALID_REQUEST, 'Custom provider not found', 400);
|
|
1028
|
+
}
|
|
1029
|
+
provOverride = customProvider.protocol;
|
|
1030
|
+
customProviderConfig = {
|
|
1031
|
+
apiKey: customProvider.apiKey,
|
|
1032
|
+
model: customProvider.model,
|
|
1033
|
+
baseUrl: customProvider.baseUrl,
|
|
1034
|
+
};
|
|
1035
|
+
} else if (isProviderId(body.providerOverride)) {
|
|
1036
|
+
provOverride = body.providerOverride as ProviderId;
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
// Per-request model override (from chat capsule model picker)
|
|
1041
|
+
const modelOverride = (body.modelOverride && typeof body.modelOverride === 'string')
|
|
1042
|
+
? body.modelOverride.trim() : undefined;
|
|
1043
|
+
|
|
991
1044
|
const { model, modelName, apiKey, provider, baseUrl } = getModelConfig({
|
|
992
1045
|
provider: provOverride,
|
|
1046
|
+
apiKey: customProviderConfig?.apiKey,
|
|
1047
|
+
model: modelOverride ?? customProviderConfig?.model,
|
|
1048
|
+
baseUrl: customProviderConfig?.baseUrl,
|
|
993
1049
|
hasImages: hasImages(messages),
|
|
994
1050
|
});
|
|
995
1051
|
|
|
1052
|
+
// ── Ollama context window guard ──
|
|
1053
|
+
// Ollama silently truncates input that exceeds the model's actual context window.
|
|
1054
|
+
// Detect this and compact the system prompt to prevent attached files from being dropped.
|
|
1055
|
+
if (provider === 'ollama') {
|
|
1056
|
+
const ollamaBase = baseUrl || 'http://localhost:11434/v1';
|
|
1057
|
+
const actualCtx = await getOllamaContextWindow(ollamaBase, modelName);
|
|
1058
|
+
const promptTokens = estimateStringTokens(systemPrompt);
|
|
1059
|
+
// Reserve ~30% of context for conversation history + model output
|
|
1060
|
+
const maxPromptTokens = actualCtx ? Math.floor(actualCtx * 0.7) : undefined;
|
|
1061
|
+
|
|
1062
|
+
if (actualCtx) {
|
|
1063
|
+
console.log(`[ask] Ollama model="${modelName}" context=${actualCtx} promptTokens=${promptTokens} maxPromptTokens=${maxPromptTokens}`);
|
|
1064
|
+
}
|
|
1065
|
+
|
|
1066
|
+
if (maxPromptTokens && promptTokens > maxPromptTokens) {
|
|
1067
|
+
console.warn(`[ask] Ollama context overflow: prompt ${promptTokens} tokens > ${maxPromptTokens} max (${actualCtx} ctx). Compacting...`);
|
|
1068
|
+
// Compact by progressively stripping lower-priority sections from system prompt.
|
|
1069
|
+
// Priority order (keep first, strip last):
|
|
1070
|
+
// 1. Core system prompt (AGENT/CHAT/ORGANIZE base) — must keep
|
|
1071
|
+
// 2. Attached/current file content — user explicitly requested these
|
|
1072
|
+
// 3. KB structure (README.md) — important for navigation
|
|
1073
|
+
// 4. Time context — low priority
|
|
1074
|
+
// 5. SKILL.md + write-supplement — largest sections, can be stripped
|
|
1075
|
+
// 6. bootstrap INSTRUCTION/CONFIG — can be stripped for local models
|
|
1076
|
+
|
|
1077
|
+
// Strategy: strip sections between "---" delimiters from the end,
|
|
1078
|
+
// but preserve sections containing "Attached:" or "Current file:" or "USER-UPLOADED"
|
|
1079
|
+
const sections = systemPrompt.split('\n\n---\n\n');
|
|
1080
|
+
const preserved: string[] = [];
|
|
1081
|
+
let currentTokens = 0;
|
|
1082
|
+
|
|
1083
|
+
for (const section of sections) {
|
|
1084
|
+
const sectionTokens = estimateStringTokens(section);
|
|
1085
|
+
const isAttachment = section.includes('## Attached:') || section.includes('## Current file:') || section.includes('USER-UPLOADED');
|
|
1086
|
+
const isCore = preserved.length === 0; // first section = base system prompt
|
|
1087
|
+
|
|
1088
|
+
if (isCore || isAttachment) {
|
|
1089
|
+
// Always keep core prompt and user attachments
|
|
1090
|
+
preserved.push(section);
|
|
1091
|
+
currentTokens += sectionTokens;
|
|
1092
|
+
} else if (currentTokens + sectionTokens <= maxPromptTokens) {
|
|
1093
|
+
preserved.push(section);
|
|
1094
|
+
currentTokens += sectionTokens;
|
|
1095
|
+
} else {
|
|
1096
|
+
console.log(`[ask] Ollama compact: stripping section (${sectionTokens} tokens): ${section.slice(0, 80)}...`);
|
|
1097
|
+
}
|
|
1098
|
+
}
|
|
1099
|
+
|
|
1100
|
+
systemPrompt = preserved.join('\n\n---\n\n');
|
|
1101
|
+
console.log(`[ask] Ollama compacted: ${promptTokens} → ${estimateStringTokens(systemPrompt)} tokens`);
|
|
1102
|
+
}
|
|
1103
|
+
}
|
|
1104
|
+
|
|
996
1105
|
// Convert frontend messages to AgentMessage[]
|
|
997
1106
|
const agentMessages = toAgentMessages(messages);
|
|
998
1107
|
|
|
@@ -20,7 +20,7 @@ import { readSettings } from '@/lib/settings';
|
|
|
20
20
|
import { scanSkillDirs } from '@/lib/pi-integration/skills';
|
|
21
21
|
import { getMindRoot } from '@/lib/fs';
|
|
22
22
|
|
|
23
|
-
function enrichMindOsAgent(agent: Record<string, unknown>) {
|
|
23
|
+
async function enrichMindOsAgent(agent: Record<string, unknown>) {
|
|
24
24
|
agent.present = true;
|
|
25
25
|
agent.installed = true;
|
|
26
26
|
agent.scope = 'builtin';
|
|
@@ -35,7 +35,7 @@ function enrichMindOsAgent(agent: Record<string, unknown>) {
|
|
|
35
35
|
|
|
36
36
|
try {
|
|
37
37
|
const projectRoot = process.env.MINDOS_PROJECT_ROOT || path.resolve(process.cwd(), '..');
|
|
38
|
-
const skills = scanSkillDirs({ projectRoot, mindRoot: getMindRoot() });
|
|
38
|
+
const skills = await scanSkillDirs({ projectRoot, mindRoot: getMindRoot() });
|
|
39
39
|
const enabledSkills = skills.filter(s => s.enabled);
|
|
40
40
|
agent.installedSkillNames = enabledSkills.map(s => s.name);
|
|
41
41
|
agent.installedSkillCount = enabledSkills.length;
|
|
@@ -217,7 +217,7 @@ export async function GET() {
|
|
|
217
217
|
});
|
|
218
218
|
|
|
219
219
|
const mindos = agents.find(a => a.key === 'mindos');
|
|
220
|
-
if (mindos) enrichMindOsAgent(mindos as unknown as Record<string, unknown>);
|
|
220
|
+
if (mindos) await enrichMindOsAgent(mindos as unknown as Record<string, unknown>);
|
|
221
221
|
|
|
222
222
|
// Runtime verification: for agents marked as installed with HTTP endpoint,
|
|
223
223
|
// verify endpoint is reachable (1s timeout to avoid blocking)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export const dynamic = 'force-dynamic';
|
|
2
2
|
import { NextRequest, NextResponse } from 'next/server';
|
|
3
3
|
import { getModels as piGetModels } from '@mariozechner/pi-ai';
|
|
4
|
-
import { effectiveAiConfig } from '@/lib/settings';
|
|
4
|
+
import { effectiveAiConfig, readSettings } from '@/lib/settings';
|
|
5
5
|
import { type ProviderId, isProviderId, PROVIDER_PRESETS, toPiProvider, getDefaultBaseUrl } from '@/lib/agent/providers';
|
|
6
|
+
import { isProviderEntryId, findProvider } from '@/lib/custom-endpoints';
|
|
6
7
|
|
|
7
8
|
const TIMEOUT = 10_000;
|
|
8
9
|
|
|
@@ -15,6 +16,31 @@ export async function POST(req: NextRequest) {
|
|
|
15
16
|
baseUrl?: string;
|
|
16
17
|
};
|
|
17
18
|
|
|
19
|
+
// Handle provider entry ID (p_*) — look up from unified providers list
|
|
20
|
+
if (provider && isProviderEntryId(provider)) {
|
|
21
|
+
const settings = readSettings();
|
|
22
|
+
const entry = findProvider(settings.ai.providers, provider);
|
|
23
|
+
if (!entry) {
|
|
24
|
+
return NextResponse.json({ ok: false, error: 'Provider not found' }, { status: 404 });
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const ctrl = new AbortController();
|
|
28
|
+
const timer = setTimeout(() => ctrl.abort(), TIMEOUT);
|
|
29
|
+
|
|
30
|
+
try {
|
|
31
|
+
const models = await fetchModels(entry.protocol, apiKey || entry.apiKey, baseUrl || entry.baseUrl, ctrl.signal);
|
|
32
|
+
return NextResponse.json({ ok: true, models });
|
|
33
|
+
} catch (e: unknown) {
|
|
34
|
+
if (e instanceof Error && e.name === 'AbortError') {
|
|
35
|
+
return NextResponse.json({ ok: false, error: 'Request timed out' });
|
|
36
|
+
}
|
|
37
|
+
return NextResponse.json({ ok: false, error: e instanceof Error ? e.message : 'Network error' });
|
|
38
|
+
} finally {
|
|
39
|
+
clearTimeout(timer);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Handle built-in protocol ID (openai, anthropic, etc.)
|
|
18
44
|
if (!provider || !isProviderId(provider)) {
|
|
19
45
|
return NextResponse.json({ ok: false, error: 'Invalid provider' }, { status: 400 });
|
|
20
46
|
}
|
|
@@ -27,13 +53,15 @@ export async function POST(req: NextRequest) {
|
|
|
27
53
|
return NextResponse.json({ ok: true, models });
|
|
28
54
|
}
|
|
29
55
|
|
|
30
|
-
const cfg = effectiveAiConfig(
|
|
56
|
+
const cfg = effectiveAiConfig();
|
|
31
57
|
let resolvedKey = apiKey || '';
|
|
32
|
-
if (!resolvedKey
|
|
58
|
+
if (!resolvedKey) {
|
|
33
59
|
resolvedKey = cfg.apiKey;
|
|
34
60
|
}
|
|
35
61
|
|
|
36
|
-
|
|
62
|
+
// Allow keyless requests when an explicit baseUrl is provided (local servers like Ollama)
|
|
63
|
+
const effectiveBaseUrl = baseUrl || cfg.baseUrl || '';
|
|
64
|
+
if (!resolvedKey && !effectiveBaseUrl) {
|
|
37
65
|
return NextResponse.json({ ok: false, error: 'No API key configured' });
|
|
38
66
|
}
|
|
39
67
|
|
|
@@ -41,7 +69,7 @@ export async function POST(req: NextRequest) {
|
|
|
41
69
|
const timer = setTimeout(() => ctrl.abort(), TIMEOUT);
|
|
42
70
|
|
|
43
71
|
try {
|
|
44
|
-
const models = await fetchModels(provider as ProviderId, resolvedKey,
|
|
72
|
+
const models = await fetchModels(provider as ProviderId, resolvedKey, effectiveBaseUrl, ctrl.signal);
|
|
45
73
|
return NextResponse.json({ ok: true, models });
|
|
46
74
|
} catch (e: unknown) {
|
|
47
75
|
if (e instanceof Error && e.name === 'AbortError') {
|
|
@@ -112,10 +140,9 @@ async function fetchAnthropicModels(apiKey: string, signal: AbortSignal): Promis
|
|
|
112
140
|
async function fetchOpenAICompatModels(
|
|
113
141
|
endpoint: string, apiKey: string, signal: AbortSignal,
|
|
114
142
|
): Promise<string[]> {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
});
|
|
143
|
+
const headers: Record<string, string> = {};
|
|
144
|
+
if (apiKey) headers['Authorization'] = `Bearer ${apiKey}`;
|
|
145
|
+
const res = await fetch(endpoint, { headers, signal });
|
|
119
146
|
|
|
120
147
|
if (!res.ok) {
|
|
121
148
|
const errBody = await res.text().catch(() => '');
|
|
@@ -2,7 +2,8 @@ export const dynamic = 'force-dynamic';
|
|
|
2
2
|
import { NextRequest, NextResponse } from 'next/server';
|
|
3
3
|
import { readSettings, writeSettings, ServerSettings } from '@/lib/settings';
|
|
4
4
|
import { invalidateCache } from '@/lib/fs';
|
|
5
|
-
import {
|
|
5
|
+
import { ALL_PROVIDER_IDS, getApiKeyEnvVar, getApiKeyFromEnv } from '@/lib/agent/providers';
|
|
6
|
+
import { parseProviders } from '@/lib/custom-endpoints';
|
|
6
7
|
|
|
7
8
|
function maskToken(token: string | undefined): string {
|
|
8
9
|
if (!token) return '';
|
|
@@ -34,31 +35,19 @@ export async function GET() {
|
|
|
34
35
|
}
|
|
35
36
|
}
|
|
36
37
|
|
|
37
|
-
|
|
38
|
-
const maskedProviders: Record<string, { apiKey: string; model: string; baseUrl?: string }> = {};
|
|
39
|
-
for (const [id, cfg] of Object.entries(settings.ai.providers)) {
|
|
40
|
-
if (!cfg) continue;
|
|
41
|
-
maskedProviders[id] = {
|
|
42
|
-
apiKey: cfg.apiKey ? '***set***' : '',
|
|
43
|
-
model: cfg.model ?? '',
|
|
44
|
-
...(cfg.baseUrl !== undefined ? { baseUrl: cfg.baseUrl } : {}),
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const masked = {
|
|
38
|
+
return NextResponse.json({
|
|
49
39
|
ai: {
|
|
50
|
-
|
|
51
|
-
providers:
|
|
40
|
+
activeProvider: settings.ai.activeProvider,
|
|
41
|
+
providers: settings.ai.providers,
|
|
52
42
|
},
|
|
53
43
|
mindRoot: settings.mindRoot,
|
|
54
|
-
webPassword: settings.webPassword
|
|
44
|
+
webPassword: settings.webPassword ?? '',
|
|
55
45
|
authToken: maskToken(settings.authToken),
|
|
56
46
|
mcpPort: settings.mcpPort ?? 8781,
|
|
57
47
|
agent: settings.agent ?? {},
|
|
58
48
|
envOverrides,
|
|
59
49
|
envValues,
|
|
60
|
-
};
|
|
61
|
-
return NextResponse.json(masked);
|
|
50
|
+
});
|
|
62
51
|
}
|
|
63
52
|
|
|
64
53
|
export async function POST(req: NextRequest) {
|
|
@@ -66,28 +55,14 @@ export async function POST(req: NextRequest) {
|
|
|
66
55
|
const body = await req.json() as Partial<ServerSettings>;
|
|
67
56
|
const current = readSettings();
|
|
68
57
|
|
|
69
|
-
//
|
|
70
|
-
const
|
|
71
|
-
if (body.ai
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
const cur = mergedProviders[id as keyof typeof mergedProviders] ?? { apiKey: '', model: '' };
|
|
75
|
-
mergedProviders[id as keyof typeof mergedProviders] = {
|
|
76
|
-
...cur,
|
|
77
|
-
...incoming,
|
|
78
|
-
apiKey: incoming.apiKey === '***set***'
|
|
79
|
-
? (cur.apiKey ?? '')
|
|
80
|
-
: (incoming.apiKey ?? cur.apiKey ?? ''),
|
|
81
|
-
model: incoming.model ?? cur.model ?? '',
|
|
82
|
-
};
|
|
83
|
-
}
|
|
58
|
+
// Resolve AI config
|
|
59
|
+
const resolvedAi = { ...current.ai };
|
|
60
|
+
if (body.ai) {
|
|
61
|
+
if (body.ai.activeProvider !== undefined) resolvedAi.activeProvider = body.ai.activeProvider;
|
|
62
|
+
if (body.ai.providers !== undefined) resolvedAi.providers = parseProviders(body.ai.providers);
|
|
84
63
|
}
|
|
85
64
|
|
|
86
|
-
|
|
87
|
-
const incomingWebPassword = body.webPassword;
|
|
88
|
-
const resolvedWebPassword = incomingWebPassword === '***set***'
|
|
89
|
-
? current.webPassword
|
|
90
|
-
: (incomingWebPassword ?? current.webPassword);
|
|
65
|
+
const resolvedWebPassword = body.webPassword ?? current.webPassword;
|
|
91
66
|
|
|
92
67
|
// authToken is read-only via POST (use /api/settings/reset-token to regenerate)
|
|
93
68
|
// but allow clearing it by passing empty string
|
|
@@ -109,10 +84,7 @@ export async function POST(req: NextRequest) {
|
|
|
109
84
|
}
|
|
110
85
|
|
|
111
86
|
const next: ServerSettings = {
|
|
112
|
-
ai:
|
|
113
|
-
provider: body.ai?.provider ?? current.ai.provider,
|
|
114
|
-
providers: mergedProviders,
|
|
115
|
-
},
|
|
87
|
+
ai: resolvedAi,
|
|
116
88
|
mindRoot: body.mindRoot ?? current.mindRoot,
|
|
117
89
|
agent: body.agent ?? current.agent,
|
|
118
90
|
webPassword: resolvedWebPassword,
|
|
@@ -4,6 +4,7 @@ import { complete } from '@mariozechner/pi-ai';
|
|
|
4
4
|
import { effectiveAiConfig, readBaseUrlCompat, writeSettings, readSettings } from '@/lib/settings';
|
|
5
5
|
import { getModelConfig } from '@/lib/agent/model';
|
|
6
6
|
import { type ProviderId, isProviderId } from '@/lib/agent/providers';
|
|
7
|
+
import { isProviderEntryId, findProvider } from '@/lib/custom-endpoints';
|
|
7
8
|
|
|
8
9
|
const TIMEOUT = 15_000;
|
|
9
10
|
|
|
@@ -39,13 +40,88 @@ function classifyPiAiError(err: unknown): { code: ErrorCode; error: string } {
|
|
|
39
40
|
export async function POST(req: NextRequest) {
|
|
40
41
|
try {
|
|
41
42
|
const body = await req.json();
|
|
42
|
-
const { provider, apiKey, model, baseUrl } = body as {
|
|
43
|
+
const { provider, apiKey, model, baseUrl, baseProviderId } = body as {
|
|
43
44
|
provider?: string;
|
|
44
45
|
apiKey?: string;
|
|
45
46
|
model?: string;
|
|
46
47
|
baseUrl?: string;
|
|
48
|
+
/** When set, run an inline test using only the supplied params (no settings fallback). */
|
|
49
|
+
baseProviderId?: string;
|
|
47
50
|
};
|
|
48
51
|
|
|
52
|
+
// Inline test for unsaved custom providers — uses only the supplied params.
|
|
53
|
+
if (baseProviderId && isProviderId(baseProviderId)) {
|
|
54
|
+
if (!apiKey) {
|
|
55
|
+
return NextResponse.json({ ok: false, code: 'auth_error', error: 'No API key configured' });
|
|
56
|
+
}
|
|
57
|
+
if (!model) {
|
|
58
|
+
return NextResponse.json({ ok: false, code: 'unknown', error: 'Model is required' }, { status: 400 });
|
|
59
|
+
}
|
|
60
|
+
const start = Date.now();
|
|
61
|
+
const ctrl = new AbortController();
|
|
62
|
+
const timer = setTimeout(() => ctrl.abort(), TIMEOUT);
|
|
63
|
+
try {
|
|
64
|
+
const { model: piModel } = getModelConfig({
|
|
65
|
+
provider: baseProviderId as ProviderId,
|
|
66
|
+
apiKey,
|
|
67
|
+
model,
|
|
68
|
+
baseUrl: baseUrl || undefined,
|
|
69
|
+
});
|
|
70
|
+
await complete(piModel, {
|
|
71
|
+
messages: [{ role: 'user', content: 'hi', timestamp: Date.now() }],
|
|
72
|
+
}, {
|
|
73
|
+
apiKey,
|
|
74
|
+
signal: ctrl.signal,
|
|
75
|
+
});
|
|
76
|
+
return NextResponse.json({ ok: true, latency: Date.now() - start });
|
|
77
|
+
} catch (e) {
|
|
78
|
+
return NextResponse.json({ ok: false, ...classifyPiAiError(e) });
|
|
79
|
+
} finally {
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// Support provider entry IDs (p_*) — look up from unified providers list
|
|
85
|
+
if (provider && isProviderEntryId(provider)) {
|
|
86
|
+
const settings = readSettings();
|
|
87
|
+
const entry = findProvider(settings.ai.providers, provider);
|
|
88
|
+
if (!entry) {
|
|
89
|
+
return NextResponse.json(
|
|
90
|
+
{ ok: false, code: 'unknown', error: 'Provider not found' },
|
|
91
|
+
{ status: 400 },
|
|
92
|
+
);
|
|
93
|
+
}
|
|
94
|
+
const resolvedKey = apiKey || entry.apiKey;
|
|
95
|
+
const resolvedModel = model || entry.model;
|
|
96
|
+
const resolvedBaseUrl = baseUrl || entry.baseUrl;
|
|
97
|
+
if (!resolvedKey) {
|
|
98
|
+
return NextResponse.json({ ok: false, code: 'auth_error', error: 'No API key configured' });
|
|
99
|
+
}
|
|
100
|
+
const start = Date.now();
|
|
101
|
+
const ctrl = new AbortController();
|
|
102
|
+
const timer = setTimeout(() => ctrl.abort(), TIMEOUT);
|
|
103
|
+
try {
|
|
104
|
+
const { model: piModel } = getModelConfig({
|
|
105
|
+
provider: entry.protocol,
|
|
106
|
+
apiKey: resolvedKey,
|
|
107
|
+
model: resolvedModel || undefined,
|
|
108
|
+
baseUrl: resolvedBaseUrl || undefined,
|
|
109
|
+
});
|
|
110
|
+
await complete(piModel, {
|
|
111
|
+
messages: [{ role: 'user', content: 'hi', timestamp: Date.now() }],
|
|
112
|
+
}, {
|
|
113
|
+
apiKey: resolvedKey,
|
|
114
|
+
signal: ctrl.signal,
|
|
115
|
+
});
|
|
116
|
+
return NextResponse.json({ ok: true, latency: Date.now() - start });
|
|
117
|
+
} catch (e) {
|
|
118
|
+
return NextResponse.json({ ok: false, ...classifyPiAiError(e) });
|
|
119
|
+
} finally {
|
|
120
|
+
clearTimeout(timer);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
// Legacy: support raw protocol IDs (openai, anthropic, etc.)
|
|
49
125
|
if (!provider || !isProviderId(provider)) {
|
|
50
126
|
return NextResponse.json(
|
|
51
127
|
{ ok: false, code: 'unknown', error: 'Invalid provider' },
|
|
@@ -55,7 +131,7 @@ export async function POST(req: NextRequest) {
|
|
|
55
131
|
|
|
56
132
|
const cfg = effectiveAiConfig(provider as ProviderId);
|
|
57
133
|
let resolvedKey = apiKey || '';
|
|
58
|
-
if (!resolvedKey
|
|
134
|
+
if (!resolvedKey) {
|
|
59
135
|
resolvedKey = cfg.apiKey;
|
|
60
136
|
}
|
|
61
137
|
|