auroq-os 2.5.11 → 2.5.12

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.
@@ -123,9 +123,8 @@ class PipelineMetrics {
123
123
  * Record that a layer encountered an error.
124
124
  *
125
125
  * @param {string} name - Layer name
126
- * @param {Error} error - The error object
127
126
  */
128
- errorLayer(name, error) {
127
+ errorLayer(name) {
129
128
  const existing = this.layers[name] || {};
130
129
  if (existing.start) {
131
130
  const endTime = process.hrtime.bigint();
@@ -135,7 +134,7 @@ class PipelineMetrics {
135
134
  this.layers[name] = {
136
135
  ...existing,
137
136
  status: 'error',
138
- error: error && error.message ? error.message : String(error),
137
+ errorCode: 'SYNAPSE_LAYER_FAILED',
139
138
  };
140
139
  }
141
140
 
@@ -170,8 +169,27 @@ class PipelineMetrics {
170
169
  // SynapseEngine
171
170
  // ---------------------------------------------------------------------------
172
171
 
173
- /** Hard pipeline timeout in milliseconds. */
172
+ /** Cooperative budget between layers; synchronous work cannot be interrupted. */
174
173
  const PIPELINE_TIMEOUT_MS = 100;
174
+ // Keep substantial headroom below the outer hook's 5000 ms safety timeout.
175
+ const MAX_PIPELINE_TIMEOUT_MS = 1000;
176
+ const SYNAPSE_PIPELINE_TIMEOUT_ENV = 'AUROQ_SYNAPSE_PIPELINE_TIMEOUT_MS';
177
+
178
+ /** Resolve a short pipeline budget. Invalid values preserve the safe default. */
179
+ function resolvePipelineTimeoutMs(config = {}) {
180
+ const fromEnv = process.env[SYNAPSE_PIPELINE_TIMEOUT_ENV];
181
+ const value = fromEnv !== undefined && fromEnv !== ''
182
+ ? fromEnv
183
+ : config.synapse?.pipelineTimeoutMs;
184
+ if (value === undefined || value === null) return PIPELINE_TIMEOUT_MS;
185
+ const timeout = typeof value === 'number' || typeof value === 'string' ? Number(value) : NaN;
186
+ if (!Number.isInteger(timeout) || timeout <= 0 || timeout > MAX_PIPELINE_TIMEOUT_MS) {
187
+ // Do not echo invalid values: environment/config may contain private text.
188
+ console.warn('[synapse:engine] SYNAPSE_INVALID_PIPELINE_TIMEOUT');
189
+ return PIPELINE_TIMEOUT_MS;
190
+ }
191
+ return timeout;
192
+ }
175
193
 
176
194
  /**
177
195
  * NOG-18: Default active layers (L0-L2 only).
@@ -250,8 +268,13 @@ class SynapseEngine {
250
268
  async process(prompt, session, processConfig) {
251
269
  const safeProcessConfig = (processConfig && typeof processConfig === 'object') ? processConfig : {};
252
270
  const mergedConfig = { ...this.config, ...safeProcessConfig };
271
+ mergedConfig.synapse = { ...(this.config.synapse || {}), ...(safeProcessConfig.synapse || {}) };
272
+ const pipelineTimeoutMs = resolvePipelineTimeoutMs(mergedConfig);
273
+ const pipelineNow = typeof safeProcessConfig.nowNs === 'function'
274
+ ? safeProcessConfig.nowNs
275
+ : process.hrtime.bigint;
253
276
  const metrics = new PipelineMetrics();
254
- metrics.totalStart = process.hrtime.bigint();
277
+ metrics.totalStart = pipelineNow();
255
278
 
256
279
  // 1. Calculate bracket (or use fixed layers in non-legacy mode)
257
280
  const promptCount = (session && session.prompt_count) || 0;
@@ -266,7 +289,7 @@ class SynapseEngine {
266
289
 
267
290
  // Guard: no layer config (invalid bracket — should not happen)
268
291
  if (!layerConfig) {
269
- metrics.totalEnd = process.hrtime.bigint();
292
+ metrics.totalEnd = pipelineNow();
270
293
  return { xml: '', metrics: metrics.getSummary() };
271
294
  }
272
295
  activeLayers = layerConfig.layers;
@@ -291,8 +314,8 @@ class SynapseEngine {
291
314
  continue;
292
315
  }
293
316
 
294
- // Check hard pipeline timeout (convert hrtime to ms for comparison)
295
- if (Number(process.hrtime.bigint() - metrics.totalStart) / 1e6 > PIPELINE_TIMEOUT_MS) {
317
+ // Cooperative check between layers; the host owns interruption of a stuck process.
318
+ if (Number(pipelineNow() - metrics.totalStart) / 1e6 >= pipelineTimeoutMs) {
296
319
  // Log remaining layers as skipped
297
320
  const remaining = this.layers.slice(this.layers.indexOf(layer));
298
321
  for (const r of remaining) {
@@ -314,14 +337,28 @@ class SynapseEngine {
314
337
  previousLayers,
315
338
  });
316
339
 
317
- const result = layer._safeProcess(context);
340
+ let result;
341
+ try {
342
+ result = layer._safeProcess(context);
343
+ } catch (_error) {
344
+ metrics.errorLayer(layer.name);
345
+ continue;
346
+ }
318
347
 
319
348
  if (result && Array.isArray(result.rules)) {
320
349
  metrics.endLayer(layer.name, result.rules.length);
321
350
  results.push(result);
322
351
  previousLayers.push(result);
323
352
  } else if (result === null || result === undefined) {
324
- metrics.skipLayer(layer.name, 'Returned null');
353
+ try {
354
+ if (typeof layer.getLastError === 'function' && layer.getLastError()) {
355
+ metrics.errorLayer(layer.name);
356
+ } else {
357
+ metrics.skipLayer(layer.name, 'Returned null');
358
+ }
359
+ } catch (_error) {
360
+ metrics.errorLayer(layer.name);
361
+ }
325
362
  } else {
326
363
  metrics.skipLayer(layer.name, 'Invalid result format');
327
364
  }
@@ -341,8 +378,9 @@ class SynapseEngine {
341
378
  }
342
379
  }
343
380
 
344
- metrics.totalEnd = process.hrtime.bigint();
381
+ metrics.totalEnd = pipelineNow();
345
382
  const summary = metrics.getSummary();
383
+ summary.pipeline_timeout_ms = pipelineTimeoutMs;
346
384
 
347
385
  // Persist hook metrics (fire-and-forget)
348
386
  this._persistHookMetrics(summary, bracket, mergedConfig);
@@ -382,6 +420,7 @@ class SynapseEngine {
382
420
  const hookBootMs = hookBootTime ? Number(process.hrtime.bigint() - hookBootTime) / 1e6 : 0;
383
421
  const data = {
384
422
  totalDuration: summary.total_ms,
423
+ pipelineTimeoutMs: summary.pipeline_timeout_ms,
385
424
  hookBootMs,
386
425
  bracket,
387
426
  layersLoaded: summary.layers_loaded,
@@ -397,6 +436,7 @@ class SynapseEngine {
397
436
  duration: info.duration || 0,
398
437
  status: info.status || 'unknown',
399
438
  rules: info.rules || 0,
439
+ ...(info.errorCode ? { errorCode: info.errorCode } : {}),
400
440
  };
401
441
  }
402
442
  fs.writeFileSync(
@@ -413,4 +453,7 @@ module.exports = {
413
453
  SynapseEngine,
414
454
  PipelineMetrics,
415
455
  PIPELINE_TIMEOUT_MS,
456
+ MAX_PIPELINE_TIMEOUT_MS,
457
+ SYNAPSE_PIPELINE_TIMEOUT_ENV,
458
+ resolvePipelineTimeoutMs,
416
459
  };
@@ -34,6 +34,7 @@ class LayerProcessor {
34
34
  this.name = name;
35
35
  this.layer = layer;
36
36
  this.timeout = timeout;
37
+ this._lastError = null;
37
38
  }
38
39
 
39
40
  /**
@@ -65,6 +66,7 @@ class LayerProcessor {
65
66
  */
66
67
  _safeProcess(context) {
67
68
  const start = Date.now();
69
+ this._lastError = null;
68
70
  try {
69
71
  const result = this.process(context);
70
72
  const elapsed = Date.now() - start;
@@ -72,11 +74,18 @@ class LayerProcessor {
72
74
  console.warn(`[synapse:${this.name}] Warning: Layer exceeded timeout (${elapsed}ms > ${this.timeout}ms)`);
73
75
  }
74
76
  return result;
75
- } catch (error) {
76
- console.warn(`[synapse:${this.name}] Error: ${error.message}`);
77
+ } catch (_error) {
78
+ // Raw error messages/stacks can contain prompt or domain content.
79
+ this._lastError = new Error('SYNAPSE_LAYER_FAILED');
80
+ console.warn(`[synapse:${this.name}] SYNAPSE_LAYER_FAILED`);
77
81
  return null;
78
82
  }
79
83
  }
84
+
85
+ /** Return a content-free failure marker for the engine's metrics. */
86
+ getLastError() {
87
+ return this._lastError;
88
+ }
80
89
  }
81
90
 
82
91
  module.exports = LayerProcessor;
@@ -6,25 +6,29 @@ const fs = require('fs');
6
6
  const DEFAULT_STALE_TTL_HOURS = 168; // 7 days
7
7
 
8
8
  /**
9
- * Read stale session TTL from core-config.yaml.
10
- * Falls back to DEFAULT_STALE_TTL_HOURS (168h = 7 days).
9
+ * Read optional project configuration without disclosing parse failures.
11
10
  *
12
11
  * @param {string} cwd - Working directory
13
- * @returns {number} TTL in hours
12
+ * @returns {object} Configuration or an empty object
14
13
  */
15
- function getStaleSessionTTL(cwd) {
14
+ function loadCoreConfig(cwd) {
16
15
  try {
17
- const yaml = require('js-yaml');
18
16
  const configPath = path.join(cwd, '.auroq-core', 'core-config.yaml');
19
- if (!fs.existsSync(configPath)) return DEFAULT_STALE_TTL_HOURS;
17
+ if (!fs.existsSync(configPath)) return {};
18
+ const yaml = require('js-yaml');
20
19
  const config = yaml.load(fs.readFileSync(configPath, 'utf8'));
21
- const ttl = config && config.synapse && config.synapse.session && config.synapse.session.staleTTLHours;
22
- return typeof ttl === 'number' && ttl > 0 ? ttl : DEFAULT_STALE_TTL_HOURS;
20
+ return config && typeof config === 'object' ? config : {};
23
21
  } catch (_err) {
24
- return DEFAULT_STALE_TTL_HOURS;
22
+ // YAML errors can contain configuration values; never log their text.
23
+ return {};
25
24
  }
26
25
  }
27
26
 
27
+ function getStaleSessionTTL(config) {
28
+ const ttl = config && config.synapse && config.synapse.session && config.synapse.session.staleTTLHours;
29
+ return typeof ttl === 'number' && ttl > 0 ? ttl : DEFAULT_STALE_TTL_HOURS;
30
+ }
31
+
28
32
  /**
29
33
  * Resolve runtime dependencies for Synapse hook execution.
30
34
  *
@@ -46,7 +50,7 @@ function resolveHookRuntime(input) {
46
50
  if (!fs.existsSync(synapsePath)) return null;
47
51
 
48
52
  try {
49
- const { loadSession, cleanStaleSessions } = require(
53
+ const { loadSession, createSession, cleanStaleSessions } = require(
50
54
  path.join(cwd, '.auroq-core', 'core', 'synapse', 'session', 'session-manager.js'),
51
55
  );
52
56
  const { SynapseEngine } = require(
@@ -54,13 +58,21 @@ function resolveHookRuntime(input) {
54
58
  );
55
59
 
56
60
  const sessionsDir = path.join(synapsePath, 'sessions');
57
- const session = loadSession(sessionId, sessionsDir) || { prompt_count: 0 };
58
- const engine = new SynapseEngine(synapsePath);
61
+ let session = sessionId ? loadSession(sessionId, sessionsDir) : null;
62
+ // loadSession validates the ID before we use it as a filename. Create only
63
+ // a missing session: an unreadable/corrupt existing file is kept intact.
64
+ if (!session && sessionId && !fs.existsSync(path.join(sessionsDir, `${sessionId}.json`))) {
65
+ session = createSession(sessionId, cwd, sessionsDir);
66
+ }
67
+ const shouldCleanStaleSessions = session?.prompt_count === 0;
68
+ session = session || { prompt_count: 0 };
69
+ const coreConfig = loadCoreConfig(cwd);
70
+ const engine = new SynapseEngine(synapsePath, { synapse: coreConfig.synapse || {} });
59
71
 
60
72
  // AC3: Run cleanup on first prompt only (fire-and-forget)
61
- if (session.prompt_count === 0) {
73
+ if (shouldCleanStaleSessions) {
62
74
  try {
63
- const ttlHours = getStaleSessionTTL(cwd);
75
+ const ttlHours = getStaleSessionTTL(coreConfig);
64
76
  const removed = cleanStaleSessions(sessionsDir, ttlHours);
65
77
  if (removed > 0 && process.env.DEBUG === '1') {
66
78
  console.error(`[hook-runtime] Cleaned ${removed} stale session(s) (TTL: ${ttlHours}h)`);
@@ -73,7 +85,7 @@ function resolveHookRuntime(input) {
73
85
  return { engine, session, sessionId, sessionsDir, cwd };
74
86
  } catch (error) {
75
87
  if (process.env.DEBUG === '1') {
76
- console.error(`[hook-runtime] Failed to resolve runtime: ${error.message}`);
88
+ console.error('[hook-runtime] SYNAPSE_RUNTIME_FAILED');
77
89
  }
78
90
  return null;
79
91
  }
@@ -1,7 +1,7 @@
1
1
  project:
2
2
  name: "Auroq OS"
3
3
  type: greenfield
4
- version: 2.5.11
4
+ version: 2.5.12
5
5
  installedAt: '2026-03-16'
6
6
 
7
7
  ide:
package/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 2.5.12 — 2026-09-21
2
+
3
+ - SYNAPSE persiste a sessao desde o primeiro prompt, limita o orcamento total entre camadas e distingue falhas sem expor mensagens/conteudo privado. Mantem manifest, eventos dos hosts e camadas opcionais desligadas.
4
+ - Pacote real passa por inventario SHA-256 apos a casca, arquivos obrigatorios/proibidos e referencias locais do runtime. O empacotamento restaura o checkout, inclusive em falhas. Integridade de conteudo nao equivale a assinatura de autoria.
5
+ - Atualiza js-yaml para 4.3.2, corrigindo avisos de consumo excessivo de CPU no parser usado pelo motor.
6
+ - Manutencao reconhece as versoes oficiais anteriores do SYNAPSE e preserva arquivos personalizados e dados do negocio. Mantem arcane-mcp@0.6.1.
7
+ - Instaladores preparados para esta versao; copias publicas so sao promovidas depois dos gates nativos macOS/Windows Node 22/24 e da verificacao no npm. Maquinas existentes atualizam pelo fluxo de manutencao.
8
+
1
9
  ## 2.5.11 — 2026-09-21
2
10
 
3
11
  ### O diagnostico passa a dizer o que achou (EMB-050)
package/README.md CHANGED
@@ -228,14 +228,14 @@ agents/ → Seu exercito (companion, workers, minds, squads)
228
228
  *Auroq OS — by Euriler Jube / Arka*
229
229
 
230
230
 
231
- ## Observabilidade do motor (candidato 2.5.2)
231
+ ## Observabilidade e integridade do motor
232
232
 
233
- Apos publicacao, `npx -y auroq-os@2.5.11 doctor` mostra a fila local segura e os comandos pinados de
234
- manutencao/diagnostico. `npx -y auroq-os@2.5.11 manutencao` preserva o caminho existente
233
+ `npx -y auroq-os@2.5.12 doctor` mostra a fila local segura e os comandos pinados de
234
+ manutencao/diagnostico. `npx -y auroq-os@2.5.12 manutencao` preserva o caminho existente
235
235
  de consentimento legado, studentFiles, customizacoes e dados do negocio. Nao ha
236
236
  migracao forcada. O registro MCP aponta para `arcane-mcp@0.6.1`, publicado no npm em
237
237
  21/09/2026; `npm run release:verify` recusa publicar com um pin que o npm nao resolva.
238
- Os instaladores oficiais seguem em 2.5.10; promover pra 2.5.11 e ato explicito, depois da publicacao e da verificacao do tarball.
238
+ A versao 2.5.12 so deve chegar aos instaladores publicos apos os gates nativos e a verificacao do tarball publicado. O pacote inclui `lib/runtime-integrity.json`: inventario SHA-256 gerado depois da montagem da casca, verificavel com `npm run integrity:verify` dentro do pacote extraido. Ele comprova consistencia do conteudo; nao e uma assinatura de autoria.
239
239
 
240
240
  O CLI inicia captura antes das dependencias pesadas e registra resultado ao sair.
241
241
  Eventos com identidade ja existente podem seguir pela rota autenticada na proxima
@@ -254,8 +254,7 @@ instalacao/manutencao/doctor/login/register-mcp. Bootstrap conversacional e o tr
254
254
  final do squad nao sao inferidos como sucesso por sair do CLI ou preparar arquivos.
255
255
  Antes de o script chegar ou em computador que nunca executa nosso codigo, nao ha sinal.
256
256
  A rota events continua autenticada, limitada e idempotente. A plataforma deve receber
257
- a whitelist antes de distribuir/promover os clientes; os instaladores sincronizados
258
- mantem o pin 2.5.1 publicado ate a promocao npm ser possivel. Nao precisa migration adicional.
257
+ a whitelist antes de distribuir/promover os clientes; a promocao dos instaladores sincronizados depende da publicacao e verificacao da versao fixada. Nao precisa migration adicional.
259
258
 
260
259
  O canal de adocao e o instalador oficial, o README/changelog, o doctor e o aviso de
261
260
  comando aposentado quando a versao nova e executada. Isso nao atualiza silenciosamente
package/bin/auroq-os.js CHANGED
@@ -31,7 +31,7 @@ async function observedMain() {
31
31
  telemetry.runtimeReady=()=>{importing=false;};
32
32
  runRuntime(telemetry);
33
33
  } catch {
34
- console.error('[auroq-os] '+(importing?'BOOTSTRAP_IMPORT':'RUNTIME_FAILURE')+'. Rode: npx -y auroq-os@2.5.11 doctor');
34
+ console.error('[auroq-os] '+(importing?'BOOTSTRAP_IMPORT':'RUNTIME_FAILURE')+'. Rode: npx -y auroq-os@2.5.12 doctor');
35
35
  process.exitCode=1;
36
36
  }
37
37
  }
@@ -1848,7 +1848,7 @@ if (['init', 'install', 'update'].includes(command)) {
1848
1848
  error('Comando aposentado. Nada foi instalado ou alterado.');
1849
1849
  error('Primeira instalacao: https://arcane.arka.education/auroq');
1850
1850
  error('No negocio existente, peca ao Ops: manutencao do motor. Conteudo chega pelo MCP.');
1851
- error('Diagnostico: npx -y auroq-os@2.5.11 doctor. Manutencao: npx -y auroq-os@2.5.11 manutencao.');
1851
+ error('Diagnostico: npx -y auroq-os@2.5.12 doctor. Manutencao: npx -y auroq-os@2.5.12 manutencao.');
1852
1852
  process.exitCode = 1;
1853
1853
  } else if (command === 'instalar' || command === 'manutencao') {
1854
1854
  // `--pasta <nome>`: o instalador manda o CLI criar e entrar na pasta do negocio,
@@ -589,7 +589,7 @@ function Invoke-AuroqLogin {
589
589
  # sucesso. Usado como pergunta de sim/nao, ele dizia "voce ja estava logado"
590
590
  # pra quem nunca logou, pulava o portao e deixava a conta sem acesso chegar ate
591
591
  # a criacao da pasta.
592
- & (Get-AuroqNpx) -y auroq-os@2.5.10 check-access
592
+ & (Get-AuroqNpx) -y auroq-os@2.5.12 check-access
593
593
  if ($LASTEXITCODE -ne 0) {
594
594
  Write-AuroqLog 'login' 'recusado'
595
595
  Write-Host ''
@@ -677,7 +677,7 @@ function Invoke-AuroqInit {
677
677
  if ($env:AUROQ_INSTALL_DRY_RUN) { Write-AuroqOk 'Sistema - seria montado aqui'; return }
678
678
 
679
679
  Set-Location $script:AuroqPastaAlvo
680
- & (Get-AuroqNpx) -y auroq-os@2.5.10 instalar
680
+ & (Get-AuroqNpx) -y auroq-os@2.5.12 instalar
681
681
  if ($LASTEXITCODE -ne 0) {
682
682
  Write-AuroqLog 'init' 'falhou'
683
683
  Write-AuroqErro 'A montagem do sistema parou no meio.' 'Rode o mesmo comando de novo. Se repetir, mande pro suporte o arquivo install.log da pasta .auroq.'
@@ -219,9 +219,9 @@ auroq_tem_terminal() {
219
219
  # recebem pedaco do script no lugar da resposta dele.
220
220
  auroq_npx_aluno() {
221
221
  if [ -t 0 ]; then
222
- npx -y auroq-os@2.5.10 "$@"
222
+ npx -y auroq-os@2.5.12 "$@"
223
223
  else
224
- npx -y auroq-os@2.5.10 "$@" < /dev/tty
224
+ npx -y auroq-os@2.5.12 "$@" < /dev/tty
225
225
  fi
226
226
  }
227
227
 
@@ -0,0 +1,288 @@
1
+ {
2
+ "schema": 1,
3
+ "algorithm": "sha256",
4
+ "scope": "packaged-runtime-and-cascas",
5
+ "version": "2.5.12",
6
+ "files": [
7
+ {
8
+ "path": ".auroq-core/constitution.md",
9
+ "sha256": "8b6df553e2708e3d9eff3f12627808ca953a4272cfbd2f94cb9ae3d4c70a73de"
10
+ },
11
+ {
12
+ "path": ".auroq-core/core-config.yaml",
13
+ "sha256": "bb98d40d411423cfd4e92100a003a029309d5db1a5514b1a7fed880b96fc1f56"
14
+ },
15
+ {
16
+ "path": ".auroq-core/core/synapse/context/context-builder.js",
17
+ "sha256": "121cd0a1df8a44098831cd4335536e8facf4e65b8aec48f4ce9c2d432dc6252a"
18
+ },
19
+ {
20
+ "path": ".auroq-core/core/synapse/context/context-tracker.js",
21
+ "sha256": "48e94db7b1778dedecc8eae829139579ad7778ff47668597ebe766610696553f"
22
+ },
23
+ {
24
+ "path": ".auroq-core/core/synapse/diagnostics/collectors/consistency-collector.js",
25
+ "sha256": "65f4255f87c9900400649dc8b9aedaac4851b5939d93e127778bd93cee99db12"
26
+ },
27
+ {
28
+ "path": ".auroq-core/core/synapse/diagnostics/collectors/hook-collector.js",
29
+ "sha256": "c2cfa1b760bcb05decf5ad05f9159140cbe0cdc6b0f91581790e44d83dc6b660"
30
+ },
31
+ {
32
+ "path": ".auroq-core/core/synapse/diagnostics/collectors/manifest-collector.js",
33
+ "sha256": "3dc895eb94485320ecbaca3a1d29e3776cfb691dd7dcc71cf44b34af30e8ebb6"
34
+ },
35
+ {
36
+ "path": ".auroq-core/core/synapse/diagnostics/collectors/output-analyzer.js",
37
+ "sha256": "e6846b1aba0a6cba17c297a871861d4f8199d7500220bff296a6a3291e32493e"
38
+ },
39
+ {
40
+ "path": ".auroq-core/core/synapse/diagnostics/collectors/pipeline-collector.js",
41
+ "sha256": "8655b6240e2f54b70def1a8c2fae00d40e2615cb95fd7ca0d64c2e0a6dfe3b73"
42
+ },
43
+ {
44
+ "path": ".auroq-core/core/synapse/diagnostics/collectors/quality-collector.js",
45
+ "sha256": "30ae299eab6d569d09afe3530a5b2f1ff35ef75366a1ab56a9e2a57d39d3611c"
46
+ },
47
+ {
48
+ "path": ".auroq-core/core/synapse/diagnostics/collectors/relevance-matrix.js",
49
+ "sha256": "f92c4f7061dc82eed4310a27b69eade33d3015f9beb1bed688601a2dccbad22e"
50
+ },
51
+ {
52
+ "path": ".auroq-core/core/synapse/diagnostics/collectors/safe-read-json.js",
53
+ "sha256": "dc7bcd13779207ad67b1c3929b7e1e0ccfa3563f3458c20cad28cb1922e9a74c"
54
+ },
55
+ {
56
+ "path": ".auroq-core/core/synapse/diagnostics/collectors/session-collector.js",
57
+ "sha256": "a116d884d6947ddc8e5f3def012d93696576c584c4fde1639b8d895924fc09ea"
58
+ },
59
+ {
60
+ "path": ".auroq-core/core/synapse/diagnostics/collectors/timing-collector.js",
61
+ "sha256": "2523ce93f863a28f798d992c4f2fab041c91a09413b3186fd290e6035b391587"
62
+ },
63
+ {
64
+ "path": ".auroq-core/core/synapse/diagnostics/collectors/uap-collector.js",
65
+ "sha256": "dd025894f8f0d3bd22a147dbc0debef8b83e96f3c59483653404b3cd5a01d5aa"
66
+ },
67
+ {
68
+ "path": ".auroq-core/core/synapse/diagnostics/report-formatter.js",
69
+ "sha256": "1aa2853928df194b669ce32c3b050a90de1311739045074f699d10210ed8d381"
70
+ },
71
+ {
72
+ "path": ".auroq-core/core/synapse/diagnostics/synapse-diagnostics.js",
73
+ "sha256": "de9dffce0e380637027cbd64b062d3eeffc37e42a84a337e5758fbef39fe3a00"
74
+ },
75
+ {
76
+ "path": ".auroq-core/core/synapse/domain/domain-loader.js",
77
+ "sha256": "af788f9da956b89eef1e5eb4ef4efdf05ca758c8969a2c375f568119495ebc05"
78
+ },
79
+ {
80
+ "path": ".auroq-core/core/synapse/engine.js",
81
+ "sha256": "775d232f67c92596f4a4a4302d5f4c69b6473680badead6ff4772f46e157381c"
82
+ },
83
+ {
84
+ "path": ".auroq-core/core/synapse/layers/l0-constitution.js",
85
+ "sha256": "2123a6a44915aaac2a6bbd26c67c285c9d1e12b50fe42a8ada668306b07d1c4a"
86
+ },
87
+ {
88
+ "path": ".auroq-core/core/synapse/layers/l1-global.js",
89
+ "sha256": "21f6969e6d64e9a85c876be6799db4ca7d090f0009057f4a06ead8da12392d45"
90
+ },
91
+ {
92
+ "path": ".auroq-core/core/synapse/layers/l2-agent.js",
93
+ "sha256": "a8677dc58ae7927c5292a4b52883bbc905c8112573b8b8631f0b8bc01ea2b6e6"
94
+ },
95
+ {
96
+ "path": ".auroq-core/core/synapse/layers/l3-workflow.js",
97
+ "sha256": "496cbd71d7dac9c1daa534ffac45c622d0c032f334fedf493e9322a565b2b181"
98
+ },
99
+ {
100
+ "path": ".auroq-core/core/synapse/layers/l4-task.js",
101
+ "sha256": "30df70c04b16e3aff95899211ef6ae3d9f0a8097ebdc7de92599fc6cb792e5f0"
102
+ },
103
+ {
104
+ "path": ".auroq-core/core/synapse/layers/l5-squad.js",
105
+ "sha256": "d6d35cbf966deb5a7d2df2b09f5abe8da8c6d4e346b385418f8dc1fae3ddf830"
106
+ },
107
+ {
108
+ "path": ".auroq-core/core/synapse/layers/l6-keyword.js",
109
+ "sha256": "8e5405999a2ce2f3ca4e62e863cf702ba27448914241f5eb8f02760bc7477523"
110
+ },
111
+ {
112
+ "path": ".auroq-core/core/synapse/layers/l7-star-command.js",
113
+ "sha256": "3b8372ac1c51830c1ef560b1012b112a3559651b0750e42f2037f7fe9e6787d6"
114
+ },
115
+ {
116
+ "path": ".auroq-core/core/synapse/layers/layer-processor.js",
117
+ "sha256": "46a7d08e088017a61fa6033ce4d9b3b8caba622e2c732fe158e0f531690fa3d9"
118
+ },
119
+ {
120
+ "path": ".auroq-core/core/synapse/memory/memory-bridge.js",
121
+ "sha256": "820875f97ceea80fc6402c0dab1706cfe58de527897b22dea68db40b0d6ec368"
122
+ },
123
+ {
124
+ "path": ".auroq-core/core/synapse/memory/synapse-memory-provider.js",
125
+ "sha256": "5d613d1fac7ee82c49a3f03b38735fd3cabfe87dd868494672ddfef300ea3145"
126
+ },
127
+ {
128
+ "path": ".auroq-core/core/synapse/output/formatter.js",
129
+ "sha256": "3e722c4736642ef96ec73a54cd2011eed7bd1074394a38eac66cb6f0af2c855b"
130
+ },
131
+ {
132
+ "path": ".auroq-core/core/synapse/runtime/hook-runtime.js",
133
+ "sha256": "77a88b9324940b4feb14de6fe4de51c0fd82e2062ef4470b93de2e57bd02c88a"
134
+ },
135
+ {
136
+ "path": ".auroq-core/core/synapse/scripts/generate-constitution.js",
137
+ "sha256": "e2f645d5de44e6b3721a5de9b19982bc7b8cf7eed3ca4d62bc53221649c59af1"
138
+ },
139
+ {
140
+ "path": ".auroq-core/core/synapse/session/session-manager.js",
141
+ "sha256": "98e8c8fa15b074b6102bcd51c6f91f101743338e8cde6de9c31d6b6eea5d6580"
142
+ },
143
+ {
144
+ "path": ".auroq-core/core/synapse/utils/atomic-write.js",
145
+ "sha256": "efeef0fbcebb184df5b79f4ba4b4b7fe274c3ba7d1c705ce1af92628e920bd8b"
146
+ },
147
+ {
148
+ "path": ".auroq-core/core/synapse/utils/paths.js",
149
+ "sha256": "bf8cf93c1a16295e7de055bee292e2778a152b6e7d6c648dbc054a4b04dffc10"
150
+ },
151
+ {
152
+ "path": ".auroq-core/core/synapse/utils/tokens.js",
153
+ "sha256": "3b927daec51d0a791f3fe4ef9aafc362773450e7cf50eb4b6d8ae9011d70df9a"
154
+ },
155
+ {
156
+ "path": ".auroq-core/dna-operacional.md",
157
+ "sha256": "36d3118831d1d406a1e5eef2394fef6628396712534c59888a5968f9ffdba796"
158
+ },
159
+ {
160
+ "path": ".claude/commands/AuroqOS/agents/ops.md",
161
+ "sha256": "6f0df7c0dfbe28f4efa4d49d010f5a2520293c972623eb5c7dc25aa62ba7fc37"
162
+ },
163
+ {
164
+ "path": ".claude/commands/auroq-companion.md",
165
+ "sha256": "789bb61f076847f5695be0ea1133cfe026024c3e7a77a48f142297cc30e02196"
166
+ },
167
+ {
168
+ "path": ".claude/hooks/ahi-capture.cjs",
169
+ "sha256": "d99564a5bebc23281da5dbba2e82ef099c390768bed2b63f70be17c75ab8fd57"
170
+ },
171
+ {
172
+ "path": ".claude/hooks/ahi/auth-config.cjs",
173
+ "sha256": "bbe1f01165f0cf4562b8685a59bd2aac94758dbd0b072d287c09db10d9c6e0eb"
174
+ },
175
+ {
176
+ "path": ".claude/hooks/ahi/runtime.cjs",
177
+ "sha256": "82c0b93351f25959e5cbe0a9a57b7ae29a3b497298ab351bddd7361a2ef6ce1a"
178
+ },
179
+ {
180
+ "path": ".claude/hooks/ahi/security.cjs",
181
+ "sha256": "dc27db1392acd0abd8102490860bfad6a94276bbc92f63e086dc997dbe6b208c"
182
+ },
183
+ {
184
+ "path": ".claude/hooks/ahi/worker.cjs",
185
+ "sha256": "3eabd293f3c9c6887f6850ca4be9ad7021d6659967f2f5eecadefb2bb78e9b85"
186
+ },
187
+ {
188
+ "path": ".claude/hooks/auroq-sync.cjs",
189
+ "sha256": "2655dbd0a6f73ae83d0fabfb9567fc7dbf79708adcf9bfa455ea6da6fc183b9f"
190
+ },
191
+ {
192
+ "path": ".claude/hooks/precompact-session-digest.cjs",
193
+ "sha256": "cca89df5591fd52581d62bd35cc097d7e3d2622bd2227e7e53b425d5a6d8bdb2"
194
+ },
195
+ {
196
+ "path": ".claude/hooks/synapse-engine.cjs",
197
+ "sha256": "4d01f376993b570b9ca995b839d292ce9b73c597a0a1ebc8cb1ffbd214093bfe"
198
+ },
199
+ {
200
+ "path": ".claude/settings.json",
201
+ "sha256": "3d0a9836997e7524d1538e96799bd5ea06575620c97a2eb03d3410b6df5d0e5a"
202
+ },
203
+ {
204
+ "path": ".claude/settings.local.json",
205
+ "sha256": "cb81d47fef6536bd472841811bb85f36a8219994d55399eac85732f920c566c6"
206
+ },
207
+ {
208
+ "path": "bin/auroq-os.js",
209
+ "sha256": "b043982de702e524799b51968b29578c04e8a6ed5f66c318d661bf776f0ef838"
210
+ },
211
+ {
212
+ "path": "lib/ahi/client.mjs",
213
+ "sha256": "ded0ba89b58193e59b4ced26321965f85b8b19abc498ed8b079cab956806d789"
214
+ },
215
+ {
216
+ "path": "lib/ahi/historico.mjs",
217
+ "sha256": "cb85e53dae59e793bdb83f59c3f8d14342912953b43dd9408d757a7b7182733a"
218
+ },
219
+ {
220
+ "path": "lib/ahi/install.cjs",
221
+ "sha256": "f3a9fec92e64b8624fb45e062b51777788831337dec6a89ce8222d7813baca9d"
222
+ },
223
+ {
224
+ "path": "lib/auth.js",
225
+ "sha256": "e775938f20b86c305e44c2b5834eeb886fec0316dccc0f3565536689b7bfb46a"
226
+ },
227
+ {
228
+ "path": "lib/credentials.js",
229
+ "sha256": "92ecfba2b7ded12de5b236d64b24781ffdf05c1e770614a8d02bf05ab78d59e0"
230
+ },
231
+ {
232
+ "path": "lib/diagnostico.js",
233
+ "sha256": "a7c15e8aa9dd3a7ceca3784ce7207f9593baaabedc87e88d0a674096b20c6835"
234
+ },
235
+ {
236
+ "path": "lib/legacy-shims.json",
237
+ "sha256": "1ea331604dfcadc579a7a27d53bfb2c1d1a2263e1d2ca83ed780c5ce5d2fe877"
238
+ },
239
+ {
240
+ "path": "lib/onepassword.js",
241
+ "sha256": "a65d425182e0698d3f80b8d81bb088b13c5e5b60ae1aa92b095ab38d60e22ea9"
242
+ },
243
+ {
244
+ "path": "lib/supabase.js",
245
+ "sha256": "a922dee14500e95b4b1f7a8d2a67baedee71c547f863e69e63f25830f9786f28"
246
+ },
247
+ {
248
+ "path": "lib/synapse-hashes.json",
249
+ "sha256": "061dfe2e2c7e2a75472ae133e3465931c1267cc18e0740e7c1515404095b47e6"
250
+ },
251
+ {
252
+ "path": "lib/telemetry.mjs",
253
+ "sha256": "13643a1f8c33d7063a07b37190ba157755f8e26aadd4059e1afdc25fada0cf38"
254
+ },
255
+ {
256
+ "path": "package.json",
257
+ "sha256": "17d5e5a26ffd8044fd8b98260ca7961b37a326acbe8b63dd3a65af73edff1317"
258
+ },
259
+ {
260
+ "path": "scripts/build-pack.mjs",
261
+ "sha256": "c4f7bbc40320dcf848307ab9d7d7c84c864ddc70a0e4db933a35406f55dcf881"
262
+ },
263
+ {
264
+ "path": "scripts/pack-state.mjs",
265
+ "sha256": "7399b388eab97dd35da49055b126aba39a0c95d82cf422c62b603e9bf5de7f36"
266
+ },
267
+ {
268
+ "path": "scripts/postpack-casca.mjs",
269
+ "sha256": "e361ed44a688d0cd6804bdbf3e240649b69171c2396433f0fdb55cb0f739d993"
270
+ },
271
+ {
272
+ "path": "scripts/prepack-casca.mjs",
273
+ "sha256": "ecd4381a411d275af339e0bdc3acc5a0ea33ad65c1eaafad6ff3d6c79b415aff"
274
+ },
275
+ {
276
+ "path": "scripts/runtime-integrity.mjs",
277
+ "sha256": "af0cb75622dcf4c049da3fc053817087648a86fc31df3be4762e974279eb87a3"
278
+ },
279
+ {
280
+ "path": "scripts/sync-codex-skills.mjs",
281
+ "sha256": "36e703e8d298a5fc4cff3a85828b93a5123ab9563eeb2808c30f23f3eefd58f8"
282
+ },
283
+ {
284
+ "path": "scripts/validate-hybrid.mjs",
285
+ "sha256": "d70636ad4fd6a7975df552de5cc4a88d535f0ec8596b7cf74c20e9ff73f02505"
286
+ }
287
+ ]
288
+ }
@@ -49,7 +49,8 @@
49
49
  ],
50
50
  "engine.js": [
51
51
  "47bab93a2144edce875ee68cc2717fee1bd824c9affdd5964454f5141310b8e0",
52
- "c061df38ec24956da9a752f13f9455c7cd69546adb330c7587b9d456e79b4aa1"
52
+ "c061df38ec24956da9a752f13f9455c7cd69546adb330c7587b9d456e79b4aa1",
53
+ "775d232f67c92596f4a4a4302d5f4c69b6473680badead6ff4772f46e157381c"
53
54
  ],
54
55
  "layers/l0-constitution.js": [
55
56
  "2123a6a44915aaac2a6bbd26c67c285c9d1e12b50fe42a8ada668306b07d1c4a"
@@ -76,7 +77,8 @@
76
77
  "3b8372ac1c51830c1ef560b1012b112a3559651b0750e42f2037f7fe9e6787d6"
77
78
  ],
78
79
  "layers/layer-processor.js": [
79
- "73cb0e5b4bada80d8e256009004679e483792077fac4358c6466cd77136f79fa"
80
+ "73cb0e5b4bada80d8e256009004679e483792077fac4358c6466cd77136f79fa",
81
+ "46a7d08e088017a61fa6033ce4d9b3b8caba622e2c732fe158e0f531690fa3d9"
80
82
  ],
81
83
  "memory/memory-bridge.js": [
82
84
  "820875f97ceea80fc6402c0dab1706cfe58de527897b22dea68db40b0d6ec368"
@@ -90,7 +92,8 @@
90
92
  ],
91
93
  "runtime/hook-runtime.js": [
92
94
  "5518692eeee7dcb570507c4bb96ef68a1ef51d6ad6c417e9d1f649cd8b21b199",
93
- "7c34b84c5693045a007356081959c335493bbd3afc69822fb6661e19cc10d56c"
95
+ "7c34b84c5693045a007356081959c335493bbd3afc69822fb6661e19cc10d56c",
96
+ "77a88b9324940b4feb14de6fe4de51c0fd82e2062ef4470b93de2e57bd02c88a"
94
97
  ],
95
98
  "scripts/generate-constitution.js": [
96
99
  "e2f645d5de44e6b3721a5de9b19982bc7b8cf7eed3ca4d62bc53221649c59af1"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "auroq-os",
3
- "version": "2.5.11",
3
+ "version": "2.5.12",
4
4
  "description": "Auroq OS — Sistema Operacional de IA para Experts",
5
5
  "bin": {
6
6
  "auroq-os": "bin/auroq-os.js"
@@ -18,14 +18,15 @@
18
18
  "auroq:sync:codex:check": "npm run sync:codex:check",
19
19
  "auroq:validate": "npm run validate:hybrid",
20
20
  "validate:hybrid": "node scripts/validate-hybrid.mjs",
21
- "lint": "node --check bin/auroq-os.js && node --check lib/auth.js && node --check lib/diagnostico.js && node --check lib/credentials.js && node --check lib/onepassword.js && node --check lib/supabase.js && node --check scripts/sync-codex-skills.mjs && node --check scripts/validate-hybrid.mjs && node --check scripts/prepack-casca.mjs && node --check scripts/postpack-casca.mjs && node --check scripts/assert-mcp-pin.mjs && node --check lib/telemetry.mjs && node --check scripts/synapse-hashes.mjs && node --check lib/ahi/historico.mjs && node --check .claude/hooks/ahi-capture.cjs && node --check .claude/hooks/ahi/security.cjs && node --check .claude/hooks/ahi/runtime.cjs && node --check .claude/hooks/ahi/worker.cjs && node --check lib/ahi/install.cjs && node --check .claude/hooks/ahi/auth-config.cjs && node --check lib/ahi/client.mjs && node --check scripts/build-pack.mjs && node --check scripts/assert-native-release.mjs",
21
+ "lint": "node --check bin/auroq-os.js && node --check lib/auth.js && node --check lib/diagnostico.js && node --check lib/credentials.js && node --check lib/onepassword.js && node --check lib/supabase.js && node --check scripts/sync-codex-skills.mjs && node --check scripts/validate-hybrid.mjs && node --check scripts/prepack-casca.mjs && node --check scripts/postpack-casca.mjs && node --check scripts/assert-mcp-pin.mjs && node --check lib/telemetry.mjs && node --check scripts/synapse-hashes.mjs && node --check lib/ahi/historico.mjs && node --check .claude/hooks/ahi-capture.cjs && node --check .claude/hooks/ahi/security.cjs && node --check .claude/hooks/ahi/runtime.cjs && node --check .claude/hooks/ahi/worker.cjs && node --check lib/ahi/install.cjs && node --check .claude/hooks/ahi/auth-config.cjs && node --check lib/ahi/client.mjs && node --check scripts/build-pack.mjs && node --check scripts/assert-native-release.mjs && node --check scripts/runtime-integrity.mjs && node --check scripts/pack-state.mjs",
22
22
  "typecheck": "npm run validate:hybrid",
23
23
  "test": "node --test tests/*.test.mjs",
24
24
  "release:verify": "node scripts/assert-mcp-pin.mjs && node scripts/assert-native-release.mjs",
25
25
  "prepublishOnly": "npm run release:verify",
26
26
  "prepack": "node scripts/prepack-casca.mjs",
27
27
  "postpack": "node scripts/postpack-casca.mjs",
28
- "build": "node scripts/build-pack.mjs"
28
+ "build": "node scripts/build-pack.mjs",
29
+ "integrity:verify": "node scripts/runtime-integrity.mjs --verify"
29
30
  },
30
31
  "files": [
31
32
  "bin/",
@@ -56,7 +57,7 @@
56
57
  "dependencies": {
57
58
  "@supabase/supabase-js": "^2.103.0",
58
59
  "fs-extra": "^11.3.0",
59
- "js-yaml": "^4.1.0",
60
+ "js-yaml": "^4.3.2",
60
61
  "prompts": "^2.4.2"
61
62
  },
62
63
  "author": "Euriler Jube / Arka",
@@ -1,13 +1,44 @@
1
1
  #!/usr/bin/env node
2
- // Portable dry-run package validation. npm lifecycle still checks/restores the casca overlay.
3
- import { spawnSync } from 'node:child_process';
4
- const command = process.platform === 'win32' ? (process.env.ComSpec || 'cmd.exe') : 'npm';
5
- const args = process.platform === 'win32' ? ['/d', '/s', '/c', 'npm pack --dry-run --json'] : ['pack', '--dry-run', '--json'];
6
- const result = spawnSync(command, args, { encoding: 'utf8', maxBuffer: 8 * 1024 * 1024, windowsHide: true });
7
- if (result.status !== 0 || result.error) { process.stderr.write(result.stderr || 'Package validation failed.\n'); process.exit(1); }
8
- // npm versions may print lifecycle logs before/after the JSON manifest.
9
- const manifest = result.stdout.match(/(\[\s*\{[\s\S]*\}\s*\])/);
10
- if (!manifest) throw new Error('Package manifest unavailable');
11
- const packages = JSON.parse(manifest[1]);
12
- if (!packages[0]?.files?.some(file => file.path === '.claude/hooks/ahi/runtime.cjs')) throw new Error('AHI runtime missing from package');
13
- process.stdout.write('Package validated with AHI runtime.\n');
2
+ // Validate actual tarball bytes AFTER the casca overlay; never pack this checkout.
3
+ import fs from 'node:fs';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { fileURLToPath } from 'node:url';
7
+ import { execFileSync } from 'node:child_process';
8
+ import { verifyRuntimeInventory } from './runtime-integrity.mjs';
9
+
10
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
11
+ const temporary = fs.mkdtempSync(path.join(os.tmpdir(), 'auroq-build-pack-'));
12
+ try {
13
+ const copy = path.join(temporary, 'source');
14
+ const excluded = new Set(['.git', 'node_modules', '.pack-casca-backup']);
15
+ if (fs.existsSync(path.join(root, '.pack-casca-backup'))) throw new Error('Pending casca backup: restore the checkout before building.');
16
+ fs.cpSync(root, copy, { recursive: true, filter: source => !path.relative(root, source).split(path.sep).some(part => excluded.has(part) || part.endsWith('.tgz')) });
17
+ const cli = process.env.npm_execpath || (process.platform === 'win32'
18
+ ? path.join(path.dirname(process.execPath), 'node_modules', 'npm', 'bin', 'npm-cli.js') : null);
19
+ const args = ['pack', '--json', '--pack-destination', temporary];
20
+ const output = execFileSync(cli ? process.execPath : 'npm', cli ? [cli, ...args] : args,
21
+ { cwd: copy, encoding: 'utf8', maxBuffer: 8 * 1024 * 1024, windowsHide: true, stdio: ['ignore', 'pipe', 'pipe'] });
22
+ const match = output.match(/(\[\s*\{[\s\S]*\}\s*\])/);
23
+ if (!match) throw new Error('Package manifest unavailable');
24
+ const packages = JSON.parse(match[1]);
25
+ if (packages.length !== 1 || !packages[0].filename || path.basename(packages[0].filename) !== packages[0].filename) throw new Error('Invalid package result');
26
+ const artifact = path.join(temporary, packages[0].filename);
27
+ const listing = execFileSync('tar', ['-tzf', artifact], { encoding: 'utf8', maxBuffer: 8 * 1024 * 1024 });
28
+ const packedPaths = listing.split(/\r?\n/).filter(Boolean).map(file => {
29
+ if (!file.startsWith('package/') || file.endsWith('/') || file.includes('..') || file.includes('\\')) throw new Error('Invalid tarball path');
30
+ return file.slice('package/'.length);
31
+ });
32
+ const extracted = path.join(temporary, 'extracted');
33
+ fs.mkdirSync(extracted);
34
+ execFileSync('tar', ['-xzf', artifact, '-C', extracted], { stdio: ['ignore', 'pipe', 'pipe'] });
35
+ const result = verifyRuntimeInventory(path.join(extracted, 'package'), packedPaths);
36
+ if (fs.existsSync(path.join(copy, '.pack-casca-backup'))) throw new Error('Pack did not restore its casca overlay');
37
+ process.stdout.write(`Package validated: ${result.files} protected files, SHA256 and local runtime references.\n`);
38
+ } catch (error) {
39
+ // Do not print npm lifecycle output: it may include environment-specific data.
40
+ console.error(`Package validation failed: ${error.status !== undefined ? 'pack/extraction command failed' : error.message}`);
41
+ process.exitCode = 1;
42
+ } finally {
43
+ fs.rmSync(temporary, { recursive: true, force: true });
44
+ }
@@ -0,0 +1,64 @@
1
+ /** Journal the temporary casca overlay, including files absent before packing. */
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+
5
+ const BACKUP = '.pack-casca-backup';
6
+ const JOURNAL = 'overlay-state.json';
7
+
8
+ function assertPath(relative) {
9
+ if (!relative || path.isAbsolute(relative) || /^[A-Za-z]:/.test(relative) || relative.includes('\\') || relative.split('/').some(p => !p || p === '.' || p === '..')) {
10
+ throw new Error('Invalid overlay path');
11
+ }
12
+ }
13
+
14
+ function assertNoSymlink(root, relative) {
15
+ let current = root;
16
+ for (const segment of relative.split('/')) {
17
+ current = path.join(current, segment);
18
+ let stat;
19
+ try { stat = fs.lstatSync(current); } catch (error) { if (error.code !== 'ENOENT') throw error; }
20
+ if (stat?.isSymbolicLink()) throw new Error(`Symlink in overlay path: ${relative}`);
21
+ }
22
+ }
23
+
24
+ export function beginOverlay(root) {
25
+ const backup = path.join(root, BACKUP);
26
+ if (fs.existsSync(backup)) throw new Error('Pending casca backup: restore with node scripts/postpack-casca.mjs before packing again.');
27
+ fs.mkdirSync(backup);
28
+ fs.writeFileSync(path.join(backup, JOURNAL), '[]\n');
29
+ }
30
+
31
+ export function preserveOverlayFile(root, relative) {
32
+ assertPath(relative);
33
+ assertNoSymlink(root, relative);
34
+ const backup = path.join(root, BACKUP);
35
+ const journalFile = path.join(backup, JOURNAL);
36
+ const journal = JSON.parse(fs.readFileSync(journalFile, 'utf8'));
37
+ if (journal.some(entry => entry.path === relative)) return;
38
+ const source = path.join(root, relative);
39
+ const existed = fs.existsSync(source);
40
+ if (existed) {
41
+ if (!fs.lstatSync(source).isFile()) throw new Error(`Overlay target is not a regular file: ${relative}`);
42
+ const target = path.join(backup, 'files', relative);
43
+ fs.mkdirSync(path.dirname(target), { recursive: true });
44
+ fs.copyFileSync(source, target);
45
+ }
46
+ journal.push({ path: relative, existed });
47
+ fs.writeFileSync(journalFile, `${JSON.stringify(journal)}\n`);
48
+ }
49
+
50
+ export function restoreOverlay(root) {
51
+ const backup = path.join(root, BACKUP);
52
+ if (!fs.existsSync(backup)) return;
53
+ const journal = JSON.parse(fs.readFileSync(path.join(backup, JOURNAL), 'utf8'));
54
+ for (const entry of journal.reverse()) {
55
+ assertPath(entry.path);
56
+ assertNoSymlink(root, entry.path);
57
+ const target = path.join(root, entry.path);
58
+ if (entry.existed) {
59
+ fs.mkdirSync(path.dirname(target), { recursive: true });
60
+ fs.copyFileSync(path.join(backup, 'files', entry.path), target);
61
+ } else fs.rmSync(target, { force: true });
62
+ }
63
+ fs.rmSync(backup, { recursive: true });
64
+ }
@@ -1,32 +1,9 @@
1
1
  #!/usr/bin/env node
2
2
  /** Restaura o miolo depois do npm pack/publish. */
3
- import fs from 'node:fs';
4
3
  import path from 'node:path';
5
4
  import { fileURLToPath } from 'node:url';
5
+ import { restoreOverlay } from './pack-state.mjs';
6
6
 
7
- const root = fs.realpathSync(path.join(path.dirname(fileURLToPath(import.meta.url)), '..'));
8
- const cascaRoot = path.join(root, 'casca');
9
- const backupRoot = path.join(root, '.pack-casca-backup');
10
-
11
- function walk(dir) {
12
- const out = [];
13
- if (!fs.existsSync(dir)) return out;
14
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
15
- const full = path.join(dir, entry.name);
16
- if (entry.isDirectory()) out.push(...walk(full));
17
- else out.push(full);
18
- }
19
- return out;
20
- }
21
-
22
- if (!fs.existsSync(backupRoot)) process.exit(0);
23
-
24
- for (const bak of walk(backupRoot)) {
25
- const rel = path.relative(backupRoot, bak);
26
- const dest = path.join(root, rel);
27
- fs.mkdirSync(path.dirname(dest), { recursive: true });
28
- fs.copyFileSync(bak, dest);
29
- }
30
-
31
- fs.rmSync(backupRoot, { recursive: true, force: true });
7
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
8
+ restoreOverlay(root);
32
9
  console.log('postpack-casca: working tree restaurado');
@@ -6,10 +6,11 @@
6
6
  import fs from 'node:fs';
7
7
  import path from 'node:path';
8
8
  import { fileURLToPath } from 'node:url';
9
+ import { beginOverlay, preserveOverlayFile, restoreOverlay } from './pack-state.mjs';
10
+ import { INVENTORY_PATH, writeRuntimeInventory, verifyRuntimeInventory } from './runtime-integrity.mjs';
9
11
 
10
12
  const root = fs.realpathSync(path.join(path.dirname(fileURLToPath(import.meta.url)), '..'));
11
13
  const cascaRoot = path.join(root, 'casca');
12
- const backupRoot = path.join(root, '.pack-casca-backup');
13
14
 
14
15
  function walk(dir) {
15
16
  const out = [];
@@ -22,45 +23,44 @@ function walk(dir) {
22
23
  return out;
23
24
  }
24
25
 
25
- if (fs.existsSync(backupRoot)) {
26
- fs.rmSync(backupRoot, { recursive: true, force: true });
27
- }
28
-
29
26
  const files = walk(cascaRoot);
30
27
  if (!files.length) {
31
28
  console.error('casca/ vazia — recusando pack');
32
29
  process.exit(1);
33
30
  }
34
31
 
35
- for (const src of files) {
36
- const rel = path.relative(cascaRoot, src);
37
- const dest = path.join(root, rel);
38
- if (fs.existsSync(dest)) {
39
- const bak = path.join(backupRoot, rel);
40
- fs.mkdirSync(path.dirname(bak), { recursive: true });
41
- fs.copyFileSync(dest, bak);
32
+ // A missing validator fails at import time, before touching the checkout.
33
+ beginOverlay(root);
34
+ try {
35
+ for (const src of files) {
36
+ const rel = path.relative(cascaRoot, src).split(path.sep).join('/');
37
+ const dest = path.join(root, rel);
38
+ preserveOverlayFile(root, rel);
39
+ fs.mkdirSync(path.dirname(dest), { recursive: true });
40
+ fs.copyFileSync(src, dest);
42
41
  }
43
- fs.mkdirSync(path.dirname(dest), { recursive: true });
44
- fs.copyFileSync(src, dest);
45
- }
46
42
 
47
- // O hook auroq-sync compara a versao lida de .auroq-core/core-config.yaml com a
48
- // do npm para avisar de update. Se o core-config ficar para tras, TODO aluno ja
49
- // atualizado ve "saiu versao nova" para sempre. Sincroniza no pack.
50
- const pkgVersion = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).version;
51
- const cfgPath = path.join(root, '.auroq-core', 'core-config.yaml');
52
- if (fs.existsSync(cfgPath)) {
53
- const cfg = fs.readFileSync(cfgPath, 'utf8');
54
- const synced = cfg.replace(/^(\s*version:\s*)["']?\d+\.\d+\.\d+["']?/m, `$1${pkgVersion}`);
55
- if (synced !== cfg) {
56
- const bak = path.join(backupRoot, '.auroq-core', 'core-config.yaml');
57
- if (!fs.existsSync(bak)) {
58
- fs.mkdirSync(path.dirname(bak), { recursive: true });
59
- fs.copyFileSync(cfgPath, bak);
43
+ // O hook auroq-sync compara a versao lida de .auroq-core/core-config.yaml com a
44
+ // do npm para avisar de update. Se o core-config ficar para tras, TODO aluno ja
45
+ // atualizado ve "saiu versao nova" para sempre. Sincroniza no pack.
46
+ const pkgVersion = JSON.parse(fs.readFileSync(path.join(root, 'package.json'), 'utf8')).version;
47
+ const cfgPath = path.join(root, '.auroq-core', 'core-config.yaml');
48
+ if (fs.existsSync(cfgPath)) {
49
+ const cfg = fs.readFileSync(cfgPath, 'utf8');
50
+ const synced = cfg.replace(/^(\s*version:\s*)["']?\d+\.\d+\.\d+["']?/m, `$1${pkgVersion}`);
51
+ if (synced !== cfg) {
52
+ preserveOverlayFile(root, '.auroq-core/core-config.yaml');
53
+ fs.writeFileSync(cfgPath, synced);
54
+ console.log(`prepack-casca: core-config.yaml sincronizado para ${pkgVersion}`);
60
55
  }
61
- fs.writeFileSync(cfgPath, synced);
62
- console.log(`prepack-casca: core-config.yaml sincronizado para ${pkgVersion}`);
63
56
  }
64
- }
65
57
 
66
- console.log(`prepack-casca: ${files.length} arquivos sobrepostos`);
58
+ preserveOverlayFile(root, INVENTORY_PATH);
59
+ writeRuntimeInventory(root);
60
+ const verified = verifyRuntimeInventory(root);
61
+ console.log(`prepack-casca: ${files.length} arquivos sobrepostos`);
62
+ console.log(`prepack-casca: inventario SHA256 de ${verified.files} arquivos (nao e assinatura)`);
63
+ } catch (error) {
64
+ restoreOverlay(root);
65
+ throw error;
66
+ }
@@ -0,0 +1,173 @@
1
+ #!/usr/bin/env node
2
+ /** Package completeness and accidental-change checks; SHA256 is not a signature.
3
+ * Scope: shipped engine/cascas only. Business files and optional legacy modules
4
+ * are not a source of dependencies. Generate AFTER the casca overlay.
5
+ */
6
+ import fs from 'node:fs';
7
+ import path from 'node:path';
8
+ import crypto from 'node:crypto';
9
+ import { fileURLToPath } from 'node:url';
10
+
11
+ export const INVENTORY_PATH = 'lib/runtime-integrity.json';
12
+ const SYNAPSE_ROOT = '.auroq-core/core/synapse/';
13
+ const SYNAPSE_LAYERS = ['l0-constitution', 'l1-global', 'l2-agent', 'l3-workflow', 'l4-task', 'l5-squad', 'l6-keyword', 'l7-star-command'];
14
+ export const REQUIRED_FILES = [
15
+ 'package.json', 'bin/auroq-os.js',
16
+ 'lib/auth.js', 'lib/credentials.js', 'lib/diagnostico.js', 'lib/supabase.js',
17
+ 'lib/onepassword.js', 'lib/telemetry.mjs', 'lib/synapse-hashes.json', 'lib/legacy-shims.json',
18
+ 'lib/ahi/install.cjs', 'lib/ahi/client.mjs', 'lib/ahi/historico.mjs',
19
+ 'scripts/runtime-integrity.mjs', 'scripts/pack-state.mjs',
20
+ 'scripts/prepack-casca.mjs', 'scripts/postpack-casca.mjs', 'scripts/build-pack.mjs',
21
+ 'scripts/sync-codex-skills.mjs', 'scripts/validate-hybrid.mjs',
22
+ '.auroq-core/core-config.yaml', '.auroq-core/constitution.md', '.auroq-core/dna-operacional.md',
23
+ '.auroq-core/core/synapse/engine.js', '.auroq-core/core/synapse/runtime/hook-runtime.js',
24
+ '.auroq-core/core/synapse/session/session-manager.js', '.auroq-core/core/synapse/domain/domain-loader.js',
25
+ ...SYNAPSE_LAYERS.map(layer => `${SYNAPSE_ROOT}layers/${layer}.js`),
26
+ '.claude/settings.json', '.claude/settings.local.json',
27
+ '.claude/commands/AuroqOS/agents/ops.md', '.claude/commands/auroq-companion.md',
28
+ '.claude/hooks/synapse-engine.cjs', '.claude/hooks/auroq-sync.cjs',
29
+ '.claude/hooks/ahi-capture.cjs', '.claude/hooks/ahi/runtime.cjs',
30
+ '.claude/hooks/ahi/security.cjs', '.claude/hooks/ahi/worker.cjs', '.claude/hooks/ahi/auth-config.cjs',
31
+ ];
32
+ const PROTECTED_TREES = ['lib/', '.auroq-core/core/synapse/', '.claude/hooks/'];
33
+ const FORBIDDEN_PREFIXES = ['business/', 'docs/knowledge/', 'casca/', '.git/', '.pack-casca-backup/'];
34
+
35
+ function safeRelative(relative) {
36
+ if (typeof relative !== 'string' || !relative || relative.includes('\\') || relative.startsWith('/')
37
+ || relative.split('/').some(part => !part || part === '.' || part === '..') || /^[A-Za-z]:/.test(relative)) {
38
+ throw new Error('Invalid package path');
39
+ }
40
+ return relative;
41
+ }
42
+
43
+ function checkedFile(root, relative) {
44
+ safeRelative(relative);
45
+ let current = root;
46
+ for (const segment of relative.split('/')) {
47
+ current = path.join(current, segment);
48
+ if (fs.lstatSync(current).isSymbolicLink()) throw new Error(`Symlink in protected path: ${relative}`);
49
+ }
50
+ if (!fs.statSync(current).isFile()) throw new Error(`Not a regular file: ${relative}`);
51
+ return current;
52
+ }
53
+
54
+ function walk(root, relative) {
55
+ const out = [];
56
+ const dir = path.join(root, relative);
57
+ if (!fs.existsSync(dir)) throw new Error(`Missing protected directory: ${relative}`);
58
+ if (fs.lstatSync(dir).isSymbolicLink()) throw new Error(`Symlink in protected directory: ${relative}`);
59
+ for (const item of fs.readdirSync(dir, { withFileTypes: true })) {
60
+ const rel = `${relative}${item.name}`;
61
+ if (item.isSymbolicLink()) throw new Error(`Symlink in protected path: ${rel}`);
62
+ if (item.isDirectory()) out.push(...walk(root, `${rel}/`));
63
+ else if (item.isFile() && rel !== INVENTORY_PATH) out.push(rel);
64
+ }
65
+ return out;
66
+ }
67
+
68
+ function inventoryPaths(root) {
69
+ return [...new Set([...REQUIRED_FILES, ...PROTECTED_TREES.flatMap(dir => walk(root, dir))])].sort();
70
+ }
71
+
72
+ function digest(root, relative) {
73
+ return crypto.createHash('sha256').update(fs.readFileSync(checkedFile(root, relative))).digest('hex');
74
+ }
75
+
76
+ function resolveLocal(root, source, reference) {
77
+ const relative = path.posix.normalize(path.posix.join(path.posix.dirname(source), reference));
78
+ safeRelative(relative);
79
+ const candidates = path.posix.extname(relative) ? [relative] : [relative, `${relative}.js`, `${relative}.json`, `${relative}/index.js`];
80
+ const found = candidates.find(candidate => fs.existsSync(path.join(root, candidate)) && fs.statSync(path.join(root, candidate)).isFile());
81
+ if (!found) throw new Error(`Broken required local reference: ${source} -> ${reference}`);
82
+ checkedFile(root, found);
83
+ return found;
84
+ }
85
+
86
+ /** Literal imports/requires in active CLI/lib/AHI/SYNAPSE code.
87
+ * Dynamic SYNAPSE layers/entry targets are explicitly required above. Diagnostic
88
+ * tooling is outside this runtime scope; only the precise optional Pro edge is exempt.
89
+ */
90
+ function verifyReferences(root, paths) {
91
+ const sources = paths.filter(p => p === 'bin/auroq-os.js'
92
+ || (/\.(?:js|mjs|cjs)$/.test(p) && (p.startsWith('lib/') || p.startsWith('.claude/hooks/ahi/')))
93
+ || p === `${SYNAPSE_ROOT}engine.js`
94
+ || /^\.auroq-core\/core\/synapse\/(?:context|domain|layers|output|session|runtime|memory|utils)\/.*\.js$/.test(p)
95
+ || p === '.claude/hooks/ahi-capture.cjs');
96
+ const protectedSet = new Set(paths);
97
+ for (const source of sources) {
98
+ const content = fs.readFileSync(checkedFile(root, source), 'utf8')
99
+ .replace(/\/\*[\s\S]*?\*\//g, '').replace(/^\s*\/\/.*$/gm, '');
100
+ const references = /(?:\brequire\s*\(\s*|\bimport\s*\(\s*|\bfrom\s+|\bimport\s+)['"](\.[^'"\r\n]+)['"]/g;
101
+ for (const match of content.matchAll(references)) {
102
+ if (source === `${SYNAPSE_ROOT}memory/synapse-memory-provider.js`
103
+ && match[1] === '../../../../pro/memory/memory-loader') continue;
104
+ const target = resolveLocal(root, source, match[1]);
105
+ if (!protectedSet.has(target)) throw new Error(`Required reference outside protected inventory: ${source} -> ${match[1]}`);
106
+ }
107
+ }
108
+ for (const source of ['.claude/settings.json', '.claude/settings.local.json']) {
109
+ const settings = JSON.parse(fs.readFileSync(checkedFile(root, source), 'utf8'));
110
+ for (const groups of Object.values(settings.hooks || {})) {
111
+ for (const group of groups) for (const hook of group.hooks || []) {
112
+ const match = typeof hook.command === 'string' && hook.command.match(/^node\s+(\.claude\/hooks\/[^\s]+)(?:\s|$)/);
113
+ if (match) checkedFile(root, match[1]);
114
+ }
115
+ }
116
+ }
117
+ }
118
+
119
+ export function writeRuntimeInventory(root) {
120
+ const paths = inventoryPaths(root);
121
+ verifyReferences(root, paths);
122
+ const version = JSON.parse(fs.readFileSync(checkedFile(root, 'package.json'), 'utf8')).version;
123
+ const inventory = { schema: 1, algorithm: 'sha256', scope: 'packaged-runtime-and-cascas', version,
124
+ files: paths.map(file => ({ path: file, sha256: digest(root, file) })) };
125
+ fs.writeFileSync(path.join(root, INVENTORY_PATH), `${JSON.stringify(inventory, null, 2)}\n`);
126
+ return inventory;
127
+ }
128
+
129
+ export function verifyRuntimeInventory(root, packedPaths) {
130
+ const inventory = JSON.parse(fs.readFileSync(checkedFile(root, INVENTORY_PATH), 'utf8'));
131
+ const version = JSON.parse(fs.readFileSync(checkedFile(root, 'package.json'), 'utf8')).version;
132
+ if (inventory.schema !== 1 || inventory.algorithm !== 'sha256' || inventory.scope !== 'packaged-runtime-and-cascas'
133
+ || inventory.version !== version || !Array.isArray(inventory.files)) throw new Error('Invalid runtime inventory');
134
+ const expected = inventoryPaths(root);
135
+ const entries = new Map();
136
+ for (const entry of inventory.files) {
137
+ safeRelative(entry.path);
138
+ if (entries.has(entry.path) || !/^[a-f0-9]{64}$/.test(entry.sha256)) throw new Error('Invalid or duplicate inventory entry');
139
+ entries.set(entry.path, entry.sha256);
140
+ }
141
+ if (entries.size !== expected.length || expected.some(file => !entries.has(file))) throw new Error('Protected inventory is incomplete or changed');
142
+ for (const file of expected) {
143
+ if (digest(root, file) !== entries.get(file)) throw new Error(`Runtime hash mismatch: ${file}`);
144
+ }
145
+ verifyReferences(root, expected);
146
+ if (packedPaths) {
147
+ const included = new Set(packedPaths.map(safeRelative));
148
+ for (const file of [...expected, INVENTORY_PATH]) {
149
+ if (!included.has(file)) throw new Error(`Protected file missing from package: ${file}`);
150
+ }
151
+ for (const file of included) {
152
+ const parts = file.split('/');
153
+ if (FORBIDDEN_PREFIXES.some(prefix => file.startsWith(prefix))
154
+ || (file.startsWith('agents/') && !file.startsWith('agents/organizer/'))
155
+ || parts.some(part => part === '.npmrc' || part === '.env' || (part.startsWith('.env.') && part !== '.env.example'))
156
+ || file.includes('arcane-method') || file.includes('consultor/knowledge')) throw new Error(`Forbidden package file: ${file}`);
157
+ }
158
+ }
159
+ return { version, files: expected.length };
160
+ }
161
+
162
+ if (process.argv[1] && fs.existsSync(process.argv[1])
163
+ && fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url))) {
164
+ try {
165
+ const args = process.argv.slice(2).filter(arg => arg !== '--verify');
166
+ if (args.length > 1 || args.some(arg => arg.startsWith('--'))) throw new Error('Usage: runtime-integrity.mjs [--verify] [package-root]');
167
+ const result = verifyRuntimeInventory(path.resolve(args[0] || '.'));
168
+ console.log(`Runtime inventory verified: ${result.files} files (SHA256; not a signature).`);
169
+ } catch (error) {
170
+ console.error(`Runtime inventory failed: ${error.message}`);
171
+ process.exitCode = 1;
172
+ }
173
+ }