@xiaohhhh1/canvas-agent 0.4.56 → 0.4.58
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
|
@@ -37,7 +37,7 @@ export class WorkerPool {
|
|
|
37
37
|
this.available.push(workerIndex);
|
|
38
38
|
}
|
|
39
39
|
}
|
|
40
|
-
export function boundedWorkerConcurrency(value, fallback =
|
|
40
|
+
export function boundedWorkerConcurrency(value, fallback = 4, maximum = 4) {
|
|
41
41
|
const parsed = Number(value);
|
|
42
42
|
if (!Number.isInteger(parsed) || parsed < 1)
|
|
43
43
|
return fallback;
|
|
@@ -5,6 +5,6 @@ export declare const FLOW_C_SCRIPT_CHUNK_SIZES: readonly [10, 5, 1];
|
|
|
5
5
|
* 长视频每条都包含总脚本和 2/3 套完整分镜,不能沿用短脚本的 30 条输出量。
|
|
6
6
|
* 小段回传既能更早显示进度,也避免单个 Codex turn 因输出过大而被中断。
|
|
7
7
|
*/
|
|
8
|
-
export declare function flowCScriptChunkSizes(durationSeconds: 10 | 20 | 30): readonly [10, 5, 1] | readonly [
|
|
8
|
+
export declare function flowCScriptChunkSizes(durationSeconds: 10 | 20 | 30): readonly [10, 5, 1] | readonly [1];
|
|
9
9
|
/** 将当前缺失 ordinal 切成可独立持久化的子批;长视频绝不恢复 30 条大回合。 */
|
|
10
10
|
export declare function flowCScriptChunks(durationSeconds: 10 | 20 | 30, ordinals: number[], size?: number): number[][];
|
|
@@ -160,6 +160,8 @@ export declare class WorkflowManager {
|
|
|
160
160
|
status: string;
|
|
161
161
|
} & {
|
|
162
162
|
error?: string;
|
|
163
|
+
code?: string;
|
|
164
|
+
resetOrdinals?: unknown;
|
|
163
165
|
}>;
|
|
164
166
|
submitCandidateChunk(idValue: unknown, groupsValue: unknown): Promise<{
|
|
165
167
|
accepted: number;
|
|
@@ -168,6 +170,8 @@ export declare class WorkflowManager {
|
|
|
168
170
|
selectedCandidates: SelectedCandidate[];
|
|
169
171
|
} & {
|
|
170
172
|
error?: string;
|
|
173
|
+
code?: string;
|
|
174
|
+
resetOrdinals?: unknown;
|
|
171
175
|
}>;
|
|
172
176
|
downloadState(): {
|
|
173
177
|
configured: boolean;
|
|
@@ -293,6 +297,9 @@ export declare function terminalScriptChunkError(results: Array<{
|
|
|
293
297
|
error?: string;
|
|
294
298
|
terminal?: boolean;
|
|
295
299
|
}>): string;
|
|
300
|
+
export declare function scriptCreativeReplanOrdinals(results: unknown[]): number[];
|
|
301
|
+
export declare function creativeReplanOrdinals(error: unknown): number[];
|
|
302
|
+
export declare function recordCreativeReplanAttempts(attempts: Map<number, number>, ordinals: number[], limit?: number): void;
|
|
296
303
|
export declare function scriptChunkPrompt(id: string, task: ScriptTask, ordinals: number[]): string;
|
|
297
304
|
export declare function creativeCandidatePrompt(id: string, task: ScriptTask, ordinals: number[]): string;
|
|
298
305
|
/**
|
package/dist/workflow/manager.js
CHANGED
|
@@ -253,6 +253,7 @@ export class WorkflowManager {
|
|
|
253
253
|
record.activeChunks = 0;
|
|
254
254
|
let chunkSizeIndex = 0;
|
|
255
255
|
let candidateChunkSize = 2;
|
|
256
|
+
const creativeReplanAttempts = new Map();
|
|
256
257
|
while (record.receivedOrdinals.length < task.requested_count) {
|
|
257
258
|
task = await this.scriptTask(id);
|
|
258
259
|
const selectedBefore = selectedCandidateOrdinals(task);
|
|
@@ -280,6 +281,22 @@ export class WorkflowManager {
|
|
|
280
281
|
const terminalError = terminalScriptChunkError(results);
|
|
281
282
|
if (terminalError)
|
|
282
283
|
throw new Error(`创意或脚本结构化契约被 Codex 拒绝,已停止自动重试(${terminalError})`);
|
|
284
|
+
const replanOrdinals = scriptCreativeReplanOrdinals(results);
|
|
285
|
+
if (replanOrdinals.length) {
|
|
286
|
+
recordCreativeReplanAttempts(creativeReplanAttempts, replanOrdinals);
|
|
287
|
+
record.message = `中心发现创意骨架重复,正在为 ordinal ${replanOrdinals.join(", ")} 重新选题;已完成脚本保持不变`;
|
|
288
|
+
record.updatedAt = now();
|
|
289
|
+
this.save();
|
|
290
|
+
const replanResults = await Promise.all(replanOrdinals.map((ordinal) => this.runCandidateChunk(id, task, [ordinal], workspace.workspacePath)));
|
|
291
|
+
const replanTerminalError = terminalScriptChunkError(replanResults);
|
|
292
|
+
if (replanTerminalError)
|
|
293
|
+
throw new Error(`创意重新选题被 Codex 拒绝,已停止自动重试(${replanTerminalError})`);
|
|
294
|
+
const replanError = replanResults.find((result) => result.error)?.error;
|
|
295
|
+
if (replanError)
|
|
296
|
+
throw new Error(`创意重新选题失败(${replanError}),请点击重试`);
|
|
297
|
+
chunkSizeIndex = 0;
|
|
298
|
+
continue;
|
|
299
|
+
}
|
|
283
300
|
if (record.receivedOrdinals.length > receivedBefore)
|
|
284
301
|
chunkSizeIndex = 0;
|
|
285
302
|
if (selectedCandidateOrdinals(task).length > selectedBefore.length) {
|
|
@@ -306,6 +323,22 @@ export class WorkflowManager {
|
|
|
306
323
|
if (terminalError) {
|
|
307
324
|
throw new Error(`本机脚本结构化契约被 Codex 拒绝,已停止自动重试且未提交缺失脚本。请先升级或修复 Canvas Agent,再手动重试(${terminalError})`);
|
|
308
325
|
}
|
|
326
|
+
const replanOrdinals = scriptCreativeReplanOrdinals(results);
|
|
327
|
+
if (replanOrdinals.length) {
|
|
328
|
+
recordCreativeReplanAttempts(creativeReplanAttempts, replanOrdinals);
|
|
329
|
+
record.message = `中心发现创意骨架重复,正在为 ordinal ${replanOrdinals.join(", ")} 重新选题;已完成脚本保持不变`;
|
|
330
|
+
record.updatedAt = now();
|
|
331
|
+
this.save();
|
|
332
|
+
const replanResults = await Promise.all(replanOrdinals.map((ordinal) => this.runCandidateChunk(id, task, [ordinal], workspace.workspacePath)));
|
|
333
|
+
const replanTerminalError = terminalScriptChunkError(replanResults);
|
|
334
|
+
if (replanTerminalError)
|
|
335
|
+
throw new Error(`创意重新选题被 Codex 拒绝,已停止自动重试(${replanTerminalError})`);
|
|
336
|
+
const replanError = replanResults.find((result) => result.error)?.error;
|
|
337
|
+
if (replanError)
|
|
338
|
+
throw new Error(`创意重新选题失败(${replanError}),请点击重试`);
|
|
339
|
+
chunkSizeIndex = 0;
|
|
340
|
+
continue;
|
|
341
|
+
}
|
|
309
342
|
const progressed = record.receivedOrdinals.length > before;
|
|
310
343
|
if (progressed) {
|
|
311
344
|
chunkSizeIndex = 0;
|
|
@@ -396,7 +429,11 @@ export class WorkflowManager {
|
|
|
396
429
|
return { terminal: false };
|
|
397
430
|
}
|
|
398
431
|
catch (error) {
|
|
399
|
-
return {
|
|
432
|
+
return {
|
|
433
|
+
error: error instanceof Error ? `结构化脚本未通过本机校验:${error.message}` : "结构化脚本未通过本机校验",
|
|
434
|
+
terminal: false,
|
|
435
|
+
replanOrdinals: creativeReplanOrdinals(error),
|
|
436
|
+
};
|
|
400
437
|
}
|
|
401
438
|
}
|
|
402
439
|
/** 只记录阶段、ordinal 和毫秒数;禁止记录 prompt、令牌或中心能力。 */
|
|
@@ -572,6 +609,23 @@ export function compareScriptQueueRecords(left, right) {
|
|
|
572
609
|
export function terminalScriptChunkError(results) {
|
|
573
610
|
return results.find((result) => result.terminal)?.error || "";
|
|
574
611
|
}
|
|
612
|
+
export function scriptCreativeReplanOrdinals(results) {
|
|
613
|
+
return [...new Set(results.flatMap((result) => (result && typeof result === "object" ? result.replanOrdinals || [] : [])).filter((ordinal) => Number.isInteger(ordinal) && ordinal > 0))].sort((left, right) => left - right);
|
|
614
|
+
}
|
|
615
|
+
export function creativeReplanOrdinals(error) {
|
|
616
|
+
if (!error || typeof error !== "object" || error.code !== "FLOW_C_CREATIVE_REPLAN_REQUIRED")
|
|
617
|
+
return [];
|
|
618
|
+
const values = error.resetOrdinals;
|
|
619
|
+
return Array.isArray(values) ? [...new Set(values.map(Number).filter((ordinal) => Number.isInteger(ordinal) && ordinal > 0))].sort((left, right) => left - right) : [];
|
|
620
|
+
}
|
|
621
|
+
export function recordCreativeReplanAttempts(attempts, ordinals, limit = 3) {
|
|
622
|
+
for (const ordinal of ordinals) {
|
|
623
|
+
const next = Number(attempts.get(ordinal) || 0) + 1;
|
|
624
|
+
attempts.set(ordinal, next);
|
|
625
|
+
if (next > limit)
|
|
626
|
+
throw new Error(`ordinal ${ordinal} 已重新选题 ${limit} 次仍与已完成脚本重复,已停止避免无效循环`);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
575
629
|
class ExpiredCapabilityError extends Error {
|
|
576
630
|
}
|
|
577
631
|
export function scriptChunkPrompt(id, task, ordinals) {
|
|
@@ -765,8 +819,13 @@ async function commerceJson(url, token, tokenHeader, init = {}) {
|
|
|
765
819
|
headers.set("content-type", "application/json");
|
|
766
820
|
const response = await fetch(url, { ...init, headers, signal: AbortSignal.timeout(120_000) });
|
|
767
821
|
const body = await response.json().catch(() => ({}));
|
|
768
|
-
if (!response.ok)
|
|
769
|
-
|
|
822
|
+
if (!response.ok) {
|
|
823
|
+
const error = new Error(body.error || `中心接口返回 ${response.status}`);
|
|
824
|
+
error.status = response.status;
|
|
825
|
+
error.code = body.code;
|
|
826
|
+
error.resetOrdinals = body.resetOrdinals;
|
|
827
|
+
throw error;
|
|
828
|
+
}
|
|
770
829
|
return body;
|
|
771
830
|
}
|
|
772
831
|
function loadState() {
|