Skip to content

What a goal is

A goal is one durable completion objective attached to an existing session. It is state, not a scheduler — the session log is its source of truth, and a separate goal-round driver decides when an armed goal actually continues. The goal family lives under packages/goal/ and splits cleanly into four packages:

PackageRolectx key
@deepseek-ai/dsh-goalGoal state, lifecycle, and replay foldctx.goals
@deepseek-ai/dsh-goal-round-driverSame-session continuation driver
@deepseek-ai/dsh-tool-goalModel-facing get_goal/create_goal/update_goal toolsregisters on ctx.tools
@deepseek-ai/dsh-command-goalHuman-facing /goal commandregisters on ctx.commands

Consumers depend on @deepseek-ai/dsh-goal, never on the concrete agent loop. Its subsystem reference is docs/subsystems/goal.md.

The domain model

The host vocabulary lives in packages/goal/goal/src/domain.ts (host-side) and its types in src/types.ts. Key types:

GoalId        — branded id of one goal across its durable revisions
GoalRef       — { id, revision } compare-and-set fence
GoalPhase     — 'active' | 'paused' | 'blocked' | 'complete'
GoalBlockReason — { code: string, message: string } (present exactly while blocked)
GoalSnapshot  — objective, phase, blockedReason?, maxGoalRounds (GoalRef + these)
GoalView      — GoalSnapshot + { roundsStarted, createdAt, updatedAt, activation }
GoalActivation — 'armed' | 'disarmed'  (process-local, never persisted)
GoalOperation — 'create' | 'edit' | 'pause' | 'resume' | 'complete' | 'block' | 'clear'

GoalPhase is the durable phase; GoalActivation is deliberately separate and process-local. roundsStarted is the highest admitted continuation round; maxGoalRounds is the total cap. A blockedReason.code is a stable lower-kebab-case classification chosen by the blocking policy (the model tool always uses model-reported), blockedReason.message is the human/model explanation.

Lifecycle transitions and authority

ctx.goals accepts only the exact live Agent instance registered under its id. Mutations use a GoalRef { id, revision } compare-and-set fence and reject stale refs. Every mutation appends a durable goal/change session event carrying the complete post-mutation snapshot (or a revisioned clear tombstone), so goal state never depends on inbox placement, claim, admission, or discard.

Transitions and who may perform them:

OperationEffectWho
createActive revision-one goal, armedmodel tool / /goal
editRetain phase, blocker reason, activation; edit a completed goal creates a fresh active goalmodel tool / /goal
pausePaused + disarmedmodel tool / /goal
resumeStopped phase or disarmed active → active + armed, clears blocker, only while cap has capacitymodel tool / human-authorized /goal
completeCompleted + disarmedmodel tool (round-authorized) / human
blockBlocked + disarmed, records code + explanationmodel tool (round-authorized) / /goal
clearTombstone; history retained/goal

A block uses one durable phase rather than multiplying lifecycle states. Resume is the only re-arm path: after session resume, fork, or driver replacement, an active goal is automatically disarmed, and only an explicit human-authorized resume (through /goal or the model tool) re-arms continuation. disarm() itself is the lifecycle-only exception — it removes process-local continuation authority without writing a revision or emitting a mutation.

Activation: armed vs disarmed

Activation is never persisted. A fresh cache and every agent/session-start edge disarm it even when replay finds an active durable phase; the continuation driver also calls disarm() before unload or after durability uncertainty. This is why activation is absent from durable replay: the durable phase says "this objective is open," but only a live, armed process may admit the next round.

Goal rounds

A goal round is one continuation cycle admitted for the current goal. The driver materializes it as one goal-sourced turn, which can contain zero or more steps. The glossary makes two essential clarifications:

  • only an admitted, goal-sourced user/message increments roundsStarted;
  • unrelated human turns in the same session do not consume the goal-round cap.

The goal-round driver

@deepseek-ai/dsh-goal-round-driver is the same-session continuation driver with no tunable configurationmaxGoalRounds belongs to the goal definition, and the model-facing blocked threshold belongs to dsh-tool-goal, so the driver duplicates neither.

Round contract (from packages/goal/goal-round-driver/src/index.ts):

  1. when an exact live agent is idle with an active, armed goal and remaining capacity, it checkpoints pending goal mutations (awaits ctx.sessions.flush() and rechecks revision + competing input after the await);
  2. reserves roundsStarted + 1 for the current { goalId, revision } (a stale reservation does not consume the round number);
  3. queues one <goal_round> prompt with GoalMessageSource { kind: 'goal', goalId, revision, round };
  4. the agent/pre-step listener verifies the claimed record and current goal before and after downstream listeners; only an entered user/message increments roundsStarted.

The retained prompt names the JSON-quoted objective and round/maxGoalRounds, treats current workspace, tool results, and durable session state as authoritative, and tells the model to leave the goal active when work remains. A flush failure arriving through agent/error disarms continuation before another round can start. If human work enters the inbox before a reservation, automatic work yields until the agent is idle. At the next idle checkpoint, a goal with a reserved or admitted attempt whose work was cancelled gets paused so cancellation cannot auto-restart it.

The model-facing tools: dsh-tool-goal

@deepseek-ai/dsh-tool-goal registers get_goal, create_goal, and update_goal:

  • get_goal() → the current goal or null, including id/revision, durable phase, admitted/capped rounds, blocker reason, and live activation.
  • create_goal(objective, max_goal_rounds?) — creates one goal from a direct top-level human turn; non-human turns and subagents are rejected at execution.
  • update_goal(goal_id, revision, action, objective?, max_goal_rounds?, blocked_reason?)edit/pause/resume/complete/blocked; blocked_reason required only for blocked, persisted with the stable code model-reported.

Authority. Execution requires the exact live exec.agent, and create/edit/pause/resume additionally require an accepted { kind: 'user' } message or steering event in a runtime-root agent's current turn. { kind: 'user' } is a host attestation: Agent.followup() and steer() assign it when the caller omits a source, so plugins/schedulers/non-human producers must pass their own source rather than inheriting human authority. Complete and blocked also accept the exact current goal round (id + revision + round equal the folded goal), but a goal-round blocked call is mechanically rejected until blockedAfterConsecutiveRounds (default 3).

An autonomous round that successfully reports complete or blocked calls concludeTurn(), stopping the physical turn after that step; direct-human mutations never do. Config: blockedAfterConsecutiveRounds (positive safe integer). The tools render a fixed goal policy into the system prompt.

The /goal human command

@deepseek-ai/dsh-command-goal registers one global command on ctx.commands:

InputResult
/goalShow objective, phase, round count/cap, activation, valid next commands
/goal <objective>Create and arm a goal / replace a completed goal (fresh id)
/goal edit <objective>Edit objective without changing phase/activation
/goal pausePause + disarm
/goal resumeResume / re-arm subject to remaining cap
/goal clearClear pointer, retaining durable history + tombstone

Control words are case-insensitive only as the complete input; anything else is an objective. Because the command plane has no modal editor, edit takes its replacement inline.

Where goals persist

The session log is the only durable authority. Every mutation appends goal/change; a clear appends a revisioned tombstone. Strict replay derives lifecycle state only from these events, rejecting malformed shapes, discontinuous revisions, illegal transitions, non-monotonic timestamps, and non-sequential admitted rounds. goal/changed (scoped emit) fires after the durable event commits. @deepseek-ai/dsh-goal/checkpoint-style helpers are not involved — the checkpoint marker used by session references is a different concept (dsh-compaction).

Replay, the scoped event, and error codes

Strict replay recomputes FoldedGoal — the goal plus roundsStarted, createdAt, updatedAt, and the latest GoalRef — purely from goal/change events. It rejects malformed shapes, discontinuous revisions, illegal lifecycle transitions, non-monotonic timestamps, and non-sequential admitted rounds; incremental replay keeps its cursor at the first corrupt event so a bad record stops the fold without corrupting memory. The flow:

text
durable goal/change events ──► strict replay fold
        │                          ─  reject malformed / illegal / non-monotonic

   FoldedGoal ──► live GoalView (adds activation, roundsStarted counters)


   mutation request ──► GoalRef { id, revision } CAS fence
                          └  stale ref rejected (GOAL_STALE_REVISION)


   commit goal/change ──► goal/changed (scoped emit)

The service exposes stable GoalErrorCode reasons: GOAL_AGENT_NOT_LIVE, GOAL_NOT_FOUND, GOAL_ALREADY_EXISTS, GOAL_STALE_REVISION, GOAL_INVALID_OBJECTIVE, GOAL_INVALID_MAX_ROUNDS, GOAL_INVALID_BLOCK_REASON, GOAL_INVALID_EDIT, GOAL_INVALID_TRANSITION. disarm() is the one operation that mutates process-local authority only: it removes continuation permission without writing a goal/change or advancing the revision, so it never appears in replay.

The shipped dsh base enables the persisted-goal stack + /goal; the ACP automation app enables the domain + model tools without a command adapter; the UI-less agent-spine-demo requires explicit goals: {} so headless one-shot callers do not silently switch from one physical turn to a multi-round operation.

yaml
- id: goal
  name: '@deepseek-ai/dsh-goal'
- id: tool-goal
  name: '@deepseek-ai/dsh-tool-goal'
- id: goal-round-driver
  name: '@deepseek-ai/dsh-goal-round-driver'

Further reading

  • Context Sources — the durable messages among which goal-round prompts sit.
  • Compaction — how accumulated goal-round prompts eventually get shadowed.
  • Interactions — the command plane and tool authority that /goal and the goal tools rely on.
  • packages/goal/goal/src/domain.ts and src/types.ts — the exact domain types and goal/change payload.
  • docs/glossary.md — the goal, goal round, and goal activation definitions.
  • .agents/notes/implemented/feature/2026-07-19-persisted-same-session-goal-domain.md — the goal-domain design decision record.