/**
 * Второй уровень осознания (conscience_level_2).
 * На втором уровне при обработке определяется цель — целевое или пассивное продолжение.
 *
 * Вызывается из stimuls_consciousness, когда первый уровень не смог подобрать
 * приемлемое автоматическое действие (ни по ЭП, ни по штатному автоматизму)
 * и установлена нерешённая проблема unsolved_problem = актуальный стимул.
 * Это переход ко второму уровню в целевом режиме (проблема с автоматизмом).
 *
 * Целевой режим, итерация второго уровня:
 *   — Сначала обрабатывается ответ оператора (isOperatorAnswerReceived), если есть.
 *   — Затем определение цели и действий: ветка «выживания» (criticalVital / shockState / suddenChange /
 *     isShockState) — Goals_findBestLifeGoalPositiveUsefulness, цепочка EpisodicMemory_findChainForGoalStack
 *     при usedRule, иначе поиск ExactRule / InaccurateRule по ЭП и запуск автоматизма или отложенный ур.3;
 *     без выживания — Goals_determineGoalInSituation (в т.ч. умолчательная цель «значение» при значимости
 *     стимула >3 и отсутствии цели по ветке), затем при currentGoalType === 'meaning' — прогноз по кадрам ЭП,
 *     Gestalt_bumpFromMeaningForecast, AbstractImages_boostSemanticRulesForAction; флаги isPlayMode / isLearningMode для ур.3.
 *   — В ходе поиска в эпизодической памяти новые образы цели накапливаются через Goals_* и ЭП.
 *
 * Ответ оператора на реакцию после автоматизма (ветка «ожидание обратной связи по кадру ЭП»):
 *   • В инфокартине поле isOperatorAnswerReceived (и связанные детекторы) поднимается при закрытии
 *     кадра ответным стимулом — см. EpisodicMemory (closeLastFrame / ориентировочный рефлекс в ожидании).
 *   • Объект цикла осмысления той же сессии получает automatismOperatorAnswerReceived и
 *     awaitingAutomatismEpisodeFeedback; пока цикл «ждёт», диспетчер крутит runOneStep по пульсам.
   *   • Первый исполняемый блок conscience_level_2 (не после постановки цели) обрабатывает этот ответ:
   *     по effect закрытого кадра — базовая корректировка usefulness цели цикла (cycle.goalImageId),
   *     затем доработка по детекторам; завершение цикла ожидания (expired).
 *   • Вызов из runOneStep передаёт _operatorAnswerCycleOnly: тогда после блока ответа выходим,
 *     не смешивая с постановкой ImportantProblem/целей в том же тике.
 *
 * @param {Object} context
 *   ожидаются как минимум:
 *   - actualId  — FinalImageId актуального стимула;
 *   - branchId  — текущий терминальный узел дерева восприятия;
 *   - well      — базовое состояние (1/2/3);
 *   - emotionId — текущая эмоция;
 *   - activityID — FinalImageId для условий (как в дереве);
 *   - sourceCycle — объект цикла осмысления (для ветки ответа на автоматизм);
 *   - _operatorAnswerCycleOnly — если true: только блок ответа оператора и выход (вызов из runOneStep).
 *   а также любые поля исходного контекста ОР.
 */
(function (global) {
  'use strict';

  var puls = function () { return typeof global.cpuls === 'number' ? global.cpuls : 0; };

  /** Лог цикла ФО: главный цикл или force (например, перед expired). */
  function appendCycleLog(cycle, text, force) {
    if (!cycle || typeof global.Consciousness_cycleAppendLog !== 'function') return;
    if (!force && !cycle.isMainCycle) return;
    global.Consciousness_cycleAppendLog(cycle, text, puls(), force ? { force: true } : undefined);
  }

  /**
   * Однократная обработка ответа оператора на автоматизм (уровень 2, «смысловая» реакция на фидбек).
   *
   * Смысл: по закрытому кадру ЭП уже известен effect; в инфокартине выставлены детекторы ситуации.
   * Сначала при числовом effect: AbstractImages_applyEpisodeEffectToSemanticContextAbstractions — усреднение meanImportance
   * клонов восприятия/действия при совпадающем кадре semanticStructure (effect>0 усиливает, effect<0 ослабляет).
   * При effect > 0 дополнительно: MentalAutomatizms_tryCreateFromSemanticStructureRule — ментальный автоматизм;
   * оба шага даже при goalImageId === 0 (ур.3).
   * Далее в образ цели цикла (cycle.goalImageId) записывается итог ожидания: Goals_applyWaitingPeriodEffectToGoalUsefulness
   * (при count цели === 1 usefulness = effect, иначе усреднение). Затем — корректировки через Goals_mergeUsefulnessSampleForGoalId.
   *
   * Защита от повторного входа: operatorAnswerPipelineConsumed — иначе при нескольких пульсах
   * или двойном вызове usefulness удвоился бы.
   *
   * Детекторы (компоненты ситуации, situatioms_tree.js):
   *   negativeMotorEffect (6), positiveActionEffect (26), lowAutomatismUsefulness (32).
   */
  function conscience_level_2_operatorAnswerPipeline(cycle) {
    if (!cycle) return;
    if (cycle.operatorAnswerPipelineConsumed) return;
    cycle.operatorAnswerPipelineConsumed = true;
    var goalId = typeof cycle.goalImageId === 'number' ? cycle.goalImageId : 0;
    var info = (typeof global.Info_initIfNeeded === 'function')
      ? global.Info_initIfNeeded()
      : global.InfoPicture;
    if (!info) return;

    var episodeEffect = cycle.lastAutomatismEpisodeEffect;
    if ((episodeEffect === null || episodeEffect === undefined) &&
        typeof global.EpisodicMemory_lastClosedFrameEffect === 'number') {
      episodeEffect = global.EpisodicMemory_lastClosedFrameEffect;
    }

    var effIsNum = (typeof episodeEffect === 'number' && !isNaN(episodeEffect));
    if (effIsNum) {
      var tcx = cycle.context || {};
      var mb = parseInt(tcx.branchId, 10) || 0;
      var mst = parseInt(tcx.actual_stimul_ID, 10) || 0;
      if (!mst && info.stimulus && info.stimulus.finalImageId) {
        mst = info.stimulus.finalImageId;
      }
      var msit = typeof info.situationId === 'number' ? info.situationId : 0;
      if (mb && mst) {
        if (typeof global.AbstractImages_applyEpisodeEffectToSemanticContextAbstractions === 'function') {
          global.AbstractImages_applyEpisodeEffectToSemanticContextAbstractions({
            perceptionNodeId: mb,
            situationId: msit,
            actualStimulusImageId: mst,
            effect: episodeEffect
          });
        }
        if (episodeEffect > 0 && typeof global.MentalAutomatizms_tryCreateFromSemanticStructureRule === 'function') {
          if (global.MentalAutomatizms_tryCreateFromSemanticStructureRule({
            perceptionNodeId: mb,
            situationId: msit,
            actualStimulusImageId: mst,
            episodeEffect: episodeEffect
          })) {
            appendCycleLog(cycle, 'Ур.2: effect>0 — ментальный автоматизм по правилу semanticStructure (клон восприятия).', true);
          }
        }
      }
    }

    if (!goalId) {
      if (typeof global.Info_updateView === 'function') global.Info_updateView();
      return;
    }
    if (effIsNum && typeof global.Goals_applyWaitingPeriodEffectToGoalUsefulness === 'function') {
      global.Goals_applyWaitingPeriodEffectToGoalUsefulness(goalId, episodeEffect);
    }

    if (typeof global.Goals_mergeUsefulnessSampleForGoalId !== 'function') {
      if (typeof global.Info_updateView === 'function') global.Info_updateView();
      return;
    }

    // negativeMotorEffect — компонент 6: снижаем полезность цели.
    if (info.negativeMotorEffect) {
      global.Goals_mergeUsefulnessSampleForGoalId(goalId, -2);
    }
    // positiveActionEffect — компонент 26: усиливаем по величине детектора (ограничение сверху).
    if (info.positiveActionEffect) {
      var pe = typeof info.positiveActionEffect === 'number' ? info.positiveActionEffect : 0;
      if (pe > 0) {
        global.Goals_mergeUsefulnessSampleForGoalId(goalId, Math.min(pe, 3));
      }
    }
    var effZero = !effIsNum || episodeEffect === 0;
    // lowAutomatismUsefulness — компонент 32: при нулевом effect кадра.
    if (effZero && info.lowAutomatismUsefulness) {
      global.Goals_mergeUsefulnessSampleForGoalId(goalId, -1);
    }

    if (typeof global.Info_updateView === 'function') global.Info_updateView();
  }

  /** Закрытие фазы ожидания (снятие флагов, expired). Когда фаза начинается — см. conscious_attention_channel.js, runElementary, ветка automatismStarted. */
  function conscience_level_2_finalizeAutomatismAnswerCycle(cycle) {
    if (!cycle || cycle.automatismAnswerLevel2Done) return;
    if (!cycle.awaitingAutomatismEpisodeFeedback || !cycle.automatismOperatorAnswerReceived) return;
    cycle.automatismAnswerLevel2Done = true;
    cycle.awaitingAutomatismEpisodeFeedback = false;
    cycle.expired = true;
    cycle.isMainCycle = false;
    if (typeof global.Info_setField === 'function') {
      global.Info_setField('isOperatorAnswerReceived', 0);
    } else if (global.InfoPicture) {
      global.InfoPicture.isOperatorAnswerReceived = 0;
    }
    appendCycleLog(cycle, 'Ур.2: ответ на автоматизм обработан, период ожидания завершён — цикл expired.', true);
    if (typeof global.Info_updateView === 'function') global.Info_updateView();
  }

  /** Выживание: шок / резкое ухудшение / критический витал (глобальный isShockState учитывается). */
  function conscience_level_2_isSurvivalContext(info) {
    if (!info) return !!global.isShockState;
    return !!(
      global.isShockState ||
      info.shockState ||
      (info.suddenChange && info.suddenChange !== 0) ||
      (info.criticalVital && info.criticalVital !== 0)
    );
  }

  /**
   * Критичность виталов «раньше Стресса» по порядку VitalsArr (Id 1…6 важнее 7-Стресс):
   * либо active criticalVital < 7, либо BadVitalsValue > 70 для любого из Id 1…6.
   */
  /**
   * id образа цели по кадру ЭП для выживания: addOrUpdate при effect &gt; 0; если id=0, но ключ цели полный —
   * повтор с минимальным положительным effect (правило всё равно фиксируется как образ цели цикла).
   */
  function conscience_level_2_survivalLifeGoalIdFromFrame(frame) {
    if (!frame || typeof global.Goals_addOrUpdateLifeGoalFromFrame !== 'function') return 0;
    var gid = global.Goals_addOrUpdateLifeGoalFromFrame(frame);
    if (gid) return gid;
    var pn = frame.perceptionNodeId != null ? frame.perceptionNodeId : 0;
    var ai = frame.actionImageId != null ? frame.actionImageId : 0;
    if (!pn || !ai) return 0;
    var eff = typeof frame.effect === 'number' ? frame.effect : 0;
    if (eff > 0) return 0;
    var f2 = {
      perceptionNodeId: pn,
      stimulusImageId: frame.stimulusImageId != null ? frame.stimulusImageId : 0,
      actionImageId: ai,
      situationId: frame.situationId != null ? frame.situationId : 0,
      effect: 1,
      usefulness: frame.usefulness
    };
    return global.Goals_addOrUpdateLifeGoalFromFrame(f2) || 0;
  }

  /**
   * Цель «что для меня означает это?»: прогноз последствий по кадрам ЭП, усиление гештальта и абстракций с правилом по ОД.
   */
  function conscience_level_2_tryMeaningGoalForecast(context, info, cycle) {
    var meaningId = typeof global.Goals_getDefaultMeaningGoalId === 'function'
      ? global.Goals_getDefaultMeaningGoalId() : 0;
    if (!meaningId) return;
    var branchId = context.branchId != null ? context.branchId : 0;
    var situationId = info && typeof info.situationId === 'number' ? info.situationId : 0;
    var stimId = context.actualId != null ? context.actualId : 0;
    if (!stimId && info && info.stimulus && info.stimulus.finalImageId) {
      stimId = info.stimulus.finalImageId;
    }
    stimId = parseInt(stimId, 10) || 0;

    var frames = [];
    if (typeof global.EpisodicMemory_getFramesByStimulus === 'function' && stimId) {
      frames = global.EpisodicMemory_getFramesByStimulus(stimId);
    }
    if ((!frames || !frames.length) && branchId && typeof global.EpisodicMemory_getFramesByPerceptionAndSituation === 'function') {
      frames = global.EpisodicMemory_getFramesByPerceptionAndSituation(branchId, situationId);
    }
    if (!frames || !frames.length) {
      if (cycle) appendCycleLog(cycle, 'Ур.2 (цель «значение»): прогноз по ЭП — нет кадров последствий.', false);
      return;
    }

    var candidates = [];
    for (var i = 0; i < frames.length; i++) {
      var f = frames[i];
      if (!f) continue;
      var aid = f.actionImageId != null ? parseInt(f.actionImageId, 10) : 0;
      if (!aid) continue;
      candidates.push(f);
    }
    candidates.sort(function (a, b) {
      return (typeof b.effect === 'number' ? b.effect : 0) - (typeof a.effect === 'number' ? a.effect : 0);
    });
    var taken = candidates.slice(0, 5);
    if (cycle) {
      appendCycleLog(cycle, 'Ур.2 (цель «значение»): прогноз по кадрам ЭП — ' + String(taken.length) + ' правил(а) с ОД.', false);
    }

    for (var j = 0; j < taken.length; j++) {
      var fr = taken[j];
      var actionId = fr.actionImageId != null ? parseInt(fr.actionImageId, 10) : 0;
      if (!actionId) continue;
      var eff = typeof fr.effect === 'number' ? fr.effect : 0;
      if (typeof global.Gestalt_bumpFromMeaningForecast === 'function') {
        global.Gestalt_bumpFromMeaningForecast(meaningId, actionId, fr, cycle);
      }
      if (typeof global.AbstractImages_boostSemanticRulesForAction === 'function') {
        var delta = eff > 0 ? Math.min(3, Math.max(0.5, eff / 3)) : 0.5;
        global.AbstractImages_boostSemanticRulesForAction(actionId, delta);
      }
    }
  }

  function conscience_level_2_hasCriticalVitalBeforeStress(info) {
    var cv = info && info.criticalVital ? parseInt(info.criticalVital, 10) : 0;
    if (cv > 0 && cv < 7) return true;
    var bad = global.BadVitalsValue;
    var vitals = global.VitalsArr;
    if (!bad || !Array.isArray(vitals)) return false;
    var TH = 70;
    for (var i = 0; i < vitals.length; i++) {
      if (vitals[i].Id >= 7) break;
      var k = bad[vitals[i].Id] || bad[String(vitals[i].Id)] || 0;
      if (k > TH) return true;
    }
    return false;
  }

  /**
   * Запуск автоматизма из ур.2 (выживание). Включает ту же фазу ожидания кадра ЭП, что и при запуске
   * автоматизма с ур.1 (info_automatismWithEpisodeFeedbackAndExpireCycle в infofunctions.js).
   * Завершение фазы после ответа оператора — conscience_level_2_finalizeAutomatismAnswerCycle.
   */
  function conscience_level_2_startSurvivalAutomatism(cycle, branchId, actionImageId, goalId) {
    if (!cycle || !branchId || !actionImageId) return false;
    var automatizmId = 0;
    if (typeof global.Automatizms_createAutomatizm === 'function') {
      automatizmId = global.Automatizms_createAutomatizm(branchId, actionImageId);
      if (automatizmId && typeof global.Automatizms_setDefaultAutomatizm === 'function') {
        global.Automatizms_setDefaultAutomatizm(automatizmId);
      }
    }
    if (typeof global.info_automatismWithEpisodeFeedbackAndExpireCycle === 'function') {
      return global.info_automatismWithEpisodeFeedbackAndExpireCycle(
        cycle,
        branchId,
        actionImageId,
        automatizmId,
        goalId || 0,
        'Ур.2 (выживание): запущен автоматизм по цели/правилу, ожидание ЭП.'
      );
    }
    return false;
  }

  /**
   * Режим выживания: определение цели/правила и действие по условному алгоритму.
   * Цепочка по Target_rule_stack — как партия в шахматах: ход → ответ → следующий ход опирается на цепочку
   * уже произошедших в ЭП правил (необязательно соседние кадры; порядок фиксируется в ленте Episodes).
   */
  function conscience_level_2_trySurvivalGoalAndRules(context, info, cycle) {
    var branchId = context.branchId != null ? context.branchId : 0;
    var situationId = info && typeof info.situationId === 'number' ? info.situationId : 0;
    var stimulusImageId = context.actualId != null ? context.actualId : 0;
    if (!stimulusImageId && info && info.stimulus && info.stimulus.finalImageId) {
      stimulusImageId = info.stimulus.finalImageId;
    }
    if (!branchId || !cycle) return;

    appendCycleLog(cycle, 'Ур.2 выживание: анализ цели и правил ЭП (ветка perception/stimulus/situation).');

    var lifeGoal = typeof global.Goals_findBestLifeGoalPositiveUsefulness === 'function'
      ? global.Goals_findBestLifeGoalPositiveUsefulness(branchId, situationId) : null;
    if (lifeGoal && lifeGoal.actionImageId) {
      appendCycleLog(cycle, 'Ур.2 выживание: образ цели с положительной usefulness, goalId=' + String(lifeGoal.id) + ', действие ОД=' + String(lifeGoal.actionImageId) + '.');
      if (lifeGoal.id && typeof global.TargetRuleStack_push === 'function') global.TargetRuleStack_push(lifeGoal.id);
      if (typeof global.Info_setField === 'function') global.Info_setField('usedRule', lifeGoal.id || 0);
      if (typeof global.Goals_setCurrentGoalInInfoPicture === 'function') {
        global.Goals_setCurrentGoalInInfoPicture('life', lifeGoal.id, 0);
      }
      conscience_level_2_startSurvivalAutomatism(cycle, branchId, lifeGoal.actionImageId, lifeGoal.id);
      return;
    }

    var usedRuleVal = info && typeof info.usedRule === 'number' ? info.usedRule : (parseInt(info.usedRule, 10) || 0);
    if (usedRuleVal && Array.isArray(global.Target_rule_stack) && global.Target_rule_stack.length &&
        typeof global.EpisodicMemory_findChainForGoalStack === 'function') {
      var chainGoals = global.Target_rule_stack.slice();
      var chainFrames = global.EpisodicMemory_findChainForGoalStack(chainGoals);
      if (chainFrames && chainFrames.length) {
        appendCycleLog(cycle, 'Ур.2 выживание: найдена цепочка кадров ЭП по Target_rule_stack (' + String(chainFrames.length) + ' звеньев).');
        var lastFr = chainFrames[chainFrames.length - 1];
        var gidChain = conscience_level_2_survivalLifeGoalIdFromFrame(lastFr);
        if (gidChain && lastFr.actionImageId) {
          if (typeof global.TargetRuleStack_push === 'function') global.TargetRuleStack_push(gidChain);
          if (typeof global.Info_setField === 'function') global.Info_setField('usedRule', gidChain);
          if (typeof global.Goals_setCurrentGoalInInfoPicture === 'function') {
            global.Goals_setCurrentGoalInInfoPicture('life', gidChain, 0);
          }
          conscience_level_2_startSurvivalAutomatism(cycle, branchId, lastFr.actionImageId, gidChain);
          return;
        }
      }
      appendCycleLog(cycle, 'Ур.2 выживание: цепочка по usedRule/Target_rule_stack не собрана — переход к поиску одиночного правила.');
    }

    if (typeof global.TargetRuleStack_clear === 'function') global.TargetRuleStack_clear();
    if (typeof global.Info_setField === 'function') global.Info_setField('usedRule', 0);
    appendCycleLog(cycle, 'Ур.2 выживание: сброшены Target_rule_stack и usedRule; выборка ExactRule/InaccurateRule из ЭП.');

    var ruleBundle = typeof global.info_bestEpisodicRule === 'function'
      ? global.info_bestEpisodicRule(branchId, situationId, {
        filterByStimulus: false,
        stimulusImageId: stimulusImageId,
        allowStimulusFallback: true
      })
      : null;
    var bestFrame = ruleBundle && ruleBundle.frame ? ruleBundle.frame : null;
    var exactRule = !!(ruleBundle && ruleBundle.exactRule);

    var inaccurate = !!bestFrame && !exactRule;

    function applyFromFrame(frame) {
      var gid = conscience_level_2_survivalLifeGoalIdFromFrame(frame);
      if (!gid || !frame.actionImageId) return false;
      if (typeof global.TargetRuleStack_push === 'function') global.TargetRuleStack_push(gid);
      if (typeof global.Info_setField === 'function') global.Info_setField('usedRule', gid);
      if (typeof global.Goals_setCurrentGoalInInfoPicture === 'function') {
        global.Goals_setCurrentGoalInInfoPicture('life', gid, 0);
      }
      conscience_level_2_startSurvivalAutomatism(cycle, branchId, frame.actionImageId, gid);
      return true;
    }

    if (exactRule && bestFrame) {
      appendCycleLog(cycle, 'Ур.2 выживание: ExactRule — лучший кадр ЭП (ветка+ситуация), effect=' + String(bestFrame.effect) + '.');
      applyFromFrame(bestFrame);
      return;
    }
    if (inaccurate && conscience_level_2_hasCriticalVitalBeforeStress(info) && bestFrame) {
      appendCycleLog(cycle, 'Ур.2 выживание: InaccurateRule при крит. виталах до Стресса — запуск действия по кадру ЭП, effect=' + String(bestFrame.effect) + '.');
      applyFromFrame(bestFrame);
      return;
    }
    if (inaccurate && bestFrame && !conscience_level_2_hasCriticalVitalBeforeStress(info)) {
      appendCycleLog(cycle, 'Ур.2 выживание: InaccurateRule без срочных виталов — без опрометчивого действия, запланирован ур.3.');
      cycle.survivalDeferredLevel3Planning = true;
      return;
    }

    if (!bestFrame) {
      var cryOd = 0;
      var cryGoalId = 0;
      var critV = info && info.criticalVital ? parseInt(info.criticalVital, 10) : 0;
      if (critV && critV !== 0) {
        if (typeof global.Goals_ensureDefaultCryGoal === 'function') {
          global.Goals_ensureDefaultCryGoal();
        }
        if (typeof global.Goals_getDefaultCryGoalId === 'function') {
          cryGoalId = global.Goals_getDefaultCryGoalId();
        }
        if (cryGoalId && typeof global.Goals_getGoalById === 'function') {
          var cryG = global.Goals_getGoalById(cryGoalId);
          if (cryG && cryG.actionImageId) {
            cryOd = cryG.actionImageId;
          }
        }
      }
      if (!cryOd && typeof global.ActionImages_getOrCreateActionImageId === 'function') {
        cryOd = global.ActionImages_getOrCreateActionImageId(stimulusImageId || 0, [1], '');
      }
      if (cryOd) {
        if (typeof global.Info_setField === 'function') global.Info_setField('usedRule', 0);
        if (cryGoalId && typeof global.Goals_setCurrentGoalInInfoPicture === 'function') {
          global.Goals_setCurrentGoalInInfoPicture('life', cryGoalId, 0);
        }
        appendCycleLog(
          cycle,
          cryGoalId
            ? 'Ур.2 выживание: критический витал, других целей нет — умолчательная цель «Плачь», автоматизм по ОД.'
            : 'Ур.2 выживание: позитивных правил в ЭП нет — автоматизм «Плачет» (ОД), затем ур.3.',
          !!cryGoalId
        );
        conscience_level_2_startSurvivalAutomatism(cycle, branchId, cryOd, cryGoalId || 0);
      } else {
        appendCycleLog(cycle, 'Ур.2 выживание: нет правил в ЭП и не удалось создать ОД «Плачет» — только ур.3.', true);
      }
      cycle.survivalCryLevel3Pending = true;
    }
  }

  function conscience_level_2(context) {
    if (!context) return;

    var info = (typeof global.Info_initIfNeeded === 'function')
      ? global.Info_initIfNeeded()
      : global.InfoPicture;

    // ========== Блок isOperatorAnswerReceived (обязательно в начале ур.2) ==========
    // Условие в инфокартине: оператор дал ответный стимул в периоде ожидания ЭП после реакции
    // (значение приходит из эпизодики/ОР; см. также InfoDet_updateOperatorAnswer при снятии).
    //
    // Почему блок здесь, а не после целей/ImportantProblem: ответ оператора — отдельное событие,
    // его нужно учесть до дальнейшей логики второго уровня в том же вызове, если контекст полный;
    // при вызове только из runOneStep флаг _operatorAnswerCycleOnly ограничивает работу этим блоком.
    //
    // Привязка к циклу:
    //   • Надёжный путь — context.sourceCycle (так передаёт runOneStep для того же цикла, который ждал).
    //   • Запасной — EpisodicMemory_waitingAutomatismCycleId + Consciousness_getCycle: имеет смысл,
    //     если id ещё не обнулён в closeLastFrame; после закрытия кадра с ответом id часто сбрасывается,
    //     поэтому основной контракт — всегда передавать sourceCycle при известном цикле.
    //
    // Доп.ограничения на цикл: и awaitingAutomatismEpisodeFeedback (мы в ветке «ждём кадр»),
    // и automatismOperatorAnswerReceived (эпизодика отметила ответ на этом цикле). Иначе поднятый
    // isOperatorAnswerReceived мог бы относиться к другой сессии — не трогаем чужой цикл.
    if (info && info.isOperatorAnswerReceived) {
      var opCycle = context.sourceCycle;
      if (!opCycle && typeof global.EpisodicMemory_waitingAutomatismCycleId === 'number' && global.EpisodicMemory_waitingAutomatismCycleId &&
          typeof global.Consciousness_getCycle === 'function') {
        opCycle = global.Consciousness_getCycle(global.EpisodicMemory_waitingAutomatismCycleId);
      }
      if (opCycle && opCycle.awaitingAutomatismEpisodeFeedback && opCycle.automatismOperatorAnswerReceived) {
        // Сначала смысловые поправки цели (один раз), затем архивирование состояния цикла ожидания.
        if (!opCycle.operatorAnswerPipelineConsumed) {
          conscience_level_2_operatorAnswerPipeline(opCycle);
        }
        conscience_level_2_finalizeAutomatismAnswerCycle(opCycle);
      }
      // Режим диспетчера: один тик — только ответ на автоматизм, без дальнейшего тела conscience_level_2.
      if (context._operatorAnswerCycleOnly) return;
    }

    if (info) {
      info.ImportantProblem = (info.criticalVital && info.criticalVital !== 0) ? 1 : 0;
    }

    // --- Определение цели и действия (после обработки ответа оператора): см. ветку выживания / режимы ---
    var cycle = context.sourceCycle;
    var survival = conscience_level_2_isSurvivalContext(info);
    if (survival && cycle) {
      appendCycleLog(cycle, 'Ур.2: активен контекст выживания (крит. витал / шок / резкое ухудшение / isShockState).');
      conscience_level_2_trySurvivalGoalAndRules(context, info, cycle);
    } else if (info) {
      // Не выживание: неудовлетворённость — флаг isDissatisfaction / блок ур.3 в runOneStep;
      // игровой и обучающий режимы — явные флаги для ур.3 (синхрон с playMode / learningMode).
      info.isPlayMode = info.playMode ? 1 : 0;
      info.isLearningMode = info.learningMode ? 1 : 0;
      if (cycle) {
        appendCycleLog(cycle, 'Ур.2: не выживание — Goals_determineGoalInSituation, isPlayMode=' + String(info.isPlayMode) + ', isLearningMode=' + String(info.isLearningMode) + '.');
      }
      if (typeof global.Goals_determineGoalInSituation === 'function') {
        global.Goals_determineGoalInSituation(context);
      }
      if (cycle && info && (
        info.currentGoalType === 'meaning' ||
        (typeof global.Goals_isDefaultMeaningGoalId === 'function' && global.Goals_isDefaultMeaningGoalId(info.currentGoalId))
      )) {
        conscience_level_2_tryMeaningGoalForecast(context, info, cycle);
      }
      if (typeof global.Info_updateView === 'function') global.Info_updateView();
    }
  }

  global.conscience_level_2 = conscience_level_2;
  global.conscience_level_2_operatorAnswerPipeline = conscience_level_2_operatorAnswerPipeline;
})(window);
