How Persistent Goals Work in Prime Agent Across Turns

Prime Agent implements persistent goals by storing a GoalState object in the session's persistent state and emitting a custom thread_goal_state message that survives across turns and process restarts.

Persistent goals are a core mechanism in Prime Agent that allow multi-turn conversations to track objectives, token budgets, and progress over time. Unlike ephemeral instructions, these goals maintain state through explicit serialization to the session transcript and reconstruction at runtime. This article examines the complete implementation based on the PrimeIntellect-ai/prime-agent source code.

Goal State Definition and Custom Message Types

The foundation of persistent goals lives in packages/coding-agent/src/core/goals.ts. This file defines the data structures and helpers that make cross-turn persistence possible.

Key constants and types:

  • GOAL_STATE_CUSTOM_TYPE = "thread_goal_state" — the identifier for persistence
  • GOAL_CONTEXT_CUSTOM_TYPE = "goal_context" — the identifier for turn-start context messages

The GoalState interface tracks all mutable goal fields:

interface GoalState {
  status: "active" | "complete" | "abandoned";
  objective: string;
  tokenBudget: number;
  tokensUsed: number;
  timeUsedSeconds: number;
  continuationsUsed: number;
  // ... additional metadata
}

Helper functions provide safe construction and validation:

  • emptyGoalState() — creates a fresh goal with zeroed counters
  • normalizeGoalState() — sanitizes partial or legacy goal data
  • isPersistedGoalState() — runtime type guard for deserialization safety

Goal Creation via the Host Bridge

When a user issues a /goal … command, the kernel-side "goal" skill invokes host requests that the agent session handles. The handleGoalHostRequest method in packages/coding-agent/src/core/agent-session.ts processes these requests.

Supported host request types:

  • goal.create — initializes a new GoalState with budget and objective
  • goal.complete — marks the goal finished and records final usage
  • goal.abandon — cancels the goal without completion

The session stores the active goal in a mutable field:

// From agent-session.ts implementation
session.handleGoalHostRequest("goal.create", {
  objective: "write a concise summary of the project README",
  token_budget: 500,
});
// → session.goalState now contains an active GoalState

The response to the kernel uses goalHostResponse to serialize the goal as SerializedGoal, including remaining token calculations for display.

Persistence in the Session JSONL Transcript

Every turn, the session writes a custom entry to the immutable JSONL transcript. This entry is handled by the serialization code in packages/coding-agent/src/core/session-jsonl.ts.

The persistence format:

{
  "type": "custom",
  "customType": "thread_goal_state",
  "data": {
    "status": "active",
    "objective": "write a concise summary of the project README",
    "tokenBudget": 500,
    "tokensUsed": 127,
    "timeUsedSeconds": 45,
    "continuationsUsed": 1
  }
}

Because this entry becomes part of the append-only transcript, the goal survives:

  • Process restarts
  • Session serialization to disk
  • Resumption from saved state

On session load, the deserializer scans for the latest thread_goal_state entry, validates it with isPersistedGoalState, and reconstructs session.goalState.

Goal Context Messages at Turn Start

Each new turn begins with a goal-context message that reminds the model of the persistent objective. The createGoalContextMessage function builds this message from the current GoalState.

Available prompt templates:

Template Use Case
continuationPrompt Default for ongoing goal work
budgetLimitPrompt When token budget is nearly exhausted
objectiveUpdatedPrompt When the goal description changes mid-session

Usage in the session loop:

// Inject the goal context at the beginning of a turn
const goalMsg = createGoalContextMessage(session.goalState, "continuation");
session.addMessage(goalMsg);   // custom message added to the transcript

This message carries customType: "goal_context" and appears in both the UI and the transcript history.

Runtime State Updates and Completion

As the model generates output, the session continuously updates consumption metrics:

// Update usage after the model finishes a turn
session.goalState.tokensUsed += goalTokenDeltaForUsage(usage);
session.goalState.timeUsedSeconds += turnDuration;

When code running in the IPython environment calls await goal.complete(), the kernel emits a goal.complete host request. The session handler:

  1. Sets session.goalState.status = "complete"
  2. Records final usage via completionBudgetReport
  3. Writes a final thread_goal_state entry to the transcript
// Mark the goal complete from IPython
await goal.complete();   // triggers goal.complete host request
// → session.goalState.status === "complete"

Session Resumption and State Restoration

The complete persistence cycle enables seamless continuity. When a saved session opens:

  1. session-jsonl.ts reads the JSONL transcript
  2. The deserializer extracts the most recent thread_goal_state entry
  3. isPersistedGoalState() validates the structure
  4. normalizeGoalState() migrates any legacy fields
  5. The reconstructed GoalState populates session.goalState
  6. The next turn's createGoalContextMessage uses this state

The UI layer in packages/coding-agent/src/modes/agents-view/agents-view-mode.ts mirrors this pattern with AgentsViewPersistentState, ensuring view-level state also survives restarts.

Summary

Frequently Asked Questions

What happens to a goal if the Prime Agent process crashes?

The goal survives. Because thread_goal_state entries are written to the JSONL transcript after each state change, resuming the session reloads the latest goal from disk. The deserializer in session-jsonl.ts reconstructs GoalState exactly as it existed before the crash.

Can the token budget be modified after goal creation?

Yes. The handleGoalHostRequest implementation supports updates to the active goal. A new thread_goal_state entry with modified tokenBudget supersedes previous entries during deserialization, though the transcript retains the full history for audit purposes.

How does the model know a goal persists across turns?

The createGoalContextMessage function explicitly prompts the model with continuation language. Unlike one-shot instructions, these prompts remind the model that the objective spans multiple turns. The goal_context message type also distinguishes persistent goals from ephemeral user messages in the UI.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →