@zq-silk/yui 0.13.4 → 0.13.5
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 +8 -8
- package/dist/cli/commandCatalog.js +22 -21
- package/dist/cli/interactionPolicy.js +0 -14
- package/dist/cli.js +42 -0
- package/dist/commands/executionAuditCommands.js +1 -1
- package/dist/commands/taskCommands.js +60 -326
- package/dist/commands/taskContextCommand.js +3 -14
- package/dist/commands/taskExecutionCommands.js +254 -0
- package/dist/commands/taskNextActionCommand.js +1 -3
- package/dist/commands/taskOverviewCommand.js +9 -2
- package/dist/commands/taskRoleRuntimeStatus.js +2 -25
- package/dist/controller/agentRuntimeObserver.js +4 -2
- package/dist/controller/clientRuntime.js +45 -2
- package/dist/controller/controller.js +6 -3
- package/dist/controller/fileSchedulerStoreAdapter.js +59 -188
- package/dist/controller/jobControl.js +3 -2
- package/dist/controller/runtime.js +6 -33
- package/dist/controller/runtimeEventProcessor.js +8 -4
- package/dist/controller/runtimeHookRunFence.js +4 -10
- package/dist/execution/executionHealth.js +8 -16
- package/dist/executor/agentExecutor.js +13 -14
- package/dist/executor/fileRoleLaunchPlanner.js +9 -16
- package/dist/lifecycle/exactRunTerminalization.js +24 -322
- package/dist/repository/taskWorkspaceCoordinator.js +0 -9
- package/dist/runtime/agentHost.js +20 -80
- package/dist/runtime/exactControlPlane.js +15 -9
- package/dist/runtime/providerContinuationReconciliationService.js +1 -1
- package/dist/runtime/providerRecoveryDecision.js +1 -1
- package/dist/runtime/providerRuntimeIdentity.js +25 -15
- package/dist/scheduler/activeRoleRunDelivery.js +1 -19
- package/dist/scheduler/leaderWakeupProcessor.js +12 -63
- package/dist/scheduler/ports.js +3 -2
- package/dist/scheduler/roleRunLiveness.js +4 -1
- package/dist/scheduler/roleRunStall.js +0 -2
- package/dist/scheduler/taskExecutionProjection.js +18 -1
- package/dist/scheduler/wakeupQueue.js +2 -1
- package/dist/storage/migration/productionRegistry.js +65 -0
- package/dist/storage/sqliteStore.js +10 -2
- package/dist/storage/taskStore.js +11 -3
- package/dist/task/completionReadiness.js +0 -67
- package/dist/task/nextAction.js +16 -32
- package/dist/task/task.js +38 -3
- package/dist/web/assets/client/i18n.js +0 -4
- package/dist/web/assets/client/view.js +0 -18
- package/dist/web/webSnapshot.js +7 -13
- package/package.json +1 -1
- package/dist/run/recoveryProjection.js +0 -252
- package/dist/runtime/conversationSwitch.js +0 -277
|
@@ -1,8 +1,7 @@
|
|
|
1
1
|
import { createHash } from "node:crypto";
|
|
2
2
|
import { isDeepStrictEqual } from "node:util";
|
|
3
|
-
import { activeLiveRoleAgentSession, bindTaskRoleProviderRuntime, bindTaskRoleRun, clearTaskRoleRun, createRoleSessionSet, markTaskRoleRunDelivered, markTaskRoleRunPushed, prepareTaskRoleRunRedispatch, recordRoleAgentSession,
|
|
3
|
+
import { activeLiveRoleAgentSession, bindTaskRoleProviderRuntime, bindTaskRoleRun, clearTaskRoleRun, createRoleSessionSet, markTaskRoleRunDelivered, markTaskRoleRunPushed, prepareTaskRoleRunRedispatch, recordRoleAgentSession, replaceTaskRoleAgentSession, recordTaskRoleTurnBoundary, rememberRoleAgentCompletedTurn, stopTaskRoleRuntimeAfterPhysicalExit, updateRoleAgentSessionStatus, updateTaskRoleProviderRuntime } from "../executor/agentExecutor.js";
|
|
4
4
|
import { acceptProviderTurn, beginProviderTurn, createProviderRuntimeBinding, endProviderActivation, currentProviderActivation, currentProviderConversation, markProviderTurnDeliveryUnknown, rejectProviderTurn, settleProviderTurnSubmission, settleProviderTurn, startProviderActivation, supersedeProviderConversation, updateProviderConversationRecoverability } from "../runtime/providerRuntimeIdentity.js";
|
|
5
|
-
import { CONVERSATION_SWITCH_DETACHED_EVENT, CONVERSATION_SWITCH_RESOLVED_EVENT, conversationDetachmentBasis, conversationReplacementBasis, pendingConversationSwitch, roleSessionDispatchModeWithConversationSwitch } from "../runtime/conversationSwitch.js";
|
|
6
5
|
import { decideProviderRecovery } from "../runtime/providerRecoveryDecision.js";
|
|
7
6
|
import { hasRecentTurnId } from "../executor/turnCompletion.js";
|
|
8
7
|
import { createTaskEvent } from "../event/taskEvent.js";
|
|
@@ -29,7 +28,7 @@ import { wakeReason } from "../scheduler/wakeReason.js";
|
|
|
29
28
|
import { foldRunProgressFacts, latestRunDurableProgressAt, latestRunEventTime, latestStallEvidenceKey, isRoleRunStalled, RUN_PROGRESS_EVENT, RUN_DIAGNOSTIC_FINISHED_EVENT, RUN_RECOVERED_EVENT, RUN_STALLED_EVENT } from "../scheduler/roleRunStall.js";
|
|
30
29
|
import { projectProviderContinuations } from "../runtime/runtimeContinuationProjection.js";
|
|
31
30
|
import { providerContinuationKey } from "../runtime/providerContinuation.js";
|
|
32
|
-
import { currentWorkItemExecutionGroup, updateWorkItemExecutionGroup
|
|
31
|
+
import { currentWorkItemExecutionGroup, updateWorkItemExecutionGroup } from "../workItem/workItem.js";
|
|
33
32
|
import { recordExecutionLaneResult } from "../execution/executionGroup.js";
|
|
34
33
|
import { formatAgentRunReceiptId, formatTaskRecordReference } from "../task/taskRecordReference.js";
|
|
35
34
|
import { bindExecution, claimPending, claimInputDelivery as claimMailboxInputDelivery, completeInputDelivery as completeMailboxInputDelivery, completeProcessing, mailboxHasPending, mailboxHasWork, pendingLane, markInputDeliveryPushed as markMailboxInputDeliveryPushed, markInputDeliveryUnknown as markMailboxInputDeliveryUnknown, releaseInputDelivery as releaseMailboxInputDelivery, resolveInputDeliveryNotAccepted as resolveMailboxInputDeliveryNotAccepted, releaseProcessing } from "../coordination/workMailbox.js";
|
|
@@ -387,7 +386,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
387
386
|
const message = [
|
|
388
387
|
`Leader native Session became unavailable while Run ${active.id} remains active.`,
|
|
389
388
|
"Yui preserved the Run because Session/host termination is not a workflow outcome.",
|
|
390
|
-
"
|
|
389
|
+
"Retire the disposable Run, or use Task execution stop/start if the current runtime cannot be settled normally."
|
|
391
390
|
].join(" ");
|
|
392
391
|
recordLeaderAttentionRequired(store, {
|
|
393
392
|
taskId: input.fence.taskId,
|
|
@@ -650,7 +649,9 @@ export class FileSchedulerStoreAdapter {
|
|
|
650
649
|
const resolved = [];
|
|
651
650
|
for (const request of expired) {
|
|
652
651
|
const task = store.getTask(request.taskId);
|
|
653
|
-
if (task?.status !== "active"
|
|
652
|
+
if (task?.status !== "active"
|
|
653
|
+
|| task.executionGate.state !== "enabled"
|
|
654
|
+
|| request.policy.kind !== "recommended")
|
|
654
655
|
continue;
|
|
655
656
|
const choiceKey = request.policy.recommendedChoiceKey;
|
|
656
657
|
const answered = answerInputRequest(request, { choiceKey }, "agent-timeout", now);
|
|
@@ -723,7 +724,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
723
724
|
return this.store.transaction((store) => {
|
|
724
725
|
const task = store.getTask(input.taskId);
|
|
725
726
|
const run = store.getActiveAgentRun(input.taskId, input.roleName);
|
|
726
|
-
if (task === null || task.status !== "active"
|
|
727
|
+
if (task === null || task.status !== "active" || task.executionGate.state !== "enabled"
|
|
727
728
|
|| run === null || run.id !== input.runId || run.status !== "active") {
|
|
728
729
|
return "state-changed";
|
|
729
730
|
}
|
|
@@ -747,6 +748,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
747
748
|
const run = store.getActiveAgentRun(input.taskId, input.roleName);
|
|
748
749
|
if (task === null
|
|
749
750
|
|| task.status !== "active"
|
|
751
|
+
|| task.executionGate.state !== "enabled"
|
|
750
752
|
|| role === null
|
|
751
753
|
|| run === null
|
|
752
754
|
|| run.id !== input.runId
|
|
@@ -783,6 +785,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
783
785
|
const run = store.getActiveAgentRun(input.taskId, input.roleName);
|
|
784
786
|
if (task === null
|
|
785
787
|
|| task.status !== "active"
|
|
788
|
+
|| task.executionGate.state !== "enabled"
|
|
786
789
|
|| run === null
|
|
787
790
|
|| run.id !== input.runId
|
|
788
791
|
|| run.status !== "active")
|
|
@@ -828,82 +831,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
828
831
|
const sessions = this.store.getTaskRoleSessionSet(taskId, roleName);
|
|
829
832
|
return sessions !== null && sessions.inFlight !== null;
|
|
830
833
|
}
|
|
831
|
-
detachAgentHostProviderForConversationSwitch(input) {
|
|
832
|
-
return this.store.transaction((store) => {
|
|
833
|
-
const sessions = store.getTaskRoleSessionSet(input.taskId, input.roleName);
|
|
834
|
-
const run = store.getAgentRun(input.taskId, input.runId);
|
|
835
|
-
const mailbox = store.getWorkMailbox({
|
|
836
|
-
kind: "role",
|
|
837
|
-
taskId: input.taskId,
|
|
838
|
-
roleName: input.roleName
|
|
839
|
-
});
|
|
840
|
-
const binding = sessions?.providerBinding;
|
|
841
|
-
const session = sessions?.sessions[input.agentId];
|
|
842
|
-
const task = store.getTask(input.taskId);
|
|
843
|
-
const role = store.getRole(input.taskId, input.roleName);
|
|
844
|
-
const activeRun = store.getActiveAgentRun(input.taskId, input.roleName);
|
|
845
|
-
if (task === null || task.status !== "active"
|
|
846
|
-
|| role === null || role.activeAgentId !== input.agentId
|
|
847
|
-
|| sessions === null || sessions === undefined
|
|
848
|
-
|| run === null || run.status !== "active" || run.mode !== "new"
|
|
849
|
-
|| activeRun?.id !== run.id
|
|
850
|
-
|| run.effective.agentId !== input.agentId
|
|
851
|
-
|| session?.nativeSessionId !== input.previousNativeSessionId
|
|
852
|
-
|| binding === null || binding === undefined
|
|
853
|
-
|| currentProviderConversation(binding).conversationId !== input.previousConversationId
|
|
854
|
-
|| sessions.inFlight?.runId !== input.runId
|
|
855
|
-
|| mailbox?.processing?.batchId !== sessions.inFlight.receiptId
|
|
856
|
-
|| mailbox.processing.owner !== "controller"
|
|
857
|
-
|| mailbox.processing.executionRef?.type !== "run"
|
|
858
|
-
|| mailbox.processing.executionRef.taskId !== input.taskId
|
|
859
|
-
|| mailbox.processing.executionRef.id !== input.runId
|
|
860
|
-
|| mailbox.inputDelivery !== null
|
|
861
|
-
|| pendingConversationSwitch(store.listEvents(input.taskId), input.roleName, sessions) === null
|
|
862
|
-
|| !runtimeHookMatchesReservation(store, { scope: "task", taskId: input.taskId, roleName: input.roleName }, input.launchId)
|
|
863
|
-
|| input.nextAuthorityHolderId !== input.launchId) {
|
|
864
|
-
throw new Error("Provider Conversation detachment no longer matches the exact switch Run.");
|
|
865
|
-
}
|
|
866
|
-
const target = binding.activations.find((entry) => (entry.activationId === input.previousActivationId
|
|
867
|
-
&& entry.conversationId === input.previousConversationId));
|
|
868
|
-
if (target === undefined) {
|
|
869
|
-
throw new Error("Provider Conversation detachment targets an unknown Activation.");
|
|
870
|
-
}
|
|
871
|
-
if (target.status !== "active") {
|
|
872
|
-
if (binding.authority.owner !== "none"
|
|
873
|
-
|| input.nextAuthorityEpoch !== binding.authority.epoch + 1) {
|
|
874
|
-
throw new Error("Provider Conversation detachment replay has a stale authority fence.");
|
|
875
|
-
}
|
|
876
|
-
return "already-detached";
|
|
877
|
-
}
|
|
878
|
-
if (conversationDetachmentBasis({
|
|
879
|
-
sessions,
|
|
880
|
-
events: store.listEvents(input.taskId),
|
|
881
|
-
mailbox,
|
|
882
|
-
roleName: input.roleName,
|
|
883
|
-
runId: input.runId,
|
|
884
|
-
runMode: run.mode
|
|
885
|
-
}) !== "actor-request"
|
|
886
|
-
|| binding.authority.owner !== "controller"
|
|
887
|
-
|| binding.authority.holderId !== target.activationId
|
|
888
|
-
|| input.nextAuthorityEpoch !== binding.authority.epoch + 2) {
|
|
889
|
-
throw new Error("Provider Conversation detachment is not authorized at this boundary.");
|
|
890
|
-
}
|
|
891
|
-
const detached = endProviderActivation(binding, target.activationId, {
|
|
892
|
-
status: "ended",
|
|
893
|
-
endedAt: input.now.toISOString(),
|
|
894
|
-
reason: "conversation-switch"
|
|
895
|
-
});
|
|
896
|
-
store.saveTaskRoleSessionSet(updateTaskRoleProviderRuntime(sessions, detached, input.now));
|
|
897
|
-
store.saveEvent(input.taskId, createTaskEvent(store.nextEventId(input.taskId), input.taskId, CONVERSATION_SWITCH_DETACHED_EVENT, {
|
|
898
|
-
runId: input.runId,
|
|
899
|
-
roleName: input.roleName,
|
|
900
|
-
launchId: input.launchId,
|
|
901
|
-
conversationId: input.previousConversationId,
|
|
902
|
-
activationId: input.previousActivationId
|
|
903
|
-
}, input.now));
|
|
904
|
-
return "detached";
|
|
905
|
-
});
|
|
906
|
-
}
|
|
907
834
|
beginAgentHostProviderTurn(input) {
|
|
908
835
|
this.store.transaction((store) => {
|
|
909
836
|
const sessions = store.getTaskRoleSessionSet(input.taskId, input.roleName);
|
|
@@ -1307,7 +1234,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1307
1234
|
saveLeaderDispatch(input) {
|
|
1308
1235
|
return this.store.transaction((store) => {
|
|
1309
1236
|
const task = store.getTask(input.task.id);
|
|
1310
|
-
if (task === null || task.status !== "active") {
|
|
1237
|
+
if (task === null || task.status !== "active" || task.executionGate.state !== "enabled") {
|
|
1311
1238
|
return "unavailable";
|
|
1312
1239
|
}
|
|
1313
1240
|
const role = requireRole(store, input.task.id, input.role.name);
|
|
@@ -1391,7 +1318,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1391
1318
|
saveRoleRunDelivery(input) {
|
|
1392
1319
|
this.store.transaction((store) => {
|
|
1393
1320
|
const task = store.getTask(input.task.id);
|
|
1394
|
-
if (task === null || task.status !== "active") {
|
|
1321
|
+
if (task === null || task.status !== "active" || task.executionGate.state !== "enabled") {
|
|
1395
1322
|
throw new Error(`Task is not active: ${input.task.id}.`);
|
|
1396
1323
|
}
|
|
1397
1324
|
const role = requireRole(store, input.task.id, input.role.name);
|
|
@@ -1464,7 +1391,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1464
1391
|
saveRoleRunPrepared(input) {
|
|
1465
1392
|
this.store.transaction((store) => {
|
|
1466
1393
|
const task = store.getTask(input.task.id);
|
|
1467
|
-
if (task === null || task.status !== "active") {
|
|
1394
|
+
if (task === null || task.status !== "active" || task.executionGate.state !== "enabled") {
|
|
1468
1395
|
throw new Error(`Task is not active: ${input.task.id}.`);
|
|
1469
1396
|
}
|
|
1470
1397
|
const role = requireRole(store, input.task.id, input.role.name);
|
|
@@ -1475,9 +1402,15 @@ export class FileSchedulerStoreAdapter {
|
|
|
1475
1402
|
if (input.session !== null && input.session.nativeSessionId !== undefined) {
|
|
1476
1403
|
const existing = store.getRoleSession(input.task.id, input.role.name);
|
|
1477
1404
|
matchingPreparedRuntimeReservation(store, input);
|
|
1405
|
+
// A terminal Session is audit history, not the current execution
|
|
1406
|
+
// identity. Preallocated providers must publish the replacement fence
|
|
1407
|
+
// before the new Agent Host starts so its exact-runtime preflight sees
|
|
1408
|
+
// the new launch. Only a still-live conflicting Session is deferred.
|
|
1478
1409
|
const defersConversationReplacement = active.mode === "new"
|
|
1479
1410
|
&& existing !== null
|
|
1480
|
-
&& existing.nativeSessionId !== input.session.nativeSessionId
|
|
1411
|
+
&& existing.nativeSessionId !== input.session.nativeSessionId
|
|
1412
|
+
&& existing.status !== "stopped"
|
|
1413
|
+
&& existing.status !== "broken";
|
|
1481
1414
|
if (!defersConversationReplacement
|
|
1482
1415
|
&& (existing?.nativeSessionId !== input.session.nativeSessionId
|
|
1483
1416
|
|| (input.launchId !== undefined && existing.launchId !== input.launchId)
|
|
@@ -1510,6 +1443,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1510
1443
|
}));
|
|
1511
1444
|
if (task === null
|
|
1512
1445
|
|| task.status !== "active"
|
|
1446
|
+
|| task.executionGate.state !== "enabled"
|
|
1513
1447
|
|| role === null
|
|
1514
1448
|
|| active === null
|
|
1515
1449
|
|| active.id !== input.runId
|
|
@@ -1556,16 +1490,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
1556
1490
|
outcome: terminal.status
|
|
1557
1491
|
}, input.now);
|
|
1558
1492
|
store.saveEvent(input.taskId, deliveryFailureEvent);
|
|
1559
|
-
if (terminal.purpose === "execution" && terminal.workItemId !== undefined) {
|
|
1560
|
-
const item = store.getWorkItem(input.taskId, terminal.workItemId);
|
|
1561
|
-
if (item !== null && ![
|
|
1562
|
-
"completed", "failed", "retired"
|
|
1563
|
-
].includes(item.status)) {
|
|
1564
|
-
if (!workItemOwnsUnresolvedExecutionLane(item, terminal.executionGroupId, terminal.executionLaneId)) {
|
|
1565
|
-
store.saveWorkItem(input.taskId, updateWorkItemStatus(item, "failed", input.now, summary));
|
|
1566
|
-
}
|
|
1567
|
-
}
|
|
1568
|
-
}
|
|
1569
1493
|
if (input.roleName !== "leader") {
|
|
1570
1494
|
routeRoleEvent(store, deliveryFailureEvent, input.roleName, terminal.purpose === "review" ? "review-failed" : "role-run-failed", input.now);
|
|
1571
1495
|
}
|
|
@@ -1593,6 +1517,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1593
1517
|
const mailbox = store.getWorkMailbox(target);
|
|
1594
1518
|
if (task === null
|
|
1595
1519
|
|| task.status !== "active"
|
|
1520
|
+
|| task.executionGate.state !== "enabled"
|
|
1596
1521
|
|| role === null
|
|
1597
1522
|
|| active === null
|
|
1598
1523
|
|| active.id !== input.claimed.run.id
|
|
@@ -1606,7 +1531,9 @@ export class FileSchedulerStoreAdapter {
|
|
|
1606
1531
|
store.saveAgentRun(failAgentRun(active, input.failure.message, input.now));
|
|
1607
1532
|
store.clearActiveAgentRun(input.task.id, input.role.name);
|
|
1608
1533
|
clearTaskRoleRunInFlight(store, role, active, input.now);
|
|
1609
|
-
|
|
1534
|
+
// Consume this failed delivery attempt. Releasing it would immediately
|
|
1535
|
+
// redispatch the same wake and manufacture another Leader Session.
|
|
1536
|
+
store.saveWorkMailbox(completeProcessing(mailbox, mailbox.processing.batchId));
|
|
1610
1537
|
store.saveRole(input.task.id, updateRoleStatus(role, "failed", input.now));
|
|
1611
1538
|
breakTaskSessionIfPresent(store, input.task.id, role.name, active.effective.agentId, input.now);
|
|
1612
1539
|
const failure = recordLeaderFailure(input.task.id, input.failure.nativeSessionId, input.failure.message, input.now, store.getLeaderFailure(input.task.id));
|
|
@@ -1630,6 +1557,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1630
1557
|
const currentRun = store.getActiveAgentRun(input.task.id, input.role.name);
|
|
1631
1558
|
if (task === null
|
|
1632
1559
|
|| task.status !== "active"
|
|
1560
|
+
|| task.executionGate.state !== "enabled"
|
|
1633
1561
|
|| role === null
|
|
1634
1562
|
|| currentRun === null
|
|
1635
1563
|
|| currentRun.id !== input.run.id
|
|
@@ -1708,17 +1636,12 @@ export class FileSchedulerStoreAdapter {
|
|
|
1708
1636
|
const group = currentWorkItemExecutionGroup(workItem);
|
|
1709
1637
|
store.saveWorkItem(task.id, updateWorkItemExecutionGroup(workItem, recordExecutionLaneResult(group, currentRun.executionLaneId, { summary: input.summary }, "failed", input.now), input.now));
|
|
1710
1638
|
}
|
|
1711
|
-
const laneUpdated = store.getWorkItem(task.id, currentRun.workItemId);
|
|
1712
|
-
if (!workItemOwnsUnresolvedExecutionLane(laneUpdated, currentRun.executionGroupId, currentRun.executionLaneId)) {
|
|
1713
|
-
store.saveWorkItem(task.id, updateWorkItemStatus(laneUpdated, "failed", input.now, input.summary));
|
|
1714
|
-
}
|
|
1715
1639
|
}
|
|
1716
1640
|
}
|
|
1717
|
-
const
|
|
1718
|
-
|
|
1719
|
-
store.saveRole(task.id, updateRoleStatus(role, blocksAutomaticLeaderRecovery ? "failed" : "exited", input.now));
|
|
1641
|
+
const leaderFailed = role.name === "leader";
|
|
1642
|
+
store.saveRole(task.id, updateRoleStatus(role, leaderFailed ? "failed" : "exited", input.now));
|
|
1720
1643
|
stopTaskSessionIfPresent(store, task.id, role.name, currentRun.effective.agentId, input.now);
|
|
1721
|
-
if (
|
|
1644
|
+
if (leaderFailed) {
|
|
1722
1645
|
const failure = recordLeaderFailure(task.id, input.session?.nativeSessionId ?? "(unregistered)", input.summary, input.now, store.getLeaderFailure(task.id));
|
|
1723
1646
|
store.saveLeaderFailure(failure);
|
|
1724
1647
|
recordLeaderAttentionRequired(store, {
|
|
@@ -1732,7 +1655,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1732
1655
|
});
|
|
1733
1656
|
return "failed";
|
|
1734
1657
|
}
|
|
1735
|
-
queueLeaderWakeup(store, task.id, wakeReason(
|
|
1658
|
+
queueLeaderWakeup(store, task.id, wakeReason("role-run-failed"), input.now);
|
|
1736
1659
|
return "failed";
|
|
1737
1660
|
});
|
|
1738
1661
|
}
|
|
@@ -1963,7 +1886,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1963
1886
|
const task = store.getTask(input.taskId);
|
|
1964
1887
|
if (task === null)
|
|
1965
1888
|
throw new Error(`Task not found: ${input.taskId}.`);
|
|
1966
|
-
if (task.status !== "active") {
|
|
1889
|
+
if (task.status !== "active" || task.executionGate.state !== "enabled") {
|
|
1967
1890
|
throw new Error(`Cannot complete a runtime turn for inactive Task: ${input.taskId}.`);
|
|
1968
1891
|
}
|
|
1969
1892
|
const role = requireRole(store, input.taskId, input.roleName);
|
|
@@ -2061,7 +1984,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
2061
1984
|
}
|
|
2062
1985
|
classifyRuntimeTurnFailed(input) {
|
|
2063
1986
|
const task = this.store.getTask(input.taskId);
|
|
2064
|
-
if (task?.status !== "active")
|
|
1987
|
+
if (task?.status !== "active" || task.executionGate.state !== "enabled")
|
|
2065
1988
|
return "obsolete";
|
|
2066
1989
|
const role = this.store.getRole(input.taskId, input.roleName);
|
|
2067
1990
|
if (role === null)
|
|
@@ -2122,14 +2045,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
2122
2045
|
roleName: input.roleName
|
|
2123
2046
|
}), RUNTIME_CLEANUP_REQUIRED_REASON, now, [{ type: "task", id: input.taskId }]);
|
|
2124
2047
|
const terminal = result.run;
|
|
2125
|
-
if (before.purpose === "execution" && before.workItemId !== undefined) {
|
|
2126
|
-
const item = store.getWorkItem(input.taskId, before.workItemId);
|
|
2127
|
-
if (item !== null && ![
|
|
2128
|
-
"completed", "failed", "retired"
|
|
2129
|
-
].includes(item.status) && !workItemOwnsUnresolvedExecutionLane(item, before.executionGroupId, before.executionLaneId)) {
|
|
2130
|
-
store.saveWorkItem(input.taskId, updateWorkItemStatus(item, "failed", now, summary));
|
|
2131
|
-
}
|
|
2132
|
-
}
|
|
2133
2048
|
const role = store.getRole(input.taskId, input.roleName);
|
|
2134
2049
|
if (role !== null) {
|
|
2135
2050
|
store.saveRole(input.taskId, updateRoleStatus(role, input.roleName === "leader" ? "failed" : "idle", now));
|
|
@@ -2513,8 +2428,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
2513
2428
|
}
|
|
2514
2429
|
const sessions = store.getTaskRoleSessionSet(entry.taskId, entry.roleName);
|
|
2515
2430
|
const providerBinding = sessions?.providerBinding;
|
|
2516
|
-
let recoveryMode = "resume";
|
|
2517
|
-
let replacementRequired = false;
|
|
2518
2431
|
if (providerBinding === null || providerBinding === undefined) {
|
|
2519
2432
|
deferUnprovenProviderRecovery(store, run, entry.roleName, "Provider Conversation identity is missing; automatic recovery is fenced.", now);
|
|
2520
2433
|
return;
|
|
@@ -2554,12 +2467,19 @@ export class FileSchedulerStoreAdapter {
|
|
|
2554
2467
|
}
|
|
2555
2468
|
return;
|
|
2556
2469
|
}
|
|
2557
|
-
if (recovery.action === "
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2470
|
+
if (recovery.action === "restart-run") {
|
|
2471
|
+
const finalized = finalizeProviderRetryDeadline(store, run, "Provider Conversation is unavailable; retry the work with a fresh Run and Session.", "provider-conversation-unavailable", now);
|
|
2472
|
+
if (finalized) {
|
|
2473
|
+
const currentSessions = store.getTaskRoleSessionSet(entry.taskId, entry.roleName);
|
|
2474
|
+
const currentSession = currentSessions?.sessions[run.effective.agentId];
|
|
2475
|
+
if (currentSessions !== null && currentSessions !== undefined
|
|
2476
|
+
&& currentSession !== undefined
|
|
2477
|
+
&& currentSession.status !== "stopped"
|
|
2478
|
+
&& currentSession.status !== "broken") {
|
|
2479
|
+
store.saveTaskRoleSessionSet(updateRoleAgentSessionStatus(currentSessions, run.effective.agentId, "broken", now));
|
|
2480
|
+
}
|
|
2561
2481
|
}
|
|
2562
|
-
|
|
2482
|
+
return;
|
|
2563
2483
|
}
|
|
2564
2484
|
const session = sessions?.sessions[run.effective.agentId];
|
|
2565
2485
|
const identityMatches = session !== undefined
|
|
@@ -2570,9 +2490,8 @@ export class FileSchedulerStoreAdapter {
|
|
|
2570
2490
|
|| session.launchId === run.providerRetry.launchId);
|
|
2571
2491
|
if (!identityMatches || session?.nativeSessionId === undefined) {
|
|
2572
2492
|
// Local Session projection loss is not native-session-dead evidence.
|
|
2573
|
-
//
|
|
2574
|
-
//
|
|
2575
|
-
// unrecoverable.
|
|
2493
|
+
// Keep the bounded retry episode; if native identity never returns,
|
|
2494
|
+
// the episode fails and a later Run can continue from Task progress.
|
|
2576
2495
|
const deferred = deferProviderRetry(run.providerRetry, now);
|
|
2577
2496
|
if (deferred === null) {
|
|
2578
2497
|
finalizeProviderRetryDeadline(store, run, "Provider retry safety could not be re-established before the bounded episode deadline; native Session recoverability remains unproven.", "native-resume-unproven", now);
|
|
@@ -2606,25 +2525,10 @@ export class FileSchedulerStoreAdapter {
|
|
|
2606
2525
|
store.saveAgentRun(withProviderRetry(run, deferred));
|
|
2607
2526
|
return;
|
|
2608
2527
|
}
|
|
2609
|
-
if (replacementRequired) {
|
|
2610
|
-
try {
|
|
2611
|
-
recoveryMode = roleSessionDispatchModeWithConversationSwitch(sessions, store.listEvents(entry.taskId), roleMailbox, entry.roleName, run.effective.agentId, run.effective);
|
|
2612
|
-
}
|
|
2613
|
-
catch (error) {
|
|
2614
|
-
deferUnprovenProviderRecovery(store, run, entry.roleName, error instanceof Error
|
|
2615
|
-
? error.message
|
|
2616
|
-
: "Independent Conversation-switch evaluation failed closed.", now);
|
|
2617
|
-
return;
|
|
2618
|
-
}
|
|
2619
|
-
if (recoveryMode !== "new") {
|
|
2620
|
-
deferUnprovenProviderRecovery(store, run, entry.roleName, "Independent Conversation-switch evaluation kept the current Conversation.", now);
|
|
2621
|
-
return;
|
|
2622
|
-
}
|
|
2623
|
-
}
|
|
2624
2528
|
// Reopen the Run on its original Session: reset transport markers,
|
|
2625
2529
|
// switch to resume, and mark the projection in-flight. The delivery
|
|
2626
2530
|
// pass re-pushes the exact same input in this same pass.
|
|
2627
|
-
const reopenedRun = reopenRunForProviderRetry(run, `provider-retry-${run.providerRetry.episodeId}-${run.providerRetry.dispatchedRetries + 1}`, now,
|
|
2531
|
+
const reopenedRun = reopenRunForProviderRetry(run, `provider-retry-${run.providerRetry.episodeId}-${run.providerRetry.dispatchedRetries + 1}`, now, "resume");
|
|
2628
2532
|
store.saveAgentRun(reopenedRun);
|
|
2629
2533
|
if (sessions !== null && sessions.inFlight !== null) {
|
|
2630
2534
|
store.saveTaskRoleSessionSet(prepareTaskRoleRunRedispatch(sessions, {
|
|
@@ -2735,22 +2639,17 @@ export class FileSchedulerStoreAdapter {
|
|
|
2735
2639
|
taskId,
|
|
2736
2640
|
roleName: input.fence.roleName
|
|
2737
2641
|
});
|
|
2738
|
-
const
|
|
2739
|
-
|
|
2642
|
+
const existingSession = sessions.sessions[input.fence.agentId];
|
|
2643
|
+
const existingNativeSessionId = existingSession?.nativeSessionId;
|
|
2740
2644
|
const replacingNativeSession = existingNativeSessionId !== undefined
|
|
2741
2645
|
&& existingNativeSessionId !== decision.outcome.nativeSessionId;
|
|
2742
2646
|
const replacementBasis = replacingNativeSession
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
mailbox,
|
|
2747
|
-
roleName: input.fence.roleName,
|
|
2748
|
-
runId: run.id,
|
|
2749
|
-
runMode: run.mode
|
|
2750
|
-
})
|
|
2647
|
+
&& run.mode === "new"
|
|
2648
|
+
&& (existingSession?.status === "stopped" || existingSession?.status === "broken")
|
|
2649
|
+
? "terminal-session"
|
|
2751
2650
|
: null;
|
|
2752
2651
|
if (replacingNativeSession && replacementBasis === null) {
|
|
2753
|
-
recordCanonicalObservationObsolete(store, input, "
|
|
2652
|
+
recordCanonicalObservationObsolete(store, input, "session-replacement-not-terminal", now);
|
|
2754
2653
|
return "obsolete";
|
|
2755
2654
|
}
|
|
2756
2655
|
const sessionInput = {
|
|
@@ -2764,17 +2663,9 @@ export class FileSchedulerStoreAdapter {
|
|
|
2764
2663
|
};
|
|
2765
2664
|
const bound = replacementBasis === null
|
|
2766
2665
|
? recordRoleAgentSession(sessions, sessionInput, now)
|
|
2767
|
-
:
|
|
2666
|
+
: replaceTaskRoleAgentSession(sessions, sessionInput, now);
|
|
2768
2667
|
const withProvider = bindOrSupersedeProviderRuntime(bound, input, now, replacementBasis ?? undefined);
|
|
2769
2668
|
store.saveTaskRoleSessionSet(withProvider);
|
|
2770
|
-
if (replacementBasis === "actor-request") {
|
|
2771
|
-
resolveConversationSwitchRequest(store, taskId, input.fence.roleName, sessions, "applied", now, {
|
|
2772
|
-
conversationId: input.fence.conversationId
|
|
2773
|
-
?? decision.outcome.nativeSessionId,
|
|
2774
|
-
activationId: input.fence.activationId ?? input.fence.launchId,
|
|
2775
|
-
launchId: input.fence.launchId
|
|
2776
|
-
});
|
|
2777
|
-
}
|
|
2778
2669
|
const driver = this.drivers.require(input.fence.driverId);
|
|
2779
2670
|
if (driver.capabilities.observation.sessionBootstrap === "discovered"
|
|
2780
2671
|
&& run.pushedAt === undefined) {
|
|
@@ -3297,6 +3188,7 @@ function preallocatedRuntimeReadyAwaitingProjection(store, observation, drivers)
|
|
|
3297
3188
|
const run = store.getActiveAgentRun(taskId, observation.fence.roleName);
|
|
3298
3189
|
const sessions = store.getTaskRoleSessionSet(taskId, observation.fence.roleName);
|
|
3299
3190
|
if (task?.status !== "active"
|
|
3191
|
+
|| task.executionGate.state !== "enabled"
|
|
3300
3192
|
|| role?.activeAgentId !== observation.fence.agentId
|
|
3301
3193
|
|| run?.id !== runId
|
|
3302
3194
|
|| run.status !== "active"
|
|
@@ -3447,14 +3339,6 @@ function finalizeProviderRetryDeadline(store, run, summary, reason, now) {
|
|
|
3447
3339
|
elapsedMs: String(Math.max(0, now.getTime() - Date.parse(retry.firstFailureAt)))
|
|
3448
3340
|
}, now);
|
|
3449
3341
|
store.saveEvent(run.taskId, exhaustionEvent);
|
|
3450
|
-
if (terminal.purpose === "execution" && terminal.workItemId !== undefined) {
|
|
3451
|
-
const item = store.getWorkItem(run.taskId, terminal.workItemId);
|
|
3452
|
-
if (item !== null
|
|
3453
|
-
&& !["completed", "failed", "retired"].includes(item.status)
|
|
3454
|
-
&& !workItemOwnsUnresolvedExecutionLane(item, terminal.executionGroupId, terminal.executionLaneId)) {
|
|
3455
|
-
store.saveWorkItem(run.taskId, updateWorkItemStatus(item, "failed", now, summary));
|
|
3456
|
-
}
|
|
3457
|
-
}
|
|
3458
3342
|
const role = store.getRole(run.taskId, run.roleName);
|
|
3459
3343
|
if (role !== null) {
|
|
3460
3344
|
store.saveRole(run.taskId, updateRoleStatus(role, run.roleName === "leader" ? "failed" : "idle", now));
|
|
@@ -3522,7 +3406,7 @@ function recordTaskRuntimeNativeSession(store, input, now) {
|
|
|
3522
3406
|
if (task.status === "archived") {
|
|
3523
3407
|
throw new Error(`Cannot register a native session for archived Task: ${input.taskId}.`);
|
|
3524
3408
|
}
|
|
3525
|
-
if (task.status !== "active") {
|
|
3409
|
+
if (task.status !== "active" || task.executionGate.state !== "enabled") {
|
|
3526
3410
|
throw new Error(`Cannot register a native session for a Task that is not active: ${input.taskId}.`);
|
|
3527
3411
|
}
|
|
3528
3412
|
const role = requireRole(store, input.taskId, input.roleName);
|
|
@@ -3990,29 +3874,16 @@ function bindOrSupersedeProviderRuntime(sessions, input, now, replacementBasis)
|
|
|
3990
3874
|
startedAt: input.observedAt ?? input.receivedAt
|
|
3991
3875
|
}), now);
|
|
3992
3876
|
}
|
|
3993
|
-
|
|
3994
|
-
|
|
3995
|
-
|
|
3877
|
+
if (replacementBasis === undefined) {
|
|
3878
|
+
throw new Error("A fresh Provider Conversation requires a terminal prior Session.");
|
|
3879
|
+
}
|
|
3996
3880
|
return updateTaskRoleProviderRuntime(sessions, supersedeProviderConversation(sessions.providerBinding, {
|
|
3997
3881
|
conversationId,
|
|
3998
3882
|
activationId,
|
|
3999
3883
|
switchedAt: input.observedAt ?? input.receivedAt,
|
|
4000
|
-
|
|
4001
|
-
...(replacementBasis === undefined ? {} : { basis: replacementBasis })
|
|
3884
|
+
basis: replacementBasis
|
|
4002
3885
|
}), now);
|
|
4003
3886
|
}
|
|
4004
|
-
function resolveConversationSwitchRequest(store, taskId, roleName, previousSessions, status, now, replacement) {
|
|
4005
|
-
const request = pendingConversationSwitch(store.listEvents(taskId), roleName, previousSessions);
|
|
4006
|
-
if (request === null)
|
|
4007
|
-
return;
|
|
4008
|
-
store.saveEvent(taskId, createTaskEvent(store.nextEventId(taskId), taskId, CONVERSATION_SWITCH_RESOLVED_EVENT, {
|
|
4009
|
-
requestId: request.requestId,
|
|
4010
|
-
roleName,
|
|
4011
|
-
generation: request.generation,
|
|
4012
|
-
status,
|
|
4013
|
-
...(replacement === undefined ? {} : replacement)
|
|
4014
|
-
}, now));
|
|
4015
|
-
}
|
|
4016
3887
|
function rejectSubmittingProviderTurn(store, target, delivery, now, reason) {
|
|
4017
3888
|
if (target.kind !== "role"
|
|
4018
3889
|
|| delivery.executionRef.type !== "run"
|
|
@@ -99,8 +99,9 @@ function validateStartParams(store, params) {
|
|
|
99
99
|
if (task === null) {
|
|
100
100
|
throw jobDomainError(`Task not found: ${params.taskId}.`);
|
|
101
101
|
}
|
|
102
|
-
if (task.status !== "active") {
|
|
103
|
-
throw jobDomainError(`DurableJob requires
|
|
102
|
+
if (task.status !== "active" || task.executionGate.state !== "enabled") {
|
|
103
|
+
throw jobDomainError(`DurableJob requires enabled Task execution; ${params.taskId} is `
|
|
104
|
+
+ `${task.status}/${task.executionGate.state}.`);
|
|
104
105
|
}
|
|
105
106
|
// The owner record must exist and must not be terminal. A terminal owner
|
|
106
107
|
// cannot run new jobs — its workspace is eligible for cleanup.
|
|
@@ -584,11 +584,6 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
584
584
|
}
|
|
585
585
|
return { recorded: true };
|
|
586
586
|
}
|
|
587
|
-
if (method === "runtime.conversation-switch-detach") {
|
|
588
|
-
const value = conversationSwitchDetachParams(params);
|
|
589
|
-
const outcome = schedulerStore.detachAgentHostProviderForConversationSwitch(value);
|
|
590
|
-
return { recorded: outcome === "detached", outcome };
|
|
591
|
-
}
|
|
592
587
|
if (method === "runtime.provider-turn-submission-resolve") {
|
|
593
588
|
const value = providerTurnControlParams(params);
|
|
594
589
|
const status = params.status;
|
|
@@ -719,10 +714,11 @@ export function createRuntimeLifecycleDispatcher(store, schedulerStore, sessionH
|
|
|
719
714
|
const task = request.scope === "task"
|
|
720
715
|
? store.getTask(request.taskId)
|
|
721
716
|
: null;
|
|
722
|
-
if (request.scope === "task"
|
|
717
|
+
if (request.scope === "task"
|
|
718
|
+
&& (task?.status !== "active" || task.executionGate.state !== "enabled")) {
|
|
723
719
|
throw applicationError("INVALID_PARAMS", task === null
|
|
724
720
|
? `Task not found: ${request.taskId}.`
|
|
725
|
-
: `Task is not
|
|
721
|
+
: `Task execution is not enabled: ${request.taskId}.`);
|
|
726
722
|
}
|
|
727
723
|
if (request.scope === "task" && task !== null) {
|
|
728
724
|
const taskWorkspace = store.getTaskWorkspace(task.id);
|
|
@@ -852,7 +848,9 @@ function assertRuntimeLaunchRequestCurrent(store, request) {
|
|
|
852
848
|
let activeRun = null;
|
|
853
849
|
if (request.owner.scope === "task") {
|
|
854
850
|
const task = store.getTask(request.owner.taskId);
|
|
855
|
-
if (task === null
|
|
851
|
+
if (task === null
|
|
852
|
+
|| task.status !== "active"
|
|
853
|
+
|| task.executionGate.state !== "enabled") {
|
|
856
854
|
throw new Error(`Task is no longer active: ${request.owner.taskId}.`);
|
|
857
855
|
}
|
|
858
856
|
activeRun = store.getActiveAgentRun(request.owner.taskId, request.owner.roleName);
|
|
@@ -1135,31 +1133,6 @@ function providerTurnControlParams(params) {
|
|
|
1135
1133
|
now: new Date(observedAt)
|
|
1136
1134
|
};
|
|
1137
1135
|
}
|
|
1138
|
-
function conversationSwitchDetachParams(params) {
|
|
1139
|
-
if (typeof params !== "object" || params === null || Array.isArray(params)) {
|
|
1140
|
-
throw applicationError("INVALID_PARAMS", "Provider Conversation detachment params are invalid.");
|
|
1141
|
-
}
|
|
1142
|
-
const value = params;
|
|
1143
|
-
const nextAuthorityEpoch = value.nextAuthorityEpoch;
|
|
1144
|
-
const observedAt = requiredParam(value.observedAt);
|
|
1145
|
-
if (!Number.isSafeInteger(nextAuthorityEpoch) || nextAuthorityEpoch < 1
|
|
1146
|
-
|| !Number.isFinite(Date.parse(observedAt))) {
|
|
1147
|
-
throw applicationError("INVALID_PARAMS", "Provider Conversation detachment fence is invalid.");
|
|
1148
|
-
}
|
|
1149
|
-
return {
|
|
1150
|
-
taskId: requiredParam(value.taskId),
|
|
1151
|
-
roleName: requiredParam(value.roleName),
|
|
1152
|
-
runId: requiredParam(value.runId),
|
|
1153
|
-
agentId: requiredParam(value.agentId),
|
|
1154
|
-
launchId: requiredParam(value.launchId),
|
|
1155
|
-
previousConversationId: requiredParam(value.previousConversationId),
|
|
1156
|
-
previousNativeSessionId: requiredParam(value.previousNativeSessionId),
|
|
1157
|
-
previousActivationId: requiredParam(value.previousActivationId),
|
|
1158
|
-
nextAuthorityEpoch: nextAuthorityEpoch,
|
|
1159
|
-
nextAuthorityHolderId: requiredParam(value.nextAuthorityHolderId),
|
|
1160
|
-
now: new Date(observedAt)
|
|
1161
|
-
};
|
|
1162
|
-
}
|
|
1163
1136
|
function abortableDelay(milliseconds, signal) {
|
|
1164
1137
|
return new Promise((resolve, reject) => {
|
|
1165
1138
|
const timer = setTimeout(() => {
|
|
@@ -137,7 +137,9 @@ export class FileRuntimeEventProcessor {
|
|
|
137
137
|
const taskId = event.observation.fence.taskId;
|
|
138
138
|
if (taskId !== undefined) {
|
|
139
139
|
const task = this.observer.getTask(taskId);
|
|
140
|
-
if (task === null
|
|
140
|
+
if (task === null
|
|
141
|
+
|| task.status !== "active"
|
|
142
|
+
|| task.executionGate.state !== "enabled")
|
|
141
143
|
return "obsolete";
|
|
142
144
|
}
|
|
143
145
|
return this.observer.observeRuntimeObservation?.(event.observation, now) ?? "obsolete";
|
|
@@ -217,7 +219,7 @@ export class FileRuntimeEventProcessor {
|
|
|
217
219
|
};
|
|
218
220
|
if (task === null)
|
|
219
221
|
return "obsolete";
|
|
220
|
-
if (task.status !== "active") {
|
|
222
|
+
if (task.status !== "active" || task.executionGate.state !== "enabled") {
|
|
221
223
|
this.recordObsolete(event, task.status === "archived" ? "task-archived" : "task-retired", now);
|
|
222
224
|
return "obsolete";
|
|
223
225
|
}
|
|
@@ -622,7 +624,9 @@ export class AsyncRuntimeEventProcessor {
|
|
|
622
624
|
const taskId = event.observation.fence.taskId;
|
|
623
625
|
if (taskId !== undefined) {
|
|
624
626
|
const task = await this.observer.getTask(taskId);
|
|
625
|
-
if (task === null
|
|
627
|
+
if (task === null
|
|
628
|
+
|| task.status !== "active"
|
|
629
|
+
|| task.executionGate.state !== "enabled")
|
|
626
630
|
return "obsolete";
|
|
627
631
|
}
|
|
628
632
|
const outcome = (await this.observer.observeRuntimeObservation?.(event.observation, now))
|
|
@@ -662,7 +666,7 @@ export class AsyncRuntimeEventProcessor {
|
|
|
662
666
|
};
|
|
663
667
|
if (task === null)
|
|
664
668
|
return "obsolete";
|
|
665
|
-
if (task.status !== "active") {
|
|
669
|
+
if (task.status !== "active" || task.executionGate.state !== "enabled") {
|
|
666
670
|
await this.recordObsolete(event, task.status === "archived" ? "task-archived" : "task-retired", now);
|
|
667
671
|
return "obsolete";
|
|
668
672
|
}
|
|
@@ -4,7 +4,6 @@ import { nativeSessionIdForLaunch } from "../runtime/preallocatedNativeSession.j
|
|
|
4
4
|
import { runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
|
|
5
5
|
import { YUI_CONTROL_PLANE_DESCRIPTOR, YUI_TASK_RUNTIME_DESCRIPTOR, assertExactTaskRuntimeEnvironment, exactControlPlaneDigest, parseExactControlPlaneDescriptor, refreshReusedTaskRuntimeDescriptorSource } from "../runtime/exactControlPlane.js";
|
|
6
6
|
import { agentRunDeliveryReceiptId } from "../run/agentRun.js";
|
|
7
|
-
import { conversationReplacementBasis } from "../runtime/conversationSwitch.js";
|
|
8
7
|
/**
|
|
9
8
|
* Resolves turn identity from the current durable in-flight fence. The one
|
|
10
9
|
* exception is a Driver-declared startup Session Hook, which can arrive before
|
|
@@ -37,7 +36,9 @@ export function resolveRuntimeHookRunFence(environment, adapterId, payloadNative
|
|
|
37
36
|
}
|
|
38
37
|
const store = openCompatibleFileTaskStore(home);
|
|
39
38
|
const task = store.getTask(taskId);
|
|
40
|
-
if (task === null
|
|
39
|
+
if (task === null
|
|
40
|
+
|| task.status !== "active"
|
|
41
|
+
|| task.executionGate.state !== "enabled") {
|
|
41
42
|
throw new Error("Runtime observation Hook Task is not current and active.");
|
|
42
43
|
}
|
|
43
44
|
const role = store.getRole(taskId, roleName);
|
|
@@ -107,14 +108,7 @@ export function resolveRuntimeHookRunFence(environment, adapterId, payloadNative
|
|
|
107
108
|
&& sessions !== null
|
|
108
109
|
&& startupReservation
|
|
109
110
|
&& startupRun?.mode === "new"
|
|
110
|
-
&&
|
|
111
|
-
sessions,
|
|
112
|
-
events: store.listEvents(taskId),
|
|
113
|
-
mailbox: roleMailbox,
|
|
114
|
-
roleName,
|
|
115
|
-
runId: startupRun.id,
|
|
116
|
-
runMode: startupRun.mode
|
|
117
|
-
}) !== null;
|
|
111
|
+
&& (session.status === "stopped" || session.status === "broken");
|
|
118
112
|
const preallocatedStartup = options.startupSession === "preallocated"
|
|
119
113
|
&& expectedNativeSessionId !== undefined
|
|
120
114
|
&& (session === undefined || replacementStartup)
|