@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
|
@@ -8,9 +8,9 @@ export function buildTaskObservabilityProjection(input) {
|
|
|
8
8
|
const dag = projectDag(input.workItems);
|
|
9
9
|
const workItems = input.workItems.map((item) => {
|
|
10
10
|
const groups = item.executionGroups;
|
|
11
|
-
const itemCost = projectWorkItemCost(groups, input.
|
|
12
|
-
const itemContext = projectContext(groups, input.
|
|
13
|
-
const producerObservability = projectWorkItemProducerObservability(item, input.
|
|
11
|
+
const itemCost = projectWorkItemCost(groups, input.runs, now);
|
|
12
|
+
const itemContext = projectContext(groups, input.runs, input.contextSnapshots);
|
|
13
|
+
const producerObservability = projectWorkItemProducerObservability(item, input.runs);
|
|
14
14
|
return Object.freeze({
|
|
15
15
|
workItemId: item.id,
|
|
16
16
|
title: item.title,
|
|
@@ -21,8 +21,8 @@ export function buildTaskObservabilityProjection(input) {
|
|
|
21
21
|
resultCount: producerObservability?.resultCount ?? null
|
|
22
22
|
});
|
|
23
23
|
});
|
|
24
|
-
const cost = projectCost(input.executionGroups, input.
|
|
25
|
-
const context = projectContext(input.executionGroups, input.
|
|
24
|
+
const cost = projectCost(input.executionGroups, input.runs, input.events, now);
|
|
25
|
+
const context = projectContext(input.executionGroups, input.runs, input.contextSnapshots);
|
|
26
26
|
return Object.freeze({
|
|
27
27
|
dag,
|
|
28
28
|
workItems,
|
|
@@ -31,21 +31,21 @@ export function buildTaskObservabilityProjection(input) {
|
|
|
31
31
|
sessionTokens: Object.freeze([...(input.sessionTokens ?? [])])
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
|
-
function projectWorkItemProducerObservability(item,
|
|
34
|
+
function projectWorkItemProducerObservability(item, runs) {
|
|
35
35
|
const successfulLanes = item.executionGroups.flatMap((group) => group.lanes.flatMap((lane) => (lane.disposition === "succeeded" ? [{ group, lane }] : [])));
|
|
36
36
|
let resultCount = 0;
|
|
37
37
|
for (const { group, lane } of successfulLanes) {
|
|
38
|
-
const
|
|
38
|
+
const run = lane.successfulRunId === undefined
|
|
39
39
|
? undefined
|
|
40
|
-
:
|
|
41
|
-
if (
|
|
42
|
-
||
|
|
43
|
-
||
|
|
44
|
-
||
|
|
45
|
-
||
|
|
46
|
-
||
|
|
47
|
-
||
|
|
48
|
-
||
|
|
40
|
+
: runs.find(({ id }) => id === lane.successfulRunId);
|
|
41
|
+
if (run === undefined
|
|
42
|
+
|| run.result === undefined
|
|
43
|
+
|| run.status !== "completed"
|
|
44
|
+
|| run.taskId !== item.taskId
|
|
45
|
+
|| run.workItemId !== item.id
|
|
46
|
+
|| run.executionGroupId !== group.id
|
|
47
|
+
|| run.executionLaneId !== lane.id
|
|
48
|
+
|| run.roleName !== lane.roleName)
|
|
49
49
|
return null;
|
|
50
50
|
resultCount += 1;
|
|
51
51
|
}
|
|
@@ -69,7 +69,7 @@ function projectDag(workItems) {
|
|
|
69
69
|
const unresolved = item.dependsOn.filter((dependency) => {
|
|
70
70
|
return !dependencySatisfied(dependency, byId);
|
|
71
71
|
});
|
|
72
|
-
const projectedStatus = item.status === "
|
|
72
|
+
const projectedStatus = item.status === "open"
|
|
73
73
|
? unresolved.length === 0 ? "ready" : "blocked"
|
|
74
74
|
: item.status;
|
|
75
75
|
return Object.freeze({
|
|
@@ -110,13 +110,13 @@ function rootCauses(id, byId) {
|
|
|
110
110
|
}
|
|
111
111
|
const unresolved = item.dependsOn.filter((dependency) => !dependencySatisfied(dependency, byId));
|
|
112
112
|
if (unresolved.length === 0) {
|
|
113
|
-
if (item.status === "
|
|
113
|
+
if (item.status === "open" && item.currentCandidateId !== undefined)
|
|
114
114
|
result.push(item.id);
|
|
115
115
|
return;
|
|
116
116
|
}
|
|
117
117
|
for (const dependency of unresolved) {
|
|
118
118
|
const target = byId.get(dependency);
|
|
119
|
-
if (target?.status === "
|
|
119
|
+
if (target?.status === "open" && target.currentCandidateId !== undefined)
|
|
120
120
|
result.push(target.id);
|
|
121
121
|
else
|
|
122
122
|
visit(dependency);
|
|
@@ -126,7 +126,7 @@ function rootCauses(id, byId) {
|
|
|
126
126
|
return [...new Set(result)];
|
|
127
127
|
}
|
|
128
128
|
function dependencySatisfied(id, byId) {
|
|
129
|
-
return byId.get(id)?.status === "
|
|
129
|
+
return byId.get(id)?.status === "accepted";
|
|
130
130
|
}
|
|
131
131
|
function dependencyEdgeStatus(id, byId) {
|
|
132
132
|
const target = byId.get(id);
|
|
@@ -136,14 +136,14 @@ function dependencyEdgeStatus(id, byId) {
|
|
|
136
136
|
return "satisfied";
|
|
137
137
|
if (target.status === "retired")
|
|
138
138
|
return "dead";
|
|
139
|
-
if (target.status === "
|
|
140
|
-
return "
|
|
139
|
+
if (target.status === "open" && target.currentCandidateId !== undefined)
|
|
140
|
+
return "active";
|
|
141
141
|
return "active";
|
|
142
142
|
}
|
|
143
|
-
function projectCost(groups,
|
|
143
|
+
function projectCost(groups, runs, _events, now) {
|
|
144
144
|
const uniqueGroups = [...new Map(groups.map((group) => [group.id, group])).values()];
|
|
145
145
|
const groupIds = new Set(uniqueGroups.map(({ id }) => id));
|
|
146
|
-
const attempts =
|
|
146
|
+
const attempts = runs.filter(({ executionGroupId }) => (executionGroupId !== undefined && groupIds.has(executionGroupId)));
|
|
147
147
|
const laneCount = uniqueGroups.reduce((total, group) => total + group.lanes.length, 0);
|
|
148
148
|
return Object.freeze({
|
|
149
149
|
tokens: 0,
|
|
@@ -158,9 +158,9 @@ function projectCost(groups, turns, _events, now) {
|
|
|
158
158
|
marginalValueStatus: "unavailable"
|
|
159
159
|
});
|
|
160
160
|
}
|
|
161
|
-
function projectWorkItemCost(groups,
|
|
161
|
+
function projectWorkItemCost(groups, runs, now) {
|
|
162
162
|
const groupIds = new Set(groups.map(({ id }) => id));
|
|
163
|
-
const attempts =
|
|
163
|
+
const attempts = runs.filter(({ executionGroupId }) => (executionGroupId !== undefined && groupIds.has(executionGroupId)));
|
|
164
164
|
const wallClockSeconds = groups.reduce((total, group) => {
|
|
165
165
|
const started = Date.parse(group.createdAt);
|
|
166
166
|
const ended = group.lanes.some(({ disposition }) => disposition === "open")
|
|
@@ -184,15 +184,15 @@ function projectWorkItemCost(groups, turns, now) {
|
|
|
184
184
|
marginalValueStatus: "unavailable"
|
|
185
185
|
});
|
|
186
186
|
}
|
|
187
|
-
function projectContext(groups,
|
|
187
|
+
function projectContext(groups, runs, snapshots) {
|
|
188
188
|
const refs = new Map();
|
|
189
189
|
for (const group of groups) {
|
|
190
190
|
const ref = group.assignment.contextSnapshotRef;
|
|
191
191
|
if (ref !== undefined)
|
|
192
192
|
refs.set(ref.id, ref);
|
|
193
193
|
}
|
|
194
|
-
for (const
|
|
195
|
-
const ref =
|
|
194
|
+
for (const run of runs) {
|
|
195
|
+
const ref = run.inputs[0].input.contextSnapshotRef;
|
|
196
196
|
if (ref !== undefined)
|
|
197
197
|
refs.set(ref.id, ref);
|
|
198
198
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { requireIdentity, requirePositiveInteger, requireText, requireTimestamp } from "../domain/validation.js";
|
|
2
2
|
import { TASK_RECORD_ID_PREFIXES } from "../task/taskRecordReference.js";
|
|
3
|
+
import { copyRef } from "../coordination/workMailbox.js";
|
|
3
4
|
export const CURRENT_TASK_WAKE_SCHEMA_VERSION = 1;
|
|
4
5
|
export function createTaskWake(input) {
|
|
5
6
|
const wake = {
|
|
@@ -8,16 +9,22 @@ export function createTaskWake(input) {
|
|
|
8
9
|
taskId: requireIdentity(input.taskId, "Task id"),
|
|
9
10
|
seq: wakeSequence(input.id),
|
|
10
11
|
reasons: Object.freeze(input.reasons.map((reason) => requireText(reason, "Wake reason"))),
|
|
12
|
+
...(input.refs === undefined ? {} : { refs: Object.freeze(input.refs.map(copyRef)) }),
|
|
11
13
|
fromCursor: requireTimestamp(input.fromCursor, "Wake fromCursor"),
|
|
12
14
|
toCursor: requireTimestamp(input.toCursor, "Wake toCursor"),
|
|
13
15
|
status: "dispatched",
|
|
14
|
-
|
|
16
|
+
...(input.runId === undefined ? {} : { runId: requireIdentity(input.runId, "Wake AgentRun id") }),
|
|
15
17
|
createdAt: input.now.toISOString()
|
|
16
18
|
};
|
|
17
19
|
validateTaskWake(wake);
|
|
18
20
|
return wake;
|
|
19
21
|
}
|
|
20
22
|
export function validateTaskWake(wake) {
|
|
23
|
+
if (wake.refs !== undefined) {
|
|
24
|
+
if (!Array.isArray(wake.refs))
|
|
25
|
+
throw new Error("Wake references must be an array.");
|
|
26
|
+
wake.refs.forEach(copyRef);
|
|
27
|
+
}
|
|
21
28
|
if (wake.schemaVersion !== CURRENT_TASK_WAKE_SCHEMA_VERSION) {
|
|
22
29
|
throw new Error(`Task wake ${wake.id} must use schemaVersion ${CURRENT_TASK_WAKE_SCHEMA_VERSION}.`);
|
|
23
30
|
}
|
|
@@ -55,12 +62,12 @@ export function latestTaskWake(wakes) {
|
|
|
55
62
|
return latest;
|
|
56
63
|
}
|
|
57
64
|
/**
|
|
58
|
-
* The delta lower bound for a Task's first wake: the last Leader
|
|
65
|
+
* The delta lower bound for a Task's first wake: the last Leader AgentRun's
|
|
59
66
|
* creation time, preserving the pre-ledger watermark semantics. When no
|
|
60
|
-
* Leader
|
|
67
|
+
* Leader AgentRun exists, the Task's own creation time bounds the window.
|
|
61
68
|
*/
|
|
62
69
|
export function fallbackWakeCursor(input) {
|
|
63
|
-
return input.
|
|
70
|
+
return input.leaderRunCreatedAt ?? input.taskCreatedAt;
|
|
64
71
|
}
|
|
65
72
|
function wakeSequence(id) {
|
|
66
73
|
const prefix = `${TASK_RECORD_ID_PREFIXES.taskWake}-`;
|
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
* queue — the wake layer owns the meaning of its tags.
|
|
9
9
|
*
|
|
10
10
|
* Tag wire format: `<kind>` or `<kind>:<ref>`. The ref is an exact record
|
|
11
|
-
* identifier (InputRequest id,
|
|
11
|
+
* identifier (InputRequest id, AgentRun id, ...) or, for `force-wake`, the
|
|
12
12
|
* operator-supplied text. Unknown kinds parse losslessly so a newer producer
|
|
13
13
|
* can never wedge an older reader.
|
|
14
14
|
*/
|
|
@@ -17,6 +17,7 @@ export const WAKE_REASON_KINDS = Object.freeze([
|
|
|
17
17
|
"input-timeout",
|
|
18
18
|
"role-turn-stalled",
|
|
19
19
|
"agent-error",
|
|
20
|
+
"activation-failed",
|
|
20
21
|
"agent-session-recovery",
|
|
21
22
|
"review-failed",
|
|
22
23
|
"task-reopened",
|
|
@@ -27,6 +28,13 @@ export const WAKE_REASON_KINDS = Object.freeze([
|
|
|
27
28
|
"force-wake"
|
|
28
29
|
]);
|
|
29
30
|
const IMMEDIATE_KINDS = new Set([
|
|
31
|
+
"operator-input",
|
|
32
|
+
"user-message",
|
|
33
|
+
"task-created",
|
|
34
|
+
"execution-started",
|
|
35
|
+
"session-replaced",
|
|
36
|
+
"force-wake",
|
|
37
|
+
"activation-failed",
|
|
30
38
|
"task-reopened",
|
|
31
39
|
"leader-turn-failed",
|
|
32
40
|
"role-turn-failed"
|
|
@@ -60,6 +60,7 @@ export async function runSetupCommand(args, env, executor, io = {}) {
|
|
|
60
60
|
`Leader configuration: ${roleStatus.leader}.`,
|
|
61
61
|
`Tmux: ${tmuxBin}.`,
|
|
62
62
|
"Setup did not configure Review, Worker, Profiles, or shell completion.",
|
|
63
|
+
"Provider login and native key/initialization confirmations remain with the Provider; setup did not call a model.",
|
|
63
64
|
"Run `yui operator enter` to continue, or `yui config show` to inspect configuration."
|
|
64
65
|
].join("\n")}\n`;
|
|
65
66
|
}
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
import { createContextSnapshot, contextContentDigest } from "../../context/contextSnapshot.js";
|
|
2
|
+
/** Storage 12 -> 13 only. Text, native identities and opaque record/receipt ids
|
|
3
|
+
* are never renamed. Ordinary runtime stores do not interpret old payloads.
|
|
4
|
+
* v13 also introduces optional Message recipient/continuation/handovers.
|
|
5
|
+
* Existing messages have no recipient and remain informational/Leader wakes;
|
|
6
|
+
* migration never guesses an owner or causes historical messages to execute.
|
|
7
|
+
*/
|
|
8
|
+
export function migrateAgentRunContract(db) {
|
|
9
|
+
// T08 already stored real planning executions. Use that frozen evidence,
|
|
10
|
+
// never the Task's current status, to classify their shared Session snapshots.
|
|
11
|
+
const planningLaunches = new Set();
|
|
12
|
+
for (const row of db.prepare("SELECT task_id, payload FROM turns").all()) {
|
|
13
|
+
const run = JSON.parse(row.payload);
|
|
14
|
+
if (run.purpose === "planning") {
|
|
15
|
+
planningLaunches.add(`${row.task_id}:${contextContentDigest(run.effective)}`);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
const snapshotDigests = new Map();
|
|
19
|
+
const renameKey = (key) => {
|
|
20
|
+
if (key === "turn")
|
|
21
|
+
return "run";
|
|
22
|
+
if (key === "turns")
|
|
23
|
+
return "runs";
|
|
24
|
+
if (key === "turnId")
|
|
25
|
+
return "runId";
|
|
26
|
+
if (key === "turnIds")
|
|
27
|
+
return "runIds";
|
|
28
|
+
if (/NativeTurn|ProviderTurn|CompletedTurn|nativeTurn|providerTurn/.test(key))
|
|
29
|
+
return key;
|
|
30
|
+
return key.replaceAll("Turn", "Run").replace(/^turn(?=[A-Z])/, "run")
|
|
31
|
+
.replaceAll("TURN_", "RUN_").replaceAll("_TURN", "_RUN");
|
|
32
|
+
};
|
|
33
|
+
const untouched = new Set(["advanced", "metadata", "environment", "raw", "output", "body",
|
|
34
|
+
"description", "summary", "directive", "config"]);
|
|
35
|
+
const migrate = (value, taskId, purpose) => {
|
|
36
|
+
if (Array.isArray(value))
|
|
37
|
+
return value.map((entry) => migrate(entry, taskId, purpose));
|
|
38
|
+
if (value === null || typeof value !== "object")
|
|
39
|
+
return value;
|
|
40
|
+
const input = value;
|
|
41
|
+
const ownerTaskId = typeof input.taskId === "string" ? input.taskId : taskId;
|
|
42
|
+
const executionPurpose = typeof input.purpose === "string" ? input.purpose : purpose;
|
|
43
|
+
const result = {};
|
|
44
|
+
for (const [key, child] of Object.entries(input)) {
|
|
45
|
+
let next = untouched.has(key) ? child : migrate(child, ownerTaskId, executionPurpose);
|
|
46
|
+
if ((key === "type" || key === "recordKind" || key === "store") && child === "turn")
|
|
47
|
+
next = "run";
|
|
48
|
+
if (key === "type" && typeof child === "string" && child.startsWith("turn.")
|
|
49
|
+
&& typeof input.id === "string" && input.id.startsWith("event-") && input.createdAt !== undefined) {
|
|
50
|
+
next = child.replace(/^turn\./, "run.");
|
|
51
|
+
}
|
|
52
|
+
if (key === "store" && child === "source-turn")
|
|
53
|
+
next = "source-run";
|
|
54
|
+
// Runtime envelopes are structured evidence encoded inside Task Events,
|
|
55
|
+
// not free-form Provider output.
|
|
56
|
+
if (key === "observation" && typeof child === "string") {
|
|
57
|
+
const parsed = JSON.parse(child);
|
|
58
|
+
next = JSON.stringify(migrate(parsed, ownerTaskId));
|
|
59
|
+
}
|
|
60
|
+
result[renameKey(key)] = next;
|
|
61
|
+
}
|
|
62
|
+
if (typeof input.digest === "string" && typeof input.id === "string"
|
|
63
|
+
&& input.id.startsWith("context-snapshot-") && typeof input.sequence === "number") {
|
|
64
|
+
result.digest = snapshotDigests.get(input.digest) ?? input.digest;
|
|
65
|
+
}
|
|
66
|
+
// A Run has its own authoritative purpose. Session copies are classified
|
|
67
|
+
// by the exact planning launch they retain; activation never upgrades them.
|
|
68
|
+
if (input.schemaVersion === 3 && typeof input.agentId === "string"
|
|
69
|
+
&& typeof input.sourceDesiredRevision === "number" && input.workspace !== undefined
|
|
70
|
+
&& input.context !== undefined && input.permission !== undefined) {
|
|
71
|
+
result.executionAuthority = executionPurpose === "planning"
|
|
72
|
+
|| (ownerTaskId !== undefined && planningLaunches.has(`${ownerTaskId}:${contextContentDigest(input)}`))
|
|
73
|
+
? "planning" : "delivery";
|
|
74
|
+
}
|
|
75
|
+
return result;
|
|
76
|
+
};
|
|
77
|
+
const snapshots = db.prepare("SELECT rowid, payload FROM context_snapshots")
|
|
78
|
+
.all();
|
|
79
|
+
const originals = new Map(snapshots.map((row) => {
|
|
80
|
+
const snapshot = JSON.parse(row.payload);
|
|
81
|
+
return [`${snapshot.taskId}/${snapshot.id}`, { ...row, snapshot }];
|
|
82
|
+
}));
|
|
83
|
+
const visiting = new Set();
|
|
84
|
+
const completed = new Set();
|
|
85
|
+
const migrateSnapshot = (key) => {
|
|
86
|
+
if (completed.has(key))
|
|
87
|
+
return;
|
|
88
|
+
const row = originals.get(key);
|
|
89
|
+
if (row === undefined)
|
|
90
|
+
return; // Preserve unresolved historical references as references.
|
|
91
|
+
if (visiting.has(key))
|
|
92
|
+
throw new Error("Context Snapshot references contain a cycle.");
|
|
93
|
+
visiting.add(key);
|
|
94
|
+
const original = row.snapshot;
|
|
95
|
+
// Dependency order, not wall-clock order: async callers can freeze with
|
|
96
|
+
// an earlier pass timestamp while referencing an already committed parent.
|
|
97
|
+
const visitReferences = (value) => {
|
|
98
|
+
if (value === null || typeof value !== "object")
|
|
99
|
+
return;
|
|
100
|
+
if (Array.isArray(value)) {
|
|
101
|
+
value.forEach(visitReferences);
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
const ref = value;
|
|
105
|
+
if (typeof ref.id === "string" && ref.id.startsWith("context-snapshot-")
|
|
106
|
+
&& typeof ref.taskId === "string" && typeof ref.digest === "string"
|
|
107
|
+
&& typeof ref.sequence === "number")
|
|
108
|
+
migrateSnapshot(`${ref.taskId}/${ref.id}`);
|
|
109
|
+
for (const [field, child] of Object.entries(ref)) {
|
|
110
|
+
if (!untouched.has(field))
|
|
111
|
+
visitReferences(child);
|
|
112
|
+
}
|
|
113
|
+
};
|
|
114
|
+
visitReferences(original.parentRef);
|
|
115
|
+
visitReferences(original.resources);
|
|
116
|
+
const current = migrate(original);
|
|
117
|
+
const resources = current.resources.map((entry) => ({
|
|
118
|
+
...entry, ref: { ...entry.ref, digest: contextContentDigest(entry.value) }
|
|
119
|
+
}));
|
|
120
|
+
const refs = current.refs.map((ref) => resources.find((entry) => entry.ref.layer === ref.layer && entry.ref.store === ref.store && entry.ref.refId === ref.refId)?.ref ?? ref);
|
|
121
|
+
const snapshot = createContextSnapshot({ ...current, resources, refs, frozenAt: new Date(current.frozenAt) });
|
|
122
|
+
snapshotDigests.set(original.digest, snapshot.digest);
|
|
123
|
+
db.prepare("UPDATE context_snapshots SET payload = ?, digest = ? WHERE rowid = ?")
|
|
124
|
+
.run(JSON.stringify(snapshot), snapshot.digest, row.rowid);
|
|
125
|
+
visiting.delete(key);
|
|
126
|
+
completed.add(key);
|
|
127
|
+
};
|
|
128
|
+
for (const key of originals.keys())
|
|
129
|
+
migrateSnapshot(key);
|
|
130
|
+
const tables = [
|
|
131
|
+
"config", "agent_profiles", "global_roles", "global_role_session_sets",
|
|
132
|
+
"task_records", "task_roles", "role_session_sets", "work_items", "work_item_candidates",
|
|
133
|
+
"turns", "active_turns", "review_rounds", "change_sets", "integration_attempts",
|
|
134
|
+
"messages", "input_requests", "decisions", "milestones", "events", "task_projections",
|
|
135
|
+
"durable_jobs", "integration_queue", "task_wakes", "telemetry", "telemetry_aggregate"
|
|
136
|
+
];
|
|
137
|
+
const rewriteColumn = (table, column) => {
|
|
138
|
+
const columns = db.prepare(`PRAGMA table_info("${table}")`).all();
|
|
139
|
+
if (!columns.some((entry) => entry.name === column))
|
|
140
|
+
return;
|
|
141
|
+
const keys = columns.filter((entry) => entry.pk > 0).sort((a, b) => a.pk - b.pk).map((entry) => entry.name);
|
|
142
|
+
if (!keys.length)
|
|
143
|
+
throw new Error(`Migration requires the primary key of ${table}.`);
|
|
144
|
+
const quote = (value) => `"${value.replaceAll('"', '""')}"`;
|
|
145
|
+
const rows = db.prepare(`SELECT * FROM ${quote(table)} WHERE ${quote(column)} IS NOT NULL`)
|
|
146
|
+
.all();
|
|
147
|
+
const update = db.prepare(`UPDATE ${quote(table)} SET ${quote(column)} = ? WHERE ${keys.map((key) => `${quote(key)} = ?`).join(" AND ")}`);
|
|
148
|
+
for (const row of rows) {
|
|
149
|
+
update.run(JSON.stringify(migrate(JSON.parse(row[column]), typeof row.task_id === "string" ? row.task_id : undefined)), ...keys.map((key) => row[key]));
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
for (const table of tables) {
|
|
153
|
+
rewriteColumn(table, "payload");
|
|
154
|
+
}
|
|
155
|
+
for (const column of ["pending", "processing"]) {
|
|
156
|
+
rewriteColumn("mailboxes", column);
|
|
157
|
+
}
|
|
158
|
+
db.prepare("UPDATE id_sequences SET kind = 'run' WHERE kind = 'turn'").run();
|
|
159
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
/** Storage 3 -> 4. Frozen migration; runtime code must not depend on this module. */
|
|
3
|
+
export const REMOVE_RUNTIME_GENERATION_SQL = `
|
|
4
|
+
-- data-transform: remove-runtime-generation-records-v1
|
|
5
|
+
DROP TRIGGER telemetry_ai;
|
|
6
|
+
DROP TRIGGER telemetry_au;
|
|
7
|
+
DROP INDEX idx_telemetry_turn;
|
|
8
|
+
ALTER TABLE telemetry RENAME TO telemetry_v3;
|
|
9
|
+
ALTER TABLE telemetry_aggregate RENAME TO telemetry_aggregate_v3;
|
|
10
|
+
CREATE TABLE telemetry (
|
|
11
|
+
task_id TEXT NOT NULL, role_name TEXT NOT NULL, turn_id TEXT NOT NULL,
|
|
12
|
+
progress_id TEXT NOT NULL, sequence INTEGER, payload TEXT NOT NULL,
|
|
13
|
+
received_at TEXT NOT NULL,
|
|
14
|
+
PRIMARY KEY (task_id, role_name, turn_id, progress_id)
|
|
15
|
+
) WITHOUT ROWID;
|
|
16
|
+
CREATE INDEX idx_telemetry_turn ON telemetry(task_id, turn_id);
|
|
17
|
+
INSERT INTO telemetry
|
|
18
|
+
SELECT task_id, role_name, turn_id, progress_id, sequence, payload, received_at
|
|
19
|
+
FROM (
|
|
20
|
+
SELECT *, ROW_NUMBER() OVER (
|
|
21
|
+
PARTITION BY task_id, role_name, turn_id, progress_id
|
|
22
|
+
ORDER BY received_at DESC, COALESCE(sequence, -1) DESC, generation DESC
|
|
23
|
+
) AS rank FROM telemetry_v3
|
|
24
|
+
) WHERE rank = 1;
|
|
25
|
+
CREATE TABLE telemetry_aggregate (
|
|
26
|
+
task_id TEXT NOT NULL, role_name TEXT NOT NULL, turn_id TEXT NOT NULL,
|
|
27
|
+
first_at TEXT NOT NULL, last_at TEXT NOT NULL, count INTEGER NOT NULL,
|
|
28
|
+
max_sequence INTEGER, error_count INTEGER NOT NULL DEFAULT 0, updated_at TEXT NOT NULL,
|
|
29
|
+
PRIMARY KEY (task_id, role_name, turn_id)
|
|
30
|
+
) WITHOUT ROWID;
|
|
31
|
+
INSERT INTO telemetry_aggregate
|
|
32
|
+
SELECT task_id, role_name, turn_id, MIN(first_at), MAX(last_at), SUM(count),
|
|
33
|
+
MAX(max_sequence), SUM(error_count), MAX(updated_at)
|
|
34
|
+
FROM telemetry_aggregate_v3 GROUP BY task_id, role_name, turn_id;
|
|
35
|
+
DROP TABLE telemetry_v3;
|
|
36
|
+
DROP TABLE telemetry_aggregate_v3;
|
|
37
|
+
CREATE TRIGGER telemetry_ai AFTER INSERT ON telemetry
|
|
38
|
+
BEGIN
|
|
39
|
+
INSERT INTO telemetry_aggregate
|
|
40
|
+
(task_id, role_name, turn_id, first_at, last_at, count, max_sequence, error_count, updated_at)
|
|
41
|
+
VALUES (NEW.task_id, NEW.role_name, NEW.turn_id, NEW.received_at, NEW.received_at,
|
|
42
|
+
1, NEW.sequence,
|
|
43
|
+
CASE WHEN json_valid(NEW.payload)
|
|
44
|
+
AND (COALESCE(json_extract(NEW.payload, '$.error'), '') <> ''
|
|
45
|
+
OR COALESCE(json_extract(NEW.payload, '$.errorKind'), '') <> '')
|
|
46
|
+
THEN 1 ELSE 0 END, NEW.received_at)
|
|
47
|
+
ON CONFLICT(task_id, role_name, turn_id) DO UPDATE SET
|
|
48
|
+
first_at = MIN(telemetry_aggregate.first_at, excluded.first_at),
|
|
49
|
+
last_at = MAX(telemetry_aggregate.last_at, excluded.last_at),
|
|
50
|
+
count = telemetry_aggregate.count + 1,
|
|
51
|
+
max_sequence = CASE WHEN excluded.max_sequence IS NOT NULL
|
|
52
|
+
AND (telemetry_aggregate.max_sequence IS NULL OR excluded.max_sequence > telemetry_aggregate.max_sequence)
|
|
53
|
+
THEN excluded.max_sequence ELSE telemetry_aggregate.max_sequence END,
|
|
54
|
+
error_count = telemetry_aggregate.error_count + excluded.error_count,
|
|
55
|
+
updated_at = excluded.updated_at;
|
|
56
|
+
END;
|
|
57
|
+
CREATE TRIGGER telemetry_au AFTER UPDATE ON telemetry
|
|
58
|
+
BEGIN
|
|
59
|
+
UPDATE telemetry_aggregate SET
|
|
60
|
+
last_at = MAX(last_at, NEW.received_at),
|
|
61
|
+
max_sequence = CASE WHEN NEW.sequence IS NOT NULL
|
|
62
|
+
AND (max_sequence IS NULL OR NEW.sequence > max_sequence) THEN NEW.sequence ELSE max_sequence END,
|
|
63
|
+
updated_at = NEW.received_at
|
|
64
|
+
WHERE task_id = NEW.task_id AND role_name = NEW.role_name AND turn_id = NEW.turn_id;
|
|
65
|
+
END;
|
|
66
|
+
|
|
67
|
+
DROP INDEX idx_session_owners_task;
|
|
68
|
+
ALTER TABLE session_owners RENAME TO session_owners_v3;
|
|
69
|
+
CREATE TABLE session_owners (
|
|
70
|
+
process_key TEXT PRIMARY KEY, scope TEXT NOT NULL CHECK(scope IN ('task','global')),
|
|
71
|
+
task_id TEXT, role_name TEXT NOT NULL, agent_id TEXT NOT NULL, native_session_id TEXT,
|
|
72
|
+
provider_root_pid INTEGER, payload TEXT NOT NULL, recorded_at TEXT NOT NULL
|
|
73
|
+
);
|
|
74
|
+
INSERT INTO session_owners
|
|
75
|
+
SELECT process_key, scope, task_id, role_name, agent_id, native_session_id,
|
|
76
|
+
provider_root_pid, payload, recorded_at
|
|
77
|
+
FROM (
|
|
78
|
+
SELECT *,
|
|
79
|
+
CAST(provider_root_pid AS TEXT) || '-' || json_extract(payload, '$.providerRoot.startIdentity') AS process_key,
|
|
80
|
+
ROW_NUMBER() OVER (
|
|
81
|
+
PARTITION BY provider_root_pid, json_extract(payload, '$.providerRoot.startIdentity')
|
|
82
|
+
ORDER BY recorded_at DESC, launch_id DESC
|
|
83
|
+
) AS rank
|
|
84
|
+
FROM session_owners_v3
|
|
85
|
+
) WHERE rank = 1;
|
|
86
|
+
DROP TABLE session_owners_v3;
|
|
87
|
+
CREATE INDEX idx_session_owners_task ON session_owners(task_id, role_name);
|
|
88
|
+
|
|
89
|
+
DROP INDEX idx_runtime_session_cleanup_required;
|
|
90
|
+
ALTER TABLE runtime_session_candidates RENAME TO runtime_session_candidates_v3;
|
|
91
|
+
CREATE TABLE runtime_session_candidates (
|
|
92
|
+
scope TEXT NOT NULL CHECK(scope IN ('task','global')), task_id TEXT NOT NULL,
|
|
93
|
+
role_name TEXT NOT NULL, agent_id TEXT NOT NULL, adapter_id TEXT NOT NULL,
|
|
94
|
+
native_session_id TEXT NOT NULL, session_updated_at TEXT NOT NULL,
|
|
95
|
+
PRIMARY KEY(scope, task_id, role_name),
|
|
96
|
+
CHECK((scope = 'task' AND length(task_id) > 0) OR (scope = 'global' AND task_id = ''))
|
|
97
|
+
);
|
|
98
|
+
INSERT INTO runtime_session_candidates
|
|
99
|
+
SELECT scope, task_id, role_name, agent_id, adapter_id, native_session_id, session_updated_at
|
|
100
|
+
FROM runtime_session_candidates_v3;
|
|
101
|
+
DROP TABLE runtime_session_candidates_v3;
|
|
102
|
+
|
|
103
|
+
-- Retire only obsolete launch bookkeeping; preserve pending intent and cleanup requests.
|
|
104
|
+
UPDATE mailboxes SET processing = NULL
|
|
105
|
+
WHERE target_kind IN ('role-runtime','global-role-runtime')
|
|
106
|
+
AND json_extract(processing, '$.owner') = 'runtime-lifecycle'
|
|
107
|
+
AND json_extract(processing, '$.batch.reasons') = '["runtime-launch-reserved"]';
|
|
108
|
+
`;
|
|
109
|
+
export function removeRuntimeGenerationRecords(db) {
|
|
110
|
+
const object = (value) => (value !== null && typeof value === "object" && !Array.isArray(value));
|
|
111
|
+
const obsolete = new Set([
|
|
112
|
+
"runtimeGenerationId", "hostActivationId", "activationId", "continuationGeneration"
|
|
113
|
+
]);
|
|
114
|
+
// Accepted Jobs retain exactly the Session authority they already had.
|
|
115
|
+
// Never refresh a revoked/stale binding to the currently active Session.
|
|
116
|
+
for (const row of db.prepare("SELECT task_id, job_id, payload FROM durable_jobs").all()) {
|
|
117
|
+
const job = JSON.parse(row.payload);
|
|
118
|
+
if (!object(job.operation) || typeof job.operation.actorId !== "string")
|
|
119
|
+
continue;
|
|
120
|
+
const prefix = `task:${row.task_id}/role:`;
|
|
121
|
+
if (!job.operation.actorId.startsWith(prefix))
|
|
122
|
+
continue;
|
|
123
|
+
const roleName = job.operation.actorId.slice(prefix.length);
|
|
124
|
+
const roleRow = db.prepare("SELECT payload FROM task_roles WHERE task_id = ? AND role_name = ?")
|
|
125
|
+
.get(row.task_id, roleName);
|
|
126
|
+
const sessionsRow = db.prepare("SELECT payload FROM role_session_sets WHERE task_id = ? AND role_name = ?")
|
|
127
|
+
.get(row.task_id, roleName);
|
|
128
|
+
if (roleRow === undefined || sessionsRow === undefined)
|
|
129
|
+
continue;
|
|
130
|
+
const role = JSON.parse(roleRow.payload);
|
|
131
|
+
const sessions = JSON.parse(sessionsRow.payload);
|
|
132
|
+
const session = sessions.sessions?.[sessions.activeAgentId];
|
|
133
|
+
if (session?.status !== "active" || role.activeAgentId !== sessions.activeAgentId)
|
|
134
|
+
continue;
|
|
135
|
+
const key = db.prepare("SELECT hash FROM job_caller_key_hashes WHERE task_id = ? AND role_name = ? AND agent_id = ?")
|
|
136
|
+
.get(row.task_id, roleName, session.agentId);
|
|
137
|
+
if (key === undefined)
|
|
138
|
+
continue;
|
|
139
|
+
const identity = [session.agentId, session.adapterId, session.nativeSessionId];
|
|
140
|
+
const prior = createHash("sha256").update(JSON.stringify([key.hash, ...identity])).digest("hex");
|
|
141
|
+
if (job.operation.authorityRef !== prior)
|
|
142
|
+
continue;
|
|
143
|
+
job.operation.authorityRef = createHash("sha256").update(JSON.stringify(identity)).digest("hex");
|
|
144
|
+
db.prepare("UPDATE durable_jobs SET payload = ? WHERE task_id = ? AND job_id = ?")
|
|
145
|
+
.run(JSON.stringify(job), row.task_id, row.job_id);
|
|
146
|
+
}
|
|
147
|
+
db.exec("DROP TABLE job_caller_key_hashes");
|
|
148
|
+
const clean = (value) => {
|
|
149
|
+
if (Array.isArray(value))
|
|
150
|
+
return value.map(clean);
|
|
151
|
+
if (!object(value))
|
|
152
|
+
return value;
|
|
153
|
+
const next = {};
|
|
154
|
+
for (const [key, child] of Object.entries(value)) {
|
|
155
|
+
if (obsolete.has(key))
|
|
156
|
+
continue;
|
|
157
|
+
if (key === "activations" && typeof value.providerNamespace === "string"
|
|
158
|
+
&& Array.isArray(value.conversations))
|
|
159
|
+
continue;
|
|
160
|
+
if (key === "generation" && typeof value.continuationId === "string")
|
|
161
|
+
continue;
|
|
162
|
+
if (key === "generation" && value.kind === "yui-task-runtime-isolation")
|
|
163
|
+
continue;
|
|
164
|
+
next[key] = clean(child);
|
|
165
|
+
}
|
|
166
|
+
if (typeof next.providerNamespace === "string" && Array.isArray(next.conversations)
|
|
167
|
+
&& object(next.authority) && next.authority.owner === "controller") {
|
|
168
|
+
next.authority.holderId = "controller";
|
|
169
|
+
}
|
|
170
|
+
if (next.authorityOwner === "controller" && typeof next.holderId === "string") {
|
|
171
|
+
next.holderId = "controller";
|
|
172
|
+
}
|
|
173
|
+
if (next.kind === "yui-task-runtime-isolation" && object(next.roots)) {
|
|
174
|
+
next.roots.runtime = next.roots.generation;
|
|
175
|
+
delete next.roots.generation;
|
|
176
|
+
}
|
|
177
|
+
return next;
|
|
178
|
+
};
|
|
179
|
+
// These are system-owned record families, not arbitrary user documents or Job outputs.
|
|
180
|
+
for (const table of ["role_session_sets", "global_role_session_sets", "turns", "session_owners", "events"]) {
|
|
181
|
+
const columns = db.prepare(`PRAGMA table_info("${table}")`).all();
|
|
182
|
+
const keys = columns.filter((column) => column.pk > 0).sort((a, b) => a.pk - b.pk).map((c) => c.name);
|
|
183
|
+
const rows = db.prepare(`SELECT * FROM "${table}"`).all();
|
|
184
|
+
const where = keys.map((key) => `"${key}" = ?`).join(" AND ");
|
|
185
|
+
const update = db.prepare(`UPDATE "${table}" SET payload = ?${table === "events" ? ", type = ?" : ""} WHERE ${where}`);
|
|
186
|
+
for (const row of rows) {
|
|
187
|
+
const raw = JSON.parse(row.payload);
|
|
188
|
+
const migrated = clean(raw);
|
|
189
|
+
if (table === "events" && object(migrated.payload)
|
|
190
|
+
&& typeof migrated.payload.observation === "string"
|
|
191
|
+
&& typeof migrated.type === "string" && migrated.type.startsWith("runtime.")) {
|
|
192
|
+
const observation = JSON.parse(migrated.payload.observation);
|
|
193
|
+
const current = clean(observation);
|
|
194
|
+
if (typeof current.kind === "string" && current.kind.startsWith("activation.")) {
|
|
195
|
+
// Keep the historical fact, but it is no longer a current lifecycle event.
|
|
196
|
+
migrated.type = "runtime.historical-attachment-observed";
|
|
197
|
+
}
|
|
198
|
+
if (typeof current.stopReceiptId === "string") {
|
|
199
|
+
current.stopRequested = true;
|
|
200
|
+
delete current.stopReceiptId;
|
|
201
|
+
}
|
|
202
|
+
migrated.payload.observation = JSON.stringify(current);
|
|
203
|
+
}
|
|
204
|
+
update.run(JSON.stringify(migrated), ...(table === "events" ? [migrated.type] : []), ...keys.map((key) => row[key]));
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
}
|