deadbyte-mcp 0.8.7 → 0.8.9

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 (48) hide show
  1. package/MANIFEST.SHA256 +47 -45
  2. package/README.txt +11 -1
  3. package/bin/WORKER-REGISTRY.txt +3 -3
  4. package/bin/appcontainer-stage.exe +0 -0
  5. package/bin/appcontainer-stage.obj +0 -0
  6. package/bin/bootstrap-advapi32.exe +0 -0
  7. package/bin/bootstrap-advapi32.obj +0 -0
  8. package/bin/bootstrap-exitcode.exe +0 -0
  9. package/bin/bootstrap-exitcode.obj +0 -0
  10. package/bin/bootstrap-kernel32.exe +0 -0
  11. package/bin/bootstrap-kernel32.obj +0 -0
  12. package/bin/child-control-probe.exe +0 -0
  13. package/bin/child-control-probe.obj +0 -0
  14. package/bin/child-control-stage.exe +0 -0
  15. package/bin/child-control-stage.obj +0 -0
  16. package/bin/contained-reverse-worker.exe +0 -0
  17. package/bin/contained-reverse-worker.obj +0 -0
  18. package/bin/contained-transform-worker.exe +0 -0
  19. package/bin/contained-transform-worker.obj +0 -0
  20. package/bin/containment-probe.exe +0 -0
  21. package/bin/containment-probe.obj +0 -0
  22. package/bin/deadbyte-contain.exe +0 -0
  23. package/bin/deadbyte-contain.obj +0 -0
  24. package/bin/deadbyte-exec.exe +0 -0
  25. package/bin/deadbyte-exec.obj +0 -0
  26. package/bin/deadbyte-process-host.exe +0 -0
  27. package/bin/deadbyte-process-host.obj +0 -0
  28. package/bin/deadbyte-tunnel-host.exe +0 -0
  29. package/bin/deadbyte-tunnel-host.obj +0 -0
  30. package/controller/README.TXT +17 -7
  31. package/controller/deadbyte-controller.ps1 +1 -1
  32. package/controller/deadbyte-process-policy.json +1 -1
  33. package/docs/ARCHITECTURE.md +10 -2
  34. package/package.json +1 -1
  35. package/scripts/deferred-slot-operation.mjs +1 -1
  36. package/scripts/final-closure-verify.mjs +3 -3
  37. package/scripts/final-closure.mjs +3 -3
  38. package/scripts/gate-windows.ps1 +3 -3
  39. package/scripts/windows-gate-evidence.mjs +4 -4
  40. package/src/deadbyte-cli.mjs +198 -8
  41. package/src/remote-mcp-r33-parity-client.mjs +1 -1
  42. package/src/remote-session-guardian.mjs +76 -0
  43. package/src/version.mjs +1 -1
  44. package/test/core.test.mjs +1 -1
  45. package/test/r33-closeout-regression.test.mjs +1 -1
  46. package/test/r33-finalization.test.mjs +2 -2
  47. package/test/release-version.test.mjs +12 -12
  48. package/test/remote-session-cli.test.mjs +215 -0
package/MANIFEST.SHA256 CHANGED
@@ -1,43 +1,43 @@
1
- 0dfa96b3036f26298bda604e9a59e89b425f2da0e7264e31237bfe0c2bb029f1 bin/appcontainer-stage.exe
2
- f5e11d1bf93aad3a59d163920b61762b797f02abe2f8e6f49a322d83a7351485 bin/appcontainer-stage.obj
3
- 15aa31b7d9a738e2b0eb701478f8fa8074beceea60838076ccd9e507b087a094 bin/bootstrap-advapi32.exe
4
- 5815bbf78a697bc01c19a1026e261ddd89ed1e00eb362032f54648c5d2d7e934 bin/bootstrap-advapi32.obj
5
- 3bcc6e9938b08931ac06574a62c88206e7865b4b77e2603f49371ed89444f0a4 bin/bootstrap-exitcode.exe
6
- 7fb4343afd6b0268d04933a1a291a3c105c20f653ccce701b85ace35a34c532b bin/bootstrap-exitcode.obj
7
- a99d367eb45b0ff869db67758658155996f2a7ba98a9e5e73fdc7226c9255cc1 bin/bootstrap-kernel32.exe
8
- 491afe64a7560df9844959ad63cb61edac9acf8df93168a1e37756fe63d7ea12 bin/bootstrap-kernel32.obj
9
- 3cf8830978b5293868eb27b9cb5af9720131c94c20a7d465169edfff58a72e32 bin/child-control-probe.exe
10
- fd8254fb0da07a15dff0d43e8ac3eadec3223f9bfb669bc726417eef185f026e bin/child-control-probe.obj
11
- 712322f1de757195cafe9cbc74aba51350b801c0a1f2f3b0cbee6c845b557b1c bin/child-control-stage.exe
12
- 71d3dade2470fcf497c68da1eff609d161e8392741c82c7513bfce2899067d08 bin/child-control-stage.obj
13
- 0ec64e0fd1ae66de894b9ebf9fec9404066b77abd0f7cadaf9514fb938db679c bin/contained-reverse-worker.exe
14
- 5dfd529586ffef8ff3ce4aa00d7af530f9c790eefa9f49575d126aa0c59a08e0 bin/contained-reverse-worker.obj
15
- d51c3cb3ed1f0e4ce4ca066288786f8fe4338588ea6b8e9f0a1a8cb5b5f33a20 bin/contained-transform-worker.exe
16
- 8f57d69e0306715a7d77e317760f9e56ecc17175a692fe2fd0fff5fd5419dd78 bin/contained-transform-worker.obj
17
- c2dc6da9e435fb5d475850e921eec520378e18b51d8fd1941e5c2a342d2c91f5 bin/containment-probe.exe
18
- e397b0d517c8aa058db879e9fdc50beae2482a8d09e041393497e33209a0111c bin/containment-probe.obj
19
- 0e852f17835a767e7b4155b8de23dfef9c7bfa90de7d4ffc8101e964d763a0e5 bin/deadbyte-contain.exe
20
- 9f0045cc69fb73888963e552a7f53bf319065f9cd39202d0a0d82376effb7cb6 bin/deadbyte-contain.obj
21
- 18e8f071deb2ff95028b77e49679a6329121860c6ed61a753d3e1fdd31f69180 bin/deadbyte-exec.exe
22
- 410d62611927601fa6dc6df826e39ab294fae91bae681d88c532a5466936c2b7 bin/deadbyte-exec.obj
23
- 40575aae3f3127f3a5649fa6f142e29ad7e8660a3bff5fad2cdfe03cdfe66816 bin/deadbyte-process-host.exe
24
- 1a46b8ab2cddcf0e28890e56b630c79f0c176d069ac0110ea93568f9682ca1a4 bin/deadbyte-process-host.obj
25
- acc0f50e78b564ebb0912d4de782557b8dccec02cde58738706edef43e70d183 bin/deadbyte-tunnel-host.exe
26
- b3b505a400e0373267220c7a99820adc28e694e37d4e25495311f4fdf951ad37 bin/deadbyte-tunnel-host.obj
27
- 58ec5732e7978b01158a605989ab5f4601e06b08406fa3103795a01cdf9330c9 bin/WORKER-REGISTRY.txt
1
+ abd328bd74f41ca13ddcefb5f610ef61c128c689fe6d4f9c1e769465dc175b58 bin/appcontainer-stage.exe
2
+ 7c332f858796b2b26a562ac3d418a6a5c6e76ea0cd595ffc095ed7be83dada9d bin/appcontainer-stage.obj
3
+ c8abc48d86298a0501afb41c24fb549a37d404edcecdfb464121b7e9824c47e1 bin/bootstrap-advapi32.exe
4
+ 618a856c6977abf12432b81815703c3878da2680086fcf4ebcbfa2d2d9604afe bin/bootstrap-advapi32.obj
5
+ 4610acb87f8b371b27eff0c883073cbd29b6a83c0ffeb0ec39c60820daa3b33f bin/bootstrap-exitcode.exe
6
+ ccd7cd42a110d1e5434bbf053fa1e421a67c1f6443eeb2f97b19a14abd76e90f bin/bootstrap-exitcode.obj
7
+ 4092b3845f5240aa0222c8e506f203c334daabc267701b1428ee6202677b2d55 bin/bootstrap-kernel32.exe
8
+ 0be7cfd0b5ef0d3d484b2d76ad686ab2d768fe2eff096590a49b82457fbe7711 bin/bootstrap-kernel32.obj
9
+ 03ab1ff4aee4c8b86c9544eef3a4f3b36d048832d505e41ae893778cdac354c7 bin/child-control-probe.exe
10
+ cd8257fe8d125e959d85ccde823a7c82d3f7372e0056a0eb1520129c430aef81 bin/child-control-probe.obj
11
+ 1e72ac25b354d5b8e7602a4ffd93ced419082043dcd83050c0e64c9cfd1daf53 bin/child-control-stage.exe
12
+ c3d2979c519419401d08e61fc0fb152af0d57e44ec28fa96ab387d31602c5617 bin/child-control-stage.obj
13
+ 4e574d929b8050de4ec8a3012f857e0aa3b5d08c034ebba4d8ffd822ace6ba00 bin/contained-reverse-worker.exe
14
+ 22d92162695852799b47edb04da168fb3765d0d00da66f3847e178cdd910350d bin/contained-reverse-worker.obj
15
+ c912a61c34142a2e1653cf4da16a5d33af3a588a0c5449752ed5af4926c092b6 bin/contained-transform-worker.exe
16
+ cc15f0b3f005607a3aadab0c2a8f565358d703e34550e4cf115f4a963f855aa8 bin/contained-transform-worker.obj
17
+ ddf50836212590be5d1c7c55cf5a35610c124d92f13c7ef7f5e3c6edc09e79fe bin/containment-probe.exe
18
+ 6cc0823e78dd4fb73de18f9a2eb4e75ea8ee399e4c7a5f547f186980f223eefe bin/containment-probe.obj
19
+ b97521f43405c00aaaa84a39ecab1c6277d45b76f24b70d2601bf3ad8aa39da5 bin/deadbyte-contain.exe
20
+ 5f487764f9ad6c82050bf6c32cacb430ead50e357b287f8de12c33be9ea32180 bin/deadbyte-contain.obj
21
+ 3406bb25ba2d3ba8d15e12702ad657ffc536c234d0489b69730f242f912ebed7 bin/deadbyte-exec.exe
22
+ 0e88a9990a081b88aaf2ed7f06ccad49feac866d7ef94498da23a257f7ed0624 bin/deadbyte-exec.obj
23
+ b74ab56b34653da2426d55bcfe3c03e05cca3a11e3d77477722ded0915da255e bin/deadbyte-process-host.exe
24
+ 67325e61a3af10cfce1f6103db14b189baa537fce3542368687a09c2840b4d4d bin/deadbyte-process-host.obj
25
+ 13c5f970c52d72c2cb9d08075d51fd488d9c9776bb6c10aeab67c2e62ba43a0c bin/deadbyte-tunnel-host.exe
26
+ ff42888a61933093d40c8814bb39becc1df1ec160fe21720a6eaa97a518f3484 bin/deadbyte-tunnel-host.obj
27
+ edcbd31b9753adf5c999c24c53f440bd4bac76b80584578d02f55a41088f85d1 bin/WORKER-REGISTRY.txt
28
28
  e74d45bd9facf03c9a2a0ff52600252924deb804a87e4b5503f48662a2b45a3b conpty-input-observer.obj
29
29
  f8e46c4bd8eaeb804ffa5af87d6543e37eba66d817d8e5555f9d0462a99e4fdb controller/deadbyte-controller.config.psd1.example
30
- d46b896dd802a5b029f6ffe7a89ecde4ff0d653194953bf6d21e3b4f4a3fb288 controller/deadbyte-controller.ps1
30
+ 2850780b793df4f72f6d0a908324472a784ce0d335bc0bf0bce60983f6ceeb69 controller/deadbyte-controller.ps1
31
31
  5e8ef223b0dbc52173aaf044a075837eef8964ff0e7e16f83a595e0d17695625 controller/deadbyte-host-policy.json.example
32
32
  6f0cc469f51444dd18211fa3346c826abeea4d2ec7a8945f0200dc5c622031ef controller/DEADBYTE-MCP.cmd
33
- 0626d4b02739d4108776678dc7acd114f8d794c394c25b4803deaa8cb5cbcd5c controller/deadbyte-process-policy.json
34
- 83f5c2e692a563062a80585d5de734bf443af4ebed4239625b1f61ef04eee7df controller/README.TXT
33
+ 1235687eba3516724f3a041ff627eae070e12adbed651e356c7f9e7212c93211 controller/deadbyte-process-policy.json
34
+ b5ae7c87123d36b20bdc4f6dcd55267f9dac6c36ced2ea4cf61dfe00a41bc7e8 controller/README.TXT
35
35
  101e613518c73c6aa31c725fe1b5f9a24c995dc9876b7b28e1f3e97ac9f8d362 DEADBYTE-MCP-R27-FINAL/R27-ADMIN-CLOSURE.json
36
36
  e9baf9abf4410ff8bd0f68e6b957dbbf9790fc88d9126311c5c135e943424952 DEADBYTE-MCP-R27-FINAL/R27-CLEANUP-FINAL-RECEIPT.json
37
37
  39cb483c0897af5027bf1790a35a4c3b9a8cd5f6e3a2e0440787615f69cd14b2 DEADBYTE-MCP-R27-FINAL/R27-FINAL-CLOSURE.json
38
38
  9aaf6d07e7511a1f0046ee8cc731d4b9660bbbf47dcea785a9c6b6cec95640bc DEADBYTE-MCP-R27-FINAL/R27-SLOT-UPDATE-RECEIPT.json
39
39
  70dfccba12797ce5fea60668c97026cf71e7942cb4d886cac5853e05b362b6c1 DEADBYTE-MCP.cmd
40
- 978b566a611d0301cd493868b983ecca6d2e2cc98a9bf09c73ff05876cb9f6ee docs/ARCHITECTURE.md
40
+ 38d10225a7a4cf645d0c806d3020b1d404da83a9081c6d3c77c126cc50fb8cb6 docs/ARCHITECTURE.md
41
41
  3f455f407933d8e044682f3415bdb51e68d7c4b508411dda7d8ed65b6e1d7e79 docs/AUTHORITY.md
42
42
  3be291331862ac5bd892345e84d6ce8d9b6d835855235d7c5f28d5cd4b04afdb docs/CHILD-CONTROL.md
43
43
  a9cbd1e2def00aae93569835f2ee17aa5cfd181e5770d7c1185c9c62deb0769e docs/CONSTRUCTION-LADDER.md
@@ -1159,7 +1159,7 @@ db605a698f917c4006462596e6526fe6a9c6eff9b8409e8472fe545d4b70027c node_modules/z
1159
1159
  f67a25ded944912bd4621b68df17c7c70a52aae117278c076c0aabbc32a3abba node_modules/zod/v4/mini/schemas.d.ts
1160
1160
  51b3a7869df2d3a1bd98acb9d5b6a61bbccc7653d7a3d8fca7f0e0db7e84e284 node_modules/zod/v4/mini/schemas.js
1161
1161
  3e3125b5a537a24c3632c1c5c09640ce7bcb3935901b127ad8ae3c37a27f133f node_modules/zod/v4/package.json
1162
- e65d5d76cfed4387ba8055427a66452dd808bdcab4c0f37e81c1aeeed9dfed9e package.json
1162
+ 51d0b074c767c6436f937c42431cf3acbfc3c651d86a88fdaa9fee8dd20e30ad package.json
1163
1163
  b3bb2324a0ee2ca81eac1e8e44ff5141fe0cb6434843c6c3325764abdca2a71a proof/CONTAINMENT-BUILD.txt
1164
1164
  a8efa13e44ff50902aee64cfca542d19605a1ae23db95280ad2d44b324f3eae7 proof/MCP-SMOKE-STATUS.txt
1165
1165
  7c115069cc4a343ca21b0ebdee74bd8f544b02d4aa906ffa13308725825d891a proof/NODE-SYNTAX.txt
@@ -1217,22 +1217,22 @@ d433aaea5cccd1e184775ac4d915f34e0b39c6c99916dddd009731ad5fd6443f proof/V073-WIN
1217
1217
  bd0773a9e02e8ab11475c9cc5a7db29a8313233b7eab7f837922737e75ec5938 proof/VERIFY.txt
1218
1218
  2c821808d4596965c577f40191daee224d3aea81989778c4af97f53a83b487db proof/WINDOWS-CONTAINED-STATUS.txt
1219
1219
  f8bc839c7b4fcb0ea7b7e7ad9448847c3beae6f95f2eb4d407d1b284aaa4ff68 proof/WINDOWS-CONTAINMENT-STATUS.txt
1220
- 0fca451ad8b3c5d5d02fa165c84900a8c3445c24df14d285677168fc17a65c77 README.txt
1220
+ 27dcf49c60b78b714f8f17425925b6d2700f86b13047231c634a8eb58a78a489 README.txt
1221
1221
  1d4e9dee6f0669bd825334a6a72e13ee85ee29ab430dcef21d893ffd744774b5 scripts/autonomous-benchmark.mjs
1222
1222
  06447dea18be1fa0a5e5a1167a6e3dcbd10ddfd28dd18d7bfd17383cf5bfc080 scripts/autonomous-build.ps1
1223
1223
  6ae23099102eb0f0be4969598ba76600d19b00abf534a039d48e63bc7d7fffe3 scripts/autonomous-tests.ps1
1224
1224
  1d987d12c5af6ffbaec1fe2f95aadc4740fe25933c0f0941f362daa2d2d84f48 scripts/build-containment.ps1
1225
1225
  7d3c741de2b1c29f82efa96ab81fc819d8520f97413ce5ab0d4a1b1efd0e8b42 scripts/coding-live-smoke.mjs
1226
- 1df3d510d09d2449cb2e2b5a8c382713a6538612533901ce7d71dd32f55d5468 scripts/deferred-slot-operation.mjs
1227
- 19086107f649f7462a6001ef6efddbc782ae5cb93903592bf89bfae6053e0628 scripts/final-closure-verify.mjs
1228
- c66e98577c7f72e3244cbce2f87200f1b9bcc00a24a33e46fad2bb0c3b634fc3 scripts/final-closure.mjs
1229
- 5406b5a208ef211f5287ab0b3a8e285c11474360c4d63d049e2e3b5c090e726d scripts/gate-windows.ps1
1226
+ 6b3455903e4c229ca70a9ead24c317051fc92664abe9d5d4626974c30e2a1fa7 scripts/deferred-slot-operation.mjs
1227
+ dbcd47f4e622922b86c0135494481dc8a565321cf444baac2ce473d8f66de817 scripts/final-closure-verify.mjs
1228
+ 607ee1f5a55d9ec035b0ad24718aae98acc1c79dd5a94f547be78f6934f70417 scripts/final-closure.mjs
1229
+ d6af5ec6084eb6b2ce15718a4191584b9185b2133648396c6614e55ce42a9668 scripts/gate-windows.ps1
1230
1230
  a64ab9a70c26495ac5d94c024a8813d8bd2bc8a708f236b672cb18556396c262 scripts/init-host-policy.ps1
1231
1231
  b655f2f9c63c7e4b651e7182c6461f9ec63d0991f770f24fe5d44b209ad6bdfb scripts/refresh-process-policy.mjs
1232
1232
  c92b518b7232849d945f0fcd9c428a2366758e5c9410be68104ed50ef869a33e scripts/release-manifest.mjs
1233
1233
  d5284978c3b82a6eaef0ddd0ded0754467791d441dfe2d2da11dc25289301607 scripts/release-parity.mjs
1234
1234
  caac2867971dfdf56456528b816ff56282a11cbba81510c13e1723accffac678 scripts/runtime-self-update.mjs
1235
- 53b968bab9039a5c3d4aa7224e894bea45427005e8063a2f975340867d9a5e29 scripts/windows-gate-evidence.mjs
1235
+ dc59e4085361781a8003f014e65045dd2ed9b03caf48c7adf62bda6f6a44387f scripts/windows-gate-evidence.mjs
1236
1236
  5f4a73a1c3dc178c4b5de193b6cfc4774cfc46d5ad95ef0b786d4d386d82323b src/agent-mcp-tools.mjs
1237
1237
  5cca555943318990ae8dc82481ab0d172e813261c71b541cc99114f411b040fa src/agent-runtime.mjs
1238
1238
  0e2d1cdcc5e592ffe36e32fa6da68d84e00f992ca3a189b7a3735d8bbac476a6 src/audit-context.mjs
@@ -1260,7 +1260,7 @@ dcd4c9784e88e253e91ddad3c24b38cb0b36906ed737f6d7bee7712360f66f7a src/containmen
1260
1260
  0fd1141a749596e4314a532ad99dabfeac213eef857e400eea1aa0ad09e0383d src/containment-construction.mjs
1261
1261
  fe1761b98738e1aea1d25ddf3a0ba56ccad95a43d45104a89b8665197c7c9e18 src/containment-result.mjs
1262
1262
  705a89cb12fedb993d55dfed32a5870d33e10db26422c944d1e52223566c096c src/containment-smoke.mjs
1263
- f5fcdc8fbfd16a5dc6bdfedbe7c951fbb74204eb684300660464ad1672040a69 src/deadbyte-cli.mjs
1263
+ ab87beb7306b32e3addbd43fa1c3e391de38443ca81c0cae7df31ba983f21cac src/deadbyte-cli.mjs
1264
1264
  f5fafd46052b09f71a528594eb230edc5766ee0d006432d744590ec97c9c580d src/executor.mjs
1265
1265
  264d21a4385a0a04613fe78203e7f5e231a5c06397086cfacc88076e60b89b71 src/hash.mjs
1266
1266
  1cc648f59d739cb3269827a7aff23dc9dc6617a77c4da607ba947ca6e4626548 src/host-plane.mjs
@@ -1300,8 +1300,9 @@ a7a63ea58291e0676c7199633ae234030f46285840cdfc519c879c279ae8bb14 src/release-sl
1300
1300
  efd3f7ae0a3a614485933942ab5e27e874175feb81eb3e5130913c1e6837e7ca src/remote-mcp-host-smoke-client.mjs
1301
1301
  aeed462a1090611ce04f2d4e453e4a6e9163091d6d99f768233943f1c24b949f src/remote-mcp-machine-smoke-client.mjs
1302
1302
  510691cf1d5d1f995b835881651da9b2a7bc22c02bc4da12e3a8266713bcc7e5 src/remote-mcp-r24-disarmed-smoke-client.mjs
1303
- 3aaaac035742914f37e15441594e01582b84fd5ad301052f9ff5d3f391700b9c src/remote-mcp-r33-parity-client.mjs
1303
+ 4b94b3431983192423aa464e313dcf6e0442d786c388a782292d4cded80060cd src/remote-mcp-r33-parity-client.mjs
1304
1304
  86a641f0a1c34ad23bc8f50809a37b57d820ea7810ef86cb4315ce75d0406237 src/remote-mcp-smoke-client.mjs
1305
+ c8fd6fd3e49e866f2c3be326ab07222b36a44528ee0fc3f7b371c34a60e3c45b src/remote-session-guardian.mjs
1305
1306
  827b156eac01cc9ec6f5ca9a49219612f0da511fde770a9eb1235aa913b5e514 src/request-identity.mjs
1306
1307
  ce2223bf6075e96cc02505c1439acce18bb1f204c5e86cf9152bb7194daf3cd2 src/runtime-identity.mjs
1307
1308
  7b7ab858adec2febe9459d7bf355e24500181dc48c575c47a53796735504bcb8 src/runtime-update.mjs
@@ -1313,7 +1314,7 @@ a765372ee27cf34b3f42ca77e43b839a306a4ca7d8b1c5e2fdd4367ab0c95769 src/tunnel-std
1313
1314
  16df134040e3ff1e73df722b568d3e8ac0a6923b097696cead7580da75aa8d2a src/tunnel-stdio-compat.mjs
1314
1315
  642cb67d1854850e336ee73fbd71b329056bdcbdab35cea19c1a995a155ff5d9 src/verifier-cli.mjs
1315
1316
  8f818a4852ce3be3a380cd4736c3073a7a3e0e45cd7c7ca90588a6b843d3d022 src/verifier.mjs
1316
- 9d1efd8ebb9b4ff260e5a1d6f5ba527fc626544d25fe73aca8fa151b67cf74bf src/version.mjs
1317
+ fe49af9733e56003b256fdc9d47e59c561f19f19423dd14711f54033a63f2760 src/version.mjs
1317
1318
  7e09c57a97289360aabfaf3d531208e54872efbfb3182f3c18203d2cb88c46b5 src/worker-registry.mjs
1318
1319
  2a78c818058b25efdc143a3a97c832da231ccd6ff9d90c7796d681a0251b85c4 test/agent-crash-child.mjs
1319
1320
  d9461ab7c118ee75d37a2d6f5e637d7e43505af6a9ad8c7ddafffc8ace185a5b test/agent-crash-worker.mjs
@@ -1347,7 +1348,7 @@ a972bd20fa927427e6d8e4181699a27e792e916b02dcf8a1ac3e2b44afd238c1 test/controlle
1347
1348
  ceba7d1a09ccdcb20f33fe4e6de7667ffe0f0170d1bc9fefadfdba4d7f228090 test/controller-r29.test.mjs
1348
1349
  4d0dc810d9af13907f31c834a13acfb7e01ffbbe65575b69649fe52741e42175 test/controller-r30.test.mjs
1349
1350
  b013bb2a100d3e784f0c0a2ef63d9d9d57cadd71a8db4fb845cbedb4a7b2c222 test/controller.test.mjs
1350
- b6c0679db51645f2e6a38171a72782fbe93018e235348cefa0aeb2c902b99bc9 test/core.test.mjs
1351
+ 0cba577c7f41d3b2b1eb9cb57c330070f9f7118695d5d48059aeae5d6e781dd1 test/core.test.mjs
1351
1352
  7ba4b06a1c4141ba87b5b371157fb59940d15a1016b4c599a13753d63ad0d3d3 test/DEADBYTE-MCP-WRITE-PROBE.txt
1352
1353
  3d4b0bdef293fa3889dec60e8e321594a67e9f7095d3c541b382c82c33b2642e test/deferred-slot-operation-behavior.test.mjs
1353
1354
  33eff6932951dd9cfd42d68b3333f8080f1a0b9fdc01b05aef5c96faf44b032e test/deferred-slot-operation.test.mjs
@@ -1373,14 +1374,15 @@ b84622d07d2ecfe27b55e45f7e89949f29a2d14184d8c6f6f82ed7f1e5fa59be test/process-n
1373
1374
  a54301192c4cc842cbb6e113db2615a13f448c702273ef0585cc015859716f8a test/process-release-gate.test.mjs
1374
1375
  e19a477abb83c63a1fb7ab5d5d1e9925113cded95d57084186b6ecd6161780e0 test/process-runtime.test.mjs
1375
1376
  34afc8a01e0c21501ec2647b7b0198b73570bce89eaaa983bd50c5709bb83fb8 test/process-supervision.test.mjs
1376
- f4e98853611403fdc993d2071b72eea6610cf3fb70e927720d736da3683ea915 test/r33-closeout-regression.test.mjs
1377
- a70467549ce775089f1850ee7d22f5074eaddd9cdcfce7eca34ede4902ab79a6 test/r33-finalization.test.mjs
1377
+ d0aadb88645bba57411f79f64d5f478b30f843a7786eb82fc80f229e4114bf5d test/r33-closeout-regression.test.mjs
1378
+ a4de241ed8f1d48d938890a9eb505e805411ef7f86597b09555e719883ded897 test/r33-finalization.test.mjs
1378
1379
  8776f707a78b29ccf2721b8bcf0ae4038a20022710ef9d6ccfea997c8c6d9d17 test/release-generation.test.mjs
1379
1380
  bb58887a2c393cd5aac3fa3bb3a04de2704bd2a30dd2083ea638543485fa7e60 test/release-manifest-windows.test.mjs
1380
1381
  7cc69e786bfd9e4a7e70888c4c1b5f756a264bc1a991139c468b63af23f24c38 test/release-slot.test.mjs
1381
1382
  0109c76b480cc600f6ed42be4519df3fa6835012bb9e4ba4ae6be6635468c4a8 test/release-smoke-isolation.test.mjs
1382
- 14a7fcb184581b75ad4ee5da2d0dd2b5a2ed9b95576f336cb253c2488e31f2cf test/release-version.test.mjs
1383
+ 6629a9a0d8dcdb0c8adc44b5f012540545be79ebde176f89d16d024b1183e720 test/release-version.test.mjs
1383
1384
  1fdb6cbd3a51476834382086bad2b92029987b6214153576cd803f9da0b06902 test/remote-r24-catalog.test.mjs
1385
+ dd2d0c54000255c9b41e26b6de41a2622bf33bc4617351804cd0854e614bdc8d test/remote-session-cli.test.mjs
1384
1386
  c05c64db73839698fdcdd4faa2bdbd78df861f53495f7a3b68a1fb53abb7d3c2 test/runtime-self-update.test.mjs
1385
1387
  2f421a60ff6baa9cc3de0cbc4804ffe6b2d26805f94d4f57e6c4a1758f464afb test/standing-grants.test.mjs
1386
1388
  6ecc712b7bcb2abdb934e7d40232ed97ac355f3f47665f557c2168b04d113b41 test/tool-audit.test.mjs
package/README.txt CHANGED
@@ -1,4 +1,4 @@
1
- DEADBYTE MCP V0.8.7 / R33 FINAL MACHINE BASELINE
1
+ DEADBYTE MCP V0.8.9 / R33 FINAL MACHINE BASELINE
2
2
  =========================
3
3
 
4
4
  AUTHENTICATED MACHINE TRUTH + DURABLE AGENT + CODING + AUTONOMOUS PRODUCTION ENGINE
@@ -14,6 +14,16 @@ Use the npm CLI entry point instead of the legacy CMD convenience wrapper:
14
14
  npx deadbyte-mcp@latest audit verify
15
15
 
16
16
  The remote CLI invokes the stable active-release PowerShell launcher directly with shell=false.
17
+ Bare `remote` is a foreground device session: it starts/resumes the verified runtime, arms process,
18
+ coding, autonomous, and host authority in dependency order, independently checks that all four
19
+ planes report ARMED, then streams new audit plus controller-runtime log records until Ctrl+C or the
20
+ foreground input closes. Session shutdown revokes all four planes and parks the Bridge. A detached
21
+ nonce-bound guardian watches the foreground owner and performs the same revoke+park cleanup if the
22
+ owner is hard-killed before `finally` can run. The v2 runtime marker binds owner PID, guardian PID,
23
+ and a random 256-bit session nonce, prevents concurrent foreground sessions, and lets a later start
24
+ recover only a dead, unguarded stale owner. Explicit subcommands such as `remote status` remain
25
+ one-shot controller operations.
26
+
17
27
  The legacy CMD file remains only as an R33 compatibility artifact until activation/rollback/restore
18
28
  proof confirms the npx path; it is not the primary operator interface. Audit data lives under the
19
29
  stable mutable state root and never inside immutable release slots.
@@ -2,11 +2,11 @@ schema=deadbyte.worker-registry.v1
2
2
  version=0.6.0
3
3
  policy=compile-time-operation-to-executable
4
4
  launcher.executable=deadbyte-exec.exe
5
- launcher.sha256=18e8f071deb2ff95028b77e49679a6329121860c6ed61a753d3e1fdd31f69180
5
+ launcher.sha256=3406bb25ba2d3ba8d15e12702ad657ffc536c234d0489b69730f242f912ebed7
6
6
  operation.lowercase_ascii=contained-transform-v1
7
7
  operation.reverse_bytes=contained-reverse-v1
8
8
  operation.uppercase_ascii=contained-transform-v1
9
9
  worker.contained-reverse-v1.executable=contained-reverse-worker.exe
10
- worker.contained-reverse-v1.sha256=0ec64e0fd1ae66de894b9ebf9fec9404066b77abd0f7cadaf9514fb938db679c
10
+ worker.contained-reverse-v1.sha256=4e574d929b8050de4ec8a3012f857e0aa3b5d08c034ebba4d8ffd822ace6ba00
11
11
  worker.contained-transform-v1.executable=contained-transform-worker.exe
12
- worker.contained-transform-v1.sha256=d51c3cb3ed1f0e4ce4ca066288786f8fe4338588ea6b8e9f0a1a8cb5b5f33a20
12
+ worker.contained-transform-v1.sha256=c912a61c34142a2e1653cf4da16a5d33af3a588a0c5449752ed5af4926c092b6
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
Binary file
@@ -1,17 +1,27 @@
1
- DEADBYTE MCP V0.8.7 / R33 CONTROLLER
1
+ DEADBYTE MCP V0.8.9 / R33 CONTROLLER
2
2
  ====================================
3
3
 
4
4
  ENTRY POINT
5
5
  -----------
6
- Use only:
6
+ Primary operator entry point:
7
7
 
8
- %USERPROFILE%\Downloads\DEADBYTE-MCP.cmd
8
+ npx deadbyte-mcp@latest remote
9
9
 
10
- The package and controller launchers forward into the canonical controller root.
11
- A copied/misbound controller fails closed before creating command runtime state.
10
+ Bare `remote` is a foreground device session. It starts the verified runtime, arms process,
11
+ coding, autonomous, and host authority, verifies all four planes are ARMED, then streams new
12
+ audit/runtime logs. Ctrl+C/SIGTERM or foreground input closure revokes all four planes and parks
13
+ the Bridge. A detached nonce-bound guardian performs the same fixed revoke+park cleanup if the
14
+ foreground owner is hard-killed. The v2 session marker records owner PID, guardian PID, and random
15
+ 256-bit nonce so a guardian cannot remove a replacement session marker; a later start recovers only
16
+ a dead stale owner with no live guardian. `remote status` and other explicit subcommands remain
17
+ one-shot operations.
12
18
 
13
- CLOSING OR EXITING THE MENU DOES NOT STOP A STARTED RUNTIME.
14
- Only explicit park/stop/restart lifecycle commands mutate verified owned processes.
19
+ The package/controller CMD launchers remain compatibility-only and forward into the canonical
20
+ controller root. A copied/misbound controller fails closed before creating command runtime state.
21
+
22
+ CLOSING OR EXITING THE LEGACY MENU DOES NOT STOP A STARTED RUNTIME.
23
+ Only explicit park/stop/restart lifecycle commands mutate verified owned processes outside the
24
+ foreground `remote` session contract.
15
25
 
16
26
  STATE
17
27
  -----
@@ -1026,7 +1026,7 @@ while ($true) {
1026
1026
  Clear-Host
1027
1027
  $cfg = Get-Config
1028
1028
  Assert-ControllerRootBound $cfg
1029
- Write-Host 'DEADBYTE MCP ON-DEMAND CONTROLLER V0.8.7 / R33'
1029
+ Write-Host 'DEADBYTE MCP ON-DEMAND CONTROLLER V0.8.9 / R33'
1030
1030
  Write-Host '======================================'
1031
1031
  Show-State $cfg | Out-Null
1032
1032
  Write-Host ''
@@ -5,7 +5,7 @@
5
5
  "state_dir": "C:\\Users\\DEADBYTE\\Downloads\\deadbyte-mcp-r33-final-candidate-v2\\controller\\runtime\\process-state",
6
6
  "native_host": {
7
7
  "path": "C:\\Users\\DEADBYTE\\Downloads\\deadbyte-mcp-r33-final-candidate-v2\\bin\\deadbyte-process-host.exe",
8
- "sha256": "40575aae3f3127f3a5649fa6f142e29ad7e8660a3bff5fad2cdfe03cdfe66816"
8
+ "sha256": "b74ab56b34653da2426d55bcfe3c03e05cca3a11e3d77477722ded0915da255e"
9
9
  },
10
10
  "roots": {
11
11
  "core": {
@@ -1,4 +1,4 @@
1
- # DEADBYTE MCP V0.8.7 / R33 ARCHITECTURE
1
+ # DEADBYTE MCP V0.8.9 / R33 ARCHITECTURE
2
2
 
3
3
  ```mermaid
4
4
  flowchart TD
@@ -40,7 +40,15 @@ flowchart TD
40
40
 
41
41
  The lifecycle controller is not execution authority. It starts, parks, resumes,
42
42
  observes, or stops verified owned processes and feature-gates the agent catalog;
43
- it cannot manufacture arbitrary MCP capabilities.
43
+ it cannot manufacture arbitrary MCP capabilities. The npx foreground `remote` session is an
44
+ operator-side orchestrator over existing controller commands: it acquires the four explicit
45
+ standing grants in dependency order, verifies their observed ARMED state, tails only newly appended
46
+ audit/runtime logs, and revokes all four grants plus parks the Bridge on graceful session exit.
47
+ A detached guardian is bound to the foreground owner by a random session nonce. The v2 marker
48
+ records owner PID, guardian PID, and nonce; if the owner is hard-killed before graceful cleanup,
49
+ the guardian independently invokes only the fixed revoke+park controller sequence and removes the
50
+ marker only when the same nonce is still current. Concurrent sessions fail closed, while a later
51
+ invocation may recover a stale owner only when no live guardian remains.
44
52
 
45
53
  ## Exact capability catalogs
46
54
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "deadbyte-mcp",
3
- "version": "0.8.7",
3
+ "version": "0.8.9",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Authenticated MCP machine-truth runtime with signed audit chain, immutable release slots, contained execution, coding/autonomous production authority, and npx-first remote control.",
@@ -22,7 +22,7 @@ const atomicJson=async(file,value)=>{
22
22
  };
23
23
  function activationArgs(manifestSha){
24
24
  if(!HEX64.test(manifestSha??'')) throw new Error('activate requires one lowercase manifest SHA-256');
25
- return ['--stage',packageRoot,'--development-root',packageRoot,'--expected-version','0.8.7',
25
+ return ['--stage',packageRoot,'--development-root',packageRoot,'--expected-version','0.8.9',
26
26
  '--expected-manifest',manifestSha,'--process-policy',path.join(packageRoot,'controller','deadbyte-process-policy.json'),
27
27
  '--process-policy-sha256',null,'--enable-machine-fs','--enable-observation'];
28
28
  }
@@ -84,7 +84,7 @@ function verifyPing(ping,status,pointer){
84
84
 
85
85
  export async function verifyFinalClosureObject(envelope,{stateRoot=path.join(os.homedir(),'.deadbyte-mcp')}={}){
86
86
  insist(envelope?.body?.schema==='deadbyte.r33-final-closure.v1','closure schema mismatch');
87
- insist(envelope.body.version==='0.8.7','closure version mismatch');
87
+ insist(envelope.body.version==='0.8.9','closure version mismatch');
88
88
  insist(envelope.body.release_label==='R33 FINAL MACHINE BASELINE','closure release label mismatch');
89
89
  const keyInfo=publicKeyInfo(await readFile(path.join(stateRoot,'trust','ed25519-public.pem')));
90
90
  const bodyBytes=Buffer.from(canonicalJson(envelope.body),'utf8');
@@ -97,7 +97,7 @@ export async function verifyFinalClosureObject(envelope,{stateRoot=path.join(os.
97
97
 
98
98
  const pointer=JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
99
99
  insist(same(pointer,envelope.body.active_pointer),'active pointer drift');
100
- insist(pointer.version==='0.8.7','active pointer version mismatch');
100
+ insist(pointer.version==='0.8.9','active pointer version mismatch');
101
101
  insist(pointer.release_id===`v${pointer.version}-${pointer.manifest_sha256.slice(0,16)}`,'release id/content address mismatch');
102
102
  const releaseFiles=await verifyTree(pointer.release_root,'MANIFEST.SHA256',pointer.manifest_sha256);
103
103
  const generationFiles=await verifyTree(pointer.generation_root,'GENERATION.SHA256',pointer.generation_manifest_sha256);
@@ -129,7 +129,7 @@ export async function verifyFinalClosureObject(envelope,{stateRoot=path.join(os.
129
129
  const windowsGate=JSON.parse(await readFile(evidence.windows_gate.path,'utf8'));
130
130
  insist(windowsGate.schema==='deadbyte.r33-windows-gate.v1'&&windowsGate.status==='passed'&&windowsGate.exit_code===0,'Windows gate evidence invalid');
131
131
  insist(windowsGate.manifest_sha256===pointer.manifest_sha256,'Windows gate manifest binding mismatch');
132
- insist(windowsGate.marker==='DEADBYTE V0.8.7 / R33 WINDOWS BATCH GATE: PASS','Windows gate PASS marker mismatch');
132
+ insist(windowsGate.marker==='DEADBYTE V0.8.9 / R33 WINDOWS BATCH GATE: PASS','Windows gate PASS marker mismatch');
133
133
 
134
134
  const machine=envelope.body.machine;
135
135
  insist(machine?.status?.release_id===pointer.release_id&&machine.status.generation_manifest_sha256===pointer.generation_manifest_sha256,'machine status binding mismatch');
@@ -22,7 +22,7 @@ function structured(result,label){
22
22
  async function publicMachineEvidence(){
23
23
  const endpoint=new URL((await readFile(path.join(stateRoot,'bridge','mcp-url.txt'),'utf8')).trim());
24
24
  insist(endpoint.protocol==='https:','final closure requires HTTPS public endpoint');
25
- const client=new Client({name:'deadbyte-r33-final-closure',version:'0.8.7'},
25
+ const client=new Client({name:'deadbyte-r33-final-closure',version:'0.8.9'},
26
26
  {versionNegotiation:{mode:{pin:'2026-07-28'}}});
27
27
  const transport=new StreamableHTTPClientTransport(endpoint);
28
28
  try{
@@ -61,7 +61,7 @@ async function atomicCreate(file,bytes){
61
61
 
62
62
  export async function writeFinalClosure({parityPath,activationPath,windowsGatePath}){
63
63
  const pointer=JSON.parse(await readFile(path.join(stateRoot,'active-release.json'),'utf8'));
64
- insist(pointer.version==='0.8.7','final closure requires V0.8.7 active release');
64
+ insist(pointer.version==='0.8.9','final closure requires V0.8.9 active release');
65
65
  const parity=JSON.parse(await readFile(path.resolve(parityPath),'utf8'));
66
66
  insist(parity.schema==='deadbyte.r33-parity.v1'&&parity.status==='passed','parity evidence is not passed');
67
67
  insist(parity.release_id===pointer.release_id&&parity.manifest_sha256===pointer.manifest_sha256,'parity/current release mismatch');
@@ -76,7 +76,7 @@ export async function writeFinalClosure({parityPath,activationPath,windowsGatePa
76
76
  rollback:await fileEvidence(parity.rollback.receipt_path), restore:await fileEvidence(parity.restore.receipt_path),
77
77
  windows_gate:await fileEvidence(windowsGatePath)
78
78
  };
79
- const body={schema:'deadbyte.r33-final-closure.v1',version:'0.8.7',release_label:'R33 FINAL MACHINE BASELINE',
79
+ const body={schema:'deadbyte.r33-final-closure.v1',version:'0.8.9',release_label:'R33 FINAL MACHINE BASELINE',
80
80
  completed_at_utc:new Date().toISOString(),active_pointer:pointer,evidence,
81
81
  public_catalog:{tool_count:machine.tool_count,strict_output_schemas:machine.strict_output_schemas},
82
82
  machine:{status:machine.status,ping:machine.ping,nonce:machine.nonce},
@@ -9,7 +9,7 @@ function Invoke-GateStep {
9
9
  Write-Host "==== $Name ===="
10
10
  & npm.cmd @NpmArgs
11
11
  if ($LASTEXITCODE -ne 0) {
12
- throw "DEADBYTE V0.8.7 / R33 gate failed at $Name with exit code $LASTEXITCODE"
12
+ throw "DEADBYTE V0.8.9 / R33 gate failed at $Name with exit code $LASTEXITCODE"
13
13
  }
14
14
  }
15
15
 
@@ -36,7 +36,7 @@ Invoke-GateStep -Name 'release-manifest-final' -NpmArgs @('run', 'manifest:verif
36
36
  Write-Host '==== windows-gate-evidence ===='
37
37
  & node.exe (Join-Path $PSScriptRoot 'windows-gate-evidence.mjs')
38
38
  if ($LASTEXITCODE -ne 0) {
39
- throw "DEADBYTE V0.8.7 / R33 gate failed at windows-gate-evidence with exit code $LASTEXITCODE"
39
+ throw "DEADBYTE V0.8.9 / R33 gate failed at windows-gate-evidence with exit code $LASTEXITCODE"
40
40
  }
41
41
 
42
- Write-Host '==== DEADBYTE V0.8.7 / R33 WINDOWS BATCH GATE: PASS ===='
42
+ Write-Host '==== DEADBYTE V0.8.9 / R33 WINDOWS BATCH GATE: PASS ===='
@@ -7,7 +7,7 @@ import { fileURLToPath } from 'node:url';
7
7
  const here=path.dirname(fileURLToPath(import.meta.url));
8
8
  const root=path.resolve(here,'..');
9
9
  const sha256=bytes=>createHash('sha256').update(bytes).digest('hex');
10
- const marker='DEADBYTE V0.8.7 / R33 WINDOWS BATCH GATE: PASS';
10
+ const marker='DEADBYTE V0.8.9 / R33 WINDOWS BATCH GATE: PASS';
11
11
  function insist(ok,message){if(!ok) throw new Error(message);}
12
12
 
13
13
  export async function writeWindowsGateEvidence({
@@ -19,11 +19,11 @@ export async function writeWindowsGateEvidence({
19
19
  const manifestBytes=await readFile(path.join(candidate,'MANIFEST.SHA256'));
20
20
  const manifestSha256=sha256(manifestBytes);
21
21
  const pkg=JSON.parse(await readFile(path.join(candidate,'package.json'),'utf8'));
22
- insist(pkg.version==='0.8.7',`Windows gate evidence requires V0.8.7, found ${pkg.version}`);
22
+ insist(pkg.version==='0.8.9',`Windows gate evidence requires V0.8.9, found ${pkg.version}`);
23
23
  const completed=clock();
24
24
  insist(completed instanceof Date&&!Number.isNaN(completed.getTime()),'Windows gate evidence clock invalid');
25
25
  const evidence={
26
- schema:'deadbyte.r33-windows-gate.v1',status:'passed',exit_code:0,version:'0.8.7',
26
+ schema:'deadbyte.r33-windows-gate.v1',status:'passed',exit_code:0,version:'0.8.9',
27
27
  marker,completed_at_utc:completed.toISOString(),manifest_sha256:manifestSha256
28
28
  };
29
29
  const bytes=Buffer.from(`${JSON.stringify(evidence,null,2)}\n`,'utf8');
@@ -39,7 +39,7 @@ export async function writeWindowsGateEvidence({
39
39
  const existingBytes=await readFile(file);
40
40
  const existing=JSON.parse(existingBytes.toString('utf8'));
41
41
  insist(existing.schema===evidence.schema&&existing.status==='passed'&&existing.exit_code===0,'existing Windows gate evidence invalid');
42
- insist(existing.version==='0.8.7'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing Windows gate evidence binding mismatch');
42
+ insist(existing.version==='0.8.9'&&existing.marker===marker&&existing.manifest_sha256===manifestSha256,'existing Windows gate evidence binding mismatch');
43
43
  return {status:'existing',path:file,sha256:sha256(existingBytes),manifest_sha256:manifestSha256};
44
44
  }
45
45
  }
@@ -1,8 +1,11 @@
1
1
  #!/usr/bin/env node
2
- import { lstat } from 'node:fs/promises';
3
- import { spawnSync } from 'node:child_process';
2
+ import { lstat, mkdir, open, readFile, readdir, rm, stat, writeFile } from 'node:fs/promises';
3
+ import { spawn, spawnSync } from 'node:child_process';
4
+ import { randomBytes } from 'node:crypto';
4
5
  import os from 'node:os';
5
6
  import path from 'node:path';
7
+ import { setTimeout as sleep } from 'node:timers/promises';
8
+ import { fileURLToPath } from 'node:url';
6
9
  import { defaultPublicKeyPath } from './trust.mjs';
7
10
  import { verifyAuditChain } from './audit-verify.mjs';
8
11
 
@@ -11,30 +14,217 @@ const REMOTE_COMMANDS=new Set([
11
14
  'armprocess','disarmprocess','processstatus','armhost','disarmhost','hoststatus','hosttest',
12
15
  'armcoding','disarmcoding','codingstatus','codingtest','armautonomous','disarmautonomous','autonomousstatus','autonomoustest'
13
16
  ]);
17
+ const SESSION_START=['start','armprocess','armcoding','armautonomous','armhost'];
18
+ const SESSION_STATUS=[
19
+ ['processstatus',/^Process plane\s*:\s*ARMED\b/m],
20
+ ['codingstatus',/^Coding plane\s*:\s*ARMED\b/m],
21
+ ['autonomousstatus',/^Autonomous\s*:\s*ARMED\b/m],
22
+ ['hoststatus',/^Host plane\s*:\s*ARMED\b/m]
23
+ ];
24
+ const SESSION_CLEANUP=['disarmautonomous','disarmcoding','disarmprocess','disarmhost','park'];
14
25
 
15
26
  function stateRoot(){return path.resolve(process.env.DEADBYTE_STATE_ROOT||path.join(os.homedir(),'.deadbyte-mcp'));}
16
27
  async function requireRegular(file,label){const info=await lstat(file);if(!info.isFile()||info.isSymbolicLink()) throw new Error(`${label} must be a regular non-symlink file`);return file;}
17
28
  function printHelp(){
18
29
  process.stdout.write([
19
30
  'DEADBYTE MCP CLI','',
20
- ' npx deadbyte-mcp@latest remote',
31
+ ' npx deadbyte-mcp@latest remote # foreground device session',
21
32
  ' npx deadbyte-mcp@latest remote status',
22
33
  ' npx deadbyte-mcp@latest remote restart',
23
34
  ' npx deadbyte-mcp@latest audit verify',
24
35
  ' npx deadbyte-mcp@latest audit status',''
25
36
  ].join('\n'));
26
37
  }
27
- async function runRemote(args){
38
+ async function remoteContext(){
28
39
  if(process.platform!=='win32') throw new Error('remote controller is currently Windows-only');
29
- if(args.length>1) throw new Error('remote accepts at most one controller command');
30
- const command=args[0]||'start';if(!REMOTE_COMMANDS.has(command)) throw new Error(`unsupported remote command '${command}'`);
31
40
  const root=stateRoot();
32
41
  const launcher=await requireRegular(path.join(root,'bin','DEADBYTE-MCP.ps1'),'stable DEADBYTE launcher');
33
42
  const systemRoot=process.env.SystemRoot||process.env.WINDIR;if(!systemRoot) throw new Error('SystemRoot/WINDIR missing');
34
43
  const powershell=await requireRegular(path.join(systemRoot,'System32','WindowsPowerShell','v1.0','powershell.exe'),'Windows PowerShell');
35
- const result=spawnSync(powershell,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,command],{stdio:'inherit',windowsHide:false,shell:false});
44
+ return {root,launcher,powershell};
45
+ }
46
+ function invokeController(ctx,command,{capture=false,allowFailure=false}={}){
47
+ const result=spawnSync(ctx.powershell,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',ctx.launcher,command],{
48
+ stdio:capture?['ignore','pipe','pipe']:'inherit',encoding:capture?'utf8':undefined,windowsHide:false,shell:false
49
+ });
36
50
  if(result.error) throw result.error;
37
- process.exitCode=Number.isInteger(result.status)?result.status:1;
51
+ const status=Number.isInteger(result.status)?result.status:1;
52
+ const stdout=capture?String(result.stdout||''):'';
53
+ const stderr=capture?String(result.stderr||''):'';
54
+ if(capture){if(stdout) process.stdout.write(stdout);if(stderr) process.stderr.write(stderr);}
55
+ if(status!==0&&!allowFailure) throw new Error(`remote controller '${command}' failed exit=${status}`);
56
+ return {status,stdout,stderr};
57
+ }
58
+ async function listLogFiles(root){
59
+ const files=[];
60
+ const audit=path.join(root,'audit');
61
+ const runtimeLogs=path.join(root,'runtime','logs');
62
+ for(const [dir,accept] of [
63
+ [audit,name=>/^segment-\d{8}\.jsonl$/.test(name)],
64
+ [runtimeLogs,name=>/\.log$/i.test(name)]
65
+ ]){
66
+ let names=[];try{names=await readdir(dir);}catch(error){if(error?.code==='ENOENT') continue;throw error;}
67
+ for(const name of names.sort()) if(accept(name)) files.push(path.join(dir,name));
68
+ }
69
+ return files;
70
+ }
71
+ function formatLogLine(file,line){
72
+ if(!line) return null;
73
+ if(/^segment-\d{8}\.jsonl$/.test(path.basename(file))){
74
+ try{
75
+ const event=JSON.parse(line);
76
+ const seq=Number.isInteger(event.seq)?`#${event.seq}`:'#?';
77
+ const request=typeof event.request_id==='string'?` ${event.request_id.slice(0,8)}`:'';
78
+ const tool=typeof event.tool==='string'?` ${event.tool}`:'';
79
+ return `[AUDIT ${seq}] ${event.event_type||'event'}${tool}${request}`;
80
+ }catch{return `[AUDIT] ${line}`;}
81
+ }
82
+ const label=path.basename(file).replace(/\.log$/i,'').toUpperCase();
83
+ return `[${label}] ${line}`;
84
+ }
85
+ async function readAppend(file,offset){
86
+ let info;try{info=await stat(file);}catch(error){if(error?.code==='ENOENT') return {offset:0,text:''};throw error;}
87
+ let start=offset;
88
+ if(info.size<start) start=0;
89
+ if(info.size===start) return {offset:start,text:''};
90
+ const length=info.size-start;
91
+ const buffer=Buffer.alloc(length);
92
+ const handle=await open(file,'r');
93
+ try{await handle.read(buffer,0,length,start);}finally{await handle.close();}
94
+ return {offset:info.size,text:buffer.toString('utf8')};
95
+ }
96
+ async function tailRemoteLogs(root,signal){
97
+ const offsets=new Map();
98
+ for(const file of await listLogFiles(root)){
99
+ try{offsets.set(file,(await stat(file)).size);}catch{}
100
+ }
101
+ process.stdout.write('📜 [LOG] Live logs active. Press Ctrl+C to disconnect and revoke authority.\n');
102
+ while(!signal.aborted){
103
+ for(const file of await listLogFiles(root)){
104
+ const prior=offsets.has(file)?offsets.get(file):0;
105
+ const next=await readAppend(file,prior);
106
+ offsets.set(file,next.offset);
107
+ for(const line of next.text.split(/\r?\n/)){
108
+ const formatted=formatLogLine(file,line);
109
+ if(formatted) process.stdout.write(`${formatted}\n`);
110
+ }
111
+ }
112
+ try{await sleep(250,null,{signal});}catch(error){if(error?.name!=='AbortError') throw error;}
113
+ }
114
+ }
115
+ async function cleanupRemoteSession(ctx){
116
+ for(const command of SESSION_CLEANUP){
117
+ try{invokeController(ctx,command,{allowFailure:true});}
118
+ catch(error){process.stderr.write(`[WARN] cleanup ${command}: ${String(error?.message||error)}\n`);}
119
+ }
120
+ }
121
+ const GUARDIAN_SCRIPT=fileURLToPath(new URL('./remote-session-guardian.mjs',import.meta.url));
122
+ function remoteSessionMarker(root){return path.join(root,'runtime','remote-session.json');}
123
+ function pidAlive(pid){
124
+ try{process.kill(pid,0);return true;}
125
+ catch(error){return error?.code!=='ESRCH';}
126
+ }
127
+ function remoteOwnerPid(marker){return marker?.schema==='deadbyte.remote-session.v2'?marker.owner_pid:marker?.pid;}
128
+ async function readRemoteSessionMarker(root){
129
+ const file=remoteSessionMarker(root);
130
+ let text;try{text=await readFile(file,'utf8');}catch(error){if(error?.code==='ENOENT') return null;throw error;}
131
+ let value;try{value=JSON.parse(text);}catch{throw new Error('remote session marker is invalid JSON');}
132
+ if(value?.schema==='deadbyte.remote-session.v1'){
133
+ if(!Number.isInteger(value?.pid)||value.pid<1) throw new Error('remote session marker schema invalid');
134
+ return value;
135
+ }
136
+ if(value?.schema==='deadbyte.remote-session.v2'){
137
+ if(!Number.isInteger(value?.owner_pid)||value.owner_pid<1||
138
+ !/^[0-9a-f]{64}$/.test(String(value?.session_nonce||''))||
139
+ !Number.isInteger(value?.guardian_pid)||value.guardian_pid<1) throw new Error('remote session marker schema invalid');
140
+ return value;
141
+ }
142
+ throw new Error('remote session marker schema invalid');
143
+ }
144
+ async function recoverRemoteSession(ctx){
145
+ let prior=await readRemoteSessionMarker(ctx.root);
146
+ if(!prior) return;
147
+ const ownerPid=remoteOwnerPid(prior);
148
+ if(pidAlive(ownerPid)) throw new Error(`remote session already active pid=${ownerPid}`);
149
+ if(prior.schema==='deadbyte.remote-session.v2'&&pidAlive(prior.guardian_pid)){
150
+ const deadline=Date.now()+3000;
151
+ while(Date.now()<deadline){
152
+ await sleep(100);
153
+ prior=await readRemoteSessionMarker(ctx.root);
154
+ if(!prior) return;
155
+ if(remoteOwnerPid(prior)!==ownerPid) throw new Error('remote session ownership changed during guardian recovery');
156
+ }
157
+ throw new Error(`remote session guardian cleanup still active pid=${prior.guardian_pid}`);
158
+ }
159
+ process.stdout.write(`[RECOVERY] Stale remote session pid=${ownerPid}; revoking authority before restart.\n`);
160
+ await cleanupRemoteSession(ctx);
161
+ await rm(remoteSessionMarker(ctx.root),{force:true});
162
+ }
163
+ async function claimRemoteSession(ctx){
164
+ const dir=path.join(ctx.root,'runtime');await mkdir(dir,{recursive:true});
165
+ const nonce=randomBytes(32).toString('hex');
166
+ const marker={schema:'deadbyte.remote-session.v2',owner_pid:process.pid,guardian_pid:0,session_nonce:nonce,started_at_utc:new Date().toISOString()};
167
+ const markerPath=remoteSessionMarker(ctx.root);
168
+ try{await writeFile(markerPath,`${JSON.stringify(marker)}\n`,{encoding:'utf8',flag:'wx'});}
169
+ catch(error){if(error?.code==='EEXIST') throw new Error('remote session already claimed');throw error;}
170
+ let guardian=null;
171
+ try{
172
+ guardian=spawn(process.execPath,[GUARDIAN_SCRIPT,'--state-root',ctx.root,'--owner-pid',String(process.pid),'--session-nonce',nonce],{
173
+ detached:true,stdio:'ignore',windowsHide:true,shell:false,env:process.env
174
+ });
175
+ if(!Number.isInteger(guardian.pid)||guardian.pid<1) throw new Error('remote session guardian did not start');
176
+ marker.guardian_pid=guardian.pid;
177
+ await writeFile(markerPath,`${JSON.stringify(marker)}\n`,'utf8');
178
+ guardian.unref();
179
+ return marker;
180
+ }catch(error){
181
+ try{guardian?.kill();}catch{}
182
+ await rm(markerPath,{force:true});
183
+ throw error;
184
+ }
185
+ }
186
+ async function releaseRemoteSession(ctx,marker){
187
+ let current=null;try{current=await readRemoteSessionMarker(ctx.root);}catch(error){process.stderr.write(`[WARN] remote session marker cleanup: ${String(error?.message||error)}\n`);return;}
188
+ const same=current?.schema==='deadbyte.remote-session.v2'&&current.owner_pid===marker.owner_pid&&current.session_nonce===marker.session_nonce;
189
+ if(same) await rm(remoteSessionMarker(ctx.root),{force:true});
190
+ }
191
+ async function runRemoteSession(){
192
+ const ctx=await remoteContext();
193
+ await recoverRemoteSession(ctx);
194
+ const marker=await claimRemoteSession(ctx);
195
+ const aborter=new AbortController();
196
+ const abort=()=>aborter.abort();
197
+ const watchStdin=!process.stdin.isTTY;
198
+ process.once('SIGINT',abort);process.once('SIGTERM',abort);
199
+ if(watchStdin){process.stdin.once('end',abort);process.stdin.once('close',abort);process.stdin.resume();}
200
+ let ready=false;
201
+ try{
202
+ process.stdout.write('DEADBYTE MCP DEVICE SESSION\n');
203
+ process.stdout.write('[INFO] Starting local + remote runtime...\n');
204
+ for(const command of SESSION_START) invokeController(ctx,command);
205
+ process.stdout.write('[AUTHORITY] Verifying runtime planes...\n');
206
+ for(const [command,pattern] of SESSION_STATUS){
207
+ const result=invokeController(ctx,command,{capture:true});
208
+ if(!pattern.test(result.stdout)) throw new Error(`remote session authority verification failed: ${command} did not report ARMED`);
209
+ }
210
+ ready=true;
211
+ process.stdout.write('[DEVICE] DEADBYTE MCP online; all authority planes ARMED.\n');
212
+ await tailRemoteLogs(ctx.root,aborter.signal);
213
+ }finally{
214
+ process.removeListener('SIGINT',abort);process.removeListener('SIGTERM',abort);
215
+ if(watchStdin){process.stdin.removeListener('end',abort);process.stdin.removeListener('close',abort);process.stdin.pause();}
216
+ if(ready||!aborter.signal.aborted) process.stdout.write('[INFO] Closing remote device session and revoking authority...\n');
217
+ await cleanupRemoteSession(ctx);
218
+ await releaseRemoteSession(ctx,marker);
219
+ }
220
+ }
221
+ async function runRemote(args){
222
+ if(args.length===0){await runRemoteSession();return;}
223
+ if(args.length>1) throw new Error('remote accepts at most one controller command');
224
+ const command=args[0];if(!REMOTE_COMMANDS.has(command)) throw new Error(`unsupported remote command '${command}'`);
225
+ const ctx=await remoteContext();
226
+ const result=invokeController(ctx,command,{allowFailure:true});
227
+ process.exitCode=result.status;
38
228
  }
39
229
  async function runAudit(args){
40
230
  const command=args[0]||'status';if(args.length>1) throw new Error('audit accepts one command');
@@ -67,7 +67,7 @@ export async function runR33Parity(endpointText) {
67
67
  if (endpoint.protocol !== 'https:') throw new Error('R33 parity requires HTTPS');
68
68
  const pointer = JSON.parse(await readFile(path.join(stateRoot, 'active-release.json'), 'utf8'));
69
69
  const policy = await loadCodingPolicy(path.join(pointer.generation_root, 'coding-policy.json'));
70
- const client = new Client({ name:'deadbyte-r33-parity', version:'0.8.7' },
70
+ const client = new Client({ name:'deadbyte-r33-parity', version:'0.8.9' },
71
71
  { versionNegotiation:{ mode:{ pin:'2026-07-28' } } });
72
72
  const transport = new StreamableHTTPClientTransport(endpoint);
73
73
  const tag = randomBytes(8).toString('hex');
@@ -0,0 +1,76 @@
1
+ #!/usr/bin/env node
2
+ import { lstat, readFile, rm } from 'node:fs/promises';
3
+ import { spawnSync } from 'node:child_process';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { setTimeout as sleep } from 'node:timers/promises';
7
+
8
+ const CLEANUP=['disarmautonomous','disarmcoding','disarmprocess','disarmhost','park'];
9
+
10
+ function parseArgs(argv){
11
+ const out={};
12
+ for(let i=0;i<argv.length;i+=2){
13
+ const key=argv[i],value=argv[i+1];
14
+ if(!key?.startsWith('--')||value===undefined) throw new Error('guardian arguments must be key/value pairs');
15
+ out[key.slice(2)]=value;
16
+ }
17
+ return out;
18
+ }
19
+ async function requireRegular(file,label){
20
+ const info=await lstat(file);
21
+ if(!info.isFile()||info.isSymbolicLink()) throw new Error(`${label} must be a regular non-symlink file`);
22
+ return file;
23
+ }
24
+ function pidAlive(pid){
25
+ try{process.kill(pid,0);return true;}
26
+ catch(error){return error?.code!=='ESRCH';}
27
+ }
28
+ async function readMarker(file){
29
+ let text;
30
+ try{text=await readFile(file,'utf8');}
31
+ catch(error){if(error?.code==='ENOENT') return null;throw error;}
32
+ try{return JSON.parse(text);}catch{return null;}
33
+ }
34
+ function markerMatches(marker,{ownerPid,nonce}){
35
+ return marker?.schema==='deadbyte.remote-session.v2'&&
36
+ marker?.owner_pid===ownerPid&&
37
+ marker?.session_nonce===nonce;
38
+ }
39
+ function invokeCleanup(powershell,launcher,command){
40
+ const result=spawnSync(powershell,['-NoLogo','-NoProfile','-ExecutionPolicy','Bypass','-File',launcher,command],{
41
+ stdio:'ignore',windowsHide:true,shell:false
42
+ });
43
+ return !result.error&&result.status===0;
44
+ }
45
+
46
+ async function main(){
47
+ if(process.platform!=='win32') return;
48
+ const args=parseArgs(process.argv.slice(2));
49
+ const root=path.resolve(args['state-root']||path.join(os.homedir(),'.deadbyte-mcp'));
50
+ const ownerPid=Number(args['owner-pid']);
51
+ const nonce=String(args['session-nonce']||'');
52
+ if(!Number.isInteger(ownerPid)||ownerPid<1) throw new Error('guardian owner pid invalid');
53
+ if(!/^[0-9a-f]{64}$/.test(nonce)) throw new Error('guardian session nonce invalid');
54
+
55
+ const launcher=await requireRegular(path.join(root,'bin','DEADBYTE-MCP.ps1'),'stable DEADBYTE launcher');
56
+ const systemRoot=process.env.SystemRoot||process.env.WINDIR;
57
+ if(!systemRoot) throw new Error('SystemRoot/WINDIR missing');
58
+ const powershell=await requireRegular(path.join(systemRoot,'System32','WindowsPowerShell','v1.0','powershell.exe'),'Windows PowerShell');
59
+ const markerPath=path.join(root,'runtime','remote-session.json');
60
+
61
+ for(;;){
62
+ const marker=await readMarker(markerPath);
63
+ if(!marker||!markerMatches(marker,{ownerPid,nonce})) return;
64
+ if(pidAlive(ownerPid)){
65
+ await sleep(250);
66
+ continue;
67
+ }
68
+
69
+ for(const command of CLEANUP) invokeCleanup(powershell,launcher,command);
70
+ const current=await readMarker(markerPath);
71
+ if(current&&markerMatches(current,{ownerPid,nonce})) await rm(markerPath,{force:true});
72
+ return;
73
+ }
74
+ }
75
+
76
+ main().catch(()=>{process.exitCode=1;});
package/src/version.mjs CHANGED
@@ -1,4 +1,4 @@
1
- export const DEADBYTE_VERSION = '0.8.7';
1
+ export const DEADBYTE_VERSION = '0.8.9';
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';
@@ -105,7 +105,7 @@ test('proof execution creates deterministic output and signed verifiable receipt
105
105
  assert.equal(result.receipt.request.schema, 'deadbyte.request.v1');
106
106
  assert.match(result.receipt.request.nonce, /^[0-9a-f]{64}$/);
107
107
  assert.match(result.receipt.request.sha256, /^[0-9a-f]{64}$/);
108
- assert.equal(result.receipt.runtime.package_version, '0.8.7');
108
+ assert.equal(result.receipt.runtime.package_version, '0.8.9');
109
109
  assert.match(result.receipt.runtime.mcp_ingress_adapter, /^(?:none|tunnel-stdio-probe-compat-v1)$/);
110
110
  assert.match(result.receipt.runtime.mcp_ingress_adapter_sha256, /^[0-9a-f]{64}$/);
111
111
  assert.match(result.receipt.runtime.mcp_ingress_mode_source_sha256, /^[0-9a-f]{64}$/);
@@ -103,7 +103,7 @@ test('Windows R33 gate emits manifest-bound machine-readable evidence outside th
103
103
  assert.equal(evidence.status,'passed');
104
104
  assert.equal(evidence.exit_code,0);
105
105
  assert.equal(evidence.manifest_sha256,manifestSha);
106
- assert.equal(evidence.marker,'DEADBYTE V0.8.7 / R33 WINDOWS BATCH GATE: PASS');
106
+ assert.equal(evidence.marker,'DEADBYTE V0.8.9 / R33 WINDOWS BATCH GATE: PASS');
107
107
  assert.ok(summary.path.startsWith(path.join(stateRoot,'evidence','release')));
108
108
  }finally{await rm(stateRoot,{recursive:true,force:true});}
109
109
  });
@@ -30,8 +30,8 @@ test('final closure verifier is implementation-independent from production helpe
30
30
  assert.match(verifier, /DEADBYTE-R27-SLOT-UPDATE-V1/);
31
31
  assert.match(verifier, /DEADBYTE-MACHINE-PING-V1/);
32
32
  assert.match(verifier, /DEADBYTE-R33-FINAL-CLOSURE-V1/);
33
- assert.match(writer, /pointer\.version==='0\.8\.7'/);
34
- assert.match(verifier, /envelope\.body\.version==='0\.8\.7'/);
33
+ assert.match(writer, /pointer\.version==='0\.8\.9'/);
34
+ assert.match(verifier, /envelope\.body\.version==='0\.8\.9'/);
35
35
  assert.match(verifier, /release_label==='R33 FINAL MACHINE BASELINE'/);
36
36
  });
37
37
 
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'node:url';
6
6
  const root = new URL('../', import.meta.url);
7
7
  const text = rel => readFile(new URL(rel, root), 'utf8');
8
8
 
9
- test('R33 final release authority agrees on V0.8.7 / R33 identity', async () => {
9
+ test('R33 patch successor release authority agrees on V0.8.9 / R33 identity', async () => {
10
10
  const pkg = JSON.parse(await text('package.json'));
11
11
  const [version, controller, gate, readme, architecture, controllerReadme, deferred, parity, closure, closureVerify] = await Promise.all([
12
12
  text('src/version.mjs'),
@@ -20,16 +20,16 @@ test('R33 final release authority agrees on V0.8.7 / R33 identity', async () =>
20
20
  text('scripts/final-closure.mjs'),
21
21
  text('scripts/final-closure-verify.mjs')
22
22
  ]);
23
- assert.equal(pkg.version, '0.8.7');
24
- assert.match(version, /DEADBYTE_VERSION = '0\.8\.7'/);
25
- assert.match(controller, /CONTROLLER V0\.8\.7 \/ R33/);
26
- assert.match(gate, /DEADBYTE V0\.8\.7 \/ R33 WINDOWS BATCH GATE: PASS/);
27
- assert.match(readme, /^DEADBYTE MCP V0\.8\.7 \/ R33 FINAL MACHINE BASELINE$/m);
28
- assert.match(architecture, /^# DEADBYTE MCP V0\.8\.7 \/ R33 ARCHITECTURE$/m);
29
- assert.match(controllerReadme, /^DEADBYTE MCP V0\.8\.7 \/ R33 CONTROLLER$/m);
23
+ assert.equal(pkg.version, '0.8.9');
24
+ assert.match(version, /DEADBYTE_VERSION = '0\.8\.9'/);
25
+ assert.match(controller, /CONTROLLER V0\.8\.9 \/ R33/);
26
+ assert.match(gate, /DEADBYTE V0\.8\.9 \/ R33 WINDOWS BATCH GATE: PASS/);
27
+ assert.match(readme, /^DEADBYTE MCP V0\.8\.9 \/ R33 FINAL MACHINE BASELINE$/m);
28
+ assert.match(architecture, /^# DEADBYTE MCP V0\.8\.9 \/ R33 ARCHITECTURE$/m);
29
+ assert.match(controllerReadme, /^DEADBYTE MCP V0\.8\.9 \/ R33 CONTROLLER$/m);
30
30
  assert.match(controllerReadme, /68-tool public catalog/);
31
- assert.match(deferred, /--expected-version','0\.8\.7'/);
32
- assert.match(parity, /version:'0\.8\.7'/);
33
- assert.match(closure, /version:'0\.8\.7'/);
34
- assert.match(closureVerify, /DEADBYTE V0\.8\.7 \/ R33 WINDOWS BATCH GATE: PASS/);
31
+ assert.match(deferred, /--expected-version','0\.8\.9'/);
32
+ assert.match(parity, /version:'0\.8\.9'/);
33
+ assert.match(closure, /version:'0\.8\.9'/);
34
+ assert.match(closureVerify, /DEADBYTE V0\.8\.9 \/ R33 WINDOWS BATCH GATE: PASS/);
35
35
  });
@@ -0,0 +1,215 @@
1
+ import test from 'node:test';
2
+ import assert from 'node:assert/strict';
3
+ import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises';
4
+ import os from 'node:os';
5
+ import path from 'node:path';
6
+ import { spawn } from 'node:child_process';
7
+ import { fileURLToPath } from 'node:url';
8
+
9
+ const root=path.resolve(fileURLToPath(new URL('..',import.meta.url)));
10
+ const cliPath=path.join(root,'src','deadbyte-cli.mjs');
11
+ const isWindows=process.platform==='win32';
12
+ const sleep=ms=>new Promise(resolve=>setTimeout(resolve,ms));
13
+
14
+ async function fixture({failCommand=''}={}){
15
+ const dir=await mkdtemp(path.join(os.tmpdir(),'deadbyte-remote-session-'));
16
+ const state=path.join(dir,'state');
17
+ const bin=path.join(state,'bin');
18
+ const trace=path.join(dir,'trace.txt');
19
+ await mkdir(bin,{recursive:true});
20
+ const launcher=path.join(bin,'DEADBYTE-MCP.ps1');
21
+ const ps1=`param([string]$Command)\nAdd-Content -LiteralPath $env:DEADBYTE_REMOTE_TEST_TRACE -Value $Command -Encoding UTF8\nif ($env:DEADBYTE_REMOTE_TEST_FAIL -and $Command -eq $env:DEADBYTE_REMOTE_TEST_FAIL) { Write-Error \"forced $Command failure\"; exit 9 }\nswitch ($Command) {\n 'start' { Write-Host 'DEADBYTE MCP = ONLINE' }\n 'processstatus' { Write-Host 'Process plane : ARMED [standing-grant]' }\n 'codingstatus' { Write-Host 'Coding plane : ARMED [standing-grant]' }\n 'autonomousstatus' { Write-Host 'Autonomous : ARMED [standing-grant]' }\n 'hoststatus' { Write-Host 'Host plane : ARMED [standing-grant]' }\n 'park' { Write-Host 'DEADBYTE MCP = PARKED' }\n default { Write-Host \"$Command OK\" }\n}\nexit 0\n`;
22
+ await writeFile(launcher,ps1,'utf8');
23
+ return {dir,state,trace,env:{...process.env,DEADBYTE_STATE_ROOT:state,DEADBYTE_REMOTE_TEST_TRACE:trace,DEADBYTE_REMOTE_TEST_FAIL:failCommand}};
24
+ }
25
+
26
+ async function traceCommands(file,{read=readFile,retryDelayMs=10,maxAttempts=20}={}){
27
+ for(let attempt=1;attempt<=maxAttempts;attempt+=1){
28
+ try{return (await read(file,'utf8')).split(/\r?\n/).map(x=>x.trim()).filter(Boolean);}
29
+ catch(error){
30
+ if(error?.code==='ENOENT') return [];
31
+ if(error?.code==='EBUSY'&&attempt<maxAttempts){await sleep(retryDelayMs);continue;}
32
+ throw error;
33
+ }
34
+ }
35
+ return [];
36
+ }
37
+ async function waitFor(file,predicate,{timeoutMs=6000}={}){
38
+ const deadline=Date.now()+timeoutMs;
39
+ while(Date.now()<deadline){
40
+ const commands=await traceCommands(file);
41
+ if(predicate(commands)) return commands;
42
+ await sleep(50);
43
+ }
44
+ return traceCommands(file);
45
+ }
46
+
47
+ test('trace reader tolerates a transient Windows sharing violation and returns complete commands',async()=>{
48
+ let calls=0;
49
+ const fakeRead=async()=>{
50
+ calls+=1;
51
+ if(calls<3){const error=new Error('simulated sharing violation');error.code='EBUSY';throw error;}
52
+ return 'start\narmprocess\n';
53
+ };
54
+ const commands=await traceCommands('ignored',{read:fakeRead,retryDelayMs:1,maxAttempts:4});
55
+ assert.deepEqual(commands,['start','armprocess']);
56
+ assert.equal(calls,3);
57
+ });
58
+ async function collect(child){
59
+ let stdout='',stderr='';
60
+ child.stdout?.on('data',chunk=>{stdout+=chunk;});
61
+ child.stderr?.on('data',chunk=>{stderr+=chunk;});
62
+ const closed=new Promise(resolve=>child.once('close',(code,signal)=>resolve({code,signal})));
63
+ return {closed,get stdout(){return stdout;},get stderr(){return stderr;}};
64
+ }
65
+
66
+ const winTest=isWindows?test:test.skip;
67
+
68
+ winTest('bare remote arms every authority plane, verifies ARMED state, streams logs, then disarms and parks when the foreground session ends',async()=>{
69
+ const fx=await fixture();
70
+ let child=null;
71
+ try{
72
+ child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
73
+ const output=await collect(child);
74
+ const ready=await waitFor(fx.trace,commands=>commands.includes('hoststatus'));
75
+ assert.deepEqual(ready.slice(0,9),[
76
+ 'start','armprocess','armcoding','armautonomous','armhost',
77
+ 'processstatus','codingstatus','autonomousstatus','hoststatus'
78
+ ]);
79
+ assert.equal(child.exitCode,null,'remote session must remain foreground after becoming ready');
80
+ const tailDeadline=Date.now()+3000;
81
+ while(Date.now()<tailDeadline&&!/📜 \[LOG\] Live logs active/.test(output.stdout)) await sleep(50);
82
+ assert.match(output.stdout,/📜 \[LOG\] Live logs active\. Press Ctrl\+C to disconnect and revoke authority\./);
83
+ const auditDir=path.join(fx.state,'audit');
84
+ await mkdir(auditDir,{recursive:true});
85
+ await writeFile(path.join(auditDir,'segment-00000001.jsonl'),`${JSON.stringify({seq:1,event_type:'tool.request',tool:'coding_tree',request_id:'abcdef1234567890'})}\n`,'utf8');
86
+ const logDeadline=Date.now()+3000;
87
+ while(Date.now()<logDeadline&&!/\[AUDIT #1\] tool\.request coding_tree abcdef12/.test(output.stdout)) await sleep(50);
88
+ assert.match(output.stdout,/\[AUDIT #1\] tool\.request coding_tree abcdef12/);
89
+ child.stdin.end();
90
+ const ended=await Promise.race([output.closed,sleep(6000).then(()=>({timeout:true}))]);
91
+ assert.equal(ended.timeout,undefined,'remote session did not exit after foreground input closed');
92
+ const commands=await traceCommands(fx.trace);
93
+ assert.deepEqual(commands.slice(-5),['disarmautonomous','disarmcoding','disarmprocess','disarmhost','park']);
94
+ assert.match(output.stdout,/DEADBYTE MCP DEVICE SESSION/i);
95
+ assert.match(output.stdout,/Live logs/i);
96
+ const withoutLiveMarker=output.stdout.replace('📜 [LOG] Live logs active. Press Ctrl+C to disconnect and revoke authority.\n','');
97
+ assert.doesNotMatch(withoutLiveMarker,/\p{Extended_Pictographic}/u,'only the live-log marker may use emoji');
98
+ }finally{
99
+ if(child?.exitCode===null) child.kill();
100
+ await rm(fx.dir,{recursive:true,force:true});
101
+ }
102
+ });
103
+
104
+ winTest('detached guardian revokes authority, parks, and removes the session marker after the owner is hard-killed',async()=>{
105
+ const fx=await fixture();
106
+ let child=null;
107
+ try{
108
+ child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['ignore','pipe','pipe'],windowsHide:true});
109
+ const output=await collect(child);
110
+ const ready=await waitFor(fx.trace,commands=>commands.includes('hoststatus'));
111
+ assert.deepEqual(ready.slice(0,9),[
112
+ 'start','armprocess','armcoding','armautonomous','armhost',
113
+ 'processstatus','codingstatus','autonomousstatus','hoststatus'
114
+ ]);
115
+ const markerPath=path.join(fx.state,'runtime','remote-session.json');
116
+ const markerDeadline=Date.now()+3000;
117
+ let marker=null;
118
+ while(Date.now()<markerDeadline){
119
+ try{marker=JSON.parse(await readFile(markerPath,'utf8'));if(marker?.guardian_pid) break;}catch{}
120
+ await sleep(50);
121
+ }
122
+ assert.equal(marker?.schema,'deadbyte.remote-session.v2');
123
+ assert.equal(marker?.owner_pid,child.pid);
124
+ assert.match(String(marker?.session_nonce||''),/^[0-9a-f]{64}$/);
125
+ assert.ok(Number.isInteger(marker?.guardian_pid)&&marker.guardian_pid>0,'guardian pid missing from v2 marker');
126
+
127
+ const killed=child.kill('SIGKILL');
128
+ assert.equal(killed,true,'failed to hard-kill remote owner');
129
+ await Promise.race([output.closed,sleep(3000)]);
130
+
131
+ const cleaned=await waitFor(fx.trace,commands=>commands.slice(-5).join(',')==='disarmautonomous,disarmcoding,disarmprocess,disarmhost,park',{timeoutMs:10000});
132
+ assert.deepEqual(cleaned.slice(-5),['disarmautonomous','disarmcoding','disarmprocess','disarmhost','park']);
133
+ const markerGoneDeadline=Date.now()+3000;
134
+ let markerGone=false;
135
+ while(Date.now()<markerGoneDeadline){
136
+ try{await readFile(markerPath,'utf8');}catch(error){if(error?.code==='ENOENT'){markerGone=true;break;}throw error;}
137
+ await sleep(50);
138
+ }
139
+ assert.equal(markerGone,true,'guardian left stale remote-session marker after hard kill');
140
+ }finally{
141
+ if(child?.exitCode===null) child.kill();
142
+ await rm(fx.dir,{recursive:true,force:true});
143
+ }
144
+ });
145
+
146
+ winTest('bare remote rolls back authority and parks if one plane fails to arm',async()=>{
147
+ const fx=await fixture({failCommand:'armhost'});
148
+ try{
149
+ const child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['ignore','pipe','pipe'],windowsHide:true});
150
+ const output=await collect(child);
151
+ const ended=await Promise.race([output.closed,sleep(6000).then(()=>({timeout:true}))]);
152
+ assert.equal(ended.timeout,undefined,'failed startup did not terminate');
153
+ assert.notEqual(ended.code,0);
154
+ const commands=await traceCommands(fx.trace);
155
+ assert.deepEqual(commands.slice(0,5),['start','armprocess','armcoding','armautonomous','armhost']);
156
+ assert.deepEqual(commands.slice(-5),['disarmautonomous','disarmcoding','disarmprocess','disarmhost','park']);
157
+ assert.doesNotMatch(output.stdout,/Live logs/i);
158
+ }finally{await rm(fx.dir,{recursive:true,force:true});}
159
+ });
160
+
161
+ winTest('bare remote recovers a stale crashed session before arming a new one',async()=>{
162
+ const fx=await fixture();
163
+ let child=null;
164
+ try{
165
+ const runtime=path.join(fx.state,'runtime');
166
+ await mkdir(runtime,{recursive:true});
167
+ await writeFile(path.join(runtime,'remote-session.json'),JSON.stringify({schema:'deadbyte.remote-session.v1',pid:2147483000,started_at_utc:'2026-01-01T00:00:00.000Z'}),'utf8');
168
+ child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
169
+ const output=await collect(child);
170
+ const ready=await waitFor(fx.trace,commands=>commands.includes('hoststatus'));
171
+ assert.deepEqual(ready.slice(0,5),['disarmautonomous','disarmcoding','disarmprocess','disarmhost','park']);
172
+ assert.equal(ready[5],'start');
173
+ child.stdin.end();
174
+ const ended=await Promise.race([output.closed,sleep(6000).then(()=>({timeout:true}))]);
175
+ assert.equal(ended.timeout,undefined);
176
+ assert.equal(ended.code,0,output.stderr);
177
+ }finally{
178
+ if(child?.exitCode===null) child.kill();
179
+ await rm(fx.dir,{recursive:true,force:true});
180
+ }
181
+ });
182
+
183
+ winTest('bare remote refuses a second live foreground session without touching authority',async()=>{
184
+ const fx=await fixture();
185
+ let child=null;
186
+ try{
187
+ const runtime=path.join(fx.state,'runtime');
188
+ await mkdir(runtime,{recursive:true});
189
+ await writeFile(path.join(runtime,'remote-session.json'),JSON.stringify({schema:'deadbyte.remote-session.v1',pid:process.pid,started_at_utc:new Date().toISOString()}),'utf8');
190
+ child=spawn(process.execPath,[cliPath,'remote'],{env:fx.env,stdio:['pipe','pipe','pipe'],windowsHide:true});
191
+ const output=await collect(child);
192
+ await sleep(500);
193
+ if(child.exitCode===null) child.stdin.end();
194
+ const ended=await Promise.race([output.closed,sleep(6000).then(()=>({timeout:true}))]);
195
+ assert.equal(ended.timeout,undefined);
196
+ assert.notEqual(ended.code,0);
197
+ assert.deepEqual(await traceCommands(fx.trace),[]);
198
+ assert.match(output.stderr,/remote session already active/i);
199
+ }finally{
200
+ if(child?.exitCode===null) child.kill();
201
+ await rm(fx.dir,{recursive:true,force:true});
202
+ }
203
+ });
204
+
205
+ winTest('explicit remote status remains a one-shot controller command',async()=>{
206
+ const fx=await fixture();
207
+ try{
208
+ const child=spawn(process.execPath,[cliPath,'remote','status'],{env:fx.env,stdio:['ignore','pipe','pipe'],windowsHide:true});
209
+ const output=await collect(child);
210
+ const ended=await Promise.race([output.closed,sleep(6000).then(()=>({timeout:true}))]);
211
+ assert.equal(ended.timeout,undefined);
212
+ assert.equal(ended.code,0,output.stderr);
213
+ assert.deepEqual(await traceCommands(fx.trace),['status']);
214
+ }finally{await rm(fx.dir,{recursive:true,force:true});}
215
+ });