Contents · 18 sections↓
- 01How do you run an AI agent from the command line?
- 02How the runtime CLI works
- 03When to use the runtime CLI (and when not to)
- 04What you need
- 05Step 1: Generate and validate a manifest
- 06Step 2: Start the runtime in the background
- 07Step 3: Check status
- 08Step 4: Read and follow the journal
- 09Step 5: Restart, rewind and stop
- 10Step 6: Stop a runtime that doesn't answer
- 11Step 7: Control what API deploys may do
- 12Which commands does the runtime CLI have?
- 13The complete example
- 14Running it in production
- 15Common problems and fixes
- 16Limitations
- 17Frequently asked questions
- 18Next steps
An AI agent that runs on a schedule needs the same command-line controls as any background service: start it, check on it, read what it did, restart it and stop it, from any terminal. This guide is for developers who run long-lived agents on a laptop, a VM or a container. You will start a Promptise Foundry agent runtime in the background from a .agent manifest, then use the runtime CLI to check status, follow logs, restart, plan a rewind and stop it, with real terminal output from one session.
How do you run an AI agent from the command line?
Declare the agent in a .agent manifest, then run promptise runtime start on it. With --detach the runtime runs in the background and prints its PID, control API address and log file. From then on, promptise runtime status, logs, restart, rewind and stop find the runtime by themselves and talk to its control API, which listens on loopback with a random token. logs --follow prints the agent's journal as it is written. stop asks for a graceful shutdown and falls back to SIGTERM, and with --force to a kill.
promptise runtime validate agents/
promptise runtime start agents/pipeline-watcher.agent --detach
promptise runtime status
promptise runtime logs pipeline-watcher --follow
promptise runtime stopKey takeaways:
`start` runs the agents and a control API. The API listens on 127.0.0.1 with a random bearer token unless you choose another host and token.
A private state file connects the commands. start records the PID, port and token in ~/.promptise/runtime/<name>.json (mode 0600), and the other commands read it.
`stop` has fallbacks. It requests a shutdown over the API, then sends SIGTERM, then kills the runtime with --force.
API deploys are locked down by default. Deploys that would run commands or read environment variables are refused unless you start the runtime with --allow-command-deploys, --allow-env-var or --allow-env-references.
`restart` doesn't reload manifests. It restarts processes with the loaded configuration; stop and start the runtime to pick up edits.
[02]
How the runtime CLI works
A command-line tool for long-running agents has two jobs: run the agents, and let later commands reach them. Running them is the easy part. Reaching them needs an address, a credential and a way to know whether the runtime still exists. Promptise solves this the way many service managers do, with a control API and a small state file per runtime:
terminal 1 background runtime (pid 66951)
────────── ──────────────────────────────
promptise runtime start agents/x.agent --detach
│ spawns ──────────────────────────────────▶ load manifests, start processes
│ control API on 127.0.0.1:<free port>
│ bearer token: random
│ waits until ready ◀── state file ───────── ~/.promptise/runtime/x.json (0600)
▼ pid, port, token, journal paths
prints PID, API, log file output ──▶ ~/.promptise/runtime/x.log
terminal 2
──────────
promptise runtime status ──▶ read state file ──▶ GET /api/v1/... (Bearer token)
promptise runtime restart ─▶ read state file ──▶ POST /api/v1/processes/x/restart
promptise runtime logs x ──▶ read state file ──▶ read the journal file directly
promptise runtime stop ────▶ 1. POST /api/v1/runtime/shutdown, wait --timeout
2. no answer or still running: SIGTERM, wait again
3. still running: --force sends SIGKILL, else exit 1Unless you set the token yourself (step 7), the state file is the only place it is stored. The state directory is created with mode 0700 and each file with 0600; a directory or file owned by another user, or one other users can read, is refused. When a runtime exits it removes its file. When it dies (kill -9, a crash), the next command notices that the PID is gone and removes the stale file. PROMPTISE_RUNTIME_STATE_DIR moves the directory.
[03]
When to use the runtime CLI (and when not to)
You want to | Use |
|---|---|
Run manifests on a dev machine or a VM and control them from other terminals | The runtime CLI (promptise runtime ...) |
Run in a container or under systemd | promptise runtime start in the foreground; the supervisor sends SIGTERM |
Deploy, patch and message agents from other services | The control API or OrchestrationClient |
Build agents in Python with callbacks and custom objects | AgentRuntime in your own program (run multiple AI agents) |
Run a Python agent program with dev tools or an HTTP API | promptise run (run, debug and deploy an AI agent) |
Don't use --detach in a container: the container's main process should stay in the foreground so the platform sees it exit and can restart it. The runtime CLI also isn't a fleet manager. It controls runtimes on one machine, or one remote API at a time with --url.
[04]
What you need
Python 3.10 or later and Promptise Foundry 1.5.0 or later: pip install "promptise>=1.5.0".
An OpenAI API key in OPENAI_API_KEY. The agent uses openai:gpt-5-mini, and each scheduled run costs a fraction of a cent.
A manifest and its tool server. This guide uses a pipeline watcher whose cron trigger fires every 30 seconds, so that something happens while you watch. The AI agent manifest guide explains every section of the file.
version: "1.0"
name: pipeline-watcher
model: openai:gpt-5-mini
instructions: |
You watch the company's data pipelines. On every run, call pipeline_status.
For each degraded pipeline that is not in open_alerts yet, call create_alert,
then save the new list of pipeline names with update_state("open_alerts", [...]).
Finish with one short line that says what you did.
servers:
pipeline_tools:
type: stdio
command: python
args: ["pipeline_tools.py"]
triggers:
- type: cron
cron_expression: "* * * * * */30" # every 30 seconds (demo only; use "*/15 * * * *")
world:
open_alerts: []
context:
writable_keys: [open_alerts]
state_tool: true
journal:
level: full
config:
restart_policy: on_failurepipeline_tools.py is a small MCP server with two tools, pipeline_status and create_alert. The complete example at the end lists every file.
[05]
Step 1: Generate and validate a manifest
promptise runtime init writes a template, so you never start from an empty file. The templates are basic, cron, webhook and full, and --force overwrites an existing file:
$ promptise runtime init --template cron --output scratch/monitor.agent
✓ Created scratch/monitor.agent
Template: cron
Next steps:
1. Edit scratch/monitor.agent to customize your agent
2. Validate: promptise runtime validate scratch/monitor.agent
3. Start: promptise runtime start scratch/monitor.agent
[exit 0]The [exit N] lines in this guide's output show each command's exit code; the script that captured the output adds them. promptise runtime validate checks one file or every *.agent file in a directory against the strict schema. Run it before every start and in CI:
$ promptise runtime validate agents/
Validating agents/pipeline-watcher.agent...
✓ Schema validation passed
✓ No warnings
Manifest Summary
…
│ Name │ pipeline-watcher │
│ Model │ openai:gpt-5-mini │
…
│ Servers │ 1 │
│ Triggers │ 1 │
└──────────────┴─────────────────────────────────────────────────────────────────┘
✓ Validation complete!
✓ All 1 manifest(s) valid
[exit 0]For a directory, a failure ends with ✗ N of M manifest(s) failed and exit code 1.
[06]
Step 2: Start the runtime in the background
start takes a manifest file or a directory. With --detach it starts the same command as a background process in a new session, waits until every process has started or failed, and prints where to find it:
$ promptise runtime start agents/pipeline-watcher.agent --detach
Started runtime pipeline-watcher in the background
PID: 66951
Processes: pipeline-watcher
Control API: http://127.0.0.1:64937
Log file: /home/me/.promptise/runtime/pipeline-watcher.log
Status: promptise runtime status pipeline-watcher
Stop it: promptise runtime stop pipeline-watcher
[exit 0]The runtime's name defaults to the process name for a file and the directory name for a directory; --runtime-name sets it (1 to 64 letters, digits, _, . or -). A second start with the name of a running runtime is refused. If the background runtime exits during startup, start prints the last 20 lines of its log and exits with 1. If it isn't ready after 60 seconds, start says so and exits with 0, and the runtime keeps starting.
The state directory now holds the runtime's state file and log, both readable only by you:
$ ls -l /home/me/.promptise/runtime
total 32
-rw-------@ 1 me wheel 1243 Oct 20 22:26 ops.log
-rw-------@ 1 me wheel 983 Oct 20 22:27 pipeline-watcher.json
-rw-------@ 1 me wheel 1141 Oct 20 22:27 pipeline-watcher.log
-rw-------@ 1 me wheel 981 Oct 20 22:26 stuck.log
[exit 0]ops.log and stuck.log are left over from two runtimes that ran earlier on this machine (the ones in steps 6 and 7): a log file stays after its runtime exits, a state file doesn't. The log is overwritten by the next detached start with the same name. The state file holds what the other commands need (the token is cut here; never print yours):
{
"name": "pipeline-watcher",
"pid": 66951,
"host": "127.0.0.1",
"port": 64937,
"token": "vOEN2U...",
"manifests": [
"/home/me/ops-agents/agents/pipeline-watcher.agent"
],
"processes": {
"pipeline-watcher": {
"journal": {
"backend": "file",
"level": "full",
"path": "/home/me/ops-agents/.promptise/journal"
}
}
},
"started_at": "2026-10-20T20:27:22+00:00",
"ready": true,
"launch_id": "e1121b5977414784",
"log_file": "/home/me/.promptise/runtime/pipeline-watcher.log",
"version": 1
}Other options of start: --name renames the process of a single file, --api-host and --api-port (default 127.0.0.1 and 0, a free port) set where the control API listens, and --dashboard opens the terminal dashboard instead of the status table (not with --detach).
[07]
Step 3: Check status
status without a name shows every runtime of your user on this machine. A process name works too, and --process (-p) shows one process in detail:
$ promptise runtime status
Runtime pipeline-watcher pid 66951 API http://127.0.0.1:64937 started 2026-10-20T20:27:22+00:00
Agent Processes
┏━━━━━━━━━━━━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━━┓
┃ Name ┃ State ┃ ID ┃ Invocations ┃ Queue ┃ Uptime ┃
┡━━━━━━━━━━━━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━━┩
│ pipeline-watcher │ RUNNING │ f17c3dba │ 2 │ 0 │ 0h 0m 49s │
└──────────────────┴─────────┴──────────┴─────────────┴───────┴───────────┘
[exit 0]$ promptise runtime status pipeline-watcher --process pipeline-watcher
Runtime pipeline-watcher pid 66951 API http://127.0.0.1:64937 started 2026-10-20T20:27:22+00:00
Process: pipeline-watcher
┏━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
┃ Field ┃ Value ┃
┡━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┩
│ name │ pipeline-watcher │
│ process_id │ f17c3dba-9dc4-45b3-9c54-b6ba12f6da4f │
│ state │ running │
│ execution_mode │ strict │
│ invocation_count │ 2 │
│ consecutive_failures │ 0 │
│ restart_count │ 0 │
│ journal_enabled │ True │
…
│ suspend_reason │ - │
│ queue_size │ 0 │
│ dead_letter_count │ 0 │
…
│ triggers │ [{"id": "cron-2721f2b845", "type": "cron", "source": "config"}] │
└───────────────────────┴─────────────────────────────────────────────────────────────────┘
[exit 0]Two scheduled runs have finished. The detail view is what the control API returns for the process: counters, the suspend reason, the dead-letter count and the triggers with their stable IDs. --json prints the same data as JSON for scripts: an object for one runtime or process, a list when you ask for every runtime. It includes the runtime's PID, URL, start time, manifests and log file, never the token.
[08]
Step 4: Read and follow the journal
logs prints the newest journal entries of a process, each with the entry ID that rewind takes. A terminal narrower than 110 columns gets one line per entry; a wider one gets a table:
$ promptise runtime logs pipeline-watcher --lines 8
Journal: pipeline-watcher (time, entry ID, type, data)
2026-10-20T20:27:39.118 a444ba29-7f7e-48cf-9f45-7fd78410780a invocation_result {"invocation": 1, "event_id": "9a779a86-a996-4ddd-88b5-fd55c402324d", "trigge...
2026-10-20T20:27:39.121 0a38955c-95a9-4f0f-82cf-eef7e3166b52 checkpoint {"state": {"context_state": {"open_alerts": ["billing-sync"]}, "lifecycle_sta...
2026-10-20T20:28:00.005 f0ea83d8-e140-4433-9066-474126cabdd8 trigger_event {"trigger_id": "cron-2721f2b845", "trigger_type": "cron", "event_id": "b462c0...
2026-10-20T20:28:00.006 5228c804-49e8-4129-840b-2f9e44fcfc85 invocation_start {"invocation": 2, "event_id": "b462c0eb-75a2-49cd-8295-c29102728f08"}
2026-10-20T20:28:05.108 4884525c-5c0a-4890-9216-a0bd4315a18d tool_call {"tool": "pipeline_status", "args": {}}
2026-10-20T20:28:05.110 38361507-9a36-4233-8528-8a0df6997a4a tool_result {"tool": "pipeline_status", "result": "{\"orders-etl\": {\"status\": \"health...
2026-10-20T20:28:10.953 cbdddd20-546a-4c5e-aca4-256cab340f9b invocation_result {"invocation": 2, "event_id": "b462c0eb-75a2-49cd-8295-c29102728f08", "trigge...
2026-10-20T20:28:10.954 0d535032-8c4f-47fe-897a-6cc78b23d7ac checkpoint {"state": {"context_state": {"open_alerts": ["billing-sync"]}, "lifecycle_sta...
[exit 0]The second run fired at 20:28:00: a trigger_event, the run's start, one tool call and its result, the run's result and a checkpoint. The data column is cut at 80 characters. --follow (-f) keeps printing entries as they are written, until Ctrl+C:
$ promptise runtime logs pipeline-watcher --follow --lines 1
Journal: pipeline-watcher (time, entry ID, type, data)
2026-10-20T20:28:10.954 0d535032-8c4f-47fe-897a-6cc78b23d7ac checkpoint {"state": {"context_state": {"open_alerts": ["billing-sync"]}, "lifecycle_sta...
Following /home/me/ops-agents/.promptise/journal/pipeline-watcher.jsonl (Ctrl+C to stop)...
2026-10-20T20:28:30.003 e5ee3337-91a7-4de6-88ed-e17c6dd1be7b trigger_event {"trigger_id": "cron-2721f2b845", "trigger_type": "cron", "event_id": "4755a3...
2026-10-20T20:28:30.005 7b16260e-941a-423c-985a-54e2293a0be2 invocation_start {"invocation": 3, "event_id": "4755a356-7c27-4c3f-b15f-71582bf828a6"}
2026-10-20T20:28:35.383 e9f64b3e-340c-486e-b302-f8969ff6e881 tool_call {"tool": "pipeline_status", "args": {}}
2026-10-20T20:28:35.388 4d25bc57-b022-4659-807a-f01725aafae1 tool_result {"tool": "pipeline_status", "result": "{\"orders-etl\": {\"status\": \"health...
2026-10-20T20:28:40.757 d8ae1406-5c76-4304-b65d-68a29be9bc53 invocation_result {"invocation": 3, "event_id": "4755a356-7c27-4c3f-b15f-71582bf828a6", "trigge...
2026-10-20T20:28:40.761 9e55039a-798c-40d0-97d1-69934808f668 checkpoint {"state": {"context_state": {"open_alerts": ["billing-sync"]}, "lifecycle_sta...
^Clogs reads the journal file directly, so it works while the runtime is busy. It finds the file through the state file. Without a running runtime it reads .promptise/journal/<name>.jsonl in the current directory, or the directory you pass with --journal-path. A process with a memory journal is read through the API, and --url reads any API's journal, polling once a second when following. A process with journal: {level: none} has nothing to show, and logs says so. Use --lines (-n, default 20) to see more.
[09]
Step 5: Restart, rewind and stop
restart restarts every process of a runtime, or one with --process. The runtime itself, its API and its token stay:
$ promptise runtime restart pipeline-watcher --process pipeline-watcher
Restarted process pipeline-watcher (running)
[exit 0]The process keeps its context state and conversation. restart doesn't read the manifest again; to apply an edited manifest, stop the runtime and start it again.
rewind rolls a process's conversation, its context state or both back to just before a journal entry. Without an entry ID it lists recent entries. With one and --plan, it shows what the rewind would change and stops there:
$ promptise runtime rewind pipeline-watcher f0ea83d8-e140-4433-9066-474126cabdd8 --plan
Rewind plan for pipeline-watcher: back to just before entry f0ea83d8-e140-4433-9066-474126cabdd8 (trigger_event, 2026-10-20T20:28:00.005114+00:00)
Mode: both (rewinds the conversation and the context state)
Affected: 18 of 31 journal entries after it (4 conversation, 8 state/tool)
Summary: rewind (both): 18 entries affected (4 conversation, 8 code/state).
The journal itself is kept (a rewind entry is appended), so a rewind can be rewound.
[exit 0]Without --plan, the command shows the same plan and asks Rewind pipeline-watcher? [y/N]; --yes skips the question. --mode picks both (the default), conversation_only, code_only (the context state) or summarize. The rewind goes through the control API, so the runtime must run and the process must have a journal. Rewind an AI agent explains the modes.
stop without --process stops every process gracefully (final checkpoint, process.stopped), closes the API, removes the state file and ends the runtime:
$ promptise runtime stop
Stopped runtime pipeline-watcher (pid 66951)
[exit 0]$ promptise runtime status
Error: No runtime is running on this machine (as this user). Start one with 'promptise runtime start <manifest>', or pass --url to reach a remote one.
[exit 1]With one runtime running, stop, restart and status need no name. With several, stop and restart ask you to name one and list the running ones. stop --process <name> stops one process and leaves the runtime running.
[10]
Step 6: Stop a runtime that doesn't answer
A healthy runtime stops through its API. To see the fallbacks, start a runtime and freeze it with kill -STOP, so it can't answer or handle signals:
promptise runtime start api-agents/ --runtime-name stuck --detach
kill -STOP <pid>$ promptise runtime stop stuck --timeout 3
Cannot reach runtime 'stuck' at http://127.0.0.1:64878: ReadTimeout
Sending SIGTERM to pid 66786...
Error: Runtime 'stuck' (pid 66786) did not stop within 3s. Wait longer with --timeout, or kill it with --force.
[exit 1]The shutdown request timed out after 10 seconds, so stop sent SIGTERM and waited --timeout seconds (default 30). The runtime was still there, so stop exited with 1 and named the two ways out. With --force, it kills the process and removes its state file:
$ promptise runtime stop stuck --timeout 3 --force
Cannot reach runtime 'stuck' at http://127.0.0.1:64878: ReadTimeout
Sending SIGTERM to pid 66786...
Killing pid 66786...
Killed runtime stuck (pid 66786)
[exit 0]When the shutdown request succeeds but the runtime takes longer than --timeout to exit, stop sends SIGTERM and waits 10 more seconds. Windows has no SIGTERM a process can catch, so there the step is skipped. In the foreground, start handles SIGTERM like Ctrl+C, which is what docker stop and process managers send:
$ promptise runtime start api-agents/ --runtime-name fg --allow-command-deploys
Loaded 1 process(es): notes
Agent Processes
┏━━━━━━━┳━━━━━━━━━┳━━━━━━━━━━┳━━━━━━━━━━━━━┳━━━━━━━┳━━━━━━━━━━┓
┃ Name ┃ State ┃ ID ┃ Invocations ┃ Queue ┃ Uptime ┃
┡━━━━━━━╇━━━━━━━━━╇━━━━━━━━━━╇━━━━━━━━━━━━━╇━━━━━━━╇━━━━━━━━━━┩
│ notes │ RUNNING │ bc7feeac │ 0 │ 0 │ 0h 0m 0s │
└───────┴─────────┴──────────┴─────────────┴───────┴──────────┘
Runtime fg (pid 66766), control API on http://127.0.0.1:64861
--allow-command-deploys: API deploys may run commands on this machine (stdio MCP servers, command
hooks, open-mode tool creation).
Press Ctrl+C to stop all processes, or run 'promptise runtime stop fg' from another terminal.
$ kill -TERM 66766
Shutting down (SIGTERM)...
All processes stopped.
[exit 0]A second Ctrl+C or SIGTERM during the shutdown exits at once with code 130, without the clean shutdown.
[11]
Step 7: Control what API deploys may do
The control API can deploy new processes at runtime. Anyone who holds the token could then run programs on your machine or read its environment, so 1.5.0 refuses both by default. Three start flags change that, and they affect only API deploys, never the manifests you start:
Flag | Allows | Default answer without it |
|---|---|---|
--allow-command-deploys | Deploys with stdio MCP servers, command hooks or open-mode tool creation | 403 COMMANDS_NOT_ALLOWED |
--allow-env-var NAME (repeatable) | ${NAME} references to this one variable | 403 ENV_REFERENCES_NOT_ALLOWED |
--allow-env-references | ${VAR} references to any variable | 403 ENV_REFERENCES_NOT_ALLOWED |
To call the API yourself, set the token before start. The runtime then uses PROMPTISE_RUNTIME_TOKEN instead of a random one and removes it from its own environment, so agents, hooks and tool servers don't inherit it:
export PROMPTISE_RUNTIME_TOKEN=$(python -c "import secrets; print(secrets.token_urlsafe(24))")
export PIPELINE_REGION=eu-central-1
promptise runtime start api-agents/ --runtime-name ops --detach --allow-env-var PIPELINE_REGION
API=$(promptise runtime status ops --json | python -c "import json,sys; print(json.load(sys.stdin)['runtime']['url'])")
curl -s -X POST "$API/api/v1/processes" -H "Authorization: Bearer $PROMPTISE_RUNTIME_TOKEN" \
-H "Content-Type: application/json" \
-d '{"name": "shell-agent", "config": {"servers": {"tools": {"command": "python", "args": ["tools.py"]}}}}'Three deploys against that runtime, one per case:
--- stdio MCP server
{"error": {"code": "COMMANDS_NOT_ALLOWED", "message": "This config runs programs or code on the runtime's host (servers['tools'] (stdio MCP server)), which the control API doesn't allow by default: anyone with the API token could run any command. …"}}
HTTP 403
--- reference to OPENAI_API_KEY
{"error": {"code": "ENV_REFERENCES_NOT_ALLOWED", "message": "This request references environment variables of the runtime's host (config.secrets.secrets.key (${OPENAI_API_KEY})), which the control API doesn't allow by default: the agent could read them back. …"}}
HTTP 403
--- reference to PIPELINE_REGION (allowed)
{"name": "region-agent", "process_id": "5602a4d9-a304-4307-b9b3-bd048e4923e1", "state": "created", "created_at": 1792527994.235204}
HTTP 201The runtime's log records each refusal, without the request body:
OrchestrationAPI: refused a deploy that runs host commands: servers['tools'] (stdio MCP server)
OrchestrationAPI: refused a request with environment references: config.secrets.secrets.key (${OPENAI_API_KEY})A foreground start with --allow-command-deploys or --allow-env-references prints a warning line, as in the SIGTERM output above. Use them only when every token holder may run commands on that machine. Paths in deployed configs (journal, memory, watch paths) must also lie inside ./.promptise under the directory where the runtime started, or the API answers 403 PATH_NOT_ALLOWED.
[12]
Which commands does the runtime CLI have?
Command | What it does | Main options |
|---|---|---|
start MANIFEST | Run a manifest file or a directory of them | --detach, --runtime-name, --name, --dashboard, --api-host, --api-port, --allow-command-deploys, --allow-env-var, --allow-env-references |
| Show runtimes, or one process | --process, --json, --url, --token |
| Stop a runtime, or one process | --process, --timeout (30), --force, --url, --token |
| Restart every process, or one | --process, --url, --token |
logs NAME | Show or follow a process's journal | --lines (20), --follow, --journal-path, --url, --token |
| List entries, plan or apply a rewind | --plan, --mode, --yes, --runtime, --lines, --url, --token |
validate PATH | Check a manifest file or directory | |
init | Write a template manifest | --template, --output, --force |
Exit codes: 0 for success (including a foreground runtime stopped by Ctrl+C, SIGTERM or stop), 1 for an error with a message on stderr, 2 for a usage error, and 130 for a second signal during shutdown. The CLI reference lists every case.
[13]
The complete example
The project, and the commands from this guide in order:
ops-agents/
├── agents/
│ └── pipeline-watcher.agent # cron every 30 s (shown above)
├── api-agents/
│ └── notes.agent # an agent with no triggers, for steps 6 and 7
└── pipeline_tools.py # MCP server: pipeline_status, create_alertexport OPENAI_API_KEY=sk-...
promptise runtime validate agents/
promptise runtime start agents/pipeline-watcher.agent --detach
promptise runtime status
promptise runtime status pipeline-watcher --process pipeline-watcher
promptise runtime logs pipeline-watcher --lines 8
promptise runtime logs pipeline-watcher --follow # Ctrl+C to stop following
promptise runtime restart pipeline-watcher --process pipeline-watcher
promptise runtime rewind pipeline-watcher # list entries
promptise runtime rewind pipeline-watcher <entry_id> --plan
promptise runtime stopThe two scripts that produced this guide's output, demo.sh and demo2.sh, run the same commands and save each output.
[14]
Running it in production
Run in the foreground under a supervisor
In a container, under systemd or under a process manager, run promptise runtime start agents/ in the foreground. SIGTERM triggers the same graceful shutdown as promptise runtime stop, and the exit code tells the supervisor what happened. Give the platform a stop timeout longer than your longest run plus the final checkpoint. Deploy an AI agent with Docker shows a container setup.
Control a runtime on another machine
Start it with --api-host 0.0.0.0 and a known PROMPTISE_RUNTIME_TOKEN, put TLS in front of it, and use the client commands with --url. Prefer the environment variable to --token, because a token on the command line is visible to other users in ps. With --url, stop requests the shutdown and returns without waiting.
export PROMPTISE_RUNTIME_TOKEN=... # the token the remote runtime was started with
promptise runtime status --url https://agents.internal.example.com
promptise runtime logs pipeline-watcher --url https://agents.internal.example.com --followScript against exit codes and JSON
status --json and the exit codes make the CLI scriptable: a health check can run promptise runtime status <name> --json and alert on a non-zero exit or a process whose state isn't running. For anything beyond that, call the control API directly.
Keep the state directory private
The state directory holds bearer tokens. Don't put it on a shared volume, and don't run the runtime and the client commands as different users: each user has their own state directory, so a runtime started with sudo is invisible to your normal user.
[15]
Common problems and fixes
Symptom | Cause | Fix |
|---|---|---|
| Another user, sudo or another PROMPTISE_RUNTIME_STATE_DIR started it | Run the command as the same user, or use --url with its token |
| Names are unique per state directory | Stop it, or pick another --runtime-name |
| stop, restart and rewind need a target | Pass the runtime name, or --runtime for rewind |
| The runtime is hung or has a long run in progress | Raise --timeout, or add --force |
logs shows nothing for a process | A relative journal.path resolved from another directory | Run logs while the runtime runs, or pass --journal-path |
An edited manifest has no effect after restart | restart reuses the loaded configuration | stop and start the runtime |
403 COMMANDS_NOT_ALLOWED on a deploy | The config has a stdio server, a command hook or tool creation | Load it from a manifest on the host, or start with --allow-command-deploys |
A traceback instead of an error for a missing ${VAR} | 1.5.0 doesn't catch EnvVarNotFoundError in start for a single file | Set the variable; the last line names it |
[16]
Limitations
One machine per command. The CLI finds runtimes in your state directory or talks to one --url. It has no inventory of runtimes across hosts; see distributed AI agents for clusters.
No log rotation. The detached log file grows until the next detached start overwrites it. Use the journal for history, and run under a supervisor that handles output in production.
`--detach` is not a service manager. Nothing restarts a detached runtime that dies. Use systemd, Docker or Kubernetes for that.
Restarts don't reload. Changing a manifest means stopping and starting the runtime, which stops every process in it.
Errors for unset variables are tracebacks in 1.5.0 for validate, and for start with a single file.
[17]
Frequently asked questions
How do I run an AI agent in the background?
Start it with promptise runtime start agents/my.agent --detach. The runtime runs as a background process with its output in ~/.promptise/runtime/<name>.log, and the command prints its PID and control API address. Stop it later with promptise runtime stop.
How do I see what a running AI agent is doing?
Use promptise runtime status for its state and counters, and promptise runtime logs <process> --follow to watch its journal: trigger events, tool calls, results and checkpoints, as they are written. For a live view of several agents, start the runtime with --dashboard.
How do I stop an AI agent process safely?
Run promptise runtime stop. It asks the runtime to shut down over its API, which stops every process with a final checkpoint, then waits. If the runtime doesn't answer, stop sends SIGTERM, and with --force it kills the process. In a container, SIGTERM from docker stop triggers the same graceful shutdown.
Where does the runtime CLI store its token?
In a state file at ~/.promptise/runtime/<runtime name>.json, created with mode 0600 in a 0700 directory. The other commands read the token from there. Files owned by another user or readable by others are refused. Set PROMPTISE_RUNTIME_STATE_DIR to use another directory.
Can I control a runtime on another server?
Yes. Start the runtime with --api-host and a known PROMPTISE_RUNTIME_TOKEN, put it behind TLS, and pass --url to status, logs, restart, rewind and stop, with the token in PROMPTISE_RUNTIME_TOKEN. Each command talks to one API at a time.
Why does restart not pick up my manifest changes?
restart restarts processes with the configuration the runtime loaded at start. Processes keep their state and conversation. To load an edited manifest, stop the runtime and start it again; the journal restores each process's state on the new start.
[18]
Next steps
Write the manifests this CLI runs: AI agent manifest.
Watch processes live: AI agent monitoring dashboard in the terminal.
Manage agents over HTTP instead: AI agent control API.
Undo a bad run: rewind an AI agent.
Understand the journal behind logs: durable AI agents.
Reference: runtime CLI and control API in the Promptise docs.