@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
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { usageError } from "../errors/cliError.js";
|
|
2
|
-
import {
|
|
2
|
+
import { isRoleRunStalled, latestStallProgressAt } from "../scheduler/roleRunStall.js";
|
|
3
3
|
import { formatTimestamp } from "../output/timePresentation.js";
|
|
4
4
|
import { pendingWakeupProjection } from "../storage/taskStore.js";
|
|
5
5
|
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
@@ -65,7 +65,7 @@ function buildTaskOverviewEntry(task, store, now, runtimeHealthPolicy) {
|
|
|
65
65
|
const workItems = store.listWorkItems(task.id);
|
|
66
66
|
const inputRequests = store.listInputRequests(task.id);
|
|
67
67
|
const openInputRequests = inputRequests.filter((request) => request.status === "open");
|
|
68
|
-
const
|
|
68
|
+
const runs = store.listRuns(task.id);
|
|
69
69
|
const events = store.listEvents(task.id);
|
|
70
70
|
const leaderFailure = store.getLeaderFailure(task.id);
|
|
71
71
|
const leaderMailbox = store.getWorkMailbox({ kind: "role", taskId: task.id, roleName: "leader" });
|
|
@@ -77,15 +77,19 @@ function buildTaskOverviewEntry(task, store, now, runtimeHealthPolicy) {
|
|
|
77
77
|
const session = store.getRoleSession(task.id, role.name);
|
|
78
78
|
return session === null ? [] : [{ roleName: role.name, ...session }];
|
|
79
79
|
});
|
|
80
|
-
const
|
|
80
|
+
const providerRuns = new Map(roles.map((role) => [role.name, store.getTaskRoleSessionSet(task.id, role.name)?.providerBinding?.run]));
|
|
81
|
+
const runDelivery = Object.fromEntries(runs.map((run) => {
|
|
82
|
+
const native = providerRuns.get(run.roleName);
|
|
83
|
+
return [run.id, native?.runId === run.id ? native.status : "unobserved"];
|
|
84
|
+
}));
|
|
85
|
+
const attention = collectAttention(task, runs, events, leaderFailure);
|
|
81
86
|
const blockers = collectBlockers(workItems, openInputRequests, attention);
|
|
82
87
|
const leader = {
|
|
83
88
|
role: "leader",
|
|
84
89
|
roleStatus: leaderRole === null
|
|
85
90
|
? "missing"
|
|
86
|
-
:
|
|
87
|
-
? "
|
|
88
|
-
: roleSessions.find((session) => session.roleName === "leader")?.status ?? "idle",
|
|
91
|
+
: providerRuns.get("leader")?.status === "accepted" ? "running"
|
|
92
|
+
: roleSessions.some((session) => session.roleName === "leader") ? "unknown" : "idle",
|
|
89
93
|
summary: brief?.leaderSummary ?? null,
|
|
90
94
|
currentFocus: brief?.currentFocus ?? null,
|
|
91
95
|
updatedAt: brief?.updatedAt ?? null,
|
|
@@ -93,29 +97,28 @@ function buildTaskOverviewEntry(task, store, now, runtimeHealthPolicy) {
|
|
|
93
97
|
summaryStatus: brief === null ? "missing" : "available"
|
|
94
98
|
};
|
|
95
99
|
const counts = countWorkItems(workItems);
|
|
96
|
-
const
|
|
100
|
+
const runtimeRuns = runs
|
|
97
101
|
.filter((run) => run.status === "active")
|
|
98
102
|
.map((run) => ({
|
|
99
103
|
id: run.id,
|
|
100
104
|
roleName: run.roleName,
|
|
101
105
|
status: run.status,
|
|
102
|
-
runtime:
|
|
103
|
-
&& session.agentId === run.effective.agentId
|
|
104
|
-
&& session.status !== "ended")) ? "session-active" : "starting",
|
|
106
|
+
runtime: runDelivery[run.id],
|
|
105
107
|
createdAt: run.createdAt,
|
|
106
108
|
updatedAt: run.updatedAt
|
|
107
109
|
}));
|
|
108
110
|
const runtime = {
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
pendingDeliveryCount:
|
|
111
|
+
activeRuns: runtimeRuns,
|
|
112
|
+
activeRunCount: runtimeRuns.length,
|
|
113
|
+
pendingDeliveryCount: runtimeRuns.filter((run) => run.runtime !== "accepted").length
|
|
112
114
|
};
|
|
113
115
|
// Fold the execution projection from the facts already read above instead of
|
|
114
116
|
// reading the store a second time for the same unchanged revision.
|
|
115
117
|
const execution = projectTaskExecutionFromFacts({
|
|
116
118
|
task,
|
|
117
119
|
roles,
|
|
118
|
-
|
|
120
|
+
runs,
|
|
121
|
+
runDelivery,
|
|
119
122
|
workItems,
|
|
120
123
|
inputRequests,
|
|
121
124
|
reviewRounds,
|
|
@@ -140,7 +143,7 @@ function buildTaskOverviewEntry(task, store, now, runtimeHealthPolicy) {
|
|
|
140
143
|
currentTaskReviewCandidate: null,
|
|
141
144
|
executionGroups: execution.executionGroups
|
|
142
145
|
}));
|
|
143
|
-
const persistedCandidate = task.status === "active" || task.status === "
|
|
146
|
+
const persistedCandidate = task.status === "active" || task.status === "cancelled"
|
|
144
147
|
? taskRemoteDeliveryCandidate(task)
|
|
145
148
|
: null;
|
|
146
149
|
const remoteDelivery = projectTaskRemoteDeliveryFromStore(store, task, persistedCandidate);
|
|
@@ -166,7 +169,7 @@ function buildTaskOverviewEntry(task, store, now, runtimeHealthPolicy) {
|
|
|
166
169
|
remoteDelivery
|
|
167
170
|
};
|
|
168
171
|
}
|
|
169
|
-
function collectAttention(task,
|
|
172
|
+
function collectAttention(task, runs, events, failure) {
|
|
170
173
|
const items = [];
|
|
171
174
|
if (failure !== null) {
|
|
172
175
|
items.push({
|
|
@@ -178,21 +181,21 @@ function collectAttention(task, turns, events, failure) {
|
|
|
178
181
|
updatedAt: failure.lastFailedAt
|
|
179
182
|
});
|
|
180
183
|
}
|
|
181
|
-
for (const
|
|
182
|
-
if (
|
|
183
|
-
||
|
|
184
|
-
|| !
|
|
184
|
+
for (const run of runs) {
|
|
185
|
+
if (run.roleName !== "leader"
|
|
186
|
+
|| run.status !== "active"
|
|
187
|
+
|| !isRoleRunStalled(events, run.id))
|
|
185
188
|
continue;
|
|
186
|
-
const progressAt = latestStallProgressAt(events,
|
|
189
|
+
const progressAt = latestStallProgressAt(events, run.id) ?? run.updatedAt;
|
|
187
190
|
items.push({
|
|
188
191
|
kind: "leader-stalled",
|
|
189
|
-
id: `leader-stall:${
|
|
192
|
+
id: `leader-stall:${run.id}:${progressAt}`,
|
|
190
193
|
status: "needs-attention",
|
|
191
194
|
owner: "operator",
|
|
192
|
-
summary: `
|
|
195
|
+
summary: `AgentRun ${run.id} for ${run.roleName} has no durable progress after ${progressAt}.`,
|
|
193
196
|
updatedAt: progressAt,
|
|
194
|
-
|
|
195
|
-
roleName:
|
|
197
|
+
runId: run.id,
|
|
198
|
+
roleName: run.roleName
|
|
196
199
|
});
|
|
197
200
|
}
|
|
198
201
|
return items.sort((left, right) => (Date.parse(left.updatedAt) - Date.parse(right.updatedAt)
|
|
@@ -202,19 +205,7 @@ function collectBlockers(workItems, openInputRequests, attention) {
|
|
|
202
205
|
const blockers = [];
|
|
203
206
|
const workById = new Map(workItems.map((item) => [item.id, item]));
|
|
204
207
|
for (const item of workItems) {
|
|
205
|
-
if (item.status === "
|
|
206
|
-
blockers.push({
|
|
207
|
-
kind: "work",
|
|
208
|
-
type: "work",
|
|
209
|
-
id: item.id,
|
|
210
|
-
status: item.status,
|
|
211
|
-
owner: item.assignee ?? "leader",
|
|
212
|
-
action: "review-failed-work",
|
|
213
|
-
summary: item.outcome ?? `Work Item ${item.id} failed.`
|
|
214
|
-
});
|
|
215
|
-
continue;
|
|
216
|
-
}
|
|
217
|
-
if (item.status === "awaiting_acceptance") {
|
|
208
|
+
if ((item.status === "open" && item.currentCandidateId !== undefined)) {
|
|
218
209
|
blockers.push({
|
|
219
210
|
kind: "work",
|
|
220
211
|
type: "work",
|
|
@@ -226,9 +217,9 @@ function collectBlockers(workItems, openInputRequests, attention) {
|
|
|
226
217
|
});
|
|
227
218
|
continue;
|
|
228
219
|
}
|
|
229
|
-
if (item.status !== "
|
|
220
|
+
if (item.status !== "open")
|
|
230
221
|
continue;
|
|
231
|
-
const dependencies = item.dependsOn.filter((dependency) => (workById.get(dependency)?.status !== "
|
|
222
|
+
const dependencies = item.dependsOn.filter((dependency) => (workById.get(dependency)?.status !== "accepted"));
|
|
232
223
|
if (dependencies.length === 0)
|
|
233
224
|
continue;
|
|
234
225
|
blockers.push({
|
|
@@ -296,11 +287,8 @@ function overviewNextAction(task, workItems, execution, action) {
|
|
|
296
287
|
function countWorkItems(items) {
|
|
297
288
|
const counts = {
|
|
298
289
|
total: items.length,
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
awaiting_acceptance: 0,
|
|
302
|
-
completed: 0,
|
|
303
|
-
failed: 0,
|
|
290
|
+
open: 0,
|
|
291
|
+
accepted: 0,
|
|
304
292
|
retired: 0
|
|
305
293
|
};
|
|
306
294
|
for (const item of items)
|
|
@@ -29,7 +29,7 @@ export function projectTaskRemoteDeliveryFromStore(store, task, currentCandidate
|
|
|
29
29
|
events: store.listEvents(task.id),
|
|
30
30
|
publications: store.listPublicationReferences(task.id),
|
|
31
31
|
managedWorkspaces: store.listManagedWorkspaces(task.id),
|
|
32
|
-
|
|
32
|
+
runs: store.listRuns(task.id),
|
|
33
33
|
currentCandidate
|
|
34
34
|
});
|
|
35
35
|
}
|
|
@@ -40,7 +40,7 @@ export function createTaskRemoteDeliveryProof(store, task, currentCandidate = nu
|
|
|
40
40
|
events: store.listEvents(task.id),
|
|
41
41
|
publications,
|
|
42
42
|
managedWorkspaces: store.listManagedWorkspaces(task.id),
|
|
43
|
-
|
|
43
|
+
runs: store.listRuns(task.id),
|
|
44
44
|
currentCandidate
|
|
45
45
|
});
|
|
46
46
|
return {
|
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { isDeepStrictEqual } from "node:util";
|
|
2
|
+
import { runExecutionObservation } from "../agentRun/agentRun.js";
|
|
2
3
|
import { hasRuntimeCleanupObligation, runtimeLifecycleTarget } from "../runtime/lifecycleReservation.js";
|
|
3
|
-
import {
|
|
4
|
+
import { currentProviderConversation, managedProviderTurnId } from "../runtime/providerRuntimeIdentity.js";
|
|
5
|
+
import { isRoleRunStalled, latestStallProgressAt } from "../scheduler/roleRunStall.js";
|
|
4
6
|
import { createRuntimeObservation, runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
|
|
5
7
|
import { classifyRuntimeHealth, projectRuntimeObservation, projectRuntimeTaskEvents, runtimeDisplayStatus } from "../runtime/runtimeProjection.js";
|
|
6
|
-
import {
|
|
8
|
+
import { latestRunDurableProgressAt } from "../scheduler/roleRunStall.js";
|
|
7
9
|
import { resolveRuntimeHealth } from "../config/yuiConfig.js";
|
|
8
10
|
import { builtinDriverIdForAdapter } from "../runtime/builtinAgentDrivers.js";
|
|
9
|
-
import {
|
|
11
|
+
import { formatRunReceiptId } from "../task/taskRecordReference.js";
|
|
10
12
|
import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
11
13
|
import { projectSessionTokenMetrics, resolveSessionTokenIdentity } from "../runtime/sessionTokenMetrics.js";
|
|
12
14
|
export function inspectTaskRoleRuntimeStatuses(taskId, roles, store, panes, now = new Date()) {
|
|
@@ -21,21 +23,25 @@ export function inspectTaskRoleRuntimeStatuses(taskId, roles, store, panes, now
|
|
|
21
23
|
return roles.map((role) => inspectTaskRoleRuntimeStatus(taskId, role, store, panesByRole.get(role.name), role.name === "leader" ? taskOpenInputRequestCount : 0, now));
|
|
22
24
|
}
|
|
23
25
|
export function renderTaskRoleRuntimeStatus(status) {
|
|
24
|
-
const
|
|
26
|
+
const activeRun = status.activeRun === null
|
|
25
27
|
? "-"
|
|
26
|
-
: `${status.
|
|
27
|
-
const
|
|
28
|
+
: `${status.activeRun.id} (${status.execution?.delivery ?? "unobserved"})`;
|
|
29
|
+
const lastRun = status.activeRun !== null || status.lastRun === null
|
|
28
30
|
? undefined
|
|
29
|
-
: `${status.
|
|
31
|
+
: `${status.lastRun.id} (${status.lastRun.status}${status.lastRun.result === undefined ? "" : ` at ${status.lastRun.result.completedAt}`})`;
|
|
30
32
|
const activeWork = status.activeWork === null
|
|
31
33
|
? "-"
|
|
32
34
|
: `${status.activeWork.id} (${status.activeWork.status}) ${status.activeWork.title}`;
|
|
35
|
+
// Both lines name the execution component, as the Role view does. The
|
|
36
|
+
// connection plan cannot stand in for it here either: two different ACP
|
|
37
|
+
// products would print identically, and this display exists to say what is
|
|
38
|
+
// actually running.
|
|
33
39
|
const nativeSession = status.nativeSession === null
|
|
34
40
|
? "not recorded"
|
|
35
|
-
: `${status.nativeSession.nativeSessionId} (${status.nativeSession.status}, ${status.nativeSession.
|
|
41
|
+
: `${status.nativeSession.nativeSessionId} (${status.nativeSession.status}, ${status.nativeSession.effective.component}, effective r${status.nativeSession.effective.sourceDesiredRevision})`;
|
|
36
42
|
const effectiveLaunch = status.effectiveLaunch === null
|
|
37
43
|
? "not started"
|
|
38
|
-
: `${status.effectiveLaunch.agentId}/${status.effectiveLaunch.
|
|
44
|
+
: `${status.effectiveLaunch.agentId}/${status.effectiveLaunch.component}; r${status.effectiveLaunch.sourceDesiredRevision}; Profile intent=${status.effectiveLaunch.profileAccess}; permission=${status.effectiveLaunch.permission.strategy}`;
|
|
39
45
|
const tmux = status.tmux.state === "missing"
|
|
40
46
|
? "missing"
|
|
41
47
|
: [
|
|
@@ -84,11 +90,11 @@ export function renderTaskRoleRuntimeStatus(status) {
|
|
|
84
90
|
` Desired drift ${status.effectiveLaunch === null
|
|
85
91
|
? "-"
|
|
86
92
|
: status.launchDrift ? "pending next launch" : "none"}`,
|
|
87
|
-
`
|
|
93
|
+
` AgentRun/session ${status.runSessionDrift ? "snapshot mismatch" : "snapshot consistent"}`,
|
|
88
94
|
` Active work ${activeWork}`,
|
|
89
|
-
` Active run ${
|
|
90
|
-
...(
|
|
91
|
-
`
|
|
95
|
+
` Active run ${activeRun}`,
|
|
96
|
+
...(lastRun === undefined ? [] : [` Last turn ${lastRun}`]),
|
|
97
|
+
` AgentRun attention ${status.stall.active
|
|
92
98
|
? `needs-attention (${status.stall.kind ?? "workflow-not-progressing"}; no workflow progress since ${status.stall.progressAt ?? "unknown"})`
|
|
93
99
|
: "none"}`,
|
|
94
100
|
` Native session ${nativeSession}`,
|
|
@@ -103,15 +109,15 @@ export function renderTaskRoleRuntimeStatus(status) {
|
|
|
103
109
|
export function taskRoleActiveWorkLabel(status) {
|
|
104
110
|
if (status.activeWork !== null)
|
|
105
111
|
return `${status.activeWork.id}: ${status.activeWork.title}`;
|
|
106
|
-
return status.
|
|
112
|
+
return status.activeRun === null ? "-" : status.activeRun.id;
|
|
107
113
|
}
|
|
108
|
-
/** Issue 09: compact last-
|
|
109
|
-
export function
|
|
110
|
-
if (status.
|
|
111
|
-
return `${status.
|
|
112
|
-
if (status.
|
|
114
|
+
/** Issue 09: compact last-AgentRun outcome label for the Role list table. */
|
|
115
|
+
export function taskRoleLastRunLabel(status) {
|
|
116
|
+
if (status.activeRun !== null)
|
|
117
|
+
return `${status.activeRun.id} ${status.activeRun.status}`;
|
|
118
|
+
if (status.lastRun === null)
|
|
113
119
|
return "-";
|
|
114
|
-
return `${status.
|
|
120
|
+
return `${status.lastRun.id} ${status.lastRun.status}`;
|
|
115
121
|
}
|
|
116
122
|
export function taskRoleNativeSessionLabel(status) {
|
|
117
123
|
if (status.runtimeCleanupPending && status.nativeSession === null)
|
|
@@ -140,26 +146,26 @@ export function taskRoleTmuxLabel(status) {
|
|
|
140
146
|
: `running (${status.tmux.currentCommand})`;
|
|
141
147
|
}
|
|
142
148
|
function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputRequestCount, now) {
|
|
143
|
-
const
|
|
144
|
-
// The last
|
|
145
|
-
// Session that stops after its
|
|
146
|
-
// that
|
|
147
|
-
const
|
|
149
|
+
const activeRun = store.getActiveRun(taskId, role.name);
|
|
150
|
+
// The last AgentRun outcome is a separate axis from the Session lifecycle. A
|
|
151
|
+
// Session that stops after its AgentRun completed must not retroactively turn
|
|
152
|
+
// that AgentRun into a failure; the status display keeps both visible.
|
|
153
|
+
const lastRun = operationalTaskRecords(store.listRuns(taskId), store.listEvents(taskId), "run")
|
|
148
154
|
.filter((candidate) => candidate.roleName === role.name)
|
|
149
155
|
.sort((left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt))[0]
|
|
150
156
|
?? null;
|
|
151
|
-
const activeWork =
|
|
157
|
+
const activeWork = activeRun?.workItemId === undefined
|
|
152
158
|
? null
|
|
153
|
-
: store.getWorkItem(taskId,
|
|
159
|
+
: store.getWorkItem(taskId, activeRun.workItemId);
|
|
154
160
|
const sessions = store.getTaskRoleSessionSet(taskId, role.name);
|
|
155
|
-
const effectiveAgentId =
|
|
161
|
+
const effectiveAgentId = activeRun?.effective.agentId ?? sessions?.activeAgentId;
|
|
156
162
|
const nativeSession = effectiveAgentId === undefined
|
|
157
163
|
? null
|
|
158
164
|
: sessions?.sessions[effectiveAgentId] ?? null;
|
|
159
|
-
const effectiveLaunch =
|
|
160
|
-
const
|
|
165
|
+
const effectiveLaunch = activeRun?.effective ?? nativeSession?.effective ?? null;
|
|
166
|
+
const runSessionDrift = activeRun !== null
|
|
161
167
|
&& nativeSession !== null
|
|
162
|
-
&& !isDeepStrictEqual(
|
|
168
|
+
&& !isDeepStrictEqual(activeRun.effective, nativeSession.effective);
|
|
163
169
|
const recovery = inspectTaskRoleSessionRecovery(taskId, role.name, store);
|
|
164
170
|
const tmux = pane === undefined
|
|
165
171
|
? { state: "missing" }
|
|
@@ -170,30 +176,46 @@ function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputReques
|
|
|
170
176
|
...(pane.pid === undefined ? {} : { pid: pane.pid }),
|
|
171
177
|
currentCommand: pane.currentCommand
|
|
172
178
|
};
|
|
173
|
-
// The active
|
|
179
|
+
// The active AgentRun snapshot is authoritative for the live Role session. It
|
|
174
180
|
// may point at a ReviewRound-owned workspace, which is intentionally
|
|
175
181
|
// distinct from the WorkItem Develop workspace.
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
182
|
+
const actualWorkspaceRoot = effectiveLaunch?.workspace.root ?? role.workspace;
|
|
183
|
+
const managedWorkspace = activeRun?.workspace
|
|
184
|
+
?? store.listManagedWorkspaces(taskId).find(({ root }) => root === actualWorkspaceRoot)
|
|
185
|
+
?? null;
|
|
180
186
|
const workspace = managedWorkspace === null
|
|
181
|
-
? { managed: false, path:
|
|
187
|
+
? { managed: false, path: actualWorkspaceRoot }
|
|
182
188
|
: { ...managedWorkspace, managed: true };
|
|
183
189
|
const events = store.listEvents(taskId);
|
|
184
190
|
const sessionTokens = projectSessionTokenMetrics(events, resolveSessionTokenIdentity(nativeSession === null
|
|
185
191
|
? null
|
|
186
192
|
: { taskId, roleName: role.name, ...nativeSession }));
|
|
187
|
-
const runtime = projectTaskRoleRuntime(
|
|
188
|
-
const stalled =
|
|
189
|
-
const stallProgressAt =
|
|
193
|
+
const runtime = projectTaskRoleRuntime(activeRun, nativeSession, tmux, events, store.getWorkMailbox({ kind: "role", taskId, roleName: role.name }), store, taskId, role.name, now);
|
|
194
|
+
const stalled = activeRun !== null && isRoleRunStalled(events, activeRun.id);
|
|
195
|
+
const stallProgressAt = activeRun === null
|
|
190
196
|
? undefined
|
|
191
|
-
: latestStallProgressAt(events,
|
|
192
|
-
const stallKind =
|
|
197
|
+
: latestStallProgressAt(events, activeRun.id);
|
|
198
|
+
const stallKind = activeRun === null
|
|
193
199
|
? undefined
|
|
194
|
-
: latestStallKind(events,
|
|
195
|
-
const
|
|
196
|
-
|
|
200
|
+
: latestStallKind(events, activeRun.id);
|
|
201
|
+
const execution = activeRun === null ? undefined
|
|
202
|
+
: runExecutionObservation(activeRun, sessions?.providerBinding, events);
|
|
203
|
+
const conversation = sessions?.providerBinding == null
|
|
204
|
+
? undefined : currentProviderConversation(sessions.providerBinding);
|
|
205
|
+
const currentProvider = nativeSession !== null
|
|
206
|
+
&& conversation?.conversationId === nativeSession.nativeSessionId
|
|
207
|
+
? sessions?.providerBinding : undefined;
|
|
208
|
+
const computedHealth = calculateHealth(role, activeRun, lastRun, nativeSession, recovery.runtimeCleanupPending, tmux, openInputRequestCount, stalled, runtime, currentProvider?.run?.status, currentProvider != null && conversation?.recoverability === "unrecoverable");
|
|
209
|
+
const retainedInput = sessions?.providerBinding?.run;
|
|
210
|
+
const health = nativeSession === null && retainedInput != null
|
|
211
|
+
&& ["submitting", "accepted", "delivery-unknown"].includes(retainedInput.status)
|
|
212
|
+
? { health: "needs-attention",
|
|
213
|
+
healthReason: `Session cache is unbound; retained native input is ${retainedInput.status}. Inspect or replace this Role's Session.` }
|
|
214
|
+
: execution !== undefined && computedHealth.health === "running" && execution.delivery !== "accepted"
|
|
215
|
+
? { health: execution?.delivery === "delivery-unknown" ? "needs-attention" : "awaiting-provider-acceptance",
|
|
216
|
+
healthReason: `Execution record is open; native admission is ${execution?.delivery ?? "unobserved"}.` }
|
|
217
|
+
: computedHealth;
|
|
218
|
+
const stall = activeRun === null
|
|
197
219
|
? { active: false }
|
|
198
220
|
: {
|
|
199
221
|
active: stalled,
|
|
@@ -209,13 +231,14 @@ function inspectTaskRoleRuntimeStatus(taskId, role, store, pane, openInputReques
|
|
|
209
231
|
effectiveLaunch,
|
|
210
232
|
launchDrift: effectiveLaunch !== null
|
|
211
233
|
&& effectiveLaunch.sourceDesiredRevision !== role.launchRevision,
|
|
212
|
-
|
|
234
|
+
runSessionDrift,
|
|
213
235
|
...health,
|
|
214
236
|
openInputRequestCount,
|
|
215
237
|
role,
|
|
216
|
-
|
|
217
|
-
|
|
238
|
+
activeRun,
|
|
239
|
+
lastRun,
|
|
218
240
|
activeWork,
|
|
241
|
+
...(execution === undefined ? {} : { execution }),
|
|
219
242
|
nativeSession,
|
|
220
243
|
tmux,
|
|
221
244
|
workspace,
|
|
@@ -236,17 +259,17 @@ function sessionCumulativeTokenLabel(metrics) {
|
|
|
236
259
|
];
|
|
237
260
|
return `${total.totalTokens} (${breakdown.join(", ")})`;
|
|
238
261
|
}
|
|
239
|
-
function latestStallKind(events,
|
|
262
|
+
function latestStallKind(events, runId) {
|
|
240
263
|
const event = [...events]
|
|
241
|
-
.filter((candidate) => candidate.type === "
|
|
242
|
-
&& candidate.payload.
|
|
264
|
+
.filter((candidate) => candidate.type === "run.stalled"
|
|
265
|
+
&& candidate.payload.runId === runId
|
|
243
266
|
&& candidate.payload.status !== "diagnostic-only")
|
|
244
267
|
.sort((left, right) => Date.parse(right.createdAt) - Date.parse(left.createdAt))[0];
|
|
245
268
|
return event?.payload.kind === "delivery-stalled" || event?.payload.kind === "workflow-not-progressing"
|
|
246
269
|
? event.payload.kind
|
|
247
270
|
: undefined;
|
|
248
271
|
}
|
|
249
|
-
function calculateHealth(role,
|
|
272
|
+
function calculateHealth(role, activeRun, lastRun, nativeSession, runtimeCleanupPending, tmux, openInputRequestCount, stalled, runtime, nativeInputStatus, nativeConversationUnrecoverable = false) {
|
|
250
273
|
if (runtimeCleanupPending && nativeSession === null) {
|
|
251
274
|
return {
|
|
252
275
|
health: "needs-attention",
|
|
@@ -254,33 +277,40 @@ function calculateHealth(role, activeTurn, lastTurn, nativeSession, runtimeClean
|
|
|
254
277
|
};
|
|
255
278
|
}
|
|
256
279
|
if (nativeSession?.status === "ended" && nativeSession.endReason === "failed") {
|
|
257
|
-
// A broken Session only fails a live
|
|
258
|
-
// completed, the Session death is a lifecycle event, not a
|
|
259
|
-
// surface it as attention with the persisted
|
|
260
|
-
if (
|
|
280
|
+
// A broken Session only fails a live AgentRun. When the last AgentRun already
|
|
281
|
+
// completed, the Session death is a lifecycle event, not a AgentRun failure —
|
|
282
|
+
// surface it as attention with the persisted AgentRun outcome.
|
|
283
|
+
if (activeRun !== null) {
|
|
261
284
|
return { health: "failed", healthReason: "the active native session is broken" };
|
|
262
285
|
}
|
|
263
286
|
return {
|
|
264
287
|
health: "needs-attention",
|
|
265
|
-
healthReason:
|
|
288
|
+
healthReason: lastRun === null
|
|
266
289
|
? "the native session is broken"
|
|
267
|
-
: `the native session is broken; last
|
|
290
|
+
: `the native session is broken; last AgentRun ${lastRun.id} ${lastRun.status}`
|
|
268
291
|
};
|
|
269
292
|
}
|
|
270
293
|
if (tmux.state === "exited") {
|
|
294
|
+
if (nativeSession?.status === "ended" && nativeSession.endReason === "stopped"
|
|
295
|
+
&& activeRun === null && !runtimeCleanupPending
|
|
296
|
+
&& !["submitting", "accepted", "delivery-unknown"].includes(nativeInputStatus ?? "")) {
|
|
297
|
+
return role.name === "leader" && openInputRequestCount > 0
|
|
298
|
+
? { health: "blocked-input", healthReason: `${openInputRequestCount} durable InputRequest(s) still require user input` }
|
|
299
|
+
: { health: "idle", healthReason: "the Session was deliberately stopped; no native input remains unsettled" };
|
|
300
|
+
}
|
|
271
301
|
return {
|
|
272
302
|
health: "needs-attention",
|
|
273
303
|
healthReason: "the tmux pane exited; Provider Conversation/continuation state is unobservable"
|
|
274
304
|
};
|
|
275
305
|
}
|
|
276
|
-
if (
|
|
306
|
+
if (activeRun !== null) {
|
|
277
307
|
if (tmux.state !== "running") {
|
|
278
|
-
return { health: "needs-attention", healthReason: "the active
|
|
308
|
+
return { health: "needs-attention", healthReason: "the active AgentRun has no live tmux pane" };
|
|
279
309
|
}
|
|
280
310
|
if (stalled) {
|
|
281
311
|
return {
|
|
282
312
|
health: "needs-attention",
|
|
283
|
-
healthReason: "the live active
|
|
313
|
+
healthReason: "the live active AgentRun has no durable progress in the configured stall window"
|
|
284
314
|
};
|
|
285
315
|
}
|
|
286
316
|
if (runtime !== null) {
|
|
@@ -315,7 +345,7 @@ function calculateHealth(role, activeTurn, lastTurn, nativeSession, runtimeClean
|
|
|
315
345
|
}
|
|
316
346
|
return {
|
|
317
347
|
health: "starting",
|
|
318
|
-
healthReason: "the active
|
|
348
|
+
healthReason: "the active AgentRun is awaiting Provider runtime observations"
|
|
319
349
|
};
|
|
320
350
|
}
|
|
321
351
|
if (nativeSession?.status === "active" && tmux.state !== "running") {
|
|
@@ -324,6 +354,20 @@ function calculateHealth(role, activeTurn, lastTurn, nativeSession, runtimeClean
|
|
|
324
354
|
if (nativeSession?.status === "ended" && tmux.state === "running") {
|
|
325
355
|
return { health: "needs-attention", healthReason: "a stopped native session has a live tmux pane" };
|
|
326
356
|
}
|
|
357
|
+
if (nativeSession?.status === "active" && tmux.state === "running") {
|
|
358
|
+
if (nativeConversationUnrecoverable) {
|
|
359
|
+
return { health: "needs-attention", healthReason: "the native conversation cannot resume; stop its idle Session and explicitly select a new one" };
|
|
360
|
+
}
|
|
361
|
+
if (nativeInputStatus === "submitting") {
|
|
362
|
+
return { health: "awaiting-provider-acceptance", healthReason: "native input is submitting; no AgentRun is required for a notification" };
|
|
363
|
+
}
|
|
364
|
+
if (nativeInputStatus === "accepted") {
|
|
365
|
+
return { health: "running", healthReason: "accepted native input is awaiting its terminal, independently of AgentRun records" };
|
|
366
|
+
}
|
|
367
|
+
if (nativeInputStatus === "delivery-unknown") {
|
|
368
|
+
return { health: "needs-attention", healthReason: "native input disposition is unknown; absence of an AgentRun does not prove idle" };
|
|
369
|
+
}
|
|
370
|
+
}
|
|
327
371
|
if (role.name === "leader" && openInputRequestCount > 0) {
|
|
328
372
|
return {
|
|
329
373
|
health: "blocked-input",
|
|
@@ -335,52 +379,51 @@ function calculateHealth(role, activeTurn, lastTurn, nativeSession, runtimeClean
|
|
|
335
379
|
: { health: "idle", healthReason: "there is no active work or live tmux pane" };
|
|
336
380
|
}
|
|
337
381
|
function projectTaskRoleRuntime(run, session, tmux, events, _mailbox, store, taskId, roleName, now) {
|
|
338
|
-
if (
|
|
382
|
+
if (session === null)
|
|
383
|
+
return null;
|
|
384
|
+
const observed = store.getTaskRoleSessionSet(taskId, roleName)?.providerBinding?.run;
|
|
385
|
+
// A newly admitted Run must not inherit the preceding input's receipt. A
|
|
386
|
+
// terminal Run no longer occupies the active index, but its exact binding
|
|
387
|
+
// still identifies the observations to render.
|
|
388
|
+
const native = run !== null && managedProviderTurnId(observed) !== run.id ? undefined : observed;
|
|
389
|
+
if (run === null && native == null)
|
|
339
390
|
return null;
|
|
340
391
|
let driverId;
|
|
341
392
|
try {
|
|
342
|
-
driverId = builtinDriverIdForAdapter(run
|
|
393
|
+
driverId = builtinDriverIdForAdapter(run?.effective.adapterId ?? session.adapterId);
|
|
343
394
|
}
|
|
344
395
|
catch {
|
|
345
396
|
return null;
|
|
346
397
|
}
|
|
347
|
-
const
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
398
|
+
const createdAt = run?.createdAt ?? native.submittedAt;
|
|
399
|
+
const updatedAt = run?.updatedAt ?? native.updatedAt;
|
|
400
|
+
const receiptId = native?.attemptId ?? formatRunReceiptId(taskId, run.id);
|
|
401
|
+
const basicFence = {
|
|
402
|
+
taskId,
|
|
403
|
+
roleName,
|
|
404
|
+
...(run?.id === undefined && native?.runId === undefined ? {} : { runId: run?.id ?? native?.runId }),
|
|
405
|
+
agentId: run?.effective.agentId ?? session.agentId,
|
|
352
406
|
driverId,
|
|
353
|
-
runtimeGenerationId: session.runtimeGenerationId,
|
|
354
407
|
nativeSessionId: session.nativeSessionId,
|
|
355
|
-
|
|
356
|
-
taskId: run.taskId,
|
|
357
|
-
roleName: run.roleName,
|
|
358
|
-
turnId: run.id,
|
|
359
|
-
agentId: run.effective.agentId,
|
|
360
|
-
driverId,
|
|
361
|
-
runtimeGenerationId: session.runtimeGenerationId,
|
|
362
|
-
nativeSessionId: session.nativeSessionId,
|
|
363
|
-
receiptId: store.getTaskRoleSessionSet(taskId, roleName)?.providerBinding?.turn?.attemptId
|
|
364
|
-
?? formatTurnReceiptId(run.taskId, run.id)
|
|
365
|
-
}) ?? run.id,
|
|
366
|
-
receiptId: store.getTaskRoleSessionSet(taskId, roleName)?.providerBinding?.turn?.attemptId
|
|
367
|
-
?? formatTurnReceiptId(run.taskId, run.id)
|
|
408
|
+
receiptId
|
|
368
409
|
};
|
|
369
|
-
|
|
410
|
+
const nativeTurnId = native?.nativeTurnId ?? runtimeNativeTurnId(events, basicFence);
|
|
411
|
+
const fence = { ...basicFence, ...(nativeTurnId === undefined ? {} : { nativeTurnId }) };
|
|
412
|
+
let projection = projectRuntimeTaskEvents(fence, createdAt, events);
|
|
370
413
|
projection = projectRuntimeObservation(projection, createRuntimeObservation({
|
|
371
414
|
schemaVersion: 4,
|
|
372
|
-
eventId: `runtime-host-${
|
|
373
|
-
semanticKey: `runtime-host-${
|
|
415
|
+
eventId: `runtime-host-${receiptId}`,
|
|
416
|
+
semanticKey: `runtime-host-${receiptId}`,
|
|
374
417
|
kind: "host.observed",
|
|
375
418
|
authority: "host",
|
|
376
|
-
receivedAt:
|
|
419
|
+
receivedAt: updatedAt,
|
|
377
420
|
fence,
|
|
378
421
|
payload: { alive: tmux.state === "running" }
|
|
379
422
|
}));
|
|
380
423
|
// The semantic progress fence is the same durable fold the scheduler stall
|
|
381
424
|
// pass consumes, so CLI/Web/scheduler share one progress clock.
|
|
382
|
-
const semanticProgress =
|
|
383
|
-
?? { progressAt:
|
|
425
|
+
const semanticProgress = (run === null ? undefined : latestRunDurableProgressAt(store, taskId, roleName, run.id))
|
|
426
|
+
?? { progressAt: createdAt };
|
|
384
427
|
const classification = classifyRuntimeHealth({
|
|
385
428
|
projection,
|
|
386
429
|
semanticProgressAt: semanticProgress.progressAt,
|
|
@@ -414,16 +457,7 @@ function projectTaskRoleRuntime(run, session, tmux, events, _mailbox, store, tas
|
|
|
414
457
|
function runtimeNativeTurnId(events, expected) {
|
|
415
458
|
const observations = events
|
|
416
459
|
.map(runtimeObservationFromTaskEvent)
|
|
417
|
-
.filter((observation) => observation !== null
|
|
418
|
-
&& observation.fence.taskId === expected.taskId
|
|
419
|
-
&& observation.fence.roleName === expected.roleName
|
|
420
|
-
&& observation.fence.turnId === expected.turnId
|
|
421
|
-
&& observation.fence.agentId === expected.agentId
|
|
422
|
-
&& observation.fence.driverId === expected.driverId
|
|
423
|
-
&& observation.fence.runtimeGenerationId === expected.runtimeGenerationId
|
|
424
|
-
&& observation.fence.nativeSessionId === expected.nativeSessionId
|
|
425
|
-
&& observation.fence.receiptId === expected.receiptId
|
|
426
|
-
&& observation.fence.nativeTurnId !== undefined)
|
|
460
|
+
.filter((observation) => observation !== null && observation.fence.taskId === expected.taskId && observation.fence.roleName === expected.roleName && observation.fence.runId === expected.runId && observation.fence.agentId === expected.agentId && observation.fence.driverId === expected.driverId && observation.fence.nativeSessionId === expected.nativeSessionId && observation.fence.receiptId === expected.receiptId && observation.fence.nativeTurnId !== undefined)
|
|
427
461
|
.sort((left, right) => (left.receivedAt.localeCompare(right.receivedAt)
|
|
428
462
|
|| (left.sequence ?? -1) - (right.sequence ?? -1)
|
|
429
463
|
|| (left.ordinal ?? -1) - (right.ordinal ?? -1)
|
|
@@ -432,6 +466,3 @@ function runtimeNativeTurnId(events, expected) {
|
|
|
432
466
|
?.fence.nativeTurnId
|
|
433
467
|
?? observations.at(-1)?.fence.nativeTurnId;
|
|
434
468
|
}
|
|
435
|
-
function activeTurnDeliveryLabel(run) {
|
|
436
|
-
return run.status === "active" ? "active" : run.status;
|
|
437
|
-
}
|