@zq-silk/yui 0.13.9 → 0.14.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 +69 -428
- package/README.md +108 -298
- package/dist/agent/managedRuntimeEnvironment.js +2 -2
- package/dist/cli/commandCatalog.js +39 -77
- package/dist/cli/interactionCandidates.js +4 -4
- package/dist/cli/interactionPolicy.js +7 -10
- package/dist/cli/invocationRouter.js +1 -1
- package/dist/cli/updateCommand.js +6 -27
- package/dist/cli/updateOrchestrator.js +63 -484
- package/dist/cli/updatePorts.js +15 -333
- package/dist/cli/upgradeCommand.js +8 -65
- package/dist/cli.js +57 -242
- package/dist/commands/configCommands.js +11 -62
- package/dist/commands/executionAuditCommands.js +23 -23
- package/dist/commands/globalRoleCommands.js +2 -7
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/projectCommands.js +5 -5
- package/dist/commands/releaseCommands.js +3 -10
- package/dist/commands/resourcesCommands.js +2 -2
- package/dist/commands/sessionCommands.js +2 -2
- package/dist/commands/taskActor.js +31 -39
- package/dist/commands/taskChangeSetCommands.js +5 -12
- package/dist/commands/taskCommands.js +464 -1249
- package/dist/commands/taskContextCommand.js +35 -36
- package/dist/commands/taskExecutionCommands.js +33 -46
- package/dist/commands/taskInputCommands.js +20 -42
- package/dist/commands/taskNextActionCommand.js +9 -9
- package/dist/commands/taskOverviewCommand.js +56 -95
- package/dist/commands/taskRoleRuntimeStatus.js +77 -107
- package/dist/commands/taskUpstreamCommands.js +3 -3
- package/dist/commands/taskWorkspaceCommands.js +7 -79
- package/dist/commands/telemetryCommands.js +32 -124
- package/dist/commands/workflowCommands.js +3 -3
- package/dist/completion/fileCompletionManager.js +1 -1
- package/dist/config/configCatalog.js +4 -6
- package/dist/config/yuiConfig.js +6 -43
- package/dist/context/dispatchContext.js +14 -30
- package/dist/context/roleSessionContext.js +2 -2
- package/dist/context/sessionBootstrapManifest.js +2 -2
- package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
- package/dist/context/turnInputContract.js +204 -0
- package/dist/context/wakeNotification.js +2 -2
- package/dist/controller/agentRuntimeObserver.js +18 -18
- package/dist/controller/clientRuntime.js +13 -20
- package/dist/controller/controller.js +89 -116
- package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
- package/dist/controller/jobControl.js +17 -17
- package/dist/controller/resourceInventory.js +6 -6
- package/dist/controller/resourceInventoryLinux.js +38 -59
- package/dist/controller/resourceInventoryRpc.js +3 -3
- package/dist/controller/resourceInventoryWorker.js +1 -1
- package/dist/controller/runtime.js +46 -53
- package/dist/controller/runtimeEventInbox.js +23 -31
- package/dist/controller/runtimeEventProcessor.js +15 -15
- package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
- package/dist/controller/runtimeLaunchCoordinator.js +3 -3
- package/dist/controller/runtimeObservationHook.js +11 -8
- package/dist/controller/sessionNotify.js +11 -11
- package/dist/controller/structuredProviderObservation.js +89 -16
- package/dist/coordination/workMailbox.js +309 -579
- package/dist/coordination/workMailboxQueue.js +5 -6
- package/dist/core/controllerClient.js +1 -91
- package/dist/core/controllerServer.js +3 -6
- package/dist/doctor/doctor.js +51 -314
- package/dist/execution/candidateConvergence.js +3 -3
- package/dist/execution/executionGroup.js +32 -33
- package/dist/execution/executionHealth.js +36 -38
- package/dist/execution/resourceBroker.js +12 -12
- package/dist/executor/agentExecutor.js +30 -265
- package/dist/executor/effectiveLaunch.js +9 -19
- package/dist/executor/executorRegistry.js +43 -17
- package/dist/executor/fileRoleLaunchPlanner.js +56 -56
- package/dist/executor/launchPlan.js +3 -3
- package/dist/executor/workspacePreflightClassification.js +16 -16
- package/dist/grant/capabilityGrant.js +20 -25
- package/dist/input/inputRequest.js +12 -12
- package/dist/integration/changeSet.js +9 -13
- package/dist/integration/changeSetManifest.js +1 -1
- package/dist/integration/gitIntegrationService.js +2 -2
- package/dist/integration/integrationQueueService.js +5 -21
- package/dist/integration/overlapDiagnostics.js +2 -2
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/job/jobRunner.js +1 -1
- package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
- package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
- package/dist/message/message.js +6 -6
- package/dist/observability/executionAudit.js +74 -86
- package/dist/observability/faultClassification.js +7 -24
- package/dist/observability/orchestrationMetrics.js +5 -5
- package/dist/observability/runtimeIdentity.js +34 -101
- package/dist/profile/agentProfile.js +1 -1
- package/dist/release/cliHomeReleaseFence.js +3 -3
- package/dist/release/releaseWorkflowEngine.js +6 -10
- package/dist/release/releaseWorkflowPorts.js +2 -2
- package/dist/release/workflowFileLock.js +1 -1
- package/dist/repository/checkoutSwap.js +1 -1
- package/dist/repository/gitWorkspace.js +0 -13
- package/dist/repository/project.js +1 -1
- package/dist/repository/projectMaintenanceLock.js +1 -2
- package/dist/repository/taskBaseFreshness.js +13 -13
- package/dist/repository/taskWorkspaceCoordinator.js +21 -25
- package/dist/repository/taskWorkspaceIdentity.js +8 -21
- package/dist/repository/taskWorkspacePreparer.js +63 -620
- package/dist/resources/autoResourceGc.js +3 -3
- package/dist/resources/liveReferences.js +1 -1
- package/dist/resources/resourceGc.js +1 -1
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/review/reviewDecision.js +7 -7
- package/dist/review/reviewFindingLedger.js +1 -1
- package/dist/review/reviewOutcomeClassifier.js +35 -50
- package/dist/review/reviewRound.js +14 -25
- package/dist/review/reviewerAvailability.js +4 -4
- package/dist/review/taskFinalReviewContractResolution.js +41 -0
- package/dist/role/role.js +6 -3
- package/dist/runtime/agentDriver.js +4 -0
- package/dist/runtime/agentHost.js +151 -36
- package/dist/runtime/builtinAgentDrivers.js +79 -2
- package/dist/runtime/codexAppServerRuntime.js +122 -0
- package/dist/runtime/continuationManager.js +3 -5
- package/dist/runtime/exactControlPlane.js +17 -42
- package/dist/runtime/firstProgressAdvisory.js +1 -1
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/launchDiagnostics.js +2 -2
- package/dist/runtime/lifecycleReservation.js +4 -4
- package/dist/runtime/processExitObservation.js +1 -1
- package/dist/runtime/processExitOutbox.js +0 -32
- package/dist/runtime/promptEnvelope.js +9 -9
- package/dist/runtime/providerContinuation.js +2 -2
- package/dist/runtime/providerContinuationReconciliationService.js +2 -2
- package/dist/runtime/providerRuntimeIdentity.js +76 -24
- package/dist/runtime/recentTurnIds.js +38 -0
- package/dist/runtime/runtimeContinuationProjection.js +2 -2
- package/dist/runtime/runtimeHealthPolicy.js +1 -1
- package/dist/runtime/runtimeObservation.js +58 -21
- package/dist/runtime/runtimeProjection.js +19 -27
- package/dist/runtime/sessionLaunchRequest.js +2 -2
- package/dist/runtime/structuredProviderHost.js +177 -12
- package/dist/runtime/tmuxAdapters.js +46 -11
- package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
- package/dist/scheduler/leaderWakeupProcessor.js +147 -445
- package/dist/scheduler/operatorEvent.js +4 -6
- package/dist/scheduler/resourceQueueProjection.js +71 -0
- package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
- package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
- package/dist/scheduler/taskExecutionProjection.js +99 -99
- package/dist/scheduler/taskObservabilityProjection.js +10 -10
- package/dist/scheduler/taskWake.js +4 -4
- package/dist/scheduler/wakeReason.js +6 -10
- package/dist/scheduler/wakeupQueue.js +0 -9
- package/dist/setup/setupCommand.js +2 -2
- package/dist/storage/currentTaskStore.js +46 -0
- package/dist/storage/persistenceWorker.js +1 -1
- package/dist/storage/sqliteSchema.js +81 -485
- package/dist/storage/sqliteStore.js +206 -365
- package/dist/storage/storageSchema.js +1 -15
- package/dist/storage/storageVersions.js +8 -9
- package/dist/storage/storeRpc.js +10 -34
- package/dist/storage/taskStore.js +63 -3415
- package/dist/storage/upgrade/recordVersions.js +55 -149
- package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
- package/dist/task/completionReadiness.js +5 -5
- package/dist/task/deliveryGuard.js +10 -10
- package/dist/task/nextAction.js +74 -77
- package/dist/task/task.js +2 -4
- package/dist/task/taskRecordReference.js +3 -3
- package/dist/task/taskRecordRetirement.js +1 -1
- package/dist/telemetry/sqliteTelemetryStore.js +53 -53
- package/dist/telemetry/telemetryConfig.js +14 -14
- package/dist/telemetry/telemetryWiring.js +8 -14
- package/dist/turn/turn.js +356 -0
- package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
- package/dist/verification/gateArtifactStore.js +2 -2
- package/dist/web/assets/client/app.js +1 -1
- package/dist/web/assets/client/components.js +29 -54
- package/dist/web/assets/client/i18n.js +17 -37
- package/dist/web/assets/client/view.js +37 -37
- package/dist/web/assets/shell.js +1 -1
- package/dist/web/assets/styles/cards.js +5 -5
- package/dist/web/assets/styles/widgets.js +1 -2
- package/dist/web/webSnapshot.js +65 -60
- package/dist/workItem/workItem.js +11 -11
- package/dist/workspace/workItemChangeSetManager.js +1 -1
- package/docs/task-local-identity.md +5 -5
- package/i18n/README.zh-CN.md +41 -54
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +85 -83
- package/skills/yui-operator/SKILL.md +16 -16
- package/skills/yui-reviewer/SKILL.md +10 -10
- package/skills/yui-runtime/SKILL.md +17 -18
- package/skills/yui-worker/SKILL.md +16 -18
- package/dist/context/runContextContract.js +0 -162
- package/dist/executor/turnCompletion.js +0 -126
- package/dist/review/taskFinalReviewContractEvent.js +0 -1
- package/dist/review/taskFinalReviewContractRebind.js +0 -367
- package/dist/run/agentRun.js +0 -356
- package/dist/run/rejectedYieldAttempt.js +0 -221
- package/dist/run/runControlRequest.js +0 -50
- package/dist/run/yieldReceipt.js +0 -65
- package/dist/runtime/turnCompletion.js +0 -3
- package/dist/scheduler/actionability.js +0 -323
- package/dist/scheduler/activeRoleRunDelivery.js +0 -868
- package/dist/scheduler/activeTaskProgress.js +0 -126
- package/dist/storage/compatibleTaskStore.js +0 -248
- package/dist/storage/migration/baseline.js +0 -78
- package/dist/storage/migration/classifier.js +0 -51
- package/dist/storage/migration/compatibleCodec.js +0 -53
- package/dist/storage/migration/engine.js +0 -147
- package/dist/storage/migration/index.js +0 -33
- package/dist/storage/migration/planner.js +0 -154
- package/dist/storage/migration/productionRegistry.js +0 -3931
- package/dist/storage/migration/registry.js +0 -169
- package/dist/storage/migration/report.js +0 -54
- package/dist/storage/migration/types.js +0 -31
- package/dist/storage/upgrade/homeClassification.js +0 -353
- package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
- package/dist/storage/upgrade/migrationReceipt.js +0 -67
- package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
- package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
- package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
- package/dist/storage/upgrade/recordVersionScan.js +0 -176
- package/dist/storage/upgrade/sqliteFileSet.js +0 -50
- package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
- package/dist/storage/upgrade/sqliteStateMigration.js +0 -918
- package/dist/storage/upgrade/switchProgress.js +0 -80
- package/dist/storage/upgrade/upgradeReceipt.js +0 -161
- package/dist/storage/upgradeCoordination.js +0 -187
- package/dist/storage/upgradeFence.js +0 -366
- package/dist/telemetry/telemetryCompaction.js +0 -252
|
@@ -22,15 +22,15 @@
|
|
|
22
22
|
* by SQLite (DDL is transactional) and re-applied on the next open.
|
|
23
23
|
*/
|
|
24
24
|
import { createHash } from "node:crypto";
|
|
25
|
-
/** The SQLite layout version
|
|
26
|
-
export const SQLITE_LAYOUT_VERSION =
|
|
25
|
+
/** The current SQLite layout version. */
|
|
26
|
+
export const SQLITE_LAYOUT_VERSION = 8;
|
|
27
27
|
/** The aggregate version of the normalized SQLite schema. */
|
|
28
|
-
export const SQLITE_AGGREGATE_VERSION =
|
|
28
|
+
export const SQLITE_AGGREGATE_VERSION = 2;
|
|
29
29
|
/** The current schema migration version. */
|
|
30
|
-
export const SQLITE_SCHEMA_VERSION =
|
|
30
|
+
export const SQLITE_SCHEMA_VERSION = 1;
|
|
31
31
|
/** Telemetry retention bounds (§4.4). Open question 3 in §11; defaults from the design. */
|
|
32
32
|
export const TELEMETRY_KEEP_PER_GENERATION = 200;
|
|
33
|
-
export const
|
|
33
|
+
export const TELEMETRY_TURN_CAP = 50_000;
|
|
34
34
|
/**
|
|
35
35
|
* Version 1 migration: creates every table and index.
|
|
36
36
|
*
|
|
@@ -38,11 +38,10 @@ export const TELEMETRY_RUN_CAP = 50_000;
|
|
|
38
38
|
* the store; `journal_mode=WAL` is a persistent database property set on open.
|
|
39
39
|
* The migration itself only contains schema objects.
|
|
40
40
|
*
|
|
41
|
-
* This
|
|
42
|
-
*
|
|
43
|
-
* projection kind it creates is retained only for old layout-7 Homes.
|
|
41
|
+
* This is the direct current baseline for a new Home. Its checksum rejects any
|
|
42
|
+
* database initialized from a different physical contract.
|
|
44
43
|
*/
|
|
45
|
-
const
|
|
44
|
+
const BASELINE_CORE_SQL = `
|
|
46
45
|
-- Global catalog and coordination (§4.1) -------------------------------------
|
|
47
46
|
|
|
48
47
|
CREATE TABLE IF NOT EXISTS home_meta (
|
|
@@ -182,10 +181,14 @@ CREATE TABLE IF NOT EXISTS mailboxes (
|
|
|
182
181
|
next_sequence INTEGER NOT NULL,
|
|
183
182
|
processing TEXT,
|
|
184
183
|
pending TEXT,
|
|
184
|
+
recent_dedupe_keys TEXT NOT NULL DEFAULT '[]',
|
|
185
185
|
UNIQUE (target_kind, task_id, role_name)
|
|
186
186
|
);
|
|
187
187
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_mailboxes_target_key
|
|
188
188
|
ON mailboxes(target_key);
|
|
189
|
+
CREATE INDEX IF NOT EXISTS idx_mailboxes_ready
|
|
190
|
+
ON mailboxes(target_key)
|
|
191
|
+
WHERE processing IS NOT NULL OR json_type(pending) <> 'null';
|
|
189
192
|
|
|
190
193
|
-- Task-partitioned tables (§4.3). Every task-local read constrains task_id. ----
|
|
191
194
|
|
|
@@ -232,22 +235,22 @@ CREATE TABLE IF NOT EXISTS work_item_candidates (
|
|
|
232
235
|
PRIMARY KEY (task_id, candidate_id)
|
|
233
236
|
);
|
|
234
237
|
|
|
235
|
-
CREATE TABLE IF NOT EXISTS
|
|
238
|
+
CREATE TABLE IF NOT EXISTS turns (
|
|
236
239
|
task_id TEXT NOT NULL,
|
|
237
|
-
|
|
240
|
+
turn_id TEXT NOT NULL,
|
|
238
241
|
role_name TEXT NOT NULL,
|
|
239
242
|
status TEXT NOT NULL,
|
|
240
243
|
payload TEXT NOT NULL,
|
|
241
244
|
updated_at TEXT NOT NULL,
|
|
242
|
-
PRIMARY KEY (task_id,
|
|
245
|
+
PRIMARY KEY (task_id, turn_id)
|
|
243
246
|
);
|
|
244
|
-
CREATE INDEX IF NOT EXISTS
|
|
247
|
+
CREATE INDEX IF NOT EXISTS idx_turns_role_status ON turns(task_id, role_name, status);
|
|
245
248
|
|
|
246
|
-
-- Active-
|
|
247
|
-
CREATE TABLE IF NOT EXISTS
|
|
249
|
+
-- Active-Turn pointers (getActiveTurn / execution-lane Turns).
|
|
250
|
+
CREATE TABLE IF NOT EXISTS active_turns (
|
|
248
251
|
task_id TEXT NOT NULL,
|
|
249
252
|
pointer TEXT NOT NULL,
|
|
250
|
-
|
|
253
|
+
turn_id TEXT NOT NULL,
|
|
251
254
|
payload TEXT NOT NULL,
|
|
252
255
|
updated_at TEXT NOT NULL,
|
|
253
256
|
PRIMARY KEY (task_id, pointer)
|
|
@@ -303,6 +306,9 @@ CREATE TABLE IF NOT EXISTS input_requests (
|
|
|
303
306
|
PRIMARY KEY (task_id, input_id)
|
|
304
307
|
);
|
|
305
308
|
CREATE INDEX IF NOT EXISTS idx_input_open ON input_requests(task_id, status) WHERE status <> 'resolved';
|
|
309
|
+
CREATE INDEX IF NOT EXISTS idx_input_requests_open_hot
|
|
310
|
+
ON input_requests(task_id, input_id)
|
|
311
|
+
WHERE status = 'open';
|
|
306
312
|
|
|
307
313
|
CREATE TABLE IF NOT EXISTS decisions (
|
|
308
314
|
task_id TEXT NOT NULL,
|
|
@@ -344,30 +350,27 @@ CREATE TABLE IF NOT EXISTS task_projections (
|
|
|
344
350
|
CREATE TABLE IF NOT EXISTS telemetry (
|
|
345
351
|
task_id TEXT NOT NULL,
|
|
346
352
|
role_name TEXT NOT NULL,
|
|
347
|
-
|
|
353
|
+
turn_id TEXT NOT NULL,
|
|
348
354
|
generation TEXT NOT NULL,
|
|
349
355
|
progress_id TEXT NOT NULL,
|
|
350
356
|
sequence INTEGER,
|
|
351
357
|
payload TEXT NOT NULL,
|
|
352
358
|
received_at TEXT NOT NULL,
|
|
353
|
-
PRIMARY KEY (task_id, role_name,
|
|
359
|
+
PRIMARY KEY (task_id, role_name, turn_id, generation, progress_id)
|
|
354
360
|
) WITHOUT ROWID;
|
|
355
|
-
CREATE INDEX IF NOT EXISTS
|
|
361
|
+
CREATE INDEX IF NOT EXISTS idx_telemetry_turn ON telemetry(task_id, turn_id);
|
|
356
362
|
`;
|
|
357
|
-
/**
|
|
358
|
-
|
|
359
|
-
* migration creates the DurableJob, CapabilityGrant, and ReleaseWorkflow
|
|
360
|
-
* tables so a fresh database receives the complete merged schema atomically.
|
|
361
|
-
*/
|
|
362
|
-
const MIGRATION_2_SQL = `
|
|
363
|
+
/** Task-scoped execution and release record families. */
|
|
364
|
+
const BASELINE_JOB_AND_RELEASE_SQL = `
|
|
363
365
|
CREATE TABLE IF NOT EXISTS durable_jobs (
|
|
364
|
-
job_id TEXT
|
|
366
|
+
job_id TEXT NOT NULL,
|
|
365
367
|
task_id TEXT NOT NULL,
|
|
366
368
|
idempotency_key TEXT,
|
|
367
369
|
status TEXT NOT NULL,
|
|
368
370
|
payload TEXT NOT NULL,
|
|
369
371
|
created_at TEXT NOT NULL,
|
|
370
|
-
updated_at TEXT NOT NULL
|
|
372
|
+
updated_at TEXT NOT NULL,
|
|
373
|
+
PRIMARY KEY (task_id, job_id)
|
|
371
374
|
);
|
|
372
375
|
CREATE INDEX IF NOT EXISTS idx_durable_jobs_task ON durable_jobs(task_id);
|
|
373
376
|
CREATE INDEX IF NOT EXISTS idx_durable_jobs_status ON durable_jobs(status);
|
|
@@ -389,12 +392,8 @@ CREATE TABLE IF NOT EXISTS release_workflows (
|
|
|
389
392
|
PRIMARY KEY (task_id, workflow_id)
|
|
390
393
|
);
|
|
391
394
|
`;
|
|
392
|
-
/**
|
|
393
|
-
|
|
394
|
-
* the caller key bound to each launched Session, used to fail-closed verify a
|
|
395
|
-
* DurableJob's caller against durable Run state.
|
|
396
|
-
*/
|
|
397
|
-
const MIGRATION_3_SQL = `
|
|
395
|
+
/** Durable caller-key hashes used to verify DurableJob ownership. */
|
|
396
|
+
const BASELINE_JOB_CALLER_SQL = `
|
|
398
397
|
CREATE TABLE IF NOT EXISTS job_caller_key_hashes (
|
|
399
398
|
task_id TEXT NOT NULL,
|
|
400
399
|
role_name TEXT NOT NULL,
|
|
@@ -405,48 +404,17 @@ CREATE TABLE IF NOT EXISTS job_caller_key_hashes (
|
|
|
405
404
|
);
|
|
406
405
|
`;
|
|
407
406
|
/**
|
|
408
|
-
*
|
|
409
|
-
*
|
|
410
|
-
* could overwrite one another through the upsert path. Rebuild the table with
|
|
411
|
-
* the actual record identity `(task_id, job_id)` and restore its indexes.
|
|
412
|
-
* Never edit migration 2's checksum: existing Homes reach this repair through
|
|
413
|
-
* this adjacent schema migration.
|
|
414
|
-
*/
|
|
415
|
-
const MIGRATION_4_SQL = `
|
|
416
|
-
CREATE TABLE durable_jobs_v4 (
|
|
417
|
-
job_id TEXT NOT NULL,
|
|
418
|
-
task_id TEXT NOT NULL,
|
|
419
|
-
idempotency_key TEXT,
|
|
420
|
-
status TEXT NOT NULL,
|
|
421
|
-
payload TEXT NOT NULL,
|
|
422
|
-
created_at TEXT NOT NULL,
|
|
423
|
-
updated_at TEXT NOT NULL,
|
|
424
|
-
PRIMARY KEY (task_id, job_id)
|
|
425
|
-
);
|
|
426
|
-
INSERT INTO durable_jobs_v4
|
|
427
|
-
(job_id, task_id, idempotency_key, status, payload, created_at, updated_at)
|
|
428
|
-
SELECT job_id, task_id, idempotency_key, status, payload, created_at, updated_at
|
|
429
|
-
FROM durable_jobs;
|
|
430
|
-
DROP TABLE durable_jobs;
|
|
431
|
-
ALTER TABLE durable_jobs_v4 RENAME TO durable_jobs;
|
|
432
|
-
CREATE INDEX idx_durable_jobs_task ON durable_jobs(task_id);
|
|
433
|
-
CREATE INDEX idx_durable_jobs_status ON durable_jobs(status);
|
|
434
|
-
CREATE UNIQUE INDEX idx_durable_jobs_idempotency
|
|
435
|
-
ON durable_jobs(task_id, idempotency_key) WHERE idempotency_key IS NOT NULL;
|
|
436
|
-
`;
|
|
437
|
-
/**
|
|
438
|
-
* Migration 5: telemetry aggregate (Issue 09). The `telemetry` table (migration
|
|
439
|
-
* 1, §4.4) stores the bounded latest-per-key progress window; this companion
|
|
440
|
-
* table holds the authoritative per-Run/generation summary (count, first/last,
|
|
407
|
+
* The `telemetry` table stores the bounded latest-per-key progress window; this companion
|
|
408
|
+
* table holds the authoritative per-Turn/generation summary (count, first/last,
|
|
441
409
|
* max sequence, error count) so aggregates stay accurate after the window is
|
|
442
410
|
* pruned. Triggers maintain it on telemetry INSERT/UPDATE; DELETE intentionally
|
|
443
411
|
* leaves it untouched because pruned rows were still observed.
|
|
444
412
|
*/
|
|
445
|
-
const
|
|
413
|
+
const BASELINE_TELEMETRY_AGGREGATE_SQL = `
|
|
446
414
|
CREATE TABLE IF NOT EXISTS telemetry_aggregate (
|
|
447
415
|
task_id TEXT NOT NULL,
|
|
448
416
|
role_name TEXT NOT NULL,
|
|
449
|
-
|
|
417
|
+
turn_id TEXT NOT NULL,
|
|
450
418
|
generation TEXT NOT NULL,
|
|
451
419
|
first_at TEXT NOT NULL,
|
|
452
420
|
last_at TEXT NOT NULL,
|
|
@@ -454,21 +422,21 @@ CREATE TABLE IF NOT EXISTS telemetry_aggregate (
|
|
|
454
422
|
max_sequence INTEGER,
|
|
455
423
|
error_count INTEGER NOT NULL DEFAULT 0,
|
|
456
424
|
updated_at TEXT NOT NULL,
|
|
457
|
-
PRIMARY KEY (task_id, role_name,
|
|
425
|
+
PRIMARY KEY (task_id, role_name, turn_id, generation)
|
|
458
426
|
) WITHOUT ROWID;
|
|
459
427
|
|
|
460
428
|
CREATE TRIGGER IF NOT EXISTS telemetry_ai AFTER INSERT ON telemetry
|
|
461
429
|
BEGIN
|
|
462
430
|
INSERT INTO telemetry_aggregate
|
|
463
|
-
(task_id, role_name,
|
|
431
|
+
(task_id, role_name, turn_id, generation, first_at, last_at, count, max_sequence, error_count, updated_at)
|
|
464
432
|
VALUES
|
|
465
|
-
(NEW.task_id, NEW.role_name, NEW.
|
|
433
|
+
(NEW.task_id, NEW.role_name, NEW.turn_id, NEW.generation, NEW.received_at, NEW.received_at, 1, NEW.sequence,
|
|
466
434
|
CASE WHEN json_valid(NEW.payload)
|
|
467
435
|
AND (COALESCE(json_extract(NEW.payload, '$.error'), '') <> ''
|
|
468
436
|
OR COALESCE(json_extract(NEW.payload, '$.errorKind'), '') <> '')
|
|
469
437
|
THEN 1 ELSE 0 END,
|
|
470
438
|
NEW.received_at)
|
|
471
|
-
ON CONFLICT(task_id, role_name,
|
|
439
|
+
ON CONFLICT(task_id, role_name, turn_id, generation) DO UPDATE SET
|
|
472
440
|
first_at = MIN(telemetry_aggregate.first_at, excluded.first_at),
|
|
473
441
|
last_at = MAX(telemetry_aggregate.last_at, excluded.last_at),
|
|
474
442
|
count = telemetry_aggregate.count + 1,
|
|
@@ -489,15 +457,15 @@ BEGIN
|
|
|
489
457
|
THEN NEW.sequence ELSE max_sequence END,
|
|
490
458
|
updated_at = NEW.received_at
|
|
491
459
|
WHERE task_id = NEW.task_id AND role_name = NEW.role_name
|
|
492
|
-
AND
|
|
460
|
+
AND turn_id = NEW.turn_id AND generation = NEW.generation;
|
|
493
461
|
END;
|
|
494
462
|
`;
|
|
495
463
|
/**
|
|
496
|
-
*
|
|
464
|
+
* Cross-Round semantic review
|
|
497
465
|
* findings with stable keys and Leader dispositions. Failed execution attempts
|
|
498
466
|
* never create rows; only completed Rounds feed the ledger.
|
|
499
467
|
*/
|
|
500
|
-
const
|
|
468
|
+
const BASELINE_REVIEW_FINDING_SQL = `
|
|
501
469
|
CREATE TABLE IF NOT EXISTS review_findings (
|
|
502
470
|
task_id TEXT NOT NULL,
|
|
503
471
|
finding_id TEXT NOT NULL,
|
|
@@ -511,13 +479,13 @@ CREATE INDEX IF NOT EXISTS idx_review_findings_task ON review_findings(task_id);
|
|
|
511
479
|
CREATE INDEX IF NOT EXISTS idx_review_findings_stable_key ON review_findings(task_id, stable_key);
|
|
512
480
|
`;
|
|
513
481
|
/**
|
|
514
|
-
*
|
|
482
|
+
* Session owner physical identity records.
|
|
515
483
|
*
|
|
516
484
|
* One row per runtime generation, keyed by launch id. The payload column
|
|
517
485
|
* stores the full versioned JSON record; typed columns support the
|
|
518
486
|
* reconciliation queries (task/role lookup, PID liveness).
|
|
519
487
|
*/
|
|
520
|
-
const
|
|
488
|
+
const BASELINE_SESSION_OWNER_SQL = `
|
|
521
489
|
CREATE TABLE IF NOT EXISTS session_owners (
|
|
522
490
|
launch_id TEXT PRIMARY KEY,
|
|
523
491
|
scope TEXT NOT NULL CHECK (scope IN ('task','global')),
|
|
@@ -533,14 +501,14 @@ CREATE INDEX IF NOT EXISTS idx_session_owners_task
|
|
|
533
501
|
ON session_owners(task_id, role_name);
|
|
534
502
|
`;
|
|
535
503
|
/**
|
|
536
|
-
*
|
|
504
|
+
* Resource GC registry.
|
|
537
505
|
*
|
|
538
506
|
* GC-owned table for resource lifecycle records. The registry is GC's own
|
|
539
507
|
* state — it is not part of the aggregate and never participates in aggregate
|
|
540
508
|
* versioning. Records are stored as full versioned JSON in `payload`, with
|
|
541
509
|
* typed columns for the fields GC queries (disposition, kind, task_id).
|
|
542
510
|
*/
|
|
543
|
-
const
|
|
511
|
+
const BASELINE_RESOURCE_REGISTRY_SQL = `
|
|
544
512
|
CREATE TABLE IF NOT EXISTS resource_registry (
|
|
545
513
|
id TEXT PRIMARY KEY,
|
|
546
514
|
kind TEXT NOT NULL,
|
|
@@ -557,14 +525,14 @@ CREATE INDEX IF NOT EXISTS idx_resource_registry_task
|
|
|
557
525
|
ON resource_registry(task_id);
|
|
558
526
|
`;
|
|
559
527
|
/**
|
|
560
|
-
*
|
|
528
|
+
* Content-addressed GateArtifact storage. Content-addressed gate
|
|
561
529
|
* evidence records with per-step logs stored as BLOBs. The artifact key is
|
|
562
530
|
* the SHA-256 of the identity tuple (Project + commit + plan digest +
|
|
563
531
|
* toolchain digest + L2 boundary), so the same tuple always maps to one row.
|
|
564
532
|
* Typed columns support the reuse lookup paths (exact-commit L2 search,
|
|
565
533
|
* Project-level prune) without scanning payloads.
|
|
566
534
|
*/
|
|
567
|
-
const
|
|
535
|
+
const BASELINE_GATE_ARTIFACT_SQL = `
|
|
568
536
|
CREATE TABLE IF NOT EXISTS gate_artifacts (
|
|
569
537
|
key TEXT PRIMARY KEY,
|
|
570
538
|
project_id TEXT NOT NULL,
|
|
@@ -597,29 +565,8 @@ CREATE TABLE IF NOT EXISTS gate_artifact_logs (
|
|
|
597
565
|
FOREIGN KEY (artifact_key) REFERENCES gate_artifacts(key) ON DELETE CASCADE
|
|
598
566
|
);
|
|
599
567
|
`;
|
|
600
|
-
/**
|
|
601
|
-
|
|
602
|
-
*
|
|
603
|
-
* Historical empty mailboxes remain durable, but only mailboxes with a
|
|
604
|
-
* processing or pending batch require scheduling. The partial index contains
|
|
605
|
-
* exactly that unsettled set in target-key order, so the Controller's recovery
|
|
606
|
-
* query is O(log H + ready) instead of scanning every historical mailbox.
|
|
607
|
-
*/
|
|
608
|
-
const MIGRATION_10_SQL = `
|
|
609
|
-
CREATE INDEX IF NOT EXISTS idx_mailboxes_ready
|
|
610
|
-
ON mailboxes(target_key)
|
|
611
|
-
WHERE processing IS NOT NULL OR pending IS NOT NULL;
|
|
612
|
-
`;
|
|
613
|
-
/**
|
|
614
|
-
* Migration 11: bounded current-Session projection for runtime cleanup.
|
|
615
|
-
*
|
|
616
|
-
* RoleSessionSet payloads remain authoritative. This table contains only the
|
|
617
|
-
* current active Agent Session while it is non-stopped, so terminal Session
|
|
618
|
-
* history cannot enlarge Controller cleanup discovery. Runtime writes maintain
|
|
619
|
-
* it in the same transaction as the source payload; this migration performs
|
|
620
|
-
* the one-time deterministic backfill for existing layout-7 Homes.
|
|
621
|
-
*/
|
|
622
|
-
const MIGRATION_11_SQL = `
|
|
568
|
+
/** Bounded current-Session projection for runtime cleanup. */
|
|
569
|
+
const BASELINE_RUNTIME_SESSION_SQL = `
|
|
623
570
|
CREATE TABLE IF NOT EXISTS runtime_session_candidates (
|
|
624
571
|
scope TEXT NOT NULL CHECK (scope IN ('task','global')),
|
|
625
572
|
task_id TEXT NOT NULL,
|
|
@@ -628,7 +575,6 @@ CREATE TABLE IF NOT EXISTS runtime_session_candidates (
|
|
|
628
575
|
adapter_id TEXT NOT NULL,
|
|
629
576
|
native_session_id TEXT NOT NULL,
|
|
630
577
|
launch_id TEXT,
|
|
631
|
-
status TEXT NOT NULL CHECK (status IN ('reserved','ready','running','broken')),
|
|
632
578
|
session_updated_at TEXT NOT NULL,
|
|
633
579
|
cleanup_required INTEGER NOT NULL CHECK (cleanup_required IN (0,1)),
|
|
634
580
|
PRIMARY KEY (scope, task_id, role_name),
|
|
@@ -637,234 +583,20 @@ CREATE TABLE IF NOT EXISTS runtime_session_candidates (
|
|
|
637
583
|
OR (scope = 'global' AND task_id = '')
|
|
638
584
|
),
|
|
639
585
|
CHECK (
|
|
640
|
-
cleanup_required = CASE
|
|
641
|
-
WHEN status NOT IN ('stopped','broken')
|
|
642
|
-
AND (status = 'running' OR launch_id IS NOT NULL)
|
|
643
|
-
THEN 1 ELSE 0
|
|
644
|
-
END
|
|
586
|
+
cleanup_required = CASE WHEN launch_id IS NOT NULL THEN 1 ELSE 0 END
|
|
645
587
|
)
|
|
646
588
|
);
|
|
647
589
|
|
|
648
590
|
CREATE INDEX IF NOT EXISTS idx_runtime_session_cleanup_required
|
|
649
591
|
ON runtime_session_candidates(scope, task_id, role_name)
|
|
650
592
|
WHERE cleanup_required = 1;
|
|
651
|
-
|
|
652
|
-
INSERT INTO runtime_session_candidates (
|
|
653
|
-
scope, task_id, role_name, agent_id, adapter_id, native_session_id,
|
|
654
|
-
launch_id, status, session_updated_at, cleanup_required
|
|
655
|
-
)
|
|
656
|
-
SELECT
|
|
657
|
-
'task', source.task_id, source.role_name,
|
|
658
|
-
json_extract(active.value, '$.agentId'),
|
|
659
|
-
json_extract(active.value, '$.adapterId'),
|
|
660
|
-
json_extract(active.value, '$.nativeSessionId'),
|
|
661
|
-
json_extract(active.value, '$.launchId'),
|
|
662
|
-
json_extract(active.value, '$.status'),
|
|
663
|
-
json_extract(active.value, '$.updatedAt'),
|
|
664
|
-
CASE
|
|
665
|
-
WHEN json_extract(active.value, '$.status') NOT IN ('stopped','broken')
|
|
666
|
-
AND (
|
|
667
|
-
json_extract(active.value, '$.status') = 'running'
|
|
668
|
-
OR json_type(active.value, '$.launchId') = 'text'
|
|
669
|
-
)
|
|
670
|
-
THEN 1 ELSE 0
|
|
671
|
-
END
|
|
672
|
-
FROM role_session_sets AS source
|
|
673
|
-
JOIN json_each(source.payload, '$.sessions') AS active
|
|
674
|
-
ON active.key = json_extract(source.payload, '$.activeAgentId')
|
|
675
|
-
WHERE json_extract(active.value, '$.status') <> 'stopped';
|
|
676
|
-
|
|
677
|
-
INSERT INTO runtime_session_candidates (
|
|
678
|
-
scope, task_id, role_name, agent_id, adapter_id, native_session_id,
|
|
679
|
-
launch_id, status, session_updated_at, cleanup_required
|
|
680
|
-
)
|
|
681
|
-
SELECT
|
|
682
|
-
'global', '', source.name,
|
|
683
|
-
json_extract(active.value, '$.agentId'),
|
|
684
|
-
json_extract(active.value, '$.adapterId'),
|
|
685
|
-
json_extract(active.value, '$.nativeSessionId'),
|
|
686
|
-
json_extract(active.value, '$.launchId'),
|
|
687
|
-
json_extract(active.value, '$.status'),
|
|
688
|
-
json_extract(active.value, '$.updatedAt'),
|
|
689
|
-
CASE
|
|
690
|
-
WHEN json_extract(active.value, '$.status') NOT IN ('stopped','broken')
|
|
691
|
-
AND (
|
|
692
|
-
json_extract(active.value, '$.status') = 'running'
|
|
693
|
-
OR json_type(active.value, '$.launchId') = 'text'
|
|
694
|
-
)
|
|
695
|
-
THEN 1 ELSE 0
|
|
696
|
-
END
|
|
697
|
-
FROM global_role_session_sets AS source
|
|
698
|
-
JOIN json_each(source.payload, '$.sessions') AS active
|
|
699
|
-
ON active.key = json_extract(source.payload, '$.activeAgentId')
|
|
700
|
-
WHERE json_extract(active.value, '$.status') <> 'stopped';
|
|
701
|
-
`;
|
|
702
|
-
/**
|
|
703
|
-
* Migration 12: bounded open-InputRequest lookup for Controller deadlines.
|
|
704
|
-
*
|
|
705
|
-
* The baseline index's historical predicate predates the current
|
|
706
|
-
* open/answered/cancelled status contract and retains terminal rows. This
|
|
707
|
-
* partial index contains only the live InputRequest set, so deadline arming and
|
|
708
|
-
* targeted auto-resolution never scan terminal request history.
|
|
709
|
-
*/
|
|
710
|
-
const MIGRATION_12_SQL = `
|
|
711
|
-
CREATE INDEX IF NOT EXISTS idx_input_requests_open_hot
|
|
712
|
-
ON input_requests(task_id, input_id)
|
|
713
|
-
WHERE status = 'open';
|
|
714
|
-
`;
|
|
715
|
-
/**
|
|
716
|
-
* Migration 13: durable pending native Turn-completion hot projection.
|
|
717
|
-
*
|
|
718
|
-
* A Task Role may have an unsettled Turn completion after the native Session
|
|
719
|
-
* has ended. That completion is independent lifecycle state, so it cannot be
|
|
720
|
-
* discovered through the current-Session projection (which intentionally
|
|
721
|
-
* removes stopped Sessions). Keep one typed row per Task Role and maintain it
|
|
722
|
-
* in the same transaction as `role_session_sets`; deadline discovery can then
|
|
723
|
-
* read this table without parsing a RoleSessionSet or its historical Sessions.
|
|
724
|
-
*/
|
|
725
|
-
const MIGRATION_13_SQL = `
|
|
726
|
-
CREATE TABLE IF NOT EXISTS pending_runtime_turn_completions (
|
|
727
|
-
task_id TEXT NOT NULL,
|
|
728
|
-
role_name TEXT NOT NULL,
|
|
729
|
-
schema_version INTEGER NOT NULL CHECK (schema_version = 1),
|
|
730
|
-
agent_id TEXT NOT NULL,
|
|
731
|
-
native_session_id TEXT NOT NULL,
|
|
732
|
-
turn_id TEXT NOT NULL,
|
|
733
|
-
run_id TEXT NOT NULL,
|
|
734
|
-
summary TEXT NOT NULL,
|
|
735
|
-
observed_at TEXT NOT NULL,
|
|
736
|
-
due_at TEXT NOT NULL,
|
|
737
|
-
PRIMARY KEY (task_id, role_name),
|
|
738
|
-
CHECK (due_at >= observed_at),
|
|
739
|
-
FOREIGN KEY (task_id, role_name)
|
|
740
|
-
REFERENCES role_session_sets(task_id, role_name) ON DELETE CASCADE
|
|
741
|
-
);
|
|
742
|
-
|
|
743
|
-
CREATE INDEX IF NOT EXISTS idx_pending_runtime_turn_completions_due
|
|
744
|
-
ON pending_runtime_turn_completions(task_id, due_at, role_name);
|
|
745
|
-
|
|
746
|
-
INSERT INTO pending_runtime_turn_completions (
|
|
747
|
-
task_id, role_name, schema_version, agent_id, native_session_id,
|
|
748
|
-
turn_id, run_id, summary, observed_at, due_at
|
|
749
|
-
)
|
|
750
|
-
SELECT
|
|
751
|
-
source.task_id,
|
|
752
|
-
source.role_name,
|
|
753
|
-
json_extract(source.payload, '$.pendingTurnCompletion.schemaVersion'),
|
|
754
|
-
json_extract(source.payload, '$.pendingTurnCompletion.agentId'),
|
|
755
|
-
json_extract(source.payload, '$.pendingTurnCompletion.nativeSessionId'),
|
|
756
|
-
json_extract(source.payload, '$.pendingTurnCompletion.turnId'),
|
|
757
|
-
json_extract(source.payload, '$.pendingTurnCompletion.runId'),
|
|
758
|
-
json_extract(source.payload, '$.pendingTurnCompletion.summary'),
|
|
759
|
-
json_extract(source.payload, '$.pendingTurnCompletion.observedAt'),
|
|
760
|
-
json_extract(source.payload, '$.pendingTurnCompletion.dueAt')
|
|
761
|
-
FROM role_session_sets AS source
|
|
762
|
-
WHERE json_type(source.payload, '$.pendingTurnCompletion') = 'object';
|
|
763
|
-
`;
|
|
764
|
-
/** Migration 14: WorkMailbox v2 lanes and the single durable model-input delivery. */
|
|
765
|
-
const MIGRATION_14_SQL = `
|
|
766
|
-
ALTER TABLE mailboxes ADD COLUMN input_delivery TEXT;
|
|
767
|
-
|
|
768
|
-
UPDATE mailboxes
|
|
769
|
-
SET processing = CASE
|
|
770
|
-
WHEN processing IS NULL THEN NULL
|
|
771
|
-
ELSE json_set(
|
|
772
|
-
processing,
|
|
773
|
-
'$.lane', 'normal',
|
|
774
|
-
'$.batch.sources', json_array('migration-v1'),
|
|
775
|
-
'$.batch.dedupeKeys', json_array(
|
|
776
|
-
'mailbox-v1:' || target_key || ':'
|
|
777
|
-
|| json_extract(processing, '$.batch.fromSequence') || '-'
|
|
778
|
-
|| json_extract(processing, '$.batch.toSequence')
|
|
779
|
-
),
|
|
780
|
-
'$.batch.deliveryModes', json_array('followup')
|
|
781
|
-
)
|
|
782
|
-
END;
|
|
783
|
-
|
|
784
|
-
UPDATE mailboxes
|
|
785
|
-
SET input_delivery = json_object(
|
|
786
|
-
'attemptId', json_extract(processing, '$.batchId'),
|
|
787
|
-
'lane', 'normal',
|
|
788
|
-
'mode', 'followup',
|
|
789
|
-
'batch', json(json_extract(processing, '$.batch')),
|
|
790
|
-
'owner', json_extract(processing, '$.owner'),
|
|
791
|
-
'status', 'delivery-unknown',
|
|
792
|
-
'startedAt', json_extract(processing, '$.startedAt'),
|
|
793
|
-
'pushedAt', json_extract(processing, '$.startedAt'),
|
|
794
|
-
'unknownReason', 'migrated-unconfirmed-provider-acceptance',
|
|
795
|
-
'executionRef', json(json_extract(processing, '$.executionRef')),
|
|
796
|
-
'providerFence', json_object(
|
|
797
|
-
'conversationId', (
|
|
798
|
-
SELECT json_extract(
|
|
799
|
-
json_extract(role_session_sets.payload, '$.sessions'),
|
|
800
|
-
'$.' || json_quote(json_extract(role_session_sets.payload, '$.activeAgentId'))
|
|
801
|
-
|| '.nativeSessionId'
|
|
802
|
-
)
|
|
803
|
-
FROM role_session_sets
|
|
804
|
-
WHERE role_session_sets.task_id = mailboxes.task_id
|
|
805
|
-
AND role_session_sets.role_name = mailboxes.role_name
|
|
806
|
-
),
|
|
807
|
-
'activationId', COALESCE((
|
|
808
|
-
SELECT json_extract(
|
|
809
|
-
json_extract(role_session_sets.payload, '$.sessions'),
|
|
810
|
-
'$.' || json_quote(json_extract(role_session_sets.payload, '$.activeAgentId'))
|
|
811
|
-
|| '.launchId'
|
|
812
|
-
)
|
|
813
|
-
FROM role_session_sets
|
|
814
|
-
WHERE role_session_sets.task_id = mailboxes.task_id
|
|
815
|
-
AND role_session_sets.role_name = mailboxes.role_name
|
|
816
|
-
), (
|
|
817
|
-
SELECT json_extract(role_session_sets.payload, '$.inFlight.receiptId')
|
|
818
|
-
FROM role_session_sets
|
|
819
|
-
WHERE role_session_sets.task_id = mailboxes.task_id
|
|
820
|
-
AND role_session_sets.role_name = mailboxes.role_name
|
|
821
|
-
))
|
|
822
|
-
)
|
|
823
|
-
)
|
|
824
|
-
WHERE target_kind = 'role'
|
|
825
|
-
AND json_extract(processing, '$.batchId') LIKE 'agent-input:%';
|
|
826
|
-
|
|
827
|
-
UPDATE mailboxes
|
|
828
|
-
SET processing = NULL
|
|
829
|
-
WHERE input_delivery IS NOT NULL;
|
|
830
|
-
|
|
831
|
-
UPDATE mailboxes
|
|
832
|
-
SET pending = json_object(
|
|
833
|
-
'normal', CASE
|
|
834
|
-
WHEN pending IS NULL THEN json('null')
|
|
835
|
-
ELSE json(json_set(
|
|
836
|
-
pending,
|
|
837
|
-
'$.sources', json_array('migration-v1'),
|
|
838
|
-
'$.dedupeKeys', json_array(
|
|
839
|
-
'mailbox-v1:' || target_key || ':'
|
|
840
|
-
|| json_extract(pending, '$.fromSequence') || '-'
|
|
841
|
-
|| json_extract(pending, '$.toSequence')
|
|
842
|
-
),
|
|
843
|
-
'$.deliveryModes', json_array('followup')
|
|
844
|
-
))
|
|
845
|
-
END,
|
|
846
|
-
'userCorrection', json('null'),
|
|
847
|
-
'cursors', json_object('normal', 0, 'userCorrection', 0),
|
|
848
|
-
'recentDedupeKeys', json_array()
|
|
849
|
-
);
|
|
850
|
-
|
|
851
|
-
DROP INDEX idx_mailboxes_ready;
|
|
852
|
-
CREATE INDEX idx_mailboxes_ready
|
|
853
|
-
ON mailboxes(target_key)
|
|
854
|
-
WHERE processing IS NOT NULL
|
|
855
|
-
OR input_delivery IS NOT NULL
|
|
856
|
-
OR json_type(pending, '$.normal') <> 'null'
|
|
857
|
-
OR json_type(pending, '$.userCorrection') <> 'null';
|
|
858
|
-
|
|
859
|
-
DROP TABLE pending_runtime_turn_completions;
|
|
860
|
-
DROP TABLE IF EXISTS mailbox_signals;
|
|
861
593
|
`;
|
|
862
594
|
/**
|
|
863
|
-
*
|
|
595
|
+
* External publication evidence. Records are immutable;
|
|
864
596
|
* a corrected MR/PR state appends a superseding record with the same
|
|
865
597
|
* external_key, so only the unsuperseded root is globally unique.
|
|
866
598
|
*/
|
|
867
|
-
const
|
|
599
|
+
const BASELINE_PUBLICATION_REFERENCE_SQL = `
|
|
868
600
|
CREATE TABLE IF NOT EXISTS publication_references (
|
|
869
601
|
task_id TEXT NOT NULL,
|
|
870
602
|
publication_id TEXT NOT NULL,
|
|
@@ -895,7 +627,7 @@ CREATE INDEX IF NOT EXISTS idx_publication_references_external
|
|
|
895
627
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_publication_references_external_root
|
|
896
628
|
ON publication_references(external_key) WHERE supersedes IS NULL;
|
|
897
629
|
`;
|
|
898
|
-
const
|
|
630
|
+
const BASELINE_TASK_WAKE_SQL = `
|
|
899
631
|
-- Durable Leader wake ledger (Issue 04 long-term design). A wake is a
|
|
900
632
|
-- notification envelope, not a context dump: the record holds the aggregated
|
|
901
633
|
-- reason tags and the delta window; the Agent reads delta content on demand.
|
|
@@ -904,7 +636,7 @@ CREATE TABLE IF NOT EXISTS task_wakes (
|
|
|
904
636
|
wake_id TEXT NOT NULL,
|
|
905
637
|
seq INTEGER NOT NULL,
|
|
906
638
|
status TEXT NOT NULL CHECK (status IN ('dispatched','consumed')),
|
|
907
|
-
|
|
639
|
+
turn_id TEXT,
|
|
908
640
|
from_cursor TEXT NOT NULL,
|
|
909
641
|
to_cursor TEXT NOT NULL,
|
|
910
642
|
reasons TEXT NOT NULL,
|
|
@@ -915,8 +647,8 @@ CREATE TABLE IF NOT EXISTS task_wakes (
|
|
|
915
647
|
);
|
|
916
648
|
CREATE INDEX IF NOT EXISTS idx_task_wakes_seq ON task_wakes(task_id, seq);
|
|
917
649
|
`;
|
|
918
|
-
/**
|
|
919
|
-
const
|
|
650
|
+
/** Immutable, Task-scoped ContextSnapshot records. */
|
|
651
|
+
const BASELINE_CONTEXT_SNAPSHOT_SQL = `
|
|
920
652
|
CREATE TABLE IF NOT EXISTS context_snapshots (
|
|
921
653
|
task_id TEXT NOT NULL,
|
|
922
654
|
snapshot_id TEXT NOT NULL,
|
|
@@ -934,82 +666,23 @@ CREATE TABLE IF NOT EXISTS context_snapshots (
|
|
|
934
666
|
CREATE UNIQUE INDEX IF NOT EXISTS idx_context_snapshots_scope_sequence
|
|
935
667
|
ON context_snapshots(task_id, scope, COALESCE(scope_ref, ''), sequence);
|
|
936
668
|
`;
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
scope TEXT NOT NULL CHECK (scope IN ('task','global')),
|
|
953
|
-
task_id TEXT NOT NULL,
|
|
954
|
-
role_name TEXT NOT NULL,
|
|
955
|
-
agent_id TEXT NOT NULL,
|
|
956
|
-
adapter_id TEXT NOT NULL,
|
|
957
|
-
native_session_id TEXT NOT NULL,
|
|
958
|
-
launch_id TEXT,
|
|
959
|
-
session_updated_at TEXT NOT NULL,
|
|
960
|
-
cleanup_required INTEGER NOT NULL CHECK (cleanup_required IN (0,1)),
|
|
961
|
-
PRIMARY KEY (scope, task_id, role_name),
|
|
962
|
-
CHECK (
|
|
963
|
-
(scope = 'task' AND length(task_id) > 0)
|
|
964
|
-
OR (scope = 'global' AND task_id = '')
|
|
965
|
-
),
|
|
966
|
-
CHECK (
|
|
967
|
-
cleanup_required = CASE WHEN launch_id IS NOT NULL THEN 1 ELSE 0 END
|
|
968
|
-
)
|
|
969
|
-
);
|
|
970
|
-
|
|
971
|
-
INSERT INTO runtime_session_candidates (
|
|
972
|
-
scope, task_id, role_name, agent_id, adapter_id, native_session_id,
|
|
973
|
-
launch_id, session_updated_at, cleanup_required
|
|
974
|
-
)
|
|
975
|
-
SELECT
|
|
976
|
-
scope, task_id, role_name, agent_id, adapter_id, native_session_id,
|
|
977
|
-
launch_id, session_updated_at,
|
|
978
|
-
CASE WHEN launch_id IS NOT NULL THEN 1 ELSE 0 END
|
|
979
|
-
FROM runtime_session_candidates_v11
|
|
980
|
-
WHERE status IN ('reserved','ready','running');
|
|
981
|
-
|
|
982
|
-
DROP TABLE runtime_session_candidates_v11;
|
|
983
|
-
|
|
984
|
-
CREATE INDEX idx_runtime_session_cleanup_required
|
|
985
|
-
ON runtime_session_candidates(scope, task_id, role_name)
|
|
986
|
-
WHERE cleanup_required = 1;
|
|
987
|
-
`;
|
|
988
|
-
/**
|
|
989
|
-
* The single forward migration history for current SQLite Homes. New durable
|
|
990
|
-
* layout, aggregate, and record changes belong here and must be expressible as
|
|
991
|
-
* one atomic in-place transaction. The separate logical migration registry is
|
|
992
|
-
* only the bridge from valid pre-SQLite Homes and is allowed to rebuild them.
|
|
993
|
-
*/
|
|
669
|
+
const MIGRATION_1_SQL = [
|
|
670
|
+
BASELINE_CORE_SQL,
|
|
671
|
+
BASELINE_JOB_AND_RELEASE_SQL,
|
|
672
|
+
BASELINE_JOB_CALLER_SQL,
|
|
673
|
+
BASELINE_TELEMETRY_AGGREGATE_SQL,
|
|
674
|
+
BASELINE_REVIEW_FINDING_SQL,
|
|
675
|
+
BASELINE_SESSION_OWNER_SQL,
|
|
676
|
+
BASELINE_RESOURCE_REGISTRY_SQL,
|
|
677
|
+
BASELINE_GATE_ARTIFACT_SQL,
|
|
678
|
+
BASELINE_RUNTIME_SESSION_SQL,
|
|
679
|
+
BASELINE_PUBLICATION_REFERENCE_SQL,
|
|
680
|
+
BASELINE_TASK_WAKE_SQL,
|
|
681
|
+
BASELINE_CONTEXT_SNAPSHOT_SQL
|
|
682
|
+
].join("\n");
|
|
683
|
+
/** Future compatible releases append new migrations after this baseline. */
|
|
994
684
|
const MIGRATIONS = [
|
|
995
|
-
{ version: 1, axis: "layout", sql: MIGRATION_1_SQL }
|
|
996
|
-
{ version: 2, axis: "record", recordKind: "durableJob+capability-grant+release-workflow", sql: MIGRATION_2_SQL },
|
|
997
|
-
{ version: 3, axis: "record", recordKind: "jobCallerKeyHash", sql: MIGRATION_3_SQL },
|
|
998
|
-
{ version: 4, axis: "record", recordKind: "durableJob", sql: MIGRATION_4_SQL },
|
|
999
|
-
{ version: 5, axis: "record", recordKind: "telemetryAggregate", sql: MIGRATION_5_SQL },
|
|
1000
|
-
{ version: 6, axis: "record", recordKind: "reviewFinding", sql: MIGRATION_6_SQL },
|
|
1001
|
-
{ version: 7, axis: "record", recordKind: "sessionOwner", sql: MIGRATION_7_SQL },
|
|
1002
|
-
{ version: 8, axis: "record", recordKind: "resource-registry", sql: MIGRATION_8_SQL },
|
|
1003
|
-
{ version: 9, axis: "record", recordKind: "gateArtifact", sql: MIGRATION_9_SQL },
|
|
1004
|
-
{ version: 10, axis: "layout", sql: MIGRATION_10_SQL },
|
|
1005
|
-
{ version: 11, axis: "layout", sql: MIGRATION_11_SQL },
|
|
1006
|
-
{ version: 12, axis: "layout", sql: MIGRATION_12_SQL },
|
|
1007
|
-
{ version: 13, axis: "layout", sql: MIGRATION_13_SQL },
|
|
1008
|
-
{ version: 14, axis: "record", recordKind: "workMailbox", sql: MIGRATION_14_SQL },
|
|
1009
|
-
{ version: 15, axis: "record", recordKind: "publicationReference", sql: MIGRATION_15_SQL },
|
|
1010
|
-
{ version: 16, axis: "record", recordKind: "taskWake", sql: MIGRATION_16_SQL },
|
|
1011
|
-
{ version: 17, axis: "record", recordKind: "contextSnapshot", sql: MIGRATION_17_SQL },
|
|
1012
|
-
{ version: 18, axis: "layout", sql: MIGRATION_18_SQL }
|
|
685
|
+
{ version: 1, axis: "layout", sql: MIGRATION_1_SQL }
|
|
1013
686
|
];
|
|
1014
687
|
/** Current hot-path indexes whose absence would invalidate a current Home. */
|
|
1015
688
|
const REQUIRED_SCHEMA_INDEXES = [
|
|
@@ -1024,7 +697,7 @@ function checksum(sql) {
|
|
|
1024
697
|
* A SQLite Home is only safe to open when its migration ledger proves exactly
|
|
1025
698
|
* which schema definition was applied. The ledger is durable metadata, not a
|
|
1026
699
|
* best-effort cache: a missing row, a changed checksum, or an unknown version
|
|
1027
|
-
* must stop startup before any pending
|
|
700
|
+
* must stop startup before any pending schema step is applied.
|
|
1028
701
|
*/
|
|
1029
702
|
export class SqliteSchemaMigrationError extends Error {
|
|
1030
703
|
constructor(detail, subject = "metadata") {
|
|
@@ -1127,82 +800,7 @@ function validateSchemaObjects(db) {
|
|
|
1127
800
|
}
|
|
1128
801
|
}
|
|
1129
802
|
}
|
|
1130
|
-
/**
|
|
1131
|
-
function validatePendingProjectionBackfillSources(db) {
|
|
1132
|
-
const existing = db.prepare(`SELECT name
|
|
1133
|
-
FROM sqlite_master
|
|
1134
|
-
WHERE name IN (
|
|
1135
|
-
'pending_runtime_turn_completions',
|
|
1136
|
-
'idx_pending_runtime_turn_completions_due'
|
|
1137
|
-
)
|
|
1138
|
-
LIMIT 1`).get();
|
|
1139
|
-
if (existing !== undefined) {
|
|
1140
|
-
throw new SqliteSchemaMigrationError(`schema object '${existing.name}' exists before migration 13 is recorded`, "backfill");
|
|
1141
|
-
}
|
|
1142
|
-
const mismatch = db.prepare(`SELECT task_id, role_name
|
|
1143
|
-
FROM role_session_sets
|
|
1144
|
-
WHERE COALESCE(json_type(payload, '$.pendingTurnCompletion'), 'missing')
|
|
1145
|
-
NOT IN ('null', 'object')
|
|
1146
|
-
OR (
|
|
1147
|
-
json_type(payload, '$.pendingTurnCompletion') = 'object'
|
|
1148
|
-
AND (
|
|
1149
|
-
json_extract(payload, '$.owner.scope') <> 'task'
|
|
1150
|
-
OR json_extract(payload, '$.owner.taskId') IS NULL
|
|
1151
|
-
OR json_extract(payload, '$.owner.taskId') <> task_id
|
|
1152
|
-
OR json_extract(payload, '$.owner.roleName') IS NULL
|
|
1153
|
-
OR json_extract(payload, '$.owner.roleName') <> role_name
|
|
1154
|
-
OR json_extract(payload, '$.pendingTurnCompletion.taskId') IS NULL
|
|
1155
|
-
OR json_extract(payload, '$.pendingTurnCompletion.taskId') <> task_id
|
|
1156
|
-
OR json_extract(payload, '$.pendingTurnCompletion.roleName') IS NULL
|
|
1157
|
-
OR json_extract(payload, '$.pendingTurnCompletion.roleName') <> role_name
|
|
1158
|
-
OR json_extract(payload, '$.pendingTurnCompletion.schemaVersion') IS NOT 1
|
|
1159
|
-
OR COALESCE(json_type(payload, '$.pendingTurnCompletion.agentId'), 'missing') <> 'text'
|
|
1160
|
-
OR COALESCE(json_type(payload, '$.pendingTurnCompletion.nativeSessionId'), 'missing') <> 'text'
|
|
1161
|
-
OR COALESCE(json_type(payload, '$.pendingTurnCompletion.turnId'), 'missing') <> 'text'
|
|
1162
|
-
OR COALESCE(json_type(payload, '$.pendingTurnCompletion.runId'), 'missing') <> 'text'
|
|
1163
|
-
OR COALESCE(json_type(payload, '$.pendingTurnCompletion.summary'), 'missing') <> 'text'
|
|
1164
|
-
OR COALESCE(json_type(payload, '$.pendingTurnCompletion.observedAt'), 'missing') <> 'text'
|
|
1165
|
-
OR COALESCE(json_type(payload, '$.pendingTurnCompletion.dueAt'), 'missing') <> 'text'
|
|
1166
|
-
OR COALESCE(json_type(payload, '$.inFlight'), 'missing') <> 'object'
|
|
1167
|
-
OR COALESCE(json_type(payload, '$.activeAgentId'), 'missing') <> 'text'
|
|
1168
|
-
OR COALESCE(json_type(payload, '$.inFlight.agentId'), 'missing') <> 'text'
|
|
1169
|
-
OR COALESCE(json_type(payload, '$.inFlight.runId'), 'missing') <> 'text'
|
|
1170
|
-
OR json_extract(payload, '$.activeAgentId')
|
|
1171
|
-
<> json_extract(payload, '$.pendingTurnCompletion.agentId')
|
|
1172
|
-
OR json_extract(payload, '$.inFlight.agentId')
|
|
1173
|
-
<> json_extract(payload, '$.pendingTurnCompletion.agentId')
|
|
1174
|
-
OR json_extract(payload, '$.inFlight.runId')
|
|
1175
|
-
<> json_extract(payload, '$.pendingTurnCompletion.runId')
|
|
1176
|
-
OR COALESCE(json_type(payload, '$.inFlight.pushedAt'), 'missing') <> 'text'
|
|
1177
|
-
OR COALESCE(
|
|
1178
|
-
json_type(
|
|
1179
|
-
payload,
|
|
1180
|
-
'$.sessions.' || json_quote(
|
|
1181
|
-
json_extract(payload, '$.pendingTurnCompletion.agentId')
|
|
1182
|
-
) || '.nativeSessionId'
|
|
1183
|
-
),
|
|
1184
|
-
'missing'
|
|
1185
|
-
) <> 'text'
|
|
1186
|
-
OR json_extract(
|
|
1187
|
-
payload,
|
|
1188
|
-
'$.sessions.' || json_quote(
|
|
1189
|
-
json_extract(payload, '$.pendingTurnCompletion.agentId')
|
|
1190
|
-
) || '.nativeSessionId'
|
|
1191
|
-
) <> json_extract(payload, '$.pendingTurnCompletion.nativeSessionId')
|
|
1192
|
-
OR json_extract(payload, '$.pendingTurnCompletion.dueAt')
|
|
1193
|
-
< json_extract(payload, '$.pendingTurnCompletion.observedAt')
|
|
1194
|
-
)
|
|
1195
|
-
)
|
|
1196
|
-
LIMIT 1`).get();
|
|
1197
|
-
if (mismatch !== undefined) {
|
|
1198
|
-
throw new SqliteSchemaMigrationError(`pending Turn completion source is invalid for ${mismatch.task_id}/${mismatch.role_name}`, "backfill");
|
|
1199
|
-
}
|
|
1200
|
-
}
|
|
1201
|
-
/**
|
|
1202
|
-
* Inspect a database's checksummed migration prefix without changing it.
|
|
1203
|
-
* Pending versions are a supported upgrade state; malformed, gapped, changed,
|
|
1204
|
-
* or future ledger entries remain explicit integrity failures.
|
|
1205
|
-
*/
|
|
803
|
+
/** Inspect a recognized migration prefix without changing it. */
|
|
1206
804
|
export function inspectSqliteSchemaMigrations(db) {
|
|
1207
805
|
const ledgerWasCreated = ensureMigrationLedger(db, "validate");
|
|
1208
806
|
const applied = validateAppliedMigrations(db, ledgerWasCreated);
|
|
@@ -1241,14 +839,11 @@ export function migrateSqliteSchema(db, options) {
|
|
|
1241
839
|
// skipping a later schema/data step.
|
|
1242
840
|
const applied = validateAppliedMigrations(db, ledgerWasCreated);
|
|
1243
841
|
const pending = MIGRATIONS.filter((migration) => !applied.has(migration.version));
|
|
1244
|
-
if (
|
|
1245
|
-
throw new SqliteSchemaMigrationError(`
|
|
1246
|
-
+ "run the explicit storage upgrade before opening this database", "admission");
|
|
842
|
+
if (!ledgerWasCreated && pending.length > 0 && options.mode === "validate") {
|
|
843
|
+
throw new SqliteSchemaMigrationError(`SQLite schema ${applied.size} requires an explicit upgrade to ${SQLITE_SCHEMA_VERSION}`, "admission");
|
|
1247
844
|
}
|
|
1248
845
|
const newlyApplied = [];
|
|
1249
846
|
for (const migration of pending) {
|
|
1250
|
-
if (migration.version === 13)
|
|
1251
|
-
validatePendingProjectionBackfillSources(db);
|
|
1252
847
|
db.exec(migration.sql);
|
|
1253
848
|
db.prepare(`INSERT INTO schema_migrations (version, axis, record_kind, applied_at, checksum)
|
|
1254
849
|
VALUES (?, ?, ?, ?, ?)`).run(migration.version, migration.axis, migration.recordKind ?? null, new Date().toISOString(), checksum(migration.sql));
|
|
@@ -1288,8 +883,8 @@ export const SQLITE_SCHEMA_TABLES = [
|
|
|
1288
883
|
"work_items",
|
|
1289
884
|
"work_item_candidates",
|
|
1290
885
|
"context_snapshots",
|
|
1291
|
-
"
|
|
1292
|
-
"
|
|
886
|
+
"turns",
|
|
887
|
+
"active_turns",
|
|
1293
888
|
"review_rounds",
|
|
1294
889
|
"review_findings",
|
|
1295
890
|
"change_sets",
|
|
@@ -1305,6 +900,7 @@ export const SQLITE_SCHEMA_TABLES = [
|
|
|
1305
900
|
"capability_grants",
|
|
1306
901
|
"release_workflows",
|
|
1307
902
|
"publication_references",
|
|
903
|
+
"task_wakes",
|
|
1308
904
|
"session_owners",
|
|
1309
905
|
"runtime_session_candidates",
|
|
1310
906
|
"resource_registry",
|