📄 self-chat.cpython-312.pyc
/home/palash/git/local-ai/__pycache__/self-chat.cpython-312.pyc
Language: pyc • Lines: 3536
�

�d�ju���
	�ddlZddlZddlZddlZddlZddlZddlZddlZddlZddl	Z	ddl
mZddlmZddl
Z
ddlmZe�ej d��Zej%ddd	�
�ej%ddd
��ej%ddd��ej%ddd��ej'�Zej*j-d�Zej0d�Zej0d�Zej6j9dd�j;d�ZdZdZ ej6j9dd�Z!ej6j9de!�Z"ej6j9de!�Z#dZ$dZ%d Z&d!Z'd"d#d$�Z(d%Z)e*e)�jW�Z,d&Z-d'Z.d(Z/ej6j9d)e!�Z0ej6j9d*e!�Z1d+Z2d,Z3d-Z4d.Z5ej*j-d/�Z6d0Z7d1Z8d2Z9ej*j-d3�Z:ej*j-d4�Z;ej*j-d5�Z<ia=d6�Z>e>e;i�Z?e>e<i�Z@e>ej*j-d7�i�ZAd�d8�ZBd9�ZCd�d:�ZDd;�ZEiZFd<�ZGd�d=�ZHd>�ZId?�ZJd�d@�ZKdA�ZLdB�ZMdC�ZNdD�ZOdE�ZPdF�ZQd�dG�ZRdH�ZSdI�ZTdJ�ZU	d�dK�ZVd�dL�ZWdM�ZXd�dN�ZYdO�ZZe7fdP�Z[e7dQfdR�Z\dS�Z]dT�Z^dU�Z_d�dV�Z`dWdXdY�ZadZ�Zbd[�Zcd\Zdd]�Ze	d�d^�Zfd�d_�ZgiZhiZiej��Zkd`Zlda�Zmdb�Zndc�Zodd�Zp	d�de�Zqdf�Zrdg�Zsd�dh�Ztdi�Zudj�Zv		d�dk�Zw						d�dl�Zx															d�dn�Zydo�Zzd�dp�Z{dq�Z|dr�Z}ds�Z~dt�Z				d�du�Z�dv�Z�dw�Z�dx�Z�dy�Z�dz�Z�d{�Z�d|�Z�d}�Z�d~�Z�d�Z�d��Z�d��Z�d��Z�e�jd��Z�d��Z�d��Z�d��Z�d��Z�d��Z�dddddde5fd��Z�					d�d��Z�						d�d��Z�d��Z�e_�\Z�Z�Z�Z?Z@e�se�d��e�dm��eDe��Z�e�d�e�e���d�e����e�j@rec�e�d��e�d��Z�e��jG��jI�d�k(Z�e�d�k(re��yy)��N)�SequenceMatcher)�datetime)�load_dotenvzSelf-chat story generator)�descriptionz--config�aYPath to a custom JSON task list to use instead of the default (~/local-ai-files/tasks.json). The file may be a plain task list or an object with 'tasks' plus optional 'genre_checklists'. Each task may also carry its own 'checklist' (editor/moderator) that wins over the genre checklist. Combine with --defaults to also include the default tasks.)�default�helpz
--defaults�
store_truez`Also load the default tasks (~/local-ai-files/tasks.json) in addition to the ones from --config.)�actionr	z	--dry-runz�Validate the task config and environment and print the full plan for each task (genre/checklist resolution, script enforcement, medium feasibility) without making any LLM call, then exit.z--gpuz�Run the self-chat agents (kolpo/kaya/editor/moderator) on the interactive GPU llama-server (8081) instead of the RAM-backed CPU server (8079). Each /api/chat request carries mode="gpu" so chat-webui routes the agent tasks to the GPU lane.z~/local-ai-files/stories�STORIES_PREMIUM_DIR�STORIES_ADMIN_DIR�SELF_CHAT_BASE_URLzhttp://localhost:3001�/�kolpo�kaya�SELF_CHAT_PASSWORD�SELF_CHAT_A_PASSWORD�SELF_CHAT_B_PASSWORD�[END CONVERSATION]g@g>@�
�Kolpo�Kaya��A�Bz)/home/palash/local-ai-files/self_chat.txti��editor�	moderator�SELF_CHAT_EDITOR_PASSWORD�SELF_CHAT_MODERATOR_PASSWORDz//home/palash/local-ai-files/contexts/editor.txtz2/home/palash/local-ai-files/contexts/moderator.txtz1/home/palash/local-ai-files/contexts/critique.txt�z~/local-ai-files/tasks.jsonz	self-chat��z&~/local-ai-files/genre_checklists.jsonz0~/local-ai-files/contexts/genre_persona_map.jsonz+~/local-ai-files/contexts/persona_pool.jsonc���	t|d��5}tj|�cddd�S#1swYyxYw#ttjf$r}td|�d|�d��|cYd}~Sd}~wwxYw)N�utf-8��encodingz[persona] Could not load �: u — using fallback)�open�json�load�FileNotFoundError�JSONDecodeError�print)�filepath�fallback�f�es    �&/home/palash/git/local-ai/self-chat.py�load_json_filer3vsk���
�(�W�
-�	 ���9�9�Q�<�	 �	 �	 ���t�3�3�4��
�)�(��2�a�S�8K�L�M�����s/�
:�.�	:�7�:�:�A1�A,�&A1�,A1z-~/local-ai-files/contexts/master_details.jsonc���|j|�xs|jd�}|dk(r|r|D�cgc]
}|dk7s�	|��}}�xsg�g}|j�D]q\}}|r||vr�
|j�D]R\}	}
|
jd�}|r&t�fd�dD��}|dk(r|s�5|dk(rd�vr�?|j||	|
f��T�s|sd	d
d�d	dd�d
�}
dd|
fS|tvs"t|dtt|d�k\r,t
|�}tj|�|dd�t|<t|}|d|d}|dxxdz
cc<|Scc}w)NrzAdventure & HorrorzParent & Child�
required_rolec3�&�K�|]}|�v���
y�w�N�)�.0�r�
task_roless  �r2�	<genexpr>z+pick_persona_round_robin.<locals>.<genexpr>�s�����)X�a�!�z�/�)X�s�)�premium�adminr=r>�	Colleaguez%Creative and energetic problem solver)�role�personaz!Methodical and structured partner)rr�
ColleagueszFocused Collaboration�idx�pairsr)rDrC�)	�get�items�any�append�_persona_cycles�len�list�random�shuffle)�pool�genre�	genre_mapr;�allowedr:�
candidates�rel�moods�mood�details�req_role�is_premium_or_admin�fallback_details�shuffled�state�choices   `             r2�pick_persona_round_robinr^�s�����m�m�E�"�>�i�m�m�I�&>�G�
�$�$��%�?���.>�)>�1�?��?��!�r�J��J��j�j�l�
4�
��U��s�'�)��"�[�[�]�
	4�M�D�'��{�{�?�3�H��&)�)X�CW�)X�&X�#��y�(�1D����(�W�J�-F�����s�D�'�2�3�
	4�
4��$�B��
$�>��	
���4�6F�F�F��O�#��u�'=�e�'D�����w�'�I�(��
�#�����x� �+3�A�!>�����E�"�E�
�7�^�E�%�L�
)�F�	�%�L�A��L��M��[@s
�
E�Ec��|j�D]A\}}t|t�r'||vr#t||t�rt|||��=|||<�C|S)z0Recursively merge dictionary source into target.)rG�
isinstance�dict�
deep_merge)�target�source�key�values    r2rbrb�sW���l�l�n� �
��U��e�T�"�s�f�}��F�3�K�QU�9V��v�c�{�E�*��F�3�K�	 �
�M�c� �i}	ttd��5}tj|�}ddd�|r|j|�|S#1swY�xYw#ttj
f$r }t
dt�d|�d��Yd}~�Ud}~wwxYw)z}Base checklists from genre_checklists.json, overridden per genre by any
    'genre_checklists' carried in a task config file.r$r%Nzhecklist] Could not load r'u — using empty checklists)r(�GENRE_CHECKLISTS_FILEr)r*r+r,r-�update)�extra�
checklistsr0r1s    r2�load_genre_checklistsrm�s����J�
�
�'�'�
:�	&�a����1��J�	&�
����%� ���	&�	&���t�3�3�4�
�
�)�*?�)@��1�#�E`�a�	
�	
��
�s-�A�A�A�A�
A�B
�-B�B
c�J�g}|D�]p}|jd�xsdj�}|s�*|jd�xsdg}t|t�r,|j	d�D�cgc]}|j���}}|jd�xsddg}t|t�r,|j	d�D�cgc]}|j���}}|jd	�xsd
g}t|t�r,|j	d�D�	cgc]}	|	j���}}	|jd�xsdj�xsd}
|jd
�xsd}t|t�r|j�}|jd�xsi}|jd�xsdj�xsd}
|jd�xsd}|jd�xsd}t
}|jd�}t
|jd��}t|jd�xsd�}|dkrd}|�	t|�}|dkrt
}|j||
||||||
|||||d�
���s|Scc}wcc}wcc}	w#ttf$r	t
}Y�UwxYw)N�taskr�	languages�English�,�mediums�image�text�roles�freerP�GeneralrW�	checklist�path�inactiveF�context�turns�research�research_turnsrEr )
rorPrprsrvrWryrzr{r|r}r~r)rF�stripr`�str�split�MAX_MESSAGES_PER_AGENT�bool�int�	TypeError�
ValueErrorrI)rG�tasks�itemrorp�lrs�mrvr:rPrWryrzr{r|r}�	raw_turnsr~rs                    r2�_parse_tasksr��s|���E��2
������ �&�B�-�-�/�����H�H�[�)�8�i�[�	��i��%�,5�O�O�C�,@�A�q�����A�I�A��(�(�9�%�:�'�6�):���g�s�#�*1�-�-��*<�=�Q�q�w�w�y�=�G�=�����!�-�f�X���e�S�!�(-���C�(8�9�1�Q�W�W�Y�9�E�9����'�"�(�b�/�/�1�>�Y���(�(�9�%�+����g�s�#��m�m�o�G��H�H�[�)�/�R�	����� �&�B�-�-�/�7�4���8�8�J�'�0�5���(�(�9�%�-���&���H�H�W�%�	������,�-���T�X�X�&6�7�<�1�=���A���N�� �
/��I����q�y�.��
�����&�"��"�&��$�"��$�"0�
�	
�E2
�f�L��[B��>��:��$�z�*�
/�.��
/�s$�(I;�;J�
J�J
�
J"�!J"c���t|t�r|St|tttf�s|�t|�St|t
tf�rtj|�St|�S)z.Render a resolved detail value as prompt text.)	r`r�r��floatr�rarLr)�dumps)rfs r2�_fmt_detail_valuer�sW���%������%�#�u�d�+�,��
��5�z���%�$���&��z�z�%� � ��u�:�rgc��|r|j|�St|�dkr	|r|dSdSdj|dd�dz|dzS)zCJoin multi-selected values naturally: 'a', 'a and b', 'a, b and c'.rErr�, N�����z and )�joinrK)�values�	separators  r2�_join_valuesr�sT����~�~�f�%�%�
�6�{�a��"�v�a�y�*��*��9�9�V�C�R�[�!�G�+�f�R�j�8�8rgc��|jd�}|�yt|t�r|nd|i}t||�d�|�}t|tt
f�rt
|�S	tdt|��S#ttf$rYywxYw)aResolve how many values a multi selector should pick.

    ``count`` may be a plain integer/string or its own selector spec
    (e.g. ``{"selector": "random", "values": [2, 3]}``), resolved through
    the same selector machinery so it can vary per round.
    �countrErfz::countr)rFr`ra�_pick_detail_valuerL�tuplerK�maxr�r�r�)ro�name�specr��
count_spec�resolveds      r2�_resolve_countr�%s���
�H�H�W��E��}��$�U�D�1����7G�J�!�$�4�&��(8�*�E�H��(�T�5�M�*��8�}����1�c�(�m�$�$���z�"����s�A2�2B�Bc	�D�d|vr|dS|jd�xsg}t|jd�xsd�j�j�}|sy|jd��
t	|||�nd}|��|dkDr�|dvr`||f}t|�}tj|d	�|z}||zt|<tt||��D�	cgc]
}	|||	z|z��c}	Stj|t|t|���S|d
k(rtj|�S|dk(r7||f}tj|d	�t|�z}
|
dzt|<||
S|dk(r6t	|||�}tj|t|t|���S|d
k(rmt	|||�}||f}t|�}tj|d	�|z}||zt|<tt||��D�	cgc]
}	|||	z|z��c}	S|d	Scc}	wcc}	w)a�Pick value(s) for one detail field according to its selector.

    Supported selectors:
      - random:           pick one value at random (fresh each round)
      - roundrobin:       cycle through values one at a time across rounds
      - random_multi:     pick ``count`` distinct values at random
      - roundrobin_multi: slide a window of ``count`` values across rounds
      - absent / static / first: use ``value`` if given, else ``values[0]``
    rfr��selectorrNr�rE)�
roundrobin�roundrobin_multirrMr��random_multir�)rFr�r��lowerr�rK�_detail_cycles�range�minrM�sampler])ror�r�r�r�r�re�n�start�irCs           r2r�r�9s"���$���G�}��
�X�X�h�
�
%�2�F��4�8�8�J�'�-�2�.�4�4�6�<�<�>�H���04����0A�0M�N�4��t�,�SW�E���U�Q�Y��9�9���,�C��F��A�"�&�&�s�A�.��2�E�"'�%�-�N�3��5:�3�u�a�=�5I�J��F�E�A�I��?�+�J�J��}�}�V�S���F��%<�=�=��8���}�}�V�$�$��<���T�l��� � ��a�(�3�v�;�6��!�A�g��s���c�{���>�!��t�T�4�0���}�}�V�S���F��%<�=�=��%�%��t�T�4�0���T�l����K���"�"�3��*�Q�.��#�e�m��s��16�s�5�!�}�1E�F�A����	�Q��'�F�F��!�9���1K��,Gs�H�>Hc���t|t�s|S|�
t�n|}|jd�}|jd�}|�>t|tt
f�r|g}nt
|�}|�||vr|jd|�n|�|g}ng}t|�}|s|St
|jd�xsg�}|jd�}|jd�}|jd�}	|jd	�}
|D]�}||vr�t|t�r|j|�nd}t|t�s�<t||||hz�}
|
jd�xsgD]3}t|tttf�s||vr�#|j|��5|�|
jd�}|�|
jd�}|	�|
jd�}	|
���|
jd	�}
��|jd��t
|d�}|jd��|d}|r||d<n|jdd�|�||d<|�||d<|	�|	|d<|
�|
|d	<|jdd�|jdd�|S)
a�Resolve ``ref`` / ``refs`` against the master dictionary.

    The ``values`` across every referenced master definition are unioned
    (order-preserving and deduplicated) so a single field can draw from several
    value pools at once. Resolution is recursive: a referenced definition may
    itself carry ``ref`` / ``refs``, allowing one value set to be composed from
    others. ``_seen`` guards against reference cycles.

    As before, an explicit inline ``values`` list on the spec wins outright over
    the merged pool (mirroring the old single-``ref`` override behaviour). The
    returned dict is ready to hand to :func:`_pick_detail_value`.
    N�ref�refsrr�r�r�r�r�)r`ra�setrFr��bytesrL�insert�_merge_value_defsr�rI�pop)r��master�_seenr�r��mergedr�r�r�r�r�re�entry�sub�vs               r2r�r�ns���d�D�!����]�C�E��E�
�(�(�5�/�C��8�8�F��D����d�S�%�L�)��6�D���:�D��?�s�$���K�K��3��	���u����
�$�Z�F���
�
�$�(�(�8�$�*��
+�F��z�z�*�%�H��:�:�f��D��J�J�w��E��
�
�;�'�I��-���%�<��#-�f�d�#;��
�
�3�����%��&����v�u��u�}�=������"�(�b�	�A��!�d�E�4�0�1�Q�&�[���M�M�!��	����w�w�z�*�H��<��7�7�6�?�D��=��G�G�G�$�E�������,�I�%-�(�x�x���%��d�8�n�%���x�x���#��F�|��
�!��x���
�
�8�T�"���%��z������v������w����'��{��
�J�J�u�d��
�J�J�v�t���Mrgc���t|t�syt|ttf�r|D�cgc]}|��|��	}}n|�|g}ng}|D]}t	|�|vs�|t	|�cSd|vr|dSycc}w)z�Select a ``when`` branch by exact match on a resolved value.

    ``*`` acts as a fallback when no exact branch matches. If the trigger value
    is a list (multi-select), any one of its elements may match.
    N�*)r`rarLr�r�)�tablerfr�rS�	candidates     r2�_pick_when_branchr��s����e�T�"���%�$���'�!&�8�A�!�-�a�8�
�8�	�	��W�
��
��)�	��y�>�U�"���Y��(�(�)��e�|��S�z����9s
�A1�A1c��|jd�}t|t�r|syg}|j�D]F\}}t|t�s�t	||j|��}|�y|j|��H|syt
|�dk(r&t|dt�rt|d�S|dSgg}
}	t�}dx}x}
x}}|D�]G}t|t�s�|jd�}|�|jd��|dg}t|ttf�r|g}|xsgD]}||	vs�|	j|��|jd�xsgD]E}t|tttf�r�||vs�$|j|�|
j|��G|�|jd�}|
�|jd	�}
|�"|jd
��|jd
�}|���$|jd����7|jd�}��Ji}|	r|	|d<|
r|
|d<d|fd	|
fd
|fd|ffD]
\}}|��	|||<�|xsdS)a�Resolve a spec's ``when`` conditions into a value-definition dict.

    Each ``when`` entry maps an already-resolved field name to a branch table
    (``{value: def}``). One branch is chosen per trigger field; a field with
    multiple triggers ANDs its branches together by unioning their pools. A
    missing trigger (or unmatched value without a ``*`` fallback) skips the
    field entirely by returning ``None``.
    �whenNrErr�r�r�r�r�r�r�)
rFr`rarGr�rIrKr�r�r�rLr��add)r�ror��trigger_valuesr��branches�triggerr��branchr�r��seen_valuesr�r�r�r��branch_refsr:r�r�re�vals                      r2�_resolve_when_specr��s����8�8�F��D��d�D�!����H��*�*�,� �����%��&��"�5�.�*<�*<�W�*E�F���>�������
 ���
�8�}���$.�x��{�D�$A�t�H�Q�K� �R�x�PQ�{�R��r�&�D��%�K�*.�.�H�.�t�.�e�i��0���&�$�'���j�j��(����6�:�:�e�#4�#@�!�%�=�/�K��k�C��<�0�&�-�K��"��	�A���}����A��	����H�%�+��	!�A��!�d�E�4�0�1����#�����"��
�
�a� �	!����z�z�*�-�H��<��:�:�f�%�D��=�V�Z�Z��0�<��J�J�w�'�E������K�!8�!D��
�
�;�/�I�10�4�F����v��
�!��x��	�X��	���	�%��	�i� �	����S��?��F�3�K���>�T�rgc	���t|t�r|St|t�r;|j�D��cgc]\}}d|it|t�r|nd|i���!c}}SgScc}}w)z>Normalize a ``details`` value into a list of field spec dicts.r�rf)r`rLrarG)rWr�r�s   r2�_details_specsr� sg���'�4� ����'�4� �&�m�m�o�
���d��T�S�j��t�&<�d�7�D�/�S�
�	
��I��	
s�$A c��t|t�sy|�yt|jd�xsd�}|dk(r|dk(S|dk(r|dk(S||k(S)z�Whether a field spec belongs to the given change-frequency pass.

    Fields without an explicit ``change_freq`` default to ``"Per Round"``;
    only genuinely ``"Per Turn"`` fields join the per-turn re-resolution.
    FT�change_freq�	Per Round�Per Turn)r`rar�rF)r��freq_filter�effs   r2�_spec_in_filterr�,sc���d�D�!�����

�d�h�h�}�%�4��
5�C��k�!��k�!�!��j� ��j� � ��+��rgc�8�td�t|�D��S)z5True if a task spec has any genuinely per-turn field.c3�4K�|]}t|d����y�w)r�N)r�)r9�ss  r2r<z(_has_per_turn_details.<locals>.<genexpr>@s����O�!��q�*�-�O�s�)rHr�)rWs r2�_has_per_turn_detailsr�>s���O�~�g�7N�O�O�Orgc��t|�D�cgc]%}t|t�r|jd�r|��'c}Scc}w)uCField specs flagged ``character: true`` — the story's named cast.�	character)r�r`rarF)�details_specr�s  r2�_character_fieldsr�Cs>�� ��-��
��a���1�5�5��#5�	
����s�*;c���|xsgD�cgc]+}t|t�s�|j�s�%||vs�*|��-}}|sy||df}tj	|d�t|�z}|dzt|<||Scc}w)z�Pick a character name for the round (round-robins across rounds).

    ``skip`` is a set of names already taken this round, so multi-member cast
    slots (``count > 1``) never reuse a name within the same round.
    rz<name>rrE)r`r�r�r�rFrK)ro�
field_name�names�skipr�r�rerCs        r2�_pick_character_namer�Ls����K�R���J�q�#�$6�1�7�7�9��RV����F������X�
&�C�
�
�
�S�!�
$�s�6�{�
2�C���'�N�3���#�;���s�A6�A6�A6�A6c	���g}|xsi}t|�D]�}t|jd�xsd�j�}|j||jd��}t	|t
tf�r|n|g}t�}	|D]k}
t|
xsd�j�}
|
s�#t|||jd�|	��}|r|	j|�|j||
|xs|
f��m��|S)u>Decide and NAME the story's characters for this round.

    Returns a list of ``(label, species, name)`` triples: one per field flagged
    ``character: true``, expanded into ``count`` members when the field carries a
    count. Species come from the already-resolved per-round value pool (a list
    means a multi-member slot); names are assigned deterministically from the
    field's ``names`` list (rotating across rounds, never reused within a round),
    so the cast — including its size — is fixed and repeatable before a single
    word of story is written.
    r�rrfr�)r�)r�r�rFr�r`rLr�r�r�r�rI)ror��round_fields�cast�fieldsr��labelrf�species_list�used�speciesr�s            r2�
build_castr�]s����D�
�
�R�F�!�,�/�;���D�H�H�V�$�*��+�1�1�3���
�
�5�$�(�(�7�"3�4�� *�5�4��-� @�u�u�g���u��#�	;�G��'�-�R�(�.�.�0�G���'��e�T�X�X�g�5F�T�R�D��������K�K������9�:�	;�;��Krgc	��|sydg}|D] \}}}|jd|�d|�d|����"|jd�dj|�S)zERender the decided-and-named cast as an immutable per-turn directive.ruF## Characters (already decided and named for this story — immutable)�- u	 — the r�z�HARD RULE: These are the ONLY characters in this story. Never create, name, or depict any additional character in the text or in any image; every generated image must show only these named characters.�
)rIr�)r��linesr�r�r�s     r2�format_cast_blockr�zsl����P�
�E�!%�=���w��
���r�$��y���r�'��;�<�=�	�L�L�	G��
�9�9�U��rgc��t|t�s
dt|�fS|jd�}t	||�}t|jd�xs|jd�xsd�j�}|sIt|t�r|r|dn|jd�xsd}t|xsd�j�}t|||�}||fS)z:Resolve a single detail field spec into ``(name, value)``.rr�r�rr�)r`rar�rFr�r�rLr�)r�ror�r�r�r��	first_refrfs        r2�_resolve_field_valuer��s����d�D�!��3�t�9�}���8�8�F��D�
�t�V�
,�F��v�z�z�&�!�;�T�X�X�f�%5�;��<�B�B�D�D��!�$��-�$�D��G�D�H�H�U�O�<Q�r�	��9�?��#�)�)�+���t�T�6�2�E���;�rgc�r�|�t}t|t�r	|dk(r|SdSt|�}|s,t|tt
f�rdn
t|�}|dk(r|SdSt
�}|D]k}t|t
�s�t||�s�!|jd�}	t|	t
�s�C|	s�F|jd�|	j�D���mi}
i}|��t|�D]w\}}|snpt|t
�r|jd�r�,t||�s�9t|||�\}
}|
|f|
|<t|jd�xs|
�}||vs�s|||<�yg}t|�D�]�\}}t|t
�s |dk(r|jt|���7t||�s�Dt|jd�xsd�j�}|�||vr|}
||
}|jd�}n�|jd�rjt||||�}|���|jd�}t|t
�rt|�nd|i}|r||d<t|||�\}
}|jd�}n?||
vr|
|\}
}|jd�}n!t|||�\}
}|jd�}|
r|���Ot|tt f�r)|D�cgc]
}t#|���}}|s���t%||�}nt#|�}|s���|j|
�d|������d	j'|�Scc}w)
a�Resolve field specs matching a specific change frequency.

    Fields carrying a ``when`` block are resolved in a second pass, once the
    plain fields they depend on have been resolved (their values form the
    trigger map). A trigger field is resolved exactly once even though it lives
    in both passes (its result is cached).

    ``preferred`` may carry already-resolved ``{name: value}`` pairs (e.g. from
    :func:`resolve_details_fields`) that should be reused verbatim instead of
    being resolved again, so the rendered prompt text always matches the values
    that were tracked. When ``preferred`` is given the when-trigger pass is
    skipped, since every value is already final.
    r�rr�c3�HK�|]\}}t|t�s�|���y�wr7�r`ra�r9�k�ts   r2r<z"resolve_details.<locals>.<genexpr>������"U���A��A�t�AT�1�"U���"�"r�r�rfr'r�)�MASTER_DETAILSr`r�r�rLrar�r�rFrjrG�	enumerater�rIr�r�r�r�r�r�)rWror�r��	preferred�specs�rendered�needed_triggersr�r��cachedr�r�r�rfre�parts�key_name�sepr��
outer_namer�r��	formatteds                        r2�resolve_detailsr	�s;�� �~����'�3��%��4�w�<�"�<��7�#�E��#�G�d�D�\�:�2��G���&�+�5�x�=�2�=��e�O��V���$��%���t�[�1���x�x�����d�D�!�d��"�"�"U�����"U�U�V��F��N��� ��'�	,�G�A�t�"���d�D�)�T�X�X�f�-=��"�4��5��.�t�T�6�B�K�D�%��u�
�F�1�I��d�h�h�v�&�.�$�/�C��o�%�&+��s�#�	,�
�E��U�#�),���4��$��%��k�)����S��Y�'���t�[�1���t�x�x��'�-�2�.�4�4�6��� �X��%:��D��d�O�E��(�(�;�'�C�
�X�X�f�
�'��d�F�N�K�F��~�����&�)�J�",�V�T�":�$�v�,��&�@Q�C��(��F��.�s�D�&�A�K�D�%��'�'�+�&�C�
�&�[� ��)�K�D�%��(�(�;�'�C�.�t�T�6�B�K�D�%��(�(�;�'�C��u�}���e�d�E�]�+�7<�=�!�*�1�-�=�I�=���#�I�s�3�H�(��/�H���
����v�R��z�*�+�S),�V�9�9�U����>s�L4c�v�|�t}t|t�riSt|�}|siSt	�}|D]k}t|t
�s�t
||�s�!|jd�}t|t
�s�C|s�F|jd�|j�D���mi}i}	t|�D]w\}
}|snpt|t
�r|jd�r�,t
||�s�9t|||�\}}||f||
<t|jd�xs|�}
|
|vs�s||	|
<�yi}t|�D]�\}
}t|t
�s�t
||�s�$|jd�rYt||||	�}|��F|jd�}t|t
�rt|�nd|i}|r||d<t|||�\}}n|
|vr	||
\}}nt|||�\}}|r|���t|ttf�r|D�cgc]
}t|���}}|r|nd}nt|�}|s��|||<��|Scc}w)a�Resolve a task's ``details`` spec into ``{field: value}`` pairs.

    Like :func:`resolve_details` but returns the raw resolved values (lists
    stay lists) keyed by field name, so the exact combination can be hashed by
    the theme tracker. A plain string returns ``{}`` (nothing to track).

    ``freq_filter`` restricts to one change-frequency pass (``"Per Round"`` /
    ``"Per Turn"``); ``None`` resolves every field (default).
    Nr�c3�HK�|]\}}t|t�s�|���y�wr7r�r�s   r2r<z)resolve_details_fields.<locals>.<genexpr> r�r�r�rf)r�r`r�r�r�rar�rFrjrGr�r�r�rLr�r�)rWror�r�rrr�r�rr�r�r�rfrer�r�rr�r�rs                    r2�resolve_details_fieldsrsQ���~����'�3���	��7�#�E���	��e�O��V���$��%���t�[�1���x�x�����d�D�!�d��"�"�"U�����"U�U�V��F��N��U�#�(���4����$��%����&�)9���t�[�1��*�4��v�>���e��5�M��q�	��$�(�(�6�"�*�d�+���/�!�"'�N�3��(��F��U�#�!���4��$��%���t�[�1���8�8�F��'��d�F�N�K�F��~�����&�)�J�",�V�T�":�$�v�,��&�@Q�C��(��F��.�s�D�&�A�K�D�%�
�&�[� ��)�K�D�%�.�t�T�6�B�K�D�%��u�}���e�d�E�]�+�7<�=�!�*�1�-�=�I�=�!*�I��E�%�e�,�E�� �F�4�L�7!�8�M��
>s�H6c
�j�|xsijdi�xsi}|xsijdi�xsi}|xsd|xsddjd�|jd�|jd�fD��djd�|jd�|jd�fD��|xsid	�S)
zPAssemble the tracked combination: detail fields + mood + genre + role + persona.rrrz / c3�&K�|]	}|s�|���y�wr7r8�r9�xs  r2r<z#build_combo_dict.<locals>.<genexpr>Ys����Q��q�1�Q����r@c3�&K�|]	}|s�|���y�wr7r8rs  r2r<z#build_combo_dict.<locals>.<genexpr>Zs����
��a�A�
�rrA)rPrVr@rArW)rFr�)rPrV�persona_details�details_fieldsrrs      r2�build_combo_dictrRs����!�r�&�&�v�r�2�8�b�D�
�
"��'�'���4�:��E���"��
���
�
�Q�t�x�x��'7����6�9J�&K�Q�Q��:�:�
�����+�U�Y�Y�y�-A�B�
�
�"�'�R��rgc���gd�}g}|D]X}|j|�}|s�|jt|t�r|ndj	|��t|�dk\s�Xn|sW|j
�D]D}|jt|t�r|ndj	|��t|�dk\s�Dn|r|j|�dj	d�|D��xs|}	|	d|S)uPBuild a short, readable theme slug purely from already-resolved data —
    no LLM call needed. detail_fields already carries real variety via its
    roundrobin/random selectors (hero, setting, festival, sweet, mystery,
    trope, etc.); combining the most 'subject-like' ones with the mood gives
    a distinct, human-readable premise for free. combo_hash (the actual
    dedup mechanism used by check_combo_used) never reads this field — it
    only makes the 'Already Produced Themes' prompt block readable for the
    agents, so there is nothing here that needs an LLM to invent.
    )
�hero�mystery�trope�sweet�festival�animals�domain�topicrc�settingr�r � · c3�&K�|]	}|s�|���y�wr7r8�r9�ps  r2r<z#build_theme_slug.<locals>.<genexpr>�s����-�Q�1�q�-�rN)rFrIr`r�r�rKr�)
rorV�
detail_fields�max_len�preferred_keysrrer�r��slugs
          r2�build_theme_slugr(as����N�
�E��������$����
���J�s�C�0�S�d�i�i��n�E��u�:��?��
���%�%�'�	�A��L�L�j��C�0��d�i�i��l�C��5�z�Q���	��
���T���;�;�-�%�-�-�5��D����>�rgc�p�t|�}	|dk(r1tjt�d�||d��}|j	�n0tj
t�d�||d��}|j	�|j
�S#t$r)}td|�d|���dt|�d	�cYd
}~Sd
}~wwxYw)zFTalk to the server's theme tracker (/api/themes). Returns parsed JSON.rLz/api/themes�)�params�headers�timeout�r)r,r-z[theme] �	 failed: F)�ok�errorN)
�auth_headers�requestsrF�BASE_URL�raise_for_status�postr)�	Exceptionr-r�)r�token�payloadr,r:r1s      r2�	theme_apir:�s����5�!�G�.��V������*�K�(��'�SU��A�
��� ��
�
��*�K�(�w��QS��A�
��� ��v�v�x����.�
����	�!��-�.��c�!�f�-�-��.�s�A5B�	B5�B0�*B5�0B5c�r�td||t��}|jd�r|jdg�SgS)zEReturn the list of already-logged theme records for the window scope.rL)�scope�limitr0�themes)r:�SELF_CHAT_THEME_LIMITrF)r8r<�datas   r2�fetch_used_themesrA�s3���V�U�%�7L�M�D�%)�X�X�d�^�4�8�8�H�b�!�;��;rg�roundc�~�td|fd||d�|��}|jd�rt|jd��SdS)N�check)�	operationr<�levelr0r�F)r:rFr�)r8�combor<rFr@s     r2�check_combo_usedrH�sJ������")��e��GL��D�&*�X�X�d�^�4����� �!�>��>rgc	�z�|syg}|D�]�}g}|jd�r|jd|d���|jd�r|jd|d���|jd�r|jd|d���|jd�r|jd	|d���|jd
�rg|jd
�dk7rS	tj|d
�}|r8|jdd
j
d�|j�D��z�|jd�r|jd|d���|s��J|jd�xsd}|jd|�d�dj
|�z����|rdj
|�SdS#tt
f$ri}Y��wxYw)zARender the shared 'Already Produced Themes' block for the prompt.�%None yet — everything is available.rPzgenre: rVzmood: r@zrole: rAz	persona: rWz{}z	details: r�c3�0K�|]\}}|�d|�����y�w)�=Nr8)r9r�r�s   r2r<z%format_theme_block.<locals>.<genexpr>�s����3W�4�1�a�q�c��1�#�J�3W�s��themeztheme: �statusrz  - (z) � | r�)rFrIr)�loadsr�r�r�rG)�recordsr�r:�bits�detrNs      r2�format_theme_blockrT�s����6��E�
�<�����5�5��>��K�K�'�!�G�*��.�/��5�5��=��K�K�&��6���,�-��5�5��=��K�K�&��6���,�-��5�5����K�K�)�A�i�L�>�2�3��5�5������i� 0�D� 8�
��j�j��9��.������K�$�)�)�3W�3�9�9�;�3W�*W�W�X��5�5��>��K�K�'�!�G�*��.�/������x��&�B��
���u�V�H�B�'�%�*�*�T�*:�:�;�-<�. %�4�9�9�U��Q�*Q�Q���z�*�
���
�s�F&�&F:�9F:c��tjj|�std|���giiifSt	|dd��5}	tj|�}	ddd�tt�r�t|jd�xsg�}|jd	�xsi}|jd
�xsi}|jd�xsi}|jd�xsi}|rtj|�||||fSt|�iiifS#t
j$r,}td|�d|j���td�|�d}~wwxYw#1swY��xYw)
NzTasks file not found: r:r$r%z[config] Invalid JSON in r'rEr��genre_checklists�genre_persona_map�persona_pool�master_details)�osrz�isfiler-r(r)r*r,�msg�
SystemExitr`rar�rFr�rj)	�
tasks_filer0r@r1r�rl�persona_maprXr�s	         r2�load_config_filer`�sJ��
�7�7�>�>�*�%�
�&�z�l�3�4��2�r�2�~��	
�j�#��	0�'�A�	'��9�9�Q�<�D�'��$����T�X�X�g�.�4�"�5���X�X�0�1�7�R�
��h�h�2�3�9�r���x�x��/�5�2�����*�+�1�r����!�!�&�)��j�+�|�;�;����r�2�r�)�)���#�#�	'��-�j�\��A�E�E�7�C�D��Q�-�Q�&��	'��'�'�s*�E�D�E�#'E
�
E�E�Ec�x�
�i}tjr�i}i}ttj�\}}}}tj}|j|�t	||�t	||�tj
r�tt�\}}	}
}|D�chc]}|d��	c}�
|j�
fd�|D��|j|	�tj�d�}nKtti�}tti�}tt�\}}	}
}|j|	�t}|||||fScc}w)Nroc3�2�K�|]}|d�vs�|���y�w)roNr8)r9r��existings  �r2r<zload_tasks.<locals>.<genexpr>�s�����J�q��&�	��0I��J�s��z + defaults)�args�configr`rjrb�defaults�DEFAULT_TASKS_FILE�extendr3�GENRE_PERSONA_MAP_FILE�PERSONA_POOL_FILE)rlr_rXr��cfg_checklists�cfg_persona_map�cfg_persona_poolrdrf�def_checklists�def_pmap�	def_ppoolr�rcs             @r2�
load_tasksrq�s#����J��{�{�����CS��K�K�D
�@��~��0@��������.�)�	�;��0��<�!1�2��=�=�<L�"�=�9�H�n�h�	�,1�1�a��&�	�1�H��L�L�J�H�J�J����n�-����}�K�0�F�$�%;�R�@��%�&7��<��5E��6
�2��~�x��	���.�)�#���&�*�k�<�?�?��2s�D7c�,�|xsij|�}|s*tj|�xsi}|j|�}|s)tjdi�j|�xsg}|jd�djd�|D��S)z�role is 'editor' or 'moderator'. A task's own checklist wins, then the
    genre's entry, then the 'default' entry, then nothing.ruRemove any out-of-character planning or meta-discussion between the collaborators (e.g. 'let's write about X', 'I'll cover Y, you do Z') that is not part of the narrative/content itself — the final piece must read as continuous, in-universe content only.r�c3�&K�|]	}d|�����y�w�r�Nr8)r9r�s  r2r<z checklist_for.<locals>.<genexpr>s����3�T�r�$��[�3���)rF�GENRE_CHECKLISTSrIr�)rPr@�task_checklistrGr�s     r2�
checklist_forrxs����
!�r�&�&�t�,�E�� �$�$�U�+�1�r���	�	�$���� �$�$�Y��3�7�7��=�C���	�L�L�	K���9�9�3�U�3�3�3rg)i�	i�	)i	i	)�bengali�hindic����|xsdj�j�}tj|�}|sy|\��t	j
dd|�}t
d�|D��}|dk(ryt
��fd�|D��}||zdkDS)	NrTz(?s)<small.*?</small>c3�BK�|]}|j�s�d���y�w�rEN)�isalpha)r9�chs  r2r<z(check_language_script.<locals>.<genexpr>&s����9�b�B�J�J�L��9�s��rFc3�R�K�|]}�t|�cxkr�ks�nnd��� y�wr})�ord)r9r�hi�los  ��r2r<z(check_language_script.<locals>.<genexpr>)s!�����?�R�2��R��+>�B�+>�q�?�s�'�
'g�?)r�r��_SCRIPT_RANGESrF�rer��sum)	ru�language�lang_key�rng�body�
total_letters�script_charsr�r�s	       @@r2�check_language_scriptr�s������B�%�%�'�-�-�/�H�
�
�
�X�
&�C���
�F�B��
�6�6�*�B��5�D��9�D�9�9�M������?�4�?�?�L��=�(�C�/�/rgc
�&�d�}td�tdtt��dt���td�t	td�D�]�\}}|d}|jd�xsd	}|jd
�xsg}|jd�xsg}|jd�xsd
g}|jd�xsd}|jd�xsi}	|	rdnd}
|jd�xsd}td|�d|���td|�d|
�d��tdt
||����td|���tddj|����|D]I}|xsdj�j�tvrtd|�d���;td|�d���K|D]7}
d}|
j�j�d k(rd!}td"|
�|����9td#dj|����td$|jd%�xst�d&��|jd'�r#td(|jd)�xsd�d*��ntd+�t|t�rb|D�cgc]&}t|t�r|jd,d-�nd-��(}}td.t|��d/djd0�|D�����ndt|t�r*td.t|��d/dj|����n*td.|rd1tt|��zd2znd3���td4�t|t!|d5|	���td6�t|t!|d7|	���td8�td9t���td:tt"��d;��td<tt$��d=��t����td�td8�td9t���td>t&���t(j*j-t&�std?�ntd@tt.��dA��t0hdB�t2hdC�t4hdC�t6hdD�i}|j9�D]�\}}t(j*j;|�}t(j*j-|�stdE|�dF���Tt=|dG�H�5}t?tAjBdI|jE���}dJdJdJ�|z
}|rtdE|�dKtG|������tdE|�dLt|��dM����t�yJcc}w#1swY�XxYw)Nz�Print the full plan for every task and validate the environment without
    making any LLM call. Exits before any session is created.c�N�djd�|j�D��S)Nr�c3�&K�|]	}d|z���y�w)z    Nr8)r9�lines  r2r<z.run_dry_run.<locals>.indent.<locals>.<genexpr>2s����E�4��$��E�ru)r��
splitlines�rus r2�indentzrun_dry_run.<locals>.indent1s���y�y�E�4�?�?�3D�E�E�ErgzD====================================================================uDRY RUN — � task(s) from zNo LLM calls will be made.
rErorPrxrsrprvrwrWrryz
genre/defaultr{Fz	=== Task r'z  genre:       z   (checklist source: �)z  path:        z  inactive:        z  languages:   r�z               - 'z.' -> script enforcement active (bengali/hindi)z' -> no script check (unmapped)�audiouG   WARNING: no audio tool exists — round will be skipped by the guardz  mediums:     z  roles:       z  turns:       r}z
 per agentr~u  research:    YES — first rz( turn(s) of each agent are research-onlyu9  research:    no — direct content turns from the startr��?z  details:     z structured field(s) -> c3�2K�|]}t|����y�wr7)r�)r9r�s  r2r<zrun_dry_run.<locals>.<genexpr>as����Qh�]^�RU�VW�RX�Qh���z	present (z chars)�EMPTYz  editor checklist (resolved):rz!  moderator checklist (resolved):r�ENVIRONMENTz  tasks source:        z  persona map:         z genre mapping(s) activez  persona pool:        z" relationship category(ies) activez  genre_checklists:    uE                         MISSING — falling back to empty checklistsz!                         loaded (z
 genre(s))>�%mood%�%task%�%_lang%�%themes%�	%details%�	%mediums%�%kaya_role%�%kolpo_role%�%kaya_persona%�%relationship%�%kolpo_persona%>�%genre%r�r��
%language%�%checklist%>�%cast%r�r�r�r�r�z  prompt file z	: MISSINGr$r%z	%[a-z_]+%Nz!: ok, but UNHANDLED placeholders z: ok (z! placeholder(s) replaced by code))$r-rK�TASKS�TASKS_SOURCEr�rF�resolve_story_pathr�r�r�r�r�r`rLrar�rx�GENRE_PERSONA_MAP�PERSONA_POOLrirZrzr[rv�SELF_CHAT_PROMPT_FILE�EDITOR_PROMPT_FILE�MODERATOR_PROMPT_FILE�CRITIQUE_PROMPT_FILErG�basenamer(r�r��findall�read�sorted)r�rCr�rorPrsrprvrWryrdr{�lang�medium�flag�dr��handledrz�placeholdersr�r0�found�	unhandleds                        r2�run_dry_runr�-sT��F�
�(�O�	�L��U���N�<�.�
A�B�	�
(�)��u�a�(�?�	��T��F�|������!�.�Y���(�(�9�%�+����H�H�[�)�/�R�	�����!�-�f�X���(�(�9�%�+����H�H�[�)�/�R�	�$��/���8�8�J�'�0�5��
�	�#��b���'�(�
���w�&<�V�H�A�F�G�
�� 2�4�� ?�@�A�B�
�#�H�:�.�/�
���	�	�)� 4�5�6�7��	R�D��
��!�!�#�)�)�+�~�=��(���.\�]���*�4�&�0O�P�Q�
	R��	4�F��D��|�|�~�#�#�%��0�`���O�F�8�D�6�2�3�		4�
	���	�	�%� 0�1�2�3�
������ 1� K�5K�L�J�W�X��8�8�J���/����9I�0J�0O�a�/P�Px�y�
�
�M�N��g�t�$�LS��GH�j��D�&9����f�c�"�s�B��E��
�!�#�g�,��/G��	�	�Qh�bg�Qh�Hh�Gi�j�
����
&��!�#�g�,��/G��	�	�RY�HZ�G[�\�
�
�!�QX�+��C��L�0A�"A�I�"M�^e�!f�g�
�	�.�/�
�f�]�5�(�I�>�?�@�
�1�2�
�f�]�5�+�y�A�B�C�
�m��
�'��~�6�7�
�%�c�*;�&<�%=�=U�V�	
�	�%�c�,�&7�%8�8Z�[�	
�	��?�B
�(�O�	�-��	�#�L�>�
2�3�	�#�$9�#:�
;�<�
�7�7�>�>�/�0�
�U�V�
�1�#�6F�2G�1H�
�S�T�	� 
�	�
�	� 
�	�
�9$�G�J&�m�m�o����l��w�w����%���w�w�~�~�d�#��N�4�&�	�2�3��
�$��
)�	<�Q���
�
�<�����:�;�E�	<��L�(�	��� ���&G��y�HY�GZ�[�
�
� ���f�S��Z�L�8Y�Z�
�� 
�G��u�^	<�	<�s�8+V�.V�V	)rruকায়াuকল্পuकायाuकल्पc�~��|xsdj���rt��dkrygd�}t�fd�|D��S)a'Detect a generic search query that grounds a citation in nothing real.

    Suitable-for/recent/kids/lighthearted phrasing (or a very short query) means
    the model searched for "something" rather than a concrete reported event, so
    the resulting citation is decorative, not grounding.
    r�T)z"\brecent\b[^.,]*\bsuitable\s+for\bz\bsuitable\s+for\bz\bfor\s+(kids|children)\bzI(latest|top|interesting|random|lighthearted)\s+(news|story|article|event)z\bnews\b[^.,]*\bfor\bc3�h�K�|])}tj|�tj����+y�wr7)r��search�I)r9r#�qs  �r2r<z(_is_placeholder_query.<locals>.<genexpr>�s"�����6��r�y�y��A�r�t�t�$�6�s�/2)r�rKrH)�query�genericr�s  @r2�_is_placeholder_queryr��sA���
��"����A���A������G��6�g�6�6�6rgc�J�g}d|vr|jd�d|vr'tjd|�s|jd�dD])}d|�d�|vs�d|�d�|vs�|jd	|�d
���+tjd|�r'tjd|�s|jd�|��tjd
|�}t	|�}|D�	cgc]	}	|	|vs�|	��}
}	|
r|jt|
��d��|rI|rG|j
�D��chc]\}}|��	}
}}|
r#td�|
D��r|jd�t||�s|jd|�d��tjdd|�}tD]E}tjdtj|��d�|�s�1|jd|�d���Gd|vr?tjd|�}|D]$}|jd|j�����&|Scc}	wcc}}w)aUDeterministic (no-LLM) checks that catch the failure classes an editor/
    moderator LLM keeps missing: declared medium never delivered, header fields
    dropped during editing, citations dropped or fabricated, ungrounded
    citations, wrong script/language, and agent-name leaks. Returns a list of
    problem strings (empty = all good).r�u�Medium 'audio' was declared, but no audio-generation tool exists yet in TOOLS — this task cannot currently be fulfilled by the agents.rtz!\[[^\]]*\]\([^)]+\)zHMedium 'image' was declared but no image is embedded in the final story.)zTask prompt�Genre�MediumszLanguage(s)z**z:**zEditor dropped the 'z' header field.z"#+\s+Citations?\s*&?\s*References?z2Editor dropped the Citations & References section.z \[[^\]]*\]\((https?://[^)\s]+)\)zC citation URL(s) in the story were never retrieved by a web search.c3�2K�|]}t|����y�wr7)r�)r9r�s  r2r<z*verify_task_fulfillment.<locals>.<genexpr>�s����I�A�4�Q�7�I�r�z�Citations are ungrounded: every search used a generic placeholder query instead of sourcing the story from a real reported event.zLStory does not appear to be predominantly written in the declared language 'z'.�*(?s)<small.*?</small>|!\[[^\]]*\]\([^)]*\)rz\bzProhibited name 'z"' still appears in the story text.z<!-- EDITOR FLAG:z<!--\s*EDITOR FLAG:\s*(.*?)-->z&Editor flagged an unresolved problem: )
rIr�r�r�r�rKr��allr�r��_PROHIBITED_NAMES�escaper�)�
original_text�
check_textrsr��retrieved_citations�problems�field�	published�backed�u�unbacked�_r��queries�stripped�bad�flagsr�s                  r2�verify_task_fulfillmentr��s@���H��'�����
R�	
�
�'��"�)�)�,C�Z�"P����V�	
�D�K��
��w�c�?�m�+�"�U�G�3��z�0Q��O�O�2�5�'��I�J�K�
�y�y�-�}���i�i�=�z�J����L�M��&��J�J�B�J�O�	��(�)��(�<�!�A�V�O�A�<��<���O�O��x�=�/�!d�e�
���%8�%?�%?�%A�B�T�Q��q�B�G�B��3�I��I�I����V��
!��X�6����Z�[c�Zd�df�g�	
��v�v�C�R��T�H� �Y��
�9�9��2�9�9�S�>�*�"�-�x�8��O�O�/��u�4V�W�X�Y��j�(��
�
�<�j�I���	U�D��O�O�D�T�Z�Z�\�N�S�T�	U��O��9=��Cs�	H�H�Hc�|�|sytd|j�|j��j�|kDS)NF)rr��ratio)�new_text�
previous_text�	thresholds   r2�is_duplicater�s:������h�n�n�.�
�0C�0C�0E�F�L�L�N�
�	�rg�<c	���	|jd�d}tjtj|dt|�dzzz��}t
|jdd��S#t$rYywxYw)z;Expiry timestamp of a JWT access token, or 0 if unreadable.�.rErLr"�expr)	r�r)rP�base64�urlsafe_b64decoderKr�rFr7)r8r9r@s   r2�_decode_expr�+sq����+�+�c�"�1�%���z�z�&�2�2�7�S�S��\�M�TU�DU�=V�3V�W�X���4�8�8�E�1�%�&�&������s�A)A,�,	A8�7A8c��ddlm}	|||�}t5||t|�t
j�d�t|<|t|<ddd�|S#t$r}td|�d|����d}~wwxYw#1swY|SxYw)Nr)�oidc_password_grantz,[login] Authentik password grant failed for r')�passwordr8r��
granted_at)	�server.authr�r7r-�_TOKEN_LOCKr��time�_TOKEN_META�_TOKEN_OWNER)�usernamer�r�r8r1s     r2�loginr�5s���/��#�H�h�7��
�'� ���u�%��)�)�+�	!
��H��'��U��'��L����
�<�X�J�b���L�M�
����'��L�s"�	A�3A7�	A4�A/�/A4�7Bc�j�t5tj|�}|�|cddd�St|}t	j�}|d}|xr||t
z
k\xs|xr||dz
t
k\}|s|dcddd�St
d|�d��t||d�cddd�S#1swYyxYw)aReturn the CURRENT token owned by whoever issued ``token``.

    Re-grants only when that token is at ``TOKEN_REFRESH_MARGIN`` seconds from
    expiry (or when its expiry is unknown and it is older than the margin).
    Unknown tokens pass through untouched.
    Nr�r�r8z[auth] u-'s access token expired — re-authenticatingr�)r�r�rFr�r��TOKEN_REFRESH_MARGINr-r�)r8r�r��nowr��expireds      r2�_fresh_tokenr�Hs���
�2��#�#�E�*�����	2�2�
�H�%���i�i�k���E�l��
�
6�S�C�"6�6�6�
O���M�C�%��"5�5�9M�M�	����>�2�2� 	���z�!N�O�P��X�u�Z�0�1�#2�2�2�s�B)�AB)�B)�)B2c�\�t5t|�}ddd�dd|��iS#1swY�xYw)zCHeaders carrying the Authentik access token as a Bearer credential.N�
AuthorizationzBearer )r�r�)r8s r2r2r2cs6��	�$��U�#��$��w�u�g�.�/�/�$�$�s�"�+c���d|i}|r||d<|r||d<|r||d<tjt�d�|t|�d��}|j	�|j�dS)	Nr��system_prompts�context_tokens�
system_promptz
/api/sessionsr*r.�
session_id)r3r6r4r2r5r))r8r�rrrr��resps       r2�create_sessionrjs{��
�D�>�D��!/��
���!/��
��� -��_���=�=��*�M�"�
��U�#��	�D�	�����9�9�;�|�$�$rgc���tjt�d|��t|�d��}|jdk7rtd|�d|j�d��ytd	|���y
)Nz/api/sessions/r*�r,r-��z"Warning: could not delete session z (HTTP r�FzDeleted session T)r3�deleter4r2�status_coder-)r8rrs   r2�delete_sessionr
~ss���?�?��*�N�:�,�/��U�#���D�
���3��
�0���G�D�DT�DT�CU�UV�W�	
��	��Z�L�
)�*�rgc��|sy|jd�d}tjjd�}tjj	||�}tjj|�syt
|d�5}tj|j��j�cddd�S#1swYyxYw)N�/output/r��~/local-ai-files/ComfyUI/output�rb)r�rZrz�
expanduserr�r[r(r��	b64encoder��decode)�	image_url�rel_name�comfy_output�abs_pathr0s     r2�image_url_to_b64r�s��������z�*�2�.�H��7�7�%�%�&G�H�L��w�w�|�|�L�(�3�H�
�7�7�>�>�(�#��	
�h��	�3���������)�0�0�2�3�3�3�s�1B>�>Cc��	tjt�d�||xsgd�d��y#t$r}t	d|���Yd}~yd}~wwxYw)Nz/api/register-agent)�tokens�	usernamesr)r)r-z([wait] Could not register agent tokens: )r3r6r4r7r-)rrr1s   r2�register_agent_tokensr�sR��>��
�
��j�+�,�"���b�A��	
��
�>�
�8���<�=�=��>�s�&)�	A
�A�A
c���	tjt�d�d��}|j�jdg�}|S#t$r}td|���gcYd}~Sd}~wwxYw)Nz/api/active-usersr)r-�usersz%[wait] Could not check active users: )r3rFr4r)r7r-)r:rr1s   r2�active_real_usersr �sc����L�L�H�:�%6�7��D��������W�b�)���L����
�5�a�S�9�:��	���s�>A�	A%�A �A%� A%c��yr7r8r8rgr2�wait_for_user_to_leaver"�s��
rgc���t|�}||tj�j�j	d��d�}t
jrd|d<|r||d<|rd|d<|rd|d	<tjt�d
�||d��}|j�|j�d
}	t�d|	��}
	tj|
t|�d��}|j�|j�}|jd�}
|
dk(r'|d|jd�|jd�d�S|
dk(rtd|����tjt ���)N�seconds��timespec)r�message�client_timestamp�gpu�modertT�no_toolsr~z	/api/chatr!r.�task_idz/api/status/�(r	rN�done�response�_search_details)rurt�searchesr1z
Task failed: )r2rr��
astimezone�	isoformatrdr)r3r6r4r5r)rF�RuntimeErrorr��sleep�POLL_INTERVAL_SECONDS)r8rr'�	image_b64r+r~r,r9�submit_respor,�
status_url�status_respr@rNs              r2�call_llmr;�sr���5�!�G�!��$�L�L�N�5�5�7�A�A�9�A�U��G�
�x�x������$�����"��
���"��
���=�=��*�I��
���	�L��!�!�#����!�)�,�G��:�\�'��3�J�
��l�l���U� 3�R�
��	�$�$�&����!�����(�#���V���Z�(����'�*� �H�H�%6�7��
�
�W����t�f�5�6�6��
�
�(�)�'rgc�F�t|}t|dk(rdnd}|�t}d|�d|�d�d|�d|�d�d	d
g}
|r|
j|�|�|
j|�|	dk(r|
jd|�d
��nN|dkr|
jd�n7||dz
k\r|
jdt�d��n|
jd|�d��|	dk(r|
r||
dzk(r|
jd�|r|
jd|�d��|r|
j	dd|g�dj|
�S)Nrrz)[SYSTEM DIRECTIVE: You are responding as z. Your partner is z.]
z[Turn rz]
z][SYSTEM DIRECTIVE: Place ALL meta-analysis, praise, and planning OUTSIDE the [CONTENT] tags. zOThe [CONTENT] block must ONLY contain clean narrative/visual deliverable text.]r~al[RESEARCH MODE: This turn is for research ONLY. Perform web searches and fetch pages to gather sourced facts, figures, and material needed for the task. Do NOT write any final story or deliverable content yet, and do NOT open a [CONTENT] block this turn. Write your findings and their sources in plain text so your partner can read them, then end with [NEXT TURN: z].r zaImmediately establish your role and provide the first creative deliverable inside [CONTENT] tags.zy[PHASE 3: FINALIZATION] Consolidate the work, write the final scene/panels, and terminate the turn sequence by appending r�z�[PHASE 2: DIRECT EXECUTION] Continue building content turn-by-turn. Do not send meta-talk or prematurely end the story. Speak in �contentrEz�[CONTENT MODE: Research is complete and all gathered materials are shared above. Now write the actual deliverable story using those materials, wrapping every publishable part in [CONTENT]...[/CONTENT].z[DYNAMIC TURN ATTRIBUTES: �]rz
----------r�)�AGENT_NAMESr�rI�STOP_PHRASErhr�)�speaker�message_number�incomingr�ror|r}�per_turn_detailsr�r*r�
current_agent�
partner_agentr�s              r2�build_inputrG�s��� ��(�M��w�#�~��3�?�M��}�&��4�M�?�BT�Ub�Tc�cg�h�
�� ��%���,�g�Y�	
�E��
���T����
���W���z��	���
 �
!.��b�
2�	
�
�1�	�
���o�	
�
�5�1�9�	$�
���<�<G�=��
K�	
�
	���L�LP�6�QR�
T�	
�
�y��^��.�ST�BT�0T�
���
T�	
��
���1�2B�1C�1�E�F��
���b�,��1�2��9�9�U��rgrEc��tj|t|�dkDrdnd�}tj|�}|
�t	t
|t|�\}}}n|
\}}}|||d�}|jdi�}|jdi�}td|�d|�d|�d	��td
|jd��d|jd
����td|jd��d|jd
����tt||	|��}|r td|j�d�d��tjd|�}|jddj|��}|jd|�}|jd|xsd�}|jd|xsd�}|jd|�}|jd|�}|jd|jdd��}|jd|jd
d��}|jd|jdd��}|jd |jd
d!��}t|�t|t d"�d#|��|�$�}t|t d%�d#|��|�$�} g}!d&d&d'�}"|�t"}d"}#d(}$d}%t%|||||||||�	\}&}'i}(	|"|#xxdz
cc<|"|#})t|!�}*|#d"k(r|n|}+|#d"k(r|n| },d(}-d}.|r�i}/t't(�D]M}0t+|	|t,d)�*�}/t/|||i|xsi�|/��}1t1||1d+�,�sntd-|0dz�d.���Otd/�t3|	|t,d)|/�0�}-t5||i|xsi�|/��}2t7d1|fd1t8d+|2d2�1��}3|3jd3�r2|3jd4�xsijd5�}.td6|.���|xr|)|k}4|4rd7nd8}5|r|nd&}6t;|#|)|!sd(n|$|||||-||5|6�9�}7t=�t?|+|,|7|%|4�:�}8|8d;}9|9jA�se|7d<z
}7t?|+|,|7|%|4�:�}8|8d;}9|9jA�s;|.rt7d=|d=|.�>�td?|.�d@��tdA|�dBt |#�dC���notC|9|$�r|7dDz
}7t?|+|,|7|%|4�:�}8|8d;}9|.rt7d=|d=|.�>�td?|.�d@��t |#|)|9|8jdE�|8jdF�|4dG�}:|!jE|:�tG|:|'|(|&||*�tH|9jK�vrtdA|�dHt |#�dI���n�|"|#|k\rtdA|�dBt |#�dJ|�dK���n�|9}$|8jdE�};|;rtM|;�}%|$dL|;�dM�z
}$|8jdF�}<|<�rg}=|<D]�}tO|tP�s�|jdNd(�}>|=jEdO|>���|jdP�xsgD]�}?|?jdQ�xs|?jdR�xsd(}@|?jdRd(�}A|?jdS�xs|?jd8�xsd(ddT}BdU@��|BrdVB��nd(z}CAr	CdA�d	�z
}C|=jEC�����|=r|$dWdIj|=�zz
}$|#d"k(rd%nd"}#tdXtR�dY��tUjVtR�tdZ���UtY|'|&|(�td[�t[|'d\d]�^�5}D|Dj]�}Eddd�tj||f| |fg�\}F}Gt_|G|F|||E�}@td_|@�dI��ta|@|&|'�\}&}'td`|'�dI��tda�tc|&|'|||||| ||||
||(�b�}Htdc�t[|'d\d]�^�5}D|Dj]�}Iddd�HrHn|'}Jt[|Jd\d]�^�5}D|Dj]�}Kddd�teIK|||(�}L|Lr�tddtL��de��|LD]}Mtdf|M����HrHn|'jdgdh�}NdidjdIjdk�LD��z||tgjh�jk�jmdl�m�dn�}Ot[|Ndod]�^�5}DtojpO|Dd�p�ddd�ntdq�tdr�|!|| |'fS#1swY���xYw#1swY��5xYw#1swY��xYw#1swY�6xYw)sNrEr )�relationshiprVrWrrz[persona] Genre: z | Dynamic: � (r�z[persona] Kaya: r@u — rAz[persona] Kolpo: z[cast] z ...r�r�� , r�r��Noner�rJr�r�r��Partnerr��Creativer�r��
Methodicalrz round �rrrrrr��r��turn)rFz/[theme] Turn combination already used (attempt z); re-rolling per-turn detailszQ[theme] Exhausted per-turn re-roll attempts; proceeding with the last combination�r�r��log)rEr<rFrMr0rM�idz"[theme] Reserved turn combination r~r=)rDr�r*r)r7r~ruzQ
[SYSTEM ERROR: Your previous output was empty. Generate real story content now.]�complete�rE�theme_idz[theme] Marked turn �
 completedzRound z ended: z# returned no content after a retry
zc
[SYSTEM ERROR: Your previous output was identical to your partner's. Generate unique content now.]rtr1)rAr'rurtr1�publishz
 ended by r�z
 reached the z
-message cap
z

[IMAGE SHARED: r>r�z	- Query: �results�title�url�snippetr
z  - rOz

[WEB SEARCH REPORTS SHARED:]
z
LLM Rest for � secondsz
LLM Rest Overz=== Title phase ===r:r$r%z
Story title: zStory renamed to: u7=== Cross-critique phase (Kaya↔Kolpo self-verify) ===)rsr�rWryr��	citationsz"=== Deterministic verification ===z	[verify] u< problem(s) found — auto-RED, skipping moderator LLM call:z
[verify]   - �.md�.moderation.json�REDz2Automatic RED (deterministic check, no LLM call):
c3�&K�|]	}d|�����y�wrtr8r"s  r2r<z*run_single_conversation.<locals>.<genexpr>�s����3�Q�"�Q�C��3�rur$r%��verdict�reasonsrorP�	timestamp�w�r�z=== Moderator phase ===z+Moderator Phase Skipped, not much value add)9rMr�rKr]r^r�r�rFr-r�r�r��STARTING_CONVERSATION�replacer�rr?r��start_storyr��MAX_THEME_REROLLrr�rrHr	r(r:�SELF_CHAT_THEME_SCOPErGr"r;r�r�rI�append_story_entryr@�upperrr`ra�SLEEP_BETWEEN_TURNSr�r5�finalize_storyr(r��
propose_title�apply_title�run_cross_critiquer�rr�r2r3r)�dump)P�token_a�token_b�round_numberrorsrprvrPrWr�ryrzr|rA�themes_contextr}r;r��
per_turn_taskr~rr�r�rIrVr�persona_info�	kaya_info�
kolpo_info�
cast_blockr��	session_a�	session_b�
transcript�counts�current_speakerrC�shared_image_b64�stories_dir�fnamer`rBrCr8�session�per_turn_str�
turn_theme_id�turn_fields�attemptrG�	turn_slug�logged�in_research_phaser*�eff_research_turns�prompt�result�replyr��shared_image�shared_searches�blockr�r:r\r]r^r�r0�
story_text�
title_session�title_token�edited_pathr��check_sourcer�r�r#�verdict_pathr@sP                                                                                r2�run_single_conversationr�>s�
��.�]�]�7��W���)9�A�q�
A�F��}�}�Y�'�H���.F��%�!2�J�/
�+��d�O�/6�+��d�O�%��"��L� �#�#�F�B�/�I� �$�$�W�b�1�J�	��e�W�L���b���a�
H�I�	��Y�]�]�6�2�3�5����y�9Q�8R�
S�T�	��j�n�n�V�4�5�U�:�>�>�)�;T�:U�
V�W�#�:�d�L�,�#O�P�J��
��
�-�-�/��2�3�4�8�9��%�%�h��5�A�	�	�	�+�u�z�z�&�1�2�A�	�	�	�)�X�&�A�	�	�	�+�w�0�&�1�A�	�	�	�*�n�W�0W�X�A�	�	�	�"�L�1�A�	�	�	�(�D�!�A�	�	�	�-����v�y�!A�B�A�	�	�	�"�I�M�M�)�Z�$H�I�A�	�	�	�.�*�.�.���"C�D�A�	�	�	�#�Z�^�^�I�|�%L�M�A�	�!�H����s��
�G�L�>�2���I�
���s��
�G�L�>�2���I��J��1�
�F��}�&���O��H���$��d�D�&�(�E�5�$����K���I�
����1�$����0���*�o��*�c�1��w��.�#�5�)�9�����
���K� �!1�2�
��4� �$��J���)���#�;��*��;�{�;�	��(���f�E���E�g�PQ�k�]�S2�3��
�"�g��+����&�%��L�)��d�C�� 2��C�{�C��I����� �+���
���F��z�z�$��!'���G�!4�!:�� ?� ?�� E�
��:�=�/�J�K�
%�I��>�)I��.�z�I��/7�^�Q����� �B�h�����)���-�
��	� �����&�&�
���v����{�{�}��j�j�F�����*�*��F��6�N�E��;�;�=� ��"��",�!.�	��0���z�J�K���\�N�(�;��3O�2P�Q:�;�����x�(��|�|�F�����*�*��F��6�N�E���j�'�Z�-�X��(���z�B�C�#�?�3�%���Z�Z��(��
�
�:�.�,�,�

��	���%� ��5�%��K��s�S��%�+�+�-�'��F�<�.�
�;��3O�2P�PR�S�T���/�"�e�+�����h�{�?�/K�.L�M�$�g�^�5�
�
����z�z�'�*���/��=���-�l�^�1�=�=�H� �*�*�Z�0����E�$�
'��!�!�T�*�����g�r�*�����y���0�1����y�)�/�R�'�A��E�E�'�N�@�a�e�e�E�l�@�b�E��%�%��r�*�C� �u�u�Y�/�I�1�5�5��3C�I�r�4�C�P�G�!�%��>��s�7�)�_�R�P�D���"�S�E���+���L�L��&�'�
'���@�4�9�9�U�CS�S�S��!0�C�!7�#�S��
�
�1�2�(�;�<��
�
�&�'�
�o��a�d�5�+�y�1�	�
� �	
�e�S�7�	+��q��V�V�X�
��!'���
�W�	�	�7�3�4�"��M�;�
�+�}�d�H�e�Z�X�E�	�M�%���
#�$�$�U�K��?��K��	��u�g�R�
(�)�	�
C�D�$��
��
���������
���K�"
�
.�/�	
�e�S�7�	+�!�q�����
�!�"-�;�5�L�	
�l�C�'�	2��a��V�V�X�
��&��z�6�8�Y��H��
���H�
��&b�c�	
��	'�A��M�!��%�&�	'�'2���F�F��%�
���L��i�i�3�(�3�3�4���!����2�2�4�>�>�	�>�R�

���,��g�
6�	)�!��I�I�d�A�a�(�	)�	)�	�'�(�
�;�<��y�)�U�2�2�}���:!�!�����.	)�	)�s0�=d&�/d3�e�(e
�&d0�3d=�e
�
ec���tj�jd�}d|�d|�d�}t|dd��5}t	j
||d�	�ddd�t
d
|���|S#1swY�xYw)N�
%Y%m%d_%H%M%S�conv_rr�z.jsonrir$r%r"rjzSaved transcript to )rr��strftimer(r)rwr-)r�rzrhr�r0s     r2�save_transcriptr��st������'�'��8�I��\�N�!�I�;�e�4�E�	
�e�S�7�	+�+�q��	�	�*�a��*�+�	� ���
(�)��L�+�+�s�A,�,A5c���tjdd|tj��}tjdd|�jd�}|d|jd�xsdS)Nz[\\/:*?"<>|\x00-\x1f]+r�r��\s+�-�story)r�r��UNICODEr�)rur%r's   r2�slugifyr��sT��
�6�6�+�R��R�Z�Z�H�D�
�6�6�&�#�t�$�*�*�3�/�D����>����$�/��/rgc��|sy|j�jd�j�dj�}tjdd|tj��}tjdd|�}tjdd	|�j�jd
�}|ddj�xsdS)z=Extract a clean single-line title from an LLM reply, or None.Nu"'«»“”‘’`ru*^(?:title|heading|name|header)\s*[::]\s*rr�z^\d+[.)]\s*r�� u.।!�P)r�r�r�r��
IGNORECASE�rstrip)ru�firsts  r2�sanitize_titler��s������J�J�L���5�6�A�A�C�A�F�L�L�N�E��F�F�5�r�5��
�
�
�E�
�F�F�>�2�u�-�E��F�F�6�3��&�,�,�.�5�5�g�>�E���"�:����%��%rgc���d|�d|�d|�d�|xsdddz}t�	t|||�}t|d�xs|S#t$r}t	d	|���|cYd}~Sd}~wwxYw)
z>Ask one of the agents to name the story after it is completed.zPYou are naming a completed article/story that has reached its conclusion.
Task: z
Genre: z
Write your reply ONLY in: u

Read the completed story below, then propose ONE short, catchy, unique title (max 60 characters) that captures its conclusion and theme. Output ONLY the title text — no quotes, no numbering, no explanation, no names of people or agents.

=== COMPLETED STORY ===

rNipruz:[title] Could not generate a title, falling back to task: )r"r;r�r7r-)	r8rror�rPr�r�r�r1s	         r2rtrt�s���	������%�%-�J�/&�			&�*4�)9�r�5�D�(A�		B������%��V�4���f�V�n�-�5��5����
�J�1�#�N�O�����s�A�	A$�
A�A$�A$c�h�t|dd��5}|j�}ddd�d|�d�}r|djd�r||d<n|jd|�t|dd��5}|j	|�ddd�tjd	|�}|s||fS|jd�jd
�jdd�}tjj|�}tjj|t|��d
|���}	|	|k7rttjj|�rUtj ||	�|	}tjj|	tjj#|��}||fS#1swY��|xYw#1swY��-xYw)
zKUpdate the story heading with the new title and rename the folder to match.r:r$r%N�# r�rriz_\d{8}_\d{6}\.md$r�rar)r(�	readlines�
startswithr��
writelinesr�r��group�lstriprlrZrz�dirnamer�r��isdir�renamer�)
r\r�r�r0r��new_headingr�rh�	genre_dir�new_stories_dirs
          r2ruru�sk��	
�e�S�7�	+��q����
����u�g�R�.�K���q��$�$�T�*���a��
���Q��$�	
�e�S�7�	+��q�	���U���	�	�	�&��.�A���E�!�!�����
�!�!�#�&�.�.�u�b�9�I�������,�I��g�g�l�l�9����0@��)��.M�N�O��+�%�"�'�'�-�-��*D�
�	�	�+��/�%�������_�b�g�g�.>�.>�u�.E�F������)�����s�F�*F'�F$�'F1c���|jd�}|rd|vr|�d�}d|vr|�d�}|Sd|vrtstd��tSd|vrtstd��tStS)Nrzr>z/adminr=z/premiumz2STORIES_ADMIN_DIR environment variable is not set!z4STORIES_PREMIUM_DIR environment variable is not set!)rF�ADMIN_STORIES_DIRr��PREMIUM_STORIES_DIR�STORY_BASE_DIR)r�rvrzs   r2r�r��s���8�8�F��D���e���V�6�?�D�����V�8�$�D����%�� ��Q�R�R� � ��E��"��S�T�T�"�"��rgc	
�z�|xst}	tj|	d��tj�}
|
jd�}t
|��d|��}tjj|	t
|��}
tj|
d��tjj|
|�}tj|d��tjj|d|�d|�d��}|xsdg}|r|jdd	�nd	}|r|jd
d	�nd	}d|�d�d
|�d|
jd��d�d|�d�d|�d|�d|�d�ddj|��d�ddj|��d|�d�dg}t|dd��5}|j|�ddd�||fS#1swY||fSxYw)NT)�exist_okr�r��story_rrarwrIzN/ArVr�r�z*Round u · Generated on z%Y-%m-%d %H:%M:%Sz*

z**Task prompt:** �

z**Genre:** u  ·  **Dynamic:** rJ�)

z**For roles:** rKz
**Mediums:** u  ·  **Language(s):** z---

rir$r%)r�rZ�makedirsrr�r�r�rzr�rFr(r�)rzror\rsr�rvrPrzr}�base_dirr�rh�folder_namer�r�r�rTrV�headerr0s                    r2rmrm�s����%�~�H��K�K��4�(�
�,�,�.�C����_�-�I��U�^�$�A�i�[�1�K������X�w�u�~�6�I��K�K�	�D�)��'�'�,�,�y�+�6�K��K�K��d�+��G�G�L�L����~�Q�y�k��&M�N�E���f�X�E�5A�,�
�
�>�5�
1�u�C�.:�<���F�E�*��D�
�U�G�2��
�,��0����>Q�1R�0S�SX�Y�
�D�6��&�
�e�W�/��u�B�t�f�E�B�
�%�*�*�U�+�,�D�1�
��
�
�7�+�,�,C�H�:�T�R���F�
�e�S�7�	+��q�	���V������������s�F.�.F:c��tjdd|tj��}tjdtj|��d�d|tj��}tjdd|tj��}tjdd|tj��}|j	dd�j�S)	Nu.^(kolpo|kaya|कल्प|কায়া):\s*rr��^z:\s*z\[NEXT TURN:\s*[^\]]*\]\s*z\[ACTION:\s*[^\]]+\]r)r�r�r�r�rlr�)rAru�cleaneds   r2�clean_speaker_textr�s����f�f�F��T�Y[�Yf�Yf�g�G��f�f��"�)�)�G�,�-�T�2�B��r�}�}�U�G��f�f�2�B��r�}�}�U�G��f�f�,�b�'����O�G��?�?�/��4�:�:�<�<rgc��tjd�}|jd|xsd�}tjdd|�j�S)u�Drop model-authored image placeholder/reference marker lines.

    The agents occasionally write standalone notes such as ``**(Image
    Reference: the squid from the last turn)**`` or ``**(Image Placeholder:
    /output/...png)**`` instead of (or alongside) a real ``![...](...)``
    embed. These add nothing to the published story, so the whole line is
    removed. Only lines that ARE the marker are stripped — real image embeds
    (`![alt](file)`) and surrounding prose are left untouched.
    zd(?im)^\s*\*{0,2}\s*[\[\(]\s*image\s+(?:reference|placeholder|ref|ph)\s*:[^\]\)]*[\]\)]\s*\*{0,2}\s*$r�\n{3,}r�)r��compiler�r�)ru�patternr�s   r2�strip_image_markersr�%sI���j�j�	,��G��{�{�2�t�z�r�*�H�
�6�6�)�V�X�.�4�4�6�6rgc�f�g}tjd|�D]}||vs�|j|��t|�D]\}}|j	|d|�d��}�tj
dd|tj��}tj
dd|tj��}tj
dd|�}tj
d	d|�}tj
d
d|tj��}|jd�}g}|D]Q}tj
dd
|�}tj
dd|�}|j|j	dd���Sdj|�}t|�D]\}}|j	d|�d�|�}�|j�S)u�Deterministically remove the agents' names from story content.

    The models often address each other by name despite the naming rules, so the
    names are scrubbed here (vocative positions first, bare mentions as fallback).
    Structural markup is protected: image alt-text (`![Kaya](...)`) and the turn
    headers (`<small ...>_Round N · Kaya Turn M_</small>`) keep their names.
    r�zPROTECT�u\b(?:Kaya|Kolpo)\b\s*[,،]+\s*rr�z,\s*\b(?:Kaya|Kolpo)\buF(?:কায়া|কল্প|काया|कल्प)\s*[,،]+\s*u>,\s*(?:কায়া|কল্প|काया|कल्प)z\b(?:Kaya|Kolpo)\br�z	[ \t]{2,}r�u[ \t]+([.,!?;:।])z\1z ,rr)
r�r�rIr�rlr�r�r�r�r�)ru�	protectedr8r�r��
cleaned_linesr�s       r2�scrub_agent_namesr�7s����I����I�4�P�$���	�!����U�#�$��i�(�:���5��|�|�E�[���4�#8�9��:��6�6�3�R��R�]�]�S�D�
�6�6�+�R��R�]�]�K�D�
�6�6�[�]_�ae�f�D�
�6�6�S�UW�Y]�^�D�
�6�6�'��T����G�D�
�J�J�t��E��M��6���v�v�l�C��.���v�v�,�e�T�:�����T�\�\�$��4�5�6�
�9�9�]�#�D��i�(�:���5��|�|�k�!��D�1�5�9��:��:�:�<�rgc�r�|j�D�cgc]'}tjdd|�j���)}}dj	|�}tjdd|�}tjdd|�}tjdd|�}tjd	d|�}tjd
d|�}tjdd
|�}tjdd|�}tjdd|�}tjdd|�}|j�dzScc}w)a�Restore structural line breaks that the editor model may have flattened.

    The editor sometimes returns the revised markdown with most newlines collapsed
    to spaces. This re-inserts line breaks before every structural marker so the
    published page renders as separate blocks again. Within-turn paragraph breaks
    that were already lost cannot be recovered, but every heading, turn header,
    image, and citation line ends up on its own line.
    z[ \t]+r�r�z(?<!\n\n)<small z	

<small z(?<!\n\n)(#{1,6}\s)z\n\n\1zE(?<!\n\n)(\*\*(?:Task prompt|Genre|For roles|Mediums|Language\(s\)):)z(?<!\n\n)(\d+\.\s+\[)z(?<!\n\n)!\[z

![z</small>(?!\n\n)z
</small>

z	\s*---\s*z

---

z(?m)^ +rr�r�)r�r�r�r�r�)rur�r�s   r2�normalize_markdown_linesr�^s��?C�o�o�>O�P�d�R�V�V�I�s�D�
)�
/�
/�
1�P�E�P��9�9�U��D��6�6�%�}�d�;�D�
�6�6�(�)�T�:�D�
�6�6�P����D�
�6�6�*�I�t�<�D�
�6�6�/�8�T�2�D�
�6�6�%�~�t�<�D�
�6�6�,�
�t�4�D��6�6�*�b�$�'�D�
�6�6�)�V�T�*�D��:�:�<�$����)
Qs�,D4c�(�|sy|jd�d}tjjd�}tjj	||�}tjj|�sytjj
|�\}}	d|�d|�d|�|	��}
tjj	||
�}	tj||�td|
���|
S#t$r}td|�d|���Yd}~yd}~wwxYw)	Nrr�r�img_rr�zWarning: could not copy r'zEmbedded image )r�rZrzrr�r[�splitext�shutil�copy�OSErrorr-)
�img_urlr�rzrArCrr�abs_srcr��ext�
local_name�destr1s
             r2�embed_story_imager�s������}�}�Z�(��,�H��7�7�%�%�&G�H�L��g�g�l�l�<��2�G�
�7�7�>�>�'�"��
�W�W�
�
�h�
'�F�A�s����a��y��#��s�e�<�J�
�7�7�<�<��Z�0�D�����G�T�"�
�O�J�<�
(�)����	��
�(��	��A�3�7�8����s�C-�-	D�6D�Dc���|xsgD]r}t|t�s�|jdd�}|jd�xsgD]5}|jdd�}|r||vr�|jd�xs||f||<�7�ty)Nr�rr[r]r\)r`rarF)r`r1r�r�r:r]s      r2�collect_citationsr��s���
�^��<���!�T�"�����g�r�"�����y�!�'�R�	<�A��%�%��r�"�C��#��*���e�e�G�n�3��U�;�I�c�N�		<�	<rgc�p�tjdd|tj��}|j�S)a:Remove any Citations & References block the model wrote into a turn.

    Only finalize_story() may emit that section, and it is built exclusively from
    web-search results. Model-authored variants (images, placeholder links, double
    hashes) are dropped so they can never leak into the published section.
    z8(?i)(?:^|\n)\s*#{1,6}\s+citations?\s*&?\s*references?.*$rr�)r�r��DOTALLr�r�s r2�strip_model_citationsr��s0���6�6�C�
���i�i�	�D��:�:�<�rgc�`�tjd|tjtjz��}|s[tjd|�}|rC|jd�}tjd|d��d}|j�}|r|gng}|syd	jd
�|D��S)uCReturn only the text inside [CONTENT] blocks.

    The closing tag may appear as ``[/CONTENT]`` (as instructed in the system
    prompt) or as the ``[END CONTENT]`` variant the models actually emit; both
    are accepted, so a turn's narrative is never mistaken for planning chatter.
    Returns None if no [CONTENT] block is present at all — the caller treats
    that as a planning-only turn with nothing to publish. Multiple blocks are
    concatenated in order.

    As a defensive fallback, a message that OPENS with ``[CONTENT]`` but is
    truncated or never closes the tag (the research mode used to provoke this)
    still yields its narrative: everything from the ``[CONTENT]`` marker up to
    the next structural tag (``[NEXT TURN:`` / ``[END CONVERSATION]`` /
    ``[IMAGE GENERATION CALL:]``) or the end of the message.z:\[CONTENT\](.*?)(?:\[/CONTENT\]|\[END CONTENT\]|\[END\]|$)r�z(?is)\s*\[CONTENT\]\s*(.*)$rEzj(?is)\s*\[(?:NEXT TURN\s*:|END CONVERSATION\]|IMAGE GENERATION CALL\s*:|THEME LOGGED\s*:|IMAGE SHARED\s*:))�maxsplitrNr�c3�^K�|]%}|j�s�|j����'y�wr7)r�)r9�bs  r2r<z)extract_tagged_content.<locals>.<genexpr>�s����>�Q�A�G�G�I�q�w�w�y�>�s�-�-)	r�r�r�r��matchr�r�r�r�)ru�blocksr��rests    r2�extract_tagged_contentr��s����Z�Z�E���i�i�"�-�-�'��F�
�
�H�H�3�T�:����7�7�1�:�D��8�8�}�����	�D�
�:�:�<�D�#�d�V��F����;�;�>�&�>�>�>rgc���|jdd�}|jdd�}|jd|�}|jd�dur.t||jd��td	|�d
|�d��yt|�}	|	��t||jd��t	|jd�||||�}
|
std
|�d
|�d��ytd
|�d
|�d��d|�d|�d|�d�d|�d|
�d�g}t|dd��5}|j
|�ddd�yt||	�}
t|
�}
t|
�}
t|
�}
d|�d|�d|�d�|
�d�g}t||jd��t	|jd�||||�}
|
r|jd|�d|
�d��t|dd��5}|j
|�ddd�y#1swYyxYw#1swYyxYw)NrA�Unknownrurr'rZFr1z
[content] z turn u, — research phase, citations captured onlyrtz [content] No [CONTENT] block in u — skipping (planning-only)u# — embedding generated image onlyz!<small style="color:#888">_Round r z Turn z_</small>

z![�](r��ar$r%r�)rFr�r-r�r�r(r�r�r�r�r�rI)r�r�r`r�rzrCrA�raw_textrRr=�	local_imgr�r0r�s              r2rprp�s=���i�i�	�9�-�G��y�y���$�H��9�9�Y��$�D�
�y�y���u�$��)�U�Y�Y�z�%:�;�
���	���v�-Y�Z�	
�	�$�X�.�G���
	�)�U�Y�Y�z�%:�;�%��I�I�g���\�7�C�
�	���2�7�)�6�$��Gd�e�
�
�
�.�w�i�v�d�V�Cf�g�	
�0��~�T�'��&�QU�PV�Vc�d���	��I�;�e�,�
���%��w�
/�	 �1�
�L�L���	 �� ��'�2�G���(�G�#�G�,�G�!�'�*�G�
+�L�>��g�Y�f�T�F�R_�`��)�4��
�E�
�i����:�!6�7�!�
�	�	�'��K��w���I��
���r�'��"�Y�K�u�5�6�	
�e�S�7�	+��q�	���U����+	 ���&��s�G�G)�G&�)G2c	�L�t||�|r|dg}t|j�d��D]3\}\}\}}|j|�d|�d|�d�|rd|�d�nd	zd
z��5t	|dd�
�5}|j|�ddd�t
d|���y#1swY�xYw)Nz!
---

## Citations & References

rE)r�z. [r�r�z *(source: z)*rr�r�r$r%zSaved story to )�sanitize_story_filer�rGrIr(r�r-)	r�r�r`r��numr]r\r�r0s	         r2rsrs	s�����{�+��9�:��*3�I�O�O�4E�Q�*O�	�&�C�&�#�~��u��L�L��%�s�5�'��C�5��*�.3�[���r�*��=���
�	��%��w�
/�	 �1�
�L�L���	 �	�O�E�7�
#�$�	 �	 �s�1B�B#c��t|d�5}tj|j��j	�cddd�S#1swYyxYw)Nr)r(r�rr�r)rzr0s  r2�file_to_b64r"	s@��	
�d�D�	�3�Q��������)�0�0�2�3�3�3�s�1A�Ac�d�g}t�}tjd|�D]�}tjj|�}||vr�'|j
|�tjj||�}tjj|�s�x|j||f���|S)zMReturn [(filename, abs_path)] of images referenced in the markdown, in order.�!\[[^\]]*\]\(([^)]+)\))
r�r�r�rZrzr�r�r�r[rI)r��
markdown_text�ordered�seenr�r��fulls       r2�story_images_in_orderr'	s����G��5�D��z�z�3�]�C�*����� � ��%���D�=��������w�w�|�|�K��/��
�7�7�>�>�$���N�N�E�4�=�)�*��Nrgc�<���fd�}tjd||�S)a�Drop markdown image references whose target file does not exist.

    The story agents and the editor sometimes emit ``![...](path)`` lines that
    point at hallucinated filenames or ComfyUI ``/output/`` URLs that were never
    copied into the story folder (real embedded copies always use the
    ``img_rN_Speaker_idx.ext`` scheme). Removing those references keeps the
    published markdown free of dead/broken image tags.
    c���|jd�}tjj|j	d�dj	d�d�}|s|jd�Stjjtjj
�|��r|jd�Std|���y)NrEr�r�#z*[images] Dropping broken image reference: r)r�rZrzr�r�r[r�r-)r�r�r�r�s   �r2�_fixz#sanitize_story_images.<locals>._fix@	s�����k�k�!�n����� � ����3���!2�!8�!8��!=�a�!@�A����;�;�q�>�!�
�7�7�>�>�"�'�'�,�,�{�E�:�;��;�;�q�>�!�
�:�3�%�@�A�rgz!\[[^\]]*\]\(([^)]*)\))r�r�)rur�rs ` r2�sanitize_story_imagesr6	s�����6�6�+�T�4�8�8rgz^\s*!\[[^\]]*\]\(([^)]+)\)\s*$c��i}g}|j�D�],}tj|�r�d}t|�D]4}dj	|�j�s�#dj	|�}nt
jd|�D]&}tjj|�}|||<�(g}��|j�jd�r"|j�jd�rg}��|j�s|jg���|r|dj|���|j|g���/|S)aGMap each image filename to the narrative paragraph right before it.

    Story files store one turn per entry as ``<small> label, paragraph(s),
    image``; the paragraph adjacent to an image is the scene that image
    illustrates, which is what re-anchoring matches against when the editor
    regroups image references.
    rr�rz<smallz</small>r�)r��_IMAGE_LINE_REr��reversedr�r�r�r�rZrzr�r��endswithrI)r�anchors�pendingr��anchorr�r��fns        r2�_image_anchorsrP	s'���G��G��(�(�*�#������%��F�!�'�*�
���7�7�5�>�'�'�)�!�Y�Y�u�-�F��
��z�z�";�T�B�
%���W�W�%�%�c�*��$����
%��G���:�:�<�"�"�8�,�����1F�1F�z�1R��G���z�z�|��N�N�2�����B�K���t�$��N�N�D�6�"�+#�,�Nrgc�l�d�}||�||�}}|r|syt||z�t||z�zS)z5Token Jaccard overlap between two text blocks (0..1).c�\�ttjd|j���S)Nz	[a-z0-9]+)r�r�r�r�)r�s r2�_toksz'_paragraph_overlap_score.<locals>._toksv	s���2�:�:�l�A�G�G�I�6�7�7rgg)rK)r�r�r�left�rights     r2�_paragraph_overlap_scorers	s?��8���(�E�!�H�%�D��u���t�e�|��s�4�%�<�0�0�0rgc��t||�D��cgc]\}}|��	}}}|s|St|�}|s|S|j�}i}|D]�}	tj	|	�s�tjd|	�jd�}
|jtjj|
�jd�djd�d|	���|D�cgc]	}||vs�|��}}|rtdt|��d|���|Sg}g}
|D]N}	tj	|	�s|	j�s|
r|j!|
�g}
�>|
j!|	��P|
r|j!|
�|D�cgc]!}dj#|�j���#}}g}d}|D]}|j%|d	�}d
\}}t'|t|��D]}t)|||�}||kDs�||}}�|dkrt+|t|�dz
�}|j!|�|dz}��g}t-|�D]h\}}|j/|�t-|�D]3\}}|||k(s�|j!d	�|j!||��5|j!d	��jdj#|�j�dzScc}}wcc}wcc}w)a�Keep every image embedded inline, right after the narrative it illustrates.

    The editor is free to polish prose but sometimes regroups all image
    references at the top or bottom of the story. This re-anchors each image (in
    the order it appeared in the original story) to the revised paragraph whose
    wording most resembles the turn text the image originally followed, so
    images reliably stay embedded inside the story flow.
    rrEr�rr
z[images] Re-anchor: z% reference(s) missing from revision: r�r)r�g�)rrr�rr�r�r�r��
setdefaultrZrzr�r�r-rKr�rIr�rFr�rr�r�rh)�revised�originalr�rr�rrr��	ref_by_fnr�r��missingr��curr��block_texts�attach�prevr�best�scorer�r��outr�r�s                          r2�reanchor_story_imagesr)	s��� 5�[�(�K�L�e�b�!�r�L�G�L�����X�&�G������� �E��I��������%��)�)�5�t�<�B�B�1�E�C�� � ���� � ��%�+�+�C�0��3�9�9�#�>�q�A�4�
��$�;�b�r��':�r�;�G�;��
�"�3�w�<�.�0U�V]�U^�_�	
���
�F�
�C��������%�T�Z�Z�\���
�
�c�"�����
�
�4��
���
�
�c��17�8�A�4�9�9�Q�<�%�%�'�8�K�8�
�F��D��
�����R��$�����e��t�S��-�.�	#�A�(��Q���@�A��5�y���t��	#��!�8��t�S��-��1�2�D��
�
�d���a�x��
�
�C��f�%����5��
�
�5���w�'�	*�E�A�r��a�y�A�~��
�
�2���
�
�9�R�=�)�	*�	�
�
�2��
��9�9�S�>���!�D�(�(��sM��<��$9s�K�	K	�'K	�9&Kc��	t|dd��5}|j�}ddd�t	|�}||k7rTt|dd��5}|j|�ddd�tdtjj|����yy#1swY�oxYw#t$r}td|�d|���Yd}~yd}~wwxYw#1swY�ixYw)	z�Rewrite a story markdown file in place, dropping broken image references.

    Used defensively after the round and after the editor phase so a dead image
    tag can never reach the hosted page.r:r$r%Nz[images] Could not read r'riz[images] sanitized )	r(r�r�r-r�writerZrzr�)r�r�r0rur1r�s      r2r�r��	s���
�
�%��w�
/�	�1��6�6�8�D�	�
$�D�+�6�G��$��
�%��w�
/�	�1�
�G�G�G��	�
�#�B�G�G�$4�$4�U�$;�#<�=�>��
	�	����
�(���r�!��5�6�����
	�	�s9�B�B�B�C�B�B�	B?�$B:�:B?�Cc���tjd|tjtjz��}|r|j	d�j�S|j�S)Nz```(?:markdown|md)?\s*(.*?)```r�rE)r�r�r�r�r�r�)rur�s  r2�extract_markdown_fencer-�	sL���I�I�)�4�r�y�y�2�=�=�7P�
�E�
��{�{�1�~�#�#�%�%��:�:�<�rgc
�4��	�
�!�	t|dd��5}|j��!ddd��
�	��!fd�}|�!�}|std�y	ttd��j�}|d	j�xsg��	xsd
|
xsdt
|d|�|xsdd
�}|j�D]\}}|j||�}�	|jdd�}td|ftd|fg}ttd|��D�]�}||t|�z\}}td|dz�dtd|��d|�dt|��d�	�|D]}td|����	t|d|dz��|��}	t�t!||d�!zdzdjd �|D��zd!zd"�#�}t#|d$�}|r:t%|�}t'|�}t)||�}t+|�}t-|�!|�}|s&td|�d%��	t.r��t1||���||�} | sUt|d&d��5}|j3|dz�ddd�td'|�d(|���|t.st1||�cScS|�!| }t.r���t1||����td*t|��d+��|D]}td|����y#1swY���xYw#t$r}td|�d|���Yd}~yd}~wwxYw#t$r}td|���Yd}~yd}~wwxYw#t$r}td|�d|���Yd}~��d}~wwxYw#1swY��xYw#t$r}td|dz�d)|���Yd}~��d}~wwxYw#t.s
t1||�wwxYw),u$Kaya↔Kolpo cross-critique of the finished story (research-style self-verify).

    The deterministic gate (verify_task_fulfillment) runs on the authored story
    first — the cheap, no-LLM check. When it is clean, the slow LLM re-write is
    skipped entirely and the story the two agents wrote is kept as-is. When
    violations exist, the two agents become the verifiers: each retry has one of
    them (rotating Kaya/Kolpo) review their partner's copy, name the exact spot
    of every violation, and return a corrected markdown where ONLY those spots
    changed. The gate re-runs after every attempt; residual problems after
    ``retries`` attempts surface as an auto-RED (never a silently shipped story).

    Returns the path to the ``.edited.md`` file, or ``None`` to keep the original.
    r:r$r%Nz [critique] Could not read story r'c�$��t�|�����S)N)r�)r�)r�r`r�rsrus ����r2�_gatez!run_cross_critique.<locals>._gate
s���&��*�g�x�Y�
�	
rguE[critique] PASS — no deterministic violations, skipping LLM rewriter�rrLr)r�r�r�r�r�r�z'[critique] Could not read prompt file: ra�
.edited.mdrrrEz[critique] Attempt rz by z residual violation(s)z[critique]   - zCross-critique attempt rPz[critique] z% could not start a critique session: z&Here is the complete story markdown:

z&

Verification violations to resolve:
r�c3�&K�|]	}d|�����y�wrtr8r"s  r2r<z%run_cross_critique.<locals>.<genexpr>:
s����7��b���H�7�ruz�

Follow your WORK MODE exactly: quote each violation's spot, then return your CRITIQUE comment and the complete corrected markdown in a single ```markdown code block, changing only the flagged spots.T)r+ruz' returned no markdown; keeping originalriz[critique] PASS after u's retry — saved r/u"[critique] FAIL after retries — z residual violation(s):)r(r�r�r-r�r�rxrGrlr?r�r�rKrr7r"r;r-r�r�rr�r)�
keep_sessionsr
r+)"r�r�rorPrxr�ryr�rsr�rWryr�r`�retriesr0r1r0r�r�r�placeholderrfr��partnersr�r�r8r#rr�r�residualrus"        ``   `                   @r2rvrv�	s���<�
�%��w�
/�	�1��6�6�8�D�	�
�
�T�{�H��
�U�V����*�W�=�B�B�D������7�=�b�1�"�.�b� �*�F�(���)�D��n�f�

��#1�"6�"6�"8�	8��K���^�^�K��7�F�	8��-�-��|�4�K�	�S�	�7�#�	�S�	�7�#��H���Q���)�42���w��X��6�7���e�
�!�'�A�+��a��A�w��/@��T�F�"��8�}�o�3�
5�	
��	)�A��O�A�3�'�(�	)�	�'��0��1��
�>�f��J�%	2�"�$����:���=�>��)�)�7�h�7�7�8�!�	!���F�-�V�F�^�<�G��+�G�4��2�7�;��/���E��-�g�6��/���{�K�����D�6�)P�Q�R��!��u�j�1��W�~�H���+�s�W�=�,���G�G�G�d�N�+�,��.�t�f�4G��}�U�V�"�!��u�j�1�!��D��H�!��u�j�1�i42�l
�.�s�8�}�o�=T�
U�V�
�%��
���s�#�$�%��}	�	����
�0���r�!��=�>�����4��
�7��s�;�<�����*�	��K��v�%J�1�#�N�O���	��<,�,���	C��'��!��}�I�a�S�A�B�B��	C��!��u�j�1�!�s��K�K�K�A<L�"L$�7BM�$M�<M�M�M�K�K�	K=�"K8�8K=�	L!�	L�L!�$	M	�-M�M	�M	�M�	M?�!M:�5N�:M?�?N�Nc	
��	ttt�}	t|	gtg�	t
td��j�}|dj|xsg�|xsd|xsd|xsdt|d	|�d
�}|j�D]\}
}|j|
|�}�	t|	d||�
�}|jdd�}	t
|dd��5}|j�}ddd�t|�D]+\}}t!�t#|	|d|�d�t%|����-t!�t#|	|d|zdz�}t'|d�}|rt)|�n|}|rt+|�n|}|s t	d�	t,s
t/|	|�yyt1||�}|rt3|�n|}t5|||�}t
|dd��5}|j7|dz�ddd�t	d|���|t,s
t/|	|�SS#t$r}
t	dt�d|
���Yd}
~
yd}
~
wwxYw#t$r}
t	d|
���Yd}
~
yd}
~
wwxYw#1swY��txYw#1swY��xYw#t$r+}
t	d|
���Yd}
~
t,s
t/|	|�yyd}
~
wwxYw#t,s
t/|	|�wwxYw)zDEditor phase: review images + markdown, write story_rN_ts.edited.md.z[editor] Could not log in r'Nr$r%r�rrLr�r�r�r�r�r�r�z%[editor] Could not read prompt file: z
Editor review�rrrar1r:�-This is the image referenced in the story as z�. Look at it carefully; it is part of the story. Decide the quality of image.If it does not match the task expectation, flag it.But never add new image or edit existing one�r7z"Here is the full story markdown:

ze

Now return the complete revised markdown, wrapped in a single ```markdown code block. Nothing else.ruz<[editor] Editor returned an empty revision; keeping originalrir�z[editor] Saved edited story to z[editor] Editor phase failed: )r��USERNAME_EDITOR�PASSWORD_EDITORr7r-rr(r�r�r�rxrGrlr�rrr"r;rr-r�r�r3r
rr�r)r+)r�r�rorPrsr�rWryr�r8r1r�rr5rfrr�r0r�	img_fnamerr�rs                       r2�
run_editorr@_
s�����o��7���5�'�O�#4�5���(�7�;�@�@�B������7�=�b�1�"�.�b� �*�F��n�f�(���)�D�

��#1�"6�"6�"8�	8��K���^�^�K��7�F�	8�
 �
���%�	�J��-�-��|�4�K�).�
�%��w�
/�	%�1��F�F�H�M�	%�4�[�-�P�
	�O�I�t�"�$����?�	�{�K?�?�&�d�+�
�
	�	� ����2��
�;�
;�
��)����8��07�#�G�,�W��7>�*�7�3�G����P�Q����5�*�-��(���=��29�%�g�.�w��'����L��
�+�s�W�
5�	$��
�G�G�G�d�N�#�	$�
�/��}�=�>��
��5�*�-���E��
�*�?�*;�2�a�S�A�B����� ��
�5�a�S�9�:�����	%�	%��>	$�	$����
�.�q�c�2�3����5�*�-���	����5�*�-��s��H
�A<H5�I2�I�'BI2�6I2�
I&�I2�
	H2�H-�-H2�5	I�>I�I�I#�I2�&I/�+I2�2	J&�;J!�	J)�!J&�&J)�)J>c

�F�	ttt�}
t|
gtg�	t
td��j�}|dj|xsg�|xsd|xsd|	xsdt|d	|�d
�}
|
j�D]\}}|j||�}�	t|
d||
�
�}	|r|n|}t
|dd��5}|j�}ddd�t|�}t!||�D]+\}}t#�t%|
|d|�d�t'|����-t#�t%|
|d|zdz�}d}t)j*d|dt(j,��}|r |j/d�j1�}n7t)j*d|d�rd}nt)j*d|d�rd}|jdd�}||d||t3j4�j7�j9d� �d!�}t
|d"d��5}t;j<||d#�$�ddd�t	d%|�d&|���|t>s
tA|
|�SS#t$r}t	dt�d|���Yd}~yd}~wwxYw#t$r}t	d|���Yd}~yd}~wwxYw#1swY���xYw#1swY��xYw#t$r+}t	d'|���Yd}~t>s
tA|
|�yyd}~wwxYw#t>s
tA|
|�wwxYw)(zKModerator phase: GREEN/RED verdict, written to story_rN_ts.moderation.json.z[moderator] Could not log in r'Nr$r%r�rrLrr9z([moderator] Could not read prompt file: zModerator reviewr:r:r;r�r<z#Here is the final story markdown:

zZ

Give your verdict using exactly these two lines:
VERDICT: GREEN
REASONS: <short reasons>�UNKNOWNzVERDICT\s*:\s*(GREEN|RED)rur�rEz	\bGREEN\b�GREENz\bRED\brcrarbr$r%rerir rjz[moderator] Verdict z
 saved to z$[moderator] Moderator phase failed: )!r��USERNAME_MODERATOR�PASSWORD_MODERATORr7r-rr(r�r�r�rxrGrlr�rrrr"r;rr�r�r�r�rqrr�r2r3r)rwr3r
)r�r�rorP�editor_pathrsr�rWryr�r8r1r�rr5rfrrdr0rr?rr�rfr�r�r@s                           r2�
run_moderatorrG�
s-����(�*<�=���5�'�$6�#7�8���+�g�>�C�C�E������7�=�b�1�"�.�b� �*�F��n�f�(���Y�G�

��#1�"6�"6�"8�	8��K���^�^�K��7�F�	8�
 �
���%�	�J�/.� +����
�&�#��
0�	%�A��F�F�H�M�	%�-�m�[�I�
�4�[�-�P�	�O�I�t�"�$����?�	�{�!�L�%�d�+�	
�	�	� ����3��
�7�
7�
�����I�I�2�F�6�N�"�-�-�X����g�g�a�j�&�&�(�G�
�Y�Y�|�V�F�^�
4��G�
�Y�Y�z�6�&�>�
2��G��}�}�U�,>�?����f�~���!����2�2�4�>�>�	�>�R�
���,��g�
6�	)�!��I�I�d�A�a�(�	)�
�$�W�I�Z��~�F�G��
��5�*�-���O��
�-�.@�-A��A�3�G�H����� ��
�8���<�=�����	%�	%��H	)�	)����
�4�Q�C�8�9����5�*�-���	����5�*�-��s��I,�A<J�6K�
J;�E
K�%K�>K�,	J�5J�J�	J8� J3�3J8�;K�K�K�
K�	L�L�+L�L�L�L c���ttt�}ttt�}t||gttg�t
d�d}d}		t|tt�z}|d}|d}|d}|jd�xsdg}|jd	�xsd
}	|jd�xsd}
|jd
�xsi}t||�}|jd�xsd}
t
|�t
d|�|jd�xsd}|rt
d|�d��|dz
}��d|vrt
d|�d��|dz
}|dz
}��t|
|td��}t|
|td|��}t|
�}i}td�D]^}t!t"|	t$|jd���\}}}|rn;t'|	|||�}t)||�snt
d|dz�d���`t
d�f}t+t-|t.���}d}|st1|||jd�xsi�}t3d |fd t.|d!�|��}|jd"�r$|jd#�xsijd$�nd}|rt
d%|�d&|���t
d'|�d(|�d)|	�d*d+j5|��d,�	�t7j6�}	t9||||||||	||
|||
|||jd-�|||jd.�|jd/�xsd�0�\}}}} |rPt3d1|d1|�2�}!|!jd"�rt
d3|�d4��n t
d5|�d6|!jd7����t:st=||�t=||�	|dz
}|dz
}t7j6�|z
}#t
d<|�d=|#d>�d?��t
d@tD�dA��t7jFtD�t
dB����#t>$r3}"tAjB�t
d8|�d9|�d:|"�d;��Yd}"~"��d}"~"wwxYw#tH$rt
dC�YywxYw)DNz	Logged InrErrorsrprvrwrPrxrWrryr|z/The stories will be generated in this directoryr{FzTask z is inactive, skipping the taskr�uT[guard] Task declares 'audio', but no audio tool exists in TOOLS — skipping round z without running it.
r�rQrSr")r;z*[theme] Combination already used (attempt z!); re-rolling persona for varietyzH[theme] Exhausted re-roll attempts; proceeding with the last combination)r<rT)rEr<rMr0rMrUz[theme] Reserved combination z for round z=== Starting round r'z	 (genre: z	, roles: r�z) ===
r}r~r)rAr{r}r�r|r~rrVrWz[theme] Marked rYz[theme] Could not mark z completed: r1z[error] Round z failed for task 'z': z=
        Skipping to the next task so the flow keeps running.zTotal time elapsed in round z - z.2fz	 seconds
z+Autonomous organization is in vacation for r_z
Vacation overz
Manual Interruption)%r��
USERNAME_A�
PASSWORD_A�
USERNAME_B�
PASSWORD_Brr-r�rKrFr�rr�r	r�r�r^r�r�rrHrTrAror(r:r�r�r�r3r
r7�	traceback�	print_exc�SLEEP_BETWEEN_ROUNDSr5�KeyboardInterrupt)$rxryrz�
task_indexr�rorsrprvrPr�ryrzr|r{r�rWr|rGr�rIrVrrA�themes_blockrX�
theme_slugr��
start_timer�r�r�r�r.r1�elapseds$                                    r2�run_foreverrV
s���J�
�+�G��J�
�+�G��7�G�,�z�:�.F�G�	�+���L��J�c'����c�%�j�0�1�D���<�D��9�o�G��[�)�I��H�H�W�%�1�&��E��H�H�W�%�2��E��8�8�I�.�4�"�L�����-�3��I�%�d�E�2�D��h�h�y�)�1�T�G��%�L��C�T�J��x�x�
�+�4�u�H����d�V�#B�C�D��a��
���'�!��&�&2�^�3I�K����!���a��
��
2��d�N���L�&����'�&��G�2�,�?�M��E� ��8�
��6N� �%�):�t�x�x�PW�GX�7�3��d�O�!��(���o�|�T��'���7���@��1��
�N5�6��
��^��$�T�?�;�G�.�!�'�1F�G��L��H� �-�d�D�%�)�)�I�:N�:T�RT�U�
�"����$�/�$���
��>D�Z�Z��=M�V�Z�Z��(�.�B�3�3�D�9�SW����7��z��\�N�[��
�%�l�^�2�d�V�9�U�G�9�UY�U^�U^�_d�Ue�Tf�fm�n�
�����J�.
7�:Q��� ������� ����#�#/��(�(�7�+�!-�"/�!�X�X�j�1�#'�8�8�,<�#=�#B��);�7�
�I�y�%�:�$�"��",�!)�	�D��x�x��~����z��D�E��5�h�Z�|�D�H�H�U\�L]�K^�_��%�"�7�I�6�"�7�I�6��A��L��!�O�J��i�i�k�J�.�G��.�|�n�C���}�J�W�
�
�=�>R�=S�S[�\�
�
�J�J�+�,��/�"�A��|�
��#�#�%��$�\�N�2D�T�F�#�a�S�QS�T����
��F�'�
�%�&�'�s?�J	Q
�AP�0CQ
�	Q
�)Q�Q
�Q
�
Q
�
Q$�#Q$z8No tasks to run. Add tasks to a config file and restart.zLoaded r�z#Keep sessions {y/n} [default: n] ? �y�__main__r7)r8)Nr�N)NN)r�)g�������?)NNN)NFF)NNrNr=r)NrxrNNNNNrNNNFFrE)r�)NrxNN)NrrNr)NNrrNr)�rZr�r)r��argparser3r�r��	threadingrM�difflibrrrM�
server.dotenvr�ArgumentParser�parser�add_argument�
parse_argsrdrzrr��getenvr�r��environrFr�r4rIrK�_SHARED_PASSWORDrJrLr@r6rrr�r?r�r(r�rkrOr=rDr>rEr�r�r��MAX_CRITIQUE_RETRIESrgror?rnririrjrJr3r�r�r�r^rbrmr�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r�r	rrr(r:rArHrTr`rqrxr�r�r�r�r�r�r�r�r��RLockr�r�r�r�r�r2rr
rrr r"r;rGr�r�r�r�rtrur�rmr�r�r�r�r�r�r�r�rprsrrrr�rrrr)r�r-rvr@rGrVr�r��TASK_CHECKLISTSr-r]rvrK�dry_run�input�
user_inputr�r�r3�__name__r8rgr2�<module>rks.��	���
���	�
���#��
�%��
�	 ��	 �	 �-H�	I�������
L��������
!��������
;��������
9�����������#�#�$>�?�� �b�i�i� 5�6���B�I�I�1�2���:�:�>�>��1���&��+�	��
�
�
��:�:�>�>�"6��;��
�Z�Z�^�^�2�4D�
E�
�
�Z�Z�^�^�2�4D�
E�
�"���������&�)��C���2�3�8�8�:������ ���*�*�.�.�!<�>N�O���Z�Z�^�^�$B�DT�U��F��L��J�����W�W�'�'�(E�F��
$���������*�*�+S�T�����+�+�6����G�G�&�&�'T�U�����#�#9�2�>���/��4����G�G���F�G����
3�l�
� 5�p���9��(2�jQ�h�,E�P	��$P�
��"�:�"�,DH�a�HK�\�&�R.�($9�<�*?�g�?�R�>*�0#@�L4�$ �
���0�J�Zi��7�*GK�=�@�(�����i�o�o�������&2�60�JN�%�(�	3�>����JO�1*�t
�
��	
�	��G�b�
����	
����
����
��+P3�f
�0�
&��.�2�8�
�	
��$�N=�7�$$�N�B�(	<�
� $?�N:�z%�3�
�9�.����=�>�� �F	1�B)�J?�$�$
�
���	�� �}�J
�
���	�Q.�r��
���	�W.�tl'�^IS��E��|�_�&7���	�
D�E�
�Q�-��(��9�����E�
�|�>�,��8�9��<�<��M�
�Q�-��
�8�
9�
�� � �"�(�(�*�c�1�
��z���M�rg