@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
|
@@ -0,0 +1,181 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* RM-040 (B7): `ork accounts esgotamentos`, a medida da metrica do item.
|
|
4
|
+
*
|
|
5
|
+
* A metrica da RM-040 e "zero despacho numa conta que outro projeto do mesmo usuario ja viu
|
|
6
|
+
* esgotada, dentro do prazo dela". Este relatorio so le: as marcas do registro compartilhado
|
|
7
|
+
* (`~/.orkastery/private/contas.json`) e os `phase_dispatch` com perfil dos ledgers de cada projeto
|
|
8
|
+
* registrado nesta maquina (`~/.orkastery/projetos.json`). Conta o despacho que caiu na conta de
|
|
9
|
+
* uma marca, dentro da janela dela, vindo de projeto diferente do que marcou.
|
|
10
|
+
*
|
|
11
|
+
* A saida nunca leva diretorio de conta: a conta vira um id opaco (sha256 da chave, 12 hex), o
|
|
12
|
+
* projeto sai pelo nome registrado e o perfil, pelo id.
|
|
13
|
+
*
|
|
14
|
+
* Limite declarado: o registro so guarda as marcas vivas. Uso bem sucedido, login reconferido e
|
|
15
|
+
* prazo vencido apagam a marca na gravacao seguinte; por isso o numero e um piso, e a leitura vale
|
|
16
|
+
* enquanto a marca existe.
|
|
17
|
+
*/
|
|
18
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
19
|
+
if (k2 === undefined) k2 = k;
|
|
20
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
21
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
22
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
23
|
+
}
|
|
24
|
+
Object.defineProperty(o, k2, desc);
|
|
25
|
+
}) : (function(o, m, k, k2) {
|
|
26
|
+
if (k2 === undefined) k2 = k;
|
|
27
|
+
o[k2] = m[k];
|
|
28
|
+
}));
|
|
29
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
30
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
31
|
+
}) : function(o, v) {
|
|
32
|
+
o["default"] = v;
|
|
33
|
+
});
|
|
34
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
35
|
+
var ownKeys = function(o) {
|
|
36
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
37
|
+
var ar = [];
|
|
38
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
39
|
+
return ar;
|
|
40
|
+
};
|
|
41
|
+
return ownKeys(o);
|
|
42
|
+
};
|
|
43
|
+
return function (mod) {
|
|
44
|
+
if (mod && mod.__esModule) return mod;
|
|
45
|
+
var result = {};
|
|
46
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
47
|
+
__setModuleDefault(result, mod);
|
|
48
|
+
return result;
|
|
49
|
+
};
|
|
50
|
+
})();
|
|
51
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
|
+
exports.NAO_REGISTRADO = exports.LIMITE_DO_RELATORIO = exports.CONTRATO_ESGOTAMENTOS = void 0;
|
|
53
|
+
exports.contaOpaca = contaOpaca;
|
|
54
|
+
exports.fimDaJanela = fimDaJanela;
|
|
55
|
+
exports.relatorioDeEsgotamentos = relatorioDeEsgotamentos;
|
|
56
|
+
exports.textoDosEsgotamentos = textoDosEsgotamentos;
|
|
57
|
+
const node_crypto_1 = require("node:crypto");
|
|
58
|
+
const fs = __importStar(require("node:fs"));
|
|
59
|
+
const horario_1 = require("./horario");
|
|
60
|
+
const ledger_1 = require("./ledger");
|
|
61
|
+
const ledger_stats_1 = require("./ledger-stats");
|
|
62
|
+
const projeto_alvo_1 = require("./projeto-alvo");
|
|
63
|
+
const runtime_profiles_1 = require("./runtime-profiles");
|
|
64
|
+
const thread_1 = require("./thread");
|
|
65
|
+
const util_1 = require("./util");
|
|
66
|
+
exports.CONTRATO_ESGOTAMENTOS = 'ork.esgotamentos/v1';
|
|
67
|
+
exports.LIMITE_DO_RELATORIO = 'o registro guarda so as marcas vivas (uso bem sucedido, login reconferido e prazo vencido ' +
|
|
68
|
+
'apagam a marca): o total e um piso, valido enquanto a marca existe';
|
|
69
|
+
exports.NAO_REGISTRADO = 'nao registrado';
|
|
70
|
+
function contaOpaca(chave) {
|
|
71
|
+
return (0, node_crypto_1.createHash)('sha256').update(chave).digest('hex').slice(0, 12);
|
|
72
|
+
}
|
|
73
|
+
function caminhoReal(p) {
|
|
74
|
+
try {
|
|
75
|
+
return fs.realpathSync(p);
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
return p;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
/** Fim da janela da marca: o prazo para cota esgotada; sem prazo (login, credencial paga), aberta. */
|
|
82
|
+
function fimDaJanela(m) {
|
|
83
|
+
if (m.estado !== 'esgotado')
|
|
84
|
+
return Number.POSITIVE_INFINITY;
|
|
85
|
+
const ate = m.esgotadoAte === null ? NaN : Date.parse(m.esgotadoAte);
|
|
86
|
+
return Number.isFinite(ate) ? ate : Number.NEGATIVE_INFINITY;
|
|
87
|
+
}
|
|
88
|
+
function relatorioDeEsgotamentos(opcoes = {}) {
|
|
89
|
+
const agoraMs = opcoes.agoraMs ?? Date.now();
|
|
90
|
+
const desdeMs = (0, ledger_stats_1.parsePeriodo)(opcoes.desde ?? '7d', agoraMs);
|
|
91
|
+
if (desdeMs >= agoraMs)
|
|
92
|
+
throw Error('ledger.period.order');
|
|
93
|
+
// Um projeto por raiz real: o registro pode trazer a mesma copia por dois caminhos.
|
|
94
|
+
const vistos = new Set();
|
|
95
|
+
const projetos = (0, projeto_alvo_1.listarProjetos)().filter((p) => {
|
|
96
|
+
const real = caminhoReal(p.raiz);
|
|
97
|
+
if (vistos.has(real))
|
|
98
|
+
return false;
|
|
99
|
+
vistos.add(real);
|
|
100
|
+
return true;
|
|
101
|
+
}).map((p) => ({ ...p, real: caminhoReal(p.raiz) }));
|
|
102
|
+
const nomeDaRaiz = (raiz) => projetos.find((p) => p.real === caminhoReal(raiz))?.nome ?? exports.NAO_REGISTRADO;
|
|
103
|
+
const contas = (0, runtime_profiles_1.lerContasCompartilhadas)();
|
|
104
|
+
const marcas = contas.map((c) => ({ c, real: caminhoReal(c.projeto), inicio: Date.parse(c.em), fim: fimDaJanela(c) }));
|
|
105
|
+
const despachos = [];
|
|
106
|
+
const lidos = [];
|
|
107
|
+
for (const p of projetos) {
|
|
108
|
+
let comPerfil = 0;
|
|
109
|
+
let ids = [];
|
|
110
|
+
try {
|
|
111
|
+
ids = p.presente ? (0, thread_1.listarIds)(p.raiz) : [];
|
|
112
|
+
}
|
|
113
|
+
catch {
|
|
114
|
+
ids = [];
|
|
115
|
+
}
|
|
116
|
+
for (const id of ids) {
|
|
117
|
+
let eventos = [];
|
|
118
|
+
try {
|
|
119
|
+
eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(p.raiz, id));
|
|
120
|
+
}
|
|
121
|
+
catch {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
for (const e of eventos) {
|
|
125
|
+
if (e.tipo !== 'phase_dispatch' || e.perfil === undefined || e.perfil === null)
|
|
126
|
+
continue;
|
|
127
|
+
const t = Date.parse(String(e.ts));
|
|
128
|
+
if (!Number.isFinite(t) || t < desdeMs || t >= agoraMs)
|
|
129
|
+
continue;
|
|
130
|
+
let perfil, chave;
|
|
131
|
+
try {
|
|
132
|
+
perfil = (0, runtime_profiles_1.validarPerfilDeDespacho)(e.perfil);
|
|
133
|
+
chave = (0, runtime_profiles_1.chaveDaConta)(perfil);
|
|
134
|
+
}
|
|
135
|
+
catch {
|
|
136
|
+
continue;
|
|
137
|
+
}
|
|
138
|
+
comPerfil++;
|
|
139
|
+
const marca = marcas.find((m) => m.c.chave === chave && m.real !== p.real && t >= m.inicio && t < m.fim);
|
|
140
|
+
if (!marca)
|
|
141
|
+
continue;
|
|
142
|
+
despachos.push({ em: new Date(t).toISOString(), projeto: p.nome, thread: String(e.thread ?? id),
|
|
143
|
+
fase: typeof e.fase === 'string' ? e.fase : null, runtime: perfil.runtime, perfil: perfil.id,
|
|
144
|
+
conta: contaOpaca(chave), marcadaPor: nomeDaRaiz(marca.c.projeto) });
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
lidos.push({ nome: p.nome, presente: p.presente, despachosComPerfil: comPerfil });
|
|
148
|
+
}
|
|
149
|
+
despachos.sort((a, b) => a.em.localeCompare(b.em));
|
|
150
|
+
return {
|
|
151
|
+
contrato: exports.CONTRATO_ESGOTAMENTOS,
|
|
152
|
+
periodo: { desde: new Date(desdeMs).toISOString(), ate: new Date(agoraMs).toISOString(), intervalo: '[desde,ate)' },
|
|
153
|
+
marcas: contas.map((c) => ({ conta: contaOpaca(c.chave), runtime: c.chave.slice(0, c.chave.indexOf(':')), estado: c.estado,
|
|
154
|
+
motivo: c.motivo, em: c.em, esgotadoAte: c.esgotadoAte, projeto: nomeDaRaiz(c.projeto) })),
|
|
155
|
+
projetos: lidos,
|
|
156
|
+
despachos,
|
|
157
|
+
total: despachos.length,
|
|
158
|
+
limite: exports.LIMITE_DO_RELATORIO,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/** Texto para o terminal, com o horario do dono e o fuso uma vez no fim. */
|
|
162
|
+
function textoDosEsgotamentos(r) {
|
|
163
|
+
const linhas = [
|
|
164
|
+
`Esgotamentos de conta ${(0, horario_1.formatarDataHora)(r.periodo.desde)} ate ${(0, horario_1.formatarDataHora)(r.periodo.ate)} ${r.periodo.intervalo}`,
|
|
165
|
+
` marcas vivas no registro: ${r.marcas.length}`,
|
|
166
|
+
];
|
|
167
|
+
for (const m of r.marcas) {
|
|
168
|
+
const ate = m.estado === 'esgotado' ? `ate ${(0, horario_1.formatarDataHora)(m.esgotadoAte)}` : 'sem prazo';
|
|
169
|
+
linhas.push(` conta ${m.conta} ${m.runtime} ${m.estado} desde ${(0, horario_1.formatarDataHora)(m.em)}, ${ate} visto em ${m.projeto} (${m.motivo})`);
|
|
170
|
+
}
|
|
171
|
+
linhas.push(` projetos lidos: ${r.projetos.length === 0 ? 'nenhum registrado'
|
|
172
|
+
: r.projetos.map((p) => `${p.nome} (${p.presente ? `${p.despachosComPerfil} despacho(s) com perfil` : 'fora do disco'})`).join(', ')}`);
|
|
173
|
+
linhas.push(` despachos em conta marcada por outro projeto: ${r.total}`);
|
|
174
|
+
if (r.despachos.length > 0) {
|
|
175
|
+
linhas.push((0, util_1.tabela)(['QUANDO', 'PROJETO', 'THREAD', 'FASE', 'PERFIL', 'CONTA', 'MARCADA POR'], r.despachos.map((d) => [(0, horario_1.formatarDataHora)(d.em), d.projeto, d.thread, d.fase ?? '-', d.perfil, d.conta, d.marcadaPor]))
|
|
176
|
+
.split('\n').map((l) => ` ${l}`).join('\n'));
|
|
177
|
+
}
|
|
178
|
+
linhas.push(` limite: ${r.limite}`);
|
|
179
|
+
linhas.push((0, horario_1.legendaDoFuso)());
|
|
180
|
+
return linhas.join('\n');
|
|
181
|
+
}
|
package/dist/experiencia.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.IDIOMA_PADRAO = void 0;
|
|
3
4
|
exports.normalizarIdioma = normalizarIdioma;
|
|
4
5
|
exports.validarPreferencias = validarPreferencias;
|
|
5
6
|
exports.idiomaDoSistema = idiomaDoSistema;
|
|
@@ -44,6 +45,11 @@ function validarPreferencias(valor) {
|
|
|
44
45
|
}
|
|
45
46
|
return resultado;
|
|
46
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Ensaio de 03/10 (R2): sem idioma escolhido, vale a língua da CLI. Antes era en-US, e uma máquina com
|
|
50
|
+
* LANG=C.UTF-8 (contêiner, CI, servidor) recebia a experiência em inglês de uma CLI que fala português.
|
|
51
|
+
*/
|
|
52
|
+
exports.IDIOMA_PADRAO = 'pt-BR';
|
|
47
53
|
/** C, POSIX e und (raiz do ICU) dizem que o sistema não escolheu idioma: vale o padrão. */
|
|
48
54
|
const SEM_IDIOMA = new Set(['c', 'posix', 'und']);
|
|
49
55
|
function idiomaEscolhido(valor) {
|
|
@@ -52,11 +58,15 @@ function idiomaEscolhido(valor) {
|
|
|
52
58
|
}
|
|
53
59
|
function idiomaDoSistema(env = process.env, doIntl = () => Intl.DateTimeFormat().resolvedOptions().locale) {
|
|
54
60
|
const locale = env.LC_ALL || env.LC_MESSAGES || env.LANG;
|
|
55
|
-
|
|
61
|
+
if (locale) {
|
|
62
|
+
// Com C ou POSIX explícito, o Intl do Node cai no en-US do ICU: ele não diz a escolha de ninguém.
|
|
63
|
+
return idiomaEscolhido(locale.split(/[.@]/)[0].replace(/_/g, '-')) ?? exports.IDIOMA_PADRAO;
|
|
64
|
+
}
|
|
65
|
+
return idiomaEscolhido(doIntl()) ?? exports.IDIOMA_PADRAO;
|
|
56
66
|
}
|
|
57
67
|
function resolverExperiencia(owner = {}, sistema = { language: idiomaDoSistema(), timezone: (0, horario_1.fusoDoSistema)() }) {
|
|
58
68
|
const p = validarPreferencias(owner);
|
|
59
|
-
const language = p.language ?? normalizarIdioma(sistema.language) ??
|
|
69
|
+
const language = p.language ?? normalizarIdioma(sistema.language) ?? exports.IDIOMA_PADRAO;
|
|
60
70
|
return {
|
|
61
71
|
language, timezone: p.timezone ?? (0, horario_1.normalizarFuso)(sistema.timezone) ?? 'UTC',
|
|
62
72
|
depth: p.depth ?? 'curta', experience: p.experience ?? true,
|
package/dist/fabrica-estado.js
CHANGED
|
@@ -81,6 +81,7 @@ const roadmap_reservas_1 = require("./roadmap-reservas");
|
|
|
81
81
|
const thread_1 = require("./thread");
|
|
82
82
|
const util_1 = require("./util");
|
|
83
83
|
const versao_1 = require("./versao");
|
|
84
|
+
const saida_segura_1 = require("./saida-segura");
|
|
84
85
|
exports.CONTRATO_MAQUINA = 'ork.fabrica-maquina/v1';
|
|
85
86
|
exports.BRANCH_DA_FABRICA = 'ork/fabrica-estado';
|
|
86
87
|
/** O diretorio dos retratos na branch; a leitura sem clone (RM-054) le o mesmo. */
|
|
@@ -234,7 +235,8 @@ function gravarMarca(raiz, marca) {
|
|
|
234
235
|
*/
|
|
235
236
|
function publicarMaquina(carregado, opcoes = {}) {
|
|
236
237
|
const raiz = carregado.raiz;
|
|
237
|
-
|
|
238
|
+
// RM-047: antes do retrato e da marca, para o remoto invalido recusar mesmo sem mudanca a enviar.
|
|
239
|
+
const remoto = (0, branch_de_estado_1.exigirRemoto)(opcoes.remoto ?? carregado.manifesto.fabrica.remoto, PREFIXO);
|
|
238
240
|
const estado = retratoDaMaquina(carregado, { ...opcoes, remoto });
|
|
239
241
|
const assinatura = assinaturaDoRetrato(estado);
|
|
240
242
|
const marca = lerMarca(raiz);
|
|
@@ -274,7 +276,7 @@ function publicarMaquina(carregado, opcoes = {}) {
|
|
|
274
276
|
/** `ork fabrica sair`: tira o retrato desta maquina da branch. `null` quando nao havia retrato. */
|
|
275
277
|
function removerMaquina(carregado, opcoes = {}) {
|
|
276
278
|
const raiz = carregado.raiz;
|
|
277
|
-
const remoto = opcoes.remoto ?? carregado.manifesto.fabrica.remoto;
|
|
279
|
+
const remoto = (0, branch_de_estado_1.exigirRemoto)(opcoes.remoto ?? carregado.manifesto.fabrica.remoto, PREFIXO);
|
|
278
280
|
const maquina = (0, roadmap_reservas_1.quemSouEu)(raiz, { maquina: opcoes.maquina }).maquina;
|
|
279
281
|
for (let tentativa = 1; tentativa <= TENTATIVAS; tentativa++) {
|
|
280
282
|
const { ponta, atualizado } = (0, branch_de_estado_1.buscarBranch)(raiz, remoto, exports.BRANCH_DA_FABRICA, PREFIXO, 30000);
|
|
@@ -331,8 +333,13 @@ function blocoDaMaquina(m, eu) {
|
|
|
331
333
|
t.id, t.modo, t.fase, runtimeDaThread(t), t.roadmap ?? '-', t.esperaVoce ? `sim: ${curto(t.pergunta ?? 'veredito', 40)}` : '-',
|
|
332
334
|
]))];
|
|
333
335
|
}
|
|
334
|
-
/**
|
|
335
|
-
|
|
336
|
+
/**
|
|
337
|
+
* `ork fabrica`: todas as maquinas. RM-053 (fatia 2): o painel vem do remoto, onde outras pessoas
|
|
338
|
+
* escrevem; cada valor dele (o `por`, o `projeto`, a pergunta) chega a tela numa linha so, sem nada
|
|
339
|
+
* que o terminal execute.
|
|
340
|
+
*/
|
|
341
|
+
function textoDaFabrica(painel, eu) {
|
|
342
|
+
const p = (0, saida_segura_1.valoresEmUmaLinha)(painel);
|
|
336
343
|
const linhas = [];
|
|
337
344
|
if (!p.atualizado)
|
|
338
345
|
linhas.push('AVISO: sem leitura nova do remoto; esta e a ultima copia lida nesta maquina.', '');
|
|
@@ -345,8 +352,9 @@ function textoDaFabrica(p, eu) {
|
|
|
345
352
|
linhas.push((0, horario_1.legendaDoFuso)());
|
|
346
353
|
return linhas.join('\n');
|
|
347
354
|
}
|
|
348
|
-
/** A secao de `ork board` com as outras maquinas
|
|
349
|
-
function textoDasOutrasMaquinas(
|
|
355
|
+
/** A secao de `ork board` com as outras maquinas, saneada como o `ork fabrica`. */
|
|
356
|
+
function textoDasOutrasMaquinas(painel, eu) {
|
|
357
|
+
const p = (0, saida_segura_1.valoresEmUmaLinha)(painel);
|
|
350
358
|
const outras = p.maquinas.filter((m) => m.maquina !== eu);
|
|
351
359
|
const origem = p.atualizado ? 'lido agora' : 'ultima copia local';
|
|
352
360
|
if (outras.length === 0)
|
package/dist/fabrica-publicar.js
CHANGED
|
@@ -39,18 +39,24 @@ exports.publicarEmSegundoPlano = publicarEmSegundoPlano;
|
|
|
39
39
|
*
|
|
40
40
|
* Modulo a parte, com o minimo de dependencias, porque quem chama (despacho de fase, entrega,
|
|
41
41
|
* thread nova) nao pode esperar rede nem arrastar o retrato inteiro para dentro de si.
|
|
42
|
+
*
|
|
43
|
+
* RM-053 (D9): o mesmo evento tambem dispara o retrato desta maquina na rede da pessoa, pela
|
|
44
|
+
* `rede-adesao` (tao leve quanto este modulo): so membro, no maximo uma vez a cada 14 minutos.
|
|
42
45
|
*/
|
|
43
46
|
const node_child_process_1 = require("node:child_process");
|
|
44
47
|
const fs = __importStar(require("node:fs"));
|
|
45
48
|
const path = __importStar(require("node:path"));
|
|
46
49
|
const maquina_1 = require("./maquina");
|
|
47
50
|
const manifest_1 = require("./manifest");
|
|
51
|
+
const rede_adesao_1 = require("./rede-adesao");
|
|
48
52
|
/**
|
|
49
53
|
* Publica sem segurar quem chamou (thread nova, despacho de fase, entrega). So com a fabrica
|
|
50
54
|
* compartilhada nesta maquina (`ork fabrica entrar`) ou no projeto; `ORK_FABRICA_PUBLICAR=0`
|
|
51
55
|
* desliga (testes e `ork eval`).
|
|
52
56
|
*/
|
|
53
57
|
function publicarEmSegundoPlano(raiz) {
|
|
58
|
+
// A rede decide por si (adesao, teto e ambiente); o retorno continua sendo o da fabrica.
|
|
59
|
+
(0, rede_adesao_1.publicarRedeEmSegundoPlano)({ diretorio: raiz });
|
|
54
60
|
if (process.env.ORK_FABRICA_PUBLICAR === '0')
|
|
55
61
|
return false;
|
|
56
62
|
let carregado;
|
package/dist/fechamento.js
CHANGED
|
@@ -1,37 +1,4 @@
|
|
|
1
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
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
3
|
exports.liberarAoFechar = liberarAoFechar;
|
|
37
4
|
/**
|
|
@@ -43,61 +10,48 @@ exports.liberarAoFechar = liberarAoFechar;
|
|
|
43
10
|
* derruba. Cada parte roda isolada (achado A1 do CHECK 1): erro de E/S numa nao impede a outra, e o
|
|
44
11
|
* que nao saiu agora sai na poda do proximo pedido de regiao ou no `ork roadmap reservas --soltar-orfas`.
|
|
45
12
|
*/
|
|
46
|
-
const fs = __importStar(require("node:fs"));
|
|
47
|
-
const path = __importStar(require("node:path"));
|
|
48
|
-
const estado_thread_1 = require("./estado-thread");
|
|
49
13
|
const ledger_1 = require("./ledger");
|
|
50
14
|
const leases_1 = require("./leases");
|
|
51
15
|
const roadmap_reservas_1 = require("./roadmap-reservas");
|
|
16
|
+
const sessoes_1 = require("./sessoes");
|
|
52
17
|
const thread_1 = require("./thread");
|
|
53
18
|
function liberarAoFechar(raiz, threadId) {
|
|
54
|
-
const saida = { leases: [], fila: [], reservas: [], falhas: [] };
|
|
19
|
+
const saida = { leases: [], fila: [], reservas: [], sessoes: [], falhas: [] };
|
|
55
20
|
const falhou = (parte, e) => saida.falhas.push(`${parte}: ${e?.message ?? String(e)}`);
|
|
56
|
-
//
|
|
57
|
-
//
|
|
58
|
-
const real = (p) => { try {
|
|
59
|
-
return fs.realpathSync(p);
|
|
60
|
-
}
|
|
61
|
-
catch {
|
|
62
|
-
return path.resolve(p);
|
|
63
|
-
} };
|
|
64
|
-
const candidatos = [raiz];
|
|
21
|
+
// RM-036 (D6): os leases de regiao moram todos no estado canonico, chamado de qualquer checkout, e a
|
|
22
|
+
// soltura de la ja alcanca o legado que a versao anterior gravou nas worktrees. Uma soltura basta.
|
|
65
23
|
try {
|
|
66
|
-
|
|
24
|
+
const solto = (0, leases_1.soltarThreadFechada)(raiz, threadId, 'fechamento');
|
|
25
|
+
saida.leases.push(...solto.leases);
|
|
26
|
+
saida.fila.push(...solto.fila);
|
|
67
27
|
}
|
|
68
28
|
catch (e) {
|
|
69
|
-
falhou(
|
|
29
|
+
falhou(`leases em ${raiz}`, e);
|
|
70
30
|
}
|
|
71
31
|
try {
|
|
72
|
-
|
|
73
|
-
if (wt && fs.existsSync(wt))
|
|
74
|
-
candidatos.push(wt);
|
|
32
|
+
saida.reservas = (0, roadmap_reservas_1.soltarReservaDaThread)(raiz, threadId);
|
|
75
33
|
}
|
|
76
34
|
catch (e) {
|
|
77
|
-
falhou('
|
|
78
|
-
}
|
|
79
|
-
const checkouts = candidatos.filter((c, i) => candidatos.findIndex((x) => real(x) === real(c)) === i);
|
|
80
|
-
for (const checkout of checkouts) {
|
|
81
|
-
try {
|
|
82
|
-
const solto = (0, leases_1.soltarThreadFechada)(checkout, threadId, 'fechamento');
|
|
83
|
-
saida.leases.push(...solto.leases);
|
|
84
|
-
saida.fila.push(...solto.fila);
|
|
85
|
-
}
|
|
86
|
-
catch (e) {
|
|
87
|
-
falhou(`leases em ${checkout}`, e);
|
|
88
|
-
}
|
|
35
|
+
falhou('reserva', e);
|
|
89
36
|
}
|
|
37
|
+
// RM-056 (ao fechar): a sessao fantasma presa a thread fechada seguia no inventario e no doctor ate
|
|
38
|
+
// alguem rodar `ork sessions limpar-fantasmas` (624f65db e d38ae1d4, 03/10). So consulta o runtime
|
|
39
|
+
// quando o ledger tem sessao sem fim; nunca para nem remove nada no runtime, e sessao viva fica.
|
|
90
40
|
try {
|
|
91
|
-
|
|
41
|
+
if ((0, sessoes_1.sessoesSemFim)(raiz, threadId).length > 0) {
|
|
42
|
+
const r = (0, sessoes_1.limparFantasmas)(raiz, { thread: threadId, origem: 'fechamento' });
|
|
43
|
+
saida.sessoes = r.itens.filter((i) => i.acao === 'solto');
|
|
44
|
+
}
|
|
92
45
|
}
|
|
93
46
|
catch (e) {
|
|
94
|
-
falhou('
|
|
47
|
+
falhou('sessoes', e);
|
|
95
48
|
}
|
|
96
49
|
// Sugestao 2 do CHECK 2: a falha tambem vai ao ledger da thread, de melhor esforco.
|
|
97
50
|
if (saida.falhas.length > 0) {
|
|
98
51
|
try {
|
|
99
52
|
(0, ledger_1.registrarSeExiste)((0, thread_1.dirThread)(raiz, threadId), threadId, ledger_1.TIPOS_DE_EVENTO.solturaFalhou, { falhas: saida.falhas,
|
|
100
|
-
correcao: 'a poda do proximo pedido de regiao solta o que ficou; reserva: ork roadmap reservas --soltar-orfas'
|
|
53
|
+
correcao: 'a poda do proximo pedido de regiao solta o que ficou; reserva: ork roadmap reservas --soltar-orfas; ' +
|
|
54
|
+
'sessao fantasma: ork sessions limpar-fantasmas' });
|
|
101
55
|
}
|
|
102
56
|
catch { /* o ledger que nao grava ja esta nas falhas */ }
|
|
103
57
|
}
|
package/dist/forja.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
exports.mesmaForja = exports.rotuloDaForja = exports.executorPadrao = void 0;
|
|
4
|
+
exports.enderecoDoRemoto = enderecoDoRemoto;
|
|
5
|
+
exports.repositorioGithubNoHost = repositorioGithubNoHost;
|
|
4
6
|
exports.identidadeDaForja = identidadeDaForja;
|
|
5
7
|
exports.forjaDoArgumento = forjaDoArgumento;
|
|
6
8
|
exports.detalheSeguro = detalheSeguro;
|
|
@@ -27,14 +29,20 @@ exports.lerDaForja = lerDaForja;
|
|
|
27
29
|
const node_child_process_1 = require("node:child_process");
|
|
28
30
|
const hitl_1 = require("./hitl");
|
|
29
31
|
const policies_1 = require("./policies");
|
|
32
|
+
const rede_forja_1 = require("./rede-forja");
|
|
30
33
|
const PRAZO_MS = 30000;
|
|
31
34
|
const SHA = /^[0-9a-f]{40}$/;
|
|
32
35
|
const SEGMENTO = /^[A-Za-z0-9_.-]+$/;
|
|
33
36
|
/** Hostname de verdade: rotulos alfanumericos separados por ponto (`..` ou `a..b` nao passam). */
|
|
34
37
|
const HOST = /^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?(?:\.[a-z0-9](?:[a-z0-9-]*[a-z0-9])?)+$/;
|
|
35
|
-
/**
|
|
38
|
+
/**
|
|
39
|
+
* Sem shell, sem token: a CLI usa a autenticacao que ja tem; prompt e aviso de versao desligados.
|
|
40
|
+
* RM-054 (fatia 3): o binario e procurado como na rede (D11 da RM-053), no PATH e depois nas pastas de
|
|
41
|
+
* usuario (`~/.local/bin` e as outras): o gateway e o cron rodam com PATH curto, e o `gh` da srvjcp86
|
|
42
|
+
* mora em `~/.local/bin`. Sem achar, o nome vai cru ao spawn, e o ENOENT continua `forja.ausente`.
|
|
43
|
+
*/
|
|
36
44
|
const executorPadrao = (cmd, args, entrada, timeoutMs) => {
|
|
37
|
-
const r = (0, node_child_process_1.spawnSync)(cmd, [...args], {
|
|
45
|
+
const r = (0, node_child_process_1.spawnSync)((0, rede_forja_1.acharBinario)(cmd) ?? cmd, [...args], {
|
|
38
46
|
input: entrada, encoding: 'utf8', timeout: timeoutMs, maxBuffer: 32 * 1024 * 1024,
|
|
39
47
|
env: { ...process.env, GH_PROMPT_DISABLED: '1', GH_NO_UPDATE_NOTIFIER: '1', GLAB_CHECK_UPDATE: 'false', NO_COLOR: '1' },
|
|
40
48
|
});
|
|
@@ -63,10 +71,10 @@ function montar(tipo, host, caminho) {
|
|
|
63
71
|
return { tipo, host, repo: partes.join('/') };
|
|
64
72
|
}
|
|
65
73
|
/**
|
|
66
|
-
*
|
|
67
|
-
* caminho local e
|
|
74
|
+
* RM-037 (fatia 5, A5): o host e o caminho de uma URL de remoto (https, ssh ou scp), de qualquer forja. Credencial
|
|
75
|
+
* na URL nunca entra; caminho local e host que nao e hostname voltam null.
|
|
68
76
|
*/
|
|
69
|
-
function
|
|
77
|
+
function enderecoDoRemoto(url) {
|
|
70
78
|
const t = url.trim();
|
|
71
79
|
const esquema = /^(?:https?|ssh|git|git\+ssh|ssh\+git):\/\/(?:[^@/]*@)?([^/:?#@]+)(?::\d+)?\/([^?#\s]+)$/i.exec(t);
|
|
72
80
|
const scp = esquema ? null : /^(?:[^@\s/:]+@)?([^:\s/@]+):(?!\/\/)([^\s]+)$/.exec(t);
|
|
@@ -74,8 +82,24 @@ function identidadeDaForja(url) {
|
|
|
74
82
|
if (!achado)
|
|
75
83
|
return null;
|
|
76
84
|
const host = achado[1].toLowerCase();
|
|
77
|
-
|
|
78
|
-
|
|
85
|
+
return HOST.test(host) ? { host, caminho: achado[2].replace(/^\/+/, '') } : null;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* RM-037 (fatia 5, A5): o `dono/nome` de um repositorio do GitHub no host do remoto, com qualquer host: o GitHub
|
|
89
|
+
* Enterprise mora em host proprio, com ou sem "github" no nome. Quem diz se o host e mesmo um GitHub e o `gh`.
|
|
90
|
+
*/
|
|
91
|
+
function repositorioGithubNoHost(url) {
|
|
92
|
+
const endereco = enderecoDoRemoto(url);
|
|
93
|
+
return endereco && tipoDoHost(endereco.host) !== 'gitlab' ? montar('github', endereco.host, endereco.caminho) : null;
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* A forja de uma URL de remoto (https, ssh ou scp). Credencial na URL nunca entra na identidade;
|
|
97
|
+
* caminho local e forja desconhecida voltam null.
|
|
98
|
+
*/
|
|
99
|
+
function identidadeDaForja(url) {
|
|
100
|
+
const endereco = enderecoDoRemoto(url);
|
|
101
|
+
const tipo = endereco ? tipoDoHost(endereco.host) : null;
|
|
102
|
+
return endereco && tipo ? montar(tipo, endereco.host, endereco.caminho) : null;
|
|
79
103
|
}
|
|
80
104
|
/** `github:dono/repo`, `gitlab:grupo/sub/repo` (com host proprio: `gitlab:host.tld/grupo/repo`) ou uma URL. */
|
|
81
105
|
function forjaDoArgumento(texto) {
|
package/dist/gates.js
CHANGED
|
@@ -46,11 +46,15 @@ exports.DESCRICAO_DO_MOTIVO = {
|
|
|
46
46
|
'runtime.quota-exhausted': 'a cota ou os creditos da conta do runtime acabaram (I-33: o perfil sai do rodizio e a fase segue em outro perfil, outro runtime ou na fila)',
|
|
47
47
|
'runtime.auth-missing': 'a conta do runtime nao esta autenticada (I-33: o perfil nunca recebe despacho ate o login ser refeito pelo proprio CLI)',
|
|
48
48
|
'runtime.model-unavailable': 'o modelo pedido nao existe ou a conta nao tem acesso a ele (RM-037: o perfil segue no rodizio e a fase vai a outro perfil com o mesmo modelo ou ao fallback do bloco)',
|
|
49
|
+
'runtime.profile-invalid': 'o perfil pedido no despacho (--perfil) nao existe no store ou e de outro runtime (RM-056: o despacho nunca troca de perfil sozinho)',
|
|
50
|
+
'runtime.workspace-untrusted': 'o runtime nao confia no diretorio da worktree e recusou o despacho (RM-055: so o dono aceita a confianca no terminal; depois o ork re-despacha o mesmo prompt)',
|
|
51
|
+
'runtime.consent-pending': 'o runtime espera o dono aceitar termos novos e recusou o despacho (RM-055: so o dono aceita no terminal; depois o ork re-despacha o mesmo prompt)',
|
|
49
52
|
'cost.violation': 'o despacho seria redirecionado para provider pago (violacao de custo: e o unico motivo que NUNCA recebe retry automatico)',
|
|
50
53
|
'tree.blocked': 'a arvore de destino nao esta disponivel para o merge (branch em check-out com alteracao nao commitada, ou worktree que nao pode ser montada)',
|
|
51
54
|
'lease.busy': 'lease ocupado por outra thread',
|
|
55
|
+
'lease.resume-unavailable': 'retomada indisponivel; libere o lease explicitamente com --forcar',
|
|
52
56
|
'conducao.em-andamento': 'outra conducao ja executa na worktree da thread (I-36): o pedido foi recusado com quem conduz e as tres acoes (esperar, acompanhar, assumir)',
|
|
53
|
-
'hitl.formato': 'o pedido HITL saiu fora do formato obrigatorio (pergunta em uma frase, de
|
|
57
|
+
'hitl.formato': 'o pedido HITL saiu fora do formato obrigatorio (pergunta em uma frase, de 3 a 5 alternativas rotuladas com consequencia, exatamente uma com o selo Recomendacao e o porque, corpo em lista; texto livre so com dependencia tecnica tipada)',
|
|
54
58
|
'human.pending': 'gate humano do modo de conducao ainda nao autorizou',
|
|
55
59
|
};
|
|
56
60
|
/**
|
package/dist/hitl-contract.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.CONTRATOS_DE_PEDIDO = exports.CAMPOS_DA_DECISAO_NO_MCP = exports.CAMPOS_DA_DECISAO_NO_CLI = exports.CAMPOS_DE_GATE = exports.TETOS_HITL_V2 = exports.LETRAS_DE_ALTERNATIVA = exports.CONTRATO_HITL_V2 = exports.ATO_IRREVERSIVEL_DO_MOTIVO = exports.ATOS_IRREVERSIVEIS = exports.CONTRATO_HITL = void 0;
|
|
3
|
+
exports.CONTRATOS_DE_PEDIDO = exports.SelecaoRecusada = exports.MAXIMO_DE_ALTERNATIVAS_DE_CONDUCAO = exports.MINIMO_DE_ALTERNATIVAS_DE_CONDUCAO = exports.CAMPOS_DA_DECISAO_NO_MCP = exports.CAMPOS_DA_DECISAO_NO_CLI = exports.CAMPOS_DE_GATE = exports.TETOS_HITL_V2 = exports.LETRAS_DE_ALTERNATIVA = exports.CONTRATO_HITL_V2 = exports.ATO_IRREVERSIVEL_DO_MOTIVO = exports.ATOS_IRREVERSIVEIS = exports.CONTRATO_HITL = void 0;
|
|
4
4
|
exports.atoDaPausa = atoDaPausa;
|
|
5
5
|
exports.profundidadeDoModo = profundidadeDoModo;
|
|
6
6
|
exports.profundidadeDoPedido = profundidadeDoPedido;
|
|
@@ -10,6 +10,7 @@ exports.respostaAceitaDoPedido = respostaAceitaDoPedido;
|
|
|
10
10
|
exports.validarRespostaHitl = validarRespostaHitl;
|
|
11
11
|
exports.vereditoDoGate = vereditoDoGate;
|
|
12
12
|
exports.recusaNaSuperficie = recusaNaSuperficie;
|
|
13
|
+
exports.exigirSelecaoDeConducao = exigirSelecaoDeConducao;
|
|
13
14
|
exports.validarPedidoHitlV2 = validarPedidoHitlV2;
|
|
14
15
|
exports.validarPedidoHitl = validarPedidoHitl;
|
|
15
16
|
exports.ehV2 = ehV2;
|
|
@@ -201,7 +202,8 @@ function vereditoDoGate(pedido, opcao) {
|
|
|
201
202
|
// `pergunta` e o unico que pode segurar, e o unico que o dono precisa responder.
|
|
202
203
|
// ---------------------------------------------------------------------------
|
|
203
204
|
exports.CONTRATO_HITL_V2 = 'ork.hitl/v2';
|
|
204
|
-
|
|
205
|
+
/** RM-057: ate cinco alternativas, de a a e. A leitura aceita as de duas do historico. */
|
|
206
|
+
exports.LETRAS_DE_ALTERNATIVA = ['a', 'b', 'c', 'd', 'e'];
|
|
205
207
|
/** D13: tetos com origem na maior medida ja gravada nos catorze pedidos do historico. */
|
|
206
208
|
exports.TETOS_HITL_V2 = Object.freeze({
|
|
207
209
|
pergunta: 200, alternativaTexto: 140, consequencia: 140, porque: 140,
|
|
@@ -284,12 +286,12 @@ function validarDecidido(v) {
|
|
|
284
286
|
function validarAlternativas(v) {
|
|
285
287
|
const alternativas = v.alternativas;
|
|
286
288
|
if (!Array.isArray(alternativas) || alternativas.length < 2 || alternativas.length > exports.LETRAS_DE_ALTERNATIVA.length) {
|
|
287
|
-
throw new Error('pedido HITL v2: de 2 a
|
|
289
|
+
throw new Error('pedido HITL v2: de 2 a 5 alternativas, rotuladas de a a e');
|
|
288
290
|
}
|
|
289
291
|
let recomendadas = 0;
|
|
290
292
|
alternativas.forEach((bruta, i) => {
|
|
291
293
|
if (!objeto(bruta) || bruta.letra !== exports.LETRAS_DE_ALTERNATIVA[i]) {
|
|
292
|
-
throw new Error('pedido HITL v2: de 2 a
|
|
294
|
+
throw new Error('pedido HITL v2: de 2 a 5 alternativas, rotuladas de a a e');
|
|
293
295
|
}
|
|
294
296
|
if (!linhaUnica(bruta.texto, exports.TETOS_HITL_V2.alternativaTexto))
|
|
295
297
|
throw new Error('pedido HITL v2: alternativa sem texto');
|
|
@@ -343,6 +345,12 @@ function validarPergunta(v) {
|
|
|
343
345
|
!['esperar', 'escalar', 'seguir-recomendada'].includes(v.acaoPadraoAoExpirar)) {
|
|
344
346
|
throw new Error('pedido HITL v2: prazo ou ação de expiração inválidos');
|
|
345
347
|
}
|
|
348
|
+
if (v.dependenciaTecnica !== undefined) {
|
|
349
|
+
const d = v.dependenciaTecnica;
|
|
350
|
+
if (!objeto(d) || !linhaUnica(d.comando, exports.TETOS_HITL_V2.campoDaDecisao) || !linhaUnica(d.porque, exports.TETOS_HITL_V2.porque)) {
|
|
351
|
+
throw new Error('pedido HITL v2: dependência técnica exige o comando exato e o porquê, uma linha cada');
|
|
352
|
+
}
|
|
353
|
+
}
|
|
346
354
|
if (typeof v.irreversivel !== 'boolean')
|
|
347
355
|
throw new Error('pedido HITL v2: irreversível precisa ser declarado');
|
|
348
356
|
if (v.irreversivel) {
|
|
@@ -358,6 +366,53 @@ function validarPergunta(v) {
|
|
|
358
366
|
throw new Error('pedido HITL v2: só pedido irreversível nomeia ato');
|
|
359
367
|
}
|
|
360
368
|
}
|
|
369
|
+
// ---------------------------------------------------------------------------
|
|
370
|
+
// RM-057: o HITL de conducao que parte do ork e uma SELECAO.
|
|
371
|
+
//
|
|
372
|
+
// Na noite de 01 para 02/10/2026 a conducao parou mais de dez horas esperando um "confirmo" em
|
|
373
|
+
// texto livre. A regra do dono (02/10) e que todo pedido que o PROPRIO ork abre ao maestro sai com
|
|
374
|
+
// de 3 a 5 alternativas, exatamente uma com o selo "Recomendação", e nunca depende de o dono
|
|
375
|
+
// escrever ou colar texto. A unica excecao e a dependencia tecnica tipada: o dono roda um comando
|
|
376
|
+
// no terminal que a fabrica nao consegue rodar sozinha.
|
|
377
|
+
//
|
|
378
|
+
// A regra vale na CRIACAO (`registrarPedidoHitl`), nunca na leitura: o ledger guarda pedidos de
|
|
379
|
+
// duas alternativas que continuam legiveis para sempre, e a pergunta nativa de uma sessao do host
|
|
380
|
+
// (v1) traz as opcoes do host, que nao sao do ork.
|
|
381
|
+
// ---------------------------------------------------------------------------
|
|
382
|
+
exports.MINIMO_DE_ALTERNATIVAS_DE_CONDUCAO = 3;
|
|
383
|
+
exports.MAXIMO_DE_ALTERNATIVAS_DE_CONDUCAO = 5;
|
|
384
|
+
class SelecaoRecusada extends Error {
|
|
385
|
+
motivo;
|
|
386
|
+
constructor(motivo, detalhe) {
|
|
387
|
+
super(`${motivo}: ${detalhe}`);
|
|
388
|
+
this.motivo = motivo;
|
|
389
|
+
this.name = 'SelecaoRecusada';
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
exports.SelecaoRecusada = SelecaoRecusada;
|
|
393
|
+
/**
|
|
394
|
+
* RM-057: confere que um pedido que o ork vai ABRIR e uma selecao. Decisao informada (`decidido`)
|
|
395
|
+
* nao pergunta nada e passa. Recusa com motivo tipado, nunca corta nem completa em silencio.
|
|
396
|
+
*/
|
|
397
|
+
function exigirSelecaoDeConducao(pedido) {
|
|
398
|
+
if (!ehV2(pedido)) {
|
|
399
|
+
throw new SelecaoRecusada('hitl.selecao.versao', 'o pedido de condução do ork sai em ork.hitl/v2, com alternativas declaradas');
|
|
400
|
+
}
|
|
401
|
+
if (pedido.classe === 'decidido')
|
|
402
|
+
return;
|
|
403
|
+
const n = pedido.alternativas.length;
|
|
404
|
+
if (n < exports.MINIMO_DE_ALTERNATIVAS_DE_CONDUCAO || n > exports.MAXIMO_DE_ALTERNATIVAS_DE_CONDUCAO) {
|
|
405
|
+
throw new SelecaoRecusada('hitl.selecao.fora-da-faixa', `de ${exports.MINIMO_DE_ALTERNATIVAS_DE_CONDUCAO} a ${exports.MAXIMO_DE_ALTERNATIVAS_DE_CONDUCAO} alternativas, este traz ${n}`);
|
|
406
|
+
}
|
|
407
|
+
const recomendadas = pedido.alternativas.filter(a => a.recomendada).length;
|
|
408
|
+
if (recomendadas !== 1) {
|
|
409
|
+
throw new SelecaoRecusada('hitl.selecao.recomendada', `exatamente uma alternativa com o selo Recomendação, este traz ${recomendadas}`);
|
|
410
|
+
}
|
|
411
|
+
const pedeTexto = pedido.tipoDeResposta === 'aberta' || pedido.respostaAceita.tipo === 'texto';
|
|
412
|
+
if (pedeTexto && !pedido.dependenciaTecnica) {
|
|
413
|
+
throw new SelecaoRecusada('hitl.selecao.texto-livre', 'resposta em texto só com dependência técnica tipada: o comando exato que o dono roda no terminal e o porquê');
|
|
414
|
+
}
|
|
415
|
+
}
|
|
361
416
|
/** Valida `ork.hitl/v2`. Nunca aceita um v1: quem julga v1 e o validador congelado. */
|
|
362
417
|
function validarPedidoHitlV2(v) {
|
|
363
418
|
if (!objeto(v) || v.contrato !== exports.CONTRATO_HITL_V2)
|