Crew.kickoff(inputs={...}) is the entry point that ties together checkpoint restore, task preparation, task execution, result assembly, and cleanup. The current source treats kickoff as a runtime path rather than a reference surface: the important question is what happens in order, what owns each handoff, and why the crew waits at each boundary before it moves on.

Entry: Crew.kickoff

The kickoff method first checks apply_checkpoint(self, from_checkpoint) in lib/crewai/src/crewai/crew.py. When that call returns a restored crew, the method immediately re-enters kickoff on the restored instance, so the resumed crew follows the same runtime path as a fresh crew from that point forward.

When self.stream is true, kickoff enables agent streaming, starts the crew work on a background thread, and returns a CrewStreamingOutput backed by a chunk generator. That branch keeps the synchronous crew pipeline intact while exposing streaming chunks to the caller, and the deeper threading and state flow live in ./05-threads-asyncio-and-the-async-barrier.md and ./07-where-state-lives.md.

Preparation: prepare_kickoff

prepare_kickoff in lib/crewai/src/crewai/crews/utils.py owns the work that has to happen before the task loop starts. It runs before_kickoff_callbacks, dispatches the execution and input interception points, normalizes the inputs mapping, extracts file objects from the inputs, stores all file inputs in the shared file store, and interpolates {placeholders} with their values before any task sees the prompt.

The same preparation step also resets the task output handler, sets task callbacks, establishes the first task trigger context, sets up the crew agents, and optionally runs planning. In the current source, kickoff uses that one preparation step to make the runtime state consistent before the process begins.

Process dispatch

After preparation, Crew.kickoff chooses either _run_sequential_process or _run_hierarchical_process, and both paths funnel into _execute_tasks in lib/crewai/src/crewai/crew.py. Hierarchical execution changes which agent runs each task and when delegation tools appear, but it does not replace the scheduler itself; the task list still drives the order of execution. See ./04-the-hierarchical-process.md for that process distinction.

The task loop

_execute_tasks walks the tasks in list order and asks prepare_task_execution for the current agent, tool set, and replay skip state. From there, the loop handles conditional-task skipping, replay aware skipping, and context assembly from earlier outputs. The default context path accumulates prior task outputs, while explicit task context narrows the input set for that task.

Async tasks enter the loop as futures, and the next synchronous task or the end of the crew acts as the join barrier. When the loop reaches a synchronous task with pending async work, it waits for the outstanding futures, collects their outputs, and then hands the current task to the executor path. That handoff leads into CrewAgentExecutor in lib/crewai/src/crewai/agents/crew_agent_executor.py, which owns the agent turn itself; the executor loop is covered in ./02-the-agent-executor-loop.md, and the async barrier behavior is covered in ./05-threads-asyncio-and-the-async-barrier.md.

_store_execution_log writes each completed task into self._task_output_handler, and that log is the replayability seam. replay() reads those stored outputs, restores the saved context, and resumes from a chosen task instead of starting the crew from the beginning.

Output assembly

_create_crew_output chooses the last task output with content as the crew result. It then aggregates token and usage metrics and builds a CrewOutput from lib/crewai/src/crewai/crews/crew_output.py, which carries raw, pydantic, json_dict, tasks_output, and token_usage fields. The final object keeps both the typed result and the plain task trail that produced it.

Tail

On the success path, kickoff runs after_kickoff_callbacks, passes the result through _post_kickoff, drains memory writes, and emits CrewKickoffCompletedEvent before it returns. If kickoff fails, the finally cleanup block still runs. It clears file inputs, unwinds the runtime scope, and the exception path emits CrewKickoffFailedEvent.

_drain_memory_writes stays in that cleanup path because memory saves can still be in flight when kickoff otherwise looks finished. Memory in lib/crewai/src/crewai/memory/unified_memory.py uses a single worker save pool, so kickoff waits for those saves before completion can race listener teardown.

Side channels

The event bus carries kickoff lifecycle events throughout the run, including CrewKickoffStartedEvent, CrewKickoffCompletedEvent, and CrewKickoffFailedEvent, and it also carries the memory save events that surface during shutdown. Execution logs give replay() the state it needs to resume from a task instead of rebuilding the entire crew history.

Lifecycle map

flowchart TD
  Entry[Kickoff entry] --> Restore{Checkpoint restore available}
  Restore -->|Yes| Reenter[Call kickoff on restored crew]
  Restore -->|No| Stream{Stream mode enabled}
  Stream -->|Yes| StreamSetup[Enable agent streaming]
  StreamSetup --> Thread[Run crew work on background thread]
  Thread --> Chunker[Return CrewStreamingOutput via chunk generator]
  Stream -->|No| Prepare[Run prepare_kickoff]
  Prepare --> Dispatch[Select sequential or hierarchical path]
  Dispatch --> Loop[_execute_tasks]
  Loop --> Output[_create_crew_output]
  Output --> Tail[Drain memory writes and emit completion]

Where to look in the code

  • lib/crewai/src/crewai/crew.py — kickoff entry, task flow, output assembly, replay, and cleanup.
  • lib/crewai/src/crewai/crews/utils.py — kickoff preparation and input handling.
  • lib/crewai/src/crewai/agents/crew_agent_executor.py — agent turn execution.
  • lib/crewai/src/crewai/memory/unified_memory.py — background saves and drain behavior.
  • lib/crewai/src/crewai/crews/crew_output.py — final crew result shape.
  • lib/crewai/src/crewai/events/types/crew_events.py — kickoff lifecycle events.