@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
|
@@ -28,45 +28,44 @@
|
|
|
28
28
|
* `telemetry` scoped by its primary key — it never rewrites global
|
|
29
29
|
* state and never touches another Task's rows (§4.4).
|
|
30
30
|
*
|
|
31
|
-
* The
|
|
32
|
-
*
|
|
33
|
-
*
|
|
34
|
-
* it performs the same cheap structural checks the file store relies on
|
|
35
|
-
* (identity presence, taskId matching, referential lookups).
|
|
31
|
+
* The same current record validators apply to direct and worker-backed access,
|
|
32
|
+
* including writes, reads and explicit Home diagnostics. There is no weaker
|
|
33
|
+
* in-process contract or deferred validation phase.
|
|
36
34
|
*/
|
|
35
|
+
import Database from "better-sqlite3";
|
|
37
36
|
import { existsSync, mkdirSync } from "node:fs";
|
|
38
37
|
import { join } from "node:path";
|
|
39
38
|
import { isDeepStrictEqual } from "node:util";
|
|
40
|
-
import
|
|
41
|
-
import { validatePluginValidation } from "../plugins/pluginPackage.js";
|
|
42
|
-
import { validatePluginIntent, validatePluginIntentFailure } from "../plugins/pluginIntent.js";
|
|
43
|
-
import { validateLocalResource, validateEnvironmentPreparation } from "../resources/projectResource.js";
|
|
44
|
-
import { validateConfiguredAgent } from "../agent/agent.js";
|
|
39
|
+
import { runPurposeAdmitsTaskState } from "../agentRun/agentRun.js";
|
|
45
40
|
import { validateTaskBrief } from "../brief/taskBrief.js";
|
|
46
|
-
import { consumePendingBatch, mailboxTargetKey, validateWorkMailbox } from "../coordination/workMailbox.js";
|
|
47
41
|
import { validateContextSnapshot } from "../context/contextSnapshot.js";
|
|
42
|
+
import { consumePendingBatch, mailboxTargetKey, validateWorkMailbox } from "../coordination/workMailbox.js";
|
|
43
|
+
import { validateDurableJob, validDurableJobTransition } from "../job/durableJob.js";
|
|
48
44
|
import { validateGlobalRoleMessage } from "../message/message.js";
|
|
49
|
-
import {
|
|
45
|
+
import { validatePluginIntent, validatePluginIntentFailure } from "../plugins/pluginIntent.js";
|
|
46
|
+
import { validatePluginValidation } from "../plugins/pluginPackage.js";
|
|
47
|
+
import { generateHomeIdentity, validateHomeIdentity } from "../repository/homeIdentity.js";
|
|
48
|
+
import { validateProject } from "../repository/project.js";
|
|
49
|
+
import { validateEnvironmentPreparation, validateLocalResource } from "../resources/projectResource.js";
|
|
50
|
+
import { validateReviewRound } from "../review/reviewRound.js";
|
|
50
51
|
import { compareRuntimeSessionCandidates, projectRuntimeSessionCandidate } from "../runtime/runtimeSessionCandidate.js";
|
|
51
52
|
import { sessionOwnerProcessKey } from "../runtime/sessionOwnerIdentity.js";
|
|
52
|
-
import { validateReviewRound } from "../review/reviewRound.js";
|
|
53
|
-
import { validateProject } from "../repository/project.js";
|
|
54
|
-
import { generateHomeIdentity, validateHomeIdentity } from "../repository/homeIdentity.js";
|
|
55
|
-
import { validateIntegrationQueueEntry } from "../integration/integrationQueueEntry.js";
|
|
56
|
-
import { validDurableJobTransition, validateDurableJob } from "../job/durableJob.js";
|
|
57
|
-
import { validateGlobalRole, validateTaskRole } from "../role/role.js";
|
|
58
53
|
import { validateTaskWake } from "../scheduler/taskWake.js";
|
|
59
|
-
import { validateTask } from "../task/task.js";
|
|
60
54
|
import { pendingCompletionMessages } from "../task/completionReadiness.js";
|
|
61
|
-
import {
|
|
55
|
+
import { publicationExternalKey } from "../task/publicationReference.js";
|
|
56
|
+
import { validateTask } from "../task/task.js";
|
|
62
57
|
import { TASK_RECORD_ID_PREFIXES } from "../task/taskRecordReference.js";
|
|
58
|
+
import { operationalTaskRecords, TASK_RECORD_RETIRED_EVENT } from "../task/taskRecordRetirement.js";
|
|
59
|
+
import { writeTelemetryBatch } from "../telemetry/sqliteTelemetryBatch.js";
|
|
60
|
+
import { STORED_RECORD_TABLES, validateStoredRecord } from "./recordValidation.js";
|
|
61
|
+
import { gateArtifactKey, validateGateArtifact } from "../verification/gateArtifact.js";
|
|
63
62
|
import { validateWorkItem } from "../workItem/workItem.js";
|
|
64
63
|
import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
|
|
65
|
-
import {
|
|
66
|
-
import { publicationExternalKey } from "../task/publicationReference.js";
|
|
67
|
-
import { gateArtifactKey, validateGateArtifact } from "../verification/gateArtifact.js";
|
|
64
|
+
import { contextInputReferences, queryContextRecords } from "./contextRecords.js";
|
|
68
65
|
import { inspectSqliteSchemaMigrations, migrateSqliteSchema, SqliteSchemaMigrationError, TELEMETRY_KEEP_PER_RUN, TELEMETRY_RUN_CAP } from "./sqliteSchema.js";
|
|
69
66
|
import { StorageSchemaError } from "./storageSchema.js";
|
|
67
|
+
import { queryTaskCatalog } from "./taskCatalog.js";
|
|
68
|
+
import { CURRENT_CONFIG_SCHEMA_VERSION, CURRENT_WORK_MAILBOX_SCHEMA_VERSION, executionLaneActiveRunKey, executionLaneActiveRunKeyParts, isValidCapabilityGrantTransition, isValidReleaseWorkflowTransition, pendingWakeupProjection, StorageCancelledError, StorageConflictError, StorageRecordError, storedCapabilityGrant, storedPublicationReference, storedReleaseWorkflow, validateYuiConfig } from "./taskStore.js";
|
|
70
69
|
/** Read the immutable Home identity without opening a writable Store connection. */
|
|
71
70
|
export function readSqliteHomeIdentity(rootDir, databaseFilename = "yui.db") {
|
|
72
71
|
const database = new Database(join(rootDir, databaseFilename), {
|
|
@@ -221,7 +220,75 @@ export class SqliteTaskStore {
|
|
|
221
220
|
}
|
|
222
221
|
#now() { return new Date().toISOString(); }
|
|
223
222
|
#json(value) { return JSON.stringify(value); }
|
|
223
|
+
#recordJson(table, value) {
|
|
224
|
+
validateStoredRecord(table, value);
|
|
225
|
+
return this.#json(value);
|
|
226
|
+
}
|
|
227
|
+
/** Explicit full-Home diagnosis; ordinary reads validate only their records. */
|
|
228
|
+
validateCurrentRecords() {
|
|
229
|
+
this.getConfig();
|
|
230
|
+
for (const table of STORED_RECORD_TABLES) {
|
|
231
|
+
for (const row of this.#db.prepare(`SELECT payload FROM ${table}`).iterate()) {
|
|
232
|
+
this.#validateTaskPayload(table, row.payload);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
for (const row of this.#db.prepare("SELECT brief FROM task_records WHERE brief IS NOT NULL").iterate()) {
|
|
236
|
+
validateTaskBrief(this.#parse(row.brief));
|
|
237
|
+
}
|
|
238
|
+
}
|
|
224
239
|
#parse(text) { return JSON.parse(text); }
|
|
240
|
+
queryContextRecords(taskId, query) {
|
|
241
|
+
return queryContextRecords(this.#db, taskId, query);
|
|
242
|
+
}
|
|
243
|
+
contextInputReferences(taskId, scope) {
|
|
244
|
+
return contextInputReferences(this.#db, taskId, scope);
|
|
245
|
+
}
|
|
246
|
+
listActiveRuns(taskId) {
|
|
247
|
+
return this.#sortById(this.#listPayload("turns", "task_id = ? AND status = 'active'", [taskId]), run => run.id);
|
|
248
|
+
}
|
|
249
|
+
latestEventSequence(taskId) {
|
|
250
|
+
return this.#db.prepare("SELECT COALESCE(MAX(CAST(substr(event_id, 7) AS INTEGER)), 0) AS seq FROM events WHERE task_id = ?").get(taskId).seq;
|
|
251
|
+
}
|
|
252
|
+
listEventsByType(taskId, types) {
|
|
253
|
+
return this.#sortById(this.#listPayload("events", "task_id = ? AND type IN (SELECT value FROM json_each(?))", [taskId, JSON.stringify(types)]), event => event.id);
|
|
254
|
+
}
|
|
255
|
+
/** Delivery needs original workspace bases, never complete Run reports. */
|
|
256
|
+
listTaskRunWorkspaceBases(taskId) {
|
|
257
|
+
return this.#db.prepare(`SELECT json_object(
|
|
258
|
+
'id', turn_id, 'createdAt', json_extract(payload, '$.createdAt'),
|
|
259
|
+
'workspace', json_extract(payload, '$.workspace')) AS payload
|
|
260
|
+
FROM turns WHERE task_id = ? AND json_extract(payload, '$.workspace.owner.type') = 'task'`).all(taskId).map(row => JSON.parse(row.payload));
|
|
261
|
+
}
|
|
262
|
+
/** A pinned read snapshot never reserves the database's single writer slot.
|
|
263
|
+
* Nested reads share an existing transaction; standalone reads also prohibit
|
|
264
|
+
* accidental writes at SQLite's boundary.
|
|
265
|
+
*/
|
|
266
|
+
readTransaction(execute) {
|
|
267
|
+
if (this.#inTransaction)
|
|
268
|
+
return execute(this);
|
|
269
|
+
const previous = this.#db.pragma("query_only", { simple: true });
|
|
270
|
+
this.#db.pragma("query_only = ON");
|
|
271
|
+
this.#db.exec("BEGIN");
|
|
272
|
+
this.#inTransaction = true;
|
|
273
|
+
try {
|
|
274
|
+
this.#prepareWrite();
|
|
275
|
+
const result = execute(this);
|
|
276
|
+
this.#commit();
|
|
277
|
+
return result;
|
|
278
|
+
}
|
|
279
|
+
catch (error) {
|
|
280
|
+
this.#rollback();
|
|
281
|
+
throw error;
|
|
282
|
+
}
|
|
283
|
+
finally {
|
|
284
|
+
this.#db.pragma(`query_only = ${previous === 1 ? "ON" : "OFF"}`);
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
/** Diagnostic worker write; deliberately outside Task revision/outbox state. */
|
|
288
|
+
flushTelemetry(entries, runCap) {
|
|
289
|
+
this.#prepareWrite();
|
|
290
|
+
writeTelemetryBatch(this.#db, entries, runCap);
|
|
291
|
+
}
|
|
225
292
|
#begin() {
|
|
226
293
|
this.#db.exec("BEGIN IMMEDIATE");
|
|
227
294
|
this.#inTransaction = true;
|
|
@@ -535,22 +602,7 @@ export class SqliteTaskStore {
|
|
|
535
602
|
}
|
|
536
603
|
#validateTaskPayload(table, payload) {
|
|
537
604
|
const record = this.#parse(payload);
|
|
538
|
-
|
|
539
|
-
validateTask(record);
|
|
540
|
-
if (table === "work_items")
|
|
541
|
-
validateWorkItem(record);
|
|
542
|
-
// Agents and Roles carry the execution component, and their validators
|
|
543
|
-
// refuse a record that lost it. Reading them unchecked defeated that: a
|
|
544
|
-
// component-less row reached display and rendered `undefined` as if it
|
|
545
|
-
// were a product, and reached launch as a binding nothing had vetted.
|
|
546
|
-
if (table === "configured_agents")
|
|
547
|
-
validateConfiguredAgent(record);
|
|
548
|
-
if (table === "task_roles")
|
|
549
|
-
validateTaskRole(record);
|
|
550
|
-
if (table === "global_roles")
|
|
551
|
-
validateGlobalRole(record);
|
|
552
|
-
if (table === "global_role_messages")
|
|
553
|
-
validateGlobalRoleMessage(record);
|
|
605
|
+
validateStoredRecord(table, record);
|
|
554
606
|
return record;
|
|
555
607
|
}
|
|
556
608
|
#sortById(rows, idOf) {
|
|
@@ -582,12 +634,12 @@ export class SqliteTaskStore {
|
|
|
582
634
|
saveConfiguredAgent(agent) {
|
|
583
635
|
this.#mutate(() => {
|
|
584
636
|
this.#db.prepare(`INSERT INTO configured_agents (id, payload, updated_at) VALUES (?, ?, ?)
|
|
585
|
-
ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(agent.id, this.#
|
|
637
|
+
ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(agent.id, this.#recordJson("configured_agents", agent), this.#now());
|
|
586
638
|
});
|
|
587
639
|
}
|
|
588
640
|
createConfiguredAgentIfAbsent(agent) {
|
|
589
641
|
return this.#mutate(() => {
|
|
590
|
-
const result = this.#db.prepare("INSERT OR IGNORE INTO configured_agents (id, payload, updated_at) VALUES (?, ?, ?)").run(agent.id, this.#
|
|
642
|
+
const result = this.#db.prepare("INSERT OR IGNORE INTO configured_agents (id, payload, updated_at) VALUES (?, ?, ?)").run(agent.id, this.#recordJson("configured_agents", agent), this.#now());
|
|
591
643
|
return result.changes > 0 ? agent : null;
|
|
592
644
|
});
|
|
593
645
|
}
|
|
@@ -627,7 +679,7 @@ export class SqliteTaskStore {
|
|
|
627
679
|
return;
|
|
628
680
|
}
|
|
629
681
|
this.#db.prepare("INSERT INTO plugin_validations (task_id, id, payload) VALUES (?, ?, ?)")
|
|
630
|
-
.run(validation.taskId, validation.id, this.#
|
|
682
|
+
.run(validation.taskId, validation.id, this.#recordJson("plugin_validations", validation));
|
|
631
683
|
});
|
|
632
684
|
}
|
|
633
685
|
savePluginIntent(intent) {
|
|
@@ -644,7 +696,7 @@ export class SqliteTaskStore {
|
|
|
644
696
|
}
|
|
645
697
|
this.#db.prepare(`INSERT INTO plugin_intents (task_id, plugin_id, payload) VALUES (?, ?, ?)
|
|
646
698
|
ON CONFLICT(task_id, plugin_id) DO UPDATE SET payload = excluded.payload`)
|
|
647
|
-
.run(intent.taskId, intent.pluginId, this.#
|
|
699
|
+
.run(intent.taskId, intent.pluginId, this.#recordJson("plugin_intents", intent));
|
|
648
700
|
});
|
|
649
701
|
}
|
|
650
702
|
getPluginIntent(taskId, pluginId) {
|
|
@@ -662,7 +714,7 @@ export class SqliteTaskStore {
|
|
|
662
714
|
return false;
|
|
663
715
|
this.#mutate(() => {
|
|
664
716
|
this.#db.prepare("UPDATE plugin_intents SET payload = ? WHERE task_id = ? AND plugin_id = ?")
|
|
665
|
-
.run(this.#
|
|
717
|
+
.run(this.#recordJson("plugin_intents", { ...current, lastFailure: failure }), taskId, pluginId);
|
|
666
718
|
});
|
|
667
719
|
return true;
|
|
668
720
|
});
|
|
@@ -681,7 +733,7 @@ export class SqliteTaskStore {
|
|
|
681
733
|
return;
|
|
682
734
|
}
|
|
683
735
|
this.#db.prepare("INSERT INTO local_resources (id, canonical_identity, payload) VALUES (?, ?, ?)")
|
|
684
|
-
.run(resource.id, `${resource.device}:${resource.inode}`, this.#
|
|
736
|
+
.run(resource.id, `${resource.device}:${resource.inode}`, this.#recordJson("local_resources", resource));
|
|
685
737
|
});
|
|
686
738
|
}
|
|
687
739
|
getLocalResource(resourceId) {
|
|
@@ -709,7 +761,7 @@ export class SqliteTaskStore {
|
|
|
709
761
|
}
|
|
710
762
|
this.#db.prepare(`INSERT INTO environment_preparations (task_id, id, payload) VALUES (?, ?, ?)
|
|
711
763
|
ON CONFLICT(task_id, id) DO UPDATE SET payload = excluded.payload`)
|
|
712
|
-
.run(preparation.taskId, preparation.id, this.#
|
|
764
|
+
.run(preparation.taskId, preparation.id, this.#recordJson("environment_preparations", preparation));
|
|
713
765
|
});
|
|
714
766
|
}
|
|
715
767
|
getEnvironmentPreparation(taskId, preparationId) {
|
|
@@ -725,13 +777,13 @@ export class SqliteTaskStore {
|
|
|
725
777
|
validateProject(project);
|
|
726
778
|
this.#mutate(() => {
|
|
727
779
|
this.#db.prepare(`INSERT INTO projects (id, name, path, payload, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)
|
|
728
|
-
ON CONFLICT(id) DO UPDATE SET name = excluded.name, path = excluded.path, payload = excluded.payload, updated_at = excluded.updated_at`).run(project.id, project.name, project.path, this.#
|
|
780
|
+
ON CONFLICT(id) DO UPDATE SET name = excluded.name, path = excluded.path, payload = excluded.payload, updated_at = excluded.updated_at`).run(project.id, project.name, project.path, this.#recordJson("projects", project), project.createdAt, project.updatedAt);
|
|
729
781
|
});
|
|
730
782
|
}
|
|
731
783
|
createProjectIfAbsent(project) {
|
|
732
784
|
validateProject(project);
|
|
733
785
|
return this.#mutate(() => {
|
|
734
|
-
const result = this.#db.prepare("INSERT OR IGNORE INTO projects (id, name, path, payload, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(project.id, project.name, project.path, this.#
|
|
786
|
+
const result = this.#db.prepare("INSERT OR IGNORE INTO projects (id, name, path, payload, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)").run(project.id, project.name, project.path, this.#recordJson("projects", project), project.createdAt, project.updatedAt);
|
|
735
787
|
return result.changes > 0 ? project : null;
|
|
736
788
|
});
|
|
737
789
|
}
|
|
@@ -785,12 +837,12 @@ export class SqliteTaskStore {
|
|
|
785
837
|
saveAgentProfile(profile) {
|
|
786
838
|
this.#mutate(() => {
|
|
787
839
|
this.#db.prepare(`INSERT INTO agent_profiles (id, payload, updated_at) VALUES (?, ?, ?)
|
|
788
|
-
ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(profile.id, this.#
|
|
840
|
+
ON CONFLICT(id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(profile.id, this.#recordJson("agent_profiles", profile), this.#now());
|
|
789
841
|
});
|
|
790
842
|
}
|
|
791
843
|
createAgentProfileIfAbsent(profile) {
|
|
792
844
|
return this.#mutate(() => {
|
|
793
|
-
const result = this.#db.prepare("INSERT OR IGNORE INTO agent_profiles (id, payload, updated_at) VALUES (?, ?, ?)").run(profile.id, this.#
|
|
845
|
+
const result = this.#db.prepare("INSERT OR IGNORE INTO agent_profiles (id, payload, updated_at) VALUES (?, ?, ?)").run(profile.id, this.#recordJson("agent_profiles", profile), this.#now());
|
|
794
846
|
return result.changes > 0 ? profile : null;
|
|
795
847
|
});
|
|
796
848
|
}
|
|
@@ -807,16 +859,16 @@ export class SqliteTaskStore {
|
|
|
807
859
|
saveGlobalRole(role) {
|
|
808
860
|
this.#mutate(() => {
|
|
809
861
|
this.#db.prepare(`INSERT INTO global_roles (name, payload, updated_at) VALUES (?, ?, ?)
|
|
810
|
-
ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(role.name, this.#
|
|
862
|
+
ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(role.name, this.#recordJson("global_roles", role), this.#now());
|
|
811
863
|
});
|
|
812
864
|
}
|
|
813
865
|
saveGlobalRoleWithSessionSet(role, sessions) {
|
|
814
866
|
this.#mutate(() => {
|
|
815
867
|
this.#db.prepare(`INSERT INTO global_roles (name, payload, updated_at) VALUES (?, ?, ?)
|
|
816
|
-
ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(role.name, this.#
|
|
868
|
+
ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(role.name, this.#recordJson("global_roles", role), this.#now());
|
|
817
869
|
if (sessions !== null) {
|
|
818
870
|
this.#db.prepare(`INSERT INTO global_role_session_sets (name, payload, updated_at) VALUES (?, ?, ?)
|
|
819
|
-
ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(role.name, this.#
|
|
871
|
+
ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(role.name, this.#recordJson("global_role_session_sets", sessions), this.#now());
|
|
820
872
|
this.#saveRuntimeSessionCandidate(sessions);
|
|
821
873
|
}
|
|
822
874
|
else {
|
|
@@ -827,7 +879,7 @@ export class SqliteTaskStore {
|
|
|
827
879
|
}
|
|
828
880
|
createGlobalRoleIfAbsent(role) {
|
|
829
881
|
return this.#mutate(() => {
|
|
830
|
-
const result = this.#db.prepare("INSERT OR IGNORE INTO global_roles (name, payload, updated_at) VALUES (?, ?, ?)").run(role.name, this.#
|
|
882
|
+
const result = this.#db.prepare("INSERT OR IGNORE INTO global_roles (name, payload, updated_at) VALUES (?, ?, ?)").run(role.name, this.#recordJson("global_roles", role), this.#now());
|
|
831
883
|
return result.changes > 0 ? role : null;
|
|
832
884
|
});
|
|
833
885
|
}
|
|
@@ -854,7 +906,7 @@ export class SqliteTaskStore {
|
|
|
854
906
|
saveGlobalRoleSessionSet(sessions) {
|
|
855
907
|
this.#mutate(() => {
|
|
856
908
|
this.#db.prepare(`INSERT INTO global_role_session_sets (name, payload, updated_at) VALUES (?, ?, ?)
|
|
857
|
-
ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(sessions.owner.roleName, this.#
|
|
909
|
+
ON CONFLICT(name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(sessions.owner.roleName, this.#recordJson("global_role_session_sets", sessions), this.#now());
|
|
858
910
|
this.#saveRuntimeSessionCandidate(sessions);
|
|
859
911
|
});
|
|
860
912
|
}
|
|
@@ -865,7 +917,7 @@ export class SqliteTaskStore {
|
|
|
865
917
|
this.#requireGlobalRole(message.roleName);
|
|
866
918
|
this.#mutate(() => {
|
|
867
919
|
const seq = this.#globalMessageSequence(message.id);
|
|
868
|
-
this.#db.prepare(`INSERT INTO global_role_messages (name, message_id, seq, payload, created_at) VALUES (?, ?, ?, ?, ?)`).run(message.roleName, message.id, seq, this.#
|
|
920
|
+
this.#db.prepare(`INSERT INTO global_role_messages (name, message_id, seq, payload, created_at) VALUES (?, ?, ?, ?, ?)`).run(message.roleName, message.id, seq, this.#recordJson("global_role_messages", message), message.createdAt);
|
|
869
921
|
});
|
|
870
922
|
}
|
|
871
923
|
listGlobalRoleMessages(roleName) {
|
|
@@ -876,7 +928,7 @@ export class SqliteTaskStore {
|
|
|
876
928
|
this.#requireGlobalRole(message.roleName);
|
|
877
929
|
this.#mutate(() => {
|
|
878
930
|
const seq = this.#globalMessageSequence(message.id);
|
|
879
|
-
const result = this.#db.prepare(`UPDATE global_role_messages SET seq = ?, payload = ? WHERE name = ? AND message_id = ?`).run(seq, this.#
|
|
931
|
+
const result = this.#db.prepare(`UPDATE global_role_messages SET seq = ?, payload = ? WHERE name = ? AND message_id = ?`).run(seq, this.#recordJson("global_role_messages", message), message.roleName, message.id);
|
|
880
932
|
if (result.changes === 0) {
|
|
881
933
|
throw new StorageRecordError(`Global message not found: ${message.roleName}/${message.id}`);
|
|
882
934
|
}
|
|
@@ -914,13 +966,22 @@ export class SqliteTaskStore {
|
|
|
914
966
|
ON CONFLICT(task_id) DO UPDATE SET status = excluded.status, lifecycle = excluded.lifecycle,
|
|
915
967
|
is_active = excluded.is_active, updated_at = excluded.updated_at`).run(task.id, task.status, task.status, isActive, task.createdAt, task.updatedAt);
|
|
916
968
|
this.#db.prepare(`INSERT INTO task_records (task_id, payload, updated_at) VALUES (?, ?, ?)
|
|
917
|
-
ON CONFLICT(task_id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(task.id, this.#
|
|
969
|
+
ON CONFLICT(task_id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(task.id, this.#recordJson("task_records", task), this.#now());
|
|
918
970
|
});
|
|
919
971
|
}
|
|
920
972
|
listTasks() {
|
|
921
973
|
const tasks = this.#listPayload("task_records", "1=1", []);
|
|
922
974
|
return this.#sortById(tasks, (task) => task.id);
|
|
923
975
|
}
|
|
976
|
+
queryTaskCatalog(query) {
|
|
977
|
+
return queryTaskCatalog(this.#db, query);
|
|
978
|
+
}
|
|
979
|
+
listTaskChoices() {
|
|
980
|
+
const rows = this.#db.prepare(`SELECT c.task_id AS id, c.status,
|
|
981
|
+
json_extract(r.payload, '$.title') AS title
|
|
982
|
+
FROM tasks_catalog c JOIN task_records r ON r.task_id = c.task_id`).all();
|
|
983
|
+
return this.#sortById(rows, task => task.id);
|
|
984
|
+
}
|
|
924
985
|
getTask(id) {
|
|
925
986
|
return this.#getPayload("task_records", "task_id = ?", [id]);
|
|
926
987
|
}
|
|
@@ -950,7 +1011,6 @@ export class SqliteTaskStore {
|
|
|
950
1011
|
integrationJobs: this.listDurableJobs(taskId)
|
|
951
1012
|
.filter(job => job.owner.kind === "integration-attempt")
|
|
952
1013
|
.map(job => ({ id: job.id, status: job.status })),
|
|
953
|
-
integrationQueueEntries: this.#sortById(this.#listPayload("integration_queue", "task_id = ?", [taskId]), (entry) => entry.id),
|
|
954
1014
|
reviewRounds: this.#sortById(this.#listPayload("review_rounds", "task_id = ?", [taskId]), (round) => round.id),
|
|
955
1015
|
reviewConfig: this.getReviewConfig(),
|
|
956
1016
|
openInputRequests: this.#sortById(this.#listPayload("input_requests", "task_id = ? AND status = 'open'", [taskId]), (request) => request.id),
|
|
@@ -970,7 +1030,6 @@ export class SqliteTaskStore {
|
|
|
970
1030
|
pendingUserMessages: pendingCompletionMessages(this, taskId),
|
|
971
1031
|
managedWorkspaces: this.#sortById(this.#listPayload("managed_workspaces", "task_id = ?", [taskId]), (workspace) => managedWorkspaceKey(workspace.owner)),
|
|
972
1032
|
durableJobs: this.#sortById(this.#listPayload("durable_jobs", "task_id = ?", [taskId]), (job) => job.id),
|
|
973
|
-
integrationQueueEntries: this.#sortById(this.#listPayload("integration_queue", "task_id = ?", [taskId]), (entry) => entry.id),
|
|
974
1033
|
};
|
|
975
1034
|
}
|
|
976
1035
|
/** Task ids flagged active in the catalog projection (the global active index). */
|
|
@@ -1035,7 +1094,7 @@ export class SqliteTaskStore {
|
|
|
1035
1094
|
this.#db.prepare(`INSERT INTO change_sets (task_id, change_set_id, project_id, head_sha, payload, created_at)
|
|
1036
1095
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
1037
1096
|
ON CONFLICT(task_id, change_set_id) DO UPDATE SET project_id = excluded.project_id,
|
|
1038
|
-
head_sha = excluded.head_sha, payload = excluded.payload`).run(taskId, changeSet.id, changeSet.projectId, changeSet.headCommit, this.#
|
|
1097
|
+
head_sha = excluded.head_sha, payload = excluded.payload`).run(taskId, changeSet.id, changeSet.projectId, changeSet.headCommit, this.#recordJson("change_sets", changeSet), changeSet.createdAt);
|
|
1039
1098
|
});
|
|
1040
1099
|
}
|
|
1041
1100
|
listChangeSets(taskId) {
|
|
@@ -1051,10 +1110,16 @@ export class SqliteTaskStore {
|
|
|
1051
1110
|
throw new StorageRecordError(`Integration attempt belongs to another Task: ${attempt.taskId}`);
|
|
1052
1111
|
this.#requireTask(taskId);
|
|
1053
1112
|
this.#mutate(() => {
|
|
1113
|
+
if (typeof attempt.rerunChecks !== "boolean")
|
|
1114
|
+
throw new StorageRecordError("Integration rerunChecks must be explicit.");
|
|
1115
|
+
const existing = this.getIntegrationAttempt(taskId, attempt.id);
|
|
1116
|
+
if (existing !== null && existing.rerunChecks !== attempt.rerunChecks) {
|
|
1117
|
+
throw new StorageRecordError("Integration check execution intent is immutable; create a new attempt.");
|
|
1118
|
+
}
|
|
1054
1119
|
this.#db.prepare(`INSERT INTO integration_attempts (task_id, integration_id, status, payload, updated_at)
|
|
1055
1120
|
VALUES (?, ?, ?, ?, ?)
|
|
1056
1121
|
ON CONFLICT(task_id, integration_id) DO UPDATE SET status = excluded.status,
|
|
1057
|
-
payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, attempt.id, attempt.status, this.#
|
|
1122
|
+
payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, attempt.id, attempt.status, this.#recordJson("integration_attempts", attempt), this.#now());
|
|
1058
1123
|
});
|
|
1059
1124
|
}
|
|
1060
1125
|
listIntegrationAttempts(taskId) {
|
|
@@ -1063,46 +1128,6 @@ export class SqliteTaskStore {
|
|
|
1063
1128
|
getIntegrationAttempt(taskId, integrationId) {
|
|
1064
1129
|
return this.#getPayload("integration_attempts", "task_id = ? AND integration_id = ?", [taskId, integrationId]);
|
|
1065
1130
|
}
|
|
1066
|
-
// -- integration queue -----------------------------------------------------
|
|
1067
|
-
nextIntegrationQueueEntryId(taskId) {
|
|
1068
|
-
return this.#nextTaskRecordId(taskId, "integrationQueue");
|
|
1069
|
-
}
|
|
1070
|
-
saveIntegrationQueueEntry(taskId, entry) {
|
|
1071
|
-
if (entry.taskId !== taskId) {
|
|
1072
|
-
throw new StorageRecordError(`Integration queue entry belongs to another Task: ${entry.taskId}`);
|
|
1073
|
-
}
|
|
1074
|
-
validateIntegrationQueueEntry(entry);
|
|
1075
|
-
this.#requireTask(taskId);
|
|
1076
|
-
const changeSet = this.getChangeSet(taskId, entry.changeSetId);
|
|
1077
|
-
if (changeSet === null) {
|
|
1078
|
-
throw new StorageRecordError(`Integration queue ChangeSet not found: ${entry.changeSetId}`);
|
|
1079
|
-
}
|
|
1080
|
-
if (changeSet.projectId !== entry.projectId) {
|
|
1081
|
-
throw new StorageRecordError(`Integration queue ChangeSet belongs to another Project: ${entry.changeSetId}`);
|
|
1082
|
-
}
|
|
1083
|
-
const existing = this.getIntegrationQueueEntry(taskId, entry.id);
|
|
1084
|
-
if (existing !== null) {
|
|
1085
|
-
if (Date.parse(entry.updatedAt) < Date.parse(existing.updatedAt)) {
|
|
1086
|
-
throw new StorageRecordError(`Integration queue entry updatedAt cannot move backwards: ${entry.id}`);
|
|
1087
|
-
}
|
|
1088
|
-
if (!validIntegrationQueueTransition(existing, entry)) {
|
|
1089
|
-
throw new StorageRecordError(`Integration queue entry transition is invalid: ${entry.id}`);
|
|
1090
|
-
}
|
|
1091
|
-
}
|
|
1092
|
-
this.#mutate(() => {
|
|
1093
|
-
this.#db.prepare(`INSERT INTO integration_queue (queue_id, task_id, project_id, change_set, status, payload, created_at, updated_at)
|
|
1094
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1095
|
-
ON CONFLICT(queue_id) DO UPDATE SET task_id = excluded.task_id,
|
|
1096
|
-
project_id = excluded.project_id, change_set = excluded.change_set,
|
|
1097
|
-
status = excluded.status, payload = excluded.payload, updated_at = excluded.updated_at`).run(entry.id, taskId, entry.projectId, entry.changeSetId, entry.status, this.#json(entry), entry.createdAt, entry.updatedAt);
|
|
1098
|
-
});
|
|
1099
|
-
}
|
|
1100
|
-
listIntegrationQueueEntries(taskId) {
|
|
1101
|
-
return this.#sortById(this.#listPayload("integration_queue", "task_id = ?", [taskId]), (entry) => entry.id);
|
|
1102
|
-
}
|
|
1103
|
-
getIntegrationQueueEntry(taskId, entryId) {
|
|
1104
|
-
return this.#getPayload("integration_queue", "task_id = ? AND queue_id = ?", [taskId, entryId]);
|
|
1105
|
-
}
|
|
1106
1131
|
// -- durable jobs -----------------------------------------------------------
|
|
1107
1132
|
nextDurableJobId(taskId) {
|
|
1108
1133
|
return this.#nextTaskRecordId(taskId, "durableJob");
|
|
@@ -1127,7 +1152,7 @@ export class SqliteTaskStore {
|
|
|
1127
1152
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1128
1153
|
ON CONFLICT(task_id, job_id) DO UPDATE SET idempotency_key = excluded.idempotency_key,
|
|
1129
1154
|
status = excluded.status,
|
|
1130
|
-
payload = excluded.payload, updated_at = excluded.updated_at`).run(job.id, taskId, job.idempotencyKey ?? null, job.status, this.#
|
|
1155
|
+
payload = excluded.payload, updated_at = excluded.updated_at`).run(job.id, taskId, job.idempotencyKey ?? null, job.status, this.#recordJson("durable_jobs", job), job.createdAt, job.updatedAt);
|
|
1131
1156
|
});
|
|
1132
1157
|
}
|
|
1133
1158
|
listDurableJobs(taskId) {
|
|
@@ -1159,12 +1184,12 @@ export class SqliteTaskStore {
|
|
|
1159
1184
|
role_name = excluded.role_name, agent_id = excluded.agent_id,
|
|
1160
1185
|
native_session_id = excluded.native_session_id,
|
|
1161
1186
|
provider_root_pid = excluded.provider_root_pid,
|
|
1162
|
-
payload = excluded.payload, recorded_at = excluded.recorded_at`).run(sessionOwnerProcessKey(identity), owner.scope, owner.scope === "task" ? owner.taskId : null, owner.roleName, identity.agentId, identity.nativeSessionId ?? null, identity.providerRoot.pid, this.#
|
|
1187
|
+
payload = excluded.payload, recorded_at = excluded.recorded_at`).run(sessionOwnerProcessKey(identity), owner.scope, owner.scope === "task" ? owner.taskId : null, owner.roleName, identity.agentId, identity.nativeSessionId ?? null, identity.providerRoot.pid, this.#recordJson("session_owners", identity), identity.recordedAt);
|
|
1163
1188
|
});
|
|
1164
1189
|
}
|
|
1165
1190
|
getSessionOwner(processKey) {
|
|
1166
1191
|
const row = this.#db.prepare("SELECT payload FROM session_owners WHERE process_key = ?").get(processKey);
|
|
1167
|
-
return row === undefined ? null : this.#
|
|
1192
|
+
return row === undefined ? null : this.#validateTaskPayload("session_owners", row.payload);
|
|
1168
1193
|
}
|
|
1169
1194
|
listSessionOwners() {
|
|
1170
1195
|
return this.#listPayload("session_owners", "1=1", []).sort((left, right) => left.recordedAt.localeCompare(right.recordedAt));
|
|
@@ -1185,7 +1210,7 @@ export class SqliteTaskStore {
|
|
|
1185
1210
|
this.#requireTask(taskId);
|
|
1186
1211
|
this.#mutate(() => {
|
|
1187
1212
|
this.#db.prepare(`INSERT INTO task_roles (task_id, role_name, payload, updated_at) VALUES (?, ?, ?, ?)
|
|
1188
|
-
ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, role.name, this.#
|
|
1213
|
+
ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, role.name, this.#recordJson("task_roles", role), this.#now());
|
|
1189
1214
|
});
|
|
1190
1215
|
}
|
|
1191
1216
|
listRoles(taskId) {
|
|
@@ -1198,9 +1223,9 @@ export class SqliteTaskStore {
|
|
|
1198
1223
|
this.#requireTask(role.name ? role.taskId : sessions.owner.taskId);
|
|
1199
1224
|
this.#mutate(() => {
|
|
1200
1225
|
this.#db.prepare(`INSERT INTO task_roles (task_id, role_name, payload, updated_at) VALUES (?, ?, ?, ?)
|
|
1201
|
-
ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(sessions.owner.taskId, role.name, this.#
|
|
1226
|
+
ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(sessions.owner.taskId, role.name, this.#recordJson("task_roles", role), this.#now());
|
|
1202
1227
|
this.#db.prepare(`INSERT INTO role_session_sets (task_id, role_name, payload, updated_at) VALUES (?, ?, ?, ?)
|
|
1203
|
-
ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(sessions.owner.taskId, sessions.owner.roleName, this.#
|
|
1228
|
+
ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(sessions.owner.taskId, sessions.owner.roleName, this.#recordJson("role_session_sets", sessions), this.#now());
|
|
1204
1229
|
this.#saveRuntimeSessionCandidate(sessions);
|
|
1205
1230
|
});
|
|
1206
1231
|
}
|
|
@@ -1226,7 +1251,7 @@ export class SqliteTaskStore {
|
|
|
1226
1251
|
this.#db.prepare(`INSERT INTO managed_workspaces (owner_kind, owner_id, task_id, path, payload, status, created_at, updated_at)
|
|
1227
1252
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1228
1253
|
ON CONFLICT(owner_kind, owner_id) DO UPDATE SET task_id = excluded.task_id, path = excluded.path,
|
|
1229
|
-
payload = excluded.payload, status = excluded.status, updated_at = excluded.updated_at`).run(workspace.owner.type, ownerId, taskId, workspace.root, this.#
|
|
1254
|
+
payload = excluded.payload, status = excluded.status, updated_at = excluded.updated_at`).run(workspace.owner.type, ownerId, taskId, workspace.root, this.#recordJson("managed_workspaces", workspace), "active", workspace.createdAt, workspace.updatedAt);
|
|
1230
1255
|
});
|
|
1231
1256
|
}
|
|
1232
1257
|
listManagedWorkspaces(taskId) {
|
|
@@ -1267,6 +1292,13 @@ export class SqliteTaskStore {
|
|
|
1267
1292
|
listRoleSessionSets(taskId) {
|
|
1268
1293
|
return this.#sortById(this.#listPayload("role_session_sets", "task_id = ?", [taskId]), (set) => set.owner.roleName);
|
|
1269
1294
|
}
|
|
1295
|
+
listProviderRetrySessions() {
|
|
1296
|
+
const pending = "json_extract(payload, '$.providerBinding.retry.status') IN ('waiting', 'in-flight')";
|
|
1297
|
+
return [
|
|
1298
|
+
...this.#listPayload("role_session_sets", pending, []),
|
|
1299
|
+
...this.#listPayload("global_role_session_sets", pending, [])
|
|
1300
|
+
];
|
|
1301
|
+
}
|
|
1270
1302
|
listRuntimeSessionCandidates(query = {}) {
|
|
1271
1303
|
const taskIds = query.taskIds === undefined
|
|
1272
1304
|
? undefined
|
|
@@ -1373,7 +1405,7 @@ export class SqliteTaskStore {
|
|
|
1373
1405
|
this.#requireTask(taskId);
|
|
1374
1406
|
this.#mutate(() => {
|
|
1375
1407
|
this.#db.prepare(`INSERT INTO role_session_sets (task_id, role_name, payload, updated_at) VALUES (?, ?, ?, ?)
|
|
1376
|
-
ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, sessions.owner.roleName, this.#
|
|
1408
|
+
ON CONFLICT(task_id, role_name) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, sessions.owner.roleName, this.#recordJson("role_session_sets", sessions), this.#now());
|
|
1377
1409
|
this.#saveRuntimeSessionCandidate(sessions);
|
|
1378
1410
|
});
|
|
1379
1411
|
}
|
|
@@ -1424,7 +1456,7 @@ export class SqliteTaskStore {
|
|
|
1424
1456
|
this.#mutate(() => {
|
|
1425
1457
|
this.#db.prepare(`INSERT INTO work_items (task_id, work_item_id, status, payload, updated_at) VALUES (?, ?, ?, ?, ?)
|
|
1426
1458
|
ON CONFLICT(task_id, work_item_id) DO UPDATE SET status = excluded.status,
|
|
1427
|
-
payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, item.id, item.status, this.#
|
|
1459
|
+
payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, item.id, item.status, this.#recordJson("work_items", item), this.#now());
|
|
1428
1460
|
});
|
|
1429
1461
|
}
|
|
1430
1462
|
// -- capability grants ------------------------------------------------------
|
|
@@ -1449,7 +1481,7 @@ export class SqliteTaskStore {
|
|
|
1449
1481
|
throw new StorageRecordError(`Capability grant cannot be overwritten: ${taskId}/${stored.id}`);
|
|
1450
1482
|
}
|
|
1451
1483
|
this.#db.prepare(`INSERT INTO capability_grants (task_id, grant_id, payload, updated_at) VALUES (?, ?, ?, ?)
|
|
1452
|
-
ON CONFLICT(task_id, grant_id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, stored.id, this.#
|
|
1484
|
+
ON CONFLICT(task_id, grant_id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, stored.id, this.#recordJson("capability_grants", stored), this.#now());
|
|
1453
1485
|
});
|
|
1454
1486
|
}
|
|
1455
1487
|
listCapabilityGrants(taskId) {
|
|
@@ -1472,7 +1504,7 @@ export class SqliteTaskStore {
|
|
|
1472
1504
|
throw new StorageRecordError(`Release workflow cannot be overwritten: ${taskId}/${stored.id}`);
|
|
1473
1505
|
}
|
|
1474
1506
|
this.#db.prepare(`INSERT INTO release_workflows (task_id, workflow_id, payload, updated_at) VALUES (?, ?, ?, ?)
|
|
1475
|
-
ON CONFLICT(task_id, workflow_id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, stored.id, this.#
|
|
1507
|
+
ON CONFLICT(task_id, workflow_id) DO UPDATE SET payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, stored.id, this.#recordJson("release_workflows", stored), this.#now());
|
|
1476
1508
|
});
|
|
1477
1509
|
}
|
|
1478
1510
|
listReleaseWorkflows(taskId) {
|
|
@@ -1519,7 +1551,7 @@ export class SqliteTaskStore {
|
|
|
1519
1551
|
remote_commit, supersedes, payload, merged_at, created_at,
|
|
1520
1552
|
title, source_branch, target_branch)
|
|
1521
1553
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1522
|
-
ON CONFLICT(task_id, publication_id) DO UPDATE SET payload = excluded.payload`).run(taskId, stored.id, stored.projectId, stored.provider, stored.repository, stored.externalKind, stored.externalId, key, stored.state, stored.verification, stored.externalUrl ?? null, stored.localCommit ?? null, stored.remoteCommit ?? null, stored.supersedes ?? null, this.#
|
|
1554
|
+
ON CONFLICT(task_id, publication_id) DO UPDATE SET payload = excluded.payload`).run(taskId, stored.id, stored.projectId, stored.provider, stored.repository, stored.externalKind, stored.externalId, key, stored.state, stored.verification, stored.externalUrl ?? null, stored.localCommit ?? null, stored.remoteCommit ?? null, stored.supersedes ?? null, this.#recordJson("publication_references", stored), stored.mergedAt ?? null, this.#now(), stored.title ?? null, stored.sourceBranch ?? null, stored.targetBranch ?? null);
|
|
1523
1555
|
});
|
|
1524
1556
|
}
|
|
1525
1557
|
listPublicationReferences(taskId) {
|
|
@@ -1536,7 +1568,7 @@ export class SqliteTaskStore {
|
|
|
1536
1568
|
saveGateArtifact(artifact, logs) {
|
|
1537
1569
|
validateGateArtifact(artifact);
|
|
1538
1570
|
this.#mutate(() => {
|
|
1539
|
-
const targetRef = artifact.boundary
|
|
1571
|
+
const targetRef = artifact.boundary.targetRef;
|
|
1540
1572
|
const completedAt = artifact.completedAt ?? null;
|
|
1541
1573
|
this.#db.prepare(`INSERT INTO gate_artifacts
|
|
1542
1574
|
(key, project_id, level, commit_sha, plan_digest, toolchain_digest,
|
|
@@ -1552,8 +1584,9 @@ export class SqliteTaskStore {
|
|
|
1552
1584
|
status = excluded.status,
|
|
1553
1585
|
outcome = excluded.outcome,
|
|
1554
1586
|
payload = excluded.payload,
|
|
1587
|
+
created_at = excluded.created_at,
|
|
1555
1588
|
completed_at = excluded.completed_at,
|
|
1556
|
-
last_used_at = excluded.last_used_at`).run(artifact.key, artifact.projectId, artifact.level, artifact.commit, artifact.planDigest, artifact.toolchainDigest, targetRef, artifact.status, artifact.outcome, this.#
|
|
1589
|
+
last_used_at = excluded.last_used_at`).run(artifact.key, artifact.projectId, artifact.level, artifact.commit, artifact.planDigest, artifact.toolchainDigest, targetRef, artifact.status, artifact.outcome, this.#recordJson("gate_artifacts", artifact), artifact.createdAt, completedAt, artifact.lastUsedAt);
|
|
1557
1590
|
// Replace all logs for this artifact in the same transaction.
|
|
1558
1591
|
this.#db.prepare("DELETE FROM gate_artifact_logs WHERE artifact_key = ?").run(artifact.key);
|
|
1559
1592
|
const insertLog = this.#db.prepare(`INSERT INTO gate_artifact_logs (artifact_key, step_name, log_content, log_digest, log_bytes)
|
|
@@ -1570,7 +1603,15 @@ export class SqliteTaskStore {
|
|
|
1570
1603
|
touchGateArtifact(artifact) {
|
|
1571
1604
|
validateGateArtifact(artifact);
|
|
1572
1605
|
this.#mutate(() => {
|
|
1573
|
-
const
|
|
1606
|
+
const current = this.getGateArtifact(artifact.projectId, artifact.key);
|
|
1607
|
+
if (current === null || !isDeepStrictEqual(current, {
|
|
1608
|
+
...artifact, reuseCount: current.reuseCount, lastUsedAt: current.lastUsedAt
|
|
1609
|
+
})) {
|
|
1610
|
+
throw new StorageConflictError("Gate artifact changed while consuming evidence; retry the exact lookup.");
|
|
1611
|
+
}
|
|
1612
|
+
const updated = { ...current, reuseCount: Math.max(current.reuseCount, artifact.reuseCount),
|
|
1613
|
+
lastUsedAt: Date.parse(artifact.lastUsedAt) > Date.parse(current.lastUsedAt) ? artifact.lastUsedAt : current.lastUsedAt };
|
|
1614
|
+
const result = this.#db.prepare(`UPDATE gate_artifacts SET payload = ?, last_used_at = ? WHERE key = ?`).run(this.#recordJson("gate_artifacts", updated), updated.lastUsedAt, artifact.key);
|
|
1574
1615
|
if (result.changes === 0) {
|
|
1575
1616
|
throw new StorageRecordError(`Gate artifact not found for touch: ${artifact.key}`);
|
|
1576
1617
|
}
|
|
@@ -1585,18 +1626,8 @@ export class SqliteTaskStore {
|
|
|
1585
1626
|
}
|
|
1586
1627
|
findL2GateArtifactsForCommit(query) {
|
|
1587
1628
|
const rows = this.#listPayload("gate_artifacts", `project_id = ? AND commit_sha = ? AND level = 'L2'
|
|
1588
|
-
AND plan_digest = ? AND toolchain_digest = ? AND target_ref =
|
|
1589
|
-
|
|
1590
|
-
const valid = [];
|
|
1591
|
-
for (const row of rows) {
|
|
1592
|
-
try {
|
|
1593
|
-
valid.push(validateGateArtifact(row));
|
|
1594
|
-
}
|
|
1595
|
-
catch {
|
|
1596
|
-
// Skip corrupt rows; a direct lookup fails closed.
|
|
1597
|
-
}
|
|
1598
|
-
}
|
|
1599
|
-
return valid;
|
|
1629
|
+
AND plan_digest = ? AND toolchain_digest = ? AND target_ref = ?`, [query.projectId, query.commit, query.planDigest, query.toolchainDigest, query.targetRef]);
|
|
1630
|
+
return rows.map(validateGateArtifact);
|
|
1600
1631
|
}
|
|
1601
1632
|
getGateArtifactLogs(artifactKey) {
|
|
1602
1633
|
const rows = this.#db.prepare("SELECT step_name, log_content FROM gate_artifact_logs WHERE artifact_key = ?").all(artifactKey);
|
|
@@ -1667,7 +1698,7 @@ export class SqliteTaskStore {
|
|
|
1667
1698
|
this.#mutate(() => {
|
|
1668
1699
|
this.#db.prepare(`INSERT INTO context_snapshots
|
|
1669
1700
|
(task_id, snapshot_id, scope, scope_ref, sequence, digest, payload, frozen_at)
|
|
1670
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(stored.taskId, stored.id, stored.scope, stored.scopeRef ?? null, stored.sequence, stored.digest, this.#
|
|
1701
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`).run(stored.taskId, stored.id, stored.scope, stored.scopeRef ?? null, stored.sequence, stored.digest, this.#recordJson("context_snapshots", stored), stored.frozenAt);
|
|
1671
1702
|
});
|
|
1672
1703
|
}
|
|
1673
1704
|
// -- turns -----------------------------------------------------------------
|
|
@@ -1698,7 +1729,7 @@ export class SqliteTaskStore {
|
|
|
1698
1729
|
this.#mutate(() => {
|
|
1699
1730
|
this.#db.prepare(`INSERT INTO turns (task_id, turn_id, role_name, status, payload, updated_at) VALUES (?, ?, ?, ?, ?, ?)
|
|
1700
1731
|
ON CONFLICT(task_id, turn_id) DO UPDATE SET role_name = excluded.role_name, status = excluded.status,
|
|
1701
|
-
payload = excluded.payload, updated_at = excluded.updated_at`).run(run.taskId, run.id, run.roleName, run.status, this.#
|
|
1732
|
+
payload = excluded.payload, updated_at = excluded.updated_at`).run(run.taskId, run.id, run.roleName, run.status, this.#recordJson("turns", run), this.#now());
|
|
1702
1733
|
});
|
|
1703
1734
|
}
|
|
1704
1735
|
// -- review rounds ----------------------------------------------------------
|
|
@@ -1717,7 +1748,7 @@ export class SqliteTaskStore {
|
|
|
1717
1748
|
this.#mutate(() => {
|
|
1718
1749
|
this.#db.prepare(`INSERT INTO review_rounds (task_id, review_round_id, status, payload, updated_at) VALUES (?, ?, ?, ?, ?)
|
|
1719
1750
|
ON CONFLICT(task_id, review_round_id) DO UPDATE SET status = excluded.status,
|
|
1720
|
-
payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, round.id, round.status, this.#
|
|
1751
|
+
payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, round.id, round.status, this.#recordJson("review_rounds", round), this.#now());
|
|
1721
1752
|
});
|
|
1722
1753
|
}
|
|
1723
1754
|
// -- active turns ----------------------------------------------------------
|
|
@@ -1905,7 +1936,7 @@ export class SqliteTaskStore {
|
|
|
1905
1936
|
this.#requireTask(taskId);
|
|
1906
1937
|
this.#mutate(() => {
|
|
1907
1938
|
const seq = this.#idSequence(message.id, "message");
|
|
1908
|
-
this.#db.prepare(`INSERT INTO messages (task_id, message_id, seq, payload, created_at) VALUES (?, ?, ?, ?, ?)`).run(taskId, message.id, seq, this.#
|
|
1939
|
+
this.#db.prepare(`INSERT INTO messages (task_id, message_id, seq, payload, created_at) VALUES (?, ?, ?, ?, ?)`).run(taskId, message.id, seq, this.#recordJson("messages", message), message.createdAt);
|
|
1909
1940
|
this.#observeHighWater(taskId, "message", seq);
|
|
1910
1941
|
});
|
|
1911
1942
|
}
|
|
@@ -1915,7 +1946,7 @@ export class SqliteTaskStore {
|
|
|
1915
1946
|
}
|
|
1916
1947
|
this.#requireTask(taskId);
|
|
1917
1948
|
this.#mutate(() => {
|
|
1918
|
-
const result = this.#db.prepare(`UPDATE messages SET payload = ? WHERE task_id = ? AND message_id = ?`).run(this.#
|
|
1949
|
+
const result = this.#db.prepare(`UPDATE messages SET payload = ? WHERE task_id = ? AND message_id = ?`).run(this.#recordJson("messages", message), taskId, message.id);
|
|
1919
1950
|
if (result.changes !== 1) {
|
|
1920
1951
|
throw new StorageRecordError(`Message does not exist: ${taskId}/${message.id}`);
|
|
1921
1952
|
}
|
|
@@ -1932,7 +1963,7 @@ export class SqliteTaskStore {
|
|
|
1932
1963
|
this.#requireTask(taskId);
|
|
1933
1964
|
this.#mutate(() => {
|
|
1934
1965
|
this.#db.prepare(`INSERT INTO input_requests (task_id, input_id, status, payload, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)
|
|
1935
|
-
ON CONFLICT(task_id, input_id) DO UPDATE SET status = excluded.status, payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, request.id, request.status, this.#
|
|
1966
|
+
ON CONFLICT(task_id, input_id) DO UPDATE SET status = excluded.status, payload = excluded.payload, updated_at = excluded.updated_at`).run(taskId, request.id, request.status, this.#recordJson("input_requests", request), request.createdAt, this.#now());
|
|
1936
1967
|
});
|
|
1937
1968
|
}
|
|
1938
1969
|
getInputRequest(taskId, requestId) {
|
|
@@ -1963,7 +1994,7 @@ export class SqliteTaskStore {
|
|
|
1963
1994
|
this.#requireTask(taskId);
|
|
1964
1995
|
this.#mutate(() => {
|
|
1965
1996
|
this.#db.prepare(`INSERT INTO decisions (task_id, decision_id, payload, created_at) VALUES (?, ?, ?, ?)
|
|
1966
|
-
ON CONFLICT(task_id, decision_id) DO UPDATE SET payload = excluded.payload`).run(taskId, decision.id, this.#
|
|
1997
|
+
ON CONFLICT(task_id, decision_id) DO UPDATE SET payload = excluded.payload`).run(taskId, decision.id, this.#recordJson("decisions", decision), decision.createdAt);
|
|
1967
1998
|
});
|
|
1968
1999
|
}
|
|
1969
2000
|
listDecisions(taskId) {
|
|
@@ -1980,7 +2011,7 @@ export class SqliteTaskStore {
|
|
|
1980
2011
|
this.#requireTask(taskId);
|
|
1981
2012
|
this.#mutate(() => {
|
|
1982
2013
|
this.#db.prepare(`INSERT INTO milestones (task_id, milestone_id, payload, created_at) VALUES (?, ?, ?, ?)
|
|
1983
|
-
ON CONFLICT(task_id, milestone_id) DO UPDATE SET payload = excluded.payload`).run(taskId, milestone.id, this.#
|
|
2014
|
+
ON CONFLICT(task_id, milestone_id) DO UPDATE SET payload = excluded.payload`).run(taskId, milestone.id, this.#recordJson("milestones", milestone), milestone.createdAt);
|
|
1984
2015
|
});
|
|
1985
2016
|
}
|
|
1986
2017
|
listMilestones(taskId) {
|
|
@@ -1998,7 +2029,7 @@ export class SqliteTaskStore {
|
|
|
1998
2029
|
this.#mutate(() => {
|
|
1999
2030
|
const seq = this.#idSequence(event.id, "event");
|
|
2000
2031
|
// Events are terminal/semantic: retained individually, never pruned (§9).
|
|
2001
|
-
this.#db.prepare(`INSERT INTO events (task_id, event_id, type, occurred_at, payload) VALUES (?, ?, ?, ?, ?)`).run(taskId, event.id, event.type, event.createdAt, this.#
|
|
2032
|
+
this.#db.prepare(`INSERT INTO events (task_id, event_id, type, occurred_at, payload) VALUES (?, ?, ?, ?, ?)`).run(taskId, event.id, event.type, event.createdAt, this.#recordJson("events", event));
|
|
2002
2033
|
this.#observeHighWater(taskId, "event", seq);
|
|
2003
2034
|
});
|
|
2004
2035
|
}
|
|
@@ -2029,14 +2060,13 @@ export class SqliteTaskStore {
|
|
|
2029
2060
|
this.#mutate(() => {
|
|
2030
2061
|
const seq = this.#idSequence(wake.id, "taskWake");
|
|
2031
2062
|
this.#db.prepare(`INSERT INTO task_wakes
|
|
2032
|
-
(task_id, wake_id, seq, status,
|
|
2033
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
2063
|
+
(task_id, wake_id, seq, status, from_cursor, to_cursor, reasons, payload, created_at, consumed_at)
|
|
2064
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
2034
2065
|
+ ` ON CONFLICT(task_id, wake_id) DO UPDATE SET
|
|
2035
2066
|
status = excluded.status,
|
|
2036
|
-
turn_id = excluded.turn_id,
|
|
2037
2067
|
reasons = excluded.reasons,
|
|
2038
2068
|
payload = excluded.payload,
|
|
2039
|
-
consumed_at = excluded.consumed_at`).run(taskId, wake.id, wake.seq, wake.status, wake.
|
|
2069
|
+
consumed_at = excluded.consumed_at`).run(taskId, wake.id, wake.seq, wake.status, wake.fromCursor, wake.toCursor, this.#json([...wake.reasons]), this.#recordJson("task_wakes", wake), wake.createdAt, wake.consumedAt ?? null);
|
|
2040
2070
|
this.#observeHighWater(taskId, "taskWake", seq);
|
|
2041
2071
|
});
|
|
2042
2072
|
}
|
|
@@ -2298,25 +2328,6 @@ export class SqliteTaskStore {
|
|
|
2298
2328
|
});
|
|
2299
2329
|
}
|
|
2300
2330
|
}
|
|
2301
|
-
function validIntegrationQueueTransition(before, after) {
|
|
2302
|
-
if (before.id !== after.id
|
|
2303
|
-
|| before.taskId !== after.taskId
|
|
2304
|
-
|| before.projectId !== after.projectId
|
|
2305
|
-
|| before.changeSetId !== after.changeSetId
|
|
2306
|
-
|| before.targetRef !== after.targetRef
|
|
2307
|
-
|| !isDeepStrictEqual(before.checkCommands, after.checkCommands)
|
|
2308
|
-
|| !isDeepStrictEqual(before.evidenceRefs, after.evidenceRefs)
|
|
2309
|
-
|| before.createdAt !== after.createdAt)
|
|
2310
|
-
return false;
|
|
2311
|
-
const allowed = {
|
|
2312
|
-
queued: ["queued", "running", "superseded"],
|
|
2313
|
-
running: ["running", "conflicted", "committed"],
|
|
2314
|
-
conflicted: ["conflicted", "running", "committed", "queued", "superseded"],
|
|
2315
|
-
committed: ["committed"],
|
|
2316
|
-
superseded: ["superseded"]
|
|
2317
|
-
};
|
|
2318
|
-
return allowed[before.status].includes(after.status);
|
|
2319
|
-
}
|
|
2320
2331
|
/** The product storage backend is one current SQLite contract. */
|
|
2321
2332
|
export function resolveTaskStoreBackendForHome(_home, _env = process.env) {
|
|
2322
2333
|
return "sqlite";
|