@zq-silk/yui 0.15.7 → 0.15.9
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 +194 -399
- package/ARCHITECTURE.zh-CN.md +151 -0
- package/README.md +306 -1131
- package/dist/agent/adapterCatalog.js +15 -2
- package/dist/agent/agent.js +23 -3
- package/dist/agent/argumentPolicy.js +7 -1
- package/dist/agent/connectionPlan.js +62 -0
- package/dist/agent/executionComponents.js +158 -0
- package/dist/agent/launchEnvironment.js +31 -3
- package/dist/agent/managedRuntimeEnvironment.js +3 -5
- package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
- package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
- package/dist/artifacts/artifactCapability.js +74 -0
- package/dist/artifacts/artifactCommitLock.js +249 -0
- package/dist/artifacts/artifactPaths.js +151 -0
- package/dist/artifacts/gitArtifactRef.js +146 -0
- package/dist/artifacts/managedGit.js +332 -0
- package/dist/artifacts/taskArtifactRepository.js +277 -0
- package/dist/brief/taskBrief.js +12 -0
- package/dist/cli/agentConfigurationPicker.js +13 -0
- package/dist/cli/commandCatalog.js +165 -74
- package/dist/cli/interactionCandidates.js +5 -5
- package/dist/cli/interactionPolicy.js +38 -8
- package/dist/cli/invocationRouter.js +1 -1
- package/dist/cli/managedDiagnostics.js +28 -0
- package/dist/cli/operatorWizard.js +1 -7
- package/dist/cli/roleOptionOrder.js +27 -0
- package/dist/cli/roleWizard.js +50 -14
- package/dist/cli/updateOrchestrator.js +1 -1
- package/dist/cli/updatePorts.js +3 -4
- package/dist/cli.js +245 -95
- package/dist/commands/agentCommands.js +72 -14
- package/dist/commands/capabilityCommands.js +9 -6
- package/dist/commands/configCommands.js +20 -20
- package/dist/commands/deliveryGuardPreflight.js +2 -2
- package/dist/commands/executionAuditCommands.js +24 -24
- package/dist/commands/globalRoleCommands.js +1 -1
- package/dist/commands/grantCommands.js +4 -4
- package/dist/commands/operatorCommands.js +34 -9
- package/dist/commands/projectCommands.js +4 -4
- package/dist/commands/resourcesCommands.js +2 -2
- package/dist/commands/roleConfiguration.js +25 -5
- package/dist/commands/roleRuntimeGuard.js +4 -5
- package/dist/commands/sessionCommands.js +3 -7
- package/dist/commands/taskActivationCommands.js +281 -0
- package/dist/commands/taskActor.js +28 -49
- package/dist/commands/taskCommands.js +1461 -720
- package/dist/commands/taskContextCommand.js +39 -583
- package/dist/commands/taskExecutionCommands.js +32 -32
- package/dist/commands/taskInputCommands.js +40 -104
- package/dist/commands/taskIntegrationCommands.js +3 -2
- package/dist/commands/taskIntegrationQueueCommands.js +1 -1
- package/dist/commands/taskNextActionCommand.js +8 -8
- package/dist/commands/taskOverviewCommand.js +33 -45
- package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
- package/dist/commands/taskRoleRuntimeStatus.js +133 -102
- package/dist/commands/telemetryCommands.js +36 -38
- package/dist/config/configCatalog.js +4 -4
- package/dist/config/yuiConfig.js +8 -8
- package/dist/context/contextSnapshot.js +10 -10
- package/dist/context/dispatchContext.js +11 -11
- package/dist/context/roleSessionContext.js +6 -3
- package/dist/context/{turnContextPack.js → runContextPack.js} +158 -81
- package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
- package/dist/context/sessionBootstrapManifest.js +21 -2
- package/dist/context/sourceRunContext.js +30 -0
- package/dist/context/taskContext.js +481 -0
- package/dist/context/wakeNotification.js +27 -27
- package/dist/controller/agentRuntimeObserver.js +21 -24
- package/dist/controller/capabilityBridge.js +17 -6
- package/dist/controller/clientRuntime.js +65 -92
- package/dist/controller/controller.js +120 -188
- package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
- package/dist/controller/jobControl.js +54 -85
- package/dist/controller/resourceInventory.js +8 -27
- package/dist/controller/resourceInventoryLinux.js +12 -13
- package/dist/controller/runtime.js +529 -479
- package/dist/controller/runtimeEventInbox.js +55 -25
- package/dist/controller/runtimeEventProcessor.js +22 -31
- package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
- package/dist/controller/runtimeLaunchCoordinator.js +80 -426
- package/dist/controller/runtimeObservationHook.js +14 -18
- package/dist/controller/sessionNotify.js +16 -24
- package/dist/controller/sessionOwnerReconciliation.js +168 -50
- package/dist/controller/structuredProviderObservation.js +138 -99
- package/dist/coordination/workMailbox.js +3 -3
- package/dist/coordination/workMailboxQueue.js +36 -33
- package/dist/core/boundedRpc.js +8 -1
- package/dist/core/controllerClient.js +20 -1
- package/dist/core/controllerServer.js +4 -4
- package/dist/doctor/doctor.js +13 -2
- package/dist/domain/agentResultTransport.js +9 -9
- package/dist/execution/codexThreadNaming.js +2 -8
- package/dist/execution/executionHealth.js +51 -63
- package/dist/execution/reviewMainRun.js +137 -0
- package/dist/execution/workItemExecution.js +28 -29
- package/dist/execution/workItemExecutionProjection.js +99 -107
- package/dist/execution/workItemMainRun.js +141 -0
- package/dist/executor/agentAdapter.js +227 -20
- package/dist/executor/agentConfigurationCatalog.js +126 -4
- package/dist/executor/agentConfigurationProbe.js +162 -4
- package/dist/executor/agentExecutor.js +79 -78
- package/dist/executor/effectiveLaunch.js +105 -18
- package/dist/executor/executorRegistry.js +29 -44
- package/dist/executor/fileRoleLaunchPlanner.js +229 -154
- package/dist/executor/workspacePreflightClassification.js +16 -16
- package/dist/grant/capabilityGrant.js +6 -3
- package/dist/input/inputRequest.js +12 -10
- package/dist/integration/gitIntegrationService.js +4 -11
- package/dist/integration/integrationQueueService.js +4 -4
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/kernel/builtinCapabilities.js +255 -12
- package/dist/kernel/capabilityRegistry.js +64 -18
- package/dist/kernel/instanceHost.js +12 -1
- package/dist/kernel/kernelPorts.js +2 -2
- package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
- package/dist/lifecycle/exactRunTerminalization.js +449 -0
- package/dist/message/message.js +118 -6
- package/dist/message/messageContinuation.js +204 -0
- package/dist/observability/executionAudit.js +70 -72
- package/dist/observability/faultClassification.js +2 -2
- package/dist/observability/orchestrationMetrics.js +8 -8
- package/dist/operator/operatorSessionHistory.js +1 -7
- package/dist/output/agentConfigurationPresentation.js +8 -3
- package/dist/output/agentRunConfigurationPresentation.js +128 -0
- package/dist/output/rolePresentation.js +54 -3
- package/dist/plugins/pluginChild.js +104 -0
- package/dist/plugins/pluginIntent.js +26 -0
- package/dist/plugins/pluginInterpreter.js +43 -0
- package/dist/plugins/pluginPackage.js +101 -0
- package/dist/plugins/pluginProcess.js +112 -0
- package/dist/plugins/pluginService.js +388 -0
- package/dist/profile/agentProfile.js +1 -1
- package/dist/repository/gitWorkspace.js +26 -4
- package/dist/repository/project.js +19 -4
- package/dist/repository/taskBaseFreshness.js +13 -13
- package/dist/repository/taskWorkspaceCoordinator.js +20 -27
- package/dist/repository/taskWorkspacePreparer.js +344 -83
- package/dist/resources/autoResourceGc.js +3 -3
- package/dist/resources/liveReferences.js +3 -3
- package/dist/resources/projectResource.js +75 -0
- package/dist/resources/projectResourceService.js +343 -0
- package/dist/resources/resourceDiscovery.js +6 -6
- package/dist/resources/resourceGc.js +1 -1
- package/dist/resources/resourceRegistrar.js +1 -1
- package/dist/resources/resourceTypes.js +1 -1
- package/dist/review/deltaRecheck.js +3 -3
- package/dist/review/reviewAcceptance.js +16 -16
- package/dist/review/reviewDecision.js +7 -7
- package/dist/review/reviewRound.js +21 -20
- package/dist/review/reviewerAvailability.js +2 -2
- package/dist/role/role.js +51 -7
- package/dist/role/taskRoleUpdate.js +30 -0
- package/dist/runtime/acpProtocol.js +425 -0
- package/dist/runtime/acpSession.js +731 -0
- package/dist/runtime/acpSessionConfiguration.js +260 -0
- package/dist/runtime/agentDriver.js +30 -11
- package/dist/runtime/agentEndpoint.js +278 -0
- package/dist/runtime/agentEndpointIdentity.js +86 -0
- package/dist/runtime/agentEndpointOwnership.js +239 -0
- package/dist/runtime/agentError.js +2 -10
- package/dist/runtime/agentHost.js +565 -314
- package/dist/runtime/agentRunConfiguration.js +258 -0
- package/dist/runtime/builtinAgentDrivers.js +134 -18
- package/dist/runtime/builtinAgentErrorMappers.js +55 -3
- package/dist/runtime/builtinTranscriptUsage.js +1 -1
- package/dist/runtime/claude-process-owner +0 -0
- package/dist/runtime/codexAppServerRuntime.js +38 -30
- package/dist/runtime/codexInteractiveHost.js +41 -6
- package/dist/runtime/continuationManager.js +2 -6
- package/dist/runtime/executionEnvironment.js +30 -0
- package/dist/runtime/firstProgressAdvisory.js +11 -11
- package/dist/runtime/index.js +4 -3
- package/dist/runtime/jsonLineChannel.js +109 -0
- package/dist/runtime/launchBroker.js +91 -16
- package/dist/runtime/launchDiagnostics.js +2 -2
- package/dist/runtime/lifecycleReservation.js +10 -18
- package/dist/runtime/managedCaller.js +61 -17
- package/dist/runtime/nativeSessionControl.js +102 -0
- package/dist/runtime/ports.js +6 -21
- package/dist/runtime/processExitObservation.js +8 -7
- package/dist/runtime/promptEnvelope.js +17 -6
- package/dist/runtime/providerContinuation.js +3 -9
- package/dist/runtime/providerContinuationReconciliationService.js +4 -13
- package/dist/runtime/providerControl.js +2 -7
- package/dist/runtime/providerRuntimeIdentity.js +110 -222
- package/dist/runtime/providerRuntimeReconciler.js +5 -9
- package/dist/runtime/runtimeBinding.js +0 -1
- package/dist/runtime/runtimeContinuationProjection.js +4 -7
- package/dist/runtime/runtimeDeadlines.js +9 -0
- package/dist/runtime/runtimeHealthPolicy.js +1 -1
- package/dist/runtime/runtimeObservation.js +29 -65
- package/dist/runtime/runtimeProjection.js +43 -51
- package/dist/runtime/runtimeSessionCandidate.js +1 -3
- package/dist/runtime/sessionLaunchRequest.js +3 -7
- package/dist/runtime/sessionOwnerIdentity.js +7 -54
- package/dist/runtime/sessionOwnerRegistry.js +22 -17
- package/dist/runtime/sessionReconciliation.js +4 -8
- package/dist/runtime/sessionTerminationGuard.js +70 -259
- package/dist/runtime/sessionTokenMetrics.js +5 -16
- package/dist/runtime/structuredProviderHost.js +237 -117
- package/dist/runtime/taskRuntimeIsolation.js +39 -122
- package/dist/runtime/tmuxAdapters.js +39 -86
- package/dist/scheduler/activeRoleRunDelivery.js +354 -0
- package/dist/scheduler/leaderWakeupProcessor.js +75 -266
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +80 -9
- package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
- package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
- package/dist/scheduler/taskExecutionProjection.js +120 -124
- package/dist/scheduler/taskObservabilityProjection.js +29 -29
- package/dist/scheduler/taskWake.js +11 -4
- package/dist/scheduler/wakeReason.js +9 -1
- package/dist/setup/setupCommand.js +3 -7
- package/dist/storage/migrations/agentRunContract.js +159 -0
- package/dist/storage/migrations/artifactsToGit.js +338 -0
- package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
- package/dist/storage/migrations/submitIntent.js +126 -0
- package/dist/storage/sqliteSchema.js +467 -7
- package/dist/storage/sqliteStore.js +355 -220
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +10 -5
- package/dist/storage/taskStore.js +13 -11
- package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
- package/dist/surface/surfaceContributions.js +102 -0
- package/dist/task/completionReadiness.js +32 -6
- package/dist/task/deliveryGuard.js +16 -16
- package/dist/task/draftPlan.js +72 -12
- package/dist/task/nextAction.js +144 -128
- package/dist/task/remoteDelivery.js +6 -6
- package/dist/task/task.js +184 -18
- package/dist/task/taskActivation.js +327 -0
- package/dist/task/taskActivationService.js +408 -0
- package/dist/task/taskRecordReference.js +5 -4
- package/dist/task/taskRecordRetirement.js +1 -1
- package/dist/task/taskSubmission.js +236 -0
- package/dist/telemetry/sqliteTelemetryStore.js +55 -68
- package/dist/telemetry/telemetryConfig.js +14 -14
- package/dist/telemetry/telemetryWiring.js +2 -2
- package/dist/web/assets/assetManifest.js +2 -0
- package/dist/web/assets/client/app.js +121 -20
- package/dist/web/assets/client/components.js +87 -54
- package/dist/web/assets/client/i18n.js +83 -41
- package/dist/web/assets/client/markdown.js +1 -1
- package/dist/web/assets/client/taskSurface.js +442 -0
- package/dist/web/assets/client/view.js +49 -44
- package/dist/web/assets/shell.js +1 -1
- package/dist/web/assets/styles/cards.js +22 -4
- package/dist/web/controllerWeb.js +60 -0
- package/dist/web/webMutation.js +28 -0
- package/dist/web/webServer.js +133 -8
- package/dist/web/webSnapshot.js +81 -74
- package/dist/web/webTaskSurface.js +64 -0
- package/dist/workItem/dependencyGate.js +1 -1
- package/dist/workItem/workItem.js +84 -48
- package/dist/workspace/workItemChangeSetManager.js +16 -9
- package/docs/agent-result-consumption.md +96 -0
- package/docs/agent-result-consumption.zh-CN.md +81 -0
- package/docs/agent-runtime-drivers.md +93 -0
- package/docs/agent-runtime-drivers.zh-CN.md +77 -0
- package/docs/architecture/README.md +50 -0
- package/docs/architecture/README.zh-CN.md +43 -0
- package/docs/architecture/capabilities-and-resources.md +118 -0
- package/docs/architecture/capabilities-and-resources.zh-CN.md +83 -0
- package/docs/managed-turn-and-session-runtime.md +224 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +180 -0
- package/docs/observability/README.md +83 -0
- package/docs/observability/README.zh-CN.md +71 -0
- package/docs/plugin-sdk.md +393 -0
- package/docs/plugin-sdk.zh-CN.md +293 -0
- package/docs/provider-runtime.md +165 -0
- package/docs/provider-runtime.zh-CN.md +132 -0
- package/docs/release-workflow.md +305 -0
- package/docs/release-workflow.zh-CN.md +237 -0
- package/docs/roles-and-configuration.md +115 -0
- package/docs/roles-and-configuration.zh-CN.md +96 -0
- package/docs/sqlite-control-plane-design.md +78 -0
- package/docs/sqlite-control-plane-design.zh-CN.md +62 -0
- package/docs/task-dag-semantics.md +80 -0
- package/docs/task-dag-semantics.zh-CN.md +59 -0
- package/docs/task-delivery.md +105 -0
- package/docs/task-delivery.zh-CN.md +82 -0
- package/docs/task-local-identity.md +8 -6
- package/docs/task-local-identity.zh-CN.md +58 -0
- package/docs/testing/verification-levels.md +88 -0
- package/docs/testing/verification-levels.zh-CN.md +69 -0
- package/i18n/README.zh-CN.md +270 -722
- package/package.json +3 -2
- package/skills/yui-leader/SKILL.md +88 -304
- package/skills/yui-leader/references/execution.md +303 -0
- package/skills/yui-leader/references/integration.md +39 -0
- package/skills/yui-leader/references/planning.md +109 -0
- package/skills/yui-leader/references/replicated-execution.md +42 -0
- package/skills/yui-leader/references/task-plugins.md +37 -0
- package/skills/yui-operator/SKILL.md +46 -62
- package/skills/yui-reviewer/SKILL.md +35 -36
- package/skills/yui-runtime/SKILL.md +88 -24
- package/skills/yui-runtime/references/publication.md +22 -0
- package/skills/yui-runtime/references/recovery.md +64 -0
- package/skills/yui-worker/SKILL.md +37 -39
- package/dist/cli/roleOptionCatalog.js +0 -68
- package/dist/context/sourceTurnContext.js +0 -30
- package/dist/execution/reviewMainTurn.js +0 -161
- package/dist/execution/workItemMainTurn.js +0 -164
- package/dist/lifecycle/exactTurnTerminalization.js +0 -407
- package/dist/runtime/preallocatedNativeSession.js +0 -13
- package/dist/runtime/runtimeStopReceipt.js +0 -42
- package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
|
@@ -0,0 +1,236 @@
|
|
|
1
|
+
import { SYSTEM_LEADER_ROLE } from "../role/systemRoles.js";
|
|
2
|
+
/**
|
|
3
|
+
* Requirement A phase and routing derivation (task-32 §2.1, §2.2).
|
|
4
|
+
*
|
|
5
|
+
* `Task.status` stays the only lifecycle statement. "Entered planning" is a
|
|
6
|
+
* derived fact, never a writable second state: this module reads it from the
|
|
7
|
+
* durable evidence a legitimate planning entry leaves behind, so idleness, a
|
|
8
|
+
* failed or replaced Session, a Controller restart, or a trimmed Context can
|
|
9
|
+
* never reset it to "not planned".
|
|
10
|
+
*/
|
|
11
|
+
/**
|
|
12
|
+
* The never-compacted event that records a Draft was routed into planning by an
|
|
13
|
+
* accepted user/operator submission, inside the same transaction that saved the
|
|
14
|
+
* message. It is one of the three independent derivation sources below, and the
|
|
15
|
+
* only one the shared submission service itself writes; the others are produced
|
|
16
|
+
* by the planning Run and its Session. Migration appends the same event for
|
|
17
|
+
* legitimate historical discussions so a post-upgrade develop cannot snatch a
|
|
18
|
+
* mid-discussion Draft into auto-activation.
|
|
19
|
+
*/
|
|
20
|
+
export const TASK_PLANNING_ENTERED_EVENT = "task.planning-entered";
|
|
21
|
+
/**
|
|
22
|
+
* Whether this Draft has already entered planning (task-32 §2.2).
|
|
23
|
+
*
|
|
24
|
+
* Derived — never a stored flag — from any of three independent, monotonic
|
|
25
|
+
* sources, each of which only ever comes into existence and never legitimately
|
|
26
|
+
* disappears:
|
|
27
|
+
*
|
|
28
|
+
* (a) a persisted planning-entered event, written in the message-save
|
|
29
|
+
* transaction the first time an accepted submission routed to planning;
|
|
30
|
+
* (b) a Leader planning AgentRun in history (the first-discuss Run, once the
|
|
31
|
+
* Controller has created it);
|
|
32
|
+
* (c) a Leader Session whose frozen launch carried planning authority.
|
|
33
|
+
*
|
|
34
|
+
* A role being configured, a non-empty body, or a non-empty Brief are
|
|
35
|
+
* deliberately NOT evidence: none of them proves the user's submission was ever
|
|
36
|
+
* accepted and routed. Only a Draft can be "in planning"; an active or terminal
|
|
37
|
+
* Task is past the question and answers false.
|
|
38
|
+
*/
|
|
39
|
+
export function draftHasEnteredPlanning(store, task) {
|
|
40
|
+
if (task.status !== "draft")
|
|
41
|
+
return false;
|
|
42
|
+
return hasPlanningEnteredEvent(store.listEvents(task.id))
|
|
43
|
+
|| hasLeaderPlanningRun(store, task.id)
|
|
44
|
+
|| hasLeaderPlanningSession(store, task.id);
|
|
45
|
+
}
|
|
46
|
+
function hasPlanningEnteredEvent(events) {
|
|
47
|
+
return events.some((event) => event.type === TASK_PLANNING_ENTERED_EVENT);
|
|
48
|
+
}
|
|
49
|
+
function hasLeaderPlanningRun(store, taskId) {
|
|
50
|
+
return store.listRuns(taskId).some((run) => run.roleName === SYSTEM_LEADER_ROLE && run.purpose === "planning");
|
|
51
|
+
}
|
|
52
|
+
function hasLeaderPlanningSession(store, taskId) {
|
|
53
|
+
const sessions = store.getTaskRoleSessionSet(taskId, SYSTEM_LEADER_ROLE);
|
|
54
|
+
if (sessions === null)
|
|
55
|
+
return false;
|
|
56
|
+
const candidates = [
|
|
57
|
+
...Object.values(sessions.sessions ?? {}),
|
|
58
|
+
...(sessions.history ?? [])
|
|
59
|
+
];
|
|
60
|
+
return candidates.some((session) => session.effective.executionAuthority === "planning");
|
|
61
|
+
}
|
|
62
|
+
export function draftActivationState(request) {
|
|
63
|
+
if (request === undefined)
|
|
64
|
+
return "none";
|
|
65
|
+
switch (request.disposition) {
|
|
66
|
+
case "pending": return "pending";
|
|
67
|
+
case "failed": return "failed";
|
|
68
|
+
case "adopted": return "adopted";
|
|
69
|
+
case "cancelled": return "none";
|
|
70
|
+
default: return "none";
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Decide how a user/operator submission routes, given its normalized intent and
|
|
75
|
+
* the phase/activation facts read inside the save transaction.
|
|
76
|
+
*
|
|
77
|
+
* This is a pure decision over already-read facts, kept separate from the
|
|
78
|
+
* transaction that saves the message and enqueues work so it can be unit-tested
|
|
79
|
+
* exhaustively against the §2.1 table without a Store. The winner of a race is
|
|
80
|
+
* decided by which submission's transaction commits first: the second one reads
|
|
81
|
+
* the phase/activation the first one already wrote and routes accordingly.
|
|
82
|
+
*
|
|
83
|
+
* `executionEnabled` is the Task's own gate, read in the same transaction. It
|
|
84
|
+
* only ever gates a *fresh* develop activation: recording an activation request
|
|
85
|
+
* against a stopped Draft is refused at the activation boundary, so routing that
|
|
86
|
+
* case to `activate` would abort the whole transaction and lose the saved
|
|
87
|
+
* message. Planning and already-open activation obligations are unaffected — the
|
|
88
|
+
* gate is enforced later at dispatch, exactly as it is for a plain message.
|
|
89
|
+
*/
|
|
90
|
+
export function decideSubmissionRouting(input) {
|
|
91
|
+
// record never wakes anything, in any state (§2.1 first column).
|
|
92
|
+
if (input.intent === "record")
|
|
93
|
+
return { kind: "record" };
|
|
94
|
+
if (input.status === "active") {
|
|
95
|
+
// Neither discuss nor develop re-activates or downgrades an active Task.
|
|
96
|
+
return { kind: "active-context" };
|
|
97
|
+
}
|
|
98
|
+
// Only Draft remains: terminal statuses are refused before routing (§2.1 last
|
|
99
|
+
// row) by the caller's assertTaskOpen, so this function is never asked about
|
|
100
|
+
// them.
|
|
101
|
+
if (input.activation === "pending") {
|
|
102
|
+
return input.intent === "develop"
|
|
103
|
+
// A compatible pending request already exists; reference it, never a
|
|
104
|
+
// second request (§2.3 "重复 develop 不产生第二个兼容激活请求").
|
|
105
|
+
? { kind: "await-activation", state: "pending" }
|
|
106
|
+
// discuss becomes post-activation input; it does not start new planning.
|
|
107
|
+
: { kind: "await-activation", state: "pending" };
|
|
108
|
+
}
|
|
109
|
+
if (input.activation === "failed") {
|
|
110
|
+
// develop never auto-retries or downgrades to planning; discuss reports the
|
|
111
|
+
// original failure and the explicit retry/cancel next step. Both save only.
|
|
112
|
+
return { kind: "await-activation", state: "failed" };
|
|
113
|
+
}
|
|
114
|
+
if (input.intent === "develop") {
|
|
115
|
+
// develop on an already-planned Draft never auto-activates — it needs
|
|
116
|
+
// explicit manual activation. On an unplanned Draft with no open activation
|
|
117
|
+
// it records the request and routes to activation, unless execution is
|
|
118
|
+
// stopped: then the message is saved but no activation is recorded.
|
|
119
|
+
if (input.enteredPlanning)
|
|
120
|
+
return { kind: "planned-needs-manual-activation" };
|
|
121
|
+
return input.executionEnabled
|
|
122
|
+
? { kind: "activate", environmentPlan: input.developEnvironmentPlan }
|
|
123
|
+
: { kind: "activation-blocked-execution-stopped" };
|
|
124
|
+
}
|
|
125
|
+
// discuss (the default): first discussion records the planning entry; later
|
|
126
|
+
// discussion continues the existing planning conversation.
|
|
127
|
+
return input.enteredPlanning
|
|
128
|
+
? { kind: "continue-planning" }
|
|
129
|
+
: { kind: "enter-planning" };
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Normalize a possibly-absent submission intent to its effective value.
|
|
133
|
+
*
|
|
134
|
+
* An absent intent is `discuss` (task-32 §2.5: "默认/旧客户端都按 discuss"), so
|
|
135
|
+
* every old client and every message saved before the field existed keeps its
|
|
136
|
+
* historical Leader-waking behaviour. Body text is never inspected.
|
|
137
|
+
*
|
|
138
|
+
* A legacy `--wake-policy none` carried the "save only, do not wake" meaning that
|
|
139
|
+
* `record` now names, so a caller that supplied only that older signal maps to
|
|
140
|
+
* `record` rather than silently becoming a Leader-waking `discuss`. An explicit
|
|
141
|
+
* intent always wins over the wake-policy shorthand.
|
|
142
|
+
*/
|
|
143
|
+
export function normalizeSubmissionIntent(intent, wakePolicy) {
|
|
144
|
+
if (intent !== undefined)
|
|
145
|
+
return intent;
|
|
146
|
+
if (wakePolicy === "none")
|
|
147
|
+
return "record";
|
|
148
|
+
return "discuss";
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Whether a retry aims at the same target the key was first bound to (§2.3). An
|
|
152
|
+
* absent prior target (a keyed Message from before receipts existed) never
|
|
153
|
+
* matches, so such a Message is treated as a conflict rather than replayed from
|
|
154
|
+
* a fabricated disposition.
|
|
155
|
+
*/
|
|
156
|
+
export function sameSubmissionTarget(prior, next) {
|
|
157
|
+
if (prior === undefined || prior.kind !== next.kind)
|
|
158
|
+
return false;
|
|
159
|
+
if (prior.kind === "task" && next.kind === "task")
|
|
160
|
+
return prior.taskId === next.taskId;
|
|
161
|
+
return true;
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Derive the §2.5 feedback for one committed submission from its routing and the
|
|
165
|
+
* facts read in the save transaction.
|
|
166
|
+
*
|
|
167
|
+
* Pure over already-read facts, so it is unit-testable against the whole table
|
|
168
|
+
* without a Store. `enteredPlanning` and `activationState` are the values read
|
|
169
|
+
* before routing executed; the routing itself names what the transaction then
|
|
170
|
+
* did, and phase is derived to reflect the post-commit truth (a just-entered
|
|
171
|
+
* planning Draft reports `draft-planning`).
|
|
172
|
+
*/
|
|
173
|
+
export function describeSubmissionFeedback(input) {
|
|
174
|
+
const { routing } = input;
|
|
175
|
+
const phase = input.status === "active"
|
|
176
|
+
? "active"
|
|
177
|
+
: routing.kind === "enter-planning"
|
|
178
|
+
|| routing.kind === "continue-planning"
|
|
179
|
+
|| routing.kind === "planned-needs-manual-activation"
|
|
180
|
+
|| input.enteredPlanning
|
|
181
|
+
? "draft-planning"
|
|
182
|
+
: "draft-unplanned";
|
|
183
|
+
const planning = routing.kind === "enter-planning"
|
|
184
|
+
? "entered"
|
|
185
|
+
: routing.kind === "continue-planning"
|
|
186
|
+
? "continued"
|
|
187
|
+
: "none";
|
|
188
|
+
const activation = routing.kind === "activate"
|
|
189
|
+
? "requested"
|
|
190
|
+
: routing.kind === "planned-needs-manual-activation"
|
|
191
|
+
? "manual-required"
|
|
192
|
+
: routing.kind === "activation-blocked-execution-stopped"
|
|
193
|
+
? "execution-stopped"
|
|
194
|
+
: routing.kind === "await-activation"
|
|
195
|
+
? routing.state
|
|
196
|
+
// record and the planning routes report the existing obligation, if
|
|
197
|
+
// any, truthfully without acting on it.
|
|
198
|
+
: input.activationState === "pending"
|
|
199
|
+
? "pending"
|
|
200
|
+
: input.activationState === "failed"
|
|
201
|
+
? "failed"
|
|
202
|
+
: "none";
|
|
203
|
+
const delivery = routing.kind === "enter-planning"
|
|
204
|
+
|| routing.kind === "continue-planning"
|
|
205
|
+
|| routing.kind === "active-context"
|
|
206
|
+
? "queued"
|
|
207
|
+
: "none";
|
|
208
|
+
const nextStep = submissionNextStep(input);
|
|
209
|
+
return {
|
|
210
|
+
saved: { taskId: input.taskId, messageId: input.messageId },
|
|
211
|
+
phase,
|
|
212
|
+
planning,
|
|
213
|
+
activation,
|
|
214
|
+
delivery,
|
|
215
|
+
...(nextStep === undefined ? {} : { nextStep })
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function submissionNextStep(input) {
|
|
219
|
+
if (input.routing.kind === "planned-needs-manual-activation") {
|
|
220
|
+
return { kind: "activate-manually", taskId: input.taskId };
|
|
221
|
+
}
|
|
222
|
+
if (input.routing.kind === "activation-blocked-execution-stopped") {
|
|
223
|
+
return { kind: "start-execution", taskId: input.taskId };
|
|
224
|
+
}
|
|
225
|
+
if (input.routing.kind === "await-activation") {
|
|
226
|
+
if (input.routing.state === "failed") {
|
|
227
|
+
return {
|
|
228
|
+
kind: "resolve-failed-activation",
|
|
229
|
+
activationRef: input.activationRef ?? "",
|
|
230
|
+
failure: input.activationFailure ?? "activation failed"
|
|
231
|
+
};
|
|
232
|
+
}
|
|
233
|
+
return { kind: "await-pending-activation", activationRef: input.activationRef ?? "" };
|
|
234
|
+
}
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
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_RUN_CAP, DEFAULT_TERMINAL_KEEP } from "./telemetryConfig.js";
|
|
5
5
|
import { migrateSqliteSchema } from "../storage/sqliteSchema.js";
|
|
6
6
|
import { CURRENT_DATABASE_FILENAME as COMMITTED_DATABASE_FILENAME } from "../storage/currentTaskStore.js";
|
|
7
7
|
/**
|
|
@@ -18,7 +18,7 @@ import { CURRENT_DATABASE_FILENAME as COMMITTED_DATABASE_FILENAME } from "../sto
|
|
|
18
18
|
*
|
|
19
19
|
* The telemetry tables are maintained by the centralized schema baseline:
|
|
20
20
|
* `telemetry` holds the bounded latest-per-key window and
|
|
21
|
-
* `telemetry_aggregate` holds the authoritative per-
|
|
21
|
+
* `telemetry_aggregate` holds the authoritative per-AgentRun
|
|
22
22
|
* summary, maintained by triggers so it survives window pruning.
|
|
23
23
|
*/
|
|
24
24
|
const MAX_PAGE_LIMIT = 500;
|
|
@@ -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.#turnCap = options.
|
|
44
|
+
this.#turnCap = options.runCap ?? DEFAULT_RUN_CAP;
|
|
45
45
|
this.#maxPending = options.maxPending ?? 10_000;
|
|
46
46
|
}
|
|
47
47
|
// -- TelemetrySink -------------------------------------------------------------
|
|
@@ -89,53 +89,52 @@ export class SqliteTelemetryStore {
|
|
|
89
89
|
this.#db = null;
|
|
90
90
|
}
|
|
91
91
|
// -- TelemetryReader -----------------------------------------------------------
|
|
92
|
-
count(taskId,
|
|
92
|
+
count(taskId, runId) {
|
|
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 (runId === 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 turn_id = ?").get(taskId,
|
|
102
|
+
return db.prepare("SELECT COUNT(*) AS n FROM telemetry WHERE task_id = ? AND turn_id = ?").get(taskId, runId).n;
|
|
103
103
|
}
|
|
104
|
-
list(taskId,
|
|
104
|
+
list(taskId, runId, 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, turn_id,
|
|
112
|
-
: db.prepare("SELECT task_id, role_name, turn_id,
|
|
110
|
+
const rows = runId === undefined
|
|
111
|
+
? db.prepare("SELECT task_id, role_name, turn_id, 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, progress_id, sequence, payload, received_at FROM telemetry WHERE task_id = ? AND turn_id = ? ORDER BY received_at, progress_id LIMIT ? OFFSET ?").all(taskId, runId, limit, offset);
|
|
113
113
|
const items = rows.map(rowToEntry);
|
|
114
|
-
const total = this.count(taskId,
|
|
114
|
+
const total = this.count(taskId, runId);
|
|
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, runId) {
|
|
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, turn_id,
|
|
122
|
+
const rows = db.prepare("SELECT task_id, role_name, turn_id, first_at, last_at, count, max_sequence, error_count FROM telemetry_aggregate WHERE task_id = ? AND turn_id = ?").all(taskId, runId);
|
|
123
123
|
if (rows.length === 0)
|
|
124
124
|
return null;
|
|
125
125
|
return mergeAggregates(rows);
|
|
126
126
|
}
|
|
127
|
-
|
|
127
|
+
aggregateRoleRun(taskId, roleName, runId) {
|
|
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, turn_id,
|
|
131
|
+
const row = db.prepare("SELECT task_id, role_name, turn_id, first_at, last_at, count, max_sequence, error_count FROM telemetry_aggregate WHERE task_id = ? AND role_name = ? AND turn_id = ?").get(taskId, roleName, runId);
|
|
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
|
-
|
|
138
|
-
generation: row.generation,
|
|
137
|
+
runId: row.turn_id,
|
|
139
138
|
firstAt: row.first_at,
|
|
140
139
|
lastAt: row.last_at,
|
|
141
140
|
count: row.count,
|
|
@@ -143,16 +142,15 @@ export class SqliteTelemetryStore {
|
|
|
143
142
|
errorCount: row.error_count
|
|
144
143
|
};
|
|
145
144
|
}
|
|
146
|
-
|
|
145
|
+
listRunAggregates(taskId) {
|
|
147
146
|
const db = this.#ensureDb();
|
|
148
147
|
if (db === null)
|
|
149
148
|
return [];
|
|
150
|
-
const rows = db.prepare("SELECT task_id, role_name, turn_id,
|
|
149
|
+
const rows = db.prepare("SELECT task_id, role_name, turn_id, first_at, last_at, count, max_sequence, error_count FROM telemetry_aggregate WHERE task_id = ? ORDER BY turn_id").all(taskId);
|
|
151
150
|
return rows.map((row) => ({
|
|
152
151
|
taskId: row.task_id,
|
|
153
152
|
roleName: row.role_name,
|
|
154
|
-
|
|
155
|
-
generation: row.generation,
|
|
153
|
+
runId: row.turn_id,
|
|
156
154
|
firstAt: row.first_at,
|
|
157
155
|
lastAt: row.last_at,
|
|
158
156
|
count: row.count,
|
|
@@ -164,52 +162,51 @@ export class SqliteTelemetryStore {
|
|
|
164
162
|
return this.#applied;
|
|
165
163
|
}
|
|
166
164
|
// -- retention -----------------------------------------------------------------
|
|
167
|
-
|
|
165
|
+
pruneRun(taskId, roleName, runId, keep = this.#terminalKeep) {
|
|
168
166
|
const db = this.#ensureDb();
|
|
169
167
|
if (db === null)
|
|
170
168
|
return 0;
|
|
171
169
|
const result = db.prepare(`DELETE FROM telemetry
|
|
172
|
-
WHERE task_id = ? AND role_name = ? AND turn_id = ?
|
|
173
|
-
AND (task_id, role_name, turn_id,
|
|
174
|
-
SELECT task_id, role_name, turn_id,
|
|
175
|
-
WHERE task_id = ? AND role_name = ? AND turn_id = ?
|
|
176
|
-
ORDER BY COALESCE(sequence, -1) DESC,
|
|
170
|
+
WHERE task_id = ? AND role_name = ? AND turn_id = ?
|
|
171
|
+
AND (task_id, role_name, turn_id, progress_id) NOT IN (
|
|
172
|
+
SELECT task_id, role_name, turn_id, progress_id FROM telemetry
|
|
173
|
+
WHERE task_id = ? AND role_name = ? AND turn_id = ?
|
|
174
|
+
ORDER BY received_at DESC, COALESCE(sequence, -1) DESC, progress_id ASC
|
|
177
175
|
LIMIT ?
|
|
178
|
-
)`).run(taskId, roleName,
|
|
176
|
+
)`).run(taskId, roleName, runId, taskId, roleName, runId, keep);
|
|
179
177
|
return result.changes;
|
|
180
178
|
}
|
|
181
|
-
|
|
179
|
+
capRun(taskId, runId, cap = this.#turnCap) {
|
|
182
180
|
const db = this.#ensureDb();
|
|
183
181
|
if (db === null)
|
|
184
182
|
return 0;
|
|
185
183
|
const result = db.prepare(`DELETE FROM telemetry
|
|
186
184
|
WHERE task_id = ? AND turn_id = ?
|
|
187
|
-
AND (task_id, role_name, turn_id,
|
|
188
|
-
SELECT task_id, role_name, turn_id,
|
|
185
|
+
AND (task_id, role_name, turn_id, progress_id) NOT IN (
|
|
186
|
+
SELECT task_id, role_name, turn_id, progress_id FROM telemetry
|
|
189
187
|
WHERE task_id = ? AND turn_id = ?
|
|
190
|
-
ORDER BY COALESCE(sequence, -1) DESC,
|
|
188
|
+
ORDER BY received_at DESC, COALESCE(sequence, -1) DESC, progress_id ASC
|
|
191
189
|
LIMIT ?
|
|
192
|
-
)`).run(taskId,
|
|
190
|
+
)`).run(taskId, runId, taskId, runId, cap);
|
|
193
191
|
return result.changes;
|
|
194
192
|
}
|
|
195
|
-
|
|
193
|
+
importRun(entries, aggregate) {
|
|
196
194
|
const db = this.#ensureDb();
|
|
197
195
|
if (db === null) {
|
|
198
196
|
this.#dropped += entries.length;
|
|
199
197
|
return;
|
|
200
198
|
}
|
|
201
|
-
const upsert = db.prepare(`INSERT INTO telemetry (task_id, role_name, turn_id,
|
|
202
|
-
VALUES (?, ?, ?, ?, ?, ?,
|
|
203
|
-
ON CONFLICT(task_id, role_name, turn_id,
|
|
199
|
+
const upsert = db.prepare(`INSERT INTO telemetry (task_id, role_name, turn_id, progress_id, sequence, payload, received_at)
|
|
200
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
201
|
+
ON CONFLICT(task_id, role_name, turn_id, progress_id) DO UPDATE SET
|
|
204
202
|
sequence = excluded.sequence,
|
|
205
203
|
payload = excluded.payload,
|
|
206
204
|
received_at = excluded.received_at
|
|
207
|
-
WHERE
|
|
208
|
-
OR (excluded.sequence IS telemetry.sequence AND excluded.received_at >= telemetry.received_at)`);
|
|
205
|
+
WHERE excluded.received_at >= telemetry.received_at`);
|
|
209
206
|
const upsertAggregate = db.prepare(`INSERT INTO telemetry_aggregate
|
|
210
|
-
(task_id, role_name, turn_id,
|
|
211
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?,
|
|
212
|
-
ON CONFLICT(task_id, role_name, turn_id
|
|
207
|
+
(task_id, role_name, turn_id, first_at, last_at, count, max_sequence, error_count, updated_at)
|
|
208
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
|
|
209
|
+
ON CONFLICT(task_id, role_name, turn_id) DO UPDATE SET
|
|
213
210
|
first_at = excluded.first_at,
|
|
214
211
|
last_at = excluded.last_at,
|
|
215
212
|
count = excluded.count,
|
|
@@ -218,9 +215,9 @@ export class SqliteTelemetryStore {
|
|
|
218
215
|
updated_at = excluded.updated_at`);
|
|
219
216
|
db.transaction(() => {
|
|
220
217
|
for (const entry of entries) {
|
|
221
|
-
upsert.run(entry.taskId, entry.roleName, entry.
|
|
218
|
+
upsert.run(entry.taskId, entry.roleName, entry.runId, entry.progressId, entry.sequence ?? null, JSON.stringify(entry.payload), entry.receivedAt);
|
|
222
219
|
}
|
|
223
|
-
upsertAggregate.run(aggregate.taskId, aggregate.roleName, aggregate.
|
|
220
|
+
upsertAggregate.run(aggregate.taskId, aggregate.roleName, aggregate.runId, aggregate.firstAt, aggregate.lastAt, aggregate.count, aggregate.maxSequence, aggregate.errorCount, aggregate.lastAt);
|
|
224
221
|
})();
|
|
225
222
|
this.#applied += entries.length;
|
|
226
223
|
}
|
|
@@ -277,26 +274,25 @@ export class SqliteTelemetryStore {
|
|
|
277
274
|
}
|
|
278
275
|
const batch = new Map(this.#pending);
|
|
279
276
|
this.#pending.clear();
|
|
280
|
-
const upsert = db.prepare(`INSERT INTO telemetry (task_id, role_name, turn_id,
|
|
281
|
-
VALUES (?, ?, ?, ?, ?, ?,
|
|
282
|
-
ON CONFLICT(task_id, role_name, turn_id,
|
|
277
|
+
const upsert = db.prepare(`INSERT INTO telemetry (task_id, role_name, turn_id, progress_id, sequence, payload, received_at)
|
|
278
|
+
VALUES (?, ?, ?, ?, ?, ?, ?)
|
|
279
|
+
ON CONFLICT(task_id, role_name, turn_id, progress_id) DO UPDATE SET
|
|
283
280
|
sequence = excluded.sequence,
|
|
284
281
|
payload = excluded.payload,
|
|
285
282
|
received_at = excluded.received_at
|
|
286
|
-
WHERE
|
|
287
|
-
OR (excluded.sequence IS telemetry.sequence AND excluded.received_at >= telemetry.received_at)`);
|
|
283
|
+
WHERE excluded.received_at >= telemetry.received_at`);
|
|
288
284
|
try {
|
|
289
|
-
const
|
|
285
|
+
const touchedRuns = new Map();
|
|
290
286
|
db.transaction(() => {
|
|
291
287
|
for (const entry of batch.values()) {
|
|
292
|
-
upsert.run(entry.taskId, entry.roleName, entry.
|
|
293
|
-
|
|
288
|
+
upsert.run(entry.taskId, entry.roleName, entry.runId, entry.progressId, entry.sequence ?? null, JSON.stringify(entry.payload), entry.receivedAt);
|
|
289
|
+
touchedRuns.set(`${entry.taskId}\0${entry.runId}`, {
|
|
294
290
|
taskId: entry.taskId,
|
|
295
|
-
|
|
291
|
+
runId: entry.runId
|
|
296
292
|
});
|
|
297
293
|
}
|
|
298
|
-
for (const { taskId,
|
|
299
|
-
this.
|
|
294
|
+
for (const { taskId, runId } of touchedRuns.values()) {
|
|
295
|
+
this.capRun(taskId, runId);
|
|
300
296
|
}
|
|
301
297
|
})();
|
|
302
298
|
this.#applied += batch.size;
|
|
@@ -308,25 +304,17 @@ export class SqliteTelemetryStore {
|
|
|
308
304
|
}
|
|
309
305
|
}
|
|
310
306
|
function pendingKey(entry) {
|
|
311
|
-
return `${entry.taskId}\u0000${entry.roleName}\u0000${entry.
|
|
307
|
+
return `${entry.taskId}\u0000${entry.roleName}\u0000${entry.runId}\u0000${entry.progressId}`;
|
|
312
308
|
}
|
|
313
|
-
/**
|
|
309
|
+
/** Transport counters may restart; the later recorded observation wins. */
|
|
314
310
|
function isNewer(candidate, current) {
|
|
315
|
-
if (candidate.sequence !== undefined && current.sequence !== undefined) {
|
|
316
|
-
return candidate.sequence > current.sequence;
|
|
317
|
-
}
|
|
318
|
-
if (candidate.sequence !== undefined)
|
|
319
|
-
return true;
|
|
320
|
-
if (current.sequence !== undefined)
|
|
321
|
-
return false;
|
|
322
311
|
return Date.parse(candidate.receivedAt) >= Date.parse(current.receivedAt);
|
|
323
312
|
}
|
|
324
313
|
function rowToEntry(row) {
|
|
325
314
|
return {
|
|
326
315
|
taskId: row.task_id,
|
|
327
316
|
roleName: row.role_name,
|
|
328
|
-
|
|
329
|
-
generation: row.generation,
|
|
317
|
+
runId: row.turn_id,
|
|
330
318
|
progressId: row.progress_id,
|
|
331
319
|
...(row.sequence === null ? {} : { sequence: row.sequence }),
|
|
332
320
|
payload: JSON.parse(row.payload),
|
|
@@ -356,8 +344,7 @@ function mergeAggregates(rows) {
|
|
|
356
344
|
return {
|
|
357
345
|
taskId: rows[0].task_id,
|
|
358
346
|
roleName,
|
|
359
|
-
|
|
360
|
-
generation: "*",
|
|
347
|
+
runId: rows[0].turn_id,
|
|
361
348
|
firstAt,
|
|
362
349
|
lastAt,
|
|
363
350
|
count,
|
|
@@ -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 {
|
|
9
|
+
import { TELEMETRY_KEEP_PER_RUN, TELEMETRY_RUN_CAP } from "../storage/sqliteSchema.js";
|
|
10
10
|
export const DEFAULT_TELEMETRY_MODE = "off";
|
|
11
|
-
/** Terminal
|
|
12
|
-
export const DEFAULT_TERMINAL_KEEP =
|
|
13
|
-
/** Hard cap of progress rows per
|
|
14
|
-
export const
|
|
11
|
+
/** Terminal AgentRun progress rows retained after prune. */
|
|
12
|
+
export const DEFAULT_TERMINAL_KEEP = TELEMETRY_KEEP_PER_RUN;
|
|
13
|
+
/** Hard cap of progress rows per AgentRun while it is still active. */
|
|
14
|
+
export const DEFAULT_RUN_CAP = TELEMETRY_RUN_CAP;
|
|
15
15
|
/**
|
|
16
|
-
* Absolute ceiling for the configurable
|
|
16
|
+
* Absolute ceiling for the configurable AgentRun 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 × AgentRuns × cap).
|
|
19
19
|
*/
|
|
20
|
-
export const
|
|
20
|
+
export const MAX_RUN_CAP = 10_000_000;
|
|
21
21
|
/**
|
|
22
|
-
* Resolve the terminal-
|
|
22
|
+
* Resolve the terminal-AgentRun 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-
|
|
29
|
+
* Resolve the active-AgentRun hard cap from the durable config value (default
|
|
30
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(`telemetryTurnCap must not exceed ${
|
|
33
|
+
export function resolveRunCap(value) {
|
|
34
|
+
const cap = resolvePositiveInteger(value, DEFAULT_RUN_CAP, "telemetryRunCap");
|
|
35
|
+
if (cap > MAX_RUN_CAP) {
|
|
36
|
+
throw new TypeError(`telemetryTurnCap must not exceed ${MAX_RUN_CAP.toLocaleString("en-US")} (retention cannot be disabled).`);
|
|
37
37
|
}
|
|
38
38
|
return cap;
|
|
39
39
|
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { existsSync } from "node:fs";
|
|
2
2
|
import { join } from "node:path";
|
|
3
|
-
import {
|
|
3
|
+
import { resolveRunCap, resolveTerminalKeep } from "./telemetryConfig.js";
|
|
4
4
|
import { resolveTelemetryEnabled } from "../config/yuiConfig.js";
|
|
5
5
|
import { SqliteTelemetryStore } from "./sqliteTelemetryStore.js";
|
|
6
6
|
import { CURRENT_DATABASE_FILENAME as COMMITTED_DATABASE_FILENAME } from "../storage/currentTaskStore.js";
|
|
@@ -22,7 +22,7 @@ export function openSchedulerTelemetry(home, config) {
|
|
|
22
22
|
const store = new SqliteTelemetryStore(home, {
|
|
23
23
|
mode,
|
|
24
24
|
terminalKeep: resolveTerminalKeep(config.telemetryTerminalKeep),
|
|
25
|
-
|
|
25
|
+
runCap: resolveRunCap(config.telemetryRunCap)
|
|
26
26
|
});
|
|
27
27
|
return { mode, sink: store, reader: store, retention: store };
|
|
28
28
|
}
|
|
@@ -8,6 +8,7 @@ import { I18N_SCRIPT } from "./client/i18n.js";
|
|
|
8
8
|
import { MARKDOWN_SCRIPT } from "./client/markdown.js";
|
|
9
9
|
import { THEME_SCRIPT } from "./client/theme.js";
|
|
10
10
|
import { VIEW_SCRIPT } from "./client/view.js";
|
|
11
|
+
import { TASK_SURFACE_SCRIPT } from "./client/taskSurface.js";
|
|
11
12
|
import { DASHBOARD_HTML } from "./shell.js";
|
|
12
13
|
import { FONT_FACE_STYLES } from "./fonts.js";
|
|
13
14
|
import { FONT_WOFF2_BASE64 } from "./fontData.js";
|
|
@@ -33,6 +34,7 @@ export const WEB_ASSETS = Object.freeze({
|
|
|
33
34
|
"/assets/js/theme.js": { contentType: "text/javascript; charset=utf-8", body: THEME_SCRIPT },
|
|
34
35
|
"/assets/js/components.js": { contentType: "text/javascript; charset=utf-8", body: COMPONENTS_SCRIPT },
|
|
35
36
|
"/assets/js/view.js": { contentType: "text/javascript; charset=utf-8", body: VIEW_SCRIPT },
|
|
37
|
+
"/assets/js/task-surface.js": { contentType: "text/javascript; charset=utf-8", body: TASK_SURFACE_SCRIPT },
|
|
36
38
|
"/assets/app.js": { contentType: "text/javascript; charset=utf-8", body: APP_SCRIPT },
|
|
37
39
|
...fontAssets(),
|
|
38
40
|
"/assets/vendor/xterm.mjs": vendorAsset("@xterm/xterm/lib/xterm.mjs", "text/javascript; charset=utf-8"),
|