@zq-silk/yui 0.11.2 → 0.12.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.
- package/ARCHITECTURE.md +23 -4
- package/README.md +80 -13
- package/dist/cli/commandCatalog.js +30 -5
- package/dist/cli.js +105 -43
- package/dist/commands/configCommands.js +12 -45
- package/dist/commands/executionAuditCommands.js +1 -1
- package/dist/commands/jobCommands.js +4 -10
- package/dist/commands/projectCommands.js +581 -2
- package/dist/commands/taskCommands.js +159 -172
- package/dist/commands/taskCompletionGate.js +29 -54
- package/dist/commands/taskContextCommand.js +55 -11
- package/dist/commands/taskInputCommands.js +8 -5
- package/dist/commands/taskIntegrationCommands.js +2 -1
- package/dist/commands/taskNextActionCommand.js +56 -6
- package/dist/commands/taskOverviewCommand.js +17 -31
- package/dist/commands/taskRoleRuntimeStatus.js +24 -0
- package/dist/commands/taskWorkspaceCommands.js +5 -2
- package/dist/config/configCatalog.js +2 -2
- package/dist/config/yuiConfig.js +3 -7
- package/dist/context/wakeNotification.js +20 -5
- package/dist/controller/agentRuntimeObserver.js +247 -51
- package/dist/controller/clientRuntime.js +1 -1
- package/dist/controller/controller.js +8 -12
- package/dist/controller/fileSchedulerStoreAdapter.js +146 -198
- package/dist/controller/runtime.js +3 -3
- package/dist/controller/runtimeEventInbox.js +17 -7
- package/dist/controller/runtimeEventProcessor.js +12 -19
- package/dist/controller/runtimeObservationHook.js +45 -0
- package/dist/execution/resourceBroker.js +5 -4
- package/dist/executor/agentExecutor.js +4 -4
- package/dist/executor/effectiveLaunch.js +8 -48
- package/dist/executor/fileRoleLaunchPlanner.js +36 -16
- package/dist/executor/workspacePreflightClassification.js +23 -2
- package/dist/interaction/operatorPresentation.js +33 -89
- package/dist/lifecycle/exactRunTerminalization.js +1 -2
- package/dist/lifecycle/taskRoleSessionReset.js +5 -13
- package/dist/observability/orchestrationMetrics.js +8 -3
- package/dist/profile/agentProfile.js +1 -1
- package/dist/release/cliHomeReleaseFence.js +123 -0
- package/dist/repository/checkoutSwap.js +61 -0
- package/dist/repository/gitWorkspace.js +36 -0
- package/dist/repository/project.js +53 -3
- package/dist/repository/taskWorkspacePreparer.js +176 -60
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/review/deltaRecheck.js +12 -51
- package/dist/review/reviewAcceptance.js +26 -0
- package/dist/review/reviewConfig.js +0 -31
- package/dist/review/reviewDecision.js +113 -0
- package/dist/review/reviewOutcomeClassifier.js +1 -1
- package/dist/review/reviewRound.js +1 -1
- package/dist/review/reviewerAvailability.js +69 -0
- package/dist/runtime/agentDriverObservation.js +24 -10
- package/dist/runtime/builtinAgentDrivers.js +4 -3
- package/dist/runtime/builtinTranscriptObserver.js +301 -64
- package/dist/runtime/builtinTranscriptUsage.js +9 -7
- package/dist/runtime/index.js +1 -0
- package/dist/runtime/runtimeObservation.js +13 -0
- package/dist/runtime/runtimeProjection.js +16 -25
- package/dist/runtime/sessionTokenMetrics.js +181 -0
- package/dist/runtime/structuredProviderHost.js +7 -1
- package/dist/scheduler/activeRoleRunDelivery.js +4 -4
- package/dist/scheduler/activeTaskProgress.js +7 -6
- package/dist/scheduler/leaderWakeupProcessor.js +3 -5
- package/dist/scheduler/operatorEvent.js +34 -0
- package/dist/scheduler/operatorInputNotificationProcessor.js +54 -94
- package/dist/scheduler/roleRunStall.js +0 -14
- package/dist/scheduler/taskExecutionProjection.js +97 -76
- package/dist/scheduler/taskObservabilityProjection.js +10 -11
- package/dist/storage/migration/productionRegistry.js +467 -0
- package/dist/storage/sqliteSchema.js +18 -8
- package/dist/storage/sqliteStore.js +61 -21
- package/dist/storage/storeRpc.js +0 -1
- package/dist/storage/taskStore.js +47 -31
- package/dist/storage/upgrade/recordVersions.js +3 -2
- package/dist/storage/upgrade/sqliteMigrationTarget.js +30 -8
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +23 -11
- package/dist/storage/upgrade/sqliteStateMigration.js +66 -8
- package/dist/task/completionReadiness.js +10 -6
- package/dist/task/nextAction.js +82 -24
- package/dist/telemetry/sqliteTelemetryStore.js +1 -1
- package/dist/web/assets/client/components.js +13 -3
- package/dist/web/assets/client/i18n.js +6 -2
- package/dist/web/webSnapshot.js +4 -0
- package/i18n/README.zh-CN.md +2 -2
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +53 -16
- package/skills/yui-operator/SKILL.md +9 -0
- package/skills/yui-reviewer/SKILL.md +28 -7
- package/dist/context/sessionContextBudget.js +0 -71
- package/dist/lifecycle/contextBudgetRollover.js +0 -81
- package/dist/scheduler/operatorNotification.js +0 -59
|
@@ -8,11 +8,9 @@ import { createTaskEvent } from "../event/taskEvent.js";
|
|
|
8
8
|
import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
9
9
|
import { buildTaskWakeEnvelope } from "../context/wakeNotification.js";
|
|
10
10
|
import { createTaskWake, fallbackWakeCursor, latestTaskWake } from "../scheduler/taskWake.js";
|
|
11
|
-
import { rolloverTaskRoleSessionForContextBudget } from "../lifecycle/contextBudgetRollover.js";
|
|
12
|
-
import { resolveContextBudget } from "../config/yuiConfig.js";
|
|
13
11
|
import { answerInputRequest } from "../input/inputRequest.js";
|
|
14
12
|
import { activeRoleAgentBinding, updateRoleStatus } from "../role/role.js";
|
|
15
|
-
import { effectiveLaunchWithTaskMainWorkspace, effectiveLaunchSnapshotsCompatible,
|
|
13
|
+
import { effectiveLaunchWithTaskMainWorkspace, effectiveLaunchSnapshotsCompatible, effectiveLaunchSnapshotsCompatibleForTaskSession, resolveEffectiveLaunch, validateEffectiveLaunchSnapshot } from "../executor/effectiveLaunch.js";
|
|
16
14
|
import { SYSTEM_OPERATOR_ROLE } from "../role/systemRoles.js";
|
|
17
15
|
import { failAgentRun, markAgentRunDelivered, markAgentRunPushed, reopenRunForProviderRetry, clearProviderRetryOnProgress, agentRunDeliveryReceiptId, withProviderRetry } from "../run/agentRun.js";
|
|
18
16
|
import { markWorkflowOutcomeRequestDispatched } from "../run/runControlRequest.js";
|
|
@@ -23,11 +21,11 @@ import { classifyRuntimeProcessExit, validateRuntimeProcessExitObservation } fro
|
|
|
23
21
|
import { terminalizeExactTaskRun } from "../lifecycle/exactRunTerminalization.js";
|
|
24
22
|
import { createCanonicalLifecycleEvent, foldCanonicalLifecycleEvent } from "../lifecycle/canonicalLifecycleEvent.js";
|
|
25
23
|
import { recordLeaderFailure } from "../scheduler/leaderFailure.js";
|
|
26
|
-
import {
|
|
24
|
+
import { enqueueOperatorEvent, recordLeaderAttentionRequired, routeRoleEvent } from "../scheduler/operatorEvent.js";
|
|
27
25
|
import { pendingWakeupsMatch } from "../scheduler/pendingWakeup.js";
|
|
28
26
|
import { queueLeaderWakeup } from "../scheduler/wakeupQueue.js";
|
|
29
27
|
import { wakeReason } from "../scheduler/wakeReason.js";
|
|
30
|
-
import { foldRunProgressFacts, latestRunDurableProgressAt, latestRunEventTime, latestStallEvidenceKey,
|
|
28
|
+
import { foldRunProgressFacts, latestRunDurableProgressAt, latestRunEventTime, latestStallEvidenceKey, RUN_PROGRESS_EVENT, RUN_RECOVERED_EVENT, RUN_STALLED_EVENT } from "../scheduler/roleRunStall.js";
|
|
31
29
|
import { projectProviderContinuations } from "../runtime/runtimeContinuationProjection.js";
|
|
32
30
|
import { providerContinuationKey } from "../runtime/providerContinuation.js";
|
|
33
31
|
import { currentWorkItemExecutionGroup, updateWorkItemExecutionGroup, updateWorkItemStatus, workItemOwnsUnresolvedExecutionLane } from "../workItem/workItem.js";
|
|
@@ -38,7 +36,7 @@ import { enqueueWork } from "../coordination/workMailboxQueue.js";
|
|
|
38
36
|
import { RUNTIME_CLEANUP_REQUIRED_REASON, RUNTIME_LAUNCH_RESERVED_REASON, RUNTIME_LIFECYCLE_OWNER, RuntimeLifecycleBusyError, hasRuntimeCleanupObligation, hasRuntimeLifecycleWork, isRuntimeLaunchReservation, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
|
|
39
37
|
import { nativeSessionIdForLaunch } from "../runtime/preallocatedNativeSession.js";
|
|
40
38
|
import { builtinAgentDriverRegistry } from "../runtime/builtinAgentDrivers.js";
|
|
41
|
-
import { RUNTIME_OBSERVATION_TASK_EVENT, createRuntimeObservation, runtimeObservationFenceMatches, runtimeObservationFromTaskEvent, runtimeObservationRunFenceMatches, runtimeObservationTaskEventPayload } from "../runtime/runtimeObservation.js";
|
|
39
|
+
import { RUNTIME_OBSERVATION_TASK_EVENT, createRuntimeObservation, isRuntimeTokenEvidence, runtimeObservationFenceMatches, runtimeObservationFromTaskEvent, runtimeObservationRunFenceMatches, runtimeObservationTaskEventPayload } from "../runtime/runtimeObservation.js";
|
|
42
40
|
import { projectRuntimeTaskEvents } from "../runtime/runtimeProjection.js";
|
|
43
41
|
import { contextSnapshotRef } from "../context/contextSnapshot.js";
|
|
44
42
|
import { contextSnapshotDeltaRefIds, freezeRunContextSnapshot } from "../context/runContextPack.js";
|
|
@@ -90,8 +88,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
90
88
|
if (adapterId === null) {
|
|
91
89
|
return this.recordObsoleteCanonicalObservation(input, "driver-or-run-mismatch", now);
|
|
92
90
|
}
|
|
93
|
-
if (this.store.listEvents(taskId)
|
|
94
|
-
&& event.payload.semanticKey === input.semanticKey)))
|
|
91
|
+
if (hasPersistedRuntimeObservation(this.store.listEvents(taskId), input))
|
|
95
92
|
return "applied";
|
|
96
93
|
let outcome;
|
|
97
94
|
switch (input.kind) {
|
|
@@ -112,6 +109,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
112
109
|
launchId: input.fence.launchId,
|
|
113
110
|
nativeSessionId: input.fence.nativeSessionId,
|
|
114
111
|
turnId: input.fence.nativeTurnId,
|
|
112
|
+
attemptId: input.fence.receiptId,
|
|
115
113
|
runId: input.fence.runId,
|
|
116
114
|
summary: input.payload.summary ?? "Agent turn completed without a workflow outcome."
|
|
117
115
|
};
|
|
@@ -152,6 +150,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
152
150
|
launchId: input.fence.launchId,
|
|
153
151
|
nativeSessionId: input.fence.nativeSessionId,
|
|
154
152
|
turnId: input.fence.nativeTurnId,
|
|
153
|
+
attemptId: input.fence.receiptId,
|
|
155
154
|
runId: input.fence.runId,
|
|
156
155
|
summary: input.payload.summary ?? `Provider Turn failed: ${failureEvidence.code}.`,
|
|
157
156
|
providerStatus: "failed"
|
|
@@ -223,7 +222,8 @@ export class FileSchedulerStoreAdapter {
|
|
|
223
222
|
}
|
|
224
223
|
if (outcome === "applied"
|
|
225
224
|
&& input.kind === "activity.observed"
|
|
226
|
-
&& input.payload.activityId !== undefined
|
|
225
|
+
&& input.payload.activityId !== undefined
|
|
226
|
+
&& input.payload.usage === undefined) {
|
|
227
227
|
this.clearProviderRetryForProgress(input, "correlated-activity", now);
|
|
228
228
|
}
|
|
229
229
|
if (outcome === "applied")
|
|
@@ -251,9 +251,9 @@ export class FileSchedulerStoreAdapter {
|
|
|
251
251
|
}
|
|
252
252
|
validateCanonicalRunObservation(input, now) {
|
|
253
253
|
return this.store.transaction((store) => {
|
|
254
|
-
if (store.listEvents(input.fence.taskId)
|
|
255
|
-
&& event.payload.semanticKey === input.semanticKey)))
|
|
254
|
+
if (hasPersistedRuntimeObservation(store.listEvents(input.fence.taskId), input)) {
|
|
256
255
|
return "applied";
|
|
256
|
+
}
|
|
257
257
|
const run = store.getAgentRun(input.fence.taskId, input.fence.runId);
|
|
258
258
|
const active = store.getActiveAgentRun(input.fence.taskId, input.fence.roleName);
|
|
259
259
|
const sessions = store.getTaskRoleSessionSet(input.fence.taskId, input.fence.roleName);
|
|
@@ -388,10 +388,12 @@ export class FileSchedulerStoreAdapter {
|
|
|
388
388
|
"Yui preserved the Run because Session/host termination is not a workflow outcome.",
|
|
389
389
|
"Inspect native-child and mailbox facts, then explicitly continue, reset, or request user input."
|
|
390
390
|
].join(" ");
|
|
391
|
-
store
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
391
|
+
recordLeaderAttentionRequired(store, {
|
|
392
|
+
taskId: input.fence.taskId,
|
|
393
|
+
reason: "leader-runtime-detached",
|
|
394
|
+
payload: { message, runId: active.id },
|
|
395
|
+
now
|
|
396
|
+
});
|
|
395
397
|
}
|
|
396
398
|
else {
|
|
397
399
|
enqueueWork(store, {
|
|
@@ -410,22 +412,16 @@ export class FileSchedulerStoreAdapter {
|
|
|
410
412
|
this.store.transaction((store) => {
|
|
411
413
|
const taskId = input.fence.taskId;
|
|
412
414
|
const events = store.listEvents(taskId);
|
|
413
|
-
if (events
|
|
414
|
-
&& event.payload.semanticKey === input.semanticKey)))
|
|
415
|
+
if (hasPersistedRuntimeObservation(events, input))
|
|
415
416
|
return;
|
|
416
417
|
if (usageSnapshotIsSuperseded(events, input))
|
|
417
418
|
return;
|
|
418
|
-
const
|
|
419
|
-
const removable = [
|
|
420
|
-
...compactedRuntimeObservationIds(events, input),
|
|
421
|
-
...(confirmedActivity === null
|
|
422
|
-
? []
|
|
423
|
-
: compactedRuntimeObservationIds(events, confirmedActivity))
|
|
424
|
-
];
|
|
419
|
+
const removable = compactedRuntimeObservationIds(events, input);
|
|
425
420
|
if (removable.length > 0)
|
|
426
421
|
store.removeEvents(taskId, removable);
|
|
427
422
|
const observationEventId = store.nextEventId(taskId);
|
|
428
|
-
|
|
423
|
+
const observationEvent = createTaskEvent(observationEventId, taskId, RUNTIME_OBSERVATION_TASK_EVENT, runtimeObservationTaskEventPayload(input), now);
|
|
424
|
+
store.saveEvent(taskId, observationEvent);
|
|
429
425
|
if (input.kind === "native-work.snapshot"
|
|
430
426
|
&& input.payload.snapshotComplete === true
|
|
431
427
|
&& input.payload.observationQuality === "exact") {
|
|
@@ -503,14 +499,9 @@ export class FileSchedulerStoreAdapter {
|
|
|
503
499
|
// Route a Yui wake only when that parent is gone and another observer
|
|
504
500
|
// supplied the child's terminal fact.
|
|
505
501
|
if (role !== null && (session?.status === "stopped" || session?.status === "broken")) {
|
|
506
|
-
|
|
507
|
-
? { kind: "operator" }
|
|
508
|
-
: { kind: "role", taskId, roleName: "leader" }, "detached-native-subagent-terminal", now, [{ type: "run", taskId, id: input.fence.runId }]);
|
|
502
|
+
routeRoleEvent(store, observationEvent, role.name, "detached-native-subagent-terminal", now);
|
|
509
503
|
}
|
|
510
504
|
}
|
|
511
|
-
if (confirmedActivity !== null) {
|
|
512
|
-
store.saveEvent(taskId, createTaskEvent(store.nextEventId(taskId), taskId, RUNTIME_OBSERVATION_TASK_EVENT, runtimeObservationTaskEventPayload(confirmedActivity), now));
|
|
513
|
-
}
|
|
514
505
|
});
|
|
515
506
|
if (this.telemetry !== null && input.fence.runId !== undefined) {
|
|
516
507
|
try {
|
|
@@ -573,9 +564,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
573
564
|
withRuntimeEventTransaction(execute) {
|
|
574
565
|
return this.store.withRuntimeEventTransaction(execute);
|
|
575
566
|
}
|
|
576
|
-
getPresentationContext() {
|
|
577
|
-
return { timeZone: this.store.getConfig().timeZone };
|
|
578
|
-
}
|
|
579
567
|
listTasks() { return this.store.listTasks(); }
|
|
580
568
|
listActiveTaskIds() {
|
|
581
569
|
return [...this.store.listActiveTaskIds()].sort((left, right) => (left.localeCompare(right, undefined, { numeric: true })));
|
|
@@ -608,12 +596,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
608
596
|
});
|
|
609
597
|
});
|
|
610
598
|
}
|
|
611
|
-
rolloverTaskRoleSessionForContextBudget(input) {
|
|
612
|
-
return this.store.transaction((reader) => rolloverTaskRoleSessionForContextBudget(reader, input.taskId, input.roleName, { peakTokens: input.peakTokens, hardTokens: input.hardTokens }, input.now));
|
|
613
|
-
}
|
|
614
|
-
getContextBudget() {
|
|
615
|
-
return resolveContextBudget(this.store.getConfig().contextBudget);
|
|
616
|
-
}
|
|
617
599
|
listRoles(taskId) {
|
|
618
600
|
return this.store.listRoles(taskId).map((role) => mapRole(this.store, role));
|
|
619
601
|
}
|
|
@@ -646,6 +628,17 @@ export class FileSchedulerStoreAdapter {
|
|
|
646
628
|
adapterId: effectiveSession.effective.adapterId
|
|
647
629
|
};
|
|
648
630
|
}
|
|
631
|
+
markOperatorTurnStarted(now) {
|
|
632
|
+
this.store.transaction((store) => {
|
|
633
|
+
const sessions = store.getGlobalRoleSessionSet(SYSTEM_OPERATOR_ROLE);
|
|
634
|
+
if (sessions === null)
|
|
635
|
+
return;
|
|
636
|
+
const active = sessions.sessions[sessions.activeAgentId];
|
|
637
|
+
if (active === undefined || active.status !== "ready")
|
|
638
|
+
return;
|
|
639
|
+
store.saveGlobalRoleSessionSet(updateRoleAgentSessionStatus(sessions, sessions.activeAgentId, "running", now));
|
|
640
|
+
});
|
|
641
|
+
}
|
|
649
642
|
resolveExpiredInputRecommendations(now, taskIds) {
|
|
650
643
|
return this.store.transaction((store) => {
|
|
651
644
|
const selectedTaskIds = taskIds === undefined
|
|
@@ -760,10 +753,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
760
753
|
}, input.now);
|
|
761
754
|
store.saveEvent(task.id, event);
|
|
762
755
|
if (role.name === "leader") {
|
|
763
|
-
store
|
|
764
|
-
enqueueWork(store, { kind: "operator" }, "leader-run-stalled", input.now, [
|
|
765
|
-
{ type: "task", id: task.id }
|
|
766
|
-
]);
|
|
756
|
+
enqueueOperatorEvent(store, event, "leader-run-stalled", input.now);
|
|
767
757
|
}
|
|
768
758
|
else {
|
|
769
759
|
queueLeaderWakeup(store, task.id, wakeReason("role-run-stalled"), input.now);
|
|
@@ -815,7 +805,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
815
805
|
progressAt: input.progressAt,
|
|
816
806
|
kind: "durable-progress"
|
|
817
807
|
}, input.now));
|
|
818
|
-
clearMatchingLeaderStallAttention(store, task.id, run.id);
|
|
819
808
|
}
|
|
820
809
|
return existing && !recovered ? "already-recorded" : "recorded";
|
|
821
810
|
});
|
|
@@ -1161,17 +1150,11 @@ export class FileSchedulerStoreAdapter {
|
|
|
1161
1150
|
const unknown = markMailboxInputDeliveryUnknown(mailbox, attemptId, reason, now);
|
|
1162
1151
|
markSubmittingProviderTurnUnknown(store, target, mailbox.inputDelivery, now, reason);
|
|
1163
1152
|
store.saveWorkMailbox(unknown);
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
], {
|
|
1170
|
-
source: "input-router",
|
|
1171
|
-
dedupeKey: `input-delivery-unknown:${attemptId}`,
|
|
1172
|
-
deliveryMode: "followup",
|
|
1173
|
-
lane: "normal"
|
|
1174
|
-
});
|
|
1153
|
+
if (target.kind === "role") {
|
|
1154
|
+
const taskId = target.taskId;
|
|
1155
|
+
const event = createTaskEvent(store.nextEventId(taskId), taskId, "run.input-delivery-unknown", { attemptId, reason }, now);
|
|
1156
|
+
store.saveEvent(taskId, event);
|
|
1157
|
+
routeRoleEvent(store, event, target.roleName, "run-input-delivery-unknown", now);
|
|
1175
1158
|
}
|
|
1176
1159
|
return true;
|
|
1177
1160
|
});
|
|
@@ -1298,7 +1281,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
1298
1281
|
}
|
|
1299
1282
|
clearPendingWakeup(taskId) { this.store.clearPendingWakeup(taskId); }
|
|
1300
1283
|
getLeaderFailure(taskId) { return this.store.getLeaderFailure(taskId); }
|
|
1301
|
-
getOperatorNotification(taskId) { return this.store.getOperatorNotification(taskId); }
|
|
1302
1284
|
saveLeaderDispatch(input) {
|
|
1303
1285
|
return this.store.transaction((store) => {
|
|
1304
1286
|
const task = store.getTask(input.task.id);
|
|
@@ -1378,7 +1360,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
1378
1360
|
}, "running", input.now);
|
|
1379
1361
|
}
|
|
1380
1362
|
store.clearLeaderFailure(input.task.id);
|
|
1381
|
-
store.clearOperatorNotification(input.task.id);
|
|
1382
1363
|
return "claimed";
|
|
1383
1364
|
});
|
|
1384
1365
|
}
|
|
@@ -1537,11 +1518,12 @@ export class FileSchedulerStoreAdapter {
|
|
|
1537
1518
|
roleName: input.roleName
|
|
1538
1519
|
}), RUNTIME_CLEANUP_REQUIRED_REASON, input.now, [{ type: "task", id: input.taskId }]);
|
|
1539
1520
|
const terminal = result.run;
|
|
1540
|
-
|
|
1521
|
+
const deliveryFailureEvent = createTaskEvent(store.nextEventId(input.taskId), input.taskId, "runtime.role-delivery-failed", {
|
|
1541
1522
|
runId: terminal.id,
|
|
1542
1523
|
roleName: input.roleName,
|
|
1543
1524
|
outcome: terminal.status
|
|
1544
|
-
}, input.now)
|
|
1525
|
+
}, input.now);
|
|
1526
|
+
store.saveEvent(input.taskId, deliveryFailureEvent);
|
|
1545
1527
|
if (terminal.purpose === "execution" && terminal.workItemId !== undefined) {
|
|
1546
1528
|
const item = store.getWorkItem(input.taskId, terminal.workItemId);
|
|
1547
1529
|
if (item !== null && ![
|
|
@@ -1553,12 +1535,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1553
1535
|
}
|
|
1554
1536
|
}
|
|
1555
1537
|
if (input.roleName !== "leader") {
|
|
1556
|
-
|
|
1557
|
-
{ type: "run", taskId: input.taskId, id: terminal.id },
|
|
1558
|
-
...(terminal.workItemId === undefined
|
|
1559
|
-
? []
|
|
1560
|
-
: [{ type: "work-item", taskId: input.taskId, id: terminal.workItemId }])
|
|
1561
|
-
]);
|
|
1538
|
+
routeRoleEvent(store, deliveryFailureEvent, input.roleName, terminal.purpose === "review" ? "review-failed" : "role-run-failed", input.now);
|
|
1562
1539
|
}
|
|
1563
1540
|
else {
|
|
1564
1541
|
const failedRole = store.getRole(input.taskId, input.roleName);
|
|
@@ -1566,11 +1543,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1566
1543
|
store.saveRole(input.taskId, updateRoleStatus(failedRole, "failed", input.now));
|
|
1567
1544
|
}
|
|
1568
1545
|
store.saveLeaderFailure(recordLeaderFailure(input.taskId, session?.nativeSessionId ?? "(unregistered)", summary, input.now, store.getLeaderFailure(input.taskId)));
|
|
1569
|
-
store
|
|
1570
|
-
enqueueWork(store, { kind: "operator" }, "leader-run-failed", input.now, [
|
|
1571
|
-
{ type: "task", id: input.taskId },
|
|
1572
|
-
{ type: "run", taskId: input.taskId, id: terminal.id }
|
|
1573
|
-
]);
|
|
1546
|
+
routeRoleEvent(store, deliveryFailureEvent, input.roleName, "leader-run-failed", input.now);
|
|
1574
1547
|
}
|
|
1575
1548
|
return "failed";
|
|
1576
1549
|
});
|
|
@@ -1606,10 +1579,15 @@ export class FileSchedulerStoreAdapter {
|
|
|
1606
1579
|
breakTaskSessionIfPresent(store, input.task.id, role.name, active.effective.agentId, input.now);
|
|
1607
1580
|
const failure = recordLeaderFailure(input.task.id, input.failure.nativeSessionId, input.failure.message, input.now, store.getLeaderFailure(input.task.id));
|
|
1608
1581
|
store.saveLeaderFailure(failure);
|
|
1609
|
-
store
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1582
|
+
recordLeaderAttentionRequired(store, {
|
|
1583
|
+
taskId: input.task.id,
|
|
1584
|
+
reason: "leader-recovery-failed",
|
|
1585
|
+
payload: {
|
|
1586
|
+
message: input.failure.message,
|
|
1587
|
+
runId: active.id
|
|
1588
|
+
},
|
|
1589
|
+
now: input.now
|
|
1590
|
+
});
|
|
1613
1591
|
return "failed";
|
|
1614
1592
|
});
|
|
1615
1593
|
}
|
|
@@ -1661,7 +1639,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
1661
1639
|
}, input.now);
|
|
1662
1640
|
if (terminal.disposition !== "applied")
|
|
1663
1641
|
return "state-changed";
|
|
1664
|
-
clearMatchingLeaderStallAttention(store, task.id, currentRun.id);
|
|
1665
1642
|
store.saveRole(task.id, updateRoleStatus(role, "exited", input.now));
|
|
1666
1643
|
stopTaskSessionIfPresent(store, task.id, role.name, currentRun.effective.agentId, input.now);
|
|
1667
1644
|
queueLeaderWakeup(store, task.id, wakeReason("review-failed"), input.now);
|
|
@@ -1679,7 +1656,6 @@ export class FileSchedulerStoreAdapter {
|
|
|
1679
1656
|
}
|
|
1680
1657
|
}
|
|
1681
1658
|
store.saveAgentRun(failAgentRun(currentRun, input.summary, input.now));
|
|
1682
|
-
clearMatchingLeaderStallAttention(store, task.id, currentRun.id);
|
|
1683
1659
|
store.clearActiveAgentRun(task.id, role.name);
|
|
1684
1660
|
clearTaskRoleRunInFlight(store, role, currentRun, input.now);
|
|
1685
1661
|
if (mailbox !== null && processing !== null && processing !== undefined) {
|
|
@@ -1945,6 +1921,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1945
1921
|
const role = requireRole(store, input.taskId, input.roleName);
|
|
1946
1922
|
let sessions = store.getTaskRoleSessionSet(input.taskId, input.roleName)
|
|
1947
1923
|
?? createRoleSessionSet({ scope: "task", taskId: input.taskId, roleName: input.roleName }, input.agentId, now);
|
|
1924
|
+
const canonicalTurnId = canonicalStructuredProviderTurnId(sessions, input.turnId, input.attemptId);
|
|
1948
1925
|
const existing = sessions.sessions[input.agentId];
|
|
1949
1926
|
const owner = {
|
|
1950
1927
|
scope: "task",
|
|
@@ -1961,7 +1938,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
1961
1938
|
throw new Error("Runtime turn completion does not match the effective launch identity.");
|
|
1962
1939
|
}
|
|
1963
1940
|
if (effectiveExisting !== undefined
|
|
1964
|
-
&& hasRecentTurnId(effectiveExisting.recentCompletedTurnIds,
|
|
1941
|
+
&& hasRecentTurnId(effectiveExisting.recentCompletedTurnIds, canonicalTurnId)) {
|
|
1965
1942
|
return { session: effectiveExisting, duplicate: true };
|
|
1966
1943
|
}
|
|
1967
1944
|
if (sessions.inFlight === null
|
|
@@ -1998,9 +1975,9 @@ export class FileSchedulerStoreAdapter {
|
|
|
1998
1975
|
: sessions.inFlight === null ? idleStatus : "running",
|
|
1999
1976
|
effective
|
|
2000
1977
|
}, now);
|
|
2001
|
-
sessions = settleStructuredProviderTurn(sessions,
|
|
1978
|
+
sessions = settleStructuredProviderTurn(sessions, canonicalTurnId, input.providerStatus ?? "completed", now);
|
|
2002
1979
|
if (sessions.inFlight === null) {
|
|
2003
|
-
sessions = rememberRoleAgentCompletedTurn(sessions, input.agentId, input.nativeSessionId,
|
|
1980
|
+
sessions = rememberRoleAgentCompletedTurn(sessions, input.agentId, input.nativeSessionId, canonicalTurnId, now);
|
|
2004
1981
|
store.saveTaskRoleSessionSet(sessions);
|
|
2005
1982
|
completeRuntimeHookReservation(store, owner, input.launchId);
|
|
2006
1983
|
// The in-flight fence was cleared by an in-place provider retry
|
|
@@ -2024,7 +2001,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
2024
2001
|
sessions = recordTaskRoleTurnBoundary(sessions, {
|
|
2025
2002
|
agentId: input.agentId,
|
|
2026
2003
|
nativeSessionId: input.nativeSessionId,
|
|
2027
|
-
turnId:
|
|
2004
|
+
turnId: canonicalTurnId
|
|
2028
2005
|
}, now);
|
|
2029
2006
|
store.saveTaskRoleSessionSet(sessions);
|
|
2030
2007
|
completeRuntimeHookReservation(store, owner, input.launchId);
|
|
@@ -2109,27 +2086,16 @@ export class FileSchedulerStoreAdapter {
|
|
|
2109
2086
|
if (role !== null) {
|
|
2110
2087
|
store.saveRole(input.taskId, updateRoleStatus(role, input.roleName === "leader" ? "failed" : "idle", now));
|
|
2111
2088
|
}
|
|
2112
|
-
|
|
2089
|
+
const failureEvent = createTaskEvent(store.nextEventId(input.taskId), input.taskId, "runtime.turn-failed", {
|
|
2113
2090
|
eventId: input.eventId,
|
|
2114
2091
|
runId: input.runId,
|
|
2115
2092
|
roleName: input.roleName,
|
|
2116
2093
|
outcome: terminal.status
|
|
2117
|
-
}, now)
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
? []
|
|
2123
|
-
: [{ type: "work-item", taskId: input.taskId, id: terminal.workItemId }])
|
|
2124
|
-
]);
|
|
2125
|
-
}
|
|
2126
|
-
else {
|
|
2127
|
-
store.saveOperatorNotification(createLeaderRecoveryNotification(input.taskId, summary, now, store.getOperatorNotification(input.taskId)));
|
|
2128
|
-
enqueueWork(store, { kind: "operator" }, "leader-run-failed", now, [
|
|
2129
|
-
{ type: "task", id: input.taskId },
|
|
2130
|
-
{ type: "run", taskId: input.taskId, id: terminal.id }
|
|
2131
|
-
]);
|
|
2132
|
-
}
|
|
2094
|
+
}, now);
|
|
2095
|
+
store.saveEvent(input.taskId, failureEvent);
|
|
2096
|
+
routeRoleEvent(store, failureEvent, input.roleName, input.roleName !== "leader" && before.purpose === "review"
|
|
2097
|
+
? "review-failed"
|
|
2098
|
+
: input.roleName === "leader" ? "leader-run-failed" : "role-run-failed", now);
|
|
2133
2099
|
return { disposition: "applied", runId: input.runId };
|
|
2134
2100
|
});
|
|
2135
2101
|
}
|
|
@@ -2222,14 +2188,12 @@ export class FileSchedulerStoreAdapter {
|
|
|
2222
2188
|
if (blocked.outcome === "exhausted")
|
|
2223
2189
|
return null;
|
|
2224
2190
|
store.saveAgentRun(withProviderRetry(run, blocked.retry));
|
|
2225
|
-
this.recordProviderRetryClassified(store, input, classification.errorClass, {
|
|
2191
|
+
const classifiedEvent = this.recordProviderRetryClassified(store, input, classification.errorClass, {
|
|
2226
2192
|
wouldRetry: "false",
|
|
2227
2193
|
shadow: "false",
|
|
2228
2194
|
note: "delivery-deduplication-unproven"
|
|
2229
2195
|
}, now);
|
|
2230
|
-
|
|
2231
|
-
? { kind: "operator" }
|
|
2232
|
-
: { kind: "role", taskId: input.taskId, roleName: "leader" }, "provider-delivery-uncertain", now, [{ type: "run", taskId: input.taskId, id: input.runId }]);
|
|
2196
|
+
routeRoleEvent(store, classifiedEvent, input.roleName, "provider-delivery-uncertain", now);
|
|
2233
2197
|
return { disposition: "applied", runId: input.runId };
|
|
2234
2198
|
}
|
|
2235
2199
|
}
|
|
@@ -2314,22 +2278,14 @@ export class FileSchedulerStoreAdapter {
|
|
|
2314
2278
|
if (retryDecision.outcome === "exhausted")
|
|
2315
2279
|
return null;
|
|
2316
2280
|
store.saveAgentRun(withProviderRetry(run, retryDecision.retry));
|
|
2317
|
-
this.recordProviderRetryClassified(store, input, "policy-denied", {
|
|
2281
|
+
const classifiedEvent = this.recordProviderRetryClassified(store, input, "policy-denied", {
|
|
2318
2282
|
wouldRetry: "false",
|
|
2319
2283
|
shadow: "false",
|
|
2320
2284
|
note: "policy-denied-blocker"
|
|
2321
2285
|
}, now);
|
|
2322
|
-
|
|
2323
|
-
|
|
2324
|
-
|
|
2325
|
-
else {
|
|
2326
|
-
const message = `Leader Run ${input.runId} is blocked by Provider policy: ${summary}`;
|
|
2327
|
-
store.saveOperatorNotification(createLeaderRecoveryNotification(input.taskId, message, now, store.getOperatorNotification(input.taskId)));
|
|
2328
|
-
enqueueWork(store, { kind: "operator" }, "leader-provider-policy-blocked", now, [
|
|
2329
|
-
{ type: "task", id: input.taskId },
|
|
2330
|
-
{ type: "run", taskId: input.taskId, id: input.runId }
|
|
2331
|
-
]);
|
|
2332
|
-
}
|
|
2286
|
+
routeRoleEvent(store, classifiedEvent, input.roleName, input.roleName === "leader"
|
|
2287
|
+
? "leader-provider-policy-blocked"
|
|
2288
|
+
: "provider-policy-blocked", now);
|
|
2333
2289
|
return { disposition: "applied", runId: input.runId };
|
|
2334
2290
|
}
|
|
2335
2291
|
/**
|
|
@@ -2355,7 +2311,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
2355
2311
|
return null;
|
|
2356
2312
|
store.saveAgentRun(withProviderRetry(run, decision.retry));
|
|
2357
2313
|
const driver = this.drivers.requireByAdapterId(input.adapterId);
|
|
2358
|
-
|
|
2314
|
+
const capacityEvent = createTaskEvent(store.nextEventId(input.taskId), input.taskId, "runtime.context-capacity-failure", {
|
|
2359
2315
|
eventId: input.eventId,
|
|
2360
2316
|
runId: input.runId,
|
|
2361
2317
|
roleName: input.roleName,
|
|
@@ -2366,15 +2322,14 @@ export class FileSchedulerStoreAdapter {
|
|
|
2366
2322
|
action: driver.capabilities.lifecycle.compaction === "native-explicit"
|
|
2367
2323
|
? "native-explicit-compaction-required"
|
|
2368
2324
|
: "await-provider-native-recovery"
|
|
2369
|
-
}, now)
|
|
2325
|
+
}, now);
|
|
2326
|
+
store.saveEvent(input.taskId, capacityEvent);
|
|
2370
2327
|
this.recordProviderRetryClassified(store, input, "context-capacity", {
|
|
2371
2328
|
wouldRetry: "false",
|
|
2372
2329
|
shadow: "false",
|
|
2373
2330
|
note: "provider-native-capacity-recovery-only"
|
|
2374
2331
|
}, now);
|
|
2375
|
-
|
|
2376
|
-
? { kind: "operator" }
|
|
2377
|
-
: { kind: "role", taskId: input.taskId, roleName: "leader" }, "provider-context-capacity-recovery-required", now, [{ type: "run", taskId: input.taskId, id: input.runId }]);
|
|
2332
|
+
routeRoleEvent(store, capacityEvent, input.roleName, "provider-context-capacity-recovery-required", now);
|
|
2378
2333
|
return { disposition: "applied", runId: input.runId };
|
|
2379
2334
|
}
|
|
2380
2335
|
applyNativeSessionRecoverabilityBlock(store, input, summary, now) {
|
|
@@ -2393,27 +2348,28 @@ export class FileSchedulerStoreAdapter {
|
|
|
2393
2348
|
if (decision.outcome === "exhausted")
|
|
2394
2349
|
return null;
|
|
2395
2350
|
store.saveAgentRun(withProviderRetry(run, decision.retry));
|
|
2396
|
-
|
|
2351
|
+
const recoverabilityEvent = createTaskEvent(store.nextEventId(input.taskId), input.taskId, "runtime.native-session-recoverability-unproven", {
|
|
2397
2352
|
eventId: input.eventId,
|
|
2398
2353
|
runId: input.runId,
|
|
2399
2354
|
roleName: input.roleName,
|
|
2400
2355
|
launchId: input.launchId,
|
|
2401
2356
|
nativeSessionId: input.nativeSessionId,
|
|
2402
2357
|
requiredEvidence: "exact-native-resume-outcome"
|
|
2403
|
-
}, now)
|
|
2404
|
-
|
|
2405
|
-
|
|
2406
|
-
: { kind: "role", taskId: input.taskId, roleName: "leader" }, "native-session-recoverability-unproven", now, [{ type: "run", taskId: input.taskId, id: input.runId }]);
|
|
2358
|
+
}, now);
|
|
2359
|
+
store.saveEvent(input.taskId, recoverabilityEvent);
|
|
2360
|
+
routeRoleEvent(store, recoverabilityEvent, input.roleName, "native-session-recoverability-unproven", now);
|
|
2407
2361
|
return { disposition: "applied", runId: input.runId };
|
|
2408
2362
|
}
|
|
2409
2363
|
recordProviderRetryClassified(store, input, errorClass, extra, now) {
|
|
2410
|
-
|
|
2364
|
+
const event = createTaskEvent(store.nextEventId(input.taskId), input.taskId, "runtime.provider-retry-classified", {
|
|
2411
2365
|
eventId: input.eventId,
|
|
2412
2366
|
runId: input.runId,
|
|
2413
2367
|
roleName: input.roleName,
|
|
2414
2368
|
errorClass,
|
|
2415
2369
|
...extra
|
|
2416
|
-
}, now)
|
|
2370
|
+
}, now);
|
|
2371
|
+
store.saveEvent(input.taskId, event);
|
|
2372
|
+
return event;
|
|
2417
2373
|
}
|
|
2418
2374
|
/**
|
|
2419
2375
|
* Issue 04 durable retry timer: lists active Runs whose in-place retry is
|
|
@@ -2580,18 +2536,17 @@ export class FileSchedulerStoreAdapter {
|
|
|
2580
2536
|
return;
|
|
2581
2537
|
}
|
|
2582
2538
|
store.saveAgentRun(withProviderRetry(run, deferred));
|
|
2583
|
-
|
|
2584
|
-
&& event.payload.runId === run.id))
|
|
2585
|
-
|
|
2539
|
+
let attentionEvent = store.listEvents(entry.taskId).find((event) => (event.type === "runtime.provider-retry-native-resume-unproven"
|
|
2540
|
+
&& event.payload.runId === run.id));
|
|
2541
|
+
if (attentionEvent === undefined) {
|
|
2542
|
+
attentionEvent = createTaskEvent(store.nextEventId(entry.taskId), entry.taskId, "runtime.provider-retry-native-resume-unproven", {
|
|
2586
2543
|
runId: run.id,
|
|
2587
2544
|
consecutiveFailures: String(run.providerRetry.consecutiveFailures),
|
|
2588
2545
|
dispatchedRetries: String(run.providerRetry.dispatchedRetries)
|
|
2589
|
-
}, now)
|
|
2546
|
+
}, now);
|
|
2547
|
+
store.saveEvent(entry.taskId, attentionEvent);
|
|
2590
2548
|
}
|
|
2591
|
-
|
|
2592
|
-
{ type: "task", id: entry.taskId },
|
|
2593
|
-
{ type: "run", taskId: entry.taskId, id: run.id }
|
|
2594
|
-
]);
|
|
2549
|
+
routeRoleEvent(store, attentionEvent, entry.roleName, "provider-retry-native-resume-unproven", now);
|
|
2595
2550
|
return;
|
|
2596
2551
|
}
|
|
2597
2552
|
if (hasUnsettledRuntimeOperations(store, entry.taskId, run.id)) {
|
|
@@ -3027,7 +2982,8 @@ export class FileSchedulerStoreAdapter {
|
|
|
3027
2982
|
&& !runtimeHookMatchesReservation(store, owner, input.launchId)) {
|
|
3028
2983
|
throw new Error("Runtime turn completion does not match the global launch reservation.");
|
|
3029
2984
|
}
|
|
3030
|
-
const
|
|
2985
|
+
const nativeSessionId = globalCompletionNativeSessionId(existing, input);
|
|
2986
|
+
const effectiveExisting = nativeTransitionExisting(store, owner, existing, nativeSessionId, input.launchId, "Runtime turn completion conflicts with the fixed global Role session.");
|
|
3031
2987
|
const effective = globalSessionEffective(role, effectiveExisting);
|
|
3032
2988
|
if (effective.agentId !== input.agentId || effective.adapterId !== input.adapterId) {
|
|
3033
2989
|
throw new Error("Runtime turn completion does not match the effective global launch identity.");
|
|
@@ -3039,7 +2995,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
3039
2995
|
current = recordRoleAgentSession(current, {
|
|
3040
2996
|
agentId: input.agentId,
|
|
3041
2997
|
adapterId: input.adapterId,
|
|
3042
|
-
nativeSessionId
|
|
2998
|
+
nativeSessionId,
|
|
3043
2999
|
...(input.launchId === undefined ? {} : { launchId: input.launchId }),
|
|
3044
3000
|
title: effectiveExisting?.title ?? input.title,
|
|
3045
3001
|
preview: effectiveExisting?.preview ?? (input.summary === undefined ? undefined : sessionPreview(input.summary)),
|
|
@@ -3047,7 +3003,7 @@ export class FileSchedulerStoreAdapter {
|
|
|
3047
3003
|
status: completedStatus,
|
|
3048
3004
|
effective
|
|
3049
3005
|
}, now);
|
|
3050
|
-
current = rememberRoleAgentCompletedTurn(current, input.agentId,
|
|
3006
|
+
current = rememberRoleAgentCompletedTurn(current, input.agentId, nativeSessionId, input.turnId, now);
|
|
3051
3007
|
store.saveGlobalRoleSessionSet(current);
|
|
3052
3008
|
completeRuntimeHookReservation(store, owner, input.launchId);
|
|
3053
3009
|
return current.sessions[input.agentId];
|
|
@@ -3066,9 +3022,10 @@ export class FileSchedulerStoreAdapter {
|
|
|
3066
3022
|
if (existing === undefined
|
|
3067
3023
|
&& !runtimeHookMatchesReservation(this.store, owner, input.launchId))
|
|
3068
3024
|
return "obsolete";
|
|
3025
|
+
const nativeSessionId = globalCompletionNativeSessionId(existing, input);
|
|
3069
3026
|
let effectiveExisting;
|
|
3070
3027
|
try {
|
|
3071
|
-
effectiveExisting = nativeTransitionExisting(this.store, owner, existing,
|
|
3028
|
+
effectiveExisting = nativeTransitionExisting(this.store, owner, existing, nativeSessionId, input.launchId, "Runtime turn completion conflicts with the fixed global Role session.");
|
|
3072
3029
|
}
|
|
3073
3030
|
catch {
|
|
3074
3031
|
return "obsolete";
|
|
@@ -3369,7 +3326,7 @@ function finalizeProviderRetryDeadline(store, run, summary, reason, now) {
|
|
|
3369
3326
|
if (terminalization.disposition !== "applied" || terminalization.run === null)
|
|
3370
3327
|
return false;
|
|
3371
3328
|
const terminal = terminalization.run;
|
|
3372
|
-
|
|
3329
|
+
const exhaustionEvent = createTaskEvent(store.nextEventId(run.taskId), run.taskId, "runtime.provider-retry-exhausted", {
|
|
3373
3330
|
runId: run.id,
|
|
3374
3331
|
roleName: run.roleName,
|
|
3375
3332
|
episodeId: retry.episodeId,
|
|
@@ -3377,7 +3334,8 @@ function finalizeProviderRetryDeadline(store, run, summary, reason, now) {
|
|
|
3377
3334
|
consecutiveFailures: String(retry.consecutiveFailures),
|
|
3378
3335
|
dispatchedRetries: String(retry.dispatchedRetries),
|
|
3379
3336
|
elapsedMs: String(Math.max(0, now.getTime() - Date.parse(retry.firstFailureAt)))
|
|
3380
|
-
}, now)
|
|
3337
|
+
}, now);
|
|
3338
|
+
store.saveEvent(run.taskId, exhaustionEvent);
|
|
3381
3339
|
if (terminal.purpose === "execution" && terminal.workItemId !== undefined) {
|
|
3382
3340
|
const item = store.getWorkItem(run.taskId, terminal.workItemId);
|
|
3383
3341
|
if (item !== null
|
|
@@ -3392,15 +3350,8 @@ function finalizeProviderRetryDeadline(store, run, summary, reason, now) {
|
|
|
3392
3350
|
}
|
|
3393
3351
|
if (run.roleName === "leader") {
|
|
3394
3352
|
store.saveLeaderFailure(recordLeaderFailure(run.taskId, retry.nativeSessionId ?? "(unproven)", summary, now, store.getLeaderFailure(run.taskId)));
|
|
3395
|
-
store.saveOperatorNotification(createLeaderRecoveryNotification(run.taskId, summary, now, store.getOperatorNotification(run.taskId)));
|
|
3396
|
-
enqueueWork(store, { kind: "operator" }, "provider-retry-exhausted", now, [
|
|
3397
|
-
{ type: "task", id: run.taskId },
|
|
3398
|
-
{ type: "run", taskId: run.taskId, id: run.id }
|
|
3399
|
-
]);
|
|
3400
|
-
}
|
|
3401
|
-
else {
|
|
3402
|
-
enqueueWork(store, { kind: "role", taskId: run.taskId, roleName: "leader" }, "provider-retry-exhausted", now, [{ type: "run", taskId: run.taskId, id: run.id }]);
|
|
3403
3353
|
}
|
|
3354
|
+
routeRoleEvent(store, exhaustionEvent, run.roleName, "provider-retry-exhausted", now);
|
|
3404
3355
|
return true;
|
|
3405
3356
|
}
|
|
3406
3357
|
function preparedDeliveryFailureSessionFenceMatches(sessions, session, input, expectedReceiptId) {
|
|
@@ -3475,7 +3426,7 @@ function recordTaskRuntimeNativeSession(store, input, now) {
|
|
|
3475
3426
|
const effective = input.effective === undefined
|
|
3476
3427
|
? resolvedEffective
|
|
3477
3428
|
: validateEffectiveLaunchSnapshot(input.effective);
|
|
3478
|
-
if (!
|
|
3429
|
+
if (!effectiveLaunchSnapshotsCompatibleForTaskSession(resolvedEffective, effective)) {
|
|
3479
3430
|
throw new Error("Reserved native Session effective launch changed before persistence.");
|
|
3480
3431
|
}
|
|
3481
3432
|
if (effective.agentId !== input.agentId || effective.adapterId !== input.adapterId) {
|
|
@@ -3536,6 +3487,17 @@ function nativeTransitionExisting(store, owner, existing, nativeSessionId, launc
|
|
|
3536
3487
|
}
|
|
3537
3488
|
throw new Error(conflictMessage);
|
|
3538
3489
|
}
|
|
3490
|
+
function globalCompletionNativeSessionId(existing, input) {
|
|
3491
|
+
// Codex app-server and its notify Hook expose different native IDs for the
|
|
3492
|
+
// same launch. The launch generation remains the authoritative fence.
|
|
3493
|
+
return input.adapterId === "codex"
|
|
3494
|
+
&& input.launchId !== undefined
|
|
3495
|
+
&& existing?.launchId === input.launchId
|
|
3496
|
+
&& existing.agentId === input.agentId
|
|
3497
|
+
&& existing.adapterId === input.adapterId
|
|
3498
|
+
? existing.nativeSessionId
|
|
3499
|
+
: input.nativeSessionId;
|
|
3500
|
+
}
|
|
3539
3501
|
function mapRole(store, role) {
|
|
3540
3502
|
const binding = activeRoleAgentBinding(role);
|
|
3541
3503
|
const item = store.listWorkItems(role.taskId).find((candidate) => (candidate.assignee === role.name
|
|
@@ -3581,8 +3543,7 @@ function taskSessionEffective(store, taskId, roleName, agentId, existing) {
|
|
|
3581
3543
|
throw new Error(`Native Session registration does not match the effective Run Agent: ${taskId}/${roleName}.`);
|
|
3582
3544
|
}
|
|
3583
3545
|
if (existing !== undefined) {
|
|
3584
|
-
|
|
3585
|
-
if (!effectiveLaunchSnapshotsCompatibleForTaskMain(existing.effective, active.effective, workspace)) {
|
|
3546
|
+
if (!effectiveLaunchSnapshotsCompatibleForTaskSession(existing.effective, active.effective)) {
|
|
3586
3547
|
throw new Error(`Native Session effective launch does not match the active Run: ${taskId}/${roleName}.`);
|
|
3587
3548
|
}
|
|
3588
3549
|
return existing.effective;
|
|
@@ -3788,11 +3749,10 @@ function runtimeObservationTelemetryEntry(input) {
|
|
|
3788
3749
|
};
|
|
3789
3750
|
}
|
|
3790
3751
|
/**
|
|
3791
|
-
*
|
|
3792
|
-
*
|
|
3793
|
-
*
|
|
3794
|
-
*
|
|
3795
|
-
* proven before an unchanged later snapshot arrived.
|
|
3752
|
+
* Usage observations and incomplete request boundaries are authoritative
|
|
3753
|
+
* read-only history: retain each one so consecutive deltas remain projectable.
|
|
3754
|
+
* Other activity observations are a current explicit boundary and may replace
|
|
3755
|
+
* their predecessor. Token evidence never becomes lifecycle activity.
|
|
3796
3756
|
*/
|
|
3797
3757
|
function compactedRuntimeObservationIds(events, incoming) {
|
|
3798
3758
|
const existing = events.flatMap((event) => {
|
|
@@ -3809,9 +3769,10 @@ function compactedRuntimeObservationIds(events, incoming) {
|
|
|
3809
3769
|
});
|
|
3810
3770
|
const remove = ({ observation }) => {
|
|
3811
3771
|
if (incoming.kind === "activity.observed") {
|
|
3772
|
+
if (isRuntimeTokenEvidence(incoming))
|
|
3773
|
+
return false;
|
|
3812
3774
|
return observation.kind === "activity.observed"
|
|
3813
|
-
&& (observation
|
|
3814
|
-
=== (incoming.payload.usage === undefined);
|
|
3775
|
+
&& !isRuntimeTokenEvidence(observation);
|
|
3815
3776
|
}
|
|
3816
3777
|
if (incoming.kind === "operation.started") {
|
|
3817
3778
|
return (observation.kind === "operation.started"
|
|
@@ -3854,7 +3815,6 @@ function recordStructuredProviderAcceptance(sessions, input, now) {
|
|
|
3854
3815
|
if (current === null || attemptId === undefined || turnId === undefined)
|
|
3855
3816
|
return sessions;
|
|
3856
3817
|
if (current.turn?.attemptId === attemptId
|
|
3857
|
-
&& current.turn.turnId === turnId
|
|
3858
3818
|
&& ["accepted", "running", "completed", "failed", "cancelled"].includes(current.turn.status))
|
|
3859
3819
|
return sessions;
|
|
3860
3820
|
const binding = acceptProviderTurn(current, {
|
|
@@ -3864,6 +3824,20 @@ function recordStructuredProviderAcceptance(sessions, input, now) {
|
|
|
3864
3824
|
});
|
|
3865
3825
|
return updateTaskRoleProviderRuntime(sessions, binding, now);
|
|
3866
3826
|
}
|
|
3827
|
+
/**
|
|
3828
|
+
* A Provider may expose the same accepted input through more than one exact
|
|
3829
|
+
* observation surface. Preserve the first native Turn identity bound to the
|
|
3830
|
+
* durable attempt; a later transport alias may settle that same attempt but
|
|
3831
|
+
* must neither replace nor strand the canonical identity.
|
|
3832
|
+
*/
|
|
3833
|
+
function canonicalStructuredProviderTurnId(sessions, observedTurnId, attemptId) {
|
|
3834
|
+
const turn = sessions.providerBinding?.turn;
|
|
3835
|
+
return attemptId !== undefined
|
|
3836
|
+
&& turn?.attemptId === attemptId
|
|
3837
|
+
&& turn.turnId !== undefined
|
|
3838
|
+
? turn.turnId
|
|
3839
|
+
: observedTurnId;
|
|
3840
|
+
}
|
|
3867
3841
|
function bindOrSupersedeProviderRuntime(sessions, input, now) {
|
|
3868
3842
|
if (sessions.inFlight === null)
|
|
3869
3843
|
return sessions;
|
|
@@ -3931,16 +3905,14 @@ function deferUnprovenProviderRecovery(store, run, roleName, reason, now) {
|
|
|
3931
3905
|
return;
|
|
3932
3906
|
}
|
|
3933
3907
|
store.saveAgentRun(withProviderRetry(run, deferred));
|
|
3934
|
-
|
|
3908
|
+
const attentionEvent = createTaskEvent(store.nextEventId(run.taskId), run.taskId, "runtime.provider-recovery-attention", {
|
|
3935
3909
|
runId: run.id,
|
|
3936
3910
|
roleName,
|
|
3937
3911
|
reason,
|
|
3938
3912
|
nextAttemptAt: deferred.nextAttemptAt ?? ""
|
|
3939
|
-
}, now)
|
|
3940
|
-
|
|
3941
|
-
|
|
3942
|
-
{ type: "run", taskId: run.taskId, id: run.id }
|
|
3943
|
-
]);
|
|
3913
|
+
}, now);
|
|
3914
|
+
store.saveEvent(run.taskId, attentionEvent);
|
|
3915
|
+
routeRoleEvent(store, attentionEvent, roleName, "provider-recovery-attention", now);
|
|
3944
3916
|
}
|
|
3945
3917
|
function markSubmittingProviderTurnUnknown(store, target, delivery, now, reason) {
|
|
3946
3918
|
if (target.kind !== "role"
|
|
@@ -3974,39 +3946,6 @@ function settleStructuredProviderTurn(sessions, turnId, status, now) {
|
|
|
3974
3946
|
settledAt: now.toISOString()
|
|
3975
3947
|
}), now);
|
|
3976
3948
|
}
|
|
3977
|
-
function confirmedUsageActivityObservation(events, incoming) {
|
|
3978
|
-
const usage = incoming.payload.usage;
|
|
3979
|
-
if (incoming.kind !== "activity.observed" || usage === undefined)
|
|
3980
|
-
return null;
|
|
3981
|
-
const previous = events
|
|
3982
|
-
.map(runtimeObservationFromTaskEvent)
|
|
3983
|
-
.filter((observation) => (observation !== null
|
|
3984
|
-
&& observation.kind === "activity.observed"
|
|
3985
|
-
&& observation.payload.usage !== undefined
|
|
3986
|
-
&& runtimeObservationFenceMatches(observation.fence, incoming.fence)))
|
|
3987
|
-
.sort(compareCanonicalObservationOrder)
|
|
3988
|
-
.at(-1)?.payload.usage;
|
|
3989
|
-
if (previous === undefined
|
|
3990
|
-
|| usage.inputTokens + usage.outputTokens
|
|
3991
|
-
<= previous.inputTokens + previous.outputTokens)
|
|
3992
|
-
return null;
|
|
3993
|
-
const digest = createHash("sha256")
|
|
3994
|
-
.update(`confirmed-runtime-activity\0${incoming.eventId}`)
|
|
3995
|
-
.digest("hex");
|
|
3996
|
-
return createRuntimeObservation({
|
|
3997
|
-
schemaVersion: 2,
|
|
3998
|
-
eventId: `runtime-activity-${digest}`,
|
|
3999
|
-
semanticKey: `runtime-activity-${digest}`,
|
|
4000
|
-
kind: "activity.observed",
|
|
4001
|
-
authority: "controller",
|
|
4002
|
-
receivedAt: incoming.receivedAt,
|
|
4003
|
-
...(incoming.observedAt === undefined ? {} : { observedAt: incoming.observedAt }),
|
|
4004
|
-
...(incoming.sequence === undefined ? {} : { sequence: incoming.sequence }),
|
|
4005
|
-
...(incoming.ordinal === undefined ? {} : { ordinal: incoming.ordinal }),
|
|
4006
|
-
fence: incoming.fence,
|
|
4007
|
-
payload: { activity: incoming.payload.activity ?? "model" }
|
|
4008
|
-
});
|
|
4009
|
-
}
|
|
4010
3949
|
function usageSnapshotIsSuperseded(events, incoming) {
|
|
4011
3950
|
if (incoming.kind !== "activity.observed" || incoming.payload.usage === undefined) {
|
|
4012
3951
|
return false;
|
|
@@ -4019,6 +3958,15 @@ function usageSnapshotIsSuperseded(events, incoming) {
|
|
|
4019
3958
|
&& runtimeObservationFenceMatches(observation.fence, incoming.fence)
|
|
4020
3959
|
&& observationIsStrictlyNewer(observation, incoming));
|
|
4021
3960
|
}
|
|
3961
|
+
function hasPersistedRuntimeObservation(events, incoming) {
|
|
3962
|
+
const usageIdentity = isRuntimeTokenEvidence(incoming)
|
|
3963
|
+
? incoming.eventId
|
|
3964
|
+
: undefined;
|
|
3965
|
+
return events.some((event) => (event.type === RUNTIME_OBSERVATION_TASK_EVENT
|
|
3966
|
+
&& (usageIdentity === undefined
|
|
3967
|
+
? event.payload.semanticKey === incoming.semanticKey
|
|
3968
|
+
: event.payload.eventId === usageIdentity)));
|
|
3969
|
+
}
|
|
4022
3970
|
function observationIsStrictlyNewer(left, right) {
|
|
4023
3971
|
return compareCanonicalObservationOrder(left, right) > 0;
|
|
4024
3972
|
}
|