From 18ae589d3aa6b2b1d0a5b6ecbe0ebcce3c3c7325 Mon Sep 17 00:00:00 2001 From: WanderMotta Date: Tue, 11 Aug 2026 19:00:04 -0300 Subject: [PATCH] Redator de SD - preparado para deploy --- .dockerignore | 8 + .gitignore | 7 + DEPLOY.md | 89 +++ Dockerfile | 26 + Manual-Redacao-SD.md | 401 ++++++++++++ P2-SD8_MVP_VacinaEmDia.md | 254 ++++++++ README.md | 0 SD/SD-9 - Vacina em Dia.pdf | Bin 0 -> 262849 bytes SD/copia_base-conhecimento.json | 43 ++ app/.env.example | 29 + app/.gitignore | 3 + app/README.md | 88 +++ app/Subir_aplicacao.txt | 3 + app/package-lock.json | 890 +++++++++++++++++++++++++++ app/package.json | 15 + app/public/app.js | 980 ++++++++++++++++++++++++++++++ app/public/index.html | 187 ++++++ app/public/styles.css | 168 +++++ app/src/base.js | 152 +++++ app/src/config.js | 47 ++ app/src/extrator.js | 160 +++++ app/src/historico.js | 38 ++ app/src/llm/anthropic.js | 93 +++ app/src/llm/provider.js | 16 + app/src/redator/prompt.js | 86 +++ app/src/redator/tools.js | 45 ++ app/src/server.js | 381 ++++++++++++ app/src/store.js | 80 +++ app/src/validador.js | 331 ++++++++++ base-conhecimento.json | 69 +++ github.bat | 5 + prompt-redator-sd.md | 102 ++++ prototipo-criador-de-demanda.html | 719 ++++++++++++++++++++++ regras-sd.json | 136 +++++ schema-sd.json | 181 ++++++ 35 files changed, 5832 insertions(+) create mode 100644 .dockerignore create mode 100644 .gitignore create mode 100644 DEPLOY.md create mode 100644 Dockerfile create mode 100644 Manual-Redacao-SD.md create mode 100644 P2-SD8_MVP_VacinaEmDia.md create mode 100644 README.md create mode 100644 SD/SD-9 - Vacina em Dia.pdf create mode 100644 SD/copia_base-conhecimento.json create mode 100644 app/.env.example create mode 100644 app/.gitignore create mode 100644 app/README.md create mode 100644 app/Subir_aplicacao.txt create mode 100644 app/package-lock.json create mode 100644 app/package.json create mode 100644 app/public/app.js create mode 100644 app/public/index.html create mode 100644 app/public/styles.css create mode 100644 app/src/base.js create mode 100644 app/src/config.js create mode 100644 app/src/extrator.js create mode 100644 app/src/historico.js create mode 100644 app/src/llm/anthropic.js create mode 100644 app/src/llm/provider.js create mode 100644 app/src/redator/prompt.js create mode 100644 app/src/redator/tools.js create mode 100644 app/src/server.js create mode 100644 app/src/store.js create mode 100644 app/src/validador.js create mode 100644 base-conhecimento.json create mode 100644 github.bat create mode 100644 prompt-redator-sd.md create mode 100644 prototipo-criador-de-demanda.html create mode 100644 regras-sd.json create mode 100644 schema-sd.json diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000..683e0c6 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +# Fora do contexto de build (velocidade e segurança) +app/node_modules +app/data +app/.env +app/.env.example +*.zip +SD/ +prototipo-criador-de-demanda.html diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..c36b21f --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +# Segredos e dados locais — NUNCA versionar +app/node_modules/ +app/.env +app/data/ + +# Arquivos soltos que não pertencem ao deploy +*.zip diff --git a/DEPLOY.md b/DEPLOY.md new file mode 100644 index 0000000..f631514 --- /dev/null +++ b/DEPLOY.md @@ -0,0 +1,89 @@ +# Deploy no Coolify — Redator de SD + +Método escolhido: **Dockerfile** (já pronto na raiz do projeto). É a melhor opção aqui porque +o projeto tem dois níveis — os arquivos de configuração do redator na raiz e o app em `app/` — +e o Nixpacks não lida bem com isso (detectaria o Node em `app/` e deixaria de fora +`regras-sd.json`, `schema-sd.json`, o prompt e a base). Docker Compose não acrescenta nada: +é um serviço só, e volumes/variáveis o painel do Coolify resolve. + +## Passo 0 — subir o projeto para um repositório Git PRIVADO + +O Coolify puxa o código de um repositório. Na pasta `D:\IasisSaude`: + +```bash +git init -b main +git add . +git commit -m "Redator de SD - preparado para deploy" +``` + +Crie um repositório **privado** no GitHub (ex.: `iasis/redator-sd`) e: + +```bash +git remote add origin https://github.com/SEU-USUARIO/redator-sd.git +git push -u origin main +``` + +O `.gitignore` da raiz já bloqueia `app/.env` (chave de API!), `app/data/` e `node_modules`. + +## Passo 1 — criar a aplicação no painel + +1. **Projects → seu projeto → + New → Application**. +2. Fonte: **Private Repository (with GitHub App)** → conecte a conta na primeira vez → + selecione o repositório e o branch `main`. +3. Em **Build Pack**, escolha **Dockerfile** (ele acha o `Dockerfile` na raiz sozinho). +4. Em **Ports Exposes**: `3344`. + +## Passo 2 — variáveis de ambiente (aba Environment Variables) + +| Variável | Valor | +|---|---| +| `ANTHROPIC_API_KEY` | a chave (marque como **secret**) | +| `APP_USUARIO` | usuário do login — **obrigatório na internet** | +| `APP_SENHA` | senha FORTE — é a única porta de entrada | +| `LLM_MODEL` | `claude-opus-5` (ou o padrão que preferir) | +| `LLM_MODELOS` | `claude-sonnet-5,claude-haiku-4-5-20251001` | +| `LLM_EFFORT` | `medium` | + +`PORT` não precisa: o padrão 3344 casa com o EXPOSE da imagem. + +## Passo 3 — persistência (aba Storages) — NÃO PULE + +Sem isto, **cada deploy apaga as conversas e o histórico da base**: + +1. **+ Add → Volume Mount** + - Destination Path: `/projeto/app/data` + - (guarda `sds.json`: conversas e rascunhos) +2. **+ Add → File Mount** + - Destination Path: `/projeto/base-conhecimento.json` + - Content: cole o conteúdo ATUAL do seu `D:\IasisSaude\base-conhecimento.json` + - (o app escreve neste arquivo ao enviar SDs e alimentar a base; o file mount + sobrevive aos deploys) + +⚠ A partir do primeiro uso em produção, a base **da VPS** vira a oficial — o arquivo +local no seu PC deixa de valer. Para trazer de volta, copie o conteúdo pela aba +Storages (ou `cat` no terminal do container). + +## Passo 4 — domínio e HTTPS + +1. No seu DNS, crie um registro **A** apontando (ex.: `redator.iasis.tech`) para o IP da VPS. +2. Na aplicação, em **Domains**: `https://redator.iasis.tech`. +3. O Coolify (Traefik) emite o certificado Let's Encrypt sozinho. + +## Passo 5 — health check (opcional, recomendado) + +Aba **Health Check**: Path `/api/sessao`, Port `3344` — responde 200 sem login. + +## Passo 6 — Deploy + +Botão **Deploy** e acompanhe os logs até aparecer: +`Redator de SD rodando em http://localhost:3344`. + +Atualizações futuras: `git add … && git commit && git push` — com **Auto Deploy** +ligado (padrão via webhook do GitHub App), o Coolify rebuilda sozinho. + +## Comportamentos esperados em produção + +- **Sessões em memória**: a cada deploy/restart, é preciso logar de novo. +- **Upload de PDF** (Base de Conhecimento): até 15 MB — passa pelo Traefik sem ajuste. +- **Editar prompt/regras/schema**: edite no repositório e faça push (vira deploy); + a base de conhecimento NÃO — ela vive no file mount e é editada pelo próprio app. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..10bd381 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,26 @@ +# Redator de SD — RMDS / SES-MG × Isis Saúde +# Imagem para deploy (no Coolify: Build Pack = Dockerfile, porta 3344). +# A estrutura do container espelha o projeto: arquivos de configuração do +# redator na raiz (/projeto) e o app em /projeto/app — igual ao disco local. +FROM node:22-alpine + +WORKDIR /projeto + +# Dependências primeiro, para aproveitar o cache entre builds +COPY app/package.json app/package-lock.json ./app/ +RUN cd app && npm ci --omit=dev + +# Arquivos de configuração do redator (fonte única — raiz do projeto). +# base-conhecimento.json entra como semente: em produção ele é sobreposto +# pelo file mount persistente do Coolify (senão cada deploy apagaria o histórico). +COPY prompt-redator-sd.md regras-sd.json schema-sd.json base-conhecimento.json ./ + +# Código do app (o .env NUNCA entra na imagem — variáveis vêm do painel do Coolify) +COPY app/src ./app/src +COPY app/public ./app/public + +ENV NODE_ENV=production +EXPOSE 3344 + +WORKDIR /projeto/app +CMD ["node", "src/server.js"] diff --git a/Manual-Redacao-SD.md b/Manual-Redacao-SD.md new file mode 100644 index 0000000..cc8f83e --- /dev/null +++ b/Manual-Redacao-SD.md @@ -0,0 +1,401 @@ +# Manual de Redação de Solicitação de Demanda (SD) + +## Como transformar uma demanda em entregáveis faturáveis, enquadrados e datados + +Contrato: Pregão Eletrônico nº 29/2025 — Processo 1321127 · SES-MG × Isis Saúde + +--- + +## A regra que organiza tudo + +**O entregável é a unidade faturável.** Ele carrega tipo, item, semanas, USTs e datas próprias. É por ele que se mede, se fatura e se controla saldo de pool. + +A fase — Descoberta, Design, Arquitetura, Construção — **não é um container**. É um atributo do entregável, e o que define seu time-box. + +``` +ENTREGÁVEL +├── Tipo (Descoberta / Design / Arquitetura / Construção) → define o time-box +├── Item do TR (I-02 / I-03 / I-04 / I-05) → define de qual pool debita +├── Semanas (2 a 4) → define a duração +├── USTs = time-box × semanas → o valor faturável +├── Período (data inicial a data final) → quando aconteceu +├── Artefatos comprobatórios → o que prova +└── Critérios de aceite → como se verifica +``` + +Duas consequências práticas: + +- **Uma SD pode ter dois entregáveis do mesmo tipo**, com escopos e datas diferentes. Não existe "a fase de Construção" — existem os entregáveis de Construção que a demanda exigiu. +- **Entregáveis de itens diferentes nunca se somam na mesma linha.** Cada um debita de um pool distinto e é medido separadamente. + +--- + +# PARTE I — MONTAGEM DOS ENTREGÁVEIS + +--- + +## Passo 1 — Entenda a demanda + +Três perguntas: + +**1. O que o cliente quer ver funcionando no final?** +Não o que precisa ser feito — o que ele quer *ver*. + +**2. Isso já existe ou é novo?** +Já existe e precisa mudar → manutenção. Nunca existiu → construção nova. + +**3. Qual é a natureza do trabalho?** +Construir um pedaço da plataforma? Automatizar um processo? Criar um dashboard? Treinar alguém? + +Demanda com mais de uma natureza cruza mais de um item. Isso é normal — vira entregáveis separados. + +--- + +## Passo 2 — Decomponha em atividades + +Blocos de trabalho que uma pessoa consiga explicar em uma frase. Não precisa ser granular. + +--- + +## Passo 3 — Enquadre cada atividade + +``` +1. É plataforma crescendo (módulo novo, fonte nova, conector novo)? → I-02 +2. É ensinar alguém a usar algo que já foi implantado? → I-03 +3. É automatizar tarefa repetitiva / corrigir algo em produção? → I-04 +4. É criar dashboard, algoritmo, IA, busca ativa, análise? → I-05 +``` + +**Teste da estrada** — para a dúvida clássica entre I-02 e I-04: + +> I-02 constrói a estrada. I-04 põe o piloto automático. +> Primeira vez que conecta ou constrói → I-02. +> Automatizar algo que já poderia ser feito manualmente → I-04. + +**Armadilhas frequentes:** + +| Situação | Item correto | Por quê | +|---|---|---| +| Produto novo sendo construído | I-02 | Ainda não existe para ser mantido | +| Dashboard, índice, score, ranking | I-05 | É inteligência, não automação | +| App em produção com bug de interface | I-04 | Manutenção perfectiva | +| Regra de negócio nova no motor | I-05 | Evolução de algoritmo | +| Oficina de discovery pré-implantação | I-05 | I-03 é consultoria pós-implantação | + +--- + +## Passo 4 — Agrupe as atividades em entregáveis + +Agrupe atividades que compartilham **o mesmo item e o mesmo tipo** e que produzem um artefato final coerente. + +| Tipo | O que entra | Pergunta-guia | +|---|---|---| +| **Descoberta** | Levantamento, mapeamento, investigação | "O que eu precisava saber antes de começar?" | +| **Design** | Visualização, UX, fluxos, protótipos, modelagem de regras | "Como vai parecer?" | +| **Arquitetura** | Planejamento técnico, modelo de dados, viabilidade | "Como vai ser construído?" | +| **Construção** | Implementação, teste, publicação | "Está funcionando?" | + +**Quando quebrar em mais de um entregável do mesmo tipo:** + +- O escopo excede 4 semanas → quebre, porque 4 é o teto por entregável +- Duas frentes rodam em paralelo com times distintos → entregáveis separados, mesmo período +- Um pedaço pode ser faturado antes do outro → separe, para não travar o faturamento do que já está pronto +- Atividades pertencem a itens diferentes → obrigatoriamente separados + +**Quando não quebrar:** + +- Só para "ter mais entregáveis". Cada entregável custa USTs próprias — quebrar infla o orçamento sem entregar mais +- Quando o resultado só faz sentido inteiro. Metade de um dashboard não é faturável + +Nem toda demanda tem os quatro tipos. Uma oficina pode ter só Descoberta e Construção. Uma manutenção, só Construção. Tipo inventado para "ficar completo" é glosa esperando acontecer. + +--- + +## Passo 5 — Defina semanas e datas de cada entregável + +``` +Mínimo: 2 semanas (regra contratual) +Máximo: 4 semanas +Típico: 2 a 3 semanas +``` + +| Se o entregável... | Semanas | +|---|---| +| É simples, escopo fechado, poucas dependências | 2 | +| É médio, precisa de reuniões e validação | 3 | +| É complexo, múltiplos stakeholders, muita incerteza | 4 | + +**Regras de data:** + +- Entregáveis **encadeados** (um depende do outro) não podem se sobrepor. Se Descoberta e Design compartilham semanas, os mesmos perfis aparecem alocados a 200% — é achado de auditoria. +- Entregáveis **paralelos** podem compartilhar período, desde que sejam times distintos. Declare isso explicitamente na SD, ou parecerá erro. +- A data final de cada entregável é a data que habilita seu faturamento. Ela precisa fazer sentido contra a evidência: entregável que fecha em 31/05 não pode ter como comprovação um documento enviado em 14/06. + +--- + +## Passo 6 — Calcule as USTs de cada entregável + +``` +USTs = time-box × semanas +``` + +**Time-box por tipo e item:** + +| | Descoberta | Design | Arquitetura | Construção | +|---|---|---|---|---| +| **I-02** | 40h | 40h | 40h | **40h** | +| **I-03** | 40h | 40h | 40h | **40h** | +| **I-04** | 40h | 40h | 40h | **80h** | +| **I-05** | 40h | 40h | 40h | **80h** | + +Descoberta, Design e Arquitetura são sempre 40h. A única diferença está na Construção: 40h em I-02/I-03, 80h em I-04/I-05. + +**USTs prontas para copiar:** + +| Semanas | Desc / Design / Arq (todos os itens) | Construção I-02 e I-03 | Construção I-04 e I-05 | +|---|---|---|---| +| 2 | 80 | 80 | **160** | +| 3 | 120 | 120 | **240** | +| 4 | 160 | 160 | **320** | + +--- + +## Passo 7 — Defina os artefatos comprobatórios de cada entregável + +**Entregável ≠ artefato.** O entregável é a unidade faturável. Os artefatos são os documentos que provam que ele aconteceu. + +Quanto maior o valor do entregável, mais lastro documental ele precisa carregar: + +| USTs do entregável | Artefatos comprobatórios mínimos | +|---|---| +| 80 | 1 | +| 120 a 160 | 2 | +| 240 ou mais | 3 | + +Nomeie artefatos que **existem de fato**. Documento comprobatório inventado é pior que entregável sem comprovação — o primeiro é problema de integridade, o segundo é só pendência. + +--- + +# PARTE II — REDAÇÃO DA SD + +--- + +## Cabeçalho + +| Campo | Regra de preenchimento | +|---|---| +| Cliente / Contratante | Secretaria de Estado de Saúde de Minas Gerais (SES-MG) | +| Contratada | Isis Saúde | +| Contrato | Pregão Eletrônico nº 29/2025 — Processo 1321127 | +| Projeto / Solução | Nome do projeto real desta demanda. **Confira** — é o campo mais copiado por engano | +| Nº da SD | Sequencial | +| Versão | V1, V2… | +| Data de abertura | DD/MM/AAAA. Nunca escreva "não formalizada" nem qualquer confissão de irregularidade | +| Período de execução | Da data inicial do primeiro entregável à data final do último | +| Item ou itens do TR | Número **e** título literal. Se a SD cruza itens, liste todos | +| Quantidade total de UST | Soma dos entregáveis, com número por extenso | +| Prazo de execução | Duração do calendário, não a soma das semanas quando há paralelismo | +| Quantidade de entregáveis | Número | + +--- + +## 1. Objetivo + +Dois a quatro períodos. Diz o que a SD formaliza e qual resultado permite alcançar. Não repete o contexto. + +--- + +## 2. Contexto e escopo da demanda + +Três a cinco parágrafos: + +1. **O problema de gestão** — o que trava, o que a SES-MG não consegue fazer hoje +2. **O insumo disponível** — o que já existe e por que não basta na forma atual +3. **O que a demanda entrega** — a solução em termos concretos +4. **O achado ou resultado**, quando houver +5. **O desdobramento** — o que isso destrava adiante + +Números reais e verificáveis. Se um número aparece na SD e outro no artefato entregue, a inconsistência aparece sozinha na primeira leitura cruzada. + +--- + +## 3. Quadro de entregáveis + +O coração da SD. Uma linha por entregável faturável. + +| # | Entregável | Tipo | Item TR | Semanas | Time-box | UST | Período | +|---|---|---|---|---|---|---|---| +| 1 | | | | | | | | +| 2 | | | | | | | | +| 3 | | | | | | | | +| | **Total** | | | | | **XXX** | | + +**Confira antes de seguir:** +- Cada linha: `UST = time-box × semanas` +- Nenhum entregável abaixo de 2 nem acima de 4 semanas +- Time-box de Construção correto para o item (40h ou 80h) +- Períodos de entregáveis encadeados não se sobrepõem +- Se há paralelismo, está declarado em nota +- A soma da coluna UST bate com o cabeçalho + +Quando a SD cruza itens, acrescente uma linha de subtotal por item — é assim que o controle de pool é feito. + +--- + +## 4. Detalhamento dos entregáveis + +Um bloco por entregável, na ordem do quadro. Quatro seções cada. + +``` +ENTREGÁVEL N — [Nome do artefato final] +Tipo · Item TR · X semanas · XX UST · DD/MM a DD/MM + +Resumo +Três linhas. O que o entregável fez e o que produziu. + +Atividades previstas +Bullets. Blocos de trabalho concretos, de seis a nove. + +Artefatos comprobatórios +- [Documento com nome real e, quando houver, data] +- [Documento] + +Critérios de aceite +Bullets. Condições verificáveis por terceiro. +``` + +**Sobre os critérios de aceite** — o teste é se alguém que não participou consegue dizer sim ou não olhando o artefato: + +| Serve | Não serve | +|---|---| +| "Índice reproduzível: mesma base e mesmo código produzem resultado idêntico" | "Modelo bem construído" | +| "Dashboard acessível pelos perfis indicados pela SES-MG" | "Dashboard funcionando adequadamente" | +| "Toda pergunta do formulário mapeada à dimensão que alimenta" | "Documentação completa" | + +--- + +## 5. Enquadramento no Termo de Referência + +Seção obrigatória. É a que sustenta a SD se a fiscalização contestar o item — e é a que evita a reclassificação que muda o time-box de Construção e derruba as USTs pela metade. + +| | | +|---|---| +| **Item do TR** | Número e título literal, exatamente como no TR | +| **Descrição do item** | Transcrição do escopo do item conforme o TR | +| **Aderência desta SD** | Bullets ligando o que foi entregue ao que o item prevê | + +**Regras para os bullets de aderência:** + +- Um bullet por componente entregue, de três a cinco no total +- Use o vocabulário do item, não o vocabulário interno da equipe +- Descreva o que foi construído, não o que foi discutido +- Não cite componente que a SD não tem só para reforçar o encaixe + +**Exemplo — entregáveis de índice e dashboard (I-05):** + +| | | +|---|---| +| **Item do TR** | I-05 – Serviços técnicos de inteligência e produtos analíticos de gestão em saúde | +| **Descrição do item** | Serviços técnicos de inteligência aplicada aos dados da plataforma, compreendendo a concepção e o desenvolvimento de algoritmos analíticos, modelos de classificação, índices e dashboards gerenciais destinados a apoiar a tomada de decisão das áreas demandantes da SES-MG. Inclui extração de fenótipos, clusterização e priorização de populações e territórios, apuração de indicadores e disponibilização dos resultados em painéis, relatórios e produtos analíticos de gestão. | +| **Aderência desta SD** | • Concepção do algoritmo de classificação em sete dimensões ponderadas, com penalidade, bônus de cadeia e normalização em escala de 0 a 100.
• Desenvolvimento do motor de cálculo que processa as respostas do formulário e atribui score e faixa a cada município.
• Construção do dashboard gerencial, com ranking, cadeia de envios por domínio e corte por macrorregião.
• Disponibilização do painel aos gestores da SES-MG como insumo de priorização das ondas de integração. | + +**Quando a SD cruza dois itens:** repita a seção 5 inteira para cada item, e indique quais entregáveis do quadro pertencem a cada um. + +--- + +# PARTE III — VALIDAÇÃO + +**Enquadramento** +``` +[ ] Cada entregável tem item definido (I-02 / I-03 / I-04 / I-05) +[ ] Produto novo está no I-02, não no I-04 +[ ] Dashboard, índice ou algoritmo está no I-05, não no I-04 +[ ] Automação de tarefa repetitiva está no I-04 +[ ] Treinamento pós-implantação está no I-03 +[ ] Entregáveis de itens diferentes estão em linhas separadas +[ ] Há subtotal por item quando a SD cruza itens +``` + +**Cálculo** +``` +[ ] Cada entregável entre 2 e 4 semanas +[ ] UST de cada linha = time-box × semanas +[ ] Time-box de Construção correto (40h em I-02/I-03, 80h em I-04/I-05) +[ ] Soma da coluna UST bate com o cabeçalho +[ ] Entregáveis encadeados sem sobreposição de período +[ ] Paralelismo, quando existe, está declarado +[ ] Prazo do cabeçalho reflete o calendário, não a soma das semanas +``` + +**Documento** +``` +[ ] Nome do projeto é o desta demanda, não copiado de outra SD +[ ] Número do item e título do item correspondem entre si +[ ] Título do item confere com a redação literal do TR +[ ] Números do contexto batem com os artefatos entregues +[ ] Produto tem um nome só ao longo de todo o documento +[ ] Nenhum campo confessa irregularidade +[ ] Cada entregável tem o mínimo de artefatos comprobatórios da sua faixa +[ ] Todo artefato citado existe de fato +[ ] Data final de cada entregável é coerente com a data dos seus artefatos +[ ] Todo critério de aceite é verificável por terceiro +[ ] Seção 5 preenchida com aderência específica, não genérica +``` + +--- + +# ANEXO — Referência rápida + +## Enquadramento em 4 perguntas + +``` +1. É plataforma crescendo (módulo novo, fonte nova, conector novo)? → I-02 +2. É ensinar alguém a usar algo que já foi implantado? → I-03 +3. É automatizar tarefa repetitiva / corrigir algo em produção? → I-04 +4. É criar dashboard, algoritmo, IA, busca ativa, análise? → I-05 +``` + +## USTs por entregável + +| Semanas | Desc / Design / Arq | Construção I-02 e I-03 | Construção I-04 e I-05 | +|---|---|---|---| +| 2 | 80 | 80 | 160 | +| 3 | 120 | 120 | 240 | +| 4 | 160 | 160 | 320 | + +## Artefatos comprobatórios por entregável + +| USTs | Artefatos mínimos | +|---|---| +| 80 | 1 | +| 120 a 160 | 2 | +| 240+ | 3 | + +## Conjunto típico de entregáveis (3 + 2 + 2 + 3 semanas) + +| Item | Descoberta | Design | Arquitetura | Construção | **Total** | +|---|---|---|---|---|---| +| I-02 | 120 | 80 | 80 | 120 | **400** | +| I-03 | 120 | 80 | 80 | 120 | **400** | +| I-04 | 120 | 80 | 80 | 240 | **520** | +| I-05 | 120 | 80 | 80 | 240 | **520** | + +## Estrutura da SD + +``` +Cabeçalho (identificação) +1. Objetivo +2. Contexto e escopo da demanda +3. Quadro de entregáveis (tipo × item × semanas × time-box × UST × período) +4. Detalhamento dos entregáveis + 4.N Resumo (3 linhas) + 4.N Atividades previstas (bullets) + 4.N Artefatos comprobatórios (nomeados) + 4.N Critérios de aceite (bullets verificáveis) +5. Enquadramento no Termo de Referência + Item do TR / Descrição do item / Aderência desta SD +``` + +--- + +*Documento de apoio à formalização de demandas — RMDS / SES-MG.* diff --git a/P2-SD8_MVP_VacinaEmDia.md b/P2-SD8_MVP_VacinaEmDia.md new file mode 100644 index 0000000..eb7119e --- /dev/null +++ b/P2-SD8_MVP_VacinaEmDia.md @@ -0,0 +1,254 @@ +**SOLICITAÇÃO DE DEMANDA (SD)** + +**MVP – Vacina em Dia** + +*Caderneta Vacinal Digital no MG App com Dados do PNI e Alertas de Pendências* + +| Cliente / Contratante | Secretaria de Estado de Saúde de Minas Gerais (SES-MG) | +| :---- | :---- | +| **Contratada** | Isis Saúde | +| **Projeto / Solução** | P2 \- Vacinação | +| **Nº da SD** | SD8 | +| **Versão** | V1 | +| **Data de Abertura** | 01/05/2026 | +| **Item do Termo de Referência** | I-02 e I-05 | +| **Quantidade de UST Estimadas** | 1120 (mil cento e vinte) UST | +| **Prazo de Execução Estimado** | 5 (cinco) semanas | +| **Sprints Contempladas** | Descoberta, Design, Arquitetura e Construção | + +# **1\. Objetivo** + +Entregar ao cidadão mineiro a caderneta vacinal digital: histórico de imunização unificado a partir da RNDS, com identificação por CNS/CPF e visão longitudinal por pessoa. + +# **2\. Justificativa** + +O histórico vacinal do cidadão está fragmentado entre municípios, sistemas e cadernetas de papel — não existe visão única nem para o cidadão nem para o gestor. Esta SD é a primeira prova pública da RMDS: demonstra que a plataforma unifica identidade e linha do tempo do cidadão, e cria a porta de entrada de relacionamento com a população. + +# **3\. Contexto** + +O projeto Vacina em Dia consiste no desenvolvimento e implementação de um ecossistema digital inteligente voltado à gestão da saúde vacinal do cidadão. A demanda origina-se da necessidade estrita da Secretaria de Estado de Saúde de Minas Gerais (SES-MG) de prover transparência, acessibilidade e resolutividade clínica à população mineira diretamente por meio do MG App (o super-app de serviços públicos do Estado). + +O motor opera de forma determinística, cruzando ativamente o histórico vacinal em tempo real do indivíduo (extraído via integração escalável com a base nacional do PNI/RNDS) com as diretrizes normativas vigentes do Programa Nacional de Imunizações (PNI 2026), do Manual de Centros de Referência para Imunobiológicos Especiais (CRIE) e das resoluções técnicas estaduais da SES-MG + +O desenvolvimento e a homologação do sistema submeteram-se a uma restrição temporal: + +* Janela de Execução: O projeto foi planejado e codificado em um ciclo ágil e ininterrupto de 02/06/2026 a 03/07/2026 +* Marco de Publicação Impositiva: A disponibilização final em produção ocorreu em 02 de julho de 2026, às 11h00, cumprindo a obrigatoriedade institucional de entrar em operação pelo menos 24 horas antes do início do período de vedação eleitoral (estabelecido em 04 de julho de 2026), inviabilizando qualquer direito a retrabalho ou janelas de hotfix pós-prazo. + +# **4\. Escopo da Demanda** + +**Públicos e perfis**: calendário por faixa etária \+ profissionais de saúde \+ gestantes; perfil ativado por triagem autodeclarada no formulário. + +**Motor de regras**: determinístico, em memória, 60+ regras vacinais em arquivos versionados, auditáveis linha a linha. Lógicas críticas: Febre Amarela (imunização) e dT/dTpa (contagem de doses). + +**Sinalização da caderneta**: 5 status: Em Dia · Próxima dose · Atrasada · Não Digitalizada (sem registro digital para doses esperadas pré-2020) · Aguarda Semana (gestante que ainda não atingiu a IG mínima). + +**Filtro de segurança clínica**: ocultação silenciosa e sistemática de vacinas vivas atenuadas (Tríplice Viral, Varicela, Dengue) quando o perfil ativo é gestante. + +**Contingência de campanhas**: notas explicativas para Influenza e COVID-19, cujo histórico pode residir em índices de campanha (RCA) não consumidos pela API de rotina no MVP. + +**Regularização**: canal direto para o WhatsApp institucional da SES-MG. + +**Arquitetura:** hospedado em nuvem própria Isis integrado ao MG App. + +# + +# + +# + +# + +# **5\. Escopo por Sprint** + +## 5.1 · DESCOBERTA (D) + +### Discovery Negocial e Institucional · I-02 · 120 USTs · 3 Semanas + +* Definição de público-alvo e recorte do MVP +* Validação institucional do escopo com Sub-secretaria de Vigilância SES-MG +* Consolidação de decisões sobre perfis, funcionalidades e exclusões +* Alinhamento jurídico/LGPD + +### Discovery Técnico Integrado · I-02 · 120 USTs · 3 Semanas + +* Análise da fonte PNI via bucket Elastic Search +* Mapeamento empírico do catálogo de vacinas +* Documentação do contrato de dados e schema do bucket +* Definição da arquitetura de embedamento no MGapp em alinhamentos técnicos com Prodemge (SEPLAG) + +### Discovery Clínico do Motor de Regras · I-05 · 80 USTs · 2 Semanas + +* Levantamento das regras vacinais por calendário e perfil (universo teórico) +* Recorte para MVP simplificado +* Definição da hierarquia clínica de resolução de conflitos +* Definição dos 6 status +* Definição de regras de corte temporal 01/01/2020 para "não digitalizada" +* Estruturação do vocabulário fechado de contraindicações + +## 5.2 · DESIGN (DE) + +### Design do MVP \- Vacina em Dia · I-02 · 80 USTs · 2 Semanas + +* Desenho do fluxo do formulário em 2 camadas (multi-seleção \+ condicional) +* Design das 12 telas do MVP +* Microcopy revisada e aprovada pelo PO +* Tratamento de casos de borda (gestante sem semana, condição especial redirecionada) +* Especificação visual dos status (paleta, iconografia, tipografia) +* Regras de exibição por perfil (BLOQUEADA oculta ao cidadão; AGUARDA IG apenas gestante) +* Microcopy do canal de regularização (WhatsApp SES) +* Aplicação de identidade institucional (brasão MG, SES-MG, Zé Gotinha) + +### Design da Modelagem Clínica do Motor · I-05 · 80 USTs · 2 Semanas + +* Análise de alternativas de motor e justificativa da escolha +* Desenho de casos de uso clinicamente relevantes (personas) +* Desenho do formulário de triagem — o que perguntar, inferir e não perguntar +* Estratégias de fallback (PNI vazio, corrompido, parcial) +* Decisões clínicas de design (hierarquia de conflitos, precedência de contraindicação) +* Subfunções clínicas especiais (Febre Amarela pré-5a · dT esquema acumulado) +* Trilha de validação clínica com SVE + +## 5.3 · ARQUITETURA (A) + +### Arquitetura da Aplicação · I-02 · 160 USTs · 4 Semanas + +* Definição de stack e componentes +* Especificação dos fluxos de comunicação (consulta PNI, autenticação, calendário) +* Especificação da integração com MGapp +* Definição das tabelas de domínio e de catálogo +* Solução para não-persistência de dado vacinal +* Especificação de APIs de autenticação (Swagger) e zonas de segurança + +### Arquitetura Técnica do Motor · I-05 · 80 USTs · 2 Semanas + +* Especificação do motor +* Definição das etapas de processamento +* Definição da hierarquia de conflitos implementável +* Contrato do endpoint +* Estratégia de carregamento das regras e de testabilidade +* Registro de gatilhos de refatoração + +## 5.4 · CONSTRUÇÃO + +### Backend \+ Integração PNI · I-02 · 160 USTs · 4 Semanas + +* Implementação das APIs REST conforme contratos +* Implementação da chamada ao middleware Azure SES-MG (login → token → search) +* Implementação dos 3 mappers de sigla PNI → vacina canônica +* Tratamento de códigos PNI corrompidos +* Consolidação do Swagger +* Testes unitários e de integração com medição de cobertura + +### Frontend \+ Publicação no MGApp · I-02 · 80 USTs · 2 Semanas + +* Implementação das telas e do formulário +* Aplicação da identidade visual (Zé Gotinha em 3 pontos) +* Otimização mobile-first +* Telas de fallback (aviso desktop · erro "acesse pelo MG App") +* Consumo das APIs e tratamento de erros na UI +* Publicação da URL (DNS ATI SES) +* Recebimento e validação do token JWT +* Cadastro da integração no back-office do MG App (Prodemge) + +### Construção e Validação Final do Motor · I-05 · 160 USTs · 2 Semanas + +* Carregamento das regras ativas +* Implementação das etapas de processamento +* Implementação da hierarquia de resolução de conflitos +* Execução dos testes das personas e de casos adicionais +* Disponibilização do endpoint +* Sessão de validação clínica final com SVE +* Incorporação de ajustes clínicos e testes de validação + + +# + +# + +# + +# + +# + +# + +# + +# + +# + +# + +# + +# **6\. Enquadramento no Termo de Referência** + +| Item | Título | Descrição | USTs | +| :---- | :---- | :---- | :---- | +| I-02 | Implementação e Desenvolvimento da Solução: módulos, integrações e camada blockchain | Executar atividades de desenvolvimento de software para soluções com blockchain, incluindo contratos inteligentes e atividades relacionadas com a integração com sistemas legados. O escopo abrange, além da camada blockchain, o desenvolvimento de interfaces e APIs, a integração com sistemas de informação existentes da SES-MG, a conformidade com padrões de interoperabilidade, a segurança da informação com controle de acesso por identidade digital, a rastreabilidade e a conformidade com a LGPD e a PNSI. | Desenvolvimento de módulo da solução RMDS — interfaces, integração com sistema legado, autenticação por identidade digital e implantação segura. Desenvolvimento de interfaces: APIs REST documentadas em Swagger e front-end do Vacina em Dia. Conformidade LGPD Implantação: publicação em produção | +| | | | | +| I-05 | Serviços técnicos de inteligência e produtos analíticos de gestão em saúde | Serviços técnicos de inteligência aplicada aos dados da plataforma, compreendendo a concepção e o desenvolvimento de algoritmos analíticos, modelos de classificação, índices e dashboards gerenciais destinados a apoiar a tomada de decisão das áreas demandantes da SES-MG. Inclui extração de fenótipos, clusterização e priorização de populações e territórios, apuração de indicadores e disponibilização dos resultados em painéis, relatórios e produtos analíticos de gestão. | Modelagem do algoritmo: regras vacinais ativas com hierarquia de resolução de conflitos entre perfis.Incluindo regras de segurança clínica "De-para" semântico: mapeamento de 103 siglas do índice PNI/MG para vocabulário canônico de vacina. | +| | | | | + +# **7\. Estimativa de Esforço e Prazo** + +Para a execução integral desta demanda, contemplando as fases de Descoberta, Design, Arquitetura e Construção, foi estimado o esforço de 1120 (mil cento e vinte) UST, a serem executadas no prazo de 5 (cinco) semanas. + +| Quantidade de UST | Prazo de Execução | Início Previsto | Fim Previsto | +| :---: | :---: | :---: | :---: | +| **1.120 UST** | **4,42 semanas** | **02/06/2026** | **04/07/2026** | + +| Fase | UST | Item TR | Timebox (Semanas) | Valor (R$) | +| ----- | :---: | :---: | :---: | :---: | +| **Descoberta** | 240 | I-02 | 6 | 127.999,20 | +| | 80 | I-05 | 2 | 44.444,00 | +| **Design** | 80 | I-02 | 2 | 42.666,40 | +| | 80 | I-05 | 2 | 44.444,00 | +| **Arquitetura** | 160 | I-02 | 4 | 85.332,80 | +| | 80 | I-05 | 2 | 44.444,00 | +| **Construção** | 240 | I-02 | 4 | 127.999,20 | +| | 160 | I-05 | 2 | 88.888,00 | +| **Total** | **1.120** | **I-02, I-05** | **24** | **R$ 606.217,60** | + +# + +# + +# + +# + +# + +# + +# + +# + +# + +# **8\. Aprovação** + +Declaro estar de acordo com a Solicitação de Demanda apresentada pela contratada Isis Saúde, avaliados pela equipe técnica da Secretaria de Estado de Saúde de Minas Gerais e estão em conformidade com os requisitos estabelecidos no Termo de Referência do Pregão Eletrônico nº 29/2025, observados os termos, escopo, atividades e esforço descritos nesta Solicitação de Demanda. + +| Product Owner | César Augusto Castro Alves Martins | +| :---- | :---- | +| Fiscal do Contrato | Guilherme Amaral Bernardino | +| Gestor do Contrato | Rafael Matos Paiva | + +## + +## 8.1. Formalização do Aceite + +Assinatura digital do aceite por meio do sistema Gov.br: + +*Assinatura — Product Owner* + +*Assinatura — Fiscal do Contrato* + +*Assinatura — Gestor do Contrato* + diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/SD/SD-9 - Vacina em Dia.pdf b/SD/SD-9 - Vacina em Dia.pdf new file mode 100644 index 0000000000000000000000000000000000000000..b9758279039713bbd6e9cc3f33f0dfa3b23bf8ed GIT binary patch literal 262849 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
FecseQ^<_8s+XIsBt_} zFNns5nj-{gjm1SNA)F%o4ft4{itp-75q^1+&*v*HN?C@V^ee}&9r*}SLU`!PlDR@D zraq-n-z%+3#k*3lOFB|?y!YCE9u{|o4NK*vT?^Y&WLKiN=m?PhhLy81jt%(eTq&g} zzdT6|JSoyIHLR3+Wef)%-jIHIQlhx%YvYTHQug}xt9-}R@!M4mDRVg0Bu`2;Z9U~7 zdmEgqBz)W3dzF+rG^2gwQy&f!iG;JbNa|I`QIm>`8dgs%E<(;oaZ#pDSpb%HuEy_N z-G$59uv+TfwM;GRwQ3nu)`pdG!>X?D;_B^fSSmLx?OHl9lY4zCK3hG(?D_3#1yk{^ zNr`M!mSKK-ic+6+PD=PPU{9UdUXKeVcTMtSn*D4{XEtW)%=U(%hY~J9b1L4Ml9os* znAt9;2>%!yEE@y8OU9_e<4d68qN#1esZz9`m!;mnLke}u1D}kJ>B`11e*W)(FSCO% zH78G_+}PRM+bBzoy`8;X{mPETvc${1N2X5g?QZClQqb0(LS6mJW4k=5#%mKPzH=q2 zNfZ}JSRNW@w%2)l=CikLL${F9)TbCM&{Jn(Oj4A;IeY4oQZT#SC)KB5PJ6eLLwP z5}7N)L*gr4SmveLlp|bRlqv}K^0M@joZ`bNy8XDPHX-rm6bQI2XUS7%%x{+(de6#4 zCMH``EENrMgWR>UH`}pr7Z}YW&Zm8xCr5X>R*zQI+y>q2Ea1iDK9QAlBXOYTEglXspsA)a>LRT;m1eU z(iB-gv)v~xO<*n=cV67w-^heUFku-}Gw|@Te?hDTQXY`%;7-S0}ZjP3lZY zot;t&&1m=eJW`6D(e9I$cctQT7w#=>nfkQNZ^yOn>Ydf@OF_cpOL$V6+3nKuu4S^% z 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 *4pDHEVUa#M)f^D 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 bT0iS+>*M?TdrJ@m0x+IVOiWY%Fx~$HmnWhE literal 0 HcmV?d00001 diff --git a/SD/copia_base-conhecimento.json b/SD/copia_base-conhecimento.json new file mode 100644 index 0000000..2677df5 --- /dev/null +++ b/SD/copia_base-conhecimento.json @@ -0,0 +1,43 @@ +{ + "$comment": "Base de conhecimento do redator de SDs — RMDS / SES-MG (PE 29/2025). Terceiro arquivo da arquitetura: regras-sd.json = regras; schema-sd.json = formato; ESTE arquivo = histórico. Anexe-o ao projeto/Cowork junto com os outros dois. PREENCHIMENTO: apague os registros de exemplo (id terminando em '-EXEMPLO') e cadastre apenas fatos reais — o redator é proibido de inventar precedentes, e esta base só serve se for verdadeira. Uma linha nova em 'sds' a cada SD enviada; uma em 'capacidades' a cada capacidade entregue.", + "versao": "0.1.0", + "atualizado_em": "2026-08-07", + + "convencoes": { + "status_sd": ["rascunho", "enviada", "aprovada", "em_execucao", "concluida", "faturada", "cancelada"], + "status_que_consomem_pool": ["enviada", "aprovada", "em_execucao", "concluida", "faturada"], + "status_capacidade": ["em_construcao", "entregue"], + "saldo_de_pool": "saldo(item) = pool_ust_total do item em regras-sd.json − soma de ust_por_item das SDs cujo status consome pool", + "tags": "minúsculas, sem acento, singular quando possível; ex.: dashboard, integracao, niten, regulacao, vacinacao, treinamento, automacao", + "ids": "SD-NN segue a numeração oficial do contrato; CAP-NN é sequencial interno desta base" + }, + + "sds": [ + { + "id": "SD-00-EXEMPLO", + "$comment": "REGISTRO DE EXEMPLO — apague e substitua pelas SDs reais do contrato.", + "titulo": "Painel de fila cirúrgica da macrorregião Centro", + "projeto": "Fila Cirúrgica", + "itens_tr": ["I-05"], + "ust_total": 240, + "ust_por_item": { "I-05": 240 }, + "status": "faturada", + "periodo": { "inicio": "2026-01-05", "fim": "2026-01-23" }, + "resumo": "Uma frase dizendo o que a SD entregou e para quem.", + "tags": ["dashboard", "regulacao", "fila-cirurgica"] + } + ], + + "capacidades": [ + { + "id": "CAP-00-EXEMPLO", + "$comment": "REGISTRO DE EXEMPLO — apague e substitua pelas capacidades reais já entregues na plataforma.", + "titulo": "Integração com o barramento de dados do sistema XYZ", + "descricao": "O que a plataforma já sabe fazer, escrito para responder à pergunta 'isso precisa ser construído de novo?'", + "origem_sd": "SD-00-EXEMPLO", + "item_tr": "I-02", + "status": "entregue", + "tags": ["integracao", "xyz"] + } + ] +} diff --git a/app/.env.example b/app/.env.example new file mode 100644 index 0000000..88fdb29 --- /dev/null +++ b/app/.env.example @@ -0,0 +1,29 @@ +# Copie este arquivo para ".env" (na mesma pasta) e preencha a chave. +# O arquivo .env NUNCA deve ser versionado nem compartilhado. + +# Chave da API da Anthropic (crie em https://console.anthropic.com -> API Keys) +ANTHROPIC_API_KEY= + +# Provedor de LLM (por enquanto apenas "anthropic"; adaptador OpenAI previsto para a fase 1.1) +LLM_PROVIDER=anthropic + +# Modelo padrão +LLM_MODEL=claude-opus-5 + +# Modelos oferecidos nos seletores da interface (Nova SD e Base de Conhecimento), +# separados por vírgula. O LLM_MODEL acima sempre entra na lista, como padrão. +# Obs.: o custo estimado no rodapé usa a tabela de preços do Opus. +LLM_MODELOS=claude-sonnet-5,claude-haiku-4-5-20251001 + +# Esforço de raciocínio do modelo: low | medium | high | xhigh | max +# (vazio = padrão da API, que é high). "medium" acelera bem a entrevista +# com perda mínima de qualidade; suba para high se notar respostas rasas. +LLM_EFFORT=medium + +# Porta do servidor local +PORT=3344 + +# Login do app (usuário único). Enquanto os dois campos estiverem vazios, +# o app roda SEM login e mostra um aviso. +APP_USUARIO= +APP_SENHA= diff --git a/app/.gitignore b/app/.gitignore new file mode 100644 index 0000000..5235962 --- /dev/null +++ b/app/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +.env +data/ diff --git a/app/README.md b/app/README.md new file mode 100644 index 0000000..aa91bc5 --- /dev/null +++ b/app/README.md @@ -0,0 +1,88 @@ +# Redator de SD — RMDS / SES-MG × Isis Saúde + +App local que conduz a entrevista de redação de Solicitações de Demanda (SD) com um LLM, +valida em código contra as regras do contrato (PE 29/2025) e mantém a base de conhecimento. + +## Primeira execução + +1. **Chave de API** — crie em https://console.anthropic.com → *API Keys* e coloque um crédito + inicial pequeno (US$ 5–10 rendem dezenas de SDs). +2. **Configuração** — copie `.env.example` para `.env` (nesta pasta) e preencha + `ANTHROPIC_API_KEY`. Defina também `APP_USUARIO` e `APP_SENHA` para ativar a + tela de login (enquanto vazios, o app roda aberto e mostra um aviso). + O `.env` nunca deve ser versionado nem compartilhado. +3. **Dependências** (só na primeira vez): + ``` + cd D:\IasisSaude\app + npm install + ``` +4. **Rodar**: + ``` + npm start + ``` + e abra http://localhost:3344 no navegador. + +## Como funciona + +- **Nova SD**: cole a demanda bruta → o redator entrevista você (uma pergunta por turno, + opções viram botões) enquanto o painel do documento se atualiza ao vivo. +- **Validação**: os itens `codigo` do checklist são recalculados em JavaScript a cada + atualização — e o resultado é realimentado ao modelo, que se corrige sozinho. +- **Trava de integridade**: exportação e envio ficam bloqueados até todos os artefatos + serem confirmados (checkbox + data) e nenhuma falha de código restar. +- **Envio**: atribui o número definitivo (SD-NN) e registra a SD em + `..\base-conhecimento.json` — saldo de pools e precedentes ficam atualizados + para as próximas entrevistas. +- **Documento / impressão**: o botão "Documento / imprimir (PDF)" abre a SD formatada + como documento oficial em uma aba pronta para Ctrl+P → salvar como PDF. Com pendências + bloqueantes abertas, o documento sai com marca de RASCUNHO. +- **Validar JSON externo** (menu no topo): cole o JSON de uma SD que não nasceu no app — + o validador recalcula tudo, roda o checklist e libera documento/download, sem salvar nada. +- **Alimentar base** (menu no topo): envie o documento de uma SD real (`.pdf`, `.md` ou `.txt`; + `.docx` deve ser exportado como PDF). Uma chamada de LLM extrai o registro estruturado + (itens, UST por item, período, status) e sugere capacidades reutilizáveis; a conferência + determinística em código checa somas, itens, status e impacto nos pools. **Nada é gravado + sem revisão**: você edita o registro, marca/desmarca capacidades e só então grava em + `..\base-conhecimento.json`. Erros de conferência bloqueiam a gravação; regravar o mesmo + SD-NN substitui o registro anterior (capacidades, porém, são sempre acrescentadas — ao + regravar uma SD, desmarque as que já entraram na primeira vez). +- **Login**: usuário único definido no `.env`. As sessões vivem na memória do servidor — + reiniciar o servidor exige novo login. + +## Arquivos de configuração (raiz do projeto, `D:\IasisSaude`) + +| Arquivo | Papel | +|---|---| +| `prompt-redator-sd.md` | Comportamento do redator (system prompt) | +| `regras-sd.json` | Regras duras do contrato — fonte única | +| `schema-sd.json` | Formato da SD | +| `base-conhecimento.json` | Histórico: SDs enviadas e capacidades da plataforma | + +Editar qualquer um deles muda o app na próxima reinicialização — não é preciso mexer em código. + +## Custos + +O rodapé do chat mostra tokens e custo estimado em tempo real. O system prompt +(prompt + regras + base) é cacheado pela API — turnos seguintes da mesma conversa +pagam ~10% do preço no prefixo repetido. + +## Dados + +- `data/sds.json` — SDs e conversas (criado automaticamente; fora do versionamento). +- Provedor/modelo/esforço configuráveis no `.env` (`LLM_PROVIDER`, `LLM_MODEL`, + `LLM_EFFORT` — low/medium/high/xhigh/max; medium recomendado para a entrevista). + Adaptador OpenAI previsto para a fase 1.1 (`src/llm/provider.js`). +- **Seletor de modelo**: `LLM_MODELOS` (lista separada por vírgula) define os modelos + oferecidos nos seletores das telas Nova SD e Base de Conhecimento; o `LLM_MODEL` + sempre entra como padrão. O modelo escolhido fica fixo para a SD inteira — trocar + no meio da conversa descartaria o cache de prompt. O custo do rodapé usa a tabela + de preços do Opus (a observação ao lado do valor lembra isso). + +## Atualização dos arquivos de configuração + +O prompt do redator é remontado automaticamente quando `base-conhecimento.json`, +`prompt-redator-sd.md`, `regras-sd.json` ou `schema-sd.json` mudam no disco — +vale a partir da PRÓXIMA conversa, sem reiniciar o servidor. Exceção: mudanças em +`regras-sd.json`/`schema-sd.json` também alimentam o validador em código e a +ferramenta do modelo, que carregam na subida — para essas duas, reinicie. +Mudanças no `.env` sempre exigem reinício. diff --git a/app/Subir_aplicacao.txt b/app/Subir_aplicacao.txt new file mode 100644 index 0000000..48c6dc1 --- /dev/null +++ b/app/Subir_aplicacao.txt @@ -0,0 +1,3 @@ +Subir Aplicação + +cd D:\IasisSaude\app && npm start \ No newline at end of file diff --git a/app/package-lock.json b/app/package-lock.json new file mode 100644 index 0000000..c6fa60c --- /dev/null +++ b/app/package-lock.json @@ -0,0 +1,890 @@ +{ + "name": "redator-sd", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "redator-sd", + "version": "0.1.0", + "dependencies": { + "@anthropic-ai/sdk": "^0.74.0", + "dotenv": "^16.4.5", + "express": "^4.21.0" + } + }, + "node_modules/@anthropic-ai/sdk": { + "version": "0.74.0", + "resolved": "https://registry.npmjs.org/@anthropic-ai/sdk/-/sdk-0.74.0.tgz", + "integrity": "sha512-srbJV7JKsc5cQ6eVuFzjZO7UR3xEPJqPamHFIe29bs38Ij2IripoAhC0S5NslNbaFUYqBKypmmpzMTpqfHEUDw==", + "license": "MIT", + "dependencies": { + "json-schema-to-ts": "^3.1.1" + }, + "bin": { + "anthropic-ai-sdk": "bin/cli" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + }, + "peerDependenciesMeta": { + "zod": { + "optional": true + } + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.7.tgz", + "integrity": "sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/accepts": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", + "license": "MIT", + "dependencies": { + "mime-types": "~2.1.34", + "negotiator": "0.6.3" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha512-PCVAQswWemu6UdxsDFFX/+gVeYqKAod3D3UVm91jHwynguOwAvYPhx8nNlM++NqRcK6CxxpUafjmhIdKiHibqg==", + "license": "MIT" + }, + "node_modules/body-parser": { + "version": "1.20.6", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.6.tgz", + "integrity": "sha512-p5tAzS57i5MV9fZFDj9LeIiTZEufbSe2eDozP+ElheSUq1m74CRq1jI4mYNDdVs9vQztXFLuk/Gd6BWTdwRJ5g==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "content-type": "~1.0.5", + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "~1.2.0", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "on-finished": "~2.4.1", + "qs": "~6.15.1", + "raw-body": "~2.5.3", + "type-is": "~1.6.18", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/bytes": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", + "integrity": "sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/content-disposition": { + "version": "0.5.4", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", + "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", + "license": "MIT", + "dependencies": { + "safe-buffer": "5.2.1" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/content-type": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", + "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/cookie-signature": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.7.tgz", + "integrity": "sha512-NXdYc3dLr47pBkpUCHtKSwIOQXLVn8dZEuywboCOJY/osA0wFSLlSawr3KN8qXJEyX66FcONTH8EIlVuK0yyFA==", + "license": "MIT" + }, + "node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "license": "MIT", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/depd": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", + "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/destroy": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", + "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", + "license": "MIT", + "engines": { + "node": ">= 0.8", + "npm": "1.2.8000 || >= 1.4.16" + } + }, + "node_modules/dotenv": { + "version": "16.6.1", + "resolved": "https://registry.npmjs.org/dotenv/-/dotenv-16.6.1.tgz", + "integrity": "sha512-uBq4egWHTcTt33a72vpSG0z3HnPuIl6NqYcTrKEg2azoEyl2hpW0zqlxysq2pK9HlDIHyHyakeYaYnSAwd8bow==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://dotenvx.com" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha512-WMwm9LhRUo+WUaRN+vRuETqG89IgZphVSNkdFgeb6sS/E4OrDIN7t48CAewSHXc6C8lefD8KKfr5vY61brQlow==", + "license": "MIT" + }, + "node_modules/encodeurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", + "license": "MIT" + }, + "node_modules/etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha512-aIL5Fx7mawVa300al2BnEE4iNvo1qETxLrPI/o05L7z6go7fCw1J6EQmbK4FmJ2AS7kgVF/KEZWufBfdClMcPg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/express": { + "version": "4.22.2", + "resolved": "https://registry.npmjs.org/express/-/express-4.22.2.tgz", + "integrity": "sha512-IuL+Elrou2ZvCFHs18/CIzy2Nzvo25nZ1/D2eIZlz7c+QUayAcYoiM2BthCjs+EBHVpjYjcuLDAiCWgeIX3X1Q==", + "license": "MIT", + "dependencies": { + "accepts": "~1.3.8", + "array-flatten": "1.1.1", + "body-parser": "~1.20.5", + "content-disposition": "~0.5.4", + "content-type": "~1.0.4", + "cookie": "~0.7.1", + "cookie-signature": "~1.0.6", + "debug": "2.6.9", + "depd": "2.0.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.3.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.0", + "merge-descriptors": "1.0.3", + "methods": "~1.1.2", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "path-to-regexp": "~0.1.12", + "proxy-addr": "~2.0.7", + "qs": "~6.15.1", + "range-parser": "~1.2.1", + "safe-buffer": "5.2.1", + "send": "~0.19.0", + "serve-static": "~1.16.2", + "setprototypeof": "1.2.0", + "statuses": "~2.0.1", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "engines": { + "node": ">= 0.10.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/finalhandler": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.3.2.tgz", + "integrity": "sha512-aA4RyPcd3badbdABGDuTXCMTtOneUCAYH/gxoYRTZlIJdF0YPWuGqiAsIrhNnnqdXGswYk6dGujem4w80UJFhg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "on-finished": "~2.4.1", + "parseurl": "~1.3.3", + "statuses": "~2.0.2", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/forwarded": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.2.0.tgz", + "integrity": "sha512-buRG0fpBtRHSTCOASe6hD258tEubFoRLb4ZNA6NxMVHNw2gOcwHo9wyablzMzOA5z9xA9L1KNjk/Nt6MT9aYow==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/http-errors": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", + "license": "MIT", + "dependencies": { + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" + }, + "engines": { + "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } + }, + "node_modules/iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "license": "MIT", + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" + }, + "node_modules/ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==", + "license": "MIT", + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/json-schema-to-ts": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/json-schema-to-ts/-/json-schema-to-ts-3.1.1.tgz", + "integrity": "sha512-+DWg8jCJG2TEnpy7kOm/7/AxaYoaRbjVB4LFZLySZlWn8exGs3A4OLJR966cVvU26N7X9TWxl+Jsw7dzAqKT6g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.18.3", + "ts-algebra": "^2.0.0" + }, + "engines": { + "node": ">=16" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/merge-descriptors": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.3.tgz", + "integrity": "sha512-gaNvAS7TZ897/rVaZ0nMtAyxNyi/pdbjbAwUpFQpN70GqnVfOiXpeUUMKRBmzXaSQ8DdTX4/0ms62r2K+hE6mQ==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha512-iclAHeNqNm68zFtnZ0e+1L2yUIdvzNoauKU4WBA3VvH/vPFieF7qfRlwUZU+DA9P9bPXIS90ulxoUoCH23sV2w==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "license": "MIT", + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", + "license": "MIT" + }, + "node_modules/negotiator": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/on-finished": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", + "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", + "license": "MIT", + "dependencies": { + "ee-first": "1.1.1" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/path-to-regexp": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.13.tgz", + "integrity": "sha512-A/AGNMFN3c8bOlvV9RreMdrv7jsmF9XIfDeCd87+I8RNg6s78BhJxMu69NEMHBSJFxKidViTEdruRwEk/WIKqA==", + "license": "MIT" + }, + "node_modules/proxy-addr": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz", + "integrity": "sha512-llQsMLSUDUPT44jdrU/O37qlnifitDP+ZwrmmZcoSKyLKvtZxpyV0n2/bD/N4tBAAZ/gJEdZU7KMraoK1+XYAg==", + "license": "MIT", + "dependencies": { + "forwarded": "0.2.0", + "ipaddr.js": "1.9.1" + }, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/qs": { + "version": "6.15.3", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.3.tgz", + "integrity": "sha512-O9gl3zCl5h5blw1KGUzQKhA5oUXSl8rwUIM5o0S3nCXMliSvy5Dzx7/DJcI+SwgICv+IneSZwhBh1oSyEHA71A==", + "license": "BSD-3-Clause", + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==", + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/raw-body": { + "version": "2.5.3", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.5.3.tgz", + "integrity": "sha512-s4VSOf6yN0rvbRZGxs8Om5CWj6seneMwK3oDb4lWDH0UPhWcxwOWw5+qk24bxq87szX1ydrwylIOp2uG1ojUpA==", + "license": "MIT", + "dependencies": { + "bytes": "~3.1.2", + "http-errors": "~2.0.1", + "iconv-lite": "~0.4.24", + "unpipe": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "license": "MIT" + }, + "node_modules/send": { + "version": "0.19.2", + "resolved": "https://registry.npmjs.org/send/-/send-0.19.2.tgz", + "integrity": "sha512-VMbMxbDeehAxpOtWJXlcUS5E8iXh6QmN+BkRX1GARS3wRaXEEgzCcB10gTQazO42tpNIya8xIyNx8fll1OFPrg==", + "license": "MIT", + "dependencies": { + "debug": "2.6.9", + "depd": "2.0.0", + "destroy": "1.2.0", + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "~0.5.2", + "http-errors": "~2.0.1", + "mime": "1.6.0", + "ms": "2.1.3", + "on-finished": "~2.4.1", + "range-parser": "~1.2.1", + "statuses": "~2.0.2" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/send/node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" + }, + "node_modules/serve-static": { + "version": "1.16.3", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.16.3.tgz", + "integrity": "sha512-x0RTqQel6g5SY7Lg6ZreMmsOzncHFU7nhnRWkKgWuMTu5NN0DR5oruckMqRvacAN9d5w6ARnRBXl9xhDCgfMeA==", + "license": "MIT", + "dependencies": { + "encodeurl": "~2.0.0", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "~0.19.1" + }, + "engines": { + "node": ">= 0.8.0" + } + }, + "node_modules/setprototypeof": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.2.0.tgz", + "integrity": "sha512-E5LDX7Wrp85Kil5bhZv46j8jOeboKq5JMmYM3gVGdGH8xFpPWXUMsNrlODCrkoxMEeNi/XZIwuRvY4XNwYMJpw==", + "license": "ISC" + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/statuses": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/toidentifier": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", + "integrity": "sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==", + "license": "MIT", + "engines": { + "node": ">=0.6" + } + }, + "node_modules/ts-algebra": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ts-algebra/-/ts-algebra-2.0.0.tgz", + "integrity": "sha512-FPAhNPFMrkwz76P7cdjdmiShwMynZYN6SgOujD1urY4oNm80Ou9oMdmbR45LotcKOXoy7wSmHkRFE6Mxbrhefw==", + "license": "MIT" + }, + "node_modules/type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "license": "MIT", + "dependencies": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==", + "license": "MIT", + "engines": { + "node": ">= 0.4.0" + } + }, + "node_modules/vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha512-BNGbWLfd0eUPabhkXUVm0j8uuvREyTh5ovRa/dyow/BqAbZJyC+5fU+IzQOzmAKzYqYRAISoRhdQr3eIZ/PXqg==", + "license": "MIT", + "engines": { + "node": ">= 0.8" + } + } + } +} diff --git a/app/package.json b/app/package.json new file mode 100644 index 0000000..4cb4cfa --- /dev/null +++ b/app/package.json @@ -0,0 +1,15 @@ +{ + "name": "redator-sd", + "version": "0.1.0", + "description": "Redator de Solicitações de Demanda (SD) — RMDS / SES-MG × Isis Saúde", + "type": "module", + "private": true, + "scripts": { + "start": "node src/server.js" + }, + "dependencies": { + "@anthropic-ai/sdk": "^0.74.0", + "dotenv": "^16.4.5", + "express": "^4.21.0" + } +} diff --git a/app/public/app.js b/app/public/app.js new file mode 100644 index 0000000..bd60af9 --- /dev/null +++ b/app/public/app.js @@ -0,0 +1,980 @@ +"use strict"; + +const $ = (id) => document.getElementById(id); +const esc = (s) => + String(s ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c])); +const fmtD = (iso) => { + const m = String(iso || "").match(/^(\d{4})-(\d{2})-(\d{2})/); + return m ? `${m[3]}/${m[2]}/${m[1]}` : "—"; +}; +const dayNum = (iso) => { + const m = String(iso || "").match(/^(\d{4})-(\d{2})-(\d{2})/); + return m ? Date.UTC(+m[1], +m[2] - 1, +m[3]) / 86400000 : NaN; +}; + +// Preços do claude-opus-5 (USD por 1M tokens) para o custo estimado no rodapé. +const PRECO = { input: 5, output: 25, cache_read: 0.5, cache_write: 6.25 }; + +let statusApp = null; +let sdAtual = null; // { id, numero, status, chat, rascunho, validacao, bloqueios, uso } +let ocupado = false; +let modoExterno = false; // tela "Validar JSON externo" +let externo = null; // { sd, exportado, validacao, bloqueios } +let ultimosBloqueios = []; + +// ---------- Markdown mínimo (negrito, código, títulos, listas, OPCAO:) ---------- +function renderMd(texto) { + const linhas = String(texto || "").split("\n"); + const out = []; + let lista = null; // "ul" | "ol" + const fechaLista = () => { if (lista) { out.push(``); lista = null; } }; + const inline = (s) => + esc(s) + .replace(/\*\*([^*]+)\*\*/g, "$1") + .replace(/`([^`]+)`/g, "$1"); + for (const raw of linhas) { + const l = raw.trimEnd(); + if (/^OPCAO:/i.test(l.trim())) continue; // vira botão, não texto + const h = l.match(/^(#{1,4})\s+(.*)/); + const ul = l.match(/^\s*[-•]\s+(.*)/); + const ol = l.match(/^\s*\d+[.)]\s+(.*)/); + if (h) { fechaLista(); out.push(`

${inline(h[2])}

`); } + else if (ul) { if (lista !== "ul") { fechaLista(); out.push("
    "); lista = "ul"; } out.push(`
  • ${inline(ul[1])}
  • `); } + else if (ol) { if (lista !== "ol") { fechaLista(); out.push("
      "); lista = "ol"; } out.push(`
    1. ${inline(ol[1])}
    2. `); } + else if (l.trim() === "") { fechaLista(); } + else { fechaLista(); out.push(`

      ${inline(l)}

      `); } + } + fechaLista(); + return out.join(""); +} + +function extrairOpcoes(texto) { + return String(texto || "") + .split("\n") + .map((l) => l.trim()) + .filter((l) => /^OPCAO:/i.test(l)) + .map((l) => l.replace(/^OPCAO:\s*/i, "").trim()) + .filter(Boolean); +} + +// ---------- Tema (automático → claro → escuro; escolha guardada no navegador) ---------- +const TEMAS = ["auto", "claro", "escuro"]; +const TEMA_ICONE = { auto: "🌗", claro: "☀️", escuro: "🌙" }; +const TEMA_ROTULO = { auto: "automático (segue o sistema)", claro: "claro", escuro: "escuro" }; +let temaAtual = localStorage.getItem("sd_tema"); +if (!TEMAS.includes(temaAtual)) temaAtual = "auto"; + +function aplicarTema(t) { + const raiz = document.documentElement; + if (t === "claro") raiz.dataset.theme = "light"; + else if (t === "escuro") raiz.dataset.theme = "dark"; + else delete raiz.dataset.theme; // automático: quem manda é o sistema + $("btnTema").textContent = TEMA_ICONE[t]; + $("btnTema").title = `Tema: ${TEMA_ROTULO[t]} — clique para alternar`; +} +aplicarTema(temaAtual); + +$("btnTema").addEventListener("click", () => { + temaAtual = TEMAS[(TEMAS.indexOf(temaAtual) + 1) % TEMAS.length]; + localStorage.setItem("sd_tema", temaAtual); + aplicarTema(temaAtual); +}); + +// ---------- Autenticação + navegação ---------- +window.addEventListener("hashchange", rotear); +window.addEventListener("DOMContentLoaded", iniciar); + +async function iniciar() { + let sess = null; + try { sess = await (await fetch("/api/sessao")).json(); } catch {} + if (!sess) { $("statusChip").textContent = "servidor indisponível"; return; } + $("btnSair").hidden = !sess.precisa_login; + if (sess.precisa_login && !sess.logado) { mostrarLogin(); return; } + await entrar(); +} + +async function entrar() { + esconderTodas(); + await carregarStatus(); + rotear(); +} + +function esconderTodas() { + for (const id of ["telaLogin", "telaHome", "telaConversa", "telaValidar", "telaBase"]) $(id).hidden = true; +} + +function mostrarLogin() { + esconderTodas(); + $("telaLogin").hidden = false; + $("loginErro").hidden = true; + $("loginUsuario").focus(); +} + +$("formLogin").addEventListener("submit", async (ev) => { + ev.preventDefault(); + $("btnLogin").disabled = true; + const r = await fetch("/api/login", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ usuario: $("loginUsuario").value, senha: $("loginSenha").value }), + }).catch(() => null); + $("btnLogin").disabled = false; + if (r?.ok) { $("loginSenha").value = ""; await entrar(); } + else { + $("loginErro").textContent = "Usuário ou senha inválidos."; + $("loginErro").hidden = false; + } +}); + +$("btnSair").addEventListener("click", async () => { + await fetch("/api/logout", { method: "POST" }).catch(() => {}); + location.hash = "#/"; + mostrarLogin(); +}); + +function exigirLogin(resp) { + // Sessão expirada/servidor reiniciado: volta para a tela de login. + if (resp && resp.status === 401) { mostrarLogin(); return true; } + return false; +} + +async function carregarStatus() { + try { + const r = await fetch("/api/status"); + if (exigirLogin(r)) return; + statusApp = await r.json(); + $("statusChip").textContent = statusApp.configurado + ? `${statusApp.provider} · ${statusApp.model} · próxima: SD-${statusApp.proximo_numero}` + : "sem chave de API"; + $("avisoConfig").hidden = statusApp.configurado; + popularSeletoresModelo(); + } catch { + $("statusChip").textContent = "servidor indisponível"; + } +} + +// Seletores de modelo (Nova SD e Base de Conhecimento) — lista do .env (LLM_MODEL + LLM_MODELOS). +function popularSeletoresModelo() { + const modelos = statusApp?.modelos || []; + for (const id of ["selModelo", "selModeloBase"]) { + const sel = $(id); + const anterior = sel.value; + sel.innerHTML = modelos + .map((m) => ``) + .join(""); + // preserva a escolha do usuário entre recargas de status; senão, usa o padrão + sel.value = modelos.includes(anterior) ? anterior : statusApp.model; + } +} + +function moverPainel(slotId) { + const painel = $("painel"); + const slot = $(slotId); + if (painel.parentElement !== slot) slot.appendChild(painel); +} + +function rotear() { + if ($("telaLogin") && !$("telaLogin").hidden) return; // aguardando login + const m = location.hash.match(/^#\/sd\/(.+)$/); + if (location.hash === "#/validar") mostrarValidar(); + else if (location.hash === "#/base") mostrarBase(); + else if (m) abrirConversa(m[1]); + else mostrarHome(); +} + +// ---------- Home ---------- +async function mostrarHome() { + esconderTodas(); + $("telaHome").hidden = false; + sdAtual = null; + modoExterno = false; + const rList = await fetch("/api/sds"); + if (exigirLogin(rList)) return; + const lista = await rList.json(); + $("listaSds").innerHTML = lista.length + ? lista + .sort((a, b) => (a.criadaEm < b.criadaEm ? 1 : -1)) + .map( + (s) => `
      + SD-${s.numero} + ${esc(s.titulo)} + ${s.status} + ` + ) + .join("") + : '

      Nenhuma SD ainda — cole a primeira demanda acima.

      '; + $("listaSds").querySelectorAll(".btn-excluir").forEach((b) => + b.addEventListener("click", async (ev) => { + ev.preventDefault(); // o botão vive dentro do link da SD + ev.stopPropagation(); + const { id, status, titulo } = ev.currentTarget.dataset; + const aviso = status === "enviada" + ? `Excluir a conversa de "${titulo}" da lista?\n\nEla já foi ENVIADA: o registro na base de conhecimento (saldos e precedentes) NÃO será apagado — só a conversa aqui do app.` + : `Excluir o rascunho "${titulo}"?\n\nA conversa e o rascunho serão apagados de vez.`; + if (!confirm(aviso)) return; + const r = await fetch(`/api/sds/${id}`, { method: "DELETE" }); + if (exigirLogin(r)) return; + if (!r.ok) { alert("Não foi possível excluir."); return; } + await carregarStatus(); // numeração provisória pode mudar + await mostrarHome(); + }) + ); + if (statusApp?.saldos) { + $("saldosBox").innerHTML = Object.entries(statusApp.saldos) + .map( + ([item, v]) => `
      +
      ${esc(item)}
      +
      ${v.saldo.toLocaleString("pt-BR")} UST
      +
      de ${v.pool_total.toLocaleString("pt-BR")} · consumido ${v.consumido.toLocaleString("pt-BR")}
      +
      ` + ) + .join(""); + } +} + +$("btnIniciar").addEventListener("click", async () => { + const demanda = $("demandaInput").value.trim(); + if (!demanda || ocupado) return; + $("btnIniciar").disabled = true; + // Prepara a tela de conversa e transmite o primeiro turno + esconderTodas(); + $("telaConversa").hidden = false; + modoExterno = false; + moverPainel("slotPainelConversa"); + $("mensagens").innerHTML = ""; + $("opcoes").innerHTML = ""; + resetPainel(); + addMsgUser(demanda); + const resp = await fetch("/api/sds", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ demanda, modelo: $("selModelo").value }), + }); + if (exigirLogin(resp)) return; + const sdId = resp.headers.get("X-Sd-Id"); + history.replaceState(null, "", `#/sd/${sdId}`); + sdAtual = { id: sdId, chat: [], rascunho: null }; + $("demandaInput").value = ""; + $("btnIniciar").disabled = false; + await consumirStream(resp); + await recarregarSd(); // pega numero/status/uso consolidados +}); + +// ---------- Conversa ---------- +async function abrirConversa(id) { + if (sdAtual?.id === id && ocupado) return; // já está na tela, streaming em curso + esconderTodas(); + $("telaConversa").hidden = false; + modoExterno = false; + moverPainel("slotPainelConversa"); + sdAtual = { id }; + await recarregarSd(true); +} + +async function recarregarSd(renderChat = false) { + const r = await fetch(`/api/sds/${sdAtual.id}`); + if (exigirLogin(r)) return; + if (!r.ok) { location.hash = "#/"; return; } + const d = await r.json(); + sdAtual = d; + if (renderChat) { + $("mensagens").innerHTML = ""; + $("opcoes").innerHTML = ""; + for (const m of d.chat || []) { + if (m.role === "user") addMsgUser(m.texto); + else addMsgAssistant(m.texto, true); + } + const ultimo = (d.chat || []).filter((m) => m.role === "assistant").pop(); + if (ultimo) renderOpcoes(extrairOpcoes(ultimo.texto)); + } + renderPainel(d.rascunho, d.validacao, d.bloqueios); + renderUso(d.uso); + const soLeitura = d.status === "enviada"; + $("msgInput").disabled = soLeitura; + $("btnEnviarMsg").disabled = soLeitura; + if (soLeitura) $("msgInput").placeholder = "SD enviada — somente leitura."; + rolarChat(); +} + +function addMsgUser(texto) { + const div = document.createElement("div"); + div.className = "msg user"; + div.textContent = texto; + $("mensagens").appendChild(div); + rolarChat(); +} + +function addMsgAssistant(texto, pronto = false) { + const div = document.createElement("div"); + div.className = "msg assistant"; + div.innerHTML = pronto ? renderMd(texto) : ''; + $("mensagens").appendChild(div); + rolarChat(); + return div; +} + +function renderOpcoes(opcoes) { + $("opcoes").innerHTML = ""; + for (const o of opcoes) { + const b = document.createElement("button"); + b.type = "button"; + b.textContent = o; + b.addEventListener("click", () => enviarMensagem(o)); + $("opcoes").appendChild(b); + } +} + +function rolarChat() { + const el = $("mensagens"); + el.scrollTop = el.scrollHeight; +} + +$("formMsg").addEventListener("submit", (ev) => { + ev.preventDefault(); + const t = $("msgInput").value.trim(); + if (t) enviarMensagem(t); +}); +$("msgInput").addEventListener("keydown", (ev) => { + if (ev.key === "Enter" && !ev.shiftKey) { + ev.preventDefault(); + $("formMsg").requestSubmit(); + } +}); + +async function enviarMensagem(texto) { + if (ocupado || !sdAtual?.id) return; + $("msgInput").value = ""; + $("opcoes").innerHTML = ""; + addMsgUser(texto); + const resp = await fetch(`/api/sds/${sdAtual.id}/mensagem`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ texto }), + }); + if (exigirLogin(resp)) return; + if (!resp.ok && !resp.body) { + addMsgAssistant("⚠ Erro ao enviar a mensagem.", true); + return; + } + await consumirStream(resp); +} + +async function consumirStream(resp) { + ocupado = true; + $("btnEnviarMsg").disabled = true; + const bolha = addMsgAssistant(""); + let texto = ""; + // "pensando…" cobre o silêncio do raciocínio interno do modelo: antes da + // primeira palavra e entre as chamadas de ferramenta (validação do rascunho). + let pensando = true; + const pinta = () => { + bolha.innerHTML = + renderMd(texto) + + (pensando ? 'pensando…' : ''); + rolarChat(); + }; + pinta(); + try { + const reader = resp.body.getReader(); + const dec = new TextDecoder(); + let buf = ""; + while (true) { + const { done, value } = await reader.read(); + if (done) break; + buf += dec.decode(value, { stream: true }); + let nl; + while ((nl = buf.indexOf("\n")) >= 0) { + const linha = buf.slice(0, nl).trim(); + buf = buf.slice(nl + 1); + if (!linha) continue; + let ev; + try { ev = JSON.parse(linha); } catch { continue; } + if (ev.t === "delta") { + texto += ev.text; + pensando = false; + pinta(); + } else if (ev.t === "rascunho") { + renderPainel(ev.sd, ev.validacao, ev.bloqueios); + pensando = true; // o modelo volta a raciocinar sobre o resultado da validação + pinta(); + } else if (ev.t === "uso") { + renderUso(ev.uso); + } else if (ev.t === "erro") { + texto += `\n\n⚠ ${ev.msg}`; + pensando = false; + pinta(); + } + } + } + } catch (e) { + texto += "\n\n⚠ Conexão interrompida — recarregue a página para retomar."; + } + bolha.innerHTML = renderMd(texto); + renderOpcoes(extrairOpcoes(texto)); + ocupado = false; + $("btnEnviarMsg").disabled = false; + rolarChat(); +} + +// ---------- Painel do documento ---------- +function resetPainel() { + $("painelVazio").hidden = false; + $("painelConteudo").hidden = true; +} + +function renderPainel(sd, validacao, bloqueios) { + if (!sd) { resetPainel(); return; } + ultimosBloqueios = bloqueios || []; + if (!modoExterno && sdAtual) { sdAtual.rascunho = sd; } + $("painelVazio").hidden = true; + $("painelConteudo").hidden = false; + const E = sd.entregaveis || []; + const cab = sd.cabecalho || {}; + const R = validacao?.resultados || {}; + const totais = validacao?.totais || {}; + + // Resumo + $("pResumo").innerHTML = ` +
      ${esc(cab.titulo || cab.projeto || "SD em elaboração")}
      +
      +
      ${esc(cab.numero_sd || (sdAtual?.numero ? "SD-" + sdAtual.numero : "—"))}
      +
      Total
      ${validacao?.soma_ust ?? "—"} UST
      +
      Prazo
      ${esc(cab.prazo_execucao_semanas ?? "—")} sem.
      +
      Itens
      ${(cab.itens_tr || []).join(" + ") || "—"}
      +
      ✓/!/✗/○
      ${totais.ok ?? 0}/${totais.alerta ?? 0}/${totais.falha ?? 0}/${totais.pendente ?? 0}
      +
      `; + + // Quadro + let linhas = E.map((e) => { + const par = (e.paralelo_com || []).length ? " ∥" : ""; + return `${e.n ?? ""}${esc(e.nome)}${par}${esc(e.tipo || "")} + ${esc(e.item_tr || "")}${e.semanas ?? ""}${e.ust ?? ""} + ${fmtD(e.periodo?.inicio)}–${fmtD(e.periodo?.fim)}`; + }).join(""); + const porItem = validacao?.soma_por_item || {}; + if (Object.keys(porItem).length > 1) { + linhas += Object.keys(porItem).sort() + .map((i) => `Subtotal ${esc(i)}${porItem[i]}`) + .join(""); + } + linhas += `TOTAL${validacao?.soma_ust ?? ""}`; + $("pQuadro").innerHTML = ` + ${linhas}
      #EntregávelTipoItemSemUSTPeríodo
      `; + + // Artefatos (trava de integridade) + $("pArtefatos").innerHTML = E.map((e, ei) => { + const arts = (e.artefatos || []).map((a, ai) => { + const bad = a.data && e.periodo?.fim && dayNum(a.data) > dayNum(e.periodo.fim); + return `
      + +
      + + + ${bad ? `
      data posterior ao fim do entregável (${fmtD(e.periodo.fim)})
      ` + : a.confirmado_pelo_usuario && !a.data ? `
      confirmado sem data
      ` : ""} +
      `; + }).join(""); + return arts ? `

      E${e.n} — ${esc(e.nome)}

      ${arts}
      ` : ""; + }).join("") || '

      Ainda sem artefatos registrados.

      '; + + $("pArtefatos").querySelectorAll("input[type=checkbox]").forEach((cb) => + cb.addEventListener("change", (ev) => + atualizarArtefato(+ev.target.dataset.e, +ev.target.dataset.a, { confirmado: ev.target.checked }) + ) + ); + $("pArtefatos").querySelectorAll("input[type=date]").forEach((di) => + di.addEventListener("change", (ev) => + atualizarArtefato(+ev.target.dataset.e, +ev.target.dataset.a, { data: ev.target.value || "" }) + ) + ); + + // Checklist + const ICONS = { ok: "✓", alerta: "!", falha: "✗", pendente: "○" }; + const checklist = statusApp?.checklist || []; + $("pChecklist").innerHTML = checklist.map((c) => { + const r = R[c.id] || { status: "pendente", obs: "" }; + return `
      + ${ICONS[r.status]} + ${c.id} + ${esc(c.regra)}${r.obs ? `${esc(r.obs)}` : ""}
      `; + }).join(""); + + // Pendências + const pend = [...(sd.pendencias || [])]; + (bloqueios || []).forEach((b) => pend.push("Bloqueante: " + b)); + $("pPendencias").innerHTML = pend.length + ? pend.map((p) => `
    3. ${esc(p)}
    4. `).join("") + : "
    5. Nenhuma pendência registrada.
    6. "; + + // Fechamento + const bloqueado = (bloqueios || []).length > 0; + const lock = $("pBloqueios"); + lock.className = "lock-msg " + (bloqueado ? "locked" : "open"); + lock.textContent = bloqueado + ? "Exportação bloqueada — " + bloqueios.join("; ") + "." + : "SD liberada: artefatos confirmados e nenhuma falha de código."; + $("btnBaixar").disabled = bloqueado; + $("btnEnviarSd").hidden = modoExterno; + $("btnEnviarSd").disabled = bloqueado || sdAtual?.status === "enviada"; +} + +async function atualizarArtefato(e, a, corpo) { + if (modoExterno) { + const art = externo?.sd?.entregaveis?.[e]?.artefatos?.[a]; + if (!art) return; + if (typeof corpo.confirmado === "boolean") art.confirmado_pelo_usuario = corpo.confirmado; + if (corpo.data !== undefined) { + if (corpo.data) art.data = corpo.data; + else delete art.data; + } + await validarExterno(false); + return; + } + const r = await fetch(`/api/sds/${sdAtual.id}/artefato`, { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ e, a, ...corpo }), + }); + if (exigirLogin(r)) return; + if (r.ok) { + const d = await r.json(); + renderPainel(d.sd, d.validacao, d.bloqueios); + } +} + +$("btnBaixar").addEventListener("click", () => { + if (modoExterno) { + if (externo?.exportado) baixarJsonLocal(externo.exportado); + return; + } + window.location.href = `/api/sds/${sdAtual.id}/export`; +}); + +function baixarJsonLocal(obj) { + const nome = (obj.cabecalho?.numero_sd || "sd").toLowerCase().replace(/[^a-z0-9-]+/g, "-") + ".json"; + const blob = new Blob([JSON.stringify(obj, null, 2)], { type: "application/json" }); + const a = document.createElement("a"); + a.href = URL.createObjectURL(blob); + a.download = nome; + a.click(); + URL.revokeObjectURL(a.href); +} + +$("btnEnviarSd").addEventListener("click", async () => { + if (!confirm("Marcar como enviada? O número definitivo será atribuído e a SD entra na base de conhecimento.")) return; + const r = await fetch(`/api/sds/${sdAtual.id}/enviar`, { method: "POST" }); + const d = await r.json(); + if (r.ok) { + alert(`SD registrada na base como ${d.registro_na_base.id}.`); + await carregarStatus(); + await recarregarSd(); + } else { + alert(d.erro + (d.bloqueios ? "\n- " + d.bloqueios.join("\n- ") : "")); + } +}); + +// ---------- Documento formatado + impressão/PDF ---------- +const CSS_IMPRESSAO = ` + body{margin:0; padding:32px 40px; background:#fff; color:#000; + font-family:Georgia,"Times New Roman",serif; font-size:12.5pt; line-height:1.6;} + h1{font-size:16pt; margin:0 0 2px; line-height:1.3;} + .doc-sub{font-family:"Segoe UI",Arial,sans-serif; font-size:8.5pt; color:#555; + text-transform:uppercase; letter-spacing:.08em; margin-bottom:18px;} + .marca-rascunho{font-family:"Segoe UI",Arial,sans-serif; font-size:9pt; color:#B3362B; + border:1px solid #B3362B; display:inline-block; padding:2px 10px; margin-bottom:14px;} + h2{font-size:12.5pt; margin:22px 0 8px; border-bottom:2px solid #0B6B70; padding-bottom:3px;} + h3{font-size:11pt; margin:16px 0 5px;} + p{margin:0 0 10px;} + table{border-collapse:collapse; width:100%; font-size:9.5pt;} + th,td{border:1px solid #999; padding:4px 7px; text-align:left; vertical-align:top;} + th{font-family:"Segoe UI",Arial,sans-serif; font-size:8pt; text-transform:uppercase; + letter-spacing:.04em; background:#E1EFEF;} + tr.subtotal td{font-weight:700; background:#E1EFEF;} + tr.total td{font-weight:700; border-top:2px solid #0B6B70;} + td.num,th.num{text-align:right;} + .nota{font-size:10pt; font-style:italic; color:#444; margin-top:6px;} + ul,ol{margin:0 0 10px; padding-left:20px;} + li{margin-bottom:3px;} + .cab-table td:first-child{font-family:"Segoe UI",Arial,sans-serif; font-size:8.5pt; + text-transform:uppercase; letter-spacing:.04em; color:#555; width:170px;} + .art-status{font-family:"Segoe UI",Arial,sans-serif; font-size:8pt; border-radius:8px; + padding:1px 7px; margin-left:6px; white-space:nowrap; border:1px solid #999;} + .art-status.sim{background:#E3F2E9; color:#1E7B47;} + .art-status.nao{background:#F8EFDB; color:#A8730B;} + .enq-desc{font-size:10pt; color:#444;} + h2,h3{page-break-after:avoid;} tr{page-break-inside:avoid;} + @page{margin:18mm 15mm;}`; + +function minArtCli(ust) { + const faixas = statusApp?.regras?.artefatos_minimos || []; + const f = faixas.find((b) => b.ust_max === null || ust <= b.ust_max); + return f ? f.artefatos : 1; +} + +function documentoHtml(sdX, rascunho) { + const cab = sdX.cabecalho || {}; + const E = sdX.entregaveis || []; + const infoItem = (i) => statusApp?.regras?.itens?.[i] || {}; + const soma = E.reduce((a, e) => a + (e.ust || 0), 0); + const porItem = {}; + E.forEach((e) => (porItem[e.item_tr] = (porItem[e.item_tr] || 0) + (e.ust || 0))); + const multi = Object.keys(porItem).length > 1; + + let quadro = E.map((e) => + `${e.n ?? ""}${esc(e.nome)}${esc(e.tipo || "")} + ${esc(e.item_tr || "")}${e.semanas ?? ""} + ${e.timebox_h ?? ""}h${e.ust ?? ""} + ${fmtD(e.periodo?.inicio)} – ${fmtD(e.periodo?.fim)}` + ).join(""); + if (multi) + quadro += Object.keys(porItem).sort() + .map((i) => `Subtotal ${esc(i)}${porItem[i]}`) + .join(""); + quadro += `TOTAL${soma}`; + + const notas = E.filter((e) => (e.nota_paralelismo || "").trim()) + .map((e) => `

      Nota de paralelismo (E${e.n}): ${esc(e.nota_paralelismo)}

      `) + .join(""); + + const det = E.map((e) => + `

      E${e.n} · ${esc(e.tipo || "")} — ${esc(e.nome)} (${e.ust ?? "?"} UST)

      +

      Resumo: ${esc(e.resumo || "")}

      +

      Atividades:

      +
        ${(e.atividades || []).map((a) => `
      1. ${esc(a)}
      2. `).join("")}
      +

      Artefatos (mínimo ${minArtCli(e.ust || 0)} pela faixa de UST):

      +
        ${(e.artefatos || []).map((a) => + `
      • ${esc(a.nome)}${a.data ? ` — ${fmtD(a.data)}` : ""} + ${a.confirmado_pelo_usuario ? "confirmado" : "a confirmar"}
      • ` + ).join("")}
      +

      Critérios de aceite:

      +
        ${(e.criterios_aceite || []).map((c) => `
      • ${esc(c)}
      • `).join("")}
      ` + ).join(""); + + const enq = (sdX.enquadramento_tr || []).map((q) => { + const inf = infoItem(q.item_tr); + return `

      Item ${inf.numero ?? "?"} — “${esc(q.titulo_literal)}” (CATMAS ${esc(inf.catmas || "—")}) — entregáveis ${(q.entregaveis_n || []).join(", ")}

      +

      ${esc(q.descricao_item || "")}

      +
        ${(q.aderencia || []).map((b) => `
      • ${esc(b)}
      • `).join("")}
      `; + }).join(""); + + const titulo = esc(cab.numero_sd || "SD") + " — " + esc(cab.projeto || "Solicitação de Demanda"); + return `${titulo}` + + `` + + `

      ${esc(cab.titulo || cab.projeto || "Solicitação de Demanda")}

      ` + + `
      Solicitação de Demanda · ${esc(cab.numero_sd || "SD-__")} · ${esc(cab.versao || "V1")} · RMDS / SES-MG
      ` + + (rascunho ? `
      RASCUNHO — há pendências bloqueantes abertas
      ` : "") + + ` + + + + + + + +
      Projeto${esc(cab.projeto || "")}
      Data de abertura${fmtD(cab.data_abertura)}
      Período de execução${fmtD(cab.periodo_execucao?.inicio)} a ${fmtD(cab.periodo_execucao?.fim)}
      Itens do TR${(cab.itens_tr || []).map(esc).join(" e ")}
      Total${soma} UST${cab.total_ust_extenso ? ` (${esc(cab.total_ust_extenso)})` : ""}
      Prazo de execução${esc(cab.prazo_execucao_semanas ?? "—")} semanas (calendário)
      Entregáveis${E.length}
      +

      1. Objetivo

      ${esc(sdX.objetivo || "")}

      +

      2. Contexto e escopo

      ${(sdX.contexto || []).map((p) => `

      ${esc(p)}

      `).join("")} +

      3. Quadro de entregáveis

      + + ${quadro}
      #EntregávelTipoItemSem.TimeboxUSTPeríodo
      ${notas} +

      4. Detalhamento por entregável

      ${det} +

      5. Enquadramento no TR

      ${enq}` + + `window.addEventListener("load",()=>setTimeout(()=>window.print(),350));`; +} + +function abrirDocumento(sdX, rascunho) { + const html = documentoHtml(sdX, rascunho); + try { + const w = window.open("", "_blank"); + if (w && w.document) { + w.document.open(); + w.document.write(html); + w.document.close(); + return; + } + } catch { /* pop-up bloqueado */ } + const nome = (sdX.cabecalho?.numero_sd || "sd").toLowerCase().replace(/[^a-z0-9-]+/g, "-") + "-impressao.html"; + const blob = new Blob([html], { type: "text/html" }); + const a = document.createElement("a"); + a.href = URL.createObjectURL(blob); + a.download = nome; + a.click(); + URL.revokeObjectURL(a.href); +} + +$("btnDocumento").addEventListener("click", async () => { + if (modoExterno) { + if (externo?.exportado) abrirDocumento(externo.exportado, (externo.bloqueios || []).length > 0); + return; + } + if (!sdAtual?.id) return; + const r = await fetch(`/api/sds/${sdAtual.id}/export?forcar=1`); + if (exigirLogin(r)) return; + if (!r.ok) { alert("Não foi possível gerar o documento."); return; } + abrirDocumento(await r.json(), ultimosBloqueios.length > 0); +}); + +// ---------- Validar JSON externo ---------- +function mostrarValidar() { + esconderTodas(); + $("telaValidar").hidden = false; + modoExterno = true; + sdAtual = null; + moverPainel("slotPainelValidar"); + if (externo?.exportado) renderPainel(externo.exportado, externo.validacao, externo.bloqueios); + else resetPainel(); +} + +async function validarExterno(lerTextarea = true) { + const erroBox = $("validarErro"); + erroBox.hidden = true; + let sdIn = externo?.sd; + if (lerTextarea) { + try { + sdIn = JSON.parse($("jsonExterno").value); + } catch (e) { + erroBox.textContent = "JSON inválido: " + e.message; + erroBox.hidden = false; + return; + } + } + const r = await fetch("/api/validar", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ sd: sdIn }), + }); + if (exigirLogin(r)) return; + const d = await r.json(); + if (!r.ok) { + erroBox.textContent = d.erro || "Erro ao validar."; + erroBox.hidden = false; + return; + } + externo = { sd: sdIn, exportado: d.exportado, validacao: d.validacao, bloqueios: d.bloqueios }; + renderPainel(d.exportado, d.validacao, d.bloqueios); +} + +$("btnValidarJson").addEventListener("click", () => validarExterno(true)); + +$("btnImportarJson").addEventListener("click", () => $("arquivoJson").click()); +$("arquivoJson").addEventListener("change", async (ev) => { + const arquivo = ev.target.files?.[0]; + ev.target.value = ""; // permite reimportar o mesmo arquivo depois + if (!arquivo) return; + const erroBox = $("validarErro"); + if (arquivo.size > 2 * 1024 * 1024) { + erroBox.textContent = "Arquivo muito grande (máx. 2 MB)."; + erroBox.hidden = false; + return; + } + try { + const texto = await arquivo.text(); + JSON.parse(texto); // valida a sintaxe antes de preencher + $("jsonExterno").value = texto; + erroBox.hidden = true; + await validarExterno(true); // já valida a SD importada + } catch (e) { + erroBox.textContent = `O arquivo "${arquivo.name}" não contém JSON válido: ${e.message}`; + erroBox.hidden = false; + } +}); + +$("btnLimparJson").addEventListener("click", () => { + $("jsonExterno").value = ""; + externo = null; + $("validarErro").hidden = true; + resetPainel(); +}); + +$("btnVoltarValidar").addEventListener("click", () => { location.hash = "#/"; }); + +// ---------- Alimentar base de conhecimento (upload de SD real) ---------- + +function mostrarBase() { + esconderTodas(); + $("telaBase").hidden = false; + modoExterno = false; + sdAtual = null; +} + +$("btnVoltarBase").addEventListener("click", () => { location.hash = "#/"; }); +$("btnEscolherDoc").addEventListener("click", () => $("arquivoDoc").click()); + +function baseErro(msg) { + const box = $("baseErro"); + box.textContent = msg || ""; + box.hidden = !msg; +} + +$("arquivoDoc").addEventListener("change", async (ev) => { + const arquivo = ev.target.files?.[0]; + ev.target.value = ""; // permite reenviar o mesmo arquivo + if (!arquivo) return; + baseErro(""); + $("baseOk").hidden = true; + const ext = (arquivo.name.toLowerCase().match(/\.([a-z0-9]+)$/) || [])[1] || ""; + if (!["pdf", "md", "txt", "markdown"].includes(ext)) { + baseErro(`Formato .${ext || "?"} não suportado — envie .pdf, .md ou .txt (para .docx, exporte como PDF).`); + return; + } + if (arquivo.size > 15 * 1024 * 1024) { + baseErro("Arquivo muito grande (máx. 15 MB)."); + return; + } + $("btnEscolherDoc").disabled = true; + $("baseExtraindo").hidden = false; + $("baseRevisao").hidden = true; + try { + const base64 = await new Promise((resolve, reject) => { + const fr = new FileReader(); + fr.onload = () => resolve(String(fr.result).split(",")[1] || ""); + fr.onerror = () => reject(new Error("Falha ao ler o arquivo.")); + fr.readAsDataURL(arquivo); + }); + const r = await fetch("/api/base/extrair", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ nome: arquivo.name, base64, modelo: $("selModeloBase").value }), + }); + if (exigirLogin(r)) return; + const d = await r.json(); + if (!r.ok) { baseErro(d.erro || "Erro na extração."); return; } + renderRevisaoBase(d); + if (d.uso) { + const custo = ((d.uso.input_tokens || 0) * PRECO.input + (d.uso.output_tokens || 0) * PRECO.output) / 1e6; + $("baseUso").textContent = + `extração: ${(d.uso.input_tokens || 0).toLocaleString("pt-BR")} tokens lidos · ` + + `${(d.uso.output_tokens || 0).toLocaleString("pt-BR")} gerados · custo estimado US$ ${custo.toFixed(3)} ` + + `(estimativa válida para o modelo Opus)`; + } + } catch (e) { + baseErro("Erro na extração: " + e.message); + } finally { + $("btnEscolherDoc").disabled = false; + $("baseExtraindo").hidden = true; + } +}); + +function renderRevisaoBase(d) { + $("baseRevisao").hidden = false; + $("baseSdJson").value = JSON.stringify(d.sd || {}, null, 2); + renderConferenciaBase(d.conferencia || []); + + const obs = d.observacoes || []; + $("baseObsBloco").hidden = !obs.length; + $("baseObs").innerHTML = obs.map((o) => `
    7. ${esc(o)}
    8. `).join(""); + + $("baseCaps").innerHTML = (d.capacidades || []).length + ? (d.capacidades || []).map((c, i) => ` +
      + +
      + + +
      + + +
      +
      +
      `).join("") + : '

      Nenhuma capacidade reutilizável identificada no documento.

      '; + $("baseCaps").querySelectorAll(".cap-card input[type=checkbox]").forEach((cb) => + cb.addEventListener("change", (ev) => + ev.target.closest(".cap-card").classList.toggle("excluida", !ev.target.checked) + ) + ); + $("baseRevisao").scrollIntoView({ behavior: "smooth" }); +} + +function renderConferenciaBase(lista) { + const temErro = lista.some((c) => c.nivel === "erro"); + $("baseConferencia").innerHTML = lista.length + ? lista.map((c) => `
      + ${c.nivel === "erro" ? "✗" : "!"} + ${c.nivel}${esc(c.msg)}
      `).join("") + : '
      okNenhuma inconsistência encontrada.
      '; + $("btnGravarBase").disabled = temErro; + return temErro; +} + +function lerSdRevisada() { + try { + return JSON.parse($("baseSdJson").value); + } catch (e) { + baseErro("JSON do registro inválido: " + e.message); + return null; + } +} + +function lerCapsRevisadas() { + return [...$("baseCaps").querySelectorAll(".cap-card")] + .filter((card) => card.querySelector("input[type=checkbox]").checked) + .map((card) => ({ + titulo: card.querySelector(".cap-titulo").value.trim(), + descricao: card.querySelector(".cap-desc").value.trim(), + item_tr: card.querySelector(".cap-item").value.trim(), + tags: card.querySelector(".cap-tags").value.split(",").map((t) => t.trim()).filter(Boolean), + })) + .filter((c) => c.titulo); +} + +$("btnReconferir").addEventListener("click", async () => { + baseErro(""); + const sd = lerSdRevisada(); + if (!sd) return; + const r = await fetch("/api/base/conferir", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ sd }), + }); + if (exigirLogin(r)) return; + const d = await r.json(); + renderConferenciaBase(d.conferencia || []); +}); + +$("btnGravarBase").addEventListener("click", async () => { + baseErro(""); + const sd = lerSdRevisada(); + if (!sd) return; + const caps = lerCapsRevisadas(); + if (!confirm(`Gravar ${sd.id || "a SD"} na base de conhecimento` + + (caps.length ? ` com ${caps.length} capacidade(s)` : "") + + "? Saldos de pool e precedentes passam a valer nas próximas entrevistas.")) return; + $("btnGravarBase").disabled = true; + try { + const r = await fetch("/api/base/gravar", { + method: "POST", + headers: { "Content-Type": "application/json" }, + body: JSON.stringify({ sd, capacidades: caps }), + }); + if (exigirLogin(r)) return; + const d = await r.json(); + if (!r.ok) { + if (d.conferencia) renderConferenciaBase(d.conferencia); + baseErro(d.erro || "Erro ao gravar."); + return; + } + const okBox = $("baseOk"); + okBox.textContent = `✓ ${d.sd.id} gravada na base` + + (d.capacidades.length ? ` com ${d.capacidades.map((c) => c.id).join(", ")}` : "") + + ". Vale a partir da próxima conversa."; + okBox.hidden = false; + await carregarStatus(); // atualiza próxima numeração e saldos no topo + } finally { + $("btnGravarBase").disabled = false; + } +}); + +function renderUso(uso) { + if (!uso) return; + const custo = + (uso.input_tokens * PRECO.input + + uso.output_tokens * PRECO.output + + uso.cache_read_input_tokens * PRECO.cache_read + + uso.cache_creation_input_tokens * PRECO.cache_write) / 1e6; + $("usoBox").textContent = + `tokens: ${uso.input_tokens.toLocaleString("pt-BR")} in · ${uso.output_tokens.toLocaleString("pt-BR")} out · ` + + `cache ${uso.cache_read_input_tokens.toLocaleString("pt-BR")} lidos / ${uso.cache_creation_input_tokens.toLocaleString("pt-BR")} gravados · ` + + `custo estimado US$ ${custo.toFixed(3)} (estimativa válida para o modelo Opus)`; +} diff --git a/app/public/index.html b/app/public/index.html new file mode 100644 index 0000000..7b0e242 --- /dev/null +++ b/app/public/index.html @@ -0,0 +1,187 @@ + + + + + + Redator de SD — RMDS / SES-MG + + + + +
      +

      Redator de SD

      + RMDS · PE 29/2025 — SES-MG × Isis Saúde + + carregando… + Nova SD + Validar JSON + Base de Conhecimento + + +
      + + + + + +
      + +
      + + +
      +
      +

      Nova SD

      +

      Cole a demanda do jeito que ela chegou — trecho de ata, e-mail encaminhado, mensagem. + Não precisa organizar. O redator lê, compara com a base e pergunta só o que faltar.

      + +
      + + +
      +

      O modelo fica fixo para a SD inteira (trocar no meio + descartaria o cache da conversa). Lista configurável em LLM_MODELOS no .env.

      +
      +
      +

      SDs em andamento

      +
      +
      +
      +

      Saldo dos pools de UST

      +
      +
      +
      + + +
      +
      +
      +
      +
      + + +
      +
      +
      + +
      + + +
      +
      +
      +

      Validar JSON externo

      +

      Cole aqui o JSON de uma SD que não nasceu neste app (ex.: redigida no Claude ou + recebida de um colega). O app recalcula UST e datas, roda o checklist do contrato e libera o + documento para impressão — sem salvar nada.

      + + +
      + + + + +
      + +
      +
      + +
      + + +
      +
      +

      Alimentar base de conhecimento

      +

      Envie o documento de uma SD real (.pdf, .md ou .txt — + se estiver em .docx, exporte como PDF). O app lê o documento, extrai o registro estruturado + (itens, UST por item, período, status) e sugere capacidades reutilizáveis. Nada é gravado + sem a sua revisão abaixo.

      +
      + + + +
      + + + +
      +
      + + +
      + + + + diff --git a/app/public/styles.css b/app/public/styles.css new file mode 100644 index 0000000..56883d9 --- /dev/null +++ b/app/public/styles.css @@ -0,0 +1,168 @@ +/* Tema: claro é o padrão; o escuro vale quando o usuário escolhe (data-theme="dark") + ou quando o sistema está escuro e o usuário não forçou o claro (data-theme="light"). + O botão 🌗 do topo alterna automático → claro → escuro (persistido em localStorage). */ +:root{ + color-scheme:light; + --bg:#F2F6F5; --surface:#FFFFFF; --ink:#1C2A2D; --muted:#5C6B6E; + --line:#D7E1DF; --accent:#0B6B70; --accent-soft:#E1EFEF; --accent-ink:#FFFFFF; + --ok:#1E7B47; --ok-bg:#E3F2E9; --warn:#A8730B; --warn-bg:#F8EFDB; + --err:#B3362B; --err-bg:#F9E4E1; --pend:#5E6E78; --pend-bg:#E8EDF0; + --shadow:0 1px 3px rgba(28,42,45,.08); + --sans:"Segoe UI",system-ui,-apple-system,sans-serif; + --mono:Consolas,"Cascadia Mono",ui-monospace,monospace; +} +:root[data-theme="dark"]{ + color-scheme:dark; + --bg:#0E191B; --surface:#152325; --ink:#E3EDEB; --muted:#94A6A4; + --line:#25393C; --accent:#56C4CA; --accent-soft:#123B3E; --accent-ink:#08282A; + --ok:#54C88A; --ok-bg:#123626; --warn:#E2AC42; --warn-bg:#3A2D0F; + --err:#E3705F; --err-bg:#41201C; --pend:#9AA8B2; --pend-bg:#22313A; + --shadow:0 1px 3px rgba(0,0,0,.35); +} +@media (prefers-color-scheme: dark){ + :root:not([data-theme="light"]){ + color-scheme:dark; + --bg:#0E191B; --surface:#152325; --ink:#E3EDEB; --muted:#94A6A4; + --line:#25393C; --accent:#56C4CA; --accent-soft:#123B3E; --accent-ink:#08282A; + --ok:#54C88A; --ok-bg:#123626; --warn:#E2AC42; --warn-bg:#3A2D0F; + --err:#E3705F; --err-bg:#41201C; --pend:#9AA8B2; --pend-bg:#22313A; + --shadow:0 1px 3px rgba(0,0,0,.35); + } +} +*{box-sizing:border-box} +[hidden]{display:none !important} +html,body{height:100%} +body{margin:0; background:var(--bg); color:var(--ink); font-family:var(--sans); line-height:1.5; display:flex; flex-direction:column;} +button{font-family:var(--sans); cursor:pointer;} +button:focus-visible, a:focus-visible, textarea:focus-visible, input:focus-visible{outline:2px solid var(--accent); outline-offset:2px;} +textarea, select, input[type=date], input[type=text]{font-family:var(--sans); font-size:14px; background:var(--surface); color:var(--ink); border:1px solid var(--line); border-radius:6px; padding:8px 10px;} +.sel-modelo{display:inline-flex; align-items:center; gap:8px; font-size:13px; color:var(--muted);} +.sel-modelo select{font-family:var(--mono); font-size:12.5px; padding:7px 8px;} +textarea{width:100%; resize:vertical;} + +.appbar{background:var(--surface); border-bottom:1px solid var(--line); padding:10px 20px; display:flex; align-items:center; gap:12px; flex-wrap:wrap;} +.appbar h1{font-size:16px; margin:0;} +.appbar .sub{color:var(--muted); font-size:12px;} +.appbar .spacer{flex:1;} +.chip-status{font-size:12px; color:var(--muted); font-family:var(--mono);} + +.aviso{background:var(--warn-bg); color:var(--warn); padding:10px 20px; font-size:14px;} +.aviso code{font-family:var(--mono);} + +.btn{border:1px solid var(--line); background:var(--surface); color:var(--ink); border-radius:6px; padding:8px 16px; font-size:14px; font-weight:600; text-decoration:none; display:inline-block;} +.btn:hover{border-color:var(--accent);} +.btn-sm{padding:4px 12px; font-size:13px;} +.btn-primary{background:var(--accent); border-color:var(--accent); color:var(--accent-ink);} +.btn-primary:hover{filter:brightness(1.08);} +.btn:disabled{opacity:.45; cursor:not-allowed;} +.btnrow{display:flex; gap:10px; flex-wrap:wrap;} + +.panel{background:var(--surface); border:1px solid var(--line); border-radius:8px; box-shadow:var(--shadow); padding:16px 20px;} +.panel h2{font-size:14px; margin:0 0 10px; text-transform:uppercase; letter-spacing:.06em; color:var(--muted);} +.hint{font-size:13px; color:var(--muted); margin:0 0 10px;} + +/* --- Login --- */ +#telaLogin{flex:1; display:flex; align-items:center; justify-content:center; padding:20px;} +.login-box{width:100%; max-width:340px; display:flex; flex-direction:column; gap:12px;} +.login-box label{font-size:13px; color:var(--muted);} +.login-box input{width:100%; margin-top:4px;} +.login-erro{background:var(--err-bg); color:var(--err); border-radius:6px; padding:8px 12px; font-size:13px;} + +/* --- Home --- */ +#telaHome, #telaBase{max-width:860px; width:100%; margin:0 auto; padding:20px; display:flex; flex-direction:column; gap:16px;} +.nova-demanda textarea{margin-bottom:10px;} +.lista-sds{display:flex; flex-direction:column; gap:6px;} +.sd-item{display:flex; gap:12px; align-items:baseline; padding:8px 10px; border:1px solid var(--line); border-radius:6px; text-decoration:none; color:var(--ink);} +.sd-item:hover{border-color:var(--accent); background:var(--accent-soft);} +.sd-item .num{font-family:var(--mono); font-size:12px; color:var(--accent); white-space:nowrap;} +.sd-item .tit{flex:1; font-size:14px; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap;} +.sd-item .st{font-size:11px; border-radius:10px; padding:1px 8px;} +.sd-item .btn-excluir{border:1px solid var(--line); background:var(--surface); color:var(--muted); + border-radius:6px; font-size:12px; line-height:1; padding:4px 8px; align-self:center; flex:0 0 auto;} +.sd-item .btn-excluir:hover{border-color:var(--err); color:var(--err); background:var(--err-bg);} +.st-rascunho{background:var(--warn-bg); color:var(--warn);} +.st-enviada{background:var(--ok-bg); color:var(--ok);} +.saldos{display:flex; gap:10px; flex-wrap:wrap;} +.saldo-chip{border:1px solid var(--line); border-radius:8px; padding:8px 14px; min-width:130px;} +.saldo-chip .label{font-size:11px; text-transform:uppercase; letter-spacing:.06em; color:var(--muted);} +.saldo-chip .value{font-size:17px; font-weight:700; font-variant-numeric:tabular-nums;} +.saldo-chip .sub{font-size:11px; color:var(--muted);} + +/* --- Conversa --- */ +.conversa{flex:1; display:grid; grid-template-columns:minmax(0,1fr) 420px; gap:0; min-height:0;} +@media (max-width: 980px){ .conversa{grid-template-columns:1fr;} .coluna-painel{border-left:none; border-top:1px solid var(--line);} } +.coluna-chat{display:flex; flex-direction:column; min-height:0; padding:16px 20px; gap:10px;} +.mensagens{flex:1; overflow-y:auto; display:flex; flex-direction:column; gap:12px; padding-right:4px; min-height:200px;} +.msg{max-width:92%; padding:10px 14px; border-radius:10px; font-size:14px;} +.msg.user{align-self:flex-end; background:var(--accent-soft); border:1px solid var(--line); white-space:pre-wrap;} +.msg.assistant{align-self:flex-start; background:var(--surface); border:1px solid var(--line); box-shadow:var(--shadow);} +.msg.assistant p{margin:0 0 8px;} .msg.assistant p:last-child{margin-bottom:0;} +.msg.assistant ul,.msg.assistant ol{margin:4px 0 8px; padding-left:20px;} +.msg.assistant li{margin-bottom:2px;} +.msg.assistant h1,.msg.assistant h2,.msg.assistant h3{font-size:14.5px; margin:10px 0 4px;} +.msg.assistant code{font-family:var(--mono); font-size:12.5px; background:var(--bg); padding:0 4px; border-radius:4px;} +.msg.assistant strong{color:var(--accent);} +.msg .digitando{display:inline-block; animation:pulse 1s infinite;} +.msg .pensando{display:inline-block; color:var(--muted); font-style:italic; font-size:13px; animation:pulse 1.4s infinite;} +@keyframes pulse{50%{opacity:.3}} +@media (prefers-reduced-motion: reduce){ .msg .digitando{animation:none;} } +.opcoes{display:flex; flex-direction:column; gap:6px;} +.opcoes button{text-align:left; background:var(--surface); border:1px solid var(--accent); color:var(--ink); border-radius:8px; padding:8px 14px; font-size:13.5px;} +.opcoes button:hover{background:var(--accent-soft);} +.form-msg{display:flex; gap:8px; align-items:flex-end;} +.form-msg textarea{flex:1;} +.uso{font-size:11.5px; color:var(--muted); font-family:var(--mono);} + +/* --- Painel --- */ +.coluna-painel{border-left:1px solid var(--line); background:var(--surface); overflow-y:auto; padding:16px 18px;} +.painel-vazio{color:var(--muted); font-size:13.5px; padding:30px 10px; text-align:center;} +.pbloco{margin-bottom:18px;} +.pbloco h3{font-size:12px; text-transform:uppercase; letter-spacing:.07em; color:var(--muted); margin:0 0 8px;} +.resumo-chips{display:flex; gap:8px; flex-wrap:wrap; margin-bottom:6px;} +.rchip{border:1px solid var(--line); border-radius:8px; padding:6px 12px;} +.rchip .label{font-size:10px; text-transform:uppercase; letter-spacing:.06em; color:var(--muted);} +.rchip .value{font-size:15px; font-weight:700; font-variant-numeric:tabular-nums;} +.ptitulo{font-size:14.5px; font-weight:700; margin-bottom:8px;} +.twrap{overflow-x:auto;} +table.quadro{border-collapse:collapse; width:100%; font-size:12px; font-variant-numeric:tabular-nums;} +table.quadro th, table.quadro td{border:1px solid var(--line); padding:4px 7px; text-align:left; vertical-align:top;} +table.quadro th{font-size:10.5px; text-transform:uppercase; letter-spacing:.04em; background:var(--accent-soft);} +table.quadro td.num{text-align:right;} +table.quadro tr.total td{font-weight:700; border-top:2px solid var(--accent);} +table.quadro tr.subtotal td{font-weight:600; background:var(--accent-soft);} + +.check-item{display:grid; grid-template-columns:18px 56px 1fr; gap:6px; padding:4px 0; border-top:1px solid var(--line); font-size:12.5px; align-items:start;} +.check-item:first-child{border-top:none;} +.check-item .icon{font-weight:700; text-align:center;} +.check-item .id{font-family:var(--mono); font-size:10.5px; color:var(--muted); padding-top:2px;} +.check-item .obs{display:block; color:var(--muted); font-size:11.5px;} +.st-ok .icon{color:var(--ok);} .st-alerta .icon{color:var(--warn);} +.st-falha .icon{color:var(--err);} .st-pendente .icon{color:var(--pend);} + +.art-entr{margin-bottom:10px;} +.art-entr h4{font-size:12px; margin:0 0 4px;} +.art-row{display:flex; gap:8px; align-items:flex-start; padding:5px 8px; border-radius:6px; font-size:12.5px; margin-bottom:3px;} +.art-row.confirmed{background:var(--ok-bg);} +.art-row.unconfirmed{background:var(--warn-bg);} +.art-row input[type=checkbox]{margin-top:3px; accent-color:var(--accent); width:15px; height:15px; flex:0 0 auto;} +.art-row .art-main{flex:1; min-width:0;} +.art-row label{cursor:pointer; display:block;} +.art-row input[type=date]{font-family:var(--mono); font-size:11.5px; margin-top:3px; padding:2px 6px;} +.art-row .flag{font-size:11px; margin-top:2px;} +.flag.bad{color:var(--err);} .flag.miss{color:var(--warn);} + +/* --- Alimentar base --- */ +.cap-card{border:1px solid var(--line); border-radius:8px; padding:10px 12px; margin-bottom:8px; + display:flex; gap:10px; align-items:flex-start;} +.cap-card input[type=checkbox]{margin-top:4px; accent-color:var(--accent); width:16px; height:16px; flex:0 0 auto;} +.cap-card .cap-main{flex:1; min-width:0; display:flex; flex-direction:column; gap:6px;} +.cap-card input[type=text]{width:100%; font-weight:600;} +.cap-card textarea{font-size:13px;} +.cap-card .cap-meta{display:flex; gap:8px; flex-wrap:wrap;} +.cap-card .cap-meta input{flex:1; min-width:120px; font-size:12.5px; font-family:var(--mono);} +.cap-card.excluida{opacity:.45;} + +.pend-list{margin:0; padding-left:18px; font-size:12.5px; display:flex; flex-direction:column; gap:5px;} +.lock-msg{font-size:12.5px; border-radius:6px; padding:8px 11px; margin-bottom:10px;} +.lock-msg.locked{background:var(--warn-bg); color:var(--warn);} +.lock-msg.open{background:var(--ok-bg); color:var(--ok);} diff --git a/app/src/base.js b/app/src/base.js new file mode 100644 index 0000000..d066de8 --- /dev/null +++ b/app/src/base.js @@ -0,0 +1,152 @@ +// Base de conhecimento: numeração sequencial, saldo de pools e registro de SDs enviadas. +import fs from "node:fs"; +import path from "node:path"; +import { PROJETO_DIR } from "./config.js"; +import { REGRAS } from "./validador.js"; + +const BASE_PATH = path.join(PROJETO_DIR, "base-conhecimento.json"); + +export function lerBase() { + try { + return JSON.parse(fs.readFileSync(BASE_PATH, "utf8")); + } catch { + return { sds: [], capacidades: [], convencoes: {} }; + } +} + +function registrosReais(base) { + // Ignora os registros de exemplo que vieram no esqueleto do arquivo. + return (base.sds || []).filter((s) => !String(s.id).includes("EXEMPLO")); +} + +export function proximoNumero(base, numerosEmUso = []) { + const nums = registrosReais(base) + .map((s) => Number(String(s.id).match(/^SD-(\d+)/)?.[1])) + .filter((n) => !isNaN(n)); + const max = Math.max(0, ...nums, ...numerosEmUso); + return max + 1; +} + +export function saldos(base) { + const consumo = {}; + const consomem = new Set( + base.convencoes?.status_que_consomem_pool || [ + "enviada", "aprovada", "em_execucao", "concluida", "faturada", + ] + ); + registrosReais(base).forEach((s) => { + if (!consomem.has(s.status)) return; + Object.entries(s.ust_por_item || {}).forEach(([item, ust]) => { + consumo[item] = (consumo[item] || 0) + (ust || 0); + }); + }); + const out = {}; + Object.entries(REGRAS.itens).forEach(([item, def]) => { + out[item] = { + pool_total: def.pool_ust_total, + consumido: consumo[item] || 0, + saldo: def.pool_ust_total - (consumo[item] || 0), + }; + }); + return out; +} + +/** Bloco de contexto injetado na primeira mensagem de cada conversa. */ +export function contextoParaConversa(numeroProvisorio) { + const base = lerBase(); + const s = saldos(base); + const reais = registrosReais(base); + const caps = (base.capacidades || []).filter((c) => !String(c.id).includes("EXEMPLO")); + const linhas = [ + `Número provisório desta SD: SD-${numeroProvisorio} (o app confirma no envio).`, + `Data de hoje: ${new Date().toISOString().slice(0, 10)}.`, + "Saldo dos pools de UST (pool total − SDs que consomem pool):", + ...Object.entries(s).map( + ([i, v]) => ` ${i}: saldo ${v.saldo} UST (total ${v.pool_total}, consumido ${v.consumido})` + ), + reais.length + ? `SDs anteriores na base: ${reais.map((r) => `${r.id} (${r.titulo}, ${r.status})`).join("; ")}` + : "Base de SDs anteriores vazia — não cite precedentes.", + caps.length + ? `Capacidades já entregues: ${caps.map((c) => `${c.id} (${c.titulo})`).join("; ")}` + : "Nenhuma capacidade registrada na base — dimensione como construção do zero, mas pergunte ao usuário se algo já existe quando for relevante.", + ]; + return linhas.join("\n"); +} + +/** + * Grava uma SD REAL (extraída de documento e revisada pelo usuário) na base. + * Substitui o registro se o id já existir; capacidades novas recebem CAP-NN sequencial. + */ +export function gravarSdReal(sd, capacidades = []) { + const base = lerBase(); + const registro = { + id: sd.id, + titulo: String(sd.titulo || "").trim(), + projeto: String(sd.projeto || "").trim(), + itens_tr: sd.itens_tr || Object.keys(sd.ust_por_item || {}), + ust_total: sd.ust_total, + ust_por_item: sd.ust_por_item || {}, + status: sd.status, + periodo: sd.periodo || null, + resumo: String(sd.resumo || "").trim(), + tags: Array.isArray(sd.tags) ? sd.tags : [], + fonte: String(sd.fonte || "").trim(), + }; + base.sds = (base.sds || []).filter((s) => s.id !== registro.id); + base.sds.push(registro); + + const capsGravadas = []; + base.capacidades = base.capacidades || []; + let n = Math.max( + 0, + ...base.capacidades + .map((c) => Number(String(c.id).match(/^CAP-(\d+)/)?.[1])) + .filter((x) => !isNaN(x)) + ); + for (const c of capacidades) { + if (!String(c?.titulo || "").trim()) continue; + const cap = { + id: `CAP-${String(++n).padStart(2, "0")}`, + titulo: String(c.titulo).trim(), + descricao: String(c.descricao || "").trim(), + origem_sd: registro.id, + item_tr: String(c.item_tr || "").trim(), + status: "entregue", + tags: Array.isArray(c.tags) ? c.tags : [], + }; + base.capacidades.push(cap); + capsGravadas.push(cap); + } + + base.atualizado_em = new Date().toISOString().slice(0, 10); + fs.writeFileSync(BASE_PATH, JSON.stringify(base, null, 2), "utf8"); + return { sd: registro, capacidades: capsGravadas }; +} + +/** Acrescenta a SD enviada à base de conhecimento (uma linha por SD, como planejado). */ +export function registrarEnvio(sdExport) { + const base = lerBase(); + const cab = sdExport.cabecalho || {}; + const porItem = {}; + (sdExport.entregaveis || []).forEach((e) => { + porItem[e.item_tr] = (porItem[e.item_tr] || 0) + (e.ust || 0); + }); + const registro = { + id: cab.numero_sd, + titulo: cab.titulo || cab.projeto || "", + projeto: cab.projeto || "", + itens_tr: cab.itens_tr || Object.keys(porItem), + ust_total: cab.total_ust, + ust_por_item: porItem, + status: "enviada", + periodo: cab.periodo_execucao || null, + resumo: String(sdExport.objetivo || "").slice(0, 300), + tags: [], + }; + base.sds = (base.sds || []).filter((s) => s.id !== registro.id); + base.sds.push(registro); + base.atualizado_em = new Date().toISOString().slice(0, 10); + fs.writeFileSync(BASE_PATH, JSON.stringify(base, null, 2), "utf8"); + return registro; +} diff --git a/app/src/config.js b/app/src/config.js new file mode 100644 index 0000000..0b90755 --- /dev/null +++ b/app/src/config.js @@ -0,0 +1,47 @@ +import path from "node:path"; +import { fileURLToPath } from "node:url"; +import dotenv from "dotenv"; + +const here = path.dirname(fileURLToPath(import.meta.url)); + +// Raiz do app (D:\IasisSaude\app) e raiz do projeto (D:\IasisSaude), +// onde vivem regras-sd.json, schema-sd.json, base-conhecimento.json e o prompt. +export const APP_DIR = path.resolve(here, ".."); +export const PROJETO_DIR = path.resolve(APP_DIR, ".."); +export const DATA_DIR = path.join(APP_DIR, "data"); + +dotenv.config({ path: path.join(APP_DIR, ".env") }); + +const EFFORTS_VALIDOS = ["low", "medium", "high", "xhigh", "max"]; +let effort = (process.env.LLM_EFFORT || "").trim().toLowerCase(); +if (effort && !EFFORTS_VALIDOS.includes(effort)) { + console.warn( + `⚠ LLM_EFFORT="${process.env.LLM_EFFORT}" inválido (use ${EFFORTS_VALIDOS.join("/")}) — usando o padrão da API.` + ); + effort = ""; +} + +const model = process.env.LLM_MODEL || "claude-opus-5"; + +// Modelos oferecidos nos seletores da interface (Nova SD e Base de Conhecimento). +// LLM_MODELOS: lista separada por vírgula; o LLM_MODEL padrão sempre entra. +const modelos = [ + ...new Set( + [model, ...(process.env.LLM_MODELOS || "").split(",").map((m) => m.trim()).filter(Boolean)] + ), +]; + +export const config = { + provider: process.env.LLM_PROVIDER || "anthropic", + model, + modelos, + apiKey: process.env.ANTHROPIC_API_KEY || "", + effort, // "" = padrão da API (high) + port: Number(process.env.PORT || 3344), + usuario: process.env.APP_USUARIO || "", + senha: process.env.APP_SENHA || "", +}; + +export function configOk() { + return Boolean(config.apiKey); +} diff --git a/app/src/extrator.js b/app/src/extrator.js new file mode 100644 index 0000000..4449d2e --- /dev/null +++ b/app/src/extrator.js @@ -0,0 +1,160 @@ +// Extração de uma SD REAL (documento .pdf/.md/.txt) para o formato da base de conhecimento. +// O LLM lê o documento e devolve o registro estruturado; a conferência determinística +// roda em código; a gravação só acontece depois da revisão humana na tela. +import { REGRAS } from "./validador.js"; +import { lerBase, saldos } from "./base.js"; + +const STATUS_SD = ["rascunho", "enviada", "aprovada", "em_execucao", "concluida", "faturada", "cancelada"]; +const STATUS_CONSOMEM = ["enviada", "aprovada", "em_execucao", "concluida", "faturada"]; + +// ---------- Ferramenta de extração (saída estruturada) ---------- + +export const TOOL_EXTRACAO = { + name: "registrar_extracao", + description: + "Registra o resultado da extração do documento da SD no formato da base de conhecimento. " + + "Chame exatamente uma vez, com todos os fatos que o documento sustenta.", + input_schema: { + type: "object", + required: ["sd"], + properties: { + sd: { + type: "object", + required: ["id", "titulo", "projeto", "itens_tr", "ust_total", "ust_por_item", "status", "resumo", "tags"], + properties: { + id: { type: "string", pattern: "^SD-\\d+$", description: "Número oficial da SD no contrato, ex.: SD-8" }, + titulo: { type: "string" }, + projeto: { type: "string", description: "Programa/projeto a que a SD pertence, ex.: 'P2 — Vacinação'" }, + itens_tr: { type: "array", items: { type: "string", enum: Object.keys(REGRAS.itens) } }, + ust_total: { type: "integer", description: "Total de UST da SD" }, + ust_por_item: { + type: "object", + description: "Soma das UST do quadro de entregáveis, por item do TR. As chaves devem ser as mesmas de itens_tr e a soma deve bater com ust_total.", + additionalProperties: { type: "integer" }, + }, + status: { type: "string", enum: STATUS_SD }, + periodo: { + type: ["object", "null"], + properties: { + inicio: { type: "string", description: "AAAA-MM-DD" }, + fim: { type: "string", description: "AAAA-MM-DD" }, + }, + }, + resumo: { type: "string", description: "Resumo factual em até ~600 caracteres: o que foi entregue e o que ficou de reutilizável." }, + tags: { type: "array", items: { type: "string" }, description: "minúsculas, sem acento, singular" }, + fonte: { type: "string", description: "Nome do arquivo enviado" }, + }, + }, + capacidades: { + type: "array", + description: "Capacidades REUTILIZÁVEIS que esta SD entregou (integrações, motores, componentes, dicionários de dados) — aquilo que uma SD futura reaproveitaria em vez de reconstruir.", + items: { + type: "object", + required: ["titulo", "descricao", "item_tr"], + properties: { + titulo: { type: "string" }, + descricao: { type: "string", description: "Padrão: o que existe hoje + o que uma nova SD NÃO precisa refazer." }, + item_tr: { type: "string", enum: Object.keys(REGRAS.itens) }, + tags: { type: "array", items: { type: "string" } }, + }, + }, + }, + observacoes: { + type: "array", + items: { type: "string" }, + description: "Tudo que ficou ambíguo no documento ou merece revisão humana (status incerto, valores conflitantes, campos ausentes).", + }, + }, + }, +}; + +export function montarSystemExtracao() { + const itens = Object.entries(REGRAS.itens) + .map(([k, v]) => ` ${k} — item ${v.item_tr_numero} do TR, "${v.titulo_literal}" (CATMAS ${v.codigo_catmas})`) + .join("\n"); + return [ + "Você extrai, de um documento oficial de Solicitação de Demanda (SD) do contrato PE 29/2025", + "(programa RMDS — SES-MG × Isis Saúde), o registro estruturado para a base de conhecimento do redator de SDs.", + "", + "Regras de extração:", + "- Registre APENAS fatos presentes no documento. A base só serve se for verdadeira — nunca estime valores que o documento não sustenta.", + "- ust_por_item: some as UST do quadro de entregáveis por item do TR. A soma por item deve bater com o total da SD; confira antes de responder.", + "- Itens do TR deste contrato:", + itens, + `- status: use a convenção [${STATUS_SD.join(", ")}]. Se o documento não permitir concluir o status, escolha o mais provável e registre a dúvida em observacoes.`, + "- capacidades: só o que é genuinamente reutilizável por SDs futuras. Prefira poucas e bem descritas a muitas e vagas.", + "- tags: minúsculas, sem acento, singular quando possível (ex.: vacinacao, integracao, dashboard).", + "- fonte: repita o nome do arquivo informado na mensagem.", + "", + "Chame a ferramenta registrar_extracao exatamente uma vez. Não escreva texto além da chamada.", + ].join("\n"); +} + +/** Monta o conteúdo da mensagem do usuário a partir do arquivo enviado (base64). */ +export function montarConteudoDocumento({ nome, base64 }) { + const ext = String(nome || "").toLowerCase().match(/\.([a-z0-9]+)$/)?.[1] || ""; + const instrucao = `Arquivo: ${nome}\nExtraia o registro desta SD para a base de conhecimento e chame registrar_extracao.`; + if (ext === "pdf") { + return [ + { type: "document", source: { type: "base64", media_type: "application/pdf", data: base64 } }, + { type: "text", text: instrucao }, + ]; + } + const texto = Buffer.from(base64, "base64").toString("utf8"); + return [{ type: "text", text: `${instrucao}\n\n\n${texto}\n` }]; +} + +// ---------- Conferência determinística (roda em código, sem LLM) ---------- + +/** Devolve a lista de inconsistências: nivel "erro" bloqueia a gravação; "alerta" só avisa. */ +export function conferirExtracao(sd) { + const avisos = []; + const erro = (msg) => avisos.push({ nivel: "erro", msg }); + const alerta = (msg) => avisos.push({ nivel: "alerta", msg }); + if (!sd || typeof sd !== "object") { + erro("Registro da SD ausente ou inválido."); + return avisos; + } + const base = lerBase(); + + if (!/^SD-\d+$/.test(String(sd.id || ""))) erro(`id "${sd.id ?? ""}" fora do padrão SD-NN.`); + if (!String(sd.titulo || "").trim()) erro("Título vazio."); + + const porItem = sd.ust_por_item && typeof sd.ust_por_item === "object" ? sd.ust_por_item : {}; + const soma = Object.values(porItem).reduce((a, b) => a + (Number(b) || 0), 0); + if (!Number.isFinite(sd.ust_total) || sd.ust_total <= 0) erro("ust_total ausente ou inválido."); + else if (soma !== sd.ust_total) + erro(`Soma de ust_por_item (${soma}) difere de ust_total (${sd.ust_total}).`); + + Object.keys(porItem).forEach((i) => { + if (!REGRAS.itens[i]) erro(`Item "${i}" em ust_por_item não existe no contrato (${Object.keys(REGRAS.itens).join(", ")}).`); + }); + const setItens = new Set(Array.isArray(sd.itens_tr) ? sd.itens_tr : []); + const setPor = new Set(Object.keys(porItem)); + if (setItens.size !== setPor.size || [...setItens].some((i) => !setPor.has(i))) + erro(`itens_tr (${[...setItens].join(", ") || "vazio"}) não bate com as chaves de ust_por_item (${[...setPor].join(", ") || "vazio"}).`); + + if (!STATUS_SD.includes(sd.status)) erro(`status "${sd.status ?? ""}" fora da convenção (${STATUS_SD.join(", ")}).`); + + const p = sd.periodo; + if (p?.inicio && p?.fim && p.inicio > p.fim) erro(`Período invertido: início ${p.inicio} posterior ao fim ${p.fim}.`); + else if (!p?.inicio || !p?.fim) alerta("Período de execução incompleto — confirme início e fim."); + + const existente = (base.sds || []).find((s) => s.id === sd.id); + if (existente) alerta(`${sd.id} já existe na base ("${existente.titulo}") — gravar vai SUBSTITUIR esse registro.`); + + // Impacto no saldo dos pools (descontando o que o registro antigo já consumia). + const consomem = new Set(base.convencoes?.status_que_consomem_pool || STATUS_CONSOMEM); + if (consomem.has(sd.status)) { + const s = saldos(base); + Object.entries(porItem).forEach(([item, ust]) => { + if (!s[item]) return; + const jaContava = existente && consomem.has(existente.status) ? existente.ust_por_item?.[item] || 0 : 0; + const novoSaldo = s[item].saldo + jaContava - (Number(ust) || 0); + if (novoSaldo < 0) alerta(`Saldo do pool ${item} ficaria negativo (${novoSaldo} UST) — confira os valores.`); + }); + } + + if (!String(sd.resumo || "").trim()) alerta("Resumo vazio — o redator usa o resumo para citar precedentes."); + return avisos; +} diff --git a/app/src/historico.js b/app/src/historico.js new file mode 100644 index 0000000..92ebb24 --- /dev/null +++ b/app/src/historico.js @@ -0,0 +1,38 @@ +/** + * Repara históricos em que um tool_use ficou sem o tool_result seguinte + * (ex.: turno truncado por max_tokens). Sem isso, a API rejeita TODAS as + * mensagens seguintes da conversa com erro 400 + * ("tool_use ids were found without tool_result blocks immediately after"). + */ +export function repararHistorico(mensagens) { + const saida = []; + for (let i = 0; i < mensagens.length; i++) { + const m = mensagens[i]; + saida.push(m); + if (m.role !== "assistant" || !Array.isArray(m.content)) continue; + const ids = m.content.filter((b) => b.type === "tool_use").map((b) => b.id); + if (!ids.length) continue; + const prox = mensagens[i + 1]; + const respondidos = new Set( + prox && prox.role === "user" && Array.isArray(prox.content) + ? prox.content.filter((b) => b.type === "tool_result").map((b) => b.tool_use_id) + : [] + ); + const faltantes = ids.filter((id) => !respondidos.has(id)); + if (!faltantes.length) continue; + const resultados = faltantes.map((id) => ({ + type: "tool_result", + tool_use_id: id, + content: + "Esta chamada foi interrompida antes de ser processada (turno truncado). " + + "O estado NÃO foi aplicado — se necessário, chame atualizar_rascunho novamente com a SD completa.", + is_error: true, + })); + if (respondidos.size) { + prox.content = [...resultados, ...prox.content]; // completa o tool_result parcial existente + } else { + saida.push({ role: "user", content: resultados }); // insere a resposta que faltava + } + } + return saida; +} diff --git a/app/src/llm/anthropic.js b/app/src/llm/anthropic.js new file mode 100644 index 0000000..08490e0 --- /dev/null +++ b/app/src/llm/anthropic.js @@ -0,0 +1,93 @@ +// Adaptador Anthropic — implementa a interface ProviderLLM do app. +// Interface: executarTurno({ system, messages, tools, onText }) -> mensagem final da API. +import Anthropic from "@anthropic-ai/sdk"; + +export function criarAnthropicProvider(config) { + const client = new Anthropic({ apiKey: config.apiKey }); + + // Fallback server-side: se um classificador de segurança recusar a requisição, + // a própria API re-executa em um modelo alternativo recomendado. Se a conta não + // tiver o beta disponível, o adaptador detecta o 400 e passa a chamar sem fallback. + let fallbackDisponivel = true; + + async function chamar({ system, messages, tools, onText, model }) { + const params = { + model: model || config.model, + // Atenção: max_tokens cobre raciocínio interno + texto + JSON da ferramenta. + // O rascunho completo da SD é grande; 16k estourava em turnos avançados. + max_tokens: 32000, + system, + messages, + tools, + cache_control: { type: "ephemeral" }, // auto-cache do último bloco (histórico da conversa) + }; + if (config.effort) params.output_config = { effort: config.effort }; + if (fallbackDisponivel) { + params.betas = ["server-side-fallback-2026-07-01"]; + params.fallbacks = "default"; + } + const stream = client.beta.messages.stream(params); + for await (const event of stream) { + if (event.type === "content_block_delta" && event.delta.type === "text_delta") { + onText?.(event.delta.text); + } + } + return await stream.finalMessage(); + } + + return { + nome: "anthropic", + modelo: config.model, + + /** + * Chamada única (sem streaming) com saída estruturada forçada — usada pela + * extração de SD real. Aceita PDF via bloco document na mensagem. + */ + async executarExtracao({ system, messages, tools, toolName, model }) { + const params = { + model: model || config.model, + max_tokens: 8000, + system, + messages, + tools, + tool_choice: { type: "tool", name: toolName }, + }; + let resp; + try { + resp = await client.messages.create(params); + } catch (err) { + // tool_choice forçado é incompatível com raciocínio estendido em algumas + // configurações — repete deixando o modelo decidir (o prompt já obriga a chamada). + if (err?.status === 400 && /tool_choice|thinking/i.test(String(err?.message || err))) { + resp = await client.messages.create({ ...params, tool_choice: { type: "auto" } }); + } else { + throw err; + } + } + const tu = (resp.content || []).find((b) => b.type === "tool_use" && b.name === toolName); + if (!tu) { + const texto = (resp.content || []).filter((b) => b.type === "text").map((b) => b.text).join(" "); + throw new Error("O modelo não retornou a extração estruturada." + (texto ? ` Resposta: ${texto.slice(0, 300)}` : "")); + } + return { input: tu.input, usage: resp.usage }; + }, + + async executarTurno(args) { + try { + return await chamar(args); + } catch (err) { + const msg = String(err?.message || err); + if ( + fallbackDisponivel && + err?.status === 400 && + /fallback/i.test(msg) + ) { + // Conta sem o beta de fallback: repete sem ele e não tenta de novo. + fallbackDisponivel = false; + return await chamar(args); + } + throw err; + } + }, + }; +} diff --git a/app/src/llm/provider.js b/app/src/llm/provider.js new file mode 100644 index 0000000..39d7162 --- /dev/null +++ b/app/src/llm/provider.js @@ -0,0 +1,16 @@ +// Camada de abstração de provedor de LLM. +// MVP: adaptador Anthropic. Fase 1.1: adaptador OpenAI implementando a mesma interface. +import { criarAnthropicProvider } from "./anthropic.js"; + +export function criarProvider(config) { + switch (config.provider) { + case "anthropic": + return criarAnthropicProvider(config); + case "openai": + throw new Error( + "Adaptador OpenAI ainda não implementado (previsto para a fase 1.1). Use LLM_PROVIDER=anthropic." + ); + default: + throw new Error(`Provedor desconhecido: ${config.provider}`); + } +} diff --git a/app/src/redator/prompt.js b/app/src/redator/prompt.js new file mode 100644 index 0000000..cd53f82 --- /dev/null +++ b/app/src/redator/prompt.js @@ -0,0 +1,86 @@ +// Monta o system prompt do redator: prompt-redator-sd.md + regras + schema + base, +// mais a seção que adapta o comportamento ao aplicativo (tool em vez de bloco de texto). +import fs from "node:fs"; +import path from "node:path"; +import { PROJETO_DIR } from "./../config.js"; + +const SECAO_APP = ` + +Você está rodando dentro do aplicativo Redator de SD. Ajustes ao e à : + +1. NÃO escreva o bloco "RASCUNHO ATÉ AQUI" em texto. Em vez disso, ao final de CADA turno em que + algum campo da SD mudou, chame a ferramenta atualizar_rascunho com o estado COMPLETO da SD até + aqui (campos ainda desconhecidos podem ficar ausentes, mas envie tudo o que já foi definido — + a ferramenta substitui o estado anterior por inteiro). É essa chamada que alimenta o painel do + documento que o usuário vê ao lado do chat. +2. O resultado da ferramenta traz a validação DETERMINÍSTICA do app (os itens "codigo" do checklist, + recalculados em código). Trate esse resultado como autoridade: se apontar falha, corrija a SD e + chame a ferramenta de novo no mesmo turno antes de responder ao usuário. +3. Na final (Passo 6), entregue apenas o rascunho legível no chat — NÃO cole o JSON em bloco + de código; o JSON vai pela ferramenta. Preencha também o array "validacao" da SD com o seu parecer + sobre os itens de "julgamento" (ENQ-02..05, DOC-01, DOC-04..06, DOC-10, DOC-11). +4. A confirmação de artefatos (DOC-08) acontece na interface, fora do chat: o usuário marca checkbox + e data no painel. Não insista em pedir confirmação textual; registre os artefatos como tipos com + confirmado_pelo_usuario=false e siga. +5. No texto do chat, não use tabelas markdown — use listas. As opções de resposta que você oferece + devem vir em linhas iniciadas por "OPCAO:" (ex.: "OPCAO: 1. Ciclo completo → 4 entregáveis"), + uma por linha, que o app transforma em botões clicáveis. +6. A primeira mensagem do usuário traz um bloco com o número provisório da SD, + a data de hoje e o saldo real dos pools — use esses valores; não os recalcule nem os questione. +`; + +const ARQUIVOS = [ + "prompt-redator-sd.md", + "regras-sd.json", + "schema-sd.json", + "base-conhecimento.json", +]; + +let cache = null; +let assinatura = ""; + +// Assinatura por data de modificação: se qualquer arquivo-fonte mudou no disco, +// o prompt é remontado na próxima conversa — sem precisar reiniciar o servidor. +function assinaturaArquivos() { + return ARQUIVOS.map((f) => { + try { + return fs.statSync(path.join(PROJETO_DIR, f)).mtimeMs; + } catch { + return 0; + } + }).join("|"); +} + +export function montarSystem() { + const atual = assinaturaArquivos(); + if (cache && assinatura === atual) return cache; + assinatura = atual; + cache = null; + const prompt = fs.readFileSync(path.join(PROJETO_DIR, "prompt-redator-sd.md"), "utf8"); + const regras = fs.readFileSync(path.join(PROJETO_DIR, "regras-sd.json"), "utf8"); + const schema = fs.readFileSync(path.join(PROJETO_DIR, "schema-sd.json"), "utf8"); + let baseTexto; + try { + baseTexto = fs.readFileSync(path.join(PROJETO_DIR, "base-conhecimento.json"), "utf8"); + } catch { + baseTexto = '{"sds": [], "capacidades": []}'; + } + + // Injeta a base no bloco do prompt (substitui o comentário de injeção). + const promptComBase = prompt.replace( + /[\s\S]*?<\/base_consultada>/, + `\nConteúdo de base-conhecimento.json (registros com "EXEMPLO" no id devem ser ignorados):\n${baseTexto}\n` + ); + + const texto = [ + promptComBase, + `\nConteúdo de regras-sd.json:\n${regras}\n`, + `\nConteúdo de schema-sd.json (formato da SD e da ferramenta atualizar_rascunho):\n${schema}\n`, + SECAO_APP, + ].join("\n\n"); + + // Um único bloco com cache_control: o prefixo inteiro (estável) é cacheado; + // o que varia por conversa vai na primeira mensagem do usuário. + cache = [{ type: "text", text: texto, cache_control: { type: "ephemeral" } }]; + return cache; +} diff --git a/app/src/redator/tools.js b/app/src/redator/tools.js new file mode 100644 index 0000000..3712c0e --- /dev/null +++ b/app/src/redator/tools.js @@ -0,0 +1,45 @@ +// Ferramenta atualizar_rascunho: o input é a própria SD, no formato de schema-sd.json. +import fs from "node:fs"; +import path from "node:path"; +import { PROJETO_DIR } from "./../config.js"; + +function limparSchema(node) { + // Remove chaves de metadado que não pertencem a um input_schema de ferramenta. + if (Array.isArray(node)) return node.map(limparSchema); + if (node && typeof node === "object") { + const out = {}; + for (const [k, v] of Object.entries(node)) { + if (["$schema", "$id", "$comment", "title"].includes(k)) continue; + out[k] = limparSchema(v); + } + return out; + } + return node; +} + +let cache = null; + +export function ferramentas() { + if (cache) return cache; + const schema = JSON.parse( + fs.readFileSync(path.join(PROJETO_DIR, "schema-sd.json"), "utf8") + ); + const inputSchema = limparSchema(schema); + // O rascunho parcial não tem todos os campos obrigatórios ainda — relaxa o required + // do nível raiz para a ferramenta aceitar estados intermediários da entrevista. + delete inputSchema.required; + + cache = [ + { + name: "atualizar_rascunho", + description: + "Atualiza o painel do documento com o estado COMPLETO e atual da SD (substitui o estado " + + "anterior por inteiro — envie todos os campos já definidos, não apenas o que mudou). " + + "Chame ao final de cada turno em que algum campo da SD mudou, e novamente no mesmo turno " + + "se o resultado da validação apontar falha que você consiga corrigir. O resultado retorna " + + "a validação determinística do app (itens 'codigo' do checklist, UST recalculada, subtotais).", + input_schema: inputSchema, + }, + ]; + return cache; +} diff --git a/app/src/server.js b/app/src/server.js new file mode 100644 index 0000000..14d4159 --- /dev/null +++ b/app/src/server.js @@ -0,0 +1,381 @@ +import express from "express"; +import path from "node:path"; +import crypto from "node:crypto"; +import { APP_DIR, config, configOk } from "./config.js"; +import { criarProvider } from "./llm/provider.js"; +import { montarSystem } from "./redator/prompt.js"; +import { ferramentas } from "./redator/tools.js"; +import { + validar, + resumoParaRedator, + construirExport, + bloqueiosDeExportacao, + CHECKLIST, + REGRAS, +} from "./validador.js"; +import { lerBase, proximoNumero, saldos, contextoParaConversa, registrarEnvio, gravarSdReal } from "./base.js"; +import { TOOL_EXTRACAO, montarSystemExtracao, montarConteudoDocumento, conferirExtracao } from "./extrator.js"; +import { repararHistorico } from "./historico.js"; +import { listarSds, obterSd, criarSd, salvarSd, excluirSd, numerosEmUso } from "./store.js"; + +const app = express(); +// 25mb comporta um PDF de SD em base64 (rota /api/base/extrair); as demais rotas têm corpos pequenos. +app.use(express.json({ limit: "25mb" })); +app.use(express.static(path.join(APP_DIR, "public"))); + +let provider = null; +function getProvider() { + if (!configOk()) throw new Error("ANTHROPIC_API_KEY não configurada. Copie .env.example para .env e preencha a chave."); + if (!provider) provider = criarProvider(config); + return provider; +} + +function somarUso(acc, usage) { + if (!usage) return; + acc.input_tokens += usage.input_tokens || 0; + acc.output_tokens += usage.output_tokens || 0; + acc.cache_read_input_tokens += usage.cache_read_input_tokens || 0; + acc.cache_creation_input_tokens += usage.cache_creation_input_tokens || 0; + acc.turnos += 1; +} + +/** + * Executa um turno do redator (com loop de ferramenta) e transmite o progresso + * ao navegador como NDJSON: {t:"delta"|"rascunho"|"uso"|"erro"|"fim", ...} + */ +async function rodarTurno(registro, res) { + res.setHeader("Content-Type", "application/x-ndjson; charset=utf-8"); + res.setHeader("Cache-Control", "no-cache"); + const send = (obj) => res.write(JSON.stringify(obj) + "\n"); + + try { + const prov = getProvider(); + const system = montarSystem(); + const tools = ferramentas(); + let textoDoTurno = ""; + let iteracoes = 0; + let truncamentos = 0; + + // Cura históricos quebrados por versões anteriores (tool_use sem tool_result). + registro.mensagens = repararHistorico(registro.mensagens); + + while (iteracoes++ < 8) { + const final = await prov.executarTurno({ + system, + messages: registro.mensagens, + tools, + model: registro.modelo || undefined, + onText: (t) => { + textoDoTurno += t; + send({ t: "delta", text: t }); + }, + }); + somarUso(registro.uso, final.usage); + registro.mensagens.push({ role: "assistant", content: final.content }); + + const truncado = final.stop_reason === "max_tokens"; + const toolUses = (final.content || []).filter((b) => b.type === "tool_use"); + + // Regra de ouro da API: TODO tool_use recebe tool_result na mensagem seguinte, + // seja qual for o stop_reason — senão o histórico fica inválido para sempre. + if (toolUses.length) { + const results = []; + for (const tu of toolUses) { + if (truncado) { + results.push({ + type: "tool_result", + tool_use_id: tu.id, + content: + "A chamada foi truncada pelo limite de tokens e NÃO foi aplicada. " + + "Chame atualizar_rascunho novamente com a SD completa.", + is_error: true, + }); + } else if (tu.name === "atualizar_rascunho") { + registro.rascunho = tu.input; + const v = validar(tu.input); + send({ t: "rascunho", sd: tu.input, validacao: v, bloqueios: bloqueiosDeExportacao(v) }); + results.push({ + type: "tool_result", + tool_use_id: tu.id, + content: JSON.stringify(resumoParaRedator(v)), + }); + } else { + results.push({ + type: "tool_result", + tool_use_id: tu.id, + content: `Ferramenta desconhecida: ${tu.name}`, + is_error: true, + }); + } + } + registro.mensagens.push({ role: "user", content: results }); + } + + if (final.stop_reason === "refusal") { + send({ t: "erro", msg: "A requisição foi recusada pelos classificadores de segurança do modelo. Reformule a mensagem e tente de novo." }); + break; + } + if (truncado) { + // Dá uma chance de o modelo retomar; se truncar de novo, encerra com aviso. + if (++truncamentos <= 1 && toolUses.length) continue; + send({ t: "erro", msg: "A resposta excedeu o limite de tokens do turno. Envie 'continue' para retomar." }); + break; + } + if (final.stop_reason === "pause_turn") continue; + if (final.stop_reason !== "tool_use" || !toolUses.length) break; + } + + registro.chat.push({ role: "assistant", texto: textoDoTurno }); + salvarSd(registro); + send({ t: "uso", uso: registro.uso }); + send({ t: "fim" }); + } catch (err) { + console.error(err); + send({ t: "erro", msg: String(err?.message || err) }); + salvarSd(registro); + } finally { + res.end(); + } +} + +// ---------- Autenticação (usuário único via .env) ---------- + +const sessoes = new Set(); // tokens ativos (memória — reiniciar o servidor exige novo login) +const authAtivo = () => Boolean(config.usuario && config.senha); +const tokenDe = (req) => /(?:^|;\s*)sd_token=([^;]+)/.exec(req.headers.cookie || "")?.[1] || null; +const logado = (req) => !authAtivo() || sessoes.has(tokenDe(req)); + +app.get("/api/sessao", (req, res) => { + res.json({ precisa_login: authAtivo(), logado: logado(req), configurado: configOk() }); +}); + +app.post("/api/login", (req, res) => { + if (!authAtivo()) return res.json({ ok: true }); + const { usuario, senha } = req.body || {}; + const okU = typeof usuario === "string" && + usuario.length === config.usuario.length && + crypto.timingSafeEqual(Buffer.from(usuario), Buffer.from(config.usuario)); + const okS = typeof senha === "string" && + senha.length === config.senha.length && + crypto.timingSafeEqual(Buffer.from(senha), Buffer.from(config.senha)); + if (okU && okS) { + const t = crypto.randomUUID(); + sessoes.add(t); + res.setHeader("Set-Cookie", `sd_token=${t}; HttpOnly; SameSite=Lax; Path=/; Max-Age=2592000`); + return res.json({ ok: true }); + } + setTimeout(() => res.status(401).json({ erro: "Usuário ou senha inválidos." }), 700); +}); + +app.post("/api/logout", (req, res) => { + sessoes.delete(tokenDe(req)); + res.setHeader("Set-Cookie", "sd_token=; Path=/; Max-Age=0"); + res.json({ ok: true }); +}); + +// Tudo abaixo desta guarda exige sessão (quando o login estiver configurado). +app.use("/api", (req, res, next) => { + if (logado(req)) return next(); + res.status(401).json({ erro: "nao_autenticado" }); +}); + +// ---------- Rotas ---------- + +app.get("/api/status", (_req, res) => { + const base = lerBase(); + res.json({ + configurado: configOk(), + provider: config.provider, + model: config.model, + modelos: config.modelos, + proximo_numero: proximoNumero(base, numerosEmUso()), + saldos: saldos(base), + checklist: CHECKLIST, + login_ativo: authAtivo(), + regras: { + artefatos_minimos: REGRAS.artefatos_minimos, + itens: Object.fromEntries( + Object.entries(REGRAS.itens).map(([k, v]) => [ + k, + { numero: v.item_tr_numero, catmas: v.codigo_catmas, titulo_literal: v.titulo_literal }, + ]) + ), + }, + }); +}); + +// Validação de uma SD externa (JSON colado — não nasceu de uma entrevista do app). +app.post("/api/validar", (req, res) => { + const sd = req.body?.sd; + if (!sd || typeof sd !== "object" || !sd.cabecalho || !Array.isArray(sd.entregaveis)) + return res.status(400).json({ + erro: "O JSON não parece uma SD: faltam os campos obrigatórios 'cabecalho' e 'entregaveis' (schema-sd.json).", + }); + const exportado = construirExport(sd); + const validacao = validar(exportado); + res.json({ exportado, validacao, bloqueios: bloqueiosDeExportacao(validacao) }); +}); + +// ---------- Alimentar a base de conhecimento com uma SD real ---------- + +const EXT_ACEITAS = new Set(["pdf", "md", "txt", "markdown"]); + +// Extrai o registro estruturado de um documento de SD (uma chamada de LLM, sem streaming). +app.post("/api/base/extrair", async (req, res) => { + try { + const { nome, base64 } = req.body || {}; + if (!nome || !base64) return res.status(400).json({ erro: "Envie o arquivo (nome + conteúdo)." }); + const ext = String(nome).toLowerCase().match(/\.([a-z0-9]+)$/)?.[1] || ""; + if (!EXT_ACEITAS.has(ext)) + return res.status(400).json({ + erro: `Formato .${ext || "?"} não suportado. Envie .pdf, .md ou .txt (para .docx, exporte como PDF).`, + }); + const prov = getProvider(); + const out = await prov.executarExtracao({ + system: montarSystemExtracao(), + messages: [{ role: "user", content: montarConteudoDocumento({ nome, base64 }) }], + tools: [TOOL_EXTRACAO], + toolName: TOOL_EXTRACAO.name, + model: modeloValido(req.body?.modelo) || undefined, + }); + const { sd, capacidades = [], observacoes = [] } = out.input || {}; + if (sd && !sd.fonte) sd.fonte = nome; + res.json({ sd, capacidades, observacoes, conferencia: conferirExtracao(sd), uso: out.usage }); + } catch (err) { + console.error(err); + res.status(500).json({ erro: String(err?.message || err) }); + } +}); + +// Reconfere um registro editado na tela de revisão (só código, sem LLM). +app.post("/api/base/conferir", (req, res) => { + res.json({ conferencia: conferirExtracao(req.body?.sd) }); +}); + +// Grava o registro revisado. Erros de conferência bloqueiam; alertas passam (o usuário revisou). +app.post("/api/base/gravar", (req, res) => { + const { sd, capacidades } = req.body || {}; + const conferencia = conferirExtracao(sd); + if (conferencia.some((c) => c.nivel === "erro")) + return res.status(409).json({ erro: "Há erros de conferência — corrija o registro antes de gravar.", conferencia }); + const r = gravarSdReal(sd, Array.isArray(capacidades) ? capacidades : []); + res.json({ ok: true, ...r, saldos: saldos(lerBase()) }); +}); + +app.get("/api/sds", (_req, res) => res.json(listarSds())); + +app.get("/api/sds/:id", (req, res) => { + const registro = obterSd(req.params.id); + if (!registro) return res.status(404).json({ erro: "SD não encontrada" }); + const validacao = registro.rascunho ? validar(registro.rascunho) : null; + res.json({ + id: registro.id, + numero: registro.numero, + modelo: registro.modelo || "", + status: registro.status, + criadaEm: registro.criadaEm, + demanda: registro.demanda, + chat: registro.chat, + rascunho: registro.rascunho, + uso: registro.uso, + validacao, + bloqueios: validacao ? bloqueiosDeExportacao(validacao) : null, + }); +}); + +// Modelo vindo da interface: só aceita os da lista configurada (LLM_MODEL + LLM_MODELOS). +function modeloValido(m) { + return typeof m === "string" && config.modelos.includes(m) ? m : ""; +} + +// Exclui um rascunho da lista. Uma SD "enviada" pode ser excluída da lista também — +// o registro dela na base de conhecimento NÃO é afetado (a base é a memória oficial). +app.delete("/api/sds/:id", (req, res) => { + if (!excluirSd(req.params.id)) return res.status(404).json({ erro: "SD não encontrada" }); + res.json({ ok: true }); +}); + +// Nova SD: cria o registro e roda o primeiro turno (streaming). +app.post("/api/sds", async (req, res) => { + const demanda = String(req.body?.demanda || "").trim(); + if (!demanda) return res.status(400).json({ erro: "Informe a demanda bruta." }); + const numero = proximoNumero(lerBase(), numerosEmUso()); + const registro = criarSd(demanda, numero, modeloValido(req.body?.modelo)); + registro.chat = [{ role: "user", texto: demanda }]; + registro.mensagens.push({ + role: "user", + content: `\n${contextoParaConversa(numero)}\n\n\nDemanda bruta:\n${demanda}`, + }); + res.setHeader("X-Sd-Id", registro.id); + await rodarTurno(registro, res); +}); + +// Turno seguinte da entrevista (streaming). +app.post("/api/sds/:id/mensagem", async (req, res) => { + const registro = obterSd(req.params.id); + if (!registro) return res.status(404).json({ erro: "SD não encontrada" }); + if (registro.status === "enviada") return res.status(409).json({ erro: "SD já enviada — somente leitura." }); + const texto = String(req.body?.texto || "").trim(); + if (!texto) return res.status(400).json({ erro: "Mensagem vazia." }); + registro.chat.push({ role: "user", texto }); + registro.mensagens.push({ role: "user", content: texto }); + await rodarTurno(registro, res); +}); + +// Trava de integridade: confirmação/data de artefato pelo usuário (fora do chat). +app.post("/api/sds/:id/artefato", (req, res) => { + const registro = obterSd(req.params.id); + if (!registro?.rascunho) return res.status(404).json({ erro: "SD sem rascunho" }); + const { e, a, confirmado, data, nome } = req.body || {}; + const art = registro.rascunho.entregaveis?.[e]?.artefatos?.[a]; + if (!art) return res.status(400).json({ erro: "Artefato não encontrado" }); + if (typeof confirmado === "boolean") art.confirmado_pelo_usuario = confirmado; + if (data !== undefined) { + if (data) art.data = data; + else delete art.data; + } + if (typeof nome === "string" && nome.trim()) art.nome = nome.trim(); + salvarSd(registro); + const validacao = validar(registro.rascunho); + res.json({ sd: registro.rascunho, validacao, bloqueios: bloqueiosDeExportacao(validacao) }); +}); + +// Exportação do JSON final (bloqueada enquanto houver pendência bloqueante). +app.get("/api/sds/:id/export", (req, res) => { + const registro = obterSd(req.params.id); + if (!registro?.rascunho) return res.status(404).json({ erro: "SD sem rascunho" }); + const exportado = construirExport(registro.rascunho); + const v = validar(exportado); + const bloqueios = bloqueiosDeExportacao(v); + if (bloqueios.length && req.query.forcar !== "1") + return res.status(409).json({ erro: "Exportação bloqueada", bloqueios }); + const nome = (exportado.cabecalho?.numero_sd || "sd").toLowerCase().replace(/[^a-z0-9-]+/g, "-"); + res.setHeader("Content-Disposition", `attachment; filename="${nome}.json"`); + res.json(exportado); +}); + +// Marca como enviada: número definitivo + registro na base de conhecimento. +app.post("/api/sds/:id/enviar", (req, res) => { + const registro = obterSd(req.params.id); + if (!registro?.rascunho) return res.status(404).json({ erro: "SD sem rascunho" }); + if (registro.status === "enviada") return res.status(409).json({ erro: "SD já enviada." }); + const exportado = construirExport(registro.rascunho); + const v = validar(exportado); + const bloqueios = bloqueiosDeExportacao(v); + if (bloqueios.length) return res.status(409).json({ erro: "Envio bloqueado", bloqueios }); + exportado.cabecalho.numero_sd = `SD-${registro.numero}`; + registro.rascunho.cabecalho.numero_sd = `SD-${registro.numero}`; + const reg = registrarEnvio(exportado); + registro.status = "enviada"; + salvarSd(registro); + res.json({ ok: true, registro_na_base: reg }); +}); + +app.listen(config.port, () => { + console.log(`\nRedator de SD rodando em http://localhost:${config.port}`); + if (!configOk()) { + console.log("⚠ ANTHROPIC_API_KEY não configurada — copie .env.example para .env e preencha a chave."); + } else { + console.log(`Provedor: ${config.provider} · Modelo: ${config.model}`); + } +}); diff --git a/app/src/store.js b/app/src/store.js new file mode 100644 index 0000000..9685385 --- /dev/null +++ b/app/src/store.js @@ -0,0 +1,80 @@ +// Persistência simples em arquivo (data/sds.json). SQLite fica para uma fase futura. +import fs from "node:fs"; +import path from "node:path"; +import crypto from "node:crypto"; +import { DATA_DIR } from "./config.js"; + +const FILE = path.join(DATA_DIR, "sds.json"); + +function lerTudo() { + try { + return JSON.parse(fs.readFileSync(FILE, "utf8")); + } catch { + return []; + } +} + +function gravarTudo(lista) { + fs.mkdirSync(DATA_DIR, { recursive: true }); + const tmp = FILE + ".tmp"; + fs.writeFileSync(tmp, JSON.stringify(lista, null, 2), "utf8"); + fs.renameSync(tmp, FILE); +} + +export function listarSds() { + return lerTudo().map((s) => ({ + id: s.id, + numero: s.numero, + titulo: s.rascunho?.cabecalho?.titulo || s.demanda.slice(0, 80), + status: s.status, + criadaEm: s.criadaEm, + turnos: (s.mensagens || []).filter((m) => m.role === "user").length, + })); +} + +export function obterSd(id) { + return lerTudo().find((s) => s.id === id) || null; +} + +export function criarSd(demanda, numero, modelo = "") { + const registro = { + id: crypto.randomUUID(), + numero, + modelo, // modelo de LLM fixado na criação ("" = padrão do .env) + status: "rascunho", // rascunho -> enviada + criadaEm: new Date().toISOString(), + demanda, + chat: [], // histórico legível para a interface [{role, texto}] + mensagens: [], // histórico no formato da API (role/content) + rascunho: null, // último estado da SD vindo da tool atualizar_rascunho + uso: { input_tokens: 0, output_tokens: 0, cache_read_input_tokens: 0, cache_creation_input_tokens: 0, turnos: 0 }, + }; + const lista = lerTudo(); + lista.push(registro); + gravarTudo(lista); + return registro; +} + +export function salvarSd(registro) { + const lista = lerTudo(); + const i = lista.findIndex((s) => s.id === registro.id); + if (i === -1) lista.push(registro); + else lista[i] = registro; + gravarTudo(lista); + return registro; +} + +export function excluirSd(id) { + const lista = lerTudo(); + const i = lista.findIndex((s) => s.id === id); + if (i === -1) return false; + lista.splice(i, 1); + gravarTudo(lista); + return true; +} + +export function numerosEmUso() { + return lerTudo() + .map((s) => Number(s.numero)) + .filter((n) => !isNaN(n)); +} diff --git a/app/src/validador.js b/app/src/validador.js new file mode 100644 index 0000000..bb5b909 --- /dev/null +++ b/app/src/validador.js @@ -0,0 +1,331 @@ +// Validador determinístico da SD — mesma lógica do painel (artefato) publicado. +// Fonte das regras: regras-sd.json na raiz do projeto. +import fs from "node:fs"; +import path from "node:path"; +import { PROJETO_DIR } from "./config.js"; + +export const REGRAS = JSON.parse( + fs.readFileSync(path.join(PROJETO_DIR, "regras-sd.json"), "utf8") +); + +const TIPO_KEY = { + Descoberta: "descoberta", + Design: "design", + Arquitetura: "arquitetura", + "Construção": "construcao", +}; + +export const CHECKLIST = REGRAS.checklist; + +const dayNum = (iso) => { + const m = String(iso || "").match(/^(\d{4})-(\d{2})-(\d{2})/); + return m ? Date.UTC(+m[1], +m[2] - 1, +m[3]) / 86400000 : NaN; +}; +const fmtD = (iso) => { + const m = String(iso || "").match(/^(\d{4})-(\d{2})-(\d{2})/); + return m ? `${m[3]}/${m[2]}/${m[1]}` : String(iso || "—"); +}; +const weeksCal = (i, f) => { + const a = dayNum(i), b = dayNum(f); + return isNaN(a) || isNaN(b) ? NaN : Math.ceil((b - a + 1) / 7); +}; + +export function timeboxDe(e) { + const it = REGRAS.itens[e.item_tr]; + const k = TIPO_KEY[e.tipo]; + return it && k ? it.timebox_h[k] : NaN; +} +export function ustDe(e) { + const t = timeboxDe(e); + return isNaN(t) ? NaN : t * (e.semanas || 0); +} +export function minArtefatos(ust) { + const faixa = REGRAS.artefatos_minimos.find( + (b) => b.ust_max === null || ust <= b.ust_max + ); + return faixa ? faixa.artefatos : 1; +} + +/** + * Roda os itens `codigo` e `usuario` do checklist sobre a SD. + * Itens `julgamento` são herdados do array validacao produzido pelo redator. + * Retorna { resultados: {id: {status, obs}}, totais: {ok, alerta, falha, pendente} } + */ +export function validar(sd) { + const E = sd.entregaveis || []; + const cab = sd.cabecalho || {}; + const byN = {}; + E.forEach((e) => (byN[e.n] = e)); + const R = {}; + const set = (id, status, obs) => (R[id] = { status, obs }); + + const semItem = E.filter((e) => !REGRAS.itens[e.item_tr]); + set( + "ENQ-01", + semItem.length ? "falha" : "ok", + semItem.length + ? "Sem item válido: linhas " + semItem.map((e) => e.n).join(", ") + : `${E.length} entregáveis, todos com item válido.` + ); + + set( + "ENQ-06", + semItem.length ? "alerta" : "ok", + "Cada linha do quadro tem um único item_tr (garantido pela estrutura)." + ); + + const somaPorItem = {}; + E.forEach((e) => { + somaPorItem[e.item_tr] = (somaPorItem[e.item_tr] || 0) + (ustDe(e) || 0); + }); + const itensUsados = Object.keys(somaPorItem); + + if (itensUsados.length <= 1) { + set("ENQ-07", "ok", "SD de item único — subtotal dispensado."); + } else { + const st = sd.subtotais_por_item || []; + const faltam = itensUsados.filter((i) => !st.some((s) => s.item_tr === i)); + const errados = st.filter( + (s) => somaPorItem[s.item_tr] !== undefined && s.ust !== somaPorItem[s.item_tr] + ); + if (faltam.length) + set("ENQ-07", "falha", "Sem subtotal declarado para: " + faltam.join(", ")); + else if (errados.length) + set( + "ENQ-07", + "falha", + "Subtotal divergente do recálculo em: " + errados.map((s) => s.item_tr).join(", ") + ); + else + set( + "ENQ-07", + "ok", + itensUsados.map((i) => `${i}: ${somaPorItem[i]} UST`).join(" · ") + ); + } + + const lim = REGRAS.limites; + const foraSem = E.filter( + (e) => !(e.semanas >= lim.semanas_min && e.semanas <= lim.semanas_max) + ); + set( + "CAL-01", + foraSem.length ? "falha" : "ok", + foraSem.length + ? "Fora de 2–4 semanas: linhas " + foraSem.map((e) => `${e.n} (${e.semanas})`).join(", ") + : "Semanas: " + E.map((e) => e.semanas).join(", ") + " — todas entre 2 e 4." + ); + + const contas = [], erros02 = []; + E.forEach((e) => { + const exp = ustDe(e); + contas.push(`${timeboxDe(e)}×${e.semanas}=${exp}`); + if (e.ust !== exp) erros02.push(`linha ${e.n}: declarado ${e.ust}, correto ${exp}`); + }); + set( + "CAL-02", + erros02.length ? "falha" : "ok", + erros02.length ? erros02.join("; ") : "Conta refeita: " + contas.join("; ") + ); + + const err03 = E.filter((e) => e.tipo === "Construção" && e.timebox_h !== timeboxDe(e)); + set( + "CAL-03", + err03.length ? "falha" : "ok", + err03.length + ? "Timebox de Construção errado: linhas " + + err03.map((e) => `${e.n} (${e.timebox_h}h, correto ${timeboxDe(e)}h)`).join(", ") + : "Construção com timebox correto por item." + ); + + const soma = E.reduce((a, e) => a + (ustDe(e) || 0), 0); + set( + "CAL-04", + cab.total_ust === soma ? "ok" : "falha", + `Σ linhas (recalculado) = ${soma} UST; cabeçalho declara ${cab.total_ust ?? "—"}.` + ); + + const overl = []; + E.forEach((e) => + (e.depende_de || []).forEach((dn) => { + const d = byN[dn]; + if (d && e.periodo && d.periodo && !(dayNum(e.periodo.inicio) > dayNum(d.periodo.fim))) + overl.push(`linha ${e.n} sobrepõe a linha ${dn}`); + }) + ); + set( + "CAL-05", + overl.length ? "falha" : "ok", + overl.length ? overl.join("; ") : "Nenhuma sobreposição entre encadeados (depende_de)." + ); + + const semNota = E.filter( + (e) => (e.paralelo_com || []).length && !(e.nota_paralelismo || "").trim() + ); + const temPar = E.some((e) => (e.paralelo_com || []).length); + set( + "CAL-06", + semNota.length ? "falha" : "ok", + semNota.length + ? "Paralelo sem nota: linhas " + semNota.map((e) => e.n).join(", ") + : temPar + ? "Paralelismo declarado em nota." + : "Não há paralelismo na SD." + ); + + const inicios = E.map((e) => e.periodo?.inicio).filter(Boolean).sort(); + const fins = E.map((e) => e.periodo?.fim).filter(Boolean).sort(); + const pi = cab.periodo_execucao?.inicio, pf = cab.periodo_execucao?.fim; + const wCal = weeksCal(pi, pf); + const probs07 = []; + if (inicios.length && pi !== inicios[0]) + probs07.push(`início do cabeçalho (${fmtD(pi)}) ≠ 1º entregável (${fmtD(inicios[0])})`); + if (fins.length && pf !== fins[fins.length - 1]) + probs07.push(`fim do cabeçalho (${fmtD(pf)}) ≠ último entregável (${fmtD(fins[fins.length - 1])})`); + if (!isNaN(wCal) && cab.prazo_execucao_semanas !== wCal) + probs07.push(`calendário = ${wCal} semanas; cabeçalho declara ${cab.prazo_execucao_semanas}`); + set( + "CAL-07", + probs07.length ? "falha" : "ok", + probs07.length + ? probs07.join("; ") + : `${fmtD(pi)} a ${fmtD(pf)} = ${wCal} semanas de calendário — confere.` + ); + + const declarados = cab.itens_tr || []; + const probs02d = []; + itensUsados.forEach((i) => { + if (!declarados.includes(i)) probs02d.push(`${i} usado no quadro mas ausente do cabeçalho`); + }); + declarados.forEach((i) => { + if (!itensUsados.includes(i)) probs02d.push(`${i} declarado no cabeçalho sem entregável`); + }); + (sd.enquadramento_tr || []).forEach((q) => { + if (!itensUsados.includes(q.item_tr)) + probs02d.push(`Seção 5 traz ${q.item_tr} sem entregável correspondente`); + }); + set( + "DOC-02", + probs02d.length ? "falha" : "ok", + probs02d.length ? probs02d.join("; ") : "Itens do cabeçalho, quadro e Seção 5 correspondem." + ); + + const err03t = (sd.enquadramento_tr || []).filter( + (q) => REGRAS.itens[q.item_tr] && q.titulo_literal !== REGRAS.itens[q.item_tr].titulo_literal + ); + set( + "DOC-03", + err03t.length ? "falha" : "ok", + err03t.length + ? "Título divergente do TR em: " + err03t.map((q) => q.item_tr).join(", ") + : "Títulos literais conferem com regras-sd.json." + ); + + const err07 = E.filter((e) => (e.artefatos || []).length < minArtefatos(ustDe(e) || 0)); + set( + "DOC-07", + err07.length ? "falha" : "ok", + err07.length + ? "Abaixo do mínimo: linhas " + + err07.map((e) => `${e.n} (${(e.artefatos || []).length}/${minArtefatos(ustDe(e) || 0)})`).join(", ") + : E.map((e) => `E${e.n}: ${(e.artefatos || []).length}/${minArtefatos(ustDe(e) || 0)}`).join(" · ") + ); + + const todos = E.flatMap((e) => e.artefatos || []); + const naoConf = todos.filter((a) => !a.confirmado_pelo_usuario).length; + set( + "DOC-08", + naoConf ? "pendente" : todos.length ? "ok" : "pendente", + naoConf + ? `${naoConf} de ${todos.length} artefatos ainda não confirmados — bloqueante para exportação.` + : todos.length + ? `Todos os ${todos.length} artefatos confirmados pelo usuário.` + : "SD ainda sem artefatos." + ); + + const errData = [], semData = []; + E.forEach((e) => + (e.artefatos || []).forEach((a) => { + if (a.data) { + if (e.periodo?.fim && dayNum(a.data) > dayNum(e.periodo.fim)) + errData.push(`E${e.n}: "${a.nome}" datado ${fmtD(a.data)} > fim ${fmtD(e.periodo.fim)}`); + } else if (a.confirmado_pelo_usuario) { + semData.push(`E${e.n}: "${a.nome}" confirmado sem data`); + } + }) + ); + if (errData.length) set("DOC-09", "falha", errData.join("; ")); + else if (semData.length) set("DOC-09", "alerta", semData.join("; ")); + else if (todos.some((a) => a.data)) + set("DOC-09", "ok", "Datas informadas são ≤ ao fim do respectivo entregável."); + else set("DOC-09", "alerta", "Nenhum artefato tem data informada ainda."); + + // Itens de julgamento: herdados do parecer do redator (array validacao da SD) + const orig = {}; + (sd.validacao || []).forEach((v) => (orig[v.id] = v)); + CHECKLIST.filter((c) => c.verificador === "julgamento").forEach((c) => { + const o = orig[c.id]; + set( + c.id, + o?.status || "pendente", + o?.observacao || (o ? "Revisão do redator: sem ressalvas." : "Sem parecer do redator ainda.") + ); + }); + + const totais = { ok: 0, alerta: 0, falha: 0, pendente: 0 }; + Object.values(R).forEach((r) => (totais[r.status] = (totais[r.status] || 0) + 1)); + return { resultados: R, totais, soma_ust: soma, soma_por_item: somaPorItem }; +} + +/** Resumo compacto (só o que não está ok) para realimentar o redator via tool_result. */ +export function resumoParaRedator(validacao) { + const pend = Object.entries(validacao.resultados) + .filter(([, r]) => r.status !== "ok") + .map(([id, r]) => `${id} [${r.status}]: ${r.obs}`); + return { + validador_do_app: { + total_ust_recalculado: validacao.soma_ust, + subtotais: validacao.soma_por_item, + itens_nao_ok: pend.length ? pend : "todos os checks de código passaram", + }, + }; +} + +/** Reconstrói a SD com valores recalculados pelo app + array validacao completo (para exportação). */ +export function construirExport(sd) { + const copy = JSON.parse(JSON.stringify(sd)); + const E = copy.entregaveis || []; + E.forEach((e) => { + e.timebox_h = timeboxDe(e); + e.ust = ustDe(e); + }); + const soma = E.reduce((a, e) => a + (e.ust || 0), 0); + copy.cabecalho = copy.cabecalho || {}; + copy.cabecalho.total_ust = soma; + const porItem = {}; + E.forEach((e) => (porItem[e.item_tr] = (porItem[e.item_tr] || 0) + (e.ust || 0))); + if (Object.keys(porItem).length > 1) + copy.subtotais_por_item = Object.keys(porItem) + .sort() + .map((i) => ({ item_tr: i, ust: porItem[i] })); + const v = validar(copy); + copy.validacao = CHECKLIST.map((c) => { + const r = v.resultados[c.id]; + const o = { id: c.id, status: r?.status || "pendente" }; + if (r?.obs && o.status !== "ok") o.observacao = r.obs; + return o; + }); + return copy; +} + +/** Exportação é bloqueada enquanto houver artefato não confirmado ou falha de código. */ +export function bloqueiosDeExportacao(validacao) { + const razoes = []; + if (validacao.resultados["DOC-08"]?.status !== "ok") + razoes.push("artefatos não confirmados (DOC-08)"); + const falhas = Object.entries(validacao.resultados) + .filter(([, r]) => r.status === "falha") + .map(([id]) => id); + if (falhas.length) razoes.push("falhas no checklist: " + falhas.join(", ")); + return razoes; +} diff --git a/base-conhecimento.json b/base-conhecimento.json new file mode 100644 index 0000000..b336816 --- /dev/null +++ b/base-conhecimento.json @@ -0,0 +1,69 @@ +{ + "$comment": "Base de conhecimento do redator de SDs — RMDS / SES-MG (PE 29/2025). Terceiro arquivo da arquitetura: regras-sd.json = regras; schema-sd.json = formato; ESTE arquivo = histórico. Cadastre apenas fatos reais — o redator é proibido de inventar precedentes, e esta base só serve se for verdadeira. Uma linha nova em 'sds' a cada SD enviada (o app faz isso automaticamente); uma em 'capacidades' a cada capacidade entregue (manual).", + "versao": "0.2.0", + "atualizado_em": "2026-08-11", + + "convencoes": { + "status_sd": ["rascunho", "enviada", "aprovada", "em_execucao", "concluida", "faturada", "cancelada"], + "status_que_consomem_pool": ["enviada", "aprovada", "em_execucao", "concluida", "faturada"], + "status_capacidade": ["em_construcao", "entregue"], + "saldo_de_pool": "saldo(item) = pool_ust_total do item em regras-sd.json − soma de ust_por_item das SDs cujo status consome pool", + "tags": "minúsculas, sem acento, singular quando possível; ex.: dashboard, integracao, niten, regulacao, vacinacao, treinamento, automacao", + "ids": "SD-NN segue a numeração oficial do contrato; CAP-NN é sequencial interno desta base" + }, + + "sds": [ + { + "id": "SD-8", + "titulo": "MVP Vacina em Dia — caderneta vacinal digital no MG App com dados do PNI e alertas de pendências", + "projeto": "P2 — Vacinação (Vacina em Dia)", + "itens_tr": ["I-02", "I-05"], + "ust_total": 1120, + "ust_por_item": { "I-02": 720, "I-05": 400 }, + "status": "concluida", + "periodo": { "inicio": "2026-06-02", "fim": "2026-07-04" }, + "resumo": "Caderneta vacinal digital do cidadão no MG App: histórico de imunização unificado via PNI/RNDS com identificação por CNS/CPF, motor determinístico com 60+ regras vacinais versionadas, 5 status de pendência, perfis por faixa etária/gestante/profissional de saúde e filtro de segurança clínica. Publicado em produção em 02/07/2026, antes da vedação eleitoral. Ciclo completo (Descoberta, Design, Arquitetura e Construção) em 10 entregáveis, com paralelismo intenso: 24 semanas de timebox em ~5 semanas de calendário.", + "tags": ["vacinacao", "caderneta-digital", "mg-app", "pni", "rnds", "motor-de-regras", "integracao", "lgpd"], + "fonte": "P2-SD8_MVP_VacinaEmDia.md" + } + ], + + "capacidades": [ + { + "id": "CAP-01", + "titulo": "Integração com a base nacional PNI/RNDS para consulta de histórico vacinal", + "descricao": "A plataforma já consome o histórico vacinal do cidadão em tempo real via middleware Azure SES-MG (login → token → search) sobre o bucket ElasticSearch do PNI, com contrato de dados e schema documentados e tratamento de códigos corrompidos. Nova SD que precise de dados de vacinação NÃO precisa reconstruir esta integração.", + "origem_sd": "SD-8", + "item_tr": "I-02", + "status": "entregue", + "tags": ["integracao", "pni", "rnds", "vacinacao"] + }, + { + "id": "CAP-02", + "titulo": "Embedamento de aplicações da plataforma no MG App", + "descricao": "Caminho técnico completo para publicar módulos dentro do MG App (super-app do Estado): autenticação com token JWT, cadastro no back-office do MG App junto à Prodemge, publicação de URL via DNS ATI SES e padrão mobile-first com telas de fallback. Nova SD com entrega ao cidadão via MG App reaproveita este caminho.", + "origem_sd": "SD-8", + "item_tr": "I-02", + "status": "entregue", + "tags": ["mg-app", "integracao", "autenticacao", "prodemge"] + }, + { + "id": "CAP-03", + "titulo": "Motor determinístico de regras vacinais", + "descricao": "Motor em memória com 60+ regras vacinais em arquivos versionados e auditáveis, hierarquia de resolução de conflitos entre perfis (faixa etária, gestante, profissional de saúde), regras de segurança clínica (ocultação de vacinas vivas para gestantes) e endpoint próprio, validado clinicamente com a SVE. Evoluções de calendário/regra são manutenção deste motor (I-05), não construção nova.", + "origem_sd": "SD-8", + "item_tr": "I-05", + "status": "entregue", + "tags": ["motor-de-regras", "vacinacao", "algoritmo"] + }, + { + "id": "CAP-04", + "titulo": "De-para semântico de siglas PNI/MG para vocabulário canônico de vacinas", + "descricao": "Mapeamento de 103 siglas do índice PNI/MG para o vocabulário canônico de vacinas (3 mappers implementados), com tratamento de siglas corrompidas. Qualquer SD que consuma dados vacinais do PNI reaproveita este dicionário.", + "origem_sd": "SD-8", + "item_tr": "I-05", + "status": "entregue", + "tags": ["pni", "vacinacao", "dado", "interoperabilidade"] + } + ] +} diff --git a/github.bat b/github.bat new file mode 100644 index 0000000..d5eb48e --- /dev/null +++ b/github.bat @@ -0,0 +1,5 @@ +git init -b main +git add . +git commit -m "Redator de SD - preparado para deploy" +git remote add origin https://gitea.aplicativopro.com/wander/RedatorSD.git +git push -u origin main \ No newline at end of file diff --git a/prompt-redator-sd.md b/prompt-redator-sd.md new file mode 100644 index 0000000..17b2070 --- /dev/null +++ b/prompt-redator-sd.md @@ -0,0 +1,102 @@ +# Prompt do Redator de Demandas (SD) — RMDS / SES-MG + +> **Como usar no Cowork ou em um Projeto do Claude:** cole o conteúdo abaixo (do primeiro `` ao fim) nas instruções, e anexe `regras-sd.json` e `schema-sd.json` como arquivos do projeto. Depois, abra a conversa colando a demanda bruta. +> **Como usar no app:** este mesmo texto vira o `system` da chamada à API; os dois JSONs são injetados nos blocos indicados. + +--- + + +Você é o Redator de Demandas do contrato Pregão Eletrônico nº 29/2025 (SES-MG × Isis Saúde), programa RMDS. Sua função: a partir de uma demanda bruta (trecho de ata, e-mail, mensagem), conduzir uma entrevista curta e devolver o rascunho completo de uma Solicitação de Demanda (SD) — enquadrada, dimensionada e validada. + +Você conduz; o usuário decide. Você propõe enquadramento, entregáveis e semanas com justificativa; o usuário confirma ou corrige. Números e regras duras vêm de ; você nunca os inventa nem os contraria. + + + +O conteúdo de `regras-sd.json` é a fonte única das regras determinísticas: itens do TR e títulos literais, timeboxes, limites de semanas, fórmula de UST, mínimos de artefatos, regras de data e o checklist com IDs. +O conteúdo de `schema-sd.json` define o formato exato da SD que você entrega ao final. +Se qualquer instrução sua conflitar com esses arquivos, os arquivos vencem. + + + +Regras invioláveis da entrevista: + +1. **Uma pergunta por turno.** Nunca duas. Nunca um questionário. +2. **Sempre ofereça 2–3 opções prontas** de resposta (com uma linha de consequência em cada: "→ muda o item", "→ encurta a descoberta"), deixando claro que o usuário pode responder livremente. +3. **Nunca pergunte o que dá para inferir.** Antes da primeira pergunta, devolva: (a) o que você entendeu da demanda em 2–3 frases; (b) o que consultou na base, citando IDs; (c) só então a primeira lacuna real. +4. **Ordem fixa das lacunas** (pule as que já estiverem respondidas na demanda bruta ou na base): + - Passo 1 · Natureza — o que o cliente quer VER funcionando; já existe ou é novo; uma ou mais naturezas de trabalho. + - Passo 2 · Enquadramento — item(ns) do TR, aplicando . Se cruzar itens, avise que viram entregáveis separados com subtotal por item. + - Passo 3 · Frentes — quantos entregáveis e de quais tipos, aplicando as regras de quebra de . Nem toda demanda tem os 4 tipos. + - Passo 4 · Dimensionamento — semanas por entregável (justifique pela complexidade), datas, encadeamento vs. paralelismo. UST você calcula pela fórmula e mostra a conta. + - Passo 5 · Lastro — que artefatos comprobatórios EXISTEM ou existirão. Aplique . + - Passo 6 · Geração — rode , apresente o rascunho e as pendências. +5. **Estado visível a cada turno.** Encerre toda resposta com um bloco `RASCUNHO ATÉ AQUI` mostrando os campos já preenchidos (título provisório, item, quadro parcial de entregáveis com UST calculada). No app, esse bloco alimenta o painel do documento. +6. **Alertas no momento certo.** Prazo do demandante menor que o desenho, dependência externa, escopo que estoura 4 semanas: sinalize assim que detectar, com a alternativa concreta (ex.: "ou repactua o prazo, ou a descoberta cai para 2 semanas com escopo reduzido a X"). + + + +As 4 perguntas, nesta ordem: +1. É plataforma crescendo (módulo novo, fonte nova, conector novo)? → I-02 +2. É ensinar alguém a usar algo já implantado? → I-03 +3. É automatizar tarefa repetitiva ou corrigir algo em produção? → I-04 +4. É criar dashboard, algoritmo, IA, busca ativa, análise? → I-05 + +Teste da estrada (dúvida I-02 × I-04): I-02 constrói a estrada; I-04 põe o piloto automático. Primeira vez que conecta ou constrói → I-02. Automatizar o que já poderia ser feito manualmente → I-04. + +Exemplos resolvidos (use como precedente, não repita ao usuário sem necessidade): +- Produto novo sendo construído → I-02 (ainda não existe para ser mantido) +- Dashboard, índice, score, ranking → I-05 (é inteligência, não automação) +- App em produção com bug de interface → I-04 (manutenção perfectiva) +- Regra de negócio nova no motor → I-05 (evolução de algoritmo) +- Oficina de discovery pré-implantação → I-05 (I-03 é consultoria PÓS-implantação) + +Demanda com mais de uma natureza cruza mais de um item: normal, vira entregáveis separados que nunca se somam na mesma linha. + + + +- Semanas por entregável: escolha 2, 3 ou 4 usando a tabela de e SEMPRE justifique em uma frase concreta ("definir fila com a SUBRAS envolve área finalística e casos de borda — não fecha em duas semanas"). +- UST: aplique a fórmula de e mostre a conta (ex.: "Construção I-05, 3 semanas → 80h × 3 = 240 UST"). Nunca estime de cabeça. +- Datas: encadeados não se sobrepõem; paralelos exigem times distintos + nota de paralelismo; data final de cada entregável precisa ser compatível com a data dos artefatos. +- Não crie tipo de entregável para "ficar completo", nem quebre entregável só para ter mais linhas. + + + +Ao gerar o rascunho final, siga o schema e estas regras de texto: + +- **Objetivo:** 2–4 períodos; o que a SD formaliza e o resultado que permite alcançar; não repete contexto. +- **Contexto:** 3–5 parágrafos na ordem: problema de gestão → insumo disponível e por que não basta → o que a demanda entrega → achado/resultado (se houver) → desdobramento. Só números reais informados pelo usuário ou presentes na base; nunca invente números. +- **Nomes:** o produto tem UM nome, usado identicamente do título ao último critério. +- **Critérios de aceite:** verificáveis por terceiro olhando o artefato. + - Servem: "Índice reproduzível: mesma base e mesmo código produzem resultado idêntico"; "Dashboard acessível pelos perfis indicados pela SES-MG"; "Toda pergunta do formulário mapeada à dimensão que alimenta". + - Não servem: "Modelo bem construído"; "Dashboard funcionando adequadamente"; "Documentação completa". +- **Aderência (Seção 5):** 3–5 bullets, um por componente ENTREGUE, no vocabulário do TR (use `descricao_tr` de ), sem citar componente que a SD não tem. Repita a seção inteira por item quando a SD cruza itens, indicando quais entregáveis pertencem a cada um. + + + +- Você sugere TIPOS de artefato comprobatório ("ata de reunião de validação", "nota técnica", "print do painel publicado com data"). Nomes concretos de documentos só entram na SD quando o usuário os informar ou confirmar — no schema, `confirmado_pelo_usuario` fica `false` até isso acontecer, e artefato não confirmado é pendência bloqueante listada em `pendencias`. +- Nunca invente números, datas de documentos ou resultados. Campo sem informação fica marcado como pendência, nunca preenchido com plausibilidade. +- Se o usuário pedir algo que contraria as regras de (ex.: entregável de 5 semanas, UST fora da fórmula, artefato com data posterior à data final), não execute silenciosamente: explique a regra, mostre a consequência (glosa, achado de auditoria, reclassificação que derruba o timebox) e ofereça a alternativa regular. + + + +Antes de entregar o rascunho, percorra o checklist de item a item, pelo ID: +- Itens `verificador: codigo` — refaça a verificação explicitamente (mostre a conta quando for cálculo). No app, estes serão recalculados em JS; na conversa, você é o executor provisório. +- Itens `verificador: julgamento` — avalie como revisor, não como autor: releia o texto gerado procurando a falha, não confirmando o acerto. +- Itens `verificador: usuario` — status `pendente` até confirmação explícita. +Reporte o resultado como lista ✓ / ! / ✗ com o ID e, para tudo que não for ✓, a observação e a ação sugerida. + + + +A entrega final tem duas partes, nesta ordem: +1. **O rascunho legível da SD**, na estrutura: Cabeçalho → 1. Objetivo → 2. Contexto e escopo → 3. Quadro de entregáveis (com linha de total e subtotais por item quando cruzar itens) → 4. Detalhamento por entregável (Resumo / Atividades / Artefatos / Critérios de aceite) → 5. Enquadramento no TR → Resultado da validação → Pendências. +2. **O mesmo conteúdo como JSON válido conforme `schema-sd.json`**, em um único bloco de código, sem comentários. É este JSON que o app consumirá. + + + +Mensagem inicial da conversa (adapte o tom, mantenha o conteúdo): +"Me conta a demanda do jeito que ela chegou — trecho de ata, e-mail encaminhado, mensagem. Não precisa organizar. Eu leio, comparo com o que já foi entregue e vou perguntando só o que faltar para fechar a SD." + + + + + diff --git a/prototipo-criador-de-demanda.html b/prototipo-criador-de-demanda.html new file mode 100644 index 0000000..bd1d3da --- /dev/null +++ b/prototipo-criador-de-demanda.html @@ -0,0 +1,719 @@ + + + + + +Redator de Demandas — protótipo + + + + + + +
      + +
      + +
      +
      Contrato Pregão 29/2025 · SES-MG · Rede Mineira de Dados em Saúde
      +
      +
      Rascunho
      + +
      +
      + +
      + + +
      +
      +
      Entrevista
      +
      passo 0 de 6
      +
      + +
      + +
      + +
      + + + Nada é enviado — protótipo local +
      +
      +
      + + +
      +
      + + +
      + +
      +
      +

      A demanda se escreve enquanto vocês conversam

      +

      Cole o texto bruto ao lado. Cada resposta preenche um campo aqui — enquadramento, entregáveis, USTs e critérios de aceite.

      +
      + +
      + + +
      + +
      +
      + + + + diff --git a/regras-sd.json b/regras-sd.json new file mode 100644 index 0000000..2c1a564 --- /dev/null +++ b/regras-sd.json @@ -0,0 +1,136 @@ +{ + "$comment": "Fonte única de verdade das regras determinísticas do Manual de Redação de SD. Consumida (a) pelo prompt do redator como referência e (b) pelo validador em código do app. Ao mudar o TR, edite APENAS este arquivo.", + "versao": "1.0.0", + "atualizado_em": "2026-08-07", + + "contrato": { + "contratante": "Secretaria de Estado de Saúde de Minas Gerais (SES-MG)", + "contratada": "Isis Saúde", + "instrumento": "Pregão Eletrônico nº 29/2025 — Processo 1321127", + "programa": "RMDS — Rede Mineira de Dados em Saúde" + }, + + "itens": { + "I-02": { + "item_tr_numero": 2, + "codigo_catmas": "000130940", + "titulo_literal": "Desenvolvimento e implantação da tecnologia blockchain", + "apelido_manual": "Desenvolvimento e implantação de plataforma", + "descricao_tr": "Setup de implantação e configuração da solução tecnológica, consistindo no levantamento dos requisitos necessários, na implantação em unidades das Redes de Assistência à Saúde do Estado de Minas Gerais e na realização das integrações com os sistemas de informação (softwares) e/ou bases de dados de saúde utilizados pela Contratante, incluindo: análise das necessidades das áreas de negócio da SES-MG; avaliação técnica dos sistemas de TI existentes; validação operacional; customização e parametrização (regras de negócio, fluxos de trabalho e interfaces); mapeamento de dados e integrações; desenvolvimento de interfaces de integração (APIs, middleware, sistemas legados); e testes de integração. (TR, subitem 1.6.4.1 — descrição do Item 2)", + "complementacao_catmas": "Executar atividades de desenvolvimento de software para soluções com blockchain, incluindo contratos inteligentes e atividades relacionadas com a integração com sistemas legados.", + "pool_ust_total": 50400, + "timebox_h": { "descoberta": 40, "design": 40, "arquitetura": 40, "construcao": 40 } + }, + "I-03": { + "item_tr_numero": 3, + "codigo_catmas": "000097144", + "titulo_literal": "Desenvolvimento e Manutenção de Sistemas de Informação Utilizando Metodologia Ágil", + "apelido_manual": "Capacitação e consultoria pós-implantação", + "descricao_tr": "Serviços de treinamento de usuários e de consultoria pós-implantação de solução tecnológica, componentes críticos para garantir que uma nova solução ou sistema seja adotado com sucesso pela SES-MG. Envolvem uma série de atividades projetadas para maximizar a eficiência operacional e a satisfação dos usuários com a nova solução tecnológica implantada, enquanto eixo estruturante da Secretaria. (TR, subitem 1.6.4.2 — descrição do Item 3)", + "pool_ust_total": 4800, + "timebox_h": { "descoberta": 40, "design": 40, "arquitetura": 40, "construcao": 40 }, + "alerta_redacao": "O título CATMAS do Item 3 é idêntico ao do Item 4 e não menciona treinamento/consultoria. Na Seção 5 da SD, cite o número do item + código CATMAS + a descrição de serviço do subitem 1.6.4.2 do TR para evitar reclassificação." + }, + "I-04": { + "item_tr_numero": 4, + "codigo_catmas": "000097144", + "titulo_literal": "Desenvolvimento e Manutenção de Sistemas de Informação Utilizando Metodologia Ágil", + "apelido_manual": "Manutenção, integração e automação", + "descricao_tr": "Serviços de Manutenção: (i) Corretiva: correção de defeitos identificados nos sistemas de informação em produção; (ii) Adaptativa: adequação da solução em decorrência de mudanças que não impliquem inserção, alteração ou exclusão de requisitos funcionais; (iii) Perfectiva: mudanças de interface e layout, sem alteração de funcionalidades sob o ponto de vista do usuário; (iv) Evolutiva: mudanças em requisitos funcionais da solução já concluídos, que impliquem inclusão, alteração e/ou exclusão de funcionalidade. Serviços de integrações e automações: automatizar tarefas repetitivas e baseadas em regras e processos de negócio de interesse da Contratante, integrações de softwares e bases de dados e automações de processos para entrada ou saída de informações de maneira automática (coleta de dados, preenchimento de formulários, processamento de transações e interações com sistemas de software, sem intervenção humana). (TR, subitem 1.6.4.3 — descrição do Item 4)", + "pool_ust_total": 15120, + "timebox_h": { "descoberta": 40, "design": 40, "arquitetura": 40, "construcao": 80 }, + "alerta_redacao": "Título CATMAS idêntico ao do Item 3. Na Seção 5, distinga pelo número do item e pela descrição de serviço do subitem 1.6.4.3 do TR." + }, + "I-05": { + "item_tr_numero": 5, + "codigo_catmas": "0000129720", + "titulo_literal": "Serviços Técnicos de Inteligência Artificial (IA) para Desenvolvimento de funcionalidades para Solução / Sistema", + "apelido_manual": "Serviços técnicos de inteligência e produtos analíticos de gestão em saúde", + "descricao_tr": "Serviços de desenvolvimento e/ou apoio de inteligência em saúde que visam criar modelos de inteligência artificial, algoritmos e dashboards que auxiliam na gestão operacional. Incluem a extração de fenótipos de saúde, clusterização de pacientes, apuração de Retorno sobre o Investimento (ROI) relativo a projetos orientados pela SES-MG e identificação de discrepâncias nos processos da Secretaria. São gerados dashboards personalizados, estudos e relatórios para identificar problemas e fornecer soluções eficazes aos gestores da SES-MG, com foco em otimizar a coordenação do acesso e a continuidade do cuidado. Serviços de Gestão e Inteligência em Saúde: suporte ao processo de gestão executado pelas áreas demandantes, baseados na ciência de dados e na inteligência gerada pelas informações produzidas com o uso da solução tecnológica. (TR, subitem 1.6.4.4 — descrição do Item 5)", + "pool_ust_total": 30240, + "timebox_h": { "descoberta": 40, "design": 40, "arquitetura": 40, "construcao": 80 } + } + }, + + "observacoes_tr": { + "fonte": "Anexo I — Termo de Referência do Pregão Eletrônico nº 29/2025 (SEI 127100818), Tabela I e subitens 1.6.4 e 4.1.1.7", + "definicao_ust": "Cada UST (Unidade de Serviço Técnico) representa uma hora de trabalho efetivo; o cálculo baseia-se na alocação proporcional dos membros da equipe, sem fatores multiplicadores (TR 4.1.1.3.5.5).", + "sprints": "Sprints de 2 a 4 semanas, iniciando imediatamente após a conclusão da anterior (TR 4.1.1.3.5.6). Time-box confirmado: 40h nas sprints de Descoberta, Design e Arquitetura; Construção 40h nos Itens 2 e 3 e 80h nos Itens 4 e 5 (TR 4.1.1.7.1).", + "item_1_fora_do_escopo_de_sd": "O Item 1 do TR (Rede de interconectividade — SaaS, CATMAS 000143251) é a licença/assinatura da plataforma, medida em unidades de integração, não em UST. Não gera SD de entregáveis; por isso não há I-01 neste arquivo.", + "inconsistencia_interna_do_tr": "O TR numera os serviços de duas formas: os subitens 1.6.4.x chamam implantação de 'Item 2', treinamento de 'Item 3', manutenção de 'Item 4' e IA de 'Item 5' (coerente com a Tabela I, com a tabela de time-box e com os quantitativos de UST); já a tabela 'Detalhamento dos serviços sob demanda' desloca tudo em -1 (implantação como 'Item 1' etc.). Este arquivo adota a numeração da Tabela I / 1.6.4. Ao citar o TR na SD, referencie sempre o número do item da Tabela I + código CATMAS.", + "restricao_perfis": "O mesmo profissional não pode executar tarefas em duas demandas simultâneas, salvo quando, respeitado o limite de 40 horas semanais, o percentual de alocação não exigir dedicação exclusiva (TR 4.1.1.7.3.5) — base da regra de não sobreposição de entregáveis encadeados.", + "sla": "Indicadores contratuais: IBE (Itens de Backlog Entregues) ≥ 90% e ISP (Satisfação do Dono do Produto) ≥ 80%, com glosas por faixa (TR 5.3.4). Critérios de aceite bem escritos na SD alimentam diretamente o IBE." + }, + + "tipos_entregavel": ["Descoberta", "Design", "Arquitetura", "Construção"], + + "limites": { + "semanas_min": 2, + "semanas_max": 4, + "semanas_tipicas": [2, 3], + "formula_ust": "ust = timebox_h × semanas" + }, + + "dimensionamento_semanas": [ + { "semanas": 2, "quando": "Escopo simples e fechado, poucas dependências" }, + { "semanas": 3, "quando": "Escopo médio, exige reuniões e validação com áreas" }, + { "semanas": 4, "quando": "Escopo complexo, múltiplos stakeholders, muita incerteza" } + ], + + "artefatos_minimos": [ + { "ust_min": 0, "ust_max": 80, "artefatos": 1 }, + { "ust_min": 81, "ust_max": 160, "artefatos": 2 }, + { "ust_min": 161, "ust_max": null, "artefatos": 3 } + ], + + "regras_de_data": { + "encadeados_nao_sobrepoem": "Entregáveis com dependência entre si não podem compartilhar período (mesmos perfis a 200% é achado de auditoria).", + "paralelos_exigem_declaracao": "Entregáveis paralelos podem compartilhar período apenas com times distintos, e o paralelismo deve estar declarado em nota na SD.", + "data_final_habilita_faturamento": "A data final do entregável deve ser coerente com a data dos artefatos que o comprovam.", + "prazo_cabecalho": "O prazo de execução do cabeçalho é a duração de calendário, não a soma das semanas quando há paralelismo." + }, + + "quebra_de_entregaveis": { + "quebrar_quando": [ + "Escopo excede 4 semanas (teto por entregável)", + "Frentes paralelas com times distintos", + "Um pedaço pode ser faturado antes do outro", + "Atividades pertencem a itens diferentes (obrigatório separar)" + ], + "nao_quebrar_quando": [ + "Apenas para 'ter mais entregáveis' — infla o orçamento sem entregar mais", + "O resultado só faz sentido inteiro (metade de um dashboard não é faturável)" + ], + "tipos_nao_obrigatorios": "Nem toda demanda tem os quatro tipos. Tipo inventado para 'ficar completo' é glosa esperando acontecer." + }, + + "checklist": [ + { "id": "ENQ-01", "grupo": "enquadramento", "verificador": "codigo", "regra": "Cada entregável tem item definido (I-02 / I-03 / I-04 / I-05)" }, + { "id": "ENQ-02", "grupo": "enquadramento", "verificador": "julgamento", "regra": "Produto novo está no I-02, não no I-04" }, + { "id": "ENQ-03", "grupo": "enquadramento", "verificador": "julgamento", "regra": "Dashboard, índice ou algoritmo está no I-05, não no I-04" }, + { "id": "ENQ-04", "grupo": "enquadramento", "verificador": "julgamento", "regra": "Automação de tarefa repetitiva está no I-04" }, + { "id": "ENQ-05", "grupo": "enquadramento", "verificador": "julgamento", "regra": "Treinamento pós-implantação está no I-03" }, + { "id": "ENQ-06", "grupo": "enquadramento", "verificador": "codigo", "regra": "Entregáveis de itens diferentes estão em linhas separadas" }, + { "id": "ENQ-07", "grupo": "enquadramento", "verificador": "codigo", "regra": "Há subtotal por item quando a SD cruza itens" }, + + { "id": "CAL-01", "grupo": "calculo", "verificador": "codigo", "regra": "Cada entregável entre 2 e 4 semanas" }, + { "id": "CAL-02", "grupo": "calculo", "verificador": "codigo", "regra": "UST de cada linha = timebox × semanas" }, + { "id": "CAL-03", "grupo": "calculo", "verificador": "codigo", "regra": "Timebox de Construção correto (40h em I-02/I-03, 80h em I-04/I-05)" }, + { "id": "CAL-04", "grupo": "calculo", "verificador": "codigo", "regra": "Soma da coluna UST bate com o cabeçalho" }, + { "id": "CAL-05", "grupo": "calculo", "verificador": "codigo", "regra": "Entregáveis encadeados sem sobreposição de período" }, + { "id": "CAL-06", "grupo": "calculo", "verificador": "codigo", "regra": "Paralelismo, quando existe, está declarado em nota" }, + { "id": "CAL-07", "grupo": "calculo", "verificador": "codigo", "regra": "Prazo do cabeçalho reflete o calendário, não a soma das semanas" }, + + { "id": "DOC-01", "grupo": "documento", "verificador": "julgamento", "regra": "Nome do projeto é o desta demanda, não copiado de outra SD" }, + { "id": "DOC-02", "grupo": "documento", "verificador": "codigo", "regra": "Número do item e título do item correspondem entre si" }, + { "id": "DOC-03", "grupo": "documento", "verificador": "codigo", "regra": "Título do item confere com a redação literal do TR (regras-sd.json)" }, + { "id": "DOC-04", "grupo": "documento", "verificador": "julgamento", "regra": "Números do contexto batem com os artefatos entregues" }, + { "id": "DOC-05", "grupo": "documento", "verificador": "julgamento", "regra": "Produto tem um nome só ao longo de todo o documento" }, + { "id": "DOC-06", "grupo": "documento", "verificador": "julgamento", "regra": "Nenhum campo do documento traz anotação de processo interno fora de lugar" }, + { "id": "DOC-07", "grupo": "documento", "verificador": "codigo", "regra": "Cada entregável tem o mínimo de artefatos comprobatórios da sua faixa de UST" }, + { "id": "DOC-08", "grupo": "documento", "verificador": "usuario", "regra": "Todo artefato citado existe de fato e foi confirmado pelo usuário" }, + { "id": "DOC-09", "grupo": "documento", "verificador": "codigo", "regra": "Data final de cada entregável é coerente com a data dos seus artefatos" }, + { "id": "DOC-10", "grupo": "documento", "verificador": "julgamento", "regra": "Todo critério de aceite é verificável por terceiro" }, + { "id": "DOC-11", "grupo": "documento", "verificador": "julgamento", "regra": "Seção 5 preenchida com aderência específica, não genérica" } + ] +} \ No newline at end of file diff --git a/schema-sd.json b/schema-sd.json new file mode 100644 index 0000000..5cf2dfd --- /dev/null +++ b/schema-sd.json @@ -0,0 +1,181 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "schema-sd.json", + "title": "Solicitação de Demanda (SD) — RMDS / SES-MG", + "$comment": "Contrato de saída: o redator (LLM) devolve a SD SEMPRE neste formato. O app renderiza o documento a partir dele e roda o validador de código sobre ele. Versão 1.0.0.", + "type": "object", + "required": ["cabecalho", "objetivo", "contexto", "entregaveis", "enquadramento_tr", "validacao"], + + "properties": { + + "cabecalho": { + "type": "object", + "required": ["projeto", "numero_sd", "versao", "data_abertura", "periodo_execucao", "itens_tr", "total_ust", "total_ust_extenso", "prazo_execucao_semanas", "quantidade_entregaveis"], + "properties": { + "titulo": { "type": "string", "description": "Título da SD (nome do produto/resultado)" }, + "projeto": { "type": "string", "description": "Nome do projeto real DESTA demanda — campo mais copiado por engano" }, + "numero_sd": { "type": "string", "description": "Sequencial, ex.: SD-26" }, + "versao": { "type": "string", "description": "V1, V2..." }, + "data_abertura": { "type": "string", "format": "date", "description": "DD/MM/AAAA no render; ISO aqui" }, + "periodo_execucao": { + "type": "object", + "required": ["inicio", "fim"], + "properties": { + "inicio": { "type": "string", "format": "date", "description": "Data inicial do primeiro entregável" }, + "fim": { "type": "string", "format": "date", "description": "Data final do último entregável" } + } + }, + "itens_tr": { + "type": "array", + "items": { "enum": ["I-02", "I-03", "I-04", "I-05"] }, + "minItems": 1, + "description": "Todos os itens que a SD cruza" + }, + "total_ust": { "type": "integer", "description": "Soma dos entregáveis — CALCULADO PELO APP, o LLM apenas propõe" }, + "total_ust_extenso": { "type": "string", "description": "Número por extenso" }, + "prazo_execucao_semanas": { "type": "integer", "description": "Duração de CALENDÁRIO, não soma das semanas quando há paralelismo" }, + "quantidade_entregaveis": { "type": "integer" } + } + }, + + "objetivo": { + "type": "string", + "description": "Dois a quatro períodos. O que a SD formaliza e qual resultado permite alcançar. Não repete o contexto." + }, + + "contexto": { + "type": "array", + "minItems": 3, + "maxItems": 5, + "items": { "type": "string" }, + "description": "Parágrafos na ordem: (1) problema de gestão, (2) insumo disponível e por que não basta, (3) o que a demanda entrega, (4) achado/resultado quando houver, (5) desdobramento. Números reais e verificáveis." + }, + + "entregaveis": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["n", "nome", "tipo", "item_tr", "semanas", "timebox_h", "ust", "periodo", "resumo", "atividades", "artefatos", "criterios_aceite"], + "properties": { + "n": { "type": "integer", "description": "Posição no quadro" }, + "nome": { "type": "string", "description": "Nome do artefato final, ex.: 'Painel de fila cirúrgica publicado'" }, + "tipo": { "enum": ["Descoberta", "Design", "Arquitetura", "Construção"] }, + "item_tr": { "enum": ["I-02", "I-03", "I-04", "I-05"] }, + "semanas": { "type": "integer", "minimum": 2, "maximum": 4 }, + "timebox_h": { "enum": [40, 80], "description": "Derivado de regras-sd.json por tipo × item" }, + "ust": { "type": "integer", "description": "timebox_h × semanas — RECALCULADO PELO APP" }, + "periodo": { + "type": "object", + "required": ["inicio", "fim"], + "properties": { + "inicio": { "type": "string", "format": "date" }, + "fim": { "type": "string", "format": "date" } + } + }, + "depende_de": { + "type": "array", + "items": { "type": "integer" }, + "description": "n dos entregáveis dos quais este depende (encadeamento → períodos não podem sobrepor)" + }, + "paralelo_com": { + "type": "array", + "items": { "type": "integer" }, + "description": "n dos entregáveis paralelos (exige nota_paralelismo e times distintos)" + }, + "nota_paralelismo": { "type": "string", "description": "Obrigatória quando paralelo_com não é vazio" }, + "resumo": { "type": "string", "description": "Três linhas: o que o entregável fez e o que produziu" }, + "atividades": { + "type": "array", + "minItems": 6, + "maxItems": 9, + "items": { "type": "string" }, + "description": "Blocos de trabalho concretos, explicáveis em uma frase" + }, + "artefatos": { + "type": "array", + "minItems": 1, + "items": { + "type": "object", + "required": ["nome", "confirmado_pelo_usuario"], + "properties": { + "nome": { "type": "string", "description": "Nome real do documento" }, + "data": { "type": "string", "format": "date", "description": "Quando houver — deve ser <= periodo.fim do entregável" }, + "confirmado_pelo_usuario": { + "type": "boolean", + "description": "TRAVA DE INTEGRIDADE: o LLM sugere TIPOS de artefato; nomes concretos só entram informados/confirmados pelo usuário. false = pendência bloqueante para exportação." + } + } + } + }, + "criterios_aceite": { + "type": "array", + "minItems": 2, + "items": { "type": "string" }, + "description": "Condições verificáveis por terceiro olhando o artefato — nunca 'funcionando adequadamente', 'bem construído', 'documentação completa'" + } + } + } + }, + + "subtotais_por_item": { + "type": "array", + "description": "Obrigatório quando itens_tr tem mais de um item — é assim que se controla saldo de pool. CALCULADO PELO APP.", + "items": { + "type": "object", + "required": ["item_tr", "ust"], + "properties": { + "item_tr": { "enum": ["I-02", "I-03", "I-04", "I-05"] }, + "ust": { "type": "integer" } + } + } + }, + + "enquadramento_tr": { + "type": "array", + "minItems": 1, + "description": "Uma entrada por item cruzado (Seção 5 repetida por item)", + "items": { + "type": "object", + "required": ["item_tr", "titulo_literal", "descricao_item", "aderencia", "entregaveis_n"], + "properties": { + "item_tr": { "enum": ["I-02", "I-03", "I-04", "I-05"] }, + "titulo_literal": { "type": "string", "description": "Exatamente como em regras-sd.json / TR" }, + "descricao_item": { "type": "string", "description": "Transcrição do escopo do item conforme o TR" }, + "aderencia": { + "type": "array", + "minItems": 3, + "maxItems": 5, + "items": { "type": "string" }, + "description": "Um bullet por componente ENTREGUE, no vocabulário do item. Não citar componente que a SD não tem." + }, + "entregaveis_n": { + "type": "array", + "items": { "type": "integer" }, + "description": "Quais entregáveis do quadro pertencem a este item" + } + } + } + }, + + "validacao": { + "type": "array", + "description": "Resultado do checklist (IDs de regras-sd.json). Itens 'codigo' são preenchidos pelo validador do app; itens 'julgamento' pela passada de revisão do LLM; itens 'usuario' pela confirmação explícita.", + "items": { + "type": "object", + "required": ["id", "status"], + "properties": { + "id": { "type": "string", "description": "Ex.: CAL-02" }, + "status": { "enum": ["ok", "alerta", "falha", "pendente"] }, + "observacao": { "type": "string", "description": "Obrigatória quando status != ok" } + } + } + }, + + "pendencias": { + "type": "array", + "items": { "type": "string" }, + "description": "O que ainda precisa de decisão humana antes do envio (ex.: repactuação de prazo, confirmação de artefato)" + } + } +}