Skip to content

Hyperion Actions

How your agent talks back to the HyperAI IDE and performs actions.

How it works

Every time the user types in the Hyperion panel, the IDE sends your agent a request:

{ 
    "user_id": "3f2a9c11-...", 
    "text": "Please create a folder for me" 
}
  • user_id is a UUID generated by the IDE — use it to keep per-user session memory
  • text is what the user typed.

Your agent answers with a stream of Server-Sent Events. Each event is a data: line holding one JSON object, followed by a blank line. There are two kinds of event, and the IDE treats them very differently.

Streaming text

Anything you stream as response shows up in the Hyperion panel as the assistant's message:

data: {"response": "Creating the folder "}

data: {"response": "for you."}

Each chunk is an increment, not the whole message — the IDE appends them one after the other, which is what produces the typing effect. The message is complete when your response body closes.

Sending an action

When you want the agent to actually do something in the IDE, stream an event with an action key instead of the response key used for streaming text:

data: {"action": "create_folder", "path": "demo/sub"}

The IDE executes it and prints nothing for it. If you want to tell the user what the action did, you have to stream that text yourself as response events (see Streaming text):

data: {"response": "I'll create the folder "}

data: {"response": "`demo/sub` for you."}

data: {"action": "create_folder", "path": "demo/sub"}

data: {"response": " Done — you can see it in the file explorer."}

Actions and text can come in any order, and you can send several actions in one answer.

Available actions

Action Payload What the IDE does Example
create_folder path Creates the folder. {"action":"create_folder","path":"demo/sub"}
delete_folder path Deletes the folder. {"action":"delete_folder","path":"demo"}
create_file path, content Creates the file and opens it in the editor. {"action":"create_file","path":"demo/nginx.yaml","content":"apiVersion: hyper.ai/v1\n..."}
edit_file path, content Finds the file and replaces it whole, then opens it in the editor. {"action":"edit_file","path":"nginx.yaml","content":"apiVersion: hyper.ai/v1\n..."}
delete_file path Deletes the file. {"action":"delete_file","path":"nginx.yaml"}

path is always relative to the workspace root — never absolute, never ...

For delete_file, edit_file and delete_folder you do not need to give the whole path — send just the name and the IDE searches the workspace and uses the first match. If you do specify a path, it is taken into account and used as-is.

Reading a file — the read_file helper

Reading a file is deliberately not an action. To see what is inside a file, your agent asks the IDE backend directly over HTTP.

The starter ships that call as a helper in helpers.py:

from helpers import read_file

content = await read_file("app.yaml")

It returns the file as a plain string.

  • The path argument is either a full path (demo/sub/app.yaml) or just a file name. Send only the name and the backend searches the whole workspace for it, the same way delete_file does.

Errors

On failure it raises ReadFileError. Possible causes are several files share that name (all of them listed, so you can ask the user which one), or the backend is unreachable.

Under the hood read_file is one GET against the IDE backend:

GET http://localhost:3001/api/agent/file?path=app.yaml
GET http://localhost:3001/api/agent/file?path=demo/app.yaml

Slashes are allowed as-is in the query string, so a full path needs no special treatment (demo%2Fapp.yaml is accepted too).

Status Body
200 {"path": "demo/sub/app.yaml", "content": "..."}path is where it actually lives
404 {"error": "File not found"}
409 {"error": "Ambiguous file name", "matches": [...]}

Validating a file — the validate_file helper

Validating is not an action either. Your agent asks the IDE backend to check a file against the Native Apps or Device Apps rules. You send only the path — the backend reads the file itself.

from helpers import validate_file

report = await validate_file("app.yaml")

It returns the report as a dict:

{
    "path": "demo/app.yaml",
    "type": "device",
    "valid": false,
    "errors": [
        {"line": 3, "column": 1, "endLine": 3, "endColumn": 9, "field": "metadata.name", "message": "is required"}
    ],
    "warnings": [
        {"line": 12, "column": 5, "endLine": 12, "endColumn": 12, "field": "spec.exec.retries", "message": "unknown field, not part of the schema"}
    ]
}
  • type is native or device.
  • valid is false as soon as there is one error.
  • line and column point at the problem. For a missing field they point at the section where it should be added.

The path works the same way as in read_file — a full path or just a name.

Errors

On failure it raises ValidateFileError, for the same reasons as read_file. An invalid file is not an error — you get the report with valid: false.

Under the hood validate_file is one GET against the IDE backend:

GET http://localhost:3001/api/agent/validation/file?path=app.yaml
Status Body
200 the report
404 {"error": "File not found"}
409 {"error": "Ambiguous file name", "matches": [...]}

Local vs Docker

If you have followed the challenge description, then when you run the agent locally the ide-backend is at http://localhost:3001/api. When the agent runs as a Docker container, the backend must be http://host.docker.internal:3001/api instead.

In the hyperion-starter this is already set in the Dockerfile, so nothing to change:

git clone https://gitlab.eclipse.org/eclipse-research-labs/hyper-ai-project/hyperion-starter.git