Skip to content

AutoOps Pre-Call Prefetch

Status: planned (2026-06-16). Plan: .indusk/planning/autoops-precall-prefetch/ (research → brief → test-plan → ADR all accepted; impl in progress). Blueprint-team dev Tiger Ma confirmed the pre-call approach. This page is authored before code (doc-before-build) and is refined as phases land.

Problem

The availability-first prompt wants the agent to open a call already knowing who is calling and to lead with a real appointment time. Today every call pays for that in-call: it round-trips autoOpsLookupCustomerContext to identify the caller, then autoOpsGetAvailability to find a slot, before it can confirm the vehicle or offer a time. That is latency and dead air at exactly the moment we want the agent to feel fast and businesslike. The pre-call pipeline already runs before the agent speaks and already holds the caller's phone number, so that work can move up front.

Current sequence

Two in-call round-trips happen while the caller waits.

Proposed sequence

The agent opens already holding the caller's context and the oil-change times. The in-call tools stay registered and are used as fallback (unmatched caller, stale data, any service other than oil change).

How it works

  • Gate. An AutoOps branch in getPrecallCRMInfo, entered when getAutoOpsConfig(teamId).enabled is true. Not typedConfig.crm — AutoOps has no crm-enum value.
  • Call pattern. Customer lookup and both oil-change availability calls (drop-off + waiter) run in one Promise.all, so total latency is roughly the slowest single call, inside the 3s PRE_CALL_CRM_TIMEOUT_MS budget.
  • Fail-open. The prefetch inherits the pipeline's timeout + error wrapper. A slow or failing prefetch returns no variables and never blocks or delays pickup; the agent falls back to the in-call tools.
  • Injection. Results flow through the existing variableValues path (message{{crmInfo}}, structured → flattened named variables), delivered to the ElevenLabs agent (the default platform) as dynamic variables in the conversation-initiation response.
  • Additive. No tool registration or agent-factory changes; "use the injected value and skip the lookup" is a prompt instruction, not a wiring change.

What's injected

Both halves are folded into the single injected {{crmInfo}} block (the same channel as the existing CRM branches), not separate named variables:

GroupContentSource
Customer contextname, vehicle(s) on file, upcoming appointments, returning-vs-newlookupCustomer + getVehiclesPage + getJobsPage
Availability (oil change)earliest drop-off and earliest waiter timegetServices (name match) + two getAvailability calls

Availability is seeded for oil change only — the caller's actual service is unknown pre-call, so any other service still uses the in-call availability call. Availability is shop-level, so it seeds even for a first-time caller with no record. An availability error drops only that lane; the customer context still injects.

Telemetry

A precall.autoops span tree is the single source for observability:

  • parent span with total duration (compared against the 3s budget), customer_found, and per-slot seeded flags;
  • child spans per AutoOps call (lookup, vehicles, jobs, drop-off availability, waiter availability) with latency and outcome;
  • failures recorded with recordException + setStatus(ERROR) + a trace-correlated log; the fail-open path sets precall.autoops.miss + miss_reason (error vs timeout).

This gives prefetch hit-rate, miss-rate + cause, and the latency distribution (the p50/p95) without a separate measurement harness. A follow-up signal confirms the in-call tool-call count actually drops for known callers — the prefetch is only valuable if it does.

See also

  • ADR: .indusk/planning/autoops-precall-prefetch/adr.md
  • The prompt side that consumes these variables: the EAS-blueprint rework (.indusk/sandbox/prompts/EAS-blueprint/).