feat(dev): surface dev-stack runtime services in agent system prompt (#503)

* feat(dev): surface dev-stack runtime services in agent system prompt

Add a structured 'runtime services' info object that the dev launchers
(`dev:safe`, `dev:automation`, `dev:docker`, and the published
`agent-canvas` binary) propagate to the frontend via
`VITE_RUNTIME_SERVICES_INFO`. The frontend renders it into a
`<RUNTIME_SERVICES>` markdown block and attaches it as
`AgentContext.system_message_suffix` on every `POST /api/conversations`.

This means agents start each conversation knowing exactly what services
exist in the current dev stack (ingress URL, automation backend URL +
`/api/automation` prefix, auth header, etc.), instead of having to probe
or — worse — assume `localhost:8000` is the automation server when it is
actually the Agent Server they are running inside of.

URLs are written from the agent's point of view: dockerless modes use
`localhost`, `dev:docker` uses `host.docker.internal`. When automation
isn't running in the current mode (e.g. `dev:safe`), the block says so
explicitly so agents know to skip `/api/automation` calls.

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

* fix(runtime-services): address review feedback on PR #503

- Validate required `agentServerPort` in `buildRuntimeServicesInfo`;
  previously a missing port baked `http://localhost:undefined` into
  the agent's system prompt.
- Skip the automation entry when the supplied `automation` object has
  no `port` (e.g. a bare `{}` from a misconfigured launcher).
- Rename the JSON service key from `vite` to `frontend` and add a
  `kind: "vite" | "static"` discriminator + mode-aware description,
  so static-build dev stacks (`dev:docker`, the published binary, ...)
  no longer surface a misleading "Vite dev server" line in the agent
  system prompt. The renderer still accepts the legacy `vite` key.
- Anchor the "don't guess" warning to the actual agent-server URL from
  runtime info instead of hardcoded `localhost:8000`, since the
  agent-server uses different ports across dev modes (18000 in
  dev:safe, 8000 in dev:docker, ...).
- Plumb `frontendKind` through `buildAutomationRuntimeServicesInfo`
  and stamp `config.frontendKind` in `dev-with-automation.mjs::main`
  so both Vite spawn and static-build paths describe the frontend
  correctly.
- Expand AGENTS.md with the JSON schema of `VITE_RUNTIME_SERVICES_INFO`
  and a concrete example of the rendered `<RUNTIME_SERVICES>` block.
- Tests: assert the new URL-in-warning behavior, the new `frontend` /
  legacy `vite` rendering, the `agentServerPort`-required guard, the
  `automation: {}` skip, and the legacy `vitePort` alias.

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

---------

Co-authored-by: openhands <openhands@all-hands.dev>
This commit is contained in:
Tim O'Farrell
2026-05-15 20:53:45 -06:00
committed by GitHub
co-authored by openhands
parent 8db469487b
commit b2b71855c6
9 changed files with 765 additions and 24 deletions
+6 -5
View File
@@ -81,11 +81,8 @@ this is a packaging error. If running from source:
let main, checkDockerPrereqs, startAgentServerDocker, CONTAINER_WORKSPACES_DIR;
try {
({ main } = await import("../scripts/dev-with-automation.mjs"));
({
checkDockerPrereqs,
startAgentServerDocker,
CONTAINER_WORKSPACES_DIR,
} = await import("../scripts/dev-docker.mjs"));
({ checkDockerPrereqs, startAgentServerDocker, CONTAINER_WORKSPACES_DIR } =
await import("../scripts/dev-docker.mjs"));
} catch (err) {
console.error("Failed to load required scripts. Try reinstalling:");
console.error(" npm install -g @openhands/agent-canvas@latest");
@@ -100,6 +97,10 @@ main({
viteWorkingDir: CONTAINER_WORKSPACES_DIR,
staticMode: true,
staticDir: BUILD_DIR,
// Agent-server runs in a Docker container; host services are reached
// via "host.docker.internal" from the agent's POV.
agentHostAlias: "host.docker.internal",
mode: "agent-canvas",
}).catch((err) => {
console.error(`Fatal error: ${err.message}`);
if (err.stack) {