Skip to content

Commit 90a3ac5

Browse files
committed
chore: release v0.3.9
1 parent 5925daf commit 90a3ac5

36 files changed

Lines changed: 11927 additions & 9216 deletions

CHANGELOG.md

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,21 @@ This project follows a pre-1.0 release flow. Minor versions may include breaking
66

77
## Unreleased
88

9+
## 0.3.9 - 2026-06-10
10+
11+
### Changed
12+
13+
- Improved assistant reply streaming with smoother text pacing and a hybrid Markdown preview while responses are still rendering.
14+
- Refined native agent tool-execution status copy so internal implementation wording is no longer shown in the user-facing transcript.
15+
- Upgraded Markdown file previews to use the shared React Markdown/GFM renderer with better table, quote, list, and code styling.
16+
17+
### Fixed
18+
19+
- Fixed completed assistant replies being duplicated when provider step snapshots repeated previously recorded text.
20+
- Fixed chat text selection being cleared by idle runtime refreshes, auto-scroll, or stable completed-message re-renders.
21+
- Fixed Cocos project launch checks so already-open projects are detected more reliably and are not opened a second time.
22+
- Fixed Cocos engine actions to skip opening another Cocos Creator instance when the current project is already connected through MCP.
23+
924
## 0.3.8 - 2026-06-08
1025

1126
### Changed

electron/main/agent-core/controller.ts

Lines changed: 53 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,9 @@ export function summarizeAgentRunControllerSnapshot(snapshot: AgentRunController
156156
};
157157
}
158158

159-
function resolveControllerContinuation(input: AgentRunControllerProviderStepInput): AgentRunControllerContinuation | undefined {
159+
function resolveControllerContinuation(
160+
input: AgentRunControllerProviderStepInput
161+
): AgentRunControllerContinuation | undefined {
160162
if (input.forceContinuation) {
161163
return input.forceContinuation;
162164
}
@@ -171,12 +173,10 @@ function resolveControllerContinuation(input: AgentRunControllerProviderStepInpu
171173
const incompleteCount = continuation?.incompleteTodo?.incompleteCount ?? 0;
172174
if (
173175
incompleteCount > 0 &&
174-
(
175-
!assistantMessage.trim() ||
176+
(!assistantMessage.trim() ||
176177
Boolean(continuation?.incompleteTodo?.hasInProgress) ||
177178
looksLikeUnfinishedAgentWriteReply(assistantMessage) ||
178-
looksLikeAgentTodoContinuationReply(assistantMessage)
179-
)
179+
looksLikeAgentTodoContinuationReply(assistantMessage))
180180
) {
181181
return {
182182
reason: 'incomplete_todo',
@@ -236,6 +236,31 @@ function createPartBase(options: {
236236
};
237237
}
238238

239+
function assistantTextPrefixCandidates(parts: AgentCoreMessagePart[]): string[] {
240+
const texts = parts
241+
.filter((part): part is Extract<AgentCoreMessagePart, { kind: 'assistant_text' }> => part.kind === 'assistant_text')
242+
.map((part) => part.text)
243+
.filter((text) => text.trim().length > 0);
244+
if (texts.length === 0) {
245+
return [];
246+
}
247+
return [...new Set([texts.join(''), texts.join('\n\n')])].sort((left, right) => right.length - left.length);
248+
}
249+
250+
function stripPreviouslyRecordedAssistantTextSnapshot(text: string, parts: AgentCoreMessagePart[]): string | undefined {
251+
const trimmed = text.trim();
252+
if (!trimmed) {
253+
return undefined;
254+
}
255+
for (const prefix of assistantTextPrefixCandidates(parts)) {
256+
if (prefix && trimmed.startsWith(prefix)) {
257+
const suffix = trimmed.slice(prefix.length).trimStart();
258+
return suffix.trim() ? suffix : undefined;
259+
}
260+
}
261+
return text;
262+
}
263+
239264
export function createAgentRunController(options: AgentRunControllerOptions = {}) {
240265
const createdAt = options.createdAt ?? (() => new Date().toISOString());
241266
const machine = createAgentCoreStateMachine(options.initialState);
@@ -257,7 +282,10 @@ export function createAgentRunController(options: AgentRunControllerOptions = {}
257282
nextSequence = Math.max(nextSequence, part.sequence + 1);
258283
}
259284

260-
function markToolCallStatus(toolUseId: string, status: Extract<AgentCoreMessagePart, { kind: 'tool_call' }>['status']): void {
285+
function markToolCallStatus(
286+
toolUseId: string,
287+
status: Extract<AgentCoreMessagePart, { kind: 'tool_call' }>['status']
288+
): void {
261289
const index = parts.findIndex((part) => part.kind === 'tool_call' && part.toolUseId === toolUseId);
262290
if (index >= 0) {
263291
const part = parts[index];
@@ -355,7 +383,10 @@ export function createAgentRunController(options: AgentRunControllerOptions = {}
355383
usage: input.providerStep.usage
356384
});
357385
}
358-
if (input.providerStep.text) {
386+
const providerStepText = input.providerStep.text
387+
? stripPreviouslyRecordedAssistantTextSnapshot(input.providerStep.text, parts)
388+
: undefined;
389+
if (providerStepText) {
359390
pushPart({
360391
...createPartBase({
361392
id: `provider_step:${providerStepCount}:text`,
@@ -366,7 +397,7 @@ export function createAgentRunController(options: AgentRunControllerOptions = {}
366397
turnId: options.turnId
367398
}),
368399
kind: 'assistant_text',
369-
text: input.providerStep.text,
400+
text: providerStepText,
370401
final: !continuation && input.providerStep.toolCalls.length === 0 && input.providerStep.finishReason === 'stop'
371402
});
372403
}
@@ -552,7 +583,11 @@ export function createAgentRunController(options: AgentRunControllerOptions = {}
552583

553584
function recordPermissionDenied(input: AgentRunControllerPermissionDeniedInput): AgentRunControllerSnapshot {
554585
if (machine.getSnapshot().state === 'awaiting_permission') {
555-
machine.transition('executing_tools', 'Permission denied; record a structured tool error for provider replay.', now());
586+
machine.transition(
587+
'executing_tools',
588+
'Permission denied; record a structured tool error for provider replay.',
589+
now()
590+
);
556591
}
557592
return recordToolResult({
558593
toolUseId: input.toolUseId,
@@ -567,7 +602,11 @@ export function createAgentRunController(options: AgentRunControllerOptions = {}
567602

568603
function recordPermissionApproved(input: AgentRunControllerPermissionApprovedInput): AgentRunControllerSnapshot {
569604
if (machine.getSnapshot().state === 'awaiting_permission') {
570-
machine.transition('executing_tools', 'Permission approved; record a structured permission result for provider replay.', now());
605+
machine.transition(
606+
'executing_tools',
607+
'Permission approved; record a structured permission result for provider replay.',
608+
now()
609+
);
571610
}
572611
return recordToolResult({
573612
toolUseId: input.toolUseId,
@@ -578,9 +617,7 @@ export function createAgentRunController(options: AgentRunControllerOptions = {}
578617
}
579618

580619
function findPendingToolName(toolUseId: string): string | undefined {
581-
const toolPart = [...parts]
582-
.reverse()
583-
.find((part) => part.kind === 'tool_call' && part.toolUseId === toolUseId);
620+
const toolPart = [...parts].reverse().find((part) => part.kind === 'tool_call' && part.toolUseId === toolUseId);
584621
return toolPart?.kind === 'tool_call' ? toolPart.name : undefined;
585622
}
586623

@@ -622,7 +659,9 @@ export function createAgentRunController(options: AgentRunControllerOptions = {}
622659
return getSnapshot();
623660
}
624661

625-
function requestContextCompression(reason = 'Context budget requires compression before the next provider input.'): AgentRunControllerSnapshot {
662+
function requestContextCompression(
663+
reason = 'Context budget requires compression before the next provider input.'
664+
): AgentRunControllerSnapshot {
626665
const currentState = machine.getSnapshot().state;
627666
if (canTransitionAgentCoreState(currentState, 'compacting_context')) {
628667
machine.transition('compacting_context', reason, now());

0 commit comments

Comments
 (0)