@letta-ai/letta-code 0.34.26 → 0.34.27
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/cursor-worker.js +4 -3
- package/dist/agent-presets.js +5 -5
- package/dist/agent-presets.js.map +1 -1
- package/dist/mcp-client.js +2 -2
- package/dist/mcp-client.js.map +1 -1
- package/dist/mcp-oauth.js +26 -16
- package/dist/mcp-oauth.js.map +6 -5
- package/dist/memory-confinement.js +109 -51
- package/dist/memory-confinement.js.map +9 -8
- package/dist/types/agent/mcp-originator-authority-file.d.ts +1 -0
- package/dist/types/agent/mcp-originator-authority-file.d.ts.map +1 -1
- package/dist/types/agent/memory-filesystem.d.ts +3 -3
- package/dist/types/agent/memory-filesystem.d.ts.map +1 -1
- package/dist/types/agent/memory-git.d.ts +2 -2
- package/dist/types/agent/memory-git.d.ts.map +1 -1
- package/dist/types/agent/skills.d.ts +3 -3
- package/dist/types/agent/skills.d.ts.map +1 -1
- package/dist/types/agent/subagents/index.d.ts +4 -0
- package/dist/types/agent/subagents/index.d.ts.map +1 -1
- package/dist/types/agent/subagents/subagent-launcher.d.ts.map +1 -1
- package/dist/types/backend/api/schedules.d.ts +16 -2
- package/dist/types/backend/api/schedules.d.ts.map +1 -1
- package/dist/types/channels/types.d.ts +1 -1
- package/dist/types/cli/helpers/chunk-log.d.ts.map +1 -1
- package/dist/types/cron/cron-file.d.ts.map +1 -1
- package/dist/types/hooks/loader.d.ts.map +1 -1
- package/dist/types/mods/paths.d.ts +3 -3
- package/dist/types/mods/paths.d.ts.map +1 -1
- package/dist/types/permissions/agent-memory-prefixes.d.ts.map +1 -1
- package/dist/types/permissions/analyzer.d.ts +1 -0
- package/dist/types/permissions/analyzer.d.ts.map +1 -1
- package/dist/types/permissions/cross-agent-guard.d.ts.map +1 -1
- package/dist/types/permissions/loader.d.ts +2 -1
- package/dist/types/permissions/loader.d.ts.map +1 -1
- package/dist/types/permissions/sandbox-policy.d.ts +9 -7
- package/dist/types/permissions/sandbox-policy.d.ts.map +1 -1
- package/dist/types/sandbox/bwrap.d.ts +2 -2
- package/dist/types/sandbox/bwrap.d.ts.map +1 -1
- package/dist/types/sandbox/policy.d.ts +20 -3
- package/dist/types/sandbox/policy.d.ts.map +1 -1
- package/dist/types/sandbox/seatbelt.d.ts +1 -1
- package/dist/types/sandbox/seatbelt.d.ts.map +1 -1
- package/dist/types/settings-manager.d.ts +4 -12
- package/dist/types/settings-manager.d.ts.map +1 -1
- package/dist/types/tools/commit-trailer.d.ts +27 -0
- package/dist/types/tools/commit-trailer.d.ts.map +1 -0
- package/dist/types/tools/impl/codex-app-server.d.ts +2 -0
- package/dist/types/tools/impl/codex-app-server.d.ts.map +1 -1
- package/dist/types/tools/impl/cursor-session.d.ts +2 -0
- package/dist/types/tools/impl/cursor-session.d.ts.map +1 -1
- package/dist/types/tools/impl/cursor-worker.d.ts +4 -1
- package/dist/types/tools/impl/cursor-worker.d.ts.map +1 -1
- package/dist/types/tools/impl/overflow.d.ts +1 -1
- package/dist/types/tools/impl/overflow.d.ts.map +1 -1
- package/dist/types/tools/impl/ripgrep-manager.d.ts.map +1 -1
- package/dist/types/tools/impl/shell-env.d.ts.map +1 -1
- package/dist/types/tools/impl/wake.d.ts.map +1 -1
- package/dist/types/tools/script-tool-scope.d.ts.map +1 -1
- package/dist/types/tools/tool-definitions.d.ts.map +1 -1
- package/dist/types/utils/fs.d.ts +5 -1
- package/dist/types/utils/fs.d.ts.map +1 -1
- package/dist/types/utils/local-backend-paths.d.ts +1 -1
- package/dist/types/utils/local-backend-paths.d.ts.map +1 -1
- package/dist/types/utils/secrets.d.ts +1 -1
- package/dist/types/utils/secrets.d.ts.map +1 -1
- package/dist/types/utils/settings-path.d.ts +14 -0
- package/dist/types/utils/settings-path.d.ts.map +1 -0
- package/dist/types/utils/transcript-paths.d.ts +1 -1
- package/dist/types/websocket/listener/constants.d.ts +6 -0
- package/dist/types/websocket/listener/constants.d.ts.map +1 -1
- package/dist/types/websocket/listener/input-disposition-path.d.ts.map +1 -1
- package/dist/types/websocket/listener/interrupted-turn-storage.d.ts.map +1 -1
- package/dist/types/websocket/listener/org-credentials.d.ts.map +1 -1
- package/dist/types/websocket/listener/remote-settings.d.ts +2 -2
- package/dist/types/websocket/listener/remote-settings.d.ts.map +1 -1
- package/dist/types/websocket/listener/turn-finished-replay.d.ts +1 -0
- package/dist/types/websocket/listener/turn-finished-replay.d.ts.map +1 -1
- package/letta.js +2592 -1947
- package/npm-shrinkwrap.json +90 -86
- package/package.json +3 -2
- package/scripts/isolated-unit-tests.json +10 -0
- package/scripts/test-home-preload.ts +1 -0
- package/skills/acquiring-skills/SKILL.md +4 -4
- package/skills/creating-mods/SKILL.md +3 -3
- package/skills/creating-mods/references/analysis-mode.md +7 -5
- package/skills/creating-mods/references/architecture.md +2 -2
- package/skills/creating-mods/references/plan-mode.md +12 -11
- package/skills/creating-mods/references/providers.md +1 -1
- package/skills/creating-skills/scripts/init-skill.ts +4 -2
- package/skills/customizing-commands/SKILL.md +3 -3
- package/skills/customizing-statusline/SKILL.md +3 -3
- package/skills/customizing-statusline/references/api.md +3 -3
- package/skills/customizing-statusline/references/migration.md +2 -2
- package/skills/initializing-memory/SKILL.md +1 -1
- package/skills/self-configuration/SKILL.md +4 -4
- package/skills/self-configuration/scripts/add_permission.py +26 -5
- package/skills/self-configuration/scripts/show_config.py +17 -2
- package/skills/video-generation/SKILL.md +152 -0
- package/skills/workflow-authoring/SKILL.md +1 -1
|
@@ -10,10 +10,10 @@ Use this as the command-specific entrypoint for local mod slash commands. For br
|
|
|
10
10
|
Mod files live in:
|
|
11
11
|
|
|
12
12
|
```text
|
|
13
|
-
|
|
13
|
+
${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/
|
|
14
14
|
```
|
|
15
15
|
|
|
16
|
-
Use a focused file name, e.g.
|
|
16
|
+
Use a focused file name, e.g. `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/review.ts` or `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/commands.ts`.
|
|
17
17
|
|
|
18
18
|
## First decide whether a command is right
|
|
19
19
|
|
|
@@ -29,7 +29,7 @@ If the command is a reusable workflow like `/goal`, put the workflow instruction
|
|
|
29
29
|
|
|
30
30
|
## Workflow
|
|
31
31
|
|
|
32
|
-
1. Inspect
|
|
32
|
+
1. Inspect `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/` for related command files.
|
|
33
33
|
2. Preserve unrelated mod code; create a focused new file if merging is messy.
|
|
34
34
|
3. Register with `letta.commands.register()` and guard with `letta.capabilities.commands`.
|
|
35
35
|
4. Return the unregister function, or a disposer that calls it plus any timer/panel cleanup.
|
|
@@ -8,7 +8,7 @@ description: Creates, edits, and migrates Letta Code statusline mods. Use when h
|
|
|
8
8
|
Use this skill to create or update the global Letta Code statusline mod:
|
|
9
9
|
|
|
10
10
|
```text
|
|
11
|
-
|
|
11
|
+
${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/statusline.tsx
|
|
12
12
|
```
|
|
13
13
|
|
|
14
14
|
The statusline is a panel registered at `order: 0` — the primary line just below the input. It overrides the built-in `agent · model` line. Host UI can still temporarily preempt it for safety confirmations and transient hints.
|
|
@@ -26,14 +26,14 @@ The order-0 panel owns the whole primary row. It renders text (not React) and ow
|
|
|
26
26
|
|
|
27
27
|
## Workflow
|
|
28
28
|
|
|
29
|
-
1. Check whether
|
|
29
|
+
1. Check whether `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/statusline.tsx` exists.
|
|
30
30
|
2. If it exists, read it before editing and preserve unrelated code.
|
|
31
31
|
3. If it does not exist, synthesize a focused starter for the user's request.
|
|
32
32
|
4. If the user asks to migrate, import a `.sh` file, or match a shell prompt, read `references/migration.md`.
|
|
33
33
|
5. If API details or concrete patterns are needed, read `references/api.md` and `references/examples.md`.
|
|
34
34
|
6. If the request combines statusline work with commands, tools, events, other panels, or stateful mod behavior, also use `creating-mods` and its `references/architecture.md`.
|
|
35
35
|
7. Guard panel work with `letta.capabilities.ui.panels` when writing new files.
|
|
36
|
-
8. Edit
|
|
36
|
+
8. Edit `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/statusline.tsx`.
|
|
37
37
|
9. Summarize the absolute file path changed and tell the user to run `/reload` unless the command can reload automatically.
|
|
38
38
|
|
|
39
39
|
## Bare `/statusline` behavior
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
# Statusline Mod API
|
|
2
2
|
|
|
3
|
-
Use this reference when creating or editing
|
|
3
|
+
Use this reference when creating or editing `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/statusline.tsx`.
|
|
4
4
|
|
|
5
5
|
## Location
|
|
6
6
|
|
|
7
7
|
```text
|
|
8
|
-
|
|
8
|
+
${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/statusline.tsx
|
|
9
9
|
```
|
|
10
10
|
|
|
11
11
|
This is a trusted, user-owned global mod file. Project mods are intentionally unsupported for now.
|
|
@@ -147,7 +147,7 @@ render: ({ width, columns }) => columns(["left", "middle", "right"], width),
|
|
|
147
147
|
|
|
148
148
|
## Reload behavior
|
|
149
149
|
|
|
150
|
-
After editing
|
|
150
|
+
After editing `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/statusline.tsx`, tell the user to run:
|
|
151
151
|
|
|
152
152
|
```text
|
|
153
153
|
/reload
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
# Statusline Migration
|
|
2
2
|
|
|
3
|
-
Use this reference when migrating legacy command statuslines, standalone `.sh` statusline scripts, or shell PS1 prompts into
|
|
3
|
+
Use this reference when migrating legacy command statuslines, standalone `.sh` statusline scripts, or shell PS1 prompts into `${LETTA_CONFIG_DIR:-$HOME/.letta}/mods/statusline.tsx`.
|
|
4
4
|
|
|
5
5
|
## Legacy Letta command statusline
|
|
6
6
|
|
|
7
7
|
Inspect these files for old config:
|
|
8
8
|
|
|
9
9
|
```text
|
|
10
|
-
|
|
10
|
+
${LETTA_CONFIG_DIR:-$HOME/.letta}/settings.json
|
|
11
11
|
<project>/.letta/settings.json
|
|
12
12
|
<project>/.letta/settings.local.json
|
|
13
13
|
```
|
|
@@ -159,7 +159,7 @@ You — not the subagents — decide what becomes memory, and you write it. Synt
|
|
|
159
159
|
|
|
160
160
|
```bash
|
|
161
161
|
node <SKILL_DIR>/scripts/history-coverage.mjs --prepared /tmp/letta-init-history \
|
|
162
|
-
--journal
|
|
162
|
+
--journal "${LETTA_CONFIG_DIR:-$HOME/.letta}/workflows/executions/<id>/journal.jsonl" \
|
|
163
163
|
--retry-out /tmp/letta-init-history/retry.json
|
|
164
164
|
```
|
|
165
165
|
|
|
@@ -17,7 +17,7 @@ The important part is choosing the right layer. Do not smear a preference into d
|
|
|
17
17
|
| Memory and identity | Facts worth retaining, style preferences, persona changes, project knowledge, reusable skills | Edit `$MEMORY_DIR` files and sync the memory repo |
|
|
18
18
|
| Server agent fields | Agent default model (only on explicit request), context limit, system prompt, compaction, agent name, description | Patch `/v1/agents/{agent_id}` |
|
|
19
19
|
| Server conversation fields | Model/context changes for the current conversation (the normal target) | Patch `/v1/conversations/{conversation_id}` |
|
|
20
|
-
| Local settings | Permissions, environment variables, UI/runtime preferences, pinned agents, toolset overrides, reflection cadence | Edit `~/.letta/settings.json
|
|
20
|
+
| Local settings | Permissions, environment variables, UI/runtime preferences, pinned agents, toolset overrides, reflection cadence | Edit `$LETTA_CONFIG_DIR/settings.json` when configured (otherwise `~/.letta/settings.json`), `./.letta/settings.json`, or `./.letta/settings.local.json` |
|
|
21
21
|
| Mods | New deterministic tools, slash commands, providers, statusline behavior, or lightweight UI | Load `creating-mods`, `customizing-commands`, or `customizing-statusline` |
|
|
22
22
|
| Skills | Reusable procedural knowledge or bundled scripts | Load `creating-skills` or `acquiring-skills` |
|
|
23
23
|
| Channels | Slack/Discord/Telegram/WhatsApp/Signal accounts, pairing, routing, listener state | Use `letta channels` or channel commands |
|
|
@@ -232,7 +232,7 @@ Settings scopes:
|
|
|
232
232
|
|
|
233
233
|
| File | Scope | Typical contents |
|
|
234
234
|
| --- | --- | --- |
|
|
235
|
-
| `~/.letta/settings.json` | User/global | Permissions, env keys, experiments, UI/runtime preferences, agents[] entries |
|
|
235
|
+
| `$LETTA_CONFIG_DIR/settings.json` when configured; otherwise `~/.letta/settings.json` | User/global | Permissions, env keys, experiments, UI/runtime preferences, agents[] entries |
|
|
236
236
|
| `./.letta/settings.json` | Project/shared | Project settings committed with the repo |
|
|
237
237
|
| `./.letta/settings.local.json` | Project-local | Personal project overrides, usually gitignored |
|
|
238
238
|
|
|
@@ -347,7 +347,7 @@ Use skills when the user wants you to become good at a repeatable workflow. Sour
|
|
|
347
347
|
|
|
348
348
|
1. Project skills: `.agents/skills/` with `.skills/` as legacy fallback
|
|
349
349
|
2. Agent skills: `$MEMORY_DIR/skills/`
|
|
350
|
-
3. Global skills: `~/.letta/skills/`
|
|
350
|
+
3. Global skills: `$LETTA_CONFIG_DIR/skills/` when configured; otherwise `~/.letta/skills/`
|
|
351
351
|
4. Bundled skills
|
|
352
352
|
|
|
353
353
|
Load `creating-skills` to create or edit a skill. Load `acquiring-skills` when the user asks for a capability you do not already have. Project, global, bundled, and agent-owned skills have different visibility; verify the target scope before changing skills another agent may load.
|
|
@@ -385,7 +385,7 @@ letta channels pair --channel <channel> --code <code> --agent <agent-id> --conve
|
|
|
385
385
|
letta server --channels <channel>
|
|
386
386
|
```
|
|
387
387
|
|
|
388
|
-
Channel state lives under
|
|
388
|
+
Channel state lives under `${LETTA_CONFIG_DIR:-$HOME/.letta}/channels/<channel>/` (`config.yaml`, `accounts.json`, routing/pairing files, and channel runtimes). Account tokens may be plaintext in `file` mode or keyring placeholders in `keyring`/`auto` mode. Configure storage with `channelCredentialsStore` (`file`, `keyring`, `auto`) or `LETTA_CHANNEL_CREDENTIALS_STORE`; do not treat keyring placeholders as usable secrets and do not print tokens. Channel configuration and pairing can route external messages to other agents/conversations; verify IDs and get human consent for interactive authorization.
|
|
389
389
|
|
|
390
390
|
`letta channels configure <channel>` is an interactive TTY wizard. Do not launch it as unattended work or claim setup succeeded while it is waiting for input; hand the authorization/setup step to the user.
|
|
391
391
|
|
|
@@ -19,10 +19,27 @@ from pathlib import Path
|
|
|
19
19
|
from typing import Any
|
|
20
20
|
|
|
21
21
|
|
|
22
|
+
def get_user_config_dir() -> Path:
|
|
23
|
+
"""Resolve the product-selected global config directory."""
|
|
24
|
+
override = os.environ.get("LETTA_CONFIG_DIR", "").strip()
|
|
25
|
+
if override:
|
|
26
|
+
path = Path(override)
|
|
27
|
+
if not path.is_absolute():
|
|
28
|
+
raise ValueError("LETTA_CONFIG_DIR must be absolute")
|
|
29
|
+
return path
|
|
30
|
+
return Path.home() / ".letta"
|
|
31
|
+
|
|
32
|
+
|
|
22
33
|
def get_settings_path(scope: str, working_directory: str) -> Path:
|
|
23
34
|
"""Get the settings file path for a given scope."""
|
|
24
35
|
if scope == "user":
|
|
25
|
-
return
|
|
36
|
+
return get_user_config_dir() / "settings.json"
|
|
37
|
+
if os.environ.get("LETTA_CONFIG_DIR", "").strip() and (
|
|
38
|
+
Path(working_directory).resolve() == Path.home().resolve()
|
|
39
|
+
):
|
|
40
|
+
raise ValueError(
|
|
41
|
+
f"Cannot save {scope} permissions under HOME while LETTA_CONFIG_DIR is set"
|
|
42
|
+
)
|
|
26
43
|
if scope == "project":
|
|
27
44
|
return Path(working_directory) / ".letta" / "settings.json"
|
|
28
45
|
if scope == "local":
|
|
@@ -48,10 +65,14 @@ def load_settings(path: Path) -> dict[str, Any]:
|
|
|
48
65
|
return parsed
|
|
49
66
|
|
|
50
67
|
|
|
51
|
-
def save_settings(path: Path, settings: dict[str, Any]) -> None:
|
|
52
|
-
"""Atomically save settings,
|
|
68
|
+
def save_settings(path: Path, settings: dict[str, Any], secure: bool = False) -> None:
|
|
69
|
+
"""Atomically save settings, securing the global config when requested."""
|
|
53
70
|
path.parent.mkdir(parents=True, exist_ok=True)
|
|
54
|
-
|
|
71
|
+
if secure:
|
|
72
|
+
os.chmod(path.parent, 0o700)
|
|
73
|
+
mode = 0o600 if secure else (
|
|
74
|
+
stat.S_IMODE(path.stat().st_mode) if path.exists() else None
|
|
75
|
+
)
|
|
55
76
|
temp_path: str | None = None
|
|
56
77
|
|
|
57
78
|
try:
|
|
@@ -194,7 +215,7 @@ def main() -> None:
|
|
|
194
215
|
|
|
195
216
|
if added:
|
|
196
217
|
try:
|
|
197
|
-
save_settings(settings_path, settings)
|
|
218
|
+
save_settings(settings_path, settings, secure=args.scope == "user")
|
|
198
219
|
except OSError as exc:
|
|
199
220
|
print(f"Could not save {settings_path}: {exc}", file=sys.stderr)
|
|
200
221
|
sys.exit(1)
|
|
@@ -54,10 +54,25 @@ VERSION_TIMEOUT_SECONDS = 2
|
|
|
54
54
|
MAX_VERSION_TEXT = 200
|
|
55
55
|
|
|
56
56
|
|
|
57
|
+
def get_user_config_dir() -> Path:
|
|
58
|
+
"""Resolve the product-selected global config directory."""
|
|
59
|
+
override = os.environ.get("LETTA_CONFIG_DIR", "").strip()
|
|
60
|
+
if override:
|
|
61
|
+
path = Path(override)
|
|
62
|
+
if not path.is_absolute():
|
|
63
|
+
raise ValueError("LETTA_CONFIG_DIR must be absolute")
|
|
64
|
+
return path
|
|
65
|
+
return Path.home() / ".letta"
|
|
66
|
+
|
|
67
|
+
|
|
57
68
|
def get_settings_paths(working_directory: str) -> list[tuple[str, Path]]:
|
|
58
69
|
"""Return (scope, path) in precedence order (lowest to highest)."""
|
|
59
|
-
|
|
60
|
-
|
|
70
|
+
paths = [("user", get_user_config_dir() / "settings.json")]
|
|
71
|
+
if os.environ.get("LETTA_CONFIG_DIR", "").strip() and (
|
|
72
|
+
Path(working_directory).resolve() == Path.home().resolve()
|
|
73
|
+
):
|
|
74
|
+
return paths
|
|
75
|
+
return paths + [
|
|
61
76
|
("project", Path(working_directory) / ".letta" / "settings.json"),
|
|
62
77
|
("local", Path(working_directory) / ".letta" / "settings.local.json"),
|
|
63
78
|
]
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: video-generation
|
|
3
|
+
description: Generate videos from text prompts (and optional reference or frame images) using Letta's asynchronous video generation API. Use when the user asks to create, generate, or make a video or animation from a description, animate an existing image, or turn a prompt into a short video clip.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Video Generation
|
|
7
|
+
|
|
8
|
+
Generate videos via Letta's async `POST /v1/videos` using `curl` + `jq`. Requires `LETTA_BASE_URL` and `LETTA_API_KEY`, which the runtime provides. If either is unset, stop and ask the user to authenticate with Letta Cloud; do not ask for a video provider key.
|
|
9
|
+
|
|
10
|
+
This skill is adapted from OpenRouter's `openrouter-video` skill
|
|
11
|
+
([OpenRouterTeam/skills](https://github.com/OpenRouterTeam/skills/blob/83827c3f3b1200bc3b609f4ab4ec3241c0cfb7ad/skills/openrouter-video/SKILL.md)).
|
|
12
|
+
Letta's endpoint forwards the request with Letta's key and charges the
|
|
13
|
+
organization's Letta credits for the cost of each completed video.
|
|
14
|
+
|
|
15
|
+
Build every URL relative to `${LETTA_BASE_URL%/}` and send `Authorization: Bearer $LETTA_API_KEY`. Do not hardcode `https://api.letta.com`: Desktop and remote runtimes may provide a proxy base URL, and the credential may only be valid through that URL.
|
|
16
|
+
|
|
17
|
+
## The three steps
|
|
18
|
+
|
|
19
|
+
Video generation is async: submit → poll → download. A single request can't return the video because generation takes 30s–a few minutes. Tell the user the job was submitted so they know the delay is expected.
|
|
20
|
+
|
|
21
|
+
1. `POST /v1/videos` → `{ id, polling_url, status: "pending" }` (`polling_url` is the absolute URL of step 2)
|
|
22
|
+
2. `GET /v1/videos/{id}` every ~30s until `status` is `completed` (terminal failures: `failed`, `cancelled`, `expired` — surface the `error` field verbatim)
|
|
23
|
+
3. `GET /v1/videos/{id}/content?index=0` **with the auth header** → MP4 bytes
|
|
24
|
+
|
|
25
|
+
A completed job also has `urls`: signed downloads that need no API key until `expires_at` (Unix seconds); anyone holding one can download the video, so prefer step 3.
|
|
26
|
+
|
|
27
|
+
## Pick parameters from the models endpoint, don't guess
|
|
28
|
+
|
|
29
|
+
`resolution`, `aspect_ratio`, `duration`, and `frame_images[].frame_type` are per-model. Before the first submit for a new model (or whenever the user asks for something specific), fetch the model's capabilities and only send values from the returned sets:
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
curl -sS "${LETTA_BASE_URL%/}/v1/videos/models" \
|
|
33
|
+
-H "Authorization: Bearer $LETTA_API_KEY" \
|
|
34
|
+
| jq '.data[] | select(.id == "MODEL_ID")'
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
Fields on each model worth knowing: `supported_resolutions`, `supported_aspect_ratios`, `supported_sizes`, `supported_durations` (often discrete like `[4,6,8]`, not a range), `supported_frame_images` (which `frame_type` values are accepted), `generate_audio` and `seed` (capability bools), `pricing_skus`, and `allowed_passthrough_parameters`. An out-of-set value returns a 400, so validate client-side.
|
|
38
|
+
|
|
39
|
+
Models include `google/veo-3.1`, `bytedance/seedance-2.5`, `kwaivgi/kling-v3.0-pro`, `minimax/hailuo-3`, `minimax/hailuo-3-max`, and `heygen/avatar-iv` (a talking head from one photo); the models endpoint has the full list.
|
|
40
|
+
|
|
41
|
+
## Full workflow (drop-in)
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
#!/usr/bin/env bash
|
|
45
|
+
set -euo pipefail
|
|
46
|
+
|
|
47
|
+
PROMPT="a golden retriever playing fetch on a sunny beach"
|
|
48
|
+
MODEL="google/veo-3.1"
|
|
49
|
+
OUTPUT="video-$(date +%Y%m%d-%H%M%S).mp4"
|
|
50
|
+
base_url="${LETTA_BASE_URL%/}"
|
|
51
|
+
|
|
52
|
+
# Build payload — extend with resolution/aspect_ratio/duration/etc. as needed.
|
|
53
|
+
payload=$(jq -n --arg model "$MODEL" --arg prompt "$PROMPT" \
|
|
54
|
+
'{model: $model, prompt: $prompt}')
|
|
55
|
+
|
|
56
|
+
submit=$(curl -sS -X POST "$base_url/v1/videos" \
|
|
57
|
+
-H "Authorization: Bearer $LETTA_API_KEY" \
|
|
58
|
+
-H "Content-Type: application/json" \
|
|
59
|
+
-d "$payload")
|
|
60
|
+
|
|
61
|
+
job_id=$(echo "$submit" | jq -r '.id // empty')
|
|
62
|
+
if [ -z "$job_id" ]; then
|
|
63
|
+
echo "Submit failed: $(echo "$submit" | jq -r '.message // .')" >&2
|
|
64
|
+
exit 1
|
|
65
|
+
fi
|
|
66
|
+
echo "Submitted $job_id" >&2
|
|
67
|
+
|
|
68
|
+
while :; do
|
|
69
|
+
sleep 30
|
|
70
|
+
resp=$(curl -sS "$base_url/v1/videos/$job_id" -H "Authorization: Bearer $LETTA_API_KEY")
|
|
71
|
+
# Avoid the name `status` — zsh treats it as read-only.
|
|
72
|
+
st=$(echo "$resp" | jq -r '.status')
|
|
73
|
+
echo "Status: $st" >&2
|
|
74
|
+
case "$st" in
|
|
75
|
+
completed) break ;;
|
|
76
|
+
failed|cancelled|expired)
|
|
77
|
+
echo "Generation $st: $(echo "$resp" | jq -r '.error // "unknown"')" >&2
|
|
78
|
+
exit 1 ;;
|
|
79
|
+
esac
|
|
80
|
+
done
|
|
81
|
+
|
|
82
|
+
curl -sS -f "$base_url/v1/videos/$job_id/content?index=0" \
|
|
83
|
+
-H "Authorization: Bearer $LETTA_API_KEY" \
|
|
84
|
+
--output "$OUTPUT"
|
|
85
|
+
|
|
86
|
+
echo "$resp" | jq --arg out "$(realpath "$OUTPUT")" \
|
|
87
|
+
'{job_id: .id, video_saved: $out, billing}'
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
Share the saved file the way your current surface supports: attach or upload it to the conversation or channel you are replying in. Keep credit amounts out of replies unless the user asks about cost; `billing.credits_charged` on the completed job has them.
|
|
91
|
+
|
|
92
|
+
## Parameters
|
|
93
|
+
|
|
94
|
+
Required: `model`. `prompt` is required by every model except those that can generate from image input alone. Common optional fields:
|
|
95
|
+
|
|
96
|
+
- `duration` (int) — must be one of the model's `supported_durations`.
|
|
97
|
+
- `resolution` (string) / `aspect_ratio` (string) / `size` (string, `"WxH"`) — `size` is interchangeable with resolution + aspect_ratio.
|
|
98
|
+
- `generate_audio` (bool) — only meaningful if the model's `generate_audio` capability is true.
|
|
99
|
+
- `seed` (int) — honored only if the model's `seed` capability is true.
|
|
100
|
+
- `frame_images[]` — image-to-video; each entry is `{ type: "image_url", image_url: { url }, frame_type: "first_frame" | "last_frame" }`.
|
|
101
|
+
- `input_references[]` — reference-to-video (style guidance); image entries have the same shape, no `frame_type`. Audio and video references are `{ type: "audio_url", audio_url: { url } }` and `{ type: "video_url", video_url: { url } }`; only some models (such as `bytedance/seedance-2.5`) use them, and the rest ignore them. If both arrays are present, `frame_images` wins.
|
|
102
|
+
- `provider.options.<slug>.parameters.<key>` — provider passthrough, see below.
|
|
103
|
+
|
|
104
|
+
Image `url` can be a public `https://` URL or a local-file data URL: `MIME=image/png; B64=$(base64 < file.png | tr -d '\n'); url="data:${MIME};base64,${B64}"`. Audio and video references take URLs the same way.
|
|
105
|
+
|
|
106
|
+
## Provider passthrough
|
|
107
|
+
|
|
108
|
+
Provider-specific params go under `provider.options.<slug>.parameters`. The allowed keys for a given model are listed (flat) in `allowed_passthrough_parameters` on the models endpoint — but the meaning, value range, and required combinations come from the *upstream provider's* API docs (Google Vertex, Alibaba Dashscope, Kwai, ByteDance Volc Engine, MiniMax, OpenAI, etc.). Read the upstream docs before using an unfamiliar key; casing conventions differ between providers (Google/OpenAI use camelCase, most others use snake_case).
|
|
109
|
+
|
|
110
|
+
Example:
|
|
111
|
+
|
|
112
|
+
```json
|
|
113
|
+
{
|
|
114
|
+
"model": "google/veo-3.1",
|
|
115
|
+
"prompt": "a time-lapse of a flower blooming",
|
|
116
|
+
"provider": {
|
|
117
|
+
"options": {
|
|
118
|
+
"google-vertex": {
|
|
119
|
+
"parameters": {
|
|
120
|
+
"personGeneration": "allow",
|
|
121
|
+
"negativePrompt": "blurry, low quality"
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
`heygen/avatar-iv` animates one photo into a lip-synced talking head: pass the photo as an image in `input_references` and describe the video in `prompt`. Its passthrough keys (slug `heygen`) include `voice_id`, `motion_prompt`, and `expressiveness`; take their values from HeyGen's API docs.
|
|
130
|
+
|
|
131
|
+
```json
|
|
132
|
+
{
|
|
133
|
+
"model": "heygen/avatar-iv",
|
|
134
|
+
"prompt": "A friendly presenter welcomes viewers to the product tour",
|
|
135
|
+
"aspect_ratio": "9:16",
|
|
136
|
+
"input_references": [
|
|
137
|
+
{ "type": "image_url", "image_url": { "url": "data:image/png;base64,..." } }
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
## Chaining with other skills
|
|
143
|
+
|
|
144
|
+
- **Keyframes:** generate stills with the `image-generation` skill, then pass them as `frame_images` (`first_frame` / `last_frame`) or image `input_references` as data URLs.
|
|
145
|
+
- **Sound:** make a voiceover with the `speech-generation` skill or a soundtrack with the `music-generation` skill, then pass the audio as an `audio_url` input reference to a model that uses audio, or mix it onto the finished MP4 (for example with `ffmpeg`).
|
|
146
|
+
|
|
147
|
+
## Notes
|
|
148
|
+
|
|
149
|
+
- **Billing**: the organization needs credits for an estimate before a job is submitted; once it completes, it is charged for the video's cost. Don't resubmit needlessly.
|
|
150
|
+
- **Errors**: `402` = insufficient credits (`credits_required` in body); `400`/`404`/`500` return `{ "message": "..." }` — surface it to the user.
|
|
151
|
+
|
|
152
|
+
Video generation is not ZDR-eligible because the provider must temporarily retain the output for the async download step.
|
|
@@ -256,7 +256,7 @@ quick checks.
|
|
|
256
256
|
|
|
257
257
|
Every run persists its script, args, and a `journal.jsonl` with one line per
|
|
258
258
|
completed subagent call (prompt, outcome, conversation id) under
|
|
259
|
-
|
|
259
|
+
`${LETTA_CONFIG_DIR:-$HOME/.letta}/workflows/executions/<id>/`; the tool result names the paths. Before
|
|
260
260
|
diagnosing why a workflow returned an empty or unexpected result, read that
|
|
261
261
|
journal — it records each agent's actual return value and failure detail.
|
|
262
262
|
A failed run is not resumable: fix the script and launch it again.
|