Cloud|

Application AI tasks

Upgrade and operate shared durable inference for application-owned background jobs.

4 min read Updated 2026-10-27 #ai#jobs#migrations

Application jobs can use the same durable inference worker as workflow AI, without creating a workflow run. See Structured and background AI for the application API.

Run the existing migrateWorkflowAi() migration before starting workers from the new image. It makes ai.workflow_task.run_id nullable and adds usage_subject for authenticated personal calculations and released_at for application result retention. Existing workflow rows and their cache identities remain unchanged. No new service or environment variable is required.

Application-owned tasks share ai.workflow_budget, ai.workflow_attempt_timeout_ms and ai.workflow_queue_timeout_ms with workflow inference. Capacity and rate admission remain atomic across replicas; cached successes do not consume another admission. The shared worker recovers queued jobs and marks expired deliveries visibly failed. Durable task requests contain source data and results; handle them with the application's data access policy.

A personal usageSubject uses Assistant model grants and allowances. Tasks without one use the central background budget and emergency stop. Both record provider usage through Cloud's existing accounting; no second charge is added for storing a task result.

Drain the application's own jobs before stopping the shared AI runtime. Aborting an observer stops its wait; the shared worker retains ownership of the inference so another observer can recover it. To recover a terminal failed request, inspect the failure and start a new intentional request. A completed request can be replayed with its original effect key without another charge.

Release results after domain settlement

The application must call releaseApplicationAiTask() from @k2b/cloud/ai when it durably settles or discards its request, including failed requests. Pass the same application, scope, raw effect key and optional usage subject used for runApplicationAiTask(). Pass the application's Postgres transaction as the second argument so the release commits together with the domain result:

ts
import { sql } from "@k2b/cloud";
import { releaseApplicationAiTask } from "@k2b/cloud/ai";

await sql.begin(async (tx) => {
  await persistRequestResult(tx, request.id, result);
  await releaseApplicationAiTask({
    appId: "inventory",
    scopeId: warehouse.id,
    effectKey: request.id,
    usageSubject: request.usageSubject,
  }, tx);
});

Release returns true when the application task exists in any status and false only when it is absent. Repeated release preserves the first release time. An abandoned queued or running task can be released without canceling its inference: the normal bounded runtime still settles it and records usage. Release does not reset admission accounting. Keep a request unreleased while another observer still needs its recovery result. If release finds no task, an obsolete worker that creates one later must release it when it notices that its request is no longer current.

The existing AI recovery sweep deletes at most 100 terminal application tasks per pass once both release and completion are more than 24 hours old. This fixed window uses the existing 24-hour admission-history horizon. It is a replay grace period after settlement, not a deadline for pending requests. Requests, outputs, errors and usage stored in those task rows are deleted; central usage accounting remains intact. Workflow-owned tasks keep their existing run deletion lifecycle.

Unreleased results remain available regardless of age, so a dormant application request can recover without another inference. Applications must reconcile releases for already settled requests when adopting this lifecycle. After pruning, an old effect key no longer prevents another inference: never resubmit a settled request. Keep the application's permanent request state authoritative.

Delete owner data

Call deleteApplicationAiTasks({ appId, scopeId }, tx) from @k2b/cloud/ai inside the transaction deleting an owning scope. Add sourceId to restrict deletion to one exact source identity. Composite snapshot identities are distinct sources: itemId + ":42" does not match itemId + ":43". There is no prefix selector; % and _ are literal characters. Omitting sourceId deletes the entire owning scope. Application and scope filters always apply. The function returns the deleted count and removes application tasks in every status immediately. Workflow-owned tasks and other scopes remain untouched. Existing rate and daily admission history retains its normal 24-hour lifetime; deleting data does not replenish budgets.

Owner cleanup serializes with AI task admission. Running workers observe a removed task as canceled, and late completion cannot recreate its row. The application must fence future task creation and recovery for deleted owners. Call cleanup last in a short deletion transaction: its global admission lock is held until that transaction commits, including when passed an existing transaction. Likewise, a worker that creates a task after an earlier release found no task must release it when it discovers that its domain request was discarded.

Applications periodically page listUnreleasedApplicationAiTaskSources({ appId, after? }) (up to 100 distinct scope/source identities). Under the domain owner lock, delete sources whose owner no longer exists. This reconciles a worker crash between late task creation and noticing owner deletion, while retaining unreleased results for live dormant requests. Mail performs this at startup and in its minute recovery sweep.

Mail removes a mailbox's full scope in its deletion transaction. Conversation sources include snapshot identities, so deleting one conversation releases its request. Known active requests are released without cancellation. A late worker deletes its exact captured source if the owner disappeared; other orphan cleanup uses the bounded exact-source reconciliation. Released results retain the normal 24-hour grace period; unreleased orphaned sources are removed on the next sweep that reaches them.

Mail retains an interrupted summary observer's request key, frozen source and daily reservation. Heartbeat failures retry after 60 seconds; shutdown recovery can resume immediately. These lease retries have no attempt cap while the request remains recoverable. They rejoin the existing platform task rather than starting another inference. Check job delivery and database health if a summary remains writing.

Connect a coding agent

The Cloud skill provides compact working instructions. MCP supplies exact current documentation when details matter.

CLI command
bunx skills add https://docs.example.com

This command installs the working instructions published by this website in the selected coding agent.

For exact, current details, also connect the MCP server through one of the agent tabs.

Installation uses the open-source Vercel Skills CLI.

The skill and MCP complement each other: the skill describes workflows, while MCP supplies current documentation.