@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
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
3
|
import Database from "better-sqlite3";
|
|
4
|
-
import {
|
|
4
|
+
import { DEFAULT_TURN_CAP, DEFAULT_TERMINAL_KEEP } from "./telemetryConfig.js";
|
|
5
5
|
import { migrateSqliteSchema } from "../storage/sqliteSchema.js";
|
|
6
|
-
import { COMMITTED_DATABASE_FILENAME } from "../storage/
|
|
6
|
+
import { CURRENT_DATABASE_FILENAME as COMMITTED_DATABASE_FILENAME } from "../storage/currentTaskStore.js";
|
|
7
7
|
/**
|
|
8
8
|
* Default sidecar implementation: the telemetry tables live inside the Home's
|
|
9
|
-
* authoritative `yui.db
|
|
9
|
+
* authoritative `yui.db`, so a Home has one durable database
|
|
10
10
|
* has one file to manage, back up, and migrate. The store opens its own
|
|
11
11
|
* connection to that file: the write path is a single serialized writer (the
|
|
12
12
|
* "WAL worker") and stays isolated from the business store's connection.
|
|
@@ -16,9 +16,9 @@ import { COMMITTED_DATABASE_FILENAME } from "../storage/upgrade/sqliteStateMigra
|
|
|
16
16
|
* batches. Queue overflow and database failures increment `dropped` and record
|
|
17
17
|
* a health warning — the semantic lane is never affected.
|
|
18
18
|
*
|
|
19
|
-
* The telemetry tables are maintained by the centralized schema
|
|
20
|
-
* `telemetry`
|
|
21
|
-
* `telemetry_aggregate`
|
|
19
|
+
* The telemetry tables are maintained by the centralized schema baseline:
|
|
20
|
+
* `telemetry` holds the bounded latest-per-key window and
|
|
21
|
+
* `telemetry_aggregate` holds the authoritative per-Turn/generation
|
|
22
22
|
* summary, maintained by triggers so it survives window pruning.
|
|
23
23
|
*/
|
|
24
24
|
const MAX_PAGE_LIMIT = 500;
|
|
@@ -26,7 +26,7 @@ export class SqliteTelemetryStore {
|
|
|
26
26
|
mode;
|
|
27
27
|
#path;
|
|
28
28
|
#terminalKeep;
|
|
29
|
-
#
|
|
29
|
+
#turnCap;
|
|
30
30
|
#maxPending;
|
|
31
31
|
#db = null;
|
|
32
32
|
#failed = false;
|
|
@@ -41,7 +41,7 @@ export class SqliteTelemetryStore {
|
|
|
41
41
|
this.mode = options.mode ?? "on";
|
|
42
42
|
this.#path = join(home, COMMITTED_DATABASE_FILENAME);
|
|
43
43
|
this.#terminalKeep = options.terminalKeep ?? DEFAULT_TERMINAL_KEEP;
|
|
44
|
-
this.#
|
|
44
|
+
this.#turnCap = options.turnCap ?? DEFAULT_TURN_CAP;
|
|
45
45
|
this.#maxPending = options.maxPending ?? 10_000;
|
|
46
46
|
}
|
|
47
47
|
// -- TelemetrySink -------------------------------------------------------------
|
|
@@ -89,52 +89,52 @@ export class SqliteTelemetryStore {
|
|
|
89
89
|
this.#db = null;
|
|
90
90
|
}
|
|
91
91
|
// -- TelemetryReader -----------------------------------------------------------
|
|
92
|
-
count(taskId,
|
|
92
|
+
count(taskId, turnId) {
|
|
93
93
|
const db = this.#ensureDb();
|
|
94
94
|
if (db === null)
|
|
95
95
|
return 0;
|
|
96
96
|
if (taskId === undefined) {
|
|
97
97
|
return db.prepare("SELECT COUNT(*) AS n FROM telemetry").get().n;
|
|
98
98
|
}
|
|
99
|
-
if (
|
|
99
|
+
if (turnId === undefined) {
|
|
100
100
|
return db.prepare("SELECT COUNT(*) AS n FROM telemetry WHERE task_id = ?").get(taskId).n;
|
|
101
101
|
}
|
|
102
|
-
return db.prepare("SELECT COUNT(*) AS n FROM telemetry WHERE task_id = ? AND
|
|
102
|
+
return db.prepare("SELECT COUNT(*) AS n FROM telemetry WHERE task_id = ? AND turn_id = ?").get(taskId, turnId).n;
|
|
103
103
|
}
|
|
104
|
-
list(taskId,
|
|
104
|
+
list(taskId, turnId, page = { limit: 100, offset: 0 }) {
|
|
105
105
|
const db = this.#ensureDb();
|
|
106
106
|
if (db === null)
|
|
107
107
|
return { items: [], nextOffset: null };
|
|
108
108
|
const limit = Math.min(Math.max(1, Math.trunc(page.limit)), MAX_PAGE_LIMIT);
|
|
109
109
|
const offset = Math.max(0, Math.trunc(page.offset));
|
|
110
|
-
const rows =
|
|
111
|
-
? db.prepare("SELECT task_id, role_name,
|
|
112
|
-
: db.prepare("SELECT task_id, role_name,
|
|
110
|
+
const rows = turnId === undefined
|
|
111
|
+
? db.prepare("SELECT task_id, role_name, turn_id, generation, progress_id, sequence, payload, received_at FROM telemetry WHERE task_id = ? ORDER BY received_at, progress_id LIMIT ? OFFSET ?").all(taskId, limit, offset)
|
|
112
|
+
: db.prepare("SELECT task_id, role_name, turn_id, generation, progress_id, sequence, payload, received_at FROM telemetry WHERE task_id = ? AND turn_id = ? ORDER BY received_at, progress_id LIMIT ? OFFSET ?").all(taskId, turnId, limit, offset);
|
|
113
113
|
const items = rows.map(rowToEntry);
|
|
114
|
-
const total = this.count(taskId,
|
|
114
|
+
const total = this.count(taskId, turnId);
|
|
115
115
|
const nextOffset = offset + items.length < total ? offset + items.length : null;
|
|
116
116
|
return { items, nextOffset };
|
|
117
117
|
}
|
|
118
|
-
aggregate(taskId,
|
|
118
|
+
aggregate(taskId, turnId) {
|
|
119
119
|
const db = this.#ensureDb();
|
|
120
120
|
if (db === null)
|
|
121
121
|
return null;
|
|
122
|
-
const rows = db.prepare("SELECT task_id, role_name,
|
|
122
|
+
const rows = db.prepare("SELECT task_id, role_name, turn_id, generation, first_at, last_at, count, max_sequence, error_count FROM telemetry_aggregate WHERE task_id = ? AND turn_id = ?").all(taskId, turnId);
|
|
123
123
|
if (rows.length === 0)
|
|
124
124
|
return null;
|
|
125
125
|
return mergeAggregates(rows);
|
|
126
126
|
}
|
|
127
|
-
aggregateGeneration(taskId, roleName,
|
|
127
|
+
aggregateGeneration(taskId, roleName, turnId, generation) {
|
|
128
128
|
const db = this.#ensureDb();
|
|
129
129
|
if (db === null)
|
|
130
130
|
return null;
|
|
131
|
-
const row = db.prepare("SELECT task_id, role_name,
|
|
131
|
+
const row = db.prepare("SELECT task_id, role_name, turn_id, generation, first_at, last_at, count, max_sequence, error_count FROM telemetry_aggregate WHERE task_id = ? AND role_name = ? AND turn_id = ? AND generation = ?").get(taskId, roleName, turnId, generation);
|
|
132
132
|
if (row === undefined)
|
|
133
133
|
return null;
|
|
134
134
|
return {
|
|
135
135
|
taskId: row.task_id,
|
|
136
136
|
roleName: row.role_name,
|
|
137
|
-
|
|
137
|
+
turnId: row.turn_id,
|
|
138
138
|
generation: row.generation,
|
|
139
139
|
firstAt: row.first_at,
|
|
140
140
|
lastAt: row.last_at,
|
|
@@ -143,15 +143,15 @@ export class SqliteTelemetryStore {
|
|
|
143
143
|
errorCount: row.error_count
|
|
144
144
|
};
|
|
145
145
|
}
|
|
146
|
-
|
|
146
|
+
listTurnAggregates(taskId) {
|
|
147
147
|
const db = this.#ensureDb();
|
|
148
148
|
if (db === null)
|
|
149
149
|
return [];
|
|
150
|
-
const rows = db.prepare("SELECT task_id, role_name,
|
|
150
|
+
const rows = db.prepare("SELECT task_id, role_name, turn_id, generation, first_at, last_at, count, max_sequence, error_count FROM telemetry_aggregate WHERE task_id = ? ORDER BY turn_id, generation").all(taskId);
|
|
151
151
|
return rows.map((row) => ({
|
|
152
152
|
taskId: row.task_id,
|
|
153
153
|
roleName: row.role_name,
|
|
154
|
-
|
|
154
|
+
turnId: row.turn_id,
|
|
155
155
|
generation: row.generation,
|
|
156
156
|
firstAt: row.first_at,
|
|
157
157
|
lastAt: row.last_at,
|
|
@@ -164,32 +164,32 @@ export class SqliteTelemetryStore {
|
|
|
164
164
|
return this.#applied;
|
|
165
165
|
}
|
|
166
166
|
// -- retention -----------------------------------------------------------------
|
|
167
|
-
pruneGeneration(taskId, roleName,
|
|
167
|
+
pruneGeneration(taskId, roleName, turnId, generation, keep = this.#terminalKeep) {
|
|
168
168
|
const db = this.#ensureDb();
|
|
169
169
|
if (db === null)
|
|
170
170
|
return 0;
|
|
171
171
|
const result = db.prepare(`DELETE FROM telemetry
|
|
172
|
-
WHERE task_id = ? AND role_name = ? AND
|
|
173
|
-
AND (task_id, role_name,
|
|
174
|
-
SELECT task_id, role_name,
|
|
175
|
-
WHERE task_id = ? AND role_name = ? AND
|
|
172
|
+
WHERE task_id = ? AND role_name = ? AND turn_id = ? AND generation = ?
|
|
173
|
+
AND (task_id, role_name, turn_id, generation, progress_id) NOT IN (
|
|
174
|
+
SELECT task_id, role_name, turn_id, generation, progress_id FROM telemetry
|
|
175
|
+
WHERE task_id = ? AND role_name = ? AND turn_id = ? AND generation = ?
|
|
176
176
|
ORDER BY COALESCE(sequence, -1) DESC, received_at DESC, progress_id ASC
|
|
177
177
|
LIMIT ?
|
|
178
|
-
)`).run(taskId, roleName,
|
|
178
|
+
)`).run(taskId, roleName, turnId, generation, taskId, roleName, turnId, generation, keep);
|
|
179
179
|
return result.changes;
|
|
180
180
|
}
|
|
181
|
-
|
|
181
|
+
capTurn(taskId, turnId, cap = this.#turnCap) {
|
|
182
182
|
const db = this.#ensureDb();
|
|
183
183
|
if (db === null)
|
|
184
184
|
return 0;
|
|
185
185
|
const result = db.prepare(`DELETE FROM telemetry
|
|
186
|
-
WHERE task_id = ? AND
|
|
187
|
-
AND (task_id, role_name,
|
|
188
|
-
SELECT task_id, role_name,
|
|
189
|
-
WHERE task_id = ? AND
|
|
186
|
+
WHERE task_id = ? AND turn_id = ?
|
|
187
|
+
AND (task_id, role_name, turn_id, generation, progress_id) NOT IN (
|
|
188
|
+
SELECT task_id, role_name, turn_id, generation, progress_id FROM telemetry
|
|
189
|
+
WHERE task_id = ? AND turn_id = ?
|
|
190
190
|
ORDER BY COALESCE(sequence, -1) DESC, received_at DESC, progress_id ASC
|
|
191
191
|
LIMIT ?
|
|
192
|
-
)`).run(taskId,
|
|
192
|
+
)`).run(taskId, turnId, taskId, turnId, cap);
|
|
193
193
|
return result.changes;
|
|
194
194
|
}
|
|
195
195
|
importGeneration(entries, aggregate) {
|
|
@@ -198,18 +198,18 @@ export class SqliteTelemetryStore {
|
|
|
198
198
|
this.#dropped += entries.length;
|
|
199
199
|
return;
|
|
200
200
|
}
|
|
201
|
-
const upsert = db.prepare(`INSERT INTO telemetry (task_id, role_name,
|
|
201
|
+
const upsert = db.prepare(`INSERT INTO telemetry (task_id, role_name, turn_id, generation, progress_id, sequence, payload, received_at)
|
|
202
202
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
203
|
-
ON CONFLICT(task_id, role_name,
|
|
203
|
+
ON CONFLICT(task_id, role_name, turn_id, generation, progress_id) DO UPDATE SET
|
|
204
204
|
sequence = excluded.sequence,
|
|
205
205
|
payload = excluded.payload,
|
|
206
206
|
received_at = excluded.received_at
|
|
207
207
|
WHERE COALESCE(excluded.sequence, -1) > COALESCE(telemetry.sequence, -1)
|
|
208
208
|
OR (excluded.sequence IS telemetry.sequence AND excluded.received_at >= telemetry.received_at)`);
|
|
209
209
|
const upsertAggregate = db.prepare(`INSERT INTO telemetry_aggregate
|
|
210
|
-
(task_id, role_name,
|
|
210
|
+
(task_id, role_name, turn_id, generation, first_at, last_at, count, max_sequence, error_count, updated_at)
|
|
211
211
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
212
|
-
ON CONFLICT(task_id, role_name,
|
|
212
|
+
ON CONFLICT(task_id, role_name, turn_id, generation) DO UPDATE SET
|
|
213
213
|
first_at = excluded.first_at,
|
|
214
214
|
last_at = excluded.last_at,
|
|
215
215
|
count = excluded.count,
|
|
@@ -218,9 +218,9 @@ export class SqliteTelemetryStore {
|
|
|
218
218
|
updated_at = excluded.updated_at`);
|
|
219
219
|
db.transaction(() => {
|
|
220
220
|
for (const entry of entries) {
|
|
221
|
-
upsert.run(entry.taskId, entry.roleName, entry.
|
|
221
|
+
upsert.run(entry.taskId, entry.roleName, entry.turnId, entry.generation, entry.progressId, entry.sequence ?? null, JSON.stringify(entry.payload), entry.receivedAt);
|
|
222
222
|
}
|
|
223
|
-
upsertAggregate.run(aggregate.taskId, aggregate.roleName, aggregate.
|
|
223
|
+
upsertAggregate.run(aggregate.taskId, aggregate.roleName, aggregate.turnId, aggregate.generation, aggregate.firstAt, aggregate.lastAt, aggregate.count, aggregate.maxSequence, aggregate.errorCount, aggregate.lastAt);
|
|
224
224
|
})();
|
|
225
225
|
this.#applied += entries.length;
|
|
226
226
|
}
|
|
@@ -277,26 +277,26 @@ export class SqliteTelemetryStore {
|
|
|
277
277
|
}
|
|
278
278
|
const batch = new Map(this.#pending);
|
|
279
279
|
this.#pending.clear();
|
|
280
|
-
const upsert = db.prepare(`INSERT INTO telemetry (task_id, role_name,
|
|
280
|
+
const upsert = db.prepare(`INSERT INTO telemetry (task_id, role_name, turn_id, generation, progress_id, sequence, payload, received_at)
|
|
281
281
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
|
282
|
-
ON CONFLICT(task_id, role_name,
|
|
282
|
+
ON CONFLICT(task_id, role_name, turn_id, generation, progress_id) DO UPDATE SET
|
|
283
283
|
sequence = excluded.sequence,
|
|
284
284
|
payload = excluded.payload,
|
|
285
285
|
received_at = excluded.received_at
|
|
286
286
|
WHERE COALESCE(excluded.sequence, -1) > COALESCE(telemetry.sequence, -1)
|
|
287
287
|
OR (excluded.sequence IS telemetry.sequence AND excluded.received_at >= telemetry.received_at)`);
|
|
288
288
|
try {
|
|
289
|
-
const
|
|
289
|
+
const touchedTurns = new Map();
|
|
290
290
|
db.transaction(() => {
|
|
291
291
|
for (const entry of batch.values()) {
|
|
292
|
-
upsert.run(entry.taskId, entry.roleName, entry.
|
|
293
|
-
|
|
292
|
+
upsert.run(entry.taskId, entry.roleName, entry.turnId, entry.generation, entry.progressId, entry.sequence ?? null, JSON.stringify(entry.payload), entry.receivedAt);
|
|
293
|
+
touchedTurns.set(`${entry.taskId}\0${entry.turnId}`, {
|
|
294
294
|
taskId: entry.taskId,
|
|
295
|
-
|
|
295
|
+
turnId: entry.turnId
|
|
296
296
|
});
|
|
297
297
|
}
|
|
298
|
-
for (const { taskId,
|
|
299
|
-
this.
|
|
298
|
+
for (const { taskId, turnId } of touchedTurns.values()) {
|
|
299
|
+
this.capTurn(taskId, turnId);
|
|
300
300
|
}
|
|
301
301
|
})();
|
|
302
302
|
this.#applied += batch.size;
|
|
@@ -308,7 +308,7 @@ export class SqliteTelemetryStore {
|
|
|
308
308
|
}
|
|
309
309
|
}
|
|
310
310
|
function pendingKey(entry) {
|
|
311
|
-
return `${entry.taskId}\u0000${entry.roleName}\u0000${entry.
|
|
311
|
+
return `${entry.taskId}\u0000${entry.roleName}\u0000${entry.turnId}\u0000${entry.generation}\u0000${entry.progressId}`;
|
|
312
312
|
}
|
|
313
313
|
/** True when `candidate` should replace `current` (sequence/receivedAt only move forward). */
|
|
314
314
|
function isNewer(candidate, current) {
|
|
@@ -325,7 +325,7 @@ function rowToEntry(row) {
|
|
|
325
325
|
return {
|
|
326
326
|
taskId: row.task_id,
|
|
327
327
|
roleName: row.role_name,
|
|
328
|
-
|
|
328
|
+
turnId: row.turn_id,
|
|
329
329
|
generation: row.generation,
|
|
330
330
|
progressId: row.progress_id,
|
|
331
331
|
...(row.sequence === null ? {} : { sequence: row.sequence }),
|
|
@@ -356,7 +356,7 @@ function mergeAggregates(rows) {
|
|
|
356
356
|
return {
|
|
357
357
|
taskId: rows[0].task_id,
|
|
358
358
|
roleName,
|
|
359
|
-
|
|
359
|
+
turnId: rows[0].turn_id,
|
|
360
360
|
generation: "*",
|
|
361
361
|
firstAt,
|
|
362
362
|
lastAt,
|
|
@@ -6,34 +6,34 @@
|
|
|
6
6
|
* never changes canonical runtime-state persistence. Retention defaults are
|
|
7
7
|
* the schema's own constants (§4.4); the environment only overrides them.
|
|
8
8
|
*/
|
|
9
|
-
import { TELEMETRY_KEEP_PER_GENERATION,
|
|
9
|
+
import { TELEMETRY_KEEP_PER_GENERATION, TELEMETRY_TURN_CAP } from "../storage/sqliteSchema.js";
|
|
10
10
|
export const DEFAULT_TELEMETRY_MODE = "off";
|
|
11
|
-
/** Terminal
|
|
11
|
+
/** Terminal Turn/generation progress rows retained after prune. */
|
|
12
12
|
export const DEFAULT_TERMINAL_KEEP = TELEMETRY_KEEP_PER_GENERATION;
|
|
13
|
-
/** Hard cap of progress rows per
|
|
14
|
-
export const
|
|
13
|
+
/** Hard cap of progress rows per Turn while it is still active. */
|
|
14
|
+
export const DEFAULT_TURN_CAP = TELEMETRY_TURN_CAP;
|
|
15
15
|
/**
|
|
16
|
-
* Absolute ceiling for the configurable
|
|
16
|
+
* Absolute ceiling for the configurable Turn cap. Retention can be tuned but
|
|
17
17
|
* never disabled: every Home keeps a computable upper bound on telemetry
|
|
18
|
-
* rows (Tasks ×
|
|
18
|
+
* rows (Tasks × Turns × cap).
|
|
19
19
|
*/
|
|
20
|
-
export const
|
|
20
|
+
export const MAX_TURN_CAP = 10_000_000;
|
|
21
21
|
/**
|
|
22
|
-
* Resolve the terminal-
|
|
22
|
+
* Resolve the terminal-Turn retention window from the durable config value
|
|
23
23
|
* (default 200). Must be a positive integer.
|
|
24
24
|
*/
|
|
25
25
|
export function resolveTerminalKeep(value) {
|
|
26
26
|
return resolvePositiveInteger(value, DEFAULT_TERMINAL_KEEP, "telemetryTerminalKeep");
|
|
27
27
|
}
|
|
28
28
|
/**
|
|
29
|
-
* Resolve the active-
|
|
30
|
-
* 50,000). Must be a positive integer not exceeding {@link
|
|
29
|
+
* Resolve the active-Turn hard cap from the durable config value (default
|
|
30
|
+
* 50,000). Must be a positive integer not exceeding {@link MAX_TURN_CAP};
|
|
31
31
|
* the cap cannot be disabled.
|
|
32
32
|
*/
|
|
33
|
-
export function
|
|
34
|
-
const cap = resolvePositiveInteger(value,
|
|
35
|
-
if (cap >
|
|
36
|
-
throw new TypeError(`
|
|
33
|
+
export function resolveTurnCap(value) {
|
|
34
|
+
const cap = resolvePositiveInteger(value, DEFAULT_TURN_CAP, "telemetryTurnCap");
|
|
35
|
+
if (cap > MAX_TURN_CAP) {
|
|
36
|
+
throw new TypeError(`telemetryTurnCap must not exceed ${MAX_TURN_CAP.toLocaleString("en-US")} (retention cannot be disabled).`);
|
|
37
37
|
}
|
|
38
38
|
return cap;
|
|
39
39
|
}
|
|
@@ -1,20 +1,14 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveTurnCap, resolveTerminalKeep } from "./telemetryConfig.js";
|
|
4
4
|
import { resolveTelemetryEnabled } from "../config/yuiConfig.js";
|
|
5
5
|
import { SqliteTelemetryStore } from "./sqliteTelemetryStore.js";
|
|
6
|
-
import { COMMITTED_DATABASE_FILENAME } from "../storage/
|
|
6
|
+
import { CURRENT_DATABASE_FILENAME as COMMITTED_DATABASE_FILENAME } from "../storage/currentTaskStore.js";
|
|
7
7
|
/**
|
|
8
|
-
* Open
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
* Telemetry lives in the Home's authoritative `yui.db`. A Home without a
|
|
13
|
-
* database has not reached SQLite storage yet, so dual/bounded mode fails
|
|
14
|
-
* closed at startup with a bounded diagnosis instead of silently creating an
|
|
15
|
-
* empty database (which would corrupt Home classification). Once opened, the
|
|
16
|
-
* store fails isolated: a broken sidecar only increments its dropped counter
|
|
17
|
-
* and never blocks the semantic lane.
|
|
8
|
+
* Open optional telemetry from the current Home's authoritative `yui.db`.
|
|
9
|
+
* Disabled telemetry performs no database work. Once opened, telemetry fails
|
|
10
|
+
* isolated: a broken sink increments its dropped counter without blocking the
|
|
11
|
+
* semantic lane.
|
|
18
12
|
*/
|
|
19
13
|
export function openSchedulerTelemetry(home, config) {
|
|
20
14
|
if (!resolveTelemetryEnabled(config.telemetryEnabled))
|
|
@@ -23,12 +17,12 @@ export function openSchedulerTelemetry(home, config) {
|
|
|
23
17
|
const dbPath = join(home, COMMITTED_DATABASE_FILENAME);
|
|
24
18
|
if (!existsSync(dbPath)) {
|
|
25
19
|
throw new Error(`telemetryEnabled=true requires SQLite storage, but ${dbPath} does not exist. `
|
|
26
|
-
+ "
|
|
20
|
+
+ "The current Home is incomplete; preserve it for diagnosis and initialize a new Home.");
|
|
27
21
|
}
|
|
28
22
|
const store = new SqliteTelemetryStore(home, {
|
|
29
23
|
mode,
|
|
30
24
|
terminalKeep: resolveTerminalKeep(config.telemetryTerminalKeep),
|
|
31
|
-
|
|
25
|
+
turnCap: resolveTurnCap(config.telemetryTurnCap)
|
|
32
26
|
});
|
|
33
27
|
return { mode, sink: store, reader: store, retention: store };
|
|
34
28
|
}
|