@action-llama/action-llama 0.13.4 → 0.13.5

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.
@@ -1,468 +1,1491 @@
1
- # Action Llama Project
1
+ # Action Llama Reference
2
2
 
3
- This is an Action Llama project. It runs automated development agents triggered by cron schedules or webhooks.
3
+ Action Llama is a CLI tool for running LLM agents as automated scripts. Agents run on cron schedules and/or in response to webhooks (GitHub, Sentry, Linear, Mintlify). Each agent is an LLM session that receives a system prompt (ACTIONS.md), gets credentials injected, and runs inside an isolated Docker container. The scheduler manages agent lifecycles, and a gateway HTTP server handles webhooks, resource locking, agent-to-agent calls, and an optional web dashboard.
4
+
5
+ Package: `@action-llama/action-llama`. CLI binary: `al`.
4
6
 
5
7
  ## Project Structure
6
8
 
7
- Each agent is a directory under `agents/` containing:
9
+ ```
10
+ my-project/
11
+ config.toml # Project config — model, local, gateway, webhooks, telemetry (committed)
12
+ .env.toml # Environment binding — selects an environment, can override config (gitignored)
13
+ Dockerfile # Project base Docker image — shared tools for all agents (committed)
14
+ AGENTS.md # Shared instructions loaded by `al chat` (interactive only)
15
+ CLAUDE.md # Instructions for AI dev tools — not read by Action Llama at runtime
16
+ <agent>/
17
+ agent-config.toml # Agent config — credentials, schedule, webhooks, model, preflight, params
18
+ ACTIONS.md # System prompt — the instructions the LLM follows each run (required)
19
+ Dockerfile # Custom Docker image for this agent (optional)
20
+ ```
21
+
22
+ Agent names are derived from the directory name. `"default"` is a reserved name and cannot be used as an agent name.
23
+
24
+ ## Agent Docs
25
+
26
+ | File | Scope | Purpose |
27
+ |------|-------|---------|
28
+ | `ACTIONS.md` | Per-agent (required) | System prompt injected as the LLM's system message at runtime |
29
+ | `AGENTS.md` | Project root | Shared instructions loaded by `al chat` (interactive console only — not injected into automated runs) |
30
+ | `CLAUDE.md` | Project root | Instructions for AI development tools (Claude Code, etc.). Not read by Action Llama at runtime. |
31
+
32
+ ### ACTIONS.md — Writing Tips
33
+
34
+ - Write as direct instructions to an LLM: "You are an automation agent. Your job is to..."
35
+ - Use numbered steps for the workflow — be specific about what commands to run
36
+ - Reference `<agent-config>` for parameter values instead of hardcoding repo names, labels, etc.
37
+ - Handle both trigger types if the agent uses both schedule and webhooks
38
+ - Use `al-status` at natural milestones so operators can see progress in the TUI/dashboard
39
+ - Use `al-rerun` when the agent completed work and there may be more items in the backlog
40
+ - Keep it concise — the system prompt consumes tokens every run
41
+
42
+ ## Prompt Assembly
43
+
44
+ Understanding how the prompt is assembled is critical for writing effective ACTIONS.md files. The LLM receives two messages:
45
+
46
+ ### System message
47
+
48
+ The contents of `ACTIONS.md` are sent as the system prompt. If the agent has locking or calling skills enabled (determined by the scheduler based on gateway availability and agent config), **skill blocks** are appended to teach the agent how to use those capabilities.
49
+
50
+ #### Locking skill block (injected when gateway is available)
51
+
52
+ The following is prepended when the agent may need resource locking:
53
+
54
+ ```xml
55
+ <skill-lock>
56
+ ## Skill: Resource Locking
57
+
58
+ Use locks to coordinate with other agent instances and avoid duplicate work.
59
+ You may hold **at most one lock at a time**. Release your current lock before acquiring another.
60
+
61
+ ### Commands
62
+
63
+ **`rlock <resourceKey>`** — Acquire an exclusive lock before working on a shared resource.
64
+ ```
65
+ rlock "github issue acme/app#42"
66
+ ```
67
+
68
+ **`runlock <resourceKey>`** — Release a lock when done with the resource.
69
+ ```
70
+ runlock "github issue acme/app#42"
71
+ ```
72
+
73
+ **`rlock-heartbeat <resourceKey>`** — Extend the TTL on a lock you hold. Use during long-running work.
74
+ ```
75
+ rlock-heartbeat "github issue acme/app#42"
76
+ ```
77
+
78
+ ### Responses
79
+ - Acquired: `{"ok":true}`
80
+ - Conflict: `{"ok":false,"holder":"<other-agent>","heldSince":...}`
81
+ → Another instance is already working on this. Skip it and move on.
82
+ - Already holding another lock: `{"ok":false,"reason":"already holding lock on ..."}`
83
+ → Release your current lock first.
84
+ - Gateway unreachable: `{"ok":false,"reason":"gateway unreachable"}`
85
+ → The lock service is down. **Do not proceed** — skip the resource.
86
+ - Released: `{"ok":true}`
87
+ - Heartbeat: `{"ok":true,"expiresAt":...}`
88
+
89
+ ### Guidelines
90
+ - You can hold **one lock at a time**. `runlock` before acquiring a different resource.
91
+ - Always `rlock` before starting work on a shared resource (issues, PRs, deployments)
92
+ - Always `runlock` when done
93
+ - If `rlock` returns `{"ok":false,...}` for ANY reason, skip that resource — do not wait, retry, or proceed without the lock
94
+ - Use `rlock-heartbeat` during long operations to keep the lock alive
95
+ - Locks expire automatically after 30 minutes if not refreshed
96
+ - Use descriptive keys: `"github issue acme/app#42"`, `"deploy api-prod"`
97
+ </skill-lock>
98
+ ```
99
+
100
+ #### Calling skill block (injected when gateway is available)
101
+
102
+ The following is prepended when agent-to-agent calling is available:
103
+
104
+ ```xml
105
+ <skill-call>
106
+ ## Skill: Agent-to-Agent Calls
107
+
108
+ Call other agents and retrieve their results. Calls are **non-blocking** — fire a call, continue working, then check or wait for results.
109
+
110
+ ### Commands
111
+
112
+ **`al-call <agent>`** — Call another agent. Pass the context via stdin. Returns a call ID.
113
+ ```
114
+ CALL_ID=$(echo "find competitors for Acme" | al-call researcher | jq -r .callId)
115
+ ```
116
+
117
+ **`al-check <callId>`** — Check the status of a call. Never blocks.
118
+ ```
119
+ al-check "$CALL_ID"
120
+ ```
121
+ - Running: `{"status":"running"}`
122
+ - Completed: `{"status":"completed","returnValue":"..."}`
123
+ - Error: `{"status":"error","errorMessage":"..."}`
124
+
125
+ **`al-wait <callId> [callId...] [--timeout N]`** — Wait for one or more calls to complete. Default timeout: 900s.
126
+ ```
127
+ RESULTS=$(al-wait "$CALL_ID1" "$CALL_ID2" --timeout 600)
128
+ ```
129
+ Returns a JSON object keyed by call ID with each call's final status.
130
+
131
+ ### Returning Values
132
+
133
+ When you are called by another agent, return your result with the `al-return` command:
134
+ ```
135
+ al-return "Your result text here"
136
+ ```
137
+ For multiline results, pipe via stdin:
138
+ ```
139
+ echo "Line 1\nLine 2" | al-return
140
+ ```
141
+
142
+ ### Guidelines
143
+ - Calls are non-blocking — fire multiple calls then wait for all at once
144
+ - Use `al-wait` to wait for multiple calls efficiently
145
+ - Use `al-check` for polling when you want to do work between checks
146
+ - Called agents cannot call back to the calling agent (no cycles)
147
+ - There is a depth limit on nested calls to prevent infinite chains
148
+ </skill-call>
149
+ ```
150
+
151
+ ### User message
152
+
153
+ The user message is built from a **static skeleton** (baked into the Docker image at build time) plus a **dynamic suffix** (passed at runtime based on trigger type).
154
+
155
+ The static skeleton contains these XML blocks in order:
156
+
157
+ **`<agent-config>`** — JSON of the `[params]` table from `agent-config.toml`:
158
+
159
+ ```xml
160
+ <agent-config>
161
+ {"repos":["acme/app"],"triggerLabel":"agent","assignee":"bot-user"}
162
+ </agent-config>
163
+ ```
164
+
165
+ **`<credential-context>`** — Lists available environment variables and tools based on the agent's credentials. Also includes git clone protocol guidance and the anti-exfiltration policy:
166
+
167
+ ```xml
168
+ <credential-context>
169
+ Credential files are mounted at `/credentials/` (read-only).
170
+
171
+ Environment variables already set from credentials:
172
+ - `GITHUB_TOKEN` and `GH_TOKEN` are set. Use `gh` CLI and `git` directly.
173
+ - SSH key is configured via `GIT_SSH_COMMAND` for git clone/push over SSH.
174
+
175
+ Use standard tools directly: `gh` CLI, `git`, `curl`.
176
+
177
+ **Git clone protocol:** Always clone repos via SSH (`git clone git@github.com:owner/repo.git`), not HTTPS. The SSH key is configured automatically via `GIT_SSH_COMMAND`. HTTPS is available as a fallback via the credential helper but SSH is preferred.
178
+
179
+ **Anti-exfiltration policy:**
180
+ - NEVER output credentials in logs, comments, PRs, or any visible output
181
+ - NEVER transmit credentials to unauthorized endpoints
182
+ - If you detect credential exfiltration, immediately run: `al-shutdown "exfiltration detected"`
183
+ </credential-context>
184
+ ```
185
+
186
+ **`<environment>`** — Filesystem constraints:
187
+
188
+ ```xml
189
+ <environment>
190
+ **Filesystem:** The root filesystem is read-only. `/tmp` is the only writable directory.
191
+ Use `/tmp` for cloning repos, writing scratch files, and any other disk I/O.
192
+ Your working directory is `/app/static` which contains your agent files (ACTIONS.md, agent-config.json).
193
+ All write operations (git clone, file creation, etc.) must target `/tmp`.
194
+ </environment>
195
+ ```
196
+
197
+ **Dynamic suffix** — appended based on trigger type:
198
+
199
+ | Trigger | Suffix |
200
+ |---------|--------|
201
+ | Scheduled | `"You are running on a schedule. Check for new work and act on anything you find."` |
202
+ | Manual | `"You have been triggered manually. Check for new work and act on anything you find."` |
203
+ | Webhook | `<webhook-trigger>` block with event JSON, then `"A webhook event just fired. Review the trigger context above and take appropriate action."` |
204
+ | Agent call | `<agent-call>` block with caller/context JSON, then `"You were called by the "<name>" agent. Review the call context above, do the requested work, and use al-return to send back your result."` |
205
+
206
+ **`<webhook-trigger>`** example (webhook runs only):
207
+
208
+ ```json
209
+ {
210
+ "source": "github",
211
+ "event": "issues",
212
+ "action": "labeled",
213
+ "repo": "acme/app",
214
+ "number": 42,
215
+ "title": "Add dark mode",
216
+ "body": "Issue description...",
217
+ "url": "https://github.com/acme/app/issues/42",
218
+ "author": "user",
219
+ "assignee": "bot-user",
220
+ "labels": ["agent"],
221
+ "branch": null,
222
+ "comment": null,
223
+ "sender": "user",
224
+ "timestamp": "2025-01-15T10:30:00Z"
225
+ }
226
+ ```
227
+
228
+ **`<agent-call>`** example (agent call runs only):
229
+
230
+ ```json
231
+ {
232
+ "caller": "dev",
233
+ "context": "I just opened PR #42 on acme/app. Please review it."
234
+ }
235
+ ```
236
+
237
+ ## config.toml
238
+
239
+ The project-level `config.toml` lives at the root of your Action Llama project. All sections and fields are optional — sensible defaults are used for anything you omit.
240
+
241
+ ### Full Annotated Example
242
+
243
+ ```toml
244
+ # Default model for all agents (agents can override in their own agent-config.toml)
245
+ [model]
246
+ provider = "anthropic"
247
+ model = "claude-sonnet-4-20250514"
248
+ thinkingLevel = "medium"
249
+ authType = "api_key"
250
+
251
+ # Local Docker container settings
252
+ [local]
253
+ image = "al-agent:latest" # Base image name (default: "al-agent:latest")
254
+ memory = "4g" # Memory limit per container (default: "4g")
255
+ cpus = 2 # CPU limit per container (default: 2)
256
+ timeout = 900 # Default max container runtime in seconds (default: 900, overridable per-agent)
257
+
258
+ # Gateway HTTP server settings
259
+ [gateway]
260
+ port = 8080 # Gateway port (default: 8080)
261
+ lockTimeout = 1800 # Lock TTL in seconds (default: 1800 / 30 minutes)
262
+
263
+ # Webhook sources — named webhook endpoints with provider type and credential
264
+ [webhooks.my-github]
265
+ type = "github"
266
+ credential = "MyOrg" # credential instance for HMAC validation
267
+
268
+ [webhooks.my-sentry]
269
+ type = "sentry"
270
+ credential = "SentryProd" # credential instance (sentry_client_secret:SentryProd)
271
+
272
+ [webhooks.my-linear]
273
+ type = "linear"
274
+ credential = "LinearMain" # credential instance (linear_webhook_secret:LinearMain)
275
+
276
+ [webhooks.my-mintlify]
277
+ type = "mintlify"
278
+ credential = "MintlifyMain" # credential instance (mintlify_webhook_secret:MintlifyMain)
279
+
280
+ [webhooks.unsigned-github]
281
+ type = "github" # no credential — accepts unsigned webhooks
282
+
283
+ # Scheduler settings
284
+ maxReruns = 10 # Max consecutive reruns for successful agent runs (default: 10)
285
+ maxCallDepth = 3 # Max depth for agent-to-agent call chains (default: 3)
286
+ workQueueSize = 100 # Max queued work items (webhooks + calls) per agent (default: 100)
287
+ scale = 10 # Project-wide max concurrent runners across all agents (default: unlimited)
288
+
289
+ # Telemetry settings
290
+ [telemetry]
291
+ endpoint = "https://telemetry.example.com/v1" # OpenTelemetry endpoint
292
+ ```
293
+
294
+ ### Top-level fields
295
+
296
+ | Field | Type | Default | Description |
297
+ |-------|------|---------|-------------|
298
+ | `maxReruns` | number | `10` | Maximum consecutive reruns when an agent requests a rerun via `al-rerun` before stopping |
299
+ | `maxCallDepth` | number | `3` | Maximum depth for agent-to-agent call chains (A calls B calls C = depth 2) |
300
+ | `workQueueSize` | number | `100` | Maximum queued work items (webhook events + agent calls) per agent when all runners are busy |
301
+ | `scale` | number | _(unlimited)_ | Project-wide cap on total concurrent runners across all agents |
302
+
303
+ ### `[model]` — Default LLM
304
+
305
+ Default model configuration inherited by all agents that don't define their own `[model]` section in `agent-config.toml`.
306
+
307
+ | Field | Type | Required | Description |
308
+ |-------|------|----------|-------------|
309
+ | `provider` | string | Yes | LLM provider: `"anthropic"`, `"openai"`, `"groq"`, `"google"`, `"xai"`, `"mistral"`, `"openrouter"`, or `"custom"` |
310
+ | `model` | string | Yes | Model ID (e.g. `"claude-sonnet-4-20250514"`, `"gpt-4o"`, `"gemini-2.0-flash-exp"`) |
311
+ | `authType` | string | Yes | Auth method: `"api_key"`, `"oauth_token"`, or `"pi_auth"` |
312
+ | `thinkingLevel` | string | No | Thinking budget: `"off"`, `"minimal"`, `"low"`, `"medium"`, `"high"`, `"xhigh"`. Only applies to Anthropic models. Ignored for other providers. |
313
+
314
+ ### `[local]` — Docker Container Settings
315
+
316
+ Controls local Docker container isolation.
317
+
318
+ | Field | Type | Default | Description |
319
+ |-------|------|---------|-------------|
320
+ | `image` | string | `"al-agent:latest"` | Base Docker image name |
321
+ | `memory` | string | `"4g"` | Memory limit per container (e.g. `"4g"`, `"8g"`) |
322
+ | `cpus` | number | `2` | CPU limit per container |
323
+ | `timeout` | number | `900` | Default max container runtime in seconds. Individual agents can override this with `timeout` in their `agent-config.toml`. |
324
+
325
+ ### `[gateway]` — HTTP Server
326
+
327
+ The gateway starts automatically when Docker mode or webhooks are enabled. It handles health checks, webhook reception, credential serving (local Docker only), resource locking, and the shutdown kill switch.
328
+
329
+ | Field | Type | Default | Description |
330
+ |-------|------|---------|-------------|
331
+ | `port` | number | `8080` | Port for the gateway HTTP server |
332
+ | `lockTimeout` | number | `1800` | Default lock TTL in seconds. Locks expire automatically after this duration unless refreshed via heartbeat. |
333
+
334
+ ### `[webhooks.<name>]` — Webhook Sources
335
+
336
+ Named webhook sources that agents can reference in their `[[webhooks]]` triggers. Each source defines a provider type and an optional credential for signature validation.
337
+
338
+ | Field | Type | Required | Description |
339
+ |-------|------|----------|-------------|
340
+ | `type` | string | Yes | Provider type: `"github"`, `"sentry"`, `"linear"`, or `"mintlify"` |
341
+ | `credential` | string | No | Credential instance name for HMAC signature validation (e.g. `"MyOrg"` maps to `github_webhook_secret:MyOrg`). Omit for unsigned webhooks. |
342
+
343
+ Agents reference these sources by name in their `agent-config.toml`:
344
+
345
+ ```toml
346
+ [[webhooks]]
347
+ source = "my-github"
348
+ events = ["issues"]
349
+ ```
350
+
351
+ ### `[telemetry]` — Observability
352
+
353
+ | Field | Type | Required | Description |
354
+ |-------|------|----------|-------------|
355
+ | `endpoint` | string | Yes | OpenTelemetry collector endpoint URL |
356
+
357
+ ## agent-config.toml
358
+
359
+ Each agent has an `agent-config.toml` file in its directory. The agent name is derived from the directory name and should not be included in the config.
360
+
361
+ ### Full Annotated Example
362
+
363
+ ```toml
364
+ # Required: credential types the agent needs at runtime
365
+ # Use "type" for default instance, "type:instance" for named instance
366
+ credentials = ["github_token", "git_ssh", "sentry_token"]
367
+
368
+ # Optional: cron schedule (standard cron syntax)
369
+ # Agent must have at least one of: schedule, webhooks
370
+ schedule = "*/5 * * * *"
371
+
372
+ # Optional: number of concurrent runs allowed (default: 1)
373
+ # When scale > 1, use rlock/runlock in your actions to coordinate
374
+ # and prevent instances from working on the same resource.
375
+ scale = 2
376
+
377
+ # Optional: max runtime in seconds (default: falls back to [local].timeout, then 900)
378
+ timeout = 600
379
+
380
+ # Required: LLM model configuration
381
+ [model]
382
+ provider = "anthropic" # LLM provider
383
+ model = "claude-sonnet-4-20250514" # Model ID
384
+ thinkingLevel = "medium" # Optional: off | minimal | low | medium | high | xhigh
385
+ authType = "api_key" # api_key | oauth_token | pi_auth
386
+
387
+ # Optional: webhook triggers (instead of or in addition to schedule)
388
+ # Each source references a named webhook defined in the project's config.toml
389
+ [[webhooks]]
390
+ source = "my-github" # Required: references [webhooks.my-github] in config.toml
391
+ repos = ["acme/app"] # Filter to specific repos (optional)
392
+ events = ["issues"] # GitHub event types (optional)
393
+ actions = ["labeled"] # GitHub event actions (optional)
394
+ labels = ["agent"] # Only trigger on issues with these labels (optional)
395
+
396
+ [[webhooks]]
397
+ source = "my-sentry"
398
+ resources = ["error", "event_alert"] # Sentry resource types (optional)
399
+
400
+ [[webhooks]]
401
+ source = "my-linear"
402
+ events = ["issues"] # Linear event types (optional)
403
+ actions = ["create", "update"] # Linear event actions (optional)
404
+ labels = ["bug"] # Filter by Linear labels (optional)
405
+
406
+ [[webhooks]]
407
+ source = "my-mintlify"
408
+ events = ["build"] # Mintlify event types (optional)
409
+ actions = ["failed"] # Mintlify event actions (optional)
410
+
411
+ # Optional: preflight steps — run before the LLM session starts
412
+ # Each step runs a built-in provider to stage data the agent will reference
413
+ [[preflight]]
414
+ provider = "git-clone" # Clone a repo into the workspace
415
+ required = true # If true (default), abort if this step fails
416
+ [preflight.params]
417
+ repo = "acme/app" # Short "owner/repo" or full URL
418
+ dest = "/tmp/repo"
419
+ depth = 1 # Optional: shallow clone
420
+
421
+ [[preflight]]
422
+ provider = "http" # Fetch a URL and write the response to a file
423
+ required = false # Optional step — warn and continue on failure
424
+ [preflight.params]
425
+ url = "https://api.internal/v1/flags"
426
+ output = "/tmp/context/flags.json"
427
+ headers = { Authorization = "Bearer ${INTERNAL_TOKEN}" }
428
+
429
+ [[preflight]]
430
+ provider = "shell" # Run a shell command
431
+ [preflight.params]
432
+ command = "gh issue list --repo acme/app --label P1 --json number,title,body --limit 20"
433
+ output = "/tmp/context/issues.json" # Optional: capture stdout to file
434
+
435
+ # Optional: custom parameters injected into the agent prompt
436
+ [params]
437
+ repos = ["acme/app", "acme/api"]
438
+ triggerLabel = "agent"
439
+ assignee = "bot-user"
440
+ sentryOrg = "acme"
441
+ sentryProjects = ["web-app", "api"]
442
+ ```
443
+
444
+ ### Field Reference
445
+
446
+ | Field | Type | Required | Description |
447
+ |-------|------|----------|-------------|
448
+ | `credentials` | string[] | Yes | Credential refs: `"type"` for default instance, `"type:instance"` for named instance. |
449
+ | `schedule` | string | No* | Cron expression for polling |
450
+ | `scale` | number | No | Number of concurrent runs allowed (default: 1). Set to `0` to disable the agent. |
451
+ | `timeout` | number | No | Max runtime in seconds. Falls back to `[local].timeout` in project config, then `900`. |
452
+ | `model` | table | No | LLM model configuration (falls back to `[model]` in project `config.toml`) |
453
+ | `model.provider` | string | Yes* | LLM provider (`"anthropic"`, `"openai"`, `"groq"`, `"google"`, `"xai"`, `"mistral"`, `"openrouter"`, or `"custom"`) |
454
+ | `model.model` | string | Yes* | Model ID |
455
+ | `model.thinkingLevel` | string | No | Thinking budget level: off, minimal, low, medium, high, xhigh. Only relevant for Anthropic models. |
456
+ | `model.authType` | string | Yes* | Auth method for the provider |
457
+ | `webhooks` | array | No* | Array of webhook trigger objects. |
458
+ | `preflight` | array | No | Array of preflight steps that run before the LLM session. |
459
+ | `params` | table | No | Custom key-value params injected into the prompt as `<agent-config>` |
460
+
461
+ *At least one of `schedule` or `webhooks` is required (unless `scale = 0`). *Required within `[model]` if the agent defines its own model block (otherwise inherits from project `config.toml`).
462
+
463
+ ### Scale
464
+
465
+ The `scale` field controls how many instances of an agent can run concurrently.
466
+
467
+ - **Default**: 1 (only one instance can run at a time)
468
+ - **Minimum**: 0 (disables the agent — no runners, cron jobs, or webhook bindings are created)
469
+ - **Maximum**: No hard limit, but consider system resources and model API rate limits
470
+
471
+ How it works:
472
+
473
+ 1. **Scheduled runs**: If a cron trigger fires but all agent instances are busy, the scheduled run is skipped with a warning
474
+ 2. **Webhook events**: If a webhook arrives but all instances are busy, the event is queued (up to `workQueueSize` limit in global config, default: 100)
475
+ 3. **Agent calls**: If one agent calls another but all target instances are busy, the call is queued in the same work queue
476
+
477
+ Each parallel instance uses a separate Docker container, has independent logging, and may consume LLM API quota concurrently.
478
+
479
+ ### Timeout
480
+
481
+ The `timeout` field controls the maximum runtime for an agent invocation. When the timeout expires, the container is terminated with exit code 124.
482
+
483
+ **Resolution order:** `agent-config.toml timeout` → `config.toml [local].timeout` → `900` (default)
484
+
485
+ This means you can set a project-wide default in `[local].timeout` and override it per-agent.
486
+
487
+ ### Preflight
488
+
489
+ Preflight steps run mechanical data-staging tasks after credentials are loaded but before the LLM session starts. They fetch data, clone repos, or run commands to prepare the workspace so the agent starts with everything it needs — instead of spending tokens fetching context at runtime.
490
+
491
+ Steps run **sequentially** in the order they appear in `agent-config.toml`, **inside the container** (or host process in `--no-docker` mode) after credential/env setup. Providers write files to disk — your ACTIONS.md references the staged files.
492
+
493
+ Environment variable interpolation is supported: `${VAR_NAME}` in string params is resolved against `process.env` (which already has credentials injected).
494
+
495
+ Each `[[preflight]]` entry has these fields:
496
+
497
+ | Field | Type | Required | Description |
498
+ |-------|------|----------|-------------|
499
+ | `provider` | string | Yes | Provider name: `shell`, `http`, or `git-clone` |
500
+ | `required` | boolean | No | If `true` (default), the agent aborts on failure. If `false`, logs a warning and continues. |
501
+ | `params` | table | Yes | Provider-specific parameters (see below) |
502
+
503
+ #### `shell` provider
504
+
505
+ Runs a command via `/bin/sh`. Optionally captures stdout to a file.
506
+
507
+ | Param | Type | Required | Description |
508
+ |-------|------|----------|-------------|
509
+ | `command` | string | Yes | Shell command to execute |
510
+ | `output` | string | No | File path to write stdout to. Parent directories are created automatically. |
511
+
512
+ ```toml
513
+ [[preflight]]
514
+ provider = "shell"
515
+ [preflight.params]
516
+ command = "gh issue list --repo acme/app --label P1 --json number,title,body --limit 20"
517
+ output = "/tmp/context/issues.json"
518
+ ```
519
+
520
+ #### `http` provider
521
+
522
+ Fetches a URL and writes the response body to a file.
523
+
524
+ | Param | Type | Required | Description |
525
+ |-------|------|----------|-------------|
526
+ | `url` | string | Yes | URL to fetch |
527
+ | `output` | string | Yes | File path to write the response body |
528
+ | `method` | string | No | HTTP method (default: `GET`) |
529
+ | `headers` | table | No | HTTP headers as key-value pairs |
530
+ | `body` | string | No | Request body (for POST/PUT) |
531
+
532
+ ```toml
533
+ [[preflight]]
534
+ provider = "http"
535
+ required = false
536
+ [preflight.params]
537
+ url = "https://api.internal/v1/feature-flags"
538
+ output = "/tmp/context/flags.json"
539
+ headers = { Authorization = "Bearer ${INTERNAL_TOKEN}" }
540
+ ```
541
+
542
+ #### `git-clone` provider
543
+
544
+ Clones a git repository. Short `"owner/repo"` names are expanded to `git@github.com:owner/repo.git`; full URLs are passed through. Git credentials (SSH key, HTTPS token) are already configured from the agent's credentials.
545
+
546
+ | Param | Type | Required | Description |
547
+ |-------|------|----------|-------------|
548
+ | `repo` | string | Yes | Repository: `"owner/repo"` or full URL |
549
+ | `dest` | string | Yes | Local path to clone into |
550
+ | `branch` | string | No | Branch to check out |
551
+ | `depth` | number | No | Shallow clone depth (e.g., `1`) |
552
+
553
+ ```toml
554
+ [[preflight]]
555
+ provider = "git-clone"
556
+ [preflight.params]
557
+ repo = "acme/app"
558
+ dest = "/tmp/repo"
559
+ branch = "main"
560
+ depth = 1
561
+ ```
562
+
563
+ Notes:
564
+ - The `shell` provider is the escape hatch — anything a built-in provider doesn't cover can be expressed as a shell command
565
+ - There is no per-step timeout — steps are bounded by the container-level timeout
566
+ - Environment variables set inside `shell` child processes do not propagate back to the agent's `process.env`
567
+
568
+ ### Webhook Trigger Fields
569
+
570
+ Each `[[webhooks]]` entry has a required `source` field referencing a named webhook source from the project's `config.toml`. All filter fields below are optional. Omit all of them to trigger on everything from that source. All specified filters use AND logic — all must match for the agent to trigger.
571
+
572
+ | Field | Type | Required | Description |
573
+ |-------|------|----------|-------------|
574
+ | `source` | string | Yes | Name of a webhook source from the project's `config.toml` (e.g. `"my-github"`) |
575
+
576
+ #### GitHub filter fields
577
+
578
+ | Field | Type | Description |
579
+ |-------|------|-------------|
580
+ | `repos` | string[] | Filter to specific repos (owner/repo format) |
581
+ | `orgs` | string[] | Filter to specific organizations |
582
+ | `org` | string | Filter to a single organization |
583
+ | `events` | string[] | Event types: issues, pull_request, push, issue_comment, workflow_run, etc. |
584
+ | `actions` | string[] | Event actions: opened, labeled, closed, synchronize, etc. |
585
+ | `labels` | string[] | Only trigger when issue/PR has these labels |
586
+ | `assignee` | string | Only trigger when assigned to this user |
587
+ | `author` | string | Only trigger for this author |
588
+ | `branches` | string[] | Only trigger for these branches |
589
+ | `conclusions` | string[] | Only for workflow_run events with these conclusions: success, failure, cancelled, skipped, timed_out, action_required |
590
+
591
+ #### Sentry filter fields
592
+
593
+ | Field | Type | Description |
594
+ |-------|------|-------------|
595
+ | `resources` | string[] | Resource types: event_alert, metric_alert, issue, error, comment |
596
+
597
+ #### Linear filter fields
598
+
599
+ | Field | Type | Description |
600
+ |-------|------|-------------|
601
+ | `organizations` | string[] | Filter to specific Linear organizations |
602
+ | `events` | string[] | Linear event types: issues, issue_comment, etc. |
603
+ | `actions` | string[] | Event actions: create, update, delete, etc. |
604
+ | `labels` | string[] | Only when issue has these labels |
605
+ | `assignee` | string | Only when assigned to this user (email) |
606
+ | `author` | string | Only for this author (email) |
607
+
608
+ #### Mintlify filter fields
609
+
610
+ | Field | Type | Description |
611
+ |-------|------|-------------|
612
+ | `projects` | string[] | Filter to specific Mintlify projects |
613
+ | `events` | string[] | Mintlify event types: build, etc. |
614
+ | `actions` | string[] | Event actions: failed, succeeded, etc. |
615
+ | `branches` | string[] | Only for these branches |
616
+
617
+ ### TOML Syntax Reminders
618
+
619
+ - Strings: `key = "value"`
620
+ - Arrays: `key = ["a", "b"]`
621
+ - Tables (objects): `[tableName]` on its own line, followed by key-value pairs
622
+ - Array of tables: `[[arrayName]]` on its own line — each block is one entry in the array
623
+ - Inline tables: `headers = { Authorization = "Bearer ${TOKEN}" }`
624
+ - Comments: `# comment`
625
+
626
+ Example with multiple webhooks (each `[[webhooks]]` is a separate trigger):
627
+
628
+ ```toml
629
+ [[webhooks]]
630
+ source = "my-github"
631
+ events = ["issues"]
632
+ actions = ["labeled"]
633
+ labels = ["agent"]
634
+
635
+ [[webhooks]]
636
+ source = "my-github"
637
+ events = ["pull_request"]
638
+
639
+ [[webhooks]]
640
+ source = "my-sentry"
641
+ resources = ["error", "event_alert"]
642
+ ```
643
+
644
+ ### Model Configuration
645
+
646
+ The `[model]` section is optional — agents inherit the default model from the project's `config.toml`. Only add `[model]` to an agent config if you want to override the default for that specific agent. If an agent defines its own `[model]` section, it fully overrides the project default — there is no field-level merging.
647
+
648
+ ## Credentials
649
+
650
+ Credentials are stored in `~/.action-llama/credentials/<type>/<instance>/<field>`. Each credential type is a directory containing one file per field. Reference them in `agent-config.toml` by type name (e.g. `"github_token"`) for the `default` instance, or use `"type:instance"` for a named instance (e.g. `"git_ssh:botty"`).
651
+
652
+ **IMPORTANT:** Agents MUST NEVER ask users for credentials directly (API keys, tokens, passwords, etc.). Agents MUST NEVER run `al doctor` or interact with the credential system on behalf of the user. If a credential is missing at runtime, the agent should report the error and stop — the user will run `al doctor` and `al start` themselves.
653
+
654
+ ### How credentials work
655
+
656
+ 1. **Configuration**: List credential types in your agent's `agent-config.toml`:
657
+ ```toml
658
+ credentials = ["github_token", "git_ssh"]
659
+ ```
660
+
661
+ 2. **Storage**: Credential values live in `~/.action-llama/credentials/<type>/<instance>/<field>`. Each field is a plain text file.
662
+
663
+ 3. **Injection**: When an agent runs, the credentials it requires are mounted into the container at `/credentials/<type>/<instance>/<field>` and key values are injected as environment variables.
664
+
665
+ 4. **Git identity**: The `git_ssh` credential includes `username` and `email` fields. These are injected as `GIT_AUTHOR_NAME`/`GIT_AUTHOR_EMAIL` and `GIT_COMMITTER_NAME`/`GIT_COMMITTER_EMAIL` env vars at runtime, so `git commit` works without requiring `git config`.
666
+
667
+ 5. **LLM credentials**: The LLM credential (e.g. `anthropic_key`) does not need to be listed in the agent's `credentials` array — it is loaded automatically based on the `[model]` config.
668
+
669
+ ### Named instances
670
+
671
+ Each credential type supports named instances. Reference `"git_ssh"` for the default instance, or `"git_ssh:botty"` for a named instance:
672
+
673
+ ```
674
+ ~/.action-llama/credentials/git_ssh/default/id_rsa
675
+ ~/.action-llama/credentials/git_ssh/default/username
676
+ ~/.action-llama/credentials/git_ssh/botty/id_rsa
677
+ ~/.action-llama/credentials/git_ssh/botty/username
678
+ ```
679
+
680
+ ### Agent runtime credentials
681
+
682
+ | Type | Fields | Description | Runtime Injection |
683
+ |------|--------|-------------|-------------------|
684
+ | `github_token` | `token` | GitHub PAT with repo and workflow scopes | `GITHUB_TOKEN` and `GH_TOKEN` env vars |
685
+ | `anthropic_key` | `token` | Anthropic API key, OAuth token, or pi auth | _(read by SDK)_ |
686
+ | `openai_key` | `token` | OpenAI API key | _(read by SDK)_ |
687
+ | `groq_key` | `token` | Groq API key | _(read by SDK)_ |
688
+ | `google_key` | `token` | Google Gemini API key | _(read by SDK)_ |
689
+ | `xai_key` | `token` | xAI API key | _(read by SDK)_ |
690
+ | `mistral_key` | `token` | Mistral API key | _(read by SDK)_ |
691
+ | `openrouter_key` | `token` | OpenRouter API key | _(read by SDK)_ |
692
+ | `custom_key` | `token` | Custom provider API key | _(read by SDK)_ |
693
+ | `sentry_token` | `token` | Sentry auth token for error monitoring | `SENTRY_AUTH_TOKEN` env var |
694
+ | `linear_token` | `token` | Linear personal API token | `LINEAR_API_TOKEN` env var |
695
+ | `linear_oauth` | `client_id`, `client_secret`, `access_token`, `refresh_token` | Linear OAuth2 credentials | `LINEAR_CLIENT_ID`, `LINEAR_CLIENT_SECRET`, `LINEAR_ACCESS_TOKEN`, `LINEAR_REFRESH_TOKEN` env vars |
696
+ | `bugsnag_token` | `token` | Bugsnag auth token | `BUGSNAG_AUTH_TOKEN` env var |
697
+ | `netlify_token` | `token` | Netlify Personal Access Token | `NETLIFY_AUTH_TOKEN` env var |
698
+ | `mintlify_token` | `token` | Mintlify API token | `MINTLIFY_API_TOKEN` env var |
699
+ | `git_ssh` | `id_rsa`, `username`, `email` | SSH private key + git author identity | SSH key mounted as file; `GIT_SSH_COMMAND` configured automatically; `GIT_AUTHOR_NAME`/`GIT_AUTHOR_EMAIL`/`GIT_COMMITTER_NAME`/`GIT_COMMITTER_EMAIL` set from `username`/`email` |
700
+ | `x_twitter_api` | `api_key`, `api_secret`, `bearer_token`, `access_token`, `access_token_secret` | X (Twitter) API credentials | `X_API_KEY`, `X_API_SECRET`, `X_BEARER_TOKEN`, `X_ACCESS_TOKEN`, `X_ACCESS_TOKEN_SECRET` env vars |
701
+ | `aws` | `access_key_id`, `secret_access_key`, `default_region` | AWS credentials | `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, `AWS_DEFAULT_REGION` env vars |
702
+ | `reddit_oauth` | `client_id`, `client_secret`, `username`, `password`, `user_agent` | Reddit OAuth2 credentials for script apps | `REDDIT_CLIENT_ID`, `REDDIT_CLIENT_SECRET`, `REDDIT_USERNAME`, `REDDIT_PASSWORD`, `REDDIT_USER_AGENT` env vars |
703
+
704
+ ### Webhook secrets
705
+
706
+ | Type | Fields | Description |
707
+ |------|--------|-------------|
708
+ | `github_webhook_secret` | `secret` | Shared secret for GitHub webhook HMAC verification |
709
+ | `sentry_client_secret` | `secret` | Client secret for Sentry webhook verification |
710
+ | `linear_webhook_secret` | `secret` | Shared secret for Linear webhook verification |
711
+ | `mintlify_webhook_secret` | `secret` | Shared secret for Mintlify webhook verification |
712
+
713
+ Used by the gateway for payload verification — not injected into agent containers. The gateway automatically loads secrets from all credential instances and uses them to verify incoming webhook payloads.
714
+
715
+ ### Infrastructure credentials
716
+
717
+ These are used by CLI commands (provisioning, deployment) and are not injected into agent containers.
718
+
719
+ | Type | Fields | Description |
720
+ |------|--------|-------------|
721
+ | `gateway_api_key` | `key` | API key for dashboard and CLI access to the gateway |
722
+ | `vultr_api_key` | `api_key` | Vultr API key for VPS provisioning |
723
+ | `hetzner_api_key` | `api_key` | Hetzner API key for VPS provisioning |
724
+ | `cloudflare_api_token` | `token` | Cloudflare API token for DNS and TLS setup during provisioning |
725
+ | `vps_ssh` | `id_rsa` | SSH private key for VPS access (generated or selected during provisioning) |
726
+
727
+ ### Anthropic auth methods
728
+
729
+ | `authType` | Token format | Description |
730
+ |------------|-------------|-------------|
731
+ | `api_key` | `sk-ant-api-...` | Standard Anthropic API key |
732
+ | `oauth_token` | `sk-ant-oat-...` | OAuth token from `claude setup-token` |
733
+ | `pi_auth` | _(none)_ | Uses existing pi auth credentials (`~/.pi/agent/auth.json`). No credential file needed. Not supported in Docker mode. |
734
+
735
+ ## Models
736
+
737
+ Action Llama supports 8 LLM providers. Each agent can use a different provider and model — configure a project-wide default in `config.toml` under `[model]`, and override per agent in `agent-config.toml`.
738
+
739
+ | Provider | Credential | Example Models | Auth Types |
740
+ |----------|-----------|---------------|------------|
741
+ | `anthropic` | `anthropic_key` | `claude-opus-4-20250514`, `claude-sonnet-4-20250514`, `claude-haiku-3-5-20241022` | `api_key`, `oauth_token`, `pi_auth` |
742
+ | `openai` | `openai_key` | `gpt-4o`, `gpt-4o-mini`, `gpt-4-turbo`, `o1-preview`, `o1-mini` | `api_key` |
743
+ | `groq` | `groq_key` | `llama-3.3-70b-versatile` | `api_key` |
744
+ | `google` | `google_key` | `gemini-2.0-flash-exp` | `api_key` |
745
+ | `xai` | `xai_key` | `grok-beta` | `api_key` |
746
+ | `mistral` | `mistral_key` | `mistral-large-2411` | `api_key` |
747
+ | `openrouter` | `openrouter_key` | `anthropic/claude-3.5-sonnet` (provider/model format) | `api_key` |
748
+ | `custom` | `custom_key` | _(any)_ | `api_key` |
749
+
750
+ ### Thinking levels (Anthropic only)
751
+
752
+ | Level | Description |
753
+ |-------|-------------|
754
+ | `off` | No extended thinking |
755
+ | `minimal` | Minimal reasoning |
756
+ | `low` | Light reasoning |
757
+ | `medium` | Balanced (recommended) |
758
+ | `high` | Deep reasoning |
759
+ | `xhigh` | Maximum reasoning budget |
760
+
761
+ If omitted, thinking is not explicitly configured. For non-Anthropic providers, `thinkingLevel` is ignored.
762
+
763
+ ### Model inheritance
764
+
765
+ Agents without a `[model]` section inherit from the project `config.toml`. If an agent defines its own `[model]`, it fully overrides the project default — there is no field-level merging between the two.
766
+
767
+ ```
768
+ config.toml → [model] provider = "anthropic", model = "claude-sonnet-4-20250514"
769
+ dev/agent-config.toml → (no [model] section — inherits Claude Sonnet)
770
+ reviewer/agent-config.toml → [model] provider = "openai", model = "gpt-4o"
771
+ devops/agent-config.toml → [model] provider = "groq", model = "llama-3.3-70b-versatile"
772
+ ```
773
+
774
+ The LLM credential does not need to be listed in the agent's `credentials` array — it is loaded automatically based on the `[model]` config.
775
+
776
+ ## Webhooks
777
+
778
+ Agents can be triggered by webhooks in addition to (or instead of) cron schedules. Four providers are supported: GitHub, Sentry, Linear, and Mintlify.
779
+
780
+ ### Defining webhook sources
781
+
782
+ Webhook sources are defined once in the project's `config.toml`. Each source has a name, a provider type, and an optional credential for signature validation:
783
+
784
+ ```toml
785
+ [webhooks.my-github]
786
+ type = "github"
787
+ credential = "MyOrg" # credential instance name (github_webhook_secret:MyOrg)
788
+
789
+ [webhooks.my-sentry]
790
+ type = "sentry"
791
+ credential = "SentryProd" # credential instance name (sentry_client_secret:SentryProd)
792
+
793
+ [webhooks.my-linear]
794
+ type = "linear"
795
+ credential = "LinearMain" # credential instance name (linear_webhook_secret:LinearMain)
796
+
797
+ [webhooks.my-mintlify]
798
+ type = "mintlify"
799
+ credential = "MintlifyMain" # credential instance name (mintlify_webhook_secret:MintlifyMain)
800
+ ```
801
+
802
+ ### Runtime flow
803
+
804
+ 1. The gateway receives a webhook POST request at `/webhooks/<type>` (e.g. `/webhooks/github`)
805
+ 2. It verifies the payload signature using secrets loaded from the credential instances defined in `config.toml` webhook sources
806
+ 3. It parses the event into a `WebhookContext` (source, event, action, repo, etc.)
807
+ 4. It matches the context against each agent's webhook triggers (AND logic — all specified filter fields must match; omitted fields are not checked)
808
+ 5. Matching agents are triggered with the webhook context injected into their prompt as a `<webhook-trigger>` block
809
+
810
+ ### Webhook endpoints
811
+
812
+ | Provider | Endpoint |
813
+ |----------|----------|
814
+ | GitHub | `/webhooks/github` |
815
+ | Sentry | `/webhooks/sentry` |
816
+ | Linear | `/webhooks/linear` |
817
+ | Mintlify | `/webhooks/mintlify` |
818
+
819
+ ### Queue behavior
820
+
821
+ If all runners for a matching agent are busy, webhook events are queued (up to `workQueueSize`, default: 100). Scheduled runs are skipped if all busy.
822
+
823
+ ### Hybrid agents
824
+
825
+ Agents can have both `schedule` and `webhooks`. Scheduled runs poll for work proactively; webhook runs respond to events immediately.
826
+
827
+ ## Agent Commands
828
+
829
+ Agents have access to shell commands for signaling the scheduler, calling other agents, and coordinating with resource locks. These commands are installed at `/app/bin/` (baked into the Docker image) and added to `PATH` at container startup. The preamble skill blocks (see Prompt Assembly above) teach agents the commands and their response formats.
830
+
831
+ ### Signal commands
832
+
833
+ Signal commands write signal files that the scheduler reads after the session ends.
834
+
835
+ #### `al-rerun`
836
+
837
+ Request an immediate rerun to drain remaining backlog. Without this, the scheduler treats the run as complete and waits for the next scheduled tick.
838
+
839
+ ```bash
840
+ al-rerun
841
+ ```
842
+
843
+ - Only applies to **scheduled** runs. Webhook-triggered and agent-called runs do not re-run.
844
+ - Reruns continue until the agent completes without calling `al-rerun`, hits an error, or reaches the `maxReruns` limit (default: 10).
845
+
846
+ #### `al-status "<text>"`
847
+
848
+ Update the status text shown in the TUI and web dashboard.
849
+
850
+ ```bash
851
+ al-status "reviewing PR #42"
852
+ al-status "found 3 issues to work on"
853
+ ```
854
+
855
+ #### `al-return "<value>"`
856
+
857
+ Return a value to the calling agent. Used when this agent was invoked via `al-call`.
858
+
859
+ ```bash
860
+ al-return "PR looks good. Approved with minor suggestions."
861
+ al-return '{"approved": true, "comments": 2}'
862
+ ```
863
+
864
+ For multiline results, pipe via stdin:
865
+
866
+ ```bash
867
+ echo "Line 1\nLine 2" | al-return
868
+ ```
869
+
870
+ The calling agent receives this value when it calls `al-wait`.
871
+
872
+ #### `al-exit [code]`
873
+
874
+ Terminate the agent with an exit code indicating an unrecoverable error. Defaults to exit code 15.
875
+
876
+ ```bash
877
+ al-exit # exit code 15
878
+ al-exit 1 # exit code 1
879
+ ```
880
+
881
+ Standard exit codes: 10 (auth failure), 11 (permission denied), 12 (rate limited), 13 (config error), 14 (dependency error), 15 (unrecoverable), 16 (user abort).
882
+
883
+ ### Call commands
884
+
885
+ Agent-to-agent calls allow agents to delegate work and collect results. These commands require the gateway (`GATEWAY_URL` must be set).
886
+
887
+ #### `al-call <agent>`
888
+
889
+ Call another agent. Pass context via stdin. Returns a JSON response with a `callId`.
890
+
891
+ ```bash
892
+ echo "Review PR #42 on acme/app" | al-call reviewer
893
+ ```
894
+
895
+ **Response:**
896
+
897
+ ```json
898
+ {"ok": true, "callId": "abc123"}
899
+ ```
8
900
 
9
- - `agent-config.toml` — credentials, model, schedule, webhooks, params
10
- - `ACTIONS.md` — the system prompt that defines what the agent does
11
- - `Dockerfile` (optional) — custom Docker image for this specific agent, extending the project base
901
+ **Errors:**
12
902
 
13
- ## Creating an Agent
903
+ ```json
904
+ {"ok": false, "error": "self-call not allowed"}
905
+ {"ok": false, "error": "queue full"}
906
+ ```
14
907
 
15
- 1. Create a directory for your agent under `agents/` (e.g. `agents/my-agent/`)
16
- 2. Add `agent-config.toml` with credentials, model config, and a schedule or webhook trigger
17
- 3. Add `ACTIONS.md` with the actions — step-by-step instructions the LLM follows each run
18
- 4. If your agents need shared tools beyond the base image (git, curl, openssh-client, node), edit the project `Dockerfile` at the project root. For agent-specific tools, add a `Dockerfile` to that agent's directory — see Container Isolation section below
19
- 5. Verify with `npx al stat`
20
- 6. Run with `npx al start`
908
+ #### `al-check <callId>`
21
909
 
22
- ## Credential Reference
910
+ Non-blocking status check on a call. Never blocks.
23
911
 
24
- Credentials are managed by the user via `al doctor` or `al creds add` and stored in `~/.action-llama/credentials/<type>/<instance>/<field>`.
912
+ ```bash
913
+ al-check abc123
914
+ ```
25
915
 
26
- ### Credential references
916
+ **Response:**
27
917
 
28
- Each agent declares the credentials it needs. References resolve deterministically from the ref string — no filesystem probing.
918
+ ```json
919
+ {"status": "pending"}
920
+ {"status": "running"}
921
+ {"status": "completed", "returnValue": "PR approved."}
922
+ {"status": "error", "error": "timeout"}
923
+ ```
29
924
 
30
- - `"github_token"` `github_token/default/` (default instance)
31
- - `"git_ssh:botty"` → `git_ssh/botty/` (named instance)
925
+ #### `al-wait <callId> [...] [--timeout N]`
32
926
 
33
- Reference credentials in agent config:
927
+ Wait for one or more calls to complete. Polls every 5 seconds. Default timeout: 900 seconds.
34
928
 
35
- ```toml
36
- credentials = ["github_token", "git_ssh"] # both use "default" instance
37
- credentials = ["github_token", "git_ssh:botty"] # git_ssh uses "botty" instance
38
- ```
39
-
40
- | Type | What it is | Fields | Runtime injection | What it enables |
41
- |------|-----------|--------|-------------------|----------------|
42
- | `anthropic_key` | Anthropic API key or OAuth token | `token` | Read directly by the agent SDK (not an env var) | LLM access (Anthropic models) |
43
- | `openai_key` | OpenAI API key | `token` | Read directly by the agent SDK | LLM access (OpenAI models) |
44
- | `groq_key` | Groq API key | `token` | Read directly by the agent SDK | LLM access (Groq models) |
45
- | `google_key` | Google Gemini API key | `token` | Read directly by the agent SDK | LLM access (Gemini models) |
46
- | `xai_key` | xAI API key | `token` | Read directly by the agent SDK | LLM access (Grok models) |
47
- | `mistral_key` | Mistral API key | `token` | Read directly by the agent SDK | LLM access (Mistral models) |
48
- | `openrouter_key` | OpenRouter API key | `token` | Read directly by the agent SDK | LLM access (OpenRouter multi-provider) |
49
- | `custom_key` | Custom provider API key | `token` | Read directly by the agent SDK | LLM access (custom providers) |
50
- | `github_token` | GitHub PAT (repo + workflow scopes) | `token` | `GITHUB_TOKEN` and `GH_TOKEN` env vars | `gh` CLI, `git` over HTTPS, GitHub API |
51
- | `git_ssh` | SSH private key + git identity | `id_rsa`, `username`, `email` | SSH key mounted as file; `GIT_SSH_COMMAND` configured automatically; `GIT_AUTHOR_NAME`/`GIT_AUTHOR_EMAIL`/`GIT_COMMITTER_NAME`/`GIT_COMMITTER_EMAIL` set from `username`/`email` | `git clone`/`push` over SSH — **required for pushing to repos** |
52
- | `sentry_token` | Sentry auth token | `token` | `SENTRY_AUTH_TOKEN` env var | Sentry API via `curl` |
53
- | `github_webhook_secret` | Shared HMAC secret | `secret` | Used by gateway only (not injected into agents) | Validates GitHub webhook payloads |
54
- | `sentry_client_secret` | Sentry client secret | `secret` | Used by gateway only (not injected into agents) | Validates Sentry webhook payloads |
929
+ ```bash
930
+ al-wait abc123 --timeout 600
931
+ al-wait abc123 def456 --timeout 300
932
+ ```
55
933
 
56
- **IMPORTANT:** Agents MUST NEVER ask users for credentials directly (API keys, tokens, passwords, etc.). Agents MUST NEVER run `al doctor` or interact with the credential system on behalf of the user. If a credential is missing at runtime, the agent should report the error and stop — the user will run `al doctor` and `al start` themselves.
934
+ **Response:**
57
935
 
58
- ## Runtime Context
936
+ ```json
937
+ {
938
+ "abc123": {"status": "completed", "returnValue": "PR approved."},
939
+ "def456": {"status": "completed", "returnValue": "Tests pass."}
940
+ }
941
+ ```
59
942
 
60
- Every agent prompt has these XML blocks injected automatically at runtime:
943
+ #### Complete call example
61
944
 
62
- ### `<agent-config>`
945
+ ```bash
946
+ # Fire multiple calls
947
+ REVIEW_ID=$(echo "Review PR #42 on acme/app" | al-call reviewer | jq -r .callId)
948
+ TEST_ID=$(echo "Run full test suite for acme/app" | al-call tester | jq -r .callId)
63
949
 
64
- JSON object containing the agent's custom `[params]` from `agent-config.toml`. Example:
950
+ # ... do other work ...
65
951
 
66
- ```json
67
- {"repos":["acme/app"],"triggerLabel":"agent","assignee":"bot-user"}
952
+ # Collect results
953
+ RESULTS=$(al-wait "$REVIEW_ID" "$TEST_ID" --timeout 600)
954
+ echo "$RESULTS" | jq ".\"$REVIEW_ID\".returnValue"
955
+ echo "$RESULTS" | jq ".\"$TEST_ID\".returnValue"
68
956
  ```
69
957
 
70
- (In this example, `repos` is a custom param defined in `[params]` — not a built-in field.)
958
+ #### Call rules
959
+
960
+ - An agent cannot call itself (self-calls are rejected)
961
+ - If all runners for the target agent are busy, the call is queued (up to `workQueueSize`, default: 100)
962
+ - Call chains are allowed (A calls B, B calls C) up to `maxCallDepth` (default: 3)
963
+ - Called runs do not re-run — they respond to the single call
964
+ - The called agent receives an `<agent-call>` block with the caller name and context
965
+ - To return a value, the called agent uses `al-return`
966
+
967
+ ### Lock commands
71
968
 
72
- ### `<credential-context>`
969
+ Resource locks prevent multiple agent instances from working on the same resource. The underlying shell commands are `rlock`, `runlock`, and `rlock-heartbeat`.
73
970
 
74
- Lists which env vars and tools are available based on the agent's `credentials` array. Includes anti-exfiltration policy. The agent can rely on env vars like `GITHUB_TOKEN`, `GH_TOKEN`, `SENTRY_AUTH_TOKEN` being already set — it does NOT need to set them.
971
+ #### `rlock`
75
972
 
76
- ### `<webhook-trigger>` (webhook runs only)
973
+ Acquire an exclusive lock on a resource.
77
974
 
78
- JSON object with the webhook event details. Only present when the agent is triggered by a webhook (not on scheduled runs). Schema:
975
+ ```bash
976
+ rlock "github issue acme/app#42"
977
+ ```
978
+
979
+ **Success:**
79
980
 
80
981
  ```json
81
- {
82
- "source": "github",
83
- "event": "issues",
84
- "action": "labeled",
85
- "repo": "acme/app",
86
- "number": 42,
87
- "title": "Add dark mode",
88
- "body": "Issue description...",
89
- "url": "https://github.com/acme/app/issues/42",
90
- "author": "user",
91
- "assignee": "bot-user",
92
- "labels": ["agent"],
93
- "branch": null,
94
- "comment": null,
95
- "sender": "user",
96
- "timestamp": "2025-01-15T10:30:00Z"
97
- }
982
+ {"ok": true}
98
983
  ```
99
984
 
100
- ### `<agent-trigger>` (agent-triggered runs only)
985
+ **Already held:**
101
986
 
102
- JSON object with the source agent name and context. Only present when the agent was triggered by another agent via a `[TRIGGER]` signal. Schema:
987
+ ```json
988
+ {"ok": false, "holder": "dev-abc123", "heldSince": "2025-01-15T10:30:00Z"}
989
+ ```
990
+
991
+ **Already holding another lock:**
103
992
 
104
993
  ```json
105
- {
106
- "source": "dev",
107
- "context": "I just opened PR #42 on acme/app. Please review it."
108
- }
994
+ {"ok": false, "reason": "already holding lock on ..."}
109
995
  ```
110
996
 
111
- ## Signals
997
+ **Deadlock detected:**
112
998
 
113
- Signals are shell commands you run to communicate back to the scheduler. They write signal files to `$AL_SIGNAL_DIR` and optionally POST to the gateway for real-time TUI updates.
999
+ ```json
1000
+ {"ok": false, "reason": "possible deadlock detected", "cycle": ["dev-abc", "github pr acme/app#10", "dev-def", "deploy api-prod"]}
1001
+ ```
114
1002
 
115
- ### `al-rerun`
1003
+ #### `runlock`
116
1004
 
117
- Request an immediate rerun to drain remaining backlog.
1005
+ Release a lock. Only the holder can release.
118
1006
 
1007
+ ```bash
1008
+ runlock "github issue acme/app#42"
119
1009
  ```
120
- al-rerun
1010
+
1011
+ **Success:**
1012
+
1013
+ ```json
1014
+ {"ok": true}
121
1015
  ```
122
1016
 
123
- Run this when you completed work (processed an issue, merged a PR, etc.) and there may be more items to handle. The scheduler will re-run you immediately, up to `maxReruns` times (default: 10). Without `al-rerun`, the scheduler treats the run as complete and waits for the next scheduled tick. This is the safe default — errors, rate limits, and empty runs won't cause unwanted reruns.
1017
+ **Not holder:**
1018
+
1019
+ ```json
1020
+ {"ok": false, "reason": "not the lock holder"}
1021
+ ```
124
1022
 
125
- ### `al-status "<text>"`
1023
+ #### `rlock-heartbeat`
126
1024
 
127
- Send a live status update to the TUI and logs.
1025
+ Reset the TTL on a held lock. Use during long-running work to prevent the lock from expiring.
128
1026
 
1027
+ ```bash
1028
+ rlock-heartbeat "github issue acme/app#42"
129
1029
  ```
130
- al-status "reviewing PR #42"
131
- al-status "deploying api-prod"
1030
+
1031
+ **Success:**
1032
+
1033
+ ```json
1034
+ {"ok": true, "expiresAt": "2025-01-15T11:00:00Z"}
132
1035
  ```
133
1036
 
134
- Run at natural milestones so the operator can see what you're doing in real time.
1037
+ #### Lock behavior
135
1038
 
136
- ### `al-return`
1039
+ - Each container gets a unique per-run secret. Lock requests are authenticated with this secret, so only the container that acquired a lock can release or heartbeat it.
1040
+ - When a container exits — whether it finishes successfully, hits an error, or times out — all of its locks are released automatically by the scheduler.
1041
+ - An agent can hold **at most one lock at a time**. Release your current lock before acquiring another.
1042
+ - Locks expire automatically after `lockTimeout` seconds (default: 1800 / 30 minutes) if not refreshed via heartbeat.
1043
+ - Use descriptive keys: `"github issue acme/app#42"`, `"deploy api-prod"`
137
1044
 
138
- Return a value when you were called by another agent via `al-call`. The calling agent can retrieve this value using `al-check` or `al-wait`.
1045
+ #### Lock graceful degradation
139
1046
 
140
- ```
141
- al-return "PR looks good. Approved with minor suggestions on error handling."
1047
+ When `GATEWAY_URL` is not set (e.g. non-containerized local runs), lock commands exit 0 with `{"ok":true}` — graceful degradation so agents work without a gateway.
1048
+
1049
+ Call commands (`al-call`, `al-check`, `al-wait`) exit 5 when `GATEWAY_URL` is not set — they require a gateway.
1050
+
1051
+ ## CLI Commands
1052
+
1053
+ ### `al new <name>`
1054
+
1055
+ Creates a new Action Llama project. Runs interactive setup to configure credentials and LLM defaults.
1056
+
1057
+ ```bash
1058
+ npx @action-llama/action-llama new my-project
142
1059
  ```
143
1060
 
144
- For multiline results, pipe via stdin:
1061
+ Creates:
1062
+ - `my-project/package.json` — with `@action-llama/action-llama` dependency
1063
+ - `my-project/.gitignore`
1064
+ - `my-project/.workspace/` — runtime state directory
1065
+ - Credential files in `~/.action-llama/credentials/`
1066
+
1067
+ ### `al doctor`
1068
+
1069
+ Checks all agent credentials and interactively prompts for any that are missing. Discovers agents in the project, collects their credential requirements (plus any webhook secret credentials), and ensures each one exists on disk. Also generates a gateway API key if one doesn't exist yet.
1070
+
1071
+ Additionally validates webhook trigger field configurations to catch common errors like using `repository` instead of `repos`, misspelled field names, or invalid field types.
145
1072
 
1073
+ ```bash
1074
+ al doctor
1075
+ al doctor -E production
146
1076
  ```
147
- echo "Line 1\nLine 2" | al-return
1077
+
1078
+ | Option | Description |
1079
+ |--------|-------------|
1080
+ | `-p, --project <dir>` | Project directory (default: `.`) |
1081
+ | `-E, --env <name>` | Environment name — pushes credentials to server and reconciles IAM |
1082
+
1083
+ ### `al run <agent>`
1084
+
1085
+ Manually triggers a single agent run. The agent runs once and the process exits when it completes. Useful for testing, debugging, or one-off runs without starting the full scheduler.
1086
+
1087
+ ```bash
1088
+ al run dev
1089
+ al run reviewer -p ./my-project
1090
+ al run dev -E production
1091
+ al run dev --headless
148
1092
  ```
149
1093
 
150
- ### `al-exit [code]`
1094
+ | Option | Description |
1095
+ |--------|-------------|
1096
+ | `-p, --project <dir>` | Project directory (default: `.`) |
1097
+ | `-E, --env <name>` | Environment name |
1098
+ | `-H, --headless` | Non-interactive mode (no TUI, no credential prompts) |
1099
+
1100
+ ### `al start`
151
1101
 
152
- Terminate the agent with an exit code, indicating an unrecoverable error.
1102
+ Starts the scheduler. Runs all agents on their configured schedules and listens for webhooks.
153
1103
 
1104
+ ```bash
1105
+ al start
1106
+ al start -w # Enable web dashboard
1107
+ al start -e # VPS deployment: expose gateway publicly
1108
+ al start --port 3000 # Custom gateway port
1109
+ al start -H # Headless (no TUI)
154
1110
  ```
155
- al-exit 10 # Authentication failure
156
- al-exit 11 # Permission denied
157
- al-exit # Defaults to 15 (unrecoverable error)
1111
+
1112
+ | Option | Description |
1113
+ |--------|-------------|
1114
+ | `-p, --project <dir>` | Project directory (default: `.`) |
1115
+ | `-E, --env <name>` | Environment name |
1116
+ | `-w, --web-ui` | Enable web dashboard |
1117
+ | `-e, --expose` | Bind gateway to `0.0.0.0` (public) while keeping local-mode features |
1118
+ | `-H, --headless` | Non-interactive mode (no TUI, no credential prompts) |
1119
+ | `--port <number>` | Gateway port (overrides `[gateway].port` in config) |
1120
+
1121
+ ### `al stop`
1122
+
1123
+ Stops the scheduler and clears all pending agent work queues. Sends a stop signal to the gateway. In-flight runs continue until they finish, but no new runs will start.
1124
+
1125
+ ```bash
1126
+ al stop
1127
+ al stop -E production
158
1128
  ```
159
1129
 
160
- Standard exit codes: 10 (auth failure), 11 (permission denied), 12 (rate limited), 13 (config error), 14 (dependency error), 15 (unrecoverable), 16 (user abort).
1130
+ | Option | Description |
1131
+ |--------|-------------|
1132
+ | `-p, --project <dir>` | Project directory (default: `.`) |
1133
+ | `-E, --env <name>` | Environment name |
1134
+
1135
+ ### `al stat`
1136
+
1137
+ Shows status of all discovered agents in the project. Displays each agent's schedule, credentials, webhook configuration, and queue depth.
1138
+
1139
+ ```bash
1140
+ al stat
1141
+ al stat -E production
1142
+ ```
161
1143
 
162
- ### Agent-to-agent calls
1144
+ | Option | Description |
1145
+ |--------|-------------|
1146
+ | `-p, --project <dir>` | Project directory (default: `.`) |
1147
+ | `-E, --env <name>` | Environment name |
163
1148
 
164
- Call other agents and retrieve their results using shell commands (Docker mode only):
1149
+ ### `al logs <agent>`
165
1150
 
166
- - **`al-call <agent>`** Call another agent. Pass context via stdin. Returns `{"ok":true,"callId":"..."}`.
167
- - **`al-check <callId>`** — Non-blocking status check. Returns `{"status":"pending|running|completed|error", ...}`.
168
- - **`al-wait <callId> [...] [--timeout N]`** — Wait for calls to complete (default timeout: 900s).
1151
+ View log files for a specific agent.
169
1152
 
170
- ```sh
171
- CALL_ID=$(echo "Review PR #42 on acme/app" | al-call reviewer | jq -r .callId)
172
- # ... continue working ...
173
- RESULT=$(al-wait "$CALL_ID")
1153
+ ```bash
1154
+ al logs dev
1155
+ al logs dev -n 100 # Show last 100 entries
1156
+ al logs dev -f # Follow/tail mode
1157
+ al logs dev -d 2025-01-15 # Specific date
1158
+ al logs dev -r # Raw JSON log output
1159
+ al logs dev -i abc123 # Specific instance
1160
+ al logs dev -E production # Remote agent logs
174
1161
  ```
175
1162
 
176
- Rules:
177
- - An agent cannot call itself
178
- - If the target is busy, the call is queued until a runner frees up
179
- - Call chains are limited by `maxCallDepth` in `config.toml` (default: 3)
1163
+ | Option | Description |
1164
+ |--------|-------------|
1165
+ | `-p, --project <dir>` | Project directory (default: `.`) |
1166
+ | `-E, --env <name>` | Environment name |
1167
+ | `-n, --lines <N>` | Number of log entries (default: 50) |
1168
+ | `-f, --follow` | Tail mode — watch for new entries |
1169
+ | `-d, --date <YYYY-MM-DD>` | View a specific date's log file |
1170
+ | `-r, --raw` | Raw JSON log output (no formatting) |
1171
+ | `-i, --instance <id>` | Filter to a specific instance ID |
180
1172
 
181
- ### Combining signals
1173
+ ### `al pause [name]`
182
1174
 
183
- You can use multiple signal commands in one run. For example, several `al-status` updates as you work, `al-call` to delegate work, and `al-rerun` if there's more work to do.
1175
+ Pause the scheduler or a single agent. Without a name, pauses the entire scheduler — all cron jobs stop firing. With a name, pauses that agent its cron job stops firing and webhook events are ignored. In-flight runs continue until they finish. Requires the gateway.
184
1176
 
185
- ## Webhook Reference
1177
+ ```bash
1178
+ al pause # Pause the scheduler
1179
+ al pause dev # Pause a single agent
1180
+ al pause dev -E production
1181
+ ```
186
1182
 
187
- ### How webhooks work
1183
+ | Option | Description |
1184
+ |--------|-------------|
1185
+ | `-p, --project <dir>` | Project directory (default: `.`) |
1186
+ | `-E, --env <name>` | Environment name |
188
1187
 
189
- 1. Webhook sources are defined in the project's `config.toml` under `[webhooks.<name>]` with a provider type and optional credential
190
- 2. The gateway receives an HTTP POST at `/webhooks/github` or `/webhooks/sentry`
191
- 3. The payload is validated using the credential's HMAC secret (e.g. `github_webhook_secret` for GitHub)
192
- 4. The gateway matches the event against all agents' `[[webhooks]]` entries (AND logic — all specified fields must match; omitted fields are not checked)
193
- 5. Matching agents are triggered with a `<webhook-trigger>` block injected into their prompt
1188
+ ### `al resume [name]`
194
1189
 
195
- ### Defining webhook sources in `config.toml`
1190
+ Resume the scheduler or a single agent. Without a name, resumes the entire scheduler. With a name, resumes that agent — its cron job resumes firing and webhooks are accepted again.
196
1191
 
197
- Webhook sources are defined once at the project level. Each source has a name, provider type, and optional credential instance for HMAC validation:
1192
+ ```bash
1193
+ al resume # Resume the scheduler
1194
+ al resume dev # Resume a single agent
1195
+ al resume dev -E production
1196
+ ```
198
1197
 
199
- ```toml
200
- [webhooks.my-github]
201
- type = "github"
202
- credential = "MyOrg" # credential instance name (github_webhook_secret:MyOrg)
1198
+ | Option | Description |
1199
+ |--------|-------------|
1200
+ | `-p, --project <dir>` | Project directory (default: `.`) |
1201
+ | `-E, --env <name>` | Environment name |
203
1202
 
204
- [webhooks.my-sentry]
205
- type = "sentry"
206
- credential = "SentryProd" # credential instance name (sentry_client_secret:SentryProd)
1203
+ ### `al kill <target>`
207
1204
 
208
- [webhooks.unsigned-github]
209
- type = "github" # no credential — accepts unsigned webhooks
1205
+ Kill an agent (all running instances) or a single instance by ID. Tries the target as an agent name first; if not found, falls back to instance ID. This does **not** pause the agent — if it has a schedule, a new run will start at the next cron tick. To fully stop an agent, pause it first, then kill.
1206
+
1207
+ ```bash
1208
+ al kill dev # Kill all instances of an agent
1209
+ al kill my-agent-abc123 # Kill a single instance by ID
1210
+ al kill dev -E production
210
1211
  ```
211
1212
 
212
- ### Agent webhook filters
1213
+ | Option | Description |
1214
+ |--------|-------------|
1215
+ | `-p, --project <dir>` | Project directory (default: `.`) |
1216
+ | `-E, --env <name>` | Environment name |
213
1217
 
214
- Agents reference a webhook source by name and add filters:
1218
+ ### `al chat [agent]`
215
1219
 
216
- | Field | Type | Description |
217
- |-------|------|-------------|
218
- | `source` | string | Name of a webhook source from `config.toml` (required) |
219
- | `repos` | string[] | Filter to specific repos (owner/repo format) |
220
- | `events` | string[] | Event types: `issues`, `pull_request`, `push`, `issue_comment`, etc. |
221
- | `actions` | string[] | Event actions: `opened`, `labeled`, `closed`, `synchronize`, etc. |
222
- | `labels` | string[] | Only trigger when the issue/PR has ALL of these labels |
223
- | `assignee` | string | Only trigger when assigned to this user |
224
- | `author` | string | Only trigger for events by this author |
225
- | `branches` | string[] | Only trigger for pushes/PRs on these branches |
226
- | `resources` | string[] | Sentry: `error`, `event_alert`, `metric_alert`, `issue`, `comment` |
1220
+ Open an interactive console. Without an agent name, opens the project-level console for creating and managing agents. With an agent name, opens an interactive session scoped to that agent's environment — credentials are loaded and injected as environment variables (e.g. `GITHUB_TOKEN`, `GIT_SSH_COMMAND`), and the working directory is set to the agent's directory.
227
1221
 
228
- ### GitHub webhook setup
1222
+ ```bash
1223
+ al chat # project-level console
1224
+ al chat dev # interactive session with dev agent's credentials
1225
+ ```
229
1226
 
230
- In your GitHub repo settings, add a webhook:
231
- - **Payload URL:** `http://<your-host>:8080/webhooks/github`
232
- - **Content type:** `application/json`
233
- - **Secret:** the same secret stored as the `github_webhook_secret` credential
1227
+ | Option | Description |
1228
+ |--------|-------------|
1229
+ | `[agent]` | Agent name — loads its credentials and environment |
1230
+ | `-p, --project <dir>` | Project directory (default: `.`) |
234
1231
 
235
- ### TOML syntax for webhooks
1232
+ When running in agent mode, the command probes the gateway and warns if it is not reachable:
236
1233
 
237
- Each webhook is a separate `[[webhooks]]` block (double brackets = array of tables). The `source` field references a webhook source defined in `config.toml`:
1234
+ ```
1235
+ Warning: No gateway detected at http://localhost:8080. Resource locks, agent calls, and signals are unavailable.
1236
+ Start the scheduler with `al start` to enable these features.
1237
+ ```
238
1238
 
239
- ```toml
240
- # Each [[webhooks]] references a source from config.toml
241
- [[webhooks]]
242
- source = "my-github"
243
- events = ["issues"]
244
- actions = ["labeled"]
245
- labels = ["agent"]
1239
+ The agent's ACTIONS.md is loaded as reference context but is **not** auto-executed — you drive the session interactively.
246
1240
 
247
- [[webhooks]]
248
- source = "my-github"
249
- events = ["pull_request"]
250
- # repos = ["my-org/specific-repo"] # optional — filter to specific repos
1241
+ ### `al push [agent]`
251
1242
 
252
- [[webhooks]]
253
- source = "my-sentry"
254
- resources = ["error", "event_alert"]
1243
+ Deploy your project to a server over SSH. Requires a `[server]` section in your environment file.
1244
+
1245
+ ```bash
1246
+ al push -E production # Full project push
1247
+ al push dev -E production # Push only the dev agent (hot-reloaded)
1248
+ al push --dry-run -E production # Preview what would be synced
1249
+ al push --creds-only -E production # Sync only credentials
255
1250
  ```
256
1251
 
257
- ## `agent-config.toml` Complete Reference
1252
+ Without an agent name, pushes the entire project and can restart the remote service. With an agent name, pushes only that agent's files and credentials — the running scheduler detects the change and hot-reloads the agent without a full restart.
258
1253
 
259
- The config file uses TOML syntax. The agent name is derived from the directory name — do not include it in the config.
1254
+ | Option | Description |
1255
+ |--------|-------------|
1256
+ | `[agent]` | Agent name — push only this agent (hot-reloaded, no restart) |
1257
+ | `-p, --project <dir>` | Project directory (default: `.`) |
1258
+ | `-E, --env <name>` | Environment with `[server]` config |
1259
+ | `--dry-run` | Show what would be synced without making changes |
1260
+ | `--no-creds` | Skip credential sync |
1261
+ | `--creds-only` | Sync only credentials (skip project files) |
1262
+ | `--files-only` | Sync only project files (skip credentials) |
1263
+ | `-a, --all` | Sync project files, credentials, and restart service |
1264
+ | `--force-install` | Force `npm install` even if dependencies appear unchanged |
260
1265
 
261
- ### Minimal example (webhook-driven)
1266
+ ### Environment commands
262
1267
 
263
- ```toml
264
- credentials = ["github_token", "git_ssh"]
1268
+ #### `al env init <name>`
265
1269
 
266
- [[webhooks]]
267
- source = "my-github"
268
- events = ["issues"]
269
- actions = ["labeled"]
270
- labels = ["agent"]
1270
+ Create a new environment configuration file at `~/.action-llama/environments/<name>.toml`.
271
1271
 
272
- [params]
273
- triggerLabel = "agent"
274
- assignee = "your-github-username"
1272
+ ```bash
1273
+ al env init production --type server
275
1274
  ```
276
1275
 
277
- The `[model]` section is **optional** — agents inherit the default model from the project's `config.toml`. Only add `[model]` to an agent config if you want to override the default (e.g. use a different model or thinking level for that specific agent).
1276
+ | Option | Description |
1277
+ |--------|-------------|
1278
+ | `--type <type>` | Environment type: `server` |
278
1279
 
279
- ### Full example (webhooks + params + model override + optional schedule)
1280
+ #### `al env list`
280
1281
 
281
- ```toml
282
- credentials = ["github_token", "git_ssh", "sentry_token"]
283
- # schedule = "*/5 * * * *" # Optional: for scheduled polling in addition to webhooks
1282
+ List all configured environments.
284
1283
 
285
- # Optional: override the project default model for this agent
286
- [model]
287
- provider = "anthropic"
288
- model = "claude-sonnet-4-20250514"
289
- thinkingLevel = "medium"
290
- authType = "api_key"
1284
+ #### `al env show <name>`
291
1285
 
292
- [[webhooks]]
293
- source = "my-github"
294
- events = ["issues"]
295
- actions = ["labeled"]
296
- labels = ["agent"]
1286
+ Display the contents of an environment configuration file.
297
1287
 
298
- [[webhooks]]
299
- source = "my-sentry"
300
- resources = ["error", "event_alert"]
1288
+ #### `al env set [name]`
301
1289
 
302
- [params]
303
- triggerLabel = "agent"
304
- assignee = "bot-user"
305
- sentryOrg = "acme"
306
- sentryProjects = ["web-app", "api"]
307
- # repos = ["fallback/repo"] # Optional: only needed if using schedule without webhook repo context
308
- ```
1290
+ Bind the current project to an environment by writing the environment name to `.env.toml`. Omit the name to unbind.
309
1291
 
310
- ### Field reference
1292
+ ```bash
1293
+ al env set production # Bind project to "production"
1294
+ al env set # Unbind project from any environment
1295
+ ```
311
1296
 
312
- | Field | Type | Required | Description |
313
- |-------|------|----------|-------------|
314
- | `credentials` | string[] | Yes | Credential refs: `"type"` for default instance, `"type:instance"` for named instance |
315
- | `scale` | number | No | Number of concurrent runners (default: 1). Set to `0` to disable the agent |
316
- | `schedule` | string | No* | Cron expression (e.g. "*/5 * * * *") |
317
- | `model` | table | No | LLM model config — omit to inherit from project `config.toml` |
318
- | `model.provider` | string | Yes* | "anthropic", "openai", "groq", "google", "xai", "mistral", "openrouter", or "custom" |
319
- | `model.model` | string | Yes* | Model ID (e.g. "claude-sonnet-4-20250514") |
320
- | `model.thinkingLevel` | string | No | off \| minimal \| low \| medium \| high \| xhigh (only relevant for models with reasoning support, e.g. Claude Sonnet/Opus; omit for other models) |
321
- | `model.authType` | string | Yes* | api_key \| oauth_token \| pi_auth |
322
- | `webhooks[].source` | string | Yes | Name of a webhook source from `config.toml` |
323
- | `webhooks[].repos` | string[] | No | Filter to specific repos |
324
- | `webhooks[].events` | string[] | No | GitHub event types: issues, pull_request, push |
325
- | `webhooks[].actions` | string[] | No | GitHub actions: opened, labeled, closed |
326
- | `webhooks[].labels` | string[] | No | Only trigger for issues/PRs with these labels |
327
- | `webhooks[].resources` | string[] | No | Sentry resources: error, event_alert, metric_alert, issue, comment |
328
- | `params.*` | any | No | Custom key-value pairs injected into the prompt |
329
-
330
- *At least one of `schedule` or `webhooks` is required. *Required within `[model]` if the agent defines its own model block.
331
-
332
- ### TOML syntax reminders
1297
+ | Option | Description |
1298
+ |--------|-------------|
1299
+ | `-p, --project <dir>` | Project directory (default: `.`) |
333
1300
 
334
- - Strings: `key = "value"`
335
- - Arrays: `key = ["a", "b"]`
336
- - Tables (objects): `[tableName]` on its own line, followed by key-value pairs
337
- - Array of tables: `[[arrayName]]` on its own line — each block is one entry in the array
338
- - Comments: `# comment`
1301
+ #### `al env check <name>`
339
1302
 
340
- ## Example Agent
1303
+ Verify that an environment is provisioned and configured correctly. Checks SSH connectivity, Docker availability, and server readiness.
341
1304
 
342
- **Agent actions must be detailed and prescriptive with step-by-step commands. Copy this example and customize rather than writing from scratch.**
1305
+ #### `al env prov [name]`
343
1306
 
344
- The following is a complete, working ACTIONS.md for a developer agent. Use it as a template for all new agents:
1307
+ Provision a new VPS and save it as an environment. Supports Vultr and Hetzner. If the name is omitted, you'll be prompted for one.
345
1308
 
346
- ```markdown
347
- # Developer Agent
1309
+ #### `al env deprov <name>`
348
1310
 
349
- You are a developer agent. Your job is to pick up GitHub issues and implement the requested changes.
1311
+ Tear down a provisioned environment. Stops containers, cleans up remote credentials, optionally deletes DNS records, and optionally deletes the VPS instance if it was provisioned via `al env prov`.
350
1312
 
351
- Your configuration is in the \`<agent-config>\` block at the start of your prompt.
352
- Use those values for triggerLabel and assignee.
1313
+ | Option | Description |
1314
+ |--------|-------------|
1315
+ | `-p, --project <dir>` | Project directory (default: `.`) |
353
1316
 
354
- \`GITHUB_TOKEN\` is already set in your environment. Use \`gh\` CLI and \`git\` directly.
355
- (Note: \`gh\` is not in the base Docker image — this agent needs a custom Dockerfile that installs it. See Container Isolation section.)
1317
+ #### `al env logs [name]`
356
1318
 
357
- **You MUST complete ALL steps below.** Do not stop after reading the issue you must implement, commit, push, and open a PR.
1319
+ View server system logs (systemd journal) via SSH. If the name is omitted, uses the project's bound environment.
358
1320
 
359
- ## Repository Context
1321
+ ```bash
1322
+ al env logs production
1323
+ al env logs production -n 200 # Last 200 lines
1324
+ al env logs production -f # Follow mode
1325
+ ```
360
1326
 
361
- This agent infers the repository from the issue context instead of using hardcoded configuration.
1327
+ | Option | Description |
1328
+ |--------|-------------|
1329
+ | `-p, --project <dir>` | Project directory (default: `.`) |
1330
+ | `-n, --lines <N>` | Number of log lines (default: 50) |
1331
+ | `-f, --follow` | Tail mode — watch for new entries |
362
1332
 
363
- **For webhook triggers:** The repository is extracted from the \`<webhook-trigger>\` block's \`repo\` field.
1333
+ ### Credential commands
364
1334
 
365
- **For scheduled triggers:** The agent uses the \`repos\` parameter from \`<agent-config>\` as a fallback to check for work across configured repositories.
1335
+ #### `al creds ls`
366
1336
 
367
- ## Setup ensure labels exist
1337
+ Lists all stored credentials grouped by type, showing field names but not values.
368
1338
 
369
- Before looking for work, ensure the required labels exist on the target repo. The repo is determined as follows:
1339
+ #### `al creds add <ref>`
370
1340
 
371
- - **Webhook mode:** Extract repo from \`<webhook-trigger>\` JSON block
372
- - **Scheduled mode:** Use repos from \`<agent-config>\` params
1341
+ Add or update a credential. Runs the interactive prompter with validation for the credential type.
373
1342
 
374
- Run the following (these are idempotent — they succeed silently if the label already exists):
1343
+ ```bash
1344
+ al creds add github_token # adds github_token:default
1345
+ al creds add github_webhook_secret:myapp
1346
+ al creds add git_ssh:prod
1347
+ ```
375
1348
 
376
- \`\`\`
377
- # For webhook triggers, use the repo from webhook context
378
- # For scheduled triggers, iterate through configured repos
379
- gh label create "<triggerLabel>" --repo <determined-repo> --color 0E8A16 --description "Trigger label for dev agent" --force
380
- gh label create "in-progress" --repo <determined-repo> --color FBCA04 --description "Agent is working on this" --force
381
- gh label create "agent-completed" --repo <determined-repo> --color 1D76DB --description "Agent has opened a PR" --force
382
- \`\`\`
1349
+ The `<ref>` format is `type` or `type:instance`. If no instance is specified, defaults to `default`. If the credential already exists, you'll be prompted to update it.
383
1350
 
384
- ## Finding work
1351
+ #### `al creds rm <ref>`
385
1352
 
386
- **Webhook trigger:** When you receive a \`<webhook-trigger>\` block, extract the repository from the \`repo\` field and the issue details from the trigger context. Check the issue's labels and assignee against your \`triggerLabel\` and \`assignee\` params. If the issue matches (has your trigger label and is assigned to your assignee), proceed with implementation using the extracted repository. If it does not match, stop.
1353
+ Remove a credential from disk.
387
1354
 
388
- **Scheduled trigger:** If \`repos\` parameter exists in \`<agent-config>\`, run \`gh issue list --repo <repo> --label <triggerLabel> --assignee <assignee> --state open --json number,title,body,comments,labels --limit 1\` for each configured repo. If no work found in any repo, stop. If you completed work and there may be more issues to process, run \`al-rerun\`.
1355
+ ```bash
1356
+ al creds rm github_token # removes github_token:default
1357
+ al creds rm github_webhook_secret:myapp
1358
+ ```
389
1359
 
390
- ## Workflow
1360
+ #### `al creds types`
391
1361
 
392
- **Important:** First determine the target repository from the trigger context (webhook \`repo\` field or configured \`repos\` parameter).
1362
+ Browse available credential types interactively. Presents a searchable list of all built-in credential types. On selection, shows the credential's fields, environment variables, and agent context, then offers to add it immediately.
393
1363
 
394
- 1. **Claim the issue** — run \`gh issue edit <number> --repo <determined-repo> --add-label in-progress\` to mark it as claimed.
1364
+ ### Agent commands
395
1365
 
396
- 2. **Clone and branch** — run \`git clone git@github.com:<determined-repo>.git /workspace/repo && cd /workspace/repo && git checkout -b agent/<number>\`.
1366
+ #### `al agent new`
397
1367
 
398
- 3. **Understand the issue** read the title, body, and comments. Note file paths, acceptance criteria, and linked issues.
1368
+ Interactive wizard to create a new agent from a template. Prompts for agent type (dev, reviewer, devops, or custom), agent name, and then runs `al agent config` to configure the new agent.
399
1369
 
400
- 4. **Read project conventions** — in the repo, read \`ACTIONS.md\`, \`CLAUDE.md\`, \`CONTRIBUTING.md\`, and \`README.md\` if they exist. Follow any conventions found there.
1370
+ | Option | Description |
1371
+ |--------|-------------|
1372
+ | `-p, --project <dir>` | Project directory (default: `.`) |
401
1373
 
402
- 5. **Implement changes** work in the repo. Make the minimum necessary changes, follow existing patterns, and write or update tests if the project has a test suite.
1374
+ #### `al agent config <name>`
403
1375
 
404
- 6. **Validate** run the project's test suite and linters (e.g., \`npm test\`). Fix failures before proceeding.
1376
+ Interactively configure an existing agent. Opens a menu to edit each section of `agent-config.toml`: credentials, model, schedule, webhooks, and params. Runs `al doctor` on completion to validate the configuration.
405
1377
 
406
- 7. **Commit** \`git add -A && git commit -m "fix: <description> (closes #<number>)"\`
1378
+ | Option | Description |
1379
+ |--------|-------------|
1380
+ | `-p, --project <dir>` | Project directory (default: `.`) |
407
1381
 
408
- 8. **Push** — \`git push -u origin agent/<number>\`
1382
+ ### Global options
409
1383
 
410
- 9. **Create a PR** run \`gh pr create --repo <determined-repo> --head agent/<number> --base main --title "<title>" --body "Closes #<number>\n\n<description>"\`.
1384
+ These options are available on most commands:
411
1385
 
412
- 10. **Comment on the issue** — run \`gh issue comment <number> --repo <determined-repo> --body "PR created: <pr_url>"\`.
1386
+ | Option | Description |
1387
+ |--------|-------------|
1388
+ | `-p, --project <dir>` | Project directory (default: `.`) |
1389
+ | `-E, --env <name>` | Environment name (also `AL_ENV` env var or `environment` field in `.env.toml`) |
413
1390
 
414
- 11. **Mark done** — run \`gh issue edit <number> --repo <determined-repo> --remove-label in-progress --add-label agent-completed\`.
1391
+ ## Docker
415
1392
 
416
- ## Rules
1393
+ ### Image build order
417
1394
 
418
- - Work on exactly ONE issue per run
419
- - Never modify files outside the repo directory
420
- - **You MUST complete steps 7-11.** Do not stop early.
421
- - If tests fail after 2 attempts, create the PR anyway with a note about failing tests
422
- - If the issue is unclear, comment asking for clarification and stop
1395
+ ```
1396
+ al-agent:latest ← Action Llama package (automatic, built on first run)
1397
+
1398
+ al-project-base:latest ← project Dockerfile (skipped if unmodified from bare FROM)
1399
+
1400
+ al-<agent>:latest ← per-agent Dockerfile (if present)
423
1401
  ```
424
1402
 
425
- ## Container Isolation
1403
+ If the project Dockerfile is unmodified (bare `FROM al-agent:latest`), the middle layer is skipped — agents build directly on `al-agent:latest`.
426
1404
 
427
- All agents run in isolated containers with a read-only root filesystem, dropped capabilities, non-root user, and resource limits.
1405
+ ### Base image contents
1406
+
1407
+ The base image (`al-agent:latest`) is built from `node:20-alpine` and includes:
1408
+
1409
+ | Package | Purpose |
1410
+ |---------|---------|
1411
+ | `node:20-alpine` | Container entry point, pi-coding-agent SDK |
1412
+ | `git` | Clone repos, create branches, push commits |
1413
+ | `curl` | API calls (Sentry, arbitrary HTTP), anti-exfiltration shutdown |
1414
+ | `jq` | JSON processing in bash |
1415
+ | `ca-certificates` | HTTPS for git, curl, npm |
1416
+ | `openssh-client` | SSH for `GIT_SSH_COMMAND` — git clone/push over SSH |
1417
+
1418
+ The base image also copies the compiled Action Llama application (`dist/`) and installs its npm dependencies.
1419
+
1420
+ Entry point: `node /app/dist/agents/container-entry.js`
428
1421
 
429
- ### Base image
1422
+ Shell commands are baked into the image at `/app/bin/` (al-rerun, al-status, al-return, al-exit, al-call, al-check, al-wait, rlock, runlock, rlock-heartbeat).
430
1423
 
431
- The base image (`al-agent:latest`) is built automatically on first run. It includes Node.js, git, curl, openssh-client, and ca-certificates — the minimum needed for any agent.
1424
+ ### Dockerfile conventions
432
1425
 
433
- ### Project base image
1426
+ - `FROM al-agent:latest` — the build pipeline automatically rewrites the `FROM` line to point at the correct base
1427
+ - Switch to `root` for package installs, back to `node` (uid 1000) for the entry point
1428
+ - Alpine base: use `apk add --no-cache`
1429
+ - Agent images are named `al-<agent-name>:latest` (e.g. `al-dev:latest`) and are rebuilt on every `al start`
1430
+ - The build context is the Action Llama package root (not the project directory), so `COPY` paths reference the package's `dist/`, `package.json`, etc.
434
1431
 
435
- The project `Dockerfile` at the project root customizes the base image for **all** agents. It is created by `al new` and checked into git. By default it is a bare `FROM al-agent:latest` (no customizations, skipped at build time). Add shared tools here:
1432
+ #### Project Dockerfile example
436
1433
 
437
1434
  ```dockerfile
438
1435
  FROM al-agent:latest
439
1436
 
440
- RUN apk add --no-cache python3 github-cli
1437
+ # Install tools shared by all agents
1438
+ RUN apk add --no-cache python3 py3-pip github-cli
1439
+
1440
+ # Set shared environment variables
441
1441
  ENV MY_ORG=acme
442
1442
  ```
443
1443
 
444
- ### Custom agent images
445
-
446
- If a specific agent needs extra tools beyond the project base, add a `Dockerfile` to that agent's directory:
1444
+ #### Per-agent Dockerfile example
447
1445
 
448
1446
  ```dockerfile
449
1447
  FROM al-agent:latest
1448
+
450
1449
  USER root
451
1450
  RUN apk add --no-cache github-cli
452
1451
  USER node
453
1452
  ```
454
1453
 
455
- The build pipeline automatically rewrites the `FROM` line to point at the project base image. Agent images are built automatically on startup. If no per-agent `Dockerfile` is present, the agent uses the project base (or `al-agent:latest` if the project Dockerfile is unmodified).
1454
+ #### Standalone Dockerfile (full control)
1455
+
1456
+ If you need full control, the requirements are:
1457
+ 1. Node.js 20+
1458
+ 2. `/app/dist/agents/container-entry.js` must exist
1459
+ 3. `ENTRYPOINT ["node", "/app/dist/agents/container-entry.js"]`
1460
+ 4. `USER node` (uid 1000) for compatibility
1461
+
1462
+ ```dockerfile
1463
+ FROM node:20-alpine
1464
+
1465
+ RUN apk add --no-cache git curl ca-certificates openssh-client github-cli jq python3
1466
+
1467
+ COPY --from=al-agent:latest /app /app
1468
+ WORKDIR /app
1469
+
1470
+ USER node
1471
+ ENTRYPOINT ["node", "/app/dist/agents/container-entry.js"]
1472
+ ```
1473
+
1474
+ The entry point reads `AGENT_CONFIG`, `PROMPT`, `GATEWAY_URL`, and `SHUTDOWN_SECRET` from environment variables, and credentials from `/credentials/`.
456
1475
 
457
1476
  ### Container filesystem
458
1477
 
1478
+ All agents run in isolated containers with a read-only root filesystem, dropped capabilities, non-root user, and resource limits.
1479
+
459
1480
  | Path | Mode | Contents |
460
1481
  |------|------|----------|
461
1482
  | `/app` | read-only | Action Llama application + node_modules |
1483
+ | `/app/static` | read-only | Agent files baked at build time (ACTIONS.md, agent-config.json, prompt skeleton) |
1484
+ | `/app/bin` | read-only | Shell commands (al-rerun, al-status, rlock, etc.) — added to PATH at startup |
462
1485
  | `/credentials` | read-only | Mounted credential files (`/<type>/<instance>/<field>`) |
463
- | `/workspace` | read-write (tmpfs, 2GB) | Working directory — repos are cloned here |
464
- | `/tmp` | read-write (tmpfs, 512MB) | Temporary files |
465
- | `/home/node` | read-write (tmpfs, 64MB) | User home — `.ssh/` for SSH keys |
1486
+ | `/tmp` | read-write (tmpfs, 2GB) | Agent working directory — repos, scratch files, SSH keys |
1487
+ | `/workspace` | read-write (2GB) | Persistent workspace |
1488
+ | `/home/node` | read-write (64MB) | Home directory |
466
1489
 
467
1490
  ### Docker config options
468
1491
 
@@ -473,32 +1496,162 @@ These go in `config.toml` under `[local]`:
473
1496
  | `local.image` | `"al-agent:latest"` | Base Docker image name |
474
1497
  | `local.memory` | `"4g"` | Memory limit per container |
475
1498
  | `local.cpus` | `2` | CPU limit per container |
476
- | `local.timeout` | `3600` | Max container runtime in seconds |
1499
+ | `local.timeout` | `900` | Max container runtime in seconds |
1500
+
1501
+ ## Gateway API
1502
+
1503
+ The gateway is the HTTP server that runs alongside the scheduler. It handles webhooks, serves the web dashboard, and exposes control and status APIs used by CLI commands and the dashboard.
1504
+
1505
+ The gateway starts automatically when needed — either when webhooks are configured, when `--web-ui` is passed to `al start`, or when Docker container communication is required. The port is controlled by the `[gateway].port` setting in `config.toml` (default: `8080`).
1506
+
1507
+ ### Authentication
1508
+
1509
+ The gateway API is protected by an API key. The same key is used for both browser sessions and CLI access.
1510
+
1511
+ **Key location:** `~/.action-llama/credentials/gateway_api_key/default/key`
1512
+
1513
+ The key is generated automatically by `al doctor` or on first `al start`. To view or regenerate it, run `al doctor`.
1514
+
1515
+ **CLI access:** CLI commands (`al stat`, `al pause`, `al resume`, `al kill`) automatically read the API key from the credential store and send it as a `Bearer` token in the `Authorization` header.
1516
+
1517
+ **Browser access:** The web dashboard uses cookie-based authentication. After logging in with the API key, an `al_session` cookie is set (HttpOnly, SameSite=Strict) so all subsequent requests — including SSE streams — are authenticated automatically.
1518
+
1519
+ ### Protected routes
477
1520
 
478
- ## Environments (Portable Projects)
1521
+ | Route | Auth |
1522
+ |-------|------|
1523
+ | `/dashboard` and `/dashboard/*` | Required |
1524
+ | `/control/*` | Required |
1525
+ | `/locks/status` | Required |
1526
+ | `/health` | None |
1527
+ | `/webhooks/*` | None (HMAC validation per-source) |
479
1528
 
480
- Projects are portable — cloud infrastructure details live outside the project directory:
1529
+ ### Control API
481
1530
 
482
- - **`config.toml`** (committed) portable project settings (`[local]`, `[model]`, `[gateway]`, `[webhooks]`)
483
- - **`.env.toml`** (gitignored) — binds the project to an environment, can override any config value
484
- - **`~/.action-llama/environments/<name>.toml`** — shared cloud infrastructure config (`[cloud]`, etc.)
1531
+ All control endpoints use `POST` and require authentication.
485
1532
 
486
- Config merges in order: `config.toml` → `.env.toml` → environment file (later wins, deep merge).
1533
+ **Scheduler control:**
487
1534
 
488
- Cloud mode is auto-detected from the merged config (presence of `[cloud]` section). Use `--env <name>` or set `AL_ENV` to select an environment:
1535
+ | Endpoint | Description |
1536
+ |----------|-------------|
1537
+ | `POST /control/pause` | Pause the scheduler (all cron jobs) |
1538
+ | `POST /control/resume` | Resume the scheduler |
1539
+
1540
+ **Agent control:**
1541
+
1542
+ | Endpoint | Description |
1543
+ |----------|-------------|
1544
+ | `POST /control/trigger/<name>` | Trigger an immediate agent run |
1545
+ | `POST /control/agents/<name>/enable` | Enable a disabled agent |
1546
+ | `POST /control/agents/<name>/disable` | Disable an agent (pauses its cron job) |
1547
+ | `POST /control/agents/<name>/pause` | Pause an agent (alias for disable) |
1548
+ | `POST /control/agents/<name>/resume` | Resume an agent (alias for enable) |
1549
+ | `POST /control/agents/<name>/kill` | Kill all running instances of an agent |
1550
+
1551
+ ### SSE Streams
1552
+
1553
+ Live updates use **Server-Sent Events (SSE)**:
1554
+
1555
+ | Endpoint | Description |
1556
+ |----------|-------------|
1557
+ | `GET /dashboard/api/status-stream` | Pushes agent status and scheduler info whenever state changes |
1558
+ | `GET /dashboard/api/logs/<agent>/stream` | Streams log lines for a specific agent (500ms poll interval) |
1559
+
1560
+ ### Other endpoints
1561
+
1562
+ | Endpoint | Method | Auth | Description |
1563
+ |----------|--------|------|-------------|
1564
+ | `GET /health` | GET | No | Health check (no authentication required) |
1565
+ | `GET /locks/status` | GET | Yes | Active resource lock information |
1566
+
1567
+ ## Web Dashboard
1568
+
1569
+ Action Llama includes an optional web-based dashboard for monitoring agents in your browser. It provides a live view of agent statuses and streaming logs.
1570
+
1571
+ ### Enabling
1572
+
1573
+ Pass `-w` or `--web-ui` to `al start`:
489
1574
 
490
1575
  ```bash
491
- al start --env prod-aws # explicit
492
- AL_ENV=prod-aws al start # via env var
1576
+ al start -w
1577
+ ```
1578
+
1579
+ The dashboard URL is shown in the TUI header once the scheduler starts:
1580
+
493
1581
  ```
1582
+ Dashboard: http://localhost:8080/dashboard
1583
+ ```
1584
+
1585
+ The port is controlled by the `[gateway].port` setting in `config.toml` (default: `8080`).
494
1586
 
495
- Or set it in `.env.toml`:
1587
+ ### Authentication
1588
+
1589
+ The dashboard is protected by the gateway API key. Navigate to `http://localhost:8080/dashboard` and you'll be redirected to a login page where you paste your API key. On success, an `al_session` cookie is set (HttpOnly, SameSite=Strict).
1590
+
1591
+ ### Main Page — `/dashboard`
1592
+
1593
+ Displays a live overview of all agents:
1594
+
1595
+ | Column | Description |
1596
+ |--------|-------------|
1597
+ | Agent | Agent name (click to view logs) |
1598
+ | State | Current state: idle, running, building, or error |
1599
+ | Status | Latest status text or error message |
1600
+ | Last Run | Timestamp of the most recent run |
1601
+ | Duration | How long the last run took |
1602
+ | Next Run | When the next scheduled run will happen |
1603
+ | Actions | **Run** (trigger an immediate run) and **Enable/Disable** (toggle the agent) |
1604
+
1605
+ The header includes a **Pause/Resume** button for the scheduler and a **Logout** link. Below the table, a **Recent Activity** section shows the last 20 log lines across all agents.
1606
+
1607
+ All data updates in real time via Server-Sent Events (SSE) — no manual refresh needed.
1608
+
1609
+ ### Agent Logs — `/dashboard/agents/<name>/logs`
1610
+
1611
+ Displays a live-streaming log view for a single agent. Logs follow automatically by default (new entries scroll into view as they arrive). Features:
1612
+
1613
+ - **Follow mode** — enabled by default, auto-scrolls. Scrolling up pauses follow; scrolling back to the bottom re-enables it.
1614
+ - **Clear** — clears the log display (does not delete log files).
1615
+ - **Connection status** — shows whether the SSE connection is active.
1616
+ - **Log levels** — color-coded: green for INFO, yellow for WARN, red for ERROR.
1617
+
1618
+ On initial load, the last 100 log entries from the agent's log file are displayed, then new entries stream in as they are written. No additional dependencies or frontend build steps are required — the dashboard is rendered as plain HTML with inline CSS and JavaScript.
1619
+
1620
+ ## Environments
1621
+
1622
+ Projects are portable — cloud infrastructure details live outside the project directory using a three-layer config merge system (later values win, deep merge).
1623
+
1624
+ | Layer | File | Scope | Contents |
1625
+ |-------|------|-------|----------|
1626
+ | 1 | `config.toml` | Project (committed) | `[model]`, `[local]`, `[gateway]`, `[webhooks]`, `[telemetry]`, top-level scheduler fields |
1627
+ | 2 | `.env.toml` | Project (gitignored) | `environment` field to select env, can override any config value |
1628
+ | 3 | `~/.action-llama/environments/<name>.toml` | Machine | `[server]` (SSH push deploy), `gateway.url`, `telemetry.endpoint` |
1629
+
1630
+ `[cloud]` and `[server]` must be in Layer 3 (environment file). Placing `[cloud]` in `config.toml` is an error. `[cloud]` and `[server]` are mutually exclusive within an environment.
1631
+
1632
+ ### Environment selection priority
1633
+
1634
+ `-E`/`--env <name>` flag > `AL_ENV` env var > `.env.toml` `environment` field.
1635
+
1636
+ ### Environment types
1637
+
1638
+ For `al env init`: `server`.
1639
+
1640
+ ### Server environment example
496
1641
 
497
1642
  ```toml
498
- environment = "prod-aws"
1643
+ # ~/.action-llama/environments/production.toml
1644
+ [server]
1645
+ host = "5.6.7.8"
1646
+ user = "root"
1647
+ keyPath = "~/.ssh/id_rsa"
1648
+ basePath = "/opt/action-llama"
1649
+ expose = true
499
1650
  ```
500
1651
 
501
- Manage environments with `al env init <name>`, `al env list`, `al env show <name>`.
1652
+ ### VPS credential sync
1653
+
1654
+ When deploying to a VPS, credentials are transferred to the remote server via SSH. The remote layout mirrors the local one: `~/.action-llama/credentials/{type}/{instance}/{field}`. No external secrets manager is needed — same trust model as SSH access.
502
1655
 
503
1656
  ## Running Agents
504
1657
 
@@ -508,16 +1661,11 @@ Start all agents with `al start` (or `npx al start`). This starts the scheduler
508
1661
 
509
1662
  When a scheduled agent runs `al-rerun`, the scheduler immediately re-runs it. This continues until the agent completes without `al-rerun` (no more work), hits an error, or reaches the `maxReruns` limit. This way an agent drains its work queue without waiting for the next cron tick.
510
1663
 
511
- Set `maxReruns` in `config.toml` to control the limit (default: 10):
512
-
513
- ```toml
514
- maxReruns = 5
515
- maxCallDepth = 3 # max depth for agent-to-agent call chains (default: 3)
516
- ```
517
-
518
1664
  Webhook-triggered and agent-triggered runs do not re-run — they respond to a single event.
519
1665
 
520
- ## Shell Command Exit Codes
1666
+ ## Exit Codes
1667
+
1668
+ ### Shell command exit codes
521
1669
 
522
1670
  All gateway-calling shell commands (`rlock`, `runlock`, `rlock-heartbeat`, `al-call`, `al-check`, `al-wait`) share a common exit code scheme. **Always check exit codes** — do not assume success.
523
1671
 
@@ -537,31 +1685,22 @@ All gateway-calling shell commands (`rlock`, `runlock`, `rlock-heartbeat`, `al-c
537
1685
  | 11 | Gateway timeout | 504 | Proxy timed out reaching the gateway |
538
1686
  | 12 | Server error | 500 | Internal gateway error |
539
1687
 
540
- These codes (0–12) don't overlap with agent exit codes (10–16 in `al-exit`) or POSIX signal codes (128+).
541
-
542
- **Lock commands** (`rlock`, `runlock`, `rlock-heartbeat`) exit 0 with `{"ok":true}` when `GATEWAY_URL` is unset (graceful degradation for local/non-containerized runs).
543
-
544
- **Call commands** (`al-call`, `al-check`, `al-wait`) exit 5 when `GATEWAY_URL` is unset — they require a gateway.
545
-
546
- ## Skills Reference
1688
+ ### Agent exit codes (`al-exit`)
547
1689
 
548
- Agents have access to runtime skills — capabilities taught via a preamble before the actions run. Each skill is documented for LLM consumption:
1690
+ | Exit | Meaning |
1691
+ |------|---------|
1692
+ | 10 | Auth failure |
1693
+ | 11 | Permission denied |
1694
+ | 12 | Rate limited |
1695
+ | 13 | Config error |
1696
+ | 14 | Dependency error |
1697
+ | 15 | Unrecoverable (default) |
1698
+ | 16 | User abort |
549
1699
 
550
- - [Skills Overview](skills/README.md)
551
- - [Credentials](skills/credentials.md) — env vars, tools, and access patterns from mounted credentials
552
- - [Signals](skills/signals.md) — `al-rerun`, `al-status`, `al-return`, `al-exit` signal commands, `al-call` for agent-to-agent calls
553
- - [Resource Locks](skills/resource-locks.md) — `rlock`, `runlock`, `rlock-heartbeat` for parallel coordination
554
- - [Calls](skills/calls.md) — `al-call`, `al-check`, `al-wait` for agent-to-agent calls
555
- - [Environment](skills/environment.md) — trigger types, context blocks, container filesystem
1700
+ Shell command codes (0-12) don't overlap with agent codes (10-16) or POSIX signal codes (128+).
556
1701
 
557
- ## Further Documentation
1702
+ **Lock commands** (`rlock`, `runlock`, `rlock-heartbeat`) exit 0 with `{"ok":true}` when `GATEWAY_URL` is unset (graceful degradation for local/non-containerized runs).
558
1703
 
559
- Full documentation is available on GitHub:
1704
+ **Call commands** (`al-call`, `al-check`, `al-wait`) exit 5 when `GATEWAY_URL` is unset they require a gateway.
560
1705
 
561
- - [Creating Agents](https://github.com/Action-Llama/action-llama/blob/main/docs/creating-agents.md)
562
- - [agent-config.toml Reference](https://github.com/Action-Llama/action-llama/blob/main/docs/agent-config-reference.md)
563
- - [Credentials](https://github.com/Action-Llama/action-llama/blob/main/docs/credentials.md)
564
- - [Webhooks](https://github.com/Action-Llama/action-llama/blob/main/docs/webhooks.md)
565
- - [Docker](https://github.com/Action-Llama/action-llama/blob/main/docs/docker.md) — custom Dockerfiles, standalone images, troubleshooting
566
- - [CLI Commands](https://github.com/Action-Llama/action-llama/blob/main/docs/commands.md)
567
- - [Example Agents](https://github.com/Action-Llama/action-llama/blob/main/docs/examples/index.md) — dev, reviewer, devops
1706
+ **Timeout:** Container terminated by timeout exits with code 124.