---
title: "Moves: SDK"
description: "List, generate, and act on recommended next moves."
---

A move is an engine-recommended next action — the answer to "given what the agent currently believes, what should it investigate next?" — ranked by expected information gain. `beliefs.moves.*` wraps the engine's recommender. See [Moves (concept)](/dev/core/moves) for the model behind the surface; this page covers the SDK methods.

`forecast(action)` and `cascade(action)` look similar but answer different questions. `forecast` projects the action's value on **the current agent's** belief state — "how much will this clarify *my* picture?" `cascade` projects the same action across **other agents' beliefs** via the influence matrix — "if I do this, how much churn does it create for the rest of the swarm?" Use forecast for self-directed planning; use cascade when you're coordinating multi-agent work.

## `beliefs.moves.list(options?)`

Get the currently-ranked moves for the bound scope. Moves come back highest-priority first.

```ts
const moves = await beliefs.moves.list({ topN: 3 })
for (const m of moves) {
  console.log(m.action, m.rationale, m.expectedDeltaH)
}
```

Options:

| Option | Type | What it does |
|--------|------|--------------|
| `topN` | `number` | Cap the returned slice. Server-side ranking is unchanged; this is a client-side trim. |

Returns `ThinkingMove[]`.

## `beliefs.moves.generate(options)`

Ask the recommender for a fresh move targeting a specific belief. Use this when you want a move *now* (e.g., the user just opened a belief detail page) rather than waiting for one to appear in `list()`.

```ts
const result = await beliefs.moves.generate({
  beliefId: 'belief-abc123',
  includeJustification: true,
})

if (result.move) {
  showRecommendation(result.move, result.move.justification)
} else if (result.reason === 'belief_complete') {
  showDoneState()
}
```

Options:

| Option | Type | What it does |
|--------|------|--------------|
| `beliefId` | `string` | **Required.** Belief to target. |
| `targetId` | `string` | Alias for `beliefId`. `beliefId` wins if both are set. |
| `includeJustification` | `boolean` | Attach the engine's full justification payload to the move. |
| `sessionId` | `string` | Bind to a specific session for analytics. |

Returns:

```ts
{
  success: true
  move: ThinkingMoveWithJustification | null
  target: ResolvedCanonicalTarget
  reason?: 'belief_complete'    // present when no move was generated
  durationMs: number
}
```

`move: null` with `reason: 'belief_complete'` is the engine signaling "this belief is in good shape — nothing to recommend right now."

## `beliefs.moves.act(moveId, action, options?)`

Record a user action on a move. The engine learns from accept/snooze/dismiss signals to improve future ranking.

```ts
await beliefs.moves.act(move.id, 'accept')
await beliefs.moves.act(move.id, 'snooze')
await beliefs.moves.act(move.id, 'dismiss')
```

`action` is one of `'accept'`, `'snooze'`, `'dismiss'`. Any other value throws `TypeError`.

Returns:

```ts
{
  success: true
  move: ThinkingMove   // updated with new status / resolvedAt
  durationMs: number
}
```

## `beliefs.moves.rank(options?)`

Engine-ranked next-best moves over the current scope. Each entry surfaces the composite ranking score, expected info-gain, cost, and a cost-normalized ratio so callers can budget-cap on any axis.

```ts
const ranked = await beliefs.moves.rank({ topN: 3, budget: 0.05 })
for (const m of ranked) {
  console.log(`${m.action}/${m.subType} → q=${m.qValue} cost=${m.cost} voi=${m.valueOfInformation}`)
}
```

Options: `topN?` (default 5, max 50), `budget?` (filters out moves whose `cost` exceeds budget before ranking), `agentId?`, `signal?`.

Returns `MoveRankingSummary[]`:

```ts
{
  id, summary, targetId
  targetKind: 'claim' | 'goal' | 'gap'
  action: string                            // 'gather_evidence', 'clarify', ...
  subType: string                           // 'design_test', 'tradeoff_mapping', ...
  qValue: number                            // composite ranking score (higher = better)
  expectedInfoGain: number                  // expected info-gain from executing this move
  cost: number                              // USD / tokens / effort units
  valueOfInformation: number                // info-gain / max(cost, 0.01)
  executor: 'agent' | 'user' | 'both'
  confidence: 'low' | 'medium' | 'high'
}
```

## `beliefs.moves.forecast(action, options?)`

Project the expected value of a candidate action on the current belief state. The engine runs its predictive model forward from the current state and returns one summary.

```ts
const summary = await beliefs.moves.forecast('gather_evidence', { depth: 3, rollouts: 50 })
console.log(`score=${summary.score} confidence=${summary.confidence}`)
console.log(`will sharpen: ${summary.willAnswer.join(', ')}`)
```

Options: `depth?` (max 5), `rollouts?` (max 200), `maxTopics?`, `agentId?`, `signal?`.

Returns `ForecastSummary` — same shape as `beliefs.forecast.predict` documented below.

## `beliefs.moves.cascade(action, options?)`

Predict how a candidate action will ripple through *other* agents' beliefs via the fit influence matrix. Use this for multi-agent coordination — knowing whether your move will cause downstream churn before you make it.

```ts
const cascade = await beliefs.moves.cascade('gather_evidence', {
  targetBeliefId: 'b-market-size',
  magnitude: 0.3,
})
for (const shift of cascade.willShift) {
  if (shift.severity !== 'none') {
    console.warn(`Agent ${shift.agent}: ${shift.summary}`)
  }
}
```

Options: `targetBeliefId?` (defaults to most-uncertain active belief), `magnitude?` (0–1, default 0.2), `maxAgents?`, `agentId?`, `signal?`.

Returns `CascadeSummary`:

```ts
{
  id, summary
  /** Aggregate cascade risk: 0 = isolated, 1 = every known agent feels it. */
  score: number
  willShift: Array<{
    agent: string
    summary: string
    severity: 'none' | 'low' | 'medium' | 'high'
    affectedBeliefs?: string[]
  }>
  confidence: 'low' | 'medium' | 'high'
  why: string
}
```

Cold-start workspaces return `score: 0` with `confidence: 'low'` — the influence matrix has no co-observation evidence yet.

---

## `beliefs.forecast.predict(actions, options?)`

Free-form action forecasting. Where `moves.forecast(action)` evaluates one action against the engine's recommended-move vocabulary, `forecast.predict(actions[])` runs the same predictive model against an arbitrary list of caller-supplied actions and returns one summary per input action, in input order.

```ts
const forecasts = await beliefs.forecast.predict(
  ['gather_evidence_apac', 'design_test_market_size', 'reframe_question'],
  { horizon: 3, rollouts: 50 },
)

const ranked = [...forecasts].sort((a, b) => b.score - a.score)
console.log(`Best action: ${ranked[0].summary} (score=${ranked[0].score})`)
```

Options:

| Option | Default | What it does |
|--------|---------|--------------|
| `horizon` | `1` | Rollout depth per action (max 5). |
| `rollouts` | `30` | Independent rollouts per action (max 200). |
| `maxTopics` | — | Cap on belief topics surfaced in `willAnswer`. |
| `agentId` | bound agent | Run the forecast as a different agent. |
| `signal` | — | `AbortSignal` for cancellation. |

Returns `ForecastSummary[]`:

```ts
{
  id: string
  summary: string
  /** 0–1 expected value. Higher = more useful. */
  score: number
  /** Plain-English belief topics most likely to sharpen under this action. */
  willAnswer: string[]
  /** Confidence in the forecast itself, not the action. */
  confidence: 'low' | 'medium' | 'high'
  /** Short human explanation. */
  why: string
  suggestion?: string
  relatedBeliefs?: string[]
}
```

`confidence` reflects how much evidence the engine's predictive model has accumulated for similar actions in this workspace — distinct from `score`. A high-`score` action with `confidence: 'low'` means "this looks great, but we haven't seen this action before, so the score is extrapolation rather than a track record."

<Callout type="info" title="Cold-start behavior">
On a fresh workspace with no archived deltas, every forecast comes back with `confidence: 'low'` and a low `score`. That's the honest answer — the model has no evidence yet. Forecasts typically reach `confidence: 'medium'` after roughly 5–10 `after()` calls in the workspace, and `'high'` once dozens of similar actions have been observed.
</Callout>

---

## `ThinkingMove` shape

```ts
{
  id: string
  targetId: string
  targetEntityType?: 'claim' | 'goal' | 'gap' | 'risk' | string
  targetEntityId?: string
  action: 'clarify' | 'gather_evidence' | 'resolve_uncertainty' | 'compare_paths' | string
  rationale: string
  expectedDeltaH: number       // expected uncertainty reduction from acting on this move
  status: 'suggested' | 'accepted' | 'snoozed' | 'dismissed' | string
  suggestedModality?: string   // hint about how to surface (e.g., 'inline', 'banner')
  qValue?: number              // recommender's internal score, when available
  executor?: 'agent' | 'user' | 'both'
  createdAt: string
  updatedAt?: string
  resolvedAt?: string
}
```

`expectedDeltaH` is the recommender's estimate of how much uncertainty this move reduces if accepted — it's the `value` field in the [concept doc](/dev/core/moves).

<Callout type="info" title="Auth">
The moves namespace requires `apiKey` or `scopeToken` auth. `serviceToken` callers cannot invoke `moves.*`. See [Auth](/dev/sdk/auth).
</Callout>
