Skip to content

fix(sdk_main): inject session URL as secret using conversation.id#142

Merged
xingyaoww merged 2 commits into
mainfrom
feat/automation-session-url
May 27, 2026
Merged

fix(sdk_main): inject session URL as secret using conversation.id#142
xingyaoww merged 2 commits into
mainfrom
feat/automation-session-url

Conversation

@all-hands-bot
Copy link
Copy Markdown
Contributor

@all-hands-bot all-hands-bot commented May 26, 2026

Problem

The PR review automation posts a progress comment and review body with a session URL, but the URL was wrong — it pointed to https://app.all-hands.dev (the app root) instead of the actual conversation.

Root cause: The correct URL is https://app.all-hands.dev/conversations/{conversation_id}. The conversation_id is only available after Conversation(...) is instantiated in sdk_main.py, but was never being captured and passed to the agent.

Fix

After Conversation(...) is created in sdk_main.py, build the session URL from conversation.id and inject it via conversation.update_secrets({"AUTOMATION_SESSION_URL": session_url}).

The SDK auto-exports secrets as env vars into the agent's bash subprocess when the command text references the key name. So the original prompt's ${AUTOMATION_SESSION_URL} just works — no prompt changes needed.

Why update_secrets() instead of prompt injection?

  • sanitized_env() only strips SESSION_API_KEYAUTOMATION_SESSION_URL passes through fine
  • Secrets are auto-exported as env vars when referenced in bash commands (case-insensitive matching)
  • The prompt doesn't need any modifications — ${AUTOMATION_SESSION_URL:-fallback} naturally picks up the secret value
  • Clean separation: infrastructure concern (URL injection) stays in sdk_main.py, prompt stays focused on agent behavior

Changes

  • openhands/automation/presets/plugin/sdk_main.py: After conversation creation, build session_url = f"{api_url}/conversations/{conversation.id}" and inject via conversation.update_secrets()
  • openhands/automation/dispatcher.py: No changes (reverted from previous approach that used sandbox_id)

Tested

Triggered via review-this label on OpenHands/software-agent-sdk#3333. The progress comment and review body now contain correct https://app.all-hands.dev/conversations/{uuid} links.

Adds AUTOMATION_SESSION_URL to the sandbox env vars when SANDBOX_ID is
available (Cloud mode). The URL is pre-built as {cloud_api_url}/conversations/{sandbox_id}
so automation scripts can link back to the session without having to
reconstruct it from SANDBOX_ID + OPENHANDS_CLOUD_API_URL individually —
both of which are stripped by sanitized_env() before they reach the
agent's bash subprocess.

Also documents the new env var in the plugin preset sdk_main.py docstring.

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

github-actions Bot commented May 26, 2026

🚀 Deploy Preview PR Created/Updated

A deploy preview has been created/updated for this PR.

Deploy PR: https://github.com/OpenHands/deploy/pull/4429
Automation SHA: a14efa6e22f5edb7837ff8be993765c734da4ea0
Last updated: May 26, 2026, 05:28:55 PM ET

Once the deploy PR's CI passes, the automation service will be deployed to the feature environment.

@github-actions
Copy link
Copy Markdown

github-actions Bot commented May 26, 2026

Coverage

Coverage Report
FileStmtsMissCoverMissing
__init__.py10100% 
app.py1518345%41, 44, 47, 53, 55, 58, 61–64, 67, 72–74, 76, 81–82, 84, 86–88, 90, 94, 96–97, 99–100, 103–109, 112–113, 116, 123–124, 127–128, 132, 140–141, 144, 151–152, 154, 157–158, 161, 166–174, 176–178, 284–286, 291–292, 294–295, 297, 300–302, 304–305, 307–308, 313–314, 318, 321, 323
auth.py160795%88, 116, 125, 162, 335, 343–344
config.py1580100% 
constants.py170100% 
db.py672956%60, 74–75, 78–80, 82, 89, 92–93, 96, 104, 111, 144, 147–148, 152–153, 161, 169, 174, 201, 204–210
dispatcher.py1864575%70, 82, 84–85, 137, 199–202, 237, 240, 255–256, 262–264, 281–282, 288–292, 295–297, 307, 373–374, 399–406, 426, 441–442, 456–457, 466–467, 469
event_router.py591967%83, 88, 119–121, 137–138, 156, 158, 160–161, 163, 173, 179–181, 184, 186, 188
exceptions.py40100% 
execution.py22013339%39–41, 76–79, 87–91, 93, 101–103, 108–112, 114, 128–131, 133, 135, 137–140, 142–147, 149, 151–158, 160–161, 163, 199–201, 207–209, 220–223, 229–231, 271–275, 284, 292, 296, 298–299, 304–305, 310, 388–389, 473–475, 477–483, 486–487, 489, 491–493, 496, 499, 502–505, 507, 510–511, 514–516, 520–521, 525–528, 530, 538–539, 543–545, 547–553, 557, 559, 568–570, 572–574
filter_eval.py50296%161–162
logger.py551769%37, 50–51, 53–59, 74, 77, 101, 103–106
models.py810100% 
preset_router.py1905670%143–145, 255–256, 261–268, 273, 276, 278–279, 291–294, 296–300, 305, 314, 386–388, 501–502, 507–514, 519, 522, 524–525, 537–540, 542–546, 551, 561
router.py1427745%92–93, 113, 115, 118, 120, 134, 147, 149–150, 152–153, 155–156, 159–161, 172–174, 194–196, 202–204, 208–209, 228–229, 231, 234, 259–262, 281, 284, 287, 294, 296, 330–332, 335–337, 341–342, 347, 351–354, 356, 364, 366–367, 372–373, 376, 378, 380–382, 385–388, 393, 395–396, 405, 426–428, 432
scheduler.py61985%137–138, 175–176, 191–192, 202–203, 205
schemas.py2701793%32, 166, 172–174, 233–235, 237, 342–343, 346, 351, 356, 500, 508, 515
trigger_matcher.py28389%72–74
uploads.py1075944%142–145, 153–155, 161–162, 165, 174–175, 178–179, 187–188, 190–193, 196–199, 201, 203–205, 207–210, 212–213, 215, 230, 236–237, 240, 243, 246, 249, 251, 264–265, 279, 282–284, 286–287, 289, 295–296, 309, 317–319, 323
watchdog.py984554%55–57, 69–70, 162–163, 204–205, 207, 209, 218, 220–222, 224, 231–233, 235–237, 239–240, 242, 257, 259, 264–267, 269–274, 276–282, 284
webhook_router.py804840%57, 82–83, 107–108, 110, 113–114, 116, 126, 128–132, 137, 139, 151, 154, 157, 164–165, 167, 180, 182–183, 188, 204, 206–207, 213–215, 217–218, 220, 235, 237–238, 243–244, 261, 263–264, 270–271, 273, 275
backends
   __init__.py130100% 
   base.py290100% 
   cloud.py1306252%43–45, 50–52, 103, 116–118, 131–135, 142–143, 145, 147, 159–160, 229–230, 232–233, 235–238, 240–245, 247–255, 257–258, 260, 267–269, 272–273, 275, 282–284, 289–293, 295
   local.py430100% 
event_schemas
   __init__.py33196%53
   bitbucket_data_center.py160100% 
   custom.py33584%52–53, 64–66
   detection.py320100% 
   github.py125496%306, 311, 456, 483
   jira_dc.py160100% 
presets
   __init__.py00100% 
storage
   __init__.py60100% 
   factory.py15193%36
   file_store.py22577%21, 30, 35, 40, 64
   google_cloud.py721184%49, 97–102, 136–137, 190, 192
   local.py680100% 
   s3.py1121586%56, 100, 102–103, 107, 109, 190, 213–215, 269–270, 275, 337–338
utils
   __init__.py50100% 
   agent_server.py530100% 
   api_key.py322425%40–41, 46–48, 50, 55, 60, 62–65, 67–68, 70–71, 73, 79, 81–82, 89, 91–92, 98
   cron.py45686%39, 45, 74, 80, 123, 140
   log_context.py100100% 
   model_profiles.py110100% 
   run.py841483%74–76, 183–184, 200–202, 207–209, 256, 262–263
   sandbox.py716114%49–50, 55–58, 60–62, 64–70, 80–81, 86–92, 115–116, 118–122, 124–128, 159–160, 162, 164–167, 172–173, 176, 180–181, 187–189, 194–196, 201–202, 210–212, 214
   tarball_validation.py480100% 
   time.py100100% 
   webhook.py57984%49, 54, 122, 132–134, 140, 203–204
TOTAL337686774% 

@xingyaoww xingyaoww changed the title feat(dispatcher): inject AUTOMATION_SESSION_URL env var into sandbox fix(sdk_main): inject session URL into prompt using conversation.id May 26, 2026
@xingyaoww xingyaoww force-pushed the feat/automation-session-url branch from 299a7aa to cab4494 Compare May 26, 2026 20:02
@xingyaoww xingyaoww changed the title fix(sdk_main): inject session URL into prompt using conversation.id fix(sdk_main): inject session URL as secret using conversation.id May 26, 2026
@xingyaoww xingyaoww marked this pull request as ready for review May 26, 2026 21:17
Copy link
Copy Markdown
Contributor Author

@all-hands-bot all-hands-bot left a comment

Choose a reason for hiding this comment

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

🟡 Acceptable — Clean implementation, but incomplete

The session URL injection logic is well-implemented and correctly scoped to Cloud mode. However, the prompt preset has identical structure and needs the same fix for consistency.

[RISK ASSESSMENT]

  • [Overall PR] ⚠️ Risk Assessment: 🟢 LOW

Adds optional runtime feature (session URL injection) to plugin preset only. No breaking changes, backward compatible. The missing prompt preset update is a consistency issue, not a functional risk.

VERDICT:
Needs rework: Apply the same fix to both presets for consistency

KEY INSIGHT:
Using update_secrets() for runtime URL injection is elegant — secrets auto-export to bash without prompt changes.


Was this automated review useful? React with 👍 or 👎 to this review to help us measure review quality.
Workflow run: https://github.com/OpenHands/automation/actions/runs/26475714804

if not IS_LOCAL_MODE and api_url:
session_url = f"{api_url}/conversations/{conversation.id}"
conversation.update_secrets({"AUTOMATION_SESSION_URL": session_url})
print(f" session URL: {session_url}")
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

🔴 Critical: This same fix must be applied to the prompt preset for consistency.

The file openhands/automation/presets/prompt/sdk_main.py has identical structure:

  • Creates a Conversation (line ~326)
  • Calls conversation.update_secrets(secrets) (line ~331)
  • But is missing the session URL injection

Apply both changes there:

  1. Add the runtime-injected secrets docstring section (lines 59-61 here)
  2. Add the session URL injection code block (lines 349-354 here)

Even though the PR review automation currently uses the plugin preset, the prompt preset will have the same bug if any automation using it tries to reference $AUTOMATION_SESSION_URL.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Fixed in a14efa6 — applied the same session URL injection and docstring changes to openhands/automation/presets/prompt/sdk_main.py.

This reply was posted by an AI agent (OpenHands) on behalf of the user.

After Conversation(...) is created, build the session URL from
conversation.id and inject it via conversation.update_secrets().
The SDK auto-exports secrets as env vars when the agent's bash command
references the key name, so the prompt's ${AUTOMATION_SESSION_URL}
just works — no prompt changes needed.

Applied to both plugin and prompt presets for consistency.

Co-authored-by: openhands <openhands@all-hands.dev>
@xingyaoww xingyaoww force-pushed the feat/automation-session-url branch from cab4494 to a14efa6 Compare May 26, 2026 21:27
Copy link
Copy Markdown
Member

@malhotra5 malhotra5 left a comment

Choose a reason for hiding this comment

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

LGTM

@xingyaoww xingyaoww merged commit 2ca55c1 into main May 27, 2026
5 checks passed
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