feat: align conversation state, composer and scoped approvals

This commit is contained in:
dot committed 2026-10-21 03:46:53 +09:00
1 parent 771ca540f7
commit 308b98df23
61 files changed
+1701 -292

No files matched your search

+10 -30
View File
@@ -95,10 +95,10 @@ const activePrefixSummary = buildCompletedFoldModel([
], { turnFinished: false })
assert.ok(activePrefixSummary)
assert.deepEqual(activePrefixSummary.summaryGroups.map(group => [group.startIndex, group.endIndex, group.shouldFold]), [
[0, 1, true], [3, 4, true]
[0, 1, true], [3, 4, false]
])
assert.equal(activePrefixSummary.summaryGroups[0].isRunning, true)
assert.equal(activePrefixSummary.segments.filter(segment => segment.kind === 'unit' && segment.unit.kind === 'summary').length, 2)
assert.equal(activePrefixSummary.summaryGroups[0].isRunning, false)
assert.equal(activePrefixSummary.segments.filter(segment => segment.kind === 'unit' && segment.unit.kind === 'summary').length, 1)
const initialInspectionMarkup = renderToStaticMarkup(<MessageBubble message={{
id: 'streaming-inspection', role: 'assistant', status: 'streaming', createdAt: 1,
@@ -127,27 +127,11 @@ const streamingInspectionMarkup = renderToStaticMarkup(<MessageBubble message={{
{ id: 'inspect-src', name: 'list_dir', status: 'stream_executing', args: { path: 'src' } }
]
}} />)
assert.equal((initialInspectionMarkup.match(/cr-streaming-activity/g) ?? []).length, 1)
assert.match(initialInspectionMarkup, /读取 接手状态-2026-10-15/)
assert.equal((streamingInspectionMarkup.match(/cr-streaming-activity/g) ?? []).length, 1,
'new tool calls replace the current activity row instead of nesting under the first action')
assert.match(streamingInspectionMarkup, /正在查看文件列表 src/)
assert.match(streamingInspectionMarkup, /data-activity-tool-id="inspect-src"/)
assert.match(streamingInspectionMarkup, /已处理 1s/,
'the elapsed duration starts at one second and is visible while the assistant turn is streaming')
assert.match(streamingInspectionMarkup, /cr-collapse--process cr-collapse--collapsed cr-live-event-feed/,
'the current action is an expandable event feed before the turn finishes')
assert.match(streamingInspectionMarkup, /aria-label="展开事件消息列表"/)
assert.match(streamingInspectionMarkup, /aria-controls="live-events-streaming-inspection-content"/)
assert.match(streamingInspectionMarkup, /data-event-type="reasoning"[^]*data-event-type="tool"/,
'reasoning and tool calls appear as ordered event messages')
assert.doesNotMatch(streamingInspectionMarkup, /过程消息/)
assert.match(streamingInspectionMarkup, /data-tool-name="list_dir"[^]*data-tool-name="read_file"/,
'all supported live tool details are mounted inside the independently expandable process section')
assert.doesNotMatch(streamingInspectionMarkup, /cr-collapse--summary/,
'streaming actions have no expandable parent summary')
assert.match(streamingInspectionMarkup, /<strong>Listing project, reading h/,
'deep-thinking details remain available and render Markdown instead of exposing raw markers')
assert.equal((initialInspectionMarkup.match(/cr-streaming-activity/g) ?? []).length, 0, 'unfinished tail is not a summary')
assert.equal((streamingInspectionMarkup.match(/cr-streaming-activity/g) ?? []).length, 0)
assert.match(streamingInspectionMarkup, /已处理 1s/)
assert.match(streamingInspectionMarkup, /data-tool-name="list_dir"[^]*data-tool-name="read_file"/)
assert.match(streamingInspectionMarkup, /<strong>Listing project, reading h/)
const completedInspectionMarkup = renderToStaticMarkup(<MessageBubble message={{
id: 'completed-inspection', role: 'assistant', status: 'done', createdAt: 1, content: 'Done.',
@@ -312,12 +296,8 @@ const streamingToolIntentMarkup = renderToStaticMarkup(<MessageBubble message={{
parts: [{ type: 'tool', toolId: 'list-src' }],
tools: [{ id: 'list-src', name: 'list_dir', status: 'running', intent: 'Inspecting source layout', args: { path: 'src' }, argsRaw: '{"path":"src"}' }]
}} />)
assert.match(streamingToolIntentMarkup, /正在查看文件列表 src/,
'the one live activity row uses the localized summary for the current tool call')
const liveIntentStatusRow = streamingToolIntentMarkup.match(/<div class="cr-captured-tool cr-streaming-activity"[^]*?<\/div><\/div>/)?.[0] ?? ''
assert.ok(liveIntentStatusRow)
assert.doesNotMatch(liveIntentStatusRow, /Inspecting source layout/,
'provider English intent stays out of the operation status line')
assert.doesNotMatch(streamingToolIntentMarkup, /cr-streaming-activity/, 'single live tool stays independent')
assert.match(streamingToolIntentMarkup, /data-tool-name="list_dir"/)
for (const status of ['cancelled', 'error'] as const) {
const terminalMarkup = renderToStaticMarkup(<MessageBubble message={{
id: `terminal-${status}`, role: 'assistant', status, createdAt: 1, turnDurationMs: 65_000,