deadbyte-mcp 0.15.60 → 0.15.62
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/MANIFEST.SHA256 +90 -42
- package/PROVENANCE.json +33 -14
- package/README.txt +18 -18
- package/RELEASE.json +45 -16
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +24 -2
- package/bridge/package.json +1 -1
- package/controller/deadbyte-controller.ps1 +78 -1
- package/controller/deadbyte-process-policy.json +4 -4
- package/docs/R156-LOOP-AND-EPOCH-ROUTING.md +86 -0
- package/docs/R157-ARCHITECTURE-DEPENDENCIES.md +58 -0
- package/docs/R157-ARCHITECTURE-GRAPH.json +2874 -0
- package/docs/R157-ENGINEERING-HANDOFF.md +71 -0
- package/docs/R157-FILE-DELTA.json +80 -0
- package/docs/R157-FOCUSED-VERIFICATION.log +0 -0
- package/docs/R157-REFERENCE-CAPABILITY-MATRIX.md +207 -0
- package/docs/R157-REFERENCE-DECISIONS.md +46 -0
- package/docs/R157-REFERENCE-INVENTORY.json +2849 -0
- package/docs/R158-COMBINED-REGRESSION.log +0 -0
- package/docs/R158-DELTA.json +80 -0
- package/docs/R158-EPOCH-BOOTSTRAP-GREEN.log +0 -0
- package/docs/R158-EPOCH-BOOTSTRAP-RED.log +0 -0
- package/docs/R158-EPOCH-IDENTITY-RED.log +0 -0
- package/docs/R158-FOCUSED-GREEN.log +0 -0
- package/docs/R158-PROTECTED-EPOCH-VERIFIED.md +43 -0
- package/docs/R158-PROTECTED-EXACT-SOURCE-LIVE.log +0 -0
- package/docs/R158-UPDATER-REGRESSION.log +0 -0
- package/docs/R159-COMPLETED-HISTORY-BACKUP/deadbyte-cli.mjs.before +965 -0
- package/docs/R159-COMPLETED-HISTORY-BACKUP/legacy-marker-evidence-r159.mjs.before +102 -0
- package/docs/R159-COMPLETED-HISTORY-BACKUP/r159-legacy-marker-evidence.test.mjs.before +79 -0
- package/docs/R159-COMPLETED-HISTORY-PATCH.json +26 -0
- package/docs/R159-MARKER-LOCATOR-CHECKPOINT.md +46 -0
- package/docs/R160-ENGINEERING-HANDOFF.md +32 -0
- package/docs/R160-REFERENCE-CAPABILITY-MATRIX.json +165 -0
- package/package.json +2 -2
- package/scripts/bootstrap-uac-migration-r153.ps1 +17 -5
- package/scripts/deploy-r48.mjs +7 -6
- package/scripts/gate-windows-r160.ps1 +57 -0
- package/scripts/issue-uac-migration-r153.mjs +12 -5
- package/scripts/npm-release-r48.mjs +5 -0
- package/scripts/npm-release-readiness-r163.mjs +67 -0
- package/scripts/r153-prepublish-identity.mjs +3 -3
- package/scripts/request-uac-migration-r153.ps1 +17 -4
- package/scripts/verify-protected-stage-r153.mjs +1 -0
- package/src/autonomous-supervisor.mjs +21 -4
- package/src/clean-activation-recovery-r153.mjs +1 -1
- package/src/deadbyte-cli.mjs +83 -60
- package/src/host-command-pins-r154.mjs +34 -0
- package/src/legacy-marker-evidence-r159.mjs +128 -0
- package/src/package-identity-r160.mjs +61 -0
- package/src/release-generation.mjs +13 -0
- package/src/release-slot.mjs +2 -1
- package/src/remote-live-log.mjs +30 -2
- package/src/runtime-update.mjs +18 -6
- package/src/trusted-epoch-bootstrap-r158.mjs +42 -0
- package/src/uac-approval-contract-r153.mjs +6 -3
- package/src/version.mjs +1 -1
- package/src/windows-powershell-environment.mjs +5 -2
- package/test/r106-trusted-powershell-spawn.test.mjs +1 -0
- package/test/r130-release-security-contract.test.mjs +9 -9
- package/test/r132-release-identity.test.mjs +8 -10
- package/test/r153-clean-activation-recovery.test.mjs +2 -2
- package/test/r153-prepublish-identity.test.mjs +26 -5
- package/test/r153-r65-signed-precondition-recovery.test.mjs +2 -2
- package/test/r153-staging-leaf-acl.test.mjs +24 -10
- package/test/r153-uac-approval-contract.test.mjs +5 -3
- package/test/r153-uac-launch-parameter-contract.test.mjs +1 -1
- package/test/r154-multipin-recovery.test.mjs +45 -0
- package/test/r155-commander-live-log.test.mjs +44 -0
- package/test/r155-default-live-log.test.mjs +89 -0
- package/test/r156-epoch-route-smoke.ps1 +48 -0
- package/test/r156-supervisor-pacing.test.mjs +59 -0
- package/test/r157-architecture-map.mjs +61 -0
- package/test/r157-reference-survey.mjs +91 -0
- package/test/r157-supervisor-singleflight.test.mjs +32 -0
- package/test/r158-epoch-bootstrap.test.mjs +34 -0
- package/test/r159-legacy-marker-evidence.test.mjs +100 -0
- package/test/r159-legacy-marker-evidence.test.mjs.before-assertion-fix-20261025-120246.bak +100 -0
- package/test/r160-marker-proof.test.mjs +87 -0
- package/test/r160-package-decision-manifest.test.mjs +67 -0
- package/test/r160-release-contract.test.mjs +30 -0
- package/test/r160-stable-launcher.test.mjs +10 -0
- package/test/r163-isolated-slot-rollback-restore.test.mjs +58 -0
- package/test/r163-publish-fence.test.mjs +13 -0
- package/test/r48-release-contract.test.mjs +7 -1
- package/test/r58-launcher-shell.test.mjs +4 -1
- package/test/r59-tool-surface-compat.test.mjs +1 -1
- package/test/release-generation.test.mjs +16 -0
- package/test/remote-live-log-r37.test.mjs +5 -3
- package/test/remote-session-cli.test.mjs +11 -11
- package/test/stabilization-state-root.test.mjs +2 -1
package/src/deadbyte-cli.mjs
CHANGED
|
@@ -16,7 +16,7 @@ import { exportOfflineDiagnostics } from './r67-diagnostic-export.mjs';
|
|
|
16
16
|
import { verifyOfflineDiagnosticsZip } from './r67-diagnostics-verify.mjs';
|
|
17
17
|
import { parseActivePointer } from './release-slot.mjs';
|
|
18
18
|
import { printDoctor } from './doctor-r45.mjs';
|
|
19
|
-
import { classifyRemoteAuditOutcome, createLiveTimelineFilter, createLiveTimelineTracker, createLiveTimelineWriter, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine, selectLiveLogNames } from './remote-live-log.mjs';
|
|
19
|
+
import { classifyRemoteAuditOutcome, createLiveTimelineFilter, createLiveTimelineTracker, createLiveTimelineWriter, defaultForegroundTimelineOptions, formatRemoteLogResetNotice, formatCompactAuditEvent, formatRemoteLifecycle, formatRemoteToolCall, formatRuntimeDiagnosticLine, selectLiveLogNames } from './remote-live-log.mjs';
|
|
20
20
|
import { prepareRemoteConsole, remoteDisplayText } from './remote-console.mjs';
|
|
21
21
|
import { ensureRuntimeCoherence } from './runtime-coherence.mjs';
|
|
22
22
|
import { windowsPowerShellEnvironment } from './runtime-update.mjs';
|
|
@@ -27,6 +27,9 @@ import {acquireForegroundSessionLock} from './foreground-session-lock.mjs';
|
|
|
27
27
|
import { AUTHORITY_PLANES, authorityVector, authorityRestorePlan, parseAuthorityObservation, assertAuthorityVector, assertRuntimeReadyObservation, authorityBanner } from './remote-authority.mjs';
|
|
28
28
|
import { decodeRemoteLogBytes, boundedRemoteDiagnostic, readRemoteLogAppend } from './remote-log-decode-r60.mjs';
|
|
29
29
|
import {reconcileStaleMarker,resumeMarkerReconciliation,recoverPublishedMarkerTerminal} from './clean-activation-recovery-r153.mjs';
|
|
30
|
+
import {selectLegacyMarkerEvidence,verifyLocatedLegacyMarkerReceipt} from './legacy-marker-evidence-r159.mjs';
|
|
31
|
+
import {normalizeHostCommandPins,encodeHostCommandPins} from './host-command-pins-r154.mjs';
|
|
32
|
+
import {verifyPackageDecisionManifest} from './package-identity-r160.mjs';
|
|
30
33
|
|
|
31
34
|
const REMOTE_COMMANDS=new Set([
|
|
32
35
|
'menu','start','resume','park','stop','restart','status','url','test','fulltest','toggle','clean','machinetest',
|
|
@@ -62,6 +65,7 @@ function printHelp(){
|
|
|
62
65
|
` npx -y ${exact} remote --full # explicit five-plane foreground session`,
|
|
63
66
|
` npx -y ${exact} remote --resume <cursor> # replay missed results, then reconnect`,
|
|
64
67
|
` npx -y ${exact} remote --trace # show trace/span identity without payload detail`,
|
|
68
|
+
|
|
65
69
|
` npx -y ${exact} remote --filter <expr> # presentation filter: tool/category/status/trace`,
|
|
66
70
|
` npx -y ${exact} remote status`,
|
|
67
71
|
` npx -y ${exact} remote restart`,
|
|
@@ -114,7 +118,7 @@ async function reportVersionCoherence(root,{displayMode='ascii'}={}){
|
|
|
114
118
|
writeRemote(process.stderr,`${remoteTag('warn',displayMode)} VERSION SKEW: CLI package ${DEADBYTE_VERSION}; active runtime ${active.version} (${active.release_id})\n`,displayMode);
|
|
115
119
|
return active;
|
|
116
120
|
}
|
|
117
|
-
async function synchronizeForegroundRuntime(ctx,{displayMode='unicode',migrationContext=null,
|
|
121
|
+
async function synchronizeForegroundRuntime(ctx,{displayMode='unicode',migrationContext=null,hostCommandPins=[]}={}){
|
|
118
122
|
const packageManifestSha256=createHash('sha256').update(await readFile(path.join(CURRENT_PACKAGE_ROOT,'MANIFEST.SHA256'))).digest('hex');
|
|
119
123
|
return ensureRuntimeCoherence({
|
|
120
124
|
cliVersion:DEADBYTE_VERSION,
|
|
@@ -182,7 +186,7 @@ async function synchronizeForegroundRuntime(ctx,{displayMode='unicode',migration
|
|
|
182
186
|
'-ExpectedBootstrapSha256',bootstrapMatch[1],
|
|
183
187
|
'-StdoutPath',stdoutPath,'-StderrPath',stderrPath,
|
|
184
188
|
'-MarkerReceiptPath',migrationContext.receipt_path,'-MarkerReceiptSha256',migrationContext.receipt_sha256,
|
|
185
|
-
...(
|
|
189
|
+
...(hostCommandPins.length?['-HostCommandPinsJson',encodeHostCommandPins(hostCommandPins)]:[])],{
|
|
186
190
|
cwd:CURRENT_PACKAGE_ROOT,env:windowsPowerShellEnvironment(process.env),encoding:'utf8',windowsHide:true,shell:false,
|
|
187
191
|
timeout:REMOTE_UPDATE_TIMEOUT_MS,maxBuffer:16*1024*1024
|
|
188
192
|
});
|
|
@@ -395,7 +399,7 @@ function timelineFilterEvent(timeline){
|
|
|
395
399
|
async function tailRemoteLogs(root,signal,{journal=null,offsets=null,activityTracker=null,timelineFilter=null,timelineWriter=null,verbose=false,displayMode='unicode'}={}){
|
|
396
400
|
process.stdout.write(verbose
|
|
397
401
|
? '[LOG] Verbose live timeline. Full sanitized request detail enabled; Ctrl+C disconnects and revokes authority.\n'
|
|
398
|
-
: '[LOG]
|
|
402
|
+
: '[LOG] Live tool calls with individual START/DONE, sanitized arguments and results. Transport hidden; signed audit remains on disk. Ctrl+C disconnects and revokes authority.\n');
|
|
399
403
|
const activeOffsets=offsets??await snapshotRemoteLogOffsets(root);
|
|
400
404
|
const partialLines=new Map();
|
|
401
405
|
let liveFiles=[...activeOffsets.keys()];
|
|
@@ -412,7 +416,7 @@ async function tailRemoteLogs(root,signal,{journal=null,offsets=null,activityTra
|
|
|
412
416
|
activeOffsets.set(file,next.cursor);
|
|
413
417
|
if(next.reset){
|
|
414
418
|
partialLines.delete(file);
|
|
415
|
-
process.stderr.write(
|
|
419
|
+
process.stderr.write(formatRemoteLogResetNotice(file,next.resetReason)+'\n');
|
|
416
420
|
}
|
|
417
421
|
if(!next.text)continue;
|
|
418
422
|
const lines=((partialLines.get(file)??'')+next.text).split(/\r?\n/);
|
|
@@ -512,64 +516,79 @@ async function publishExclusiveJson(file,value){
|
|
|
512
516
|
}
|
|
513
517
|
return {path:file,sha256:createHash('sha256').update(bytes).digest('hex')};
|
|
514
518
|
}
|
|
519
|
+
async function readMarkerRecoveryAuthority(ctx,active){
|
|
520
|
+
const grantField={process:'grant_file',coding:'lease_file',autonomous:'lease_file',desktop:'grant_file',host:'arm_file'};
|
|
521
|
+
const policySha256={},grantPresence={};
|
|
522
|
+
for(const plane of AUTHORITY_PLANES){
|
|
523
|
+
const policyFile=path.join(active.generation_root,`${plane}-policy.json`),info=await lstat(policyFile);
|
|
524
|
+
if(!info.isFile()||info.isSymbolicLink()||path.resolve(path.dirname(policyFile)).toLowerCase()!==path.resolve(active.generation_root).toLowerCase())
|
|
525
|
+
throw new Error(`marker reconciliation ${plane} policy identity invalid`);
|
|
526
|
+
const bytes=await readFile(policyFile),policy=JSON.parse(bytes.toString('utf8').replace(/^\uFEFF/,''));
|
|
527
|
+
policySha256[plane]=createHash('sha256').update(bytes).digest('hex');
|
|
528
|
+
const grant=policy?.[grantField[plane]];
|
|
529
|
+
if(typeof grant!=='string'||!path.isAbsolute(grant)||
|
|
530
|
+
path.resolve(path.dirname(grant)).toLowerCase()!==path.resolve(ctx.root,'runtime').toLowerCase())
|
|
531
|
+
throw new Error(`marker reconciliation ${plane} grant path invalid`);
|
|
532
|
+
try{await lstat(grant);grantPresence[plane]=true;}
|
|
533
|
+
catch(error){if(error?.code==='ENOENT')grantPresence[plane]=false;else throw error;}
|
|
534
|
+
}
|
|
535
|
+
return {policySha256,grantPresence};
|
|
536
|
+
}
|
|
515
537
|
async function recoverRemoteSession(ctx,{displayMode='unicode'}={}){
|
|
516
538
|
let prior=await readRemoteSessionMarker(ctx.root);
|
|
517
539
|
const evidenceDir=path.join(ctx.root,'evidence','legacy-migration');
|
|
518
|
-
const locatorPath=path.join(evidenceDir,'r153-pending-migration.json');
|
|
519
540
|
if(!prior){
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
541
|
+
const activePointer=await readActiveRuntimeBinding(ctx.root);
|
|
542
|
+
// A historical terminal is not an uncompleted migration. Ignore only
|
|
543
|
+
// when the installed release identity matches the CLI's manifest identity;
|
|
544
|
+
// this path grants no authority and publishes no recovery evidence.
|
|
545
|
+
const currentManifestSha256=createHash('sha256').update(
|
|
546
|
+
await readFile(path.join(CURRENT_PACKAGE_ROOT,'MANIFEST.SHA256'))).digest('hex');
|
|
547
|
+
const historyAllowed=activePointer?.version===DEADBYTE_VERSION&&
|
|
548
|
+
activePointer?.manifest_sha256===currentManifestSha256&&
|
|
549
|
+
activePointer?.release_id===('v'+DEADBYTE_VERSION+'-'+currentManifestSha256.slice(0,16));
|
|
550
|
+
const selected=await selectLegacyMarkerEvidence({evidenceDir,activePointer,
|
|
551
|
+
allowCompletedHistory:historyAllowed});
|
|
552
|
+
if(selected.kind==='none')return null;
|
|
553
|
+
if(selected.kind==='completed_history_ambiguous'){
|
|
554
|
+
if(!historyAllowed)throw new Error('R159 completed history cannot be reused across a release change');
|
|
555
|
+
// Nothing is pending: do not select an arbitrary completed receipt.
|
|
556
|
+
// A future upgrade still requires its own exact signed migration proof.
|
|
557
|
+
return null;
|
|
558
|
+
}
|
|
559
|
+
if(selected.kind==='located'){
|
|
560
|
+
const readback=await readActiveRuntimeBinding(ctx.root);
|
|
561
|
+
for(const key of ['release_id','version','manifest_sha256','generation_manifest_sha256'])
|
|
562
|
+
if(readback?.[key]!==activePointer?.[key])
|
|
563
|
+
throw new Error('R159 active release changed during located marker verification');
|
|
564
|
+
const {policySha256}=await readMarkerRecoveryAuthority(ctx,readback);
|
|
565
|
+
const verified=await verifyLocatedLegacyMarkerReceipt({located:selected,pointer:readback,policySha256});
|
|
566
|
+
const finalPointer=await readActiveRuntimeBinding(ctx.root);
|
|
567
|
+
for(const key of ['release_id','version','manifest_sha256','generation_manifest_sha256'])
|
|
568
|
+
if(finalPointer?.[key]!==readback?.[key])
|
|
569
|
+
throw new Error('R160 active release changed after located marker verification');
|
|
570
|
+
return verified;
|
|
571
|
+
}
|
|
572
|
+
// Completed historical receipts are not a pending migration for an
|
|
573
|
+
// already-online current release. Keep bytes and their SHA untouched.
|
|
574
|
+
if(selected.kind==='completed'&&observedAuthority(ctx).state==='ONLINE')return null;
|
|
575
|
+
if(selected.kind==='pending'&&observedAuthority(ctx).state!=='OFFLINE')
|
|
576
|
+
throw new Error('R159 pending marker reconciliation requires an offline runtime');
|
|
532
577
|
// Crash recovery for the narrow window after marker CAS deletion and
|
|
533
578
|
// before terminal receipt publication. Never infer completion from the
|
|
534
579
|
// missing marker alone: require exactly one durable intent and re-observe
|
|
535
580
|
// the full predecessor/policy/grant/runtime subject.
|
|
536
|
-
|
|
537
|
-
try{entries=await readdir(evidenceDir,{withFileTypes:true});}
|
|
538
|
-
catch(error){if(error?.code==='ENOENT')return null;throw error;}
|
|
539
|
-
const candidates=[];
|
|
540
|
-
for(const entry of entries){
|
|
541
|
-
const match=/^marker-([0-9a-f]{64})\.intent\.json$/.exec(entry.name);
|
|
542
|
-
if(!match||!entry.isFile())continue;
|
|
543
|
-
const terminal=path.join(evidenceDir,`marker-${match[1]}.terminal.json`);
|
|
544
|
-
let terminalExists=true;
|
|
545
|
-
try{await lstat(terminal);}
|
|
546
|
-
catch(error){if(error?.code==='ENOENT')terminalExists=false;else throw error;}
|
|
547
|
-
candidates.push({markerSha256:match[1],intent:path.join(evidenceDir,entry.name),terminal,terminalExists});
|
|
548
|
-
}
|
|
549
|
-
if(candidates.length===0)return null;
|
|
550
|
-
if(candidates.length!==1)throw new Error('multiple unlocated marker reconciliation claims; manual reconciliation required');
|
|
551
|
-
const pending=candidates[0],intentInfo=await lstat(pending.intent);
|
|
581
|
+
const pending=selected.pending,intentInfo=await lstat(selected.pending.intent);
|
|
552
582
|
if(!intentInfo.isFile()||intentInfo.isSymbolicLink())throw new Error('marker reconciliation intent is not a regular file');
|
|
553
583
|
const intentBytes=await readFile(pending.intent),intent=JSON.parse(intentBytes.toString('utf8').replace(/^\uFEFF/,''));
|
|
554
584
|
if(intent.marker_sha256!==pending.markerSha256)throw new Error('marker reconciliation intent filename identity mismatch');
|
|
555
585
|
const active=await readActiveRuntimeBinding(ctx.root);
|
|
556
586
|
if(!active)throw new Error('marker reconciliation predecessor pointer missing');
|
|
587
|
+
for(const key of ['release_id','version','manifest_sha256','generation_manifest_sha256'])
|
|
588
|
+
if(active[key]!==activePointer?.[key])
|
|
589
|
+
throw new Error('R159 active release changed during marker recovery');
|
|
557
590
|
const observed=observedAuthority(ctx);
|
|
558
|
-
const
|
|
559
|
-
const policySha256={},grantPresence={};
|
|
560
|
-
for(const plane of AUTHORITY_PLANES){
|
|
561
|
-
const policyFile=path.join(active.generation_root,`${plane}-policy.json`),info=await lstat(policyFile);
|
|
562
|
-
if(!info.isFile()||info.isSymbolicLink()||path.resolve(path.dirname(policyFile)).toLowerCase()!==path.resolve(active.generation_root).toLowerCase())
|
|
563
|
-
throw new Error(`marker reconciliation ${plane} policy identity invalid`);
|
|
564
|
-
const bytes=await readFile(policyFile),policy=JSON.parse(bytes.toString('utf8').replace(/^\uFEFF/,''));
|
|
565
|
-
policySha256[plane]=createHash('sha256').update(bytes).digest('hex');
|
|
566
|
-
const grant=policy?.[grantField[plane]];
|
|
567
|
-
if(typeof grant!=='string'||!path.isAbsolute(grant)||
|
|
568
|
-
path.resolve(path.dirname(grant)).toLowerCase()!==path.resolve(ctx.root,'runtime').toLowerCase())
|
|
569
|
-
throw new Error(`marker reconciliation ${plane} grant path invalid`);
|
|
570
|
-
try{await lstat(grant);grantPresence[plane]=true;}
|
|
571
|
-
catch(error){if(error?.code==='ENOENT')grantPresence[plane]=false;else throw error;}
|
|
572
|
-
}
|
|
591
|
+
const {policySha256,grantPresence}=await readMarkerRecoveryAuthority(ctx,active);
|
|
573
592
|
if(await readRemoteMarkerSnapshot(remoteSessionMarker(ctx.root))!==null)
|
|
574
593
|
throw new Error('marker reconciliation marker reappeared');
|
|
575
594
|
const intentSha256=createHash('sha256').update(intentBytes).digest('hex');
|
|
@@ -588,7 +607,7 @@ async function recoverRemoteSession(ctx,{displayMode='unicode'}={}){
|
|
|
588
607
|
}
|
|
589
608
|
const locator={schema:'deadbyte.r153-pending-migration.v1',receipt_path:pending.terminal,
|
|
590
609
|
receipt_sha256:reconciled.receipt_sha256,marker_sha256:reconciled.marker_sha256};
|
|
591
|
-
await publishExclusiveJson(
|
|
610
|
+
await publishExclusiveJson(path.join(evidenceDir,'marker-'+reconciled.marker_sha256+'.locator.json'),locator);
|
|
592
611
|
writeRemote(process.stdout,`${remoteTag('recovery',displayMode)} Interrupted marker reconciliation completed (${reconciled.marker_sha256.slice(0,12)}).\n`,displayMode);
|
|
593
612
|
return {...reconciled,receipt_path:pending.terminal};
|
|
594
613
|
}
|
|
@@ -691,7 +710,7 @@ async function recoverRemoteSession(ctx,{displayMode='unicode'}={}){
|
|
|
691
710
|
deleteIfHash:async expected=>deletePinnedRemoteMarker(markerPath,expected)});
|
|
692
711
|
const locator={schema:'deadbyte.r153-pending-migration.v1',receipt_path:terminalPath,
|
|
693
712
|
receipt_sha256:reconciled.receipt_sha256,marker_sha256:reconciled.marker_sha256};
|
|
694
|
-
await publishExclusiveJson(
|
|
713
|
+
await publishExclusiveJson(path.join(evidenceDir,'marker-'+reconciled.marker_sha256+'.locator.json'),locator);
|
|
695
714
|
writeRemote(process.stdout,`${remoteTag('recovery',displayMode)} Stale session evidence reconciled (${reconciled.marker_sha256.slice(0,12)}).\n`,displayMode);
|
|
696
715
|
return {...reconciled,receipt_path:terminalPath};
|
|
697
716
|
}
|
|
@@ -744,7 +763,7 @@ async function releaseRemoteSession(ctx,marker,{displayMode='unicode'}={}){
|
|
|
744
763
|
if(deletePinnedRemoteMarker(remoteSessionMarker(ctx.root),snapshot.sha256)!=='REMOVED')
|
|
745
764
|
throw new Error('remote session ownership marker replaced before native deletion; marker retained');
|
|
746
765
|
}
|
|
747
|
-
async function runRemoteSession({resumeCursor=null,verbose=false,trace=false,filterExpression='',mode='default',
|
|
766
|
+
async function runRemoteSession({resumeCursor=null,verbose=false,trace=false,filterExpression='',mode='default',hostCommandPins=[]}={}){
|
|
748
767
|
const display=prepareRemoteConsole();
|
|
749
768
|
const displayMode=display.mode;
|
|
750
769
|
const ctx=await remoteContext();
|
|
@@ -753,10 +772,12 @@ async function runRemoteSession({resumeCursor=null,verbose=false,trace=false,fil
|
|
|
753
772
|
const startupLock=await acquireForegroundSessionLock({root:ctx.root,powershell:ctx.powershell});
|
|
754
773
|
let marker;
|
|
755
774
|
try{
|
|
775
|
+
startupLock.assertHeld();
|
|
776
|
+
await verifyPackageDecisionManifest({root:CURRENT_PACKAGE_ROOT,expectedVersion:DEADBYTE_VERSION});
|
|
756
777
|
startupLock.assertHeld();
|
|
757
778
|
const migrationContext=await recoverRemoteSession(ctx,{displayMode});
|
|
758
779
|
startupLock.assertHeld();
|
|
759
|
-
await synchronizeForegroundRuntime(ctx,{displayMode,migrationContext,
|
|
780
|
+
await synchronizeForegroundRuntime(ctx,{displayMode,migrationContext,hostCommandPins});
|
|
760
781
|
startupLock.assertHeld();
|
|
761
782
|
marker=await claimRemoteSession(ctx);
|
|
762
783
|
startupLock.assertHeld();
|
|
@@ -765,7 +786,7 @@ async function runRemoteSession({resumeCursor=null,verbose=false,trace=false,fil
|
|
|
765
786
|
filePath:remoteResultJournalPath(ctx.root,marker.result_session_id),sessionId:marker.result_session_id,maxEntries:2048
|
|
766
787
|
});
|
|
767
788
|
const rootPaths=await readLiveRootPaths(ctx.root);
|
|
768
|
-
const activityTracker=createLiveTimelineTracker({verbose,displayMode,rootPaths,trace
|
|
789
|
+
const activityTracker=createLiveTimelineTracker({verbose,displayMode,rootPaths,trace,...defaultForegroundTimelineOptions({verbose})});
|
|
769
790
|
const timelineFilter=filterExpression?createLiveTimelineFilter(filterExpression):null;
|
|
770
791
|
const timelineWriter=createLiveTimelineWriter({stream:process.stdout,displayMode,rewrite:false});
|
|
771
792
|
const aborter=new AbortController();
|
|
@@ -869,17 +890,16 @@ async function runRemoteSession({resumeCursor=null,verbose=false,trace=false,fil
|
|
|
869
890
|
}
|
|
870
891
|
}
|
|
871
892
|
async function runRemote(args){
|
|
872
|
-
let verbose=false,trace=false,filterExpression='',resumeCursor=null,mode='default'
|
|
893
|
+
let verbose=false,trace=false,filterExpression='',resumeCursor=null,mode='default';
|
|
894
|
+
const hostCommandPinValues=[];
|
|
873
895
|
const positional=[];
|
|
874
896
|
for(let index=0;index<args.length;index++){
|
|
875
897
|
const arg=args[index];
|
|
876
898
|
if(arg==='--verbose'||arg==='-v'){verbose=true;continue;}
|
|
877
899
|
if(arg==='--trace'){trace=true;continue;}
|
|
878
900
|
if(arg==='--host-command-pin'){
|
|
879
|
-
if(hostCommandPin!==null)throw new Error('remote --host-command-pin may be specified once');
|
|
880
901
|
const value=String(args[++index]??'');
|
|
881
|
-
|
|
882
|
-
hostCommandPin=value;continue;
|
|
902
|
+
hostCommandPinValues.push(value);continue;
|
|
883
903
|
}
|
|
884
904
|
if(['--operator','--desktop','--host','--full'].includes(arg)){
|
|
885
905
|
if(mode!=='default') throw new Error('remote authority modes are mutually exclusive');
|
|
@@ -897,8 +917,11 @@ async function runRemote(args){
|
|
|
897
917
|
}
|
|
898
918
|
positional.push(arg);
|
|
899
919
|
}
|
|
900
|
-
|
|
901
|
-
|
|
920
|
+
let hostCommandPins;
|
|
921
|
+
try{hostCommandPins=normalizeHostCommandPins(hostCommandPinValues);}
|
|
922
|
+
catch(error){throw new Error(`remote --host-command-pin ${error.message}`);}
|
|
923
|
+
const presentation=verbose||trace||Boolean(filterExpression)||resumeCursor!==null||mode!=='default'||hostCommandPins.length>0;
|
|
924
|
+
if(positional.length===0){await runRemoteSession({resumeCursor,verbose,trace,filterExpression,mode,hostCommandPins});return;}
|
|
902
925
|
if(presentation) throw new Error('remote presentation flags are valid only for foreground session');
|
|
903
926
|
if(positional.length>1) throw new Error('remote accepts one controller command');
|
|
904
927
|
const command=positional[0];if(!REMOTE_COMMANDS.has(command)) throw new Error(`unsupported remote command '${command}'`);
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const PIN=/^([A-Za-z0-9._-]{1,64})=([0-9a-f]{64})$/;
|
|
2
|
+
const LIMIT=16;
|
|
3
|
+
const REPINNABLE=new Set(['powershell_script','powershell_inline','cmd_script','cmd_inline','node_script','node_inline']);
|
|
4
|
+
|
|
5
|
+
export function normalizeHostCommandPins(values=[]){
|
|
6
|
+
if(!Array.isArray(values)||values.length>LIMIT)throw new Error(`host command pin limit is ${LIMIT}`);
|
|
7
|
+
const seen=new Set(),pins=[];
|
|
8
|
+
for(const value of values){
|
|
9
|
+
const match=PIN.exec(String(value??''));
|
|
10
|
+
if(!match)throw new Error('host command pin contract requires command_id=lowercase_sha256');
|
|
11
|
+
const [,command_id,sha256]=match;
|
|
12
|
+
if(!REPINNABLE.has(command_id))throw new Error(`host command '${command_id}' is not repinnable`);
|
|
13
|
+
if(seen.has(command_id))throw new Error(`duplicate host command pin '${command_id}'`);
|
|
14
|
+
seen.add(command_id);pins.push(Object.freeze({command_id,sha256}));
|
|
15
|
+
}
|
|
16
|
+
pins.sort((a,b)=>a.command_id<b.command_id?-1:a.command_id>b.command_id?1:0);
|
|
17
|
+
return Object.freeze(pins);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function encodeHostCommandPins(pins=[]){
|
|
21
|
+
return JSON.stringify(normalizeHostCommandPins(pins.map(pin=>`${pin.command_id}=${pin.sha256}`))
|
|
22
|
+
.map(pin=>`${pin.command_id}=${pin.sha256}`));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function decodeHostCommandPins(encoded='[]'){
|
|
26
|
+
let values;
|
|
27
|
+
try{values=JSON.parse(String(encoded));}catch{throw new Error('host command pins JSON invalid');}
|
|
28
|
+
return normalizeHostCommandPins(values);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function hostCommandPinArgs(pins=[]){
|
|
32
|
+
return normalizeHostCommandPins(pins.map(pin=>`${pin.command_id}=${pin.sha256}`))
|
|
33
|
+
.flatMap(pin=>['--host-command-pin',`${pin.command_id}=${pin.sha256}`]);
|
|
34
|
+
}
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import {readdir,lstat,readFile} from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import {createHash} from 'node:crypto';
|
|
4
|
+
import {verifyMarkerReconciliationReceipt} from './clean-activation-recovery-r153.mjs';
|
|
5
|
+
|
|
6
|
+
const H=/^[0-9a-f]{64}$/;
|
|
7
|
+
const digest=b=>createHash('sha256').update(b).digest('hex');
|
|
8
|
+
function samePredecessor(a,b){
|
|
9
|
+
return !!a&&!!b&&['release_id','version','manifest_sha256','generation_manifest_sha256']
|
|
10
|
+
.every(k=>typeof a[k]==='string'&&a[k]===b[k]);
|
|
11
|
+
}
|
|
12
|
+
async function regular(file){
|
|
13
|
+
const st=await lstat(file);
|
|
14
|
+
if(!st.isFile()||st.isSymbolicLink())throw new Error('R159 evidence file must be regular and not a link');
|
|
15
|
+
}
|
|
16
|
+
function strictJson(bytes,label){
|
|
17
|
+
let result;
|
|
18
|
+
try{result=JSON.parse(bytes.toString('utf8').replace(/^\uFEFF/,''));}
|
|
19
|
+
catch{throw new Error('R159 '+label+' invalid JSON');}
|
|
20
|
+
if(!result||typeof result!=='object'||Array.isArray(result))
|
|
21
|
+
throw new Error('R159 '+label+' invalid object');
|
|
22
|
+
return result;
|
|
23
|
+
}
|
|
24
|
+
// Examine ONLY local immutable receipt candidates. It must never infer a live
|
|
25
|
+
// process state, delete a marker, grant an authority plane, or mutate evidence.
|
|
26
|
+
export async function selectLegacyMarkerEvidence({evidenceDir,activePointer,allowCompletedHistory=false}){
|
|
27
|
+
if(typeof evidenceDir!=='string'||!path.isAbsolute(evidenceDir))
|
|
28
|
+
throw new Error('R159 evidence root must be absolute');
|
|
29
|
+
let entries;
|
|
30
|
+
try{entries=await readdir(evidenceDir,{withFileTypes:true});}
|
|
31
|
+
catch(e){if(e?.code==='ENOENT')return {kind:'none'};throw e;}
|
|
32
|
+
if(entries.length>4096)throw new Error('R159 evidence inventory exceeds bounded scan');
|
|
33
|
+
const fileSet=new Map(entries.map(e=>[e.name,e]));
|
|
34
|
+
const locatorPath=path.join(evidenceDir,'r153-pending-migration.json');
|
|
35
|
+
let located=null;
|
|
36
|
+
const locatorEntry=fileSet.get('r153-pending-migration.json');
|
|
37
|
+
if(locatorEntry){
|
|
38
|
+
if(!locatorEntry.isFile()||locatorEntry.isSymbolicLink())
|
|
39
|
+
throw new Error('R159 locator is not regular');
|
|
40
|
+
await regular(locatorPath);
|
|
41
|
+
const locator=strictJson(await readFile(locatorPath),'legacy locator');
|
|
42
|
+
if(locator.schema!=='deadbyte.r153-pending-migration.v1'||
|
|
43
|
+
!H.test(String(locator.marker_sha256??''))||
|
|
44
|
+
!H.test(String(locator.receipt_sha256??''))||
|
|
45
|
+
typeof locator.receipt_path!=='string')
|
|
46
|
+
throw new Error('R159 legacy locator schema invalid');
|
|
47
|
+
const expected=path.join(evidenceDir,'marker-'+locator.marker_sha256+'.terminal.json');
|
|
48
|
+
if(path.resolve(locator.receipt_path).toLowerCase()!==path.resolve(expected).toLowerCase())
|
|
49
|
+
throw new Error('R159 legacy locator receipt path mismatch');
|
|
50
|
+
await regular(expected);
|
|
51
|
+
const raw=await readFile(expected);
|
|
52
|
+
if(digest(raw)!==locator.receipt_sha256)
|
|
53
|
+
throw new Error('R159 legacy locator receipt hash mismatch');
|
|
54
|
+
const terminal=strictJson(raw,'located terminal');
|
|
55
|
+
if(terminal.schema!=='deadbyte.r153-marker-reconciliation.v1'||
|
|
56
|
+
terminal.state!=='MARKER_RECONCILED'||terminal.marker_sha256!==locator.marker_sha256)
|
|
57
|
+
throw new Error('R159 located terminal identity mismatch');
|
|
58
|
+
if(samePredecessor(terminal.predecessor,activePointer))
|
|
59
|
+
located={kind:'located',receiptPath:expected,receiptSha256:locator.receipt_sha256,
|
|
60
|
+
markerSha256:locator.marker_sha256};
|
|
61
|
+
}
|
|
62
|
+
const candidates=[];
|
|
63
|
+
for(const entry of entries){
|
|
64
|
+
const match=/^marker-([a-f0-9]{64})\.intent\.json$/.exec(entry.name);
|
|
65
|
+
if(!match)continue;
|
|
66
|
+
if(!entry.isFile()||entry.isSymbolicLink())throw new Error('R159 intent is not regular');
|
|
67
|
+
const intent=path.join(evidenceDir,entry.name);
|
|
68
|
+
await regular(intent);
|
|
69
|
+
const intentBytes=await readFile(intent),value=strictJson(intentBytes,'recovery intent');
|
|
70
|
+
if(value.schema!=='deadbyte.r153-marker-reconciliation-intent.v1'||
|
|
71
|
+
value.state!=='RECONCILIATION_CLAIMED'||value.marker_sha256!==match[1])
|
|
72
|
+
throw new Error('R159 intent filename/identity mismatch');
|
|
73
|
+
if(!samePredecessor(value.predecessor,activePointer))continue;
|
|
74
|
+
const terminal=path.join(evidenceDir,'marker-'+match[1]+'.terminal.json');
|
|
75
|
+
const terminalEntry=fileSet.get(path.basename(terminal));
|
|
76
|
+
let terminalExists=false;
|
|
77
|
+
if(terminalEntry){
|
|
78
|
+
if(!terminalEntry.isFile()||terminalEntry.isSymbolicLink())
|
|
79
|
+
throw new Error('R159 terminal is not regular');
|
|
80
|
+
await regular(terminal);
|
|
81
|
+
const parsed=strictJson(await readFile(terminal),'current terminal');
|
|
82
|
+
if(parsed.schema!=='deadbyte.r153-marker-reconciliation.v1'||
|
|
83
|
+
parsed.state!=='MARKER_RECONCILED'||parsed.marker_sha256!==match[1]||
|
|
84
|
+
parsed.intent_sha256!==digest(intentBytes)||
|
|
85
|
+
!samePredecessor(parsed.predecessor,activePointer))
|
|
86
|
+
throw new Error('R159 current terminal intent/subject mismatch');
|
|
87
|
+
terminalExists=true;
|
|
88
|
+
}
|
|
89
|
+
candidates.push({markerSha256:match[1],intent,terminal,terminalExists});
|
|
90
|
+
}
|
|
91
|
+
const pending=candidates.filter(c=>!c.terminalExists);
|
|
92
|
+
if(pending.length>1)throw new Error('R159 multiple matching pending reconciliation claims; manual reconciliation required');
|
|
93
|
+
if(pending.length===1)return {kind:'pending',pending:pending[0]};
|
|
94
|
+
if(located){
|
|
95
|
+
const other=candidates.filter(c=>c.markerSha256!==located.markerSha256);
|
|
96
|
+
if(other.length)throw new Error('R159 ambiguous multiple current reconciliation receipts');
|
|
97
|
+
return located;
|
|
98
|
+
}
|
|
99
|
+
const completed=candidates.filter(c=>c.terminalExists);
|
|
100
|
+
if(completed.length>1){
|
|
101
|
+
// Keep the original fail-closed default. A caller that independently
|
|
102
|
+
// verifies it is already running the exact installed release may choose
|
|
103
|
+
// not to reuse any fully completed receipts for a new startup.
|
|
104
|
+
if(allowCompletedHistory===true)
|
|
105
|
+
return {kind:'completed_history_ambiguous',count:completed.length};
|
|
106
|
+
throw new Error('R159 ambiguous multiple completed reconciliation receipts');
|
|
107
|
+
}
|
|
108
|
+
if(completed.length===1)return {kind:'completed',pending:completed[0]};
|
|
109
|
+
return {kind:'none'};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// Selection proves only that the locator and terminal bytes are internally
|
|
113
|
+
// consistent. Adoption is a separate boundary: reread the terminal and bind
|
|
114
|
+
// its R153 proof to the currently observed predecessor and policy generation.
|
|
115
|
+
export async function verifyLocatedLegacyMarkerReceipt({located,pointer,policySha256}){
|
|
116
|
+
if(located?.kind!=='located'||typeof located.receiptPath!=='string'||
|
|
117
|
+
!H.test(String(located.receiptSha256??''))||!H.test(String(located.markerSha256??'')))
|
|
118
|
+
throw new Error('R160 located receipt selection invalid');
|
|
119
|
+
await regular(located.receiptPath);
|
|
120
|
+
const raw=await readFile(located.receiptPath);
|
|
121
|
+
if(digest(raw)!==located.receiptSha256)
|
|
122
|
+
throw new Error('R160 located terminal changed after selection');
|
|
123
|
+
const terminal=strictJson(raw,'located terminal readback');
|
|
124
|
+
verifyMarkerReconciliationReceipt(terminal,{pointer,policySha256,
|
|
125
|
+
markerSha256:located.markerSha256});
|
|
126
|
+
return Object.freeze({...terminal,receipt_path:located.receiptPath,
|
|
127
|
+
receipt_sha256:located.receiptSha256});
|
|
128
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {createHash} from 'node:crypto';
|
|
2
|
+
import {lstat,readFile} from 'node:fs/promises';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {parseManifest} from './release-slot.mjs';
|
|
5
|
+
|
|
6
|
+
const SEMVER=/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
|
|
7
|
+
const sha=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
8
|
+
|
|
9
|
+
export const R160_STARTUP_DECISION_FILES=Object.freeze([
|
|
10
|
+
'package.json',
|
|
11
|
+
'src/package-identity-r160.mjs',
|
|
12
|
+
'src/deadbyte-cli.mjs',
|
|
13
|
+
'src/legacy-marker-evidence-r159.mjs',
|
|
14
|
+
'src/clean-activation-recovery-r153.mjs',
|
|
15
|
+
'src/runtime-coherence.mjs',
|
|
16
|
+
'src/runtime-update.mjs',
|
|
17
|
+
'src/release-generation.mjs',
|
|
18
|
+
'src/release-slot.mjs',
|
|
19
|
+
'src/remote-authority.mjs',
|
|
20
|
+
'src/foreground-session-lock.mjs',
|
|
21
|
+
'src/foreground-marker-cas-win32.mjs',
|
|
22
|
+
'src/windows-powershell-environment.mjs',
|
|
23
|
+
'src/state-root.mjs'
|
|
24
|
+
]);
|
|
25
|
+
|
|
26
|
+
async function regularUnder(root,rel){
|
|
27
|
+
const absolute=path.resolve(root,...rel.split('/'));
|
|
28
|
+
const boundary=path.relative(root,absolute);
|
|
29
|
+
if(boundary.startsWith(`..${path.sep}`)||path.isAbsolute(boundary))
|
|
30
|
+
throw new Error(`startup decision path escapes package: ${rel}`);
|
|
31
|
+
let cursor=path.resolve(root);
|
|
32
|
+
for(const part of rel.split('/')){
|
|
33
|
+
cursor=path.join(cursor,part);
|
|
34
|
+
const stat=await lstat(cursor);
|
|
35
|
+
if(stat.isSymbolicLink())throw new Error(`startup decision link forbidden: ${rel}`);
|
|
36
|
+
}
|
|
37
|
+
const stat=await lstat(absolute);
|
|
38
|
+
if(!stat.isFile())throw new Error(`startup decision file invalid: ${rel}`);
|
|
39
|
+
return absolute;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export async function verifyPackageDecisionManifest({root,expectedVersion}){
|
|
43
|
+
if(typeof root!=='string'||!path.isAbsolute(root))throw new Error('startup package root invalid');
|
|
44
|
+
if(!SEMVER.test(String(expectedVersion??'')))throw new Error('startup package version invalid');
|
|
45
|
+
const resolved=path.resolve(root),rootStat=await lstat(resolved);
|
|
46
|
+
if(!rootStat.isDirectory()||rootStat.isSymbolicLink())throw new Error('startup package root must be a real directory');
|
|
47
|
+
const manifestPath=await regularUnder(resolved,'MANIFEST.SHA256');
|
|
48
|
+
const manifestBytes=await readFile(manifestPath),manifest=parseManifest(manifestBytes.toString('utf8'));
|
|
49
|
+
for(const rel of R160_STARTUP_DECISION_FILES){
|
|
50
|
+
const expected=manifest.get(rel);
|
|
51
|
+
if(!expected)throw new Error(`startup decision manifest missing: ${rel}`);
|
|
52
|
+
const actual=sha(await readFile(await regularUnder(resolved,rel)));
|
|
53
|
+
if(actual!==expected)throw new Error(`startup decision file hash mismatch: ${rel}`);
|
|
54
|
+
}
|
|
55
|
+
const pkg=JSON.parse(await readFile(path.join(resolved,'package.json'),'utf8'));
|
|
56
|
+
if(pkg?.name!=='deadbyte-mcp'||pkg?.version!==expectedVersion)
|
|
57
|
+
throw new Error('startup package identity mismatch');
|
|
58
|
+
return Object.freeze({files:R160_STARTUP_DECISION_FILES.length,
|
|
59
|
+
manifest_sha256:sha(manifestBytes),version:expectedVersion});
|
|
60
|
+
}
|
|
61
|
+
|
|
@@ -185,6 +185,10 @@ function renderControllerConfig({currentConfig,coreRoot,workspaceRoot,processPol
|
|
|
185
185
|
const supervisor=currentConfig.Supervisor??{},bridge=currentConfig.Bridge??{},cloud=currentConfig.Cloud??{};
|
|
186
186
|
return [
|
|
187
187
|
'@{'," Schema = 'deadbyte.controller.v1'",` CoreRoot = ${psQuote(coreRoot)}`,
|
|
188
|
+
// A pre-provisioned, OS-owned exact release copy; never auto-discover
|
|
189
|
+
// an unverified directory or grant authority while creating a generation.
|
|
190
|
+
...(typeof currentConfig.AuthorityEpochRoot==='string'&¤tConfig.AuthorityEpochRoot.length>0
|
|
191
|
+
?[` AuthorityEpochRoot = ${psQuote(currentConfig.AuthorityEpochRoot)}`]:[]),
|
|
188
192
|
` WorkspaceRoot = ${psQuote(workspaceRoot)}`,
|
|
189
193
|
` MachineRuntimeEnabled = ${psBool(currentConfig.MachineRuntimeEnabled!==false)}`,
|
|
190
194
|
` AgentRuntimeEnabled = ${psBool(currentConfig.AgentRuntimeEnabled)}`,
|
|
@@ -270,6 +274,13 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
|
|
|
270
274
|
}
|
|
271
275
|
if(typeof currentConfig?.Bridge?.ScriptPath!=='string'||!path.isAbsolute(currentConfig.Bridge.ScriptPath)) throw new Error('generation Bridge ScriptPath must be absolute');
|
|
272
276
|
if(typeof currentConfig?.Bridge?.ScriptSha256!=='string'||!HEX64.test(currentConfig.Bridge.ScriptSha256)) throw new Error('generation Bridge ScriptSha256 invalid');
|
|
277
|
+
// Legacy generations omit Epoch binding. Present values must be explicit
|
|
278
|
+
// Windows drive-absolute paths, never an implicit writable source.
|
|
279
|
+
const epochRoot=currentConfig.AuthorityEpochRoot;
|
|
280
|
+
if(epochRoot!==undefined&&(typeof epochRoot!=='string'||!/^[A-Za-z]:[\\/]/.test(epochRoot)||
|
|
281
|
+
!path.win32.isAbsolute(epochRoot)||epochRoot.includes('\0')||
|
|
282
|
+
path.win32.parse(epochRoot).root.toLowerCase()===path.win32.normalize(epochRoot).toLowerCase()))
|
|
283
|
+
throw new Error('generation AuthorityEpochRoot must be a Windows drive-absolute directory');
|
|
273
284
|
const semanticSource=String(currentConfig.SemanticLspConfigFile??'');
|
|
274
285
|
const semanticExpected=String(currentConfig.SemanticLspConfigSha256??'').toLowerCase();
|
|
275
286
|
if(Boolean(semanticSource)!==Boolean(semanticExpected)) throw new Error('generation semantic LSP config path/SHA must be supplied together');
|
|
@@ -316,6 +327,8 @@ export async function createGenerationBundle({stateRoot,releaseRoot,developmentR
|
|
|
316
327
|
mcp_url_file:currentConfig.McpUrlFile??'',mcp_url_file_sha256:currentConfig.McpUrlFileSha256??'',mcp_path_file:currentConfig.McpPathFile??'',
|
|
317
328
|
start_timeout_seconds:Number(currentConfig.StartTimeoutSeconds??30),probe_timeout_seconds:Number(currentConfig.ProbeTimeoutSeconds??3),
|
|
318
329
|
semantic_lsp_config_sha256:semanticConfigSha256,
|
|
330
|
+
// Changing a protected code origin changes this immutable generation.
|
|
331
|
+
...(epochRoot!==undefined?{authority_epoch_root:epochRoot}:{}),
|
|
319
332
|
process_policy_sha256:processBytes?sha256(processBytes):null,
|
|
320
333
|
desktop_policy_sha256:desktopBytes?sha256(desktopBytes):null,
|
|
321
334
|
coding_policy_sha256:sha256(codingBytes),autonomous_policy_sha256:sha256(autonomousBytes),host_policy_sha256:hostBytes?sha256(hostBytes):null
|
package/src/release-slot.mjs
CHANGED
|
@@ -205,8 +205,9 @@ export function renderStableLauncherCmd(){
|
|
|
205
205
|
return [
|
|
206
206
|
'@echo off',
|
|
207
207
|
'setlocal EnableExtensions DisableDelayedExpansion',
|
|
208
|
+
'if not defined SystemRoot exit /b 1',
|
|
208
209
|
'if not defined DEADBYTE_STATE_ROOT set "DEADBYTE_STATE_ROOT=%USERPROFILE%\\.deadbyte-mcp"',
|
|
209
|
-
'powershell.exe -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%DEADBYTE_STATE_ROOT%\\bin\\DEADBYTE-MCP.ps1" %*',
|
|
210
|
+
'"%SystemRoot%\\System32\\WindowsPowerShell\\v1.0\\powershell.exe" -NoLogo -NoProfile -ExecutionPolicy Bypass -File "%DEADBYTE_STATE_ROOT%\\bin\\DEADBYTE-MCP.ps1" %*',
|
|
210
211
|
'exit /b %ERRORLEVEL%',
|
|
211
212
|
''
|
|
212
213
|
].join('\r\n');
|
package/src/remote-live-log.mjs
CHANGED
|
@@ -53,6 +53,15 @@ 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 formatRemoteLogResetNotice(file,reason){
|
|
57
|
+
const name=String(file).split(/[\\/]/).at(-1);
|
|
58
|
+
const rawReason=sanitizeAuditArgs(typeof reason==='string'?reason:'unknown');
|
|
59
|
+
const detail=typeof rawReason==='string'?clipped(rawReason,96):'[REDACTED]';
|
|
60
|
+
if(/^segment-\d{8}\.jsonl$/.test(name))
|
|
61
|
+
return '[LIVELOG] AUDIT_STREAM_RESET '+name+' — '+detail+'; verify signed audit continuity before trusting timeline coverage';
|
|
62
|
+
return '[LIVELOG] RUNTIME_LOG_REOPENED '+name+' — '+detail+'; console diagnostic continuity may have gaps; audit verification is separate';
|
|
63
|
+
}
|
|
64
|
+
|
|
56
65
|
export function selectLiveLogNames({auditNames=[],runtimeNames=[]}={}){
|
|
57
66
|
if(!Array.isArray(auditNames)||!Array.isArray(runtimeNames)) throw new Error('live log names must be arrays');
|
|
58
67
|
const audit=[...new Set(auditNames.filter(name=>typeof name==='string'&&/^segment-\d{8}\.jsonl$/.test(name)))].sort().slice(-2);
|
|
@@ -388,13 +397,19 @@ export function createLiveTimelineWriter({stream=process.stdout,displayMode='uni
|
|
|
388
397
|
retention(){return Object.freeze({congested,suppressed_since_drain:suppressedSinceDrain,total_suppressed:totalSuppressed});}});
|
|
389
398
|
}
|
|
390
399
|
|
|
391
|
-
export function
|
|
400
|
+
export function defaultForegroundTimelineOptions({verbose=false}={}){
|
|
401
|
+
if(typeof verbose!=='boolean')throw new Error('default live timeline verbosity invalid');
|
|
402
|
+
return Object.freeze({commanderStyle:!verbose,foldHeartbeats:false});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,foldHeartbeats=false,heartbeatBurstLimit=10,heartbeatWindowMs=3000,displayMode='unicode',rootPaths={},trace=false,slowCallMs=1000,commanderStyle=false}={}){
|
|
392
406
|
if(typeof clock!=='function') throw new Error('live timeline clock must be a function');
|
|
393
407
|
if(!REMOTE_DISPLAY_MODES.includes(displayMode)) throw new Error('remote display mode invalid');
|
|
394
408
|
if(!Number.isInteger(heartbeatBurstLimit)||heartbeatBurstLimit<2||heartbeatBurstLimit>100) throw new Error('heartbeatBurstLimit invalid');
|
|
395
409
|
if(!Number.isInteger(heartbeatWindowMs)||heartbeatWindowMs<250||heartbeatWindowMs>60000) throw new Error('heartbeatWindowMs invalid');
|
|
396
410
|
if(!rootPaths||typeof rootPaths!=='object'||Array.isArray(rootPaths)) throw new Error('live timeline rootPaths invalid');
|
|
397
411
|
if(typeof trace!=='boolean') throw new Error('live timeline trace invalid');
|
|
412
|
+
if(typeof commanderStyle!=='boolean') throw new Error('live timeline commanderStyle invalid');
|
|
398
413
|
if(typeof foldHeartbeats!=='boolean') throw new Error('live timeline foldHeartbeats invalid');
|
|
399
414
|
if(!Number.isInteger(slowCallMs)||slowCallMs<100||slowCallMs>600000) throw new Error('live timeline slowCallMs invalid');
|
|
400
415
|
const pending=new Map();
|
|
@@ -443,6 +458,13 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,fo
|
|
|
443
458
|
const prefixIcon=unknown?(displayMode==='ascii'?'WARN':'⚠️'):null;
|
|
444
459
|
const evidence=receipt?' '+(displayMode==='ascii'?'RECEIPT':'🧾 receipt')+(displayMode==='ascii'?'=':'=')+receipt:'';
|
|
445
460
|
const reasonSep=displayMode==='ascii'?' - ':' — ';
|
|
461
|
+
if(commanderStyle){
|
|
462
|
+
const tag=unknown?'OUTCOME_UNKNOWN':pendingResult?'pending':error?'failed':'completed';
|
|
463
|
+
const state=rawStatus?' status='+rawStatus:'';
|
|
464
|
+
return remoteDisplayText('['+displayClock(eventMs(event,clock)).slice(0,8)+'] '+(unknown?'[WARN]':error?'[FAIL]':'[DONE]')+
|
|
465
|
+
' Tool call '+displayTool+' '+tag+' ['+shortSpanId(event.request_id)+']'+traceSuffix(span?.trace_meta)+(duration?' '+duration:'')+state+
|
|
466
|
+
(reason?reasonSep+reason:'')+evidence,displayMode);
|
|
467
|
+
}
|
|
446
468
|
return remoteDisplayText(eventPrefix(event,sequence,{displayTool,icon:prefixIcon})+traceSuffix(span?.trace_meta)+(summary?' '+summary:'')+(duration?' '+duration:'')+slow+' '+marker+statusDetail+evidence+(reason?reasonSep+reason:''),displayMode);
|
|
447
469
|
}
|
|
448
470
|
function burstLine(value){
|
|
@@ -497,6 +519,12 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,fo
|
|
|
497
519
|
}
|
|
498
520
|
pending.set(requestId,{request_id:requestId,tool,display_tool:displayTool,summary,trace_meta:traceMeta,started_at_ms:clock()});
|
|
499
521
|
peakInFlight=Math.max(peakInFlight,pending.size);
|
|
522
|
+
if(commanderStyle){
|
|
523
|
+
const safeArgs=sanitizeAuditArgs(args);
|
|
524
|
+
const visible=truncateUtf8(canonicalJson(safeArgs),768);
|
|
525
|
+
return remoteDisplayText('['+displayClock(eventMs(event,clock)).slice(0,8)+'] [CALL] Received tool call ['+
|
|
526
|
+
shortSpanId(requestId)+'] '+displayTool+traceSuffix(traceMeta)+' Arguments: '+visible,displayMode);
|
|
527
|
+
}
|
|
500
528
|
if(verbose) return formatRemoteToolCall({...event,tool:displayTool},{clock,displayMode})+traceSuffix(traceMeta);
|
|
501
529
|
if(visibleStart(displayTool)){
|
|
502
530
|
const startMarker=displayMode==='ascii'?'START':'▶ START';
|
|
@@ -526,7 +554,7 @@ export function createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,fo
|
|
|
526
554
|
const snapshot=event.outcome?.snapshot_id,epoch=event.outcome?.state_epoch;
|
|
527
555
|
if(/^[0-9a-f]{64}$/.test(String(snapshot??''))&&Number.isInteger(epoch)&&epoch>0){
|
|
528
556
|
const identity=`${snapshot}:${epoch}`;
|
|
529
|
-
if(identity===lastCapability){suppressedCapability++;return null;}
|
|
557
|
+
if(identity===lastCapability&&!commanderStyle){suppressedCapability++;return null;}
|
|
530
558
|
lastCapability=identity;
|
|
531
559
|
const prior=flushBurst();
|
|
532
560
|
const line=resultLine(event,span,sequence)+` snapshot=${snapshot.slice(0,8)} epoch=${epoch}`;
|