@agen-ai/agent-runtime 0.3.1 → 0.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +176 -25
  2. package/dist/adapterValidation.d.ts +1 -1
  3. package/dist/adapterValidation.js +162 -95
  4. package/dist/contractErrors.d.ts +15 -2
  5. package/dist/contractErrors.js +39 -1
  6. package/dist/hostBindings/index.d.ts +4 -0
  7. package/dist/hostBindings/index.js +9 -0
  8. package/dist/hostBindings/lifetime.d.ts +25 -0
  9. package/dist/hostBindings/lifetime.js +147 -0
  10. package/dist/hostBindings/opening.d.ts +12 -0
  11. package/dist/hostBindings/opening.js +24 -0
  12. package/dist/hostBindings/schema.d.ts +9 -0
  13. package/dist/hostBindings/schema.js +169 -0
  14. package/dist/hostBindings/types.d.ts +71 -0
  15. package/dist/hostBindings/types.js +0 -0
  16. package/dist/hostBindings/validation.d.ts +14 -0
  17. package/dist/hostBindings/validation.js +107 -0
  18. package/dist/index.d.ts +2 -0
  19. package/dist/index.js +6 -0
  20. package/dist/internal/objectShape.d.ts +2 -0
  21. package/dist/internal/objectShape.js +7 -0
  22. package/dist/outputValidation.js +1 -0
  23. package/dist/sessionCleanup.d.ts +9 -0
  24. package/dist/sessionCleanup.js +17 -0
  25. package/dist/sessionValidation.d.ts +12 -2
  26. package/dist/sessionValidation.js +134 -305
  27. package/dist/sessions.d.ts +28 -4
  28. package/dist/testing/conformance.d.ts +3 -3
  29. package/dist/testing/conformance.js +24 -30
  30. package/dist/testing/fakeProvider.js +56 -45
  31. package/dist/turnExecution/execution.d.ts +26 -0
  32. package/dist/turnExecution/execution.js +214 -0
  33. package/dist/turnExecution/receipts.d.ts +6 -0
  34. package/dist/turnExecution/receipts.js +39 -0
  35. package/dist/turnExecution/requests.d.ts +19 -0
  36. package/dist/turnExecution/requests.js +56 -0
  37. package/package.json +2 -2
@@ -9,8 +9,6 @@ import {
9
9
  parseAgentGeneratedResourceId,
10
10
  parseAgentOperationInvocation,
11
11
  parseAgentOperationInvocationFor,
12
- parseAgentRequestResolution,
13
- parseAgentRequestResolutionFor,
14
12
  parseAgentSessionBinding,
15
13
  parseAgentTurnId,
16
14
  parseAgentTurnInputContent,
@@ -32,8 +30,12 @@ import {
32
30
  } from "./interactionValidation.js";
33
31
  import {
34
32
  AgentProviderDelegatedOperationError,
33
+ AgentProviderSessionCleanupError,
35
34
  throwAgentProviderContractError
36
35
  } from "./contractErrors.js";
36
+ import { createAgentHostBindingCleanupError } from "./hostBindings/lifetime.js";
37
+ import { associateAgentSessionCleanupError } from "./sessionCleanup.js";
38
+ import { hasExactOwnKeys } from "./internal/objectShape.js";
37
39
  import {
38
40
  throwIfAgentOperationAborted
39
41
  } from "./foundation.js";
@@ -42,18 +44,12 @@ import {
42
44
  validateAgentProviderOutputForContext
43
45
  } from "./outputValidation.js";
44
46
  import { validateAgentTurnSteeringResult } from "./steeringValidation.js";
45
- function hasExactOwnKeys(candidate, expectedKeys) {
46
- const ownKeys = Reflect.ownKeys(candidate);
47
- if (ownKeys.some((key) => typeof key !== "string")) return false;
48
- const actualKeys = ownKeys.sort();
49
- const canonicalExpectedKeys = [...expectedKeys].sort();
50
- return actualKeys.length === canonicalExpectedKeys.length && actualKeys.every((key, index) => key === canonicalExpectedKeys[index]);
51
- }
47
+ import { createValidatedAgentTurnExecution } from "./turnExecution/execution.js";
48
+ import { validateAgentProviderTurnInterruptionResult } from "./turnExecution/receipts.js";
52
49
  function validateSessionPorts(capabilities, session) {
53
50
  if (!hasExactOwnKeys(session, [
54
51
  "binding",
55
- "runTurn",
56
- "resolveRequest",
52
+ "startTurn",
57
53
  "interruption",
58
54
  "steering",
59
55
  "configuration",
@@ -63,7 +59,7 @@ function validateSessionPorts(capabilities, session) {
63
59
  "collaboration",
64
60
  "generatedResources",
65
61
  "close"
66
- ]) || typeof session.runTurn !== "function" || typeof session.resolveRequest !== "function" || typeof session.close !== "function" || session.interruption === null || typeof session.interruption !== "object" || !["supported", "unsupported"].includes(session.interruption.kind) || session.steering === null || typeof session.steering !== "object" || !["supported", "unsupported"].includes(session.steering.kind) || session.configuration === null || typeof session.configuration !== "object" || !["managed", "selectable"].includes(session.configuration.kind) || session.operations === null || typeof session.operations !== "object" || !["supported", "unsupported"].includes(session.operations.kind) || session.managedContent === null || typeof session.managedContent !== "object" || !["supported", "unsupported"].includes(session.managedContent.kind) || session.environment === null || typeof session.environment !== "object" || !["read", "read_and_watch", "unsupported"].includes(session.environment.kind) || session.collaboration === null || typeof session.collaboration !== "object" || !["supported", "unsupported"].includes(session.collaboration.kind) || session.generatedResources === null || typeof session.generatedResources !== "object" || !["supported", "unsupported"].includes(session.generatedResources.kind)) {
62
+ ]) || typeof session.startTurn !== "function" || typeof session.close !== "function" || session.interruption === null || typeof session.interruption !== "object" || !["supported", "unsupported"].includes(session.interruption.kind) || session.steering === null || typeof session.steering !== "object" || !["supported", "unsupported"].includes(session.steering.kind) || session.configuration === null || typeof session.configuration !== "object" || !["managed", "selectable"].includes(session.configuration.kind) || session.operations === null || typeof session.operations !== "object" || !["supported", "unsupported"].includes(session.operations.kind) || session.managedContent === null || typeof session.managedContent !== "object" || !["supported", "unsupported"].includes(session.managedContent.kind) || session.environment === null || typeof session.environment !== "object" || !["read", "read_and_watch", "unsupported"].includes(session.environment.kind) || session.collaboration === null || typeof session.collaboration !== "object" || !["supported", "unsupported"].includes(session.collaboration.kind) || session.generatedResources === null || typeof session.generatedResources !== "object" || !["supported", "unsupported"].includes(session.generatedResources.kind)) {
67
63
  throwAgentProviderContractError(
68
64
  capabilities.providerKey,
69
65
  "invalid_session",
@@ -169,7 +165,6 @@ function completeTurnSequence(input) {
169
165
  );
170
166
  }
171
167
  input.state.terminal = true;
172
- input.state.waiting = false;
173
168
  }
174
169
  const FINAL_DIFF_TRAILING_EVENT_TYPES = [
175
170
  "artifact.referenced",
@@ -305,6 +300,7 @@ function observeTurnEvent(input) {
305
300
  contextUsageState,
306
301
  event,
307
302
  openedRequestIds,
303
+ turnRequestIds,
308
304
  pendingRequests,
309
305
  providerKey,
310
306
  state
@@ -399,14 +395,15 @@ function observeTurnEvent(input) {
399
395
  state
400
396
  });
401
397
  openedRequestIds.add(requestId);
398
+ turnRequestIds.add(requestId);
402
399
  pendingRequests.set(requestId, {
403
400
  request: event.payload.request,
404
401
  turnId: event.turnId
405
402
  });
406
403
  }
407
404
  if (event.type === "turn.state_changed") {
408
- state.waiting = event.payload.state === "waiting_for_request";
409
- if (state.waiting && !pendingRequests.has(event.payload.requestId)) {
405
+ const waiting = event.payload.state === "waiting_for_request";
406
+ if (waiting && !turnRequestIds.has(event.payload.requestId)) {
410
407
  invalidTurnSequence(
411
408
  providerKey,
412
409
  "Provider entered a waiting state for a request it did not open."
@@ -414,6 +411,7 @@ function observeTurnEvent(input) {
414
411
  }
415
412
  }
416
413
  if (event.type === "turn.completed") {
414
+ if (event.payload.outcome !== "completed") pendingRequests.clear();
417
415
  completeTurnSequence({ providerKey, state, pendingRequests });
418
416
  }
419
417
  }
@@ -506,6 +504,7 @@ function observeTurnOutput(input) {
506
504
  state: input.state,
507
505
  pendingRequests: input.pendingRequests,
508
506
  openedRequestIds: input.openedRequestIds,
507
+ turnRequestIds: input.turnRequestIds,
509
508
  contextUsageState: input.contextUsageState
510
509
  });
511
510
  }
@@ -544,23 +543,11 @@ function observeSessionOperationOutput(input) {
544
543
  state: input.state,
545
544
  pendingRequests: /* @__PURE__ */ new Map(),
546
545
  openedRequestIds: /* @__PURE__ */ new Set(),
546
+ turnRequestIds: /* @__PURE__ */ new Set(),
547
547
  contextUsageState: input.contextUsageState,
548
548
  interactionState: input.interactionState
549
549
  });
550
550
  }
551
- function assertStableTurnBoundary(input) {
552
- const terminal = input.state.terminal && !input.state.waiting;
553
- const waiting = !input.state.terminal && input.state.waiting && input.pendingRequests.size === 1;
554
- if (terminal && input.pendingRequests.size === 0 || waiting) return;
555
- invalidTurnSequence(
556
- input.providerKey,
557
- "Provider turn must finish with completion or exactly one pending request."
558
- );
559
- }
560
- function replacePendingRequests(target, source) {
561
- target.clear();
562
- for (const [requestId, pending] of source) target.set(requestId, pending);
563
- }
564
551
  function trackActiveTurnOperation(turn) {
565
552
  if (!turn) return null;
566
553
  let settle;
@@ -573,45 +560,6 @@ function trackActiveTurnOperation(turn) {
573
560
  settle();
574
561
  };
575
562
  }
576
- function interruptionTerminalizesTurn(input) {
577
- if (["completed", "canceled"].includes(input.result.status)) {
578
- return true;
579
- }
580
- return (input.result.outputs ?? []).some(
581
- (output) => output.kind === "event" && output.event.turnId === input.turnId && output.event.type === "turn.completed"
582
- );
583
- }
584
- function observeTurnOperationOutputs(input) {
585
- for (const output of input.outputs ?? []) {
586
- observeTurnOutput({
587
- providerKey: input.providerKey,
588
- output,
589
- state: input.state,
590
- pendingRequests: input.pendingRequests,
591
- openedRequestIds: input.openedRequestIds,
592
- contextUsageState: input.contextUsageState,
593
- capabilities: input.capabilities,
594
- interactionState: input.interactionState
595
- });
596
- }
597
- }
598
- function reconstructWaitingTurn(input) {
599
- const targetedPendingRequests = new Map(
600
- [...input.pendingRequests].filter(
601
- ([, pending]) => pending.turnId === input.turnId
602
- )
603
- );
604
- if (targetedPendingRequests.size === 0 || input.state === void 0 || input.state.terminal || !input.state.waiting) return null;
605
- return {
606
- state: input.state,
607
- pendingRequests: targetedPendingRequests
608
- };
609
- }
610
- function clearPendingRequestsForTurn(pendingRequests, turnId) {
611
- for (const [requestId, pending] of pendingRequests) {
612
- if (pending.turnId === turnId) pendingRequests.delete(requestId);
613
- }
614
- }
615
563
  function assertAgentTurnInputCapability(input) {
616
564
  const imageParts = input.parts.filter((part) => part.type === "image");
617
565
  if (imageParts.length > 0) {
@@ -726,8 +674,6 @@ function validateAgentProviderSession(input) {
726
674
  port: input.candidate.environment,
727
675
  signal: sessionLifetime.signal
728
676
  });
729
- const pendingRequests = /* @__PURE__ */ new Map();
730
- const waitingTurnStates = /* @__PURE__ */ new Map();
731
677
  const openedRequestIds = /* @__PURE__ */ new Set();
732
678
  const contextUsageState = {
733
679
  latestByScope: /* @__PURE__ */ new Map(),
@@ -746,6 +692,7 @@ function validateAgentProviderSession(input) {
746
692
  let activeTurn = null;
747
693
  let closePromise = null;
748
694
  let closed = false;
695
+ let providerClosed = false;
749
696
  let unusable = false;
750
697
  const requireOpen = () => {
751
698
  if (closed)
@@ -771,17 +718,6 @@ function validateAgentProviderSession(input) {
771
718
  sessionId,
772
719
  ...turnId === void 0 ? {} : { turnId }
773
720
  });
774
- const delegateOperation = async (operation, turnId) => {
775
- try {
776
- return validateAgentProviderOperationResult(
777
- await operation(),
778
- outputContext(turnId)
779
- );
780
- } catch (error) {
781
- unusable = true;
782
- throw error;
783
- }
784
- };
785
721
  const delegateProviderMutation = async (input2) => {
786
722
  let executionStarted = false;
787
723
  const onProviderExecutionStarted = () => {
@@ -825,7 +761,7 @@ function validateAgentProviderSession(input) {
825
761
  );
826
762
  }
827
763
  };
828
- const runTurn = async function* (turnInput) {
764
+ const startTurn = (turnInput) => {
829
765
  requireUsable();
830
766
  throwIfAgentOperationAborted(turnInput.signal);
831
767
  const parsedInput = parseAgentTurnRunInput({
@@ -835,11 +771,7 @@ function validateAgentProviderSession(input) {
835
771
  ...turnInput.summary === void 0 ? {} : { summary: turnInput.summary },
836
772
  ...turnInput.deadlineAt === void 0 ? {} : { deadlineAt: turnInput.deadlineAt }
837
773
  });
838
- assertAgentTurnInputCapability({
839
- capability: capabilities.input,
840
- parts: parsedInput.parts,
841
- providerKey
842
- });
774
+ assertAgentTurnInputCapability({ capability: capabilities.input, parts: parsedInput.parts, providerKey });
843
775
  if (!capabilities.turns.interactionModes.includes(parsedInput.interactionMode)) {
844
776
  throwAgentProviderContractError(
845
777
  providerKey,
@@ -847,179 +779,63 @@ function validateAgentProviderSession(input) {
847
779
  `Provider ${providerKey} does not support ${parsedInput.interactionMode} turn interaction mode.`
848
780
  );
849
781
  }
850
- if (pendingRequests.size > 0) {
851
- throwAgentProviderContractError(
852
- providerKey,
853
- "request_pending",
854
- "Provider session cannot start another turn while a request is pending."
855
- );
856
- }
857
782
  if (activeTurn !== null) {
858
- throwAgentProviderContractError(
859
- providerKey,
860
- "concurrent_turn",
861
- "Provider session already has an active turn."
862
- );
783
+ throwAgentProviderContractError(providerKey, "concurrent_turn", "Provider session already has an active turn.");
863
784
  }
864
- let reachedStableBoundary = false;
865
- const currentTurn = {
866
- turnId: parsedInput.turnId,
867
- pendingRequests: /* @__PURE__ */ new Map(),
868
- state: {
869
- fileChangeMode: capabilities.output.fileChanges,
870
- observedItems: /* @__PURE__ */ new Map(),
871
- proposedPlans: /* @__PURE__ */ new Map(),
872
- generatedResourceIds: /* @__PURE__ */ new Set(),
873
- started: false,
874
- terminal: false,
875
- waiting: false,
876
- finalDiffObserved: false
877
- },
878
- inFlightOperations: /* @__PURE__ */ new Set()
785
+ const pendingRequests = /* @__PURE__ */ new Map();
786
+ const turnRequestIds = /* @__PURE__ */ new Set();
787
+ const state = {
788
+ fileChangeMode: capabilities.output.fileChanges,
789
+ observedItems: /* @__PURE__ */ new Map(),
790
+ proposedPlans: /* @__PURE__ */ new Map(),
791
+ generatedResourceIds: /* @__PURE__ */ new Set(),
792
+ started: false,
793
+ terminal: false,
794
+ finalDiffObserved: false
879
795
  };
880
- activeTurn = currentTurn;
881
- try {
882
- turnInput.onProviderExecutionStarted?.();
883
- for await (const candidate of input.candidate.runTurn({
796
+ const inFlightOperations = /* @__PURE__ */ new Set();
797
+ const owner = createValidatedAgentTurnExecution({
798
+ providerKey,
799
+ turnInput: {
884
800
  ...parsedInput,
885
- ...turnInput.signal === void 0 ? {} : { signal: turnInput.signal }
886
- })) {
887
- const output = validateAgentProviderOutputForContext(
888
- candidate,
889
- outputContext(parsedInput.turnId)
890
- );
891
- observeTurnOutput({
892
- capabilities,
893
- providerKey,
894
- output,
895
- state: currentTurn.state,
896
- pendingRequests: currentTurn.pendingRequests,
897
- openedRequestIds,
898
- contextUsageState,
899
- interactionState
900
- });
901
- yield output;
902
- }
903
- while (currentTurn.inFlightOperations.size > 0) {
904
- await Promise.all(currentTurn.inFlightOperations);
905
- }
906
- if (!currentTurn.state.started) {
907
- invalidTurnSequence(
908
- providerKey,
909
- "Provider turn must emit turn.started before reaching a stable boundary."
910
- );
911
- }
912
- assertStableTurnBoundary({
913
- providerKey,
914
- state: currentTurn.state,
915
- pendingRequests: currentTurn.pendingRequests
916
- });
917
- if (currentTurn.state.waiting) {
918
- replacePendingRequests(pendingRequests, currentTurn.pendingRequests);
919
- waitingTurnStates.set(currentTurn.turnId, currentTurn.state);
920
- } else {
921
- waitingTurnStates.delete(currentTurn.turnId);
922
- }
923
- reachedStableBoundary = true;
924
- } finally {
925
- if (!reachedStableBoundary) unusable = true;
926
- if (activeTurn === currentTurn) activeTurn = null;
927
- }
928
- };
929
- const resolveRequest = async function* (requestInput) {
930
- requireUsable();
931
- throwIfAgentOperationAborted(requestInput.signal);
932
- const resolution = parseAgentRequestResolution(requestInput.resolution);
933
- const pending = pendingRequests.get(resolution.requestId);
934
- if (!pending) {
935
- throwAgentProviderContractError(
936
- providerKey,
937
- "request_resolution_mismatch",
938
- "Provider request resolution does not identify a pending request."
939
- );
940
- }
941
- try {
942
- parseAgentRequestResolutionFor(pending.request, resolution);
943
- } catch {
944
- throwAgentProviderContractError(
945
- providerKey,
946
- "request_resolution_mismatch",
947
- "Provider request resolution does not match the opened request."
948
- );
949
- }
950
- if (pending.request.expiresAt !== void 0 && Date.parse(pending.request.expiresAt) <= Date.now() && resolution.disposition !== "canceled") {
951
- throwAgentProviderContractError(
952
- providerKey,
953
- "request_resolution_mismatch",
954
- "Provider request resolution identifies an expired request."
955
- );
956
- }
957
- const nextPendingRequests = new Map(pendingRequests);
958
- nextPendingRequests.delete(resolution.requestId);
959
- if (activeTurn !== null) {
960
- throwAgentProviderContractError(
961
- providerKey,
962
- "concurrent_turn",
963
- "Provider session already has an active turn."
964
- );
965
- }
966
- const waitingTurnState = waitingTurnStates.get(pending.turnId);
967
- if (waitingTurnState === void 0 || waitingTurnState.terminal || !waitingTurnState.waiting) {
968
- throwAgentProviderContractError(
969
- providerKey,
970
- "request_resolution_mismatch",
971
- "Provider request resolution does not identify a waiting turn."
972
- );
973
- }
974
- waitingTurnState.waiting = false;
975
- const currentTurn = {
976
- turnId: pending.turnId,
977
- pendingRequests: nextPendingRequests,
978
- state: waitingTurnState,
979
- inFlightOperations: /* @__PURE__ */ new Set()
980
- };
981
- let reachedStableBoundary = false;
982
- activeTurn = currentTurn;
983
- try {
984
- for await (const candidate of input.candidate.resolveRequest({
985
- resolution,
986
- ...requestInput.signal === void 0 ? {} : { signal: requestInput.signal }
987
- })) {
988
- const output = validateAgentProviderOutputForContext(
989
- candidate,
990
- outputContext(pending.turnId)
991
- );
801
+ ...turnInput.signal === void 0 ? {} : { signal: turnInput.signal },
802
+ ...turnInput.onProviderExecutionStarted === void 0 ? {} : { onProviderExecutionStarted: turnInput.onProviderExecutionStarted }
803
+ },
804
+ pendingRequests,
805
+ inFlightOperations,
806
+ start: (inputTurn) => input.candidate.startTurn(inputTurn),
807
+ requireUsable,
808
+ isTerminal: () => state.terminal,
809
+ observe(candidate) {
810
+ const output = validateAgentProviderOutputForContext(candidate, outputContext(parsedInput.turnId));
992
811
  observeTurnOutput({
993
812
  capabilities,
994
813
  providerKey,
995
814
  output,
996
- state: currentTurn.state,
997
- pendingRequests: currentTurn.pendingRequests,
815
+ state,
816
+ pendingRequests,
998
817
  openedRequestIds,
818
+ turnRequestIds,
999
819
  contextUsageState,
1000
820
  interactionState
1001
821
  });
1002
- yield output;
1003
- }
1004
- while (currentTurn.inFlightOperations.size > 0) {
1005
- await Promise.all(currentTurn.inFlightOperations);
1006
- }
1007
- assertStableTurnBoundary({
1008
- providerKey,
1009
- state: currentTurn.state,
1010
- pendingRequests: currentTurn.pendingRequests
1011
- });
1012
- replacePendingRequests(pendingRequests, currentTurn.pendingRequests);
1013
- if (currentTurn.state.waiting) {
1014
- waitingTurnStates.set(currentTurn.turnId, currentTurn.state);
1015
- } else {
1016
- waitingTurnStates.delete(currentTurn.turnId);
1017
- }
1018
- reachedStableBoundary = true;
1019
- } finally {
1020
- if (!reachedStableBoundary) unusable = true;
1021
- if (activeTurn === currentTurn) activeTurn = null;
1022
- }
822
+ return output;
823
+ },
824
+ assertCompleted() {
825
+ if (!state.started || !state.terminal || pendingRequests.size > 0) {
826
+ invalidTurnSequence(providerKey, "Provider turn output must remain open until actual terminal completion.");
827
+ }
828
+ },
829
+ release() {
830
+ if (activeTurn?.owner === owner) activeTurn = null;
831
+ },
832
+ retire() {
833
+ unusable = true;
834
+ },
835
+ abandon: () => close({ reason: "other" })
836
+ });
837
+ activeTurn = { turnId: parsedInput.turnId, pendingRequests, state, inFlightOperations, owner };
838
+ return owner.execution;
1023
839
  };
1024
840
  const declaredInterruption = input.candidate.interruption;
1025
841
  const interruption = declaredInterruption.kind === "unsupported" ? Object.freeze({ kind: "unsupported" }) : Object.freeze({
@@ -1033,68 +849,36 @@ function validateAgentProviderSession(input) {
1033
849
  ...interruptionInput.requestedAt === void 0 ? {} : { requestedAt: interruptionInput.requestedAt }
1034
850
  });
1035
851
  const targetedActiveTurn = activeTurn?.turnId === parsed.turnId ? activeTurn : null;
1036
- const targetedWaitingTurn = targetedActiveTurn === null ? reconstructWaitingTurn({
1037
- pendingRequests,
1038
- turnId: parsed.turnId,
1039
- state: waitingTurnStates.get(parsed.turnId)
1040
- }) : null;
1041
- const targetedTurn = targetedActiveTurn ?? targetedWaitingTurn;
1042
- if (targetedTurn === null) {
852
+ const targetedTurn = targetedActiveTurn;
853
+ if (targetedTurn === null || targetedTurn.state.terminal || !targetedTurn.owner.hasEntered()) {
1043
854
  throwAgentProviderContractError(
1044
855
  providerKey,
1045
856
  "active_turn_mismatch",
1046
- "Provider interruption does not identify an active or waiting turn."
857
+ "Provider interruption does not identify an active turn."
1047
858
  );
1048
859
  }
1049
860
  const settleActiveInterruption = trackActiveTurnOperation(
1050
861
  targetedActiveTurn
1051
862
  );
1052
863
  try {
1053
- const result = await delegateOperation(
1054
- () => declaredInterruption.interruptTurn({
1055
- ...parsed,
1056
- ...interruptionInput.signal === void 0 ? {} : { signal: interruptionInput.signal }
1057
- }),
1058
- parsed.turnId
1059
- );
1060
- const terminalized = interruptionTerminalizesTurn({
1061
- turnId: parsed.turnId,
1062
- result
1063
- });
1064
- if (terminalized || result.status === "accepted") {
1065
- clearPendingRequestsForTurn(pendingRequests, parsed.turnId);
1066
- clearPendingRequestsForTurn(
1067
- targetedTurn.pendingRequests,
1068
- parsed.turnId
1069
- );
1070
- targetedTurn.state.waiting = false;
1071
- waitingTurnStates.delete(parsed.turnId);
1072
- }
864
+ const suspendedRequests = targetedTurn.owner.suspendRequests();
865
+ let result;
1073
866
  try {
1074
- observeTurnOperationOutputs({
1075
- capabilities,
1076
- providerKey,
1077
- outputs: result.outputs,
1078
- state: targetedTurn.state,
1079
- pendingRequests: targetedTurn.pendingRequests,
1080
- openedRequestIds,
1081
- contextUsageState,
1082
- interactionState
1083
- });
1084
- if (terminalized && !targetedTurn.state.terminal) {
1085
- completeTurnSequence({
1086
- providerKey,
1087
- state: targetedTurn.state,
1088
- pendingRequests: targetedTurn.pendingRequests
1089
- });
1090
- }
867
+ result = validateAgentProviderTurnInterruptionResult(
868
+ await declaredInterruption.interruptTurn({
869
+ ...parsed,
870
+ ...interruptionInput.signal === void 0 ? {} : { signal: interruptionInput.signal }
871
+ }),
872
+ outputContext(parsed.turnId)
873
+ );
1091
874
  } catch (error) {
875
+ suspendedRequests.withdraw();
1092
876
  unusable = true;
1093
877
  throw error;
1094
878
  }
1095
- if (result.status === "accepted" && targetedWaitingTurn !== null && !terminalized) {
1096
- unusable = true;
1097
- }
879
+ if (result.status === "failed") {
880
+ suspendedRequests.restore();
881
+ } else suspendedRequests.withdraw();
1098
882
  return result;
1099
883
  } finally {
1100
884
  settleActiveInterruption?.();
@@ -1108,7 +892,7 @@ function validateAgentProviderSession(input) {
1108
892
  requireUsable();
1109
893
  throwIfAgentOperationAborted(steeringInput.signal);
1110
894
  const turnId = parseAgentTurnId(steeringInput.turnId);
1111
- if (activeTurn?.turnId !== turnId || activeTurn.state.terminal || activeTurn.state.waiting) {
895
+ if (activeTurn?.turnId !== turnId || !activeTurn.owner.hasEntered() || activeTurn.state.terminal || activeTurn.pendingRequests.size > 0) {
1112
896
  throwAgentProviderContractError(
1113
897
  providerKey,
1114
898
  "active_turn_mismatch",
@@ -1128,7 +912,7 @@ function validateAgentProviderSession(input) {
1128
912
  const settleSteering = trackActiveTurnOperation(targetedActiveTurn);
1129
913
  try {
1130
914
  try {
1131
- return validateAgentTurnSteeringResult(
915
+ const result = validateAgentTurnSteeringResult(
1132
916
  await declaredSteering.steerTurn({
1133
917
  turnId,
1134
918
  ...content,
@@ -1136,6 +920,8 @@ function validateAgentProviderSession(input) {
1136
920
  }),
1137
921
  providerKey
1138
922
  );
923
+ if (result.status === "delivery_uncertain") unusable = true;
924
+ return result;
1139
925
  } catch (error) {
1140
926
  unusable = true;
1141
927
  throw new AgentProviderDelegatedOperationError(
@@ -1287,7 +1073,6 @@ function validateAgentProviderSession(input) {
1287
1073
  generatedResourceIds: /* @__PURE__ */ new Set(),
1288
1074
  started: true,
1289
1075
  terminal: false,
1290
- waiting: false,
1291
1076
  finalDiffObserved: false
1292
1077
  };
1293
1078
  interactionState.operationInvocations.set(
@@ -1617,7 +1402,17 @@ function validateAgentProviderSession(input) {
1617
1402
  }
1618
1403
  unusable = true;
1619
1404
  sessionLifetime.abort(new DOMException("The provider session was closed.", "AbortError"));
1620
- closePromise = Promise.resolve().then(() => input.candidate.close(closeInput)).then(() => {
1405
+ input.hostBindingLease?.stop();
1406
+ const closingTurn = activeTurn;
1407
+ closingTurn?.owner.stop(new DOMException("The provider session was closed.", "AbortError"));
1408
+ closePromise = Promise.resolve().then(async () => {
1409
+ if (!providerClosed) {
1410
+ await input.candidate.close(closeInput);
1411
+ providerClosed = true;
1412
+ }
1413
+ await closingTurn?.owner.join();
1414
+ await input.hostBindingLease?.release();
1415
+ }).then(() => {
1621
1416
  closed = true;
1622
1417
  }).finally(() => {
1623
1418
  closePromise = null;
@@ -1626,8 +1421,7 @@ function validateAgentProviderSession(input) {
1626
1421
  };
1627
1422
  return Object.freeze({
1628
1423
  binding,
1629
- runTurn,
1630
- resolveRequest,
1424
+ startTurn,
1631
1425
  interruption,
1632
1426
  steering,
1633
1427
  configuration,
@@ -1639,11 +1433,37 @@ function validateAgentProviderSession(input) {
1639
1433
  close
1640
1434
  });
1641
1435
  }
1642
- async function closeRejectedAgentProviderSession(session, error) {
1643
- if (!session || typeof session.close !== "function") throw error;
1436
+ async function closeRejectedAgentProviderSession(session, error, hostBinding, opening) {
1437
+ if (!session) throw error;
1438
+ hostBinding?.lease.stop();
1439
+ let providerClosed = false;
1440
+ const cleanup = async () => {
1441
+ if (!providerClosed) {
1442
+ if (typeof session.close !== "function") {
1443
+ throw new TypeError("Rejected provider session has no callable close port.");
1444
+ }
1445
+ await session.close({ reason: "contract_rejected" });
1446
+ providerClosed = true;
1447
+ }
1448
+ await hostBinding?.lease.release();
1449
+ };
1644
1450
  try {
1645
- await session.close({ reason: "contract_rejected" });
1451
+ await cleanup();
1646
1452
  } catch (cleanupError) {
1453
+ if (hostBinding) {
1454
+ const retained = createAgentHostBindingCleanupError({
1455
+ providerKey: hostBinding.providerKey,
1456
+ lease: hostBinding.lease,
1457
+ errors: [error, cleanupError],
1458
+ retryCleanup: cleanup
1459
+ });
1460
+ throw opening ? associateAgentSessionCleanupError(retained, opening.input) : retained;
1461
+ }
1462
+ if (opening) throw associateAgentSessionCleanupError(new AgentProviderSessionCleanupError({
1463
+ providerKey: opening.providerKey,
1464
+ errors: [error, cleanupError],
1465
+ retryCleanup: cleanup
1466
+ }), opening.input);
1647
1467
  throw new AggregateError(
1648
1468
  [error, cleanupError],
1649
1469
  "Provider returned an invalid session and cleanup failed."
@@ -1713,12 +1533,21 @@ async function openIdentityCreatingAgentProviderSession(input) {
1713
1533
  capabilities: input.capabilities,
1714
1534
  sessionId: input.sessionId,
1715
1535
  candidate,
1536
+ ...input.hostBindingLease === void 0 ? {} : { hostBindingLease: input.hostBindingLease },
1716
1537
  ...input.sourceBinding === void 0 ? {} : { sourceBinding: input.sourceBinding }
1717
1538
  });
1718
1539
  tracker.requireMatch(session.binding);
1719
1540
  return session;
1720
1541
  } catch (error) {
1721
- return closeRejectedAgentProviderSession(candidate, error);
1542
+ return closeRejectedAgentProviderSession(
1543
+ candidate,
1544
+ error,
1545
+ input.hostBindingLease === void 0 ? void 0 : {
1546
+ providerKey: input.capabilities.providerKey,
1547
+ lease: input.hostBindingLease
1548
+ },
1549
+ input.openingInput === void 0 ? void 0 : { providerKey: input.capabilities.providerKey, input: input.openingInput }
1550
+ );
1722
1551
  }
1723
1552
  }
1724
1553
  export {