Skip to content

Examples

Every example in this list lives under examples/ in the repo. Two shapes:

  • Config-only recipes — a self-contained .agents/ directory. Drop in, run core-agent, done. No Go code, no custom binary.
  • Library examples — a single main.go you go run. Shows how to wire core-agent into your own Go program.

Pick by what you’re building.


Run with the bundled binary; no Go code on your side.

GKE incident-triage agent that fans out one investigator per service in parallel via spawn_agent, then synthesizes a root-cause report. Wires the GKE MCP server (read-only endpoint) via Application Default Credentials. Use when you have a GKE cluster and want the platform-engineering pattern.

Highlights: parallel subagent fan-out · MCP server integration · read-only by design · multi-model routing tunable (Pro orchestrator + Flash investigators)

A long-lived, propose-only GKE platform operator authored for this runtime — the recommended starting point for a GKE agent on core-agent, and the second half of #704 (whose first half froze kube-platform-agent, below). Triages watcher-driven incidents, delegates single-cluster diagnosis to a cluster specialist with its own content root ("root": "../cluster" — its own persona, six GKE domain skills and read-only gke MCP, none of which the parent sees), and returns a root-cause analysis plus a proposed manifest patch. Self-contained: no content_roots, no @include, no vendored upstream.

The authoring rule it exists to demonstrate is identity → equipment → conduct, not role → lifecycle. Its predecessor imported a persona verbatim and inherited that persona’s lifecycle along with it — a kanban worker that accepts a task, loops until done, files a completion report and exits — which on a live cluster answered a general question in incident-report costume, confabulated a verification with zero tool calls (#639), and looped with no reachable “done”. An overlay cannot out-argue that, and a skill loaded at the point of use speaks last (#703). So the identity is written native and the lifecycle is dropped, not patched.

The difference is measurable rather than asserted. All six cluster/skills/ are native rewrites naming the gke_* MCP read that performs each step — no shell fences, no kubectl/gcloud. The frozen recipe carries 188 waived findings in the recipecheck executability gate; this one is absent from the waiver map entirely, so it is checked with zero waivers and produces zero findings. Every safety claim in the persona is backed by config rather than prose — read-only endpoint, bash and the write tools disabled, plan_mode: "required", safety.watchdog: "enforce", per-turn and per-session cost ceilings — and the recipe’s own test suite pins each one. Minimum daemon image 2.9.0-dev.6 — the config floor is 2.9.0-dev.1, and the deploy/ tree raises it to dev.6 by mounting the users.json Secret directly (#944) and probing GET /healthz (#946) instead of carrying a root initContainer and a TCP probe.

Note what CI here does not prove: recipe_test.go is credential-free and LLM-free, so it establishes that the recipe is well-formed, never that the agent answers well. That is measured by the live GKE drill (#970) against a real cluster. The drill itself is CI-checked offline — dev/ci/presubmits/verify-gke-drill runs its driver end to end against a fake kubectl, curl and gcloud, so a regression in the instrument shows up as a red build rather than as a wasted cluster day.

It ships the cluster deployment for that drill too: a deploy/ kustomize tree (base + 2 × 2 read-only overlays, plus two that compose the apply leg) and a scripts/ operator rig that grants the Workload Identity bindings, builds the content image, mints bearer tokens, deploys the hub and its lookout watcher, breaks a real workload six different ways to fire an incident, attaches the TUI, and tears it all down — walkthrough in DEMO.md. The two enumerated overlay axes are forced rather than chosen: content arrives as an OCI image volume on GKE 1.35+ and via an initContainer copy below it, and tracing to Cloud Trace turns on if the cluster serves GKE Managed OpenTelemetry. That is the rule the tree follows — a forced axis is enumerated, a chosen one is composed — which is why apply-capability, the one decision here with no cluster-side answer, is a component rather than a third axis that would have made the tree 2 × 2 × 2. set-up-demo.sh probes for both, patches your coordinates into the overlay, and then greps the rendered manifest for surviving placeholders before it applies anything — note that the patching rewrites tracked files in place, so a run leaves your checkout dirty with your real project and cluster names, and you want git checkout -- examples/gke-platform-agent/ before committing anything else — a deploy that carries the literal string your-project-id boots healthy, passes every probe, and 403s on its first model call. The other way to earn that 403 is a fresh namespace: Workload Identity principals are per-namespace, so the bindings do not follow the recipe from one demo to the next, and nothing about their absence is visible from the cluster until a turn is already running. grant-iam.sh enables the five APIs and grants the six bindings up front — five on the project and one on the node service account, which a project-scoped grant does not substitute for — and set-up-demo.sh re-checks them on the way out. Only one of the six stops the agent outright; the rest are quieter, and roles/mcp.toolUser is the quietest of all, because without it the model still answers and every cluster read comes back denied, so what you get is a fluent incident report assembled from the alert text alone.

Propose-only is the default, and the deploy tree keeps the exception opt-in: deploy/components/gated-apply/ is a kustomize component, composed only by an overlay that asks for it — overlays/gated-apply and overlays/gated-apply-otel, two stanzas each. It carries three things that have to move together: the daemon’s -c, repointed at the apply-capable content root; the writable plans emptyDir, which has to follow it because record_plan derives its directory from dir(-c); and the RBAC, granting patch on apps/deployments in the target namespace and nothing else. Every proper subset of those three is broken and none of them says so — config without the remount dies at the first plan with a healthy pod and passing probes, config without the RBAC 403s, and RBAC without the config swap leaves the daemon holding patch rights it was never reconfigured to use. One component means no subset is composable. It is not in base — base is every deployment’s posture, and drill scenarios A/B/C depend on no mutating call reaching the cluster. It is not a hand-applied manifest either, because it lives in the target namespace, which kubectl delete namespace on the agent’s namespace never reaches: applied by hand it outlives teardown and a full rebuild, leaving a standing patch grant on a cluster the operator believes is clean. The subject is the part to read twice — kind: User naming GKE’s Workload Identity username, serviceAccount:<PROJECT_ID>.svc.id.goog[<NAMESPACE>/core-agent-daemon], not kind: ServiceAccount and not the principal:// direct-binding form — because a subject that does not match is silently inert: no error, no event, no log line, just an agent that cannot do the one thing it was deployed to do. Only the project id is substituted at deploy time; the namespace in the bracket is a literal that has to agree with what deploy/base hardcodes, so a recipe test reads the daemon’s ServiceAccount out of base and fails when it stops agreeing, rather than anything rewriting it from a variable the manifests ignore. scripts/verify-gated-apply.sh is the check, and it is the only valid one: kubectl auth can-i --as= exercises only the RBAC authorizer while GKE delivers IAM through a webhook keyed to the authenticated identity, so the script runs a pod as the daemon’s ServiceAccount, mints the real token, and probes the verb, the namespace and the resource axes against a Deployment name that does not exist — authorization is evaluated before existence, so authorized answers 404, denied answers 403, and nothing is mutated either way.

The agent that uses those rights is a second content root, gated-apply/, selected entirely by -c — so nothing loads it unless you point at it, and the recipe’s default posture is unchanged by its presence. It is a directory rather than a third config.json because of where this recipe’s configuration actually lives: mcp.json is found by a fixed name inside the agents dir and no config field names a different one, so two MCP surfaces need two agents dirs, and it ships in the content image rather than the deploy tree, so it could not be a kustomize patch either. The extra level pays twice, because AGENTS.md loads from the directory above the agents dir, and this leg needs a persona of its own — the base one is propose-only in seven separate passages, so shipping it unchanged would register the patch tool and then instruct the model not to use it. The two personas are kept in sync by marked stance regions: seven comment-delimited pairs present in both files, wrapping exactly the passages allowed to differ, with tests asserting the files are byte-identical outside them and that every region’s content actually differs — a copy that was never edited passes the first check while telling the apply agent it may not apply. Two configs differ by one line: config.d1.json runs mode: "ask", so the patch waits for a human; config.d2.json runs mode: "allow" with the patch allowlisted, so nothing prompts. D1 is an attended leg for now — the approval_timeout and approval_notify that would let it run with nobody watching require a newer agent version than the overlays pin, and a recipe’s version floor is a union over every config it ships, so declaring them would make the read-only overlays undeployable to buy a field nobody can run yet; a test flips direction at the pin and starts demanding those fields the moment it moves. allow rather than yolo is the point — it denies anything unlisted and never prompts, which is deny-by-default with no human in the loop and no prompt that can hang in a pod with nobody attached. plan_mode: "required" stays on in both, so the propose step does not disappear when the human does; it becomes a recorded artifact instead of a rendered prompt.

Whether that leg actually worked is answered by a drill scenario of its own, and it needed a second scorecard to answer it. The drill’s propose-only sheet asks “did no mutating call reach the cluster?” — a question the apply leg passes by failing, and which a run where the agent did nothing at all passes outright. So the apply scenario declares itself as one, and is graded on a sheet whose fourth box is inverted: instead of “nothing moved”, four witnesses — the Deployment’s generation advanced, its image changed and its replicas went Ready; the Admin Activity audit log names the daemon’s Workload Identity principal on a call the API server granted, which is what proves RBAC was the boundary rather than a stray yolo; record_plan fired before the first patch; and no mutating call outside the grant returned success. The first two are read from the cluster and the audit log rather than from the session, because a transcript is the agent’s account of itself. Each of the four exists because something weaker passes a run that failed: a patch from one unpullable tag to another advances the generation and changes the image string, so readiness is part of witness 1; a refused write is Admin Activity too — that is how the boundary was confirmed in the first place — so witness 2 reads the authorization decision and not just the identity; and the first three all pass on a cluster with no boundary at all, which is what the fourth is for.

Highlights: core-agent-native persona (identity → equipment → conduct) · rooted cluster subagent with its own skills and MCP · six native GKE domain skills, zero recipecheck waivers · propose-only by construction, patch rights opt-in as a kustomize component · apply-capable variant as an opt-in second content root with stance-region-diffed persona · apply leg graded by its own drill scenario, on its own scorecard, from cluster and audit-log witnesses · watchdog enforced by the recipe, not inherited · env-manifest-driven coordinates · credential-free loader validation · kustomize deploy with OCI image-volume content delivery · operator rig with six live break modes

Runs the kube-agents Platform Agent — its persona, 10 governance SOPs, and all 18 skills — on core-agent instead of Hermes. Its workspace instructions and skills load from a content root (content_roots) — the faithful unmodified snapshot vendored under upstream/ by default, or a real kube-agents checkout when you point content_roots at one, so there is no copied platform-skill tree to drift. Translates the remote Google MCPs to a single read-only gke plus developer_knowledge over core-agent’s native HTTP transport, disables bash, and gates every mutation behind record_plan — the agent is propose-only by construction (there is no read-write GKE endpoint), not just by persona. Maps Hermes’ per-cluster Cluster Agent to a declarative cluster subagent with its own content root ("root": "../cluster") — its persona, six GKE domain-diagnostic skills, and read-only MCP all load from a self-contained cluster/ tree, independent of the platform parent — the profiles→subagents story, config-only. Ships a credential-free loader test (no cluster) as the validation, plus a deploy/ kustomize tree that runs the hub daemon + lookout watcher in-cluster — the recipe’s ~1.3 MiB of content ships as an OCI image volume (with an initContainer-copy overlay for clusters below the image-volume floor). Use when you want to run kube-agents content on core-agent, or as the reference for porting a foreign agent framework’s content onto the v2 loader. Minimum daemon image 2.9.0-dev.1 — content_roots and a rooted subagent do not exist before v2.9, and an older daemon boots without either rather than failing (#680).

Highlights: unmodified upstream snapshot (@include + on-demand SOP index) · Hermes-runtime → core-agent component mapping documented · native-HTTP MCP translation · declarative least-privilege cluster subagent · plan-first hub config · hermetic loader validation · GKE deploy via OCI image-volume content distribution

Substrate-enforced plan-before-action. The agent must call record_plan before any write_file/bash/etc. tool call succeeds — read tools stay open during research. Ships four config.json variants: ask / acceptEdits / yolo × plan_mode: "required" so you pick the post-plan friction level, plus a plan_mode: "advisory" one that records the plan artifact without arming the gate (for unattended runs with nobody to approve). Use when you want the safety of a written plan before the agent touches anything.

Highlights: gate-level enforcement (not just AGENTS.md convention) · advisory mode for audit-without-blocking · plan artifacts on disk under .agents/plans/ · /replan slash to revoke + redraft · composes with every existing mode

Deploy core-agent as a long-lived pod in a GKE cluster, reachable by operators over an internal HTTP LoadBalancer. Uses Workload Identity Federation for GKE direct binding (no Google Service Account in the middle — IAM roles bind directly to the KSA’s principal://... identifier) for credential-free Vertex AI inference + GKE read-only MCP access. Publishes an A2A AgentCard at /.well-known/agent-card.json for Google Cloud Agent Registry discovery, and opts into GKE Managed Workload Identity for auto-rotated SPIFFE certs (mTLS-ready; on-ramp to Google Cloud Agent Identity when GA). No Dockerfile in the recipe — uses the published ghcr.io/go-steer/core-agent:2.9.0 image. Use when you want a managed-runtime deployment of core-agent for a platform team or a long-running fleet auditor.

Highlights: WIF-for-GKE direct binding (no GSA / no key files) · internal LoadBalancer (VPC-only) · Agent Registry registration + A2A AgentCard discovery · GKE Managed Workload Identity (SPIFFE certs) · GKE read-only MCP wired · agentic small-model cost routing (Pro orchestrator + Flash tool subagents) · 10Gi PVC for session DB + plans · variant configs for Anthropic-on-Vertex + plan-first + slim image · operator attach via Cloud Workstations / IAP / VPN


Embedding core-agent in your own Go binary. Each is one main.go you go run.

Minimal multi-turn agent — agent.New + a single Run loop. Gemini by default; GOOGLE_API_KEY required. Start here if you want the simplest “how do I drive the agent” answer.

One custom tool plus MCP servers from .agents/mcp.json and skills from .agents/skills/. Shows how operator-defined and library-defined tools coexist.

Parent + subagent end-to-end with no LLM credentials — two scripted-mock providers drive both sides deterministically. The shape to copy when you want a fan-out structure in your own binary.

The standard built-in tools (read_file, list_dir, bash, …) wired into an interactive chat. Closest to “what the CLI does, but you own the binary.”

Ports the generator + checker pair of gke-demos/bouncer — a Python google-adk system that derives verified single-slice TPU preflight smoke tests from completed GKE production workloads — onto core-agent as a library, with the upstream prompts copied verbatim. Shows the four things a config recipe can’t express: a jail for model-authored shell (bwrap + sudo -u agent-runner registered as the only shell, with the built-in bash never wired in), structured output inverted into a tool (output_schema=CheckerResult becomes report_verdict(success, details), fail-closed when never called), an agent calling an agent synchronously for a typed result (replacing subprocess.Popen(["adk","run","checker"]) + a "success: True" stdout grep), and a model decorator (the upstream BaseApiClient.async_request retry monkeypatch becomes an adkmodel.LLM wrapper). Hermetic end-to-end: two scripted transcripts plus a fake kubectl, no credentials. Use as the reference for porting a foreign Python agent framework onto the Go substrate, or for any agent that must contain the shell it hands the model.

Highlights: verbatim upstream prompts (a test fails if one names a tool the port doesn’t register) · bwrap jail asserted flag-by-flag · structured-output-as-tool with fail-closed default · in-process typed hand-off between two agents · pkg/-only imports · autonomous.Run turn/wallclock/cost budgets · one event log across both agents


Long-running agents driven by a goal rather than turn-by-turn operator prompts. All use autonomous.Run.

End-to-end autonomous.Run against the mock “scripted” provider. No LLM credentials needed. Shows the full Goal → cost-bounded loop → terminal-report shape.

Same as above plus the autonomous.Handle API — Pause / Resume / Inject / Stop an in-flight run from another goroutine. Pattern for “long task + operator can steer mid-run.”

Drive a run, hit a tight max_turns budget (simulated crash), then continue from the eventlog. Shows the crash-resume contract.

Wire BackgroundAgentManager and demonstrate in-process spawn end-to-end with no LLM credentials. Use as the template for “parent agent + background subagent workers.”

One assistant turn fans out three independent background subagents via the spawn_agent tool family; their reports drain back into the parent’s next turn automatically. The Claude-Code-style parallel dispatch pattern, hermetic on the scripted mock.

The supervision-tree topology from docs/scheduled-monitoring-design.md — periodic health sweeps with a scheduler + supervisor + worker layout. Pattern for cron-style monitoring agents.


Running core-agent as a long-lived daemon — from the library or from the published binary.

The canonical library embedding of a headless daemon: agent.New → attachadapter.New → session registry → attach.NewServer → runner.WakeLoop. Self-demonstrates over real HTTP (list, status, inject, SSE tail with the capabilities boot frame). Hermetic — echo model, loopback listener, no credentials.

A multi-session daemon built from pkg/compose instead of re-implementing the binary’s wiring: bearer-table auth (BuildMultiSessionAuthn), per-caller session factory + resumer, and ConfigGrantStore persisting “allow always” grants to .agents/config.json. Demonstrates per-identity session isolation (bob can’t see alice’s session) over real HTTP. Hermetic.

Config-only counterpart: the published binary serving two bearer-token users from a static table, with per-caller instruction overlays. Pairs with the multi-session concepts page.

core-agent as a long-lived Cloud Run service: IAM-gated HTTPS, Vertex runtime service account, Dockerfile + prebuilt-image path.

Event-driven, propose-only K8s triage: the daemon plus the event-watcher sidecar and a triage skill that diagnoses through GKE’s read-only MCP endpoint, verifies with wait_and_verify, proposes a fix, and pages on-call. It cannot mutate the cluster — enforced by the read-only endpoint, tools.disable, and a read-only custom IAM role (container.viewer plus container.pods.getLogs), not by the persona. The watcher ships from go-steer/k8s-lookout (ghcr.io/go-steer/lookout, pinned v0.26.0 and deployed as lookout-watch); v0.17.0 is the floor, since that release retired the k8s-event-watcher naming the manifests used to carry. Minimum daemon image 2.9.0-dev.6 — from dev.1 the config uses alerts and tools.wait_and_verify, which an older daemon drops silently rather than failing (#680); from dev.6 the manifests mount the users.json Secret directly and probe GET /healthz, which an older daemon fails loudly (#986).

Drive the agent loop offline by replaying a recorded JSONL transcript through the mock “scripted” provider. Useful for regression tests, reproducing bugs from production captures, and CI runs that don’t need real LLM calls.


The config-only recipes are designed to layer. With the v2 instruction loader, you can drop a recipe’s AGENTS.md into your existing project’s AGENTS.d/ and merge their config.json settings:

Terminal window
# Layer plan-first into an existing GKE-triage setup
mkdir -p <your-project>/.agents/AGENTS.d
cp examples/plan-first/.agents/AGENTS.md \
<your-project>/.agents/AGENTS.d/00-plan-first.md
# Merge plan-first's permissions into the existing config.json
# (plan_mode: "required" + read-tool allowlist)

The recipe READMEs (examples/<name>/README.md) each cover their own composition + tuning notes — read those before forking.


  • Want help picking? Getting started walks the same decision tree end-to-end.
  • Building something new? The patterns in Agent design generalize across these examples — start there for prompt + tool-description guidance.
  • Idea for a recipe? Open a GitHub discussion. Recipes ship as PRs against examples/. CI discovers every recipe automatically and fails the build if the skill content names a tool or a CLI the recipe’s own config can’t produce — a kubectl runbook in a recipe that disables bash is a build break, not a runtime surprise. See Contributing.