freebeat-mcp 0.4.2 → 0.4.3

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,192 @@
1
+ import { z } from "zod";
2
+ export const CO_CREATE_DESCRIPTION = "On freebeat.ai a person creates a video by chatting with a director agent and seeing what it made. In auto mode the agent makes the creative decisions and delivers the film; otherwise it pauses where a person's taste matters. Through these tools you stand where that person stands: read the agent's words and work, and say anything the person could type there, including approval, changes, direction, or handing the rest to auto. Decide whether and how to involve the user from their intent. Credit consent is given when the video starts; continuing this session needs no new consent. Relay any credit notice the agent reports. ";
3
+ export const autoModeSchema = z.boolean().describe("true: let the director agent make creative decisions and deliver the film. false: co-create through get_video_session and send_video_agent_message when it pauses for creative input. Choose from the user's request, or discuss the choice when needed.");
4
+ export const videoTaskIdSchema = z.string().max(500).regex(/^(music_video|dance|lyric_video):.+/)
5
+ .describe("The task_id from generate_music_video, generate_dance_video, or generate_lyric_video.");
6
+ const stageStatusSchema = z.enum(["not_started", "queued", "running", "retrying", "pending_review", "approved", "complete", "completed", "failed", "cancelled", "warning", "unknown"]);
7
+ const fieldSchema = z.object({ path: z.string().max(500), value: z.union([z.string().max(4000), z.number().finite(), z.boolean()]) });
8
+ const mediaSchema = z.object({ path: z.string().max(500), url: z.string().max(2048) });
9
+ const displaySchema = { fields: z.array(fieldSchema).max(400), media: z.array(mediaSchema).max(100) };
10
+ export const videoSessionSchema = z.object({
11
+ task_id: videoTaskIdSchema,
12
+ session_url: z.string(),
13
+ status: z.enum(["running", "waiting_for_user", "idle"]),
14
+ auto_mode: z.boolean().nullable(),
15
+ current_stage: z.string().max(120).nullable(),
16
+ stages: z.array(z.object({ name: z.string().max(120), status: stageStatusSchema })).max(64),
17
+ waiting_for: z.array(z.object({ ...displaySchema })).max(20),
18
+ messages: z.array(z.object({ role: z.enum(["user", "assistant"]), parts: z.array(z.object({
19
+ type: z.string().max(80), ...displaySchema,
20
+ })).max(40) })).max(20),
21
+ artifacts: z.array(z.object({
22
+ type: z.string().max(120), version: z.number().finite().optional(), is_latest: z.boolean().optional(),
23
+ ...displaySchema,
24
+ })).max(24),
25
+ truncated: z.boolean(),
26
+ });
27
+ export function record(value) {
28
+ return value && typeof value === "object" && !Array.isArray(value) ? value : {};
29
+ }
30
+ // Match the website's structural text boundary before treating assistant text as visible content.
31
+ export function creatorFacingText(value) {
32
+ let text = value.trimStart();
33
+ if (/^\[reasoning\]/i.test(text) && !/<\/mm:think>/i.test(text))
34
+ return undefined;
35
+ text = text.replace(/(?:^|\n\n)\[reasoning\][\s\S]*?<\/mm:think>\s*/gi, "")
36
+ .replace(/(?:^|\n\n)\[(?:summary|clarification|support needed)\][^\n]*(?=\n\n|$)/gi, "")
37
+ .replace(/^\[assistant text compacted:[^\n]+\]\n[^\n]*\n\n/, "").trim();
38
+ if (/^\[reasoning\]/i.test(text) && !/<\/mm:think>/i.test(text))
39
+ return undefined;
40
+ if (!text || text.startsWith("{") || /^Updated todo list to\s+/i.test(text) ||
41
+ /^##\s*SESSION INTENT\b/i.test(text) || (/##\s*ARTIFACTS\b/i.test(text) && /##\s*NEXT STEPS\b/i.test(text)))
42
+ return undefined;
43
+ if (text.startsWith("[")) {
44
+ try {
45
+ JSON.parse(text);
46
+ return undefined;
47
+ }
48
+ catch {
49
+ if (/"[\w-]+"\s*:/.test(text))
50
+ return undefined;
51
+ }
52
+ }
53
+ return text;
54
+ }
55
+ // Artifacts are the agent's work product and pass through whole; only credentials, diagnostics and raw upstream data never do.
56
+ const hiddenKey = (key) => /(^|_)(token|secret|passw(or)?d|api_?key|authori[sz]ation|cookie|credentials?|signature|metadata|debug|trace|traceback|stack_?trace)$/i.test(key) ||
57
+ /(Token|Secret|Password|ApiKey|Authorization|Cookie|Credentials?|Signature|Metadata|Debug|Trace)$/.test(key) || /^(provider|raw)(_|[A-Z])/.test(key);
58
+ const mediaFields = new Set(`url src href public_url publicUrl preview_url previewUrl thumbnail_url thumbnailUrl cover_url coverUrl
59
+ image_url imageUrl video_url videoUrl audio_url audioUrl result_url resultUrl final_video_url finalVideoUrl grid_image_url gridImageUrl subject_photo_uri
60
+ object_key objectKey payload_ref payloadRef preview_object_key previewObjectKey image_urls imageUrls storyboard_cover_image_url storyboardCoverImageUrl cover_image_url coverImageUrl`.split(/\s+/));
61
+ const mediaContainers = new Set(["image", "video", "audio", "thumbnail", "grid_image", "frame", "cover"]);
62
+ export function projectVideoSession(taskId, sessionUrl, host, rawState, rawMessages, rawAssets) {
63
+ let remaining = 60_000;
64
+ let truncated = false;
65
+ const take = (value, max) => {
66
+ const length = Math.min(max, remaining);
67
+ if (value.length > length)
68
+ truncated = true;
69
+ const text = value.slice(0, length);
70
+ remaining -= text.length;
71
+ return text;
72
+ };
73
+ const limit = (items, max) => {
74
+ if (items.length > max)
75
+ truncated = true;
76
+ return items.slice(0, max);
77
+ };
78
+ const display = (raw) => {
79
+ const fields = [];
80
+ const media = [];
81
+ const visit = (value, path, depth, isMedia = false) => {
82
+ if (depth > 10 || remaining <= 0 || fields.length >= 400 || media.length >= 100) {
83
+ truncated = true;
84
+ return;
85
+ }
86
+ if (Array.isArray(value)) {
87
+ limit(value, 64).forEach((item, i) => visit(item, `${path}[${i}]`, depth + 1, isMedia));
88
+ return;
89
+ }
90
+ if (typeof value === "string" || typeof value === "boolean" || typeof value === "number") {
91
+ if (remaining < path.length + 100) {
92
+ truncated = true;
93
+ return;
94
+ }
95
+ remaining -= path.length + 100;
96
+ }
97
+ if (typeof value === "string") {
98
+ if (isMedia || /^https?:\/\/\S+$/i.test(value)) {
99
+ try {
100
+ const url = new URL(value, host);
101
+ if (!/^https?:$/.test(url.protocol) || url.username || url.password || url.href.length > 2048)
102
+ return;
103
+ if (url.href.length > remaining) {
104
+ truncated = true;
105
+ return;
106
+ }
107
+ media.push({ path: path.slice(0, 500), url: take(url.href, 2048) });
108
+ }
109
+ catch { /* Invalid media has no viewable URL. */ }
110
+ }
111
+ else if (value.trim())
112
+ fields.push({ path: path.slice(0, 500), value: take(value, 4000) });
113
+ }
114
+ else if (typeof value === "boolean" || (typeof value === "number" && Number.isFinite(value))) {
115
+ fields.push({ path: path.slice(0, 500), value });
116
+ }
117
+ else {
118
+ for (const [key, child] of Object.entries(record(value))) {
119
+ if (!hiddenKey(key))
120
+ visit(child, path ? `${path}.${key}` : key, depth + 1, mediaFields.has(key) || mediaContainers.has(key));
121
+ }
122
+ }
123
+ };
124
+ visit(raw, "", 0);
125
+ return { fields, media };
126
+ };
127
+ const state = record(rawState);
128
+ const thread = record(rawMessages);
129
+ const pipeline = record(state.pipeline_state);
130
+ const stages = limit(Object.entries(record(pipeline.stages)), 64).map(([name, status]) => ({
131
+ name: take(name, 120), status: stageStatusSchema.safeParse(status).success ? status : "unknown",
132
+ }));
133
+ const rawMessageList = Array.isArray(thread.messages) ? thread.messages : [];
134
+ if (rawMessageList.length > 20)
135
+ truncated = true;
136
+ remaining -= 24_000;
137
+ const messages = rawMessageList.slice(-20).reverse().flatMap(value => {
138
+ const message = record(value);
139
+ if (message.role !== "user" && message.role !== "assistant")
140
+ return [];
141
+ // Reasoning and tool-call parts are the agent's internals; text, files and data parts are what the page shows.
142
+ const parts = (Array.isArray(message.parts) ? message.parts : []).map(record).filter(part => typeof part.type === "string" && (part.type === "text" || part.type === "file" || part.type.startsWith("data-")));
143
+ if (parts.length > 40)
144
+ truncated = true;
145
+ return [{ role: message.role, parts: parts.slice(-40).reverse().flatMap(part => {
146
+ if (typeof part.type !== "string")
147
+ return [];
148
+ let payload = part.type === "text" || part.type === "file" ? Object.fromEntries(Object.entries(part).filter(([key]) => key !== "type")) : record(part.data);
149
+ if (message.role === "assistant" && ["text", "data-assistant-note"].includes(part.type)) {
150
+ const text = typeof payload.text === "string" ? creatorFacingText(payload.text) : undefined;
151
+ if (!text)
152
+ return [];
153
+ payload = { text };
154
+ }
155
+ return [{ type: part.type.slice(0, 80), ...display(payload) }];
156
+ }).reverse() }];
157
+ }).reverse();
158
+ remaining += 24_000;
159
+ const running = Boolean(state.active_run_id || thread.active_run_id);
160
+ const latest = messages.at(-1);
161
+ const pending = stages.some(stage => stage.status === "pending_review") ||
162
+ (Array.isArray(pipeline.reconciled_pending) && pipeline.reconciled_pending.length > 0);
163
+ const rawAssetList = Array.isArray(record(rawAssets).assets) ? record(rawAssets).assets : [];
164
+ // Once the agent stops, the next word is the person's, also after a film: they can approve it or ask for changes.
165
+ const waiting = !running && (pending || (latest?.role === "assistant" && latest.parts.some(part => part.fields.length || part.media.length)));
166
+ const latestRequest = [...messages].reverse().find(message => message.role === "assistant" && message.parts.some(part => ["text", "data-assistant-note", "data-review-request", "data-credit-notice"].includes(part.type) && (part.fields.length || part.media.length)));
167
+ const waitingFor = waiting && latestRequest ? latestRequest.parts.filter(part => ["text", "data-assistant-note", "data-review-request", "data-credit-notice"].includes(part.type)).slice(-20).map(({ fields, media }) => ({ fields, media })) : [];
168
+ // The agents list assets by type name; keep the current, newest ones when the bound applies.
169
+ const recency = (value) => {
170
+ const asset = record(value);
171
+ return [asset.is_latest === true ? 1 : 0, Date.parse(String(asset.created_at ?? record(asset.payload).created_at ?? "")) || 0];
172
+ };
173
+ const artifacts = limit([...rawAssetList].sort((a, b) => {
174
+ const [latestA, timeA] = recency(a), [latestB, timeB] = recency(b);
175
+ return latestB - latestA || timeB - timeA;
176
+ }), 24).map(value => {
177
+ const asset = record(value);
178
+ return {
179
+ type: take(typeof asset.asset_type === "string" ? asset.asset_type : "unknown", 120),
180
+ version: typeof asset.version === "number" && Number.isFinite(asset.version) ? asset.version : undefined,
181
+ is_latest: typeof asset.is_latest === "boolean" ? asset.is_latest : undefined,
182
+ ...display({ ...record(asset.payload), ...(typeof asset.preview_url === "string" ? { preview_url: asset.preview_url } : {}) }),
183
+ };
184
+ });
185
+ return videoSessionSchema.parse({
186
+ task_id: taskId, session_url: sessionUrl, status: running ? "running" : waiting ? "waiting_for_user" : "idle",
187
+ auto_mode: typeof state.auto_mode === "boolean" ? state.auto_mode : null,
188
+ current_stage: typeof pipeline.current === "string" ? take(pipeline.current, 120) : null,
189
+ stages, waiting_for: waitingFor, messages, artifacts, truncated,
190
+ });
191
+ }
192
+ //# sourceMappingURL=video-session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"video-session.js","sourceRoot":"","sources":["../../src/core/video-session.ts"],"names":[],"mappings":"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"}
@@ -1,3 +1,4 @@
1
+ import { type VideoSession } from "../core/video-session.js";
1
2
  import { type TaskFailure, type TaskBilling } from "../core/task-outcome.js";
2
3
  import type { Readable } from "node:stream";
3
4
  import { z } from "zod";
@@ -276,6 +277,7 @@ export type GenerateTextParams = PaidOptions & {
276
277
  };
277
278
  export type CreateAgentVideoParams = PaidOptions & {
278
279
  product: AgentProduct;
280
+ auto_mode: boolean;
279
281
  music_id: number | string;
280
282
  prompt?: string;
281
283
  start_ms?: number;
@@ -1079,6 +1081,19 @@ export declare class FreebeatApiClient {
1079
1081
  generateMusic(params: GenerateMusicParams): Promise<TaskStartResponse>;
1080
1082
  agentSessionUrl(product: AgentProduct, flowUuid: string): string;
1081
1083
  createAgentVideo(params: CreateAgentVideoParams): Promise<AgentTaskStartResponse | MusicPreparationResponse>;
1084
+ private videoTask;
1085
+ private postVideoAgentMessage;
1086
+ getVideoSession(taskId: string): Promise<VideoSession>;
1087
+ sendVideoAgentMessage(params: {
1088
+ task_id: string;
1089
+ message: string;
1090
+ auto_mode?: boolean;
1091
+ }): Promise<{
1092
+ message: string;
1093
+ auto_mode?: boolean | undefined;
1094
+ task_id: string;
1095
+ delivery: "accepted";
1096
+ }>;
1082
1097
  private getAgentTaskStatus;
1083
1098
  generateSingingVideo(params: GenerateSingingVideoParams): Promise<TaskStartResponse>;
1084
1099
  private prepareLipSyncRequest;
@@ -1,5 +1,6 @@
1
1
  import { normalizeTaskStatus, upstreamTaskStatusSchema } from "../core/task-status.js";
2
2
  import { normalizeCreativeResult } from "../core/task-result.js";
3
+ import { projectVideoSession, record } from "../core/video-session.js";
3
4
  import { taskOutcome } from "../core/task-outcome.js";
4
5
  import { randomUUID } from "node:crypto";
5
6
  import axios from "axios";
@@ -254,6 +255,19 @@ function agentCreditNoticeText(value, depth = 0) {
254
255
  }
255
256
  return undefined;
256
257
  }
258
+ // Only the newest message can be the active run's reply; an older reply's progress belongs to a finished run.
259
+ function agentProgressMessage(messages) {
260
+ const reply = messages.at(-1)?.role === "assistant" ? messages.at(-1) : undefined;
261
+ const data = [...(reply?.parts ?? [])].reverse().find((part) => part.type === "data-stage-progress")?.data;
262
+ const label = typeof data?.label === "string" ? data.label.trim() : "";
263
+ const percent = ["running", "retrying"].includes(String(data?.status)) && typeof data?.progress === "number"
264
+ ? ` (${Math.round(data.progress)}% of this step)` : "";
265
+ return [
266
+ "The Freebeat agent is working.",
267
+ label && `Latest step: ${label}${percent}.`,
268
+ "Full videos usually take 15-60 minutes; poll again later.",
269
+ ].filter(Boolean).join(" ");
270
+ }
257
271
  function musicModelCapabilities(model, pricing) {
258
272
  const parameters = pricing.find(group => group.generationType === 7)?.parameters ?? [];
259
273
  const parameter = (code) => parameters.find(item => item.code === code);
@@ -714,37 +728,14 @@ export class FreebeatApiClient {
714
728
  openWatermark: !params.remove_watermark,
715
729
  enablePublic: false,
716
730
  resolution,
717
- autoExecEnabled: true,
731
+ autoExecEnabled: params.auto_mode,
718
732
  type: product.sessionType,
719
733
  ...product.createFields,
720
734
  }, agentSessionSchema, "createAgentVideo");
721
735
  const flowUuid = session.flowUuid;
722
736
  const sessionUrl = this.agentSessionUrl(params.product, flowUuid);
723
- const clientRunId = randomUUID();
724
737
  try {
725
- const response = await this.agentClient.post(`${this.agentEndpoints[params.product]}/v1/chat`, {
726
- thread_id: flowUuid,
727
- message: {
728
- id: clientRunId,
729
- role: "user",
730
- parts: [
731
- ...(params.image_urls ?? []).map((url) => ({
732
- type: "file",
733
- mediaType: imageMediaType(url),
734
- url,
735
- })),
736
- { type: "text", text: prompt },
737
- ],
738
- },
739
- business_id: flowUuid,
740
- multitask_strategy: "enqueue",
741
- metadata: {
742
- client_run_id: clientRunId,
743
- ...(params.product === "dance" ? { agent_kind: "dance" } : {}),
744
- },
745
- }, { responseType: "stream", headers: { Accept: "text/event-stream, application/json" } });
746
- // The run is queued server-side; the plugin polls instead of holding the stream open.
747
- response.data?.destroy?.();
738
+ await this.postVideoAgentMessage(params.product, flowUuid, prompt, params.image_urls);
748
739
  }
749
740
  catch {
750
741
  throw new AppError(`The ${params.product} session was created but the agent start could not be confirmed. Open ${sessionUrl} to continue.`, "API_ERROR");
@@ -757,6 +748,73 @@ export class FreebeatApiClient {
757
748
  session_url: sessionUrl,
758
749
  };
759
750
  }
751
+ videoTask(taskId) {
752
+ const task = decodeCreativeTaskId(taskId);
753
+ if (!task || !Object.hasOwn(AGENT_PRODUCTS, task.kind)) {
754
+ throw new AppError("Use a task_id from generate_music_video, generate_dance_video, or generate_lyric_video.", "INVALID_PARAMS");
755
+ }
756
+ const product = task.kind;
757
+ return { product, threadId: task.rawId, url: `${this.agentEndpoints[product]}/v1/threads/${encodeURIComponent(task.rawId)}` };
758
+ }
759
+ async postVideoAgentMessage(product, threadId, message, imageUrls = []) {
760
+ const clientRunId = randomUUID();
761
+ const response = await this.agentClient.post(`${this.agentEndpoints[product]}/v1/chat`, {
762
+ thread_id: threadId,
763
+ message: { id: clientRunId, role: "user", parts: [
764
+ ...imageUrls.map(url => ({ type: "file", mediaType: imageMediaType(url), url })),
765
+ { type: "text", text: message },
766
+ ] },
767
+ business_id: threadId,
768
+ multitask_strategy: "enqueue",
769
+ metadata: { client_run_id: clientRunId, ...(product === "dance" ? { agent_kind: "dance" } : {}) },
770
+ }, { responseType: "stream", headers: { Accept: "text/event-stream, application/json" } });
771
+ // The run persists before the SSE response; disconnecting does not cancel it.
772
+ response.data?.destroy?.();
773
+ }
774
+ async getVideoSession(taskId) {
775
+ const task = this.videoTask(taskId);
776
+ // Like the website, a failed state read (its auto-mode lookup can be down) still shows the conversation and artifacts.
777
+ const [state, messages, assets] = await Promise.all([
778
+ this.agentClient.get(`${task.url}/state`).then(response => response.data, () => ({})),
779
+ this.agentClient.get(`${task.url}/messages`, { params: { limit: 20 } }),
780
+ this.agentClient.get(`${task.url}/assets`),
781
+ ]);
782
+ if (!Array.isArray(record(messages.data).messages) || !Array.isArray(record(assets.data).assets)) {
783
+ throw new AppError("Freebeat could not read the video session. Retry get_video_session before sending another message.", "INVALID_RESPONSE");
784
+ }
785
+ return projectVideoSession(taskId, this.agentSessionUrl(task.product, task.threadId), this.agentEndpoints[task.product], state, messages.data, assets.data);
786
+ }
787
+ async sendVideoAgentMessage(params) {
788
+ const task = this.videoTask(params.task_id);
789
+ // Like the website, which locks its chat box while a run is active: a queued message would start paid work nobody reviewed.
790
+ const thread = agentMessagesSchema.safeParse((await this.agentClient.get(`${task.url}/messages`, { params: { limit: 1 } })).data);
791
+ if (!thread.success)
792
+ throw new AppError("Freebeat could not read the video session. Read get_video_session before sending.", "INVALID_RESPONSE");
793
+ if (thread.data.active_run_id) {
794
+ throw new AppError("The agent is still working, so this message was not sent and the auto setting was not changed. Poll get_task_status and send it once the agent is no longer running.", "TASK_NOT_COMPLETED");
795
+ }
796
+ let autoMode;
797
+ if (params.auto_mode !== undefined) {
798
+ const response = await this.agentClient.patch(`${task.url}/auto-mode`, { enabled: params.auto_mode });
799
+ const parsed = z.object({ auto_mode: z.boolean() }).safeParse(response.data);
800
+ if (!parsed.success)
801
+ throw new AppError("Freebeat could not confirm the auto setting. Read get_video_session before continuing.", "INVALID_RESPONSE");
802
+ autoMode = parsed.data.auto_mode;
803
+ }
804
+ try {
805
+ await this.postVideoAgentMessage(task.product, task.threadId, params.message);
806
+ }
807
+ catch (error) {
808
+ if (axios.isAxiosError(error) && error.response?.status === 409) {
809
+ throw new AppError("Freebeat did not accept this message. Read get_video_session before trying again; an optional auto setting may already have changed.", "API_ERROR");
810
+ }
811
+ throw new AppError("Message delivery could not be confirmed. Read get_video_session before resending to avoid duplicate work; an optional auto setting may already have changed.", "API_ERROR");
812
+ }
813
+ return {
814
+ task_id: params.task_id, delivery: "accepted", ...(autoMode === undefined ? {} : { auto_mode: autoMode }),
815
+ message: "Freebeat accepted the message and the agent is starting on it. Poll get_task_status for progress and read get_video_session for the agent's response and current auto setting.",
816
+ };
817
+ }
760
818
  async getAgentTaskStatus(product, flowUuid) {
761
819
  const config = AGENT_PRODUCTS[product];
762
820
  const threadUrl = `${this.agentEndpoints[product]}/v1/threads/${encodeURIComponent(flowUuid)}`;
@@ -771,12 +829,28 @@ export class FreebeatApiClient {
771
829
  const threadOutcome = taskOutcome(thread.data);
772
830
  Object.assign(base, threadOutcome);
773
831
  if (thread.data.active_run_id) {
774
- return {
775
- ...base,
776
- status: "running",
777
- message: "The Freebeat agent is working. Full videos usually take 15-60 minutes; poll again later.",
778
- };
832
+ return { ...base, status: "running", message: agentProgressMessage(thread.data.messages) };
779
833
  }
834
+ const lastAssistant = [...thread.data.messages].reverse().find((message) => message.role === "assistant" && message.parts.some(part => ["data-credit-notice", "data-review-request", "data-assistant-note"].includes(String(part.type)) || (part.type === "text" && typeof part.text === "string" && part.text.trim())));
835
+ const agentWords = (lastAssistant?.parts ?? []).flatMap((part) => {
836
+ const data = record(part.data);
837
+ return part.type === "text" ? [part.text] : part.type === "data-assistant-note" ? [data.text]
838
+ : part.type === "data-review-request" ? [data.title, data.message] : [];
839
+ }).filter((text) => typeof text === "string" && text.trim() !== "").join("\n");
840
+ const waitingForPerson = () => ({
841
+ ...base,
842
+ status: "action_required",
843
+ message: [
844
+ "The Freebeat agent is waiting for the person's input; this is not a failure.",
845
+ agentWords,
846
+ "Read get_video_session with this task_id to see the agent's work and request; use send_video_agent_message to answer.",
847
+ ].filter(Boolean).join("\n"),
848
+ });
849
+ // A stage awaiting review outranks an older finished film: the person asked for a change and the agent waits on it.
850
+ const stages = await this.agentClient.get(`${threadUrl}/state`)
851
+ .then((response) => record(record(record(response.data).pipeline_state).stages), () => ({}));
852
+ if (Object.values(stages).includes("pending_review"))
853
+ return waitingForPerson();
780
854
  const assets = agentAssetListSchema.safeParse((await this.agentClient.get(`${threadUrl}/assets`, { params: { type: config.finalAssetType } })).data);
781
855
  if (!assets.success)
782
856
  throw new AppError("Unexpected agent assets response.", "INVALID_RESPONSE");
@@ -847,7 +921,6 @@ export class FreebeatApiClient {
847
921
  return { ...base, status: "completed", result_payload: JSON.stringify({ video_url: rendered.video_url }) };
848
922
  }
849
923
  }
850
- const lastAssistant = [...thread.data.messages].reverse().find((message) => message.role === "assistant" && message.parts.some(part => part.type === "data-credit-notice" || (part.type === "text" && typeof part.text === "string" && part.text.trim())));
851
924
  if (!lastAssistant) {
852
925
  return {
853
926
  ...base,
@@ -856,10 +929,10 @@ export class FreebeatApiClient {
856
929
  };
857
930
  }
858
931
  const creditNotice = lastAssistant.parts.find((part) => part.type === "data-credit-notice");
859
- const agentMessage = lastAssistant.parts
860
- .filter((part) => part.type === "text" && typeof part.text === "string")
861
- .map((part) => part.text)
862
- .join("\n");
932
+ const upstream = record(thread.data);
933
+ if (!creditNotice && (upstream.error ?? upstream.errorMsg ?? upstream.errorMessage) === undefined)
934
+ return waitingForPerson();
935
+ const agentMessage = agentWords;
863
936
  const noticeData = creditNotice?.data && typeof creditNotice.data === "object" ? creditNotice.data : {};
864
937
  const noticeDetail = noticeData.detail && typeof noticeData.detail === "object" ? noticeData.detail : {};
865
938
  const noticeMessage = (typeof noticeData.message === "string" ? noticeData.message : undefined)
@@ -876,7 +949,7 @@ export class FreebeatApiClient {
876
949
  typeof noticeData.title === "string" ? noticeData.title : undefined,
877
950
  noticeMessage,
878
951
  agentMessage !== outcome.failure?.reason ? agentMessage : undefined,
879
- `Review and continue this session at ${base.session_url}.`,
952
+ "Read get_video_session with this task_id to see the agent's work and request; use send_video_agent_message to continue the conversation.",
880
953
  ].filter(Boolean).join("\n"),
881
954
  };
882
955
  }