@circuitwall/jarela 2.3.0 → 2.5.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/standalone/.next/BUILD_ID +1 -1
- package/.next/standalone/.next/app-path-routes-manifest.json +5 -5
- package/.next/standalone/.next/build-manifest.json +2 -2
- package/.next/standalone/.next/prerender-manifest.json +4 -4
- package/.next/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_global-error.html +1 -1
- package/.next/standalone/.next/server/app/_global-error.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/_global-error/__PAGE__.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_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/_not-found.html +14 -2
- package/.next/standalone/.next/server/app/_not-found.rsc +16 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +16 -4
- package/.next/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +16 -4
- 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/_tree.segment.rsc +2 -2
- package/.next/standalone/.next/server/app/api/v1/access/[identity]/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/access/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/agents/[id]/compact/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/agents/[id]/display-filters/route.js +17 -5
- package/.next/standalone/.next/server/app/api/v1/agents/[id]/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/agents/[id]/thread/route.js +17 -5
- package/.next/standalone/.next/server/app/api/v1/agents/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/allowed-sites/[hostname]/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/allowed-sites/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/artifacts/lifecycle/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/bridges/[id]/chats/route.js +26 -32
- package/.next/standalone/.next/server/app/api/v1/bridges/[id]/chats/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1/bridges/[id]/ignores/[remote_jid]/route.js +18 -7
- package/.next/standalone/.next/server/app/api/v1/bridges/[id]/ignores/route.js +18 -7
- package/.next/standalone/.next/server/app/api/v1/bridges/[id]/lookup/route.js +26 -32
- package/.next/standalone/.next/server/app/api/v1/bridges/[id]/lookup/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1/bridges/[id]/pair/route.js +27 -33
- package/.next/standalone/.next/server/app/api/v1/bridges/[id]/pair/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1/bridges/[id]/route.js +26 -32
- package/.next/standalone/.next/server/app/api/v1/bridges/[id]/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1/bridges/[id]/routes/[route_id]/route.js +28 -12
- package/.next/standalone/.next/server/app/api/v1/bridges/[id]/routes/route.js +28 -12
- package/.next/standalone/.next/server/app/api/v1/bridges/[id]/status/route.js +26 -32
- package/.next/standalone/.next/server/app/api/v1/bridges/[id]/status/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1/bridges/kinds/route.js +9 -2
- package/.next/standalone/.next/server/app/api/v1/bridges/kinds/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1/bridges/route.js +19 -7
- package/.next/standalone/.next/server/app/api/v1/bridges/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1/dashboard/metrics/route.js +17 -5
- package/.next/standalone/.next/server/app/api/v1/dashboard/pricing/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/dashboard/pricing-task/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/documents/search/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/documents/settings/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/documents/sources/[id]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/documents/sources/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/env-sync/allowlist/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/env-sync/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/events/route.js +26 -32
- package/.next/standalone/.next/server/app/api/v1/events/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1/extension/agents/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/extension/browser/activate/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/extension/browser/foreground/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/extension/browser/history/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/extension/browser/poll/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/extension/browser/progress/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/extension/browser/result/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/extension/browser/retry/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/extension/browser/status/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/extension/browser/tabs/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/extension/fill/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/extension/refine/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/extension/turn/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/extensions/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/extensions/tools/[name]/config/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/extensions/tools/[name]/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/extensions/tools/[name]/secrets/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/harnesses/[id]/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/harnesses/default/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/harnesses/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/health/route.js +17 -5
- package/.next/standalone/.next/server/app/api/v1/integrations/[name]/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/integrations/gmail/oauth/callback/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/integrations/gmail/oauth/start/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/integrations/gmail/oauth/status/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/integrations/outlook/oauth/callback/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/integrations/outlook/oauth/start/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/integrations/outlook/oauth/status/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/integrations/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/lifecycle/adoption/route.js +17 -5
- package/.next/standalone/.next/server/app/api/v1/maps/embed/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/mcp-servers/[name]/route.js +17 -5
- package/.next/standalone/.next/server/app/api/v1/mcp-servers/route.js +17 -5
- package/.next/standalone/.next/server/app/api/v1/memory/[namespace]/[key]/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/memory/policy/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/memory/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/memory/search/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/models/[name]/compact-threads/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/models/[name]/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/models/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/packages/defaults/[id]/route.js +28 -4
- package/.next/standalone/.next/server/app/api/v1/packages/reload/route.js +28 -4
- package/.next/standalone/.next/server/app/api/v1/packages/route.js +28 -4
- package/.next/standalone/.next/server/app/api/v1/page-capture/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/pending-actions/[id]/approve/route.js +27 -12
- package/.next/standalone/.next/server/app/api/v1/profile/location/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/profile/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/providers/[provider]/models/route.js +585 -58
- package/.next/standalone/.next/server/app/api/v1/providers/[provider]/probe/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/providers/github-copilot/auth/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/providers/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/proxy-config/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/redaction/init/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/redaction/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/scheduled-tasks/[id]/route.js +17 -5
- package/.next/standalone/.next/server/app/api/v1/scheduled-tasks/[id]/run/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/scheduled-tasks/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/security/idle-timeout/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/security/pin/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/security/state/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/security/unlock/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/security/verify-pin/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/skills/[id]/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/skills/repos/[id]/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/skills/repos/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/skills/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/tasks/[agent_id]/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/tasks/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/activity/route.js +7 -0
- package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/context-pin/route.js +12 -6
- package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/route.js +125 -26
- package/.next/standalone/.next/server/app/api/v1/threads/[thread_id]/run/route.js +1 -2
- package/.next/standalone/.next/server/app/api/v1/threads/route.js +17 -5
- package/.next/standalone/.next/server/app/api/v1/tools/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/update/apply/route.js.nft.json +1 -1
- package/.next/standalone/.next/server/app/api/v1/voice/transcribe/route.js +17 -5
- package/.next/standalone/.next/server/app/api/v1/voice/tts/route.js +17 -5
- package/.next/standalone/.next/server/app/api/v1/watchers/[id]/route.js +17 -5
- package/.next/standalone/.next/server/app/api/v1/watchers/[id]/run/route.js +1 -1
- package/.next/standalone/.next/server/app/api/v1/watchers/route.js +1 -1
- package/.next/standalone/.next/server/app/page.js +27323 -27034
- package/.next/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app/setup/page.js +8 -0
- package/.next/standalone/.next/server/app/setup/page_client-reference-manifest.js +1 -1
- package/.next/standalone/.next/server/app-paths-manifest.json +5 -5
- package/.next/standalone/.next/server/chunks/1388.js +106 -36
- package/.next/standalone/.next/server/chunks/2068.js +47 -10
- package/.next/standalone/.next/server/chunks/210.js +6 -6
- package/.next/standalone/.next/server/chunks/2447.js +10 -5
- package/.next/standalone/.next/server/chunks/2653.js +80 -1
- package/.next/standalone/.next/server/chunks/3042.js +10 -8
- package/.next/standalone/.next/server/chunks/{4540.js → 309.js} +104 -38
- package/.next/standalone/.next/server/chunks/{2350.js → 3272.js} +104 -37
- package/.next/standalone/.next/server/chunks/3524.js +150 -67
- package/.next/standalone/.next/server/chunks/3566.js +1 -1
- package/.next/standalone/.next/server/chunks/4189.js +1 -1
- package/.next/standalone/.next/server/chunks/4596.js +28 -4
- package/.next/standalone/.next/server/chunks/46.js +91 -31
- package/.next/standalone/.next/server/chunks/466.js +53 -7
- package/.next/standalone/.next/server/chunks/4955.js +30 -3
- package/.next/standalone/.next/server/chunks/5585.js +28 -4
- package/.next/standalone/.next/server/chunks/5908.js +63 -42
- package/.next/standalone/.next/server/chunks/6142.js +10 -2
- package/.next/standalone/.next/server/chunks/8461.js +10 -8
- package/.next/standalone/.next/server/chunks/9032.js +5 -5
- package/.next/standalone/.next/server/chunks/9164.js +487 -177
- package/.next/standalone/.next/server/chunks/9304.js +51 -12
- package/.next/standalone/.next/server/chunks/{423.js → 9590.js} +84 -24
- package/.next/standalone/.next/server/middleware-build-manifest.js +2 -2
- package/.next/standalone/.next/server/middleware.js +89 -21
- package/.next/standalone/.next/server/pages/404.html +14 -2
- package/.next/standalone/.next/server/pages/500.html +1 -1
- package/.next/standalone/.next/server/preview-props.json +3 -3
- package/.next/standalone/.next/server/server-reference-manifest.json +1 -1
- package/.next/standalone/.next/static/chunks/{1395-e4a99912b8f83044.js → 1395-bfe1f3ef760c0f50.js} +54 -8
- package/.next/standalone/.next/static/chunks/app/{layout-9bc0b10127cb7d42.js → layout-7b24ad13faa3944b.js} +69 -2
- package/.next/standalone/.next/static/chunks/app/{page-213a623b37226452.js → page-2ef8d75d40eb4311.js} +10643 -10295
- package/.next/standalone/.next/static/css/c160ea862133acd2.css +5 -0
- package/.next/standalone/docs/adoption/changesets.json +2805 -0
- package/.next/standalone/package.json +1 -1
- package/CHANGELOG.md +52 -0
- package/api/client.models.test.ts +10 -0
- package/api/client.startup.test.ts +129 -0
- package/api/client.ts +51 -9
- package/api/thread-metadata.test.ts +72 -0
- package/api/types.ts +6 -3
- package/app/api/v1/providers/[provider]/models/route.test.ts +216 -6
- package/app/api/v1/providers/[provider]/models/route.ts +259 -40
- package/app/api/v1/threads/[thread_id]/route.ts +26 -23
- package/app/api/v1/threads/[thread_id]/run/route.ts +0 -1
- package/app/globals.css +119 -29
- package/app/layout.tsx +12 -0
- package/components/agents/AgentsPanel.tsx +5 -13
- package/components/chat/ChatView.tsx +9 -18
- package/components/chat/InputBar.tsx +1 -1
- package/components/chat/MessageBubble.tsx +13 -1
- package/components/chat/MessageList.test.tsx +45 -4
- package/components/chat/MessageList.tsx +123 -89
- package/components/chat/ToolList.tsx +88 -84
- package/components/chat/useThreadActivity.test.tsx +2 -2
- package/components/chat/useThreadActivity.ts +2 -2
- package/components/chat/useThreadData.ts +1 -30
- package/components/chat/useThreadMetadataPolling.test.tsx +128 -0
- package/components/chat/useThreadMetadataPolling.ts +50 -0
- package/components/chat/useUserProfileAndAgent.test.tsx +31 -0
- package/components/chat/useUserProfileAndAgent.ts +13 -2
- package/components/dashboard/DashboardPanel.tsx +1 -1
- package/components/dashboard/Series.tsx +34 -21
- package/components/dashboard/SummaryCards.tsx +1 -1
- package/components/integrations/NetworkSection.tsx +2 -2
- package/components/layout/AppShell.tsx +22 -7
- package/components/layout/MenuPanel.tsx +14 -24
- package/components/profile/ProfilePanel.tsx +2 -2
- package/components/settings/AppearancePanel.tsx +70 -3
- package/components/settings/SettingsPanel.tsx +2 -2
- package/components/tools/ToolsPanel.tsx +2 -7
- package/components/ui/AppearancePreferences.test.tsx +68 -0
- package/components/ui/BootScreen.test.tsx +44 -0
- package/components/ui/BootScreen.tsx +24 -11
- package/components/ui/NotificationStatus.test.tsx +2 -2
- package/components/ui/NotificationStatus.tsx +5 -5
- package/components/ui/SubTabBar.tsx +2 -3
- package/contexts/ThemeContext.tsx +84 -1
- package/hooks/useAgentSession.test.ts +12 -1
- package/hooks/useAgentSession.ts +6 -4
- package/hooks/useUrlSync.test.ts +13 -0
- package/hooks/useUrlSync.ts +0 -4
- package/lib/agents/citation-checker.test.ts +40 -2
- package/lib/agents/citation-checker.ts +56 -11
- package/lib/agents/context-boundary.ts +5 -6
- package/lib/agents/instruction-edits.test.ts +120 -0
- package/lib/agents/instruction-edits.ts +65 -42
- package/lib/agents/prepare/request.ts +0 -2
- package/lib/agents/prepare/system-prompt.test.ts +10 -0
- package/lib/agents/prepare/system-prompt.ts +8 -2
- package/lib/agents/proposals.test.ts +76 -0
- package/lib/agents/proposals.ts +13 -5
- package/lib/agents/run-thread.auto-boundary.test.ts +125 -5
- package/lib/agents/run-thread.ts +24 -9
- package/lib/agents/sqlite-checkpoint-saver.test.ts +91 -0
- package/lib/agents/warm-summary-background.ts +22 -2
- package/lib/agents/warm-topics.test.ts +155 -1
- package/lib/agents/warm-topics.ts +27 -7
- package/lib/bridges/dispatcher.ts +1 -0
- package/lib/bridges/message-role.test.ts +81 -0
- package/lib/bridges/message-role.ts +23 -10
- package/lib/bridges/types.ts +6 -0
- package/lib/bridges/whatsapp-link.test.ts +32 -0
- package/lib/bridges/whatsapp-link.ts +21 -0
- package/lib/bridges/whatsapp.inbound.test.ts +239 -0
- package/lib/bridges/whatsapp.sender.test.ts +80 -1
- package/lib/bridges/whatsapp.ts +76 -38
- package/lib/db/migrations.test.ts +61 -0
- package/lib/db/migrations.ts +13 -0
- package/lib/embeddings/performance.test.ts +114 -0
- package/lib/pricing/snapshot.test.ts +70 -0
- package/lib/pricing/snapshot.ts +10 -2
- package/lib/proxy/dispatcher.test.ts +119 -1
- package/lib/proxy/dispatcher.ts +22 -9
- package/lib/stores/agent-configs.ts +10 -5
- package/lib/stores/bridges.test.ts +81 -0
- package/lib/stores/bridges.ts +9 -8
- package/lib/stores/dashboard-metrics-cache.test.ts +99 -0
- package/lib/stores/dashboard-metrics.ts +30 -4
- package/lib/stores/message-embedding-jobs.test.ts +27 -0
- package/lib/stores/message-embedding-jobs.ts +5 -3
- package/lib/stores/message-usage.test.ts +21 -1
- package/lib/stores/pricing-cache.test.ts +31 -0
- package/lib/stores/pricing.ts +3 -0
- package/lib/stores/proxy-config.test.ts +120 -0
- package/lib/stores/proxy-config.ts +22 -4
- package/lib/stores/threads.ts +12 -8
- package/lib/tools/communications/calendar.test.ts +43 -2
- package/lib/tools/communications/calendar.ts +2 -0
- package/lib/tools/communications/gmail.test.ts +51 -1
- package/lib/tools/communications/gmail.ts +44 -2
- package/lib/tools/communications/outlook-calendar.ts +2 -0
- package/lib/tools/communications/outlook.test.ts +33 -2
- package/lib/tools/communications/outlook.ts +2 -0
- package/lib/tools/filesystem/edit-match.test.ts +133 -0
- package/lib/tools/filesystem/edit-match.ts +7 -2
- package/lib/tools/packages/default-packages.test.ts +21 -0
- package/lib/tools/packages/optional-package.test.ts +72 -0
- package/lib/tools/packages/optional-package.ts +33 -4
- package/lib/tools/support/result-refs.test.ts +131 -0
- package/lib/tools/support/result-refs.ts +41 -9
- package/lib/ui/navigate.test.ts +13 -0
- package/lib/ui/navigate.ts +10 -2
- package/lib/utils/source-link.test.ts +51 -0
- package/lib/utils/source-link.ts +45 -0
- package/package.json +1 -1
- package/.next/standalone/.next/static/css/312542a5725ba0d5.css +0 -5
- /package/.next/standalone/.next/static/{bMRK5y3ruT7c3WtBfCQOG → 544OFJV-q0RC6Gjgje4LT}/_buildManifest.js +0 -0
- /package/.next/standalone/.next/static/{bMRK5y3ruT7c3WtBfCQOG → 544OFJV-q0RC6Gjgje4LT}/_ssgManifest.js +0 -0
|
@@ -7,6 +7,7 @@
|
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import { NextRequest, NextResponse } from "next/server";
|
|
10
|
+
import { z } from "zod";
|
|
10
11
|
import {
|
|
11
12
|
deleteThread,
|
|
12
13
|
getMessagesAfter,
|
|
@@ -23,6 +24,7 @@ import { parseStoredTopics } from "@/lib/agents/conversation-summary";
|
|
|
23
24
|
import { getThreadActivity } from "@/lib/agents/thread-activity";
|
|
24
25
|
import { getCheckpointer } from "@/lib/agents/checkpointer";
|
|
25
26
|
import { errorMessage } from "@/lib/utils/error";
|
|
27
|
+
import { errorResponse, noStoreJson } from "@/lib/api/responses";
|
|
26
28
|
|
|
27
29
|
type Params = { params: Promise<{ thread_id: string }> };
|
|
28
30
|
|
|
@@ -41,6 +43,9 @@ export async function GET(req: NextRequest, { params }: Params) {
|
|
|
41
43
|
if (!thread) return NextResponse.json({ error: "Thread not found" }, { status: 404 });
|
|
42
44
|
|
|
43
45
|
const url = new URL(req.url);
|
|
46
|
+
const view = z.enum(["messages", "metadata"]).default("messages")
|
|
47
|
+
.safeParse(url.searchParams.get("view") ?? undefined);
|
|
48
|
+
if (!view.success) return errorResponse("Invalid thread view", 400);
|
|
44
49
|
const limit = Math.max(1, Math.min(200, Number(url.searchParams.get("limit")) || DEFAULT_PAGE));
|
|
45
50
|
// Cursors are the message `seq` (SQLite rowid) — a monotonic order key,
|
|
46
51
|
// not a timestamp — so pagination can't skip/duplicate rows on a
|
|
@@ -48,28 +53,6 @@ export async function GET(req: NextRequest, { params }: Params) {
|
|
|
48
53
|
const before = parseSeqParam(url.searchParams.get("before"));
|
|
49
54
|
const after = parseSeqParam(url.searchParams.get("after"));
|
|
50
55
|
|
|
51
|
-
// `after` is a forward-fetch shortcut: caller (typically ChatView's
|
|
52
|
-
// run-completion handler) already has the recent messages and just wants
|
|
53
|
-
// the freshly-persisted ones. has_more is fixed to false because the
|
|
54
|
-
// forward window is not paginated — caller already has everything older.
|
|
55
|
-
let messages: MessageRow[];
|
|
56
|
-
let has_more: boolean;
|
|
57
|
-
if (after !== undefined) {
|
|
58
|
-
messages = getMessagesAfter(thread_id, after, limit);
|
|
59
|
-
has_more = false;
|
|
60
|
-
} else {
|
|
61
|
-
const page = getMessagesPage(thread_id, limit, before);
|
|
62
|
-
messages = page.messages;
|
|
63
|
-
has_more = page.has_more;
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
// Attach per-assistant-turn usage so the chat UI can render a context
|
|
67
|
-
// utilisation bar without an extra round-trip per message. User turns
|
|
68
|
-
// and legacy assistant rows (pre-message_usage table) simply omit the
|
|
69
|
-
// field.
|
|
70
|
-
const assistantIds = messages.filter((m) => m.role === "assistant").map((m) => m.msg_id);
|
|
71
|
-
const usageById = getMessageUsageByIds(assistantIds);
|
|
72
|
-
|
|
73
56
|
// Resolve the agent's effective context-window size once for the whole
|
|
74
57
|
// thread. The bar scales against this cap so the visual matches the
|
|
75
58
|
// budget the agent applies at run time.
|
|
@@ -82,7 +65,7 @@ export async function GET(req: NextRequest, { params }: Params) {
|
|
|
82
65
|
DEFAULT_CONTEXT_WINDOW_TOKENS,
|
|
83
66
|
);
|
|
84
67
|
|
|
85
|
-
|
|
68
|
+
const metadata = {
|
|
86
69
|
...thread,
|
|
87
70
|
warm_summary_topics: parseStoredTopics(thread.warm_summary_topics),
|
|
88
71
|
warm_channel_summaries: currentChannelSummaries(thread_id, thread.hot_since_seq),
|
|
@@ -91,6 +74,26 @@ export async function GET(req: NextRequest, { params }: Params) {
|
|
|
91
74
|
pending_hot_since_seq: pendingCompactionBoundarySeq(thread_id),
|
|
92
75
|
manual_compaction_pending: isManualCompactionRunning(thread_id),
|
|
93
76
|
activity: getThreadActivity(thread_id),
|
|
77
|
+
};
|
|
78
|
+
if (view.data === "metadata") return noStoreJson(metadata);
|
|
79
|
+
|
|
80
|
+
// `after` is a forward-fetch shortcut: caller already has the recent
|
|
81
|
+
// messages and just wants the freshly persisted ones.
|
|
82
|
+
let messages: MessageRow[];
|
|
83
|
+
let has_more: boolean;
|
|
84
|
+
if (after !== undefined) {
|
|
85
|
+
messages = getMessagesAfter(thread_id, after, limit);
|
|
86
|
+
has_more = false;
|
|
87
|
+
} else {
|
|
88
|
+
const page = getMessagesPage(thread_id, limit, before);
|
|
89
|
+
messages = page.messages;
|
|
90
|
+
has_more = page.has_more;
|
|
91
|
+
}
|
|
92
|
+
const assistantIds = messages.filter((m) => m.role === "assistant").map((m) => m.msg_id);
|
|
93
|
+
const usageById = getMessageUsageByIds(assistantIds);
|
|
94
|
+
|
|
95
|
+
return noStoreJson({
|
|
96
|
+
...metadata,
|
|
94
97
|
// No server-side filtering: clients receive every message with its
|
|
95
98
|
// `category` tag and apply the chat-panel filter toolbar on the
|
|
96
99
|
// render side. Keeping the raw transcript over the wire means audit
|
|
@@ -189,7 +189,6 @@ export async function POST(req: NextRequest, { params }: Params) {
|
|
|
189
189
|
_pinned_model_config_name: pinnedModelConfigName,
|
|
190
190
|
_onCompacting: () => broadcast(active, { type: "status", data: { phase: "compacting", label: "Compacting earlier messages…" } }),
|
|
191
191
|
_user_message_persisted: true,
|
|
192
|
-
_message_count_before_user: admittedTurn.messageCountBefore,
|
|
193
192
|
_idle_ms_before_user: admittedTurn.idleMsBefore,
|
|
194
193
|
});
|
|
195
194
|
prepDurationMs = Date.now() - prepStartedAt;
|
package/app/globals.css
CHANGED
|
@@ -115,6 +115,23 @@
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
|
|
118
|
+
html[data-accent-color="blue"] {
|
|
119
|
+
--color-accent: #2563eb;
|
|
120
|
+
--color-accent-hover: #1d4ed8;
|
|
121
|
+
}
|
|
122
|
+
html[data-accent-color="teal"] {
|
|
123
|
+
--color-accent: #0f766e;
|
|
124
|
+
--color-accent-hover: #115e59;
|
|
125
|
+
}
|
|
126
|
+
html[data-accent-color="violet"] {
|
|
127
|
+
--color-accent: #6d28d9;
|
|
128
|
+
--color-accent-hover: #5b21b6;
|
|
129
|
+
}
|
|
130
|
+
html[data-accent-color="rose"] {
|
|
131
|
+
--color-accent: #be123c;
|
|
132
|
+
--color-accent-hover: #9f1239;
|
|
133
|
+
}
|
|
134
|
+
|
|
118
135
|
/* Tailwind v4 changed the default border colour from gray-200 to
|
|
119
136
|
* currentColor. Restore the v3 behaviour so existing `border` shorthand
|
|
120
137
|
* (without an explicit colour) keeps rendering as the themed divider. */
|
|
@@ -195,31 +212,27 @@ body {
|
|
|
195
212
|
* pronounced top sheen. *
|
|
196
213
|
* ---------------------------------------------------------------------- */
|
|
197
214
|
.glass {
|
|
198
|
-
background-color: rgba(24, 24, 27, 0.
|
|
199
|
-
background-image:
|
|
200
|
-
|
|
201
|
-
rgba(255, 255, 255, 0.
|
|
202
|
-
transparent 65%
|
|
203
|
-
);
|
|
215
|
+
background-color: rgba(24, 24, 27, 0.38);
|
|
216
|
+
background-image:
|
|
217
|
+
linear-gradient(135deg, rgba(255, 255, 255, 0.12) 0%, rgba(255, 255, 255, 0.035) 34%, transparent 68%),
|
|
218
|
+
linear-gradient(180deg, rgba(255, 255, 255, 0.08) 0%, transparent 68%);
|
|
204
219
|
-webkit-backdrop-filter: blur(24px) saturate(2);
|
|
205
220
|
backdrop-filter: blur(24px) saturate(2);
|
|
206
221
|
box-shadow:
|
|
207
|
-
inset 0 1px 0 0 rgba(255, 255, 255, 0.
|
|
208
|
-
inset 0 -1px 0 0 rgba(255, 255, 255, 0.
|
|
209
|
-
inset 0 0 0 1px rgba(255, 255, 255, 0.
|
|
222
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.25),
|
|
223
|
+
inset 0 -1px 0 0 rgba(255, 255, 255, 0.06),
|
|
224
|
+
inset 0 0 0 1px rgba(255, 255, 255, 0.07);
|
|
210
225
|
}
|
|
211
226
|
.glass-elevated {
|
|
212
|
-
background-color: rgba(24, 24, 27, 0.
|
|
213
|
-
background-image:
|
|
214
|
-
|
|
215
|
-
rgba(255, 255, 255, 0.
|
|
216
|
-
transparent 60%
|
|
217
|
-
);
|
|
227
|
+
background-color: rgba(24, 24, 27, 0.5);
|
|
228
|
+
background-image:
|
|
229
|
+
linear-gradient(135deg, rgba(255, 255, 255, 0.16) 0%, rgba(255, 255, 255, 0.04) 34%, transparent 68%),
|
|
230
|
+
linear-gradient(180deg, rgba(255, 255, 255, 0.1) 0%, transparent 65%);
|
|
218
231
|
-webkit-backdrop-filter: blur(32px) saturate(2.1);
|
|
219
232
|
backdrop-filter: blur(32px) saturate(2.1);
|
|
220
233
|
box-shadow:
|
|
221
|
-
inset 0 1px 0 0 rgba(255, 255, 255, 0.
|
|
222
|
-
inset 0 0 0 1px rgba(255, 255, 255, 0.
|
|
234
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.3),
|
|
235
|
+
inset 0 0 0 1px rgba(255, 255, 255, 0.08),
|
|
223
236
|
0 24px 60px -12px rgba(0, 0, 0, 0.5);
|
|
224
237
|
}
|
|
225
238
|
.glass-bubble {
|
|
@@ -253,15 +266,19 @@ body {
|
|
|
253
266
|
* inner edge flips to a hairline of slate so the glass reads as raised
|
|
254
267
|
* rather than dissolved into the page. */
|
|
255
268
|
[data-theme="light"] .glass {
|
|
256
|
-
background-color: rgba(255, 255, 255, 0.
|
|
257
|
-
background-image:
|
|
269
|
+
background-color: rgba(255, 255, 255, 0.48);
|
|
270
|
+
background-image:
|
|
271
|
+
linear-gradient(135deg, rgba(255, 255, 255, 0.62) 0%, rgba(255, 255, 255, 0.18) 34%, transparent 68%),
|
|
272
|
+
linear-gradient(180deg, rgba(255, 255, 255, 0.28) 0%, transparent 65%);
|
|
258
273
|
box-shadow:
|
|
259
|
-
inset 0 1px 0 0 rgba(255, 255, 255, 0.
|
|
260
|
-
inset 0 0 0 1px rgba(15, 23, 42, 0.
|
|
274
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.98),
|
|
275
|
+
inset 0 0 0 1px rgba(15, 23, 42, 0.09);
|
|
261
276
|
}
|
|
262
277
|
[data-theme="light"] .glass-elevated {
|
|
263
|
-
background-color: rgba(255, 255, 255, 0.
|
|
264
|
-
background-image:
|
|
278
|
+
background-color: rgba(255, 255, 255, 0.58);
|
|
279
|
+
background-image:
|
|
280
|
+
linear-gradient(135deg, rgba(255, 255, 255, 0.7) 0%, rgba(255, 255, 255, 0.22) 34%, transparent 68%),
|
|
281
|
+
linear-gradient(180deg, rgba(255, 255, 255, 0.36) 0%, transparent 60%);
|
|
265
282
|
box-shadow:
|
|
266
283
|
inset 0 1px 0 0 rgba(255, 255, 255, 0.9),
|
|
267
284
|
inset 0 0 0 1px rgba(15, 23, 42, 0.07),
|
|
@@ -276,15 +293,19 @@ body {
|
|
|
276
293
|
}
|
|
277
294
|
@media (prefers-color-scheme: light) {
|
|
278
295
|
[data-theme="system"] .glass {
|
|
279
|
-
background-color: rgba(255, 255, 255, 0.
|
|
280
|
-
background-image:
|
|
296
|
+
background-color: rgba(255, 255, 255, 0.48);
|
|
297
|
+
background-image:
|
|
298
|
+
linear-gradient(135deg, rgba(255, 255, 255, 0.62) 0%, rgba(255, 255, 255, 0.18) 34%, transparent 68%),
|
|
299
|
+
linear-gradient(180deg, rgba(255, 255, 255, 0.28) 0%, transparent 65%);
|
|
281
300
|
box-shadow:
|
|
282
|
-
inset 0 1px 0 0 rgba(255, 255, 255, 0.
|
|
283
|
-
inset 0 0 0 1px rgba(15, 23, 42, 0.
|
|
301
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.98),
|
|
302
|
+
inset 0 0 0 1px rgba(15, 23, 42, 0.09);
|
|
284
303
|
}
|
|
285
304
|
[data-theme="system"] .glass-elevated {
|
|
286
|
-
background-color: rgba(255, 255, 255, 0.
|
|
287
|
-
background-image:
|
|
305
|
+
background-color: rgba(255, 255, 255, 0.58);
|
|
306
|
+
background-image:
|
|
307
|
+
linear-gradient(135deg, rgba(255, 255, 255, 0.7) 0%, rgba(255, 255, 255, 0.22) 34%, transparent 68%),
|
|
308
|
+
linear-gradient(180deg, rgba(255, 255, 255, 0.36) 0%, transparent 60%);
|
|
288
309
|
box-shadow:
|
|
289
310
|
inset 0 1px 0 0 rgba(255, 255, 255, 0.9),
|
|
290
311
|
inset 0 0 0 1px rgba(15, 23, 42, 0.07),
|
|
@@ -310,6 +331,75 @@ body {
|
|
|
310
331
|
}
|
|
311
332
|
}
|
|
312
333
|
|
|
334
|
+
[data-surface-style="calm"] body {
|
|
335
|
+
background-image: none;
|
|
336
|
+
}
|
|
337
|
+
[data-surface-style="calm"] .dashboard-atmosphere {
|
|
338
|
+
display: none;
|
|
339
|
+
}
|
|
340
|
+
[data-surface-style="calm"] .glass,
|
|
341
|
+
[data-surface-style="calm"] .glass-elevated {
|
|
342
|
+
-webkit-backdrop-filter: blur(14px) saturate(1.2);
|
|
343
|
+
backdrop-filter: blur(14px) saturate(1.2);
|
|
344
|
+
background-image: linear-gradient(180deg, rgba(255, 255, 255, 0.07) 0%, transparent 65%);
|
|
345
|
+
}
|
|
346
|
+
[data-surface-style="calm"] .glass-bubble,
|
|
347
|
+
[data-surface-style="calm"] .glass-bubble-accent {
|
|
348
|
+
-webkit-backdrop-filter: none;
|
|
349
|
+
backdrop-filter: none;
|
|
350
|
+
background-image: none;
|
|
351
|
+
}
|
|
352
|
+
[data-surface-style="calm"] .glass,
|
|
353
|
+
[data-surface-style="calm"] .glass-elevated {
|
|
354
|
+
background-color: color-mix(in srgb, var(--color-surface-2) 78%, transparent);
|
|
355
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-border) 70%, transparent);
|
|
356
|
+
}
|
|
357
|
+
[data-surface-style="calm"] .glass-elevated {
|
|
358
|
+
box-shadow:
|
|
359
|
+
inset 0 0 0 1px color-mix(in srgb, var(--color-border) 70%, transparent),
|
|
360
|
+
0 12px 32px -16px rgba(0, 0, 0, 0.35);
|
|
361
|
+
}
|
|
362
|
+
[data-surface-style="calm"] .glass-bubble {
|
|
363
|
+
background-color: var(--color-surface-3);
|
|
364
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-border) 50%, transparent);
|
|
365
|
+
}
|
|
366
|
+
[data-surface-style="calm"] .glass-bubble-accent {
|
|
367
|
+
background-color: color-mix(in srgb, var(--color-accent) 82%, var(--color-surface-2));
|
|
368
|
+
box-shadow: inset 0 0 0 1px color-mix(in srgb, var(--color-accent) 45%, transparent);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
[data-surface-style="liquid"] .glass,
|
|
372
|
+
[data-surface-style="liquid"] .glass-elevated {
|
|
373
|
+
background-color: color-mix(in srgb, var(--color-surface-2) 30%, transparent);
|
|
374
|
+
background-image:
|
|
375
|
+
radial-gradient(ellipse 120% 150% at 0% 0%, rgba(255, 255, 255, 0.2), transparent 54%),
|
|
376
|
+
linear-gradient(115deg, rgba(255, 255, 255, 0.18) 0%, rgba(255, 255, 255, 0.035) 24%, transparent 50%, color-mix(in srgb, var(--color-accent) 8%, transparent) 100%),
|
|
377
|
+
linear-gradient(180deg, rgba(255, 255, 255, 0.08) 0%, transparent 68%);
|
|
378
|
+
-webkit-backdrop-filter: blur(22px) saturate(1.65) contrast(1.06);
|
|
379
|
+
backdrop-filter: blur(22px) saturate(1.65) contrast(1.06);
|
|
380
|
+
box-shadow:
|
|
381
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.48),
|
|
382
|
+
inset 0 -1px 0 0 rgba(0, 0, 0, 0.08),
|
|
383
|
+
inset 0 0 0 1px rgba(255, 255, 255, 0.16),
|
|
384
|
+
0 8px 24px -18px rgba(0, 0, 0, 0.45);
|
|
385
|
+
}
|
|
386
|
+
[data-surface-style="liquid"] .glass-elevated {
|
|
387
|
+
background-color: color-mix(in srgb, var(--color-surface-2) 38%, transparent);
|
|
388
|
+
box-shadow:
|
|
389
|
+
inset 0 1px 0 0 rgba(255, 255, 255, 0.56),
|
|
390
|
+
inset 0 0 0 1px rgba(255, 255, 255, 0.2),
|
|
391
|
+
0 24px 60px -12px rgba(0, 0, 0, 0.42),
|
|
392
|
+
0 0 30px -22px color-mix(in srgb, var(--color-accent) 40%, transparent);
|
|
393
|
+
}
|
|
394
|
+
@supports not (backdrop-filter: blur(1px)) {
|
|
395
|
+
[data-surface-style="liquid"] .glass,
|
|
396
|
+
[data-surface-style="liquid"] .glass-elevated {
|
|
397
|
+
background-color: var(--color-surface-2);
|
|
398
|
+
background-image: none;
|
|
399
|
+
box-shadow: inset 0 0 0 1px var(--color-border);
|
|
400
|
+
}
|
|
401
|
+
}
|
|
402
|
+
|
|
313
403
|
/* Disable the blue tap flash on iOS / Android; we use explicit :active styles. */
|
|
314
404
|
* { -webkit-tap-highlight-color: transparent; }
|
|
315
405
|
|
package/app/layout.tsx
CHANGED
|
@@ -90,6 +90,18 @@ const themeBootstrap = `(() => {
|
|
|
90
90
|
var t = localStorage.getItem("jarela-theme");
|
|
91
91
|
if (t !== "light" && t !== "dark" && t !== "system") t = "system";
|
|
92
92
|
document.documentElement.setAttribute("data-theme", t);
|
|
93
|
+
var accent = localStorage.getItem("jarela-accent-color");
|
|
94
|
+
if (["blue", "teal", "violet", "rose"].includes(accent)) {
|
|
95
|
+
document.documentElement.setAttribute("data-accent-color", accent);
|
|
96
|
+
} else {
|
|
97
|
+
document.documentElement.removeAttribute("data-accent-color");
|
|
98
|
+
}
|
|
99
|
+
var surface = localStorage.getItem("jarela-surface-style");
|
|
100
|
+
if (surface === "calm" || surface === "ambient" || surface === "liquid") {
|
|
101
|
+
document.documentElement.setAttribute("data-surface-style", surface);
|
|
102
|
+
} else {
|
|
103
|
+
document.documentElement.removeAttribute("data-surface-style");
|
|
104
|
+
}
|
|
93
105
|
// Collapse any media-scoped theme-color metas Next emits to a single tag
|
|
94
106
|
// so the PWA window chrome matches the user's explicit choice (not just
|
|
95
107
|
// prefers-color-scheme). ThemeContext keeps this in sync on change.
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useEffect, useRef, useState } from "react";
|
|
3
|
-
import { Plus, Trash2 } from "lucide-react";
|
|
3
|
+
import { Bot, Plus, Trash2 } from "lucide-react";
|
|
4
4
|
import type { AgentConfig, AgentConfigIn } from "@/api/types";
|
|
5
5
|
import { api } from "@/api/client";
|
|
6
6
|
import { useAgents } from "@/hooks/useAgents";
|
|
7
7
|
import { useModels } from "@/hooks/useModels";
|
|
8
8
|
import { useDeepLinkScroll } from "@/hooks/useDeepLinkScroll";
|
|
9
|
-
import {
|
|
9
|
+
import { HeaderAction, PanelHeader } from "@/components/ui/PanelHeader";
|
|
10
10
|
import { AgentEditor } from "./AgentEditor";
|
|
11
11
|
import { useAppContext } from "@/contexts/AppContext";
|
|
12
12
|
|
|
@@ -100,17 +100,9 @@ export function AgentsPanel() {
|
|
|
100
100
|
return (
|
|
101
101
|
<div className="flex flex-col h-full min-h-0">
|
|
102
102
|
<>
|
|
103
|
-
{
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
<Button
|
|
107
|
-
onClick={() => setEditing("new")}
|
|
108
|
-
size="sm"
|
|
109
|
-
icon={<Plus size={11} />}
|
|
110
|
-
>
|
|
111
|
-
New
|
|
112
|
-
</Button>
|
|
113
|
-
</div>
|
|
103
|
+
<PanelHeader icon={<Bot size={14} />} title="Agents">
|
|
104
|
+
<HeaderAction onClick={() => setEditing("new")} icon={<Plus size={14} />} label="New" />
|
|
105
|
+
</PanelHeader>
|
|
114
106
|
|
|
115
107
|
{/* List */}
|
|
116
108
|
<div ref={containerRef} className="flex-1 overflow-y-auto px-2 pb-2">
|
|
@@ -17,6 +17,7 @@ import { useChatErrorReporting } from "./useChatErrorReporting";
|
|
|
17
17
|
import { useChatQueue, type ChatQueueApi } from "./useChatQueue";
|
|
18
18
|
import { useThreadCrossDeviceSync } from "./useThreadCrossDeviceSync";
|
|
19
19
|
import { useThreadData } from "./useThreadData";
|
|
20
|
+
import { useThreadMetadataPolling } from "./useThreadMetadataPolling";
|
|
20
21
|
import { useThreadActivity } from "./useThreadActivity";
|
|
21
22
|
import { useUserProfileAndAgent } from "./useUserProfileAndAgent";
|
|
22
23
|
import { composerPlaceholder, finalizeRunFromServer } from "./chat-run-utils";
|
|
@@ -109,21 +110,11 @@ export function ChatView({ threadId, agentId, sessionLoading, sessionError, onMe
|
|
|
109
110
|
const currentThreadIdRef = useRef(threadId);
|
|
110
111
|
currentThreadIdRef.current = threadId;
|
|
111
112
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
void api.threads.get(threadId, { limit: 1 }).then((d) => {
|
|
118
|
-
if (!cancelled) applyThreadMeta(thread.metaApplier, d);
|
|
119
|
-
}).catch(() => { /* best-effort */ });
|
|
120
|
-
}, 2000);
|
|
121
|
-
return () => {
|
|
122
|
-
cancelled = true;
|
|
123
|
-
window.clearInterval(timer);
|
|
124
|
-
};
|
|
125
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
126
|
-
}, [threadId, sse.streaming]);
|
|
113
|
+
useThreadMetadataPolling(
|
|
114
|
+
threadId,
|
|
115
|
+
sse.streaming || thread.warmSummaryPending || thread.compactionPending || thread.manualCompactionPending,
|
|
116
|
+
thread.metaApplier,
|
|
117
|
+
);
|
|
127
118
|
|
|
128
119
|
useThreadCrossDeviceSync({
|
|
129
120
|
threadId,
|
|
@@ -262,9 +253,9 @@ export function ChatView({ threadId, agentId, sessionLoading, sessionError, onMe
|
|
|
262
253
|
thread.metaApplier.setWarmSummaryTopics?.(parseWarmSummaryTopics(result.warm_summary_topics));
|
|
263
254
|
// The compact response carries only the chat recap; channel recaps
|
|
264
255
|
// (bridge, watcher, scheduled task) come with the thread metadata.
|
|
265
|
-
void api.threads.
|
|
256
|
+
void api.threads.metadata(compactedThreadId)
|
|
266
257
|
.then((d) => { if (stillCurrent()) applyThreadMeta(thread.metaApplier, d); })
|
|
267
|
-
.catch(() =>
|
|
258
|
+
.catch((err) => console.error("compaction metadata refresh failed", err));
|
|
268
259
|
}
|
|
269
260
|
notifyCompaction(result);
|
|
270
261
|
} catch (err) {
|
|
@@ -296,7 +287,7 @@ export function ChatView({ threadId, agentId, sessionLoading, sessionError, onMe
|
|
|
296
287
|
});
|
|
297
288
|
|
|
298
289
|
return (
|
|
299
|
-
<div className="flex
|
|
290
|
+
<div className="flex h-full min-h-0 w-full min-w-0 flex-col">
|
|
300
291
|
<MessageList
|
|
301
292
|
threadId={threadId}
|
|
302
293
|
messages={thread.messages}
|
|
@@ -366,7 +366,7 @@ export function InputBar({ attachments, onAttachmentsChange, onSubmit, onQueue,
|
|
|
366
366
|
)}
|
|
367
367
|
|
|
368
368
|
<textarea
|
|
369
|
-
className="flex-1 resize-none bg-surface-3/60 text-fg text-sm rounded-xl px-3 py-2 border border-border/60 focus:outline-none focus:ring-1 focus:ring-accent focus:border-accent/40 placeholder:text-fg-faint max-h-[84px] min-h-[44px] transition-colors"
|
|
369
|
+
className="flex-1 min-w-0 resize-none bg-surface-3/60 text-fg text-sm rounded-xl px-3 py-2 border border-border/60 focus:outline-none focus:ring-1 focus:ring-accent focus:border-accent/40 placeholder:text-fg-faint max-h-[84px] min-h-[44px] transition-colors"
|
|
370
370
|
ref={textareaRef}
|
|
371
371
|
placeholder={placeholder ?? "Message…"}
|
|
372
372
|
rows={1}
|
|
@@ -849,6 +849,18 @@ function BridgeMessageCard({ ctx, activity }: {
|
|
|
849
849
|
{activity.occurrence_count > 1 ? ` · ${activity.occurrence_count} checks` : ""}
|
|
850
850
|
</span>
|
|
851
851
|
) : null}
|
|
852
|
+
{ctx.chatLink ? (
|
|
853
|
+
<a
|
|
854
|
+
href={ctx.chatLink}
|
|
855
|
+
target="_blank"
|
|
856
|
+
rel="noopener noreferrer"
|
|
857
|
+
className="inline-flex items-center gap-1 px-1.5 py-0.5 rounded-full bg-white/15 hover:bg-white/25 text-[9.5px] shrink-0"
|
|
858
|
+
title={`Open this chat (${ctx.chatLink})`}
|
|
859
|
+
>
|
|
860
|
+
<ExternalLink size={9} />
|
|
861
|
+
open chat
|
|
862
|
+
</a>
|
|
863
|
+
) : null}
|
|
852
864
|
</>
|
|
853
865
|
);
|
|
854
866
|
const sections: Array<{ label: string; content: ReactNode; defaultOpen?: boolean; hint?: string }> = [
|
|
@@ -2028,7 +2040,7 @@ export const MessageBubble = memo(function MessageBubble({ message, attachedActi
|
|
|
2028
2040
|
)}
|
|
2029
2041
|
</div>
|
|
2030
2042
|
|
|
2031
|
-
<div className={`flex flex-col min-w-0 ${containsImageAttachment ? "max-w-[calc(100%-2.25rem)] sm:max-w-[92%] md:max-w-[86%]" : "max-w-[
|
|
2043
|
+
<div className={`flex flex-col min-w-0 ${!isUser ? "flex-1 items-start" : containsImageAttachment ? "max-w-[calc(100%-2.25rem)] sm:max-w-[92%] md:max-w-[86%] items-end" : "max-w-[calc(100%-2.25rem)] sm:max-w-[75%] items-end"}`}>
|
|
2032
2044
|
{(timeLabel || messageId) && (
|
|
2033
2045
|
<div className={`flex items-center gap-1 mb-0.5 px-1 opacity-0 group-hover:opacity-100 pointer-coarse:opacity-100 transition-opacity ${isUser ? "flex-row-reverse" : ""}`}>
|
|
2034
2046
|
{timeLabel && (
|
|
@@ -4,14 +4,16 @@ import { render, screen, fireEvent, within, waitFor } from "@testing-library/rea
|
|
|
4
4
|
import { MessageList } from "./MessageList";
|
|
5
5
|
import type { Message } from "@/api/types";
|
|
6
6
|
|
|
7
|
+
const renderCounts = vi.hoisted(() => new Map<string, number>());
|
|
7
8
|
vi.mock("./MessageBubble", () => ({
|
|
8
9
|
MessageBubble: ({ message, attachedActivity, showAvatar }: {
|
|
9
|
-
message: Pick<Message, "content">;
|
|
10
|
+
message: Pick<Message, "id" | "content">;
|
|
10
11
|
attachedActivity?: { source_kind: string };
|
|
11
12
|
showAvatar?: boolean;
|
|
12
|
-
}) =>
|
|
13
|
-
|
|
14
|
-
|
|
13
|
+
}) => {
|
|
14
|
+
renderCounts.set(message.id, (renderCounts.get(message.id) ?? 0) + 1);
|
|
15
|
+
return <div data-activity-kind={attachedActivity?.source_kind} data-show-avatar={String(showAvatar)}>{message.content}</div>;
|
|
16
|
+
},
|
|
15
17
|
}));
|
|
16
18
|
|
|
17
19
|
vi.mock("./ToolList", () => ({
|
|
@@ -65,6 +67,45 @@ function installPointerCapture(button: HTMLButtonElement) {
|
|
|
65
67
|
}
|
|
66
68
|
|
|
67
69
|
describe("MessageList conversation focus", () => {
|
|
70
|
+
it("does not re-render historical rows as streaming tokens or thinking arrive", () => {
|
|
71
|
+
renderCounts.clear();
|
|
72
|
+
const messages = [
|
|
73
|
+
mkMessage("history1", "user", "earlier prompt", "2026-10-18T08:00:00.000Z"),
|
|
74
|
+
mkMessage("history2", "assistant", "earlier answer", "2026-10-18T08:00:01.000Z"),
|
|
75
|
+
];
|
|
76
|
+
const { rerender } = render(
|
|
77
|
+
<MessageList messages={messages} streaming streamingContent="A" thinkingContent="thinking" />,
|
|
78
|
+
);
|
|
79
|
+
const firstCounts = messages.map((m) => renderCounts.get(m.id));
|
|
80
|
+
rerender(<MessageList messages={messages} streaming streamingContent="A longer reply" thinkingContent="more thinking" />);
|
|
81
|
+
expect(messages.map((m) => renderCounts.get(m.id))).toEqual(firstCounts);
|
|
82
|
+
expect(screen.getByText("A longer reply")).toBeTruthy();
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it("updates history when a persisted message changes", () => {
|
|
86
|
+
const messages = [mkMessage("history3", "assistant", "before edit", "2026-10-18T08:00:00.000Z")];
|
|
87
|
+
const { rerender } = render(<MessageList messages={messages} />);
|
|
88
|
+
rerender(<MessageList messages={[{ ...messages[0], content: "after edit" }]} />);
|
|
89
|
+
expect(screen.getByText("after edit")).toBeTruthy();
|
|
90
|
+
expect(screen.queryByText("before edit")).toBeNull();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it("preserves historical message nodes when the context boundary moves", () => {
|
|
94
|
+
const messages = [
|
|
95
|
+
mkMessage("history4", "user", "first message", "2026-10-18T08:00:00.000Z"),
|
|
96
|
+
mkMessage("history5", "assistant", "second message", "2026-10-18T08:00:01.000Z"),
|
|
97
|
+
];
|
|
98
|
+
const { container, rerender } = render(<MessageList messages={messages} hotSinceSeq={4} />);
|
|
99
|
+
const first = container.querySelector('[data-message-id="history4"]');
|
|
100
|
+
const second = container.querySelector('[data-message-id="history5"]');
|
|
101
|
+
rerender(<MessageList messages={messages} hotSinceSeq={5} />);
|
|
102
|
+
expect(container.querySelector('[data-message-id="history4"]')).toBe(first);
|
|
103
|
+
expect(container.querySelector('[data-message-id="history5"]')).toBe(second);
|
|
104
|
+
const boundary = container.querySelector("[data-focus-boundary='1']");
|
|
105
|
+
expect(first!.compareDocumentPosition(boundary!) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
|
106
|
+
expect(boundary!.compareDocumentPosition(second!) & Node.DOCUMENT_POSITION_FOLLOWING).toBeTruthy();
|
|
107
|
+
});
|
|
108
|
+
|
|
68
109
|
it.each([
|
|
69
110
|
["bridge", "bridge-1:chat-1", "user", "bridge", { bridge_conversation: { key: "bridge-1:chat-1" } }],
|
|
70
111
|
["scheduled_task", "task-1", "assistant", "scheduled_task", undefined],
|