From 283638c193340abe3ea65282a66eb6d6eacd0442 Mon Sep 17 00:00:00 2001 From: vegu-ai-tools <152010387+vegu-ai-tools@users.noreply.github.com> Date: Sun, 5 Apr 2026 02:28:26 +0300 Subject: [PATCH] Add configurable action confirmation timeout in Director Chat settings --- CHANGELOG.yaml | 1 + src/talemate/agents/director/chat/mixin.py | 14 ++++++++++++++ src/talemate/agents/director/chat/nodes.py | 5 +++-- .../DirectorConsoleChatMessageConfirm.vue | 2 +- .../src/components/DirectorConsoleChatMessages.vue | 1 + .../src/components/DirectorConsoleChats.vue | 8 ++++---- 6 files changed, 24 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.yaml b/CHANGELOG.yaml index 3b32117e..9ab6c3db 100644 --- a/CHANGELOG.yaml +++ b/CHANGELOG.yaml @@ -36,3 +36,4 @@ - "Context Formatting: The |condensed template filter no longer permanently compacts multi-line context (e.g., world info, memories, pins) into a single line when sending prompts to the LLM. Formatting is now only compacted temporarily for deduplication comparison and restored to its original multi-line structure afterwards." - "Client Config UX: Moved advanced settings (Inference Presets, Structured Data Format, Section Format, Response Length Enforcement, Prompt Caching, Rate Limit) into a dedicated Advanced tab to declutter the general view. A convenient link in the general tab provides quick access. Toggling Simple View on now resets to the General tab, and switching clients always starts on General." - "Prompts Tab Alert: The top-level Prompts tab now shows a warning icon when any active template overrides are outdated, so you can spot them without opening the tab." + - "Action Confirm Timeout: The director chat action confirmation timeout is now configurable (0–60 minutes, default 3). Set to 0 to wait indefinitely. Found in the Director Chat settings under Action Confirmation." diff --git a/src/talemate/agents/director/chat/mixin.py b/src/talemate/agents/director/chat/mixin.py index 2a613d5b..4583b825 100644 --- a/src/talemate/agents/director/chat/mixin.py +++ b/src/talemate/agents/director/chat/mixin.py @@ -105,6 +105,15 @@ class DirectorChatMixin: min=0.05, max=0.95, ), + "action_confirm_timeout": AgentActionConfig( + type="number", + label="Action confirm timeout", + description="How long to wait (in minutes) for user confirmation of write actions. 0 means wait indefinitely.", + value=3, + step=1, + min=0, + max=60, + ), "custom_instructions": AgentActionConfig( type="blob", label="Custom instructions", @@ -157,6 +166,11 @@ class DirectorChatMixin: def chat_custom_instructions(self) -> str: return self.actions["chat"].config["custom_instructions"].value + @property + def chat_action_confirm_timeout(self) -> int: + """Timeout in seconds. 0 means wait indefinitely.""" + return int(self.actions["chat"].config["action_confirm_timeout"].value) * 60 + # === Node initialization === @classmethod diff --git a/src/talemate/agents/director/chat/nodes.py b/src/talemate/agents/director/chat/nodes.py index 97eda377..787a0398 100644 --- a/src/talemate/agents/director/chat/nodes.py +++ b/src/talemate/agents/director/chat/nodes.py @@ -325,7 +325,8 @@ class DirectorChatActionConfirm(Node): async def run(self, state: GraphState): state_value = self.get_input_value("state") - max_wait_time: int = 180 + director = get_agent("director") + max_wait_time: int = director.chat_action_confirm_timeout key: str = f"_director_chat_action_confirm_{self.id}" name: str = self.normalized_input_value("name") description: str = self.normalized_input_value("description") @@ -364,7 +365,7 @@ class DirectorChatActionConfirm(Node): node=self.id, ) await asyncio.sleep(1) - if time.time() - start_time > max_wait_time: + if max_wait_time and time.time() - start_time > max_wait_time: log.error( "Director Chat Action Confirm: Max wait time reached", node=self.id, diff --git a/talemate_frontend/src/components/DirectorConsoleChatMessageConfirm.vue b/talemate_frontend/src/components/DirectorConsoleChatMessageConfirm.vue index 14872116..ee8e0e81 100644 --- a/talemate_frontend/src/components/DirectorConsoleChatMessageConfirm.vue +++ b/talemate_frontend/src/components/DirectorConsoleChatMessageConfirm.vue @@ -58,7 +58,7 @@ export default { }, }, mounted() { - if (!this.decision) { + if (!this.decision && this.timer > 0) { this.startCountdown(); } }, diff --git a/talemate_frontend/src/components/DirectorConsoleChatMessages.vue b/talemate_frontend/src/components/DirectorConsoleChatMessages.vue index 264de239..ac2c0791 100644 --- a/talemate_frontend/src/components/DirectorConsoleChatMessages.vue +++ b/talemate_frontend/src/components/DirectorConsoleChatMessages.vue @@ -14,6 +14,7 @@ :name="m.name" :description="m.description" :decision="m.decision" + :timer="m.timer" :confirming="confirming[m.id]" @decide="onDecide" /> diff --git a/talemate_frontend/src/components/DirectorConsoleChats.vue b/talemate_frontend/src/components/DirectorConsoleChats.vue index 101cfdb4..cc316b03 100644 --- a/talemate_frontend/src/components/DirectorConsoleChats.vue +++ b/talemate_frontend/src/components/DirectorConsoleChats.vue @@ -146,18 +146,18 @@ export default { if(!params || !params.chat_id) return; this.deleteChat(); }, - addOrUpdateConfirmRequest(id, name, description) { + addOrUpdateConfirmRequest(id, name, description, timer) { try { // If there's already a pending entry for this id, just update its details const pendingIdx = this.chatMessages.findIndex((m) => m.type === 'confirm_request' && m.id === id && !m.decision); if (pendingIdx !== -1) { const existing = this.chatMessages[pendingIdx]; - const updated = { ...existing, name: name ?? existing.name, description: description ?? existing.description }; + const updated = { ...existing, name: name ?? existing.name, description: description ?? existing.description, timer: timer ?? existing.timer }; this.chatMessages.splice(pendingIdx, 1, updated); return; } // If previous entries exist but are already decided, push a new one - this.chatMessages.push({ source: 'director', type: 'confirm_request', id, name, description, decision: null }); + this.chatMessages.push({ source: 'director', type: 'confirm_request', id, name, description, timer, decision: null }); } catch (e) { /* safely ignore malformed confirm requests */ } }, markConfirmRequestDecision(id, decision) { @@ -348,7 +348,7 @@ export default { console.log('request_action_confirmation', data); // Remove any trailing thinking placeholder while waiting for user decision this.removeTrailingPlaceholder(); - this.addOrUpdateConfirmRequest(data.id, data.name, data.description); + this.addOrUpdateConfirmRequest(data.id, data.name, data.description, data.timer); return; }