@orkastery/cli 0.4.2 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapters/README.md +11 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +3 -1
- package/adapters/claude-code/agents/ork-check.md +1 -1
- package/adapters/claude-code/agents/ork-go.md +2 -2
- package/adapters/claude-code/agents/ork-goal.md +1 -1
- package/adapters/claude-code/agents/ork-master.md +1 -1
- package/adapters/claude-code/agents/ork-plan.md +1 -1
- package/adapters/claude-code/agents/ork-ship.md +1 -1
- package/adapters/claude-code/commands/master.md +11 -8
- package/adapters/claude-code/commands/ork.md +8 -0
- package/adapters/claude-code/commands/ship.md +2 -2
- package/adapters/codex/skills/ork/SKILL.md +12 -1
- package/adapters/hermes/README.md +3 -0
- package/adapters/hermes/bin/ork-abrir-thread.sh +17 -2
- package/adapters/hermes/bin/ork-brain.sh +3 -0
- package/adapters/hermes/bin/ork-maestro.sh +4 -1
- package/adapters/hermes/bin/ork-network-roadmap.sh +11 -0
- package/adapters/hermes/bin/ork-roadmap-status.sh +9 -0
- package/adapters/hermes/hermes.plugin.json +2 -0
- package/adapters/hermes/hitl-ingress/__init__.py +36 -3
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +19 -20
- package/adapters/openclaw/README.md +34 -3
- package/adapters/openclaw/dist/hitl-ingress.js +37 -3
- package/adapters/openclaw/dist/index.js +92 -39
- package/adapters/openclaw/openclaw.plugin.json +10 -3
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +32 -3
- package/adapters/openclaw/src/index.ts +108 -41
- package/assets/docs/padroes/documentacao-de-produto.md +2 -2
- package/assets/docs/padroes/roadmap-de-produto.md +1 -1
- package/assets/docs/produto/_modelo-feature.md +2 -0
- package/assets/orkmind_bridge.py +214 -1
- package/dist/adapters/claude-bg.js +88 -9
- package/dist/adapters/codex-events.js +3 -1
- package/dist/board.js +74 -0
- package/dist/branch-de-estado.js +5 -4
- package/dist/busca-semantica.js +161 -0
- package/dist/ci.js +141 -10
- package/dist/company-brain-cli.js +27 -2
- package/dist/company-brain-context.js +132 -0
- package/dist/company-brain-dossie.js +376 -0
- package/dist/company-brain-mcp.js +4 -1
- package/dist/conducao.js +117 -31
- package/dist/decisao-autonoma.js +42 -0
- package/dist/docs.js +64 -22
- package/dist/doctor.js +33 -0
- package/dist/entrega-pr.js +109 -5
- package/dist/experiencia-instalacao.js +256 -0
- package/dist/experiencia.js +72 -0
- package/dist/fabrica-estado.js +43 -10
- package/dist/fechamento.js +105 -0
- package/dist/fix.js +1 -1
- package/dist/forja.js +435 -0
- package/dist/gates.js +10 -1
- package/dist/hitl-classificacao.js +11 -1
- package/dist/hitl-contract.js +50 -7
- package/dist/hitl-curto.js +158 -0
- package/dist/hitl-gates.js +67 -1
- package/dist/hitl-lote.js +30 -15
- package/dist/hitl-presentation.js +10 -7
- package/dist/hitl-resumo.js +33 -12
- package/dist/hitl-texto-livre.js +150 -0
- package/dist/hosts.js +86 -1
- package/dist/index.js +605 -41
- package/dist/indice-vetorial.js +317 -0
- package/dist/init.js +13 -0
- package/dist/integracoes-locais.js +1 -1
- package/dist/intelligence-benchmark-contract.js +570 -0
- package/dist/intelligence-graph-cli.js +430 -0
- package/dist/intelligence-graph-contract.js +577 -0
- package/dist/intelligence-graph-extract-md.js +469 -0
- package/dist/intelligence-graph-extract-ts.js +810 -0
- package/dist/intelligence-graph-extract.js +369 -0
- package/dist/intelligence-graph-index.js +512 -0
- package/dist/intelligence-graph-parsers.js +327 -0
- package/dist/intelligence-graph-query.js +344 -0
- package/dist/intelligence-graph-repo.js +187 -0
- package/dist/leases.js +84 -0
- package/dist/ledger.js +14 -0
- package/dist/maestro-cli.js +10 -1
- package/dist/maestro-contract.js +5 -1
- package/dist/maestro-discovery.js +16 -2
- package/dist/maestro-snapshot.js +6 -2
- package/dist/manifest.js +142 -5
- package/dist/master-batch.js +3 -2
- package/dist/master-digest.js +2 -2
- package/dist/master-nota.js +203 -0
- package/dist/master.js +4 -0
- package/dist/mcp-experiencia.js +164 -0
- package/dist/mcp-git.js +36 -10
- package/dist/mcp-install.js +3 -1
- package/dist/mcp-maestro.js +3 -2
- package/dist/mcp-server.js +84 -5
- package/dist/memoria.js +147 -1
- package/dist/network-roadmap.js +907 -0
- package/dist/ocupacao.js +38 -2
- package/dist/onboarding.js +113 -7
- package/dist/orkmind.js +165 -9
- package/dist/phase.js +166 -13
- package/dist/policies.js +2 -0
- package/dist/projeto-alvo.js +551 -0
- package/dist/prompts.js +1 -1
- package/dist/pulse-consentimento.js +4 -2
- package/dist/pulse-delivery.js +5 -0
- package/dist/pulse-resposta.js +674 -49
- package/dist/pulse.js +21 -0
- package/dist/ratelimit.js +3 -2
- package/dist/retry.js +230 -9
- package/dist/roadmap-reservas.js +262 -20
- package/dist/roadmap-status.js +213 -0
- package/dist/runtime-ambiente.js +12 -1
- package/dist/session-watcher-claude.js +26 -0
- package/dist/sessoes.js +49 -15
- package/dist/thread-close.js +3 -0
- package/dist/verify.js +5 -4
- package/dist/worktree.js +77 -0
- package/eval/casos/orchestration-experience-pt-br.json +48 -0
- package/eval/casos/orchestration-experience.json +48 -0
- package/package.json +1 -1
- package/schemas/code-artifact-graph.v1.schema.json +581 -0
- package/schemas/graph-benchmark.v1.schema.json +2240 -0
- package/schemas/maestro-snapshot.schema.json +23 -0
- package/skills/README.md +7 -5
- package/skills/core/onboarding/SKILL.md +7 -0
- package/skills/core/orchestration-experience/SKILL.md +113 -0
- package/skills/core/orchestration-experience-pt-br/SKILL.md +112 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/go-implementation/SKILL.md +2 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.TEXTO_DO_ATO = exports.TETO_DE_LINHAS_DO_CORPO = exports.TETO_DE_LINHAS_DO_PEDIDO = exports.CONTRATO_PEDIDO_CURTO = void 0;
|
|
4
|
+
exports.entradaDoPedido = entradaDoPedido;
|
|
5
|
+
exports.montarPedidoCurto = montarPedidoCurto;
|
|
6
|
+
exports.linhaDeResposta = linhaDeResposta;
|
|
7
|
+
exports.textoDoPedidoCurto = textoDoPedidoCurto;
|
|
8
|
+
exports.desdeDoPedido = desdeDoPedido;
|
|
9
|
+
/**
|
|
10
|
+
* RM-048 (itens 1, 4 e 5): o contrato UNICO de apresentacao de um pedido ao dono.
|
|
11
|
+
*
|
|
12
|
+
* Ate aqui o mesmo pedido saia com tres textos diferentes: o dialogo do host (`apresentarDecisao`),
|
|
13
|
+
* o lote do Telegram (`textoDoLote`) e o item do pulse, que levava um comando com o identificador
|
|
14
|
+
* longo. Cada um gastava de tres a quatro linhas por alternativa, e o dono, que conduz dezenas de
|
|
15
|
+
* frentes ao mesmo tempo, tinha de reler a mensagem para achar a pergunta. O pedido do dono foi
|
|
16
|
+
* este: pergunta curta, alternativas de uma linha, sempre uma recomendada, e uma linha que diz
|
|
17
|
+
* como responder.
|
|
18
|
+
*
|
|
19
|
+
* O contrato tem sete partes e um teto, e o teto e garantido por construcao, nao por revisao:
|
|
20
|
+
*
|
|
21
|
+
* titulo de onde vem (thread e fase), em uma linha;
|
|
22
|
+
* pergunta o que se decide, em uma frase;
|
|
23
|
+
* trava o que esta parado, desde quando, e o que acontece sem resposta;
|
|
24
|
+
* sem volta so quando responder autoriza um ato da lista congelada;
|
|
25
|
+
* corpo no maximo `TETO_DE_LINHAS_DO_CORPO` linhas de evidencia;
|
|
26
|
+
* alternativas de 2 a 4, uma linha cada, com a consequencia, e UMA recomendada com o porque;
|
|
27
|
+
* responder a ultima linha, dizendo exatamente o que digitar.
|
|
28
|
+
*
|
|
29
|
+
* O detalhe profundo (artefato, claims, riscos, diff) fica a um pedido de distancia: a ultima
|
|
30
|
+
* linha diz como pedir, e o nucleo responde com o bloco de evidencia (`<codigo> detalhes`).
|
|
31
|
+
*
|
|
32
|
+
* Telegram e terminal carregam o MESMO conteudo; muda o marcador (icone no Telegram, texto no
|
|
33
|
+
* terminal). OpenClaw transporta o texto do Telegram. Quem monta e o nucleo; o host so transporta.
|
|
34
|
+
*/
|
|
35
|
+
const hitl_1 = require("./hitl");
|
|
36
|
+
const hitl_contract_1 = require("./hitl-contract");
|
|
37
|
+
const horario_1 = require("./horario");
|
|
38
|
+
exports.CONTRATO_PEDIDO_CURTO = 'ork.hitl-curto/v1';
|
|
39
|
+
/** Nenhum pedido passa disto por padrao (RM-048, criterio c). */
|
|
40
|
+
exports.TETO_DE_LINHAS_DO_PEDIDO = 15;
|
|
41
|
+
/** Evidencia que cabe na pergunta curta; o resto vai no detalhe. */
|
|
42
|
+
exports.TETO_DE_LINHAS_DO_CORPO = 2;
|
|
43
|
+
/** Teto de uma linha logica: alternativa com consequencia ainda e leitura rapida. */
|
|
44
|
+
const TETO_DA_LINHA = 200;
|
|
45
|
+
/** O texto de cada ato congelado, como o dono o le. */
|
|
46
|
+
exports.TEXTO_DO_ATO = Object.freeze({
|
|
47
|
+
dinheiro: 'gasta dinheiro',
|
|
48
|
+
'publicacao-externa': 'publica fora do projeto',
|
|
49
|
+
'apagar-dado': 'apaga dado',
|
|
50
|
+
'push-base-protegida': 'faz push na base protegida',
|
|
51
|
+
});
|
|
52
|
+
const linha = (texto, teto = TETO_DA_LINHA) => {
|
|
53
|
+
const limpo = (0, hitl_1.redigirSegredos)(String(texto ?? '')).replace(/\s+/g, ' ').trim();
|
|
54
|
+
return limpo.length > teto ? `${limpo.slice(0, teto - 3)}...` : limpo;
|
|
55
|
+
};
|
|
56
|
+
/** A entrada de um pedido v2 `pergunta`. Um v1 chega pelo lote, que ja o traduziu. */
|
|
57
|
+
function entradaDoPedido(pedido, desde) {
|
|
58
|
+
(0, hitl_contract_1.validarPedidoHitl)(pedido);
|
|
59
|
+
if (!(0, hitl_contract_1.ehV2)(pedido) || pedido.classe !== 'pergunta') {
|
|
60
|
+
throw new Error('pedido curto: só uma pergunta ork.hitl/v2 tem alternativas declaradas');
|
|
61
|
+
}
|
|
62
|
+
const alvo = (0, hitl_contract_1.alvoDoPedido)(pedido);
|
|
63
|
+
return {
|
|
64
|
+
thread: pedido.thread, fase: pedido.fase, pergunta: (0, hitl_contract_1.textoDoPedido)(pedido),
|
|
65
|
+
alternativas: pedido.alternativas.map(a => ({ chave: a.letra, texto: a.texto, consequencia: a.consequencia,
|
|
66
|
+
...(a.recomendada ? { recomendada: { porque: a.porque ?? '' } } : {}) })),
|
|
67
|
+
alvo: alvo?.tipo === 'session' ? { tipo: 'session', sessionId: alvo.sessionId } : { tipo: 'gate' },
|
|
68
|
+
corpo: pedido.corpo, ...(pedido.ato ? { ato: pedido.ato } : {}),
|
|
69
|
+
expiracao: (0, hitl_contract_1.expiracaoDoPedido)(pedido), prazo: (0, hitl_contract_1.prazoDoPedido)(pedido) ?? null, desde: desde ?? null,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
/** O que acontece sem resposta, em meia linha. Expirar nunca autoriza. */
|
|
73
|
+
function semResposta(e, agora) {
|
|
74
|
+
const ate = e.prazo ? ` até ${(0, horario_1.formatarHora)(e.prazo, { agora })}` : '';
|
|
75
|
+
if (e.expiracao === 'seguir-recomendada')
|
|
76
|
+
return `Sem resposta${ate}, sigo com a recomendada.`;
|
|
77
|
+
if (e.expiracao === 'escalar')
|
|
78
|
+
return `Sem resposta${ate}, o pedido é escalado.`;
|
|
79
|
+
return e.alvo?.tipo === 'session' ? 'Sem resposta, a sessão continua parada.' : 'Sem resposta, nada avança.';
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Monta o pedido curto. Recusa, em vez de cortar em silencio, o que o contrato nao aceita: menos
|
|
83
|
+
* de duas alternativas, mais de quatro, ou recomendada diferente de exatamente uma.
|
|
84
|
+
*/
|
|
85
|
+
function montarPedidoCurto(e, opcoes) {
|
|
86
|
+
if (e.alternativas.length < 2 || e.alternativas.length > 4)
|
|
87
|
+
throw new Error('pedido curto: de 2 a 4 alternativas');
|
|
88
|
+
if (e.alternativas.filter(a => a.recomendada).length !== 1)
|
|
89
|
+
throw new Error('pedido curto: exatamente uma alternativa é recomendada');
|
|
90
|
+
const onde = e.alvo?.tipo === 'session'
|
|
91
|
+
? `A sessão ${e.alvo.sessionId.slice(0, 8)} está parada esperando você`
|
|
92
|
+
: `${e.fase ? `A fase ${e.fase}` : 'A thread'} está parada esperando você`;
|
|
93
|
+
const desde = e.desde ? ` desde ${(0, horario_1.formatarDesde)(e.desde, { agora: opcoes.quando })}` : '';
|
|
94
|
+
return {
|
|
95
|
+
contrato: exports.CONTRATO_PEDIDO_CURTO,
|
|
96
|
+
titulo: linha([e.thread, e.fase].filter(Boolean).join(' · ') || 'sem thread'),
|
|
97
|
+
pergunta: linha(e.pergunta),
|
|
98
|
+
trava: linha(`${onde}${desde}. ${semResposta(e, opcoes.quando)}`),
|
|
99
|
+
...(e.ato ? { semVolta: `Sem volta depois de feito: ${exports.TEXTO_DO_ATO[e.ato]}.` } : {}),
|
|
100
|
+
corpo: (e.corpo ?? []).slice(0, exports.TETO_DE_LINHAS_DO_CORPO).map(l => linha(l)).filter(Boolean),
|
|
101
|
+
alternativas: e.alternativas.map(a => ({ chave: a.chave, texto: linha(a.texto, 140), consequencia: linha(a.consequencia, 140),
|
|
102
|
+
...(a.recomendada ? { recomendada: { porque: linha(a.recomendada.porque, 140) } } : {}) })),
|
|
103
|
+
responder: opcoes.responder,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/** A ultima linha: exatamente o que digitar, sem identificador longo. */
|
|
107
|
+
function linhaDeResposta(p, canal) {
|
|
108
|
+
const r = p.responder, recomendada = p.alternativas.find(a => a.recomendada).chave;
|
|
109
|
+
if (r.tipo === 'numero')
|
|
110
|
+
return null;
|
|
111
|
+
if (r.tipo === 'texto')
|
|
112
|
+
return `${canal === 'telegram' ? '↩️ ' : ''}Responda escrevendo o texto; ele é enviado como está.`;
|
|
113
|
+
if (r.tipo === 'dialogo')
|
|
114
|
+
return `${canal === 'telegram' ? '↩️ ' : ''}Responda selecionando a opção no diálogo; digitar a letra também vale.`;
|
|
115
|
+
return canal === 'telegram'
|
|
116
|
+
? `↩️ Responda: ${r.codigo} ${recomendada} (ou outra letra). Evidências: ${r.codigo} detalhes.`
|
|
117
|
+
: `Responda pelo Telegram: ${r.codigo} ${recomendada} (ou outra letra); aqui, pelo diálogo do host. Evidências: ${r.codigo} detalhes.`;
|
|
118
|
+
}
|
|
119
|
+
/**
|
|
120
|
+
* O texto do pedido no canal. Telegram e terminal dizem a mesma coisa com marcadores diferentes;
|
|
121
|
+
* nenhum dos dois passa de `TETO_DE_LINHAS_DO_PEDIDO` linhas.
|
|
122
|
+
*/
|
|
123
|
+
function textoDoPedidoCurto(p, canal) {
|
|
124
|
+
if (p.contrato !== exports.CONTRATO_PEDIDO_CURTO)
|
|
125
|
+
throw new Error('pedido curto: contrato inválido');
|
|
126
|
+
const tg = canal === 'telegram';
|
|
127
|
+
const numero = p.responder.tipo === 'numero' ? `${p.responder.numero}. ` : '';
|
|
128
|
+
const linhas = [
|
|
129
|
+
`${tg && !numero ? '🔔 ' : ''}${numero}${tg || numero ? '' : 'Orkastery · '}${p.titulo}`,
|
|
130
|
+
`${tg ? '❓ ' : 'Pergunta: '}${p.pergunta}`,
|
|
131
|
+
`${tg ? '⏳ ' : 'Situação: '}${p.trava}`,
|
|
132
|
+
...(p.semVolta ? [`${tg ? '🔒 ' : '! '}${p.semVolta}`] : []),
|
|
133
|
+
...p.corpo.map(l => `${tg ? '• ' : '- '}${l}`),
|
|
134
|
+
...p.alternativas.flatMap(a => [
|
|
135
|
+
`${a.chave}) ${a.texto}${a.recomendada ? (tg ? ' ✅ recomendada' : ' [recomendada]') : ''}: ${a.consequencia}`,
|
|
136
|
+
...(a.recomendada ? [` porquê: ${a.recomendada.porque}`] : []),
|
|
137
|
+
]),
|
|
138
|
+
];
|
|
139
|
+
const resposta = linhaDeResposta(p, canal);
|
|
140
|
+
if (resposta)
|
|
141
|
+
linhas.push(resposta);
|
|
142
|
+
if (linhas.length > exports.TETO_DE_LINHAS_DO_PEDIDO)
|
|
143
|
+
throw new Error('pedido curto: acima do teto de linhas');
|
|
144
|
+
return linhas.join('\n');
|
|
145
|
+
}
|
|
146
|
+
/**
|
|
147
|
+
* Desde quando o dono e esperado nesta pergunta: o primeiro pedido do ledger com o mesmo codigo
|
|
148
|
+
* no mesmo contexto. Com o codigo estavel (D4), renovar o pedido nao zera o relogio que o dono ve.
|
|
149
|
+
*/
|
|
150
|
+
function desdeDoPedido(eventos, pedido) {
|
|
151
|
+
const codigo = pedido.codigo;
|
|
152
|
+
const doPedido = eventos.find(e => e.tipo === 'hitl_requested' && e.pedido?.id === pedido.id);
|
|
153
|
+
if (typeof codigo !== 'string' || !doPedido)
|
|
154
|
+
return pedido.criadoEm;
|
|
155
|
+
const primeiro = eventos.find(e => e.tipo === 'hitl_requested' && e.contexto === doPedido.contexto &&
|
|
156
|
+
e.pedido?.codigo === codigo);
|
|
157
|
+
return primeiro?.pedido?.criadoEm ?? pedido.criadoEm;
|
|
158
|
+
}
|
package/dist/hitl-gates.js
CHANGED
|
@@ -45,6 +45,8 @@ exports.resolverCriterio = resolverCriterio;
|
|
|
45
45
|
exports.registrarPedidoHitl = registrarPedidoHitl;
|
|
46
46
|
exports.abrirPedidoGate = abrirPedidoGate;
|
|
47
47
|
exports.prepararPedidoGate = prepararPedidoGate;
|
|
48
|
+
exports.essenciaDoPedido = essenciaDoPedido;
|
|
49
|
+
exports.renovarPedidoDoGate = renovarPedidoDoGate;
|
|
48
50
|
exports.conferirResposta = conferirResposta;
|
|
49
51
|
exports.dadosDaResposta = dadosDaResposta;
|
|
50
52
|
exports.receberResposta = receberResposta;
|
|
@@ -330,7 +332,13 @@ function prepararPedidoGate(raiz, id, motivo = 'human.pending', quando = new Dat
|
|
|
330
332
|
const ato = (0, hitl_contract_1.atoDaPausa)(motivo, bloco.pausaSobre);
|
|
331
333
|
// O codigo curto e unico contra os que ja estao no ledger desta thread. T11 acrescenta o
|
|
332
334
|
// comando que o resolve; a unicidade precisa nascer com o primeiro pedido que o carrega.
|
|
333
|
-
|
|
335
|
+
// RM-048 (D4): o gate reaberto no MESMO contexto e pelo mesmo motivo reaproveita o codigo do
|
|
336
|
+
// pedido anterior. Era a troca de codigo a cada hora que fazia a linha recebida virar po.
|
|
337
|
+
// A2 do CHECK: gate ja respondido de forma definitiva (aprovado ou recusado) neste contexto nao
|
|
338
|
+
// empresta o codigo: o que renasce depois dele e outra pergunta, com outro codigo.
|
|
339
|
+
const definitivo = gateRespondidoDeVez(eventos, contexto, motivo);
|
|
340
|
+
const anterior = definitivo ? undefined : pedidoAtual?.codigo;
|
|
341
|
+
const codigo = typeof anterior === 'string' ? anterior : (0, pulse_consentimento_1.gerarCodigo)(eventos.flatMap(e => {
|
|
334
342
|
const c = e.pedido?.codigo;
|
|
335
343
|
return typeof c === 'string' ? [c] : [];
|
|
336
344
|
}));
|
|
@@ -353,8 +361,66 @@ function prepararPedidoGate(raiz, id, motivo = 'human.pending', quando = new Dat
|
|
|
353
361
|
tipoDeResposta: 'objetiva', irreversivel: ato !== undefined, ...(ato ? { ato } : {}), codigo,
|
|
354
362
|
prazo: new Date(Date.parse(quando) + 60 * 60 * 1000).toISOString(), acaoPadraoAoExpirar: 'esperar',
|
|
355
363
|
respostaAceita: { tipo: 'opcao', maxCaracteres: 100 } };
|
|
364
|
+
// RM-048 (D4): um codigo quer dizer UMA pergunta. Se o que sairia agora nao e o que o dono leu
|
|
365
|
+
// com este codigo (a recomendada mudou, o diagnostico mudou), o codigo e outro: "DE6H a" nunca
|
|
366
|
+
// se aplica a uma pergunta que o dono nao viu.
|
|
367
|
+
if (typeof anterior === 'string' && pedidoAtual && essenciaDoPedido(pedido) !== essenciaDoPedido(pedidoAtual)) {
|
|
368
|
+
pedido.codigo = (0, pulse_consentimento_1.gerarCodigo)(eventos.flatMap(e => {
|
|
369
|
+
const c = e.pedido?.codigo;
|
|
370
|
+
return typeof c === 'string' ? [c] : [];
|
|
371
|
+
}));
|
|
372
|
+
}
|
|
356
373
|
return { novo: pedido };
|
|
357
374
|
}
|
|
375
|
+
/** Algum pedido deste gate (contexto e motivo) ja recebeu veredito definitivo (nao `aguardando`). */
|
|
376
|
+
function gateRespondidoDeVez(eventos, contexto, motivo) {
|
|
377
|
+
const ids = new Set(eventos.filter(e => e.tipo === 'hitl_requested' && e.contexto === contexto &&
|
|
378
|
+
(0, hitl_contract_1.alvoDoPedido)(e.pedido)?.tipo === 'gate' && (0, hitl_contract_1.motivoDoPedido)(e.pedido) === motivo)
|
|
379
|
+
.map(e => e.pedido.id));
|
|
380
|
+
return eventos.some(e => e.tipo === 'human_gate' && ids.has(String(e.pedidoId)) && e.estado !== 'aguardando');
|
|
381
|
+
}
|
|
382
|
+
/**
|
|
383
|
+
* RM-048 (D4): o que faz dois pedidos de gate serem a MESMA pergunta para o dono. O identificador,
|
|
384
|
+
* o instante e o prazo mudam a cada renovacao; o que o dono leu e respondeu nao pode mudar.
|
|
385
|
+
*/
|
|
386
|
+
function essenciaDoPedido(p) {
|
|
387
|
+
if (!(0, hitl_contract_1.ehV2)(p) || p.classe !== 'pergunta')
|
|
388
|
+
return JSON.stringify(['v1', p.id]);
|
|
389
|
+
return JSON.stringify([p.thread, p.fase, p.modo, p.alvo, p.motivo, p.pergunta, p.alternativas, p.corpo,
|
|
390
|
+
p.tipoDeResposta, p.irreversivel, p.ato ?? null, p.codigo]);
|
|
391
|
+
}
|
|
392
|
+
/**
|
|
393
|
+
* RM-048 (D4): a resposta a um pedido de gate vencido vai para o pedido RENOVADO, e so quando a
|
|
394
|
+
* pergunta e a mesma.
|
|
395
|
+
*
|
|
396
|
+
* O prazo de uma hora existe para que uma aprovacao velha nao se aplique a um mundo novo. Aqui o
|
|
397
|
+
* mundo e conferido de novo, e nao presumido: o contexto da thread (modo, fase, status, base,
|
|
398
|
+
* despacho) tem de ser o do pedido original, o gate tem de continuar esperando o dono agora
|
|
399
|
+
* (`prepararPedidoGate`, com as mesmas recusas de sempre), e a essencia do pedido que sairia
|
|
400
|
+
* agora tem de ser identica a do que o dono leu. Qualquer diferenca e recusa com "pedido antigo":
|
|
401
|
+
* a letra que o dono digitou nunca e aplicada a uma pergunta que ele nao viu.
|
|
402
|
+
*
|
|
403
|
+
* Nao escolhe resposta, nao aprova nada e nao toca a prova: quem registra e `responderGate`, com
|
|
404
|
+
* o envelope assinado pelo ingresso, contra o pedido que esta funcao devolve.
|
|
405
|
+
*/
|
|
406
|
+
function renovarPedidoDoGate(raiz, original, quando) {
|
|
407
|
+
(0, hitl_contract_1.validarPedidoHitl)(original);
|
|
408
|
+
if ((0, hitl_contract_1.alvoDoPedido)(original)?.tipo !== 'gate')
|
|
409
|
+
throw new Error('pedido antigo: só pedido de gate é renovado');
|
|
410
|
+
const t = (0, thread_1.lerThread)(raiz, original.thread), eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, original.thread));
|
|
411
|
+
const evento = eventos.find(e => e.tipo === 'hitl_requested' && e.pedido?.id === original.id);
|
|
412
|
+
if (!evento || evento.contexto !== contextoHitlDosEventos(t, eventos))
|
|
413
|
+
throw new Error('pedido antigo: fase, modo ou sessão mudou');
|
|
414
|
+
// A2 do CHECK: a linha velha nao desfaz um veredito definitivo dado por outra linha.
|
|
415
|
+
if (gateRespondidoDeVez(eventos, evento.contexto, (0, hitl_contract_1.motivoDoPedido)(original))) {
|
|
416
|
+
throw new Error('pedido antigo: o gate já foi respondido');
|
|
417
|
+
}
|
|
418
|
+
const preparado = prepararPedidoGate(raiz, original.thread, (0, hitl_contract_1.motivoDoPedido)(original), quando);
|
|
419
|
+
const candidato = 'aberto' in preparado ? preparado.aberto : preparado.novo;
|
|
420
|
+
if (essenciaDoPedido(candidato) !== essenciaDoPedido(original))
|
|
421
|
+
throw new Error('pedido antigo: a pergunta mudou desde que saiu');
|
|
422
|
+
return 'aberto' in preparado ? preparado.aberto : registrarPedidoHitl(raiz, preparado.novo);
|
|
423
|
+
}
|
|
358
424
|
const LETRAS_DO_GATE = ['a', 'b', 'c'];
|
|
359
425
|
/** Corta uma linha de evidencia no teto do corpo, sem deixar o pedido inteiro ser recusado. */
|
|
360
426
|
const linhaDoCorpo = (texto) => texto.slice(0, hitl_contract_1.TETOS_HITL_V2.corpoLinha);
|
package/dist/hitl-lote.js
CHANGED
|
@@ -30,6 +30,7 @@ exports.textoDoLote = textoDoLote;
|
|
|
30
30
|
*/
|
|
31
31
|
const hitl_contract_1 = require("./hitl-contract");
|
|
32
32
|
const hitl_1 = require("./hitl");
|
|
33
|
+
const hitl_curto_1 = require("./hitl-curto");
|
|
33
34
|
exports.CONTRATO_LOTE = 'ork.hitl-lote/v1';
|
|
34
35
|
/** De cinco em cinco, como o dono pediu. As que sobram continuam guardadas, nao somem. */
|
|
35
36
|
exports.TETO_DE_PERGUNTAS_POR_LOTE = 5;
|
|
@@ -86,6 +87,16 @@ function alternativaRecomendada(pedido) {
|
|
|
86
87
|
.filter(o => o.achou);
|
|
87
88
|
return citadas.length === 1 ? citadas[0].i : -1;
|
|
88
89
|
}
|
|
90
|
+
/** O que o contrato curto le do pedido alem das alternativas: alvo, corpo, ato e expiracao. */
|
|
91
|
+
function contextoDaPergunta(pedido) {
|
|
92
|
+
const alvo = (0, hitl_contract_1.alvoDoPedido)(pedido);
|
|
93
|
+
const v2 = (0, hitl_contract_1.ehV2)(pedido) && pedido.classe === 'pergunta' ? pedido : undefined;
|
|
94
|
+
return {
|
|
95
|
+
alvo: alvo?.tipo === 'session' ? { tipo: 'session', sessionId: alvo.sessionId } : { tipo: 'gate' },
|
|
96
|
+
...(v2 ? { corpo: [...v2.corpo] } : {}), ...(v2?.ato ? { ato: v2.ato } : {}),
|
|
97
|
+
expiracao: (0, hitl_contract_1.expiracaoDoPedido)(pedido), prazo: (0, hitl_contract_1.prazoDoPedido)(pedido) ?? null,
|
|
98
|
+
};
|
|
99
|
+
}
|
|
89
100
|
/**
|
|
90
101
|
* Traduz um pedido em pergunta do lote, ou diz por que ele nao virou uma.
|
|
91
102
|
*
|
|
@@ -105,7 +116,8 @@ function perguntaDoPedido(pedido, numero) {
|
|
|
105
116
|
return recusa('pergunta vazia');
|
|
106
117
|
const declaradas = alternativasDeclaradas(pedido);
|
|
107
118
|
if (declaradas) {
|
|
108
|
-
return { numero, thread: pedido.thread ?? null, fase: pedido.fase ?? null, pergunta, alternativas: declaradas, pedidoId: pedido.id
|
|
119
|
+
return { numero, thread: pedido.thread ?? null, fase: pedido.fase ?? null, pergunta, alternativas: declaradas, pedidoId: pedido.id,
|
|
120
|
+
...contextoDaPergunta(pedido) };
|
|
109
121
|
}
|
|
110
122
|
// I-41 (T4c): o parser da recomendacao e exclusivo do v1, e a porta se fecha aqui.
|
|
111
123
|
//
|
|
@@ -125,7 +137,7 @@ function perguntaDoPedido(pedido, numero) {
|
|
|
125
137
|
if (recomendada < 0)
|
|
126
138
|
return recusa('a recomendação não nomeia exatamente uma alternativa');
|
|
127
139
|
return {
|
|
128
|
-
numero, thread: pedido.thread ?? null, fase: pedido.fase ?? null, pergunta, pedidoId: pedido.id,
|
|
140
|
+
numero, thread: pedido.thread ?? null, fase: pedido.fase ?? null, pergunta, pedidoId: pedido.id, ...contextoDaPergunta(pedido),
|
|
129
141
|
alternativas: opcoes.map((o, i) => ({
|
|
130
142
|
letra: exports.LETRAS[i], texto: linha(o.texto), consequencia: exports.CONSEQUENCIA_DA_ACAO[o.acao],
|
|
131
143
|
...(i === recomendada ? { recomendada: { porque: linha((0, hitl_contract_1.recomendacaoDoPedido)(pedido)) } } : {}),
|
|
@@ -172,17 +184,18 @@ function montarLote(pedidos, opcoes = {}) {
|
|
|
172
184
|
function guardadasDepoisDoLote(lote) {
|
|
173
185
|
return lote.restantes;
|
|
174
186
|
}
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
187
|
+
/**
|
|
188
|
+
* RM-048 (D1): cada pergunta do lote sai pelo contrato curto, com o numero no titulo. A linha de
|
|
189
|
+
* como responder e uma so, no fim do lote, porque o dono responde todas numa mensagem: "1a 2c".
|
|
190
|
+
*/
|
|
191
|
+
function corpoDaPergunta(p, canal, quando) {
|
|
192
|
+
const curto = (0, hitl_curto_1.montarPedidoCurto)({
|
|
193
|
+
thread: p.thread, fase: p.fase, pergunta: p.pergunta, alvo: p.alvo, corpo: p.corpo, ato: p.ato,
|
|
194
|
+
expiracao: p.expiracao, prazo: p.prazo, desde: p.desde,
|
|
195
|
+
alternativas: p.alternativas.map(a => ({ chave: a.letra, texto: a.texto, consequencia: a.consequencia,
|
|
196
|
+
...(a.recomendada ? { recomendada: { porque: a.recomendada.porque } } : {}) })),
|
|
197
|
+
}, { quando, responder: { tipo: 'numero', numero: p.numero } });
|
|
198
|
+
return (0, hitl_curto_1.textoDoPedidoCurto)(curto, canal).split('\n');
|
|
186
199
|
}
|
|
187
200
|
/** A5: a frase dos pedidos que nao viraram pergunta, com a concordancia certa para o dono ler. */
|
|
188
201
|
function linhaDasRecusas(n) {
|
|
@@ -200,15 +213,17 @@ function textoDoLote(lote, opcoes) {
|
|
|
200
213
|
if (lote.contrato !== exports.CONTRATO_LOTE)
|
|
201
214
|
throw new Error('lote HITL: contrato inválido');
|
|
202
215
|
const telegram = opcoes.canal === 'telegram';
|
|
216
|
+
const quando = opcoes.quando ?? new Date().toISOString();
|
|
203
217
|
const n = lote.perguntas.length;
|
|
204
218
|
const exemplo = lote.perguntas.map(p => `${p.numero}${p.alternativas[0].letra}`).join(' ');
|
|
205
219
|
const faltam = opcoes.proximo?.faltam ?? lote.restantes;
|
|
206
220
|
return [
|
|
207
221
|
`${telegram ? '📋 ' : ''}Orkastery, ${n === 1 ? '1 pergunta' : `${n} perguntas`}`,
|
|
208
222
|
'',
|
|
209
|
-
...lote.perguntas.flatMap((p, i) => [...corpoDaPergunta(p,
|
|
223
|
+
...lote.perguntas.flatMap((p, i) => [...corpoDaPergunta(p, opcoes.canal, quando), ...(i < n - 1 ? [''] : [])]),
|
|
210
224
|
'',
|
|
211
|
-
...(n ? [
|
|
225
|
+
...(n ? [`${telegram ? '↩️ ' : ''}Responda com o número e a letra, por exemplo: ${exemplo}. Evidências: ${lote.perguntas[0].numero} detalhes.`]
|
|
226
|
+
: ['Nenhuma pergunta em formato respondível agora.']),
|
|
212
227
|
...(opcoes.proximo && faltam
|
|
213
228
|
? [`Falta${faltam === 1 ? '' : 'm'} ${faltam}. Para receber as próximas, responda ${opcoes.proximo.codigo} a.`]
|
|
214
229
|
: faltam ? [`Sobra${faltam === 1 ? '' : 'm'} ${faltam} para o próximo lote.`] : []),
|
|
@@ -51,6 +51,7 @@ const hitl_contract_1 = require("./hitl-contract");
|
|
|
51
51
|
const hitl_canais_1 = require("./hitl-canais");
|
|
52
52
|
const hitl_native_offer_1 = require("./hitl-native-offer");
|
|
53
53
|
const horario_1 = require("./horario");
|
|
54
|
+
const hitl_curto_1 = require("./hitl-curto");
|
|
54
55
|
/**
|
|
55
56
|
* I-35: prazo do pedido no fuso do dono, absoluto e rotulado, para JSON de exibição
|
|
56
57
|
* (`ork gate context` e `ork_hitl_pending`). `pedido.prazo` continua o ISO assinado.
|
|
@@ -66,25 +67,27 @@ function prazoLocalDoPedido(pedido, agora) {
|
|
|
66
67
|
* e o que ele troca é o número pela letra. Um `decidido` não passa por aqui de propósito: ele
|
|
67
68
|
* não tem escolha a oferecer, e montar um diálogo para um fato consumado é pedir ação por engano.
|
|
68
69
|
*/
|
|
69
|
-
function apresentarDecisao(pedido, agora) {
|
|
70
|
+
function apresentarDecisao(pedido, agora, desde) {
|
|
70
71
|
(0, hitl_contract_1.validarPedidoHitl)(pedido);
|
|
71
72
|
const aceita = (0, hitl_contract_1.respostaAceitaDoPedido)(pedido);
|
|
72
73
|
if (!aceita)
|
|
73
74
|
throw new Error('decisão informada não tem diálogo: ela não pede nada');
|
|
74
75
|
const livre = aceita.tipo === 'texto';
|
|
75
76
|
const escolhas = (0, hitl_contract_1.escolhasDoPedido)(pedido).map(o => ({ const: (0, hitl_contract_1.chaveDaEscolha)(pedido, o.numero), title: (0, hitl_1.redigirSegredos)(o.texto) }));
|
|
76
|
-
|
|
77
|
+
// RM-048 (D1): a pergunta v2 sai pelo contrato curto, o mesmo do Telegram e do lote. O v1 fica
|
|
78
|
+
// byte a byte abaixo: recibo antigo continua batendo com o texto que o dono viu.
|
|
79
|
+
if ((0, hitl_contract_1.ehV2)(pedido) && pedido.classe === 'pergunta') {
|
|
80
|
+
const curto = (0, hitl_curto_1.montarPedidoCurto)((0, hitl_curto_1.entradaDoPedido)(pedido, desde ?? pedido.criadoEm), { quando: agora ?? new Date().toISOString(), responder: livre ? { tipo: 'texto' } : { tipo: 'dialogo' } });
|
|
81
|
+
return { mensagem: (0, hitl_curto_1.textoDoPedidoCurto)(curto, 'terminal'), escolhas };
|
|
82
|
+
}
|
|
77
83
|
const expiracao = (0, hitl_contract_1.expiracaoDoPedido)(pedido);
|
|
78
84
|
const linhas = [
|
|
79
85
|
`Orkastery · ${pedido.fase}`,
|
|
80
86
|
`• Decisão: ${(0, hitl_1.redigirSegredos)((0, hitl_contract_1.textoDoPedido)(pedido))}`,
|
|
81
87
|
`• Recomendo: ${(0, hitl_1.redigirSegredos)((0, hitl_contract_1.recomendacaoDoPedido)(pedido))}`,
|
|
82
|
-
...escolhas.
|
|
83
|
-
` ${o.const}. ${o.title}`,
|
|
84
|
-
...(v2 ? [` → ${(0, hitl_1.redigirSegredos)(v2.alternativas[i].consequencia)}`] : []),
|
|
85
|
-
]),
|
|
88
|
+
...escolhas.map(o => ` ${o.const}. ${o.title}`),
|
|
86
89
|
livre ? '• Resposta: escreva sua resposta; o texto será enviado literalmente.'
|
|
87
|
-
:
|
|
90
|
+
: '• Resposta: selecione uma opção no diálogo. Se necessário, digite o número.',
|
|
88
91
|
...(livre && escolhas.length ? [' Exemplo: digitar “2” envia o texto “2”, sem selecionar a segunda opção.'] : []),
|
|
89
92
|
`• Prazo: ${(0, horario_1.formatarPrazo)((0, hitl_contract_1.prazoDoPedido)(pedido), { rotulo: true, agora })}. Sem resposta, ${expiracao === 'esperar' ? 'aguardamos' : expiracao === 'seguir-recomendada' ? 'sigo com a recomendada' : 'o pedido será escalado'}.`,
|
|
90
93
|
];
|
package/dist/hitl-resumo.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.SEM_PERGUNTA_NO_RESUMO = exports.TETO_DE_DECISOES_DETALHADAS = exports.TETO_DE_ESPERAS_POR_MAQUINA = exports.TETO_DE_THREADS_NOMEADAS = exports.PERGUNTA_DO_RESUMO = exports.CONTRATO_RESUMO = void 0;
|
|
4
4
|
exports.resumirHitl = resumirHitl;
|
|
5
|
+
exports.linhaDosTecnicos = linhaDosTecnicos;
|
|
5
6
|
exports.linhasDasOutrasMaquinas = linhasDasOutrasMaquinas;
|
|
6
7
|
exports.linhasDasDecisoes = linhasDasDecisoes;
|
|
7
8
|
exports.linhaDasPerguntas = linhaDasPerguntas;
|
|
@@ -41,7 +42,10 @@ exports.PERGUNTA_DO_RESUMO = 'Posso te mandar as perguntas agora?';
|
|
|
41
42
|
exports.TETO_DE_THREADS_NOMEADAS = 5;
|
|
42
43
|
function resumirHitl(itens, opcoes) {
|
|
43
44
|
const janelaMin = opcoes.janelaMin ?? hitl_classificacao_1.JANELA_PADRAO_MIN;
|
|
44
|
-
|
|
45
|
+
// RM-048 (D6): o que e do dono e contado para ele; o que e tecnico e dito a parte.
|
|
46
|
+
const doDono = itens.filter(i => (0, hitl_classificacao_1.quemDecide)(i.motivo) === 'dono');
|
|
47
|
+
const tecnicos = itens.filter(i => (0, hitl_classificacao_1.quemDecide)(i.motivo) === 'orquestrador');
|
|
48
|
+
const contagem = (0, hitl_classificacao_1.contarClassificacoes)(doDono, { quando: opcoes.quando, janelaMin, atoDoItem: opcoes.atoDoItem });
|
|
45
49
|
const prontas = opcoes.prontas ?? 0;
|
|
46
50
|
return {
|
|
47
51
|
contrato: exports.CONTRATO_RESUMO, consultadoEm: opcoes.quando, janelaMin, ...contagem,
|
|
@@ -49,8 +53,15 @@ function resumirHitl(itens, opcoes) {
|
|
|
49
53
|
pergunta: prontas > 0 ? exports.PERGUNTA_DO_RESUMO : null,
|
|
50
54
|
decisoes: [...(opcoes.decisoes ?? [])], acimaDoLimiar: [...(opcoes.acimaDoLimiar ?? [])],
|
|
51
55
|
...(opcoes.outrasMaquinas?.length ? { outrasMaquinas: opcoes.outrasMaquinas.map(m => ({ ...m, esperando: [...m.esperando] })) } : {}),
|
|
56
|
+
tecnicos: tecnicos.length,
|
|
57
|
+
threadsTecnicas: [...new Set(tecnicos.map(i => i.thread).filter((t) => !!t))].sort(),
|
|
52
58
|
};
|
|
53
59
|
}
|
|
60
|
+
/** RM-048 (D6): a linha do que e tecnico, igual nos dois canais. Nao pede nada ao dono. */
|
|
61
|
+
function linhaDosTecnicos(r) {
|
|
62
|
+
const nomes = r.threadsTecnicas.length ? ` (${nomearThreads(r.threadsTecnicas)})` : '';
|
|
63
|
+
return `Conosco, impedimento técnico: ${r.tecnicos}${nomes}. Não precisa de você.`;
|
|
64
|
+
}
|
|
54
65
|
/** Quantas threads de outra maquina o resumo nomeia antes de contar o resto. */
|
|
55
66
|
exports.TETO_DE_ESPERAS_POR_MAQUINA = 3;
|
|
56
67
|
/**
|
|
@@ -91,10 +102,11 @@ function linhasDasDecisoes(r, marcador, recuo) {
|
|
|
91
102
|
const mostradas = r.decisoes.slice(-exports.TETO_DE_DECISOES_DETALHADAS), sobra = r.decisoes.length - mostradas.length;
|
|
92
103
|
return [
|
|
93
104
|
...(r.decisoes.length ? [`${marcador}Decidi sem te perguntar: ${r.decisoes.length}`] : []),
|
|
105
|
+
// RM-048 (item 4): duas linhas por decisao, efeito primeiro; o resto do detalhe esta no placar.
|
|
94
106
|
...mostradas.flatMap(d => [
|
|
95
107
|
`${recuo}• ${d.thread} ${d.fase}: ${uma(d.decidido)}`,
|
|
96
|
-
`${recuo} porquê: ${uma(d.porque, 120)}. Para mudar: ${uma(d.comoMudar, 120)}
|
|
97
|
-
|
|
108
|
+
`${recuo} porquê: ${uma(d.porque, 120)}. Para mudar: ${uma(d.comoMudar, 120)}; ` +
|
|
109
|
+
`reverter agora: ${uma(d.custoDeReverter.agora, 80)}; depois: ${uma(d.custoDeReverter.depois, 80)}.`,
|
|
98
110
|
]),
|
|
99
111
|
...(sobra > 0 ? [`${recuo}e mais ${sobra}: ork decisao placar <thread>`] : []),
|
|
100
112
|
...r.acimaDoLimiar.map(f => `${recuo}${marcador ? '⚠️ ' : '! '}${f.thread} ${f.fase} já tomou ${f.decididas} decisões sozinha, ` +
|
|
@@ -143,27 +155,35 @@ function linhaDeResposta(codigo) {
|
|
|
143
155
|
? [`Responda com ${codigo} a (sim) ou ${codigo} b (agora não).`]
|
|
144
156
|
: ['Responda: a (sim) ou b (agora não).'];
|
|
145
157
|
}
|
|
146
|
-
/**
|
|
147
|
-
|
|
148
|
-
|
|
158
|
+
/**
|
|
159
|
+
* RM-048 (item 4): a pergunta sai no ALTO, logo depois do titulo, com a linha de como responder.
|
|
160
|
+
* Em 27/09 ela ficou escondida no fim, depois de tres blocos de decisoes informadas. Sem pergunta,
|
|
161
|
+
* a frase honesta continua sendo a ultima linha.
|
|
162
|
+
*/
|
|
163
|
+
function abertura(r, codigo, marca, resposta) {
|
|
164
|
+
return r.pergunta ? [`${marca}${r.pergunta}`, ...linhaDeResposta(codigo).map(l => `${resposta}${l}`), ''] : [];
|
|
165
|
+
}
|
|
166
|
+
function fecho(r) {
|
|
167
|
+
return r.pergunta ? [] : ['', exports.SEM_PERGUNTA_NO_RESUMO];
|
|
149
168
|
}
|
|
150
169
|
function textoTelegram(r, codigo) {
|
|
151
170
|
const quando = (0, horario_1.formatarDataHoraRotulada)(r.consultadoEm, { agora: r.consultadoEm });
|
|
152
171
|
const linhas = [
|
|
153
172
|
`🔔 Orkastery, resumo de ${quando}`,
|
|
154
|
-
'',
|
|
173
|
+
...abertura(r, codigo, '❓ ', '↩️ '),
|
|
174
|
+
...(r.pergunta ? [] : ['']),
|
|
155
175
|
`Esperando você: ${r.total}`,
|
|
156
176
|
`⏱ Urgentes: ${r.urgentes}`,
|
|
157
177
|
`⛔ Travando o avanço: ${r.bloqueantes}${r.threadsBloqueadas.length ? `, em ${r.threadsBloqueadas.length} threads` : ''}`,
|
|
158
178
|
...(r.threadsBloqueadas.length ? [` ${nomearThreads(r.threadsBloqueadas)}`] : []),
|
|
159
179
|
`🔒 Sem volta depois de feito: ${r.criticos}`,
|
|
160
180
|
`❓ ${linhaDasPerguntas(r.prontas)}`,
|
|
181
|
+
...(r.tecnicos ? [`🔧 ${linhaDosTecnicos(r)}`] : []),
|
|
161
182
|
...(r.consertos ? [`🔧 ${linhaDosConsertos(r.consertos)}`] : []),
|
|
162
183
|
...(r.acumuladas ? ['', `📥 ${linhaDoLoteGuardado(r.acumuladas)}`] : []),
|
|
163
184
|
...(r.decisoes.length || r.acimaDoLimiar.length ? ['', ...linhasDasDecisoes(r, '🧭 ', ' ')] : []),
|
|
164
185
|
...(r.outrasMaquinas?.length ? ['', ...linhasDasOutrasMaquinas(r, '🖥️ ', ' ')] : []),
|
|
165
|
-
|
|
166
|
-
...fecho(r, codigo),
|
|
186
|
+
...fecho(r),
|
|
167
187
|
];
|
|
168
188
|
return linhas.join('\n');
|
|
169
189
|
}
|
|
@@ -181,18 +201,19 @@ function textoTerminal(r, codigo) {
|
|
|
181
201
|
const regua = ` ${'-'.repeat(largura)} ${'-'.repeat(numero)}`;
|
|
182
202
|
const linhas = [
|
|
183
203
|
`Orkastery, resumo de ${quando}`,
|
|
184
|
-
'',
|
|
204
|
+
...abertura(r, codigo, '', ''),
|
|
205
|
+
...(r.pergunta ? [] : ['']),
|
|
185
206
|
` ${'o que'.padEnd(largura)} ${'quantos'.padStart(numero)}`,
|
|
186
207
|
regua,
|
|
187
208
|
...celulas.map(([nome, n]) => ` ${nome.padEnd(largura)} ${String(n).padStart(numero)}`),
|
|
188
209
|
...(r.threadsBloqueadas.length
|
|
189
210
|
? ['', ` threads travadas (${r.threadsBloqueadas.length}): ${nomearThreads(r.threadsBloqueadas)}`] : []),
|
|
211
|
+
...(r.tecnicos ? ['', ` ${linhaDosTecnicos(r)}`] : []),
|
|
190
212
|
...(r.consertos ? ['', ` ${linhaDosConsertos(r.consertos)}`] : []),
|
|
191
213
|
...(r.acumuladas ? ['', ` ${linhaDoLoteGuardado(r.acumuladas)}`] : []),
|
|
192
214
|
...(r.decisoes.length || r.acimaDoLimiar.length ? ['', ...linhasDasDecisoes(r, '', ' ').map(l => l.startsWith(' ') ? l : ` ${l}`)] : []),
|
|
193
215
|
...(r.outrasMaquinas?.length ? ['', ...linhasDasOutrasMaquinas(r, '', ' ').map(l => l.startsWith(' ') ? l : ` ${l}`)] : []),
|
|
194
|
-
|
|
195
|
-
...fecho(r, codigo),
|
|
216
|
+
...fecho(r),
|
|
196
217
|
];
|
|
197
218
|
return linhas.join('\n');
|
|
198
219
|
}
|