@orkastery/cli 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (242) 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/onboarding.md +21 -0
  7. package/adapters/claude-code/commands/ork.md +99 -27
  8. package/adapters/claude-code/hooks/hooks.json +6 -1
  9. package/adapters/claude-code/hooks/ork-sensor.js +66 -0
  10. package/adapters/codex/skills/ork/SKILL.md +57 -0
  11. package/adapters/hermes/README.md +163 -0
  12. package/adapters/hermes/bin/ork-abrir-thread.sh +2 -0
  13. package/adapters/hermes/bin/ork-brain.sh +4 -0
  14. package/adapters/hermes/bin/ork-hitl-answer.py +82 -0
  15. package/adapters/hermes/bin/ork-maestro.sh +6 -0
  16. package/adapters/hermes/bin/ork-master-enviar.py +84 -0
  17. package/adapters/hermes/bin/ork-objective-message.sh +17 -0
  18. package/adapters/hermes/bin/ork-objective-status.sh +12 -0
  19. package/adapters/hermes/bin/ork-pulse-enviar.py +47 -0
  20. package/adapters/hermes/hermes.plugin.json +26 -4
  21. package/adapters/hermes/hitl-ingress/__init__.py +356 -0
  22. package/adapters/hermes/hitl-ingress/plugin.yaml +4 -0
  23. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +75 -22
  24. package/adapters/openclaw/README.md +135 -26
  25. package/adapters/openclaw/bin/ork-brain.sh +4 -0
  26. package/adapters/openclaw/construir.sh +19 -0
  27. package/adapters/openclaw/dist/hitl-ingress.js +199 -0
  28. package/adapters/openclaw/dist/index.js +437 -0
  29. package/adapters/openclaw/openclaw.plugin.json +39 -134
  30. package/adapters/openclaw/package.json +26 -0
  31. package/adapters/openclaw/src/hitl-ingress.ts +180 -0
  32. package/adapters/openclaw/src/index.ts +458 -0
  33. package/adapters/openclaw/src/tipos-openclaw.d.ts +58 -0
  34. package/adapters/openclaw/tsconfig.json +15 -0
  35. package/assets/docs/markdownlint-cli2.jsonc +22 -0
  36. package/assets/docs/padroes/documentacao-de-produto.md +133 -0
  37. package/assets/docs/padroes/roadmap-de-produto.md +98 -0
  38. package/assets/docs/produto/README.md +14 -0
  39. package/assets/docs/produto/_modelo-feature.md +59 -0
  40. package/assets/docs/roadmap/README.md +14 -0
  41. package/assets/docs/roadmap/_modelo-item.md +73 -0
  42. package/assets/orkmind-native-schema.json +32 -0
  43. package/assets/orkmind_bridge.py +357 -0
  44. package/assets/orkmind_fixture.py +67 -0
  45. package/assets/orkmind_prospective.py +138 -0
  46. package/assets/reference-tariffs-i07.json +23 -0
  47. package/dist/adapters/claude-bg.js +488 -39
  48. package/dist/adapters/codex-controller-sensor.js +449 -0
  49. package/dist/adapters/codex-controller-worker.js +361 -0
  50. package/dist/adapters/codex-controller.js +252 -0
  51. package/dist/adapters/codex-events.js +205 -0
  52. package/dist/adapters/codex-question.js +26 -0
  53. package/dist/adapters/codex-runner.js +133 -0
  54. package/dist/adapters/codex.js +394 -0
  55. package/dist/agents-md.js +84 -0
  56. package/dist/auditoria.js +11 -8
  57. package/dist/auditrun.js +5 -4
  58. package/dist/board.js +173 -22
  59. package/dist/branch-de-estado.js +136 -0
  60. package/dist/canarios-hitl.js +177 -0
  61. package/dist/canarios-i43.js +543 -0
  62. package/dist/canarios-pulse.js +147 -0
  63. package/dist/canarios-sensores.js +129 -0
  64. package/dist/canarios.js +111 -2
  65. package/dist/catalogo.js +9 -0
  66. package/dist/ci.js +216 -0
  67. package/dist/ciclos.js +2 -1
  68. package/dist/claim-lint.js +64 -0
  69. package/dist/claims.js +60 -0
  70. package/dist/company-brain-capture.js +195 -0
  71. package/dist/company-brain-cli.js +123 -0
  72. package/dist/company-brain-client.js +61 -0
  73. package/dist/company-brain-contract.js +166 -0
  74. package/dist/company-brain-journal.js +192 -0
  75. package/dist/company-brain-mcp.js +33 -0
  76. package/dist/company-brain-migration.js +60 -0
  77. package/dist/company-brain-source.js +177 -0
  78. package/dist/company-brain-worker.js +18 -0
  79. package/dist/conducao-texto.js +61 -0
  80. package/dist/conducao.js +876 -0
  81. package/dist/contrato-publico.js +39 -0
  82. package/dist/creation-operation-store.js +251 -0
  83. package/dist/creation-operation.js +148 -0
  84. package/dist/decisao-autonoma.js +183 -0
  85. package/dist/delegation.js +79 -0
  86. package/dist/demo.js +120 -0
  87. package/dist/docs.js +828 -0
  88. package/dist/doctor.js +183 -24
  89. package/dist/entrega-pr.js +117 -0
  90. package/dist/escopo-escrita.js +71 -0
  91. package/dist/estado-thread.js +222 -0
  92. package/dist/evalrunner.js +12 -0
  93. package/dist/fabrica-estado.js +329 -0
  94. package/dist/fabrica-publicar.js +76 -0
  95. package/dist/fix.js +21 -0
  96. package/dist/gates.js +55 -9
  97. package/dist/handoff.js +39 -23
  98. package/dist/hitl-canais.js +333 -0
  99. package/dist/hitl-classificacao.js +131 -0
  100. package/dist/hitl-contract.js +465 -0
  101. package/dist/hitl-estado.js +136 -0
  102. package/dist/hitl-gates.js +554 -0
  103. package/dist/hitl-ingress-receipt.js +382 -0
  104. package/dist/hitl-local-atestado.js +57 -0
  105. package/dist/hitl-local-receipt.js +328 -0
  106. package/dist/hitl-local.js +143 -0
  107. package/dist/hitl-lock.js +139 -0
  108. package/dist/hitl-lote.js +223 -0
  109. package/dist/hitl-native-offer.js +97 -0
  110. package/dist/hitl-native.js +65 -0
  111. package/dist/hitl-presentation.js +209 -0
  112. package/dist/hitl-public-receipt.js +176 -0
  113. package/dist/hitl-resumo.js +209 -0
  114. package/dist/hitl-sessions.js +475 -0
  115. package/dist/hitl.js +711 -0
  116. package/dist/horario.js +269 -0
  117. package/dist/hosts.js +198 -22
  118. package/dist/index.js +1736 -93
  119. package/dist/indice.js +99 -0
  120. package/dist/init.js +28 -3
  121. package/dist/integracoes-locais.js +17 -0
  122. package/dist/leases.js +65 -21
  123. package/dist/ledger-stats.js +272 -0
  124. package/dist/ledger.js +107 -2
  125. package/dist/licoes.js +215 -0
  126. package/dist/liveness.js +218 -0
  127. package/dist/maestro-actions.js +51 -0
  128. package/dist/maestro-authority.js +152 -0
  129. package/dist/maestro-cli.js +97 -0
  130. package/dist/maestro-contract.js +55 -0
  131. package/dist/maestro-discovery.js +132 -0
  132. package/dist/maestro-runtime.js +268 -0
  133. package/dist/maestro-snapshot.js +86 -0
  134. package/dist/maestro-sources.js +224 -0
  135. package/dist/manifest.js +165 -6
  136. package/dist/maquina.js +102 -0
  137. package/dist/master-audit.js +101 -0
  138. package/dist/master-batch.js +43 -0
  139. package/dist/master-digest.js +149 -0
  140. package/dist/master-migracao.js +155 -0
  141. package/dist/master.js +361 -77
  142. package/dist/mcp-artifacts.js +234 -0
  143. package/dist/mcp-git.js +433 -0
  144. package/dist/mcp-install.js +310 -0
  145. package/dist/mcp-maestro.js +25 -0
  146. package/dist/mcp-server.js +469 -0
  147. package/dist/mcp-ship.js +436 -0
  148. package/dist/mcp-verify.js +173 -0
  149. package/dist/memoria-humana.js +207 -0
  150. package/dist/memoria.js +314 -78
  151. package/dist/memory-migration.js +279 -0
  152. package/dist/memory-prospective.js +148 -0
  153. package/dist/modos-migracao.js +191 -0
  154. package/dist/modos.js +151 -15
  155. package/dist/monitor-lock.js +110 -0
  156. package/dist/objective.js +449 -0
  157. package/dist/ocupacao.js +267 -0
  158. package/dist/onboarding.js +303 -0
  159. package/dist/orkmind.js +389 -90
  160. package/dist/orquestracao.js +96 -69
  161. package/dist/phase.js +596 -150
  162. package/dist/playbook-capabilities.js +150 -0
  163. package/dist/playbook-contracts.js +188 -0
  164. package/dist/portfolio-context.js +61 -0
  165. package/dist/portfolio.js +180 -0
  166. package/dist/preflight.js +207 -0
  167. package/dist/process-audit.js +112 -0
  168. package/dist/project-state.js +93 -0
  169. package/dist/prompts.js +25 -5
  170. package/dist/prova-minima.js +112 -0
  171. package/dist/pulse-cadencia.js +163 -0
  172. package/dist/pulse-consentimento.js +260 -0
  173. package/dist/pulse-delivery.js +323 -0
  174. package/dist/pulse-resposta.js +542 -0
  175. package/dist/pulse.js +262 -0
  176. package/dist/ratelimit.js +35 -2
  177. package/dist/recall.js +80 -3
  178. package/dist/redacao-saida.js +45 -0
  179. package/dist/redacao-url.js +66 -0
  180. package/dist/retry.js +637 -115
  181. package/dist/roadmap-reservas.js +243 -0
  182. package/dist/runtime-ambiente.js +38 -0
  183. package/dist/runtime-context.js +215 -0
  184. package/dist/runtime-profiles.js +869 -0
  185. package/dist/runtimes.js +103 -0
  186. package/dist/sandbox.js +9 -0
  187. package/dist/session-events.js +203 -0
  188. package/dist/session-watcher-claude.js +637 -0
  189. package/dist/session-watcher.js +683 -0
  190. package/dist/sessoes-adopt.js +154 -0
  191. package/dist/sessoes-inventario.js +159 -0
  192. package/dist/sessoes.js +16 -35
  193. package/dist/setup.js +691 -0
  194. package/dist/ship.js +150 -15
  195. package/dist/slug.js +1 -1
  196. package/dist/thread-close.js +115 -0
  197. package/dist/thread.js +142 -28
  198. package/dist/tokens.js +1 -1
  199. package/dist/util.js +4 -2
  200. package/dist/verify-sandbox.js +265 -0
  201. package/dist/verify.js +270 -25
  202. package/dist/versao.js +60 -0
  203. package/dist/worktree.js +31 -2
  204. package/dist/write-activation.js +291 -0
  205. package/dist/yaml.js +2 -0
  206. package/eval/casos/onboarding.json +102 -0
  207. package/eval/casos/orkastery-bootstrap.json +35 -1
  208. package/eval/casos/scope-check-capability-map.json +45 -7
  209. package/eval/casos/ship-release.json +2 -2
  210. package/eval/fixtures/b0-slug-e-modos/caso.json +35 -22
  211. package/eval/fixtures/b2-master-log/caso.json +1 -1
  212. package/eval/fixtures/fx-adapter-editado-detectado/caso.json +19 -0
  213. package/eval/fixtures/fx-auto-quiet/caso.json +11 -0
  214. package/eval/fixtures/fx-blanket-approve/caso.json +10 -0
  215. package/eval/fixtures/fx-check-runtime-cruzado/caso.json +20 -0
  216. package/eval/fixtures/fx-codex-dry/caso.json +18 -0
  217. package/eval/fixtures/fx-donewhen-executavel/caso.json +15 -0
  218. package/eval/fixtures/fx-estado-dividido/caso.json +15 -0
  219. package/eval/fixtures/fx-fase-orfa/caso.json +116 -0
  220. package/eval/fixtures/fx-happy/caso.json +6 -4
  221. package/eval/fixtures/fx-hitl-latency/caso.json +20 -0
  222. package/eval/fixtures/fx-indice-reversao/caso.json +25 -0
  223. package/eval/fixtures/fx-listagem-abertas/caso.json +16 -0
  224. package/eval/fixtures/fx-maestro-bootstrap/caso.json +21 -0
  225. package/eval/fixtures/fx-modo-aposentado-escritor/caso.json +21 -0
  226. package/eval/fixtures/fx-modo-aposentado-leitor/caso.json +16 -0
  227. package/eval/fixtures/fx-objective-oscillation/caso.json +14 -0
  228. package/eval/fixtures/fx-omnicanal/caso.json +17 -0
  229. package/eval/fixtures/fx-sensores-runtime/caso.json +17 -0
  230. package/monitor/company-brain.cjs +17 -0
  231. package/monitor/pulse-scope.cjs +45 -0
  232. package/monitor/pulse.cron +18 -0
  233. package/monitor/varredura-pulse.sh +27 -0
  234. package/package.json +25 -4
  235. package/schemas/claims.schema.json +25 -0
  236. package/schemas/company-brain.schema.json +1158 -0
  237. package/schemas/creation-operation.schema.json +365 -0
  238. package/schemas/maestro-snapshot.schema.json +2716 -0
  239. package/skills/core/onboarding/SKILL.md +50 -0
  240. package/skills/core/orkastery-bootstrap/SKILL.md +95 -65
  241. package/skills/core/thread-state/SKILL.md +3 -1
  242. package/skills/governance/scope-check-capability-map/SKILL.md +17 -7
package/dist/retry.js CHANGED
@@ -1,24 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * Politica de retry TIPADA e retomada automatica (bloco B3).
4
- *
5
- * O B1 deu ao `ork` o MOTIVO tipado de cada reprovacao; o B2 deu a thread um estado
6
- * retomavel em disco. O B3 e a camada que liga os dois: para cada motivo tipado existe
7
- * UMA acao de retry deterministica, igual em todos os modos de conducao.
8
- *
9
- * Tres regras mandam aqui, e nenhuma delas e afrouxavel por modo:
10
- *
11
- * 1. **Custo nunca reexecuta.** `cost.violation` (despacho que seria redirecionado
12
- * para provider pago) e o unico motivo sem retry automatico nenhum. Reexecutar
13
- * violacao de custo e gastar de novo, e a licao dos US$ 16 esta no manifesto.
14
- * 2. **O modo afrouxa a PAUSA, nunca a VERIFICACAO.** A politica e identica em
15
- * `#Look` e em `#Auto`. O que muda e a AUTORIZACAO: num bloco de loop que pausa,
16
- * a acao espera `ork gate approve`; num bloco sem pausa (`#Maestro`, `#Auto`) o
17
- * `ork` executa sozinho e grava a decisao autonoma no ledger.
18
- * 3. **O limite de escalacao pausa QUALQUER modo.** Estouradas as tentativas do
19
- * manifesto pelo MESMO motivo na MESMA fase, a acao vira `escalar-humano`,
20
- * inclusive em `#Auto`. E a invariante de escalacao tipada da secao 3.5.
21
- */
22
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
23
3
  if (k2 === undefined) k2 = k;
24
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -61,6 +41,7 @@ exports.tabelaDaPolitica = tabelaDaPolitica;
61
41
  exports.ultimaReprovacao = ultimaReprovacao;
62
42
  exports.tentativasFeitas = tentativasFeitas;
63
43
  exports.blocoPausa = blocoPausa;
44
+ exports.estadoTerminalDeRetry = estadoTerminalDeRetry;
64
45
  exports.planejarRetry = planejarRetry;
65
46
  exports.escalarEsforco = escalarEsforco;
66
47
  exports.redespachar = redespachar;
@@ -69,24 +50,58 @@ exports.retomarPedido = retomarPedido;
69
50
  exports.retomarFila = retomarFila;
70
51
  exports.retomarPorId = retomarPorId;
71
52
  exports.cancelarPedido = cancelarPedido;
53
+ exports.alvosDeFallback = alvosDeFallback;
54
+ exports.proximoAlvo = proximoAlvo;
55
+ exports.producaoNoIntervalo = producaoNoIntervalo;
72
56
  exports.executarRetry = executarRetry;
73
57
  exports.textoDoPlano = textoDoPlano;
74
58
  exports.textoDoRetry = textoDoRetry;
75
59
  exports.textoDaRetomada = textoDaRetomada;
60
+ const hitl_gates_1 = require("./hitl-gates");
61
+ const claude_bg_1 = require("./adapters/claude-bg");
62
+ /**
63
+ * Politica de retry TIPADA e retomada automatica (bloco B3).
64
+ *
65
+ * O B1 deu ao `ork` o MOTIVO tipado de cada reprovacao; o B2 deu a thread um estado
66
+ * retomavel em disco. O B3 e a camada que liga os dois: para cada motivo tipado existe
67
+ * UMA acao de retry deterministica, igual em todos os modos de conducao.
68
+ *
69
+ * Tres regras mandam aqui, e nenhuma delas e afrouxavel por modo:
70
+ *
71
+ * 1. **Custo nunca reexecuta.** `cost.violation` (despacho que seria redirecionado
72
+ * para provider pago) e o unico motivo sem retry automatico nenhum. Reexecutar
73
+ * violacao de custo e gastar de novo, e a politica `subscription-only` esta no manifesto.
74
+ * 2. **O modo afrouxa a PAUSA, nunca a VERIFICACAO.** A politica e identica em
75
+ * `#Classic` e em `#Auto`. O que muda e a AUTORIZACAO: num bloco de loop que pausa,
76
+ * a acao espera `ork gate approve`; num bloco sem pausa (`#Maestro`, `#Auto`) o
77
+ * `ork` executa sozinho e grava a decisao autonoma no ledger.
78
+ * 3. **O limite de escalacao pausa QUALQUER modo.** Estouradas as tentativas do
79
+ * manifesto pelo MESMO motivo na MESMA fase, a acao vira `escalar-humano`,
80
+ * inclusive em `#Auto`. E a invariante de escalacao tipada da secao 3.5.
81
+ */
76
82
  const fs = __importStar(require("node:fs"));
77
83
  const path = __importStar(require("node:path"));
78
- const adapter = __importStar(require("./adapters/claude-bg"));
79
84
  const fix_1 = require("./fix");
85
+ const runtimes_1 = require("./runtimes");
86
+ const setup_1 = require("./setup");
80
87
  const gates_1 = require("./gates");
81
88
  const ledger_1 = require("./ledger");
82
89
  const phase_1 = require("./phase");
90
+ const runtime_context_1 = require("./runtime-context");
83
91
  const slug_1 = require("./slug");
84
92
  const policies_1 = require("./policies");
85
93
  const ratelimit_1 = require("./ratelimit");
94
+ const session_watcher_claude_1 = require("./session-watcher-claude");
95
+ const runtime_profiles_1 = require("./runtime-profiles");
86
96
  const thread_1 = require("./thread");
87
97
  const tokens_1 = require("./tokens");
88
98
  const util_1 = require("./util");
89
99
  const worktree_1 = require("./worktree");
100
+ const delegation_1 = require("./delegation");
101
+ const gates_2 = require("./gates");
102
+ const horario_1 = require("./horario");
103
+ const conducao_1 = require("./conducao");
104
+ const conducao_texto_1 = require("./conducao-texto");
90
105
  /**
91
106
  * Escada de esforco da 2a tentativa em diante.
92
107
  *
@@ -136,11 +151,22 @@ exports.POLITICA_DE_RETRY = {
136
151
  porque: 'policy com severidade block reprova em qualquer modo: reexecutar sozinho seria a maquina revogando a policy do manifesto',
137
152
  correcao: 'corrija a violacao no pedido/manifesto; policy block nao se resolve por repeticao',
138
153
  },
154
+ 'runtime.autoconferencia': {
155
+ motivo: 'runtime.autoconferencia',
156
+ // `escalar-humano` e nao `sem-retry`: `sem-retry` e o lugar reservado a violacao
157
+ // de CUSTO, e o teste do bloco B3 guarda essa exclusividade de proposito. Aqui a
158
+ // maquina nao esta proibida de agir, ela esta sem o que escolher sozinha: quem
159
+ // decide em que runtime o CHECK roda e quem conduz a thread.
160
+ acao: 'escalar-humano',
161
+ automatica: false,
162
+ porque: 'repetir o despacho no MESMO runtime daria o mesmo resultado: o que falta nao e tentativa, e um validador que nao seja quem executou. Escolher o outro runtime e decisao de conducao, nao retry',
163
+ correcao: 'despache o CHECK em outro runtime (`ork phase run <thread> CHECK --runtime <outro>`), ou tire a exigencia de validacao cruzada da thread se ela nao se justifica',
164
+ },
139
165
  'cost.violation': {
140
166
  motivo: 'cost.violation',
141
167
  acao: 'sem-retry',
142
168
  automatica: false,
143
- porque: 'violacao de CUSTO nunca recebe retry automatico: cada reexecucao gastaria de novo pelo provider pago (a licao dos US$ 16 e regra de codigo, nao lembrete)',
169
+ porque: 'violacao de CUSTO nunca recebe retry automatico: cada reexecucao gastaria de novo pelo provider pago (subscription-only e regra de codigo, nao lembrete)',
144
170
  correcao: 'tire do ambiente a variavel que redireciona o despacho e rode a fase de novo a mao, com o gasto sob os olhos de um humano',
145
171
  },
146
172
  'verify.regression': {
@@ -157,6 +183,27 @@ exports.POLITICA_DE_RETRY = {
157
183
  porque: 'o comando falha sem baseline para separar regressao de divida: a correcao dirigida conserta ou grava a baseline, mas nao chuta de quem e a culpa',
158
184
  correcao: 'ork fix open <thread>, ou ork verify <thread> --baseline antes do proximo GO',
159
185
  },
186
+ 'verify.timeout': {
187
+ motivo: 'verify.timeout',
188
+ acao: 'reexecutar',
189
+ automatica: true,
190
+ porque: 'o prazo estourou antes de o comando terminar: a prova nao chegou ao fim, e carga de maquina nao e defeito. A reexecucao e completa, e o limite de escalacao do manifesto impede repetir para sempre',
191
+ correcao: 'ork verify <thread> de novo; se estourar sempre, suba verify.timeout_ms no manifesto ou divida o comando',
192
+ },
193
+ 'verify.sem-veredito': {
194
+ motivo: 'verify.sem-veredito',
195
+ acao: 'reexecutar',
196
+ automatica: true,
197
+ porque: 'um comando nao chegou a rodar ou o produto mudou no meio da rodada: nao ha veredito sobre ele, e a prova certa e rodar de novo, inteira',
198
+ correcao: 'ork verify <thread> de novo, sem editar a worktree durante a rodada',
199
+ },
200
+ 'ci.failed': {
201
+ motivo: 'ci.failed',
202
+ acao: 'escalar-humano',
203
+ automatica: false,
204
+ porque: 'o recibo pertence ao provedor e ao SHA candidato: repetir uma fase não cria nem altera legitimamente esse resultado',
205
+ correcao: 'publique a candidata, aguarde o check obrigatório e repita o SHIP; se reprovou, corrija a causa registrada no runner',
206
+ },
160
207
  'runtime.unavailable': {
161
208
  motivo: 'runtime.unavailable',
162
209
  acao: 'reexecutar',
@@ -164,6 +211,11 @@ exports.POLITICA_DE_RETRY = {
164
211
  porque: 'falha de infraestrutura no despacho nao e falha de conteudo: o MESMO prompt, com o mesmo sha256, e redespachado sem alterar uma virgula',
165
212
  correcao: 'ork retry run <thread> redespacha o prompt ja gravado em disco',
166
213
  },
214
+ 'runtime.silencio': {
215
+ motivo: 'runtime.silencio', acao: 'reexecutar', automatica: true,
216
+ porque: 'fase sem heartbeat: retomar o despacho original com hash conferido e limite de tentativas',
217
+ correcao: 'ork retry run <thread> --motivo runtime.silencio',
218
+ },
167
219
  'runtime.rate-limited': {
168
220
  motivo: 'runtime.rate-limited',
169
221
  acao: 'esperar-janela',
@@ -171,6 +223,24 @@ exports.POLITICA_DE_RETRY = {
171
223
  porque: 'o limite de uso tem HORA de volta: a fase entra na fila duravel e e retomada na janela seguinte, sem humano no meio e sem gastar por fora da assinatura',
172
224
  correcao: 'ork retry list mostra a fila; ork retry resume retoma o que ja liberou',
173
225
  },
226
+ // I-33 (D5, D11): a falha e da CONTA. O perfil sai do rodizio e o MESMO prompt vai ao proximo
227
+ // perfil ativo, depois ao runtime de fallback do bloco, e so por ultimo a fila duravel. A
228
+ // rotacao so redespacha quando a prova do `ork` confirma que o intervalo nao produziu nada;
229
+ // producao parcial sob cota esgotada vira `human.pending`, nunca redespacho sobre a worktree.
230
+ 'runtime.quota-exhausted': {
231
+ motivo: 'runtime.quota-exhausted',
232
+ acao: 'reexecutar',
233
+ automatica: true,
234
+ porque: 'a cota ou os creditos da CONTA acabaram, nao a infraestrutura: o perfil sai do rodizio ate o prazo e o MESMO prompt, com o mesmo sha256, segue no proximo perfil, no runtime de fallback ou na fila ate o menor prazo',
235
+ correcao: 'ork accounts list mostra o perfil esgotado; ork retry run <thread> rotaciona o prompt ja gravado',
236
+ },
237
+ 'runtime.auth-missing': {
238
+ motivo: 'runtime.auth-missing',
239
+ acao: 'reexecutar',
240
+ automatica: true,
241
+ porque: 'a conta do runtime perdeu a autenticacao: o perfil nunca mais recebe despacho ate o login ser refeito pelo proprio CLI, e o MESMO prompt segue no proximo perfil ou no runtime de fallback',
242
+ correcao: 'ork accounts check confere o login de cada perfil; ork retry run <thread> rotaciona o prompt ja gravado',
243
+ },
174
244
  'tree.blocked': {
175
245
  motivo: 'tree.blocked',
176
246
  acao: 'sincronizar-worktree',
@@ -185,6 +255,24 @@ exports.POLITICA_DE_RETRY = {
185
255
  porque: 'o lease e de outra thread e vai ser liberado: a fila ja serializa, entao a acao e tentar de novo quando chegar a vez, nunca furar a fila',
186
256
  correcao: 'ork lease list mostra a fila; a vez chega sem ninguem forcar',
187
257
  },
258
+ // I-36 (T6): a vez chega quando a conducao atual terminar. Redespachar agora so recebe a mesma recusa,
259
+ // e passar na frente de quem conduz e exatamente o incidente de 19/09/2026.
260
+ 'conducao.em-andamento': {
261
+ motivo: 'conducao.em-andamento',
262
+ acao: 'esperar-janela',
263
+ automatica: true,
264
+ porque: 'outra conducao executa na mesma worktree agora: a vez chega quando ela terminar, e furar a fila e o incidente de 19/09/2026',
265
+ correcao: 'ork conducao status <thread> mostra quem conduz; repita o pedido com --esperar <min> para seguir sozinho quando liberar',
266
+ },
267
+ // I-41 (D10): formato e defeito de quem escreveu o pedido, e a spec dirigida conserta.
268
+ // Escalar isto para o dono seria pedir que ele revise a sintaxe do robo.
269
+ 'hitl.formato': {
270
+ motivo: 'hitl.formato',
271
+ acao: 'corrigir-dirigido',
272
+ automatica: true,
273
+ porque: 'o pedido HITL saiu fora do formato obrigatorio: quem errou foi o emissor, e a correcao e reemitir com alternativas rotuladas, consequencia por alternativa e exatamente uma recomendada com o porque',
274
+ correcao: 'ork fix open <thread> abre a rodada com a spec exata do formato',
275
+ },
188
276
  'human.pending': {
189
277
  motivo: 'human.pending',
190
278
  acao: 'escalar-humano',
@@ -245,6 +333,9 @@ function tentativasFeitas(raiz, threadId, fase, motivo) {
245
333
  function blocoPausa(thread, fase) {
246
334
  return (0, thread_1.blocoDaThread)(thread, fase).pausa;
247
335
  }
336
+ function estadoTerminalDeRetry(estado) {
337
+ return ['completed', 'failed', 'stopped', 'cancelled', 'interrupted', 'terminated'].includes(estado.trim().toLowerCase());
338
+ }
248
339
  /**
249
340
  * Calcula o plano de retry SEM executar nada.
250
341
  *
@@ -327,10 +418,14 @@ function planejarRetry(carregado, threadId, opcoes = {}) {
327
418
  const effort = escala ? escalarEsforco(effortBase, tentativas) : effortBase;
328
419
  const acao = escala ? 'escalar-esforco' : politica.acao;
329
420
  const pausa = blocoPausa(thread, fase);
330
- const autorizado = (opcoes.autorizadoPor ?? '').trim() !== '';
421
+ const delegacao = (0, delegation_1.avaliarDelegacao)(manifesto, { thread: threadId, modo: thread.modo, fase,
422
+ escopo: 'premissas', conteudo: detalhe + '\n' + (0, delegation_1.conteudoDePremissas)(raiz, threadId) });
423
+ const autorizado = delegacao.permitida || (0, gates_2.aprovacoesHumanas)(raiz, threadId).some(e => e.fase === fase && e.autorizadoPor === opcoes.autorizadoPor);
331
424
  let bloqueio = null;
332
425
  let automatica = politica.automatica;
333
426
  let razao = politica.porque;
427
+ if (delegacao.permitida && politica.automatica)
428
+ razao += `; ${delegacao.razao}; delegado ${delegacao.delegado}; evidência ${delegacao.evidencia}`;
334
429
  if (!politica.automatica) {
335
430
  automatica = false;
336
431
  bloqueio = 'politica.nao-automatica';
@@ -345,21 +440,18 @@ function planejarRetry(carregado, threadId, opcoes = {}) {
345
440
  `(${(0, thread_1.blocoDaThread)(thread, fase).pausaSobre}): a acao ${acao} espera autorizacao humana. ` +
346
441
  'O modo afrouxa a pausa, nunca a verificacao.';
347
442
  }
443
+ if (motivo === 'runtime.silencio') {
444
+ const despacho = ultimoDespachoFalho(raiz, threadId, fase, true);
445
+ const prova = opcoes.estadoDaSessao;
446
+ if (!prova || prova.sessionId !== despacho?.sessionId || !estadoTerminalDeRetry(prova.estado)) {
447
+ automatica = false;
448
+ bloqueio = 'politica.nao-automatica';
449
+ razao = 'runtime.silencio exige prova terminal atual da mesma sessao; ausencia ou silencio nao provam morte';
450
+ }
451
+ }
348
452
  return {
349
- thread: threadId,
350
- modo: thread.modo,
351
- fase,
352
- motivo,
353
- detalhe,
354
- politica,
355
- acao,
356
- tentativas,
357
- limite,
358
- effort,
359
- effortAnterior,
360
- automatica,
361
- bloqueio,
362
- razao,
453
+ thread: threadId, modo: thread.modo, fase, motivo, detalhe, politica, acao, tentativas,
454
+ limite, effort, effortAnterior, automatica, bloqueio, razao,
363
455
  };
364
456
  }
365
457
  /** O esforco depois de `n` escaladas a partir do esforco base do manifesto. */
@@ -380,12 +472,19 @@ function escalarEsforco(base, degraus) {
380
472
  * de provider pago enquanto a fase esperava, a retomada recusa em vez de gastar.
381
473
  */
382
474
  function redespachar(carregado, thread, fase, promptRelativo, sha, opcoes = {}) {
475
+ const run = () => redespacharSobLock(carregado, (0, thread_1.lerThread)(carregado.raiz, thread.id), fase, promptRelativo, sha, opcoes);
476
+ return opcoes.dryRun ? run() : (0, hitl_gates_1.comLockHitl)(carregado.raiz, thread.id, run);
477
+ }
478
+ function redespacharSobLock(carregado, thread, fase, promptRelativo, sha, opcoes = {}) {
383
479
  const { raiz, manifesto } = carregado;
384
480
  const absoluto = path.isAbsolute(promptRelativo)
385
481
  ? promptRelativo
386
482
  : path.join(raiz, promptRelativo);
387
483
  const slug = opcoes.slug ?? (0, phase_1.slugDaSessao)(thread, fase);
388
484
  const vazio = { slug, sessionId: null, verificada: false, sinal: null, comando: [] };
485
+ const erroDeEstado = (0, phase_1.estadoParaDespacho)(raiz, thread, opcoes.dryRun);
486
+ if (erroDeEstado)
487
+ return { ...vazio, ok: false, motivo: 'tree.blocked', detalhe: erroDeEstado, dryRun: opcoes.dryRun === true };
389
488
  if (!fs.existsSync(absoluto)) {
390
489
  return {
391
490
  ...vazio,
@@ -424,73 +523,171 @@ function redespachar(carregado, thread, fase, promptRelativo, sha, opcoes = {})
424
523
  });
425
524
  return { ...vazio, ok: false, motivo, detalhe, dryRun: opcoes.dryRun === true };
426
525
  }
427
- const cwd = opcoes.cwd ?? thread.worktree ?? raiz;
428
- // Mesma resolucao do `ork phase run`: a retomada carimba no ledger o par que ela de
429
- // fato despachou, inclusive quando o pedido da fila trazia um modelo proprio.
430
- const { model, effort } = (0, phase_1.resolverDespacho)(manifesto, {
431
- model: opcoes.model ?? undefined,
432
- effort: opcoes.effort ?? undefined,
433
- });
434
- const resultado = adapter.despachar({
435
- prompt,
436
- nome: slug,
437
- cwd,
438
- model,
439
- effort,
440
- dryRun: opcoes.dryRun,
441
- });
442
- if (opcoes.dryRun) {
526
+ // I-36 (T6): validado o pedido, a retomada toma a conducao antes de redespachar, como o
527
+ // `ork phase run`. A mesma fase com o mesmo prompt de quem ja conduz nao abre sessao nova;
528
+ // outro pedido espera a vez.
529
+ const identidade = (0, runtime_context_1.novaIdentidadeDeDespacho)(thread.id, fase);
530
+ const canal = opcoes.canal ?? (0, conducao_1.canalDoProcesso)();
531
+ const doBlocoDaConducao = (0, setup_1.limitesDoBloco)(manifesto, (0, setup_1.lerSetup)(raiz), thread.modo, fase);
532
+ const pedidoDeConducao = { canal, correlacao: null, operacao: 'retry.run', fase, promptSha256: sha,
533
+ identidade: identidade.dispatchId, prazoMs: (0, phase_1.prazoDaSessao)(doBlocoDaConducao) };
534
+ let conducao = null;
535
+ if (!opcoes.dryRun) {
536
+ const tomada = (0, conducao_1.tomarConducao)(raiz, thread.id, pedidoDeConducao);
537
+ if (!tomada.ok) {
538
+ if (tomada.idempotente && tomada.atual) {
539
+ (0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, thread.id), thread.id, ledger_1.TIPOS_DE_EVENTO.despachoIdempotente, { fase, promptSha256: sha, canal,
540
+ sessionId: tomada.atual.sessao, origem: opcoes.origem ?? 'retry',
541
+ razao: 'mesma fase e mesmo prompt de quem ja conduz: a sessao em andamento atende a retomada, sem sessao nova' });
542
+ return { ...vazio, ok: true, sessionId: tomada.atual.sessao, motivo: null, dryRun: false,
543
+ detalhe: `a sessao ${String(tomada.atual.sessao).slice(0, 8)} ja conduz a fase ${fase} com este prompt: nada foi redespachado` };
544
+ }
545
+ const recusa = (0, conducao_1.recusaDeConducao)(thread.id, tomada.atual, pedidoDeConducao);
546
+ (0, conducao_1.registrarRecusa)(raiz, recusa);
547
+ return { ...vazio, ok: false, motivo: 'conducao.em-andamento', detalhe: recusa.texto, dryRun: false };
548
+ }
549
+ conducao = tomada;
550
+ }
551
+ try {
552
+ const cwd = opcoes.cwd ?? thread.worktree ?? raiz;
553
+ // Mesma resolucao do `ork phase run`: a retomada carimba no ledger o trio que ela de
554
+ // fato despachou, inclusive quando o pedido da fila trazia um modelo proprio. O setup
555
+ // do bloco (feature #setup) vale aqui tambem: a fase retomada abre no MESMO runtime
556
+ // que a fase original abriria.
557
+ const doBloco = (0, setup_1.configDoBloco)((0, setup_1.lerSetup)(raiz), thread.modo, fase);
558
+ const { runtime, model, effort } = (0, phase_1.resolverDespacho)(manifesto, {
559
+ runtime: opcoes.runtime ?? undefined,
560
+ model: opcoes.model ?? undefined,
561
+ effort: opcoes.effort ?? undefined,
562
+ }, doBloco);
563
+ const rt = (0, runtimes_1.resolverRuntime)(runtime);
564
+ // D14 (GO-FIX 2): a retomada claude-bg abre com a mesma colaboração e o mesmo contexto de
565
+ // runtime do `ork phase run`; sem eles, o PLAN retomado saía sem negar Edit, Write e
566
+ // NotebookEdit e sem a allowlist do núcleo. O ramo Codex não muda.
567
+ let contextoRuntime;
568
+ if (runtime === 'claude-bg') {
569
+ try {
570
+ contextoRuntime = (0, runtime_context_1.contextoDoProjeto)(raiz, runtime, cwd, thread.id);
571
+ }
572
+ catch (e) {
573
+ return { ...vazio, ok: false, motivo: 'runtime.unavailable',
574
+ detalhe: `runtime.unavailable: ${e.message}`, dryRun: opcoes.dryRun === true };
575
+ }
576
+ if (contextoRuntime)
577
+ contextoRuntime.identidade = identidade;
578
+ }
579
+ // I-33 (D4, D7): a retomada escolhe o perfil como o `ork phase run`, com o login conferido.
580
+ let escolha;
581
+ try {
582
+ escolha = (0, phase_1.perfilParaDespacho)(carregado, thread, fase, runtime, { perfil: opcoes.perfil ?? undefined, dryRun: opcoes.dryRun,
583
+ origem: opcoes.origem ?? 'retry' });
584
+ }
585
+ catch (e) {
586
+ escolha = { perfil: null, configurados: 0, erro: e.message };
587
+ }
588
+ if (escolha.erro) {
589
+ const motivo = escolha.motivo ?? 'runtime.unavailable';
590
+ const prazo = motivo === 'runtime.quota-exhausted' ? (0, runtime_profiles_1.prazoDoRuntime)((0, runtime_profiles_1.lerPerfisComContas)(raiz), runtime, (0, runtime_profiles_1.politicaDeRotacao)(manifesto)) : null;
591
+ return { ...vazio, ok: false, motivo, detalhe: `${motivo}: ${escolha.erro}`, dryRun: opcoes.dryRun === true,
592
+ sinal: prazo ? { resetEm: prazo, fonte: 'iso', trecho: `menor esgotadoAte dos perfis ${runtime}` } : null };
593
+ }
594
+ const perfil = escolha.perfil ?? undefined;
595
+ // I-33 (N1): o HEAD do GO codex sai antes do despacho, como no `ork phase run`.
596
+ const headNoDespacho = fase === 'GO' && runtime === 'codex' && !opcoes.dryRun ? (0, session_watcher_claude_1.headDaWorktree)(cwd) : undefined;
597
+ const resultado = rt.despachar({
598
+ prompt,
599
+ nome: slug,
600
+ cwd,
601
+ model,
602
+ effort,
603
+ dryRun: opcoes.dryRun,
604
+ sandbox: manifesto.runtime.sandbox,
605
+ logDir: path.join((0, thread_1.dirThread)(raiz, thread.id), 'sessoes'),
606
+ vinculo: { thread: thread.id, fase, promptSha256: sha },
607
+ ...(perfil ? { perfil } : {}),
608
+ ...(runtime === 'claude-bg' ? { contextoRuntime, ...(fase === 'PLAN' ? { colaboracao: 'plan' } : {}) } : {}),
609
+ // I-36 (D4): a sessao retomada herda a identidade do despacho e o canal, como no `ork phase run`.
610
+ ambienteExtra: (0, conducao_1.ambienteDaConducao)(identidade.dispatchId, thread.id, canal),
611
+ });
612
+ if (opcoes.dryRun) {
613
+ return {
614
+ ok: true,
615
+ slug,
616
+ sessionId: null,
617
+ verificada: false,
618
+ motivo: null,
619
+ detalhe: 'ensaio (--dry-run): comando montado, nada despachado',
620
+ sinal: null,
621
+ comando: resultado.comando,
622
+ dryRun: true,
623
+ };
624
+ }
625
+ if (!resultado.ok || !resultado.sessionId) {
626
+ (0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, thread.id), thread.id, 'phase_dispatch_failed', { fase, runtime, slug, cwd,
627
+ promptPath: absoluto, promptSha256: sha, controlador: resultado.controlador, model, effort,
628
+ erro: resultado.erro ?? 'runtime.unavailable: sem sessionId', origem: 'retry', ...(perfil ? { perfil } : {}) });
629
+ const sinal = resultado.rateLimit ?? null;
630
+ // I-33 (D5): a conta recusou o redespacho; o perfil sai do rodizio aqui, onde a evidencia esta.
631
+ if (resultado.falhaDeConta) {
632
+ try {
633
+ (0, ratelimit_1.marcarContaDaFalha)(carregado, perfil, resultado.falhaDeConta);
634
+ }
635
+ catch { /* a rotacao segue pelo motivo tipado */ }
636
+ }
637
+ return {
638
+ ok: false,
639
+ slug,
640
+ sessionId: null,
641
+ verificada: false,
642
+ // I-33 (D11b): falha de conta reconhecida na saida e o motivo mais especifico.
643
+ motivo: resultado.falhaDeConta?.motivo ?? (sinal ? 'runtime.rate-limited' : 'runtime.unavailable'),
644
+ detalhe: resultado.erro ?? 'sem sessionId na saida do runtime',
645
+ sinal,
646
+ falhaDeConta: resultado.falhaDeConta ?? null,
647
+ perfil: perfil ?? null,
648
+ comando: resultado.comando,
649
+ controlador: resultado.controlador,
650
+ dryRun: false,
651
+ };
652
+ }
653
+ const ctx = {
654
+ identidade,
655
+ canal,
656
+ ...(conducao ? { conducao } : {}),
657
+ prazoDaSessaoMs: pedidoDeConducao.prazoMs,
658
+ raiz,
659
+ thread,
660
+ fase,
661
+ slug,
662
+ promptPath: absoluto,
663
+ promptSha256: sha,
664
+ runtime,
665
+ cwd,
666
+ model,
667
+ effort,
668
+ origem: opcoes.origem ?? 'retry',
669
+ ...(perfil ? { perfil } : {}),
670
+ ...(headNoDespacho !== undefined ? { headNoDespacho } : {}),
671
+ };
672
+ (0, phase_1.concluirDespacho)(ctx, resultado.sessionId, resultado.verificada, resultado.comando, rt.fonteVerificacao, resultado.controlador);
443
673
  return {
444
674
  ok: true,
445
675
  slug,
446
- sessionId: null,
447
- verificada: false,
676
+ sessionId: resultado.sessionId,
677
+ verificada: resultado.verificada,
448
678
  motivo: null,
449
- detalhe: 'ensaio (--dry-run): comando montado, nada despachado',
679
+ detalhe: `sessao ${resultado.sessionId} despachada com o mesmo prompt sha256 ${sha.slice(0, 12)}` +
680
+ (perfil ? ` no perfil ${perfil.id} (${runtime})` : ''),
450
681
  sinal: null,
451
- comando: resultado.comando,
452
- dryRun: true,
453
- };
454
- }
455
- if (!resultado.ok || !resultado.sessionId) {
456
- const sinal = resultado.rateLimit ?? null;
457
- return {
458
- ok: false,
459
- slug,
460
- sessionId: null,
461
- verificada: false,
462
- motivo: sinal ? 'runtime.rate-limited' : 'runtime.unavailable',
463
- detalhe: resultado.erro ?? 'sem sessionId na saida do runtime',
464
- sinal,
682
+ perfil: perfil ?? null,
465
683
  comando: resultado.comando,
466
684
  dryRun: false,
467
685
  };
468
686
  }
469
- const ctx = {
470
- raiz,
471
- thread,
472
- fase,
473
- slug,
474
- promptPath: absoluto,
475
- promptSha256: sha,
476
- runtime: manifesto.runtime.adapter,
477
- cwd,
478
- model,
479
- effort,
480
- origem: opcoes.origem ?? 'retry',
481
- };
482
- (0, phase_1.concluirDespacho)(ctx, resultado.sessionId, resultado.verificada, resultado.comando);
483
- return {
484
- ok: true,
485
- slug,
486
- sessionId: resultado.sessionId,
487
- verificada: resultado.verificada,
488
- motivo: null,
489
- detalhe: `sessao ${resultado.sessionId} despachada com o mesmo prompt sha256 ${sha.slice(0, 12)}`,
490
- sinal: null,
491
- comando: resultado.comando,
492
- dryRun: false,
493
- };
687
+ finally {
688
+ // Retomada que nao virou sessao devolve a conducao; a que virou ja a entregou a sessao.
689
+ conducao?.liberar();
690
+ }
494
691
  }
495
692
  // ---------------------------------------------------------------------------
496
693
  // Os 3 playbooks de recuperacao, em codigo.
@@ -596,14 +793,25 @@ function retomarPedido(carregado, pedidoOriginal, opcoes = {}) {
596
793
  const slug = escolha.playbook === 'nova-sessao'
597
794
  ? (0, slug_1.proximaRotacao)(pedido.slug, [...thread.sessoes.map((s) => s.slug), pedido.slug])
598
795
  : pedido.slug;
599
- const r = redespachar(carregado, thread, pedido.fase, pedido.promptPath, pedido.promptSha256, {
600
- slug,
601
- model: pedido.model,
602
- effort: pedido.effort,
603
- cwd: pedido.cwd,
604
- origem: 'retry.rate-limit',
605
- dryRun: opcoes.dryRun,
606
- });
796
+ // I-33 (R3, D15/A4): pedido da conta retoma no primeiro runtime, original ou da ordem de fallback,
797
+ // cujo perfil da vez voltou: o prazo que liberou a fila pode ter sido o do fallback. Pedido antigo,
798
+ // sem os campos, segue igual.
799
+ const doPedido = pedido;
800
+ const alvo = alvoDaRetomada(carregado, thread, doPedido);
801
+ const r = !alvo.destino
802
+ ? { ok: false, slug, sessionId: null, verificada: false, motivo: doPedido.motivo ?? 'runtime.quota-exhausted',
803
+ detalhe: `${doPedido.motivo ?? 'runtime.quota-exhausted'}: nenhum perfil da vez disponivel em ${alvo.runtimes.join(', ')}`,
804
+ sinal: null, comando: [], dryRun: opcoes.dryRun === true }
805
+ : redespachar(carregado, thread, pedido.fase, pedido.promptPath, pedido.promptSha256, {
806
+ slug,
807
+ runtime: alvo.runtime,
808
+ perfil: alvo.perfil ?? doPedido.perfil ?? null,
809
+ model: alvo.model,
810
+ effort: alvo.effort,
811
+ cwd: pedido.cwd,
812
+ origem: 'retry.rate-limit',
813
+ dryRun: opcoes.dryRun,
814
+ });
607
815
  (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.retryTentado, {
608
816
  fase: pedido.fase,
609
817
  motivo: 'runtime.rate-limited',
@@ -638,6 +846,8 @@ function retomarPedido(carregado, pedidoOriginal, opcoes = {}) {
638
846
  playbook: escolha.playbook,
639
847
  slug,
640
848
  sessionId: r.sessionId,
849
+ ...(alvo.destino && alvo.runtime !== null ? { runtime: alvo.runtime } : {}),
850
+ ...(r.perfil ? { perfil: r.perfil.id } : {}),
641
851
  promptSha256: pedido.promptSha256,
642
852
  esperouAte: pedido.liberaEm,
643
853
  janelaEstimada: pedido.janelaEstimada,
@@ -666,12 +876,21 @@ function retomarPedido(carregado, pedidoOriginal, opcoes = {}) {
666
876
  }
667
877
  // Falhou de novo. Rate limit outra vez reagenda o MESMO pedido com a hora nova;
668
878
  // qualquer outro motivo devolve o pedido para a fila com a tentativa contada.
669
- const novaJanela = r.sinal?.resetEm ?? pedido.liberaEm;
879
+ // D15 (A4): pedido da conta reagenda no proximo prazo entre TODOS os runtimes candidatos, e o
880
+ // novo prazo e sempre posterior ao instante da retomada: nada volta a vencer na mesma chamada.
881
+ const instante = Math.max(Date.parse(quando), Date.now());
882
+ const prazoDaConta = alvo.contas ? (0, runtime_profiles_1.prazoDaFila)((0, runtime_profiles_1.lerPerfisComContas)(raiz), alvo.runtimes, (0, runtime_profiles_1.politicaDeRotacao)(carregado.manifesto), instante) : null;
883
+ let novaJanela = prazoDaConta ?? r.sinal?.resetEm ?? pedido.liberaEm;
884
+ let estimada = prazoDaConta ? false : r.sinal ? r.sinal.resetEm === null : pedido.janelaEstimada;
885
+ if (alvo.contas && !(Date.parse(novaJanela) > instante)) {
886
+ novaJanela = new Date(instante + (0, ratelimit_1.janelaPadraoMs)(carregado.manifesto)).toISOString();
887
+ estimada = true;
888
+ }
670
889
  pedido = (0, ratelimit_1.gravarPedido)(raiz, {
671
890
  ...pedido,
672
891
  tentativas: pedido.tentativas + 1,
673
892
  liberaEm: novaJanela,
674
- janelaEstimada: r.sinal ? r.sinal.resetEm === null : pedido.janelaEstimada,
893
+ janelaEstimada: estimada,
675
894
  sinal: r.sinal ?? pedido.sinal,
676
895
  detalhe: r.detalhe,
677
896
  });
@@ -687,6 +906,36 @@ function retomarPedido(carregado, pedidoOriginal, opcoes = {}) {
687
906
  dryRun: false,
688
907
  };
689
908
  }
909
+ /**
910
+ * D15 (A4): o destino da retomada. Pedido da conta tenta o runtime original e depois cada runtime da
911
+ * ordem de fallback do bloco, e fica com o primeiro cujo perfil da vez esta disponivel agora, pela
912
+ * politica da D14. Runtime original sem perfil configurado segue pelo ambiente do processo, como
913
+ * antes; pedido antigo (rate limit) segue no runtime gravado.
914
+ */
915
+ function alvoDaRetomada(carregado, thread, pedido) {
916
+ const { raiz, manifesto } = carregado;
917
+ const original = { runtime: pedido.runtime ?? null, model: pedido.model, effort: pedido.effort };
918
+ if (pedido.motivo !== 'runtime.quota-exhausted' && pedido.motivo !== 'runtime.auth-missing')
919
+ return { ...original, destino: true, runtimes: original.runtime ? [original.runtime] : [], contas: false };
920
+ const doBloco = (0, setup_1.configDoBloco)((0, setup_1.lerSetup)(raiz), thread.modo, pedido.fase);
921
+ const principal = pedido.runtime ?? doBloco?.runtime ?? manifesto.runtime.adapter;
922
+ const candidatos = [{ runtime: principal, model: pedido.model, effort: pedido.effort },
923
+ ...alvosDeFallback(doBloco, doBloco?.runtime ?? manifesto.runtime.adapter).filter(f => f.runtime !== principal)
924
+ .map(f => ({ runtime: f.runtime, model: f.model, effort: f.effort ?? pedido.effort }))];
925
+ const runtimes = candidatos.map(c => c.runtime);
926
+ const store = (0, runtime_profiles_1.lerPerfisComContas)(raiz), politica = (0, runtime_profiles_1.politicaDeRotacao)(manifesto);
927
+ for (const c of candidatos) {
928
+ if ((0, runtime_profiles_1.perfisDoRuntime)(store, c.runtime).length === 0) {
929
+ if (c.runtime === principal)
930
+ return { ...c, destino: true, perfil: null, runtimes, contas: true };
931
+ continue;
932
+ }
933
+ const p = (0, runtime_profiles_1.proximoPerfilDisponivel)(store, c.runtime, politica);
934
+ if (p)
935
+ return { ...c, destino: true, perfil: p.id, runtimes, contas: true };
936
+ }
937
+ return { destino: false, runtime: null, model: pedido.model, effort: pedido.effort, runtimes, contas: true };
938
+ }
690
939
  /** Retoma todos os pedidos cuja janela ja liberou. */
691
940
  function retomarFila(carregado, opcoes = {}) {
692
941
  const quando = opcoes.quando ?? (0, util_1.agora)();
@@ -715,12 +964,123 @@ function cancelarPedido(carregado, id, motivo) {
715
964
  });
716
965
  return cancelado;
717
966
  }
967
+ /**
968
+ * D6 (A13): a ordem de fallback do bloco, `runtime:modelo[:esforco]`, pelo parser unico do setup
969
+ * (domicilio da D6): entrada malformada, runtime nao homologado, repetido ou igual ao principal e
970
+ * descartada com as mesmas regras da edicao e da listagem.
971
+ */
972
+ function alvosDeFallback(bloco, principal) {
973
+ return (0, setup_1.alvosDaOrdemDeFallback)(bloco?.fallback, principal);
974
+ }
975
+ /**
976
+ * D5, D14, D16: proximo destino do MESMO prompt. Primeiro o perfil da vez do mesmo runtime (outro
977
+ * perfil so com a troca do manifesto ligada para o motivo: por cota, ligada por padrao desde a
978
+ * decisao do dono em 19/09/2026); depois cada runtime da ordem de fallback (perfil da vez dele, ou o ambiente do
979
+ * processo quando ele nao tem perfil configurado e esse ambiente ainda nao falhou nesta rotacao).
980
+ * Sem destino, `null`.
981
+ */
982
+ function proximoAlvo(raiz, atual, fallback, excluir, politica, agoraMs = Date.now()) {
983
+ const store = (0, runtime_profiles_1.lerPerfisComContas)(raiz, agoraMs);
984
+ const perfis = [...excluir.perfis, ...(atual.perfil ? [atual.perfil.id] : [])];
985
+ const mesmo = (0, runtime_profiles_1.proximoPerfilDisponivel)(store, atual.runtime, politica, { excluir: perfis, agoraMs });
986
+ if (mesmo)
987
+ return { runtime: atual.runtime, perfil: (0, runtime_profiles_1.perfilDeDespacho)(mesmo), model: null, effort: null };
988
+ for (const f of fallback) {
989
+ if (f.runtime === atual.runtime)
990
+ continue;
991
+ if ((0, runtime_profiles_1.perfisDoRuntime)(store, f.runtime).length > 0) {
992
+ const p = (0, runtime_profiles_1.proximoPerfilDisponivel)(store, f.runtime, politica, { excluir: perfis, agoraMs });
993
+ if (p)
994
+ return { runtime: f.runtime, perfil: (0, runtime_profiles_1.perfilDeDespacho)(p), model: f.model, effort: f.effort ?? null };
995
+ continue;
996
+ }
997
+ if (!excluir.runtimesImplicitos.includes(f.runtime))
998
+ return { runtime: f.runtime, perfil: null, model: f.model, effort: f.effort ?? null };
999
+ }
1000
+ return null;
1001
+ }
1002
+ /**
1003
+ * D11 (c): o intervalo do despacho produziu alguma coisa? Leitura de disco, do ledger e do git,
1004
+ * como a prova da I-34: artefato da fase gravado, commit registrado, entrega registrada ou, no
1005
+ * GO, HEAD movido ou worktree alterada. Duvida conta como producao (fail-closed): so o intervalo
1006
+ * provadamente vazio aceita redespacho do mesmo prompt.
1007
+ */
1008
+ function producaoNoIntervalo(raiz, thread, sessao, eventos) {
1009
+ const fonte = (0, session_watcher_claude_1.fonteRegistrada)(eventos, sessao);
1010
+ const saida = (0, session_watcher_claude_1.saidaDoArtefato)(raiz, thread.id, sessao, fonte, eventos);
1011
+ if (saida?.produzido)
1012
+ return { produziu: true, evidencia: `${saida.arquivo} gravado no intervalo do despacho` };
1013
+ const intervalo = (0, session_watcher_claude_1.eventosDoIntervalo)(eventos, sessao);
1014
+ const commits = intervalo.filter(e => e.tipo === 'mcp_git_committed' || (e.tipo === 'commit' && e.sessionId === sessao.sessionId));
1015
+ if (commits.length)
1016
+ return { produziu: true, evidencia: `${commits.length} commit(s) registrado(s) no intervalo do despacho` };
1017
+ const entrega = intervalo.find(e => e.tipo === 'ship_done' || e.tipo === 'score_proposto');
1018
+ if (entrega)
1019
+ return { produziu: true, evidencia: `${entrega.tipo} registrado no intervalo do despacho` };
1020
+ if (sessao.fase === 'GO') {
1021
+ const despacho = eventos.filter(e => e.tipo === 'phase_dispatch' && e.sessionId === sessao.sessionId).at(-1);
1022
+ // I-33 (N1): o HEAD vem do registro do despacho em qualquer runtime (claude-bg e codex); commit feito
1023
+ // pelo shell nao passa pelo ledger, entao so o HEAD o revela.
1024
+ const registro = eventos.filter(e => e.tipo === 'session_sensor_registered' && e.sessionId === sessao.sessionId &&
1025
+ e.despachoEm === sessao.despachadaEm).at(-1);
1026
+ const cwd = typeof registro?.cwd === 'string' ? registro.cwd : typeof despacho?.cwd === 'string' ? despacho.cwd : thread.worktree ?? raiz;
1027
+ const head = registro?.head;
1028
+ if (typeof head !== 'string' || !/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(head))
1029
+ return { produziu: true, evidencia: 'HEAD da worktree no despacho desconhecido; producao nao descartada' };
1030
+ if ((0, session_watcher_claude_1.headDaWorktree)(cwd) !== head)
1031
+ return { produziu: true, evidencia: 'HEAD da worktree mudou desde o despacho' };
1032
+ const status = (0, util_1.exec)('git', ['status', '--porcelain', '--', '.', ':(exclude).orkastery'], cwd, 30000);
1033
+ if (!status.ok)
1034
+ return { produziu: true, evidencia: 'git status da worktree falhou; producao nao descartada' };
1035
+ if (status.stdout.trim() !== '')
1036
+ return { produziu: true, evidencia: 'worktree com alteracoes nao commitadas' };
1037
+ }
1038
+ return { produziu: false, evidencia: 'nenhum artefato, commit, entrega ou alteracao da worktree no intervalo do despacho' };
1039
+ }
1040
+ /** O despacho que falhou pela conta: o da sessao do gate (observador) ou o ultimo recusado na fase. */
1041
+ function despachoDaFalha(eventos, gate, fase) {
1042
+ if (gate && typeof gate.sessionId === 'string')
1043
+ return eventos.filter(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.faseDespachada && e.sessionId === gate.sessionId).at(-1) ?? null;
1044
+ for (let i = eventos.length - 1; i >= 0; i--) {
1045
+ const e = eventos[i];
1046
+ if (e.fase === fase && e.tipo === ledger_1.TIPOS_DE_EVENTO.despachoFalhou)
1047
+ return e;
1048
+ if (e.fase === fase && e.tipo === ledger_1.TIPOS_DE_EVENTO.faseDespachada)
1049
+ return null;
1050
+ }
1051
+ return null;
1052
+ }
1053
+ function perfilDoEvento(e) {
1054
+ return e && e.perfil !== undefined && e.perfil !== null ? (0, runtime_profiles_1.validarPerfilDeDespacho)(e.perfil) : null;
1055
+ }
1056
+ /** O perfil ja esta fora do rodizio por esta falha (cota com prazo futuro, ou sem login)? */
1057
+ function jaMarcado(raiz, perfil, falha, agoraMs = Date.now()) {
1058
+ let atual;
1059
+ try {
1060
+ atual = (0, runtime_profiles_1.lerPerfis)(raiz).perfis.find(p => p.id === perfil.id);
1061
+ }
1062
+ catch {
1063
+ return false;
1064
+ }
1065
+ if (!atual)
1066
+ return false;
1067
+ return falha.motivo === 'runtime.quota-exhausted'
1068
+ ? atual.estado === 'esgotado' && atual.esgotadoAte !== null && Date.parse(atual.esgotadoAte) > agoraMs
1069
+ : atual.estado === 'sem-auth';
1070
+ }
1071
+ function sinalDoGate(gate, motivo) {
1072
+ const f = gate?.falhaDeConta;
1073
+ return { motivo: motivo === 'runtime.auth-missing' ? 'runtime.auth-missing' : 'runtime.quota-exhausted',
1074
+ resetEm: typeof f?.resetEm === 'string' ? f.resetEm : null, fonte: f?.fonte ?? 'sem-horario',
1075
+ trecho: typeof f?.trecho === 'string' ? f.trecho : String(gate?.detalhe ?? motivo).slice(0, 200) };
1076
+ }
718
1077
  /** O ultimo despacho que falhou nesta fase, de onde sai o prompt a redespachar. */
719
- function ultimoDespachoFalho(raiz, threadId, fase) {
1078
+ function ultimoDespachoFalho(raiz, threadId, fase, silencio = false) {
720
1079
  const eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, threadId));
721
1080
  for (let i = eventos.length - 1; i >= 0; i--) {
722
1081
  const e = eventos[i];
723
- if (e.tipo === ledger_1.TIPOS_DE_EVENTO.despachoFalhou && e.fase === fase)
1082
+ if (e.fase === fase && (e.tipo === ledger_1.TIPOS_DE_EVENTO.despachoFalhou ||
1083
+ (silencio && e.tipo === ledger_1.TIPOS_DE_EVENTO.faseDespachada)))
724
1084
  return e;
725
1085
  }
726
1086
  return null;
@@ -733,7 +1093,18 @@ function ultimoDespachoFalho(raiz, threadId, fase) {
733
1093
  */
734
1094
  function executarRetry(carregado, threadId, opcoes = {}) {
735
1095
  const { raiz } = carregado;
736
- const plano = planejarRetry(carregado, threadId, opcoes);
1096
+ let plano = planejarRetry(carregado, threadId, { ...opcoes, estadoDaSessao: undefined });
1097
+ if (plano.motivo === 'runtime.silencio' && plano.fase && plano.tentativas < plano.limite) {
1098
+ const despacho = ultimoDespachoFalho(raiz, threadId, plano.fase, true);
1099
+ // O adapter Codex da baseline nao fornece prova terminal. I-04 adiciona esse contrato.
1100
+ if (despacho?.runtime === 'claude-bg' && typeof despacho.sessionId === 'string') {
1101
+ const consulta = (0, claude_bg_1.consultarSessoes)(undefined, true);
1102
+ const sessao = consulta.ok ? consulta.sessoes.find(s => s.sessionId === despacho.sessionId) : undefined;
1103
+ const estado = sessao?.state ?? sessao?.status;
1104
+ plano = planejarRetry(carregado, threadId, { ...opcoes,
1105
+ estadoDaSessao: typeof estado === 'string' ? { sessionId: despacho.sessionId, estado } : undefined });
1106
+ }
1107
+ }
737
1108
  const thread = (0, thread_1.lerThread)(raiz, threadId);
738
1109
  const dir = (0, thread_1.dirThread)(raiz, threadId);
739
1110
  const vazio = {
@@ -798,12 +1169,23 @@ function executarRetry(carregado, threadId, opcoes = {}) {
798
1169
  effortAnterior: plano.effortAnterior,
799
1170
  dryRun: opcoes.dryRun === true,
800
1171
  autorizadoPor: blocoPausa(thread, plano.fase)
801
- ? (opcoes.autorizadoPor ?? 'humano')
1172
+ ? ((0, gates_2.aprovacoesHumanas)(raiz, threadId).some(e => e.fase === plano.fase && e.autorizadoPor === opcoes.autorizadoPor)
1173
+ ? opcoes.autorizadoPor : `delegacao:${carregado.manifesto.conduction.delegation?.delegado ?? 'ausente'}`)
802
1174
  : `politica de retry do bloco B3 (bloco sem pausa no modo ${thread.modo})`,
803
1175
  ok,
804
1176
  detalhe,
805
1177
  });
806
1178
  };
1179
+ // I-36 (T6): conducao em andamento nao e fila de rate limit. Nada e redespachado; o registro diz
1180
+ // quem conduz, e a vez chega quando essa conducao terminar.
1181
+ if (plano.motivo === 'conducao.em-andamento') {
1182
+ const atual = (0, conducao_1.conducaoDaThread)(raiz, threadId);
1183
+ const detalhe = atual
1184
+ ? `${(0, conducao_texto_1.linhaDeConducao)(atual)}; nada foi redespachado, a vez chega quando essa conducao terminar`
1185
+ : 'ninguem conduz a thread agora: o proximo pedido segue normalmente';
1186
+ registrarTentativa(!atual, detalhe);
1187
+ return { ...vazio, executada: false, detalhe };
1188
+ }
807
1189
  // `esperar-janela`: quem retoma e a fila, e ela ja esta gravada em disco.
808
1190
  if (acaoBase === 'esperar-janela') {
809
1191
  const fila = (0, ratelimit_1.lerFilaDeRetomada)(raiz).filter((p) => p.thread === threadId && p.estado === 'aguardando');
@@ -833,8 +1215,11 @@ function executarRetry(carregado, threadId, opcoes = {}) {
833
1215
  registrarTentativa(rodada.correcoes.length > 0, detalhe);
834
1216
  return { ...vazio, executada: rodada.correcoes.length > 0, rodada, reverify, detalhe };
835
1217
  }
1218
+ if (plano.motivo === 'runtime.quota-exhausted' || plano.motivo === 'runtime.auth-missing') {
1219
+ return rotacionarConta(carregado, thread, plano, opcoes, vazio, registrarTentativa);
1220
+ }
836
1221
  // `reexecutar` e `escalar-esforco`: o MESMO prompt volta ao runtime.
837
- const falho = ultimoDespachoFalho(raiz, threadId, plano.fase);
1222
+ const falho = ultimoDespachoFalho(raiz, threadId, plano.fase, plano.motivo === 'runtime.silencio');
838
1223
  if (!falho || typeof falho.promptPath !== 'string' || typeof falho.promptSha256 !== 'string') {
839
1224
  const detalhe = `nao ha despacho falho registrado em ${plano.fase} com prompt gravado: ` +
840
1225
  `rode a fase de novo com \`ork phase run ${threadId} ${plano.fase} --prompt "..."\``;
@@ -843,6 +1228,7 @@ function executarRetry(carregado, threadId, opcoes = {}) {
843
1228
  }
844
1229
  const redespacho = redespachar(carregado, thread, plano.fase, falho.promptPath, falho.promptSha256, {
845
1230
  effort: plano.effort,
1231
+ runtime: typeof falho.runtime === 'string' ? falho.runtime : null,
846
1232
  model: typeof falho.model === 'string' ? falho.model : null,
847
1233
  origem: 'retry.run',
848
1234
  dryRun: opcoes.dryRun,
@@ -855,6 +1241,134 @@ function executarRetry(carregado, threadId, opcoes = {}) {
855
1241
  detalhe: redespacho.detalhe,
856
1242
  };
857
1243
  }
1244
+ /**
1245
+ * D5 + D11: rotacao depois de falha da conta. Com sessao no gate (o observador classificou a
1246
+ * morte), a prova do `ork` decide antes: producao parcial vira `human.pending` com diagnostico.
1247
+ * Sem producao, o perfil sai do rodizio e o MESMO prompt segue no proximo destino; recusa da
1248
+ * conta no redespacho (nada rodou) continua a rotacao na mesma chamada, dentro do limite de
1249
+ * tentativas. Sem destino: fila ate o menor prazo, ou o humano quando nenhum prazo existe.
1250
+ */
1251
+ function rotacionarConta(carregado, thread, plano, opcoes, vazio, registrarTentativa) {
1252
+ const { raiz, manifesto } = carregado;
1253
+ const dir = (0, thread_1.dirThread)(raiz, thread.id);
1254
+ const fase = plano.fase;
1255
+ const motivo = plano.motivo;
1256
+ const eventos = (0, ledger_1.lerLedger)(dir);
1257
+ const gate = ultimaReprovacao(raiz, thread.id);
1258
+ const despacho = despachoDaFalha(eventos, gate, fase);
1259
+ const escalar = (razao, diagnostico) => {
1260
+ if (!opcoes.dryRun) {
1261
+ (0, gates_1.registrarGateBloqueado)(dir, thread.id, { gate: 'phase.dispatch', motivo: 'human.pending', modo: thread.modo, detalhe: diagnostico,
1262
+ diagnostico, correcao: `ork gate approve ${thread.id} retomada --por <quem>`, fase, origem: 'retry.rotacao', pausaQualquerModo: true });
1263
+ (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.retryEscalado, { fase, motivo, origem: 'retry.rotacao', acao: plano.acao,
1264
+ tentativas: plano.tentativas, limite: plano.limite, modo: thread.modo, razao });
1265
+ }
1266
+ return { ...vazio, detalhe: diagnostico };
1267
+ };
1268
+ if (!despacho || typeof despacho.promptPath !== 'string' || typeof despacho.promptSha256 !== 'string') {
1269
+ const detalhe = `nao ha despacho com prompt gravado para ${motivo} em ${fase}: rode a fase de novo com ork phase run`;
1270
+ registrarTentativa(false, detalhe);
1271
+ return { ...vazio, detalhe };
1272
+ }
1273
+ const runtime = typeof despacho.runtime === 'string' ? despacho.runtime : manifesto.runtime.adapter;
1274
+ let perfil;
1275
+ try {
1276
+ perfil = typeof gate?.sessionId === 'string'
1277
+ ? (0, runtime_profiles_1.perfilDoRegistro)(eventos, { sessionId: gate.sessionId, despachadaEm: typeof gate.despachoEm === 'string' ? gate.despachoEm : undefined })
1278
+ : perfilDoEvento(despacho);
1279
+ }
1280
+ catch (e) {
1281
+ return escalar('perfil registrado invalido', `rotacao recusada: ${e.message}`);
1282
+ }
1283
+ // D11 (c): sessao que morreu por cota depois de produzir nao e redespachada.
1284
+ if (typeof gate?.sessionId === 'string' && typeof gate.despachoEm === 'string') {
1285
+ const producao = producaoNoIntervalo(raiz, thread, { sessionId: gate.sessionId, fase, despachadaEm: gate.despachoEm }, eventos);
1286
+ if (producao.produziu) {
1287
+ return escalar('producao parcial sob falha da conta', `producao parcial sob ${motivo === 'runtime.quota-exhausted' ? 'cota esgotada' : 'auth ausente'}: ${producao.evidencia}; ` +
1288
+ 'a sessao nao e redespachada sobre a worktree alterada e a fase nao conclui sem o humano');
1289
+ }
1290
+ }
1291
+ const doBloco = (0, setup_1.configDoBloco)((0, setup_1.lerSetup)(raiz), thread.modo, fase);
1292
+ const fallback = alvosDeFallback(doBloco, doBloco?.runtime ?? manifesto.runtime.adapter);
1293
+ const politica = (0, runtime_profiles_1.politicaDeRotacao)(manifesto);
1294
+ const falha = sinalDoGate(gate, motivo);
1295
+ // I-33 (D12): o observador ja marca o perfil no caminho terminal; remarcar empurraria o prazo a cada retry.
1296
+ if (!opcoes.dryRun && perfil && !jaMarcado(raiz, perfil, falha)) {
1297
+ try {
1298
+ (0, ratelimit_1.marcarContaDaFalha)(carregado, perfil, falha);
1299
+ }
1300
+ catch { /* o store pode ja estar marcado */ }
1301
+ }
1302
+ const tentados = [];
1303
+ const implicitos = perfil ? [] : [runtime];
1304
+ let atual = { runtime, perfil };
1305
+ let tentativas = plano.tentativas;
1306
+ let ultimo = null;
1307
+ let falhaAtual = falha;
1308
+ const emitir = (para, razao) => {
1309
+ if (!opcoes.dryRun)
1310
+ (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.perfilRotacionado, { fase, motivo: falhaAtual.motivo, origem: 'retry.rotacao',
1311
+ de: { runtime: atual.runtime, perfil: atual.perfil?.id ?? null },
1312
+ para: para ? { runtime: para.runtime, perfil: para.perfil?.id ?? null } : null,
1313
+ evidencia: falhaAtual.trecho, razao, promptSha256: despacho.promptSha256, modo: thread.modo,
1314
+ autorizadoPor: `politica de retry da I-33 (D5), bloco sem pausa ou delegado no modo ${thread.modo}` });
1315
+ };
1316
+ for (;;) {
1317
+ const alvo = proximoAlvo(raiz, atual, fallback, { perfis: tentados, runtimesImplicitos: implicitos }, politica);
1318
+ if (!alvo)
1319
+ break;
1320
+ emitir(alvo, alvo.runtime === atual.runtime ? 'proximo perfil ativo do mesmo runtime' : 'fallback de runtime pela ordem do bloco');
1321
+ const mudouRuntime = alvo.runtime !== runtime;
1322
+ ultimo = redespachar(carregado, thread, fase, despacho.promptPath, despacho.promptSha256, {
1323
+ runtime: alvo.runtime, perfil: alvo.perfil?.id ?? null,
1324
+ model: mudouRuntime ? alvo.model : typeof despacho.model === 'string' ? despacho.model : null,
1325
+ effort: mudouRuntime ? alvo.effort ?? (typeof despacho.effort === 'string' ? despacho.effort : null) : plano.effort,
1326
+ origem: 'retry.rotacao', dryRun: opcoes.dryRun,
1327
+ });
1328
+ registrarTentativa(ultimo.ok, ultimo.detalhe);
1329
+ tentativas += 1;
1330
+ if (ultimo.ok || opcoes.dryRun)
1331
+ return { ...vazio, executada: ultimo.ok, redespacho: ultimo, detalhe: ultimo.detalhe };
1332
+ // So a recusa da CONTA no redespacho (nada rodou) continua a rotacao; outra falha para aqui.
1333
+ if (!ultimo.falhaDeConta && ultimo.motivo !== 'runtime.auth-missing' && ultimo.motivo !== 'runtime.quota-exhausted')
1334
+ return { ...vazio, redespacho: ultimo, detalhe: ultimo.detalhe };
1335
+ if (alvo.perfil)
1336
+ tentados.push(alvo.perfil.id);
1337
+ else
1338
+ implicitos.push(alvo.runtime);
1339
+ atual = { runtime: alvo.runtime, perfil: alvo.perfil };
1340
+ falhaAtual = ultimo.falhaDeConta ?? falhaAtual;
1341
+ if (tentativas >= plano.limite) {
1342
+ return escalar('limite de tentativas na rotacao', `${tentativas} tentativa(s) de rotacao por ${motivo} em ${fase}, no limite de ` +
1343
+ `${plano.limite} do manifesto: escalacao tipada pausa qualquer modo`);
1344
+ }
1345
+ }
1346
+ // Sem destino: a fila so faz sentido quando algum perfil tem prazo para voltar. Sem perfil (ambiente do
1347
+ // processo), o prazo e o horario dito pelo proprio runtime, quando ainda esta no futuro.
1348
+ const runtimes = [runtime, ...fallback.map(f => f.runtime)];
1349
+ const dito = !perfil && falhaAtual.resetEm !== null && Date.parse(falhaAtual.resetEm) > Date.now() ? falhaAtual.resetEm : null;
1350
+ const prazo = (0, runtime_profiles_1.prazoDaFila)((0, runtime_profiles_1.lerPerfisComContas)(raiz), runtimes, politica) ?? dito;
1351
+ if (!prazo && falhaAtual.motivo === 'runtime.auth-missing') {
1352
+ emitir(null, 'nenhum perfil com login em nenhum runtime da ordem de fallback');
1353
+ return escalar('auth ausente sem alternativa', 'nenhum perfil com login conferido em nenhum runtime da ordem de fallback: ' +
1354
+ 'refaca o login com ork accounts add ou ork accounts check antes de retomar');
1355
+ }
1356
+ const liberaEm = prazo ?? new Date(Date.now() + (0, ratelimit_1.janelaPadraoMs)(manifesto)).toISOString();
1357
+ emitir(null, `nenhum perfil ativo em ${runtimes.join(', ')}: fila duravel ate ${liberaEm}`);
1358
+ if (opcoes.dryRun)
1359
+ return { ...vazio, detalhe: `ensaio: entraria na fila duravel ate ${liberaEm}` };
1360
+ const pedido = (0, ratelimit_1.enfileirar)(carregado, { thread: thread.id, fase, slug: typeof despacho.slug === 'string' ? despacho.slug : (0, phase_1.slugDaSessao)(thread, fase),
1361
+ promptPath: despacho.promptPath, promptSha256: despacho.promptSha256,
1362
+ cwd: typeof despacho.cwd === 'string' ? despacho.cwd : thread.worktree ?? raiz,
1363
+ model: typeof despacho.model === 'string' ? despacho.model : null, effort: typeof despacho.effort === 'string' ? despacho.effort : null,
1364
+ sinal: { resetEm: prazo, fonte: prazo ? 'iso' : 'sem-horario', trecho: falhaAtual.trecho },
1365
+ detalhe: `${falhaAtual.motivo}: nenhum perfil ativo em ${runtimes.join(', ')}`, runtime, perfil: null, motivo: falhaAtual.motivo });
1366
+ (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.rateLimitEnfileirado, { fase, slug: pedido.slug, pedido: pedido.id, liberaEm: pedido.liberaEm,
1367
+ janelaEstimada: pedido.janelaEstimada, motivo: falhaAtual.motivo, promptSha256: despacho.promptSha256,
1368
+ correcao: `ork retry resume (ou aguarde ate ${pedido.liberaEm})` });
1369
+ registrarTentativa(true, `fila duravel: pedido ${pedido.id} libera em ${pedido.liberaEm}`);
1370
+ return { ...vazio, executada: true, fila: [pedido], redespacho: ultimo, detalhe: `pedido ${pedido.id} na fila ate ${pedido.liberaEm}` };
1371
+ }
858
1372
  /** Texto de `ork retry plan`. */
859
1373
  function textoDoPlano(p) {
860
1374
  const linhas = [];
@@ -863,7 +1377,7 @@ function textoDoPlano(p) {
863
1377
  linhas.push(` fase ${p.fase ?? '(nenhuma)'}`);
864
1378
  linhas.push(` motivo tipado ${p.motivo ?? '(nenhuma reprovacao pendente)'}`);
865
1379
  if (p.detalhe)
866
- linhas.push(` detalhe ${p.detalhe}`);
1380
+ linhas.push(` detalhe ${(0, horario_1.localizarTexto)(p.detalhe)}`);
867
1381
  linhas.push(` acao ${p.acao}`);
868
1382
  linhas.push(` tentativas ${p.tentativas} de ${p.limite} antes da escalacao`);
869
1383
  linhas.push(` esforco ${p.effort}${p.effort !== p.effortAnterior ? ` (escalado de ${p.effortAnterior})` : ''}`);
@@ -873,13 +1387,16 @@ function textoDoPlano(p) {
873
1387
  linhas.push(` razao ${p.razao}`);
874
1388
  if (p.politica && !p.automatica)
875
1389
  linhas.push(` correcao ${p.politica.correcao}`);
1390
+ if (p.detalhe && (0, horario_1.localizarTexto)(p.detalhe) !== p.detalhe)
1391
+ linhas.push((0, horario_1.legendaDoFuso)());
876
1392
  return linhas.join('\n');
877
1393
  }
878
1394
  /** Texto de `ork retry run`. */
879
1395
  function textoDoRetry(r) {
880
- const linhas = [textoDoPlano(r.plano), ''];
1396
+ const plano = textoDoPlano(r.plano);
1397
+ const linhas = [plano, ''];
881
1398
  linhas.push(`Execucao: ${r.executada ? 'acao executada' : 'nada executado'}`);
882
- linhas.push(` ${r.detalhe}`);
1399
+ linhas.push(` ${(0, horario_1.localizarTexto)(r.detalhe)}`);
883
1400
  if (r.rodada && r.rodada.correcoes.length > 0) {
884
1401
  linhas.push('');
885
1402
  linhas.push(`GO-FIX rodada ${r.rodada.rodada}: ` +
@@ -892,9 +1409,12 @@ function textoDoRetry(r) {
892
1409
  linhas.push('');
893
1410
  linhas.push('Fila de rate limit desta thread:');
894
1411
  for (const p of r.fila) {
895
- linhas.push(` ${p.id} ${p.fase} libera em ${p.liberaEm}${p.janelaEstimada ? ' (janela estimada)' : ''}`);
1412
+ linhas.push(` ${p.id} ${p.fase} libera em ${(0, horario_1.formatarDataHora)(p.liberaEm)}${p.janelaEstimada ? ' (janela estimada)' : ''}`);
896
1413
  }
897
1414
  }
1415
+ if ((r.fila.length > 0 || (0, horario_1.localizarTexto)(r.detalhe) !== r.detalhe) && !plano.includes((0, horario_1.legendaDoFuso)())) {
1416
+ linhas.push((0, horario_1.legendaDoFuso)());
1417
+ }
898
1418
  return linhas.join('\n');
899
1419
  }
900
1420
  /** Texto de `ork retry resume`. */
@@ -910,8 +1430,10 @@ function textoDaRetomada(rs) {
910
1430
  linhas.push(` retomada ${r.despachada ? `sim (sessao ${r.sessionId})` : 'nao'}`);
911
1431
  if (r.motivo)
912
1432
  linhas.push(` motivo ${r.motivo}`);
913
- linhas.push(` detalhe ${r.detalhe}`);
1433
+ linhas.push(` detalhe ${(0, horario_1.localizarTexto)(r.detalhe)}`);
914
1434
  linhas.push(` estado ${r.pedido.estado}`);
915
1435
  }
1436
+ if (rs.some((r) => (0, horario_1.localizarTexto)(r.detalhe) !== r.detalhe))
1437
+ linhas.push((0, horario_1.legendaDoFuso)());
916
1438
  return linhas.join('\n');
917
1439
  }