Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
8dd26d8
feat: add encrypted secrets in transit for settings API
openhands-agent May 5, 2026
385ee87
chore: remove RemoteWorkspace settings methods (deferred to follow-up)
openhands-agent May 5, 2026
8598e40
fix: decrypt secrets in StartConversationRequest when secrets_encrypt…
openhands-agent May 5, 2026
29488c4
test: add unit tests for encrypted secrets feature
openhands-agent May 5, 2026
3cd1c35
fix: address review feedback for encrypted secrets feature
openhands-agent May 5, 2026
4d9e4a9
docs: clarify authentication model in settings endpoint docstring
openhands-agent May 5, 2026
5527f61
fix: address PR review feedback for encrypted secrets
openhands-agent May 5, 2026
8d8b6c4
chore: fix pre-commit formatting issues
openhands-agent May 5, 2026
b9e34eb
fix: address PR review feedback for settings API security
openhands-agent May 5, 2026
444e189
style: fix pre-commit formatting issues
openhands-agent May 5, 2026
dd3d55d
fix: add type: ignore for intentional type error tests
openhands-agent May 5, 2026
7fb15e9
fix: shorten comment to fix line length
openhands-agent May 5, 2026
e741e60
fix: address PR review feedback - atomicity, error handling, and tests
openhands-agent May 5, 2026
013d081
fix: type assertion for api_key in test to satisfy pyright
openhands-agent May 5, 2026
93ca458
fix: consolidate SECRET_NAME_PATTERN and add live server test
openhands-agent May 5, 2026
bcf3c91
docs: add inline comments explaining 'from None' security rationale
openhands-agent May 5, 2026
d2f2dc0
feat: add settings and secrets API example
openhands-agent May 5, 2026
73308e3
chore: Remove PR-only artifacts [automated]
May 5, 2026
7b9d014
refactor: remove def main nesting from settings example
openhands-agent May 5, 2026
1453121
feat: add real agent session to settings/secrets API example
openhands-agent May 5, 2026
1d62e8d
feat: use REST API to start conversation with encrypted secrets
openhands-agent May 5, 2026
fa84627
refactor: simplify by using encrypted_llm directly from settings
openhands-agent May 5, 2026
4722817
fix: set OH_SECRET_KEY to enable encrypted secrets feature
openhands-agent May 5, 2026
7623968
fix: use correct endpoint /api/conversations/{id} for polling
openhands-agent May 5, 2026
baeb77d
refactor: use LookupSecret for conversation secrets
openhands-agent May 5, 2026
c3434bf
fix: set TMUX_TMPDIR to avoid socket path length issues on macOS
openhands-agent May 5, 2026
f94b748
feat: support optional LLM_BASE_URL environment variable
openhands-agent May 5, 2026
72b8b2f
fix: use agent_final_response endpoint instead of non-existent execute
openhands-agent May 5, 2026
9e932a7
fix: handle None metrics in conversation response
openhands-agent May 6, 2026
b56e7c8
fix: use stats.usage_to_metrics for accumulated cost
openhands-agent May 6, 2026
eee11d6
simplify task
malhotra5 May 6, 2026
5f2ecd5
style: fix formatting
openhands-agent May 6, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -348,6 +348,7 @@ Note: This is separate from `persistence_dir` which is used for conversation sta
- Clean caches: `make clean`
- Run SDK examples: see [openhands-sdk/openhands/sdk/AGENTS.md](openhands-sdk/openhands/sdk/AGENTS.md).
- The example workflow runs `uv run pytest tests/examples/test_examples.py --run-examples`; each successful example must print an `EXAMPLE_COST: ...` line to stdout (use `EXAMPLE_COST: 0` for non-LLM examples).
- Example scripts in `examples/` should use top-level code flow (e.g. `with` blocks, bare statements) rather than wrapping logic in a `def main()` function. The `def main` pattern creates unnecessary nesting that makes examples harder to read; keep the code flat and script-like.
- Conversation plugins passed via `plugins=[...]` are lazy-loaded on the first `send_message()` or `run()`, so example code should inspect plugin-added skills or `resolved_plugins` only after that first interaction.
- Programmatic settings live in `openhands-sdk/openhands/sdk/settings/`. Keep the exported schema focused on neutral config structure and semantics; downstream apps should own client-specific ordering, icons, widgets, and slash-command presentation.
</QUICK_COMMANDS>
Expand Down
369 changes: 369 additions & 0 deletions examples/02_remote_agent_server/12_settings_and_secrets_api.py

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -477,10 +477,23 @@ async def _start_conversation(
# serialize to plain strings. Pass expose_secrets=True so StaticSecret values
# are preserved through the round-trip; the dict is only used in-process to
# construct StoredConversation, not sent over the network.
stored = StoredConversation(
id=conversation_id,
**request.model_dump(mode="json", context={"expose_secrets": True}),
)
request_data = request.model_dump(mode="json", context={"expose_secrets": True})

# If secrets_encrypted=True, the agent's secrets (e.g., LLM api_key) are
# cipher-encrypted and need decryption during model validation. Pass the
# cipher in the validation context so validate_secret() can decrypt them.
Comment thread
malhotra5 marked this conversation as resolved.
if request.secrets_encrypted:
if self.cipher is None:
raise ValueError(
"Cannot decrypt secrets: cipher not configured. "
"Set OH_SECRET_KEY environment variable."
)
stored = StoredConversation.model_validate(
{"id": conversation_id, **request_data},
context={"cipher": self.cipher},
)
else:
stored = StoredConversation(id=conversation_id, **request_data)
event_service = await self._start_event_service(stored)
initial_message = request.initial_message
if initial_message:
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
"""Persistence module for settings and secrets storage."""

from openhands.agent_server.persistence.models import (
SECRET_NAME_PATTERN,
CustomSecret,
CustomSecretCreate,
CustomSecretResponse,
PersistedSettings,
Secrets,
SecretsResponse,
SettingsUpdatePayload,
)
from openhands.agent_server.persistence.store import (
FileSecretsStore,
FileSettingsStore,
SecretsStore,
SettingsStore,
get_secrets_store,
get_settings_store,
reset_stores,
)


__all__ = [
# Constants
"SECRET_NAME_PATTERN",
# Models
"CustomSecret",
"CustomSecretCreate",
"CustomSecretResponse",
"PersistedSettings",
"Secrets",
"SecretsResponse",
"SettingsUpdatePayload",
# Stores
"FileSecretsStore",
"FileSettingsStore",
"SecretsStore",
"SettingsStore",
"get_secrets_store",
"get_settings_store",
"reset_stores",
]
Loading
Loading