@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.
- package/adapters/README.md +11 -0
- package/adapters/claude-code/.claude-plugin/plugin.json +3 -1
- package/adapters/claude-code/agents/ork-check.md +1 -1
- package/adapters/claude-code/agents/ork-go.md +2 -2
- package/adapters/claude-code/agents/ork-goal.md +1 -1
- package/adapters/claude-code/agents/ork-master.md +1 -1
- package/adapters/claude-code/agents/ork-plan.md +1 -1
- package/adapters/claude-code/agents/ork-ship.md +1 -1
- package/adapters/claude-code/commands/master.md +11 -8
- package/adapters/claude-code/commands/ork.md +8 -0
- package/adapters/claude-code/commands/ship.md +2 -2
- package/adapters/codex/skills/ork/SKILL.md +12 -1
- package/adapters/hermes/README.md +3 -0
- package/adapters/hermes/bin/ork-abrir-thread.sh +17 -2
- package/adapters/hermes/bin/ork-brain.sh +3 -0
- package/adapters/hermes/bin/ork-maestro.sh +4 -1
- package/adapters/hermes/bin/ork-network-roadmap.sh +11 -0
- package/adapters/hermes/bin/ork-roadmap-status.sh +9 -0
- package/adapters/hermes/hermes.plugin.json +2 -0
- package/adapters/hermes/hitl-ingress/__init__.py +36 -3
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +19 -20
- package/adapters/openclaw/README.md +34 -2
- package/adapters/openclaw/dist/hitl-ingress.js +37 -3
- package/adapters/openclaw/dist/index.js +92 -20
- package/adapters/openclaw/openclaw.plugin.json +10 -1
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +32 -3
- package/adapters/openclaw/src/index.ts +108 -20
- package/assets/docs/padroes/documentacao-de-produto.md +2 -2
- package/assets/docs/padroes/roadmap-de-produto.md +1 -1
- package/assets/docs/produto/_modelo-feature.md +2 -0
- package/assets/orkmind_bridge.py +214 -1
- package/dist/adapters/claude-bg.js +6 -3
- package/dist/board.js +74 -0
- package/dist/branch-de-estado.js +5 -4
- package/dist/busca-semantica.js +161 -0
- package/dist/ci.js +141 -10
- package/dist/company-brain-cli.js +27 -2
- package/dist/company-brain-context.js +132 -0
- package/dist/company-brain-dossie.js +376 -0
- package/dist/company-brain-mcp.js +4 -1
- package/dist/conducao.js +71 -18
- package/dist/decisao-autonoma.js +42 -0
- package/dist/docs.js +64 -22
- package/dist/doctor.js +33 -0
- package/dist/entrega-pr.js +109 -5
- package/dist/experiencia-instalacao.js +256 -0
- package/dist/experiencia.js +72 -0
- package/dist/fabrica-estado.js +43 -10
- package/dist/fechamento.js +105 -0
- package/dist/forja.js +435 -0
- package/dist/gates.js +9 -1
- package/dist/hitl-classificacao.js +11 -1
- package/dist/hitl-contract.js +50 -7
- package/dist/hitl-curto.js +158 -0
- package/dist/hitl-gates.js +67 -1
- package/dist/hitl-lote.js +30 -15
- package/dist/hitl-presentation.js +10 -7
- package/dist/hitl-resumo.js +33 -12
- package/dist/hitl-texto-livre.js +150 -0
- package/dist/hosts.js +86 -1
- package/dist/index.js +602 -38
- package/dist/indice-vetorial.js +317 -0
- package/dist/init.js +13 -0
- package/dist/integracoes-locais.js +1 -1
- package/dist/intelligence-benchmark-contract.js +570 -0
- package/dist/intelligence-graph-cli.js +430 -0
- package/dist/intelligence-graph-contract.js +577 -0
- package/dist/intelligence-graph-extract-md.js +469 -0
- package/dist/intelligence-graph-extract-ts.js +810 -0
- package/dist/intelligence-graph-extract.js +369 -0
- package/dist/intelligence-graph-index.js +512 -0
- package/dist/intelligence-graph-parsers.js +327 -0
- package/dist/intelligence-graph-query.js +344 -0
- package/dist/intelligence-graph-repo.js +187 -0
- package/dist/leases.js +84 -0
- package/dist/ledger.js +14 -0
- package/dist/maestro-cli.js +10 -1
- package/dist/maestro-contract.js +5 -1
- package/dist/maestro-discovery.js +16 -2
- package/dist/maestro-snapshot.js +6 -2
- package/dist/manifest.js +142 -5
- package/dist/master-batch.js +3 -2
- package/dist/master-digest.js +2 -2
- package/dist/master-nota.js +203 -0
- package/dist/master.js +4 -0
- package/dist/mcp-experiencia.js +164 -0
- package/dist/mcp-git.js +4 -0
- package/dist/mcp-install.js +3 -1
- package/dist/mcp-maestro.js +3 -2
- package/dist/mcp-server.js +84 -5
- package/dist/memoria.js +147 -1
- package/dist/network-roadmap.js +907 -0
- package/dist/ocupacao.js +38 -2
- package/dist/onboarding.js +113 -7
- package/dist/orkmind.js +165 -9
- package/dist/phase.js +166 -13
- package/dist/policies.js +2 -0
- package/dist/projeto-alvo.js +551 -0
- package/dist/prompts.js +1 -1
- package/dist/pulse-consentimento.js +4 -2
- package/dist/pulse-delivery.js +5 -0
- package/dist/pulse-resposta.js +674 -49
- package/dist/pulse.js +21 -0
- package/dist/retry.js +51 -8
- package/dist/roadmap-reservas.js +262 -20
- package/dist/roadmap-status.js +213 -0
- package/dist/thread-close.js +3 -0
- package/dist/verify.js +4 -3
- package/eval/casos/orchestration-experience-pt-br.json +48 -0
- package/eval/casos/orchestration-experience.json +48 -0
- package/package.json +1 -1
- package/schemas/code-artifact-graph.v1.schema.json +581 -0
- package/schemas/graph-benchmark.v1.schema.json +2240 -0
- package/schemas/maestro-snapshot.schema.json +23 -0
- package/skills/README.md +7 -5
- package/skills/core/onboarding/SKILL.md +7 -0
- package/skills/core/orchestration-experience/SKILL.md +113 -0
- package/skills/core/orchestration-experience-pt-br/SKILL.md +112 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/go-implementation/SKILL.md +2 -0
package/dist/master-batch.js
CHANGED
|
@@ -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
|
});
|
package/dist/master-digest.js
CHANGED
|
@@ -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))
|
package/dist/mcp-install.js
CHANGED
|
@@ -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
|
-
|
|
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");
|
package/dist/mcp-maestro.js
CHANGED
|
@@ -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
|
-
|
|
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) }] };
|
package/dist/mcp-server.js
CHANGED
|
@@ -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
|
|
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) =>
|
|
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
|
-
|
|
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
|
-
|
|
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',
|