@zq-silk/yui 0.15.7 → 0.15.8
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 +192 -399
- package/README.md +127 -1149
- package/dist/agent/adapterCatalog.js +15 -2
- package/dist/agent/agent.js +23 -3
- package/dist/agent/argumentPolicy.js +7 -1
- package/dist/agent/connectionPlan.js +62 -0
- package/dist/agent/executionComponents.js +158 -0
- package/dist/agent/launchEnvironment.js +31 -3
- package/dist/agent/managedRuntimeEnvironment.js +3 -5
- package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
- package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
- package/dist/brief/taskBrief.js +12 -0
- package/dist/cli/agentConfigurationPicker.js +13 -0
- package/dist/cli/commandCatalog.js +157 -70
- package/dist/cli/interactionCandidates.js +5 -5
- package/dist/cli/interactionPolicy.js +38 -8
- package/dist/cli/invocationRouter.js +1 -1
- package/dist/cli/managedDiagnostics.js +28 -0
- package/dist/cli/operatorWizard.js +1 -7
- package/dist/cli/roleOptionOrder.js +27 -0
- package/dist/cli/roleWizard.js +50 -14
- package/dist/cli/updateOrchestrator.js +1 -1
- package/dist/cli/updatePorts.js +3 -4
- package/dist/cli.js +178 -95
- package/dist/commands/agentCommands.js +72 -14
- package/dist/commands/capabilityCommands.js +9 -6
- package/dist/commands/configCommands.js +20 -20
- package/dist/commands/deliveryGuardPreflight.js +2 -2
- package/dist/commands/executionAuditCommands.js +24 -24
- package/dist/commands/globalRoleCommands.js +1 -1
- package/dist/commands/grantCommands.js +4 -4
- package/dist/commands/operatorCommands.js +1 -7
- package/dist/commands/projectCommands.js +4 -4
- package/dist/commands/resourcesCommands.js +2 -2
- package/dist/commands/roleConfiguration.js +25 -5
- package/dist/commands/roleRuntimeGuard.js +4 -5
- package/dist/commands/sessionCommands.js +3 -7
- package/dist/commands/taskActivationCommands.js +259 -0
- package/dist/commands/taskActor.js +28 -49
- package/dist/commands/taskCommands.js +1188 -710
- package/dist/commands/taskContextCommand.js +39 -583
- package/dist/commands/taskExecutionCommands.js +32 -32
- package/dist/commands/taskInputCommands.js +40 -104
- package/dist/commands/taskIntegrationCommands.js +3 -2
- package/dist/commands/taskIntegrationQueueCommands.js +1 -1
- package/dist/commands/taskNextActionCommand.js +8 -8
- package/dist/commands/taskOverviewCommand.js +33 -45
- package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
- package/dist/commands/taskRoleRuntimeStatus.js +133 -102
- package/dist/commands/telemetryCommands.js +36 -38
- package/dist/config/configCatalog.js +4 -4
- package/dist/config/yuiConfig.js +8 -8
- package/dist/context/contextSnapshot.js +10 -10
- package/dist/context/dispatchContext.js +11 -11
- package/dist/context/roleSessionContext.js +6 -3
- package/dist/context/{turnContextPack.js → runContextPack.js} +146 -81
- package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
- package/dist/context/sessionBootstrapManifest.js +21 -2
- package/dist/context/sourceRunContext.js +30 -0
- package/dist/context/taskContext.js +458 -0
- package/dist/context/wakeNotification.js +27 -27
- package/dist/controller/agentRuntimeObserver.js +21 -24
- package/dist/controller/capabilityBridge.js +17 -6
- package/dist/controller/clientRuntime.js +65 -92
- package/dist/controller/controller.js +114 -188
- package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
- package/dist/controller/jobControl.js +54 -85
- package/dist/controller/resourceInventory.js +8 -27
- package/dist/controller/resourceInventoryLinux.js +12 -13
- package/dist/controller/runtime.js +529 -479
- package/dist/controller/runtimeEventInbox.js +55 -25
- package/dist/controller/runtimeEventProcessor.js +22 -31
- package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
- package/dist/controller/runtimeLaunchCoordinator.js +80 -426
- package/dist/controller/runtimeObservationHook.js +14 -18
- package/dist/controller/sessionNotify.js +16 -24
- package/dist/controller/sessionOwnerReconciliation.js +168 -50
- package/dist/controller/structuredProviderObservation.js +138 -99
- package/dist/coordination/workMailbox.js +3 -3
- package/dist/coordination/workMailboxQueue.js +36 -33
- package/dist/core/boundedRpc.js +8 -1
- package/dist/core/controllerClient.js +20 -1
- package/dist/core/controllerServer.js +4 -4
- package/dist/doctor/doctor.js +13 -2
- package/dist/domain/agentResultTransport.js +9 -9
- package/dist/execution/codexThreadNaming.js +2 -8
- package/dist/execution/executionHealth.js +51 -63
- package/dist/execution/reviewMainRun.js +137 -0
- package/dist/execution/workItemExecution.js +28 -29
- package/dist/execution/workItemExecutionProjection.js +99 -107
- package/dist/execution/workItemMainRun.js +141 -0
- package/dist/executor/agentAdapter.js +227 -20
- package/dist/executor/agentConfigurationCatalog.js +126 -4
- package/dist/executor/agentConfigurationProbe.js +162 -4
- package/dist/executor/agentExecutor.js +79 -78
- package/dist/executor/effectiveLaunch.js +105 -18
- package/dist/executor/executorRegistry.js +29 -44
- package/dist/executor/fileRoleLaunchPlanner.js +229 -154
- package/dist/executor/workspacePreflightClassification.js +16 -16
- package/dist/grant/capabilityGrant.js +6 -3
- package/dist/input/inputRequest.js +12 -10
- package/dist/integration/gitIntegrationService.js +4 -11
- package/dist/integration/integrationQueueService.js +4 -4
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/kernel/builtinCapabilities.js +247 -12
- package/dist/kernel/capabilityRegistry.js +64 -18
- package/dist/kernel/instanceHost.js +12 -1
- package/dist/kernel/kernelPorts.js +2 -2
- package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
- package/dist/lifecycle/exactRunTerminalization.js +449 -0
- package/dist/message/message.js +62 -6
- package/dist/message/messageContinuation.js +204 -0
- package/dist/observability/executionAudit.js +70 -72
- package/dist/observability/faultClassification.js +2 -2
- package/dist/observability/orchestrationMetrics.js +8 -8
- package/dist/operator/operatorSessionHistory.js +1 -7
- package/dist/output/agentConfigurationPresentation.js +8 -3
- package/dist/output/agentRunConfigurationPresentation.js +128 -0
- package/dist/output/rolePresentation.js +54 -3
- package/dist/plugins/pluginChild.js +104 -0
- package/dist/plugins/pluginIntent.js +26 -0
- package/dist/plugins/pluginInterpreter.js +43 -0
- package/dist/plugins/pluginPackage.js +101 -0
- package/dist/plugins/pluginProcess.js +112 -0
- package/dist/plugins/pluginService.js +380 -0
- package/dist/profile/agentProfile.js +1 -1
- package/dist/repository/gitWorkspace.js +26 -4
- package/dist/repository/project.js +19 -4
- package/dist/repository/taskBaseFreshness.js +13 -13
- package/dist/repository/taskWorkspaceCoordinator.js +20 -27
- package/dist/repository/taskWorkspacePreparer.js +344 -83
- package/dist/resources/autoResourceGc.js +3 -3
- package/dist/resources/liveReferences.js +3 -3
- package/dist/resources/projectResource.js +123 -0
- package/dist/resources/projectResourceService.js +421 -0
- package/dist/resources/resourceDiscovery.js +6 -6
- package/dist/resources/resourceGc.js +1 -1
- package/dist/resources/resourceRegistrar.js +1 -1
- package/dist/resources/resourceTypes.js +1 -1
- package/dist/review/deltaRecheck.js +3 -3
- package/dist/review/reviewAcceptance.js +16 -16
- package/dist/review/reviewDecision.js +7 -7
- package/dist/review/reviewRound.js +21 -20
- package/dist/review/reviewerAvailability.js +2 -2
- package/dist/role/role.js +51 -7
- package/dist/role/taskRoleUpdate.js +30 -0
- package/dist/runtime/acpProtocol.js +425 -0
- package/dist/runtime/acpSession.js +731 -0
- package/dist/runtime/acpSessionConfiguration.js +260 -0
- package/dist/runtime/agentDriver.js +30 -11
- package/dist/runtime/agentEndpoint.js +278 -0
- package/dist/runtime/agentEndpointIdentity.js +86 -0
- package/dist/runtime/agentEndpointOwnership.js +239 -0
- package/dist/runtime/agentError.js +2 -10
- package/dist/runtime/agentHost.js +565 -314
- package/dist/runtime/agentRunConfiguration.js +258 -0
- package/dist/runtime/builtinAgentDrivers.js +134 -18
- package/dist/runtime/builtinAgentErrorMappers.js +55 -3
- package/dist/runtime/builtinTranscriptUsage.js +1 -1
- package/dist/runtime/claude-process-owner +0 -0
- package/dist/runtime/codexAppServerRuntime.js +38 -30
- package/dist/runtime/codexInteractiveHost.js +41 -6
- package/dist/runtime/continuationManager.js +2 -6
- package/dist/runtime/executionEnvironment.js +30 -0
- package/dist/runtime/firstProgressAdvisory.js +11 -11
- package/dist/runtime/index.js +4 -3
- package/dist/runtime/jsonLineChannel.js +109 -0
- package/dist/runtime/launchBroker.js +91 -16
- package/dist/runtime/launchDiagnostics.js +2 -2
- package/dist/runtime/lifecycleReservation.js +10 -18
- package/dist/runtime/managedCaller.js +61 -17
- package/dist/runtime/nativeSessionControl.js +102 -0
- package/dist/runtime/ports.js +6 -21
- package/dist/runtime/processExitObservation.js +8 -7
- package/dist/runtime/promptEnvelope.js +17 -6
- package/dist/runtime/providerContinuation.js +3 -9
- package/dist/runtime/providerContinuationReconciliationService.js +4 -13
- package/dist/runtime/providerControl.js +2 -7
- package/dist/runtime/providerRuntimeIdentity.js +110 -222
- package/dist/runtime/providerRuntimeReconciler.js +5 -9
- package/dist/runtime/runtimeBinding.js +0 -1
- package/dist/runtime/runtimeContinuationProjection.js +4 -7
- package/dist/runtime/runtimeDeadlines.js +9 -0
- package/dist/runtime/runtimeHealthPolicy.js +1 -1
- package/dist/runtime/runtimeObservation.js +29 -65
- package/dist/runtime/runtimeProjection.js +43 -51
- package/dist/runtime/runtimeSessionCandidate.js +1 -3
- package/dist/runtime/sessionLaunchRequest.js +3 -7
- package/dist/runtime/sessionOwnerIdentity.js +7 -54
- package/dist/runtime/sessionOwnerRegistry.js +22 -17
- package/dist/runtime/sessionReconciliation.js +4 -8
- package/dist/runtime/sessionTerminationGuard.js +70 -259
- package/dist/runtime/sessionTokenMetrics.js +5 -16
- package/dist/runtime/structuredProviderHost.js +237 -117
- package/dist/runtime/taskRuntimeIsolation.js +39 -122
- package/dist/runtime/tmuxAdapters.js +39 -86
- package/dist/scheduler/activeRoleRunDelivery.js +354 -0
- package/dist/scheduler/leaderWakeupProcessor.js +75 -266
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +80 -9
- package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
- package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
- package/dist/scheduler/taskExecutionProjection.js +120 -124
- package/dist/scheduler/taskObservabilityProjection.js +29 -29
- package/dist/scheduler/taskWake.js +11 -4
- package/dist/scheduler/wakeReason.js +9 -1
- package/dist/setup/setupCommand.js +1 -0
- package/dist/storage/migrations/agentRunContract.js +159 -0
- package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
- package/dist/storage/sqliteSchema.js +431 -5
- package/dist/storage/sqliteStore.js +375 -220
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +10 -5
- package/dist/storage/taskStore.js +13 -11
- package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
- package/dist/surface/surfaceContributions.js +102 -0
- package/dist/task/completionReadiness.js +32 -6
- package/dist/task/deliveryGuard.js +16 -16
- package/dist/task/draftPlan.js +72 -12
- package/dist/task/nextAction.js +144 -128
- package/dist/task/remoteDelivery.js +6 -6
- package/dist/task/task.js +184 -18
- package/dist/task/taskActivation.js +301 -0
- package/dist/task/taskActivationService.js +392 -0
- package/dist/task/taskRecordReference.js +5 -4
- package/dist/task/taskRecordRetirement.js +1 -1
- package/dist/telemetry/sqliteTelemetryStore.js +55 -68
- package/dist/telemetry/telemetryConfig.js +14 -14
- package/dist/telemetry/telemetryWiring.js +2 -2
- package/dist/web/assets/assetManifest.js +2 -0
- package/dist/web/assets/client/app.js +120 -20
- package/dist/web/assets/client/components.js +87 -54
- package/dist/web/assets/client/i18n.js +83 -41
- package/dist/web/assets/client/markdown.js +1 -1
- package/dist/web/assets/client/taskSurface.js +353 -0
- package/dist/web/assets/client/view.js +49 -44
- package/dist/web/assets/shell.js +1 -1
- package/dist/web/assets/styles/cards.js +22 -4
- package/dist/web/controllerWeb.js +60 -0
- package/dist/web/webMutation.js +28 -0
- package/dist/web/webServer.js +118 -8
- package/dist/web/webSnapshot.js +81 -74
- package/dist/web/webTaskSurface.js +64 -0
- package/dist/workItem/dependencyGate.js +1 -1
- package/dist/workItem/workItem.js +80 -48
- package/dist/workspace/workItemChangeSetManager.js +16 -9
- package/docs/agent-result-consumption.md +94 -0
- package/docs/agent-runtime-drivers.md +91 -0
- package/docs/architecture/README.md +38 -0
- package/docs/architecture/capabilities-and-resources.md +79 -0
- package/docs/managed-turn-and-session-runtime.md +222 -0
- package/docs/observability/README.md +81 -0
- package/docs/plugin-sdk.md +290 -0
- package/docs/provider-runtime.md +163 -0
- package/docs/release-workflow.md +303 -0
- package/docs/roles-and-configuration.md +113 -0
- package/docs/sqlite-control-plane-design.md +76 -0
- package/docs/task-dag-semantics.md +57 -0
- package/docs/task-delivery.md +103 -0
- package/docs/task-local-identity.md +6 -6
- package/docs/testing/verification-levels.md +86 -0
- package/i18n/README.zh-CN.md +98 -739
- package/package.json +2 -2
- package/skills/yui-leader/SKILL.md +130 -103
- package/skills/yui-leader/references/integration.md +39 -0
- package/skills/yui-leader/references/replicated-execution.md +42 -0
- package/skills/yui-leader/references/task-plugins.md +33 -0
- package/skills/yui-operator/SKILL.md +30 -59
- package/skills/yui-reviewer/SKILL.md +35 -36
- package/skills/yui-runtime/SKILL.md +88 -24
- package/skills/yui-runtime/references/publication.md +22 -0
- package/skills/yui-runtime/references/recovery.md +64 -0
- package/skills/yui-worker/SKILL.md +37 -39
- package/dist/cli/roleOptionCatalog.js +0 -68
- package/dist/context/sourceTurnContext.js +0 -30
- package/dist/execution/reviewMainTurn.js +0 -161
- package/dist/execution/workItemMainTurn.js +0 -164
- package/dist/lifecycle/exactTurnTerminalization.js +0 -407
- package/dist/runtime/preallocatedNativeSession.js +0 -13
- package/dist/runtime/runtimeStopReceipt.js +0 -42
- package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
|
@@ -2,21 +2,21 @@ import { selectedSchedulerRoles, selectedActiveSchedulerTasks } from "./ports.js
|
|
|
2
2
|
import { nextPendingBatch } from "../coordination/workMailbox.js";
|
|
3
3
|
import { RUNTIME_DIAGNOSTIC_AFTER_MS, SEMANTIC_STALL_WINDOW_MS } from "../runtime/runtimeHealthPolicy.js";
|
|
4
4
|
/**
|
|
5
|
-
* Default window of no durable progress before a live-but-idle
|
|
6
|
-
* traceable needs-attention signal. It is deliberately long: a healthy
|
|
5
|
+
* Default window of no durable progress before a live-but-idle AgentRun becomes a
|
|
6
|
+
* traceable needs-attention signal. It is deliberately long: a healthy AgentRun that
|
|
7
7
|
* is simply slow keeps its structured checkpoint fresh and never crosses it.
|
|
8
8
|
*/
|
|
9
9
|
export const DEFAULT_STALL_WINDOW_MS = SEMANTIC_STALL_WINDOW_MS;
|
|
10
10
|
/** Cheap workflow-stall candidate filter; the real threshold remains 30m. */
|
|
11
11
|
export const DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS = RUNTIME_DIAGNOSTIC_AFTER_MS;
|
|
12
|
-
export const
|
|
13
|
-
export const
|
|
14
|
-
export const
|
|
15
|
-
export const
|
|
12
|
+
export const RUN_PROGRESS_EVENT = "run.progress";
|
|
13
|
+
export const RUN_STALLED_EVENT = "run.stalled";
|
|
14
|
+
export const RUN_RECOVERED_EVENT = "run.recovered";
|
|
15
|
+
export const RUN_DIAGNOSTIC_FINISHED_EVENT = "runtime.diagnostic-finished";
|
|
16
16
|
/** Structured, non-Message recovery evidence written by an explicit Leader. */
|
|
17
17
|
/** Workflow-semantic events that count for the durable progress clock. */
|
|
18
18
|
const ACTIVITY_EVENT_TYPES = new Set([
|
|
19
|
-
|
|
19
|
+
RUN_PROGRESS_EVENT,
|
|
20
20
|
"message.sent",
|
|
21
21
|
"input.answered",
|
|
22
22
|
"input.auto-answered",
|
|
@@ -31,17 +31,17 @@ const ACTIVITY_EVENT_TYPES = new Set([
|
|
|
31
31
|
]);
|
|
32
32
|
/**
|
|
33
33
|
* One pure projection used by every Role. Resource activity is retained only
|
|
34
|
-
* as exact-
|
|
34
|
+
* as exact-Session diagnostic evidence; it never changes the durable
|
|
35
35
|
* progress clock, suppresses workflow attention, or authorizes recovery.
|
|
36
36
|
*/
|
|
37
|
-
export function
|
|
37
|
+
export function projectRoleRunHealth(input) {
|
|
38
38
|
const windowMs = input.windowMs ?? DEFAULT_STALL_WINDOW_MS;
|
|
39
39
|
const diagnosticAfterMs = input.diagnosticAfterMs
|
|
40
40
|
?? DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS;
|
|
41
41
|
if (!Number.isFinite(windowMs) || windowMs <= 0) {
|
|
42
|
-
throw new Error("Role
|
|
42
|
+
throw new Error("Role AgentRun stall window must be a positive number of milliseconds.");
|
|
43
43
|
}
|
|
44
|
-
const evaluation =
|
|
44
|
+
const evaluation = evaluateRoleRunStall({
|
|
45
45
|
progressAt: input.progressAt,
|
|
46
46
|
now: input.now,
|
|
47
47
|
windowMs
|
|
@@ -58,7 +58,6 @@ export function projectRoleTurnHealth(input) {
|
|
|
58
58
|
: session.status === "ended"
|
|
59
59
|
? session.endReason === "failed" ? "broken" : "stopped"
|
|
60
60
|
: !hasResourceIdentityText(session.nativeSessionId)
|
|
61
|
-
&& !hasResourceIdentityText(session.runtimeGenerationId)
|
|
62
61
|
? "unknown"
|
|
63
62
|
: "matching";
|
|
64
63
|
const resourceActivity = hostLiveness === "present"
|
|
@@ -107,16 +106,16 @@ export function projectRoleTurnHealth(input) {
|
|
|
107
106
|
/**
|
|
108
107
|
* Pure stall decision. Given the last durable progress timestamp and whatever
|
|
109
108
|
* progress point the previous attention (if any) recorded, decide whether the
|
|
110
|
-
*
|
|
109
|
+
* AgentRun is stalled and whether this is new evidence worth surfacing again.
|
|
111
110
|
*/
|
|
112
|
-
export function
|
|
111
|
+
export function evaluateRoleRunStall(input) {
|
|
113
112
|
const windowMs = input.windowMs ?? DEFAULT_STALL_WINDOW_MS;
|
|
114
113
|
if (!Number.isFinite(windowMs) || windowMs <= 0) {
|
|
115
|
-
throw new Error("Role
|
|
114
|
+
throw new Error("Role AgentRun stall window must be a positive number of milliseconds.");
|
|
116
115
|
}
|
|
117
116
|
const progressMs = Date.parse(input.progressAt);
|
|
118
117
|
if (!Number.isFinite(progressMs)) {
|
|
119
|
-
throw new Error("Role
|
|
118
|
+
throw new Error("Role AgentRun stall progress timestamp is invalid.");
|
|
120
119
|
}
|
|
121
120
|
const idleMs = input.now.getTime() - progressMs;
|
|
122
121
|
const stalled = idleMs >= windowMs;
|
|
@@ -124,7 +123,7 @@ export function evaluateRoleTurnStall(input) {
|
|
|
124
123
|
return { stalled, isNewEpisode, idleMs, progressAt: input.progressAt };
|
|
125
124
|
}
|
|
126
125
|
/**
|
|
127
|
-
* Latest durable progress timestamp for an active
|
|
126
|
+
* Latest durable progress timestamp for an active AgentRun. AgentRun creation,
|
|
128
127
|
* explicit workflow checkpoints, and semantic domain activity count as
|
|
129
128
|
* progress. Provider operations, tokens, CPU/memory and bookkeeping are
|
|
130
129
|
* intentionally excluded.
|
|
@@ -136,17 +135,17 @@ export function latestDurableProgressAt(input) {
|
|
|
136
135
|
input.latestActivityAt
|
|
137
136
|
].filter((value) => (typeof value === "string" && Number.isFinite(Date.parse(value))));
|
|
138
137
|
if (candidates.length === 0) {
|
|
139
|
-
throw new Error("No durable progress timestamp is available for the
|
|
138
|
+
throw new Error("No durable progress timestamp is available for the AgentRun.");
|
|
140
139
|
}
|
|
141
140
|
return candidates.reduce((latest, value) => (Date.parse(value) > Date.parse(latest) ? value : latest));
|
|
142
141
|
}
|
|
143
|
-
/** Most recent semantic timestamp carried by a
|
|
144
|
-
export function
|
|
142
|
+
/** Most recent semantic timestamp carried by a AgentRun progress event. */
|
|
143
|
+
export function latestRunProgressAt(events, runId) {
|
|
145
144
|
let latest;
|
|
146
145
|
for (const event of events) {
|
|
147
|
-
if (event.type !==
|
|
146
|
+
if (event.type !== RUN_PROGRESS_EVENT)
|
|
148
147
|
continue;
|
|
149
|
-
if (event.payload.
|
|
148
|
+
if (event.payload.runId !== runId)
|
|
150
149
|
continue;
|
|
151
150
|
const progressAt = typeof event.payload.progressAt === "string"
|
|
152
151
|
&& Number.isFinite(Date.parse(event.payload.progressAt))
|
|
@@ -163,11 +162,11 @@ export function latestTurnProgressAt(events, turnId) {
|
|
|
163
162
|
}
|
|
164
163
|
/**
|
|
165
164
|
* Resolve the exact semantic progress fence shared by resource production and
|
|
166
|
-
* stall consumption for one active
|
|
165
|
+
* stall consumption for one active AgentRun. Resource evidence carries this value
|
|
167
166
|
* as an exact fence, but never advances it.
|
|
168
167
|
*/
|
|
169
|
-
export function
|
|
170
|
-
const progress = store.
|
|
168
|
+
export function currentRoleRunProgressAt(store, taskId, roleName, run) {
|
|
169
|
+
const progress = store.getRunDurableProgress(taskId, roleName, run.id);
|
|
171
170
|
if (progress !== null) {
|
|
172
171
|
return {
|
|
173
172
|
progressAt: progress.progressAt,
|
|
@@ -179,12 +178,12 @@ export function currentRoleTurnProgressAt(store, taskId, roleName, run) {
|
|
|
179
178
|
return { progressAt: run.createdAt };
|
|
180
179
|
}
|
|
181
180
|
/**
|
|
182
|
-
* Computes the current semantic progress fence for an exact
|
|
183
|
-
* related-record readers add Work/Review/Integration evidence to the
|
|
181
|
+
* Computes the current semantic progress fence for an exact AgentRun. The optional
|
|
182
|
+
* related-record readers add Work/Review/Integration evidence to the AgentRun and
|
|
184
183
|
* event facts used by on-demand projections.
|
|
185
184
|
*/
|
|
186
|
-
export function
|
|
187
|
-
const run = store.
|
|
185
|
+
export function latestRunDurableProgressAt(store, taskId, roleName, runId, precomputed) {
|
|
186
|
+
const run = store.getRun(taskId, runId);
|
|
188
187
|
if (run === null || run.taskId !== taskId || run.roleName !== roleName)
|
|
189
188
|
return null;
|
|
190
189
|
// Scheduler reconciliation supplies its once-per-revision fold; on-demand
|
|
@@ -193,10 +192,10 @@ export function latestTurnDurableProgressAt(store, taskId, roleName, turnId, pre
|
|
|
193
192
|
const events = folded ? undefined : store.listEvents(taskId);
|
|
194
193
|
const latestCheckpointAt = folded
|
|
195
194
|
? precomputed.latestCheckpointAt
|
|
196
|
-
:
|
|
195
|
+
: latestRunProgressAt(events, run.id);
|
|
197
196
|
const latestActivityAt = folded
|
|
198
197
|
? precomputed.latestActivityAt
|
|
199
|
-
:
|
|
198
|
+
: latestRunActivityAt(events, run.id);
|
|
200
199
|
const baseline = latestDurableProgressAt({
|
|
201
200
|
startedAt: run.createdAt,
|
|
202
201
|
latestCheckpointAt,
|
|
@@ -216,8 +215,8 @@ export function latestTurnDurableProgressAt(store, taskId, roleName, turnId, pre
|
|
|
216
215
|
.filter(({ source }) => (source.kind === "work-item" && source.workItemId === run.workItemId))
|
|
217
216
|
?? [];
|
|
218
217
|
const inputProgress = store.listInputRequests?.(taskId)
|
|
219
|
-
.filter((request) => (request.requester.
|
|
220
|
-
|| request.blockedRefs.some((ref) => ref.type === "
|
|
218
|
+
.filter((request) => (request.requester.runId === run.id
|
|
219
|
+
|| request.blockedRefs.some((ref) => ref.type === "run" && ref.id === run.id)))
|
|
221
220
|
?? [];
|
|
222
221
|
const related = [
|
|
223
222
|
workItem?.updatedAt,
|
|
@@ -238,11 +237,11 @@ export function latestTurnDurableProgressAt(store, taskId, roleName, turnId, pre
|
|
|
238
237
|
...(latestRelatedAt === undefined ? {} : { evidence: "work-review-integration" })
|
|
239
238
|
};
|
|
240
239
|
}
|
|
241
|
-
/** Most recent createdAt of a
|
|
242
|
-
export function
|
|
240
|
+
/** Most recent createdAt of a AgentRun-scoped event of one type, if any. */
|
|
241
|
+
export function latestRunEventTime(events, type, runId) {
|
|
243
242
|
let latest;
|
|
244
243
|
for (const event of events) {
|
|
245
|
-
if (event.type !== type || event.payload.
|
|
244
|
+
if (event.type !== type || event.payload.runId !== runId)
|
|
246
245
|
continue;
|
|
247
246
|
if (latest === undefined || Date.parse(event.createdAt) > Date.parse(latest)) {
|
|
248
247
|
latest = event.createdAt;
|
|
@@ -250,16 +249,16 @@ export function latestTurnEventTime(events, type, turnId) {
|
|
|
250
249
|
}
|
|
251
250
|
return latest;
|
|
252
251
|
}
|
|
253
|
-
/** Most recent non-control event carrying a
|
|
254
|
-
export function
|
|
252
|
+
/** Most recent non-control event carrying a AgentRun identity. */
|
|
253
|
+
export function latestRunActivityAt(events, runId) {
|
|
255
254
|
let latest;
|
|
256
255
|
for (const event of events) {
|
|
257
256
|
if (!ACTIVITY_EVENT_TYPES.has(event.type)
|
|
258
|
-
|| event.payload.
|
|
259
|
-
|| event.type ===
|
|
260
|
-
|| event.type ===
|
|
257
|
+
|| event.payload.runId !== runId
|
|
258
|
+
|| event.type === RUN_STALLED_EVENT
|
|
259
|
+
|| event.type === RUN_RECOVERED_EVENT)
|
|
261
260
|
continue;
|
|
262
|
-
const activityAt = event.type ===
|
|
261
|
+
const activityAt = event.type === RUN_PROGRESS_EVENT
|
|
263
262
|
&& typeof event.payload.progressAt === "string"
|
|
264
263
|
&& Number.isFinite(Date.parse(event.payload.progressAt))
|
|
265
264
|
? event.payload.progressAt
|
|
@@ -270,11 +269,11 @@ export function latestTurnActivityAt(events, turnId) {
|
|
|
270
269
|
}
|
|
271
270
|
return latest;
|
|
272
271
|
}
|
|
273
|
-
/** The progress point recorded by the most recent stall attention for a
|
|
274
|
-
export function latestStallProgressAt(events,
|
|
272
|
+
/** The progress point recorded by the most recent stall attention for a AgentRun. */
|
|
273
|
+
export function latestStallProgressAt(events, runId) {
|
|
275
274
|
let latest;
|
|
276
275
|
for (const event of events) {
|
|
277
|
-
if (event.type !==
|
|
276
|
+
if (event.type !== RUN_STALLED_EVENT || event.payload.runId !== runId)
|
|
278
277
|
continue;
|
|
279
278
|
if (latest === undefined || Date.parse(event.createdAt) > Date.parse(latest.createdAt)) {
|
|
280
279
|
latest = event;
|
|
@@ -283,10 +282,10 @@ export function latestStallProgressAt(events, turnId) {
|
|
|
283
282
|
return latest?.payload.progressAt;
|
|
284
283
|
}
|
|
285
284
|
/** Latest stall episode identity used for source-idempotent attention. */
|
|
286
|
-
export function latestStallEvidenceKey(events,
|
|
285
|
+
export function latestStallEvidenceKey(events, runId) {
|
|
287
286
|
let latest;
|
|
288
287
|
for (const event of events) {
|
|
289
|
-
if (event.type !==
|
|
288
|
+
if (event.type !== RUN_STALLED_EVENT || event.payload.runId !== runId)
|
|
290
289
|
continue;
|
|
291
290
|
if (latest === undefined || Date.parse(event.createdAt) > Date.parse(latest.createdAt)) {
|
|
292
291
|
latest = event;
|
|
@@ -300,27 +299,27 @@ export function latestStallEvidenceKey(events, turnId) {
|
|
|
300
299
|
};
|
|
301
300
|
}
|
|
302
301
|
/**
|
|
303
|
-
* Folds a Task's event history into per-
|
|
302
|
+
* Folds a Task's event history into per-AgentRun progress facts in one O(events)
|
|
304
303
|
* pass. The adapter builds this once per durable revision and serves the
|
|
305
304
|
* stall reconciliation from it, replacing the per-candidate full-history
|
|
306
305
|
* clones and scans that turned one pass into an O(candidates x events) hot
|
|
307
306
|
* read. The fold mirrors latestTurnProgressAt/latestTurnActivityAt/
|
|
308
307
|
* latestStallProgressAt/latestStallEvidenceKey exactly.
|
|
309
308
|
*/
|
|
310
|
-
export function
|
|
311
|
-
const
|
|
309
|
+
export function foldRunProgressFacts(events) {
|
|
310
|
+
const byRun = new Map();
|
|
312
311
|
const latestStallCreatedAt = new Map();
|
|
313
312
|
for (const event of events) {
|
|
314
|
-
const
|
|
315
|
-
if (typeof
|
|
313
|
+
const runId = event.payload.runId;
|
|
314
|
+
if (typeof runId !== "string")
|
|
316
315
|
continue;
|
|
317
|
-
let facts =
|
|
316
|
+
let facts = byRun.get(runId);
|
|
318
317
|
if (facts === undefined) {
|
|
319
318
|
facts = {};
|
|
320
|
-
|
|
319
|
+
byRun.set(runId, facts);
|
|
321
320
|
}
|
|
322
321
|
const type = event.type;
|
|
323
|
-
const isProgress = type ===
|
|
322
|
+
const isProgress = type === RUN_PROGRESS_EVENT;
|
|
324
323
|
if (isProgress) {
|
|
325
324
|
const validProgress = typeof event.payload.progressAt === "string"
|
|
326
325
|
&& Number.isFinite(Date.parse(event.payload.progressAt));
|
|
@@ -337,11 +336,11 @@ export function foldTurnProgressFacts(events) {
|
|
|
337
336
|
facts.latestActivityAt = progressAt;
|
|
338
337
|
}
|
|
339
338
|
}
|
|
340
|
-
else if (type ===
|
|
341
|
-
const previousCreatedAt = latestStallCreatedAt.get(
|
|
339
|
+
else if (type === RUN_STALLED_EVENT) {
|
|
340
|
+
const previousCreatedAt = latestStallCreatedAt.get(runId);
|
|
342
341
|
if (previousCreatedAt === undefined
|
|
343
342
|
|| Date.parse(event.createdAt) > Date.parse(previousCreatedAt)) {
|
|
344
|
-
latestStallCreatedAt.set(
|
|
343
|
+
latestStallCreatedAt.set(runId, event.createdAt);
|
|
345
344
|
facts.latestStall = typeof event.payload.progressAt === "string"
|
|
346
345
|
? {
|
|
347
346
|
progressAt: event.payload.progressAt,
|
|
@@ -353,33 +352,33 @@ export function foldTurnProgressFacts(events) {
|
|
|
353
352
|
}
|
|
354
353
|
}
|
|
355
354
|
else if (ACTIVITY_EVENT_TYPES.has(type)
|
|
356
|
-
&& type !==
|
|
357
|
-
&& type !==
|
|
355
|
+
&& type !== RUN_STALLED_EVENT
|
|
356
|
+
&& type !== RUN_RECOVERED_EVENT) {
|
|
358
357
|
if (facts.latestActivityAt === undefined
|
|
359
358
|
|| Date.parse(event.createdAt) > Date.parse(facts.latestActivityAt)) {
|
|
360
359
|
facts.latestActivityAt = event.createdAt;
|
|
361
360
|
}
|
|
362
361
|
}
|
|
363
362
|
}
|
|
364
|
-
return
|
|
363
|
+
return byRun;
|
|
365
364
|
}
|
|
366
365
|
/** Yield the event loop so already-written control requests stay responsive. */
|
|
367
366
|
function yieldEventLoop() {
|
|
368
367
|
return new Promise((resolve) => setImmediate(resolve));
|
|
369
368
|
}
|
|
370
369
|
/**
|
|
371
|
-
* True while a
|
|
370
|
+
* True while a AgentRun remains in an unresolved stall episode: a stall was raised
|
|
372
371
|
* and no later checkpoint has advanced its durable progress since. This is the
|
|
373
372
|
* projection the runtime-health view reads to surface needs-attention.
|
|
374
373
|
*/
|
|
375
|
-
export function
|
|
374
|
+
export function isRoleRunStalled(events, runId) {
|
|
376
375
|
let stalled;
|
|
377
376
|
let recoveredAt;
|
|
378
377
|
let progressAt;
|
|
379
378
|
for (const event of events) {
|
|
380
|
-
if (event.payload.
|
|
379
|
+
if (event.payload.runId !== runId)
|
|
381
380
|
continue;
|
|
382
|
-
if (event.type ===
|
|
381
|
+
if (event.type === RUN_STALLED_EVENT) {
|
|
383
382
|
if (event.payload.status === "diagnostic-only")
|
|
384
383
|
continue;
|
|
385
384
|
if (stalled === undefined
|
|
@@ -388,14 +387,14 @@ export function isRoleTurnStalled(events, turnId) {
|
|
|
388
387
|
}
|
|
389
388
|
continue;
|
|
390
389
|
}
|
|
391
|
-
if (event.type ===
|
|
390
|
+
if (event.type === RUN_RECOVERED_EVENT) {
|
|
392
391
|
if (recoveredAt === undefined
|
|
393
392
|
|| Date.parse(event.createdAt) > Date.parse(recoveredAt)) {
|
|
394
393
|
recoveredAt = event.createdAt;
|
|
395
394
|
}
|
|
396
395
|
continue;
|
|
397
396
|
}
|
|
398
|
-
if (event.type !==
|
|
397
|
+
if (event.type !== RUN_PROGRESS_EVENT)
|
|
399
398
|
continue;
|
|
400
399
|
const candidate = typeof event.payload.progressAt === "string"
|
|
401
400
|
&& Number.isFinite(Date.parse(event.payload.progressAt))
|
|
@@ -418,26 +417,30 @@ export function isRoleTurnStalled(events, turnId) {
|
|
|
418
417
|
return progressAt === undefined || Date.parse(progressAt) <= Date.parse(stalledProgressAt);
|
|
419
418
|
}
|
|
420
419
|
/**
|
|
421
|
-
* Low-frequency health pass for active Task Role
|
|
420
|
+
* Low-frequency health pass for active Task Role AgentRuns. An unaccepted AgentRun is
|
|
422
421
|
* watched as delivery-stalled after the reasonable delivery window; an
|
|
423
|
-
* accepted
|
|
422
|
+
* accepted AgentRun is displayed as checkpoint-overdue after fifteen minutes, but
|
|
424
423
|
* this scheduler performs no runtime inspection until the thirty-minute
|
|
425
424
|
* diagnostic window is due.
|
|
426
|
-
* Leader
|
|
425
|
+
* Leader AgentRuns are only persisted when classification reaches truly-stalled —
|
|
427
426
|
* healthy downstream work, open user input, and recent own progress remain
|
|
428
427
|
* structured waiting/working facts. No branch sends terminal bytes, retries,
|
|
429
|
-
* replaces a Session, or changes
|
|
428
|
+
* replaces a Session, or changes AgentRun status.
|
|
430
429
|
*/
|
|
431
|
-
export async function
|
|
430
|
+
export async function reconcileStalledRoleRuns(store, delivery, now, selection, windowMs = DEFAULT_STALL_WINDOW_MS, liveStatuses, resourceEvidence, diagnosticAfterMs = DEFAULT_WORKFLOW_STALL_CANDIDATE_AGE_MS) {
|
|
432
431
|
// Dirty mailbox passes are intentionally not a second scheduler. Full
|
|
433
|
-
// reconcile owns the all-active-
|
|
432
|
+
// reconcile owns the all-active-AgentRun scan; dirty passes may still route the
|
|
434
433
|
// existing mailbox work without manufacturing another episode.
|
|
435
434
|
if (selection !== undefined && !selection.full)
|
|
436
435
|
return [];
|
|
437
|
-
if (store.
|
|
436
|
+
if (store.recordRoleRunStall === undefined)
|
|
438
437
|
return [];
|
|
439
|
-
|
|
440
|
-
|
|
438
|
+
// A Draft planning Turn can stall exactly like any other admitted Turn, so it
|
|
439
|
+
// is selected here too; the episode stays diagnostic-only either way.
|
|
440
|
+
const candidates = selectedActiveSchedulerTasks(store, selection, {
|
|
441
|
+
includePlanningDrafts: true
|
|
442
|
+
}).flatMap((task) => (selectedSchedulerRoles(store, task.id, selection).flatMap((role) => {
|
|
443
|
+
const run = store.getActiveRun(task.id, role.name);
|
|
441
444
|
if (run === null || run.status !== "active")
|
|
442
445
|
return [];
|
|
443
446
|
return [{
|
|
@@ -456,20 +459,20 @@ export async function reconcileStalledRoleTurns(store, delivery, now, selection,
|
|
|
456
459
|
eventsByTask.set(taskId, events);
|
|
457
460
|
return events;
|
|
458
461
|
};
|
|
459
|
-
const cadenceCandidates = candidates.filter(({ task, run }) => isStallCandidate(run, now, windowMs,
|
|
462
|
+
const cadenceCandidates = candidates.filter(({ task, run }) => isStallCandidate(run, now, windowMs, latestRunEventTime(diagnosticEvents(task.id), RUN_DIAGNOSTIC_FINISHED_EVENT, run.id)));
|
|
460
463
|
const stallCandidates = cadenceCandidates.filter(({ task, role, run }) => {
|
|
461
|
-
const progressAt =
|
|
462
|
-
return isStallCandidate(run, now, windowMs,
|
|
464
|
+
const progressAt = currentRoleRunProgressAt(store, task.id, role.name, run).progressAt;
|
|
465
|
+
return isStallCandidate(run, now, windowMs, latestRunEventTime(diagnosticEvents(task.id), RUN_DIAGNOSTIC_FINISHED_EVENT, run.id), progressAt);
|
|
463
466
|
});
|
|
464
467
|
if (stallCandidates.length === 0)
|
|
465
468
|
return [];
|
|
466
469
|
const diagnosticStartedAt = now.toISOString();
|
|
467
470
|
const finishDiagnostics = (outcome) => {
|
|
468
471
|
for (const { task, role, run } of stallCandidates) {
|
|
469
|
-
store.
|
|
472
|
+
store.recordRoleRunDiagnostic?.({
|
|
470
473
|
taskId: task.id,
|
|
471
474
|
roleName: role.name,
|
|
472
|
-
|
|
475
|
+
runId: run.id,
|
|
473
476
|
startedAt: diagnosticStartedAt,
|
|
474
477
|
outcome,
|
|
475
478
|
now
|
|
@@ -478,7 +481,7 @@ export async function reconcileStalledRoleTurns(store, delivery, now, selection,
|
|
|
478
481
|
};
|
|
479
482
|
const stallCandidateKeys = new Set(stallCandidates.map(({ task, role }) => (`${task.id}\0${role.name}`)));
|
|
480
483
|
const observed = new Map();
|
|
481
|
-
// Recent active
|
|
484
|
+
// Recent active AgentRuns are known healthy enough for Leader classification from
|
|
482
485
|
// their exact acceptance/creation boundary, but are deliberately not read
|
|
483
486
|
// from tmux or Event history until the 30-minute diagnostic window.
|
|
484
487
|
for (const candidate of candidates) {
|
|
@@ -504,22 +507,20 @@ export async function reconcileStalledRoleTurns(store, delivery, now, selection,
|
|
|
504
507
|
statuses = await delivery.inspectRoles(stallCandidates.map(({ task, role, run, session }) => ({
|
|
505
508
|
taskId: task.id,
|
|
506
509
|
roleName: role.name,
|
|
507
|
-
|
|
510
|
+
runId: run.id,
|
|
508
511
|
progressAt: run.createdAt,
|
|
509
512
|
agentId: session?.agentId ?? run.effective.agentId,
|
|
510
513
|
adapterId: session?.adapterId ?? run.effective.adapterId,
|
|
511
|
-
...(session?.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: session.runtimeGenerationId }),
|
|
512
514
|
...(session?.nativeSessionId === undefined
|
|
513
515
|
? {}
|
|
514
516
|
: { nativeSessionId: session.nativeSessionId })
|
|
515
517
|
})), stallCandidates.map(({ task, role, run, session }) => ({
|
|
516
518
|
taskId: task.id,
|
|
517
519
|
roleName: role.name,
|
|
518
|
-
|
|
519
|
-
progressAt:
|
|
520
|
+
runId: run.id,
|
|
521
|
+
progressAt: currentRoleRunProgressAt(store, task.id, role.name, run).progressAt,
|
|
520
522
|
agentId: session?.agentId ?? run.effective.agentId,
|
|
521
523
|
adapterId: session?.adapterId ?? run.effective.adapterId,
|
|
522
|
-
...(session?.runtimeGenerationId === undefined ? {} : { runtimeGenerationId: session.runtimeGenerationId }),
|
|
523
524
|
...(session?.nativeSessionId === undefined
|
|
524
525
|
? {}
|
|
525
526
|
: { nativeSessionId: session.nativeSessionId })
|
|
@@ -527,7 +528,7 @@ export async function reconcileStalledRoleTurns(store, delivery, now, selection,
|
|
|
527
528
|
}
|
|
528
529
|
catch {
|
|
529
530
|
// Health inspection is advisory. Unknown host state must leave the exact
|
|
530
|
-
//
|
|
531
|
+
// AgentRun/Session fence untouched. Closing this window schedules the next
|
|
531
532
|
// bounded diagnostic thirty minutes later instead of hot-looping.
|
|
532
533
|
finishDiagnostics("observation-error");
|
|
533
534
|
return [];
|
|
@@ -574,7 +575,7 @@ export async function reconcileStalledRoleTurns(store, delivery, now, selection,
|
|
|
574
575
|
: byRole.get(key);
|
|
575
576
|
}
|
|
576
577
|
catch {
|
|
577
|
-
// Without a complete live snapshot, especially for downstream
|
|
578
|
+
// Without a complete live snapshot, especially for downstream AgentRuns,
|
|
578
579
|
// Leader classification would be unsafe. Treat the whole advisory pass
|
|
579
580
|
// as unknown instead of escalating a false stall.
|
|
580
581
|
finishDiagnostics("observation-error");
|
|
@@ -594,12 +595,12 @@ export async function reconcileStalledRoleTurns(store, delivery, now, selection,
|
|
|
594
595
|
});
|
|
595
596
|
continue;
|
|
596
597
|
}
|
|
597
|
-
const progressFacts = store.
|
|
598
|
-
// The
|
|
598
|
+
const progressFacts = store.getRunProgressFacts(candidate.task.id, candidate.run.id);
|
|
599
|
+
// The AgentRun creation boundary starts the execution clock; durable workflow
|
|
599
600
|
// facts may advance it independently of Provider transport observations.
|
|
600
|
-
const progress =
|
|
601
|
+
const progress = currentRoleRunProgressAt(store, candidate.task.id, candidate.role.name, candidate.run);
|
|
601
602
|
const progressAt = progress.progressAt;
|
|
602
|
-
const evaluation =
|
|
603
|
+
const evaluation = evaluateRoleRunStall({
|
|
603
604
|
progressAt,
|
|
604
605
|
now,
|
|
605
606
|
windowMs,
|
|
@@ -617,15 +618,12 @@ export async function reconcileStalledRoleTurns(store, delivery, now, selection,
|
|
|
617
618
|
: {
|
|
618
619
|
taskId: candidate.task.id,
|
|
619
620
|
roleName: candidate.role.name,
|
|
620
|
-
|
|
621
|
+
runId: candidate.run.id,
|
|
621
622
|
agentId: runAgentId,
|
|
622
623
|
adapterId: runAdapterId,
|
|
623
624
|
...(candidate.session.nativeSessionId === undefined
|
|
624
625
|
? {}
|
|
625
626
|
: { nativeSessionId: candidate.session.nativeSessionId }),
|
|
626
|
-
...(candidate.session.runtimeGenerationId === undefined
|
|
627
|
-
? {}
|
|
628
|
-
: { runtimeGenerationId: candidate.session.runtimeGenerationId })
|
|
629
627
|
};
|
|
630
628
|
const resourceSnapshot = resourceForRun(resourceEvidence, candidate.task.id, candidate.role.name, candidate.run.id);
|
|
631
629
|
const resourceIsCurrent = live === "present"
|
|
@@ -635,7 +633,7 @@ export async function reconcileStalledRoleTurns(store, delivery, now, selection,
|
|
|
635
633
|
&& resourceEvidenceMatchesCurrentRun(resourceSnapshot, expectedResourceIdentity, progressAt)
|
|
636
634
|
&& resourceEvidenceIsFresh(resourceSnapshot, now, windowMs, diagnosticAfterMs);
|
|
637
635
|
const resource = resourceIsCurrent ? resourceSnapshot : undefined;
|
|
638
|
-
const health =
|
|
636
|
+
const health = projectRoleRunHealth({
|
|
639
637
|
progressAt,
|
|
640
638
|
createdAt: candidate.run.createdAt,
|
|
641
639
|
now,
|
|
@@ -647,8 +645,8 @@ export async function reconcileStalledRoleTurns(store, delivery, now, selection,
|
|
|
647
645
|
resource,
|
|
648
646
|
roleName: candidate.role.name
|
|
649
647
|
});
|
|
650
|
-
// Delivery-stalled
|
|
651
|
-
// Provider-uncertainty path. Accepted execution
|
|
648
|
+
// Delivery-stalled AgentRuns retain the existing delivery clock and immediate
|
|
649
|
+
// Provider-uncertainty path. Accepted execution AgentRuns use the shared
|
|
652
650
|
// projection. Exact Session/host resource evidence remains diagnostic.
|
|
653
651
|
const resourceActivity = health.resourceActivity;
|
|
654
652
|
const stalled = health.stalled && sessionMatchesRun && sessionUsable;
|
|
@@ -672,16 +670,16 @@ export async function reconcileStalledRoleTurns(store, delivery, now, selection,
|
|
|
672
670
|
if (current.live !== "present" || !current.stalled || !current.stallCandidate)
|
|
673
671
|
continue;
|
|
674
672
|
const { candidate, progressAt } = current;
|
|
675
|
-
const previous = store.
|
|
673
|
+
const previous = store.getRunProgressFacts(candidate.task.id, candidate.run.id)?.latestStall;
|
|
676
674
|
if (previous !== undefined
|
|
677
675
|
&& Date.parse(progressAt) > Date.parse(previous.progressAt)) {
|
|
678
676
|
// A new semantic progress point closes the previous episode first. It
|
|
679
677
|
// may itself already be older than the window, in which case the same
|
|
680
|
-
// pass records the next
|
|
681
|
-
store.
|
|
678
|
+
// pass records the next AgentRun+progressAt episode below.
|
|
679
|
+
store.recordRoleRunProgress?.({
|
|
682
680
|
taskId: candidate.task.id,
|
|
683
681
|
roleName: candidate.role.name,
|
|
684
|
-
|
|
682
|
+
runId: candidate.run.id,
|
|
685
683
|
progressAt,
|
|
686
684
|
...(current.evidence === undefined ? {} : { evidence: current.evidence }),
|
|
687
685
|
now
|
|
@@ -706,15 +704,15 @@ export async function reconcileStalledRoleTurns(store, delivery, now, selection,
|
|
|
706
704
|
: []),
|
|
707
705
|
...(current.evidence === undefined ? [] : [current.evidence])
|
|
708
706
|
].join(":");
|
|
709
|
-
// The final contract is one episode per
|
|
707
|
+
// The final contract is one episode per AgentRun + semantic progress point.
|
|
710
708
|
// New role/session/provider evidence is retained in the Leader's next
|
|
711
709
|
// diagnostic context, not duplicated as another Task-level alert.
|
|
712
710
|
if (previous?.progressAt === progressAt)
|
|
713
711
|
continue;
|
|
714
|
-
const persisted = store.
|
|
712
|
+
const persisted = store.recordRoleRunStall({
|
|
715
713
|
taskId: candidate.task.id,
|
|
716
714
|
roleName: candidate.role.name,
|
|
717
|
-
|
|
715
|
+
runId: candidate.run.id,
|
|
718
716
|
agentId: candidate.run.effective.agentId,
|
|
719
717
|
adapterId: candidate.run.effective.adapterId,
|
|
720
718
|
session: candidate.session,
|
|
@@ -729,7 +727,7 @@ export async function reconcileStalledRoleTurns(store, delivery, now, selection,
|
|
|
729
727
|
raised.push({
|
|
730
728
|
taskId: candidate.task.id,
|
|
731
729
|
roleName: candidate.role.name,
|
|
732
|
-
|
|
730
|
+
runId: candidate.run.id,
|
|
733
731
|
status: persisted,
|
|
734
732
|
kind,
|
|
735
733
|
classification,
|
|
@@ -738,19 +736,19 @@ export async function reconcileStalledRoleTurns(store, delivery, now, selection,
|
|
|
738
736
|
}
|
|
739
737
|
}
|
|
740
738
|
// A related WorkItem/Review/Integration fold may advance progress without
|
|
741
|
-
// carrying the
|
|
739
|
+
// carrying the AgentRun id. Materialize it once so context/web can clear the
|
|
742
740
|
// attention projection and the event history records the recovery boundary.
|
|
743
741
|
for (const current of observed.values()) {
|
|
744
742
|
if (current.live !== "present" || current.stalled || !current.stallCandidate)
|
|
745
743
|
continue;
|
|
746
744
|
const { candidate, progressAt } = current;
|
|
747
|
-
const previous = store.
|
|
745
|
+
const previous = store.getRunProgressFacts(candidate.task.id, candidate.run.id)?.latestStall;
|
|
748
746
|
if (previous !== undefined
|
|
749
747
|
&& Date.parse(progressAt) > Date.parse(previous.progressAt)) {
|
|
750
|
-
store.
|
|
748
|
+
store.recordRoleRunProgress?.({
|
|
751
749
|
taskId: candidate.task.id,
|
|
752
750
|
roleName: candidate.role.name,
|
|
753
|
-
|
|
751
|
+
runId: candidate.run.id,
|
|
754
752
|
progressAt,
|
|
755
753
|
...(current.evidence === undefined ? {} : { evidence: current.evidence }),
|
|
756
754
|
now
|
|
@@ -773,7 +771,7 @@ function classifyLeaderStall(store, taskId, observed, now, windowMs) {
|
|
|
773
771
|
if (store.hasOpenInputRequest(taskId))
|
|
774
772
|
return "waiting-user";
|
|
775
773
|
const downstream = [...observed.values()].filter((entry) => (entry.candidate.task.id === taskId && entry.candidate.role.name !== "leader"));
|
|
776
|
-
// A present downstream
|
|
774
|
+
// A present downstream AgentRun keeps recovery Leader-owned. If that AgentRun is
|
|
777
775
|
// itself stalled, this pass has just routed its structured attention to the
|
|
778
776
|
// Leader; escalating the Leader to the Operator in the same pass would skip
|
|
779
777
|
// the intended recovery owner.
|
|
@@ -782,13 +780,13 @@ function classifyLeaderStall(store, taskId, observed, now, windowMs) {
|
|
|
782
780
|
const mailbox = store.getWorkMailbox({ kind: "role", taskId, roleName: "leader" });
|
|
783
781
|
const pending = mailbox === null || mailbox === undefined ? null : nextPendingBatch(mailbox);
|
|
784
782
|
const processing = mailbox?.processing;
|
|
785
|
-
const processingCurrent = processing?.executionRef?.type === "
|
|
783
|
+
const processingCurrent = processing?.executionRef?.type === "run"
|
|
786
784
|
&& processing.executionRef.taskId === taskId
|
|
787
785
|
&& leader !== undefined
|
|
788
786
|
&& processing.executionRef.id === leader.candidate.run.id;
|
|
789
|
-
const
|
|
790
|
-
if (processingCurrent &&
|
|
791
|
-
const actionProgressAt = latestLeaderActionProgressAt(store, taskId,
|
|
787
|
+
const currentRunId = leader?.candidate.run.id;
|
|
788
|
+
if (processingCurrent && currentRunId !== undefined && processing !== null) {
|
|
789
|
+
const actionProgressAt = latestLeaderActionProgressAt(store, taskId, currentRunId, processing.startedAt, processing.batch, now);
|
|
792
790
|
const actionProgressMs = Date.parse(actionProgressAt);
|
|
793
791
|
const actionStalled = Number.isFinite(actionProgressMs)
|
|
794
792
|
&& now.getTime() - actionProgressMs >= windowMs;
|
|
@@ -807,7 +805,7 @@ function classifyLeaderStall(store, taskId, observed, now, windowMs) {
|
|
|
807
805
|
return "truly-stalled";
|
|
808
806
|
}
|
|
809
807
|
const LEADER_ACTION_PROGRESS_TYPES = new Set([
|
|
810
|
-
|
|
808
|
+
RUN_PROGRESS_EVENT,
|
|
811
809
|
"input.answered",
|
|
812
810
|
"input.auto-answered",
|
|
813
811
|
"input.cancelled",
|
|
@@ -823,7 +821,7 @@ const LEADER_ACTION_PROGRESS_TYPES = new Set([
|
|
|
823
821
|
"decision.superseded",
|
|
824
822
|
"milestone.added"
|
|
825
823
|
]);
|
|
826
|
-
function latestLeaderActionProgressAt(store, taskId,
|
|
824
|
+
function latestLeaderActionProgressAt(store, taskId, runId, startedAt, batch, now) {
|
|
827
825
|
const startedMs = Date.parse(startedAt);
|
|
828
826
|
if (!Number.isFinite(startedMs))
|
|
829
827
|
return "";
|
|
@@ -834,14 +832,14 @@ function latestLeaderActionProgressAt(store, taskId, turnId, startedAt, batch, n
|
|
|
834
832
|
continue;
|
|
835
833
|
if (event.payload.taskId !== undefined && event.payload.taskId !== taskId)
|
|
836
834
|
continue;
|
|
837
|
-
if (!leaderActionEventMatches(event, taskId,
|
|
835
|
+
if (!leaderActionEventMatches(event, taskId, runId, batch))
|
|
838
836
|
continue;
|
|
839
837
|
const createdMs = Date.parse(event.createdAt);
|
|
840
838
|
if (!Number.isFinite(createdMs)
|
|
841
839
|
|| createdMs < startedMs
|
|
842
840
|
|| createdMs > now.getTime())
|
|
843
841
|
continue;
|
|
844
|
-
const value = event.type ===
|
|
842
|
+
const value = event.type === RUN_PROGRESS_EVENT
|
|
845
843
|
&& typeof event.payload.progressAt === "string"
|
|
846
844
|
&& Number.isFinite(Date.parse(event.payload.progressAt))
|
|
847
845
|
? event.payload.progressAt
|
|
@@ -857,12 +855,12 @@ function latestLeaderActionProgressAt(store, taskId, turnId, startedAt, batch, n
|
|
|
857
855
|
}
|
|
858
856
|
return latest;
|
|
859
857
|
}
|
|
860
|
-
function leaderActionEventMatches(event, taskId,
|
|
858
|
+
function leaderActionEventMatches(event, taskId, runId, batch) {
|
|
861
859
|
if (event.taskId !== taskId)
|
|
862
860
|
return false;
|
|
863
|
-
if (event.payload.
|
|
861
|
+
if (event.payload.leaderRunId === runId)
|
|
864
862
|
return true;
|
|
865
|
-
if (event.payload.
|
|
863
|
+
if (event.payload.runId === runId)
|
|
866
864
|
return true;
|
|
867
865
|
const refs = batch.refs ?? [];
|
|
868
866
|
const refMatches = (type, payloadKey) => {
|
|
@@ -871,7 +869,7 @@ function leaderActionEventMatches(event, taskId, turnId, batch) {
|
|
|
871
869
|
&& ref.id === id
|
|
872
870
|
&& (ref.taskId === undefined || ref.taskId === taskId)));
|
|
873
871
|
};
|
|
874
|
-
if (refMatches("
|
|
872
|
+
if (refMatches("run", "runId"))
|
|
875
873
|
return true;
|
|
876
874
|
if (refMatches("work-item", "workItemId"))
|
|
877
875
|
return true;
|
|
@@ -897,7 +895,7 @@ function leaderStallEvidence(store, taskId, observed, now, windowMs) {
|
|
|
897
895
|
: "recent";
|
|
898
896
|
const processing = store.getWorkMailbox({ kind: "role", taskId, roleName: "leader" })?.processing;
|
|
899
897
|
const leader = [...observed.values()].find((entry) => (entry.candidate.task.id === taskId && entry.candidate.role.name === "leader"));
|
|
900
|
-
const processingCurrent = processing?.executionRef?.type === "
|
|
898
|
+
const processingCurrent = processing?.executionRef?.type === "run"
|
|
901
899
|
&& processing.executionRef.taskId === taskId
|
|
902
900
|
&& leader !== undefined
|
|
903
901
|
&& processing.executionRef.id === leader.candidate.run.id;
|
|
@@ -928,27 +926,18 @@ function exactLiveStatuses(statuses, candidates) {
|
|
|
928
926
|
throw new Error("Tmux Role stall snapshot is incomplete.");
|
|
929
927
|
return result;
|
|
930
928
|
}
|
|
931
|
-
function resourceForRun(snapshot, taskId, roleName,
|
|
929
|
+
function resourceForRun(snapshot, taskId, roleName, runId) {
|
|
932
930
|
if (snapshot === undefined)
|
|
933
931
|
return undefined;
|
|
934
|
-
return snapshot.get(`${taskId}\0${roleName}\0${
|
|
932
|
+
return snapshot.get(`${taskId}\0${roleName}\0${runId}`);
|
|
935
933
|
}
|
|
936
934
|
function resourceEvidenceMatchesCurrentRun(resource, expected, progressAt) {
|
|
937
935
|
if (resource === undefined || expected === undefined)
|
|
938
936
|
return false;
|
|
939
937
|
const identity = resource.identity;
|
|
940
|
-
if (identity === undefined
|
|
941
|
-
|| resource.progressAt !== progressAt
|
|
942
|
-
|| identity.taskId !== expected.taskId
|
|
943
|
-
|| identity.roleName !== expected.roleName
|
|
944
|
-
|| identity.turnId !== expected.turnId
|
|
945
|
-
|| identity.agentId !== expected.agentId
|
|
946
|
-
|| identity.adapterId !== expected.adapterId
|
|
947
|
-
|| identity.nativeSessionId !== expected.nativeSessionId
|
|
948
|
-
|| identity.runtimeGenerationId !== expected.runtimeGenerationId)
|
|
938
|
+
if (identity === undefined || resource.progressAt !== progressAt || identity.taskId !== expected.taskId || identity.roleName !== expected.roleName || identity.runId !== expected.runId || identity.agentId !== expected.agentId || identity.adapterId !== expected.adapterId || identity.nativeSessionId !== expected.nativeSessionId)
|
|
949
939
|
return false;
|
|
950
|
-
return hasResourceIdentityText(identity.nativeSessionId)
|
|
951
|
-
|| hasResourceIdentityText(identity.runtimeGenerationId);
|
|
940
|
+
return hasResourceIdentityText(identity.nativeSessionId);
|
|
952
941
|
}
|
|
953
942
|
function hasResourceIdentityText(value) {
|
|
954
943
|
return typeof value === "string" && value.trim().length > 0;
|