Commerce agent architecture
Paths are in the reference repo: commerce_common/ is commerce-common/commerce_common/, shopping_agent/ is
shopping-agent/core/shopping_agent/, merchant_agent/ is merchant-agent/core/merchant_agent/, and the runtimes are
shopping-agent/runtime-messages-api/shopping_agent_runtime/ and merchant-agent/runtime-messages-api/merchant_agent_runtime/.
One loop
- One model owns the conversation. A turn is
ShoppingAgent.stream_turnorMerchantAgent.stream_turn(each runtime'sorchestrator.py): the model reads the cached prompt, calls tools (one round's calls run concurrently), and ends with text plus presentation calls. There is no router, classifier, or hand-off. - Every call on every path runs through the role executor (
ShoppingToolExecutorinshopping_agent/executor.py,MerchantToolExecutorinmerchant_agent/executor.py) overBaseToolExecutorincommerce_common/execution.py. The Messages API runtime, the Agent SDK toolset, and the MCP server call the sameexecute, which never raises; aftermax_tool_iterationsrounds (commerce_common/config.py) the runtime forces a round without tools.
Where a rule lives
| A rule that applies | Lives in | Reference |
|---|---|---|
| While one call's arguments are being filled in | That tool's description | build_tools in each role's tools/registry.py |
| On most turns: cart and checkout, the staged-write contract, presentation grammar, tool order, trust rules | The static prompt | build_static_system in each role's prompt.py |
| On the minority of requests that need a multi-step procedure | A skill, loaded on demand | shopping-agent/skills/, merchant-agent/skills/ |
A rule the core journey needs on most conversations moves a layer down. This table is the one statement of the layering.
Skills
- A skill is a directory holding
SKILL.md: frontmatternameanddescription, then the body (parse_skill_mdincommerce_common/skills.py). The description names the request class; it carries no sample utterances. - The static prompt carries the index alone (
SkillRegistry.index_block, sorted by name) andload_skillreturns one body (_load_skillincommerce_common/execution.py). On the Agent SDK,ensure_project_skillslinks the same directories into.claude/skills/andSKILL_TOOL_ADAPTERpoints the model at the SDK'sSkilltool (commerce_common/agent_sdk.py); the hosted manifests list the directories underskills[]inagent.yaml.
The backend interface
A deployment implements one abstract class per role; nothing else reaches its systems.
StorefrontBackend(shopping_agent/backend.py): 11 abstract methods, catalog (2), cart (4), preferences (1), orders (2), policies (1), fulfillment (1);get_account_context,get_disclosure, andcheckout_handoffhave defaults. No method places an order or moves money;checkout_handoffnames where payment happens (a hosted checkout URL, or one per seller) and the host renders it.MerchantBackend(merchant_agent/backend.py): 16 abstract methods, reads (8: performance 3, catalog 2, inventory and order health 2, pricing 1) and the change lifecycle (8: fivestage_*,get_pending_changes,apply_change,discard_change);execute_analysis_query,get_analysis_schema,get_merchant_contexthave defaults.- A product or listing sold by size, color, or tier is a family record carrying
options, its variants each a record withoption_valuesandvariant_of; the cart and price or restock writes take a variant's id and the gates hold a family's.docs/backends.mdhas the mapping from common catalog models and what to return for a figure the platform cannot supply. - Eligibility, pricing, inventory, and quantity rules are enforced in the backend; the gates check provenance and caps (commerce-trust-safety). Everything a backend returns is fenced before the model reads it.
- A system that is not wired yet is a method that fails (merchant methods raise
ChangeNotApplicablefrommerchant_agent/changes.py); the tool stays registered, so prompt bytes do not change. A system the business does not have at all is absent: anenable_*switch on the role's config turned off, which removes its tools (absent_tools()), prompt lines, and grounding rule on all three paths, and the executor refuses those names; the skills that need it are parked underskills/_staged/, and the bytes are then fixed for that deployment.NotOffered(shopping_agent/backend.py) is the narrower signal: one item or seller the store does not serve while the system itself is on. executor_class=onShoppingAgent/MerchantAgent, the SDK toolsets, and the MCP servers'build_servertakes a subclass of the role's executor for a deployment's owndomain_errormapping or result wording.
Delegates
A second model call takes one shape: a DelegateExtension (commerce_common/delegation.py). It receives a brief
and the handles in DelegationContext, never the conversation or the executor; it returns one result validated
against result_model; it cannot write, present, or call a delegate; _run_delegate in commerce_common/execution.py
caps its calls per turn at max_delegate_calls_per_turn. MerchantAgent takes extra_delegates, and its
built-in instance is run_analysis (commerce-merchant-operations). ShoppingAgent registers none.
Model fields
model runs the turn loop and memory_model the post-turn extraction (BaseAgentConfig in commerce_common/config.py);
analysis_model runs the delegate, and None means model (MerchantAgentConfig in merchant_agent/config.py). The
SDK runtimes copy config.model into the options; the manifests set model:. Choose the values with your own evals.
Do not
- Vary
tools[]or the static prompt by request (commerce-prompt-caching). - Put a most-turns rule in a skill, or a one-tool rule in the prompt.
- Enforce a cap or a permission in prompt text alone; it belongs in the executor or the backend.
- Let the model author a price, a figure, or a term; components are joined from server records (commerce-ui-tools).
- Add a domain tool to a core package; a vertical adds UI through
PresentationExtensionand keeps the rest in its own code.

