@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.
Files changed (110) hide show
  1. package/adapters/claude-code/agents/ork-master.md +2 -0
  2. package/adapters/claude-code/commands/master.md +8 -0
  3. package/adapters/claude-code/commands/ork.md +9 -0
  4. package/adapters/codex/skills/ork/SKILL.md +18 -3
  5. package/adapters/hermes/README.md +1 -0
  6. package/adapters/hermes/bin/ork-network-status.sh +10 -0
  7. package/adapters/hermes/hermes.plugin.json +1 -0
  8. package/adapters/hermes/hitl-ingress/__init__.py +2 -2
  9. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +6 -5
  10. package/adapters/openclaw/README.md +24 -4
  11. package/adapters/openclaw/dist/hitl-ingress.js +2 -2
  12. package/adapters/openclaw/dist/index.js +46 -11
  13. package/adapters/openclaw/openclaw.plugin.json +5 -1
  14. package/adapters/openclaw/package.json +2 -2
  15. package/adapters/openclaw/src/hitl-ingress.ts +2 -2
  16. package/adapters/openclaw/src/index.ts +49 -11
  17. package/assets/orkmind_bridge.py +117 -9
  18. package/dist/adapters/claude-bg.js +39 -5
  19. package/dist/adapters/codex.js +4 -3
  20. package/dist/agents-md.js +1 -0
  21. package/dist/auditrun.js +5 -1
  22. package/dist/board.js +16 -14
  23. package/dist/branch-de-estado.js +66 -10
  24. package/dist/busca-semantica.js +52 -12
  25. package/dist/canarios-rm057.js +149 -0
  26. package/dist/canarios.js +2 -0
  27. package/dist/ci.js +8 -1
  28. package/dist/claims.js +39 -1
  29. package/dist/company-brain-context.js +158 -22
  30. package/dist/docs.js +48 -9
  31. package/dist/doctor-pulse-cron.js +194 -0
  32. package/dist/doctor-rede.js +149 -0
  33. package/dist/doctor.js +89 -9
  34. package/dist/entrega-pr.js +20 -8
  35. package/dist/esgotamentos.js +181 -0
  36. package/dist/experiencia.js +12 -2
  37. package/dist/fabrica-estado.js +14 -6
  38. package/dist/fabrica-publicar.js +6 -0
  39. package/dist/fechamento.js +20 -66
  40. package/dist/forja.js +31 -7
  41. package/dist/gates.js +5 -1
  42. package/dist/hitl-contract.js +59 -4
  43. package/dist/hitl-curto.js +11 -6
  44. package/dist/hitl-gates.js +5 -0
  45. package/dist/hitl-lote.js +5 -2
  46. package/dist/hitl-resumo.js +9 -0
  47. package/dist/hitl-tempo-parado.js +201 -0
  48. package/dist/hitl-texto-livre.js +5 -5
  49. package/dist/horario.js +27 -0
  50. package/dist/hosts.js +52 -10
  51. package/dist/impedimento.js +205 -0
  52. package/dist/index.js +391 -74
  53. package/dist/indice-vetorial.js +87 -10
  54. package/dist/indice.js +3 -1
  55. package/dist/init.js +17 -0
  56. package/dist/integracoes-locais.js +2 -0
  57. package/dist/intelligence-graph-cli.js +91 -8
  58. package/dist/intelligence-graph-contexto.js +215 -0
  59. package/dist/intelligence-graph-parsers.js +6 -4
  60. package/dist/leases.js +664 -87
  61. package/dist/ledger-stats.js +15 -1
  62. package/dist/ledger.js +3 -0
  63. package/dist/licoes.js +1 -0
  64. package/dist/maestro-runtime.js +4 -1
  65. package/dist/manifest.js +20 -1
  66. package/dist/master-nota.js +14 -5
  67. package/dist/master.js +129 -12
  68. package/dist/mcp-artifacts.js +34 -3
  69. package/dist/mcp-git.js +20 -3
  70. package/dist/mcp-grafo-worker.js +5 -3
  71. package/dist/mcp-grafo.js +50 -4
  72. package/dist/mcp-server.js +14 -1
  73. package/dist/memoria.js +48 -6
  74. package/dist/network-roadmap.js +191 -16
  75. package/dist/ocupacao.js +11 -3
  76. package/dist/orkmind.js +90 -5
  77. package/dist/orquestracao.js +12 -10
  78. package/dist/parado-no-condutor.js +188 -32
  79. package/dist/phase.js +68 -15
  80. package/dist/policies.js +52 -1
  81. package/dist/portfolio.js +4 -1
  82. package/dist/projeto-alvo.js +5 -1
  83. package/dist/prompts.js +36 -1
  84. package/dist/prova-ativacao.js +75 -6
  85. package/dist/pulse-delivery.js +34 -2
  86. package/dist/pulse.js +49 -3
  87. package/dist/ratelimit.js +20 -4
  88. package/dist/recall.js +3 -1
  89. package/dist/rede-adesao.js +399 -0
  90. package/dist/rede-forja.js +289 -0
  91. package/dist/rede-projetos.js +239 -0
  92. package/dist/rede-status.js +336 -0
  93. package/dist/rede.js +980 -0
  94. package/dist/retry.js +69 -2
  95. package/dist/roadmap-status.js +23 -3
  96. package/dist/runtime-profiles.js +22 -1
  97. package/dist/saida-segura.js +58 -0
  98. package/dist/session-watcher-claude.js +113 -23
  99. package/dist/sessoes-contas.js +187 -0
  100. package/dist/sessoes-inventario.js +6 -18
  101. package/dist/sessoes.js +83 -2
  102. package/dist/ship.js +85 -19
  103. package/dist/thread.js +192 -13
  104. package/dist/worktree.js +5 -2
  105. package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
  106. package/package.json +7 -3
  107. package/references/definition-of-done.md +1 -1
  108. package/skills/README.md +1 -1
  109. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  110. package/skills/phases/master-metrics/SKILL.md +8 -3
@@ -56,13 +56,19 @@ exports.decodificarVetor = decodificarVetor;
56
56
  exports.lerIndice = lerIndice;
57
57
  exports.serializarIndice = serializarIndice;
58
58
  exports.gravarIndice = gravarIndice;
59
- exports.universoDoTenant = universoDoTenant;
59
+ exports.conferirUniverso = conferirUniverso;
60
+ exports.universoDaBusca = universoDaBusca;
61
+ exports.codigoDaFalha = codigoDaFalha;
60
62
  exports.conteudoRecusado = conteudoRecusado;
63
+ exports.indexavel = indexavel;
61
64
  exports.tokensEstimados = tokensEstimados;
62
65
  exports.dimDoModeloLocal = dimDoModeloLocal;
63
66
  exports.codigoDeEmbedding = codigoDeEmbedding;
64
67
  exports.espacoDoAlvo = espacoDoAlvo;
65
68
  exports.indexar = indexar;
69
+ exports.avisoDeCobertura = avisoDeCobertura;
70
+ exports.textoForaDaBusca = textoForaDaBusca;
71
+ exports.textoDoIndice = textoDoIndice;
66
72
  exports.vetoresCoerentes = vetoresCoerentes;
67
73
  const fs = __importStar(require("node:fs"));
68
74
  const os = __importStar(require("node:os"));
@@ -170,18 +176,48 @@ function gravarIndice(arquivo, indice) {
170
176
  return true;
171
177
  }
172
178
  /**
173
- * Universo do tenant: o export governado das colecoes do ork (filtro de injecao e visibilidade
174
- * da biblioteca), com checagem EXPLICITA do tenant antes de qualquer texto ir ao embed.
179
+ * RM-038 (D6): a fronteira do universo no `ork`, num lugar so. Entrada de outro tenant, de colecao
180
+ * fora do ork ou com id repetido e violacao tipada, nunca descarte silencioso: nada disso vai ao embed.
175
181
  */
176
- function universoDoTenant(fonte, tenant, colecoes = orkmind_1.COLECOES_DO_ORK) {
177
- return colecoes.flatMap(c => fonte.buscar({ collection: c, tags: { project: [tenant] } })
178
- .filter(e => e.collection === c && (e.tags.project ?? []).includes(tenant)))
179
- .sort((a, b) => a.collection.localeCompare(b.collection) || a.id.localeCompare(b.id));
182
+ function conferirUniverso(entradas, tenant, lidoEm = Date.now()) {
183
+ const vistos = new Set();
184
+ for (const e of entradas) {
185
+ if (!(0, orkmind_1.pertenceAoUniverso)(e, tenant, lidoEm) || vistos.has(e.id))
186
+ throw new Error('memory.query.scope-violation');
187
+ vistos.add(e.id);
188
+ }
189
+ }
190
+ /**
191
+ * RM-038 (D4): o universo da busca e do indice, o domicilio unico. A fonte le uma vez (a ponte
192
+ * filtra o tenant na origem e aplica a governanca da biblioteca); aqui a fronteira e conferida de
193
+ * novo, a ordem fica estavel e a contagem por colecao sai pronta para o status e o index.
194
+ */
195
+ function universoDaBusca(fonte, tenant) {
196
+ const lidoEm = Date.now();
197
+ const lido = fonte.universo(tenant);
198
+ conferirUniverso(lido.entradas, tenant, lidoEm);
199
+ const entradas = [...lido.entradas].sort((a, b) => a.collection.localeCompare(b.collection) || a.id.localeCompare(b.id));
200
+ const porColecao = Object.fromEntries(orkmind_1.COLECOES_DO_ORK.map(c => [c, 0]));
201
+ for (const e of entradas)
202
+ porColecao[e.collection] += 1;
203
+ return { tenant, lidoEm, entradas, porColecao, foraDaBusca: lido.foraDaBusca,
204
+ ...(lido.latenciaMs === undefined ? {} : { latenciaMs: lido.latenciaMs }) };
205
+ }
206
+ /** Codigo tipado de uma falha (`memory.query.window-saturated`), ou o padrao quando nao ha. */
207
+ function codigoDaFalha(erro, padrao = 'memory.universo.indisponivel') {
208
+ return /^[a-z]+(?:\.[a-z-]+)+/.exec(erro instanceof Error ? erro.message : '')?.[0] ?? padrao;
180
209
  }
181
210
  /** Conteudo que nunca vai ao provider: padrao de segredo da policy ou URL com credencial. */
182
211
  function conteudoRecusado(conteudo) {
183
212
  return (0, policies_1.procurarSegredos)(conteudo).length > 0 || URL_COM_CREDENCIAL.test(conteudo);
184
213
  }
214
+ /**
215
+ * RM-038: o indice embeda a entrada? Nao embeda texto vazio, acima do limite nem com padrao de
216
+ * segredo. Domicilio unico para o `indexar` e para o aviso de cobertura do status.
217
+ */
218
+ function indexavel(e) {
219
+ return e.content.trim() !== '' && e.content.length <= orkmind_1.EMBED_MAX_CARACTERES && !conteudoRecusado(e.content);
220
+ }
185
221
  function tokensEstimados(conteudo) {
186
222
  return Math.ceil(conteudo.length / exports.CARACTERES_POR_TOKEN);
187
223
  }
@@ -236,13 +272,20 @@ function espacoDoAlvo(config, alvo, env = process.env) {
236
272
  * que ja voltaram, porque cada vetor gravado e coerente por si.
237
273
  */
238
274
  function indexar(o) {
275
+ // RM-038 (D6): conferir de novo antes de qualquer coisa; nada de outro tenant vai ao embed, nem de
276
+ // um chamador que montou o universo a mao. Violacao falha alto, nunca vira descarte silencioso.
277
+ if (o.universo.tenant !== o.tenant)
278
+ throw new Error('memory.query.scope-violation');
279
+ conferirUniverso(o.universo.entradas, o.tenant, o.universo.lidoEm);
239
280
  const espaco = espacoDoAlvo(o.config, o.alvo, o.env);
240
- const universo = o.universo.filter(e => (e.tags.project ?? []).includes(o.tenant));
281
+ const universo = o.universo.entradas;
241
282
  const foraDoLimite = universo.filter(e => e.content.length > orkmind_1.EMBED_MAX_CARACTERES);
242
283
  const recusados = universo.filter(e => e.content.length <= orkmind_1.EMBED_MAX_CARACTERES && conteudoRecusado(e.content));
243
- const indexaveis = universo.filter(e => e.content.length <= orkmind_1.EMBED_MAX_CARACTERES && !conteudoRecusado(e.content) && e.content.trim());
284
+ const indexaveis = universo.filter(indexavel);
244
285
  const base = { alvo: o.alvo, modelo: null, dim: null, arquivo: null, dryRun: o.dryRun,
245
- universo: universo.length, coerentes: 0, embedados: 0, reescritos: 0, removidos: 0, recusados: recusados.length,
286
+ universo: universo.length, porColecao: { ...o.universo.porColecao }, foraDaBusca: o.universo.foraDaBusca,
287
+ latenciaUniversoMs: o.universo.latenciaMs ?? null,
288
+ coerentes: 0, embedados: 0, reescritos: 0, removidos: 0, recusados: recusados.length,
246
289
  foraDoLimite: foraDoLimite.length, truncados: 0, tokensEstimados: 0, custoEstimadoUsd: 0, chamadasAoProvider: 0, motivo: null, detalhe: '' };
247
290
  if ('motivo' in espaco) {
248
291
  const tokens = indexaveis.reduce((t, e) => t + tokensEstimados(e.content), 0);
@@ -302,6 +345,40 @@ function indexar(o) {
302
345
  return { ...r, motivo, detalhe: `indexacao interrompida depois de ${r.embedados} vetor(es); os lotes concluidos ficaram gravados` };
303
346
  return r;
304
347
  }
348
+ /**
349
+ * RM-038 (D7): o aviso de cobertura, o mesmo no status e na busca. Reindexar so e sugerido quando
350
+ * resolve; o que o indice nunca embeda (vazio, acima do limite, padrao de segredo) e dito a parte.
351
+ */
352
+ function avisoDeCobertura(coerentes, entradas, alvo, onde = '') {
353
+ if (coerentes >= entradas.length)
354
+ return null;
355
+ const foraPorDesenho = entradas.filter(e => !indexavel(e)).length;
356
+ const base = `o indice cobre ${coerentes} de ${entradas.length} entrada(s) que a busca enxerga${onde}`;
357
+ if (coerentes < entradas.length - foraPorDesenho)
358
+ return `${base}: rode ork memory index${alvo === 'fallback' ? ' --modelo fallback' : ''}`;
359
+ return `${base}; ${foraPorDesenho} fica(m) fora do indice por desenho (vazia, acima de ${orkmind_1.EMBED_MAX_CARACTERES} caracteres ou com padrao de segredo)`;
360
+ }
361
+ /** RM-038: a linha do que fica fora da busca, a mesma no `ork memory index` e no `ork memory status`. */
362
+ function textoForaDaBusca(f) {
363
+ return f ? `${f.injecao} com injection_risk e ${f.expiradas} expirada(s) (governanca da biblioteca), ` +
364
+ `${f.outrasColecoes} em outras colecoes (fora da busca do ork); nada disso vai ao embed` : 'nao medido nesta base';
365
+ }
366
+ /** Texto de `ork memory index`: de que universo, o que foi (ou seria) embedado e quanto custa estimado. */
367
+ function textoDoIndice(r) {
368
+ const custo = r.custoEstimadoUsd === null ? 'nao estimado' : `US$ ${r.custoEstimadoUsd.toFixed(8)}`;
369
+ const colecoes = Object.entries(r.porColecao).map(([c, n]) => `${c} ${n}`).join(', ');
370
+ return [
371
+ `Indice vetorial (${r.alvo}${r.dryRun ? ', --dry-run' : ''}): ${r.modelo ?? '(sem modelo)'}${r.dim ? ` / ${r.dim} dim` : ''}`,
372
+ ` universo da busca ${r.universo} entrada(s): ${colecoes}; coerentes ${r.coerentes}`,
373
+ ` fora da busca ${textoForaDaBusca(r.foraDaBusca)}`,
374
+ ` embedados ${r.embedados} (reescritos ${r.reescritos}); removidos ${r.removidos}`,
375
+ ` fora do indice ${r.recusados} recusada(s) por padrao de segredo, ${r.foraDoLimite} acima do limite`,
376
+ ...(r.truncados ? [` truncados ${r.truncados} acima do contexto do modelo local, embedados pelo comeco`] : []),
377
+ ` estimativa ${r.tokensEstimados} token(s), ${custo}; chamadas ao provider ${r.chamadasAoProvider}`,
378
+ ...(r.arquivo ? [` arquivo ${r.arquivo}`] : []),
379
+ ...(r.motivo ? [` motivo ${r.motivo}: ${r.detalhe}`] : r.detalhe ? [` ${r.detalhe}`] : []),
380
+ ].join('\n');
381
+ }
305
382
  /** Vetores coerentes de um indice para o universo informado: a base da busca e da cobertura. */
306
383
  function vetoresCoerentes(indice, universo) {
307
384
  const saida = new Map();
package/dist/indice.js CHANGED
@@ -63,7 +63,9 @@ function calcularIndice(eventos) {
63
63
  const parcelas = [];
64
64
  let valor = exports.INDICE_BASE;
65
65
  for (const insumo of exports.INSUMOS_DO_INDICE) {
66
- const ocorrencias = eventos.filter((e) => e.tipo === insumo.evento).length;
66
+ // Ensaio de 03/10/2026 (RM-049): o `ork ship --dry-run` barrado grava `ship_blocked` com `dryRun: true`;
67
+ // ensaio nao e tropeco da entrega, como ja nao e pausa no board (fatia 2 do ensaio da 0.5.0, P2).
68
+ const ocorrencias = eventos.filter((e) => e.tipo === insumo.evento && e.dryRun !== true).length;
67
69
  if (ocorrencias === 0)
68
70
  continue;
69
71
  const bruto = insumo.peso * ocorrencias;
package/dist/init.js CHANGED
@@ -46,6 +46,7 @@ exports.baseBranchDetectada = baseBranchDetectada;
46
46
  exports.verifyDetectado = verifyDetectado;
47
47
  exports.detectar = detectar;
48
48
  exports.manifestoYaml = manifestoYaml;
49
+ exports.exigirRepositorioParaInit = exigirRepositorioParaInit;
49
50
  exports.init = init;
50
51
  const fs = __importStar(require("node:fs"));
51
52
  const path = __importStar(require("node:path"));
@@ -192,6 +193,8 @@ conduction:
192
193
  worktree:
193
194
  base_branch: "${d.baseBranch}"
194
195
  dir: ".claude/worktrees"
196
+ # true: o ork thread new cria a worktree e a branch da thread sem flag (--sem-worktree cria sem);
197
+ # false ou ausente: só com --worktree auto
195
198
  por_thread: true
196
199
 
197
200
  verify:
@@ -259,6 +262,20 @@ policies:
259
262
  push_direto_na_base: block
260
263
  `;
261
264
  }
265
+ /**
266
+ * Ensaio de 03/10/2026 (RM-049): o `ork init` rodado por engano fora de um repositorio (no HOME, por
267
+ * exemplo) gravava manifesto, `AGENTS.md` e `.orkastery/` ali e saia 0; um repositorio criado depois
268
+ * dentro dessa pasta herdava o manifesto de fora, com outro nome e outra abbrev, e o doctor dizia PRONTO.
269
+ * A CLI recusa antes de gravar qualquer coisa. A funcao `init` segue aceitando qualquer pasta (demo e
270
+ * canarios montam o projeto por ela).
271
+ */
272
+ function exigirRepositorioParaInit(dir) {
273
+ const r = (0, util_1.exec)('git', ['rev-parse', '--is-inside-work-tree'], dir);
274
+ if (r.ok && r.stdout.trim() === 'true')
275
+ return;
276
+ throw new Error(`init.fora-do-repositorio: ${path.resolve(dir)} não está num repositório git; nada foi criado. ` +
277
+ 'Entre no repositório do projeto (ou crie um com git init e o primeiro commit) e rode ork init de novo.');
278
+ }
262
279
  /** Gera o manifesto e o esqueleto de `.orkastery/`. Nao sobrescreve sem `force`. */
263
280
  function init(dirInicial, opcoes = {}) {
264
281
  const deteccao = detectar(dirInicial, opcoes.nome, opcoes.abbrev);
@@ -16,4 +16,6 @@ exports.TESTES_DE_INTEGRACAO_LOCAL = new Set([
16
16
  'memory-publication-profile.test.js', 'native-fixture.test.js', 'orkmind-transport.test.js', 'verify-sandbox.test.js',
17
17
  // RM-037 (fatia 3): o worker do ork_git_commit e o sandbox do verify de verdade, como no mcp-git.test.js.
18
18
  'rm037-fatia3-hard-link-mcp-git.test.js', 'rm037-fatia3-lease-mcp-git.test.js',
19
+ // RM-038 (fatia de correcao): a ponte com o GovernedStore real da biblioteca instalada.
20
+ 'rm038-universo-ponte.test.js',
19
21
  ]);
@@ -34,6 +34,9 @@ var __importStar = (this && this.__importStar) || (function () {
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.CORRECAO_DO_INDICE = exports.USO_DO_GRAFO = exports.AMOSTRA_SCHEMA = exports.STATUS_SCHEMA = void 0;
37
+ exports.descreverVersoes = descreverVersoes;
38
+ exports.correcaoDosAnalisadores = correcaoDosAnalisadores;
39
+ exports.checarAnalisadoresDoGrafo = checarAnalisadoresDoGrafo;
37
40
  exports.lerPedido = lerPedido;
38
41
  exports.executarGrafo = executarGrafo;
39
42
  /**
@@ -56,14 +59,62 @@ const fs = __importStar(require("node:fs"));
56
59
  const path = __importStar(require("node:path"));
57
60
  const intelligence_graph_contract_1 = require("./intelligence-graph-contract");
58
61
  const intelligence_graph_index_1 = require("./intelligence-graph-index");
62
+ const intelligence_graph_parsers_1 = require("./intelligence-graph-parsers");
59
63
  const intelligence_graph_query_1 = require("./intelligence-graph-query");
60
64
  const intelligence_graph_repo_1 = require("./intelligence-graph-repo");
65
+ const intelligence_graph_contexto_1 = require("./intelligence-graph-contexto");
61
66
  exports.STATUS_SCHEMA = 'ork.code-graph-index-status/v0';
67
+ /** As versoes dos analisadores como o `ork grafo status` e o `ork doctor` as mostram. */
68
+ function descreverVersoes(v) {
69
+ return Object.entries(v).map(([k, x]) => `${k} ${x}`).join(', ');
70
+ }
71
+ /** O prefixo das recusas dos analisadores (`intelligence-graph-parsers.ts`). */
72
+ const RECUSA_DOS_ANALISADORES = 'grafo.parser.indisponivel';
73
+ /**
74
+ * RM-031 (pacote do npm): o que fazer quando os analisadores nao carregam, dito pelo `ork doctor` e
75
+ * pelo `ork grafo status`. O micromark e so ESM e carrega por `require`, o que o Node so faz sem flag
76
+ * a partir de 20.19 e de 22.12. Nos outros casos o pacote falta ou esta fora da instalacao do `ork`,
77
+ * e a instalacao global do npm traz os analisadores, que sao dependencias do pacote. Mora aqui, fora
78
+ * de `MODULOS_DO_EXTRATOR`, para a redacao nao entrar na chave do indice; a versao vem de quem chama
79
+ * (o `index.ts`), porque o CLI do grafo so importa a propria familia (fronteira do KG3).
80
+ */
81
+ function correcaoDosAnalisadores(motivo, versao) {
82
+ if (/ERR_REQUIRE_ESM/.test(motivo)) {
83
+ return `o micromark e so ESM, e este Node (${process.version}) nao o carrega por require: use Node 20.19, 22.12 ou mais novo, ` +
84
+ 'sem a opcao --no-experimental-require-module (na linha de comando ou no NODE_OPTIONS)';
85
+ }
86
+ // Sem a versao do pacote (`0.0.0-desconhecida` quando o package.json dele nao e achado), o comando
87
+ // sai sem `@<versao>` e o npm instala a ultima publicada.
88
+ const fixada = versao && /^\d+\.\d+\.\d+/.test(versao) && !versao.startsWith('0.0.0') ? `@${versao}` : '';
89
+ return `reinstale o ork global, que traz os analisadores como dependencias: npm install -g @orkastery/cli${fixada}` +
90
+ ' (instalado dentro de um projeto ou pelo npx, o npm deixa os analisadores fora do pacote do ork, e o grafo os recusa)';
91
+ }
92
+ /**
93
+ * RM-031 (pacote do npm): o check "analisadores do grafo" do `ork doctor`. Carrega o que o
94
+ * `ork grafo indexar` carrega (`carregarAnalisadores`), entao diz o que o indexar faria, inclusive no
95
+ * Node sem `require` de ESM. A falta e `warn`, com a correcao: o grafo nao e condicao do despacho de
96
+ * fase. Fora da familia do grafo so o `index.ts` e o worker do MCP a abrem (fronteira do KG1), entao o
97
+ * `index.ts` passa este check ao doctor, que nao importa o grafo.
98
+ */
99
+ function checarAnalisadoresDoGrafo(versao, carregar = intelligence_graph_parsers_1.carregarAnalisadores, versoes = intelligence_graph_parsers_1.versoesDosAnalisadores) {
100
+ const nome = 'analisadores do grafo';
101
+ try {
102
+ carregar();
103
+ return { nome, nivel: 'ok', detalhe: descreverVersoes(versoes()) };
104
+ }
105
+ catch (e) {
106
+ const motivo = e.message;
107
+ return { nome, nivel: 'warn',
108
+ detalhe: `${motivo}: o ork grafo indexar recusa nesta instalacao, e sem o indice as consultas e as tools ork_grafo_* tambem`,
109
+ correcao: correcaoDosAnalisadores(motivo, versao) };
110
+ }
111
+ }
62
112
  /** O mesmo schema da amostra do KG2: as amostras auditadas continuam validas. */
63
113
  exports.AMOSTRA_SCHEMA = 'ork.graph-edge-audit-sample/v0';
64
114
  exports.USO_DO_GRAFO = [
65
115
  'uso: ork grafo indexar [--verificar] [--forcar] [--json]',
66
116
  ' ork grafo status [--json]',
117
+ ' ork grafo contexto <thread> [--json [--teto-bytes N]]',
67
118
  ' ork grafo vizinhos <no> [--profundidade N] [--sentido entrada|saida|ambos] [--tipo T,...] [--limite N] [--json [--teto-bytes N]]',
68
119
  ' ork grafo chamadores <simbolo> [--profundidade N] [--limite N] [--json [--teto-bytes N]]',
69
120
  ' ork grafo importadores <arquivo|simbolo> [--profundidade N] [--limite N] [--json [--teto-bytes N]]',
@@ -83,6 +134,7 @@ const ESTADO_DO_INDICE = Object.freeze({
83
134
  const SUBCOMANDOS = Object.freeze({
84
135
  indexar: { posicionais: 0, bandeiras: ['verificar', 'forcar', 'json'], valores: [] },
85
136
  status: { posicionais: 0, bandeiras: ['json'], valores: [] },
137
+ contexto: { posicionais: 1, bandeiras: ['json'], valores: ['teto-bytes'] },
86
138
  vizinhos: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'sentido', 'tipo', 'limite', 'teto-bytes'] },
87
139
  chamadores: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'limite', 'teto-bytes'] },
88
140
  importadores: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'limite', 'teto-bytes'] },
@@ -238,9 +290,11 @@ function status(ctx, p) {
238
290
  const doHead = chave ? estado.indices.find((i) => i.chave === chave) ?? null : null;
239
291
  // Sem analisadores nao ha chave: o indice do HEAD fica indisponivel, nunca "ausente" (indexar tambem nao roda).
240
292
  const indiceDoHeadEstado = erro ? 'indisponivel' : doHead ? (doHead.problema ? 'com-problema' : 'presente') : 'ausente';
293
+ // RM-031 (pacote do npm): a recusa dos analisadores vem com o que fazer, a mesma correcao do `ork doctor`.
294
+ const correcao = erro !== null && erro.startsWith(RECUSA_DOS_ANALISADORES) ? correcaoDosAnalisadores(erro, ctx.versao) : null;
241
295
  const r = {
242
296
  schema: exports.STATUS_SCHEMA, head: arvore.head, arvore: arvore.motivo === null ? 'limpa' : arvore.motivo, chave_do_head: chave,
243
- indice_do_head: indiceDoHeadEstado, analisadores, erro,
297
+ indice_do_head: indiceDoHeadEstado, analisadores, erro, correcao,
244
298
  dir: estado.dir, bytes: estado.bytes, indices: estado.indices, sobras: estado.sobras,
245
299
  };
246
300
  if (p.bandeiras.has('json')) {
@@ -250,8 +304,11 @@ function status(ctx, p) {
250
304
  const linhas = [
251
305
  `grafo: HEAD ${arvore.head ? arvore.head.slice(0, 12) : 'sem commit'}, arvore ${r.arvore}`,
252
306
  ` indice do HEAD ${chave ?? 'indisponivel'} ${r.indice_do_head === 'ausente' ? '(ausente: rode ork grafo indexar)'
253
- : r.indice_do_head === 'indisponivel' ? '(sem os analisadores nesta instalacao, nem a consulta nem o indexar rodam)' : `(${r.indice_do_head})`}`,
254
- ` analisadores ${erro ?? Object.entries(analisadores).map(([k, v]) => `${k} ${v}`).join(', ')}`,
307
+ : r.indice_do_head === 'indisponivel'
308
+ ? (correcao ? '(sem os analisadores nesta instalacao, nem a consulta nem o indexar rodam)' : '(sem a chave do indice: o erro esta na linha de baixo)')
309
+ : `(${r.indice_do_head})`}`,
310
+ ` analisadores ${erro ?? descreverVersoes(analisadores)}`,
311
+ ...(correcao ? [` correcao ${correcao}`] : []),
255
312
  ` pasta ${estado.dir ?? 'ainda nao criada'} (${estado.indices.length} indice(s), ${mb(estado.bytes)})`,
256
313
  ];
257
314
  for (const i of estado.indices) {
@@ -388,6 +445,29 @@ function consultar(ctx, p) {
388
445
  ctx.escrever(respostaComTeto(perguntar, limite, teto));
389
446
  return 0;
390
447
  }
448
+ function contexto(ctx, p) {
449
+ const teto = tetoDeBytes(p);
450
+ if (teto !== undefined && (teto < 4096 || teto > 65536))
451
+ throw new intelligence_graph_query_1.ErroDeConsulta('grafo.contexto.teto-invalido', 'teto deve ser inteiro de 4096 a 65536 bytes');
452
+ if (!ctx.contextoDaThread)
453
+ throw new intelligence_graph_query_1.ErroDeConsulta('grafo.contexto.indisponivel', 'borda sem leitor da thread');
454
+ const lido = ctx.contextoDaThread(p.posicionais[0]);
455
+ const { grafo, cabecalho } = consultavel({ ...ctx, raiz: lido.raiz });
456
+ if (lido.head !== cabecalho.revision)
457
+ throw new intelligence_graph_query_1.ErroDeConsulta('grafo.contexto.revisao-mudou', 'HEAD mudou durante a leitura; repita a consulta');
458
+ const json = (0, intelligence_graph_contexto_1.pacoteDeContexto)(grafo, cabecalho, lido.entrada, teto);
459
+ if (p.bandeiras.has('json'))
460
+ ctx.escrever(json);
461
+ else {
462
+ const r = JSON.parse(json);
463
+ ctx.escrever([`Contexto ${r.thread} @ ${r.indice.revision}`,
464
+ ` ${r.sementes.length}/${r.total_sementes} sementes, ${r.arestas.length}/${r.total_ligacoes} ligacoes agregadas; ${r.medida.pacote_bytes} bytes JSON`,
465
+ ` tamanho dos arquivos no pacote (nao descoberta): ${r.medida.leitura_crua_bytes} bytes (revisao indexada); tokens: unavailable`,
466
+ ` diff: ${r.fontes.diff}`,
467
+ ` truncado: ${r.truncado}; arvore: ${r.indice.arvore}; use --json para nos e evidencias`, json].join('\n'));
468
+ }
469
+ return 0;
470
+ }
391
471
  const sha256 = (b) => (0, node_crypto_1.createHash)('sha256').update(b).digest('hex');
392
472
  /**
393
473
  * Bytes da arvore, conferidos arquivo a arquivo contra o manifesto do indice: o trecho da amostra e
@@ -541,12 +621,12 @@ function limpar(ctx, p) {
541
621
  return 0;
542
622
  }
543
623
  /** KG5 (D4, D5): com `--teto-bytes` o erro tambem sai compacto; recusa de indice traz o estado e a correcao. */
544
- const erroEmJson = (ctx, e, compacto) => {
624
+ const erroEmJson = (ctx, e, compacto, schema = intelligence_graph_query_1.CONSULTA_SCHEMA) => {
545
625
  const [codigo, ...detalhe] = String(e.message).split('\n')[0].split(': ');
546
626
  const doIndice = Object.prototype.hasOwnProperty.call(ESTADO_DO_INDICE, codigo)
547
627
  ? { estado_do_indice: ESTADO_DO_INDICE[codigo], correcao: exports.CORRECAO_DO_INDICE } : {};
548
628
  const r = {
549
- schema: intelligence_graph_query_1.CONSULTA_SCHEMA,
629
+ schema,
550
630
  erro: { codigo, detalhe: detalhe.join(': ') || null, candidatos: e instanceof intelligence_graph_query_1.ErroDeConsulta ? e.candidatos : [], ...doIndice },
551
631
  };
552
632
  ctx.escrever(compacto ? JSON.stringify(r) : JSON.stringify(r, null, 2));
@@ -554,7 +634,8 @@ const erroEmJson = (ctx, e, compacto) => {
554
634
  };
555
635
  /** `ork grafo <subcomando> ...`: devolve o codigo de saida; sem `--json`, o erro de uso ou tipado e lancado. */
556
636
  function executarGrafo(argv, ctx) {
557
- const compacto = argv.some((a) => a === '--teto-bytes' || a.startsWith('--teto-bytes='));
637
+ const schema = argv[0] === 'contexto' ? intelligence_graph_contexto_1.CONTEXTO_SCHEMA : intelligence_graph_query_1.CONSULTA_SCHEMA;
638
+ const compacto = argv[0] === 'contexto' || argv.some((a) => a === '--teto-bytes' || a.startsWith('--teto-bytes='));
558
639
  let p;
559
640
  try {
560
641
  p = lerPedido(argv);
@@ -562,7 +643,7 @@ function executarGrafo(argv, ctx) {
562
643
  catch (e) {
563
644
  // Com `--json` no argv, tambem o erro de uso sai como objeto, como os demais.
564
645
  if (argv.includes('--json'))
565
- return erroEmJson(ctx, e, compacto);
646
+ return erroEmJson(ctx, e, compacto, schema);
566
647
  throw e;
567
648
  }
568
649
  try {
@@ -574,11 +655,13 @@ function executarGrafo(argv, ctx) {
574
655
  return amostra(ctx, p);
575
656
  if (p.sub === 'limpar')
576
657
  return limpar(ctx, p);
658
+ if (p.sub === 'contexto')
659
+ return contexto(ctx, p);
577
660
  return consultar(ctx, p);
578
661
  }
579
662
  catch (e) {
580
663
  if (!p.bandeiras.has('json'))
581
664
  throw e;
582
- return erroEmJson(ctx, e, compacto);
665
+ return erroEmJson(ctx, e, compacto, schema);
583
666
  }
584
667
  }
@@ -0,0 +1,215 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.CONTEXTO_FORA_DO_INDICE = exports.CONTEXTO_POR_ALVO = exports.CONTEXTO_TETO_PADRAO = exports.CONTEXTO_SCHEMA = void 0;
4
+ exports.sementesDaThread = sementesDaThread;
5
+ exports.pacoteDeContexto = pacoteDeContexto;
6
+ /** KG5 fatia 3: composicao pura do contexto da thread. Sem E/S, relogio ou inferencia. */
7
+ const node_crypto_1 = require("node:crypto");
8
+ const intelligence_graph_contract_1 = require("./intelligence-graph-contract");
9
+ const intelligence_graph_query_1 = require("./intelligence-graph-query");
10
+ exports.CONTEXTO_SCHEMA = 'ork.thread-graph-context/v2';
11
+ exports.CONTEXTO_TETO_PADRAO = 32768;
12
+ exports.CONTEXTO_POR_ALVO = 8;
13
+ exports.CONTEXTO_FORA_DO_INDICE = 3;
14
+ const hash = (texto) => (0, node_crypto_1.createHash)('sha256').update(texto).digest('hex');
15
+ /** Caminho relativo do repositorio, nunca caminho de maquina, URL ou estado privado. */
16
+ function caminhoPublico(bruto) {
17
+ const p = bruto.replace(/^\.\//, '');
18
+ if (!p || p.startsWith('/') || p.startsWith('~') || /[:\\\u0000-\u001f\u007f]/.test(p) || p.length > 500
19
+ || p.split('/').some((x) => !x || x === '..' || x === '.' || x === '.git' || x === '.orkastery'))
20
+ return null;
21
+ return p;
22
+ }
23
+ /** GOAL/PLAN citam caminhos relativos a raiz em codigo, links ou texto; nao executam instrucoes. */
24
+ function caminhosCitados(texto, diretorios) {
25
+ const r = [];
26
+ const semCitacoes = texto.replace(/`([^`\n]+)`|\]\(([^)\n]+)\)/g, (_m, codigo, link) => {
27
+ const v = codigo ?? link ?? '';
28
+ if (!/^(?:[a-z]+:|\/)/i.test(v) && (v.includes('/') || /\.[a-z0-9]+(?:#.*|:\d+(?::\d+)?)?$/i.test(v)
29
+ || /^(?:Dockerfile|Makefile|LICENSE)$/.test(v)))
30
+ r.push(v.replace(/#.*$/, '').replace(/:\d+(?::\d+)?$/, ''));
31
+ return ' ';
32
+ });
33
+ // Em prosa, exigir diretorio ou extensao de fonte conhecida evita versoes, Node.js e dominios.
34
+ for (const m of semCitacoes.matchAll(/(?:^|[\s(\["'])((?:[\p{L}\p{N}_@.-]+\/)+[\p{L}\p{N}_@.-]+|[\p{L}\p{N}_@-]+\.(?:ts|tsx|js|jsx|mjs|cjs|mts|cts|json|md|markdown|yaml|yml|toml|py|rs|go|c|h|css|html))(?=\.(?:\s|$)|[:#\s),;\]"']|$)/gu))
35
+ if (!/^Node\.js\.?$/i.test(m[1]))
36
+ r.push(m[1].replace(/\.$/, ''));
37
+ return r.filter((bruto) => {
38
+ const p = caminhoPublico(bruto);
39
+ return p !== null && !/^Node\.js$/i.test(p) && (/\.(?:ts|tsx|js|jsx|mjs|cjs|mts|cts|json|md|markdown|yaml|yml|toml|py|rs|go|c|h|css|html)$/i.test(p)
40
+ || /(?:^|\/)(?:Dockerfile|Makefile|LICENSE)$/.test(p)
41
+ || (p.includes('/') && diretorios.has(p.split('/')[0])));
42
+ });
43
+ }
44
+ function sementesDaThread(entrada, caminhosDoRepositorio = []) {
45
+ const diretorios = new Set(caminhosDoRepositorio.filter((p) => p.includes('/')).map((p) => p.split('/')[0]));
46
+ const mapa = new Map();
47
+ const adicionar = (bruto, origem) => {
48
+ const p = caminhoPublico(bruto);
49
+ if (!p)
50
+ return;
51
+ const origens = mapa.get(p) ?? new Set();
52
+ origens.add(origem);
53
+ mapa.set(p, origens);
54
+ };
55
+ if (entrada.diffEstado !== 'ignorado-sem-worktree')
56
+ for (const p of entrada.diff)
57
+ adicionar(p, 'diff');
58
+ for (const tipo of ['goal', 'plan'])
59
+ for (const p of caminhosCitados(entrada[tipo] ?? '', diretorios))
60
+ adicionar(p, tipo);
61
+ for (const c of entrada.claims)
62
+ adicionar(c.arquivo, `claim:${c.id}`);
63
+ return [...mapa].sort(([a], [b]) => (0, intelligence_graph_contract_1.compararUtf8)(a, b))
64
+ .map(([arquivo, origens]) => ({ arquivo, origens: [...origens].sort(intelligence_graph_contract_1.compararUtf8) }));
65
+ }
66
+ /** O grafo recebido ja deve estar filtrado pela concessao de quem consulta. */
67
+ function pacoteDeContexto(grafo, indice, entrada, tetoBytes = exports.CONTEXTO_TETO_PADRAO) {
68
+ if (!Number.isInteger(tetoBytes) || tetoBytes < 4096 || tetoBytes > 65536)
69
+ throw new intelligence_graph_query_1.ErroDeConsulta('grafo.contexto.teto-invalido', 'teto deve ser inteiro de 4096 a 65536 bytes');
70
+ const porId = new Map(grafo.nodes.map((n) => [n.node_id, n]));
71
+ const arquivos = new Set(grafo.nodes.filter((n) => n.kind === 'file').map((n) => n.locator.path));
72
+ const sementes = sementesDaThread(entrada, [...arquivos]).map((s) => ({ ...s, estado: arquivos.has(s.arquivo) ? 'indexado' : 'fora-do-indice' }));
73
+ const indexadas = sementes.filter((s) => s.estado === 'indexado'), ausentes = sementes.filter((s) => s.estado !== 'indexado');
74
+ const alvos = new Set(indexadas.map((s) => s.arquivo));
75
+ const diff = entrada.diffEstado === 'ignorado-sem-worktree' ? [] : [...new Set(entrada.diff)].sort(intelligence_graph_contract_1.compararUtf8);
76
+ // Distancia em arquivos, em ambos os sentidos, sem depender da ordem das arestas.
77
+ const adj = new Map();
78
+ for (const a of grafo.edges) {
79
+ const de = porId.get(a.from).locator.path, para = porId.get(a.to).locator.path;
80
+ if (de === para)
81
+ continue;
82
+ if (!adj.has(de))
83
+ adj.set(de, new Set());
84
+ if (!adj.has(para))
85
+ adj.set(para, new Set());
86
+ adj.get(de).add(para);
87
+ adj.get(para).add(de);
88
+ }
89
+ const dist = new Map(diff.filter((p) => arquivos.has(p)).map((p) => [p, 0]));
90
+ const fila = [...dist.keys()];
91
+ for (let i = 0; i < fila.length; i++)
92
+ for (const p of adj.get(fila[i]) ?? []) {
93
+ if (!dist.has(p)) {
94
+ dist.set(p, dist.get(fila[i]) + 1);
95
+ fila.push(p);
96
+ }
97
+ }
98
+ const extratores = [...indice.extratores].sort((a, b) => (0, intelligence_graph_contract_1.compararUtf8)((0, intelligence_graph_contract_1.canonico)(a), (0, intelligence_graph_contract_1.canonico)(b)));
99
+ const extratorIds = new Map(extratores.map((e, i) => [(0, intelligence_graph_contract_1.canonico)(e), i]));
100
+ const grupos = new Map();
101
+ let totalArestas = 0, estruturais = 0, evidenciasAuxiliares = 0;
102
+ for (const a of grafo.edges) {
103
+ const de = porId.get(a.from), para = porId.get(a.to);
104
+ if (!alvos.has(de.locator.path) && !alvos.has(para.locator.path))
105
+ continue;
106
+ totalArestas++;
107
+ if ((a.kind === 'declares' || a.kind === 'contains') && de.locator.path === para.locator.path && alvos.has(de.locator.path)) {
108
+ estruturais++;
109
+ continue;
110
+ }
111
+ const proprias = a.evidence.filter((e) => e.path === de.locator.path);
112
+ evidenciasAuxiliares += a.evidence.length - proprias.length;
113
+ // Sem evidencia no arquivo de origem, a aresta fica em omitidos.arestas.
114
+ if (!proprias.length)
115
+ continue;
116
+ const origem = `file ${de.locator.path}`, destino = (0, intelligence_graph_query_1.rotuloDoNo)(para);
117
+ const chave = (0, intelligence_graph_contract_1.canonico)([a.kind, destino, de.locator.path]);
118
+ const grupo = grupos.get(chave) ?? { kind: a.kind, origem, destino, alvo: para.node_id, quantidade: 0,
119
+ evidencias: new Map(), entreArquivos: de.locator.path !== para.locator.path,
120
+ distancia: Math.min(dist.get(de.locator.path) ?? Infinity, dist.get(para.locator.path) ?? Infinity) };
121
+ grupo.quantidade++;
122
+ for (const e of proprias) {
123
+ const x = extratorIds.get((0, intelligence_graph_contract_1.canonico)({ extractor_id: e.extractor_id, extractor_version: e.extractor_version }));
124
+ if (x === undefined)
125
+ throw new intelligence_graph_query_1.ErroDeConsulta('grafo.contexto.extrator-ausente', 'extrator da evidencia ausente no cabecalho');
126
+ const s = e.span;
127
+ const tupla = [x, e.extraction_method,
128
+ s.type === 'text' ? [s.line_start, s.line_end] : ['pdf', s.page, s.extracted_text_hash], [s.byte_start, s.byte_end]];
129
+ grupo.evidencias.set((0, intelligence_graph_contract_1.canonico)(tupla), tupla);
130
+ }
131
+ grupos.set(chave, grupo);
132
+ }
133
+ // Entre arquivos > perto do diff > tipo > rotulos; empates independem da ordem do indice.
134
+ const ordenados = [...grupos.values()].sort((a, b) => Number(b.entreArquivos) - Number(a.entreArquivos)
135
+ || (a.distancia === b.distancia ? 0 : a.distancia < b.distancia ? -1 : 1)
136
+ || (0, intelligence_graph_contract_1.compararUtf8)(a.kind, b.kind) || (0, intelligence_graph_contract_1.compararUtf8)(a.destino, b.destino) || (0, intelligence_graph_contract_1.compararUtf8)(a.origem, b.origem));
137
+ // Dentro da mesma prioridade, intercalar tipos impede calls de expulsar imports/references.
138
+ const rodadas = new Map();
139
+ const justos = ordenados.map((g) => {
140
+ const chave = JSON.stringify([g.alvo, g.entreArquivos, g.distancia, g.kind]);
141
+ const rodada = rodadas.get(chave) ?? 0;
142
+ rodadas.set(chave, rodada + 1);
143
+ return { g, rodada };
144
+ }).sort((a, b) => Number(b.g.entreArquivos) - Number(a.g.entreArquivos)
145
+ || (a.g.distancia === b.g.distancia ? 0 : a.g.distancia < b.g.distancia ? -1 : 1)
146
+ || a.rodada - b.rodada || (0, intelligence_graph_contract_1.compararUtf8)(a.g.kind, b.g.kind)
147
+ || (0, intelligence_graph_contract_1.compararUtf8)(a.g.destino, b.g.destino) || (0, intelligence_graph_contract_1.compararUtf8)(a.g.origem, b.g.origem)).map(({ g }) => g);
148
+ const normalizada = { ...entrada, diff, diffEstado: entrada.diffEstado ?? 'coletado-na-worktree',
149
+ claims: [...entrada.claims].sort((a, b) => (0, intelligence_graph_contract_1.compararUtf8)((0, intelligence_graph_contract_1.canonico)(a), (0, intelligence_graph_contract_1.canonico)(b))) };
150
+ const fixo = { schema: exports.CONTEXTO_SCHEMA, thread: entrada.thread, base: entrada.base, entrada_sha256: hash((0, intelligence_graph_contract_1.canonico)(normalizada)),
151
+ indice: { ...indice, extratores },
152
+ fontes: { goal: entrada.goal === null ? 'ausente' : hash(entrada.goal), plan: entrada.plan === null ? 'ausente' : hash(entrada.plan),
153
+ diff: normalizada.diffEstado },
154
+ consulta: { profundidade: 1, sentido: 'ambos', por_alvo: exports.CONTEXTO_POR_ALVO, fora_do_indice: exports.CONTEXTO_FORA_DO_INDICE },
155
+ total_sementes: sementes.length, sementes_fora_do_indice: ausentes.length, total_arestas: totalArestas,
156
+ total_ligacoes: ordenados.length, resumidas: { estruturais }, parcial: intelligence_graph_query_1.AVISO_DE_PARCIALIDADE };
157
+ const selecionadas = [], ligacoes = [];
158
+ const montar = () => {
159
+ const caminhos = new Set(selecionadas.filter((s) => s.estado === 'indexado').map((s) => s.arquivo));
160
+ const rotulos = new Set([...caminhos].map((p) => `file ${p}`));
161
+ for (const a of ligacoes) {
162
+ rotulos.add(a.origem);
163
+ rotulos.add(a.destino);
164
+ caminhos.add(porId.get(a.alvo).locator.path);
165
+ caminhos.add(a.origem.slice(5));
166
+ }
167
+ const refs = new Map([...rotulos].sort(intelligence_graph_contract_1.compararUtf8).map((r, i) => [r, `n${i + 1}`]));
168
+ const fontes = grafo.snapshot.source_manifest.filter((m) => caminhos.has(m.path)).sort((a, b) => (0, intelligence_graph_contract_1.compararUtf8)(a.path, b.path));
169
+ const omitidos = { sementes: sementes.length - selecionadas.length, ligacoes: ordenados.length - ligacoes.length,
170
+ arestas: totalArestas - estruturais - ligacoes.reduce((s, a) => s + a.quantidade, 0), evidencias_auxiliares: evidenciasAuxiliares };
171
+ const cortado = omitidos.sementes > 0 || omitidos.ligacoes > 0 || omitidos.arestas > 0 || evidenciasAuxiliares > 0;
172
+ const r = { ...fixo, sementes: selecionadas, nos: Object.fromEntries([...refs].map(([r, ref]) => [ref, r])),
173
+ arestas: ligacoes.map((a) => ({ kind: a.kind, from: refs.get(a.origem), to: refs.get(a.destino), quantidade: a.quantidade,
174
+ evidencias: [...a.evidencias].sort(([a], [b]) => (0, intelligence_graph_contract_1.compararUtf8)(a, b)).map(([, e]) => e) })),
175
+ truncado: cortado, omitidos, teto: { bytes: tetoBytes, cortado },
176
+ medida: { pacote_bytes: 0, leitura_crua_bytes: fontes.reduce((s, f) => s + f.size_bytes, 0),
177
+ arquivos: fontes.map((f) => ({ path: f.path, bytes: f.size_bytes })),
178
+ origem: 'source_manifest; tamanho dos arquivos, nao custo de descoberta', tokens: 'unavailable' } };
179
+ let texto = (0, intelligence_graph_contract_1.canonico)(r);
180
+ while (r.medida.pacote_bytes !== Buffer.byteLength(texto)) {
181
+ r.medida.pacote_bytes = Buffer.byteLength(texto);
182
+ texto = (0, intelligence_graph_contract_1.canonico)(r);
183
+ }
184
+ return texto;
185
+ };
186
+ let melhor = montar();
187
+ if (Buffer.byteLength(melhor) > tetoBytes)
188
+ throw new intelligence_graph_query_1.ErroDeConsulta('grafo.contexto.teto-excedido', 'cabecalho nao cabe no teto');
189
+ const tentarSemente = (s) => {
190
+ selecionadas.push(s);
191
+ const texto = montar();
192
+ if (Buffer.byteLength(texto) <= tetoBytes)
193
+ melhor = texto;
194
+ else
195
+ selecionadas.pop();
196
+ };
197
+ for (const s of indexadas)
198
+ tentarSemente(s);
199
+ const porAlvo = new Map();
200
+ for (const a of justos) {
201
+ if ((porAlvo.get(a.alvo) ?? 0) >= exports.CONTEXTO_POR_ALVO)
202
+ continue;
203
+ ligacoes.push(a);
204
+ const texto = montar();
205
+ if (Buffer.byteLength(texto) <= tetoBytes) {
206
+ melhor = texto;
207
+ porAlvo.set(a.alvo, (porAlvo.get(a.alvo) ?? 0) + 1);
208
+ }
209
+ else
210
+ ligacoes.pop(); // Um hub grande nao impede uma ligacao menor e relevante de entrar.
211
+ }
212
+ for (const s of ausentes.slice(0, exports.CONTEXTO_FORA_DO_INDICE))
213
+ tentarSemente(s);
214
+ return melhor;
215
+ }
@@ -46,10 +46,12 @@ exports.carregarAnalisadores = carregarAnalisadores;
46
46
  * `typescript` e os pacotes do micromark so valem dentro da instalacao do `ork` que roda (o
47
47
  * `node_modules` do pacote `@orkastery/cli` que contem este modulo), nunca do diretorio atual, do
48
48
  * `NODE_PATH` nem de uma pasta acima, como a do projeto que instalou o `ork` como dependencia: o
49
- * repositorio lido e dado, e codigo dele nunca e carregado. Eles nao sao dependencias de runtime do
50
- * pacote publicado (D1: dependencia nova e decisao de produto); onde faltam ou estao fora da
51
- * instalacao, a recusa e `grafo.parser.indisponivel: <pacote>`. `pacotesDosAnalisadores` lista o
52
- * fecho de dependencias deles, com versao, para a chave do indice (CHECK rodada 1, A10).
49
+ * repositorio lido e dado, e codigo dele nunca e carregado. Desde a correcao de empacotamento da
50
+ * RM-031 eles sao dependencias de runtime do pacote publicado, com versao exata, e o
51
+ * `npm install -g` os poe dentro da instalacao; onde faltam ou estao fora dela (o `ork` instalado
52
+ * dentro de um projeto ou pelo `npx`, que icam as dependencias), a recusa e
53
+ * `grafo.parser.indisponivel: <pacote>`, e o `ork doctor` diz a correcao. `pacotesDosAnalisadores`
54
+ * lista o fecho de dependencias deles, com versao, para a chave do indice (CHECK rodada 1, A10).
53
55
  *
54
56
  * O juiz de sintaxe e o V8 do Node que roda a extracao, o mesmo que carregaria o arquivo. CommonJS
55
57
  * compila aqui, em funcao com as variaveis do modulo, como o carregador faz. ESM compila num unico