@zq-silk/yui 0.15.11 → 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 +11 -6
- package/ARCHITECTURE.zh-CN.md +8 -4
- package/README.md +13 -5
- package/dist/agentRun/agentRun.js +3 -0
- 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 +92 -48
- 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 +11 -3
- package/dist/cli/invocationAuthority.js +64 -0
- package/dist/cli/managedDiagnostics.js +2 -2
- package/dist/cli/parseRepeatable.js +35 -0
- package/dist/cli/updatePorts.js +26 -5
- package/dist/cli.js +1303 -1235
- 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/executionAuditCommands.js +10 -0
- package/dist/commands/globalRoleCommands.js +78 -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 +45 -25
- 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 +355 -732
- 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 +20 -58
- package/dist/commands/taskNextActionCommand.js +7 -6
- package/dist/commands/taskPublicationAdoptCommand.js +127 -0
- package/dist/commands/taskPublicationCommands.js +12 -3
- package/dist/commands/taskPublicationVerifyCommand.js +24 -40
- package/dist/commands/taskRemoteDeliveryCommand.js +5 -86
- 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 +5 -3
- package/dist/context/taskCatalog.js +185 -0
- package/dist/context/taskContext.js +152 -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 +20 -24
- package/dist/controller/fileSchedulerStoreAdapter.js +191 -225
- package/dist/controller/globalInputDelivery.js +13 -0
- package/dist/controller/jobClient.js +4 -4
- package/dist/controller/jobControl.js +60 -25
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/providerRetryAdmission.js +100 -0
- package/dist/controller/providerRetryDelivery.js +218 -0
- package/dist/controller/runtime.js +85 -43
- 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 +79 -90
- 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 +5 -10
- package/dist/message/globalProviderRetry.js +15 -0
- 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/executionAudit.js +19 -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/gitWorkspace.js +371 -105
- package/dist/repository/projectMaintenanceLock.js +98 -82
- package/dist/repository/taskBaseFreshness.js +1 -1
- package/dist/repository/taskWorkspaceCoordinator.js +82 -144
- package/dist/repository/taskWorkspacePreparer.js +113 -47
- package/dist/repository/workspaceCleanupInspection.js +187 -0
- 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/agentError.js +5 -3
- package/dist/runtime/agentFailureContext.js +43 -0
- package/dist/runtime/agentHost.js +42 -25
- package/dist/runtime/builtinAgentErrorMappers.js +91 -0
- package/dist/runtime/codexAppServerRuntime.js +34 -3
- package/dist/runtime/codexInteractiveHost.js +2 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/managedCaller.js +21 -1
- package/dist/runtime/providerControl.js +5 -1
- package/dist/runtime/providerErrors.js +38 -0
- package/dist/runtime/providerRetry.js +198 -0
- package/dist/runtime/providerRuntimeIdentity.js +28 -2
- package/dist/runtime/runtimeObservation.js +0 -5
- package/dist/runtime/runtimeSessionCandidate.js +2 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/sessionTokenMetrics.js +15 -5
- package/dist/runtime/structuredProviderHost.js +10 -43
- package/dist/runtime/taskUsageMetrics.js +275 -0
- package/dist/runtime/tmuxAdapters.js +6 -8
- package/dist/scheduler/activeRoleRunDelivery.js +19 -7
- package/dist/scheduler/leaderWakeupProcessor.js +24 -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 +49 -36
- package/dist/scheduler/taskObservabilityProjection.js +6 -45
- 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 +173 -1
- package/dist/storage/sqliteStore.js +172 -161
- package/dist/storage/storageSchema.js +2 -14
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +16 -7
- package/dist/storage/taskCatalog.js +123 -0
- package/dist/storage/taskStore.js +5 -7
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
- package/dist/task/archiveDiagnostics.js +1 -0
- package/dist/task/archivePreflight.js +124 -0
- package/dist/task/completionReadiness.js +5 -24
- package/dist/task/draftPlan.js +0 -53
- package/dist/task/nextAction.js +7 -13
- package/dist/task/publicationAdoption.js +56 -0
- package/dist/task/publicationReference.js +10 -0
- package/dist/task/remoteDelivery.js +31 -16
- 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 +92 -17
- package/dist/web/assets/client/components.js +55 -13
- package/dist/web/assets/client/i18n.js +72 -4
- package/dist/web/assets/client/taskSurface.js +6 -5
- package/dist/web/assets/client/view.js +35 -7
- package/dist/web/assets/shell.js +6 -0
- package/dist/web/assets/styles/layout.js +7 -0
- package/dist/web/webServer.js +12 -3
- package/dist/web/webSnapshot.js +14 -85
- package/dist/web/webTaskSurface.js +17 -46
- package/dist/workItem/workItem.js +1 -12
- package/dist/workspace/cleanupInspection.js +63 -0
- package/dist/workspace/workItemChangeSetManager.js +110 -50
- package/docs/agent-result-consumption.md +4 -0
- package/docs/agent-result-consumption.zh-CN.md +3 -0
- package/docs/agent-runtime-drivers.md +7 -0
- package/docs/agent-runtime-drivers.zh-CN.md +5 -0
- package/docs/architecture/README.md +2 -0
- package/docs/architecture/README.zh-CN.md +3 -1
- package/docs/architecture/capabilities-and-resources.md +42 -5
- package/docs/architecture/capabilities-and-resources.zh-CN.md +33 -3
- package/docs/managed-turn-and-session-runtime.md +121 -1
- package/docs/managed-turn-and-session-runtime.zh-CN.md +96 -2
- package/docs/observability/README.md +62 -0
- package/docs/observability/README.zh-CN.md +47 -0
- package/docs/plugin-sdk.md +4 -2
- package/docs/project-refresh.md +77 -0
- package/docs/project-refresh.zh-CN.md +59 -0
- package/docs/provider-retry.md +70 -0
- 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 +223 -17
- package/docs/task-delivery.zh-CN.md +171 -14
- package/docs/task-discovery.md +101 -0
- package/docs/task-discovery.zh-CN.md +85 -0
- package/docs/testing/verification-levels.md +161 -9
- package/docs/testing/verification-levels.zh-CN.md +116 -9
- package/i18n/README.zh-CN.md +13 -7
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -0
- package/skills/yui-leader/references/execution.md +3 -2
- package/skills/yui-leader/references/integration.md +9 -5
- package/skills/yui-leader/references/planning.md +9 -3
- package/skills/yui-operator/SKILL.md +24 -8
- package/skills/yui-reviewer/SKILL.md +4 -0
- package/skills/yui-runtime/SKILL.md +17 -2
- package/skills/yui-runtime/references/publication.md +26 -4
- package/skills/yui-runtime/references/recovery.md +71 -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 -376
- 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)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
2
2
|
import { runtimeObservationFromTaskEvent } from "../runtime/runtimeObservation.js";
|
|
3
|
+
import { providerRetryProjection } from "../runtime/providerRetry.js";
|
|
3
4
|
import { taskOwnsManagedWorkspace } from "../task/task.js";
|
|
4
5
|
import { currentWorkItemExecutionGroup } from "../workItem/workItem.js";
|
|
5
6
|
import { mailboxBatches } from "../coordination/workMailbox.js";
|
|
@@ -13,21 +14,24 @@ import { projectSessionTokenMetrics, resolveSessionTokenIdentity } from "../runt
|
|
|
13
14
|
* aggregate. This function only reads; it never starts a Controller, queues a
|
|
14
15
|
* wake, writes a Message, or mutates any record.
|
|
15
16
|
*/
|
|
16
|
-
export function buildTaskExecutionProjection(store, taskId,
|
|
17
|
-
const task = store.getTask
|
|
17
|
+
export function buildTaskExecutionProjection(store, taskId, now = new Date()) {
|
|
18
|
+
const task = store.getTask(taskId);
|
|
18
19
|
if (task === null)
|
|
19
20
|
return null;
|
|
20
|
-
const roles = store.listRoles
|
|
21
|
-
const events = store.listEvents
|
|
22
|
-
const
|
|
23
|
-
const
|
|
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);
|
|
26
|
+
const runs = operationalTaskRecords(usageRuns, events, "run");
|
|
27
|
+
const leaderMailbox = store.getWorkMailbox({
|
|
24
28
|
kind: "role",
|
|
25
29
|
taskId,
|
|
26
30
|
roleName: "leader"
|
|
27
|
-
})
|
|
31
|
+
});
|
|
28
32
|
const roleSessions = roles.flatMap((role) => {
|
|
29
|
-
const session = store.getRoleSession
|
|
30
|
-
return session === null
|
|
33
|
+
const session = store.getRoleSession(taskId, role.name);
|
|
34
|
+
return session === null
|
|
31
35
|
? []
|
|
32
36
|
: [{ roleName: role.name, ...session }];
|
|
33
37
|
});
|
|
@@ -35,35 +39,30 @@ export function buildTaskExecutionProjection(store, taskId, taskOverride, now =
|
|
|
35
39
|
task,
|
|
36
40
|
roles,
|
|
37
41
|
runs,
|
|
42
|
+
usageRuns,
|
|
43
|
+
providerRetries: roles.flatMap(role => {
|
|
44
|
+
const retry = providerRetryProjection(store.getTaskRoleSessionSet(taskId, role.name)?.providerBinding);
|
|
45
|
+
return retry === null ? [] : [retry];
|
|
46
|
+
}),
|
|
38
47
|
runDelivery: Object.fromEntries(runs.map((run) => {
|
|
39
|
-
const observed = store.getTaskRoleSessionSet
|
|
48
|
+
const observed = store.getTaskRoleSessionSet(taskId, run.roleName)?.providerBinding?.run;
|
|
40
49
|
return [run.id, observed?.runId === run.id ? observed.status : "unobserved"];
|
|
41
50
|
})),
|
|
42
|
-
executionGroups:
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
: { reviewRounds: store.listReviewRounds(taskId) }),
|
|
53
|
-
...(store.listChangeSets === undefined
|
|
54
|
-
? {}
|
|
55
|
-
: { changeSets: store.listChangeSets(taskId) }),
|
|
56
|
-
...(store.listIntegrationAttempts === undefined
|
|
57
|
-
? {}
|
|
58
|
-
: { integrations: store.listIntegrationAttempts(taskId) }),
|
|
59
|
-
...(store.listEvents === undefined ? {} : { events }),
|
|
60
|
-
...(store.getTaskBrief === undefined ? {} : { brief: store.getTaskBrief(taskId) }),
|
|
61
|
-
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),
|
|
62
61
|
leaderMailbox,
|
|
63
|
-
leaderFailure: store.getLeaderFailure
|
|
62
|
+
leaderFailure: store.getLeaderFailure(taskId),
|
|
64
63
|
roleSessions,
|
|
65
64
|
now,
|
|
66
|
-
runtimeHealthPolicy: resolveRuntimeHealth(store.getConfig
|
|
65
|
+
runtimeHealthPolicy: resolveRuntimeHealth(store.getConfig().runtimeHealth)
|
|
67
66
|
});
|
|
68
67
|
}
|
|
69
68
|
/**
|
|
@@ -79,6 +78,7 @@ export function projectTaskExecutionFromFacts(facts) {
|
|
|
79
78
|
return projectTaskExecution({
|
|
80
79
|
...facts,
|
|
81
80
|
runs,
|
|
81
|
+
usageRuns: facts.usageRuns ?? facts.runs,
|
|
82
82
|
executionGroups
|
|
83
83
|
});
|
|
84
84
|
}
|
|
@@ -107,9 +107,10 @@ export function projectTaskExecution(facts) {
|
|
|
107
107
|
});
|
|
108
108
|
});
|
|
109
109
|
const observability = buildTaskObservabilityProjection({
|
|
110
|
+
task,
|
|
110
111
|
workItems,
|
|
111
112
|
executionGroups: observabilityGroups,
|
|
112
|
-
runs,
|
|
113
|
+
runs: facts.usageRuns ?? runs,
|
|
113
114
|
events,
|
|
114
115
|
contextSnapshots: facts.contextSnapshots,
|
|
115
116
|
sessionTokens,
|
|
@@ -206,8 +207,12 @@ export function projectTaskExecution(facts) {
|
|
|
206
207
|
const hasPendingLeaderWork = pendingWakeup !== null
|
|
207
208
|
|| leaderMailbox?.pending !== null;
|
|
208
209
|
const recoveryPending = isRecoveryPending(pendingWakeup, leaderMailbox, leaderFailure);
|
|
209
|
-
const
|
|
210
|
-
|
|
210
|
+
const providerRecoveries = (facts.providerRetries ?? []).filter(retry => retry.status === "waiting" || retry.status === "in-flight");
|
|
211
|
+
const recoveringRunIds = new Set(providerRecoveries.flatMap(retry => [retry.previousRunId, retry.successorRunId].filter((id) => id !== undefined)));
|
|
212
|
+
const failedWork = workItems.some((item) => {
|
|
213
|
+
const last = runs.filter((run) => run.workItemId === item.id).at(-1);
|
|
214
|
+
return item.status === "open" && last?.status === "failed" && !recoveringRunIds.has(last.id);
|
|
215
|
+
});
|
|
211
216
|
const candidateReady = workItems.some((item) => (item.status === "open" && item.currentCandidateId !== undefined));
|
|
212
217
|
const blockedIntegration = integrations.some((attempt) => attempt.status === "blocked");
|
|
213
218
|
const conflictedIntegration = integrations.find((attempt) => attempt.status === "conflicted");
|
|
@@ -258,7 +263,7 @@ export function projectTaskExecution(facts) {
|
|
|
258
263
|
pendingWakeup
|
|
259
264
|
});
|
|
260
265
|
}
|
|
261
|
-
if (laneRecovery !== undefined) {
|
|
266
|
+
if (laneRecovery !== undefined && (laneRecovery.runId === undefined || !recoveringRunIds.has(laneRecovery.runId))) {
|
|
262
267
|
return render({
|
|
263
268
|
task,
|
|
264
269
|
status: laneRecovery.runtimeHealth === "confirmed-dead"
|
|
@@ -306,6 +311,14 @@ export function projectTaskExecution(facts) {
|
|
|
306
311
|
pendingWakeup
|
|
307
312
|
});
|
|
308
313
|
}
|
|
314
|
+
if (providerRecoveries.length > 0 && healthyActiveRuns.length === 0) {
|
|
315
|
+
return render({
|
|
316
|
+
task, status: "recovering", owner: "none", action: "none",
|
|
317
|
+
summary: `${providerRecoveries.length} Provider recovery chain(s) are cooling down or awaiting their exact result; existing work is preserved. Do not dispatch a duplicate.`,
|
|
318
|
+
reason: "provider-recovery", monitoring, failClosed: false,
|
|
319
|
+
activeRuns: activeRunViews, attention, blockers, pendingWakeup
|
|
320
|
+
});
|
|
321
|
+
}
|
|
309
322
|
if (recoveryPending) {
|
|
310
323
|
return render({
|
|
311
324
|
task,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { projectTaskUsageMetrics } from "../runtime/taskUsageMetrics.js";
|
|
1
2
|
/**
|
|
2
3
|
* Build the read-only DAG, execution, cost, and context view consumed by CLI
|
|
3
4
|
* and Web. It deliberately derives every value from existing Task records and
|
|
@@ -8,7 +9,7 @@ export function buildTaskObservabilityProjection(input) {
|
|
|
8
9
|
const dag = projectDag(input.workItems);
|
|
9
10
|
const workItems = input.workItems.map((item) => {
|
|
10
11
|
const groups = item.executionGroups;
|
|
11
|
-
const itemCost =
|
|
12
|
+
const itemCost = projectCost({ ...input, now }, groups, item.id);
|
|
12
13
|
const itemContext = projectContext(groups, input.runs, input.contextSnapshots);
|
|
13
14
|
const producerObservability = projectWorkItemProducerObservability(item, input.runs);
|
|
14
15
|
return Object.freeze({
|
|
@@ -21,7 +22,7 @@ export function buildTaskObservabilityProjection(input) {
|
|
|
21
22
|
resultCount: producerObservability?.resultCount ?? null
|
|
22
23
|
});
|
|
23
24
|
});
|
|
24
|
-
const cost = projectCost(input
|
|
25
|
+
const cost = projectCost({ ...input, now }, input.executionGroups);
|
|
25
26
|
const context = projectContext(input.executionGroups, input.runs, input.contextSnapshots);
|
|
26
27
|
return Object.freeze({
|
|
27
28
|
dag,
|
|
@@ -59,7 +60,6 @@ function projectDag(workItems) {
|
|
|
59
60
|
const edges = [];
|
|
60
61
|
for (const item of workItems) {
|
|
61
62
|
for (const dependency of item.dependsOn) {
|
|
62
|
-
const target = byId.get(dependency);
|
|
63
63
|
const status = dependencyEdgeStatus(dependency, byId);
|
|
64
64
|
edges.push({ from: dependency, to: item.id, status });
|
|
65
65
|
dependents.get(dependency)?.push(item.id);
|
|
@@ -140,17 +140,13 @@ function dependencyEdgeStatus(id, byId) {
|
|
|
140
140
|
return "active";
|
|
141
141
|
return "active";
|
|
142
142
|
}
|
|
143
|
-
function projectCost(
|
|
143
|
+
function projectCost(input, groups, workItemId) {
|
|
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 = runs.filter(({ executionGroupId }) => (executionGroupId !== undefined && groupIds.has(executionGroupId)));
|
|
146
|
+
const attempts = input.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
|
-
|
|
150
|
-
toolCalls: 0,
|
|
151
|
-
wallClockSeconds: uniqueGroups.reduce((total, group) => total + groupDurationSeconds(group, now), 0),
|
|
152
|
-
tokensObservable: false,
|
|
153
|
-
toolCallsObservable: false,
|
|
149
|
+
...projectTaskUsageMetrics({ ...input, workItemId }),
|
|
154
150
|
laneCount,
|
|
155
151
|
groupCount: uniqueGroups.length,
|
|
156
152
|
retryCount: Math.max(0, attempts.length - laneCount),
|
|
@@ -158,32 +154,6 @@ function projectCost(groups, runs, _events, now) {
|
|
|
158
154
|
marginalValueStatus: "unavailable"
|
|
159
155
|
});
|
|
160
156
|
}
|
|
161
|
-
function projectWorkItemCost(groups, runs, now) {
|
|
162
|
-
const groupIds = new Set(groups.map(({ id }) => id));
|
|
163
|
-
const attempts = runs.filter(({ executionGroupId }) => (executionGroupId !== undefined && groupIds.has(executionGroupId)));
|
|
164
|
-
const wallClockSeconds = groups.reduce((total, group) => {
|
|
165
|
-
const started = Date.parse(group.createdAt);
|
|
166
|
-
const ended = group.lanes.some(({ disposition }) => disposition === "open")
|
|
167
|
-
? now.getTime()
|
|
168
|
-
: Date.parse(group.updatedAt);
|
|
169
|
-
return total + (Number.isFinite(started) && Number.isFinite(ended)
|
|
170
|
-
? Math.max(0, Math.floor((ended - started) / 1_000))
|
|
171
|
-
: 0);
|
|
172
|
-
}, 0);
|
|
173
|
-
const laneCount = groups.reduce((total, group) => total + group.lanes.length, 0);
|
|
174
|
-
return Object.freeze({
|
|
175
|
-
tokens: 0,
|
|
176
|
-
toolCalls: 0,
|
|
177
|
-
wallClockSeconds,
|
|
178
|
-
tokensObservable: false,
|
|
179
|
-
toolCallsObservable: false,
|
|
180
|
-
laneCount,
|
|
181
|
-
groupCount: groups.length,
|
|
182
|
-
retryCount: Math.max(0, attempts.length - laneCount),
|
|
183
|
-
marginalValuePercent: null,
|
|
184
|
-
marginalValueStatus: "unavailable"
|
|
185
|
-
});
|
|
186
|
-
}
|
|
187
157
|
function projectContext(groups, runs, snapshots) {
|
|
188
158
|
const refs = new Map();
|
|
189
159
|
for (const group of groups) {
|
|
@@ -223,12 +193,3 @@ function projectContext(groups, runs, snapshots) {
|
|
|
223
193
|
compressionStatus: "unavailable"
|
|
224
194
|
});
|
|
225
195
|
}
|
|
226
|
-
function groupDurationSeconds(group, now) {
|
|
227
|
-
const started = Date.parse(group.createdAt);
|
|
228
|
-
const ended = group.lanes.some(({ disposition }) => disposition === "open")
|
|
229
|
-
? now.getTime()
|
|
230
|
-
: Date.parse(group.updatedAt);
|
|
231
|
-
return Number.isFinite(started) && Number.isFinite(ended)
|
|
232
|
-
? Math.max(0, Math.floor((ended - started) / 1_000))
|
|
233
|
-
: 0;
|
|
234
|
-
}
|
|
@@ -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
|
+
}
|