@agentmedia/schema 0.4.0 → 0.5.0
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.
- package/dist/v2/character.d.ts +3 -3
- package/dist/v2/character.d.ts.map +1 -1
- package/dist/v2/character.js +4 -2
- package/dist/v2/character.js.map +1 -1
- package/dist/v2/selfie.d.ts +6 -6
- package/dist/v2/selfie.d.ts.map +1 -1
- package/dist/v2/selfie.js +18 -7
- package/dist/v2/selfie.js.map +1 -1
- package/package.json +1 -1
- package/scripts/generate-v2-docs.ts +224 -4
- package/src/v2/character.ts +4 -2
- package/src/v2/selfie.ts +19 -7
package/dist/v2/character.d.ts
CHANGED
|
@@ -10,21 +10,21 @@
|
|
|
10
10
|
*/
|
|
11
11
|
import { z } from 'zod';
|
|
12
12
|
export declare const CharacterCreateSchema: z.ZodObject<{
|
|
13
|
-
photo_url: z.ZodString
|
|
13
|
+
photo_url: z.ZodOptional<z.ZodString>;
|
|
14
14
|
display_name: z.ZodString;
|
|
15
15
|
description: z.ZodString;
|
|
16
16
|
voice_brief: z.ZodOptional<z.ZodString>;
|
|
17
17
|
preset_default: z.ZodOptional<z.ZodEnum<["bedroom-morning-ritual", "getting-ready-mirror-edge", "bathroom-skincare-routine", "bedside-lamp-evening", "kitchen-glow-up", "backyard-morning-coffee", "picnic-blanket-outdoor", "car-quick-honest-review", "car-passenger-honest", "outdoor-walking-talking", "couch-haul-show-off", "closet-fit-check", "studio-apartment-tour", "balcony-evening-vibes", "desk-wfh-quick-pitch", "cafe-window-seat", "office-bathroom-discreet", "gym-post-workout", "salon-mirror-result", "travel-hotel-room-review"]>>;
|
|
18
18
|
}, "strip", z.ZodTypeAny, {
|
|
19
19
|
description: string;
|
|
20
|
-
photo_url: string;
|
|
21
20
|
display_name: string;
|
|
21
|
+
photo_url?: string | undefined;
|
|
22
22
|
voice_brief?: string | undefined;
|
|
23
23
|
preset_default?: "bedroom-morning-ritual" | "getting-ready-mirror-edge" | "bathroom-skincare-routine" | "bedside-lamp-evening" | "kitchen-glow-up" | "backyard-morning-coffee" | "picnic-blanket-outdoor" | "car-quick-honest-review" | "car-passenger-honest" | "outdoor-walking-talking" | "couch-haul-show-off" | "closet-fit-check" | "studio-apartment-tour" | "balcony-evening-vibes" | "desk-wfh-quick-pitch" | "cafe-window-seat" | "office-bathroom-discreet" | "gym-post-workout" | "salon-mirror-result" | "travel-hotel-room-review" | undefined;
|
|
24
24
|
}, {
|
|
25
25
|
description: string;
|
|
26
|
-
photo_url: string;
|
|
27
26
|
display_name: string;
|
|
27
|
+
photo_url?: string | undefined;
|
|
28
28
|
voice_brief?: string | undefined;
|
|
29
29
|
preset_default?: "bedroom-morning-ritual" | "getting-ready-mirror-edge" | "bathroom-skincare-routine" | "bedside-lamp-evening" | "kitchen-glow-up" | "backyard-morning-coffee" | "picnic-blanket-outdoor" | "car-quick-honest-review" | "car-passenger-honest" | "outdoor-walking-talking" | "couch-haul-show-off" | "closet-fit-check" | "studio-apartment-tour" | "balcony-evening-vibes" | "desk-wfh-quick-pitch" | "cafe-window-seat" | "office-bathroom-discreet" | "gym-post-workout" | "salon-mirror-result" | "travel-hotel-room-review" | undefined;
|
|
30
30
|
}>;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"character.d.ts","sourceRoot":"","sources":["../../src/v2/character.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"character.d.ts","sourceRoot":"","sources":["../../src/v2/character.ts"],"names":[],"mappings":"AAEA;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;EAsBhC,CAAC;AAEH,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC"}
|
package/dist/v2/character.js
CHANGED
|
@@ -12,8 +12,10 @@
|
|
|
12
12
|
import { z } from 'zod';
|
|
13
13
|
import { V2_SHOT_PRESETS } from './selfie.js';
|
|
14
14
|
export const CharacterCreateSchema = z.object({
|
|
15
|
-
//
|
|
16
|
-
|
|
15
|
+
// Optional source photo — if absent, agent-media generates the
|
|
16
|
+
// portrait from `description` alone. Pass a real person's photo
|
|
17
|
+
// ONLY when you want that exact person's likeness.
|
|
18
|
+
photo_url: z.string().url().optional(),
|
|
17
19
|
// Required identity
|
|
18
20
|
display_name: z
|
|
19
21
|
.string()
|
package/dist/v2/character.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"character.js","sourceRoot":"","sources":["../../src/v2/character.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAE/D;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,
|
|
1
|
+
{"version":3,"file":"character.js","sourceRoot":"","sources":["../../src/v2/character.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAE/D;;;;;;;;;GASG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAE9C,MAAM,CAAC,MAAM,qBAAqB,GAAG,CAAC,CAAC,MAAM,CAAC;IAC5C,+DAA+D;IAC/D,gEAAgE;IAChE,mDAAmD;IACnD,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAEtC,oBAAoB;IACpB,YAAY,EAAE,CAAC;SACZ,MAAM,EAAE;SACR,IAAI,EAAE;SACN,GAAG,CAAC,CAAC,CAAC;SACN,GAAG,CAAC,EAAE,CAAC;SACP,KAAK,CACJ,mBAAmB,EACnB,gFAAgF,CACjF;IAEH,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC;IAEvC,uDAAuD;IACvD,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,EAAE;IAClD,cAAc,EAAE,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,QAAQ,EAAE;CACnD,CAAC,CAAC"}
|
package/dist/v2/selfie.d.ts
CHANGED
|
@@ -21,7 +21,7 @@ export declare const V2_SHOT_PRESETS: readonly ["bedroom-morning-ritual", "getti
|
|
|
21
21
|
export type V2ShotPreset = (typeof V2_SHOT_PRESETS)[number];
|
|
22
22
|
export declare const V2_VIBES: readonly ["excited", "calm", "sassy", "serious", "curious"];
|
|
23
23
|
export type V2Vibe = (typeof V2_VIBES)[number];
|
|
24
|
-
export declare const V2_DURATIONS: readonly [5,
|
|
24
|
+
export declare const V2_DURATIONS: readonly [5, 10, 15];
|
|
25
25
|
export type V2Duration = (typeof V2_DURATIONS)[number];
|
|
26
26
|
export declare const SelfieSchema: z.ZodEffects<z.ZodObject<{
|
|
27
27
|
character_id: z.ZodOptional<z.ZodString>;
|
|
@@ -30,11 +30,11 @@ export declare const SelfieSchema: z.ZodEffects<z.ZodObject<{
|
|
|
30
30
|
script: z.ZodString;
|
|
31
31
|
preset: z.ZodDefault<z.ZodEnum<["bedroom-morning-ritual", "getting-ready-mirror-edge", "bathroom-skincare-routine", "bedside-lamp-evening", "kitchen-glow-up", "backyard-morning-coffee", "picnic-blanket-outdoor", "car-quick-honest-review", "car-passenger-honest", "outdoor-walking-talking", "couch-haul-show-off", "closet-fit-check", "studio-apartment-tour", "balcony-evening-vibes", "desk-wfh-quick-pitch", "cafe-window-seat", "office-bathroom-discreet", "gym-post-workout", "salon-mirror-result", "travel-hotel-room-review"]>>;
|
|
32
32
|
vibe: z.ZodDefault<z.ZodEnum<["excited", "calm", "sassy", "serious", "curious"]>>;
|
|
33
|
-
duration: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<5>, z.ZodLiteral<
|
|
33
|
+
duration: z.ZodDefault<z.ZodUnion<[z.ZodLiteral<5>, z.ZodLiteral<10>, z.ZodLiteral<15>]>>;
|
|
34
34
|
voice_brief: z.ZodOptional<z.ZodString>;
|
|
35
35
|
subtitles: z.ZodDefault<z.ZodBoolean>;
|
|
36
36
|
}, "strip", z.ZodTypeAny, {
|
|
37
|
-
duration: 5 |
|
|
37
|
+
duration: 5 | 10 | 15;
|
|
38
38
|
script: string;
|
|
39
39
|
subtitles: boolean;
|
|
40
40
|
vibe: "serious" | "calm" | "excited" | "sassy" | "curious";
|
|
@@ -45,7 +45,7 @@ export declare const SelfieSchema: z.ZodEffects<z.ZodObject<{
|
|
|
45
45
|
voice_brief?: string | undefined;
|
|
46
46
|
}, {
|
|
47
47
|
script: string;
|
|
48
|
-
duration?: 5 |
|
|
48
|
+
duration?: 5 | 10 | 15 | undefined;
|
|
49
49
|
subtitles?: boolean | undefined;
|
|
50
50
|
description?: string | undefined;
|
|
51
51
|
vibe?: "serious" | "calm" | "excited" | "sassy" | "curious" | undefined;
|
|
@@ -54,7 +54,7 @@ export declare const SelfieSchema: z.ZodEffects<z.ZodObject<{
|
|
|
54
54
|
preset?: "bedroom-morning-ritual" | "getting-ready-mirror-edge" | "bathroom-skincare-routine" | "bedside-lamp-evening" | "kitchen-glow-up" | "backyard-morning-coffee" | "picnic-blanket-outdoor" | "car-quick-honest-review" | "car-passenger-honest" | "outdoor-walking-talking" | "couch-haul-show-off" | "closet-fit-check" | "studio-apartment-tour" | "balcony-evening-vibes" | "desk-wfh-quick-pitch" | "cafe-window-seat" | "office-bathroom-discreet" | "gym-post-workout" | "salon-mirror-result" | "travel-hotel-room-review" | undefined;
|
|
55
55
|
voice_brief?: string | undefined;
|
|
56
56
|
}>, {
|
|
57
|
-
duration: 5 |
|
|
57
|
+
duration: 5 | 10 | 15;
|
|
58
58
|
script: string;
|
|
59
59
|
subtitles: boolean;
|
|
60
60
|
vibe: "serious" | "calm" | "excited" | "sassy" | "curious";
|
|
@@ -65,7 +65,7 @@ export declare const SelfieSchema: z.ZodEffects<z.ZodObject<{
|
|
|
65
65
|
voice_brief?: string | undefined;
|
|
66
66
|
}, {
|
|
67
67
|
script: string;
|
|
68
|
-
duration?: 5 |
|
|
68
|
+
duration?: 5 | 10 | 15 | undefined;
|
|
69
69
|
subtitles?: boolean | undefined;
|
|
70
70
|
description?: string | undefined;
|
|
71
71
|
vibe?: "serious" | "calm" | "excited" | "sassy" | "curious" | undefined;
|
package/dist/v2/selfie.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selfie.d.ts","sourceRoot":"","sources":["../../src/v2/selfie.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,eAAe,yfAqBlB,CAAC;AACX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,eAAO,MAAM,QAAQ,6DAA8D,CAAC;AACpF,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/C,eAAO,MAAM,YAAY,
|
|
1
|
+
{"version":3,"file":"selfie.d.ts","sourceRoot":"","sources":["../../src/v2/selfie.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAGxB,eAAO,MAAM,eAAe,yfAqBlB,CAAC;AACX,MAAM,MAAM,YAAY,GAAG,CAAC,OAAO,eAAe,CAAC,CAAC,MAAM,CAAC,CAAC;AAE5D,eAAO,MAAM,QAAQ,6DAA8D,CAAC;AACpF,MAAM,MAAM,MAAM,GAAG,CAAC,OAAO,QAAQ,CAAC,CAAC,MAAM,CAAC,CAAC;AAE/C,eAAO,MAAM,YAAY,sBAAuB,CAAC;AACjD,MAAM,MAAM,UAAU,GAAG,CAAC,OAAO,YAAY,CAAC,CAAC,MAAM,CAAC,CAAC;AAGvD,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAyDrB,CAAC;AAEL,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,YAAY,CAAC,CAAC"}
|
package/dist/v2/selfie.js
CHANGED
|
@@ -42,7 +42,7 @@ export const V2_SHOT_PRESETS = [
|
|
|
42
42
|
'travel-hotel-room-review',
|
|
43
43
|
];
|
|
44
44
|
export const V2_VIBES = ['excited', 'calm', 'sassy', 'serious', 'curious'];
|
|
45
|
-
export const V2_DURATIONS = [5,
|
|
45
|
+
export const V2_DURATIONS = [5, 10, 15];
|
|
46
46
|
// ── Input schema ──────────────────────────────────────────────────────────
|
|
47
47
|
export const SelfieSchema = z
|
|
48
48
|
.object({
|
|
@@ -59,8 +59,8 @@ export const SelfieSchema = z
|
|
|
59
59
|
preset: z.enum(V2_SHOT_PRESETS).default('bedroom-morning-ritual'),
|
|
60
60
|
vibe: z.enum(V2_VIBES).default('excited'),
|
|
61
61
|
duration: z
|
|
62
|
-
.union([z.literal(5), z.literal(
|
|
63
|
-
.default(
|
|
62
|
+
.union([z.literal(5), z.literal(10), z.literal(15)])
|
|
63
|
+
.default(10),
|
|
64
64
|
// Voice direction (one line, natural language). Pulled from the
|
|
65
65
|
// character record when character_id is used; user can still
|
|
66
66
|
// override per-job.
|
|
@@ -70,17 +70,28 @@ export const SelfieSchema = z
|
|
|
70
70
|
})
|
|
71
71
|
.superRefine((val, ctx) => {
|
|
72
72
|
const hasSavedCharacter = !!val.character_id;
|
|
73
|
-
const
|
|
74
|
-
|
|
73
|
+
const hasDescription = !!val.description;
|
|
74
|
+
// Three valid input paths:
|
|
75
|
+
// 1. character_id alone (reuse saved character)
|
|
76
|
+
// 2. description alone (agent-media generates the portrait from text)
|
|
77
|
+
// 3. photo_url + description (use the user's photo as reference)
|
|
78
|
+
// Anything else is rejected.
|
|
79
|
+
if (!hasSavedCharacter && !hasDescription) {
|
|
75
80
|
ctx.addIssue({
|
|
76
81
|
code: z.ZodIssueCode.custom,
|
|
77
|
-
message: 'Provide either character_id, OR
|
|
82
|
+
message: 'Provide either character_id, OR description (with optional photo_url for a real reference person).',
|
|
78
83
|
});
|
|
79
84
|
}
|
|
80
85
|
if (hasSavedCharacter && (val.photo_url || val.description)) {
|
|
81
86
|
ctx.addIssue({
|
|
82
87
|
code: z.ZodIssueCode.custom,
|
|
83
|
-
message: 'Use character_id OR (
|
|
88
|
+
message: 'Use character_id OR description (+ optional photo_url) — not both.',
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
if (val.photo_url && !val.description) {
|
|
92
|
+
ctx.addIssue({
|
|
93
|
+
code: z.ZodIssueCode.custom,
|
|
94
|
+
message: 'photo_url requires a description so we know what to emphasize.',
|
|
84
95
|
});
|
|
85
96
|
}
|
|
86
97
|
});
|
package/dist/v2/selfie.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"selfie.js","sourceRoot":"","sources":["../../src/v2/selfie.ts"],"names":[],"mappings":"AAAA,+DAA+D;AAE/D;;;;;;;;;;;;;;;;;GAiBG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,4EAA4E;AAC5E,MAAM,CAAC,MAAM,eAAe,GAAG;IAC7B,wBAAwB;IACxB,2BAA2B;IAC3B,2BAA2B;IAC3B,sBAAsB;IACtB,iBAAiB;IACjB,yBAAyB;IACzB,wBAAwB;IACxB,yBAAyB;IACzB,sBAAsB;IACtB,yBAAyB;IACzB,qBAAqB;IACrB,kBAAkB;IAClB,uBAAuB;IACvB,uBAAuB;IACvB,sBAAsB;IACtB,kBAAkB;IAClB,0BAA0B;IAC1B,kBAAkB;IAClB,qBAAqB;IACrB,0BAA0B;CAClB,CAAC;AAGX,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,SAAS,EAAE,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,SAAS,CAAU,CAAC;AAGpF,MAAM,CAAC,MAAM,YAAY,GAAG,CAAC,CAAC,EAAE,
|
|
1
|
+
{"version":3,"file":"selfie.js","sourceRoot":"","sources":["../../src/v2/selfie.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agentmedia/schema",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Type-safe schema definitions for AI UGC video generation — enums, Zod validation, and generator registry. Single source of truth for the agent-media platform.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "Apache-2.0",
|
|
@@ -55,8 +55,8 @@ function fmtPricing(def: V2GeneratorRecord): string {
|
|
|
55
55
|
const c = def.pricing.baseCredits;
|
|
56
56
|
return `One-shot: **${c} credits** ($${(c / 100).toFixed(2)})`;
|
|
57
57
|
}
|
|
58
|
-
// per_clip — show 5/
|
|
59
|
-
const rows = [5,
|
|
58
|
+
// per_clip — show 5/10/15
|
|
59
|
+
const rows = [5, 10, 15].map((s) => {
|
|
60
60
|
const c = quoteV2Credits(def.id as any, { durationSeconds: s });
|
|
61
61
|
return `| ${s}s | ${c} | $${(c / 100).toFixed(2)} |`;
|
|
62
62
|
});
|
|
@@ -210,12 +210,232 @@ function renderSkill(): string {
|
|
|
210
210
|
'---',
|
|
211
211
|
'name: agent-media-v2',
|
|
212
212
|
'description: AI UGC video production via agent-media v2 — Selfie videos and reusable Characters. Use this skill when the user wants to make TikTok-style "AI person talking to camera" clips or save a character for reuse across multiple generations.',
|
|
213
|
-
'homepage: https://agent-media.ai',
|
|
214
|
-
`version: 1.
|
|
213
|
+
'homepage: https://agent-media.ai/skill',
|
|
214
|
+
`version: 1.3.0`,
|
|
215
215
|
'---',
|
|
216
216
|
'',
|
|
217
217
|
GENERATED_NOTE,
|
|
218
218
|
'',
|
|
219
|
+
'# agent-media — STRICT RULES (read first, every time)',
|
|
220
|
+
'',
|
|
221
|
+
'## 0. CONVERSATION FLOW — follow this in order',
|
|
222
|
+
'',
|
|
223
|
+
'Before you EVER call the CLI, walk the user through these steps. Ask in batches of 2-3 questions at a time, never one-by-one, never silently default. Use the user\'s answers to populate the CLI args.',
|
|
224
|
+
'',
|
|
225
|
+
'### Step 1 — What\'s the video about?',
|
|
226
|
+
'',
|
|
227
|
+
'This is the FIRST question, every time. Even if the user typed a full prompt with a script, confirm:',
|
|
228
|
+
'',
|
|
229
|
+
'> *"Got it — quick check before I generate:*',
|
|
230
|
+
'> *• What\'s the video about? (the topic / what they\'re selling / what\'s happening in the scene)*',
|
|
231
|
+
'> *• What\'s the exact line you want them to say? (the script — 1-3 sentences usually works best)"*',
|
|
232
|
+
'',
|
|
233
|
+
'Don\'t skip this. The "topic" informs the preset + vibe selection later. If the user already gave you a script, repeat it back so they can confirm or tweak it.',
|
|
234
|
+
'',
|
|
235
|
+
'### Step 2 — Who\'s the character?',
|
|
236
|
+
'',
|
|
237
|
+
'**agent-media generates the character image itself from your description. You DO NOT need to provide a photo.** A description like "25yo asian woman with long wavy dark hair" is enough — the pipeline produces a consistent on-model person.',
|
|
238
|
+
'',
|
|
239
|
+
'Three valid input paths (in order of preference):',
|
|
240
|
+
'',
|
|
241
|
+
'1. **Description only** (the default — use this 95% of the time)',
|
|
242
|
+
' Just `--description "..."`. agent-media generates the portrait and the multi-pose sheet, then runs the video. No photo, no upload, no questions about photos. Example:',
|
|
243
|
+
' ```bash',
|
|
244
|
+
' agent-media selfie --description "25yo asian woman, long wavy dark hair, soft smile" --script "..."',
|
|
245
|
+
' ```',
|
|
246
|
+
'',
|
|
247
|
+
'2. **Saved character** (`--character char_xxxxxxxxxx`)',
|
|
248
|
+
' Use when the user references a person from a previous run ("use the same girl again", "the one from yesterday\'s video"). Run `agent-media character` to list saved characters if you need to look one up.',
|
|
249
|
+
'',
|
|
250
|
+
'3. **Real-person photo + description** (`--photo <file|url> --description "..."`)',
|
|
251
|
+
' Use ONLY when the user explicitly says "use THIS person" and gives you a photo of someone specific. Otherwise default to path 1.',
|
|
252
|
+
'',
|
|
253
|
+
'Hard rules:',
|
|
254
|
+
'- NEVER ask the user "do you have a photo?". Default to description-only.',
|
|
255
|
+
'- NEVER ask the user about the underlying models (gpt-image-2, Seedance, etc.). They are implementation details. Just say "agent-media generates the character".',
|
|
256
|
+
'- NEVER fall back to `agent-media ugc` or a stock actor library. Forbidden.',
|
|
257
|
+
'- If the user wants the SAME person across multiple videos, run `agent-media character create --description "..."` once first (no photo needed) to get a `char_xxxxxxxxxx`, then pass `--character <id>` to every selfie.',
|
|
258
|
+
'',
|
|
259
|
+
'### Step 3 — Where + how does it feel? (preset + vibe)',
|
|
260
|
+
'',
|
|
261
|
+
'Pick one **preset** (location/shot grammar) + one **vibe** (emotional tone). If you can guess from the topic, propose them as defaults to confirm; if not, ask.',
|
|
262
|
+
'',
|
|
263
|
+
'**Presets (20, one of):**',
|
|
264
|
+
'',
|
|
265
|
+
'| Preset | Best for |',
|
|
266
|
+
'|---|---|',
|
|
267
|
+
'| `bedroom-morning-ritual` | Default. Skincare, routine, "morning vibes" content |',
|
|
268
|
+
'| `getting-ready-mirror-edge` | OOTD, makeup, fashion |',
|
|
269
|
+
'| `bathroom-skincare-routine` | Beauty, hair care, skincare reveals |',
|
|
270
|
+
'| `bedside-lamp-evening` | Wind-down, journal, ASMR-style |',
|
|
271
|
+
'| `kitchen-glow-up` | Food, drinks, supplements, cooking |',
|
|
272
|
+
'| `backyard-morning-coffee` | Lifestyle, mindfulness, slow content |',
|
|
273
|
+
'| `picnic-blanket-outdoor` | Outdoor, summer, friends |',
|
|
274
|
+
'| `car-quick-honest-review` | Honest reviews, "I just bought this..." |',
|
|
275
|
+
'| `car-passenger-honest` | Same vibe, passenger angle |',
|
|
276
|
+
'| `outdoor-walking-talking` | Walking-and-talking, candid |',
|
|
277
|
+
'| `couch-haul-show-off` | Unboxing, hauls, "look what I got" |',
|
|
278
|
+
'| `closet-fit-check` | Fashion, fit-checks, OOTD |',
|
|
279
|
+
'| `studio-apartment-tour` | Lifestyle, apartment content |',
|
|
280
|
+
'| `balcony-evening-vibes` | Aesthetic, lifestyle, golden hour |',
|
|
281
|
+
'| `desk-wfh-quick-pitch` | SaaS, productivity, work-from-home |',
|
|
282
|
+
'| `cafe-window-seat` | Lifestyle, work, coffee culture |',
|
|
283
|
+
'| `office-bathroom-discreet` | Workplace anecdotes, "let me tell you" |',
|
|
284
|
+
'| `gym-post-workout` | Fitness, supplements, wellness |',
|
|
285
|
+
'| `salon-mirror-result` | Hair, beauty reveals, "before/after" |',
|
|
286
|
+
'| `travel-hotel-room-review` | Travel, hotel reviews |',
|
|
287
|
+
'',
|
|
288
|
+
'**Vibes (5, one of):** `excited` · `calm` · `sassy` · `serious` · `curious`',
|
|
289
|
+
'',
|
|
290
|
+
'- `excited` — high energy, "you won\'t believe this", default for hype/product/reveal',
|
|
291
|
+
'- `calm` — softer, intimate, default for wellness/skincare/lifestyle',
|
|
292
|
+
'- `sassy` — playful, eye-roll, default for "let me tell you" anecdotes',
|
|
293
|
+
'- `serious` — measured, default for honest reviews / SaaS / B2B',
|
|
294
|
+
'- `curious` — thoughtful, leaning-in, default for storytelling',
|
|
295
|
+
'',
|
|
296
|
+
'### Step 4 — How long?',
|
|
297
|
+
'',
|
|
298
|
+
'**Allowed durations: 5, 10, or 15 seconds. ONLY these three.** Default to 10 if unsure. The schema rejects any other value (the worker will reject 6, 8, 12, etc.).',
|
|
299
|
+
'',
|
|
300
|
+
'- `5` — single hook, no body. Best for ads with strong intro.',
|
|
301
|
+
'- `10` — default. Hook + payoff. Best for organic UGC.',
|
|
302
|
+
'- `15` — full mini-story with setup + reveal.',
|
|
303
|
+
'',
|
|
304
|
+
'### Step 5 — Voice direction (optional, but improves quality)',
|
|
305
|
+
'',
|
|
306
|
+
'Ask: *"Any voice direction? E.g. \'whisper\', \'gen-z deadpan\', \'NYC accent\', \'sleepy morning voice\'. Skip if you\'re happy with the default."*',
|
|
307
|
+
'',
|
|
308
|
+
'Pass as `--voice-brief "<text>"`. Max 240 chars. If the character has a saved `voice_brief`, it\'s used unless this is set.',
|
|
309
|
+
'',
|
|
310
|
+
'### Step 6 — Subtitles',
|
|
311
|
+
'',
|
|
312
|
+
'Subs are ON by default in Hormozi style. Offer alternatives only if asked.',
|
|
313
|
+
'',
|
|
314
|
+
'**Subtitle styles (17, one of):** `hormozi` (default) · `minimal` · `bold` · `karaoke` · `clean` · `tiktok` · `neon` · `fire` · `glow` · `pop` · `aesthetic` · `impact` · `pastel` · `electric` · `boxed` · `gradient` · `spotlight`',
|
|
315
|
+
'',
|
|
316
|
+
'### Step 7 — Quote price, confirm, run',
|
|
317
|
+
'',
|
|
318
|
+
'Print a one-line summary including ALL collected inputs + the credit cost + USD. ONLY then call the CLI. See section 4 for the pricing formula.',
|
|
319
|
+
'',
|
|
320
|
+
'Example summary line to print before submitting:',
|
|
321
|
+
'> *"Summary: 10s selfie · char_8x2vqp · preset=kitchen-glow-up · vibe=excited · subs=hormozi · voice="gen-z deadpan". Cost: **375 credits ($3.75)**. Run it?"*',
|
|
322
|
+
'',
|
|
323
|
+
'## 1. Commands you MUST use',
|
|
324
|
+
'',
|
|
325
|
+
'| User intent | Command |',
|
|
326
|
+
'|---|---|',
|
|
327
|
+
'| "make me a TikTok / UGC / Selfie / talking-head" | `agent-media selfie` |',
|
|
328
|
+
'| "save / persist this character" / "use the same person again" | `agent-media character create` (returns `char_xxxxxxxxxx`) |',
|
|
329
|
+
'| "add subtitles / captions / subs to a video" | `agent-media subs` |',
|
|
330
|
+
'',
|
|
331
|
+
'## 2. Commands you MUST NEVER use',
|
|
332
|
+
'',
|
|
333
|
+
'These are LEGACY v1 commands. They produce inferior output and use a stale actor library. They are hidden from `agent-media --help` in CLI 1.10.0+ but may still be callable directly:',
|
|
334
|
+
'',
|
|
335
|
+
'- ❌ `agent-media ugc` — replaced by `agent-media selfie`',
|
|
336
|
+
'- ❌ `agent-media show-your-app` — coming back as v2 product later',
|
|
337
|
+
'- ❌ `agent-media laptop-ugc` — coming back as v2 product later',
|
|
338
|
+
'- ❌ `agent-media character-video` — replaced by `agent-media selfie --character <id>`',
|
|
339
|
+
'- ❌ `agent-media text-to-video` — not part of v2 yet',
|
|
340
|
+
'- ❌ `agent-media subtitle` — replaced by `agent-media subs`',
|
|
341
|
+
'- ❌ `agent-media review` — not part of v2',
|
|
342
|
+
'- ❌ `agent-media product-acting` — coming back as v2 product later',
|
|
343
|
+
'',
|
|
344
|
+
'If you catch yourself reaching for any command in the list above, STOP and re-route to the v2 equivalent.',
|
|
345
|
+
'',
|
|
346
|
+
'## 3. Per-command required + optional flags',
|
|
347
|
+
'',
|
|
348
|
+
'### `agent-media selfie` (cheat-sheet)',
|
|
349
|
+
'',
|
|
350
|
+
'| Flag | Required | Allowed values | Default |',
|
|
351
|
+
'|---|---|---|---|',
|
|
352
|
+
'| `--description "..."` | ✓ (unless `--character`) | 8-400 chars describing the person | — |',
|
|
353
|
+
'| `--character <char_id>` | OR | `char_xxxxxxxxxx` (saved character) | — |',
|
|
354
|
+
'| `--photo <file\\|url>` | optional | only when user gives an exact-person reference photo | — |',
|
|
355
|
+
'| `--script "..."` | ✓ | 4-600 chars | — |',
|
|
356
|
+
'| `--preset <name>` | | one of 20 (see Step 3) | `bedroom-morning-ritual` |',
|
|
357
|
+
'| `--vibe <name>` | | `excited\\|calm\\|sassy\\|serious\\|curious` | `excited` |',
|
|
358
|
+
'| `--duration <n>` | | **`5` \\| `10` \\| `15`** | `10` |',
|
|
359
|
+
'| `--voice-brief "..."` | | 4-240 chars | (none / character default) |',
|
|
360
|
+
'| `--subs-style <name>` | | one of 17 (see Step 6) | `hormozi` |',
|
|
361
|
+
'| `--no-subs` | | flag | (subs on) |',
|
|
362
|
+
'',
|
|
363
|
+
'### `agent-media character create`',
|
|
364
|
+
'',
|
|
365
|
+
'| Flag | Required | Notes |',
|
|
366
|
+
'|---|---|---|',
|
|
367
|
+
'| `--name <slug>` | ✓ | lowercase, hyphens, e.g. `sofia` |',
|
|
368
|
+
'| `--description "..."` | ✓ | free text — age, look, vibe. agent-media generates the portrait from this. |',
|
|
369
|
+
'| `--photo <file\\|url>` | optional | ONLY when the user wants an exact real-person likeness. Otherwise omit. |',
|
|
370
|
+
'| `--voice-brief "..."` | | default voice direction baked into character |',
|
|
371
|
+
'| `--preset-default <name>` | | preset to use when this character runs selfie |',
|
|
372
|
+
'',
|
|
373
|
+
'Returns `char_xxxxxxxxxx` — copy and reuse it.',
|
|
374
|
+
'',
|
|
375
|
+
'### `agent-media subs`',
|
|
376
|
+
'',
|
|
377
|
+
'| Flag | Required | Notes |',
|
|
378
|
+
'|---|---|---|',
|
|
379
|
+
'| `--video <url>` | ✓ | publicly-fetchable mp4 URL |',
|
|
380
|
+
'| `--style <name>` | | one of 17 (see Step 6). Default: `hormozi`. |',
|
|
381
|
+
'| `--transcript "..."` | | skip Whisper if you already have the exact words |',
|
|
382
|
+
'| `--language <code>` | | ISO code (`en`, `es`, `pt`, `fr`, …) |',
|
|
383
|
+
'',
|
|
384
|
+
'## 4. PRICING FORMULA — do not improvise',
|
|
385
|
+
'',
|
|
386
|
+
'**1 credit = $0.01 USD. Period.** Never quote a price without using this conversion.',
|
|
387
|
+
'',
|
|
388
|
+
'Selfie cost = `75 base + 30 × seconds`:',
|
|
389
|
+
'',
|
|
390
|
+
'| Duration | Credits | USD |',
|
|
391
|
+
'|---|---:|---:|',
|
|
392
|
+
'| 5s | 225 | **$2.25** |',
|
|
393
|
+
'| 10s | 375 | **$3.75** |',
|
|
394
|
+
'| 15s | 525 | **$5.25** |',
|
|
395
|
+
'',
|
|
396
|
+
'Character create = 27 credits = **$0.27** (one-shot).',
|
|
397
|
+
'Subtitle = `0 base + 3 × seconds` (a 10s clip = 30 credits = **$0.30**).',
|
|
398
|
+
'',
|
|
399
|
+
'If you find yourself about to print any USD value not in this table or derivable by `credits / 100`, STOP. Recompute.',
|
|
400
|
+
'',
|
|
401
|
+
'## 5. Output handling',
|
|
402
|
+
'',
|
|
403
|
+
'Every command returns a job id. Poll until terminal:',
|
|
404
|
+
'',
|
|
405
|
+
'```bash',
|
|
406
|
+
'agent-media status <job-id>',
|
|
407
|
+
'```',
|
|
408
|
+
'',
|
|
409
|
+
'When `status: "completed"`, the response carries `video_url`. Print the FULL url to the user verbatim — do not abbreviate.',
|
|
410
|
+
'',
|
|
411
|
+
'Sync mode (blocking): add `--sync` to selfie/subs and the CLI will poll for you and print the URL when done.',
|
|
412
|
+
'',
|
|
413
|
+
'### For `agent-media character create`',
|
|
414
|
+
'',
|
|
415
|
+
'Required: `--photo <file|url>`, `--name <slug>`, `--description "..."`.',
|
|
416
|
+
'Optional: `--voice-brief`, `--preset`.',
|
|
417
|
+
'',
|
|
418
|
+
'Cost: 27 credits ($0.27). Confirm.',
|
|
419
|
+
'',
|
|
420
|
+
'### For `agent-media subs`',
|
|
421
|
+
'',
|
|
422
|
+
'Required: `--video <url>`.',
|
|
423
|
+
'Optional: `--style` (default `hormozi`, one of 17), `--transcript` (skip Whisper), `--language`.',
|
|
424
|
+
'',
|
|
425
|
+
'Cost: ~24 credits / 8s clip. Confirm.',
|
|
426
|
+
'',
|
|
427
|
+
'## 4. Output handling',
|
|
428
|
+
'',
|
|
429
|
+
'Every command returns a job id. Poll until terminal:',
|
|
430
|
+
'',
|
|
431
|
+
'```bash',
|
|
432
|
+
'agent-media status <job-id>',
|
|
433
|
+
'```',
|
|
434
|
+
'',
|
|
435
|
+
'When status is `completed`, the final mp4 URL is printed. Show it to the user. Do not summarize or shorten it.',
|
|
436
|
+
'',
|
|
437
|
+
'---',
|
|
438
|
+
'',
|
|
219
439
|
'# agent-media v2 — Selfie + Characters',
|
|
220
440
|
'',
|
|
221
441
|
'The v2 surface ships two generators today: **Selfie** (a 9:16 TikTok-style video of an AI person talking to camera) and **character_create** (persist an AI character so subsequent Selfies stay on-model). When the next v2 product lands (Product-in-hands), it appears here automatically — this file is generated from `packages/schema/src/v2/generators.ts`.',
|
package/src/v2/character.ts
CHANGED
|
@@ -15,8 +15,10 @@ import { z } from 'zod';
|
|
|
15
15
|
import { V2_SHOT_PRESETS } from './selfie.js';
|
|
16
16
|
|
|
17
17
|
export const CharacterCreateSchema = z.object({
|
|
18
|
-
//
|
|
19
|
-
|
|
18
|
+
// Optional source photo — if absent, agent-media generates the
|
|
19
|
+
// portrait from `description` alone. Pass a real person's photo
|
|
20
|
+
// ONLY when you want that exact person's likeness.
|
|
21
|
+
photo_url: z.string().url().optional(),
|
|
20
22
|
|
|
21
23
|
// Required identity
|
|
22
24
|
display_name: z
|
package/src/v2/selfie.ts
CHANGED
|
@@ -49,7 +49,7 @@ export type V2ShotPreset = (typeof V2_SHOT_PRESETS)[number];
|
|
|
49
49
|
export const V2_VIBES = ['excited', 'calm', 'sassy', 'serious', 'curious'] as const;
|
|
50
50
|
export type V2Vibe = (typeof V2_VIBES)[number];
|
|
51
51
|
|
|
52
|
-
export const V2_DURATIONS = [5,
|
|
52
|
+
export const V2_DURATIONS = [5, 10, 15] as const;
|
|
53
53
|
export type V2Duration = (typeof V2_DURATIONS)[number];
|
|
54
54
|
|
|
55
55
|
// ── Input schema ──────────────────────────────────────────────────────────
|
|
@@ -70,8 +70,8 @@ export const SelfieSchema = z
|
|
|
70
70
|
preset: z.enum(V2_SHOT_PRESETS).default('bedroom-morning-ritual'),
|
|
71
71
|
vibe: z.enum(V2_VIBES).default('excited'),
|
|
72
72
|
duration: z
|
|
73
|
-
.union([z.literal(5), z.literal(
|
|
74
|
-
.default(
|
|
73
|
+
.union([z.literal(5), z.literal(10), z.literal(15)])
|
|
74
|
+
.default(10),
|
|
75
75
|
|
|
76
76
|
// Voice direction (one line, natural language). Pulled from the
|
|
77
77
|
// character record when character_id is used; user can still
|
|
@@ -83,19 +83,31 @@ export const SelfieSchema = z
|
|
|
83
83
|
})
|
|
84
84
|
.superRefine((val, ctx) => {
|
|
85
85
|
const hasSavedCharacter = !!val.character_id;
|
|
86
|
-
const
|
|
87
|
-
|
|
86
|
+
const hasDescription = !!val.description;
|
|
87
|
+
// Three valid input paths:
|
|
88
|
+
// 1. character_id alone (reuse saved character)
|
|
89
|
+
// 2. description alone (agent-media generates the portrait from text)
|
|
90
|
+
// 3. photo_url + description (use the user's photo as reference)
|
|
91
|
+
// Anything else is rejected.
|
|
92
|
+
if (!hasSavedCharacter && !hasDescription) {
|
|
88
93
|
ctx.addIssue({
|
|
89
94
|
code: z.ZodIssueCode.custom,
|
|
90
95
|
message:
|
|
91
|
-
'Provide either character_id, OR
|
|
96
|
+
'Provide either character_id, OR description (with optional photo_url for a real reference person).',
|
|
92
97
|
});
|
|
93
98
|
}
|
|
94
99
|
if (hasSavedCharacter && (val.photo_url || val.description)) {
|
|
95
100
|
ctx.addIssue({
|
|
96
101
|
code: z.ZodIssueCode.custom,
|
|
97
102
|
message:
|
|
98
|
-
'Use character_id OR (
|
|
103
|
+
'Use character_id OR description (+ optional photo_url) — not both.',
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
if (val.photo_url && !val.description) {
|
|
107
|
+
ctx.addIssue({
|
|
108
|
+
code: z.ZodIssueCode.custom,
|
|
109
|
+
message:
|
|
110
|
+
'photo_url requires a description so we know what to emphasize.',
|
|
99
111
|
});
|
|
100
112
|
}
|
|
101
113
|
});
|