@xiaohhhh1/canvas-agent 0.4.76 → 0.4.78

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.
@@ -0,0 +1,55 @@
1
+ export declare const FLOW_C_CONTENT_STRATEGY_VERSION = "flow-c-content-strategy-v1";
2
+ export type FlowCContentStrategy = {
3
+ contractVersion: typeof FLOW_C_CONTENT_STRATEGY_VERSION;
4
+ mode: "smart-diverse" | "best-match";
5
+ seed: string;
6
+ };
7
+ export type FlowCContentDirection = {
8
+ contractVersion: typeof FLOW_C_CONTENT_STRATEGY_VERSION;
9
+ mode: FlowCContentStrategy["mode"];
10
+ directionKey: string;
11
+ intentLabel: string;
12
+ brief: string;
13
+ selectionReason: string;
14
+ variationSeed: string;
15
+ recentAvoidance?: {
16
+ sameProductSelections: number;
17
+ matchedDirectionUses: number;
18
+ policy: "soft-penalty-only";
19
+ };
20
+ };
21
+ export type FlowCContentSummary = {
22
+ ordinal: number;
23
+ productIndex: number;
24
+ opening: string;
25
+ proof: string;
26
+ voiceover: string;
27
+ };
28
+ export type FlowCContentAdvisory = {
29
+ ordinal: number;
30
+ code: "voice_pacing" | "ending_frame_unanchored" | "repeated_opening";
31
+ segment?: number;
32
+ shot?: number;
33
+ wordCount?: number;
34
+ suggestedMaxWords?: number;
35
+ durationSeconds?: number;
36
+ matchedOrdinal?: number;
37
+ };
38
+ /** Missing/unknown versions keep historical tasks on their exact original prompt. */
39
+ export declare function flowCContentStrategy(value: unknown): FlowCContentStrategy | null;
40
+ export declare function flowCContentDirection(value: unknown, strategy: FlowCContentStrategy | null): FlowCContentDirection | null;
41
+ /** Summarize actual rendered output, never the seed-labelled candidate fingerprint. */
42
+ export declare function summarizeFlowCContentJob(value: unknown): FlowCContentSummary | null;
43
+ /** Only centrally acknowledged ordinals are durable avoidance evidence. */
44
+ export declare function mergeFlowCContentSummaries(values: unknown, jobs: unknown, acceptedOrdinals: readonly number[]): FlowCContentSummary[];
45
+ /** Advisory only: never rewrite, reject, or claim semantic acceptance of a script. */
46
+ export declare function flowCContentAdvisories(jobs: unknown, strategy: FlowCContentStrategy | null, options?: {
47
+ targetLanguage?: unknown;
48
+ recentScripts?: unknown;
49
+ }): FlowCContentAdvisory[];
50
+ export declare function flowCContentMethodPrompt(strategy: FlowCContentStrategy | null, options: {
51
+ recentScripts?: unknown;
52
+ productIndexes: number[];
53
+ ordinals: number[];
54
+ frameworkOrdinals: number[];
55
+ }): string;
@@ -0,0 +1,134 @@
1
+ export const FLOW_C_CONTENT_STRATEGY_VERSION = "flow-c-content-strategy-v1";
2
+ function object(value) {
3
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
4
+ }
5
+ function text(value, limit) {
6
+ return String(value ?? "").normalize("NFKC").trim().replace(/\s+/g, " ").slice(0, limit);
7
+ }
8
+ function summaryText(value, limit) {
9
+ return text(typeof value === "string" ? value.replace(/https?:\/\/\S+|data:\S+/gi, "[link]") : "", limit);
10
+ }
11
+ /** Missing/unknown versions keep historical tasks on their exact original prompt. */
12
+ export function flowCContentStrategy(value) {
13
+ const input = object(value);
14
+ if (input.contractVersion !== FLOW_C_CONTENT_STRATEGY_VERSION || (input.mode !== "smart-diverse" && input.mode !== "best-match"))
15
+ return null;
16
+ const seed = text(input.seed, 1000);
17
+ return seed ? { contractVersion: FLOW_C_CONTENT_STRATEGY_VERSION, mode: input.mode, seed } : null;
18
+ }
19
+ export function flowCContentDirection(value, strategy) {
20
+ const input = object(value);
21
+ if (!strategy || input.contractVersion !== strategy.contractVersion || input.mode !== strategy.mode)
22
+ return null;
23
+ const directionKey = text(input.directionKey, 100);
24
+ const variationSeed = text(input.variationSeed, 100);
25
+ if (!/^flow-c-direction:[a-f0-9]{32}$/.test(directionKey) || !/^[a-f0-9]{24}$/.test(variationSeed))
26
+ return null;
27
+ const avoidance = object(input.recentAvoidance);
28
+ const count = (value) => Math.min(1000, Math.max(0, Math.floor(Number(value) || 0)));
29
+ const recentAvoidance = input.recentAvoidance && typeof input.recentAvoidance === "object" && !Array.isArray(input.recentAvoidance)
30
+ ? { sameProductSelections: count(avoidance.sameProductSelections), matchedDirectionUses: count(avoidance.matchedDirectionUses), policy: "soft-penalty-only" }
31
+ : undefined;
32
+ return {
33
+ contractVersion: strategy.contractVersion,
34
+ mode: strategy.mode,
35
+ directionKey,
36
+ intentLabel: text(input.intentLabel, 120),
37
+ brief: text(input.brief, 2400),
38
+ selectionReason: text(input.selectionReason, 700),
39
+ variationSeed,
40
+ ...(recentAvoidance ? { recentAvoidance } : {}),
41
+ };
42
+ }
43
+ function summary(value) {
44
+ const input = object(value);
45
+ if (!Number.isInteger(input.ordinal) || Number(input.ordinal) < 1 || !Number.isInteger(input.productIndex) || Number(input.productIndex) < 0)
46
+ return null;
47
+ const result = { ordinal: Number(input.ordinal), productIndex: Number(input.productIndex), opening: summaryText(input.opening, 240), proof: summaryText(input.proof, 240), voiceover: summaryText(input.voiceover, 160) };
48
+ return result.opening || result.proof || result.voiceover ? result : null;
49
+ }
50
+ /** Summarize actual rendered output, never the seed-labelled candidate fingerprint. */
51
+ export function summarizeFlowCContentJob(value) {
52
+ const job = object(value);
53
+ const script = typeof job.script === "string" ? job.script : "";
54
+ const fields = (name) => [...script.matchAll(new RegExp(`(?:^|\\|)\\s*${name}:\\s*([^|\\r\\n]*)`, "gm"))]
55
+ .map((match) => match[1].trim()).filter((item) => item && !/^(?:none|无|sin voz|sin diálogo)$/i.test(item));
56
+ return summary({ ordinal: job.ordinal, productIndex: job.productIndex, opening: fields("VISUAL")[0], proof: fields("EVIDENCE").slice(0, 3).join("; "), voiceover: fields("VO")[0] });
57
+ }
58
+ /** Only centrally acknowledged ordinals are durable avoidance evidence. */
59
+ export function mergeFlowCContentSummaries(values, jobs, acceptedOrdinals) {
60
+ const accepted = new Set(acceptedOrdinals);
61
+ const result = new Map();
62
+ for (const item of [...(Array.isArray(values) ? values.map(summary) : []), ...(Array.isArray(jobs) ? jobs.map(summarizeFlowCContentJob) : [])]) {
63
+ if (item && accepted.has(item.ordinal))
64
+ result.set(item.ordinal, item);
65
+ }
66
+ return [...result.values()].slice(-40);
67
+ }
68
+ /** Advisory only: never rewrite, reject, or claim semantic acceptance of a script. */
69
+ export function flowCContentAdvisories(jobs, strategy, options = {}) {
70
+ if (!strategy || !Array.isArray(jobs))
71
+ return [];
72
+ const language = text(options.targetLanguage, 160).toLowerCase();
73
+ const wordBudgetApplies = /^(?:en|es)(?:[-_]|$)|\b(?:english|spanish|español|espanol|inglés|ingles)\b|英语|英語|美语|美語|西班牙语|西班牙語|西语|西語/u.test(language);
74
+ const spoken = (value) => {
75
+ const line = text(typeof value === "string" ? value : "", 20_000);
76
+ return /^(?:none|无|sin voz|sin diálogo)$/i.test(line) ? "" : line;
77
+ };
78
+ const seen = (Array.isArray(options.recentScripts) ? options.recentScripts : []).map(summary).filter((item) => Boolean(item));
79
+ const advisories = [];
80
+ for (const value of jobs) {
81
+ const job = object(value);
82
+ if (!Number.isInteger(job.ordinal) || Number(job.ordinal) < 1 || !Number.isInteger(job.productIndex) || Number(job.productIndex) < 0)
83
+ continue;
84
+ const ordinal = Number(job.ordinal);
85
+ const segments = Array.isArray(job.segments) ? job.segments : job.segment ? [job.segment] : [];
86
+ let opening = "";
87
+ for (const [segmentIndex, segmentValue] of segments.entries()) {
88
+ const segment = object(segmentValue);
89
+ const shots = Array.isArray(segment.shots) ? segment.shots.map(object) : [];
90
+ for (const [shotIndex, shot] of shots.entries()) {
91
+ const voice = spoken(shot.voiceover);
92
+ if (!opening && voice)
93
+ opening = voice;
94
+ const seconds = Number(shot.endSeconds) - Number(shot.startSeconds);
95
+ // Do not apply an English/Spanish word estimate to other scripts.
96
+ if (!voice || !wordBudgetApplies || /[\p{Script=Han}\p{Script=Hiragana}\p{Script=Katakana}\p{Script=Hangul}]/u.test(voice) || !Number.isFinite(seconds) || seconds <= 0)
97
+ continue;
98
+ const words = (voice.match(/[\p{L}\p{N}]+(?:['’][\p{L}\p{N}]+)*/gu) || []).length;
99
+ const suggestedMaxWords = Math.floor(seconds * 2);
100
+ if (words > suggestedMaxWords)
101
+ advisories.push({ ordinal, code: "voice_pacing", segment: segmentIndex + 1, shot: shotIndex + 1, wordCount: words, suggestedMaxWords, durationSeconds: seconds });
102
+ }
103
+ const frame = text(object(segment.endingState).endingFrame, 20_000);
104
+ if (shots.length && frame && !text(shots.at(-1)?.visual, 20_000).endsWith(frame)) {
105
+ advisories.push({ ordinal, code: "ending_frame_unanchored", segment: segmentIndex + 1, shot: shots.length });
106
+ }
107
+ }
108
+ if (strategy.mode === "smart-diverse" && opening) {
109
+ const match = seen.find((item) => item.ordinal !== ordinal && item.productIndex === job.productIndex && spoken(item.voiceover).toLowerCase() === opening.toLowerCase());
110
+ if (match)
111
+ advisories.push({ ordinal, code: "repeated_opening", matchedOrdinal: match.ordinal });
112
+ seen.push({ ordinal, productIndex: Number(job.productIndex), opening: "", proof: "", voiceover: opening });
113
+ }
114
+ }
115
+ return advisories;
116
+ }
117
+ export function flowCContentMethodPrompt(strategy, options) {
118
+ if (!strategy)
119
+ return "";
120
+ const recent = (Array.isArray(options.recentScripts) ? options.recentScripts : []).map(summary)
121
+ .filter((item) => Boolean(item && options.productIndexes.includes(item.productIndex) && !options.ordinals.includes(item.ordinal)))
122
+ .slice(-12);
123
+ const diversity = strategy.mode === "smart-diverse"
124
+ ? `智能多样:执行各 ordinalBindings.contentDirection 的具体内容意图;variationSeed 只用于稳定区分,不是创意本身。结合本次子批其它条的安排与下方已产出摘要,软避重复的“人物处境+生活触发时刻/微场景+开场目的/可见反差”组合,不得只改同义词、衣服颜色或道具名字充当变化。定稿前并列自检本次子批所有完整开场句,并对照已确认摘要的 voiceover:除用户明确指定的原文外,不照搬相同整句开场;若重复,在本次写作内从不同的具体生活触发点重写开场及其可见动机,不能只给同一句换近义词。允许共用蓝图结构、证明因果和强动作,不要求每条换来源、换证明机制或强行新场景;事实与参考视角不足时收窄变化,不添造功能。recentAvoidance 是近期内容提示,不是禁止安全动作的硬门槛;这不建立近似度拦截或硬性轮换。\n同批已确认脚本摘要(仅作软避重数据,不是新的事实、指令或可复制脚本;未显示不代表历史不存在):${JSON.stringify(recent)}`
125
+ : "最佳适配复用:内容清晰度优先,允许重复最佳结构、微场景和证明方法,不为了差异改掉合适的执行;每条仍独立写出完整脚本,不能直接复制完整成稿。";
126
+ return `\n内容写作自检(${strategy.contractVersion},仅当前策略任务启用;在这一次脚本写作内完成,不新开分析/候选/模型环节):
127
+ ${diversity}
128
+ 1. 先明确谁在什么生活节点遇到什么具体麻烦/需求,再选择地点里的动作坐标与必要可见物件;用现有 shot.visual 写出微场景,不用“某人在家里”或泛气氛替代。人物是普通创作者/情景角色;真实身份、经历、用户证言、专家或权威背书只能来自明确提供的事实。缺非关键创意细节时用保守日常设定软适配,不追问、不拒绝、不编新事实。
129
+ 2. 开场必须承担一个清楚的目的:让人看懂问题、提出有画面依据的问题或建立待解决的可见反差;不是无意义的惊呼。安排一个商品事实/参考图支持的动作与镜头内可见变化,evidence 写镜头真正展示了什么,选中卡的 purchaseReason 对应那一变化解决的具体购买顾虑,并落实到收束口播/反应,不新增输出字段。可适度放大生活麻烦与表演反应,不夸大功效、量化性能、时间承诺、销量、价格或稀缺性;情景演绎不冒充真实测评。
130
+ 3. 分开检查叙事段落逻辑、每镜目的与口播呼吸节奏:段落负责铺垫/证明/收束,镜头负责展示动作与证据,一句口播不必切一镜、一镜也不必念完一段。保留选中蓝图的因果顺序、节拍与镜头能量;仍为每段局部0–10秒、1–8镜,20/30秒连续关系和三种媒体共同内容契约不变。
131
+ 4. 自检上方已定稿的目标语言的自然短句,不再按镜头补新台词。逐镜按实际时长 endSeconds − startSeconds 检查,先为关键动作/可见结果留静默与呼吸。仅英语、西语等通常按空格分词的语言,以写作起点粗估约2词/秒,动作/结果镜头更少:1.5秒约2–3词,不塞完整8词问句;2秒约3–4词,不塞10词CTA;2.6秒约4–5词,不塞13词整句。整段10秒约12–20词只是起点,可以更少,不是最低字数要求;全段词数够少也不能把口播挤在一两个短镜头,不能借其它静默镜头的时长冲抵当前镜头超载。不能套用到中文、日语等其它语言,不套统一英语词数或固定八秒模板。这是同轮软预算,不是语言验收拒绝条件或新重写环节;只在用户锁定内容之外收窄意图、缩短句子,不得提高语速硬塞。voiceProfile.speakingRate/pauseHabit 与 voiceCue 要符合实际可说完的语速,不得标成 unhurried/慢速却塞满台词,也不能用 brisk 掩盖超载;emotionalNote 只写镜头目的/情绪转折。不输出计算、推理或预算报告。
132
+ 5. contentDirection 只细化既有蓝图可替换的人物、微场景、措辞和执行槽位,不改变商品身份、纹理文字、能力证据、0–10秒分段或媒体制作方式。creativeBrief 始终最高优先;用户框架 ordinal ${JSON.stringify(options.frameworkOrdinals)} 的开头、事件顺序、核心剧情和结尾不可被方向卡/避重覆盖,只能在其留白处具体化。
133
+ 返回前按以上内容要求自检,修正能在本次写作中修正的空泛描述。自检不是新验收闸门;只输出原严格 schema 的既有字段,不输出推理、自检报告、contentDirection 或其它新增字段。\n`;
134
+ }
@@ -2,8 +2,9 @@ import type { AgentEmit } from "../agent/types.js";
2
2
  import { type CanvasAgentConfig } from "../config.js";
3
3
  import { type FlowCProductExecutionProfile } from "./product-profile.js";
4
4
  import { type CommerceRequestDiagnostic } from "./commerce-http.js";
5
+ import { type FlowCContentAdvisory, type FlowCContentStrategy, type FlowCContentSummary } from "./content-method.js";
5
6
  export declare const FLOW_C_CODEX_TURN_TIMEOUT_MS: number;
6
- export declare const FLOW_C_SELECTED_BLUEPRINT_PROMPT_MAX_CHARS = 36000;
7
+ export declare const FLOW_C_SELECTED_BLUEPRINT_PROMPT_MAX_CHARS = 45000;
7
8
  export declare const FLOW_C_SCRIPT_REWRITE_MAX_ATTEMPTS = 2;
8
9
  export declare const FLOW_C_CANDIDATE_REVISION_MAX_ATTEMPTS = 2;
9
10
  export declare const FLOW_C_LEGACY_DIRECT_CONTRACT_VERSION = "flow-c-template-direct-v2";
@@ -26,6 +27,9 @@ type ScriptRecord = {
26
27
  activeChunks?: number;
27
28
  productProfiles?: FlowCProductExecutionProfile[];
28
29
  pendingScriptJobs?: DraftJob[];
30
+ contentStrategy?: FlowCContentStrategy;
31
+ contentSummaries?: FlowCContentSummary[];
32
+ contentAdvisories?: FlowCContentAdvisory[];
29
33
  lastFailure?: CommerceRequestDiagnostic;
30
34
  retryRequested?: boolean;
31
35
  priorityAt?: string;
@@ -78,6 +82,7 @@ type SelectedCandidate = {
78
82
  ordinal: number;
79
83
  productIndex: number;
80
84
  candidateRevision?: string;
85
+ contentDirection?: unknown;
81
86
  scriptSource?: string;
82
87
  creativeSource?: string;
83
88
  sellingFormCardId?: string | null;
@@ -122,6 +127,8 @@ type ScriptTask = {
122
127
  market: string;
123
128
  target_language?: string | null;
124
129
  localization?: Record<string, unknown>;
130
+ creative_strategy?: unknown;
131
+ content_recent_scripts?: FlowCContentSummary[];
125
132
  script_source_default?: string;
126
133
  duration_seconds?: 10 | 20 | 30;
127
134
  script_output_contract_version?: string;
@@ -197,6 +204,11 @@ export declare class WorkflowManager {
197
204
  accessToken?: unknown;
198
205
  expiresAt?: unknown;
199
206
  }): {
207
+ contentReview?: {
208
+ blocking: boolean;
209
+ semanticAcceptance: string;
210
+ advisories: FlowCContentAdvisory[];
211
+ } | undefined;
200
212
  id: string;
201
213
  status: ScriptStatus;
202
214
  requestedCount: number;
@@ -209,6 +221,11 @@ export declare class WorkflowManager {
209
221
  updatedAt: string;
210
222
  };
211
223
  retryScript(idValue: unknown): {
224
+ contentReview?: {
225
+ blocking: boolean;
226
+ semanticAcceptance: string;
227
+ advisories: FlowCContentAdvisory[];
228
+ } | undefined;
212
229
  id: string;
213
230
  status: ScriptStatus;
214
231
  requestedCount: number;
@@ -221,6 +238,11 @@ export declare class WorkflowManager {
221
238
  updatedAt: string;
222
239
  };
223
240
  scriptStatus(idValue: unknown): {
241
+ contentReview?: {
242
+ blocking: boolean;
243
+ semanticAcceptance: string;
244
+ advisories: FlowCContentAdvisory[];
245
+ } | undefined;
224
246
  id: string;
225
247
  status: ScriptStatus;
226
248
  requestedCount: number;
@@ -271,6 +293,7 @@ export declare class WorkflowManager {
271
293
  }[];
272
294
  };
273
295
  health(): {
296
+ contentStrategyVersion: string;
274
297
  activeScriptHandoffIds: string[];
275
298
  activeScripts: number;
276
299
  scriptConcurrencyLimit: number;
@@ -364,6 +387,8 @@ export declare class WorkflowManager {
364
387
  private runCandidateChunk;
365
388
  /** 一个 chunk 使用独立线程;解析与中心持久化失败不会影响并行 lane。 */
366
389
  private runScriptChunk;
390
+ /** Nonblocking, text-free diagnostics for newly generated policy scripts only. */
391
+ private noteScriptContentAdvisories;
367
392
  /** 只记录阶段、ordinal 和毫秒数;禁止记录 prompt、令牌或中心能力。 */
368
393
  private emitScriptStage;
369
394
  /**
@@ -404,7 +429,7 @@ export declare function isFlowCPromptPayloadTooLarge(error: unknown): error is E
404
429
  export declare function productExecutionProfilePrompt(product: ProductInput, contractVersion?: string): string;
405
430
  export declare function scriptChunkPrompt(id: string, task: ScriptTask, ordinals: number[], rewriteAttempt?: number): string;
406
431
  export declare function creativeCandidatePrompt(id: string, task: ScriptTask, ordinals: number[]): string;
407
- export declare function selectedBlueprintPromptPayload(values: SelectedCandidate[], fallbackTargetDurationSeconds?: number): {
432
+ export declare function selectedBlueprintPromptPayload(values: SelectedCandidate[], fallbackTargetDurationSeconds?: number, contentStrategy?: FlowCContentStrategy | null): {
408
433
  executionBlueprints: {
409
434
  blueprintRef: string;
410
435
  executionBlueprint: string;
@@ -414,6 +439,7 @@ export declare function selectedBlueprintPromptPayload(values: SelectedCandidate
414
439
  }[];
415
440
  productAdaptations: Record<string, unknown>[];
416
441
  ordinalBindings: {
442
+ contentDirection?: import("./content-method.js").FlowCContentDirection | undefined;
417
443
  ordinal: number;
418
444
  productIndex: number;
419
445
  blueprintRef: string | null;
@@ -421,6 +447,49 @@ export declare function selectedBlueprintPromptPayload(values: SelectedCandidate
421
447
  variationSeed: string;
422
448
  }[];
423
449
  };
450
+ export declare function selectedCandidatePlan(value: SelectedCandidate | undefined, contentStrategy?: FlowCContentStrategy | null): {
451
+ contentDirection?: import("./content-method.js").FlowCContentDirection | undefined;
452
+ visualPremise: string;
453
+ learnedTemplateId: string | null;
454
+ learnedTemplateSource: Record<string, unknown> | null;
455
+ scriptSource: string | null;
456
+ sellingFormCardId: string | null;
457
+ sellingFormName: string | null;
458
+ sellingFormSelectionReason: string | null;
459
+ sellingFormLibraryVersion: string | null;
460
+ sellingFormSelectionVersion: string | null;
461
+ productIdentityProfile: Record<string, unknown>;
462
+ productExecutionProfileRef: string;
463
+ mutationAxes: string[];
464
+ culturalAnchors: Record<string, unknown>[];
465
+ spectacleEscalation: string;
466
+ productProofAction: string;
467
+ purchaseReason: string;
468
+ truthBoundary: string;
469
+ riskFlags: string[];
470
+ firstFrame: string;
471
+ visualHook: string;
472
+ conflict: string;
473
+ escalation: string;
474
+ turn: string;
475
+ productIntervention: string;
476
+ visibleProof: string;
477
+ callbackMotivation: string;
478
+ differentiationKey: string | undefined;
479
+ firstFrameFocus: string;
480
+ visualContrast: string;
481
+ sensoryTexture: string;
482
+ motionPeak: string;
483
+ compositionLighting: string;
484
+ localNativeDetail: string;
485
+ antiFlatness: string;
486
+ creativeFingerprint: Record<string, string>;
487
+ fingerprintKey: string | undefined;
488
+ selectionScore: number | undefined;
489
+ selectionBreakdown: Record<string, unknown> | undefined;
490
+ selectionMode: string | undefined;
491
+ visualExecutionVersion: string;
492
+ };
424
493
  /**
425
494
  * 已选创意永远优先进入脚本扩写;只有当前没有可写脚本时才生成下一小波候选。
426
495
  * 每次最多占用受控 worker 数量的子批,避免把整批候选提前塞满 worker 队列。
@@ -14,9 +14,10 @@ import { FLOW_C_CREATIVE_CANDIDATE_CONTRACT_VERSION, flowCCreativeCandidateOutpu
14
14
  import { FLOW_C_LEGACY_PRODUCT_EXECUTION_PROFILE_CONTRACT_VERSION, FLOW_C_PRODUCT_EXECUTION_PROFILE_CONTRACT_VERSION, flowCProductExecutionProfileOutputSchema, parseFlowCProductExecutionProfileOutput } from "./product-profile.js";
15
15
  import { flowCScriptOutputSchema, parseFlowCScriptOutput } from "./script-output.js";
16
16
  import { commerceJson, CommerceRequestError } from "./commerce-http.js";
17
+ import { FLOW_C_CONTENT_STRATEGY_VERSION, flowCContentAdvisories, flowCContentDirection, flowCContentMethodPrompt, flowCContentStrategy, mergeFlowCContentSummaries } from "./content-method.js";
17
18
  const STATE_FILE = path.join(CONFIG_DIR, "workflow-state.json");
18
19
  export const FLOW_C_CODEX_TURN_TIMEOUT_MS = 8 * 60 * 1000;
19
- export const FLOW_C_SELECTED_BLUEPRINT_PROMPT_MAX_CHARS = 36_000;
20
+ export const FLOW_C_SELECTED_BLUEPRINT_PROMPT_MAX_CHARS = 45_000;
20
21
  export const FLOW_C_SCRIPT_REWRITE_MAX_ATTEMPTS = 2;
21
22
  export const FLOW_C_CANDIDATE_REVISION_MAX_ATTEMPTS = 2;
22
23
  export const FLOW_C_LEGACY_DIRECT_CONTRACT_VERSION = "flow-c-template-direct-v2";
@@ -69,6 +70,7 @@ export class WorkflowManager {
69
70
  activeChunks: 0,
70
71
  productProfiles: previous?.productProfiles || [],
71
72
  pendingScriptJobs: previous?.pendingScriptJobs || [],
73
+ ...(previous?.contentAdvisories ? { contentAdvisories: previous.contentAdvisories } : {}),
72
74
  lastFailure: previous?.lastFailure,
73
75
  priorityAt: now(),
74
76
  message: previous?.status === "complete" ? previous.message : "已进入本机 Codex 队列",
@@ -115,6 +117,12 @@ export class WorkflowManager {
115
117
  record.receivedOrdinals = [...new Set([...record.receivedOrdinals, ...task.received_ordinals.map(Number).filter(Number.isInteger)])].sort((left, right) => left - right);
116
118
  task.received_ordinals = record.receivedOrdinals;
117
119
  record.productProfiles = mergeProductProfiles(record.productProfiles, task.product_execution_profiles);
120
+ const contentStrategy = flowCContentStrategy(task.creative_strategy);
121
+ if (contentStrategy) {
122
+ record.contentStrategy = contentStrategy;
123
+ record.contentSummaries = mergeFlowCContentSummaries([...(record.contentSummaries || []), ...(Array.isArray(task.content_recent_scripts) ? task.content_recent_scripts : [])], record.pendingScriptJobs, record.receivedOrdinals);
124
+ task.content_recent_scripts = record.contentSummaries;
125
+ }
118
126
  record.pendingScriptJobs = (record.pendingScriptJobs || []).filter((job) => !record.receivedOrdinals.includes(job.ordinal));
119
127
  record.expiresAt = task.expires_at;
120
128
  record.updatedAt = now();
@@ -144,9 +152,13 @@ export class WorkflowManager {
144
152
  record.receivedOrdinals = [...accepted].sort((a, b) => a - b);
145
153
  }
146
154
  record.requestedCount = Number(data.requestedCount || record.requestedCount);
155
+ if (flowCContentStrategy(record.contentStrategy))
156
+ record.contentSummaries = mergeFlowCContentSummaries(record.contentSummaries, jobs, record.receivedOrdinals);
147
157
  record.pendingScriptJobs = (record.pendingScriptJobs || []).filter((job) => !record.receivedOrdinals.includes(job.ordinal));
148
158
  record.status = data.status === "ready" ? "complete" : "running";
149
- record.message = data.status === "ready" ? `全部 ${record.requestedCount} 条高质量脚本已回传` : `已回传 ${data.received}/${data.requestedCount} 条脚本`;
159
+ record.message = data.status === "ready"
160
+ ? flowCContentStrategy(record.contentStrategy) ? `全部 ${record.requestedCount} 条脚本已回传(内容语义仍需审阅)` : `全部 ${record.requestedCount} 条高质量脚本已回传`
161
+ : `已回传 ${data.received}/${data.requestedCount} 条脚本`;
150
162
  record.updatedAt = now();
151
163
  this.save();
152
164
  return data;
@@ -182,6 +194,7 @@ export class WorkflowManager {
182
194
  const records = Object.values(this.state.scripts);
183
195
  const workers = flowCCodexWorkerStatus();
184
196
  return {
197
+ contentStrategyVersion: FLOW_C_CONTENT_STRATEGY_VERSION,
185
198
  activeScriptHandoffIds: [...this.runningScripts],
186
199
  activeScripts: workers.active,
187
200
  scriptConcurrencyLimit: workers.limit,
@@ -436,7 +449,7 @@ export class WorkflowManager {
436
449
  const finalTask = await this.scriptTask(id);
437
450
  if (finalTask.status === "ready" && record.receivedOrdinals.length === task.requested_count) {
438
451
  record.status = "complete";
439
- record.message = `全部 ${task.requested_count} 条高质量脚本已回传`;
452
+ record.message = flowCContentStrategy(task.creative_strategy) ? `全部 ${task.requested_count} 条脚本已回传(内容语义仍需审阅)` : `全部 ${task.requested_count} 条高质量脚本已回传`;
440
453
  }
441
454
  else
442
455
  throw new Error(`中心校验尚未通过(${record.receivedOrdinals.length}/${task.requested_count} 条),请点击重试`);
@@ -611,7 +624,10 @@ export class WorkflowManager {
611
624
  const durationSeconds = Number(task.duration_seconds || 10);
612
625
  let prompt;
613
626
  try {
614
- prompt = scriptChunkPrompt(id, task, ordinals, rewriteAttempt);
627
+ const promptTask = flowCContentStrategy(task.creative_strategy)
628
+ ? { ...task, received_ordinals: record.receivedOrdinals, content_recent_scripts: mergeFlowCContentSummaries([...(Array.isArray(task.content_recent_scripts) ? task.content_recent_scripts : []), ...(record.contentSummaries || [])], [], record.receivedOrdinals) }
629
+ : task;
630
+ prompt = scriptChunkPrompt(id, promptTask, ordinals, rewriteAttempt);
615
631
  }
616
632
  catch (error) {
617
633
  if (isFlowCPromptPayloadTooLarge(error))
@@ -640,9 +656,10 @@ export class WorkflowManager {
640
656
  ...job,
641
657
  sellingFormId: selected.get(job.ordinal)?.sellingFormCardId || job.sellingFormId,
642
658
  expectedCandidateRevision: selected.get(job.ordinal)?.candidateRevision,
643
- creativePlan: { ...(job.creativePlan || {}), ...selectedCandidatePlan(selected.get(job.ordinal)) },
659
+ creativePlan: { ...(job.creativePlan || {}), ...selectedCandidatePlan(selected.get(job.ordinal), flowCContentStrategy(task.creative_strategy)) },
644
660
  }));
645
661
  this.emitScriptStage(id, ordinals, "parse", Date.now() - parseStartedAt);
662
+ this.noteScriptContentAdvisories(id, task, jobs);
646
663
  const persistStartedAt = Date.now();
647
664
  await this.submitGeneratedScriptJobs(id, jobs);
648
665
  this.emitScriptStage(id, ordinals, "persist", Date.now() - persistStartedAt);
@@ -724,6 +741,21 @@ export class WorkflowManager {
724
741
  };
725
742
  }
726
743
  }
744
+ /** Nonblocking, text-free diagnostics for newly generated policy scripts only. */
745
+ noteScriptContentAdvisories(id, task, jobs) {
746
+ const strategy = flowCContentStrategy(task.creative_strategy);
747
+ if (!strategy)
748
+ return;
749
+ const record = this.scriptRecord(id);
750
+ const current = jobs.filter((job) => !record.receivedOrdinals.includes(job.ordinal));
751
+ const localization = compactTaskLocalization(task);
752
+ const recentScripts = mergeFlowCContentSummaries([...(Array.isArray(task.content_recent_scripts) ? task.content_recent_scripts : []), ...(record.contentSummaries || [])], [], record.receivedOrdinals);
753
+ const advisories = flowCContentAdvisories(current, strategy, { targetLanguage: localization.targetLanguage || task.target_language || localization.targetLocale, recentScripts });
754
+ const ordinals = new Set(current.map((job) => job.ordinal));
755
+ record.contentAdvisories = [...(record.contentAdvisories || []).filter((item) => !ordinals.has(item.ordinal)), ...advisories].slice(-120);
756
+ // The following normal delivery saves these metadata with the untouched
757
+ // draft. Advisories never change status, discard output or start a turn.
758
+ }
727
759
  /** 只记录阶段、ordinal 和毫秒数;禁止记录 prompt、令牌或中心能力。 */
728
760
  emitScriptStage(handoffId, ordinals, stage, durationMs) {
729
761
  this.emit("agent_event", { agent: "codex", type: "workflow.script.stage", handoffId, ordinals, stage, duration_ms: Math.max(0, Math.round(durationMs)), model: FLOW_C_CODEX_MODEL, reasoning: FLOW_C_CODEX_REASONING_EFFORT });
@@ -1060,22 +1092,29 @@ export function scriptChunkPrompt(id, task, ordinals, rewriteAttempt = 0) {
1060
1092
  : "";
1061
1093
  const localization = compactTaskLocalization(task);
1062
1094
  const targetVoiceLanguage = String(localization.targetLanguage || task.target_language || localization.targetLocale || "").trim() || `目标市场 ${task.market} 的自然当地语言`;
1095
+ const contentStrategy = flowCContentStrategy(task.creative_strategy);
1096
+ const contentMethod = flowCContentMethodPrompt(contentStrategy, {
1097
+ recentScripts: mergeFlowCContentSummaries(task.content_recent_scripts, [], task.received_ordinals),
1098
+ productIndexes: products.map((product) => product.productIndex),
1099
+ ordinals,
1100
+ frameworkOrdinals: [...selected.values()].filter((candidate) => candidate.selectionMode === "user-framework").map((candidate) => candidate.ordinal),
1101
+ });
1063
1102
  return `你正在后台完成 Flow C 脚本交接 ${id},只写 ordinal:${ordinals.join(", ")};映射:${scriptProductAssignments(task.product_quantities, ordinals)}。
1064
1103
  目标市场:${task.market}。目标口播语言:${targetVoiceLanguage}。结构化当地化事实(国家、语言/locale、出镜者、受众、生活场景、创作者口吻和CTA彼此独立;不得从国家推断族群):${compactJson(localization, 3_000, "当前脚本子批的当地化事实")}。商品事实:${compactJson(productFacts, 12_000, "当前脚本子批的商品事实")}
1065
- 中心已完成当前 ordinal 的创意选择。executionBlueprints 只保存一次共享的模型无关创意蓝图(可能来自已学习爆款,也可能来自已筛选带货形式卡);productAdaptations 只保存一次每个不同的当前商品执行卡;ordinalBindings 只用 blueprintRef/adaptationRef 和 variationSeed 映射到具体脚本。存在 blueprintRef 时,对应 executionBlueprint 是唯一结构权威;没有 blueprintRef 时,对应 productAdaptation 是用户框架的唯一执行权威。相同蓝图可供同一商品整批复用,直接替换成当前商品执行,不要生成候选、重新选题或重新评分:
1066
- ${compactJson(selectedBlueprintPromptPayload([...selected.values()], duration), FLOW_C_SELECTED_BLUEPRINT_PROMPT_MAX_CHARS, "当前脚本子批的共享蓝图与商品适配")}
1104
+ 中心已完成当前 ordinal 的创意选择。executionBlueprints 只保存一次共享的模型无关创意蓝图(可能来自已学习爆款,也可能来自已筛选带货形式卡);productAdaptations 只保存一次每个不同的当前商品执行卡;ordinalBindings ${contentStrategy ? "用 blueprintRef/adaptationRef、variationSeed 和逐条 contentDirection" : "只用 blueprintRef/adaptationRef 和 variationSeed"} 映射到具体脚本。存在 blueprintRef 时,对应 executionBlueprint 是唯一结构权威;没有 blueprintRef 时,对应 productAdaptation 是用户框架的唯一执行权威。相同蓝图可供同一商品整批复用,直接替换成当前商品执行,不要生成候选、重新选题或重新评分:
1105
+ ${compactJson(selectedBlueprintPromptPayload([...selected.values()], duration, contentStrategy), FLOW_C_SELECTED_BLUEPRINT_PROMPT_MAX_CHARS, "当前脚本子批的共享蓝图与商品适配")}
1067
1106
  ${rewriteInstruction}
1068
1107
  ${durationRules}
1069
1108
  写作要求:
1070
1109
  1. visualHook、conflict、escalation/turn、productIntervention、visibleProof、purchaseReason、callbackMotivation 与所有 shots 必须执行选中卡的 visualPremise、firstFrame、spectacleEscalation、productProofAction、purchaseReason、truthBoundary 以及七项视觉执行字段;保留所选蓝图的因果时间线、节拍比例、镜头/剪辑节奏、动作压力、证明方式和购买逻辑,只替换当前商品、人物、场景与当地口播槽位,不得稀释构图或换成普通模板。爆款来源的旧商品身份/动作必须替换;形式卡只提供抽象创意结构,绝不能反向改变现有首帧、分段、Omni或参考图制作方式。形式库中的工厂、仓库、街访、使用者体验、主理人、探店、补货、对比、促销和耐用形式都可以用 AI 人物与场景正常演绎;缺少现实素材或事实证据不是脚本失败条件,必须按 executionBlueprint.executionAdaptation 保留所选形式,同时不把演绎的角色、地点、订单、库存、销量、价格、身份或经历写成已核验的现实事实。若 productAdaptation.templateMatch.actionCompatibility 的 openingAction、proofAction 或 spectacleAction 为 false,对应 productAdaptation 的 firstFrame、productProofAction 或 spectacleEscalation 对商品动作内容具有绝对优先级,必须删除其旧商品动作,不得折中混用。productProofAction 必须同时写清一个商品图或执行档案能支持的可见动作及其镜头内可见结果;purchaseReason 只解释该结果为何值得目标用户购买,不能变成泛泛 CTA。
1071
1110
  2. 严格执行 executionBlueprints 中的 sourceDurationSeconds → targetDurationSeconds 和 retimingMode:expand 只增加有用的证明停留、使用语境与购买理由,compress 只压缩重复、冗余铺垫和转场,same 保持原节拍比例;三种模式都必须保留 Hook、证明和购买因果,禁止整体拉伸、截断或机械加速。
1072
- 3. 每条生成一份稳定 voiceProfile;每段只写简短 voiceCue。口播必须使用${targetVoiceLanguage},并执行 creatorVoiceStyle/ctaStyle;未提供 creatorVoiceStyle 时使用当地 TikTok 带货创作者真实会说的口吻。不是逐字翻译,整条内容按 Hook → Body/visible proof → Close 组织,短句口语化且与当前镜头可见动作同步,结尾只给一个简短自然 CTA。只使用少量可信的 localSceneProfile/audienceContext 日常细节;presenterContext 没填写时使用普通创作者,不得把国家代码、语言或地区自动等同于人物族群。商品事实没有提供时不得编造价格、折扣、销量、库存或稀缺性。商品标题只用于理解商品类型与事实,所有输出不得主动写出或念出品牌名、品牌广告语、包装文案,也不得要求模型把品牌字样变得更清晰;商品外观与已有标识的位置、比例、颜色和版式只服从第1张产品参考图。TikTok/TikTok Shop 只用于定义这条写作规则,不得把平台名写入 voiceover、onScreenText、visual、soundBgm、emotionalNote、openingState、endingState 或任何会进入故事板/视频的导演字段。导演说明统一用简洁制作英文;这里只做写作指令,不做语言检测字段。
1073
- 4. 每段永远是独立 0–10 秒,含 1–8 个按剧情需要决定的 shots、准确 voiceover、onScreenText、evidence、soundBgm、emotionalNote 与 endingState;模型不要输出 continuity 或 continuityMode。
1111
+ 3. 每条生成一份稳定 voiceProfile;每段只写简短 voiceCue。口播必须使用${targetVoiceLanguage},并执行 creatorVoiceStyle/ctaStyle;未提供 creatorVoiceStyle 时使用当地 TikTok 带货创作者真实会说的口吻。${contentStrategy ? '不是逐字翻译。Hook → Body/visible proof → Close 是画面叙事结构,不等于口播结构;镜头数量不等于台词数量。除用户明确锁定的对白外,先为整个局部10秒定稿1–2个能自然说完的很短口语句,再分配到真正需要补充人物动机或购买判断的镜头,不逐镜复述 visual;其它镜头默认 voiceover="none",只在 soundBgm 保留动作现场声。结尾的一个简短自然 CTA 并入已定稿短句,不另外追加一句。用户锁定的对白、原框架和目标语言优先,不擅自删改;上述定稿与分配在同一次写作内完成,不输出中间声音轨。' : '不是逐字翻译,整条内容按 Hook → Body/visible proof → Close 组织,短句口语化且与当前镜头可见动作同步,结尾只给一个简短自然 CTA。'}只使用少量可信的 localSceneProfile/audienceContext 日常细节;presenterContext 没填写时使用普通创作者,不得把国家代码、语言或地区自动等同于人物族群。商品事实没有提供时不得编造价格、折扣、销量、库存或稀缺性。商品标题只用于理解商品类型与事实,所有输出不得主动写出或念出品牌名、品牌广告语、包装文案,也不得要求模型把品牌字样变得更清晰;商品外观与已有标识的位置、比例、颜色和版式只服从第1张产品参考图。TikTok/TikTok Shop 只用于定义这条写作规则,不得把平台名写入 voiceover、onScreenText、visual、soundBgm、emotionalNote、openingState、endingState 或任何会进入故事板/视频的导演字段。导演说明统一用简洁制作英文;这里只做写作指令,不做语言检测字段。
1112
+ 4. 每段永远是独立 0–10 秒,含 1–8 个按剧情需要决定的 ${contentStrategy ? 'shots。每镜 voiceover 字段必须存在,但不等于必须有台词:只填上一步分配给该镜的原句,其余填非空静默标记 "none",绝不让配音念出 none;每镜同时给出准确 onScreenText、evidence、soundBgm、emotionalNote,并提供每段 endingState。先写末镜实际动作,再在末镜 visual 最后用一个短句明确第10秒的最终画面;endingState.endingFrame 逐字复用这个收尾短句,其余 endingState 字段也只描述同一瞬间,不另编姿势或动作。已走出画面的人不能仍在画内回头,已经落地的脚不能又悬在半空,已经完成的动作不能仍写为待完成;确已完成且无续接动作时 unfinishedAction 写 none。下一段首镜从这个真实终点继续,不重置人物、物体或动作;最终一段也要检查,不能只保证跨段字段相等' : 'shots、准确 voiceover、onScreenText、evidence、soundBgm、emotionalNote 与 endingState'};模型不要输出 continuity 或 continuityMode。
1074
1113
  5. shot.visual 要直接写出主体位置、景别、光线、材质、动作峰值和当地自然环境,并保留真实皮肤、细发、布料/商品微纹理和实景混合光;禁止塑料蜡感、静态举产品、品牌广告片或电视购物。允许执行所选形式的 AI 工厂/仓库/零售/人物场景,但不得声称它们是该商家真实工厂、产地、生产档案、订单、库存、销量、实名客户证言或线下价格。
1075
1114
  6. 20/30 秒的后一段剧情承接前段 endingState,但每段仍只使用自己的局部 0–10 秒执行内容。
1076
1115
  7. 不要输出 creativePlan、executionBindings 或 masterScript;这些字段由 Agent 从选中蓝图确定性回填/合成,避免重复 token 和脆弱的文字绑定校验。若 selectionMode=user-framework,用户原框架的开头、事件顺序、核心剧情和结尾仍是最高权威。
1077
1116
  8. 商品身份以按顺序提供的原商品图为最高权威:第1张是主SKU身份图,当前商品标题和大概类目只补充商品用途与事实,后续图片只是同一SKU补充视角。任何爆款源商品、首帧、故事板或上一段尾帧都不得改变商品颜色、款式、结构、比例、材质、包装、标识位置、部件、配件和实际套装数量;修正身份时不得降低动作、运镜、节奏、景深、真实感或画质。商品本体或包装表面的印花、图案、微纹理、已印文字/字符、图标、色块、行距、相对位置和朝向全部视为不可编辑的原图纹理;不得在 shot.visual、onScreenText、voiceover、evidence、openingState、endingState 或任何导演字段中要求重写、翻译、替换、删改、重排、镜像这些表面内容,也不得要求生成“另一段清晰可读文案”。新增屏幕字只能是与商品像素分离的场景叠加字幕,绝不能印到商品或包装上。旋转、翻转、弯折或开合商品时,完整表面纹理必须随实体整体运动,不能漂移、翻面后复写或重新生成。若 executionBlueprint 或 productAdaptation 与本规则冲突,只保留其钩子、动作因果和镜头节奏,改写构图、机位或表演,绝不改商品表面。
1078
- 固定使用 GPT-5.6 Terra 中等推理。媒体制作方式与分镜布局由中心在脚本完成后管理,不得影响这里的创意、镜头、口播或结构化脚本。只返回当前结构化 jobs,不调用工具、不创建任何图片或视频任务。`;
1117
+ ${contentMethod}固定使用 GPT-5.6 Terra 中等推理。媒体制作方式与分镜布局由中心在脚本完成后管理,不得影响这里的创意、镜头、口播或结构化脚本。只返回当前结构化 jobs,不调用工具、不创建任何图片或视频任务。`;
1079
1118
  }
1080
1119
  export function creativeCandidatePrompt(id, task, ordinals) {
1081
1120
  const products = relevantProductInputs(task, ordinals);
@@ -1195,7 +1234,7 @@ function selectedCandidatesForOrdinals(task, ordinals) {
1195
1234
  const expected = new Set(ordinals);
1196
1235
  return new Map((task.selected_candidates || []).filter((candidate) => expected.has(Number(candidate.ordinal))).map((candidate) => [Number(candidate.ordinal), candidate]));
1197
1236
  }
1198
- export function selectedBlueprintPromptPayload(values, fallbackTargetDurationSeconds) {
1237
+ export function selectedBlueprintPromptPayload(values, fallbackTargetDurationSeconds, contentStrategy = null) {
1199
1238
  const blueprints = new Map();
1200
1239
  const blueprintRefBySignature = new Map();
1201
1240
  const adaptations = new Map();
@@ -1255,12 +1294,14 @@ export function selectedBlueprintPromptPayload(values, fallbackTargetDurationSec
1255
1294
  const adaptationRef = `product-${candidate.productIndex}-${createHash("sha256").update(signature).digest("hex").slice(0, 16)}`;
1256
1295
  if (!adaptations.has(adaptationRef))
1257
1296
  adaptations.set(adaptationRef, { adaptationRef, ...adaptation });
1297
+ const contentDirection = flowCContentDirection(candidate.contentDirection, contentStrategy);
1258
1298
  return {
1259
1299
  ordinal: candidate.ordinal,
1260
1300
  productIndex: candidate.productIndex,
1261
1301
  blueprintRef,
1262
1302
  adaptationRef,
1263
- variationSeed: candidate.fingerprintKey || `ordinal-${candidate.ordinal}`,
1303
+ variationSeed: contentDirection?.variationSeed || candidate.fingerprintKey || `ordinal-${candidate.ordinal}`,
1304
+ ...(contentDirection ? { contentDirection } : {}),
1264
1305
  };
1265
1306
  });
1266
1307
  return {
@@ -1273,9 +1314,10 @@ function positiveDuration(value) {
1273
1314
  const duration = Number(value);
1274
1315
  return Number.isFinite(duration) && duration > 0 ? Math.round(duration * 1000) / 1000 : null;
1275
1316
  }
1276
- function selectedCandidatePlan(value) {
1317
+ export function selectedCandidatePlan(value, contentStrategy = null) {
1277
1318
  if (!value)
1278
1319
  throw new Error("中心缺少选中的创意候选");
1320
+ const contentDirection = flowCContentDirection(value.contentDirection, contentStrategy);
1279
1321
  return {
1280
1322
  visualPremise: value.visualPremise,
1281
1323
  learnedTemplateId: value.learnedTemplateId || null,
@@ -1317,6 +1359,7 @@ function selectedCandidatePlan(value) {
1317
1359
  selectionBreakdown: value.selectionBreakdown,
1318
1360
  selectionMode: value.selectionMode,
1319
1361
  visualExecutionVersion: "flow-c-visual-execution-v1",
1362
+ ...(contentDirection ? { contentDirection } : {}),
1320
1363
  };
1321
1364
  }
1322
1365
  function chunkNumbers(values, size) {
@@ -1388,7 +1431,7 @@ function productIndexForOrdinal(productQuantities, ordinal) {
1388
1431
  return -1;
1389
1432
  }
1390
1433
  function publicScript(record) {
1391
- return { id: record.id, status: record.status, requestedCount: record.requestedCount, received: record.receivedOrdinals.length, threadId: record.threadId, chunkSize: record.chunkSize || null, activeChunks: Number(record.activeChunks || 0), message: record.message, expiresAt: record.expiresAt, updatedAt: record.updatedAt };
1434
+ return { id: record.id, status: record.status, requestedCount: record.requestedCount, received: record.receivedOrdinals.length, threadId: record.threadId, chunkSize: record.chunkSize || null, activeChunks: Number(record.activeChunks || 0), message: record.message, expiresAt: record.expiresAt, updatedAt: record.updatedAt, ...(record.contentAdvisories ? { contentReview: { blocking: false, semanticAcceptance: "not-evaluated", advisories: record.contentAdvisories } } : {}) };
1392
1435
  }
1393
1436
  function publicDownload(record) {
1394
1437
  return { batchId: record.batchId, status: record.status, downloaded: record.downloadedOrdinals.length, market: record.market, message: record.message, expiresAt: record.expiresAt, updatedAt: record.updatedAt };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@xiaohhhh1/canvas-agent",
3
- "version": "0.4.76",
3
+ "version": "0.4.78",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",