@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
|
@@ -0,0 +1,345 @@
|
|
|
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.ENTRADAS = exports.HOSTS_DA_PROVA = void 0;
|
|
37
|
+
exports.transcriptDoClaude = transcriptDoClaude;
|
|
38
|
+
exports.transcriptDoOpenclaw = transcriptDoOpenclaw;
|
|
39
|
+
exports.extrairSnapshot = extrairSnapshot;
|
|
40
|
+
exports.conferirProva = conferirProva;
|
|
41
|
+
exports.redigir = redigir;
|
|
42
|
+
exports.redigirObjeto = redigirObjeto;
|
|
43
|
+
/**
|
|
44
|
+
* RM-032: conferência da prova de ativação por host, sem host.
|
|
45
|
+
*
|
|
46
|
+
* O roteiro `core/scripts/prova-ativacao.cjs` abre uma sessão nova e não interativa no host
|
|
47
|
+
* (Claude Code ou OpenClaw), diz `orkastery maestro` e entrega aqui o transcript. Esta
|
|
48
|
+
* conferência olha só o que é determinístico: qual ferramenta o modelo chamou, o resultado que
|
|
49
|
+
* ela devolveu (contra o contrato da entrada) e o projeto lido. Da resposta final em texto
|
|
50
|
+
* livre, exige apenas que cite o projeto e não repita o incidente de 29/09 ("roadmap vazio").
|
|
51
|
+
*/
|
|
52
|
+
const fs = __importStar(require("node:fs"));
|
|
53
|
+
const path = __importStar(require("node:path"));
|
|
54
|
+
const maestro_contract_1 = require("./maestro-contract");
|
|
55
|
+
exports.HOSTS_DA_PROVA = ['claude-code', 'openclaw'];
|
|
56
|
+
/**
|
|
57
|
+
* As entradas da frase em cada host. No Claude, a tool MCP do servidor `orkastery` fixado no
|
|
58
|
+
* projeto (MAESTRO_HOST_SURFACES em hosts.ts). No OpenClaw 0.5.0 (RM-054, fatia 2), a frase sem
|
|
59
|
+
* projeto vai a `ork_network_roadmap`, a única tool da extensão no perfil `coding`; com projeto
|
|
60
|
+
* nomeado, a `ork_maestro`. `ork maestro` pelo shell do host é desvio: o CLI resolve o projeto
|
|
61
|
+
* pelo diretório atual, a classe de erro do incidente.
|
|
62
|
+
*/
|
|
63
|
+
exports.ENTRADAS = {
|
|
64
|
+
'claude-code': [{ ferramenta: 'mcp__orkastery__ork_maestro', contrato: 'maestro' }],
|
|
65
|
+
openclaw: [{ ferramenta: 'ork_network_roadmap', contrato: 'rede' }, { ferramenta: 'ork_maestro', contrato: 'maestro' }],
|
|
66
|
+
};
|
|
67
|
+
const SHELL_DO_HOST = { 'claude-code': 'Bash', openclaw: 'exec' };
|
|
68
|
+
const ORK_MAESTRO_NO_SHELL = /(^|[\s;&|(/])ork(\s+(--projeto|--project|-p)(\s+|=)\S+)?\s+maestro\b/;
|
|
69
|
+
function linhasJson(texto) {
|
|
70
|
+
const eventos = [];
|
|
71
|
+
for (const linha of texto.split('\n')) {
|
|
72
|
+
const limpa = linha.trim();
|
|
73
|
+
if (!limpa.startsWith('{'))
|
|
74
|
+
continue;
|
|
75
|
+
try {
|
|
76
|
+
eventos.push(JSON.parse(limpa));
|
|
77
|
+
}
|
|
78
|
+
catch { /* linha de log do host, não evento */ }
|
|
79
|
+
}
|
|
80
|
+
return eventos;
|
|
81
|
+
}
|
|
82
|
+
function textoDoConteudo(conteudo) {
|
|
83
|
+
if (typeof conteudo === 'string')
|
|
84
|
+
return conteudo;
|
|
85
|
+
if (!Array.isArray(conteudo))
|
|
86
|
+
return null;
|
|
87
|
+
const partes = conteudo.filter((c) => !!c && typeof c === 'object' && c.type === 'text' && typeof c.text === 'string');
|
|
88
|
+
return partes.length ? partes.map(p => p.text).join('\n') : null;
|
|
89
|
+
}
|
|
90
|
+
function classificar(host, ferramenta, argumentos) {
|
|
91
|
+
const entrada = exports.ENTRADAS[host].find(e => e.ferramenta === ferramenta);
|
|
92
|
+
if (entrada)
|
|
93
|
+
return { via: 'entrada', contrato: entrada.contrato };
|
|
94
|
+
const comando = argumentos?.command;
|
|
95
|
+
if (ferramenta === SHELL_DO_HOST[host] && typeof comando === 'string' && ORK_MAESTRO_NO_SHELL.test(comando))
|
|
96
|
+
return { via: 'shell', contrato: null };
|
|
97
|
+
return null;
|
|
98
|
+
}
|
|
99
|
+
/** Transcript `claude -p --output-format stream-json --verbose`. */
|
|
100
|
+
function transcriptDoClaude(streamJson) {
|
|
101
|
+
const eventos = linhasJson(streamJson);
|
|
102
|
+
let ferramentasExpostas = null;
|
|
103
|
+
let respostaFinal = null;
|
|
104
|
+
const pedidas = new Map();
|
|
105
|
+
const chamadas = [];
|
|
106
|
+
for (const e of eventos) {
|
|
107
|
+
if (e.type === 'system' && e.subtype === 'init' && Array.isArray(e.tools))
|
|
108
|
+
ferramentasExpostas = e.tools.filter((t) => typeof t === 'string');
|
|
109
|
+
const mensagem = e.message;
|
|
110
|
+
const blocos = Array.isArray(mensagem?.content) ? mensagem.content : [];
|
|
111
|
+
if (e.type === 'assistant') {
|
|
112
|
+
for (const b of blocos) {
|
|
113
|
+
if (b?.type !== 'tool_use' || typeof b.name !== 'string' || typeof b.id !== 'string')
|
|
114
|
+
continue;
|
|
115
|
+
const tipo = classificar('claude-code', b.name, b.input);
|
|
116
|
+
if (!tipo)
|
|
117
|
+
continue;
|
|
118
|
+
const chamada = { ferramenta: b.name, ...tipo, argumentos: b.input ?? null, resultado: null, erro: false };
|
|
119
|
+
pedidas.set(b.id, chamada);
|
|
120
|
+
chamadas.push(chamada);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
else if (e.type === 'user') {
|
|
124
|
+
for (const b of blocos) {
|
|
125
|
+
if (b?.type !== 'tool_result' || typeof b.tool_use_id !== 'string')
|
|
126
|
+
continue;
|
|
127
|
+
const chamada = pedidas.get(b.tool_use_id);
|
|
128
|
+
if (!chamada)
|
|
129
|
+
continue;
|
|
130
|
+
chamada.resultado = textoDoConteudo(b.content);
|
|
131
|
+
chamada.erro = b.is_error === true;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else if (e.type === 'result' && typeof e.result === 'string')
|
|
135
|
+
respostaFinal = e.result;
|
|
136
|
+
}
|
|
137
|
+
return { ferramentasExpostas, chamadas, respostaFinal };
|
|
138
|
+
}
|
|
139
|
+
/**
|
|
140
|
+
* Trajetória do OpenClaw (`openclaw sessions export-trajectory`, `events.jsonl`): eventos
|
|
141
|
+
* `tool.call`/`tool.result` da origem `runtime`. As ferramentas expostas e a resposta final vêm
|
|
142
|
+
* do `openclaw agent --json` (`meta.systemPromptReport.tools.entries`, `payloads[].text`).
|
|
143
|
+
*/
|
|
144
|
+
function transcriptDoOpenclaw(eventsJsonl, agenteJson) {
|
|
145
|
+
const pedidas = new Map();
|
|
146
|
+
const chamadas = [];
|
|
147
|
+
for (const e of linhasJson(eventsJsonl)) {
|
|
148
|
+
if (e.source !== 'runtime')
|
|
149
|
+
continue;
|
|
150
|
+
const d = (e.data ?? {});
|
|
151
|
+
if (typeof d.toolCallId !== 'string' || typeof d.name !== 'string')
|
|
152
|
+
continue;
|
|
153
|
+
if (e.type === 'tool.call') {
|
|
154
|
+
const tipo = classificar('openclaw', d.name, d.args);
|
|
155
|
+
if (!tipo)
|
|
156
|
+
continue;
|
|
157
|
+
const chamada = { ferramenta: d.name, ...tipo, argumentos: d.args ?? null, resultado: null, erro: false };
|
|
158
|
+
pedidas.set(d.toolCallId, chamada);
|
|
159
|
+
chamadas.push(chamada);
|
|
160
|
+
}
|
|
161
|
+
else if (e.type === 'tool.result') {
|
|
162
|
+
const chamada = pedidas.get(d.toolCallId);
|
|
163
|
+
if (!chamada)
|
|
164
|
+
continue;
|
|
165
|
+
chamada.resultado = textoDoConteudo(d.result?.content);
|
|
166
|
+
chamada.erro = d.success !== true;
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
const agente = (agenteJson ?? {});
|
|
170
|
+
const entradas = agente.meta?.systemPromptReport?.tools?.entries;
|
|
171
|
+
const ferramentasExpostas = Array.isArray(entradas) ? entradas.map(t => t?.name).filter((n) => typeof n === 'string') : null;
|
|
172
|
+
const textos = (agente.payloads ?? []).map(p => p?.text).filter((t) => typeof t === 'string');
|
|
173
|
+
return { ferramentasExpostas, chamadas, respostaFinal: textos.length ? textos.join('\n') : null };
|
|
174
|
+
}
|
|
175
|
+
/** O primeiro objeto `ork.maestro-snapshot/v1` do texto (o host pode juntar stderr ao stdout). */
|
|
176
|
+
function extrairSnapshot(texto) {
|
|
177
|
+
const inicio = texto.indexOf('{"schema":"ork.maestro-snapshot/v1"');
|
|
178
|
+
if (inicio < 0)
|
|
179
|
+
return null;
|
|
180
|
+
let profundidade = 0, emTexto = false, escape = false;
|
|
181
|
+
for (let i = inicio; i < texto.length; i++) {
|
|
182
|
+
const c = texto[i];
|
|
183
|
+
if (emTexto) {
|
|
184
|
+
if (escape)
|
|
185
|
+
escape = false;
|
|
186
|
+
else if (c === '\\')
|
|
187
|
+
escape = true;
|
|
188
|
+
else if (c === '"')
|
|
189
|
+
emTexto = false;
|
|
190
|
+
}
|
|
191
|
+
else if (c === '"')
|
|
192
|
+
emTexto = true;
|
|
193
|
+
else if (c === '{')
|
|
194
|
+
profundidade++;
|
|
195
|
+
else if (c === '}' && --profundidade === 0) {
|
|
196
|
+
try {
|
|
197
|
+
return JSON.parse(texto.slice(inicio, i + 1));
|
|
198
|
+
}
|
|
199
|
+
catch {
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
return null;
|
|
205
|
+
}
|
|
206
|
+
function expandirHome(raiz, home) {
|
|
207
|
+
return raiz === '~' ? home : raiz.startsWith('~/') ? path.join(home, raiz.slice(2)) : raiz;
|
|
208
|
+
}
|
|
209
|
+
const ROADMAP_VAZIO = /\broadmap\b:?((?:\s+[^\s.!?:;,]+){0,4}?)\s+(vazio|empty)\b/giu;
|
|
210
|
+
const NEGACAO = /(^|[^\p{L}])(n[ãa]o|nunca|jamais|not|never|isn't|doesn't|nem)([^\p{L}]|$)/iu;
|
|
211
|
+
/**
|
|
212
|
+
* A frase que conclui "roadmap vazio". A negação só conta quando governa a conclusão: dentro do
|
|
213
|
+
* trecho ("the roadmap is not empty") ou nas até seis palavras antes dele, sem atravessar
|
|
214
|
+
* pontuação ("zero threads não quer dizer roadmap vazio"). "O roadmap está vazio e não há
|
|
215
|
+
* threads" e "Não há itens: o roadmap está vazio" concluem.
|
|
216
|
+
*/
|
|
217
|
+
function concluiRoadmapVazio(texto) {
|
|
218
|
+
for (const m of texto.matchAll(ROADMAP_VAZIO)) {
|
|
219
|
+
const antes = texto.slice(0, m.index).split(/[.!?:;,\n]/).pop().trim().split(/\s+/).slice(-6).join(' ');
|
|
220
|
+
if (!NEGACAO.test(antes) && !NEGACAO.test(m[1]))
|
|
221
|
+
return `${antes} ${m[0]}`.trim();
|
|
222
|
+
}
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
const canonica = (p) => { try {
|
|
226
|
+
return fs.realpathSync(p);
|
|
227
|
+
}
|
|
228
|
+
catch {
|
|
229
|
+
return path.resolve(p);
|
|
230
|
+
} };
|
|
231
|
+
/** Nome inteiro, não pedaço de palavra: a abreviação `pav` não casa com "pavimento". */
|
|
232
|
+
const nomeia = (texto, nome) => new RegExp(`(^|[^\\p{L}\\p{N}_-])${nome.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}([^\\p{L}\\p{N}_-]|$)`, 'iu').test(texto);
|
|
233
|
+
/**
|
|
234
|
+
* O texto de `ork network roadmap` (`textoDoPanoramaDaRede`): a primeira linha diz de onde foi
|
|
235
|
+
* lido, a segunda quais projetos foram consultados (`<nome> (clone em <raiz>)`) e o bloco
|
|
236
|
+
* "Não consultado:" o que ficou de fora.
|
|
237
|
+
*/
|
|
238
|
+
function lerPanoramaDaRede(texto) {
|
|
239
|
+
const linhas = texto.split('\n');
|
|
240
|
+
const i = linhas.findIndex(l => /^Panorama da rede lido de /.test(l));
|
|
241
|
+
if (i < 0 || !/^Consultado: /.test(linhas[i + 1] ?? '') || linhas[i + 2] !== 'Não consultado:')
|
|
242
|
+
return null;
|
|
243
|
+
const naoConsultado = [];
|
|
244
|
+
for (const l of linhas.slice(i + 3)) {
|
|
245
|
+
if (!l.startsWith('• '))
|
|
246
|
+
break;
|
|
247
|
+
naoConsultado.push(l.slice(2));
|
|
248
|
+
}
|
|
249
|
+
return { cabecalho: linhas[i], consultado: linhas[i + 1], naoConsultado };
|
|
250
|
+
}
|
|
251
|
+
function conferirProva(host, t, esperado) {
|
|
252
|
+
const conferencias = [];
|
|
253
|
+
const conferir = (id, ok, detalhe) => { conferencias.push({ id, ok, detalhe }); return ok; };
|
|
254
|
+
const nomes = exports.ENTRADAS[host].map(e => e.ferramenta);
|
|
255
|
+
const expostas = nomes.filter(n => t.ferramentasExpostas?.includes(n));
|
|
256
|
+
conferir('entrada.exposta', expostas.length > 0, t.ferramentasExpostas === null ? 'o transcript não lista as ferramentas expostas'
|
|
257
|
+
: expostas.length ? `${expostas.join(', ')} exposta(s) ao modelo`
|
|
258
|
+
: `${nomes.join(' e ')} ausente(s) entre ${t.ferramentasExpostas.length} ferramentas expostas`);
|
|
259
|
+
// A última chamada contratada com resultado é a que vale (uma nova tentativa depois de erro não
|
|
260
|
+
// reprova); qualquer `ork maestro` pelo shell reprova, mesmo ao lado da chamada contratada.
|
|
261
|
+
const contratadas = t.chamadas.filter(c => c.via === 'entrada');
|
|
262
|
+
const contratada = [...contratadas].reverse().find(c => c.resultado !== null && !c.erro) ?? contratadas.at(-1);
|
|
263
|
+
const desvio = t.chamadas.find(c => c.via === 'shell');
|
|
264
|
+
const comando = desvio && JSON.stringify(desvio.argumentos?.command);
|
|
265
|
+
conferir('entrada.chamada', !!contratada && !desvio, contratada && desvio ? `${contratada.ferramenta} chamada, mas também desvio: ${desvio.ferramenta} ${comando}`
|
|
266
|
+
: contratada ? `${contratada.ferramenta} chamada`
|
|
267
|
+
: desvio ? `desvio: ${desvio.ferramenta} ${comando} em vez de ${nomes.join(' ou ')}`
|
|
268
|
+
: `${nomes.join(' ou ')} não foi chamada`);
|
|
269
|
+
let snapshot = null;
|
|
270
|
+
let rede = null;
|
|
271
|
+
if (contratada && conferir('resultado.sem-erro', contratada.resultado !== null && !contratada.erro, contratada.resultado === null ? 'a chamada não teve resultado' : contratada.erro ? `erro: ${contratada.resultado.slice(0, 300)}` : 'resultado sem erro')) {
|
|
272
|
+
if (contratada.contrato === 'maestro') {
|
|
273
|
+
try {
|
|
274
|
+
snapshot = (0, maestro_contract_1.validateMaestroSnapshot)(extrairSnapshot(contratada.resultado));
|
|
275
|
+
conferir('resultado.contrato', true, `${snapshot.schema} validado`);
|
|
276
|
+
}
|
|
277
|
+
catch (e) {
|
|
278
|
+
conferir('resultado.contrato', false, `fora do contrato: ${e.message.slice(0, 300)}`);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
else {
|
|
282
|
+
rede = lerPanoramaDaRede(contratada.resultado);
|
|
283
|
+
conferir('resultado.contrato', rede !== null, rede ? 'texto do ork.network-roadmap/v1: cabeçalho, Consultado e Não consultado'
|
|
284
|
+
: 'fora do contrato: sem o cabeçalho "Panorama da rede lido de", "Consultado:" e "Não consultado:"');
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (snapshot) {
|
|
288
|
+
const p = snapshot.project;
|
|
289
|
+
const exibida = p.root === undefined || p.root.includes('[caminho privado]') ? null : canonica(expandirHome(p.root, esperado.home));
|
|
290
|
+
const mesmaCopia = p.fingerprint === esperado.projeto.fingerprint && (exibida === null || exibida === canonica(esperado.projeto.raiz));
|
|
291
|
+
conferir('resultado.projeto', p.name === esperado.projeto.nome && mesmaCopia, `leu ${p.name} (${p.origin}) em ${p.root ?? 'raiz não exibida'}, impressão ${p.fingerprint.slice(0, 12)}; ` +
|
|
292
|
+
`esperado ${esperado.projeto.nome}, impressão ${esperado.projeto.fingerprint.slice(0, 12)}`);
|
|
293
|
+
const lacunas = snapshot.notConsulted ?? [];
|
|
294
|
+
conferir('resultado.nao-consultado', lacunas.length > 0, lacunas.length ? `declara o que não leu: ${lacunas.length} item(ns)` : 'notConsulted ausente: zero threads pode virar "roadmap vazio"');
|
|
295
|
+
}
|
|
296
|
+
if (rede) {
|
|
297
|
+
// "Consultado: a (clone em X); b (clone em Y)": vale o projeto esperado na raiz esperada.
|
|
298
|
+
const lidos = [...rede.consultado.replace(/^Consultado: /, '').matchAll(/(?:^|; )(.+?) \(clone em ([^)]+)\)/g)];
|
|
299
|
+
const leu = lidos.some(([, nome, raiz]) => nome === esperado.projeto.nome &&
|
|
300
|
+
canonica(expandirHome(raiz, esperado.home)) === canonica(esperado.projeto.raiz));
|
|
301
|
+
conferir('resultado.projeto', leu, `${rede.consultado.slice(0, 300)}; esperado ${esperado.projeto.nome} (clone em ${esperado.projeto.raiz})`);
|
|
302
|
+
conferir('resultado.nao-consultado', rede.naoConsultado.length > 0, rede.naoConsultado.length ? `declara o que não leu: ${rede.naoConsultado.length} item(ns)` : 'sem o bloco do que não foi consultado');
|
|
303
|
+
}
|
|
304
|
+
const resposta = t.respostaFinal ?? '';
|
|
305
|
+
const citaProjeto = [esperado.projeto.nome, esperado.projeto.id].some(n => nomeia(resposta, n));
|
|
306
|
+
conferir('resposta.cita-projeto', citaProjeto, t.respostaFinal === null ? 'sem resposta final' : citaProjeto ? 'a resposta nomeia o projeto lido' : 'a resposta não nomeia o projeto lido');
|
|
307
|
+
const vazio = concluiRoadmapVazio(resposta);
|
|
308
|
+
conferir('resposta.sem-roadmap-vazio', vazio === null, vazio ? `a resposta conclui "roadmap vazio" a partir do panorama (incidente de 29/09): ${vazio.slice(0, 200)}` : 'sem concluir "roadmap vazio" a partir do panorama');
|
|
309
|
+
return {
|
|
310
|
+
ok: conferencias.every(c => c.ok),
|
|
311
|
+
conferencias,
|
|
312
|
+
snapshot: snapshot && {
|
|
313
|
+
schema: snapshot.schema, projeto: snapshot.project,
|
|
314
|
+
secoes: Object.fromEntries(Object.entries(snapshot.sections).map(([nome, s]) => [nome, s.state])),
|
|
315
|
+
notConsulted: snapshot.notConsulted ?? [],
|
|
316
|
+
},
|
|
317
|
+
rede,
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
/**
|
|
321
|
+
* Redação antes de qualquer gravação: o recibo vai ao repositório. Cobre tokens com prefixo
|
|
322
|
+
* conhecido, `Bearer`/`Basic`, variáveis de ambiente com nome de segredo e valores de chaves JSON
|
|
323
|
+
* com nome de segredo. Para objetos, use `redigirObjeto`, que aplica a regra a cada texto antes
|
|
324
|
+
* da serialização (JSON dentro de stdout escaparia depois dela).
|
|
325
|
+
*/
|
|
326
|
+
const CHAVE_DE_SEGREDO = /token|secret|password|passwd|api[_-]?key|apikey|credential|authorization|cookie/i;
|
|
327
|
+
function redigir(texto) {
|
|
328
|
+
return texto
|
|
329
|
+
.replace(/\b(sk-ant-[A-Za-z0-9_-]+|sk-or-[A-Za-z0-9_-]+|sk-[A-Za-z0-9_-]{16,}|[sr]k_live_[A-Za-z0-9]{10,}|AIza[0-9A-Za-z_-]{20,})/g, '[REDIGIDO]')
|
|
330
|
+
.replace(/\b(gh[pousr]_[A-Za-z0-9]{20,}|github_pat_[A-Za-z0-9_]{20,}|glpat-[A-Za-z0-9_-]{16,}|xox[abprs]-[A-Za-z0-9-]{10,})/g, '[REDIGIDO]')
|
|
331
|
+
.replace(/\b(Bearer|Basic)(\s+)[A-Za-z0-9._~+/-]{8,}=*/gi, '$1$2[REDIGIDO]')
|
|
332
|
+
// O valor para em aspas, barra invertida e espaço: aplicada a JSON, a regra não come o fecho.
|
|
333
|
+
.replace(/\b([A-Z0-9_]*(?:KEY|TOKEN|SECRET|PASSWORD)[A-Z0-9_]*\s*=\s*)(?!\d+\b)("[^"\\]*"|'[^'\\]*'|[^\s"'\\]+)/g, '$1[REDIGIDO]')
|
|
334
|
+
.replace(/("[^"]*(?:token|secret|password|passwd|api[_-]?key|apikey|credential|authorization|cookie)[^"]*"\s*:\s*)"(?:[^"\\]|\\.)*"/gi, '$1"[REDIGIDO]"');
|
|
335
|
+
}
|
|
336
|
+
function redigirObjeto(valor) {
|
|
337
|
+
if (typeof valor === 'string')
|
|
338
|
+
return redigir(valor);
|
|
339
|
+
if (Array.isArray(valor))
|
|
340
|
+
return valor.map(v => redigirObjeto(v));
|
|
341
|
+
if (valor && typeof valor === 'object') {
|
|
342
|
+
return Object.fromEntries(Object.entries(valor).map(([k, v]) => [k, typeof v === 'string' && CHAVE_DE_SEGREDO.test(k) ? '[REDIGIDO]' : redigirObjeto(v)]));
|
|
343
|
+
}
|
|
344
|
+
return valor;
|
|
345
|
+
}
|
|
@@ -72,6 +72,7 @@ const path = __importStar(require("node:path"));
|
|
|
72
72
|
const node_crypto_1 = require("node:crypto");
|
|
73
73
|
const estado_thread_1 = require("./estado-thread");
|
|
74
74
|
const hitl_gates_1 = require("./hitl-gates");
|
|
75
|
+
const hitl_texto_livre_1 = require("./hitl-texto-livre");
|
|
75
76
|
exports.CONTRATO_CONSENTIMENTO = 'ork.pulse-consent/v1';
|
|
76
77
|
/**
|
|
77
78
|
* Crockford base32 sem os quatro ambiguos (I, L, O, U). Com letra na frente e ao menos um digito,
|
|
@@ -140,7 +141,8 @@ function interpretarResposta(bruto, codigo) {
|
|
|
140
141
|
return 'sim';
|
|
141
142
|
if (['b', 'n', 'nao', 'não', 'agora nao', 'agora não'].includes(corpo))
|
|
142
143
|
return 'nao';
|
|
143
|
-
|
|
144
|
+
// RM-048 (D2): o mesmo vocabulario fechado do texto livre ("pode seguir", "aprovo", "depois").
|
|
145
|
+
return (0, hitl_texto_livre_1.consentimentoDoTrecho)((0, hitl_texto_livre_1.lerTrecho)(corpo));
|
|
144
146
|
}
|
|
145
147
|
/**
|
|
146
148
|
* A assinatura de um conjunto de candidatos: os mesmos gates, a mesma oferta. O pedido aberto nao
|
|
@@ -234,7 +236,7 @@ function responderConsentimento(raiz, entrada) {
|
|
|
234
236
|
}
|
|
235
237
|
return { resposta: estado.respondido.resposta, repetida: true, pedido, respondido: estado.respondido };
|
|
236
238
|
}
|
|
237
|
-
if (Date.parse(quando) >= Date.parse(pedido.prazo)) {
|
|
239
|
+
if (Date.parse(quando) >= Date.parse(pedido.prazo) && entrada.aceitarVencido !== true) {
|
|
238
240
|
throw new Error('consentimento do pulse: pedido vencido; nenhuma autorização concedida');
|
|
239
241
|
}
|
|
240
242
|
if (!resposta)
|
package/dist/pulse-delivery.js
CHANGED
|
@@ -33,7 +33,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
33
33
|
};
|
|
34
34
|
})();
|
|
35
35
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
-
exports.adquirirLockMonitor = void 0;
|
|
36
|
+
exports.RETEM_PARADO_MS = exports.adquirirLockMonitor = void 0;
|
|
37
37
|
exports.assinaturaPulse = assinaturaPulse;
|
|
38
38
|
exports.assinaturaDoResumo = assinaturaDoResumo;
|
|
39
39
|
exports.mensagemPulse = mensagemPulse;
|
|
@@ -63,6 +63,8 @@ const pulse_cadencia_1 = require("./pulse-cadencia");
|
|
|
63
63
|
// O lock dos monitores mudou de modulo (I-41, GO-FIX 1); quem o importava daqui continua importando.
|
|
64
64
|
var monitor_lock_2 = require("./monitor-lock");
|
|
65
65
|
Object.defineProperty(exports, "adquirirLockMonitor", { enumerable: true, get: function () { return monitor_lock_2.adquirirLockMonitor; } });
|
|
66
|
+
/** RM-037 (fatia 4): por quanto tempo a parada ja informada continua sabida depois de sumir de uma batida. */
|
|
67
|
+
exports.RETEM_PARADO_MS = 6 * 60 * 60 * 1000;
|
|
66
68
|
/**
|
|
67
69
|
* I-41 (D9): a assinatura de deduplicacao NAO inclui `apresentacao`.
|
|
68
70
|
*
|
|
@@ -183,9 +185,24 @@ function varrerPulse(opcoes) {
|
|
|
183
185
|
const esperasFora = outras.flatMap(m => m.esperando.map(e => `maquina:${m.maquina}:${e.thread}:${e.fase}`));
|
|
184
186
|
for (const k of esperasFora)
|
|
185
187
|
presentes.add(k);
|
|
186
|
-
|
|
188
|
+
// RM-037 (fatia 4, D7): o trabalho parado no condutor e noticia uma vez por thread e caso, como a
|
|
189
|
+
// decisao informada: sai na janela da cadencia do dono e nunca abre pedido.
|
|
190
|
+
const parados = pulse.paradoNoCondutor ?? [];
|
|
191
|
+
const chaveDoParado = (p) => `parado:${p.thread}:${p.caso}`;
|
|
192
|
+
for (const p of parados)
|
|
193
|
+
presentes.add(chaveDoParado(p));
|
|
194
|
+
// A parada que some por uma batida (gh que falhou, um `ork verify` do condutor) e volta nao e noticia
|
|
195
|
+
// nova: a marca fica guardada por `RETEM_PARADO_MS` depois da ultima vez que a parada apareceu.
|
|
196
|
+
const guardada = (id, valor) => id.startsWith('parado:') &&
|
|
197
|
+
Date.parse(quando) - Number(valor.split('|')[1]) < exports.RETEM_PARADO_MS;
|
|
198
|
+
vistas = Object.fromEntries(Object.entries(vistas).filter(([id, valor]) => presentes.has(id) || guardada(id, valor)));
|
|
187
199
|
const esperaNovaFora = esperasFora.some(k => vistas[k] === undefined);
|
|
188
200
|
const decisoesNovas = decisoes.filter(d => vistas[chaveDaDecisao(d.id)] === undefined);
|
|
201
|
+
const paradosNovos = parados.filter(p => vistas[chaveDoParado(p)] === undefined);
|
|
202
|
+
// A marca guarda o instante em milissegundos: e dado de maquina, nao texto para o dono.
|
|
203
|
+
for (const p of parados)
|
|
204
|
+
if (vistas[chaveDoParado(p)] !== undefined)
|
|
205
|
+
vistas[chaveDoParado(p)] = `informada|${Date.parse(quando)}`;
|
|
189
206
|
// I-41 (GO-FIX 1): a thread que ja recebeu a pergunta e ainda nao respondeu nao e noticia. A
|
|
190
207
|
// mudanca no item dela (o pedido que o sim do dono abriu) e obra nossa, e avisar sobre ela seria
|
|
191
208
|
// mandar ao dono um resumo sobre a pergunta que ele acabou de receber.
|
|
@@ -205,7 +222,8 @@ function varrerPulse(opcoes) {
|
|
|
205
222
|
!config.argumentos.every(a => typeof a === 'string') || !config.argumentos.some(a => a.includes('{{mensagem}}'))) {
|
|
206
223
|
throw new Error('transporte requer executavel e argumentos com {{mensagem}}');
|
|
207
224
|
}
|
|
208
|
-
|
|
225
|
+
// A mensagem entra como texto: `$&` e `$'` nela nao sao padrao de substituicao (CHECK da RM-037, fatia 4).
|
|
226
|
+
const r = (0, node_child_process_1.spawnSync)(config.executavel, config.argumentos.map(a => a.replaceAll('{{mensagem}}', () => mensagem)), { cwd: raiz, encoding: 'utf8', input: mensagem, timeout: 30000, maxBuffer: 1024 * 1024 });
|
|
209
227
|
entregue = r.status === 0;
|
|
210
228
|
}
|
|
211
229
|
if (!entregue)
|
|
@@ -229,17 +247,23 @@ function varrerPulse(opcoes) {
|
|
|
229
247
|
// I-50: pergunta nova sai na hora; o resto (status, decisoes informadas) espera a janela da
|
|
230
248
|
// cadencia do dono, e a novidade que esperou continua nao vista ate sair.
|
|
231
249
|
const noPrazo = !opcoes.comCadencia || (0, pulse_cadencia_1.janelaAberta)(cadencia, (0, pulse_cadencia_1.lerUltimoResumo)(opcoes.raiz, dir), quando);
|
|
232
|
-
const
|
|
233
|
-
|
|
250
|
+
const informacaoNova = novas > 0 || decisoesNovas.length > 0 || paradosNovos.length > 0;
|
|
251
|
+
const adiada = !conjuntoNovo && !esperaNovaFora && informacaoNova && !noPrazo;
|
|
252
|
+
if (conjuntoNovo || esperaNovaFora || (noPrazo && informacaoNova)) {
|
|
234
253
|
const guardadas = anterior ? fila.candidatos.filter(c => anterior.candidatos.some(a => a.thread === c.thread)).length : 0;
|
|
235
254
|
const resumo = (0, hitl_resumo_1.resumirHitl)(itens, { quando, janelaMin, prontas: fila.candidatos.length, acumuladas: guardadas,
|
|
236
255
|
consertos: fila.consertos, atoDoItem: i => fila.atos.get(`${i.thread}|${i.fase}`),
|
|
237
|
-
decisoes: decisoesNovas, acimaDoLimiar: pulse.acimaDoLimiar ?? [], outrasMaquinas: outras });
|
|
256
|
+
decisoes: decisoesNovas, acimaDoLimiar: pulse.acimaDoLimiar ?? [], outrasMaquinas: outras, paradosNoCondutor: parados });
|
|
238
257
|
// Pedir licenca para mandar zero perguntas era o defeito: sem pergunta, nao ha codigo.
|
|
239
258
|
// I-50: cadencia curta nao encurta o prazo do dono para responder ao resumo (minimo de 60 min).
|
|
240
259
|
const consentimento = fila.candidatos.length ? (0, monitor_lock_1.comLockDaConversa)(dir, () => (0, pulse_consentimento_1.abrirConsentimento)(raiz, { quando,
|
|
241
260
|
resumoSha256: assinaturaDoResumo(resumo), candidatos: fila.candidatos, prazoMin: Math.max(janelaMin, pulse_consentimento_1.PRAZO_PADRAO_MIN), estadoDir: dir })) : undefined;
|
|
242
261
|
entregar((0, hitl_resumo_1.textoDoResumo)(resumo, { canal, codigo: consentimento?.codigo }), 'o resumo');
|
|
262
|
+
// RM-048 (D3): o resumo acabou de sair; a palavra solta ("sim") vale na janela curta dele.
|
|
263
|
+
try {
|
|
264
|
+
(0, pulse_resposta_1.atualizarEscuta)(raiz, quando, dir);
|
|
265
|
+
}
|
|
266
|
+
catch { /* dica de rota; a prova nao depende dela */ }
|
|
243
267
|
// Marcado so depois do recibo do transporte: envio que falhou preserva a novidade.
|
|
244
268
|
for (const item of itens)
|
|
245
269
|
vistas[item.id] = assinaturaPulse(item);
|
|
@@ -247,6 +271,8 @@ function varrerPulse(opcoes) {
|
|
|
247
271
|
vistas[chaveDaDecisao(d.id)] = 'informada';
|
|
248
272
|
for (const k of esperasFora)
|
|
249
273
|
vistas[k] = 'informada';
|
|
274
|
+
for (const p of parados)
|
|
275
|
+
vistas[chaveDoParado(p)] = `informada|${Date.parse(quando)}`;
|
|
250
276
|
(0, pulse_cadencia_1.gravarUltimoResumo)(opcoes.raiz, quando, dir);
|
|
251
277
|
}
|
|
252
278
|
gravarCache(cache, vistas);
|