fix(captain/hermes-builder): namespace controller correto + botao iniciar

Bug: controller estava em enterprise/api/v1/accounts/captain/ com namespace
Enterprise:: — convencao Chatwoot eh enterprise/app/controllers/api/v1/...
direto, classe Api::V1::Accounts::Captain::HermesBuilderController. Sem
namespace Enterprise::. 404 acontecia porque rotas registravam Captain::
sem prefixo Enterprise::.

Move controller pro path correto. Remove diretorios vazios criados.

UX: adiciona endpoint POST /start que envia comando-gatilho oculto pro
Construtor comecar fluxo socratico — admin nao precisa digitar primeira
mensagem. Vue mostra empty state com botao "Iniciar criacao" em vez de
exigir mensagem inicial.

i18n keys novas: START + EMPTY_STATE atualizado em pt_BR + en.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Rodribm10 2026-05-01 22:06:04 -03:00
parent bfa06597f2
commit 041766b427
6 changed files with 57 additions and 6 deletions

View File

@ -14,6 +14,10 @@ class HermesBuilder extends ApiClient {
return axios.post(this.url, { text });
}
start() {
return axios.post(`${this.url}/start`);
}
reset() {
return axios.delete(`${this.url}/reset`);
}

View File

@ -899,11 +899,12 @@
"HEADER_DESCRIPTION": "Chat with the Builder to create a new Hermes agent. It asks questions and saves the spec as JSON for review at the end.",
"RESET": "Clear conversation",
"RESET_CONFIRM": "Clear current conversation with the Builder?",
"EMPTY_STATE": "Type \"hello\" to start. The Builder will guide you.",
"EMPTY_STATE": "Ready to create a new Hermes agent? Click \"Start creation\" and the Builder will guide you.",
"PLACEHOLDER": "Type and press Enter to send (Shift+Enter for new line)",
"SEND": "Send",
"SESSION_LABEL": "Session:",
"SEND_FAILED": "Send failed: {message}",
"RESET_FAILED": "Failed to clear session."
"RESET_FAILED": "Failed to clear session.",
"START": "Start creation"
}
}

View File

@ -900,11 +900,12 @@
"HEADER_DESCRIPTION": "Converse com o Construtor pra criar um novo agente Hermes. Ele faz perguntas e ao final salva a especificação em JSON pra revisão.",
"RESET": "Limpar conversa",
"RESET_CONFIRM": "Limpar conversa atual com o Construtor?",
"EMPTY_STATE": "Mande \"olá\" pra começar. O Construtor vai te guiar.",
"EMPTY_STATE": "Pronto pra criar um novo agente Hermes? Clica em \"Iniciar criação\" e o Construtor te guia.",
"PLACEHOLDER": "Escreva e Enter pra enviar (Shift+Enter pula linha)",
"SEND": "Enviar",
"SESSION_LABEL": "Sessão:",
"SEND_FAILED": "Erro ao enviar: {message}",
"RESET_FAILED": "Falha ao limpar sessão."
"RESET_FAILED": "Falha ao limpar sessão.",
"START": "Iniciar criação"
}
}

View File

@ -88,6 +88,24 @@ const resetSession = async () => {
}
};
const startSession = async () => {
if (sending.value) return;
sending.value = true;
try {
await hermesBuilderApi.start();
// Construtor vai responder com primeira pergunta via callback async
// polling pega em ~2s
} catch (e) {
useAlert(
t('CAPTAIN_HERMES_BUILDER.SEND_FAILED', {
message: e.response?.data?.error || e.message || 'unknown',
})
);
} finally {
sending.value = false;
}
};
const formatTime = iso => {
if (!iso) return '';
const d = new Date(iso);
@ -124,7 +142,10 @@ watch(messages, () => nextTick().then(scrollToBottom), { deep: true });
<section ref="scrollContainer" class="messages">
<div v-if="!messages.length" class="empty-state">
{{ t('CAPTAIN_HERMES_BUILDER.EMPTY_STATE') }}
<p>{{ t('CAPTAIN_HERMES_BUILDER.EMPTY_STATE') }}</p>
<Button variant="primary" :disabled="sending" @click="startSession">
{{ t('CAPTAIN_HERMES_BUILDER.START') }}
</Button>
</div>
<div
v-for="(msg, idx) in messages"
@ -216,6 +237,14 @@ watch(messages, () => nextTick().then(scrollToBottom), { deep: true });
color: var(--color-text-light, #9ca3af);
font-size: 14px;
text-align: center;
display: flex;
flex-direction: column;
gap: 16px;
align-items: center;
p {
margin: 0;
}
}
.msg {

View File

@ -60,6 +60,7 @@ Rails.application.routes.draw do
namespace :captain do
resources :hermes_builder, only: [:index, :create] do
collection do
post :start
delete :reset
end
end

View File

@ -8,7 +8,7 @@
# 5. UI faz GET /messages a cada 2s (polling) e renderiza
#
# Sessão é por account+user (1 admin → 1 sessão de builder por vez).
class Enterprise::Api::V1::Accounts::Captain::HermesBuilderController < Api::V1::Accounts::BaseController
class Api::V1::Accounts::Captain::HermesBuilderController < Api::V1::Accounts::BaseController
before_action :authorize_admin
def index
@ -29,6 +29,21 @@ class Enterprise::Api::V1::Accounts::Captain::HermesBuilderController < Api::V1:
render json: { error: "Falha ao contatar Construtor: #{e.message}" }, status: :bad_gateway
end
# Inicia sessão limpa enviando comando-gatilho oculto pro Construtor pra
# ele começar o fluxo socrático de criação. UI chama este endpoint
# quando admin clica "Iniciar" — sem ter que digitar primeira msg.
def start
HermesBuilder::Storage.clear(session_key)
HermesBuilder::Dispatcher.send_to_construtor(
session_id: session_id,
message: '__START__ Inicie o fluxo de criação de novo agente Hermes. Comece pela primeira pergunta do Bloco 1 (nome do agente).'
)
render json: { ok: true, session_id: session_id }, status: :accepted
rescue HermesBuilder::Dispatcher::DispatchError => e
Rails.logger.error("[HermesBuilder#start] dispatch failed: #{e.message}")
render json: { error: "Falha ao contatar Construtor: #{e.message}" }, status: :bad_gateway
end
def reset
HermesBuilder::Storage.clear(session_key)
render json: { ok: true }