From 194d6bd2e1a9e63b4457ef1e41dbfdcfa4e1edab Mon Sep 17 00:00:00 2001 From: Sebastian B Date: Wed, 29 Jul 2026 22:52:04 +0100 Subject: [PATCH 1/3] feat(tui): show the bound model in background subagent start entries Background spawns append their started/completed/failed transcript entry synchronously from subagent.spawned, which carries the bound model alias on the v2 engine. Resolve it through modelDisplayName and render it in the headline: 'coder agent (GLM 5.2) started in background'. v1 spawns carry no model on the event, so their entries render unchanged. --- .../tui/controllers/subagent-event-handler.ts | 4 +++ apps/kimi-code/src/tui/types.ts | 2 ++ .../src/tui/utils/background-agent-status.ts | 8 +++--- .../messages/background-agent-status.test.ts | 26 +++++++++++++++++++ 4 files changed, 37 insertions(+), 3 deletions(-) diff --git a/apps/kimi-code/src/tui/controllers/subagent-event-handler.ts b/apps/kimi-code/src/tui/controllers/subagent-event-handler.ts index d632b50288..0982d02678 100644 --- a/apps/kimi-code/src/tui/controllers/subagent-event-handler.ts +++ b/apps/kimi-code/src/tui/controllers/subagent-event-handler.ts @@ -384,6 +384,10 @@ export class SubAgentEventHandler { parentToolCallId: event.parentToolCallId, agentName: event.subagentName, description: typeof description === 'string' ? description : undefined, + model: + event.model === undefined + ? undefined + : modelDisplayName(event.model, this.host.state.appState.availableModels[event.model]), }; } diff --git a/apps/kimi-code/src/tui/types.ts b/apps/kimi-code/src/tui/types.ts index 30de918e31..f2cfda98d9 100644 --- a/apps/kimi-code/src/tui/types.ts +++ b/apps/kimi-code/src/tui/types.ts @@ -119,6 +119,8 @@ export interface BackgroundAgentMetadata { readonly parentToolCallId: string; readonly agentName?: string; readonly description?: string; + /** Display name of the model bound to the subagent (v2 spawns only). */ + readonly model?: string; } export type BackgroundAgentStatusPhase = 'started' | 'completed' | 'failed'; diff --git a/apps/kimi-code/src/tui/utils/background-agent-status.ts b/apps/kimi-code/src/tui/utils/background-agent-status.ts index a54257a971..015dcf1b94 100644 --- a/apps/kimi-code/src/tui/utils/background-agent-status.ts +++ b/apps/kimi-code/src/tui/utils/background-agent-status.ts @@ -21,12 +21,14 @@ export function formatBackgroundAgentTranscript( ): BackgroundAgentStatusData { const normalizedAgentName = normalizeBackgroundField(meta.agentName); const subject = normalizedAgentName !== undefined ? `${normalizedAgentName} agent` : 'agent'; + const model = normalizeBackgroundField(meta.model); + const subjectWithModel = model === undefined ? subject : `${subject} (${model})`; const headline = phase === 'started' - ? `${subject} started in background` + ? `${subjectWithModel} started in background` : phase === 'completed' - ? `${subject} completed in background` - : `${subject} failed in background`; + ? `${subjectWithModel} completed in background` + : `${subjectWithModel} failed in background`; const tail = phase === 'failed' ? normalizeBackgroundField(extras?.error) : undefined; const detailParts = [normalizeBackgroundField(meta.description), tail].filter( (part): part is string => part !== undefined, diff --git a/apps/kimi-code/test/tui/components/messages/background-agent-status.test.ts b/apps/kimi-code/test/tui/components/messages/background-agent-status.test.ts index e8f395ec87..67f13e7507 100644 --- a/apps/kimi-code/test/tui/components/messages/background-agent-status.test.ts +++ b/apps/kimi-code/test/tui/components/messages/background-agent-status.test.ts @@ -3,11 +3,37 @@ import { describe, expect, it } from 'vitest'; import { BackgroundAgentStatusComponent } from '#/tui/components/messages/background-agent-status'; import { STATUS_BULLET } from '#/tui/constant/symbols'; +import { formatBackgroundAgentTranscript } from '#/tui/utils/background-agent-status'; function strip(text: string): string { return text.replaceAll(/\u001B\[[0-9;]*m/g, ''); } +describe('formatBackgroundAgentTranscript', () => { + it('includes the bound model in the headline when known', () => { + const status = formatBackgroundAgentTranscript('started', { + agentId: 'agent-1', + parentToolCallId: 'call-1', + agentName: 'coder', + description: 'Implement the fix', + model: 'GLM 5.2', + }); + + expect(status.headline).toBe('coder agent (GLM 5.2) started in background'); + expect(status.detail).toBe('Implement the fix'); + }); + + it('falls back to the plain headline when the model is unknown', () => { + const status = formatBackgroundAgentTranscript('started', { + agentId: 'agent-1', + parentToolCallId: 'call-1', + agentName: 'coder', + }); + + expect(status.headline).toBe('coder agent started in background'); + }); +}); + describe('BackgroundAgentStatusComponent', () => { it('renders started/completed with the shared bullet and failed with a red x marker', () => { const started = new BackgroundAgentStatusComponent({ From 9b6fdf4a58a629369eef7d1fefa2945a2677159d Mon Sep 17 00:00:00 2001 From: Sebastian B Date: Wed, 29 Jul 2026 22:52:04 +0100 Subject: [PATCH 2/3] fix(agent-core): support wire protocol 1.5 in the v1 engine agent-core-v2 bumped the wire protocol to 1.5 (upstream #1695), so sessions written through kap-server / kimi web replayed in the v1 TUI with a 'newer than the current version 1.4' warning and no migration. Port the engine-agnostic 1.4 to 1.5 migration (backfill the goal wall-clock anchor from the record timestamp) into agent-core and bump AGENT_WIRE_PROTOCOL_VERSION to 1.5, matching upstream v2. --- .../src/agent/records/migration/index.ts | 4 +- .../src/agent/records/migration/v1.5.ts | 32 +++++++++ .../test/agent/records/migration/v1.4.test.ts | 2 +- .../test/agent/records/migration/v1.5.test.ts | 69 +++++++++++++++++++ 4 files changed, 105 insertions(+), 2 deletions(-) create mode 100644 packages/agent-core/src/agent/records/migration/v1.5.ts create mode 100644 packages/agent-core/test/agent/records/migration/v1.5.test.ts diff --git a/packages/agent-core/src/agent/records/migration/index.ts b/packages/agent-core/src/agent/records/migration/index.ts index 8431c16329..2d12e951b8 100644 --- a/packages/agent-core/src/agent/records/migration/index.ts +++ b/packages/agent-core/src/agent/records/migration/index.ts @@ -2,13 +2,14 @@ import { migrateV1_0ToV1_1 } from './v1.1'; import { migrateV1_1ToV1_2 } from './v1.2'; import { migrateV1_2ToV1_3 } from './v1.3'; import { migrateV1_3ToV1_4 } from './v1.4'; +import { migrateV1_4ToV1_5 } from './v1.5'; // Wire protocol versions currently support only the `number.number` format. // Bump this only for changes that require migration of existing records or // change how existing records must be interpreted. Do not bump it only because // a new feature adds a new wire record type: older versions do not implement // that feature and do not need to understand the new record type. -export const AGENT_WIRE_PROTOCOL_VERSION = '1.4'; +export const AGENT_WIRE_PROTOCOL_VERSION = '1.5'; export interface WireMigrationRecord { readonly type: string; @@ -26,6 +27,7 @@ const MIGRATIONS: readonly WireMigration[] = [ migrateV1_1ToV1_2, migrateV1_2ToV1_3, migrateV1_3ToV1_4, + migrateV1_4ToV1_5, ]; export function isNewerWireVersion(readVersion: string): boolean { diff --git a/packages/agent-core/src/agent/records/migration/v1.5.ts b/packages/agent-core/src/agent/records/migration/v1.5.ts new file mode 100644 index 0000000000..34392f2985 --- /dev/null +++ b/packages/agent-core/src/agent/records/migration/v1.5.ts @@ -0,0 +1,32 @@ +/** + * Wire protocol 1.5 persists an epoch-ms anchor at every goal create/resume + * boundary and wall-clock checkpoint. Version 1.4 records already carry an + * epoch-ms `time`, so the migration can recover that boundary without + * inventing a crash timestamp or adding periodic checkpoint writes. Existing + * anchors are authoritative. + * + * Ported from agent-core-v2 (`wire/migration/v1.5.ts`) so the v1 engine can + * resume v2-written 1.5 sessions natively instead of replaying them without + * migration. + */ +import type { WireMigration, WireMigrationRecord } from './index'; + +export const migrateV1_4ToV1_5: WireMigration = { + sourceVersion: '1.4', + targetVersion: '1.5', + migrateRecord(record: WireMigrationRecord): WireMigrationRecord { + if (!advancesActiveInterval(record)) return record; + if (record['wallClockResumedAt'] !== undefined) return record; + if (typeof record['time'] !== 'number') return record; + return { ...record, wallClockResumedAt: record['time'] }; + }, +}; + +function advancesActiveInterval(record: WireMigrationRecord): boolean { + return ( + record.type === 'goal.create' || + (record.type === 'goal.update' && + (record['status'] === 'active' || + (record['status'] === undefined && typeof record['wallClockMs'] === 'number'))) + ); +} diff --git a/packages/agent-core/test/agent/records/migration/v1.4.test.ts b/packages/agent-core/test/agent/records/migration/v1.4.test.ts index e5e4ae0b60..c7e59bd82d 100644 --- a/packages/agent-core/test/agent/records/migration/v1.4.test.ts +++ b/packages/agent-core/test/agent/records/migration/v1.4.test.ts @@ -64,7 +64,7 @@ describe('1.3 to 1.4', () => { }, ]), ).toMatchInlineSnapshot(` - [wire] metadata { "protocol_version": "", "created_at": "