mirror of
https://github.com/NVIDIA/OpenShell.git
synced 2026-10-15 05:09:44 +08:00
* chore(gator): add gator gate skill * chore(gator): add sandbox launcher scaffold * chore(gator): add codex image and docs checks * chore(gator): fold approved provider policy rules * chore(gator): add deterministic reviewer runner * chore(gator): clarify ok-to-test comments * chore(gator): structure launcher harnesses * chore(gator): require e2e for dependabot * chore(gator): add codex refresh profile * chore(gator): wip manifest agent launcher * feat(agents): supervise watch cycles in sandbox * fix(agents): preserve gateway refresh state * fix(gator): continue human response threads * fix(agents): keep watch supervisor retrying * fix(agents): use refreshed Codex credential aliases * fix(gator): avoid misleading gh auth checks * docs(agents): remove architecture build update * fix(gator): use REST-backed GitHub writes * fix(agents): bake immutable agent payloads * fix(agents): upload writable agent workspace * fix(agents): surface gator watch progress * fix(agents): prevent codex stdin hang * fix(agents): align codex subagent input * fix(agents): heartbeat during active cycles * fix(agents): clean up heartbeat sleep * fix(agents): disable gh telemetry in codex harness * fix(agents): reconcile closed gator PRs * fix(agents): query closed gator PR labels separately * fix(agents): tolerate rotated credential placeholders * fix(agents): enforce gator same-sha comment guard Signed-off-by: John Myers <johntmyers@users.noreply.github.com> * docs(agents): scope gator trusted commentary Signed-off-by: John Myers <johntmyers@users.noreply.github.com> * fix(gator): treat reviewer failures as transient Signed-off-by: Evan Lezar <elezar@nvidia.com> * feat(agents): refine gator supervised workflow Signed-off-by: John Myers <johntmyers@users.noreply.github.com> * fix(agents): stream codex prompts via stdin Signed-off-by: John Myers <johntmyers@users.noreply.github.com> * docs(agents): clarify trusted gator responses Signed-off-by: John Myers <johntmyers@users.noreply.github.com> * refactor(agents): scope gator PR to scripts Signed-off-by: John Myers <johntmyers@users.noreply.github.com> --------- Signed-off-by: John Myers <johntmyers@users.noreply.github.com> Signed-off-by: Evan Lezar <elezar@nvidia.com> Co-authored-by: John Myers <johntmyers@users.noreply.github.com> Co-authored-by: Evan Lezar <elezar@nvidia.com>
197 lines
8.9 KiB
Markdown
197 lines
8.9 KiB
Markdown
# OpenShell Agents
|
|
|
|
`scripts/agents/` contains repository-owned agent launchers. An agent is a
|
|
manifest plus prompt assets that the shared launcher turns into an OpenShell
|
|
sandbox run. Agents do not own harness implementations. Harness-specific setup
|
|
and execution live in `runtime/harnesses/<name>/`.
|
|
|
|
## Directory Layout
|
|
|
|
```text
|
|
scripts/agents/
|
|
run.sh # Generic manifest-driven launcher
|
|
runtime/ # Shared in-sandbox runtime
|
|
entrypoint.sh # Starts the in-sandbox supervisor
|
|
supervisor.sh # Runs bounded harness cycles in once/watch mode
|
|
subagent.sh # Generic subagent dispatcher
|
|
harnesses/
|
|
codex/ # Codex install and execution adapter
|
|
<agent>/
|
|
agent.yaml # Agent manifest
|
|
prompts/ # Prompt templates rendered at launch
|
|
providers/ # Provider profile YAML files for this agent
|
|
policy.yaml # Optional image policy source
|
|
```
|
|
|
|
Agent directories should contain agent-specific intent and payloads: manifests,
|
|
prompt templates, provider profiles, policies, and references to skills or
|
|
subagents. They should not contain `harnesses/codex`, `harnesses/opencode`, or
|
|
similar runtime code.
|
|
|
|
## Agent Manifest
|
|
|
|
Each agent has an `agent.yaml` manifest. The launcher currently reads these
|
|
sections:
|
|
|
|
- `id`, `display_name`, `description`: human and runtime identity.
|
|
- `sandbox`: default sandbox name prefix, gateway, source image or Dockerfile,
|
|
and background log directory.
|
|
- `harness`: default harness and per-harness settings such as model and
|
|
reasoning effort.
|
|
- `runtime`: in-sandbox run mode (`once` or `watch`), watch poll interval, and
|
|
transient failure logging threshold.
|
|
- `profile_paths`: ordered directories to scan for provider profile YAML files.
|
|
- `settings`: gateway settings to apply before launch.
|
|
- `providers`: provider instances to create or update, credential sources, and
|
|
optional refresh configuration.
|
|
- `skills`: files to inject into the sandbox payload.
|
|
- `subagents`: subagent definitions to inject into the sandbox payload.
|
|
- `prompt_template`: prompt template rendered into the immutable agent payload as
|
|
`agent-prompt.md`.
|
|
|
|
Manifest paths support these prefixes:
|
|
|
|
- `repo://path`: resolve from the repository root.
|
|
- `agent://path`: resolve from the agent directory.
|
|
- Relative paths without a prefix: resolve from the agent directory.
|
|
- Absolute paths: use as-is.
|
|
|
|
## Launch Order
|
|
|
|
`scripts/agents/run.sh` performs the launch in this order:
|
|
|
|
1. Parse CLI flags and select the agent directory from `--agent`.
|
|
2. Load `agent.yaml`, select the requested harness, and reject unsupported
|
|
harness names.
|
|
3. Resolve sandbox defaults from the manifest and CLI/environment overrides.
|
|
4. Build a temporary payload directory.
|
|
5. Copy `runtime/` into the payload so every agent uses the same in-sandbox
|
|
entrypoint and harness adapters.
|
|
6. Optionally copy a host Codex binary into the shared Codex runtime path when
|
|
`--codex-bin` is supplied.
|
|
7. Copy manifest-declared skills and subagents into the payload.
|
|
8. Render the prompt template with runtime values such as `{{HARNESS}}`,
|
|
`{{RUN_MODE}}`, `{{POLL_INTERVAL_SECONDS}}`, `{{USER_PROMPT}}`, and
|
|
manifest-declared subagent variables such as `{{REVIEWER_COMMAND}}`.
|
|
9. Build a temporary Docker context that bakes the rendered payload into
|
|
`/etc/openshell/agent-payload`.
|
|
10. Apply manifest-declared gateway settings.
|
|
11. Resolve provider profile IDs by scanning `profile_paths` in order.
|
|
12. Import each provider profile into the gateway. If an active profile already
|
|
exists, the launcher keeps going and uses it.
|
|
13. Resolve provider credentials from host commands, JSON files, or literal
|
|
manifest values.
|
|
14. Create or update each provider instance and attach every selected provider
|
|
to the sandbox.
|
|
15. Configure and rotate refresh-backed provider credentials when declared by
|
|
the manifest.
|
|
16. Run `openshell sandbox create` from that temporary Dockerfile source.
|
|
17. Inside the sandbox, run `/etc/openshell/agent-payload/runtime/entrypoint.sh`.
|
|
18. The runtime entrypoint starts
|
|
`/etc/openshell/agent-payload/runtime/supervisor.sh`.
|
|
19. The supervisor invokes
|
|
`/etc/openshell/agent-payload/runtime/harnesses/<harness>/exec.sh` as a
|
|
bounded child execution.
|
|
20. Harness adapters prepare harness-local auth/config and execute the agent
|
|
prompt headlessly.
|
|
|
|
The payload directory is baked into the image under `/etc/openshell`, which the
|
|
gator filesystem policy mounts read-only for agent processes. Prompts, skills,
|
|
subagent definitions, and runtime scripts are agent guts, not workspace state.
|
|
Agents should write session artifacts, checkouts, temporary files, and future
|
|
memory records under `/sandbox` or `/tmp` instead.
|
|
|
|
## Runtime Modes
|
|
|
|
Agents can run in `once` or `watch` mode. In `once` mode the supervisor runs one
|
|
harness cycle and exits with the harness result unless the agent emits an
|
|
`OPENSHELL_AGENT_RESULT` sentinel.
|
|
|
|
In `watch` mode the sandbox stays alive while the supervisor repeatedly runs
|
|
bounded harness cycles. The harness must not sleep or poll indefinitely. Instead,
|
|
it performs one reconciliation cycle, then prints a final-line sentinel:
|
|
|
|
```text
|
|
OPENSHELL_AGENT_RESULT {"status":"waiting","next_poll_seconds":900,"reason":"checks_pending"}
|
|
```
|
|
|
|
Supported statuses are `complete`, `waiting`, `blocked`, `transient_failure`, and
|
|
`terminal_failure`. The supervisor sleeps between `waiting` or `blocked` cycles
|
|
without keeping the harness connected, then launches a fresh harness cycle inside
|
|
the same sandbox. During active harness cycles and long sleeps, it prints a
|
|
heartbeat every 60 seconds by default so operators can distinguish deliberate
|
|
work or waiting from a stuck launch. Set `OPENSHELL_AGENT_HEARTBEAT_SECONDS=0`
|
|
to disable heartbeats or another integer to change the interval. In `watch`
|
|
mode, missing or malformed result sentinels and harness transport failures are
|
|
retried indefinitely with bounded backoff; only `complete` and
|
|
`terminal_failure` stop the supervisor. This keeps long-lived agents resilient
|
|
to upstream model errors while leaving durable state ownership to the agent
|
|
domain.
|
|
|
|
The shared runtime does not prescribe the durable state store. Gator uses GitHub
|
|
labels, comments, reviews, and checks. Other agents can use a repository branch,
|
|
issue tracker, object store, database, or another domain-specific store as long
|
|
as each cycle can reconcile from that state.
|
|
|
|
Use `--once` or `--watch` to override the manifest default. Use
|
|
`--poll-interval <seconds>` to override the watch sleep interval.
|
|
|
|
Refresh-backed providers are bootstrapped from manifest credential sources when
|
|
no gateway refresh state exists. Later launches preserve gateway-owned refresh
|
|
material and request a credential rotation first. If that rotation fails, the
|
|
launcher treats the host credential source as a repair source, replaces the
|
|
gateway refresh material, and retries rotation once. Use `--reset-refresh` to
|
|
skip the preserve-first path and intentionally replace gateway refresh material
|
|
from the host credential source before rotating.
|
|
|
|
Long-lived harnesses must not persist revision-scoped provider placeholders such
|
|
as `openshell:resolve:env:v123_TOKEN` into files they reuse across refreshes.
|
|
Persist the current-name alias, for example `openshell:resolve:env:TOKEN`, so the
|
|
sandbox proxy resolves the latest gateway-refreshed credential on each request.
|
|
|
|
## Subagents
|
|
|
|
The launcher injects subagent definitions under
|
|
`/etc/openshell/agent-payload/subagents/`.
|
|
Prompt templates should refer to the generic command instead of a harness-specific
|
|
script:
|
|
|
|
```shell
|
|
bash /etc/openshell/agent-payload/runtime/subagent.sh <subagent-id> < task.md
|
|
```
|
|
|
|
The shared subagent dispatcher forwards the task to the active harness adapter.
|
|
For Codex, this runs a separate bounded `codex exec` invocation using the same
|
|
model and reasoning defaults as the parent harness.
|
|
|
|
## Providers
|
|
|
|
Listing a provider in `agent.yaml` means the provider is attached to the sandbox.
|
|
Provider profiles describe credential shape, endpoint policy, discovery metadata,
|
|
and refresh metadata. The launcher only creates provider instances and supplies
|
|
runtime credential values.
|
|
|
|
`profile_paths` are ordered. The first profile file with the requested `id` wins.
|
|
If the same directory contains duplicate profile IDs, the launcher fails. If a
|
|
later profile path contains a profile ID that was already found, the launcher
|
|
warns that the later file is shadowed.
|
|
|
|
## Gator Example
|
|
|
|
`gator/` is the first manifest-driven agent. It uses:
|
|
|
|
- `gator/agent.yaml` for the launch contract.
|
|
- `gator/prompts/gator.md` for the rendered operator prompt.
|
|
- `gator/providers/` for scoped GitHub and Codex provider profiles.
|
|
- `gator/Dockerfile` for the local sandbox image.
|
|
- `runtime/harnesses/codex/` for Codex installation and execution.
|
|
|
|
Run it through the generic launcher:
|
|
|
|
```shell
|
|
./scripts/agents/run.sh \
|
|
--agent gator \
|
|
--gateway docker-dev \
|
|
"Run gator on PR 1536 and keep watching until it closes or merges."
|
|
```
|