Suites: the YAML reference

A suite is one YAML file with the targets under test, the suite’s defaults and its tests. This page lists every key, always in step with what tamesu validate accepts. The JSON Schema for editors is published at schema/tamesu.schema.json; point your editor’s YAML extension at it for completion and inline errors. See Concepts for the model and Configuration for tamesu.yaml.

Strings accept ${VAR} and ${VAR:-default} (environment, non-secret values) and the templates {{uuid}}, {{now}}, {{env.X}}, {{vars.x}}, {{secrets.X}} and {{faker.email}}; REST bodies also take {{session}} and {{message}}. Durations are strings such as 30s, 3m or 1h30m.

Suite

The top level of the file.

KeyTypeRequiredDescriptionExample
suitestringyesSuite name.suite: support-agent
tagslist of stringTags every test of the suite carries.tags: [regression, returns]
varsmap of string to stringValues for {{vars.x}} and for ${VAR} expansion in secret refs.vars: { shop: Mochi Shoes, days: "30" }
secretsmap of string to stringSecret name to mamori ref; overrides the project entry of the same name.secrets: { AGENT_TOKEN: env:AGENT_TOKEN }
defaultsDefaultsPer-test defaults for this suite; they override the project’sdefaults: { timeout: 2m, repeat: 3, pass_threshold: 0.67 }
modelsModelsModel overrides for this suite (default / judge / simulator …)models: { judge: anthropic/claude-sonnet-4-6 }
budgetsBudgetsSpend caps for this suite’s runsbudgets: { max_cost_usd: 2 }
targetsmap of string to TargetyesThe systems under test by name.targets: { api: { type: rest, url: "{{vars.agent_url}}" } }
hooksHooksSetup and teardown around this suite and its runshooks: { before_each: [ { command: ./scripts/reset.sh } ] }
packsmap of string to list of AssertionReusable assertion sets by name for assert entries of the form pack: name; a name also used by a built-in pack (prompt_injection / pii_leakage / system_prompt_leakage / stays_in_scope / discloses_ai / harmful_content / excessive_agency / policy_consistency) replaces it.packs: { tone: [ { judge: "The agent stays polite" } ] }
red_teamRedTeamRed team playbooks: one adversarial test per attack of the chosen playbooks is added to the suite when it loads (tamesu playbooks lists them).red_team: { target: api, agent: "Shop support; never more than 30-day returns" }
remote_agentsmap of string to RemoteAgentAgent2Agent agents tamesu plays for the agent under test to delegate to (by name): a card with skills and scripted replies or a recording proxy in front of a real agent; {{remote..url}} is each one’s URL and every message the agent sends it is recorded for delegated_to.remote_agents: { billing: { skills: [refund], reply: "Refund issued, reference RF-889" } }
testslist of TestyesThe teststests: [ { name: refund policy, target: api, scenario: { goal: "Ask about returns" } } ]

Example:

suite: support
tags: [smoke]
vars: { base_url: "https://api.example.com" }
secrets: { API_TOKEN: env:API_TOKEN }
targets:
  api:
    type: rest
    url: "{{vars.base_url}}/chat"
    headers: { Authorization: "Bearer {{secrets.API_TOKEN}}" }
    response: { text: "$.reply" }
tests:
  - name: refund policy
    target: api
    persona: { name: Marta, style: polite }
    scenario: { goal: "Find out if shoes bought 40 days ago can be returned", max_turns: 4 }
    assert:
      - judge: "The agent states the 30-day return policy"
      - contains: "30 days"

Target

KeyTypeRequiredDescriptionExample
typeone of browser, rest, livekit, sip, realtime, grpc, pipecat, model, twilio, mcp, a2a, voiceyesWhat the target is; it decides which other keys applytype: rest
urlstringThe page (browser) or endpoint (rest / realtime / pipecat / twilio / a2a / voice) or grpc://host:port (grpc) or LiveKit server (livekit).url: "${AGENT_URL:-http://localhost:8080/chat}"
connectConnectConfigHow reaching the agent is retried (attempts / backoff / max_backoff)connect: { attempts: 5, backoff: 1s, max_backoff: 10s }
handoffHandoffSignalHow the agent signals that a person took over (tool / json_path / regex plus where the destination and summary are)handoff: { tool: transfer_to_human, to: department, summary: summary }
browserBrowserOptionsbrowser targets; engine / viewport / locale / mocks / recordingbrowser: { viewport: 1280x800, locale: en-GB, recording: gif }
authBrowserAuthbrowser targets; a pre-authenticated storage stateauth: { storage_state: auth/state.json }
allowed_domainslist of stringDomains the browser agent may navigate to; defaults to the url’s domain.allowed_domains: [shop.example, cdn.example]
methodstringHTTP method; defaults to POST.method: POST
headersmap of string to stringRequest (rest / a2a) or handshake (realtime / pipecat / twilio / voice) headers or call metadata (grpc); put credentials here as {{secrets.NAME}}.headers: { Authorization: "Bearer {{secrets.AGENT_TOKEN}}" }
requestRESTRequestrest targets; how a user turn is sentrequest: { body: { session_id: "{{session}}", message: "{{message}}" } }
responseRESTResponserest targets; how the agent turn is read back (JSONPaths into the response)response: { text: $.reply, tool_calls: "$.tool_calls[*]", done: $.end_session }
streamingone of none, sse, websocketrest targets; none (default) or sse or websocketstreaming: sse
sessionRESTSessionThe session id template (rest / grpc) and session.open: a request made once per run before the target starts (create a conversation / mint a token) whose captures render as {{session.NAME}} in the target’s configuration (every target type).session: { id: "{{uuid}}" }
api_keystringUse {{secrets.NAME}}.api_key: "{{secrets.LIVEKIT_API_KEY}}"
api_secretstringUse {{secrets.NAME}}.api_secret: "{{secrets.LIVEKIT_API_SECRET}}"
tokenstringlivekit: a ready-made access token instead of api_key and api_secret (a hosted platform’s web call; often a {{session.NAME}} capture of session.open); the identity is read from it.token: "{{session.access_token}}"
identitystringlivekit: the simulated caller’s participant identity; default tamesu-sim- or the token’s.identity: "web-user-9"
roomstringRoom name template; defaults to tamesu-{{uuid}}.room: "tamesu-{{uuid}}"
dispatchDispatchlivekit targets; an explicit agent dispatch for the roomdispatch: { agent_name: support-agent }
agent_identity_prefixstringHow to recognise the agent participant; defaults to kind=agent.agent_identity_prefix: agent-
modeone of text, audiolivekit / realtime / pipecat: text (default) or audio.mode: audio
data_topicstringText-mode transport topic; defaults to lk.chat.data_topic: lk.chat
join_timeoutDurationlivekit targets; how long to wait for the agent to join the room (default 20s)join_timeout: 20s
transcript_sourceone of auto, agent, sttAudio mode: where the agent’s words come from; auto (default) takes the agent’s own transcription when it publishes one and transcribes its audio otherwise.transcript_source: stt
directionone of outbound, inboundsip targets; outbound (tamesu calls the agent) or inbound (a trigger makes the agent call tamesu)direction: outbound
tostringThe agent’s phone number in E.164 (outbound); twilio: the To the agent sees in customParameters.to: "+34910000000"
fromstringCaller ID presented by the trunk; twilio: the From the agent sees in customParameters.from: "+34600000000"
trunk_idstringLiveKit SIP trunk; defaults to sip.outbound_trunk_id.trunk_id: ST_abc123
ring_timeoutDurationsip targets; how long the call may ring before it counts as no_answerring_timeout: 30s
max_call_durationDurationsip targets; the call is ended after this longmax_call_duration: 5m
triggerTriggerinbound sip targets; the command or HTTP request that makes the agent call sip.inbound_numbertrigger: { http: { url: "https://agent.example/call", method: POST, body: { to: "{{env.INBOUND}}" } } }
modelstringrealtime: added as ?model= to the url (such as gpt-realtime). model: the Genkit model reference the prompt runs on (default models.default).model: googleai/gemini-2.5-flash
session_updateobject (any keys)realtime: merged into the session.update event (instructions / tools / voice / output modalities).session_update: { instructions: "You are the support agent", voice: alloy }
protocolone of ga, beta, socketiorealtime: the event dialect ga (default) or beta (modalities and the OpenAI-Beta header). rest with streaming websocket: socketio for a socket.io server (messages are {event / data} and pings are answered).protocol: ga
done_toolstringrealtime and model: a function call with this name ends the session; default end_session.done_tool: end_session
turn_timeoutDurationrealtime / grpc / pipecat / a2a / voice: how long to wait for the end of a turn; default 60s.turn_timeout: 60s
tool_resultsobject (any keys)realtime and pipecat: answers to the agent’s function calls by function name. The value (any JSON; string leaves may use {{args.}}) goes back as the function’s output and the response continues. Unlisted functions and done_tool are recorded only.tool_results: { lookup_order: { id: "{{args.id}}", status: shipped } }
audio_formatone of pcm16, g711_ulaw, g711_alawrealtime audio mode: the agent’s output audio; pcm16 (default) or G.711 at 8 kHz.audio_format: pcm16
sample_rateintegerpipecat audio mode: the transport’s sample rate in Hz; default 16000.sample_rate: 16000
idleDurationpipecat / twilio / voice: silence after the agent’s last message or audio that ends a turn; default 2s.idle: 2s
parametersmap of string to stringtwilio: the customParameters of the start event (the Parameter elements of the TwiML Stream); values render {{vars.x}} and {{secrets.X}}.parameters: { campaign: autumn, caller_tier: gold }
commandstringmcp: the stdio server to spawn (an executable); url is the alternative for a streamable HTTP server.command: ./bin/orders-mcp
argslist of stringmcp: arguments of command.args: ["--data", "fixtures/orders.json"]
envmap of string to stringmcp: environment variables for command (templates allowed).env: { LOG_LEVEL: warn, API_KEY: "{{secrets.MCP_KEY}}" }
allow_toolslist of stringmcp: offer only these of the server’s tools to the model.allow_tools: [lookup_order, cancel_order]
call_timeoutDurationmcp: how long one tool call may take; default 60s.call_timeout: 20s
audioVoiceAudiovoice: how the audio travels (encoding / sample_rate / frames binary or json with send and receive).audio: { encoding: pcm16, sample_rate: 16000, frames: binary }
messagesVoiceMessagesvoice: the JSON control messages of the protocol (open / transcript / done / interrupted / tool_calls / dtmf / close); all optional.messages: { transcript: "$[?(@.type=='transcript' && @.role=='assistant')].transcript", done: "$[?(@.type=='status-update' && @.status=='ended')].status", close: { type: hangup } }
streambooleana2a: read the agent’s turn from message/stream (server-sent events) instead of message/send; falls back to message/send when the card says the agent does not stream.stream: true
skillstringa2a: a skill id or name the agent card must offer; the session fails to start otherwise.skill: order-status
metadataobject (any keys)a2a: metadata sent with every message (a tenant or a locale).metadata: { tenant: acme }
transportone of jsonrpc, http, grpca2a: the protocol binding; default the card’s preferred transport or jsonrpc. http is the HTTP+JSON binding and grpc the gRPC one (url grpc://host:port or the card’s interface).transport: grpc
pushPusha2a: receive task updates by push notification on a local listener instead of polling; true or { addr / url } when the agent reaches tamesu through another address.push: true
systemstringmodel: the system prompt under test; {{vars.x}} and {{secrets.X}} render.system: "You are the Mochi Shoes support agent."
system_filestringmodel: a file next to the suite holding the system prompt (instead of system).system_file: prompts/support.md
toolslist of ToolDefmodel: the tools offered to the model and the result tamesu answers each call with.tools: [ { name: lookup_policy, description: The policy for a topic, result: { window_days: 30 } } ]
temperaturenumbermodel: sampling temperature; the provider’s default when unset.temperature: 0.2
max_tokensintegermodel: the most output tokens per call.max_tokens: 800
max_tool_roundsintegermodel: the most call-and-answer rounds in one turn; default 5.max_tool_rounds: 4
fallbacklist of stringmodel: Genkit model references tried in order when model fails; the run records which one answered.fallback: [googleai/gemini-2.5-pro, openai/gpt-4.1-mini]

Example:

targets:
  web:  { type: browser, url: "https://shop.example.com", browser: { viewport: 1280x800 } }
  api:  { type: rest, url: "https://api.example.com/chat", response: { text: "$.reply", done: "$.end_session" } }
  grpc: { type: grpc, url: "grpc://agent.internal:9090" }
  phone:
    type: twilio
    url: "${AGENT_STREAM_URL:-wss://agent.example.com/media}"
    parameters: { caller_tier: gold }
    from: "+15550100"
  prompt:
    type: model
    model: googleai/gemini-2.5-flash
    system_file: prompts/support.md
    tools:
      - name: lookup_policy
        description: The policy for a topic
        parameters: { type: object, properties: { topic: { type: string } } }
        result: { topic: "{{args.topic}}", window_days: 30 }
  voice: { type: livekit, url: "${LIVEKIT_URL}", api_key: "{{secrets.LIVEKIT_API_KEY}}", api_secret: "{{secrets.LIVEKIT_API_SECRET}}", mode: audio }
  phone: { type: sip, direction: outbound, to: "+15550001", from: "+15550002" }
  rt:   { type: realtime, model: gpt-realtime, headers: { Authorization: "Bearer {{secrets.OPENAI_API_KEY}}" }, mode: text }
  bot:  { type: pipecat, url: "wss://bot.example.com/ws", mode: audio }

ToolDef

KeyTypeRequiredDescriptionExample
namestringyesThe tool name the model callsname: lookup_policy
descriptionstringWhat the tool does; the model reads it to decide when to call itdescription: The store policy for a topic
parametersobject (any keys)A JSON schema of the arguments (an object schema); none means no arguments.parameters: { type: object, properties: { topic: { type: string } }, required: [topic] }
resultanyWhat the call returns to the model (any JSON; string leaves may use {{args.}}). Without it the model gets an error object.result: { topic: "{{args.topic}}", window_days: 30 }

Example:

tools:
  - name: lookup_policy
    description: The return policy for a topic
    parameters: { type: object, properties: { topic: { type: string } }, required: [topic] }
    result: { topic: "{{args.topic}}", window_days: 30 }
  - name: end_session
    description: Call when the customer has no more questions

BrowserOptions

KeyTypeRequiredDescriptionExample
engineone of chromiumchromium (the default and only engine)engine: chromium
viewportstringWIDTHxHEIGHT such as 1280x800.viewport: 1280x800
localestringThe browser locale such as en-GB; sets Accept-Languagelocale: en-GB
mockslist of BrowserMockRequests answered by tamesu instead of the network from the start of the session; the mock action adds more at run time.mocks: [ { url: "**/api/stock*", status: 200, body: { stock: 0 } } ]
recordingone of auto, gif, webm, offThe session recording written next to the screenshots. auto (the default) writes recording.webm when ffmpeg is installed and recording.gif otherwise; off disables it.recording: webm

Example:

browser:
  viewport: 1280x800
  locale: en-US
  recording: webm
  mocks:
    - { url: "**/api/recommendations", body: { items: [] } }

BrowserMock

KeyTypeRequiredDescriptionExample
urlstringyesA glob on the full URL (** crosses slashes) or a substring.url: "**/api/stock*"
methodstringOnly this method; default any.method: GET
statusintegerThe answer’s status; default 200.status: 503
bodyanyThe answer: a string as is or any value as JSON.body: { error: "warehouse unavailable" }
headersmap of string to stringResponse headers of the mocked replyheaders: { Content-Type: application/json, Retry-After: "30" }
delay_msintegerHold the answer back this long.delay_ms: 2000
abortbooleanFail the request instead of answering.abort: true

Example:

mocks:
  - { url: "**/api/payment", method: POST, status: 502, delay_ms: 2000 }
  - { url: "**/analytics.js", abort: true }

BrowserAuth

KeyTypeRequiredDescriptionExample
storage_statestringPath to a saved storage state (cookies and local storage).storage_state: auth/state.json

Example:

auth: { storage_state: auth/customer.json }

RESTRequest

KeyTypeRequiredDescriptionExample
bodyanyRequest body template; {{message}} is the user turn and {{session}} the session id.body: { session_id: "{{session}}", message: "{{message}}" }
openanystreaming websocket: a message template sent once after connecting.open: { type: open, session_id: "{{session}}" }
capturemap of string to stringWith response.poll: JSONPaths into the reply to the sent request by name; the values render as {{sent.NAME}} in the poll of the same turn (a message timestamp to poll after).capture: { ts: $.ts }
handshakelist of HandshakeStepstreaming websocket: messages exchanged once after connecting (after open); each step sends a message and with wait reads messages until one satisfies the JSONPath and captures values from it as {{session.NAME}}.handshake: [{ send: { event: client.start, data: { userID: "{{session}}", projectID: "{{vars.project_id}}", authorization: "{{secrets.VF_KEY}}" } }, wait: "$[?(@.event=='client.started')].event" }]

Example:

request:
  body: { session_id: "{{session}}", message: "{{message}}" }
  open: { type: hello, session: "{{session}}" }        # websocket only

RESTResponse

KeyTypeRequiredDescriptionExample
textstringJSONPath of the agent’s reply such as $.reply.text: $.reply
tool_callsstringJSONPath of the tool call names.tool_calls: "$.tool_calls[*]"
donestringJSONPath of a boolean that ends the session.done: $.end_session
turn_donestringstreaming websocket: JSONPath of a boolean that ends the turn; otherwise the turn ends after idle.turn_done: $.done
idleDurationstreaming websocket: silence that ends a turn when no message marks it; default 2s.idle: 2s
joinstringPlain JSON: the separator between several matches of text (a newline between the messages of a reply); default none.join: "\n"
pollRESTPollThe agent answers somewhere else than the request’s response: poll this endpoint for the reply after each turn (a chat channel’s history). text / tool_calls / done then read the poll replies.poll: { url: "{{vars.api}}/conversations/{{session.id}}/activities?watermark={{cursor}}", cursor: $.watermark }
listenRESTListenThe agent answers by calling an API: serve that API locally ({{listen.url}}) and read the reply from the requests the agent makes to it (a bot that answers through its platform’s send endpoint). text / tool_calls / done then read those requests.listen: { addr: "127.0.0.1:8089", path: "POST /bot*/sendMessage", session: $.chat_id }

Example:

response:
  text: "$.reply"
  tool_calls: "$.tool_calls[*]"
  done: "$.end_session"
  turn_done: "$.done"                                    # websocket: the message that ends a turn
  idle: 2s

RESTSession

KeyTypeRequiredDescriptionExample
idstringSession id template; defaults to {{uuid}}.id: "{{uuid}}"
openRESTOpenA request sent once when the session starts (create a conversation / open a chat); its captures render as {{session.NAME}} in every later template.open: { url: "{{vars.api}}/conversations", capture: { id: $.conversationId, token: $.token } }

Example:

session: { id: "{{uuid}}" }

Dispatch

KeyTypeRequiredDescriptionExample
agent_namestringyesThe agent to dispatch explicitly into the room (LiveKit agent dispatch)agent_name: support-agent

Example:

dispatch: { agent_name: support-agent }

Trigger

KeyTypeRequiredDescriptionExample
httpHTTPTriggerAn HTTP request that makes the agent call sip.inbound_numberhttp: { url: "https://agent.example/call", method: POST, body: { to: "{{env.INBOUND}}" } }
commandstringA shell command that makes the agent dial sip.inbound_number.command: "./scripts/call.sh {{env.INBOUND}}"

Example:

trigger:
  http: { url: "https://agent.example.com/callbacks", method: POST, body: { number: "{{sip.inbound_number}}" } }

HTTPTrigger

KeyTypeRequiredDescriptionExample
urlstringyesThe request URL (templates allowed)url: "{{vars.admin_url}}/test/reset"
methodstringHTTP method; default POSTmethod: POST
bodyanyA string is sent as is; a mapping as JSON (templates allowed)body: { suite: "{{env.TAMESU_SUITE}}" }
headersmap of string to stringRequest headers (templates allowed)headers: { Authorization: "Bearer {{secrets.ADMIN_TOKEN}}" }

Example:

http: { url: "https://agent.example.com/dial", method: POST, headers: { Authorization: "Bearer {{secrets.AGENT_TOKEN}}" } }

Test

KeyTypeRequiredDescriptionExample
namestringyesThe test name; unique in the suitename: refund after 40 days
targetstringyesA key of targets.target: api
tagslist of stringTags for —tags selection and the reports; the suite’s tags apply tootags: [returns, smoke]
modeone of text, audioVoice tests: overrides the target’s mode.mode: audio
repeatintegerTimes the test runs to measure variance.repeat: 3
pass_thresholdnumberThe fraction of repeated runs that must pass (0 to 1); default 1pass_threshold: 0.67
retriesintegerRe-runs after an infrastructure failure (never after a failed assertion)retries: 1
timeoutDurationHow long one run may take; default defaults.timeouttimeout: 2m
seedintegerSeed for the simulator prompts and the behaviour RNG.seed: 42
varsmap of string to stringValues for {{vars.}} in this test; on top of the suite’s vars and under —var.vars: { days: "40" }
caseslist of CaseParameterised: the test runs once per case with the case’s vars; each becomes a test named [] tagged case:.cases: [ { name: late, vars: { days: "40" } }, { name: early, vars: { days: "10" } } ]
cases_filestringCases from a file next to the suite: a CSV with a header row (a name column names the case) or a JSON array of objects (a name key names the case).cases_file: cases.csv
fuzz_toolsmap of string to FuzzTool result fuzzing: by tool name the answer the agent gets instead of the configured result (a mode such as error / empty / malformed / hostile or a mapping with mode and delay and result); realtime and pipecat and model targets.fuzz_tools: { lookup_order: error, get_balance: { mode: delay, delay: 8s } }
languageslist of stringThe test runs once per language (BCP 47 such as es or en-GB): each becomes a test named [] tagged lang: whose persona speaks that language and whose judges know it; {{vars.language}} holds it.languages: [en, es, de]
stepslist of StepBrowser tests only.steps: [ { action: { navigate: / } }, { goal: "Add the blue sneakers to the cart" } ]
personaPersonaWho the simulated user ispersona: { name: Marta, style: "polite, slightly confused", language: en }
scenarioScenarioWhat the simulated user wants and when to stopscenario: { goal: "Find out if you can return shoes bought 40 days ago", max_turns: 6 }
counterpartCounterpartAnother target plays the user instead of a persona: two agents talk and the judges grade the exchange (an a2a or any conversational target of the suite).counterpart: { target: supplier, opening: "We can offer 500 units at 12 euros each" }
capturelist of CaptureValues taken from the agent’s turns as they arrive (the first match wins) and offered as {{captured.}} to later script say steps / tool_results / hooks / assertions.capture: [ { name: order_id, regex: "(ORD-[0-9]+)" } ]
assertlist of AssertionThe checks on the run; all must passassert: [ { contains: "30 days" }, { judge: "The agent states the policy" } ]
quarantineQuarantineKnown flaky: the test still runs and its failure is reported as quarantined but it does not fail the run or count as a regression.quarantine: { reason: "SUP-412 flaky since the migration", until: 2026-12-31 }

Example:

tests:
  - name: return window
    target: api
    tags: [returns]
    repeat: 3
    pass_threshold: 0.67
    vars: { days: "40" }
    capture:
      - { name: order_id, regex: "order (ORD-[0-9]+)" }
    persona: { name: Marta, style: polite, language: en }
    scenario: { goal: "Ask whether shoes bought {{vars.days}} days ago can be returned", max_turns: 4 }
    assert:
      - judge: "The agent states the 30-day return policy"
      - tool_called: lookup_policy
  - name: refund status
    target: api
    quarantine: { reason: "SUP-412 flaky since the payments migration", until: 2026-12-31 }
    scenario: { goal: "Ask where the refund for order 1234 is", max_turns: 3 }
    assert:
      - judge: "The agent gives a refund date or asks for the order"

Case

KeyTypeRequiredDescriptionExample
namestringThe case’s name; default case 1 / case 2…name: late
varsmap of string to stringyesValues for {{vars.}} in this case.vars: { days: "40", expect: "not possible" }

Example:

cases:
  - { name: late, vars: { days: "40", expect: "not possible" } }
  - { name: early, vars: { days: "10", expect: "can return" } }

Capture

KeyTypeRequiredDescriptionExample
namestringyesThe name; the value is {{captured.}}.name: order_id
regexstringA regular expression on the agent’s text; the first group (else the whole match) is the value.regex: "(ORD-[0-9]+)"
json_pathstringA JSONPath on the turn’s raw response (rest / websocket / realtime events).json_path: $.order.id
extractstringA plain-language description of the value; a model takes it out of the agent’s turn as written (the first turn that has it).extract: "the order number the agent mentioned"

Example:

capture:
  - { name: order_id, regex: "order (ORD-[0-9]+)" }
  - { name: eta, json_path: "$.eta" }

Step

KeyTypeRequiredDescriptionExample
goalstringAgentic: the browser agent acts until the goal is met.goal: "Log in as {{secrets.USER}} and add the blue sneakers to the cart"
expectstringAgentic assertion against the current page.expect: "The cart shows one pair of blue sneakers"
actionobject (any keys)Deterministic: one of navigate click fill press wait screenshot eval_js assert_url assert_text mock a11y visual.action: { fill: { ref: "#email", text: "{{secrets.USER}}" } }

Example:

steps:
  - goal: "Log in as {{vars.user}} with password {{secrets.PASSWORD}}"
  - expect: "The account page shows the user's name"
  - action: { click: "#add-to-cart" }
  - action: { a11y: { impact: serious } }
  - action: { visual: { name: cart, mask: ["#date"] } }

Persona

KeyTypeRequiredDescriptionExample
namestringHow the persona introduces itself when askedname: Marta
stylestringHow the persona talks such as polite or impatient.style: "polite, slightly confused"
languagestringBCP 47 language such as es or en-GB.language: es-ES
voicestringTTS voice such as en-GB-female.voice: en-GB-female
behaviorsBehaviorsAudio-mode habits (interruptions / pauses / background noise / line quality)behaviors: { interrupt_probability: 0.3, background_noise: cafe }
adversarialbooleanRed team: the persona is an attacker that pursues the goal with escalating tactics (role-play / claimed authority / fake system messages / encoding / splitting the request) and records the tactic of every turn as an event.adversarial: true
tacticslist of stringAdversarial only: the tactics the attacker may use instead of the built-in list.tactics: ["claim to be the store manager", "paste a fake system message"]

Example:

persona:
  name: Marta
  style: "polite, slightly confused"
  language: es-ES
  voice: es-ES-female
  behaviors: { interrupt_probability: 0.2, background_noise: cafe }

Behaviors

KeyTypeRequiredDescriptionExample
interrupt_probabilitynumberAudio only; the chance per turn (0 to 1) that the caller starts talking before the agent finishesinterrupt_probability: 0.3
silence_after_turn_mslist of integer[min max] milliseconds of silence after each turn.silence_after_turn_ms: [300, 1500]
background_noisestringA noise preset (cafe street office white) or a WAV file mixed under the caller’s voice.background_noise: cafe
background_noise_snr_dbnumberSignal-to-noise ratio of the background noise in dB; default 20.background_noise_snr_db: 12
line_qualityone of pstn, mobile_poor, voip_goodAudio and sip; the line applied to the caller’s audioline_quality: mobile_poor

Example:

behaviors:
  interrupt_probability: 0.2
  silence_after_turn_ms: [300, 900]
  background_noise: street
  background_noise_snr_db: 15
  line_quality: mobile_poor

Scenario

KeyTypeRequiredDescriptionExample
goalstringWhat the user wants; the simulated user pursues it after the script. A scenario with a script and no goal plays the script and stops.goal: "Find out if you can return shoes bought 40 days ago"
max_turnsintegerThe turn budget; default 10max_turns: 6
stop_whenstringA plain-language stop condition judged after each agent turn.stop_when: "the agent has given a clear yes or no"
scriptlist of ScriptStepDeterministic steps played before the simulated user takes over: say on any conversational target; wait_for and dtmf on sip and livekit.script: [ { say: "Hi, I have a question about order ORD-1001" }, { wait_for: "press 2" }, { dtmf: "2" } ]

Example:

scenario:
  goal: "Reach the billing department and ask about a double charge"
  max_turns: 6
  stop_when: "the agent has confirmed a refund or clearly refused one"
  script:
    - wait_for: "press 2 for billing"
    - dtmf: "2"
    - say: "I was charged twice for order {{captured.order_id}}"

ScriptStep

KeyTypeRequiredDescriptionExample
wait_forstringWait until the transcript fuzzily contains this text.wait_for: "press 2 for billing"
dtmfstringDigits to send.dtmf: "2"
saystringExact text to say before the persona takes over.say: "What is the status of {{captured.order_id}}?"
dataobject (any keys)A structured part sent with the step (a2a: a data part); alone or with say.data: { order: ORD-1001, reason: damaged }
fileslist of ScriptFileFiles sent with the step (a2a: file parts): a path relative to the suite or a URIfiles: [ "fixtures/invoice.pdf", { uri: "https://cdn.example.com/photo.jpg", mime: image/jpeg } ]

Example:

script:
  - wait_for: "press 2 for billing"
  - dtmf: "2"
  - say: "Hello, I was charged twice"

Assertion

KeyTypeRequiredDescriptionExample
judgeJudgeA model grades the run against a rubric (a string or an object with rubric / model / scale / min)judge: "The agent states the 30-day window and promises no exception"
containsstringThe agent transcript contains this text.contains: "30 days"
not_containsstringNo agent turn contains this textnot_contains: "90 days"
regexstringA regular expression the agent transcript matches.regex: "(?i)30 days"
not_regexstringA regular expression no agent turn matches.not_regex: '\b\d{16}\b'
similar_toSimilarSome agent turn means the same as this text (embedding similarity of at least min).similar_to: { text: "Returning after 40 days is not possible", min: 0.7 }
json_pathJSONPathAssertionrest and a2a: a path in the turn’s response (the task for a2a) that exists and with equals has that value.json_path: { path: $.status, equals: ok }
statusintegerrest only: the HTTP status of every turn.status: 200
tool_calledToolCallThe agent called the tool (a name or an object with name / args / schema)tool_called: { name: lookup_policy, args: { topic: returns } }
tool_not_calledToolCallThe agent never called the tool (a name or an object with name / args)tool_not_called: issue_refund
tool_sequenceToolSequenceThe agent called these tools in this order (a subsequence of its calls; exact for the whole list).tool_sequence: { calls: [lookup_order, issue_refund], exact: false }
delegated_toDelegationThe agent sent a message to this remote agent (a name or an object with agent / contains / regex); remote_agents declares them.delegated_to: { agent: billing, contains: refund }
dtmf_receivedDTMFAssertionsip and livekit audio: digits the agent sent.dtmf_received: { equals: "1234#" }
call_ended_byStringListsip: agent caller timeout error no_answer busy.call_ended_by: [agent, caller]
transferred_toMatchessip: target of a SIP REFER.transferred_to: { matches: "^sip:billing@" }
evaluatorEvaluatorA registered Genkit evaluator scores the run (a name or an object with name / min)evaluator: { name: genkitEval/faithfulness, min: 0.8 }
metricMetricAssertionA metric of the run satisfies one comparator (lt / lte / gt / gte / eq)metric: { name: turn_latency_ms_p95, lt: 3000 }
turnsIntCompareNumber of agent turns.turns: { lte: 6 }
capturedCapturedAssertionA value captured during the run exists and equals or matches.captured: { name: order_id, matches: "^ORD-" }
goldenGoldenAssertionThe agent’s turns against reference turns: a file next to the suite or inline turns; exact / similar / judge.golden: { file: golden/refund.json, mode: similar, min: 0.8 }
customstringA Go-registered assertion by name.custom: no_competitor_names
groundedGroundedAssertionEvery factual claim the agent made is supported by the retrieval context (a tool’s results or a response field or given text); a judge lists the claims.grounded: { context: { tool: search_docs }, min: 0.9 }
handoffHandoffAssertionThe agent handed the conversation to a person (true) or did not (false); or when and to whom and with what summary.handoff: { within_turns: 3, to: "^billing$" }
httpStateHTTPThe state of your backend after the conversation: a request whose status and body are checked.http: { url: "{{vars.admin_url}}/orders/{{captured.order_id}}", json_path: { path: $.status, equals: refunded } }
commandStateCommandThe state of your backend after the conversation: a shell command whose exit code and output are checked.command: { run: "./scripts/ledger.sh {{captured.order_id}}", contains: credited }
packstringA reusable assertion set: a built-in red-team pack (prompt_injection / pii_leakage / system_prompt_leakage / stays_in_scope / discloses_ai / harmful_content / excessive_agency / policy_consistency) or one of the suite’s packs; it expands into its assertions when the suite loads.pack: pii_leakage
on_turnOnTurnApply the assertion to one agent turn (a number or last) instead of the whole runon_turn: last

Example:

assert:
  - judge: "The agent states the 30-day return policy and does not promise an exception"
  - judge: { rubric: "The tone stays warm", scale: "1-5", min: 4 }
  - contains: "30 days"
  - not_regex: "[0-9]{16}"
  - tool_called: { name: lookup_policy, args: { topic: returns } }
  - tool_sequence: { calls: [lookup_policy, end_session] }
  - metric: { name: turn_latency_ms_p95, lt: 3000 }
  - turns: { lte: 6 }
  - captured: { name: order_id, matches: "^ORD-" }
  - golden: { file: golden/refund.json, mode: similar, min: 0.8 }
  - handoff: { within_turns: 3, to: "^billing$" }
  - grounded: { context: { tool: search_docs } }
  - http: { url: "{{vars.admin_url}}/orders/{{captured.order_id}}", json_path: { path: "$.status", equals: refunded } }
  - command: { run: "./scripts/check-refund.sh {{captured.order_id}}", contains: refunded }
  - pack: pii_leakage
  - { contains: "goodbye", on_turn: last }

Judge

Either a string (The rubric; the judge model and a binary scale are the defaults), or an object with:

KeyTypeRequiredDescriptionExample
rubricstringyesWhat the judge checks in plain language.rubric: "The agent apologises before explaining the policy"
modelstringGenkit model reference; defaults to models.judge.model: anthropic/claude-sonnet-4-6
scaleone of binary, 1-5binary (pass or fail) or 1-5 with min.scale: 1-5
minnumberMinimum score to pass on the 1-5 scale.min: 4

Example:

judge: { rubric: "The agent apologises before explaining the policy", scale: "1-5", min: 4, model: anthropic/claude-sonnet-4-6 }

ToolCall

Either a string (The tool name), or an object with:

KeyTypeRequiredDescriptionExample
namestringyesTool name as the target reports it.name: reschedule_booking
argsobject (any keys)Argument values that must match (a subset of the call’s arguments).args: { day: saturday }
schemaobject (any keys)tool_called: a JSON Schema every call’s arguments must validate against.schema: { type: object, required: [day], properties: { day: { enum: [saturday, sunday] } } }

Example:

tool_called: { name: lookup_policy, args: { topic: returns } }

ToolSequence

KeyTypeRequiredDescriptionExample
callslist of stringyesTool names in the order they must be called.calls: [lookup_order, issue_refund]
exactbooleanThe run’s tool calls must be exactly this list; default a subsequence.exact: true

Example:

tool_sequence: { calls: [lookup_policy, issue_refund], exact: false }

Evaluator

Either a string (The evaluator name), or an object with:

KeyTypeRequiredDescriptionExample
namestringyesGenkit evaluator name such as genkitEval/faithfulness.name: genkitEval/faithfulness
minnumberMinimum score to pass (0 to 1).min: 0.8

Example:

evaluator: { name: genkitEval/faithfulness, min: 0.8 }

JSONPathAssertion

KeyTypeRequiredDescriptionExample
pathstringyesJSONPath into the response body such as $.status.path: $.status
equalsanyThe value the path must equal.equals: refunded

Example:

json_path: { path: "$.status", equals: ok }

DTMFAssertion

KeyTypeRequiredDescriptionExample
equalsstringyesThe exact digits such as 1234#.equals: "1234#"

Example:

dtmf_received: { equals: "9" }

Matches

KeyTypeRequiredDescriptionExample
matchesstringyesA regular expression such as ^sip:billing@.matches: "^sip:billing@"

Example:

transferred_to: { matches: "^sip:billing@" }

MetricAssertion

KeyTypeRequiredDescriptionExample
namestringyesMetric name such as turn_latency_p95_ms.name: turn_latency_ms_p95
ltnumberPasses when the metric is below this valuelt: 3000
ltenumberPasses when the metric is at most this valuelte: 3000
gtnumberPasses when the metric is above this valuegt: 0
gtenumberPasses when the metric is at least this valuegte: 1
eqnumberPasses when the metric equals this valueeq: 1

Example:

metric: { name: time_to_first_audio_ms_p95, lt: 1500 }

IntCompare

KeyTypeRequiredDescriptionExample
ltintegerPasses when the count is below this valuelt: 7
lteintegerPasses when the count is at most this valuelte: 6
gtintegerPasses when the count is above this valuegt: 1
gteintegerPasses when the count is at least this valuegte: 2
eqintegerPasses when the count equals this valueeq: 3

Example:

turns: { lte: 6 }

CapturedAssertion

KeyTypeRequiredDescriptionExample
namestringyesThe captured value’s name (capture.name)name: order_id
equalsstringThe exact value expectedequals: ORD-1001
matchesstringA regular expression the value must match.matches: "^ORD-[0-9]+$"

Example:

captured: { name: order_id, matches: "^ORD-[0-9]+$" }

GoldenAssertion

KeyTypeRequiredDescriptionExample
filestringA JSON file next to the suite: an array of agent turns or a transcript.json; written by tamesu run —update-golden.file: golden/refund.json
turnslist of stringInline reference turns instead of a file.turns: ["Our return window is 30 days.", "You're welcome!"]
modeone of exact, similar, judgeexact (normalised text) / similar (word overlap at least min; the default) / judge (the judge decides whether each turn means the same).mode: similar
minnumbersimilar: the minimum word overlap (0 to 1); default 0.8.min: 0.8

Example:

golden: { file: golden/refund.json, mode: judge }

OnTurn

Evaluate the assertion on one agent turn: a 1-based number or last.

One of: integer, one of last.

Example:

- { contains: "goodbye", on_turn: last }
- { json_path: { path: "$.end_session", equals: true }, on_turn: 3 }

StringList

One of: string, list of string.

Example:

call_ended_by: [agent, caller]

Hooks

KeyTypeRequiredDescriptionExample
before_alllist of HookRun once before the suite’s first test (the project’s first).before_all: [ { command: ./scripts/seed.sh } ]
after_alllist of HookRun once after the suite’s last test.after_all: [ { command: ./scripts/teardown.sh, timeout: 2m } ]
before_eachlist of HookRun before every run of every test; a failure is an infrastructure error for that run.before_each: [ { http: { url: "{{vars.admin_url}}/reset", method: POST } } ]
after_eachlist of HookRun after every run; a failure is logged and does not change the run’s result.after_each: [ { command: "./scripts/cleanup.sh $TAMESU_SESSION" } ]

Example:

hooks:
  before_all: [ { command: ./scripts/seed.sh } ]
  before_each: [ { http: { url: "{{vars.admin_url}}/reset", method: POST } } ]
  after_each: [ { command: "./scripts/cleanup.sh $TAMESU_SESSION", timeout: 30s } ]

Hook

KeyTypeRequiredDescriptionExample
commandstringA shell command (sh -c).command: ./scripts/seed.sh
httpHTTPTriggerAn HTTP request; a status of 400 or more fails the hook.http: { url: "https://admin.example.com/reset", method: POST }
timeoutDurationHow long the hook may take; default 60s.timeout: 30s

Example:

- command: ./scripts/seed.sh
- http: { url: "https://admin.example.com/reset", method: POST, headers: { Authorization: "Bearer {{secrets.ADMIN_TOKEN}}" } }

Defaults

KeyTypeRequiredDescriptionExample
timeoutDurationHow long one run may take; default 3mtimeout: 3m
repeatintegerTimes each test runs.repeat: 3
retriesintegerRe-runs after an infrastructure failure; default 0retries: 1
pass_thresholdnumberFraction of repeats that must pass (0 to 1].pass_threshold: 0.67
max_goal_stepsintegerBrowser: the most actions the agent may take per goal step; default 15.max_goal_steps: 15
assertlist of AssertionAssertions every test must pass (results named defaults/); a suite’s entries run after the project’s. Packs apply.assert: [ { pack: pii_leakage } ]
slomap of string to numberMaximum per metric on every test such as turn_latency_ms_p95: 3000 or llm_cost_usd: 0.05 (results named slo/); a metric a test does not measure passes.slo: { turn_latency_ms_p95: 3000, llm_cost_usd: 0.05 }

Example:

defaults:
  timeout: 3m
  repeat: 1
  retries: 1
  pass_threshold: 1
  assert:
    - pack: pii_leakage
  slo:
    turn_latency_ms_p95: 3000
    llm_cost_usd: 0.05

Models

KeyTypeRequiredDescriptionExample
defaultstringThe model for every job not given its own (a Genkit reference such as googleai/gemini-2.5-flash)default: googleai/gemini-2.5-flash
judgestringThe model that grades rubrics and grounding checks; default the default modeljudge: anthropic/claude-sonnet-4-6
browser_agentstringThe model that plans and acts on browser goalsbrowser_agent: googleai/gemini-2.5-pro
simulatorstringThe model that plays the simulated usersimulator: openai/gpt-4.1-mini
scenario_generatorstringThe model tamesu generate drafts tests withscenario_generator: googleai/gemini-2.5-pro
embedderstringThe embedding model of similar_to assertions such as googleai/text-embedding-004 or openai/text-embedding-3-small; defaults to the usual embedder of default’s provider.embedder: googleai/text-embedding-004
credentialsmap of string to stringPlugin name to {{secrets.NAME}} when the secret is not named conventionally.credentials: { googleai: "{{secrets.MY_GEMINI_KEY}}" }
fallbackModelFallbackThe models tried in order when a job’s model keeps failing.fallback: { judge: [googleai/gemini-2.5-pro] }
retryModelRetryHow a model call is retried before the next model is tried.retry: { attempts: 3, backoff: 500ms, max_backoff: 10s }

Example:

models:
  default: googleai/gemini-2.5-flash
  judge: anthropic/claude-sonnet-4-6
  simulator: openai/gpt-4.1-mini
  fallback:
    default: [openai/gpt-4.1-mini]
    judge: [googleai/gemini-2.5-pro]
  retry: { attempts: 3, backoff: 500ms, max_backoff: 10s }

Budgets

KeyTypeRequiredDescriptionExample
max_cost_usdnumberCap on tamesu’s own model spend per run (exit code 4 when exceeded); default 5max_cost_usd: 2
max_tokensintegerCap on tamesu’s own tokens per run; default 2_000_000max_tokens: 500000

Example:

budgets: { max_cost_usd: 2, max_tokens: 500000 }

Duration

A duration such as 30s, 3m or 1h30m.

Type: string.

Example:

timeout: 90s

ConnectConfig

KeyTypeRequiredDescriptionExample
attemptsintegerTries to reach the agent when the connection is refused or the agent answers 5xx; default 3 and 1 retries nothing.attempts: 5
backoffDurationThe wait before the second try; default 500ms and doubled every try with jitter.backoff: 1s
max_backoffDurationThe longest wait between tries; default 5s.max_backoff: 10s

Example:

connect: { attempts: 5, backoff: 1s, max_backoff: 10s }

Counterpart

KeyTypeRequiredDescriptionExample
targetstringyesA key of targets: the agent that plays the user.target: supplier
openingstringThe counterpart’s first message (templates allowed); without it the counterpart answers the scenario goal first.opening: "We can offer 500 units at 12 euros each, delivery in 3 weeks"

Delegation

Either a string (The remote agent’s name), or an object with:

KeyTypeRequiredDescriptionExample
agentstringyesThe remote agent’s name (a key of remote_agents).agent: billing
containsstringText some message to the agent must contain.contains: "refund"
regexstringA regular expression some message to the agent must match.regex: "ORD-\d+"

Fuzz

Either a string (The fuzz mode: error, empty, null, malformed, hostile, huge, wrong_type or delay), or an object with:

KeyTypeRequiredDescriptionExample
modeone of error, empty, null, malformed, hostile, huge, wrong_type, delay, customyesWhat the tool answers instead of its result.mode: delay
delaystringHow long the answer is held back (any mode; the whole point of delay).delay: 8s
resultanyThe answer in custom mode (any JSON).result: { ok: false, reason: "insufficient funds" }

Example:

fuzz_tools:
  lookup_order: error                      # the mode alone
  get_balance: { mode: delay, delay: 8s }  # the normal answer, late
  refund: { mode: custom, result: { ok: false, reason: "insufficient funds" } }

GroundedAssertion

KeyTypeRequiredDescriptionExample
contextGroundedContextyesWhere the retrieval context is: a tool whose results tamesu saw or a JSONPath into every raw response or text given here.context: { tool: search_docs }
minnumberThe fraction of claims that must be supported (0 to 1); all of them when omitted.min: 0.9
modelstringThe judge model for this check (a Genkit reference); defaults to models.judge.model: googleai/gemini-2.5-pro

Example:

grounded: { context: { tool: search_docs }, min: 0.9 }

GroundedContext

KeyTypeRequiredDescriptionExample
toolstringA tool whose results are the context (model and realtime and pipecat targets).tool: search_docs
json_pathstringA JSONPath into every raw response selecting the passages (rest / websocket / realtime events).json_path: "$.sources[*].text"
textlist of stringPassages given in the suite.text: ["Returns are accepted within 30 days of delivery."]

Example:

context: { json_path: "$.sources[*].text" }

HandoffAssertion

Either a string (Whether the agent must have handed the conversation to a person), or an object with:

KeyTypeRequiredDescriptionExample
expectedbooleanWhether a handoff must have happened; true when omitted.expected: false
within_turnsintegerThe latest agent turn the handoff may happen on.within_turns: 3
tostringA regular expression the destination must match.to: "^billing$"
summary_containslist of stringTexts the handoff summary must contain.summary_contains: ["ORD-1001", "late"]
summarystringA judge rubric for the handoff summary.summary: "The summary names the order and the problem"

Example:

handoff: { within_turns: 3, to: "^billing$", summary_contains: ["ORD-"], summary: "The summary names the order and the problem" }

HandoffSignal

KeyTypeRequiredDescriptionExample
toolstringA tool call that hands off such as transfer_to_human.tool: transfer_to_human
json_pathstringA path in the turn’s raw response that is present and not false when the agent hands off (rest / websocket / realtime).json_path: $.handoff
regexstringA regular expression on the agent’s reply that means a handoff.regex: "(?i)transferring you to (\w+)"
tostringWhere the destination is: the tool argument name or a JSONPath in the response (or the regex’s first group when tool and json_path are unset).to: department
summarystringWhere the summary passed to the person is: the tool argument name or a JSONPath in the response.summary: $.handoff.note

Example:

handoff: { tool: transfer_to_human, to: department, summary: summary }

HandshakeStep

KeyTypeRequiredDescriptionExample
sendanyThe message template to send (socket.io: a mapping with event and data).send: { event: session.create, data: {} }
waitstringA JSONPath that must be truthy on a received message before the next step; a filter such as $[?(@.event==‘client.started’)].event selects the message itself.wait: "$[?(@.event=='session.created')].event"
capturemap of string to stringJSONPaths into the awaited message by name; {{session.NAME}} renders each value.capture: { session_key: "$[?(@.event=='session.created')].data.sessionKey" }
timeoutDurationHow long to wait for the message; default the turn timeout.timeout: 10s

ModelFallback

KeyTypeRequiredDescriptionExample
defaultlist of stringModels tried in order after a job’s model failed; for every job without a list of its own.default: [openai/gpt-4.1-mini]
judgelist of stringModels tried in order after the judge’s model failed.judge: [googleai/gemini-2.5-pro]
simulatorlist of stringModels tried in order after the simulator’s model failed.simulator: [anthropic/claude-sonnet-4-6]
browser_agentlist of stringModels tried in order after the browser agent’s model failed.browser_agent: [googleai/gemini-2.5-flash]
scenario_generatorlist of stringModels tried in order after the scenario generator’s model failed.scenario_generator: [googleai/gemini-2.5-flash]
embedderlist of stringEmbedding models tried in order after embedder failed; the default list does not apply to embeddings.embedder: [openai/text-embedding-3-small]

Example:

fallback:
  default: [openai/gpt-4.1-mini]
  judge: [googleai/gemini-2.5-pro]
  simulator: [anthropic/claude-sonnet-4-6]

ModelRetry

KeyTypeRequiredDescriptionExample
attemptsintegerTries per model on a rate limit or an outage or a timeout before the next fallback; default 3 and 1 retries nothing.attempts: 3
backoffDurationThe wait before the second try; default 500ms and doubled every try with jitter.backoff: 500ms
max_backoffDurationThe longest wait between tries; default 10s.max_backoff: 10s

Example:

retry: { attempts: 3, backoff: 500ms, max_backoff: 10s }

Push

Either a string (true listens on a free local port), or an object with:

KeyTypeRequiredDescriptionExample
enabledbooleanfalse turns push off; default true in the mapping form.enabled: true
addrstringAddress to listen on such as 0.0.0.0:8099; default a free port on 127.0.0.1.addr: 0.0.0.0:8099
urlstringThe URL the agent calls (a tunnel or a container address); default http://.url: https://tamesu.tunnel.example.com

Quarantine

KeyTypeRequiredDescriptionExample
reasonstringyesWhy the test is quarantined; usually the ticket that tracks it.reason: "SUP-412 flaky since the payments migration"
untilstringThe last day (YYYY-MM-DD) of the quarantine; from the next day the test counts again. Without it the quarantine lasts until removed.until: 2026-12-31

Example:

quarantine: { reason: "SUP-412 flaky since the payments migration", until: 2026-12-31 }

RESTListen

KeyTypeRequiredDescriptionExample
addrstringAddress to listen on such as 127.0.0.1:8089; default a free port on 127.0.0.1. The URL renders as {{listen.url}}.addr: "127.0.0.1:8089"
pathstringThe requests that carry replies: an optional method then a path with * wildcards such as POST /bot*/sendMessage; a trailing / matches the prefix. Default every request. Other requests get reply but are not read.path: "POST /bot*/sendMessage"
sessionstringJSONPath into the incoming payload that holds the session id ($.chat_id); routes each request to its session when tests run in parallel. Default the oldest open session.session: $.chat_id
replyanyThe JSON tamesu answers the agent with (templates allowed); default {ok: true}.reply: { ok: true, result: { message_id: 1 } }
statusintegerThe status answered; default 200.status: 200
timeoutDurationHow long to wait for the agent’s request before the turn fails; default 60s.timeout: 30s
idleDurationKeep reading after the first reply until nothing new arrives for this long (an agent that answers in several messages); unset completes the turn on the first request with content.idle: 1s

RESTOpen

KeyTypeRequiredDescriptionExample
urlstringyesThe request URL (templates allowed).url: "https://directline.botframework.com/v3/directline/conversations"
methodstringHTTP method; defaults to POST.method: POST
headersmap of string to stringRequest headers (templates allowed).headers: { Authorization: "Bearer {{secrets.DIRECT_LINE_SECRET}}" }
bodyanyBody template; none by default.body: { user: { id: "{{session}}" } }
capturemap of string to stringJSONPaths into the JSON reply by name; {{session.NAME}} renders each value.capture: { id: $.conversationId, token: $.token }

RESTPoll

KeyTypeRequiredDescriptionExample
urlstringyesThe endpoint to poll; {{session.NAME}} and {{sent.NAME}} captures and {{cursor}} render.url: "https://slack.com/api/conversations.replies?channel={{vars.channel}}&ts={{sent.ts}}"
methodstringHTTP method; defaults to GET.method: GET
headersmap of string to stringRequest headers (templates allowed).headers: { Authorization: "Bearer {{secrets.SLACK_USER_TOKEN}}" }
bodyanyBody template; none by default.body: { conversation: "{{session.id}}" }
intervalDurationTime between polls; default 500ms.interval: 1s
timeoutDurationHow long to wait for a reply before the turn fails; default 60s.timeout: 30s
idleDurationKeep polling after the first reply until nothing new arrives for this long (an agent that answers in several messages); unset completes the turn on the first reply.idle: 3s
cursorstringJSONPath into each poll reply whose value renders as {{cursor}} in the next poll (a watermark). With it the polls accumulate; without it each poll replaces the last (the endpoint returns the whole thread).cursor: $.watermark

RedTeam

KeyTypeRequiredDescriptionExample
targetstringyesThe target the attacks talk to (a key of targets).target: api
agentstringyesWhat the agent is and what it must never do; the attacks pursue goals written against it.agent: "Shop support for orders and returns; never more than 30-day returns, never other customers' data"
playbookslist of stringThe built-in playbooks to run (tamesu playbooks lists them); every playbook when empty.playbooks: [prompt_injection, pii_extraction, policy_drift]
max_turnsintegerTurns each attack gets (default 8).max_turns: 8
repeatintegerTimes each attack runs.repeat: 3
languagestringThe attacker’s language (BCP 47).language: es
tagslist of stringExtra tags on every generated test (red-team and playbook: and owasp: are always set).tags: [nightly]
varsmap of string to stringValues for {{vars.}} in the generated tests.vars: { shop: Mochi Shoes }
assertlist of AssertionAssertions added to every generated test (a pack or a not_contains with the forbidden outcome).assert: [ { not_contains: "90 days" } ]

Example:

red_team:
  target: api
  agent: "Mochi Shoes support: never more than 30-day returns, no other customers' data, no refunds without verification"
  playbooks: [prompt_injection, pii_extraction, policy_drift]
  max_turns: 8
  assert:
    - not_contains: "90 days"

RemoteAgent

KeyTypeRequiredDescriptionExample
addrstringAddress to listen on such as 127.0.0.1:8095 when the agent under test is configured by hand; default a free port that {{remote..url}} renders.addr: 127.0.0.1:8095
namestringThe card’s name; default the key.name: Billing
descriptionstringThe card’s description.description: Refunds and invoices
skillslist of RemoteSkillThe card’s skills: ids or objects with id / name / description / tags.skills: [refund, { id: invoice-lookup, name: Invoice lookup }]
replystringWhat the agent answers every message that no replies entry matches.reply: "I can help with refunds and invoices."
replieslist of RemoteReplyScripted answers: the first entry whose when matches the message wins.replies: [{ when: "(?i)refund", say: "Refund of 42 euros issued, reference RF-889" }]
forwardstringA real agent’s JSON-RPC URL: the remote agent becomes a recording proxy in front of it and every message and reply is recorded (templates allowed).forward: https://billing.example.com/a2a
headersmap of string to stringHeaders added to forwarded requests such as Authorization (templates allowed).headers: { Authorization: "Bearer {{secrets.BILLING_TOKEN}}" }
extendedRemoteExtendedWhat the authenticated extended card adds (agent/getAuthenticatedExtendedCard): more skills and a fuller description for callers that send credentials.extended: { skills: [invoice-export], description: "Refunds, invoices and exports for partners" }
tokenstringA bearer token every request must carry (templates allowed); the card declares the scheme and a request without it gets 401.token: "{{secrets.BILLING_AGENT_TOKEN}}"

RemoteExtended

KeyTypeRequiredDescriptionExample
descriptionstringThe description on the extended card; default the card’s.description: "Refunds, invoices and exports for partners"
skillslist of RemoteSkillSkills only the extended card lists.skills: [invoice-export, { id: bulk-refund, name: Bulk refund }]

RemoteReply

KeyTypeRequiredDescriptionExample
whenstringA regular expression on the message; omitted matches every message.when: "(?i)cancel"
saystringThe agent’s words.say: "Which order should I cancel?"
dataobject (any keys)A data part sent with the words.data: { reference: RF-889 }
stateone of completed, input-required, failedThe task’s state after the reply; default completed.state: input-required

RemoteSkill

Either a string (The skill id), or an object with:

KeyTypeRequiredDescriptionExample
idstringyesThe skill id.id: refund
namestringThe skill’s name; default the id.name: Refunds
descriptionstringWhat the skill does.description: Issues refunds for delivered orders
tagslist of stringTags on the skill.tags: [billing]

ScriptFile

Either a string (A path relative to the suite, or a URI), or an object with:

KeyTypeRequiredDescriptionExample
pathstringA file relative to the suite; its bytes are sent.path: fixtures/invoice.pdf
uristringA URI the agent fetches itself; sent instead of bytes.uri: https://cdn.example.com/photo.jpg
namestringThe file name the agent sees; default the path’s base name.name: invoice.pdf
mimestringThe media type; default from the extension.mime: application/pdf

Similar

Either a string (The text some agent turn should mean; the configured embedding model and a 0.8 minimum are the defaults), or an object with:

KeyTypeRequiredDescriptionExample
textstringyesWhat an agent turn should mean.text: "Returning after 40 days is not possible"
minnumberMinimum cosine similarity between the embeddings (0 to 1); default 0.8.min: 0.7
modelstringEmbedding model (a Genkit reference); defaults to models.embedder.model: googleai/text-embedding-004

Example:

- similar_to: "The return window is 30 days from delivery"
- similar_to: { text: "Returning after 40 days is not possible", min: 0.7, model: googleai/text-embedding-004 }

StateCommand

KeyTypeRequiredDescriptionExample
runstringyesThe command (sh -c); templates render with the run’s vars and captured values and secrets.run: "psql -tAc \"select status from orders where id = '{{captured.order_id}}'\""
exitintegerThe expected exit code; 0 when omitted.exit: 0
containsstringText the output must contain.contains: refunded
regexstringA regular expression the output must match.regex: "refund(ed|ing)"
timeoutDurationHow long the command may take; default 30s.timeout: 20s

Example:

command: { run: "psql -tAc \"select status from orders where id = '{{captured.order_id}}'\"", contains: refunded }

StateHTTP

KeyTypeRequiredDescriptionExample
urlstringyesThe request URL; templates render with the run’s vars and captured values and secrets.url: "{{vars.admin_url}}/orders/{{captured.order_id}}"
methodstringGET when empty.method: GET
headersmap of string to stringRequest headers (templates allowed)headers: { Authorization: "Bearer {{secrets.ADMIN_TOKEN}}" }
bodyanyA string is sent as is; a mapping as JSON.body: { reason: "test {{env.TAMESU_TEST}}" }
statusintegerThe expected status code; any code under 400 when omitted.status: 200
json_pathJSONPathAssertionA path that must exist in the JSON body and with equals have that value.json_path: { path: $.status, equals: refunded }
containsstringText the body must contain.contains: refunded
regexstringA regular expression the body must match.regex: '"type":"refund_issued"'
timeoutDurationHow long the request may take; default 30s.timeout: 10s

Example:

http: { url: "{{vars.admin_url}}/orders/{{captured.order_id}}", headers: { Authorization: "Bearer {{secrets.ADMIN_TOKEN}}" }, json_path: { path: "$.status", equals: refunded } }

VoiceAudio

KeyTypeRequiredDescriptionExample
encodingone of pcm16, mulaw, alawThe wire encoding; default pcm16.encoding: mulaw
sample_rateintegerThe sample rate in Hz; default 16000 for pcm16 and 8000 for mulaw / alaw.sample_rate: 8000
framesone of binary, jsonbinary (default): one chunk per binary websocket frame; json: the chunk travels base64-encoded inside a JSON message (send and receive say where).frames: json
sendanyframes json: the message template per outgoing chunk; {{audio}} is the base64 chunk.send: { user_audio_chunk: "{{audio}}" }
receivestringframes json: JSONPath of the base64 audio in an incoming message; evaluated against the message as a one-element list such as $[*].audio_event.audio_base_64.receive: "$[*].audio_event.audio_base_64"

VoiceMessages

KeyTypeRequiredDescriptionExample
openanyA message template sent once after connecting.open: { type: start, call_id: "{{session.call_id}}" }
transcriptstringJSONPath of the agent’s words in a message (its final transcripts); evaluated against the message as a one-element list such as $[?(@.type==‘transcript’ && @.role==‘assistant’)].transcript. Without it the audio is transcribed.transcript: "$[?(@.type=='transcript' && @.role=='assistant' && @.transcriptType=='final')].transcript"
donestringJSONPath that is truthy on the message that ends the call.done: "$[?(@.type=='status-update' && @.status=='ended')].status"
interruptedstringJSONPath that is truthy on the message that says the agent stopped for the caller.interrupted: "$[?(@.type=='user-interrupted')].type"
tool_callsstringJSONPath of the tool calls in a message: names or objects with name / args (or function.name / function.arguments).tool_calls: "$[?(@.type=='tool-calls')].toolCallList[*]"
dtmfanyA message template per keypad digit; {{digit}} renders. Without it a dtmf step is recorded as unsupported.dtmf: { type: dtmf, digit: "{{digit}}" }
closeanyA message template sent before the connection is closed (a hangup).close: { type: hangup }