selectors.ts 2.2 KB

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