'use strict';
/**
 * Loop-prompt discipline -- borrowed from OpenClaw's heartbeat pattern.
 *
 * Loop ticks (initiative, periodic reflection, autonomous decision-making)
 * are not chat. Without explicit framing, the LLM may treat the prompt as a
 * conversation continuation and "remember" tasks the user mentioned in some
 * earlier session that no longer apply. OpenClaw's heartbeat prompt
 * (src/auto-reply/heartbeat.ts:13-16) explicitly tells the model:
 *
 *   "Read HEARTBEAT.md if it exists (workspace context). Follow it strictly.
 *    Do not infer or repeat old tasks from prior chats. If nothing needs
 *    attention, reply HEARTBEAT_OK."
 *
 * Two ideas worth porting:
 *   1. Explicit "loop tick, not conversation" framing.
 *   2. A short fast-exit token the model can return when there's no signal.
 *
 * This module emits a small header you prepend to the loop's system prompt.
 * It does NOT change the LLM call shape -- callers still build their own
 * structured response format.
 */

/**
 * Build a discipline header for a loop's prompt.
 *
 * @param {object} opts
 * @param {string} opts.loopName       - Short loop name, e.g. "initiative",
 *                                       "reflect", "skill-planner".
 * @param {string} [opts.cadence]      - Human-readable cadence string for
 *                                       context (e.g. "every 5 minutes").
 * @param {string} [opts.fastExitToken] - Token the LLM can return to indicate
 *                                       no action. Defaults to "NO_ACTION".
 * @returns {string} Header text. Prepend to the rest of your prompt.
 */
function buildDisciplineHeader({ loopName, cadence, fastExitToken = 'NO_ACTION' } = {}) {
  if (!loopName) throw new Error('buildDisciplineHeader requires loopName');
  const cadenceLine = cadence ? ` (running ${cadence})` : '';
  return [
    `LOOP CONTEXT — ${loopName}${cadenceLine}`,
    '',
    'This is a periodic loop tick, NOT a conversation reply.',
    '- Act ONLY on the freshest signals shown below in the state snapshot.',
    '- Do NOT infer or repeat tasks from prior chats. The user is not in this turn.',
    '- Do NOT hallucinate context. If a piece of information is not in the snapshot, it is not relevant to this tick.',
    `- If nothing in the snapshot is genuinely actionable RIGHT NOW, return ${fastExitToken} (or the equivalent no-op decision in the structured response below) and stop. Choosing to do nothing is correct most of the time.`,
    '',
  ].join('\n');
}

/**
 * Wrap an existing prompt with the discipline header.
 * Convenience for call sites that already produce a fully-formed prompt.
 */
function wrapWithDiscipline(prompt, opts) {
  return buildDisciplineHeader(opts) + prompt;
}

/**
 * Re-export `isContentEffectivelyEmpty` from workspace-prompts so loops
 * have one import surface for skip-when-empty + discipline-header.
 *
 * Used by: think.js + reflect.js + tasks.js + backfill.js + boot-check.js
 * to decide "the trigger content is empty, skip the LLM call entirely."
 *
 * Pattern: load the relevant content (HEARTBEAT.md, BOOT.md, pending
 * memories, etc.). If `isContentEffectivelyEmpty()` returns true, return
 * a structured `{ skipped: true, reason: 'effectively-empty' }` from the
 * loop without calling the model. Saves $$ + latency on idle days.
 */
let _wp;
function isContentEffectivelyEmpty(content) {
  if (!_wp) _wp = require('./workspace-prompts');
  return _wp.isContentEffectivelyEmpty(content);
}

module.exports = {
  buildDisciplineHeader,
  wrapWithDiscipline,
  isContentEffectivelyEmpty,
  DEFAULT_FAST_EXIT_TOKEN: 'NO_ACTION',
};
