deadbyte-mcp 0.15.1 → 0.15.3
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 +95 -64
- package/PROVENANCE.json +6 -5
- package/README.txt +36 -20
- package/RELEASE.json +24 -8
- package/SBOM.cdx.json +3 -3
- package/controller/README.TXT +2 -2
- package/controller/deadbyte-controller.ps1 +50 -2
- package/controller/deadbyte-process-policy.json +63 -1
- package/docs/ARCHITECTURE.md +3 -4
- package/package.json +88 -86
- package/scripts/autonomous-build.ps1 +1 -0
- package/scripts/autonomous-tests.ps1 +1 -0
- package/scripts/check-agent-backend-r46.mjs +77 -0
- package/scripts/check-atomic-resource-r46.mjs +84 -0
- package/scripts/check-autonomous-race-r46.mjs +160 -0
- package/scripts/check-coding-task-deadline-r46.mjs +59 -0
- package/scripts/check-deadline-cancellation-r46.mjs +49 -0
- package/scripts/check-process-deadline-r46.mjs +52 -0
- package/scripts/check-r46-background.mjs +5 -0
- package/scripts/deferred-slot-operation-r46.mjs +126 -0
- package/scripts/extract-r46-failures.mjs +5 -0
- package/scripts/final-closure-r46.mjs +98 -0
- package/scripts/final-closure-verify-r46.mjs +119 -0
- package/scripts/full-regression-r46.mjs +28 -0
- package/scripts/gate-windows-r46.ps1 +58 -0
- package/scripts/inspect-r46-active.mjs +2 -0
- package/scripts/r47-core-failures.mjs +1 -0
- package/scripts/refresh-process-policy.mjs +64 -7
- package/scripts/release-manifest.mjs +1 -1
- package/scripts/release-parity-r46.mjs +187 -0
- package/scripts/run-r46-full-regression-background.mjs +7 -0
- package/scripts/run-r46-windows-gate-background.mjs +6 -0
- package/scripts/run-r46-windows-gate-background.ps1 +10 -0
- package/scripts/windows-gate-evidence-r46.mjs +111 -0
- package/scripts/workload-contract-regression-r47.mjs +14 -0
- package/scripts/workload-stream-regression.mjs +16 -0
- package/scripts/workload-substrate-r47-regression.mjs +12 -0
- package/src/agent-backend-r44.mjs +75 -10
- package/src/atomic-mutation.mjs +55 -0
- package/src/autonomous-mcp-tools.mjs +4 -3
- package/src/autonomous-output-contracts.mjs +16 -3
- package/src/autonomous-runtime.mjs +83 -30
- package/src/capability-ledger.mjs +1 -0
- package/src/coding-mcp-tools.mjs +2 -2
- package/src/coding-output-contracts.mjs +18 -4
- package/src/coding-plane.mjs +3 -24
- package/src/coding-search.mjs +11 -2
- package/src/coding-task-runtime.mjs +41 -6
- package/src/compact-gateway-mcp-tools.mjs +7 -5
- package/src/deadbyte-cli.mjs +7 -3
- package/src/deadline-cancellation.mjs +83 -0
- package/src/machine-fs-mcp-tools.mjs +3 -3
- package/src/machine-fs-output-contracts.mjs +1 -1
- package/src/machine-fs-read-worker.mjs +46 -0
- package/src/machine-fs-smoke-client.mjs +1 -1
- package/src/machine-fs.mjs +66 -33
- package/src/mcp-server.mjs +10 -0
- package/src/mcp-smoke-client.mjs +4 -1
- package/src/observation-runtime.mjs +25 -4
- package/src/observation-smoke-client.mjs +1 -1
- package/src/process-mcp-tools.mjs +29 -8
- package/src/process-output-contracts.mjs +43 -3
- package/src/process-policy.mjs +3 -1
- package/src/process-runtime.mjs +152 -23
- package/src/process-smoke-client.mjs +67 -22
- package/src/release-generation.mjs +22 -3
- package/src/remote-device-r44.mjs +53 -15
- package/src/remote-mcp-smoke-client.mjs +1 -1
- package/src/resource-identity.mjs +59 -0
- package/src/runtime-update.mjs +526 -162
- package/src/stdio-console-guard.mjs +24 -0
- package/src/version.mjs +1 -1
- package/src/workload-substrate.mjs +87 -0
- package/test/cli-entrypoint.test.mjs +3 -3
- package/test/coding-runtime.test.mjs +12 -0
- package/test/coding-search-r36.test.mjs +7 -0
- package/test/compact-gateway-r36.test.mjs +28 -0
- package/test/core.test.mjs +1 -1
- package/test/full-max-catalog-r46.test.mjs +92 -0
- package/test/helpers/autonomous-r34-fixture.mjs +10 -1
- package/test/machine-fs-mcp-tools.test.mjs +4 -1
- package/test/machine-fs-runtime.test.mjs +18 -2
- package/test/observation-runtime.test.mjs +29 -2
- package/test/process-mcp-tools.test.mjs +27 -6
- package/test/process-policy-refresh.test.mjs +87 -10
- package/test/process-runtime.test.mjs +129 -2
- package/test/process-supervision.test.mjs +4 -2
- package/test/production-docs.test.mjs +3 -3
- package/test/r33-finalization.test.mjs +3 -3
- package/test/r45-finalization.test.mjs +1 -1
- package/test/release-generation.test.mjs +25 -11
- package/test/release-version.test.mjs +99 -44
- package/test/remote-device-r44.test.mjs +75 -0
- package/test/remote-r24-catalog.test.mjs +2 -2
- package/test/runtime-self-update.test.mjs +118 -1
- package/test/workload-substrate.test.mjs +22 -0
|
@@ -17,6 +17,8 @@ import { deriveAgentMemory } from './agent-memory-plane.mjs';
|
|
|
17
17
|
import { effectIdentity, loopGuardDisposition, bindEffectCompletion } from './effect-ledger.mjs';
|
|
18
18
|
import { createWorkerScheduler } from './worker-scheduler.mjs';
|
|
19
19
|
import { issueObservationLease, verifyObservationLease } from './observation-lease.mjs';
|
|
20
|
+
import { createJournalStream } from './workload-substrate.mjs';
|
|
21
|
+
import { createDeadline, deadlineState } from './deadline-cancellation.mjs';
|
|
20
22
|
|
|
21
23
|
export const AUTONOMOUS_GOAL_SCHEMA = 'deadbyte.autonomous-goal.v1';
|
|
22
24
|
export const AUTONOMOUS_EVENT_SCHEMA = 'deadbyte.autonomous-event.v1';
|
|
@@ -229,7 +231,7 @@ function profileOutcome(profile, checked) {
|
|
|
229
231
|
{ median_ms:receipt.median_ms, p95_ms:receipt.p95_ms, regression_pct:receipt.regression_pct, verdict:receipt.verdict }) } };
|
|
230
232
|
}
|
|
231
233
|
async function executeProfile({ ctx, events, goalId, actionKey, profileId, epoch, policy, codingRuntime, signingKeyPath,
|
|
232
|
-
r34ResumePhase = null }) {
|
|
234
|
+
r34ResumePhase = null, childDeadline = null }) {
|
|
233
235
|
const profile = policy.profiles[profileId];
|
|
234
236
|
if (!profile) throw new Error(`autonomous profile '${profileId}' not found`);
|
|
235
237
|
const prior = [...events].reverse().find(event => event.type === 'profile_completed' && event.payload?.action_key === actionKey && event.payload?.profile_id === profileId);
|
|
@@ -274,6 +276,9 @@ async function executeProfile({ ctx, events, goalId, actionKey, profileId, epoch
|
|
|
274
276
|
await appendEvent(ctx,events,signingKeyPath,'profile_completed',payload);
|
|
275
277
|
return { status:outcome.passed ? 'passed' : 'failed', observation:payload, existing:false };
|
|
276
278
|
}
|
|
279
|
+
const childRemainingRaw = childDeadline ? deadlineState(childDeadline).remaining_ms : null;
|
|
280
|
+
const childRemainingMs = childRemainingRaw === null ? null : Math.floor(childRemainingRaw);
|
|
281
|
+
if (childRemainingMs !== null && childRemainingMs < 100) throw new Error('deadline_exceeded');
|
|
277
282
|
const autonomousAuthority = await assertAutonomousArmed(policy,profile.root_id);
|
|
278
283
|
if (Number.isFinite(autonomousAuthority.remaining_ms) && autonomousAuthority.remaining_ms < 100) {
|
|
279
284
|
throw new Error('autonomous lease expires too soon to execute profile');
|
|
@@ -283,20 +288,22 @@ async function executeProfile({ ctx, events, goalId, actionKey, profileId, epoch
|
|
|
283
288
|
authority_instance_nonce:autonomousAuthority.instance_nonce,
|
|
284
289
|
authority_expires_at_utc:autonomousAuthority.expires_at_utc });
|
|
285
290
|
const preExecutionCheck = () => recheckAutonomousAuthority(policy,autonomousAuthority,profile.root_id);
|
|
291
|
+
const childTimeoutMs = childRemainingMs === null ? profile.timeout_ms : Math.max(100, Math.min(profile.timeout_ms, childRemainingMs));
|
|
286
292
|
let result;
|
|
287
293
|
try {
|
|
288
294
|
result = profile.kind === 'command'
|
|
289
295
|
? await codingRuntime.commandRun({ rootId:profile.root_id, commandId:profile.command_id, scriptPath:profile.script_path,
|
|
290
|
-
scriptSha256:profile.script_sha256, args:profile.args, cwd:profile.cwd, timeoutMs:
|
|
296
|
+
scriptSha256:profile.script_sha256, args:profile.args, cwd:profile.cwd, timeoutMs:childTimeoutMs,
|
|
291
297
|
executionDeadlineUtc:autonomousAuthority.expires_at_utc, preExecutionCheck, receiptId, requestSha256 })
|
|
292
298
|
: await codingRuntime.benchmarkRun({ rootId:profile.root_id, commandId:profile.command_id, scriptPath:profile.script_path,
|
|
293
|
-
scriptSha256:profile.script_sha256, args:profile.args, cwd:profile.cwd, timeoutMs:
|
|
299
|
+
scriptSha256:profile.script_sha256, args:profile.args, cwd:profile.cwd, timeoutMs:childTimeoutMs,
|
|
294
300
|
executionDeadlineUtc:autonomousAuthority.expires_at_utc, preExecutionCheck,
|
|
295
301
|
warmups:profile.warmups, iterations:profile.iterations, baselineMedianMs:profile.baseline_median_ms,
|
|
296
302
|
maxRegressionPct:profile.max_regression_pct, receiptId, requestSha256 });
|
|
297
303
|
} catch (error) {
|
|
298
304
|
const checked = await codingRuntime.verifyReceiptDetailed(receiptId);
|
|
299
305
|
if (!checked.ok) {
|
|
306
|
+
if (childDeadline && deadlineState(childDeadline).expired) throw new Error('deadline_exceeded');
|
|
300
307
|
const r34Paused=await pauseR34Ambiguous(checked.reason ?? error);
|
|
301
308
|
if (r34Paused) return r34Paused;
|
|
302
309
|
await appendEvent(ctx,events,signingKeyPath,'goal_paused',{ reason:'profile_execution_without_receipt', profile_id:profileId,
|
|
@@ -311,6 +318,7 @@ async function executeProfile({ ctx, events, goalId, actionKey, profileId, epoch
|
|
|
311
318
|
if (r34Paused) return r34Paused;
|
|
312
319
|
throw new Error(`profile '${profileId}' post-execution receipt verification failed`);
|
|
313
320
|
}
|
|
321
|
+
if (childDeadline && deadlineState(childDeadline).expired) throw new Error('deadline_exceeded');
|
|
314
322
|
const outcome = profileOutcome(profile,checked);
|
|
315
323
|
if (!outcome.valid) {
|
|
316
324
|
const r34Paused=await pauseR34Ambiguous(outcome.reason);
|
|
@@ -363,7 +371,7 @@ function normalizeTaskContract(raw) {
|
|
|
363
371
|
constraints:list('constraints',32,512),evidence_requirements:list('evidence_requirements',32,512),
|
|
364
372
|
risk_level:value.risk_level,clarification_policy:value.clarification_policy,...rigor});
|
|
365
373
|
}
|
|
366
|
-
function normalizeGoal(policy, { submissionId, rootId, title = '', goal, acceptance = '', taskContract = null, maxIterations = null }) {
|
|
374
|
+
function normalizeGoal(policy, { submissionId, rootId, title = '', goal, acceptance = '', taskContract = null, maxIterations = null, timeoutMs = null }) {
|
|
367
375
|
if (!ID_RE.test(submissionId ?? '')) throw new Error('autonomous submission_id invalid');
|
|
368
376
|
if (!policy.root_ids.includes(rootId)) throw new Error(`autonomous root '${rootId}' is not allowed`);
|
|
369
377
|
if (typeof title !== 'string' || title.length > 160) throw new Error('autonomous title must be <=160 characters');
|
|
@@ -371,9 +379,10 @@ function normalizeGoal(policy, { submissionId, rootId, title = '', goal, accepta
|
|
|
371
379
|
if (typeof acceptance !== 'string' || acceptance.length > 4096) throw new Error('autonomous acceptance must be <=4096 characters');
|
|
372
380
|
const iterations = maxIterations ?? policy.limits.max_iterations;
|
|
373
381
|
if (!Number.isInteger(iterations) || iterations < 1 || iterations > policy.limits.max_iterations) throw new Error('autonomous max_iterations exceeds policy');
|
|
382
|
+
if (timeoutMs !== null && (!Number.isInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 900000)) throw new Error('autonomous timeout_ms must be null or in [100,900000]');
|
|
374
383
|
const body = { schema:AUTONOMOUS_GOAL_SCHEMA, policy_sha256:policy.policy_sha256,
|
|
375
384
|
coding_policy_sha256:policy.coding_policy_sha256, submission_id:submissionId, root_id:rootId,
|
|
376
|
-
title, goal, acceptance, task_contract:normalizeTaskContract(taskContract),max_iterations:iterations };
|
|
385
|
+
title, goal, acceptance, task_contract:normalizeTaskContract(taskContract),max_iterations:iterations, timeout_ms:timeoutMs };
|
|
377
386
|
return Object.freeze({ ...body, goal_sha256:sha256Object(body) });
|
|
378
387
|
}
|
|
379
388
|
|
|
@@ -731,7 +740,9 @@ function assertDelegatedIdentity(events, operation) {
|
|
|
731
740
|
}
|
|
732
741
|
}
|
|
733
742
|
}
|
|
734
|
-
async function invokeDelegatedOperation(goal, operation, runtimes, desktopContext=null) {
|
|
743
|
+
async function invokeDelegatedOperation(goal, operation, runtimes, desktopContext=null, childDeadline=null) {
|
|
744
|
+
const remainingMs = childDeadline ? Math.max(100, Math.floor(deadlineState(childDeadline).remaining_ms)) : null;
|
|
745
|
+
if (childDeadline && deadlineState(childDeadline).expired) throw new Error('deadline_exceeded');
|
|
735
746
|
const { codingRuntime, semanticRuntime, nativeDiffRuntime, machineFsRuntime, observationRuntime, processRuntime, desktopRuntime } = runtimes;
|
|
736
747
|
switch (operation.kind) {
|
|
737
748
|
case 'stat':
|
|
@@ -804,7 +815,7 @@ async function invokeDelegatedOperation(goal, operation, runtimes, desktopContex
|
|
|
804
815
|
return observationRuntime.systemStatus();
|
|
805
816
|
case 'process_start':
|
|
806
817
|
if (!processRuntime) throw new Error('process runtime unavailable');
|
|
807
|
-
return processRuntime.start({ profileId:operation.profile_id, rootId:goal.root_id, cwd:operation.cwd, args:operation.args??[] });
|
|
818
|
+
return processRuntime.start({ profileId:operation.profile_id, rootId:goal.root_id, cwd:operation.cwd, args:operation.args??[], timeoutMs:remainingMs });
|
|
808
819
|
case 'process_read':
|
|
809
820
|
if (!processRuntime) throw new Error('process runtime unavailable');
|
|
810
821
|
return processRuntime.read({ sessionId:operation.session_id, offset:operation.offset, maxBytes:operation.max_bytes });
|
|
@@ -893,7 +904,7 @@ async function verifyDelegatedEvidence(operation, result, runtimes, verifyKeyPat
|
|
|
893
904
|
return { kind:'runtime_result', result_sha256:sha256Object(result) };
|
|
894
905
|
}
|
|
895
906
|
async function executeDelegate({ ctx, events, goal, decisionEvent, decision, policy, runtimes, assertRunAuthority,
|
|
896
|
-
signingKeyPath, verifyKeyPath }) {
|
|
907
|
+
signingKeyPath, verifyKeyPath, childDeadline = null }) {
|
|
897
908
|
const capabilityId=capabilityIdForOperation(decision.operation);
|
|
898
909
|
if(!capabilityId) throw new Error(`unsupported typed capability action '${String(decision.operation?.kind)}'`);
|
|
899
910
|
const actionKey = `capability-${decisionEvent.event_sha256}`;
|
|
@@ -939,7 +950,7 @@ async function executeDelegate({ ctx, events, goal, decisionEvent, decision, pol
|
|
|
939
950
|
}
|
|
940
951
|
};
|
|
941
952
|
}
|
|
942
|
-
const result = await invokeDelegatedOperation(goal,decision.operation,runtimes,desktopContext);
|
|
953
|
+
const result = await invokeDelegatedOperation(goal,decision.operation,runtimes,desktopContext,childDeadline);
|
|
943
954
|
if (Buffer.byteLength(canonicalJson(result),'utf8') > policy.limits.max_context_bytes) {
|
|
944
955
|
throw new Error('delegated result exceeds autonomous context bound');
|
|
945
956
|
}
|
|
@@ -971,7 +982,7 @@ async function executeDelegate({ ctx, events, goal, decisionEvent, decision, pol
|
|
|
971
982
|
}
|
|
972
983
|
}
|
|
973
984
|
|
|
974
|
-
async function executeProfileWithLease({workerScheduler,ctx,events,goal,epoch,actionKey,profileId,policy,codingRuntime,signingKeyPath,r34ResumePhase=null}) {
|
|
985
|
+
async function executeProfileWithLease({workerScheduler,ctx,events,goal,epoch,actionKey,profileId,policy,codingRuntime,signingKeyPath,r34ResumePhase=null,childDeadline=null}) {
|
|
975
986
|
if(!workerScheduler) throw new Error('R37 worker scheduler unavailable');
|
|
976
987
|
const taskId=`${goal.goal_id}:${actionKey}:${profileId}`;
|
|
977
988
|
const lease=workerScheduler.acquire({taskId,workerClass:'profile',cpuUnits:1,memoryMb:256});
|
|
@@ -981,7 +992,7 @@ async function executeProfileWithLease({workerScheduler,ctx,events,goal,epoch,ac
|
|
|
981
992
|
});
|
|
982
993
|
let outcomeStatus='failed';
|
|
983
994
|
try {
|
|
984
|
-
const result=await executeProfile({ctx,events,goalId:goal.goal_id,actionKey,profileId,epoch,policy,codingRuntime,signingKeyPath,r34ResumePhase});
|
|
995
|
+
const result=await executeProfile({ctx,events,goalId:goal.goal_id,actionKey,profileId,epoch,policy,codingRuntime,signingKeyPath,r34ResumePhase,childDeadline});
|
|
985
996
|
outcomeStatus=result.status;
|
|
986
997
|
return result;
|
|
987
998
|
} finally {
|
|
@@ -993,14 +1004,14 @@ async function executeProfileWithLease({workerScheduler,ctx,events,goal,epoch,ac
|
|
|
993
1004
|
}
|
|
994
1005
|
}
|
|
995
1006
|
|
|
996
|
-
async function runMutationProfiles({ ctx, events, goal, epoch, actionKey, policy, codingRuntime, signingKeyPath, workerScheduler }) {
|
|
1007
|
+
async function runMutationProfiles({ ctx, events, goal, epoch, actionKey, policy, codingRuntime, signingKeyPath, workerScheduler, childDeadline=null }) {
|
|
997
1008
|
const profileIds = Object.entries(policy.profiles)
|
|
998
1009
|
.filter(([,profile]) => profile.run_after_mutation)
|
|
999
1010
|
.map(([id]) => id);
|
|
1000
1011
|
const results = [];
|
|
1001
1012
|
for (const profileId of profileIds) {
|
|
1002
1013
|
const result = await executeProfileWithLease({workerScheduler,ctx,events,goal,epoch,
|
|
1003
|
-
actionKey:`${actionKey}:${profileId}`,profileId,policy,codingRuntime,signingKeyPath});
|
|
1014
|
+
actionKey:`${actionKey}:${profileId}`,profileId,policy,codingRuntime,signingKeyPath,childDeadline});
|
|
1004
1015
|
results.push({ profile_id:profileId, status:result.status, observation:result.observation });
|
|
1005
1016
|
if (result.status === 'paused') break;
|
|
1006
1017
|
}
|
|
@@ -1402,7 +1413,7 @@ function r34ReasonHash(goal,decisionEvent,reason) {
|
|
|
1402
1413
|
}
|
|
1403
1414
|
async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,policy,codingRuntime,
|
|
1404
1415
|
nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime,assertRunAuthority,
|
|
1405
|
-
signingKeyPath,verifyKeyPath,workerScheduler}) {
|
|
1416
|
+
signingKeyPath,verifyKeyPath,workerScheduler,childDeadline=null}) {
|
|
1406
1417
|
let loop=deriveLoopState(goal,events);
|
|
1407
1418
|
if (decision.kind === 'plan_create') {
|
|
1408
1419
|
if (loop.phase !== 'PLAN' || loop.plan) throw new Error('R34 plan_create requires empty PLANNING state');
|
|
@@ -1460,11 +1471,11 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
|
|
|
1460
1471
|
await assertRunAuthority(goal.root_id);
|
|
1461
1472
|
outcome=await executeProfileWithLease({workerScheduler,ctx,events,goal,
|
|
1462
1473
|
actionKey:`r34-${decisionEvent.event_sha256}`,profileId:decision.action.profile_id,
|
|
1463
|
-
epoch:state.mutation_epoch,policy,codingRuntime,signingKeyPath,r34ResumePhase:loop.phase});
|
|
1474
|
+
epoch:state.mutation_epoch,policy,codingRuntime,signingKeyPath,r34ResumePhase:loop.phase,childDeadline});
|
|
1464
1475
|
} else if (decision.action.kind === 'delegate') {
|
|
1465
1476
|
outcome=await executeDelegate({ctx,events,goal,decisionEvent,
|
|
1466
1477
|
decision:{operation:decision.action.operation},policy,
|
|
1467
|
-
runtimes:{codingRuntime,semanticRuntime,nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,desktopRuntime},assertRunAuthority,signingKeyPath,verifyKeyPath});
|
|
1478
|
+
runtimes:{codingRuntime,semanticRuntime,nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,desktopRuntime},assertRunAuthority,signingKeyPath,verifyKeyPath,childDeadline});
|
|
1468
1479
|
} else if (decision.action.kind === 'native_diff_apply') {
|
|
1469
1480
|
await assertRunAuthority(goal.root_id);
|
|
1470
1481
|
outcome=await executePreparedDiff({ctx,events,goal,state,decisionEvent,diffId:decision.action.diff_id,
|
|
@@ -1493,7 +1504,7 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
|
|
|
1493
1504
|
} else if (capabilityIdForOperation(decision.action)) {
|
|
1494
1505
|
outcome=await executeDelegate({ctx,events,goal,decisionEvent,
|
|
1495
1506
|
decision:{operation:decision.action},policy,
|
|
1496
|
-
runtimes:{codingRuntime,semanticRuntime,nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,desktopRuntime},assertRunAuthority,signingKeyPath,verifyKeyPath});
|
|
1507
|
+
runtimes:{codingRuntime,semanticRuntime,nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,desktopRuntime},assertRunAuthority,signingKeyPath,verifyKeyPath,childDeadline});
|
|
1497
1508
|
} else {
|
|
1498
1509
|
throw new Error(`unsupported R34 action '${String(decision.action.kind)}'`);
|
|
1499
1510
|
}
|
|
@@ -1612,12 +1623,12 @@ async function executeR34Decision({ctx,events,goal,state,decisionEvent,decision,
|
|
|
1612
1623
|
|
|
1613
1624
|
async function executeDecisionCore({ ctx, events, goal, state, decisionEvent, policy, codingRuntime,
|
|
1614
1625
|
nativeDiffRuntime, machineFsRuntime, observationRuntime, processRuntime, semanticRuntime, desktopRuntime, assertRunAuthority,
|
|
1615
|
-
signingKeyPath, verifyKeyPath, workerScheduler }) {
|
|
1626
|
+
signingKeyPath, verifyKeyPath, workerScheduler, childDeadline = null }) {
|
|
1616
1627
|
const decision = decisionEvent.decision;
|
|
1617
1628
|
if (decision?.schema === 'deadbyte.autonomous-decision.v2') {
|
|
1618
1629
|
const outcome=await executeR34Decision({ctx,events,goal,state,decisionEvent,decision,policy,codingRuntime,
|
|
1619
1630
|
nativeDiffRuntime,machineFsRuntime,observationRuntime,processRuntime,semanticRuntime,desktopRuntime,assertRunAuthority,
|
|
1620
|
-
signingKeyPath,verifyKeyPath,workerScheduler});
|
|
1631
|
+
signingKeyPath,verifyKeyPath,workerScheduler,childDeadline});
|
|
1621
1632
|
await appendEvent(ctx,events,signingKeyPath,'decision_completed',{
|
|
1622
1633
|
decision_event_sha256:decisionEvent.event_sha256,action:decision.kind,
|
|
1623
1634
|
outcome_status:outcome.status ?? 'completed'
|
|
@@ -1650,12 +1661,12 @@ async function executeDecisionCore({ ctx, events, goal, state, decisionEvent, po
|
|
|
1650
1661
|
policy,codingRuntime,signingKeyPath,workerScheduler });
|
|
1651
1662
|
} else if (decision.action === 'delegate') {
|
|
1652
1663
|
outcome = await executeDelegate({ ctx,events,goal,decisionEvent,decision,policy,
|
|
1653
|
-
runtimes:{ machineFsRuntime,observationRuntime,processRuntime },assertRunAuthority,signingKeyPath,verifyKeyPath });
|
|
1664
|
+
runtimes:{ machineFsRuntime,observationRuntime,processRuntime },assertRunAuthority,signingKeyPath,verifyKeyPath,childDeadline });
|
|
1654
1665
|
} else if (decision.action === 'run_profile') {
|
|
1655
1666
|
await assertRunAuthority(goal.root_id);
|
|
1656
1667
|
outcome = await executeProfileWithLease({workerScheduler,ctx,events,goal,
|
|
1657
1668
|
actionKey:`planner-${decisionEvent.event_sha256}`,profileId:decision.profile_id,
|
|
1658
|
-
epoch:state.mutation_epoch,policy,codingRuntime,signingKeyPath});
|
|
1669
|
+
epoch:state.mutation_epoch,policy,codingRuntime,signingKeyPath,childDeadline});
|
|
1659
1670
|
} else if (decision.action === 'pause') {
|
|
1660
1671
|
await appendEvent(ctx,events,signingKeyPath,'goal_paused',{
|
|
1661
1672
|
reason:decision.reason, planner_summary:decision.summary
|
|
@@ -1817,9 +1828,9 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1817
1828
|
return { autonomous, coding };
|
|
1818
1829
|
}
|
|
1819
1830
|
|
|
1820
|
-
async function submit({ submissionId, rootId, title = '', goal, acceptance = '', taskContract = null, maxIterations = null }) {
|
|
1831
|
+
async function submit({ submissionId, rootId, title = '', goal, acceptance = '', taskContract = null, maxIterations = null, timeoutMs = null }) {
|
|
1821
1832
|
await assertRunAuthority(rootId);
|
|
1822
|
-
const normalized = normalizeGoal(policy,{ submissionId,rootId,title,goal,acceptance,taskContract,maxIterations });
|
|
1833
|
+
const normalized = normalizeGoal(policy,{ submissionId,rootId,title,goal,acceptance,taskContract,maxIterations,timeoutMs });
|
|
1823
1834
|
const goalId = goalIdFor(policy.policy_sha256,submissionId);
|
|
1824
1835
|
const ctx = goalContext(policy,goalId);
|
|
1825
1836
|
if (await exists(ctx.dir)) {
|
|
@@ -1838,7 +1849,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1838
1849
|
await appendEvent(ctx,journal,signingKeyPath,'goal_created',{
|
|
1839
1850
|
submission_id:submissionId, root_id:rootId, title, acceptance:normalized.acceptance,
|
|
1840
1851
|
task_contract:normalized.task_contract,goal_sha256:normalized.goal_sha256,
|
|
1841
|
-
max_iterations:normalized.max_iterations,
|
|
1852
|
+
max_iterations:normalized.max_iterations, timeout_ms:normalized.timeout_ms,
|
|
1842
1853
|
...(memoryProvider?{source_release_manifest:memoryProvider.source_release_manifest}:{})
|
|
1843
1854
|
});
|
|
1844
1855
|
return { status:'accepted', goal_id:goalId, goal_sha256:normalized.goal_sha256, state:'pending' };
|
|
@@ -1849,11 +1860,18 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1849
1860
|
const goal = await loadGoal(ctx,policy,verifyKeyPath);
|
|
1850
1861
|
const journal = await readEvents(ctx,verifyKeyPath);
|
|
1851
1862
|
const derived = deriveState(goal,journal);
|
|
1863
|
+
const goalCreated = journal.find(event => event.type === 'goal_created');
|
|
1864
|
+
const goalCreatedAtMs = goalCreated ? Date.parse(goalCreated.created_at_utc) : NaN;
|
|
1865
|
+
if (!Number.isFinite(goalCreatedAtMs)) throw new Error('autonomous goal creation timestamp invalid');
|
|
1866
|
+
const deadline = createDeadline({ timeoutMs:goal.timeout_ms, nowMs:goalCreatedAtMs, id:goalId });
|
|
1852
1867
|
const loop = publicLoopState(loopStateOrNull(goal,journal),goal,journal);
|
|
1853
1868
|
const lock=await runLockStatus(ctx);
|
|
1869
|
+
const stream=createJournalStream({streamId:goalId,events:journal,maxItems:10000,maxBytes:16*1024*1024,producer:'autonomous-goal',channel:'event'});
|
|
1870
|
+
const snapshot=stream.snapshot();
|
|
1854
1871
|
return { status:'ok', goal_id:goalId, goal_sha256:goal.goal_sha256,
|
|
1855
|
-
title:goal.title, root_id:goal.root_id, journal_verified:true, ...derived, ...lock,
|
|
1856
|
-
|
|
1872
|
+
title:goal.title, root_id:goal.root_id, timeout_ms:goal.timeout_ms, deadline, journal_verified:true, ...derived, ...lock,
|
|
1873
|
+
stream_id:snapshot.stream_id, stream_generation:snapshot.generation, stream_next_seq:snapshot.next_seq,
|
|
1874
|
+
stream_coverage:stream.coverage(), ...(loop ? {loop} : {}) };
|
|
1857
1875
|
}
|
|
1858
1876
|
|
|
1859
1877
|
async function events({ goalId, fromSeq = 1, limit = 100 }) {
|
|
@@ -1861,9 +1879,13 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1861
1879
|
if (!Number.isInteger(limit) || limit < 1 || limit > 200) throw new Error('autonomous event limit must be in [1,200]');
|
|
1862
1880
|
const ctx = goalContext(policy,goalId); await loadGoal(ctx,policy,verifyKeyPath);
|
|
1863
1881
|
const journal = await readEvents(ctx,verifyKeyPath);
|
|
1882
|
+
const stream=createJournalStream({streamId:goalId,events:journal,maxItems:10000,maxBytes:16*1024*1024,producer:'autonomous-goal',channel:'event'});
|
|
1883
|
+
const page=stream.read(fromSeq,limit);
|
|
1884
|
+
const snapshot=stream.snapshot();
|
|
1864
1885
|
return { status:'ok', goal_id:goalId, journal_verified:true,
|
|
1865
1886
|
journal_head_sha256:journal.at(-1)?.event_sha256 ?? ZERO_HASH,
|
|
1866
|
-
|
|
1887
|
+
stream_id:snapshot.stream_id, stream_generation:snapshot.generation, stream_next_seq:snapshot.next_seq,
|
|
1888
|
+
stream_coverage:stream.coverage(), events:page.items.map(item=>item.value) };
|
|
1867
1889
|
}
|
|
1868
1890
|
async function cancel({ goalId, reason = 'operator_request' }) {
|
|
1869
1891
|
if (typeof reason !== 'string' || reason.length < 1 || reason.length > 256) throw new Error('autonomous cancel reason invalid');
|
|
@@ -1896,6 +1918,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
1896
1918
|
const current = deriveState(goal,journal);
|
|
1897
1919
|
if (terminalState(current)) return withLoopResult(goal,journal,{ status:'terminal', goal_id:goalId, state:current.state });
|
|
1898
1920
|
if (!pauseState(current)) throw new Error('autonomous goal is not paused');
|
|
1921
|
+
if (current.reason === 'deadline_exceeded') throw new Error('autonomous goal deadline expired');
|
|
1899
1922
|
if (decision === 'reject') {
|
|
1900
1923
|
await appendEvent(ctx,journal,signingKeyPath,'goal_cancelled',{ reason:'resume_rejected' });
|
|
1901
1924
|
return withLoopResult(goal,journal,{ status:'rejected', goal_id:goalId, state:'cancelled' });
|
|
@@ -2026,9 +2049,26 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
2026
2049
|
if (await consumeCancel(ctx,journal,signingKeyPath,verifyKeyPath)) {
|
|
2027
2050
|
state = deriveState(goal,journal); return withLoopResult(goal,journal,{ status:'cancelled', goal_id:goalId, ...state });
|
|
2028
2051
|
}
|
|
2029
|
-
|
|
2052
|
+
const goalCreated = journal.find(event => event.type === 'goal_created');
|
|
2053
|
+
const goalCreatedAtMs = goalCreated ? Date.parse(goalCreated.created_at_utc) : NaN;
|
|
2054
|
+
if (!Number.isFinite(goalCreatedAtMs)) throw new Error('autonomous goal creation timestamp invalid');
|
|
2055
|
+
const deadline = createDeadline({ timeoutMs:goal.timeout_ms, nowMs:goalCreatedAtMs, id:goalId });
|
|
2056
|
+
const deadlineExpired = () => goal.timeout_ms !== null && deadlineState(deadline).expired;
|
|
2057
|
+
const stopAtDeadline = async () => {
|
|
2058
|
+
if (!deadlineExpired()) return false;
|
|
2059
|
+
const existing = deriveState(goal,journal);
|
|
2060
|
+
if (!(existing.state === 'paused' && existing.reason === 'deadline_exceeded')) {
|
|
2061
|
+
await appendEvent(ctx,journal,signingKeyPath,'goal_paused',{ reason:'deadline_exceeded', deadline });
|
|
2062
|
+
}
|
|
2063
|
+
return true;
|
|
2064
|
+
};
|
|
2065
|
+
await appendEvent(ctx,journal,signingKeyPath,'goal_run_started',{ max_cycles:maxCycles, deadline });
|
|
2030
2066
|
let cycles = 0; let suppliedResponse = plannerResponse;
|
|
2031
2067
|
while (cycles < maxCycles) {
|
|
2068
|
+
if (await stopAtDeadline()) {
|
|
2069
|
+
state = deriveState(goal,journal);
|
|
2070
|
+
return withLoopResult(goal,journal,{ status:'paused', reason:'deadline_exceeded', goal_id:goalId, deadline, ...state });
|
|
2071
|
+
}
|
|
2032
2072
|
state = deriveState(goal,journal);
|
|
2033
2073
|
if (terminalState(state) || pauseState(state)) return withLoopResult(goal,journal,{ status:state.state, goal_id:goalId, ...state });
|
|
2034
2074
|
if (await consumeCancel(ctx,journal,signingKeyPath,verifyKeyPath)) {
|
|
@@ -2039,14 +2079,23 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
2039
2079
|
try {
|
|
2040
2080
|
outcome = await executeDecision({ ctx,events:journal,goal,state,
|
|
2041
2081
|
decisionEvent:state.pending_decision,policy,codingRuntime,nativeDiffRuntime,machineFsRuntime,
|
|
2042
|
-
observationRuntime,processRuntime,semanticRuntime,desktopRuntime,assertRunAuthority,signingKeyPath,verifyKeyPath,workerScheduler });
|
|
2082
|
+
observationRuntime,processRuntime,semanticRuntime,desktopRuntime,assertRunAuthority,signingKeyPath,verifyKeyPath,workerScheduler,childDeadline:deadline });
|
|
2043
2083
|
} catch (error) {
|
|
2084
|
+
if (deadlineExpired() || error?.message === 'deadline_exceeded') {
|
|
2085
|
+
await stopAtDeadline();
|
|
2086
|
+
state = deriveState(goal,journal);
|
|
2087
|
+
return withLoopResult(goal,journal,{ status:'paused', reason:'deadline_exceeded', goal_id:goalId, deadline, ...state });
|
|
2088
|
+
}
|
|
2044
2089
|
const loop=loopStateOrNull(goal,journal);
|
|
2045
2090
|
if (!loop || loop.pending_action || !isAuthorityUnavailable(error)) throw error;
|
|
2046
2091
|
outcome=await r34EnterWaiting({ctx,events:journal,goal,signingKeyPath,
|
|
2047
2092
|
reason:'authority_unavailable',resumePhase:loop.phase});
|
|
2048
2093
|
}
|
|
2049
2094
|
cycles += 1; state = deriveState(goal,journal);
|
|
2095
|
+
if (await stopAtDeadline()) {
|
|
2096
|
+
state = deriveState(goal,journal);
|
|
2097
|
+
return withLoopResult(goal,journal,{ status:'paused', reason:'deadline_exceeded', goal_id:goalId, deadline, ...state });
|
|
2098
|
+
}
|
|
2050
2099
|
if (outcome.status === 'paused' || pauseState(state)) return withLoopResult(goal,journal,{ status:'paused', goal_id:goalId, ...state });
|
|
2051
2100
|
if (outcome.status === 'finish_requested') {
|
|
2052
2101
|
const gate = await releaseGateLocked(ctx,goal,journal);
|
|
@@ -2063,6 +2112,10 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
2063
2112
|
}
|
|
2064
2113
|
continue;
|
|
2065
2114
|
}
|
|
2115
|
+
if (await stopAtDeadline()) {
|
|
2116
|
+
state = deriveState(goal,journal);
|
|
2117
|
+
return withLoopResult(goal,journal,{ status:'paused', reason:'deadline_exceeded', goal_id:goalId, deadline, ...state });
|
|
2118
|
+
}
|
|
2066
2119
|
const accepted = await acceptPlannerDecision({ ctx,events:journal,goal,state,policy,codingRuntime,capabilityManifest,
|
|
2067
2120
|
desktopMemoryProvider,memoryProvider,signingKeyPath,plannerResponse:suppliedResponse,plannerProviderRuntime });
|
|
2068
2121
|
suppliedResponse = null;
|
|
@@ -2079,7 +2132,7 @@ export function createAutonomousRuntime({ policy, codingRuntime, nativeDiffRunti
|
|
|
2079
2132
|
if (pauseState(state)) return withLoopResult(goal,journal,{ status:'paused', goal_id:goalId, ...state });
|
|
2080
2133
|
}
|
|
2081
2134
|
state = deriveState(goal,journal);
|
|
2082
|
-
return withLoopResult(goal,journal,{ status:'yielded', goal_id:goalId, cycles, ...state });
|
|
2135
|
+
return withLoopResult(goal,journal,{ status:'yielded', goal_id:goalId, cycles, deadline, ...state });
|
|
2083
2136
|
} finally { await releaseLock(ctx); }
|
|
2084
2137
|
}
|
|
2085
2138
|
|
|
@@ -89,6 +89,7 @@ export const R36_CAPABILITY_ENTRIES=Object.freeze([
|
|
|
89
89
|
|
|
90
90
|
gateway('process.start','process_session_start','process_session','start','DEADBYTE_PROCESS_RUNTIME','process',{mutating:true,receipt_class:'process'}),
|
|
91
91
|
gateway('process.read','process_session_read','process_session','read','DEADBYTE_PROCESS_RUNTIME','process',{receipt_class:'process'}),
|
|
92
|
+
gateway('process.write','process_session_write','process_session','write','DEADBYTE_PROCESS_RUNTIME','process',{mutating:true,receipt_class:'process'}),
|
|
92
93
|
gateway('process.status','process_session_status','process_session','status','DEADBYTE_PROCESS_RUNTIME','process',{receipt_class:'process'}),
|
|
93
94
|
gateway('process.list','process_session_list','process_session','list','DEADBYTE_PROCESS_RUNTIME','process',{receipt_class:'process'}),
|
|
94
95
|
gateway('process.action','process_session_action','process_session','action','DEADBYTE_PROCESS_RUNTIME','process',{mutating:true,receipt_class:'process'}),
|
package/src/coding-mcp-tools.mjs
CHANGED
|
@@ -171,8 +171,8 @@ export function registerCodingTools(server, { codingRuntime, taskRuntime, semant
|
|
|
171
171
|
}, async ({ submission_id, title, steps }) => { try { return result(codingTaskSubmitOutputSchema, await taskRuntime.submit({ submissionId: submission_id, title, steps })); } catch (error) { return fail(codingTaskSubmitOutputSchema, error); } });
|
|
172
172
|
server.registerTool('coding_task_run', {
|
|
173
173
|
description: 'Advance one durable coding task by bounded steps with retry/checkpoint/crash reconciliation.',
|
|
174
|
-
inputSchema: z.strictObject({ task_id: taskId, max_steps: z.number().int().min(1).max(32).default(8) }), outputSchema: codingTaskRunOutputSchema
|
|
175
|
-
}, async ({ task_id, max_steps }) => { try { return result(codingTaskRunOutputSchema, await taskRuntime.run({ taskId: task_id, maxSteps: max_steps })); } catch (error) { return fail(codingTaskRunOutputSchema, error); } });
|
|
174
|
+
inputSchema: z.strictObject({ task_id: taskId, max_steps: z.number().int().min(1).max(32).default(8), timeout_ms: z.number().int().min(100).max(900000).nullable().default(null) }), outputSchema: codingTaskRunOutputSchema
|
|
175
|
+
}, async ({ task_id, max_steps, timeout_ms }) => { try { return result(codingTaskRunOutputSchema, await taskRuntime.run({ taskId: task_id, maxSteps: max_steps, timeoutMs: timeout_ms })); } catch (error) { return fail(codingTaskRunOutputSchema, error); } });
|
|
176
176
|
|
|
177
177
|
server.registerTool('coding_task_status', {
|
|
178
178
|
description: 'Read derived coding-task state from the authenticated event journal.',
|
|
@@ -8,9 +8,19 @@ const hex64 = z.string().regex(HEX64_RE);
|
|
|
8
8
|
const receiptId = z.string().regex(HEX24_RE);
|
|
9
9
|
const taskId = z.string().regex(TASK_ID_RE);
|
|
10
10
|
const stepId = z.string().regex(STEP_ID_RE);
|
|
11
|
+
const streamCoverage = z.strictObject({
|
|
12
|
+
logical_first_seq:z.number().int().min(1), logical_last_seq:z.number().int().min(0),
|
|
13
|
+
retained_first_seq:z.number().int().min(1), retained_last_seq:z.number().int().min(0),
|
|
14
|
+
complete:z.boolean(), truncated:z.boolean(),
|
|
15
|
+
missing_ranges:z.array(z.strictObject({from:z.number().int().min(1),to:z.number().int().min(0)})).max(100000),
|
|
16
|
+
logical_item_count:z.number().int().min(0), retained_item_count:z.number().int().min(0),
|
|
17
|
+
logical_bytes:z.number().int().min(0), retained_bytes:z.number().int().min(0),
|
|
18
|
+
dropped_items:z.number().int().min(0), dropped_bytes:z.number().int().min(0), status:z.enum(['open','closed'])
|
|
19
|
+
});
|
|
11
20
|
const jsonObject = z.record(z.string(), z.json());
|
|
12
21
|
const taskState = z.enum(['pending', 'running', 'paused', 'failed', 'cancelled', 'succeeded']);
|
|
13
22
|
const codingKind = z.enum(['checkpoint', 'write', 'replace', 'command', 'benchmark']);
|
|
23
|
+
const deadline = z.strictObject({ schema:z.literal('deadbyte.deadline.v1'), id:z.string().nullable(), created_at_ms:z.number(), timeout_ms:z.number().int().min(1).nullable(), deadline_ms:z.number().nullable() });
|
|
14
24
|
|
|
15
25
|
function requireFields(value, ctx, fields) {
|
|
16
26
|
for (const field of fields) if (value[field] === undefined) ctx.addIssue({ code: 'custom', path: [field], message: `${field} is required` });
|
|
@@ -87,7 +97,9 @@ export const codingSearchOutputSchema = statusContract({
|
|
|
87
97
|
binary_files_skipped: z.number().int().min(0).optional(), truncated: z.boolean().optional(),
|
|
88
98
|
truncation_reason: z.enum(['tree_limit','scan_byte_limit','max_results']).nullable().optional(),
|
|
89
99
|
query_sha256: hex64.optional(), snapshot_sha256: hex64.optional(), page_sha256: hex64.optional(),
|
|
90
|
-
next_cursor: z.string().max(8192).nullable().optional()
|
|
100
|
+
next_cursor: z.string().max(8192).nullable().optional(),
|
|
101
|
+
stream_id: hex64.optional(), stream_generation: z.number().int().min(1).optional(), stream_next_seq: z.number().int().min(1).optional(),
|
|
102
|
+
stream_coverage: streamCoverage.optional() },
|
|
91
103
|
successStatuses: ['ok'], successRequired: ['root_id','path','pattern','target','mode','case_sensitive','matches','files_scanned','bytes_scanned','binary_files_skipped','truncated','truncation_reason','query_sha256','snapshot_sha256','page_sha256','next_cursor']
|
|
92
104
|
});
|
|
93
105
|
const semanticLocationSchema = z.strictObject({
|
|
@@ -222,7 +234,7 @@ export const codingTaskSubmitOutputSchema = statusContract({
|
|
|
222
234
|
successStatuses: ['accepted', 'existing'], successRequired: ['task_id', 'plan_sha256', 'state']
|
|
223
235
|
});
|
|
224
236
|
export const codingTaskRunOutputSchema = z.strictObject({
|
|
225
|
-
status: z.enum(['terminal', 'paused', 'completed', 'cancelled', 'failed', 'yielded', 'succeeded']), error: z.string().optional(), task_id: taskId.optional(),
|
|
237
|
+
status: z.enum(['terminal', 'paused', 'completed', 'cancelled', 'failed', 'yielded', 'succeeded']), error: z.string().optional(), task_id: taskId.optional(), deadline: deadline.optional(),
|
|
226
238
|
...Object.fromEntries(Object.entries(derivedShape).map(([key, schema]) => [key, schema.optional()]))
|
|
227
239
|
}).superRefine((value, ctx) => {
|
|
228
240
|
if (value.error !== undefined) { if (value.status !== 'failed') ctx.addIssue({ code: 'custom', path: ['error'], message: 'error wrapper requires status=failed' }); return; }
|
|
@@ -231,13 +243,15 @@ export const codingTaskRunOutputSchema = z.strictObject({
|
|
|
231
243
|
export const codingTaskStatusOutputSchema = statusContract({
|
|
232
244
|
statuses: ['ok', 'failed'],
|
|
233
245
|
shape: { task_id: taskId.optional(), plan_sha256: hex64.optional(),
|
|
234
|
-
...Object.fromEntries(Object.entries(derivedShape).map(([key, schema]) => [key, schema.optional()])), journal_verified: z.literal(true).optional()
|
|
246
|
+
...Object.fromEntries(Object.entries(derivedShape).map(([key, schema]) => [key, schema.optional()])), journal_verified: z.literal(true).optional(),
|
|
247
|
+
stream_id: taskId.optional(), stream_generation: z.number().int().min(1).optional(), stream_next_seq: z.number().int().min(1).optional(), stream_coverage: streamCoverage.optional() },
|
|
235
248
|
successStatuses: ['ok'], successRequired: ['task_id', 'plan_sha256', ...Object.keys(derivedShape), 'journal_verified']
|
|
236
249
|
});
|
|
237
250
|
export const codingTaskEventsOutputSchema = statusContract({
|
|
238
251
|
statuses: ['ok', 'failed'],
|
|
239
252
|
shape: { task_id: taskId.optional(), plan_sha256: hex64.optional(), journal_verified: z.literal(true).optional(),
|
|
240
|
-
journal_head_sha256: hex64.optional(), events: z.array(eventSchema).max(200).optional()
|
|
253
|
+
journal_head_sha256: hex64.optional(), events: z.array(eventSchema).max(200).optional(),
|
|
254
|
+
stream_id: taskId.optional(), stream_generation: z.number().int().min(1).optional(), stream_next_seq: z.number().int().min(1).optional(), stream_coverage: streamCoverage.optional() },
|
|
241
255
|
successStatuses: ['ok'], successRequired: ['task_id', 'plan_sha256', 'journal_verified', 'journal_head_sha256', 'events']
|
|
242
256
|
});
|
|
243
257
|
export const codingTaskCancelOutputSchema = statusContract({
|
package/src/coding-plane.mjs
CHANGED
|
@@ -10,6 +10,7 @@ import { isInside, validatePortableRelativePath } from './path-policy.mjs';
|
|
|
10
10
|
import { loadPrivateSigningKey, loadPublicVerifyKey } from './trust.mjs';
|
|
11
11
|
import { createCodingSearch } from './coding-search.mjs';
|
|
12
12
|
import { createSearchCursorCodec } from './search-cursor.mjs';
|
|
13
|
+
import { atomicMutation } from './atomic-mutation.mjs';
|
|
13
14
|
|
|
14
15
|
export const CODING_POLICY_SCHEMA = 'deadbyte.coding-policy.v1';
|
|
15
16
|
export const CODING_LEASE_SCHEMA = 'deadbyte.coding-lease.v1';
|
|
@@ -261,30 +262,8 @@ function decodeContent(content, encoding) {
|
|
|
261
262
|
throw new Error('encoding must be utf8 or base64');
|
|
262
263
|
}
|
|
263
264
|
async function atomicWrite(target, bytes, expectedSha256, authorityCheck = null) {
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
const temp = path.join(path.dirname(target), `.${path.basename(target)}.deadbyte-coding-${process.pid}-${randomBytes(8).toString('hex')}.tmp`);
|
|
267
|
-
let handle;
|
|
268
|
-
try {
|
|
269
|
-
if (authorityCheck) await authorityCheck();
|
|
270
|
-
handle = await open(temp, fsConstants.O_CREAT | fsConstants.O_EXCL | fsConstants.O_WRONLY, 0o600);
|
|
271
|
-
await handle.writeFile(bytes);
|
|
272
|
-
await handle.sync();
|
|
273
|
-
await handle.close();
|
|
274
|
-
handle = null;
|
|
275
|
-
const race = await targetState(target);
|
|
276
|
-
checkExpected(race, expectedSha256);
|
|
277
|
-
if (authorityCheck) await authorityCheck();
|
|
278
|
-
await rename(temp, target);
|
|
279
|
-
} catch (error) {
|
|
280
|
-
if (handle) await handle.close().catch(() => {});
|
|
281
|
-
await rm(temp, { force: true }).catch(() => {});
|
|
282
|
-
throw error;
|
|
283
|
-
}
|
|
284
|
-
const after = await targetState(target);
|
|
285
|
-
const expectedAfter = sha256Bytes(bytes);
|
|
286
|
-
if (!after.exists || after.sha256 !== expectedAfter) throw new Error('coding write post-commit hash mismatch');
|
|
287
|
-
return { before, after };
|
|
265
|
+
return atomicMutation({ target, bytes, expected: expectedSha256, readState: targetState,
|
|
266
|
+
assertExpected: checkExpected, authorityCheck, tempTag: 'deadbyte-coding' });
|
|
288
267
|
}
|
|
289
268
|
function codingEnvironment() {
|
|
290
269
|
const allowed = new Set([
|
package/src/coding-search.mjs
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'node:crypto';
|
|
2
2
|
import { canonicalJson } from './canonical-json.mjs';
|
|
3
3
|
import { decodeUtf8Strict } from './text-policy.mjs';
|
|
4
|
+
import { createSequenceStream } from './workload-substrate.mjs';
|
|
4
5
|
|
|
5
6
|
const sha256Object=value=>createHash('sha256').update(canonicalJson(value),'utf8').digest('hex');
|
|
6
7
|
const sha256Bytes=bytes=>createHash('sha256').update(bytes).digest('hex');
|
|
@@ -121,18 +122,26 @@ export function createCodingSearch({tree,fileRead,cursorCodec}){
|
|
|
121
122
|
|
|
122
123
|
const snapshotSha=sha256Object({schema:'deadbyte.coding-search-snapshot.v2',query_sha256:querySha,entries:snapshotEntries});
|
|
123
124
|
if(decodedCursor&&decodedCursor.snapshot_sha256!==snapshotSha) throw new Error('coding search cursor stale snapshot mismatch');
|
|
125
|
+
const stream=createSequenceStream({streamId:querySha,maxItems:Math.max(1,maxResults),maxBytes:Math.max(1024, maxResults*4096)});
|
|
126
|
+
for(const match of matches) stream.append(match,{producer:'coding-search',channel:'match'});
|
|
124
127
|
const offset=decodedCursor?.offset??0;
|
|
125
128
|
if(offset>matches.length) throw new Error('coding search cursor offset exceeds retained results');
|
|
126
|
-
const
|
|
129
|
+
const page=stream.read(offset+1,pageSize);
|
|
130
|
+
if(page.missing) throw new Error('coding search cursor offset is outside retained stream coverage');
|
|
131
|
+
const pageMatches=page.items.map(item=>item.value);
|
|
127
132
|
const nextOffset=offset+pageMatches.length;
|
|
128
133
|
const hasMore=nextOffset<matches.length;
|
|
129
134
|
const nextCursor=hasMore?await cursorCodec.encode({query_sha256:querySha,snapshot_sha256:snapshotSha,offset:nextOffset}):null;
|
|
130
135
|
const pageSha=sha256Object({schema:'deadbyte.coding-search-page.v2',query_sha256:querySha,snapshot_sha256:snapshotSha,offset,matches:pageMatches});
|
|
136
|
+
const coverage=stream.coverage();
|
|
137
|
+
const streamSnapshot=stream.snapshot();
|
|
131
138
|
return {
|
|
132
139
|
status:'ok',root_id:rootId,path,pattern,target,mode,case_sensitive:caseSensitive,
|
|
133
140
|
matches:pageMatches,files_scanned:filesScanned,bytes_scanned:bytesScanned,binary_files_skipped:binaryFilesSkipped,
|
|
134
141
|
truncated:truncated||hasMore,truncation_reason:truncationReason,query_sha256:querySha,snapshot_sha256:snapshotSha,
|
|
135
|
-
page_sha256:pageSha,next_cursor:nextCursor
|
|
142
|
+
page_sha256:pageSha,next_cursor:nextCursor,
|
|
143
|
+
stream_id:stream.streamId,stream_generation:stream.generation,stream_next_seq:streamSnapshot.next_seq,
|
|
144
|
+
stream_coverage:coverage
|
|
136
145
|
};
|
|
137
146
|
};
|
|
138
147
|
}
|
|
@@ -4,6 +4,8 @@ import path from 'node:path';
|
|
|
4
4
|
import { canonicalJson } from './canonical-json.mjs';
|
|
5
5
|
import { validatePortableRelativePath } from './path-policy.mjs';
|
|
6
6
|
import { loadPrivateSigningKey, loadPublicVerifyKey } from './trust.mjs';
|
|
7
|
+
import { createJournalStream } from './workload-substrate.mjs';
|
|
8
|
+
import { createDeadline, deadlineState } from './deadline-cancellation.mjs';
|
|
7
9
|
|
|
8
10
|
export const CODING_TASK_PLAN_SCHEMA = 'deadbyte.coding-task-plan.v1';
|
|
9
11
|
export const CODING_TASK_EVENT_SCHEMA = 'deadbyte.coding-task-event.v1';
|
|
@@ -525,7 +527,11 @@ export function createCodingTaskRuntime({ codingRuntime, signingKeyPath, verifyK
|
|
|
525
527
|
const ctx = taskContext(policy, taskId);
|
|
526
528
|
const plan = await loadPlan(ctx, policy, verifyKeyPath);
|
|
527
529
|
const events = await readEvents(ctx, verifyKeyPath);
|
|
528
|
-
|
|
530
|
+
const stream = createJournalStream({streamId:taskId,events,maxItems:MAX_EVENTS,maxBytes:8*1024*1024,producer:'coding-task',channel:'event'});
|
|
531
|
+
const snapshot = stream.snapshot();
|
|
532
|
+
return { status: 'ok', task_id: taskId, plan_sha256: plan.plan_sha256, ...deriveState(plan, events), journal_verified: true,
|
|
533
|
+
stream_id:snapshot.stream_id, stream_generation:snapshot.generation, stream_next_seq:snapshot.next_seq,
|
|
534
|
+
stream_coverage:stream.coverage() };
|
|
529
535
|
}
|
|
530
536
|
|
|
531
537
|
async function events({ taskId, fromSeq = 1, limit = 100 }) {
|
|
@@ -534,10 +540,15 @@ export function createCodingTaskRuntime({ codingRuntime, signingKeyPath, verifyK
|
|
|
534
540
|
const ctx = taskContext(policy, taskId);
|
|
535
541
|
const plan = await loadPlan(ctx, policy, verifyKeyPath);
|
|
536
542
|
const all = await readEvents(ctx, verifyKeyPath);
|
|
543
|
+
const stream = createJournalStream({streamId:taskId,events:all,maxItems:MAX_EVENTS,maxBytes:8*1024*1024,producer:'coding-task',channel:'event'});
|
|
544
|
+
const page = stream.read(fromSeq,limit);
|
|
545
|
+
const snapshot = stream.snapshot();
|
|
537
546
|
return {
|
|
538
547
|
status: 'ok', task_id: taskId, plan_sha256: plan.plan_sha256, journal_verified: true,
|
|
539
548
|
journal_head_sha256: all.at(-1)?.event_sha256 ?? ZERO_HASH,
|
|
540
|
-
|
|
549
|
+
stream_id: snapshot.stream_id, stream_generation: snapshot.generation, stream_next_seq: snapshot.next_seq,
|
|
550
|
+
stream_coverage: stream.coverage(),
|
|
551
|
+
events: page.items.map(item=>item.value)
|
|
541
552
|
};
|
|
542
553
|
}
|
|
543
554
|
|
|
@@ -590,8 +601,18 @@ export function createCodingTaskRuntime({ codingRuntime, signingKeyPath, verifyK
|
|
|
590
601
|
const journal = await readEvents(ctx, verifyKeyPath);
|
|
591
602
|
return verifyTaskReceipt(ctx, plan, journal, policy, verifyKeyPath, codingRuntime);
|
|
592
603
|
}
|
|
593
|
-
async function run({ taskId, maxSteps = 8 }) {
|
|
604
|
+
async function run({ taskId, maxSteps = 8, timeoutMs = null }) {
|
|
594
605
|
if (!Number.isInteger(maxSteps) || maxSteps < 1 || maxSteps > MAX_STEPS) throw new Error(`max_steps must be in [1, ${MAX_STEPS}]`);
|
|
606
|
+
if (timeoutMs !== null && (!Number.isInteger(timeoutMs) || timeoutMs < 100 || timeoutMs > 900000)) throw new Error('timeout_ms must be null or in [100,900000]');
|
|
607
|
+
const deadline = createDeadline({ timeoutMs });
|
|
608
|
+
const deadlineExpired = () => timeoutMs !== null && deadlineState(deadline).expired;
|
|
609
|
+
const failDeadline = async (ctx, journal, plan, stepId = null) => {
|
|
610
|
+
await appendEvent(ctx, journal, signingKeyPath, 'task_failed', {
|
|
611
|
+
stepId,
|
|
612
|
+
payload: { reason: 'deadline_exceeded', deadline }
|
|
613
|
+
});
|
|
614
|
+
return deriveState(plan, journal);
|
|
615
|
+
};
|
|
595
616
|
const session = await codingRuntime.sessionStatus();
|
|
596
617
|
if (!session.armed) throw new Error(`coding plane is disarmed: ${session.reason}`);
|
|
597
618
|
const ctx = taskContext(policy, taskId);
|
|
@@ -610,10 +631,14 @@ export function createCodingTaskRuntime({ codingRuntime, signingKeyPath, verifyK
|
|
|
610
631
|
if (await consumeCancel(ctx, journal, signingKeyPath, verifyKeyPath)) {
|
|
611
632
|
derived = deriveState(plan, journal); return { status: 'cancelled', task_id: taskId, ...derived };
|
|
612
633
|
}
|
|
613
|
-
await appendEvent(ctx, journal, signingKeyPath, 'run_started', { payload: { max_steps: maxSteps } });
|
|
634
|
+
await appendEvent(ctx, journal, signingKeyPath, 'run_started', { payload: { max_steps: maxSteps, deadline } });
|
|
614
635
|
derived = deriveState(plan, journal);
|
|
615
636
|
let consumed = 0;
|
|
616
637
|
while (consumed < maxSteps) {
|
|
638
|
+
if (deadlineExpired()) {
|
|
639
|
+
derived = await failDeadline(ctx, journal, plan);
|
|
640
|
+
return { status: 'failed', task_id: taskId, ...derived, deadline };
|
|
641
|
+
}
|
|
617
642
|
if (await consumeCancel(ctx, journal, signingKeyPath, verifyKeyPath)) {
|
|
618
643
|
derived = deriveState(plan, journal); return { status: 'cancelled', task_id: taskId, ...derived };
|
|
619
644
|
}
|
|
@@ -638,7 +663,9 @@ export function createCodingTaskRuntime({ codingRuntime, signingKeyPath, verifyK
|
|
|
638
663
|
derived = deriveState(plan, journal);
|
|
639
664
|
consumed += 1;
|
|
640
665
|
try {
|
|
641
|
-
const
|
|
666
|
+
const remainingMs = timeoutMs === null ? null : Math.max(100, Math.floor(deadlineState(deadline).remaining_ms));
|
|
667
|
+
const boundedStep = remainingMs === null ? next : { ...next, timeout_ms: next.timeout_ms === null || next.timeout_ms === undefined ? remainingMs : Math.min(next.timeout_ms, remainingMs) };
|
|
668
|
+
const executed = await executeStep(codingRuntime, plan, boundedStep, attempt);
|
|
642
669
|
if (!executed.outcome.valid) {
|
|
643
670
|
await appendEvent(ctx, journal, signingKeyPath, 'task_paused', {
|
|
644
671
|
stepId: next.step_id, payload: { reason: 'post_execute_receipt_mismatch', detail: executed.outcome.reason }
|
|
@@ -646,6 +673,10 @@ export function createCodingTaskRuntime({ codingRuntime, signingKeyPath, verifyK
|
|
|
646
673
|
derived = deriveState(plan, journal);
|
|
647
674
|
return { status: 'paused', task_id: taskId, ...derived };
|
|
648
675
|
}
|
|
676
|
+
if (deadlineExpired()) {
|
|
677
|
+
derived = await failDeadline(ctx, journal, plan, next.step_id);
|
|
678
|
+
return { status: 'failed', task_id: taskId, ...derived, deadline };
|
|
679
|
+
}
|
|
649
680
|
if (executed.outcome.success) {
|
|
650
681
|
await appendEvent(ctx, journal, signingKeyPath, 'step_succeeded', {
|
|
651
682
|
stepId: next.step_id, payload: { ...executed.outcome.payload, attempt, recovered: false }
|
|
@@ -664,6 +695,10 @@ export function createCodingTaskRuntime({ codingRuntime, signingKeyPath, verifyK
|
|
|
664
695
|
}
|
|
665
696
|
derived = deriveState(plan, journal);
|
|
666
697
|
} catch (error) {
|
|
698
|
+
if (deadlineExpired()) {
|
|
699
|
+
derived = await failDeadline(ctx, journal, plan, next.step_id);
|
|
700
|
+
return { status: 'failed', task_id: taskId, ...derived, deadline };
|
|
701
|
+
}
|
|
667
702
|
const checked = await codingRuntime.verifyReceiptDetailed(receiptId);
|
|
668
703
|
if (checked.ok) {
|
|
669
704
|
const outcome = receiptOutcome(next, checked, requestSha256);
|
|
@@ -717,7 +752,7 @@ export function createCodingTaskRuntime({ codingRuntime, signingKeyPath, verifyK
|
|
|
717
752
|
}
|
|
718
753
|
}
|
|
719
754
|
}
|
|
720
|
-
return { status: 'yielded', task_id: taskId, ...derived };
|
|
755
|
+
return { status: 'yielded', task_id: taskId, ...derived, deadline };
|
|
721
756
|
} finally { await releaseLock(ctx); }
|
|
722
757
|
}
|
|
723
758
|
|