deadbyte-mcp 0.11.2 → 0.12.0

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 (102) hide show
  1. package/MANIFEST.SHA256 +101 -39
  2. package/README.txt +10 -9
  3. package/acceptance/r40/desktop-live-drift.mjs +106 -0
  4. package/acceptance/r40/desktop-live-ground.mjs +110 -0
  5. package/acceptance/r40/desktop-live-input.mjs +128 -0
  6. package/acceptance/r40/desktop-live-observe.mjs +100 -0
  7. package/acceptance/r40/desktop-live-uia.mjs +121 -0
  8. package/bin/deadbyte-desktop-input-r40.exe +0 -0
  9. package/bin/deadbyte-desktop-input-r40.obj +0 -0
  10. package/bin/deadbyte-desktop-observer-r40.exe +0 -0
  11. package/bin/deadbyte-desktop-observer-r40.obj +0 -0
  12. package/bin/deadbyte-desktop-uia-r40.exe +0 -0
  13. package/bin/deadbyte-desktop-uia-r40.obj +0 -0
  14. package/controller/README.TXT +7 -5
  15. package/controller/deadbyte-controller.config.psd1.example +3 -0
  16. package/controller/deadbyte-controller.ps1 +149 -4
  17. package/controller/deadbyte-desktop-policy.json +41 -0
  18. package/docs/ARCHITECTURE.md +6 -1
  19. package/docs/superpowers/specs/2026-09-19-r40-desktop-computer-use-design.md +214 -0
  20. package/native/deadbyte-desktop-input.cpp +455 -0
  21. package/native/deadbyte-desktop-observer.cpp +253 -0
  22. package/native/deadbyte-desktop-uia.cpp +307 -0
  23. package/package.json +10 -8
  24. package/scripts/build-desktop-r40.ps1 +106 -0
  25. package/scripts/deferred-slot-operation-r39.mjs +124 -0
  26. package/scripts/deferred-slot-operation-r40.mjs +126 -0
  27. package/scripts/final-closure-r39.mjs +113 -0
  28. package/scripts/final-closure-r40.mjs +114 -0
  29. package/scripts/final-closure-verify-r39.mjs +208 -0
  30. package/scripts/final-closure-verify-r40.mjs +212 -0
  31. package/scripts/gate-windows-r39.ps1 +42 -0
  32. package/scripts/gate-windows-r40.ps1 +43 -0
  33. package/scripts/init-desktop-policy-r40.ps1 +79 -0
  34. package/scripts/r40-compact-ledger-probe.mjs +4 -0
  35. package/scripts/release-parity-r39.mjs +200 -0
  36. package/scripts/release-parity-r40.mjs +212 -0
  37. package/scripts/release-parity-tests-r39.ps1 +9 -0
  38. package/scripts/release-parity-tests-r40.ps1 +9 -0
  39. package/scripts/windows-gate-evidence-r39.mjs +52 -0
  40. package/scripts/windows-gate-evidence-r40.mjs +52 -0
  41. package/src/autonomous-context-engine.mjs +2 -1
  42. package/src/autonomous-planner-contracts.mjs +19 -1
  43. package/src/autonomous-planner.mjs +22 -0
  44. package/src/autonomous-runtime.mjs +144 -24
  45. package/src/capability-ledger.mjs +6 -1
  46. package/src/compact-gateway-mcp-tools.mjs +14 -2
  47. package/src/deadbyte-cli.mjs +79 -47
  48. package/src/desktop-contract.mjs +263 -0
  49. package/src/desktop-input-r40.mjs +148 -0
  50. package/src/desktop-mcp-tools-r40.mjs +126 -0
  51. package/src/desktop-memory-r40.mjs +272 -0
  52. package/src/desktop-observer-r40.mjs +231 -0
  53. package/src/desktop-policy-r40.mjs +110 -0
  54. package/src/desktop-runtime-r40.mjs +318 -0
  55. package/src/desktop-uia-r40.mjs +123 -0
  56. package/src/mcp-server.mjs +74 -19
  57. package/src/r40-compact-disarmed-probe.mjs +71 -0
  58. package/src/r40-full-surface-probe.mjs +34 -0
  59. package/src/release-generation.mjs +38 -5
  60. package/src/remote-console.mjs +41 -0
  61. package/src/remote-live-log.mjs +215 -45
  62. package/src/remote-mcp-desktop-r40-smoke-client.mjs +70 -0
  63. package/src/remote-mcp-r40-parity-client.mjs +200 -0
  64. package/src/remote-mcp-smoke-client.mjs +20 -6
  65. package/src/remote-result-journal.mjs +10 -2
  66. package/src/runtime-update.mjs +55 -17
  67. package/src/tool-audit.mjs +29 -2
  68. package/src/version.mjs +1 -1
  69. package/test/autonomous-capability-r36.test.mjs +43 -1
  70. package/test/autonomous-desktop-r40.test.mjs +188 -0
  71. package/test/capability-ledger-r36.test.mjs +1 -0
  72. package/test/cli-entrypoint.test.mjs +1 -1
  73. package/test/compact-catalog-r36.test.mjs +2 -2
  74. package/test/compact-gateway-r36.test.mjs +56 -4
  75. package/test/compact-max-catalog-r40.test.mjs +95 -0
  76. package/test/controller-desktop-r40.test.mjs +85 -0
  77. package/test/core.test.mjs +1 -1
  78. package/test/desktop-adversarial-r40.test.mjs +62 -0
  79. package/test/desktop-contract-r40.test.mjs +200 -0
  80. package/test/desktop-input-r40.test.mjs +63 -0
  81. package/test/desktop-mcp-tools-r40.test.mjs +61 -0
  82. package/test/desktop-memory-r40.test.mjs +141 -0
  83. package/test/desktop-observer-r40.test.mjs +119 -0
  84. package/test/desktop-policy-r40.test.mjs +111 -0
  85. package/test/desktop-runtime-r40.test.mjs +278 -0
  86. package/test/desktop-uia-r40.test.mjs +42 -0
  87. package/test/full-max-catalog-r40.test.mjs +91 -0
  88. package/test/helpers/autonomous-r34-fixture.mjs +3 -3
  89. package/test/production-docs.test.mjs +3 -3
  90. package/test/r33-closeout-regression.test.mjs +8 -8
  91. package/test/r33-finalization.test.mjs +14 -14
  92. package/test/r37-remote-tool-call-timeline.test.mjs +7 -6
  93. package/test/r39-finalization.test.mjs +74 -0
  94. package/test/r40-finalization.test.mjs +118 -0
  95. package/test/release-generation.test.mjs +41 -9
  96. package/test/release-version.test.mjs +19 -58
  97. package/test/remote-console-r40.test.mjs +38 -0
  98. package/test/remote-live-log-r37.test.mjs +89 -32
  99. package/test/remote-r24-catalog.test.mjs +12 -3
  100. package/test/remote-result-journal.test.mjs +15 -0
  101. package/test/remote-session-cli.test.mjs +112 -55
  102. package/test/runtime-self-update.test.mjs +44 -0
@@ -134,6 +134,26 @@ function validateR36TypedAction(action) {
134
134
  if (typeof action.destination_path === 'string') pathFields.push(action.destination_path);
135
135
  for (const item of action.files ?? []) if (typeof item?.path === 'string') pathFields.push(item.path);
136
136
  for (const candidate of pathFields) validatePortableRelativePath(candidate);
137
+ if(action.kind==='desktop_target'){
138
+ const q=action.uia_query;
139
+ if(q.kind==='root'&&q.value!=='') throw new Error('desktop root query value must be empty');
140
+ if(q.kind!=='root'&&q.value.length<1) throw new Error('desktop UIA query value required');
141
+ if(q.ordinal>=q.max_results) throw new Error('desktop UIA ordinal must be below max_results');
142
+ }
143
+ if(action.kind==='desktop_action'){
144
+ const pointer=['pointer_move','click_primary','click_secondary','double_click_primary','scroll'].includes(action.action_kind);
145
+ if(pointer&&((action.target_id===null)===(action.point===null))) throw new Error('desktop pointer action requires exactly one of target_id or point');
146
+ if(!pointer&&(action.target_id!==null||action.point!==null)) throw new Error('desktop non-pointer action cannot carry target_id or point');
147
+ if(action.action_kind==='scroll'){
148
+ if(action.scroll_delta===null||action.scroll_delta===0) throw new Error('desktop scroll requires non-zero scroll_delta');
149
+ }else if(action.scroll_delta!==null) throw new Error('desktop non-scroll action cannot carry scroll_delta');
150
+ if(action.action_kind==='key_chord'){
151
+ if(action.key_chord===null||new Set(action.key_chord).size!==action.key_chord.length) throw new Error('desktop key_chord missing or duplicate');
152
+ }else if(action.key_chord!==null) throw new Error('desktop non-key action cannot carry key_chord');
153
+ if(action.action_kind==='text_input'){
154
+ if(action.text===null) throw new Error('desktop text_input requires text');
155
+ }else if(action.text!==null) throw new Error('desktop non-text action cannot carry text');
156
+ }
137
157
  return action;
138
158
  }
139
159
  function normalizeR36TypedAction(action) {
@@ -334,6 +354,7 @@ export function buildPlannerPrompt({ goal, policy, view }) {
334
354
  observations:view.observations ?? [],
335
355
  decisions:view.recent_decisions ?? [],
336
356
  projectMemory:view.project_memory ?? null,
357
+ desktopMemory:view.desktop_memory ?? null,
337
358
  capabilities:view.capabilities ?? null
338
359
  });
339
360
  const stateJson = canonicalJson(state);
@@ -350,6 +371,7 @@ export function buildPlannerPrompt({ goal, policy, view }) {
350
371
  'patch: operations are DEADBYTE Native Diff operations (write, replace, delete) and MUST use exact SHA-256 preconditions from supplied context.',
351
372
  'run_profile: profile_id must be one of the policy profile ids in state.',
352
373
  'delegate: operation is one bounded existing-runtime action. Allowed kinds: machine_stat, machine_list, machine_read, observation_search_start, observation_search_status, observation_search_page, observation_search_cancel, observation_process_list, observation_system_status, process_start, process_read, process_status, process_list, process_action, process_terminate.',
374
+ 'When Desktop capabilities are present, use typed desktop_status, desktop_observe, desktop_target with one bounded UIA query, and desktop_action. desktop_action requires separate Desktop authority; Autonomous authority never implies Desktop authority. After stale or unknown Desktop evidence, observe again and replan; never blindly repeat an action.',
353
375
  'Delegate paths are relative to the goal root. Process start accepts only profile_id and cwd; process_action accepts only session_id and fixed action_id. Never emit raw stdin, executable, PID, environment, shell command, or process arguments.',
354
376
  'finish only when the goal is actually satisfied. Required release profiles are enforced by the engine and cannot be bypassed.',
355
377
  'Never invent a file SHA. If content is elided or the current SHA/content is missing, choose inspect first.',
@@ -339,15 +339,16 @@ function normalizeGoal(policy, { submissionId, rootId, title = '', goal, accepta
339
339
  return Object.freeze({ ...body, goal_sha256:sha256Object(body) });
340
340
  }
341
341
 
342
- function configuredCapabilityManifest({machineFsRuntime,observationRuntime,processRuntime,semanticRuntime}) {
342
+ function configuredCapabilityManifest({machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime}) {
343
343
  const plannerReachable=id => id === 'coding.stat' || id === 'coding.search' || id === 'coding.files_read' ||
344
344
  id.startsWith('semantic.') || id.startsWith('native_diff.') || id.startsWith('process.') ||
345
- id.startsWith('machine_fs.') || id.startsWith('observation.') || id === 'autonomous.release_gate';
345
+ id.startsWith('machine_fs.') || id.startsWith('observation.') || id.startsWith('desktop.') || id === 'autonomous.release_gate';
346
346
  const runtimeEnabled=entry => {
347
347
  if (entry.runtime_flag === 'DEADBYTE_CODING_RUNTIME' || entry.runtime_flag === 'DEADBYTE_AUTONOMOUS_RUNTIME') return true;
348
348
  if (entry.runtime_flag === 'DEADBYTE_MACHINE_FS_RUNTIME') return Boolean(machineFsRuntime);
349
349
  if (entry.runtime_flag === 'DEADBYTE_PROCESS_RUNTIME') return Boolean(processRuntime);
350
350
  if (entry.runtime_flag === 'DEADBYTE_OBSERVATION_RUNTIME') return Boolean(observationRuntime);
351
+ if (entry.runtime_flag === 'DEADBYTE_DESKTOP_RUNTIME') return Boolean(desktopRuntime);
351
352
  return entry.runtime_flag === null;
352
353
  };
353
354
  const entries=R36_CAPABILITY_LEDGER.entries
@@ -359,19 +360,20 @@ function configuredCapabilityManifest({machineFsRuntime,observationRuntime,proce
359
360
  entries:Object.freeze(entries)});
360
361
  }
361
362
 
362
- async function plannerView(goal, state, events, codingRuntime, capabilityManifest) {
363
+ async function plannerView(goal, state, events, codingRuntime, capabilityManifest, desktopMemoryProvider=null) {
363
364
  const tree = await codingRuntime.tree({ rootId:goal.root_id, path:'.', maxDepth:4, maxEntries:600 });
364
365
  const memory=deriveAgentMemory(events,{maxArchival:24});
366
+ const desktopMemory=desktopMemoryProvider?await desktopMemoryProvider.plannerContext():null;
365
367
  return { mutation_epoch:state.mutation_epoch, planner_round:state.planner_round, tree:tree.entries,
366
368
  contexts:state.contexts.slice(-16), observations:state.observations.slice(-24), active_plan:state.active_plan,
367
- capabilities:capabilityManifest, project_memory:memory,
369
+ capabilities:capabilityManifest, project_memory:memory, desktop_memory:desktopMemory,
368
370
  recent_decisions:state.decisions.slice(-8).map(item => ({ planner_round:item.planner_round,
369
371
  action:item.decision?.action, summary:item.decision?.summary })) };
370
372
  }
371
373
  function requestIdFor(goalId, plannerRound, journalHead) {
372
374
  return sha256Object({ schema:'deadbyte.autonomous-planner-request.v1', goal_id:goalId, planner_round:plannerRound, journal_head_sha256:journalHead });
373
375
  }
374
- async function requestPlanner(ctx, goal, events, state, policy, codingRuntime, capabilityManifest, signingKeyPath) {
376
+ async function requestPlanner(ctx, goal, events, state, policy, codingRuntime, capabilityManifest, desktopMemoryProvider, signingKeyPath) {
375
377
  if (state.pending_request) return state.pending_request;
376
378
  const round = state.planner_round + 1;
377
379
  if (round > goal.max_iterations) {
@@ -382,7 +384,7 @@ async function requestPlanner(ctx, goal, events, state, policy, codingRuntime, c
382
384
  await appendEvent(ctx,events,signingKeyPath,'goal_paused',{ reason:'wall_time_limit', max_wall_ms:policy.limits.max_wall_ms });
383
385
  return null;
384
386
  }
385
- const view = await plannerView(goal,state,events,codingRuntime,capabilityManifest);
387
+ const view = await plannerView(goal,state,events,codingRuntime,capabilityManifest,desktopMemoryProvider);
386
388
  let prompt = buildPlannerPrompt({ goal:{ title:goal.title, goal:goal.goal, acceptance:goal.acceptance }, policy, view });
387
389
  const repair = [...(state.planner_rejections ?? [])].reverse().find(item => item.planner_round === round) ?? null;
388
390
  if (repair) {
@@ -511,7 +513,10 @@ async function requestProviderWithCircuit({ctx,events,goal,policy,pending,signin
511
513
  provider_id:providerId,request_id:pending.request_id,failure_class:classifyProviderFailure(error),
512
514
  probe:disposition.probe===true
513
515
  });
514
- circuit=providerCircuitSnapshot(policy,events);
516
+ // Bind the transition to the durable failure event's own timestamp. A short OPEN
517
+ // interval must not expire inside the same run before the caller observes the pause.
518
+ const failureObservedAt=events.at(-1)?.created_at_utc;
519
+ circuit=providerCircuitSnapshot(policy,events,failureObservedAt);
515
520
  if (circuit.state==='OPEN'||circuit.state==='UNAVAILABLE') {
516
521
  const reason=circuit.state==='UNAVAILABLE'?'provider_circuit_unavailable':'provider_circuit_open';
517
522
  return {waiting:await enterProviderCircuitWait({ctx,events,goal,policy,signingKeyPath,reason,circuit})};
@@ -519,9 +524,9 @@ async function requestProviderWithCircuit({ctx,events,goal,policy,pending,signin
519
524
  throw error;
520
525
  }
521
526
  }
522
- async function acceptPlannerDecision({ ctx, events, goal, state, policy, codingRuntime, capabilityManifest, signingKeyPath, plannerResponse }) {
527
+ async function acceptPlannerDecision({ ctx, events, goal, state, policy, codingRuntime, capabilityManifest, desktopMemoryProvider, signingKeyPath, plannerResponse }) {
523
528
  let currentState = state;
524
- let pending = currentState.pending_request ?? await requestPlanner(ctx,goal,events,currentState,policy,codingRuntime,capabilityManifest,signingKeyPath);
529
+ let pending = currentState.pending_request ?? await requestPlanner(ctx,goal,events,currentState,policy,codingRuntime,capabilityManifest,desktopMemoryProvider,signingKeyPath);
525
530
  if (!pending) return null;
526
531
  let suppliedResponse = plannerResponse;
527
532
  while (pending) {
@@ -560,7 +565,7 @@ async function acceptPlannerDecision({ ctx, events, goal, state, policy, codingR
560
565
  return { needs_planner:false, rejected:true, paused:true };
561
566
  }
562
567
  currentState = deriveState(goal,events);
563
- pending = await requestPlanner(ctx,goal,events,currentState,policy,codingRuntime,capabilityManifest,signingKeyPath);
568
+ pending = await requestPlanner(ctx,goal,events,currentState,policy,codingRuntime,capabilityManifest,desktopMemoryProvider,signingKeyPath);
564
569
  if (!pending) return null;
565
570
  if (policy.planner.backend === 'client_sampling') return { needs_planner:true, request:pending };
566
571
  continue;
@@ -599,7 +604,7 @@ async function executeInspect({ ctx, events, goal, decisionEvent, decision, codi
599
604
  return { status:'completed', contexts };
600
605
  }
601
606
 
602
- const DELEGATED_MUTATIONS = new Set(['process_start','process_action','process_terminate','observation_search_cancel']);
607
+ const DELEGATED_MUTATIONS = new Set(['process_start','process_action','process_terminate','observation_search_cancel','desktop_action']);
603
608
  const CAPABILITY_BY_ACTION=Object.freeze({
604
609
  search:'coding.search',stat:'coding.stat',multi_read:'coding.files_read',
605
610
  semantic_outline:'semantic.outline',semantic_definition:'semantic.definition',semantic_references:'semantic.references',
@@ -609,7 +614,8 @@ const CAPABILITY_BY_ACTION=Object.freeze({
609
614
  machine_fs_stat:'machine_fs.stat',machine_fs_list:'machine_fs.list',machine_fs_read:'machine_fs.read',machine_fs_mkdir:'machine_fs.mkdir',machine_fs_move:'machine_fs.move',machine_fs_delete:'machine_fs.delete',
610
615
  machine_stat:'machine_fs.stat',machine_list:'machine_fs.list',machine_read:'machine_fs.read',
611
616
  observation_search_start:'observation.search_start',observation_search_status:'observation.search_status',observation_search_page:'observation.search_page',observation_search_cancel:'observation.search_cancel',
612
- observation_process_list:'observation.process_list',observation_system_status:'observation.system_status'
617
+ observation_process_list:'observation.process_list',observation_system_status:'observation.system_status',
618
+ desktop_status:'desktop.status',desktop_observe:'desktop.observe',desktop_target:'desktop.target',desktop_action:'desktop.action'
613
619
  });
614
620
  function capabilityIdForOperation(operation){return CAPABILITY_BY_ACTION[operation?.kind]??null;}
615
621
  function assertDelegatedIdentity(events, operation) {
@@ -623,9 +629,27 @@ function assertDelegatedIdentity(events, operation) {
623
629
  event.payload?.operation?.kind === 'process_start' && event.payload?.result?.session_id === operation.session_id);
624
630
  if (!owned) throw new Error(`delegated process session '${operation.session_id}' is stale or belongs to another goal`);
625
631
  }
632
+ if(operation.kind==='desktop_target'){
633
+ const owned=events.some(event=>event.type==='delegated_action_completed'&&
634
+ event.payload?.operation?.kind==='desktop_observe'&&event.payload?.result?.frame?.frame_id===operation.frame_id);
635
+ if(!owned) throw new Error(`desktop frame '${operation.frame_id}' was not freshly observed by this goal`);
636
+ }
637
+ if(operation.kind==='desktop_action'){
638
+ const frameOwned=events.some(event=>event.type==='delegated_action_completed'&&
639
+ ['desktop_observe','desktop_target'].includes(event.payload?.operation?.kind)&&
640
+ event.payload?.result?.frame?.frame_id===operation.expected_frame_id);
641
+ if(!frameOwned) throw new Error(`desktop action frame '${operation.expected_frame_id}' was not observed or grounded by this goal`);
642
+ if(operation.target_id!==null){
643
+ const targetOwned=events.some(event=>event.type==='delegated_action_completed'&&
644
+ event.payload?.operation?.kind==='desktop_target'&&
645
+ event.payload?.result?.target?.target_id===operation.target_id&&
646
+ event.payload?.result?.frame?.frame_id===operation.expected_frame_id);
647
+ if(!targetOwned) throw new Error(`desktop target '${operation.target_id}' is stale or belongs to another goal`);
648
+ }
649
+ }
626
650
  }
627
- async function invokeDelegatedOperation(goal, operation, runtimes) {
628
- const { codingRuntime, semanticRuntime, nativeDiffRuntime, machineFsRuntime, observationRuntime, processRuntime } = runtimes;
651
+ async function invokeDelegatedOperation(goal, operation, runtimes, desktopContext=null) {
652
+ const { codingRuntime, semanticRuntime, nativeDiffRuntime, machineFsRuntime, observationRuntime, processRuntime, desktopRuntime } = runtimes;
629
653
  switch (operation.kind) {
630
654
  case 'stat':
631
655
  return codingRuntime.stat({rootId:goal.root_id,relativePath:operation.path,hash:operation.hash});
@@ -713,6 +737,33 @@ async function invokeDelegatedOperation(goal, operation, runtimes) {
713
737
  case 'process_terminate':
714
738
  if (!processRuntime) throw new Error('process runtime unavailable');
715
739
  return processRuntime.terminate({ sessionId:operation.session_id });
740
+ case 'desktop_status':
741
+ if(!desktopRuntime) throw new Error('desktop runtime unavailable');
742
+ return desktopRuntime.status();
743
+ case 'desktop_observe':
744
+ if(!desktopRuntime) throw new Error('desktop runtime unavailable');
745
+ return desktopRuntime.observe({preview:false});
746
+ case 'desktop_target':
747
+ if(!desktopRuntime) throw new Error('desktop runtime unavailable');
748
+ return desktopRuntime.ground({frameId:operation.frame_id,query:operation.uia_query});
749
+ case 'desktop_action': {
750
+ if(!desktopRuntime) throw new Error('desktop runtime unavailable');
751
+ if(!desktopContext) throw new Error('desktop autonomous effect context missing');
752
+ const result=await desktopRuntime.action({
753
+ intentId:desktopContext.intentId,kind:operation.action_kind,expectedFrameId:operation.expected_frame_id,
754
+ targetId:operation.target_id,point:operation.point,scrollDelta:operation.scroll_delta,
755
+ keyChord:operation.key_chord,text:operation.text,effectContext:desktopContext.effectContext
756
+ });
757
+ const {action,...rest}=result;
758
+ return Object.freeze({...rest,action_summary:Object.freeze({
759
+ schema:'deadbyte.desktop-action-summary.v1',intent_id:action.intent_id,kind:action.kind,
760
+ frame_id:action.frame_id,desktop_epoch:action.desktop_epoch,topology_sha256:action.topology_sha256,
761
+ expected_window:action.expected_window,target_id:action.target_id,point:action.point,
762
+ scroll_delta:action.scroll_delta,key_chord:action.key_chord,text_sha256:action.text_sha256,
763
+ ...(action.kind==='text_input'?{text_bytes:Buffer.byteLength(action.text??'','utf8')}:{}),
764
+ action_sha256:action.action_sha256
765
+ })});
766
+ }
716
767
  default: throw new Error(`unsupported delegated operation '${String(operation.kind)}'`);
717
768
  }
718
769
  }
@@ -735,6 +786,27 @@ async function verifyDelegatedEvidence(operation, result, runtimes, verifyKeyPat
735
786
  journal_head_sha256:checked.head_sha256 };
736
787
  }
737
788
  }
789
+ if(operation.kind.startsWith('desktop_')&&operation.kind!=='desktop_status'){
790
+ if(!runtimes.desktopRuntime?.verifyReceipt) throw new Error('desktop receipt verifier unavailable');
791
+ const refs=[];
792
+ if(operation.kind==='desktop_observe'&&result?.receipt_id) refs.push([result.receipt_id,result.receipt_sha256,'desktop_observation']);
793
+ if(operation.kind==='desktop_target'&&result?.grounding?.receipt_id) refs.push([result.grounding.receipt_id,result.grounding.receipt_sha256,'desktop_grounding']);
794
+ if(operation.kind==='desktop_action'){
795
+ refs.push(
796
+ [result?.started_receipt_id,result?.started_receipt_sha256,'desktop_action_started'],
797
+ [result?.post_receipt_id,result?.post_receipt_sha256,'desktop_post_observation'],
798
+ [result?.receipt_id,result?.receipt_sha256,'desktop_action_completed']
799
+ );
800
+ }
801
+ if(refs.length<1||refs.some(([id,sha])=>typeof id!=='string'||!SHA256_RE.test(sha??''))) throw new Error('desktop delegated evidence references missing');
802
+ const verified=[];
803
+ for(const [id,sha,kind] of refs){
804
+ const proof=await runtimes.desktopRuntime.verifyReceipt({receiptId:id});
805
+ if(!proof.ok||proof.receipt_sha256!==sha||proof.kind!==kind) throw new Error('desktop delegated receipt mismatch');
806
+ verified.push({receipt_id:id,receipt_sha256:sha,kind});
807
+ }
808
+ return {kind:'desktop_receipts',receipts:verified,result_sha256:sha256Object(result)};
809
+ }
738
810
  return { kind:'runtime_result', result_sha256:sha256Object(result) };
739
811
  }
740
812
  async function executeDelegate({ ctx, events, goal, decisionEvent, decision, policy, runtimes, assertRunAuthority,
@@ -742,6 +814,22 @@ async function executeDelegate({ ctx, events, goal, decisionEvent, decision, pol
742
814
  const capabilityId=capabilityIdForOperation(decision.operation);
743
815
  if(!capabilityId) throw new Error(`unsupported typed capability action '${String(decision.operation?.kind)}'`);
744
816
  const actionKey = `capability-${decisionEvent.event_sha256}`;
817
+ if(decision.operation.kind==='desktop_action'){
818
+ if(!runtimes.desktopRuntime) throw new Error('desktop runtime unavailable');
819
+ const desktopStatus=await runtimes.desktopRuntime.status();
820
+ if(!desktopStatus.armed||!desktopStatus.action_kinds.includes(decision.operation.action_kind)){
821
+ if(!events.some(event=>event.type==='waiting_entered'&&event.payload?.reason==='desktop_authority_unavailable')){
822
+ await appendEvent(ctx,events,signingKeyPath,'waiting_entered',{
823
+ reason:'desktop_authority_unavailable',requires_approval:true,resume_phase:'ACT',
824
+ desktop_action_kind:decision.operation.action_kind
825
+ });
826
+ }
827
+ await appendEvent(ctx,events,signingKeyPath,'goal_paused',{
828
+ reason:'desktop_authority_unavailable',desktop_action_kind:decision.operation.action_kind
829
+ });
830
+ return {status:'paused',reason:'desktop_authority_unavailable'};
831
+ }
832
+ }
745
833
  const prior = [...events].reverse().find(event => event.type === 'delegated_action_completed' && event.payload?.action_key === actionKey);
746
834
  if (prior) return { status:'completed', result:prior.payload.result, existing:true };
747
835
  const started = [...events].reverse().find(event => event.type === 'delegated_action_started' && event.payload?.action_key === actionKey);
@@ -755,7 +843,20 @@ async function executeDelegate({ ctx, events, goal, decisionEvent, decision, pol
755
843
  try {
756
844
  assertDelegatedIdentity(events,decision.operation);
757
845
  if (DELEGATED_MUTATIONS.has(decision.operation.kind)) await assertRunAuthority(goal.root_id);
758
- const result = await invokeDelegatedOperation(goal,decision.operation,runtimes);
846
+ let desktopContext=null;
847
+ if(decision.operation.kind==='desktop_action'){
848
+ const loop=deriveLoopState(goal,events);
849
+ const current=deriveState(goal,events);
850
+ desktopContext={
851
+ intentId:sha256Object({schema:'deadbyte.autonomous-desktop-intent.v1',goal_id:goal.goal_id,
852
+ decision_event_sha256:decisionEvent.event_sha256,operation:decision.operation}),
853
+ effectContext:{
854
+ goalId:goal.goal_id,planId:loop.plan_id,stepId:loop.current_step?.step_id??'desktop',
855
+ mutationEpoch:current.mutation_epoch,observationHeadSha256:decision.operation.expected_frame_id
856
+ }
857
+ };
858
+ }
859
+ const result = await invokeDelegatedOperation(goal,decision.operation,runtimes,desktopContext);
759
860
  if (Buffer.byteLength(canonicalJson(result),'utf8') > policy.limits.max_context_bytes) {
760
861
  throw new Error('delegated result exceeds autonomous context bound');
761
862
  }
@@ -764,6 +865,23 @@ async function executeDelegate({ ctx, events, goal, decisionEvent, decision, pol
764
865
  decision_event_sha256:decisionEvent.event_sha256, operation:decision.operation, result, evidence });
765
866
  return { status:'completed', result, evidence };
766
867
  } catch (error) {
868
+ if(decision.operation.kind==='desktop_action'){
869
+ const sideEffectState=error?.side_effect_state??null;
870
+ const reason=sideEffectState==='unknown'?'desktop_outcome_unknown':
871
+ sideEffectState==='not_started'?'desktop_effect_not_started':'desktop_action_failed';
872
+ const resumePhase=sideEffectState==='unknown'?'CRITIC':'ACT';
873
+ if(!events.some(event=>event.type==='waiting_entered'&&event.payload?.reason===reason)){
874
+ await appendEvent(ctx,events,signingKeyPath,'waiting_entered',{
875
+ reason,requires_approval:true,resume_phase:resumePhase,
876
+ receipt_id:error?.receipt_id??null,receipt_sha256:error?.receipt_sha256??null
877
+ });
878
+ }
879
+ await appendEvent(ctx,events,signingKeyPath,'goal_paused',{
880
+ reason,action_key:actionKey,operation_kind:decision.operation.kind,side_effect_state:sideEffectState,
881
+ receipt_id:error?.receipt_id??null,receipt_sha256:error?.receipt_sha256??null,detail:truncated(error)
882
+ });
883
+ return {status:'paused',reason,side_effect_state:sideEffectState};
884
+ }
767
885
  await appendEvent(ctx,events,signingKeyPath,'goal_paused',{ reason:'delegated_action_failed', action_key:actionKey,
768
886
  operation_kind:decision.operation.kind, detail:truncated(error) });
769
887
  return { status:'paused', reason:'delegated_action_failed' };
@@ -1195,7 +1313,7 @@ function r34ReasonHash(goal,decisionEvent,reason) {
1195
1313
  decision_event_sha256:decisionEvent.event_sha256,reason:String(reason ?? '')});
1196
1314
  }
1197
1315
  async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,policy,codingRuntime,
1198
- nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,assertRunAuthority,
1316
+ nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime,assertRunAuthority,
1199
1317
  signingKeyPath,verifyKeyPath,workerScheduler}) {
1200
1318
  let loop=deriveLoopState(goal,events);
1201
1319
  if (decision.kind === 'plan_create') {
@@ -1258,7 +1376,7 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
1258
1376
  } else if (decision.action.kind === 'delegate') {
1259
1377
  outcome=await executeDelegate({ctx,events,goal,decisionEvent,
1260
1378
  decision:{operation:decision.action.operation},policy,
1261
- runtimes:{codingRuntime,semanticRuntime,nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime},assertRunAuthority,signingKeyPath,verifyKeyPath});
1379
+ runtimes:{codingRuntime,semanticRuntime,nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,desktopRuntime},assertRunAuthority,signingKeyPath,verifyKeyPath});
1262
1380
  } else if (decision.action.kind === 'native_diff_apply') {
1263
1381
  await assertRunAuthority(goal.root_id);
1264
1382
  outcome=await executePreparedDiff({ctx,events,goal,state,decisionEvent,diffId:decision.action.diff_id,
@@ -1287,7 +1405,7 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
1287
1405
  } else if (capabilityIdForOperation(decision.action)) {
1288
1406
  outcome=await executeDelegate({ctx,events,goal,decisionEvent,
1289
1407
  decision:{operation:decision.action},policy,
1290
- runtimes:{codingRuntime,semanticRuntime,nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime},assertRunAuthority,signingKeyPath,verifyKeyPath});
1408
+ runtimes:{codingRuntime,semanticRuntime,nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,desktopRuntime},assertRunAuthority,signingKeyPath,verifyKeyPath});
1291
1409
  } else {
1292
1410
  throw new Error(`unsupported R34 action '${String(decision.action.kind)}'`);
1293
1411
  }
@@ -1391,12 +1509,12 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
1391
1509
  }
1392
1510
 
1393
1511
  async function executeDecisionCore({ ctx, events, goal, state, decisionEvent, policy, codingRuntime,
1394
- nativeDiffRuntime, machineFsRuntime, observationRuntime, processRuntime, semanticRuntime, assertRunAuthority,
1512
+ nativeDiffRuntime, machineFsRuntime, observationRuntime, processRuntime, semanticRuntime, desktopRuntime, assertRunAuthority,
1395
1513
  signingKeyPath, verifyKeyPath, workerScheduler }) {
1396
1514
  const decision = decisionEvent.decision;
1397
1515
  if (decision?.schema === 'deadbyte.autonomous-decision.v2') {
1398
1516
  const outcome=await executeR34Decision({ctx,events,goal,state,decisionEvent,decision,policy,codingRuntime,
1399
- nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,assertRunAuthority,
1517
+ nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime,assertRunAuthority,
1400
1518
  signingKeyPath,verifyKeyPath,workerScheduler});
1401
1519
  await appendEvent(ctx,events,signingKeyPath,'decision_completed',{
1402
1520
  decision_event_sha256:decisionEvent.event_sha256,action:decision.kind,
@@ -1463,6 +1581,7 @@ function effectDescriptor(goal,state,events,decisionEvent){
1463
1581
  if(decision?.schema==='deadbyte.autonomous-decision.v2'&&decision.kind==='act'){
1464
1582
  const loop=deriveLoopState(goal,events);
1465
1583
  if(!loop.plan_id||!loop.current_step?.step_id) return null;
1584
+ if(String(decision.action?.kind??'').startsWith('desktop_')) return null;
1466
1585
  action=decision.action;
1467
1586
  coordinates={planId:loop.plan_id,stepId:loop.current_step.step_id};
1468
1587
  }else if(['patch','delegate','run_profile'].includes(decision?.action)){
@@ -1558,10 +1677,11 @@ function isAuthorityUnavailable(error) {
1558
1677
  }
1559
1678
 
1560
1679
  export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRuntime, machineFsRuntime = null,
1561
- observationRuntime = null, processRuntime = null, semanticRuntime = null, signingKeyPath, verifyKeyPath }) {
1680
+ observationRuntime = null, processRuntime = null, semanticRuntime = null, desktopRuntime = null,
1681
+ desktopMemoryProvider = null, signingKeyPath, verifyKeyPath }) {
1562
1682
  if (!policy || !codingRuntime || !nativeDiffRuntime) throw new Error('autonomous runtime requires policy/coding/native-diff runtimes');
1563
1683
  if (!signingKeyPath || !verifyKeyPath) throw new Error('autonomous runtime requires trust keys');
1564
- const capabilityManifest=configuredCapabilityManifest({machineFsRuntime,observationRuntime,processRuntime,semanticRuntime});
1684
+ const capabilityManifest=configuredCapabilityManifest({machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime});
1565
1685
  const profileTimeoutCeiling=Math.max(120000,...Object.values(policy.profiles??{}).map(profile=>
1566
1686
  Number.isInteger(profile?.timeout_ms)&&profile.timeout_ms>0?profile.timeout_ms+120000:120000));
1567
1687
  const workerScheduler=createWorkerScheduler({maxConcurrent:2,totalCpuUnits:2,totalMemoryMb:2048,leaseMs:profileTimeoutCeiling});
@@ -1782,7 +1902,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1782
1902
  try {
1783
1903
  outcome = await executeDecision({ ctx,events:journal,goal,state,
1784
1904
  decisionEvent:state.pending_decision,policy,codingRuntime,nativeDiffRuntime,machineFsRuntime,
1785
- observationRuntime,processRuntime,semanticRuntime,assertRunAuthority,signingKeyPath,verifyKeyPath,workerScheduler });
1905
+ observationRuntime,processRuntime,semanticRuntime,desktopRuntime,assertRunAuthority,signingKeyPath,verifyKeyPath,workerScheduler });
1786
1906
  } catch (error) {
1787
1907
  const loop=loopStateOrNull(goal,journal);
1788
1908
  if (!loop || loop.pending_action || !isAuthorityUnavailable(error)) throw error;
@@ -1807,7 +1927,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
1807
1927
  continue;
1808
1928
  }
1809
1929
  const accepted = await acceptPlannerDecision({ ctx,events:journal,goal,state,policy,codingRuntime,capabilityManifest,
1810
- signingKeyPath,plannerResponse:suppliedResponse });
1930
+ desktopMemoryProvider,signingKeyPath,plannerResponse:suppliedResponse });
1811
1931
  suppliedResponse = null;
1812
1932
  if (accepted?.needs_planner) {
1813
1933
  state = deriveState(goal,journal);
@@ -1,7 +1,7 @@
1
1
  import { createHash } from 'node:crypto';
2
2
 
3
3
  export const CAPABILITY_LEDGER_SCHEMA='deadbyte.capability-ledger.v1';
4
- const AUTHORITIES=new Set(['none','process','coding','autonomous','host']);
4
+ const AUTHORITIES=new Set(['none','process','coding','autonomous','desktop','host']);
5
5
  const PROFILE_ENV='DEADBYTE_CAPABILITY_PROFILE';
6
6
  const COMPACT_TOOL_LIMIT=42;
7
7
 
@@ -135,6 +135,11 @@ export const R36_CAPABILITY_ENTRIES=Object.freeze([
135
135
  gateway('observation.process_list','machine_process_list','machine_observe','process_list','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
136
136
  gateway('observation.system_status','machine_system_status','machine_observe','system_status','DEADBYTE_OBSERVATION_RUNTIME','none',{receipt_class:'observation'}),
137
137
 
138
+ gateway('desktop.status','desktop_status','desktop_control','status','DEADBYTE_DESKTOP_RUNTIME','none',{receipt_class:'desktop'}),
139
+ gateway('desktop.observe','desktop_observe','desktop_control','observe','DEADBYTE_DESKTOP_RUNTIME','none',{receipt_class:'desktop'}),
140
+ gateway('desktop.target','desktop_target','desktop_control','target','DEADBYTE_DESKTOP_RUNTIME','none',{receipt_class:'desktop'}),
141
+ gateway('desktop.action','desktop_action','desktop_control','action','DEADBYTE_DESKTOP_RUNTIME','desktop',{mutating:true,receipt_class:'desktop'}),
142
+
138
143
  direct('autonomous.session_status','autonomous_session_status','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{receipt_class:'autonomous'}),
139
144
  direct('native_diff.preview','native_diff_preview','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{mutating:true,receipt_class:'native_diff'}),
140
145
  direct('native_diff.apply','native_diff_apply','DEADBYTE_AUTONOMOUS_RUNTIME','autonomous',{mutating:true,receipt_class:'native_diff'}),
@@ -1,5 +1,7 @@
1
+ import { createHash } from 'node:crypto';
1
2
  import * as z from 'zod/v4';
2
3
  import { compactGatewayOutputSchema, compactGatewayResult } from './compact-gateway-output-contracts.mjs';
4
+ import { desktopControlInputSchema,invokeDesktopOperation } from './desktop-mcp-tools-r40.mjs';
3
5
 
4
6
  const id=z.string().regex(/^[A-Za-z0-9._-]{1,64}$/);
5
7
  const rel=z.string().min(1).max(2048);
@@ -117,12 +119,22 @@ async function machineObserve(args,machineRuntime,observationRuntime){
117
119
  return invoke(args.op,observationRuntime,'observation',map[args.op]);
118
120
  }
119
121
 
120
- export function registerCompactGatewayTools(server,{profile,ledger,codingRuntime,semanticRuntime,machineFsRuntime,processRuntime,observationRuntime,machineRuntime}){
122
+ export function registerCompactGatewayTools(server,{profile,ledger,codingRuntime,semanticRuntime,machineFsRuntime,processRuntime,observationRuntime,machineRuntime,desktopRuntime,catalogProvider}){
121
123
  if(!server) throw new Error('compact gateway registration requires server');
122
124
  if(profile!=='compact') return;
123
- server.registerTool('capability_manifest',{description:'Read the canonical R36 capability ledger and active compact profile.',inputSchema:capabilityManifestInputSchema,outputSchema:compactGatewayOutputSchema},async()=>compactGatewayResult({status:'ok',op:'manifest',result:{schema:'deadbyte.capability-manifest.v1',profile,ledger_sha256:ledger?.ledger_sha256??null,capabilities:ledger?.entries??[]}},false));
125
+ if(typeof catalogProvider!=='function') throw new Error('compact gateway requires catalogProvider');
126
+ server.registerTool('capability_manifest',{description:'Read the canonical capability ledger and exact active compact tool catalog.',inputSchema:capabilityManifestInputSchema,outputSchema:compactGatewayOutputSchema},async()=>{
127
+ const toolCatalog=[...catalogProvider()].map(String).sort();
128
+ if(toolCatalog.length<1||new Set(toolCatalog).size!==toolCatalog.length) throw new Error('compact catalog provider returned invalid catalog');
129
+ const catalogSha256=createHash('sha256').update(JSON.stringify(toolCatalog),'utf8').digest('hex');
130
+ return compactGatewayResult({status:'ok',op:'manifest',result:{
131
+ schema:'deadbyte.capability-manifest.v2',profile,ledger_sha256:ledger?.ledger_sha256??null,
132
+ tool_catalog:toolCatalog,tool_count:toolCatalog.length,catalog_sha256:catalogSha256,capabilities:ledger?.entries??[]
133
+ }},false);
134
+ });
124
135
  server.registerTool('coding_inspect',{description:'Typed compact coding inspection and semantic navigation gateway. No arbitrary tool dispatch.',inputSchema:codingInspectInputSchema,outputSchema:compactGatewayOutputSchema},args=>codingInspect(args,codingRuntime,semanticRuntime));
125
136
  server.registerTool('machine_fs',{description:'Typed compact machine filesystem gateway preserving root policy and mutation preconditions.',inputSchema:machineFsGatewayInputSchema,outputSchema:compactGatewayOutputSchema},args=>machineFs(args,machineFsRuntime));
126
137
  server.registerTool('process_session',{description:'Typed compact DEADBYTE-owned process session gateway. No raw stdin or PID authority.',inputSchema:processSessionGatewayInputSchema,outputSchema:compactGatewayOutputSchema},args=>processSession(args,processRuntime));
127
138
  server.registerTool('machine_observe',{description:'Typed compact machine identity and read-only observation gateway. Revoke is intentionally excluded.',inputSchema:machineObserveInputSchema,outputSchema:compactGatewayOutputSchema},args=>machineObserve(args,machineRuntime,observationRuntime));
139
+ server.registerTool('desktop_control',{description:'Typed Desktop observe, target, status and guarded action gateway. No arbitrary dispatch; mutation still requires separate Desktop authority.',inputSchema:desktopControlInputSchema,outputSchema:compactGatewayOutputSchema},args=>invokeDesktopOperation(args,desktopRuntime));
128
140
  }