feat: 完善图像工作区与创作工具体验
This commit is contained in:
@@ -8,7 +8,12 @@ import type {
|
||||
DesignConfirmGenerationInput,
|
||||
DesignCreateConversationInput,
|
||||
DesignCreateWorkspaceInput,
|
||||
DesignDeleteWorkspaceResult,
|
||||
DesignGenerationOption,
|
||||
DesignGenerationOptions,
|
||||
DesignGenerationParameters,
|
||||
DesignGenerationQuote,
|
||||
DesignGenerationQuoteUpdateInput,
|
||||
DesignGenerationTask,
|
||||
DesignMessage,
|
||||
DesignMedium,
|
||||
@@ -28,6 +33,224 @@ const LEGACY_LOCAL_WORKSPACE_FILE = 'design-workspace-v2.json';
|
||||
const MAX_PROJECT_NAME_LENGTH = 80;
|
||||
const MAX_MESSAGE_LENGTH = 4_000;
|
||||
|
||||
const LOCAL_GENERATION_OPTIONS: Record<DesignMedium, DesignGenerationOptions> = {
|
||||
image: {
|
||||
models: [{
|
||||
value: 'local-image-preview',
|
||||
label: '本地图片预览',
|
||||
default: true,
|
||||
disabled: false,
|
||||
multiplier: 1,
|
||||
}],
|
||||
resolutions: [{
|
||||
value: '1024x1024',
|
||||
label: '1024 × 1024',
|
||||
default: true,
|
||||
disabled: false,
|
||||
multiplier: 1,
|
||||
}],
|
||||
durations: [],
|
||||
aspectRatios: [
|
||||
{
|
||||
value: '1:1',
|
||||
label: '1:1',
|
||||
default: true,
|
||||
disabled: false,
|
||||
multiplier: 1,
|
||||
},
|
||||
{
|
||||
value: '16:9',
|
||||
label: '16:9',
|
||||
default: false,
|
||||
disabled: false,
|
||||
multiplier: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
video: {
|
||||
models: [{
|
||||
value: 'local-video-preview',
|
||||
label: '本地视频预览',
|
||||
default: true,
|
||||
disabled: false,
|
||||
multiplier: 1,
|
||||
}],
|
||||
resolutions: [
|
||||
{
|
||||
value: '720P',
|
||||
label: '720P',
|
||||
default: false,
|
||||
disabled: false,
|
||||
multiplier: 0.8,
|
||||
},
|
||||
{
|
||||
value: '1080P',
|
||||
label: '1080P',
|
||||
default: true,
|
||||
disabled: false,
|
||||
multiplier: 1,
|
||||
},
|
||||
],
|
||||
durations: [
|
||||
{
|
||||
value: 4,
|
||||
label: '4 秒',
|
||||
default: false,
|
||||
disabled: false,
|
||||
multiplier: 0.8,
|
||||
},
|
||||
{
|
||||
value: 6,
|
||||
label: '6 秒',
|
||||
default: true,
|
||||
disabled: false,
|
||||
multiplier: 1,
|
||||
},
|
||||
{
|
||||
value: 8,
|
||||
label: '8 秒',
|
||||
default: false,
|
||||
disabled: true,
|
||||
multiplier: 1.3,
|
||||
},
|
||||
],
|
||||
aspectRatios: [
|
||||
{
|
||||
value: '16:9',
|
||||
label: '16:9',
|
||||
default: true,
|
||||
disabled: false,
|
||||
multiplier: 1,
|
||||
},
|
||||
{
|
||||
value: '9:16',
|
||||
label: '9:16',
|
||||
default: false,
|
||||
disabled: false,
|
||||
multiplier: 1,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
const LOCAL_PRICING_SCHEMA = 'local-design-pricing-v1';
|
||||
|
||||
function defaultOption<TValue extends string | number>(
|
||||
options: DesignGenerationOption<TValue>[],
|
||||
): DesignGenerationOption<TValue> {
|
||||
return options.find((option) => option.default && !option.disabled)
|
||||
?? options.find((option) => !option.disabled)
|
||||
?? options[0]!;
|
||||
}
|
||||
|
||||
function localGenerationParameters(medium: DesignMedium): DesignGenerationParameters {
|
||||
const options = LOCAL_GENERATION_OPTIONS[medium];
|
||||
return {
|
||||
model: defaultOption(options.models).value,
|
||||
resolution: defaultOption(options.resolutions).value,
|
||||
aspectRatio: defaultOption(options.aspectRatios).value,
|
||||
durationSeconds: options.durations.length > 0
|
||||
? defaultOption(options.durations).value
|
||||
: null,
|
||||
};
|
||||
}
|
||||
|
||||
function localGenerationPricing(
|
||||
medium: DesignMedium,
|
||||
parameters: DesignGenerationParameters,
|
||||
): number {
|
||||
const options = LOCAL_GENERATION_OPTIONS[medium];
|
||||
const model = options.models.find((option) => option.value === parameters.model);
|
||||
const resolution = options.resolutions.find((option) => option.value === parameters.resolution);
|
||||
const aspectRatio = options.aspectRatios.find((option) => option.value === parameters.aspectRatio);
|
||||
const duration = parameters.durationSeconds === null
|
||||
? null
|
||||
: options.durations.find((option) => option.value === parameters.durationSeconds);
|
||||
if (!model || model.disabled || !resolution || resolution.disabled
|
||||
|| !aspectRatio || aspectRatio.disabled
|
||||
|| (options.durations.length > 0 && (!duration || duration.disabled))
|
||||
|| (options.durations.length === 0 && parameters.durationSeconds !== null)) {
|
||||
throw new LocalImageWorkspaceError(
|
||||
422,
|
||||
'generation_option_invalid',
|
||||
'当前生成参数不可用,请重新选择',
|
||||
);
|
||||
}
|
||||
const baseAmount = medium === 'video' ? 2 : 1;
|
||||
return Math.ceil(baseAmount * model.multiplier * resolution.multiplier * (duration?.multiplier ?? 1));
|
||||
}
|
||||
|
||||
function createLocalQuote(
|
||||
medium: DesignMedium,
|
||||
briefVersion: number,
|
||||
briefSummary: string,
|
||||
finalPrompt: string,
|
||||
quoteId: string,
|
||||
expiresAt: string,
|
||||
): DesignGenerationQuote {
|
||||
const generationParameters = localGenerationParameters(medium);
|
||||
const amount = localGenerationPricing(medium, generationParameters);
|
||||
return {
|
||||
quoteId,
|
||||
status: 'active',
|
||||
medium,
|
||||
briefVersion,
|
||||
briefSummary,
|
||||
finalPrompt,
|
||||
promptMode: 'guided',
|
||||
generationParameters,
|
||||
generationOptions: clone(LOCAL_GENERATION_OPTIONS[medium]),
|
||||
pricing: {
|
||||
schema: LOCAL_PRICING_SCHEMA,
|
||||
amount,
|
||||
rounding: 'ceil',
|
||||
},
|
||||
quotedDesignPoints: amount,
|
||||
expiresAt,
|
||||
};
|
||||
}
|
||||
|
||||
function sameGenerationParameters(
|
||||
left: DesignGenerationParameters,
|
||||
right: DesignGenerationParameters,
|
||||
): boolean {
|
||||
return left.model === right.model
|
||||
&& left.resolution === right.resolution
|
||||
&& left.aspectRatio === right.aspectRatio
|
||||
&& left.durationSeconds === right.durationSeconds;
|
||||
}
|
||||
|
||||
function normalizePersistedGenerationQuotes(
|
||||
state: PersistedImageWorkspace,
|
||||
): PersistedImageWorkspace {
|
||||
for (const conversations of Object.values(state.conversationsByWorkspaceId)) {
|
||||
for (const conversation of conversations) {
|
||||
for (const message of conversation.messages) {
|
||||
const quote = message.generationQuote;
|
||||
if (!quote || (quote.finalPrompt !== undefined
|
||||
&& quote.generationParameters?.aspectRatio !== undefined
|
||||
&& quote.generationOptions?.aspectRatios !== undefined
|
||||
&& quote.pricing)) continue;
|
||||
const finalPrompt = quote.finalPrompt ?? quote.briefSummary;
|
||||
const normalized = createLocalQuote(
|
||||
quote.medium,
|
||||
quote.briefVersion,
|
||||
quote.briefSummary,
|
||||
finalPrompt,
|
||||
quote.quoteId,
|
||||
quote.expiresAt,
|
||||
);
|
||||
Object.assign(quote, {
|
||||
...normalized,
|
||||
status: quote.status,
|
||||
quotedDesignPoints: quote.quotedDesignPoints,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
const LOCAL_CAPABILITIES: DesignCapabilities = {
|
||||
conversation: true,
|
||||
generation: true,
|
||||
@@ -272,6 +495,25 @@ export class LocalImageWorkspace implements DesignWorkspaceModule {
|
||||
});
|
||||
}
|
||||
|
||||
deleteWorkspace(workspaceId: string): Promise<DesignDeleteWorkspaceResult> {
|
||||
return this.enqueue(async () => {
|
||||
const state = await this.load();
|
||||
this.requireWorkspace(state, workspaceId);
|
||||
|
||||
const nextState = clone(state);
|
||||
nextState.workspaces = nextState.workspaces.filter(
|
||||
(workspace) => workspace.workspaceId !== workspaceId,
|
||||
);
|
||||
delete nextState.conversationsByWorkspaceId[workspaceId];
|
||||
delete nextState.tasksByWorkspaceId[workspaceId];
|
||||
for (const [assetId, asset] of Object.entries(nextState.assetsById)) {
|
||||
if (asset.workspaceId === workspaceId) delete nextState.assetsById[assetId];
|
||||
}
|
||||
await this.persist(nextState);
|
||||
return { workspaceId, deleted: true };
|
||||
});
|
||||
}
|
||||
|
||||
createConversation(input: DesignCreateConversationInput): Promise<DesignConversation> {
|
||||
return this.mutateConversation((state) => {
|
||||
const workspace = this.requireWorkspace(state, input.workspaceId);
|
||||
@@ -337,15 +579,14 @@ export class LocalImageWorkspace implements DesignWorkspaceModule {
|
||||
const timestamp = this.now().toISOString();
|
||||
const nextRevision = conversation.turnRevision + 1;
|
||||
const medium = detectMedium(message);
|
||||
const quote: DesignGenerationQuote = {
|
||||
quoteId: `local-quote-${this.createId()}`,
|
||||
status: 'active',
|
||||
const quote = createLocalQuote(
|
||||
medium,
|
||||
briefVersion: nextRevision,
|
||||
briefSummary: message,
|
||||
quotedDesignPoints: medium === 'video' ? 2 : 1,
|
||||
expiresAt: new Date(this.now().getTime() + 15 * 60 * 1000).toISOString(),
|
||||
};
|
||||
nextRevision,
|
||||
message,
|
||||
message,
|
||||
`local-quote-${this.createId()}`,
|
||||
new Date(this.now().getTime() + 15 * 60 * 1000).toISOString(),
|
||||
);
|
||||
conversation.messages.push(
|
||||
{
|
||||
id: `local-message-${this.createId()}`,
|
||||
@@ -389,6 +630,46 @@ export class LocalImageWorkspace implements DesignWorkspaceModule {
|
||||
});
|
||||
}
|
||||
|
||||
updateGenerationQuote(
|
||||
input: DesignGenerationQuoteUpdateInput,
|
||||
): Promise<DesignGenerationQuote> {
|
||||
return this.enqueue(async () => {
|
||||
const state = await this.load();
|
||||
const workspace = this.requireWorkspace(state, input.workspaceId);
|
||||
const conversation = (state.conversationsByWorkspaceId[input.workspaceId] ?? [])
|
||||
.find((candidate) => candidate.messages.some((message) => (
|
||||
message.generationQuote?.quoteId === input.quoteId
|
||||
)));
|
||||
const quote = conversation?.messages
|
||||
.map((message) => message.generationQuote)
|
||||
.find((candidate) => candidate?.quoteId === input.quoteId);
|
||||
if (!conversation || !quote || quote.status !== 'active') {
|
||||
throw new LocalImageWorkspaceError(409, 'generation_quote_invalid', '当前生成报价已失效');
|
||||
}
|
||||
if (new Date(quote.expiresAt).getTime() <= this.now().getTime()) {
|
||||
quote.status = 'expired';
|
||||
throw new LocalImageWorkspaceError(409, 'generation_quote_expired', '当前生成报价已过期');
|
||||
}
|
||||
if (!input.finalPrompt.trim()) {
|
||||
throw new LocalImageWorkspaceError(422, 'generation_prompt_invalid', '提示词不能为空');
|
||||
}
|
||||
const amount = localGenerationPricing(quote.medium, input.generationParameters);
|
||||
quote.finalPrompt = input.finalPrompt;
|
||||
quote.generationParameters = clone(input.generationParameters);
|
||||
quote.pricing = {
|
||||
...quote.pricing,
|
||||
amount,
|
||||
};
|
||||
quote.quotedDesignPoints = amount;
|
||||
const timestamp = this.now().toISOString();
|
||||
conversation.updatedAt = timestamp;
|
||||
workspace.viewRevision += 1;
|
||||
workspace.updatedAt = timestamp;
|
||||
await this.persist(state);
|
||||
return clone(quote);
|
||||
});
|
||||
}
|
||||
|
||||
confirmGeneration(input: DesignConfirmGenerationInput): Promise<DesignConversation> {
|
||||
return this.mutateConversation((state) => {
|
||||
const workspace = this.requireWorkspace(state, input.workspaceId);
|
||||
@@ -410,6 +691,14 @@ export class LocalImageWorkspace implements DesignWorkspaceModule {
|
||||
quote.status = 'expired';
|
||||
throw new LocalImageWorkspaceError(409, 'generation_quote_expired', '当前生成报价已过期');
|
||||
}
|
||||
if (quote.finalPrompt !== input.finalPrompt
|
||||
|| !sameGenerationParameters(quote.generationParameters, input.generationParameters)) {
|
||||
throw new LocalImageWorkspaceError(
|
||||
409,
|
||||
'generation_quote_changed',
|
||||
'生成参数已变更,请等待重新报价完成',
|
||||
);
|
||||
}
|
||||
|
||||
quote.status = 'consumed';
|
||||
const timestamp = this.now().toISOString();
|
||||
@@ -609,7 +898,7 @@ export class LocalImageWorkspace implements DesignWorkspaceModule {
|
||||
'AI 设计本地数据暂时无法读取',
|
||||
);
|
||||
}
|
||||
this.state = parsed;
|
||||
this.state = normalizePersistedGenerationQuotes(parsed);
|
||||
} catch (error) {
|
||||
if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
|
||||
this.state = await this.loadLegacyState();
|
||||
@@ -631,6 +920,7 @@ export class LocalImageWorkspace implements DesignWorkspaceModule {
|
||||
);
|
||||
}
|
||||
const migrated = this.migrateLegacyState(parsed);
|
||||
normalizePersistedGenerationQuotes(migrated);
|
||||
await this.persist(migrated);
|
||||
return migrated;
|
||||
} catch (error) {
|
||||
|
||||
@@ -6,6 +6,9 @@ import type {
|
||||
DesignCreateConversationInput,
|
||||
DesignConfirmGenerationInput,
|
||||
DesignCreateWorkspaceInput,
|
||||
DesignDeleteWorkspaceResult,
|
||||
DesignGenerationQuote,
|
||||
DesignGenerationQuoteUpdateInput,
|
||||
DesignGenerationTask,
|
||||
DesignRenameWorkspaceInput,
|
||||
DesignSubmitMessageInput,
|
||||
@@ -46,11 +49,13 @@ export interface DesignWorkspaceModule {
|
||||
bootstrap(): Promise<DesignWorkspaceBootstrap>;
|
||||
getCapabilities(): Promise<DesignCapabilities>;
|
||||
createWorkspace(input: DesignCreateWorkspaceInput): Promise<DesignWorkspace>;
|
||||
deleteWorkspace(workspaceId: string): Promise<DesignDeleteWorkspaceResult>;
|
||||
renameWorkspace(input: DesignRenameWorkspaceInput): Promise<DesignWorkspace>;
|
||||
getWorkspace(workspaceId: string): Promise<DesignWorkspace>;
|
||||
createConversation(input: DesignCreateConversationInput): Promise<DesignConversation>;
|
||||
getConversation(workspaceId: string, conversationId: string): Promise<DesignConversation>;
|
||||
submitMessage(input: DesignSubmitMessageInput): Promise<DesignConversation>;
|
||||
updateGenerationQuote(input: DesignGenerationQuoteUpdateInput): Promise<DesignGenerationQuote>;
|
||||
confirmGeneration(input: DesignConfirmGenerationInput): Promise<DesignConversation>;
|
||||
listTasks(workspaceId: string): Promise<DesignGenerationTask[]>;
|
||||
uploadAsset?(input: DesignAssetUploadInput): Promise<DesignAsset>;
|
||||
|
||||
@@ -10,6 +10,9 @@ import type {
|
||||
DesignConfirmGenerationInput,
|
||||
DesignCreateConversationInput,
|
||||
DesignCreateWorkspaceInput,
|
||||
DesignDeleteWorkspaceResult,
|
||||
DesignGenerationOption,
|
||||
DesignGenerationQuoteUpdateInput,
|
||||
DesignGenerationQuote,
|
||||
DesignGenerationTask,
|
||||
DesignGenerationTaskUpdatedEvent,
|
||||
@@ -48,6 +51,25 @@ type ServerQuote = {
|
||||
medium: DesignGenerationQuote['medium'];
|
||||
brief_version: number;
|
||||
brief_summary: string;
|
||||
final_prompt: string;
|
||||
prompt_mode: string;
|
||||
generation_parameters: {
|
||||
model: string;
|
||||
resolution: string;
|
||||
aspect_ratio: string;
|
||||
duration_seconds: number | null;
|
||||
};
|
||||
generation_options: {
|
||||
models: Array<DesignGenerationOption<string>>;
|
||||
resolutions: Array<DesignGenerationOption<string>>;
|
||||
durations: Array<DesignGenerationOption<number>>;
|
||||
aspect_ratios: Array<DesignGenerationOption<string>>;
|
||||
};
|
||||
pricing: {
|
||||
schema: string;
|
||||
amount: number;
|
||||
rounding: string;
|
||||
};
|
||||
quoted_design_points: number;
|
||||
expires_at: string;
|
||||
};
|
||||
@@ -176,6 +198,13 @@ type AgentDesignTurnSubmission = {
|
||||
action: null | {
|
||||
type: 'confirm_generation';
|
||||
quote_id: string;
|
||||
final_prompt?: string;
|
||||
aspect_ratio?: string;
|
||||
generation_parameters?: {
|
||||
model: string;
|
||||
resolution: string;
|
||||
duration_seconds: number | null;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
@@ -260,6 +289,25 @@ function mapQuote(quote: ServerQuote | null): DesignGenerationQuote | null {
|
||||
medium: quote.medium,
|
||||
briefVersion: quote.brief_version,
|
||||
briefSummary: quote.brief_summary,
|
||||
finalPrompt: quote.final_prompt,
|
||||
promptMode: quote.prompt_mode,
|
||||
generationParameters: {
|
||||
model: quote.generation_parameters.model,
|
||||
resolution: quote.generation_parameters.resolution,
|
||||
aspectRatio: quote.generation_parameters.aspect_ratio,
|
||||
durationSeconds: quote.generation_parameters.duration_seconds,
|
||||
},
|
||||
generationOptions: {
|
||||
models: quote.generation_options.models,
|
||||
resolutions: quote.generation_options.resolutions,
|
||||
durations: quote.generation_options.durations,
|
||||
aspectRatios: quote.generation_options.aspect_ratios,
|
||||
},
|
||||
pricing: {
|
||||
schema: quote.pricing.schema,
|
||||
amount: quote.pricing.amount,
|
||||
rounding: quote.pricing.rounding,
|
||||
},
|
||||
quotedDesignPoints: quote.quoted_design_points,
|
||||
expiresAt: quote.expires_at,
|
||||
};
|
||||
@@ -810,6 +858,15 @@ export class WorksSquareDesignWorkspace implements DesignWorkspaceModule {
|
||||
return this.getWorkspace(workspace.workspace_id);
|
||||
}
|
||||
|
||||
async deleteWorkspace(workspaceId: string): Promise<DesignDeleteWorkspaceResult> {
|
||||
await this.requestJson<unknown>(
|
||||
`/api/design/workspaces/${encodeURIComponent(workspaceId)}`,
|
||||
{ method: 'DELETE' },
|
||||
);
|
||||
this.forgetWorkspace(workspaceId);
|
||||
return { workspaceId, deleted: true };
|
||||
}
|
||||
|
||||
async renameWorkspace(input: DesignRenameWorkspaceInput): Promise<DesignWorkspace> {
|
||||
const workspace = await this.requestJson<ServerWorkspace>(
|
||||
`/api/design/workspaces/${encodeURIComponent(input.workspaceId)}`,
|
||||
@@ -873,8 +930,43 @@ export class WorksSquareDesignWorkspace implements DesignWorkspaceModule {
|
||||
});
|
||||
}
|
||||
|
||||
async updateGenerationQuote(
|
||||
input: DesignGenerationQuoteUpdateInput,
|
||||
): Promise<DesignGenerationQuote> {
|
||||
const quote = await this.requestJson<ServerQuote>(
|
||||
`/api/design/workspaces/${encodeURIComponent(input.workspaceId)}/generation-quotes/${encodeURIComponent(input.quoteId)}`,
|
||||
{
|
||||
method: 'PATCH',
|
||||
body: JSON.stringify({
|
||||
final_prompt: input.finalPrompt,
|
||||
model: input.generationParameters.model,
|
||||
resolution: input.generationParameters.resolution,
|
||||
aspect_ratio: input.generationParameters.aspectRatio,
|
||||
duration_seconds: input.generationParameters.durationSeconds,
|
||||
}),
|
||||
},
|
||||
);
|
||||
return mapQuote(quote)!;
|
||||
}
|
||||
|
||||
async confirmGeneration(input: DesignConfirmGenerationInput): Promise<DesignConversation> {
|
||||
const agentSessionId = await this.getAgentSessionId(input.workspaceId, input.conversationId);
|
||||
const action = input.finalPrompt === undefined || input.generationParameters === undefined
|
||||
? {
|
||||
type: 'confirm_generation' as const,
|
||||
quote_id: input.quoteId,
|
||||
}
|
||||
: {
|
||||
type: 'confirm_generation' as const,
|
||||
quote_id: input.quoteId,
|
||||
final_prompt: input.finalPrompt,
|
||||
aspect_ratio: input.generationParameters.aspectRatio,
|
||||
generation_parameters: {
|
||||
model: input.generationParameters.model,
|
||||
resolution: input.generationParameters.resolution,
|
||||
duration_seconds: input.generationParameters.durationSeconds,
|
||||
},
|
||||
};
|
||||
return this.executeAgentTurn({
|
||||
workspaceId: input.workspaceId,
|
||||
conversationId: input.conversationId,
|
||||
@@ -883,10 +975,7 @@ export class WorksSquareDesignWorkspace implements DesignWorkspaceModule {
|
||||
expectedTurnRevision: input.expectedTurnRevision,
|
||||
message: '确认生成',
|
||||
attachmentAssetIds: [],
|
||||
action: {
|
||||
type: 'confirm_generation',
|
||||
quote_id: input.quoteId,
|
||||
},
|
||||
action,
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1438,6 +1527,30 @@ export class WorksSquareDesignWorkspace implements DesignWorkspaceModule {
|
||||
this.conversationSessionIds.delete(this.conversationKey(workspaceId, conversationId));
|
||||
}
|
||||
|
||||
private forgetWorkspace(workspaceId: string): void {
|
||||
const keyPrefix = `${workspaceId}:`;
|
||||
const sessionIds = new Set<string>();
|
||||
for (const [key, sessionId] of this.conversationSessionIds) {
|
||||
if (!key.startsWith(keyPrefix)) continue;
|
||||
sessionIds.add(sessionId);
|
||||
this.conversationSessionIds.delete(key);
|
||||
}
|
||||
|
||||
const activeSubscriptions: Array<() => void> = [];
|
||||
for (const [key, closers] of this.eventSubscriptionClosers) {
|
||||
if (!key.startsWith(keyPrefix)) continue;
|
||||
activeSubscriptions.push(...closers);
|
||||
this.eventSubscriptionClosers.delete(key);
|
||||
}
|
||||
for (const close of activeSubscriptions) close();
|
||||
|
||||
for (const key of this.terminalAgentRuns.keys()) {
|
||||
if ([...sessionIds].some((sessionId) => key.startsWith(`${sessionId}:`))) {
|
||||
this.terminalAgentRuns.delete(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async getAgentSessionId(
|
||||
workspaceId: string,
|
||||
conversationId: string,
|
||||
|
||||
Reference in New Issue
Block a user