@orkastery/cli 0.4.3 → 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 (121) 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 -2
  23. package/adapters/openclaw/dist/hitl-ingress.js +37 -3
  24. package/adapters/openclaw/dist/index.js +92 -20
  25. package/adapters/openclaw/openclaw.plugin.json +10 -1
  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 -20
  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 +6 -3
  34. package/dist/board.js +74 -0
  35. package/dist/branch-de-estado.js +5 -4
  36. package/dist/busca-semantica.js +161 -0
  37. package/dist/ci.js +141 -10
  38. package/dist/company-brain-cli.js +27 -2
  39. package/dist/company-brain-context.js +132 -0
  40. package/dist/company-brain-dossie.js +376 -0
  41. package/dist/company-brain-mcp.js +4 -1
  42. package/dist/conducao.js +71 -18
  43. package/dist/decisao-autonoma.js +42 -0
  44. package/dist/docs.js +64 -22
  45. package/dist/doctor.js +33 -0
  46. package/dist/entrega-pr.js +109 -5
  47. package/dist/experiencia-instalacao.js +256 -0
  48. package/dist/experiencia.js +72 -0
  49. package/dist/fabrica-estado.js +43 -10
  50. package/dist/fechamento.js +105 -0
  51. package/dist/forja.js +435 -0
  52. package/dist/gates.js +9 -1
  53. package/dist/hitl-classificacao.js +11 -1
  54. package/dist/hitl-contract.js +50 -7
  55. package/dist/hitl-curto.js +158 -0
  56. package/dist/hitl-gates.js +67 -1
  57. package/dist/hitl-lote.js +30 -15
  58. package/dist/hitl-presentation.js +10 -7
  59. package/dist/hitl-resumo.js +33 -12
  60. package/dist/hitl-texto-livre.js +150 -0
  61. package/dist/hosts.js +86 -1
  62. package/dist/index.js +602 -38
  63. package/dist/indice-vetorial.js +317 -0
  64. package/dist/init.js +13 -0
  65. package/dist/integracoes-locais.js +1 -1
  66. package/dist/intelligence-benchmark-contract.js +570 -0
  67. package/dist/intelligence-graph-cli.js +430 -0
  68. package/dist/intelligence-graph-contract.js +577 -0
  69. package/dist/intelligence-graph-extract-md.js +469 -0
  70. package/dist/intelligence-graph-extract-ts.js +810 -0
  71. package/dist/intelligence-graph-extract.js +369 -0
  72. package/dist/intelligence-graph-index.js +512 -0
  73. package/dist/intelligence-graph-parsers.js +327 -0
  74. package/dist/intelligence-graph-query.js +344 -0
  75. package/dist/intelligence-graph-repo.js +187 -0
  76. package/dist/leases.js +84 -0
  77. package/dist/ledger.js +14 -0
  78. package/dist/maestro-cli.js +10 -1
  79. package/dist/maestro-contract.js +5 -1
  80. package/dist/maestro-discovery.js +16 -2
  81. package/dist/maestro-snapshot.js +6 -2
  82. package/dist/manifest.js +142 -5
  83. package/dist/master-batch.js +3 -2
  84. package/dist/master-digest.js +2 -2
  85. package/dist/master-nota.js +203 -0
  86. package/dist/master.js +4 -0
  87. package/dist/mcp-experiencia.js +164 -0
  88. package/dist/mcp-git.js +4 -0
  89. package/dist/mcp-install.js +3 -1
  90. package/dist/mcp-maestro.js +3 -2
  91. package/dist/mcp-server.js +84 -5
  92. package/dist/memoria.js +147 -1
  93. package/dist/network-roadmap.js +907 -0
  94. package/dist/ocupacao.js +38 -2
  95. package/dist/onboarding.js +113 -7
  96. package/dist/orkmind.js +165 -9
  97. package/dist/phase.js +166 -13
  98. package/dist/policies.js +2 -0
  99. package/dist/projeto-alvo.js +551 -0
  100. package/dist/prompts.js +1 -1
  101. package/dist/pulse-consentimento.js +4 -2
  102. package/dist/pulse-delivery.js +5 -0
  103. package/dist/pulse-resposta.js +674 -49
  104. package/dist/pulse.js +21 -0
  105. package/dist/retry.js +51 -8
  106. package/dist/roadmap-reservas.js +262 -20
  107. package/dist/roadmap-status.js +213 -0
  108. package/dist/thread-close.js +3 -0
  109. package/dist/verify.js +4 -3
  110. package/eval/casos/orchestration-experience-pt-br.json +48 -0
  111. package/eval/casos/orchestration-experience.json +48 -0
  112. package/package.json +1 -1
  113. package/schemas/code-artifact-graph.v1.schema.json +581 -0
  114. package/schemas/graph-benchmark.v1.schema.json +2240 -0
  115. package/schemas/maestro-snapshot.schema.json +23 -0
  116. package/skills/README.md +7 -5
  117. package/skills/core/onboarding/SKILL.md +7 -0
  118. package/skills/core/orchestration-experience/SKILL.md +113 -0
  119. package/skills/core/orchestration-experience-pt-br/SKILL.md +112 -0
  120. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  121. package/skills/phases/go-implementation/SKILL.md +2 -0
@@ -24,7 +24,7 @@ function listarBatch(raiz, todas = false) {
24
24
  return (0, master_1.pendentesDeScore)(raiz, todas).map(p => ({ thread: p.thread.id, nome: p.thread.nome, entregue: p.entregou,
25
25
  proposta: p.thread.score_proposto ?? null, assinatura: p.thread.score_proposto ? assinaturaProposta(p.thread.score_proposto) : null }));
26
26
  }
27
- function ratificarBatch(raiz, selecao, por) {
27
+ function ratificarBatch(raiz, selecao, por, prova) {
28
28
  (0, master_1.exigirAutoriaHumana)(por);
29
29
  if (!selecao.length || new Set(selecao.map(s => s.thread)).size !== selecao.length)
30
30
  throw new Error('selecione threads distintas explicitamente');
@@ -34,7 +34,8 @@ function ratificarBatch(raiz, selecao, por) {
34
34
  if (!item?.proposta || item.assinatura !== s.assinatura)
35
35
  throw new Error(`proposta ausente ou alterada: ${s.thread}; revise o batch`);
36
36
  const p = item.proposta;
37
- const opcoes = { score: p.valor, justificativa: p.justificativa, classes: s.classe ? [s.classe] : p.classes, resumo: p.resumo, por };
37
+ const opcoes = { score: p.valor, justificativa: p.justificativa, classes: s.classe ? [s.classe] : p.classes, resumo: p.resumo, por,
38
+ ...(prova ? { prova } : {}) };
38
39
  (0, master_1.preflightMaster)(raiz, s.thread, opcoes);
39
40
  return { id: s.thread, opcoes };
40
41
  });
@@ -100,14 +100,14 @@ function lerDigest(raiz, semana) {
100
100
  throw new Error('digest inválido');
101
101
  return job;
102
102
  }
103
- function responderLoteDigest(raiz, resposta, por) {
103
+ function responderLoteDigest(raiz, resposta, por, prova) {
104
104
  const partes = resposta.trim().split(/\s+/);
105
105
  if (partes.length !== 3 || partes[0] !== 'ratificar-lote')
106
106
  throw new Error('resposta de lote inválida');
107
107
  const job = lerDigest(raiz, partes[1]);
108
108
  if (!job.concluidoEm || partes[2] !== job.assinatura)
109
109
  throw new Error('lote não entregue ou assinatura divergente');
110
- return (0, master_batch_1.ratificarBatch)(raiz, job.selecao, por);
110
+ return (0, master_batch_1.ratificarBatch)(raiz, job.selecao, por, prova);
111
111
  }
112
112
  function enviarDigest(opcoes) {
113
113
  const quando = opcoes.quando ?? new Date().toISOString(), semana = sextaLocal(quando);
@@ -0,0 +1,203 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.ERRO_PROVA_DE_CANAL = exports.CONTRATO_PEDIDO_DE_NOTA = void 0;
37
+ exports.exigirNotaSemHost = exigirNotaSemHost;
38
+ exports.arquivoDasNotas = arquivoDasNotas;
39
+ exports.pedirNota = pedirNota;
40
+ exports.pedidoDeNotaDoCodigo = pedidoDeNotaDoCodigo;
41
+ exports.textoDoPedidoDeNota = textoDoPedidoDeNota;
42
+ exports.lerNota = lerNota;
43
+ exports.reciboDoCanal = reciboDoCanal;
44
+ exports.mensagemJaUsadaEmNota = mensagemJaUsadaEmNota;
45
+ /**
46
+ * RM-048 (item 8, D8): a nota do MASTER com prova de origem humana.
47
+ *
48
+ * Ate aqui toda nota gravava o nome que viesse em `--por`: a `ork_master` do OpenClaw repassava o
49
+ * parametro `quem` do modelo, e `master ratificar`, `batch --aceitar` e `digest responder` idem.
50
+ * `autoriaHumana` so recusa nome de agente; ela mesma diz que nao e autenticacao. O dono pediu que
51
+ * a nota dada por ferramenta de host passe pela MESMA prova de canal que o gate ja exige.
52
+ *
53
+ * O caminho com prova e este, e ele nao cria autenticacao nova:
54
+ *
55
+ * 1. `ork master pedir <thread>` guarda um pedido de nota com codigo curto e devolve a linha
56
+ * "<codigo> <0 a 5> <porque>" para o canal mostrar ao dono;
57
+ * 2. o dono responde no Telegram; o ingresso (Hermes ou OpenClaw) assina o texto dele no
58
+ * endereco do pulse, com a chave do proprio canal, e o nucleo confere com a mesma
59
+ * `autenticarResposta` de sempre, ANTES de ler o conteudo;
60
+ * 3. a nota e gravada com `por` = o remetente autenticado (`telegram:<id>`) e com o recibo:
61
+ * canal, conta, mensagem, instante, o sha da prova e o envelope assinado guardado para a
62
+ * auditoria reconferir o HMAC depois.
63
+ *
64
+ * O teclado do digest semanal ("ratificar <thread> <assinatura> <classe>") passa pelo mesmo
65
+ * endereco. E o processo de host (Claude Code, Codex, Hermes, OpenClaw, sessao despachada) que
66
+ * tenta gravar nota com `--por` recebe `master.prova-de-canal`.
67
+ *
68
+ * LIMITE DITO: o canal do processo e declarado pelo ambiente (`canalDoProcesso`). Ele serve para
69
+ * RECUSAR, nunca para autorizar, e um agente que apague as variaveis do proprio host de proposito
70
+ * passa como terminal. Essa e a mesma fronteira do `humano-no-cli` do SHIP; fecha-la de vez exige o
71
+ * dialogo nativo do host para a nota, que fica fora desta entrega.
72
+ */
73
+ const fs = __importStar(require("node:fs"));
74
+ const path = __importStar(require("node:path"));
75
+ const node_crypto_1 = require("node:crypto");
76
+ const estado_thread_1 = require("./estado-thread");
77
+ const thread_1 = require("./thread");
78
+ const ledger_1 = require("./ledger");
79
+ const thread_close_1 = require("./thread-close");
80
+ const conducao_1 = require("./conducao");
81
+ const pulse_consentimento_1 = require("./pulse-consentimento");
82
+ const monitor_lock_1 = require("./monitor-lock");
83
+ exports.CONTRATO_PEDIDO_DE_NOTA = 'ork.master-nota/v1';
84
+ exports.ERRO_PROVA_DE_CANAL = 'master.prova-de-canal';
85
+ const sha = (s) => (0, node_crypto_1.createHash)('sha256').update(s).digest('hex');
86
+ /**
87
+ * D8: nota em nome de pessoa, vinda de processo de host, e recusada. O canal e lido SO do
88
+ * ambiente: `--canal` em argv nao conta, porque quem escreve argv e justamente quem chama.
89
+ */
90
+ function exigirNotaSemHost(ambiente = process.env) {
91
+ let canal;
92
+ try {
93
+ canal = (0, conducao_1.canalDoProcesso)(undefined, ambiente);
94
+ }
95
+ catch {
96
+ canal = 'mcp';
97
+ }
98
+ // B2 do CHECK: `ORK_CANAL=cli` NAO absolve. A sessao despachada herda `ORK_CANAL=cli` quando a
99
+ // thread e conduzida do terminal, e o Claude Code e o Codex marcam os proprios processos. Qualquer
100
+ // marca de host ou de despacho recusa, mesmo com o canal declarado como terminal.
101
+ const marca = canal !== 'cli' ? canal
102
+ : ambiente.ORK_DISPATCH_ID ? 'sessão despachada (ORK_DISPATCH_ID)'
103
+ : ambiente.CLAUDECODE === '1' ? 'claude-code (CLAUDECODE)'
104
+ : ambiente.CODEX_SANDBOX || ambiente.CODEX_SANDBOX_NETWORK_DISABLED ? 'codex (CODEX_SANDBOX)'
105
+ : (ambiente.HERMES_HOME ?? '').trim() ? 'hermes (HERMES_HOME)' : undefined;
106
+ if (marca) {
107
+ throw new Error(`${exports.ERRO_PROVA_DE_CANAL}: nota em nome de pessoa vinda de ${marca} exige a prova de canal do ingresso. ` +
108
+ 'Peça a nota com ork master pedir <thread>: o dono responde pelo Telegram com o código, e a nota é gravada com o recibo.');
109
+ }
110
+ }
111
+ function arquivoDasNotas(raiz, estadoDir) {
112
+ return path.join(estadoDir ?? path.join((0, estado_thread_1.raizDoEstado)(raiz), '.orkastery', 'monitor'), 'master-notas.json');
113
+ }
114
+ function lerNotas(raiz, estadoDir) {
115
+ const arquivo = arquivoDasNotas(raiz, estadoDir);
116
+ if (!fs.existsSync(arquivo))
117
+ return {};
118
+ const v = JSON.parse(fs.readFileSync(arquivo, 'utf8'));
119
+ if (v.contrato !== exports.CONTRATO_PEDIDO_DE_NOTA || !v.notas || typeof v.notas !== 'object')
120
+ throw new Error('pedidos de nota: estado inválido; exige inspeção');
121
+ return v.notas;
122
+ }
123
+ function gravarNotas(raiz, notas, estadoDir) {
124
+ const arquivo = arquivoDasNotas(raiz, estadoDir);
125
+ fs.mkdirSync(path.dirname(arquivo), { recursive: true });
126
+ const tmp = `${arquivo}.${process.pid}.tmp`;
127
+ fs.writeFileSync(tmp, JSON.stringify({ contrato: exports.CONTRATO_PEDIDO_DE_NOTA, notas }) + '\n', { mode: 0o600 });
128
+ fs.renameSync(tmp, arquivo);
129
+ }
130
+ /**
131
+ * Pede a nota ao dono. Idempotente: a mesma entrega sem nota devolve o MESMO codigo, para a
132
+ * linha que o dono recebeu nao vencer. So entrega provada recebe pedido de nota.
133
+ */
134
+ function pedirNota(raiz, thread, opcoes = {}) {
135
+ const quando = opcoes.quando ?? new Date().toISOString();
136
+ (0, thread_close_1.exigirEntrega)(raiz, thread);
137
+ const estadoDir = opcoes.estadoDir ?? path.join((0, estado_thread_1.raizDoEstado)(raiz), '.orkastery', 'monitor');
138
+ // A4 do CHECK: o registro dos pedidos de nota tem dois escritores (pedir e o receptor do pulse);
139
+ // um de cada vez, pelo mesmo lock da conversa.
140
+ return (0, monitor_lock_1.comLockDaConversa)(estadoDir, () => {
141
+ const notas = lerNotas(raiz, estadoDir);
142
+ const existente = Object.values(notas).find(n => n.thread === thread);
143
+ if (existente && !notaDada(raiz, existente))
144
+ return existente;
145
+ // A1 do CHECK: o codigo da nota nao repete o de gate aberto nem o do resumo (quem chama passa).
146
+ const codigo = (0, pulse_consentimento_1.gerarCodigo)([...Object.keys(notas), ...(opcoes.codigosEmUso ?? [])]);
147
+ const pedido = { contrato: exports.CONTRATO_PEDIDO_DE_NOTA, pedidoId: (0, node_crypto_1.randomUUID)(), thread, codigo, criadoEm: quando };
148
+ (0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, thread), thread, ledger_1.TIPOS_DE_EVENTO.notaPedida, { fase: 'MASTER', pedidoId: pedido.pedidoId, codigo });
149
+ gravarNotas(raiz, { ...Object.fromEntries(Object.entries(notas).filter(([, n]) => n.thread !== thread)), [codigo]: pedido }, estadoDir);
150
+ return pedido;
151
+ });
152
+ }
153
+ /** A nota deste pedido ja foi dada pelo canal. */
154
+ function notaDada(raiz, p) {
155
+ return (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, p.thread)).some(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.masterConcluido &&
156
+ e.notaPedida?.pedidoId === p.pedidoId);
157
+ }
158
+ /** O pedido de nota que usa este codigo, quando ha um. */
159
+ function pedidoDeNotaDoCodigo(raiz, codigo, estadoDir) {
160
+ try {
161
+ return lerNotas(raiz, estadoDir)[codigo.trim().toUpperCase()];
162
+ }
163
+ catch {
164
+ return undefined;
165
+ }
166
+ }
167
+ /** A linha que o canal mostra ao dono. Uma linha de contexto e uma de resposta. */
168
+ function textoDoPedidoDeNota(raiz, p, canal) {
169
+ const t = (0, thread_1.lerThread)(raiz, p.thread);
170
+ const tg = canal === 'telegram';
171
+ return [
172
+ `${tg ? '🧾 ' : ''}Nota da entrega ${p.thread} (${t.nome.slice(0, 80)})`,
173
+ `${tg ? '• ' : '- '}Sem nota, vale a aceita por omissão, com o índice derivado do ledger ao lado.`,
174
+ `${tg ? '↩️ ' : ''}Responda: ${p.codigo} <0 a 5> <porquê>, por exemplo: ${p.codigo} 4 entregou o que pedi.`,
175
+ ].join('\n');
176
+ }
177
+ /** "4 entregou o que pedi" vira nota e justificativa; o resto e devolvido como pergunta. */
178
+ function lerNota(resto) {
179
+ const m = /^\s*([0-5])(?:\s*\/\s*5)?\s*[-:,.]?\s+(\S[\s\S]{2,})$/.exec(resto);
180
+ return m ? { score: Number(m[1]), justificativa: m[2].trim() } : undefined;
181
+ }
182
+ /**
183
+ * O recibo que acompanha a nota no `master_done`. O envelope assinado inteiro fica guardado, para
184
+ * que a auditoria reconfira o HMAC do canal depois (`autenticarResposta` no instante recebido). Um
185
+ * arquivo por mensagem: a mesma mensagem nunca vira duas notas.
186
+ */
187
+ function reciboDoCanal(raiz, envelope, vinculo, estadoDir) {
188
+ const dir = path.join(estadoDir ?? path.join((0, estado_thread_1.raizDoEstado)(raiz), '.orkastery', 'monitor'), 'master-nota');
189
+ fs.mkdirSync(dir, { recursive: true, mode: 0o700 });
190
+ const arquivo = path.join(dir, `${sha(envelope.mensagem)}.json`);
191
+ const corpo = JSON.stringify({ contrato: exports.CONTRATO_PEDIDO_DE_NOTA, vinculo, envelope }) + '\n';
192
+ if (!fs.existsSync(arquivo))
193
+ fs.writeFileSync(arquivo, corpo, { mode: 0o600, flag: 'wx' });
194
+ return {
195
+ origem: envelope.origem, canal: envelope.canal ?? null, conta: envelope.conta ?? null, mensagem: envelope.mensagem,
196
+ recebidoEm: envelope.recebidoEm, recibo: sha(envelope.prova), contratoResposta: envelope.canal === undefined ? 'ork.hitl-answer/v1' : 'ork.hitl-answer/v2',
197
+ evidencia: path.relative((0, estado_thread_1.raizDoEstado)(raiz), arquivo), evidenciaSha256: sha(fs.readFileSync(arquivo, 'utf8')), ...vinculo,
198
+ };
199
+ }
200
+ /** Mensagem ja usada numa nota desta thread nao vale de novo, igual ao gate. */
201
+ function mensagemJaUsadaEmNota(raiz, thread, mensagem) {
202
+ return (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, thread)).some(e => e.tipo === ledger_1.TIPOS_DE_EVENTO.masterConcluido && e.mensagem === mensagem);
203
+ }
package/dist/master.js CHANGED
@@ -88,6 +88,7 @@ const horario_1 = require("./horario");
88
88
  const indice_1 = require("./indice");
89
89
  const ship_1 = require("./ship");
90
90
  const fabrica_publicar_1 = require("./fabrica-publicar");
91
+ const fechamento_1 = require("./fechamento");
91
92
  /** Identificador do contrato congelado do MASTER log. */
92
93
  exports.CONTRATO_MASTER_LOG = 'ork.master-log/v1';
93
94
  /** Versao do contrato. Uma mudanca de contrato exige uma versao nova, nao um campo novo. */
@@ -392,7 +393,10 @@ function registrarMaster(raiz, id, opcoes) {
392
393
  classesDeFalha: masterLog.classesDeFalha, regime: postmortem.score.regime,
393
394
  contrato: exports.CONTRATO_MASTER_LOG, arquivo: path.relative((0, estado_thread_1.raizDoEstado)(raiz), r.caminhoMasterLog), estado: 'thread fechada',
394
395
  masterLogSha256: hashDocumento(r.caminhoMasterLog), postmortemSha256: hashDocumento(r.caminhoPostmortem),
396
+ ...(opcoes.prova ? { prova: 'ingresso-autenticado', ...opcoes.prova } : {}),
395
397
  });
398
+ // RM-037 (rm037noite): a thread fechada solta o que segurava, com registro no ledger dela.
399
+ (0, fechamento_1.liberarAoFechar)(raiz, id);
396
400
  return r;
397
401
  }
398
402
  exports.CONTRATO_MASTER_PENDENTE = 'ork.master-pending/v1';
@@ -0,0 +1,164 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.consultaExperienciaSchema = void 0;
37
+ exports.apresentarConsulta = apresentarConsulta;
38
+ exports.consultarPainel = consultarPainel;
39
+ exports.criarLeitorExperiencia = criarLeitorExperiencia;
40
+ exports.registrarConsultasExperiencia = registrarConsultasExperiencia;
41
+ /** Consultas do projeto fixado. Nunca reserva item, publica máquina ou executa push. */
42
+ const fs = __importStar(require("node:fs"));
43
+ const path = __importStar(require("node:path"));
44
+ const node_child_process_1 = require("node:child_process");
45
+ const node_crypto_1 = require("node:crypto");
46
+ const zod_1 = require("zod");
47
+ const roadmap_reservas_1 = require("./roadmap-reservas");
48
+ const fabrica_estado_1 = require("./fabrica-estado");
49
+ const mcp_ship_1 = require("./mcp-ship");
50
+ const mcp_git_1 = require("./mcp-git");
51
+ exports.consultaExperienciaSchema = zod_1.z.object({}).strict();
52
+ function apresentarConsulta(painel) {
53
+ const estado = painel.atualizado ? 'atualizado' : painel.ponta ? 'desatualizado' : 'indisponivel';
54
+ return { estado, dados: estado === 'indisponivel' ? null : painel,
55
+ motivo: estado === 'atualizado' ? null : 'mcp.experiencia.remoto.indisponivel' };
56
+ }
57
+ const indisponivel = () => ({ estado: 'indisponivel', dados: null, motivo: 'mcp.experiencia.consulta.indisponivel' });
58
+ /** Chamada somente depois de validar a fixação do worker. */
59
+ function consultarPainel(raiz, tipo) {
60
+ try {
61
+ return apresentarConsulta(tipo === 'reservas' ? (0, roadmap_reservas_1.listarReservas)(raiz, { remoto: 'origin' })
62
+ : (0, fabrica_estado_1.lerFabrica)(raiz, { remoto: 'origin', timeoutMs: 15000 }));
63
+ }
64
+ catch {
65
+ return indisponivel();
66
+ }
67
+ }
68
+ /** Usa as mesmas guardas de transporte do núcleo; não cria autorização de SHIP. */
69
+ function assinatura(raiz, transporte) {
70
+ (0, mcp_ship_1.criarPerfilShipMcp)(raiz, transporte);
71
+ const g = path.join(raiz, '.git');
72
+ (0, mcp_git_1.perfilGitMcp)(raiz, g, g, 'consulta-experiencia', []);
73
+ const st = fs.statSync(raiz);
74
+ return (0, node_crypto_1.createHash)('sha256').update(JSON.stringify([st.dev, st.ino, fs.realpathSync(raiz),
75
+ (0, mcp_git_1.gitPassivoMcp)(raiz, ['remote', 'get-url', '--all', 'origin']),
76
+ (0, mcp_git_1.gitPassivoMcp)(raiz, ['config', '--null', '--list'])])).digest('hex');
77
+ }
78
+ const PRAZO_DA_CONSULTA_MS = 90000;
79
+ /**
80
+ * O worker herda somente ambiente permitido; nunca modifica env do servidor. Roda assíncrono para
81
+ * o servidor MCP seguir atendendo as outras tools durante a consulta remota, e em grupo próprio:
82
+ * prazo ou cancelamento da chamada matam o worker e o git que ele abriu.
83
+ */
84
+ function criarLeitorExperiencia(raiz, transporte, prazoMs = PRAZO_DA_CONSULTA_MS) {
85
+ let fixacao = null;
86
+ try {
87
+ fixacao = assinatura(raiz, transporte);
88
+ }
89
+ catch { /* ferramenta continua descoberta, com lacuna explícita */ }
90
+ return (tipo, signal) => new Promise(resolve => {
91
+ if (!fixacao || signal?.aborted) {
92
+ resolve(indisponivel());
93
+ return;
94
+ }
95
+ const grupo = process.platform !== 'win32';
96
+ const filho = (0, node_child_process_1.spawn)(process.execPath, [__filename, '--consulta-experiencia'], {
97
+ cwd: raiz, env: { ...(0, mcp_git_1.ambienteGitMcp)(), SSH_AUTH_SOCK: process.env.SSH_AUTH_SOCK },
98
+ stdio: ['pipe', 'pipe', 'ignore'], detached: grupo,
99
+ });
100
+ let saida = '', terminado = false;
101
+ const concluir = (r) => {
102
+ if (terminado)
103
+ return;
104
+ terminado = true;
105
+ clearTimeout(prazo);
106
+ signal?.removeEventListener('abort', abortar);
107
+ resolve(r);
108
+ };
109
+ const abortar = () => {
110
+ if (terminado)
111
+ return;
112
+ filho.stdout?.destroy();
113
+ try {
114
+ if (filho.pid)
115
+ process.kill(grupo ? -filho.pid : filho.pid, 'SIGKILL');
116
+ }
117
+ catch { /* já terminou */ }
118
+ concluir(indisponivel());
119
+ };
120
+ const prazo = setTimeout(abortar, prazoMs);
121
+ signal?.addEventListener('abort', abortar, { once: true });
122
+ filho.stdout.setEncoding('utf8');
123
+ filho.stdout.on('data', (parte) => { saida += parte; if (saida.length > 1024 * 1024)
124
+ abortar(); });
125
+ filho.stdin.on('error', () => undefined);
126
+ filho.on('error', () => concluir(indisponivel()));
127
+ filho.on('close', (codigo, sinal) => {
128
+ if (codigo !== 0 || sinal) {
129
+ concluir(indisponivel());
130
+ return;
131
+ }
132
+ try {
133
+ concluir(JSON.parse(saida));
134
+ }
135
+ catch {
136
+ concluir(indisponivel());
137
+ }
138
+ });
139
+ filho.stdin.end(JSON.stringify({ raiz, transporte, fixacao, tipo }));
140
+ });
141
+ }
142
+ function registrarConsultasExperiencia(registrar, opcoes) {
143
+ const ler = criarLeitorExperiencia(opcoes.raiz, opcoes.transporte);
144
+ for (const [nome, tipo] of [['ork_roadmap_reservas', 'reservas'], ['ork_fabrica', 'fabrica']]) {
145
+ registrar(nome, { description: 'Consulta de ' + tipo + ' do projeto e origin fixados; distingue remoto atualizado, cópia desatualizada e indisponibilidade. Não reserva nem publica.',
146
+ inputSchema: exports.consultaExperienciaSchema, annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true } }, async (_args, extra) => {
147
+ opcoes.carregar();
148
+ return { content: [{ type: 'text', text: JSON.stringify(await ler(tipo, extra.signal)) }] };
149
+ });
150
+ }
151
+ }
152
+ if (require.main === module && process.argv[2] === '--consulta-experiencia') {
153
+ try {
154
+ const p = zod_1.z.object({ raiz: zod_1.z.string(), transporte: zod_1.z.enum(['github-ssh', 'bare-local']),
155
+ fixacao: zod_1.z.string().regex(/^[a-f0-9]{64}$/), tipo: zod_1.z.enum(['reservas', 'fabrica']) }).strict()
156
+ .parse(JSON.parse(fs.readFileSync(0, 'utf8')));
157
+ if (assinatura(p.raiz, p.transporte) !== p.fixacao)
158
+ throw Error('scope.changed');
159
+ process.stdout.write(JSON.stringify(consultarPainel(p.raiz, p.tipo)));
160
+ }
161
+ catch {
162
+ process.stdout.write(JSON.stringify(indisponivel()));
163
+ }
164
+ }
package/dist/mcp-git.js CHANGED
@@ -131,6 +131,10 @@ function configPassiva(chave, valor, credenciaisInertes = false) {
131
131
  return valor.length <= 8192 && !valor.includes('\0');
132
132
  if (['user.name', 'user.email'].includes(chave))
133
133
  return !!valor.trim() && valor.length <= 512 && !/[\x00-\x1f]/.test(valor);
134
+ // RM-051: só afrouxa a checagem de dono, e o Git só a lê no sistema ou no global; o repositório
135
+ // continua conferido chave a chave aqui. A imagem do runner hospedado do GitHub grava `safe.directory=*`.
136
+ if (chave === 'safe.directory')
137
+ return valor.length <= 4096 && !/[\x00-\x1f\x7f]/.test(valor);
134
138
  // RM-037 (defeitosdeco D-3): a seção do próprio produto (por exemplo `orkastery.cortefeito`, lida
135
139
  // pela trava do corte no pre-push). O Git nunca lê essa seção, então ela não executa nada.
136
140
  if (/^orkastery\.[a-z0-9-]+$/.test(chave))
@@ -47,7 +47,9 @@ const node_util_1 = require("node:util");
47
47
  const smol_toml_1 = require("smol-toml");
48
48
  const manifest_1 = require("./manifest");
49
49
  exports.TOOLS_DONO_CODEX = ['ork_thread_new', 'ork_phase_run', 'ork_request_decision'];
50
- exports.TOOLS_FILHO_CODEX = ['ork_artifact_write', 'ork_claim_add', 'ork_git_commit', 'ork_verify', 'ork_ship'];
50
+ // RM-037 (rm037defeito, defeito 1): a decisao autonoma da sessao entra com as outras mutacoes do filho;
51
+ // sem o grant, o codex com approval_policy never recusaria a chamada.
52
+ exports.TOOLS_FILHO_CODEX = ['ork_artifact_write', 'ork_claim_add', 'ork_decision_record', 'ork_git_commit', 'ork_verify', 'ork_ship'];
51
53
  // Brain mutation tools are deliberately excluded from the existing opt-in bundle.
52
54
  // A native integration must request exact startup grants plus scoped activation.
53
55
  var company_brain_mcp_1 = require("./company-brain-mcp");
@@ -9,7 +9,7 @@ const maestro_cli_1 = require("./maestro-cli");
9
9
  exports.maestroInputSchema = zod_1.z.object({ threadId: zod_1.z.string().regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/).optional(),
10
10
  section: zod_1.z.enum(maestro_contract_1.SECTION_NAMES).optional(), offset: zod_1.z.number().int().min(0).max(100000).optional() }).strict();
11
11
  function registerMaestro(register, options) {
12
- register('ork_maestro', { description: 'Panorama Maestro somente leitura, fixado ao projeto e ao escopo da sessão; cobertura, lacunas e próximas ações pelos endpoints existentes.',
12
+ register('ork_maestro', { description: 'Panorama Maestro somente leitura, fixado ao projeto e ao escopo da sessão; cobertura, lacunas e próximas ações pelos endpoints existentes. Não lê o roadmap, as reservas nem as outras máquinas (veja notConsulted): zero threads nunca é roadmap vazio; para o roadmap, com as threads de todas as máquinas, use ork_network_roadmap.',
13
13
  inputSchema: exports.maestroInputSchema, annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false } }, async (args) => {
14
14
  options.load();
15
15
  if ((args.section === undefined) !== (args.offset === undefined))
@@ -17,7 +17,8 @@ function registerMaestro(register, options) {
17
17
  const threadId = args.threadId ?? options.threadId;
18
18
  if (threadId)
19
19
  options.checkThread(threadId);
20
- const context = (0, maestro_discovery_1.discoverMaestro)({ cwd: options.root, pinned: options.root });
20
+ // RM-052 (D5): fixado no projeto servido; os outros projetos da maquina nao entram na conta.
21
+ const context = (0, maestro_discovery_1.discoverMaestro)({ cwd: options.root, pinned: options.root, countOtherProjects: false });
21
22
  const result = (0, maestro_cli_1.readMaestro)(context, { threadId, native: true, offsets: args.section ? { [args.section]: args.offset } : undefined,
22
23
  host: { tools: options.tools(), child: !!options.threadId } });
23
24
  return { content: [{ type: 'text', text: JSON.stringify(result) }] };
@@ -56,6 +56,8 @@ const leases_1 = require("./leases");
56
56
  const hitl_gates_1 = require("./hitl-gates");
57
57
  const hitl_contract_1 = require("./hitl-contract");
58
58
  const hitl_presentation_1 = require("./hitl-presentation");
59
+ const roadmap_status_1 = require("./roadmap-status");
60
+ const network_roadmap_1 = require("./network-roadmap");
59
61
  const hitl_sessions_1 = require("./hitl-sessions");
60
62
  const hitl_local_1 = require("./hitl-local");
61
63
  const mcp_artifacts_1 = require("./mcp-artifacts");
@@ -65,6 +67,7 @@ const modos_1 = require("./modos");
65
67
  const versao_1 = require("./versao");
66
68
  const mcp_verify_1 = require("./mcp-verify");
67
69
  const conducao_1 = require("./conducao");
70
+ const decisao_autonoma_1 = require("./decisao-autonoma");
68
71
  const conducao_texto_1 = require("./conducao-texto");
69
72
  const mcp_ship_1 = require("./mcp-ship");
70
73
  const company_brain_mcp_1 = require("./company-brain-mcp");
@@ -72,6 +75,8 @@ const claude_bg_1 = require("./adapters/claude-bg");
72
75
  const session_watcher_claude_1 = require("./session-watcher-claude");
73
76
  const runtime_profiles_1 = require("./runtime-profiles");
74
77
  const mcp_maestro_1 = require("./mcp-maestro");
78
+ const projeto_alvo_1 = require("./projeto-alvo");
79
+ const mcp_experiencia_1 = require("./mcp-experiencia");
75
80
  const identidade = zod_1.z.string().min(1).max(80).regex(/^[a-zA-Z0-9][a-zA-Z0-9._-]*$/);
76
81
  const daThread = zod_1.z.object({ threadId: identidade }).strict();
77
82
  const daDecisao = daThread.extend({ pedidoId: identidade }).strict();
@@ -297,15 +302,33 @@ function criarServidorMcp(opcoes) {
297
302
  throw Error('lease.busy: ' + conflitos.map(l => l.nome + ' (' + l.thread + ')').join(', '));
298
303
  }
299
304
  };
300
- const server = new index_js_1.Server({ name: 'orkastery', version: versao_1.VERSAO_DO_ORK }, { capabilities: { tools: {} }, instructions: 'Opere somente este projeto pelas ferramentas do nucleo. Decisoes humanas usam ork_request_decision e o dialogo do host; argumentos de ferramenta nunca sao respostas.' });
305
+ const server = new index_js_1.Server({ name: 'orkastery', version: versao_1.VERSAO_DO_ORK }, { capabilities: { tools: {} }, instructions: 'Opere somente este projeto pelas ferramentas do nucleo. O parametro opcional projeto so confere o projeto servido; nunca troca a raiz. O status do roadmap vem de ork_network_roadmap, transportado como vem. Decisoes humanas usam ork_request_decision e o dialogo do host; argumentos de ferramenta nunca sao respostas.' });
306
+ /**
307
+ * RM-052 (D5): toda tool aceita `projeto`, e ele so CONFERE o projeto fixado no startup. Pedido de
308
+ * outro projeto recusa tipado, com o projeto servido como unico candidato; a raiz nunca muda aqui.
309
+ */
310
+ const conferirProjeto = (pedido) => {
311
+ const c = carregar(), nome = c.manifesto.project.name, abbrev = c.manifesto.project.abbrev, v = pedido.toLowerCase();
312
+ if (nome.toLowerCase() === v || (!!abbrev && abbrev.toLowerCase() === v))
313
+ return;
314
+ throw new projeto_alvo_1.ErroDeProjeto('projeto.fora-do-servidor', `este servidor MCP atende somente o projeto ${nome}; "${pedido}" nao e ele`, [{ nome, abbrev, raiz: (0, projeto_alvo_1.raizParaExibir)(raiz), remoto: (0, projeto_alvo_1.remotoDoProjeto)(raiz, c.manifesto.fabrica?.remoto ?? 'origin'), presente: true }], 'Use o servidor MCP instalado no outro projeto (ork mcp install --project <raiz>), ou o CLI com --projeto <nome>.');
315
+ };
316
+ const campoProjeto = zod_1.z.string().regex(projeto_alvo_1.PADRAO_DO_NOME_DE_PROJETO)
317
+ .describe('Nome ou abbrev do projeto pedido; este servidor atende somente o projeto fixado na instalacao');
301
318
  const ferramentas = new Map();
302
319
  function registrarTool(nome, config, executar) {
303
320
  if (opcoes.threadId && ['ork_thread_new', 'ork_phase_run', 'ork_request_decision', 'ork_preflight'].includes(nome))
304
321
  return;
305
- const schema = converterSchema(config.inputSchema, { $refStrategy: 'none' });
322
+ const entrada = config.inputSchema.extend({ projeto: campoProjeto.optional() });
323
+ const schema = converterSchema(entrada, { $refStrategy: 'none' });
306
324
  ferramentas.set(nome, { tool: { name: nome, description: config.description, annotations: config.annotations,
307
325
  inputSchema: schema },
308
- executar: async (raw, extra) => executar(config.inputSchema.parse(raw), extra) });
326
+ executar: async (raw, extra) => {
327
+ const { projeto, ...args } = entrada.parse(raw);
328
+ if (projeto !== undefined)
329
+ conferirProjeto(projeto);
330
+ return executar(args, extra);
331
+ } });
309
332
  }
310
333
  server.setRequestHandler(types_js_1.ListToolsRequestSchema, async () => ({ tools: [...ferramentas.values()].map(f => f.tool) }));
311
334
  server.setRequestHandler(types_js_1.CallToolRequestSchema, async (request, extra) => {
@@ -316,6 +339,8 @@ function criarServidorMcp(opcoes) {
316
339
  return await f.executar(request.params.arguments ?? {}, extra);
317
340
  }
318
341
  catch (e) {
342
+ if (e instanceof projeto_alvo_1.ErroDeProjeto)
343
+ return { ...resposta(e.recusa), isError: true };
319
344
  return { ...resposta({ erro: e.message }), isError: true };
320
345
  }
321
346
  });
@@ -344,6 +369,7 @@ function criarServidorMcp(opcoes) {
344
369
  });
345
370
  server.onclose = () => ingresso.fechar();
346
371
  (0, company_brain_mcp_1.registerBrainTools)(registrarTool, carregar, thread, opcoes.brainWrites ?? []);
372
+ (0, mcp_experiencia_1.registrarConsultasExperiencia)(registrarTool, { raiz, carregar, transporte: opcoes.transporteShip ?? 'github-ssh' });
347
373
  (0, mcp_maestro_1.registerMaestro)(registrarTool, { root: raiz, threadId: opcoes.threadId, load: carregar, checkThread: thread, tools: () => [...ferramentas.keys()] });
348
374
  registrarTool('ork_preflight', { description: 'Preflight contextual por modo e bloco, sem inventário global/modelos. Não autoriza despacho nem comprova entrega.',
349
375
  inputSchema: zod_1.z.object({ modo: modoDeConducao }).strict(), annotations: { readOnlyHint: true } }, async ({ modo }) => {
@@ -402,6 +428,35 @@ function criarServidorMcp(opcoes) {
402
428
  livre(threadId);
403
429
  return resposta((0, mcp_artifacts_1.escreverArtefatoMcp)(raiz, threadId, tipo, conteudo, expectedSha256));
404
430
  });
431
+ // RM-037 (rm037defeito, defeito 1): a decisao que a sessao toma sem perguntar ao dono. O sandbox do
432
+ // codex nao grava o ledger e o `ork decisao registrar` morria em EROFS. Mesma `registrarDecisao` do CLI:
433
+ // contrato v2, criterio que resolve e rastro no mesmo evento. Nao pergunta ao dono e nao toca gate.
434
+ const campoDaDecisao = zod_1.z.string().min(1).max(4096);
435
+ registrarTool('ork_decision_record', { description: 'Registra decisao ja tomada pela sessao sem perguntar ao dono (classe decidido), com criterio que resolve e rastro. Nao pergunta, nao responde nem aprova gate.',
436
+ inputSchema: daThread.extend({ decidido: campoDaDecisao, porque: campoDaDecisao, comoMudar: campoDaDecisao,
437
+ custoAgora: campoDaDecisao, custoDepois: campoDaDecisao,
438
+ criterio: zod_1.z.object({ tipo: zod_1.z.enum(['manifesto', 'ledger', 'medicao']), referencia: campoDaDecisao }).strict(),
439
+ quemDecidiu: zod_1.z.string().min(1).max(200), evidencia: campoDaDecisao, razao: campoDaDecisao.optional(),
440
+ reverte: zod_1.z.string().min(1).max(80).optional() }).strict(),
441
+ annotations: { readOnlyHint: false, destructiveHint: false } }, async ({ threadId, ...e }) => {
442
+ thread(threadId);
443
+ livre(threadId);
444
+ // A sessao decide por ela: o rastro nao pode nascer assinado em nome do dono.
445
+ if (/^\s*(?:o\s+|a\s+)?(?:dono|owner|builder|maestro)\b/i.test(e.quemDecidiu))
446
+ throw Error('mcp.decision.author: quemDecidiu e a sessao que decidiu, nunca o dono');
447
+ let registro;
448
+ try {
449
+ registro = (0, decisao_autonoma_1.registrarDecisao)(raiz, threadId, { decidido: e.decidido, porque: e.porque, comoMudar: e.comoMudar,
450
+ custoDeReverter: { agora: e.custoAgora, depois: e.custoDepois }, criterio: e.criterio, quemDecidiu: e.quemDecidiu,
451
+ evidencia: e.evidencia, ...(e.razao ? { razao: e.razao } : {}), ...(e.reverte ? { reverte: e.reverte } : {}), origem: 'mcp', host: opcoes.host,
452
+ despacho: opcoes.dispatchId ?? null });
453
+ }
454
+ catch (erro) {
455
+ throw Error((0, hitl_contract_1.recusaNaSuperficie)(erro.message, hitl_contract_1.CAMPOS_DA_DECISAO_NO_MCP));
456
+ }
457
+ const { pedido, evento } = registro;
458
+ return resposta({ ok: true, pedidoId: pedido.id, eventId: evento.eventId, fase: pedido.fase, reciboOficial: false });
459
+ });
405
460
  registrarTool('ork_claims_list', { description: 'Lista alegacoes e seu estado de verificacao registrado pelo nucleo.',
406
461
  inputSchema: daThread, annotations: { readOnlyHint: true } }, async ({ threadId }) => {
407
462
  thread(threadId);
@@ -423,6 +478,25 @@ function criarServidorMcp(opcoes) {
423
478
  });
424
479
  registrarTool('ork_hitl_pending', { description: 'Pedidos HITL atuais da thread, incluindo prazo; nao aprova nem abre pedido.',
425
480
  inputSchema: daThread, annotations: { readOnlyHint: true } }, async ({ threadId }) => resposta({ threadId, pendencias: pendencias(threadId) }));
481
+ registrarTool('ork_roadmap_status', { description: 'Status report do roadmap SO desta maquina (o checkout local), no formato aprovado pelo dono: grupos com icones, #HITL no que espera o dono e o fecho. Somente leitura; transporte o texto como vem. O cabecalho diz o projeto consultado e o que nao foi lido (reservas, outras maquinas). Para o status do roadmap com as threads de todas as maquinas, as reservas, as fontes e as lacunas, use ork_network_roadmap; nunca conclua sobre ele a partir de ork_maestro ou de threads.',
482
+ inputSchema: zod_1.z.object({}).strict(), annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: false } }, async () => {
483
+ const c = carregar();
484
+ // RM-052: origem `instalacao`; o servidor fixado nao revela os outros projetos da maquina.
485
+ const consulta = (0, projeto_alvo_1.consultaDoProjeto)(c, { origem: 'instalacao', outrosProjetos: false,
486
+ lido: ['roadmap (docs/roadmap)', projeto_alvo_1.FORA_DA_CONSULTA.threadsDaMaquina], naoLido: [projeto_alvo_1.FORA_DA_CONSULTA.reservas, 'threads de outras máquinas (ork network roadmap)'] });
487
+ const status = (0, roadmap_status_1.montarStatusDoRoadmap)(raiz, { projeto: c.manifesto.project.name, consulta });
488
+ return resposta({ texto: (0, roadmap_status_1.textoDoStatusDoRoadmap)(status), status });
489
+ });
490
+ /**
491
+ * RM-054 (fatia 2, D-G5): o roadmap da rede do projeto servido, em todas as maquinas dele. O servidor
492
+ * fixado nao le o registro nem revela os outros projetos (D5 da RM-052); o texto e o do CLI.
493
+ */
494
+ registrarTool('ork_network_roadmap', { description: 'Roadmap da rede do projeto servido, somente leitura: o status report do roadmap (RM-048) com as threads de TODAS as maquinas, as reservas, as threads por maquina com a idade da batida, a fonte e a hora de cada parte e as lacunas tipadas. E a fonte do status do roadmap: transporte o texto como vem, sem reescrever nem resumir. Lacuna e "Nao consultado" sao o que nao foi lido: nunca conclua "roadmap vazio" nem "nenhuma maquina publicou" a partir deles. Este servidor le so o projeto fixado; os outros projetos vem do CLI ork network roadmap.',
495
+ inputSchema: zod_1.z.object({}).strict(), annotations: { readOnlyHint: true, destructiveHint: false, openWorldHint: true } }, async () => {
496
+ carregar();
497
+ const panorama = (0, network_roadmap_1.montarPanoramaDaRede)({ fixado: raiz });
498
+ return resposta({ texto: (0, network_roadmap_1.textoDoPanoramaDaRede)(panorama), panorama });
499
+ });
426
500
  registrarTool('ork_observe', { description: 'Observa uma vez o progresso canonico e pedidos da thread. Nao cria monitor ou despacho.',
427
501
  inputSchema: daThread, annotations: { readOnlyHint: true } }, async ({ threadId }) => {
428
502
  return resposta(observar(threadId));
@@ -439,14 +513,19 @@ function criarServidorMcp(opcoes) {
439
513
  while (!fs.existsSync(existente))
440
514
  existente = path.dirname(existente);
441
515
  confinado(existente);
442
- return resposta((0, thread_1.novaThread)(c, { nome, modo: modo ?? c.manifesto.conduction.default_mode, criarWorktree: true }));
516
+ const criada = (0, thread_1.novaThread)(c, { nome, modo: modo ?? c.manifesto.conduction.default_mode, criarWorktree: true });
517
+ // RM-052 (D7): thread nova, projeto conhecido pelo nome nesta maquina; o registro nunca derruba a criacao.
518
+ if (criada.gravada)
519
+ (0, projeto_alvo_1.registrarProjetoEmSilencio)(raiz, 'mcp thread new');
520
+ return resposta(criada);
443
521
  });
444
522
  registrarTool('ork_phase_run', { description: 'Despacha fase autorizada pelo nucleo, preservando policies, estado e runtime/modelo. dryRun nao inicia modelo, mas pode registrar prompt/eventos.',
445
523
  inputSchema: fase, annotations: { readOnlyHint: false, destructiveHint: false } }, async (args) => {
446
524
  const t = thread(args.threadId);
447
525
  livre(t.id);
448
526
  const { threadId, fase: f, ...op } = args;
449
- return resposta((0, phase_1.rodarFase)(carregar(), threadId, { ...op, fase: (0, thread_1.exigirFase)(t, f), canal: conducao.canal }));
527
+ // RM-037 (A3): o MCP nao roda a suite para a baseline do despacho; a falta dela volta como pendencia.
528
+ return resposta((0, phase_1.rodarFase)(carregar(), threadId, { ...op, fase: (0, thread_1.exigirFase)(t, f), canal: conducao.canal, baselinePeloDespacho: false }));
450
529
  });
451
530
  registrarTool('ork_gate_request', { description: 'Abre pedido de gate somente quando pausa ou escalacao tipada esta comprovada no nucleo; nao aprova.',
452
531
  inputSchema: daThread.extend({ motivo: zod_1.z.enum(['human.pending', 'policy.violation', 'cost.violation',