1
This commit is contained in:
@@ -16,6 +16,8 @@ import type {
|
||||
} from '../../../packages/shared/src/contracts/customWorldAgent.js';
|
||||
import { badRequest, notFound } from '../errors.js';
|
||||
import { prepareEventStreamResponse } from '../http.js';
|
||||
import { normalizeCustomWorldProfile } from '../modules/custom-world/runtimeProfile.js';
|
||||
import type { CustomWorldProfile } from '../modules/custom-world/runtimeTypes.js';
|
||||
import { CustomWorldAgentAssetBridgeService } from './customWorldAgentAssetBridgeService.js';
|
||||
import { CustomWorldAgentAutoAssetService } from './customWorldAgentAutoAssetService.js';
|
||||
import { CustomWorldAgentChangeSummaryService } from './customWorldAgentChangeSummaryService.js';
|
||||
@@ -150,6 +152,8 @@ function buildOperation(type: CustomWorldAgentOperationRecord['type']) {
|
||||
? '正在把已确认设定编成第一版世界底稿。'
|
||||
: type === 'update_draft_card'
|
||||
? '正在把这次设定改动写回草稿。'
|
||||
: type === 'sync_result_profile'
|
||||
? '正在把结果页里的世界快照同步回当前草稿。'
|
||||
: type === 'generate_characters'
|
||||
? '正在围绕当前底稿补出新角色。'
|
||||
: type === 'generate_landmarks'
|
||||
@@ -194,6 +198,27 @@ function buildRoleAssetSyncResultText(params: {
|
||||
return `已把「${params.roleName}」的角色资产写回草稿,当前状态:${params.assetStatusLabel}。`;
|
||||
}
|
||||
|
||||
function syncResultProfileIntoDraftProfile(params: {
|
||||
currentDraftProfile: Record<string, unknown> | null | undefined;
|
||||
resultProfile: CustomWorldProfile;
|
||||
}) {
|
||||
const currentDraftProfile = params.currentDraftProfile ?? {};
|
||||
const resultProfile = params.resultProfile;
|
||||
|
||||
return {
|
||||
// 阶段一只回写基础摘要和完整 legacy 快照,避免把结果页的运行时结构反向拆回 foundation draft。
|
||||
...currentDraftProfile,
|
||||
name: resultProfile.name,
|
||||
subtitle: resultProfile.subtitle,
|
||||
summary: resultProfile.summary,
|
||||
tone: resultProfile.tone,
|
||||
playerGoal: resultProfile.playerGoal,
|
||||
majorFactions: resultProfile.majorFactions,
|
||||
coreConflicts: resultProfile.coreConflicts,
|
||||
legacyResultProfile: resultProfile as unknown as Record<string, unknown>,
|
||||
} satisfies Record<string, unknown>;
|
||||
}
|
||||
|
||||
function buildQuestionLines(
|
||||
pendingClarifications: CustomWorldPendingClarification[],
|
||||
) {
|
||||
@@ -548,6 +573,7 @@ export class CustomWorldAgentOrchestrator {
|
||||
|
||||
if (
|
||||
payload.action === 'update_draft_card' ||
|
||||
payload.action === 'sync_result_profile' ||
|
||||
payload.action === 'generate_characters' ||
|
||||
payload.action === 'generate_landmarks' ||
|
||||
payload.action === 'generate_role_assets' ||
|
||||
@@ -595,6 +621,32 @@ export class CustomWorldAgentOrchestrator {
|
||||
};
|
||||
}
|
||||
|
||||
if (payload.action === 'sync_result_profile') {
|
||||
const normalizedProfile = normalizeCustomWorldProfile(
|
||||
payload.profile,
|
||||
'',
|
||||
);
|
||||
if (!normalizedProfile) {
|
||||
throw badRequest('sync_result_profile requires a valid profile');
|
||||
}
|
||||
|
||||
const operation = buildOperation('sync_result_profile');
|
||||
await this.sessionStore.createOperation(userId, sessionId, operation);
|
||||
void this.processSyncResultProfileOperation({
|
||||
userId,
|
||||
sessionId,
|
||||
operationId: operation.operationId,
|
||||
payload: {
|
||||
...payload,
|
||||
profile: normalizedProfile as unknown as Record<string, unknown>,
|
||||
},
|
||||
});
|
||||
|
||||
return {
|
||||
operation,
|
||||
};
|
||||
}
|
||||
|
||||
if (payload.action === 'generate_characters') {
|
||||
if (payload.count < 1 || payload.count > 3) {
|
||||
throw badRequest('generate_characters count must be between 1 and 3');
|
||||
@@ -1113,6 +1165,97 @@ export class CustomWorldAgentOrchestrator {
|
||||
}
|
||||
}
|
||||
|
||||
private async processSyncResultProfileOperation(params: {
|
||||
userId: string;
|
||||
sessionId: string;
|
||||
operationId: string;
|
||||
payload: Extract<
|
||||
CustomWorldAgentActionRequest,
|
||||
{ action: 'sync_result_profile' }
|
||||
>;
|
||||
}) {
|
||||
const { userId, sessionId, operationId, payload } = params;
|
||||
|
||||
try {
|
||||
await this.sessionStore.updateOperation(userId, sessionId, operationId, {
|
||||
status: 'running',
|
||||
phaseLabel: '同步结果页快照',
|
||||
phaseDetail: '正在把结果页里的最新世界结构写回当前草稿。',
|
||||
progress: 36,
|
||||
});
|
||||
|
||||
const latestSession = (await this.sessionStore.get(
|
||||
userId,
|
||||
sessionId,
|
||||
)) as CustomWorldAgentSessionRecord | null;
|
||||
if (!latestSession) {
|
||||
throw new Error('custom world agent session not found');
|
||||
}
|
||||
|
||||
const resultProfile = payload.profile as unknown as CustomWorldProfile;
|
||||
const nextDraftProfile = syncResultProfileIntoDraftProfile({
|
||||
currentDraftProfile: latestSession.draftProfile,
|
||||
resultProfile,
|
||||
});
|
||||
|
||||
await this.sessionStore.updateOperation(userId, sessionId, operationId, {
|
||||
phaseLabel: '重编译草稿摘要',
|
||||
phaseDetail: '正在刷新草稿卡摘要、资产覆盖和结果页恢复快照。',
|
||||
progress: 72,
|
||||
});
|
||||
|
||||
const nextDraftCards = this.draftCompiler.compileDraftCards(nextDraftProfile);
|
||||
const assetCoverage = rebuildRoleAssetCoverage(nextDraftProfile);
|
||||
const nextStage =
|
||||
latestSession.stage === 'visual_refining'
|
||||
? ('visual_refining' as const)
|
||||
: ('object_refining' as const);
|
||||
const nextSuggestedActions = buildSuggestedActions({
|
||||
stage: nextStage,
|
||||
isReady: true,
|
||||
draftProfile: nextDraftProfile,
|
||||
draftCards: nextDraftCards,
|
||||
});
|
||||
|
||||
await this.sessionStore.replaceDerivedState(userId, sessionId, {
|
||||
stage: nextStage,
|
||||
draftProfile: nextDraftProfile,
|
||||
draftCards: nextDraftCards,
|
||||
assetCoverage,
|
||||
suggestedActions: nextSuggestedActions,
|
||||
recommendedReplies: [],
|
||||
});
|
||||
await this.sessionStore.appendCheckpoint(userId, sessionId, {
|
||||
label: '同步结果页编辑',
|
||||
});
|
||||
await this.sessionStore.appendMessage(
|
||||
userId,
|
||||
sessionId,
|
||||
buildActionResultMessage({
|
||||
relatedOperationId: operationId,
|
||||
text: '结果页里的最新世界结构已经同步回当前草稿。',
|
||||
}),
|
||||
);
|
||||
|
||||
await this.sessionStore.updateOperation(userId, sessionId, operationId, {
|
||||
status: 'completed',
|
||||
phaseLabel: '结果页快照已同步',
|
||||
phaseDetail: '当前结果页编辑内容已经并回 Agent 草稿主链。',
|
||||
progress: 100,
|
||||
error: null,
|
||||
});
|
||||
} catch (error) {
|
||||
await this.sessionStore.updateOperation(userId, sessionId, operationId, {
|
||||
status: 'failed',
|
||||
phaseLabel: '结果页同步失败',
|
||||
phaseDetail: '这一轮结果页编辑没有成功写回当前草稿。',
|
||||
progress: 100,
|
||||
error:
|
||||
error instanceof Error ? error.message : 'sync result profile failed',
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private async processGenerateCharactersOperation(params: {
|
||||
userId: string;
|
||||
sessionId: string;
|
||||
|
||||
Reference in New Issue
Block a user