@orkastery/cli 0.5.1 → 0.5.3

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 (110) hide show
  1. package/adapters/claude-code/agents/ork-master.md +2 -0
  2. package/adapters/claude-code/commands/master.md +8 -0
  3. package/adapters/claude-code/commands/ork.md +9 -0
  4. package/adapters/codex/skills/ork/SKILL.md +18 -3
  5. package/adapters/hermes/README.md +1 -0
  6. package/adapters/hermes/bin/ork-network-status.sh +10 -0
  7. package/adapters/hermes/hermes.plugin.json +1 -0
  8. package/adapters/hermes/hitl-ingress/__init__.py +2 -2
  9. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +6 -5
  10. package/adapters/openclaw/README.md +24 -4
  11. package/adapters/openclaw/dist/hitl-ingress.js +2 -2
  12. package/adapters/openclaw/dist/index.js +46 -11
  13. package/adapters/openclaw/openclaw.plugin.json +5 -1
  14. package/adapters/openclaw/package.json +2 -2
  15. package/adapters/openclaw/src/hitl-ingress.ts +2 -2
  16. package/adapters/openclaw/src/index.ts +49 -11
  17. package/assets/orkmind_bridge.py +117 -9
  18. package/dist/adapters/claude-bg.js +39 -5
  19. package/dist/adapters/codex.js +4 -3
  20. package/dist/agents-md.js +1 -0
  21. package/dist/auditrun.js +5 -1
  22. package/dist/board.js +16 -14
  23. package/dist/branch-de-estado.js +66 -10
  24. package/dist/busca-semantica.js +52 -12
  25. package/dist/canarios-rm057.js +149 -0
  26. package/dist/canarios.js +2 -0
  27. package/dist/ci.js +8 -1
  28. package/dist/claims.js +39 -1
  29. package/dist/company-brain-context.js +158 -22
  30. package/dist/docs.js +48 -9
  31. package/dist/doctor-pulse-cron.js +194 -0
  32. package/dist/doctor-rede.js +149 -0
  33. package/dist/doctor.js +89 -9
  34. package/dist/entrega-pr.js +20 -8
  35. package/dist/esgotamentos.js +181 -0
  36. package/dist/experiencia.js +12 -2
  37. package/dist/fabrica-estado.js +14 -6
  38. package/dist/fabrica-publicar.js +6 -0
  39. package/dist/fechamento.js +20 -66
  40. package/dist/forja.js +31 -7
  41. package/dist/gates.js +5 -1
  42. package/dist/hitl-contract.js +59 -4
  43. package/dist/hitl-curto.js +11 -6
  44. package/dist/hitl-gates.js +5 -0
  45. package/dist/hitl-lote.js +5 -2
  46. package/dist/hitl-resumo.js +9 -0
  47. package/dist/hitl-tempo-parado.js +201 -0
  48. package/dist/hitl-texto-livre.js +5 -5
  49. package/dist/horario.js +27 -0
  50. package/dist/hosts.js +52 -10
  51. package/dist/impedimento.js +205 -0
  52. package/dist/index.js +391 -74
  53. package/dist/indice-vetorial.js +87 -10
  54. package/dist/indice.js +3 -1
  55. package/dist/init.js +17 -0
  56. package/dist/integracoes-locais.js +2 -0
  57. package/dist/intelligence-graph-cli.js +91 -8
  58. package/dist/intelligence-graph-contexto.js +215 -0
  59. package/dist/intelligence-graph-parsers.js +6 -4
  60. package/dist/leases.js +664 -87
  61. package/dist/ledger-stats.js +15 -1
  62. package/dist/ledger.js +3 -0
  63. package/dist/licoes.js +1 -0
  64. package/dist/maestro-runtime.js +4 -1
  65. package/dist/manifest.js +20 -1
  66. package/dist/master-nota.js +14 -5
  67. package/dist/master.js +129 -12
  68. package/dist/mcp-artifacts.js +34 -3
  69. package/dist/mcp-git.js +20 -3
  70. package/dist/mcp-grafo-worker.js +5 -3
  71. package/dist/mcp-grafo.js +50 -4
  72. package/dist/mcp-server.js +14 -1
  73. package/dist/memoria.js +48 -6
  74. package/dist/network-roadmap.js +191 -16
  75. package/dist/ocupacao.js +11 -3
  76. package/dist/orkmind.js +90 -5
  77. package/dist/orquestracao.js +12 -10
  78. package/dist/parado-no-condutor.js +188 -32
  79. package/dist/phase.js +68 -15
  80. package/dist/policies.js +52 -1
  81. package/dist/portfolio.js +4 -1
  82. package/dist/projeto-alvo.js +5 -1
  83. package/dist/prompts.js +36 -1
  84. package/dist/prova-ativacao.js +75 -6
  85. package/dist/pulse-delivery.js +34 -2
  86. package/dist/pulse.js +49 -3
  87. package/dist/ratelimit.js +20 -4
  88. package/dist/recall.js +3 -1
  89. package/dist/rede-adesao.js +399 -0
  90. package/dist/rede-forja.js +289 -0
  91. package/dist/rede-projetos.js +239 -0
  92. package/dist/rede-status.js +336 -0
  93. package/dist/rede.js +980 -0
  94. package/dist/retry.js +69 -2
  95. package/dist/roadmap-status.js +23 -3
  96. package/dist/runtime-profiles.js +22 -1
  97. package/dist/saida-segura.js +58 -0
  98. package/dist/session-watcher-claude.js +113 -23
  99. package/dist/sessoes-contas.js +187 -0
  100. package/dist/sessoes-inventario.js +6 -18
  101. package/dist/sessoes.js +83 -2
  102. package/dist/ship.js +85 -19
  103. package/dist/thread.js +192 -13
  104. package/dist/worktree.js +5 -2
  105. package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
  106. package/package.json +7 -3
  107. package/references/definition-of-done.md +1 -1
  108. package/skills/README.md +1 -1
  109. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  110. package/skills/phases/master-metrics/SKILL.md +8 -3
@@ -0,0 +1,181 @@
1
+ "use strict";
2
+ /**
3
+ * RM-040 (B7): `ork accounts esgotamentos`, a medida da metrica do item.
4
+ *
5
+ * A metrica da RM-040 e "zero despacho numa conta que outro projeto do mesmo usuario ja viu
6
+ * esgotada, dentro do prazo dela". Este relatorio so le: as marcas do registro compartilhado
7
+ * (`~/.orkastery/private/contas.json`) e os `phase_dispatch` com perfil dos ledgers de cada projeto
8
+ * registrado nesta maquina (`~/.orkastery/projetos.json`). Conta o despacho que caiu na conta de
9
+ * uma marca, dentro da janela dela, vindo de projeto diferente do que marcou.
10
+ *
11
+ * A saida nunca leva diretorio de conta: a conta vira um id opaco (sha256 da chave, 12 hex), o
12
+ * projeto sai pelo nome registrado e o perfil, pelo id.
13
+ *
14
+ * Limite declarado: o registro so guarda as marcas vivas. Uso bem sucedido, login reconferido e
15
+ * prazo vencido apagam a marca na gravacao seguinte; por isso o numero e um piso, e a leitura vale
16
+ * enquanto a marca existe.
17
+ */
18
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
19
+ if (k2 === undefined) k2 = k;
20
+ var desc = Object.getOwnPropertyDescriptor(m, k);
21
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
22
+ desc = { enumerable: true, get: function() { return m[k]; } };
23
+ }
24
+ Object.defineProperty(o, k2, desc);
25
+ }) : (function(o, m, k, k2) {
26
+ if (k2 === undefined) k2 = k;
27
+ o[k2] = m[k];
28
+ }));
29
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
30
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
31
+ }) : function(o, v) {
32
+ o["default"] = v;
33
+ });
34
+ var __importStar = (this && this.__importStar) || (function () {
35
+ var ownKeys = function(o) {
36
+ ownKeys = Object.getOwnPropertyNames || function (o) {
37
+ var ar = [];
38
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
39
+ return ar;
40
+ };
41
+ return ownKeys(o);
42
+ };
43
+ return function (mod) {
44
+ if (mod && mod.__esModule) return mod;
45
+ var result = {};
46
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
47
+ __setModuleDefault(result, mod);
48
+ return result;
49
+ };
50
+ })();
51
+ Object.defineProperty(exports, "__esModule", { value: true });
52
+ exports.NAO_REGISTRADO = exports.LIMITE_DO_RELATORIO = exports.CONTRATO_ESGOTAMENTOS = void 0;
53
+ exports.contaOpaca = contaOpaca;
54
+ exports.fimDaJanela = fimDaJanela;
55
+ exports.relatorioDeEsgotamentos = relatorioDeEsgotamentos;
56
+ exports.textoDosEsgotamentos = textoDosEsgotamentos;
57
+ const node_crypto_1 = require("node:crypto");
58
+ const fs = __importStar(require("node:fs"));
59
+ const horario_1 = require("./horario");
60
+ const ledger_1 = require("./ledger");
61
+ const ledger_stats_1 = require("./ledger-stats");
62
+ const projeto_alvo_1 = require("./projeto-alvo");
63
+ const runtime_profiles_1 = require("./runtime-profiles");
64
+ const thread_1 = require("./thread");
65
+ const util_1 = require("./util");
66
+ exports.CONTRATO_ESGOTAMENTOS = 'ork.esgotamentos/v1';
67
+ exports.LIMITE_DO_RELATORIO = 'o registro guarda so as marcas vivas (uso bem sucedido, login reconferido e prazo vencido ' +
68
+ 'apagam a marca): o total e um piso, valido enquanto a marca existe';
69
+ exports.NAO_REGISTRADO = 'nao registrado';
70
+ function contaOpaca(chave) {
71
+ return (0, node_crypto_1.createHash)('sha256').update(chave).digest('hex').slice(0, 12);
72
+ }
73
+ function caminhoReal(p) {
74
+ try {
75
+ return fs.realpathSync(p);
76
+ }
77
+ catch {
78
+ return p;
79
+ }
80
+ }
81
+ /** Fim da janela da marca: o prazo para cota esgotada; sem prazo (login, credencial paga), aberta. */
82
+ function fimDaJanela(m) {
83
+ if (m.estado !== 'esgotado')
84
+ return Number.POSITIVE_INFINITY;
85
+ const ate = m.esgotadoAte === null ? NaN : Date.parse(m.esgotadoAte);
86
+ return Number.isFinite(ate) ? ate : Number.NEGATIVE_INFINITY;
87
+ }
88
+ function relatorioDeEsgotamentos(opcoes = {}) {
89
+ const agoraMs = opcoes.agoraMs ?? Date.now();
90
+ const desdeMs = (0, ledger_stats_1.parsePeriodo)(opcoes.desde ?? '7d', agoraMs);
91
+ if (desdeMs >= agoraMs)
92
+ throw Error('ledger.period.order');
93
+ // Um projeto por raiz real: o registro pode trazer a mesma copia por dois caminhos.
94
+ const vistos = new Set();
95
+ const projetos = (0, projeto_alvo_1.listarProjetos)().filter((p) => {
96
+ const real = caminhoReal(p.raiz);
97
+ if (vistos.has(real))
98
+ return false;
99
+ vistos.add(real);
100
+ return true;
101
+ }).map((p) => ({ ...p, real: caminhoReal(p.raiz) }));
102
+ const nomeDaRaiz = (raiz) => projetos.find((p) => p.real === caminhoReal(raiz))?.nome ?? exports.NAO_REGISTRADO;
103
+ const contas = (0, runtime_profiles_1.lerContasCompartilhadas)();
104
+ const marcas = contas.map((c) => ({ c, real: caminhoReal(c.projeto), inicio: Date.parse(c.em), fim: fimDaJanela(c) }));
105
+ const despachos = [];
106
+ const lidos = [];
107
+ for (const p of projetos) {
108
+ let comPerfil = 0;
109
+ let ids = [];
110
+ try {
111
+ ids = p.presente ? (0, thread_1.listarIds)(p.raiz) : [];
112
+ }
113
+ catch {
114
+ ids = [];
115
+ }
116
+ for (const id of ids) {
117
+ let eventos = [];
118
+ try {
119
+ eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(p.raiz, id));
120
+ }
121
+ catch {
122
+ continue;
123
+ }
124
+ for (const e of eventos) {
125
+ if (e.tipo !== 'phase_dispatch' || e.perfil === undefined || e.perfil === null)
126
+ continue;
127
+ const t = Date.parse(String(e.ts));
128
+ if (!Number.isFinite(t) || t < desdeMs || t >= agoraMs)
129
+ continue;
130
+ let perfil, chave;
131
+ try {
132
+ perfil = (0, runtime_profiles_1.validarPerfilDeDespacho)(e.perfil);
133
+ chave = (0, runtime_profiles_1.chaveDaConta)(perfil);
134
+ }
135
+ catch {
136
+ continue;
137
+ }
138
+ comPerfil++;
139
+ const marca = marcas.find((m) => m.c.chave === chave && m.real !== p.real && t >= m.inicio && t < m.fim);
140
+ if (!marca)
141
+ continue;
142
+ despachos.push({ em: new Date(t).toISOString(), projeto: p.nome, thread: String(e.thread ?? id),
143
+ fase: typeof e.fase === 'string' ? e.fase : null, runtime: perfil.runtime, perfil: perfil.id,
144
+ conta: contaOpaca(chave), marcadaPor: nomeDaRaiz(marca.c.projeto) });
145
+ }
146
+ }
147
+ lidos.push({ nome: p.nome, presente: p.presente, despachosComPerfil: comPerfil });
148
+ }
149
+ despachos.sort((a, b) => a.em.localeCompare(b.em));
150
+ return {
151
+ contrato: exports.CONTRATO_ESGOTAMENTOS,
152
+ periodo: { desde: new Date(desdeMs).toISOString(), ate: new Date(agoraMs).toISOString(), intervalo: '[desde,ate)' },
153
+ marcas: contas.map((c) => ({ conta: contaOpaca(c.chave), runtime: c.chave.slice(0, c.chave.indexOf(':')), estado: c.estado,
154
+ motivo: c.motivo, em: c.em, esgotadoAte: c.esgotadoAte, projeto: nomeDaRaiz(c.projeto) })),
155
+ projetos: lidos,
156
+ despachos,
157
+ total: despachos.length,
158
+ limite: exports.LIMITE_DO_RELATORIO,
159
+ };
160
+ }
161
+ /** Texto para o terminal, com o horario do dono e o fuso uma vez no fim. */
162
+ function textoDosEsgotamentos(r) {
163
+ const linhas = [
164
+ `Esgotamentos de conta ${(0, horario_1.formatarDataHora)(r.periodo.desde)} ate ${(0, horario_1.formatarDataHora)(r.periodo.ate)} ${r.periodo.intervalo}`,
165
+ ` marcas vivas no registro: ${r.marcas.length}`,
166
+ ];
167
+ for (const m of r.marcas) {
168
+ const ate = m.estado === 'esgotado' ? `ate ${(0, horario_1.formatarDataHora)(m.esgotadoAte)}` : 'sem prazo';
169
+ linhas.push(` conta ${m.conta} ${m.runtime} ${m.estado} desde ${(0, horario_1.formatarDataHora)(m.em)}, ${ate} visto em ${m.projeto} (${m.motivo})`);
170
+ }
171
+ linhas.push(` projetos lidos: ${r.projetos.length === 0 ? 'nenhum registrado'
172
+ : r.projetos.map((p) => `${p.nome} (${p.presente ? `${p.despachosComPerfil} despacho(s) com perfil` : 'fora do disco'})`).join(', ')}`);
173
+ linhas.push(` despachos em conta marcada por outro projeto: ${r.total}`);
174
+ if (r.despachos.length > 0) {
175
+ linhas.push((0, util_1.tabela)(['QUANDO', 'PROJETO', 'THREAD', 'FASE', 'PERFIL', 'CONTA', 'MARCADA POR'], r.despachos.map((d) => [(0, horario_1.formatarDataHora)(d.em), d.projeto, d.thread, d.fase ?? '-', d.perfil, d.conta, d.marcadaPor]))
176
+ .split('\n').map((l) => ` ${l}`).join('\n'));
177
+ }
178
+ linhas.push(` limite: ${r.limite}`);
179
+ linhas.push((0, horario_1.legendaDoFuso)());
180
+ return linhas.join('\n');
181
+ }
@@ -1,5 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.IDIOMA_PADRAO = void 0;
3
4
  exports.normalizarIdioma = normalizarIdioma;
4
5
  exports.validarPreferencias = validarPreferencias;
5
6
  exports.idiomaDoSistema = idiomaDoSistema;
@@ -44,6 +45,11 @@ function validarPreferencias(valor) {
44
45
  }
45
46
  return resultado;
46
47
  }
48
+ /**
49
+ * Ensaio de 03/10 (R2): sem idioma escolhido, vale a língua da CLI. Antes era en-US, e uma máquina com
50
+ * LANG=C.UTF-8 (contêiner, CI, servidor) recebia a experiência em inglês de uma CLI que fala português.
51
+ */
52
+ exports.IDIOMA_PADRAO = 'pt-BR';
47
53
  /** C, POSIX e und (raiz do ICU) dizem que o sistema não escolheu idioma: vale o padrão. */
48
54
  const SEM_IDIOMA = new Set(['c', 'posix', 'und']);
49
55
  function idiomaEscolhido(valor) {
@@ -52,11 +58,15 @@ function idiomaEscolhido(valor) {
52
58
  }
53
59
  function idiomaDoSistema(env = process.env, doIntl = () => Intl.DateTimeFormat().resolvedOptions().locale) {
54
60
  const locale = env.LC_ALL || env.LC_MESSAGES || env.LANG;
55
- return idiomaEscolhido(locale?.split(/[.@]/)[0].replace(/_/g, '-')) ?? idiomaEscolhido(doIntl()) ?? 'en-US';
61
+ if (locale) {
62
+ // Com C ou POSIX explícito, o Intl do Node cai no en-US do ICU: ele não diz a escolha de ninguém.
63
+ return idiomaEscolhido(locale.split(/[.@]/)[0].replace(/_/g, '-')) ?? exports.IDIOMA_PADRAO;
64
+ }
65
+ return idiomaEscolhido(doIntl()) ?? exports.IDIOMA_PADRAO;
56
66
  }
57
67
  function resolverExperiencia(owner = {}, sistema = { language: idiomaDoSistema(), timezone: (0, horario_1.fusoDoSistema)() }) {
58
68
  const p = validarPreferencias(owner);
59
- const language = p.language ?? normalizarIdioma(sistema.language) ?? 'en-US';
69
+ const language = p.language ?? normalizarIdioma(sistema.language) ?? exports.IDIOMA_PADRAO;
60
70
  return {
61
71
  language, timezone: p.timezone ?? (0, horario_1.normalizarFuso)(sistema.timezone) ?? 'UTC',
62
72
  depth: p.depth ?? 'curta', experience: p.experience ?? true,
@@ -81,6 +81,7 @@ const roadmap_reservas_1 = require("./roadmap-reservas");
81
81
  const thread_1 = require("./thread");
82
82
  const util_1 = require("./util");
83
83
  const versao_1 = require("./versao");
84
+ const saida_segura_1 = require("./saida-segura");
84
85
  exports.CONTRATO_MAQUINA = 'ork.fabrica-maquina/v1';
85
86
  exports.BRANCH_DA_FABRICA = 'ork/fabrica-estado';
86
87
  /** O diretorio dos retratos na branch; a leitura sem clone (RM-054) le o mesmo. */
@@ -234,7 +235,8 @@ function gravarMarca(raiz, marca) {
234
235
  */
235
236
  function publicarMaquina(carregado, opcoes = {}) {
236
237
  const raiz = carregado.raiz;
237
- const remoto = opcoes.remoto ?? carregado.manifesto.fabrica.remoto;
238
+ // RM-047: antes do retrato e da marca, para o remoto invalido recusar mesmo sem mudanca a enviar.
239
+ const remoto = (0, branch_de_estado_1.exigirRemoto)(opcoes.remoto ?? carregado.manifesto.fabrica.remoto, PREFIXO);
238
240
  const estado = retratoDaMaquina(carregado, { ...opcoes, remoto });
239
241
  const assinatura = assinaturaDoRetrato(estado);
240
242
  const marca = lerMarca(raiz);
@@ -274,7 +276,7 @@ function publicarMaquina(carregado, opcoes = {}) {
274
276
  /** `ork fabrica sair`: tira o retrato desta maquina da branch. `null` quando nao havia retrato. */
275
277
  function removerMaquina(carregado, opcoes = {}) {
276
278
  const raiz = carregado.raiz;
277
- const remoto = opcoes.remoto ?? carregado.manifesto.fabrica.remoto;
279
+ const remoto = (0, branch_de_estado_1.exigirRemoto)(opcoes.remoto ?? carregado.manifesto.fabrica.remoto, PREFIXO);
278
280
  const maquina = (0, roadmap_reservas_1.quemSouEu)(raiz, { maquina: opcoes.maquina }).maquina;
279
281
  for (let tentativa = 1; tentativa <= TENTATIVAS; tentativa++) {
280
282
  const { ponta, atualizado } = (0, branch_de_estado_1.buscarBranch)(raiz, remoto, exports.BRANCH_DA_FABRICA, PREFIXO, 30000);
@@ -331,8 +333,13 @@ function blocoDaMaquina(m, eu) {
331
333
  t.id, t.modo, t.fase, runtimeDaThread(t), t.roadmap ?? '-', t.esperaVoce ? `sim: ${curto(t.pergunta ?? 'veredito', 40)}` : '-',
332
334
  ]))];
333
335
  }
334
- /** `ork fabrica`: todas as maquinas. */
335
- function textoDaFabrica(p, eu) {
336
+ /**
337
+ * `ork fabrica`: todas as maquinas. RM-053 (fatia 2): o painel vem do remoto, onde outras pessoas
338
+ * escrevem; cada valor dele (o `por`, o `projeto`, a pergunta) chega a tela numa linha so, sem nada
339
+ * que o terminal execute.
340
+ */
341
+ function textoDaFabrica(painel, eu) {
342
+ const p = (0, saida_segura_1.valoresEmUmaLinha)(painel);
336
343
  const linhas = [];
337
344
  if (!p.atualizado)
338
345
  linhas.push('AVISO: sem leitura nova do remoto; esta e a ultima copia lida nesta maquina.', '');
@@ -345,8 +352,9 @@ function textoDaFabrica(p, eu) {
345
352
  linhas.push((0, horario_1.legendaDoFuso)());
346
353
  return linhas.join('\n');
347
354
  }
348
- /** A secao de `ork board` com as outras maquinas. */
349
- function textoDasOutrasMaquinas(p, eu) {
355
+ /** A secao de `ork board` com as outras maquinas, saneada como o `ork fabrica`. */
356
+ function textoDasOutrasMaquinas(painel, eu) {
357
+ const p = (0, saida_segura_1.valoresEmUmaLinha)(painel);
350
358
  const outras = p.maquinas.filter((m) => m.maquina !== eu);
351
359
  const origem = p.atualizado ? 'lido agora' : 'ultima copia local';
352
360
  if (outras.length === 0)
@@ -39,18 +39,24 @@ exports.publicarEmSegundoPlano = publicarEmSegundoPlano;
39
39
  *
40
40
  * Modulo a parte, com o minimo de dependencias, porque quem chama (despacho de fase, entrega,
41
41
  * thread nova) nao pode esperar rede nem arrastar o retrato inteiro para dentro de si.
42
+ *
43
+ * RM-053 (D9): o mesmo evento tambem dispara o retrato desta maquina na rede da pessoa, pela
44
+ * `rede-adesao` (tao leve quanto este modulo): so membro, no maximo uma vez a cada 14 minutos.
42
45
  */
43
46
  const node_child_process_1 = require("node:child_process");
44
47
  const fs = __importStar(require("node:fs"));
45
48
  const path = __importStar(require("node:path"));
46
49
  const maquina_1 = require("./maquina");
47
50
  const manifest_1 = require("./manifest");
51
+ const rede_adesao_1 = require("./rede-adesao");
48
52
  /**
49
53
  * Publica sem segurar quem chamou (thread nova, despacho de fase, entrega). So com a fabrica
50
54
  * compartilhada nesta maquina (`ork fabrica entrar`) ou no projeto; `ORK_FABRICA_PUBLICAR=0`
51
55
  * desliga (testes e `ork eval`).
52
56
  */
53
57
  function publicarEmSegundoPlano(raiz) {
58
+ // A rede decide por si (adesao, teto e ambiente); o retorno continua sendo o da fabrica.
59
+ (0, rede_adesao_1.publicarRedeEmSegundoPlano)({ diretorio: raiz });
54
60
  if (process.env.ORK_FABRICA_PUBLICAR === '0')
55
61
  return false;
56
62
  let carregado;
@@ -1,37 +1,4 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.liberarAoFechar = liberarAoFechar;
37
4
  /**
@@ -43,61 +10,48 @@ exports.liberarAoFechar = liberarAoFechar;
43
10
  * derruba. Cada parte roda isolada (achado A1 do CHECK 1): erro de E/S numa nao impede a outra, e o
44
11
  * que nao saiu agora sai na poda do proximo pedido de regiao ou no `ork roadmap reservas --soltar-orfas`.
45
12
  */
46
- const fs = __importStar(require("node:fs"));
47
- const path = __importStar(require("node:path"));
48
- const estado_thread_1 = require("./estado-thread");
49
13
  const ledger_1 = require("./ledger");
50
14
  const leases_1 = require("./leases");
51
15
  const roadmap_reservas_1 = require("./roadmap-reservas");
16
+ const sessoes_1 = require("./sessoes");
52
17
  const thread_1 = require("./thread");
53
18
  function liberarAoFechar(raiz, threadId) {
54
- const saida = { leases: [], fila: [], reservas: [], falhas: [] };
19
+ const saida = { leases: [], fila: [], reservas: [], sessoes: [], falhas: [] };
55
20
  const falhou = (parte, e) => saida.falhas.push(`${parte}: ${e?.message ?? String(e)}`);
56
- // Os leases de regiao sao por checkout: o de quem fecha, a raiz do projeto e, quando a thread tem
57
- // worktree, os pegos de la (sugestao 6 do CHECK 2: fechar da worktree tambem solta os da raiz).
58
- const real = (p) => { try {
59
- return fs.realpathSync(p);
60
- }
61
- catch {
62
- return path.resolve(p);
63
- } };
64
- const candidatos = [raiz];
21
+ // RM-036 (D6): os leases de regiao moram todos no estado canonico, chamado de qualquer checkout, e a
22
+ // soltura de la ja alcanca o legado que a versao anterior gravou nas worktrees. Uma soltura basta.
65
23
  try {
66
- candidatos.push((0, estado_thread_1.raizDoEstado)(raiz));
24
+ const solto = (0, leases_1.soltarThreadFechada)(raiz, threadId, 'fechamento');
25
+ saida.leases.push(...solto.leases);
26
+ saida.fila.push(...solto.fila);
67
27
  }
68
28
  catch (e) {
69
- falhou('raiz', e);
29
+ falhou(`leases em ${raiz}`, e);
70
30
  }
71
31
  try {
72
- const wt = (0, thread_1.lerThread)(raiz, threadId).worktree;
73
- if (wt && fs.existsSync(wt))
74
- candidatos.push(wt);
32
+ saida.reservas = (0, roadmap_reservas_1.soltarReservaDaThread)(raiz, threadId);
75
33
  }
76
34
  catch (e) {
77
- falhou('thread', e);
78
- }
79
- const checkouts = candidatos.filter((c, i) => candidatos.findIndex((x) => real(x) === real(c)) === i);
80
- for (const checkout of checkouts) {
81
- try {
82
- const solto = (0, leases_1.soltarThreadFechada)(checkout, threadId, 'fechamento');
83
- saida.leases.push(...solto.leases);
84
- saida.fila.push(...solto.fila);
85
- }
86
- catch (e) {
87
- falhou(`leases em ${checkout}`, e);
88
- }
35
+ falhou('reserva', e);
89
36
  }
37
+ // RM-056 (ao fechar): a sessao fantasma presa a thread fechada seguia no inventario e no doctor ate
38
+ // alguem rodar `ork sessions limpar-fantasmas` (624f65db e d38ae1d4, 03/10). So consulta o runtime
39
+ // quando o ledger tem sessao sem fim; nunca para nem remove nada no runtime, e sessao viva fica.
90
40
  try {
91
- saida.reservas = (0, roadmap_reservas_1.soltarReservaDaThread)(raiz, threadId);
41
+ if ((0, sessoes_1.sessoesSemFim)(raiz, threadId).length > 0) {
42
+ const r = (0, sessoes_1.limparFantasmas)(raiz, { thread: threadId, origem: 'fechamento' });
43
+ saida.sessoes = r.itens.filter((i) => i.acao === 'solto');
44
+ }
92
45
  }
93
46
  catch (e) {
94
- falhou('reserva', e);
47
+ falhou('sessoes', e);
95
48
  }
96
49
  // Sugestao 2 do CHECK 2: a falha tambem vai ao ledger da thread, de melhor esforco.
97
50
  if (saida.falhas.length > 0) {
98
51
  try {
99
52
  (0, ledger_1.registrarSeExiste)((0, thread_1.dirThread)(raiz, threadId), threadId, ledger_1.TIPOS_DE_EVENTO.solturaFalhou, { falhas: saida.falhas,
100
- correcao: 'a poda do proximo pedido de regiao solta o que ficou; reserva: ork roadmap reservas --soltar-orfas' });
53
+ correcao: 'a poda do proximo pedido de regiao solta o que ficou; reserva: ork roadmap reservas --soltar-orfas; ' +
54
+ 'sessao fantasma: ork sessions limpar-fantasmas' });
101
55
  }
102
56
  catch { /* o ledger que nao grava ja esta nas falhas */ }
103
57
  }
package/dist/forja.js CHANGED
@@ -1,6 +1,8 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.mesmaForja = exports.rotuloDaForja = exports.executorPadrao = void 0;
4
+ exports.enderecoDoRemoto = enderecoDoRemoto;
5
+ exports.repositorioGithubNoHost = repositorioGithubNoHost;
4
6
  exports.identidadeDaForja = identidadeDaForja;
5
7
  exports.forjaDoArgumento = forjaDoArgumento;
6
8
  exports.detalheSeguro = detalheSeguro;
@@ -27,14 +29,20 @@ exports.lerDaForja = lerDaForja;
27
29
  const node_child_process_1 = require("node:child_process");
28
30
  const hitl_1 = require("./hitl");
29
31
  const policies_1 = require("./policies");
32
+ const rede_forja_1 = require("./rede-forja");
30
33
  const PRAZO_MS = 30000;
31
34
  const SHA = /^[0-9a-f]{40}$/;
32
35
  const SEGMENTO = /^[A-Za-z0-9_.-]+$/;
33
36
  /** Hostname de verdade: rotulos alfanumericos separados por ponto (`..` ou `a..b` nao passam). */
34
37
  const HOST = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/;
35
- /** Sem shell, sem token: a CLI usa a autenticacao que ja tem; prompt e aviso de versao desligados. */
38
+ /**
39
+ * Sem shell, sem token: a CLI usa a autenticacao que ja tem; prompt e aviso de versao desligados.
40
+ * RM-054 (fatia 3): o binario e procurado como na rede (D11 da RM-053), no PATH e depois nas pastas de
41
+ * usuario (`~/.local/bin` e as outras): o gateway e o cron rodam com PATH curto, e o `gh` da srvjcp86
42
+ * mora em `~/.local/bin`. Sem achar, o nome vai cru ao spawn, e o ENOENT continua `forja.ausente`.
43
+ */
36
44
  const executorPadrao = (cmd, args, entrada, timeoutMs) => {
37
- const r = (0, node_child_process_1.spawnSync)(cmd, [...args], {
45
+ const r = (0, node_child_process_1.spawnSync)((0, rede_forja_1.acharBinario)(cmd) ?? cmd, [...args], {
38
46
  input: entrada, encoding: 'utf8', timeout: timeoutMs, maxBuffer: 32 * 1024 * 1024,
39
47
  env: { ...process.env, GH_PROMPT_DISABLED: '1', GH_NO_UPDATE_NOTIFIER: '1', GLAB_CHECK_UPDATE: 'false', NO_COLOR: '1' },
40
48
  });
@@ -63,10 +71,10 @@ function montar(tipo, host, caminho) {
63
71
  return { tipo, host, repo: partes.join('/') };
64
72
  }
65
73
  /**
66
- * A forja de uma URL de remoto (https, ssh ou scp). Credencial na URL nunca entra na identidade;
67
- * caminho local e forja desconhecida voltam null.
74
+ * RM-037 (fatia 5, A5): o host e o caminho de uma URL de remoto (https, ssh ou scp), de qualquer forja. Credencial
75
+ * na URL nunca entra; caminho local e host que nao e hostname voltam null.
68
76
  */
69
- function identidadeDaForja(url) {
77
+ function enderecoDoRemoto(url) {
70
78
  const t = url.trim();
71
79
  const esquema = /^(?:https?|ssh|git|git\+ssh|ssh\+git):\/\/(?:[^@/]*@)?([^/:?#@]+)(?::\d+)?\/([^?#\s]+)$/i.exec(t);
72
80
  const scp = esquema ? null : /^(?:[^@\s/:]+@)?([^:\s/@]+):(?!\/\/)([^\s]+)$/.exec(t);
@@ -74,8 +82,24 @@ function identidadeDaForja(url) {
74
82
  if (!achado)
75
83
  return null;
76
84
  const host = achado[1].toLowerCase();
77
- const tipo = tipoDoHost(host);
78
- return tipo ? montar(tipo, host, achado[2].replace(/^\/+/, '')) : null;
85
+ return HOST.test(host) ? { host, caminho: achado[2].replace(/^\/+/, '') } : null;
86
+ }
87
+ /**
88
+ * RM-037 (fatia 5, A5): o `dono/nome` de um repositorio do GitHub no host do remoto, com qualquer host: o GitHub
89
+ * Enterprise mora em host proprio, com ou sem "github" no nome. Quem diz se o host e mesmo um GitHub e o `gh`.
90
+ */
91
+ function repositorioGithubNoHost(url) {
92
+ const endereco = enderecoDoRemoto(url);
93
+ return endereco && tipoDoHost(endereco.host) !== 'gitlab' ? montar('github', endereco.host, endereco.caminho) : null;
94
+ }
95
+ /**
96
+ * A forja de uma URL de remoto (https, ssh ou scp). Credencial na URL nunca entra na identidade;
97
+ * caminho local e forja desconhecida voltam null.
98
+ */
99
+ function identidadeDaForja(url) {
100
+ const endereco = enderecoDoRemoto(url);
101
+ const tipo = endereco ? tipoDoHost(endereco.host) : null;
102
+ return endereco && tipo ? montar(tipo, endereco.host, endereco.caminho) : null;
79
103
  }
80
104
  /** `github:dono/repo`, `gitlab:grupo/sub/repo` (com host proprio: `gitlab:host.tld/grupo/repo`) ou uma URL. */
81
105
  function forjaDoArgumento(texto) {
package/dist/gates.js CHANGED
@@ -46,11 +46,15 @@ exports.DESCRICAO_DO_MOTIVO = {
46
46
  'runtime.quota-exhausted': 'a cota ou os creditos da conta do runtime acabaram (I-33: o perfil sai do rodizio e a fase segue em outro perfil, outro runtime ou na fila)',
47
47
  'runtime.auth-missing': 'a conta do runtime nao esta autenticada (I-33: o perfil nunca recebe despacho ate o login ser refeito pelo proprio CLI)',
48
48
  'runtime.model-unavailable': 'o modelo pedido nao existe ou a conta nao tem acesso a ele (RM-037: o perfil segue no rodizio e a fase vai a outro perfil com o mesmo modelo ou ao fallback do bloco)',
49
+ 'runtime.profile-invalid': 'o perfil pedido no despacho (--perfil) nao existe no store ou e de outro runtime (RM-056: o despacho nunca troca de perfil sozinho)',
50
+ 'runtime.workspace-untrusted': 'o runtime nao confia no diretorio da worktree e recusou o despacho (RM-055: so o dono aceita a confianca no terminal; depois o ork re-despacha o mesmo prompt)',
51
+ 'runtime.consent-pending': 'o runtime espera o dono aceitar termos novos e recusou o despacho (RM-055: so o dono aceita no terminal; depois o ork re-despacha o mesmo prompt)',
49
52
  'cost.violation': 'o despacho seria redirecionado para provider pago (violacao de custo: e o unico motivo que NUNCA recebe retry automatico)',
50
53
  'tree.blocked': 'a arvore de destino nao esta disponivel para o merge (branch em check-out com alteracao nao commitada, ou worktree que nao pode ser montada)',
51
54
  'lease.busy': 'lease ocupado por outra thread',
55
+ 'lease.resume-unavailable': 'retomada indisponivel; libere o lease explicitamente com --forcar',
52
56
  'conducao.em-andamento': 'outra conducao ja executa na worktree da thread (I-36): o pedido foi recusado com quem conduz e as tres acoes (esperar, acompanhar, assumir)',
53
- 'hitl.formato': 'o pedido HITL saiu fora do formato obrigatorio (pergunta em uma frase, de 2 a 4 alternativas rotuladas com consequencia, exatamente uma recomendada com o porque, corpo em lista)',
57
+ 'hitl.formato': 'o pedido HITL saiu fora do formato obrigatorio (pergunta em uma frase, de 3 a 5 alternativas rotuladas com consequencia, exatamente uma com o selo Recomendacao e o porque, corpo em lista; texto livre so com dependencia tecnica tipada)',
54
58
  'human.pending': 'gate humano do modo de conducao ainda nao autorizou',
55
59
  };
56
60
  /**
@@ -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)