@orkastery/cli 0.2.0 → 0.4.1

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 (257) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +86 -65
  3. package/adapters/README.md +30 -15
  4. package/adapters/claude-code/.claude-plugin/plugin.json +11 -5
  5. package/adapters/claude-code/README.md +110 -8
  6. package/adapters/claude-code/commands/check.md +4 -3
  7. package/adapters/claude-code/commands/goal.md +3 -2
  8. package/adapters/claude-code/commands/master.md +3 -2
  9. package/adapters/claude-code/commands/onboarding.md +21 -0
  10. package/adapters/claude-code/commands/ork.md +99 -27
  11. package/adapters/claude-code/hooks/hooks.json +6 -1
  12. package/adapters/claude-code/hooks/ork-guard.js +1 -1
  13. package/adapters/claude-code/hooks/ork-sensor.js +66 -0
  14. package/adapters/codex/skills/ork/SKILL.md +57 -0
  15. package/adapters/hermes/README.md +161 -0
  16. package/adapters/hermes/bin/ork-abrir-thread.sh +2 -0
  17. package/adapters/hermes/bin/ork-brain.sh +4 -0
  18. package/adapters/hermes/bin/ork-hitl-answer.py +82 -0
  19. package/adapters/hermes/bin/ork-maestro.sh +6 -0
  20. package/adapters/hermes/bin/ork-master-enviar.py +84 -0
  21. package/adapters/hermes/bin/ork-pulse-enviar.py +47 -0
  22. package/adapters/hermes/hermes.plugin.json +24 -4
  23. package/adapters/hermes/hitl-ingress/__init__.py +356 -0
  24. package/adapters/hermes/hitl-ingress/plugin.yaml +4 -0
  25. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +75 -22
  26. package/adapters/openclaw/README.md +136 -27
  27. package/adapters/openclaw/bin/ork-brain.sh +4 -0
  28. package/adapters/openclaw/construir.sh +19 -0
  29. package/adapters/openclaw/dist/hitl-ingress.js +199 -0
  30. package/adapters/openclaw/dist/index.js +437 -0
  31. package/adapters/openclaw/openclaw.plugin.json +39 -134
  32. package/adapters/openclaw/package.json +26 -0
  33. package/adapters/openclaw/src/hitl-ingress.ts +180 -0
  34. package/adapters/openclaw/src/index.ts +460 -0
  35. package/adapters/openclaw/src/tipos-openclaw.d.ts +58 -0
  36. package/adapters/openclaw/tsconfig.json +15 -0
  37. package/assets/docs/markdownlint-cli2.jsonc +22 -0
  38. package/assets/docs/padroes/documentacao-de-produto.md +133 -0
  39. package/assets/docs/padroes/roadmap-de-produto.md +98 -0
  40. package/assets/docs/produto/README.md +14 -0
  41. package/assets/docs/produto/_modelo-feature.md +59 -0
  42. package/assets/docs/roadmap/README.md +14 -0
  43. package/assets/docs/roadmap/_modelo-item.md +73 -0
  44. package/assets/orkmind-native-schema.json +32 -0
  45. package/assets/orkmind_bridge.py +357 -0
  46. package/assets/orkmind_fixture.py +67 -0
  47. package/assets/orkmind_prospective.py +138 -0
  48. package/assets/reference-tariffs-i07.json +23 -0
  49. package/dist/adapters/claude-bg.js +488 -39
  50. package/dist/adapters/codex-controller-sensor.js +449 -0
  51. package/dist/adapters/codex-controller-worker.js +361 -0
  52. package/dist/adapters/codex-controller.js +252 -0
  53. package/dist/adapters/codex-events.js +205 -0
  54. package/dist/adapters/codex-question.js +26 -0
  55. package/dist/adapters/codex-runner.js +133 -0
  56. package/dist/adapters/codex.js +394 -0
  57. package/dist/agents-md.js +84 -0
  58. package/dist/auditoria.js +12 -9
  59. package/dist/auditrun.js +5 -4
  60. package/dist/board.js +174 -23
  61. package/dist/branch-de-estado.js +136 -0
  62. package/dist/canarios-hitl.js +177 -0
  63. package/dist/canarios-i43.js +543 -0
  64. package/dist/canarios-pulse.js +147 -0
  65. package/dist/canarios-sensores.js +129 -0
  66. package/dist/canarios.js +111 -2
  67. package/dist/catalogo.js +9 -0
  68. package/dist/ci.js +223 -0
  69. package/dist/ciclos.js +2 -1
  70. package/dist/claim-lint.js +64 -0
  71. package/dist/claims.js +60 -0
  72. package/dist/company-brain-capture.js +195 -0
  73. package/dist/company-brain-cli.js +123 -0
  74. package/dist/company-brain-client.js +61 -0
  75. package/dist/company-brain-contract.js +166 -0
  76. package/dist/company-brain-journal.js +192 -0
  77. package/dist/company-brain-mcp.js +33 -0
  78. package/dist/company-brain-migration.js +60 -0
  79. package/dist/company-brain-source.js +177 -0
  80. package/dist/company-brain-worker.js +18 -0
  81. package/dist/conducao-texto.js +61 -0
  82. package/dist/conducao.js +876 -0
  83. package/dist/contrato-publico.js +39 -0
  84. package/dist/creation-operation-store.js +251 -0
  85. package/dist/creation-operation.js +148 -0
  86. package/dist/decisao-autonoma.js +183 -0
  87. package/dist/delegation.js +79 -0
  88. package/dist/demo.js +120 -0
  89. package/dist/docs.js +828 -0
  90. package/dist/doctor.js +184 -25
  91. package/dist/entrega-pr.js +117 -0
  92. package/dist/escopo-escrita.js +71 -0
  93. package/dist/estado-thread.js +222 -0
  94. package/dist/evalrunner.js +12 -0
  95. package/dist/fabrica-estado.js +329 -0
  96. package/dist/fabrica-publicar.js +76 -0
  97. package/dist/fix.js +22 -1
  98. package/dist/gates.js +59 -11
  99. package/dist/handoff.js +39 -23
  100. package/dist/hitl-canais.js +333 -0
  101. package/dist/hitl-classificacao.js +131 -0
  102. package/dist/hitl-contract.js +465 -0
  103. package/dist/hitl-estado.js +136 -0
  104. package/dist/hitl-gates.js +554 -0
  105. package/dist/hitl-ingress-receipt.js +382 -0
  106. package/dist/hitl-local-atestado.js +57 -0
  107. package/dist/hitl-local-receipt.js +328 -0
  108. package/dist/hitl-local.js +143 -0
  109. package/dist/hitl-lock.js +139 -0
  110. package/dist/hitl-lote.js +223 -0
  111. package/dist/hitl-native-offer.js +97 -0
  112. package/dist/hitl-native.js +65 -0
  113. package/dist/hitl-presentation.js +209 -0
  114. package/dist/hitl-public-receipt.js +176 -0
  115. package/dist/hitl-resumo.js +209 -0
  116. package/dist/hitl-sessions.js +475 -0
  117. package/dist/hitl.js +711 -0
  118. package/dist/horario.js +269 -0
  119. package/dist/hosts.js +198 -22
  120. package/dist/index.js +1741 -94
  121. package/dist/indice.js +99 -0
  122. package/dist/init.js +28 -3
  123. package/dist/integracoes-locais.js +17 -0
  124. package/dist/leases.js +65 -21
  125. package/dist/ledger-stats.js +272 -0
  126. package/dist/ledger.js +109 -2
  127. package/dist/licoes.js +221 -0
  128. package/dist/liveness.js +218 -0
  129. package/dist/maestro-actions.js +51 -0
  130. package/dist/maestro-authority.js +152 -0
  131. package/dist/maestro-cli.js +97 -0
  132. package/dist/maestro-contract.js +55 -0
  133. package/dist/maestro-discovery.js +132 -0
  134. package/dist/maestro-runtime.js +268 -0
  135. package/dist/maestro-snapshot.js +86 -0
  136. package/dist/maestro-sources.js +224 -0
  137. package/dist/manifest.js +165 -6
  138. package/dist/maquina.js +102 -0
  139. package/dist/master-audit.js +101 -0
  140. package/dist/master-batch.js +43 -0
  141. package/dist/master-digest.js +149 -0
  142. package/dist/master-migracao.js +155 -0
  143. package/dist/master.js +361 -77
  144. package/dist/mcp-artifacts.js +234 -0
  145. package/dist/mcp-git.js +433 -0
  146. package/dist/mcp-install.js +310 -0
  147. package/dist/mcp-maestro.js +25 -0
  148. package/dist/mcp-server.js +469 -0
  149. package/dist/mcp-ship.js +436 -0
  150. package/dist/mcp-verify.js +173 -0
  151. package/dist/memoria-humana.js +207 -0
  152. package/dist/memoria.js +314 -78
  153. package/dist/memory-migration.js +279 -0
  154. package/dist/memory-prospective.js +148 -0
  155. package/dist/modos-migracao.js +191 -0
  156. package/dist/modos.js +151 -15
  157. package/dist/monitor-lock.js +110 -0
  158. package/dist/objective.js +449 -0
  159. package/dist/ocupacao.js +267 -0
  160. package/dist/onboarding.js +303 -0
  161. package/dist/orkmind.js +389 -90
  162. package/dist/orquestracao.js +96 -69
  163. package/dist/phase.js +612 -151
  164. package/dist/playbook-capabilities.js +150 -0
  165. package/dist/playbook-contracts.js +188 -0
  166. package/dist/policies.js +72 -0
  167. package/dist/portfolio-context.js +61 -0
  168. package/dist/portfolio.js +180 -0
  169. package/dist/preflight.js +207 -0
  170. package/dist/process-audit.js +112 -0
  171. package/dist/project-state.js +93 -0
  172. package/dist/prompts.js +25 -5
  173. package/dist/prova-minima.js +112 -0
  174. package/dist/pulse-cadencia.js +163 -0
  175. package/dist/pulse-consentimento.js +260 -0
  176. package/dist/pulse-delivery.js +323 -0
  177. package/dist/pulse-resposta.js +542 -0
  178. package/dist/pulse.js +262 -0
  179. package/dist/ratelimit.js +35 -2
  180. package/dist/recall.js +80 -3
  181. package/dist/redacao-saida.js +45 -0
  182. package/dist/redacao-url.js +66 -0
  183. package/dist/retry.js +640 -118
  184. package/dist/roadmap-reservas.js +243 -0
  185. package/dist/runtime-ambiente.js +38 -0
  186. package/dist/runtime-context.js +215 -0
  187. package/dist/runtime-profiles.js +869 -0
  188. package/dist/runtimes.js +103 -0
  189. package/dist/sandbox.js +9 -0
  190. package/dist/session-events.js +203 -0
  191. package/dist/session-watcher-claude.js +637 -0
  192. package/dist/session-watcher.js +700 -0
  193. package/dist/sessoes-adopt.js +154 -0
  194. package/dist/sessoes-inventario.js +159 -0
  195. package/dist/sessoes.js +16 -35
  196. package/dist/setup.js +691 -0
  197. package/dist/ship.js +168 -19
  198. package/dist/slug.js +1 -1
  199. package/dist/thread-close.js +115 -0
  200. package/dist/thread.js +142 -28
  201. package/dist/tokens.js +1 -1
  202. package/dist/util.js +4 -2
  203. package/dist/verify-sandbox.js +265 -0
  204. package/dist/verify.js +270 -25
  205. package/dist/versao.js +60 -0
  206. package/dist/worktree.js +31 -2
  207. package/dist/write-activation.js +291 -0
  208. package/dist/yaml.js +5 -1
  209. package/eval/casos/master-metrics.json +1 -1
  210. package/eval/casos/onboarding.json +102 -0
  211. package/eval/casos/orkastery-bootstrap.json +37 -3
  212. package/eval/casos/scope-check-capability-map.json +45 -7
  213. package/eval/casos/ship-release.json +3 -3
  214. package/eval/fixtures/b0-slug-e-modos/caso.json +35 -22
  215. package/eval/fixtures/b2-master-log/caso.json +1 -1
  216. package/eval/fixtures/fx-adapter-editado-detectado/caso.json +19 -0
  217. package/eval/fixtures/fx-auto-quiet/caso.json +11 -0
  218. package/eval/fixtures/fx-blanket-approve/caso.json +10 -0
  219. package/eval/fixtures/fx-check-runtime-cruzado/caso.json +20 -0
  220. package/eval/fixtures/fx-codex-dry/caso.json +18 -0
  221. package/eval/fixtures/fx-donewhen-executavel/caso.json +15 -0
  222. package/eval/fixtures/fx-estado-dividido/caso.json +15 -0
  223. package/eval/fixtures/fx-fase-orfa/caso.json +116 -0
  224. package/eval/fixtures/fx-happy/caso.json +6 -4
  225. package/eval/fixtures/fx-hitl-latency/caso.json +20 -0
  226. package/eval/fixtures/fx-indice-reversao/caso.json +25 -0
  227. package/eval/fixtures/fx-listagem-abertas/caso.json +16 -0
  228. package/eval/fixtures/fx-maestro-bootstrap/caso.json +21 -0
  229. package/eval/fixtures/fx-modo-aposentado-escritor/caso.json +21 -0
  230. package/eval/fixtures/fx-modo-aposentado-leitor/caso.json +16 -0
  231. package/eval/fixtures/fx-objective-oscillation/caso.json +14 -0
  232. package/eval/fixtures/fx-omnicanal/caso.json +17 -0
  233. package/eval/fixtures/fx-sensores-runtime/caso.json +17 -0
  234. package/monitor/company-brain.cjs +17 -0
  235. package/monitor/pulse-scope.cjs +45 -0
  236. package/monitor/pulse.cron +18 -0
  237. package/monitor/varredura-pulse.sh +27 -0
  238. package/package.json +28 -7
  239. package/references/definition-of-done.md +2 -2
  240. package/schemas/claims.schema.json +25 -0
  241. package/schemas/company-brain.schema.json +1158 -0
  242. package/schemas/creation-operation.schema.json +365 -0
  243. package/schemas/maestro-snapshot.schema.json +2716 -0
  244. package/skills/README.md +3 -3
  245. package/skills/core/onboarding/SKILL.md +50 -0
  246. package/skills/core/orkastery-bootstrap/SKILL.md +95 -65
  247. package/skills/core/thread-state/SKILL.md +3 -1
  248. package/skills/governance/decision-triage/SKILL.md +4 -4
  249. package/skills/governance/narrative-guardian/SKILL.md +1 -1
  250. package/skills/governance/roadmap-keeper/SKILL.md +1 -1
  251. package/skills/governance/scope-check-capability-map/SKILL.md +17 -7
  252. package/skills/observability/thread-tracing/SKILL.md +1 -1
  253. package/skills/phases/check-quality/SKILL.md +2 -2
  254. package/skills/phases/goal-definition/SKILL.md +1 -1
  255. package/skills/phases/master-metrics/SKILL.md +9 -7
  256. package/skills/phases/plan-specification/SKILL.md +1 -1
  257. package/skills/phases/ship-release/SKILL.md +1 -1
package/dist/board.js CHANGED
@@ -49,16 +49,27 @@ var __importStar = (this && this.__importStar) || (function () {
49
49
  Object.defineProperty(exports, "__esModule", { value: true });
50
50
  exports.perfisDetectados = perfisDetectados;
51
51
  exports.threadsDeTodosOsPerfis = threadsDeTodosOsPerfis;
52
+ exports.estadosDeSessao = estadosDeSessao;
52
53
  exports.planejar = planejar;
54
+ exports.devolverVagas = devolverVagas;
55
+ exports.textoDoReap = textoDoReap;
53
56
  exports.textoDoPlano = textoDoPlano;
54
57
  exports.textoDoBoard = textoDoBoard;
55
58
  const fs = __importStar(require("node:fs"));
56
59
  const path = __importStar(require("node:path"));
60
+ const adapter = __importStar(require("./adapters/claude-bg"));
61
+ const hitl_1 = require("./hitl");
57
62
  const leases_1 = require("./leases");
63
+ const ledger_1 = require("./ledger");
58
64
  const manifest_1 = require("./manifest");
59
65
  const modos_1 = require("./modos");
66
+ const ocupacao_1 = require("./ocupacao");
60
67
  const thread_1 = require("./thread");
61
68
  const util_1 = require("./util");
69
+ const sessoes_adopt_1 = require("./sessoes-adopt");
70
+ const horario_1 = require("./horario");
71
+ const conducao_1 = require("./conducao");
72
+ const conducao_texto_1 = require("./conducao-texto");
62
73
  /**
63
74
  * Perfis detectados no projeto.
64
75
  *
@@ -95,34 +106,60 @@ function threadsDeTodosOsPerfis(carregado, todos = true) {
95
106
  perfil: perfil.nome,
96
107
  raiz: perfil.raiz,
97
108
  thread,
98
- leases: (0, leases_1.leasesDaThread)(perfil.raiz, id).map((l) => l.nome),
109
+ // I-36: a conducao (`exec:`) tem secao propria; o escalonador segue contando so os leases de escrita.
110
+ leases: (0, leases_1.leasesDaThread)(perfil.raiz, id).filter((l) => (0, leases_1.tipoDoLease)(l.nome) !== 'exec').map((l) => l.nome),
99
111
  naFila: fila.filter((p) => p.thread === id),
100
112
  });
101
113
  }
102
114
  }
103
115
  return saida;
104
116
  }
117
+ /** Estados de sessao do runtime, ou null quando ele nao pode ser consultado. */
118
+ function estadosDeSessao() {
119
+ if (!adapter.disponivel())
120
+ return null;
121
+ return new Map(adapter
122
+ .listarSessoes()
123
+ .filter((s) => !!s.sessionId)
124
+ .map((s) => [s.sessionId, (0, hitl_1.estadoBruto)(s)]));
125
+ }
105
126
  /**
106
127
  * Escalonador por maquina: decide quem avanca agora e quem espera, com motivo tipado.
107
128
  *
108
- * A ordem e FIFO por criacao da thread. Quem ja segura lease conta como em andamento e
109
- * ocupa vaga: e o unico jeito de o limite de paralelismo significar alguma coisa.
129
+ * A vaga e OCUPACAO REAL, nao existencia de thread aberta (correcao do incidente de
130
+ * 06/09/2026): ocupa vaga quem tem procura ativa (`avaliarOcupacao`) ou quem segura
131
+ * lease sem estar travada. Pausa humana aberta, impedimento nao-transitorio e stale
132
+ * devolvem a vaga com motivo tipado, e a fila anda em FIFO por criacao da thread.
110
133
  */
111
- function planejar(carregado) {
134
+ function planejar(carregado, opcoes = {}) {
112
135
  const { raiz, manifesto } = carregado;
113
136
  const max = Math.max(1, manifesto.concurrency.max_parallel_threads);
137
+ const staleMin = manifesto.concurrency.stale_after_min;
138
+ const quando = opcoes.agora ?? (0, util_1.agora)();
139
+ const estados = opcoes.estados === undefined ? estadosDeSessao() : opcoes.estados;
114
140
  const fila = (0, leases_1.lerFila)(raiz);
115
- const ativos = (0, leases_1.listarLeases)(raiz).filter((l) => !(0, leases_1.expirado)(l));
141
+ // I-36: a conducao (`exec:`) nao entra na conta de vagas: a ocupacao da sessao ja sai do ledger
142
+ // e do runtime (`avaliarOcupacao`), e conta-la de novo mudaria o escalonador.
143
+ const ativos = (0, leases_1.listarLeases)(raiz).filter((l) => !(0, leases_1.expirado)(l) && (0, leases_1.tipoDoLease)(l.nome) !== 'exec');
116
144
  const threads = (0, thread_1.listarIds)(raiz)
117
145
  .map((id) => (0, thread_1.lerThread)(raiz, id))
118
146
  .sort((a, b) => a.criadaEm.localeCompare(b.criadaEm));
147
+ const ocupacoes = new Map(threads.map((t) => [
148
+ t.id,
149
+ (0, ocupacao_1.avaliarOcupacao)(t, (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, t.id)), { agora: quando, estados, staleMin }),
150
+ ]));
119
151
  const vagas = [];
120
152
  let ocupadas = 0;
121
- // 1a passada: quem ja segura lease esta em andamento e ocupa vaga.
153
+ // 1a passada: quem tem procura ativa esta em andamento e ocupa vaga. Segurar lease
154
+ // sem estar travada tambem ocupa: o lease e trabalho em curso na regiao protegida.
122
155
  const emAndamento = new Set();
123
156
  for (const t of threads) {
157
+ if (t.status === 'fechada' || (0, sessoes_adopt_1.ehRegistroDeAdocao)(t))
158
+ continue;
159
+ const oc = ocupacoes.get(t.id);
124
160
  const meus = ativos.filter((l) => l.thread === t.id).map((l) => l.nome);
125
- if (t.status === 'fechada' || meus.length === 0)
161
+ const ocupa = oc.ocupaVaga || (meus.length > 0 && oc.classe === 'ociosa');
162
+ if (!ocupa)
126
163
  continue;
127
164
  emAndamento.add(t.id);
128
165
  ocupadas += 1;
@@ -133,15 +170,17 @@ function planejar(carregado) {
133
170
  fase: t.faseAtual,
134
171
  situacao: 'em-andamento',
135
172
  motivo: null,
136
- detalhe: `segura ${meus.join(', ')}`,
173
+ detalhe: meus.length > 0 ? `segura ${meus.join(', ')}; ${oc.detalhe}` : oc.detalhe,
137
174
  correcao: '',
138
175
  leases: meus,
139
176
  });
140
177
  }
141
- // 2a passada: as demais, em FIFO, ate o limite de paralelismo da maquina.
178
+ // 2a passada: as demais, em FIFO. Thread travada (pausa humana, impedimento, stale)
179
+ // NAO ocupa vaga: e exatamente a thread orfa que sufocava as demandas legitimas.
142
180
  for (const t of threads) {
143
181
  if (emAndamento.has(t.id))
144
182
  continue;
183
+ const meus = ativos.filter((l) => l.thread === t.id).map((l) => l.nome);
145
184
  if (t.status === 'fechada') {
146
185
  vagas.push({
147
186
  thread: t.id,
@@ -156,7 +195,15 @@ function planejar(carregado) {
156
195
  });
157
196
  continue;
158
197
  }
159
- if (t.status === 'pausada') {
198
+ const oc = ocupacoes.get(t.id);
199
+ if ((0, sessoes_adopt_1.ehRegistroDeAdocao)(t)) {
200
+ vagas.push({ thread: t.id, slug: t.slug, modo: t.modo, fase: t.faseAtual,
201
+ situacao: oc.classe === 'pausa-humana' ? 'pausada' : 'espera',
202
+ motivo: oc.motivo, detalhe: 'registro de adoção: identidade preservada, sem trabalho a despachar',
203
+ correcao: oc.classe === 'pausa-humana' ? 'ork sessions hitl' : '', leases: meus });
204
+ continue;
205
+ }
206
+ if (oc.classe === 'pausa-humana') {
160
207
  vagas.push({
161
208
  thread: t.id,
162
209
  slug: t.slug,
@@ -164,9 +211,38 @@ function planejar(carregado) {
164
211
  fase: t.faseAtual,
165
212
  situacao: 'pausada',
166
213
  motivo: 'human.pending',
167
- detalhe: `esperando veredito humano na fase ${t.faseAtual}`,
168
- correcao: `ork gate approve ${t.id} <sobre> --por <quem>`,
169
- leases: [],
214
+ detalhe: oc.detalhe,
215
+ correcao: `ork gate request ${t.id}`,
216
+ leases: meus,
217
+ });
218
+ continue;
219
+ }
220
+ if (oc.classe === 'impedida') {
221
+ vagas.push({
222
+ thread: t.id,
223
+ slug: t.slug,
224
+ modo: t.modo,
225
+ fase: t.faseAtual,
226
+ situacao: 'espera',
227
+ motivo: oc.motivo,
228
+ detalhe: oc.detalhe,
229
+ correcao: `ork retry plan ${t.id} --motivo ${String(oc.motivo)}, ou feche com ork master ${t.id}`,
230
+ leases: meus,
231
+ });
232
+ continue;
233
+ }
234
+ if (oc.classe === 'stale') {
235
+ vagas.push({
236
+ thread: t.id,
237
+ slug: t.slug,
238
+ modo: t.modo,
239
+ fase: t.faseAtual,
240
+ situacao: 'espera',
241
+ motivo: 'vaga.stale',
242
+ detalhe: oc.detalhe,
243
+ correcao: `redespache com ork phase run ${t.id} --fase ${t.faseAtual}, ` +
244
+ `ou feche com ork master ${t.id}`,
245
+ leases: meus,
170
246
  });
171
247
  continue;
172
248
  }
@@ -219,18 +295,85 @@ function planejar(carregado) {
219
295
  vagas,
220
296
  filaDeMerge: (0, leases_1.esperandoPor)(raiz, leases_1.LEASE_MAIN_TREE),
221
297
  filaDeRegiao: fila.filter((p) => p.tipo !== 'main-tree'),
222
- decididoEm: (0, util_1.agora)(),
298
+ decididoEm: quando,
223
299
  };
224
300
  }
225
- function linhaDeFila(p, i) {
226
- return ` ${i + 1}. ${p.thread} quer ${p.nome} (colide com ${p.colidiuCom}, thread ${p.bloqueadaPor}) desde ${p.desdeEm}`;
301
+ /**
302
+ * `ork board reap`: grava no ledger de cada thread SEM procura ativa o evento
303
+ * `slot_released`, com quem decidiu, evidencia e razao.
304
+ *
305
+ * A vaga em si e derivada (o `planejar` ja a devolve sozinho a cada decisao); o reap
306
+ * existe para a devolucao virar EVENTO VERIFICAVEL no ledger da thread devolvida.
307
+ * Ele e idempotente: enquanto um `slot_released` estiver aberto (sem evento posterior
308
+ * que destrave a thread), rodar de novo nao grava linha nova.
309
+ */
310
+ function devolverVagas(carregado, opcoes = {}) {
311
+ const { raiz, manifesto } = carregado;
312
+ const staleMin = manifesto.concurrency.stale_after_min;
313
+ const quando = opcoes.agora ?? (0, util_1.agora)();
314
+ const estados = opcoes.estados === undefined ? estadosDeSessao() : opcoes.estados;
315
+ const quem = opcoes.por ?? 'escalonador (ork board reap)';
316
+ const devolvidas = [];
317
+ for (const id of (0, thread_1.listarIds)(raiz)) {
318
+ const t = (0, thread_1.lerThread)(raiz, id);
319
+ if (t.status === 'fechada' || (0, sessoes_adopt_1.ehRegistroDeAdocao)(t))
320
+ continue;
321
+ const dir = (0, thread_1.dirThread)(raiz, id);
322
+ const eventos = (0, ledger_1.lerLedger)(dir);
323
+ const oc = (0, ocupacao_1.avaliarOcupacao)(t, eventos, { agora: quando, estados, staleMin });
324
+ if (oc.classe !== 'pausa-humana' && oc.classe !== 'impedida' && oc.classe !== 'stale') {
325
+ continue;
326
+ }
327
+ const jaRegistrada = eventos.some((e, i) => e.tipo === ledger_1.TIPOS_DE_EVENTO.vagaLiberada &&
328
+ !eventos
329
+ .slice(i + 1)
330
+ .some((p) => ocupacao_1.EVENTOS_QUE_DESTRAVAM.includes(p.tipo) || (0, ocupacao_1.ehAprovacaoHumana)(p)));
331
+ if (!jaRegistrada) {
332
+ (0, ledger_1.registrar)(dir, id, ledger_1.TIPOS_DE_EVENTO.vagaLiberada, {
333
+ fase: t.faseAtual,
334
+ motivo: oc.motivo,
335
+ quem,
336
+ detalhe: oc.detalhe,
337
+ evidencia: oc.evidencia,
338
+ razao: 'thread sem procura ativa nao ocupa vaga de concurrency.max_parallel_threads: ' +
339
+ 'a vaga volta para as threads prontas para rodar',
340
+ });
341
+ }
342
+ devolvidas.push({
343
+ thread: id,
344
+ classe: oc.classe,
345
+ motivo: oc.motivo,
346
+ detalhe: oc.detalhe,
347
+ evidencia: oc.evidencia,
348
+ registrada: !jaRegistrada,
349
+ });
350
+ }
351
+ return devolvidas;
352
+ }
353
+ /** Texto de `ork board reap`. */
354
+ function textoDoReap(devolvidas) {
355
+ if (devolvidas.length === 0) {
356
+ return 'Nenhuma vaga a devolver: toda thread aberta tem procura ativa ou esta pronta para rodar.';
357
+ }
358
+ // I-35: o ledger guarda o ISO; o texto ao dono sai no fuso dele, rotulado uma vez.
359
+ const linhas = [`Vagas devolvidas por falta de procura ativa: ${devolvidas.length}. ${(0, horario_1.legendaDoFuso)()}`, ''];
360
+ for (const d of devolvidas) {
361
+ const marca = d.registrada ? 'slot_released gravado no ledger' : 'ja registrado antes (idempotente)';
362
+ linhas.push(` ${d.thread.padEnd(20)} [${d.motivo}] ${marca}`);
363
+ linhas.push(` detalhe : ${(0, horario_1.localizarTexto)(d.detalhe)}`);
364
+ linhas.push(` evidencia: ${(0, horario_1.localizarTexto)(d.evidencia)}`);
365
+ }
366
+ return linhas.join('\n');
367
+ }
368
+ function linhaDeFila(p, i, agora) {
369
+ return ` ${i + 1}. ${p.thread} quer ${p.nome} (colide com ${p.colidiuCom}, thread ${p.bloqueadaPor}) desde ${(0, horario_1.formatarDesde)(p.desdeEm, { agora })}`;
227
370
  }
228
371
  /** Texto de `ork board plan`. */
229
372
  function textoDoPlano(plano) {
230
373
  const linhas = [];
231
- linhas.push('Escalonador por maquina');
374
+ linhas.push(`Escalonador por maquina. ${(0, horario_1.legendaDoFuso)(undefined, plano.decididoEm)}`);
232
375
  linhas.push(` limite de paralelismo: ${plano.maxParalelas} (concurrency.max_parallel_threads) | ` +
233
- `em andamento agora: ${plano.emAndamento}`);
376
+ `em andamento agora (procura ativa): ${plano.emAndamento}`);
234
377
  linhas.push('');
235
378
  if (plano.vagas.length === 0) {
236
379
  linhas.push(' Nenhuma thread no projeto.');
@@ -243,14 +386,14 @@ function textoDoPlano(plano) {
243
386
  v.fase,
244
387
  v.situacao,
245
388
  v.motivo ?? '-',
246
- v.detalhe,
389
+ (0, horario_1.localizarTexto)(v.detalhe, { agora: plano.decididoEm }),
247
390
  ])));
248
391
  const comCorrecao = plano.vagas.filter((v) => v.correcao !== '');
249
392
  if (comCorrecao.length > 0) {
250
393
  linhas.push('');
251
394
  linhas.push(' Correcoes acionaveis:');
252
395
  for (const v of comCorrecao)
253
- linhas.push(` ${v.thread}: ${v.correcao}`);
396
+ linhas.push(` ${v.thread}: ${(0, horario_1.localizarTexto)(v.correcao, { agora: plano.decididoEm })}`);
254
397
  }
255
398
  }
256
399
  linhas.push('');
@@ -259,7 +402,7 @@ function textoDoPlano(plano) {
259
402
  linhas.push(' vazia: nenhuma thread esperando para mergear');
260
403
  }
261
404
  else {
262
- plano.filaDeMerge.forEach((p, i) => linhas.push(linhaDeFila(p, i)));
405
+ plano.filaDeMerge.forEach((p, i) => linhas.push(linhaDeFila(p, i, plano.decididoEm)));
263
406
  }
264
407
  linhas.push('');
265
408
  linhas.push('Fila por colisao de regiao');
@@ -267,7 +410,7 @@ function textoDoPlano(plano) {
267
410
  linhas.push(' vazia: nenhuma colisao de path, board ou porta');
268
411
  }
269
412
  else {
270
- plano.filaDeRegiao.forEach((p, i) => linhas.push(linhaDeFila(p, i)));
413
+ plano.filaDeRegiao.forEach((p, i) => linhas.push(linhaDeFila(p, i, plano.decididoEm)));
271
414
  }
272
415
  return linhas.join('\n');
273
416
  }
@@ -295,6 +438,14 @@ function textoDoBoard(carregado, todos) {
295
438
  i.thread.worktree ? path.basename(i.thread.worktree) : '-',
296
439
  i.leases.length > 0 ? i.leases.join(' ') : i.naFila.length > 0 ? `fila:${i.naFila[0].nome}` : '-',
297
440
  ])));
441
+ // I-36 (T17): quem conduz cada thread agora, pela leitura unica do nucleo.
442
+ const conduzidas = itens.map((i) => ({ id: i.thread.id, c: (0, conducao_1.conducaoDaThread)(i.raiz, i.thread.id) })).filter((x) => x.c);
443
+ if (conduzidas.length > 0) {
444
+ linhas.push('');
445
+ linhas.push('Conducao agora');
446
+ for (const x of conduzidas)
447
+ linhas.push(` ${x.id}: ${(0, conducao_texto_1.linhaDeConducao)(x.c)}`);
448
+ }
298
449
  const comVariante = itens.filter((i) => i.thread.variante);
299
450
  if (comVariante.length > 0) {
300
451
  linhas.push('');
@@ -308,7 +459,7 @@ function textoDoBoard(carregado, todos) {
308
459
  if (semScore.length > 0) {
309
460
  linhas.push('');
310
461
  linhas.push(`Fila de batch scoring: ${semScore.length} thread(s) esperando score humano`);
311
- linhas.push(' veja em: ork master --batch');
462
+ linhas.push(' veja em: ork master (com o indice derivado do ledger)');
312
463
  }
313
464
  return linhas.join('\n');
314
465
  }
@@ -0,0 +1,136 @@
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
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.refRemota = void 0;
37
+ exports.git = git;
38
+ exports.exigirGit = exigirGit;
39
+ exports.pontaLocal = pontaLocal;
40
+ exports.buscarBranch = buscarBranch;
41
+ exports.jsonsDaPonta = jsonsDaPonta;
42
+ exports.gravarNaBranch = gravarNaBranch;
43
+ /**
44
+ * I-51 (RM-047): uma branch de estado compartilhada entre maquinas, fora da `main` e sem PR.
45
+ *
46
+ * Nasceu nas reservas do roadmap (I-47) e agora serve tambem ao estado da fabrica. Nada aqui toca
47
+ * a arvore de trabalho nem o indice do repositorio: blobs, arvore e commit sao montados com um
48
+ * indice temporario, e a branch local nunca e criada.
49
+ *
50
+ * A atomicidade vem do proprio git: a versao nova e um commit em cima da ponta lida, e o push
51
+ * nunca e forcado. Push recusado volta `false`, e quem chamou rele a ponta e aplica a propria
52
+ * regra de novo sobre o estado novo.
53
+ *
54
+ * `prefixo` da nome aos erros de quem usa (`roadmap.git`, `fabrica.sem-remoto`...).
55
+ */
56
+ const node_child_process_1 = require("node:child_process");
57
+ const fs = __importStar(require("node:fs"));
58
+ const os = __importStar(require("node:os"));
59
+ const path = __importStar(require("node:path"));
60
+ function git(raiz, args, extra = {}) {
61
+ const r = (0, node_child_process_1.spawnSync)('git', args, { cwd: raiz, encoding: 'utf8', input: extra.input, timeout: extra.timeoutMs ?? 60000,
62
+ env: { ...process.env, ...extra.env } });
63
+ return { ok: r.status === 0, stdout: (r.stdout ?? '').toString(), stderr: (r.stderr ?? '').toString() };
64
+ }
65
+ function exigirGit(raiz, args, prefixo, extra = {}) {
66
+ const r = git(raiz, args, extra);
67
+ if (!r.ok)
68
+ throw new Error(`${prefixo}.git: git ${args[0]} falhou: ${r.stderr.trim().split('\n').pop() ?? ''}`);
69
+ return r.stdout;
70
+ }
71
+ const refRemota = (remoto, branch) => `refs/remotes/${remoto}/${branch}`;
72
+ exports.refRemota = refRemota;
73
+ /** A ultima copia lida da branch nesta maquina, sem rede. `null` quando nunca foi lida. */
74
+ function pontaLocal(raiz, remoto, branch) {
75
+ const local = git(raiz, ['rev-parse', '--verify', '--quiet', (0, exports.refRemota)(remoto, branch)]);
76
+ return local.ok ? local.stdout.trim() : null;
77
+ }
78
+ /**
79
+ * Traz a ponta da branch. `ponta: null` quando ela ainda nao existe no remoto; sem rede, a ultima
80
+ * copia local com `atualizado: false`.
81
+ */
82
+ function buscarBranch(raiz, remoto, branch, prefixo, timeoutMs) {
83
+ const r = git(raiz, ['fetch', '--quiet', '--no-tags', remoto, `+refs/heads/${branch}:${(0, exports.refRemota)(remoto, branch)}`], { timeoutMs });
84
+ if (r.ok)
85
+ return { ponta: exigirGit(raiz, ['rev-parse', '--verify', (0, exports.refRemota)(remoto, branch)], prefixo).trim(), atualizado: true };
86
+ if (/couldn't find remote ref|could not find remote ref/i.test(r.stderr)) {
87
+ // A branch ainda nao nasceu: a primeira gravacao a cria. Copia local antiga nao vale mais.
88
+ git(raiz, ['update-ref', '-d', (0, exports.refRemota)(remoto, branch)]);
89
+ return { ponta: null, atualizado: true };
90
+ }
91
+ return { ponta: pontaLocal(raiz, remoto, branch), atualizado: false };
92
+ }
93
+ /** Os `.json` de um diretorio da ponta, ja lidos. Filtro por caminho: diretorio vazio some da arvore. */
94
+ function jsonsDaPonta(raiz, ponta, dir, prefixo) {
95
+ if (!ponta)
96
+ return [];
97
+ const caminhos = exigirGit(raiz, ['ls-tree', '-r', '--name-only', ponta, '--', `${dir}/`], prefixo)
98
+ .split('\n').filter((n) => n.endsWith('.json'));
99
+ return caminhos.map((c) => JSON.parse(exigirGit(raiz, ['show', `${ponta}:${c}`], prefixo)));
100
+ }
101
+ /**
102
+ * Grava uma versao nova da branch em cima de `ponta`, sem tocar arvore de trabalho nem indice:
103
+ * indice temporario, blobs, `write-tree` e `commit-tree`. Push sem forca; recusa volta `false`.
104
+ */
105
+ function gravarNaBranch(raiz, remoto, branch, ponta, mudancas, mensagem, prefixo) {
106
+ const indice = path.join(fs.mkdtempSync(path.join(os.tmpdir(), 'ork-branch-estado-')), 'index');
107
+ const env = { GIT_INDEX_FILE: indice };
108
+ try {
109
+ if (ponta)
110
+ exigirGit(raiz, ['read-tree', ponta], prefixo, { env });
111
+ else
112
+ exigirGit(raiz, ['read-tree', '--empty'], prefixo, { env });
113
+ for (const m of mudancas) {
114
+ if (m.conteudo === null) {
115
+ exigirGit(raiz, ['update-index', '--force-remove', m.caminho], prefixo, { env });
116
+ }
117
+ else {
118
+ const blob = exigirGit(raiz, ['hash-object', '-w', '--stdin'], prefixo, { input: m.conteudo }).trim();
119
+ exigirGit(raiz, ['update-index', '--add', '--cacheinfo', `100644,${blob},${m.caminho}`], prefixo, { env });
120
+ }
121
+ }
122
+ const arvore = exigirGit(raiz, ['write-tree'], prefixo, { env }).trim();
123
+ const commit = exigirGit(raiz, ['commit-tree', arvore, ...(ponta ? ['-p', ponta] : []), '-m', mensagem], prefixo).trim();
124
+ const push = git(raiz, ['push', '--quiet', remoto, `${commit}:refs/heads/${branch}`]);
125
+ if (push.ok) {
126
+ git(raiz, ['update-ref', (0, exports.refRemota)(remoto, branch), commit]);
127
+ return commit;
128
+ }
129
+ if (/rejected|non-fast-forward|fetch first|stale info|failed to push/i.test(push.stderr))
130
+ return false;
131
+ throw new Error(`${prefixo}.sem-remoto: o push para ${remoto} falhou: ${push.stderr.trim().split('\n').pop() ?? ''}`);
132
+ }
133
+ finally {
134
+ fs.rmSync(path.dirname(indice), { recursive: true, force: true });
135
+ }
136
+ }
@@ -0,0 +1,177 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CANARIOS_HITL = void 0;
4
+ const sandbox_1 = require("./sandbox");
5
+ const thread_1 = require("./thread");
6
+ const ledger_1 = require("./ledger");
7
+ const gates_1 = require("./gates");
8
+ const hitl_gates_1 = require("./hitl-gates");
9
+ const pulse_1 = require("./pulse");
10
+ const hitl_gates_2 = require("./hitl-gates");
11
+ const hitl_gates_3 = require("./hitl-gates");
12
+ const hitl_local_atestado_1 = require("./hitl-local-atestado");
13
+ const hitl_canais_1 = require("./hitl-canais");
14
+ exports.CANARIOS_HITL = [
15
+ { id: 'fx-blanket-approve', sobre: 'SIMULADO: aprovação cega e envelope sem autenticação são recusados', precisaDeGit: true, rodar: () => {
16
+ const s = (0, sandbox_1.sandboxGit)('blanket-approve');
17
+ try {
18
+ let recusas = 0, humanos = 0;
19
+ for (const modo of ['classic']) {
20
+ const t = (0, thread_1.novaThread)(s.carregado, { nome: modo, modo }).thread;
21
+ (0, ledger_1.registrar)((0, thread_1.dirThread)(s.dir, t.id), t.id, 'phase_result', { fase: 'GOAL' });
22
+ const p = (0, hitl_gates_1.abrirPedidoGate)(s.dir, t.id);
23
+ try {
24
+ (0, gates_1.aprovarGateHumano)(s.dir, t.id, 'push', 'modelo');
25
+ }
26
+ catch {
27
+ recusas++;
28
+ }
29
+ try {
30
+ (0, hitl_gates_1.responderGate)(s.dir, t.id, p.id, { origem: 'telegram', por: 'telegram:42', mensagem: 'telegram:-7:1',
31
+ recebidoEm: new Date().toISOString(), resposta: '1', prova: '0'.repeat(64) });
32
+ }
33
+ catch {
34
+ recusas++;
35
+ }
36
+ humanos += (0, ledger_1.lerLedger)((0, thread_1.dirThread)(s.dir, t.id)).filter(e => e.tipo === 'human_gate').length;
37
+ }
38
+ return { simulado: true, recusas, humanos };
39
+ }
40
+ finally {
41
+ s.limpar();
42
+ }
43
+ } },
44
+ { id: 'fx-auto-quiet', sobre: 'SIMULADO: Auto/Maestro silenciosos fora de pausas; policy bloqueia', precisaDeGit: true, rodar: () => {
45
+ const s = (0, sandbox_1.sandboxGit)('auto-quiet');
46
+ try {
47
+ let pedidosRecusados = 0;
48
+ for (const modo of ['auto', 'maestro']) {
49
+ const t = (0, thread_1.novaThread)(s.carregado, { nome: modo, modo }).thread;
50
+ // Registro histórico incorreto não se sobrepõe ao contrato do modo corrente.
51
+ (0, ledger_1.registrar)((0, thread_1.dirThread)(s.dir, t.id), t.id, 'human_gate', { fase: 'GOAL', estado: 'prevista ao fim do bloco' });
52
+ try {
53
+ (0, hitl_gates_1.abrirPedidoGate)(s.dir, t.id);
54
+ }
55
+ catch {
56
+ pedidosRecusados++;
57
+ }
58
+ }
59
+ const consulta = { ok: true, sessoes: [], detalhe: 'runtime SIMULADO' };
60
+ const antes = (0, pulse_1.montarPulse)(s.carregado, { consulta }).resumo.humanos;
61
+ const t = (0, thread_1.novaThread)(s.carregado, { nome: 'policy', modo: 'auto' }).thread;
62
+ (0, ledger_1.registrar)((0, thread_1.dirThread)(s.dir, t.id), t.id, 'gate_blocked', { fase: 'GOAL', motivo: 'policy.violation' });
63
+ const depois = (0, pulse_1.montarPulse)(s.carregado, { consulta });
64
+ return { simulado: true, pedidosRecusados, humanosSemEscalacao: antes,
65
+ policyPreservada: depois.precisaDeHumanoAgora.some(i => i.thread === t.id && i.motivo === 'policy.violation') };
66
+ }
67
+ finally {
68
+ s.limpar();
69
+ }
70
+ } },
71
+ // O transporte de cada canal tem teste proprio; aqui o alvo e o RECIBO: os quatro
72
+ // precisam responder e ficar distinguiveis depois, que e o que D12 exige.
73
+ { id: 'fx-omnicanal', sobre: 'SIMULADO: os quatro canais respondem e ficam distinguiveis no recibo', precisaDeGit: true, rodar: () => {
74
+ const s = (0, sandbox_1.sandboxGit)('omnicanal');
75
+ // FX1: cada canal de telegram tem a SUA chave; o canario nunca compartilha segredo.
76
+ const nomes = ['ORK_HITL_INGRESS_KEY_HERMES', 'ORK_HITL_INGRESS_KEY_OPENCLAW',
77
+ 'ORK_HITL_OPENCLAW_ACCOUNT', 'ORK_HITL_TELEGRAM_USERS', 'ORK_HITL_TELEGRAM_CHATS',
78
+ 'ORK_HITL_ROOT', 'ORK_HITL_TELEGRAM_BOT_ID'];
79
+ const antigos = nomes.map(n => process.env[n]);
80
+ const chaves = { hermes: 'chave-SIMULADA-do-canario-hermes-00000000',
81
+ openclaw: 'chave-SIMULADA-do-canario-openclaw-000000' };
82
+ // FX5: so o OpenClaw tem conta homologada, e ela entra no corpo assinado.
83
+ const conta = 'conta-SIMULADA-do-canario-openclaw';
84
+ [chaves.hermes, chaves.openclaw, conta, '42', '-7', '/tmp/ork-hitl-canario', '999']
85
+ .forEach((v, i) => { process.env[nomes[i]] = v; });
86
+ try {
87
+ const registro = (0, hitl_canais_1.conferirRegistro)();
88
+ // Um gate por canal: a mensagem do Telegram nao se repete entre pedidos.
89
+ const gate = (nome) => {
90
+ const t = (0, thread_1.novaThread)(s.carregado, { nome, modo: 'classic' }).thread;
91
+ (0, ledger_1.registrar)((0, thread_1.dirThread)(s.dir, t.id), t.id, 'phase_result', { fase: 'GOAL' });
92
+ return { t, pedido: (0, hitl_gates_1.abrirPedidoGate)(s.dir, t.id) };
93
+ };
94
+ const recibos = {};
95
+ for (const canal of ['hermes', 'openclaw']) {
96
+ const { t, pedido } = gate(canal);
97
+ const base = { resposta: '1', origem: 'telegram', canal, por: 'telegram:42',
98
+ mensagem: `telegram:-7:${canal}`, recebidoEm: new Date().toISOString(),
99
+ ...(canal === 'openclaw' ? { conta } : {}) };
100
+ (0, hitl_gates_1.responderGate)(s.dir, t.id, pedido.id, { ...base, prova: (0, hitl_gates_1.assinaturaDaResposta)(t.id, pedido.id, base, chaves[canal]) });
101
+ recibos[canal] = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(s.dir, t.id)).find(e => e.tipo === 'human_gate');
102
+ }
103
+ for (const canal of ['claude-code', 'codex']) {
104
+ const { t, pedido } = gate(canal);
105
+ // Atestado da conexao, como o servidor MCP emite depois de uma elicitation viva.
106
+ const token = Object.freeze({});
107
+ (0, hitl_local_atestado_1.registrarAtestadoLocal)(token, { raiz: s.dir, thread: t.id, pedidoId: pedido.id,
108
+ contexto: (0, hitl_gates_3.contextoHitl)(s.dir, t.id), pedidoSha256: (0, hitl_local_atestado_1.hashPedidoLocal)(pedido), host: canal,
109
+ connectionId: `conn-${canal}`, requestId: `req-${canal}`, opcao: '1',
110
+ recebidoEm: new Date().toISOString(), alvo: 'gate' });
111
+ (0, hitl_gates_2.responderGateLocal)(s.dir, t.id, pedido.id, token);
112
+ recibos[canal] = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(s.dir, t.id)).find(e => e.tipo === 'human_gate');
113
+ }
114
+ // Os quatro responderam, e o recibo de cada um nomeia o seu canal.
115
+ const canais = (0, hitl_canais_1.canaisHomologados)().map(c => c.canal);
116
+ const aprovados = canais.filter(c => recibos[c]?.estado === 'aprovado');
117
+ const lidos = canais.map(c => (0, hitl_canais_1.canalDaResposta)((recibos[c] ?? {})).canal);
118
+ // Recusas: aprovacao cega, ingresso nao homologado e canal de outro transporte.
119
+ let recusas = 0;
120
+ const { t: tr, pedido: pr } = gate('recusas');
121
+ try {
122
+ (0, gates_1.aprovarGateHumano)(s.dir, tr.id, 'push', 'modelo');
123
+ }
124
+ catch {
125
+ recusas++;
126
+ }
127
+ try {
128
+ (0, hitl_canais_1.recusarIngressoNaoHomologado)('transcrito-de-tool');
129
+ }
130
+ catch {
131
+ recusas++;
132
+ }
133
+ const trocado = { resposta: '1', origem: 'telegram', canal: 'codex',
134
+ por: 'telegram:42', mensagem: 'telegram:-7:trocado', recebidoEm: new Date().toISOString() };
135
+ try {
136
+ (0, hitl_gates_1.responderGate)(s.dir, tr.id, pr.id, { ...trocado, prova: (0, hitl_gates_1.assinaturaDaResposta)(tr.id, pr.id, trocado, chaves.hermes) });
137
+ }
138
+ catch {
139
+ recusas++;
140
+ }
141
+ // FX1: chave de um canal nao autentica envelope do outro, mesmo com tudo mais correto.
142
+ const cruzado = { resposta: '1', origem: 'telegram', canal: 'openclaw', conta,
143
+ por: 'telegram:42', mensagem: 'telegram:-7:cruzado', recebidoEm: new Date().toISOString() };
144
+ try {
145
+ (0, hitl_gates_1.responderGate)(s.dir, tr.id, pr.id, { ...cruzado, prova: (0, hitl_gates_1.assinaturaDaResposta)(tr.id, pr.id, cruzado, chaves.hermes) });
146
+ }
147
+ catch {
148
+ recusas++;
149
+ }
150
+ // FX5: conta fora da allowlist do canal, com assinatura correta, tambem e recusada.
151
+ const outraConta = { ...cruzado, conta: 'conta-nao-autorizada', mensagem: 'telegram:-7:conta' };
152
+ try {
153
+ (0, hitl_gates_1.responderGate)(s.dir, tr.id, pr.id, { ...outraConta, prova: (0, hitl_gates_1.assinaturaDaResposta)(tr.id, pr.id, outraConta, chaves.openclaw) });
154
+ }
155
+ catch {
156
+ recusas++;
157
+ }
158
+ const semIngresso = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(s.dir, tr.id)).filter(e => e.tipo === 'human_gate').length;
159
+ // Oferta ao humano: com credencial e conexoes, os quatro ficam disponiveis.
160
+ const oferta = (0, hitl_canais_1.ofertaDeCanais)({ env: process.env,
161
+ conexoesMcp: ['claude-code', 'codex'] });
162
+ return { simulado: true, canaisRegistrados: registro.canais, transportes: registro.transportes,
163
+ chavesIndependentes: registro.chaves,
164
+ aprovados: aprovados.length, canaisDistinguiveis: new Set(lidos).size,
165
+ canalPorInferencia: lidos.filter(c => c === null).length,
166
+ recusas, humanosSemIngresso: semIngresso,
167
+ ofertaDisponivel: oferta.filter(o => o.estado === 'disponivel').length };
168
+ }
169
+ finally {
170
+ nomes.forEach((n, i) => { if (antigos[i] === undefined)
171
+ delete process.env[n];
172
+ else
173
+ process.env[n] = antigos[i]; });
174
+ s.limpar();
175
+ }
176
+ } },
177
+ ];