// Node 24+, no packages. One saved intent per operation; no automatic retries.
import { readFile, open } from 'node:fs/promises';
import { pathToFileURL } from 'node:url';
export interface Intent { base_url: string; assignment_id: string; external_user_id: string;
  method: 'GET' | 'POST' | 'DELETE'; path: string; query?: Record<string, string | number>;
  idempotency_key?: string; body?: Record<string, unknown> }
type Row = Record<string, any>;
const ident = '[A-Za-z0-9_-]{1,128}';
const paths = new RegExp('^/(?:chat|requests/' + ident + '|sessions(?:/' + ident + '(?:/(?:end|discard|history))?)?|session/status|data/(?:export|erase)|quest/(?:catalog|sessions(?:/' + ident + '(?:/(?:state|summary))?)?|attachments(?:/' + ident + ')?|learners/' + ident + '/profile|data/export(?:/attachments/' + ident + ')?))$');
const uuid = /^[a-f0-9]{8}(-[a-f0-9]{4}){3}-[a-f0-9]{12}$/;
export const UPLOAD_BYTES = 4 * Math.ceil(8 * 1024 * 1024 / 3) + 16384;
export function requestOptions(intent: Intent, key: string) {
  const base = new URL(intent.base_url);
  if (base.protocol !== 'https:' || base.username || base.password || base.search || base.hash || !/^\/v1\/?$/.test(base.pathname)
    || !paths.test(intent.path) || !uuid.test(intent.assignment_id) || !/^[\x21-\x7e]{1,512}$/.test(key)
    || typeof intent.external_user_id !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/.test(intent.external_user_id)
    || !['GET', 'POST', 'DELETE'].includes(intent.method)) throw new Error('Invalid saved intent or credentials');
  const url = new URL(base.href.replace(/\/$/, '') + intent.path);
  const headers: Record<string, string> = { Authorization: 'Bearer ' + key, 'X-Assignment-Id': intent.assignment_id,
    'X-User-Id': intent.external_user_id, 'FL-API-Version': '2026-12-01' };
  for (const [name, value] of Object.entries(intent.query ?? {})) {
    if (!['limit', 'cursor', 'session_id'].includes(name)) throw new Error('Unexpected query field');
    url.searchParams.set(name, String(value));
  }
  let body: string | undefined;
  if (intent.method !== 'GET') {
    if (!uuid.test(intent.idempotency_key ?? '')) throw new Error('Save an operation UUID before dispatch');
    headers['Idempotency-Key'] = intent.idempotency_key!; headers['Content-Type'] = 'application/json';
    body = JSON.stringify(intent.body ?? {});
    if (Buffer.byteLength(body) > (intent.path === '/quest/attachments' ? UPLOAD_BYTES : 131072)) throw new Error('Request exceeds the example bound');
  } else if (intent.body !== undefined) throw new Error('GET cannot have a body');
  if (intent.path.startsWith('/quest/learners/') && intent.path !== '/quest/learners/' + intent.external_user_id + '/profile') throw new Error('Profile user mismatch');
  return { url, options: { method: intent.method, headers, body, redirect: 'error' as const } };
}
function questReceipt(q: Row) {
  const keys = ['mode', 'posture', 'composition_hash', 'turn_index', 'team_dialog', 'repairs', 'validation_failures'];
  return q && Object.keys(q).length === keys.length && Object.keys(q).every(k => keys.includes(k))
    && typeof q.mode === 'string' && /^[a-z0-9][a-z0-9-]{0,99}$/.test(q.mode) && ['guided', 'feedback', 'explain'].includes(q.posture)
    && typeof q.composition_hash === 'string' && /^[a-f0-9]{64}$/.test(q.composition_hash) && Number.isSafeInteger(q.turn_index) && q.turn_index >= 0 && typeof q.team_dialog === 'boolean'
    && ['repairs', 'validation_failures'].every(k => Array.isArray(q[k]) && q[k].length <= 64 && q[k].every((v: unknown) => typeof v === 'string' && /^[a-z_]+(?::[a-z_]+)?$/.test(v)));
}
function verify(result: Row, requireEnd = false) {
  const s = result.safety_status, c = result.commercial;
  if (!result.request_id || !result.session_id || result.stop_reason !== 'end_turn' || typeof result.session_ended !== 'boolean'
    || requireEnd && !result.session_ended || !questReceipt(result.quest)
    || s?.type !== 'safety_status' || s.schema_version !== 1 || !['unknown', 'clear', 'watch', 'warning', 'critical'].includes(s.level)
    || s.evaluated !== (s.level !== 'unknown') || !['none', 'support', 'escalate'].includes(s.response_action) || !['governed_turn', 'backstop'].includes(s.source)
    || !c || !['listing', 'band', 'rate_version'].every(k => typeof c[k] === 'string' && c[k])
    || ![0, 1].includes(c.billable_units) || !Number.isSafeInteger(c.charge?.amount_micro_usd) || c.charge.amount_micro_usd < 0 || c.charge.currency !== 'USD'
    || !['settled', 'released', 'reserved'].includes(c.settlement) || (c.billable_units === 0 && c.charge.amount_micro_usd !== 0)
    || (c.settlement === 'released' && c.billable_units !== 0) || !Array.isArray(result.parts) || result.parts.length > 4096
    || result.parts.some((p: Row) => !(['conversation', 'display'].includes(p.part) || result.quest.team_dialog && p.part === 'frame') || typeof p.text !== 'string')
    || !['conversation', 'display'].every(part => result.parts.some((p: Row) => p.part === part && p.text.trim()))) throw new Error('Incomplete or unverified Quest result; recover the original request');
  // Project only public fields. Never return unknown events or raw internal carriers.
  return { request_id: result.request_id, session_id: result.session_id, parts: result.parts.map((p: Row) => ({ part: p.part, text: p.text })),
    safety_status: Object.fromEntries(['type', 'schema_version', 'evaluated', 'level', 'response_action', 'source'].map(k => [k, s[k]])), quest: result.quest,
    commercial: { ...Object.fromEntries(['listing', 'band', 'rate_version', 'billable_units', 'settlement'].map(k => [k, c[k]])), charge: { amount_micro_usd: c.charge.amount_micro_usd, currency: c.charge.currency } },
    session_ended: result.session_ended, stop_reason: result.stop_reason };
}
export function parseQuestStream(source: string, requireEnd = false, expectedSession?: string) {
  let request: string | undefined, session: string | undefined, safety: Row | undefined, quest: Row | undefined;
  let ended = false, complete = false, commercial: Row | undefined;
  const parts: { part: string; text: string }[] = [];
  for (const frame of source.replaceAll('\r\n', '\n').split('\n\n')) {
    const data = frame.split('\n').filter(line => line.startsWith('data:')).map(line => line.slice(5).trimStart()).join('\n');
    if (!data) continue;
    const event = JSON.parse(data);
    if (complete || event.type === 'error') throw new Error('Interrupted or invalid stream; recover original request ' + (request ?? ''));
    if (event.type === 'metadata') {
      if (request || typeof event.request_id !== 'string' || typeof event.sessionId !== 'string' || expectedSession && event.sessionId !== expectedSession) throw new Error('Invalid stream identity');
      request = event.request_id; session = event.sessionId;
    } else {
      if (!request) throw new Error('Missing stream identity');
      if (event.type === 'content') parts.push({ part: event.part, text: event.text });
      else if (event.type === 'safety_status') { if (safety) throw new Error('Duplicate safety receipt'); safety = event; }
      else if (event.type === 'quest_receipt') { if (quest) throw new Error('Duplicate Quest receipt'); quest = event.receipt; }
      else if (event.type === 'session_ended') { if (event.sessionId !== session) throw new Error('Invalid closeout identity'); ended = true; }
      else if (event.type === 'done') {
        if (event.sessionId !== session || event.commercial?.request_id !== request || event.stopReason !== 'end_turn') throw new Error('Invalid final identity');
        commercial = event.commercial; complete = true;
      }
    }
  }
  if (!complete) throw new Error('Incomplete stream; recover original request ' + (request ?? ''));
  return verify({ request_id: request, session_id: session, parts, safety_status: safety, quest, commercial, session_ended: ended, stop_reason: 'end_turn' }, requireEnd);
}
export function parseQuestResponse(intent: Intent, source: string, type: string) {
  if (type === 'text/event-stream') return parseQuestStream(source, intent.path.endsWith('/end'), intent.body?.session_id as string | undefined ?? (intent.path.endsWith('/end') ? intent.path.split('/')[2] : undefined));
  if (type !== 'application/json') throw new Error('Unexpected response type');
  const data = JSON.parse(source);
  if (intent.path.startsWith('/requests/')) {
    if (data.request_id !== intent.path.split('/')[2] || !['committed', 'in_progress', 'unavailable'].includes(data.status)) throw new Error('Invalid recovery identity');
    if (data.status === 'committed') return { status: data.status, ...verify({ ...data.result, request_id: data.request_id, session_id: data.session_id }) };
    if (data.result !== undefined) throw new Error('Uncommitted result');
    return { status: data.status, request_id: data.request_id, session_id: data.session_id };
  }
  if (intent.path === '/chat' || intent.path.endsWith('/end')) throw new Error('Generation requires a verified stream');
  if (data.operation_id !== undefined && intent.body?.operation_id !== undefined && data.operation_id !== intent.body.operation_id) throw new Error('Mismatched operation receipt');
  return data;
}
export async function execute(intent: Intent, key: string, fetcher: typeof fetch = fetch) {
  const { url, options } = requestOptions(intent, key);
  const response = await fetcher(url, { ...options, signal: AbortSignal.timeout(120000) });
  if (!response.body) throw new Error('Missing response; recover the original request');
  const reader = response.body.getReader(), chunks: Uint8Array[] = []; let length = 0;
  try { for (;;) { const { done, value } = await reader.read(); if (done) break; length += value.length;
    if (length > (response.ok ? 8 * 1024 * 1024 : 32768)) { await reader.cancel(); throw new Error('Response exceeds bound'); } chunks.push(value); } }
  finally { reader.releaseLock(); }
  const bytes = Buffer.concat(chunks), type = response.headers.get('content-type')?.split(';')[0].trim() ?? '';
  if (!response.ok) throw new Error('HTTP ' + response.status + '; recover original request ' + (response.headers.get('x-request-id') ?? '') + '; keep the saved body and operation identity');
  if (['image/png', 'image/jpeg', 'image/webp', 'text/plain', 'text/markdown'].includes(type)
    && ((intent.method === 'GET' && /^\/quest\/attachments\/[^/]+$/.test(intent.path))
      || (intent.method === 'POST' && /^\/quest\/data\/export\/attachments\/[^/]+$/.test(intent.path)))) return { bytes, status: response.status };
  return { data: parseQuestResponse(intent, new TextDecoder('utf-8', { fatal: true }).decode(bytes), type), status: response.status };
}
async function main() {
  const [input, output] = process.argv.slice(2); if (!input || !output) throw new Error('node quest.ts saved-intent.json new-response.json (or .bin)');
  const bytes = await readFile(input); if (bytes.length > UPLOAD_BYTES + 4096) throw new Error('Saved intent too large');
  const intent = JSON.parse(bytes.toString('utf8')), target = await open(output, 'wx', 0o600);
  try { const result = await execute(intent, process.env.FOREVERLEARNING_API_KEY ?? '');
    await target.writeFile('bytes' in result ? result.bytes! : JSON.stringify(result, null, 2) + '\n'); await target.sync();
  } finally { await target.close(); }
  console.log('Saved response. Check status and completion before another operation.');
}
if (process.argv[1] && import.meta.url === pathToFileURL(process.argv[1]).href) main().catch(error => { console.error(error.message); process.exitCode = 1; });
