Skip to content

Commit d5c9fc4

Browse files
committed
feat: add GET /system endpoint for LLM system prompt injection
- GET /system returns a structured system prompt grounding the LLM in the environment (OS, hostname, user, shell, Python version) with directives for tool usage - Gated by OPEN_TERMINAL_ENABLE_SYSTEM_PROMPT env var (default: true) - Advertised via features.system in GET /api/config for consumer discovery - OPEN_TERMINAL_SYSTEM_PROMPT env var for full prompt override - Endpoint excluded from OpenAPI schema, auth-protected
1 parent a5e44c4 commit d5c9fc4

4 files changed

Lines changed: 58 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
44

55
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/).
66

7+
## [0.11.27] - 2026-03-22
8+
9+
### Added
10+
11+
- 🧠 **System prompt endpoint** (`GET /system`) — returns a structured system prompt for LLM integration, grounding the model in the environment (OS, hostname, user, shell, Python version) with directives for tool usage. Gated by `OPEN_TERMINAL_ENABLE_SYSTEM_PROMPT` (default `true`); advertised via `features.system` in `GET /api/config` so consumers can check support before fetching.
12+
- ⚙️ **`OPEN_TERMINAL_ENABLE_SYSTEM_PROMPT`** — environment variable (or `enable_system_prompt` in config.toml) to enable/disable the `/system` endpoint and feature flag. Defaults to `true`.
13+
- ⚙️ **`OPEN_TERMINAL_SYSTEM_PROMPT`** — environment variable (or `system_prompt` in config.toml) to fully override the generated system prompt with custom content.
14+
715
## [0.11.26] - 2026-03-20
816

917
### Changed

open_terminal/env.py

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,6 +131,16 @@ def _resolve_file_env(var: str, default: str = "") -> str:
131131
str(config.get("enable_notebooks", True)),
132132
).lower() not in ("false", "0", "no")
133133

134+
ENABLE_SYSTEM_PROMPT = os.environ.get(
135+
"OPEN_TERMINAL_ENABLE_SYSTEM_PROMPT",
136+
str(config.get("enable_system_prompt", True)),
137+
).lower() not in ("false", "0", "no")
138+
139+
SYSTEM_PROMPT = os.environ.get(
140+
"OPEN_TERMINAL_SYSTEM_PROMPT",
141+
config.get("system_prompt", ""),
142+
)
143+
134144
MULTI_USER = os.environ.get(
135145
"OPEN_TERMINAL_MULTI_USER",
136146
str(config.get("multi_user", False)),

open_terminal/main.py

Lines changed: 39 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
from fastapi.security import HTTPAuthorizationCredentials, HTTPBearer
2424
from pydantic import BaseModel, Field
2525

26-
from open_terminal.env import API_KEY, BINARY_FILE_MIME_PREFIXES, CORS_ALLOWED_ORIGINS, ENABLE_NOTEBOOKS, ENABLE_TERMINAL, EXECUTE_DESCRIPTION, EXECUTE_TIMEOUT, LOG_DIR, MAX_TERMINAL_SESSIONS, MULTI_USER, OPEN_TERMINAL_INFO, PROCESS_LOG_RETENTION, TERMINAL_TERM
26+
from open_terminal.env import API_KEY, BINARY_FILE_MIME_PREFIXES, CORS_ALLOWED_ORIGINS, ENABLE_NOTEBOOKS, ENABLE_SYSTEM_PROMPT, ENABLE_TERMINAL, EXECUTE_DESCRIPTION, EXECUTE_TIMEOUT, LOG_DIR, MAX_TERMINAL_SESSIONS, MULTI_USER, OPEN_TERMINAL_INFO, PROCESS_LOG_RETENTION, SYSTEM_PROMPT, TERMINAL_TERM
2727
from open_terminal.utils.runner import PipeRunner, ProcessRunner, create_runner
2828
from open_terminal.utils.fs import UserFS
2929

@@ -54,6 +54,31 @@ def get_system_info() -> str:
5454
)
5555

5656

57+
def get_system_prompt() -> str:
58+
"""Build a default system prompt for LLM integration."""
59+
if SYSTEM_PROMPT:
60+
return SYSTEM_PROMPT
61+
62+
shell = os.environ.get("SHELL", "/bin/sh")
63+
user_part = f" as user '{os.getenv('USER', 'unknown')}'" if not MULTI_USER else ""
64+
65+
prompt = (
66+
f"You have access to a computer running {platform.system()} {platform.release()} ({platform.machine()}) "
67+
f'on host "{socket.gethostname()}"{user_part} with {shell}. '
68+
f"Python {sys.version.split()[0]} is available.\n\n"
69+
"Use your tools to directly interact with the system \u2014 run commands, read and write files, "
70+
"and search the filesystem. "
71+
"Prefer verifying the current state before making changes. "
72+
"When running commands, check the output to confirm success. "
73+
"If a command produces no output, that typically means it succeeded."
74+
)
75+
76+
if OPEN_TERMINAL_INFO:
77+
prompt += f"\n\n{OPEN_TERMINAL_INFO}"
78+
79+
return prompt
80+
81+
5782
_EXECUTE_DESCRIPTION = (
5883
"Run a shell command in the background and return a command ID.\n\n"
5984
+ get_system_info()
@@ -304,10 +329,23 @@ async def get_config():
304329
"features": {
305330
"terminal": ENABLE_TERMINAL,
306331
"notebooks": ENABLE_NOTEBOOKS,
332+
"system": ENABLE_SYSTEM_PROMPT,
307333
},
308334
}
309335

310336

337+
if ENABLE_SYSTEM_PROMPT:
338+
339+
@app.get(
340+
"/system",
341+
include_in_schema=False,
342+
dependencies=[Depends(verify_api_key)],
343+
)
344+
async def get_system():
345+
"""Return a system prompt for LLM integration."""
346+
return {"prompt": get_system_prompt()}
347+
348+
311349
if OPEN_TERMINAL_INFO:
312350

313351
@app.get(

pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "open-terminal"
3-
version = "0.11.26"
3+
version = "0.11.27"
44
description = "A remote terminal API."
55
readme = "README.md"
66
authors = [

0 commit comments

Comments
 (0)