@orkastery/cli 0.5.1 → 0.5.6

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.
Files changed (260) hide show
  1. package/adapters/claude-code/README.md +158 -0
  2. package/adapters/claude-code/agents/ork-master.md +2 -0
  3. package/adapters/claude-code/commands/master.md +8 -0
  4. package/adapters/claude-code/commands/ork.md +9 -0
  5. package/adapters/claude-code/mods/autoridade.json +56 -0
  6. package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
  7. package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
  8. package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
  9. package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
  10. package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
  11. package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
  12. package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
  13. package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
  14. package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
  15. package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
  16. package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
  17. package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
  18. package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
  19. package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
  20. package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
  21. package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
  22. package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
  23. package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
  24. package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
  25. package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
  26. package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
  27. package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
  28. package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
  29. package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
  30. package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
  31. package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
  32. package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +117 -0
  33. package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
  34. package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
  35. package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
  36. package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
  37. package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
  38. package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
  39. package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
  40. package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
  41. package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
  42. package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
  43. package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
  44. package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
  45. package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
  46. package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
  47. package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
  48. package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
  49. package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
  50. package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
  51. package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
  52. package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
  53. package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
  54. package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
  55. package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
  56. package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
  57. package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
  58. package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
  59. package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
  60. package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
  61. package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
  62. package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
  63. package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
  64. package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
  65. package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
  66. package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
  67. package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
  68. package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
  69. package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
  70. package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
  71. package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
  72. package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
  73. package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
  74. package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
  75. package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
  76. package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
  77. package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
  78. package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
  79. package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
  80. package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
  81. package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
  82. package/adapters/codex/skills/ork/SKILL.md +18 -3
  83. package/adapters/hermes/README.md +1 -0
  84. package/adapters/hermes/bin/ork-network-status.sh +10 -0
  85. package/adapters/hermes/hermes.plugin.json +1 -0
  86. package/adapters/hermes/hitl-ingress/__init__.py +2 -2
  87. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +7 -6
  88. package/adapters/openclaw/README.md +26 -5
  89. package/adapters/openclaw/dist/hitl-ingress.js +2 -2
  90. package/adapters/openclaw/dist/index.js +46 -11
  91. package/adapters/openclaw/openclaw.plugin.json +5 -1
  92. package/adapters/openclaw/package.json +2 -2
  93. package/adapters/openclaw/src/hitl-ingress.ts +2 -2
  94. package/adapters/openclaw/src/index.ts +49 -11
  95. package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
  96. package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
  97. package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
  98. package/assets/docs-en/produto/README.md +14 -0
  99. package/assets/docs-en/produto/_modelo-feature.md +62 -0
  100. package/assets/docs-en/roadmap/README.md +14 -0
  101. package/assets/docs-en/roadmap/_modelo-item.md +75 -0
  102. package/assets/orkmind_bridge.py +117 -9
  103. package/dist/adapters/claude-bg.js +116 -18
  104. package/dist/adapters/codex.js +14 -4
  105. package/dist/agents-md.js +24 -14
  106. package/dist/auditrun.js +22 -3
  107. package/dist/board.js +20 -17
  108. package/dist/branch-de-estado.js +88 -10
  109. package/dist/busca-semantica.js +52 -12
  110. package/dist/canarios-rm057.js +149 -0
  111. package/dist/canarios.js +2 -0
  112. package/dist/catalogo.js +33 -0
  113. package/dist/ci.js +21 -8
  114. package/dist/claims-conferir.js +342 -0
  115. package/dist/claims.js +41 -1
  116. package/dist/company-brain-client.js +36 -1
  117. package/dist/company-brain-context.js +158 -22
  118. package/dist/company-brain-journal.js +2 -1
  119. package/dist/company-brain-migration.js +36 -1
  120. package/dist/conducao-sentinela.js +388 -0
  121. package/dist/conducao-texto.js +12 -18
  122. package/dist/conducao.js +122 -30
  123. package/dist/conhecimento.js +490 -0
  124. package/dist/docs.js +70 -17
  125. package/dist/doctor-pulse-cron.js +349 -0
  126. package/dist/doctor-rede.js +150 -0
  127. package/dist/doctor.js +195 -111
  128. package/dist/entrega-pr.js +42 -29
  129. package/dist/escopo-escrita.js +10 -1
  130. package/dist/esgotamentos.js +181 -0
  131. package/dist/estado-thread.js +23 -1
  132. package/dist/experiencia-instalacao.js +45 -25
  133. package/dist/experiencia.js +12 -2
  134. package/dist/fabrica-batida.js +310 -0
  135. package/dist/fabrica-estado.js +52 -14
  136. package/dist/fabrica-publicar.js +9 -1
  137. package/dist/fechamento.js +20 -66
  138. package/dist/fila-pesada.js +444 -0
  139. package/dist/fix.js +3 -2
  140. package/dist/forja.js +31 -7
  141. package/dist/gates.js +3 -24
  142. package/dist/handoff.js +10 -6
  143. package/dist/hitl-contract.js +59 -4
  144. package/dist/hitl-curto.js +11 -6
  145. package/dist/hitl-gates.js +5 -0
  146. package/dist/hitl-local-receipt.js +2 -0
  147. package/dist/hitl-lote.js +5 -2
  148. package/dist/hitl-resumo.js +38 -3
  149. package/dist/hitl-tempo-parado.js +201 -0
  150. package/dist/hitl-texto-livre.js +5 -5
  151. package/dist/hitl.js +29 -33
  152. package/dist/horario.js +41 -7
  153. package/dist/hosts.js +104 -11
  154. package/dist/impedimento.js +299 -0
  155. package/dist/index.js +961 -542
  156. package/dist/indice-vetorial.js +87 -10
  157. package/dist/indice.js +9 -1
  158. package/dist/init.js +56 -43
  159. package/dist/instabilidade.js +144 -0
  160. package/dist/integracoes-locais.js +4 -0
  161. package/dist/intelligence-graph-cli.js +170 -16
  162. package/dist/intelligence-graph-contexto.js +254 -0
  163. package/dist/intelligence-graph-contract.js +2 -1
  164. package/dist/intelligence-graph-extract-md.js +56 -4
  165. package/dist/intelligence-graph-extract-ts.js +71 -3
  166. package/dist/intelligence-graph-parsers.js +6 -4
  167. package/dist/intelligence-graph-query.js +66 -19
  168. package/dist/leases.js +664 -87
  169. package/dist/ledger-stats.js +15 -1
  170. package/dist/ledger.js +18 -1
  171. package/dist/licoes.js +1 -0
  172. package/dist/liveness.js +6 -3
  173. package/dist/locale-ajuda.js +761 -0
  174. package/dist/locale-gerados.js +160 -0
  175. package/dist/locale-master.js +99 -0
  176. package/dist/locale-onboarding.js +92 -0
  177. package/dist/locale-phase.js +151 -0
  178. package/dist/locale-pulse.js +211 -0
  179. package/dist/locale-retry.js +385 -0
  180. package/dist/locale-roadmap.js +102 -0
  181. package/dist/locale-ship.js +494 -0
  182. package/dist/locale-thread.js +277 -0
  183. package/dist/locale-verify.js +125 -0
  184. package/dist/locale.js +714 -0
  185. package/dist/maestro-runtime.js +4 -1
  186. package/dist/manifest.js +149 -4
  187. package/dist/maquina.js +23 -4
  188. package/dist/master-digest.js +6 -1
  189. package/dist/master-nota.js +18 -11
  190. package/dist/master.js +145 -31
  191. package/dist/mcp-artifacts.js +43 -3
  192. package/dist/mcp-git.js +42 -8
  193. package/dist/mcp-grafo-worker.js +5 -3
  194. package/dist/mcp-grafo.js +55 -6
  195. package/dist/mcp-server.js +22 -3
  196. package/dist/mcp-ship.js +3 -1
  197. package/dist/mcp-verify.js +6 -1
  198. package/dist/memoria.js +48 -6
  199. package/dist/modos.js +3 -6
  200. package/dist/network-roadmap.js +196 -18
  201. package/dist/noctum-arco.js +911 -0
  202. package/dist/noctum-capacidade.js +141 -0
  203. package/dist/noctum-envelope.js +810 -0
  204. package/dist/noctum-execucao.js +304 -0
  205. package/dist/noctum-fila.js +280 -0
  206. package/dist/ocupacao.js +40 -5
  207. package/dist/onboarding.js +40 -21
  208. package/dist/orkmind.js +99 -7
  209. package/dist/orquestracao.js +24 -21
  210. package/dist/parado-no-condutor.js +219 -38
  211. package/dist/phase.js +160 -52
  212. package/dist/policies.js +56 -5
  213. package/dist/portfolio.js +8 -3
  214. package/dist/postura-local.js +234 -0
  215. package/dist/preflight.js +11 -10
  216. package/dist/procedencia.js +225 -0
  217. package/dist/projeto-alvo.js +132 -5
  218. package/dist/prompts.js +40 -1
  219. package/dist/prova-ativacao.js +75 -6
  220. package/dist/pulse-delivery.js +47 -4
  221. package/dist/pulse.js +98 -19
  222. package/dist/ratelimit.js +24 -6
  223. package/dist/recall.js +3 -1
  224. package/dist/redacao-saida.js +63 -0
  225. package/dist/rede-adesao.js +399 -0
  226. package/dist/rede-forja.js +289 -0
  227. package/dist/rede-projetos.js +239 -0
  228. package/dist/rede-status.js +337 -0
  229. package/dist/rede.js +980 -0
  230. package/dist/retry.js +291 -140
  231. package/dist/roadmap-reservas.js +20 -3
  232. package/dist/roadmap-status.js +150 -34
  233. package/dist/runtime-context.js +181 -3
  234. package/dist/runtime-profiles.js +22 -1
  235. package/dist/runtimes.js +2 -1
  236. package/dist/saida-segura.js +58 -0
  237. package/dist/session-events.js +7 -3
  238. package/dist/session-watcher-claude.js +120 -24
  239. package/dist/session-watcher.js +185 -33
  240. package/dist/sessoes-contas.js +187 -0
  241. package/dist/sessoes-inventario.js +6 -18
  242. package/dist/sessoes.js +83 -2
  243. package/dist/ship-publicar.js +800 -0
  244. package/dist/ship.js +157 -88
  245. package/dist/steal.js +77 -0
  246. package/dist/thread-close.js +3 -2
  247. package/dist/thread.js +299 -49
  248. package/dist/util.js +8 -1
  249. package/dist/verify.js +141 -52
  250. package/dist/vida-da-thread.js +254 -0
  251. package/dist/worktree-local.js +165 -0
  252. package/dist/worktree.js +25 -2
  253. package/dist/yaml.js +27 -8
  254. package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
  255. package/package.json +7 -3
  256. package/references/definition-of-done.md +1 -1
  257. package/schemas/code-artifact-graph.v1.schema.json +2 -1
  258. package/skills/README.md +1 -1
  259. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  260. package/skills/phases/master-metrics/SKILL.md +8 -3
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.CONTRATOS_DE_PEDIDO = exports.CAMPOS_DA_DECISAO_NO_MCP = exports.CAMPOS_DA_DECISAO_NO_CLI = exports.CAMPOS_DE_GATE = exports.TETOS_HITL_V2 = exports.LETRAS_DE_ALTERNATIVA = exports.CONTRATO_HITL_V2 = exports.ATO_IRREVERSIVEL_DO_MOTIVO = exports.ATOS_IRREVERSIVEIS = exports.CONTRATO_HITL = void 0;
3
+ exports.CONTRATOS_DE_PEDIDO = exports.SelecaoRecusada = exports.MAXIMO_DE_ALTERNATIVAS_DE_CONDUCAO = exports.MINIMO_DE_ALTERNATIVAS_DE_CONDUCAO = exports.CAMPOS_DA_DECISAO_NO_MCP = exports.CAMPOS_DA_DECISAO_NO_CLI = exports.CAMPOS_DE_GATE = exports.TETOS_HITL_V2 = exports.LETRAS_DE_ALTERNATIVA = exports.CONTRATO_HITL_V2 = exports.ATO_IRREVERSIVEL_DO_MOTIVO = exports.ATOS_IRREVERSIVEIS = exports.CONTRATO_HITL = void 0;
4
4
  exports.atoDaPausa = atoDaPausa;
5
5
  exports.profundidadeDoModo = profundidadeDoModo;
6
6
  exports.profundidadeDoPedido = profundidadeDoPedido;
@@ -10,6 +10,7 @@ exports.respostaAceitaDoPedido = respostaAceitaDoPedido;
10
10
  exports.validarRespostaHitl = validarRespostaHitl;
11
11
  exports.vereditoDoGate = vereditoDoGate;
12
12
  exports.recusaNaSuperficie = recusaNaSuperficie;
13
+ exports.exigirSelecaoDeConducao = exigirSelecaoDeConducao;
13
14
  exports.validarPedidoHitlV2 = validarPedidoHitlV2;
14
15
  exports.validarPedidoHitl = validarPedidoHitl;
15
16
  exports.ehV2 = ehV2;
@@ -201,7 +202,8 @@ function vereditoDoGate(pedido, opcao) {
201
202
  // `pergunta` e o unico que pode segurar, e o unico que o dono precisa responder.
202
203
  // ---------------------------------------------------------------------------
203
204
  exports.CONTRATO_HITL_V2 = 'ork.hitl/v2';
204
- exports.LETRAS_DE_ALTERNATIVA = ['a', 'b', 'c', 'd'];
205
+ /** RM-057: ate cinco alternativas, de a a e. A leitura aceita as de duas do historico. */
206
+ exports.LETRAS_DE_ALTERNATIVA = ['a', 'b', 'c', 'd', 'e'];
205
207
  /** D13: tetos com origem na maior medida ja gravada nos catorze pedidos do historico. */
206
208
  exports.TETOS_HITL_V2 = Object.freeze({
207
209
  pergunta: 200, alternativaTexto: 140, consequencia: 140, porque: 140,
@@ -284,12 +286,12 @@ function validarDecidido(v) {
284
286
  function validarAlternativas(v) {
285
287
  const alternativas = v.alternativas;
286
288
  if (!Array.isArray(alternativas) || alternativas.length < 2 || alternativas.length > exports.LETRAS_DE_ALTERNATIVA.length) {
287
- throw new Error('pedido HITL v2: de 2 a 4 alternativas, rotuladas de a a d');
289
+ throw new Error('pedido HITL v2: de 2 a 5 alternativas, rotuladas de a a e');
288
290
  }
289
291
  let recomendadas = 0;
290
292
  alternativas.forEach((bruta, i) => {
291
293
  if (!objeto(bruta) || bruta.letra !== exports.LETRAS_DE_ALTERNATIVA[i]) {
292
- throw new Error('pedido HITL v2: de 2 a 4 alternativas, rotuladas de a a d');
294
+ throw new Error('pedido HITL v2: de 2 a 5 alternativas, rotuladas de a a e');
293
295
  }
294
296
  if (!linhaUnica(bruta.texto, exports.TETOS_HITL_V2.alternativaTexto))
295
297
  throw new Error('pedido HITL v2: alternativa sem texto');
@@ -343,6 +345,12 @@ function validarPergunta(v) {
343
345
  !['esperar', 'escalar', 'seguir-recomendada'].includes(v.acaoPadraoAoExpirar)) {
344
346
  throw new Error('pedido HITL v2: prazo ou ação de expiração inválidos');
345
347
  }
348
+ if (v.dependenciaTecnica !== undefined) {
349
+ const d = v.dependenciaTecnica;
350
+ if (!objeto(d) || !linhaUnica(d.comando, exports.TETOS_HITL_V2.campoDaDecisao) || !linhaUnica(d.porque, exports.TETOS_HITL_V2.porque)) {
351
+ throw new Error('pedido HITL v2: dependência técnica exige o comando exato e o porquê, uma linha cada');
352
+ }
353
+ }
346
354
  if (typeof v.irreversivel !== 'boolean')
347
355
  throw new Error('pedido HITL v2: irreversível precisa ser declarado');
348
356
  if (v.irreversivel) {
@@ -358,6 +366,53 @@ function validarPergunta(v) {
358
366
  throw new Error('pedido HITL v2: só pedido irreversível nomeia ato');
359
367
  }
360
368
  }
369
+ // ---------------------------------------------------------------------------
370
+ // RM-057: o HITL de conducao que parte do ork e uma SELECAO.
371
+ //
372
+ // Na noite de 01 para 02/10/2026 a conducao parou mais de dez horas esperando um "confirmo" em
373
+ // texto livre. A regra do dono (02/10) e que todo pedido que o PROPRIO ork abre ao maestro sai com
374
+ // de 3 a 5 alternativas, exatamente uma com o selo "Recomendação", e nunca depende de o dono
375
+ // escrever ou colar texto. A unica excecao e a dependencia tecnica tipada: o dono roda um comando
376
+ // no terminal que a fabrica nao consegue rodar sozinha.
377
+ //
378
+ // A regra vale na CRIACAO (`registrarPedidoHitl`), nunca na leitura: o ledger guarda pedidos de
379
+ // duas alternativas que continuam legiveis para sempre, e a pergunta nativa de uma sessao do host
380
+ // (v1) traz as opcoes do host, que nao sao do ork.
381
+ // ---------------------------------------------------------------------------
382
+ exports.MINIMO_DE_ALTERNATIVAS_DE_CONDUCAO = 3;
383
+ exports.MAXIMO_DE_ALTERNATIVAS_DE_CONDUCAO = 5;
384
+ class SelecaoRecusada extends Error {
385
+ motivo;
386
+ constructor(motivo, detalhe) {
387
+ super(`${motivo}: ${detalhe}`);
388
+ this.motivo = motivo;
389
+ this.name = 'SelecaoRecusada';
390
+ }
391
+ }
392
+ exports.SelecaoRecusada = SelecaoRecusada;
393
+ /**
394
+ * RM-057: confere que um pedido que o ork vai ABRIR e uma selecao. Decisao informada (`decidido`)
395
+ * nao pergunta nada e passa. Recusa com motivo tipado, nunca corta nem completa em silencio.
396
+ */
397
+ function exigirSelecaoDeConducao(pedido) {
398
+ if (!ehV2(pedido)) {
399
+ throw new SelecaoRecusada('hitl.selecao.versao', 'o pedido de condução do ork sai em ork.hitl/v2, com alternativas declaradas');
400
+ }
401
+ if (pedido.classe === 'decidido')
402
+ return;
403
+ const n = pedido.alternativas.length;
404
+ if (n < exports.MINIMO_DE_ALTERNATIVAS_DE_CONDUCAO || n > exports.MAXIMO_DE_ALTERNATIVAS_DE_CONDUCAO) {
405
+ throw new SelecaoRecusada('hitl.selecao.fora-da-faixa', `de ${exports.MINIMO_DE_ALTERNATIVAS_DE_CONDUCAO} a ${exports.MAXIMO_DE_ALTERNATIVAS_DE_CONDUCAO} alternativas, este traz ${n}`);
406
+ }
407
+ const recomendadas = pedido.alternativas.filter(a => a.recomendada).length;
408
+ if (recomendadas !== 1) {
409
+ throw new SelecaoRecusada('hitl.selecao.recomendada', `exatamente uma alternativa com o selo Recomendação, este traz ${recomendadas}`);
410
+ }
411
+ const pedeTexto = pedido.tipoDeResposta === 'aberta' || pedido.respostaAceita.tipo === 'texto';
412
+ if (pedeTexto && !pedido.dependenciaTecnica) {
413
+ throw new SelecaoRecusada('hitl.selecao.texto-livre', 'resposta em texto só com dependência técnica tipada: o comando exato que o dono roda no terminal e o porquê');
414
+ }
415
+ }
361
416
  /** Valida `ork.hitl/v2`. Nunca aceita um v1: quem julga v1 e o validador congelado. */
362
417
  function validarPedidoHitlV2(v) {
363
418
  if (!objeto(v) || v.contrato !== exports.CONTRATO_HITL_V2)
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
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;
3
+ exports.TEXTO_DO_ATO = exports.TETO_DE_LINHAS_DO_CORPO = exports.TETO_DE_LINHAS_DO_PEDIDO = exports.SELO_DA_RECOMENDADA = exports.CONTRATO_PEDIDO_CURTO = void 0;
4
4
  exports.entradaDoPedido = entradaDoPedido;
5
5
  exports.montarPedidoCurto = montarPedidoCurto;
6
6
  exports.linhaDeResposta = linhaDeResposta;
@@ -23,7 +23,8 @@ exports.desdeDoPedido = desdeDoPedido;
23
23
  * trava o que esta parado, desde quando, e o que acontece sem resposta;
24
24
  * sem volta so quando responder autoriza um ato da lista congelada;
25
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;
26
+ * alternativas de 2 a 5, uma linha cada, com a consequencia, e UMA com o selo "Recomendação" e o
27
+ * porque (RM-057: o pedido que o proprio ork abre traz de 3 a 5);
27
28
  * responder a ultima linha, dizendo exatamente o que digitar.
28
29
  *
29
30
  * O detalhe profundo (artefato, claims, riscos, diff) fica a um pedido de distancia: a ultima
@@ -36,6 +37,8 @@ const hitl_1 = require("./hitl");
36
37
  const hitl_contract_1 = require("./hitl-contract");
37
38
  const horario_1 = require("./horario");
38
39
  exports.CONTRATO_PEDIDO_CURTO = 'ork.hitl-curto/v1';
40
+ /** RM-057: o selo que o dono procura na lista, igual em todo canal. */
41
+ exports.SELO_DA_RECOMENDADA = 'Recomendação';
39
42
  /** Nenhum pedido passa disto por padrao (RM-048, criterio c). */
40
43
  exports.TETO_DE_LINHAS_DO_PEDIDO = 15;
41
44
  /** Evidencia que cabe na pergunta curta; o resto vai no detalhe. */
@@ -80,11 +83,11 @@ function semResposta(e, agora) {
80
83
  }
81
84
  /**
82
85
  * 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.
86
+ * de duas alternativas, mais de cinco, ou recomendada diferente de exatamente uma.
84
87
  */
85
88
  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');
89
+ if (e.alternativas.length < 2 || e.alternativas.length > 5)
90
+ throw new Error('pedido curto: de 2 a 5 alternativas');
88
91
  if (e.alternativas.filter(a => a.recomendada).length !== 1)
89
92
  throw new Error('pedido curto: exatamente uma alternativa é recomendada');
90
93
  const onde = e.alvo?.tipo === 'session'
@@ -110,6 +113,8 @@ function linhaDeResposta(p, canal) {
110
113
  return null;
111
114
  if (r.tipo === 'texto')
112
115
  return `${canal === 'telegram' ? '↩️ ' : ''}Responda escrevendo o texto; ele é enviado como está.`;
116
+ if (r.tipo === 'terminal')
117
+ return `${canal === 'telegram' ? '↩️ ' : 'Responder: '}feito o comando, ${recomendada}) é \`${r.comando}\`.`;
113
118
  if (r.tipo === 'dialogo')
114
119
  return `${canal === 'telegram' ? '↩️ ' : ''}Responda selecionando a opção no diálogo; digitar a letra também vale.`;
115
120
  return canal === 'telegram'
@@ -132,7 +137,7 @@ function textoDoPedidoCurto(p, canal) {
132
137
  ...(p.semVolta ? [`${tg ? '🔒 ' : '! '}${p.semVolta}`] : []),
133
138
  ...p.corpo.map(l => `${tg ? '• ' : '- '}${l}`),
134
139
  ...p.alternativas.flatMap(a => [
135
- `${a.chave}) ${a.texto}${a.recomendada ? (tg ? ' ✅ recomendada' : ' [recomendada]') : ''}: ${a.consequencia}`,
140
+ `${a.chave}) ${a.texto}${a.recomendada ? (tg ? ` ✅ ${exports.SELO_DA_RECOMENDADA}` : ` [${exports.SELO_DA_RECOMENDADA}]`) : ''}: ${a.consequencia}`,
136
141
  ...(a.recomendada ? [` porquê: ${a.recomendada.porque}`] : []),
137
142
  ]),
138
143
  ];
@@ -215,6 +215,11 @@ function resolverCriterio(raiz, pedido) {
215
215
  }
216
216
  function registrarPedidoHitl(raiz, pedido) {
217
217
  (0, hitl_contract_1.validarPedidoHitl)(pedido);
218
+ // RM-057: o pedido que o ork abre ao dono e uma selecao de 3 a 5, com uma recomendada. O v1 e o
219
+ // contrato congelado que o nucleo nao emite mais por aqui (o gate sai em v2 desde a I-41); ele
220
+ // segue registravel so para os leitores do historico e para os testes que o reproduzem.
221
+ if ((0, hitl_contract_1.ehV2)(pedido))
222
+ (0, hitl_contract_1.exigirSelecaoDeConducao)(pedido);
218
223
  // R1, camada 1: um `decidido` cujo critério não resolve não chega a existir.
219
224
  if ((0, hitl_contract_1.ehV2)(pedido) && pedido.classe === 'decidido') {
220
225
  const { resolve, detalhe } = resolverCriterio(raiz, pedido);
@@ -33,6 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.chaveLocal = chaveLocal;
36
37
  exports.gravarEvidenciaLocal = gravarEvidenciaLocal;
37
38
  exports.gravarEvidenciaLocalSessao = gravarEvidenciaLocalSessao;
38
39
  exports.validarEvidenciaLocalSessao = validarEvidenciaLocalSessao;
@@ -70,6 +71,7 @@ function diretorioPrivado(file) {
70
71
  }
71
72
  return validarDiretorio(file, 0o700);
72
73
  }
74
+ /** RM-058 (N1): exportada só para o arco #Noctum derivar a subchave dele (`noctum-arco.ts`). */
73
75
  function chaveLocal(raiz, criar) {
74
76
  const estado = path.join((0, estado_thread_1.raizDoEstado)(raiz), '.orkastery');
75
77
  const stEstado = fs.lstatSync(estado);
package/dist/hitl-lote.js CHANGED
@@ -34,8 +34,11 @@ const hitl_curto_1 = require("./hitl-curto");
34
34
  exports.CONTRATO_LOTE = 'ork.hitl-lote/v1';
35
35
  /** De cinco em cinco, como o dono pediu. As que sobram continuam guardadas, nao somem. */
36
36
  exports.TETO_DE_PERGUNTAS_POR_LOTE = 5;
37
- /** De 2 a 4 alternativas, rotuladas em sequencia. Cinco nao cabem numa escolha rapida. */
38
- exports.LETRAS = ['a', 'b', 'c', 'd'];
37
+ /**
38
+ * De 2 a 5 alternativas, rotuladas em sequencia. RM-057: o pedido de conducao do ork traz de 3 a 5;
39
+ * o lote ainda le as de 2 do historico e as perguntas nativas das sessoes.
40
+ */
41
+ exports.LETRAS = ['a', 'b', 'c', 'd', 'e'];
39
42
  /**
40
43
  * O que acontece ao escolher, por acao. Mapa TOTAL sobre as acoes do pedido: acao nova quebra o
41
44
  * build aqui, que e o comportamento desejado. Uma alternativa sem consequencia dita e uma
@@ -1,9 +1,10 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.SEM_PERGUNTA_NO_RESUMO = exports.TETO_DE_DECISOES_DETALHADAS = exports.TETO_DE_ESPERAS_POR_MAQUINA = exports.TETO_DE_PARADOS_NO_CONDUTOR = exports.TETO_DE_THREADS_NOMEADAS = exports.PERGUNTA_DO_RESUMO = exports.CONTRATO_RESUMO = void 0;
3
+ exports.SEM_PERGUNTA_COM_IMPEDIMENTO = exports.SEM_PERGUNTA_NO_RESUMO = exports.TETO_DE_DECISOES_DETALHADAS = exports.TETO_DE_ESPERAS_POR_MAQUINA = exports.TETO_DE_IMPEDIMENTOS = exports.TETO_DE_PARADOS_NO_CONDUTOR = exports.TETO_DE_THREADS_NOMEADAS = exports.PERGUNTA_DO_RESUMO = exports.CONTRATO_RESUMO = void 0;
4
4
  exports.resumirHitl = resumirHitl;
5
5
  exports.linhaDoParadoNoCondutor = linhaDoParadoNoCondutor;
6
6
  exports.linhasDoCondutor = linhasDoCondutor;
7
+ exports.linhasDosImpedimentos = linhasDosImpedimentos;
7
8
  exports.linhaDosTecnicos = linhaDosTecnicos;
8
9
  exports.linhasDasOutrasMaquinas = linhasDasOutrasMaquinas;
9
10
  exports.linhasDasDecisoes = linhasDasDecisoes;
@@ -29,6 +30,8 @@ exports.textoDoResumo = textoDoResumo;
29
30
  * mensagem do canal. Quem entrega chama a segunda.
30
31
  */
31
32
  const horario_1 = require("./horario");
33
+ const locale_1 = require("./locale");
34
+ const hitl_tempo_parado_1 = require("./hitl-tempo-parado");
32
35
  const decisao_autonoma_1 = require("./decisao-autonoma");
33
36
  const hitl_classificacao_1 = require("./hitl-classificacao");
34
37
  exports.CONTRATO_RESUMO = 'ork.hitl-resumo/v1';
@@ -59,6 +62,9 @@ function resumirHitl(itens, opcoes) {
59
62
  threadsTecnicas: [...new Set(tecnicos.map(i => i.thread).filter((t) => !!t))].sort(),
60
63
  ...(opcoes.paradosNoCondutor?.length ? { paradosNoCondutor: opcoes.paradosNoCondutor.map(p => ({ thread: p.thread, caso: p.caso,
61
64
  desdeEm: p.desdeEm, proximoPasso: p.proximoPasso })) } : {}),
65
+ ...(opcoes.impedimentosDoDono?.length ? { impedimentosDoDono: opcoes.impedimentosDoDono.map(i => ({ thread: i.thread,
66
+ fase: i.fase, comando: i.comando, retry: i.retry })) } : {}),
67
+ ...(opcoes.hitlDeConducao?.acimaDaMeta ? { hitlDeConducao: opcoes.hitlDeConducao } : {}),
62
68
  };
63
69
  }
64
70
  /** Quantas linhas do condutor o resumo mostra antes de contar o resto. */
@@ -69,7 +75,7 @@ exports.TETO_DE_PARADOS_NO_CONDUTOR = 5;
69
75
  */
70
76
  function linhaDoParadoNoCondutor(p, opcoes) {
71
77
  const { desdeEm: desde } = p;
72
- return `${p.thread} parado no condutor desde ${(0, horario_1.formatarHora)(desde, opcoes)}: ${p.proximoPasso}`;
78
+ return (0, locale_1.msg)().pulse.linhaDoCondutor(p.thread, (0, horario_1.formatarHora)(desde, opcoes), p.proximoPasso);
73
79
  }
74
80
  /** As linhas do condutor no resumo, uma por thread, com o resto contado. */
75
81
  function linhasDoCondutor(r, marcador, recuo) {
@@ -78,6 +84,29 @@ function linhasDoCondutor(r, marcador, recuo) {
78
84
  return [...mostradas.map(p => `${marcador}${linhaDoParadoNoCondutor(p, { agora: r.consultadoEm })}`),
79
85
  ...(sobra > 0 ? [`${recuo}e mais ${sobra}: ork pulse`] : [])];
80
86
  }
87
+ /** Quantos impedimentos do dono o resumo detalha antes de contar o resto. */
88
+ exports.TETO_DE_IMPEDIMENTOS = 5;
89
+ /** Uma linha so, sem caractere de controle e sem corte: o comando precisa chegar inteiro para ser copiado. */
90
+ const semQuebra = (texto) => String(texto).replace(/\p{Cc}/gu, ' ').replace(/\s+/g, ' ').trim();
91
+ /**
92
+ * RM-055 (continuacao, D3): o titulo e uma linha por impedimento do dono, iguais nos dois canais (so o
93
+ * marcador e o recuo mudam). Cada linha diz a thread, a fase, o comando exato e o retry que vem depois.
94
+ */
95
+ function linhasDosImpedimentos(r, marcador, recuo) {
96
+ const impedimentos = r.impedimentosDoDono ?? [];
97
+ if (!impedimentos.length)
98
+ return [];
99
+ const mostrados = impedimentos.slice(0, exports.TETO_DE_IMPEDIMENTOS), sobra = impedimentos.length - mostrados.length;
100
+ return [`${marcador}Só você destrava, no terminal: ${impedimentos.length}`,
101
+ ...mostrados.map(i => `${recuo}• ${semQuebra(i.thread)}${i.fase ? ` ${semQuebra(i.fase)}` : ''}: ` +
102
+ `rode \`${semQuebra(i.comando)}\`; depois \`${semQuebra(i.retry)}\``),
103
+ ...(sobra > 0 ? [`${recuo}e mais ${sobra}: ork pulse`] : [])];
104
+ }
105
+ /** RM-057 (fatia 3): a linha do tempo parado por HITL de conducao, a mesma nos dois canais. */
106
+ function linhaDoHitlParado(r) {
107
+ const linha = (0, hitl_tempo_parado_1.linhaDoHitlAcimaDaMeta)(r.hitlDeConducao, { agora: r.consultadoEm });
108
+ return linha ? [linha] : [];
109
+ }
81
110
  /** RM-048 (D6): a linha do que e tecnico, igual nos dois canais. Nao pede nada ao dono. */
82
111
  function linhaDosTecnicos(r) {
83
112
  const nomes = r.threadsTecnicas.length ? ` (${nomearThreads(r.threadsTecnicas)})` : '';
@@ -136,6 +165,8 @@ function linhasDasDecisoes(r, marcador, recuo) {
136
165
  }
137
166
  /** Quando nao ha pergunta, o resumo diz isso em vez de pedir licenca para mandar nada. */
138
167
  exports.SEM_PERGUNTA_NO_RESUMO = 'Nada aqui pede resposta sua por este canal agora.';
168
+ /** RM-055 (continuacao, D3): sem pergunta, mas com impedimento do dono: o que fazer esta no bloco acima. */
169
+ exports.SEM_PERGUNTA_COM_IMPEDIMENTO = 'Nada aqui pede resposta sua por este canal agora; o que só você destrava está acima, no terminal.';
139
170
  /** A linha das perguntas prontas, uma so para os dois canais, pelo mesmo motivo da de baixo. */
140
171
  function linhaDasPerguntas(n) {
141
172
  return `Perguntas para você: ${n}`;
@@ -185,7 +216,7 @@ function abertura(r, codigo, marca, resposta) {
185
216
  return r.pergunta ? [`${marca}${r.pergunta}`, ...linhaDeResposta(codigo).map(l => `${resposta}${l}`), ''] : [];
186
217
  }
187
218
  function fecho(r) {
188
- return r.pergunta ? [] : ['', exports.SEM_PERGUNTA_NO_RESUMO];
219
+ return r.pergunta ? [] : ['', r.impedimentosDoDono?.length ? exports.SEM_PERGUNTA_COM_IMPEDIMENTO : exports.SEM_PERGUNTA_NO_RESUMO];
189
220
  }
190
221
  function textoTelegram(r, codigo) {
191
222
  const quando = (0, horario_1.formatarDataHoraRotulada)(r.consultadoEm, { agora: r.consultadoEm });
@@ -201,6 +232,8 @@ function textoTelegram(r, codigo) {
201
232
  `❓ ${linhaDasPerguntas(r.prontas)}`,
202
233
  ...(r.tecnicos ? [`🔧 ${linhaDosTecnicos(r)}`] : []),
203
234
  ...(r.consertos ? [`🔧 ${linhaDosConsertos(r.consertos)}`] : []),
235
+ ...linhaDoHitlParado(r).map(l => `⏳ ${l}`),
236
+ ...(r.impedimentosDoDono?.length ? ['', ...linhasDosImpedimentos(r, '🔑 ', ' ')] : []),
204
237
  ...(r.paradosNoCondutor?.length ? ['', ...linhasDoCondutor(r, '🚧 ', ' ')] : []),
205
238
  ...(r.acumuladas ? ['', `📥 ${linhaDoLoteGuardado(r.acumuladas)}`] : []),
206
239
  ...(r.decisoes.length || r.acimaDoLimiar.length ? ['', ...linhasDasDecisoes(r, '🧭 ', ' ')] : []),
@@ -232,6 +265,8 @@ function textoTerminal(r, codigo) {
232
265
  ? ['', ` threads travadas (${r.threadsBloqueadas.length}): ${nomearThreads(r.threadsBloqueadas)}`] : []),
233
266
  ...(r.tecnicos ? ['', ` ${linhaDosTecnicos(r)}`] : []),
234
267
  ...(r.consertos ? ['', ` ${linhaDosConsertos(r.consertos)}`] : []),
268
+ ...linhaDoHitlParado(r).flatMap(l => ['', ` ${l}`]),
269
+ ...(r.impedimentosDoDono?.length ? ['', ...linhasDosImpedimentos(r, ' ', ' ')] : []),
235
270
  ...(r.paradosNoCondutor?.length ? ['', ...linhasDoCondutor(r, ' ', ' ')] : []),
236
271
  ...(r.acumuladas ? ['', ` ${linhaDoLoteGuardado(r.acumuladas)}`] : []),
237
272
  ...(r.decisoes.length || r.acimaDoLimiar.length ? ['', ...linhasDasDecisoes(r, '', ' ').map(l => l.startsWith(' ') ? l : ` ${l}`)] : []),
@@ -0,0 +1,201 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.JANELA_DA_MEDIANA_MS = exports.META_DA_MEDIANA_DE_RESPOSTA_MS = void 0;
4
+ exports.esperasDeHitl = esperasDeHitl;
5
+ exports.resumirTempoParado = resumirTempoParado;
6
+ exports.hitlDeConducaoAgora = hitlDeConducaoAgora;
7
+ exports.lerHitlDeConducaoAgora = lerHitlDeConducaoAgora;
8
+ exports.textoDaMediana = textoDaMediana;
9
+ exports.linhaDaPerguntaParada = linhaDaPerguntaParada;
10
+ exports.linhaDoHitlAcimaDaMeta = linhaDoHitlAcimaDaMeta;
11
+ /**
12
+ * RM-057 (fatia 2): o tempo parado por HITL de conducao, medido do ledger.
13
+ *
14
+ * A metrica principal da RM-057 e o tempo em que a fabrica fica parada esperando o dono numa
15
+ * pergunta de conducao. A linha de base e a noite de 01 para 02/10/2026: mais de 10 h parada num
16
+ * "confirmo" em texto livre. A meta e mediana de resposta abaixo de 5 min e nenhum pedido em texto
17
+ * fora da excecao tecnica.
18
+ *
19
+ * O que conta como HITL de conducao: o pedido `ork.hitl/v2` de classe `pergunta` que o ork abriu
20
+ * (`hitl_requested`). A decisao informada (`decidido`) nao para nada e fica fora; o v1 e a pergunta
21
+ * nativa de uma sessao do host, com as opcoes do host, e tambem fica fora.
22
+ *
23
+ * Quando a espera acaba, na ordem em que o ledger prova:
24
+ *
25
+ * respondido o primeiro `human_gate` ou `session_answered` com o `pedidoId`;
26
+ * seguiu-recomendada o prazo venceu e o pedido declara `seguir-recomendada` (ele se resolve so);
27
+ * substituido um pedido novo para o mesmo alvo (o velho deixou de ser a pergunta);
28
+ * encerrado a thread fechou (`master_done` ou `thread_closed_admin`) sem resposta;
29
+ * aberto nada disso ainda: a espera corre ate o fim do periodo.
30
+ *
31
+ * Limite dito: o "confirmo" que um condutor pede na conversa, fora do ork, nao chega ao ledger e
32
+ * nao entra aqui. Por isso o canario `fx-pedido-colado` e a regra nos adaptadores existem.
33
+ *
34
+ * Funcoes puras sobre eventos: nao leem disco nem relogio. Ausencia de pedido e zero pedidos, e a
35
+ * mediana sem resposta e `null`, nunca zero inventado. A unica leitura de disco e
36
+ * `lerHitlDeConducaoAgora`, que so junta os ledgers e entrega a conta as funcoes puras.
37
+ *
38
+ * Fatia 3: o mesmo tempo parado chega ao `ork pulse` e ao `ork roadmap status`, com as perguntas
39
+ * abertas agora (ha quanto tempo cada uma espera) e a mediana dos ultimos 7 dias.
40
+ */
41
+ const hitl_contract_1 = require("./hitl-contract");
42
+ const horario_1 = require("./horario");
43
+ const ledger_1 = require("./ledger");
44
+ const thread_1 = require("./thread");
45
+ const locale_1 = require("./locale");
46
+ /** A meta da RM-057: mediana de resposta abaixo de 5 minutos. */
47
+ exports.META_DA_MEDIANA_DE_RESPOSTA_MS = 5 * 60_000;
48
+ const RESPOSTAS = ['human_gate', 'session_answered'];
49
+ const FECHAMENTOS_DA_THREAD = ['master_done', 'thread_closed_admin'];
50
+ function instante(valor) {
51
+ const t = typeof valor === 'string' ? Date.parse(valor) : NaN;
52
+ return Number.isFinite(t) ? t : null;
53
+ }
54
+ function formaDaResposta(p) {
55
+ const pedeTexto = p.tipoDeResposta === 'aberta' || p.respostaAceita?.tipo === 'texto';
56
+ if (!pedeTexto)
57
+ return 'selecao';
58
+ return p.dependenciaTecnica ? 'dependencia' : 'fora-da-excecao';
59
+ }
60
+ function chaveDoAlvo(p) {
61
+ try {
62
+ const alvo = (0, hitl_contract_1.alvoDoPedido)(p);
63
+ if (alvo)
64
+ return JSON.stringify(alvo);
65
+ }
66
+ catch { /* sem alvo legivel */ }
67
+ return `pedido:${String(p.id)}`;
68
+ }
69
+ /** As esperas de uma thread, em ordem de abertura. Le so o ledger dela. */
70
+ function esperasDeHitl(thread, eventos) {
71
+ const esperas = [];
72
+ const vistos = new Set();
73
+ for (const e of eventos) {
74
+ if (e.tipo !== 'hitl_requested')
75
+ continue;
76
+ const p = e.pedido;
77
+ if (!p || typeof p !== 'object' || !(0, hitl_contract_1.ehV2)(p) || p.classe !== 'pergunta' || vistos.has(String(p.id)))
78
+ continue;
79
+ const inicioMs = instante(p.criadoEm) ?? instante(e.ts);
80
+ if (inicioMs === null)
81
+ continue;
82
+ vistos.add(String(p.id));
83
+ esperas.push({ thread, pedidoId: String(p.id), fase: String(p.fase), inicioMs, fimMs: null, fim: 'aberto',
84
+ forma: formaDaResposta(p), alvo: chaveDoAlvo(p), prazoMs: instante(p.prazo),
85
+ seguir: p.acaoPadraoAoExpirar === 'seguir-recomendada' });
86
+ }
87
+ const fechar = (x, fimMs, fim) => {
88
+ if (x.fimMs === null || fimMs < x.fimMs) {
89
+ x.fimMs = Math.max(fimMs, x.inicioMs);
90
+ x.fim = fim;
91
+ }
92
+ };
93
+ for (const x of esperas) {
94
+ const resposta = eventos.find(e => RESPOSTAS.includes(e.tipo) && String(e.pedidoId) === x.pedidoId && instante(e.ts) !== null);
95
+ if (resposta)
96
+ fechar(x, instante(resposta.ts), 'respondido');
97
+ if (x.seguir && x.prazoMs !== null)
98
+ fechar(x, x.prazoMs, 'seguiu-recomendada');
99
+ const novo = esperas.find(y => y !== x && y.alvo === x.alvo && y.inicioMs > x.inicioMs);
100
+ if (novo)
101
+ fechar(x, novo.inicioMs, 'substituido');
102
+ const fechou = eventos.find(e => FECHAMENTOS_DA_THREAD.includes(e.tipo) && (instante(e.ts) ?? -Infinity) >= x.inicioMs);
103
+ if (fechou)
104
+ fechar(x, instante(fechou.ts), 'encerrado');
105
+ }
106
+ return esperas.map(({ alvo: _a, prazoMs: _p, seguir: _s, ...x }) => x);
107
+ }
108
+ function mediana(valores) {
109
+ if (valores.length === 0)
110
+ return null;
111
+ const v = [...valores].sort((a, b) => a - b), m = Math.floor(v.length / 2);
112
+ return v.length % 2 ? v[m] : (v[m - 1] + v[m]) / 2;
113
+ }
114
+ /** Resume as esperas de todas as threads no periodo `[desdeMs, ateMs)`. */
115
+ function resumirTempoParado(esperas, desdeMs, ateMs) {
116
+ let pedidos = 0, respondidos = 0, semResposta = 0, abertos = 0, paradoMs = 0, comDependencia = 0, foraDaExcecao = 0;
117
+ const respostas = [];
118
+ for (const x of esperas) {
119
+ const fim = x.fimMs ?? ateMs;
120
+ const a = Math.max(x.inicioMs, desdeMs), b = Math.min(fim, ateMs);
121
+ if (b > a)
122
+ paradoMs += b - a;
123
+ if (x.inicioMs >= desdeMs && x.inicioMs < ateMs) {
124
+ pedidos++;
125
+ if (x.forma === 'dependencia')
126
+ comDependencia++;
127
+ if (x.forma === 'fora-da-excecao')
128
+ foraDaExcecao++;
129
+ }
130
+ if (x.fimMs === null || x.fimMs >= ateMs) {
131
+ if (x.inicioMs < ateMs)
132
+ abertos++;
133
+ continue;
134
+ }
135
+ if (x.fimMs < desdeMs)
136
+ continue;
137
+ if (x.fim === 'respondido') {
138
+ respondidos++;
139
+ respostas.push(x.fimMs - x.inicioMs);
140
+ }
141
+ else
142
+ semResposta++;
143
+ }
144
+ const med = mediana(respostas);
145
+ return {
146
+ pedidos, respondidos, semResposta, abertos, paradoMs,
147
+ medianaRespostaMs: med, maiorRespostaMs: respostas.length ? Math.max(...respostas) : null,
148
+ metaMedianaMs: exports.META_DA_MEDIANA_DE_RESPOSTA_MS, dentroDaMeta: med === null ? null : med < exports.META_DA_MEDIANA_DE_RESPOSTA_MS,
149
+ emTexto: { comDependenciaTecnica: comDependencia, foraDaExcecao },
150
+ };
151
+ }
152
+ /** RM-057 (fatia 3): a janela da mediana que o pulse e o status mostram. */
153
+ exports.JANELA_DA_MEDIANA_MS = 7 * 24 * 60 * 60_000;
154
+ /** O retrato num instante. Pura: `quando` e o relogio de quem chama. */
155
+ function hitlDeConducaoAgora(esperas, quando) {
156
+ const agoraMs = Date.parse(quando);
157
+ const abertas = esperas
158
+ .filter(x => x.inicioMs <= agoraMs && (x.fimMs === null || x.fimMs > agoraMs))
159
+ .sort((a, b) => a.inicioMs - b.inicioMs || a.thread.localeCompare(b.thread))
160
+ .map(x => ({ thread: x.thread, pedidoId: x.pedidoId, fase: x.fase, desdeEm: new Date(x.inicioMs).toISOString(),
161
+ paradaHaMin: Math.floor((agoraMs - x.inicioMs) / 60_000) }));
162
+ const seteDias = resumirTempoParado(esperas, agoraMs - exports.JANELA_DA_MEDIANA_MS, agoraMs);
163
+ const abertaAcima = abertas.some(a => (agoraMs - Date.parse(a.desdeEm)) > exports.META_DA_MEDIANA_DE_RESPOSTA_MS);
164
+ return { abertas, seteDias, acimaDaMeta: abertaAcima || seteDias.dentroDaMeta === false };
165
+ }
166
+ /** Junta os ledgers de todas as threads do projeto e devolve o retrato. Ledger ilegivel fica de fora. */
167
+ function lerHitlDeConducaoAgora(raiz, quando) {
168
+ const esperas = [];
169
+ for (const id of (0, thread_1.listarIds)(raiz)) {
170
+ try {
171
+ esperas.push(...esperasDeHitl(id, (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, id))));
172
+ }
173
+ catch { /* ledger ilegivel nao para o pulse */ }
174
+ }
175
+ return hitlDeConducaoAgora(esperas, quando);
176
+ }
177
+ const emMinutos = (ms) => `${(ms / 60_000).toFixed(1)} min`;
178
+ /** "mediana de 7 dias 3.0 min (dentro da meta de 5 min)", ou sem resposta medida. */
179
+ function textoDaMediana(h) {
180
+ const s = h.seteDias;
181
+ if (s.medianaRespostaMs === null)
182
+ return (0, locale_1.msg)().pulse.medianaSemResposta;
183
+ return (0, locale_1.msg)().pulse.mediana(emMinutos(s.medianaRespostaMs), s.dentroDaMeta, s.respondidos);
184
+ }
185
+ /** "<thread> (<fase>) parada ha 12min, desde 01:02": a hora no fuso do dono (RM-035). */
186
+ function linhaDaPerguntaParada(p, opcoes) {
187
+ return (0, locale_1.msg)().pulse.perguntaParada(p.thread, p.fase, (0, horario_1.duracaoRelativa)(p.paradaHaMin), (0, horario_1.formatarHora)(p.desdeEm, opcoes));
188
+ }
189
+ /**
190
+ * A linha unica do resumo do pulse, so quando passou da meta: quantas abertas, a mais antiga e a
191
+ * mediana. Abaixo da meta, nada: o resumo nao ganha uma linha que nao pede atencao.
192
+ */
193
+ function linhaDoHitlAcimaDaMeta(h, opcoes) {
194
+ if (!h || !h.acimaDaMeta)
195
+ return null;
196
+ const velha = h.abertas[0];
197
+ const abertas = h.abertas.length
198
+ ? `${h.abertas.length} pergunta(s) de condução aberta(s), a mais antiga ${linhaDaPerguntaParada(velha, opcoes)}; `
199
+ : 'nenhuma pergunta de condução aberta; ';
200
+ return `HITL de condução acima da meta de 5 min: ${abertas}${textoDaMediana(h)}.`;
201
+ }
@@ -10,7 +10,7 @@
10
10
  *
11
11
  * "Inequivoca" nao e julgamento de ninguem. E esta regra, fechada e pura:
12
12
  *
13
- * 1. o texto tem a forma de uma resposta: letra a-d, digito 1-4, ou uma palavra do VOCABULARIO
13
+ * 1. o texto tem a forma de uma resposta: letra a-e, digito 1-5, ou uma palavra do VOCABULARIO
14
14
  * (que e uma lista, e nao um classificador), sozinha ou numerada ("1. B, 2. aprovo");
15
15
  * 2. a palavra casa com UMA alternativa, pela acao dela ("aprovo" casa `aprovar`; "nao" num gate
16
16
  * casa `recusar` e `esperar`, entao e ambigua e volta como pergunta);
@@ -62,7 +62,7 @@ const PALAVRAS_DA_FORMA = 'pode seguir|pode ir|pode|sim|s|ok|aprovo|aprovado|apr
62
62
  'confirmo|confirmado|de acordo|agora n[aã]o|n[aã]o|n|recuso|recusado|recusar|reprovo|reprovado|revisar|revis[aã]o|refazer|' +
63
63
  'volta|voltar|esperar|espera|aguardar|aguarda|aguarde|depois|mais tarde|detalhes?|evid[eê]ncias?';
64
64
  /** Uma resposta: letra, ou palavra do vocabulario. */
65
- const RESPOSTA_DA_FORMA = `(?:[a-d]|${PALAVRAS_DA_FORMA})`;
65
+ const RESPOSTA_DA_FORMA = `(?:[a-e]|${PALAVRAS_DA_FORMA})`;
66
66
  /**
67
67
  * Um item numerado: "1a", "1 a", "1. B", "1) aprovo", "1 - nao", "1: detalhes".
68
68
  *
@@ -78,7 +78,7 @@ exports.FORMAS_DO_TEXTO_LIVRE = Object.freeze({
78
78
  /** Resposta numerada, uma ou varias, separadas por virgula, ponto e virgula, espaco ou linha. */
79
79
  lista: `^[ \\t]*${ITEM_DA_FORMA}(?:[ \\t\\r\\n,;]*${ITEM_DA_FORMA})*[ \\t]*(?:[.!][ \\t]*)?$`,
80
80
  /** Resposta solta: letra, digito ou palavra, sozinha na mensagem. */
81
- livre: `^[ \\t]*(?:[1-4]|${RESPOSTA_DA_FORMA})[ \\t]*(?:[.!]+[ \\t]*)?$`,
81
+ livre: `^[ \\t]*(?:[1-5]|${RESPOSTA_DA_FORMA})[ \\t]*(?:[.!]+[ \\t]*)?$`,
82
82
  });
83
83
  /** Minusculas, sem acento, espaco unico, sem pontuacao final. */
84
84
  function normalizarLivre(texto) {
@@ -87,9 +87,9 @@ function normalizarLivre(texto) {
87
87
  /** O que um pedaco de resposta quer dizer, ou `null` quando ele nao e resposta. */
88
88
  function lerTrecho(bruto) {
89
89
  const t = normalizarLivre(bruto);
90
- if (/^[a-d]$/.test(t))
90
+ if (/^[a-e]$/.test(t))
91
91
  return { tipo: 'letra', letra: t };
92
- if (/^[1-4]$/.test(t))
92
+ if (/^[1-5]$/.test(t))
93
93
  return { tipo: 'digito', numero: Number(t) };
94
94
  for (const [intencao, palavras] of Object.entries(exports.VOCABULARIO_LIVRE)) {
95
95
  if (palavras.includes(t))