@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/orkmind.js
CHANGED
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* declarada no manifesto (`memory.database_url_env`). Variavel nao declarada ou vazia
|
|
17
17
|
* nao vira "usa a base padrao": vira degradacao. E o precedente do incidente
|
|
18
18
|
* memory-orkmind, em que a memoria de um produto foi parar na base de outro.
|
|
19
|
-
* 3. GOVERNANCA.
|
|
19
|
+
* 3. GOVERNANCA. `human` exige decision com proveniencia confirmada de human_gate; nunca `mandatory` nem
|
|
20
20
|
* `rule`/`instruction` com prioridade `critical`. Regra critica so nasce de humano
|
|
21
21
|
* autenticado. Aqui isso e checado antes de qualquer chamada, nao depois.
|
|
22
22
|
*/
|
|
@@ -54,19 +54,30 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
54
54
|
};
|
|
55
55
|
})();
|
|
56
56
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
57
|
-
exports.DriverEmMemoria = exports.DriverCliOrkMind = exports.ESQUEMA_ORKMIND = exports.DIMENSOES = exports.COLECOES_DE_GOVERNANCA = exports.COLECOES_DO_ORK = void 0;
|
|
57
|
+
exports.DriverEmMemoria = exports.DriverCliOrkMind = exports.LIMITE_CONSULTA_MAXIMO = exports.LIMITE_CONSULTA_PADRAO = exports.ESQUEMA_ORKMIND = exports.DIMENSOES = exports.COLECOES_DE_GOVERNANCA = exports.COLECOES_DO_ORK = void 0;
|
|
58
58
|
exports.enderecoDeMemoria = enderecoDeMemoria;
|
|
59
59
|
exports.lerEndereco = lerEndereco;
|
|
60
60
|
exports.violacoesDeGovernanca = violacoesDeGovernanca;
|
|
61
61
|
exports.casaTags = casaTags;
|
|
62
|
+
exports.criarEscopoDeLeitura = criarEscopoDeLeitura;
|
|
63
|
+
exports.validarConsultaDelimitada = validarConsultaDelimitada;
|
|
64
|
+
exports.tagsDoEscopo = tagsDoEscopo;
|
|
65
|
+
exports.pertenceAoEscopo = pertenceAoEscopo;
|
|
66
|
+
exports.concluirConsultaDelimitada = concluirConsultaDelimitada;
|
|
62
67
|
exports.entradaDoJson = entradaDoJson;
|
|
68
|
+
exports.camposNativos = camposNativos;
|
|
69
|
+
exports.agenteProspectivoValido = agenteProspectivoValido;
|
|
70
|
+
exports.prospectivaCompativel = prospectivaCompativel;
|
|
63
71
|
exports.configDoManifesto = configDoManifesto;
|
|
64
72
|
exports.resolverRegime = resolverRegime;
|
|
65
73
|
exports.filtrarPorTags = filtrarPorTags;
|
|
66
74
|
exports.relativoDaRaiz = relativoDaRaiz;
|
|
67
75
|
const path = __importStar(require("node:path"));
|
|
76
|
+
const fs = __importStar(require("node:fs"));
|
|
68
77
|
const node_child_process_1 = require("node:child_process");
|
|
69
|
-
|
|
78
|
+
const node_crypto_1 = require("node:crypto");
|
|
79
|
+
const node_util_1 = require("node:util");
|
|
80
|
+
/** As colecoes do OrkMind que o `ork` grava (guia: docs/guias/memoria-e-handoff.md). */
|
|
70
81
|
exports.COLECOES_DO_ORK = [
|
|
71
82
|
'decision',
|
|
72
83
|
'handoff',
|
|
@@ -112,6 +123,22 @@ function lerEndereco(endereco) {
|
|
|
112
123
|
*/
|
|
113
124
|
function violacoesDeGovernanca(entrada) {
|
|
114
125
|
const problemas = [];
|
|
126
|
+
if ('scope' in entrada && entrada.scope !== 'project')
|
|
127
|
+
problemas.push('scope fora do contrato project');
|
|
128
|
+
if (['orkastery_identity', 'orkastery_identity_version', 'orkastery_prospective'].some(k => k in entrada.metadata))
|
|
129
|
+
problemas.push('metadata de identidade reservada');
|
|
130
|
+
if (entrada.mandatory)
|
|
131
|
+
problemas.push('mandatory proibido');
|
|
132
|
+
if (entrada.source && !['agent', 'human'].includes(entrada.source))
|
|
133
|
+
problemas.push('source proibido');
|
|
134
|
+
if (entrada.source === 'human') {
|
|
135
|
+
const p = entrada.metadata.proveniencia;
|
|
136
|
+
if (entrada.collection !== 'decision' || entrada.priority === 'critical' ||
|
|
137
|
+
!p || p.tipo !== 'human_gate' || p.source !== 'human' || p.confirmado !== true ||
|
|
138
|
+
!['autor', 'evento', 'evidencia', 'sha256'].every(k => typeof p[k] === 'string' && p[k])) {
|
|
139
|
+
problemas.push('source human exige decision nao critica e proveniencia de human_gate confirmado');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
115
142
|
if (!exports.COLECOES_DO_ORK.includes(entrada.collection)) {
|
|
116
143
|
problemas.push(`colecao "${entrada.collection}" fora do escopo do ork (${exports.COLECOES_DO_ORK.join(', ')})`);
|
|
117
144
|
}
|
|
@@ -153,6 +180,67 @@ function casaTags(entrada, consulta) {
|
|
|
153
180
|
}
|
|
154
181
|
return todasCasaram || (entrada.mandatory && algumaCasou);
|
|
155
182
|
}
|
|
183
|
+
exports.LIMITE_CONSULTA_PADRAO = 100;
|
|
184
|
+
exports.LIMITE_CONSULTA_MAXIMO = 1000;
|
|
185
|
+
function objetoDeConsulta(x) {
|
|
186
|
+
return !!x && typeof x === 'object' && !Array.isArray(x);
|
|
187
|
+
}
|
|
188
|
+
function textoDeConsulta(x, max) {
|
|
189
|
+
return typeof x === 'string' && x.length > 0 && x.length <= max && !/[\x00-\x1f\x7f]/.test(x);
|
|
190
|
+
}
|
|
191
|
+
/** Delimitacao de leitura, nao credencial. O chamador operacional deriva tenant do manifesto. */
|
|
192
|
+
function criarEscopoDeLeitura(manifesto, thread) {
|
|
193
|
+
const tenant = manifesto.memory.tenant || manifesto.project.name;
|
|
194
|
+
return validarConsultaDelimitada({ collection: 'handoff', escopo: { tenant, thread },
|
|
195
|
+
tags: {}, limite: exports.LIMITE_CONSULTA_PADRAO }).escopo;
|
|
196
|
+
}
|
|
197
|
+
/** Copia validada impede mutacao do escopo/tags entre validar e transportar. */
|
|
198
|
+
function validarConsultaDelimitada(bruto) {
|
|
199
|
+
const falhar = () => { throw Error('memory.query.invalid'); };
|
|
200
|
+
if (!objetoDeConsulta(bruto) || Object.keys(bruto).some(k => !['collection', 'escopo', 'tags', 'limite'].includes(k)))
|
|
201
|
+
return falhar();
|
|
202
|
+
const { collection, escopo, tags, limite } = bruto;
|
|
203
|
+
if (!exports.COLECOES_DO_ORK.includes(collection) || !objetoDeConsulta(escopo) ||
|
|
204
|
+
Object.keys(escopo).some(k => !['tenant', 'thread'].includes(k)) ||
|
|
205
|
+
!textoDeConsulta(escopo.tenant, 128) || !textoDeConsulta(escopo.thread, 128) ||
|
|
206
|
+
!/^[a-z0-9][a-z0-9_-]*$/.test(escopo.thread) || !Number.isInteger(limite) ||
|
|
207
|
+
Number(limite) < 1 || Number(limite) > exports.LIMITE_CONSULTA_MAXIMO || !objetoDeConsulta(tags))
|
|
208
|
+
return falhar();
|
|
209
|
+
const copia = {};
|
|
210
|
+
for (const [k, values] of Object.entries(tags)) {
|
|
211
|
+
if (!exports.DIMENSOES.includes(k) || !Array.isArray(values) || values.length > 32 ||
|
|
212
|
+
!values.every(v => textoDeConsulta(v, 256)))
|
|
213
|
+
return falhar();
|
|
214
|
+
if ((k === 'project' && values.some(v => v !== escopo.tenant)) ||
|
|
215
|
+
(k === 'situation' && values.some(v => v.startsWith('thread:') && v !== 'thread:' + escopo.thread))) {
|
|
216
|
+
throw Error('memory.query.scope-conflict');
|
|
217
|
+
}
|
|
218
|
+
copia[k] = [...values];
|
|
219
|
+
Object.freeze(copia[k]);
|
|
220
|
+
}
|
|
221
|
+
return Object.freeze({ collection: collection,
|
|
222
|
+
escopo: Object.freeze({ tenant: escopo.tenant, thread: escopo.thread }),
|
|
223
|
+
tags: Object.freeze(copia), limite: Number(limite) });
|
|
224
|
+
}
|
|
225
|
+
/** Apenas as duas fronteiras obrigatorias: filtros extras perderiam mandatory valida. */
|
|
226
|
+
function tagsDoEscopo(escopo) {
|
|
227
|
+
return { project: [escopo.tenant], situation: ['thread:' + escopo.thread] };
|
|
228
|
+
}
|
|
229
|
+
function pertenceAoEscopo(e, q) {
|
|
230
|
+
return e.collection === q.collection && Object.entries(tagsDoEscopo(q.escopo))
|
|
231
|
+
.every(([k, values]) => values.every(v => e.tags[k]?.includes(v)));
|
|
232
|
+
}
|
|
233
|
+
/** Saturacao antes de filtros locais, inclusive procura por id: ausencia precisa ser provada. */
|
|
234
|
+
function concluirConsultaDelimitada(dados, q) {
|
|
235
|
+
if (!Array.isArray(dados))
|
|
236
|
+
throw Error('memory.query.response-invalid');
|
|
237
|
+
if (dados.length >= q.limite)
|
|
238
|
+
throw Error('memory.query.window-saturated');
|
|
239
|
+
if (dados.some(e => !pertenceAoEscopo(e, q)))
|
|
240
|
+
throw Error('memory.query.scope-violation');
|
|
241
|
+
return filtrarPorTags(dados, { collection: q.collection, tags: { ...q.tags,
|
|
242
|
+
project: [q.escopo.tenant] } });
|
|
243
|
+
}
|
|
156
244
|
/** Normaliza uma entrada como o OrkMind a devolve (`orkmind export`). */
|
|
157
245
|
function entradaDoJson(bruto) {
|
|
158
246
|
if (!bruto || typeof bruto !== 'object')
|
|
@@ -183,6 +271,10 @@ function entradaDoJson(bruto) {
|
|
|
183
271
|
metadata: o.metadata && typeof o.metadata === 'object' && !Array.isArray(o.metadata)
|
|
184
272
|
? o.metadata
|
|
185
273
|
: {},
|
|
274
|
+
parent_id: typeof o.parent_id === 'string' ? o.parent_id : null,
|
|
275
|
+
author_id: typeof o.author_id === 'string' ? o.author_id : null,
|
|
276
|
+
visibility: typeof o.visibility === 'string' ? o.visibility : undefined,
|
|
277
|
+
protected: typeof o.protected === 'boolean' ? o.protected : undefined,
|
|
186
278
|
criadaEm: typeof o.created_at === 'string' ? o.created_at : '',
|
|
187
279
|
};
|
|
188
280
|
}
|
|
@@ -201,101 +293,248 @@ class DriverCliOrkMind {
|
|
|
201
293
|
this.config = config;
|
|
202
294
|
this.nome = `cli:${config.cli}`;
|
|
203
295
|
}
|
|
204
|
-
|
|
205
|
-
const
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
296
|
+
python() {
|
|
297
|
+
const cli = this.config.cli;
|
|
298
|
+
const candidatos = cli.includes(path.sep) ? [path.resolve(cli)] :
|
|
299
|
+
(process.env.PATH ?? '').split(path.delimiter).map(p => path.join(p, cli));
|
|
300
|
+
const executavel = candidatos.find(p => {
|
|
301
|
+
try {
|
|
302
|
+
fs.accessSync(p, fs.constants.X_OK);
|
|
303
|
+
return fs.statSync(p).isFile();
|
|
304
|
+
}
|
|
305
|
+
catch {
|
|
306
|
+
return false;
|
|
307
|
+
}
|
|
211
308
|
});
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
309
|
+
if (!executavel)
|
|
310
|
+
throw new Error('cli.ausente: executavel OrkMind nao encontrado');
|
|
311
|
+
const linha = fs.readFileSync(executavel, 'utf8').split('\n')[0];
|
|
312
|
+
const match = /^#!(\S+)(?:\s+(\S+))?\s*$/.exec(linha);
|
|
313
|
+
if (!match)
|
|
314
|
+
throw new Error('memory.transport.interpreter: shebang nao suportado');
|
|
315
|
+
return match[2] ? [match[1], match[2]] : [match[1]];
|
|
316
|
+
}
|
|
317
|
+
contemSegredo(valor) {
|
|
318
|
+
const segredo = this.config.dsn;
|
|
319
|
+
const proibidos = [segredo];
|
|
320
|
+
try {
|
|
321
|
+
const senha = new URL(segredo).password;
|
|
322
|
+
if (senha.length >= 12)
|
|
323
|
+
proibidos.push(senha, decodeURIComponent(senha));
|
|
324
|
+
}
|
|
325
|
+
catch { /* DSN opaca */ }
|
|
326
|
+
const examinar = (x) => typeof x === 'string'
|
|
327
|
+
? proibidos.some(p => p && x.includes(p))
|
|
328
|
+
: !!x && typeof x === 'object' && Object.entries(x).some(([k, v]) => examinar(k) || examinar(v));
|
|
329
|
+
return examinar(valor);
|
|
330
|
+
}
|
|
331
|
+
rodar(pedido) {
|
|
332
|
+
if (!this.config.dsn.trim())
|
|
333
|
+
throw new Error('dsn.env-ausente');
|
|
334
|
+
if (this.contemSegredo(pedido))
|
|
335
|
+
throw new Error('memory.transport.secret: conteudo recusado');
|
|
336
|
+
const [python, ...prefixo] = this.python();
|
|
337
|
+
const ponte = [path.join(__dirname, '../assets/orkmind_bridge.py'),
|
|
338
|
+
path.join(__dirname, '../../assets/orkmind_bridge.py')].find(p => fs.existsSync(p));
|
|
339
|
+
if (!ponte)
|
|
340
|
+
throw new Error('memory.transport.bridge: ponte ausente no pacote');
|
|
341
|
+
const env = { ORKMIND_DATABASE_URL: this.config.dsn, PYTHONDONTWRITEBYTECODE: '1' };
|
|
342
|
+
for (const nome of ['PATH', 'HOME', 'LANG', 'LC_ALL', 'SYSTEMROOT']) {
|
|
343
|
+
if (process.env[nome])
|
|
344
|
+
env[nome] = process.env[nome];
|
|
345
|
+
}
|
|
346
|
+
const r = (0, node_child_process_1.spawnSync)(python, [...prefixo, ponte], {
|
|
347
|
+
encoding: 'utf8', input: JSON.stringify(pedido), timeout: this.config.timeoutMs,
|
|
348
|
+
maxBuffer: 32 * 1024 * 1024, env,
|
|
349
|
+
});
|
|
350
|
+
// Nunca reutilizar stdout, stderr, error.message ou causa de uma falha do filho.
|
|
351
|
+
if (r.error) {
|
|
352
|
+
const code = r.error.code;
|
|
353
|
+
throw new Error(code === 'ETIMEDOUT' ? 'memory.transport.timeout' :
|
|
354
|
+
code === 'ENOENT' ? 'cli.ausente: interpretador nao encontrado' : 'memory.transport.spawn');
|
|
355
|
+
}
|
|
356
|
+
if (r.status !== 0) {
|
|
357
|
+
let code = '';
|
|
358
|
+
try {
|
|
359
|
+
code = JSON.parse(r.stdout)?.error;
|
|
360
|
+
}
|
|
361
|
+
catch { /* nenhum detalhe do filho */ }
|
|
362
|
+
throw new Error(['memory.schema.absent', 'memory.legacy.provenance-collision', 'memory.prospective.marker-invalid', 'memory.native.schema-mismatch', 'memory.query.invalid', 'memory.query.window-saturated', 'memory.query.scope-violation'].includes(code) ? code : 'memory.transport.failed');
|
|
363
|
+
}
|
|
364
|
+
let json;
|
|
365
|
+
try {
|
|
366
|
+
json = JSON.parse(r.stdout);
|
|
367
|
+
}
|
|
368
|
+
catch {
|
|
369
|
+
throw new Error('memory.transport.json');
|
|
370
|
+
}
|
|
371
|
+
if (this.contemSegredo(json))
|
|
372
|
+
throw new Error('memory.transport.secret: resposta recusada');
|
|
373
|
+
return json;
|
|
217
374
|
}
|
|
218
375
|
disponivel() {
|
|
219
376
|
if (this.saude)
|
|
220
377
|
return this.saude;
|
|
221
|
-
|
|
222
|
-
this.
|
|
223
|
-
|
|
378
|
+
try {
|
|
379
|
+
this.contagens();
|
|
380
|
+
this.saude = { ok: true, detalhe: 'ponte OrkMind respondeu; embeddings desativados' };
|
|
381
|
+
}
|
|
382
|
+
catch (e) {
|
|
383
|
+
this.saude = { ok: false, detalhe: e.message };
|
|
224
384
|
}
|
|
225
|
-
const r = this.rodar(['stats']);
|
|
226
|
-
this.saude = r.ok
|
|
227
|
-
? { ok: true, detalhe: r.stdout.split('\n')[0]?.trim() || 'OrkMind respondeu' }
|
|
228
|
-
: { ok: false, detalhe: (r.stderr || r.stdout).trim().split('\n').slice(-1)[0] ?? 'sem detalhe' };
|
|
229
385
|
return this.saude;
|
|
230
386
|
}
|
|
231
|
-
|
|
232
|
-
const
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
'--content',
|
|
237
|
-
entrada.content,
|
|
238
|
-
'--tags',
|
|
239
|
-
JSON.stringify(entrada.tags),
|
|
240
|
-
'--priority',
|
|
241
|
-
entrada.priority,
|
|
242
|
-
'--scope',
|
|
243
|
-
'project',
|
|
244
|
-
// `source: agent` sempre: e assim que o OrkMind sabe que isto nao veio de humano.
|
|
245
|
-
'--source',
|
|
246
|
-
'agent',
|
|
247
|
-
// `--dedupe` torna a gravacao idempotente por content_hash: reprocessar um MASTER ou
|
|
248
|
-
// reexportar um handoff nao duplica memoria.
|
|
249
|
-
'--dedupe',
|
|
250
|
-
'--json',
|
|
251
|
-
];
|
|
252
|
-
const r = this.rodar(args);
|
|
253
|
-
if (!r.ok) {
|
|
254
|
-
return {
|
|
255
|
-
ok: false,
|
|
256
|
-
id: null,
|
|
257
|
-
duplicada: false,
|
|
258
|
-
collection: entrada.collection,
|
|
259
|
-
detalhe: (r.stderr || r.stdout).trim().split('\n').slice(-1)[0] ?? 'falha sem detalhe',
|
|
260
|
-
};
|
|
387
|
+
contagens() {
|
|
388
|
+
const r = this.rodar({ op: 'stats' });
|
|
389
|
+
if (!r || Array.isArray(r) || typeof r !== 'object' ||
|
|
390
|
+
!Object.values(r).every(v => Number.isInteger(v) && Number(v) >= 0)) {
|
|
391
|
+
throw new Error('memory.transport.stats');
|
|
261
392
|
}
|
|
262
|
-
|
|
393
|
+
return r;
|
|
394
|
+
}
|
|
395
|
+
adicionar(entrada) {
|
|
263
396
|
try {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
};
|
|
397
|
+
if (violacoesDeGovernanca(entrada).length)
|
|
398
|
+
throw new Error('memory.governance');
|
|
399
|
+
const r = this.rodar({ op: 'add', entrada });
|
|
400
|
+
if (!r || r.ok !== true || typeof r.id !== 'string' || !r.id || r.collection !== entrada.collection) {
|
|
401
|
+
throw new Error('memory.transport.receipt');
|
|
402
|
+
}
|
|
403
|
+
return r;
|
|
272
404
|
}
|
|
273
|
-
catch {
|
|
274
|
-
return {
|
|
275
|
-
ok: false,
|
|
276
|
-
id: null,
|
|
277
|
-
duplicada: false,
|
|
278
|
-
collection: entrada.collection,
|
|
279
|
-
detalhe: `saida do OrkMind nao e JSON: ${linha.slice(0, 120)}`,
|
|
280
|
-
};
|
|
405
|
+
catch (e) {
|
|
406
|
+
return { ok: false, id: null, duplicada: false, collection: entrada.collection, detalhe: e.message };
|
|
281
407
|
}
|
|
282
408
|
}
|
|
283
409
|
exportar(colecao) {
|
|
284
|
-
const
|
|
285
|
-
if (!
|
|
286
|
-
|
|
410
|
+
const dados = this.rodar({ op: 'export', collection: colecao });
|
|
411
|
+
if (!Array.isArray(dados))
|
|
412
|
+
throw new Error('memory.transport.export');
|
|
413
|
+
return dados.map(d => {
|
|
414
|
+
const e = entradaDoJson(d);
|
|
415
|
+
if (!e || e.collection !== colecao)
|
|
416
|
+
throw new Error('memory.transport.entry');
|
|
417
|
+
return e;
|
|
418
|
+
});
|
|
419
|
+
}
|
|
420
|
+
consultar(consulta) {
|
|
421
|
+
const q = validarConsultaDelimitada(consulta);
|
|
422
|
+
const dados = this.rodar({ op: 'query', collection: q.collection,
|
|
423
|
+
tags: tagsDoEscopo(q.escopo), limit: q.limite });
|
|
424
|
+
if (!Array.isArray(dados))
|
|
425
|
+
throw Error('memory.query.response-invalid');
|
|
426
|
+
// A janela inteira precede normalizacao e filtros; nao provar ausencia numa pagina cheia.
|
|
427
|
+
if (dados.length >= q.limite)
|
|
428
|
+
throw Error('memory.query.window-saturated');
|
|
429
|
+
const entradas = dados.map(d => {
|
|
430
|
+
const e = entradaDoJson(d);
|
|
431
|
+
if (!e)
|
|
432
|
+
throw Error('memory.query.response-invalid');
|
|
433
|
+
return e;
|
|
434
|
+
});
|
|
435
|
+
return concluirConsultaDelimitada(entradas, q);
|
|
436
|
+
}
|
|
437
|
+
/** Readback de manutencao. O recall continua usando export governado. */
|
|
438
|
+
recuperar(colecao, id) {
|
|
439
|
+
const dado = this.rodar({ op: 'get', collection: colecao, id });
|
|
440
|
+
if (dado === null)
|
|
441
|
+
return null;
|
|
442
|
+
const e = entradaDoJson(dado);
|
|
443
|
+
if (!e || e.id !== id || e.collection !== colecao)
|
|
444
|
+
throw new Error('memory.transport.readback');
|
|
445
|
+
return e;
|
|
446
|
+
}
|
|
447
|
+
submeterHandoff(pedido) {
|
|
287
448
|
try {
|
|
288
|
-
const
|
|
289
|
-
if (!
|
|
290
|
-
|
|
291
|
-
|
|
449
|
+
const r = this.rodar({ op: 'handoff', pedido });
|
|
450
|
+
if (!r || r.ok !== true || !r.id || !r.package_id || !r.package_entry_id ||
|
|
451
|
+
!r.session_entry_id || r.readback !== true)
|
|
452
|
+
throw new Error('memory.g3.rejected');
|
|
453
|
+
return r;
|
|
292
454
|
}
|
|
293
|
-
catch {
|
|
294
|
-
return
|
|
455
|
+
catch (e) {
|
|
456
|
+
return { ok: false, id: null, duplicada: false, collection: 'handoff', detalhe: e.message };
|
|
295
457
|
}
|
|
296
458
|
}
|
|
297
459
|
}
|
|
298
460
|
exports.DriverCliOrkMind = DriverCliOrkMind;
|
|
461
|
+
function tagsFixasDaDecisao(tags) {
|
|
462
|
+
const fases = new Set(['GOAL', 'PLAN', 'GO', 'CHECK', 'SHIP', 'MASTER']);
|
|
463
|
+
return Object.fromEntries(Object.entries(tags).filter(([k]) => k !== 'agent').map(([k, values]) => [k, [...new Set(values.filter(v => !(k === 'skill' && fases.has(v)) &&
|
|
464
|
+
!(k === 'situation' && v.startsWith('fase:') && fases.has(v.slice(5)))))].sort()]).filter(([, values]) => values.length));
|
|
465
|
+
}
|
|
466
|
+
/** Mesma serializacao JSON canonica e fingerprint da ponte Python. */
|
|
467
|
+
function jsonDeIdentidade(value) {
|
|
468
|
+
if (Array.isArray(value))
|
|
469
|
+
return '[' + value.map(jsonDeIdentidade).join(',') + ']';
|
|
470
|
+
if (value && typeof value === 'object')
|
|
471
|
+
return '{' + Object.entries(value).filter(([, v]) => v !== undefined)
|
|
472
|
+
.sort(([a], [b]) => {
|
|
473
|
+
const aa = Array.from(a, c => c.codePointAt(0)), bb = Array.from(b, c => c.codePointAt(0));
|
|
474
|
+
for (let i = 0; i < Math.min(aa.length, bb.length); i++)
|
|
475
|
+
if (aa[i] !== bb[i])
|
|
476
|
+
return aa[i] - bb[i];
|
|
477
|
+
return aa.length - bb.length;
|
|
478
|
+
})
|
|
479
|
+
.map(([k, v]) => JSON.stringify(k) + ':' + jsonDeIdentidade(v)).join(',') + '}';
|
|
480
|
+
return JSON.stringify(value);
|
|
481
|
+
}
|
|
482
|
+
const fingerprint = (value) => (0, node_crypto_1.createHash)('sha256').update(jsonDeIdentidade(value)).digest('hex');
|
|
483
|
+
/** Domicilio unico de campos; a ponte verifica o contrato contra a biblioteca instalada antes de usa-la. */
|
|
484
|
+
function camposNativos() {
|
|
485
|
+
try {
|
|
486
|
+
const arquivo = [path.join(__dirname, '../assets/orkmind-native-schema.json'),
|
|
487
|
+
path.join(__dirname, '../../assets/orkmind-native-schema.json')].find(p => fs.existsSync(p));
|
|
488
|
+
if (!arquivo)
|
|
489
|
+
throw Error('missing native schema');
|
|
490
|
+
const esquema = JSON.parse(fs.readFileSync(arquivo, 'utf8'));
|
|
491
|
+
if (esquema.schema !== 'ork.native-entry-fields/v1' || !Array.isArray(esquema.fields) ||
|
|
492
|
+
esquema.fields.some((k) => typeof k !== 'string') ||
|
|
493
|
+
new Set(esquema.fields).size !== esquema.fields.length)
|
|
494
|
+
throw Error('invalid native schema');
|
|
495
|
+
return Object.freeze(esquema.fields);
|
|
496
|
+
}
|
|
497
|
+
catch {
|
|
498
|
+
throw Error('memory.native.schema-mismatch');
|
|
499
|
+
}
|
|
500
|
+
}
|
|
501
|
+
function agenteProspectivoValido(value) {
|
|
502
|
+
return typeof value === 'string' && /^[a-z0-9.-]+:[a-z0-9.-]+$/.test(value) && !/human/i.test(value);
|
|
503
|
+
}
|
|
504
|
+
/** Fingerprint de integridade do recibo, nunca autenticacao da autoria historica. */
|
|
505
|
+
function prospectivaCompativel(old, entrada, metadata) {
|
|
506
|
+
const campos = camposNativos();
|
|
507
|
+
try {
|
|
508
|
+
const p = old.metadata.orkastery_prospective;
|
|
509
|
+
if (!p || p.schema !== 'ork.prospective-origin/v1' || p.historicalOrigin !== 'unknown' || p.source !== 'agent' ||
|
|
510
|
+
!/^[a-f0-9]{64}$/.test(p.receiptSha256) || !/^[a-f0-9]{40}$/.test(p.revision) ||
|
|
511
|
+
!agenteProspectivoValido(p.agent) || p.entryId !== old.id ||
|
|
512
|
+
(0, node_crypto_1.createHash)('sha256').update(p.originalJson).digest('hex') !== p.originalSha256)
|
|
513
|
+
return false;
|
|
514
|
+
const original = JSON.parse(p.originalJson);
|
|
515
|
+
if (Object.keys(original).length !== campos.length || campos.some(k => !(k in original)))
|
|
516
|
+
return false;
|
|
517
|
+
const tags = Object.fromEntries([...new Set([...Object.keys(original.tags), ...Object.keys(entrada.tags)])].map(k => [k, [...new Set([...(original.tags[k] ?? []), ...(entrada.tags[k] ?? [])])].sort()]));
|
|
518
|
+
return old.collection === 'rule' && old.source === 'agent' && old.priority === 'high' && old.mandatory === false &&
|
|
519
|
+
old.protected === false && old.visibility === 'private' && original.visibility === 'private' &&
|
|
520
|
+
original.id === old.id && original.content === old.content && original.source === 'agent' &&
|
|
521
|
+
original.priority === 'high' && original.mandatory === false && original.protected === false &&
|
|
522
|
+
(old.author_id ?? null) === original.author_id && old.scope === original.scope && old.scope === ('scope' in entrada ? entrada.scope : 'project') &&
|
|
523
|
+
(0, node_util_1.isDeepStrictEqual)(old.tags, tags) && (0, node_util_1.isDeepStrictEqual)(old.metadata, { ...metadata, orkastery_prospective: p });
|
|
524
|
+
}
|
|
525
|
+
catch {
|
|
526
|
+
return false;
|
|
527
|
+
}
|
|
528
|
+
}
|
|
529
|
+
const semIdentidade = (metadata) => Object.fromEntries(Object.entries(metadata)
|
|
530
|
+
.filter(([k]) => !['orkastery_identity', 'orkastery_identity_version'].includes(k)));
|
|
531
|
+
function identidadeLegadaComprovada(entry) {
|
|
532
|
+
if ('orkastery_identity_version' in entry.metadata)
|
|
533
|
+
return false;
|
|
534
|
+
const base = { collection: entry.collection, content: entry.content, tags: entry.tags,
|
|
535
|
+
priority: entry.priority, metadata: semIdentidade(entry.metadata) };
|
|
536
|
+
return [false, true].some(source => [false, true].some(mandatory => fingerprint({ ...base, ...(source ? { source: entry.source } : {}), ...(mandatory ? { mandatory: false } : {}) }) === entry.metadata.orkastery_identity));
|
|
537
|
+
}
|
|
299
538
|
/**
|
|
300
539
|
* Driver em memoria, usado pelos testes e por `--dry-run`.
|
|
301
540
|
*
|
|
@@ -319,6 +558,8 @@ class DriverEmMemoria {
|
|
|
319
558
|
: { ok: false, detalhe: 'driver em memoria desligado' };
|
|
320
559
|
}
|
|
321
560
|
adicionar(entrada) {
|
|
561
|
+
if (violacoesDeGovernanca(entrada).length)
|
|
562
|
+
return { ok: false, id: null, duplicada: false, collection: entrada.collection, detalhe: 'memory.governance' };
|
|
322
563
|
if (!this.ligado) {
|
|
323
564
|
return {
|
|
324
565
|
ok: false,
|
|
@@ -328,15 +569,36 @@ class DriverEmMemoria {
|
|
|
328
569
|
detalhe: 'driver em memoria desligado',
|
|
329
570
|
};
|
|
330
571
|
}
|
|
331
|
-
const
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
572
|
+
const evolutiva = entrada.collection === 'decision' && (entrada.source ?? 'agent') === 'agent';
|
|
573
|
+
const falha = { ok: false, id: null, duplicada: false, collection: entrada.collection, detalhe: 'memory.transport.failed' };
|
|
574
|
+
if (evolutiva && (entrada.tags.project?.length !== 1 || !entrada.tags.project[0]))
|
|
575
|
+
return falha;
|
|
576
|
+
const identidade = fingerprint(evolutiva ? { collection: entrada.collection, content: entrada.content,
|
|
577
|
+
priority: entrada.priority, metadata: entrada.metadata, source: 'agent', tags: tagsFixasDaDecisao(entrada.tags) } : entrada);
|
|
578
|
+
const metadata = { ...entrada.metadata, orkastery_identity: identidade,
|
|
579
|
+
...(evolutiva ? { orkastery_identity_version: 2 } : {}) };
|
|
580
|
+
const old = this.entradas.find(e => e.collection === entrada.collection && e.content === entrada.content);
|
|
581
|
+
if (old) {
|
|
582
|
+
if ('orkastery_prospective' in old.metadata) {
|
|
583
|
+
if (!prospectivaCompativel(old, entrada, metadata))
|
|
584
|
+
return { ...falha, detalhe: 'memory.prospective.marker-invalid' };
|
|
585
|
+
return { ok: true, id: old.id, duplicada: true, collection: old.collection, detalhe: 'duplicate; historical-origin-unknown' };
|
|
586
|
+
}
|
|
587
|
+
if (!old.metadata.orkastery_identity)
|
|
588
|
+
return { ...falha, detalhe: 'memory.legacy.provenance-collision' };
|
|
589
|
+
if (old.source !== (entrada.source ?? 'agent') || old.priority !== entrada.priority || old.mandatory || old.scope !== 'project')
|
|
590
|
+
return falha;
|
|
591
|
+
if (evolutiva) {
|
|
592
|
+
const reconhecida = (old.metadata.orkastery_identity_version === 2 && old.metadata.orkastery_identity === identidade) || identidadeLegadaComprovada(old);
|
|
593
|
+
if (!reconhecida || !(0, node_util_1.isDeepStrictEqual)(semIdentidade(old.metadata), entrada.metadata) ||
|
|
594
|
+
!(0, node_util_1.isDeepStrictEqual)(tagsFixasDaDecisao(old.tags), tagsFixasDaDecisao(entrada.tags)))
|
|
595
|
+
return falha;
|
|
596
|
+
old.tags = Object.fromEntries([...new Set([...Object.keys(old.tags), ...Object.keys(entrada.tags)])].map(k => [k, [...new Set([...(old.tags[k] ?? []), ...(entrada.tags[k] ?? [])])].sort()]));
|
|
597
|
+
old.metadata = metadata;
|
|
598
|
+
}
|
|
599
|
+
else if (!(0, node_util_1.isDeepStrictEqual)(old.metadata, metadata) || !(0, node_util_1.isDeepStrictEqual)(old.tags, entrada.tags))
|
|
600
|
+
return falha;
|
|
601
|
+
return { ok: true, id: old.id, duplicada: true, collection: entrada.collection, detalhe: 'duplicate' };
|
|
340
602
|
}
|
|
341
603
|
this.contador += 1;
|
|
342
604
|
const nova = {
|
|
@@ -347,8 +609,8 @@ class DriverEmMemoria {
|
|
|
347
609
|
priority: entrada.priority,
|
|
348
610
|
mandatory: false,
|
|
349
611
|
scope: 'project',
|
|
350
|
-
source: 'agent',
|
|
351
|
-
metadata
|
|
612
|
+
source: entrada.source ?? 'agent',
|
|
613
|
+
metadata,
|
|
352
614
|
criadaEm: new Date().toISOString(),
|
|
353
615
|
};
|
|
354
616
|
this.entradas.push(nova);
|
|
@@ -362,9 +624,46 @@ class DriverEmMemoria {
|
|
|
362
624
|
}
|
|
363
625
|
exportar(colecao) {
|
|
364
626
|
if (!this.ligado)
|
|
365
|
-
|
|
627
|
+
throw new Error('memory.transport.unavailable');
|
|
366
628
|
return this.entradas.filter((e) => e.collection === colecao);
|
|
367
629
|
}
|
|
630
|
+
consultar(consulta) {
|
|
631
|
+
const q = validarConsultaDelimitada(consulta);
|
|
632
|
+
if (!this.ligado)
|
|
633
|
+
throw new Error('memory.transport.unavailable');
|
|
634
|
+
// Duplo de selecao na origem; nao chama o export amplo nem implementa ACL.
|
|
635
|
+
const pagina = this.entradas.filter(e => pertenceAoEscopo(e, q)).slice(0, q.limite);
|
|
636
|
+
return concluirConsultaDelimitada(pagina, q);
|
|
637
|
+
}
|
|
638
|
+
recuperar(colecao, id) {
|
|
639
|
+
return this.exportar(colecao).find(e => e.id === id) ?? null;
|
|
640
|
+
}
|
|
641
|
+
contagens() {
|
|
642
|
+
return this.entradas.reduce((r, e) => { r[e.collection] = (r[e.collection] ?? 0) + 1; return r; }, {});
|
|
643
|
+
}
|
|
644
|
+
/** Duplo de transporte para o CI. Nao constitui prova de validacao G3. */
|
|
645
|
+
submeterHandoff(p) {
|
|
646
|
+
const existente = this.entradas.find(e => e.collection === 'handoff' && e.metadata.orkastery_identity === p.identidade);
|
|
647
|
+
if (existente)
|
|
648
|
+
return { ok: true, id: existente.id, duplicada: true, collection: 'handoff',
|
|
649
|
+
detalhe: 'duplo G3', session_entry_id: existente.parent_id, package_entry_id: String(existente.metadata.package_entry_id),
|
|
650
|
+
package_id: String(existente.metadata.package_id), readback: true };
|
|
651
|
+
const inserir = (collection, content, metadata, parent_id) => {
|
|
652
|
+
const e = { id: `mem-${++this.contador}`, collection, content, metadata, parent_id,
|
|
653
|
+
tags: p.tags, source: 'agent', priority: 'high', mandatory: false, scope: 'project', criadaEm: new Date().toISOString() };
|
|
654
|
+
this.entradas.push(e);
|
|
655
|
+
return e;
|
|
656
|
+
};
|
|
657
|
+
const sessao = this.entradas.find(e => e.collection === 'session' && e.metadata.session_id === p.sessionId) ??
|
|
658
|
+
inserir('session', `Sessao ${p.sessionId}`, { session_id: p.sessionId });
|
|
659
|
+
const packageId = `package-${this.contador + 1}`;
|
|
660
|
+
const meta = { ...p.metadata, orkastery_identity: p.identidade, package_id: packageId, session_id: p.sessionId,
|
|
661
|
+
origin: p.origem, destination: p.destino };
|
|
662
|
+
const pacote = inserir('semantic_log', JSON.stringify(p.payload), meta, sessao.id);
|
|
663
|
+
const handoff = inserir('handoff', String(p.payload.progresso), { ...meta, package_entry_id: pacote.id }, sessao.id);
|
|
664
|
+
return { ok: true, id: handoff.id, duplicada: false, collection: 'handoff', detalhe: 'duplo de transporte G3',
|
|
665
|
+
session_entry_id: sessao.id, package_entry_id: pacote.id, package_id: packageId, readback: true };
|
|
666
|
+
}
|
|
368
667
|
/** So para os testes: injeta uma entrada que o `ork` nunca gravaria (humano, mandatory). */
|
|
369
668
|
semear(entrada) {
|
|
370
669
|
this.entradas.push(entrada);
|