( state: SessionState, details?: RequiresActionDetails, )
| 167 | } |
| 168 | |
| 169 | export function notifySessionStateChanged( |
| 170 | state: SessionState, |
| 171 | details?: RequiresActionDetails, |
| 172 | ): void { |
| 173 | currentState = state |
| 174 | stateListener?.(state, details) |
| 175 | |
| 176 | // Mirror details into external_metadata so GetSession carries the |
| 177 | // pending-action context without proto changes. Cleared via RFC 7396 |
| 178 | // null on the next non-blocked transition. |
| 179 | if (state === 'requires_action' && details) { |
| 180 | hasPendingAction = true |
| 181 | notifySessionMetadataChanged({ |
| 182 | pending_action: details, |
| 183 | }) |
| 184 | } else if (hasPendingAction) { |
| 185 | hasPendingAction = false |
| 186 | notifySessionMetadataChanged({ pending_action: null }) |
| 187 | } |
| 188 | |
| 189 | // task_summary is written mid-turn by the forked summarizer; clear it at |
| 190 | // idle so the next turn doesn't briefly show the previous turn's progress. |
| 191 | if (state === 'idle') { |
| 192 | notifySessionMetadataChanged({ task_summary: null }) |
| 193 | } |
| 194 | |
| 195 | if (state !== 'idle') { |
| 196 | notifyAutomationStateChanged( |
| 197 | isProactiveActive() |
| 198 | ? { |
| 199 | enabled: true, |
| 200 | phase: null, |
| 201 | next_tick_at: null, |
| 202 | sleep_until: null, |
| 203 | } |
| 204 | : null, |
| 205 | ) |
| 206 | } |
| 207 | |
| 208 | // Mirror to the SDK event stream so non-CCR consumers (scmuxd, VS Code) |
| 209 | // see the same authoritative idle/running signal the CCR bridge does. |
| 210 | // 'idle' fires after heldBackResult flushes — lets scmuxd flip IDLE and |
| 211 | // show the bg-task dot instead of a stuck generating spinner. |
| 212 | // |
| 213 | // Opt-in until CCR web + mobile clients learn to ignore this subtype in |
| 214 | // their isWorking() last-message heuristics — the trailing idle event |
| 215 | // currently pins them at "Running...". |
| 216 | // https://anthropic.slack.com/archives/C093BJBD1CP/p1774152406752229 |
| 217 | if (isEnvTruthy(process.env.CLAUDE_CODE_EMIT_SESSION_STATE_EVENTS)) { |
| 218 | enqueueSdkEvent({ |
| 219 | type: 'system', |
| 220 | subtype: 'session_state_changed', |
| 221 | state, |
| 222 | }) |
| 223 | } |
| 224 | } |
| 225 | |
| 226 | export function notifySessionMetadataChanged( |
no test coverage detected