
    `gj?                        d Z ddlmZ ddlZddlmZmZmZ  eddh      Z	dZ
ddZdd	Zdd
Zdde
dd	 	 	 	 	 	 	 	 	 ddZg dZy)u  Turn-end guard for kanban workers.

Kanban workers must end with ``kanban_complete`` or ``kanban_block``. Models
(especially GLM / Qwen families) sometimes narrate the next step
("Let me write the report now") and stop with ``finish_reason=stop`` and no
tool calls. Hermes treats that as a clean exit → ``rc=0`` → dispatcher
``protocol_violation``.

This module is policy-only: when a kanban worker tries to finish without a
terminal board tool, return a bounded synthetic nudge so the conversation
loop continues instead of exiting.
    )annotationsN)AnyIterableOptionalkanban_completekanban_block   c                     t         j                  j                  d      } | !| j                         j	                         dv ryt         j                  j                  d      xs dj                         }t        |      S )zReturn whether the kanban stop-guard is active for this process.

    On when ``HERMES_KANBAN_TASK`` is set (dispatcher-spawned worker), unless
    ``HERMES_KANBAN_STOP_NUDGE`` explicitly disables it.
    HERMES_KANBAN_STOP_NUDGE>   0noofffalseFHERMES_KANBAN_TASK )osenvirongetstriplowerbool)envtasks     D/root/.hermes/venv/lib/python3.12/site-packages/agent/kanban_stop.pykanban_stop_nudge_enabledr      sa     **..3
4C
399;,,.2MMJJNN/06B==?D:    c                b   t        | t              r]| j                  d      }t        |t              rt        |j                  d      xs d      S t        | j                  d      xs d      S t	        | dd       }|t        t	        |dd      xs d      S t        t	        | dd      xs d      S )Nfunctionnamer   )
isinstancedictr   strgetattr)tcfns     r   _tool_call_namer&   &   s    "dVVJb$rvvf~+,,266&>'R((	Z	&B	~72vr*0b11wr62&,"--r   c                    | sy| D ]  }t        |t              s|j                  d      }|dk(  r/|j                  d      xs g D ]  }t        |      t        v s  y Y|dk(  s_t        |j                  d      xs d      }|t        v s y y)	zATrue if this conversation already invoked a terminal kanban tool.Frole	assistant
tool_callsTtoolr   r   )r    r!   r   r&   _TERMINAL_KANBAN_TOOLSr"   )messagesmsgr(   r$   r   s        r   session_called_kanban_terminalr/   2   s     #t$wwv;ggl+1r  "2&*@@  V^swwv,"-D-- r   )r-   attemptsmax_attemptstask_idc                    t               sy||k\  ryt        |       ry|xs# t        j                  j	                  d      xs dj                         xs d}d| dS )zReturn a synthetic follow-up when a kanban worker exits without a terminal tool.

    Returns ``None`` when the guard should not fire (not a kanban worker,
    already completed/blocked, or nudge budget exhausted).
    Nr   r   z	this taskzj[System: You are a Hermes kanban worker. A plain-text reply is NOT a terminal state for the board.

Task `u!  ` is still `running`. Ending now without a board tool causes a protocol violation (clean exit with no `kanban_complete` / `kanban_block`).

Do this immediately in your next response — do not narrate intent:
1. Finish any remaining deliverable (write the required file(s) now).
2. Call `kanban_complete(summary=..., artifacts=[...])` if the work is done, OR `kanban_block(reason=...)` if you are blocked.

Never end a turn with only a promise of future action. Repeated protocol violations will block this task and require manual intervention.])r   r/   r   r   r   r   )r-   r0   r1   r2   tids        r   build_kanban_stop_nudger5   E   si     %&<%h/@bjjnn%9:@b
G
G
I
X[C	 U
	Ur   )r5   r   r/   )returnr   )r$   r   r6   r"   )r-   Iterable[dict] | Noner6   r   )
r-   r7   r0   intr1   r8   r2   Optional[str]r6   r9   )__doc__
__future__r   r   typingr   r   r   	frozensetr,   _DEFAULT_MAX_ATTEMPTSr   r&   r/   r5   __all__ r   r   <module>rA      s    # 	 * * #$5~#FG  
	.* '+-! #    	 
    Fr   