@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.
Files changed (308) hide show
  1. package/ARCHITECTURE.md +194 -399
  2. package/ARCHITECTURE.zh-CN.md +151 -0
  3. package/README.md +306 -1131
  4. package/dist/agent/adapterCatalog.js +15 -2
  5. package/dist/agent/agent.js +23 -3
  6. package/dist/agent/argumentPolicy.js +7 -1
  7. package/dist/agent/connectionPlan.js +62 -0
  8. package/dist/agent/executionComponents.js +158 -0
  9. package/dist/agent/launchEnvironment.js +31 -3
  10. package/dist/agent/managedRuntimeEnvironment.js +3 -5
  11. package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
  12. package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
  13. package/dist/artifacts/artifactCapability.js +74 -0
  14. package/dist/artifacts/artifactCommitLock.js +249 -0
  15. package/dist/artifacts/artifactPaths.js +151 -0
  16. package/dist/artifacts/gitArtifactRef.js +146 -0
  17. package/dist/artifacts/managedGit.js +332 -0
  18. package/dist/artifacts/taskArtifactRepository.js +277 -0
  19. package/dist/brief/taskBrief.js +12 -0
  20. package/dist/cli/agentConfigurationPicker.js +13 -0
  21. package/dist/cli/commandCatalog.js +165 -74
  22. package/dist/cli/interactionCandidates.js +5 -5
  23. package/dist/cli/interactionPolicy.js +38 -8
  24. package/dist/cli/invocationRouter.js +1 -1
  25. package/dist/cli/managedDiagnostics.js +28 -0
  26. package/dist/cli/operatorWizard.js +1 -7
  27. package/dist/cli/roleOptionOrder.js +27 -0
  28. package/dist/cli/roleWizard.js +50 -14
  29. package/dist/cli/updateOrchestrator.js +1 -1
  30. package/dist/cli/updatePorts.js +3 -4
  31. package/dist/cli.js +245 -95
  32. package/dist/commands/agentCommands.js +72 -14
  33. package/dist/commands/capabilityCommands.js +9 -6
  34. package/dist/commands/configCommands.js +20 -20
  35. package/dist/commands/deliveryGuardPreflight.js +2 -2
  36. package/dist/commands/executionAuditCommands.js +24 -24
  37. package/dist/commands/globalRoleCommands.js +1 -1
  38. package/dist/commands/grantCommands.js +4 -4
  39. package/dist/commands/operatorCommands.js +34 -9
  40. package/dist/commands/projectCommands.js +4 -4
  41. package/dist/commands/resourcesCommands.js +2 -2
  42. package/dist/commands/roleConfiguration.js +25 -5
  43. package/dist/commands/roleRuntimeGuard.js +4 -5
  44. package/dist/commands/sessionCommands.js +3 -7
  45. package/dist/commands/taskActivationCommands.js +281 -0
  46. package/dist/commands/taskActor.js +28 -49
  47. package/dist/commands/taskCommands.js +1461 -720
  48. package/dist/commands/taskContextCommand.js +39 -583
  49. package/dist/commands/taskExecutionCommands.js +32 -32
  50. package/dist/commands/taskInputCommands.js +40 -104
  51. package/dist/commands/taskIntegrationCommands.js +3 -2
  52. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  53. package/dist/commands/taskNextActionCommand.js +8 -8
  54. package/dist/commands/taskOverviewCommand.js +33 -45
  55. package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
  56. package/dist/commands/taskRoleRuntimeStatus.js +133 -102
  57. package/dist/commands/telemetryCommands.js +36 -38
  58. package/dist/config/configCatalog.js +4 -4
  59. package/dist/config/yuiConfig.js +8 -8
  60. package/dist/context/contextSnapshot.js +10 -10
  61. package/dist/context/dispatchContext.js +11 -11
  62. package/dist/context/roleSessionContext.js +6 -3
  63. package/dist/context/{turnContextPack.js → runContextPack.js} +158 -81
  64. package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
  65. package/dist/context/sessionBootstrapManifest.js +21 -2
  66. package/dist/context/sourceRunContext.js +30 -0
  67. package/dist/context/taskContext.js +481 -0
  68. package/dist/context/wakeNotification.js +27 -27
  69. package/dist/controller/agentRuntimeObserver.js +21 -24
  70. package/dist/controller/capabilityBridge.js +17 -6
  71. package/dist/controller/clientRuntime.js +65 -92
  72. package/dist/controller/controller.js +120 -188
  73. package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
  74. package/dist/controller/jobControl.js +54 -85
  75. package/dist/controller/resourceInventory.js +8 -27
  76. package/dist/controller/resourceInventoryLinux.js +12 -13
  77. package/dist/controller/runtime.js +529 -479
  78. package/dist/controller/runtimeEventInbox.js +55 -25
  79. package/dist/controller/runtimeEventProcessor.js +22 -31
  80. package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
  81. package/dist/controller/runtimeLaunchCoordinator.js +80 -426
  82. package/dist/controller/runtimeObservationHook.js +14 -18
  83. package/dist/controller/sessionNotify.js +16 -24
  84. package/dist/controller/sessionOwnerReconciliation.js +168 -50
  85. package/dist/controller/structuredProviderObservation.js +138 -99
  86. package/dist/coordination/workMailbox.js +3 -3
  87. package/dist/coordination/workMailboxQueue.js +36 -33
  88. package/dist/core/boundedRpc.js +8 -1
  89. package/dist/core/controllerClient.js +20 -1
  90. package/dist/core/controllerServer.js +4 -4
  91. package/dist/doctor/doctor.js +13 -2
  92. package/dist/domain/agentResultTransport.js +9 -9
  93. package/dist/execution/codexThreadNaming.js +2 -8
  94. package/dist/execution/executionHealth.js +51 -63
  95. package/dist/execution/reviewMainRun.js +137 -0
  96. package/dist/execution/workItemExecution.js +28 -29
  97. package/dist/execution/workItemExecutionProjection.js +99 -107
  98. package/dist/execution/workItemMainRun.js +141 -0
  99. package/dist/executor/agentAdapter.js +227 -20
  100. package/dist/executor/agentConfigurationCatalog.js +126 -4
  101. package/dist/executor/agentConfigurationProbe.js +162 -4
  102. package/dist/executor/agentExecutor.js +79 -78
  103. package/dist/executor/effectiveLaunch.js +105 -18
  104. package/dist/executor/executorRegistry.js +29 -44
  105. package/dist/executor/fileRoleLaunchPlanner.js +229 -154
  106. package/dist/executor/workspacePreflightClassification.js +16 -16
  107. package/dist/grant/capabilityGrant.js +6 -3
  108. package/dist/input/inputRequest.js +12 -10
  109. package/dist/integration/gitIntegrationService.js +4 -11
  110. package/dist/integration/integrationQueueService.js +4 -4
  111. package/dist/interaction/operatorPresentation.js +1 -1
  112. package/dist/kernel/builtinCapabilities.js +255 -12
  113. package/dist/kernel/capabilityRegistry.js +64 -18
  114. package/dist/kernel/instanceHost.js +12 -1
  115. package/dist/kernel/kernelPorts.js +2 -2
  116. package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
  117. package/dist/lifecycle/exactRunTerminalization.js +449 -0
  118. package/dist/message/message.js +118 -6
  119. package/dist/message/messageContinuation.js +204 -0
  120. package/dist/observability/executionAudit.js +70 -72
  121. package/dist/observability/faultClassification.js +2 -2
  122. package/dist/observability/orchestrationMetrics.js +8 -8
  123. package/dist/operator/operatorSessionHistory.js +1 -7
  124. package/dist/output/agentConfigurationPresentation.js +8 -3
  125. package/dist/output/agentRunConfigurationPresentation.js +128 -0
  126. package/dist/output/rolePresentation.js +54 -3
  127. package/dist/plugins/pluginChild.js +104 -0
  128. package/dist/plugins/pluginIntent.js +26 -0
  129. package/dist/plugins/pluginInterpreter.js +43 -0
  130. package/dist/plugins/pluginPackage.js +101 -0
  131. package/dist/plugins/pluginProcess.js +112 -0
  132. package/dist/plugins/pluginService.js +388 -0
  133. package/dist/profile/agentProfile.js +1 -1
  134. package/dist/repository/gitWorkspace.js +26 -4
  135. package/dist/repository/project.js +19 -4
  136. package/dist/repository/taskBaseFreshness.js +13 -13
  137. package/dist/repository/taskWorkspaceCoordinator.js +20 -27
  138. package/dist/repository/taskWorkspacePreparer.js +344 -83
  139. package/dist/resources/autoResourceGc.js +3 -3
  140. package/dist/resources/liveReferences.js +3 -3
  141. package/dist/resources/projectResource.js +75 -0
  142. package/dist/resources/projectResourceService.js +343 -0
  143. package/dist/resources/resourceDiscovery.js +6 -6
  144. package/dist/resources/resourceGc.js +1 -1
  145. package/dist/resources/resourceRegistrar.js +1 -1
  146. package/dist/resources/resourceTypes.js +1 -1
  147. package/dist/review/deltaRecheck.js +3 -3
  148. package/dist/review/reviewAcceptance.js +16 -16
  149. package/dist/review/reviewDecision.js +7 -7
  150. package/dist/review/reviewRound.js +21 -20
  151. package/dist/review/reviewerAvailability.js +2 -2
  152. package/dist/role/role.js +51 -7
  153. package/dist/role/taskRoleUpdate.js +30 -0
  154. package/dist/runtime/acpProtocol.js +425 -0
  155. package/dist/runtime/acpSession.js +731 -0
  156. package/dist/runtime/acpSessionConfiguration.js +260 -0
  157. package/dist/runtime/agentDriver.js +30 -11
  158. package/dist/runtime/agentEndpoint.js +278 -0
  159. package/dist/runtime/agentEndpointIdentity.js +86 -0
  160. package/dist/runtime/agentEndpointOwnership.js +239 -0
  161. package/dist/runtime/agentError.js +2 -10
  162. package/dist/runtime/agentHost.js +565 -314
  163. package/dist/runtime/agentRunConfiguration.js +258 -0
  164. package/dist/runtime/builtinAgentDrivers.js +134 -18
  165. package/dist/runtime/builtinAgentErrorMappers.js +55 -3
  166. package/dist/runtime/builtinTranscriptUsage.js +1 -1
  167. package/dist/runtime/claude-process-owner +0 -0
  168. package/dist/runtime/codexAppServerRuntime.js +38 -30
  169. package/dist/runtime/codexInteractiveHost.js +41 -6
  170. package/dist/runtime/continuationManager.js +2 -6
  171. package/dist/runtime/executionEnvironment.js +30 -0
  172. package/dist/runtime/firstProgressAdvisory.js +11 -11
  173. package/dist/runtime/index.js +4 -3
  174. package/dist/runtime/jsonLineChannel.js +109 -0
  175. package/dist/runtime/launchBroker.js +91 -16
  176. package/dist/runtime/launchDiagnostics.js +2 -2
  177. package/dist/runtime/lifecycleReservation.js +10 -18
  178. package/dist/runtime/managedCaller.js +61 -17
  179. package/dist/runtime/nativeSessionControl.js +102 -0
  180. package/dist/runtime/ports.js +6 -21
  181. package/dist/runtime/processExitObservation.js +8 -7
  182. package/dist/runtime/promptEnvelope.js +17 -6
  183. package/dist/runtime/providerContinuation.js +3 -9
  184. package/dist/runtime/providerContinuationReconciliationService.js +4 -13
  185. package/dist/runtime/providerControl.js +2 -7
  186. package/dist/runtime/providerRuntimeIdentity.js +110 -222
  187. package/dist/runtime/providerRuntimeReconciler.js +5 -9
  188. package/dist/runtime/runtimeBinding.js +0 -1
  189. package/dist/runtime/runtimeContinuationProjection.js +4 -7
  190. package/dist/runtime/runtimeDeadlines.js +9 -0
  191. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  192. package/dist/runtime/runtimeObservation.js +29 -65
  193. package/dist/runtime/runtimeProjection.js +43 -51
  194. package/dist/runtime/runtimeSessionCandidate.js +1 -3
  195. package/dist/runtime/sessionLaunchRequest.js +3 -7
  196. package/dist/runtime/sessionOwnerIdentity.js +7 -54
  197. package/dist/runtime/sessionOwnerRegistry.js +22 -17
  198. package/dist/runtime/sessionReconciliation.js +4 -8
  199. package/dist/runtime/sessionTerminationGuard.js +70 -259
  200. package/dist/runtime/sessionTokenMetrics.js +5 -16
  201. package/dist/runtime/structuredProviderHost.js +237 -117
  202. package/dist/runtime/taskRuntimeIsolation.js +39 -122
  203. package/dist/runtime/tmuxAdapters.js +39 -86
  204. package/dist/scheduler/activeRoleRunDelivery.js +354 -0
  205. package/dist/scheduler/leaderWakeupProcessor.js +75 -266
  206. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  207. package/dist/scheduler/ports.js +80 -9
  208. package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
  209. package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
  210. package/dist/scheduler/taskExecutionProjection.js +120 -124
  211. package/dist/scheduler/taskObservabilityProjection.js +29 -29
  212. package/dist/scheduler/taskWake.js +11 -4
  213. package/dist/scheduler/wakeReason.js +9 -1
  214. package/dist/setup/setupCommand.js +3 -7
  215. package/dist/storage/migrations/agentRunContract.js +159 -0
  216. package/dist/storage/migrations/artifactsToGit.js +338 -0
  217. package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
  218. package/dist/storage/migrations/submitIntent.js +126 -0
  219. package/dist/storage/sqliteSchema.js +467 -7
  220. package/dist/storage/sqliteStore.js +355 -220
  221. package/dist/storage/storageVersions.js +1 -1
  222. package/dist/storage/storeRpc.js +10 -5
  223. package/dist/storage/taskStore.js +13 -11
  224. package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
  225. package/dist/surface/surfaceContributions.js +102 -0
  226. package/dist/task/completionReadiness.js +32 -6
  227. package/dist/task/deliveryGuard.js +16 -16
  228. package/dist/task/draftPlan.js +72 -12
  229. package/dist/task/nextAction.js +144 -128
  230. package/dist/task/remoteDelivery.js +6 -6
  231. package/dist/task/task.js +184 -18
  232. package/dist/task/taskActivation.js +327 -0
  233. package/dist/task/taskActivationService.js +408 -0
  234. package/dist/task/taskRecordReference.js +5 -4
  235. package/dist/task/taskRecordRetirement.js +1 -1
  236. package/dist/task/taskSubmission.js +236 -0
  237. package/dist/telemetry/sqliteTelemetryStore.js +55 -68
  238. package/dist/telemetry/telemetryConfig.js +14 -14
  239. package/dist/telemetry/telemetryWiring.js +2 -2
  240. package/dist/web/assets/assetManifest.js +2 -0
  241. package/dist/web/assets/client/app.js +121 -20
  242. package/dist/web/assets/client/components.js +87 -54
  243. package/dist/web/assets/client/i18n.js +83 -41
  244. package/dist/web/assets/client/markdown.js +1 -1
  245. package/dist/web/assets/client/taskSurface.js +442 -0
  246. package/dist/web/assets/client/view.js +49 -44
  247. package/dist/web/assets/shell.js +1 -1
  248. package/dist/web/assets/styles/cards.js +22 -4
  249. package/dist/web/controllerWeb.js +60 -0
  250. package/dist/web/webMutation.js +28 -0
  251. package/dist/web/webServer.js +133 -8
  252. package/dist/web/webSnapshot.js +81 -74
  253. package/dist/web/webTaskSurface.js +64 -0
  254. package/dist/workItem/dependencyGate.js +1 -1
  255. package/dist/workItem/workItem.js +84 -48
  256. package/dist/workspace/workItemChangeSetManager.js +16 -9
  257. package/docs/agent-result-consumption.md +96 -0
  258. package/docs/agent-result-consumption.zh-CN.md +81 -0
  259. package/docs/agent-runtime-drivers.md +93 -0
  260. package/docs/agent-runtime-drivers.zh-CN.md +77 -0
  261. package/docs/architecture/README.md +50 -0
  262. package/docs/architecture/README.zh-CN.md +43 -0
  263. package/docs/architecture/capabilities-and-resources.md +118 -0
  264. package/docs/architecture/capabilities-and-resources.zh-CN.md +83 -0
  265. package/docs/managed-turn-and-session-runtime.md +224 -0
  266. package/docs/managed-turn-and-session-runtime.zh-CN.md +180 -0
  267. package/docs/observability/README.md +83 -0
  268. package/docs/observability/README.zh-CN.md +71 -0
  269. package/docs/plugin-sdk.md +393 -0
  270. package/docs/plugin-sdk.zh-CN.md +293 -0
  271. package/docs/provider-runtime.md +165 -0
  272. package/docs/provider-runtime.zh-CN.md +132 -0
  273. package/docs/release-workflow.md +305 -0
  274. package/docs/release-workflow.zh-CN.md +237 -0
  275. package/docs/roles-and-configuration.md +115 -0
  276. package/docs/roles-and-configuration.zh-CN.md +96 -0
  277. package/docs/sqlite-control-plane-design.md +78 -0
  278. package/docs/sqlite-control-plane-design.zh-CN.md +62 -0
  279. package/docs/task-dag-semantics.md +80 -0
  280. package/docs/task-dag-semantics.zh-CN.md +59 -0
  281. package/docs/task-delivery.md +105 -0
  282. package/docs/task-delivery.zh-CN.md +82 -0
  283. package/docs/task-local-identity.md +8 -6
  284. package/docs/task-local-identity.zh-CN.md +58 -0
  285. package/docs/testing/verification-levels.md +88 -0
  286. package/docs/testing/verification-levels.zh-CN.md +69 -0
  287. package/i18n/README.zh-CN.md +270 -722
  288. package/package.json +3 -2
  289. package/skills/yui-leader/SKILL.md +88 -304
  290. package/skills/yui-leader/references/execution.md +303 -0
  291. package/skills/yui-leader/references/integration.md +39 -0
  292. package/skills/yui-leader/references/planning.md +109 -0
  293. package/skills/yui-leader/references/replicated-execution.md +42 -0
  294. package/skills/yui-leader/references/task-plugins.md +37 -0
  295. package/skills/yui-operator/SKILL.md +46 -62
  296. package/skills/yui-reviewer/SKILL.md +35 -36
  297. package/skills/yui-runtime/SKILL.md +88 -24
  298. package/skills/yui-runtime/references/publication.md +22 -0
  299. package/skills/yui-runtime/references/recovery.md +64 -0
  300. package/skills/yui-worker/SKILL.md +37 -39
  301. package/dist/cli/roleOptionCatalog.js +0 -68
  302. package/dist/context/sourceTurnContext.js +0 -30
  303. package/dist/execution/reviewMainTurn.js +0 -161
  304. package/dist/execution/workItemMainTurn.js +0 -164
  305. package/dist/lifecycle/exactTurnTerminalization.js +0 -407
  306. package/dist/runtime/preallocatedNativeSession.js +0 -13
  307. package/dist/runtime/runtimeStopReceipt.js +0 -42
  308. 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 { DEFAULT_TURN_CAP, DEFAULT_TERMINAL_KEEP } from "./telemetryConfig.js";
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-Turn/generation
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.turnCap ?? DEFAULT_TURN_CAP;
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, turnId) {
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 (turnId === undefined) {
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, turnId).n;
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, turnId, page = { limit: 100, offset: 0 }) {
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 = 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);
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, turnId);
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, turnId) {
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, generation, first_at, last_at, count, max_sequence, error_count FROM telemetry_aggregate WHERE task_id = ? AND turn_id = ?").all(taskId, turnId);
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
- aggregateGeneration(taskId, roleName, turnId, generation) {
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, 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);
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
- turnId: row.turn_id,
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
- listTurnAggregates(taskId) {
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, generation, first_at, last_at, count, max_sequence, error_count FROM telemetry_aggregate WHERE task_id = ? ORDER BY turn_id, generation").all(taskId);
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
- turnId: row.turn_id,
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
- pruneGeneration(taskId, roleName, turnId, generation, keep = this.#terminalKeep) {
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 = ? 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
- ORDER BY COALESCE(sequence, -1) DESC, received_at DESC, progress_id ASC
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, turnId, generation, taskId, roleName, turnId, generation, keep);
176
+ )`).run(taskId, roleName, runId, taskId, roleName, runId, keep);
179
177
  return result.changes;
180
178
  }
181
- capTurn(taskId, turnId, cap = this.#turnCap) {
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, generation, progress_id) NOT IN (
188
- SELECT task_id, role_name, turn_id, generation, progress_id FROM telemetry
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, received_at DESC, progress_id ASC
188
+ ORDER BY received_at DESC, COALESCE(sequence, -1) DESC, progress_id ASC
191
189
  LIMIT ?
192
- )`).run(taskId, turnId, taskId, turnId, cap);
190
+ )`).run(taskId, runId, taskId, runId, cap);
193
191
  return result.changes;
194
192
  }
195
- importGeneration(entries, aggregate) {
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, generation, progress_id, sequence, payload, received_at)
202
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
203
- ON CONFLICT(task_id, role_name, turn_id, generation, progress_id) DO UPDATE SET
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 COALESCE(excluded.sequence, -1) > COALESCE(telemetry.sequence, -1)
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, generation, first_at, last_at, count, max_sequence, error_count, updated_at)
211
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
212
- ON CONFLICT(task_id, role_name, turn_id, generation) DO UPDATE SET
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.turnId, entry.generation, entry.progressId, entry.sequence ?? null, JSON.stringify(entry.payload), entry.receivedAt);
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.turnId, aggregate.generation, aggregate.firstAt, aggregate.lastAt, aggregate.count, aggregate.maxSequence, aggregate.errorCount, aggregate.lastAt);
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, generation, progress_id, sequence, payload, received_at)
281
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)
282
- ON CONFLICT(task_id, role_name, turn_id, generation, progress_id) DO UPDATE SET
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 COALESCE(excluded.sequence, -1) > COALESCE(telemetry.sequence, -1)
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 touchedTurns = new Map();
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.turnId, entry.generation, entry.progressId, entry.sequence ?? null, JSON.stringify(entry.payload), entry.receivedAt);
293
- touchedTurns.set(`${entry.taskId}\0${entry.turnId}`, {
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
- turnId: entry.turnId
291
+ runId: entry.runId
296
292
  });
297
293
  }
298
- for (const { taskId, turnId } of touchedTurns.values()) {
299
- this.capTurn(taskId, turnId);
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.turnId}\u0000${entry.generation}\u0000${entry.progressId}`;
307
+ return `${entry.taskId}\u0000${entry.roleName}\u0000${entry.runId}\u0000${entry.progressId}`;
312
308
  }
313
- /** True when `candidate` should replace `current` (sequence/receivedAt only move forward). */
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
- turnId: row.turn_id,
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
- turnId: rows[0].turn_id,
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 { TELEMETRY_KEEP_PER_GENERATION, TELEMETRY_TURN_CAP } from "../storage/sqliteSchema.js";
9
+ import { TELEMETRY_KEEP_PER_RUN, TELEMETRY_RUN_CAP } from "../storage/sqliteSchema.js";
10
10
  export const DEFAULT_TELEMETRY_MODE = "off";
11
- /** Terminal Turn/generation progress rows retained after prune. */
12
- export const DEFAULT_TERMINAL_KEEP = TELEMETRY_KEEP_PER_GENERATION;
13
- /** Hard cap of progress rows per Turn while it is still active. */
14
- export const DEFAULT_TURN_CAP = TELEMETRY_TURN_CAP;
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 Turn cap. Retention can be tuned but
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 × Turns × cap).
18
+ * rows (Tasks × AgentRuns × cap).
19
19
  */
20
- export const MAX_TURN_CAP = 10_000_000;
20
+ export const MAX_RUN_CAP = 10_000_000;
21
21
  /**
22
- * Resolve the terminal-Turn retention window from the durable config value
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-Turn hard cap from the durable config value (default
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 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).`);
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 { resolveTurnCap, resolveTerminalKeep } from "./telemetryConfig.js";
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
- turnCap: resolveTurnCap(config.telemetryTurnCap)
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"),