deadbyte-mcp 0.15.27 → 0.15.28

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.
@@ -8,7 +8,7 @@ import { loadPowerShellDataFile, runSlotUpdateCli } from '../src/runtime-update.
8
8
  import { parseActivePointer } from '../src/release-slot.mjs';
9
9
 
10
10
  const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
11
- const state=path.join(os.homedir(),'.deadbyte-mcp');
11
+ const state=path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));
12
12
  const sha=async f=>createHash('sha256').update(await readFile(f)).digest('hex');
13
13
  const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
14
14
 
@@ -0,0 +1,58 @@
1
+ $ErrorActionPreference = 'Stop'
2
+ Set-StrictMode -Version 3.0
3
+
4
+ function Invoke-GateStep {
5
+ param([Parameter(Mandatory)][string]$Name,[Parameter(Mandatory)][string[]]$NpmArgs)
6
+ Write-Host "==== $Name ===="
7
+ & npm.cmd @NpmArgs
8
+ if ($LASTEXITCODE -ne 0) { throw "DEADBYTE 0.15.28 / R51 gate failed at $Name with exit code $LASTEXITCODE" }
9
+ }
10
+
11
+ function Invoke-NodeTest {
12
+ param([Parameter(Mandatory)][string]$Name,[Parameter(Mandatory)][string[]]$TestFiles)
13
+ Write-Host "==== $Name ===="
14
+ & node.exe --test --test-concurrency=1 @TestFiles
15
+ if ($LASTEXITCODE -ne 0) { throw "DEADBYTE 0.15.28 / R51 gate failed at $Name with exit code $LASTEXITCODE" }
16
+ }
17
+
18
+ Invoke-NodeTest 'r51-package-boundary' @('test/package-boundary.test.mjs')
19
+ Invoke-NodeTest 'r51-release-smoke-isolation' @('test/release-smoke-isolation.test.mjs')
20
+ Invoke-GateStep 'r51-focused-contracts' @('run','r51:focused:test')
21
+ Invoke-GateStep 'containment-build' @('run','containment:build')
22
+ Invoke-GateStep 'process-policy-refresh' @('run','process:policy:refresh')
23
+ Write-Host "==== desktop-policy-refresh ===="
24
+ & (Join-Path $PSScriptRoot 'init-desktop-policy-r40.ps1')
25
+ Invoke-GateStep 'hardware-build' @('run','hardware:build')
26
+
27
+ $hostPolicy = [System.IO.Path]::GetFullPath((Join-Path $PSScriptRoot '..\controller\deadbyte-host-policy.json'))
28
+ Remove-Item -LiteralPath $hostPolicy -Force -ErrorAction SilentlyContinue
29
+ if (Test-Path -LiteralPath $hostPolicy) { throw 'could not establish hermetic Host policy absence' }
30
+
31
+ Invoke-GateStep 'r51-release-focused' @('run','r48:release:test')
32
+ Invoke-NodeTest 'r49-donor-proof' @('test/r49-remote-control-donors.test.mjs')
33
+ Invoke-NodeTest 'r49-desktop-wait' @('test/desktop-runtime-r40.test.mjs','test/desktop-mcp-tools-r40.test.mjs','test/autonomous-desktop-r40.test.mjs')
34
+ Invoke-GateStep 'r49-catalog' @('run','r48:catalog:test')
35
+ Invoke-GateStep 'commander-hardening' @('run','r47:hardening:test')
36
+ Invoke-GateStep 'full-regression' @('run','r48:full:test')
37
+ Invoke-GateStep 'mcp-smoke' @('run','mcp:smoke')
38
+ Invoke-GateStep 'mcp-agent-smoke' @('run','mcp:agent:smoke')
39
+ Invoke-GateStep 'mcp-coding-smoke' @('run','mcp:coding:smoke')
40
+ Invoke-GateStep 'mcp-machine-fs-smoke' @('run','mcp:machine-fs:smoke')
41
+ Invoke-GateStep 'mcp-observation-smoke' @('run','mcp:observation:smoke')
42
+ Invoke-GateStep 'mcp-process-smoke' @('run','mcp:process:smoke')
43
+ Invoke-GateStep 'mcp-autonomous-smoke' @('run','mcp:autonomous:smoke')
44
+ Invoke-GateStep 'tunnel-stdio-compat-smoke' @('run','mcp:tunnel-compat:smoke')
45
+ Invoke-GateStep 'containment-smoke' @('run','containment:smoke')
46
+ Invoke-GateStep 'contained-smoke' @('run','contained:smoke')
47
+ Invoke-GateStep 'mcp-contained-smoke' @('run','mcp:contained:smoke')
48
+ Invoke-GateStep 'host-policy-refresh' @('run','host:policy:init')
49
+ Invoke-GateStep 'manifest-write' @('run','manifest:write')
50
+ Invoke-GateStep 'manifest-verify' @('run','manifest:verify')
51
+ Invoke-GateStep 'doctor' @('run','doctor')
52
+ Invoke-GateStep 'mcp-host-smoke' @('run','mcp:host:smoke')
53
+ Invoke-GateStep 'authority-smoke' @('run','authority:smoke')
54
+ Invoke-GateStep 'manifest-final-verify' @('run','manifest:verify')
55
+
56
+ & node.exe (Join-Path $PSScriptRoot 'windows-gate-evidence-r51.mjs')
57
+ if ($LASTEXITCODE -ne 0) { throw "R51 evidence writer failed with exit code $LASTEXITCODE" }
58
+ Write-Host '==== DEADBYTE 0.15.28 / R51 WINDOWS GATE: PASS ===='
@@ -0,0 +1,84 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdir, open, readFile } from 'node:fs/promises';
3
+ import os from 'node:os';
4
+ import path from 'node:path';
5
+ import { fileURLToPath } from 'node:url';
6
+ import { doctor } from '../src/doctor-r45.mjs';
7
+
8
+ const root = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..');
9
+ const stateRoot = path.resolve(process.env.DEADBYTE_STATE_ROOT || path.join(os.homedir(), '.deadbyte-mcp'));
10
+ const sha256 = bytes => createHash('sha256').update(bytes).digest('hex');
11
+ const insist = (ok, message) => { if (!ok) throw new Error(message); };
12
+ const pkg = JSON.parse(await readFile(path.join(root, 'package.json'), 'utf8'));
13
+ const release = JSON.parse(await readFile(path.join(root, 'RELEASE.json'), 'utf8'));
14
+ const active = JSON.parse(await readFile(path.join(stateRoot, 'active-release.json'), 'utf8'));
15
+ const manifestSha256 = sha256(await readFile(path.join(root, 'MANIFEST.SHA256')));
16
+
17
+ insist(pkg.version === '0.15.28' && release.version === pkg.version && release.train === 'R51', 'R51 candidate identity mismatch');
18
+ insist(
19
+ active.release_id === release.parent.release_id &&
20
+ active.manifest_sha256 === release.parent.manifest_sha256 &&
21
+ active.generation_manifest_sha256 === release.parent.generation_manifest_sha256,
22
+ 'active runtime is not the exact sealed R51 parent'
23
+ );
24
+
25
+ const health = await doctor();
26
+ insist(health.status === 'ok' && health.package_version === pkg.version && health.manifest?.verified === true, 'R51 doctor evidence invalid');
27
+ insist(health.policy?.host_v2_ready === true, 'R51 Host policy v2 is not ready');
28
+
29
+ const evidence = {
30
+ schema: 'deadbyte.r51-windows-gate.v1',
31
+ status: 'passed',
32
+ exit_code: 0,
33
+ version: pkg.version,
34
+ train: 'R51',
35
+ completed_at_utc: new Date().toISOString(),
36
+ manifest_sha256: manifestSha256,
37
+ parent: release.parent,
38
+ active_predecessor: {
39
+ release_id: active.release_id,
40
+ version: active.version,
41
+ manifest_sha256: active.manifest_sha256,
42
+ generation_manifest_sha256: active.generation_manifest_sha256
43
+ },
44
+ doctor: { verified_entries: health.manifest.verified_entries, host_v2_ready: true },
45
+ gates: {
46
+ package_boundary: true,
47
+ release_smoke_isolation: true,
48
+ runtime_coherence: true,
49
+ coding_command_singleflight: true,
50
+ foreground_desktop_host_fail_closed: true,
51
+ livelog_optimizer: true,
52
+ full_regression: true,
53
+ commander_hardening: true,
54
+ donor_proof: true,
55
+ desktop_wait: true,
56
+ exact_catalog: true,
57
+ stdio_smokes: true,
58
+ manifest_final_verify: true
59
+ }
60
+ };
61
+
62
+ const bytes = Buffer.from(`${JSON.stringify(evidence, null, 2)}\n`);
63
+ const dir = path.join(stateRoot, 'evidence', 'release');
64
+ await mkdir(dir, { recursive: true });
65
+ const target = path.join(dir, `r51-windows-gate-${manifestSha256.slice(0, 16)}.json`);
66
+
67
+ try {
68
+ const handle = await open(target, 'wx', 0o600);
69
+ try { await handle.writeFile(bytes); await handle.sync(); }
70
+ finally { await handle.close(); }
71
+ } catch (error) {
72
+ if (error?.code !== 'EEXIST') throw error;
73
+ const prior = JSON.parse(await readFile(target, 'utf8'));
74
+ insist(
75
+ prior.schema === evidence.schema &&
76
+ prior.status === 'passed' &&
77
+ prior.version === evidence.version &&
78
+ prior.manifest_sha256 === manifestSha256 &&
79
+ prior.parent?.release_id === release.parent.release_id,
80
+ 'conflicting R51 gate evidence identity'
81
+ );
82
+ }
83
+
84
+ console.log(JSON.stringify({ status: 'passed', path: target, sha256: sha256(bytes), manifest_sha256: manifestSha256 }));
@@ -368,6 +368,93 @@ function requestSha(operation, args) {
368
368
  export function createCodingRuntime({ policy, signingKeyPath, verifyKeyPath }) {
369
369
  if (!policy) throw new Error('coding runtime requires policy');
370
370
  if (!signingKeyPath || !verifyKeyPath) throw new Error('coding runtime requires trust key paths');
371
+ const commandFlights = new Map();
372
+ const commandResults = new Map();
373
+ const COMMAND_RESULT_TTL_MS = 5 * 60 * 1000;
374
+ const COMMAND_RESULT_CACHE_MAX = 16;
375
+ const COMMAND_FLIGHT_SCHEMA = 'deadbyte.coding-command-flight.v1';
376
+ const COMMAND_RESULT_SCHEMA = 'deadbyte.coding-command-result.v1';
377
+ const commandFlightDir = path.join(policy.evidence_dir, 'command-flights');
378
+ function pruneCommandResults(now = Date.now()) {
379
+ for (const [key, entry] of commandResults) if (now - entry.completed_at_ms > COMMAND_RESULT_TTL_MS) commandResults.delete(key);
380
+ while (commandResults.size > COMMAND_RESULT_CACHE_MAX) commandResults.delete(commandResults.keys().next().value);
381
+ }
382
+ function commandFlightPaths(executionKey) {
383
+ const executionKeySha256 = sha256Bytes(Buffer.from(executionKey, 'utf8'));
384
+ return {
385
+ executionKeySha256,
386
+ claimPath: path.join(commandFlightDir, `${executionKeySha256}.claim.json`),
387
+ resultPath: path.join(commandFlightDir, `${executionKeySha256}.result.json`)
388
+ };
389
+ }
390
+ async function readDurableCommandResult(resultPath, executionKeySha256, request) {
391
+ let raw;
392
+ try { raw = JSON.parse(decodeUtf8Strict(await readFile(resultPath))); }
393
+ catch (error) {
394
+ if (error?.code === 'ENOENT') return null;
395
+ throw new Error(`coding command durable result invalid: ${String(error?.message ?? error)}`);
396
+ }
397
+ if (raw?.schema !== COMMAND_RESULT_SCHEMA || raw.execution_key_sha256 !== executionKeySha256 || raw.request_sha256 !== request ||
398
+ !Number.isInteger(raw.completed_at_ms) || !raw.result || typeof raw.result !== 'object' || Array.isArray(raw.result)) {
399
+ throw new Error('coding command durable result identity invalid');
400
+ }
401
+ if (Date.now() - raw.completed_at_ms > COMMAND_RESULT_TTL_MS) {
402
+ await rm(resultPath, { force: true });
403
+ return null;
404
+ }
405
+ const value = raw.result;
406
+ if (value.request_sha256 !== request || !RECEIPT_ID_RE.test(value.receipt_id ?? '') || !SHA256_RE.test(value.receipt_sha256 ?? '')) {
407
+ throw new Error('coding command durable result receipt identity invalid');
408
+ }
409
+ const verified = await verifyCodingReceiptFile(policy, verifyKeyPath, value.receipt_id);
410
+ if (!verified.ok || verified.request_sha256 !== request || verified.receipt_sha256 !== value.receipt_sha256) {
411
+ throw new Error(`coding command durable result receipt verification failed: ${verified.reason ?? 'identity mismatch'}`);
412
+ }
413
+ return { ...value };
414
+ }
415
+ async function writeDurableCommandResult(resultPath, executionKeySha256, request, value) {
416
+ const body = Buffer.from(`${JSON.stringify({ schema:COMMAND_RESULT_SCHEMA, execution_key_sha256:executionKeySha256,
417
+ request_sha256:request, completed_at_ms:Date.now(), result:value })}\n`, 'utf8');
418
+ try {
419
+ const handle = await open(resultPath, 'wx', 0o600);
420
+ try { await handle.writeFile(body); await handle.sync(); }
421
+ finally { await handle.close(); }
422
+ } catch (error) {
423
+ if (error?.code !== 'EEXIST') throw error;
424
+ const prior = await readDurableCommandResult(resultPath, executionKeySha256, request);
425
+ if (!prior || prior.receipt_id !== value.receipt_id || prior.receipt_sha256 !== value.receipt_sha256) {
426
+ throw new Error('coding command durable result conflict');
427
+ }
428
+ }
429
+ }
430
+ async function acquireDurableCommandFlight(executionKey, request, waitMs) {
431
+ await mkdir(commandFlightDir, { recursive: true });
432
+ const paths = commandFlightPaths(executionKey);
433
+ const cached = await readDurableCommandResult(paths.resultPath, paths.executionKeySha256, request);
434
+ if (cached) return { ...paths, owned:false, cached };
435
+ try {
436
+ const handle = await open(paths.claimPath, 'wx', 0o600);
437
+ try {
438
+ await handle.writeFile(Buffer.from(`${JSON.stringify({ schema:COMMAND_FLIGHT_SCHEMA, execution_key_sha256:paths.executionKeySha256,
439
+ request_sha256:request, owner_pid:process.pid, started_at_ms:Date.now() })}\n`, 'utf8'));
440
+ await handle.sync();
441
+ } finally { await handle.close(); }
442
+ return { ...paths, owned:true, cached:null };
443
+ } catch (error) {
444
+ if (error?.code !== 'EEXIST') throw error;
445
+ }
446
+ const deadline = Date.now() + waitMs;
447
+ for (;;) {
448
+ const result = await readDurableCommandResult(paths.resultPath, paths.executionKeySha256, request);
449
+ if (result) return { ...paths, owned:false, cached:result };
450
+ let claimExists = true;
451
+ try { await stat(paths.claimPath); }
452
+ catch (error) { if (error?.code === 'ENOENT') claimExists = false; else throw error; }
453
+ if (!claimExists) return acquireDurableCommandFlight(executionKey, request, Math.max(100, deadline - Date.now()));
454
+ if (Date.now() >= deadline) throw new Error('identical coding command is still running in another runtime instance');
455
+ await new Promise(resolve => setTimeout(resolve, 100));
456
+ }
457
+ }
371
458
 
372
459
  async function sessionStatus() {
373
460
  const status = await codingSessionStatus(policy);
@@ -587,23 +674,67 @@ export function createCodingRuntime({ policy, signingKeyPath, verifyKeyPath }) {
587
674
  root_id: rootId, command_id: commandId, script_path: scriptPath, script_sha256: scriptSha256,
588
675
  args, cwd, timeout_ms: timeoutMs
589
676
  });
590
- const started = new Date().toISOString();
591
- const result = await runCommandOnce({ rootId, commandId, scriptPath, scriptSha256, args, cwd, timeoutMs, executionDeadlineUtc, preExecutionCheck });
592
- const signed = await signCodingReceipt(policy, signingKeyPath, result.lease, {
593
- operation: 'coding_command_run', request_sha256: request, root_id: rootId, command_id: commandId,
594
- script_path: scriptPath, executable_sha256: result.executable_sha256, script_sha256: result.script_sha256,
595
- started_at_utc: started, completed_at_utc: new Date().toISOString(), exit_code: result.exit_code,
596
- signal: result.signal, timed_out: result.timed_out, elapsed_ms: result.elapsed_ms,
597
- stdout_sha256: result.stdout_sha256, stderr_sha256: result.stderr_sha256
598
- }, receiptId);
599
- return {
600
- status: result.timed_out || result.exit_code !== 0 ? 'observed_failure' : 'passed',
601
- root_id: rootId, command_id: commandId, executable_sha256: result.executable_sha256,
602
- script_sha256: result.script_sha256, exit_code: result.exit_code, signal: result.signal,
603
- timed_out: result.timed_out, timeout_ms: result.timeout_ms, elapsed_ms: result.elapsed_ms,
604
- stdout: result.stdout, stderr: result.stderr,
605
- receipt_id: signed.receipt.receipt_id, receipt_sha256: signed.receipt.receipt_sha256, request_sha256: request
606
- };
677
+ const executionKey = receiptId === null ? request : `${request}:${receiptId}`;
678
+ pruneCommandResults();
679
+ const cached = commandResults.get(executionKey);
680
+ if (cached) {
681
+ await assertCodingArmed(policy, rootId);
682
+ return { ...cached.result };
683
+ }
684
+ const inFlight = commandFlights.get(executionKey);
685
+ if (inFlight) {
686
+ await assertCodingArmed(policy, rootId);
687
+ return { ...(await inFlight) };
688
+ }
689
+ if (!ID_RE.test(commandId ?? '')) throw new Error('invalid coding command id');
690
+ const command = policy.commands[commandId];
691
+ if (!command) throw new Error(`coding command '${commandId}' is not allowed`);
692
+ if (!command.root_ids.includes(rootId)) throw new Error(`coding command '${commandId}' is not allowed for root '${rootId}'`);
693
+ if (!Array.isArray(args) || args.length > 64 || args.some(item => typeof item !== 'string' || item.length > 2048 || item.includes('\0'))) {
694
+ throw new Error('coding command args must be <=64 strings of <=2048 characters');
695
+ }
696
+ if (!SHA256_RE.test(scriptSha256 ?? '')) throw new Error('script_sha256 must be lowercase SHA-256');
697
+ const requestedTimeout = timeoutMs === null ? command.timeout_ms : finiteInteger(timeoutMs, 'timeout_ms', 100, command.timeout_ms, command.timeout_ms);
698
+ const durable = await acquireDurableCommandFlight(executionKey, request, Math.min(900_000, requestedTimeout + 10_000));
699
+ if (durable.cached) {
700
+ await assertCodingArmed(policy, rootId);
701
+ commandResults.set(executionKey, { completed_at_ms: Date.now(), result: durable.cached });
702
+ pruneCommandResults();
703
+ return { ...durable.cached };
704
+ }
705
+ const execution = (async () => {
706
+ const started = new Date().toISOString();
707
+ const result = await runCommandOnce({ rootId, commandId, scriptPath, scriptSha256, args, cwd, timeoutMs, executionDeadlineUtc, preExecutionCheck });
708
+ const signed = await signCodingReceipt(policy, signingKeyPath, result.lease, {
709
+ operation: 'coding_command_run', request_sha256: request, root_id: rootId, command_id: commandId,
710
+ script_path: scriptPath, executable_sha256: result.executable_sha256, script_sha256: result.script_sha256,
711
+ started_at_utc: started, completed_at_utc: new Date().toISOString(), exit_code: result.exit_code,
712
+ signal: result.signal, timed_out: result.timed_out, elapsed_ms: result.elapsed_ms,
713
+ stdout_sha256: result.stdout_sha256, stderr_sha256: result.stderr_sha256
714
+ }, receiptId);
715
+ const value = {
716
+ status: result.timed_out || result.exit_code !== 0 ? 'observed_failure' : 'passed',
717
+ root_id: rootId, command_id: commandId, executable_sha256: result.executable_sha256,
718
+ script_sha256: result.script_sha256, exit_code: result.exit_code, signal: result.signal,
719
+ timed_out: result.timed_out, timeout_ms: result.timeout_ms, elapsed_ms: result.elapsed_ms,
720
+ stdout: result.stdout, stderr: result.stderr,
721
+ receipt_id: signed.receipt.receipt_id, receipt_sha256: signed.receipt.receipt_sha256, request_sha256: request
722
+ };
723
+ await writeDurableCommandResult(durable.resultPath, durable.executionKeySha256, request, value);
724
+ return value;
725
+ })();
726
+ commandFlights.set(executionKey, execution);
727
+ let completedDurably = false;
728
+ try {
729
+ const value = await execution;
730
+ completedDurably = true;
731
+ commandResults.set(executionKey, { completed_at_ms: Date.now(), result: value });
732
+ pruneCommandResults();
733
+ return { ...value };
734
+ } finally {
735
+ if (completedDurably && durable.owned) await rm(durable.claimPath, { force: true });
736
+ if (commandFlights.get(executionKey) === execution) commandFlights.delete(executionKey);
737
+ }
607
738
  }
608
739
 
609
740
  function percentile(samples, fraction) {
@@ -13,8 +13,9 @@ import { openRemoteResultJournal, parseRemoteResultCursor } from './remote-resul
13
13
  import { DEADBYTE_VERSION } from './version.mjs';
14
14
  import { parseActivePointer } from './release-slot.mjs';
15
15
  import { printDoctor } from './doctor-r45.mjs';
16
- import { createLiveTimelineFilter, createLiveTimelineTracker, createLiveTimelineWriter, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine } from './remote-live-log.mjs';
16
+ import { createLiveTimelineFilter, createLiveTimelineTracker, createLiveTimelineWriter, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine, selectLiveLogNames } from './remote-live-log.mjs';
17
17
  import { prepareRemoteConsole, remoteDisplayText } from './remote-console.mjs';
18
+ import { ensureRuntimeCoherence } from './runtime-coherence.mjs';
18
19
 
19
20
  const REMOTE_COMMANDS=new Set([
20
21
  'menu','start','resume','park','stop','restart','status','url','test','fulltest','toggle','clean','machinetest',
@@ -29,12 +30,17 @@ const SESSION_STATUS=[
29
30
  ['processstatus',/^Process plane\s*:\s*ARMED\b/m],
30
31
  ['codingstatus',/^Coding plane\s*:\s*ARMED\b/m],
31
32
  ['autonomousstatus',/^Autonomous\s*:\s*ARMED\b/m],
33
+ ['desktopstatus',/^Desktop plane\s*:\s*DISARMED\b/m],
32
34
  ['hoststatus',/^Host plane\s*:\s*DISARMED\b/m]
33
35
  ];
34
- const SESSION_CLEANUP=['disarmautonomous','disarmcoding','disarmprocess','disarmhost','park'];
36
+ const SESSION_CLEANUP=['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','park'];
35
37
  const SUPERVISOR_COMMANDS=Object.freeze({
36
38
  start:'supervisorstart',stop:'supervisorstop',status:'supervisorstatus',recover:'supervisorrecover'
37
39
  });
40
+ const CURRENT_PACKAGE_ROOT=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
41
+ const CURRENT_DEPLOY_SCRIPT=path.join(CURRENT_PACKAGE_ROOT,'scripts','deploy-r48.mjs');
42
+ const REMOTE_UPDATE_TIMEOUT_MS=900000;
43
+ const LIVE_LOG_DISCOVERY_INTERVAL_MS=1000;
38
44
 
39
45
  function stateRoot(){return path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));}
40
46
  async function requireRegular(file,label){const info=await lstat(file);if(!info.isFile()||info.isSymbolicLink()) throw new Error(`${label} must be a regular non-symlink file`);return file;}
@@ -94,6 +100,43 @@ async function reportVersionCoherence(root,{displayMode='ascii'}={}){
94
100
  writeRemote(process.stderr,`${remoteTag('warn',displayMode)} VERSION SKEW: CLI package ${DEADBYTE_VERSION}; active runtime ${active.version} (${active.release_id})\n`,displayMode);
95
101
  return active;
96
102
  }
103
+ async function synchronizeForegroundRuntime(ctx,{displayMode='unicode'}={}){
104
+ const packageManifestSha256=createHash('sha256').update(await readFile(path.join(CURRENT_PACKAGE_ROOT,'MANIFEST.SHA256'))).digest('hex');
105
+ return ensureRuntimeCoherence({
106
+ cliVersion:DEADBYTE_VERSION,
107
+ packageManifestSha256,
108
+ readActive:()=>readActiveRuntimeBinding(ctx.root),
109
+ disarmPrivileged:async()=>{
110
+ for(const command of ['disarmhost','disarmdesktop']){
111
+ const result=invokeController(ctx,command,{capture:true,allowFailure:true,echoCaptured:false,timeoutMs:REMOTE_STATUS_TIMEOUT_MS});
112
+ if(result.status!==0) throw new Error(`runtime coherence '${command}' failed exit=${result.status}`);
113
+ }
114
+ },
115
+ activateCurrentPackage:async()=>{
116
+ const result=spawnSync(process.execPath,[CURRENT_DEPLOY_SCRIPT],{
117
+ cwd:CURRENT_PACKAGE_ROOT,
118
+ env:{...process.env,DEADBYTE_STATE_ROOT:ctx.root},
119
+ encoding:'utf8',
120
+ windowsHide:true,
121
+ shell:false,
122
+ timeout:REMOTE_UPDATE_TIMEOUT_MS,
123
+ maxBuffer:16*1024*1024
124
+ });
125
+ if(result.error){
126
+ if(result.error?.code==='ETIMEDOUT') throw new Error(`runtime coherence activation timed out after ${REMOTE_UPDATE_TIMEOUT_MS}ms`);
127
+ throw result.error;
128
+ }
129
+ if(result.status!==0){
130
+ const detail=boundedSingleLine(result.stderr||result.stdout||'no deployment output',2048);
131
+ throw new Error(`runtime coherence activation failed exit=${String(result.status)}: ${detail}`);
132
+ }
133
+ },
134
+ onEvent:event=>{
135
+ if(event.phase==='upgrade_start') writeRemote(process.stdout,`[UPDATE] Runtime ${event.from_version} -> ${event.to_version}; activating current package...\n`,displayMode);
136
+ else if(event.phase==='upgrade_complete') writeRemote(process.stdout,`[UPDATE] Runtime synchronized: ${event.release_id||event.to_version}\n`,displayMode);
137
+ }
138
+ });
139
+ }
97
140
  async function readControllerStartFailure(root,command,{maxChars=2048}={}){
98
141
  const file=path.join(root,'runtime','controller-start-failure.json');
99
142
  let raw;
@@ -158,32 +201,34 @@ function controllerRuntimeDegraded(text){
158
201
  return /^DEADBYTE MCP\s*=\s*DEGRADED\s*$/m.test(String(text||''));
159
202
  }
160
203
  async function listLogFiles(root){
161
- const files=[];
162
- const audit=path.join(root,'audit');
163
- const runtimeLogs=path.join(root,'runtime','logs');
164
- for(const [dir,accept] of [
165
- [audit,name=>/^segment-\d{8}\.jsonl$/.test(name)],
166
- [runtimeLogs,name=>/\.log$/i.test(name)]
167
- ]){
168
- let names=[];try{names=await readdir(dir);}catch(error){if(error?.code==='ENOENT') continue;throw error;}
169
- for(const name of names.sort()) if(accept(name)) files.push(path.join(dir,name));
170
- }
171
- return files;
204
+ const auditDir=path.join(root,'audit');
205
+ const runtimeDir=path.join(root,'runtime','logs');
206
+ let auditNames=[],runtimeNames=[];
207
+ try{auditNames=await readdir(auditDir);}catch(error){if(error?.code!=='ENOENT') throw error;}
208
+ try{runtimeNames=await readdir(runtimeDir);}catch(error){if(error?.code!=='ENOENT') throw error;}
209
+ const selected=selectLiveLogNames({auditNames,runtimeNames});
210
+ return [
211
+ ...selected.audit.map(name=>path.join(auditDir,name)),
212
+ ...selected.runtime.map(name=>path.join(runtimeDir,name))
213
+ ];
214
+ }
215
+ function parseAuditLogLine(file,line){
216
+ if(!/^segment-\d{8}\.jsonl$/.test(path.basename(file))||!line) return null;
217
+ try{return JSON.parse(line);}catch{return null;}
172
218
  }
173
- function formatLogLine(file,line,{activityTracker=null,sequence=null,verbose=false,displayMode='unicode'}={}){
219
+ function formatLogLine(file,line,{auditEvent=null,activityTracker=null,sequence=null,verbose=false,displayMode='unicode'}={}){
174
220
  if(!line) return null;
175
221
  if(/^segment-\d{8}\.jsonl$/.test(path.basename(file))){
176
- try{
177
- const event=JSON.parse(line);
178
- return activityTracker?activityTracker.observe(event,{sequence}):formatCompactAuditEvent(event);
179
- }catch{return null;}
222
+ const event=auditEvent??parseAuditLogLine(file,line);
223
+ if(!event) return null;
224
+ return activityTracker?activityTracker.observe(event,{sequence}):formatCompactAuditEvent(event);
180
225
  }
181
226
  return formatRuntimeDiagnosticLine(file,line,{verbose,displayMode});
182
227
  }
183
228
  function remoteResultJournalPath(root,sessionId){return path.join(root,'runtime','remote-results',`${sessionId}.json`);}
184
- function auditTimelineRecord(file,line){
185
- if(!/^segment-\d{8}\.jsonl$/.test(path.basename(file))||!line) return null;
186
- let event;try{event=JSON.parse(line);}catch{return null;}
229
+ function auditTimelineRecord(file,line,auditEvent=null){
230
+ const event=auditEvent??parseAuditLogLine(file,line);
231
+ if(!event) return null;
187
232
  if(typeof event?.request_id!=='string'||!event.request_id||typeof event.tool!=='string'||!event.tool) return null;
188
233
  const stamp=[event.ts,event.created_at_utc,event.observed_at_utc].find(value=>typeof value==='string'&&Number.isFinite(Date.parse(value)));
189
234
  const observed=stamp?new Date(Date.parse(stamp)).toISOString():new Date().toISOString();
@@ -239,7 +284,8 @@ async function readAppend(file,offset){
239
284
  }
240
285
  async function snapshotRemoteLogOffsets(root){
241
286
  const offsets=new Map();
242
- for(const file of await listLogFiles(root)){
287
+ const files=await listLogFiles(root);
288
+ for(const file of files){
243
289
  try{offsets.set(file,(await stat(file)).size);}catch{}
244
290
  }
245
291
  return offsets;
@@ -264,20 +310,28 @@ async function tailRemoteLogs(root,signal,{journal=null,offsets=null,activityTra
264
310
  ? '[LOG] Verbose live timeline. Full sanitized request detail enabled; Ctrl+C disconnects and revokes authority.\n'
265
311
  : '[LOG] Human live timeline. Transport IN/OUT hidden; full replay/audit detail remains on disk. Ctrl+C disconnects and revokes authority.\n');
266
312
  const activeOffsets=offsets??await snapshotRemoteLogOffsets(root);
313
+ let liveFiles=[...activeOffsets.keys()];
314
+ let nextDiscoveryAt=0;
267
315
  while(!signal.aborted){
268
- for(const file of await listLogFiles(root)){
316
+ const now=Date.now();
317
+ if(now>=nextDiscoveryAt){
318
+ liveFiles=await listLogFiles(root);
319
+ nextDiscoveryAt=now+LIVE_LOG_DISCOVERY_INTERVAL_MS;
320
+ }
321
+ for(const file of liveFiles){
269
322
  const prior=activeOffsets.has(file)?activeOffsets.get(file):0;
270
323
  const next=await readAppend(file,prior);
271
324
  activeOffsets.set(file,next.offset);
272
325
  for(const line of next.text.split(/\r?\n/)){
273
- const timeline=auditTimelineRecord(file,line);
326
+ const auditEvent=parseAuditLogLine(file,line);
327
+ const timeline=auditTimelineRecord(file,line,auditEvent);
274
328
  let recorded=null;
275
329
  if(timeline&&journal) recorded=timeline.event_type==='tool.call'
276
330
  ? await journal.recordCall(timeline)
277
331
  : await journal.recordTerminal(timeline);
278
332
  if(recorded?.duplicate) continue;
279
333
  const accepted=timelineFilter&&timeline?timelineFilter.accept(timelineFilterEvent(timeline)):true;
280
- const formatted=formatLogLine(file,line,{activityTracker,sequence:recorded?.event?.seq??null,verbose,displayMode});
334
+ const formatted=formatLogLine(file,line,{auditEvent,activityTracker,sequence:recorded?.event?.seq??null,verbose,displayMode});
281
335
  if(!formatted||!accepted) continue;
282
336
  const output=Array.isArray(formatted)?formatted:[formatted];
283
337
  for(const item of output) if(item){
@@ -381,11 +435,17 @@ async function runRemoteSession({resumeCursor=null,verbose=false,trace=false,fil
381
435
  const display=prepareRemoteConsole();
382
436
  const displayMode=display.mode;
383
437
  const ctx=await remoteContext();
384
- await reportVersionCoherence(ctx.root,{displayMode});
385
438
  if(displayMode==='ascii') writeRemote(process.stdout,`[DISPLAY] ASCII fallback: ${display.reason}\n`,displayMode);
386
439
  if(resumeCursor!==null) await replayRemoteResults(ctx.root,resumeCursor,{displayMode});
387
440
  await recoverRemoteSession(ctx,{displayMode});
388
441
  const marker=await claimRemoteSession(ctx);
442
+ try{
443
+ await synchronizeForegroundRuntime(ctx,{displayMode});
444
+ }catch(error){
445
+ await cleanupRemoteSession(ctx,{displayMode});
446
+ await releaseRemoteSession(ctx,marker,{displayMode});
447
+ throw error;
448
+ }
389
449
  const resultJournal=await openRemoteResultJournal({
390
450
  filePath:remoteResultJournalPath(ctx.root,marker.result_session_id),sessionId:marker.result_session_id,maxEntries:2048
391
451
  });
@@ -442,13 +502,13 @@ async function runRemoteSession({resumeCursor=null,verbose=false,trace=false,fil
442
502
  process.stdout.write('[OK] ONLINE\n');
443
503
  process.stdout.write(`${formatRemoteLifecycle('ready',{displayMode})}\n`);
444
504
  if(url) writeRemote(process.stdout,`URL: ${url}\n`,displayMode);
445
- writeRemote(process.stdout,`${remoteTag('auth',displayMode)} Verifying process, coding, autonomous; host must stay DISARMED...\n`,displayMode);
505
+ writeRemote(process.stdout,`${remoteTag('auth',displayMode)} Verifying process, coding, autonomous; desktop + host must stay DISARMED...\n`,displayMode);
446
506
  for(const [command,pattern] of SESSION_STATUS){
447
507
  const result=invokeController(ctx,command,{capture:true,echoCaptured:false});
448
508
  if(!pattern.test(result.stdout)) throw new Error(`remote session authority verification failed: ${command} state mismatch`);
449
509
  }
450
510
  ready=true;
451
- writeRemote(process.stdout,`${remoteTag('auth',displayMode)} ARMED: process, coding, autonomous | host DISARMED\n`,displayMode);
511
+ writeRemote(process.stdout,`${remoteTag('auth',displayMode)} ARMED: process, coding, autonomous | desktop, host DISARMED\n`,displayMode);
452
512
  await logTask;
453
513
  }finally{
454
514
  const wasAborted=aborter.signal.aborted;
@@ -53,6 +53,13 @@ const TOOL_ICONS_ASCII=Object.freeze({read:'R',write:'W',edit:'E',run:'>',verify
53
53
  const START_VISIBLE_PATTERNS=[/(?:^|_)file_write$/,/text_replace$/,/symbol_replace$/,/native_diff_(?:apply|preview)$/,
54
54
  /(?:command|benchmark)_run$/,/task_run$/,/goal_run$/,/goal_submit$/,/goal_approve$/,/process_session_(?:start|action|terminate)$/,/^desktop_action$/];
55
55
 
56
+ export function selectLiveLogNames({auditNames=[],runtimeNames=[]}={}){
57
+ if(!Array.isArray(auditNames)||!Array.isArray(runtimeNames)) throw new Error('live log names must be arrays');
58
+ const audit=auditNames.filter(name=>typeof name==='string'&&/^segment-\d{8}\.jsonl$/.test(name)).sort().slice(-2);
59
+ const runtime=runtimeNames.filter(name=>typeof name==='string'&&/\.log$/i.test(name)).sort();
60
+ return Object.freeze({audit:Object.freeze(audit),runtime:Object.freeze(runtime)});
61
+ }
62
+
56
63
  export function truncateUtf8(value,maxBytes,{ellipsis='…'}={}){
57
64
  if(typeof value!=='string'||!Number.isInteger(maxBytes)||maxBytes<1) return '';
58
65
  const bytes=new TextEncoder().encode(value);
@@ -312,18 +319,19 @@ export function createLiveTimelineWriter({stream=process.stdout,displayMode='uni
312
319
  return Object.freeze({write,flush,pending(){return active?{...active}:null;}});
313
320
  }
314
321
 
315
- export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,heartbeatBurstLimit=10,heartbeatWindowMs=3000,displayMode='unicode',rootPaths={},trace=false}={}){
322
+ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,heartbeatBurstLimit=10,heartbeatWindowMs=3000,displayMode='unicode',rootPaths={},trace=false,slowCallMs=1000}={}){
316
323
  if(typeof clock!=='function') throw new Error('live timeline clock must be a function');
317
324
  if(!REMOTE_DISPLAY_MODES.includes(displayMode)) throw new Error('remote display mode invalid');
318
325
  if(!Number.isInteger(heartbeatBurstLimit)||heartbeatBurstLimit<2||heartbeatBurstLimit>100) throw new Error('heartbeatBurstLimit invalid');
319
326
  if(!Number.isInteger(heartbeatWindowMs)||heartbeatWindowMs<250||heartbeatWindowMs>60000) throw new Error('heartbeatWindowMs invalid');
320
327
  if(!rootPaths||typeof rootPaths!=='object'||Array.isArray(rootPaths)) throw new Error('live timeline rootPaths invalid');
321
328
  if(typeof trace!=='boolean') throw new Error('live timeline trace invalid');
329
+ if(!Number.isInteger(slowCallMs)||slowCallMs<100||slowCallMs>600000) throw new Error('live timeline slowCallMs invalid');
322
330
  const pending=new Map();
323
331
  const completed=new Set();
324
332
  const toolCounts=new Map();
325
333
  const durationSamples=[];
326
- let resultCount=0,okCount=0,errorCount=0,unknownCount=0,mutationCount=0,totalDurationMs=0,slowest=null,burst=null,foldedHeartbeatLines=0;
334
+ let resultCount=0,okCount=0,errorCount=0,unknownCount=0,mutationCount=0,totalDurationMs=0,slowest=null,burst=null,foldedHeartbeatLines=0,peakInFlight=0;
327
335
 
328
336
  function eventPrefix(event,sequence,{displayTool=null,icon=null}={}){
329
337
  const seq=Number.isSafeInteger(sequence)&&sequence>0?'#'+sequence+' ':'';
@@ -336,9 +344,11 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,he
336
344
  const error=event.outcome?.is_error===true;
337
345
  const unknown=event.outcome?.side_effect_state==='unknown';
338
346
  const displayTool=span?.display_tool||event.tool;
339
- const duration=Number.isFinite(Number(event.outcome?.duration_ms))
340
- ?formatDuration(event.outcome.duration_ms)
347
+ const rawDuration=Number(event.outcome?.duration_ms);
348
+ const duration=Number.isFinite(rawDuration)
349
+ ?formatDuration(rawDuration)
341
350
  :span?formatDuration(Math.max(0,clock()-span.started_at_ms)):'';
351
+ const slow=Number.isFinite(rawDuration)&&rawDuration>=slowCallMs?' [SLOW]':'';
342
352
  const summary=span?.summary||'';
343
353
  const rawReason=typeof event.outcome?.reason==='string'&&event.outcome.reason?event.outcome.reason:
344
354
  (unknown&&typeof event.outcome?.recovery?.reason==='string'?event.outcome.recovery.reason:'');
@@ -353,7 +363,7 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,he
353
363
  const prefixIcon=unknown?(displayMode==='ascii'?'WARN':'⚠️'):null;
354
364
  const evidence=receipt?' '+(displayMode==='ascii'?'RECEIPT':'🧾 receipt')+(displayMode==='ascii'?'=':'=')+receipt:'';
355
365
  const reasonSep=displayMode==='ascii'?' - ':' — ';
356
- return remoteDisplayText(eventPrefix(event,sequence,{displayTool,icon:prefixIcon})+traceSuffix(span?.trace_meta)+(summary?' '+summary:'')+(duration?' '+duration:'')+' '+marker+evidence+(reason?reasonSep+reason:''),displayMode);
366
+ return remoteDisplayText(eventPrefix(event,sequence,{displayTool,icon:prefixIcon})+traceSuffix(span?.trace_meta)+(summary?' '+summary:'')+(duration?' '+duration:'')+slow+' '+marker+evidence+(reason?reasonSep+reason:''),displayMode);
357
367
  }
358
368
  function burstLine(value){
359
369
  if(!value) return null;
@@ -396,6 +406,7 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,he
396
406
  const summary=summarizeLiveToolArgs(displayTool,args,{rootPaths});
397
407
  const traceMeta=traceIdentity(event,args);
398
408
  pending.set(requestId,{request_id:requestId,tool,display_tool:displayTool,summary,trace_meta:traceMeta,started_at_ms:clock()});
409
+ peakInFlight=Math.max(peakInFlight,pending.size);
399
410
  if(verbose) return formatRemoteToolCall({...event,tool:displayTool},{clock,displayMode})+traceSuffix(traceMeta);
400
411
  if(visibleStart(displayTool)) return eventPrefix(event,sequence,{displayTool})+traceSuffix(traceMeta)+(summary?' '+summary:'')+' …';
401
412
  return null;
@@ -453,6 +464,7 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,he
453
464
  ' | tool time '+(formatDuration(totalDurationMs)||'0ms')+
454
465
  (p50!==null?' | p50 '+formatDuration(p50)+' | p95 '+formatDuration(p95):'')+
455
466
  (slowest?' | slowest '+slowest.tool+' '+formatDuration(slowest.duration_ms):'')+
467
+ ' | peak in-flight '+peakInFlight+
456
468
  (foldedHeartbeatLines?' | folded '+foldedHeartbeatLines+' heartbeat lines':'')+
457
469
  (top?' | top: '+top:''),displayMode);
458
470
  }
@@ -5,7 +5,7 @@ import os from 'node:os';
5
5
  import path from 'node:path';
6
6
  import { setTimeout as sleep } from 'node:timers/promises';
7
7
 
8
- const CLEANUP=['disarmautonomous','disarmcoding','disarmprocess','disarmhost','park'];
8
+ const CLEANUP=['disarmautonomous','disarmcoding','disarmprocess','disarmdesktop','disarmhost','park'];
9
9
 
10
10
  function parseArgs(argv){
11
11
  const out={};