@zq-silk/yui 0.15.12 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +3 -2
- package/ARCHITECTURE.zh-CN.md +3 -2
- package/dist/artifacts/artifactCommitLock.js +16 -55
- package/dist/artifacts/taskArtifactRepository.js +3 -3
- package/dist/cli/agentConfigurationPicker.js +1 -1
- package/dist/cli/commandCatalog.js +57 -41
- package/dist/cli/completion.js +18 -18
- package/dist/cli/completionWizard.js +1 -1
- package/dist/cli/dynamicCompletion.js +1 -1
- package/dist/cli/interactionPolicy.js +7 -3
- package/dist/cli/invocationAuthority.js +64 -0
- package/dist/cli/managedDiagnostics.js +1 -1
- package/dist/cli/parseRepeatable.js +35 -0
- package/dist/cli/updatePorts.js +26 -5
- package/dist/cli.js +1280 -1243
- package/dist/commands/agentCommands.js +4 -4
- package/dist/commands/capabilityCommands.js +1 -1
- package/dist/commands/configCommands.js +6 -57
- package/dist/commands/configOverview.js +2 -2
- package/dist/commands/durableJobCommands.js +12 -6
- package/dist/commands/globalRoleCommands.js +53 -29
- package/dist/commands/grantCommands.js +0 -3
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/operatorCommands.js +3 -3
- package/dist/commands/projectCommands.js +1 -10
- package/dist/commands/resourcesCommands.js +10 -40
- package/dist/commands/roleConfiguration.js +2 -6
- package/dist/commands/taskActivationCommands.js +4 -8
- package/dist/commands/taskAgentCapabilities.js +9 -0
- package/dist/commands/taskCommandSupport.js +155 -0
- package/dist/commands/taskCommandTypes.js +1 -0
- package/dist/commands/taskCommands.js +271 -710
- package/dist/commands/taskCompletionGate.js +1 -1
- package/dist/commands/taskExecutionCommands.js +1 -1
- package/dist/commands/taskFactCommands.js +325 -0
- package/dist/commands/taskInputCommands.js +12 -42
- package/dist/commands/taskIntegrationCommands.js +17 -57
- package/dist/commands/taskNextActionCommand.js +7 -6
- package/dist/commands/taskPublicationAdoptCommand.js +3 -3
- package/dist/commands/taskPublicationCommands.js +1 -1
- package/dist/commands/taskPublicationVerifyCommand.js +1 -1
- package/dist/commands/taskRemoteDeliveryCommand.js +2 -94
- package/dist/commands/taskUpstreamCommands.js +19 -8
- package/dist/commands/workflowCommands.js +6 -1
- package/dist/completion/completionInstaller.js +26 -26
- package/dist/completion/completionState.js +26 -26
- package/dist/completion/fileCompletionManager.js +6 -11
- package/dist/config/configCatalog.js +0 -2
- package/dist/config/timeZone.js +14 -0
- package/dist/config/yuiConfig.js +0 -29
- package/dist/context/runContextPack.js +2 -3
- package/dist/context/taskCatalog.js +3 -5
- package/dist/context/taskContext.js +139 -151
- package/dist/context/wakeRunReferences.js +11 -0
- package/dist/controller/agentCapabilities.js +58 -0
- package/dist/controller/agentRuntimeObserver.js +6 -11
- package/dist/controller/clientRuntime.js +22 -14
- package/dist/controller/controller.js +9 -22
- package/dist/controller/fileSchedulerStoreAdapter.js +117 -212
- package/dist/controller/jobClient.js +4 -4
- package/dist/controller/jobControl.js +60 -25
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/providerRetryDelivery.js +7 -7
- package/dist/controller/runtime.js +51 -44
- package/dist/controller/runtimeEventProcessor.js +0 -5
- package/dist/controller/sessionOwnerReconciliation.js +5 -0
- package/dist/controller/taskAgentError.js +35 -0
- package/dist/coordination/workMailboxQueue.js +2 -24
- package/dist/core/controllerClient.js +2 -2
- package/dist/core/fileLockOwner.js +74 -0
- package/dist/decision/decision.js +17 -0
- package/dist/doctor/doctor.js +18 -9
- package/dist/event/taskEvent.js +12 -0
- package/dist/execution/workItemExecutionProjection.js +2 -2
- package/dist/executor/agentCapabilityConfig.js +43 -0
- package/dist/executor/agentConfigurationCatalog.js +49 -11
- package/dist/executor/agentExecutor.js +5 -8
- package/dist/executor/fileRoleLaunchPlanner.js +11 -53
- package/dist/executor/taskAgentCapabilities.js +64 -0
- package/dist/integration/deliveryObligation.js +1 -69
- package/dist/integration/gitIntegrationService.js +60 -84
- package/dist/integration/integrationAttempt.js +3 -14
- package/dist/integration/integrationSourceApplication.js +4 -12
- package/dist/integration/manifestTags.js +2 -2
- package/dist/job/durableJob.js +4 -25
- package/dist/job/jobAssignmentScope.js +22 -0
- package/dist/job/jobOperation.js +16 -0
- package/dist/job/jobRunner.js +2 -1
- package/dist/job/stepDirectory.js +15 -0
- package/dist/kernel/builtinCapabilities.js +15 -11
- package/dist/kernel/kernelPorts.js +1 -17
- package/dist/lifecycle/exactRunTerminalization.js +1 -9
- package/dist/message/inputControlResolution.js +3 -4
- package/dist/message/message.js +17 -20
- package/dist/message/messageContinuation.js +1 -1
- package/dist/milestone/milestone.js +11 -0
- package/dist/observability/orchestrationMetrics.js +24 -1
- package/dist/observability/runtimeIdentity.js +1 -17
- package/dist/output/agentConfigurationPresentation.js +2 -0
- package/dist/output/timePresentation.js +1 -14
- package/dist/plugins/pluginService.js +12 -4
- package/dist/release/releaseWorkflowPorts.js +45 -96
- package/dist/release/runtimeRelease.js +9 -15
- package/dist/repository/projectMaintenanceLock.js +23 -64
- package/dist/repository/taskBaseFreshness.js +1 -1
- package/dist/repository/taskWorkspaceCoordinator.js +13 -22
- package/dist/repository/taskWorkspacePreparer.js +29 -25
- package/dist/repository/workspaceCleanupInspection.js +5 -5
- package/dist/resources/autoResourceGc.js +4 -77
- package/dist/resources/liveReferences.js +7 -54
- package/dist/resources/resourceDiscovery.js +5 -118
- package/dist/resources/resourceGc.js +233 -181
- package/dist/resources/resourceRegistrar.js +5 -4
- package/dist/resources/resourceRegistry.js +3 -37
- package/dist/resources/resourceRegistryStore.js +13 -36
- package/dist/resources/sqliteResourceRegistry.js +43 -26
- package/dist/review/deltaRecheck.js +1 -1
- package/dist/review/reviewAcceptance.js +1 -1
- package/dist/review/reviewDecision.js +1 -1
- package/dist/review/reviewRound.js +8 -7
- package/dist/review/taskFinalReviewContractResolution.js +1 -1
- package/dist/runtime/acpProtocol.js +4 -51
- package/dist/runtime/acpSession.js +10 -75
- package/dist/runtime/acpSessionConfiguration.js +1 -8
- package/dist/runtime/agentEndpoint.js +3 -2
- package/dist/runtime/agentFailureContext.js +43 -0
- package/dist/runtime/agentHost.js +15 -15
- package/dist/runtime/codexInteractiveHost.js +2 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/managedCaller.js +21 -1
- package/dist/runtime/providerErrors.js +38 -0
- package/dist/runtime/runtimeObservation.js +0 -5
- package/dist/runtime/runtimeSessionCandidate.js +2 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/structuredProviderHost.js +4 -41
- package/dist/runtime/tmuxAdapters.js +6 -8
- package/dist/scheduler/activeRoleRunDelivery.js +7 -7
- package/dist/scheduler/leaderWakeupProcessor.js +19 -6
- package/dist/scheduler/operatorEvent.js +12 -20
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +5 -8
- package/dist/scheduler/roleRunLiveness.js +4 -4
- package/dist/scheduler/roleRunStall.js +16 -23
- package/dist/scheduler/taskExecutionProjection.js +25 -33
- package/dist/scheduler/taskObservabilityProjection.js +0 -1
- package/dist/scheduler/taskWake.js +5 -10
- package/dist/scheduler/wakeReason.js +2 -0
- package/dist/scheduler/wakeupQueue.js +2 -4
- package/dist/setup/setupCommand.js +1 -1
- package/dist/storage/contextRecords.js +106 -0
- package/dist/storage/currentTaskStore.js +1 -1
- package/dist/storage/homeLayout.js +13 -17
- package/dist/storage/migrations/agentFailureContext.js +22 -0
- package/dist/storage/migrations/currentInputContract.js +86 -0
- package/dist/storage/migrations/currentRuntimeContract.js +228 -0
- package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
- package/dist/storage/migrations/integrationContinuation.js +5 -4
- package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
- package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
- package/dist/storage/migrations/verificationPlanV1.js +162 -0
- package/dist/storage/migrations/verificationPolicy.js +74 -0
- package/dist/storage/migrations/workItemHistory.js +46 -0
- package/dist/storage/persistenceWorker.js +12 -4
- package/dist/storage/recordValidation.js +87 -0
- package/dist/storage/sqliteSchema.js +142 -1
- package/dist/storage/sqliteStore.js +156 -162
- package/dist/storage/storageSchema.js +2 -14
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +15 -7
- package/dist/storage/taskStore.js +3 -7
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
- package/dist/task/completionReadiness.js +5 -24
- package/dist/task/draftPlan.js +0 -53
- package/dist/task/nextAction.js +7 -13
- package/dist/task/remoteDeliveryService.js +89 -0
- package/dist/task/taskActivation.js +2 -25
- package/dist/task/taskActivationService.js +0 -2
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/task/taskSubmission.js +3 -14
- package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
- package/dist/telemetry/sqliteTelemetryStore.js +49 -49
- package/dist/telemetry/telemetryWiring.js +4 -3
- package/dist/tmux/tmuxManager.js +29 -20
- package/dist/verification/gateArtifact.js +15 -24
- package/dist/verification/gateArtifactStore.js +14 -7
- package/dist/verification/verificationGateService.js +29 -88
- package/dist/verification/verificationPlan.js +27 -72
- package/dist/web/assets/client/app.js +1 -1
- package/dist/web/assets/client/taskSurface.js +4 -4
- package/dist/web/webServer.js +4 -6
- package/dist/web/webSnapshot.js +3 -83
- package/dist/web/webTaskSurface.js +17 -46
- package/dist/workItem/workItem.js +1 -12
- package/docs/architecture/capabilities-and-resources.md +14 -2
- package/docs/architecture/capabilities-and-resources.zh-CN.md +11 -1
- package/docs/managed-turn-and-session-runtime.md +83 -10
- package/docs/managed-turn-and-session-runtime.zh-CN.md +65 -11
- package/docs/plugin-sdk.md +4 -2
- package/docs/provider-runtime.md +3 -1
- package/docs/provider-runtime.zh-CN.md +4 -2
- package/docs/release-workflow.md +190 -7
- package/docs/release-workflow.zh-CN.md +139 -5
- package/docs/roles-and-configuration.md +29 -0
- package/docs/roles-and-configuration.zh-CN.md +21 -0
- package/docs/sqlite-control-plane-design.md +16 -3
- package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
- package/docs/task-dag-semantics.md +3 -3
- package/docs/task-dag-semantics.zh-CN.md +2 -2
- package/docs/task-delivery.md +91 -5
- package/docs/task-delivery.zh-CN.md +72 -4
- package/docs/task-discovery.md +10 -11
- package/docs/task-discovery.zh-CN.md +8 -9
- package/docs/testing/verification-levels.md +145 -9
- package/docs/testing/verification-levels.zh-CN.md +103 -8
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -0
- package/skills/yui-leader/references/integration.md +9 -5
- package/skills/yui-leader/references/planning.md +9 -3
- package/skills/yui-operator/SKILL.md +15 -10
- package/skills/yui-runtime/SKILL.md +6 -6
- package/skills/yui-runtime/references/recovery.md +47 -0
- package/dist/agent/agentRegistry.js +0 -10
- package/dist/agentRun/runIdentity.js +0 -22
- package/dist/commands/deliveryGuardPreflight.js +0 -30
- package/dist/commands/taskIntegrationQueueCommands.js +0 -226
- package/dist/commands/taskOverviewCommand.js +0 -377
- package/dist/completion/completionWizard.js +0 -125
- package/dist/context/dispatchContext.js +0 -110
- package/dist/context/wakeNotification.js +0 -144
- package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
- package/dist/integration/integrationQueueEntry.js +0 -191
- package/dist/integration/integrationQueueService.js +0 -810
- package/dist/release/fakeReleasePorts.js +0 -55
- package/dist/runtime/sessionOwnerRegistry.js +0 -137
- package/dist/task/deliveryGuard.js +0 -226
- /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
|
@@ -178,9 +178,8 @@ export function currentRoleRunProgressAt(store, taskId, roleName, run) {
|
|
|
178
178
|
return { progressAt: run.createdAt };
|
|
179
179
|
}
|
|
180
180
|
/**
|
|
181
|
-
* Computes the current semantic progress fence for an exact AgentRun
|
|
182
|
-
*
|
|
183
|
-
* event facts used by on-demand projections.
|
|
181
|
+
* Computes the current semantic progress fence for an exact AgentRun from
|
|
182
|
+
* required Work/Review/Integration readers and the AgentRun's event facts.
|
|
184
183
|
*/
|
|
185
184
|
export function latestRunDurableProgressAt(store, taskId, roleName, runId, precomputed) {
|
|
186
185
|
const run = store.getRun(taskId, runId);
|
|
@@ -204,20 +203,16 @@ export function latestRunDurableProgressAt(store, taskId, roleName, runId, preco
|
|
|
204
203
|
if (run.workItemId === undefined) {
|
|
205
204
|
return { progressAt: baseline };
|
|
206
205
|
}
|
|
207
|
-
const workItem = store.getWorkItem
|
|
208
|
-
const reviewRounds = store.listReviewRounds
|
|
209
|
-
.filter(({ workItemId }) => workItemId === run.workItemId)
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
const
|
|
215
|
-
.filter(({ source }) => (source.kind === "work-item" && source.workItemId === run.workItemId))
|
|
216
|
-
?? [];
|
|
217
|
-
const inputProgress = store.listInputRequests?.(taskId)
|
|
206
|
+
const workItem = store.getWorkItem(taskId, run.workItemId);
|
|
207
|
+
const reviewRounds = store.listReviewRounds(taskId)
|
|
208
|
+
.filter(({ workItemId }) => workItemId === run.workItemId);
|
|
209
|
+
const changeSets = store.listChangeSets(taskId)
|
|
210
|
+
.filter(({ workItemId }) => workItemId === run.workItemId);
|
|
211
|
+
const integrations = store.listIntegrationAttempts(taskId)
|
|
212
|
+
.filter(({ source }) => (source.kind === "work-item" && source.workItemId === run.workItemId));
|
|
213
|
+
const inputProgress = store.listInputRequests(taskId)
|
|
218
214
|
.filter((request) => (request.requester.runId === run.id
|
|
219
|
-
|| request.blockedRefs.some((ref) => ref.type === "run" && ref.id === run.id)))
|
|
220
|
-
?? [];
|
|
215
|
+
|| request.blockedRefs.some((ref) => ref.type === "run" && ref.id === run.id)));
|
|
221
216
|
const related = [
|
|
222
217
|
workItem?.updatedAt,
|
|
223
218
|
...reviewRounds.map(({ endedAt, createdAt }) => endedAt ?? createdAt),
|
|
@@ -433,8 +428,6 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
|
|
|
433
428
|
// existing mailbox work without manufacturing another episode.
|
|
434
429
|
if (selection !== undefined && !selection.full)
|
|
435
430
|
return [];
|
|
436
|
-
if (store.recordRoleRunStall === undefined)
|
|
437
|
-
return [];
|
|
438
431
|
// A Draft planning Turn can stall exactly like any other admitted Turn, so it
|
|
439
432
|
// is selected here too; the episode stays diagnostic-only either way.
|
|
440
433
|
const candidates = selectedActiveSchedulerTasks(store, selection, {
|
|
@@ -455,7 +448,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
|
|
|
455
448
|
const existing = eventsByTask.get(taskId);
|
|
456
449
|
if (existing !== undefined)
|
|
457
450
|
return existing;
|
|
458
|
-
const events = store.listEvents
|
|
451
|
+
const events = store.listEvents(taskId);
|
|
459
452
|
eventsByTask.set(taskId, events);
|
|
460
453
|
return events;
|
|
461
454
|
};
|
|
@@ -469,7 +462,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
|
|
|
469
462
|
const diagnosticStartedAt = now.toISOString();
|
|
470
463
|
const finishDiagnostics = (outcome) => {
|
|
471
464
|
for (const { task, role, run } of stallCandidates) {
|
|
472
|
-
store.recordRoleRunDiagnostic
|
|
465
|
+
store.recordRoleRunDiagnostic({
|
|
473
466
|
taskId: task.id,
|
|
474
467
|
roleName: role.name,
|
|
475
468
|
runId: run.id,
|
|
@@ -676,7 +669,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
|
|
|
676
669
|
// A new semantic progress point closes the previous episode first. It
|
|
677
670
|
// may itself already be older than the window, in which case the same
|
|
678
671
|
// pass records the next AgentRun+progressAt episode below.
|
|
679
|
-
store.recordRoleRunProgress
|
|
672
|
+
store.recordRoleRunProgress({
|
|
680
673
|
taskId: candidate.task.id,
|
|
681
674
|
roleName: candidate.role.name,
|
|
682
675
|
runId: candidate.run.id,
|
|
@@ -745,7 +738,7 @@ export async function reconcileStalledRoleRuns(store, delivery, now, selection,
|
|
|
745
738
|
const previous = store.getRunProgressFacts(candidate.task.id, candidate.run.id)?.latestStall;
|
|
746
739
|
if (previous !== undefined
|
|
747
740
|
&& Date.parse(progressAt) > Date.parse(previous.progressAt)) {
|
|
748
|
-
store.recordRoleRunProgress
|
|
741
|
+
store.recordRoleRunProgress({
|
|
749
742
|
taskId: candidate.task.id,
|
|
750
743
|
roleName: candidate.role.name,
|
|
751
744
|
runId: candidate.run.id,
|
|
@@ -827,7 +820,7 @@ function latestLeaderActionProgressAt(store, taskId, runId, startedAt, batch, no
|
|
|
827
820
|
return "";
|
|
828
821
|
let latest = startedAt;
|
|
829
822
|
let latestMs = startedMs;
|
|
830
|
-
for (const event of store.listEvents
|
|
823
|
+
for (const event of store.listEvents(taskId)) {
|
|
831
824
|
if (!LEADER_ACTION_PROGRESS_TYPES.has(event.type))
|
|
832
825
|
continue;
|
|
833
826
|
if (event.payload.taskId !== undefined && event.payload.taskId !== taskId)
|
|
@@ -14,22 +14,24 @@ import { projectSessionTokenMetrics, resolveSessionTokenIdentity } from "../runt
|
|
|
14
14
|
* aggregate. This function only reads; it never starts a Controller, queues a
|
|
15
15
|
* wake, writes a Message, or mutates any record.
|
|
16
16
|
*/
|
|
17
|
-
export function buildTaskExecutionProjection(store, taskId,
|
|
18
|
-
const task = store.getTask
|
|
17
|
+
export function buildTaskExecutionProjection(store, taskId, now = new Date()) {
|
|
18
|
+
const task = store.getTask(taskId);
|
|
19
19
|
if (task === null)
|
|
20
20
|
return null;
|
|
21
|
-
const roles = store.listRoles
|
|
22
|
-
const events = store.listEvents
|
|
23
|
-
const usageRuns = store.listRuns
|
|
21
|
+
const roles = store.listRoles(taskId);
|
|
22
|
+
const events = store.listEvents(taskId);
|
|
23
|
+
const usageRuns = store.listRuns(taskId);
|
|
24
|
+
const workItems = store.listWorkItems(taskId);
|
|
25
|
+
const reviewRounds = store.listReviewRounds(taskId);
|
|
24
26
|
const runs = operationalTaskRecords(usageRuns, events, "run");
|
|
25
|
-
const leaderMailbox = store.getWorkMailbox
|
|
27
|
+
const leaderMailbox = store.getWorkMailbox({
|
|
26
28
|
kind: "role",
|
|
27
29
|
taskId,
|
|
28
30
|
roleName: "leader"
|
|
29
|
-
})
|
|
31
|
+
});
|
|
30
32
|
const roleSessions = roles.flatMap((role) => {
|
|
31
|
-
const session = store.getRoleSession
|
|
32
|
-
return session === null
|
|
33
|
+
const session = store.getRoleSession(taskId, role.name);
|
|
34
|
+
return session === null
|
|
33
35
|
? []
|
|
34
36
|
: [{ roleName: role.name, ...session }];
|
|
35
37
|
});
|
|
@@ -39,38 +41,28 @@ export function buildTaskExecutionProjection(store, taskId, taskOverride, now =
|
|
|
39
41
|
runs,
|
|
40
42
|
usageRuns,
|
|
41
43
|
providerRetries: roles.flatMap(role => {
|
|
42
|
-
const retry = providerRetryProjection(store.getTaskRoleSessionSet
|
|
44
|
+
const retry = providerRetryProjection(store.getTaskRoleSessionSet(taskId, role.name)?.providerBinding);
|
|
43
45
|
return retry === null ? [] : [retry];
|
|
44
46
|
}),
|
|
45
47
|
runDelivery: Object.fromEntries(runs.map((run) => {
|
|
46
|
-
const observed = store.getTaskRoleSessionSet
|
|
48
|
+
const observed = store.getTaskRoleSessionSet(taskId, run.roleName)?.providerBinding?.run;
|
|
47
49
|
return [run.id, observed?.runId === run.id ? observed.status : "unobserved"];
|
|
48
50
|
})),
|
|
49
|
-
executionGroups:
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
: { reviewRounds: store.listReviewRounds(taskId) }),
|
|
60
|
-
...(store.listChangeSets === undefined
|
|
61
|
-
? {}
|
|
62
|
-
: { changeSets: store.listChangeSets(taskId) }),
|
|
63
|
-
...(store.listIntegrationAttempts === undefined
|
|
64
|
-
? {}
|
|
65
|
-
: { integrations: store.listIntegrationAttempts(taskId) }),
|
|
66
|
-
...(store.listEvents === undefined ? {} : { events }),
|
|
67
|
-
...(store.getTaskBrief === undefined ? {} : { brief: store.getTaskBrief(taskId) }),
|
|
68
|
-
pendingWakeup: store.getPendingWakeup?.(taskId) ?? null,
|
|
51
|
+
executionGroups: collectExecutionGroups(workItems, reviewRounds, runs),
|
|
52
|
+
workItems,
|
|
53
|
+
contextSnapshots: store.listContextSnapshots(taskId),
|
|
54
|
+
inputRequests: store.listInputRequests(taskId),
|
|
55
|
+
reviewRounds,
|
|
56
|
+
changeSets: store.listChangeSets(taskId),
|
|
57
|
+
integrations: store.listIntegrationAttempts(taskId),
|
|
58
|
+
events,
|
|
59
|
+
brief: store.getTaskBrief(taskId),
|
|
60
|
+
pendingWakeup: store.getPendingWakeup(taskId),
|
|
69
61
|
leaderMailbox,
|
|
70
|
-
leaderFailure: store.getLeaderFailure
|
|
62
|
+
leaderFailure: store.getLeaderFailure(taskId),
|
|
71
63
|
roleSessions,
|
|
72
64
|
now,
|
|
73
|
-
runtimeHealthPolicy: resolveRuntimeHealth(store.getConfig
|
|
65
|
+
runtimeHealthPolicy: resolveRuntimeHealth(store.getConfig().runtimeHealth)
|
|
74
66
|
});
|
|
75
67
|
}
|
|
76
68
|
/**
|
|
@@ -60,7 +60,6 @@ function projectDag(workItems) {
|
|
|
60
60
|
const edges = [];
|
|
61
61
|
for (const item of workItems) {
|
|
62
62
|
for (const dependency of item.dependsOn) {
|
|
63
|
-
const target = byId.get(dependency);
|
|
64
63
|
const status = dependencyEdgeStatus(dependency, byId);
|
|
65
64
|
edges.push({ from: dependency, to: item.id, status });
|
|
66
65
|
dependents.get(dependency)?.push(item.id);
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import { requireIdentity, requirePositiveInteger, requireText, requireTimestamp } from "../domain/validation.js";
|
|
2
2
|
import { TASK_RECORD_ID_PREFIXES } from "../task/taskRecordReference.js";
|
|
3
3
|
import { copyRef } from "../coordination/workMailbox.js";
|
|
4
|
-
export const CURRENT_TASK_WAKE_SCHEMA_VERSION =
|
|
4
|
+
export const CURRENT_TASK_WAKE_SCHEMA_VERSION = 2;
|
|
5
5
|
export function createTaskWake(input) {
|
|
6
|
+
if (Object.hasOwn(input, "runId"))
|
|
7
|
+
throw new Error("A notification wake cannot dispatch an AgentRun.");
|
|
6
8
|
const wake = {
|
|
7
9
|
schemaVersion: CURRENT_TASK_WAKE_SCHEMA_VERSION,
|
|
8
10
|
id: requireIdentity(input.id, "Task wake id"),
|
|
@@ -13,13 +15,14 @@ export function createTaskWake(input) {
|
|
|
13
15
|
fromCursor: requireTimestamp(input.fromCursor, "Wake fromCursor"),
|
|
14
16
|
toCursor: requireTimestamp(input.toCursor, "Wake toCursor"),
|
|
15
17
|
status: "dispatched",
|
|
16
|
-
...(input.runId === undefined ? {} : { runId: requireIdentity(input.runId, "Wake AgentRun id") }),
|
|
17
18
|
createdAt: input.now.toISOString()
|
|
18
19
|
};
|
|
19
20
|
validateTaskWake(wake);
|
|
20
21
|
return wake;
|
|
21
22
|
}
|
|
22
23
|
export function validateTaskWake(wake) {
|
|
24
|
+
if (Object.hasOwn(wake, "runId"))
|
|
25
|
+
throw new Error("Run-backed wakes are not current notification records.");
|
|
23
26
|
if (wake.refs !== undefined) {
|
|
24
27
|
if (!Array.isArray(wake.refs))
|
|
25
28
|
throw new Error("Wake references must be an array.");
|
|
@@ -61,14 +64,6 @@ export function latestTaskWake(wakes) {
|
|
|
61
64
|
}
|
|
62
65
|
return latest;
|
|
63
66
|
}
|
|
64
|
-
/**
|
|
65
|
-
* The delta lower bound for a Task's first wake: the last Leader AgentRun's
|
|
66
|
-
* creation time, preserving the pre-ledger watermark semantics. When no
|
|
67
|
-
* Leader AgentRun exists, the Task's own creation time bounds the window.
|
|
68
|
-
*/
|
|
69
|
-
export function fallbackWakeCursor(input) {
|
|
70
|
-
return input.leaderRunCreatedAt ?? input.taskCreatedAt;
|
|
71
|
-
}
|
|
72
67
|
function wakeSequence(id) {
|
|
73
68
|
const prefix = `${TASK_RECORD_ID_PREFIXES.taskWake}-`;
|
|
74
69
|
if (!id.startsWith(prefix)) {
|
|
@@ -25,6 +25,7 @@ export const WAKE_REASON_KINDS = Object.freeze([
|
|
|
25
25
|
"role-turn-failed",
|
|
26
26
|
"job-finished",
|
|
27
27
|
"published-tree-authorized",
|
|
28
|
+
"notification-retry",
|
|
28
29
|
"force-wake"
|
|
29
30
|
]);
|
|
30
31
|
const IMMEDIATE_KINDS = new Set([
|
|
@@ -34,6 +35,7 @@ const IMMEDIATE_KINDS = new Set([
|
|
|
34
35
|
"execution-started",
|
|
35
36
|
"session-replaced",
|
|
36
37
|
"force-wake",
|
|
38
|
+
"notification-retry",
|
|
37
39
|
"activation-failed",
|
|
38
40
|
"task-reopened",
|
|
39
41
|
"leader-turn-failed",
|
|
@@ -1,10 +1,8 @@
|
|
|
1
1
|
import { mergePendingWakeup } from "./pendingWakeup.js";
|
|
2
|
+
/** The caller's TaskStore transaction owns this read/merge/write operation. */
|
|
2
3
|
export function queueLeaderWakeup(store, taskId, reason, now) {
|
|
3
|
-
if (store.getTask
|
|
4
|
+
if (store.getTask(taskId)?.status === "archived")
|
|
4
5
|
return null;
|
|
5
|
-
if (store.enqueueLeaderWakeup !== undefined) {
|
|
6
|
-
return store.enqueueLeaderWakeup(taskId, reason, now);
|
|
7
|
-
}
|
|
8
6
|
const pending = mergePendingWakeup(taskId, reason, now, store.getPendingWakeup(taskId));
|
|
9
7
|
store.savePendingWakeup(pending);
|
|
10
8
|
return pending;
|
|
@@ -5,10 +5,10 @@ import { configuredAgentToDefinition, createConfiguredAgent } from "../agent/age
|
|
|
5
5
|
import { assertRoleRuntimeMutationAllowed } from "../commands/roleRuntimeGuard.js";
|
|
6
6
|
import { resolveTmuxBin } from "../config/yuiConfig.js";
|
|
7
7
|
import { usageError } from "../errors/cliError.js";
|
|
8
|
-
import { managedGlobalRoleWorkspace } from "../storage/homeLayout.js";
|
|
9
8
|
import { createGlobalRole, createRoleAgentBinding, updateGlobalRole } from "../role/role.js";
|
|
10
9
|
import { SYSTEM_LEADER_ROLE, SYSTEM_OPERATOR_ROLE } from "../role/systemRoles.js";
|
|
11
10
|
import { initializeCurrentTaskStore } from "../storage/currentTaskStore.js";
|
|
11
|
+
import { managedGlobalRoleWorkspace } from "../storage/homeLayout.js";
|
|
12
12
|
import { ensureYuiHome, resolveYuiHome } from "../storage/taskStore.js";
|
|
13
13
|
const BUILTIN_AGENTS = Object.freeze([
|
|
14
14
|
Object.freeze({
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import { validateStoredRecord } from "./recordValidation.js";
|
|
2
|
+
import { validateWorkItemCandidate } from "../workItem/workItem.js";
|
|
3
|
+
/** Read-only projections of existing tables, not a new Context index or Store. */
|
|
4
|
+
const FAMILIES = {
|
|
5
|
+
"work-item": ["work_items", "work_item_id"],
|
|
6
|
+
"candidate": ["work_items", "work_item_id"],
|
|
7
|
+
"environment-preparation": ["environment_preparations", "id"],
|
|
8
|
+
"managed-workspace": ["managed_workspaces", "owner_id"],
|
|
9
|
+
"change-set": ["change_sets", "change_set_id"],
|
|
10
|
+
"task-message": ["messages", "message_id"],
|
|
11
|
+
"run": ["turns", "turn_id"],
|
|
12
|
+
"review-round": ["review_rounds", "review_round_id"],
|
|
13
|
+
"input-request": ["input_requests", "input_id"],
|
|
14
|
+
"task-decision": ["decisions", "decision_id"],
|
|
15
|
+
"task-milestone": ["milestones", "milestone_id"],
|
|
16
|
+
"job": ["durable_jobs", "job_id"],
|
|
17
|
+
"publication": ["publication_references", "publication_id"],
|
|
18
|
+
"task-event": ["events", "event_id"]
|
|
19
|
+
};
|
|
20
|
+
/** Only identities cross this query: shared user intent and the exact live
|
|
21
|
+
* Assignment's steers augment frozen read refs without loading other messages.
|
|
22
|
+
*/
|
|
23
|
+
export function contextInputReferences(db, taskId, scope) {
|
|
24
|
+
const shared = `json_extract(m.payload, '$.kind') IN ('user','operator')
|
|
25
|
+
AND json_extract(m.payload, '$.recipient') IS NULL
|
|
26
|
+
AND json_extract(m.payload, '$.workItemId') IS NULL
|
|
27
|
+
AND json_extract(m.payload, '$.runId') IS NULL`;
|
|
28
|
+
const steer = `(COALESCE(json_extract(m.payload, '$.inputControl.action'), '') = 'steer'
|
|
29
|
+
OR COALESCE(json_extract(m.payload, '$.interruptThen.reusedInput.action'), '') = 'steer')`;
|
|
30
|
+
const matches = `m.task_id = @taskId AND (
|
|
31
|
+
((${shared}) AND NOT ${steer}) OR (${steer}
|
|
32
|
+
AND json_extract(m.payload, '$.recipient.roleName') = @roleName
|
|
33
|
+
AND json_extract(m.payload, '$.recipient.workItemId') IS @workItemId
|
|
34
|
+
AND json_extract(m.payload, '$.recipient.reviewRoundId') IS @reviewRoundId))`;
|
|
35
|
+
const params = { taskId, roleName: scope.roleName,
|
|
36
|
+
workItemId: scope.workItemId ?? null, reviewRoundId: scope.reviewRoundId ?? null };
|
|
37
|
+
const ids = `SELECT m.message_id FROM messages m WHERE ${matches}`;
|
|
38
|
+
const messages = db.prepare(ids).all(params).map(row => row.message_id);
|
|
39
|
+
const events = db.prepare(`SELECT event_id FROM events
|
|
40
|
+
WHERE task_id = @taskId AND type GLOB 'message.*'
|
|
41
|
+
AND json_extract(payload, '$.payload.messageId') IN (${ids})`).all(params).map(row => row.event_id);
|
|
42
|
+
return { messages, events };
|
|
43
|
+
}
|
|
44
|
+
export function queryContextRecords(db, taskId, query) {
|
|
45
|
+
const family = FAMILIES[query.family];
|
|
46
|
+
if (family === undefined || !Number.isSafeInteger(query.limit) || query.limit < 0 || query.limit > 256
|
|
47
|
+
|| !Number.isSafeInteger(query.offset ?? 0) || (query.offset ?? 0) < 0) {
|
|
48
|
+
throw new Error("Context record query exceeds its bounded contract.");
|
|
49
|
+
}
|
|
50
|
+
const [table, key] = family;
|
|
51
|
+
const candidate = query.family === "candidate";
|
|
52
|
+
const payload = candidate ? "candidate.value" : "r.payload";
|
|
53
|
+
const id = candidate ? `r.${key} || '/' || json_extract(candidate.value, '$.id')` : `r.${key}`;
|
|
54
|
+
const from = `${table} r${candidate ? ", json_each(r.payload, '$.candidates') candidate" : ""}`;
|
|
55
|
+
const where = ["r.task_id = @taskId"];
|
|
56
|
+
const params = { taskId };
|
|
57
|
+
if (query.ids !== undefined) {
|
|
58
|
+
where.push(`${id} IN (SELECT value FROM json_each(@ids))`);
|
|
59
|
+
params.ids = JSON.stringify(query.ids);
|
|
60
|
+
}
|
|
61
|
+
for (const [label, statuses, not] of [
|
|
62
|
+
["statuses", query.statuses, ""],
|
|
63
|
+
["excludeStatuses", query.excludeStatuses, "NOT"]
|
|
64
|
+
]) {
|
|
65
|
+
if (statuses !== undefined) {
|
|
66
|
+
where.push(`json_extract(${payload}, '$.status') ${not} IN (SELECT value FROM json_each(@${label}))`);
|
|
67
|
+
params[label] = JSON.stringify(statuses);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
const sequence = `CAST(substr(r.${key}, length(r.${key}) - length(ltrim(r.${key}, 'abcdefghijklmnopqrstuvwxyz-')) + 1) AS INTEGER)`;
|
|
71
|
+
// Task-local numeric IDs are the stable historical order, not lexical 1,10,2.
|
|
72
|
+
if (query.family === "task-event") {
|
|
73
|
+
if (query.eventTypes !== undefined) {
|
|
74
|
+
where.push("r.type IN (SELECT value FROM json_each(@types))");
|
|
75
|
+
params.types = JSON.stringify(query.eventTypes);
|
|
76
|
+
}
|
|
77
|
+
for (const [key, value, comparison] of [
|
|
78
|
+
["after", query.afterSequence, ">"], ["through", query.throughSequence, "<="]
|
|
79
|
+
]) {
|
|
80
|
+
if (value !== undefined) {
|
|
81
|
+
if (!Number.isSafeInteger(value) || value < 0)
|
|
82
|
+
throw new Error("Invalid event cursor.");
|
|
83
|
+
where.push(`${sequence} ${comparison} @${key}`);
|
|
84
|
+
params[key] = value;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
const condition = where.join(" AND ");
|
|
89
|
+
const count = db.prepare(`SELECT count(*) AS n FROM ${from} WHERE ${condition}`).get(params).n;
|
|
90
|
+
if (query.limit === 0 || count === 0)
|
|
91
|
+
return { count, records: [] };
|
|
92
|
+
const direction = query.descending ? "DESC" : "ASC";
|
|
93
|
+
const order = query.family === "work-item" || candidate
|
|
94
|
+
? `r.updated_at DESC, ${sequence} ASC${candidate ? ", candidate.key DESC" : ""}`
|
|
95
|
+
: query.family === "managed-workspace" ? `r.${key} ${direction}` : `${sequence} ${direction}, r.${key} ${direction}`;
|
|
96
|
+
const rows = db.prepare(`SELECT ${id} AS id, ${payload} AS payload
|
|
97
|
+
FROM ${from} WHERE ${condition} ORDER BY ${order} LIMIT @limit OFFSET @offset`).all({ ...params, limit: query.limit, offset: query.offset ?? 0 });
|
|
98
|
+
return { count, records: rows.map(row => {
|
|
99
|
+
const value = JSON.parse(row.payload);
|
|
100
|
+
if (candidate)
|
|
101
|
+
validateWorkItemCandidate(value);
|
|
102
|
+
else
|
|
103
|
+
validateStoredRecord(table, value);
|
|
104
|
+
return { id: row.id, value };
|
|
105
|
+
}) };
|
|
106
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { isAbsolute, join, relative, resolve } from "node:path";
|
|
2
|
+
import { createHash } from "node:crypto";
|
|
2
3
|
/**
|
|
3
4
|
* The single authority for every Yui self-managed path derived from a Home.
|
|
4
5
|
*
|
|
@@ -10,7 +11,7 @@ import { isAbsolute, join, relative, resolve } from "node:path";
|
|
|
10
11
|
* single canonical YUI_HOME contains all self-managed data, with only the
|
|
11
12
|
* approved short-path IPC sockets (Controller, tmux, Agent Host, integration
|
|
12
13
|
* runtime) remaining outside it for the `sockaddr_un` length budget. Within the
|
|
13
|
-
* managed workspace root, storage
|
|
14
|
+
* managed workspace root, storage v25 further collapses the former
|
|
14
15
|
* physical-worktree/symlink-view split into a single layer of real worktrees
|
|
15
16
|
* under `tasks/` (see {@link managedTaskRoot}).
|
|
16
17
|
*
|
|
@@ -27,33 +28,21 @@ function homeRoot(home) {
|
|
|
27
28
|
return resolved;
|
|
28
29
|
}
|
|
29
30
|
/**
|
|
30
|
-
* Root that contains every managed Git workspace. Since storage
|
|
31
|
+
* Root that contains every managed Git workspace. Since storage v25 this holds a
|
|
31
32
|
* single layer of real worktrees under `tasks/` (plus the legacy `worktree/`
|
|
32
|
-
* root, preserved by the
|
|
33
|
+
* root, preserved by the 24->25 migration as its rollback anchor). A single
|
|
33
34
|
* parent keeps the managed families adjacent and lets the storage migrations
|
|
34
35
|
* relocate them as one subtree.
|
|
35
36
|
*/
|
|
36
37
|
export function managedWorkspacesRoot(home) {
|
|
37
38
|
return join(homeRoot(home), "workspaces");
|
|
38
39
|
}
|
|
39
|
-
/**
|
|
40
|
-
* LEGACY physical worktree root: `<home>/workspaces/worktree`. Before storage
|
|
41
|
-
* v20 every managed clone/linked worktree lived here under a per-Project subtree,
|
|
42
|
-
* surfaced to each Task through a symlink view under `tasks/`. The 19->20
|
|
43
|
-
* migration collapses those into real worktrees under `tasks/` and PRESERVES this
|
|
44
|
-
* tree as the rollback anchor, so this path survives only for that migration's
|
|
45
|
-
* inline layout and for post-upgrade cleanup — no current runtime code derives a
|
|
46
|
-
* live worktree path from it.
|
|
47
|
-
*/
|
|
48
|
-
export function managedWorktreeRoot(home) {
|
|
49
|
-
return join(managedWorkspacesRoot(home), "worktree");
|
|
50
|
-
}
|
|
51
40
|
/**
|
|
52
41
|
* Managed Git worktrees, one real worktree per Task owner and Project at
|
|
53
42
|
* `<home>/workspaces/tasks/<taskId>/<owner>/<projectDirectory>` (owner being
|
|
54
43
|
* `main`, `work-items/<id>`, `reviews/<name>`, `integrations/<id>`, or
|
|
55
|
-
* `execution-lanes/<g>/<l>`). Before storage
|
|
56
|
-
* the physical `worktree/` root; the
|
|
44
|
+
* `execution-lanes/<g>/<l>`). Before storage v25 these were symlink views over
|
|
45
|
+
* the physical `worktree/` root; the 24->25 migration makes them the worktrees
|
|
57
46
|
* themselves, so the logical entry path and the physical Git worktree coincide.
|
|
58
47
|
*/
|
|
59
48
|
export function managedTaskRoot(home) {
|
|
@@ -79,6 +68,13 @@ export function managedRuntimeRoot(home) {
|
|
|
79
68
|
export function managedIntegrationRuntimeRoot(home) {
|
|
80
69
|
return join(homeRoot(home), "runtime", "integration-runtimes");
|
|
81
70
|
}
|
|
71
|
+
/** The shared Home-scoped short IPC directory; ordinary integration data stays
|
|
72
|
+
* inside Home. A fixture that owns the whole Home may remove this when empty. */
|
|
73
|
+
export function integrationTmuxSocketRoot(home) {
|
|
74
|
+
const uid = typeof process.getuid === "function" ? process.getuid() : 0;
|
|
75
|
+
const homeDigest = createHash("sha256").update(homeRoot(home)).digest("hex").slice(0, 16);
|
|
76
|
+
return join("/tmp", `yi-${uid.toString(36)}-${homeDigest}`);
|
|
77
|
+
}
|
|
82
78
|
/**
|
|
83
79
|
* Disposable staging prefix for `yui update`'s side-by-side package install
|
|
84
80
|
* (an upgrade artifact): `<home>/runtime/update-staging`. Kept under Home so a
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/** Historical errors have no trustworthy launch snapshot. Preserve their raw
|
|
2
|
+
* payloads and explicitly record that fact; never infer it from today's Role. */
|
|
3
|
+
export function migrateAgentFailureContext(db) {
|
|
4
|
+
const archive = db.prepare(`INSERT INTO storage_migration_archive
|
|
5
|
+
(migration_version,family,record_key,payload,content) VALUES(36,'agent-error',?,?,NULL)`);
|
|
6
|
+
const update = db.prepare("UPDATE events SET payload=? WHERE task_id=? AND event_id=?");
|
|
7
|
+
const rows = db.prepare("SELECT task_id,event_id,payload FROM events WHERE type='runtime.agent-error'").all();
|
|
8
|
+
for (const row of rows) {
|
|
9
|
+
const event = JSON.parse(row.payload);
|
|
10
|
+
if (!event || event.schemaVersion !== 2 || event.type !== "runtime.agent-error" || event.taskId !== row.task_id
|
|
11
|
+
|| event.id !== row.event_id || !event.payload || typeof event.payload !== "object"
|
|
12
|
+
|| Array.isArray(event.payload) || Object.values(event.payload).some(value => typeof value !== "string")
|
|
13
|
+
|| Object.hasOwn(event.payload, "capabilityContext")) {
|
|
14
|
+
throw new Error(`Invalid pre-context Agent error: ${row.task_id}/${row.event_id}.`);
|
|
15
|
+
}
|
|
16
|
+
archive.run(JSON.stringify([row.task_id, row.event_id]), row.payload);
|
|
17
|
+
event.payload.capabilityContext = JSON.stringify({
|
|
18
|
+
status: "unavailable", reason: "Launch configuration was not recorded by this historical error producer."
|
|
19
|
+
});
|
|
20
|
+
update.run(JSON.stringify(event), row.task_id, row.event_id);
|
|
21
|
+
}
|
|
22
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/** Frozen 33→34 boundary. An old immediate request without origin was not
|
|
2
|
+
* auto-adoptable. Its owner must activate or cancel it with the old release;
|
|
3
|
+
* an upgrade cannot make that choice, even while execution is stopped. */
|
|
4
|
+
export function preflightCurrentInputContract(db) {
|
|
5
|
+
const blocked = db.prepare(`SELECT task_id FROM task_records
|
|
6
|
+
WHERE json_extract(payload,'$.status')='draft'
|
|
7
|
+
AND json_type(payload,'$.retiredAt') IS NULL
|
|
8
|
+
AND json_extract(payload,'$.activationRequest.startMode')='immediate'
|
|
9
|
+
AND json_extract(payload,'$.activationRequest.disposition')='pending'
|
|
10
|
+
AND json_type(payload,'$.activationRequest.origin') IS NULL LIMIT 1`)
|
|
11
|
+
.get();
|
|
12
|
+
if (blocked !== undefined) {
|
|
13
|
+
throw new Error(`Input contract cutover requires explicit activation or cancellation of the origin-less pending request: ${blocked.task_id}. Preserve the request and resolve it with the old release before upgrading.`);
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
/** Preserve original representations as audit Events, not runtime alternatives.
|
|
17
|
+
* All interpretation of wakePolicy/origin ends at this migration. */
|
|
18
|
+
export function migrateCurrentInputContract(db) {
|
|
19
|
+
preflightCurrentInputContract(db);
|
|
20
|
+
const highWater = db.prepare(`SELECT max(
|
|
21
|
+
coalesce((SELECT high_water FROM id_sequences WHERE task_id=? AND kind='event'),0),
|
|
22
|
+
coalesce((SELECT max(CAST(substr(event_id,7) AS INTEGER)) FROM events WHERE task_id=?),0)
|
|
23
|
+
) AS value`);
|
|
24
|
+
const saveEvent = db.prepare("INSERT INTO events(task_id,event_id,type,occurred_at,payload) VALUES(?,?,?,?,?)");
|
|
25
|
+
const saveCounter = db.prepare(`INSERT INTO id_sequences(task_id,kind,high_water) VALUES(?,'event',?)
|
|
26
|
+
ON CONFLICT(task_id,kind) DO UPDATE SET high_water=max(high_water,excluded.high_water)`);
|
|
27
|
+
const at = new Date().toISOString();
|
|
28
|
+
const archive = (taskId, type, record) => {
|
|
29
|
+
const seq = highWater.get(taskId, taskId).value + 1;
|
|
30
|
+
const event = { schemaVersion: 2, id: `event-${seq}`, taskId, type, createdAt: at,
|
|
31
|
+
payload: { record, disposition: "audit-only" } };
|
|
32
|
+
saveEvent.run(taskId, event.id, type, at, JSON.stringify(event));
|
|
33
|
+
saveCounter.run(taskId, seq);
|
|
34
|
+
};
|
|
35
|
+
const messages = db.prepare("SELECT task_id,message_id,payload FROM messages ORDER BY task_id,seq")
|
|
36
|
+
.all();
|
|
37
|
+
const saveMessage = db.prepare("UPDATE messages SET payload=? WHERE task_id=? AND message_id=?");
|
|
38
|
+
for (const row of messages) {
|
|
39
|
+
const message = JSON.parse(row.payload);
|
|
40
|
+
const user = message.kind === "user" || message.kind === "operator";
|
|
41
|
+
const hasPolicy = Object.hasOwn(message, "wakePolicy");
|
|
42
|
+
if (message.schemaVersion !== 3 || message.id !== row.message_id || message.taskId !== row.task_id
|
|
43
|
+
|| (hasPolicy && (!user || !["leader", "none"].includes(message.wakePolicy)))
|
|
44
|
+
|| (message.intent !== undefined && (!user || !["record", "discuss", "develop"].includes(message.intent)))
|
|
45
|
+
|| (hasPolicy && message.intent !== undefined
|
|
46
|
+
&& (message.wakePolicy === "none") !== (message.intent === "record"))) {
|
|
47
|
+
throw new Error(`Invalid historical Message input: ${row.task_id}/${row.message_id}. Preserve it for diagnosis.`);
|
|
48
|
+
}
|
|
49
|
+
if (!hasPolicy && (!user || message.intent !== undefined))
|
|
50
|
+
continue;
|
|
51
|
+
archive(row.task_id, "message.input-contract-retired", row.payload);
|
|
52
|
+
if (user && message.intent === undefined) {
|
|
53
|
+
message.intent = message.wakePolicy === "none" ? "record" : "discuss";
|
|
54
|
+
}
|
|
55
|
+
delete message.wakePolicy;
|
|
56
|
+
saveMessage.run(JSON.stringify(message), row.task_id, row.message_id);
|
|
57
|
+
}
|
|
58
|
+
const tasks = db.prepare("SELECT task_id,payload FROM task_records ORDER BY task_id")
|
|
59
|
+
.all();
|
|
60
|
+
const saveTask = db.prepare("UPDATE task_records SET payload=? WHERE task_id=?");
|
|
61
|
+
for (const row of tasks) {
|
|
62
|
+
const task = JSON.parse(row.payload);
|
|
63
|
+
const requests = [
|
|
64
|
+
...(task.activationRequest === undefined ? [] : [task.activationRequest]),
|
|
65
|
+
...(task.settledActivationRequests ?? [])
|
|
66
|
+
];
|
|
67
|
+
let changed = false;
|
|
68
|
+
for (const request of requests) {
|
|
69
|
+
if (request === null || typeof request !== "object" || request.schemaVersion !== 1
|
|
70
|
+
|| request.operation?.targetId !== row.task_id
|
|
71
|
+
|| !["immediate", "after-planning-turn"].includes(request.startMode)
|
|
72
|
+
|| !["pending", "failed", "cancelled", "adopted"].includes(request.disposition)
|
|
73
|
+
|| (Object.hasOwn(request, "origin") && !["explicit", "submit-develop"].includes(request.origin))) {
|
|
74
|
+
throw new Error(`Invalid historical activation request: ${row.task_id}. Preserve it for diagnosis.`);
|
|
75
|
+
}
|
|
76
|
+
if (Object.hasOwn(request, "origin")) {
|
|
77
|
+
changed = true;
|
|
78
|
+
delete request.origin;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
if (changed) {
|
|
82
|
+
archive(row.task_id, "task.activation-origin-retired", row.payload);
|
|
83
|
+
saveTask.run(JSON.stringify(task), row.task_id);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
}
|