@orkastery/cli 0.5.1 → 0.5.6

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 (260) hide show
  1. package/adapters/claude-code/README.md +158 -0
  2. package/adapters/claude-code/agents/ork-master.md +2 -0
  3. package/adapters/claude-code/commands/master.md +8 -0
  4. package/adapters/claude-code/commands/ork.md +9 -0
  5. package/adapters/claude-code/mods/autoridade.json +56 -0
  6. package/adapters/claude-code/mods/ork-clima/.claude-plugin/plugin.json +14 -0
  7. package/adapters/claude-code/mods/ork-clima/hooks/contas.ts +283 -0
  8. package/adapters/claude-code/mods/ork-clima/hooks/hooks.json +3 -0
  9. package/adapters/claude-code/mods/ork-clima/hooks/previsao.ts +393 -0
  10. package/adapters/claude-code/mods/ork-clima/hooks/register.ts +205 -0
  11. package/adapters/claude-code/mods/ork-clima/orkastery-mod.json +5 -0
  12. package/adapters/claude-code/mods/ork-clima/types/index.d.ts +80 -0
  13. package/adapters/claude-code/mods/ork-freio/.claude-plugin/plugin.json +14 -0
  14. package/adapters/claude-code/mods/ork-freio/hooks/carga.ts +160 -0
  15. package/adapters/claude-code/mods/ork-freio/hooks/conferencia.ts +309 -0
  16. package/adapters/claude-code/mods/ork-freio/hooks/hooks.json +3 -0
  17. package/adapters/claude-code/mods/ork-freio/hooks/lote.ts +270 -0
  18. package/adapters/claude-code/mods/ork-freio/hooks/register.ts +285 -0
  19. package/adapters/claude-code/mods/ork-freio/orkastery-mod.json +5 -0
  20. package/adapters/claude-code/mods/ork-freio/types/index.d.ts +127 -0
  21. package/adapters/claude-code/mods/ork-mesa/.claude-plugin/plugin.json +14 -0
  22. package/adapters/claude-code/mods/ork-mesa/hooks/cartoes.tsx +246 -0
  23. package/adapters/claude-code/mods/ork-mesa/hooks/decidir.ts +110 -0
  24. package/adapters/claude-code/mods/ork-mesa/hooks/fila.ts +253 -0
  25. package/adapters/claude-code/mods/ork-mesa/hooks/hooks.json +3 -0
  26. package/adapters/claude-code/mods/ork-mesa/hooks/obvia.ts +98 -0
  27. package/adapters/claude-code/mods/ork-mesa/hooks/register.tsx +493 -0
  28. package/adapters/claude-code/mods/ork-mesa/orkastery-mod.json +5 -0
  29. package/adapters/claude-code/mods/ork-mesa/types/index.d.ts +131 -0
  30. package/adapters/claude-code/mods/ork-pista/.claude-plugin/plugin.json +14 -0
  31. package/adapters/claude-code/mods/ork-pista/hooks/acoes.ts +71 -0
  32. package/adapters/claude-code/mods/ork-pista/hooks/ci.ts +117 -0
  33. package/adapters/claude-code/mods/ork-pista/hooks/hooks.json +3 -0
  34. package/adapters/claude-code/mods/ork-pista/hooks/pista.tsx +356 -0
  35. package/adapters/claude-code/mods/ork-pista/hooks/register.tsx +439 -0
  36. package/adapters/claude-code/mods/ork-pista/orkastery-mod.json +5 -0
  37. package/adapters/claude-code/mods/ork-pista/types/index.d.ts +85 -0
  38. package/adapters/claude-code/mods/ork-ponte/.claude-plugin/plugin.json +13 -0
  39. package/adapters/claude-code/mods/ork-ponte/hooks/faixa.tsx +57 -0
  40. package/adapters/claude-code/mods/ork-ponte/hooks/guarda.ts +410 -0
  41. package/adapters/claude-code/mods/ork-ponte/hooks/hooks.json +3 -0
  42. package/adapters/claude-code/mods/ork-ponte/hooks/mostrado.ts +55 -0
  43. package/adapters/claude-code/mods/ork-ponte/hooks/nucleo.ts +157 -0
  44. package/adapters/claude-code/mods/ork-ponte/hooks/papel.ts +42 -0
  45. package/adapters/claude-code/mods/ork-ponte/hooks/register.ts +331 -0
  46. package/adapters/claude-code/mods/ork-ponte/hooks/vigia.ts +76 -0
  47. package/adapters/claude-code/mods/ork-ponte/orkastery-mod.json +5 -0
  48. package/adapters/claude-code/mods/ork-ponte/types/index.d.ts +178 -0
  49. package/adapters/claude-code/mods/ork-portao/.claude-plugin/plugin.json +14 -0
  50. package/adapters/claude-code/mods/ork-portao/hooks/hooks.json +3 -0
  51. package/adapters/claude-code/mods/ork-portao/hooks/lease.ts +201 -0
  52. package/adapters/claude-code/mods/ork-portao/hooks/plano.ts +304 -0
  53. package/adapters/claude-code/mods/ork-portao/hooks/register.ts +339 -0
  54. package/adapters/claude-code/mods/ork-portao/hooks/verify.ts +152 -0
  55. package/adapters/claude-code/mods/ork-portao/orkastery-mod.json +5 -0
  56. package/adapters/claude-code/mods/ork-portao/types/index.d.ts +60 -0
  57. package/adapters/claude-code/mods/ork-radar/.claude-plugin/plugin.json +14 -0
  58. package/adapters/claude-code/mods/ork-radar/hooks/colisao.ts +420 -0
  59. package/adapters/claude-code/mods/ork-radar/hooks/hooks.json +3 -0
  60. package/adapters/claude-code/mods/ork-radar/hooks/mapa.tsx +322 -0
  61. package/adapters/claude-code/mods/ork-radar/hooks/register.tsx +470 -0
  62. package/adapters/claude-code/mods/ork-radar/orkastery-mod.json +5 -0
  63. package/adapters/claude-code/mods/ork-radar/types/index.d.ts +72 -0
  64. package/adapters/claude-code/mods/ork-sentinela/.claude-plugin/plugin.json +14 -0
  65. package/adapters/claude-code/mods/ork-sentinela/hooks/alertas.ts +85 -0
  66. package/adapters/claude-code/mods/ork-sentinela/hooks/despacho.ts +156 -0
  67. package/adapters/claude-code/mods/ork-sentinela/hooks/hooks.json +3 -0
  68. package/adapters/claude-code/mods/ork-sentinela/hooks/register.ts +511 -0
  69. package/adapters/claude-code/mods/ork-sentinela/hooks/vigia.ts +186 -0
  70. package/adapters/claude-code/mods/ork-sentinela/orkastery-mod.json +5 -0
  71. package/adapters/claude-code/mods/ork-sentinela/types/index.d.ts +65 -0
  72. package/adapters/claude-code/mods/ork-torre/.claude-plugin/plugin.json +14 -0
  73. package/adapters/claude-code/mods/ork-torre/hooks/agenda.ts +60 -0
  74. package/adapters/claude-code/mods/ork-torre/hooks/detalhe.tsx +236 -0
  75. package/adapters/claude-code/mods/ork-torre/hooks/frases.ts +42 -0
  76. package/adapters/claude-code/mods/ork-torre/hooks/hooks.json +3 -0
  77. package/adapters/claude-code/mods/ork-torre/hooks/painel.tsx +344 -0
  78. package/adapters/claude-code/mods/ork-torre/hooks/rede.ts +97 -0
  79. package/adapters/claude-code/mods/ork-torre/hooks/register.tsx +437 -0
  80. package/adapters/claude-code/mods/ork-torre/orkastery-mod.json +5 -0
  81. package/adapters/claude-code/mods/ork-torre/types/index.d.ts +108 -0
  82. package/adapters/codex/skills/ork/SKILL.md +18 -3
  83. package/adapters/hermes/README.md +1 -0
  84. package/adapters/hermes/bin/ork-network-status.sh +10 -0
  85. package/adapters/hermes/hermes.plugin.json +1 -0
  86. package/adapters/hermes/hitl-ingress/__init__.py +2 -2
  87. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +7 -6
  88. package/adapters/openclaw/README.md +26 -5
  89. package/adapters/openclaw/dist/hitl-ingress.js +2 -2
  90. package/adapters/openclaw/dist/index.js +46 -11
  91. package/adapters/openclaw/openclaw.plugin.json +5 -1
  92. package/adapters/openclaw/package.json +2 -2
  93. package/adapters/openclaw/src/hitl-ingress.ts +2 -2
  94. package/adapters/openclaw/src/index.ts +49 -11
  95. package/assets/docs-en/markdownlint-cli2.jsonc +22 -0
  96. package/assets/docs-en/padroes/documentacao-de-produto.md +135 -0
  97. package/assets/docs-en/padroes/roadmap-de-produto.md +100 -0
  98. package/assets/docs-en/produto/README.md +14 -0
  99. package/assets/docs-en/produto/_modelo-feature.md +62 -0
  100. package/assets/docs-en/roadmap/README.md +14 -0
  101. package/assets/docs-en/roadmap/_modelo-item.md +75 -0
  102. package/assets/orkmind_bridge.py +117 -9
  103. package/dist/adapters/claude-bg.js +116 -18
  104. package/dist/adapters/codex.js +14 -4
  105. package/dist/agents-md.js +24 -14
  106. package/dist/auditrun.js +22 -3
  107. package/dist/board.js +20 -17
  108. package/dist/branch-de-estado.js +88 -10
  109. package/dist/busca-semantica.js +52 -12
  110. package/dist/canarios-rm057.js +149 -0
  111. package/dist/canarios.js +2 -0
  112. package/dist/catalogo.js +33 -0
  113. package/dist/ci.js +21 -8
  114. package/dist/claims-conferir.js +342 -0
  115. package/dist/claims.js +41 -1
  116. package/dist/company-brain-client.js +36 -1
  117. package/dist/company-brain-context.js +158 -22
  118. package/dist/company-brain-journal.js +2 -1
  119. package/dist/company-brain-migration.js +36 -1
  120. package/dist/conducao-sentinela.js +388 -0
  121. package/dist/conducao-texto.js +12 -18
  122. package/dist/conducao.js +122 -30
  123. package/dist/conhecimento.js +490 -0
  124. package/dist/docs.js +70 -17
  125. package/dist/doctor-pulse-cron.js +349 -0
  126. package/dist/doctor-rede.js +150 -0
  127. package/dist/doctor.js +195 -111
  128. package/dist/entrega-pr.js +42 -29
  129. package/dist/escopo-escrita.js +10 -1
  130. package/dist/esgotamentos.js +181 -0
  131. package/dist/estado-thread.js +23 -1
  132. package/dist/experiencia-instalacao.js +45 -25
  133. package/dist/experiencia.js +12 -2
  134. package/dist/fabrica-batida.js +310 -0
  135. package/dist/fabrica-estado.js +52 -14
  136. package/dist/fabrica-publicar.js +9 -1
  137. package/dist/fechamento.js +20 -66
  138. package/dist/fila-pesada.js +444 -0
  139. package/dist/fix.js +3 -2
  140. package/dist/forja.js +31 -7
  141. package/dist/gates.js +3 -24
  142. package/dist/handoff.js +10 -6
  143. package/dist/hitl-contract.js +59 -4
  144. package/dist/hitl-curto.js +11 -6
  145. package/dist/hitl-gates.js +5 -0
  146. package/dist/hitl-local-receipt.js +2 -0
  147. package/dist/hitl-lote.js +5 -2
  148. package/dist/hitl-resumo.js +38 -3
  149. package/dist/hitl-tempo-parado.js +201 -0
  150. package/dist/hitl-texto-livre.js +5 -5
  151. package/dist/hitl.js +29 -33
  152. package/dist/horario.js +41 -7
  153. package/dist/hosts.js +104 -11
  154. package/dist/impedimento.js +299 -0
  155. package/dist/index.js +961 -542
  156. package/dist/indice-vetorial.js +87 -10
  157. package/dist/indice.js +9 -1
  158. package/dist/init.js +56 -43
  159. package/dist/instabilidade.js +144 -0
  160. package/dist/integracoes-locais.js +4 -0
  161. package/dist/intelligence-graph-cli.js +170 -16
  162. package/dist/intelligence-graph-contexto.js +254 -0
  163. package/dist/intelligence-graph-contract.js +2 -1
  164. package/dist/intelligence-graph-extract-md.js +56 -4
  165. package/dist/intelligence-graph-extract-ts.js +71 -3
  166. package/dist/intelligence-graph-parsers.js +6 -4
  167. package/dist/intelligence-graph-query.js +66 -19
  168. package/dist/leases.js +664 -87
  169. package/dist/ledger-stats.js +15 -1
  170. package/dist/ledger.js +18 -1
  171. package/dist/licoes.js +1 -0
  172. package/dist/liveness.js +6 -3
  173. package/dist/locale-ajuda.js +761 -0
  174. package/dist/locale-gerados.js +160 -0
  175. package/dist/locale-master.js +99 -0
  176. package/dist/locale-onboarding.js +92 -0
  177. package/dist/locale-phase.js +151 -0
  178. package/dist/locale-pulse.js +211 -0
  179. package/dist/locale-retry.js +385 -0
  180. package/dist/locale-roadmap.js +102 -0
  181. package/dist/locale-ship.js +494 -0
  182. package/dist/locale-thread.js +277 -0
  183. package/dist/locale-verify.js +125 -0
  184. package/dist/locale.js +714 -0
  185. package/dist/maestro-runtime.js +4 -1
  186. package/dist/manifest.js +149 -4
  187. package/dist/maquina.js +23 -4
  188. package/dist/master-digest.js +6 -1
  189. package/dist/master-nota.js +18 -11
  190. package/dist/master.js +145 -31
  191. package/dist/mcp-artifacts.js +43 -3
  192. package/dist/mcp-git.js +42 -8
  193. package/dist/mcp-grafo-worker.js +5 -3
  194. package/dist/mcp-grafo.js +55 -6
  195. package/dist/mcp-server.js +22 -3
  196. package/dist/mcp-ship.js +3 -1
  197. package/dist/mcp-verify.js +6 -1
  198. package/dist/memoria.js +48 -6
  199. package/dist/modos.js +3 -6
  200. package/dist/network-roadmap.js +196 -18
  201. package/dist/noctum-arco.js +911 -0
  202. package/dist/noctum-capacidade.js +141 -0
  203. package/dist/noctum-envelope.js +810 -0
  204. package/dist/noctum-execucao.js +304 -0
  205. package/dist/noctum-fila.js +280 -0
  206. package/dist/ocupacao.js +40 -5
  207. package/dist/onboarding.js +40 -21
  208. package/dist/orkmind.js +99 -7
  209. package/dist/orquestracao.js +24 -21
  210. package/dist/parado-no-condutor.js +219 -38
  211. package/dist/phase.js +160 -52
  212. package/dist/policies.js +56 -5
  213. package/dist/portfolio.js +8 -3
  214. package/dist/postura-local.js +234 -0
  215. package/dist/preflight.js +11 -10
  216. package/dist/procedencia.js +225 -0
  217. package/dist/projeto-alvo.js +132 -5
  218. package/dist/prompts.js +40 -1
  219. package/dist/prova-ativacao.js +75 -6
  220. package/dist/pulse-delivery.js +47 -4
  221. package/dist/pulse.js +98 -19
  222. package/dist/ratelimit.js +24 -6
  223. package/dist/recall.js +3 -1
  224. package/dist/redacao-saida.js +63 -0
  225. package/dist/rede-adesao.js +399 -0
  226. package/dist/rede-forja.js +289 -0
  227. package/dist/rede-projetos.js +239 -0
  228. package/dist/rede-status.js +337 -0
  229. package/dist/rede.js +980 -0
  230. package/dist/retry.js +291 -140
  231. package/dist/roadmap-reservas.js +20 -3
  232. package/dist/roadmap-status.js +150 -34
  233. package/dist/runtime-context.js +181 -3
  234. package/dist/runtime-profiles.js +22 -1
  235. package/dist/runtimes.js +2 -1
  236. package/dist/saida-segura.js +58 -0
  237. package/dist/session-events.js +7 -3
  238. package/dist/session-watcher-claude.js +120 -24
  239. package/dist/session-watcher.js +185 -33
  240. package/dist/sessoes-contas.js +187 -0
  241. package/dist/sessoes-inventario.js +6 -18
  242. package/dist/sessoes.js +83 -2
  243. package/dist/ship-publicar.js +800 -0
  244. package/dist/ship.js +157 -88
  245. package/dist/steal.js +77 -0
  246. package/dist/thread-close.js +3 -2
  247. package/dist/thread.js +299 -49
  248. package/dist/util.js +8 -1
  249. package/dist/verify.js +141 -52
  250. package/dist/vida-da-thread.js +254 -0
  251. package/dist/worktree-local.js +165 -0
  252. package/dist/worktree.js +25 -2
  253. package/dist/yaml.js +27 -8
  254. package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
  255. package/package.json +7 -3
  256. package/references/definition-of-done.md +1 -1
  257. package/schemas/code-artifact-graph.v1.schema.json +2 -1
  258. package/skills/README.md +1 -1
  259. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  260. package/skills/phases/master-metrics/SKILL.md +8 -3
@@ -67,6 +67,7 @@ exports.perfisDoRuntime = perfisDoRuntime;
67
67
  exports.perfilDisponivel = perfilDisponivel;
68
68
  exports.politicaDeRotacao = politicaDeRotacao;
69
69
  exports.proximoPerfilDisponivel = proximoPerfilDisponivel;
70
+ exports.proximoPerfilPorCarga = proximoPerfilPorCarga;
70
71
  exports.prazoDoRuntime = prazoDoRuntime;
71
72
  exports.prazoDaFila = prazoDaFila;
72
73
  exports.menorEsgotadoAte = menorEsgotadoAte;
@@ -794,7 +795,8 @@ exports.POLITICA_PADRAO = { mesmoRuntimePorCota: true, mesmoRuntimePorAuth: true
794
795
  function politicaDeRotacao(manifesto) {
795
796
  const r = manifesto.runtime_profiles;
796
797
  return { mesmoRuntimePorCota: r?.rotate_same_runtime_on_quota !== false,
797
- mesmoRuntimePorAuth: r?.rotate_same_runtime_on_auth !== false };
798
+ mesmoRuntimePorAuth: r?.rotate_same_runtime_on_auth !== false,
799
+ ...(r?.distribuir === 'carga' ? { distribuir: 'carga' } : {}) };
798
800
  }
799
801
  /**
800
802
  * D14: o perfil da vez do runtime, na ordem do store. `desativado` e `provider-pago` nunca foram
@@ -819,6 +821,25 @@ function proximoPerfilDisponivel(store, runtime, politica, opcoes = {}) {
819
821
  }
820
822
  return null;
821
823
  }
824
+ /**
825
+ * RM-056 (D3): a escolha por carga. Respeita a politica da D14 (sem perfil da vez pela ordem, sem
826
+ * perfil nenhum: a mesma retencao do runtime), e entre os perfis disponiveis vence o de menor
827
+ * carga (sessoes vivas da conta nesta maquina); empate pelo `ultimoUso` mais antigo (nunca usado
828
+ * primeiro), depois a ordem do store. O `ultimoUso` e gravado no despacho, entao dois despachos
829
+ * seguidos com carga igual vao a perfis diferentes mesmo antes de o runtime listar a sessao.
830
+ */
831
+ function proximoPerfilPorCarga(store, runtime, politica, carga, opcoes = {}) {
832
+ const daVez = proximoPerfilDisponivel(store, runtime, politica, opcoes);
833
+ if (!daVez)
834
+ return null;
835
+ const agoraMs = opcoes.agoraMs ?? Date.now();
836
+ const candidatos = perfisDoRuntime(store, runtime)
837
+ .map((p, i) => ({ p, i }))
838
+ .filter(({ p }) => !(opcoes.excluir ?? []).includes(p.id) && perfilDisponivel(p, agoraMs));
839
+ const uso = (p) => p.ultimoUso === null ? -Infinity : Date.parse(p.ultimoUso);
840
+ candidatos.sort((x, y) => (carga.get(x.p.id) ?? 0) - (carga.get(y.p.id) ?? 0) || uso(x.p) - uso(y.p) || x.i - y.i);
841
+ return candidatos[0]?.p ?? daVez;
842
+ }
822
843
  /**
823
844
  * D14: quando o runtime volta a ter perfil da vez, pela mesma regra da escolha. Com a troca por
824
845
  * cota desligada, e o prazo do perfil que segura o runtime; ligada, o menor prazo entre os
package/dist/runtimes.js CHANGED
@@ -55,6 +55,7 @@ exports.resolverRuntime = resolverRuntime;
55
55
  const claudeBg = __importStar(require("./adapters/claude-bg"));
56
56
  const codex = __importStar(require("./adapters/codex"));
57
57
  const hitl_ingress_receipt_1 = require("./hitl-ingress-receipt");
58
+ const locale_1 = require("./locale");
58
59
  exports.RUNTIME_PADRAO = 'claude-bg';
59
60
  /** Phase e retry atravessam o mesmo preparo, antes de retirar segredos do filho. */
60
61
  function comRecibosPublicos(p, dispatch) {
@@ -97,7 +98,7 @@ function runtimeConhecido(nome) {
97
98
  function resolverRuntime(nome) {
98
99
  const adapter = exports.RUNTIMES[nome];
99
100
  if (!adapter) {
100
- throw new Error(`runtime de despacho desconhecido: "${nome}" (homologados: ${exports.ORDEM_DOS_RUNTIMES.join(', ')})`);
101
+ throw new locale_1.ErroDoCatalogo((m) => m.phase.runtimeDesconhecido(nome, exports.ORDEM_DOS_RUNTIMES.join(', ')));
101
102
  }
102
103
  return adapter;
103
104
  }
@@ -0,0 +1,58 @@
1
+ "use strict";
2
+ /**
3
+ * RM-053 (fatia 2): o saneador de saida comum ao nucleo.
4
+ *
5
+ * Texto que vem de fora (o retrato de outra maquina, a branch `ork/fabrica-estado` do remoto de um
6
+ * projeto, onde outras pessoas escrevem) chega a tela do dono. A rede ja limpava o que imprime desde
7
+ * a revisao 4; o `ork fabrica` imprimia o `por` e o `projeto` legados crus. Este modulo e a regra
8
+ * unica: quem imprime valor alheio passa por aqui.
9
+ */
10
+ Object.defineProperty(exports, "__esModule", { value: true });
11
+ exports.emUmaLinha = exports.INVISIVEL = exports.CF_VISIVEL = void 0;
12
+ exports.valoresEmUmaLinha = valoresEmUmaLinha;
13
+ exports.jsonSemInvisivel = jsonSemInvisivel;
14
+ /**
15
+ * Y4 do CHECK 7: as marcas de `Cf` que se veem, lista fechada (todo `Prepended_Concatenation_Mark=Yes`
16
+ * do Unicode 17: os sinais numericos arabes U+0600 a U+0605, o fim de aya U+06DD, a abreviacao siriaca
17
+ * U+070F, as marcas de libra e piastra U+0890 e U+0891, o sanah U+08E2 e as marcas de numero kaithi
18
+ * U+110BD e U+110CD). Elas tem glifo e nao movem o texto, ao contrario das bidi, de largura zero e das
19
+ * tags, que seguem invisiveis. O regex do JS nao aceita `\p{Prepended_Concatenation_Mark}`; por isso a
20
+ * lista literal, presa ao `INVISIVEL` e conferida ponto a ponto no teste.
21
+ */
22
+ exports.CF_VISIVEL = Object.freeze([
23
+ 0x0600, 0x0601, 0x0602, 0x0603, 0x0604, 0x0605, 0x06dd, 0x070f, 0x0890, 0x0891, 0x08e2, 0x110bd, 0x110cd,
24
+ ]);
25
+ /**
26
+ * V2 da revisao 4, W11 da revisao 5 e X3 da revisao 6: caractere que o terminal executa ou que
27
+ * ninguem ve, pelas classes do Unicode: controles (`Cc`: ESC, BEL, CSI, quebra de linha), formato
28
+ * (`Cf`: bidi, largura zero, tags), separadores de linha e todo `Default_Ignorable_Code_Point`
29
+ * (preenchimentos Hangul, seletores de variacao, os de musica e de estenografia), mais o braile
30
+ * vazio. Ficam de fora o ZWJ (U+200D) e os seletores U+FE0E e U+FE0F: eles montam emoji comuns, e as
31
+ * marcas visiveis de `Cf` (`CF_VISIVEL`, Y4 do CHECK 7).
32
+ */
33
+ exports.INVISIVEL = /(?![\u200d\ufe0e\ufe0f\u0600-\u0605\u06dd\u070f\u0890\u0891\u08e2\u{110bd}\u{110cd}])[\p{Cc}\p{Cf}\p{Zl}\p{Zp}\p{Default_Ignorable_Code_Point}\u2800]/u;
34
+ const INVISIVEIS = new RegExp(exports.INVISIVEL.source, 'gu');
35
+ /**
36
+ * W4 da revisao 5: o valor numa linha so, sem nada invisivel. A quebra de linha (e a tabulacao) vira
37
+ * espaco: um valor que vem de fora nunca abre uma linha propria na saida, como se fosse do `ork`.
38
+ */
39
+ const emUmaLinha = (texto) => texto.replace(INVISIVEIS, (c) => (/[\t\n\v\f\r\u0085\u2028\u2029]/.test(c) ? ' ' : ''));
40
+ exports.emUmaLinha = emUmaLinha;
41
+ /**
42
+ * Cada texto de uma estrutura (objeto, lista, aninhados) em uma linha, sem invisivel; numero,
43
+ * booleano e `null` ficam como estao (X5 da revisao 6: a ida e volta em JSON trocava `NaN` por `null`).
44
+ */
45
+ function valoresEmUmaLinha(valor) {
46
+ const limpar = (v) => typeof v === 'string' ? (0, exports.emUmaLinha)(v)
47
+ : Array.isArray(v) ? v.map(limpar)
48
+ : v && typeof v === 'object' ? Object.fromEntries(Object.entries(v).map(([k, x]) => [k, limpar(x)])) : v;
49
+ return limpar(valor);
50
+ }
51
+ /**
52
+ * O JSON para a tela: o mesmo valor, com os caracteres invisiveis escritos como `\uXXXX` (quem le o
53
+ * JSON recebe o mesmo texto; so o terminal deixa de executa-lo). A quebra de linha da indentacao fica.
54
+ */
55
+ function jsonSemInvisivel(valor) {
56
+ return JSON.stringify(valor, null, 2).replace(INVISIVEIS, (c) => c === '\n' ? c
57
+ : Array.from({ length: c.length }, (_, i) => `\\u${c.charCodeAt(i).toString(16).padStart(4, '0')}`).join(''));
58
+ }
@@ -63,10 +63,12 @@ function exigirSessaoDespachada(sessao, sessionId) {
63
63
  }
64
64
  return sessao;
65
65
  }
66
- function resolverSessao(raiz, sessionId) {
66
+ function resolverSessao(raiz, sessionId, threadId) {
67
67
  if (!/^[a-zA-Z0-9][a-zA-Z0-9_-]{0,127}$/.test(sessionId))
68
68
  throw new Error('sessionId inválido');
69
- const achadas = (0, thread_1.listarIds)(raiz).flatMap(id => {
69
+ // Um watcher já vinculado não depende da leitura de threads alheias a cada poll.
70
+ // O vínculo explícito continua exigindo uma única sessão e o estado canônico.
71
+ const achadas = (threadId === undefined ? (0, thread_1.listarIds)(raiz) : [threadId]).flatMap(id => {
70
72
  const thread = (0, thread_1.lerThread)(raiz, id);
71
73
  return thread.sessoes.filter(s => s.sessionId === sessionId).map(sessao => ({ thread, sessao }));
72
74
  });
@@ -75,8 +77,10 @@ function resolverSessao(raiz, sessionId) {
75
77
  const achada = achadas[0];
76
78
  if (achada.thread.worktree) {
77
79
  const estado = (0, estado_thread_1.auditarEstado)(raiz, achada.thread.id, achada.thread.worktree);
80
+ // comEstadoParaGit materializa o estado local durante commit/rebase. Não leia essa
81
+ // cópia: o watcher pode repetir a auditoria, com seu orçamento finito de falhas.
78
82
  if (estado.nivel !== 'ok')
79
- throw new Error(estado.detalhe);
83
+ throw Object.assign(new Error(estado.detalhe), { code: 'SESSION_STATE_SPLIT' });
80
84
  }
81
85
  return achada;
82
86
  }
@@ -38,6 +38,7 @@ exports.normalizarNativo = normalizarNativo;
38
38
  exports.stopCorrelacionado = stopCorrelacionado;
39
39
  exports.bloqueioPendente = bloqueioPendente;
40
40
  exports.classificarSessaoClaude = classificarSessaoClaude;
41
+ exports.ultimoErroDeApi = ultimoErroDeApi;
41
42
  exports.falhaDeContaDaTranscricao = falhaDeContaDaTranscricao;
42
43
  exports.comFalhaDeConta = comFalhaDeConta;
43
44
  exports.processoNativo = processoNativo;
@@ -74,6 +75,7 @@ const session_events_1 = require("./session-events");
74
75
  const ledger_1 = require("./ledger");
75
76
  const thread_1 = require("./thread");
76
77
  const util_1 = require("./util");
78
+ const procedencia_1 = require("./procedencia");
77
79
  const hitl_1 = require("./hitl");
78
80
  /** Uma consulta nativa custa cerca de 0,3 s; cinco segundos mantém o custo abaixo de 10%. */
79
81
  exports.INTERVALO_WATCH_CLAUDE_MS = 5000;
@@ -268,22 +270,27 @@ function textoDoErroDeApi(e) {
268
270
  const codigo = typeof e.error === 'string' ? e.error : '';
269
271
  return (0, hitl_1.redigirSegredos)([codigo, textos.filter(Boolean).join(' ')].filter(Boolean).join(': ')).slice(0, 4096);
270
272
  }
273
+ const UUID = /^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i;
274
+ /** A transcricao da sessao no diretorio do perfil (ou no implicito, sem perfil); `null` com sessao que nao e uuid. */
275
+ function arquivoDaTranscricao(perfil, cwd, sessionId) {
276
+ if (!UUID.test(sessionId))
277
+ return null;
278
+ return path.join((0, runtime_profiles_1.diretorioEfetivo)('claude-bg', perfil), 'projects', path.resolve(cwd).replace(/[^a-zA-Z0-9-]/g, '-'), `${sessionId}.jsonl`);
279
+ }
271
280
  /**
272
- * I-33 (D11b): falha de conta registrada pelo proprio Claude Code na transcricao da sessao, no
273
- * diretorio do perfil (ou no implicito, sem perfil). So entram linhas marcadas como erro de API
274
- * (`isApiErrorMessage` ou campo `error`): texto do agente nunca vira motivo. Leitura limitada
275
- * da cauda, sem seguir link; ausencia de transcricao e ausencia de evidencia. Vale o ULTIMO erro de API
276
- * (N3), o que encerrou a sessao. O codigo `rate_limit`
277
- * do Claude Code cobre tanto o limite do plano quanto o 429 transitorio ("Request rejected (429)",
278
- * "not your usage limit"): quem separa e o criterio unico da D16 (`naturezaDoLimite`), pela frase.
281
+ * I-33 (D11b): o erro de API que o proprio Claude Code gravou na transcricao da sessao. So entram linhas marcadas
282
+ * como erro de API (`isApiErrorMessage` ou campo `error`): texto do agente nunca vira motivo. Leitura limitada da
283
+ * cauda, sem seguir link; ausencia de transcricao e ausencia de evidencia. Vale o ULTIMO erro de API (N3), o que
284
+ * encerrou a sessao. RM-037 (fatia 5): com ele vem o instante da linha, que e a ancora da hora dita.
279
285
  */
280
- function falhaDeContaDaTranscricao(perfil, cwd, sessionId, agoraMs = Date.now()) {
281
- if (!/^[0-9a-f]{8}(?:-[0-9a-f]{4}){3}-[0-9a-f]{12}$/i.test(sessionId))
286
+ function ultimoErroDeApi(perfil, cwd, sessionId) {
287
+ const arquivo = arquivoDaTranscricao(perfil, cwd, sessionId);
288
+ if (!arquivo)
282
289
  return null;
283
- const arquivo = path.join((0, runtime_profiles_1.diretorioEfetivo)('claude-bg', perfil), 'projects', path.resolve(cwd).replace(/[^a-zA-Z0-9-]/g, '-'), `${sessionId}.jsonl`);
284
290
  let fd;
291
+ // O_NONBLOCK: um FIFO no lugar da transcricao nao trava o observador no open (sugestao da rodada 1 do CHECK).
285
292
  try {
286
- fd = fs.openSync(arquivo, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
293
+ fd = fs.openSync(arquivo, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
287
294
  }
288
295
  catch {
289
296
  return null;
@@ -300,7 +307,7 @@ function falhaDeContaDaTranscricao(perfil, cwd, sessionId, agoraMs = Date.now())
300
307
  finally {
301
308
  fs.closeSync(fd);
302
309
  }
303
- const erros = [];
310
+ let ultimo = null;
304
311
  for (const linha of cauda.split('\n')) {
305
312
  let e;
306
313
  try {
@@ -311,14 +318,82 @@ function falhaDeContaDaTranscricao(perfil, cwd, sessionId, agoraMs = Date.now())
311
318
  }
312
319
  if (!e || typeof e !== 'object' || (e.isApiErrorMessage !== true && e.error === undefined))
313
320
  continue;
314
- erros.push(textoDoErroDeApi(e));
321
+ const ms = typeof e.timestamp === 'string' ? Date.parse(e.timestamp) : NaN;
322
+ ultimo = { texto: textoDoErroDeApi(e), em: Number.isFinite(ms) ? new Date(ms).toISOString() : null,
323
+ uuid: typeof e.uuid === 'string' && UUID.test(e.uuid) ? e.uuid.toLowerCase() : null };
315
324
  }
316
- if (!erros.length)
317
- return null;
318
- // I-33 (N3): o criterio roda sobre o erro que encerrou a sessao (o ultimo erro de API), nunca sobre os
319
- // anteriores juntos: um 429 transitorio do meio nao esconde o limite que matou a sessao, e um
320
- // esgotamento ja superado nao tira o perfil do rodizio por causa de um 429 transitorio no fim.
321
- return (0, claude_bg_1.parseFalhaDeConta)(erros[erros.length - 1], agoraMs);
325
+ return ultimo;
326
+ }
327
+ /**
328
+ * I-33 (D11b): falha de conta registrada pelo Claude Code na transcricao da sessao. O codigo `rate_limit` do
329
+ * Claude Code cobre tanto o limite do plano quanto o 429 transitorio ("Request rejected (429)", "not your usage
330
+ * limit"): quem separa e o criterio unico da D16 (`naturezaDoLimite`), pela frase. I-33 (N3): o criterio roda
331
+ * sobre o erro que encerrou a sessao, nunca sobre os anteriores juntos. RM-037 (fatia 5): a hora dita e ancorada
332
+ * no instante da mensagem; sem instante na linha, no relogio de quem le, como antes.
333
+ */
334
+ function falhaDeContaDaTranscricao(perfil, cwd, sessionId, agoraMs = Date.now()) {
335
+ return falhaDoErro(ultimoErroDeApi(perfil, cwd, sessionId), agoraMs);
336
+ }
337
+ function falhaDoErro(erro, agoraMs) {
338
+ return erro ? (0, claude_bg_1.parseFalhaDeConta)(erro.texto, erro.em ? Date.parse(erro.em) : agoraMs) : null;
339
+ }
340
+ /**
341
+ * RM-037 (fatia 5): a cota vista com a sessao viva. Em 02/10/2026 as sessoes de uma conta pararam as 06:00Z no
342
+ * limite de gasto ("your session limit resets 4:40am (<fuso>)"), sem Stop e com o processo vivo por mais uma
343
+ * hora; o perfil so saiu do rodizio as 07:15Z, quando o processo morreu, e o redespacho das 06:14Z caiu na mesma
344
+ * conta. Aqui, a cada volta com a transcricao maior que na anterior, o ultimo erro de API depois do despacho passa
345
+ * pelo criterio unico da D16; esgotamento com prazo futuro marca o perfil ate a hora dita (ou a janela padrao
346
+ * contada da mensagem) e grava `runtime_quota_detected` uma vez por erro. A fase nao fecha e a conducao nao muda:
347
+ * a sessao continua viva, e o fecho segue o caminho de sempre, sem marcar de novo o mesmo erro.
348
+ */
349
+ function cotaAoVivo(ctx, perfil, cwd, cursor, erroDaTranscricao, gravar) {
350
+ const { sessao, dir } = ctx;
351
+ const arquivo = arquivoDaTranscricao(perfil, cwd, sessao.sessionId);
352
+ let tamanho;
353
+ try {
354
+ const st = arquivo ? fs.lstatSync(arquivo) : null;
355
+ // Link, FIFO ou diretorio no lugar da transcricao nao e transcricao: nada a ler.
356
+ tamanho = st?.isFile() ? st.size : NaN;
357
+ }
358
+ catch {
359
+ return;
360
+ }
361
+ if (!Number.isFinite(tamanho) || cursor.transcricaoBytes === tamanho)
362
+ return;
363
+ cursor.transcricaoBytes = tamanho;
364
+ const erro = erroDaTranscricao();
365
+ const erroMs = erro?.em ? Date.parse(erro.em) : NaN;
366
+ // Linha sem instante, ou de antes deste despacho (sessao retomada), nao prova que a cota e deste turno.
367
+ if (!erro || !Number.isFinite(erroMs) || erroMs < Date.parse(sessao.despachadaEm))
368
+ return;
369
+ const falha = falhaDoErro(erro, ctx.agoraMs);
370
+ if (falha?.motivo !== 'runtime.quota-exhausted' || !ctx.carregado)
371
+ return;
372
+ const esgotadoAte = (0, ratelimit_1.prazoDaCotaVista)(ctx.carregado.manifesto, falha, erroMs, ctx.agoraMs);
373
+ if (!esgotadoAte)
374
+ return;
375
+ // A chave do erro: o uuid da linha ou, sem ele, o instante e o trecho (dado de maquina, nunca texto).
376
+ const sensorEventId = (0, node_crypto_1.createHash)('sha256')
377
+ .update(JSON.stringify([sessao.sessionId, sessao.despachadaEm, erro.uuid ?? [erro.em, falha.trecho]])).digest('hex');
378
+ (0, session_events_1.comLockDeSessao)(dir, () => {
379
+ if ((0, ledger_1.lerLedger)(dir).some(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.cotaVistaNaTranscricao && e.sensorEventId === sensorEventId))
380
+ return;
381
+ if (perfil) {
382
+ try {
383
+ (0, ratelimit_1.marcarContaDaFalha)(ctx.carregado, perfil, falha, ctx.agoraMs, esgotadoAte);
384
+ }
385
+ catch {
386
+ // Store ocupado ou invalido: a proxima volta le a transcricao de novo e tenta outra vez.
387
+ delete cursor.transcricaoBytes;
388
+ return;
389
+ }
390
+ }
391
+ gravar(ledger_1.TIPOS_DE_EVENTO.cotaVistaNaTranscricao, { fase: sessao.fase, sessionId: sessao.sessionId, despachoEm: sessao.despachadaEm,
392
+ runtime: 'claude-bg', sensorEventId, motivo: falha.motivo, erroEm: erro.em, resetEm: falha.resetEm, fonteDoPrazo: falha.fonte,
393
+ // O trecho ja vem redigido e numa linha; sem controle, como no store de perfis.
394
+ esgotadoAte, trecho: falha.trecho.replace(/\p{Cc}/gu, ' '), perfil: perfil ? { id: perfil.id, runtime: perfil.runtime } : null,
395
+ perfilMarcado: !!perfil, origem: 'sessions.watch' });
396
+ });
322
397
  }
323
398
  /**
324
399
  * I-33 (D11): precedencia entre a prova da I-34 e a falha de conta. Com Stop correlacionado vale
@@ -360,8 +435,10 @@ function estadoDoArtefato(raiz, threadId, arquivo) {
360
435
  const file = path.join((0, thread_1.dirThread)(raiz, threadId), arquivo);
361
436
  const vazio = { sha256: null, mtimeMs: null, texto: null };
362
437
  let fd;
438
+ // RM-037 (fatia 5, sugestao da rodada 2 do CHECK): O_NONBLOCK, como na transcricao, para um FIFO no lugar do
439
+ // artefato nao travar o observador no open; o `isFile` abaixo o recusa.
363
440
  try {
364
- fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
441
+ fd = fs.openSync(file, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW | fs.constants.O_NONBLOCK);
365
442
  }
366
443
  catch (erro) {
367
444
  return erro.code === 'ENOENT' ? vazio : { ...vazio, invalido: true };
@@ -470,6 +547,9 @@ function provaDoOrk(raiz, threadId, sessao, eventos, fonte) {
470
547
  const cwd = fonte?.cwd, headNoDespacho = fonte?.head;
471
548
  if (!cwd || typeof headNoDespacho !== 'string')
472
549
  return falta('HEAD da worktree no despacho não registrado; commit não conferível', { commits: [], recusados: registrados });
550
+ // RM-047 (P2): o git da prova só roda na worktree registrada; o cwd vem do ledger.
551
+ if ((0, procedencia_1.cwdLocalOuNulo)(raiz, threadId, cwd, 'ledger') === null)
552
+ return falta('cwd do despacho fora das worktrees registradas no git; commit não conferível', { commits: [], recusados: registrados });
473
553
  const commits = registrados.filter(sha => commitConferido(cwd, sha, headNoDespacho));
474
554
  const recusados = registrados.filter(sha => !commits.includes(sha));
475
555
  if (!commits.length)
@@ -546,7 +626,9 @@ function garantirFonteClaude(raiz, thread, sessao) {
546
626
  if (atual)
547
627
  return atual;
548
628
  const despacho = eventos.filter(e => e.tipo === 'phase_dispatch' && e.sessionId === sessao.sessionId).at(-1);
549
- const cwd = typeof despacho?.cwd === 'string' ? despacho.cwd : thread.worktree ?? raiz;
629
+ // RM-047 (P2): o cwd do ledger ou do thread.json so vale como worktree registrada no git.
630
+ const cwd = typeof despacho?.cwd === 'string' ? (0, procedencia_1.exigirCwdLocal)(raiz, thread.id, despacho.cwd, 'ledger')
631
+ : (0, procedencia_1.diretorioDaThread)(raiz, thread);
550
632
  const fonte = { nativo: 'claude-agents', cwd, origem: 'sessions.watch' };
551
633
  validarFonteClaude(eventos, sessao.sessionId, fonte);
552
634
  if (!(0, ledger_1.registrarSeExiste)(dir, thread.id, 'session_sensor_registered', { fase: sessao.fase, sessionId: sessao.sessionId,
@@ -600,9 +682,13 @@ function observarClaudeSobLock(ctx) {
600
682
  const registro = consulta.ok ? consulta.registros.find(s => s.sessionId === sessao.sessionId && s.cwd === fonte.cwd) ?? null : null;
601
683
  const artefato = saidaDoArtefato(raiz, thread.id, sessao, fonte, eventos);
602
684
  let prova;
685
+ // RM-037 (fatia 5): o ultimo erro de API da transcricao, lido no maximo uma vez por volta, serve a cota ao vivo
686
+ // e ao fecho.
687
+ let erroLido;
688
+ const erroDaTranscricao = () => erroLido !== undefined ? erroLido : (erroLido = ultimoErroDeApi(perfil, fonte.cwd, sessao.sessionId));
603
689
  const { c, falha } = comFalhaDeConta(classificarSessaoClaude({ sessao, eventos, consulta, registro, cursor, agoraMs: ctx.agoraMs,
604
690
  limiteMs: ctx.limiteMs, pausaAoFim: (0, thread_1.pausaNaThread)(thread, sessao.fase),
605
- prova: () => prova ??= provaDoOrk(raiz, thread.id, sessao, eventos, fonte), processo: processoNativo }), () => falhaDeContaDaTranscricao(perfil, fonte.cwd, sessao.sessionId, ctx.agoraMs));
691
+ prova: () => prova ??= provaDoOrk(raiz, thread.id, sessao, eventos, fonte), processo: processoNativo }), () => falhaDoErro(erroDaTranscricao(), ctx.agoraMs));
606
692
  (0, codex_runner_1.gravarAtomico)(arquivoCursor, cursor);
607
693
  // GO-FIX 3 (achado 1): cursor gravado numa thread que sumiu durante a consulta é desfeito aqui;
608
694
  // as pastas criadas pela observação, pelo `session-watcher`.
@@ -610,6 +696,12 @@ function observarClaudeSobLock(ctx) {
610
696
  fs.rmSync(arquivoCursor, { force: true });
611
697
  throw new Error(exports.THREAD_REMOVIDA);
612
698
  }
699
+ if (!c.terminal) {
700
+ const lidos = cursor.transcricaoBytes;
701
+ cotaAoVivo(ctx, perfil, fonte.cwd, cursor, erroDaTranscricao, gravar);
702
+ if (cursor.transcricaoBytes !== lidos)
703
+ (0, codex_runner_1.gravarAtomico)(arquivoCursor, cursor);
704
+ }
613
705
  if (!c.terminal && c.inconclusivo)
614
706
  return (0, session_events_1.comLockDeSessao)(dir, () => {
615
707
  const diagnosticoId = `${resultadoId}:inconclusivo`;
@@ -649,8 +741,12 @@ function observarClaudeSobLock(ctx) {
649
741
  observadoEm, gate: 'phase.dispatch', origem: 'sessions.watch' };
650
742
  if (!atuais.some(e => e.tipo === c.classificacao && e.sensorResultId === resultadoId))
651
743
  gravar(c.classificacao, dados);
652
- // I-33 (D12): o perfil sai do rodizio na primeira gravacao do resultado, onde a evidencia esta.
653
- if (falha && perfil && ctx.carregado && !atuais.some(e => e.tipo === 'phase_result' && e.sensorResultId === resultadoId)) {
744
+ // I-33 (D12): o perfil sai do rodizio na primeira gravacao do resultado, onde a evidencia esta. RM-037 (fatia 5):
745
+ // o erro que a cota ao vivo ja marcou nao marca de novo; depois do prazo dito, marcar de novo tiraria do
746
+ // rodizio uma conta que ja voltou.
747
+ const jaVista = !!erroLido?.em && atuais.some(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.cotaVistaNaTranscricao &&
748
+ e.sessionId === sessao.sessionId && e.despachoEm === sessao.despachadaEm && e.erroEm === erroLido.em);
749
+ if (falha && perfil && ctx.carregado && !jaVista && !atuais.some(e => e.tipo === 'phase_result' && e.sensorResultId === resultadoId)) {
654
750
  try {
655
751
  (0, ratelimit_1.marcarContaDaFalha)(ctx.carregado, perfil, falha, ctx.agoraMs);
656
752
  }