@orkastery/cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +1 -1
- package/README.md +86 -65
- package/adapters/README.md +30 -15
- package/adapters/claude-code/.claude-plugin/plugin.json +11 -5
- package/adapters/claude-code/README.md +110 -8
- package/adapters/claude-code/commands/onboarding.md +21 -0
- package/adapters/claude-code/commands/ork.md +99 -27
- package/adapters/claude-code/hooks/hooks.json +6 -1
- package/adapters/claude-code/hooks/ork-sensor.js +66 -0
- package/adapters/codex/skills/ork/SKILL.md +57 -0
- package/adapters/hermes/README.md +163 -0
- package/adapters/hermes/bin/ork-abrir-thread.sh +2 -0
- package/adapters/hermes/bin/ork-brain.sh +4 -0
- package/adapters/hermes/bin/ork-hitl-answer.py +82 -0
- package/adapters/hermes/bin/ork-maestro.sh +6 -0
- package/adapters/hermes/bin/ork-master-enviar.py +84 -0
- package/adapters/hermes/bin/ork-objective-message.sh +17 -0
- package/adapters/hermes/bin/ork-objective-status.sh +12 -0
- package/adapters/hermes/bin/ork-pulse-enviar.py +47 -0
- package/adapters/hermes/hermes.plugin.json +26 -4
- package/adapters/hermes/hitl-ingress/__init__.py +356 -0
- package/adapters/hermes/hitl-ingress/plugin.yaml +4 -0
- package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +75 -22
- package/adapters/openclaw/README.md +135 -26
- package/adapters/openclaw/bin/ork-brain.sh +4 -0
- package/adapters/openclaw/construir.sh +19 -0
- package/adapters/openclaw/dist/hitl-ingress.js +199 -0
- package/adapters/openclaw/dist/index.js +437 -0
- package/adapters/openclaw/openclaw.plugin.json +39 -134
- package/adapters/openclaw/package.json +26 -0
- package/adapters/openclaw/src/hitl-ingress.ts +180 -0
- package/adapters/openclaw/src/index.ts +458 -0
- package/adapters/openclaw/src/tipos-openclaw.d.ts +58 -0
- package/adapters/openclaw/tsconfig.json +15 -0
- package/assets/docs/markdownlint-cli2.jsonc +22 -0
- package/assets/docs/padroes/documentacao-de-produto.md +133 -0
- package/assets/docs/padroes/roadmap-de-produto.md +98 -0
- package/assets/docs/produto/README.md +14 -0
- package/assets/docs/produto/_modelo-feature.md +59 -0
- package/assets/docs/roadmap/README.md +14 -0
- package/assets/docs/roadmap/_modelo-item.md +73 -0
- package/assets/orkmind-native-schema.json +32 -0
- package/assets/orkmind_bridge.py +357 -0
- package/assets/orkmind_fixture.py +67 -0
- package/assets/orkmind_prospective.py +138 -0
- package/assets/reference-tariffs-i07.json +23 -0
- package/dist/adapters/claude-bg.js +488 -39
- package/dist/adapters/codex-controller-sensor.js +449 -0
- package/dist/adapters/codex-controller-worker.js +361 -0
- package/dist/adapters/codex-controller.js +252 -0
- package/dist/adapters/codex-events.js +205 -0
- package/dist/adapters/codex-question.js +26 -0
- package/dist/adapters/codex-runner.js +133 -0
- package/dist/adapters/codex.js +394 -0
- package/dist/agents-md.js +84 -0
- package/dist/auditoria.js +11 -8
- package/dist/auditrun.js +5 -4
- package/dist/board.js +173 -22
- package/dist/branch-de-estado.js +136 -0
- package/dist/canarios-hitl.js +177 -0
- package/dist/canarios-i43.js +543 -0
- package/dist/canarios-pulse.js +147 -0
- package/dist/canarios-sensores.js +129 -0
- package/dist/canarios.js +111 -2
- package/dist/catalogo.js +9 -0
- package/dist/ci.js +216 -0
- package/dist/ciclos.js +2 -1
- package/dist/claim-lint.js +64 -0
- package/dist/claims.js +60 -0
- package/dist/company-brain-capture.js +195 -0
- package/dist/company-brain-cli.js +123 -0
- package/dist/company-brain-client.js +61 -0
- package/dist/company-brain-contract.js +166 -0
- package/dist/company-brain-journal.js +192 -0
- package/dist/company-brain-mcp.js +33 -0
- package/dist/company-brain-migration.js +60 -0
- package/dist/company-brain-source.js +177 -0
- package/dist/company-brain-worker.js +18 -0
- package/dist/conducao-texto.js +61 -0
- package/dist/conducao.js +876 -0
- package/dist/contrato-publico.js +39 -0
- package/dist/creation-operation-store.js +251 -0
- package/dist/creation-operation.js +148 -0
- package/dist/decisao-autonoma.js +183 -0
- package/dist/delegation.js +79 -0
- package/dist/demo.js +120 -0
- package/dist/docs.js +828 -0
- package/dist/doctor.js +183 -24
- package/dist/entrega-pr.js +117 -0
- package/dist/escopo-escrita.js +71 -0
- package/dist/estado-thread.js +222 -0
- package/dist/evalrunner.js +12 -0
- package/dist/fabrica-estado.js +329 -0
- package/dist/fabrica-publicar.js +76 -0
- package/dist/fix.js +21 -0
- package/dist/gates.js +55 -9
- package/dist/handoff.js +39 -23
- package/dist/hitl-canais.js +333 -0
- package/dist/hitl-classificacao.js +131 -0
- package/dist/hitl-contract.js +465 -0
- package/dist/hitl-estado.js +136 -0
- package/dist/hitl-gates.js +554 -0
- package/dist/hitl-ingress-receipt.js +382 -0
- package/dist/hitl-local-atestado.js +57 -0
- package/dist/hitl-local-receipt.js +328 -0
- package/dist/hitl-local.js +143 -0
- package/dist/hitl-lock.js +139 -0
- package/dist/hitl-lote.js +223 -0
- package/dist/hitl-native-offer.js +97 -0
- package/dist/hitl-native.js +65 -0
- package/dist/hitl-presentation.js +209 -0
- package/dist/hitl-public-receipt.js +176 -0
- package/dist/hitl-resumo.js +209 -0
- package/dist/hitl-sessions.js +475 -0
- package/dist/hitl.js +711 -0
- package/dist/horario.js +269 -0
- package/dist/hosts.js +198 -22
- package/dist/index.js +1736 -93
- package/dist/indice.js +99 -0
- package/dist/init.js +28 -3
- package/dist/integracoes-locais.js +17 -0
- package/dist/leases.js +65 -21
- package/dist/ledger-stats.js +272 -0
- package/dist/ledger.js +107 -2
- package/dist/licoes.js +215 -0
- package/dist/liveness.js +218 -0
- package/dist/maestro-actions.js +51 -0
- package/dist/maestro-authority.js +152 -0
- package/dist/maestro-cli.js +97 -0
- package/dist/maestro-contract.js +55 -0
- package/dist/maestro-discovery.js +132 -0
- package/dist/maestro-runtime.js +268 -0
- package/dist/maestro-snapshot.js +86 -0
- package/dist/maestro-sources.js +224 -0
- package/dist/manifest.js +165 -6
- package/dist/maquina.js +102 -0
- package/dist/master-audit.js +101 -0
- package/dist/master-batch.js +43 -0
- package/dist/master-digest.js +149 -0
- package/dist/master-migracao.js +155 -0
- package/dist/master.js +361 -77
- package/dist/mcp-artifacts.js +234 -0
- package/dist/mcp-git.js +433 -0
- package/dist/mcp-install.js +310 -0
- package/dist/mcp-maestro.js +25 -0
- package/dist/mcp-server.js +469 -0
- package/dist/mcp-ship.js +436 -0
- package/dist/mcp-verify.js +173 -0
- package/dist/memoria-humana.js +207 -0
- package/dist/memoria.js +314 -78
- package/dist/memory-migration.js +279 -0
- package/dist/memory-prospective.js +148 -0
- package/dist/modos-migracao.js +191 -0
- package/dist/modos.js +151 -15
- package/dist/monitor-lock.js +110 -0
- package/dist/objective.js +449 -0
- package/dist/ocupacao.js +267 -0
- package/dist/onboarding.js +303 -0
- package/dist/orkmind.js +389 -90
- package/dist/orquestracao.js +96 -69
- package/dist/phase.js +596 -150
- package/dist/playbook-capabilities.js +150 -0
- package/dist/playbook-contracts.js +188 -0
- package/dist/portfolio-context.js +61 -0
- package/dist/portfolio.js +180 -0
- package/dist/preflight.js +207 -0
- package/dist/process-audit.js +112 -0
- package/dist/project-state.js +93 -0
- package/dist/prompts.js +25 -5
- package/dist/prova-minima.js +112 -0
- package/dist/pulse-cadencia.js +163 -0
- package/dist/pulse-consentimento.js +260 -0
- package/dist/pulse-delivery.js +323 -0
- package/dist/pulse-resposta.js +542 -0
- package/dist/pulse.js +262 -0
- package/dist/ratelimit.js +35 -2
- package/dist/recall.js +80 -3
- package/dist/redacao-saida.js +45 -0
- package/dist/redacao-url.js +66 -0
- package/dist/retry.js +637 -115
- package/dist/roadmap-reservas.js +243 -0
- package/dist/runtime-ambiente.js +38 -0
- package/dist/runtime-context.js +215 -0
- package/dist/runtime-profiles.js +869 -0
- package/dist/runtimes.js +103 -0
- package/dist/sandbox.js +9 -0
- package/dist/session-events.js +203 -0
- package/dist/session-watcher-claude.js +637 -0
- package/dist/session-watcher.js +683 -0
- package/dist/sessoes-adopt.js +154 -0
- package/dist/sessoes-inventario.js +159 -0
- package/dist/sessoes.js +16 -35
- package/dist/setup.js +691 -0
- package/dist/ship.js +150 -15
- package/dist/slug.js +1 -1
- package/dist/thread-close.js +115 -0
- package/dist/thread.js +142 -28
- package/dist/tokens.js +1 -1
- package/dist/util.js +4 -2
- package/dist/verify-sandbox.js +265 -0
- package/dist/verify.js +270 -25
- package/dist/versao.js +60 -0
- package/dist/worktree.js +31 -2
- package/dist/write-activation.js +291 -0
- package/dist/yaml.js +2 -0
- package/eval/casos/onboarding.json +102 -0
- package/eval/casos/orkastery-bootstrap.json +35 -1
- package/eval/casos/scope-check-capability-map.json +45 -7
- package/eval/casos/ship-release.json +2 -2
- package/eval/fixtures/b0-slug-e-modos/caso.json +35 -22
- package/eval/fixtures/b2-master-log/caso.json +1 -1
- package/eval/fixtures/fx-adapter-editado-detectado/caso.json +19 -0
- package/eval/fixtures/fx-auto-quiet/caso.json +11 -0
- package/eval/fixtures/fx-blanket-approve/caso.json +10 -0
- package/eval/fixtures/fx-check-runtime-cruzado/caso.json +20 -0
- package/eval/fixtures/fx-codex-dry/caso.json +18 -0
- package/eval/fixtures/fx-donewhen-executavel/caso.json +15 -0
- package/eval/fixtures/fx-estado-dividido/caso.json +15 -0
- package/eval/fixtures/fx-fase-orfa/caso.json +116 -0
- package/eval/fixtures/fx-happy/caso.json +6 -4
- package/eval/fixtures/fx-hitl-latency/caso.json +20 -0
- package/eval/fixtures/fx-indice-reversao/caso.json +25 -0
- package/eval/fixtures/fx-listagem-abertas/caso.json +16 -0
- package/eval/fixtures/fx-maestro-bootstrap/caso.json +21 -0
- package/eval/fixtures/fx-modo-aposentado-escritor/caso.json +21 -0
- package/eval/fixtures/fx-modo-aposentado-leitor/caso.json +16 -0
- package/eval/fixtures/fx-objective-oscillation/caso.json +14 -0
- package/eval/fixtures/fx-omnicanal/caso.json +17 -0
- package/eval/fixtures/fx-sensores-runtime/caso.json +17 -0
- package/monitor/company-brain.cjs +17 -0
- package/monitor/pulse-scope.cjs +45 -0
- package/monitor/pulse.cron +18 -0
- package/monitor/varredura-pulse.sh +27 -0
- package/package.json +25 -4
- package/schemas/claims.schema.json +25 -0
- package/schemas/company-brain.schema.json +1158 -0
- package/schemas/creation-operation.schema.json +365 -0
- package/schemas/maestro-snapshot.schema.json +2716 -0
- package/skills/core/onboarding/SKILL.md +50 -0
- package/skills/core/orkastery-bootstrap/SKILL.md +95 -65
- package/skills/core/thread-state/SKILL.md +3 -1
- package/skills/governance/scope-check-capability-map/SKILL.md +17 -7
package/dist/pulse.js
ADDED
|
@@ -0,0 +1,262 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.JANELA_DAS_DECISOES_MS = exports.CONTRATO_PULSE = void 0;
|
|
4
|
+
exports.comporPulse = comporPulse;
|
|
5
|
+
exports.montarPulse = montarPulse;
|
|
6
|
+
exports.textoDoPulse = textoDoPulse;
|
|
7
|
+
const escopo_escrita_1 = require("./escopo-escrita");
|
|
8
|
+
/** D4: uma fila para quem precisa de humano, com fontes e espera observada. */
|
|
9
|
+
const claude_bg_1 = require("./adapters/claude-bg");
|
|
10
|
+
const hitl_1 = require("./hitl");
|
|
11
|
+
const liveness_1 = require("./liveness");
|
|
12
|
+
const master_1 = require("./master");
|
|
13
|
+
const master_batch_1 = require("./master-batch");
|
|
14
|
+
const orquestracao_1 = require("./orquestracao");
|
|
15
|
+
const thread_1 = require("./thread");
|
|
16
|
+
const sessoes_adopt_1 = require("./sessoes-adopt");
|
|
17
|
+
const ledger_1 = require("./ledger");
|
|
18
|
+
const types_1 = require("./types");
|
|
19
|
+
const util_1 = require("./util");
|
|
20
|
+
const horario_1 = require("./horario");
|
|
21
|
+
const hitl_presentation_1 = require("./hitl-presentation");
|
|
22
|
+
const hitl_contract_1 = require("./hitl-contract");
|
|
23
|
+
const decisao_autonoma_1 = require("./decisao-autonoma");
|
|
24
|
+
const docs_1 = require("./docs");
|
|
25
|
+
const conducao_1 = require("./conducao");
|
|
26
|
+
const conducao_texto_1 = require("./conducao-texto");
|
|
27
|
+
exports.CONTRATO_PULSE = 'ork.pulse/v1';
|
|
28
|
+
/** Uma semana: o resumo cita a decisao uma vez, e ela continua lida ate sair da janela. */
|
|
29
|
+
exports.JANELA_DAS_DECISOES_MS = 7 * 24 * 60 * 60 * 1000;
|
|
30
|
+
const impacto = (fase) => types_1.FASES.indexOf(fase) + 1;
|
|
31
|
+
const ordenar = (a, b) => (b.paradaHaMin ?? -1) - (a.paradaHaMin ?? -1) || b.impacto - a.impacto || a.id.localeCompare(b.id);
|
|
32
|
+
const minutos = (desde, quando) => desde && Number.isFinite(Date.parse(desde)) ? Math.max(0, Math.floor((Date.parse(quando) - Date.parse(desde)) / 60000)) : null;
|
|
33
|
+
function comporPulse(carregado, entrada) {
|
|
34
|
+
const { radar, monitor, batch, orfas } = entrada, quando = radar.consultadoEm;
|
|
35
|
+
const humanos = [], automaticas = [];
|
|
36
|
+
const registros = new Map();
|
|
37
|
+
const ehRegistro = (id) => {
|
|
38
|
+
if (!id)
|
|
39
|
+
return false;
|
|
40
|
+
if (!registros.has(id)) {
|
|
41
|
+
try {
|
|
42
|
+
registros.set(id, (0, sessoes_adopt_1.ehRegistroDeAdocao)((0, thread_1.lerThread)(carregado.raiz, id)));
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
registros.set(id, false);
|
|
46
|
+
} // Não esconder atenção sem confirmar proveniência.
|
|
47
|
+
}
|
|
48
|
+
return registros.get(id);
|
|
49
|
+
};
|
|
50
|
+
// I-45: so pede o dono quem pode receber resposta. Em 24/09/2026 a fila tinha 92 itens e 2 eram
|
|
51
|
+
// do dono: o resto era sessao morta, de thread fechada ou sem thread, que ninguem pode responder.
|
|
52
|
+
const statusDaThread = new Map();
|
|
53
|
+
const threadAberta = (id) => {
|
|
54
|
+
if (!id)
|
|
55
|
+
return false;
|
|
56
|
+
if (!statusDaThread.has(id)) {
|
|
57
|
+
try {
|
|
58
|
+
statusDaThread.set(id, (0, thread_1.lerThread)(carregado.raiz, id).status);
|
|
59
|
+
}
|
|
60
|
+
catch {
|
|
61
|
+
statusDaThread.set(id, null);
|
|
62
|
+
} // Thread ilegivel nao prova fechamento: fica visivel.
|
|
63
|
+
}
|
|
64
|
+
return statusDaThread.get(id) !== 'fechada';
|
|
65
|
+
};
|
|
66
|
+
const TERMINAIS = ['failed', 'done', 'stopped', 'completed'];
|
|
67
|
+
for (const s of radar.sessoes.filter(s => s.precisaDeHumano)) {
|
|
68
|
+
// Job morto ou estado terminal é história. Bloqueio vivo ou desconhecido continua visível.
|
|
69
|
+
if (ehRegistro(s.thread?.id ?? null) && s.jobVivo !== true &&
|
|
70
|
+
(s.jobVivo === false || TERMINAIS.includes(s.estadoBruto)))
|
|
71
|
+
continue;
|
|
72
|
+
const semThread = !s.thread?.id, aberta = threadAberta(s.thread?.id ?? null);
|
|
73
|
+
const encerrada = s.jobVivo !== true && (s.jobVivo === false || TERMINAIS.includes(s.estadoBruto));
|
|
74
|
+
// Morta ou terminal, sem thread ou de thread fechada: historia, ninguem para responder.
|
|
75
|
+
if (encerrada && !aberta)
|
|
76
|
+
continue;
|
|
77
|
+
// Incerta de thread fechada tambem e historia. Incerta SEM thread continua visivel: pode ser
|
|
78
|
+
// uma sessao do proprio dono esperando resposta (o alerta I-01, fixado no fx-hitl-latency).
|
|
79
|
+
if (s.jobVivo !== true && !encerrada && !aberta && !semThread)
|
|
80
|
+
continue;
|
|
81
|
+
// Thread aberta com sessao morta ou terminal: quem resolve e o retry de orfas, nao o dono.
|
|
82
|
+
const destino = encerrada ? automaticas : humanos;
|
|
83
|
+
destino.push({ id: `sessao:${s.sessionId || s.id}`, classe: s.classe, motivo: s.tipoDeHitl ?? s.classe,
|
|
84
|
+
thread: s.thread?.id ?? null, fase: s.thread?.fase ?? null, sessionId: s.sessionId,
|
|
85
|
+
desdeEm: s.bloqueadaDesdeEm ?? null, paradaHaMin: s.paradaHaMin ?? null, impacto: impacto(s.thread?.fase ?? null),
|
|
86
|
+
pergunta: s.pergunta || s.detalhe, opcoes: s.alternativas, recomendacao: s.recomendacao,
|
|
87
|
+
comandoResposta: s.classe === 'abandonada' ? s.comandos.logs : s.comandos.attach,
|
|
88
|
+
evidencia: [`claude agents: state=${s.estadoBruto}`, s.comandos.logs], fontes: ['sessions hitl'], contextoLogs: s.contextoLogs ?? [] });
|
|
89
|
+
}
|
|
90
|
+
// I-45: verificacao reprovada de uma candidata cujo codigo ja esta na base nao e pendencia do
|
|
91
|
+
// dono: o CHECK independente decidiu e o merge aconteceu. Vale o primeiro merge (regra do `ork docs`).
|
|
92
|
+
const base = (0, docs_1.resolverBase)(carregado.raiz, carregado.manifesto.worktree?.base_branch ?? 'main');
|
|
93
|
+
const mesclada = new Map();
|
|
94
|
+
const jaMesclada = (thread) => {
|
|
95
|
+
if (!base)
|
|
96
|
+
return false;
|
|
97
|
+
if (!mesclada.has(thread)) {
|
|
98
|
+
try {
|
|
99
|
+
mesclada.set(thread, (0, docs_1.mergeDaThread)(carregado.raiz, thread, base) !== null);
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
mesclada.set(thread, false);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return mesclada.get(thread);
|
|
106
|
+
};
|
|
107
|
+
const VERIFICACAO = ['claims.failed', 'claims.unverifiable', 'verify.failed', 'verify.regression', 'verify.timeout', 'verify.sem-veredito', 'ci.failed'];
|
|
108
|
+
for (const l of monitor.linhas)
|
|
109
|
+
for (const p of l.paradas) {
|
|
110
|
+
if (VERIFICACAO.includes(p.motivo) && jaMesclada(l.thread))
|
|
111
|
+
continue;
|
|
112
|
+
if (ehRegistro(l.thread)) {
|
|
113
|
+
if (p.fonte === 'escalonador' || ['lease.busy', 'conducao.em-andamento', 'concurrency.limite', 'vaga.stale', 'runtime.silencio'].includes(p.motivo))
|
|
114
|
+
continue;
|
|
115
|
+
const sessoes = radar.sessoes.filter(s => s.thread?.id === l.thread && s.thread.fase === p.fase);
|
|
116
|
+
// Escalada explícita de ledger não é apagada pela conclusão de uma sessão.
|
|
117
|
+
if (p.fonte !== 'ledger' && sessoes.length > 0 && sessoes.every(s => s.jobVivo !== true &&
|
|
118
|
+
(s.jobVivo === false || ['failed', 'done', 'stopped', 'completed'].includes(s.estadoBruto))))
|
|
119
|
+
continue;
|
|
120
|
+
}
|
|
121
|
+
if (p.sessaoViva === true)
|
|
122
|
+
continue;
|
|
123
|
+
if (orfas.some(o => o.thread === l.thread && o.fase === p.fase) &&
|
|
124
|
+
['runtime.silencio', 'vaga.stale'].includes(p.motivo))
|
|
125
|
+
continue;
|
|
126
|
+
const mesmaEspera = humanos.find(s => s.thread === l.thread && s.fase === p.fase &&
|
|
127
|
+
p.sessaoEstado === 'blocked' && !!s.sessionId && p.evidencia.includes(s.sessionId.slice(0, 8)));
|
|
128
|
+
if (mesmaEspera) {
|
|
129
|
+
mesmaEspera.fontes.push('monitor');
|
|
130
|
+
mesmaEspera.evidencia.push(p.evidencia);
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
const item = { id: `thread:${l.thread}:${p.fase}:${p.motivo}:${p.fonte}`, classe: 'thread', motivo: p.motivo,
|
|
134
|
+
thread: l.thread, fase: p.fase, sessionId: null, desdeEm: p.desdeEm, paradaHaMin: p.paradaHaMin, impacto: impacto(p.fase),
|
|
135
|
+
pergunta: p.detalhe, opcoes: [], recomendacao: p.correcao, comandoResposta: p.correcao, evidencia: [p.evidencia],
|
|
136
|
+
fontes: ['monitor'], contextoLogs: [] };
|
|
137
|
+
// I-45: vaga parada e sinal do escalonador (`ork board reap` devolve a vaga), nao pergunta ao dono.
|
|
138
|
+
const esperaAutomatica = ['lease.busy', 'conducao.em-andamento', 'concurrency.limite', 'runtime.rate-limited', 'vaga.stale'].includes(p.motivo);
|
|
139
|
+
(esperaAutomatica ? automaticas : humanos).push(item);
|
|
140
|
+
}
|
|
141
|
+
for (const b of batch) {
|
|
142
|
+
const eventos = (0, ledger_1.lerLedger)((0, thread_1.dirThread)(carregado.raiz, b.thread.id));
|
|
143
|
+
const desde = eventos.filter(e => e.tipo === 'ship_done').at(-1)?.ts ??
|
|
144
|
+
eventos.filter(e => e.tipo === 'phase_result' && e.fase === 'MASTER').at(-1)?.ts ?? b.thread.atualizadaEm;
|
|
145
|
+
const proposta = b.thread.score_proposto;
|
|
146
|
+
const respostas = proposta && b.entregou ? master_1.ORDEM_DAS_CLASSES.map(c => `ratificar ${b.thread.id} ${(0, master_batch_1.assinaturaProposta)(proposta)} ${c}`) : [];
|
|
147
|
+
// I-45: a fila de nota foi aposentada na I-43. A entrega sem nota e aceita por padrao, com
|
|
148
|
+
// registro; ela sai da atencao do dono e fica na faixa automatica. A nota humana, quando vier,
|
|
149
|
+
// continua sobrescrevendo, e as respostas de ratificacao seguem no item.
|
|
150
|
+
automaticas.push({ id: `score:${b.thread.id}`, classe: 'score_pendente', motivo: 'master.score-pendente', thread: b.thread.id,
|
|
151
|
+
fase: 'MASTER', sessionId: null, desdeEm: desde, paradaHaMin: minutos(desde, quando), impacto: impacto('MASTER'),
|
|
152
|
+
pergunta: proposta ? `Proposta para ${b.thread.nome}: ${proposta.valor}/5. ${proposta.justificativa}` : `Entrega ${b.thread.nome} sem nota: aceita por padrão, com registro.`,
|
|
153
|
+
opcoes: respostas.length ? respostas : ['0', '1', '2', '3', '4', '5'],
|
|
154
|
+
recomendacao: 'Aceitar por padrão com registro; a nota humana, quando vier, sobrescreve.',
|
|
155
|
+
comandoResposta: 'ork master --aceitar-omissao',
|
|
156
|
+
evidencia: [`ork master; regime ${b.regime}`], fontes: ['master'], contextoLogs: [] });
|
|
157
|
+
}
|
|
158
|
+
for (const o of orfas) {
|
|
159
|
+
if (ehRegistro(o.thread))
|
|
160
|
+
continue;
|
|
161
|
+
const item = { id: `orfa:${o.thread}:${o.fase}:${o.sessionId}`, classe: o.classe, motivo: o.motivo,
|
|
162
|
+
thread: o.thread, fase: o.fase, sessionId: o.sessionId, desdeEm: o.desdeEm, paradaHaMin: minutos(o.desdeEm, quando),
|
|
163
|
+
impacto: impacto(o.fase), pergunta: `Fase ${o.fase} sem heartbeat desde ${o.ultimoHeartbeatEm}.`,
|
|
164
|
+
opcoes: [], recomendacao: o.retry.razao,
|
|
165
|
+
comandoResposta: o.retry.automatica ? `ork retry run ${o.thread} --motivo runtime.silencio --fase ${o.fase}` :
|
|
166
|
+
`ork retry plan ${o.thread} --motivo runtime.silencio --fase ${o.fase}`,
|
|
167
|
+
evidencia: [...o.evidencia, `retry ${o.retry.tentativas}/${o.retry.limite}`], fontes: ['liveness'], contextoLogs: [] };
|
|
168
|
+
(o.retry.automatica ? automaticas : humanos).push(item);
|
|
169
|
+
}
|
|
170
|
+
const unicos = (items) => [...new Map(items.map(i => [i.id, i])).values()].sort(ordenar);
|
|
171
|
+
const h = unicos(humanos), a = unicos(automaticas);
|
|
172
|
+
for (const item of h) {
|
|
173
|
+
if (!item.thread || item.classe === 'score_pendente')
|
|
174
|
+
continue;
|
|
175
|
+
try {
|
|
176
|
+
// T6: o pedido vem primeiro, porque e ele quem diz quanta evidencia esta pergunta merece.
|
|
177
|
+
// Sem pedido aberto, `apresentarHitl` cai no default do modo, como sempre fez.
|
|
178
|
+
const pedido = (0, hitl_presentation_1.pedidoHitlAberto)(carregado.raiz, item.thread, item.fase, item.sessionId);
|
|
179
|
+
item.apresentacao = (0, hitl_presentation_1.apresentarHitl)(carregado.raiz, item.thread, pedido?.profundidade);
|
|
180
|
+
if (pedido) {
|
|
181
|
+
// I-41 (T4b): o pulse le o pedido pelos leitores uniformes, e o que muda para o dono e a
|
|
182
|
+
// CHAVE que ele digita: numero no v1, letra no v2. `pedidoHitlAberto` ja descartou
|
|
183
|
+
// `decidido`, que nao tem alvo; e por isso que existe alvo para ler aqui.
|
|
184
|
+
item.pedido = pedido;
|
|
185
|
+
item.pergunta = (0, hitl_contract_1.textoDoPedido)(pedido);
|
|
186
|
+
item.opcoes = (0, hitl_contract_1.escolhasDoPedido)(pedido).map(o => `${(0, hitl_contract_1.chaveDaEscolha)(pedido, o.numero)}. ${o.texto}`);
|
|
187
|
+
item.recomendacao = (0, hitl_contract_1.recomendacaoDoPedido)(pedido);
|
|
188
|
+
item.comandoResposta = `/ork ${(0, hitl_contract_1.alvoDoPedido)(pedido)?.tipo === 'session' ? 'session' : 'gate'} ${pedido.thread} ${pedido.id} <resposta>`;
|
|
189
|
+
// O Pulse nao e uma conexao MCP: nenhum canal mcp-local sai `disponivel` daqui.
|
|
190
|
+
item.canais = (0, hitl_presentation_1.ofertaDoPedido)(pedido);
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
item.evidencia.push('Apresentação indisponível; conferir estado canônico da thread.');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
return { contrato: exports.CONTRATO_PULSE, consultadoEm: quando, runtime: { ok: radar.runtimeConsultado, detalhe: radar.runtimeDetalhe },
|
|
198
|
+
precisaDeHumanoAgora: h, acoesAutomaticas: a, resumo: { humanos: h.length, automaticas: a.length, scores: batch.length, fasesOrfas: orfas.length } };
|
|
199
|
+
}
|
|
200
|
+
function montarPulse(carregado, opcoes = {}) {
|
|
201
|
+
if (opcoes.registrar)
|
|
202
|
+
(0, escopo_escrita_1.exigirEscopoDeEscrita)(opcoes.escopo);
|
|
203
|
+
const diagnosticos = [];
|
|
204
|
+
const quando = opcoes.quando ?? (0, util_1.agora)();
|
|
205
|
+
const consulta = opcoes.consulta ?? (opcoes.semRuntime
|
|
206
|
+
? { ok: false, sessoes: [], detalhe: 'runtime não consultado por --sem-runtime' } : (0, claude_bg_1.consultarSessoes)(undefined, true));
|
|
207
|
+
const estados = consulta.ok ? new Map(consulta.sessoes.map(s => [s.sessionId ?? '', (0, hitl_1.estadoBruto)(s)])) : null;
|
|
208
|
+
const radar = (0, hitl_1.varrerSessoes)({ raiz: carregado.raiz, consulta, agora: quando, registrar: opcoes.registrar, escopo: opcoes.escopo, linhasLogs: opcoes.linhasLogs });
|
|
209
|
+
if (radar.telasAdiadas)
|
|
210
|
+
diagnosticos.push(`radar.orcamento: ${radar.telasAdiadas} tela(s) de sessão ficaram para a próxima varredura`);
|
|
211
|
+
// Falha de observação não prova silêncio: Claude indisponível preserva sua guarda de HITL e Codex mantém sua coleta independente.
|
|
212
|
+
const orfas = (0, liveness_1.detectarFasesOrfas)(carregado, { quando, registrar: opcoes.registrar, escopo: opcoes.escopo, diagnosticos,
|
|
213
|
+
fontes: opcoes.fontes, estados: estados ?? undefined, runtimes: consulta.ok ? undefined : ['codex'] });
|
|
214
|
+
// I-36 (T16): conducao sem dono vivo sai aqui, com prova no ledger, so dentro do escopo de escrita.
|
|
215
|
+
if (opcoes.registrar)
|
|
216
|
+
for (const id of opcoes.escopo ?? []) {
|
|
217
|
+
try {
|
|
218
|
+
(0, conducao_1.liberarSeOrfa)(carregado.raiz, id);
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
diagnosticos.push(`conducao.recuperacao: ${id}: ${e.message.slice(0, 120)}`);
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
const monitor = (0, orquestracao_1.montarMonitor)(carregado, { agora: quando, estados });
|
|
225
|
+
const pulse = comporPulse(carregado, { radar, monitor, orfas, batch: (0, master_1.pendentesDeScore)(carregado.raiz) });
|
|
226
|
+
const conducoes = monitor.linhas.filter((l) => l.conducao).map((l) => ({ thread: l.thread, linha: (0, conducao_texto_1.linhaDeConducao)(l.conducao, { agora: quando }) }));
|
|
227
|
+
if (conducoes.length > 0)
|
|
228
|
+
pulse.conducoes = conducoes;
|
|
229
|
+
// B4: a metade que presta contas. Leitura pura; falhar aqui nunca derruba a fila de atencao.
|
|
230
|
+
try {
|
|
231
|
+
const { decisoes, acimaDoLimiar } = (0, decisao_autonoma_1.decisoesParaODono)(carregado.raiz, new Date(Date.parse(quando) - exports.JANELA_DAS_DECISOES_MS).toISOString());
|
|
232
|
+
pulse.decisoes = decisoes;
|
|
233
|
+
pulse.acimaDoLimiar = acimaDoLimiar;
|
|
234
|
+
}
|
|
235
|
+
catch (e) {
|
|
236
|
+
diagnosticos.push(`decisoes.indisponiveis: ${e.message.slice(0, 120)}`);
|
|
237
|
+
}
|
|
238
|
+
if (diagnosticos.length)
|
|
239
|
+
pulse.runtime.detalhe = [pulse.runtime.detalhe, ...diagnosticos].filter(Boolean).join('; ');
|
|
240
|
+
return pulse;
|
|
241
|
+
}
|
|
242
|
+
function textoDoPulse(p) {
|
|
243
|
+
// I-35: o pulse guarda ISO; o texto ao dono sai no fuso dele, rotulado no cabeçalho.
|
|
244
|
+
const local = (texto) => (0, horario_1.localizarTexto)(texto, { agora: p.consultadoEm });
|
|
245
|
+
const linhas = [`Pulse (${p.contrato}) ${(0, horario_1.formatarDataHoraRotulada)(p.consultadoEm, { agora: p.consultadoEm })}`, `Precisa de humano agora: ${p.resumo.humanos}`, `${p.resumo.scores} entrega(s) sem nota, aceitas por padrão com registro`];
|
|
246
|
+
if (p.runtime.ok && p.runtime.detalhe.includes('liveness.snapshot.invalid'))
|
|
247
|
+
linhas.push(`[diagnostico] ${local(p.runtime.detalhe)}`);
|
|
248
|
+
if (!p.runtime.ok)
|
|
249
|
+
linhas.push(`[consulta incompleta] ${local(p.runtime.detalhe)}`);
|
|
250
|
+
for (const i of p.precisaDeHumanoAgora) {
|
|
251
|
+
linhas.push('', `${i.thread ?? i.sessionId} [${i.classe}] ${i.paradaHaMin == null ? 'desconhecido' : i.paradaHaMin < 1 ? 'menos de 1' : i.paradaHaMin} min`, local(i.pergunta), ...i.opcoes.map(o => ` opção: ${o}`), `Recomendação: ${i.recomendacao}`, `Responder: ${i.comandoResposta}`, ...i.contextoLogs.map(l => ` log: ${l}`));
|
|
252
|
+
}
|
|
253
|
+
if (p.conducoes?.length) {
|
|
254
|
+
linhas.push('', `Conduzidas agora: ${p.conducoes.length}`);
|
|
255
|
+
for (const c of p.conducoes)
|
|
256
|
+
linhas.push(`${c.thread}: ${c.linha}`);
|
|
257
|
+
}
|
|
258
|
+
linhas.push('', `Ações automáticas pendentes: ${p.resumo.automaticas}`);
|
|
259
|
+
for (const i of p.acoesAutomaticas)
|
|
260
|
+
linhas.push(`${i.thread}: ${i.motivo}; ${i.comandoResposta}`);
|
|
261
|
+
return linhas.join('\n');
|
|
262
|
+
}
|
package/dist/ratelimit.js
CHANGED
|
@@ -60,6 +60,8 @@ exports.caminhoDaFila = caminhoDaFila;
|
|
|
60
60
|
exports.lerFilaDeRetomada = lerFilaDeRetomada;
|
|
61
61
|
exports.janelaPadraoMs = janelaPadraoMs;
|
|
62
62
|
exports.enfileirar = enfileirar;
|
|
63
|
+
exports.prazoDaConta = prazoDaConta;
|
|
64
|
+
exports.marcarContaDaFalha = marcarContaDaFalha;
|
|
63
65
|
exports.gravarPedido = gravarPedido;
|
|
64
66
|
exports.exigirPedido = exigirPedido;
|
|
65
67
|
exports.aguardando = aguardando;
|
|
@@ -69,6 +71,8 @@ exports.tabelaDaFila = tabelaDaFila;
|
|
|
69
71
|
const path = __importStar(require("node:path"));
|
|
70
72
|
const manifest_1 = require("./manifest");
|
|
71
73
|
const util_1 = require("./util");
|
|
74
|
+
const horario_1 = require("./horario");
|
|
75
|
+
const runtime_profiles_1 = require("./runtime-profiles");
|
|
72
76
|
var claude_bg_1 = require("./adapters/claude-bg");
|
|
73
77
|
Object.defineProperty(exports, "parseRateLimit", { enumerable: true, get: function () { return claude_bg_1.parseRateLimit; } });
|
|
74
78
|
/** Diretorio de estado da autonomia do B3. */
|
|
@@ -81,6 +85,11 @@ function dirRetry(raiz) {
|
|
|
81
85
|
* Fica no projeto, e nao na thread, porque o limite de uso e da ASSINATURA: uma fase de
|
|
82
86
|
* outra thread que morre no mesmo minuto espera a mesma janela, e quem olha a fila
|
|
83
87
|
* precisa ver as duas de uma vez.
|
|
88
|
+
*
|
|
89
|
+
* I-33 (P4, R3): com perfis, a assinatura deixa de ser uma so. A fila vira o ULTIMO recurso,
|
|
90
|
+
* quando nenhum perfil ativo resta em nenhum runtime da ordem de fallback, e o prazo e o menor
|
|
91
|
+
* `esgotadoAte` entre os perfis. O formato do `fila.jsonl` nao muda: o pedido so ganha campos
|
|
92
|
+
* opcionais (runtime, perfil e motivo), e pedido antigo continua legivel sem eles.
|
|
84
93
|
*/
|
|
85
94
|
function caminhoDaFila(raiz) {
|
|
86
95
|
return path.join(dirRetry(raiz), 'fila.jsonl');
|
|
@@ -123,10 +132,34 @@ function enfileirar(carregado, novo, agoraMs = Date.now()) {
|
|
|
123
132
|
criadoEm: (0, util_1.agora)(),
|
|
124
133
|
atualizadoEm: (0, util_1.agora)(),
|
|
125
134
|
detalhe: novo.detalhe,
|
|
135
|
+
...(novo.runtime !== undefined ? { runtime: novo.runtime } : {}),
|
|
136
|
+
...(novo.perfil !== undefined ? { perfil: novo.perfil } : {}),
|
|
137
|
+
...(novo.motivo !== undefined ? { motivo: novo.motivo } : {}),
|
|
126
138
|
};
|
|
127
139
|
(0, util_1.anexarJsonl)(caminhoDaFila(raiz), pedido);
|
|
128
140
|
return pedido;
|
|
129
141
|
}
|
|
142
|
+
/**
|
|
143
|
+
* I-33 (D5): ate quando a conta sai do rodizio. Cota esgotada: a hora dita pelo runtime ou, sem
|
|
144
|
+
* ela, a janela padrao do manifesto (estimativa declarada, nunca chute). Auth ausente: sem prazo.
|
|
145
|
+
*/
|
|
146
|
+
function prazoDaConta(manifesto, falha, agoraMs = Date.now()) {
|
|
147
|
+
if (falha.motivo === 'runtime.auth-missing')
|
|
148
|
+
return null;
|
|
149
|
+
// A10: horario dito que ja passou nao devolve o perfil ao rodizio na hora: vale a janela padrao.
|
|
150
|
+
if (falha.resetEm !== null && Date.parse(falha.resetEm) > agoraMs)
|
|
151
|
+
return falha.resetEm;
|
|
152
|
+
return new Date(agoraMs + janelaPadraoMs(manifesto)).toISOString();
|
|
153
|
+
}
|
|
154
|
+
/** Marca no store o perfil que falhou pela conta. Sem perfil (ambiente do processo), nada a marcar. */
|
|
155
|
+
function marcarContaDaFalha(carregado, perfil, falha, agoraMs = Date.now()) {
|
|
156
|
+
if (!perfil)
|
|
157
|
+
return null;
|
|
158
|
+
const esgotado = falha.motivo === 'runtime.quota-exhausted';
|
|
159
|
+
return (0, runtime_profiles_1.marcarFalhaDePerfil)(carregado.raiz, perfil.id, { estado: esgotado ? 'esgotado' : 'sem-auth',
|
|
160
|
+
esgotadoAte: prazoDaConta(carregado.manifesto, falha, agoraMs), motivo: falha.motivo, detalhe: falha.trecho,
|
|
161
|
+
em: new Date(agoraMs).toISOString() });
|
|
162
|
+
}
|
|
130
163
|
/** Regrava um pedido (append-only: a versao nova vence, o historico fica). */
|
|
131
164
|
function gravarPedido(raiz, pedido) {
|
|
132
165
|
const atualizado = { ...pedido, atualizadoEm: (0, util_1.agora)() };
|
|
@@ -170,10 +203,10 @@ function tabelaDaFila(raiz, quando = (0, util_1.agora)()) {
|
|
|
170
203
|
p.thread,
|
|
171
204
|
p.fase,
|
|
172
205
|
p.estado,
|
|
173
|
-
|
|
206
|
+
(0, horario_1.formatarDataHora)(p.liberaEm, { agora: quando }),
|
|
174
207
|
p.janelaEstimada ? `estimada (${p.sinal.fonte})` : `dita pelo runtime (${p.sinal.fonte})`,
|
|
175
208
|
p.estado === 'aguardando' ? faltaPara(p, quando) : '',
|
|
176
209
|
String(p.tentativas),
|
|
177
210
|
]);
|
|
178
|
-
return (0, util_1.tabela)(['ID', 'THREAD', 'FASE', 'ESTADO', 'LIBERA EM', 'ORIGEM DA HORA', 'FALTA', 'TENT'], linhas)
|
|
211
|
+
return (0, util_1.tabela)(['ID', 'THREAD', 'FASE', 'ESTADO', 'LIBERA EM', 'ORIGEM DA HORA', 'FALTA', 'TENT'], linhas) + `\n${(0, horario_1.legendaDoFuso)(undefined, quando)}`;
|
|
179
212
|
}
|
package/dist/recall.js
CHANGED
|
@@ -61,6 +61,7 @@ exports.textoDoRecall = textoDoRecall;
|
|
|
61
61
|
const fs = __importStar(require("node:fs"));
|
|
62
62
|
const handoff_1 = require("./handoff");
|
|
63
63
|
const ledger_1 = require("./ledger");
|
|
64
|
+
const memoria_1 = require("./memoria");
|
|
64
65
|
const orkmind_1 = require("./orkmind");
|
|
65
66
|
const thread_1 = require("./thread");
|
|
66
67
|
const util_1 = require("./util");
|
|
@@ -93,18 +94,62 @@ function naoResolvido(p, motivo, detalhe) {
|
|
|
93
94
|
detalhe,
|
|
94
95
|
};
|
|
95
96
|
}
|
|
97
|
+
/** O contexto da sessão não pode ser reutilizado para outra thread ou tenant. */
|
|
98
|
+
function conferirEscopo(carregado, memoria, threadId) {
|
|
99
|
+
const escopo = memoria.leituraRestrita;
|
|
100
|
+
if (escopo && (escopo.thread !== threadId ||
|
|
101
|
+
escopo.tenant !== (0, memoria_1.tagsDoProjeto)(carregado.manifesto).project[0])) {
|
|
102
|
+
throw Error('memory.query.scope-conflict');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
/** Somente códigos conhecidos atravessam a fronteira; detalhes do driver podem conter segredos. */
|
|
106
|
+
function falhaDeConsulta(erro) {
|
|
107
|
+
const permitidos = new Set([
|
|
108
|
+
'memory.query.invalid', 'memory.query.unsupported', 'memory.query.window-saturated',
|
|
109
|
+
'memory.query.scope-conflict', 'memory.query.scope-violation', 'memory.query.response-invalid',
|
|
110
|
+
'memory.transport.interpreter', 'memory.transport.secret', 'memory.transport.bridge',
|
|
111
|
+
'memory.transport.timeout', 'memory.transport.spawn', 'memory.transport.failed',
|
|
112
|
+
'memory.transport.json', 'memory.transport.unavailable', 'memory.schema.absent',
|
|
113
|
+
'memory.legacy.provenance-collision', 'memory.prospective.marker-invalid',
|
|
114
|
+
'memory.native.schema-mismatch', 'cli.ausente',
|
|
115
|
+
]);
|
|
116
|
+
const codigo = erro instanceof Error ? erro.message.split(':', 1)[0] : '';
|
|
117
|
+
return permitidos.has(codigo) ? codigo : 'memory.query.failed';
|
|
118
|
+
}
|
|
96
119
|
/**
|
|
97
120
|
* Resolve UM ponteiro, escolhendo a via pelo regime efetivo.
|
|
98
121
|
*
|
|
99
122
|
* Preferencia pela memoria semantica quando ela existe e o ponteiro tem endereco; queda
|
|
100
123
|
* para o arquivo quando ela nao existe, quando o endereco nao resolve ou quando o
|
|
101
|
-
* ponteiro nunca teve endereco (todo handoff do regime `files`).
|
|
124
|
+
* ponteiro nunca teve endereco (todo handoff do regime `files`). No contexto restrito
|
|
125
|
+
* ativo, falha ou ausência semântica permanece explícita, sem queda silenciosa.
|
|
102
126
|
*/
|
|
103
127
|
function resolverPonteiro(carregado, memoria, threadId, p) {
|
|
128
|
+
conferirEscopo(carregado, memoria, threadId);
|
|
129
|
+
const restrito = memoria.leituraRestrita !== undefined;
|
|
104
130
|
if (memoria.ativo && p.orkmind) {
|
|
105
131
|
const endereco = (0, orkmind_1.lerEndereco)(p.orkmind);
|
|
106
|
-
|
|
107
|
-
|
|
132
|
+
if (restrito && !endereco)
|
|
133
|
+
return naoResolvido(p, 'nao-resolve', 'memory.query.invalid');
|
|
134
|
+
let entrada = null;
|
|
135
|
+
try {
|
|
136
|
+
entrada = endereco ? memoria.porId(endereco.colecao, endereco.id) : null;
|
|
137
|
+
}
|
|
138
|
+
catch (erro) {
|
|
139
|
+
if (restrito)
|
|
140
|
+
return naoResolvido(p, 'orkmind-indisponivel', falhaDeConsulta(erro));
|
|
141
|
+
// Compatibilidade da API de manutenção: queda dirigida para arquivo.
|
|
142
|
+
}
|
|
143
|
+
if (restrito && !entrada)
|
|
144
|
+
return naoResolvido(p, 'nao-resolve', 'memory.query.not-found');
|
|
145
|
+
if (restrito && entrada && (entrada.collection !== endereco.colecao ||
|
|
146
|
+
entrada.id !== endereco.id ||
|
|
147
|
+
!entrada.tags.situation?.includes((0, memoria_1.situacaoDaThread)(threadId)))) {
|
|
148
|
+
return naoResolvido(p, 'nao-resolve', 'memory.query.scope-violation');
|
|
149
|
+
}
|
|
150
|
+
const tenant = (0, memoria_1.tagsDoProjeto)(carregado.manifesto).project[0];
|
|
151
|
+
if (entrada && entrada.tags.project?.includes(tenant) &&
|
|
152
|
+
(entrada.collection !== 'handoff' || (0, memoria_1.threadDaEntrada)(entrada) === threadId)) {
|
|
108
153
|
return {
|
|
109
154
|
id: p.id,
|
|
110
155
|
titulo: p.titulo,
|
|
@@ -121,6 +166,8 @@ function resolverPonteiro(carregado, memoria, threadId, p) {
|
|
|
121
166
|
detalhe: `orkmind://${entrada.collection}/${entrada.id}`,
|
|
122
167
|
};
|
|
123
168
|
}
|
|
169
|
+
if (restrito)
|
|
170
|
+
return naoResolvido(p, 'nao-resolve', 'memory.query.scope-violation');
|
|
124
171
|
}
|
|
125
172
|
try {
|
|
126
173
|
const r = (0, handoff_1.recall)(carregado, threadId, p.location);
|
|
@@ -155,6 +202,7 @@ function resolverPonteiro(carregado, memoria, threadId, p) {
|
|
|
155
202
|
* secao 6.1 da visao (rastreabilidade): nada some, nada entra sem hora marcada.
|
|
156
203
|
*/
|
|
157
204
|
function recallDaThread(carregado, memoria, threadId, opcoes = {}) {
|
|
205
|
+
conferirEscopo(carregado, memoria, threadId);
|
|
158
206
|
const { raiz } = carregado;
|
|
159
207
|
// Thread inexistente e erro, nao lista vazia: o CLI precisa reprovar o id errado.
|
|
160
208
|
(0, thread_1.lerThread)(raiz, threadId);
|
|
@@ -183,6 +231,35 @@ function recallDaThread(carregado, memoria, threadId, opcoes = {}) {
|
|
|
183
231
|
else
|
|
184
232
|
falhas.push(r);
|
|
185
233
|
}
|
|
234
|
+
// Descoberta de handoffs destinados exatamente a esta fase. Referencias explicitas
|
|
235
|
+
// continuam obedecendo retrieve_when, inclusive para um handoff anterior.
|
|
236
|
+
if (memoria.ativo && momento && opcoes.id === undefined && !opcoes.todos && !opcoes.forcar) {
|
|
237
|
+
const fase = momento.trim().toUpperCase();
|
|
238
|
+
const tags = { project: (0, memoria_1.tagsDoProjeto)(carregado.manifesto).project,
|
|
239
|
+
skill: [fase], situation: [(0, memoria_1.situacaoDaThread)(threadId)] };
|
|
240
|
+
const vistos = new Set(resolvidos.map(r => r.orkmind));
|
|
241
|
+
try {
|
|
242
|
+
const entradas = memoria.buscar({ collection: 'handoff', tags }).filter(e => e.collection === 'handoff' && (0, memoria_1.threadDaEntrada)(e) === threadId &&
|
|
243
|
+
Object.entries(tags).every(([k, values]) => values.every(v => e.tags[k]?.includes(v))));
|
|
244
|
+
for (const e of entradas) {
|
|
245
|
+
const endereco = `orkmind://handoff/${e.id}`;
|
|
246
|
+
if (vistos.has(endereco))
|
|
247
|
+
continue;
|
|
248
|
+
vistos.add(endereco);
|
|
249
|
+
resolvidos.push({ id: `memoria-${e.id}`, titulo: `Handoff destinado a ${fase}`,
|
|
250
|
+
location: `${(0, memoria_1.arquivoDoHandoff)(e) ?? ''}#tudo`, orkmind: endereco,
|
|
251
|
+
retrieve_when: fase, resolvido: true, motivo: 'ok', via: 'orkmind', conteudo: e.content,
|
|
252
|
+
metodo: 'busca exata por tenant, thread e fase', sha256: String(e.metadata.sha256 ?? ''),
|
|
253
|
+
intervalo: null, detalhe: endereco });
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
catch (erro) {
|
|
257
|
+
falhas.push({ id: 'memoria-busca', titulo: 'Descoberta de handoffs da fase', location: '',
|
|
258
|
+
orkmind: null, retrieve_when: fase, resolvido: false, motivo: 'orkmind-indisponivel', via: null,
|
|
259
|
+
conteudo: '', metodo: '', sha256: '', intervalo: null,
|
|
260
|
+
detalhe: `${memoria.leituraRestrita ? falhaDeConsulta(erro) : 'memory.transport.export'}: busca semantica falhou; resultado nao comprova colecao vazia` });
|
|
261
|
+
}
|
|
262
|
+
}
|
|
186
263
|
if (opcoes.id !== undefined && resolvidos.length === 0 && falhas.length === 0 && adiados.length === 0) {
|
|
187
264
|
throw new Error(`ponteiro "${opcoes.id}" nao existe no handoff da thread ${threadId} ` +
|
|
188
265
|
`(ids: ${handoff.pointers.map((p) => p.id).join(', ') || 'nenhum'})`);
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LIMITE_DE_TESTES = exports.LIMITE_DO_TRECHO = void 0;
|
|
4
|
+
exports.redigirSaida = redigirSaida;
|
|
5
|
+
exports.testesQueCairam = testesQueCairam;
|
|
6
|
+
/**
|
|
7
|
+
* I-37 (D9): a saida de um comando que falhou, pronta para ir ao ledger.
|
|
8
|
+
*
|
|
9
|
+
* O ledger e append-only e cresce para sempre, e ele viaja: CI, pulse, memoria. Por isso o
|
|
10
|
+
* trecho e curto (1024 caracteres, o fim da saida, onde o erro mora) e passa pela mesma
|
|
11
|
+
* disciplina que o nucleo ja aplica ao prompt: credencial em URL e redigida, e se qualquer
|
|
12
|
+
* padrao de segredo casar, o trecho inteiro e descartado. Relatorio que imprime o segredo e
|
|
13
|
+
* um segundo vazamento.
|
|
14
|
+
*/
|
|
15
|
+
const policies_1 = require("./policies");
|
|
16
|
+
const redacao_url_1 = require("./redacao-url");
|
|
17
|
+
exports.LIMITE_DO_TRECHO = 1024;
|
|
18
|
+
exports.LIMITE_DE_TESTES = 10;
|
|
19
|
+
const LIMITE_DO_NOME = 200;
|
|
20
|
+
/** O trecho que vai ao ledger, ou o aviso de que ele foi omitido de proposito. */
|
|
21
|
+
function redigirSaida(saida) {
|
|
22
|
+
const redigida = (0, redacao_url_1.redigirCredenciaisUrl)(saida.trim()).slice(-exports.LIMITE_DO_TRECHO);
|
|
23
|
+
const achado = (0, policies_1.procurarSegredos)(redigida)[0];
|
|
24
|
+
return achado ? `[trecho omitido: padrao ${achado.nome} casou]` : redigida;
|
|
25
|
+
}
|
|
26
|
+
/**
|
|
27
|
+
* Os testes que o runner reportou como reprovados, sem reexecutar nada.
|
|
28
|
+
*
|
|
29
|
+
* Reconhece a saida TAP do `node --test` (`not ok N - nome`, ignorando o sumario de arquivo
|
|
30
|
+
* quando ha subteste) e as linhas `FAIL <arquivo>` de jest e vitest. Ate 10 nomes.
|
|
31
|
+
*/
|
|
32
|
+
function testesQueCairam(saida) {
|
|
33
|
+
const nomes = [];
|
|
34
|
+
for (const linha of saida.split('\n')) {
|
|
35
|
+
const tap = /^\s*not ok \d+ - (.+?)\s*(?:#.*)?$/.exec(linha);
|
|
36
|
+
const fail = /^\s*(?:FAIL|✕|×)\s+(\S.*?)\s*$/.exec(linha);
|
|
37
|
+
const nome = (tap?.[1] ?? fail?.[1] ?? '').trim();
|
|
38
|
+
if (!nome || nomes.includes(nome))
|
|
39
|
+
continue;
|
|
40
|
+
nomes.push(nome.slice(0, LIMITE_DO_NOME));
|
|
41
|
+
if (nomes.length === exports.LIMITE_DE_TESTES)
|
|
42
|
+
break;
|
|
43
|
+
}
|
|
44
|
+
return nomes;
|
|
45
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.redigirCredenciaisUrl = redigirCredenciaisUrl;
|
|
4
|
+
/**
|
|
5
|
+
* Redacao de logs, inclusive userinfo malformado com /?# e varias arrobas.
|
|
6
|
+
* Cada delimitador :// e cada trecho sao percorridos uma vez: O(n) tempo/espaco.
|
|
7
|
+
* Userinfo candidato atravessa :// internos ate o ultimo @ do grupo. Esquemas
|
|
8
|
+
* colados sem separador podem ser senha: nesses casos a URL publica ambigua se
|
|
9
|
+
* perde. Espaco, delimitadores de citacao e , ; )( entre URLs encerram o grupo.
|
|
10
|
+
* URLs publicas sem login: antes de /?# preservam @ no caminho e na query.
|
|
11
|
+
*/
|
|
12
|
+
function redigirCredenciaisUrl(texto) {
|
|
13
|
+
const esquemas = [];
|
|
14
|
+
const delimitador = /:\/\//g;
|
|
15
|
+
const caractere = (c) => (c >= 65 && c <= 90) || (c >= 97 && c <= 122) ||
|
|
16
|
+
(c >= 48 && c <= 57) || c === 43 || c === 45 || c === 46;
|
|
17
|
+
for (let m; (m = delimitador.exec(texto));) {
|
|
18
|
+
let inicio = m.index;
|
|
19
|
+
while (inicio > 0 && caractere(texto.charCodeAt(inicio - 1)))
|
|
20
|
+
inicio--;
|
|
21
|
+
const c = texto.charCodeAt(inicio);
|
|
22
|
+
if ((c >= 65 && c <= 90) || (c >= 97 && c <= 122))
|
|
23
|
+
esquemas.push({ inicio, fim: m.index + 3 });
|
|
24
|
+
}
|
|
25
|
+
const partes = [];
|
|
26
|
+
let copiado = 0;
|
|
27
|
+
let inicioCredencial = null, ultimoAtDoGrupo = -1;
|
|
28
|
+
for (let n = 0; n < esquemas.length; n++) {
|
|
29
|
+
const { fim } = esquemas[n];
|
|
30
|
+
const limite = esquemas[n + 1]?.inicio ?? texto.length;
|
|
31
|
+
let ultimoAt = -1, primeiroAt = -1, separador = limite, colon = -1;
|
|
32
|
+
let terminouTexto = false;
|
|
33
|
+
for (let i = fim; i < limite; i++) {
|
|
34
|
+
const c = texto[i];
|
|
35
|
+
if (/\s|[<>"']/.test(c)) {
|
|
36
|
+
terminouTexto = true;
|
|
37
|
+
break;
|
|
38
|
+
}
|
|
39
|
+
if ('/?#'.includes(c) && separador === limite)
|
|
40
|
+
separador = i;
|
|
41
|
+
if (c === ':' && colon < 0)
|
|
42
|
+
colon = i;
|
|
43
|
+
if (c === '@') {
|
|
44
|
+
ultimoAt = i;
|
|
45
|
+
if (primeiroAt < 0)
|
|
46
|
+
primeiroAt = i;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
const credencial = primeiroAt >= fim && primeiroAt < separador || colon >= fim && colon < separador;
|
|
50
|
+
if (credencial)
|
|
51
|
+
inicioCredencial ??= fim;
|
|
52
|
+
if (ultimoAt >= fim)
|
|
53
|
+
ultimoAtDoGrupo = ultimoAt;
|
|
54
|
+
const separacaoExplicita = terminouTexto || /[,;)(]$/.test(texto.slice(Math.max(fim, limite - 1), limite));
|
|
55
|
+
if (separacaoExplicita || n + 1 === esquemas.length) {
|
|
56
|
+
if (inicioCredencial !== null && ultimoAtDoGrupo >= inicioCredencial) {
|
|
57
|
+
partes.push(texto.slice(copiado, inicioCredencial), '[credencial redigida]@');
|
|
58
|
+
copiado = ultimoAtDoGrupo + 1;
|
|
59
|
+
}
|
|
60
|
+
inicioCredencial = null;
|
|
61
|
+
ultimoAtDoGrupo = -1;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
partes.push(texto.slice(copiado));
|
|
65
|
+
return partes.join('');
|
|
66
|
+
}
|