149 lines
4.8 KiB
TypeScript
149 lines
4.8 KiB
TypeScript
import type {
|
|
CanvasOperationPlan,
|
|
CanvasViewOperationPlan,
|
|
} from "@/types/canvas/agent-protocol";
|
|
|
|
export type PendingCanvasOperation = {
|
|
version: 1;
|
|
pendingId: string;
|
|
projectId: string;
|
|
operationId: string;
|
|
operation: string;
|
|
riskOperation: string;
|
|
targetNodeIds: string[];
|
|
operationPlan?: CanvasOperationPlan;
|
|
viewPlan?: CanvasViewOperationPlan;
|
|
prompt: string;
|
|
reply: string;
|
|
requestedAt: string;
|
|
requestedByMessageId: string;
|
|
};
|
|
|
|
function trimString(value: unknown, maxLength: number) {
|
|
return typeof value === "string" ? value.trim().slice(0, maxLength) : "";
|
|
}
|
|
|
|
function normalizeStringArray(value: unknown, maxItems: number) {
|
|
if (!Array.isArray(value)) return [];
|
|
return value
|
|
.map((item) => trimString(item, 160))
|
|
.filter(Boolean)
|
|
.slice(0, maxItems);
|
|
}
|
|
|
|
function normalizeOperationPlan(value: unknown): CanvasOperationPlan | undefined {
|
|
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
const raw = value as CanvasOperationPlan;
|
|
if (raw.version !== 1 || !raw.operationId || !raw.operation || !raw.projectId) {
|
|
return undefined;
|
|
}
|
|
return raw;
|
|
}
|
|
|
|
function normalizeViewPlan(value: unknown): CanvasViewOperationPlan | undefined {
|
|
if (!value || typeof value !== "object" || Array.isArray(value)) return undefined;
|
|
const raw = value as CanvasViewOperationPlan;
|
|
if (raw.version !== 1 || !raw.viewOperationId || !raw.operation) {
|
|
return undefined;
|
|
}
|
|
return raw;
|
|
}
|
|
|
|
export function extractPendingCanvasOperationFromWorkingState(
|
|
workingState?: { lastTurnSummary?: unknown } | null
|
|
): PendingCanvasOperation | null {
|
|
const summary = workingState?.lastTurnSummary;
|
|
if (!summary || typeof summary !== "object" || Array.isArray(summary)) return null;
|
|
const pending = (summary as Record<string, unknown>).pendingCanvasOperation;
|
|
if (!pending || typeof pending !== "object" || Array.isArray(pending)) return null;
|
|
|
|
const record = pending as Record<string, unknown>;
|
|
const projectId = trimString(record.projectId, 160);
|
|
const operationId = trimString(record.operationId, 180);
|
|
const operation = trimString(record.operation, 80);
|
|
const riskOperation = trimString(record.riskOperation, 80);
|
|
const requestedByMessageId = trimString(record.requestedByMessageId, 160);
|
|
const prompt = trimString(record.prompt, 1000);
|
|
const reply = trimString(record.reply, 500);
|
|
if (!projectId || !operationId || !operation || !riskOperation || !requestedByMessageId) {
|
|
return null;
|
|
}
|
|
|
|
return {
|
|
version: 1,
|
|
pendingId: trimString(record.pendingId, 180) || operationId,
|
|
projectId,
|
|
operationId,
|
|
operation,
|
|
riskOperation,
|
|
targetNodeIds: normalizeStringArray(record.targetNodeIds, 20),
|
|
operationPlan: normalizeOperationPlan(record.operationPlan),
|
|
viewPlan: normalizeViewPlan(record.viewPlan),
|
|
prompt,
|
|
reply,
|
|
requestedAt: trimString(record.requestedAt, 80) || new Date(0).toISOString(),
|
|
requestedByMessageId,
|
|
};
|
|
}
|
|
|
|
export function isCanvasOperationConfirmText(text: string) {
|
|
const normalized = String(text || "").toLowerCase().replace(/\s+/g, "");
|
|
if (!normalized) return false;
|
|
return /确认|同意|可以执行|执行吧|就这样|没问题|按这个|开始执行|确认执行/.test(
|
|
normalized
|
|
);
|
|
}
|
|
|
|
export function isCanvasOperationCancelText(text: string) {
|
|
const normalized = String(text || "").toLowerCase().replace(/\s+/g, "");
|
|
if (!normalized) return false;
|
|
return /取消|先不|不要|别执行|不执行|算了|停止/.test(normalized);
|
|
}
|
|
|
|
export function buildPendingCanvasOperation(params: {
|
|
projectId: string;
|
|
messageId: string;
|
|
userText: string;
|
|
riskOperation: string;
|
|
targetNodeIds: string[];
|
|
reply: string;
|
|
operationPlan?: CanvasOperationPlan;
|
|
viewPlan?: CanvasViewOperationPlan;
|
|
}): PendingCanvasOperation {
|
|
const operationId =
|
|
params.operationPlan?.operationId || `${params.messageId}:pending-canvas-operation`;
|
|
return {
|
|
version: 1,
|
|
pendingId: operationId,
|
|
projectId: params.projectId,
|
|
operationId,
|
|
operation: params.operationPlan?.operation || params.riskOperation,
|
|
riskOperation: params.riskOperation,
|
|
targetNodeIds: params.targetNodeIds.slice(0, 20),
|
|
operationPlan: params.operationPlan,
|
|
viewPlan: params.viewPlan,
|
|
prompt: params.userText.slice(0, 1000),
|
|
reply: params.reply.slice(0, 500),
|
|
requestedAt: new Date().toISOString(),
|
|
requestedByMessageId: params.messageId,
|
|
};
|
|
}
|
|
|
|
export function withConfirmedCanvasOperationPlan(
|
|
pending: PendingCanvasOperation,
|
|
confirmingMessageId: string
|
|
): CanvasOperationPlan | null {
|
|
if (!pending.operationPlan) return null;
|
|
return {
|
|
...pending.operationPlan,
|
|
confirmed: true,
|
|
metadata: {
|
|
...(pending.operationPlan.metadata || {}),
|
|
confirmedByUser: true,
|
|
confirmedByMessageId: confirmingMessageId,
|
|
confirmedAt: new Date().toISOString(),
|
|
pendingOperationId: pending.pendingId,
|
|
},
|
|
};
|
|
}
|