/ tasks / InProcessTeammateTask / InProcessTeammateTask.tsx
InProcessTeammateTask.tsx
  1  /**
  2   * InProcessTeammateTask - Manages in-process teammate lifecycle
  3   *
  4   * This component implements the Task interface for in-process teammates.
  5   * Unlike LocalAgentTask (background agents), in-process teammates:
  6   * 1. Run in the same Node.js process using AsyncLocalStorage for isolation
  7   * 2. Have team-aware identity (agentName@teamName)
  8   * 3. Support plan mode approval flow
  9   * 4. Can be idle (waiting for work) or active (processing)
 10   */
 11  
 12  import { isTerminalTaskStatus, type SetAppState, type Task, type TaskStateBase } from '../../Task.js';
 13  import type { Message } from '../../types/message.js';
 14  import { logForDebugging } from '../../utils/debug.js';
 15  import { createUserMessage } from '../../utils/messages.js';
 16  import { killInProcessTeammate } from '../../utils/swarm/spawnInProcess.js';
 17  import { updateTaskState } from '../../utils/task/framework.js';
 18  import type { InProcessTeammateTaskState } from './types.js';
 19  import { appendCappedMessage, isInProcessTeammateTask } from './types.js';
 20  
 21  /**
 22   * InProcessTeammateTask - Handles in-process teammate execution.
 23   */
 24  export const InProcessTeammateTask: Task = {
 25    name: 'InProcessTeammateTask',
 26    type: 'in_process_teammate',
 27    async kill(taskId, setAppState) {
 28      killInProcessTeammate(taskId, setAppState);
 29    }
 30  };
 31  
 32  /**
 33   * Request shutdown for a teammate.
 34   */
 35  export function requestTeammateShutdown(taskId: string, setAppState: SetAppState): void {
 36    updateTaskState<InProcessTeammateTaskState>(taskId, setAppState, task => {
 37      if (task.status !== 'running' || task.shutdownRequested) {
 38        return task;
 39      }
 40      return {
 41        ...task,
 42        shutdownRequested: true
 43      };
 44    });
 45  }
 46  
 47  /**
 48   * Append a message to a teammate's conversation history.
 49   * Used for zoomed view to show the teammate's conversation.
 50   */
 51  export function appendTeammateMessage(taskId: string, message: Message, setAppState: SetAppState): void {
 52    updateTaskState<InProcessTeammateTaskState>(taskId, setAppState, task => {
 53      if (task.status !== 'running') {
 54        return task;
 55      }
 56      return {
 57        ...task,
 58        messages: appendCappedMessage(task.messages, message)
 59      };
 60    });
 61  }
 62  
 63  /**
 64   * Inject a user message to a teammate's pending queue.
 65   * Used when viewing a teammate's transcript to send typed messages to them.
 66   * Also adds the message to task.messages so it appears immediately in the transcript.
 67   */
 68  export function injectUserMessageToTeammate(taskId: string, message: string, setAppState: SetAppState): void {
 69    updateTaskState<InProcessTeammateTaskState>(taskId, setAppState, task => {
 70      // Allow message injection when teammate is running or idle (waiting for input)
 71      // Only reject if teammate is in a terminal state
 72      if (isTerminalTaskStatus(task.status)) {
 73        logForDebugging(`Dropping message for teammate task ${taskId}: task status is "${task.status}"`);
 74        return task;
 75      }
 76      return {
 77        ...task,
 78        pendingUserMessages: [...task.pendingUserMessages, message],
 79        messages: appendCappedMessage(task.messages, createUserMessage({
 80          content: message
 81        }))
 82      };
 83    });
 84  }
 85  
 86  /**
 87   * Get teammate task by agent ID from AppState.
 88   * Prefers running tasks over killed/completed ones in case multiple tasks
 89   * with the same agentId exist.
 90   * Returns undefined if not found.
 91   */
 92  export function findTeammateTaskByAgentId(agentId: string, tasks: Record<string, TaskStateBase>): InProcessTeammateTaskState | undefined {
 93    let fallback: InProcessTeammateTaskState | undefined;
 94    for (const task of Object.values(tasks)) {
 95      if (isInProcessTeammateTask(task) && task.identity.agentId === agentId) {
 96        // Prefer running tasks in case old killed tasks still exist in AppState
 97        // alongside new running ones with the same agentId
 98        if (task.status === 'running') {
 99          return task;
100        }
101        // Keep first match as fallback in case no running task exists
102        if (!fallback) {
103          fallback = task;
104        }
105      }
106    }
107    return fallback;
108  }
109  
110  /**
111   * Get all in-process teammate tasks from AppState.
112   */
113  export function getAllInProcessTeammateTasks(tasks: Record<string, TaskStateBase>): InProcessTeammateTaskState[] {
114    return Object.values(tasks).filter(isInProcessTeammateTask);
115  }
116  
117  /**
118   * Get running in-process teammates sorted alphabetically by agentName.
119   * Shared between TeammateSpinnerTree display, PromptInput footer selector,
120   * and useBackgroundTaskNavigation — selectedIPAgentIndex maps into this
121   * array, so all three must agree on sort order.
122   */
123  export function getRunningTeammatesSorted(tasks: Record<string, TaskStateBase>): InProcessTeammateTaskState[] {
124    return getAllInProcessTeammateTasks(tasks).filter(t => t.status === 'running').sort((a, b) => a.identity.agentName.localeCompare(b.identity.agentName));
125  }
126  //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJuYW1lcyI6WyJpc1Rlcm1pbmFsVGFza1N0YXR1cyIsIlNldEFwcFN0YXRlIiwiVGFzayIsIlRhc2tTdGF0ZUJhc2UiLCJNZXNzYWdlIiwibG9nRm9yRGVidWdnaW5nIiwiY3JlYXRlVXNlck1lc3NhZ2UiLCJraWxsSW5Qcm9jZXNzVGVhbW1hdGUiLCJ1cGRhdGVUYXNrU3RhdGUiLCJJblByb2Nlc3NUZWFtbWF0ZVRhc2tTdGF0ZSIsImFwcGVuZENhcHBlZE1lc3NhZ2UiLCJpc0luUHJvY2Vzc1RlYW1tYXRlVGFzayIsIkluUHJvY2Vzc1RlYW1tYXRlVGFzayIsIm5hbWUiLCJ0eXBlIiwia2lsbCIsInRhc2tJZCIsInNldEFwcFN0YXRlIiwicmVxdWVzdFRlYW1tYXRlU2h1dGRvd24iLCJ0YXNrIiwic3RhdHVzIiwic2h1dGRvd25SZXF1ZXN0ZWQiLCJhcHBlbmRUZWFtbWF0ZU1lc3NhZ2UiLCJtZXNzYWdlIiwibWVzc2FnZXMiLCJpbmplY3RVc2VyTWVzc2FnZVRvVGVhbW1hdGUiLCJwZW5kaW5nVXNlck1lc3NhZ2VzIiwiY29udGVudCIsImZpbmRUZWFtbWF0ZVRhc2tCeUFnZW50SWQiLCJhZ2VudElkIiwidGFza3MiLCJSZWNvcmQiLCJmYWxsYmFjayIsIk9iamVjdCIsInZhbHVlcyIsImlkZW50aXR5IiwiZ2V0QWxsSW5Qcm9jZXNzVGVhbW1hdGVUYXNrcyIsImZpbHRlciIsImdldFJ1bm5pbmdUZWFtbWF0ZXNTb3J0ZWQiLCJ0Iiwic29ydCIsImEiLCJiIiwiYWdlbnROYW1lIiwibG9jYWxlQ29tcGFyZSJdLCJzb3VyY2VzIjpbIkluUHJvY2Vzc1RlYW1tYXRlVGFzay50c3giXSwic291cmNlc0NvbnRlbnQiOlsiLyoqXG4gKiBJblByb2Nlc3NUZWFtbWF0ZVRhc2sgLSBNYW5hZ2VzIGluLXByb2Nlc3MgdGVhbW1hdGUgbGlmZWN5Y2xlXG4gKlxuICogVGhpcyBjb21wb25lbnQgaW1wbGVtZW50cyB0aGUgVGFzayBpbnRlcmZhY2UgZm9yIGluLXByb2Nlc3MgdGVhbW1hdGVzLlxuICogVW5saWtlIExvY2FsQWdlbnRUYXNrIChiYWNrZ3JvdW5kIGFnZW50cyksIGluLXByb2Nlc3MgdGVhbW1hdGVzOlxuICogMS4gUnVuIGluIHRoZSBzYW1lIE5vZGUuanMgcHJvY2VzcyB1c2luZyBBc3luY0xvY2FsU3RvcmFnZSBmb3IgaXNvbGF0aW9uXG4gKiAyLiBIYXZlIHRlYW0tYXdhcmUgaWRlbnRpdHkgKGFnZW50TmFtZUB0ZWFtTmFtZSlcbiAqIDMuIFN1cHBvcnQgcGxhbiBtb2RlIGFwcHJvdmFsIGZsb3dcbiAqIDQuIENhbiBiZSBpZGxlICh3YWl0aW5nIGZvciB3b3JrKSBvciBhY3RpdmUgKHByb2Nlc3NpbmcpXG4gKi9cblxuaW1wb3J0IHtcbiAgaXNUZXJtaW5hbFRhc2tTdGF0dXMsXG4gIHR5cGUgU2V0QXBwU3RhdGUsXG4gIHR5cGUgVGFzayxcbiAgdHlwZSBUYXNrU3RhdGVCYXNlLFxufSBmcm9tICcuLi8uLi9UYXNrLmpzJ1xuaW1wb3J0IHR5cGUgeyBNZXNzYWdlIH0gZnJvbSAnLi4vLi4vdHlwZXMvbWVzc2FnZS5qcydcbmltcG9ydCB7IGxvZ0ZvckRlYnVnZ2luZyB9IGZyb20gJy4uLy4uL3V0aWxzL2RlYnVnLmpzJ1xuaW1wb3J0IHsgY3JlYXRlVXNlck1lc3NhZ2UgfSBmcm9tICcuLi8uLi91dGlscy9tZXNzYWdlcy5qcydcbmltcG9ydCB7IGtpbGxJblByb2Nlc3NUZWFtbWF0ZSB9IGZyb20gJy4uLy4uL3V0aWxzL3N3YXJtL3NwYXduSW5Qcm9jZXNzLmpzJ1xuaW1wb3J0IHsgdXBkYXRlVGFza1N0YXRlIH0gZnJvbSAnLi4vLi4vdXRpbHMvdGFzay9mcmFtZXdvcmsuanMnXG5pbXBvcnQgdHlwZSB7IEluUHJvY2Vzc1RlYW1tYXRlVGFza1N0YXRlIH0gZnJvbSAnLi90eXBlcy5qcydcbmltcG9ydCB7IGFwcGVuZENhcHBlZE1lc3NhZ2UsIGlzSW5Qcm9jZXNzVGVhbW1hdGVUYXNrIH0gZnJvbSAnLi90eXBlcy5qcydcblxuLyoqXG4gKiBJblByb2Nlc3NUZWFtbWF0ZVRhc2sgLSBIYW5kbGVzIGluLXByb2Nlc3MgdGVhbW1hdGUgZXhlY3V0aW9uLlxuICovXG5leHBvcnQgY29uc3QgSW5Qcm9jZXNzVGVhbW1hdGVUYXNrOiBUYXNrID0ge1xuICBuYW1lOiAnSW5Qcm9jZXNzVGVhbW1hdGVUYXNrJyxcbiAgdHlwZTogJ2luX3Byb2Nlc3NfdGVhbW1hdGUnLFxuICBhc3luYyBraWxsKHRhc2tJZCwgc2V0QXBwU3RhdGUpIHtcbiAgICBraWxsSW5Qcm9jZXNzVGVhbW1hdGUodGFza0lkLCBzZXRBcHBTdGF0ZSlcbiAgfSxcbn1cblxuLyoqXG4gKiBSZXF1ZXN0IHNodXRkb3duIGZvciBhIHRlYW1tYXRlLlxuICovXG5leHBvcnQgZnVuY3Rpb24gcmVxdWVzdFRlYW1tYXRlU2h1dGRvd24oXG4gIHRhc2tJZDogc3RyaW5nLFxuICBzZXRBcHBTdGF0ZTogU2V0QXBwU3RhdGUsXG4pOiB2b2lkIHtcbiAgdXBkYXRlVGFza1N0YXRlPEluUHJvY2Vzc1RlYW1tYXRlVGFza1N0YXRlPih0YXNrSWQsIHNldEFwcFN0YXRlLCB0YXNrID0+IHtcbiAgICBpZiAodGFzay5zdGF0dXMgIT09ICdydW5uaW5nJyB8fCB0YXNrLnNodXRkb3duUmVxdWVzdGVkKSB7XG4gICAgICByZXR1cm4gdGFza1xuICAgIH1cblxuICAgIHJldHVybiB7XG4gICAgICAuLi50YXNrLFxuICAgICAgc2h1dGRvd25SZXF1ZXN0ZWQ6IHRydWUsXG4gICAgfVxuICB9KVxufVxuXG4vKipcbiAqIEFwcGVuZCBhIG1lc3NhZ2UgdG8gYSB0ZWFtbWF0ZSdzIGNvbnZlcnNhdGlvbiBoaXN0b3J5LlxuICogVXNlZCBmb3Igem9vbWVkIHZpZXcgdG8gc2hvdyB0aGUgdGVhbW1hdGUncyBjb252ZXJzYXRpb24uXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBhcHBlbmRUZWFtbWF0ZU1lc3NhZ2UoXG4gIHRhc2tJZDogc3RyaW5nLFxuICBtZXNzYWdlOiBNZXNzYWdlLFxuICBzZXRBcHBTdGF0ZTogU2V0QXBwU3RhdGUsXG4pOiB2b2lkIHtcbiAgdXBkYXRlVGFza1N0YXRlPEluUHJvY2Vzc1RlYW1tYXRlVGFza1N0YXRlPih0YXNrSWQsIHNldEFwcFN0YXRlLCB0YXNrID0+IHtcbiAgICBpZiAodGFzay5zdGF0dXMgIT09ICdydW5uaW5nJykge1xuICAgICAgcmV0dXJuIHRhc2tcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgLi4udGFzayxcbiAgICAgIG1lc3NhZ2VzOiBhcHBlbmRDYXBwZWRNZXNzYWdlKHRhc2subWVzc2FnZXMsIG1lc3NhZ2UpLFxuICAgIH1cbiAgfSlcbn1cblxuLyoqXG4gKiBJbmplY3QgYSB1c2VyIG1lc3NhZ2UgdG8gYSB0ZWFtbWF0ZSdzIHBlbmRpbmcgcXVldWUuXG4gKiBVc2VkIHdoZW4gdmlld2luZyBhIHRlYW1tYXRlJ3MgdHJhbnNjcmlwdCB0byBzZW5kIHR5cGVkIG1lc3NhZ2VzIHRvIHRoZW0uXG4gKiBBbHNvIGFkZHMgdGhlIG1lc3NhZ2UgdG8gdGFzay5tZXNzYWdlcyBzbyBpdCBhcHBlYXJzIGltbWVkaWF0ZWx5IGluIHRoZSB0cmFuc2NyaXB0LlxuICovXG5leHBvcnQgZnVuY3Rpb24gaW5qZWN0VXNlck1lc3NhZ2VUb1RlYW1tYXRlKFxuICB0YXNrSWQ6IHN0cmluZyxcbiAgbWVzc2FnZTogc3RyaW5nLFxuICBzZXRBcHBTdGF0ZTogU2V0QXBwU3RhdGUsXG4pOiB2b2lkIHtcbiAgdXBkYXRlVGFza1N0YXRlPEluUHJvY2Vzc1RlYW1tYXRlVGFza1N0YXRlPih0YXNrSWQsIHNldEFwcFN0YXRlLCB0YXNrID0+IHtcbiAgICAvLyBBbGxvdyBtZXNzYWdlIGluamVjdGlvbiB3aGVuIHRlYW1tYXRlIGlzIHJ1bm5pbmcgb3IgaWRsZSAod2FpdGluZyBmb3IgaW5wdXQpXG4gICAgLy8gT25seSByZWplY3QgaWYgdGVhbW1hdGUgaXMgaW4gYSB0ZXJtaW5hbCBzdGF0ZVxuICAgIGlmIChpc1Rlcm1pbmFsVGFza1N0YXR1cyh0YXNrLnN0YXR1cykpIHtcbiAgICAgIGxvZ0ZvckRlYnVnZ2luZyhcbiAgICAgICAgYERyb3BwaW5nIG1lc3NhZ2UgZm9yIHRlYW1tYXRlIHRhc2sgJHt0YXNrSWR9OiB0YXNrIHN0YXR1cyBpcyBcIiR7dGFzay5zdGF0dXN9XCJgLFxuICAgICAgKVxuICAgICAgcmV0dXJuIHRhc2tcbiAgICB9XG5cbiAgICByZXR1cm4ge1xuICAgICAgLi4udGFzayxcbiAgICAgIHBlbmRpbmdVc2VyTWVzc2FnZXM6IFsuLi50YXNrLnBlbmRpbmdVc2VyTWVzc2FnZXMsIG1lc3NhZ2VdLFxuICAgICAgbWVzc2FnZXM6IGFwcGVuZENhcHBlZE1lc3NhZ2UoXG4gICAgICAgIHRhc2subWVzc2FnZXMsXG4gICAgICAgIGNyZWF0ZVVzZXJNZXNzYWdlKHsgY29udGVudDogbWVzc2FnZSB9KSxcbiAgICAgICksXG4gICAgfVxuICB9KVxufVxuXG4vKipcbiAqIEdldCB0ZWFtbWF0ZSB0YXNrIGJ5IGFnZW50IElEIGZyb20gQXBwU3RhdGUuXG4gKiBQcmVmZXJzIHJ1bm5pbmcgdGFza3Mgb3ZlciBraWxsZWQvY29tcGxldGVkIG9uZXMgaW4gY2FzZSBtdWx0aXBsZSB0YXNrc1xuICogd2l0aCB0aGUgc2FtZSBhZ2VudElkIGV4aXN0LlxuICogUmV0dXJucyB1bmRlZmluZWQgaWYgbm90IGZvdW5kLlxuICovXG5leHBvcnQgZnVuY3Rpb24gZmluZFRlYW1tYXRlVGFza0J5QWdlbnRJZChcbiAgYWdlbnRJZDogc3RyaW5nLFxuICB0YXNrczogUmVjb3JkPHN0cmluZywgVGFza1N0YXRlQmFzZT4sXG4pOiBJblByb2Nlc3NUZWFtbWF0ZVRhc2tTdGF0ZSB8IHVuZGVmaW5lZCB7XG4gIGxldCBmYWxsYmFjazogSW5Qcm9jZXNzVGVhbW1hdGVUYXNrU3RhdGUgfCB1bmRlZmluZWRcbiAgZm9yIChjb25zdCB0YXNrIG9mIE9iamVjdC52YWx1ZXModGFza3MpKSB7XG4gICAgaWYgKGlzSW5Qcm9jZXNzVGVhbW1hdGVUYXNrKHRhc2spICYmIHRhc2suaWRlbnRpdHkuYWdlbnRJZCA9PT0gYWdlbnRJZCkge1xuICAgICAgLy8gUHJlZmVyIHJ1bm5pbmcgdGFza3MgaW4gY2FzZSBvbGQga2lsbGVkIHRhc2tzIHN0aWxsIGV4aXN0IGluIEFwcFN0YXRlXG4gICAgICAvLyBhbG9uZ3NpZGUgbmV3IHJ1bm5pbmcgb25lcyB3aXRoIHRoZSBzYW1lIGFnZW50SWRcbiAgICAgIGlmICh0YXNrLnN0YXR1cyA9PT0gJ3J1bm5pbmcnKSB7XG4gICAgICAgIHJldHVybiB0YXNrXG4gICAgICB9XG4gICAgICAvLyBLZWVwIGZpcnN0IG1hdGNoIGFzIGZhbGxiYWNrIGluIGNhc2Ugbm8gcnVubmluZyB0YXNrIGV4aXN0c1xuICAgICAgaWYgKCFmYWxsYmFjaykge1xuICAgICAgICBmYWxsYmFjayA9IHRhc2tcbiAgICAgIH1cbiAgICB9XG4gIH1cbiAgcmV0dXJuIGZhbGxiYWNrXG59XG5cbi8qKlxuICogR2V0IGFsbCBpbi1wcm9jZXNzIHRlYW1tYXRlIHRhc2tzIGZyb20gQXBwU3RhdGUuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRBbGxJblByb2Nlc3NUZWFtbWF0ZVRhc2tzKFxuICB0YXNrczogUmVjb3JkPHN0cmluZywgVGFza1N0YXRlQmFzZT4sXG4pOiBJblByb2Nlc3NUZWFtbWF0ZVRhc2tTdGF0ZVtdIHtcbiAgcmV0dXJuIE9iamVjdC52YWx1ZXModGFza3MpLmZpbHRlcihpc0luUHJvY2Vzc1RlYW1tYXRlVGFzaylcbn1cblxuLyoqXG4gKiBHZXQgcnVubmluZyBpbi1wcm9jZXNzIHRlYW1tYXRlcyBzb3J0ZWQgYWxwaGFiZXRpY2FsbHkgYnkgYWdlbnROYW1lLlxuICogU2hhcmVkIGJldHdlZW4gVGVhbW1hdGVTcGlubmVyVHJlZSBkaXNwbGF5LCBQcm9tcHRJbnB1dCBmb290ZXIgc2VsZWN0b3IsXG4gKiBhbmQgdXNlQmFja2dyb3VuZFRhc2tOYXZpZ2F0aW9uIOKAlCBzZWxlY3RlZElQQWdlbnRJbmRleCBtYXBzIGludG8gdGhpc1xuICogYXJyYXksIHNvIGFsbCB0aHJlZSBtdXN0IGFncmVlIG9uIHNvcnQgb3JkZXIuXG4gKi9cbmV4cG9ydCBmdW5jdGlvbiBnZXRSdW5uaW5nVGVhbW1hdGVzU29ydGVkKFxuICB0YXNrczogUmVjb3JkPHN0cmluZywgVGFza1N0YXRlQmFzZT4sXG4pOiBJblByb2Nlc3NUZWFtbWF0ZVRhc2tTdGF0ZVtdIHtcbiAgcmV0dXJuIGdldEFsbEluUHJvY2Vzc1RlYW1tYXRlVGFza3ModGFza3MpXG4gICAgLmZpbHRlcih0ID0+IHQuc3RhdHVzID09PSAncnVubmluZycpXG4gICAgLnNvcnQoKGEsIGIpID0+IGEuaWRlbnRpdHkuYWdlbnROYW1lLmxvY2FsZUNvbXBhcmUoYi5pZGVudGl0eS5hZ2VudE5hbWUpKVxufVxuIl0sIm1hcHBpbmdzIjoiQUFBQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxTQUNFQSxvQkFBb0IsRUFDcEIsS0FBS0MsV0FBVyxFQUNoQixLQUFLQyxJQUFJLEVBQ1QsS0FBS0MsYUFBYSxRQUNiLGVBQWU7QUFDdEIsY0FBY0MsT0FBTyxRQUFRLHdCQUF3QjtBQUNyRCxTQUFTQyxlQUFlLFFBQVEsc0JBQXNCO0FBQ3RELFNBQVNDLGlCQUFpQixRQUFRLHlCQUF5QjtBQUMzRCxTQUFTQyxxQkFBcUIsUUFBUSxxQ0FBcUM7QUFDM0UsU0FBU0MsZUFBZSxRQUFRLCtCQUErQjtBQUMvRCxjQUFjQywwQkFBMEIsUUFBUSxZQUFZO0FBQzVELFNBQVNDLG1CQUFtQixFQUFFQyx1QkFBdUIsUUFBUSxZQUFZOztBQUV6RTtBQUNBO0FBQ0E7QUFDQSxPQUFPLE1BQU1DLHFCQUFxQixFQUFFVixJQUFJLEdBQUc7RUFDekNXLElBQUksRUFBRSx1QkFBdUI7RUFDN0JDLElBQUksRUFBRSxxQkFBcUI7RUFDM0IsTUFBTUMsSUFBSUEsQ0FBQ0MsTUFBTSxFQUFFQyxXQUFXLEVBQUU7SUFDOUJWLHFCQUFxQixDQUFDUyxNQUFNLEVBQUVDLFdBQVcsQ0FBQztFQUM1QztBQUNGLENBQUM7O0FBRUQ7QUFD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