This commit is contained in:
2026-04-28 19:36:39 +08:00
parent a9febe7678
commit f0471a4f8d
206 changed files with 18456 additions and 10133 deletions

View File

@@ -4,9 +4,9 @@ import type {
CustomWorldAgentOperationRecord,
CustomWorldAgentSessionSnapshot,
} from '../../../packages/shared/src/contracts/customWorldAgent';
import type {
CustomWorldLibraryEntry,
} from '../../../packages/shared/src/contracts/runtime';
import type { RpgCreationResultView } from '../../../packages/shared/src/contracts/rpgCreationResultView';
import type { CustomWorldLibraryEntry } from '../../../packages/shared/src/contracts/runtime';
import { normalizeCustomWorldProfileRecord } from '../../data/customWorldLibrary';
import {
executeRpgCreationAction,
getRpgCreationOperation,
@@ -14,7 +14,6 @@ import {
} from '../../services/rpg-creation';
import type { CustomWorldProfile } from '../../types';
import {
normalizeAgentBackedProfile,
resolveRpgCreationErrorMessage,
stringifyAgentBackedProfile,
} from './rpgEntryShared';
@@ -27,7 +26,6 @@ import type {
type UseRpgCreationResultAutosaveParams = {
selectionStage: SelectionStage;
activeAgentSessionId: string | null;
agentSession: CustomWorldAgentSessionSnapshot | null;
generatedCustomWorldProfile: CustomWorldProfile | null;
isAgentDraftResultView: boolean;
userId: string | null | undefined;
@@ -55,8 +53,11 @@ type UseRpgCreationResultAutosaveParams = {
syncAgentSessionSnapshot: (
sessionId: string,
) => Promise<CustomWorldAgentSessionSnapshot | null>;
syncAgentCreationResultView: (
sessionId: string,
) => Promise<RpgCreationResultView | null>;
buildDraftResultProfile: (
session: CustomWorldAgentSessionSnapshot | null,
view: RpgCreationResultView | null,
) => CustomWorldProfile | null;
};
@@ -70,7 +71,6 @@ export function useRpgCreationResultAutosave(
const {
selectionStage,
activeAgentSessionId,
agentSession,
generatedCustomWorldProfile,
isAgentDraftResultView,
userId,
@@ -81,6 +81,7 @@ export function useRpgCreationResultAutosave(
refreshCustomWorldWorks,
persistAgentUiState,
syncAgentSessionSnapshot,
syncAgentCreationResultView,
buildDraftResultProfile,
} = params;
@@ -118,29 +119,33 @@ export function useRpgCreationResultAutosave(
return null;
}
const normalizedProfile = normalizeAgentBackedProfile(profile);
const profileSignature = stringifyAgentBackedProfile(normalizedProfile);
const requestId = latestAutoSaveRequestIdRef.current + 1;
latestAutoSaveRequestIdRef.current = requestId;
setCustomWorldAutoSaveState('saving');
setCustomWorldAutoSaveError(null);
try {
const mutation =
await upsertRpgWorldProfile(
normalizedProfile,
{
sourceAgentSessionId:
isAgentDraftResultView && activeAgentSessionId
? activeAgentSessionId
: null,
},
);
const mutation = await upsertRpgWorldProfile(profile, {
sourceAgentSessionId:
isAgentDraftResultView && activeAgentSessionId
? activeAgentSessionId
: null,
});
if (latestAutoSaveRequestIdRef.current !== requestId) {
return mutation;
}
lastAutoSavedProfileSignatureRef.current = profileSignature;
const canonicalProfile =
normalizeCustomWorldProfileRecord(mutation.entry.profile) ??
mutation.entry.profile;
// Agent 结果页的界面真相来自 result-view作品库响应只用于列表与签名回写
// 避免旧兼容响应缺字段时覆盖当前完整编辑态。
lastAutoSavedProfileSignatureRef.current = stringifyAgentBackedProfile(
isAgentDraftResultView ? profile : canonicalProfile,
);
if (!isAgentDraftResultView) {
setGeneratedCustomWorldProfile(canonicalProfile);
}
setSavedCustomWorldEntries(mutation.entries);
if (userId) {
void refreshCustomWorldWorks().catch(() => {});
@@ -174,73 +179,8 @@ export function useRpgCreationResultAutosave(
refreshCustomWorldWorks,
setSavedCustomWorldEntries,
setSelectedDetailEntry,
userId,
],
);
const syncAgentDraftResultProfile = useCallback(
async (profile: CustomWorldProfile) => {
if (!activeAgentSessionId) {
return {
session: null,
profile: null,
} satisfies SyncedAgentDraftResult;
}
const normalizedProfile = normalizeAgentBackedProfile(profile);
const profileSignature = stringifyAgentBackedProfile(normalizedProfile);
const latestSessionProfile = buildDraftResultProfile(agentSession);
const latestSessionProfileSignature = latestSessionProfile
? stringifyAgentBackedProfile(latestSessionProfile)
: '';
const shouldRefreshPublishGate = Boolean(
agentSession?.resultPreview && !agentSession.resultPreview.publishReady,
);
if (
latestSessionProfileSignature === profileSignature &&
!shouldRefreshPublishGate
) {
latestAgentResultSyncSignatureRef.current = profileSignature;
return {
session: agentSession,
profile: normalizeAgentBackedProfile(latestSessionProfile ?? profile),
} satisfies SyncedAgentDraftResult;
}
if (
latestAgentResultSyncSignatureRef.current === profileSignature &&
!shouldRefreshPublishGate
) {
return {
session: agentSession,
profile: normalizeAgentBackedProfile(latestSessionProfile ?? profile),
} satisfies SyncedAgentDraftResult;
}
// Agent 结果页不再把前端 profile 回写到 session。
// 这里只刷新后端结果页快照,避免在采集/生成早期误触 sync_result_profile。
const latestSession = await syncAgentSessionSnapshot(activeAgentSessionId);
const latestProfile = normalizeAgentBackedProfile(
buildDraftResultProfile(latestSession) ?? profile,
);
if (latestProfile) {
setGeneratedCustomWorldProfile(latestProfile);
}
latestAgentResultSyncSignatureRef.current =
stringifyAgentBackedProfile(latestProfile);
return {
session: latestSession,
profile: latestProfile,
} satisfies SyncedAgentDraftResult;
},
[
activeAgentSessionId,
agentSession,
buildDraftResultProfile,
setGeneratedCustomWorldProfile,
syncAgentSessionSnapshot,
userId,
],
);
@@ -290,6 +230,65 @@ export function useRpgCreationResultAutosave(
],
);
const syncAgentDraftResultProfile = useCallback(
async (profile: CustomWorldProfile) => {
if (!activeAgentSessionId) {
return {
session: null,
profile: null,
} satisfies SyncedAgentDraftResult;
}
const profileSignature = stringifyAgentBackedProfile(profile);
const currentView =
await syncAgentCreationResultView(activeAgentSessionId);
if (!currentView?.canSyncResultProfile) {
const latestProfile = buildDraftResultProfile(currentView) ?? profile;
if (latestProfile) {
setGeneratedCustomWorldProfile(latestProfile);
}
latestAgentResultSyncSignatureRef.current =
stringifyAgentBackedProfile(latestProfile);
return {
session: currentView?.session ?? null,
profile: latestProfile,
view: currentView,
} satisfies SyncedAgentDraftResult;
}
if (latestAgentResultSyncSignatureRef.current !== profileSignature) {
await executeAgentActionAndWait({
action: 'sync_result_profile',
profile: profile as unknown as Record<string, unknown>,
});
latestAgentResultSyncSignatureRef.current = profileSignature;
}
const latestView =
await syncAgentCreationResultView(activeAgentSessionId);
const latestProfile = buildDraftResultProfile(latestView) ?? profile;
if (latestProfile) {
setGeneratedCustomWorldProfile(latestProfile);
}
latestAgentResultSyncSignatureRef.current =
stringifyAgentBackedProfile(latestProfile);
return {
session: latestView?.session ?? null,
profile: latestProfile,
view: latestView,
} satisfies SyncedAgentDraftResult;
},
[
activeAgentSessionId,
buildDraftResultProfile,
executeAgentActionAndWait,
setGeneratedCustomWorldProfile,
syncAgentCreationResultView,
],
);
useEffect(
() => () => {
if (customWorldAutoSaveTimeoutRef.current !== null) {
@@ -313,7 +312,9 @@ export function useRpgCreationResultAutosave(
return;
}
const nextSignature = stringifyAgentBackedProfile(generatedCustomWorldProfile);
const nextSignature = stringifyAgentBackedProfile(
generatedCustomWorldProfile,
);
if (nextSignature === lastAutoSavedProfileSignatureRef.current) {
return;
}
@@ -328,14 +329,16 @@ export function useRpgCreationResultAutosave(
void (async () => {
isCustomWorldAutoSaveBusyRef.current = true;
try {
let latestProfileToSave = normalizeAgentBackedProfile(profileToSave);
let latestProfileToSave = profileToSave;
if (isAgentDraftResultView) {
const syncedResult =
await syncAgentDraftResultProfile(profileToSave);
if (syncedResult.view && !syncedResult.view.canAutosaveLibrary) {
setCustomWorldAutoSaveState('idle');
return;
}
// 作品库自动保存优先落同步后 session 重编译出的结果,避免继续保存旧的前端内存态。
latestProfileToSave = normalizeAgentBackedProfile(
syncedResult.profile ?? profileToSave,
);
latestProfileToSave = syncedResult.profile ?? profileToSave;
}
await saveGeneratedCustomWorld(latestProfileToSave);
} catch (error) {