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.
Files changed (83) hide show
  1. package/MANIFEST.SHA256 +81 -32
  2. package/PROVENANCE.json +38 -11
  3. package/README.txt +18 -18
  4. package/RELEASE.json +59 -15
  5. package/SBOM.cdx.json +2 -2
  6. package/TEST-MATRIX.json +31 -2
  7. package/bridge/package.json +1 -1
  8. package/controller/deadbyte-controller.ps1 +78 -1
  9. package/controller/deadbyte-process-policy.json +4 -4
  10. package/docs/R156-LOOP-AND-EPOCH-ROUTING.md +86 -0
  11. package/docs/R157-ARCHITECTURE-DEPENDENCIES.md +58 -0
  12. package/docs/R157-ARCHITECTURE-GRAPH.json +2874 -0
  13. package/docs/R157-ENGINEERING-HANDOFF.md +71 -0
  14. package/docs/R157-FILE-DELTA.json +80 -0
  15. package/docs/R157-FOCUSED-VERIFICATION.log +0 -0
  16. package/docs/R157-REFERENCE-CAPABILITY-MATRIX.md +207 -0
  17. package/docs/R157-REFERENCE-DECISIONS.md +46 -0
  18. package/docs/R157-REFERENCE-INVENTORY.json +2849 -0
  19. package/docs/R158-COMBINED-REGRESSION.log +0 -0
  20. package/docs/R158-DELTA.json +80 -0
  21. package/docs/R158-EPOCH-BOOTSTRAP-GREEN.log +0 -0
  22. package/docs/R158-EPOCH-BOOTSTRAP-RED.log +0 -0
  23. package/docs/R158-EPOCH-IDENTITY-RED.log +0 -0
  24. package/docs/R158-FOCUSED-GREEN.log +0 -0
  25. package/docs/R158-PROTECTED-EPOCH-VERIFIED.md +43 -0
  26. package/docs/R158-PROTECTED-EXACT-SOURCE-LIVE.log +0 -0
  27. package/docs/R158-UPDATER-REGRESSION.log +0 -0
  28. package/docs/R159-COMPLETED-HISTORY-BACKUP/deadbyte-cli.mjs.before +965 -0
  29. package/docs/R159-COMPLETED-HISTORY-BACKUP/legacy-marker-evidence-r159.mjs.before +102 -0
  30. package/docs/R159-COMPLETED-HISTORY-BACKUP/r159-legacy-marker-evidence.test.mjs.before +79 -0
  31. package/docs/R159-COMPLETED-HISTORY-PATCH.json +26 -0
  32. package/docs/R159-MARKER-LOCATOR-CHECKPOINT.md +46 -0
  33. package/docs/R160-ENGINEERING-HANDOFF.md +32 -0
  34. package/docs/R160-REFERENCE-CAPABILITY-MATRIX.json +165 -0
  35. package/package.json +2 -2
  36. package/scripts/gate-windows-r160.ps1 +57 -0
  37. package/scripts/npm-release-r48.mjs +10 -0
  38. package/scripts/npm-release-readiness-r163.mjs +67 -0
  39. package/scripts/npm-release-readiness-r165.mjs +68 -0
  40. package/src/autonomous-supervisor.mjs +21 -4
  41. package/src/deadbyte-cli.mjs +90 -50
  42. package/src/legacy-marker-evidence-r159.mjs +128 -0
  43. package/src/package-identity-r160.mjs +61 -0
  44. package/src/release-generation.mjs +13 -0
  45. package/src/release-slot.mjs +2 -1
  46. package/src/remote-live-log.mjs +30 -2
  47. package/src/runtime-update.mjs +18 -6
  48. package/src/trusted-epoch-bootstrap-r158.mjs +42 -0
  49. package/src/version.mjs +1 -1
  50. package/src/windows-powershell-environment.mjs +5 -2
  51. package/test/r106-trusted-powershell-spawn.test.mjs +1 -0
  52. package/test/r130-release-security-contract.test.mjs +9 -9
  53. package/test/r132-release-identity.test.mjs +12 -10
  54. package/test/r153-prepublish-identity.test.mjs +25 -4
  55. package/test/r153-staging-leaf-acl.test.mjs +5 -2
  56. package/test/r155-commander-live-log.test.mjs +44 -0
  57. package/test/r155-default-live-log.test.mjs +89 -0
  58. package/test/r156-epoch-route-smoke.ps1 +48 -0
  59. package/test/r156-supervisor-pacing.test.mjs +59 -0
  60. package/test/r157-architecture-map.mjs +61 -0
  61. package/test/r157-reference-survey.mjs +91 -0
  62. package/test/r157-supervisor-singleflight.test.mjs +32 -0
  63. package/test/r158-epoch-bootstrap.test.mjs +34 -0
  64. package/test/r159-legacy-marker-evidence.test.mjs +103 -0
  65. package/test/r159-legacy-marker-evidence.test.mjs.before-assertion-fix-20261025-120246.bak +100 -0
  66. package/test/r160-marker-proof.test.mjs +87 -0
  67. package/test/r160-package-decision-manifest.test.mjs +67 -0
  68. package/test/r160-release-contract.test.mjs +30 -0
  69. package/test/r160-stable-launcher.test.mjs +10 -0
  70. package/test/r163-isolated-slot-rollback-restore.test.mjs +58 -0
  71. package/test/r163-publish-fence.test.mjs +13 -0
  72. package/test/r164-remote-history-skew.test.mjs +13 -0
  73. package/test/r165-live-cli-skew.test.mjs +90 -0
  74. package/test/r165-publish-fence.test.mjs +13 -0
  75. package/test/r48-release-contract.test.mjs +7 -1
  76. package/test/r58-launcher-shell.test.mjs +4 -1
  77. package/test/r59-tool-surface-compat.test.mjs +1 -1
  78. package/test/r65-cli-invocation.test.mjs +27 -7
  79. package/test/release-generation.test.mjs +16 -0
  80. package/test/remote-live-log-r37.test.mjs +5 -3
  81. package/test/remote-session-cli.test.mjs +11 -11
  82. package/test/stabilization-state-root.test.mjs +2 -1
  83. 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'&&currentConfig.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
@@ -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');
@@ -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 createLiveTimelineTracker({clock=()=>Date.now(),verbose=false,foldHeartbeats=false,heartbeatBurstLimit=10,heartbeatWindowMs=3000,displayMode='unicode',rootPaths={},trace=false,slowCallMs=1000}={}){
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}`;
@@ -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
@@ -1,4 +1,4 @@
1
- export const DEADBYTE_VERSION = '0.15.61';
1
+ export const DEADBYTE_VERSION = '0.15.63';
2
2
  export const MCP_PROTOCOL_VERSION = '2026-07-28';
3
3
  export const RECEIPT_SCHEMA_V1 = 'deadbyte.receipt.v1';
4
4
  export const RECEIPT_SCHEMA_V2 = 'deadbyte.receipt.v2';
@@ -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
- if(hasWindir)environment.WINDIR=systemRoot;
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.61');
17
- assert.equal(release.train,'R154');
18
- assert.equal(matrix.train,'R154');
19
- assert.equal(release.source_predecessor.version,'0.15.60');
20
- assert.match(release.source_predecessor.source_path,/0\.15\.60/);
21
- assert.equal(release.runtime_predecessor.version,'0.15.45');
22
- assert.equal(provenance.source_predecessor_tarball_sha256,'b85f735f4c8be56f97ea5c78f0cc48583e8d5708a704c903596f5720e36ace83');
23
- assert.equal(provenance.source_predecessor_version,'0.15.60');
24
- assert.equal(provenance.source_predecessor_manifest_file_sha256,'2a19c9da7f11eb831dfb24ba6c6d9789e25a373cea2f3a25ff21d460e07fba36');
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('R144 package identities, R132 historic evidence and published predecessor are consistent',async()=>{
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,'R154');
19
- assert.equal(matrix.train,'R154');
20
- assert.equal(release.source_predecessor.version,'0.15.60');
21
- assert.match(release.source_predecessor.source_path,/0\.15\.60/);
22
- assert.equal(prov.source_predecessor_version,'0.15.60');
23
- assert.equal(prov.source_predecessor_manifest_file_sha256,'2a19c9da7f11eb831dfb24ba6c6d9789e25a373cea2f3a25ff21d460e07fba36');
24
- assert.equal(prov.parent_tarball_sha256,'b85f735f4c8be56f97ea5c78f0cc48583e8d5708a704c903596f5720e36ace83');
25
- assert.equal(prov.parent_registry_tarball_sha1,'04cc1a1e80bcb7062ac39eee6b7bab70edd5d8c3');
26
- assert.equal(release.runtime_predecessor.version,'0.15.45');
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
- const pkg=JSON.parse(await readFile(path.join(root,'package.json'),'utf8'));
9
- const release=JSON.parse(await readFile(path.join(root,'RELEASE.json'),'utf8'));
10
- const bridge=JSON.parse(await readFile(path.join(root,'bridge','package.json'),'utf8'));
11
- const copy=()=>structuredClone({pkg,release,bridge});
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 result=spawnSync('powershell.exe',['-NoLogo','-NoProfile','-NonInteractive','-EncodedCommand',encoded],
55
- {encoding:'utf8',timeout:30000,maxBuffer:256*1024});
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'