effect-agent 0.1.0-beta.125 → 0.1.0-beta.129

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 (80) hide show
  1. package/dist/{Records-uGX4_EGf.d.mts → Admin-C34cVKlT.d.mts} +2908 -3941
  2. package/dist/{AgentRuntime-ov0LFEb-.d.mts → AgentRuntime-DEDvMkAR.d.mts} +2 -2
  3. package/dist/Digest-U69rDHuO.d.mts +22 -0
  4. package/dist/{DurableAgentRuntime-BvDGe7F9.mjs → DurableAgentRuntime-BydyN_n-.mjs} +88 -15
  5. package/dist/DurableAgentRuntime-BydyN_n-.mjs.map +1 -0
  6. package/dist/{EventSource-CNBoY8AA.d.mts → EventSource-Bac0SxKL.d.mts} +3 -3
  7. package/dist/{PreparedInputAdmission--_APJzWY.d.mts → PreparedInputAdmission-DcQGQ0Yp.d.mts} +4 -4
  8. package/dist/Records-B5GFWCpn.d.mts +1104 -0
  9. package/dist/{Schedule-C8ze8WTW.d.mts → Schedule-CtYREFnz.d.mts} +2 -2
  10. package/dist/{Subagent-CudP5BlT.mjs → Subagent-DJeZDrbD.mjs} +5 -2
  11. package/dist/Subagent-DJeZDrbD.mjs.map +1 -0
  12. package/dist/{Subagent-CCf7rY70.d.mts → Subagent-DRFyQxdb.d.mts} +13 -4
  13. package/dist/{SubagentHost-B0-cRCqs.d.mts → SubagentHost-DQjHFGqH.d.mts} +7 -2
  14. package/dist/{Subscription-CYb8ltMK.d.mts → Subscription-Bk2Smmqc.d.mts} +34 -3
  15. package/dist/{SubscriptionInput-C1XE9Vyi.d.mts → SubscriptionInput-ekbJz3pF.d.mts} +3 -3
  16. package/dist/capabilities/Subagent.d.mts +1 -1
  17. package/dist/capabilities/Subagent.mjs +1 -1
  18. package/dist/durable/ActivityStore.d.mts +1 -1
  19. package/dist/durable/Admin.d.mts +1 -1
  20. package/dist/durable/AgentRegistration.d.mts +1 -1
  21. package/dist/durable/Certification.d.mts +1 -1
  22. package/dist/durable/CommittedActivity.d.mts +3 -1
  23. package/dist/durable/Digest.d.mts +1 -1
  24. package/dist/durable/DurableAgentRuntime.d.mts +1 -1
  25. package/dist/durable/DurableAgentRuntime.mjs +1 -1
  26. package/dist/durable/EventSource.d.mts +1 -1
  27. package/dist/durable/GitHubWorkflowSource.d.mts +3 -3
  28. package/dist/durable/MessageDelivery.d.mts +44 -3
  29. package/dist/durable/MessageDeliveryStoreConformance.d.mts +23 -1
  30. package/dist/durable/PersistentHistory.d.mts +1 -1
  31. package/dist/durable/PreparedInputAdmission.d.mts +1 -1
  32. package/dist/durable/Records.d.mts +2 -2
  33. package/dist/durable/Records.mjs +12 -2
  34. package/dist/durable/Records.mjs.map +1 -1
  35. package/dist/durable/Recovery.d.mts +1 -1
  36. package/dist/durable/RunJournal.d.mts +1 -1
  37. package/dist/durable/Schedule.d.mts +1 -1
  38. package/dist/durable/ScheduleStoreConformance.d.mts +1 -1
  39. package/dist/durable/ScheduleTransition.d.mts +1 -1
  40. package/dist/durable/Scheduling.d.mts +3 -2
  41. package/dist/durable/SqlStorageV2Upgrade.d.mts +2 -2
  42. package/dist/durable/SqlSubscriptionStore.d.mts +2 -2
  43. package/dist/durable/SqlThreadNativeReads.d.mts +2 -1
  44. package/dist/durable/SqlThreadNativeReads.mjs +1 -1
  45. package/dist/durable/SqlThreadNativeReads.mjs.map +1 -1
  46. package/dist/durable/SubmissionLedger.d.mts +1 -1
  47. package/dist/durable/SubmissionLedgerConformance.d.mts +2 -1
  48. package/dist/durable/SubmissionStatus.d.mts +1 -1
  49. package/dist/durable/Subscription.d.mts +1 -1
  50. package/dist/durable/SubscriptionInput.d.mts +1 -1
  51. package/dist/durable/SubscriptionStoreConformance.d.mts +1 -1
  52. package/dist/durable/SubscriptionTools.d.mts +4 -3
  53. package/dist/durable/SubscriptionTransition.d.mts +1 -1
  54. package/dist/durable/Subscriptions.d.mts +6 -5
  55. package/dist/durable/ThreadContextHistory.d.mts +1 -1
  56. package/dist/durable/ThreadContextHistoryProjection.d.mts +1 -1
  57. package/dist/durable/ThreadInvariants.d.mts +2 -1
  58. package/dist/durable/ThreadProjection.d.mts +1 -1
  59. package/dist/durable/ThreadProjectionMaintenance.d.mts +1 -1
  60. package/dist/durable/ThreadStore.d.mts +1 -1
  61. package/dist/durable/ThreadStoreConformance.d.mts +1 -1
  62. package/dist/durable/ThreadStoreConformance.mjs +10 -1
  63. package/dist/durable/ThreadStoreConformance.mjs.map +1 -1
  64. package/dist/durable/WorkerHost.d.mts +9 -2
  65. package/dist/durable/WorkerHost.mjs.map +1 -1
  66. package/dist/engine/AgentRuntime.d.mts +1 -1
  67. package/dist/engine/SubagentHost.d.mts +1 -1
  68. package/dist/engine/SubagentHost.mjs.map +1 -1
  69. package/dist/index.d.mts +12 -10
  70. package/dist/index.mjs +2 -2
  71. package/package.json +1 -1
  72. package/src/capabilities/internal/subagent-background.ts +25 -3
  73. package/src/durable/Records.ts +13 -1
  74. package/src/durable/SqlThreadNativeReads.ts +1 -1
  75. package/src/durable/ThreadStoreConformance.ts +28 -0
  76. package/src/durable/WorkerHost.ts +14 -2
  77. package/src/durable/internal/worker-host.ts +282 -9
  78. package/src/engine/SubagentHost.ts +6 -0
  79. package/dist/DurableAgentRuntime-BvDGe7F9.mjs.map +0 -1
  80. package/dist/Subagent-CudP5BlT.mjs.map +0 -1
@@ -71,6 +71,7 @@ import {
71
71
  WorkerInputRequested,
72
72
  WorkerStopRequested,
73
73
  WorkerOrigin,
74
+ WorkerContinuation,
74
75
  WorkerOriginRecorded,
75
76
  WorkerInputCompleted,
76
77
  WorkerReportPrepared,
@@ -292,6 +293,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
292
293
  origin: WorkerOrigin,
293
294
  principal: Principal,
294
295
  ) {
296
+ yield* verifyContinuation(origin);
295
297
  if (origin.budgetScope !== "worker-run") return;
296
298
  // Independence changes accounting ownership, never delegation generations.
297
299
  if (origin.depth !== 1) return yield* failure("start", "denied");
@@ -300,6 +302,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
300
302
  source: origin.source,
301
303
  principal,
302
304
  worker: origin.worker,
305
+ ...(origin.continuationOf === undefined ? {} : { continuationOf: origin.continuationOf }),
303
306
  policy: origin.policy,
304
307
  budget: origin.budget,
305
308
  });
@@ -321,6 +324,170 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
321
324
  );
322
325
  });
323
326
 
327
+ const completedContinuation = Effect.fn("WorkerHost.completedContinuation")(function* (
328
+ source: WorkerContext["source"],
329
+ worker: WorkerRef,
330
+ targetAgentId: AgentId,
331
+ delegationId: WorkerRef["delegationId"],
332
+ expected?: WorkerContinuation,
333
+ ) {
334
+ const selected = yield* decode(WorkerRef, worker, "start");
335
+
336
+ if (
337
+ selected.targetAgentId !== targetAgentId ||
338
+ selected.delegationId !== delegationId ||
339
+ selected.threadId === source.threadId ||
340
+ deps.ledger.inspectWorker === undefined
341
+ )
342
+ return yield* failure("start", "worker-mismatch");
343
+ const sourceState = yield* readWorkerState(source.threadId, "start");
344
+
345
+ if (
346
+ sourceState.records.some(
347
+ ({ record }) =>
348
+ record.payload._tag === "WorkerStopRequested" &&
349
+ record.payload.command.worker.threadId === selected.threadId,
350
+ )
351
+ )
352
+ return yield* failure("start", "denied");
353
+
354
+ const first = sourceState.records.find(
355
+ ({ record }) => record.recordId === firstWorkerInputRecordId(selected),
356
+ )?.record.payload;
357
+
358
+ const origin = first?._tag === "WorkerInputRequested" ? first.admission.origin : undefined;
359
+
360
+ if (
361
+ origin === undefined ||
362
+ origin.lifecycle !== "assignment" ||
363
+ !Schema.toEquivalence(WorkerRef)(origin.worker, selected) ||
364
+ origin.source.threadId !== source.threadId ||
365
+ origin.source.agentId !== source.agentId ||
366
+ first?._tag !== "WorkerInputRequested" ||
367
+ first.admission.messageId !== origin.firstMessageId
368
+ )
369
+ return yield* failure("start", "worker-mismatch");
370
+
371
+ const recorded = Option.getOrUndefined(
372
+ yield* exactRecord(selected.threadId, workerOriginRecordId(selected.threadId), "start"),
373
+ )?.record.payload;
374
+
375
+ if (recorded?._tag !== "WorkerOriginRecorded" || !sameOrigin(recorded.origin, origin))
376
+ return yield* failure("start", "worker-mismatch");
377
+
378
+ const control = yield* deps.ledger
379
+ .inspectWorker(selected.threadId)
380
+ .pipe(Effect.mapError(storageFailure("start")));
381
+
382
+ if (control.terminal !== "completed" || !control.stopped || control.active !== null)
383
+ return yield* failure("start", "denied");
384
+
385
+ const tail = yield* deps.store
386
+ .inspectTail(ThreadTailRequest.make({ threadId: selected.threadId }))
387
+ .pipe(Effect.mapError(storageFailure("start")));
388
+
389
+ const execution = yield* deps.store
390
+ .read({
391
+ threadId: selected.threadId,
392
+ selection: {
393
+ _tag: "WorkerExecution",
394
+ expectedTailSequence: tail.tailSequence,
395
+ expectedTailDigest: tail.tailDigest,
396
+ },
397
+ page: { limit: 2 },
398
+ })
399
+ .pipe(Stream.runCollect, Effect.mapError(storageFailure("start")));
400
+
401
+ const run = execution.find(({ record }) => record.payload._tag === "RunStarted")?.record
402
+ .payload;
403
+
404
+ if (run?._tag !== "RunStarted") return yield* failure("start", "corrupt");
405
+
406
+ const input = Option.getOrUndefined(
407
+ yield* getRunInput({ threadId: selected.threadId, runId: run.runId }).pipe(
408
+ Effect.provideService(ThreadStore, deps.store),
409
+ Effect.mapError(storageFailure("start")),
410
+ ),
411
+ )?.record.payload;
412
+
413
+ if (input?._tag !== "UserInputRecorded" || input.submissionId === undefined)
414
+ return yield* failure("start", "corrupt");
415
+
416
+ const found = yield* deps.ledger
417
+ .lookup(SubmissionLookupById.make({ submissionId: input.submissionId }))
418
+ .pipe(Effect.mapError(storageFailure("start")));
419
+
420
+ const row = Option.getOrUndefined(found);
421
+
422
+ const settlement = Option.getOrUndefined(
423
+ yield* exactRecord(
424
+ selected.threadId,
425
+ submissionSettlementRecordId(input.submissionId),
426
+ "start",
427
+ ),
428
+ )?.record.payload;
429
+
430
+ if (
431
+ row === undefined ||
432
+ row.threadId !== selected.threadId ||
433
+ row.state !== "settled" ||
434
+ row.settledOutcome !== "completed" ||
435
+ row.workerAdmission === undefined ||
436
+ !sameOrigin(row.workerAdmission.origin, origin) ||
437
+ settlement?._tag !== "SubmissionSettled" ||
438
+ settlement.outcome !== "completed" ||
439
+ settlement.runDisposition !== "completed" ||
440
+ settlement.finishReason !== undefined ||
441
+ settlement.runId !== run.runId ||
442
+ settlement.submissionId !== row.submissionId ||
443
+ settlement.receiptId !== row.receiptId
444
+ )
445
+ return yield* failure("start", "denied");
446
+
447
+ const evidence = WorkerContinuation.make({
448
+ worker: selected,
449
+ receipt: Receipt.make({
450
+ threadId: row.threadId,
451
+ submissionId: row.submissionId,
452
+ receiptId: row.receiptId,
453
+ queueSequence: row.queueSequence,
454
+ }),
455
+ settlementId: settlement.settlementId,
456
+ });
457
+
458
+ if (expected !== undefined && !Schema.toEquivalence(WorkerContinuation)(expected, evidence))
459
+ return yield* failure("start", "worker-mismatch");
460
+
461
+ return { evidence, origin };
462
+ });
463
+
464
+ const verifyContinuation = Effect.fn("WorkerHost.verifyContinuation")(function* (
465
+ origin: WorkerOrigin,
466
+ ) {
467
+ if (origin.continuationOf === undefined) return;
468
+
469
+ const previous = yield* completedContinuation(
470
+ origin.source,
471
+ origin.continuationOf.worker,
472
+ origin.worker.targetAgentId,
473
+ origin.worker.delegationId,
474
+ origin.continuationOf,
475
+ );
476
+
477
+ if (
478
+ origin.worker.threadId === previous.origin.worker.threadId ||
479
+ origin.lifecycle !== "assignment" ||
480
+ !samePolicy(origin.policy, previous.origin.policy) ||
481
+ !Schema.toEquivalence(WorkerOrigin.fields.budget)(origin.budget, previous.origin.budget) ||
482
+ !Schema.toEquivalence(SubagentGrant)(origin.grant, previous.origin.grant) ||
483
+ origin.budgetScope !== previous.origin.budgetScope ||
484
+ origin.depth !== previous.origin.depth ||
485
+ origin.toolCallAllowance !== previous.origin.toolCallAllowance ||
486
+ origin.expiresAtMillis > previous.origin.expiresAtMillis
487
+ )
488
+ return yield* failure("start", "denied");
489
+ });
490
+
324
491
  const readIdentity = Effect.fn("WorkerHost.readIdentity")(function* (
325
492
  threadId: ThreadId,
326
493
  operation: WorkerError["operation"],
@@ -885,6 +1052,8 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
885
1052
  retainedFrameworkInput: boolean,
886
1053
  ): Effect.fn.Return<void, WorkerError> {
887
1054
  const origin = admission.origin;
1055
+
1056
+ yield* verifyContinuation(origin);
888
1057
  const id = workerInputRecordId(admission.messageId);
889
1058
 
890
1059
  for (let attempt = 0; attempt < 16; attempt++) {
@@ -895,6 +1064,16 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
895
1064
  );
896
1065
 
897
1066
  const current = source.current;
1067
+
1068
+ if (
1069
+ origin.continuationOf !== undefined &&
1070
+ current.records.some(
1071
+ ({ record }) =>
1072
+ record.payload._tag === "WorkerStopRequested" &&
1073
+ record.payload.command.worker.threadId === origin.continuationOf?.worker.threadId,
1074
+ )
1075
+ )
1076
+ return yield* failure("start", "denied");
898
1077
  const rows = requests(current.records);
899
1078
  const existing = current.records.find(({ record }) => record.recordId === id)?.record.payload;
900
1079
 
@@ -955,6 +1134,9 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
955
1134
  ? {
956
1135
  ...targetRequest,
957
1136
  _tag: "InitialInput",
1137
+ ...(origin.continuationOf === undefined
1138
+ ? {}
1139
+ : { continuationOf: origin.continuationOf }),
958
1140
  input,
959
1141
  inputDigest,
960
1142
  ...(source.submission === undefined ? {} : { sourceSubmission: source.submission }),
@@ -1057,6 +1239,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
1057
1239
  const selectedConcurrency = yield* deps.concurrencyResolver.resolve({
1058
1240
  source: origin.source,
1059
1241
  worker: origin.worker,
1242
+ ...(origin.continuationOf === undefined ? {} : { continuationOf: origin.continuationOf }),
1060
1243
  principal,
1061
1244
  ...(source.submission === undefined ? {} : { sourceSubmission: source.submission }),
1062
1245
  });
@@ -1168,6 +1351,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
1168
1351
  yield* validateCompletion(options.messageAdmission, options, agentId, inputDigest);
1169
1352
  }
1170
1353
 
1354
+ yield* verifyContinuation(admission.origin);
1171
1355
  yield* deps.authorizer.authorize({
1172
1356
  sourceThreadId: admission.origin.source.threadId,
1173
1357
  ...(admission.sourceSubmissionId === undefined
@@ -1177,6 +1361,9 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
1177
1361
  operation: "start",
1178
1362
  access: "send",
1179
1363
  worker: admission.origin.worker,
1364
+ ...(admission.origin.continuationOf === undefined
1365
+ ? {}
1366
+ : { continuationOf: admission.origin.continuationOf }),
1180
1367
  });
1181
1368
  yield* authorizeBudget(admission.origin, options.principal);
1182
1369
  if (
@@ -1440,7 +1627,11 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
1440
1627
  },
1441
1628
  };
1442
1629
 
1443
- const projection = yield* Effect.suspend(() => descriptor.prepare(report)).pipe(
1630
+ const projection = yield* Effect.suspend(() =>
1631
+ descriptor.reportCompletion?.(report) === false
1632
+ ? Effect.succeed(undefined)
1633
+ : descriptor.prepare(report),
1634
+ ).pipe(
1444
1635
  Effect.timeout(
1445
1636
  Math.min(deps.limits.reportPreparationTimeoutMillis ?? 5_000, deadlineAtMillis - now),
1446
1637
  ),
@@ -1457,6 +1648,8 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
1457
1648
 
1458
1649
  if (projection._tag === "WorkerReportRefused") return projection;
1459
1650
 
1651
+ if (projection.value === undefined) return refused("filtered");
1652
+
1460
1653
  const message = projection.value.message;
1461
1654
 
1462
1655
  if (!Schema.is(WorkerCompletion)(message)) return refused("input");
@@ -1678,6 +1871,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
1678
1871
  operation: WorkerError["operation"],
1679
1872
  access: "context" | "read" | "send" | "control",
1680
1873
  worker?: WorkerRef,
1874
+ continuationOf?: WorkerContinuation,
1681
1875
  ) =>
1682
1876
  deps.authorizer.authorize({
1683
1877
  sourceThreadId: context.source.threadId,
@@ -1686,6 +1880,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
1686
1880
  operation,
1687
1881
  access,
1688
1882
  ...(worker === undefined ? {} : { worker }),
1883
+ ...(continuationOf === undefined ? {} : { continuationOf }),
1689
1884
  });
1690
1885
 
1691
1886
  const binding = (target: Agent.AnyDefinition, operation: WorkerError["operation"]) =>
@@ -1699,6 +1894,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
1699
1894
  const preparedTarget = Effect.fn("WorkerHost.preparedTarget")(function* (
1700
1895
  target: Agent.AnyDefinition,
1701
1896
  encodedInput: unknown,
1897
+ continuationOf?: WorkerContinuation,
1702
1898
  ) {
1703
1899
  const resolved = yield* binding(target, "start");
1704
1900
 
@@ -1715,6 +1911,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
1715
1911
 
1716
1912
  const policy = yield* resolveTargetPolicy({
1717
1913
  _tag: "InitialInput",
1914
+ ...(continuationOf === undefined ? {} : { continuationOf }),
1718
1915
  definition: resolved.definition,
1719
1916
  definitions: resolved.digests,
1720
1917
  source: context.source,
@@ -2187,14 +2384,35 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
2187
2384
  };
2188
2385
  }),
2189
2386
  resolveTargetPolicy: Effect.fn("WorkerHost.resolvePreparedTargetPolicy")(function* (request) {
2190
- yield* authorize("start", "send");
2191
-
2192
- return (yield* preparedTarget(request.target, request.encodedInput)).policy;
2387
+ const previous =
2388
+ request.continuationOf === undefined
2389
+ ? undefined
2390
+ : yield* completedContinuation(
2391
+ context.source,
2392
+ request.continuationOf,
2393
+ request.target.id,
2394
+ request.continuationOf.delegationId,
2395
+ );
2396
+
2397
+ yield* authorize("start", "send", undefined, previous?.evidence);
2398
+
2399
+ return (yield* preparedTarget(request.target, request.encodedInput, previous?.evidence))
2400
+ .policy;
2193
2401
  }),
2194
2402
  start: Effect.fn("WorkerHost.start")(function* <E = never, R = never>(
2195
2403
  command: StartWorkerRequest | DeferredStartWorkerRequest<E, R>,
2196
2404
  ): Effect.fn.Return<WorkerStarted, WorkerError | E, R> {
2197
- const principal = yield* authorize("start", "send");
2405
+ const previous =
2406
+ command.continuationOf === undefined
2407
+ ? undefined
2408
+ : yield* completedContinuation(
2409
+ context.source,
2410
+ command.continuationOf,
2411
+ command.target.id,
2412
+ command.delegationId,
2413
+ );
2414
+
2415
+ const principal = yield* authorize("start", "send", undefined, previous?.evidence);
2198
2416
 
2199
2417
  if (context.depth !== 0 && sourceSubmissionId === undefined)
2200
2418
  return yield* failure("start", "denied");
@@ -2221,7 +2439,23 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
2221
2439
 
2222
2440
  const replay = (saved: MessageDeliveryRecord) =>
2223
2441
  Effect.gen(function* () {
2224
- const replayPrincipal = yield* authorize("start", "send");
2442
+ const currentPrevious =
2443
+ command.continuationOf === undefined
2444
+ ? undefined
2445
+ : yield* completedContinuation(
2446
+ context.source,
2447
+ command.continuationOf,
2448
+ command.target.id,
2449
+ command.delegationId,
2450
+ previous?.evidence,
2451
+ );
2452
+
2453
+ const replayPrincipal = yield* authorize(
2454
+ "start",
2455
+ "send",
2456
+ undefined,
2457
+ currentPrevious?.evidence,
2458
+ );
2225
2459
 
2226
2460
  yield* binding(command.target, "start");
2227
2461
  const metadata = saved.envelope.workerAdmission;
@@ -2230,6 +2464,10 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
2230
2464
  const origin = metadata.origin;
2231
2465
 
2232
2466
  if (
2467
+ !Schema.toEquivalence(Schema.UndefinedOr(WorkerContinuation))(
2468
+ origin.continuationOf,
2469
+ currentPrevious?.evidence,
2470
+ ) ||
2233
2471
  origin.firstMessageId !== messageId ||
2234
2472
  metadata.messageId !== messageId ||
2235
2473
  origin.source.threadId !== context.source.threadId ||
@@ -2249,6 +2487,8 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
2249
2487
  )
2250
2488
  return yield* failure("start", "idempotency-conflict");
2251
2489
 
2490
+ yield* verifyContinuation(origin);
2491
+
2252
2492
  return {
2253
2493
  worker: origin.worker,
2254
2494
  delivery: yield* send(
@@ -2270,7 +2510,34 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
2270
2510
  const request =
2271
2511
  "prepare" in command ? { ...(yield* command.prepare), ...command } : command;
2272
2512
 
2273
- const prepared = yield* preparedTarget(request.target, request.encodedInput);
2513
+ const currentPrevious =
2514
+ command.continuationOf === undefined
2515
+ ? undefined
2516
+ : yield* completedContinuation(
2517
+ context.source,
2518
+ command.continuationOf,
2519
+ command.target.id,
2520
+ command.delegationId,
2521
+ previous?.evidence,
2522
+ );
2523
+
2524
+ if (currentPrevious !== undefined) {
2525
+ const preparedPrincipal = yield* authorize(
2526
+ "start",
2527
+ "send",
2528
+ undefined,
2529
+ currentPrevious.evidence,
2530
+ );
2531
+
2532
+ if (preparedPrincipal !== principal) return yield* failure("start", "denied");
2533
+ }
2534
+
2535
+ const prepared = yield* preparedTarget(
2536
+ request.target,
2537
+ request.encodedInput,
2538
+ currentPrevious?.evidence,
2539
+ );
2540
+
2274
2541
  const resolved = prepared.resolved;
2275
2542
 
2276
2543
  const sourcePolicy = Option.getOrElse(
@@ -2304,6 +2571,9 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
2304
2571
  threadId: Schema.decodeSync(ThreadId)(messageId),
2305
2572
  },
2306
2573
  source: context.source,
2574
+ ...(currentPrevious === undefined
2575
+ ? {}
2576
+ : { continuationOf: currentPrevious.evidence }),
2307
2577
  ...(resolved.definition.runDisposition?.workerLifecycle === "assignment"
2308
2578
  ? { lifecycle: "assignment" }
2309
2579
  : {}),
@@ -2315,7 +2585,10 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
2315
2585
  depth: context.depth + 1,
2316
2586
  firstMessageId: messageId,
2317
2587
  createdAtMillis: now,
2318
- expiresAtMillis: now + deps.limits.lifetimeMillis,
2588
+ expiresAtMillis: Math.min(
2589
+ now + deps.limits.lifetimeMillis,
2590
+ currentPrevious?.origin.expiresAtMillis ?? Infinity,
2591
+ ),
2319
2592
  ...(reporting === undefined ? {} : { reporting }),
2320
2593
  ...(request.toolCallAllowance === undefined
2321
2594
  ? {}
@@ -2798,7 +3071,7 @@ export const makeWorkerRuntime = Effect.fn("WorkerHost.make")(function* (
2798
3071
  ...(sourceSubmissionId === undefined ? {} : { sourceSubmissionId }),
2799
3072
  principal: options.principal,
2800
3073
  operation: "followUp",
2801
- access: "send",
3074
+ access: "report",
2802
3075
  worker: receiving?.origin.worker ?? origin.worker,
2803
3076
  });
2804
3077
 
@@ -27,6 +27,8 @@ import {
27
27
  /** Prepared values cross this port only to be Schema-decoded before durable storage. */
28
28
  export interface StartWorkerRequest {
29
29
  readonly delegationId: DelegationId;
30
+ /** Start a distinct assignment using verified completed predecessor authority. */
31
+ readonly continuationOf?: WorkerRef;
30
32
  readonly target: Agent.AnyDefinition;
31
33
  readonly idempotencyKey: IdempotencyKey;
32
34
  readonly encodedInput: unknown;
@@ -50,6 +52,7 @@ export interface StartWorkerRequest {
50
52
  export interface DeferredStartWorkerRequest<E = never, R = never> extends Pick<
51
53
  StartWorkerRequest,
52
54
  | "delegationId"
55
+ | "continuationOf"
53
56
  | "target"
54
57
  | "idempotencyKey"
55
58
  | "encodedParameters"
@@ -140,6 +143,8 @@ export interface WorkerReporting<E = never, R = never> {
140
143
  readonly target: Agent.AnyDefinition;
141
144
  /** Pure first-emission selection. False retains the canonical update without parent delivery. */
142
145
  readonly reportUpdate?: (update: Update) => boolean;
146
+ /** False retains a filtered report receipt without waking the parent. */
147
+ readonly reportCompletion?: (report: WorkerRunReport) => boolean;
143
148
  readonly prepare: (report: WorkerRunReport) => Effect.Effect<
144
149
  {
145
150
  readonly message: WorkerCompletion;
@@ -165,6 +170,7 @@ export class SubagentHost extends Context.Service<
165
170
  readonly context: Effect.Effect<WorkerContext, WorkerError>;
166
171
  /** Resolve prepared input through host authority; None retains legacy target inheritance. */
167
172
  readonly resolveTargetPolicy: (request: {
173
+ readonly continuationOf?: WorkerRef;
168
174
  readonly target: Agent.AnyDefinition;
169
175
  readonly encodedInput: unknown;
170
176
  }) => Effect.Effect<Option.Option<AgentPolicy>, WorkerError>;