Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ jobs:
- name: Run frontend tests
run: npm run test

- name: Build frontend
run: npm run build

rust-test:
name: Rust Tests
runs-on: ubuntu-22.04
Expand Down
22 changes: 15 additions & 7 deletions src/__tests__/runtime.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ describe('Runtime Utility & Bridge', () => {
});
globalThis.fetch = mockFetch;

const result = await appInvoke<any>('get_mobile_service_status');
const result = await appInvoke('get_mobile_service_status');
expect(result.isRunning).toBe(true);
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/mobile/status', {
cache: 'no-store',
Expand All @@ -128,7 +128,7 @@ describe('Runtime Utility & Bridge', () => {
});
globalThis.fetch = mockFetch;

const result = await appInvoke<any>('list_agent_sessions', { prefix: 'partner-session-' });
const result = await appInvoke('list_agent_sessions', { prefix: 'partner-session-' });
expect(result).toEqual([]);
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/mobile/sessions?prefix=partner-session-', {
cache: 'no-store',
Expand All @@ -146,7 +146,7 @@ describe('Runtime Utility & Bridge', () => {
});
globalThis.fetch = mockFetch;

await appInvoke<any>('list_agent_sessions', {
await appInvoke('list_agent_sessions', {
prefix: 'story-session-',
sessionKind: 'story',
});
Expand All @@ -164,8 +164,16 @@ describe('Runtime Utility & Bridge', () => {
});
globalThis.fetch = mockFetch;

const sessionObj = { id: 's1', title: 'test session' };
const result = await appInvoke<any>('save_agent_session', { session: sessionObj });
const sessionObj = {
id: 's1',
title: 'test session',
savedAt: 0,
messages: [],
selectedReferenceFiles: [],
selectedOutlineFile: null,
todos: [],
};
const result = await appInvoke('save_agent_session', { session: sessionObj });
expect(result.id).toBe('s1');
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/mobile/sessions', {
method: 'POST',
Expand Down Expand Up @@ -198,7 +206,7 @@ describe('Runtime Utility & Bridge', () => {
characterCardId: 'card-1',
selectedWorldBookId: null,
};
await appInvoke<any>('save_agent_session', { session: sessionObj });
await appInvoke('save_agent_session', { session: sessionObj });

expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/mobile/sessions', expect.objectContaining({
method: 'POST',
Expand All @@ -214,7 +222,7 @@ describe('Runtime Utility & Bridge', () => {
globalThis.fetch = mockFetch;

const reqBody = { messages: [] };
const result = await appInvoke<any>('start_chat_completion_stream', { request: reqBody });
const result = await appInvoke('start_chat_completion_stream', { request: reqBody });
expect(result.runId).toBe('run-123');
expect(mockFetch).toHaveBeenCalledWith('http://localhost:3000/api/mobile/chat/start', {
method: 'POST',
Expand Down
10 changes: 5 additions & 5 deletions src/pages/MobileBond.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,7 @@ const MobileBond: React.FC = () => {

// Reload partner store from backend to ensure latest character cards
try {
const partnerStoreContent = await appInvoke<string>('load_app_state', { name: 'partner-store' });
const partnerStoreContent = await appInvoke('load_app_state', { name: 'partner-store' });
if (partnerStoreContent) {
const parsed = JSON.parse(partnerStoreContent);
if (parsed.state) {
Expand All @@ -150,7 +150,7 @@ const MobileBond: React.FC = () => {
}

try {
const summaries = await appInvoke<AgentSessionSummary[]>('list_agent_sessions', { prefix: 'partner-session-' });
const summaries = await appInvoke('list_agent_sessions', { prefix: 'partner-session-' });
patchUiState({ sessions: summaries });
} catch (err) {
console.error('加载会话足迹失败:', err);
Expand All @@ -159,7 +159,7 @@ const MobileBond: React.FC = () => {
}

try {
const summaries = await appInvoke<AgentSessionSummary[]>('list_agent_sessions', {
const summaries = await appInvoke('list_agent_sessions', {
prefix: 'story-session-',
sessionKind: 'story',
});
Expand Down Expand Up @@ -193,7 +193,7 @@ const MobileBond: React.FC = () => {
setExpandedSessionId(id);
setLoadingRecord(true);
try {
const record = await appInvoke<AgentSessionRecord>('load_agent_session', { id });
const record = await appInvoke('load_agent_session', { id });
setExpandedRecord(record);
} catch (err) {
console.error('加载会话详情失败:', err);
Expand All @@ -211,7 +211,7 @@ const MobileBond: React.FC = () => {
setExpandedAdventureId(id);
setLoadingAdventureRecord(true);
try {
const record = await appInvoke<AgentSessionRecord>('load_agent_session', { id });
const record = await appInvoke('load_agent_session', { id });
setExpandedAdventureRecord(record);
} catch (err) {
console.error('加载冒险详情失败:', err);
Expand Down
26 changes: 13 additions & 13 deletions src/pages/MobileChat.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ import { createStableContentKey } from '../utils/renderKeys';
import { useStateGroup } from '../utils/reducerState';
import { ensureSessionId } from '../utils/sessionIds';
import { resolveSessionTitle } from '../utils/sessionTitle';
import type { Message, AgentSessionSummary } from '../stores/useAgentStore';
import type { AgentSessionRecord, Message } from '../stores/useAgentStore';

interface MobileChatUiState {
isArchiveModalOpen: boolean;
Expand Down Expand Up @@ -172,7 +172,7 @@ const useMobileChatView = () => {

// Load session list on mount
useEffect(() => {
appInvoke<AgentSessionSummary[]>('list_agent_sessions', { prefix: 'partner-session-' })
appInvoke('list_agent_sessions', { prefix: 'partner-session-' })
.then((list) => setSessions(list))
.catch((e) => console.error('加载会话列表失败:', e));
}, [setSessions]);
Expand All @@ -185,7 +185,7 @@ const useMobileChatView = () => {
setSessionId(currentSessionId);
}
try {
const record = {
const record: AgentSessionRecord = {
id: currentSessionId,
title,
messages: list,
Expand All @@ -198,10 +198,10 @@ const useMobileChatView = () => {
characterCardId: selectedCharacterCardId,
selectedWorldBookId,
};
await appInvoke<AgentSessionSummary>('save_agent_session', { session: record });
await appInvoke('save_agent_session', { session: record });

// Update session summary list
const listRes = await appInvoke<AgentSessionSummary[]>('list_agent_sessions', { prefix: 'partner-session-' });
const listRes = await appInvoke('list_agent_sessions', { prefix: 'partner-session-' });
setSessions(listRes);
return true;
} catch (e) {
Expand Down Expand Up @@ -230,10 +230,10 @@ const useMobileChatView = () => {
messages,
finalFallback: '未命名会话',
summarize: async () => {
const res = await appInvoke<{ title: string }>('summarize_text', {
const res = await appInvoke('summarize_text', {
request: { text: chatHistoryText },
});
return res.title;
return typeof res === 'string' ? res : res.title;
},
});
setSessionTitle(finalTitle);
Expand All @@ -257,7 +257,7 @@ const useMobileChatView = () => {
return;
}
try {
const record = await appInvoke<any>('load_agent_session', { id });
const record = await appInvoke('load_agent_session', { id });
setSessionId(record.id);
setSessionTitle(record.title);
setMessages(record.messages || []);
Expand Down Expand Up @@ -285,7 +285,7 @@ const useMobileChatView = () => {
if (sessionId === id) {
createNewSession();
}
const listRes = await appInvoke<AgentSessionSummary[]>('list_agent_sessions', { prefix: 'partner-session-' });
const listRes = await appInvoke('list_agent_sessions', { prefix: 'partner-session-' });
setSessions(listRes);
} catch (e) {
message.error('删除会话失败');
Expand Down Expand Up @@ -371,7 +371,7 @@ const useMobileChatView = () => {
}));

try {
const { runId } = await appInvoke<{ runId: string }>('start_chat_completion_stream', {
const { runId } = await appInvoke('start_chat_completion_stream', {
request: {
agentId: 'partnerChat',
modelInterface: settings.modelInterface,
Expand Down Expand Up @@ -502,7 +502,7 @@ const useMobileChatView = () => {
return;
}

const result = await appInvoke<string | Record<string, any>>('analyze_character_memory', { sessionId });
const result = await appInvoke('analyze_character_memory', { sessionId });
const parsed = parseArchiveAnalysisResponse(result);
setArchiveAnalysis(parsed);
setEditedTitle(parsed.sessionTitle || parsed.recommendedSessionTitle || sessionTitle);
Expand Down Expand Up @@ -536,11 +536,11 @@ const useMobileChatView = () => {
message.success('伴侣记忆封存成功!该会话已归档锁定。');

// Reload sessions
const sessList = await appInvoke<AgentSessionSummary[]>('list_agent_sessions', { prefix: 'partner-session-' });
const sessList = await appInvoke('list_agent_sessions', { prefix: 'partner-session-' });
setSessions(sessList);

// Reload partner store
const partnerStoreContent = await appInvoke<string>('load_app_state', { name: 'partner-store' });
const partnerStoreContent = await appInvoke('load_app_state', { name: 'partner-store' });
if (partnerStoreContent) {
const parsed = JSON.parse(partnerStoreContent);
if (parsed.state) {
Expand Down
5 changes: 2 additions & 3 deletions src/pages/MobileHome.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { appInvoke, clearMobileToken, getMobileToken, setMobileToken } from '../
import { usePartnerChatStore } from '../stores/usePartnerChatStore';
import { usePartnerStore } from '../stores/usePartnerStore';
import { useStoryStore } from '../stores/useStoryStore';
import type { AgentSessionSummary } from '../stores/useAgentStore';

type ConnectionStatus = 'waiting' | 'verifying' | 'verified' | 'invalid';

Expand Down Expand Up @@ -59,10 +58,10 @@ const MobileHome: React.FC = () => {
useStoryStore.persist.rehydrate(),
]);
const [chatSessions, storySessions] = await Promise.all([
appInvoke<AgentSessionSummary[]>('list_agent_sessions', {
appInvoke('list_agent_sessions', {
prefix: 'partner-session-',
}),
appInvoke<AgentSessionSummary[]>('list_agent_sessions', {
appInvoke('list_agent_sessions', {
prefix: 'story-session-',
sessionKind: 'story',
}),
Expand Down
28 changes: 14 additions & 14 deletions src/pages/MobileStory.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import {
getStoryAllowedTools,
getRolePlayCharacterName,
} from './storyAgent';
import type { Message, AgentSessionSummary, AgentToolEntry } from '../stores/useAgentStore';
import type { AgentSessionRecord, AgentToolEntry, Message } from '../stores/useAgentStore';

interface MobileStoryUiState {
isArchiveModalOpen: boolean;
Expand Down Expand Up @@ -196,7 +196,7 @@ const useMobileStoryView = () => {

// Load session list on mount
useEffect(() => {
appInvoke<AgentSessionSummary[]>('list_agent_sessions', {
appInvoke('list_agent_sessions', {
prefix: 'story-session-',
sessionKind: 'story',
})
Expand All @@ -212,7 +212,7 @@ const useMobileStoryView = () => {
setSessionId(currentSessionId);
}
try {
const record = {
const record: AgentSessionRecord = {
id: currentSessionId,
title,
messages: list,
Expand All @@ -227,10 +227,10 @@ const useMobileStoryView = () => {
selectedWorldBookId,
dynamicRoleLoadingEnabled,
};
await appInvoke<AgentSessionSummary>('save_agent_session', { session: record });
await appInvoke('save_agent_session', { session: record });

// Update session summary list
const listRes = await appInvoke<AgentSessionSummary[]>('list_agent_sessions', {
const listRes = await appInvoke('list_agent_sessions', {
prefix: 'story-session-',
sessionKind: 'story',
});
Expand Down Expand Up @@ -262,10 +262,10 @@ const useMobileStoryView = () => {
messages,
finalFallback: '未命名故事',
summarize: async () => {
const res = await appInvoke<{ title: string }>('summarize_text', {
const res = await appInvoke('summarize_text', {
request: { text: chatHistoryText },
});
return res.title;
return typeof res === 'string' ? res : res.title;
},
});
setSessionTitle(finalTitle);
Expand All @@ -289,7 +289,7 @@ const useMobileStoryView = () => {
return;
}
try {
const record = await appInvoke<any>('load_agent_session', { id });
const record = await appInvoke('load_agent_session', { id });
setSessionId(record.id);
setSessionTitle(record.title);
setMessages(record.messages || []);
Expand Down Expand Up @@ -318,7 +318,7 @@ const useMobileStoryView = () => {
if (sessionId === id) {
createNewSession();
}
const listRes = await appInvoke<AgentSessionSummary[]>('list_agent_sessions', {
const listRes = await appInvoke('list_agent_sessions', {
prefix: 'story-session-',
sessionKind: 'story',
});
Expand Down Expand Up @@ -408,7 +408,7 @@ const useMobileStoryView = () => {
const storyAgentConfig = settings.agentConfigs?.[storyAgentConfigId] || {};

try {
const { runId } = await appInvoke<{ runId: string }>('start_chat_completion_stream', {
const { runId } = await appInvoke('start_chat_completion_stream', {
request: {
agentId: storyAgentConfigId,
modelInterface: settings.modelInterface,
Expand Down Expand Up @@ -630,7 +630,7 @@ const useMobileStoryView = () => {

const filteredCards = selectedCards.filter(card => tempSelectedCardIds.includes(card.id));
const results = await Promise.all(filteredCards.map(async (card) => {
const result = await appInvoke<string | Record<string, any>>('analyze_character_memory', {
const result = await appInvoke('analyze_character_memory', {
sessionId,
characterCardId: card.id,
});
Expand Down Expand Up @@ -692,14 +692,14 @@ const useMobileStoryView = () => {
message.success('记忆封存成功!故事已锁定归档。');

// Reload sessions
const listRes = await appInvoke<AgentSessionSummary[]>('list_agent_sessions', {
const listRes = await appInvoke('list_agent_sessions', {
prefix: 'story-session-',
sessionKind: 'story',
});
setSessions(listRes);

// Reload partner store
const partnerStoreContent = await appInvoke<string>('load_app_state', { name: 'partner-store' });
const partnerStoreContent = await appInvoke('load_app_state', { name: 'partner-store' });
if (partnerStoreContent) {
const parsed = JSON.parse(partnerStoreContent);
if (parsed.state) {
Expand Down
2 changes: 1 addition & 1 deletion src/stores/diskStorage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export function createDiskStorage(
return {
getItem: async () => {
try {
const content = await appInvoke<string>('load_app_state', { name });
const content = await appInvoke('load_app_state', { name });
return content;
} catch {
if (localStorageKey) {
Expand Down
2 changes: 1 addition & 1 deletion src/utils/bookTravelMaterials.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ const fileNameFromPath = (path: string) => {
};

export const resolveOutlineMaterial = async (path: string): Promise<BookTravelMaterial> => {
const content = await appInvoke<string>('read_file', { path });
const content = await appInvoke('read_file', { path });
return {
id: path,
title: fileNameFromPath(path),
Expand Down
Loading
Loading