v0.20.0: thinking blocks, chat windows overhaul, scroll fix
Backend: - Add isThinking flag to agentEvent for text before tool calls - Separate thinking from response text in runChatTurn and continue.go - Persist thinking in a dedicated field in message content Frontend: - Add thinking field to MessageContent, ChatMessage, ChatTextEvent types - Create ThinkingBlock.svelte — collapsible block with brain icon - SSE handler moves text_delta content to thinking on isThinking flag - Render thinking block between tools and response in ChatThread - Fix chat window scroll reset on focus change (stable windowKeys order) - Remove redundant #key id wrapper in WindowLayer - Enlarge sidebar rail (24→32 default, 40→60 max) - Remove glyph from sidebar, square graph at top - Replace AgentTrace/ToolCallCard/UnifiedTimeline with TurnTrace/ToolLine
This commit is contained in:
@@ -25,7 +25,7 @@ vi.mock('./execstream', () => ({
|
||||
liveExecutionOutputFor: vi.fn(() => writable(null))
|
||||
}))
|
||||
|
||||
import { computeActivityLog } from './activity'
|
||||
import { computeActivityLog, toolResultSummary } from './activity'
|
||||
import type { ChatMessage } from './chat'
|
||||
import type { PlanStep, Session } from '$lib/api'
|
||||
|
||||
@@ -172,3 +172,76 @@ describe('computeActivityLog generation awareness (F4)', () => {
|
||||
expect(entries.find((e) => e.id === 't1')!.stepSeq).toBeUndefined()
|
||||
})
|
||||
})
|
||||
|
||||
// toolResultSummary (chat interaction overhaul): a one-line, humanized outcome
|
||||
// per tool so each inline tool line reads as a result instead of raw JSON.
|
||||
describe('toolResultSummary', () => {
|
||||
type TR = NonNullable<ChatMessage['tools']>[number]
|
||||
const done = (name: string, result: unknown, args?: Record<string, unknown>): TR => ({
|
||||
type: 'tool_result',
|
||||
name,
|
||||
id: name,
|
||||
result,
|
||||
args
|
||||
})
|
||||
|
||||
it('is empty for a still-running call and for an errored one', () => {
|
||||
expect(toolResultSummary({ type: 'tool_use', name: 'run', id: 'r' })).toBe('')
|
||||
expect(
|
||||
toolResultSummary({ type: 'tool_result', name: 'run', id: 'r', error: 'boom' })
|
||||
).toBe('')
|
||||
})
|
||||
|
||||
it('parses run exit status', () => {
|
||||
expect(
|
||||
toolResultSummary(done('run', 'run on lxc:caddy: ERROR exit status 1'))
|
||||
).toContain('exit 1')
|
||||
})
|
||||
|
||||
it('summarizes a clean run with its first line', () => {
|
||||
const s = toolResultSummary(done('run', 'Active: active (running)'))
|
||||
expect(s.startsWith('ok')).toBe(true)
|
||||
expect(s).toContain('active')
|
||||
})
|
||||
|
||||
it('formats get_entity as slug (health)', () => {
|
||||
expect(
|
||||
toolResultSummary(done('get_entity', { slug: 'host:hubris', health: 'healthy' }))
|
||||
).toBe('host:hubris (healthy)')
|
||||
})
|
||||
|
||||
it('counts list results', () => {
|
||||
expect(
|
||||
toolResultSummary(done('list_entities', { entities: Array(10).fill({}) }))
|
||||
).toBe('10 entities')
|
||||
expect(toolResultSummary(done('list_lxcs', { containers: [1, 2] }))).toBe('2 containers')
|
||||
})
|
||||
|
||||
it('formats fleet health counts', () => {
|
||||
expect(
|
||||
toolResultSummary(
|
||||
done('get_health_summary', { health: { healthy: 5, degraded: 1, down: 0, unknown: 2 } })
|
||||
)
|
||||
).toBe('healthy 5 · degraded 1 · down 0 · unknown 2')
|
||||
})
|
||||
|
||||
it('extracts the knowledge slug from upsert_knowledge', () => {
|
||||
expect(
|
||||
toolResultSummary(done('upsert_knowledge', 'Saved document:nomos/foo-bar to the DB'))
|
||||
).toBe('recorded document:nomos/foo-bar')
|
||||
})
|
||||
|
||||
it('formats update_plan_step from args', () => {
|
||||
expect(
|
||||
toolResultSummary(done('update_plan_step', 'ok', { seq: 2, status: 'done' }))
|
||||
).toBe('step 2 → done')
|
||||
})
|
||||
|
||||
it('counts proposed plan steps', () => {
|
||||
expect(toolResultSummary(done('propose_plan', { steps: [{}, {}, {}] }))).toBe('3 steps')
|
||||
})
|
||||
|
||||
it('falls back to the first line for unmapped tools', () => {
|
||||
expect(toolResultSummary(done('some_new_tool', 'first line\nsecond line'))).toBe('first line')
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user