Claude e66ce42d73
feat(web): start exports, transcriptions and cancels from the media card
The card's follow-ups now reach the media job RPCs, and it only offers what
the space can run.

- media.capabilities answers which operations the actor's space can run
  (booleans only). The card asks once per minute per space, and only when it
  shows finished video or audio, so other cards cost nothing.
- Export and Transcribe call media.jobs.start with the card's message, so the
  same bot posts the new card; Cancel calls media.jobs.cancel. A refused
  start shows the server's message under the card.
- New files use top-level import type.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01MRtGG9ARfqqhma3t85Ro2u
2026-10-03 14:00:34 +00:00

40 lines
1.6 KiB
TypeScript

import * as z from "zod";
import { Id } from "./ids.js";
import { MediaJobStatusSchema, MediaOperationSchema, MediaOutputSchema } from "./media.js";
/** Operations a user starts from a media card (export, transcribe, edit); generation goes through a bot. */
export const MediaCardOperationSchema = z.enum(["edit_image", "export_video", "transcribe"]);
export type MediaCardOperation = z.infer<typeof MediaCardOperationSchema>;
export const MediaJobStartInputSchema = z
.object({
/** The conversation, like other thread procedures: a bot chat or a group. */
botId: Id.optional(),
groupId: Id.optional(),
/** The message the action came from; its bot posts the new card. */
messageId: Id,
operation: MediaCardOperationSchema,
/** The operation's input; validated by the operation. */
input: z.record(z.string(), z.unknown()),
})
.refine((input) => Boolean(input.botId) !== Boolean(input.groupId), {
message: "Pass botId or groupId",
});
export type MediaJobStartInput = z.infer<typeof MediaJobStartInputSchema>;
export const MediaJobSnapshotSchema = z.object({
id: Id,
operation: MediaOperationSchema,
status: MediaJobStatusSchema,
title: z.string(),
progress: z.number().min(0).max(1).nullable(),
error: z.string().nullable(),
outputs: z.array(MediaOutputSchema),
});
export type MediaJobSnapshotDto = z.infer<typeof MediaJobSnapshotSchema>;
export const MediaJobStartOutputSchema = z.object({ jobId: Id, messageId: Id });
/** Which media operations the actor's space can run now, so cards offer only those. */
export const MediaCapabilitiesSchema = z.record(MediaOperationSchema, z.boolean());