Integrate role asset studio into custom world agent flow
This commit is contained in:
711
server-node/src/services/customWorldAgentSessionStore.ts
Normal file
711
server-node/src/services/customWorldAgentSessionStore.ts
Normal file
@@ -0,0 +1,711 @@
|
||||
import crypto from 'node:crypto';
|
||||
|
||||
import type {
|
||||
CustomWorldAssetCoverageSummary,
|
||||
CreatorIntentReadiness,
|
||||
CustomWorldAgentMessage,
|
||||
CustomWorldAgentOperationRecord,
|
||||
CustomWorldAgentSessionSnapshot,
|
||||
CustomWorldAgentStage,
|
||||
CustomWorldDraftCardSummary,
|
||||
CustomWorldPendingClarification,
|
||||
CustomWorldSuggestedAction,
|
||||
} from '../../../packages/shared/src/contracts/customWorldAgent.js';
|
||||
import type { CustomWorldSessionRecord as LegacyCustomWorldSessionRecord } from '../../../packages/shared/src/contracts/runtime.js';
|
||||
import type { RuntimeRepositoryPort } from '../repositories/runtimeRepository.js';
|
||||
import {
|
||||
buildPendingClarifications,
|
||||
evaluateCreatorIntentReadiness,
|
||||
resolveCreatorIntentStage,
|
||||
} from './customWorldAgentClarificationService.js';
|
||||
import {
|
||||
buildAnchorPackFromIntent,
|
||||
buildDraftSummaryFromIntent,
|
||||
buildDraftTitleFromIntent,
|
||||
createEmptyCreatorIntentRecord,
|
||||
extractCreatorIntentPatch,
|
||||
mergeCreatorIntentRecord,
|
||||
normalizeCreatorIntentRecord,
|
||||
} from './customWorldAgentIntentExtractionService.js';
|
||||
import { rebuildRoleAssetCoverage } from './customWorldAgentRoleAssetStateService.js';
|
||||
|
||||
export const CUSTOM_WORLD_AGENT_SESSION_ID_PREFIX =
|
||||
'custom-world-agent-session-';
|
||||
|
||||
export type CustomWorldAgentSessionRecord = {
|
||||
sessionId: string;
|
||||
userId: string;
|
||||
seedText: string;
|
||||
stage: CustomWorldAgentStage;
|
||||
focusCardId: string | null;
|
||||
creatorIntent: Record<string, unknown> | null;
|
||||
creatorIntentReadiness: CreatorIntentReadiness;
|
||||
anchorPack: Record<string, unknown> | null;
|
||||
lockState: Record<string, unknown> | null;
|
||||
draftProfile: Record<string, unknown> | null;
|
||||
messages: CustomWorldAgentMessage[];
|
||||
draftCards: CustomWorldDraftCardSummary[];
|
||||
pendingClarifications: CustomWorldPendingClarification[];
|
||||
suggestedActions: CustomWorldSuggestedAction[];
|
||||
recommendedReplies: string[];
|
||||
qualityFindings: Array<{
|
||||
id: string;
|
||||
severity: 'info' | 'warning' | 'blocker';
|
||||
code: string;
|
||||
targetId?: string | null;
|
||||
message: string;
|
||||
}>;
|
||||
assetCoverage: CustomWorldAssetCoverageSummary;
|
||||
operations: CustomWorldAgentOperationRecord[];
|
||||
checkpoints: Array<{
|
||||
checkpointId: string;
|
||||
createdAt: string;
|
||||
label: string;
|
||||
}>;
|
||||
createdAt: string;
|
||||
updatedAt: string;
|
||||
};
|
||||
|
||||
type CreateSessionInput = {
|
||||
seedText?: string;
|
||||
welcomeMessage: string;
|
||||
pendingClarifications: CustomWorldAgentSessionRecord['pendingClarifications'];
|
||||
creatorIntent?: CustomWorldAgentSessionRecord['creatorIntent'];
|
||||
creatorIntentReadiness?: CreatorIntentReadiness;
|
||||
anchorPack?: CustomWorldAgentSessionRecord['anchorPack'];
|
||||
draftProfile?: CustomWorldAgentSessionRecord['draftProfile'];
|
||||
stage?: CustomWorldAgentStage;
|
||||
suggestedActions: CustomWorldSuggestedAction[];
|
||||
recommendedReplies?: string[];
|
||||
};
|
||||
|
||||
function cloneRecord<T>(value: T): T {
|
||||
return JSON.parse(JSON.stringify(value)) as T;
|
||||
}
|
||||
|
||||
function toRecord(value: unknown) {
|
||||
return value && typeof value === 'object' && !Array.isArray(value)
|
||||
? (value as Record<string, unknown>)
|
||||
: null;
|
||||
}
|
||||
|
||||
function toText(value: unknown) {
|
||||
return typeof value === 'string' ? value.trim() : '';
|
||||
}
|
||||
|
||||
function isStage(value: unknown): value is CustomWorldAgentStage {
|
||||
return (
|
||||
value === 'collecting_intent' ||
|
||||
value === 'clarifying' ||
|
||||
value === 'foundation_review' ||
|
||||
value === 'object_refining' ||
|
||||
value === 'visual_refining' ||
|
||||
value === 'long_tail_review' ||
|
||||
value === 'ready_to_publish' ||
|
||||
value === 'published' ||
|
||||
value === 'error'
|
||||
);
|
||||
}
|
||||
|
||||
function isAgentSessionRecord(
|
||||
value: unknown,
|
||||
): value is CustomWorldAgentSessionRecord {
|
||||
const record = toRecord(value);
|
||||
if (!record) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
typeof record.sessionId === 'string' &&
|
||||
record.sessionId.startsWith(CUSTOM_WORLD_AGENT_SESSION_ID_PREFIX) &&
|
||||
typeof record.userId === 'string' &&
|
||||
isStage(record.stage) &&
|
||||
Array.isArray(record.messages) &&
|
||||
Array.isArray(record.operations) &&
|
||||
typeof record.createdAt === 'string' &&
|
||||
typeof record.updatedAt === 'string'
|
||||
);
|
||||
}
|
||||
|
||||
function isCreatorIntentReadiness(
|
||||
value: unknown,
|
||||
): value is CreatorIntentReadiness {
|
||||
const record = toRecord(value);
|
||||
if (!record) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return (
|
||||
typeof record.isReady === 'boolean' &&
|
||||
Array.isArray(record.completedKeys) &&
|
||||
Array.isArray(record.missingKeys)
|
||||
);
|
||||
}
|
||||
|
||||
function mapLegacyClarificationTargetKey(id: string) {
|
||||
if (id === 'world_hook') return 'world_hook';
|
||||
if (id === 'player_premise') return 'player_premise';
|
||||
if (id === 'theme_and_tone' || id === 'tone_boundary') {
|
||||
return 'theme_and_tone';
|
||||
}
|
||||
if (id === 'core_conflict') return 'core_conflict';
|
||||
if (id === 'relationship_seed' || id === 'relationship_hook') {
|
||||
return 'relationship_seed';
|
||||
}
|
||||
if (id === 'iconic_element' || id === 'iconic_elements') {
|
||||
return 'iconic_element';
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
function hasUserInput(record: CustomWorldAgentSessionRecord) {
|
||||
return (
|
||||
Boolean(record.seedText.trim()) ||
|
||||
record.messages.some(
|
||||
(message) => message.role === 'user' && message.text.trim(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
function buildCompatibleCreatorIntent(record: CustomWorldAgentSessionRecord) {
|
||||
const existingIntent =
|
||||
normalizeCreatorIntentRecord(record.creatorIntent) ??
|
||||
createEmptyCreatorIntentRecord('freeform');
|
||||
|
||||
if (!record.seedText.trim()) {
|
||||
return existingIntent;
|
||||
}
|
||||
|
||||
const seedPatch = extractCreatorIntentPatch({
|
||||
currentIntent: existingIntent,
|
||||
latestUserMessage: record.seedText,
|
||||
});
|
||||
|
||||
return mergeCreatorIntentRecord(existingIntent, seedPatch);
|
||||
}
|
||||
|
||||
function buildCompatibleReadiness(record: CustomWorldAgentSessionRecord) {
|
||||
if (
|
||||
isCreatorIntentReadiness(
|
||||
(record as Record<string, unknown>).creatorIntentReadiness,
|
||||
)
|
||||
) {
|
||||
return record.creatorIntentReadiness;
|
||||
}
|
||||
|
||||
return evaluateCreatorIntentReadiness(
|
||||
normalizeCreatorIntentRecord(record.creatorIntent),
|
||||
);
|
||||
}
|
||||
|
||||
function buildCompatiblePendingClarifications(
|
||||
record: CustomWorldAgentSessionRecord,
|
||||
) {
|
||||
const normalizedIntent = normalizeCreatorIntentRecord(record.creatorIntent);
|
||||
const readiness = buildCompatibleReadiness(record);
|
||||
const legacyClarifications = Array.isArray(record.pendingClarifications)
|
||||
? record.pendingClarifications
|
||||
: [];
|
||||
|
||||
const nextClarifications = legacyClarifications
|
||||
.map((entry, index) => {
|
||||
const targetKey = mapLegacyClarificationTargetKey(entry.id);
|
||||
if (!targetKey) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return {
|
||||
id: entry.id || targetKey,
|
||||
label: entry.label || '待补充问题',
|
||||
question: entry.question || '',
|
||||
targetKey,
|
||||
priority:
|
||||
typeof entry.priority === 'number' ? entry.priority : index + 1,
|
||||
answer: entry.answer,
|
||||
} satisfies CustomWorldPendingClarification;
|
||||
})
|
||||
.filter((entry): entry is CustomWorldPendingClarification =>
|
||||
Boolean(entry?.question),
|
||||
)
|
||||
.slice(0, 3);
|
||||
|
||||
if (nextClarifications.length > 0) {
|
||||
return nextClarifications;
|
||||
}
|
||||
|
||||
return buildPendingClarifications(normalizedIntent, readiness);
|
||||
}
|
||||
|
||||
function buildCompatibleDraftProfile(
|
||||
record: CustomWorldAgentSessionRecord,
|
||||
creatorIntent: ReturnType<typeof buildCompatibleCreatorIntent>,
|
||||
) {
|
||||
const existingDraftProfile = toRecord(record.draftProfile);
|
||||
const hasFoundationContent = Boolean(
|
||||
existingDraftProfile &&
|
||||
(typeof existingDraftProfile.name === 'string' ||
|
||||
Array.isArray(existingDraftProfile.playableNpcs) ||
|
||||
Array.isArray(existingDraftProfile.landmarks) ||
|
||||
Array.isArray(existingDraftProfile.factions) ||
|
||||
Array.isArray(existingDraftProfile.threads) ||
|
||||
Array.isArray(existingDraftProfile.chapters)),
|
||||
);
|
||||
|
||||
if (hasFoundationContent) {
|
||||
return {
|
||||
...existingDraftProfile,
|
||||
name:
|
||||
toText(existingDraftProfile?.name) ||
|
||||
toText(existingDraftProfile?.title) ||
|
||||
buildDraftTitleFromIntent(creatorIntent),
|
||||
summary:
|
||||
toText(existingDraftProfile?.summary) ||
|
||||
buildDraftSummaryFromIntent(creatorIntent),
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
...(existingDraftProfile ?? {}),
|
||||
title:
|
||||
toText(existingDraftProfile?.title) || buildDraftTitleFromIntent(creatorIntent),
|
||||
summary:
|
||||
toText(existingDraftProfile?.summary) ||
|
||||
buildDraftSummaryFromIntent(creatorIntent),
|
||||
};
|
||||
}
|
||||
|
||||
function buildCompatibleSuggestedActions(params: {
|
||||
record: CustomWorldAgentSessionRecord;
|
||||
stage: CustomWorldAgentStage;
|
||||
readiness: CreatorIntentReadiness;
|
||||
draftProfile: Record<string, unknown>;
|
||||
}) {
|
||||
if (params.record.suggestedActions.length > 0) {
|
||||
return params.record.suggestedActions;
|
||||
}
|
||||
|
||||
const actions: CustomWorldSuggestedAction[] = [
|
||||
{
|
||||
id: 'request_summary',
|
||||
type: 'request_summary',
|
||||
label:
|
||||
params.stage === 'object_refining' || params.stage === 'visual_refining'
|
||||
? '总结当前世界底稿'
|
||||
: '总结当前设定',
|
||||
},
|
||||
];
|
||||
const playableNpcs = Array.isArray(params.draftProfile.playableNpcs)
|
||||
? params.draftProfile.playableNpcs
|
||||
: [];
|
||||
const storyNpcs = Array.isArray(params.draftProfile.storyNpcs)
|
||||
? params.draftProfile.storyNpcs
|
||||
: [];
|
||||
const landmarks = Array.isArray(params.draftProfile.landmarks)
|
||||
? params.draftProfile.landmarks
|
||||
: [];
|
||||
|
||||
if (params.stage === 'foundation_review' && params.readiness.isReady) {
|
||||
actions.push({
|
||||
id: 'draft_foundation',
|
||||
type: 'draft_foundation',
|
||||
label: '整理一版世界底稿',
|
||||
});
|
||||
return actions;
|
||||
}
|
||||
|
||||
if (params.stage === 'object_refining' || params.stage === 'visual_refining') {
|
||||
const firstCharacter = toRecord([...playableNpcs, ...storyNpcs][0]);
|
||||
const firstLandmark = toRecord(landmarks[0]);
|
||||
|
||||
actions.push({
|
||||
id: 'refine_world',
|
||||
type: 'refine_focus_target',
|
||||
label: '先看世界总卡',
|
||||
targetId: 'world-foundation',
|
||||
});
|
||||
|
||||
if (firstCharacter) {
|
||||
actions.push({
|
||||
id: `refine-character-${toText(firstCharacter.id) || 'seed'}`,
|
||||
type: 'refine_focus_target',
|
||||
label: `精修角色:${toText(firstCharacter.name) || '关键角色'}`,
|
||||
targetId: toText(firstCharacter.id) || null,
|
||||
});
|
||||
}
|
||||
|
||||
if (firstLandmark) {
|
||||
actions.push({
|
||||
id: `refine-landmark-${toText(firstLandmark.id) || 'seed'}`,
|
||||
type: 'refine_focus_target',
|
||||
label: `继续补地点:${toText(firstLandmark.name) || '关键地点'}`,
|
||||
targetId: toText(firstLandmark.id) || null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return actions;
|
||||
}
|
||||
|
||||
function normalizeRecommendedReplies(value: unknown) {
|
||||
if (!Array.isArray(value)) {
|
||||
return [];
|
||||
}
|
||||
|
||||
return value
|
||||
.map((item) => toText(item))
|
||||
.filter(Boolean)
|
||||
.slice(0, 3);
|
||||
}
|
||||
|
||||
function buildCompatibleAssetCoverage(
|
||||
record: CustomWorldAgentSessionRecord,
|
||||
draftProfile: Record<string, unknown>,
|
||||
) {
|
||||
const derivedCoverage = rebuildRoleAssetCoverage(draftProfile);
|
||||
const existingCoverage = toRecord(record.assetCoverage);
|
||||
const sceneAssets = Array.isArray(existingCoverage?.sceneAssets)
|
||||
? existingCoverage.sceneAssets
|
||||
: [];
|
||||
const allSceneAssetsReady =
|
||||
typeof existingCoverage?.allSceneAssetsReady === 'boolean'
|
||||
? existingCoverage.allSceneAssetsReady
|
||||
: false;
|
||||
|
||||
return {
|
||||
...derivedCoverage,
|
||||
sceneAssets,
|
||||
allSceneAssetsReady,
|
||||
} satisfies CustomWorldAssetCoverageSummary;
|
||||
}
|
||||
|
||||
function applyCompatibility(record: CustomWorldAgentSessionRecord) {
|
||||
const creatorIntent = buildCompatibleCreatorIntent(record);
|
||||
const creatorIntentReadiness = evaluateCreatorIntentReadiness(creatorIntent);
|
||||
const stage =
|
||||
record.stage === 'collecting_intent' ||
|
||||
record.stage === 'clarifying' ||
|
||||
record.stage === 'foundation_review'
|
||||
? resolveCreatorIntentStage({
|
||||
hasUserInput: hasUserInput(record),
|
||||
readiness: creatorIntentReadiness,
|
||||
})
|
||||
: record.stage;
|
||||
const pendingClarifications = buildCompatiblePendingClarifications({
|
||||
...record,
|
||||
creatorIntent,
|
||||
creatorIntentReadiness,
|
||||
});
|
||||
const draftProfile = buildCompatibleDraftProfile(record, creatorIntent);
|
||||
|
||||
return {
|
||||
...record,
|
||||
stage,
|
||||
creatorIntent,
|
||||
creatorIntentReadiness,
|
||||
anchorPack:
|
||||
record.anchorPack && Object.keys(record.anchorPack).length > 0
|
||||
? record.anchorPack
|
||||
: buildAnchorPackFromIntent(creatorIntent, {
|
||||
completedKeys: creatorIntentReadiness.completedKeys,
|
||||
missingKeys: creatorIntentReadiness.missingKeys,
|
||||
}),
|
||||
draftProfile,
|
||||
pendingClarifications,
|
||||
suggestedActions: buildCompatibleSuggestedActions({
|
||||
record,
|
||||
stage,
|
||||
readiness: creatorIntentReadiness,
|
||||
draftProfile,
|
||||
}),
|
||||
assetCoverage: buildCompatibleAssetCoverage(record, draftProfile),
|
||||
recommendedReplies: normalizeRecommendedReplies(
|
||||
(record as Record<string, unknown>).recommendedReplies,
|
||||
),
|
||||
} satisfies CustomWorldAgentSessionRecord;
|
||||
}
|
||||
|
||||
function toSnapshot(
|
||||
record: CustomWorldAgentSessionRecord,
|
||||
): CustomWorldAgentSessionSnapshot {
|
||||
return {
|
||||
sessionId: record.sessionId,
|
||||
stage: record.stage,
|
||||
focusCardId: record.focusCardId,
|
||||
creatorIntent: cloneRecord(record.creatorIntent),
|
||||
creatorIntentReadiness: cloneRecord(record.creatorIntentReadiness),
|
||||
anchorPack: cloneRecord(record.anchorPack),
|
||||
lockState: cloneRecord(record.lockState),
|
||||
draftProfile: cloneRecord(record.draftProfile),
|
||||
messages: cloneRecord(record.messages),
|
||||
draftCards: cloneRecord(record.draftCards),
|
||||
pendingClarifications: cloneRecord(record.pendingClarifications),
|
||||
suggestedActions: cloneRecord(record.suggestedActions),
|
||||
recommendedReplies: cloneRecord(record.recommendedReplies),
|
||||
qualityFindings: cloneRecord(record.qualityFindings),
|
||||
assetCoverage: cloneRecord(record.assetCoverage),
|
||||
updatedAt: record.updatedAt,
|
||||
};
|
||||
}
|
||||
|
||||
export class CustomWorldAgentSessionStore {
|
||||
constructor(private readonly runtimeRepository: RuntimeRepositoryPort) {}
|
||||
|
||||
private async persist(record: CustomWorldAgentSessionRecord) {
|
||||
await this.runtimeRepository.upsertCustomWorldSession(
|
||||
record.userId,
|
||||
record.sessionId,
|
||||
record as unknown as LegacyCustomWorldSessionRecord,
|
||||
);
|
||||
return cloneRecord(record);
|
||||
}
|
||||
|
||||
private async mutate(
|
||||
userId: string,
|
||||
sessionId: string,
|
||||
mutateFn: (record: CustomWorldAgentSessionRecord) => void,
|
||||
) {
|
||||
const current = await this.get(userId, sessionId);
|
||||
if (!current) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const nextRecord = cloneRecord(current);
|
||||
mutateFn(nextRecord);
|
||||
nextRecord.updatedAt = new Date().toISOString();
|
||||
return this.persist(nextRecord);
|
||||
}
|
||||
|
||||
async create(userId: string, input: CreateSessionInput) {
|
||||
const sessionId = `${CUSTOM_WORLD_AGENT_SESSION_ID_PREFIX}${crypto.randomBytes(16).toString('hex')}`;
|
||||
const now = new Date().toISOString();
|
||||
const welcomeMessage: CustomWorldAgentMessage = {
|
||||
id: `message-${crypto.randomBytes(8).toString('hex')}`,
|
||||
role: 'assistant',
|
||||
kind: 'chat',
|
||||
text: input.welcomeMessage,
|
||||
createdAt: now,
|
||||
relatedOperationId: null,
|
||||
};
|
||||
const record: CustomWorldAgentSessionRecord = {
|
||||
sessionId,
|
||||
userId,
|
||||
seedText: input.seedText?.trim() ?? '',
|
||||
stage: input.stage ?? 'collecting_intent',
|
||||
focusCardId: null,
|
||||
creatorIntent: cloneRecord(input.creatorIntent ?? {}),
|
||||
creatorIntentReadiness: input.creatorIntentReadiness ?? {
|
||||
isReady: false,
|
||||
completedKeys: [],
|
||||
missingKeys: [],
|
||||
},
|
||||
anchorPack: cloneRecord(input.anchorPack ?? {}),
|
||||
lockState: {},
|
||||
draftProfile: cloneRecord(input.draftProfile ?? {}),
|
||||
messages: [welcomeMessage],
|
||||
draftCards: [],
|
||||
pendingClarifications: cloneRecord(input.pendingClarifications),
|
||||
suggestedActions: cloneRecord(input.suggestedActions),
|
||||
recommendedReplies: cloneRecord(input.recommendedReplies ?? []),
|
||||
qualityFindings: [],
|
||||
assetCoverage: rebuildRoleAssetCoverage(input.draftProfile ?? {}),
|
||||
operations: [],
|
||||
checkpoints: [],
|
||||
createdAt: now,
|
||||
updatedAt: now,
|
||||
};
|
||||
|
||||
const compatibleRecord = applyCompatibility(record);
|
||||
await this.persist(compatibleRecord);
|
||||
return cloneRecord(compatibleRecord);
|
||||
}
|
||||
|
||||
async list(userId: string) {
|
||||
const records =
|
||||
await this.runtimeRepository.listCustomWorldSessions(userId);
|
||||
|
||||
return records
|
||||
.filter((record) => isAgentSessionRecord(record))
|
||||
.map((record) => cloneRecord(applyCompatibility(record)))
|
||||
.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
|
||||
}
|
||||
|
||||
async get(userId: string, sessionId: string) {
|
||||
if (!sessionId.trim()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const record = await this.runtimeRepository.getCustomWorldSession(
|
||||
userId,
|
||||
sessionId,
|
||||
);
|
||||
if (!isAgentSessionRecord(record)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return cloneRecord(applyCompatibility(record));
|
||||
}
|
||||
|
||||
async getSnapshot(userId: string, sessionId: string) {
|
||||
const record = await this.get(userId, sessionId);
|
||||
return record ? toSnapshot(record) : null;
|
||||
}
|
||||
|
||||
async appendMessage(
|
||||
userId: string,
|
||||
sessionId: string,
|
||||
message: CustomWorldAgentMessage,
|
||||
) {
|
||||
return this.mutate(userId, sessionId, (record) => {
|
||||
record.messages.push(cloneRecord(message));
|
||||
});
|
||||
}
|
||||
|
||||
async replaceDerivedState(
|
||||
userId: string,
|
||||
sessionId: string,
|
||||
patch: Partial<
|
||||
Pick<
|
||||
CustomWorldAgentSessionRecord,
|
||||
| 'stage'
|
||||
| 'creatorIntent'
|
||||
| 'creatorIntentReadiness'
|
||||
| 'anchorPack'
|
||||
| 'lockState'
|
||||
| 'draftProfile'
|
||||
| 'pendingClarifications'
|
||||
| 'suggestedActions'
|
||||
| 'recommendedReplies'
|
||||
| 'draftCards'
|
||||
| 'qualityFindings'
|
||||
| 'focusCardId'
|
||||
| 'assetCoverage'
|
||||
>
|
||||
>,
|
||||
) {
|
||||
return this.mutate(userId, sessionId, (record) => {
|
||||
if (patch.stage) {
|
||||
record.stage = patch.stage;
|
||||
}
|
||||
if (patch.focusCardId !== undefined) {
|
||||
record.focusCardId = patch.focusCardId;
|
||||
}
|
||||
if (patch.creatorIntent !== undefined) {
|
||||
record.creatorIntent = cloneRecord(patch.creatorIntent);
|
||||
}
|
||||
if (patch.creatorIntentReadiness !== undefined) {
|
||||
record.creatorIntentReadiness = cloneRecord(
|
||||
patch.creatorIntentReadiness,
|
||||
);
|
||||
}
|
||||
if (patch.anchorPack !== undefined) {
|
||||
record.anchorPack = cloneRecord(patch.anchorPack);
|
||||
}
|
||||
if (patch.lockState !== undefined) {
|
||||
record.lockState = cloneRecord(patch.lockState);
|
||||
}
|
||||
if (patch.draftProfile !== undefined) {
|
||||
record.draftProfile = cloneRecord(patch.draftProfile);
|
||||
}
|
||||
if (patch.pendingClarifications !== undefined) {
|
||||
record.pendingClarifications = cloneRecord(patch.pendingClarifications);
|
||||
}
|
||||
if (patch.suggestedActions !== undefined) {
|
||||
record.suggestedActions = cloneRecord(patch.suggestedActions);
|
||||
}
|
||||
if (patch.recommendedReplies !== undefined) {
|
||||
record.recommendedReplies = cloneRecord(patch.recommendedReplies);
|
||||
}
|
||||
if (patch.draftCards !== undefined) {
|
||||
record.draftCards = cloneRecord(patch.draftCards);
|
||||
}
|
||||
if (patch.qualityFindings !== undefined) {
|
||||
record.qualityFindings = cloneRecord(patch.qualityFindings);
|
||||
}
|
||||
if (patch.assetCoverage !== undefined) {
|
||||
record.assetCoverage = cloneRecord(patch.assetCoverage);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async createOperation(
|
||||
userId: string,
|
||||
sessionId: string,
|
||||
operation: CustomWorldAgentOperationRecord,
|
||||
) {
|
||||
return this.mutate(userId, sessionId, (record) => {
|
||||
record.operations.push(cloneRecord(operation));
|
||||
});
|
||||
}
|
||||
|
||||
async getOperation(userId: string, sessionId: string, operationId: string) {
|
||||
const record = await this.get(userId, sessionId);
|
||||
if (!record) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const operation = record.operations.find(
|
||||
(item) => item.operationId === operationId,
|
||||
);
|
||||
return operation ? cloneRecord(operation) : null;
|
||||
}
|
||||
|
||||
async updateOperation(
|
||||
userId: string,
|
||||
sessionId: string,
|
||||
operationId: string,
|
||||
patch: Partial<CustomWorldAgentOperationRecord>,
|
||||
) {
|
||||
return this.mutate(userId, sessionId, (record) => {
|
||||
const operation = record.operations.find(
|
||||
(item) => item.operationId === operationId,
|
||||
);
|
||||
if (!operation) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (patch.type) {
|
||||
operation.type = patch.type;
|
||||
}
|
||||
if (patch.status) {
|
||||
operation.status = patch.status;
|
||||
}
|
||||
if (patch.phaseLabel) {
|
||||
operation.phaseLabel = patch.phaseLabel;
|
||||
}
|
||||
if (patch.phaseDetail) {
|
||||
operation.phaseDetail = patch.phaseDetail;
|
||||
}
|
||||
if (typeof patch.progress === 'number') {
|
||||
operation.progress = patch.progress;
|
||||
}
|
||||
if (patch.error !== undefined) {
|
||||
operation.error = patch.error;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
async appendCheckpoint(
|
||||
userId: string,
|
||||
sessionId: string,
|
||||
input: {
|
||||
checkpointId?: string;
|
||||
label: string;
|
||||
},
|
||||
) {
|
||||
return this.mutate(userId, sessionId, (record) => {
|
||||
record.checkpoints.push({
|
||||
checkpointId:
|
||||
input.checkpointId ||
|
||||
`checkpoint-${crypto.randomBytes(8).toString('hex')}`,
|
||||
createdAt: new Date().toISOString(),
|
||||
label: input.label,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
async listDraftCards(userId: string, sessionId: string) {
|
||||
const record = await this.get(userId, sessionId);
|
||||
return record ? cloneRecord(record.draftCards) : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user