From 32f05d99a0ee7334ef45853316d40633b37618d2 Mon Sep 17 00:00:00 2001 From: weishu Date: Fri, 20 Mar 2026 12:17:35 +0800 Subject: [PATCH] Release version 0.16.3 --- bun.lock | 20 ++-- cli/package.json | 12 +-- cli/src/claude/utils/chatVisibility.test.ts | 23 +++++ cli/src/claude/utils/chatVisibility.ts | 16 +++ web/src/chat/normalize.test.ts | 107 ++++++++++++++++++++ 5 files changed, 162 insertions(+), 16 deletions(-) create mode 100644 cli/src/claude/utils/chatVisibility.test.ts create mode 100644 cli/src/claude/utils/chatVisibility.ts create mode 100644 web/src/chat/normalize.test.ts diff --git a/bun.lock b/bun.lock index c8f2b6fe..4723445e 100644 --- a/bun.lock +++ b/bun.lock @@ -12,7 +12,7 @@ }, "cli": { "name": "@twsxtd/hapi", - "version": "0.16.1", + "version": "0.16.3", "bin": { "hapi": "bin/hapi.cjs", }, @@ -43,11 +43,11 @@ "vitest": "^4.0.16", }, "optionalDependencies": { - "@twsxtd/hapi-darwin-arm64": "0.16.1", - "@twsxtd/hapi-darwin-x64": "0.16.1", - "@twsxtd/hapi-linux-arm64": "0.16.1", - "@twsxtd/hapi-linux-x64": "0.16.1", - "@twsxtd/hapi-win32-x64": "0.16.1", + "@twsxtd/hapi-darwin-arm64": "0.16.3", + "@twsxtd/hapi-darwin-x64": "0.16.3", + "@twsxtd/hapi-linux-arm64": "0.16.3", + "@twsxtd/hapi-linux-x64": "0.16.3", + "@twsxtd/hapi-win32-x64": "0.16.3", }, }, "docs": { @@ -874,13 +874,13 @@ "@twsxtd/hapi": ["@twsxtd/hapi@workspace:cli"], - "@twsxtd/hapi-darwin-arm64": ["@twsxtd/hapi-darwin-arm64@0.16.1", "", { "os": "darwin", "cpu": "arm64", "bin": { "hapi": "bin/hapi" } }, "sha512-ayCxWfwMhLKXgoRFz7y7Flls9gfQpprf5AFgz83Fvb6GumCBe5emSvb/Gk/woj7dlmfAaSYFNvJ1VVRel1Bh+Q=="], + "@twsxtd/hapi-darwin-arm64": ["@twsxtd/hapi-darwin-arm64@0.16.3", "", { "os": "darwin", "cpu": "arm64", "bin": { "hapi": "bin/hapi" } }, "sha512-uIP+5MPPG0S5uWTsvToHdFuwl3x2i9ba6uaSzM7yhC0f0BViSshM5CtspNkOxKKcb/0WvQYsPsRpqkhF+O2kgQ=="], - "@twsxtd/hapi-darwin-x64": ["@twsxtd/hapi-darwin-x64@0.16.1", "", { "os": "darwin", "cpu": "x64", "bin": { "hapi": "bin/hapi" } }, "sha512-e2XMFhlx7P9c3dpnY+lCVunpXdRt3IE0lGrdB1kQaF3ThVpYyk8x1E6YKv55un01g/8Tz7fZPk5CKD5YNdXWzQ=="], + "@twsxtd/hapi-darwin-x64": ["@twsxtd/hapi-darwin-x64@0.16.3", "", { "os": "darwin", "cpu": "x64", "bin": { "hapi": "bin/hapi" } }, "sha512-S2Jhp8HY+1I560lD1CkOduXR7yZhZbMMdlIn8QekhXbAEay4enPK4Y0F1CMoYu4KemcwdYU0I2/jyowknVOAZA=="], - "@twsxtd/hapi-linux-arm64": ["@twsxtd/hapi-linux-arm64@0.16.1", "", { "os": "linux", "cpu": "arm64", "bin": { "hapi": "bin/hapi" } }, "sha512-ElwnYQqVIw1/nkgE4gdPuaYbq+IeCmk5mJhB0EjdbFcsjZXXmOT0ifWWjpoT0emaC8LY7a5OmOH0v1DmWLsxEQ=="], + "@twsxtd/hapi-linux-arm64": ["@twsxtd/hapi-linux-arm64@0.16.3", "", { "os": "linux", "cpu": "arm64", "bin": { "hapi": "bin/hapi" } }, "sha512-4/QzrFgr8xalLn0BSFFKKo4GY6mWHZLKwntYiSOTZ+aPHmoD/JxpXF39To5Dmp3FPRV+flyLMPy2p+FvPgg/ng=="], - "@twsxtd/hapi-linux-x64": ["@twsxtd/hapi-linux-x64@0.16.1", "", { "os": "linux", "cpu": "x64", "bin": { "hapi": "bin/hapi" } }, "sha512-0KNIsS4/g2XW59cOXpXG16Mmcc1gU4Z/VNgvQzJL0utfOxMUTrWzDVlrt7qN13yMocrqqgKCf0JTrEdgU1221w=="], + "@twsxtd/hapi-linux-x64": ["@twsxtd/hapi-linux-x64@0.16.3", "", { "os": "linux", "cpu": "x64", "bin": { "hapi": "bin/hapi" } }, "sha512-ZQmI1T62R+1+VL4bG/0x/uC8Y2pffgFj8kwl0tpBOUcjWWi8VKOIvjc7kcwtTdBplQ6vm8GAcyDb4kWJCP1ZQA=="], "@types/aria-query": ["@types/aria-query@5.0.4", "", {}, "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw=="], diff --git a/cli/package.json b/cli/package.json index 3536194f..f17bd891 100644 --- a/cli/package.json +++ b/cli/package.json @@ -1,6 +1,6 @@ { "name": "@twsxtd/hapi", - "version": "0.16.1", + "version": "0.16.3", "description": "App for agentic coding - access coding agent anywhere", "author": "Kirill Dubovitskiy & weishu", "license": "AGPL-3.0-only", @@ -26,11 +26,11 @@ } }, "optionalDependencies": { - "@twsxtd/hapi-darwin-arm64": "0.16.1", - "@twsxtd/hapi-darwin-x64": "0.16.1", - "@twsxtd/hapi-linux-arm64": "0.16.1", - "@twsxtd/hapi-linux-x64": "0.16.1", - "@twsxtd/hapi-win32-x64": "0.16.1" + "@twsxtd/hapi-darwin-arm64": "0.16.3", + "@twsxtd/hapi-darwin-x64": "0.16.3", + "@twsxtd/hapi-linux-arm64": "0.16.3", + "@twsxtd/hapi-linux-x64": "0.16.3", + "@twsxtd/hapi-win32-x64": "0.16.3" }, "scripts": { "postinstall": "node -e \"try{require('fs').chmodSync(require('path').join(__dirname,'bin','hapi.cjs'),0o755)}catch(e){}\"", diff --git a/cli/src/claude/utils/chatVisibility.test.ts b/cli/src/claude/utils/chatVisibility.test.ts new file mode 100644 index 00000000..65b8cfde --- /dev/null +++ b/cli/src/claude/utils/chatVisibility.test.ts @@ -0,0 +1,23 @@ +import { describe, expect, it } from 'vitest' +import { isClaudeChatVisibleMessage } from './chatVisibility' + +describe('isClaudeChatVisibleMessage', () => { + it('hides unsupported Claude system messages from chat delivery', () => { + expect(isClaudeChatVisibleMessage({ type: 'system', subtype: 'stop_hook_summary' })).toBe(false) + expect(isClaudeChatVisibleMessage({ type: 'system', subtype: 'init' })).toBe(false) + expect(isClaudeChatVisibleMessage({ type: 'system' })).toBe(false) + }) + + it('keeps supported Claude system events visible', () => { + expect(isClaudeChatVisibleMessage({ type: 'system', subtype: 'turn_duration' })).toBe(true) + expect(isClaudeChatVisibleMessage({ type: 'system', subtype: 'api_error' })).toBe(true) + expect(isClaudeChatVisibleMessage({ type: 'system', subtype: 'microcompact_boundary' })).toBe(true) + expect(isClaudeChatVisibleMessage({ type: 'system', subtype: 'compact_boundary' })).toBe(true) + }) + + it('keeps conversation messages visible', () => { + expect(isClaudeChatVisibleMessage({ type: 'user' })).toBe(true) + expect(isClaudeChatVisibleMessage({ type: 'assistant' })).toBe(true) + expect(isClaudeChatVisibleMessage({ type: 'summary' })).toBe(true) + }) +}) diff --git a/cli/src/claude/utils/chatVisibility.ts b/cli/src/claude/utils/chatVisibility.ts new file mode 100644 index 00000000..2515d69f --- /dev/null +++ b/cli/src/claude/utils/chatVisibility.ts @@ -0,0 +1,16 @@ +import type { RawJSONLines } from '@/claude/types' + +const VISIBLE_SYSTEM_SUBTYPES = new Set([ + 'api_error', + 'turn_duration', + 'microcompact_boundary', + 'compact_boundary' +]) + +export function isClaudeChatVisibleMessage(message: Pick & { subtype?: string }): boolean { + if (message.type !== 'system') { + return true + } + + return typeof message.subtype === 'string' && VISIBLE_SYSTEM_SUBTYPES.has(message.subtype) +} diff --git a/web/src/chat/normalize.test.ts b/web/src/chat/normalize.test.ts new file mode 100644 index 00000000..f6f38a0d --- /dev/null +++ b/web/src/chat/normalize.test.ts @@ -0,0 +1,107 @@ +import { describe, expect, it } from 'vitest' +import { normalizeDecryptedMessage } from './normalize' +import type { DecryptedMessage } from '@/types/api' + +function makeMessage(content: unknown): DecryptedMessage { + return { + id: 'msg-1', + seq: 1, + localId: null, + content, + createdAt: 1_742_372_800_000 + } +} + +describe('normalizeDecryptedMessage', () => { + it('drops unsupported Claude system output records', () => { + const message = makeMessage({ + role: 'agent', + content: { + type: 'output', + data: { + type: 'system', + subtype: 'stop_hook_summary', + uuid: 'sys-1' + } + } + }) + + expect(normalizeDecryptedMessage(message)).toBeNull() + }) + + it('drops Claude init system output records', () => { + const message = makeMessage({ + role: 'agent', + content: { + type: 'output', + data: { + type: 'system', + subtype: 'init', + uuid: 'sys-init', + session_id: 'session-1' + } + } + }) + + expect(normalizeDecryptedMessage(message)).toBeNull() + }) + + it('keeps known Claude system subtypes as normalized events', () => { + const message = makeMessage({ + role: 'agent', + content: { + type: 'output', + data: { + type: 'system', + subtype: 'turn_duration', + uuid: 'sys-2', + durationMs: 1200 + } + } + }) + + expect(normalizeDecryptedMessage(message)).toMatchObject({ + id: 'msg-1', + role: 'event', + isSidechain: false, + content: { + type: 'turn-duration', + durationMs: 1200 + } + }) + }) + + it('keeps the stringify fallback for unknown non-system agent payloads', () => { + const message = makeMessage({ + role: 'agent', + content: { + type: 'output', + data: { + type: 'assistant', + foo: 'bar' + } + } + }) + + const normalized = normalizeDecryptedMessage(message) + + expect(normalized).toMatchObject({ + id: 'msg-1', + role: 'agent', + isSidechain: false + }) + + expect(normalized?.role).toBe('agent') + if (!normalized || normalized.role !== 'agent') { + throw new Error('Expected agent message') + } + const firstBlock = normalized.content[0] + expect(firstBlock).toMatchObject({ + type: 'text', + }) + if (firstBlock.type !== 'text') { + throw new Error('Expected fallback text block') + } + expect(firstBlock.text).toContain('"foo": "bar"') + }) +})