mirror of
https://github.com/wu736139669/hapi.git
synced 2026-08-05 06:24:37 +00:00
fix(kimi): sync native local session titles
This commit is contained in:
@@ -19,6 +19,7 @@ describe('registerAcpSessionTitleSync', () => {
|
||||
listener!({ sessionId: 'session-1', title: '' });
|
||||
listener!({ sessionId: 'session-1', title: null });
|
||||
listener!({ sessionId: 'session-1', title: 'Untitled' });
|
||||
listener!({ sessionId: 'session-1', title: 'New Session' });
|
||||
listener!({ sessionId: 'session-1', title: 'New session - 2026-07-12T15:30:03.251Z' });
|
||||
|
||||
expect(sendClaudeSessionMessage).toHaveBeenCalledTimes(1);
|
||||
|
||||
@@ -1,28 +1,18 @@
|
||||
import { randomUUID } from 'node:crypto';
|
||||
import type { ApiSessionClient } from '@/api/apiSession';
|
||||
import type { AcpSdkBackend } from '@/agent/backends/acp';
|
||||
import { normalizeNativeSessionTitle } from '@/agent/nativeSessionTitle';
|
||||
|
||||
type AcpSessionTitleBackend = Pick<AcpSdkBackend, 'setSessionInfoUpdateListener'>;
|
||||
type AcpSessionTitleClient = Pick<ApiSessionClient, 'sendClaudeSessionMessage'>;
|
||||
|
||||
function isPlaceholderTitle(title: string): boolean {
|
||||
return title === 'Untitled'
|
||||
|| /^(?:New|Child) session - \d{4}-\d{2}-\d{2}T/.test(title);
|
||||
}
|
||||
|
||||
/** Syncs agent-generated ACP session titles into HAPI session metadata. */
|
||||
export function registerAcpSessionTitleSync(
|
||||
backend: AcpSessionTitleBackend,
|
||||
client: AcpSessionTitleClient
|
||||
): void {
|
||||
/** Creates a normalized, deduplicated native-title sink for a HAPI session. */
|
||||
function createSessionTitleSync(client: AcpSessionTitleClient): (title: unknown) => void {
|
||||
let lastTitle: string | null = null;
|
||||
|
||||
backend.setSessionInfoUpdateListener(({ title }) => {
|
||||
if (typeof title !== 'string') {
|
||||
return;
|
||||
}
|
||||
const normalizedTitle = title.trim();
|
||||
if (!normalizedTitle || isPlaceholderTitle(normalizedTitle) || normalizedTitle === lastTitle) {
|
||||
return (title) => {
|
||||
const normalizedTitle = normalizeNativeSessionTitle(title);
|
||||
if (!normalizedTitle || normalizedTitle === lastTitle) {
|
||||
return;
|
||||
}
|
||||
lastTitle = normalizedTitle;
|
||||
@@ -31,5 +21,17 @@ export function registerAcpSessionTitleSync(
|
||||
summary: normalizedTitle,
|
||||
leafUuid: randomUUID()
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
/** Syncs agent-generated ACP session titles into HAPI session metadata. */
|
||||
export function registerAcpSessionTitleSync(
|
||||
backend: AcpSessionTitleBackend,
|
||||
client: AcpSessionTitleClient
|
||||
): void {
|
||||
const syncTitle = createSessionTitleSync(client);
|
||||
|
||||
backend.setSessionInfoUpdateListener(({ title }) => {
|
||||
syncTitle(title);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -0,0 +1,52 @@
|
||||
import type { ApiSessionClient } from '@/api/apiSession';
|
||||
import type { Metadata } from '@/api/types';
|
||||
|
||||
type NativeSessionTitleMetadataClient = Pick<ApiSessionClient, 'getMetadata' | 'updateMetadata'>;
|
||||
|
||||
export function normalizeNativeSessionTitle(title: unknown): string | null {
|
||||
if (typeof title !== 'string') {
|
||||
return null;
|
||||
}
|
||||
const normalizedTitle = title.trim();
|
||||
if (!normalizedTitle
|
||||
|| /^(?:Untitled|New Session)$/i.test(normalizedTitle)
|
||||
|| /^(?:New|Child) session - \d{4}-\d{2}-\d{2}T/i.test(normalizedTitle)) {
|
||||
return null;
|
||||
}
|
||||
return normalizedTitle;
|
||||
}
|
||||
|
||||
/** Syncs a native agent title into HAPI metadata without creating a chat row. */
|
||||
export function createNativeSessionTitleMetadataSync(
|
||||
client: NativeSessionTitleMetadataClient
|
||||
): (title: unknown) => void {
|
||||
let lastTitle: string | null = null;
|
||||
|
||||
return (title) => {
|
||||
const normalizedTitle = normalizeNativeSessionTitle(title);
|
||||
if (!normalizedTitle || normalizedTitle === lastTitle) {
|
||||
return;
|
||||
}
|
||||
lastTitle = normalizedTitle;
|
||||
|
||||
if (metadataHasTitle(client.getMetadata(), normalizedTitle)) {
|
||||
return;
|
||||
}
|
||||
client.updateMetadata((metadata) => {
|
||||
if (metadataHasTitle(metadata, normalizedTitle)) {
|
||||
return metadata;
|
||||
}
|
||||
return {
|
||||
...metadata,
|
||||
summary: {
|
||||
text: normalizedTitle,
|
||||
updatedAt: Date.now()
|
||||
}
|
||||
};
|
||||
});
|
||||
};
|
||||
}
|
||||
|
||||
function metadataHasTitle(metadata: Readonly<Metadata> | null, title: string): boolean {
|
||||
return metadata?.name?.trim() === title || metadata?.summary?.text.trim() === title;
|
||||
}
|
||||
Reference in New Issue
Block a user