feat(web+nomos): fix chat streaming reactivity, unified activity timeline, tool cards in chat
Some checks failed
ci / build-test (push) Has been cancelled
ci / docker-build (push) Has been cancelled
ci / web (push) Has been cancelled
Desktop App / Build Linux (amd64) (push) Has been cancelled
Desktop App / Attach to Release (push) Has been cancelled

- fix(web): Svelte 5 identity-based reactivity broke text_delta streaming —
  immutable message objects in all three chat handlers so text streams live
- feat(web): streaming cursor + inline status indicator merged into message flow
- feat(web): expandable inline tool call cards in chat thread
- feat(web): merge Plan + Event log into one backbone Activity timeline —
  filled status nodes, branch stubs, auto-scroll follow mode, per-session
  activityLog, compact for the rail
- fix(nomos): add X-Accel-Buffering:no to /chat SSE (proxy buffering)
- fix(nomos): plan step auto-close SQL param bug (store.go)
- polish: timestamps, role labels, code copy button, table overflow, min
  window size, delete AgentIndicator/ActivityTimeline dead code
This commit is contained in:
2026-07-21 07:49:02 +02:00
parent 55b93c59ef
commit ce34cfeac7
13 changed files with 742 additions and 392 deletions

View File

@@ -1,13 +1,12 @@
<script lang="ts">
// Floating-window content for a task/session — the per-window counterpart
// to the main Chat page (thread + rail), fully self-contained per
// Floating-window content for a task/session — self-contained per
// sessionId via chat.ts's chatFor()/loadSessionChat()/sendSessionMessage()
// and workspace.ts's workspaceFor()/startSessionWorkspace(), so several of
// these can be open (and independently live) at once without the "which
// one's on screen" guarding the main page's singleton stores need.
// these can be open (and independently live) at once.
import { onDestroy, onMount } from 'svelte'
import { Pane, Splitpanes } from 'svelte-splitpanes'
import { chatFor, loadSessionChat, sendSessionMessage, cancelSessionStream, stopSessionPolling, dismissError, chatErrors } from '$lib/stores/chat'
import { activityLogFor } from '$lib/stores/activity'
import ChatThread from '$lib/components/ChatThread.svelte'
import TaskContextPanel from '$lib/components/TaskContextPanel.svelte'
@@ -16,12 +15,17 @@
// Svelte's `$store` auto-subscription only works on a plain identifier
// bound directly to a store, not a member expression — chatFor() returns
// an object of stores, so pull each one out into its own identifier here.
// sessionId is a stable prop (one per window mount, never changes), so
// capturing it at init is safe and intended.
// eslint-disable-next-line svelte/valid-compile
const chat = chatFor(sessionId)
const chatMessages = chat.messages
const chatStreaming = chat.streaming
const chatConnectionState = chat.connectionState
const chatError = chat.error
const chatNotFound = chat.notFound
// eslint-disable-next-line svelte/valid-compile
const sessionActivityLog = activityLogFor(sessionId)
let loading = $state(true)
onMount(async () => {
@@ -53,6 +57,7 @@
connectionState={$chatConnectionState}
error={$chatError}
chatErrors={$chatErrors}
activityLog={sessionActivityLog}
onSend={(text) => sendSessionMessage(sessionId, text)}
onCancel={() => cancelSessionStream(sessionId)}
onReconnect={() => loadSessionChat(sessionId)}