0.17

Redis stores need a fresh key prefix, and agents are served with agent().

Redis: a fresh keyPrefix

server/store.ts
import { redis } from 'experimental-a2/store-redis'

export const store = redis({
  url: process.env.REDIS_URL,
  // a prefix no earlier release wrote under:
  keyPrefix: 'a2-017',
})

Both Redis stores change how they keep event ids and per-event metadata: one key per event id instead of one shared hash, and metadata in hashes of 1,000 events. 0.17 no longer reads the shared id hash, and earlier releases can't see what 0.17 writes, so on a shared prefix a retry on either side can append a duplicate event, re-run work, or stall a lane. Switch to a fresh keyPrefix when you upgrade, and don't roll back to an earlier release on a prefix 0.17 has written. Vercel Queues keeps running recovery and scheduled work on the deployment that published it for up to seven days, so earlier deployments keep touching their sessions after you upgrade.

Before you clear sessions

0.17 adds session.clear(). Upgrade every instance before you clear any session: an earlier release ignores the clear and can run cleared events in the new lifetime. Upgrade every scheduler route before you schedule clears, and don't roll back while clear tasks are pending.

Vercel Queues

vercelQueues({ scheduledAppends }) is replaced by deploymentId?: string | null: leave it out to deliver to the current deployment, pass a deployment ID to target one, or pass null for deploymentless delivery. It applies to every task, recovery included, so null makes recovery deploymentless too, unlike 'deploymentless' did. Changing it doesn't move tasks already queued. A custom QueueTransport.send no longer takes a fourth routing argument. See Deployments.

Agents

Before0.17
createAgentServer({ agent: definition, ... })agent({ name, ... }), or agent({ contract, ... }) with a shared contract
agent() from experimental-a2/aiagentContract()
createReducer(), reducerNameagentContract({ reducer })
createHandlers(), authorizeAgentIngressremoved: pass handlers and events to agent()
handlerContext(agent)handlerContext(), or handlerContext(contract)
request in generate and messageMetadataevent
requestIdevent.id
responseMessageIdevent.payload.responseMessageId
  • agent({ contract }) takes only an agentContract(). Any other contract throws. A fully custom contract is core A2, served by createServer().
  • Upgrade clients with the server. A client from before 0.17 ignores the new shape of streamed progress, so a response's output doesn't appear in it until it reloads.
  • Custom telemetry recorders must accept the new span names: core claim and commit spans, and a2.ai.*.

0.21 later renames handlers to consumers and handlerContext() to consumerContext(); see 0.21.