@agen-ai/agent-runtime 0.4.0 → 0.4.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +200 -9
- package/dist/adapterValidation.d.ts +4 -2
- package/dist/adapterValidation.js +140 -51
- package/dist/artifacts.js +0 -1
- package/dist/contractErrors.d.ts +2 -2
- package/dist/contractErrors.js +11 -7
- package/dist/hostBindings/opening.d.ts +2 -0
- package/dist/hostBindings/opening.js +58 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/nativeContinuations/admission.d.ts +17 -0
- package/dist/nativeContinuations/admission.js +250 -0
- package/dist/nativeContinuations/index.d.ts +2 -0
- package/dist/nativeContinuations/index.js +1 -0
- package/dist/nativeContinuations/types.d.ts +52 -0
- package/dist/nativeContinuations/types.js +0 -0
- package/dist/outputValidation.js +3 -2
- package/dist/providerCatalog.d.ts +1 -1
- package/dist/providerDriver.d.ts +4 -1
- package/dist/providerDriver.js +0 -1
- package/dist/providerInstanceRegistry.d.ts +4 -4
- package/dist/providerInstanceRegistry.js +2 -1
- package/dist/sessionAdmission.d.ts +12 -0
- package/dist/sessionAdmission.js +54 -0
- package/dist/sessionInteractions/index.d.ts +2 -0
- package/dist/sessionInteractions/index.js +1 -0
- package/dist/sessionInteractions/lifetime.d.ts +81 -0
- package/dist/sessionInteractions/lifetime.js +383 -0
- package/dist/sessionInteractions/request.d.ts +43 -0
- package/dist/sessionInteractions/request.js +381 -0
- package/dist/sessionInteractions/types.d.ts +110 -0
- package/dist/sessionInteractions/types.js +10 -0
- package/dist/sessionInteractions/validation.d.ts +25 -0
- package/dist/sessionInteractions/validation.js +180 -0
- package/dist/sessionValidation.d.ts +82 -3
- package/dist/sessionValidation.js +240 -181
- package/dist/sessions.d.ts +34 -0
- package/dist/testing/conformance.d.ts +3 -1
- package/dist/testing/conformance.js +180 -86
- package/dist/testing/fakeProvider.js +5 -6
- package/dist/turnExecution/execution.d.ts +23 -6
- package/dist/turnExecution/execution.js +136 -32
- package/dist/turnExecution/requests.d.ts +15 -3
- package/dist/turnExecution/requests.js +93 -28
- package/package.json +2 -2
|
@@ -44,8 +44,15 @@ import {
|
|
|
44
44
|
validateAgentProviderOutputForContext
|
|
45
45
|
} from "./outputValidation.js";
|
|
46
46
|
import { validateAgentTurnSteeringResult } from "./steeringValidation.js";
|
|
47
|
-
import { createValidatedAgentTurnExecution } from "./turnExecution/execution.js";
|
|
47
|
+
import { createValidatedAgentTurnExecution, adoptValidatedAgentTurnExecution } from "./turnExecution/execution.js";
|
|
48
48
|
import { validateAgentProviderTurnInterruptionResult } from "./turnExecution/receipts.js";
|
|
49
|
+
import { createAgentTurnRequestAdmission } from "./turnExecution/requests.js";
|
|
50
|
+
import { createAgentSessionAdmission } from "./sessionAdmission.js";
|
|
51
|
+
function validateCloseReason(reason) {
|
|
52
|
+
if (reason !== void 0 && !["idle", "shutdown", "replaced", "contract_rejected", "error", "other"].includes(reason)) {
|
|
53
|
+
throw new TypeError("Provider session close reason is unsupported.");
|
|
54
|
+
}
|
|
55
|
+
}
|
|
49
56
|
function validateSessionPorts(capabilities, session) {
|
|
50
57
|
if (!hasExactOwnKeys(session, [
|
|
51
58
|
"binding",
|
|
@@ -158,7 +165,7 @@ function invalidTurnSequence(providerKey, message) {
|
|
|
158
165
|
);
|
|
159
166
|
}
|
|
160
167
|
function completeTurnSequence(input) {
|
|
161
|
-
if (input.
|
|
168
|
+
if (input.requests.hasUnclaimedRequest()) {
|
|
162
169
|
invalidTurnSequence(
|
|
163
170
|
input.providerKey,
|
|
164
171
|
"Provider completed a turn while an interaction request remained pending."
|
|
@@ -171,6 +178,7 @@ const FINAL_DIFF_TRAILING_EVENT_TYPES = [
|
|
|
171
178
|
"collaboration.updated",
|
|
172
179
|
"operation.updated",
|
|
173
180
|
"provider.diagnostic",
|
|
181
|
+
"request.unavailable",
|
|
174
182
|
"resource.updated",
|
|
175
183
|
"runtime.error",
|
|
176
184
|
"runtime.warning",
|
|
@@ -221,29 +229,19 @@ function observeItemLifecycle(input) {
|
|
|
221
229
|
terminal: event.type === "item.completed" || TERMINAL_ITEM_STATUSES.has(event.payload.status)
|
|
222
230
|
});
|
|
223
231
|
}
|
|
232
|
+
function hasLiveApprovalSubject(request, state) {
|
|
233
|
+
if (request.requestKind !== "approval") return true;
|
|
234
|
+
if (request.subject.kind === "plan") return state.proposedPlans.get(request.subject.artifactId) === request.requestId;
|
|
235
|
+
const item = state.observedItems.get(request.subject.itemId);
|
|
236
|
+
return item !== void 0 && !item.terminal && (item.status === "pending" || item.status === "in_progress" || item.status === "unknown" && item.inProgressObserved);
|
|
237
|
+
}
|
|
224
238
|
function assertApprovalSubjectCorrelation(input) {
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
if (request.subject.kind === "plan") {
|
|
228
|
-
if (input.state.proposedPlans.get(request.subject.artifactId) !== request.requestId) {
|
|
229
|
-
invalidTurnSequence(
|
|
230
|
-
input.providerKey,
|
|
231
|
-
"Provider approval request does not identify a proposed plan from this turn."
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
return;
|
|
235
|
-
}
|
|
236
|
-
const item = input.state.observedItems.get(request.subject.itemId);
|
|
237
|
-
const itemIsLive = item !== void 0 && !item.terminal && (item.status === "pending" || item.status === "in_progress" || item.status === "unknown" && item.inProgressObserved);
|
|
238
|
-
if (!itemIsLive) {
|
|
239
|
-
invalidTurnSequence(
|
|
240
|
-
input.providerKey,
|
|
241
|
-
"Provider approval request does not identify a live item from this turn."
|
|
242
|
-
);
|
|
239
|
+
if (!hasLiveApprovalSubject(input.request, input.state)) {
|
|
240
|
+
invalidTurnSequence(input.providerKey, input.request.requestKind === "approval" && input.request.subject.kind === "plan" ? "Provider approval request does not identify a proposed plan from this turn." : "Provider approval request does not identify a live item from this turn.");
|
|
243
241
|
}
|
|
244
242
|
}
|
|
245
243
|
function assertPendingApprovalSubjectCorrelations(input) {
|
|
246
|
-
for (const pending of input.
|
|
244
|
+
for (const pending of input.requests.unclaimedRequests()) {
|
|
247
245
|
assertApprovalSubjectCorrelation({
|
|
248
246
|
providerKey: input.providerKey,
|
|
249
247
|
request: pending.request,
|
|
@@ -299,9 +297,7 @@ function observeTurnEvent(input) {
|
|
|
299
297
|
const {
|
|
300
298
|
contextUsageState,
|
|
301
299
|
event,
|
|
302
|
-
|
|
303
|
-
turnRequestIds,
|
|
304
|
-
pendingRequests,
|
|
300
|
+
requests,
|
|
305
301
|
providerKey,
|
|
306
302
|
state
|
|
307
303
|
} = input;
|
|
@@ -327,7 +323,7 @@ function observeTurnEvent(input) {
|
|
|
327
323
|
}
|
|
328
324
|
if (state.fileChangeMode === "final_diff") {
|
|
329
325
|
if (event.type === "turn.diff.updated") {
|
|
330
|
-
if (
|
|
326
|
+
if (requests.hasUnclaimedRequest()) {
|
|
331
327
|
invalidTurnSequence(
|
|
332
328
|
providerKey,
|
|
333
329
|
"Provider emitted a terminal diff while an interaction request remained pending."
|
|
@@ -351,7 +347,7 @@ function observeTurnEvent(input) {
|
|
|
351
347
|
observeItemLifecycle({ providerKey, event, state });
|
|
352
348
|
assertPendingApprovalSubjectCorrelations({
|
|
353
349
|
providerKey,
|
|
354
|
-
|
|
350
|
+
requests,
|
|
355
351
|
state
|
|
356
352
|
});
|
|
357
353
|
if (event.payload.itemKind === "context_compaction" && event.type === "item.completed" && event.payload.status === "completed") {
|
|
@@ -364,7 +360,7 @@ function observeTurnEvent(input) {
|
|
|
364
360
|
state.proposedPlans.set(event.payload.artifactId, event.payload.requestId);
|
|
365
361
|
assertPendingApprovalSubjectCorrelations({
|
|
366
362
|
providerKey,
|
|
367
|
-
|
|
363
|
+
requests,
|
|
368
364
|
state
|
|
369
365
|
});
|
|
370
366
|
}
|
|
@@ -376,34 +372,20 @@ function observeTurnEvent(input) {
|
|
|
376
372
|
});
|
|
377
373
|
}
|
|
378
374
|
if (event.type === "request.opened") {
|
|
379
|
-
if (pendingRequests.size > 0) {
|
|
380
|
-
invalidTurnSequence(
|
|
381
|
-
providerKey,
|
|
382
|
-
"Provider opened more than one pending request for a session."
|
|
383
|
-
);
|
|
384
|
-
}
|
|
385
|
-
const requestId = event.payload.request.requestId;
|
|
386
|
-
if (openedRequestIds.has(requestId)) {
|
|
387
|
-
invalidTurnSequence(
|
|
388
|
-
providerKey,
|
|
389
|
-
"Provider reused a request ID within a session."
|
|
390
|
-
);
|
|
391
|
-
}
|
|
392
375
|
assertApprovalSubjectCorrelation({
|
|
393
376
|
providerKey,
|
|
394
377
|
request: event.payload.request,
|
|
395
378
|
state
|
|
396
379
|
});
|
|
397
|
-
|
|
398
|
-
turnRequestIds.add(requestId);
|
|
399
|
-
pendingRequests.set(requestId, {
|
|
380
|
+
requests.open({
|
|
400
381
|
request: event.payload.request,
|
|
401
382
|
turnId: event.turnId
|
|
402
383
|
});
|
|
403
384
|
}
|
|
385
|
+
if (event.type === "request.unavailable") requests.unavailable(event.payload.requestId);
|
|
404
386
|
if (event.type === "turn.state_changed") {
|
|
405
387
|
const waiting = event.payload.state === "waiting_for_request";
|
|
406
|
-
if (waiting && !
|
|
388
|
+
if (waiting && !requests.hasOpened(event.payload.requestId)) {
|
|
407
389
|
invalidTurnSequence(
|
|
408
390
|
providerKey,
|
|
409
391
|
"Provider entered a waiting state for a request it did not open."
|
|
@@ -411,8 +393,8 @@ function observeTurnEvent(input) {
|
|
|
411
393
|
}
|
|
412
394
|
}
|
|
413
395
|
if (event.type === "turn.completed") {
|
|
414
|
-
if (event.payload.outcome !== "completed")
|
|
415
|
-
completeTurnSequence({ providerKey, state,
|
|
396
|
+
if (event.payload.outcome !== "completed") requests.withdraw();
|
|
397
|
+
completeTurnSequence({ providerKey, state, requests });
|
|
416
398
|
}
|
|
417
399
|
}
|
|
418
400
|
function observeTurnOutput(input) {
|
|
@@ -503,8 +485,7 @@ function observeTurnOutput(input) {
|
|
|
503
485
|
event: input.output.event,
|
|
504
486
|
state: input.state,
|
|
505
487
|
pendingRequests: input.pendingRequests,
|
|
506
|
-
|
|
507
|
-
turnRequestIds: input.turnRequestIds,
|
|
488
|
+
requests: input.requests,
|
|
508
489
|
contextUsageState: input.contextUsageState
|
|
509
490
|
});
|
|
510
491
|
}
|
|
@@ -536,14 +517,20 @@ function observeSessionOperationOutput(input) {
|
|
|
536
517
|
);
|
|
537
518
|
}
|
|
538
519
|
}
|
|
520
|
+
const pendingRequests = /* @__PURE__ */ new Map();
|
|
539
521
|
observeTurnOutput({
|
|
540
522
|
capabilities: input.capabilities,
|
|
541
523
|
providerKey: input.providerKey,
|
|
542
524
|
output: input.output,
|
|
543
525
|
state: input.state,
|
|
544
|
-
pendingRequests
|
|
545
|
-
|
|
546
|
-
|
|
526
|
+
pendingRequests,
|
|
527
|
+
requests: createAgentTurnRequestAdmission({
|
|
528
|
+
providerKey: input.providerKey,
|
|
529
|
+
sessionAdmission: createAgentSessionAdmission(input.providerKey),
|
|
530
|
+
pendingRequests,
|
|
531
|
+
canRestore: () => false,
|
|
532
|
+
canRestoreRequest: () => false
|
|
533
|
+
}),
|
|
547
534
|
contextUsageState: input.contextUsageState,
|
|
548
535
|
interactionState: input.interactionState
|
|
549
536
|
});
|
|
@@ -632,6 +619,146 @@ function assertAgentTurnInputCapability(input) {
|
|
|
632
619
|
});
|
|
633
620
|
if (textIssue) rejectReference(`${textIssue.target} must use ${textIssue.format} text`);
|
|
634
621
|
}
|
|
622
|
+
function createAgentSessionValidationOwner(capabilities, sessionId) {
|
|
623
|
+
const providerKey = capabilities.providerKey;
|
|
624
|
+
const sessionLifetime = new AbortController();
|
|
625
|
+
const sessionAdmission = createAgentSessionAdmission(providerKey);
|
|
626
|
+
const contextUsageState = {
|
|
627
|
+
latestByScope: /* @__PURE__ */ new Map(),
|
|
628
|
+
cumulativeByScope: /* @__PURE__ */ new Map(),
|
|
629
|
+
compactionReadyScopes: /* @__PURE__ */ new Set()
|
|
630
|
+
};
|
|
631
|
+
const interactionState = {
|
|
632
|
+
operationResults: /* @__PURE__ */ new Map(),
|
|
633
|
+
operationInvocations: /* @__PURE__ */ new Map(),
|
|
634
|
+
collaborationNodes: /* @__PURE__ */ new Map(),
|
|
635
|
+
collaborationSpawnReservations: /* @__PURE__ */ new Map(),
|
|
636
|
+
generatedResources: /* @__PURE__ */ new Map()
|
|
637
|
+
};
|
|
638
|
+
let closed = false;
|
|
639
|
+
let unusable = false;
|
|
640
|
+
let closing;
|
|
641
|
+
const requireOpen = () => {
|
|
642
|
+
if (closed)
|
|
643
|
+
throwAgentProviderContractError(
|
|
644
|
+
providerKey,
|
|
645
|
+
"session_closed",
|
|
646
|
+
"Provider session is closed."
|
|
647
|
+
);
|
|
648
|
+
};
|
|
649
|
+
const requireUsable = () => {
|
|
650
|
+
requireOpen();
|
|
651
|
+
if (unusable) {
|
|
652
|
+
throwAgentProviderContractError(
|
|
653
|
+
providerKey,
|
|
654
|
+
"session_unusable",
|
|
655
|
+
"Provider session cannot be reused after an incomplete operation."
|
|
656
|
+
);
|
|
657
|
+
}
|
|
658
|
+
};
|
|
659
|
+
const outputContext = (turnId) => ({
|
|
660
|
+
capabilities,
|
|
661
|
+
providerKey,
|
|
662
|
+
sessionId,
|
|
663
|
+
...turnId === void 0 ? {} : { turnId }
|
|
664
|
+
});
|
|
665
|
+
function installExecution(token, turnId, source) {
|
|
666
|
+
const pendingRequests = /* @__PURE__ */ new Map();
|
|
667
|
+
const state = {
|
|
668
|
+
fileChangeMode: capabilities.output.fileChanges,
|
|
669
|
+
observedItems: /* @__PURE__ */ new Map(),
|
|
670
|
+
proposedPlans: /* @__PURE__ */ new Map(),
|
|
671
|
+
generatedResourceIds: /* @__PURE__ */ new Set(),
|
|
672
|
+
started: false,
|
|
673
|
+
terminal: false,
|
|
674
|
+
finalDiffObserved: false
|
|
675
|
+
};
|
|
676
|
+
const inFlightOperations = /* @__PURE__ */ new Set();
|
|
677
|
+
const common = {
|
|
678
|
+
providerKey,
|
|
679
|
+
pendingRequests,
|
|
680
|
+
sessionAdmission,
|
|
681
|
+
inFlightOperations,
|
|
682
|
+
requireUsable,
|
|
683
|
+
isTerminal: () => state.terminal,
|
|
684
|
+
canRestoreRequest: (pending) => hasLiveApprovalSubject(pending.request, state),
|
|
685
|
+
observe(candidate, requests) {
|
|
686
|
+
const output = validateAgentProviderOutputForContext(candidate, outputContext(turnId));
|
|
687
|
+
observeTurnOutput({
|
|
688
|
+
capabilities,
|
|
689
|
+
providerKey,
|
|
690
|
+
output,
|
|
691
|
+
state,
|
|
692
|
+
pendingRequests,
|
|
693
|
+
requests,
|
|
694
|
+
contextUsageState,
|
|
695
|
+
interactionState
|
|
696
|
+
});
|
|
697
|
+
return output;
|
|
698
|
+
},
|
|
699
|
+
assertCompleted() {
|
|
700
|
+
if (!state.started || !state.terminal || pendingRequests.size > 0) {
|
|
701
|
+
invalidTurnSequence(providerKey, "Provider turn output must remain open until actual terminal completion.");
|
|
702
|
+
}
|
|
703
|
+
},
|
|
704
|
+
release() {
|
|
705
|
+
if (source.kind === "caller") sessionAdmission.releaseExecution(token);
|
|
706
|
+
},
|
|
707
|
+
retire() {
|
|
708
|
+
unusable = true;
|
|
709
|
+
},
|
|
710
|
+
abandon: () => closing ? closing() : Promise.reject(new Error("Session cleanup is waiting for its opening owner."))
|
|
711
|
+
};
|
|
712
|
+
const owner = source.kind === "caller" ? createValidatedAgentTurnExecution({ ...common, turnInput: source.input, start: source.start }) : adoptValidatedAgentTurnExecution({ ...common, turnId, createCandidate: source.createCandidate });
|
|
713
|
+
try {
|
|
714
|
+
sessionAdmission.installExecution(token, { turnId, pendingRequests, state, inFlightOperations, owner });
|
|
715
|
+
} catch (cause) {
|
|
716
|
+
owner.stop(cause instanceof Error ? cause : new Error("Execution installation failed.", { cause }));
|
|
717
|
+
throw cause;
|
|
718
|
+
}
|
|
719
|
+
return owner;
|
|
720
|
+
}
|
|
721
|
+
return {
|
|
722
|
+
sessionAdmission,
|
|
723
|
+
sessionLifetime,
|
|
724
|
+
contextUsageState,
|
|
725
|
+
interactionState,
|
|
726
|
+
outputContext,
|
|
727
|
+
requireUsable,
|
|
728
|
+
get activeTurn() {
|
|
729
|
+
return sessionAdmission.execution;
|
|
730
|
+
},
|
|
731
|
+
get closed() {
|
|
732
|
+
return closed;
|
|
733
|
+
},
|
|
734
|
+
get unusable() {
|
|
735
|
+
return unusable;
|
|
736
|
+
},
|
|
737
|
+
fence() {
|
|
738
|
+
unusable = true;
|
|
739
|
+
},
|
|
740
|
+
stop() {
|
|
741
|
+
unusable = true;
|
|
742
|
+
sessionAdmission.stop();
|
|
743
|
+
sessionLifetime.abort(new DOMException("The provider session was closed.", "AbortError"));
|
|
744
|
+
},
|
|
745
|
+
completeClose() {
|
|
746
|
+
closed = true;
|
|
747
|
+
},
|
|
748
|
+
installClose(close) {
|
|
749
|
+
closing = close;
|
|
750
|
+
},
|
|
751
|
+
reserveExecution: sessionAdmission.reserveExecution,
|
|
752
|
+
ownsExecution: sessionAdmission.ownsExecution,
|
|
753
|
+
releaseExecution: sessionAdmission.releaseExecution,
|
|
754
|
+
installNativeExecution(token, turnId, createCandidate) {
|
|
755
|
+
return installExecution(token, turnId, { kind: "native", createCandidate });
|
|
756
|
+
},
|
|
757
|
+
installCallerExecution(token, turnInput, start) {
|
|
758
|
+
return installExecution(token, turnInput.turnId, { kind: "caller", input: turnInput, start });
|
|
759
|
+
}
|
|
760
|
+
};
|
|
761
|
+
}
|
|
635
762
|
function validateAgentProviderSession(input) {
|
|
636
763
|
const { capabilities, sessionId } = input;
|
|
637
764
|
const providerKey = capabilities.providerKey;
|
|
@@ -667,57 +794,22 @@ function validateAgentProviderSession(input) {
|
|
|
667
794
|
);
|
|
668
795
|
}
|
|
669
796
|
validateSessionPorts(capabilities, input.candidate);
|
|
670
|
-
const
|
|
797
|
+
const validationOwner = input.validationOwner ?? createAgentSessionValidationOwner(capabilities, sessionId);
|
|
798
|
+
const { sessionLifetime, contextUsageState, interactionState, outputContext } = validationOwner;
|
|
799
|
+
const collaborationNodes = interactionState.collaborationNodes;
|
|
800
|
+
const collaborationSpawnReservations = interactionState.collaborationSpawnReservations;
|
|
801
|
+
const requireUsable = () => {
|
|
802
|
+
input.sessionInteractionOwner?.requireUsable();
|
|
803
|
+
validationOwner.requireUsable();
|
|
804
|
+
};
|
|
671
805
|
const environment = validateAgentEnvironmentObservationPort({
|
|
672
806
|
providerKey,
|
|
673
807
|
capability: capabilities.environment.session,
|
|
674
808
|
port: input.candidate.environment,
|
|
675
809
|
signal: sessionLifetime.signal
|
|
676
810
|
});
|
|
677
|
-
const openedRequestIds = /* @__PURE__ */ new Set();
|
|
678
|
-
const contextUsageState = {
|
|
679
|
-
latestByScope: /* @__PURE__ */ new Map(),
|
|
680
|
-
cumulativeByScope: /* @__PURE__ */ new Map(),
|
|
681
|
-
compactionReadyScopes: /* @__PURE__ */ new Set()
|
|
682
|
-
};
|
|
683
|
-
const interactionState = {
|
|
684
|
-
operationResults: /* @__PURE__ */ new Map(),
|
|
685
|
-
operationInvocations: /* @__PURE__ */ new Map(),
|
|
686
|
-
collaborationNodes: /* @__PURE__ */ new Map(),
|
|
687
|
-
collaborationSpawnReservations: /* @__PURE__ */ new Map(),
|
|
688
|
-
generatedResources: /* @__PURE__ */ new Map()
|
|
689
|
-
};
|
|
690
|
-
const collaborationNodes = interactionState.collaborationNodes;
|
|
691
|
-
const collaborationSpawnReservations = interactionState.collaborationSpawnReservations;
|
|
692
|
-
let activeTurn = null;
|
|
693
811
|
let closePromise = null;
|
|
694
|
-
let closed = false;
|
|
695
812
|
let providerClosed = false;
|
|
696
|
-
let unusable = false;
|
|
697
|
-
const requireOpen = () => {
|
|
698
|
-
if (closed)
|
|
699
|
-
throwAgentProviderContractError(
|
|
700
|
-
providerKey,
|
|
701
|
-
"session_closed",
|
|
702
|
-
"Provider session is closed."
|
|
703
|
-
);
|
|
704
|
-
};
|
|
705
|
-
const requireUsable = () => {
|
|
706
|
-
requireOpen();
|
|
707
|
-
if (unusable) {
|
|
708
|
-
throwAgentProviderContractError(
|
|
709
|
-
providerKey,
|
|
710
|
-
"session_unusable",
|
|
711
|
-
"Provider session cannot be reused after an incomplete operation."
|
|
712
|
-
);
|
|
713
|
-
}
|
|
714
|
-
};
|
|
715
|
-
const outputContext = (turnId) => ({
|
|
716
|
-
capabilities,
|
|
717
|
-
providerKey,
|
|
718
|
-
sessionId,
|
|
719
|
-
...turnId === void 0 ? {} : { turnId }
|
|
720
|
-
});
|
|
721
813
|
const delegateProviderMutation = async (input2) => {
|
|
722
814
|
let executionStarted = false;
|
|
723
815
|
const onProviderExecutionStarted = () => {
|
|
@@ -742,6 +834,7 @@ function validateAgentProviderSession(input) {
|
|
|
742
834
|
await input2.observeOutput?.(candidate);
|
|
743
835
|
};
|
|
744
836
|
try {
|
|
837
|
+
requireUsable();
|
|
745
838
|
const candidate = await input2.invoke(onProviderExecutionStarted, onOutput);
|
|
746
839
|
if (!executionStarted) {
|
|
747
840
|
throwAgentProviderContractError(
|
|
@@ -753,7 +846,7 @@ function validateAgentProviderSession(input) {
|
|
|
753
846
|
return input2.validate(candidate);
|
|
754
847
|
} catch (error) {
|
|
755
848
|
if (!executionStarted) throw error;
|
|
756
|
-
|
|
849
|
+
validationOwner.fence();
|
|
757
850
|
throw new AgentProviderDelegatedOperationError(
|
|
758
851
|
providerKey,
|
|
759
852
|
input2.operation,
|
|
@@ -779,63 +872,17 @@ function validateAgentProviderSession(input) {
|
|
|
779
872
|
`Provider ${providerKey} does not support ${parsedInput.interactionMode} turn interaction mode.`
|
|
780
873
|
);
|
|
781
874
|
}
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
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
|
|
795
|
-
};
|
|
796
|
-
const inFlightOperations = /* @__PURE__ */ new Set();
|
|
797
|
-
const owner = createValidatedAgentTurnExecution({
|
|
798
|
-
providerKey,
|
|
799
|
-
turnInput: {
|
|
875
|
+
const token = validationOwner.reserveExecution();
|
|
876
|
+
try {
|
|
877
|
+
return validationOwner.installCallerExecution(token, {
|
|
800
878
|
...parsedInput,
|
|
801
879
|
...turnInput.signal === void 0 ? {} : { signal: turnInput.signal },
|
|
802
880
|
...turnInput.onProviderExecutionStarted === void 0 ? {} : { onProviderExecutionStarted: turnInput.onProviderExecutionStarted }
|
|
803
|
-
},
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
isTerminal: () => state.terminal,
|
|
809
|
-
observe(candidate) {
|
|
810
|
-
const output = validateAgentProviderOutputForContext(candidate, outputContext(parsedInput.turnId));
|
|
811
|
-
observeTurnOutput({
|
|
812
|
-
capabilities,
|
|
813
|
-
providerKey,
|
|
814
|
-
output,
|
|
815
|
-
state,
|
|
816
|
-
pendingRequests,
|
|
817
|
-
openedRequestIds,
|
|
818
|
-
turnRequestIds,
|
|
819
|
-
contextUsageState,
|
|
820
|
-
interactionState
|
|
821
|
-
});
|
|
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;
|
|
881
|
+
}, (inputTurn) => input.candidate.startTurn(inputTurn)).execution;
|
|
882
|
+
} catch (error) {
|
|
883
|
+
validationOwner.releaseExecution(token);
|
|
884
|
+
throw error;
|
|
885
|
+
}
|
|
839
886
|
};
|
|
840
887
|
const declaredInterruption = input.candidate.interruption;
|
|
841
888
|
const interruption = declaredInterruption.kind === "unsupported" ? Object.freeze({ kind: "unsupported" }) : Object.freeze({
|
|
@@ -848,7 +895,7 @@ function validateAgentProviderSession(input) {
|
|
|
848
895
|
reason: interruptionInput.reason,
|
|
849
896
|
...interruptionInput.requestedAt === void 0 ? {} : { requestedAt: interruptionInput.requestedAt }
|
|
850
897
|
});
|
|
851
|
-
const targetedActiveTurn = activeTurn?.turnId === parsed.turnId ? activeTurn : null;
|
|
898
|
+
const targetedActiveTurn = validationOwner.activeTurn?.turnId === parsed.turnId ? validationOwner.activeTurn : null;
|
|
852
899
|
const targetedTurn = targetedActiveTurn;
|
|
853
900
|
if (targetedTurn === null || targetedTurn.state.terminal || !targetedTurn.owner.hasEntered()) {
|
|
854
901
|
throwAgentProviderContractError(
|
|
@@ -873,7 +920,7 @@ function validateAgentProviderSession(input) {
|
|
|
873
920
|
);
|
|
874
921
|
} catch (error) {
|
|
875
922
|
suspendedRequests.withdraw();
|
|
876
|
-
|
|
923
|
+
validationOwner.fence();
|
|
877
924
|
throw error;
|
|
878
925
|
}
|
|
879
926
|
if (result.status === "failed") {
|
|
@@ -892,7 +939,7 @@ function validateAgentProviderSession(input) {
|
|
|
892
939
|
requireUsable();
|
|
893
940
|
throwIfAgentOperationAborted(steeringInput.signal);
|
|
894
941
|
const turnId = parseAgentTurnId(steeringInput.turnId);
|
|
895
|
-
if (activeTurn?.turnId !== turnId || !activeTurn.owner.hasEntered() || activeTurn.state.terminal || activeTurn.pendingRequests.size > 0) {
|
|
942
|
+
if (validationOwner.activeTurn?.turnId !== turnId || !validationOwner.activeTurn.owner.hasEntered() || validationOwner.activeTurn.state.terminal || validationOwner.activeTurn.pendingRequests.size > 0) {
|
|
896
943
|
throwAgentProviderContractError(
|
|
897
944
|
providerKey,
|
|
898
945
|
"active_turn_mismatch",
|
|
@@ -908,7 +955,7 @@ function validateAgentProviderSession(input) {
|
|
|
908
955
|
parts: content.parts,
|
|
909
956
|
providerKey
|
|
910
957
|
});
|
|
911
|
-
const targetedActiveTurn = activeTurn;
|
|
958
|
+
const targetedActiveTurn = validationOwner.activeTurn;
|
|
912
959
|
const settleSteering = trackActiveTurnOperation(targetedActiveTurn);
|
|
913
960
|
try {
|
|
914
961
|
try {
|
|
@@ -920,10 +967,10 @@ function validateAgentProviderSession(input) {
|
|
|
920
967
|
}),
|
|
921
968
|
providerKey
|
|
922
969
|
);
|
|
923
|
-
if (result.status === "delivery_uncertain")
|
|
970
|
+
if (result.status === "delivery_uncertain") validationOwner.fence();
|
|
924
971
|
return result;
|
|
925
972
|
} catch (error) {
|
|
926
|
-
|
|
973
|
+
validationOwner.fence();
|
|
927
974
|
throw new AgentProviderDelegatedOperationError(
|
|
928
975
|
providerKey,
|
|
929
976
|
"steer_turn",
|
|
@@ -1386,39 +1433,35 @@ function validateAgentProviderSession(input) {
|
|
|
1386
1433
|
return inspection;
|
|
1387
1434
|
}
|
|
1388
1435
|
});
|
|
1389
|
-
const
|
|
1390
|
-
if (closed) return Promise.resolve();
|
|
1391
|
-
if (closePromise) return closePromise;
|
|
1436
|
+
const closeOwned = (closeInput, explicit) => {
|
|
1437
|
+
if (validationOwner.closed && !input.sessionInteractionOwner) return Promise.resolve();
|
|
1438
|
+
if (!input.sessionInteractionOwner && closePromise) return closePromise;
|
|
1392
1439
|
throwIfAgentOperationAborted(closeInput.signal);
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
"replaced",
|
|
1397
|
-
"contract_rejected",
|
|
1398
|
-
"error",
|
|
1399
|
-
"other"
|
|
1400
|
-
].includes(closeInput.reason)) {
|
|
1401
|
-
throw new TypeError("Provider session close reason is unsupported.");
|
|
1402
|
-
}
|
|
1403
|
-
unusable = true;
|
|
1404
|
-
sessionLifetime.abort(new DOMException("The provider session was closed.", "AbortError"));
|
|
1440
|
+
validateCloseReason(closeInput.reason);
|
|
1441
|
+
validationOwner.fence();
|
|
1442
|
+
validationOwner.stop();
|
|
1405
1443
|
input.hostBindingLease?.stop();
|
|
1406
|
-
const closingTurn = activeTurn;
|
|
1444
|
+
const closingTurn = validationOwner.activeTurn;
|
|
1407
1445
|
closingTurn?.owner.stop(new DOMException("The provider session was closed.", "AbortError"));
|
|
1408
|
-
|
|
1446
|
+
const cleanup = async () => {
|
|
1409
1447
|
if (!providerClosed) {
|
|
1410
1448
|
await input.candidate.close(closeInput);
|
|
1411
1449
|
providerClosed = true;
|
|
1412
1450
|
}
|
|
1451
|
+
await input.sessionInteractionOwner?.join();
|
|
1413
1452
|
await closingTurn?.owner.join();
|
|
1414
1453
|
await input.hostBindingLease?.release();
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1454
|
+
validationOwner.completeClose();
|
|
1455
|
+
};
|
|
1456
|
+
if (input.sessionInteractionOwner) return input.sessionInteractionOwner.close(cleanup, explicit);
|
|
1457
|
+
closePromise = Promise.resolve().then(cleanup).finally(() => {
|
|
1418
1458
|
closePromise = null;
|
|
1419
1459
|
});
|
|
1420
1460
|
return closePromise;
|
|
1421
1461
|
};
|
|
1462
|
+
const close = (closeInput) => closeOwned(closeInput, true);
|
|
1463
|
+
validationOwner.installClose(() => closeOwned({ reason: "error" }, false));
|
|
1464
|
+
input.sessionInteractionOwner?.installClose(() => closeOwned({ reason: "error" }, false));
|
|
1422
1465
|
return Object.freeze({
|
|
1423
1466
|
binding,
|
|
1424
1467
|
startTurn,
|
|
@@ -1433,9 +1476,10 @@ function validateAgentProviderSession(input) {
|
|
|
1433
1476
|
close
|
|
1434
1477
|
});
|
|
1435
1478
|
}
|
|
1436
|
-
async function closeRejectedAgentProviderSession(session, error, hostBinding, opening) {
|
|
1479
|
+
async function closeRejectedAgentProviderSession(session, error, hostBinding, opening, interactions) {
|
|
1437
1480
|
if (!session) throw error;
|
|
1438
1481
|
hostBinding?.lease.stop();
|
|
1482
|
+
interactions?.stop();
|
|
1439
1483
|
let providerClosed = false;
|
|
1440
1484
|
const cleanup = async () => {
|
|
1441
1485
|
if (!providerClosed) {
|
|
@@ -1445,24 +1489,26 @@ async function closeRejectedAgentProviderSession(session, error, hostBinding, op
|
|
|
1445
1489
|
await session.close({ reason: "contract_rejected" });
|
|
1446
1490
|
providerClosed = true;
|
|
1447
1491
|
}
|
|
1492
|
+
await interactions?.join();
|
|
1448
1493
|
await hostBinding?.lease.release();
|
|
1449
1494
|
};
|
|
1450
1495
|
try {
|
|
1451
|
-
await cleanup
|
|
1496
|
+
if (interactions) await interactions.close(cleanup, false);
|
|
1497
|
+
else await cleanup();
|
|
1452
1498
|
} catch (cleanupError) {
|
|
1453
1499
|
if (hostBinding) {
|
|
1454
1500
|
const retained = createAgentHostBindingCleanupError({
|
|
1455
1501
|
providerKey: hostBinding.providerKey,
|
|
1456
1502
|
lease: hostBinding.lease,
|
|
1457
1503
|
errors: [error, cleanupError],
|
|
1458
|
-
retryCleanup: cleanup
|
|
1504
|
+
retryCleanup: () => interactions ? interactions.close(cleanup, false) : cleanup()
|
|
1459
1505
|
});
|
|
1460
1506
|
throw opening ? associateAgentSessionCleanupError(retained, opening.input) : retained;
|
|
1461
1507
|
}
|
|
1462
1508
|
if (opening) throw associateAgentSessionCleanupError(new AgentProviderSessionCleanupError({
|
|
1463
1509
|
providerKey: opening.providerKey,
|
|
1464
1510
|
errors: [error, cleanupError],
|
|
1465
|
-
retryCleanup: cleanup
|
|
1511
|
+
retryCleanup: () => interactions ? interactions.close(cleanup, false) : cleanup()
|
|
1466
1512
|
}), opening.input);
|
|
1467
1513
|
throw new AggregateError(
|
|
1468
1514
|
[error, cleanupError],
|
|
@@ -1499,8 +1545,14 @@ function bindingTracker(input) {
|
|
|
1499
1545
|
"Provider branch reused its source binding."
|
|
1500
1546
|
);
|
|
1501
1547
|
}
|
|
1502
|
-
input.observer(binding);
|
|
1503
1548
|
observed = binding;
|
|
1549
|
+
try {
|
|
1550
|
+
input.interactions?.bind();
|
|
1551
|
+
input.observer(binding);
|
|
1552
|
+
} catch (cause) {
|
|
1553
|
+
input.interactions?.fail(cause);
|
|
1554
|
+
throw cause;
|
|
1555
|
+
}
|
|
1504
1556
|
},
|
|
1505
1557
|
requireMatch(binding) {
|
|
1506
1558
|
if (!observed) {
|
|
@@ -1524,19 +1576,24 @@ async function openIdentityCreatingAgentProviderSession(input) {
|
|
|
1524
1576
|
const tracker = bindingTracker({
|
|
1525
1577
|
providerKey: input.capabilities.providerKey,
|
|
1526
1578
|
observer: input.observer,
|
|
1579
|
+
...input.sessionInteractionOwner === void 0 ? {} : { interactions: input.sessionInteractionOwner },
|
|
1527
1580
|
...input.sourceBinding === void 0 ? {} : { sourceBinding: input.sourceBinding }
|
|
1528
1581
|
});
|
|
1529
1582
|
let candidate = null;
|
|
1530
1583
|
try {
|
|
1531
1584
|
candidate = await input.open(tracker.observe);
|
|
1585
|
+
tracker.requireMatch(parseAgentSessionBinding(candidate.binding));
|
|
1532
1586
|
const session = validateAgentProviderSession({
|
|
1533
1587
|
capabilities: input.capabilities,
|
|
1534
1588
|
sessionId: input.sessionId,
|
|
1535
1589
|
candidate,
|
|
1590
|
+
...input.validationOwner === void 0 ? {} : { validationOwner: input.validationOwner },
|
|
1591
|
+
...input.sessionInteractionOwner === void 0 ? {} : { sessionInteractionOwner: input.sessionInteractionOwner },
|
|
1536
1592
|
...input.hostBindingLease === void 0 ? {} : { hostBindingLease: input.hostBindingLease },
|
|
1537
1593
|
...input.sourceBinding === void 0 ? {} : { sourceBinding: input.sourceBinding }
|
|
1538
1594
|
});
|
|
1539
1595
|
tracker.requireMatch(session.binding);
|
|
1596
|
+
input.sessionInteractionOwner?.adopt();
|
|
1540
1597
|
return session;
|
|
1541
1598
|
} catch (error) {
|
|
1542
1599
|
return closeRejectedAgentProviderSession(
|
|
@@ -1546,12 +1603,14 @@ async function openIdentityCreatingAgentProviderSession(input) {
|
|
|
1546
1603
|
providerKey: input.capabilities.providerKey,
|
|
1547
1604
|
lease: input.hostBindingLease
|
|
1548
1605
|
},
|
|
1549
|
-
input.openingInput === void 0 ? void 0 : { providerKey: input.capabilities.providerKey, input: input.openingInput }
|
|
1606
|
+
input.openingInput === void 0 ? void 0 : { providerKey: input.capabilities.providerKey, input: input.openingInput },
|
|
1607
|
+
input.sessionInteractionOwner
|
|
1550
1608
|
);
|
|
1551
1609
|
}
|
|
1552
1610
|
}
|
|
1553
1611
|
export {
|
|
1554
1612
|
closeRejectedAgentProviderSession,
|
|
1613
|
+
createAgentSessionValidationOwner,
|
|
1555
1614
|
openIdentityCreatingAgentProviderSession,
|
|
1556
1615
|
validateAgentProviderSession
|
|
1557
1616
|
};
|