runshiftrunshift

docs / amp

AMP

Agent Message Protocol

powered by runshift

v1.0

Your agent sends a signal. runshift governs it. Your code doesn't change.

March 2026 // runshift.ai/amp // draft

The problem with autonomous agents

AI agents can send emails, publish content, move money, and modify production systems. Most agent frameworks give you no human-in-the-loop AI approval layer. If an agent decides to act, it acts.

AMP exists so every consequential action passes through an operator gate before it executes. One signal. One decision. Full audit trail.

Pull requests for AI agents.

When you pay for something online, your checkout doesn't talk directly to your bank. It talks to Stripe. Stripe validates the transaction, applies rules, and returns approved or declined. Your checkout never touches the payment logic.

AMP works the same way. Your agent doesn't execute consequential actions directly. It sends a signal to runshift. runshift validates it against operator rules and returns approved, rejected, or pending. Your agent never touches the gate logic.

Stripe is in the payment path. runshift is in the agent execution path. Neither controls your code.

You have 3–4 agents running. You can only watch one at a time. You review outputs manually before acting on them. You context-switch between them because there's no single surface that shows you all of them at once.

You're already operating like runshift exists. AMP is the infrastructure for what you're already doing.

control layer

Relay

The operator's interface to every running agent

oversight layer

Trust Gates

Human approval before every consequential action

reporting contract

AMP

The standard surface agents report into

the flow

How a signal moves

Signal

Your agent POSTs an AMP payload to runshift

Gate

runshift holds it and notifies the operator

Resolution

approved | rejected | pending returns to your agent

integration patterns

Two ways to gate — both v1, both first-class

pattern 1

Complete → Signal

Agent runs to completion, then POSTs the AMP payload. Gate fires on the outcome. Operator reviews and approves or rejects.

Best for agents where the output needs review before it's acted on — drafts, reports, summaries.

pattern 2 — coming soon

Signal → Gate → Continue

Agent routes its model call through the runshift proxy. Gate fires before the action executes. Agent receives 202 + gate_id, then waits for webhook callback.

Best for agents where the action itself is consequential — send, publish, delete, transfer.

pattern-1-flow.txt
your agent runs
  → task finishes
  → POST /api/amp/signal with AMP payload
  → gate fires if status: "gate"
  → operator approves/rejects
  → relay returns approved | rejected | pending

the contract

The AMP payload

When an agent completes a run — approved or not — it sends a single structured payload to relay. relay validates it, writes the audit trail, fires the trust gate if required, and records the outcome. No SDK required. One JSON object, one endpoint.

POSTrunshift.ai/api/amp/signal
Authorization: Bearer rs-amp-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

API keys are scoped to workspace and agent. Get yours from the runshift dashboard.

amp-payload.json
{
  // identity
  "agent_id":   "resume-tailor",
  "agent_name": "Resume Tailor",
  "run_id":     "run_01jt4k...",

  // execution
  "status":     "gate",
  "summary":    "Rewrote resume for Senior PM role",
  "action":     "Send tailored resume to applicant",
  "reversible": false,
  "content":    "...",

  // cost (relay calculates from tokens if cost_usd omitted)
  "tokens": {
    "model":  "claude-sonnet-4-5",
    "input":  1842,
    "output": 614
  }
}

summary vs action. summary is what the agent already did — a record. action is what happens next if the operator approves — a proposal. relay shows both in the gate slide.

status: "gate". Send status: "gate" with an action field to trigger an approval gate. relay holds the signal, notifies the operator, and returns approved or rejected.

schema reference

Field definitions

fieldtyperequireddescription
agent_idstringrequiredUnique identifier for the agent. Matched against the registered agent in the runshift roster.
agent_namestringrequiredHuman-readable name shown in the audit trail and gate UI.
statusenumrequiredrunning | gate | done | failed | idle. Use 'gate' to trigger an operator approval gate.
summarystringoptionalWhat the agent did or is doing. Shown in the audit trail. Also accepted as 'task' — either field is valid.
run_idstringoptionalUnique execution ID. relay deduplicates gate signals with the same run_id.
workflow_idstringoptionalOptional workflow or project grouping for the runshift deck.
actionstringoptionalThe proposed action. Required when status is 'gate'. Shown to the operator in the trust gate slide.
reversiblebooleanoptionalWhether the proposed action can be undone. Displayed in the gate UI to inform operator judgment.
contentstringoptionalDraft content for gate review — email body, post draft, code output. Also accepted as 'output'.
cost_usdfloatoptionalAgent-reported cost in USD. If omitted and tokens are provided, relay calculates from model pricing.
tokensobjectoptionalToken usage: { input: number, output: number, model: string }. Used for cost calculation when cost_usd is not provided.

integration

Ways to connect an agent

Pick the path that matches how your agent is built. All paths produce the same result: your agent appears in the runshift roster, relay can see it, and the operator has full control.

recommended

One line of code

Add the AMP reporter to your existing agent. One fetch call at the end of your function. No dependencies, no config.

coming soon

Proxy route

Change base_url in your existing Anthropic client. Register a webhook URL in the dashboard. Gate fires mid-execution. Zero agent refactor.

coming soon

@runshift/agent SDK

Wrap your agent function. Token counting, cost calculation, and error reporting handled automatically.

coming soon

Webhook endpoint

Point any automation tool — Make, Zapier, n8n — at your AMP endpoint.

path 1 — complete → signal

One line. Your agent is live.

At the end of your agent function, post the AMP payload to relay. That's the entire integration.

your_agent.py
import requests, uuid

def resume_tailor(job_description: str, resume: str) -> str:
    """Your existing agent — unchanged."""

    # ... your agent logic ...
    result = rewrite_resume(job_description, resume)
    input_tokens = 1842
    output_tokens = 614

    # ── AMP: one call, agent is live ──
    requests.post("https://runshift.ai/api/amp/signal",
        headers={"Authorization": "Bearer rs-amp-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"},
        json={
        "agent_id":   "resume-tailor",
        "agent_name": "Resume Tailor",
        "run_id":     str(uuid.uuid4()),
        "status":     "gate",
        "summary":    f"Rewrote resume for {job_description[:60]}",
        "action":     "Send tailored resume to applicant",
        "reversible": False,
        "content":    result,
        "tokens": {
            "model":  "claude-sonnet-4-5",
            "input":  input_tokens,
            "output": output_tokens,
        },
    })

    return result

summary vs action. summary is what the agent already did — a record. action is what happens next if the operator approves — a proposal. relay shows both in the gate slide.

path 2 — sdk

@runshift/agent SDK

SDK coming soon.

what happens next

What relay does with the payload

The payload alone is not the product. What relay does after ingestion is the product.

01

Validates the payload — fields, agent_id registration, amp_version, duplicate run_id rejection

02

Writes to the audit trail — immutable, before gate fires

03

Fires the trust gate — if gate_required, shows operator summary + proposed_action + artifacts

04

Records the outcome — execution record, live counter updates

05

Notifies the agent — returns approved | rejected | pending

relay-response.json
// gate_required: true → pending until operator decides
{
  "status":  "pending",
  "gate_id": "gate_01jt...",
  "message": "Awaiting operator approval"
}

// operator approved
{
  "status":  "approved",
  "gate_id": "gate_01jt...",
  "message": "Gate approved by operator"
}

// operator denied
{
  "status":  "rejected",
  "gate_id": "gate_01jt...",
  "message": "Gate rejected by operator"
}
webhook-callback.json
// Pattern 2: runshift POSTs this to your webhook_url on resolution
{
  "gate_id":     "gate_01jt...",
  "status":      "approved",
  "run_id":      "run_01jt4k...",
  "agent_id":    "resume-tailor",
  "resolved_at": "2026-03-06T22:11:14Z",
  "resolved_by": "operator"
}

protocol boundaries

What AMP v1 does and does not do

runshift does

Read the signal payload

Apply operator-defined gate rules

Return approved / rejected / pending

Surface payload to operator for review

Log every gate decision immutably

Notify via Slack

runshift does not

Modify your agent's logic

Change the payload content

Retry your agent

Initiate an agent run (v1.1 — registration payload)

Stop or pause a running agent (v2.0 — heartbeat)

Access anything outside the signal

Start, stop, and manage agents from the runshift dashboard — this is a product capability, not an AMP protocol concern. See the roadmap for when agent lifecycle management arrives in the protocol.

cost calculation

Calculating cost_usd

In v1, agents calculate their own cost using known model pricing. runshift maintains a pricing constants file agents can import.

amp-pricing.ts
export const MODEL_PRICING: Record<string, { input: number; output: number }> = {
  // Anthropic — live pricing (per 1M tokens)
  'claude-sonnet-4-5':           { input: 3.00,  output: 15.00 },
  'claude-haiku-4-5-20251001':   { input: 0.80,  output: 4.00  },
  'claude-opus-4-5':             { input: 15.00, output: 75.00 },

  // OpenAI — coming soon (OpenRouter integration)
  // 'gpt-4o':      { input: 2.50,  output: 10.00 },
  // 'gpt-4o-mini': { input: 0.15,  output: 0.60  },
  // 'o3':          { input: 10.00, output: 40.00 },

  // Google — coming soon (OpenRouter integration)
  // 'gemini-2.5-pro':   { input: 1.25, output: 10.00 },
  // 'gemini-2.5-flash': { input: 0.15, output: 0.60  },
}

export function calculateCost(
  model: string,
  inputTokens: number,
  outputTokens: number,
): number {
  const pricing = MODEL_PRICING[model]
  if (!pricing) return 0
  return (inputTokens / 1_000_000) * pricing.input
       + (outputTokens / 1_000_000) * pricing.output
}

Claude pricing is live. OpenAI and Google model pricing activates with OpenRouter integration.

v1 trust model. relay accepts agent-reported cost and may independently verify when model pricing metadata is available.

roadmap

What AMP becomes

AMP v1 ships with two first-class patterns: completion reporting and proxy-based gating. The protocol is designed to extend — future versions add message types without breaking existing integrations.

v1.0

Completion + proxy patterns

Agent reports on completion (Pattern 1) or routes through the runshift proxy for mid-execution gates (Pattern 2). What you're reading now.

completionproxy
v1.1

Registration payload

Agent registers identity, capabilities, and trust level with relay on startup.

completionproxyregistration
v2.0

Gate request + heartbeat

Agents request gates mid-execution. Cost verification layer added.

completionproxyregistrationgate_requestheartbeatfailure
v3.0

Policy + routing layer

relay becomes the policy surface. Trust levels, governance, routing.

completionproxyregistrationgate_requestheartbeatfailurepolicyroutingaction_receipt