@orkastery/cli 0.5.1 → 0.5.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/adapters/claude-code/agents/ork-master.md +2 -0
- package/adapters/claude-code/commands/master.md +8 -0
- package/adapters/claude-code/commands/ork.md +9 -0
- package/adapters/codex/skills/ork/SKILL.md +18 -3
- package/adapters/hermes/README.md +1 -0
- package/adapters/hermes/bin/ork-network-status.sh +10 -0
- package/adapters/hermes/hermes.plugin.json +1 -0
- package/adapters/hermes/hitl-ingress/__init__.py +2 -2
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +6 -5
- package/adapters/openclaw/README.md +24 -4
- package/adapters/openclaw/dist/hitl-ingress.js +2 -2
- package/adapters/openclaw/dist/index.js +46 -11
- package/adapters/openclaw/openclaw.plugin.json +5 -1
- package/adapters/openclaw/package.json +2 -2
- package/adapters/openclaw/src/hitl-ingress.ts +2 -2
- package/adapters/openclaw/src/index.ts +49 -11
- package/assets/orkmind_bridge.py +117 -9
- package/dist/adapters/claude-bg.js +39 -5
- package/dist/adapters/codex.js +4 -3
- package/dist/agents-md.js +1 -0
- package/dist/auditrun.js +5 -1
- package/dist/board.js +16 -14
- package/dist/branch-de-estado.js +66 -10
- package/dist/busca-semantica.js +52 -12
- package/dist/canarios-rm057.js +149 -0
- package/dist/canarios.js +2 -0
- package/dist/ci.js +8 -1
- package/dist/claims.js +39 -1
- package/dist/company-brain-context.js +158 -22
- package/dist/docs.js +48 -9
- package/dist/doctor-pulse-cron.js +194 -0
- package/dist/doctor-rede.js +149 -0
- package/dist/doctor.js +89 -9
- package/dist/entrega-pr.js +20 -8
- package/dist/esgotamentos.js +181 -0
- package/dist/experiencia.js +12 -2
- package/dist/fabrica-estado.js +14 -6
- package/dist/fabrica-publicar.js +6 -0
- package/dist/fechamento.js +20 -66
- package/dist/forja.js +31 -7
- package/dist/gates.js +5 -1
- package/dist/hitl-contract.js +59 -4
- package/dist/hitl-curto.js +11 -6
- package/dist/hitl-gates.js +5 -0
- package/dist/hitl-lote.js +5 -2
- package/dist/hitl-resumo.js +9 -0
- package/dist/hitl-tempo-parado.js +201 -0
- package/dist/hitl-texto-livre.js +5 -5
- package/dist/horario.js +27 -0
- package/dist/hosts.js +52 -10
- package/dist/impedimento.js +205 -0
- package/dist/index.js +391 -74
- package/dist/indice-vetorial.js +87 -10
- package/dist/indice.js +3 -1
- package/dist/init.js +17 -0
- package/dist/integracoes-locais.js +2 -0
- package/dist/intelligence-graph-cli.js +91 -8
- package/dist/intelligence-graph-contexto.js +215 -0
- package/dist/intelligence-graph-parsers.js +6 -4
- package/dist/leases.js +664 -87
- package/dist/ledger-stats.js +15 -1
- package/dist/ledger.js +3 -0
- package/dist/licoes.js +1 -0
- package/dist/maestro-runtime.js +4 -1
- package/dist/manifest.js +20 -1
- package/dist/master-nota.js +14 -5
- package/dist/master.js +129 -12
- package/dist/mcp-artifacts.js +34 -3
- package/dist/mcp-git.js +20 -3
- package/dist/mcp-grafo-worker.js +5 -3
- package/dist/mcp-grafo.js +50 -4
- package/dist/mcp-server.js +14 -1
- package/dist/memoria.js +48 -6
- package/dist/network-roadmap.js +191 -16
- package/dist/ocupacao.js +11 -3
- package/dist/orkmind.js +90 -5
- package/dist/orquestracao.js +12 -10
- package/dist/parado-no-condutor.js +188 -32
- package/dist/phase.js +68 -15
- package/dist/policies.js +52 -1
- package/dist/portfolio.js +4 -1
- package/dist/projeto-alvo.js +5 -1
- package/dist/prompts.js +36 -1
- package/dist/prova-ativacao.js +75 -6
- package/dist/pulse-delivery.js +34 -2
- package/dist/pulse.js +49 -3
- package/dist/ratelimit.js +20 -4
- package/dist/recall.js +3 -1
- package/dist/rede-adesao.js +399 -0
- package/dist/rede-forja.js +289 -0
- package/dist/rede-projetos.js +239 -0
- package/dist/rede-status.js +336 -0
- package/dist/rede.js +980 -0
- package/dist/retry.js +69 -2
- package/dist/roadmap-status.js +23 -3
- package/dist/runtime-profiles.js +22 -1
- package/dist/saida-segura.js +58 -0
- package/dist/session-watcher-claude.js +113 -23
- package/dist/sessoes-contas.js +187 -0
- package/dist/sessoes-inventario.js +6 -18
- package/dist/sessoes.js +83 -2
- package/dist/ship.js +85 -19
- package/dist/thread.js +192 -13
- package/dist/worktree.js +5 -2
- package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
- package/package.json +7 -3
- package/references/definition-of-done.md +1 -1
- package/skills/README.md +1 -1
- package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
- package/skills/phases/master-metrics/SKILL.md +8 -3
package/dist/ledger-stats.js
CHANGED
|
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|
|
36
36
|
exports.parsePeriodo = parsePeriodo;
|
|
37
37
|
exports.coletarEstatisticas = coletarEstatisticas;
|
|
38
38
|
exports.registrarEstimativaPlano = registrarEstimativaPlano;
|
|
39
|
+
exports.linhaDoHitlDeConducao = linhaDoHitlDeConducao;
|
|
39
40
|
exports.textoDasEstatisticas = textoDasEstatisticas;
|
|
40
41
|
const fs = __importStar(require("node:fs"));
|
|
41
42
|
const path = __importStar(require("node:path"));
|
|
@@ -43,6 +44,7 @@ const thread_1 = require("./thread");
|
|
|
43
44
|
const ledger_1 = require("./ledger");
|
|
44
45
|
const util_1 = require("./util");
|
|
45
46
|
const horario_1 = require("./horario");
|
|
47
|
+
const hitl_tempo_parado_1 = require("./hitl-tempo-parado");
|
|
46
48
|
const MOTIVOS_PREVENTIVOS = new Set([
|
|
47
49
|
'policy.violation', 'cost.violation', 'verify.regression', 'claims.failed', 'claims.unverifiable',
|
|
48
50
|
]);
|
|
@@ -118,7 +120,9 @@ function coletarEstatisticas(raiz, filtros) {
|
|
|
118
120
|
const riscos = {};
|
|
119
121
|
const leads = [];
|
|
120
122
|
const fontes = new Set();
|
|
121
|
-
|
|
123
|
+
const esperasDeConducao = [];
|
|
124
|
+
for (const { thread, eventos } of selecionadas) {
|
|
125
|
+
esperasDeConducao.push(...(0, hitl_tempo_parado_1.esperasDeHitl)(thread.id, eventos));
|
|
122
126
|
corrompidos += eventos.filter(e => e.tipo === 'ledger_corrupted').length;
|
|
123
127
|
const despachos = new Map();
|
|
124
128
|
for (const e of eventos)
|
|
@@ -237,6 +241,7 @@ function coletarEstatisticas(raiz, filtros) {
|
|
|
237
241
|
custoReferencia: { natureza: tarifas.nature, moeda: tarifas.currency, valor: precificadas ? Number(custo.toFixed(6)) : null, sessoesPrecificadas: precificadas, sessoesSemTarifa: semTarifa, tarifasVerificadasEm: tarifas.verifiedAt, fontes: [...fontes].sort() },
|
|
238
242
|
leadTime: { mediaMs: leads.length ? leads.reduce((a, b) => a + b, 0) / leads.length : null, amostras: leads.length },
|
|
239
243
|
esperaHumana: { milissegundos: esperaMs, fechadas: esperasFechadas, abertas: esperasAbertas },
|
|
244
|
+
hitlDeConducao: (0, hitl_tempo_parado_1.resumirTempoParado)(esperasDeConducao, desdeMs, ateMs),
|
|
240
245
|
riscosPreventivos: { total: Object.values(riscos).reduce((a, b) => a + b, 0), porMotivo: riscos },
|
|
241
246
|
estimativasPlano: { amostras: estimativas, semIaHoras, iaSemOrkHoras },
|
|
242
247
|
qualidade: { ledgersCorrompidos: corrompidos, observacoes },
|
|
@@ -255,6 +260,14 @@ function registrarEstimativaPlano(raiz, threadId, dados) {
|
|
|
255
260
|
throw Error('ledger.estimate.head-unavailable');
|
|
256
261
|
return (0, ledger_1.registrar)((0, thread_1.dirThread)(raiz, threadId), threadId, 'plan_estimate', { fase: 'PLAN', unidade: 'hours', semIaHoras: dados.semIaHoras, iaSemOrkHoras: dados.iaSemOrkHoras, por: dados.por, metodo: dados.metodo, premissas: dados.premissas, incerteza: dados.incerteza ?? 'nao informada', head: head.stdout.trim() });
|
|
257
262
|
}
|
|
263
|
+
const minutos = (ms) => ms === null ? 'sem resposta medida' : `${(ms / 60_000).toFixed(1)} min`;
|
|
264
|
+
/** RM-057: uma linha, com a meta ao lado da mediana, para o dono ver o tempo parado sem abrir o JSON. */
|
|
265
|
+
function linhaDoHitlDeConducao(h) {
|
|
266
|
+
const meta = h.dentroDaMeta === null ? '' : h.dentroDaMeta ? ' (dentro da meta de 5 min)' : ' (acima da meta de 5 min)';
|
|
267
|
+
return ` HITL de conducao ${h.pedidos} pergunta(s); parado ${(h.paradoMs / 3_600_000).toFixed(2)} h; ` +
|
|
268
|
+
`mediana ${minutos(h.medianaRespostaMs)}${meta}; ${h.abertos} aberta(s); ` +
|
|
269
|
+
`texto fora da excecao ${h.emTexto.foraDaExcecao}`;
|
|
270
|
+
}
|
|
258
271
|
function textoDasEstatisticas(r) {
|
|
259
272
|
const dinheiro = r.custoReferencia.valor === null ? 'sem cobertura' : `USD ${r.custoReferencia.valor.toFixed(6)}`;
|
|
260
273
|
return [
|
|
@@ -265,6 +278,7 @@ function textoDasEstatisticas(r) {
|
|
|
265
278
|
` tokens ${r.tokens.total} total; input ${r.tokens.input} (cached ${r.tokens.cachedInput}); output ${r.tokens.output}`,
|
|
266
279
|
` custo referencia ${dinheiro} — estimativa por tarifa publica, nao gasto/fatura`,
|
|
267
280
|
` espera humana ${(r.esperaHumana.milissegundos / 3_600_000).toFixed(2)} h; ${r.esperaHumana.abertas} aberta(s)`,
|
|
281
|
+
linhaDoHitlDeConducao(r.hitlDeConducao),
|
|
268
282
|
` riscos evitados ${r.riscosPreventivos.total} gate(s) preventivo(s)`,
|
|
269
283
|
` estimativas PLAN ${r.estimativasPlano.amostras} amostra(s)`,
|
|
270
284
|
...r.qualidade.observacoes.map(o => ` cobertura ${o}`),
|
package/dist/ledger.js
CHANGED
|
@@ -141,6 +141,9 @@ exports.TIPOS_DE_EVENTO = {
|
|
|
141
141
|
// I-33 (D5): troca de perfil ou de runtime depois de falha da conta (quem, de qual para
|
|
142
142
|
// qual, evidencia e razao). Faz parte do catalogo, nao de literal solto.
|
|
143
143
|
perfilRotacionado: 'runtime_profile_rotated',
|
|
144
|
+
// RM-037 (fatia 5): a cota que o observador viu na transcricao com a sessao viva; o perfil saiu do rodizio
|
|
145
|
+
// ali, ate a hora dita na mensagem, e nao so no fecho da fase.
|
|
146
|
+
cotaVistaNaTranscricao: 'runtime_quota_detected',
|
|
144
147
|
fixAberto: 'go_fix_opened',
|
|
145
148
|
fixDespachado: 'go_fix_dispatched',
|
|
146
149
|
reverifyConcluido: 'check_reverify',
|
package/dist/licoes.js
CHANGED
|
@@ -66,6 +66,7 @@ exports.DICA_DO_MOTIVO = {
|
|
|
66
66
|
'verify.timeout': 'divida o comando lento ou suba `verify.timeout_ms` no manifesto',
|
|
67
67
|
'runtime.unavailable': 'declare a ordem de fallback do bloco (`ork setup <modo> --bloco N --fallback runtime:modelo`)',
|
|
68
68
|
'runtime.quota-exhausted': 'tenha um segundo perfil da conta (`ork accounts add`) ou fallback no bloco',
|
|
69
|
+
'runtime.profile-invalid': 'confira os perfis do runtime em `ork accounts list` antes de pedir `--perfil`',
|
|
69
70
|
'tree.blocked': 'sincronize a worktree com a base antes do SHIP (`ork worktree sync <thread>`)',
|
|
70
71
|
// Fatia 2 do ensaio da 0.5.0 (P6): o que o CI do projeto roda, e nao o script do Orkastery.
|
|
71
72
|
'ci.failed': 'rode antes do push o que o CI do projeto roda (`ci.command` do manifesto ou, sem ele, os comandos de `verify`)',
|
package/dist/maestro-runtime.js
CHANGED
|
@@ -75,8 +75,11 @@ const shipView = zod_1.z.union([
|
|
|
75
75
|
zod_1.z.object({
|
|
76
76
|
tipo: zod_1.z.literal('ship_done'), ts: shipTs, pushVerificado: zod_1.z.boolean(), mergeSha: shipSha,
|
|
77
77
|
shaRemoto: shipSha.nullable().optional(),
|
|
78
|
+
// RM-037 (fatia 5, A1): o push prova a ponta da base; o `mergeSha` e o merge que entregou, que pode ser mais
|
|
79
|
+
// velho que ela. Recibo sem o campo continua valendo pela regra de antes.
|
|
80
|
+
pontaDaBase: shipSha.nullable().optional(),
|
|
78
81
|
fonteDaProva: zod_1.z.string().min(1).max(512).refine(v => !/[\u0000-\u001f\u007f]/.test(v)).nullable().optional(),
|
|
79
|
-
}).refine(s => !s.pushVerificado || (s.shaRemoto === s.mergeSha && typeof s.fonteDaProva === 'string')),
|
|
82
|
+
}).refine(s => !s.pushVerificado || (s.shaRemoto === (s.pontaDaBase ?? s.mergeSha) && typeof s.fonteDaProva === 'string')),
|
|
80
83
|
zod_1.z.object({ tipo: zod_1.z.enum(['ship_started', 'ship_blocked']), ts: shipTs }),
|
|
81
84
|
]);
|
|
82
85
|
function operationalSources(reader, threads, native = [], now = new Date().toISOString(), threadScope) {
|
package/dist/manifest.js
CHANGED
|
@@ -356,6 +356,16 @@ function carregarManifesto(dirInicial = diretorioDoProjeto()) {
|
|
|
356
356
|
avisos.push(`runtime_profiles.${nome} deve ser true ou false; vale o padrao ${padrao} (${JSON.stringify(v)} nao reconhecido)`);
|
|
357
357
|
return padrao;
|
|
358
358
|
};
|
|
359
|
+
// RM-056 (D2): opt-in. Valor desconhecido vale o padrao `ordem`, com aviso.
|
|
360
|
+
const distribuicaoDePerfis = () => {
|
|
361
|
+
const v = perfisDeRuntime.distribuir;
|
|
362
|
+
if (v === undefined || v === null || v === 'ordem')
|
|
363
|
+
return 'ordem';
|
|
364
|
+
if (v === 'carga')
|
|
365
|
+
return 'carga';
|
|
366
|
+
avisos.push(`runtime_profiles.distribuir aceita ordem ou carga; vale o padrao ordem (${JSON.stringify(v)} nao reconhecido)`);
|
|
367
|
+
return 'ordem';
|
|
368
|
+
};
|
|
359
369
|
const liveness = mapa(dados.liveness);
|
|
360
370
|
const silencioMaxMin = numero(liveness.silencio_max_min, 10);
|
|
361
371
|
if (!Number.isFinite(silencioMaxMin) || silencioMaxMin <= 0)
|
|
@@ -456,6 +466,11 @@ function carregarManifesto(dirInicial = diretorioDoProjeto()) {
|
|
|
456
466
|
'(o `ork` nao adivinha DSN, e base alheia e pior do que base nenhuma)');
|
|
457
467
|
}
|
|
458
468
|
const embedding = lerEmbedding(memory.embedding, variavelDaDsn, erros);
|
|
469
|
+
const prazoDoUniverso = memory.universo_timeout_ms === undefined ? orkmind_1.TIMEOUT_DO_UNIVERSO_MS : memory.universo_timeout_ms;
|
|
470
|
+
if (typeof prazoDoUniverso !== 'number' || !Number.isSafeInteger(prazoDoUniverso) || prazoDoUniverso <= 0 ||
|
|
471
|
+
prazoDoUniverso > orkmind_1.LIMITE_TIMEOUT_DO_UNIVERSO_MS) {
|
|
472
|
+
erros.push('memory.universo_timeout_ms: esperado inteiro em ms entre 1 e 86400000 (24 h; valor omitido)');
|
|
473
|
+
}
|
|
459
474
|
const bytes = Buffer.byteLength(bruto, 'utf8');
|
|
460
475
|
if (bytes > exports.LIMITE_MANIFESTO_BYTES) {
|
|
461
476
|
erros.push(`manifesto com ${bytes} bytes, acima do limite duro de ${exports.LIMITE_MANIFESTO_BYTES} (prosa vai para memoria com tag, nao para o YAML)`);
|
|
@@ -491,7 +506,9 @@ function carregarManifesto(dirInicial = diretorioDoProjeto()) {
|
|
|
491
506
|
worktree: {
|
|
492
507
|
base_branch: texto(worktree.base_branch, 'main'),
|
|
493
508
|
dir: texto(worktree.dir, '.claude/worktrees'),
|
|
494
|
-
|
|
509
|
+
// P4 do ensaio da 0.5.0: o `ork thread new` cria a worktree sem flag quando a chave diz true. Ausente le
|
|
510
|
+
// false, para o projeto que nunca a declarou seguir como era.
|
|
511
|
+
por_thread: booleano(worktree.por_thread, false),
|
|
495
512
|
},
|
|
496
513
|
verify: {
|
|
497
514
|
build: verify.build === null || verify.build === undefined ? undefined : texto(verify.build, ''),
|
|
@@ -522,6 +539,7 @@ function carregarManifesto(dirInicial = diretorioDoProjeto()) {
|
|
|
522
539
|
runtime_profiles: {
|
|
523
540
|
rotate_same_runtime_on_quota: chaveDeRotacao('rotate_same_runtime_on_quota', true),
|
|
524
541
|
rotate_same_runtime_on_auth: chaveDeRotacao('rotate_same_runtime_on_auth', true),
|
|
542
|
+
distribuir: distribuicaoDePerfis(),
|
|
525
543
|
},
|
|
526
544
|
// Bloco B3: o limite de escalacao tem padrao, e o padrao e conservador. Manifesto
|
|
527
545
|
// sem bloco `retry:` continua andando com 3 tentativas e janela estimada de 60 min,
|
|
@@ -540,6 +558,7 @@ function carregarManifesto(dirInicial = diretorioDoProjeto()) {
|
|
|
540
558
|
cli: texto(memory.cli, 'orkmind'),
|
|
541
559
|
tenant: texto(memory.tenant, '') || nomeProjeto,
|
|
542
560
|
timeout_ms: numero(memory.timeout_ms, 15000),
|
|
561
|
+
universo_timeout_ms: typeof prazoDoUniverso === 'number' ? prazoDoUniverso : orkmind_1.TIMEOUT_DO_UNIVERSO_MS,
|
|
543
562
|
...(embedding ? { embedding } : {}),
|
|
544
563
|
},
|
|
545
564
|
// Bloco B5: governanca de custo dos auditores. Ausente no manifesto quer dizer
|
package/dist/master-nota.js
CHANGED
|
@@ -34,6 +34,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.ERRO_PROVA_DE_CANAL = exports.CONTRATO_PEDIDO_DE_NOTA = void 0;
|
|
37
|
+
exports.marcaDeHost = marcaDeHost;
|
|
37
38
|
exports.exigirNotaSemHost = exigirNotaSemHost;
|
|
38
39
|
exports.arquivoDasNotas = arquivoDasNotas;
|
|
39
40
|
exports.pedirNota = pedirNota;
|
|
@@ -84,10 +85,11 @@ exports.CONTRATO_PEDIDO_DE_NOTA = 'ork.master-nota/v1';
|
|
|
84
85
|
exports.ERRO_PROVA_DE_CANAL = 'master.prova-de-canal';
|
|
85
86
|
const sha = (s) => (0, node_crypto_1.createHash)('sha256').update(s).digest('hex');
|
|
86
87
|
/**
|
|
87
|
-
*
|
|
88
|
-
* ambiente
|
|
88
|
+
* A marca de host ou de despacho do processo, ou `undefined` no terminal do dono. Lida SO do
|
|
89
|
+
* ambiente. Serve para RECUSAR (D8) e para AVISAR (RM-008, aceite por omissao sem thread), nunca
|
|
90
|
+
* para autorizar.
|
|
89
91
|
*/
|
|
90
|
-
function
|
|
92
|
+
function marcaDeHost(ambiente = process.env) {
|
|
91
93
|
let canal;
|
|
92
94
|
try {
|
|
93
95
|
canal = (0, conducao_1.canalDoProcesso)(undefined, ambiente);
|
|
@@ -97,12 +99,19 @@ function exigirNotaSemHost(ambiente = process.env) {
|
|
|
97
99
|
}
|
|
98
100
|
// B2 do CHECK: `ORK_CANAL=cli` NAO absolve. A sessao despachada herda `ORK_CANAL=cli` quando a
|
|
99
101
|
// thread e conduzida do terminal, e o Claude Code e o Codex marcam os proprios processos. Qualquer
|
|
100
|
-
// marca de host ou de despacho
|
|
101
|
-
|
|
102
|
+
// marca de host ou de despacho conta, mesmo com o canal declarado como terminal.
|
|
103
|
+
return canal !== 'cli' ? canal
|
|
102
104
|
: ambiente.ORK_DISPATCH_ID ? 'sessão despachada (ORK_DISPATCH_ID)'
|
|
103
105
|
: ambiente.CLAUDECODE === '1' ? 'claude-code (CLAUDECODE)'
|
|
104
106
|
: ambiente.CODEX_SANDBOX || ambiente.CODEX_SANDBOX_NETWORK_DISABLED ? 'codex (CODEX_SANDBOX)'
|
|
105
107
|
: (ambiente.HERMES_HOME ?? '').trim() ? 'hermes (HERMES_HOME)' : undefined;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* D8: nota em nome de pessoa, vinda de processo de host, e recusada. O canal e lido SO do
|
|
111
|
+
* ambiente: `--canal` em argv nao conta, porque quem escreve argv e justamente quem chama.
|
|
112
|
+
*/
|
|
113
|
+
function exigirNotaSemHost(ambiente = process.env) {
|
|
114
|
+
const marca = marcaDeHost(ambiente);
|
|
106
115
|
if (marca) {
|
|
107
116
|
throw new Error(`${exports.ERRO_PROVA_DE_CANAL}: nota em nome de pessoa vinda de ${marca} exige a prova de canal do ingresso. ` +
|
|
108
117
|
'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.');
|
package/dist/master.js
CHANGED
|
@@ -49,7 +49,9 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
49
49
|
};
|
|
50
50
|
})();
|
|
51
51
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
-
exports.AUTOR_DA_OMISSAO = exports.CONTRATO_MASTER_PENDENTE = exports.ORDEM_DAS_CLASSES = exports.CLASSES_DE_FALHA = exports.VERSAO_MASTER_LOG = exports.CONTRATO_MASTER_LOG = void 0;
|
|
52
|
+
exports.AUTOR_DA_OMISSAO = exports.CONTRATO_MASTER_PENDENTE = exports.FASE_DO_TIPO = exports.ORDEM_DAS_CLASSES = exports.CLASSE_DO_MOTIVO = exports.CLASSES_DE_FALHA = exports.VERSAO_MASTER_LOG = exports.CONTRATO_MASTER_LOG = void 0;
|
|
53
|
+
exports.classesPelosGates = classesPelosGates;
|
|
54
|
+
exports.sincronizacoesComABase = sincronizacoesComABase;
|
|
53
55
|
exports.parseClasse = parseClasse;
|
|
54
56
|
exports.caminhoPostmortem = caminhoPostmortem;
|
|
55
57
|
exports.caminhoMasterLog = caminhoMasterLog;
|
|
@@ -72,6 +74,8 @@ exports.aceitosPorOmissao = aceitosPorOmissao;
|
|
|
72
74
|
exports.pendentesDeScore = pendentesDeScore;
|
|
73
75
|
exports.tabelaDeEntregas = tabelaDeEntregas;
|
|
74
76
|
exports.aceitarPendentesPorOmissao = aceitarPendentesPorOmissao;
|
|
77
|
+
exports.entregasParaOmissao = entregasParaOmissao;
|
|
78
|
+
exports.motivoForaDaOmissao = motivoForaDaOmissao;
|
|
75
79
|
exports.textoDoMaster = textoDoMaster;
|
|
76
80
|
exports.tabelaDeClasses = tabelaDeClasses;
|
|
77
81
|
const path = __importStar(require("node:path"));
|
|
@@ -105,6 +109,65 @@ exports.CLASSES_DE_FALHA = {
|
|
|
105
109
|
'scope-creep': 'a entrega cresceu alem do que o GOAL prometia',
|
|
106
110
|
outra: 'nao cabe em nenhuma classe acima (descreva na justificativa)',
|
|
107
111
|
};
|
|
112
|
+
/**
|
|
113
|
+
* RM-008 (classe pelo gate): a classe que cada motivo tipado do gate sustenta quando ninguem informa
|
|
114
|
+
* `--classe`. Antes, todo gate virava "outra", a classe que o `ork licoes` ignora: 8 POSTMORTEMs de
|
|
115
|
+
* 03/10 fecharam assim por aceite por omissao. Motivo fora da tabela (prazo, falta de veredito,
|
|
116
|
+
* runtime, conta, custo, espera humana) nao cabe numa classe fixa e segue "outra".
|
|
117
|
+
*/
|
|
118
|
+
exports.CLASSE_DO_MOTIVO = {
|
|
119
|
+
'artifact.missing': 'processo',
|
|
120
|
+
'claims.failed': 'processo',
|
|
121
|
+
'claims.unverifiable': 'processo',
|
|
122
|
+
'policy.violation': 'processo',
|
|
123
|
+
'runtime.autoconferencia': 'processo',
|
|
124
|
+
'verify.regression': 'processo',
|
|
125
|
+
'verify.failed': 'processo',
|
|
126
|
+
'ci.failed': 'processo',
|
|
127
|
+
'hitl.formato': 'processo',
|
|
128
|
+
'runtime.rate-limited': 'rate-limit',
|
|
129
|
+
'runtime.quota-exhausted': 'rate-limit',
|
|
130
|
+
'lease.busy': 'conflito',
|
|
131
|
+
'conducao.em-andamento': 'conflito',
|
|
132
|
+
'tree.blocked': 'conflito',
|
|
133
|
+
};
|
|
134
|
+
/** As classes que os motivos dos gates sustentam, na ordem canonica e sem repetir. */
|
|
135
|
+
function classesPelosGates(motivos) {
|
|
136
|
+
const achadas = new Set();
|
|
137
|
+
for (const m of motivos)
|
|
138
|
+
achadas.add(exports.CLASSE_DO_MOTIVO[m] ?? 'outra');
|
|
139
|
+
if (achadas.size === 0)
|
|
140
|
+
achadas.add('sem-falha');
|
|
141
|
+
return exports.ORDEM_DAS_CLASSES.filter((c) => achadas.has(c));
|
|
142
|
+
}
|
|
143
|
+
/** Teto de merges conferidos por entrega: a branch de 03/10 com mais sincronizacoes trouxe 16. */
|
|
144
|
+
const TETO_DE_MERGES_POR_ENTREGA = 200;
|
|
145
|
+
/**
|
|
146
|
+
* RM-008 (base-avancou): quantas vezes a thread precisou trazer a base para dentro da branch. Conta os
|
|
147
|
+
* `worktree_synced` do ledger e, em cada entrega com merge, os merges da branch (entre o primeiro e o
|
|
148
|
+
* segundo pai do merge) cujo pai trazido ja estava na base. Em 03/10/2026, 29 de 35 entregas trouxeram
|
|
149
|
+
* a `origin/main` antes do merge, e nenhuma fechou com `base-avancou`. Git sem resposta conta 0 e nunca
|
|
150
|
+
* derruba o MASTER.
|
|
151
|
+
*/
|
|
152
|
+
function sincronizacoesComABase(raiz, eventos, mergeShas) {
|
|
153
|
+
let total = eventos.filter((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.worktreeSincronizada).length;
|
|
154
|
+
for (const sha of new Set(mergeShas)) {
|
|
155
|
+
if (!/^[0-9a-f]{40}$/.test(sha))
|
|
156
|
+
continue;
|
|
157
|
+
const r = (0, util_1.exec)('git', ['rev-list', '--merges', '--parents', `--max-count=${TETO_DE_MERGES_POR_ENTREGA}`,
|
|
158
|
+
`${sha}^1..${sha}^2`], raiz, 30000);
|
|
159
|
+
if (!r.ok)
|
|
160
|
+
continue;
|
|
161
|
+
for (const linha of r.stdout.split('\n')) {
|
|
162
|
+
const pais = linha.trim().split(/\s+/).slice(1);
|
|
163
|
+
// O pai trazido (o segundo em diante) ja estava na base: e a base entrando na branch.
|
|
164
|
+
if (pais.slice(1).some((p) => /^[0-9a-f]{40}$/.test(p) &&
|
|
165
|
+
(0, util_1.exec)('git', ['merge-base', '--is-ancestor', p, `${sha}^1`], raiz, 30000).ok))
|
|
166
|
+
total += 1;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return total;
|
|
170
|
+
}
|
|
108
171
|
/** Ordem canonica das classes na saida do CLI. */
|
|
109
172
|
exports.ORDEM_DAS_CLASSES = [
|
|
110
173
|
'sem-falha',
|
|
@@ -138,6 +201,19 @@ function caminhoPostmortem(raiz, id) {
|
|
|
138
201
|
function caminhoMasterLog(raiz, id) {
|
|
139
202
|
return path.join((0, thread_1.dirThread)(raiz, id), 'master-log.json');
|
|
140
203
|
}
|
|
204
|
+
/**
|
|
205
|
+
* RM-008 (fases pelo tipo): a fase dos eventos que o nucleo grava sem o campo `fase`. O `verify_run`
|
|
206
|
+
* e o `ship_done` nunca trouxeram fase, e 34 dos 35 POSTMORTEM de 03/10 sairam sem o CHECK, e 33 sem o
|
|
207
|
+
* SHIP, embora o ledger os tivesse (`ork-rm037testesi`: 6 `verify_run`, 1 `ship_done`, "GOAL GO MASTER").
|
|
208
|
+
* So vale quando o evento nao traz `fase`: o campo explicito continua vencendo.
|
|
209
|
+
*/
|
|
210
|
+
exports.FASE_DO_TIPO = {
|
|
211
|
+
[ledger_1.TIPOS_DE_EVENTO.verificacao]: 'CHECK',
|
|
212
|
+
[ledger_1.TIPOS_DE_EVENTO.reverifyConcluido]: 'CHECK',
|
|
213
|
+
[ledger_1.TIPOS_DE_EVENTO.shipIniciado]: 'SHIP',
|
|
214
|
+
[ledger_1.TIPOS_DE_EVENTO.shipConcluido]: 'SHIP',
|
|
215
|
+
[ledger_1.TIPOS_DE_EVENTO.shipBloqueado]: 'SHIP',
|
|
216
|
+
};
|
|
141
217
|
/**
|
|
142
218
|
* As fases que a thread realmente percorreu, reconstruidas do LEDGER.
|
|
143
219
|
*
|
|
@@ -148,7 +224,7 @@ function fasesPercorridas(raiz, id) {
|
|
|
148
224
|
const eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, id));
|
|
149
225
|
const porFase = new Map();
|
|
150
226
|
for (const e of eventos) {
|
|
151
|
-
const bruta = typeof e.fase === 'string' ? e.fase.toUpperCase() : null;
|
|
227
|
+
const bruta = typeof e.fase === 'string' ? e.fase.toUpperCase() : (exports.FASE_DO_TIPO[e.tipo] ?? null);
|
|
152
228
|
if (!bruta || !types_1.FASES.includes(bruta))
|
|
153
229
|
continue;
|
|
154
230
|
const atual = porFase.get(bruta) ?? {
|
|
@@ -244,7 +320,8 @@ function prepararDocumentos(raiz, id, opcoes) {
|
|
|
244
320
|
sincronizarReversao(raiz, id);
|
|
245
321
|
const eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(raiz, id));
|
|
246
322
|
const gatesBloqueados = eventos
|
|
247
|
-
|
|
323
|
+
// Ensaio de 03/10/2026 (RM-049): o gate barrado por um `ork ship --dry-run` e ensaio, nao reprovacao.
|
|
324
|
+
.filter((e) => e.tipo === ledger_1.TIPOS_DE_EVENTO.gateBloqueado && e.dryRun !== true)
|
|
248
325
|
.map((e) => ({
|
|
249
326
|
ts: e.ts,
|
|
250
327
|
motivo: String(e.motivo ?? '(sem motivo)'),
|
|
@@ -264,11 +341,20 @@ function prepararDocumentos(raiz, id, opcoes) {
|
|
|
264
341
|
let classes = opcoes.classes ?? [];
|
|
265
342
|
if (classes.length === 0) {
|
|
266
343
|
classesInferidas = true;
|
|
267
|
-
classes = gatesBloqueados.
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
344
|
+
classes = classesPelosGates(gatesBloqueados.map((g) => g.motivo));
|
|
345
|
+
const motivos = [...new Set(gatesBloqueados.map((g) => g.motivo))];
|
|
346
|
+
const sincronizacoes = sincronizacoesComABase(raiz, eventos, entregas.map((e) => e.mergeSha));
|
|
347
|
+
if (sincronizacoes > 0) {
|
|
348
|
+
classes = exports.ORDEM_DAS_CLASSES.filter((c) => c === 'base-avancou' || (c !== 'sem-falha' && classes.includes(c)));
|
|
349
|
+
}
|
|
350
|
+
const pelaBase = sincronizacoes > 0
|
|
351
|
+
? ` A branch trouxe a base ${sincronizacoes} vez(es) antes da entrega: o \`ork\` juntou "base-avancou".` : '';
|
|
352
|
+
avisos.push((gatesBloqueados.length > 0
|
|
353
|
+
? `a thread levou ${gatesBloqueados.length} reprovacao(oes) tipada(s) (${motivos.join(', ')}) e nenhuma --classe foi ` +
|
|
354
|
+
`informada: o \`ork\` inferiu "${classes.join('", "')}" pelo motivo do gate.${pelaBase} Corrija com --classe <classe> --refazer.`
|
|
355
|
+
: sincronizacoes > 0
|
|
356
|
+
? `nenhuma --classe informada e nenhum gate reprovou.${pelaBase} Corrija com --classe <classe> --refazer.`
|
|
357
|
+
: 'nenhuma --classe informada e nenhum gate reprovou: o `ork` gravou "sem-falha".'));
|
|
272
358
|
}
|
|
273
359
|
// I-43 (D3): o indice e DERIVADO do ledger no momento do registro, e acompanha o
|
|
274
360
|
// score como o que a maquina achava antes de o dono falar. Quando as duas existem,
|
|
@@ -351,7 +437,7 @@ function autoriaHumana(por) {
|
|
|
351
437
|
}
|
|
352
438
|
function exigirAutoriaHumana(por) {
|
|
353
439
|
if (!autoriaHumana(por))
|
|
354
|
-
throw new Error('ratificação exige --por humano explícito; agente ou autoria pendente não pode pontuar');
|
|
440
|
+
throw new Error('ratificação exige --por humano explícito (ex.: --por "seu-nome"); agente ou autoria pendente não pode pontuar');
|
|
355
441
|
}
|
|
356
442
|
function masterRatificado(raiz, id) {
|
|
357
443
|
const t = (0, thread_1.lerThread)(raiz, id);
|
|
@@ -692,7 +778,8 @@ function tabelaDeEntregas(raiz, todas = false) {
|
|
|
692
778
|
(0, util_1.tabela)(['ID', 'SLUG', 'MODO', 'FASE', 'STATUS', 'ENTREGOU', 'INDICE', 'DE ONDE VEIO'], linhas),
|
|
693
779
|
'',
|
|
694
780
|
'ENTREGUE E ACEITO, a menos que voce diga o contrario.',
|
|
695
|
-
' ork master --aceitar-omissao
|
|
781
|
+
' ork master <id> --aceitar-omissao aceita so esta entrega, com o indice e os insumos no ledger',
|
|
782
|
+
' ork master --aceitar-omissao aceita TODAS as entregues da lista (--dry-run mostra quais, sem gravar)',
|
|
696
783
|
' ork master <id> --score <0-5> --justificativa "<por que>" --por <humano> se quiser reclamar',
|
|
697
784
|
'',
|
|
698
785
|
`Classes fixas: ${exports.ORDEM_DAS_CLASSES.join(', ')}`,
|
|
@@ -706,10 +793,40 @@ function tabelaDeEntregas(raiz, todas = false) {
|
|
|
706
793
|
* ENTREGOU: uma thread que nunca chegou ao ship nao tem entrega para aceitar, e
|
|
707
794
|
* aceita-la seria inventar um fato.
|
|
708
795
|
*/
|
|
709
|
-
function aceitarPendentesPorOmissao(raiz) {
|
|
796
|
+
function aceitarPendentesPorOmissao(raiz, opcoes = {}) {
|
|
797
|
+
return entregasParaOmissao(raiz, opcoes.thread).map((p) => aceitarPorOmissao(raiz, p.thread.id));
|
|
798
|
+
}
|
|
799
|
+
/**
|
|
800
|
+
* RM-008 (03/10/2026): o que `ork master --aceitar-omissao` fecharia, sem gravar nada.
|
|
801
|
+
*
|
|
802
|
+
* Na madrugada de 03/10, tres vezes, um condutor rodou `ork master --aceitar-omissao` para fechar
|
|
803
|
+
* a PROPRIA thread, e o comando fechou tambem as entregues de outras frentes paralelas. A CLI nao
|
|
804
|
+
* deixava indicar a thread. Com `thread`, o alvo e so ela; sem, continua sendo toda entrega que
|
|
805
|
+
* espera decisao (o padrao nao mudou). Thread inexistente recusa pelo `lerThread`.
|
|
806
|
+
*/
|
|
807
|
+
function entregasParaOmissao(raiz, thread) {
|
|
808
|
+
if (thread !== undefined)
|
|
809
|
+
(0, thread_1.lerThread)(raiz, thread);
|
|
710
810
|
return pendentesDeScore(raiz)
|
|
711
811
|
.filter((p) => p.entregou)
|
|
712
|
-
.
|
|
812
|
+
.filter((p) => thread === undefined || p.thread.id === thread);
|
|
813
|
+
}
|
|
814
|
+
/**
|
|
815
|
+
* Por que a thread indicada nao entra no aceite por omissao, ou `null` quando entra.
|
|
816
|
+
* `ja-fechada` e idempotencia (nada a fazer); `fechamento-admin` e `sem-entrega` sao recusa.
|
|
817
|
+
*/
|
|
818
|
+
function motivoForaDaOmissao(raiz, id) {
|
|
819
|
+
const thread = (0, thread_1.lerThread)(raiz, id);
|
|
820
|
+
if (thread.fechamentoAdmin) {
|
|
821
|
+
return { motivo: 'fechamento-admin', texto: `a thread ${id} foi fechada administrativamente (${thread.fechamentoAdmin.motivo}); nao ha entrega para aceitar.` };
|
|
822
|
+
}
|
|
823
|
+
if (masterRatificado(raiz, id)) {
|
|
824
|
+
return { motivo: 'ja-fechada', texto: `a thread ${id} ja tem MASTER registrado; nada a aceitar.` };
|
|
825
|
+
}
|
|
826
|
+
if (!entregou(raiz, id)) {
|
|
827
|
+
return { motivo: 'sem-entrega', texto: `a thread ${id} ainda nao entregou (sem ship_done no ledger); registre a entrega antes: ork ship registrar-pr ${id}` };
|
|
828
|
+
}
|
|
829
|
+
return null;
|
|
713
830
|
}
|
|
714
831
|
/** Texto de `ork master <thread-id> --score`. */
|
|
715
832
|
function textoDoMaster(r) {
|
package/dist/mcp-artifacts.js
CHANGED
|
@@ -150,8 +150,39 @@ function comEscrita(raiz, id, executar) {
|
|
|
150
150
|
if (st) {
|
|
151
151
|
if (!st.isDirectory() || st.isSymbolicLink() || fs.realpathSync(pastaLeases) !== pastaLeases)
|
|
152
152
|
throw Error('mcp.state.unsafe: leases');
|
|
153
|
-
for (const
|
|
154
|
-
const
|
|
153
|
+
for (const nomeBytes of fs.readdirSync(pastaLeases, { encoding: 'buffer' })) {
|
|
154
|
+
const nome = nomeBytes.toString('utf8');
|
|
155
|
+
// Antes do lstat: uma decodificacao com perda nao prova que a entrada desapareceu.
|
|
156
|
+
if (!Buffer.from(nome, 'utf8').equals(nomeBytes))
|
|
157
|
+
throw Error('mcp.state.unsafe: nome fora de UTF-8 em leases');
|
|
158
|
+
const entrada = path.join(pastaLeases, nome), s = fs.lstatSync(entrada, { throwIfNoEntry: false });
|
|
159
|
+
if (!s)
|
|
160
|
+
continue; // Uma retomada pode ter acabado durante o preflight.
|
|
161
|
+
if (s.isDirectory() && nome.endsWith('.json.retomadas')) {
|
|
162
|
+
const base = nome.slice(0, -'.json.retomadas'.length);
|
|
163
|
+
if (!base || encodeURIComponent(decodeURIComponent(base)) !== base)
|
|
164
|
+
throw Error('mcp.state.unsafe: fila de retomada');
|
|
165
|
+
let candidatos;
|
|
166
|
+
try {
|
|
167
|
+
candidatos = fs.readdirSync(entrada, { encoding: 'buffer' });
|
|
168
|
+
}
|
|
169
|
+
catch (e) {
|
|
170
|
+
if (e.code === 'ENOENT')
|
|
171
|
+
continue;
|
|
172
|
+
throw e;
|
|
173
|
+
}
|
|
174
|
+
for (const candidatoBytes of candidatos) {
|
|
175
|
+
const candidato = candidatoBytes.toString('utf8');
|
|
176
|
+
if (!Buffer.from(candidato, 'utf8').equals(candidatoBytes))
|
|
177
|
+
throw Error('mcp.state.unsafe: nome fora de UTF-8 na fila');
|
|
178
|
+
const c = fs.lstatSync(path.join(entrada, candidato), { throwIfNoEntry: false });
|
|
179
|
+
if (!c)
|
|
180
|
+
continue;
|
|
181
|
+
if (!/^\d+-[a-f0-9-]+\.json(?:\.tmp)?$/.test(candidato) || !c.isFile() || c.nlink !== 1)
|
|
182
|
+
throw Error('mcp.state.unsafe: candidato de retomada');
|
|
183
|
+
}
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
155
186
|
if (!s.isFile() || s.isSymbolicLink() || s.nlink !== 1)
|
|
156
187
|
throw Error('mcp.state.unsafe: lease ou fila');
|
|
157
188
|
}
|
|
@@ -231,6 +262,6 @@ function adicionarClaimMcp(raiz, id, entrada) {
|
|
|
231
262
|
if ((0, thread_1.lerThread)(raiz, id).status === 'fechada')
|
|
232
263
|
throw Error('mcp.claim.closed');
|
|
233
264
|
// Strings sao dados da claim; a reexecucao MCP deve usar o executor confinado.
|
|
234
|
-
return (0, claims_1.adicionarClaim)(raiz, id, entrada);
|
|
265
|
+
return (0, claims_1.adicionarClaim)(raiz, id, { ...entrada, semProvaLocal: true });
|
|
235
266
|
}));
|
|
236
267
|
}
|
package/dist/mcp-git.js
CHANGED
|
@@ -195,7 +195,9 @@ function arvoreMetadados(root, alvo, opcoes = {}) {
|
|
|
195
195
|
? '; empacote os objetos soltos sem perda com git repack -d (git count-objects -v mostra quantos)' : ''));
|
|
196
196
|
if (profundidade > 32)
|
|
197
197
|
falha(`metadata.unsupported: mais de 32 niveis em ${rotulo(alvo)}`);
|
|
198
|
-
const st = fs.lstatSync(f);
|
|
198
|
+
const st = fs.lstatSync(f, { throwIfNoEntry: false });
|
|
199
|
+
if (!st)
|
|
200
|
+
return; // Uma retomada pode remover a fila ou o candidato depois da enumeracao.
|
|
199
201
|
if (st.isSymbolicLink())
|
|
200
202
|
falha(`metadata.unsafe: link simbolico em ${rotulo(f)}`);
|
|
201
203
|
if (!st.isDirectory() && !st.isFile())
|
|
@@ -205,9 +207,24 @@ function arvoreMetadados(root, alvo, opcoes = {}) {
|
|
|
205
207
|
`tire o vinculo sem perder conteudo: cp -p ${rotulo(f)} ${rotulo(f + '.tmp')} && mv ${rotulo(f + '.tmp')} ${rotulo(f)}`);
|
|
206
208
|
if (f !== root && !dentro(root, f))
|
|
207
209
|
falha(`metadata.unsafe: ${rotulo(f)} fora de ${path.basename(root)}`);
|
|
208
|
-
if (st.isDirectory())
|
|
209
|
-
|
|
210
|
+
if (st.isDirectory()) {
|
|
211
|
+
let entradas;
|
|
212
|
+
try {
|
|
213
|
+
entradas = fs.readdirSync(f, { encoding: 'buffer' });
|
|
214
|
+
}
|
|
215
|
+
catch (e) {
|
|
216
|
+
if (e.code === 'ENOENT')
|
|
217
|
+
return;
|
|
218
|
+
throw e;
|
|
219
|
+
}
|
|
220
|
+
for (const bytes of entradas) {
|
|
221
|
+
const n = bytes.toString('utf8');
|
|
222
|
+
// Perda de bytes nao e ENOENT transitorio: o nome decodificado pode nem apontar para esta entrada.
|
|
223
|
+
if (!Buffer.from(n, 'utf8').equals(bytes))
|
|
224
|
+
falha(`metadata.unsafe: nome fora de UTF-8 em ${rotulo(f)}`);
|
|
210
225
|
visitar(path.join(f, n), profundidade + 1);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
211
228
|
};
|
|
212
229
|
if (fs.realpathSync(alvo) !== alvo)
|
|
213
230
|
falha(`metadata.unsafe: ${rotulo(alvo)} passa por link simbolico`);
|
package/dist/mcp-grafo-worker.js
CHANGED
|
@@ -38,7 +38,7 @@ exports.rodarConsulta = rodarConsulta;
|
|
|
38
38
|
* RM-031 KG5 (D3, D8): o worker da consulta do grafo pelo MCP.
|
|
39
39
|
*
|
|
40
40
|
* Roda num processo filho, aberto pelo `mcp-grafo.ts` com o ambiente minimo do MCP. Le `{raiz, argv}`
|
|
41
|
-
* do stdin, confere que o cwd e a raiz, aceita so as
|
|
41
|
+
* do stdin, confere que o cwd e a raiz, aceita so as cinco consultas no argv que as tools montam (com
|
|
42
42
|
* `--json` e `--teto-bytes`), carrega o manifesto da raiz (a worktree da thread) e chama o
|
|
43
43
|
* `executarGrafo` com o argv que a CLI receberia: a resposta e a do `ork grafo`, byte a byte. O `main` do
|
|
44
44
|
* `ork` nao roda aqui, entao nada do argv vira `--projeto`, `--version` ou `--help`.
|
|
@@ -52,7 +52,8 @@ const zod_1 = require("zod");
|
|
|
52
52
|
const intelligence_graph_cli_1 = require("./intelligence-graph-cli");
|
|
53
53
|
const manifest_1 = require("./manifest");
|
|
54
54
|
const estado_thread_1 = require("./estado-thread");
|
|
55
|
-
const
|
|
55
|
+
const mcp_grafo_1 = require("./mcp-grafo");
|
|
56
|
+
const CONSULTAS = Object.freeze({ vizinhos: 1, chamadores: 1, importadores: 1, caminho: 2, contexto: 1 });
|
|
56
57
|
const OPCAO = /^--(profundidade|sentido|tipo|limite|teto-bytes)=[a-z0-9_,]{1,120}$/;
|
|
57
58
|
const PEDIDO = zod_1.z.object({
|
|
58
59
|
raiz: zod_1.z.string().min(1).max(4096).refine((p) => path.isAbsolute(p), 'raiz absoluta'),
|
|
@@ -78,7 +79,8 @@ function rodarConsulta(entrada, cwd, escrever) {
|
|
|
78
79
|
const carregado = (0, manifest_1.exigirManifesto)(raiz);
|
|
79
80
|
if (fs.realpathSync(carregado.raiz) !== fs.realpathSync(raiz))
|
|
80
81
|
throw Error('grafo.mcp.worker: o manifesto nao e o da raiz pedida');
|
|
81
|
-
return (0, intelligence_graph_cli_1.executarGrafo)(argv, { raiz: carregado.raiz, estado: (0, estado_thread_1.raizDoEstado)(carregado.raiz), repositorio: carregado.manifesto.project.name,
|
|
82
|
+
return (0, intelligence_graph_cli_1.executarGrafo)(argv, { raiz: carregado.raiz, estado: (0, estado_thread_1.raizDoEstado)(carregado.raiz), repositorio: carregado.manifesto.project.name,
|
|
83
|
+
contextoDaThread: (id) => (0, mcp_grafo_1.lerEntradaDaThread)(carregado.raiz, id), escrever });
|
|
82
84
|
}
|
|
83
85
|
if (require.main === module) {
|
|
84
86
|
try {
|
package/dist/mcp-grafo.js
CHANGED
|
@@ -35,16 +35,17 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
36
|
exports.LIMITE_MAXIMO_DO_MCP = exports.PROFUNDIDADE_MAXIMA_DO_MCP = exports.TIPOS_DE_ARESTA_DO_MCP = exports.PRAZO_DA_CONSULTA_MS = exports.TETO_MAXIMO = exports.TETO_MINIMO = exports.TETO_PADRAO = exports.TOOLS_DO_GRAFO = void 0;
|
|
37
37
|
exports.grafoLigado = grafoLigado;
|
|
38
|
+
exports.lerEntradaDaThread = lerEntradaDaThread;
|
|
38
39
|
exports.argvDaTool = argvDaTool;
|
|
39
40
|
exports.consultarPeloWorker = consultarPeloWorker;
|
|
40
41
|
exports.registrarConsultasDoGrafo = registrarConsultasDoGrafo;
|
|
41
42
|
/**
|
|
42
43
|
* RM-031 KG5: a consulta do grafo de codigo pelas fases, pelo MCP do projeto.
|
|
43
44
|
*
|
|
44
|
-
*
|
|
45
|
+
* Cinco tools de leitura com o contrato do `ork grafo` (D6): cada uma roda, na worktree da thread, o
|
|
45
46
|
* argv que a CLI receberia (`<consulta> ... --json --teto-bytes N`) e devolve o que ela escreve, sem
|
|
46
47
|
* transformar: a resposta e JSON `ork.code-graph-query/v0` com a evidencia de cada aresta e no maximo
|
|
47
|
-
* `tetoBytes` bytes (D4);
|
|
48
|
+
* `tetoBytes` bytes (D4); contexto usa ork.thread-graph-context/v2. A recusa nao passa pelo teto.
|
|
48
49
|
*
|
|
49
50
|
* Desligadas por padrao (D2): so existem com `grafo.mcp: true` no manifesto da raiz, lido no startup, e
|
|
50
51
|
* cada chamada confere a flag de novo. A consulta roda num worker (D3), um processo filho por chamada,
|
|
@@ -58,7 +59,10 @@ const path = __importStar(require("node:path"));
|
|
|
58
59
|
const node_child_process_1 = require("node:child_process");
|
|
59
60
|
const zod_1 = require("zod");
|
|
60
61
|
const mcp_git_1 = require("./mcp-git");
|
|
61
|
-
|
|
62
|
+
const mcp_artifacts_1 = require("./mcp-artifacts");
|
|
63
|
+
const thread_1 = require("./thread");
|
|
64
|
+
const estado_thread_1 = require("./estado-thread");
|
|
65
|
+
exports.TOOLS_DO_GRAFO = ['ork_grafo_vizinhos', 'ork_grafo_chamadores', 'ork_grafo_importadores', 'ork_grafo_caminho', 'ork_grafo_contexto'];
|
|
62
66
|
/** D4: o teto das respostas, em bytes do JSON. */
|
|
63
67
|
exports.TETO_PADRAO = 32768;
|
|
64
68
|
exports.TETO_MINIMO = 4096;
|
|
@@ -134,7 +138,49 @@ const DEFINICOES = [
|
|
|
134
138
|
schema: zod_1.z.object({ threadId, de: no(`origem: ${DESCRICAO_DO_NO}`), para: no('destino, na mesma forma'), sentido, tipos, tetoBytes }).strict(),
|
|
135
139
|
argv: (a) => ['caminho', a.de, a.para, ...opcoes(a)],
|
|
136
140
|
},
|
|
141
|
+
{
|
|
142
|
+
nome: 'ork_grafo_contexto',
|
|
143
|
+
descricao: 'Pacote deterministico da thread: diff da worktree contra base (ignorado sem worktree), GOAL, PLAN e claims; fan-in agregado e evidencias compactas. '
|
|
144
|
+
+ 'JSON ork.thread-graph-context/v2 limitado em bytes, refs locais, prioridade entre arquivos e por proximidade ao diff. Sem indice do HEAD, recusa com ork grafo indexar.',
|
|
145
|
+
schema: zod_1.z.object({ threadId, tetoBytes }).strict(),
|
|
146
|
+
argv: (a) => ['contexto', a.threadId, ...opcoes(a)],
|
|
147
|
+
},
|
|
137
148
|
];
|
|
149
|
+
/** Leitura compartilhada por CLI e worker, fora da familia do grafo. Nunca escreve estado. */
|
|
150
|
+
function lerEntradaDaThread(raiz, id) {
|
|
151
|
+
if (!/^[a-zA-Z0-9][a-zA-Z0-9._-]{0,79}$/.test(id))
|
|
152
|
+
throw Error('grafo.contexto.thread-invalida: id invalido');
|
|
153
|
+
const projeto = fs.realpathSync((0, estado_thread_1.raizDoEstado)(raiz));
|
|
154
|
+
(0, mcp_artifacts_1.validarArquivosEstadoMcp)(projeto, id);
|
|
155
|
+
const t = (0, thread_1.lerThread)(projeto, id);
|
|
156
|
+
const cwd = fs.realpathSync(t.worktree ?? projeto);
|
|
157
|
+
if (cwd !== projeto && !cwd.startsWith(projeto + path.sep))
|
|
158
|
+
throw Error('grafo.contexto.escopo: worktree fora do projeto');
|
|
159
|
+
if (fs.realpathSync((0, estado_thread_1.raizDoEstado)(cwd)) !== projeto)
|
|
160
|
+
throw Error('grafo.contexto.escopo: worktree de outro repositorio');
|
|
161
|
+
// Em uma WT, consultar outra thread daria um pacote de outra arvore silenciosamente.
|
|
162
|
+
const origem = fs.realpathSync(raiz);
|
|
163
|
+
if (origem !== projeto && origem !== cwd)
|
|
164
|
+
throw Error('grafo.contexto.escopo: thread de outra worktree');
|
|
165
|
+
if (!/^(?:[a-f0-9]{40}|[a-f0-9]{64})$/.test(t.base.commit))
|
|
166
|
+
throw Error('grafo.contexto.base-invalida: thread sem commit base');
|
|
167
|
+
const git = (args) => {
|
|
168
|
+
const r = (0, node_child_process_1.spawnSync)('git', ['-c', 'core.fsmonitor=false', ...args], {
|
|
169
|
+
cwd, env: { ...(0, mcp_git_1.ambienteGitMcp)(), GIT_OPTIONAL_LOCKS: '0' }, encoding: 'utf8', timeout: 10000, maxBuffer: 8 * 1024 * 1024,
|
|
170
|
+
});
|
|
171
|
+
if (r.error || r.status !== 0)
|
|
172
|
+
throw Error('grafo.contexto.git-falhou: nao foi possivel ler HEAD/base/diff da thread');
|
|
173
|
+
return r.stdout;
|
|
174
|
+
};
|
|
175
|
+
const head = git(['rev-parse', '--verify', 'HEAD']).trim();
|
|
176
|
+
const diff = !t.worktree || cwd === projeto ? [] : git(['diff', '--no-ext-diff', '--no-textconv', '--no-renames', '--name-only', '-z', t.base.commit, '--']).split('\0').filter(Boolean);
|
|
177
|
+
const goal = (0, mcp_artifacts_1.lerArtefatoMcp)(projeto, id, 'goal').conteudo;
|
|
178
|
+
const plan = (0, mcp_artifacts_1.lerArtefatoMcp)(projeto, id, 'plan').conteudo;
|
|
179
|
+
const claims = (0, mcp_artifacts_1.listarClaimsMcp)(projeto, id).filter((c) => c.estado !== 'retirada').map(({ id, arquivo }) => ({ id, arquivo }));
|
|
180
|
+
if (head !== git(['rev-parse', '--verify', 'HEAD']).trim())
|
|
181
|
+
throw Error('grafo.contexto.revisao-mudou: HEAD mudou durante a leitura; repita');
|
|
182
|
+
return { raiz: cwd, head, entrada: { thread: id, base: t.base.commit, diff, diffEstado: !t.worktree || cwd === projeto ? 'ignorado-sem-worktree' : 'coletado-na-worktree', goal, plan, claims } };
|
|
183
|
+
}
|
|
138
184
|
/** D9: o argv que a tool monta para os argumentos dados; a medida offline usa o mesmo. */
|
|
139
185
|
function argvDaTool(nome, args) {
|
|
140
186
|
const d = DEFINICOES.find((x) => x.nome === nome);
|
|
@@ -204,7 +250,7 @@ function consultarPeloWorker(raiz, argv, opcoesDoWorker = {}) {
|
|
|
204
250
|
}
|
|
205
251
|
const texto = (t, isError = false) => ({ content: [{ type: 'text', text: t }], ...(isError ? { isError: true } : {}) });
|
|
206
252
|
/**
|
|
207
|
-
* As
|
|
253
|
+
* As cinco tools, chamadas pelo servidor so com a flag ligada no startup. `carregar` e `thread` sao os
|
|
208
254
|
* do servidor: o projeto fixado e a thread conferida (escopo da sessao filha e worktree confinada).
|
|
209
255
|
*/
|
|
210
256
|
function registrarConsultasDoGrafo(registrar, contexto) {
|