From ec4afbd852250e7a8a45eb9dfa001c2ea27abe21 Mon Sep 17 00:00:00 2001 From: _Kerman Date: Wed, 27 May 2026 12:43:10 +0800 Subject: [PATCH] fix(agent-core): route session logs exclusively to session sink - Session-tagged entries no longer duplicate into the global sink. - Stable main-agent context key omission now applies to all session lines with agentId=main, not only 'llm request'. - Update test expectations to match the new routing behavior. --- .changeset/session-log-routing.md | 6 ++++++ packages/agent-core/src/logging/logger.ts | 12 +++++------- packages/agent-core/test/logging/logger.test.ts | 17 +++++++++-------- packages/node-sdk/test/local-logging.test.ts | 8 +++++--- 4 files changed, 25 insertions(+), 18 deletions(-) create mode 100644 .changeset/session-log-routing.md diff --git a/.changeset/session-log-routing.md b/.changeset/session-log-routing.md new file mode 100644 index 0000000000..618fc50e13 --- /dev/null +++ b/.changeset/session-log-routing.md @@ -0,0 +1,6 @@ +--- +"@moonshot-ai/agent-core": patch +"@moonshot-ai/kimi-code": patch +--- + +Route session-tagged log entries exclusively to the session sink instead of duplicating them to the global sink. Consistently omit stable main-agent context keys from all session log lines that carry `agentId=main`. diff --git a/packages/agent-core/src/logging/logger.ts b/packages/agent-core/src/logging/logger.ts index fe4ce2781c..c8b507bd17 100644 --- a/packages/agent-core/src/logging/logger.ts +++ b/packages/agent-core/src/logging/logger.ts @@ -122,21 +122,19 @@ class RootLoggerImpl implements RootLogger { if (config === undefined || config.level === 'off') return; if (!levelEnabled(config.level, entry.level)) return; - const formatted = formatEntry(entry); - if (formatted.dropped) return; - - this.globalSink?.enqueue(formatted.text + '\n'); - const session = this.resolveSessionEntry(entry); if (session !== undefined) { - const omitContextKeys = - entry.msg === 'llm request' ? llmRequestSessionLogOmittedKeys(entry) : undefined; + const omitContextKeys = llmRequestSessionLogOmittedKeys(entry); const sessionFormatted = formatEntry(entry, { omitContextKeys, }); if (!sessionFormatted.dropped) { session.sink.enqueue(sessionFormatted.text + '\n'); } + } else { + const formatted = formatEntry(entry); + if (formatted.dropped) return; + this.globalSink?.enqueue(formatted.text + '\n'); } } diff --git a/packages/agent-core/test/logging/logger.test.ts b/packages/agent-core/test/logging/logger.test.ts index ed06870498..2b70d20113 100644 --- a/packages/agent-core/test/logging/logger.test.ts +++ b/packages/agent-core/test/logging/logger.test.ts @@ -237,7 +237,7 @@ describe('createChild', () => { }); describe('session routing', () => { - it('writes sessionId-tagged entries to both global and session sink', async () => { + it('writes sessionId-tagged entries to session sink only', async () => { const sessionDir = await mkdtemp(join(tmpdir(), 'logger-session-')); try { await getRootLogger().configure(defaultConfig()); @@ -248,7 +248,7 @@ describe('session routing', () => { await getRootLogger().flush(); const global = await readGlobal(); const session = await readFile(join(sessionDir, 'logs', 'kimi-code.log'), 'utf-8'); - expect(global).toContain('hello'); + expect(global).not.toContain('hello'); expect(session).toContain('hello'); await handle.close(); } finally { @@ -256,7 +256,7 @@ describe('session routing', () => { } }); - it('prints sessionId once on llm config but omits stable main-agent fields from session llm request lines', async () => { + it('omits stable main-agent fields from all session lines with agentId=main', async () => { const sessionDir = await mkdtemp(join(tmpdir(), 'logger-session-')); try { await getRootLogger().configure(defaultConfig()); @@ -268,11 +268,12 @@ describe('session routing', () => { await getRootLogger().flush(); const global = await readGlobal(); - expect(global).toMatch(/llm config.*sessionId=ses_abc/); - expect(global).toMatch(/llm request.*sessionId=ses_abc/); + expect(global).not.toMatch(/llm config/); + expect(global).not.toMatch(/llm request/); const session = await readFile(join(sessionDir, 'logs', 'kimi-code.log'), 'utf-8'); - expect(session).toMatch(/llm config.*sessionId=ses_abc/); + expect(session).toMatch(/llm config(?!.*sessionId=ses_abc)/); + expect(session).toMatch(/llm config(?!.*agentId=main)/); expect(session).toMatch(/llm request(?!.*sessionId=ses_abc)/); expect(session).toMatch(/llm request(?!.*agentId=main)/); await handle.close(); @@ -347,8 +348,8 @@ describe('session routing', () => { expect(secondText).not.toContain('ambiguous session id'); const global = await readGlobal(); - expect(global).toContain('first only'); - expect(global).toContain('second only'); + expect(global).not.toContain('first only'); + expect(global).not.toContain('second only'); expect(global).toContain('ambiguous session id'); await first.close(); diff --git a/packages/node-sdk/test/local-logging.test.ts b/packages/node-sdk/test/local-logging.test.ts index 1a4fb3cec1..417b939d34 100644 --- a/packages/node-sdk/test/local-logging.test.ts +++ b/packages/node-sdk/test/local-logging.test.ts @@ -98,7 +98,7 @@ function readZipEntries(buf: Buffer): Map { } describe('Local logging — harness integration', () => { - it('writes session-tagged entries to both global and session log files', async () => { + it('writes session-tagged entries to session log only and untagged entries to global', async () => { const homeDir = await makeTempDir('kimi-log-home-'); const workDir = await makeTempDir('kimi-log-work-'); @@ -131,7 +131,7 @@ describe('Local logging — harness integration', () => { const global = await readFile(globalPath, 'utf-8'); const sessionLog = await readFile(sessionLogPath, 'utf-8'); - expect(global).toContain('session diagnostic'); + expect(global).not.toContain('session diagnostic'); expect(sessionLog).toContain('session diagnostic'); expect(global).toContain('untagged event'); expect(sessionLog).not.toContain('untagged event'); @@ -284,6 +284,7 @@ describe('Local logging — harness integration', () => { const harness = new KimiHarness({ identity: TEST_IDENTITY, homeDir }); const session = await harness.createSession({ id: 'ses_flush_warning', workDir }); log.warn('flush warning setup', { sessionId: session.id }); + log.warn('global untagged marker'); const root = getRootLogger(); const flushSessionSpy = vi @@ -305,7 +306,8 @@ describe('Local logging — harness integration', () => { const globalLog = entries.get('logs/global/kimi-code.log')!.toString('utf-8'); expect(sessionLog).toContain('export session log flush failed'); expect(sessionLog).toContain('export global log flush failed'); - expect(globalLog).toContain('export global log flush failed'); + expect(globalLog).toContain('global untagged marker'); + expect(globalLog).not.toContain('export global log flush failed'); } finally { flushSessionSpy.mockRestore(); flushGlobalSpy.mockRestore();