@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/phase.js CHANGED
@@ -1,12 +1,4 @@
1
1
  "use strict";
2
- /**
3
- * `ork phase run|list`: despacho de fase pelo runtime adapter e leitura do ledger.
4
- *
5
- * O prompt e montado pelo `ork` (nomenclatura das fases + modo de conducao + contexto da
6
- * thread + pedido do builder), gravado com hash sha256 no diretorio da thread e so entao
7
- * despachado. Depois do despacho o `ork` RE-VERIFICA no runtime que a sessao existe:
8
- * self-report de despacho nao vale como evidencia.
9
- */
10
2
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
11
3
  if (k2 === undefined) k2 = k;
12
4
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -42,6 +34,11 @@ var __importStar = (this && this.__importStar) || (function () {
42
34
  })();
43
35
  Object.defineProperty(exports, "__esModule", { value: true });
44
36
  exports.REGRAS_DE_EVIDENCIA = exports.CONTRATO_DAS_FASES = void 0;
37
+ exports.escolherPerfil = escolherPerfil;
38
+ exports.perfilParaDespacho = perfilParaDespacho;
39
+ exports.runtimeCruzadoParaDespacho = runtimeCruzadoParaDespacho;
40
+ exports.estadoParaDespacho = estadoParaDespacho;
41
+ exports.fasesDoCicloNoPrompt = fasesDoCicloNoPrompt;
45
42
  exports.valoresDoPrompt = valoresDoPrompt;
46
43
  exports.montarPrompt = montarPrompt;
47
44
  exports.montarPromptComMemoria = montarPromptComMemoria;
@@ -50,16 +47,33 @@ exports.slugDaSessao = slugDaSessao;
50
47
  exports.resolverDespacho = resolverDespacho;
51
48
  exports.dadosDoDespacho = dadosDoDespacho;
52
49
  exports.concluirDespacho = concluirDespacho;
50
+ exports.prazoDaSessao = prazoDaSessao;
53
51
  exports.rodarFase = rodarFase;
54
52
  exports.tabelaDoLedger = tabelaDoLedger;
55
53
  exports.descreverSlug = descreverSlug;
54
+ const hitl_gates_1 = require("./hitl-gates");
55
+ const runtime_context_1 = require("./runtime-context");
56
+ /**
57
+ * `ork phase run|list`: despacho de fase pelo runtime adapter e leitura do ledger.
58
+ *
59
+ * O prompt e montado pelo `ork` (nomenclatura das fases + modo de conducao + contexto da
60
+ * thread + pedido do builder), gravado com hash sha256 no diretorio da thread e so entao
61
+ * despachado. Depois do despacho o `ork` RE-VERIFICA no runtime que a sessao existe:
62
+ * self-report de despacho nao vale como evidencia.
63
+ */
56
64
  const node_crypto_1 = require("node:crypto");
65
+ const fs = __importStar(require("node:fs"));
57
66
  const path = __importStar(require("node:path"));
58
- const adapter = __importStar(require("./adapters/claude-bg"));
67
+ const session_watcher_1 = require("./session-watcher");
68
+ const session_watcher_claude_1 = require("./session-watcher-claude");
69
+ const runtimes_1 = require("./runtimes");
70
+ const setup_1 = require("./setup");
59
71
  const modos_1 = require("./modos");
60
72
  const gates_1 = require("./gates");
61
73
  const policies_1 = require("./policies");
62
74
  const ratelimit_1 = require("./ratelimit");
75
+ const claude_bg_1 = require("./adapters/claude-bg");
76
+ const codex_1 = require("./adapters/codex");
63
77
  const memoria_1 = require("./memoria");
64
78
  const prompts_1 = require("./prompts");
65
79
  const slug_1 = require("./slug");
@@ -67,6 +81,145 @@ const thread_1 = require("./thread");
67
81
  const types_1 = require("./types");
68
82
  const util_1 = require("./util");
69
83
  const ledger_1 = require("./ledger");
84
+ const estado_thread_1 = require("./estado-thread");
85
+ const horario_1 = require("./horario");
86
+ const runtime_profiles_1 = require("./runtime-profiles");
87
+ const maquina_1 = require("./maquina");
88
+ const fabrica_publicar_1 = require("./fabrica-publicar");
89
+ const conducao_1 = require("./conducao");
90
+ /**
91
+ * I-33 (D2, D4, D14): o perfil do despacho. Sem perfis do runtime no store, nenhum (P8). Com
92
+ * perfis, o pedido explicito ou o perfil da vez na ordem do store pela politica de rotacao do
93
+ * manifesto (sem ela, a padrao da D16); perfil esgotado, sem auth, pago ou desativado nunca
94
+ * recebe despacho.
95
+ */
96
+ function escolherPerfil(raiz, runtime, opcoes = {}) {
97
+ // I-49: o despacho enxerga o estado que as outras fabricas viram na mesma conta.
98
+ const store = (0, runtime_profiles_1.lerPerfisComContas)(raiz, opcoes.agoraMs);
99
+ const doRuntime = (0, runtime_profiles_1.perfisDoRuntime)(store, runtime);
100
+ if (opcoes.perfil !== undefined) {
101
+ const pedido = doRuntime.find(p => p.id === opcoes.perfil);
102
+ if (!pedido)
103
+ return { perfil: null, configurados: doRuntime.length, erro: `perfil "${opcoes.perfil}" nao existe para o runtime ${runtime}` };
104
+ if (!(0, runtime_profiles_1.perfilDisponivel)(pedido, opcoes.agoraMs))
105
+ return { perfil: null, configurados: doRuntime.length,
106
+ erro: `perfil "${pedido.id}" esta ${pedido.estado}${pedido.esgotadoAte ? ` ate ${pedido.esgotadoAte}` : ''}` };
107
+ return { perfil: (0, runtime_profiles_1.perfilDeDespacho)(pedido), configurados: doRuntime.length };
108
+ }
109
+ if (doRuntime.length === 0)
110
+ return { perfil: null, configurados: 0 };
111
+ const proximo = (0, runtime_profiles_1.proximoPerfilDisponivel)(store, runtime, opcoes.politica ?? runtime_profiles_1.POLITICA_PADRAO, opcoes);
112
+ if (proximo)
113
+ return { perfil: (0, runtime_profiles_1.perfilDeDespacho)(proximo), configurados: doRuntime.length };
114
+ const agoraMs = opcoes.agoraMs ?? Date.now();
115
+ // Motivo do bloqueio pelo estado dos perfis: com algum esgotado ha prazo (fila); so sem-auth, login;
116
+ // so provider pago (D13), custo: `cost.violation` nunca recebe retry automatico.
117
+ const motivo = doRuntime.some(p => p.estado === 'esgotado' && p.esgotadoAte !== null && Date.parse(p.esgotadoAte) > agoraMs)
118
+ ? 'runtime.quota-exhausted' : doRuntime.some(p => p.estado === 'sem-auth') ? 'runtime.auth-missing'
119
+ : doRuntime.some(p => p.estado === 'provider-pago') ? 'cost.violation' : 'runtime.unavailable';
120
+ return { perfil: null, configurados: doRuntime.length, motivo,
121
+ erro: `nenhum perfil disponivel do runtime ${runtime} (${doRuntime.map(p => `${p.id}: ${p.estado}`).join(', ')})` };
122
+ }
123
+ /**
124
+ * I-33 (D7, D13): preflight de auth antes do despacho. O perfil escolhido tem o login conferido
125
+ * pelo proprio CLI com o env dele; reprovado, vira `sem-auth` no store (ou `provider-pago`, quando
126
+ * o login e por API key, helper, Console ou nuvem), a troca vai ao ledger (`runtime_profile_rotated`)
127
+ * e o proximo perfil do mesmo runtime e conferido. Perfil sem login de assinatura nunca recebe
128
+ * despacho. Sem perfil (ambiente do processo) ou em ensaio, nada e conferido.
129
+ */
130
+ function perfilParaDespacho(carregado, thread, fase, runtime, opcoes) {
131
+ const { raiz } = carregado;
132
+ const excluir = [...(opcoes.excluir ?? [])];
133
+ const politica = (0, runtime_profiles_1.politicaDeRotacao)(carregado.manifesto);
134
+ for (;;) {
135
+ const escolha = escolherPerfil(raiz, runtime, { perfil: opcoes.perfil, excluir, politica });
136
+ if (escolha.erro || !escolha.perfil || opcoes.dryRun)
137
+ return escolha;
138
+ const auth = runtime === 'claude-bg' ? (0, claude_bg_1.conferirAuth)(escolha.perfil) : runtime === 'codex' ? (0, codex_1.conferirAuth)(escolha.perfil)
139
+ : { ok: false, detalhe: `runtime ${runtime} sem conferencia de login` };
140
+ if (auth.ok)
141
+ return escolha;
142
+ let motivo, razao;
143
+ if (auth.transitorio) {
144
+ // A14: conferencia inconclusiva nao marca sem-auth: o perfil fica fora so deste despacho.
145
+ (0, runtime_profiles_1.registrarConferenciaInconclusiva)(raiz, escolha.perfil.id, auth.detalhe);
146
+ motivo = 'runtime.unavailable';
147
+ razao = 'a conferencia de login nao concluiu (A14): o perfil fica fora so deste despacho e e conferido de novo no proximo';
148
+ }
149
+ else if (auth.pago) {
150
+ // D13: login valido, mas cobrado por token. O perfil sai do rodizio com estado proprio e nunca despacha.
151
+ (0, runtime_profiles_1.marcarFalhaDePerfil)(raiz, escolha.perfil.id, { estado: 'provider-pago', esgotadoAte: null, motivo: 'cost.violation', detalhe: auth.detalhe });
152
+ motivo = 'cost.violation';
153
+ razao = 'o preflight achou login por provider pago (D13): perfil de API key, helper, Console ou nuvem nunca recebe despacho';
154
+ }
155
+ else {
156
+ const falha = { motivo: 'runtime.auth-missing', resetEm: null, fonte: 'sem-horario', trecho: auth.detalhe };
157
+ (0, ratelimit_1.marcarContaDaFalha)(carregado, escolha.perfil, falha);
158
+ motivo = falha.motivo;
159
+ razao = 'o preflight de auth reprovou o perfil antes do despacho (D7): perfil sem login nunca recebe despacho';
160
+ }
161
+ excluir.push(escolha.perfil.id);
162
+ const proximo = opcoes.perfil === undefined ? escolherPerfil(raiz, runtime, { excluir, politica }).perfil : null;
163
+ (0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, thread.id), thread.id, ledger_1.TIPOS_DE_EVENTO.perfilRotacionado, {
164
+ fase, motivo, origem: `${opcoes.origem}.preflight`,
165
+ de: { runtime, perfil: escolha.perfil.id }, para: proximo ? { runtime, perfil: proximo.id } : null,
166
+ evidencia: auth.detalhe, razao,
167
+ autorizadoPor: 'politica de rotacao da I-33 (D5, D7, D13), igual em todos os modos',
168
+ });
169
+ if (opcoes.perfil !== undefined)
170
+ return { perfil: null, configurados: escolha.configurados, motivo,
171
+ erro: `perfil "${opcoes.perfil}" ${auth.transitorio ? 'com conferencia de login inconclusiva' : auth.pago ? 'autenticado por provider pago' : 'sem login conferido'}: ${auth.detalhe}` };
172
+ }
173
+ }
174
+ /**
175
+ * O CHECK desta thread pode rodar neste runtime? (I-43, D4, viga a)
176
+ *
177
+ * A regra: quem VALIDA nao pode ser quem EXECUTOU. Ela vinha do Objective Envelope,
178
+ * onde valia so na criacao e para tres threads que nunca rodaram nada; aqui ela passa
179
+ * a valer no despacho de qualquer thread que a exija.
180
+ *
181
+ * Compara o runtime do CHECK com os runtimes das sessoes de GO JA REGISTRADAS em
182
+ * `thread.sessoes`, que ja carregam `runtime` e ja aparecem em `ork thread status`.
183
+ * Nenhum dado novo precisa ser coletado: o que faltava era a regra, nao o dado.
184
+ *
185
+ * Sem sessao de GO registrada nao ha o que cruzar, e o despacho passa: inventar
186
+ * impedimento sobre um GO que nao aconteceu seria barrar por suposicao.
187
+ */
188
+ function runtimeCruzadoParaDespacho(thread, fase, runtime) {
189
+ if (thread.exigeRuntimeDiferente !== true || fase !== 'CHECK')
190
+ return null;
191
+ const doGo = [...new Set(thread.sessoes.filter((s) => s.fase === 'GO').map((s) => s.runtime))];
192
+ if (doGo.length === 0)
193
+ return null;
194
+ if (!doGo.includes(runtime))
195
+ return null;
196
+ return `runtime.autoconferencia: o CHECK precisa de runtime diferente do GO, e "${runtime}" `
197
+ + `foi o runtime do GO desta thread (${doGo.join(', ')}). `
198
+ + `Despache o CHECK em outro runtime, ou tire a exigencia da thread.`;
199
+ }
200
+ /**
201
+ * I-43 (T11): o ramo do Objective Envelope saiu daqui.
202
+ *
203
+ * Ele era a UNICA consulta do produto ao envelope, e dependia de `thread.creationOrigin`.
204
+ * A medida: das 137 threads do projeto, ZERO tinham `creationOrigin`. O ramo nunca
205
+ * disparou para thread nenhuma, e a "unica pausa planejada do #Maestro" nao bloqueava
206
+ * o despacho nem das tres threads que o proprio envelope criou.
207
+ *
208
+ * O que ele PRETENDIA garantir (que ninguem despacha sem validacao combinada) continua
209
+ * garantido, e agora de verdade, por `runtimeCruzadoParaDespacho` logo acima e pelos
210
+ * criterios de pronto executaveis que `ork verify` cobra.
211
+ */
212
+ function estadoParaDespacho(raiz, thread, dryRun = false) {
213
+ if (!thread.worktree)
214
+ return null;
215
+ try {
216
+ (0, estado_thread_1.vincularEstado)(raiz, thread.id, thread.worktree, dryRun);
217
+ return null;
218
+ }
219
+ catch (e) {
220
+ return e.message;
221
+ }
222
+ }
70
223
  /** O que cada fase canonica entrega. Vai no cabecalho do prompt de toda sessao. */
71
224
  exports.CONTRATO_DAS_FASES = {
72
225
  GOAL: 'GOAL (F1): objetivo verificavel, impact map, criterios de sucesso e claims com comando de verificacao. Nao implementa.',
@@ -82,6 +235,21 @@ exports.REGRAS_DE_EVIDENCIA = [
82
235
  'Trabalhe apenas no diretorio de trabalho indicado acima.',
83
236
  'Passos irreversiveis (push, merge, delecao) so acontecem quando o modo autoriza.',
84
237
  ];
238
+ /**
239
+ * I-42 (D11): as fases que o cabecalho do prompt descreve.
240
+ *
241
+ * Regra por desenho de bloco, nunca por nome de modo: quando o ciclo e um bloco so e esse bloco
242
+ * nao percorre as seis fases, o prompt descreve so as fases dele. As outras cinco linhas seriam
243
+ * cerimonia de um ciclo que a sessao nao vai percorrer. Bloco unico com as seis fases e ciclo de
244
+ * varios blocos continuam com as seis linhas, byte a byte.
245
+ */
246
+ function fasesDoCicloNoPrompt(thread) {
247
+ const [unico, ...resto] = thread.blocos;
248
+ if (unico && resto.length === 0 && unico.fases.length < types_1.FASES.length) {
249
+ return types_1.FASES.filter((f) => unico.fases.includes(f));
250
+ }
251
+ return types_1.FASES;
252
+ }
85
253
  /**
86
254
  * Os valores que o template de fase recebe.
87
255
  *
@@ -98,7 +266,7 @@ function valoresDoPrompt(thread, fase, pedido, memoriaInjetada = '') {
98
266
  thread: thread.id,
99
267
  bloco: bloco.fases.join('-'),
100
268
  nome: thread.nome,
101
- ciclo_canonico: types_1.FASES.map((f) => `${bloco.fases.includes(f) ? '>' : ' '} ${exports.CONTRATO_DAS_FASES[f]}`).join('\n'),
269
+ ciclo_canonico: fasesDoCicloNoPrompt(thread).map((f) => `${bloco.fases.includes(f) ? '>' : ' '} ${exports.CONTRATO_DAS_FASES[f]}`).join('\n'),
102
270
  tag: def.tag,
103
271
  blocos: thread.blocos.map((b) => b.fases.join('-')).join(' / '),
104
272
  pausas: String(thread.blocos.filter((b) => b.pausa).length),
@@ -150,6 +318,16 @@ function montarPromptComMemoria(carregado, thread, fase, pedido, memoria) {
150
318
  function hashDoPrompt(prompt) {
151
319
  return (0, node_crypto_1.createHash)('sha256').update(prompt, 'utf8').digest('hex');
152
320
  }
321
+ function schemaDeClaimsDoPacote() {
322
+ const candidatos = [
323
+ path.join(__dirname, '..', 'schemas', 'claims.schema.json'),
324
+ path.join(__dirname, '..', '..', 'schemas', 'claims.schema.json'),
325
+ ];
326
+ const arquivo = candidatos.find(c => fs.existsSync(c));
327
+ if (!arquivo)
328
+ throw new Error('runtime.unavailable: schema de claims nao encontrado no pacote');
329
+ return JSON.parse(fs.readFileSync(arquivo, 'utf8'));
330
+ }
153
331
  /** Slug da sessao que conduz esta fase, rotacionando quando o slug ja foi usado na thread. */
154
332
  function slugDaSessao(thread, fase) {
155
333
  const bloco = (0, thread_1.blocoDaThread)(thread, fase);
@@ -160,18 +338,28 @@ function slugDaSessao(thread, fase) {
160
338
  return (0, slug_1.proximaRotacao)(base, usados);
161
339
  }
162
340
  /**
163
- * Modelo e esforco EFETIVAMENTE usados num despacho, resolvidos numa funcao so.
341
+ * Runtime, modelo e esforco EFETIVAMENTE usados num despacho, resolvidos numa funcao so.
342
+ *
343
+ * Precedencia (decisao D4 da thread ork-homologarcod): opcao explicita do CLI > setup do
344
+ * bloco (feature #setup, `.orkastery/setup.json`) > runtime do manifesto.
164
345
  *
165
346
  * Regra do esforco: quem informa SO o modelo esta pedindo o despacho mais capaz que
166
- * existe, entao o esforco assume `high` em vez de herdar o do manifesto. Quem informa o
167
- * esforco manda, e sem nenhum dos dois vale o manifesto. Resolver isso aqui, e nao no
168
- * ponto de chamada, e o que permite gravar o par no ledger como FATO: o mesmo valor que
169
- * foi para o runtime adapter e o que fica carimbado no evento.
347
+ * existe, entao o esforco assume `high` em vez de herdar o do setup ou do manifesto.
348
+ * Quem informa o esforco manda. Resolver isso aqui, e nao no ponto de chamada, e o que
349
+ * permite gravar o trio no ledger como FATO: o mesmo valor que foi para o runtime
350
+ * adapter e o que fica carimbado no evento.
170
351
  */
171
- function resolverDespacho(manifesto, opcoes) {
172
- const model = opcoes.model ?? manifesto.runtime.model;
173
- const effort = opcoes.effort ?? (opcoes.model !== undefined ? 'high' : manifesto.runtime.effort);
174
- return { model, effort };
352
+ function resolverDespacho(manifesto, opcoes, doBloco) {
353
+ const origem = doBloco?.runtime ?? manifesto.runtime.adapter;
354
+ const runtime = opcoes.runtime ?? origem;
355
+ if ((0, runtimes_1.runtimeConhecido)(runtime) && runtime !== origem && !opcoes.model?.trim())
356
+ throw new Error(`setup.model.required: ao trocar runtime de ${origem} para ${runtime}, informe --runtime ${runtime} --model <modelo> para este despacho; o setup do projeto permanece intacto.`);
357
+ const model = opcoes.model ?? doBloco?.model ?? manifesto.runtime.model;
358
+ if (!model.trim())
359
+ throw new Error(`setup.model.required: modelo pendente para ${runtime}; informe --model <modelo> neste despacho ou complete o bloco em ork setup.`);
360
+ const effort = opcoes.effort ??
361
+ (opcoes.model !== undefined ? 'high' : doBloco?.effort ?? manifesto.runtime.effort);
362
+ return { runtime, model, effort };
175
363
  }
176
364
  /** O comando com o prompt mascarado pelo hash: o ledger nao guarda o texto inteiro. */
177
365
  function comandoMascarado(comando, sha) {
@@ -194,6 +382,12 @@ function dadosDoDespacho(ctx, comando) {
194
382
  promptPath: path.relative(ctx.raiz, ctx.promptPath),
195
383
  promptSha256: ctx.promptSha256,
196
384
  origem: ctx.origem,
385
+ // I-36 (T10): a porta por onde o dono chegou. `runtime` e o motor; `canal` e a porta.
386
+ ...(ctx.canal ? { canal: ctx.canal } : {}),
387
+ ...(ctx.correlacao ? { correlacao: ctx.correlacao } : {}),
388
+ ...(ctx.identidade ? { identidade: ctx.identidade } : {}),
389
+ // I-33 (D4): so identidade e diretorio do perfil; o store guarda o resto e nada e segredo.
390
+ ...(ctx.perfil ? { perfil: ctx.perfil } : {}),
197
391
  comando: comandoMascarado(comando, ctx.promptSha256),
198
392
  };
199
393
  }
@@ -201,27 +395,36 @@ function dadosDoDespacho(ctx, comando) {
201
395
  * Fecha um despacho BEM SUCEDIDO: ledger, decisao autonoma ou pausa prevista, e a
202
396
  * sessao gravada no `thread.json`. Usada pelo `ork phase run` e pela retomada do B3.
203
397
  */
204
- function concluirDespacho(ctx, sessionId, verificada, comando) {
398
+ function concluirDespacho(ctx, sessionId, verificada, comando, fonteVerificacao, controlador,
399
+ /** Variante histórica do supervisor; a variante do controller não tem log próprio. */
400
+ sensores) {
205
401
  const dir = (0, thread_1.dirThread)(ctx.raiz, ctx.thread.id);
206
402
  const comum = dadosDoDespacho(ctx, comando);
207
403
  const bloco = (0, thread_1.blocoDaThread)(ctx.thread, ctx.fase);
208
404
  const pausaAoFim = (0, thread_1.pausaNaThread)(ctx.thread, ctx.fase);
209
- (0, ledger_1.registrar)(dir, ctx.thread.id, ledger_1.TIPOS_DE_EVENTO.faseDespachada, { ...comum, sessionId });
405
+ // I-51 (RM-047): a maquina que despachou. So o evento de conducao; os de HITL nao mudam.
406
+ (0, ledger_1.registrar)(dir, ctx.thread.id, ledger_1.TIPOS_DE_EVENTO.faseDespachada, { ...comum, sessionId, ...(controlador ? { controlador } : {}),
407
+ maquina: (0, maquina_1.nomeDaMaquina)() });
210
408
  (0, ledger_1.registrar)(dir, ctx.thread.id, ledger_1.TIPOS_DE_EVENTO.despachoVerificado, {
211
409
  fase: ctx.fase,
212
410
  slug: ctx.slug,
213
411
  sessionId,
214
- fonte: 'claude agents --json',
412
+ // A fonte e do runtime que despachou: e ela que diz ONDE conferir a sessao depois.
413
+ fonte: fonteVerificacao ?? (0, runtimes_1.resolverRuntime)(ctx.runtime).fonteVerificacao,
215
414
  encontrada: verificada,
216
415
  });
217
416
  if (!pausaAoFim) {
417
+ const tag = (0, modos_1.definicaoDoModo)(ctx.thread.modo).tag;
218
418
  (0, ledger_1.registrar)(dir, ctx.thread.id, ledger_1.TIPOS_DE_EVENTO.decisaoAutonoma, {
219
419
  fase: ctx.fase,
220
420
  slug: ctx.slug,
221
421
  sessionId,
222
422
  decisao: `bloco ${bloco.fases.join('-')} segue sem pausa humana`,
223
- autorizadoPor: `#TAG ${(0, modos_1.definicaoDoModo)(ctx.thread.modo).tag} no pedido do builder`,
423
+ // I-41 (GO-FIX 1, D11): o rastro tipado. `autorizadoPor` fica pelo que ja o lia.
424
+ quemDecidiu: `o builder, pela #TAG ${tag} no pedido`,
425
+ autorizadoPor: `#TAG ${tag} no pedido do builder`,
224
426
  evidencia: `prompt sha256 ${ctx.promptSha256}`,
427
+ razao: `o modo ${tag} não prevê pausa humana ao fim do bloco ${bloco.fases.join('-')}`,
225
428
  });
226
429
  }
227
430
  else {
@@ -234,6 +437,7 @@ function concluirDespacho(ctx, sessionId, verificada, comando) {
234
437
  });
235
438
  }
236
439
  const sessao = {
440
+ ...(controlador ? { controlador } : {}),
237
441
  slug: ctx.slug,
238
442
  fase: ctx.fase,
239
443
  bloco: bloco.fases.join('-'),
@@ -243,23 +447,110 @@ function concluirDespacho(ctx, sessionId, verificada, comando) {
243
447
  promptPath: path.relative(ctx.raiz, ctx.promptPath),
244
448
  promptSha256: ctx.promptSha256,
245
449
  verificada,
450
+ ...(ctx.perfil ? { perfil: ctx.perfil } : {}),
451
+ ...(ctx.canal ? { canal: ctx.canal } : {}),
452
+ ...(ctx.correlacao ? { correlacao: ctx.correlacao } : {}),
246
453
  };
247
454
  ctx.thread.sessoes.push(sessao);
455
+ // `ultimoUso` e informativo: store indisponivel nao desfaz um despacho ja provado.
456
+ if (ctx.perfil) {
457
+ try {
458
+ (0, runtime_profiles_1.registrarUsoDePerfil)(ctx.raiz, ctx.perfil.id, sessao.despachadaEm);
459
+ }
460
+ catch { /* informativo */ }
461
+ }
248
462
  ctx.thread.faseAtual = ctx.fase;
249
463
  (0, thread_1.gravarThread)(ctx.raiz, ctx.thread);
464
+ // I-36 (T7): a conducao deixa de ser do ato de despachar e passa a ser da janela da sessao.
465
+ vincularConducaoDaSessao(ctx, sessionId);
466
+ // I-51 (RM-047): as outras maquinas veem a fase nova; so com a fabrica compartilhada.
467
+ (0, fabrica_publicar_1.publicarEmSegundoPlano)(ctx.raiz);
468
+ if (ctx.runtime === 'codex' || ctx.runtime === 'claude-bg')
469
+ registrarObservacao(ctx, sessao, dir, controlador, sensores);
250
470
  return sessao;
251
471
  }
472
+ /**
473
+ * I-36 (T7): o lease de execucao sobrevive ao `ork phase run`, porque quem executa e a sessao. Ele
474
+ * so sai quando a fase termina (a prova da I-34 no ledger) ou com prova de abandono (D7).
475
+ */
476
+ function vincularConducaoDaSessao(ctx, sessionId) {
477
+ const sessao = { sessionId, runtime: ctx.runtime, perfil: ctx.perfil?.id ?? null };
478
+ const prazoMs = ctx.prazoDaSessaoMs ?? conducao_1.PRAZO_DE_SESSAO_PADRAO_MS;
479
+ if (ctx.conducao) {
480
+ ctx.conducao.converterEmSessao({ ...sessao, prazoMs });
481
+ return;
482
+ }
483
+ try {
484
+ (0, conducao_1.registrarConducaoDaSessao)(ctx.raiz, ctx.thread.id, { canal: ctx.canal ?? 'cli', correlacao: ctx.correlacao ?? null,
485
+ operacao: ctx.origem.startsWith('retry') ? 'retry.run' : 'phase.run', fase: ctx.fase, promptSha256: ctx.promptSha256,
486
+ identidade: ctx.identidade?.dispatchId ?? null, prazoMs }, sessao);
487
+ }
488
+ catch { /* a conducao protege a worktree; um despacho ja provado nao se desfaz por falta dela */ }
489
+ }
490
+ /** I-36: o prazo do lease de uma sessao do bloco: o limite de duracao dele, ou o padrao. */
491
+ function prazoDaSessao(limites) {
492
+ return limites?.duracaoMs ? limites.duracaoMs + conducao_1.MARGEM_DO_PRAZO_MS : conducao_1.PRAZO_DE_SESSAO_PADRAO_MS;
493
+ }
494
+ /**
495
+ * D4: registro da fonte e arranque do observador no ponto comum a `phase` e `retry`.
496
+ * Sem CLI manual e sem segundo runtime: quem despacha e prova a sessao inicia a observacao.
497
+ */
498
+ function registrarObservacao(ctx, sessao, dir, controlador, sensores) {
499
+ const mesmoDespacho = (e) => e.sessionId === sessao.sessionId && e.despachoEm === sessao.despachadaEm;
500
+ try {
501
+ // I-34: claude-bg observa pela fonte nativa; no PLAN o registro guarda o sha de base do artefato.
502
+ // I-33 (N1): o GO codex registra o HEAD do despacho, como a fonte claude-bg; sem ele a D11c nao ve commit pelo shell.
503
+ const head = ctx.runtime === 'codex' && ctx.fase === 'GO' ? { head: ctx.headNoDespacho ?? null } : {};
504
+ const fonte = ctx.runtime === 'claude-bg' ? (0, session_watcher_claude_1.fonteClaudeDoDespacho)(ctx.raiz, ctx.thread.id, ctx.fase, ctx.cwd)
505
+ : sensores?.logPath ? { logPath: sensores.logPath, processoPath: sensores.processoPath, reciboPath: sensores.reciboPath, ...head }
506
+ : controlador ? { controlador, cwd: ctx.cwd, ...head } : null;
507
+ if (!fonte)
508
+ return;
509
+ (0, session_watcher_1.validarFonteWatcher)(ctx.raiz, sessao.sessionId, fonte);
510
+ const eventos = (0, ledger_1.lerLedger)(dir);
511
+ if (!eventos.some(e => e.tipo === 'session_sensor_registered' && mesmoDespacho(e))) {
512
+ (0, ledger_1.registrar)(dir, ctx.thread.id, 'session_sensor_registered', { fase: ctx.fase,
513
+ sessionId: sessao.sessionId, despachoEm: sessao.despachadaEm, ...fonte, ...(ctx.perfil ? { perfil: ctx.perfil } : {}) });
514
+ }
515
+ // A primitiva verifica identidade/prontidao e ignora registros antigos sem prova.
516
+ (0, session_watcher_1.iniciarWatcher)(ctx.raiz, sessao.sessionId);
517
+ }
518
+ catch {
519
+ (0, ledger_1.registrar)(dir, ctx.thread.id, 'session_watcher_error', { fase: ctx.fase, sessionId: sessao.sessionId,
520
+ despachoEm: sessao.despachadaEm, motivo: 'runtime.unavailable', erro: 'fonte ou prontidao do watcher invalida',
521
+ origem: 'phase.dispatch', ...(controlador ? { controlador } : {}) });
522
+ }
523
+ }
252
524
  /** Despacha a fase pelo runtime adapter e registra tudo no ledger da thread. */
253
525
  function rodarFase(carregado, threadId, opcoes) {
526
+ const rodar = () => opcoes.dryRun ? rodarFaseSobLock(carregado, threadId, opcoes) :
527
+ (0, hitl_gates_1.comLockHitl)(carregado.raiz, threadId, () => rodarFaseSobLock(carregado, threadId, opcoes));
528
+ // I-36 (D2): `--esperar` espera a vez FORA do lock HITL (que serializa respostas do dono) e so
529
+ // depois repete o pedido. Sem ele, a recusa sai na hora.
530
+ const prazo = opcoes.esperarMs && opcoes.esperarMs > 0 && !opcoes.dryRun ? Date.now() + opcoes.esperarMs : null;
531
+ for (;;) {
532
+ if (prazo)
533
+ (0, conducao_1.esperarConducaoLivre)(carregado.raiz, threadId, Math.max(0, prazo - Date.now()));
534
+ const r = rodar();
535
+ if (r.motivo !== 'conducao.em-andamento' || !prazo || Date.now() >= prazo)
536
+ return r;
537
+ }
538
+ }
539
+ function rodarFaseSobLock(carregado, threadId, opcoes) {
254
540
  const { raiz, manifesto } = carregado;
255
541
  const thread = (0, thread_1.lerThread)(raiz, threadId);
256
542
  const fase = opcoes.fase;
257
543
  const dir = (0, thread_1.dirThread)(raiz, thread.id);
258
544
  const slug = slugDaSessao(thread, fase);
259
- // Resolve UMA vez o par modelo/esforco: o mesmo objeto vai ao runtime adapter, ao
260
- // ledger e a resposta do CLI. Resolver duas vezes e o caminho curto para o ledger
261
- // dizer um modelo e o `claude --bg` receber outro.
262
- const { model, effort } = resolverDespacho(manifesto, opcoes);
545
+ // Resolve UMA vez o trio runtime/modelo/esforco: o mesmo objeto vai ao runtime adapter,
546
+ // ao ledger e a resposta do CLI. Resolver duas vezes e o caminho curto para o ledger
547
+ // dizer um modelo e o runtime receber outro. O setup do bloco (feature #setup) entra
548
+ // aqui: cada bloco de cada modo pode abrir a sessao com runtime/modelo/esforco proprios.
549
+ const setup = (0, setup_1.lerSetup)(raiz);
550
+ const doBloco = (0, setup_1.configDoBloco)(setup, thread.modo, fase);
551
+ const { runtime, model, effort } = resolverDespacho(manifesto, opcoes, doBloco);
552
+ const limites = (0, setup_1.limitesDoBloco)(manifesto, setup, thread.modo, fase);
553
+ const rt = (0, runtimes_1.resolverRuntime)(runtime);
263
554
  // Bloco B6: a memoria entra ANTES do hash. O prompt que o ledger carimba e o mesmo que
264
555
  // o agente recebe, memoria injetada inclusa.
265
556
  const memoria = opcoes.memoria ?? (0, memoria_1.abrirMemoria)(carregado);
@@ -269,8 +560,23 @@ function rodarFase(carregado, threadId, opcoes) {
269
560
  // Gate tipado do bloco B1: as policies do manifesto valem ANTES do despacho, em
270
561
  // qualquer modo. Prompt reprovado nem chega a ser gravado em disco: gravar um prompt
271
562
  // com credencial e criar um segundo vazamento no proprio repositorio.
272
- const violacoes = (0, policies_1.avaliarPolicies)(manifesto, { gate: 'phase.dispatch', prompt });
563
+ // RM-008 (fatia 3): as licoes que viraram policy recebem os fatos da thread, calculados aqui.
564
+ const blocoDespachado = (0, thread_1.blocoDaThread)(thread, fase);
565
+ const violacoes = (0, policies_1.avaliarPolicies)(manifesto, {
566
+ gate: 'phase.dispatch', prompt, threadId: thread.id, modo: thread.modo, fase,
567
+ bloco: thread.blocos.indexOf(blocoDespachado) + 1,
568
+ blocoComGo: blocoDespachado.fases.includes('GO'),
569
+ temBaseline: (0, ledger_1.lerLedger)(dir).some((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.baselineGravada),
570
+ fallbackDoBloco: (0, setup_1.fallbackDoBloco)(setup, thread.modo, fase),
571
+ });
273
572
  const bloqueiam = (0, policies_1.bloqueantes)(violacoes);
573
+ // Warn registra e segue: o aviso vai ao ledger (fora do ensaio) e o despacho continua.
574
+ if (!opcoes.dryRun) {
575
+ for (const v of (0, policies_1.avisos)(violacoes)) {
576
+ (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.politicaAviso, { gate: 'phase.dispatch', fase, slug,
577
+ policy: v.policy, detalhe: v.detalhe, correcao: v.correcao, modo: thread.modo });
578
+ }
579
+ }
274
580
  if (bloqueiam.length > 0) {
275
581
  const detalhe = bloqueiam.map((v) => `${v.policy}: ${v.detalhe}`).join('; ');
276
582
  // Bloco B3: violacao de CUSTO sai tipada como tal. E o que permite a politica de
@@ -296,6 +602,7 @@ function rodarFase(carregado, threadId, opcoes) {
296
602
  verificada: false,
297
603
  pausaAoFim: (0, thread_1.pausaNaThread)(thread, fase),
298
604
  dryRun: opcoes.dryRun === true,
605
+ runtime,
299
606
  model,
300
607
  effort,
301
608
  bloqueado: true,
@@ -304,173 +611,327 @@ function rodarFase(carregado, threadId, opcoes) {
304
611
  erro: detalhe,
305
612
  };
306
613
  }
307
- (0, util_1.gravar)(promptPath, prompt);
308
- // A injecao e evidencia: o ledger diz quantos itens entraram, de que colecao e de onde,
309
- // para que "o prompt tinha a decisao D2" seja verificavel depois sem reler o prompt.
310
- if (injecao.itens.length > 0) {
311
- (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.memoriaInjetada, {
312
- fase,
313
- slug,
314
- regime: injecao.regime,
315
- decisoes: injecao.decisoes,
316
- licoes: injecao.licoes,
317
- regras: injecao.regras,
318
- itens: injecao.itens.map((i) => `${i.id}:${i.colecao}:${i.location}`),
319
- promptSha256: sha,
320
- });
614
+ // I-43 (D4, viga a): a validacao por runtime diferente e cobrada no MESMO portao
615
+ // por onde todo despacho ja passa. Trocar um portao por outro no mesmo lugar e a
616
+ // mudanca que o CHECK consegue conferir lendo um diff.
617
+ const erroDeRuntime = runtimeCruzadoParaDespacho(thread, fase, runtime);
618
+ if (erroDeRuntime) {
619
+ (0, gates_1.registrarGateBloqueado)(dir, thread.id, { gate: 'phase.dispatch', motivo: 'runtime.autoconferencia',
620
+ modo: thread.modo, detalhe: erroDeRuntime, fase, slug,
621
+ correcao: `ork phase run ${thread.id} CHECK --runtime <outro> --prompt "<pedido>"` });
622
+ return { thread, slug, promptPath, promptSha256: sha, comando: [], sessionId: null,
623
+ verificada: false, pausaAoFim: (0, thread_1.pausaNaThread)(thread, fase), dryRun: opcoes.dryRun === true,
624
+ runtime, model, effort, bloqueado: true, motivo: 'runtime.autoconferencia', violacoes, erro: erroDeRuntime };
321
625
  }
322
- if (memoria.estado.motivo && memoria.estado.pedido === 'orkmind') {
323
- (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.memoriaDegradada, {
324
- fase,
325
- slug,
326
- pedido: memoria.estado.pedido,
327
- efetivo: memoria.estado.efetivo,
328
- motivo: memoria.estado.motivo,
329
- detalhe: memoria.estado.detalhe,
330
- correcao: memoria.estado.correcao,
331
- });
626
+ const erroDeEstado = estadoParaDespacho(raiz, thread, opcoes.dryRun);
627
+ if (erroDeEstado) {
628
+ (0, gates_1.registrarGateBloqueado)(dir, thread.id, { gate: 'phase.dispatch', motivo: 'tree.blocked',
629
+ modo: thread.modo, detalhe: erroDeEstado, correcao: `ork worktree sync ${thread.id}`, fase, slug });
630
+ return { thread, slug, promptPath, promptSha256: sha, comando: [], sessionId: null,
631
+ verificada: false, pausaAoFim: (0, thread_1.pausaNaThread)(thread, fase), dryRun: opcoes.dryRun === true,
632
+ runtime, model, effort, bloqueado: true, motivo: 'tree.blocked', violacoes, erro: erroDeEstado };
332
633
  }
333
- const cwd = thread.worktree ?? raiz;
334
- const pausaAoFim = (0, thread_1.pausaNaThread)(thread, fase);
335
- const resultado = adapter.despachar({
336
- prompt,
337
- nome: slug,
338
- cwd,
339
- model,
340
- effort,
341
- dryRun: opcoes.dryRun,
342
- });
343
- const ctx = {
344
- raiz,
345
- thread,
346
- fase,
347
- slug,
348
- promptPath,
349
- promptSha256: sha,
350
- runtime: manifesto.runtime.adapter,
351
- cwd,
352
- model,
353
- effort,
354
- origem: 'phase.run',
634
+ // I-36 (T7, T14): validado o pedido, a conducao da thread, antes de tocar a worktree. A mesma fase com o
635
+ // mesmo prompt de quem conduz devolve a sessao em andamento sem chamar o adapter; outro pedido recebe a recusa.
636
+ const identidade = (0, runtime_context_1.novaIdentidadeDeDespacho)(thread.id, fase);
637
+ const canal = opcoes.canal ?? (0, conducao_1.canalDoProcesso)();
638
+ const pedidoDeConducao = { canal, correlacao: opcoes.correlacao ?? null, operacao: 'phase.run', fase,
639
+ promptSha256: sha, identidade: identidade.dispatchId, prazoMs: prazoDaSessao(limites) };
640
+ const ocupadaPor = (ocupada, gravarNoLedger) => {
641
+ const base = { thread, slug, promptPath, promptSha256: sha, comando: [], verificada: false,
642
+ pausaAoFim: (0, thread_1.pausaNaThread)(thread, fase), dryRun: opcoes.dryRun === true, runtime, model, effort, violacoes: [] };
643
+ const atual = ocupada.atual;
644
+ if (ocupada.idempotente && atual) {
645
+ if (gravarNoLedger)
646
+ (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.despachoIdempotente, { fase, promptSha256: sha, canal,
647
+ sessionId: atual.sessao, conducao: { canal: atual.canal, desde: atual.desde },
648
+ razao: 'mesma fase e mesmo prompt de quem ja conduz: a sessao em andamento atende o pedido, sem sessao nova e sem cota' });
649
+ return { ...base, sessionId: atual.sessao, bloqueado: false, motivo: null, idempotente: true, conducao: atual };
650
+ }
651
+ const recusa = (0, conducao_1.recusaDeConducao)(thread.id, atual, pedidoDeConducao);
652
+ if (gravarNoLedger)
653
+ (0, conducao_1.registrarRecusa)(raiz, recusa);
654
+ return { ...base, sessionId: null, bloqueado: true, motivo: 'conducao.em-andamento', erro: recusa.texto, recusa,
655
+ ...(atual ? { conducao: atual } : {}) };
355
656
  };
356
- const comum = dadosDoDespacho(ctx, resultado.comando);
657
+ let conducao = null;
357
658
  if (opcoes.dryRun) {
358
- return {
659
+ // O ensaio nao toma nada, mas diz a verdade: com conducao em andamento, o despacho real seria recusado.
660
+ const atual = (0, conducao_1.conducaoDaThread)(raiz, thread.id);
661
+ if (atual)
662
+ return ocupadaPor({ ok: false, idempotente: atual.fase === fase && atual.promptSha256 === sha, atual }, false);
663
+ }
664
+ else {
665
+ const tomada = (0, conducao_1.tomarConducao)(raiz, thread.id, pedidoDeConducao);
666
+ if (!tomada.ok)
667
+ return ocupadaPor(tomada, true);
668
+ conducao = tomada;
669
+ }
670
+ try {
671
+ const cwd = thread.worktree ?? raiz;
672
+ let contextoRuntime;
673
+ try {
674
+ contextoRuntime = (0, runtime_context_1.contextoDoProjeto)(raiz, runtime, cwd, thread.id);
675
+ }
676
+ catch (e) {
677
+ const erro = `runtime.unavailable: ${e.message}`;
678
+ (0, gates_1.registrarGateBloqueado)(dir, thread.id, { gate: 'phase.dispatch', motivo: 'runtime.unavailable',
679
+ modo: thread.modo, detalhe: erro, fase, slug });
680
+ return { thread, slug, promptPath, promptSha256: sha, comando: [], sessionId: null,
681
+ verificada: false, pausaAoFim: (0, thread_1.pausaNaThread)(thread, fase), dryRun: opcoes.dryRun === true,
682
+ runtime, model, effort, bloqueado: true, motivo: 'runtime.unavailable', violacoes, erro };
683
+ }
684
+ if (contextoRuntime)
685
+ contextoRuntime.identidade = identidade;
686
+ (0, util_1.gravar)(promptPath, prompt);
687
+ // I-33 (D2, D4, D7): o perfil sai do store com o login conferido. Sem perfil disponivel, o
688
+ // despacho nao sai pela conta errada: o prompt ja gravado fica registrado para a rotacao do
689
+ // retry (outro runtime da ordem de fallback, ou a fila), com o motivo tipado.
690
+ let escolha;
691
+ try {
692
+ escolha = perfilParaDespacho(carregado, thread, fase, runtime, { perfil: opcoes.perfil, dryRun: opcoes.dryRun, origem: 'phase.run' });
693
+ }
694
+ catch (e) {
695
+ escolha = { perfil: null, configurados: 0, erro: e.message };
696
+ }
697
+ if (escolha.erro) {
698
+ const motivo = escolha.motivo ?? 'runtime.unavailable';
699
+ const erro = `${motivo}: ${escolha.erro}`;
700
+ if (!opcoes.dryRun) {
701
+ const semPerfil = { identidade, raiz, thread, fase, slug, promptPath, promptSha256: sha, runtime, cwd, model, effort,
702
+ origem: 'phase.run', canal, correlacao: opcoes.correlacao ?? null };
703
+ (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.despachoFalhou, { ...dadosDoDespacho(semPerfil, []), erro });
704
+ (0, gates_1.registrarGateBloqueado)(dir, thread.id, { gate: 'phase.dispatch', motivo, modo: thread.modo, detalhe: erro,
705
+ correcao: motivo === 'cost.violation'
706
+ ? 'refaca o login do perfil pela assinatura (claude auth login --claudeai ou codex login com ChatGPT) e rode ork accounts check; perfil de provider pago nunca despacha'
707
+ : 'ork accounts list mostra o estado de cada perfil; ork retry run segue pela ordem de fallback ou pela fila',
708
+ fase, slug, runtime, origem: 'phase.run' });
709
+ }
710
+ return { thread, slug, promptPath, promptSha256: sha, comando: [], sessionId: null,
711
+ verificada: false, pausaAoFim: (0, thread_1.pausaNaThread)(thread, fase), dryRun: opcoes.dryRun === true,
712
+ runtime, model, effort, bloqueado: true, motivo, violacoes, erro };
713
+ }
714
+ const perfil = escolha.perfil ?? undefined;
715
+ // A injecao e evidencia: o ledger diz quantos itens entraram, de que colecao e de onde,
716
+ // para que "o prompt tinha a decisao D2" seja verificavel depois sem reler o prompt.
717
+ if (injecao.itens.length > 0) {
718
+ (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.memoriaInjetada, {
719
+ fase,
720
+ slug,
721
+ regime: injecao.regime,
722
+ decisoes: injecao.decisoes,
723
+ licoes: injecao.licoes,
724
+ regras: injecao.regras,
725
+ itens: injecao.itens.map((i) => `${i.id}:${i.colecao}:${i.location}`),
726
+ promptSha256: sha,
727
+ });
728
+ }
729
+ if (memoria.estado.motivo && memoria.estado.pedido === 'orkmind') {
730
+ (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.memoriaDegradada, {
731
+ fase,
732
+ slug,
733
+ pedido: memoria.estado.pedido,
734
+ efetivo: memoria.estado.efetivo,
735
+ motivo: memoria.estado.motivo,
736
+ detalhe: memoria.estado.detalhe,
737
+ correcao: memoria.estado.correcao,
738
+ });
739
+ }
740
+ const pausaAoFim = (0, thread_1.pausaNaThread)(thread, fase);
741
+ const claimsSchema = fase === 'GO' && runtime === 'codex'
742
+ ? schemaDeClaimsDoPacote()
743
+ : undefined;
744
+ // I-33 (N1): lido antes de a sessao existir; depois dela, um commit rapido ja estaria no HEAD.
745
+ const headNoDespacho = fase === 'GO' && runtime === 'codex' && !opcoes.dryRun ? (0, session_watcher_claude_1.headDaWorktree)(cwd) : undefined;
746
+ const resultado = rt.despachar({
747
+ prompt,
748
+ nome: slug,
749
+ cwd,
750
+ model,
751
+ effort,
752
+ dryRun: opcoes.dryRun,
753
+ sandbox: manifesto.runtime.sandbox,
754
+ logDir: path.join(dir, 'sessoes'),
755
+ vinculo: { thread: thread.id, fase, promptSha256: sha },
756
+ contextoRuntime,
757
+ ...(perfil ? { perfil } : {}),
758
+ ...(fase === 'PLAN' ? { colaboracao: 'plan' } : {}),
759
+ ...(claimsSchema ? { outputSchema: claimsSchema } : {}),
760
+ // `thread.base.branch` vira a branch fonte quando a thread ganha worktree
761
+ // (`ork/<slug>`). O review nativo precisa comparar essa fonte com a base de
762
+ // integração do projeto; usar a própria fonte produz um diff vazio.
763
+ ...(fase === 'CHECK' && runtime === 'codex' ? { reviewBaseBranch: manifesto.worktree.base_branch } : {}),
764
+ ...(limites ? { duracaoMaximaMs: limites.duracaoMs } : {}),
765
+ // I-36 (D4): a sessao filha herda a identidade do despacho e o canal; e assim que ela reentra.
766
+ ambienteExtra: (0, conducao_1.ambienteDaConducao)(identidade.dispatchId, thread.id, canal),
767
+ });
768
+ const ctx = {
769
+ identidade,
770
+ canal,
771
+ correlacao: opcoes.correlacao ?? null,
772
+ ...(conducao ? { conducao } : {}),
773
+ prazoDaSessaoMs: pedidoDeConducao.prazoMs,
774
+ raiz,
359
775
  thread,
776
+ fase,
360
777
  slug,
361
778
  promptPath,
362
779
  promptSha256: sha,
363
- comando: resultado.comando,
364
- sessionId: null,
365
- verificada: false,
366
- pausaAoFim,
367
- dryRun: true,
368
- // O ensaio NAO toca no ledger (a garantia do B0 continua de pe), mas devolve o
369
- // par resolvido: e assim que o `--dry-run` mostra o modelo que seria despachado.
780
+ runtime,
781
+ cwd,
370
782
  model,
371
783
  effort,
372
- bloqueado: false,
373
- motivo: null,
374
- violacoes,
784
+ origem: 'phase.run',
785
+ ...(perfil ? { perfil } : {}),
786
+ ...(headNoDespacho !== undefined ? { headNoDespacho } : {}),
375
787
  };
376
- }
377
- if (!resultado.ok || !resultado.sessionId) {
378
- const erro = resultado.erro ?? 'sem sessionId na saida do runtime';
379
- (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.despachoFalhou, {
380
- ...comum,
381
- erro,
382
- stderr: resultado.stderr.slice(0, 500),
383
- });
384
- // Bloco B3: rate limit nao e falha seca. A fase morreu com HORA de volta, entao ela
385
- // vira pedido na fila duravel em vez de virar tarefa da memoria de alguem. O prompt
386
- // ja esta gravado em disco com o sha256 acima, e e ele que sera redespachado.
387
- const sinal = resultado.rateLimit ?? null;
388
- if (sinal) {
389
- (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.rateLimitDetectado, {
390
- fase,
391
- slug,
392
- fonte: sinal.fonte,
393
- resetEm: sinal.resetEm,
394
- trecho: sinal.trecho,
395
- evidencia: 'stderr real do runtime adapter, nao relato do agente',
396
- });
397
- const pedido = (0, ratelimit_1.enfileirar)(carregado, {
398
- thread: thread.id,
399
- fase,
788
+ const comum = dadosDoDespacho(ctx, resultado.comando);
789
+ if (opcoes.dryRun) {
790
+ return {
791
+ thread,
400
792
  slug,
401
793
  promptPath,
402
794
  promptSha256: sha,
403
- cwd,
795
+ comando: resultado.comando,
796
+ sessionId: null,
797
+ verificada: false,
798
+ pausaAoFim,
799
+ dryRun: true,
800
+ // O ensaio NAO toca no ledger (a garantia do B0 continua de pe), mas devolve o
801
+ // par resolvido: e assim que o `--dry-run` mostra o modelo que seria despachado.
802
+ runtime,
404
803
  model,
405
804
  effort,
406
- sinal,
407
- detalhe: erro,
408
- });
409
- (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.rateLimitEnfileirado, {
410
- fase,
411
- slug,
412
- pedido: pedido.id,
413
- liberaEm: pedido.liberaEm,
414
- janelaEstimada: pedido.janelaEstimada,
415
- fonte: sinal.fonte,
416
- promptSha256: sha,
417
- correcao: `ork retry resume (ou aguarde ate ${pedido.liberaEm})`,
805
+ bloqueado: false,
806
+ motivo: null,
807
+ violacoes,
808
+ };
809
+ }
810
+ if (!resultado.ok || !resultado.sessionId) {
811
+ const erro = resultado.erro ?? 'sem sessionId na saida do runtime';
812
+ (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.despachoFalhou, {
813
+ ...comum,
814
+ controlador: resultado.controlador,
815
+ erro,
816
+ stderr: resultado.stderr.slice(0, 500),
418
817
  });
818
+ // I-33 (D1, D5): a CONTA recusou o despacho. Nada rodou, entao nada foi produzido: o perfil
819
+ // sai do rodizio e o gate tipado fica para a rotacao do retry, que redespacha o mesmo prompt.
820
+ const falha = resultado.falhaDeConta ?? null;
821
+ if (falha) {
822
+ try {
823
+ (0, ratelimit_1.marcarContaDaFalha)(carregado, perfil, falha);
824
+ }
825
+ catch { /* o gate abaixo continua registrando a falha */ }
826
+ (0, gates_1.registrarGateBloqueado)(dir, thread.id, { gate: 'phase.dispatch', motivo: falha.motivo, modo: thread.modo, detalhe: erro,
827
+ correcao: 'ork retry run rotaciona para o proximo perfil, para o runtime de fallback ou para a fila', fase, slug,
828
+ runtime, origem: 'phase.run', falhaDeConta: falha, ...(perfil ? { perfil } : {}),
829
+ evidencia: 'saida real do runtime adapter no despacho, nao relato do agente' });
830
+ return { thread, slug, promptPath, promptSha256: sha, comando: resultado.comando, controlador: resultado.controlador,
831
+ sessionId: null, verificada: false, pausaAoFim, dryRun: false, runtime, model, effort, bloqueado: false,
832
+ motivo: falha.motivo, violacoes, naFila: null, erro };
833
+ }
834
+ // Bloco B3: rate limit nao e falha seca. A fase morreu com HORA de volta, entao ela
835
+ // vira pedido na fila duravel em vez de virar tarefa da memoria de alguem. O prompt
836
+ // ja esta gravado em disco com o sha256 acima, e e ele que sera redespachado.
837
+ const sinal = resultado.rateLimit ?? null;
838
+ if (sinal) {
839
+ (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.rateLimitDetectado, {
840
+ fase,
841
+ slug,
842
+ fonte: sinal.fonte,
843
+ resetEm: sinal.resetEm,
844
+ trecho: sinal.trecho,
845
+ evidencia: 'stderr real do runtime adapter, nao relato do agente',
846
+ });
847
+ const pedido = (0, ratelimit_1.enfileirar)(carregado, {
848
+ thread: thread.id,
849
+ fase,
850
+ slug,
851
+ promptPath,
852
+ promptSha256: sha,
853
+ cwd,
854
+ model,
855
+ effort,
856
+ sinal,
857
+ detalhe: erro,
858
+ });
859
+ (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.rateLimitEnfileirado, {
860
+ fase,
861
+ slug,
862
+ pedido: pedido.id,
863
+ liberaEm: pedido.liberaEm,
864
+ janelaEstimada: pedido.janelaEstimada,
865
+ fonte: sinal.fonte,
866
+ promptSha256: sha,
867
+ correcao: `ork retry resume (ou aguarde ate ${pedido.liberaEm})`,
868
+ });
869
+ return {
870
+ thread,
871
+ slug,
872
+ promptPath,
873
+ promptSha256: sha,
874
+ comando: resultado.comando,
875
+ controlador: resultado.controlador,
876
+ sessionId: null,
877
+ verificada: false,
878
+ pausaAoFim,
879
+ dryRun: false,
880
+ runtime,
881
+ model,
882
+ effort,
883
+ bloqueado: false,
884
+ motivo: 'runtime.rate-limited',
885
+ violacoes,
886
+ naFila: pedido,
887
+ erro,
888
+ };
889
+ }
419
890
  return {
420
891
  thread,
421
892
  slug,
422
893
  promptPath,
423
894
  promptSha256: sha,
424
895
  comando: resultado.comando,
896
+ controlador: resultado.controlador,
425
897
  sessionId: null,
426
898
  verificada: false,
427
899
  pausaAoFim,
428
900
  dryRun: false,
901
+ runtime,
429
902
  model,
430
903
  effort,
431
904
  bloqueado: false,
432
- motivo: 'runtime.rate-limited',
905
+ motivo: 'runtime.unavailable',
433
906
  violacoes,
434
- naFila: pedido,
907
+ naFila: null,
435
908
  erro,
436
909
  };
437
910
  }
911
+ concluirDespacho(ctx, resultado.sessionId, resultado.verificada, resultado.comando, rt.fonteVerificacao, resultado.controlador, { logPath: resultado.logPath, processoPath: resultado.processoPath, reciboPath: resultado.reciboPath });
438
912
  return {
439
913
  thread,
440
914
  slug,
441
915
  promptPath,
442
916
  promptSha256: sha,
443
917
  comando: resultado.comando,
444
- sessionId: null,
445
- verificada: false,
918
+ sessionId: resultado.sessionId,
919
+ controlador: resultado.controlador,
920
+ verificada: resultado.verificada,
446
921
  pausaAoFim,
447
922
  dryRun: false,
923
+ runtime,
448
924
  model,
449
925
  effort,
450
926
  bloqueado: false,
451
- motivo: 'runtime.unavailable',
927
+ motivo: null,
452
928
  violacoes,
453
- naFila: null,
454
- erro,
455
929
  };
456
930
  }
457
- concluirDespacho(ctx, resultado.sessionId, resultado.verificada, resultado.comando);
458
- return {
459
- thread,
460
- slug,
461
- promptPath,
462
- promptSha256: sha,
463
- comando: resultado.comando,
464
- sessionId: resultado.sessionId,
465
- verificada: resultado.verificada,
466
- pausaAoFim,
467
- dryRun: false,
468
- model,
469
- effort,
470
- bloqueado: false,
471
- motivo: null,
472
- violacoes,
473
- };
931
+ finally {
932
+ // Despacho que nao virou sessao devolve a conducao; o que virou ja a entregou a sessao.
933
+ conducao?.liberar();
934
+ }
474
935
  }
475
936
  /** Tabela de `ork phase list`: o historico do ledger da thread. */
476
937
  function tabelaDoLedger(raiz, threadId) {
@@ -523,7 +984,7 @@ function tabelaDoLedger(raiz, threadId) {
523
984
  if (e.cobertura)
524
985
  detalhe.push(`cobertura ${String(e.cobertura)}`);
525
986
  if (e.liberaEm)
526
- detalhe.push(`libera em ${String(e.liberaEm)}`);
987
+ detalhe.push(`libera em ${(0, horario_1.formatarDataHora)(String(e.liberaEm))}`);
527
988
  if (e.tentativa !== undefined && e.limite !== undefined) {
528
989
  detalhe.push(`tentativa ${String(e.tentativa)}/${String(e.limite)}`);
529
990
  }
@@ -555,12 +1016,12 @@ function tabelaDoLedger(raiz, threadId) {
555
1016
  if (e.erro)
556
1017
  detalhe.push(`erro: ${String(e.erro)}`);
557
1018
  if (e.detalhe && !e.decisao)
558
- detalhe.push(String(e.detalhe).slice(0, 90));
1019
+ detalhe.push((0, horario_1.localizarTexto)(String(e.detalhe)).slice(0, 90));
559
1020
  if (e.modo && !e.fase)
560
1021
  detalhe.push(`modo ${String(e.modo)}`);
561
- return [e.ts.replace('T', ' ').slice(0, 19), e.tipo, detalhe.join(' | ')];
1022
+ return [(0, horario_1.formatarDataHora)(e.ts, { segundos: true }), e.tipo, detalhe.join(' | ')];
562
1023
  });
563
- return (0, util_1.tabela)(['QUANDO', 'EVENTO', 'DETALHE'], linhas);
1024
+ return `${(0, util_1.tabela)(['QUANDO', 'EVENTO', 'DETALHE'], linhas)}\n${(0, horario_1.legendaDoFuso)()}`;
564
1025
  }
565
1026
  /** Slug legivel por humano de uma sessao (usado nas mensagens do CLI). */
566
1027
  function descreverSlug(slug) {