@orkastery/cli 0.5.1 → 0.5.3

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 (110) hide show
  1. package/adapters/claude-code/agents/ork-master.md +2 -0
  2. package/adapters/claude-code/commands/master.md +8 -0
  3. package/adapters/claude-code/commands/ork.md +9 -0
  4. package/adapters/codex/skills/ork/SKILL.md +18 -3
  5. package/adapters/hermes/README.md +1 -0
  6. package/adapters/hermes/bin/ork-network-status.sh +10 -0
  7. package/adapters/hermes/hermes.plugin.json +1 -0
  8. package/adapters/hermes/hitl-ingress/__init__.py +2 -2
  9. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +6 -5
  10. package/adapters/openclaw/README.md +24 -4
  11. package/adapters/openclaw/dist/hitl-ingress.js +2 -2
  12. package/adapters/openclaw/dist/index.js +46 -11
  13. package/adapters/openclaw/openclaw.plugin.json +5 -1
  14. package/adapters/openclaw/package.json +2 -2
  15. package/adapters/openclaw/src/hitl-ingress.ts +2 -2
  16. package/adapters/openclaw/src/index.ts +49 -11
  17. package/assets/orkmind_bridge.py +117 -9
  18. package/dist/adapters/claude-bg.js +39 -5
  19. package/dist/adapters/codex.js +4 -3
  20. package/dist/agents-md.js +1 -0
  21. package/dist/auditrun.js +5 -1
  22. package/dist/board.js +16 -14
  23. package/dist/branch-de-estado.js +66 -10
  24. package/dist/busca-semantica.js +52 -12
  25. package/dist/canarios-rm057.js +149 -0
  26. package/dist/canarios.js +2 -0
  27. package/dist/ci.js +8 -1
  28. package/dist/claims.js +39 -1
  29. package/dist/company-brain-context.js +158 -22
  30. package/dist/docs.js +48 -9
  31. package/dist/doctor-pulse-cron.js +194 -0
  32. package/dist/doctor-rede.js +149 -0
  33. package/dist/doctor.js +89 -9
  34. package/dist/entrega-pr.js +20 -8
  35. package/dist/esgotamentos.js +181 -0
  36. package/dist/experiencia.js +12 -2
  37. package/dist/fabrica-estado.js +14 -6
  38. package/dist/fabrica-publicar.js +6 -0
  39. package/dist/fechamento.js +20 -66
  40. package/dist/forja.js +31 -7
  41. package/dist/gates.js +5 -1
  42. package/dist/hitl-contract.js +59 -4
  43. package/dist/hitl-curto.js +11 -6
  44. package/dist/hitl-gates.js +5 -0
  45. package/dist/hitl-lote.js +5 -2
  46. package/dist/hitl-resumo.js +9 -0
  47. package/dist/hitl-tempo-parado.js +201 -0
  48. package/dist/hitl-texto-livre.js +5 -5
  49. package/dist/horario.js +27 -0
  50. package/dist/hosts.js +52 -10
  51. package/dist/impedimento.js +205 -0
  52. package/dist/index.js +391 -74
  53. package/dist/indice-vetorial.js +87 -10
  54. package/dist/indice.js +3 -1
  55. package/dist/init.js +17 -0
  56. package/dist/integracoes-locais.js +2 -0
  57. package/dist/intelligence-graph-cli.js +91 -8
  58. package/dist/intelligence-graph-contexto.js +215 -0
  59. package/dist/intelligence-graph-parsers.js +6 -4
  60. package/dist/leases.js +664 -87
  61. package/dist/ledger-stats.js +15 -1
  62. package/dist/ledger.js +3 -0
  63. package/dist/licoes.js +1 -0
  64. package/dist/maestro-runtime.js +4 -1
  65. package/dist/manifest.js +20 -1
  66. package/dist/master-nota.js +14 -5
  67. package/dist/master.js +129 -12
  68. package/dist/mcp-artifacts.js +34 -3
  69. package/dist/mcp-git.js +20 -3
  70. package/dist/mcp-grafo-worker.js +5 -3
  71. package/dist/mcp-grafo.js +50 -4
  72. package/dist/mcp-server.js +14 -1
  73. package/dist/memoria.js +48 -6
  74. package/dist/network-roadmap.js +191 -16
  75. package/dist/ocupacao.js +11 -3
  76. package/dist/orkmind.js +90 -5
  77. package/dist/orquestracao.js +12 -10
  78. package/dist/parado-no-condutor.js +188 -32
  79. package/dist/phase.js +68 -15
  80. package/dist/policies.js +52 -1
  81. package/dist/portfolio.js +4 -1
  82. package/dist/projeto-alvo.js +5 -1
  83. package/dist/prompts.js +36 -1
  84. package/dist/prova-ativacao.js +75 -6
  85. package/dist/pulse-delivery.js +34 -2
  86. package/dist/pulse.js +49 -3
  87. package/dist/ratelimit.js +20 -4
  88. package/dist/recall.js +3 -1
  89. package/dist/rede-adesao.js +399 -0
  90. package/dist/rede-forja.js +289 -0
  91. package/dist/rede-projetos.js +239 -0
  92. package/dist/rede-status.js +336 -0
  93. package/dist/rede.js +980 -0
  94. package/dist/retry.js +69 -2
  95. package/dist/roadmap-status.js +23 -3
  96. package/dist/runtime-profiles.js +22 -1
  97. package/dist/saida-segura.js +58 -0
  98. package/dist/session-watcher-claude.js +113 -23
  99. package/dist/sessoes-contas.js +187 -0
  100. package/dist/sessoes-inventario.js +6 -18
  101. package/dist/sessoes.js +83 -2
  102. package/dist/ship.js +85 -19
  103. package/dist/thread.js +192 -13
  104. package/dist/worktree.js +5 -2
  105. package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
  106. package/package.json +7 -3
  107. package/references/definition-of-done.md +1 -1
  108. package/skills/README.md +1 -1
  109. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  110. package/skills/phases/master-metrics/SKILL.md +8 -3
package/dist/policies.js CHANGED
@@ -11,8 +11,9 @@
11
11
  * ela volta na lista de desconhecidas, para o `doctor` e o CLI dizerem que ela nao vale.
12
12
  */
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
- exports.POLICIES_CONHECIDAS = void 0;
14
+ exports.POLICIES_DE_PROVA_LOCAL = exports.POLICIES_CONHECIDAS = void 0;
15
15
  exports.procurarSegredos = procurarSegredos;
16
+ exports.policyDeProvaLocal = policyDeProvaLocal;
16
17
  exports.policiesDesconhecidas = policiesDesconhecidas;
17
18
  exports.avaliarPolicies = avaliarPolicies;
18
19
  exports.avisos = avisos;
@@ -95,7 +96,28 @@ exports.POLICIES_CONHECIDAS = {
95
96
  quando: ['ship'],
96
97
  descricao: 'avisa quando a branch da thread esta atras da base',
97
98
  },
99
+ // RM-008 (B8): a licao `claims.failed` ("rode o comando da claim antes de registra-la"). Desligada por
100
+ // padrao; declarada, o `ork claims add` roda o comando uma vez no prazo do verify. So avisa, nunca para.
101
+ claim_sem_prova_local: {
102
+ quando: ['claims.add'],
103
+ descricao: 'avisa quando o comando da claim reprova (ou estoura o prazo do verify) no registro',
104
+ },
105
+ claims_failed: {
106
+ quando: ['claims.add'],
107
+ descricao: 'a mesma conferencia de claim_sem_prova_local, com o nome que o ork licoes propoe',
108
+ },
98
109
  };
110
+ /** RM-008 (B8): as policies que conferem a claim no registro (um aviso so, como verify_regression e verify_failed). */
111
+ exports.POLICIES_DE_PROVA_LOCAL = ['claim_sem_prova_local', 'claims_failed'];
112
+ /** A primeira policy de prova local declarada fora de `off`, ou `null` (o `claims add` nao roda nada). */
113
+ function policyDeProvaLocal(manifesto) {
114
+ const declaradas = manifesto.policies ?? {};
115
+ for (const [nome, bruta] of Object.entries(declaradas)) {
116
+ if (exports.POLICIES_DE_PROVA_LOCAL.includes(nome) && severidade(bruta) !== 'off')
117
+ return nome;
118
+ }
119
+ return null;
120
+ }
99
121
  /**
100
122
  * Ensaio da 0.5.0: a thread criada sem `--worktree auto` entrega a base para a base, e a correcao
101
123
  * repetia o proprio `ork ship`. A thread que ainda nao passou do GO ganha a branch com
@@ -122,6 +144,8 @@ function avaliarPolicies(manifesto, ctx) {
122
144
  const violacoes = [];
123
145
  // verify_regression e verify_failed conferem a mesma coisa: um aviso so, com o nome da primeira declarada.
124
146
  let baselineJaAvaliada = false;
147
+ // claim_sem_prova_local e claims_failed idem.
148
+ let provaLocalJaAvaliada = false;
125
149
  for (const [nome, bruta] of Object.entries(declaradas)) {
126
150
  const conhecida = exports.POLICIES_CONHECIDAS[nome];
127
151
  if (!conhecida || !conhecida.quando.includes(ctx.gate))
@@ -224,6 +248,33 @@ function avaliarPolicies(manifesto, ctx) {
224
248
  }
225
249
  continue;
226
250
  }
251
+ if (exports.POLICIES_DE_PROVA_LOCAL.includes(nome)) {
252
+ if (provaLocalJaAvaliada)
253
+ continue;
254
+ provaLocalJaAvaliada = true;
255
+ const r = ctx.provaLocalReprovada;
256
+ if (r) {
257
+ const id = ctx.threadId ?? '<thread>';
258
+ violacoes.push({
259
+ policy: nome,
260
+ // O registro de claim nunca para: `block` declarado avisa como `warn`.
261
+ severidade: 'warn',
262
+ motivo: r.estourou ? 'verify.timeout' : 'claims.failed',
263
+ detalhe: r.semDiretorio
264
+ ? `a worktree da thread (${r.semDiretorio}) nao existe; o comando da claim ${r.claim} nao rodou e a claim entrou sem prova local`
265
+ : r.estourou
266
+ ? `o comando da claim ${r.claim} estourou o prazo do verify (${Math.round(r.prazoMs / 1000)} s) no registro; a claim entrou sem prova local`
267
+ : `o comando da claim ${r.claim} saiu ${r.code} no registro; a claim entrou sem prova local e reprova no verify`,
268
+ correcao: r.semDiretorio
269
+ ? `recrie a worktree da thread (ou feche a thread que perdeu a worktree); depois, ork verify ${id}`
270
+ : r.estourou
271
+ ? `divida o comando ou suba verify.timeout_ms; depois, ork verify ${id}`
272
+ : `corrija o produto ou o comando e anexe o certo com ork claims verificar ${id} ${r.claim} --comando "<comando>"; ` +
273
+ `se a alegacao nao vale, ork claims retirar ${id} ${r.claim} --motivo "<motivo>"`,
274
+ });
275
+ }
276
+ continue;
277
+ }
227
278
  if (nome === 'tree_blocked') {
228
279
  if (ctx.branchAtrasDaBase === true) {
229
280
  violacoes.push({
package/dist/portfolio.js CHANGED
@@ -44,6 +44,7 @@ exports.assertCycleScope = assertCycleScope;
44
44
  /** Catálogo determinístico de produto -> projeto -> iniciativa. */
45
45
  const fs = __importStar(require("node:fs"));
46
46
  const project_state_1 = require("./project-state");
47
+ const estado_thread_1 = require("./estado-thread");
47
48
  const company_brain_journal_1 = require("./company-brain-journal");
48
49
  const util_1 = require("./util");
49
50
  const creation_operation_store_1 = require("./creation-operation-store");
@@ -65,7 +66,9 @@ function readPortfolio(root) {
65
66
  }
66
67
  function persist(root, value) { value.updatedAt = (0, util_1.agora)(); (0, company_brain_journal_1.replaceWithJournal)(file(root), value); return value; }
67
68
  function mutatePortfolio(root, run) {
68
- return (0, creation_operation_store_1.withCreationLock)(root, 'portfolio', () => (0, company_brain_journal_1.portfolioMutation)(file(root), run));
69
+ // RM-036 (D5): o portfolio.json e canonico, e o lock de espera tambem: chamado da raiz ou de uma worktree,
70
+ // quem chega depois espera a vez, em vez de recusar com brain.journal.busy no lock interno.
71
+ return (0, creation_operation_store_1.withCreationLock)((0, estado_thread_1.raizDoEstado)(root), 'portfolio', () => (0, company_brain_journal_1.portfolioMutation)(file(root), run));
69
72
  }
70
73
  function validateBase(kind, input) {
71
74
  if (!ID.test(input.id) || !input.id.startsWith(PREFIX[kind]))
@@ -75,6 +75,7 @@ exports.linhasDaConsulta = linhasDaConsulta;
75
75
  const fs = __importStar(require("node:fs"));
76
76
  const os = __importStar(require("node:os"));
77
77
  const path = __importStar(require("node:path"));
78
+ const branch_de_estado_1 = require("./branch-de-estado");
78
79
  const estado_thread_1 = require("./estado-thread");
79
80
  const manifest_1 = require("./manifest");
80
81
  const maquina_1 = require("./maquina");
@@ -198,7 +199,10 @@ function remotoSemCredencial(url) {
198
199
  }
199
200
  /** URL do remoto, sem credencial. `null` quando o projeto nao tem aquele remoto. */
200
201
  function remotoDoProjeto(raiz, remoto = 'origin') {
201
- const r = (0, util_1.exec)('git', ['remote', 'get-url', remoto], raiz, 5000);
202
+ // RM-047: o nome vem do manifesto; fora do formato de remoto, nada vai ao git.
203
+ if (!(0, branch_de_estado_1.remotoValido)(remoto))
204
+ return null;
205
+ const r = (0, util_1.exec)('git', ['remote', 'get-url', '--', remoto], raiz, 5000);
202
206
  const url = r.ok ? r.stdout.trim() : '';
203
207
  return url ? remotoSemCredencial(url) : null;
204
208
  }
package/dist/prompts.js CHANGED
@@ -49,11 +49,13 @@ var __importStar = (this && this.__importStar) || (function () {
49
49
  };
50
50
  })();
51
51
  Object.defineProperty(exports, "__esModule", { value: true });
52
- exports.TEMPLATES_EMBUTIDOS = exports.TEMPLATE_FASE_PADRAO = exports.CONTRATO_DE_FASE = exports.REGRA_CENTRAL = exports.SECOES_OBRIGATORIAS = exports.LIMITE_TEMPLATE_BYTES = void 0;
52
+ exports.TEMPLATES_EMBUTIDOS = exports.TEMPLATE_FASE_PADRAO = exports.PADROES_DE_HITL_EM_TEXTO_LIVRE = exports.CONTRATO_DE_FASE = exports.REGRA_CENTRAL = exports.SECOES_OBRIGATORIAS = exports.LIMITE_TEMPLATE_BYTES = void 0;
53
+ exports.pedidoComDicaDoGrafo = pedidoComDicaDoGrafo;
53
54
  exports.dirDeTemplates = dirDeTemplates;
54
55
  exports.variaveisUsadas = variaveisUsadas;
55
56
  exports.renderizar = renderizar;
56
57
  exports.parseTemplate = parseTemplate;
58
+ exports.procurarHitlEmTextoLivre = procurarHitlEmTextoLivre;
57
59
  exports.lintTemplate = lintTemplate;
58
60
  exports.carregarTemplatesDe = carregarTemplatesDe;
59
61
  exports.carregarTemplates = carregarTemplates;
@@ -69,6 +71,15 @@ const policies_1 = require("./policies");
69
71
  const types_1 = require("./types");
70
72
  /** Limite duro de um template, o mesmo do manifesto: prosa longa nao vira prompt. */
71
73
  exports.LIMITE_TEMPLATE_BYTES = 16384;
74
+ /** KG5 fatia 2: dica opt-in no pedido; desligada preserva o texto byte a byte. */
75
+ function pedidoComDicaDoGrafo(pedido, thread, ligado) {
76
+ if (!ligado)
77
+ return pedido;
78
+ return pedido.trimEnd() + '\n\n## Contexto do grafo (opcional)\n'
79
+ + `Consulte ork_grafo_contexto {threadId: "${thread}"} ou ork grafo contexto ${thread} --json para o pacote da thread com evidencias e teto em bytes.\n`
80
+ + 'As tools ork_grafo_vizinhos, ork_grafo_chamadores, ork_grafo_importadores e ork_grafo_caminho permitem aprofundar. '
81
+ + 'Sem indice do HEAD, rode ork grafo indexar. O grafo e parcial; com arvore modificada, descreve o HEAD.\n';
82
+ }
72
83
  /** Onde um projeto guarda os templates que sobrescrevem os embutidos. */
73
84
  function dirDeTemplates(raiz) {
74
85
  return path.join(raiz, 'prompts');
@@ -170,6 +181,27 @@ exports.CONTRATO_DE_FASE = {
170
181
  variavelObrigatoria: 'pedido',
171
182
  validaFases: true,
172
183
  };
184
+ /**
185
+ * RM-057: os jeitos de um template pedir ao dono texto livre ou texto colado. Lista fechada, como o
186
+ * vocabulario do pulse: acrescentar um padrao e decisao de contrato, com teste. Casa sobre a linha
187
+ * em minusculas e sem acento.
188
+ */
189
+ exports.PADROES_DE_HITL_EM_TEXTO_LIVRE = Object.freeze([
190
+ { oQue: 'que o dono cole texto', padrao: /\b(cole|colar|colem)\b[^.\n]{0,60}\b(texto|aqui|mensagem|pedido|resposta|saida|conteudo)\b/ },
191
+ { oQue: 'que o dono cole texto', padrao: /\bpaste\b/ },
192
+ { oQue: 'confirmacao em texto livre', padrao: /\b(responda|digite|escreva|confirme|responder|digitar|escrever)\b[^.\n]{0,40}(\bconfirmo\b|texto livre|\bem texto\b)/ },
193
+ { oQue: 'confirmacao em texto livre', padrao: /\b(peca|peco|pedir|solicite|solicitar)\b[^.\n]{0,30}\bconfirmacao\b[^.\n]{0,20}\b(por escrito|em texto|textual)\b/ },
194
+ ]);
195
+ function procurarHitlEmTextoLivre(corpo) {
196
+ const achados = [];
197
+ corpo.split('\n').forEach((bruta, i) => {
198
+ const linha = bruta.normalize('NFD').replace(/[\u0300-\u036f]/g, '').toLowerCase();
199
+ const casou = exports.PADROES_DE_HITL_EM_TEXTO_LIVRE.find(p => p.padrao.test(linha));
200
+ if (casou)
201
+ achados.push({ linha: i + 1, oQue: casou.oQue });
202
+ });
203
+ return achados;
204
+ }
173
205
  /**
174
206
  * Lint de um template. Devolve os problemas; lista vazia quer dizer aprovado.
175
207
  *
@@ -231,6 +263,9 @@ function lintTemplate(t, contrato = exports.CONTRATO_DE_FASE) {
231
263
  if (bytes > exports.LIMITE_TEMPLATE_BYTES) {
232
264
  erro('tamanho', `${bytes} bytes acima do limite de ${exports.LIMITE_TEMPLATE_BYTES}`);
233
265
  }
266
+ for (const achado of procurarHitlEmTextoLivre(t.corpo)) {
267
+ erro('hitl-texto-livre', `linha ${achado.linha} pede ${achado.oQue}; HITL de conducao e selecao de 3 a 5 alternativas com uma Recomendacao (RM-057)`);
268
+ }
234
269
  if (t.bruto.includes('—')) {
235
270
  aviso('idioma', 'o template usa travessao longo (U+2014); o padrao do projeto e hifen ou virgula');
236
271
  }
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.ENTRADAS = exports.HOSTS_DA_PROVA = void 0;
37
37
  exports.transcriptDoClaude = transcriptDoClaude;
38
38
  exports.transcriptDoOpenclaw = transcriptDoOpenclaw;
39
+ exports.transcriptDoCodex = transcriptDoCodex;
39
40
  exports.extrairSnapshot = extrairSnapshot;
40
41
  exports.conferirProva = conferirProva;
41
42
  exports.redigir = redigir;
@@ -44,7 +45,7 @@ exports.redigirObjeto = redigirObjeto;
44
45
  * RM-032: conferência da prova de ativação por host, sem host.
45
46
  *
46
47
  * O roteiro `core/scripts/prova-ativacao.cjs` abre uma sessão nova e não interativa no host
47
- * (Claude Code ou OpenClaw), diz `orkastery maestro` e entrega aqui o transcript. Esta
48
+ * (Claude Code, OpenClaw ou Codex), diz `orkastery maestro` e entrega aqui o transcript. Esta
48
49
  * conferência olha só o que é determinístico: qual ferramenta o modelo chamou, o resultado que
49
50
  * ela devolveu (contra o contrato da entrada) e o projeto lido. Da resposta final em texto
50
51
  * livre, exige apenas que cite o projeto e não repita o incidente de 29/09 ("roadmap vazio").
@@ -52,20 +53,31 @@ exports.redigirObjeto = redigirObjeto;
52
53
  const fs = __importStar(require("node:fs"));
53
54
  const path = __importStar(require("node:path"));
54
55
  const maestro_contract_1 = require("./maestro-contract");
55
- exports.HOSTS_DA_PROVA = ['claude-code', 'openclaw'];
56
+ exports.HOSTS_DA_PROVA = ['claude-code', 'openclaw', 'codex'];
56
57
  /**
57
58
  * As entradas da frase em cada host. No Claude, a tool MCP do servidor `orkastery` fixado no
58
59
  * projeto (MAESTRO_HOST_SURFACES em hosts.ts). No OpenClaw 0.5.0 (RM-054, fatia 2), a frase sem
59
60
  * projeto vai a `ork_network_roadmap`, a única tool da extensão no perfil `coding`; com projeto
60
- * nomeado, a `ork_maestro`. `ork maestro` pelo shell do host é desvio: o CLI resolve o projeto
61
- * pelo diretório atual, a classe de erro do incidente.
61
+ * nomeado, a `ork_maestro`. No Codex (B11), a tool `ork_maestro` do servidor `orkastery` que o
62
+ * `ork mcp install --host codex` grava no projeto; o nome segue o padrão `mcp__<servidor>__<tool>`.
63
+ * `ork maestro` pelo shell do host é desvio: o CLI resolve o projeto pelo diretório atual, a classe
64
+ * de erro do incidente.
62
65
  */
63
66
  exports.ENTRADAS = {
64
67
  'claude-code': [{ ferramenta: 'mcp__orkastery__ork_maestro', contrato: 'maestro' }],
65
68
  openclaw: [{ ferramenta: 'ork_network_roadmap', contrato: 'rede' }, { ferramenta: 'ork_maestro', contrato: 'maestro' }],
69
+ codex: [{ ferramenta: 'mcp__orkastery__ork_maestro', contrato: 'maestro' }],
66
70
  };
67
- const SHELL_DO_HOST = { 'claude-code': 'Bash', openclaw: 'exec' };
68
- const ORK_MAESTRO_NO_SHELL = /(^|[\s;&|(/])ork(\s+(--projeto|--project|-p)(\s+|=)\S+)?\s+maestro\b/;
71
+ const SHELL_DO_HOST = { 'claude-code': 'Bash', openclaw: 'exec', codex: 'command_execution' };
72
+ // B11: o shell do Codex chega embrulhado (`bash -lc 'ork maestro'`), então aspas também abrem o comando.
73
+ // Suspeitas da revisão de 03/10: o mesmo CLI roda também pelo caminho do binário (`$(which ork)`), pelo
74
+ // node (`core/dist/index.js`), pelo npx (`@orkastery/cli`) e com o projeto entre aspas. É desvio a
75
+ // invocação do ork cujo subcomando, depois só de opções (com valor, até entre aspas), é `maestro`;
76
+ // `maestro` dentro do argumento de outro subcomando não é.
77
+ const INVOCACAO_DO_ORK = String.raw `(?:(?:^|[^\w.@-])ork|@orkastery\/cli(?:@[\w.-]+)?|\bdist\/index\.js)(?![\w.-])[)\x60]?`;
78
+ const VALOR = String.raw `(?:"[^"]*"|'[^']*'|[^\s"'-][^\s"']*)`;
79
+ const OPCOES = String.raw `(?:\s+-[\w-]*(?:=(?:"[^"]*"|'[^']*'|[^\s"']+))?(?:\s+${VALOR})?)*`;
80
+ const ORK_MAESTRO_NO_SHELL = new RegExp(String.raw `${INVOCACAO_DO_ORK}${OPCOES}\s+maestro(?![\w.-])`);
69
81
  function linhasJson(texto) {
70
82
  const eventos = [];
71
83
  for (const linha of texto.split('\n')) {
@@ -172,6 +184,63 @@ function transcriptDoOpenclaw(eventsJsonl, agenteJson) {
172
184
  const textos = (agente.payloads ?? []).map(p => p?.text).filter((t) => typeof t === 'string');
173
185
  return { ferramentasExpostas, chamadas, respostaFinal: textos.length ? textos.join('\n') : null };
174
186
  }
187
+ /**
188
+ * Eventos do `codex exec --json` (B11): `item.started`/`item.completed` com `mcp_tool_call` (servidor,
189
+ * tool, argumentos, resultado, erro), `command_execution` (o shell do host) e `agent_message` (a
190
+ * resposta). O `codex exec` não lista as ferramentas expostas ao modelo: quem chama passa as do
191
+ * `tools/list` do mesmo servidor MCP que a sessão recebeu, já com o nome `mcp__<servidor>__<tool>`.
192
+ */
193
+ function transcriptDoCodex(eventosJsonl, ferramentasExpostas) {
194
+ const porItem = new Map();
195
+ const chamadas = [];
196
+ const mensagens = [];
197
+ for (const e of linhasJson(eventosJsonl)) {
198
+ if (e.type !== 'item.started' && e.type !== 'item.completed')
199
+ continue;
200
+ const item = (e.item ?? {});
201
+ const id = typeof item.id === 'string' ? item.id : null;
202
+ if (item.type === 'agent_message') {
203
+ if (e.type === 'item.completed' && typeof item.text === 'string')
204
+ mensagens.push(item.text);
205
+ continue;
206
+ }
207
+ let ferramenta, argumentos, resultado = null, erro = false;
208
+ if (item.type === 'mcp_tool_call' && typeof item.server === 'string' && typeof item.tool === 'string') {
209
+ ferramenta = `mcp__${item.server}__${item.tool}`;
210
+ argumentos = item.arguments ?? null;
211
+ const r = item.result;
212
+ resultado = typeof r === 'string' ? r : textoDoConteudo(r?.content);
213
+ const falha = item.error;
214
+ erro = !!falha || item.status === 'failed';
215
+ if (erro && resultado === null)
216
+ resultado = typeof falha === 'string' ? falha : typeof falha?.message === 'string' ? falha.message : 'falhou';
217
+ }
218
+ else if (item.type === 'command_execution' && typeof item.command === 'string') {
219
+ ferramenta = 'command_execution';
220
+ argumentos = { command: item.command };
221
+ resultado = typeof item.aggregated_output === 'string' ? item.aggregated_output : null;
222
+ erro = item.status === 'failed' || (typeof item.exit_code === 'number' && item.exit_code !== 0);
223
+ }
224
+ else
225
+ continue;
226
+ const tipo = classificar('codex', ferramenta, argumentos);
227
+ if (!tipo)
228
+ continue;
229
+ const anterior = id ? porItem.get(id) : undefined;
230
+ if (anterior) {
231
+ // `item.completed` do mesmo item fecha a chamada aberta no `item.started`.
232
+ if (e.type === 'item.completed')
233
+ Object.assign(anterior, { argumentos, resultado, erro });
234
+ continue;
235
+ }
236
+ const chamada = { ferramenta, ...tipo, argumentos, resultado: e.type === 'item.completed' ? resultado : null,
237
+ erro: e.type === 'item.completed' ? erro : false };
238
+ if (id)
239
+ porItem.set(id, chamada);
240
+ chamadas.push(chamada);
241
+ }
242
+ return { ferramentasExpostas, chamadas, respostaFinal: mensagens.length ? mensagens.at(-1) : null };
243
+ }
175
244
  /** O primeiro objeto `ork.maestro-snapshot/v1` do texto (o host pode juntar stderr ao stdout). */
176
245
  function extrairSnapshot(texto) {
177
246
  const inicio = texto.indexOf('{"schema":"ork.maestro-snapshot/v1"');
@@ -38,6 +38,7 @@ exports.assinaturaPulse = assinaturaPulse;
38
38
  exports.assinaturaDoResumo = assinaturaDoResumo;
39
39
  exports.mensagemPulse = mensagemPulse;
40
40
  exports.varrerPulse = varrerPulse;
41
+ exports.varrerEDepoisPublicarNaRede = varrerEDepoisPublicarNaRede;
41
42
  exports.enviarPayloadHost = enviarPayloadHost;
42
43
  const manifest_1 = require("./manifest");
43
44
  const write_activation_1 = require("./write-activation");
@@ -59,6 +60,8 @@ const hitl_contract_1 = require("./hitl-contract");
59
60
  const monitor_lock_1 = require("./monitor-lock");
60
61
  const fabrica_estado_1 = require("./fabrica-estado");
61
62
  const maquina_1 = require("./maquina");
63
+ const rede_1 = require("./rede");
64
+ const rede_adesao_1 = require("./rede-adesao");
62
65
  const pulse_cadencia_1 = require("./pulse-cadencia");
63
66
  // O lock dos monitores mudou de modulo (I-41, GO-FIX 1); quem o importava daqui continua importando.
64
67
  var monitor_lock_2 = require("./monitor-lock");
@@ -253,7 +256,9 @@ function varrerPulse(opcoes) {
253
256
  const guardadas = anterior ? fila.candidatos.filter(c => anterior.candidatos.some(a => a.thread === c.thread)).length : 0;
254
257
  const resumo = (0, hitl_resumo_1.resumirHitl)(itens, { quando, janelaMin, prontas: fila.candidatos.length, acumuladas: guardadas,
255
258
  consertos: fila.consertos, atoDoItem: i => fila.atos.get(`${i.thread}|${i.fase}`),
256
- decisoes: decisoesNovas, acimaDoLimiar: pulse.acimaDoLimiar ?? [], outrasMaquinas: outras, paradosNoCondutor: parados });
259
+ decisoes: decisoesNovas, acimaDoLimiar: pulse.acimaDoLimiar ?? [], outrasMaquinas: outras, paradosNoCondutor: parados,
260
+ // RM-057 (fatia 3): a linha so acompanha o resumo que ja sai; nao e novidade nem fura a cadencia.
261
+ ...(pulse.hitlDeConducao ? { hitlDeConducao: pulse.hitlDeConducao } : {}) });
257
262
  // Pedir licenca para mandar zero perguntas era o defeito: sem pergunta, nao ha codigo.
258
263
  // I-50: cadencia curta nao encurta o prazo do dono para responder ao resumo (minimo de 60 min).
259
264
  const consentimento = fila.candidatos.length ? (0, monitor_lock_1.comLockDaConversa)(dir, () => (0, pulse_consentimento_1.abrirConsentimento)(raiz, { quando,
@@ -287,6 +292,22 @@ function varrerPulse(opcoes) {
287
292
  trava.liberar();
288
293
  }
289
294
  }
295
+ /**
296
+ * RM-053: a entrega ao dono vem primeiro, e a rede depois, mesmo quando a varredura falha. M4 do
297
+ * CHECK 1: antes da entrega, uma forja lenta atrasava o HITL em minutos. S13 da revisao 2: sem o
298
+ * `finally`, uma excecao na varredura suprimia tambem a batida da rede.
299
+ */
300
+ function varrerEDepoisPublicarNaRede(varrer, publicar) {
301
+ try {
302
+ return varrer();
303
+ }
304
+ finally {
305
+ try {
306
+ publicar();
307
+ }
308
+ catch { /* a rede registra a propria falha no rede.log */ }
309
+ }
310
+ }
290
311
  if (require.main === module) {
291
312
  try {
292
313
  const raiz = process.argv[2] ?? process.cwd();
@@ -317,7 +338,18 @@ if (require.main === module) {
317
338
  }
318
339
  const outrasMaquinas = compartilhada
319
340
  ? () => (0, fabrica_estado_1.resumoDasOutrasMaquinas)((0, fabrica_estado_1.lerFabrica)(carregado.raiz, { remoto, semRemoto: true }), (0, maquina_1.nomeDaMaquina)()) : undefined;
320
- const r = varrerPulse({ raiz, escopo, comCadencia: true, outrasMaquinas });
341
+ // RM-053 (D9): depois da entrega ao dono, a mesma batida publica o retrato desta maquina na rede da
342
+ // pessoa (so membro, uma tentativa a cada 14 min, retrato igual so de hora em hora).
343
+ const r = varrerEDepoisPublicarNaRede(() => varrerPulse({ raiz, escopo, comCadencia: true, outrasMaquinas }), () => {
344
+ try {
345
+ const rede = (0, rede_1.publicarRedeNaBatida)({ diretorio: carregado.raiz });
346
+ if (rede)
347
+ (0, rede_adesao_1.registrarNaRede)({ ...rede, origem: 'pulse' });
348
+ }
349
+ catch (e) {
350
+ (0, rede_adesao_1.registrarNaRede)({ acao: 'falhou', origem: 'pulse', erro: e.message });
351
+ }
352
+ });
321
353
  console.log(JSON.stringify(r));
322
354
  process.exitCode = r.code;
323
355
  }
package/dist/pulse.js CHANGED
@@ -13,6 +13,7 @@ const liveness_1 = require("./liveness");
13
13
  const master_1 = require("./master");
14
14
  const master_batch_1 = require("./master-batch");
15
15
  const orquestracao_1 = require("./orquestracao");
16
+ const impedimento_1 = require("./impedimento");
16
17
  const thread_1 = require("./thread");
17
18
  const sessoes_adopt_1 = require("./sessoes-adopt");
18
19
  const ledger_1 = require("./ledger");
@@ -27,6 +28,7 @@ const docs_1 = require("./docs");
27
28
  const conducao_1 = require("./conducao");
28
29
  const conducao_texto_1 = require("./conducao-texto");
29
30
  const hitl_resumo_1 = require("./hitl-resumo");
31
+ const hitl_tempo_parado_1 = require("./hitl-tempo-parado");
30
32
  const parado_no_condutor_1 = require("./parado-no-condutor");
31
33
  exports.CONTRATO_PULSE = 'ork.pulse/v1';
32
34
  /** Uma semana: o resumo cita a decisao uma vez, e ela continua lida ate sair da janela. */
@@ -169,6 +171,19 @@ function comporPulse(carregado, entrada) {
169
171
  thread: l.thread, fase: p.fase, sessionId: null, desdeEm: p.desdeEm, paradaHaMin: p.paradaHaMin, impacto: impacto(p.fase),
170
172
  pergunta: p.detalhe, opcoes: [], recomendacao: p.correcao, comandoResposta: p.correcao, evidencia: [p.evidencia],
171
173
  fontes: ['monitor'], contextoLogs: [] };
174
+ // RM-055: o impedimento do despacho sai no contrato curto, com o comando exato e o re-despacho depois.
175
+ if (p.impedimento) {
176
+ try {
177
+ const curto = (0, impedimento_1.pedidoCurtoDoImpedimento)({ thread: l.thread, fase: p.fase, motivo: p.motivo, detalhe: p.detalhe,
178
+ desdeEm: p.desdeEm, impedimento: p.impedimento }, quando);
179
+ item.apresentacaoCurta = { telegram: (0, hitl_curto_1.textoDoPedidoCurto)(curto, 'telegram'), terminal: (0, hitl_curto_1.textoDoPedidoCurto)(curto, 'terminal') };
180
+ item.opcoes = curto.alternativas.map(a => `${a.chave}. ${a.texto}`);
181
+ item.comandoResposta = `ork retry run ${l.thread}`;
182
+ }
183
+ catch {
184
+ item.evidencia.push('Pedido curto do impedimento indisponível; a correção acima vale.');
185
+ }
186
+ }
172
187
  // I-45: vaga parada e sinal do escalonador (`ork board reap` devolve a vaga), nao pergunta ao dono.
173
188
  const esperaAutomatica = ['lease.busy', 'conducao.em-andamento', 'concurrency.limite', 'runtime.rate-limited', 'vaga.stale'].includes(p.motivo);
174
189
  (esperaAutomatica ? automaticas : humanos).push(item);
@@ -276,11 +291,26 @@ function montarPulse(carregado, opcoes = {}) {
276
291
  (0, parado_no_condutor_1.gravarRetratoDePrs)(carregado.raiz, leitura.retrato);
277
292
  }
278
293
  catch { /* o retrato e economia do status */ }
294
+ try {
295
+ (0, parado_no_condutor_1.esquecerForjaSemLeitura)(carregado.raiz);
296
+ }
297
+ catch { /* sem a marca, a proxima perda de leitura e dita de novo */ }
279
298
  }
280
299
  return leitura;
281
300
  } });
282
- if (entregas.prs && !entregas.prs.ok)
283
- diagnosticos.push(`prs.nao-lidos: ${entregas.prs.erro.slice(0, 160)}`);
301
+ const leitura = entregas.prs;
302
+ if (leitura && !leitura.ok && leitura.semLeitura) {
303
+ // RM-037 (fatia 5, A5): a forja sem leitura de PR e dita uma vez por remoto e host; as batidas seguintes nao repetem.
304
+ let novidade = true;
305
+ try {
306
+ novidade = (0, parado_no_condutor_1.avisarForjaSemLeitura)(carregado.raiz, leitura.semLeitura, leitura.erro, quando);
307
+ }
308
+ catch { /* sem marca, diz de novo */ }
309
+ if (novidade)
310
+ diagnosticos.push(`prs.sem-leitura: ${leitura.erro.slice(0, 160)}`);
311
+ }
312
+ else if (leitura && !leitura.ok)
313
+ diagnosticos.push(`prs.nao-lidos: ${leitura.erro.slice(0, 160)}`);
284
314
  }
285
315
  catch (e) {
286
316
  diagnosticos.push(`entregas.indisponiveis: ${e.message.slice(0, 120)}`);
@@ -298,6 +328,15 @@ function montarPulse(carregado, opcoes = {}) {
298
328
  catch (e) {
299
329
  diagnosticos.push(`decisoes.indisponiveis: ${e.message.slice(0, 120)}`);
300
330
  }
331
+ // RM-057 (fatia 3): leitura pura do ledger; falhar aqui nunca derruba a fila de atencao.
332
+ try {
333
+ const hitl = (0, hitl_tempo_parado_1.lerHitlDeConducaoAgora)(carregado.raiz, quando);
334
+ if (hitl.abertas.length || hitl.seteDias.pedidos || hitl.seteDias.respondidos)
335
+ pulse.hitlDeConducao = hitl;
336
+ }
337
+ catch (e) {
338
+ diagnosticos.push(`hitl-conducao.indisponivel: ${e.message.slice(0, 120)}`);
339
+ }
301
340
  if (diagnosticos.length)
302
341
  pulse.runtime.detalhe = [pulse.runtime.detalhe, ...diagnosticos].filter(Boolean).join('; ');
303
342
  return pulse;
@@ -307,7 +346,7 @@ function textoDoPulse(p) {
307
346
  const local = (texto) => (0, horario_1.localizarTexto)(texto, { agora: p.consultadoEm });
308
347
  const linhas = [`Pulse (${p.contrato}) ${(0, horario_1.formatarDataHoraRotulada)(p.consultadoEm, { agora: p.consultadoEm })}`, `Precisa de humano agora: ${p.resumo.humanos}`, `${p.resumo.scores} entrega(s) sem nota, aceitas por padrão com registro`];
309
348
  // RM-037 (fatia 4): PR nao lido tambem e dito, para a falta de linha do condutor nao parecer "nada parado".
310
- if (p.runtime.ok && ['liveness.snapshot.invalid', 'prs.nao-lidos', 'entregas.indisponiveis'].some(d => p.runtime.detalhe.includes(d))) {
349
+ if (p.runtime.ok && ['liveness.snapshot.invalid', 'prs.nao-lidos', 'prs.sem-leitura', 'entregas.indisponiveis'].some(d => p.runtime.detalhe.includes(d))) {
311
350
  linhas.push(`[diagnostico] ${local(p.runtime.detalhe)}`);
312
351
  }
313
352
  if (!p.runtime.ok)
@@ -326,6 +365,13 @@ function textoDoPulse(p) {
326
365
  for (const x of p.paradoNoCondutor)
327
366
  linhas.push((0, hitl_resumo_1.linhaDoParadoNoCondutor)(x, { agora: p.consultadoEm }));
328
367
  }
368
+ // RM-057 (fatia 3): quanto tempo cada pergunta de conducao aberta ja parou a thread, e a mediana da semana.
369
+ if (p.hitlDeConducao) {
370
+ const h = p.hitlDeConducao;
371
+ linhas.push('', `HITL de condução: ${h.abertas.length} aberta(s); ${(0, hitl_tempo_parado_1.textoDaMediana)(h)}`);
372
+ for (const a of h.abertas)
373
+ linhas.push((0, hitl_tempo_parado_1.linhaDaPerguntaParada)(a, { agora: p.consultadoEm }));
374
+ }
329
375
  linhas.push('', `Ações automáticas pendentes: ${p.resumo.automaticas}`);
330
376
  for (const i of p.acoesAutomaticas)
331
377
  linhas.push(`${i.thread}: ${i.motivo}; ${i.comandoResposta}`);
package/dist/ratelimit.js CHANGED
@@ -61,6 +61,7 @@ exports.lerFilaDeRetomada = lerFilaDeRetomada;
61
61
  exports.janelaPadraoMs = janelaPadraoMs;
62
62
  exports.enfileirar = enfileirar;
63
63
  exports.prazoDaConta = prazoDaConta;
64
+ exports.prazoDaCotaVista = prazoDaCotaVista;
64
65
  exports.marcarContaDaFalha = marcarContaDaFalha;
65
66
  exports.gravarPedido = gravarPedido;
66
67
  exports.exigirPedido = exigirPedido;
@@ -151,15 +152,30 @@ function prazoDaConta(manifesto, falha, agoraMs = Date.now()) {
151
152
  return falha.resetEm;
152
153
  return new Date(agoraMs + janelaPadraoMs(manifesto)).toISOString();
153
154
  }
154
- /** Marca no store o perfil que falhou pela conta. Sem perfil (ambiente do processo), nada a marcar. */
155
- function marcarContaDaFalha(carregado, perfil, falha, agoraMs = Date.now()) {
155
+ /**
156
+ * RM-037 (fatia 5): ate quando a conta sai do rodizio pela cota que o observador viu na transcricao, com a
157
+ * sessao viva. Vale a hora dita (ja ancorada no instante da mensagem) ou a janela padrao contada da mensagem,
158
+ * nunca do relogio de quem leu. Prazo que ja venceu volta `null`: a conta ja voltou, e marcar agora tiraria do
159
+ * rodizio uma conta boa (a regra A10 do despacho recusado nao vale aqui, porque ninguem acabou de recusar nada).
160
+ */
161
+ function prazoDaCotaVista(manifesto, falha, mensagemMs, agoraMs = Date.now()) {
162
+ if (falha.motivo !== 'runtime.quota-exhausted' || !Number.isFinite(mensagemMs))
163
+ return null;
164
+ const prazo = falha.resetEm ?? new Date(mensagemMs + janelaPadraoMs(manifesto)).toISOString();
165
+ return Date.parse(prazo) > agoraMs ? prazo : null;
166
+ }
167
+ /**
168
+ * Marca no store o perfil que falhou pela conta. Sem perfil (ambiente do processo), nada a marcar. `prazo`,
169
+ * quando vem, e o ja calculado por quem viu a falha (`prazoDaCotaVista`); sem ele, vale `prazoDaConta`.
170
+ */
171
+ function marcarContaDaFalha(carregado, perfil, falha, agoraMs = Date.now(), prazo) {
156
172
  // RM-037 (defeitosdeco D-6): modelo inacessivel nao e falha da conta inteira; o perfil fica no rodizio.
157
173
  if (!perfil || falha.motivo === 'runtime.model-unavailable')
158
174
  return null;
159
175
  const esgotado = falha.motivo === 'runtime.quota-exhausted';
160
176
  return (0, runtime_profiles_1.marcarFalhaDePerfil)(carregado.raiz, perfil.id, { estado: esgotado ? 'esgotado' : 'sem-auth',
161
- esgotadoAte: prazoDaConta(carregado.manifesto, falha, agoraMs), motivo: falha.motivo, detalhe: falha.trecho,
162
- em: new Date(agoraMs).toISOString() });
177
+ esgotadoAte: esgotado && prazo ? prazo : prazoDaConta(carregado.manifesto, falha, agoraMs), motivo: falha.motivo,
178
+ detalhe: falha.trecho, em: new Date(agoraMs).toISOString() });
163
179
  }
164
180
  /** Regrava um pedido (append-only: a versao nova vence, o historico fica). */
165
181
  function gravarPedido(raiz, pedido) {
package/dist/recall.js CHANGED
@@ -257,7 +257,9 @@ function recallDaThread(carregado, memoria, threadId, opcoes = {}) {
257
257
  falhas.push({ id: 'memoria-busca', titulo: 'Descoberta de handoffs da fase', location: '',
258
258
  orkmind: null, retrieve_when: fase, resolvido: false, motivo: 'orkmind-indisponivel', via: null,
259
259
  conteudo: '', metodo: '', sha256: '', intervalo: null,
260
- detalhe: `${memoria.leituraRestrita ? falhaDeConsulta(erro) : 'memory.transport.export'}: busca semantica falhou; resultado nao comprova colecao vazia` });
260
+ // RM-038: a janela cheia do export tem codigo proprio; o resto segue como falha de transporte.
261
+ detalhe: `${memoria.leituraRestrita ? falhaDeConsulta(erro) : erro instanceof Error && erro.message === 'memory.query.window-saturated'
262
+ ? erro.message : 'memory.transport.export'}: busca semantica falhou; resultado nao comprova colecao vazia` });
261
263
  }
262
264
  }
263
265
  if (opcoes.id !== undefined && resolvidos.length === 0 && falhas.length === 0 && adiados.length === 0) {