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.
Files changed (96) hide show
  1. package/MANIFEST.SHA256 +95 -64
  2. package/PROVENANCE.json +6 -5
  3. package/README.txt +36 -20
  4. package/RELEASE.json +24 -8
  5. package/SBOM.cdx.json +3 -3
  6. package/controller/README.TXT +2 -2
  7. package/controller/deadbyte-controller.ps1 +50 -2
  8. package/controller/deadbyte-process-policy.json +63 -1
  9. package/docs/ARCHITECTURE.md +3 -4
  10. package/package.json +88 -86
  11. package/scripts/autonomous-build.ps1 +1 -0
  12. package/scripts/autonomous-tests.ps1 +1 -0
  13. package/scripts/check-agent-backend-r46.mjs +77 -0
  14. package/scripts/check-atomic-resource-r46.mjs +84 -0
  15. package/scripts/check-autonomous-race-r46.mjs +160 -0
  16. package/scripts/check-coding-task-deadline-r46.mjs +59 -0
  17. package/scripts/check-deadline-cancellation-r46.mjs +49 -0
  18. package/scripts/check-process-deadline-r46.mjs +52 -0
  19. package/scripts/check-r46-background.mjs +5 -0
  20. package/scripts/deferred-slot-operation-r46.mjs +126 -0
  21. package/scripts/extract-r46-failures.mjs +5 -0
  22. package/scripts/final-closure-r46.mjs +98 -0
  23. package/scripts/final-closure-verify-r46.mjs +119 -0
  24. package/scripts/full-regression-r46.mjs +28 -0
  25. package/scripts/gate-windows-r46.ps1 +58 -0
  26. package/scripts/inspect-r46-active.mjs +2 -0
  27. package/scripts/r47-core-failures.mjs +1 -0
  28. package/scripts/refresh-process-policy.mjs +64 -7
  29. package/scripts/release-manifest.mjs +1 -1
  30. package/scripts/release-parity-r46.mjs +187 -0
  31. package/scripts/run-r46-full-regression-background.mjs +7 -0
  32. package/scripts/run-r46-windows-gate-background.mjs +6 -0
  33. package/scripts/run-r46-windows-gate-background.ps1 +10 -0
  34. package/scripts/windows-gate-evidence-r46.mjs +111 -0
  35. package/scripts/workload-contract-regression-r47.mjs +14 -0
  36. package/scripts/workload-stream-regression.mjs +16 -0
  37. package/scripts/workload-substrate-r47-regression.mjs +12 -0
  38. package/src/agent-backend-r44.mjs +75 -10
  39. package/src/atomic-mutation.mjs +55 -0
  40. package/src/autonomous-mcp-tools.mjs +4 -3
  41. package/src/autonomous-output-contracts.mjs +16 -3
  42. package/src/autonomous-runtime.mjs +83 -30
  43. package/src/capability-ledger.mjs +1 -0
  44. package/src/coding-mcp-tools.mjs +2 -2
  45. package/src/coding-output-contracts.mjs +18 -4
  46. package/src/coding-plane.mjs +3 -24
  47. package/src/coding-search.mjs +11 -2
  48. package/src/coding-task-runtime.mjs +41 -6
  49. package/src/compact-gateway-mcp-tools.mjs +7 -5
  50. package/src/deadbyte-cli.mjs +7 -3
  51. package/src/deadline-cancellation.mjs +83 -0
  52. package/src/machine-fs-mcp-tools.mjs +3 -3
  53. package/src/machine-fs-output-contracts.mjs +1 -1
  54. package/src/machine-fs-read-worker.mjs +46 -0
  55. package/src/machine-fs-smoke-client.mjs +1 -1
  56. package/src/machine-fs.mjs +66 -33
  57. package/src/mcp-server.mjs +10 -0
  58. package/src/mcp-smoke-client.mjs +4 -1
  59. package/src/observation-runtime.mjs +25 -4
  60. package/src/observation-smoke-client.mjs +1 -1
  61. package/src/process-mcp-tools.mjs +29 -8
  62. package/src/process-output-contracts.mjs +43 -3
  63. package/src/process-policy.mjs +3 -1
  64. package/src/process-runtime.mjs +152 -23
  65. package/src/process-smoke-client.mjs +67 -22
  66. package/src/release-generation.mjs +22 -3
  67. package/src/remote-device-r44.mjs +53 -15
  68. package/src/remote-mcp-smoke-client.mjs +1 -1
  69. package/src/resource-identity.mjs +59 -0
  70. package/src/runtime-update.mjs +526 -162
  71. package/src/stdio-console-guard.mjs +24 -0
  72. package/src/version.mjs +1 -1
  73. package/src/workload-substrate.mjs +87 -0
  74. package/test/cli-entrypoint.test.mjs +3 -3
  75. package/test/coding-runtime.test.mjs +12 -0
  76. package/test/coding-search-r36.test.mjs +7 -0
  77. package/test/compact-gateway-r36.test.mjs +28 -0
  78. package/test/core.test.mjs +1 -1
  79. package/test/full-max-catalog-r46.test.mjs +92 -0
  80. package/test/helpers/autonomous-r34-fixture.mjs +10 -1
  81. package/test/machine-fs-mcp-tools.test.mjs +4 -1
  82. package/test/machine-fs-runtime.test.mjs +18 -2
  83. package/test/observation-runtime.test.mjs +29 -2
  84. package/test/process-mcp-tools.test.mjs +27 -6
  85. package/test/process-policy-refresh.test.mjs +87 -10
  86. package/test/process-runtime.test.mjs +129 -2
  87. package/test/process-supervision.test.mjs +4 -2
  88. package/test/production-docs.test.mjs +3 -3
  89. package/test/r33-finalization.test.mjs +3 -3
  90. package/test/r45-finalization.test.mjs +1 -1
  91. package/test/release-generation.test.mjs +25 -11
  92. package/test/release-version.test.mjs +99 -44
  93. package/test/remote-device-r44.test.mjs +75 -0
  94. package/test/remote-r24-catalog.test.mjs +2 -2
  95. package/test/runtime-self-update.test.mjs +118 -1
  96. 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:profile.timeout_ms,
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:profile.timeout_ms,
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
- ...(loop ? {loop} : {}) };
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
- events:journal.slice(fromSeq-1,fromSeq-1+limit) };
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
- await appendEvent(ctx,journal,signingKeyPath,'goal_run_started',{ max_cycles:maxCycles });
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'}),
@@ -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({
@@ -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
- const before = await targetState(target);
265
- checkExpected(before, expectedSha256);
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([
@@ -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 pageMatches=matches.slice(offset,offset+pageSize);
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
- return { status: 'ok', task_id: taskId, plan_sha256: plan.plan_sha256, ...deriveState(plan, events), journal_verified: true };
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
- events: all.slice(fromSeq - 1, fromSeq - 1 + limit)
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 executed = await executeStep(codingRuntime, plan, next, attempt);
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