@orkastery/cli 0.4.3 → 0.5.1
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/README.md +11 -2
- 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/onboarding.md +3 -2
- 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 +45 -2
- package/adapters/openclaw/dist/hitl-ingress.js +37 -3
- package/adapters/openclaw/dist/index.js +93 -21
- 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 +109 -21
- 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 +24 -7
- package/dist/auditrun.js +1 -1
- 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/claim-lint.js +3 -1
- 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 +135 -25
- package/dist/doctor.js +242 -32
- package/dist/entrega-pr.js +116 -5
- package/dist/experiencia-instalacao.js +256 -0
- package/dist/experiencia.js +72 -0
- package/dist/fabrica-estado.js +63 -12
- package/dist/fechamento.js +105 -0
- package/dist/fix.js +3 -3
- 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 +57 -13
- package/dist/hitl-texto-livre.js +150 -0
- package/dist/hosts.js +92 -3
- package/dist/index.js +637 -45
- package/dist/indice-vetorial.js +317 -0
- package/dist/init.js +24 -5
- package/dist/integracoes-locais.js +3 -1
- package/dist/intelligence-benchmark-contract.js +570 -0
- package/dist/intelligence-graph-cli.js +584 -0
- package/dist/intelligence-graph-contract.js +636 -0
- package/dist/intelligence-graph-extract-md.js +487 -0
- package/dist/intelligence-graph-extract-ts.js +980 -0
- package/dist/intelligence-graph-extract.js +546 -0
- package/dist/intelligence-graph-index.js +693 -0
- package/dist/intelligence-graph-parsers.js +327 -0
- package/dist/intelligence-graph-query.js +344 -0
- package/dist/intelligence-graph-repo.js +197 -0
- package/dist/leases.js +100 -0
- package/dist/ledger.js +23 -0
- package/dist/licoes.js +5 -4
- package/dist/liveness.js +2 -1
- package/dist/maestro-cli.js +10 -1
- package/dist/maestro-contract.js +5 -1
- package/dist/maestro-discovery.js +16 -2
- package/dist/maestro-runtime.js +3 -0
- package/dist/maestro-snapshot.js +6 -2
- package/dist/manifest.js +166 -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-artifacts.js +2 -0
- package/dist/mcp-experiencia.js +164 -0
- package/dist/mcp-git.js +64 -12
- package/dist/mcp-grafo-worker.js +91 -0
- package/dist/mcp-grafo.js +233 -0
- package/dist/mcp-install.js +5 -2
- package/dist/mcp-maestro.js +3 -2
- package/dist/mcp-server.js +93 -5
- package/dist/mcp-ship.js +2 -0
- package/dist/mcp-verify.js +3 -1
- package/dist/memoria.js +147 -1
- package/dist/network-roadmap.js +907 -0
- package/dist/ocupacao.js +43 -3
- package/dist/onboarding.js +122 -11
- package/dist/orkmind.js +165 -9
- package/dist/orquestracao.js +2 -1
- package/dist/parado-no-condutor.js +933 -0
- package/dist/phase.js +166 -13
- package/dist/policies.js +29 -2
- package/dist/projeto-alvo.js +551 -0
- package/dist/prompts.js +1 -1
- package/dist/prova-ativacao.js +345 -0
- package/dist/pulse-consentimento.js +4 -2
- package/dist/pulse-delivery.js +32 -6
- package/dist/pulse-resposta.js +674 -49
- package/dist/pulse.js +74 -3
- package/dist/retry.js +51 -8
- package/dist/roadmap-reservas.js +262 -20
- package/dist/roadmap-status.js +348 -0
- package/dist/runtime-context.js +5 -1
- package/dist/sessoes-inventario.js +7 -1
- package/dist/sessoes.js +4 -1
- package/dist/ship.js +25 -1
- package/dist/thread-close.js +3 -0
- package/dist/thread.js +21 -2
- package/dist/util.js +71 -4
- package/dist/verify.js +6 -5
- package/dist/worktree.js +29 -6
- package/eval/casos/onboarding.json +1 -1
- 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 +10 -2
- 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/skills/phases/ship-release/SKILL.md +6 -3
package/dist/manifest.js
CHANGED
|
@@ -37,10 +37,13 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
37
37
|
};
|
|
38
38
|
})();
|
|
39
39
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
40
|
-
exports.LIMITE_MANIFESTO_BYTES = exports.DIR_ESTADO = exports.NOME_MANIFESTO_LEGADO = exports.NOME_MANIFESTO = void 0;
|
|
40
|
+
exports.EMBEDDING_PADRAO = exports.LIMITE_MANIFESTO_BYTES = exports.DIR_ESTADO = exports.NOME_MANIFESTO_LEGADO = exports.NOME_MANIFESTO = void 0;
|
|
41
|
+
exports.fixarDiretorioDoProjeto = fixarDiretorioDoProjeto;
|
|
42
|
+
exports.diretorioDoProjeto = diretorioDoProjeto;
|
|
41
43
|
exports.acharRaiz = acharRaiz;
|
|
42
44
|
exports.dirEstado = dirEstado;
|
|
43
45
|
exports.validarOptInPlaybook = validarOptInPlaybook;
|
|
46
|
+
exports.configDeEmbedding = configDeEmbedding;
|
|
44
47
|
exports.carregarManifesto = carregarManifesto;
|
|
45
48
|
exports.exigirManifesto = exigirManifesto;
|
|
46
49
|
const fs = __importStar(require("node:fs"));
|
|
@@ -53,13 +56,28 @@ const slug_1 = require("./slug");
|
|
|
53
56
|
const util_1 = require("./util");
|
|
54
57
|
const delegation_1 = require("./delegation");
|
|
55
58
|
const horario_1 = require("./horario");
|
|
59
|
+
const experiencia_1 = require("./experiencia");
|
|
60
|
+
const runtime_ambiente_1 = require("./runtime-ambiente");
|
|
61
|
+
const orkmind_1 = require("./orkmind");
|
|
56
62
|
exports.NOME_MANIFESTO = 'orkastery.yaml';
|
|
57
63
|
exports.NOME_MANIFESTO_LEGADO = 'devmaster.yaml';
|
|
58
64
|
exports.DIR_ESTADO = '.orkastery';
|
|
59
65
|
/** Limite duro: o manifesto nao pode virar o novo arquivo de 100 KB. */
|
|
60
66
|
exports.LIMITE_MANIFESTO_BYTES = 16384;
|
|
67
|
+
let diretorioFixado = null;
|
|
68
|
+
/**
|
|
69
|
+
* RM-052: o projeto-alvo fixado pelo CLI (`--projeto`, `ORK_PROJETO`, host sem cwd) vira o ponto
|
|
70
|
+
* de partida padrao das raizes. Sem ele, vale o cwd, como sempre. Quem fixa e `fixarProjetoAlvo`.
|
|
71
|
+
*/
|
|
72
|
+
function fixarDiretorioDoProjeto(dir) {
|
|
73
|
+
diretorioFixado = dir;
|
|
74
|
+
}
|
|
75
|
+
/** De onde as raizes padrao partem: o projeto-alvo fixado ou, sem ele, o cwd do processo. */
|
|
76
|
+
function diretorioDoProjeto() {
|
|
77
|
+
return diretorioFixado ?? process.cwd();
|
|
78
|
+
}
|
|
61
79
|
/** Raiz do projeto: o diretorio que contem o manifesto; senao, a raiz do repositorio git. */
|
|
62
|
-
function acharRaiz(dirInicial =
|
|
80
|
+
function acharRaiz(dirInicial = diretorioDoProjeto()) {
|
|
63
81
|
const porManifesto = (0, util_1.subirAte)(dirInicial, exports.NOME_MANIFESTO);
|
|
64
82
|
if (porManifesto)
|
|
65
83
|
return porManifesto;
|
|
@@ -116,12 +134,59 @@ function prazosDoVerify(verify, erros) {
|
|
|
116
134
|
saida.timeout_ms_por_comando = porComando;
|
|
117
135
|
return saida;
|
|
118
136
|
}
|
|
137
|
+
/**
|
|
138
|
+
* RM-037 (rm037defeito, defeito 5): `ci.external_repositories`, mapa `dono/nome` para o check exigido no
|
|
139
|
+
* head do PR (texto vazio ou nulo: repositorio sem CI). Forma invalida e erro do manifesto, nunca adivinhada.
|
|
140
|
+
*/
|
|
141
|
+
function repositoriosExternos(v, erros) {
|
|
142
|
+
if (v === undefined || v === null)
|
|
143
|
+
return {};
|
|
144
|
+
if (typeof v !== 'object' || Array.isArray(v)) {
|
|
145
|
+
erros.push('ci.external_repositories deve ser um mapa "dono/nome": "check exigido" (vazio para repositorio sem CI)');
|
|
146
|
+
return {};
|
|
147
|
+
}
|
|
148
|
+
const r = {};
|
|
149
|
+
for (const [repo, check] of Object.entries(v)) {
|
|
150
|
+
if (!/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repo)) {
|
|
151
|
+
erros.push(`ci.external_repositories: "${repo}" nao e dono/nome do GitHub`);
|
|
152
|
+
continue;
|
|
153
|
+
}
|
|
154
|
+
if (check !== null && typeof check !== 'string') {
|
|
155
|
+
erros.push(`ci.external_repositories.${repo}: o check exigido e texto (vazio para sem CI)`);
|
|
156
|
+
continue;
|
|
157
|
+
}
|
|
158
|
+
r[repo] = (check ?? '').trim();
|
|
159
|
+
}
|
|
160
|
+
return r;
|
|
161
|
+
}
|
|
119
162
|
function booleano(v, padrao) {
|
|
120
163
|
return typeof v === 'boolean' ? v : padrao;
|
|
121
164
|
}
|
|
122
165
|
function mapa(v) {
|
|
123
166
|
return v && typeof v === 'object' && !Array.isArray(v) ? v : {};
|
|
124
167
|
}
|
|
168
|
+
/**
|
|
169
|
+
* RM-031 KG5 (D2): `grafo.mcp` so liga com o booleano `true`. Ausente vale `false`; texto que parece
|
|
170
|
+
* booleano, outro tipo ou `grafo` que nao e mapa tambem valem `false`, com aviso: ligar expoe as tools
|
|
171
|
+
* do grafo, e na duvida fica desligado. Chave desconhecida em `grafo` so avisa.
|
|
172
|
+
*/
|
|
173
|
+
function lerGrafo(v, avisos) {
|
|
174
|
+
const mostrar = (x) => JSON.stringify(x).slice(0, 80);
|
|
175
|
+
if (v === undefined || v === null)
|
|
176
|
+
return { mcp: false };
|
|
177
|
+
if (typeof v !== 'object' || Array.isArray(v)) {
|
|
178
|
+
avisos.push(`grafo deve ser um mapa com mcp: true ou false; vale mcp: false (${mostrar(v)} nao reconhecido)`);
|
|
179
|
+
return { mcp: false };
|
|
180
|
+
}
|
|
181
|
+
for (const chave of Object.keys(v))
|
|
182
|
+
if (chave !== 'mcp')
|
|
183
|
+
avisos.push(`grafo.${chave} desconhecida; o bloco grafo so tem mcp`);
|
|
184
|
+
const mcp = v.mcp;
|
|
185
|
+
if (mcp === undefined || mcp === null || typeof mcp === 'boolean')
|
|
186
|
+
return { mcp: mcp === true };
|
|
187
|
+
avisos.push(`grafo.mcp deve ser true ou false; vale false (${mostrar(mcp)} nao reconhecido)`);
|
|
188
|
+
return { mcp: false };
|
|
189
|
+
}
|
|
125
190
|
/** Mesmo contrato na leitura do YAML e nos consumidores de manifesto em memoria. */
|
|
126
191
|
function validarOptInPlaybook(valor) {
|
|
127
192
|
if (valor === undefined)
|
|
@@ -133,6 +198,81 @@ function validarOptInPlaybook(valor) {
|
|
|
133
198
|
}
|
|
134
199
|
return valor.ativo;
|
|
135
200
|
}
|
|
201
|
+
/** I-38 (D5): sem o bloco `memory.embedding`, os embeddings ficam desligados. */
|
|
202
|
+
exports.EMBEDDING_PADRAO = Object.freeze({
|
|
203
|
+
provider: 'none', model: '', dim: 1024, api_key_env: '', fallback_model: '', max_tokens_por_execucao: 1_000_000,
|
|
204
|
+
});
|
|
205
|
+
const CHAVES_DE_EMBEDDING = ['provider', 'model', 'dim', 'api_key_env', 'fallback_model', 'max_tokens_por_execucao'];
|
|
206
|
+
/** Valor que parece segredo (URL, prefixo de chave, sequencia longa) nunca e repetido num erro. */
|
|
207
|
+
function exibir(v) {
|
|
208
|
+
const t = typeof v === 'string' ? v : JSON.stringify(v);
|
|
209
|
+
return /:\/\/|\bsk-|[A-Za-z0-9_-]{32,}/.test(t) ? '(valor omitido: parece segredo)' : JSON.stringify(v);
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* I-38 (D5): le e valida `memory.embedding`. A chave so entra pelo NOME da variavel: valor com
|
|
213
|
+
* cara de chave ou DSN reprova o manifesto, e nome da lista de provider pago tambem, porque a
|
|
214
|
+
* entrada do `ork` apagaria a variavel sob `subscription-only` e o preflight reprovaria o codex.
|
|
215
|
+
*/
|
|
216
|
+
function lerEmbedding(bruto, variavelDaDsn, erros) {
|
|
217
|
+
if (bruto === undefined || bruto === null)
|
|
218
|
+
return undefined;
|
|
219
|
+
if (typeof bruto !== 'object' || Array.isArray(bruto)) {
|
|
220
|
+
erros.push('memory.embedding precisa ser um bloco com provider, model, dim, api_key_env, fallback_model e max_tokens_por_execucao');
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
const e = bruto;
|
|
224
|
+
for (const chave of Object.keys(e)) {
|
|
225
|
+
if (!CHAVES_DE_EMBEDDING.includes(chave)) {
|
|
226
|
+
erros.push(`memory.embedding.${chave} nao existe (a chave entra pelo ambiente: declare so o NOME em api_key_env)`);
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
const provider = texto(e.provider, exports.EMBEDDING_PADRAO.provider).trim();
|
|
230
|
+
if (provider !== 'none' && provider !== 'openrouter') {
|
|
231
|
+
erros.push(`memory.embedding.provider invalido: ${exibir(provider)} (aceitos: none, openrouter)`);
|
|
232
|
+
}
|
|
233
|
+
const model = texto(e.model, '').trim();
|
|
234
|
+
const apiKeyEnv = texto(e.api_key_env, '').trim();
|
|
235
|
+
const fallback = texto(e.fallback_model, '').trim();
|
|
236
|
+
const inteiro = (chave, v, padrao, min, max) => {
|
|
237
|
+
if (v === undefined || v === null)
|
|
238
|
+
return padrao;
|
|
239
|
+
const n = typeof v === 'number' ? v : typeof v === 'string' && v.trim() !== '' ? Number(v) : NaN;
|
|
240
|
+
if (!Number.isInteger(n) || n < min || n > max) {
|
|
241
|
+
erros.push(`memory.embedding.${chave} precisa ser inteiro entre ${min} e ${max} (recebido ${exibir(v)})`);
|
|
242
|
+
return padrao;
|
|
243
|
+
}
|
|
244
|
+
return n;
|
|
245
|
+
};
|
|
246
|
+
const dim = inteiro('dim', e.dim, exports.EMBEDDING_PADRAO.dim, 32, 4096);
|
|
247
|
+
const teto = inteiro('max_tokens_por_execucao', e.max_tokens_por_execucao, exports.EMBEDDING_PADRAO.max_tokens_por_execucao, 1, 10_000_000);
|
|
248
|
+
if (apiKeyEnv.includes('://') || /^sk-/i.test(apiKeyEnv)) {
|
|
249
|
+
erros.push('memory.embedding.api_key_env recebe o NOME da variavel de ambiente com a chave, nunca o valor da chave nem uma DSN');
|
|
250
|
+
}
|
|
251
|
+
else if (apiKeyEnv !== '' && !/^[A-Z][A-Z0-9_]*$/.test(apiKeyEnv)) {
|
|
252
|
+
erros.push('memory.embedding.api_key_env invalido: esperado nome de variavel de ambiente, [A-Z][A-Z0-9_]* (o valor recebido nao e repetido)');
|
|
253
|
+
}
|
|
254
|
+
else if (runtime_ambiente_1.ENVS_DE_PROVIDER_PAGO.includes(apiKeyEnv)) {
|
|
255
|
+
erros.push(`memory.embedding.api_key_env nao pode ser ${apiKeyEnv}: nomes de provider pago sao removidos sob subscription-only; use uma chave dedicada com nome proprio`);
|
|
256
|
+
}
|
|
257
|
+
else if (apiKeyEnv !== '' && apiKeyEnv === variavelDaDsn) {
|
|
258
|
+
erros.push('memory.embedding.api_key_env nao pode repetir memory.database_url_env: a DSN nunca vai ao provider');
|
|
259
|
+
}
|
|
260
|
+
if (provider === 'openrouter') {
|
|
261
|
+
if (!orkmind_1.MODELO_DE_EMBEDDING.test(model))
|
|
262
|
+
erros.push(`memory.embedding.model invalido: ${exibir(model)} (esperado org/nome, por exemplo qwen/qwen3-embedding-8b)`);
|
|
263
|
+
if (apiKeyEnv === '')
|
|
264
|
+
erros.push('memory.embedding.api_key_env e obrigatorio com provider openrouter (o NOME da variavel com a chave dedicada)');
|
|
265
|
+
}
|
|
266
|
+
if (fallback !== '' && !orkmind_1.MODELO_DE_EMBEDDING.test(fallback)) {
|
|
267
|
+
erros.push(`memory.embedding.fallback_model invalido: ${exibir(fallback)} (esperado org/nome de um modelo do Hugging Face)`);
|
|
268
|
+
}
|
|
269
|
+
return { provider: provider === 'openrouter' ? 'openrouter' : 'none', model, dim, api_key_env: apiKeyEnv,
|
|
270
|
+
fallback_model: fallback, max_tokens_por_execucao: teto };
|
|
271
|
+
}
|
|
272
|
+
/** I-38 (D5): leitura unica da configuracao de embedding, com o padrao `none`. */
|
|
273
|
+
function configDeEmbedding(manifesto) {
|
|
274
|
+
return { ...exports.EMBEDDING_PADRAO, ...(manifesto.memory.embedding ?? {}) };
|
|
275
|
+
}
|
|
136
276
|
/** I-33 (N2): texto e numero que dizem sim ou nao sem ambiguidade; o resto nao e reconhecido. */
|
|
137
277
|
const BOOLEANOS_RECONHECIVEIS = new Map([
|
|
138
278
|
['true', true], ['yes', true], ['on', true], ['sim', true], ['1', true],
|
|
@@ -146,7 +286,7 @@ function booleanoReconhecivel(v) {
|
|
|
146
286
|
return BOOLEANOS_RECONHECIVEIS.get(v.trim().toLowerCase()) ?? null;
|
|
147
287
|
}
|
|
148
288
|
/** Le e valida o manifesto. Nunca lanca: erros e avisos voltam na estrutura. */
|
|
149
|
-
function carregarManifesto(dirInicial =
|
|
289
|
+
function carregarManifesto(dirInicial = diretorioDoProjeto()) {
|
|
150
290
|
const raiz = acharRaiz(dirInicial);
|
|
151
291
|
const candidatos = [
|
|
152
292
|
{ caminho: path.join(raiz, exports.NOME_MANIFESTO), legado: false },
|
|
@@ -171,6 +311,22 @@ function carregarManifesto(dirInicial = process.cwd()) {
|
|
|
171
311
|
const fusoDoDono = (0, horario_1.lerFusoDoDono)(mapa(dados.owner).timezone);
|
|
172
312
|
if (fusoDoDono.aviso)
|
|
173
313
|
avisos.push(fusoDoDono.aviso);
|
|
314
|
+
// Preferência inválida segue o fuso: avisa e cai no padrão, sem parar os comandos (e o próprio
|
|
315
|
+
// onboarding set, que é o caminho para corrigir).
|
|
316
|
+
const preferencias = {};
|
|
317
|
+
for (const chave of ['language', 'depth', 'experience']) {
|
|
318
|
+
const valor = mapa(dados.owner)[chave];
|
|
319
|
+
if (valor === undefined)
|
|
320
|
+
continue;
|
|
321
|
+
try {
|
|
322
|
+
Object.assign(preferencias, (0, experiencia_1.validarPreferencias)({ [chave]: valor }));
|
|
323
|
+
}
|
|
324
|
+
catch (e) {
|
|
325
|
+
avisos.push(`${e.message}; vale o padrão até corrigir`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
if (fusoDoDono.origem === 'manifesto')
|
|
329
|
+
preferencias.timezone = fusoDoDono.fuso;
|
|
174
330
|
const board = mapa(dados.board);
|
|
175
331
|
const runtime = mapa(dados.runtime);
|
|
176
332
|
const conduction = mapa(dados.conduction);
|
|
@@ -299,6 +455,7 @@ function carregarManifesto(dirInicial = process.cwd()) {
|
|
|
299
455
|
avisos.push('memory.mode: orkmind sem memory.database_url_env: o regime efetivo cai para files ' +
|
|
300
456
|
'(o `ork` nao adivinha DSN, e base alheia e pior do que base nenhuma)');
|
|
301
457
|
}
|
|
458
|
+
const embedding = lerEmbedding(memory.embedding, variavelDaDsn, erros);
|
|
302
459
|
const bytes = Buffer.byteLength(bruto, 'utf8');
|
|
303
460
|
if (bytes > exports.LIMITE_MANIFESTO_BYTES) {
|
|
304
461
|
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)`);
|
|
@@ -313,7 +470,7 @@ function carregarManifesto(dirInicial = process.cwd()) {
|
|
|
313
470
|
stage: texto(project.stage, 'nascente') ?? 'nascente',
|
|
314
471
|
repo_root: texto(project.repo_root, raiz),
|
|
315
472
|
},
|
|
316
|
-
...(
|
|
473
|
+
...(Object.keys(preferencias).length ? { owner: preferencias } : {}),
|
|
317
474
|
board: {
|
|
318
475
|
adapter: texto(board.adapter, 'hermes-kanban'),
|
|
319
476
|
default: texto(board.default, 'default'),
|
|
@@ -350,6 +507,7 @@ function carregarManifesto(dirInicial = process.cwd()) {
|
|
|
350
507
|
required_for_ship: booleano(ci.required_for_ship, false),
|
|
351
508
|
context: texto(ci.context, 'ork-verify'),
|
|
352
509
|
command: ci.command === null || ci.command === undefined ? undefined : texto(ci.command, ''),
|
|
510
|
+
external_repositories: repositoriosExternos(ci.external_repositories, erros),
|
|
353
511
|
},
|
|
354
512
|
concurrency: {
|
|
355
513
|
max_parallel_threads: numero(concurrency.max_parallel_threads, 3),
|
|
@@ -382,6 +540,7 @@ function carregarManifesto(dirInicial = process.cwd()) {
|
|
|
382
540
|
cli: texto(memory.cli, 'orkmind'),
|
|
383
541
|
tenant: texto(memory.tenant, '') || nomeProjeto,
|
|
384
542
|
timeout_ms: numero(memory.timeout_ms, 15000),
|
|
543
|
+
...(embedding ? { embedding } : {}),
|
|
385
544
|
},
|
|
386
545
|
// Bloco B5: governanca de custo dos auditores. Ausente no manifesto quer dizer
|
|
387
546
|
// "nao declarada", nao "sem limite": a rodada avisa que so `eco` e o escopo
|
|
@@ -397,12 +556,14 @@ function carregarManifesto(dirInicial = process.cwd()) {
|
|
|
397
556
|
compartilhada: booleano(fabrica.compartilhada, false),
|
|
398
557
|
remoto: texto(fabrica.remoto, 'origin'),
|
|
399
558
|
},
|
|
559
|
+
// RM-031 KG5 (D2): expor as tools do grafo no MCP e opt-in do dono.
|
|
560
|
+
grafo: lerGrafo(dados.grafo, avisos),
|
|
400
561
|
policies: Object.fromEntries(Object.entries(mapa(dados.policies)).map(([k, v]) => [k, texto(v, '')])),
|
|
401
562
|
};
|
|
402
563
|
return { caminho: achado.caminho, raiz, legado: achado.legado, bytes, manifesto, erros, avisos };
|
|
403
564
|
}
|
|
404
565
|
/** Carrega o manifesto ou encerra com mensagem acionavel (uso nos subcomandos). */
|
|
405
|
-
function exigirManifesto(dirInicial =
|
|
566
|
+
function exigirManifesto(dirInicial = diretorioDoProjeto()) {
|
|
406
567
|
const carregado = carregarManifesto(dirInicial);
|
|
407
568
|
if (!carregado) {
|
|
408
569
|
throw new Error(`manifesto ${exports.NOME_MANIFESTO} nao encontrado a partir de ${dirInicial}. Rode: ork init`);
|
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';
|
package/dist/mcp-artifacts.js
CHANGED
|
@@ -157,6 +157,8 @@ function comEscrita(raiz, id, executar) {
|
|
|
157
157
|
}
|
|
158
158
|
}
|
|
159
159
|
const nome = `path:.orkastery/threads/${id}`;
|
|
160
|
+
// RM-037 (fatia 3, defeito 4): lease e fila de thread fechada sao orfaos e saem antes da conferencia.
|
|
161
|
+
(0, leases_1.podarRegioesDeThreadsFechadas)(raiz, [nome], id);
|
|
160
162
|
if ((0, leases_1.lerLease)(raiz, nome) || (0, leases_1.leasesColidentes)(raiz, nome).length)
|
|
161
163
|
throw Error('lease.busy: estado canonico ocupado');
|
|
162
164
|
const r = (0, leases_1.adquirirRegiao)(raiz, nome, { thread: id, motivo: 'MCP documento ou claim', ttlMs: 30000, retomarVencido: false });
|