deadbyte-mcp 0.10.0 → 0.11.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.
- package/MANIFEST.SHA256 +69 -55
- package/README.txt +13 -14
- package/bin/WORKER-REGISTRY.txt +3 -3
- package/bin/appcontainer-stage.exe +0 -0
- package/bin/appcontainer-stage.obj +0 -0
- package/bin/bootstrap-advapi32.exe +0 -0
- package/bin/bootstrap-advapi32.obj +0 -0
- package/bin/bootstrap-exitcode.exe +0 -0
- package/bin/bootstrap-exitcode.obj +0 -0
- package/bin/bootstrap-kernel32.exe +0 -0
- package/bin/bootstrap-kernel32.obj +0 -0
- package/bin/child-control-probe.exe +0 -0
- package/bin/child-control-probe.obj +0 -0
- package/bin/child-control-stage.exe +0 -0
- package/bin/child-control-stage.obj +0 -0
- package/bin/contained-reverse-worker.exe +0 -0
- package/bin/contained-reverse-worker.obj +0 -0
- package/bin/contained-transform-worker.exe +0 -0
- package/bin/contained-transform-worker.obj +0 -0
- package/bin/containment-probe.exe +0 -0
- package/bin/containment-probe.obj +0 -0
- package/bin/deadbyte-contain.exe +0 -0
- package/bin/deadbyte-contain.obj +0 -0
- package/bin/deadbyte-exec.exe +0 -0
- package/bin/deadbyte-exec.obj +0 -0
- package/bin/deadbyte-process-host.exe +0 -0
- package/bin/deadbyte-process-host.obj +0 -0
- package/bin/deadbyte-tunnel-host.exe +0 -0
- package/bin/deadbyte-tunnel-host.obj +0 -0
- package/controller/README.TXT +3 -3
- package/controller/deadbyte-controller.ps1 +1 -1
- package/controller/deadbyte-process-policy.json +1 -1
- package/docs/ARCHITECTURE.md +1 -1
- package/package.json +1 -1
- package/scripts/deferred-slot-operation.mjs +1 -1
- package/scripts/final-closure-r36.mjs +113 -0
- package/scripts/final-closure-verify-r36.mjs +208 -0
- package/scripts/final-closure-verify.mjs +48 -48
- package/scripts/final-closure.mjs +13 -13
- package/scripts/gate-windows-r36.ps1 +42 -0
- package/scripts/gate-windows.ps1 +3 -3
- package/scripts/release-parity-r36.mjs +200 -0
- package/scripts/release-parity-tests.ps1 +2 -2
- package/scripts/release-parity.mjs +37 -37
- package/scripts/windows-gate-evidence-r36.mjs +52 -0
- package/scripts/windows-gate-evidence.mjs +5 -5
- package/src/agent-memory-plane.mjs +98 -0
- package/src/autonomous-output-contracts.mjs +5 -2
- package/src/autonomous-runtime.mjs +155 -31
- package/src/autonomous-timeout-recovery.mjs +27 -0
- package/src/deadbyte-cli.mjs +37 -76
- package/src/effect-ledger.mjs +47 -0
- package/src/observation-lease.mjs +32 -0
- package/src/remote-live-log.mjs +59 -0
- package/src/remote-mcp-autonomous-smoke-client.mjs +15 -9
- package/src/version.mjs +1 -1
- package/src/worker-scheduler.mjs +53 -0
- package/test/autonomous-output-contract.test.mjs +1 -1
- package/test/core.test.mjs +1 -1
- package/test/production-docs.test.mjs +5 -5
- package/test/r33-closeout-regression.test.mjs +4 -4
- package/test/r33-finalization.test.mjs +6 -6
- package/test/r36-finalization.test.mjs +5 -5
- package/test/r36-release-identity.test.mjs +12 -19
- package/test/r37-agent-fabric.test.mjs +92 -0
- package/test/r37-autonomous-timeout-recovery.test.mjs +60 -0
- package/test/r37-finalization.test.mjs +74 -0
- package/test/release-version.test.mjs +42 -36
- package/test/remote-live-log-r37.test.mjs +47 -0
- package/test/remote-session-cli.test.mjs +27 -50
|
@@ -12,6 +12,10 @@ import { judgeProgress, stallDisposition } from './autonomous-progress.mjs';
|
|
|
12
12
|
import { classifyProviderFailure, deriveCircuit, providerCallDisposition, providerCircuitId } from './provider-circuit.mjs';
|
|
13
13
|
import { verifyObservationReceiptFile } from './observation-runtime.mjs';
|
|
14
14
|
import { R36_CAPABILITY_LEDGER } from './capability-ledger.mjs';
|
|
15
|
+
import { deriveAgentMemory } from './agent-memory-plane.mjs';
|
|
16
|
+
import { effectIdentity, loopGuardDisposition, bindEffectCompletion } from './effect-ledger.mjs';
|
|
17
|
+
import { createWorkerScheduler } from './worker-scheduler.mjs';
|
|
18
|
+
import { issueObservationLease, verifyObservationLease } from './observation-lease.mjs';
|
|
15
19
|
|
|
16
20
|
export const AUTONOMOUS_GOAL_SCHEMA = 'deadbyte.autonomous-goal.v1';
|
|
17
21
|
export const AUTONOMOUS_EVENT_SCHEMA = 'deadbyte.autonomous-event.v1';
|
|
@@ -164,6 +168,22 @@ async function acquireLock(ctx) {
|
|
|
164
168
|
throw new Error('autonomous lock acquisition failed');
|
|
165
169
|
}
|
|
166
170
|
async function releaseLock(ctx) { await rm(ctx.lockPath,{force:true}); }
|
|
171
|
+
async function runLockStatus(ctx) {
|
|
172
|
+
let parsed;
|
|
173
|
+
try { parsed = JSON.parse(await readFile(ctx.lockPath,'utf8')); }
|
|
174
|
+
catch (error) {
|
|
175
|
+
if (error?.code === 'ENOENT') return { run_busy:false, run_owner_pid:null, run_lock_created_at_utc:null };
|
|
176
|
+
throw new Error('autonomous lock malformed');
|
|
177
|
+
}
|
|
178
|
+
if (parsed?.schema !== 'deadbyte.autonomous-lock.v1' || parsed?.goal_id !== path.basename(ctx.dir) ||
|
|
179
|
+
!Number.isInteger(parsed?.pid) || parsed.pid < 1 || typeof parsed?.created_at_utc !== 'string' || !parsed.created_at_utc) {
|
|
180
|
+
throw new Error('autonomous lock malformed');
|
|
181
|
+
}
|
|
182
|
+
const alive=await processExists(parsed.pid);
|
|
183
|
+
return alive
|
|
184
|
+
? { run_busy:true, run_owner_pid:parsed.pid, run_lock_created_at_utc:parsed.created_at_utc }
|
|
185
|
+
: { run_busy:false, run_owner_pid:null, run_lock_created_at_utc:null };
|
|
186
|
+
}
|
|
167
187
|
async function readCancel(ctx, verifyKeyPath) {
|
|
168
188
|
if (!await exists(ctx.cancelPath)) return null;
|
|
169
189
|
const parsed = JSON.parse(await readFile(ctx.cancelPath,'utf8'));
|
|
@@ -339,11 +359,12 @@ function configuredCapabilityManifest({machineFsRuntime,observationRuntime,proce
|
|
|
339
359
|
entries:Object.freeze(entries)});
|
|
340
360
|
}
|
|
341
361
|
|
|
342
|
-
async function plannerView(goal, state, codingRuntime, capabilityManifest) {
|
|
362
|
+
async function plannerView(goal, state, events, codingRuntime, capabilityManifest) {
|
|
343
363
|
const tree = await codingRuntime.tree({ rootId:goal.root_id, path:'.', maxDepth:4, maxEntries:600 });
|
|
364
|
+
const memory=deriveAgentMemory(events,{maxArchival:24});
|
|
344
365
|
return { mutation_epoch:state.mutation_epoch, planner_round:state.planner_round, tree:tree.entries,
|
|
345
366
|
contexts:state.contexts.slice(-16), observations:state.observations.slice(-24), active_plan:state.active_plan,
|
|
346
|
-
capabilities:capabilityManifest,
|
|
367
|
+
capabilities:capabilityManifest, project_memory:memory,
|
|
347
368
|
recent_decisions:state.decisions.slice(-8).map(item => ({ planner_round:item.planner_round,
|
|
348
369
|
action:item.decision?.action, summary:item.decision?.summary })) };
|
|
349
370
|
}
|
|
@@ -361,7 +382,7 @@ async function requestPlanner(ctx, goal, events, state, policy, codingRuntime, c
|
|
|
361
382
|
await appendEvent(ctx,events,signingKeyPath,'goal_paused',{ reason:'wall_time_limit', max_wall_ms:policy.limits.max_wall_ms });
|
|
362
383
|
return null;
|
|
363
384
|
}
|
|
364
|
-
const view = await plannerView(goal,state,codingRuntime,capabilityManifest);
|
|
385
|
+
const view = await plannerView(goal,state,events,codingRuntime,capabilityManifest);
|
|
365
386
|
let prompt = buildPlannerPrompt({ goal:{ title:goal.title, goal:goal.goal, acceptance:goal.acceptance }, policy, view });
|
|
366
387
|
const repair = [...(state.planner_rejections ?? [])].reverse().find(item => item.planner_round === round) ?? null;
|
|
367
388
|
if (repair) {
|
|
@@ -749,14 +770,36 @@ async function executeDelegate({ ctx, events, goal, decisionEvent, decision, pol
|
|
|
749
770
|
}
|
|
750
771
|
}
|
|
751
772
|
|
|
752
|
-
async function
|
|
773
|
+
async function executeProfileWithLease({workerScheduler,ctx,events,goal,epoch,actionKey,profileId,policy,codingRuntime,signingKeyPath,r34ResumePhase=null}) {
|
|
774
|
+
if(!workerScheduler) throw new Error('R37 worker scheduler unavailable');
|
|
775
|
+
const taskId=`${goal.goal_id}:${actionKey}:${profileId}`;
|
|
776
|
+
const lease=workerScheduler.acquire({taskId,workerClass:'profile',cpuUnits:1,memoryMb:256});
|
|
777
|
+
await appendEvent(ctx,events,signingKeyPath,'worker_lease_acquired',{
|
|
778
|
+
lease_id:lease.lease_id,task_id:lease.task_id,worker_class:lease.worker_class,profile_id:profileId,
|
|
779
|
+
attempt:lease.attempt,cpu_units:lease.cpu_units,memory_mb:lease.memory_mb,expires_at_ms:lease.expires_at_ms
|
|
780
|
+
});
|
|
781
|
+
let outcomeStatus='failed';
|
|
782
|
+
try {
|
|
783
|
+
const result=await executeProfile({ctx,events,goalId:goal.goal_id,actionKey,profileId,epoch,policy,codingRuntime,signingKeyPath,r34ResumePhase});
|
|
784
|
+
outcomeStatus=result.status;
|
|
785
|
+
return result;
|
|
786
|
+
} finally {
|
|
787
|
+
const released=workerScheduler.release(lease.lease_id,{status:outcomeStatus});
|
|
788
|
+
await appendEvent(ctx,events,signingKeyPath,'worker_lease_released',{
|
|
789
|
+
lease_id:lease.lease_id,task_id:lease.task_id,worker_class:lease.worker_class,profile_id:profileId,
|
|
790
|
+
status:released.lease.status,released_at_ms:released.lease.released_at_ms
|
|
791
|
+
});
|
|
792
|
+
}
|
|
793
|
+
}
|
|
794
|
+
|
|
795
|
+
async function runMutationProfiles({ ctx, events, goal, epoch, actionKey, policy, codingRuntime, signingKeyPath, workerScheduler }) {
|
|
753
796
|
const profileIds = Object.entries(policy.profiles)
|
|
754
797
|
.filter(([,profile]) => profile.run_after_mutation)
|
|
755
798
|
.map(([id]) => id);
|
|
756
799
|
const results = [];
|
|
757
800
|
for (const profileId of profileIds) {
|
|
758
|
-
const result = await
|
|
759
|
-
profileId,
|
|
801
|
+
const result = await executeProfileWithLease({workerScheduler,ctx,events,goal,epoch,
|
|
802
|
+
actionKey:`${actionKey}:${profileId}`,profileId,policy,codingRuntime,signingKeyPath});
|
|
760
803
|
results.push({ profile_id:profileId, status:result.status, observation:result.observation });
|
|
761
804
|
if (result.status === 'paused') break;
|
|
762
805
|
}
|
|
@@ -817,7 +860,7 @@ async function appendPatchCommit({ctx,events,signingKeyPath,started,decisionEven
|
|
|
817
860
|
});
|
|
818
861
|
}
|
|
819
862
|
async function executePatch({ ctx, events, goal, state, decisionEvent, decision, nativeDiffRuntime,
|
|
820
|
-
policy, codingRuntime, signingKeyPath, actionCoordinatesOverride = null }) {
|
|
863
|
+
policy, codingRuntime, signingKeyPath, workerScheduler, actionCoordinatesOverride = null }) {
|
|
821
864
|
let started = patchStartedForDecision(events,decisionEvent.event_sha256);
|
|
822
865
|
if (!started && state.mutation_count + decision.operations.length > policy.limits.max_mutations) {
|
|
823
866
|
await appendEvent(ctx,events,signingKeyPath,'goal_paused',{
|
|
@@ -853,7 +896,7 @@ async function executePatch({ ctx, events, goal, state, decisionEvent, decision,
|
|
|
853
896
|
if (!committed) throw new Error('completed patch action missing native diff commit');
|
|
854
897
|
const epoch = committed.payload.mutation_epoch;
|
|
855
898
|
const profiles = await runMutationProfiles({ ctx,events,goal,epoch,
|
|
856
|
-
actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath });
|
|
899
|
+
actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath,workerScheduler });
|
|
857
900
|
return { status:profiles.some(item => item.status === 'paused') ? 'paused' : 'completed',
|
|
858
901
|
diff_id:diffId, mutation_epoch:epoch, profiles, recovered:true };
|
|
859
902
|
}
|
|
@@ -901,12 +944,12 @@ async function executePatch({ ctx, events, goal, state, decisionEvent, decision,
|
|
|
901
944
|
}
|
|
902
945
|
const epoch = committed.payload.mutation_epoch;
|
|
903
946
|
const profiles = await runMutationProfiles({ ctx,events,goal,epoch,
|
|
904
|
-
actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath });
|
|
947
|
+
actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath,workerScheduler });
|
|
905
948
|
return { status:profiles.some(item => item.status === 'paused') ? 'paused' : 'completed',
|
|
906
949
|
diff_id:diffId, mutation_epoch:epoch, profiles, recovered };
|
|
907
950
|
}
|
|
908
951
|
async function executePreparedDiff({ctx,events,goal,state,decisionEvent,diffId,expectedOperationCount,
|
|
909
|
-
policy,codingRuntime,nativeDiffRuntime,signingKeyPath}) {
|
|
952
|
+
policy,codingRuntime,nativeDiffRuntime,signingKeyPath,workerScheduler}) {
|
|
910
953
|
let started=patchStartedForDecision(events,decisionEvent.event_sha256);
|
|
911
954
|
if(!started&&state.mutation_count+expectedOperationCount>policy.limits.max_mutations){
|
|
912
955
|
await appendEvent(ctx,events,signingKeyPath,'goal_paused',{reason:'mutation_limit',max_mutations:policy.limits.max_mutations});
|
|
@@ -929,7 +972,7 @@ async function executePreparedDiff({ctx,events,goal,state,decisionEvent,diffId,e
|
|
|
929
972
|
const committed=patchCommitForAction(events,actionId);
|
|
930
973
|
if(!committed) throw new Error('completed prepared diff action missing native diff commit');
|
|
931
974
|
const epoch=committed.payload.mutation_epoch;
|
|
932
|
-
const profiles=await runMutationProfiles({ctx,events,goal,epoch,actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath});
|
|
975
|
+
const profiles=await runMutationProfiles({ctx,events,goal,epoch,actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath,workerScheduler});
|
|
933
976
|
return {status:profiles.some(item=>item.status==='paused')?'paused':'completed',diff_id:diffId,mutation_epoch:epoch,profiles,recovered:true};
|
|
934
977
|
}
|
|
935
978
|
const reconciliation=await reconcileStartedAction({startedEvent:started,journal:events,runtimes:{nativeDiffRuntime,codingRuntime}});
|
|
@@ -963,10 +1006,10 @@ async function executePreparedDiff({ctx,events,goal,state,decisionEvent,diffId,e
|
|
|
963
1006
|
receipt_sha256:verified.receipt_sha256,recovered});
|
|
964
1007
|
}
|
|
965
1008
|
const epoch=committed.payload.mutation_epoch;
|
|
966
|
-
const profiles=await runMutationProfiles({ctx,events,goal,epoch,actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath});
|
|
1009
|
+
const profiles=await runMutationProfiles({ctx,events,goal,epoch,actionKey:`mutation-${epoch}`,policy,codingRuntime,signingKeyPath,workerScheduler});
|
|
967
1010
|
return {status:profiles.some(item=>item.status==='paused')?'paused':'completed',diff_id:diffId,mutation_epoch:epoch,profiles,recovered};
|
|
968
1011
|
}
|
|
969
|
-
async function runRequiredReleaseProfiles({ ctx, events, goal, state, policy, codingRuntime, signingKeyPath }) {
|
|
1012
|
+
async function runRequiredReleaseProfiles({ ctx, events, goal, state, policy, codingRuntime, signingKeyPath, workerScheduler }) {
|
|
970
1013
|
const requiredIds = Object.entries(policy.profiles)
|
|
971
1014
|
.filter(([,profile]) => profile.required_for_release)
|
|
972
1015
|
.map(([id]) => id);
|
|
@@ -977,9 +1020,9 @@ async function runRequiredReleaseProfiles({ ctx, events, goal, state, policy, co
|
|
|
977
1020
|
results.push({ profile_id:profileId, status:'passed', observation:existing, existing:true });
|
|
978
1021
|
continue;
|
|
979
1022
|
}
|
|
980
|
-
const result = await
|
|
981
|
-
actionKey:`release-${state.mutation_epoch}:${profileId}`,
|
|
982
|
-
epoch:state.mutation_epoch,policy,codingRuntime,signingKeyPath
|
|
1023
|
+
const result = await executeProfileWithLease({workerScheduler,ctx,events,goal,
|
|
1024
|
+
actionKey:`release-${state.mutation_epoch}:${profileId}`,profileId,
|
|
1025
|
+
epoch:state.mutation_epoch,policy,codingRuntime,signingKeyPath});
|
|
983
1026
|
results.push({ profile_id:profileId, ...result });
|
|
984
1027
|
if (result.status === 'paused') break;
|
|
985
1028
|
}
|
|
@@ -1153,7 +1196,7 @@ function r34ReasonHash(goal,decisionEvent,reason) {
|
|
|
1153
1196
|
}
|
|
1154
1197
|
async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,policy,codingRuntime,
|
|
1155
1198
|
nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,assertRunAuthority,
|
|
1156
|
-
signingKeyPath,verifyKeyPath}) {
|
|
1199
|
+
signingKeyPath,verifyKeyPath,workerScheduler}) {
|
|
1157
1200
|
let loop=deriveLoopState(goal,events);
|
|
1158
1201
|
if (decision.kind === 'plan_create') {
|
|
1159
1202
|
if (loop.phase !== 'PLAN' || loop.plan) throw new Error('R34 plan_create requires empty PLANNING state');
|
|
@@ -1196,7 +1239,7 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
|
|
|
1196
1239
|
try {
|
|
1197
1240
|
outcome=await executePatch({ctx,events,goal,state,decisionEvent,
|
|
1198
1241
|
decision:{operations:decision.action.operations,summary:`R34 ${currentStep.step_id}`},nativeDiffRuntime,
|
|
1199
|
-
policy,codingRuntime,signingKeyPath,actionCoordinatesOverride:{planId:loop.plan_id,
|
|
1242
|
+
policy,codingRuntime,signingKeyPath,workerScheduler,actionCoordinatesOverride:{planId:loop.plan_id,
|
|
1200
1243
|
planVersion:loop.plan_version,stepId:currentStep.step_id}});
|
|
1201
1244
|
} catch (error) {
|
|
1202
1245
|
const detail=truncated(error,512);
|
|
@@ -1209,7 +1252,7 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
|
|
|
1209
1252
|
}
|
|
1210
1253
|
} else if (decision.action.kind === 'run_profile') {
|
|
1211
1254
|
await assertRunAuthority(goal.root_id);
|
|
1212
|
-
outcome=await
|
|
1255
|
+
outcome=await executeProfileWithLease({workerScheduler,ctx,events,goal,
|
|
1213
1256
|
actionKey:`r34-${decisionEvent.event_sha256}`,profileId:decision.action.profile_id,
|
|
1214
1257
|
epoch:state.mutation_epoch,policy,codingRuntime,signingKeyPath,r34ResumePhase:loop.phase});
|
|
1215
1258
|
} else if (decision.action.kind === 'delegate') {
|
|
@@ -1219,7 +1262,7 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
|
|
|
1219
1262
|
} else if (decision.action.kind === 'native_diff_apply') {
|
|
1220
1263
|
await assertRunAuthority(goal.root_id);
|
|
1221
1264
|
outcome=await executePreparedDiff({ctx,events,goal,state,decisionEvent,diffId:decision.action.diff_id,
|
|
1222
|
-
expectedOperationCount:decision.action.expected_operation_count,policy,codingRuntime,nativeDiffRuntime,signingKeyPath});
|
|
1265
|
+
expectedOperationCount:decision.action.expected_operation_count,policy,codingRuntime,nativeDiffRuntime,signingKeyPath,workerScheduler});
|
|
1223
1266
|
} else if (['machine_fs_mkdir','machine_fs_move','machine_fs_delete'].includes(decision.action.kind)) {
|
|
1224
1267
|
await assertRunAuthority(goal.root_id);
|
|
1225
1268
|
if(!machineFsRuntime) throw new Error('machine filesystem runtime unavailable');
|
|
@@ -1240,7 +1283,7 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
|
|
|
1240
1283
|
}
|
|
1241
1284
|
}
|
|
1242
1285
|
outcome=await executePatch({ctx,events,goal,state,decisionEvent,decision:{operations,summary:`R36 ${decision.action.kind}`},
|
|
1243
|
-
nativeDiffRuntime,policy,codingRuntime,signingKeyPath});
|
|
1286
|
+
nativeDiffRuntime,policy,codingRuntime,signingKeyPath,workerScheduler});
|
|
1244
1287
|
} else if (capabilityIdForOperation(decision.action)) {
|
|
1245
1288
|
outcome=await executeDelegate({ctx,events,goal,decisionEvent,
|
|
1246
1289
|
decision:{operation:decision.action},policy,
|
|
@@ -1347,14 +1390,14 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
|
|
|
1347
1390
|
throw new Error(`unsupported R34 decision '${String(decision.kind)}'`);
|
|
1348
1391
|
}
|
|
1349
1392
|
|
|
1350
|
-
async function
|
|
1393
|
+
async function executeDecisionCore({ ctx, events, goal, state, decisionEvent, policy, codingRuntime,
|
|
1351
1394
|
nativeDiffRuntime, machineFsRuntime, observationRuntime, processRuntime, semanticRuntime, assertRunAuthority,
|
|
1352
|
-
signingKeyPath, verifyKeyPath }) {
|
|
1395
|
+
signingKeyPath, verifyKeyPath, workerScheduler }) {
|
|
1353
1396
|
const decision = decisionEvent.decision;
|
|
1354
1397
|
if (decision?.schema === 'deadbyte.autonomous-decision.v2') {
|
|
1355
1398
|
const outcome=await executeR34Decision({ctx,events,goal,state,decisionEvent,decision,policy,codingRuntime,
|
|
1356
1399
|
nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,assertRunAuthority,
|
|
1357
|
-
signingKeyPath,verifyKeyPath});
|
|
1400
|
+
signingKeyPath,verifyKeyPath,workerScheduler});
|
|
1358
1401
|
await appendEvent(ctx,events,signingKeyPath,'decision_completed',{
|
|
1359
1402
|
decision_event_sha256:decisionEvent.event_sha256,action:decision.kind,
|
|
1360
1403
|
outcome_status:outcome.status ?? 'completed'
|
|
@@ -1384,15 +1427,15 @@ async function executeDecision({ ctx, events, goal, state, decisionEvent, policy
|
|
|
1384
1427
|
} else if (decision.action === 'patch') {
|
|
1385
1428
|
await assertRunAuthority(goal.root_id);
|
|
1386
1429
|
outcome = await executePatch({ ctx,events,goal,state,decisionEvent,decision,nativeDiffRuntime,
|
|
1387
|
-
policy,codingRuntime,signingKeyPath });
|
|
1430
|
+
policy,codingRuntime,signingKeyPath,workerScheduler });
|
|
1388
1431
|
} else if (decision.action === 'delegate') {
|
|
1389
1432
|
outcome = await executeDelegate({ ctx,events,goal,decisionEvent,decision,policy,
|
|
1390
1433
|
runtimes:{ machineFsRuntime,observationRuntime,processRuntime },assertRunAuthority,signingKeyPath,verifyKeyPath });
|
|
1391
1434
|
} else if (decision.action === 'run_profile') {
|
|
1392
1435
|
await assertRunAuthority(goal.root_id);
|
|
1393
|
-
outcome = await
|
|
1394
|
-
actionKey:`planner-${decisionEvent.event_sha256}`,
|
|
1395
|
-
epoch:state.mutation_epoch,policy,codingRuntime,signingKeyPath
|
|
1436
|
+
outcome = await executeProfileWithLease({workerScheduler,ctx,events,goal,
|
|
1437
|
+
actionKey:`planner-${decisionEvent.event_sha256}`,profileId:decision.profile_id,
|
|
1438
|
+
epoch:state.mutation_epoch,policy,codingRuntime,signingKeyPath});
|
|
1396
1439
|
} else if (decision.action === 'pause') {
|
|
1397
1440
|
await appendEvent(ctx,events,signingKeyPath,'goal_paused',{
|
|
1398
1441
|
reason:decision.reason, planner_summary:decision.summary
|
|
@@ -1414,6 +1457,83 @@ async function executeDecision({ ctx, events, goal, state, decisionEvent, policy
|
|
|
1414
1457
|
return outcome;
|
|
1415
1458
|
}
|
|
1416
1459
|
|
|
1460
|
+
function effectDescriptor(goal,state,events,decisionEvent){
|
|
1461
|
+
const decision=decisionEvent?.decision;
|
|
1462
|
+
let action=null,coordinates=null;
|
|
1463
|
+
if(decision?.schema==='deadbyte.autonomous-decision.v2'&&decision.kind==='act'){
|
|
1464
|
+
const loop=deriveLoopState(goal,events);
|
|
1465
|
+
if(!loop.plan_id||!loop.current_step?.step_id) return null;
|
|
1466
|
+
action=decision.action;
|
|
1467
|
+
coordinates={planId:loop.plan_id,stepId:loop.current_step.step_id};
|
|
1468
|
+
}else if(['patch','delegate','run_profile'].includes(decision?.action)){
|
|
1469
|
+
coordinates=patchActionCoordinates(state);
|
|
1470
|
+
if(decision.action==='patch') action={kind:'patch',operations:decision.operations};
|
|
1471
|
+
else if(decision.action==='delegate') action={kind:'delegate',operation:decision.operation};
|
|
1472
|
+
else action={kind:'run_profile',profile_id:decision.profile_id};
|
|
1473
|
+
}
|
|
1474
|
+
if(!action||!coordinates) return null;
|
|
1475
|
+
const observed=[...events].reverse().find(event=>[
|
|
1476
|
+
'inspection_completed','inspect_completed','profile_completed','delegated_action_completed','observation_completed'
|
|
1477
|
+
].includes(event.type)&&SHA256_RE.test(event.event_sha256??''))?.event_sha256 ?? state.journal_head_sha256;
|
|
1478
|
+
return {action,planId:coordinates.planId,stepId:coordinates.stepId,observationHeadSha256:observed};
|
|
1479
|
+
}
|
|
1480
|
+
|
|
1481
|
+
async function executeDecision(args) {
|
|
1482
|
+
const {ctx,events,goal,state,decisionEvent,signingKeyPath}=args;
|
|
1483
|
+
const descriptor=effectDescriptor(goal,state,events,decisionEvent);
|
|
1484
|
+
if(!descriptor) return executeDecisionCore(args);
|
|
1485
|
+
const lease=issueObservationLease({goalId:goal.goal_id,mutationEpoch:state.mutation_epoch,
|
|
1486
|
+
observations:[{event_sha256:descriptor.observationHeadSha256}],ttlMs:120000});
|
|
1487
|
+
verifyObservationLease(lease,{goalId:goal.goal_id,currentMutationEpoch:state.mutation_epoch,
|
|
1488
|
+
observationHeadSha256:descriptor.observationHeadSha256});
|
|
1489
|
+
if(!events.some(event=>event.type==='observation_lease_issued'&&event.payload?.lease_id===lease.lease_id)){
|
|
1490
|
+
await appendEvent(ctx,events,signingKeyPath,'observation_lease_issued',{
|
|
1491
|
+
lease_id:lease.lease_id,mutation_epoch:lease.mutation_epoch,
|
|
1492
|
+
observation_head_sha256:lease.observation_head_sha256,expires_at_ms:lease.expires_at_ms,
|
|
1493
|
+
decision_event_sha256:decisionEvent.event_sha256
|
|
1494
|
+
});
|
|
1495
|
+
}
|
|
1496
|
+
const effect=effectIdentity({goalId:goal.goal_id,planId:descriptor.planId,stepId:descriptor.stepId,
|
|
1497
|
+
mutationEpoch:state.mutation_epoch,action:descriptor.action,observationHeadSha256:descriptor.observationHeadSha256});
|
|
1498
|
+
const guard=loopGuardDisposition(events,effect,{maxRepeats:2,window:48});
|
|
1499
|
+
if(!guard.allowed){
|
|
1500
|
+
await appendEvent(ctx,events,signingKeyPath,'effect_guard_blocked',{
|
|
1501
|
+
effect_id:effect.effect_id,fingerprint:effect.fingerprint,repeated_count:guard.repeated_count,
|
|
1502
|
+
max_repeats:guard.max_repeats,reason:guard.reason,decision_event_sha256:decisionEvent.event_sha256
|
|
1503
|
+
});
|
|
1504
|
+
await appendEvent(ctx,events,signingKeyPath,'goal_paused',{reason:'effect_repeat_limit',
|
|
1505
|
+
effect_id:effect.effect_id,fingerprint:effect.fingerprint});
|
|
1506
|
+
return {status:'paused',reason:'effect_repeat_limit'};
|
|
1507
|
+
}
|
|
1508
|
+
if(!events.some(event=>event.type==='effect_started'&&event.payload?.effect_id===effect.effect_id)){
|
|
1509
|
+
await appendEvent(ctx,events,signingKeyPath,'effect_started',{
|
|
1510
|
+
...effect,observation_lease_id:lease.lease_id,decision_event_sha256:decisionEvent.event_sha256
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
1513
|
+
try{
|
|
1514
|
+
const outcome=await executeDecisionCore(args);
|
|
1515
|
+
if(!events.some(event=>['effect_completed','effect_failed'].includes(event.type)&&event.payload?.effect_id===effect.effect_id)){
|
|
1516
|
+
const completion=bindEffectCompletion(effect,{status:outcome?.status??'completed',
|
|
1517
|
+
mutation_epoch:deriveState(goal,events).mutation_epoch,observation_lease_id:lease.lease_id});
|
|
1518
|
+
await appendEvent(ctx,events,signingKeyPath,'effect_completed',{
|
|
1519
|
+
...completion,observation_lease_id:lease.lease_id,decision_event_sha256:decisionEvent.event_sha256,
|
|
1520
|
+
outcome_status:outcome?.status??'completed'
|
|
1521
|
+
});
|
|
1522
|
+
}
|
|
1523
|
+
return outcome;
|
|
1524
|
+
}catch(error){
|
|
1525
|
+
if(!events.some(event=>['effect_completed','effect_failed'].includes(event.type)&&event.payload?.effect_id===effect.effect_id)){
|
|
1526
|
+
const completion=bindEffectCompletion(effect,{status:'failed',error:truncated(error,512),
|
|
1527
|
+
mutation_epoch:deriveState(goal,events).mutation_epoch,observation_lease_id:lease.lease_id});
|
|
1528
|
+
await appendEvent(ctx,events,signingKeyPath,'effect_failed',{
|
|
1529
|
+
...completion,observation_lease_id:lease.lease_id,decision_event_sha256:decisionEvent.event_sha256,
|
|
1530
|
+
detail:truncated(error,512)
|
|
1531
|
+
});
|
|
1532
|
+
}
|
|
1533
|
+
throw error;
|
|
1534
|
+
}
|
|
1535
|
+
}
|
|
1536
|
+
|
|
1417
1537
|
function terminalState(state) {
|
|
1418
1538
|
return ['succeeded','cancelled'].includes(state.state);
|
|
1419
1539
|
}
|
|
@@ -1442,6 +1562,9 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1442
1562
|
if (!policy || !codingRuntime || !nativeDiffRuntime) throw new Error('autonomous runtime requires policy/coding/native-diff runtimes');
|
|
1443
1563
|
if (!signingKeyPath || !verifyKeyPath) throw new Error('autonomous runtime requires trust keys');
|
|
1444
1564
|
const capabilityManifest=configuredCapabilityManifest({machineFsRuntime,observationRuntime,processRuntime,semanticRuntime});
|
|
1565
|
+
const profileTimeoutCeiling=Math.max(120000,...Object.values(policy.profiles??{}).map(profile=>
|
|
1566
|
+
Number.isInteger(profile?.timeout_ms)&&profile.timeout_ms>0?profile.timeout_ms+120000:120000));
|
|
1567
|
+
const workerScheduler=createWorkerScheduler({maxConcurrent:2,totalCpuUnits:2,totalMemoryMb:2048,leaseMs:profileTimeoutCeiling});
|
|
1445
1568
|
async function sessionStatus() {
|
|
1446
1569
|
const autonomous = await autonomousSessionStatus(policy);
|
|
1447
1570
|
const coding = await codingRuntime.sessionStatus();
|
|
@@ -1500,8 +1623,9 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1500
1623
|
const journal = await readEvents(ctx,verifyKeyPath);
|
|
1501
1624
|
const derived = deriveState(goal,journal);
|
|
1502
1625
|
const loop = publicLoopState(loopStateOrNull(goal,journal));
|
|
1626
|
+
const lock=await runLockStatus(ctx);
|
|
1503
1627
|
return { status:'ok', goal_id:goalId, goal_sha256:goal.goal_sha256,
|
|
1504
|
-
title:goal.title, root_id:goal.root_id, journal_verified:true, ...derived,
|
|
1628
|
+
title:goal.title, root_id:goal.root_id, journal_verified:true, ...derived, ...lock,
|
|
1505
1629
|
...(loop ? {loop} : {}) };
|
|
1506
1630
|
}
|
|
1507
1631
|
|
|
@@ -1577,7 +1701,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1577
1701
|
}
|
|
1578
1702
|
state = deriveState(goal,journal);
|
|
1579
1703
|
const profiles = await runRequiredReleaseProfiles({ ctx,events:journal,goal,state,
|
|
1580
|
-
policy,codingRuntime,signingKeyPath });
|
|
1704
|
+
policy,codingRuntime,signingKeyPath,workerScheduler });
|
|
1581
1705
|
if (profiles.some(item => item.status === 'paused')) {
|
|
1582
1706
|
return { status:'paused', goal_id:goal.goal_id, ready:false,
|
|
1583
1707
|
reason:'release_profile_interrupted', mutation_epoch:state.mutation_epoch };
|
|
@@ -1658,7 +1782,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1658
1782
|
try {
|
|
1659
1783
|
outcome = await executeDecision({ ctx,events:journal,goal,state,
|
|
1660
1784
|
decisionEvent:state.pending_decision,policy,codingRuntime,nativeDiffRuntime,machineFsRuntime,
|
|
1661
|
-
observationRuntime,processRuntime,semanticRuntime,assertRunAuthority,signingKeyPath,verifyKeyPath });
|
|
1785
|
+
observationRuntime,processRuntime,semanticRuntime,assertRunAuthority,signingKeyPath,verifyKeyPath,workerScheduler });
|
|
1662
1786
|
} catch (error) {
|
|
1663
1787
|
const loop=loopStateOrNull(goal,journal);
|
|
1664
1788
|
if (!loop || loop.pending_action || !isAuthorityUnavailable(error)) throw error;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
const HEX64=/^[0-9a-f]{64}$/;
|
|
2
|
+
|
|
3
|
+
export function classifyAutonomousTimeoutRecovery(status,{beforeJournalHead}={}){
|
|
4
|
+
if(!status||typeof status!=='object'||Array.isArray(status)) throw new Error('autonomous timeout status invalid');
|
|
5
|
+
if(!HEX64.test(beforeJournalHead??'')) throw new Error('autonomous timeout before journal head invalid');
|
|
6
|
+
if(status.status!=='ok') throw new Error('autonomous timeout status not ok');
|
|
7
|
+
if(!HEX64.test(status.journal_head_sha256??'')) throw new Error('autonomous timeout journal head invalid');
|
|
8
|
+
if(status.state==='succeeded') return Object.freeze({kind:'completed',state:'succeeded',journal_head_sha256:status.journal_head_sha256});
|
|
9
|
+
if(status.state==='cancelled'||status.state==='paused'){
|
|
10
|
+
return Object.freeze({kind:'terminal_failure',state:status.state,reason:status.reason??null,journal_head_sha256:status.journal_head_sha256});
|
|
11
|
+
}
|
|
12
|
+
if(status.state!=='pending'&&status.state!=='running') throw new Error(`autonomous timeout unexpected state: ${status.state}`);
|
|
13
|
+
if(status.run_busy===true){
|
|
14
|
+
return Object.freeze({kind:'wait',state:status.state,reason:'server_run_in_flight',journal_head_sha256:status.journal_head_sha256});
|
|
15
|
+
}
|
|
16
|
+
if(status.run_busy!==false) throw new Error('autonomous timeout run-lock state unavailable');
|
|
17
|
+
if(status.pending_request!==null&&status.pending_request!==undefined){
|
|
18
|
+
return Object.freeze({kind:'wait',state:status.state,reason:'planner_request_in_flight',journal_head_sha256:status.journal_head_sha256});
|
|
19
|
+
}
|
|
20
|
+
if(status.pending_decision!==null&&status.pending_decision!==undefined){
|
|
21
|
+
return Object.freeze({kind:'wait',state:status.state,reason:'decision_in_flight',journal_head_sha256:status.journal_head_sha256});
|
|
22
|
+
}
|
|
23
|
+
if(status.journal_head_sha256===beforeJournalHead){
|
|
24
|
+
return Object.freeze({kind:'wait',state:status.state,reason:'no_durable_progress',journal_head_sha256:status.journal_head_sha256});
|
|
25
|
+
}
|
|
26
|
+
return Object.freeze({kind:'resumable',state:status.state,reason:'durable_cycle_completed',journal_head_sha256:status.journal_head_sha256});
|
|
27
|
+
}
|
package/src/deadbyte-cli.mjs
CHANGED
|
@@ -9,7 +9,7 @@ import { fileURLToPath } from 'node:url';
|
|
|
9
9
|
import { defaultPublicKeyPath } from './trust.mjs';
|
|
10
10
|
import { verifyAuditChain } from './audit-verify.mjs';
|
|
11
11
|
import { openRemoteResultJournal, parseRemoteResultCursor } from './remote-result-journal.mjs';
|
|
12
|
-
import {
|
|
12
|
+
import { createLiveTimelineTracker, formatCompactAuditEvent, formatRemoteLifecycle, formatRuntimeDiagnosticLine } from './remote-live-log.mjs';
|
|
13
13
|
|
|
14
14
|
const REMOTE_COMMANDS=new Set([
|
|
15
15
|
'menu','start','resume','park','stop','restart','status','url','test','fulltest','toggle','clean','machinetest',
|
|
@@ -34,9 +34,8 @@ function printHelp(){
|
|
|
34
34
|
process.stdout.write([
|
|
35
35
|
'DEADBYTE MCP CLI','',
|
|
36
36
|
' npx deadbyte-mcp@latest mcp # stdio MCP server for local clients',
|
|
37
|
-
' npx deadbyte-mcp@latest remote #
|
|
38
|
-
' npx deadbyte-mcp@latest remote --
|
|
39
|
-
' npx deadbyte-mcp@latest remote --resume <cursor> # replay missed compact results, then reconnect',
|
|
37
|
+
' npx deadbyte-mcp@latest remote # unified foreground device session + live timeline',
|
|
38
|
+
' npx deadbyte-mcp@latest remote --resume <cursor> # replay missed results, then reconnect',
|
|
40
39
|
' npx deadbyte-mcp@latest remote status',
|
|
41
40
|
' npx deadbyte-mcp@latest remote restart',
|
|
42
41
|
' npx deadbyte-mcp@latest supervisor status',
|
|
@@ -78,35 +77,15 @@ async function listLogFiles(root){
|
|
|
78
77
|
}
|
|
79
78
|
return files;
|
|
80
79
|
}
|
|
81
|
-
function formatLogLine(file,line,{
|
|
80
|
+
function formatLogLine(file,line,{activityTracker=null}={}){
|
|
82
81
|
if(!line) return null;
|
|
83
82
|
if(/^segment-\d{8}\.jsonl$/.test(path.basename(file))){
|
|
84
83
|
try{
|
|
85
84
|
const event=JSON.parse(line);
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
const request=typeof event.request_id==='string'?` ${event.request_id.slice(0,8)}`:'';
|
|
89
|
-
const tool=typeof event.tool==='string'?` ${event.tool}`:'';
|
|
90
|
-
let marker='';
|
|
91
|
-
if(event.event_type==='tool.request') marker='📥 ';
|
|
92
|
-
else if(event.event_type==='tool.result'){
|
|
93
|
-
if(event.outcome?.is_error===true) marker='📤❌ ';
|
|
94
|
-
else if(event.outcome?.is_error===false) marker='📤✅ ';
|
|
95
|
-
else marker='📤 ';
|
|
96
|
-
}
|
|
97
|
-
return `${marker}[AUDIT ${seq}] ${event.event_type||'event'}${tool}${request}`;
|
|
98
|
-
}catch{return verbose?`[AUDIT] ${line}`:null;}
|
|
99
|
-
}
|
|
100
|
-
if(!verbose) return null;
|
|
101
|
-
const label=path.basename(file).replace(/\.log$/i,'').toUpperCase();
|
|
102
|
-
let marker='';
|
|
103
|
-
if(/\[TOOL IN \]/.test(line)) marker='📥 ';
|
|
104
|
-
else if(/\[TOOL OUT\]/.test(line)){
|
|
105
|
-
if(/"is_error"\s*:\s*true/.test(line)) marker='📤❌ ';
|
|
106
|
-
else if(/"is_error"\s*:\s*false/.test(line)) marker='📤✅ ';
|
|
107
|
-
else marker='📤 ';
|
|
85
|
+
return activityTracker?activityTracker.observe(event):formatCompactAuditEvent(event);
|
|
86
|
+
}catch{return null;}
|
|
108
87
|
}
|
|
109
|
-
return
|
|
88
|
+
return formatRuntimeDiagnosticLine(file,line);
|
|
110
89
|
}
|
|
111
90
|
function remoteResultJournalPath(root,sessionId){return path.join(root,'runtime','remote-results',`${sessionId}.json`);}
|
|
112
91
|
function auditTerminalResult(file,line){
|
|
@@ -117,7 +96,7 @@ function auditTerminalResult(file,line){
|
|
|
117
96
|
return {request_id:event.request_id,tool:event.tool,status:event.outcome?.is_error===true?'error':'ok',observed_at_utc:observed,
|
|
118
97
|
payload_sha256:createHash('sha256').update(Buffer.from(line,'utf8')).digest('hex')};
|
|
119
98
|
}
|
|
120
|
-
async function replayRemoteResults(root,cursor
|
|
99
|
+
async function replayRemoteResults(root,cursor){
|
|
121
100
|
const parsed=parseRemoteResultCursor(cursor);
|
|
122
101
|
const file=remoteResultJournalPath(root,parsed.session_id);
|
|
123
102
|
await requireRegular(file,'remote result journal');
|
|
@@ -127,7 +106,7 @@ async function replayRemoteResults(root,cursor,{verbose=false}={}){
|
|
|
127
106
|
const page=await journal.replayAfter(next,{limit:200});
|
|
128
107
|
for(const event of page.events){
|
|
129
108
|
const marker=event.status==='error'?'[ERR]':'[OK]';
|
|
130
|
-
process.stdout.write(
|
|
109
|
+
process.stdout.write(`[REPLAY] ${marker} ${event.tool} [cursor r1:${event.session_id}:${event.seq}]\n`);
|
|
131
110
|
}
|
|
132
111
|
next=page.next_cursor;
|
|
133
112
|
if(!page.has_more) return next;
|
|
@@ -151,10 +130,8 @@ async function snapshotRemoteLogOffsets(root){
|
|
|
151
130
|
}
|
|
152
131
|
return offsets;
|
|
153
132
|
}
|
|
154
|
-
async function tailRemoteLogs(root,signal,{
|
|
155
|
-
process.stdout.write(
|
|
156
|
-
?'📜 [LOG] Live logs active. Press Ctrl+C to disconnect and revoke authority.\n'
|
|
157
|
-
:'[LOG] Live activity. Ctrl+C disconnects and revokes authority.\n');
|
|
133
|
+
async function tailRemoteLogs(root,signal,{journal=null,offsets=null,activityTracker=null}={}){
|
|
134
|
+
process.stdout.write('[LOG] Unified live timeline. Ctrl+C disconnects and revokes authority.\n');
|
|
158
135
|
const activeOffsets=offsets??await snapshotRemoteLogOffsets(root);
|
|
159
136
|
while(!signal.aborted){
|
|
160
137
|
for(const file of await listLogFiles(root)){
|
|
@@ -165,22 +142,20 @@ async function tailRemoteLogs(root,signal,{verbose=false,journal=null,offsets=nu
|
|
|
165
142
|
const terminal=auditTerminalResult(file,line);
|
|
166
143
|
let recorded=null;
|
|
167
144
|
if(terminal&&journal) recorded=await journal.recordTerminal(terminal);
|
|
168
|
-
if(recorded?.duplicate
|
|
169
|
-
const formatted=formatLogLine(file,line,{
|
|
145
|
+
if(recorded?.duplicate) continue;
|
|
146
|
+
const formatted=formatLogLine(file,line,{activityTracker});
|
|
170
147
|
if(!formatted) continue;
|
|
171
|
-
if(
|
|
148
|
+
if(recorded) process.stdout.write(`${formatted} [cursor ${recorded.cursor}]\n`);
|
|
172
149
|
else process.stdout.write(`${formatted}\n`);
|
|
173
150
|
}
|
|
174
151
|
}
|
|
175
152
|
try{await sleep(250,null,{signal});}catch(error){if(error?.name!=='AbortError') throw error;}
|
|
176
153
|
}
|
|
177
154
|
}
|
|
178
|
-
async function cleanupRemoteSession(ctx
|
|
155
|
+
async function cleanupRemoteSession(ctx){
|
|
179
156
|
for(const command of SESSION_CLEANUP){
|
|
180
157
|
try{
|
|
181
|
-
const result=invokeController(ctx,command,
|
|
182
|
-
?{allowFailure:true}
|
|
183
|
-
:{allowFailure:true,capture:true,echoCaptured:false});
|
|
158
|
+
const result=invokeController(ctx,command,{allowFailure:true,capture:true,echoCaptured:false});
|
|
184
159
|
if(result.status!==0) process.stderr.write(`⚠️ [WARN] cleanup ${command} exit=${result.status}\n`);
|
|
185
160
|
}
|
|
186
161
|
catch(error){process.stderr.write(`⚠️ [WARN] cleanup ${command}: ${String(error?.message||error)}\n`);}
|
|
@@ -259,15 +234,15 @@ async function releaseRemoteSession(ctx,marker){
|
|
|
259
234
|
const same=current?.schema==='deadbyte.remote-session.v2'&¤t.owner_pid===marker.owner_pid&¤t.session_nonce===marker.session_nonce;
|
|
260
235
|
if(same) await rm(remoteSessionMarker(ctx.root),{force:true});
|
|
261
236
|
}
|
|
262
|
-
async function runRemoteSession({
|
|
237
|
+
async function runRemoteSession({resumeCursor=null}={}){
|
|
263
238
|
const ctx=await remoteContext();
|
|
264
|
-
if(resumeCursor!==null) await replayRemoteResults(ctx.root,resumeCursor
|
|
239
|
+
if(resumeCursor!==null) await replayRemoteResults(ctx.root,resumeCursor);
|
|
265
240
|
await recoverRemoteSession(ctx);
|
|
266
241
|
const marker=await claimRemoteSession(ctx);
|
|
267
242
|
const resultJournal=await openRemoteResultJournal({
|
|
268
243
|
filePath:remoteResultJournalPath(ctx.root,marker.result_session_id),sessionId:marker.result_session_id,maxEntries:2048
|
|
269
244
|
});
|
|
270
|
-
const activityTracker=
|
|
245
|
+
const activityTracker=createLiveTimelineTracker();
|
|
271
246
|
const aborter=new AbortController();
|
|
272
247
|
const abort=()=>aborter.abort();
|
|
273
248
|
const watchStdin=!process.stdin.isTTY;
|
|
@@ -276,67 +251,53 @@ async function runRemoteSession({verbose=false,resumeCursor=null}={}){
|
|
|
276
251
|
let ready=false;
|
|
277
252
|
let logTask=null;
|
|
278
253
|
try{
|
|
279
|
-
|
|
280
|
-
else process.stdout.write('DEADBYTE MCP\n');
|
|
254
|
+
process.stdout.write('DEADBYTE MCP\n');
|
|
281
255
|
process.stdout.write(`${formatRemoteLifecycle('device_start')}\n`);
|
|
282
256
|
process.stdout.write(`${formatRemoteLifecycle(resumeCursor!==null?'session_restored':'session_new')}\n`);
|
|
283
257
|
const logOffsets=await snapshotRemoteLogOffsets(ctx.root);
|
|
284
|
-
logTask=tailRemoteLogs(ctx.root,aborter.signal,{
|
|
285
|
-
|
|
286
|
-
else process.stdout.write('[BOOT] Starting local + remote runtime...\n');
|
|
258
|
+
logTask=tailRemoteLogs(ctx.root,aborter.signal,{journal:resultJournal,offsets:logOffsets,activityTracker});
|
|
259
|
+
process.stdout.write('[BOOT] Starting local + remote runtime...\n');
|
|
287
260
|
const started=[];
|
|
288
261
|
for(const command of SESSION_START){
|
|
289
|
-
if(
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
}
|
|
293
|
-
const controllerResult=invokeController(ctx,command,verbose?{}:{capture:true,echoCaptured:false});
|
|
262
|
+
if(command==='start') process.stdout.write('[BOOT] Starting bridge/runtime...\n');
|
|
263
|
+
else process.stdout.write(`🔐 [AUTH] Arming ${command.slice(3)}...\n`);
|
|
264
|
+
const controllerResult=invokeController(ctx,command,{capture:true,echoCaptured:false});
|
|
294
265
|
started.push(controllerResult);
|
|
295
266
|
if(command==='start'){
|
|
296
267
|
process.stdout.write(`${formatRemoteLifecycle('bridge_connected')}\n`);
|
|
297
268
|
process.stdout.write(`${formatRemoteLifecycle('cloud_connected')}\n`);
|
|
298
269
|
}
|
|
299
270
|
}
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
process.stdout.write('🔐 [AUTH] Verifying process, coding, autonomous; host must stay DISARMED...\n');
|
|
307
|
-
}else process.stdout.write('🛡️ [AUTHORITY] Verifying runtime planes; host must stay DISARMED...\n');
|
|
271
|
+
const startText=started[0]?.stdout||'';
|
|
272
|
+
const url=startText.match(/^URL\s*:\s*(\S+)/m)?.[1]||'';
|
|
273
|
+
process.stdout.write('[OK] ONLINE\n');
|
|
274
|
+
process.stdout.write(`${formatRemoteLifecycle('ready')}\n`);
|
|
275
|
+
if(url) process.stdout.write(`URL: ${url}\n`);
|
|
276
|
+
process.stdout.write('🔐 [AUTH] Verifying process, coding, autonomous; host must stay DISARMED...\n');
|
|
308
277
|
for(const [command,pattern] of SESSION_STATUS){
|
|
309
|
-
const result=invokeController(ctx,command,{capture:true,echoCaptured:
|
|
278
|
+
const result=invokeController(ctx,command,{capture:true,echoCaptured:false});
|
|
310
279
|
if(!pattern.test(result.stdout)) throw new Error(`remote session authority verification failed: ${command} state mismatch`);
|
|
311
280
|
}
|
|
312
281
|
ready=true;
|
|
313
|
-
|
|
314
|
-
else process.stdout.write('🔐 [AUTH] ARMED: process, coding, autonomous | host DISARMED\n');
|
|
282
|
+
process.stdout.write('🔐 [AUTH] ARMED: process, coding, autonomous | host DISARMED\n');
|
|
315
283
|
await logTask;
|
|
316
284
|
}finally{
|
|
317
285
|
const wasAborted=aborter.signal.aborted;
|
|
318
286
|
aborter.abort();
|
|
319
287
|
if(logTask) await logTask.catch(()=>{});
|
|
320
|
-
|
|
288
|
+
for(const line of activityTracker.reconcileInterrupted('session_end')) process.stdout.write(`${line}\n`);
|
|
321
289
|
process.removeListener('SIGINT',abort);process.removeListener('SIGTERM',abort);
|
|
322
290
|
if(watchStdin){process.stdin.removeListener('end',abort);process.stdin.removeListener('close',abort);process.stdin.pause();}
|
|
323
291
|
process.stdout.write(`${formatRemoteLifecycle('cleanup')}\n`);
|
|
324
|
-
if(ready||!wasAborted) process.stdout.write(
|
|
325
|
-
await cleanupRemoteSession(ctx
|
|
292
|
+
if(ready||!wasAborted) process.stdout.write('[INFO] Disconnecting; revoking authority...\n');
|
|
293
|
+
await cleanupRemoteSession(ctx);
|
|
326
294
|
await releaseRemoteSession(ctx,marker);
|
|
327
295
|
}
|
|
328
296
|
}
|
|
329
297
|
async function runRemote(args){
|
|
330
298
|
if(args.length===0){await runRemoteSession();return;}
|
|
331
|
-
if(args.length===1&&args[0]==='--verbose'){await runRemoteSession({verbose:true});return;}
|
|
332
299
|
if(args.length===2&&args[0]==='--resume'){await runRemoteSession({resumeCursor:args[1]});return;}
|
|
333
|
-
if(args.length
|
|
334
|
-
await runRemoteSession({verbose:true,resumeCursor:args[2]});return;
|
|
335
|
-
}
|
|
336
|
-
if(args.length===3&&args[0]==='--resume'&&args[2]==='--verbose'){
|
|
337
|
-
await runRemoteSession({verbose:true,resumeCursor:args[1]});return;
|
|
338
|
-
}
|
|
339
|
-
if(args.length>1) throw new Error('remote accepts one controller command, --verbose, or --resume <cursor>');
|
|
300
|
+
if(args.length>1) throw new Error('remote accepts one controller command or --resume <cursor>');
|
|
340
301
|
const command=args[0];if(!REMOTE_COMMANDS.has(command)) throw new Error(`unsupported remote command '${command}'`);
|
|
341
302
|
const ctx=await remoteContext();
|
|
342
303
|
const result=invokeController(ctx,command,{allowFailure:true});
|