Files
vscode/.github
44065855e4 agentHost: observe dispatched actions through a chat contribution hook (#333238)
* agentHost: report every terminal turn outcome through onTurnEnd

Closes three gaps where a turn ended without telling contributions. A turn that captured a
start checkpoint and then failed to send never scheduled its changeset recompute, because
`captureTurnStartCheckpoint` runs before `sendMessage` but the catch never reported the end.

- Reports a turn end when a send throws. This is the bug: `CheckpointAndChangesetContribution`
  handles `kind === 'error'` and never heard about these turns, so their changeset recompute
  and unread marking did not run.
- Reports a turn end for a client-dispatched cancellation, which the agent-signal path already
  did. This fires exactly once: the reducer clears the active turn first, so a later
  agent-emitted cancellation takes the no-active-turn path, which reports only completions.
- Adds a `rejected` reason for requests refused before their turn started, and fires it from
  both `startTurn` early returns: a rejected admission and a missing provider. This restores
  the per-rejection observability that hoisting the admission gate removed.
- Makes `MarkUnreadContribution` ignore `rejected`. It filtered negatively, so the new reason
  would have marked a session unread for a request that never ran, and a rejection on an
  archived session would resurface it.
- Leaves `QueueDrainContribution` untouched for `rejected`. A rejected queued turn consumes its
  message and the queue stalls, but draining would cascade an archived chat through every
  queued message. The fix is to return the message to the queue, which is recorded as such.
- Adds tests for all three paths, including that a rejected turn end does not resurface a read
  session. Both new `agentSideEffects` tests were confirmed to fail without the fix.

Rewrites `chatContributions/TODO.md` as a backlog. It had grown into a changelog of finished
extractions and a design-decision record, and two of its claims were already wrong: the
side-chat migration it listed as pending was complete, and `githubReferences` was filed under
outgoing-turn contributions although it only implements `onTurnEnd`. Rationale that had no
other home moved into JSDoc beside the code it explains.

(Commit message generated by Copilot)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* agentHost: observe dispatched actions through a chat contribution hook

Moves session input-needed aggregation, turn-usage persistence, and session flag persistence
out of `AgentSideEffects`, which loses 252 lines. All three observed
`AgentHostStateManager.onDidEmitEnvelope` rather than the client dispatch path, so none of
them could use the existing action hook: most of the actions they react to are dispatched by
the host, not by a client.

- Adds `onDidDispatchAction`, which observes an action from any origin once its outcome is
  known, including server-dispatched actions and rejected ones that never reduced. It is a
  separate hook and not a widening of the client hook, because `queueDrain` depends on that
  hook being client-only and would otherwise see each client action twice.
- Adds `SessionInputNeededContribution` (order 200), which mirrors per-chat blockers into the
  owning session's `inputNeeded` list. This is the bulk of the move at roughly 170 lines.
- Gives `PersistedTurnUsageContribution` the write half of turn-usage persistence. It already
  owned the restore half, so one contribution now owns both directions, as chat drafts and
  chat titles already do.
- Adds `SessionFlagsContribution` (order 700) for read state, archived state, and merged
  config values. It preserves an asymmetry that is easy to lose: read and archived changes
  skip a rejected action, config values do not.
- Makes `AgentHostToolCallTracker` injectable. Input-needed reports blocked and unblocked tool
  calls to it, and it was constructed inside `AgentSideEffects`, so a contribution could not
  reach it.

Renames the two action hooks so each says what it observes:

- `onAction` becomes `onDidApplyClientAction`, and `IObservedAction` becomes
  `IAppliedClientAction`. Rejected client actions never reach it, so an action it sees always
  reduced.
- `onEnvelope` becomes `onDidDispatchAction`, and `IObservedEnvelope` becomes
  `IDispatchedAction`. It also delivers rejected actions, so a name claiming they were applied
  would be wrong.

Registers the built-in contributions in the tool-call and turn-hang telemetry test graphs.
Both build `AgentSideEffects` without them, so they stopped exercising blocked-call telemetry
once that reporting moved into a contribution. The graphs now mirror production wiring; no
assertion changed.

Updates the contributions skill, which described four hooks and named two that no longer
exist. It now documents all seven, records that the admission hook fails closed and is
synchronous while every other hook isolates failures, and carries the design rationale that
`TODO.md` used to hold. `TODO.md` keeps only open work and caveats, with each fact in one
place.

(Commit message generated by Copilot)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

* agentHost: fix cyclic dependency and rejected-action handling from review

- Moves `toolSourceKindFromContributor` and `canRefineContributor` into
  `node/shared/toolCallContributor.ts`. Injecting the turn tracker into the tool-call tracker
  made the two import each other at runtime, which failed the cyclic dependency check.
- Reports a turn end only when a turn actually ended. `AgentHostTurnTracker.turnCompleted` now
  returns whether it had a tracked turn, and the client cancellation and failed-send paths use
  that. The reducer no-ops a stale or duplicate cancellation, so reporting one unconditionally
  marked a read session unread for a turn that never stopped.
- Reports a turn end when a resumed turn fails to send. That path dispatched `ChatError` and
  completed tracking without telling contributions, so it stayed invisible.
- Skips rejected actions in `sessionInputNeeded` and `persistedTurnUsage`. A rejected action
  never reduced, so mirroring it can clear a blocker that is still outstanding, and persisting
  it can write durable state that was refused.
- Starts the turn through `handleAction` in the client-cancellation test, as `_dispatchActionNow`
  does. The shared `startTurn` helper only dispatches to state, so the turn was never tracked
  and the test did not reflect production wiring.
- Adds a test that a stale cancellation reports no turn end, and corrects a stale
  `_chatContributions.action(...)` reference in the backlog.

(Commit message generated by Copilot)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

---------

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Dmitriy Vasyura <dmitriv@microsoft.com>
2026-08-29 00:48:07 +00:00
..