Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
ab12d28
feat(renderer): edit SessionItem style
Mar 25, 2026
6d84b08
feat(renderer): replace SessionItem context menu with hover interaction
Mar 25, 2026
49d7851
feat(renderer): replace SessionItem context menu with hover interaction
Mar 25, 2026
93307a1
Merge branch 'ThinkInAIXYZ:dev' into dev
xiao-text Mar 25, 2026
4176c9b
Merge branch 'ThinkInAIXYZ:dev' into dev
xiao-text Mar 26, 2026
3e2367e
Merge branch 'ThinkInAIXYZ:dev' into dev
xiao-text Mar 26, 2026
644de94
Merge branch 'ThinkInAIXYZ:dev' into dev
xiao-text Mar 27, 2026
ce20091
Merge branch 'ThinkInAIXYZ:dev' into dev
xiao-text Jun 12, 2026
637aa0c
Merge branch 'ThinkInAIXYZ:dev' into dev
xiao-text Jul 13, 2026
cb46d8b
Merge branch 'ThinkInAIXYZ:dev' into dev
xiao-text Sep 7, 2026
de93bdd
Merge branch 'ThinkInAIXYZ:dev' into dev
xiao-text Sep 8, 2026
b27897a
fix(images): cache generated and tool-returned image previews to disk
Sep 8, 2026
c747cfa
fix(images): address review on cache signal and limits
zhangmo8 Sep 8, 2026
b1a7948
Merge remote-tracking branch 'origin/dev' into pr-2268
zhangmo8 Sep 8, 2026
6687499
style(images): apply oxfmt to cache tests
zhangmo8 Sep 8, 2026
4a2cd3a
fix(images): preserve previews and cancellation
zerob13 Sep 9, 2026
6cf73ad
Merge branch 'ThinkInAIXYZ:dev' into dev
xiao-text Sep 10, 2026
3f59ad2
feat(agent): reap orphaned child processes
Sep 10, 2026
eb193e8
Merge branch 'ThinkInAIXYZ:dev' into dev
xiao-text Sep 10, 2026
32fdf3d
fix(agent): harden orphaned child process recovery
Sep 10, 2026
34eb7b3
Merge branch 'dev' of https://github.com/xiao-text/deepchat into dev
Sep 10, 2026
ec03951
fix(agent): attest and await orphan recovery
zerob13 Sep 10, 2026
e90edf5
Merge branch 'ThinkInAIXYZ:dev' into dev
xiao-text Sep 11, 2026
00a678b
Merge branch 'ThinkInAIXYZ:dev' into dev
xiao-text Sep 11, 2026
9a46189
fix(renderer): bind store events at setup scope
Sep 11, 2026
518a77b
Merge branch 'ThinkInAIXYZ:dev' into dev
xiao-text Sep 12, 2026
ad8295a
perf(stream): dedupe snapshot serialization
Sep 12, 2026
19fa87d
fix(stream): apply the first revision-0 snapshot
Sep 13, 2026
3909058
fix(stream): advance and scope stream revisions
Sep 13, 2026
95aa76d
test(stream): drop stringify call-count assertion
Sep 14, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion src/main/agent/acp/compatibility/adapters.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
import { createAcpPromptTerminalEvents } from '@/agent/acp/runtime/acpContentMapper'
import {
createState,
markStreamChanged,
type DeepChatEventPublisher,
type DeepChatSessionUpdatePublisher,
type IoParams,
Expand Down Expand Up @@ -159,7 +160,7 @@ export class AcpCompatibilityProjectionAdapter implements AcpCompatibilityProjec
? block.extra.permissionType
: 'all'
markStreamingProviderPermissionResolved(block, granted, permissionType)
state.stream.dirty = true
markStreamChanged(state.stream)
this.flushIfDirty(state)
}

Expand Down
22 changes: 11 additions & 11 deletions src/main/agent/deepchat/runtime/accumulator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type {
ProviderUrlSourcePayload
} from '@shared/types/core/llm-events'
import type { ChatMessageProviderOptions } from '@shared/types/core/chat-message'
import type { StreamState } from './types'
import { markStreamChanged, type StreamState } from './types'

const MAX_VISIBLE_SEARCH_PAGES = 6

Expand Down Expand Up @@ -144,7 +144,7 @@ export function accumulate(state: StreamState, event: LLMCoreStreamEvent): void
if (state.firstTokenTime === null) state.firstTokenTime = Date.now()
const block = getCurrentBlock(state.blocks, 'content', event.provider_options)
block.content += event.content
state.dirty = true
markStreamChanged(state)
break
}
case 'reasoning': {
Expand All @@ -167,12 +167,12 @@ export function accumulate(state: StreamState, event: LLMCoreStreamEvent): void
}
const reasoningTime = block.reasoning_time as { start: number; end: number }
updateReasoningMetadata(state, reasoningTime.start, reasoningTime.end)
state.dirty = true
markStreamChanged(state)
break
}
case 'plan': {
if (finalizeTrailingPendingNarrativeBlocks(state.blocks)) {
state.dirty = true
markStreamChanged(state)
}
const revision = event.revision ?? (state.latestAgentPlanSnapshot?.revision ?? 0) + 1
state.latestAgentPlanSnapshot = {
Expand Down Expand Up @@ -209,7 +209,7 @@ export function accumulate(state: StreamState, event: LLMCoreStreamEvent): void
executionOwner: event.tool_call_execution_owner ?? 'deepchat',
providerOptions: event.provider_options
})
state.dirty = true
markStreamChanged(state)
break
}
case 'tool_call_chunk': {
Expand All @@ -229,7 +229,7 @@ export function accumulate(state: StreamState, event: LLMCoreStreamEvent): void
}
}
}
state.dirty = true
markStreamChanged(state)
}
break
}
Expand Down Expand Up @@ -265,7 +265,7 @@ export function accumulate(state: StreamState, event: LLMCoreStreamEvent): void
})
}
state.pendingToolCalls.delete(event.tool_call_id)
state.dirty = true
markStreamChanged(state)
}
break
}
Expand Down Expand Up @@ -296,12 +296,12 @@ export function accumulate(state: StreamState, event: LLMCoreStreamEvent): void
}
}
state.blocks.push(block)
state.dirty = true
markStreamChanged(state)
break
}
case 'provider_url_source': {
if (appendProviderUrlSource(state.blocks, event.provider_url_source)) {
state.dirty = true
markStreamChanged(state)
}
break
}
Expand All @@ -318,7 +318,7 @@ export function accumulate(state: StreamState, event: LLMCoreStreamEvent): void
}
}
state.blocks.push(block)
state.dirty = true
markStreamChanged(state)
break
}
case 'usage': {
Expand Down Expand Up @@ -354,7 +354,7 @@ export function accumulate(state: StreamState, event: LLMCoreStreamEvent): void
if (block.status === 'pending') block.status = 'error'
}
state.stopReason = 'error'
state.dirty = true
markStreamChanged(state)
break
}
default:
Expand Down
3 changes: 3 additions & 0 deletions src/main/agent/deepchat/runtime/deepChatLoopRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -661,6 +661,7 @@ export function buildTapeViewSelection(

export class DeepChatLoopRunner {
private readonly toolSurfaceAdapterHistory = new ToolSurfaceAdapterHistory()
private rateLimitRevision = 0

constructor(private readonly ports: DeepChatLoopRunnerPorts) {}

Expand Down Expand Up @@ -2726,6 +2727,7 @@ export class DeepChatLoopRunner {
sessionId,
messageId,
updatedAt: Date.now(),
revision: ++this.rateLimitRevision,
blocks: cloneBlocksForRenderer([block])
})
}
Expand All @@ -2737,6 +2739,7 @@ export class DeepChatLoopRunner {
sessionId,
messageId,
updatedAt: Date.now(),
revision: ++this.rateLimitRevision,
blocks: []
})
}
Expand Down
42 changes: 23 additions & 19 deletions src/main/agent/deepchat/runtime/dispatch.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import type {
ToolCallResult,
ToolDispatchCollaborators
} from './types'
import { markStreamChanged } from './types'
import type {
ChatMessage,
ChatMessageProviderOptions,
Expand Down Expand Up @@ -1253,7 +1254,7 @@ function finalizePendingNarrativeBeforeToolSettlement(state: StreamState): void
}

finalizeTrailingPendingNarrativeBlocks(state.blocks)
state.dirty = true
markStreamChanged(state)
}

function applyFinalizedToolResults(params: {
Expand Down Expand Up @@ -1387,7 +1388,7 @@ function applyFinalizedToolResults(params: {
}
}

state.dirty = true
markStreamChanged(state)
return interactions
}

Expand Down Expand Up @@ -1673,7 +1674,7 @@ async function reviewAutoApproveAction(params: {
}

if (setToolCallAutoApproveReviewing(batchToolCallBlocks, execution.completedToolCall.id, true)) {
state.dirty = true
markStreamChanged(state)
rendererFlushHandle.flush()
}
try {
Expand Down Expand Up @@ -1705,7 +1706,7 @@ async function reviewAutoApproveAction(params: {
if (
setToolCallAutoApproveReviewing(batchToolCallBlocks, execution.completedToolCall.id, false)
) {
state.dirty = true
markStreamChanged(state)
rendererFlushHandle.flush()
}
}
Expand Down Expand Up @@ -1772,7 +1773,7 @@ function appendPermissionActionBlock(
...(permission.rememberable === false ? { rememberable: false } : {})
}
})
state.dirty = true
markStreamChanged(state)
return {
type: 'permission',
origin,
Expand Down Expand Up @@ -1830,7 +1831,7 @@ function appendQuestionActionBlock(
...extra
}
})
state.dirty = true
markStreamChanged(state)
return {
type: 'question',
origin,
Expand Down Expand Up @@ -1896,8 +1897,8 @@ function appendSkillDraftQuestionActionBlock(
)
}

function flushBlocksToRenderer(io: IoParams, blocks: AssistantMessageBlock[]): void {
const renderedBlocks = cloneBlocksForRenderer(blocks)
function flushBlocksToRenderer(io: IoParams, state: StreamState): void {
const renderedBlocks = cloneBlocksForRenderer(state.blocks)
io.publishEvent('chat.stream.updated', {
kind: 'snapshot',
requestId: io.requestId,
Expand All @@ -1906,6 +1907,7 @@ function flushBlocksToRenderer(io: IoParams, blocks: AssistantMessageBlock[]): v
providerId: io.providerId,
modelId: io.modelId,
updatedAt: Date.now(),
revision: state.blocksRevision,
blocks: renderedBlocks
})

Expand All @@ -1914,10 +1916,10 @@ function flushBlocksToRenderer(io: IoParams, blocks: AssistantMessageBlock[]): v
kind: 'blocks',
updatedAt: Date.now(),
messageId: io.messageId,
previewMarkdown: buildAssistantPreviewMarkdown(blocks),
responseMarkdown: buildAssistantResponseMarkdown(blocks),
deliverySegments: buildAssistantDeliverySegments(io.messageId, blocks),
waitingInteraction: extractWaitingInteraction(blocks, io.messageId)
previewMarkdown: buildAssistantPreviewMarkdown(state.blocks),
responseMarkdown: buildAssistantResponseMarkdown(state.blocks),
deliverySegments: buildAssistantDeliverySegments(io.messageId, state.blocks),
waitingInteraction: extractWaitingInteraction(state.blocks, io.messageId)
})
}

Expand Down Expand Up @@ -2130,7 +2132,7 @@ async function runToolCall(params: {
}
state.latestAgentPlanSnapshot = snapshot
publishPlanUpdated(io, snapshot)
state.dirty = true
markStreamChanged(state)
scheduleRendererFlush(state, rendererFlushHandle)
return
}
Expand All @@ -2149,7 +2151,7 @@ async function runToolCall(params: {
update.responseMarkdown,
update.progressJson
)
state.dirty = true
markStreamChanged(state)
scheduleRendererFlush(state, rendererFlushHandle)
}

Expand Down Expand Up @@ -3204,7 +3206,7 @@ export async function settleToolBatch(
content: errorText
})
updateToolCallBlock(batchToolCallBlocks, tc.id, errorText, true)
state.dirty = true
markStreamChanged(state)
batchState.committedResultCallIds.add(tc.id)
executed += 1
persistToolExecutionState(io, state, rendererFlushHandle)
Expand Down Expand Up @@ -3534,7 +3536,7 @@ export function finalizePaused(state: StreamState, io: IoParams): void {
stampGenerationTiming(state)

io.messageStore.updateAssistantContent(io.messageId, state.blocks, JSON.stringify(state.metadata))
flushBlocksToRenderer(io, state.blocks)
flushBlocksToRenderer(io, state)
io.publishEvent('chat.stream.completed', {
requestId: io.requestId,
sessionId: io.sessionId,
Expand All @@ -3547,6 +3549,7 @@ export function finalize(state: StreamState, io: IoParams): void {
for (const block of state.blocks) {
if (block.status === 'pending') block.status = 'success'
}
markStreamChanged(state)
stampPlanTerminalIfOpen(state, io, state.planTerminalReason)

stampGenerationTiming(state)
Expand All @@ -3556,7 +3559,7 @@ export function finalize(state: StreamState, io: IoParams): void {
state.blocks,
JSON.stringify(state.metadata)
)
flushBlocksToRenderer(io, state.blocks)
flushBlocksToRenderer(io, state)
io.publishEvent('chat.stream.completed', {
requestId: io.requestId,
sessionId: io.sessionId,
Expand All @@ -3568,6 +3571,7 @@ export function finalize(state: StreamState, io: IoParams): void {
export function finalizeError(state: StreamState, io: IoParams, error: unknown): void {
const errorMessage = error instanceof Error ? error.message : String(error)
state.blocks = buildTerminalErrorBlocks(state.blocks, errorMessage)
markStreamChanged(state)
stampPlanTerminalIfOpen(
state,
io,
Expand All @@ -3577,7 +3581,7 @@ export function finalizeError(state: StreamState, io: IoParams, error: unknown):
stampGenerationTiming(state)

io.messageStore.setMessageError(io.messageId, state.blocks, JSON.stringify(state.metadata))
flushBlocksToRenderer(io, state.blocks)
flushBlocksToRenderer(io, state)
io.publishEvent('chat.stream.failed', {
requestId: io.requestId,
sessionId: io.sessionId,
Expand All @@ -3596,5 +3600,5 @@ export function persistAbortExceptionPlanState(state: StreamState, io: IoParams)
}

io.messageStore.updateAssistantContent(io.messageId, state.blocks)
flushBlocksToRenderer(io, state.blocks)
flushBlocksToRenderer(io, state)
}
1 change: 1 addition & 0 deletions src/main/agent/deepchat/runtime/echo.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ export function startEcho(state: StreamState, io: IoParams): EchoHandle {
providerId: io.providerId,
modelId: io.modelId,
updatedAt: Date.now(),
revision: state.blocksRevision,
blocks: renderedBlocks
})
}
Expand Down
Loading