@orkastery/cli 0.5.0 → 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 (131) hide show
  1. package/adapters/claude-code/README.md +11 -2
  2. package/adapters/claude-code/agents/ork-master.md +2 -0
  3. package/adapters/claude-code/commands/master.md +8 -0
  4. package/adapters/claude-code/commands/onboarding.md +3 -2
  5. package/adapters/claude-code/commands/ork.md +9 -0
  6. package/adapters/codex/skills/ork/SKILL.md +18 -3
  7. package/adapters/hermes/README.md +1 -0
  8. package/adapters/hermes/bin/ork-network-status.sh +10 -0
  9. package/adapters/hermes/hermes.plugin.json +1 -0
  10. package/adapters/hermes/hitl-ingress/__init__.py +2 -2
  11. package/adapters/hermes/skills/orkastery-devmaster/SKILL.md +6 -5
  12. package/adapters/openclaw/README.md +35 -4
  13. package/adapters/openclaw/dist/hitl-ingress.js +2 -2
  14. package/adapters/openclaw/dist/index.js +47 -12
  15. package/adapters/openclaw/openclaw.plugin.json +5 -1
  16. package/adapters/openclaw/package.json +2 -2
  17. package/adapters/openclaw/src/hitl-ingress.ts +2 -2
  18. package/adapters/openclaw/src/index.ts +50 -12
  19. package/assets/orkmind_bridge.py +117 -9
  20. package/dist/adapters/claude-bg.js +59 -11
  21. package/dist/adapters/codex.js +4 -3
  22. package/dist/agents-md.js +1 -0
  23. package/dist/auditrun.js +6 -2
  24. package/dist/board.js +16 -14
  25. package/dist/branch-de-estado.js +66 -10
  26. package/dist/busca-semantica.js +52 -12
  27. package/dist/canarios-rm057.js +149 -0
  28. package/dist/canarios.js +2 -0
  29. package/dist/ci.js +8 -1
  30. package/dist/claim-lint.js +3 -1
  31. package/dist/claims.js +39 -1
  32. package/dist/company-brain-context.js +158 -22
  33. package/dist/docs.js +110 -3
  34. package/dist/doctor-pulse-cron.js +194 -0
  35. package/dist/doctor-rede.js +149 -0
  36. package/dist/doctor.js +294 -37
  37. package/dist/entrega-pr.js +27 -8
  38. package/dist/esgotamentos.js +181 -0
  39. package/dist/experiencia.js +12 -2
  40. package/dist/fabrica-estado.js +35 -9
  41. package/dist/fabrica-publicar.js +6 -0
  42. package/dist/fechamento.js +20 -66
  43. package/dist/fix.js +3 -3
  44. package/dist/forja.js +31 -7
  45. package/dist/gates.js +5 -1
  46. package/dist/hitl-contract.js +59 -4
  47. package/dist/hitl-curto.js +11 -6
  48. package/dist/hitl-gates.js +5 -0
  49. package/dist/hitl-lote.js +5 -2
  50. package/dist/hitl-resumo.js +33 -1
  51. package/dist/hitl-tempo-parado.js +201 -0
  52. package/dist/hitl-texto-livre.js +5 -5
  53. package/dist/horario.js +27 -0
  54. package/dist/hosts.js +59 -13
  55. package/dist/impedimento.js +205 -0
  56. package/dist/index.js +433 -88
  57. package/dist/indice-vetorial.js +87 -10
  58. package/dist/indice.js +3 -1
  59. package/dist/init.js +28 -5
  60. package/dist/integracoes-locais.js +4 -0
  61. package/dist/intelligence-graph-cli.js +267 -30
  62. package/dist/intelligence-graph-contexto.js +215 -0
  63. package/dist/intelligence-graph-contract.js +71 -12
  64. package/dist/intelligence-graph-extract-md.js +41 -23
  65. package/dist/intelligence-graph-extract-ts.js +181 -11
  66. package/dist/intelligence-graph-extract.js +186 -9
  67. package/dist/intelligence-graph-index.js +205 -24
  68. package/dist/intelligence-graph-parsers.js +6 -4
  69. package/dist/intelligence-graph-repo.js +10 -0
  70. package/dist/leases.js +680 -87
  71. package/dist/ledger-stats.js +15 -1
  72. package/dist/ledger.js +12 -0
  73. package/dist/licoes.js +6 -4
  74. package/dist/liveness.js +2 -1
  75. package/dist/maestro-runtime.js +7 -1
  76. package/dist/manifest.js +44 -1
  77. package/dist/master-nota.js +14 -5
  78. package/dist/master.js +129 -12
  79. package/dist/mcp-artifacts.js +36 -3
  80. package/dist/mcp-git.js +80 -15
  81. package/dist/mcp-grafo-worker.js +93 -0
  82. package/dist/mcp-grafo.js +279 -0
  83. package/dist/mcp-install.js +2 -1
  84. package/dist/mcp-server.js +23 -1
  85. package/dist/mcp-ship.js +2 -0
  86. package/dist/mcp-verify.js +3 -1
  87. package/dist/memoria.js +48 -6
  88. package/dist/network-roadmap.js +195 -20
  89. package/dist/ocupacao.js +17 -5
  90. package/dist/onboarding.js +9 -4
  91. package/dist/orkmind.js +90 -5
  92. package/dist/orquestracao.js +14 -11
  93. package/dist/parado-no-condutor.js +1089 -0
  94. package/dist/phase.js +68 -15
  95. package/dist/policies.js +79 -3
  96. package/dist/portfolio.js +4 -1
  97. package/dist/projeto-alvo.js +5 -1
  98. package/dist/prompts.js +36 -1
  99. package/dist/prova-ativacao.js +414 -0
  100. package/dist/pulse-delivery.js +60 -7
  101. package/dist/pulse.js +99 -3
  102. package/dist/ratelimit.js +20 -4
  103. package/dist/recall.js +3 -1
  104. package/dist/rede-adesao.js +399 -0
  105. package/dist/rede-forja.js +289 -0
  106. package/dist/rede-projetos.js +239 -0
  107. package/dist/rede-status.js +336 -0
  108. package/dist/rede.js +980 -0
  109. package/dist/retry.js +69 -2
  110. package/dist/roadmap-status.js +162 -7
  111. package/dist/runtime-context.js +5 -1
  112. package/dist/runtime-profiles.js +22 -1
  113. package/dist/saida-segura.js +58 -0
  114. package/dist/session-watcher-claude.js +113 -23
  115. package/dist/sessoes-contas.js +187 -0
  116. package/dist/sessoes-inventario.js +6 -12
  117. package/dist/sessoes.js +87 -3
  118. package/dist/ship.js +110 -20
  119. package/dist/thread.js +212 -14
  120. package/dist/util.js +71 -4
  121. package/dist/verify.js +2 -2
  122. package/dist/worktree.js +34 -8
  123. package/eval/casos/onboarding.json +1 -1
  124. package/eval/fixtures/fx-pedido-colado/caso.json +20 -0
  125. package/package.json +7 -3
  126. package/references/definition-of-done.md +1 -1
  127. package/skills/README.md +1 -1
  128. package/skills/core/onboarding/SKILL.md +3 -2
  129. package/skills/core/orkastery-bootstrap/SKILL.md +2 -2
  130. package/skills/phases/master-metrics/SKILL.md +8 -3
  131. package/skills/phases/ship-release/SKILL.md +6 -3
@@ -33,7 +33,10 @@ var __importStar = (this && this.__importStar) || (function () {
33
33
  };
34
34
  })();
35
35
  Object.defineProperty(exports, "__esModule", { value: true });
36
- exports.USO_DO_GRAFO = exports.AMOSTRA_SCHEMA = exports.STATUS_SCHEMA = void 0;
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
  /**
@@ -46,34 +49,96 @@ exports.executarGrafo = executarGrafo;
46
49
  * O argv chega cru e o parser e estrito: subcomando conhecido, opcao conhecida uma vez so, valor
47
50
  * onde a opcao pede valor e bandeira sem valor, numero exato de posicionais. Resposta sai 0, mesmo
48
51
  * vazia; erro tipado sai 1 e, com `--json`, vem como objeto.
52
+ *
53
+ * RM-031 KG5 (D4, D5): `--teto-bytes N` limita a resposta em bytes (JSON compacto), para quem a leva
54
+ * ao contexto de um agente, como as tools `ork_grafo_*` do MCP; sem a opcao, a saida e a de antes. Sem
55
+ * o indice do HEAD, a recusa diz se ha indice de outra revisao ou de outro extrator, e a correcao.
49
56
  */
50
57
  const node_crypto_1 = require("node:crypto");
51
58
  const fs = __importStar(require("node:fs"));
52
59
  const path = __importStar(require("node:path"));
53
60
  const intelligence_graph_contract_1 = require("./intelligence-graph-contract");
54
61
  const intelligence_graph_index_1 = require("./intelligence-graph-index");
62
+ const intelligence_graph_parsers_1 = require("./intelligence-graph-parsers");
55
63
  const intelligence_graph_query_1 = require("./intelligence-graph-query");
56
64
  const intelligence_graph_repo_1 = require("./intelligence-graph-repo");
65
+ const intelligence_graph_contexto_1 = require("./intelligence-graph-contexto");
57
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
+ }
58
112
  /** O mesmo schema da amostra do KG2: as amostras auditadas continuam validas. */
59
113
  exports.AMOSTRA_SCHEMA = 'ork.graph-edge-audit-sample/v0';
60
114
  exports.USO_DO_GRAFO = [
61
115
  'uso: ork grafo indexar [--verificar] [--forcar] [--json]',
62
116
  ' ork grafo status [--json]',
63
- ' ork grafo vizinhos <no> [--profundidade N] [--sentido entrada|saida|ambos] [--tipo T,...] [--limite N] [--json]',
64
- ' ork grafo chamadores <simbolo> [--profundidade N] [--limite N] [--json]',
65
- ' ork grafo importadores <arquivo|simbolo> [--profundidade N] [--limite N] [--json]',
66
- ' ork grafo caminho <de> <para> [--sentido saida|entrada|ambos] [--tipo T,...] [--json]',
117
+ ' ork grafo contexto <thread> [--json [--teto-bytes N]]',
118
+ ' ork grafo vizinhos <no> [--profundidade N] [--sentido entrada|saida|ambos] [--tipo T,...] [--limite N] [--json [--teto-bytes N]]',
119
+ ' ork grafo chamadores <simbolo> [--profundidade N] [--limite N] [--json [--teto-bytes N]]',
120
+ ' ork grafo importadores <arquivo|simbolo> [--profundidade N] [--limite N] [--json [--teto-bytes N]]',
121
+ ' ork grafo caminho <de> <para> [--sentido saida|entrada|ambos] [--tipo T,...] [--json [--teto-bytes N]]',
67
122
  ' ork grafo amostra [--por-estrato N] | ork grafo amostra --conferir ARQ [--json]',
68
123
  ' ork grafo limpar [--tudo] [--json]',
69
124
  ].join('\n');
125
+ /** KG5 (D5): a correcao das recusas de indice que a proxima indexacao resolve. */
126
+ exports.CORRECAO_DO_INDICE = 'ork grafo indexar';
127
+ /** KG5 (D5): o estado do indice do HEAD que cada recusa declara no JSON, ao lado da correcao. */
128
+ const ESTADO_DO_INDICE = Object.freeze({
129
+ 'grafo.indice.ausente': 'nao-indexado',
130
+ 'grafo.indice.outra-revisao': 'outra-revisao',
131
+ 'grafo.indice.outro-extrator': 'outro-extrator',
132
+ 'grafo.indice.corrompido': 'corrompido',
133
+ });
70
134
  const SUBCOMANDOS = Object.freeze({
71
135
  indexar: { posicionais: 0, bandeiras: ['verificar', 'forcar', 'json'], valores: [] },
72
136
  status: { posicionais: 0, bandeiras: ['json'], valores: [] },
73
- vizinhos: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'sentido', 'tipo', 'limite'] },
74
- chamadores: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'limite'] },
75
- importadores: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'limite'] },
76
- caminho: { posicionais: 2, bandeiras: ['json'], valores: ['sentido', 'tipo'] },
137
+ contexto: { posicionais: 1, bandeiras: ['json'], valores: ['teto-bytes'] },
138
+ vizinhos: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'sentido', 'tipo', 'limite', 'teto-bytes'] },
139
+ chamadores: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'limite', 'teto-bytes'] },
140
+ importadores: { posicionais: 1, bandeiras: ['json'], valores: ['profundidade', 'limite', 'teto-bytes'] },
141
+ caminho: { posicionais: 2, bandeiras: ['json'], valores: ['sentido', 'tipo', 'teto-bytes'] },
77
142
  amostra: { posicionais: 0, bandeiras: ['json'], valores: ['por-estrato', 'conferir'] },
78
143
  limpar: { posicionais: 0, bandeiras: ['tudo', 'json'], valores: [] },
79
144
  });
@@ -141,8 +206,40 @@ function sentido(p) {
141
206
  uso(`--sentido desconhecido: ${v}`);
142
207
  return v;
143
208
  }
209
+ /** KG5 (D4): o teto vale para o JSON, que e o que vai ao contexto de quem consulta. */
210
+ function tetoDeBytes(p) {
211
+ const teto = inteiro(p, 'teto-bytes');
212
+ if (teto === undefined)
213
+ return undefined;
214
+ if (!p.bandeiras.has('json'))
215
+ uso('--teto-bytes exige --json');
216
+ if (teto < 1)
217
+ uso('--teto-bytes precisa ser ao menos 1');
218
+ return teto;
219
+ }
144
220
  const mb = (bytes) => `${(bytes / 1024 / 1024).toFixed(1)} MB`;
145
221
  const contagem = (o) => Object.entries(o).map(([k, v]) => `${k}=${v}`).join(' ');
222
+ /** Ate cinco caminhos e quantos ficaram de fora. */
223
+ const alguns = (lista) => (lista.length ? ` (${lista.slice(0, 5).join(', ')}${lista.length > 5 ? ` e mais ${lista.length - 5}` : ''})` : '');
224
+ /** KG4: como a extracao rodou, a base e o que veio dela, ou por que foi completa. */
225
+ function linhasDaExtracao(r) {
226
+ if (r.modo === null)
227
+ return [];
228
+ const a = r.reaproveitamento;
229
+ if (r.modo === 'incremental' && r.base && a) {
230
+ // Desestruturado: o lint de horario le o campo de nome curto do TypeScript como instante.
231
+ const { ts: doTs, md: doMd, mudanca } = a;
232
+ const ts = doTs.modo === 'inteiro' ? `TypeScript inteiro (${doTs.motivo})`
233
+ : doTs.modo === 'parcial' ? `TypeScript parcial, ${doTs.reextraidos.length} reextraido(s)${alguns(doTs.reextraidos)} num programa de ${doTs.programa}, ${doTs.reaproveitados} da base`
234
+ : `TypeScript todo da base (${doTs.reaproveitados})`;
235
+ return [
236
+ ` extracao incremental a partir do indice de ${r.base.revision.slice(0, 12)}: ${mudanca.alterados} alterado(s), ${mudanca.novos} novo(s), ${mudanca.removidos} removido(s)`,
237
+ ` ${ts}`,
238
+ ` Markdown: ${doMd.reextraidos.length} reextraido(s)${alguns(doMd.reextraidos)}, ${doMd.reaproveitados} da base`,
239
+ ];
240
+ }
241
+ return [` extracao completa${r.motivo_completo ? `: ${r.motivo_completo}` : ''}`];
242
+ }
146
243
  function indexar(ctx, p) {
147
244
  const r = (0, intelligence_graph_index_1.construirIndice)(ctx, { verificar: p.bandeiras.has('verificar'), forcar: p.bandeiras.has('forcar') });
148
245
  if (p.bandeiras.has('json')) {
@@ -159,12 +256,18 @@ function indexar(ctx, p) {
159
256
  ` conferencia conferirFontes verificada: ${m.conferencia.fontes} fontes e ${m.conferencia.evidencias} evidencias contra os bytes`,
160
257
  ` nos ${contagem(m.contagens.nos)}`,
161
258
  ` arestas ${contagem(m.contagens.arestas)}`,
162
- ` lugar ${r.dir} (${mb(m.graph_bytes + m.report_bytes)})`,
259
+ ` lugar ${r.dir} (${mb(m.graph_bytes + m.report_bytes + m.units_bytes)})`,
260
+ ...linhasDaExtracao(r),
163
261
  ];
164
262
  if (r.motivo)
165
263
  linhas.push(` motivo ${r.motivo}`);
166
- for (const d of r.determinismo ?? [])
167
- linhas.push(` determinismo ${d.ordem}: digest ${d.digest.slice(0, 16)}, relatorio ${d.relatorio.slice(0, 16)}, ${d.igual ? 'igual' : 'DIFERENTE'}`);
264
+ for (const d of r.determinismo ?? []) {
265
+ linhas.push(` determinismo ${d.ordem}: digest ${d.digest.slice(0, 16)}, relatorio ${d.relatorio.slice(0, 16)}, unidades ${d.unidades.slice(0, 16)}, ${d.igual ? 'igual' : 'DIFERENTE'}`);
266
+ }
267
+ if (r.incremental)
268
+ linhas.push(` incremental a partir do indice de ${r.incremental.base.slice(0, 12)}: ${r.incremental.igual ? 'igual a completa nos quatro arquivos' : 'DIFERENTE'}`);
269
+ else if (r.determinismo)
270
+ linhas.push(` incremental nao conferido: ${r.motivo_completo ?? 'sem base'}`);
168
271
  linhas.push(` tempo ${r.ms} ms`);
169
272
  if (r.determinismo)
170
273
  linhas.push('aprovado');
@@ -187,9 +290,11 @@ function status(ctx, p) {
187
290
  const doHead = chave ? estado.indices.find((i) => i.chave === chave) ?? null : null;
188
291
  // Sem analisadores nao ha chave: o indice do HEAD fica indisponivel, nunca "ausente" (indexar tambem nao roda).
189
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;
190
295
  const r = {
191
296
  schema: exports.STATUS_SCHEMA, head: arvore.head, arvore: arvore.motivo === null ? 'limpa' : arvore.motivo, chave_do_head: chave,
192
- indice_do_head: indiceDoHeadEstado, analisadores, erro,
297
+ indice_do_head: indiceDoHeadEstado, analisadores, erro, correcao,
193
298
  dir: estado.dir, bytes: estado.bytes, indices: estado.indices, sobras: estado.sobras,
194
299
  };
195
300
  if (p.bandeiras.has('json')) {
@@ -199,8 +304,11 @@ function status(ctx, p) {
199
304
  const linhas = [
200
305
  `grafo: HEAD ${arvore.head ? arvore.head.slice(0, 12) : 'sem commit'}, arvore ${r.arvore}`,
201
306
  ` indice do HEAD ${chave ?? 'indisponivel'} ${r.indice_do_head === 'ausente' ? '(ausente: rode ork grafo indexar)'
202
- : r.indice_do_head === 'indisponivel' ? '(sem os analisadores nesta instalacao, nem a consulta nem o indexar rodam)' : `(${r.indice_do_head})`}`,
203
- ` 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}`] : []),
204
312
  ` pasta ${estado.dir ?? 'ainda nao criada'} (${estado.indices.length} indice(s), ${mb(estado.bytes)})`,
205
313
  ];
206
314
  for (const i of estado.indices) {
@@ -211,9 +319,65 @@ function status(ctx, p) {
211
319
  ctx.escrever(linhas.join('\n'));
212
320
  return 0;
213
321
  }
322
+ /**
323
+ * KG5 (CHECK rodada 1, A1): o que difere entre o extrator do indice guardado e o de quem consulta. A
324
+ * versao do Node entra no rotulo do analisador de JavaScript, entao outra instalacao ou outro Node dao
325
+ * outra chave com o mesmo conteudo.
326
+ */
327
+ function diferencasDoExtrator(guardado, atual) {
328
+ const r = [];
329
+ for (const k of Object.keys(atual.analisadores).sort(intelligence_graph_contract_1.compararUtf8)) {
330
+ const antes = guardado.analisadores?.[k];
331
+ if (antes !== atual.analisadores[k])
332
+ r.push(`${k} ${antes ?? '?'} no indice, ${atual.analisadores[k]} aqui`);
333
+ }
334
+ if ((0, intelligence_graph_contract_1.canonico)(guardado.pacotes) !== (0, intelligence_graph_contract_1.canonico)(atual.pacotes))
335
+ r.push('pacotes dos analisadores');
336
+ if (guardado.codigo !== atual.codigo)
337
+ r.push('codigo do extrator');
338
+ return r;
339
+ }
340
+ /**
341
+ * KG5 (D5): sem o indice do HEAD, diz o que ha no lugar. Indice guardado do mesmo repositorio e da
342
+ * revisao do HEAD com outra chave e de outro extrator (instalacao, Node, analisadores ou codigo do
343
+ * extrator mudaram), e a recusa diz o que mudou; de outra revisao, e indice velho ou de outra arvore.
344
+ * Nos dois casos a correcao e indexar de novo, com a instalacao de quem consulta; sem nenhum, fica a
345
+ * recusa original (nao indexado). Nunca responde por um indice que nao e o do HEAD.
346
+ */
347
+ function semIndiceDoHead(ctx, original) {
348
+ const arvore = (0, intelligence_graph_repo_1.revisaoDaArvore)(ctx.raiz);
349
+ if (!arvore.head)
350
+ throw original;
351
+ const perfil = (0, intelligence_graph_index_1.perfilDoIndice)(arvore.raiz, undefined, ctx.repositorio ? { repository_id: ctx.repositorio } : {});
352
+ const chave = (0, intelligence_graph_index_1.chaveDoIndice)(arvore.head, perfil), head = arvore.head.slice(0, 12);
353
+ const guardados = (0, intelligence_graph_index_1.estadoDosIndices)(ctx).indices
354
+ .filter((i) => i.problema === null && i.chave !== chave && i.repository_id === perfil.repository_id && i.revision !== null);
355
+ const doHead = guardados.find((i) => i.revision === arvore.head);
356
+ if (doHead) {
357
+ const diferencas = diferencasDoExtrator((0, intelligence_graph_index_1.lerIndice)(ctx, doHead.chave, { grafo: false }).manifesto, perfil);
358
+ throw new Error(`grafo.indice.outro-extrator: ha indice do HEAD ${head} de outro extrator (${diferencas.join('; ') || 'perfil diferente'}); `
359
+ + `rode ${exports.CORRECAO_DO_INDICE} com a mesma instalacao do ork e o mesmo Node de quem consulta (o servidor MCP consulta com a dele)`);
360
+ }
361
+ if (guardados.length) {
362
+ const revisoes = [...new Set(guardados.map((i) => i.revision.slice(0, 12)))].sort(intelligence_graph_contract_1.compararUtf8);
363
+ // CHECK rodada 2 (N7): a ressalva da instalacao ja na primeira recusa, a de cada commit do GO.
364
+ throw new Error(`grafo.indice.outra-revisao: o HEAD ${head} nao tem indice; o guardado e de outra revisao${alguns(revisoes)}; `
365
+ + `rode ${exports.CORRECAO_DO_INDICE} com a mesma instalacao do ork e o mesmo Node de quem consulta`);
366
+ }
367
+ throw original;
368
+ }
214
369
  /** O indice do HEAD pronto para consulta, com a concessao local e o cabecalho; `grafo` ja vem filtrado por ela. */
215
370
  function consultavel(ctx) {
216
- const { arvore, perfil, chave, indice } = (0, intelligence_graph_index_1.indiceDoHead)(ctx);
371
+ let doHead;
372
+ try {
373
+ doHead = (0, intelligence_graph_index_1.indiceDoHead)(ctx);
374
+ }
375
+ catch (e) {
376
+ if (String(e.message).startsWith('grafo.indice.ausente'))
377
+ semIndiceDoHead(ctx, e);
378
+ throw e;
379
+ }
380
+ const { arvore, perfil, chave, indice } = doHead;
217
381
  const concessao = (0, intelligence_graph_index_1.concessaoLocal)(arvore.raiz, perfil);
218
382
  const grafo = (0, intelligence_graph_query_1.filtrarGrafo)(indice.grafo, concessao), m = indice.manifesto;
219
383
  const cabecalho = {
@@ -227,18 +391,82 @@ function responder(ctx, p, r) {
227
391
  ctx.escrever(p.bandeiras.has('json') ? (0, intelligence_graph_query_1.jsonDaResposta)(r) : (0, intelligence_graph_query_1.textoDaResposta)(r));
228
392
  return 0;
229
393
  }
394
+ /**
395
+ * KG5 (D4): a resposta em JSON compacto que cabe em `teto` bytes. Se a pedida nao cabe, vale o maior
396
+ * limite que cabe: as arestas mais longe do alvo saem primeiro, pela mesma ordem do `--limite`, e
397
+ * `consulta.limite` passa a ser o efetivo. O tamanho cresce com o limite (cada aresta a mais so soma
398
+ * bytes), entao a busca binaria acha o maior que cabe, sempre o mesmo. Aresta nunca sai sem toda a
399
+ * evidencia; o caminho nao se corta.
400
+ */
401
+ function respostaComTeto(perguntar, limite, teto) {
402
+ const pedida = perguntar(limite);
403
+ const limitePedido = pedida.consulta.limite;
404
+ const comTeto = (r, cortado) => JSON.stringify({ ...r, teto: { bytes: teto, limite_pedido: limitePedido, cortado } });
405
+ const inteira = comTeto(pedida, false);
406
+ if (Buffer.byteLength(inteira) <= teto)
407
+ return inteira;
408
+ const ehCaminho = pedida.consulta.tipo === 'caminho';
409
+ // So chega aqui o que nao cabe nem com a aresta mais perto do alvo: estreitar a consulta nao ajuda.
410
+ const excedido = (resposta, bytes) => {
411
+ throw new intelligence_graph_query_1.ErroDeConsulta('grafo.consulta.teto-excedido', `${resposta} tem ${bytes} bytes, acima do teto de ${teto}; ${ehCaminho ? 'o caminho nao se corta: ' : ''}use --teto-bytes maior`);
412
+ };
413
+ if (ehCaminho)
414
+ excedido(`o caminho de ${pedida.arestas.length} passo(s)`, Buffer.byteLength(inteira));
415
+ if (pedida.arestas.length <= 1)
416
+ excedido(`a resposta com ${pedida.arestas.length} aresta(s)`, Buffer.byteLength(inteira));
417
+ let menor = 1, maior = pedida.arestas.length - 1, melhor = null;
418
+ while (menor <= maior) {
419
+ const k = Math.floor((menor + maior) / 2), texto = comTeto(perguntar(k), true);
420
+ if (Buffer.byteLength(texto) <= teto) {
421
+ melhor = texto;
422
+ menor = k + 1;
423
+ }
424
+ else
425
+ maior = k - 1;
426
+ }
427
+ return melhor ?? excedido('a resposta com 1 aresta', Buffer.byteLength(comTeto(perguntar(1), true)));
428
+ }
230
429
  function consultar(ctx, p) {
231
430
  // As opcoes sao conferidas antes de carregar o indice: erro de uso nao custa a leitura do grafo.
232
431
  const [a, b] = p.posicionais;
233
- const profundidade = inteiro(p, 'profundidade'), limite = inteiro(p, 'limite'), s = sentido(p), t = tipos(p);
432
+ const profundidade = inteiro(p, 'profundidade'), limite = inteiro(p, 'limite'), s = sentido(p), t = tipos(p), teto = tetoDeBytes(p);
234
433
  const { g, cabecalho } = consultavel(ctx);
235
- if (p.sub === 'vizinhos')
236
- return responder(ctx, p, (0, intelligence_graph_query_1.vizinhos)(g, cabecalho, a, { profundidade, limite, sentido: s, tipos: t }));
237
- if (p.sub === 'chamadores')
238
- return responder(ctx, p, (0, intelligence_graph_query_1.chamadores)(g, cabecalho, a, { profundidade, limite }));
239
- if (p.sub === 'importadores')
240
- return responder(ctx, p, (0, intelligence_graph_query_1.importadores)(g, cabecalho, a, { profundidade, limite }));
241
- return responder(ctx, p, (0, intelligence_graph_query_1.caminho)(g, cabecalho, a, b, { sentido: s, tipos: t }));
434
+ const perguntar = (lim) => {
435
+ if (p.sub === 'vizinhos')
436
+ return (0, intelligence_graph_query_1.vizinhos)(g, cabecalho, a, { profundidade, limite: lim, sentido: s, tipos: t });
437
+ if (p.sub === 'chamadores')
438
+ return (0, intelligence_graph_query_1.chamadores)(g, cabecalho, a, { profundidade, limite: lim });
439
+ if (p.sub === 'importadores')
440
+ return (0, intelligence_graph_query_1.importadores)(g, cabecalho, a, { profundidade, limite: lim });
441
+ return (0, intelligence_graph_query_1.caminho)(g, cabecalho, a, b, { sentido: s, tipos: t });
442
+ };
443
+ if (teto === undefined)
444
+ return responder(ctx, p, perguntar(limite));
445
+ ctx.escrever(respostaComTeto(perguntar, limite, teto));
446
+ return 0;
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;
242
470
  }
243
471
  const sha256 = (b) => (0, node_crypto_1.createHash)('sha256').update(b).digest('hex');
244
472
  /**
@@ -392,15 +620,22 @@ function limpar(ctx, p) {
392
620
  ctx.escrever([`grafo limpar: ${r.removidos.length} removido(s), ${mb(r.bytes)}`, ...r.removidos.map((x) => ` ${x.nome} ${mb(x.bytes)}`)].join('\n'));
393
621
  return 0;
394
622
  }
395
- const erroEmJson = (ctx, e) => {
623
+ /** KG5 (D4, D5): com `--teto-bytes` o erro tambem sai compacto; recusa de indice traz o estado e a correcao. */
624
+ const erroEmJson = (ctx, e, compacto, schema = intelligence_graph_query_1.CONSULTA_SCHEMA) => {
396
625
  const [codigo, ...detalhe] = String(e.message).split('\n')[0].split(': ');
397
- ctx.escrever(JSON.stringify({
398
- schema: intelligence_graph_query_1.CONSULTA_SCHEMA, erro: { codigo, detalhe: detalhe.join(': ') || null, candidatos: e instanceof intelligence_graph_query_1.ErroDeConsulta ? e.candidatos : [] },
399
- }, null, 2));
626
+ const doIndice = Object.prototype.hasOwnProperty.call(ESTADO_DO_INDICE, codigo)
627
+ ? { estado_do_indice: ESTADO_DO_INDICE[codigo], correcao: exports.CORRECAO_DO_INDICE } : {};
628
+ const r = {
629
+ schema,
630
+ erro: { codigo, detalhe: detalhe.join(': ') || null, candidatos: e instanceof intelligence_graph_query_1.ErroDeConsulta ? e.candidatos : [], ...doIndice },
631
+ };
632
+ ctx.escrever(compacto ? JSON.stringify(r) : JSON.stringify(r, null, 2));
400
633
  return 1;
401
634
  };
402
635
  /** `ork grafo <subcomando> ...`: devolve o codigo de saida; sem `--json`, o erro de uso ou tipado e lancado. */
403
636
  function executarGrafo(argv, ctx) {
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='));
404
639
  let p;
405
640
  try {
406
641
  p = lerPedido(argv);
@@ -408,7 +643,7 @@ function executarGrafo(argv, ctx) {
408
643
  catch (e) {
409
644
  // Com `--json` no argv, tambem o erro de uso sai como objeto, como os demais.
410
645
  if (argv.includes('--json'))
411
- return erroEmJson(ctx, e);
646
+ return erroEmJson(ctx, e, compacto, schema);
412
647
  throw e;
413
648
  }
414
649
  try {
@@ -420,11 +655,13 @@ function executarGrafo(argv, ctx) {
420
655
  return amostra(ctx, p);
421
656
  if (p.sub === 'limpar')
422
657
  return limpar(ctx, p);
658
+ if (p.sub === 'contexto')
659
+ return contexto(ctx, p);
423
660
  return consultar(ctx, p);
424
661
  }
425
662
  catch (e) {
426
663
  if (!p.bandeiras.has('json'))
427
664
  throw e;
428
- return erroEmJson(ctx, e);
665
+ return erroEmJson(ctx, e, compacto, schema);
429
666
  }
430
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
+ }