MCPcopy Index your code
hub / github.com/coder/mux / updateStreamingStatus

Method updateStreamingStatus

src/node/services/workspaceService.ts:2449–2513  ·  view source on GitHub ↗
(
    workspaceId: string,
    streaming: boolean,
    update: ExtensionMetadataStreamingUpdate = {}
  )

Source from the content-addressed store, hash-verified

2447 }
2448
2449 private async updateStreamingStatus(
2450 workspaceId: string,
2451 streaming: boolean,
2452 update: ExtensionMetadataStreamingUpdate = {}
2453 ): Promise<void> {
2454 const streamGeneration = update.generation ?? this.streamingGenerations.get(workspaceId) ?? 0;
2455 try {
2456 let { hasTodos, todoStatus } = update;
2457 if (!streaming && (hasTodos === undefined || todoStatus === undefined)) {
2458 // Stop snapshots need an authoritative todo summary even for background workspaces,
2459 // and centralizing the read here preserves the fire-and-forget abort/error handlers.
2460 const sessionDir = this.config.getSessionDir(workspaceId);
2461 const todos = await readTodosForSessionDir(sessionDir);
2462 hasTodos ??= todos.length > 0;
2463 // When there are no todos to derive from, leave `todoStatus` undefined
2464 // so setStreaming doesn't touch the slot. AgentStatusService writes
2465 // its AI-generated summary into the same `todoStatus` field — passing
2466 // `null` here would clobber a freshly generated summary every time a
2467 // free-form (no-todo) turn ends. Explicit clears still happen via
2468 // setTodoStatus(null) when the agent calls `todo_write([])`.
2469 todoStatus ??= deriveTodoStatus(todos);
2470 }
2471 if (
2472 !streaming &&
2473 update.generation !== undefined &&
2474 update.generation !== (this.streamingGenerations.get(workspaceId) ?? 0)
2475 ) {
2476 // A newer stream has started since this stop was initiated, so dropping the stale
2477 // streaming=false write preserves the active stream's metadata snapshot.
2478 return;
2479 }
2480
2481 const snapshot = await this.extensionMetadata.setStreaming(workspaceId, streaming, {
2482 ...update,
2483 ...(todoStatus !== undefined ? { todoStatus } : {}),
2484 ...(hasTodos !== undefined ? { hasTodos } : {}),
2485 });
2486 // Compaction tagging is stop-snapshot only. Never tag streaming=true updates,
2487 // otherwise fast follow-up turns can inherit stale compaction metadata before cleanup runs.
2488 const shouldTagCompaction =
2489 !streaming && this.compactionStreamGenerations.get(workspaceId) === streamGeneration;
2490 const shouldTagIdleCompaction = !streaming && this.idleCompactingWorkspaces.has(workspaceId);
2491 this.emitWorkspaceActivity(
2492 workspaceId,
2493 await this.mergeCurrentActiveWorkflowRunCount(workspaceId, {
2494 ...snapshot,
2495 ...(shouldTagCompaction ? { isCompaction: true } : {}),
2496 ...(shouldTagIdleCompaction ? { isIdleCompaction: true } : {}),
2497 })
2498 );
2499 } catch (error) {
2500 log.error("Failed to update workspace streaming status", { workspaceId, error });
2501 } finally {
2502 // Compaction markers are turn-scoped. Always clear matching streaming=false
2503 // transitions, even when metadata writes fail, so stale state cannot leak into
2504 // future user streams. Match by generation so an old stop cannot clear a newer
2505 // compaction that started while the stop snapshot was doing async work.
2506 if (!streaming) {

Callers 3

stopStreamingStatusMethod · 0.95

Calls 9

emitWorkspaceActivityMethod · 0.95
readTodosForSessionDirFunction · 0.90
deriveTodoStatusFunction · 0.90
setStreamingMethod · 0.80
getMethod · 0.65
getSessionDirMethod · 0.65
hasMethod · 0.45
deleteMethod · 0.45

Tested by

no test coverage detected