Query
A Query is a request to run work on an Ark resource. The CRD ships an input plus exactly one of target (a single agent / team / model / tool) or selector (picks a single target by label — first match wins, not a fan-out). For a task-oriented walkthrough see User Guide → Run queries / chat with agents and teams; this page is the field-by-field reference.
Spec
apiVersion: ark.mckinsey.com/v1alpha1
kind: Query
metadata:
name: example-query
spec:
# --- Required ----------------------------------------------------------
input: "What's the weather in {{.city}}?" # user message (string)
# Exactly ONE of target or selector is required.
target:
type: agent # agent | team | model | tool
name: weather-agent
# OR pick a single target by label (first match wins; not a fan-out).
# selector:
# matchLabels:
# role: reviewer
# --- Common optional fields --------------------------------------------
type: user # only 'user' is accepted (default)
parameters: # template values for {{.name}} in input
- name: city
value: Boston
conversationId: chat-alice-001 # thread queries into one conversation
memory:
name: broker # Memory resource that stores history
sessionId: user-session-123 # telemetry / tracking key (separate from conversationId)
serviceAccount: my-sa # RBAC for execution
timeout: 5m # default '5m'
ttl: 720h # default from ArkConfig/default (720h fallback)
cancel: false # set to true to cancel mid-flight
overrides: # HTTP header overrides for downstream model/MCP calls
- resourceType: model
headers:
- name: X-Trace-ID
value:
value: trace-xyz789Fields
| Field | Type | Required | Description |
|---|---|---|---|
spec.input | string | yes | The user message. Supports Go template substitution via {{.name}} against spec.parameters. |
spec.target | object | conditional | Single execution target. Either target or selector must be set — the validation webhook rejects queries with neither. |
spec.target.type | enum (agent, team, model, tool) | yes (when target set) | Resource kind to dispatch to. |
spec.target.name | string | yes (when target set) | Name of the target resource in the same namespace. |
spec.selector | LabelSelector | conditional | Pick a single target by label (alternative to target). Resolves to the first matching resource, checked in order Agent → Team → Model → Tool. Not a fan-out — it does not run against multiple matches. Empty match set fails with no matching resources found for selector. |
spec.type | enum (user) | no | Only user is accepted (default). The previous messages type is deprecated; the mutating webhook migrates it to user and stamps a migration-warning-input-type annotation. |
spec.parameters[] | list | no | Template values substituted into input (and into any agent prompt that references queryParameterRef). Each entry has a name and either a value or valueFrom (configMapKeyRef, secretKeyRef). |
spec.memory.name | string | no | Name of a Memory resource that stores conversation history keyed by conversationId. If unset, the completions executor falls back to a Memory named default in the same namespace; if that doesn’t exist either, it uses a no-op (in-process) store. |
spec.sessionId | string | no | Identifier for grouping queries in tracking / telemetry. Auto-derived from the Query UID if unset. Not the same as conversationId — sessionId is observability, conversationId is memory continuity. |
spec.conversationId | string | no | Conversation threading identifier. Sent to execution engines as the A2A contextId. With memory active, ties subsequent queries to the same history. Auto-generated by the memory backend if unset (a new conversation is created and the ID written back to status.conversationId). |
spec.serviceAccount | string | no | Kubernetes ServiceAccount used for RBAC during execution. |
spec.timeout | duration | no | Execution timeout (30s, 5m, 1h). Defaults to 5m. Applies to all targets in the query. |
spec.ttl | duration | no | How long the Query resource lingers after completion. Mutating webhook resolves the default from ArkConfig/default.spec.queryTTL (falls back to 720h). |
spec.cancel | bool | no | When true, indicates intent to cancel a running query. The controller transitions the query to phase: canceled. |
spec.overrides[] | list | no | HTTP header overrides applied when the query’s target calls its model or any MCP server. See Overrides. |
Targets
A query dispatches to exactly one target — named directly via spec.target, or picked by label via spec.selector. Supported type values: agent, team, model, tool.
spec:
input: "Summarise the news."
target:
type: team
name: news-teamPick by label (resolves to a single target — first match across Agent → Team → Model → Tool):
spec:
input: "Should we ship this feature?"
selector:
matchLabels:
role: reviewerThe webhook enforces that one of target / selector is set, and that the named resource exists when target is used.
Parameters
Parameters are substituted into spec.input (and into agent prompts that declare queryParameterRef) before dispatch. Sources:
spec:
input: "Hit {{.endpoint}} with token {{.key}}, mode {{.mode}}."
parameters:
- name: mode
value: "production" # direct value
- name: endpoint
valueFrom:
configMapKeyRef: # from ConfigMap
name: api-config
key: url
- name: key
valueFrom:
secretKeyRef: # from Secret
name: api-credentials
key: api-keyTemplate syntax is Go templates: {{.parameter_name}}. Names must match the parameters[].name field.
Wiring parameters into the agent
Agents can pull the same query parameters into their own prompt or system message using queryParameterRef:
apiVersion: ark.mckinsey.com/v1alpha1
kind: Agent
metadata:
name: dynamic-agent
spec:
prompt: |
You are running in {{.mode}} mode against {{.environment}}.
parameters:
- name: mode
valueFrom:
queryParameterRef:
name: operation_mode
- name: environment
valueFrom:
queryParameterRef:
name: target_envThe matching query supplies them:
apiVersion: ark.mckinsey.com/v1alpha1
kind: Query
metadata:
name: parameterised-q1
spec:
input: "Run a health check."
parameters:
- name: operation_mode
value: "production"
- name: target_env
value: "us-west-2"
target:
type: agent
name: dynamic-agentSee Agents → Pass parameter values at query time for the dashboard walkthrough.
Conversations + memory
conversationId threads queries together; Memory stores the history. By default the completions executor uses the Memory named default in the query’s namespace when spec.memory is unset — most Ark installs ship one backed by the in-cluster broker. Two queries with the same conversationId share context as long as they hit the same memory store:
apiVersion: ark.mckinsey.com/v1alpha1
kind: Query
metadata:
name: chat-q1
spec:
input: "Hi, my name is Alice."
conversationId: chat-alice-001
memory:
name: broker
target:
type: agent
name: assistant
---
apiVersion: ark.mckinsey.com/v1alpha1
kind: Query
metadata:
name: chat-q2
spec:
input: "What's my name?"
conversationId: chat-alice-001 # same conversation
memory:
name: broker
target:
type: agent
name: assistantIf you set conversationId without spec.memory, the executor still resolves the default Memory (so continuity works out of the box on a standard install). The conversationId also flows through to execution engines as the A2A contextId — named engines (Claude Agent SDK, LangChain, …) can use it for their own session model.
If you set memory without conversationId, the controller auto-generates a fresh conversationId on each query.
Conversation ID vs session ID
conversationId and sessionId look similar but serve different purposes, and setting one has no effect on the other.
conversationId | sessionId | |
|---|---|---|
| Purpose | Conversation threading and memory continuity | Observability grouping key for telemetry, traces, events, and the dashboard |
| Affects the model? | Yes — determines which history is loaded and persisted | No — never influences execution or history |
| Where it flows | Sent to execution engines as the A2A contextId; used as the Memory store key | Emitted as OpenTelemetry baggage (ark.session.id) and as the broker event-stream key; groups conversations in the dashboard UI |
| Written to status? | Yes — echoed to status.conversationId | No |
| Default when unset | Auto-generated by the memory backend, then written back to status.conversationId | Derived from the Query UID |
Set conversationId when you want several queries to share history — a multi-turn conversation where a later query should “remember” earlier turns. Queries with the same conversationId that hit the same Memory store share context.
Set sessionId when you want to group related queries under one tracking session — in traces and events, and where the dashboard groups conversations by session. For example, all queries a single user issued in one sitting, or every step of a workflow run. A single sessionId can span many distinct conversationIds, and grouping queries this way does not make them share history.
Timeout
spec.timeout caps execution. Default is 5m. Format follows Go’s time.ParseDuration (30s, 5m, 1h).
- Completes within timeout →
status.phase: done. - Exceeds timeout →
status.phase: errorwith a timeout error message instatus.response.content.
For A2A agents the timeout flows through as a Go context deadline. A2A servers can also enforce their own per-server timeouts via A2AServer.spec.timeout — see Building A2A Servers → Timeout configuration.
Cancel
To cancel a running query, patch spec.cancel: true:
kubectl patch query slow-q1 --type='merge' -p='{"spec":{"cancel": true}}'The controller transitions the query to phase: canceled once the in-flight dispatch acknowledges. The dashboard chat panel does the same when you click Stop on an embedded chat.
Deletion and cleanup
When a Query is deleted, a controller finalizer removes the messages and operation events that query wrote to the broker, so deleting a Query does not leave orphaned history behind.
Messages go through the Memory contract:
- Memory resolution mirrors execution: the controller uses
spec.memoryif set, otherwise aMemorynameddefaultin the query’s namespace. If neither exists, cleanup is skipped. - The controller resolves the backend address from the
Memoryresource (status.lastResolvedAddress, falling back tospec.address) and issuesDELETE /queries/{queryId}/messagesagainst it — see the Memory delete endpoint.
Operation events are not part of the Memory contract, so they use a separate path:
- The controller discovers the broker endpoint from the
ark-config-brokerConfigMap and issuesDELETE /events/{queryId}directly against it. If no broker is configured for the namespace, cleanup is skipped. - Events are keyed by the Query’s UID, not its name (unlike messages), so
{queryId}here is the UID.
Both cleanups run independently and either can fail without blocking the other. A backend that does not implement a delete endpoint returns 404/405, which the controller treats as “not supported” and skips. Scope: messages and events only — chunks, traces, and the broker’s sessions read model are not affected. Failure policy: if a backend is unreachable the finalizer retries every 15s; after a 5-minute grace period it gives up and removes itself, so a Query is never stuck in Deleting.
This is complementary to spec.ttl: TTL expires history on a timer, while deletion cleans it up immediately when the Query is removed.
Status
status:
phase: done
conditions:
- type: Completed
status: "True"
reason: QuerySucceeded
message: Query completed successfully
conversationId: ctx-86e022b0-1f4f-49f5-bc4b-e0d6437a789b
duration: 1.82s
response:
target:
type: agent
name: weather-agent
content: "It's 72°F and sunny in New York"
raw: '[{"content":"...","role":"assistant","name":"weather-agent"}]'
phase: done
a2a:
contextId: ctx-86e022b0-1f4f-49f5-bc4b-e0d6437a789b
taskId: task-xyz789
tokenUsage:
promptTokens: 97
completionTokens: 20
totalTokens: 117Status fields
| Field | Description |
|---|---|
status.phase | One of pending, provisioning, running, done, error, canceled. |
status.conditions[] | Standard Kubernetes conditions. The Completed condition flips to True on success or failure. |
status.response | Single response object (singular — there is no responses[] array). A selector resolves to one target, so there is always exactly one response. |
status.response.content | The final assistant message. |
status.response.raw | The full conversation as a JSON-encoded array of messages — what the dashboard’s Debug tab renders. |
status.response.phase | Per-response phase (mirrors top-level phase). |
status.response.target | The target the response is for. |
status.response.a2a.contextId | A2A context ID returned by the engine. When conversationId is set on the spec, the engine receives it as the contextId and echoes it back here. |
status.response.a2a.taskId | Set when the target is an A2A agent and a task was created. References an A2ATask resource — see A2ATask. |
status.conversationId | Effective conversation ID — either the one you supplied, or the auto-generated one when memory is set without a conversationId. |
status.duration | Total wall-clock execution time. |
status.tokenUsage | promptTokens, completionTokens, totalTokens from the underlying model call. |
Phases
| Phase | Meaning |
|---|---|
pending | Resource accepted by admission, controller hasn’t picked it up yet. |
provisioning | Controller is preparing the dispatch (parameter resolution, target lookup). |
running | Dispatched to the executor; waiting for the response. |
done | Execution finished successfully. |
error | Execution failed; details in status.response.content. |
canceled | spec.cancel: true was honoured. |
Print columns
kubectl get queries prints Type, Phase, Duration, Age. Pipe through -o jsonpath for the content/raw fields.
Webhook validation
Enforced by ark/internal/validation/query.go:
- Exactly one of
spec.targetandspec.selectoris required. - When
spec.targetis set, the named resource must exist in the namespace andtarget.typemust be one ofagent,team,model,tool. - Parameter resolution must succeed (referenced ConfigMaps and Secrets must exist).
spec.overrides[]must reference real resources for anyvalueFromsource.
Mutating webhook
Enforced by ark/internal/validation/defaults.go:
spec.type: messagesis deprecated. The mutating webhook extracts the last user-message text, setsspec.type: user, replacesspec.inputwith that text, and stamps anark.mckinsey.com/migration-warning-input-typeannotation. UseconversationIdfor multi-turn instead.spec.ttlis defaulted fromArkConfig/default.spec.queryTTL(or720hif absent).