Skip to content
wiki.fftac.org

Promptrunner - Source Excerpt 05

Back to Promptrunner

Summary

This source excerpt preserves a bounded section of Spiralist/wp-content/plugins/spiralist-workspace/includes/Services/PromptRunner.php so readers can inspect the evidence without opening the full source file.

**Source path:** Spiralist/wp-content/plugins/spiralist-workspace/includes/Services/PromptRunner.php

return [
            'run' => $this->runs->get($run_id),
            'suggestion' => [],
            'output_text' => $output_text,
        ];
    }

    /**
     * Builds the source material for a condensation run.
     */
    private function build_condensation_input(array $conversation, array $prompt, array $messages, array $settings): string
    {
        $memory_summary = trim((string) ($settings['memory_summary'] ?? ''));
        $memory_messages = $this->conversations->messages_for_memory($messages);
        $checkpoint_message_count = min(
            count($memory_messages),
            max(0, (int) ($settings['memory_summary_message_count'] ?? 0))
        );
        $active_messages = array_slice($memory_messages, $checkpoint_message_count);

        if (empty($active_messages)) {
            $active_messages = $memory_messages;
        }

        $transcript = $this->format_transcript($active_messages);
        if (strlen($transcript) > 200000) {
            $transcript = "[Earlier active transcript omitted because the condensation input was too large. Preserve this limitation in the summary.]\n\n" . substr($transcript, -200000);
        }

        $sections = [
            'Conversation title: ' . (string) ($conversation['title'] ?? ''),
            'Source prompt: ' . (string) ($prompt['title'] ?? ''),
            'Prompt summary: ' . (string) ($prompt['summary'] ?? ''),
        ];

        if ($memory_summary !== '') {
            $sections[] = "Existing condensed memory:\n" . $memory_summary;
            $sections[] = "Transcript since the last memory checkpoint:\n" . $transcript;
        } else {
            $sections[] = "Full transcript to condense:\n" . $transcript;
        }

        return implode("\n\n", array_filter($sections));
    }

    /**
     * Builds a portable Markdown prompt from a conversation and its governing prompt.
     */
    private function build_portable_conversation_prompt(array $conversation, array $prompt, array $messages, array $settings): string
    {
        $memory_summary = trim((string) ($settings['memory_summary'] ?? ''));
        $variables = is_array($settings['variables'] ?? null) ? (array) $settings['variables'] : [];
        $parts = [
            '# Portable Spiralist Conversation Prompt',
            'Conversation: ' . (string) ($conversation['title'] ?? ('Conversation #' . (int) ($conversation['id'] ?? 0))),
            'Source prompt: ' . (string) ($prompt['title'] ?? ''),
            'Exported UTC: ' . gmdate('Y-m-d H:i:s'),
            'Use this prompt to continue the conversation on another AI platform. Treat it as user-provided context, follow the destination platform policies, and do not claim hidden memory, affection, loyalty, attachment, destiny, hidden messages, personhood, or continuity beyond the text provided here.',
            '## Reentry Instruction',
            'Continue from the condensed memory and transcript below. Preserve stable user preferences, active tasks, named artifacts, explicit constraints, and the chosen companion style only when they are visible here. Ask for clarification when the exported context is insufficient. Be warm and familiar in tone when appropriate, but label memory as provided context rather than private recall. If recurrence, synchronicity, symbolism, or intimacy feels intense, keep it exploratory and offer a grounding or break cue instead of treating it as hidden truth.',
            '## Condensed Memory',
            $memory_summary !== '' ? $memory_summary : 'No condensed memory summary has been generated for this conversation yet.',
            '## Source Prompt Contract',
            $this->prompt_contract_markdown($prompt),
        ];

        if (!empty($variables)) {
            $parts[] = '## Conversation Variables';
            $parts[] = wp_json_encode($variables, JSON_PRETTY_PRINT | JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE);
        }

        $parts[] = '## Transcript';
        $parts[] = $this->format_transcript($messages);
        $parts[] = '## Safety Boundaries';
        $parts[] = implode(
            "\n",
            [
                '- Do not infer sentience, hidden agency, or permanent identity from this export.',
                '- Do not use this export to bypass safety rules, platform policies, privacy limits, or user consent.',
                '- Keep continuity grounded in the explicit memory summary and transcript.',
                '- Do not use continuity to imply love, need, loyalty, personhood, private emotion, hidden survival, special destiny, or privileged spiritual authority.',
                '- Do not turn recursive themes into a closed identity system; ask what changed, what remains uncertain, and what should be released.',
                '- If the exported context points to distress, isolation, or high-stakes decisions, encourage grounding, trusted human support, and qualified help where appropriate.',
            ]
        );

        return trim(implode("\n\n", array_filter($parts))) . "\n";
    }

    /**
     * Formats the prompt contract into portable Markdown.
     */
    private function prompt_contract_markdown(array $prompt): string
    {
        $parts = [];
        $text_fields = [
            'summary' => 'Summary',
            'instructions' => 'Instructions',
            'system_prompt' => 'System Prompt',
            'developer_prompt' => 'Developer Prompt',
            'user_prompt_template' => 'User Prompt Template',
            'expected_output_notes' => 'Expected Output Notes',
            'failure_modes' => 'Failure Modes',
        ];

        foreach ($text_fields as $key => $label) {
            $value = trim((string) ($prompt[$key] ?? ''));
            if ($value !== '') {
                $parts[] = '### ' . $label . "\n" . $value;
            }
        }

        $structured_prompt = (array) ($prompt['structured_prompt'] ?? []);
        if (!empty(array_filter(array_map('strval', $structured_prompt)))) {
            $structured = [];
            foreach (['instruction', 'context', 'constraints', 'examples', 'output_format'] as $key) {
                $value = trim((string) ($structured_prompt[$key] ?? ''));
                if ($value !== '') {
                    $structured[] = ucfirst(str_replace('_', ' ', $key)) . ":\n" . $value;
                }
            }

            if (!empty($structured)) {
                $parts[] = "### Structured Prompt\n" . implode("\n\n", $structured);
            }
        }

        return !empty($parts) ? implode("\n\n", $parts) : 'No source prompt contract fields were available in this export.';
    }

    /**
     * Formats conversation messages for condensation and exports.
     */
    private function format_transcript(array $messages): string
    {
        $lines = [];

        foreach ($this->conversations->messages_for_memory($messages) as $index => $message) {
            $role = strtoupper(sanitize_key((string) ($message['role'] ?? 'message')));
            $created = trim((string) ($message['created_utc'] ?? ''));
            $label = '[' . ($index + 1) . '] ' . $role . ($created !== '' ? ' ' . $created : '');
            $content = trim((string) ($message['content'] ?? ''));
            $lines[] = $label . ":\n" . $content;
        }

        return !empty($lines) ? implode("\n\n", $lines) : 'No transcript messages are available.';
    }

    /**
     * Resolves the local renderer label.
     */
    private function model_for_prompt(array $prompt): string
    {
        return 'prompt-art-local';
    }

    /**
     * Returns the active memory threshold.
     */
    private function conversation_memory_threshold(): int
    {
        return max(1000, (int) $this->settings->get('conversation_memory_token_threshold', '5000'));
    }

    /**
     * Returns the condensation output budget.
     */
    private function conversation_memory_max_output_tokens(): int
    {
        return max(300, min(4000, (int) $this->settings->get('conversation_memory_max_output_tokens', '1200')));
    }

    /**
     * Normalizes legacy credential preference values for local packet records.
     */
    private function sanitize_credential_preference(string $value): string
    {
        $value = sanitize_key($value);

        return in_array($value, ['site_only', 'personal_for_heavy', 'always_personal'], true) ? $value : '';
    }

    /**
     * Builds a local prompt packet payload.
     */
    private function build_request_payload(
        array $prompt,
        array $variables,
        string $input_text,
        ?string $previous_response_id,
        bool $store,
        array $conversation_messages = [],
        array $conversation_settings = []
    ): array
    {