Schema and middleware for per-checkpoint state restored when resuming.
ResumeState declares several checkpointed, schema-private channels. They fall
into two groups with different write paths:
Written from inside the graph on successful model turns:
_context_tokens — total context tokens from the latest
AIMessage.usage_metadata, written by ResumeStateMiddleware.after_model.
Powers /tokens and the status bar._model_spec / _model_params — the model and invocation params effectively
in use for the turn, written by ConfigurableModelMiddleware after a
successful model call. Lets dcode -r restore the model the resumed thread
was actually using instead of falling back to the user's global default.Written through the main graph or by the TUI client via aupdate_state (see
DeepAgentsApp._persist_goal_rubric_state) — these are user/agent-owned. Their
write sites are called out below:
_goal_objective / _goal_status / _goal_rubric / _goal_status_note —
the accepted goal and its lifecycle status. _goal_objective/_goal_rubric
are client-only, but _goal_status/_goal_status_note are also written
from inside the graph by the agent's update_goal tool._pending_goal_completion_note — optional agent-provided completion evidence
awaiting the post-turn rubric result._sticky_rubric — the TUI-owned persistent rubric. This is separate from
the public rubric graph input so one-shot rubric turns can be checkpointed
without being restored as sticky state._pending_goal_objective / _pending_goal_rubric / _pending_goal_kind /
_pending_goal_request_id — a proposed goal or amendment and its originating
request, written by GoalCriteriaMiddleware inside the main graph, then
cleared by the TUI when the user accepts or rejects it.All of these are facts the CLI reads back from state_values on thread resume
so it can rehydrate the session without replaying or re-tokenizing history.
The model-turn channels are persisted from inside the graph (rather than via a
separate client-side aupdate_state call) so the write rides the same checkpoint
as the model response and avoids creating a standalone UpdateState run in
LangSmith. Because they are versioned channel state, resuming a specific
checkpoint yields the values as of that checkpoint — not a thread-level
aggregate. Accepted goal/rubric state is client-written because the user sets it
outside any model turn; pending criteria proposals and agent-driven status
updates are graph-written. Both paths work identically against local and remote
(HTTP) graphs.
Lifecycle status of a TUI-owned goal.
active and blocked are unfinished working states, paused preserves the goal
without driving work, and complete is terminal. A blocked goal is still
considered actionable (active=True) by get_goal, whereas a paused goal is
unfinished but reports active=False.
Whether a pending review creates a goal or amends the current one.
Every verdict RubricMiddleware can emit for a completed grading run.
Derived from the SDK's RubricResult Literal so it cannot drift out of sync
with the grader vocabulary: if the SDK renames or adds a verdict, this set
follows automatically. Consumers that branch on a rubric result (goal
auto-completion in app.py, the rubric-event formatters in textual_adapter)
treat any value outside this set as an unrecognized grade rather than silently
mishandling it.
Narrow a persisted proposal kind to a known value.
Narrow a persisted goal-status value to a known GoalStatus.
A corrupt or forward-version checkpoint can carry an unexpected status
string (or a non-string). Coercing to None rather than passing the raw
value through keeps the GoalStatus Literal load-bearing on the read
path, so an unknown status is treated as "no goal status" instead of a
silently active goal. Resume/restore callers should log the discard
separately so it is surfaced rather than dropped; the model-read path
(_goal_snapshot) intentionally treats an unknown status as active
without logging.
Goal/rubric state channels shared by every schema that touches them.
Declared once here so each schema that carries these channels —
ResumeState and goal_tools.GoalToolState — inherits the same
PrivateStateAttr-marked annotations. Middleware state schemas merge with
later entries winning, so an independent re-declaration that dropped the
PrivateStateAttr marker would override these and leak the field into the
public graph input/output schema. Inheriting from a single base makes that
drift unrepresentable.
Extends agent state with per-checkpoint facts restored on resume.
Inherits the shared goal/rubric channels from GoalRubricChannels and adds
the channels unique to resume: the after-model token/spec facts and the
pending-goal proposal awaiting acceptance.
Persists per-checkpoint resume facts after each model call.
See the module docstring for why this rides the model node's checkpoint
instead of a separate aupdate_state (avoids a standalone UpdateState
run in LangSmith and works identically against remote graphs).