deadbyte-mcp 0.15.44 → 0.15.46

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 (174) hide show
  1. package/MANIFEST.SHA256 +86 -138
  2. package/PROVENANCE.json +35 -18
  3. package/README.txt +22 -21
  4. package/RELEASE.json +369 -334
  5. package/SBOM.cdx.json +2 -2
  6. package/TEST-MATRIX.json +27 -17
  7. package/bridge/package.json +1 -1
  8. package/controller/deadbyte-controller.ps1 +1871 -1769
  9. package/controller/deadbyte-desktop-policy.json +6 -6
  10. package/controller/deadbyte-process-policy.json +4 -4
  11. package/docs/R68-COMPAT-HOST-EXEC-CANDIDATE.md +63 -0
  12. package/docs/R69-LIVE-OUTCOME-R70-POLICY-TRANSFER.md +136 -0
  13. package/docs/R70-CODING-DIGEST-SUPERVISOR-CHECKPOINT-20261013.md +42 -0
  14. package/docs/R70-FINITE-GRANT-OWNER-BOUNDARY-20261013.md +80 -0
  15. package/docs/R70-OWNER-BOOTSTRAP-CHECKPOINT-20261013.md +60 -0
  16. package/docs/R70-REFERENCE-MECHANISMS.json +95 -0
  17. package/docs/R70-TRUST-GUARD-EXPIRY-CHECKPOINT-20261013.md +47 -0
  18. package/package.json +2 -2
  19. package/scripts/benchmark-l0-r71.mjs +76 -0
  20. package/scripts/gate-windows-r71.ps1 +64 -0
  21. package/scripts/gate-windows-r72.ps1 +76 -0
  22. package/scripts/npm-release-r48.mjs +4 -0
  23. package/scripts/npm-release-readiness-r71.mjs +21 -0
  24. package/scripts/npm-release-readiness-r72.mjs +76 -0
  25. package/scripts/verify-r70-full-regression.ps1 +72 -0
  26. package/src/authority-continuity-r65.mjs +28 -8
  27. package/src/autonomous-mcp-tools.mjs +1 -1
  28. package/src/autonomous-output-contracts.mjs +4 -1
  29. package/src/autonomous-policy.mjs +54 -7
  30. package/src/capability-ledger.mjs +1 -1
  31. package/src/capability-resolver.mjs +2 -1
  32. package/src/commander-compat-runtime.mjs +30 -2
  33. package/src/deadbyte-cli.mjs +722 -719
  34. package/src/launcher-isolation.mjs +58 -0
  35. package/src/native-diff-v2.mjs +18 -5
  36. package/src/native-diff.mjs +21 -5
  37. package/src/r70-owner-approval.mjs +78 -0
  38. package/src/remote-authority.mjs +75 -75
  39. package/src/remote-live-log.mjs +48 -14
  40. package/src/remote-mcp-machine-smoke-client.mjs +84 -84
  41. package/src/remote-result-journal.mjs +1 -1
  42. package/src/runtime-update.mjs +232 -24
  43. package/src/staging-input-isolation.mjs +79 -0
  44. package/src/tool-audit.mjs +2 -0
  45. package/src/version.mjs +1 -1
  46. package/test/autonomous-local-supervisor.test.mjs +2 -1
  47. package/test/autonomous-output-contract.test.mjs +17 -1
  48. package/test/autonomous-supervisor-integration.test.mjs +2 -1
  49. package/test/capability-resolver.test.mjs +17 -0
  50. package/test/codex-autonomous-r44.test.mjs +1 -1
  51. package/test/commander-compat-runtime.test.mjs +42 -0
  52. package/test/controller-policy-preflight-r59.test.mjs +1 -0
  53. package/test/deferred-slot-operation-behavior.test.mjs +2 -2
  54. package/test/native-diff-topology-r36.test.mjs +35 -2
  55. package/test/native-diff.test.mjs +83 -3
  56. package/test/r58-cleanup-retention.test.mjs +3 -3
  57. package/test/r58-concurrent-recovery.test.mjs +3 -3
  58. package/test/r58-stale-owner.test.mjs +1 -1
  59. package/test/r59-tool-surface-compat.test.mjs +9 -5
  60. package/test/r60-authority-coherence.test.mjs +10 -2
  61. package/test/r65-authority-runtime-epoch.test.mjs +9 -1
  62. package/test/r67-isolated-slot-rollback-restore.test.mjs +2 -1
  63. package/test/r67-npm-publish-gate.test.mjs +4 -2
  64. package/test/r67-release-contract.test.mjs +8 -7
  65. package/test/r69-live-outcome.test.mjs +137 -0
  66. package/test/r70-autonomous-grant-expiry.test.mjs +89 -0
  67. package/test/r70-autonomous-policy-bootstrap.test.mjs +166 -0
  68. package/test/r70-controller-autonomous-root.test.mjs +104 -0
  69. package/test/r70-controller-grant-subset.test.mjs +28 -0
  70. package/test/r70-controller-owner-issue-boundary.test.mjs +80 -0
  71. package/test/r70-controller-policy-snapshot.test.mjs +66 -0
  72. package/test/r70-owner-approval.test.mjs +67 -0
  73. package/test/r71-full-verifier-artifact.test.mjs +28 -0
  74. package/test/r71-isolated-slot-rollback-restore.test.mjs +62 -0
  75. package/test/r71-native-diff-mcp-wire.test.mjs +75 -0
  76. package/test/r71-publish-fence.test.mjs +38 -0
  77. package/test/r71-release-identity.test.mjs +33 -0
  78. package/test/r72-launcher-isolation.test.mjs +99 -0
  79. package/test/r72-publication-fence.test.mjs +56 -0
  80. package/test/r72-publication-output-integrity.test.mjs +110 -0
  81. package/test/r72-remote-safe-contract.test.mjs +101 -0
  82. package/test/r72-staging-input-isolation.test.mjs +87 -0
  83. package/test/r72-staging-workspace-isolation.test.mjs +114 -0
  84. package/test/remote-result-journal.test.mjs +12 -0
  85. package/test/remote-session-cli.test.mjs +112 -65
  86. package/test/standing-grants.test.mjs +2 -2
  87. package/test/tool-audit.test.mjs +14 -0
  88. package/bin/appcontainer-stage.obj +0 -0
  89. package/bin/bootstrap-advapi32.obj +0 -0
  90. package/bin/bootstrap-exitcode.obj +0 -0
  91. package/bin/bootstrap-kernel32.obj +0 -0
  92. package/bin/child-control-probe.obj +0 -0
  93. package/bin/child-control-stage.obj +0 -0
  94. package/bin/contained-reverse-worker.obj +0 -0
  95. package/bin/contained-transform-worker.obj +0 -0
  96. package/bin/containment-probe.obj +0 -0
  97. package/bin/deadbyte-contain.obj +0 -0
  98. package/bin/deadbyte-desktop-input-r40.obj +0 -0
  99. package/bin/deadbyte-desktop-observer-r40.obj +0 -0
  100. package/bin/deadbyte-desktop-uia-r40.obj +0 -0
  101. package/bin/deadbyte-exec.obj +0 -0
  102. package/bin/deadbyte-hardware-observer-r45.obj +0 -0
  103. package/bin/deadbyte-native-inspect-r67.obj +0 -0
  104. package/bin/deadbyte-process-host.obj +0 -0
  105. package/bin/deadbyte-tunnel-host.obj +0 -0
  106. package/conpty-input-observer.obj +0 -0
  107. package/miniconpty.obj +0 -0
  108. package/proof/CONTAINMENT-BUILD.txt +0 -163
  109. package/proof/MCP-SMOKE-STATUS.txt +0 -9
  110. package/proof/NODE-SYNTAX.txt +0 -4
  111. package/proof/PROOF.txt +0 -6
  112. package/proof/R34-TASK8-SUPERVISOR-CHECKPOINT.json +0 -29
  113. package/proof/R34-TASK9-MCP-SURFACE-CHECKPOINT.json +0 -28
  114. package/proof/SECRET-SCAN.txt +0 -4
  115. package/proof/SOURCE-GATES.txt +0 -16
  116. package/proof/STATUS.txt +0 -11
  117. package/proof/TARGET-V0313-CONTAINMENT-PASS.sha256 +0 -1
  118. package/proof/TARGET-V0313-CONTAINMENT-PASS.txt +0 -215
  119. package/proof/TARGET-V040-CONTAINED-IPC-FAIL.sha256 +0 -1
  120. package/proof/TARGET-V040-CONTAINED-IPC-FAIL.txt +0 -299
  121. package/proof/TARGET-V041-CONTAINED-CREATEPROCESS-203.sha256 +0 -1
  122. package/proof/TARGET-V041-CONTAINED-CREATEPROCESS-203.txt +0 -311
  123. package/proof/TARGET-V042-CONTAINED-CREATEPROCESS-203.sha256 +0 -1
  124. package/proof/TARGET-V042-CONTAINED-CREATEPROCESS-203.txt +0 -313
  125. package/proof/TARGET-V043-CONTAINED-CREATEPROCESS-203.sha256 +0 -1
  126. package/proof/TARGET-V043-CONTAINED-CREATEPROCESS-203.txt +0 -308
  127. package/proof/TARGET-V044-CONTAINED-PASS.sha256 +0 -1
  128. package/proof/TARGET-V044-CONTAINED-PASS.txt +0 -432
  129. package/proof/TARGET-V045-DUAL-OPERATION-PASS.sha256 +0 -1
  130. package/proof/TARGET-V045-DUAL-OPERATION-PASS.txt +0 -481
  131. package/proof/TARGET-V046-FIXED-EXECUTABLE-REGISTRY-PASS.sha256 +0 -1
  132. package/proof/TARGET-V046-FIXED-EXECUTABLE-REGISTRY-PASS.txt +0 -610
  133. package/proof/TARGET-V047-RUNTIME-MANIFEST-PASS.sha256 +0 -1
  134. package/proof/TARGET-V047-RUNTIME-MANIFEST-PASS.txt +0 -642
  135. package/proof/TARGET-V050-BATCH-GATE-PASS.sha256 +0 -1
  136. package/proof/TARGET-V050-BATCH-GATE-PASS.txt +0 -713
  137. package/proof/TESTS.txt +0 -686
  138. package/proof/TRUST-EPHEMERAL.txt +0 -4
  139. package/proof/V03-FREEZE.txt +0 -27
  140. package/proof/V041-IPC-HOTFIX.txt +0 -16
  141. package/proof/V042-ENV-HOTFIX.txt +0 -15
  142. package/proof/V043-DRIVE-CWD-HOTFIX.txt +0 -14
  143. package/proof/V044-LOCALAPPDATA-HOTFIX.txt +0 -18
  144. package/proof/V045-OPERATION-REGISTRY.txt +0 -7
  145. package/proof/V046-WORKER-REGISTRY.txt +0 -12
  146. package/proof/V047-WORKER-MANIFEST.txt +0 -18
  147. package/proof/V050-BATCH-TRUST-CLOSURE.txt +0 -21
  148. package/proof/V060-ACTIVE-NETWORK-AUTHORITY.txt +0 -29
  149. package/proof/V070-NODE-SYNTAX.txt +0 -36
  150. package/proof/V070-NODE-TESTS.txt +0 -752
  151. package/proof/V070-PACK-DRY-RUN.txt +0 -126
  152. package/proof/V070-SOURCE-GATE.txt +0 -30
  153. package/proof/V070-STATIC-SCAN.txt +0 -4
  154. package/proof/V070-WINDOWS-GATE-PENDING.txt +0 -25
  155. package/proof/V071-NODE-SYNTAX.txt +0 -1
  156. package/proof/V071-NODE-TESTS.txt +0 -796
  157. package/proof/V071-PACK-DRY-RUN.json +0 -656
  158. package/proof/V071-SOURCE-GATE.txt +0 -16
  159. package/proof/V071-WINDOWS-GATE-PENDING.txt +0 -24
  160. package/proof/V073-NODE-SYNTAX.txt +0 -4
  161. package/proof/V073-SOURCE-GATE.txt +0 -21
  162. package/proof/V073-STATIC-SCAN.txt +0 -10
  163. package/proof/V073-WINDOWS-GATE-PENDING.txt +0 -5
  164. package/proof/VERIFY.txt +0 -7
  165. package/proof/WINDOWS-CONTAINED-STATUS.txt +0 -5
  166. package/proof/WINDOWS-CONTAINMENT-STATUS.txt +0 -3
  167. package/proof/commander-closure-20260927/CONTINUATION.json +0 -43
  168. package/proof/commander-closure-20260927/RESULT.json +0 -30
  169. package/proof/commander-closure-20260927/SEARCH-CONTINUATION.json +0 -30
  170. package/proof/commander-closure-20260927/XLS-REFERENCE-PROBE.json +0 -13
  171. package/proof/commander-closure-20260928/DIFFERENTIAL-CHECKPOINT.json +0 -122
  172. package/proof/r59-full-regression-current.json +0 -13
  173. package/proof/r59-full-regression-current.stderr.log +0 -0
  174. package/proof/r59-full-regression-current.stdout.log +0 -1268
@@ -1,6 +1,6 @@
1
1
  import { createHash, createPrivateKey, createPublicKey, randomBytes, sign as cryptoSign, verify as cryptoVerify } from 'node:crypto';
2
2
  import { spawn, spawnSync } from 'node:child_process';
3
- import { existsSync } from 'node:fs';
3
+ import { existsSync, lstatSync, realpathSync } from 'node:fs';
4
4
  import { lstat, mkdir, open, readFile, readdir, realpath, rename, rm, writeFile } from 'node:fs/promises';
5
5
  import os from 'node:os';
6
6
  import path from 'node:path';
@@ -18,6 +18,9 @@ import {
18
18
  } from './release-slot.mjs';
19
19
  import { createGenerationBundle, verifyGenerationBundle } from './release-generation.mjs';
20
20
  import { resolveStateRoot } from './state-root.mjs';
21
+ import {canonicalLauncherPaths,assertCanonicalLaunchers,renderStateBoundLauncherCmd} from './launcher-isolation.mjs';
22
+ import {assertStageOwnedPath,assertStagingBootstrapInputs,assertStagingControlConfig} from './staging-input-isolation.mjs';
23
+ import { verifyProjectOwnerApproval } from './r70-owner-approval.mjs';
21
24
 
22
25
  const HEX64=/^[0-9a-f]{64}$/;
23
26
  const SEMVER=/^\d+\.\d+\.\d+(?:-[0-9A-Za-z.-]+)?(?:\+[0-9A-Za-z.-]+)?$/;
@@ -28,7 +31,6 @@ const FAILURE_SCHEMA='deadbyte.r27-slot-update-failure.v1';
28
31
  const ROLLBACK_SCHEMA='deadbyte.r31-slot-rollback-receipt.v1';
29
32
  const ROLLBACK_FAILURE_SCHEMA='deadbyte.r31-slot-rollback-failure.v1';
30
33
  const DEFAULT_STATE_ROOT=undefined;
31
- const TOP_LAUNCHER=path.join(os.homedir(),'Downloads','DEADBYTE-MCP.cmd');
32
34
  const RUNTIME_STABILITY_MS=12000;
33
35
  const RUNTIME_STABILITY_POLL_MS=500;
34
36
 
@@ -48,8 +50,8 @@ function normalizePersistentPublicOrigin(value){
48
50
  return publicOrigin.origin;
49
51
  }
50
52
  export function parseUpdateArgs(argv){
51
- 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,desktopPolicyBootstrap:null,semanticLspBootstrap:null,hostCommandPins:[]};
52
- const cloud={},bridge={},processPolicy={},hostPolicy={},codingPolicy={},desktopPolicy={},semanticLsp={},seen=new Set(),hostPinIds=new Set();
53
+ 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:[]};
54
+ const cloud={},bridge={},processPolicy={},hostPolicy={},codingPolicy={},autonomousPolicy={},ownerApproval={},desktopPolicy={},semanticLsp={},seen=new Set(),hostPinIds=new Set();
53
55
  const bridgeFlags=new Map([
54
56
  ['--bridge-root','root'],['--bridge-script-sha256','scriptSha256']
55
57
  ]);
@@ -62,6 +64,12 @@ export function parseUpdateArgs(argv){
62
64
  const codingPolicyFlags=new Map([
63
65
  ['--coding-policy','path'],['--coding-policy-sha256','sha256']
64
66
  ]);
67
+ const autonomousPolicyFlags=new Map([
68
+ ['--autonomous-policy','path'],['--autonomous-policy-sha256','sha256']
69
+ ]);
70
+ const ownerApprovalFlags=new Map([
71
+ ['--owner-approval','path'],['--owner-approval-sha256','sha256']
72
+ ]);
65
73
  const desktopPolicyFlags=new Map([
66
74
  ['--desktop-policy','path'],['--desktop-policy-sha256','sha256']
67
75
  ]);
@@ -73,7 +81,7 @@ export function parseUpdateArgs(argv){
73
81
  ['--cloud-token-file','tokenFilePath'],['--cloud-token-sha256','tokenFileSha256'],
74
82
  ['--cloud-child-working-directory','childWorkingDirectory'],['--cloud-public-origin','publicOrigin']
75
83
  ]);
76
- const valued=new Set(['--stage','--development-root','--expected-version','--expected-manifest','--source-root','--host-command-pin',...bridgeFlags.keys(),...processPolicyFlags.keys(),...hostPolicyFlags.keys(),...codingPolicyFlags.keys(),...desktopPolicyFlags.keys(),...semanticLspFlags.keys(),...cloudFlags.keys()]);
84
+ 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()]);
77
85
  for(let index=0;index<argv.length;index++){
78
86
  const arg=argv[index];
79
87
  if(arg==='--restore-standing-grants'||arg==='--recover-offline-predecessor'||arg==='--enable-machine-fs'||arg==='--enable-observation'||arg==='--enable-desktop'||arg==='--enable-autonomous-supervisor'){
@@ -110,6 +118,8 @@ export function parseUpdateArgs(argv){
110
118
  else if(processPolicyFlags.has(arg)) processPolicy[processPolicyFlags.get(arg)]=value;
111
119
  else if(hostPolicyFlags.has(arg)) hostPolicy[hostPolicyFlags.get(arg)]=value;
112
120
  else if(codingPolicyFlags.has(arg)) codingPolicy[codingPolicyFlags.get(arg)]=value;
121
+ else if(autonomousPolicyFlags.has(arg)) autonomousPolicy[autonomousPolicyFlags.get(arg)]=value;
122
+ else if(ownerApprovalFlags.has(arg)) ownerApproval[ownerApprovalFlags.get(arg)]=value;
113
123
  else if(desktopPolicyFlags.has(arg)) desktopPolicy[desktopPolicyFlags.get(arg)]=value;
114
124
  else if(semanticLspFlags.has(arg)) semanticLsp[semanticLspFlags.get(arg)]=value;
115
125
  else cloud[cloudFlags.get(arg)]=value;
@@ -146,6 +156,21 @@ export function parseUpdateArgs(argv){
146
156
  if(!HEX64.test(sha256)) throw new Error('coding policy SHA-256 must be lowercase hex');
147
157
  out.codingPolicyBootstrap={path:path.resolve(codingPolicy.path),sha256};
148
158
  }
159
+ const autonomousPolicyFields=[...autonomousPolicyFlags.values()],autonomousPolicyPresent=autonomousPolicyFields.filter(key=>autonomousPolicy[key]!==undefined);
160
+ if(autonomousPolicyPresent.length!==0&&autonomousPolicyPresent.length!==autonomousPolicyFields.length) throw new Error('autonomous policy bootstrap requires path plus SHA-256 as one pair');
161
+ if(autonomousPolicyPresent.length===autonomousPolicyFields.length){
162
+ const sha256=String(autonomousPolicy.sha256).toLowerCase();
163
+ if(!HEX64.test(sha256)) throw new Error('autonomous policy SHA-256 must be lowercase hex');
164
+ out.autonomousPolicyBootstrap={path:path.resolve(autonomousPolicy.path),sha256};
165
+ }
166
+ const ownerApprovalFields=[...ownerApprovalFlags.values()],ownerApprovalPresent=ownerApprovalFields.filter(key=>ownerApproval[key]!==undefined);
167
+ if(ownerApprovalPresent.length!==0&&ownerApprovalPresent.length!==ownerApprovalFields.length) throw new Error('owner approval bootstrap requires path plus SHA-256 as one pair');
168
+ if(ownerApprovalPresent.length===ownerApprovalFields.length){
169
+ const sha256=String(ownerApproval.sha256).toLowerCase();
170
+ if(!HEX64.test(sha256)) throw new Error('owner approval SHA-256 must be lowercase hex');
171
+ out.ownerApprovalBootstrap={path:path.resolve(ownerApproval.path),sha256};
172
+ }
173
+ if(Boolean(out.autonomousPolicyBootstrap)!==Boolean(out.ownerApprovalBootstrap)) throw new Error('autonomous policy bootstrap requires pinned owner approval, and owner approval requires autonomous policy');
149
174
  const desktopPolicyFields=[...desktopPolicyFlags.values()],desktopPolicyPresent=desktopPolicyFields.filter(key=>desktopPolicy[key]!==undefined);
150
175
  if(desktopPolicyPresent.length!==0&&desktopPolicyPresent.length!==desktopPolicyFields.length) throw new Error('desktop policy bootstrap requires path plus SHA-256 as one pair');
151
176
  if(desktopPolicyPresent.length===desktopPolicyFields.length){
@@ -446,6 +471,21 @@ async function verifyPinnedRegularFile(file,expected,label){
446
471
  return {path:absolute,sha256:actual};
447
472
  }
448
473
 
474
+ export async function readPinnedJsonPolicy(bootstrap,label,readBytes=readFile){
475
+ const file=bootstrap?.path,expected=bootstrap?.sha256;
476
+ if(typeof file!=='string'||!path.isAbsolute(file)||!HEX64.test(String(expected??''))) throw new Error(`${label} pin contract invalid`);
477
+ const absolute=path.resolve(file),stat=await lstat(absolute);
478
+ if(!stat.isFile()||stat.isSymbolicLink()) throw new Error(`${label} must be a regular file`);
479
+ const bytes=await readBytes(absolute);
480
+ if(!Buffer.isBuffer(bytes)) throw new Error(`${label} read must return bytes`);
481
+ const actual=sha256Bytes(bytes);
482
+ if(actual!==expected) throw new Error(`${label} SHA-256 mismatch: expected ${expected}, found ${actual}`);
483
+ let policy;
484
+ try{policy=JSON.parse(bytes.toString('utf8'));}
485
+ catch{throw new Error(`${label} JSON invalid`);}
486
+ return {path:absolute,sha256:actual,policy};
487
+ }
488
+
449
489
  export async function prepareProcessPolicyBootstrap(bootstrap){
450
490
  if(!bootstrap) return null;
451
491
  const pinned=await verifyPinnedRegularFile(bootstrap.path,bootstrap.sha256,'process policy');
@@ -470,8 +510,7 @@ export async function prepareHostPolicyBootstrap(bootstrap){
470
510
 
471
511
  export async function prepareCodingPolicyBootstrap(bootstrap){
472
512
  if(!bootstrap) return null;
473
- const pinned=await verifyPinnedRegularFile(bootstrap.path,bootstrap.sha256,'coding policy');
474
- const policy=await readJson(pinned.path);
513
+ const {policy,...pinned}=await readPinnedJsonPolicy(bootstrap,'coding policy');
475
514
  if(policy?.schema!=='deadbyte.coding-policy.v1'||policy?.enabled!==true||
476
515
  !policy?.roots||typeof policy.roots!=='object'||Array.isArray(policy.roots)||
477
516
  !policy?.commands||typeof policy.commands!=='object'||Array.isArray(policy.commands)) {
@@ -480,6 +519,110 @@ export async function prepareCodingPolicyBootstrap(bootstrap){
480
519
  return {...pinned,policy};
481
520
  }
482
521
 
522
+ export async function prepareAutonomousPolicyBootstrap(bootstrap){
523
+ if(!bootstrap) return null;
524
+ const {policy,...pinned}=await readPinnedJsonPolicy(bootstrap,'autonomous policy');
525
+ if(policy?.schema!=='deadbyte.autonomous-policy.v1'||policy?.enabled!==true||
526
+ !HEX64.test(String(policy.coding_policy_sha256??''))||
527
+ !Array.isArray(policy.root_ids)||policy.root_ids.length<1||
528
+ !policy.profiles||typeof policy.profiles!=='object'||Array.isArray(policy.profiles)) {
529
+ throw new Error('autonomous policy schema/enabled/coding digest/roots/profiles mismatch');
530
+ }
531
+ return {...pinned,policy};
532
+ }
533
+
534
+ export function assertAutonomousBootstrapRoots(autonomousPolicy,codingPolicy,selectedCodingSha256=null){
535
+ const roots=autonomousPolicy?.root_ids;
536
+ for(const codingRoot of Object.values(codingPolicy?.roots??{})){
537
+ const candidate=codingRoot?.path;
538
+ if((codingRoot?.read===true||codingRoot?.write===true||codingRoot?.exec===true)&&
539
+ typeof candidate==='string'&&path.isAbsolute(candidate)){
540
+ const resolved=path.resolve(candidate),filesystemRoot=path.parse(resolved).root;
541
+ const normalize=value=>process.platform==='win32'?value.toLowerCase():value;
542
+ if(normalize(resolved)===normalize(filesystemRoot)) throw new Error('coding policy contains a broad filesystem root');
543
+ }
544
+ }
545
+ if(!Array.isArray(roots)||roots.length<1||roots.some(id=>typeof id!=='string'||
546
+ !/^[A-Za-z0-9._-]{1,64}$/.test(id)||!Object.hasOwn(codingPolicy?.roots??{},id))) {
547
+ throw new Error('autonomous bootstrap root is outside selected coding policy');
548
+ }
549
+ const profiles=autonomousPolicy?.profiles??{};
550
+ if(!profiles||typeof profiles!=='object'||Array.isArray(profiles)) throw new Error('autonomous bootstrap profiles invalid');
551
+ for(const [id,profile] of Object.entries(profiles)){
552
+ if(typeof profile?.root_id!=='string'||!roots.includes(profile.root_id)){
553
+ throw new Error(`autonomous bootstrap profile '${id}' root outside autonomous roots`);
554
+ }
555
+ }
556
+ if(selectedCodingSha256!==null&&autonomousPolicy.coding_policy_sha256!==selectedCodingSha256) {
557
+ throw new Error('autonomous bootstrap coding policy SHA-256 mismatch');
558
+ }
559
+ for(const id of roots){
560
+ const rootPath=codingPolicy.roots[id]?.path;
561
+ if(typeof rootPath==='string'&&path.isAbsolute(rootPath)){
562
+ const resolved=path.resolve(rootPath),filesystemRoot=path.parse(resolved).root;
563
+ const normalize=value=>process.platform==='win32'?value.toLowerCase():value;
564
+ if(normalize(resolved)===normalize(filesystemRoot)) throw new Error('autonomous bootstrap requires a project-scoped root');
565
+ }
566
+ try{
567
+ if(typeof rootPath!=='string'||!path.isAbsolute(rootPath)) throw new Error('path invalid');
568
+ const stat=lstatSync(rootPath);
569
+ if(!stat.isDirectory()||stat.isSymbolicLink()) throw new Error('not a regular directory');
570
+ const resolved=path.resolve(rootPath),real=realpathSync(rootPath);
571
+ const normalize=value=>process.platform==='win32'?value.toLowerCase():value;
572
+ if(normalize(resolved)!==normalize(path.resolve(real))) throw new Error('path indirection');
573
+ }catch{ throw new Error(`autonomous bootstrap root '${id}' must be an existing real directory`); }
574
+ }
575
+ return roots;
576
+ }
577
+
578
+ // R70: Admission check for successor POLICY BOOTSTRAP only. This does not
579
+ // grant authority or establish the owner trust anchor by itself.
580
+ export async function verifyAutonomousOwnerBootstrap({
581
+ stateRoot,bootstrap,autonomousPolicy,codingPolicy,
582
+ codingPolicySha256,autonomousPolicySha256,expectedManifest,now=new Date()
583
+ }={}){
584
+ if(!bootstrap) throw new Error('owner approval bootstrap is required');
585
+ if(typeof stateRoot!=='string'||!path.isAbsolute(stateRoot)) throw new Error('owner approval state root invalid');
586
+ if(!HEX64.test(String(codingPolicySha256??''))||
587
+ !HEX64.test(String(autonomousPolicySha256??''))||
588
+ !HEX64.test(String(expectedManifest??''))) throw new Error('owner approval digest inputs invalid');
589
+ const ids=assertAutonomousBootstrapRoots(autonomousPolicy,codingPolicy,codingPolicySha256);
590
+ if(ids.length!==1) throw new Error('owner approval requires exactly one project-scoped autonomous root');
591
+ const rootId=ids[0];
592
+ const projectRoot=codingPolicy.roots[rootId].path;
593
+ const trustRoot=path.resolve(stateRoot,'trust');
594
+ const ownerKeyPath=path.join(trustRoot,'r70-owner-public.pem');
595
+ const normalize=p=>process.platform==='win32'?path.resolve(p).toLowerCase():path.resolve(p);
596
+ const under=(file,root)=>{
597
+ const relative=path.relative(normalize(root),normalize(file));
598
+ return relative===''||(!relative.startsWith('..'+path.sep)&&relative!=='..'&&!path.isAbsolute(relative));
599
+ };
600
+ // Coding commands execute as the owner OS principal, not inside a
601
+ // filesystem sandbox. Project-scoped write checks cannot protect a trust
602
+ // anchor while any selected Coding root can execute arbitrary scripts.
603
+ for(const root of Object.values(codingPolicy.roots)){
604
+ if(root?.exec===true) throw new Error('owner approval cannot trust an anchor with unsandboxed Coding execution');
605
+ if(root?.write===true&&typeof root.path==='string'&&path.isAbsolute(root.path)&&
606
+ under(ownerKeyPath,root.path)) throw new Error('owner trust key is writable by Coding policy');
607
+ }
608
+ const trustStat=await lstat(trustRoot);
609
+ const keyStat=await lstat(ownerKeyPath);
610
+ if(!trustStat.isDirectory()||trustStat.isSymbolicLink()||
611
+ !keyStat.isFile()||keyStat.isSymbolicLink()||
612
+ normalize(await realpath(trustRoot))!==normalize(trustRoot)||
613
+ normalize(await realpath(ownerKeyPath))!==normalize(ownerKeyPath)){
614
+ throw new Error('owner trust key must be a regular file below a real trust directory');
615
+ }
616
+ const ownerPublicKey=await readFile(ownerKeyPath);
617
+ const pinned=await readPinnedJsonPolicy(bootstrap,'owner approval');
618
+ return verifyProjectOwnerApproval({
619
+ approval:pinned.policy,ownerPublicKey,
620
+ expected:{rootId,projectRoot,codingPolicySha256,autonomousPolicySha256,
621
+ reviewedArtifactSha256:expectedManifest},
622
+ now
623
+ });
624
+ }
625
+
483
626
  export async function prepareDesktopPolicyBootstrap(bootstrap){
484
627
  if(!bootstrap) return null;
485
628
  const pinned=await verifyPinnedRegularFile(bootstrap.path,bootstrap.sha256,'desktop policy');
@@ -1000,7 +1143,9 @@ async function loadControlInputs({stateRoot,sourceRoot,pointerInfo,bridgeBootstr
1000
1143
  if(!sourceRoot) throw new Error('--source-root is required for initial slot migration');
1001
1144
  ctx=legacyContext(sourceRoot); configPath=path.join(ctx.source_root,'controller','deadbyte-controller.config.psd1');
1002
1145
  }
1146
+ assertStageOwnedPath(configPath,stateRoot,'controller config file');
1003
1147
  let config=await loadPowerShellDataFile(configPath);
1148
+ assertStagingControlConfig(config,stateRoot,{configPath});
1004
1149
  if(bridgeBootstrap){
1005
1150
  if(!bridgeBootstrap.root||!path.isAbsolute(bridgeBootstrap.root)||!HEX64.test(String(bridgeBootstrap.scriptSha256??''))) throw new Error('bridge bootstrap requires absolute root plus lowercase script SHA-256');
1006
1151
  const bootstrapRoot=path.resolve(bridgeBootstrap.root);
@@ -1030,7 +1175,33 @@ export function assertDevelopmentRootSafe(developmentRoot,stateRoot){
1030
1175
  if(forbidden.some(prefix=>lower===prefix||lower.startsWith(`${prefix}${path.sep}`))) throw new Error('development root cannot live inside immutable/runtime state stores');
1031
1176
  return dev;
1032
1177
  }
1033
- async function workspacePlan(currentWorkspace,stateRoot){
1178
+ function assertStageWorkspaceBoundary(currentWorkspace,stateRoot){
1179
+ const state=path.resolve(stateRoot),source=path.resolve(currentWorkspace);
1180
+ const production=path.resolve(os.homedir(),'.deadbyte-mcp');
1181
+ const normalized=file=>process.platform==='win32'?file.toLowerCase():file;
1182
+ if(normalized(state)===normalized(production)) return;
1183
+ // Fail closed before staging can plan, mutate or roll back a workspace migration.
1184
+ canonicalLauncherPaths(state);
1185
+ const within=(parent,child)=>{
1186
+ const relative=path.relative(normalized(parent),normalized(child));
1187
+ return relative===''||(!path.isAbsolute(relative)&&relative!=='..'&&!relative.startsWith('..'+path.sep));
1188
+ };
1189
+ if(!within(state,source)) throw new Error('STAGING_WORKSPACE_OUTSIDE_STATE');
1190
+ if(normalized(state)===normalized(source)) throw new Error('STAGING_WORKSPACE_STATE_ROOT');
1191
+ const forbidden=['releases','generations','runtime','bin','launchers','trust','evidence','controller'];
1192
+ if(forbidden.some(child=>within(path.join(state,child),source))) throw new Error('STAGING_WORKSPACE_RESERVED_STATE');
1193
+ // Reject reparse points on either path, even when final leaf is absent.
1194
+ for(const file of [source,path.join(state,'workspaces')]){
1195
+ let cursor=file;
1196
+ for(;;){
1197
+ try {if(lstatSync(cursor).isSymbolicLink())throw new Error('STAGING_WORKSPACE_REPARSE');}
1198
+ catch(error){if(error?.code!=='ENOENT')throw error;}
1199
+ const parent=path.dirname(cursor);if(parent===cursor)break;cursor=parent;
1200
+ }
1201
+ }
1202
+ }
1203
+ export async function workspacePlan(currentWorkspace,stateRoot){
1204
+ assertStageWorkspaceBoundary(currentWorkspace,stateRoot);
1034
1205
  const source=path.resolve(currentWorkspace),target=path.join(path.resolve(stateRoot),'workspaces');
1035
1206
  if(source.toLowerCase()===path.resolve(target).toLowerCase()) return {source,target,mode:'existing',moved:false};
1036
1207
  const sourceExists=existsSync(source),targetExists=existsSync(target);
@@ -1044,7 +1215,8 @@ async function workspacePlan(currentWorkspace,stateRoot){
1044
1215
  if(path.parse(source).root.toLowerCase()!==path.parse(target).root.toLowerCase()) throw new Error('workspace migration requires same-volume rename');
1045
1216
  return {source,target,mode:'rename',moved:false};
1046
1217
  }
1047
- async function applyWorkspacePlan(plan){
1218
+ export async function applyWorkspacePlan(plan){
1219
+ assertStageWorkspaceBoundary(plan.source,path.dirname(plan.target));
1048
1220
  if(plan.mode==='existing'||plan.mode==='target-existing') return plan;
1049
1221
  const sourceExists=existsSync(plan.source),targetExists=existsSync(plan.target);
1050
1222
  if(plan.mode==='create'){
@@ -1057,29 +1229,41 @@ async function applyWorkspacePlan(plan){
1057
1229
  await mkdir(path.dirname(plan.target),{recursive:true});
1058
1230
  await rename(plan.source,plan.target); plan.moved=true; return plan;
1059
1231
  }
1060
- async function rollbackWorkspacePlan(plan){
1232
+ export async function rollbackWorkspacePlan(plan){
1233
+ assertStageWorkspaceBoundary(plan.source,path.dirname(plan.target));
1061
1234
  if(plan.mode==='rename'&&existsSync(plan.target)&&!existsSync(plan.source)){ await rename(plan.target,plan.source); plan.moved=false; return true; }
1062
1235
  if(plan.mode==='create'&&existsSync(plan.target)){ await rm(plan.target,{recursive:true,force:true}); plan.created=false; return true; }
1063
1236
  return false;
1064
1237
  }
1065
1238
  async function launcherPlan(stateRoot){
1066
- const bin=path.join(path.resolve(stateRoot),'bin');
1067
- const ps1=path.join(bin,'DEADBYTE-MCP.ps1'),cmd=path.join(bin,'DEADBYTE-MCP.cmd'),top=TOP_LAUNCHER;
1068
- const ps1Bytes=Buffer.from(renderStableLauncherPs1(),'utf8'),cmdBytes=Buffer.from(renderStableLauncherCmd(),'utf8');
1069
- return {ps1,cmd,top,ps1Bytes,cmdBytes,launcher_sha256:sha256Bytes(ps1Bytes),
1239
+ const target=canonicalLauncherPaths(stateRoot);
1240
+ const {state,ps1,cmd,top}=target;
1241
+ const ps1Bytes=Buffer.from(renderStableLauncherPs1(),'utf8');
1242
+ const cmdBytes=Buffer.from(target.production?renderStableLauncherCmd():renderStateBoundLauncherCmd(),'utf8');
1243
+ return {stateRoot:state,ps1,cmd,top,ps1Bytes,cmdBytes,launcher_sha256:sha256Bytes(ps1Bytes),
1070
1244
  snapshots:{ps1:await snapshotFile(ps1),cmd:await snapshotFile(cmd),top:await snapshotFile(top)},installed:false};
1071
1245
  }
1072
1246
  async function installLaunchers(plan){
1073
- await atomicBytes(plan.ps1,plan.ps1Bytes); await atomicBytes(plan.cmd,plan.cmdBytes); await atomicBytes(plan.top,plan.cmdBytes);
1247
+ assertCanonicalLaunchers(plan.stateRoot,plan);
1248
+ await atomicBytes(plan.ps1,plan.ps1Bytes);
1249
+ assertCanonicalLaunchers(plan.stateRoot,plan);
1250
+ await atomicBytes(plan.cmd,plan.cmdBytes);
1251
+ assertCanonicalLaunchers(plan.stateRoot,plan);
1252
+ await atomicBytes(plan.top,plan.cmdBytes);
1074
1253
  plan.installed=true;
1075
1254
  if(await sha256File(plan.ps1)!==plan.launcher_sha256) throw new Error('stable launcher post-install hash mismatch');
1076
1255
  return plan;
1077
1256
  }
1078
1257
  async function rollbackLaunchers(plan){
1079
1258
  if(!plan) return false;
1259
+ assertCanonicalLaunchers(plan.stateRoot,plan);
1080
1260
  const touched=plan.installed===true || (existsSync(plan.ps1)&&sha256Bytes(await readFile(plan.ps1))===plan.launcher_sha256);
1081
1261
  if(!touched) return false;
1082
- await restoreFile(plan.ps1,plan.snapshots.ps1); await restoreFile(plan.cmd,plan.snapshots.cmd); await restoreFile(plan.top,plan.snapshots.top);
1262
+ await restoreFile(plan.ps1,plan.snapshots.ps1);
1263
+ assertCanonicalLaunchers(plan.stateRoot,plan);
1264
+ await restoreFile(plan.cmd,plan.snapshots.cmd);
1265
+ assertCanonicalLaunchers(plan.stateRoot,plan);
1266
+ await restoreFile(plan.top,plan.snapshots.top);
1083
1267
  plan.installed=false; return true;
1084
1268
  }
1085
1269
  async function revokeAuthority(ctx,prior){
@@ -1205,9 +1389,10 @@ export function rollbackLauncherPlanFromHandoff(body,priorPointer,activePointer,
1205
1389
  if(!samePublicPointer(handoffPrior,priorPointer)||!samePublicPointer(handoffActive,activePointer)) throw new Error('rollback handoff pointer binding mismatch');
1206
1390
  const state=path.resolve(stateRoot);
1207
1391
  const launchers={
1392
+ stateRoot:state,
1208
1393
  ps1:path.resolve(String(body.launchers?.ps1??'')),
1209
1394
  cmd:path.resolve(String(body.launchers?.cmd??'')),
1210
- top:path.resolve(String(body.launchers?.top??TOP_LAUNCHER)),
1395
+ top:path.resolve(String(body.launchers?.top??'')),
1211
1396
  launcher_sha256:String(body.launchers?.launcher_sha256??''),
1212
1397
  ps1Bytes:Buffer.from(String(body.launchers?.ps1_bytes??''),'base64'),
1213
1398
  cmdBytes:Buffer.from(String(body.launchers?.cmd_bytes??''),'base64'),
@@ -1218,17 +1403,18 @@ export function rollbackLauncherPlanFromHandoff(body,priorPointer,activePointer,
1218
1403
  },
1219
1404
  installed:true
1220
1405
  };
1221
- if(launchers.ps1!==path.resolve(launcherPath(state))||
1222
- launchers.cmd!==path.resolve(path.join(state,'bin','DEADBYTE-MCP.cmd'))||
1223
- launchers.top!==path.resolve(TOP_LAUNCHER)) throw new Error('rollback launcher paths are not canonical');
1406
+ assertCanonicalLaunchers(state,launchers);
1224
1407
  if(!HEX64.test(launchers.launcher_sha256)||sha256Bytes(launchers.ps1Bytes)!==launchers.launcher_sha256) throw new Error('rollback successor launcher bytes/hash mismatch');
1225
1408
  if(launchers.launcher_sha256!==activePointer.launcher_sha256) throw new Error('rollback successor launcher hash does not match active pointer');
1226
1409
  if(!launchers.snapshots.ps1.exists||launchers.snapshots.ps1.sha256!==priorPointer.launcher_sha256) throw new Error('rollback predecessor launcher snapshot does not match signed prior pointer');
1227
1410
  return launchers;
1228
1411
  }
1229
1412
  async function restoreRollbackLaunchers(plan){
1413
+ assertCanonicalLaunchers(plan.stateRoot,plan);
1230
1414
  await restoreFile(plan.ps1,plan.snapshots.ps1);
1415
+ assertCanonicalLaunchers(plan.stateRoot,plan);
1231
1416
  await restoreFile(plan.cmd,plan.snapshots.cmd);
1417
+ assertCanonicalLaunchers(plan.stateRoot,plan);
1232
1418
  await restoreFile(plan.top,plan.snapshots.top);
1233
1419
  if(await sha256File(plan.ps1)!==plan.snapshots.ps1.sha256) throw new Error('rollback predecessor launcher restore hash mismatch');
1234
1420
  return plan;
@@ -1442,6 +1628,7 @@ async function resumeSlotUpdate({handoffPath}){
1442
1628
  ...verifiedReceipt,historical_receipt_verified:true,current_runtime_verified:false};
1443
1629
  }
1444
1630
  if(phase.startsWith('failed')) throw new Error('slot update already failed at phase='+phase);
1631
+ assertCanonicalLaunchers(stateRoot,body.launchers);
1445
1632
  const priorPointer=handoffPriorPointer(body),candidatePointer=handoffNewPointer(body);
1446
1633
  const statePrior=statePointer(releaseState,'prior_pointer'),stateCandidate=statePointer(releaseState,'new_pointer');
1447
1634
  if(!samePublicPointer(priorPointer,statePrior)||!samePublicPointer(candidatePointer,stateCandidate)) throw new Error('handoff pointer binding mismatch');
@@ -1459,7 +1646,8 @@ async function resumeSlotUpdate({handoffPath}){
1459
1646
  const oldRuntime=body.old_runtime?{bridge:body.old_runtime.bridge??null,cloud:body.old_runtime.cloud??null,core:body.old_runtime.core??null}:null;
1460
1647
  if(oldRuntime?.core&&(!Number.isInteger(Number(oldRuntime.core.pid))||String(oldRuntime.core.creation_date??'').length<1)) throw new Error('handoff old runtime core identity invalid');
1461
1648
  const launchers={
1462
- ps1:path.resolve(String(body.launchers?.ps1??'')),cmd:path.resolve(String(body.launchers?.cmd??'')),top:path.resolve(String(body.launchers?.top??TOP_LAUNCHER)),
1649
+ stateRoot,
1650
+ ps1:path.resolve(String(body.launchers?.ps1??'')),cmd:path.resolve(String(body.launchers?.cmd??'')),top:path.resolve(String(body.launchers?.top??'')),
1463
1651
  launcher_sha256:String(body.launchers?.launcher_sha256??''),
1464
1652
  ps1Bytes:Buffer.from(String(body.launchers?.ps1_bytes??''),'base64'),cmdBytes:Buffer.from(String(body.launchers?.cmd_bytes??''),'base64'),
1465
1653
  snapshots:{
@@ -1467,6 +1655,7 @@ async function resumeSlotUpdate({handoffPath}){
1467
1655
  },
1468
1656
  installed:phaseRank(phase)>=phaseRank('launcher_installed')
1469
1657
  };
1658
+ assertCanonicalLaunchers(stateRoot,launchers);
1470
1659
  if(!HEX64.test(launchers.launcher_sha256)||sha256Bytes(launchers.ps1Bytes)!==launchers.launcher_sha256) throw new Error('handoff launcher bytes/hash mismatch');
1471
1660
  const workspace={
1472
1661
  source:path.resolve(String(body.workspace?.source??releaseState.workspace?.source??'')),
@@ -1657,13 +1846,15 @@ export async function recoverInterruptedSlotUpdates({stateRoot=DEFAULT_STATE_ROO
1657
1846
  return {ok:true,launched,updates};
1658
1847
  }
1659
1848
 
1660
- 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,desktopPolicyBootstrap=null,semanticLspBootstrap=null,enableMachineFs=false,enableObservation=false,enableDesktop=false,enableAutonomousSupervisor=false,hostCommandPins=[]}){
1849
+ 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=[]}){
1661
1850
  // R64 grants have no signed revocation epoch. The historical boolean restore
1662
1851
  // cannot prove continuity, and must never materialize a successor grant.
1663
1852
  if(restoreStandingGrants)throw new Error('R65 standing grant auto-restore disabled: explicit reauthorization required');
1853
+ if(Boolean(autonomousPolicyBootstrap)!==Boolean(ownerApprovalBootstrap)) throw new Error('autonomous policy bootstrap requires pinned owner approval');
1664
1854
  stateRoot=resolveStateRoot(stateRoot);
1665
1855
  if(process.platform!=='win32') throw new Error('R27 slot update is Windows-only');
1666
1856
  const state=path.resolve(stateRoot),stageRoot=path.resolve(stage),development=assertDevelopmentRootSafe(developmentRoot,state);
1857
+ assertStagingBootstrapInputs({stateRoot:state,sourceRoot,bridgeBootstrap,cloudTransport,processPolicyBootstrap,hostPolicyBootstrap,codingPolicyBootstrap,autonomousPolicyBootstrap,ownerApprovalBootstrap,desktopPolicyBootstrap,semanticLspBootstrap});
1667
1858
  const key=await loadUpdateKey(state),updateId='r27-'+expectedManifest.slice(0,16)+'-'+Date.now().toString(36)+'-'+randomBytes(4).toString('hex');
1668
1859
  const evidenceDir=path.join(state,'evidence','updates',updateId),statePath=path.join(evidenceDir,'state.json'),failurePath=path.join(evidenceDir,'failure-receipt.json');
1669
1860
  await mkdir(evidenceDir,{recursive:true});
@@ -1679,13 +1870,27 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
1679
1870
  const stageVerified=await verifyPackageRoot(stageRoot,expectedVersion,expectedManifest,'stage');
1680
1871
  const developmentVerified=await verifyPackageRoot(development,expectedVersion,expectedManifest,'development');
1681
1872
  control=await loadControlInputs({stateRoot:state,sourceRoot,pointerInfo,bridgeBootstrap});
1873
+ const codingBootstrap=await prepareCodingPolicyBootstrap(codingPolicyBootstrap);
1874
+ const autonomousBootstrap=await prepareAutonomousPolicyBootstrap(autonomousPolicyBootstrap);
1875
+ let ownerApprovalProof=null;
1876
+ if(autonomousBootstrap){
1877
+ if(hostCommandPins.length>0) throw new Error('autonomous bootstrap cannot combine with command repinning');
1878
+ const selectedCoding=codingBootstrap?.policy??control.codingPolicy;
1879
+ const selectedCodingSha256=codingBootstrap?.sha256??await sha256File(control.config.CodingPolicyFile);
1880
+ ownerApprovalProof=await verifyAutonomousOwnerBootstrap({
1881
+ stateRoot:state,bootstrap:ownerApprovalBootstrap,autonomousPolicy:autonomousBootstrap.policy,
1882
+ codingPolicy:selectedCoding,codingPolicySha256:selectedCodingSha256,
1883
+ autonomousPolicySha256:autonomousBootstrap.sha256,expectedManifest
1884
+ });
1885
+ }
1682
1886
  // Read-only predecessor authority and process identity must be safe before
1683
1887
  // materializing a slot or changing any stable runtime file.
1684
1888
  await capturePredecessor(control.ctx,recoverOfflinePredecessor,{allowHostDisarm:false});
1685
1889
  mutationBegan=true;
1686
1890
  const slot=await materializeReleaseSlot({source:stageRoot,stateRoot:state,version:expectedVersion,manifestSha256:expectedManifest});
1687
1891
  const hostBootstrap=await prepareHostPolicyBootstrap(hostPolicyBootstrap);if(hostBootstrap) control.hostPolicy=hostBootstrap.policy;
1688
- const codingBootstrap=await prepareCodingPolicyBootstrap(codingPolicyBootstrap);if(codingBootstrap) control.codingPolicy=codingBootstrap.policy;
1892
+ if(codingBootstrap) control.codingPolicy=codingBootstrap.policy;
1893
+ if(autonomousBootstrap) control.autonomousPolicy=autonomousBootstrap.policy;
1689
1894
  const processBootstrap=await prepareProcessPolicyBootstrap(processPolicyBootstrap);
1690
1895
  if(processBootstrap){ control.processPolicy=processBootstrap.policy;control.config.ProcessRuntimeEnabled=true; }
1691
1896
  const desktopBootstrap=await prepareDesktopPolicyBootstrap(desktopPolicyBootstrap);if(desktopBootstrap) control.desktopPolicy=desktopBootstrap.policy;
@@ -1730,6 +1935,8 @@ export async function performSlotUpdate({stage,developmentRoot,expectedVersion,e
1730
1935
  stable_mcp_url:stableMcpUrl?{path:stableMcpUrl.path,sha256:stableMcpUrl.sha256,origin:stableMcpUrl.origin,changed:stableMcpUrl.changed}:null,
1731
1936
  bridge:{working_directory:control.config.Bridge.WorkingDirectory,script_path:control.config.Bridge.ScriptPath,script_sha256:control.config.Bridge.ScriptSha256},
1732
1937
  host_repin:{requested:hostCommandPins.length>0,repinned:authorityPreparation.hostRepinned},coding_repin:{requested:hostCommandPins.length>0,repinned:authorityPreparation.codingRepinned},
1938
+ owner_approval:ownerApprovalProof?{subject_sha256:ownerApprovalProof.subject_sha256,owner_key_id:ownerApprovalProof.owner_key_id,
1939
+ root_id:ownerApprovalProof.root_id,scope:ownerApprovalProof.scope,expires_at_utc:ownerApprovalProof.expires_at_utc}:null,
1733
1940
  new_pointer:publicPointer(newPointer)});
1734
1941
  predecessor=await capturePredecessor(control.ctx,recoverOfflinePredecessor,{allowHostDisarm:false});oldRuntime=predecessor.runtime;priorStatus=predecessor.status;
1735
1942
  await writeState(statePath,journal,'old_runtime_verified',{predecessor_mode:predecessor.mode,old_runtime:generationTuple(oldRuntime),
@@ -1865,7 +2072,8 @@ export async function runSlotUpdateCli(argv=process.argv.slice(2),{stateRoot=DEF
1865
2072
  stage:args.stage,developmentRoot:args.developmentRoot,expectedVersion:args.expectedVersion,expectedManifest:args.expectedManifest,
1866
2073
  sourceRoot:args.sourceRoot,stateRoot,restoreStandingGrants:args.restoreStandingGrants,recoverOfflinePredecessor:args.recoverOfflinePredecessor,
1867
2074
  cloudTransport:args.cloudTransport,bridgeBootstrap:args.bridgeBootstrap,processPolicyBootstrap:args.processPolicyBootstrap,
1868
- hostPolicyBootstrap:args.hostPolicyBootstrap,codingPolicyBootstrap:args.codingPolicyBootstrap,desktopPolicyBootstrap:args.desktopPolicyBootstrap,
2075
+ hostPolicyBootstrap:args.hostPolicyBootstrap,codingPolicyBootstrap:args.codingPolicyBootstrap,
2076
+ autonomousPolicyBootstrap:args.autonomousPolicyBootstrap,ownerApprovalBootstrap:args.ownerApprovalBootstrap,desktopPolicyBootstrap:args.desktopPolicyBootstrap,
1869
2077
  semanticLspBootstrap:args.semanticLspBootstrap,enableMachineFs:args.enableMachineFs,enableObservation:args.enableObservation,
1870
2078
  enableDesktop:args.enableDesktop,enableAutonomousSupervisor:args.enableAutonomousSupervisor,hostCommandPins:args.hostCommandPins
1871
2079
  });
@@ -0,0 +1,79 @@
1
+ import {lstatSync} from 'node:fs';
2
+ import os from 'node:os';
3
+ import path from 'node:path';
4
+
5
+ const canonical = value => process.platform === 'win32' ? value.toLowerCase() : value;
6
+ function within(parent,child){
7
+ const relative=path.relative(canonical(parent),canonical(child));
8
+ return relative===''||(!path.isAbsolute(relative)&&relative!=='..'&&!relative.startsWith('..'+path.sep));
9
+ }
10
+ function productionRoot(){return path.resolve(os.homedir(),'.deadbyte-mcp');}
11
+ function isStage(stateRoot){
12
+ const state=path.resolve(stateRoot),production=productionRoot();
13
+ if(state===production||canonical(state)===canonical(production))return false;
14
+ if(within(production,state)||within(state,production))throw new Error('STAGING_STATE_OVERLAPS_PRODUCTION');
15
+ return true;
16
+ }
17
+ function noReparseComponents(file,label){
18
+ let current=file;
19
+ for(;;){
20
+ try {if(lstatSync(current).isSymbolicLink())throw new Error('STAGING_REPARSE_INPUT: '+label);}
21
+ catch(error){if(error?.code!=='ENOENT')throw error;}
22
+ const parent=path.dirname(current);
23
+ if(parent===current)break;
24
+ current=parent;
25
+ }
26
+ }
27
+ /** A stage never reads policy, token, executable or config bytes from a production state. */
28
+ export function assertStageOwnedPath(file,stateRoot,label){
29
+ if(!isStage(stateRoot))return;
30
+ if(typeof file!=='string'||!path.isAbsolute(file)||file.includes('\0'))throw new Error('STAGING_ABSOLUTE_PATH_REQUIRED: '+label);
31
+ const state=path.resolve(stateRoot),target=path.resolve(file);
32
+ if(!within(state,target)||canonical(state)===canonical(target))throw new Error('STAGING_EXTERNAL_INPUT: '+label);
33
+ noReparseComponents(target,label);
34
+ }
35
+ export function assertStageNoProductionPath(file,stateRoot,label){
36
+ if(!isStage(stateRoot))return;
37
+ if(typeof file!=='string'||!path.isAbsolute(file)||file.includes('\0'))throw new Error('STAGING_ABSOLUTE_PATH_REQUIRED: '+label);
38
+ const target=path.resolve(file),production=productionRoot();
39
+ if(within(production,target))throw new Error('STAGING_PRODUCTION_INPUT: '+label);
40
+ noReparseComponents(target,label);
41
+ }
42
+ export function assertStagingBootstrapInputs({stateRoot,sourceRoot=null,bridgeBootstrap=null,cloudTransport=null,
43
+ processPolicyBootstrap=null,hostPolicyBootstrap=null,codingPolicyBootstrap=null,autonomousPolicyBootstrap=null,
44
+ ownerApprovalBootstrap=null,desktopPolicyBootstrap=null,semanticLspBootstrap=null}={}){
45
+ if(!isStage(stateRoot))return;
46
+ noReparseComponents(path.resolve(stateRoot),'state root');
47
+ if(sourceRoot!==null)assertStageOwnedPath(sourceRoot,stateRoot,'legacy source root');
48
+ if(bridgeBootstrap)assertStageOwnedPath(bridgeBootstrap.root,stateRoot,'bridge bootstrap root');
49
+ for(const [label,item] of Object.entries({processPolicyBootstrap,hostPolicyBootstrap,codingPolicyBootstrap,
50
+ autonomousPolicyBootstrap,ownerApprovalBootstrap,desktopPolicyBootstrap,semanticLspBootstrap})){
51
+ if(item)assertStageOwnedPath(item.path,stateRoot,label);
52
+ }
53
+ if(cloudTransport){
54
+ assertStageOwnedPath(cloudTransport.tokenFile?.path,stateRoot,'named tunnel token file');
55
+ assertStageOwnedPath(cloudTransport.childWorkingDirectory,stateRoot,'named tunnel child directory');
56
+ assertStageNoProductionPath(cloudTransport.cloudflared?.path,stateRoot,'named tunnel executable');
57
+ }
58
+ }
59
+ export function assertStagingControlConfig(config,stateRoot,{configPath}={}){
60
+ if(!isStage(stateRoot))return;
61
+ assertStageOwnedPath(configPath,stateRoot,'controller config file');
62
+ const owned=['CoreRoot','WorkspaceRoot','CodingPolicyFile','AutonomousPolicyFile','HostPolicyFile',
63
+ 'ProcessPolicyFile','DesktopPolicyFile','SemanticLspConfigFile','McpPathFile','McpUrlFile'];
64
+ for(const key of owned)if(config[key]!==undefined&&config[key]!==null&&config[key]!=='')
65
+ assertStageOwnedPath(config[key],stateRoot,key);
66
+ if(!config.CodingPolicyFile||!config.AutonomousPolicyFile)throw new Error('STAGING_REQUIRED_POLICIES_MISSING');
67
+ for(const [name,component] of [['Bridge',config.Bridge],['Cloud',config.Cloud]]){
68
+ if(!component||typeof component!=='object')throw new Error('STAGING_COMPONENT_MISSING: '+name);
69
+ // A hash-pinned executable (e.g. system Node) may be shared. Its script,
70
+ // cwd, policy inputs and secrets may not be shared with production.
71
+ if(component.FilePath)assertStageNoProductionPath(component.FilePath,stateRoot,name+'.FilePath');
72
+ for(const key of ['WorkingDirectory','ScriptPath'])if(component[key])
73
+ assertStageOwnedPath(component[key],stateRoot,name+'.'+key);
74
+ if(Array.isArray(component.Dependencies))for(const dep of component.Dependencies){
75
+ if(dep?.Name==='cloudflared'&&name==='Cloud')assertStageNoProductionPath(dep.Path,stateRoot,'Cloud.cloudflared');
76
+ else assertStageOwnedPath(dep?.Path,stateRoot,name+'.dependency.'+String(dep?.Name??''));
77
+ }
78
+ }
79
+ }
@@ -116,11 +116,13 @@ function summarizeOutcome(result,error,durationMs){
116
116
  const structured=result?.structuredContent;
117
117
  const status=typeof structured?.status==='string'?structured.status:(result?.isError===true?'error':'ok');
118
118
  const isError=result?.isError===true;
119
+ const unknownSideEffect=structured?.side_effect_state==='unknown'||structured?.result?.side_effect_state==='unknown';
119
120
  const capability=structured?.result;
120
121
  const snapshot=capability?.schema==='deadbyte.capability-manifest.v3'&&HEX64.test(String(capability.snapshot_id??''))&&
121
122
  Number.isInteger(capability.state_epoch)&&capability.state_epoch>0
122
123
  ?{snapshot_id:capability.snapshot_id,state_epoch:capability.state_epoch}:{};
123
124
  return {status:String(status).slice(0,128),is_error:isError,duration_ms:duration,receipt_sha256s:outcomeReceiptHashes(result),
125
+ ...(unknownSideEffect?{side_effect_state:'unknown'}:{}),
124
126
  ...snapshot,
125
127
  ...(isError?{reason:resultOutcomeReason(result)}:{})};
126
128
  }
package/src/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export const DEADBYTE_VERSION = '0.15.44';
1
+ export const DEADBYTE_VERSION = '0.15.46';
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';
@@ -53,7 +53,8 @@ async function createProviderHostFixture(){
53
53
  planner:{backend:'openai_compatible',endpoint:`http://127.0.0.1:${planner.address().port}/v1/chat/completions`,model:'r34-host-test',max_tokens:2048,timeout_ms:30000,max_response_bytes:262144},
54
54
  limits:{max_iterations:8,max_mutations:8,max_wall_ms:null,max_context_bytes:262144,max_diff_bytes:262144,max_files_per_diff:4},profiles:{}}));
55
55
  const autonomousPolicy=await loadAutonomousPolicy(autonomousPath,codingPolicy);
56
- await writeBoundStandingGrant('autonomous', autonomousPolicy.lease_file, {schema:'deadbyte.autonomous-grant.v2',policy_sha256:autonomousPolicy.policy_sha256,
56
+ await writeBoundStandingGrant('autonomous', autonomousPolicy.lease_file, {schema:'deadbyte.autonomous-grant.v2',policy_sha256:autonomousPolicy.policy_sha256,
57
+ coding_policy_sha256:codingPolicy.policy_sha256,
57
58
  instance_nonce:nonce(),root_ids:['project'],granted_at_utc:new Date().toISOString()});
58
59
  const codingRuntime=createCodingRuntime({policy:codingPolicy,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
59
60
  const nativeDiffRuntime=createNativeDiffRuntime({policy:autonomousPolicy,codingRuntime,signingKeyPath:keys.privateKeyPath,verifyKeyPath:keys.publicKeyPath});
@@ -2,10 +2,26 @@ import test from 'node:test';
2
2
  import assert from 'node:assert/strict';
3
3
  import {
4
4
  autonomousGoalApproveOutputSchema, autonomousGoalRunOutputSchema, autonomousGoalStatusOutputSchema,
5
- autonomousGoalVerifyOutputSchema,
5
+ autonomousGoalVerifyOutputSchema, nativeDiffPreviewOutputSchema,
6
6
  autonomousStructuredResult
7
7
  } from '../src/autonomous-output-contracts.mjs';
8
8
 
9
+ test('R71 preview-only MCP output cannot masquerade as a signed ready-to-apply diff',()=>{
10
+ const base={root_id:'preview',operation_count:1,payload_bytes:0,paths:['a.txt'],
11
+ operations:[{kind:'delete',path:'a.txt',expected_sha256:'a'.repeat(64)}]};
12
+ const unsigned=autonomousStructuredResult(nativeDiffPreviewOutputSchema,{
13
+ status:'preview_only',schema:'deadbyte.native-diff.v1',...base
14
+ });
15
+ assert.equal(unsigned.structuredContent.status,'preview_only');
16
+ assert.equal(unsigned.structuredContent.diff_id,undefined);
17
+ assert.throws(()=>autonomousStructuredResult(nativeDiffPreviewOutputSchema,{
18
+ status:'preview_only',diff_id:'b'.repeat(64),...base
19
+ }),/autonomous output contract violation/);
20
+ assert.throws(()=>autonomousStructuredResult(nativeDiffPreviewOutputSchema,{
21
+ status:'ready',...base
22
+ }),/autonomous output contract violation/);
23
+ });
24
+
9
25
  test('autonomous_goal_verify structured contract preserves delegated action evidence count', () => {
10
26
  const value = {
11
27
  ok:true,
@@ -73,7 +73,8 @@ async function startPlanner() {
73
73
  }));
74
74
  const autonomousSha = sha(await readFile(autonomousPath));
75
75
  await writeBoundStandingGrant('autonomous', path.join(root, 'runtime', 'autonomous-grant.json'), {
76
- schema:'deadbyte.autonomous-grant.v2', policy_sha256:autonomousSha,
76
+ schema:'deadbyte.autonomous-grant.v2', policy_sha256:autonomousSha,
77
+ coding_policy_sha256:codingSha,
77
78
  instance_nonce:nonce(), root_ids:['project'], granted_at_utc:new Date().toISOString()
78
79
  });
79
80
  const client = new Client({ name:'deadbyte-supervisor-integration', version:'0.8.0' },