@zq-silk/yui 0.15.12 → 0.16.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/ARCHITECTURE.md +3 -2
- package/ARCHITECTURE.zh-CN.md +3 -2
- package/dist/artifacts/artifactCommitLock.js +16 -55
- package/dist/artifacts/taskArtifactRepository.js +3 -3
- package/dist/cli/agentConfigurationPicker.js +1 -1
- package/dist/cli/commandCatalog.js +57 -41
- package/dist/cli/completion.js +18 -18
- package/dist/cli/completionWizard.js +1 -1
- package/dist/cli/dynamicCompletion.js +1 -1
- package/dist/cli/interactionPolicy.js +7 -3
- package/dist/cli/invocationAuthority.js +64 -0
- package/dist/cli/managedDiagnostics.js +1 -1
- package/dist/cli/parseRepeatable.js +35 -0
- package/dist/cli/updatePorts.js +26 -5
- package/dist/cli.js +1280 -1243
- package/dist/commands/agentCommands.js +4 -4
- package/dist/commands/capabilityCommands.js +1 -1
- package/dist/commands/configCommands.js +6 -57
- package/dist/commands/configOverview.js +2 -2
- package/dist/commands/durableJobCommands.js +12 -6
- package/dist/commands/globalRoleCommands.js +53 -29
- package/dist/commands/grantCommands.js +0 -3
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/operatorCommands.js +3 -3
- package/dist/commands/projectCommands.js +1 -10
- package/dist/commands/resourcesCommands.js +10 -40
- package/dist/commands/roleConfiguration.js +2 -6
- package/dist/commands/taskActivationCommands.js +4 -8
- package/dist/commands/taskAgentCapabilities.js +9 -0
- package/dist/commands/taskCommandSupport.js +155 -0
- package/dist/commands/taskCommandTypes.js +1 -0
- package/dist/commands/taskCommands.js +271 -710
- package/dist/commands/taskCompletionGate.js +1 -1
- package/dist/commands/taskExecutionCommands.js +1 -1
- package/dist/commands/taskFactCommands.js +325 -0
- package/dist/commands/taskInputCommands.js +12 -42
- package/dist/commands/taskIntegrationCommands.js +17 -57
- package/dist/commands/taskNextActionCommand.js +7 -6
- package/dist/commands/taskPublicationAdoptCommand.js +3 -3
- package/dist/commands/taskPublicationCommands.js +1 -1
- package/dist/commands/taskPublicationVerifyCommand.js +1 -1
- package/dist/commands/taskRemoteDeliveryCommand.js +2 -94
- package/dist/commands/taskUpstreamCommands.js +19 -8
- package/dist/commands/workflowCommands.js +6 -1
- package/dist/completion/completionInstaller.js +26 -26
- package/dist/completion/completionState.js +26 -26
- package/dist/completion/fileCompletionManager.js +6 -11
- package/dist/config/configCatalog.js +0 -2
- package/dist/config/timeZone.js +14 -0
- package/dist/config/yuiConfig.js +0 -29
- package/dist/context/runContextPack.js +2 -3
- package/dist/context/taskCatalog.js +3 -5
- package/dist/context/taskContext.js +139 -151
- package/dist/context/wakeRunReferences.js +11 -0
- package/dist/controller/agentCapabilities.js +58 -0
- package/dist/controller/agentRuntimeObserver.js +6 -11
- package/dist/controller/clientRuntime.js +22 -14
- package/dist/controller/controller.js +9 -22
- package/dist/controller/fileSchedulerStoreAdapter.js +117 -212
- package/dist/controller/jobClient.js +4 -4
- package/dist/controller/jobControl.js +60 -25
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/providerRetryDelivery.js +7 -7
- package/dist/controller/runtime.js +51 -44
- package/dist/controller/runtimeEventProcessor.js +0 -5
- package/dist/controller/sessionOwnerReconciliation.js +5 -0
- package/dist/controller/taskAgentError.js +35 -0
- package/dist/coordination/workMailboxQueue.js +2 -24
- package/dist/core/controllerClient.js +2 -2
- package/dist/core/fileLockOwner.js +74 -0
- package/dist/decision/decision.js +17 -0
- package/dist/doctor/doctor.js +18 -9
- package/dist/event/taskEvent.js +12 -0
- package/dist/execution/workItemExecutionProjection.js +2 -2
- package/dist/executor/agentCapabilityConfig.js +43 -0
- package/dist/executor/agentConfigurationCatalog.js +49 -11
- package/dist/executor/agentExecutor.js +5 -8
- package/dist/executor/fileRoleLaunchPlanner.js +11 -53
- package/dist/executor/taskAgentCapabilities.js +64 -0
- package/dist/integration/deliveryObligation.js +1 -69
- package/dist/integration/gitIntegrationService.js +60 -84
- package/dist/integration/integrationAttempt.js +3 -14
- package/dist/integration/integrationSourceApplication.js +4 -12
- package/dist/integration/manifestTags.js +2 -2
- package/dist/job/durableJob.js +4 -25
- package/dist/job/jobAssignmentScope.js +22 -0
- package/dist/job/jobOperation.js +16 -0
- package/dist/job/jobRunner.js +2 -1
- package/dist/job/stepDirectory.js +15 -0
- package/dist/kernel/builtinCapabilities.js +15 -11
- package/dist/kernel/kernelPorts.js +1 -17
- package/dist/lifecycle/exactRunTerminalization.js +1 -9
- package/dist/message/inputControlResolution.js +3 -4
- package/dist/message/message.js +17 -20
- package/dist/message/messageContinuation.js +1 -1
- package/dist/milestone/milestone.js +11 -0
- package/dist/observability/orchestrationMetrics.js +24 -1
- package/dist/observability/runtimeIdentity.js +1 -17
- package/dist/output/agentConfigurationPresentation.js +2 -0
- package/dist/output/timePresentation.js +1 -14
- package/dist/plugins/pluginService.js +12 -4
- package/dist/release/releaseWorkflowPorts.js +45 -96
- package/dist/release/runtimeRelease.js +9 -15
- package/dist/repository/projectMaintenanceLock.js +23 -64
- package/dist/repository/taskBaseFreshness.js +1 -1
- package/dist/repository/taskWorkspaceCoordinator.js +13 -22
- package/dist/repository/taskWorkspacePreparer.js +29 -25
- package/dist/repository/workspaceCleanupInspection.js +5 -5
- package/dist/resources/autoResourceGc.js +4 -77
- package/dist/resources/liveReferences.js +7 -54
- package/dist/resources/resourceDiscovery.js +5 -118
- package/dist/resources/resourceGc.js +233 -181
- package/dist/resources/resourceRegistrar.js +5 -4
- package/dist/resources/resourceRegistry.js +3 -37
- package/dist/resources/resourceRegistryStore.js +13 -36
- package/dist/resources/sqliteResourceRegistry.js +43 -26
- package/dist/review/deltaRecheck.js +1 -1
- package/dist/review/reviewAcceptance.js +1 -1
- package/dist/review/reviewDecision.js +1 -1
- package/dist/review/reviewRound.js +8 -7
- package/dist/review/taskFinalReviewContractResolution.js +1 -1
- package/dist/runtime/acpProtocol.js +4 -51
- package/dist/runtime/acpSession.js +10 -75
- package/dist/runtime/acpSessionConfiguration.js +1 -8
- package/dist/runtime/agentEndpoint.js +3 -2
- package/dist/runtime/agentFailureContext.js +43 -0
- package/dist/runtime/agentHost.js +15 -15
- package/dist/runtime/codexInteractiveHost.js +2 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/managedCaller.js +21 -1
- package/dist/runtime/providerErrors.js +38 -0
- package/dist/runtime/runtimeObservation.js +0 -5
- package/dist/runtime/runtimeSessionCandidate.js +2 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/structuredProviderHost.js +4 -41
- package/dist/runtime/tmuxAdapters.js +6 -8
- package/dist/scheduler/activeRoleRunDelivery.js +7 -7
- package/dist/scheduler/leaderWakeupProcessor.js +19 -6
- package/dist/scheduler/operatorEvent.js +12 -20
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +5 -8
- package/dist/scheduler/roleRunLiveness.js +4 -4
- package/dist/scheduler/roleRunStall.js +16 -23
- package/dist/scheduler/taskExecutionProjection.js +25 -33
- package/dist/scheduler/taskObservabilityProjection.js +0 -1
- package/dist/scheduler/taskWake.js +5 -10
- package/dist/scheduler/wakeReason.js +2 -0
- package/dist/scheduler/wakeupQueue.js +2 -4
- package/dist/setup/setupCommand.js +1 -1
- package/dist/storage/contextRecords.js +106 -0
- package/dist/storage/currentTaskStore.js +1 -1
- package/dist/storage/homeLayout.js +13 -17
- package/dist/storage/migrations/agentFailureContext.js +22 -0
- package/dist/storage/migrations/currentInputContract.js +86 -0
- package/dist/storage/migrations/currentRuntimeContract.js +228 -0
- package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
- package/dist/storage/migrations/integrationContinuation.js +5 -4
- package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
- package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
- package/dist/storage/migrations/verificationPlanV1.js +162 -0
- package/dist/storage/migrations/verificationPolicy.js +74 -0
- package/dist/storage/migrations/workItemHistory.js +46 -0
- package/dist/storage/persistenceWorker.js +12 -4
- package/dist/storage/recordValidation.js +87 -0
- package/dist/storage/sqliteSchema.js +142 -1
- package/dist/storage/sqliteStore.js +156 -162
- package/dist/storage/storageSchema.js +2 -14
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +15 -7
- package/dist/storage/taskStore.js +3 -7
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
- package/dist/task/completionReadiness.js +5 -24
- package/dist/task/draftPlan.js +0 -53
- package/dist/task/nextAction.js +7 -13
- package/dist/task/remoteDeliveryService.js +89 -0
- package/dist/task/taskActivation.js +2 -25
- package/dist/task/taskActivationService.js +0 -2
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/task/taskSubmission.js +3 -14
- package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
- package/dist/telemetry/sqliteTelemetryStore.js +49 -49
- package/dist/telemetry/telemetryWiring.js +4 -3
- package/dist/tmux/tmuxManager.js +29 -20
- package/dist/verification/gateArtifact.js +15 -24
- package/dist/verification/gateArtifactStore.js +14 -7
- package/dist/verification/verificationGateService.js +29 -88
- package/dist/verification/verificationPlan.js +27 -72
- package/dist/web/assets/client/app.js +1 -1
- package/dist/web/assets/client/taskSurface.js +4 -4
- package/dist/web/webServer.js +4 -6
- package/dist/web/webSnapshot.js +3 -83
- package/dist/web/webTaskSurface.js +17 -46
- package/dist/workItem/workItem.js +1 -12
- package/docs/architecture/capabilities-and-resources.md +14 -2
- package/docs/architecture/capabilities-and-resources.zh-CN.md +11 -1
- package/docs/managed-turn-and-session-runtime.md +83 -10
- package/docs/managed-turn-and-session-runtime.zh-CN.md +65 -11
- package/docs/plugin-sdk.md +4 -2
- package/docs/provider-runtime.md +3 -1
- package/docs/provider-runtime.zh-CN.md +4 -2
- package/docs/release-workflow.md +190 -7
- package/docs/release-workflow.zh-CN.md +139 -5
- package/docs/roles-and-configuration.md +29 -0
- package/docs/roles-and-configuration.zh-CN.md +21 -0
- package/docs/sqlite-control-plane-design.md +16 -3
- package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
- package/docs/task-dag-semantics.md +3 -3
- package/docs/task-dag-semantics.zh-CN.md +2 -2
- package/docs/task-delivery.md +91 -5
- package/docs/task-delivery.zh-CN.md +72 -4
- package/docs/task-discovery.md +10 -11
- package/docs/task-discovery.zh-CN.md +8 -9
- package/docs/testing/verification-levels.md +145 -9
- package/docs/testing/verification-levels.zh-CN.md +103 -8
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -0
- package/skills/yui-leader/references/integration.md +9 -5
- package/skills/yui-leader/references/planning.md +9 -3
- package/skills/yui-operator/SKILL.md +15 -10
- package/skills/yui-runtime/SKILL.md +6 -6
- package/skills/yui-runtime/references/recovery.md +47 -0
- package/dist/agent/agentRegistry.js +0 -10
- package/dist/agentRun/runIdentity.js +0 -22
- package/dist/commands/deliveryGuardPreflight.js +0 -30
- package/dist/commands/taskIntegrationQueueCommands.js +0 -226
- package/dist/commands/taskOverviewCommand.js +0 -377
- package/dist/completion/completionWizard.js +0 -125
- package/dist/context/dispatchContext.js +0 -110
- package/dist/context/wakeNotification.js +0 -144
- package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
- package/dist/integration/integrationQueueEntry.js +0 -191
- package/dist/integration/integrationQueueService.js +0 -810
- package/dist/release/fakeReleasePorts.js +0 -55
- package/dist/runtime/sessionOwnerRegistry.js +0 -137
- package/dist/task/deliveryGuard.js +0 -226
- /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
|
@@ -28,46 +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 { queryTaskCatalog } from "./taskCatalog.js";
|
|
42
|
-
import { validatePluginValidation } from "../plugins/pluginPackage.js";
|
|
43
|
-
import { validatePluginIntent, validatePluginIntentFailure } from "../plugins/pluginIntent.js";
|
|
44
|
-
import { validateLocalResource, validateEnvironmentPreparation } from "../resources/projectResource.js";
|
|
45
|
-
import { validateConfiguredAgent } from "../agent/agent.js";
|
|
39
|
+
import { runPurposeAdmitsTaskState } from "../agentRun/agentRun.js";
|
|
46
40
|
import { validateTaskBrief } from "../brief/taskBrief.js";
|
|
47
|
-
import { consumePendingBatch, mailboxTargetKey, validateWorkMailbox } from "../coordination/workMailbox.js";
|
|
48
41
|
import { validateContextSnapshot } from "../context/contextSnapshot.js";
|
|
42
|
+
import { consumePendingBatch, mailboxTargetKey, validateWorkMailbox } from "../coordination/workMailbox.js";
|
|
43
|
+
import { validateDurableJob, validDurableJobTransition } from "../job/durableJob.js";
|
|
49
44
|
import { validateGlobalRoleMessage } from "../message/message.js";
|
|
50
|
-
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";
|
|
51
51
|
import { compareRuntimeSessionCandidates, projectRuntimeSessionCandidate } from "../runtime/runtimeSessionCandidate.js";
|
|
52
52
|
import { sessionOwnerProcessKey } from "../runtime/sessionOwnerIdentity.js";
|
|
53
|
-
import { validateReviewRound } from "../review/reviewRound.js";
|
|
54
|
-
import { validateProject } from "../repository/project.js";
|
|
55
|
-
import { generateHomeIdentity, validateHomeIdentity } from "../repository/homeIdentity.js";
|
|
56
|
-
import { validateIntegrationQueueEntry } from "../integration/integrationQueueEntry.js";
|
|
57
|
-
import { validDurableJobTransition, validateDurableJob } from "../job/durableJob.js";
|
|
58
|
-
import { validateGlobalRole, validateTaskRole } from "../role/role.js";
|
|
59
53
|
import { validateTaskWake } from "../scheduler/taskWake.js";
|
|
60
|
-
import { validateTask } from "../task/task.js";
|
|
61
54
|
import { pendingCompletionMessages } from "../task/completionReadiness.js";
|
|
62
|
-
import {
|
|
55
|
+
import { publicationExternalKey } from "../task/publicationReference.js";
|
|
56
|
+
import { validateTask } from "../task/task.js";
|
|
63
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";
|
|
64
62
|
import { validateWorkItem } from "../workItem/workItem.js";
|
|
65
63
|
import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
|
|
66
|
-
import {
|
|
67
|
-
import { publicationExternalKey } from "../task/publicationReference.js";
|
|
68
|
-
import { gateArtifactKey, validateGateArtifact } from "../verification/gateArtifact.js";
|
|
64
|
+
import { contextInputReferences, queryContextRecords } from "./contextRecords.js";
|
|
69
65
|
import { inspectSqliteSchemaMigrations, migrateSqliteSchema, SqliteSchemaMigrationError, TELEMETRY_KEEP_PER_RUN, TELEMETRY_RUN_CAP } from "./sqliteSchema.js";
|
|
70
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";
|
|
71
69
|
/** Read the immutable Home identity without opening a writable Store connection. */
|
|
72
70
|
export function readSqliteHomeIdentity(rootDir, databaseFilename = "yui.db") {
|
|
73
71
|
const database = new Database(join(rootDir, databaseFilename), {
|
|
@@ -222,7 +220,75 @@ export class SqliteTaskStore {
|
|
|
222
220
|
}
|
|
223
221
|
#now() { return new Date().toISOString(); }
|
|
224
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
|
+
}
|
|
225
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
|
+
}
|
|
226
292
|
#begin() {
|
|
227
293
|
this.#db.exec("BEGIN IMMEDIATE");
|
|
228
294
|
this.#inTransaction = true;
|
|
@@ -536,22 +602,7 @@ export class SqliteTaskStore {
|
|
|
536
602
|
}
|
|
537
603
|
#validateTaskPayload(table, payload) {
|
|
538
604
|
const record = this.#parse(payload);
|
|
539
|
-
|
|
540
|
-
validateTask(record);
|
|
541
|
-
if (table === "work_items")
|
|
542
|
-
validateWorkItem(record);
|
|
543
|
-
// Agents and Roles carry the execution component, and their validators
|
|
544
|
-
// refuse a record that lost it. Reading them unchecked defeated that: a
|
|
545
|
-
// component-less row reached display and rendered `undefined` as if it
|
|
546
|
-
// were a product, and reached launch as a binding nothing had vetted.
|
|
547
|
-
if (table === "configured_agents")
|
|
548
|
-
validateConfiguredAgent(record);
|
|
549
|
-
if (table === "task_roles")
|
|
550
|
-
validateTaskRole(record);
|
|
551
|
-
if (table === "global_roles")
|
|
552
|
-
validateGlobalRole(record);
|
|
553
|
-
if (table === "global_role_messages")
|
|
554
|
-
validateGlobalRoleMessage(record);
|
|
605
|
+
validateStoredRecord(table, record);
|
|
555
606
|
return record;
|
|
556
607
|
}
|
|
557
608
|
#sortById(rows, idOf) {
|
|
@@ -583,12 +634,12 @@ export class SqliteTaskStore {
|
|
|
583
634
|
saveConfiguredAgent(agent) {
|
|
584
635
|
this.#mutate(() => {
|
|
585
636
|
this.#db.prepare(`INSERT INTO configured_agents (id, payload, updated_at) VALUES (?, ?, ?)
|
|
586
|
-
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());
|
|
587
638
|
});
|
|
588
639
|
}
|
|
589
640
|
createConfiguredAgentIfAbsent(agent) {
|
|
590
641
|
return this.#mutate(() => {
|
|
591
|
-
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());
|
|
592
643
|
return result.changes > 0 ? agent : null;
|
|
593
644
|
});
|
|
594
645
|
}
|
|
@@ -628,7 +679,7 @@ export class SqliteTaskStore {
|
|
|
628
679
|
return;
|
|
629
680
|
}
|
|
630
681
|
this.#db.prepare("INSERT INTO plugin_validations (task_id, id, payload) VALUES (?, ?, ?)")
|
|
631
|
-
.run(validation.taskId, validation.id, this.#
|
|
682
|
+
.run(validation.taskId, validation.id, this.#recordJson("plugin_validations", validation));
|
|
632
683
|
});
|
|
633
684
|
}
|
|
634
685
|
savePluginIntent(intent) {
|
|
@@ -645,7 +696,7 @@ export class SqliteTaskStore {
|
|
|
645
696
|
}
|
|
646
697
|
this.#db.prepare(`INSERT INTO plugin_intents (task_id, plugin_id, payload) VALUES (?, ?, ?)
|
|
647
698
|
ON CONFLICT(task_id, plugin_id) DO UPDATE SET payload = excluded.payload`)
|
|
648
|
-
.run(intent.taskId, intent.pluginId, this.#
|
|
699
|
+
.run(intent.taskId, intent.pluginId, this.#recordJson("plugin_intents", intent));
|
|
649
700
|
});
|
|
650
701
|
}
|
|
651
702
|
getPluginIntent(taskId, pluginId) {
|
|
@@ -663,7 +714,7 @@ export class SqliteTaskStore {
|
|
|
663
714
|
return false;
|
|
664
715
|
this.#mutate(() => {
|
|
665
716
|
this.#db.prepare("UPDATE plugin_intents SET payload = ? WHERE task_id = ? AND plugin_id = ?")
|
|
666
|
-
.run(this.#
|
|
717
|
+
.run(this.#recordJson("plugin_intents", { ...current, lastFailure: failure }), taskId, pluginId);
|
|
667
718
|
});
|
|
668
719
|
return true;
|
|
669
720
|
});
|
|
@@ -682,7 +733,7 @@ export class SqliteTaskStore {
|
|
|
682
733
|
return;
|
|
683
734
|
}
|
|
684
735
|
this.#db.prepare("INSERT INTO local_resources (id, canonical_identity, payload) VALUES (?, ?, ?)")
|
|
685
|
-
.run(resource.id, `${resource.device}:${resource.inode}`, this.#
|
|
736
|
+
.run(resource.id, `${resource.device}:${resource.inode}`, this.#recordJson("local_resources", resource));
|
|
686
737
|
});
|
|
687
738
|
}
|
|
688
739
|
getLocalResource(resourceId) {
|
|
@@ -710,7 +761,7 @@ export class SqliteTaskStore {
|
|
|
710
761
|
}
|
|
711
762
|
this.#db.prepare(`INSERT INTO environment_preparations (task_id, id, payload) VALUES (?, ?, ?)
|
|
712
763
|
ON CONFLICT(task_id, id) DO UPDATE SET payload = excluded.payload`)
|
|
713
|
-
.run(preparation.taskId, preparation.id, this.#
|
|
764
|
+
.run(preparation.taskId, preparation.id, this.#recordJson("environment_preparations", preparation));
|
|
714
765
|
});
|
|
715
766
|
}
|
|
716
767
|
getEnvironmentPreparation(taskId, preparationId) {
|
|
@@ -726,13 +777,13 @@ export class SqliteTaskStore {
|
|
|
726
777
|
validateProject(project);
|
|
727
778
|
this.#mutate(() => {
|
|
728
779
|
this.#db.prepare(`INSERT INTO projects (id, name, path, payload, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)
|
|
729
|
-
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);
|
|
730
781
|
});
|
|
731
782
|
}
|
|
732
783
|
createProjectIfAbsent(project) {
|
|
733
784
|
validateProject(project);
|
|
734
785
|
return this.#mutate(() => {
|
|
735
|
-
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);
|
|
736
787
|
return result.changes > 0 ? project : null;
|
|
737
788
|
});
|
|
738
789
|
}
|
|
@@ -786,12 +837,12 @@ export class SqliteTaskStore {
|
|
|
786
837
|
saveAgentProfile(profile) {
|
|
787
838
|
this.#mutate(() => {
|
|
788
839
|
this.#db.prepare(`INSERT INTO agent_profiles (id, payload, updated_at) VALUES (?, ?, ?)
|
|
789
|
-
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());
|
|
790
841
|
});
|
|
791
842
|
}
|
|
792
843
|
createAgentProfileIfAbsent(profile) {
|
|
793
844
|
return this.#mutate(() => {
|
|
794
|
-
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());
|
|
795
846
|
return result.changes > 0 ? profile : null;
|
|
796
847
|
});
|
|
797
848
|
}
|
|
@@ -808,16 +859,16 @@ export class SqliteTaskStore {
|
|
|
808
859
|
saveGlobalRole(role) {
|
|
809
860
|
this.#mutate(() => {
|
|
810
861
|
this.#db.prepare(`INSERT INTO global_roles (name, payload, updated_at) VALUES (?, ?, ?)
|
|
811
|
-
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());
|
|
812
863
|
});
|
|
813
864
|
}
|
|
814
865
|
saveGlobalRoleWithSessionSet(role, sessions) {
|
|
815
866
|
this.#mutate(() => {
|
|
816
867
|
this.#db.prepare(`INSERT INTO global_roles (name, payload, updated_at) VALUES (?, ?, ?)
|
|
817
|
-
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());
|
|
818
869
|
if (sessions !== null) {
|
|
819
870
|
this.#db.prepare(`INSERT INTO global_role_session_sets (name, payload, updated_at) VALUES (?, ?, ?)
|
|
820
|
-
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());
|
|
821
872
|
this.#saveRuntimeSessionCandidate(sessions);
|
|
822
873
|
}
|
|
823
874
|
else {
|
|
@@ -828,7 +879,7 @@ export class SqliteTaskStore {
|
|
|
828
879
|
}
|
|
829
880
|
createGlobalRoleIfAbsent(role) {
|
|
830
881
|
return this.#mutate(() => {
|
|
831
|
-
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());
|
|
832
883
|
return result.changes > 0 ? role : null;
|
|
833
884
|
});
|
|
834
885
|
}
|
|
@@ -855,7 +906,7 @@ export class SqliteTaskStore {
|
|
|
855
906
|
saveGlobalRoleSessionSet(sessions) {
|
|
856
907
|
this.#mutate(() => {
|
|
857
908
|
this.#db.prepare(`INSERT INTO global_role_session_sets (name, payload, updated_at) VALUES (?, ?, ?)
|
|
858
|
-
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());
|
|
859
910
|
this.#saveRuntimeSessionCandidate(sessions);
|
|
860
911
|
});
|
|
861
912
|
}
|
|
@@ -866,7 +917,7 @@ export class SqliteTaskStore {
|
|
|
866
917
|
this.#requireGlobalRole(message.roleName);
|
|
867
918
|
this.#mutate(() => {
|
|
868
919
|
const seq = this.#globalMessageSequence(message.id);
|
|
869
|
-
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);
|
|
870
921
|
});
|
|
871
922
|
}
|
|
872
923
|
listGlobalRoleMessages(roleName) {
|
|
@@ -877,7 +928,7 @@ export class SqliteTaskStore {
|
|
|
877
928
|
this.#requireGlobalRole(message.roleName);
|
|
878
929
|
this.#mutate(() => {
|
|
879
930
|
const seq = this.#globalMessageSequence(message.id);
|
|
880
|
-
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);
|
|
881
932
|
if (result.changes === 0) {
|
|
882
933
|
throw new StorageRecordError(`Global message not found: ${message.roleName}/${message.id}`);
|
|
883
934
|
}
|
|
@@ -915,7 +966,7 @@ export class SqliteTaskStore {
|
|
|
915
966
|
ON CONFLICT(task_id) DO UPDATE SET status = excluded.status, lifecycle = excluded.lifecycle,
|
|
916
967
|
is_active = excluded.is_active, updated_at = excluded.updated_at`).run(task.id, task.status, task.status, isActive, task.createdAt, task.updatedAt);
|
|
917
968
|
this.#db.prepare(`INSERT INTO task_records (task_id, payload, updated_at) VALUES (?, ?, ?)
|
|
918
|
-
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());
|
|
919
970
|
});
|
|
920
971
|
}
|
|
921
972
|
listTasks() {
|
|
@@ -960,7 +1011,6 @@ export class SqliteTaskStore {
|
|
|
960
1011
|
integrationJobs: this.listDurableJobs(taskId)
|
|
961
1012
|
.filter(job => job.owner.kind === "integration-attempt")
|
|
962
1013
|
.map(job => ({ id: job.id, status: job.status })),
|
|
963
|
-
integrationQueueEntries: this.#sortById(this.#listPayload("integration_queue", "task_id = ?", [taskId]), (entry) => entry.id),
|
|
964
1014
|
reviewRounds: this.#sortById(this.#listPayload("review_rounds", "task_id = ?", [taskId]), (round) => round.id),
|
|
965
1015
|
reviewConfig: this.getReviewConfig(),
|
|
966
1016
|
openInputRequests: this.#sortById(this.#listPayload("input_requests", "task_id = ? AND status = 'open'", [taskId]), (request) => request.id),
|
|
@@ -980,7 +1030,6 @@ export class SqliteTaskStore {
|
|
|
980
1030
|
pendingUserMessages: pendingCompletionMessages(this, taskId),
|
|
981
1031
|
managedWorkspaces: this.#sortById(this.#listPayload("managed_workspaces", "task_id = ?", [taskId]), (workspace) => managedWorkspaceKey(workspace.owner)),
|
|
982
1032
|
durableJobs: this.#sortById(this.#listPayload("durable_jobs", "task_id = ?", [taskId]), (job) => job.id),
|
|
983
|
-
integrationQueueEntries: this.#sortById(this.#listPayload("integration_queue", "task_id = ?", [taskId]), (entry) => entry.id),
|
|
984
1033
|
};
|
|
985
1034
|
}
|
|
986
1035
|
/** Task ids flagged active in the catalog projection (the global active index). */
|
|
@@ -1045,7 +1094,7 @@ export class SqliteTaskStore {
|
|
|
1045
1094
|
this.#db.prepare(`INSERT INTO change_sets (task_id, change_set_id, project_id, head_sha, payload, created_at)
|
|
1046
1095
|
VALUES (?, ?, ?, ?, ?, ?)
|
|
1047
1096
|
ON CONFLICT(task_id, change_set_id) DO UPDATE SET project_id = excluded.project_id,
|
|
1048
|
-
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);
|
|
1049
1098
|
});
|
|
1050
1099
|
}
|
|
1051
1100
|
listChangeSets(taskId) {
|
|
@@ -1061,10 +1110,16 @@ export class SqliteTaskStore {
|
|
|
1061
1110
|
throw new StorageRecordError(`Integration attempt belongs to another Task: ${attempt.taskId}`);
|
|
1062
1111
|
this.#requireTask(taskId);
|
|
1063
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
|
+
}
|
|
1064
1119
|
this.#db.prepare(`INSERT INTO integration_attempts (task_id, integration_id, status, payload, updated_at)
|
|
1065
1120
|
VALUES (?, ?, ?, ?, ?)
|
|
1066
1121
|
ON CONFLICT(task_id, integration_id) DO UPDATE SET status = excluded.status,
|
|
1067
|
-
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());
|
|
1068
1123
|
});
|
|
1069
1124
|
}
|
|
1070
1125
|
listIntegrationAttempts(taskId) {
|
|
@@ -1073,46 +1128,6 @@ export class SqliteTaskStore {
|
|
|
1073
1128
|
getIntegrationAttempt(taskId, integrationId) {
|
|
1074
1129
|
return this.#getPayload("integration_attempts", "task_id = ? AND integration_id = ?", [taskId, integrationId]);
|
|
1075
1130
|
}
|
|
1076
|
-
// -- integration queue -----------------------------------------------------
|
|
1077
|
-
nextIntegrationQueueEntryId(taskId) {
|
|
1078
|
-
return this.#nextTaskRecordId(taskId, "integrationQueue");
|
|
1079
|
-
}
|
|
1080
|
-
saveIntegrationQueueEntry(taskId, entry) {
|
|
1081
|
-
if (entry.taskId !== taskId) {
|
|
1082
|
-
throw new StorageRecordError(`Integration queue entry belongs to another Task: ${entry.taskId}`);
|
|
1083
|
-
}
|
|
1084
|
-
validateIntegrationQueueEntry(entry);
|
|
1085
|
-
this.#requireTask(taskId);
|
|
1086
|
-
const changeSet = this.getChangeSet(taskId, entry.changeSetId);
|
|
1087
|
-
if (changeSet === null) {
|
|
1088
|
-
throw new StorageRecordError(`Integration queue ChangeSet not found: ${entry.changeSetId}`);
|
|
1089
|
-
}
|
|
1090
|
-
if (changeSet.projectId !== entry.projectId) {
|
|
1091
|
-
throw new StorageRecordError(`Integration queue ChangeSet belongs to another Project: ${entry.changeSetId}`);
|
|
1092
|
-
}
|
|
1093
|
-
const existing = this.getIntegrationQueueEntry(taskId, entry.id);
|
|
1094
|
-
if (existing !== null) {
|
|
1095
|
-
if (Date.parse(entry.updatedAt) < Date.parse(existing.updatedAt)) {
|
|
1096
|
-
throw new StorageRecordError(`Integration queue entry updatedAt cannot move backwards: ${entry.id}`);
|
|
1097
|
-
}
|
|
1098
|
-
if (!validIntegrationQueueTransition(existing, entry)) {
|
|
1099
|
-
throw new StorageRecordError(`Integration queue entry transition is invalid: ${entry.id}`);
|
|
1100
|
-
}
|
|
1101
|
-
}
|
|
1102
|
-
this.#mutate(() => {
|
|
1103
|
-
this.#db.prepare(`INSERT INTO integration_queue (queue_id, task_id, project_id, change_set, status, payload, created_at, updated_at)
|
|
1104
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1105
|
-
ON CONFLICT(queue_id) DO UPDATE SET task_id = excluded.task_id,
|
|
1106
|
-
project_id = excluded.project_id, change_set = excluded.change_set,
|
|
1107
|
-
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);
|
|
1108
|
-
});
|
|
1109
|
-
}
|
|
1110
|
-
listIntegrationQueueEntries(taskId) {
|
|
1111
|
-
return this.#sortById(this.#listPayload("integration_queue", "task_id = ?", [taskId]), (entry) => entry.id);
|
|
1112
|
-
}
|
|
1113
|
-
getIntegrationQueueEntry(taskId, entryId) {
|
|
1114
|
-
return this.#getPayload("integration_queue", "task_id = ? AND queue_id = ?", [taskId, entryId]);
|
|
1115
|
-
}
|
|
1116
1131
|
// -- durable jobs -----------------------------------------------------------
|
|
1117
1132
|
nextDurableJobId(taskId) {
|
|
1118
1133
|
return this.#nextTaskRecordId(taskId, "durableJob");
|
|
@@ -1137,7 +1152,7 @@ export class SqliteTaskStore {
|
|
|
1137
1152
|
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
1138
1153
|
ON CONFLICT(task_id, job_id) DO UPDATE SET idempotency_key = excluded.idempotency_key,
|
|
1139
1154
|
status = excluded.status,
|
|
1140
|
-
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);
|
|
1141
1156
|
});
|
|
1142
1157
|
}
|
|
1143
1158
|
listDurableJobs(taskId) {
|
|
@@ -1169,12 +1184,12 @@ export class SqliteTaskStore {
|
|
|
1169
1184
|
role_name = excluded.role_name, agent_id = excluded.agent_id,
|
|
1170
1185
|
native_session_id = excluded.native_session_id,
|
|
1171
1186
|
provider_root_pid = excluded.provider_root_pid,
|
|
1172
|
-
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);
|
|
1173
1188
|
});
|
|
1174
1189
|
}
|
|
1175
1190
|
getSessionOwner(processKey) {
|
|
1176
1191
|
const row = this.#db.prepare("SELECT payload FROM session_owners WHERE process_key = ?").get(processKey);
|
|
1177
|
-
return row === undefined ? null : this.#
|
|
1192
|
+
return row === undefined ? null : this.#validateTaskPayload("session_owners", row.payload);
|
|
1178
1193
|
}
|
|
1179
1194
|
listSessionOwners() {
|
|
1180
1195
|
return this.#listPayload("session_owners", "1=1", []).sort((left, right) => left.recordedAt.localeCompare(right.recordedAt));
|
|
@@ -1195,7 +1210,7 @@ export class SqliteTaskStore {
|
|
|
1195
1210
|
this.#requireTask(taskId);
|
|
1196
1211
|
this.#mutate(() => {
|
|
1197
1212
|
this.#db.prepare(`INSERT INTO task_roles (task_id, role_name, payload, updated_at) VALUES (?, ?, ?, ?)
|
|
1198
|
-
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());
|
|
1199
1214
|
});
|
|
1200
1215
|
}
|
|
1201
1216
|
listRoles(taskId) {
|
|
@@ -1208,9 +1223,9 @@ export class SqliteTaskStore {
|
|
|
1208
1223
|
this.#requireTask(role.name ? role.taskId : sessions.owner.taskId);
|
|
1209
1224
|
this.#mutate(() => {
|
|
1210
1225
|
this.#db.prepare(`INSERT INTO task_roles (task_id, role_name, payload, updated_at) VALUES (?, ?, ?, ?)
|
|
1211
|
-
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());
|
|
1212
1227
|
this.#db.prepare(`INSERT INTO role_session_sets (task_id, role_name, payload, updated_at) VALUES (?, ?, ?, ?)
|
|
1213
|
-
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());
|
|
1214
1229
|
this.#saveRuntimeSessionCandidate(sessions);
|
|
1215
1230
|
});
|
|
1216
1231
|
}
|
|
@@ -1236,7 +1251,7 @@ export class SqliteTaskStore {
|
|
|
1236
1251
|
this.#db.prepare(`INSERT INTO managed_workspaces (owner_kind, owner_id, task_id, path, payload, status, created_at, updated_at)
|
|
1237
1252
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
1238
1253
|
ON CONFLICT(owner_kind, owner_id) DO UPDATE SET task_id = excluded.task_id, path = excluded.path,
|
|
1239
|
-
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);
|
|
1240
1255
|
});
|
|
1241
1256
|
}
|
|
1242
1257
|
listManagedWorkspaces(taskId) {
|
|
@@ -1390,7 +1405,7 @@ export class SqliteTaskStore {
|
|
|
1390
1405
|
this.#requireTask(taskId);
|
|
1391
1406
|
this.#mutate(() => {
|
|
1392
1407
|
this.#db.prepare(`INSERT INTO role_session_sets (task_id, role_name, payload, updated_at) VALUES (?, ?, ?, ?)
|
|
1393
|
-
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());
|
|
1394
1409
|
this.#saveRuntimeSessionCandidate(sessions);
|
|
1395
1410
|
});
|
|
1396
1411
|
}
|
|
@@ -1441,7 +1456,7 @@ export class SqliteTaskStore {
|
|
|
1441
1456
|
this.#mutate(() => {
|
|
1442
1457
|
this.#db.prepare(`INSERT INTO work_items (task_id, work_item_id, status, payload, updated_at) VALUES (?, ?, ?, ?, ?)
|
|
1443
1458
|
ON CONFLICT(task_id, work_item_id) DO UPDATE SET status = excluded.status,
|
|
1444
|
-
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());
|
|
1445
1460
|
});
|
|
1446
1461
|
}
|
|
1447
1462
|
// -- capability grants ------------------------------------------------------
|
|
@@ -1466,7 +1481,7 @@ export class SqliteTaskStore {
|
|
|
1466
1481
|
throw new StorageRecordError(`Capability grant cannot be overwritten: ${taskId}/${stored.id}`);
|
|
1467
1482
|
}
|
|
1468
1483
|
this.#db.prepare(`INSERT INTO capability_grants (task_id, grant_id, payload, updated_at) VALUES (?, ?, ?, ?)
|
|
1469
|
-
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());
|
|
1470
1485
|
});
|
|
1471
1486
|
}
|
|
1472
1487
|
listCapabilityGrants(taskId) {
|
|
@@ -1489,7 +1504,7 @@ export class SqliteTaskStore {
|
|
|
1489
1504
|
throw new StorageRecordError(`Release workflow cannot be overwritten: ${taskId}/${stored.id}`);
|
|
1490
1505
|
}
|
|
1491
1506
|
this.#db.prepare(`INSERT INTO release_workflows (task_id, workflow_id, payload, updated_at) VALUES (?, ?, ?, ?)
|
|
1492
|
-
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());
|
|
1493
1508
|
});
|
|
1494
1509
|
}
|
|
1495
1510
|
listReleaseWorkflows(taskId) {
|
|
@@ -1536,7 +1551,7 @@ export class SqliteTaskStore {
|
|
|
1536
1551
|
remote_commit, supersedes, payload, merged_at, created_at,
|
|
1537
1552
|
title, source_branch, target_branch)
|
|
1538
1553
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
1539
|
-
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);
|
|
1540
1555
|
});
|
|
1541
1556
|
}
|
|
1542
1557
|
listPublicationReferences(taskId) {
|
|
@@ -1553,7 +1568,7 @@ export class SqliteTaskStore {
|
|
|
1553
1568
|
saveGateArtifact(artifact, logs) {
|
|
1554
1569
|
validateGateArtifact(artifact);
|
|
1555
1570
|
this.#mutate(() => {
|
|
1556
|
-
const targetRef = artifact.boundary
|
|
1571
|
+
const targetRef = artifact.boundary.targetRef;
|
|
1557
1572
|
const completedAt = artifact.completedAt ?? null;
|
|
1558
1573
|
this.#db.prepare(`INSERT INTO gate_artifacts
|
|
1559
1574
|
(key, project_id, level, commit_sha, plan_digest, toolchain_digest,
|
|
@@ -1569,8 +1584,9 @@ export class SqliteTaskStore {
|
|
|
1569
1584
|
status = excluded.status,
|
|
1570
1585
|
outcome = excluded.outcome,
|
|
1571
1586
|
payload = excluded.payload,
|
|
1587
|
+
created_at = excluded.created_at,
|
|
1572
1588
|
completed_at = excluded.completed_at,
|
|
1573
|
-
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);
|
|
1574
1590
|
// Replace all logs for this artifact in the same transaction.
|
|
1575
1591
|
this.#db.prepare("DELETE FROM gate_artifact_logs WHERE artifact_key = ?").run(artifact.key);
|
|
1576
1592
|
const insertLog = this.#db.prepare(`INSERT INTO gate_artifact_logs (artifact_key, step_name, log_content, log_digest, log_bytes)
|
|
@@ -1587,7 +1603,15 @@ export class SqliteTaskStore {
|
|
|
1587
1603
|
touchGateArtifact(artifact) {
|
|
1588
1604
|
validateGateArtifact(artifact);
|
|
1589
1605
|
this.#mutate(() => {
|
|
1590
|
-
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);
|
|
1591
1615
|
if (result.changes === 0) {
|
|
1592
1616
|
throw new StorageRecordError(`Gate artifact not found for touch: ${artifact.key}`);
|
|
1593
1617
|
}
|
|
@@ -1602,18 +1626,8 @@ export class SqliteTaskStore {
|
|
|
1602
1626
|
}
|
|
1603
1627
|
findL2GateArtifactsForCommit(query) {
|
|
1604
1628
|
const rows = this.#listPayload("gate_artifacts", `project_id = ? AND commit_sha = ? AND level = 'L2'
|
|
1605
|
-
AND plan_digest = ? AND toolchain_digest = ? AND target_ref =
|
|
1606
|
-
|
|
1607
|
-
const valid = [];
|
|
1608
|
-
for (const row of rows) {
|
|
1609
|
-
try {
|
|
1610
|
-
valid.push(validateGateArtifact(row));
|
|
1611
|
-
}
|
|
1612
|
-
catch {
|
|
1613
|
-
// Skip corrupt rows; a direct lookup fails closed.
|
|
1614
|
-
}
|
|
1615
|
-
}
|
|
1616
|
-
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);
|
|
1617
1631
|
}
|
|
1618
1632
|
getGateArtifactLogs(artifactKey) {
|
|
1619
1633
|
const rows = this.#db.prepare("SELECT step_name, log_content FROM gate_artifact_logs WHERE artifact_key = ?").all(artifactKey);
|
|
@@ -1684,7 +1698,7 @@ export class SqliteTaskStore {
|
|
|
1684
1698
|
this.#mutate(() => {
|
|
1685
1699
|
this.#db.prepare(`INSERT INTO context_snapshots
|
|
1686
1700
|
(task_id, snapshot_id, scope, scope_ref, sequence, digest, payload, frozen_at)
|
|
1687
|
-
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);
|
|
1688
1702
|
});
|
|
1689
1703
|
}
|
|
1690
1704
|
// -- turns -----------------------------------------------------------------
|
|
@@ -1715,7 +1729,7 @@ export class SqliteTaskStore {
|
|
|
1715
1729
|
this.#mutate(() => {
|
|
1716
1730
|
this.#db.prepare(`INSERT INTO turns (task_id, turn_id, role_name, status, payload, updated_at) VALUES (?, ?, ?, ?, ?, ?)
|
|
1717
1731
|
ON CONFLICT(task_id, turn_id) DO UPDATE SET role_name = excluded.role_name, status = excluded.status,
|
|
1718
|
-
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());
|
|
1719
1733
|
});
|
|
1720
1734
|
}
|
|
1721
1735
|
// -- review rounds ----------------------------------------------------------
|
|
@@ -1734,7 +1748,7 @@ export class SqliteTaskStore {
|
|
|
1734
1748
|
this.#mutate(() => {
|
|
1735
1749
|
this.#db.prepare(`INSERT INTO review_rounds (task_id, review_round_id, status, payload, updated_at) VALUES (?, ?, ?, ?, ?)
|
|
1736
1750
|
ON CONFLICT(task_id, review_round_id) DO UPDATE SET status = excluded.status,
|
|
1737
|
-
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());
|
|
1738
1752
|
});
|
|
1739
1753
|
}
|
|
1740
1754
|
// -- active turns ----------------------------------------------------------
|
|
@@ -1922,7 +1936,7 @@ export class SqliteTaskStore {
|
|
|
1922
1936
|
this.#requireTask(taskId);
|
|
1923
1937
|
this.#mutate(() => {
|
|
1924
1938
|
const seq = this.#idSequence(message.id, "message");
|
|
1925
|
-
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);
|
|
1926
1940
|
this.#observeHighWater(taskId, "message", seq);
|
|
1927
1941
|
});
|
|
1928
1942
|
}
|
|
@@ -1932,7 +1946,7 @@ export class SqliteTaskStore {
|
|
|
1932
1946
|
}
|
|
1933
1947
|
this.#requireTask(taskId);
|
|
1934
1948
|
this.#mutate(() => {
|
|
1935
|
-
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);
|
|
1936
1950
|
if (result.changes !== 1) {
|
|
1937
1951
|
throw new StorageRecordError(`Message does not exist: ${taskId}/${message.id}`);
|
|
1938
1952
|
}
|
|
@@ -1949,7 +1963,7 @@ export class SqliteTaskStore {
|
|
|
1949
1963
|
this.#requireTask(taskId);
|
|
1950
1964
|
this.#mutate(() => {
|
|
1951
1965
|
this.#db.prepare(`INSERT INTO input_requests (task_id, input_id, status, payload, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?)
|
|
1952
|
-
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());
|
|
1953
1967
|
});
|
|
1954
1968
|
}
|
|
1955
1969
|
getInputRequest(taskId, requestId) {
|
|
@@ -1980,7 +1994,7 @@ export class SqliteTaskStore {
|
|
|
1980
1994
|
this.#requireTask(taskId);
|
|
1981
1995
|
this.#mutate(() => {
|
|
1982
1996
|
this.#db.prepare(`INSERT INTO decisions (task_id, decision_id, payload, created_at) VALUES (?, ?, ?, ?)
|
|
1983
|
-
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);
|
|
1984
1998
|
});
|
|
1985
1999
|
}
|
|
1986
2000
|
listDecisions(taskId) {
|
|
@@ -1997,7 +2011,7 @@ export class SqliteTaskStore {
|
|
|
1997
2011
|
this.#requireTask(taskId);
|
|
1998
2012
|
this.#mutate(() => {
|
|
1999
2013
|
this.#db.prepare(`INSERT INTO milestones (task_id, milestone_id, payload, created_at) VALUES (?, ?, ?, ?)
|
|
2000
|
-
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);
|
|
2001
2015
|
});
|
|
2002
2016
|
}
|
|
2003
2017
|
listMilestones(taskId) {
|
|
@@ -2015,7 +2029,7 @@ export class SqliteTaskStore {
|
|
|
2015
2029
|
this.#mutate(() => {
|
|
2016
2030
|
const seq = this.#idSequence(event.id, "event");
|
|
2017
2031
|
// Events are terminal/semantic: retained individually, never pruned (§9).
|
|
2018
|
-
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));
|
|
2019
2033
|
this.#observeHighWater(taskId, "event", seq);
|
|
2020
2034
|
});
|
|
2021
2035
|
}
|
|
@@ -2046,14 +2060,13 @@ export class SqliteTaskStore {
|
|
|
2046
2060
|
this.#mutate(() => {
|
|
2047
2061
|
const seq = this.#idSequence(wake.id, "taskWake");
|
|
2048
2062
|
this.#db.prepare(`INSERT INTO task_wakes
|
|
2049
|
-
(task_id, wake_id, seq, status,
|
|
2050
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?,
|
|
2063
|
+
(task_id, wake_id, seq, status, from_cursor, to_cursor, reasons, payload, created_at, consumed_at)
|
|
2064
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`
|
|
2051
2065
|
+ ` ON CONFLICT(task_id, wake_id) DO UPDATE SET
|
|
2052
2066
|
status = excluded.status,
|
|
2053
|
-
turn_id = excluded.turn_id,
|
|
2054
2067
|
reasons = excluded.reasons,
|
|
2055
2068
|
payload = excluded.payload,
|
|
2056
|
-
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);
|
|
2057
2070
|
this.#observeHighWater(taskId, "taskWake", seq);
|
|
2058
2071
|
});
|
|
2059
2072
|
}
|
|
@@ -2315,25 +2328,6 @@ export class SqliteTaskStore {
|
|
|
2315
2328
|
});
|
|
2316
2329
|
}
|
|
2317
2330
|
}
|
|
2318
|
-
function validIntegrationQueueTransition(before, after) {
|
|
2319
|
-
if (before.id !== after.id
|
|
2320
|
-
|| before.taskId !== after.taskId
|
|
2321
|
-
|| before.projectId !== after.projectId
|
|
2322
|
-
|| before.changeSetId !== after.changeSetId
|
|
2323
|
-
|| before.targetRef !== after.targetRef
|
|
2324
|
-
|| !isDeepStrictEqual(before.checkCommands, after.checkCommands)
|
|
2325
|
-
|| !isDeepStrictEqual(before.evidenceRefs, after.evidenceRefs)
|
|
2326
|
-
|| before.createdAt !== after.createdAt)
|
|
2327
|
-
return false;
|
|
2328
|
-
const allowed = {
|
|
2329
|
-
queued: ["queued", "running", "superseded"],
|
|
2330
|
-
running: ["running", "conflicted", "committed"],
|
|
2331
|
-
conflicted: ["conflicted", "running", "committed", "queued", "superseded"],
|
|
2332
|
-
committed: ["committed"],
|
|
2333
|
-
superseded: ["superseded"]
|
|
2334
|
-
};
|
|
2335
|
-
return allowed[before.status].includes(after.status);
|
|
2336
|
-
}
|
|
2337
2331
|
/** The product storage backend is one current SQLite contract. */
|
|
2338
2332
|
export function resolveTaskStoreBackendForHome(_home, _env = process.env) {
|
|
2339
2333
|
return "sqlite";
|