@event4u/agent-config 2.23.0 → 2.24.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/.agent-src/commands/video/from-script.md +123 -0
- package/.agent-src/commands/video/scene.md +92 -0
- package/.agent-src/commands/video/stitch.md +83 -0
- package/.agent-src/commands/video/storyboard.md +95 -0
- package/.agent-src/commands/video.md +59 -0
- package/.agent-src/personas/README.md +3 -0
- package/.agent-src/personas/ai-video-technical-director.md +81 -0
- package/.agent-src/personas/hollywood-director.md +99 -0
- package/.agent-src/personas/pixar-storyboard-artist.md +98 -0
- package/.agent-src/skills/character-consistency/SKILL.md +120 -0
- package/.agent-src/skills/motion-choreographer/SKILL.md +149 -0
- package/.agent-src/skills/pixar-storyteller/SKILL.md +107 -0
- package/.agent-src/skills/scene-expander/SKILL.md +122 -0
- package/.agent-src/skills/scene-expander/scene-blueprint.schema.yaml +108 -0
- package/.agent-src/skills/subagent-orchestration/SKILL.md +17 -15
- package/.agent-src/skills/video-director/SKILL.md +113 -0
- package/.agent-src/templates/agent-settings.md +19 -0
- package/.agent-src/templates/agents/agent-project-settings.example.yml +1 -1
- package/.claude-plugin/marketplace.json +11 -1
- package/CHANGELOG.md +22 -0
- package/README.md +4 -4
- package/config/agent-settings.template.yml +28 -0
- package/docs/adrs/caveman/0001-default-off-until-bench.md +2 -2
- package/docs/adrs/cost/0001-hard-stop-hook.md +1 -1
- package/docs/adrs/smoke/0001-per-tier-smoke-scripts.md +2 -2
- package/docs/architecture.md +2 -2
- package/docs/catalog.md +14 -4
- package/docs/contracts/command-clusters.md +1 -0
- package/docs/contracts/compression-default-kill-criterion.md +1 -1
- package/docs/contracts/file-ownership-matrix.json +337 -0
- package/docs/getting-started.md +1 -1
- package/docs/parity/ruflo.md +3 -3
- package/package.json +1 -1
- package/scripts/ai-video/adapters/gemini-veo.sh +57 -0
- package/scripts/ai-video/adapters/higgsfield.sh +82 -0
- package/scripts/ai-video/adapters/kling.sh +54 -0
- package/scripts/ai-video/adapters/openai-images.sh +52 -0
- package/scripts/ai-video/adapters/sora.sh +54 -0
- package/scripts/ai-video/lib/adapter-common.sh +116 -0
- package/scripts/ai-video/lib/adapter-contract.md +163 -0
- package/scripts/ai-video/lib/fixtures/gemini-veo/result.json +1 -0
- package/scripts/ai-video/lib/fixtures/gemini-veo/scene-0001.mp4 +1 -0
- package/scripts/ai-video/lib/fixtures/higgsfield/result.json +1 -0
- package/scripts/ai-video/lib/fixtures/higgsfield/scene-0001.mp4 +1 -0
- package/scripts/ai-video/lib/fixtures/kling/result.json +1 -0
- package/scripts/ai-video/lib/fixtures/kling/scene-0001.mp4 +1 -0
- package/scripts/ai-video/lib/fixtures/openai-images/result.json +1 -0
- package/scripts/ai-video/lib/fixtures/openai-images/scene-0001.png +3 -0
- package/scripts/ai-video/lib/fixtures/sora/result.json +1 -0
- package/scripts/ai-video/lib/fixtures/sora/scene-0001.mp4 +1 -0
- package/scripts/ai-video/lib/load-config.sh +140 -0
- package/scripts/ai-video/lib/operator-pick.sh +119 -0
- package/scripts/ai-video/lib/parse-blueprint.sh +122 -0
- package/scripts/ai-video/lib/redact.sh +85 -0
- package/scripts/ai-video/lib/validate-deps.sh +132 -0
- package/scripts/ai-video/stitch.sh +154 -0
- package/scripts/ai-video/test-pipeline.sh +169 -0
- package/scripts/schemas/command.schema.json +8 -0
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# kling.sh — Kuaishou Kling motion-tuned video generation adapter.
|
|
3
|
+
#
|
|
4
|
+
# Capability: audio=none by default (Kling current generation does not
|
|
5
|
+
# emit native audio); the orchestrator muxes operator-supplied dialogue
|
|
6
|
+
# / ambient at stitch time. Max duration is model-dependent; clip-time
|
|
7
|
+
# clamping happens in motion-choreographer (it tunes the prompt to fit).
|
|
8
|
+
#
|
|
9
|
+
# Contract: scripts/ai-video/lib/adapter-contract.md
|
|
10
|
+
# Provider: top-level <provider id="kling" kind="video"> in
|
|
11
|
+
# agents/.ai-video.xml.
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
# shellcheck source=../lib/adapter-common.sh
|
|
16
|
+
. "$(dirname "$0")/../lib/adapter-common.sh"
|
|
17
|
+
|
|
18
|
+
ADAPTER_ID="kling"
|
|
19
|
+
KLING_MAX_DURATION="${KLING_MAX_DURATION:-10}"
|
|
20
|
+
|
|
21
|
+
aiv_cmd_submit() {
|
|
22
|
+
aiv_assert_dryrun
|
|
23
|
+
aiv_require_cmd curl jq
|
|
24
|
+
aiv_load_provider "${ADAPTER_ID}"
|
|
25
|
+
[ "$(aiv_key_status)" = "present" ] \
|
|
26
|
+
|| aiv_die 6 "${ADAPTER_ID}: api key missing in agents/.ai-video.xml"
|
|
27
|
+
|
|
28
|
+
local stdin_json duration
|
|
29
|
+
stdin_json="$(cat)"
|
|
30
|
+
duration="$(printf '%s' "${stdin_json}" | jq -r '.duration // empty')"
|
|
31
|
+
if [ -n "${duration}" ]; then
|
|
32
|
+
awk -v d="${duration}" -v m="${KLING_MAX_DURATION}" \
|
|
33
|
+
'BEGIN { if (d+0 > m+0) exit 1; exit 0 }' \
|
|
34
|
+
|| aiv_die 3 "${ADAPTER_ID}: duration ${duration}s exceeds model max ${KLING_MAX_DURATION}s"
|
|
35
|
+
fi
|
|
36
|
+
|
|
37
|
+
aiv_die 9 "${ADAPTER_ID}: live submit not yet wired (max duration ${KLING_MAX_DURATION}s honored)"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
aiv_cmd_poll() {
|
|
41
|
+
local job_id="${1:-}"
|
|
42
|
+
[ -n "${job_id}" ] || aiv_die 2 "${ADAPTER_ID}: poll <job_id> required"
|
|
43
|
+
aiv_assert_dryrun
|
|
44
|
+
aiv_die 9 "${ADAPTER_ID}: live poll not yet wired (job=${job_id})"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
aiv_cmd_fetch() {
|
|
48
|
+
local job_id="${1:-}"
|
|
49
|
+
[ -n "${job_id}" ] || aiv_die 2 "${ADAPTER_ID}: fetch <job_id> required"
|
|
50
|
+
aiv_assert_dryrun
|
|
51
|
+
aiv_die 9 "${ADAPTER_ID}: live fetch not yet wired (job=${job_id})"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
aiv_dispatch "${ADAPTER_ID}" "none" "$@"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# openai-images.sh — image generation adapter (DALL-E / gpt-image-1).
|
|
3
|
+
#
|
|
4
|
+
# Capability: audio=none (still images only — motion handled by a
|
|
5
|
+
# downstream video adapter). Ref-image and seed reuse are passed
|
|
6
|
+
# through verbatim so character-consistency can lock a face across
|
|
7
|
+
# scenes by reusing the same seed.
|
|
8
|
+
#
|
|
9
|
+
# Contract: scripts/ai-video/lib/adapter-contract.md
|
|
10
|
+
# Provider: top-level <provider id="openai-images" kind="image"> in
|
|
11
|
+
# agents/.ai-video.xml.
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
# shellcheck source=../lib/adapter-common.sh
|
|
16
|
+
. "$(dirname "$0")/../lib/adapter-common.sh"
|
|
17
|
+
|
|
18
|
+
ADAPTER_ID="openai-images"
|
|
19
|
+
|
|
20
|
+
aiv_cmd_run() {
|
|
21
|
+
aiv_assert_dryrun
|
|
22
|
+
aiv_require_cmd curl jq
|
|
23
|
+
aiv_load_provider "${ADAPTER_ID}"
|
|
24
|
+
[ "$(aiv_key_status)" = "present" ] \
|
|
25
|
+
|| aiv_die 6 "${ADAPTER_ID}: api key missing in agents/.ai-video.xml"
|
|
26
|
+
|
|
27
|
+
# Read contract stdin JSON; compose a single text prompt from the
|
|
28
|
+
# eight prose blocks. Ref-image + seed are passed verbatim.
|
|
29
|
+
local stdin_json prompt seed ref_first
|
|
30
|
+
stdin_json="$(cat)"
|
|
31
|
+
prompt="$(printf '%s' "${stdin_json}" | jq -r '
|
|
32
|
+
[.prompt.style, .prompt.subject, .prompt.environment, .prompt.action,
|
|
33
|
+
.prompt.camera, .prompt.lens, .prompt.lighting, .prompt.mood]
|
|
34
|
+
| map(select(. != null and . != ""))
|
|
35
|
+
| join(". ")
|
|
36
|
+
')"
|
|
37
|
+
seed="$(printf '%s' "${stdin_json}" | jq -r '.seed // empty')"
|
|
38
|
+
ref_first="$(printf '%s' "${stdin_json}" | jq -r '.ref_images[0] // empty')"
|
|
39
|
+
|
|
40
|
+
# Live mode is implementation-scaffolded: we do NOT yet POST to the
|
|
41
|
+
# OpenAI Images API from inside this adapter. The cost-floor gate
|
|
42
|
+
# (Phase 5 Step 6) refuses live mode unless the operator explicitly
|
|
43
|
+
# confirms in-turn; this branch surfaces a clear stub error so the
|
|
44
|
+
# contract surface stays stable while the live path is wired later.
|
|
45
|
+
aiv_die 9 "${ADAPTER_ID}: live mode not yet wired (prompt=${#prompt} chars, seed=${seed:-unset}, ref=${ref_first:-none})"
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
aiv_cmd_submit() { aiv_cmd_run "$@"; }
|
|
49
|
+
aiv_cmd_fetch() { aiv_cmd_run "$@"; }
|
|
50
|
+
aiv_cmd_poll() { printf '{"status":"done"}\n'; }
|
|
51
|
+
|
|
52
|
+
aiv_dispatch "${ADAPTER_ID}" "none" "$@"
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# sora.sh — OpenAI Sora structural-prompt video adapter.
|
|
3
|
+
#
|
|
4
|
+
# Capability: audio=native. Sora-class models produce muxed MP4 with
|
|
5
|
+
# dialogue + ambient sound; we pass the audio block straight through.
|
|
6
|
+
# Structural-prompt path informed by upstream `awesome-sora-prompts`
|
|
7
|
+
# (attribution in agents/ai-video/prompts/cinematic-blueprint.md).
|
|
8
|
+
#
|
|
9
|
+
# Contract: scripts/ai-video/lib/adapter-contract.md
|
|
10
|
+
# Provider: top-level <provider id="sora" kind="video"> in
|
|
11
|
+
# agents/.ai-video.xml.
|
|
12
|
+
|
|
13
|
+
set -euo pipefail
|
|
14
|
+
|
|
15
|
+
# shellcheck source=../lib/adapter-common.sh
|
|
16
|
+
. "$(dirname "$0")/../lib/adapter-common.sh"
|
|
17
|
+
|
|
18
|
+
ADAPTER_ID="sora"
|
|
19
|
+
|
|
20
|
+
aiv_cmd_submit() {
|
|
21
|
+
aiv_assert_dryrun
|
|
22
|
+
aiv_require_cmd curl jq
|
|
23
|
+
aiv_load_provider "${ADAPTER_ID}"
|
|
24
|
+
[ "$(aiv_key_status)" = "present" ] \
|
|
25
|
+
|| aiv_die 6 "${ADAPTER_ID}: api key missing in agents/.ai-video.xml"
|
|
26
|
+
|
|
27
|
+
local stdin_json
|
|
28
|
+
stdin_json="$(cat)"
|
|
29
|
+
# Sora's structural-prompt path expects all eight blocks present;
|
|
30
|
+
# fail loud if the blueprint parser dropped any.
|
|
31
|
+
printf '%s' "${stdin_json}" \
|
|
32
|
+
| jq -e '.prompt.style and .prompt.subject and .prompt.environment and
|
|
33
|
+
.prompt.action and .prompt.camera and .prompt.lens and
|
|
34
|
+
.prompt.lighting and .prompt.mood' >/dev/null \
|
|
35
|
+
|| aiv_die 3 "${ADAPTER_ID}: stdin JSON missing one or more required prompt blocks"
|
|
36
|
+
|
|
37
|
+
aiv_die 9 "${ADAPTER_ID}: live submit not yet wired"
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
aiv_cmd_poll() {
|
|
41
|
+
local job_id="${1:-}"
|
|
42
|
+
[ -n "${job_id}" ] || aiv_die 2 "${ADAPTER_ID}: poll <job_id> required"
|
|
43
|
+
aiv_assert_dryrun
|
|
44
|
+
aiv_die 9 "${ADAPTER_ID}: live poll not yet wired (job=${job_id})"
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
aiv_cmd_fetch() {
|
|
48
|
+
local job_id="${1:-}"
|
|
49
|
+
[ -n "${job_id}" ] || aiv_die 2 "${ADAPTER_ID}: fetch <job_id> required"
|
|
50
|
+
aiv_assert_dryrun
|
|
51
|
+
aiv_die 9 "${ADAPTER_ID}: live fetch not yet wired (job=${job_id})"
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
aiv_dispatch "${ADAPTER_ID}" "native" "$@"
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# adapter-common.sh — shared boilerplate sourced by every adapter under
|
|
3
|
+
# scripts/ai-video/adapters/. Keeps individual adapters thin and makes
|
|
4
|
+
# the contract surface (subcommand dispatch, dry-run plumbing, stdout
|
|
5
|
+
# shape) uniform across providers.
|
|
6
|
+
#
|
|
7
|
+
# Sourced; never executed directly. Strict-mode flags are the caller's
|
|
8
|
+
# responsibility (every adapter starts with `set -euo pipefail`).
|
|
9
|
+
|
|
10
|
+
if [ -n "${AIV_ADAPTER_COMMON_LOADED:-}" ]; then
|
|
11
|
+
return 0 2>/dev/null || exit 0
|
|
12
|
+
fi
|
|
13
|
+
AIV_ADAPTER_COMMON_LOADED=1
|
|
14
|
+
|
|
15
|
+
# Resolve the lib dir from this file's location so adapters can source
|
|
16
|
+
# us via the canonical `$(dirname "$0")/../lib/...` path.
|
|
17
|
+
_aiv_lib_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
|
18
|
+
# shellcheck source=/dev/null
|
|
19
|
+
. "${_aiv_lib_dir}/redact.sh"
|
|
20
|
+
# shellcheck source=/dev/null
|
|
21
|
+
. "${_aiv_lib_dir}/load-config.sh"
|
|
22
|
+
|
|
23
|
+
AIV_LIB_DIR="${_aiv_lib_dir}"
|
|
24
|
+
AIV_FIXTURE_ROOT="${_aiv_lib_dir}/fixtures"
|
|
25
|
+
export AIV_LIB_DIR AIV_FIXTURE_ROOT
|
|
26
|
+
|
|
27
|
+
# aiv_die <exit-code> <message> — write redacted message to stderr.
|
|
28
|
+
aiv_die() {
|
|
29
|
+
local code="${1:-1}"; shift || true
|
|
30
|
+
printf 'adapter: %s\n' "$*" | aiv_redact_stream >&2
|
|
31
|
+
exit "${code}"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
# aiv_require_cmd <cmd> [<cmd> ...] — fail fast on missing dependency.
|
|
35
|
+
aiv_require_cmd() {
|
|
36
|
+
local cmd
|
|
37
|
+
for cmd in "$@"; do
|
|
38
|
+
command -v "${cmd}" >/dev/null 2>&1 \
|
|
39
|
+
|| aiv_die 3 "required tool not found: ${cmd}"
|
|
40
|
+
done
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
# aiv_emit_dry_run <adapter-id> — print contract-shaped stdout JSON
|
|
44
|
+
# pointing at the adapter's fixture directory. Used by every adapter's
|
|
45
|
+
# `dry-run` subcommand.
|
|
46
|
+
aiv_emit_dry_run() {
|
|
47
|
+
local adapter="${1:-}"
|
|
48
|
+
[ -n "${adapter}" ] || aiv_die 3 "aiv_emit_dry_run: adapter id required"
|
|
49
|
+
local fixture_dir="${AIV_FIXTURE_ROOT}/${adapter}"
|
|
50
|
+
local result="${fixture_dir}/result.json"
|
|
51
|
+
if [ ! -f "${result}" ]; then
|
|
52
|
+
aiv_die 3 "fixture missing: ${result}"
|
|
53
|
+
fi
|
|
54
|
+
cat "${result}"
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# aiv_capability <flag> — print the capability JSON for `--help`-style
|
|
58
|
+
# discovery: `{"audio": "native|none|per-model"}`.
|
|
59
|
+
aiv_capability() {
|
|
60
|
+
local flag="${1:-none}"
|
|
61
|
+
printf '{"audio":"%s"}\n' "${flag}"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# aiv_assert_dryrun — refuse to run a network path unless the caller
|
|
65
|
+
# explicitly opted in (AIV_DRYRUN=false set in this turn).
|
|
66
|
+
aiv_assert_dryrun() {
|
|
67
|
+
case "${AIV_DRYRUN:-true}" in
|
|
68
|
+
false|FALSE|0|no|NO) return 0 ;;
|
|
69
|
+
*) aiv_die 4 "live call refused: AIV_DRYRUN=${AIV_DRYRUN:-true}; use dry-run subcommand or set AIV_DRYRUN=false" ;;
|
|
70
|
+
esac
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
# aiv_dispatch <adapter-id> <capability> "$@" — generic subcommand router.
|
|
74
|
+
# Adapters override individual subcommands by defining bash functions
|
|
75
|
+
# named `aiv_cmd_submit` / `aiv_cmd_poll` / `aiv_cmd_fetch` / `aiv_cmd_run`
|
|
76
|
+
# BEFORE calling aiv_dispatch. Anything not overridden falls through to
|
|
77
|
+
# a stub that exits non-zero with a clear message.
|
|
78
|
+
aiv_dispatch() {
|
|
79
|
+
local adapter="${1:-}"; shift || true
|
|
80
|
+
local cap="${1:-none}"; shift || true
|
|
81
|
+
local sub="${1:-}"; shift || true
|
|
82
|
+
case "${sub}" in
|
|
83
|
+
capability)
|
|
84
|
+
aiv_capability "${cap}"
|
|
85
|
+
;;
|
|
86
|
+
dry-run)
|
|
87
|
+
aiv_emit_dry_run "${adapter}"
|
|
88
|
+
;;
|
|
89
|
+
submit)
|
|
90
|
+
declare -F aiv_cmd_submit >/dev/null \
|
|
91
|
+
&& aiv_cmd_submit "$@" \
|
|
92
|
+
|| aiv_die 5 "${adapter}: submit not implemented"
|
|
93
|
+
;;
|
|
94
|
+
poll)
|
|
95
|
+
declare -F aiv_cmd_poll >/dev/null \
|
|
96
|
+
&& aiv_cmd_poll "$@" \
|
|
97
|
+
|| aiv_die 5 "${adapter}: poll not implemented"
|
|
98
|
+
;;
|
|
99
|
+
fetch)
|
|
100
|
+
declare -F aiv_cmd_fetch >/dev/null \
|
|
101
|
+
&& aiv_cmd_fetch "$@" \
|
|
102
|
+
|| aiv_die 5 "${adapter}: fetch not implemented"
|
|
103
|
+
;;
|
|
104
|
+
run)
|
|
105
|
+
declare -F aiv_cmd_run >/dev/null \
|
|
106
|
+
&& aiv_cmd_run "$@" \
|
|
107
|
+
|| aiv_die 5 "${adapter}: run not implemented"
|
|
108
|
+
;;
|
|
109
|
+
"")
|
|
110
|
+
aiv_die 2 "${adapter}: subcommand required (capability|dry-run|submit|poll|fetch|run)"
|
|
111
|
+
;;
|
|
112
|
+
*)
|
|
113
|
+
aiv_die 2 "${adapter}: unknown subcommand '${sub}'"
|
|
114
|
+
;;
|
|
115
|
+
esac
|
|
116
|
+
}
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
# Adapter Contract (v1)
|
|
2
|
+
|
|
3
|
+
> Single source of truth for every adapter under
|
|
4
|
+
> `scripts/ai-video/adapters/`. Phase 4 Step 1 finalizes any open
|
|
5
|
+
> point in this draft; **breaking changes bump the version pin
|
|
6
|
+
> and require every adapter to be rerun against the fixture set**
|
|
7
|
+
> (`scripts/ai-video/lib/fixtures/`).
|
|
8
|
+
|
|
9
|
+
## Scope
|
|
10
|
+
|
|
11
|
+
Every backend (OpenAI Images, Gemini Veo, Kling, Higgsfield, Sora,
|
|
12
|
+
…) ships as one executable shell entry under
|
|
13
|
+
`scripts/ai-video/adapters/<id>.sh`. The orchestrator never speaks
|
|
14
|
+
to a network API directly — it speaks to one of these scripts.
|
|
15
|
+
|
|
16
|
+
## Capability flag
|
|
17
|
+
|
|
18
|
+
Each adapter declares its audio capability via a top-of-file
|
|
19
|
+
comment and via the `capability` subcommand:
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
# capability: audio=native | audio=none
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
`audio=native` — the provider returns a muxed MP4 with synchronized
|
|
26
|
+
dialogue / ambient. `audio=none` — video-only output; the
|
|
27
|
+
orchestrator muxes the operator-supplied track at stitch time.
|
|
28
|
+
|
|
29
|
+
A per-model adapter (e.g. Higgsfield) MAY declare `audio=per-model`
|
|
30
|
+
and surface the capability via `capability --model <id>`.
|
|
31
|
+
|
|
32
|
+
## Subcommands
|
|
33
|
+
|
|
34
|
+
Every adapter implements four:
|
|
35
|
+
|
|
36
|
+
| Subcommand | Purpose | I/O |
|
|
37
|
+
|---|---|---|
|
|
38
|
+
| `submit` | Submit a render job to the backend | stdin JSON · stdout `{job_id}` |
|
|
39
|
+
| `poll <job_id>` | Poll job status | stdout `{status: queued\|running\|done\|failed, progress?}` |
|
|
40
|
+
| `fetch <job_id>` | Download artifacts after `status=done` | stdout `{video_path, audio_path?, audio_embedded}` |
|
|
41
|
+
| `dry-run` | Return a deterministic fixture path without network | stdout same shape as `fetch` |
|
|
42
|
+
|
|
43
|
+
`submit` + `poll` + `fetch` may be collapsed into a single
|
|
44
|
+
`run` for synchronous backends (OpenAI Images) — the wrapper exposes
|
|
45
|
+
the same stdout shape on stdout and an exit code on completion.
|
|
46
|
+
|
|
47
|
+
## Stdin JSON (consumed by `submit` / `run`)
|
|
48
|
+
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"prompt": {
|
|
52
|
+
"style": "...",
|
|
53
|
+
"subject": "...",
|
|
54
|
+
"environment": "...",
|
|
55
|
+
"action": "...",
|
|
56
|
+
"camera": "...",
|
|
57
|
+
"lens": "...",
|
|
58
|
+
"lighting": "...",
|
|
59
|
+
"mood": "..."
|
|
60
|
+
},
|
|
61
|
+
"ref_images": ["/abs/path/frame.png", "..."],
|
|
62
|
+
"duration": 4.5,
|
|
63
|
+
"aspect": "16:9",
|
|
64
|
+
"seed": 1234567,
|
|
65
|
+
"audio": {
|
|
66
|
+
"dialogue": ["speaker: \"line\"", "..."],
|
|
67
|
+
"ambient": ["rain on metal", "..."],
|
|
68
|
+
"language": "en",
|
|
69
|
+
"enable_native_audio": true
|
|
70
|
+
},
|
|
71
|
+
"negative": ["centered framing", "..."]
|
|
72
|
+
}
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
`ref_images`, `duration`, `aspect`, `seed`, `audio`, `negative` are
|
|
76
|
+
optional. `prompt.*` blocks are mandatory. Unknown top-level keys
|
|
77
|
+
are logged to stderr and ignored.
|
|
78
|
+
|
|
79
|
+
## Stdout JSON (emitted by `fetch` / `run` / `dry-run`)
|
|
80
|
+
|
|
81
|
+
```json
|
|
82
|
+
{
|
|
83
|
+
"video_path": "/abs/path/scene-0001.mp4",
|
|
84
|
+
"audio_path": "/abs/path/scene-0001.wav",
|
|
85
|
+
"audio_embedded": true
|
|
86
|
+
}
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
Semantics:
|
|
90
|
+
|
|
91
|
+
- `audio_embedded: true` — `video_path` is a muxed MP4 with audio;
|
|
92
|
+
`audio_path` is omitted or echoes the same path. Stitcher
|
|
93
|
+
pass-through.
|
|
94
|
+
- `audio_embedded: false` with `audio_path` — separate track to mux
|
|
95
|
+
at stitch time via `ffmpeg`.
|
|
96
|
+
- `audio_embedded: false` without `audio_path` — video-only;
|
|
97
|
+
operator supplies the audio bed at stitch time.
|
|
98
|
+
|
|
99
|
+
## Error contract
|
|
100
|
+
|
|
101
|
+
Adapter failure: **non-zero exit code** AND a JSON file at
|
|
102
|
+
`<project>/scenes/<scene_id>/error.json`:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"adapter": "gemini-veo",
|
|
107
|
+
"subcommand": "fetch",
|
|
108
|
+
"job_id": "...",
|
|
109
|
+
"exit_code": 7,
|
|
110
|
+
"stderr_tail": "...",
|
|
111
|
+
"retryable": true,
|
|
112
|
+
"user_action": "regenerate-prompt | retry | skip | abort"
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
`retryable: false` means a prompt or input change is required —
|
|
117
|
+
the orchestrator MUST NOT auto-retry. `retryable: true` does **not**
|
|
118
|
+
authorize auto-retry either; the orchestrator surfaces a single
|
|
119
|
+
numbered-options block (retry · regenerate prompt · skip · abort)
|
|
120
|
+
per `non-destructive-by-default` and waits.
|
|
121
|
+
|
|
122
|
+
## Dry-run
|
|
123
|
+
|
|
124
|
+
`AIV_DRYRUN=true` (set by command default; see Phase 5 Step 6) OR
|
|
125
|
+
the explicit `dry-run` subcommand:
|
|
126
|
+
|
|
127
|
+
- No network call.
|
|
128
|
+
- Stdout JSON points at a deterministic fixture under
|
|
129
|
+
`scripts/ai-video/lib/fixtures/<adapter-id>/`.
|
|
130
|
+
- Exit code 0.
|
|
131
|
+
|
|
132
|
+
Fixtures are committed and cover one happy path per adapter.
|
|
133
|
+
Phase 6 golden runs assert byte-identical stdout under
|
|
134
|
+
`AIV_DRYRUN=true`.
|
|
135
|
+
|
|
136
|
+
## Logging & redaction
|
|
137
|
+
|
|
138
|
+
- **stderr only** for log output. Stdout is reserved for the
|
|
139
|
+
contract JSON.
|
|
140
|
+
- Every adapter MUST source `scripts/ai-video/lib/redact.sh` and
|
|
141
|
+
pipe network responses through `aiv_redact_stream` before any
|
|
142
|
+
`>&2` write. API keys, bearer tokens, and operator-registered
|
|
143
|
+
secrets are masked.
|
|
144
|
+
|
|
145
|
+
## Strict-mode shell
|
|
146
|
+
|
|
147
|
+
Every adapter:
|
|
148
|
+
|
|
149
|
+
```bash
|
|
150
|
+
#!/usr/bin/env bash
|
|
151
|
+
set -euo pipefail
|
|
152
|
+
. "$(dirname "$0")/../lib/redact.sh"
|
|
153
|
+
. "$(dirname "$0")/../lib/load-config.sh"
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
`shellcheck` clean (Phase 4 Step 9).
|
|
157
|
+
|
|
158
|
+
## Versioning
|
|
159
|
+
|
|
160
|
+
This contract is `v1`. Backward-incompatible changes (renamed
|
|
161
|
+
field, removed subcommand, changed stdout shape) bump to `v2` and
|
|
162
|
+
require an `adapter-contract` migration note plus a rerun of every
|
|
163
|
+
adapter against the fixture set.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"video_path":"scripts/ai-video/lib/fixtures/gemini-veo/scene-0001.mp4","audio_embedded":true}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
FIXTURE-gemini-veo-mp4
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"video_path":"scripts/ai-video/lib/fixtures/higgsfield/scene-0001.mp4","audio_embedded":false}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
FIXTURE-higgsfield-mp4
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"video_path":"scripts/ai-video/lib/fixtures/kling/scene-0001.mp4","audio_embedded":false}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
FIXTURE-kling-mp4
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"video_path":"scripts/ai-video/lib/fixtures/openai-images/scene-0001.png","audio_embedded":false}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"video_path":"scripts/ai-video/lib/fixtures/sora/scene-0001.mp4","audio_embedded":true}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
FIXTURE-sora-mp4
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# scripts/ai-video/lib/load-config.sh — provider config loader for /video:*.
|
|
3
|
+
#
|
|
4
|
+
# Parses agents/.ai-video.xml with xmllint --xpath and surfaces a single
|
|
5
|
+
# provider's settings as AIV_KEY / AIV_ENDPOINT / AIV_MODEL / AIV_DRYRUN
|
|
6
|
+
# env vars to its caller. The key is NEVER echoed — status output is only
|
|
7
|
+
# `present` or `missing`.
|
|
8
|
+
#
|
|
9
|
+
# Usage (sourced):
|
|
10
|
+
#
|
|
11
|
+
# . scripts/ai-video/lib/load-config.sh
|
|
12
|
+
# aiv_load_provider gemini-veo
|
|
13
|
+
# echo "key: $(aiv_key_status)" # → present | missing
|
|
14
|
+
# echo "endpoint: $AIV_ENDPOINT"
|
|
15
|
+
# echo "dryrun: $AIV_DRYRUN"
|
|
16
|
+
#
|
|
17
|
+
# Usage (CLI status check, no key echoed):
|
|
18
|
+
#
|
|
19
|
+
# bash scripts/ai-video/lib/load-config.sh status gemini-veo
|
|
20
|
+
# → provider=gemini-veo key=present dryrun=true model=veo-3.0-generate-001
|
|
21
|
+
#
|
|
22
|
+
# Defaults:
|
|
23
|
+
# - AIV_DRYRUN defaults to `true` when not set in XML
|
|
24
|
+
# - AIV_DRYRUN can be overridden by the AIV_DRYRUN env var set by caller
|
|
25
|
+
# - missing file is non-fatal in `status` mode (prints all-missing line)
|
|
26
|
+
|
|
27
|
+
set -u
|
|
28
|
+
|
|
29
|
+
AIV_CONFIG_PATH="${AIV_CONFIG_PATH:-agents/.ai-video.xml}"
|
|
30
|
+
|
|
31
|
+
_aiv_xpath() {
|
|
32
|
+
# $1 = xpath expression; reads from $AIV_CONFIG_PATH
|
|
33
|
+
# Returns text content or empty string. Never errors on missing path.
|
|
34
|
+
if [ ! -f "${AIV_CONFIG_PATH}" ]; then
|
|
35
|
+
printf ''
|
|
36
|
+
return 0
|
|
37
|
+
fi
|
|
38
|
+
if ! command -v xmllint >/dev/null 2>&1; then
|
|
39
|
+
printf ''
|
|
40
|
+
return 0
|
|
41
|
+
fi
|
|
42
|
+
xmllint --xpath "string(${1})" "${AIV_CONFIG_PATH}" 2>/dev/null || printf ''
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
aiv_default_image_provider() {
|
|
46
|
+
_aiv_xpath '/ai-video/default-image-provider'
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
aiv_default_video_provider() {
|
|
50
|
+
_aiv_xpath '/ai-video/default-video-provider'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
aiv_load_provider() {
|
|
54
|
+
local pid="${1:-}"
|
|
55
|
+
if [ -z "${pid}" ]; then
|
|
56
|
+
echo "aiv_load_provider: provider id required" >&2
|
|
57
|
+
return 2
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
# Resolve from the top-level <provider> blocks OR the <extra> slot.
|
|
61
|
+
local base="(/ai-video/provider[@id='${pid}']|/ai-video/extra/provider[@id='${pid}'])"
|
|
62
|
+
|
|
63
|
+
AIV_PROVIDER_ID="${pid}"
|
|
64
|
+
AIV_KEY="$(_aiv_xpath "${base}/api-key")"
|
|
65
|
+
AIV_ENDPOINT="$(_aiv_xpath "${base}/endpoint")"
|
|
66
|
+
AIV_MODEL="$(_aiv_xpath "${base}/default-model")"
|
|
67
|
+
AIV_KIND="$(_aiv_xpath "${base}/@kind")"
|
|
68
|
+
|
|
69
|
+
local dryrun_xml
|
|
70
|
+
dryrun_xml="$(_aiv_xpath "${base}/dry-run")"
|
|
71
|
+
# Caller-set AIV_DRYRUN takes precedence; otherwise XML; otherwise true.
|
|
72
|
+
if [ -n "${AIV_DRYRUN:-}" ]; then
|
|
73
|
+
: # respect caller
|
|
74
|
+
elif [ -n "${dryrun_xml}" ]; then
|
|
75
|
+
AIV_DRYRUN="${dryrun_xml}"
|
|
76
|
+
else
|
|
77
|
+
AIV_DRYRUN="true"
|
|
78
|
+
fi
|
|
79
|
+
export AIV_DRYRUN
|
|
80
|
+
|
|
81
|
+
# Tuning fields — adapters read these directly when present.
|
|
82
|
+
AIV_TUNING_ASPECT="$(_aiv_xpath "${base}/tuning/aspect")"
|
|
83
|
+
AIV_TUNING_FPS="$(_aiv_xpath "${base}/tuning/fps")"
|
|
84
|
+
AIV_TUNING_MAX_DURATION="$(_aiv_xpath "${base}/tuning/max-duration")"
|
|
85
|
+
AIV_TUNING_AUDIO_NATIVE="$(_aiv_xpath "${base}/tuning/audio-native")"
|
|
86
|
+
AIV_TUNING_PRESET="$(_aiv_xpath "${base}/tuning/preset")"
|
|
87
|
+
AIV_TUNING_QUALITY="$(_aiv_xpath "${base}/tuning/quality")"
|
|
88
|
+
AIV_TUNING_BEST_OF_N="$(_aiv_xpath "${base}/tuning/best-of-n")"
|
|
89
|
+
export AIV_TUNING_ASPECT AIV_TUNING_FPS AIV_TUNING_MAX_DURATION \
|
|
90
|
+
AIV_TUNING_AUDIO_NATIVE AIV_TUNING_PRESET AIV_TUNING_QUALITY \
|
|
91
|
+
AIV_TUNING_BEST_OF_N
|
|
92
|
+
|
|
93
|
+
# Register key with redact.sh if loaded — adapters always source both.
|
|
94
|
+
if command -v aiv_redact_register >/dev/null 2>&1; then
|
|
95
|
+
aiv_redact_register "${AIV_KEY}"
|
|
96
|
+
fi
|
|
97
|
+
|
|
98
|
+
return 0
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
aiv_key_status() {
|
|
102
|
+
case "${AIV_KEY:-}" in
|
|
103
|
+
""|"REPLACE-ME"|*"-REPLACE-ME") printf 'missing' ;;
|
|
104
|
+
*) printf 'present' ;;
|
|
105
|
+
esac
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
# CLI mode — only `status <provider-id>` is supported; never echoes the key.
|
|
109
|
+
if [ "${BASH_SOURCE[0]:-}" = "${0}" ]; then
|
|
110
|
+
cmd="${1:-}"
|
|
111
|
+
case "${cmd}" in
|
|
112
|
+
status)
|
|
113
|
+
pid="${2:-}"
|
|
114
|
+
if [ -z "${pid}" ]; then
|
|
115
|
+
echo "usage: load-config.sh status <provider-id>" >&2
|
|
116
|
+
exit 2
|
|
117
|
+
fi
|
|
118
|
+
aiv_load_provider "${pid}" >/dev/null
|
|
119
|
+
printf 'provider=%s key=%s dryrun=%s model=%s endpoint=%s kind=%s\n' \
|
|
120
|
+
"${AIV_PROVIDER_ID}" \
|
|
121
|
+
"$(aiv_key_status)" \
|
|
122
|
+
"${AIV_DRYRUN:-true}" \
|
|
123
|
+
"${AIV_MODEL:-}" \
|
|
124
|
+
"${AIV_ENDPOINT:-}" \
|
|
125
|
+
"${AIV_KIND:-}"
|
|
126
|
+
;;
|
|
127
|
+
defaults)
|
|
128
|
+
printf 'default-image-provider=%s\n' "$(aiv_default_image_provider)"
|
|
129
|
+
printf 'default-video-provider=%s\n' "$(aiv_default_video_provider)"
|
|
130
|
+
;;
|
|
131
|
+
"")
|
|
132
|
+
echo "usage: load-config.sh {status <id> | defaults}" >&2
|
|
133
|
+
exit 2
|
|
134
|
+
;;
|
|
135
|
+
*)
|
|
136
|
+
echo "load-config.sh: unknown subcommand '${cmd}'" >&2
|
|
137
|
+
exit 2
|
|
138
|
+
;;
|
|
139
|
+
esac
|
|
140
|
+
fi
|