/ state / selectors.ts
selectors.ts
 1  /**
 2   * Selectors for deriving computed state from AppState.
 3   * Keep selectors pure and simple - just data extraction, no side effects.
 4   */
 5  
 6  import type { InProcessTeammateTaskState } from '../tasks/InProcessTeammateTask/types.js'
 7  import { isInProcessTeammateTask } from '../tasks/InProcessTeammateTask/types.js'
 8  import type { LocalAgentTaskState } from '../tasks/LocalAgentTask/LocalAgentTask.js'
 9  import type { AppState } from './AppStateStore.js'
10  
11  /**
12   * Get the currently viewed teammate task, if any.
13   * Returns undefined if:
14   * - No teammate is being viewed (viewingAgentTaskId is undefined)
15   * - The task ID doesn't exist in tasks
16   * - The task is not an in-process teammate task
17   */
18  export function getViewedTeammateTask(
19    appState: Pick<AppState, 'viewingAgentTaskId' | 'tasks'>,
20  ): InProcessTeammateTaskState | undefined {
21    const { viewingAgentTaskId, tasks } = appState
22  
23    // Not viewing any teammate
24    if (!viewingAgentTaskId) {
25      return undefined
26    }
27  
28    // Look up the task
29    const task = tasks[viewingAgentTaskId]
30    if (!task) {
31      return undefined
32    }
33  
34    // Verify it's an in-process teammate task
35    if (!isInProcessTeammateTask(task)) {
36      return undefined
37    }
38  
39    return task
40  }
41  
42  /**
43   * Return type for getActiveAgentForInput selector.
44   * Discriminated union for type-safe input routing.
45   */
46  export type ActiveAgentForInput =
47    | { type: 'leader' }
48    | { type: 'viewed'; task: InProcessTeammateTaskState }
49    | { type: 'named_agent'; task: LocalAgentTaskState }
50  
51  /**
52   * Determine where user input should be routed.
53   * Returns:
54   * - { type: 'leader' } when not viewing a teammate (input goes to leader)
55   * - { type: 'viewed', task } when viewing an agent (input goes to that agent)
56   *
57   * Used by input routing logic to direct user messages to the correct agent.
58   */
59  export function getActiveAgentForInput(
60    appState: AppState,
61  ): ActiveAgentForInput {
62    const viewedTask = getViewedTeammateTask(appState)
63    if (viewedTask) {
64      return { type: 'viewed', task: viewedTask }
65    }
66  
67    const { viewingAgentTaskId, tasks } = appState
68    if (viewingAgentTaskId) {
69      const task = tasks[viewingAgentTaskId]
70      if (task?.type === 'local_agent') {
71        return { type: 'named_agent', task }
72      }
73    }
74  
75    return { type: 'leader' }
76  }