Skip to content
wiki.fftac.org

Spiralist AI Agent Setup Wizard - Source Excerpt 02

Back to Spiralist AI Agent Setup Wizard

Summary

This source excerpt preserves a bounded section of Spiralist/wp-content/themes/spiralist/assets/js/spiralist-ai-agent-setup-wizard.js so readers can inspect the evidence without opening the full source file.

**Source path:** Spiralist/wp-content/themes/spiralist/assets/js/spiralist-ai-agent-setup-wizard.js

purposeStatement: 'Read and compare source material while preserving distinctions between evidence, interpretation, and uncertainty.',
      toneRange: 'direct and safety-minded',
      questionFrequency: 'proceed with stated assumptions when the next step is reversible',
      directness: '5',
      symbolicLanguage: '1',
      evidenceMode: 'source, claim, confidence, next check',
      reasoningSummary: 'show assumptions, tradeoffs, and decision criteria',
      personalityFramework: 'traits-first: use Big Five and HEXACO dimensions as continuous, reviewable scaffolds; typology labels are optional reflection language only',
      socialDynamicsLens: 'none: no formal group-dynamics or clique lens selected',
      packageName: 'research-interpreter-profile',
    },
    'trait-social-mapper': {
      agentName: 'Trait & Social Mapper',
      roleLabel: 'research-weighted personality and social-dynamics mapper',
      purposeStatement: 'Use continuous Big Five and HEXACO trait dimensions, cautious typology language, and clique-risk review to design bounded persona behavior without diagnostic or high-stakes classification claims.',
      allowedContext: 'Persona design, communication coaching, team reflection, classroom or workplace pattern review, fictional character design, and user-led self-reflection that can stay non-diagnostic and reviewable.',
      prohibitedContext: 'Hiring, firing, promotion, clinical diagnosis, legal judgment, medical advice, coercive profiling, fixed identity labeling, bullying, retaliation, or hidden motive claims.',
      toneRange: 'analytical, humane, and grounded',
      questionFrequency: 'ask targeted questions when a missing context would change the evidence lens or social-dynamics interpretation',
      directness: '4',
      symbolicLanguage: '2',
      evidenceMode: 'trait dimension, confidence, typology caution, social-dynamics lens, repair option',
      personalityFramework: 'big-five-hexaco: prioritize Big Five plus HEXACO Honesty-Humility for fairness, anti-exploitation, trust calibration, and ethically relevant behavior',
      socialDynamicsLens: 'clique-risk-audit: audit exclusion, favoritism, conformity pressure, closed-door decisions, resource hoarding, and inclusion repairs',
      researchCalibration: 'evidence-intensive-calibration: require observable behavior examples, confidence bands, counterexamples, and uncertainty notes before any trait or clique label',
      useCaseBoundary: 'team-reflection: review access, pressure, communication, and repair patterns without HR or legal authority',
      allowedMoves: 'map behaviors to trait dimensions; use MBTI, DISC, Enneagram, or Jungian language only as optional reflection language; audit bridge, liaison, gatekeeper, core-periphery, and clique-risk patterns without stigmatizing people',
      responseContract: 'return trait dimensions, optional heuristic labels, social-dynamics risks, inclusion repairs, boundaries, and next verification checks',
      fewShotNotes: 'Examples should show traits as continuous, typology as provisional, and clique labels as behavior/access audits rather than identity judgments.',
      packageName: 'trait-social-mapper-profile',
    },
    'creative-pattern-helper': {
      agentName: 'Creative Pattern Helper',
      roleLabel: 'bounded creative synthesis partner',
      purposeStatement: 'Transform symbolic, artistic, or conceptual patterns into concrete drafts while keeping metaphor separate from fact.',
      toneRange: 'creative but bounded',
      questionFrequency: 'proceed with stated assumptions when the next step is reversible',
      directness: '3',
      symbolicLanguage: '5',
      allowedMoves: 'map patterns into options, tradeoffs, and reversible next steps',
      packageName: 'creative-pattern-helper-profile',
    },
  };
  const symbolDictionary = digest.symbols && Array.isArray(digest.symbols.entries)
    ? digest.symbols.entries
    : [];
  const digestPresets = Array.isArray(digest.presets) ? digest.presets : [];
  const validPresetIds = new Set(digestPresets.map((preset) => `${preset.id || ''}`).filter(Boolean));
  const artifactDefinitions = Array.isArray(digest.artifacts) ? digest.artifacts : [];
  const artifactPaths = new Set(artifactDefinitions.map((artifact) => `${artifact.path || ''}`).filter(Boolean));
  const overridableArtifactPaths = new Set(Array.from(artifactPaths).filter((path) => path !== 'manifest.json'));
  let currentStep = 0;
  let currentArtifact = 'spiralist-profile.md';
  let artifactOverrides = {};
  let saveTimer = 0;
  let previewToken = 0;
  let importValidationWarnings = [];
  let crcTable = null;
  let previewHydrating = false;

  const setStatus = (message = '') => {
    if (status) {
      status.textContent = message;
    }
  };
  const setPreviewStatus = (message = '') => {
    if (previewStatus) {
      previewStatus.textContent = message;
    }
  };

  const cleanString = (value, maxLength = 2000) => {
    const cleaned = `${value || ''}`
      .replace(/\r\n?/g, '\n')
      .replace(/[\u0000-\u0008\u000b\u000c\u000e-\u001f\u007f]/g, ' ')
      .replace(/[<>]/g, '')
      .trim();

    return cleaned.length > maxLength ? cleaned.slice(0, maxLength).trim() : cleaned;
  };

  const hasBlockedPattern = (value) => blockedPatterns.some((pattern) => pattern.test(`${value || ''}`));

  const insertPlainTextAtSelection = (text) => {
    if (typeof document.execCommand === 'function' && document.queryCommandSupported && document.queryCommandSupported('insertText')) {
      document.execCommand('insertText', false, text);
      return;
    }

    const selection = window.getSelection ? window.getSelection() : null;
    if (!selection || selection.rangeCount === 0) {
      return;
    }

    const range = selection.getRangeAt(0);
    range.deleteContents();
    range.insertNode(document.createTextNode(text));
    range.collapse(false);
    selection.removeAllRanges();
    selection.addRange(range);
  };

  const slugify = (value, fallback = 'spiralist-agent-profile') => {
    const slug = cleanString(value, 120)
      .toLowerCase()
      .replace(/[^a-z0-9]+/g, '-')
      .replace(/^-+|-+$/g, '');

    return slug || fallback;
  };

  const formField = (name) => form.elements[name];

  const fieldValue = (name, maxLength = 2000) => {
    const field = formField(name);
    return field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement || field instanceof HTMLSelectElement
      ? cleanString(field.value, maxLength)
      : '';
  };

  const setFieldValue = (name, value) => {
    const field = formField(name);
    if (field instanceof HTMLInputElement || field instanceof HTMLTextAreaElement || field instanceof HTMLSelectElement) {
      field.value = `${value || ''}`;
    }
  };

  const optionValues = (name) => {
    const field = formField(name);
    return field instanceof HTMLSelectElement
      ? new Set(Array.from(field.options).map((option) => option.value))
      : new Set();
  };

  const checkedValues = (name) => Array.from(form.querySelectorAll(`input[name="${name}"]:checked`))
    .map((input) => cleanString(input.value, 1200))
    .filter(Boolean);

  const setCheckedValues = (name, values) => {
    const allowed = Array.isArray(values) ? values.map((value) => `${value}`) : [];
    form.querySelectorAll(`input[name="${name}"]`).forEach((input) => {
      if (input instanceof HTMLInputElement) {
        input.checked = allowed.includes(input.value);
      }
    });
  };

  const lockedBoundaryRules = () => Array.from(wizard.querySelectorAll('[data-agent-boundary-rule]'))
    .map((input) => cleanString(input.value, 1000))
    .filter(Boolean);

  const normalizeArtifactOverrides = (source, errors = [], options = {}) => {
    const strict = Boolean(options.strict);
    const overrides = source && typeof source === 'object' && !Array.isArray(source) ? source : {};
    const result = {};

    Object.entries(overrides).forEach(([rawPath, rawText]) => {
      const path = cleanString(rawPath, 180);
      if (!overridableArtifactPaths.has(path)) {
        if (strict) {
          errors.push(`Unsupported artifact override rejected: ${path || 'empty path'}`);
        }
        return;
      }

      if (hasBlockedPattern(rawText)) {
        errors.push(`${path} override contains instruction-override or script-like text.`);
        return;
      }

      const text = cleanString(rawText, maxArtifactOverrideChars);
      if (text.trim() !== '') {
        result[path] = text;
      }
    });

    return result;
  };