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 configuration — maxGoalRounds 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.