File size: 3,848 Bytes
a070805
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
import { useMemo } from "react";
import { useActiveConversation } from "#/hooks/query/use-active-conversation";
import { useOptionalConversationId } from "#/hooks/use-conversation-id";
import { useConversationStateStore } from "#/stores/conversation-state-store";
import { useConversationStore } from "#/stores/conversation-store";
import { AgentState } from "#/types/agent-state";
import { ExecutionStatus } from "#/types/agent-server/core/base/common";

/**
 * Maps agent execution status to AgentState
 */
function mapExecutionStatusToAgentState(
  status: ExecutionStatus | null,
): AgentState {
  if (!status) {
    return AgentState.LOADING;
  }

  switch (status) {
    case ExecutionStatus.IDLE:
      return AgentState.AWAITING_USER_INPUT;
    case ExecutionStatus.RUNNING:
      return AgentState.RUNNING;
    case ExecutionStatus.PAUSED:
      return AgentState.PAUSED;
    case ExecutionStatus.WAITING_FOR_CONFIRMATION:
      return AgentState.AWAITING_USER_CONFIRMATION;
    case ExecutionStatus.FINISHED:
      return AgentState.FINISHED;
    case ExecutionStatus.ERROR:
      return AgentState.ERROR;
    case ExecutionStatus.STUCK:
      return AgentState.ERROR; // Map STUCK to ERROR for now
    default:
      return AgentState.LOADING;
  }
}

export interface UseAgentStateResult {
  curAgentState: AgentState;
  executionStatus?: ExecutionStatus | null;
}

/**
 * Returns the current agent state from conversation execution status.
 *
 * Defaults to the conversation in the current route. Pass `conversationId`
 * to read another conversation's status instead — e.g. a local planner
 * helper conversation, whose run/idle transitions are tracked separately
 * from the main conversation's (see conversation-state-store.ts).
 */
export function useAgentState(conversationId?: string): UseAgentStateResult {
  const { conversationId: routeConversationId } = useOptionalConversationId();
  const targetConversationId = conversationId ?? routeConversationId;
  const isRouteConversation =
    !conversationId || conversationId === routeConversationId;

  const liveExecutionStatus = useConversationStateStore((state) =>
    targetConversationId
      ? (state.executionStatusByConversation[targetConversationId] ?? null)
      : null,
  );
  // The REST fallback only ever describes the route's own conversation, so it
  // only applies when no other conversation was explicitly requested.
  const routeConversationExecutionStatus =
    useActiveConversation().data?.execution_status ?? null;
  const fallbackExecutionStatus = isRouteConversation
    ? routeConversationExecutionStatus
    : null;

  const executionStatus = liveExecutionStatus ?? fallbackExecutionStatus;
  const curAgentState = useMemo(
    () => mapExecutionStatusToAgentState(executionStatus),
    [executionStatus],
  );

  return { curAgentState, executionStatus };
}

export interface UsePlanningAgentStateResult {
  localPlanningConversationId: string | null;
  curPlanningAgentState: AgentState;
  /** Running or loading. `false` (not "unknown") when there's no planner yet. */
  isPlanningAgentRunning: boolean;
}

/**
 * The local planner helper's own state — read separately from the main
 * conversation's via `useAgentState` since the two run independently.
 */
export function usePlanningAgentState(): UsePlanningAgentStateResult {
  const localPlanningConversationId = useConversationStore(
    (state) => state.localPlanningConversationId,
  );
  const { curAgentState: curPlanningAgentState } = useAgentState(
    localPlanningConversationId ?? undefined,
  );
  const isPlanningAgentRunning =
    !!localPlanningConversationId &&
    (curPlanningAgentState === AgentState.RUNNING ||
      curPlanningAgentState === AgentState.LOADING);
  return {
    localPlanningConversationId,
    curPlanningAgentState,
    isPlanningAgentRunning,
  };
}