Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions .github/workflows/check_code_quality.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,12 @@ jobs:
runs-on: ubuntu-latest
timeout-minutes: 15
services:
redis-tctoken:
image: redis:7.4-alpine
ports: ['16782:6379']
options: >-
--health-cmd "redis-cli ping"
--health-interval 5s --health-timeout 5s --health-retries 10
postgres:
image: postgres:16-alpine
env:
Expand All @@ -28,16 +34,20 @@ jobs:
DATABASE_PROVIDER: postgresql
DATABASE_CONNECTION_URI: postgresql://evolution_test:local_ci_only@127.0.0.1:5432/evolution_test?connection_limit=64
XMACNA_INBOX_INTEGRATION: '1'
XMACNA_TCTOKEN_REDIS_URL: redis://127.0.0.1:16782
steps:
- uses: actions/checkout@v5
- uses: actions/setup-node@v5
with:
node-version: '24'
cache: npm
- run: npm ci
- run: npm run patch:xmacna-tctoken
- run: npm run verify:xmacna-tctoken
- run: npm run db:generate
- run: npm run lint:check
- run: npm run test:xmacna-compat
- run: npm run test:xmacna-tctoken
- run: npm run test:xmacna-inbox
- name: Prepare isolated PostgreSQL schema
run: npx prisma db push --schema prisma/postgresql-schema.prisma --skip-generate
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -44,5 +44,6 @@ lerna-debug.log*
.DS_Store
*.DS_Store
.tool-versions
.local/

/prisma/migrations/*
98 changes: 98 additions & 0 deletions Docker/scripts/patch-baileys-tctoken.cjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
// Apply only to the audited rc14 bytes. No network, process startup or auth-state access.
const fs = require('node:fs');
const path = require('node:path');
const crypto = require('node:crypto');
const { spawnSync } = require('node:child_process');

const MARKER = 'XMACNA_TCTOKEN_ISOLATED_TRANSACTION';
const sha = (text) => crypto.createHash('sha256').update(text).digest('hex');
const EXPECTED = {
'lib/Utils/auth-utils.js': 'd85b07863507235407a3a1430fbb1cfae108c4023f746c50393d9933578caf15',
'lib/Socket/messages-send.js': '81387f00b457ea28b481b1320bfdbf5ada766305f6f2bc7033931f49811519dc',
'lib/Types/Auth.d.ts': '509e25a092ba7b4cd9b7be1f8a1730014ea3745fd02cc14e12d9009e4554958e',
};
function once(text, needle, replacement) {
if (text.split(needle).length !== 2) throw new Error('rc14 anchor missing or ambiguous');
return text.replace(needle, replacement);
}
function transform(file, text) {
if (file.endsWith('auth-utils.js')) {
const start = text.indexOf('export const addTransactionCapability =');
const prefix = text.slice(0, start);
let block = text.slice(start);
block = once(block, ' return {\n get: async (type, ids) => {',
' const transactionalStore = {\n get: async (type, ids) => {');
block = once(block, ' isInTransaction,',
` // ${MARKER}: response persistence must not reuse a finished send context.\n` +
' transactionIsolated: (work, key) => txStorage.exit(() => transactionalStore.transaction(work, key)),\n' +
' isInTransaction,');
block = once(block, ' };\n};\n/**\n * Returns the authenticated user',
' };\n return transactionalStore;\n};\n/**\n * Returns the authenticated user');
return prefix + block;
}
if (file.endsWith('messages-send.js')) {
text = once(text,
' .then(async (result) => {\n await storeTcTokensFromIqResult({',
` // ${MARKER}: IQ stays detached; only its response takes a new transaction.\n` +
' .then((result) => authState.keys.transactionIsolated(async () => {\n' +
' await storeTcTokensFromIqResult({');
return once(text,
' ...indexWrite\n }\n });\n })\n .catch(err => {',
" ...indexWrite\n }\n });\n }, meId))\n .catch(err => {");
}
return once(text,
' transaction<T>(exec: () => Promise<T>, key: string): Promise<T>;',
' transaction<T>(exec: () => Promise<T>, key: string): Promise<T>;\n' +
` /** ${MARKER}: starts a fresh transaction outside this store's inherited context. */\n` +
' transactionIsolated<T>(exec: () => Promise<T>, key: string): Promise<T>;');
}

function apply(root = path.resolve(__dirname, '../../node_modules/baileys'), { check = false } = {}) {
const version = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).version;
if (version !== '7.0.0-rc14') throw new Error(`Unsupported Baileys ${version}; review the patch before upgrading`);
const edits = [];
for (const [file, digest] of Object.entries(EXPECTED)) {
const target = path.join(root, file);
const current = fs.readFileSync(target, 'utf8');
let baseline = current;
if (current.includes(MARKER)) {
// Reconstruct original bytes and compare the full patched file; a marker alone is never enough.
if (file.endsWith('auth-utils.js')) {
baseline = once(baseline, ' const transactionalStore = {\n get: async (type, ids) => {', ' return {\n get: async (type, ids) => {');
baseline = once(baseline,
` // ${MARKER}: response persistence must not reuse a finished send context.\n` +
' transactionIsolated: (work, key) => txStorage.exit(() => transactionalStore.transaction(work, key)),\n', '');
baseline = once(baseline, ' };\n return transactionalStore;\n};\n/**\n * Returns the authenticated user', ' };\n};\n/**\n * Returns the authenticated user');
} else if (file.endsWith('messages-send.js')) {
baseline = once(baseline,
` // ${MARKER}: IQ stays detached; only its response takes a new transaction.\n` +
' .then((result) => authState.keys.transactionIsolated(async () => {\n',
' .then(async (result) => {\n');
baseline = once(baseline, " }, meId))\n .catch(err => {", ' })\n .catch(err => {');
} else {
baseline = once(baseline,
`\n /** ${MARKER}: starts a fresh transaction outside this store's inherited context. */\n` +
' transactionIsolated<T>(exec: () => Promise<T>, key: string): Promise<T>;', '');
}
}
if (sha(baseline) !== digest) throw new Error(`Unexpected bytes in ${file}; refusing a partial or stale patch`);
const patched = transform(file, baseline);
if (current !== baseline && current !== patched) throw new Error(`Invalid patched content in ${file}`);
if (file.endsWith('.js')) {
const parsed = spawnSync(process.execPath, ['--input-type=module', '--check'], {
input: patched, encoding: 'utf8', windowsHide: true,
});
if (parsed.status !== 0) throw new Error(`Patched syntax failed in ${file}`);
}
if (check && current !== patched) throw new Error(`Patch not installed in ${file}`);
edits.push({ target, patched, changed: current !== patched });
}
// Validate every file before writing any: build failure cannot silently retain only one half.
if (!check) for (const edit of edits) if (edit.changed) fs.writeFileSync(edit.target, edit.patched);
return { files: edits.length, changed: check ? 0 : edits.filter((edit) => edit.changed).length };
}
if (require.main === module) {
try { console.log(JSON.stringify(apply(undefined, { check: process.argv.includes('--check') }))); }
catch (error) { console.error(error.message); process.exitCode = 1; }
}
module.exports = { apply, MARKER };
2 changes: 2 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,10 @@ WORKDIR /evolution
COPY ./package*.json ./
COPY ./tsconfig.json ./
COPY ./tsup.config.ts ./
COPY ./Docker/scripts/patch-baileys-tctoken.cjs ./Docker/scripts/patch-baileys-tctoken.cjs

RUN npm ci --silent
RUN node ./Docker/scripts/patch-baileys-tctoken.cjs

COPY ./src ./src
COPY ./public ./public
Expand Down
46 changes: 46 additions & 0 deletions HANDOFF.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# Handoff — Evolution / TC token

CHECKPOINT_REBOOT — ordem do Lucca retransmitida por Vesper em 01/11/2026, prompt 13:58Z. Dono: wk-evolution-versao-sol; responsável: Vesper/Argos. Não fechar o próprio pane; o reboot encerra a sessão. Sem mensagem externa.

Trabalho a retomar depois do reboot: **SIM, condicionado a revisão/novo evento e às autorizações abaixo**. Não há execução local pela metade nem tarefa para reiniciar automaticamente. Consulta de processos Windows desta frente não encontrou render, servidor ou navegador de teste pelos caminhos do checkout, testes TC token ou porta 16782. Redis temporário já foi encerrado no ensaio anterior. Nenhum processo de outra frente foi parado.

## Checkout e retomada

- cwd: `C:/Users/lucca/evolution-tctoken-1026`.
- Repositório: `xmacna/evolution-api`.
- Branch: `fix/tctoken-persistence-1026-20261029`, upstream `origin/fix/tctoken-persistence-1026-20261029`.
- HEAD conferido antes deste checkpoint documental: `6770e3bff0278a0f3edafeee1baabcaabb5a3e69`. O candidato testado permanece o checkpoint `998a75dd3b9c8db05bbcf19d65680f51f54d1889`; os commits posteriores são documentação.
- HEAD final pré-reboot: consultar `git rev-parse HEAD`; o valor exato depois do commit e push está na ENTREGA cujo texto começa com CHECKPOINT_REBOOT em `C:/Users/lucca/argos/inbox/general2.ndjson`. O hash do próprio commit não pode ser embutido nele.
- ID real da sessão Codex, conferido em CODEX_SESSION_ID e CODEX_THREAD_ID: `01a18995-3615-7b53-a519-7dbb059f940e`.

```powershell
Set-Location C:/Users/lucca/evolution-tctoken-1026
codex resume 01a18995-3615-7b53-a519-7dbb059f940e
```

## Pronto e publicado

Correção focal de persistência TC token publicada na branch e no [PR 7, aberto em draft](https://github.com/xmacna/evolution-api/pull/7). O IQ continua desacoplado; a resposta tardia grava em transação nova, fora do contexto encerrado, com o mutex do mesmo proprietário. Instalador recusa versão ou bytes inesperados; não altera schema. Código e retorno documentados em `docs/tctoken-persistence-1026.md`.

Validação retida: rc14 original falha em quatro dos sete casos; candidato passa dez testes específicos, 14 de compatibilidade e TypeScript. Cache/providers reais e Redis local foram exercitados; fixtures limitadas a IQ e repositório inicial de credenciais. Os quatro checks GitHub do HEAD acima estão concluídos com sucesso: Check Code Quality, CodeQL Analysis, Dependency Review e CodeQL. Não repetir testes somente para o fechamento documental.

Investigação e comentários das issues 1025/1026 entregues. Atribuição histórica de logouts continua sem prova de autor/rotina ou chamada HTTP; a mensagem de aplicação aparece em quatro caminhos e a retenção não cobre os segundos solicitados. Provas privadas em `C:/Users/lucca/argos/.local/evolution-versao-20261029/seguimento-1026/`; mapa e diagnóstico em sua pasta pai. Nenhum deploy ou canário foi executado por este worker. Redis temporário foi encerrado; não há monitor ou job ativo mantido por esta frente.

## Imagem pronta e suspensa — frente relacionada

O registro da imagem ARM64 pertence ao ger-disparo-ativo-sol, em `C:/Users/lucca/elysium-worktrees/disparo-ativo-20261029/.local/disparo-ativo/EVOLUTION-PRONTO-30.md`, atualizado em 30/10 09:52 UTC. Fonte: [PR Meta 8](https://github.com/xmacna/evolution-api/pull/8). O registro declara build e smoke prontos, publicação suspensa, nenhum push da imagem ou update Evolution e nenhum canário enviado. Portanto, o código está publicado em branches/PRs; a publicação da imagem não está comprovada por esse registro.

Referência da imagem local: `evolution-meta-receipts-1030-31716cf`, Linux/ARM64, identidade `sha256:acc9a3f0cb717f0411f9199ab845a969109aefa868e0c5944a63e43045371c6f`. Não presumir que o candidato de TC token do PR 7 esteja incluído na imagem do PR 8: conferir a composição no responsável antes de qualquer release dessa correção.

Rollback dessa frente relacionado à baseline `evolution-api-candidate@sha256:4aaa5d80bfb46b66e786fe1691d1766878e02fb1739a8164fe83c31eb4f2b541`: ensaio offline de preparo, aplicação e retorno registrado em `rollback-exercise-30.log`, na mesma pasta. Não foi rollback real. O estado de cutover real ainda depende de autorização e preparo; usar o plano canônico do responsável, sem executar comandos live deste handoff.

## O que falta e condição de retomada

- **AGUARDA Lucca:** OK escrito para publicar/deployar. Vesper confirma o aviso prévio ao Rafael e a janela antes de qualquer mudança. O pedido de fechar esta sessão não autoriza deploy.
- **AGUARDA revisão:** avaliar PR 7 e sua inclusão no candidato correto; preservar patches e requisitos de ARM64, segurança e retorno. Imagem Meta e correção TC token são entregas distintas.
- **Canário pendente:** somente após autorização específica e sob o plano do responsável. Na frente Meta há previsão de um único envio interno com prova DELIVERED/READ; nenhum canário foi feito. Persistência offline não prova entrega nem causa/solução de banimento.
- **Rollback ensaiado offline:** evidência existe na frente da imagem. Antes do cutover autorizado, atualizar snapshot, confirmar digest anterior e preparar estado de avanço/retorno. Não declarar ensaio em produção ou retorno pronto no runtime sem essas etapas.
- **Vínculos, até segunda ordem:** nenhum agente pareia, desconecta, faz logout ou lê QR por script/Manager. Pareamento apenas por uma pessoa pelo Partenon, com Vesper avisado antes. A regra também vale para o canário. Campanhas em lista ficam com ger-disparo-ativo-sol.
- **Histórico administrativo:** Argos/Rafael podem indicar fonte adicional dos segundos de 25/09 16:32 e 20/10 14:12. Retomar por evidência nova, sem polling.

Este worker para no checkpoint documental. Não há ação de produção autorizada na retomada automática. Vesper lê a ENTREGA; o reboot fecha o pane, sem fechamento próprio.
31 changes: 31 additions & 0 deletions PLANO.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# TC token e logouts — issue elysium 1026

CHECKPOINT_REBOOT, ordem do Lucca retransmitida por Vesper em 01/11, prompt 13:58Z: parar com segurança o que for próprio, commit por caminho/push na branch, atualizar HANDOFF.md e registrar ENTREGA CHECKPOINT_REBOOT com caminho e retomada SIM/NAO. Nenhum processo próprio em execução foi encontrado na consulta Windows; nada ficou no meio. Retomada SIM, condicionada às pendências/autorizações do HANDOFF.md, sem reinício automático de tarefas. O reboot fecha o pane; não fechar nem enviar mensagem externa.

Fechamento solicitado por Vesper em 01/11 14:00Z para liberar memória: preservar git limpo/push, HANDOFF.md com cwd/branch/HEAD/sessão/retomada e pendências, e uma ENTREGA no general2 com caminho e HEAD. Não fechar o próprio pane. Handoff canônico de saída: `HANDOFF.md` na raiz deste checkout; imagem relacionada permanece sob ger-disparo-ativo-sol, sem presumir inclusão deste PR nela. Deploy aguarda OK escrito do Lucca e aviso ao Rafael; canário pendente, rollback da imagem ensaiado apenas offline.

Regra vigente de Argos, 29/10 às 22:41Z, até segunda ordem: nenhum agente pareia, desconecta, faz logout ou lê QR de instância por script ou pelo Manager. Pareamento somente por uma pessoa, exclusivamente pelo Partenon, com Vesper avisado antes. Qualquer canário futuro também deve obedecer; os ensaios desta frente são offline, sem conta WhatsApp. Pausa de campanhas em lista é responsabilidade do ger-disparo-ativo-sol; não operar campanhas aqui.

Dono: wk-evolution-versao-sol. Responsável: Vesper/Argos.
Posse: branch fix/tctoken-persistence-1026-20261029 neste checkout isolado; evidências locais em C:/Users/lucca/argos/.local/evolution-versao-20261029/seguimento-1026.
Origem: xmacna/evolution-api, main de 23/10; não é afirmação de que main inteiro esteja implantado.

Ordem de Argos, 29/10 às 21:58Z: “(1) siga com a atribuição dos logouts de 25/09 16:32 e 20/10 14:12, só leitura, pelos logs de acesso e auditoria retidos: quem ou qual rotina chamou /instance/logout; (2) siga com o teste de regressão offline e a correção focal da persistência (store e cache reais, token frio, resposta tardia, recriar o store), sem deploy e sem tocar no serviço. Resultados comentados na #1026 e ENTREGA no general2. O canário com número só vem depois, com a minha decisão.”

- [x] Investigar atribuição dos logouts e registrar limite preciso: autor/rotina e chamada HTTP não comprovados.
- [x] Reproduzir falha com módulos rc14 reais, cache real, armazenamento persistente e store recriado.
- [x] Implementar correção focal sem aguardar IQ no mutex de envio; testar concorrência, isolamento e falhas.
- [x] Documentar instalação/rollback do patch sem deploy; publicação da branch na entrega.
- [x] Comentar resultado sem dados de cliente na issue1026 e registrar ENTREGA no general2.

Critério de pronto: regressão falha no rc14 original e passa com correção; token e índice sobrevivem ao cache/store recriados; callback usa transação nova; erros de IQ liberam dedup; código revisto e checks proporcionais. Nenhum deploy, serviço alterado ou canário.

Verificação de logs: o mesmo texto de logout existe em quatro famílias de caminhos; não atribuir a chamada HTTP por esse texto. AWS em operações de leitura; sem send-command. Logs remotos recentes e metadados de retenção já coletados são reaproveitados.

AGUARDA: Argos decidir canário somente depois da entrega; não executar nesta missão. Eventual pareamento cabe exclusivamente a uma pessoa pelo Partenon, com Vesper avisado antes; nenhuma operação de vínculo por agente.

Prova: rc14 original falha em quatro dos sete casos; candidato passa dez testes específicos, sem skips, mais 14 de compatibilidade e tsc. Redis 7.4.12 local encerrado após o ensaio. As quatro implementações de provider/cache testadas são iguais às fontes da imagem principal implantada. Evidências, relatório e limites na pasta local citada acima; documentação do patch em docs/tctoken-persistence-1026.md.

AGUARDA: Argos/Rafael indicar eventual access/audit log histórico dos segundos solicitados, se existir fora das fontes examinadas. Não houve fonte suficiente para identificar o chamador; quatro famílias de código podem emitir a mesma mensagem de logout.

Candidato em draft: https://github.com/xmacna/evolution-api/pull/7 . Resultado publicado e readback conferido: https://github.com/xmacna/elysium/issues/1026#issuecomment-6509599968 . ENTREGA registrada no general2, com recibos ISSUE-READBACK.json e ENTREGA-GENERAL2.json na pasta local da evidência. Checks locais aprovados; os quatro checks GitHub do candidato também estão concluídos com sucesso, conferidos no fechamento de 01/11. Atualização documental de fechamento será enviada na mesma branch; nenhum build/teste adicional solicitado.
Loading
Loading