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 persistenceGOAL_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 countersnormalizeGoalState()— sanitizes partial or legacy goal dataisPersistedGoalState()— 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 newGoalStatewith budget and objectivegoal.complete— marks the goal finished and records final usagegoal.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:
- Sets
session.goalState.status = "complete" - Records final usage via
completionBudgetReport - Writes a final
thread_goal_stateentry 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:
session-jsonl.tsreads the JSONL transcript- The deserializer extracts the most recent
thread_goal_stateentry isPersistedGoalState()validates the structurenormalizeGoalState()migrates any legacy fields- The reconstructed
GoalStatepopulatessession.goalState - The next turn's
createGoalContextMessageuses 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
-
Goal definition:
packages/coding-agent/src/core/goals.tsdefinesGoalState, serialization helpers, and thethread_goal_state/goal_contextcustom message types. -
Host bridge:
packages/coding-agent/src/core/agent-session.tshandles/goalcommands, maintains in-memory state, and writes persistence entries. -
JSONL transcript:
packages/coding-agent/src/core/session-jsonl.tsprovides the immutable storage that survives process restarts. -
Context injection: Every turn starts with a goal-context message built from current state, keeping the objective present in the model's context window.
-
Token accounting: Runtime usage tracking updates
tokensUsed,timeUsedSeconds, andcontinuationsUseduntil explicit completion. -
Test coverage:
packages/coding-agent/test/suite/agent-session-goal.test.tsvalidates creation, continuation, and persistence behavior.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →