merge: integrate marketplace and design v2 client

This commit is contained in:
2026-08-30 22:45:44 +08:00
43 changed files with 5662 additions and 14134 deletions

View File

@@ -3,9 +3,17 @@ export const IMAGE_WORKSPACE_UNAVAILABLE_MESSAGE = 'AI 设计暂不可用';
export const IMAGE_WORKSPACE_API_PATH = '/api/works/image-workspace';
export type DesignMedium = 'image' | 'video';
export type DesignWorkspacePhase = 'shaping' | 'awaiting_confirmation' | 'blocked';
export type DesignTaskStatus = 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled';
export type DesignQuoteStatus = 'active' | 'consumed' | 'expired' | 'superseded';
export type DesignResolution = 'open' | 'proposed' | 'resolved' | 'delegated' | 'not_applicable';
export type DesignProvenance =
| 'user'
| 'extracted_user'
| 'agent_proposal'
| 'delegated_agent'
| 'deterministic'
| 'inherited'
| 'reference_observation'
| 'restored'
| 'legacy_import';
export type DesignCapabilities = {
conversation: boolean;
@@ -14,181 +22,309 @@ export type DesignCapabilities = {
video: boolean;
};
export type DesignBrief = {
version: number;
status: 'draft' | 'ready' | 'confirmed';
medium: DesignMedium | null;
summary: string;
ready: boolean;
missingDecision: string | null;
export type DesignFieldDecision = {
resolution: DesignResolution;
provenance: DesignProvenance;
locked: boolean;
changed_by: string;
origin_turn_id: string | null;
};
export type DesignGenerationOption<TValue extends string | number> = {
value: TValue;
export type DesignSpecificationValues = {
intent: {
media: DesignMedium | null;
creation_intent: 'create' | 'edit' | 'vary' | 'composite' | 'animate' | 'extend' | null;
purpose: string | null;
channel: string | null;
audience: string | null;
intended_response: string | null;
communication_goal: string | null;
success_definition: string | null;
related_direction_id: string | null;
related_asset_id: string | null;
};
content: {
concept: string | null;
narrative: string | null;
subjects: Array<{
id: string;
description: string;
attributes: string[];
action: string | null;
spatial_relationship: string | null;
}>;
action: string | null;
environment: string | null;
emotional_tone: string | null;
exact_content: Array<{
id: string;
text: string;
role: string | null;
language: string | null;
hierarchy: string | null;
exact: boolean;
}>;
};
visual: {
art_direction: string | null;
cultural_cues: string | null;
composition: string | null;
viewpoint: string | null;
lighting: string | null;
palette: string | null;
materials: string | null;
finish: string | null;
};
layout: {
regions: Array<{ id: string; role: string; placement: string; priority: number }>;
subject_placement: string | null;
text_placement: string | null;
logo_placement: string | null;
safe_areas: string | null;
crop_behavior: string | null;
typography: string | null;
exact_rendering: string | null;
};
references: Array<{
id: string;
asset_id: string;
asset_revision: number | null;
role: string;
preserve: string[];
adapt: string[];
do_not_copy: string[];
reviewed_observations: string[];
}>;
image: {
operation_intent: string | null;
source_asset_id: string | null;
mask_asset_id: string | null;
preserve_instructions: string[];
change_instructions: string[];
background_behavior: string | null;
transparency_required: boolean | null;
edge_crop_behavior: string | null;
variant_relationship: string | null;
};
video: {
total_duration_seconds: number | null;
pacing: string | null;
first_frame_asset_id: string | null;
last_frame_asset_id: string | null;
shots: Array<{
id: string;
start_milliseconds: number | null;
end_milliseconds: number | null;
story_beat: string;
framing: string | null;
camera: string | null;
subject_motion: string | null;
environment_motion: string | null;
focus_behavior: string | null;
transition: string | null;
}>;
rhythm: string | null;
speed_behavior: string | null;
stillness: string | null;
loop_behavior: string | null;
end_state: string | null;
continuity: string | null;
audio_intent: string | null;
};
output: {
aspect_ratio: string | null;
orientation: string | null;
quality_intent: string | null;
delivery_format: string | null;
variant_count: number | null;
variant_purposes: string[];
crop_requirements: string | null;
export_requirements: string | null;
safe_areas: string | null;
};
constraints: {
must_include: string[];
must_preserve: string[];
must_avoid: string[];
brand_constraints: string[];
flexible_preferences: string[];
acceptance_criteria: Array<{ id: string; description: string; required: boolean }>;
};
};
export type DesignSpecification = {
schema_version: 1;
values: DesignSpecificationValues;
field_decisions: Record<string, DesignFieldDecision>;
};
export type DesignFieldChange = {
path: string;
kind: 'set' | 'update' | 'clear' | 'decision' | 'lock' | 'unlock';
before_value?: unknown;
after_value?: unknown;
before_decision?: DesignFieldDecision | null;
after_decision?: DesignFieldDecision | null;
};
export type DesignChangeSet = {
interaction_id: string;
changes: DesignFieldChange[];
};
export type DesignDecisionOption = {
id: string;
label: string;
default: boolean;
disabled: boolean;
multiplier: number;
description: string | null;
};
export type DesignGenerationParameters = {
model: string;
resolution: string;
export type DesignDecisionPrompt = {
id: string;
kind: 'suggestion' | 'question';
basedOnSpecificationRevisionId: string;
targetPaths: string[];
title: string;
body: string;
options: DesignDecisionOption[];
};
export type DesignQuoteOutputSummary = {
medium: DesignMedium;
strategy: 'direct' | 'multi_step';
aspectRatio: string;
outputCount: number;
deliveryFormat: string | null;
durationSeconds: number | null;
};
export type DesignGenerationOptions = {
models: DesignGenerationOption<string>[];
resolutions: DesignGenerationOption<string>[];
durations: DesignGenerationOption<number>[];
aspectRatios: DesignGenerationOption<string>[];
};
export type DesignGenerationPricing = {
schema: string;
amount: number;
rounding: string;
requiredOutputRoles: string[];
};
export type DesignGenerationQuote = {
quoteId: string;
status: DesignQuoteStatus;
status: 'offered' | 'consumed' | 'expired' | 'superseded';
specificationRevision: number;
specificationRevisionId: string;
specificationDigest: string;
medium: DesignMedium;
briefVersion: number;
briefSummary: string;
finalPrompt: string;
promptMode: string;
generationParameters: DesignGenerationParameters;
generationOptions: DesignGenerationOptions;
pricing: DesignGenerationPricing;
compilerVersion: string;
outputSummary: DesignQuoteOutputSummary;
warnings: DesignCompilationIssue[];
quotedDesignPoints: number;
maximumCustomerChargeAtoms: number;
expiresAt: string;
};
export type DesignMessage = {
id: string;
role: 'user' | 'assistant';
kind: 'user' | 'reply' | 'choice' | 'confirmation' | 'safety_redirect' | 'failure';
text: string;
quickReplies: string[];
generationQuote: DesignGenerationQuote | null;
turnRevision: number;
createdAt: string;
export type DesignForm = {
schemaVersion: 1;
workspaceId: string;
directionId: string;
directionRevision: number;
rawTurnSequence: number;
specificationRevision: number;
workspaceViewRevision: number;
specificationRevisionId: string;
specificationDigest: string;
specification: DesignSpecification;
decisionPrompts: DesignDecisionPrompt[];
activeQuotes: DesignGenerationQuote[];
recentChangeSet: DesignChangeSet;
};
export type DesignTurn = {
turnId: string;
rawTurnSequence: number;
userMessage: string;
assistantMessage: string;
};
export type DesignWorkspaceSummary = {
workspaceId: string;
clientWorkspaceId: string;
title: string;
viewRevision: number;
conversationCount: number;
phase: DesignWorkspacePhase;
directionId: string;
sessionId: string;
directionRevision: number;
specificationRevision: number;
workspaceViewRevision: number;
updatedAt: string;
};
export type DesignWorkspace = DesignWorkspaceSummary & {
conversations: DesignConversationSummary[];
};
export type DesignConversationSummary = {
conversationId: string;
workspaceId: string;
title: string;
/** A compact preview for history navigation; full messages are loaded on selection. */
latestMessagePreview?: string | null;
turnRevision: number;
phase: DesignWorkspacePhase;
brief: DesignBrief;
createdAt: string;
updatedAt: string;
};
export type DesignConversation = DesignConversationSummary & {
messages: DesignMessage[];
/** Cursor for the next older message page; absent only for legacy adapters. */
messagePage?: DesignConversationMessagePage;
};
export type DesignConversationMessagePage = {
hasOlder: boolean;
nextBefore: string | null;
};
export type DesignAsset = {
assetId: string;
workspaceId: string;
role: 'uploaded' | 'generated';
mediaType: DesignMedium;
mimeType: string;
width: number;
height: number;
durationMilliseconds: number | null;
generationTaskId: string | null;
createdAt: string;
contentPath: string;
};
export type DesignAssetSaveResult = {
status: 'saved' | 'cancelled';
};
export type DesignImageUploadMimeType = 'image/jpeg' | 'image/png' | 'image/webp';
export type DesignAssetUploadInput = {
workspaceId: string;
fileName: string;
mimeType: DesignImageUploadMimeType;
bytes: Uint8Array;
export type DesignTaskProgress = {
stage:
| 'queued'
| 'preparing'
| 'submitting'
| 'generating'
| 'composing'
| 'validating'
| 'finalizing'
| 'completed'
| 'failed'
| 'cancelled';
completedRequiredSteps: number;
totalRequiredSteps: number;
};
export type DesignGenerationTask = {
taskId: string;
workspaceId: string;
conversationId: string | null;
quoteId: string;
status: 'queued' | 'running' | 'succeeded' | 'failed' | 'cancelled';
taskRevision: number;
specificationRevision: number;
specificationRevisionId: string;
specificationDigest: string;
medium: DesignMedium;
status: DesignTaskStatus;
briefVersion: number;
briefSummary: string;
quoteId: string | null;
quotedDesignPoints: number | null;
failureCode: string | null;
resultAssets: DesignAsset[];
compilerVersion: string;
outputSummary: DesignQuoteOutputSummary;
maximumCustomerChargeAtoms: number;
customerBilling: {
quotedAtoms: number;
heldAtoms: number;
chargedAtoms: number;
refundedAtoms: number;
pendingResolutionAtoms: number;
};
customerHoldExpiresAt: string;
resolutionDeadlineAt: string | null;
progress: DesignTaskProgress;
executionHealth: 'normal' | 'recovering' | 'operator_required';
cancellation: {
state: 'available' | 'requested' | 'resolved' | 'unavailable';
outcome: 'stopped_before_submit' | null;
};
issues: Array<{
code: string;
message: string;
recoveryOwner: 'platform' | 'operator';
}>;
resultAssetIds: string[];
nextActions: Array<
'cancel' | 'view_result' | 'revise_design' | 'generate_again' | 'wait_for_resolution'
>;
createdAt: string;
updatedAt: string;
};
export type DesignGenerationTaskUpdatedEvent = {
id: string;
type: 'design.generation_task.updated';
workspaceId: string;
workspaceViewRevision: number;
generationTask: DesignGenerationTask;
export type DesignWorkspace = {
workspace: DesignWorkspaceSummary;
form: DesignForm;
turns: DesignTurn[];
tasks: DesignGenerationTask[];
assets: DesignAsset[];
};
export type DesignConversationSnapshotEvent = {
id: string;
type: 'design.conversation.snapshot';
workspaceId: string;
conversationId: string;
workspaceViewRevision: number;
conversation: DesignConversation;
generationTasks: DesignGenerationTask[];
};
export type DesignAssistantDeltaEvent = {
id: string;
type: 'design.assistant.delta';
workspaceId: string;
conversationId: string;
clientTurnId: string;
turnRevision: number;
chunkIndex: number;
delta: string;
};
export type DesignWorkspaceEvent =
| DesignGenerationTaskUpdatedEvent
| DesignConversationSnapshotEvent
| DesignAssistantDeltaEvent;
export type DesignWorkspaceBootstrap = {
capabilities: DesignCapabilities;
workspaces: DesignWorkspaceSummary[];
@@ -199,46 +335,173 @@ export type DesignCreateWorkspaceInput = {
title: string;
};
export type DesignDeleteWorkspaceResult = {
workspaceId: string;
deleted: true;
};
export type DesignCreateConversationInput = {
workspaceId: string;
clientConversationId: string;
title: string;
};
export type DesignRenameWorkspaceInput = {
workspaceId: string;
title: string;
};
export type DesignSubmitMessageInput = {
export type DesignDeleteWorkspaceResult = {
workspaceId: string;
conversationId: string;
clientTurnId: string;
expectedTurnRevision: number;
deleted: true;
};
export type DesignUserFieldOperation =
| { kind: 'set'; path: string; value: unknown }
| { kind: 'clear'; path: string; resolution?: 'open' | 'not_applicable' }
| { kind: 'set_lock'; path: string; locked: boolean };
export type DesignUserInput =
| { kind: 'direct_edit'; operations: DesignUserFieldOperation[] }
| { kind: 'chat'; message: string }
| {
kind: 'prompt_resolution';
promptId: string;
action: 'select' | 'reject';
optionId?: string;
}
| { kind: 'restore'; specificationRevisionId: string };
export type DesignCommandInput =
| {
kind: 'apply_input';
workspaceId: string;
sessionId: string;
expectedDirectionRevision: number;
clientOperationId: string;
input: DesignUserInput;
}
| {
kind: 'request_quote';
workspaceId: string;
sessionId: string;
expectedDirectionRevision: number;
specificationRevision: number;
clientOperationId: string;
}
| {
kind: 'confirm_generation';
workspaceId: string;
sessionId: string;
quoteId: string;
expectedDirectionRevision: number;
clientOperationId: string;
};
export type DesignCommandResult = {
clientOperationId: string;
runId: string;
workspace: DesignWorkspace;
};
export type DesignCompilationIssue = {
code: string;
message: string;
attachmentAssetIds?: string[];
severity: 'blocker' | 'warning';
path?: string | null;
};
export type DesignGenerationQuoteUpdateInput = {
workspaceId: string;
quoteId: string;
finalPrompt: string;
generationParameters: DesignGenerationParameters;
export type DesignInteractionOutcome = {
operationKind:
| 'bootstrap'
| 'legacy_import'
| 'direct_edit'
| 'chat'
| 'prompt_resolution'
| 'restore'
| 'quote_request'
| 'generation_confirmation';
interactionId: string;
baseDirectionRevision: number;
newDirectionRevision: number;
rawTurnSequence: number;
specificationRevision: number;
specificationRevisionId: string;
workspaceViewRevision: number;
specificationRevisionCreated: boolean;
meaningChanged: boolean;
changeSet: DesignChangeSet;
turnId: string | null;
assistantMessage: string | null;
createdDecisionPromptIds: string[];
resolvedDecisionPromptId: string | null;
supersededDecisionPromptIds: string[];
supersededQuoteCount: number;
quoteId: string | null;
generationTaskId: string | null;
};
export type DesignConfirmGenerationInput = {
export type DesignSessionSnapshotEvent = {
id: string;
type: 'design.session.snapshot';
form: DesignForm;
};
export type DesignAssistantDeltaEvent = {
id: string;
type: 'design.assistant.delta';
workspaceId: string;
conversationId: string;
clientTurnId: string;
expectedTurnRevision: number;
quoteId: string;
finalPrompt: string;
generationParameters: DesignGenerationParameters;
directionId: string;
clientOperationId: string;
directionRevision: number;
chunkIndex: number;
delta: string;
};
export type DesignDirectionUpdatedEvent = {
id: string;
type: 'design.direction.updated';
replayed: boolean;
operation: DesignInteractionOutcome;
form: DesignForm;
};
export type DesignQuoteBlockedEvent = {
id: string;
type: 'design.quote.blocked';
clientOperationId: string;
blockers: DesignCompilationIssue[];
warnings: DesignCompilationIssue[];
form: DesignForm;
};
export type DesignWorkspaceUpdatedEvent = {
id: string;
type: 'design.workspace.updated';
workspaceId: string;
workspaceViewRevision: number;
changedDirection: {
directionId: string;
directionRevision: number;
specificationRevision: number;
} | null;
changedTask: DesignGenerationTask | null;
};
export type DesignWorkspaceEvent =
| DesignSessionSnapshotEvent
| DesignAssistantDeltaEvent
| DesignDirectionUpdatedEvent
| DesignQuoteBlockedEvent
| DesignWorkspaceUpdatedEvent;
export type DesignWorkspaceEventSubscription = {
events: AsyncIterable<DesignWorkspaceEvent>;
close(): void;
};
export type DesignWorkspaceEventSubscriptionInput = {
workspaceId: string;
sessionId: string;
afterEventId?: string;
};
export type DesignAssetSaveResult = { status: 'saved' | 'cancelled' };
export type DesignImageUploadMimeType = 'image/jpeg' | 'image/png' | 'image/webp';
export type DesignAssetUploadInput = {
workspaceId: string;
fileName: string;
mimeType: DesignImageUploadMimeType;
bytes: Uint8Array;
};
export function designAssetContentPath(workspaceId: string, assetId: string): string {