From 3cb7936cf5eab30c5ce5542e4e87d62228f1fc76 Mon Sep 17 00:00:00 2001 From: liruifengv Date: Wed, 1 Jul 2026 20:04:31 +0800 Subject: [PATCH] chore(telemetry): track conversation undo, shell mode, and effort changes (#1271) * chore(telemetry): track conversation undo, shell mode, and effort changes - conversation_undo: fires when an undo reverts history (double-Esc selector, /undo, /undo N) - shell_command: fires when a ! shell command runs in the TUI - thinking_toggle: now fires on any effort change (not just on/off flips) and carries { enabled, effort, from }; align the no-session TUI path to the same payload * chore(telemetry): track conversation_undo in core undoHistory Tracking it in the TUI only covered the TUI; web/acp/REST undos reach core.rpc.undoHistory directly and were missed. Emit the event from the agent undoHistory RPC after the undo succeeds so every host is covered, and drop the now-redundant TUI track. Addresses review feedback on #1271. --- apps/kimi-code/src/tui/commands/config.ts | 6 +++++- apps/kimi-code/src/tui/kimi-tui.ts | 2 ++ .../test/tui/kimi-tui-message-flow.test.ts | 4 +++- packages/agent-core/src/agent/index.ts | 13 +++++++++---- packages/agent-core/test/agent/context.test.ts | 16 ++++++++++++++++ .../test/harness/model-alias-session.test.ts | 2 +- 6 files changed, 36 insertions(+), 7 deletions(-) diff --git a/apps/kimi-code/src/tui/commands/config.ts b/apps/kimi-code/src/tui/commands/config.ts index bdbd27f25..8c02afb98 100644 --- a/apps/kimi-code/src/tui/commands/config.ts +++ b/apps/kimi-code/src/tui/commands/config.ts @@ -422,7 +422,11 @@ async function performModelSwitch( host.track('model_switch', { model: alias }); } if (effort !== prevEffort) { - host.track('thinking_toggle', { effort }); + host.track('thinking_toggle', { + enabled: effort !== 'off', + effort, + from: prevEffort, + }); } } diff --git a/apps/kimi-code/src/tui/kimi-tui.ts b/apps/kimi-code/src/tui/kimi-tui.ts index 32f41b403..45493c1b4 100644 --- a/apps/kimi-code/src/tui/kimi-tui.ts +++ b/apps/kimi-code/src/tui/kimi-tui.ts @@ -955,6 +955,8 @@ export class KimiTUI { this.setAppState({ streamingPhase: 'shell' }); this.state.ui.requestRender(); + this.track('shell_command'); + void session.runShellCommand(command, { commandId }).then( ({ stdout, stderr, isError, backgrounded }) => { this.finishShellOutput(commandId, stdout, stderr, isError, backgrounded); diff --git a/apps/kimi-code/test/tui/kimi-tui-message-flow.test.ts b/apps/kimi-code/test/tui/kimi-tui-message-flow.test.ts index ff4ff9356..bdc11ad85 100644 --- a/apps/kimi-code/test/tui/kimi-tui-message-flow.test.ts +++ b/apps/kimi-code/test/tui/kimi-tui-message-flow.test.ts @@ -1766,13 +1766,15 @@ command = "vim" it('echoes a bash command with a $ prompt in the transcript', async () => { const runShellCommand = vi.fn(async () => ({ stdout: '', stderr: '', isError: false })); const session = makeSession({ runShellCommand }); - const { driver } = await makeDriver(session); + const { driver, harness } = await makeDriver(session); driver.state.appState.inputMode = 'bash'; driver.state.editor.inputMode = 'bash'; driver.handleUserInput('ls'); await Promise.resolve(); + expect(harness.track).toHaveBeenCalledWith('shell_command', undefined); + const transcript = stripSgr(driver.state.transcriptContainer.render(120).join('\n')); expect(transcript).toContain('$ ls'); expect(transcript).not.toContain('! ls'); diff --git a/packages/agent-core/src/agent/index.ts b/packages/agent-core/src/agent/index.ts index 3e841ef6f..e68d11660 100644 --- a/packages/agent-core/src/agent/index.ts +++ b/packages/agent-core/src/agent/index.ts @@ -344,13 +344,18 @@ export class Agent { }, undoHistory: (payload) => { this.context.undo(payload.count); + this.telemetry.track('conversation_undo', { count: payload.count }); }, setThinking: (payload) => { - const wasEnabled = this.config.thinkingEffort !== 'off'; + const previousEffort = this.config.thinkingEffort; this.config.update({ thinkingEffort: payload.effort }); - const enabled = this.config.thinkingEffort !== 'off'; - if (enabled !== wasEnabled) { - this.telemetry.track('thinking_toggle', { enabled }); + const effort = this.config.thinkingEffort; + if (effort !== previousEffort) { + this.telemetry.track('thinking_toggle', { + enabled: effort !== 'off', + effort, + from: previousEffort, + }); } }, setPermission: (payload) => { diff --git a/packages/agent-core/test/agent/context.test.ts b/packages/agent-core/test/agent/context.test.ts index 9ebe197ab..2d3d15e88 100644 --- a/packages/agent-core/test/agent/context.test.ts +++ b/packages/agent-core/test/agent/context.test.ts @@ -9,6 +9,7 @@ import { project } from '../../src/agent/context/projector'; import type { ContextMessage } from '../../src/agent/context/types'; import { estimateTokensForMessages } from '../../src/utils/tokens'; import { createFakeKaos } from '../tools/fixtures/fake-kaos'; +import { recordingTelemetry, type TelemetryRecord } from '../fixtures/telemetry'; import { testAgent } from './harness/agent'; describe('Agent context', () => { @@ -58,6 +59,21 @@ describe('Agent context', () => { expect(ctx.agent.context.messages.some((message) => 'origin' in message)).toBe(false); }); + it('tracks conversation_undo when undoHistory reverts a user message', async () => { + const records: TelemetryRecord[] = []; + const ctx = testAgent({ telemetry: recordingTelemetry(records) }); + ctx.configure(); + + ctx.agent.context.appendUserMessage([{ type: 'text', text: 'hello' }]); + + await ctx.agent.rpcMethods.undoHistory({ count: 1 }); + + expect(records).toContainEqual({ + event: 'conversation_undo', + properties: { count: 1 }, + }); + }); + it('records bash input/output as shell_command origin with tagged content', () => { const ctx = testAgent(); ctx.configure(); diff --git a/packages/agent-core/test/harness/model-alias-session.test.ts b/packages/agent-core/test/harness/model-alias-session.test.ts index 38aad6078..6d9b66388 100644 --- a/packages/agent-core/test/harness/model-alias-session.test.ts +++ b/packages/agent-core/test/harness/model-alias-session.test.ts @@ -391,7 +391,7 @@ max_context_size = 1000000 expect(resumeRecords).toContainEqual({ event: 'thinking_toggle', sessionId: created.id, - properties: { enabled: false }, + properties: { enabled: false, effort: 'off', from: 'high' }, }); });