deadbyte-mcp 0.15.2 → 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 (56) hide show
  1. package/MANIFEST.SHA256 +54 -33
  2. package/package.json +88 -88
  3. package/scripts/autonomous-build.ps1 +1 -0
  4. package/scripts/autonomous-tests.ps1 +1 -0
  5. package/scripts/check-agent-backend-r46.mjs +77 -0
  6. package/scripts/check-atomic-resource-r46.mjs +84 -0
  7. package/scripts/check-autonomous-race-r46.mjs +160 -0
  8. package/scripts/check-coding-task-deadline-r46.mjs +59 -0
  9. package/scripts/check-deadline-cancellation-r46.mjs +49 -0
  10. package/scripts/check-process-deadline-r46.mjs +52 -0
  11. package/scripts/check-r46-background.mjs +5 -0
  12. package/scripts/extract-r46-failures.mjs +5 -0
  13. package/scripts/full-regression-r46.mjs +20 -12
  14. package/scripts/gate-windows-r46.ps1 +2 -0
  15. package/scripts/inspect-r46-active.mjs +2 -0
  16. package/scripts/r47-core-failures.mjs +1 -0
  17. package/scripts/release-manifest.mjs +1 -1
  18. package/scripts/run-r46-full-regression-background.mjs +7 -0
  19. package/scripts/run-r46-windows-gate-background.mjs +6 -0
  20. package/scripts/run-r46-windows-gate-background.ps1 +10 -0
  21. package/scripts/windows-gate-evidence-r46.mjs +4 -3
  22. package/scripts/workload-contract-regression-r47.mjs +14 -0
  23. package/scripts/workload-stream-regression.mjs +16 -0
  24. package/scripts/workload-substrate-r47-regression.mjs +12 -0
  25. package/src/agent-backend-r44.mjs +75 -10
  26. package/src/atomic-mutation.mjs +55 -0
  27. package/src/autonomous-mcp-tools.mjs +4 -3
  28. package/src/autonomous-output-contracts.mjs +16 -3
  29. package/src/autonomous-runtime.mjs +83 -30
  30. package/src/coding-mcp-tools.mjs +2 -2
  31. package/src/coding-output-contracts.mjs +18 -4
  32. package/src/coding-plane.mjs +3 -24
  33. package/src/coding-search.mjs +11 -2
  34. package/src/coding-task-runtime.mjs +41 -6
  35. package/src/deadline-cancellation.mjs +83 -0
  36. package/src/machine-fs-smoke-client.mjs +1 -1
  37. package/src/machine-fs.mjs +3 -23
  38. package/src/mcp-server.mjs +6 -0
  39. package/src/observation-smoke-client.mjs +1 -1
  40. package/src/process-mcp-tools.mjs +11 -3
  41. package/src/process-output-contracts.mjs +23 -0
  42. package/src/process-runtime.mjs +54 -5
  43. package/src/process-smoke-client.mjs +1 -1
  44. package/src/resource-identity.mjs +59 -0
  45. package/src/runtime-update.mjs +526 -162
  46. package/src/workload-substrate.mjs +87 -0
  47. package/test/coding-runtime.test.mjs +12 -0
  48. package/test/coding-search-r36.test.mjs +7 -0
  49. package/test/full-max-catalog-r46.test.mjs +2 -2
  50. package/test/helpers/autonomous-r34-fixture.mjs +10 -1
  51. package/test/process-mcp-tools.test.mjs +5 -2
  52. package/test/process-runtime.test.mjs +18 -1
  53. package/test/r45-finalization.test.mjs +1 -1
  54. package/test/release-version.test.mjs +1 -1
  55. package/test/runtime-self-update.test.mjs +118 -1
  56. package/test/workload-substrate.test.mjs +22 -0
@@ -3,6 +3,7 @@ import { EventEmitter } from 'node:events';
3
3
  import { lstat, mkdir, open, readFile, realpath, rename } from 'node:fs/promises';
4
4
  import path from 'node:path';
5
5
  import { spawn } from 'node:child_process';
6
+ import { classifyTermination, composeAbortSignal, createCancellationToken, createDeadline } from './deadline-cancellation.mjs';
6
7
 
7
8
  const STATE_SCHEMA='deadbyte.agent-backend-state.v1';
8
9
  const HEX64=/^[0-9a-f]{64}$/;
@@ -46,6 +47,16 @@ function validateState(state,backendId){
46
47
  insist(typeof item.method==='string'&&METHOD.test(item.method),'agent backend request method invalid');
47
48
  insist(HEX64.test(item.params_sha256??''),'agent backend params hash invalid');
48
49
  insist(typeof item.created_at_utc==='string'&&typeof item.updated_at_utc==='string','agent backend request timestamp invalid');
50
+ if(item.deadline!==undefined){
51
+ insist(item.deadline&&item.deadline.schema==='deadbyte.deadline.v1','agent backend request deadline invalid');
52
+ insist(item.deadline.timeout_ms===null||Number.isInteger(item.deadline.timeout_ms),'agent backend request deadline timeout invalid');
53
+ insist(item.deadline.deadline_ms===null||Number.isFinite(item.deadline.deadline_ms),'agent backend request deadline timestamp invalid');
54
+ }
55
+ if(item.termination!==undefined){
56
+ insist(item.termination&&typeof item.termination==='object'&&!Array.isArray(item.termination),'agent backend request termination invalid');
57
+ for(const key of ['deadline_exceeded','cancellation_requested','physical_termination','verified'])insist(typeof item.termination[key]==='boolean',`agent backend request termination ${key} invalid`);
58
+ insist(typeof item.termination.terminal_semantics==='string','agent backend request termination semantics invalid');
59
+ }
49
60
  }
50
61
  insist(typeof state.updated_at_utc==='string','agent backend state timestamp invalid');
51
62
  return state;
@@ -157,11 +168,12 @@ export async function createAgentBackendR44({
157
168
  threads:structuredClone(state.threads),requests:structuredClone(state.requests)
158
169
  });
159
170
 
160
- async function terminal(id,{stateName,result=null,error=null}){
171
+ async function terminal(id,{stateName,result=null,error=null,termination=null}){
161
172
  const record=state.requests[id];if(!record||record.state!=='pending')return;
162
173
  record.state=stateName;record.updated_at_utc=now();
163
174
  if(result!==null)record.result=result;
164
175
  if(error!==null)record.error=String(error);
176
+ if(termination!==null)record.termination=termination;
165
177
  await persist();
166
178
  const waiter=pending.get(id);pending.delete(id);
167
179
  if(!waiter)return;
@@ -198,11 +210,29 @@ export async function createAgentBackendR44({
198
210
  for(const id of pending.keys())void terminal(id,{stateName:'failed',error:String(error?.message??error)});
199
211
  });
200
212
  transport.on('transport_error',error=>{
201
- for(const id of pending.keys())void terminal(id,{stateName:'interrupted',error:String(error?.message??error)});
213
+ for(const id of pending.keys()){
214
+ const record=state.requests[id];
215
+ const termination=classifyTermination({
216
+ deadlineExceeded:record?.termination?.deadline_exceeded===true,
217
+ cancellationRequested:record?.termination?.cancellation_requested===true,
218
+ physicallyTerminated:false,
219
+ verified:false
220
+ });
221
+ void terminal(id,{stateName:'interrupted',error:String(error?.message??error),termination});
222
+ }
202
223
  started=false;
203
224
  });
204
225
  transport.on('exit',()=>{
205
- for(const id of pending.keys())void terminal(id,{stateName:'interrupted',error:'agent backend transport exited'});
226
+ for(const id of pending.keys()){
227
+ const record=state.requests[id];
228
+ const termination=classifyTermination({
229
+ deadlineExceeded:record?.termination?.deadline_exceeded===true,
230
+ cancellationRequested:record?.termination?.cancellation_requested===true,
231
+ physicallyTerminated:true,
232
+ verified:true
233
+ });
234
+ void terminal(id,{stateName:'interrupted',error:'agent backend transport exited',termination});
235
+ }
206
236
  started=false;
207
237
  });
208
238
  await transport.start();started=true;return publicState();
@@ -223,17 +253,52 @@ export async function createAgentBackendR44({
223
253
  const sequence=++state.next_sequence;
224
254
  const paramsSha=sha(Buffer.from(canonicalJson(params),'utf8'));
225
255
  const id=sha(Buffer.from(canonicalJson({backend_id:backendId,state_epoch:state.state_epoch,sequence,method,params_sha256:paramsSha}),'utf8'));
226
- state.requests[id]={state:'pending',method,params_sha256:paramsSha,created_at_utc:now(),updated_at_utc:now()};
256
+ const deadline=createDeadline({timeoutMs,id});
257
+ const cancellation=createCancellationToken({id});
258
+ const composed=composeAbortSignal({deadline,cancellation});
259
+ state.requests[id]={
260
+ state:'pending',method,params_sha256:paramsSha,created_at_utc:now(),updated_at_utc:now(),
261
+ deadline,
262
+ termination:classifyTermination()
263
+ };
227
264
  await persist();
228
- let timer;
229
265
  const promise=new Promise((resolve,reject)=>{
230
- pending.set(id,{resolve,reject});
231
- timer=setTimeout(()=>{void terminal(id,{stateName:'failed',error:'agent backend request timeout'});},timeoutMs);
232
- }).finally(()=>clearTimeout(timer));
266
+ pending.set(id,{resolve,reject,cancellation,composed});
267
+ composed.signal.addEventListener('abort',()=>{
268
+ const reason=String(composed.signal.reason??'cancelled');
269
+ const deadlineExceeded=reason==='deadline_exceeded';
270
+ const cancellationRequested=cancellation.snapshot().requested;
271
+ const termination=classifyTermination({deadlineExceeded,cancellationRequested,physicallyTerminated:false,verified:false});
272
+ void terminal(id,{stateName:'failed',error:deadlineExceeded?'agent backend request deadline exceeded':'agent backend request cancelled',termination});
273
+ },{once:true});
274
+ }).finally(()=>{
275
+ composed.close();
276
+ const waiter=pending.get(id);
277
+ if(waiter?.composed===composed)pending.delete(id);
278
+ });
233
279
  try{await transport.send(wire({id,method,params}));}
234
- catch(error){await terminal(id,{stateName:'failed',error:String(error?.message??error)});}
280
+ catch(error){
281
+ const record=state.requests[id];
282
+ const termination=classifyTermination({
283
+ deadlineExceeded:record?.termination?.deadline_exceeded===true,
284
+ cancellationRequested:record?.termination?.cancellation_requested===true,
285
+ physicallyTerminated:false,
286
+ verified:false
287
+ });
288
+ await terminal(id,{stateName:'failed',error:String(error?.message??error),termination});
289
+ }
235
290
  return promise;
236
291
  }
292
+ async function cancelRequest(id,{reason='operator_request'}={}){
293
+ insist(HEX64.test(id),'agent backend request id invalid');
294
+ const record=state.requests[id];
295
+ if(!record)return {status:'missing',request_id:id};
296
+ if(record.state!=='pending')return {status:'terminal',request_id:id,state:record.state,termination:record.termination??null};
297
+ const waiter=pending.get(id);
298
+ if(!waiter?.cancellation)return {status:'pending',request_id:id,termination:record.termination??null};
299
+ waiter.cancellation.request(reason);
300
+ return {status:'requested',request_id:id};
301
+ }
237
302
  async function notify({method,params={}}={}){
238
303
  insist(started&&transport,'agent backend is not started');
239
304
  insist(typeof method==='string'&&METHOD.test(method),'agent backend notification method invalid');
@@ -250,5 +315,5 @@ export async function createAgentBackendR44({
250
315
  for(const [id,waiter] of pending){waiter.reject(new Error('simulated backend crash'));pending.delete(id);}
251
316
  if(transport?.stop)await transport.stop();transport=null;started=false;
252
317
  }
253
- return Object.freeze({start,bindThread,request,notify,status,close,simulateCrashForTest,state_file:stateFile,wire_mode:wireMode});
318
+ return Object.freeze({start,bindThread,request,cancelRequest,notify,status,close,simulateCrashForTest,state_file:stateFile,wire_mode:wireMode});
254
319
  }
@@ -0,0 +1,55 @@
1
+ import { createHash, randomBytes } from 'node:crypto';
2
+ import { open, rename, rm } from 'node:fs/promises';
3
+ import path from 'node:path';
4
+
5
+ const sha256Bytes = bytes => createHash('sha256').update(bytes).digest('hex');
6
+
7
+ export async function atomicMutation({
8
+ target,
9
+ bytes,
10
+ expected,
11
+ readState,
12
+ assertExpected,
13
+ authorityCheck = null,
14
+ tempTag = 'deadbyte-atomic',
15
+ verifyPostcondition = null
16
+ }) {
17
+ if (typeof target !== 'string' || target.length === 0) throw new Error('atomic mutation target is required');
18
+ if (!Buffer.isBuffer(bytes)) throw new Error('atomic mutation bytes must be a Buffer');
19
+ if (typeof readState !== 'function' || typeof assertExpected !== 'function') {
20
+ throw new Error('atomic mutation requires readState and assertExpected');
21
+ }
22
+ const before = await readState(target);
23
+ assertExpected(before, expected);
24
+ const temp = path.join(
25
+ path.dirname(target),
26
+ `.\${path.basename(target)}.\${tempTag}-\${process.pid}-\${randomBytes(8).toString('hex')}.tmp`
27
+ );
28
+ let handle;
29
+ try {
30
+ if (authorityCheck) await authorityCheck();
31
+ handle = await open(temp, 'wx', 0o600);
32
+ await handle.writeFile(bytes);
33
+ await handle.sync();
34
+ await handle.close();
35
+ handle = null;
36
+
37
+ const race = await readState(target);
38
+ assertExpected(race, expected);
39
+ if (authorityCheck) await authorityCheck();
40
+
41
+ await rename(temp, target);
42
+ } catch (error) {
43
+ if (handle) await handle.close().catch(() => {});
44
+ await rm(temp, { force: true }).catch(() => {});
45
+ throw error;
46
+ }
47
+
48
+ const after = await readState(target);
49
+ const expectedAfter = sha256Bytes(bytes);
50
+ if (!after.exists || after.sha256 !== expectedAfter) {
51
+ throw new Error('atomic mutation post-commit hash mismatch');
52
+ }
53
+ if (verifyPostcondition) await verifyPostcondition({ before, after, expected_after_sha256: expectedAfter });
54
+ return { before, after, expected_after_sha256: expectedAfter };
55
+ }
@@ -72,11 +72,12 @@ export function registerAutonomousTools(server,{ autonomousRuntime, nativeDiffRu
72
72
  inputSchema:z.strictObject({ submission_id:id, root_id:id, title:z.string().max(160).default(''),
73
73
  goal:z.string().min(1).max(8192), acceptance:z.string().max(4096).default(''),
74
74
  task_contract:taskContract.nullable().default(null),
75
- max_iterations:z.number().int().min(1).max(64).nullable().default(null) }),
75
+ max_iterations:z.number().int().min(1).max(64).nullable().default(null),
76
+ timeout_ms:z.number().int().min(100).max(900000).nullable().default(null) }),
76
77
  outputSchema:autonomousGoalSubmitOutputSchema
77
- },async({submission_id,root_id,title,goal,acceptance,task_contract,max_iterations})=>{ try {
78
+ },async({submission_id,root_id,title,goal,acceptance,task_contract,max_iterations,timeout_ms})=>{ try {
78
79
  return result(autonomousGoalSubmitOutputSchema,await autonomousRuntime.submit({ submissionId:submission_id,
79
- rootId:root_id,title,goal,acceptance,taskContract:task_contract,maxIterations:max_iterations })); }
80
+ rootId:root_id,title,goal,acceptance,taskContract:task_contract,maxIterations:max_iterations,timeoutMs:timeout_ms })); }
80
81
  catch(error){ return fail(autonomousGoalSubmitOutputSchema,error); } });
81
82
  server.registerTool('autonomous_goal_run',{
82
83
  description:'Advance one autonomous production goal through inspect/native-diff/build/test/benchmark/replan cycles. Client-planner mode returns a typed input_required sentinel for the Bridge MRTR adapter.',
@@ -3,7 +3,17 @@ import * as z from 'zod/v4';
3
3
  const hex64 = z.string().regex(/^[0-9a-f]{64}$/);
4
4
  const goalId = hex64;
5
5
  const jsonObject = z.record(z.string(),z.json());
6
+ const streamCoverage = z.strictObject({
7
+ logical_first_seq:z.number().int().min(1), logical_last_seq:z.number().int().min(0),
8
+ retained_first_seq:z.number().int().min(1), retained_last_seq:z.number().int().min(0),
9
+ complete:z.boolean(), truncated:z.boolean(),
10
+ missing_ranges:z.array(z.strictObject({from:z.number().int().min(1),to:z.number().int().min(0)})).max(100000),
11
+ logical_item_count:z.number().int().min(0), retained_item_count:z.number().int().min(0),
12
+ logical_bytes:z.number().int().min(0), retained_bytes:z.number().int().min(0),
13
+ dropped_items:z.number().int().min(0), dropped_bytes:z.number().int().min(0), status:z.enum(['open','closed'])
14
+ });
6
15
  const goalState = z.enum(['pending','running','paused','cancelled','succeeded']);
16
+ 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() });
7
17
  const loopPhase = z.enum(['PLANNING','INSPECTING','ACTING','VERIFYING','REPLANNING','WAITING','COMPLETE']);
8
18
  const loopStep = z.strictObject({
9
19
  step_id:z.string().regex(/^[A-Za-z0-9._-]{1,64}$/), intent:z.string().min(1).max(512), status:z.literal('active')
@@ -106,7 +116,7 @@ export const autonomousGoalSubmitOutputSchema = statusContract({
106
116
  const runShape = Object.fromEntries(Object.entries(derivedShape).map(([key,schema]) => [key,schema.optional()]));
107
117
  export const autonomousGoalRunOutputSchema = z.strictObject({
108
118
  status:z.enum(['terminal','paused','cancelled','completed','yielded','running','input_required','failed']), error:z.string().optional(),
109
- goal_id:goalId.optional(), release:jsonObject.optional(), planner_request:jsonObject.optional(), loop:publicLoopSchema.optional(),
119
+ goal_id:goalId.optional(), release:jsonObject.optional(), planner_request:jsonObject.optional(), deadline:deadline.optional(), loop:publicLoopSchema.optional(),
110
120
  cycles:z.number().int().min(1).max(32).optional(), ...runShape
111
121
  }).superRefine((value,ctx) => {
112
122
  if (value.error !== undefined) { if (value.status !== 'failed') ctx.addIssue({code:'custom',path:['error'],message:'error wrapper requires failed'}); return; }
@@ -119,12 +129,15 @@ export const autonomousGoalStatusOutputSchema = statusContract({
119
129
  shape:{ goal_id:goalId.optional(), goal_sha256:hex64.optional(), title:z.string().optional(), root_id:z.string().optional(),
120
130
  journal_verified:z.literal(true).optional(), run_busy:z.boolean().optional(),
121
131
  run_owner_pid:z.number().int().min(1).nullable().optional(),
122
- run_lock_created_at_utc:z.string().nullable().optional(), loop:publicLoopSchema.optional(), ...runShape }
132
+ run_lock_created_at_utc:z.string().nullable().optional(), timeout_ms:z.number().int().min(100).max(900000).nullable().optional(), deadline:deadline.optional(), loop:publicLoopSchema.optional(),
133
+ stream_id:goalId.nullable().optional(), stream_generation:z.number().int().min(1).nullable().optional(),
134
+ stream_next_seq:z.number().int().min(1).nullable().optional(), stream_coverage:streamCoverage.nullable().optional(), ...runShape }
123
135
  });
124
136
  export const autonomousGoalEventsOutputSchema = statusContract({
125
137
  statuses:['ok','failed'], success:['ok'], required:['goal_id','journal_verified','journal_head_sha256','events'],
126
138
  shape:{ goal_id:goalId.optional(), journal_verified:z.literal(true).optional(), journal_head_sha256:hex64.optional(),
127
- events:z.array(jsonObject).max(200).optional() }
139
+ events:z.array(jsonObject).max(200).optional(), stream_id:goalId.optional(), stream_generation:z.number().int().min(1).optional(),
140
+ stream_next_seq:z.number().int().min(1).optional(), stream_coverage:streamCoverage.optional() }
128
141
  });
129
142
  export const autonomousGoalCancelOutputSchema = statusContract({
130
143
  statuses:['terminal','requested','existing','failed'], success:['terminal','requested','existing'], required:['goal_id','state'],
@@ -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
 
@@ -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.',