Skip to content

Conversation

@xingyaoww
Copy link
Collaborator

@xingyaoww xingyaoww commented Nov 3, 2025

Summary

This PR enhances the execute_bash tool to help agents understand and use the secret manager system effectively.

Changes

1. Documentation (Initial Commit)

Added a new "Credential Access" section to the TOOL_DESCRIPTION in openhands-tools/openhands/tools/execute_bash/definition.py that documents:

  • Automatic secret injection: How secrets are automatically exported as environment variables when referenced in bash commands
  • How to use secrets: Clear examples showing agents how to reference secret keys in their commands (e.g., $GITHUB_TOKEN, $API_KEY)
  • Secret detection: Explanation of the case-insensitive matching mechanism that detects secret keys in command text
  • Security: Information about automatic output masking to prevent accidental secret exposure

2. Available Secrets in Metadata (New Feature)

Enhanced ExecuteBashObservation to show agents which secrets are available:

  • Added available_secrets field to CmdOutputMetadata to store list of available secret names
  • Populate available secrets from conversation.state.secret_registry.secret_sources.keys() in the executor
  • Display in LLM output via ExecuteBashObservation.to_llm_content() showing format: [Available secrets: $SECRET1, $SECRET2, $SECRET3]
  • Added test coverage in test_bash_executor_metadata_available_secrets() to verify the functionality

This helps agents discover what credentials they can access without having to guess or try different secret names.

Implementation Details

The implementation reflects the actual secret manager behavior in openhands-tools/openhands/tools/execute_bash/impl.py:

  1. Before executing a bash command, the _export_envs() method calls secret_registry.get_secrets_as_env_vars(action.command)
  2. The secret registry searches the command text for registered secret keys (case-insensitive)
  3. Found secrets are exported as environment variables using export KEY="value" before the actual command runs
  4. Secret values are automatically masked in output, showing <secret-hidden> instead
  5. Available secret names are now shown in the observation metadata and LLM-facing output

Testing

  • ✅ All pre-commit checks pass (ruff format, ruff lint, pycodestyle, pyright)
  • ✅ All secret masking tests pass: tests/tools/execute_bash/test_secrets_masking.py (3 tests)
  • ✅ New test added: test_bash_executor_metadata_available_secrets() verifies:
    • Available secrets are correctly populated in metadata.available_secrets
    • Secrets are displayed in LLM-facing content with $ prefix
    • Multiple secrets are properly listed

Example Output

When an agent executes a bash command with secrets available, they'll now see:

[Command: echo "test"]
test
[Current working directory: /workspace]
[Python interpreter: /usr/bin/python3]
[Available secrets: $GITHUB_TOKEN, $API_KEY, $DATABASE_URL]
[Command finished with exit code 0]

Why This Matters

This enhancement helps agents:

  1. Understand the correct way to access credentials in bash commands
  2. Discover which credentials are available without trial and error
  3. Use credentials correctly for tasks involving:
    • API interactions requiring authentication
    • Git operations needing tokens
    • Database access
    • External service integrations
    • Any authenticated operations

Co-authored-by: openhands openhands@all-hands.dev


Agent Server images for this PR

GHCR package: https://github.com/OpenHands/agent-sdk/pkgs/container/agent-server

Variants & Base Images

Variant Architectures Base Image Docs / Tags
java amd64, arm64 eclipse-temurin:17-jdk Link
python amd64, arm64 nikolaik/python-nodejs:python3.12-nodejs22 Link
golang amd64, arm64 golang:1.21-bookworm Link

Pull (multi-arch manifest)

# Each variant is a multi-arch manifest supporting both amd64 and arm64
docker pull ghcr.io/openhands/agent-server:f90c770-python

Run

docker run -it --rm \
  -p 8000:8000 \
  --name agent-server-f90c770-python \
  ghcr.io/openhands/agent-server:f90c770-python

All tags pushed for this build

ghcr.io/openhands/agent-server:f90c770-golang-amd64
ghcr.io/openhands/agent-server:f90c770-golang_tag_1.21-bookworm-amd64
ghcr.io/openhands/agent-server:f90c770-golang-arm64
ghcr.io/openhands/agent-server:f90c770-golang_tag_1.21-bookworm-arm64
ghcr.io/openhands/agent-server:f90c770-java-amd64
ghcr.io/openhands/agent-server:f90c770-eclipse-temurin_tag_17-jdk-amd64
ghcr.io/openhands/agent-server:f90c770-java-arm64
ghcr.io/openhands/agent-server:f90c770-eclipse-temurin_tag_17-jdk-arm64
ghcr.io/openhands/agent-server:f90c770-python-amd64
ghcr.io/openhands/agent-server:f90c770-nikolaik_s_python-nodejs_tag_python3.12-nodejs22-amd64
ghcr.io/openhands/agent-server:f90c770-python-arm64
ghcr.io/openhands/agent-server:f90c770-nikolaik_s_python-nodejs_tag_python3.12-nodejs22-arm64
ghcr.io/openhands/agent-server:f90c770-golang
ghcr.io/openhands/agent-server:f90c770-java
ghcr.io/openhands/agent-server:f90c770-python

About Multi-Architecture Support

  • Each variant tag (e.g., f90c770-python) is a multi-arch manifest supporting both amd64 and arm64
  • Docker automatically pulls the correct architecture for your platform
  • Individual architecture tags (e.g., f90c770-python-amd64) are also available if needed

openhands-agent and others added 2 commits November 3, 2025 18:47
Document how the secret manager works with execute_bash tool to help
agents understand how to access registered credentials in bash commands.

The new section explains:
- Automatic secret injection mechanism
- How to reference secrets in commands (using environment variable syntax)
- Case-insensitive secret detection
- Automatic output masking for security

Co-authored-by: openhands <openhands@all-hands.dev>
xingyaoww and others added 2 commits November 4, 2025 04:57
This change enhances the execute_bash tool to show agents which secrets
are available for use in bash commands. The available secret names are
now populated in CmdOutputMetadata and displayed in the LLM-facing
observation output.

Changes:
- Add available_secrets field to CmdOutputMetadata
- Populate available_secrets from conversation's secret_registry
- Display available secrets in ExecuteBashObservation.to_llm_content
- Add test to verify the new functionality

This helps agents discover what credentials they can access without
having to guess or try different secret names.

Co-authored-by: openhands <openhands@all-hands.dev>
@github-actions
Copy link
Contributor

github-actions bot commented Nov 3, 2025

Coverage

Coverage Report •
FileStmtsMissCoverMissing
openhands-tools/openhands/tools/terminal
   definition.py1066736%54, 57, 60–61, 63, 66–68, 70–72, 74–76, 78, 102, 106, 109–110, 113, 115–125, 127, 132, 134–136, 139, 141, 143–145, 147, 151–152, 155–157, 159–160, 163–166, 170–172, 177, 181–186, 188–189, 191, 263
   impl.py762567%59, 62, 70–71, 98–100, 102–103, 109, 111, 115, 131, 134, 137–138, 143–144, 147–148, 150, 160, 186–187, 189
   metadata.py51688%78–79, 83, 101–102, 106
TOTAL12182564453% 

@xingyaoww
Copy link
Collaborator Author

@OpenHands merge from main and resolve all conflicts for this pr

@openhands-ai
Copy link

openhands-ai bot commented Nov 11, 2025

I'm on it! xingyaoww can track my progress at all-hands.dev

Resolved conflicts:
- openhands-tools/openhands/tools/terminal/impl.py:
  * Combined available_secrets population logic from feature branch
  * Updated to use observation.text instead of observation.output (API change in main)
  * Maintained proper secret masking with ExecuteBashObservation.from_text()
- tests/tools/terminal/test_secrets_masking.py:
  * Updated test to use from_text() instead of output parameter

Co-authored-by: openhands <openhands@all-hands.dev>
@openhands-ai
Copy link

openhands-ai bot commented Nov 11, 2025

Summary

I have successfully merged the main branch into the document-bash-credential-access branch and resolved all conflicts. The changes have been pushed to the remote repository.

What Was Done

Merge Conflict Resolution:

  1. openhands-tools/openhands/tools/terminal/impl.py - Resolved conflict by combining:

    • The available_secrets population logic from the feature branch
    • The new API using observation.text and ExecuteBashObservation.from_text() from main
  2. tests/tools/terminal/test_secrets_masking.py - Updated test to use the new API:

    • Changed from ExecuteBashObservation(output="...") to ExecuteBashObservation.from_text(text="...")

Verification

✅ All pre-commit hooks pass (Ruff format, Ruff lint, pycodestyle, pyright)
✅ All 3 secret masking tests pass
✅ Changes committed and pushed to origin/document-bash-credential-access

The PR #1005 is now fully up-to-date with the latest changes from main (v1.1.0 release) and ready for review!

View full conversation

@xingyaoww xingyaoww requested review from enyst and malhotra5 November 11, 2025 20:29
@xingyaoww xingyaoww marked this pull request as ready for review November 11, 2025 20:29
openhands-agent and others added 3 commits November 11, 2025 23:03
Test verifies that shell parameter expansion syntax like ${GITHUB_TOKEN:0:8}
is properly detected by the secret manager's find_secrets_in_text method.

The test covers various parameter expansion forms:
- ${var:offset:length} - substring expansion
- ${var:-default} - default value if unset
- ${var:?error} - error if unset
- ${var#pattern} - remove shortest prefix match
- ${var%pattern} - remove shortest suffix match

This ensures agents can safely use these bash features with secrets.

Co-authored-by: openhands <openhands@all-hands.dev>
Co-authored-by: Engel Nyst <engel.nyst@gmail.com>
Add documentation explaining that secrets like GITHUB_TOKEN may be updated
periodically or expire over time. When authentication failures occur, agents
should try using the secret again in a new command to pick up the refreshed
value. Include specific example of updating git remote URLs with refreshed
GITHUB_TOKEN.

Co-authored-by: openhands <openhands@all-hands.dev>
* How to use secrets: Simply reference the secret key in your command (e.g., `echo ${GITHUB_TOKEN:0:8}` or `curl -H "Authorization: Bearer $API_KEY" https://api.example.com`). The system will detect the key name in your command text and export it as environment variable before it executes your command.
* Secret detection: The system performs case-insensitive matching to find secret keys in your command text. If a registered secret key appears anywhere in your command, its value will be made available as an environment variable.
* Security: Secret values are automatically masked in command output to prevent accidental exposure. You will see `<secret-hidden>` instead of the actual secret value in the output.
* Refreshing expired secrets: Some secrets (like GITHUB_TOKEN) may be updated periodically or expire over time. If a secret stops working (e.g., authentication failures), try using it again in a new command - the system will automatically use the refreshed value. For example, if GITHUB_TOKEN was used in a git remote URL and later expired, you can update the remote URL with the current token: `git remote set-url origin https://${GITHUB_TOKEN}@github.com/username/repo.git` to pick up the refreshed token value.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
* Refreshing expired secrets: Some secrets (like GITHUB_TOKEN) may be updated periodically or expire over time. If a secret stops working (e.g., authentication failures), try using it again in a new command - the system will automatically use the refreshed value. For example, if GITHUB_TOKEN was used in a git remote URL and later expired, you can update the remote URL with the current token: `git remote set-url origin https://${GITHUB_TOKEN}@github.com/username/repo.git` to pick up the refreshed token value.
* Refreshing expired secrets: Some secrets (like GITHUB_TOKEN) may be updated periodically or expire over time. If a secret stops working (e.g., authentication failures), try using it again in a new command - the system should automatically use the refreshed value. For example, if GITHUB_TOKEN was used in a git remote URL and later expired, you can update the remote URL with the current token: `git remote set-url origin https://${GITHUB_TOKEN}@github.com/username/repo.git` to pick up the refreshed token value.
* If it still fails, report it to the user.

OK! Looks good. Verbose, but these LLMs will deal with it.

This is not always true, though: this happens on the Cloud, and I don't think, as of now, anywhere else in the project. So how about we add an escape hatch, maybe, for the LLM to not assume it will always happen?

Copy link
Collaborator

@enyst enyst Nov 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

e.g.
"* If it still fails, report it to the user."
or "you can ask the user about it"

@blacksmith-sh
Copy link
Contributor

blacksmith-sh bot commented Nov 15, 2025

[Automatic Post]: This PR seems to be currently waiting for review. @malhotra5, could you please take a look when you have a chance?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants