diff --git a/.changeset/fix-background-task-notification.md b/.changeset/fix-background-task-notification.md new file mode 100644 index 00000000000..157fbbf36b8 --- /dev/null +++ b/.changeset/fix-background-task-notification.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Show background task completion notices in the session message stream. diff --git a/.changeset/fix-empty-thinking-parts.md b/.changeset/fix-empty-thinking-parts.md new file mode 100644 index 00000000000..6eca76a753b --- /dev/null +++ b/.changeset/fix-empty-thinking-parts.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix empty thinking entries recorded for steps without reasoning content. diff --git a/.changeset/fix-history-paging-past-compaction.md b/.changeset/fix-history-paging-past-compaction.md new file mode 100644 index 00000000000..fc0f9d7f277 --- /dev/null +++ b/.changeset/fix-history-paging-past-compaction.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix earlier-message history paging returning empty pages before compaction points. diff --git a/.changeset/fix-plan-approval-display.md b/.changeset/fix-plan-approval-display.md new file mode 100644 index 00000000000..6c6571d897e --- /dev/null +++ b/.changeset/fix-plan-approval-display.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix plan approvals and plan blocks missing the plan content in the v2 message stream. diff --git a/.changeset/fix-steer-duplicate-user-message.md b/.changeset/fix-steer-duplicate-user-message.md new file mode 100644 index 00000000000..edffe16ce77 --- /dev/null +++ b/.changeset/fix-steer-duplicate-user-message.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Fix steered prompts appearing twice in the session message stream. diff --git a/.changeset/goal-status-transitions-only.md b/.changeset/goal-status-transitions-only.md new file mode 100644 index 00000000000..3fc87fa9ce1 --- /dev/null +++ b/.changeset/goal-status-transitions-only.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Emit goal status stream entries only when the goal status actually changes. diff --git a/.changeset/skill-activation-user-message.md b/.changeset/skill-activation-user-message.md new file mode 100644 index 00000000000..8699278ed6e --- /dev/null +++ b/.changeset/skill-activation-user-message.md @@ -0,0 +1,5 @@ +--- +"@moonshot-ai/kimi-code": patch +--- + +Show skill activations as user messages in the session message stream. diff --git a/packages/agent-core-v2/docs/wire-manifest.d.ts b/packages/agent-core-v2/docs/wire-manifest.d.ts index afa3652a160..d7d2688bf5b 100644 --- a/packages/agent-core-v2/docs/wire-manifest.d.ts +++ b/packages/agent-core-v2/docs/wire-manifest.d.ts @@ -489,6 +489,7 @@ interface PlanRevisionPayload { key: string; sha256: string; bytes: number; + summary?: string; } /** diff --git a/packages/agent-core-v2/src/agent/loop/loopService.ts b/packages/agent-core-v2/src/agent/loop/loopService.ts index 7751e8d5add..2aa6826f53d 100644 --- a/packages/agent-core-v2/src/agent/loop/loopService.ts +++ b/packages/agent-core-v2/src/agent/loop/loopService.ts @@ -809,6 +809,7 @@ export class AgentLoopService extends Disposable implements IAgentLoopService { ); return; case 'thinking': + if (delta.delta === '') return; this.accumulateMachinePart(turn, { type: 'think', think: delta.delta }); void this.dispatcher.dispatch( new ThinkingDelta({ agentId: this.scopeContext.agentId, turnId: turn.id, delta: delta.delta }), @@ -840,6 +841,7 @@ export class AgentLoopService extends Disposable implements IAgentLoopService { step.rawFinishReason = event.finish?.rawFinishReason ?? undefined; step.messageId = event.messageId; for (const part of event.entry.message.content) { + if (part.type === 'think' && part.think === '' && (part as { encrypted?: string }).encrypted === undefined) continue; this.context.appendLoopEvent({ type: 'content.part', uuid: randomUUID(), diff --git a/packages/agent-core-v2/src/agent/prompt/prompt.ts b/packages/agent-core-v2/src/agent/prompt/prompt.ts index ff6fb115184..15eed633312 100644 --- a/packages/agent-core-v2/src/agent/prompt/prompt.ts +++ b/packages/agent-core-v2/src/agent/prompt/prompt.ts @@ -66,7 +66,7 @@ export interface PromptLaunchResult { export interface PromptReservation extends IDisposable { readonly id: string; - submit(message: ContextMessage): Promise; + submit(message: ContextMessage, opts?: { steer?: boolean }): Promise; } export const promptAdmission = Symbol('promptAdmission'); diff --git a/packages/agent-core-v2/src/agent/prompt/promptService.ts b/packages/agent-core-v2/src/agent/prompt/promptService.ts index ce4383a28c6..790622a6be2 100644 --- a/packages/agent-core-v2/src/agent/prompt/promptService.ts +++ b/packages/agent-core-v2/src/agent/prompt/promptService.ts @@ -156,6 +156,7 @@ export interface PromptSubmittedPayload { readonly status: 'running' | 'queued'; readonly content: ContentPart[]; readonly createdAt: string; + readonly steer?: boolean; } export class PromptSubmitted extends AgentEvent2 { @@ -178,6 +179,7 @@ export interface PromptStarted extends PromptStartedPayload {} interface Deferred { readonly promise: Promise; resolve(value: T): void; reject(reason: unknown): void } interface Record extends PromptSnapshot { state: PromptState; + steerIntended: boolean; readonly launchedDeferred: Deferred; readonly completionDeferred: Deferred; handle: PromptHandle; @@ -276,7 +278,7 @@ export class AgentPromptService implements IAgentPromptService { let submitted = false; return { id, - submit: async (message) => { + submit: async (message, opts) => { if (submitted) throw new Error2(ErrorCodes.REQUEST_INVALID, 'prompt reservation already submitted'); submitted = true; this.reservedPromptIds.delete(id); @@ -287,7 +289,7 @@ export class AgentPromptService implements IAgentPromptService { content: stripBundledSkillBlocks(message), }), ); - return this.enqueue({ id, message }); + return this.enqueue({ id, message }, opts); }, dispose: () => { this.reservedPromptIds.delete(id); @@ -295,7 +297,7 @@ export class AgentPromptService implements IAgentPromptService { }; } - async enqueue(input: PromptInput): Promise { + async enqueue(input: PromptInput, opts?: { steer?: boolean }): Promise { const id = input.id ?? input.message.id ?? newMessageId(); const message = { ...input.message, id }; const launchedDeferred = deferred(); @@ -303,7 +305,7 @@ export class AgentPromptService implements IAgentPromptService { const record = {} as Record; Object.assign(record, { id, userMessageId: id, createdAt: new Date().toISOString(), state: 'pending', message, - launchedDeferred, completionDeferred, + launchedDeferred, completionDeferred, steerIntended: opts?.steer === true, }); record.handle = { get id() { return record.id; }, get userMessageId() { return record.userMessageId; }, @@ -359,7 +361,7 @@ export class AgentPromptService implements IAgentPromptService { role: 'user', content: [...payload.input], toolCalls: [], - } }); + } }, { steer: true }); if (queued.state !== 'pending') { const turn = await queued.launched; return turn === undefined ? undefined : { turn_id: turn.id }; @@ -600,7 +602,7 @@ export class AgentPromptService implements IAgentPromptService { } private publishSubmitted(record: Record, status: 'running' | 'queued'): void { if ((record.message.origin ?? USER_PROMPT_ORIGIN).kind !== 'user') return; - void this.dispatcher.dispatch(new PromptSubmitted({ agentId: this.scopeContext.agentId, promptId: record.id, userMessageId: record.userMessageId, status, content: stripBundledSkillBlocks(record.message), createdAt: record.createdAt })); + void this.dispatcher.dispatch(new PromptSubmitted({ agentId: this.scopeContext.agentId, promptId: record.id, userMessageId: record.userMessageId, status, content: stripBundledSkillBlocks(record.message), createdAt: record.createdAt, steer: record.steerIntended ? true : undefined })); } private publishStarted(record: Record): void { if ((record.message.origin ?? USER_PROMPT_ORIGIN).kind !== 'user') return; diff --git a/packages/agent-core-v2/src/agent/task/task.ts b/packages/agent-core-v2/src/agent/task/task.ts index 631f417afc6..852d668df94 100644 --- a/packages/agent-core-v2/src/agent/task/task.ts +++ b/packages/agent-core-v2/src/agent/task/task.ts @@ -64,6 +64,8 @@ export interface AgentTaskNotificationContext { readonly severity: 'info' | 'warning'; readonly sourceKind: string; readonly sourceId: string; + readonly sourceAgentId?: string; + readonly raw?: string; } export interface AgentTaskWaitDelivery { diff --git a/packages/agent-core-v2/src/agent/task/taskService.ts b/packages/agent-core-v2/src/agent/task/taskService.ts index 5bea340a513..1f4798c6c6a 100644 --- a/packages/agent-core-v2/src/agent/task/taskService.ts +++ b/packages/agent-core-v2/src/agent/task/taskService.ts @@ -1284,6 +1284,8 @@ export class AgentTaskService extends Disposable implements IAgentTaskService { severity: notification.severity, sourceKind: notification.source_kind, sourceId: notification.source_id, + sourceAgentId: typeof notification.agent_id === 'string' ? notification.agent_id : undefined, + raw: renderNotificationXml(notification), }), ); } diff --git a/packages/agent-core-v2/src/features/plan/planOps.ts b/packages/agent-core-v2/src/features/plan/planOps.ts index 317aa003ca4..08b8e36ac8a 100644 --- a/packages/agent-core-v2/src/features/plan/planOps.ts +++ b/packages/agent-core-v2/src/features/plan/planOps.ts @@ -62,6 +62,7 @@ export interface PlanRevisionRecordedEvent { readonly key: string; readonly sha256: string; readonly bytes: number; + readonly summary?: string; } const planRevisionSchema = z.object({ @@ -71,6 +72,7 @@ const planRevisionSchema = z.object({ key: z.string(), sha256: z.string(), bytes: z.number(), + summary: z.string().optional(), }); export class PlanRevision extends AgentEvent2 { diff --git a/packages/agent-core-v2/src/features/plan/planService.ts b/packages/agent-core-v2/src/features/plan/planService.ts index 3df74d331ab..5a9de29a49d 100644 --- a/packages/agent-core-v2/src/features/plan/planService.ts +++ b/packages/agent-core-v2/src/features/plan/planService.ts @@ -206,6 +206,11 @@ export class AgentPlanService extends Service implements IAgentPlanService { const scope = this.agentCtx.scope(); const key = `plan/${id}/v${version}.md`; await this.blobs.put(scope, key, bytes); + const heading = content + .split('\n') + .map((line) => line.trim()) + .find((line) => line.startsWith('#')); + const summary = heading === undefined ? undefined : heading.replace(/^#+\s*/, '').trim() || undefined; await this.dispatcher.dispatch( new PlanRevision({ agentId: this.agentCtx.agentId, @@ -214,6 +219,7 @@ export class AgentPlanService extends Service implements IAgentPlanService { key, sha256: createHash('sha256').update(bytes).digest('hex'), bytes: bytes.byteLength, + summary, }), ); } diff --git a/packages/agent-core-v2/src/features/todo/todoOps.ts b/packages/agent-core-v2/src/features/todo/todoOps.ts index db8f746e0ea..9ae80604ff8 100644 --- a/packages/agent-core-v2/src/features/todo/todoOps.ts +++ b/packages/agent-core-v2/src/features/todo/todoOps.ts @@ -16,6 +16,7 @@ const toolsUpdateStoreSchema = z.object({ export class ToolsUpdateStore extends AgentEvent2> { static override readonly type = 'tools.update_store'; static override readonly durable = true; + static override readonly observable = true; static override readonly schema = toolsUpdateStoreSchema; } export interface ToolsUpdateStore { diff --git a/packages/agent-core-v2/test/agent/loop/loop.test.ts b/packages/agent-core-v2/test/agent/loop/loop.test.ts index d7b931d4f5b..fd46ddca6ee 100644 --- a/packages/agent-core-v2/test/agent/loop/loop.test.ts +++ b/packages/agent-core-v2/test/agent/loop/loop.test.ts @@ -95,11 +95,8 @@ describe('Agent loop', () => { [wire] llm.tools_snapshot { "agentId": "main", "hash": "4f53cda18c2baa0c0354bb5f9a3ecbe5ed12ab4d8e11ba873c2f11161202b945", "tools": [], "time": "