deadbyte-mcp 0.15.61 → 0.15.63
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 +81 -32
- package/PROVENANCE.json +38 -11
- package/README.txt +18 -18
- package/RELEASE.json +59 -15
- package/SBOM.cdx.json +2 -2
- package/TEST-MATRIX.json +31 -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/gate-windows-r160.ps1 +57 -0
- package/scripts/npm-release-r48.mjs +10 -0
- package/scripts/npm-release-readiness-r163.mjs +67 -0
- package/scripts/npm-release-readiness-r165.mjs +68 -0
- package/src/autonomous-supervisor.mjs +21 -4
- package/src/deadbyte-cli.mjs +90 -50
- 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/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 +12 -10
- package/test/r153-prepublish-identity.test.mjs +25 -4
- package/test/r153-staging-leaf-acl.test.mjs +5 -2
- 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 +103 -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/r164-remote-history-skew.test.mjs +13 -0
- package/test/r165-live-cli-skew.test.mjs +90 -0
- package/test/r165-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/r65-cli-invocation.test.mjs +27 -7
- 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/node_modules/which/CHANGELOG.md +0 -166
|
@@ -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}`;
|
package/src/runtime-update.mjs
CHANGED
|
@@ -22,7 +22,8 @@ import {canonicalLauncherPaths,assertCanonicalLaunchers,renderStateBoundLauncher
|
|
|
22
22
|
import {assertStageOwnedPath,assertStagingBootstrapInputs,assertStagingControlConfig} from './staging-input-isolation.mjs';
|
|
23
23
|
import { verifyProjectOwnerApproval } from './r70-owner-approval.mjs';
|
|
24
24
|
import {windowsPowerShellEnvironment} from './windows-powershell-environment.mjs';
|
|
25
|
-
import {verifyNarrowedPolicyGeneration} from './clean-activation-recovery-r153.mjs';
|
|
25
|
+
import {verifyNarrowedPolicyGeneration} from './clean-activation-recovery-r153.mjs';
|
|
26
|
+
import {parseProtectedEpochRoot,verifyTrustedEpochRelease} from './trusted-epoch-bootstrap-r158.mjs';
|
|
26
27
|
|
|
27
28
|
export {windowsPowerShellEnvironment};
|
|
28
29
|
|
|
@@ -54,7 +55,7 @@ function normalizePersistentPublicOrigin(value){
|
|
|
54
55
|
return publicOrigin.origin;
|
|
55
56
|
}
|
|
56
57
|
export function parseUpdateArgs(argv){
|
|
57
|
-
const out={stage:null,developmentRoot:null,expectedVersion:null,expectedManifest:null,sourceRoot:null,restoreStandingGrants:false,recoverOfflinePredecessor:false,enableMachineFs:false,enableObservation:false,enableDesktop:false,enableAutonomousSupervisor:false,cloudTransport:null,bridgeBootstrap:null,processPolicyBootstrap:null,hostPolicyBootstrap:null,codingPolicyBootstrap:null,autonomousPolicyBootstrap:null,ownerApprovalBootstrap:null,desktopPolicyBootstrap:null,semanticLspBootstrap:null,hostCommandPins:[]};
|
|
58
|
+
const out={stage:null,developmentRoot:null,expectedVersion:null,expectedManifest:null,sourceRoot:null,authorityEpochRoot:null,restoreStandingGrants:false,recoverOfflinePredecessor:false,enableMachineFs:false,enableObservation:false,enableDesktop:false,enableAutonomousSupervisor:false,cloudTransport:null,bridgeBootstrap:null,processPolicyBootstrap:null,hostPolicyBootstrap:null,codingPolicyBootstrap:null,autonomousPolicyBootstrap:null,ownerApprovalBootstrap:null,desktopPolicyBootstrap:null,semanticLspBootstrap:null,hostCommandPins:[]};
|
|
58
59
|
const cloud={},bridge={},processPolicy={},hostPolicy={},codingPolicy={},autonomousPolicy={},ownerApproval={},desktopPolicy={},semanticLsp={},seen=new Set(),hostPinIds=new Set();
|
|
59
60
|
const bridgeFlags=new Map([
|
|
60
61
|
['--bridge-root','root'],['--bridge-script-sha256','scriptSha256']
|
|
@@ -85,7 +86,7 @@ export function parseUpdateArgs(argv){
|
|
|
85
86
|
['--cloud-token-file','tokenFilePath'],['--cloud-token-sha256','tokenFileSha256'],
|
|
86
87
|
['--cloud-child-working-directory','childWorkingDirectory'],['--cloud-public-origin','publicOrigin']
|
|
87
88
|
]);
|
|
88
|
-
const valued=new Set(['--stage','--development-root','--expected-version','--expected-manifest','--source-root','--host-command-pin',...bridgeFlags.keys(),...processPolicyFlags.keys(),...hostPolicyFlags.keys(),...codingPolicyFlags.keys(),...autonomousPolicyFlags.keys(),...ownerApprovalFlags.keys(),...desktopPolicyFlags.keys(),...semanticLspFlags.keys(),...cloudFlags.keys()]);
|
|
89
|
+
const valued=new Set(['--stage','--development-root','--expected-version','--expected-manifest','--source-root','--authority-epoch-root','--host-command-pin',...bridgeFlags.keys(),...processPolicyFlags.keys(),...hostPolicyFlags.keys(),...codingPolicyFlags.keys(),...autonomousPolicyFlags.keys(),...ownerApprovalFlags.keys(),...desktopPolicyFlags.keys(),...semanticLspFlags.keys(),...cloudFlags.keys()]);
|
|
89
90
|
for(let index=0;index<argv.length;index++){
|
|
90
91
|
const arg=argv[index];
|
|
91
92
|
if(arg==='--restore-standing-grants'||arg==='--recover-offline-predecessor'||arg==='--enable-machine-fs'||arg==='--enable-observation'||arg==='--enable-desktop'||arg==='--enable-autonomous-supervisor'){
|
|
@@ -117,7 +118,8 @@ export function parseUpdateArgs(argv){
|
|
|
117
118
|
else if(arg==='--development-root') out.developmentRoot=path.resolve(value);
|
|
118
119
|
else if(arg==='--expected-version') out.expectedVersion=value;
|
|
119
120
|
else if(arg==='--expected-manifest') out.expectedManifest=value.toLowerCase();
|
|
120
|
-
else if(arg==='--source-root') out.sourceRoot=path.resolve(value);
|
|
121
|
+
else if(arg==='--source-root') out.sourceRoot=path.resolve(value);
|
|
122
|
+
else if(arg==='--authority-epoch-root') out.authorityEpochRoot=parseProtectedEpochRoot(value);
|
|
121
123
|
else if(bridgeFlags.has(arg)) bridge[bridgeFlags.get(arg)]=value;
|
|
122
124
|
else if(processPolicyFlags.has(arg)) processPolicy[processPolicyFlags.get(arg)]=value;
|
|
123
125
|
else if(hostPolicyFlags.has(arg)) hostPolicy[hostPolicyFlags.get(arg)]=value;
|
|
@@ -1830,7 +1832,7 @@ export async function recoverInterruptedSlotUpdates({stateRoot=DEFAULT_STATE_ROO
|
|
|
1830
1832
|
return {ok:true,launched,updates};
|
|
1831
1833
|
}
|
|
1832
1834
|
|
|
1833
|
-
export async function performSlotUpdate({stage,developmentRoot,expectedVersion,expectedManifest,sourceRoot=null,stateRoot=DEFAULT_STATE_ROOT,restoreStandingGrants=false,recoverOfflinePredecessor=false,cloudTransport=null,bridgeBootstrap=null,processPolicyBootstrap=null,hostPolicyBootstrap=null,codingPolicyBootstrap=null,autonomousPolicyBootstrap=null,ownerApprovalBootstrap=null,desktopPolicyBootstrap=null,semanticLspBootstrap=null,enableMachineFs=false,enableObservation=false,enableDesktop=false,enableAutonomousSupervisor=false,hostCommandPins=[]}){
|
|
1835
|
+
export async function performSlotUpdate({stage,developmentRoot,expectedVersion,expectedManifest,sourceRoot=null,authorityEpochRoot=null,stateRoot=DEFAULT_STATE_ROOT,restoreStandingGrants=false,recoverOfflinePredecessor=false,cloudTransport=null,bridgeBootstrap=null,processPolicyBootstrap=null,hostPolicyBootstrap=null,codingPolicyBootstrap=null,autonomousPolicyBootstrap=null,ownerApprovalBootstrap=null,desktopPolicyBootstrap=null,semanticLspBootstrap=null,enableMachineFs=false,enableObservation=false,enableDesktop=false,enableAutonomousSupervisor=false,hostCommandPins=[]}){
|
|
1834
1836
|
// R64 grants have no signed revocation epoch. The historical boolean restore
|
|
1835
1837
|
// cannot prove continuity, and must never materialize a successor grant.
|
|
1836
1838
|
if(restoreStandingGrants)throw new Error('R65 standing grant auto-restore disabled: explicit reauthorization required');
|
|
@@ -1909,6 +1911,16 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
|
|
|
1909
1911
|
control.config.McpUrlFile=pinned.path;control.config.McpUrlFileSha256=pinned.sha256;
|
|
1910
1912
|
}
|
|
1911
1913
|
workspace=await workspacePlan(control.workspaceRoot,state);launchers=await launcherPlan(state);
|
|
1914
|
+
// A protected installation is an explicit owner-provisioned candidate.
|
|
1915
|
+
// No discovery/fallback to writable npm roots and no automatic grant.
|
|
1916
|
+
const requestedEpochRoot=authorityEpochRoot??control.config.AuthorityEpochRoot;
|
|
1917
|
+
if(requestedEpochRoot){
|
|
1918
|
+
const proof=await verifyTrustedEpochRelease({
|
|
1919
|
+
epochRoot:requestedEpochRoot,releaseRoot:slot.release_root,
|
|
1920
|
+
nodeExecutable:control.config.Bridge.FilePath
|
|
1921
|
+
});
|
|
1922
|
+
control.config.AuthorityEpochRoot=proof.root;
|
|
1923
|
+
}
|
|
1912
1924
|
const generation=await createGenerationBundle({stateRoot:state,releaseRoot:slot.release_root,developmentRoot:development,workspaceRoot:workspace.target,releaseId:slot.release_id,currentConfig:control.config,codingPolicy:control.codingPolicy,autonomousPolicy:control.autonomousPolicy,hostPolicy:control.hostPolicy,processPolicy:control.processPolicy,desktopPolicy:control.desktopPolicy});
|
|
1913
1925
|
// A newly materialized generation is still pre-pointer-switch. Prove that
|
|
1914
1926
|
// legacy whole-drive roots were narrowed here, while a failure is still a
|
|
@@ -2068,7 +2080,7 @@ export async function runSlotUpdateCli(argv=process.argv.slice(2),{stateRoot=DEF
|
|
|
2068
2080
|
const args=parseUpdateArgs(argv);
|
|
2069
2081
|
const result=await performSlotUpdate({
|
|
2070
2082
|
stage:args.stage,developmentRoot:args.developmentRoot,expectedVersion:args.expectedVersion,expectedManifest:args.expectedManifest,
|
|
2071
|
-
sourceRoot:args.sourceRoot,stateRoot,restoreStandingGrants:args.restoreStandingGrants,recoverOfflinePredecessor:args.recoverOfflinePredecessor,
|
|
2083
|
+
sourceRoot:args.sourceRoot,authorityEpochRoot:args.authorityEpochRoot,stateRoot,restoreStandingGrants:args.restoreStandingGrants,recoverOfflinePredecessor:args.recoverOfflinePredecessor,
|
|
2072
2084
|
cloudTransport:args.cloudTransport,bridgeBootstrap:args.bridgeBootstrap,processPolicyBootstrap:args.processPolicyBootstrap,
|
|
2073
2085
|
hostPolicyBootstrap:args.hostPolicyBootstrap,codingPolicyBootstrap:args.codingPolicyBootstrap,
|
|
2074
2086
|
autonomousPolicyBootstrap:args.autonomousPolicyBootstrap,ownerApprovalBootstrap:args.ownerApprovalBootstrap,desktopPolicyBootstrap:args.desktopPolicyBootstrap,
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import {readFile,lstat} from 'node:fs/promises';
|
|
2
|
+
import {createHash} from 'node:crypto';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import {inspectTrustedInstallation,privilegedEpochInstallFiles} from './privileged-install-trust-r89.mjs';
|
|
5
|
+
|
|
6
|
+
const hash=buf=>createHash('sha256').update(buf).digest('hex');
|
|
7
|
+
export function parseProtectedEpochRoot(input){
|
|
8
|
+
if(typeof input!=='string'||!/^[A-Za-z]:[\\/]/.test(input)||!path.win32.isAbsolute(input)||
|
|
9
|
+
input.includes('\0')||input.startsWith('\\\\'))
|
|
10
|
+
throw new Error('authority epoch root must be Windows drive-absolute');
|
|
11
|
+
const root=path.win32.normalize(input);
|
|
12
|
+
if(path.win32.parse(root).root.toLowerCase()===root.toLowerCase())
|
|
13
|
+
throw new Error('authority epoch root may not be a drive root');
|
|
14
|
+
return root;
|
|
15
|
+
}
|
|
16
|
+
// This is a read-only proof, not installation or an authority grant.
|
|
17
|
+
// Windows ACL verification must run against OS Get-Acl, never a staged snapshot.
|
|
18
|
+
export async function verifyTrustedEpochRelease({epochRoot,releaseRoot,nodeExecutable}){
|
|
19
|
+
if(process.platform!=='win32'||process.arch!=='x64')
|
|
20
|
+
throw new Error('authority epoch protected source requires Windows x64');
|
|
21
|
+
const root=parseProtectedEpochRoot(epochRoot);
|
|
22
|
+
if(typeof releaseRoot!=='string'||!path.win32.isAbsolute(releaseRoot)||
|
|
23
|
+
typeof nodeExecutable!=='string'||!path.win32.isAbsolute(nodeExecutable))
|
|
24
|
+
throw new Error('authority epoch release or Node path invalid');
|
|
25
|
+
if(path.win32.normalize(releaseRoot).toLowerCase()===root.toLowerCase())
|
|
26
|
+
throw new Error('authority epoch root must be separate from writable release');
|
|
27
|
+
await inspectTrustedInstallation(privilegedEpochInstallFiles(root,nodeExecutable));
|
|
28
|
+
const sourcePaths=privilegedEpochInstallFiles(root,nodeExecutable)
|
|
29
|
+
.filter(p=>p.toLowerCase().startsWith(root.toLowerCase()+'\\'))
|
|
30
|
+
.map(p=>path.win32.relative(root,p));
|
|
31
|
+
const required=['MANIFEST.SHA256','src\\authority-epoch-cli-r65.mjs',...sourcePaths];
|
|
32
|
+
const unique=[...new Set(required.map(p=>p.toLowerCase()))];
|
|
33
|
+
for(const relative of unique){
|
|
34
|
+
const [a,b]=[path.join(releaseRoot,relative),path.join(root,relative)];
|
|
35
|
+
const [stA,stB]=await Promise.all([lstat(a),lstat(b)]);
|
|
36
|
+
if(!stA.isFile()||stA.isSymbolicLink()||!stB.isFile()||stB.isSymbolicLink())
|
|
37
|
+
throw new Error('authority epoch protected source not regular: '+relative);
|
|
38
|
+
const [aBytes,bBytes]=await Promise.all([readFile(a),readFile(b)]);
|
|
39
|
+
if(hash(aBytes)!==hash(bBytes)) throw new Error('authority epoch exact release source hash mismatch: '+relative);
|
|
40
|
+
}
|
|
41
|
+
return {root,checked_files:unique.length,decision:'EXACT_PROTECTED_SOURCE_MATCH'};
|
|
42
|
+
}
|
package/src/version.mjs
CHANGED
|
@@ -7,13 +7,16 @@ export function windowsPowerShellEnvironment(base=process.env){
|
|
|
7
7
|
const systemRoot=base.SystemRoot||base.SYSTEMROOT||'C:\\Windows';
|
|
8
8
|
const userProfile=base.USERPROFILE||os.homedir();
|
|
9
9
|
const programFiles=base.ProgramFiles||base.PROGRAMFILES||'C:\\Program Files';
|
|
10
|
-
const hasWindir=Object.keys(base).some(key=>key.toLowerCase()==='windir');
|
|
11
10
|
const environment={...base};
|
|
12
11
|
const inherited=Object.entries(base).filter(([key])=>key.toLowerCase()==='psmodulepath')
|
|
13
12
|
.flatMap(([,value])=>String(value??'').split(';')).filter(Boolean);
|
|
14
13
|
for(const key of Object.keys(environment))if(['psmodulepath','systemroot','windir'].includes(key.toLowerCase()))delete environment[key];
|
|
15
14
|
environment.SystemRoot=systemRoot;
|
|
16
|
-
|
|
15
|
+
environment.WINDIR=systemRoot;
|
|
16
|
+
const pathExt=String(base.PATHEXT??base.Pathext??base.pathext??'')
|
|
17
|
+
.split(';').map(item=>item.trim().toUpperCase()).filter(Boolean);
|
|
18
|
+
const standard=['.COM','.EXE','.BAT','.CMD','.VBS','.VBE','.JS','.JSE','.WSF','.WSH','.MSC'];
|
|
19
|
+
environment.PATHEXT=[...new Set([...standard,...pathExt])].join(';');
|
|
17
20
|
const modules=[path.join(systemRoot,'System32','WindowsPowerShell','v1.0','Modules'),
|
|
18
21
|
path.join(userProfile,'Documents','WindowsPowerShell','Modules'),
|
|
19
22
|
path.join(programFiles,'WindowsPowerShell','Modules'),...inherited];
|
|
@@ -16,6 +16,7 @@ test('PowerShell child environment collapses case-variant Windows root keys',()=
|
|
|
16
16
|
assert.deepEqual(Object.keys(environment).filter(key=>key.toLowerCase()==='windir'),['WINDIR']);
|
|
17
17
|
assert.equal(environment.SystemRoot,'C:\\Windows');
|
|
18
18
|
assert.equal(environment.WINDIR,'C:\\Windows');
|
|
19
|
+
assert.ok(environment.PATHEXT.split(';').includes('.EXE'));
|
|
19
20
|
});
|
|
20
21
|
|
|
21
22
|
test('security-sensitive Windows probes cannot select PowerShell via caller environment',
|
|
@@ -13,15 +13,15 @@ test('R130 security patch has a new immutable package identity and truthful sour
|
|
|
13
13
|
read('SBOM.cdx.json'),read('package-lock.json'),read('TEST-MATRIX.json')]);
|
|
14
14
|
assert.equal(pkg.name,'deadbyte-mcp');
|
|
15
15
|
for(const actual of [pkg.version,release.version,provenance.version,matrix.version,bom.metadata.component.version,lock.version,lock.packages[''].version,DEADBYTE_VERSION])
|
|
16
|
-
assert.equal(actual,'0.15.
|
|
17
|
-
assert.equal(release.train,'
|
|
18
|
-
assert.equal(matrix.train,'
|
|
19
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
20
|
-
assert.
|
|
21
|
-
assert.
|
|
22
|
-
assert.equal(
|
|
23
|
-
assert.equal(
|
|
24
|
-
assert.equal(provenance.
|
|
16
|
+
assert.equal(actual,'0.15.63');
|
|
17
|
+
assert.equal(release.train,'R165');
|
|
18
|
+
assert.equal(matrix.train,'R165');
|
|
19
|
+
assert.equal(release.source_predecessor.version,'0.15.62');
|
|
20
|
+
assert.equal(release.source_predecessor.train,'R163');
|
|
21
|
+
assert.match(release.source_predecessor.source_path,/0\.15\.62-r163-release-closure/i);
|
|
22
|
+
assert.equal(release.source_predecessor.manifest_status,'VERIFIED_FINAL_BYTES');
|
|
23
|
+
assert.equal(release.runtime_predecessor.version,'0.15.61');
|
|
24
|
+
assert.equal(provenance.r160.source_predecessor_manifest_status,'STALE_REJECTED');
|
|
25
25
|
assert.equal(pkg.dependencies['@modelcontextprotocol/client'],'2.3.1');
|
|
26
26
|
assert.equal(pkg.dependencies['@modelcontextprotocol/server'],'2.3.1');
|
|
27
27
|
for(const dep of ['@modelcontextprotocol/client','@modelcontextprotocol/server'])
|
|
@@ -8,22 +8,24 @@ import {DEADBYTE_VERSION} from '../src/version.mjs';
|
|
|
8
8
|
const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
|
|
9
9
|
const read=async rel=>JSON.parse(await readFile(path.join(root,rel),'utf8'));
|
|
10
10
|
|
|
11
|
-
test('
|
|
11
|
+
test('R165 package identity preserves R132 evidence and published source predecessor',async()=>{
|
|
12
12
|
const [pkg,lock,release,prov,bom,matrix]=await Promise.all([
|
|
13
13
|
read('package.json'),read('package-lock.json'),read('RELEASE.json'),
|
|
14
14
|
read('PROVENANCE.json'),read('SBOM.cdx.json'),read('TEST-MATRIX.json')]);
|
|
15
15
|
for(const value of [pkg.version,lock.version,lock.packages[''].version,release.version,prov.version,bom.metadata.component.version,matrix.version,DEADBYTE_VERSION]){
|
|
16
16
|
assert.equal(value,pkg.version);
|
|
17
17
|
}
|
|
18
|
-
assert.equal(release.train,'
|
|
19
|
-
assert.equal(matrix.train,'
|
|
20
|
-
assert.equal(release.source_predecessor.version,'0.15.
|
|
21
|
-
assert.
|
|
22
|
-
assert.
|
|
23
|
-
assert.equal(prov.
|
|
24
|
-
assert.equal(
|
|
25
|
-
assert.equal(
|
|
26
|
-
assert.equal(release.
|
|
18
|
+
assert.equal(release.train,'R165');
|
|
19
|
+
assert.equal(matrix.train,'R165');
|
|
20
|
+
assert.equal(release.source_predecessor.version,'0.15.62');
|
|
21
|
+
assert.equal(release.source_predecessor.train,'R163');
|
|
22
|
+
assert.match(release.source_predecessor.source_path,/0\.15\.62-r163-release-closure/i);
|
|
23
|
+
assert.equal(prov.r160.source_predecessor_manifest_status,'STALE_REJECTED');
|
|
24
|
+
assert.equal(release.r165.published_source_manifest_sha256,'4adf7d94682fbb71c4a91eb3147ecc48076bbc5d79cb67fcb875cde6a1bb4805');
|
|
25
|
+
assert.equal(release.r165.published_source_tarball_sha256,'812e97609b7c51353ed25f2202129737e43bb4fbe0778c3adec43ec065352641');
|
|
26
|
+
assert.equal(release.source_predecessor.tarball_sha256,release.r165.published_source_tarball_sha256);
|
|
27
|
+
assert.equal(prov.source_predecessor_tarball_sha256,release.source_predecessor.tarball_sha256);
|
|
28
|
+
assert.equal(release.runtime_predecessor.version,'0.15.61');
|
|
27
29
|
assert.equal(prov.runtime_predecessor.release_id,release.runtime_predecessor.release_id);
|
|
28
30
|
assert.equal(prov.runtime_predecessor.manifest_sha256,release.runtime_predecessor.manifest_sha256);
|
|
29
31
|
assert.equal(prov.runtime_predecessor.generation_manifest_sha256,release.runtime_predecessor.generation_manifest_sha256);
|
|
@@ -5,10 +5,31 @@ import path from 'node:path';
|
|
|
5
5
|
import {fileURLToPath} from 'node:url';
|
|
6
6
|
import {verifyR153Lineage,verifyR153IdentityTree} from '../scripts/r153-prepublish-identity.mjs';
|
|
7
7
|
const root=path.resolve(path.dirname(fileURLToPath(import.meta.url)),'..');
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
8
|
+
// R153 is a frozen 0.15.61/R154 lineage rule, not the R160 release gate.
|
|
9
|
+
// Exercise its historical contract with an immutable synthetic fixture.
|
|
10
|
+
// Independently prove it rejects the live R160 metadata rather than trying to
|
|
11
|
+
// force 0.15.62 into a 0.15.61 verifier.
|
|
12
|
+
const historical=()=>{
|
|
13
|
+
const runtime={version:'0.15.45',release_id:'v0.15.45-34aa8d1241cfb8bf',
|
|
14
|
+
manifest_sha256:'34aa8d1241cfb8bf0b5ba303bf3c06e0e9e1bf01f64457bca659d6dd4d634fdc',
|
|
15
|
+
generation_manifest_sha256:'ff2b73410062688651abd7bc2dd7b2b8c87810a28e3808bfe1f4352a25b0e6e4'};
|
|
16
|
+
return {pkg:{name:'deadbyte-mcp',version:'0.15.61'},
|
|
17
|
+
bridge:{name:'deadbyte-chatgpt-bridge',version:'0.15.61'},
|
|
18
|
+
release:{name:'deadbyte-mcp',schema:'deadbyte.release.v1',version:'0.15.61',train:'R154',
|
|
19
|
+
source_predecessor:{version:'0.15.60',
|
|
20
|
+
tarball_sha256:'b85f735f4c8be56f97ea5c78f0cc48583e8d5708a704c903596f5720e36ace83',
|
|
21
|
+
release_status:'REGISTRY_PUBLISHED'},
|
|
22
|
+
runtime_predecessor:runtime,parent:{...runtime}}};
|
|
23
|
+
};
|
|
24
|
+
const copy=()=>historical();
|
|
25
|
+
test('R153 historical verifier refuses current R160 package identity',async()=>{
|
|
26
|
+
const state={pkg:JSON.parse(await readFile(path.join(root,'package.json'),'utf8')),
|
|
27
|
+
release:JSON.parse(await readFile(path.join(root,'RELEASE.json'),'utf8')),
|
|
28
|
+
bridge:JSON.parse(await readFile(path.join(root,'bridge/package.json'),'utf8'))};
|
|
29
|
+
assert.equal(state.pkg.version,'0.15.63');
|
|
30
|
+
assert.throws(()=>verifyR153Lineage(state),/package version/);
|
|
31
|
+
await assert.rejects(verifyR153IdentityTree(root),/package version/);
|
|
32
|
+
});
|
|
12
33
|
test('R153 exact source and runtime lineage: metadata-only PASS',()=>{
|
|
13
34
|
assert.equal(verifyR153Lineage(copy()).status,'PASS');
|
|
14
35
|
});
|
|
@@ -3,6 +3,8 @@ import assert from 'node:assert/strict';
|
|
|
3
3
|
import {readFileSync} from 'node:fs';
|
|
4
4
|
import {fileURLToPath} from 'node:url';
|
|
5
5
|
import {spawnSync} from 'node:child_process';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import {windowsPowerShellEnvironment} from '../src/runtime-update.mjs';
|
|
6
8
|
|
|
7
9
|
const bootstrap=readFileSync(fileURLToPath(new URL('../scripts/bootstrap-uac-migration-r153.ps1',import.meta.url)),'utf8');
|
|
8
10
|
|
|
@@ -51,8 +53,9 @@ try {
|
|
|
51
53
|
}
|
|
52
54
|
}`;
|
|
53
55
|
const encoded=Buffer.from(ps,'utf16le').toString('base64');
|
|
54
|
-
const
|
|
55
|
-
|
|
56
|
+
const powershell=path.join(process.env.SystemRoot||'C:\\Windows','System32','WindowsPowerShell','v1.0','powershell.exe');
|
|
57
|
+
const result=spawnSync(powershell,['-NoLogo','-NoProfile','-NonInteractive','-EncodedCommand',encoded],
|
|
58
|
+
{env:windowsPowerShellEnvironment(process.env),encoding:'utf8',timeout:30000,maxBuffer:256*1024});
|
|
56
59
|
assert.equal(result.status,0,result.stderr||result.stdout||String(result.error));
|
|
57
60
|
assert.match(result.stdout,/R153_WIN32_LEAF_ACL_REPAIR_PASS/);
|
|
58
61
|
});
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {createLiveTimelineTracker} from '../src/remote-live-log.mjs';
|
|
4
|
+
|
|
5
|
+
test('R155 commander mode renders an individual START/DONE pair with elapsed time and correlation',()=>{
|
|
6
|
+
const tracker=createLiveTimelineTracker({commanderStyle:true,clock:()=>Date.parse('2026-10-24T14:15:00.000Z')});
|
|
7
|
+
const begin=tracker.observe({event_type:'tool.request',tool:'machine_status',request_id:'abcdef012345',args:{}},{sequence:9});
|
|
8
|
+
assert.match(begin,/\[21:15:00\].*Received tool call.*machine_status/);
|
|
9
|
+
assert.match(begin,/abcdef01/);
|
|
10
|
+
const done=tracker.observe({event_type:'tool.result',tool:'machine_status',request_id:'abcdef012345',outcome:{status:'ok',duration_ms:4}},{sequence:10});
|
|
11
|
+
assert.match(done,/\[21:15:00\].*machine_status.*completed/);
|
|
12
|
+
assert.match(done,/4ms/);
|
|
13
|
+
assert.match(tracker.summary(),/1 calls \| 1 ok/);
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
test('R155 commander mode never prints plain credentials even with opt-in detailed timeline',()=>{
|
|
17
|
+
const tracker=createLiveTimelineTracker({commanderStyle:true,clock:()=>Date.parse('2026-10-24T14:15:00.000Z')});
|
|
18
|
+
const line=tracker.observe({event_type:'tool.request',tool:'host_exec',request_id:'secret-one',args:{
|
|
19
|
+
root_id:'core',command_id:'node_script',api_key:'sk-do-not-print-this-123456',content:'top secret source payload',
|
|
20
|
+
metadata:{token:'Bearer do-not-print-token'}}},{sequence:11});
|
|
21
|
+
assert.doesNotMatch(line,/sk-do-not-print-this|top secret source payload|do-not-print-token/);
|
|
22
|
+
assert.match(line,/Received tool call/);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('R155 commander mode does not classify denied or unknown mutations as completed successes',()=>{
|
|
26
|
+
const tracker=createLiveTimelineTracker({commanderStyle:true,clock:()=>Date.parse('2026-10-24T14:15:00.000Z')});
|
|
27
|
+
tracker.observe({event_type:'tool.request',tool:'host_file_write',request_id:'fail-id',args:{root_id:'core',path:'a.txt'}},{sequence:1});
|
|
28
|
+
const denied=tracker.observe({event_type:'tool.result',tool:'host_file_write',request_id:'fail-id',outcome:{status:'denied',is_error:true,duration_ms:3}},{sequence:2});
|
|
29
|
+
assert.match(denied,/denied|failed/i);
|
|
30
|
+
assert.doesNotMatch(denied,/completed.*✓|completed.*OK/);
|
|
31
|
+
tracker.observe({event_type:'tool.request',tool:'machine_fs',request_id:'unknown-id',args:{op:'write',root_id:'core'}},{sequence:3});
|
|
32
|
+
const unknown=tracker.observe({event_type:'tool.result',tool:'machine_fs',request_id:'unknown-id',outcome:{status:'interrupted',side_effect_state:'unknown'}},{sequence:4});
|
|
33
|
+
assert.match(unknown,/OUTCOME_UNKNOWN|UNKNOWN/);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('R155 commander mode preserves individual heartbeat calls instead of folding bursts',()=>{
|
|
37
|
+
const tracker=createLiveTimelineTracker({commanderStyle:true,foldHeartbeats:false,clock:()=>Date.parse('2026-10-24T14:15:00.000Z')});
|
|
38
|
+
for(let n=0;n<3;n++){
|
|
39
|
+
const id='id-'+n;
|
|
40
|
+
assert.match(tracker.observe({event_type:'tool.request',tool:'machine_ping',request_id:id,args:{}},{sequence:2*n+1}),/Received tool call/);
|
|
41
|
+
assert.match(tracker.observe({event_type:'tool.result',tool:'machine_ping',request_id:id,outcome:{status:'ok',duration_ms:1}},{sequence:2*n+2}),/completed/);
|
|
42
|
+
}
|
|
43
|
+
assert.match(tracker.summary(),/3 calls \| 3 ok/);
|
|
44
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import {readFileSync} from 'node:fs';
|
|
4
|
+
import {spawnSync} from 'node:child_process';
|
|
5
|
+
import {fileURLToPath} from 'node:url';
|
|
6
|
+
import path from 'node:path';
|
|
7
|
+
import * as live from '../src/remote-live-log.mjs';
|
|
8
|
+
|
|
9
|
+
const root=path.dirname(path.dirname(fileURLToPath(import.meta.url)));
|
|
10
|
+
const cli=path.join(root,'src','deadbyte-cli.mjs');
|
|
11
|
+
const now=()=>Date.parse('2026-10-24T15:00:00.000Z');
|
|
12
|
+
function defaultTracker(){
|
|
13
|
+
assert.equal(typeof live.defaultForegroundTimelineOptions,'function','default timeline configuration must be exported');
|
|
14
|
+
return live.createLiveTimelineTracker({clock:now,...live.defaultForegroundTimelineOptions({verbose:false})});
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
test('R155 ordinary remote has per-call START and DONE without opt-in flag',()=>{
|
|
18
|
+
const tracker=defaultTracker();
|
|
19
|
+
const start=tracker.observe({event_type:'tool.request',request_id:'abc012345678',tool:'machine_status',args:{}},{sequence:1});
|
|
20
|
+
assert.match(start,/Received tool call/);
|
|
21
|
+
assert.match(start,/machine_status/);
|
|
22
|
+
const done=tracker.observe({event_type:'tool.result',request_id:'abc012345678',tool:'machine_status',outcome:{status:'ok',duration_ms:3}},{sequence:2});
|
|
23
|
+
assert.match(done,/completed/);
|
|
24
|
+
assert.match(done,/3ms/);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('R155 ordinary remote does not fold successive status calls into a burst',()=>{
|
|
28
|
+
const tracker=defaultTracker();
|
|
29
|
+
for(let n=0;n<3;n++){
|
|
30
|
+
const id='repeat-'+n;
|
|
31
|
+
assert.match(tracker.observe({event_type:'tool.request',request_id:id,tool:'machine_ping',args:{}},{sequence:n*2+1}),/Received tool call/);
|
|
32
|
+
assert.match(tracker.observe({event_type:'tool.result',request_id:id,tool:'machine_ping',outcome:{status:'ok',duration_ms:1}},{sequence:n*2+2}),/completed/);
|
|
33
|
+
}
|
|
34
|
+
assert.match(tracker.summary(),/3 calls \| 3 ok/);
|
|
35
|
+
assert.doesNotMatch(tracker.summary(),/folded [1-9]/);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('R155 ordinary remote keeps credentials and payload opaque by default',()=>{
|
|
39
|
+
const tracker=defaultTracker();
|
|
40
|
+
const line=tracker.observe({event_type:'tool.request',request_id:'secret-123456',tool:'host_exec',args:{
|
|
41
|
+
api_key:'sk-secret-test-1234567890',content:'PRIVATE SOURCE CONTENT',root_id:'core',
|
|
42
|
+
command_id:'node_script',path:'C:\\Safe\\test.js',
|
|
43
|
+
metadata:{token:'Bearer should-not-appear'}
|
|
44
|
+
}},{sequence:1});
|
|
45
|
+
assert.match(line,/Received tool call/);
|
|
46
|
+
assert.match(line,/node_script/);
|
|
47
|
+
assert.doesNotMatch(line,/sk-secret-test|PRIVATE SOURCE CONTENT|should-not-appear/);
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('R155 ordinary remote prints DENIED and UNKNOWN as failures rather than completed success',()=>{
|
|
51
|
+
const tracker=defaultTracker();
|
|
52
|
+
tracker.observe({event_type:'tool.request',request_id:'one',tool:'host_file_read',args:{path:'a.txt'}},{sequence:1});
|
|
53
|
+
const fail=tracker.observe({event_type:'tool.result',request_id:'one',tool:'host_file_read',outcome:{status:'denied',is_error:true,reason:'arm file missing'}},{sequence:2});
|
|
54
|
+
assert.match(fail,/failed/);assert.match(fail,/arm file missing/);
|
|
55
|
+
tracker.observe({event_type:'tool.request',request_id:'two',tool:'coding_file_write',args:{path:'x.txt'}},{sequence:3});
|
|
56
|
+
const unknown=tracker.observe({event_type:'tool.result',request_id:'two',tool:'coding_file_write',outcome:{status:'interrupted',side_effect_state:'unknown'}},{sequence:4});
|
|
57
|
+
assert.match(unknown,/OUTCOME_UNKNOWN/);
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
test('R155 ordinary remote has no new CLI live-log mode and wired default live behavior',()=>{
|
|
61
|
+
const source=readFileSync(cli,'utf8');
|
|
62
|
+
const help=spawnSync(process.execPath,[cli,'--help'],{encoding:'utf8',timeout:10000});
|
|
63
|
+
assert.equal(help.status,0);
|
|
64
|
+
assert.doesNotMatch(help.stdout,/live-log=commander/);
|
|
65
|
+
assert.doesNotMatch(source,/--live-log=commander|commanderLiveLog/);
|
|
66
|
+
assert.match(source,/defaultForegroundTimelineOptions\(\{verbose\}\)/);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('R155 unchanged capability polling still has a matching visible DONE for every CALL',()=>{
|
|
70
|
+
const tracker=defaultTracker();
|
|
71
|
+
const snapshot='a'.repeat(64);
|
|
72
|
+
for(let n=0;n<2;n++){
|
|
73
|
+
const id='cap-'+n;
|
|
74
|
+
assert.match(tracker.observe({event_type:'tool.request',request_id:id,tool:'capability_manifest',args:{}},{sequence:n*2+1}),/Received tool call/);
|
|
75
|
+
const done=tracker.observe({event_type:'tool.result',request_id:id,tool:'capability_manifest',outcome:{status:'ok',snapshot_id:snapshot,state_epoch:1,duration_ms:2}},{sequence:n*2+2});
|
|
76
|
+
assert.match(done,/completed/);
|
|
77
|
+
}
|
|
78
|
+
assert.match(tracker.summary(),/2 calls \\| 2 ok/);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
test('R155 runtime-only stream reset does not claim audit corruption, audit reset remains severe',()=>{
|
|
82
|
+
assert.equal(typeof live.formatRemoteLogResetNotice,'function');
|
|
83
|
+
const runtime=live.formatRemoteLogResetNotice('C:\\state\\runtime\\logs\\coregate.stdout.log','file identity changed');
|
|
84
|
+
assert.match(runtime,/RUNTIME_LOG_REOPENED/);
|
|
85
|
+
assert.doesNotMatch(runtime,/AUDIT_STREAM_RESET/);
|
|
86
|
+
const audit=live.formatRemoteLogResetNotice('C:\\state\\audit\\segment-00000012.jsonl','file identity changed');
|
|
87
|
+
assert.match(audit,/AUDIT_STREAM_RESET/);
|
|
88
|
+
assert.match(audit,/verify|audit/i);
|
|
89
|
+
});
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
# R156 read-only verifier: extracts only the controller's authority routing function.
|
|
2
|
+
Set-StrictMode -Version Latest
|
|
3
|
+
$ErrorActionPreference='Stop'
|
|
4
|
+
$root='D:\Projects\deadbyte-mcp\development\deadbyte-mcp-0.15.62-r156-loop-epoch'
|
|
5
|
+
$stage='C:\Program Files\DEADBYTE-R154-Exact-01561-Activation'
|
|
6
|
+
$file=Join-Path $root 'controller\deadbyte-controller.ps1'
|
|
7
|
+
$tokens=$null;$errors=$null
|
|
8
|
+
$ast=[System.Management.Automation.Language.Parser]::ParseFile($file,[ref]$tokens,[ref]$errors)
|
|
9
|
+
if(@($errors).Count -ne 0){throw 'R156_CONTROLLER_PARSER_RED'}
|
|
10
|
+
$matches=@($ast.FindAll({param($n) $n -is [System.Management.Automation.Language.FunctionDefinitionAst] -and $n.Name -in @('Invoke-AuthorityEpochCommand','Assert-ProtectedEpochSourceAcl')},$true))
|
|
11
|
+
if($matches.Count -ne 2){throw 'R156_AUTHORITY_ENTRY_OR_ACL_MISSING'}
|
|
12
|
+
function Fail([string]$message){throw $message}
|
|
13
|
+
function Get-ExecutableHash([string]$File){(Get-FileHash -LiteralPath $File -Algorithm SHA256).Hash.ToLowerInvariant()}
|
|
14
|
+
foreach($f in $matches){Invoke-Expression $f.Extent.Text}
|
|
15
|
+
$cfg=@{CoreRoot=$root;AuthorityEpochRoot=$stage;Bridge=@{FilePath='C:\Program Files\nodejs\node.exe'}}
|
|
16
|
+
# A bogus plane stops before invoking Node or modifying state. Reaching this
|
|
17
|
+
# branch proves the protected exact source/manifest comparisons succeeded.
|
|
18
|
+
$ok=$false
|
|
19
|
+
try{Invoke-AuthorityEpochCommand $cfg 'invalid-plane' 'status' 'not-used'}catch{
|
|
20
|
+
if($_.Exception.Message -eq 'authority epoch plane invalid: invalid-plane'){$ok=$true}
|
|
21
|
+
else{throw}
|
|
22
|
+
}
|
|
23
|
+
if(-not $ok){throw 'R156_EXPECTED_POLICY_BOUNDARY_NOT_REACHED'}
|
|
24
|
+
Write-Output 'R156_R89_MATCHED_PROTECTED_EPOCH_ROOT_PASS no_grants_written=true'
|
|
25
|
+
$cfg.AuthorityEpochRoot='C:\Windows\System32'
|
|
26
|
+
$blocked=$false
|
|
27
|
+
try{Invoke-AuthorityEpochCommand $cfg 'invalid-plane' 'status' 'not-used'}catch{
|
|
28
|
+
if($_.Exception.Message -eq 'authority epoch trusted root release manifest mismatch'){$blocked=$true}
|
|
29
|
+
else{throw}
|
|
30
|
+
}
|
|
31
|
+
if(-not $blocked){throw 'R156_INCOMPATIBLE_ROOT_WAS_ACCEPTED'}
|
|
32
|
+
Write-Output 'R156_R89_DIFFERENT_RELEASE_ROOT_REJECTED'
|
|
33
|
+
# Even a byte-identical directory is not authority if the OS ACL is writable.
|
|
34
|
+
$cfg.AuthorityEpochRoot=$root
|
|
35
|
+
$blockedWritable=$false
|
|
36
|
+
try { Invoke-AuthorityEpochCommand $cfg 'invalid-plane' 'status' 'not-used' } catch {
|
|
37
|
+
if($_.Exception.Message -match '^authority epoch protected path (owner denied|writable by an untrusted principal|reparse denied):'){$blockedWritable=$true}
|
|
38
|
+
else{throw}
|
|
39
|
+
}
|
|
40
|
+
if(-not $blockedWritable){throw 'R156_WRITABLE_EPOCH_COPY_WAS_ACCEPTED'}
|
|
41
|
+
Write-Output 'R156_R89_WRITABLE_SAME_MANIFEST_ROOT_REJECTED'
|
|
42
|
+
$cfg.AuthorityEpochRoot=$stage
|
|
43
|
+
$original=Join-Path $root 'src\authority-continuity-r65.mjs'
|
|
44
|
+
$protected=Join-Path $stage 'src\authority-continuity-r65.mjs'
|
|
45
|
+
if((Get-ExecutableHash $original) -cne (Get-ExecutableHash $protected)){
|
|
46
|
+
throw 'R156_STAGE_SOURCE_DIFFERENT'
|
|
47
|
+
}
|
|
48
|
+
Write-Output 'R156_R89_SOURCE_BYTES_MATCH_PASS'
|