Skip to content

Lifecycle Hooks

Run containers before and after your agent — fetch context from external systems, upload artifacts, notify Slack, clean up resources. Lifecycle hooks let you wire arbitrary setup and teardown logic into the agent execution flow without modifying the agent itself.

How It Works

AgentRun created
  → Pending phase
    → Controller creates workspace PVC (if postRun defined)
    → Controller creates lifecycle RBAC (if rbac defined)
    → PreRun init containers execute sequentially
  → Running phase
    → Agent container runs
  → PostRunning phase (if postRun defined)
    → Controller creates follow-up Job
    → PostRun init containers execute sequentially
  → Succeeded / Failed
    → Workspace PVC cleaned up
    → Lifecycle RBAC garbage-collected (owner reference)

PreRun Hooks

PreRun hooks execute as init containers before the agent starts. They have access to:

Path Description
/workspace Shared working directory — write files here for the agent to read
/ipc IPC bus (tool calls, task input)
/tmp Scratch space

Use cases: Fetch incident context from PagerDuty, clone a git repo, download test data, warm caches.

PostRun Hooks

PostRun hooks execute in a follow-up Job after the agent completes. They receive everything preRun hooks get, plus:

Env Var Description
AGENT_EXIT_CODE "0" on success, non-zero on failure
AGENT_RESULT The agent's final response text (truncated to 32Ki)

The workspace is shared between the agent and postRun hooks via a PersistentVolumeClaim. PostRun failures are best-effort — they're recorded as a PostRunFailed Condition but don't change the agent's final phase.

Use cases: Upload artifacts to S3, post a summary to Slack, clean up temporary resources, trigger downstream pipelines.

Environment Variables

All lifecycle hook containers receive these env vars:

Env Var Description
AGENT_RUN_ID Unique identifier for this agent run
INSTANCE_NAME The SympoziumInstance this run belongs to
AGENT_NAMESPACE Kubernetes namespace
Custom env vars Any spec.env entries from the AgentRun

RBAC for Hooks

By default, lifecycle hook containers run with the sympozium-agent ServiceAccount, which has no Kubernetes permissions. If your hooks need to interact with the Kubernetes API (e.g., create or delete ConfigMaps), declare the required RBAC rules:

spec:
  lifecycle:
    rbac:
      - apiGroups: [""]
        resources: ["configmaps"]
        verbs: ["get", "list", "create", "delete"]
    preRun:
      - name: create-context
        image: bitnami/kubectl:latest
        command: ["kubectl", "create", "configmap", "run-context",
                  "--from-literal=started=$(date)"]
    postRun:
      - name: cleanup-context
        image: bitnami/kubectl:latest
        command: ["kubectl", "delete", "configmap", "run-context"]

The controller creates a namespace-scoped Role and RoleBinding for the run, bound to sympozium-agent. These are garbage-collected when the AgentRun is deleted — no standing permissions.

Examples

Fetch PagerDuty incidents before the agent runs

apiVersion: sympozium.ai/v1alpha1
kind: SympoziumInstance
metadata:
  name: oncall-agent
spec:
  agents:
    default:
      model: gpt-4o
      lifecycle:
        preRun:
          - name: fetch-incidents
            image: curlimages/curl:latest
            command: ["sh", "-c",
              "curl -s -H 'Authorization: Token token=$PD_TOKEN' \
               https://api.pagerduty.com/incidents?statuses[]=triggered \
               > /workspace/context/incidents.json"]
            env:
              - name: PD_TOKEN
                value: "your-pagerduty-token"

The agent's system prompt can then instruct it to read /workspace/context/incidents.json for current incident context.

Upload artifacts to S3 after completion

spec:
  lifecycle:
    postRun:
      - name: upload-report
        image: amazon/aws-cli:latest
        command: ["sh", "-c",
          "aws s3 cp /workspace/report.md s3://my-bucket/reports/$AGENT_RUN_ID.md"]
        env:
          - name: AWS_ACCESS_KEY_ID
            value: "AKIA..."
          - name: AWS_SECRET_ACCESS_KEY
            value: "..."

Create and clean up a ConfigMap

spec:
  lifecycle:
    rbac:
      - apiGroups: [""]
        resources: ["configmaps"]
        verbs: ["create", "delete", "get"]
    preRun:
      - name: create-config
        image: bitnami/kubectl:latest
        command: ["sh", "-c",
          "kubectl create configmap agent-scratch --from-literal=run=$AGENT_RUN_ID"]
    postRun:
      - name: delete-config
        image: bitnami/kubectl:latest
        command: ["kubectl", "delete", "configmap", "agent-scratch"]

PersonaPack with lifecycle hooks

apiVersion: sympozium.ai/v1alpha1
kind: PersonaPack
metadata:
  name: oncall-team
spec:
  personas:
    - name: triage-agent
      systemPrompt: "You are an SRE triage agent..."
      lifecycle:
        preRun:
          - name: fetch-alerts
            image: curlimages/curl:latest
            command: ["sh", "-c", "curl -s $ALERTMANAGER_URL/api/v2/alerts > /workspace/context/alerts.json"]

Agent Sandbox Compatibility

Lifecycle hooks work with both standard Job mode and Agent Sandbox mode:

  • PreRun hooks are injected as init containers into the Sandbox CR — they execute inside the gVisor/Kata sandbox.
  • PostRun hooks always run as a separate follow-up Job (outside the sandbox), since the sandbox is torn down after the agent completes.
  • The workspace PVC is shared between both.

Phases

With lifecycle hooks, the AgentRun phase transitions become:

PendingRunningPostRunningSucceeded (or Failed)

The PostRunning phase is only entered when postRun hooks are defined. Without them, the flow is the standard PendingRunningSucceeded/Failed.