@orkastery/cli 0.4.2 → 0.5.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 (128) hide show
  1. package/adapters/README.md +11 -0
  2. package/adapters/claude-code/.claude-plugin/plugin.json +3 -1
  3. package/adapters/claude-code/agents/ork-check.md +1 -1
  4. package/adapters/claude-code/agents/ork-go.md +2 -2
  5. package/adapters/claude-code/agents/ork-goal.md +1 -1
  6. package/adapters/claude-code/agents/ork-master.md +1 -1
  7. package/adapters/claude-code/agents/ork-plan.md +1 -1
  8. package/adapters/claude-code/agents/ork-ship.md +1 -1
  9. package/adapters/claude-code/commands/master.md +11 -8
  10. package/adapters/claude-code/commands/ork.md +8 -0
  11. package/adapters/claude-code/commands/ship.md +2 -2
  12. package/adapters/codex/skills/ork/SKILL.md +12 -1
  13. package/adapters/hermes/README.md +3 -0
  14. package/adapters/hermes/bin/ork-abrir-thread.sh +17 -2
  15. package/adapters/hermes/bin/ork-brain.sh +3 -0
  16. package/adapters/hermes/bin/ork-maestro.sh +4 -1
  17. package/adapters/hermes/bin/ork-network-roadmap.sh +11 -0
  18. package/adapters/hermes/bin/ork-roadmap-status.sh +9 -0
  19. package/adapters/hermes/hermes.plugin.json +2 -0
  20. package/adapters/hermes/hitl-ingress/__init__.py +36 -3
  21. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +19 -20
  22. package/adapters/openclaw/README.md +34 -3
  23. package/adapters/openclaw/dist/hitl-ingress.js +37 -3
  24. package/adapters/openclaw/dist/index.js +92 -39
  25. package/adapters/openclaw/openclaw.plugin.json +10 -3
  26. package/adapters/openclaw/package.json +2 -2
  27. package/adapters/openclaw/src/hitl-ingress.ts +32 -3
  28. package/adapters/openclaw/src/index.ts +108 -41
  29. package/assets/docs/padroes/documentacao-de-produto.md +2 -2
  30. package/assets/docs/padroes/roadmap-de-produto.md +1 -1
  31. package/assets/docs/produto/_modelo-feature.md +2 -0
  32. package/assets/orkmind_bridge.py +214 -1
  33. package/dist/adapters/claude-bg.js +88 -9
  34. package/dist/adapters/codex-events.js +3 -1
  35. package/dist/board.js +74 -0
  36. package/dist/branch-de-estado.js +5 -4
  37. package/dist/busca-semantica.js +161 -0
  38. package/dist/ci.js +141 -10
  39. package/dist/company-brain-cli.js +27 -2
  40. package/dist/company-brain-context.js +132 -0
  41. package/dist/company-brain-dossie.js +376 -0
  42. package/dist/company-brain-mcp.js +4 -1
  43. package/dist/conducao.js +117 -31
  44. package/dist/decisao-autonoma.js +42 -0
  45. package/dist/docs.js +64 -22
  46. package/dist/doctor.js +33 -0
  47. package/dist/entrega-pr.js +109 -5
  48. package/dist/experiencia-instalacao.js +256 -0
  49. package/dist/experiencia.js +72 -0
  50. package/dist/fabrica-estado.js +43 -10
  51. package/dist/fechamento.js +105 -0
  52. package/dist/fix.js +1 -1
  53. package/dist/forja.js +435 -0
  54. package/dist/gates.js +10 -1
  55. package/dist/hitl-classificacao.js +11 -1
  56. package/dist/hitl-contract.js +50 -7
  57. package/dist/hitl-curto.js +158 -0
  58. package/dist/hitl-gates.js +67 -1
  59. package/dist/hitl-lote.js +30 -15
  60. package/dist/hitl-presentation.js +10 -7
  61. package/dist/hitl-resumo.js +33 -12
  62. package/dist/hitl-texto-livre.js +150 -0
  63. package/dist/hosts.js +86 -1
  64. package/dist/index.js +605 -41
  65. package/dist/indice-vetorial.js +317 -0
  66. package/dist/init.js +13 -0
  67. package/dist/integracoes-locais.js +1 -1
  68. package/dist/intelligence-benchmark-contract.js +570 -0
  69. package/dist/intelligence-graph-cli.js +430 -0
  70. package/dist/intelligence-graph-contract.js +577 -0
  71. package/dist/intelligence-graph-extract-md.js +469 -0
  72. package/dist/intelligence-graph-extract-ts.js +810 -0
  73. package/dist/intelligence-graph-extract.js +369 -0
  74. package/dist/intelligence-graph-index.js +512 -0
  75. package/dist/intelligence-graph-parsers.js +327 -0
  76. package/dist/intelligence-graph-query.js +344 -0
  77. package/dist/intelligence-graph-repo.js +187 -0
  78. package/dist/leases.js +84 -0
  79. package/dist/ledger.js +14 -0
  80. package/dist/maestro-cli.js +10 -1
  81. package/dist/maestro-contract.js +5 -1
  82. package/dist/maestro-discovery.js +16 -2
  83. package/dist/maestro-snapshot.js +6 -2
  84. package/dist/manifest.js +142 -5
  85. package/dist/master-batch.js +3 -2
  86. package/dist/master-digest.js +2 -2
  87. package/dist/master-nota.js +203 -0
  88. package/dist/master.js +4 -0
  89. package/dist/mcp-experiencia.js +164 -0
  90. package/dist/mcp-git.js +36 -10
  91. package/dist/mcp-install.js +3 -1
  92. package/dist/mcp-maestro.js +3 -2
  93. package/dist/mcp-server.js +84 -5
  94. package/dist/memoria.js +147 -1
  95. package/dist/network-roadmap.js +907 -0
  96. package/dist/ocupacao.js +38 -2
  97. package/dist/onboarding.js +113 -7
  98. package/dist/orkmind.js +165 -9
  99. package/dist/phase.js +166 -13
  100. package/dist/policies.js +2 -0
  101. package/dist/projeto-alvo.js +551 -0
  102. package/dist/prompts.js +1 -1
  103. package/dist/pulse-consentimento.js +4 -2
  104. package/dist/pulse-delivery.js +5 -0
  105. package/dist/pulse-resposta.js +674 -49
  106. package/dist/pulse.js +21 -0
  107. package/dist/ratelimit.js +3 -2
  108. package/dist/retry.js +230 -9
  109. package/dist/roadmap-reservas.js +262 -20
  110. package/dist/roadmap-status.js +213 -0
  111. package/dist/runtime-ambiente.js +12 -1
  112. package/dist/session-watcher-claude.js +26 -0
  113. package/dist/sessoes.js +49 -15
  114. package/dist/thread-close.js +3 -0
  115. package/dist/verify.js +5 -4
  116. package/dist/worktree.js +77 -0
  117. package/eval/casos/orchestration-experience-pt-br.json +48 -0
  118. package/eval/casos/orchestration-experience.json +48 -0
  119. package/package.json +1 -1
  120. package/schemas/code-artifact-graph.v1.schema.json +581 -0
  121. package/schemas/graph-benchmark.v1.schema.json +2240 -0
  122. package/schemas/maestro-snapshot.schema.json +23 -0
  123. package/skills/README.md +7 -5
  124. package/skills/core/onboarding/SKILL.md +7 -0
  125. package/skills/core/orchestration-experience/SKILL.md +113 -0
  126. package/skills/core/orchestration-experience-pt-br/SKILL.md +112 -0
  127. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  128. package/skills/phases/go-implementation/SKILL.md +2 -0
package/dist/pulse.js CHANGED
@@ -1,6 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.JANELA_DAS_DECISOES_MS = exports.CONTRATO_PULSE = void 0;
4
+ exports.apresentacaoCurtaDoItem = apresentacaoCurtaDoItem;
4
5
  exports.comporPulse = comporPulse;
5
6
  exports.montarPulse = montarPulse;
6
7
  exports.textoDoPulse = textoDoPulse;
@@ -20,6 +21,7 @@ const util_1 = require("./util");
20
21
  const horario_1 = require("./horario");
21
22
  const hitl_presentation_1 = require("./hitl-presentation");
22
23
  const hitl_contract_1 = require("./hitl-contract");
24
+ const hitl_curto_1 = require("./hitl-curto");
23
25
  const decisao_autonoma_1 = require("./decisao-autonoma");
24
26
  const docs_1 = require("./docs");
25
27
  const conducao_1 = require("./conducao");
@@ -30,6 +32,18 @@ exports.JANELA_DAS_DECISOES_MS = 7 * 24 * 60 * 60 * 1000;
30
32
  const impacto = (fase) => types_1.FASES.indexOf(fase) + 1;
31
33
  const ordenar = (a, b) => (b.paradaHaMin ?? -1) - (a.paradaHaMin ?? -1) || b.impacto - a.impacto || a.id.localeCompare(b.id);
32
34
  const minutos = (desde, quando) => desde && Number.isFinite(Date.parse(desde)) ? Math.max(0, Math.floor((Date.parse(quando) - Date.parse(desde)) / 60000)) : null;
35
+ /**
36
+ * RM-048 (D1 e D4): o texto curto de uma pergunta v2 de gate e a linha com o codigo estavel.
37
+ * Pedido sem codigo (v1, sessao) continua com o comando de antes: nada e inventado para ele.
38
+ */
39
+ function apresentacaoCurtaDoItem(pedido, desde, quando) {
40
+ if (!(0, hitl_contract_1.ehV2)(pedido) || pedido.classe !== 'pergunta' || pedido.alvo.tipo !== 'gate')
41
+ return undefined;
42
+ const curto = (0, hitl_curto_1.montarPedidoCurto)((0, hitl_curto_1.entradaDoPedido)(pedido, desde ?? pedido.criadoEm), { quando, responder: { tipo: 'codigo', codigo: pedido.codigo } });
43
+ const recomendada = pedido.alternativas.find(a => a.recomendada)?.letra ?? 'a';
44
+ return { textos: { telegram: (0, hitl_curto_1.textoDoPedidoCurto)(curto, 'telegram'), terminal: (0, hitl_curto_1.textoDoPedidoCurto)(curto, 'terminal') },
45
+ comando: `${pedido.codigo} ${recomendada}` };
46
+ }
33
47
  function comporPulse(carregado, entrada) {
34
48
  const { radar, monitor, batch, orfas } = entrada, quando = radar.consultadoEm;
35
49
  const humanos = [], automaticas = [];
@@ -186,6 +200,13 @@ function comporPulse(carregado, entrada) {
186
200
  item.opcoes = (0, hitl_contract_1.escolhasDoPedido)(pedido).map(o => `${(0, hitl_contract_1.chaveDaEscolha)(pedido, o.numero)}. ${o.texto}`);
187
201
  item.recomendacao = (0, hitl_contract_1.recomendacaoDoPedido)(pedido);
188
202
  item.comandoResposta = `/ork ${(0, hitl_contract_1.alvoDoPedido)(pedido)?.tipo === 'session' ? 'session' : 'gate'} ${pedido.thread} ${pedido.id} <resposta>`;
203
+ // RM-048 (D1 e D4): a pergunta v2 de gate sai pelo contrato curto, e a linha que o dono
204
+ // recebe e o codigo estavel ("DE6H a"), nao o identificador que muda a cada renovacao.
205
+ const curta = apresentacaoCurtaDoItem(pedido, item.desdeEm, quando);
206
+ if (curta) {
207
+ item.apresentacaoCurta = curta.textos;
208
+ item.comandoResposta = curta.comando;
209
+ }
189
210
  // O Pulse nao e uma conexao MCP: nenhum canal mcp-local sai `disponivel` daqui.
190
211
  item.canais = (0, hitl_presentation_1.ofertaDoPedido)(pedido);
191
212
  }
package/dist/ratelimit.js CHANGED
@@ -144,7 +144,7 @@ function enfileirar(carregado, novo, agoraMs = Date.now()) {
144
144
  * ela, a janela padrao do manifesto (estimativa declarada, nunca chute). Auth ausente: sem prazo.
145
145
  */
146
146
  function prazoDaConta(manifesto, falha, agoraMs = Date.now()) {
147
- if (falha.motivo === 'runtime.auth-missing')
147
+ if (falha.motivo !== 'runtime.quota-exhausted')
148
148
  return null;
149
149
  // A10: horario dito que ja passou nao devolve o perfil ao rodizio na hora: vale a janela padrao.
150
150
  if (falha.resetEm !== null && Date.parse(falha.resetEm) > agoraMs)
@@ -153,7 +153,8 @@ function prazoDaConta(manifesto, falha, agoraMs = Date.now()) {
153
153
  }
154
154
  /** Marca no store o perfil que falhou pela conta. Sem perfil (ambiente do processo), nada a marcar. */
155
155
  function marcarContaDaFalha(carregado, perfil, falha, agoraMs = Date.now()) {
156
- if (!perfil)
156
+ // RM-037 (defeitosdeco D-6): modelo inacessivel nao e falha da conta inteira; o perfil fica no rodizio.
157
+ if (!perfil || falha.motivo === 'runtime.model-unavailable')
157
158
  return null;
158
159
  const esgotado = falha.motivo === 'runtime.quota-exhausted';
159
160
  return (0, runtime_profiles_1.marcarFalhaDePerfil)(carregado.raiz, perfil.id, { estado: esgotado ? 'esgotado' : 'sem-auth',
package/dist/retry.js CHANGED
@@ -93,6 +93,7 @@ const policies_1 = require("./policies");
93
93
  const ratelimit_1 = require("./ratelimit");
94
94
  const session_watcher_claude_1 = require("./session-watcher-claude");
95
95
  const runtime_profiles_1 = require("./runtime-profiles");
96
+ const modos_1 = require("./modos");
96
97
  const thread_1 = require("./thread");
97
98
  const tokens_1 = require("./tokens");
98
99
  const util_1 = require("./util");
@@ -241,6 +242,15 @@ exports.POLITICA_DE_RETRY = {
241
242
  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
243
  correcao: 'ork accounts check confere o login de cada perfil; ork retry run <thread> rotaciona o prompt ja gravado',
243
244
  },
245
+ // RM-037 (defeitosdeco D-6): o modelo e que falta, nao a conta. Repetir o mesmo modelo na mesma conta
246
+ // so repete a recusa; o perfil continua no rodizio para os outros modelos.
247
+ 'runtime.model-unavailable': {
248
+ motivo: 'runtime.model-unavailable',
249
+ acao: 'reexecutar',
250
+ automatica: true,
251
+ porque: 'o modelo pedido nao existe ou a conta nao tem acesso a ele: o MESMO prompt, com o mesmo sha256, segue no proximo perfil do mesmo runtime com o mesmo modelo e depois no fallback do bloco, nunca de novo no par que ja recusou',
252
+ correcao: 'ork retry run <thread> troca o destino; sem destino, ork setup <modo> --bloco N --model <modelo acessivel> ou --fallback runtime:modelo',
253
+ },
244
254
  'tree.blocked': {
245
255
  motivo: 'tree.blocked',
246
256
  acao: 'sincronizar-worktree',
@@ -472,10 +482,29 @@ function escalarEsforco(base, degraus) {
472
482
  * de provider pago enquanto a fase esperava, a retomada recusa em vez de gastar.
473
483
  */
474
484
  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);
485
+ const run = (retida) => redespacharSobLock(carregado, (0, thread_1.lerThread)(carregado.raiz, thread.id), fase, promptRelativo, sha, opcoes, retida);
486
+ if (opcoes.dryRun)
487
+ return run();
488
+ // RM-037 (defeito 1; A2, G1 e N3 do CHECK 1 e 2, B-1 e A-1r do CHECK 4): o fallback do bloco leva a
489
+ // retomada ao codex. O redespacho pede a baseline depois das guardas baratas, RETENDO a conducao tomada; ela
490
+ // e gravada aqui, fora do lock HITL, como reentrada dessa tomada, e a retomada repete uma vez com ela.
491
+ const r = (0, hitl_gates_1.comLockHitl)(carregado.raiz, thread.id, () => run());
492
+ const retida = r.retida;
493
+ if (!retida)
494
+ return r;
495
+ delete r.retida;
496
+ try {
497
+ (0, phase_1.garantirBaselineDoDespacho)(carregado, thread.id, { fase, prompt: '', runtime: opcoes.runtime ?? undefined,
498
+ model: opcoes.model ?? undefined, effort: opcoes.effort ?? undefined, ...(opcoes.canal ? { canal: opcoes.canal } : {}) }, retida.identidade);
499
+ return (0, hitl_gates_1.comLockHitl)(carregado.raiz, thread.id, () => run(retida));
500
+ }
501
+ finally {
502
+ retida.devolver();
503
+ }
477
504
  }
478
- function redespacharSobLock(carregado, thread, fase, promptRelativo, sha, opcoes = {}) {
505
+ function redespacharSobLock(carregado, thread, fase, promptRelativo, sha, opcoes = {},
506
+ /** A tomada que a primeira passada reteve para a baseline; com ela, esta e a repeticao. */
507
+ tomadaPrevia) {
479
508
  const { raiz, manifesto } = carregado;
480
509
  const absoluto = path.isAbsolute(promptRelativo)
481
510
  ? promptRelativo
@@ -526,13 +555,24 @@ function redespacharSobLock(carregado, thread, fase, promptRelativo, sha, opcoes
526
555
  // I-36 (T6): validado o pedido, a retomada toma a conducao antes de redespachar, como o
527
556
  // `ork phase run`. A mesma fase com o mesmo prompt de quem ja conduz nao abre sessao nova;
528
557
  // outro pedido espera a vez.
529
- const identidade = (0, runtime_context_1.novaIdentidadeDeDespacho)(thread.id, fase);
558
+ const identidade = tomadaPrevia ? { ...(0, runtime_context_1.novaIdentidadeDeDespacho)(thread.id, fase), dispatchId: tomadaPrevia.identidade }
559
+ : (0, runtime_context_1.novaIdentidadeDeDespacho)(thread.id, fase);
530
560
  const canal = opcoes.canal ?? (0, conducao_1.canalDoProcesso)();
531
561
  const doBlocoDaConducao = (0, setup_1.limitesDoBloco)(manifesto, (0, setup_1.lerSetup)(raiz), thread.modo, fase);
532
562
  const pedidoDeConducao = { canal, correlacao: null, operacao: 'retry.run', fase, promptSha256: sha,
533
563
  identidade: identidade.dispatchId, prazoMs: (0, phase_1.prazoDaSessao)(doBlocoDaConducao) };
534
564
  let conducao = null;
535
- if (!opcoes.dryRun) {
565
+ if (tomadaPrevia) {
566
+ // R5-B1 do CHECK 5: a sucedida que o dono respondeu durante a baseline recebe o lease de volta.
567
+ if (!tomadaPrevia.sucessaoAindaVale()) {
568
+ tomadaPrevia.devolver();
569
+ const recusa = (0, conducao_1.recusaDeConducao)(thread.id, (0, conducao_1.conducaoDaThread)(raiz, thread.id), pedidoDeConducao);
570
+ (0, conducao_1.registrarRecusa)(raiz, recusa);
571
+ return { ...vazio, ok: false, motivo: 'conducao.em-andamento', detalhe: recusa.texto, dryRun: false };
572
+ }
573
+ conducao = tomadaPrevia;
574
+ }
575
+ else if (!opcoes.dryRun) {
536
576
  const tomada = (0, conducao_1.tomarConducao)(raiz, thread.id, pedidoDeConducao);
537
577
  if (!tomada.ok) {
538
578
  if (tomada.idempotente && tomada.atual) {
@@ -561,6 +601,17 @@ function redespacharSobLock(carregado, thread, fase, promptRelativo, sha, opcoes
561
601
  effort: opcoes.effort ?? undefined,
562
602
  }, doBloco);
563
603
  const rt = (0, runtimes_1.resolverRuntime)(runtime);
604
+ if (!opcoes.dryRun && (0, phase_1.baselineDoDespachoNecessaria)(carregado, thread.id, { fase, prompt: '', runtime, model, effort })) {
605
+ // Na primeira passada a tomada fica retida para a baseline (o `finally` nao a solta); na repeticao, o
606
+ // `finally` devolve o que a tomada consumiu (A-1 do CHECK 3).
607
+ const pendente = { ...vazio, ok: false, motivo: 'runtime.unavailable', dryRun: false,
608
+ detalhe: `baseline.pendente: o bloco com GO no ${runtime} precisa da baseline antes da retomada` };
609
+ if (!tomadaPrevia && conducao) {
610
+ pendente.retida = conducao;
611
+ conducao = null;
612
+ }
613
+ return pendente;
614
+ }
564
615
  // D14 (GO-FIX 2): a retomada claude-bg abre com a mesma colaboração e o mesmo contexto de
565
616
  // runtime do `ork phase run`; sem eles, o PLAN retomado saía sem negar Edit, Write e
566
617
  // NotebookEdit e sem a allowlist do núcleo. O ramo Codex não muda.
@@ -605,7 +656,8 @@ function redespacharSobLock(carregado, thread, fase, promptRelativo, sha, opcoes
605
656
  logDir: path.join((0, thread_1.dirThread)(raiz, thread.id), 'sessoes'),
606
657
  vinculo: { thread: thread.id, fase, promptSha256: sha },
607
658
  ...(perfil ? { perfil } : {}),
608
- ...(runtime === 'claude-bg' ? { contextoRuntime, ...(fase === 'PLAN' ? { colaboracao: 'plan' } : {}) } : {}),
659
+ // RM-037 (defeito 2): o modo plano segue a mesma regra do `ork phase run`, pelo bloco.
660
+ ...(runtime === 'claude-bg' ? { contextoRuntime, ...((0, phase_1.modoDaSessaoDoBloco)(thread, fase).plano ? { colaboracao: 'plan' } : {}) } : {}),
609
661
  // I-36 (D4): a sessao retomada herda a identidade do despacho e o canal, como no `ork phase run`.
610
662
  ambienteExtra: (0, conducao_1.ambienteDaConducao)(identidade.dispatchId, thread.id, canal),
611
663
  });
@@ -685,8 +737,9 @@ function redespacharSobLock(carregado, thread, fase, promptRelativo, sha, opcoes
685
737
  };
686
738
  }
687
739
  finally {
688
- // Retomada que nao virou sessao devolve a conducao; a que virou ja a entregou a sessao.
689
- conducao?.liberar();
740
+ // Retomada que nao virou sessao devolve a conducao, com o que a tomada consumiu; a que virou ja a
741
+ // entregou a sessao, e devolver entao nao faz nada.
742
+ conducao?.devolver();
690
743
  }
691
744
  }
692
745
  // ---------------------------------------------------------------------------
@@ -1218,6 +1271,9 @@ function executarRetry(carregado, threadId, opcoes = {}) {
1218
1271
  if (plano.motivo === 'runtime.quota-exhausted' || plano.motivo === 'runtime.auth-missing') {
1219
1272
  return rotacionarConta(carregado, thread, plano, opcoes, vazio, registrarTentativa);
1220
1273
  }
1274
+ if (plano.motivo === 'runtime.model-unavailable') {
1275
+ return trocarDestinoDoModelo(carregado, thread, plano, opcoes, vazio, registrarTentativa);
1276
+ }
1221
1277
  // `reexecutar` e `escalar-esforco`: o MESMO prompt volta ao runtime.
1222
1278
  const falho = ultimoDespachoFalho(raiz, threadId, plano.fase, plano.motivo === 'runtime.silencio');
1223
1279
  if (!falho || typeof falho.promptPath !== 'string' || typeof falho.promptSha256 !== 'string') {
@@ -1337,7 +1393,10 @@ function rotacionarConta(carregado, thread, plano, opcoes, vazio, registrarTenta
1337
1393
  else
1338
1394
  implicitos.push(alvo.runtime);
1339
1395
  atual = { runtime: alvo.runtime, perfil: alvo.perfil };
1340
- falhaAtual = ultimo.falhaDeConta ?? falhaAtual;
1396
+ // GO-FIX (R4b): destino que recusou o MODELO sai da rotacao, mas a falha que manda (e o motivo da
1397
+ // fila) continua sendo a da conta; o modelo inacessivel tem troca propria (trocarDestinoDoModelo).
1398
+ if (ultimo.falhaDeConta?.motivo !== 'runtime.model-unavailable')
1399
+ falhaAtual = ultimo.falhaDeConta ?? falhaAtual;
1341
1400
  if (tentativas >= plano.limite) {
1342
1401
  return escalar('limite de tentativas na rotacao', `${tentativas} tentativa(s) de rotacao por ${motivo} em ${fase}, no limite de ` +
1343
1402
  `${plano.limite} do manifesto: escalacao tipada pausa qualquer modo`);
@@ -1369,6 +1428,168 @@ function rotacionarConta(carregado, thread, plano, opcoes, vazio, registrarTenta
1369
1428
  registrarTentativa(true, `fila duravel: pedido ${pedido.id} libera em ${pedido.liberaEm}`);
1370
1429
  return { ...vazio, executada: true, fila: [pedido], redespacho: ultimo, detalhe: `pedido ${pedido.id} na fila ate ${pedido.liberaEm}` };
1371
1430
  }
1431
+ /** RM-037 (defeitosdeco D-6): chave de um destino (runtime, perfil, modelo) que recusou o modelo. */
1432
+ function chaveDoModelo(runtime, perfil, model) {
1433
+ return `${runtime}|${perfil ?? ''}|${model ?? ''}`;
1434
+ }
1435
+ /**
1436
+ * D-6: todo destino que ja recusou o modelo nesta fase, lido do ledger. O gate do observador traz o
1437
+ * `sessionId` (o modelo vem do `phase_dispatch` dele); o do despacho sincrono, o ultimo
1438
+ * `phase_dispatch_failed` da fase antes dele.
1439
+ */
1440
+ function destinosQueRecusaramOModelo(eventos, fase) {
1441
+ const chaves = [];
1442
+ const chaveDo = (origem) => {
1443
+ if (typeof origem.runtime !== 'string')
1444
+ return null;
1445
+ let perfil = null;
1446
+ try {
1447
+ perfil = perfilDoEvento(origem);
1448
+ }
1449
+ catch { /* perfil ilegivel nao exclui ninguem */ }
1450
+ return chaveDoModelo(origem.runtime, perfil?.id ?? null, typeof origem.model === 'string' ? origem.model : null);
1451
+ };
1452
+ eventos.forEach((e, i) => {
1453
+ // GO-FIX (R4a): a recusa sincrona do redespacho (nada rodou) so grava `phase_dispatch_failed`.
1454
+ if (e.tipo === ledger_1.TIPOS_DE_EVENTO.despachoFalhou && e.fase === fase && String(e.erro ?? '').startsWith('runtime.model-unavailable')) {
1455
+ const chave = chaveDo(e);
1456
+ if (chave)
1457
+ chaves.push(chave);
1458
+ return;
1459
+ }
1460
+ if (e.tipo !== ledger_1.TIPOS_DE_EVENTO.gateBloqueado || e.fase !== fase || e.motivo !== 'runtime.model-unavailable')
1461
+ return;
1462
+ const origem = typeof e.sessionId === 'string'
1463
+ ? eventos.filter(d => d.tipo === ledger_1.TIPOS_DE_EVENTO.faseDespachada && d.sessionId === e.sessionId).at(-1)
1464
+ : eventos.slice(0, i).filter(d => d.tipo === ledger_1.TIPOS_DE_EVENTO.despachoFalhou && d.fase === fase).at(-1);
1465
+ const chave = origem ? chaveDo(origem) : null;
1466
+ if (chave)
1467
+ chaves.push(chave);
1468
+ });
1469
+ return chaves;
1470
+ }
1471
+ /**
1472
+ * D-6: o proximo destino do MESMO prompt depois de `runtime.model-unavailable`. Primeiro outro perfil do
1473
+ * mesmo runtime com o MESMO modelo (outra conta pode ter acesso), depois cada runtime da ordem de
1474
+ * fallback do bloco com o modelo dele. Perfil fora do rodizio (esgotado, sem login, desativado, pago)
1475
+ * nao entra, e nenhum destino que ja recusou volta. Sem destino, `null`.
1476
+ */
1477
+ function proximoDestinoDoModelo(raiz, runtime, model, fallback, recusados) {
1478
+ const store = (0, runtime_profiles_1.lerPerfisComContas)(raiz);
1479
+ const utilizavel = (p) => !['desativado', 'provider-pago', 'sem-auth'].includes(p.estado) && (0, runtime_profiles_1.perfilDisponivel)(p);
1480
+ const mesmo = (0, runtime_profiles_1.perfisDoRuntime)(store, runtime).find(p => utilizavel(p) && !recusados.has(chaveDoModelo(runtime, p.id, model)));
1481
+ if (mesmo)
1482
+ return { runtime, perfil: (0, runtime_profiles_1.perfilDeDespacho)(mesmo), model, effort: null };
1483
+ for (const f of fallback) {
1484
+ const perfis = (0, runtime_profiles_1.perfisDoRuntime)(store, f.runtime);
1485
+ if (perfis.length) {
1486
+ const p = perfis.find(x => utilizavel(x) && !recusados.has(chaveDoModelo(f.runtime, x.id, f.model)));
1487
+ if (p)
1488
+ return { runtime: f.runtime, perfil: (0, runtime_profiles_1.perfilDeDespacho)(p), model: f.model, effort: f.effort ?? null };
1489
+ continue;
1490
+ }
1491
+ if (!recusados.has(chaveDoModelo(f.runtime, null, f.model)))
1492
+ return { runtime: f.runtime, perfil: null, model: f.model, effort: f.effort ?? null };
1493
+ }
1494
+ return null;
1495
+ }
1496
+ /** D-6: a correcao exata quando nenhum destino tem o modelo: o bloco do setup que conduz a fase. */
1497
+ function correcaoDoModelo(thread, fase) {
1498
+ const indice = modos_1.MODOS[thread.modo]?.blocos.findIndex(b => b.fases.includes(fase)) ?? -1;
1499
+ const bloco = indice >= 0 ? String(indice + 1) : 'N';
1500
+ return `ork setup ${thread.modo} --bloco ${bloco} --model <modelo acessivel>, ou declare no mesmo bloco --fallback runtime:modelo`;
1501
+ }
1502
+ /**
1503
+ * RM-037 (defeitosdeco D-6): troca de destino depois de `runtime.model-unavailable`. O modelo pedido nao
1504
+ * existe ou a conta nao tem acesso a ele (`model_not_found` na transcricao); o perfil continua no
1505
+ * rodizio e o MESMO prompt segue no proximo destino (`proximoDestinoDoModelo`). Producao no intervalo
1506
+ * escala ao humano, como na rotacao por conta. Recusa do modelo no redespacho (nada rodou) continua a
1507
+ * troca na mesma chamada, dentro do limite de tentativas. Sem destino, o humano recebe a correcao exata.
1508
+ */
1509
+ function trocarDestinoDoModelo(carregado, thread, plano, opcoes, vazio, registrarTentativa) {
1510
+ const { raiz, manifesto } = carregado;
1511
+ const dir = (0, thread_1.dirThread)(raiz, thread.id);
1512
+ const fase = plano.fase;
1513
+ const motivo = 'runtime.model-unavailable';
1514
+ const eventos = (0, ledger_1.lerLedger)(dir);
1515
+ const gate = ultimaReprovacao(raiz, thread.id);
1516
+ const despacho = despachoDaFalha(eventos, gate, fase);
1517
+ const escalar = (razao, diagnostico) => {
1518
+ if (!opcoes.dryRun) {
1519
+ (0, gates_1.registrarGateBloqueado)(dir, thread.id, { gate: 'phase.dispatch', motivo: 'human.pending', modo: thread.modo, detalhe: diagnostico,
1520
+ diagnostico, correcao: `ork gate request ${thread.id}`, fase, origem: 'retry.modelo', pausaQualquerModo: true });
1521
+ (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.retryEscalado, { fase, motivo, origem: 'retry.modelo', acao: plano.acao,
1522
+ tentativas: plano.tentativas, limite: plano.limite, modo: thread.modo, razao });
1523
+ }
1524
+ return { ...vazio, detalhe: diagnostico };
1525
+ };
1526
+ if (!despacho || typeof despacho.promptPath !== 'string' || typeof despacho.promptSha256 !== 'string') {
1527
+ const detalhe = `nao ha despacho com prompt gravado para ${motivo} em ${fase}: rode a fase de novo com ork phase run`;
1528
+ registrarTentativa(false, detalhe);
1529
+ return { ...vazio, detalhe };
1530
+ }
1531
+ const runtime = typeof despacho.runtime === 'string' ? despacho.runtime : manifesto.runtime.adapter;
1532
+ const model = typeof despacho.model === 'string' ? despacho.model : null;
1533
+ const rotulo = model ?? '(padrao do runtime)';
1534
+ let perfil;
1535
+ try {
1536
+ perfil = typeof gate?.sessionId === 'string'
1537
+ ? (0, runtime_profiles_1.perfilDoRegistro)(eventos, { sessionId: gate.sessionId, despachadaEm: typeof gate.despachoEm === 'string' ? gate.despachoEm : undefined })
1538
+ : perfilDoEvento(despacho);
1539
+ }
1540
+ catch (e) {
1541
+ return escalar('perfil registrado invalido', `troca de destino recusada: ${e.message}`);
1542
+ }
1543
+ if (typeof gate?.sessionId === 'string' && typeof gate.despachoEm === 'string') {
1544
+ const producao = producaoNoIntervalo(raiz, thread, { sessionId: gate.sessionId, fase, despachadaEm: gate.despachoEm }, eventos);
1545
+ if (producao.produziu) {
1546
+ return escalar('producao parcial com modelo inacessivel', `producao parcial com o modelo ${rotulo} inacessivel: ${producao.evidencia}; ` +
1547
+ 'a sessao nao e redespachada sobre a worktree alterada e a fase nao conclui sem o humano');
1548
+ }
1549
+ }
1550
+ const recusados = new Set([...destinosQueRecusaramOModelo(eventos, fase), chaveDoModelo(runtime, perfil?.id ?? null, model)]);
1551
+ const doBloco = (0, setup_1.configDoBloco)((0, setup_1.lerSetup)(raiz), thread.modo, fase);
1552
+ const fallback = alvosDeFallback(doBloco, doBloco?.runtime ?? manifesto.runtime.adapter);
1553
+ let atual = { runtime, perfil, model };
1554
+ let tentativas = plano.tentativas;
1555
+ for (;;) {
1556
+ const alvo = proximoDestinoDoModelo(raiz, runtime, model, fallback, recusados);
1557
+ if (!alvo)
1558
+ break;
1559
+ if (!opcoes.dryRun)
1560
+ (0, ledger_1.registrar)(dir, thread.id, ledger_1.TIPOS_DE_EVENTO.perfilRotacionado, { fase, motivo, origem: 'retry.modelo',
1561
+ de: { runtime: atual.runtime, perfil: atual.perfil?.id ?? null, model: atual.model },
1562
+ para: { runtime: alvo.runtime, perfil: alvo.perfil?.id ?? null, model: alvo.model },
1563
+ razao: alvo.runtime === runtime ? 'outro perfil do mesmo runtime com o mesmo modelo' : 'fallback de runtime pela ordem do bloco',
1564
+ promptSha256: despacho.promptSha256, modo: thread.modo,
1565
+ autorizadoPor: `politica de retry da RM-037 (D-6), bloco sem pausa ou delegado no modo ${thread.modo}` });
1566
+ const mudouRuntime = alvo.runtime !== runtime;
1567
+ const r = redespachar(carregado, thread, fase, despacho.promptPath, despacho.promptSha256, {
1568
+ runtime: alvo.runtime, perfil: alvo.perfil?.id ?? null, model: alvo.model,
1569
+ effort: mudouRuntime ? alvo.effort ?? (typeof despacho.effort === 'string' ? despacho.effort : null) : plano.effort,
1570
+ origem: 'retry.modelo', dryRun: opcoes.dryRun,
1571
+ });
1572
+ if (opcoes.dryRun)
1573
+ return { ...vazio, redespacho: r, detalhe: `ensaio: o mesmo prompt iria a ${alvo.runtime}` +
1574
+ `${alvo.perfil ? ` (perfil ${alvo.perfil.id})` : ''} com o modelo ${alvo.model ?? '(padrao do runtime)'}; ${r.detalhe}` };
1575
+ registrarTentativa(r.ok, r.detalhe);
1576
+ tentativas += 1;
1577
+ if (r.ok)
1578
+ return { ...vazio, executada: true, redespacho: r, detalhe: r.detalhe };
1579
+ // So a recusa do MODELO no redespacho (nada rodou) continua a troca; outra falha para aqui.
1580
+ if (r.falhaDeConta?.motivo !== 'runtime.model-unavailable')
1581
+ return { ...vazio, redespacho: r, detalhe: r.detalhe };
1582
+ recusados.add(chaveDoModelo(alvo.runtime, alvo.perfil?.id ?? null, alvo.model));
1583
+ atual = { runtime: alvo.runtime, perfil: alvo.perfil, model: alvo.model };
1584
+ if (tentativas >= plano.limite) {
1585
+ return escalar('limite de tentativas na troca de modelo', `${tentativas} tentativa(s) de troca por ${motivo} em ${fase}, no limite de ` +
1586
+ `${plano.limite} do manifesto: escalacao tipada pausa qualquer modo`);
1587
+ }
1588
+ }
1589
+ const tentados = [...recusados].map(k => { const [rt, p, m] = k.split('|'); return `${rt}${p ? `/${p}` : ''}:${m || '(padrao)'}`; });
1590
+ return escalar('modelo inacessivel sem outro destino', `o modelo ${rotulo} nao esta acessivel em ${tentados.join(', ')} e o bloco ` +
1591
+ `nao tem outro destino: ${correcaoDoModelo(thread, fase)}`);
1592
+ }
1372
1593
  /** Texto de `ork retry plan`. */
1373
1594
  function textoDoPlano(p) {
1374
1595
  const linhas = [];