Skip to main content
connic
Build

Runtime Controls

Execution limits, coordinated concurrent runs, persistent sessions, and shared configuration across agent families.

Last updated

Execution limits and retries

agents/agent.yaml
max_concurrent_runs: 10
max_iterations: 50
timeout: 180

retry_options:
  attempts: 3
  attempt_timeout: 120
  initial_delay: 10
  max_delay: 30
  # Tool and sequential agents only:
  rerun_middleware: true
  • max_concurrent_runs caps simultaneous runs, subject to the subscription limit.
  • max_iterations limits the LLM loop so repeated tool calls cannot run indefinitely.
  • timeout sets the maximum execution time in seconds, with a minimum of five seconds. It is capped by your plan’s run timeout, listed on the pricing page.
  • retry_options applies to individual model requests in an LLM run. All failures of those requests use the configured retry or fallback policy, including request timeouts and provider rejections. A file type the model does not accept (unsupported_file_type) is not retried on the same model but can still switch to the fallback. Completed tools and middleware keep their results. Tool and sequential agents retain operation-level retries.
  • attempts defaults to 3 and allows 1–10 attempts, including the first. Without a fallback, this is the primary model’s budget. With a fallback, the primary gets one attempt and the fallback gets this budget. Configured context compression can add one repair request.
  • attempt_timeout defaults to 120 seconds and must be a positive, finite number. Each completion or stream can use at most this time or the run’s remaining time, whichever is shorter. The run’s deadline is shared by all attempts and backoff waits; it is never divided by the attempt count or reset by a retry.
  • Same-model retries use exponential backoff with equal jitter: a random delay between half and all of the capped backoff. initial_delay defaults to 10 seconds and max_delay to 30. A valid provider Retry-After replaces this generated delay. A fallback switch is immediate. If the required wait cannot fit before the run’s deadline, no further request starts.
  • rerun_middleware only affects operation-level retries on tool and sequential agents. Model-request retries keep the current LLM run and middleware state. Provider SDK retries are disabled so they do not multiply these attempts.

With the example above, a response taking 90 seconds succeeds on the first attempt. If an attempt reaches 120 seconds, subsequent attempts share the time left in the 180-second run.

The stream timeout covers the entire response; incoming chunks do not reset it. Once a request has delivered output, an interruption is reported as an incomplete response and the request is not restarted. Cancellation and the run’s deadline also stop further attempts. These rules apply to ordinary LLM streaming; native voice sessions have a separate lifecycle.

Concurrency rules

Concurrency rules ensure that only one run per unique key is active at a time. Use them when an agent processes events for a specific support process, customer, or order and parallel processing would conflict.

Queue mode (default)

When another event arrives for an active key, it waits until the first run finishes and then starts automatically.

agents/support-processor.yaml
version: "1.0"

name: support-processor
type: llm
model: connic/gpt-5.6-terra
description: "Processes support tickets one at a time per process"
system_prompt: "You are a support agent. Process the incoming ticket."

# Only one run per process_id at a time. Additional runs wait in queue.
concurrency:
  key: "process_id"
  on_conflict: queue

Drop mode

When another event arrives for an active key, the new run is cancelled immediately. Use this when duplicate work would be wasteful.

agents/notification-handler.yaml
version: "1.0"

name: notification-handler
type: tool
description: "Handles notifications, skipping duplicates"
tool_name: notifications.handle

# Drop duplicate runs for the same customer while one is active.
concurrency:
  key: "data.customer_id"
  on_conflict: drop

Coalesce mode

When another event arrives for an active key, its run waits as in queue mode, but only one run per key can wait. A newer event replaces the waiting run, so a burst of events during a long run leads to a single follow-up run with the latest payload. Use this when every event asks the agent to re-check the same state.

The replaced run is cancelled. Its context contains superseded_by_concurrency_rule and superseded_by, the ID of the run that replaced it.

agents/process-agent.yaml
version: "1.0"

name: process-agent
type: llm
model: connic/gpt-5.6-terra
description: "Re-evaluates the current process step on every status event"
system_prompt: "Check the current step of the process and act on it."

# Events that arrive during a run collapse into one follow-up run.
concurrency:
  key: "process_id"
  on_conflict: coalesce

Key extraction

The key uses dot notation to read the trigger payload. If a Kafka payload is {"data": {"customer_id": "abc"}}, use data.customer_id. If the path does not exist, concurrency enforcement is skipped for that run.

Interaction with max_concurrent_runs

Both constraints apply. With max_concurrent_runs: 5 and concurrency.key: "process_id", five process IDs can run in parallel, but only one run per process ID can be active. Runs waiting for their key do not take a slot.

Waiting runs

  • Waiting runs are persisted. They survive restarts and deployments and start on the active deployment.
  • A key stays held during a deployment. A run still finishing on the previous deployment keeps its key until it ends.
  • A run paused for an approval keeps its key. After the decision, it resumes before any waiting run.
  • In queue mode, waiting runs start in the order their events arrived.
  • There is no limit on how many runs can wait and no wait timeout. Waiting time does not count toward timeout, and a run uses no Project credit before it starts. Runs cancelled by drop or coalesce never start.
Limitations

Concurrency rules are not supported on sequential agents. The key comes from the raw trigger payload, so it applies uniformly to all trigger types, including Kafka, HTTP, Cron, API, and trigger_agent.

Persistent sessions

Set session: true for one shared session across the agent’s runs. An object without key or with key: null uses the same shared session. false, null, or omitting session disables persistence.

agents/support-bot.yaml
version: "1.0"

name: support-bot
type: llm
model: connic/gpt-5.6-terra
description: "A support chatbot that remembers conversation history"
system_prompt: |
  You are a helpful support agent. Use the conversation history
  to provide contextual responses.

# Maintain persistent sessions keyed by chat ID from middleware
session:
  key: context.chat_id
  ttl: 86400  # Sessions expire after 24 hours of inactivity

# Keep long conversations within the model context window
context_compression:
  enabled: true
  # Optional model for compression summaries; defaults to the agent model
  model: connic/gpt-5.6-luna
  keep_recent_messages: 12
  # Optional early compression based on model-reported prompt usage
  max_prompt_tokens: 100000

History and browser state

history and browser both default to true. This example retains cookies and local storage for each chat while starting every run with fresh conversation history. With browser: false, browser tools still work but start with fresh browser state.

agents/agent.yaml
session:
  key: context.chat_id
  ttl: 86400
  history: false
  browser: true

Session key

The optional key separates sessions by a resolved value. When provided, it must use one of two prefixes:

  • context.<path> resolves from middleware context, including keys derived from request data.
  • input.<path> resolves from the raw connector payload before middleware. It requires valid JSON and supports nested paths.

Set the key in middleware

middleware/support-bot.py
async def before(content: dict, context: dict) -> dict:
    # Extract a stable identifier for this conversation from the connector payload.
    payload = context.get("payload", {})
    context["chat_id"] = payload.get("chat_id")
    return content

Use an input key

agents/agent.yaml
# When the connector payload is JSON like {"user_id": "u123", "message": "..."}
session:
  key: input.user_id

TTL and session management

The optional ttl expires sessions after that many inactive seconds. The minimum is 60 seconds; without a TTL, sessions never expire. View and delete active sessions in the dashboard under Storage > Sessions. Sessions are scoped per environment.

Runs that share a session are not serialized. If two runs for the same session overlap, the run that writes second fails. When that can happen, add a concurrency rule on the field that identifies the session, for example concurrency.key: conversation_id with session.key: input.conversation_id.

Context compression

Enable context_compression to shorten conversation history when a provider reports that the context window is full. connic compresses before switching to a fallback, then makes one additional request to the same model with the shortened history. Each model call in the agent loop allows at most one such repair request, outside the normal attempt count. It keeps the original run deadline and the attempts already used. If repair fails, the normal retry or fallback policy continues. Set context_compression.model to use a separate summary model; the repair request uses the model that rejected the context. Set max_prompt_tokens to compress earlier using reported prompt usage. Use session_history.interval when stored history should also be compacted between runs.

Stored history has no limit on total size or number of runs; each stored message can be up to 5 MB. With session_history.interval: 4, every fourth run ends by summarizing older history and keeps the last keep_recent_runs runs (default 1) verbatim. Without it, every run loads the full stored history, and compression only shortens the request, not the stored session.

Configured session keys must resolve

If a configured key path resolves to an empty or missing value, the run fails. Omitting the key uses the agent’s shared session.

Cascading defaults with _defaults.yaml

Add _defaults.yaml to any directory under agents/ to share configuration with agents at that level and below. Use it for a common model, guardrails, tools, or database.collections.

agents/ layout
agents/
_defaults.yamlapplies to every agent in the project
process/
_defaults.yamladds/overrides for everything under process/
ingest/
foo.yamlinherits root + process defaults
enrich/
_defaults.yamladds/overrides for process/enrich/*
bar.yamlinherits root + process + enrich defaults
support-assistant.yamlinherits only root defaults

connic combines defaults shallowest-first, then applies the agent file so its values win on conflict.

What can live in a defaults file

Defaults accept the same fields as agent YAML and may be partial, with two exceptions:

  • name and description are forbidden because they identify a specific agent.
  • version is allowed to pin the schema version project-wide. The agent file must still set it together with name and description.

Merge rules

  • Scalars such as model, temperature, system_prompt, and timeout are replaced by the deeper layer.
  • Objects such as database, retrieval, retry_options, approval, session, and guardrails merge recursively by key.
  • Lists concatenate with deduplication:
    • tools, discoverable_tools, and approval.tools deduplicate by tool reference; a deeper declaration overrides the inherited entry.
    • approval.inputs deduplicates by input-tool name; a deeper declaration replaces the inherited configuration for that name.
    • mcp_servers deduplicates by server name; a deeper full server configuration replaces the inherited one.
    • guardrails.input and guardrails.output deduplicate by rule name when present and otherwise append.
    • Sequential agents deduplicate by string.

Example

agents/_defaults.yaml
version: "1.0"
type: llm
model: connic/gpt-5.6-terra
temperature: 0
guardrails:
  input:
    - type: prompt_injection
      mode: block
  output:
    - type: system_prompt_leakage
      mode: block
tools:
  - audit.log_event
agents/billing/refund-agent.yaml
version: "1.0"
name: refund-agent
description: "Issues refunds against the billing system."
system_prompt: "Refund only valid charges. Use the tools."
tools:
  - billing.lookup_charge
  - billing.issue_refund
approval:
  tools:
    - billing.issue_refund: param.amount > 50
  timeout: 3600

The effective refund-agent inherits the model, temperature, and both guardrails. Its tools become [audit.log_event, billing.lookup_charge, billing.issue_refund].

Run connic lint to validate the merged configuration. A/B test variants named <base>-test-<name>.yaml inherit the same defaults as their base.

Cloud development environment