@zq-silk/yui 0.15.11 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) hide show
  1. package/ARCHITECTURE.md +11 -6
  2. package/ARCHITECTURE.zh-CN.md +8 -4
  3. package/README.md +13 -5
  4. package/dist/agentRun/agentRun.js +3 -0
  5. package/dist/artifacts/artifactCommitLock.js +16 -55
  6. package/dist/artifacts/taskArtifactRepository.js +3 -3
  7. package/dist/cli/agentConfigurationPicker.js +1 -1
  8. package/dist/cli/commandCatalog.js +92 -48
  9. package/dist/cli/completion.js +18 -18
  10. package/dist/cli/completionWizard.js +1 -1
  11. package/dist/cli/dynamicCompletion.js +1 -1
  12. package/dist/cli/interactionPolicy.js +11 -3
  13. package/dist/cli/invocationAuthority.js +64 -0
  14. package/dist/cli/managedDiagnostics.js +2 -2
  15. package/dist/cli/parseRepeatable.js +35 -0
  16. package/dist/cli/updatePorts.js +26 -5
  17. package/dist/cli.js +1303 -1235
  18. package/dist/commands/agentCommands.js +4 -4
  19. package/dist/commands/capabilityCommands.js +1 -1
  20. package/dist/commands/configCommands.js +6 -57
  21. package/dist/commands/configOverview.js +2 -2
  22. package/dist/commands/durableJobCommands.js +12 -6
  23. package/dist/commands/executionAuditCommands.js +10 -0
  24. package/dist/commands/globalRoleCommands.js +78 -29
  25. package/dist/commands/grantCommands.js +0 -3
  26. package/dist/commands/jobCommands.js +1 -4
  27. package/dist/commands/operatorCommands.js +3 -3
  28. package/dist/commands/projectCommands.js +45 -25
  29. package/dist/commands/resourcesCommands.js +10 -40
  30. package/dist/commands/roleConfiguration.js +2 -6
  31. package/dist/commands/taskActivationCommands.js +4 -8
  32. package/dist/commands/taskAgentCapabilities.js +9 -0
  33. package/dist/commands/taskCommandSupport.js +155 -0
  34. package/dist/commands/taskCommandTypes.js +1 -0
  35. package/dist/commands/taskCommands.js +355 -732
  36. package/dist/commands/taskCompletionGate.js +1 -1
  37. package/dist/commands/taskExecutionCommands.js +1 -1
  38. package/dist/commands/taskFactCommands.js +325 -0
  39. package/dist/commands/taskInputCommands.js +12 -42
  40. package/dist/commands/taskIntegrationCommands.js +20 -58
  41. package/dist/commands/taskNextActionCommand.js +7 -6
  42. package/dist/commands/taskPublicationAdoptCommand.js +127 -0
  43. package/dist/commands/taskPublicationCommands.js +12 -3
  44. package/dist/commands/taskPublicationVerifyCommand.js +24 -40
  45. package/dist/commands/taskRemoteDeliveryCommand.js +5 -86
  46. package/dist/commands/taskUpstreamCommands.js +19 -8
  47. package/dist/commands/workflowCommands.js +6 -1
  48. package/dist/completion/completionInstaller.js +26 -26
  49. package/dist/completion/completionState.js +26 -26
  50. package/dist/completion/fileCompletionManager.js +6 -11
  51. package/dist/config/configCatalog.js +0 -2
  52. package/dist/config/timeZone.js +14 -0
  53. package/dist/config/yuiConfig.js +0 -29
  54. package/dist/context/runContextPack.js +5 -3
  55. package/dist/context/taskCatalog.js +185 -0
  56. package/dist/context/taskContext.js +152 -151
  57. package/dist/context/wakeRunReferences.js +11 -0
  58. package/dist/controller/agentCapabilities.js +58 -0
  59. package/dist/controller/agentRuntimeObserver.js +6 -11
  60. package/dist/controller/clientRuntime.js +22 -14
  61. package/dist/controller/controller.js +20 -24
  62. package/dist/controller/fileSchedulerStoreAdapter.js +191 -225
  63. package/dist/controller/globalInputDelivery.js +13 -0
  64. package/dist/controller/jobClient.js +4 -4
  65. package/dist/controller/jobControl.js +60 -25
  66. package/dist/controller/jobSupervisor.js +4 -4
  67. package/dist/controller/providerRetryAdmission.js +100 -0
  68. package/dist/controller/providerRetryDelivery.js +218 -0
  69. package/dist/controller/runtime.js +85 -43
  70. package/dist/controller/runtimeEventProcessor.js +0 -5
  71. package/dist/controller/sessionOwnerReconciliation.js +5 -0
  72. package/dist/controller/taskAgentError.js +35 -0
  73. package/dist/coordination/workMailboxQueue.js +2 -24
  74. package/dist/core/controllerClient.js +2 -2
  75. package/dist/core/fileLockOwner.js +74 -0
  76. package/dist/decision/decision.js +17 -0
  77. package/dist/doctor/doctor.js +18 -9
  78. package/dist/event/taskEvent.js +12 -0
  79. package/dist/execution/workItemExecutionProjection.js +2 -2
  80. package/dist/executor/agentCapabilityConfig.js +43 -0
  81. package/dist/executor/agentConfigurationCatalog.js +49 -11
  82. package/dist/executor/agentExecutor.js +5 -8
  83. package/dist/executor/fileRoleLaunchPlanner.js +11 -53
  84. package/dist/executor/taskAgentCapabilities.js +64 -0
  85. package/dist/integration/deliveryObligation.js +1 -69
  86. package/dist/integration/gitIntegrationService.js +79 -90
  87. package/dist/integration/integrationAttempt.js +3 -14
  88. package/dist/integration/integrationSourceApplication.js +4 -12
  89. package/dist/integration/manifestTags.js +2 -2
  90. package/dist/job/durableJob.js +4 -25
  91. package/dist/job/jobAssignmentScope.js +22 -0
  92. package/dist/job/jobOperation.js +16 -0
  93. package/dist/job/jobRunner.js +2 -1
  94. package/dist/job/stepDirectory.js +15 -0
  95. package/dist/kernel/builtinCapabilities.js +15 -11
  96. package/dist/kernel/kernelPorts.js +1 -17
  97. package/dist/lifecycle/exactRunTerminalization.js +5 -10
  98. package/dist/message/globalProviderRetry.js +15 -0
  99. package/dist/message/inputControlResolution.js +3 -4
  100. package/dist/message/message.js +17 -20
  101. package/dist/message/messageContinuation.js +1 -1
  102. package/dist/milestone/milestone.js +11 -0
  103. package/dist/observability/executionAudit.js +19 -0
  104. package/dist/observability/orchestrationMetrics.js +24 -1
  105. package/dist/observability/runtimeIdentity.js +1 -17
  106. package/dist/output/agentConfigurationPresentation.js +2 -0
  107. package/dist/output/timePresentation.js +1 -14
  108. package/dist/plugins/pluginService.js +12 -4
  109. package/dist/release/releaseWorkflowPorts.js +45 -96
  110. package/dist/release/runtimeRelease.js +9 -15
  111. package/dist/repository/gitWorkspace.js +371 -105
  112. package/dist/repository/projectMaintenanceLock.js +98 -82
  113. package/dist/repository/taskBaseFreshness.js +1 -1
  114. package/dist/repository/taskWorkspaceCoordinator.js +82 -144
  115. package/dist/repository/taskWorkspacePreparer.js +113 -47
  116. package/dist/repository/workspaceCleanupInspection.js +187 -0
  117. package/dist/resources/autoResourceGc.js +4 -77
  118. package/dist/resources/liveReferences.js +7 -54
  119. package/dist/resources/resourceDiscovery.js +5 -118
  120. package/dist/resources/resourceGc.js +233 -181
  121. package/dist/resources/resourceRegistrar.js +5 -4
  122. package/dist/resources/resourceRegistry.js +3 -37
  123. package/dist/resources/resourceRegistryStore.js +13 -36
  124. package/dist/resources/sqliteResourceRegistry.js +43 -26
  125. package/dist/review/deltaRecheck.js +1 -1
  126. package/dist/review/reviewAcceptance.js +1 -1
  127. package/dist/review/reviewDecision.js +1 -1
  128. package/dist/review/reviewRound.js +8 -7
  129. package/dist/review/taskFinalReviewContractResolution.js +1 -1
  130. package/dist/runtime/acpProtocol.js +4 -51
  131. package/dist/runtime/acpSession.js +10 -75
  132. package/dist/runtime/acpSessionConfiguration.js +1 -8
  133. package/dist/runtime/agentEndpoint.js +3 -2
  134. package/dist/runtime/agentError.js +5 -3
  135. package/dist/runtime/agentFailureContext.js +43 -0
  136. package/dist/runtime/agentHost.js +42 -25
  137. package/dist/runtime/builtinAgentErrorMappers.js +91 -0
  138. package/dist/runtime/codexAppServerRuntime.js +34 -3
  139. package/dist/runtime/codexInteractiveHost.js +2 -2
  140. package/dist/runtime/index.js +0 -1
  141. package/dist/runtime/managedCaller.js +21 -1
  142. package/dist/runtime/providerControl.js +5 -1
  143. package/dist/runtime/providerErrors.js +38 -0
  144. package/dist/runtime/providerRetry.js +198 -0
  145. package/dist/runtime/providerRuntimeIdentity.js +28 -2
  146. package/dist/runtime/runtimeObservation.js +0 -5
  147. package/dist/runtime/runtimeSessionCandidate.js +2 -3
  148. package/dist/runtime/sessionOwnerIdentity.js +1 -1
  149. package/dist/runtime/sessionTokenMetrics.js +15 -5
  150. package/dist/runtime/structuredProviderHost.js +10 -43
  151. package/dist/runtime/taskUsageMetrics.js +275 -0
  152. package/dist/runtime/tmuxAdapters.js +6 -8
  153. package/dist/scheduler/activeRoleRunDelivery.js +19 -7
  154. package/dist/scheduler/leaderWakeupProcessor.js +24 -6
  155. package/dist/scheduler/operatorEvent.js +12 -20
  156. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  157. package/dist/scheduler/ports.js +5 -8
  158. package/dist/scheduler/roleRunLiveness.js +4 -4
  159. package/dist/scheduler/roleRunStall.js +16 -23
  160. package/dist/scheduler/taskExecutionProjection.js +49 -36
  161. package/dist/scheduler/taskObservabilityProjection.js +6 -45
  162. package/dist/scheduler/taskWake.js +5 -10
  163. package/dist/scheduler/wakeReason.js +2 -0
  164. package/dist/scheduler/wakeupQueue.js +2 -4
  165. package/dist/setup/setupCommand.js +1 -1
  166. package/dist/storage/contextRecords.js +106 -0
  167. package/dist/storage/currentTaskStore.js +1 -1
  168. package/dist/storage/homeLayout.js +13 -17
  169. package/dist/storage/migrations/agentFailureContext.js +22 -0
  170. package/dist/storage/migrations/currentInputContract.js +86 -0
  171. package/dist/storage/migrations/currentRuntimeContract.js +228 -0
  172. package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
  173. package/dist/storage/migrations/integrationContinuation.js +5 -4
  174. package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
  175. package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
  176. package/dist/storage/migrations/verificationPlanV1.js +162 -0
  177. package/dist/storage/migrations/verificationPolicy.js +74 -0
  178. package/dist/storage/migrations/workItemHistory.js +46 -0
  179. package/dist/storage/persistenceWorker.js +12 -4
  180. package/dist/storage/recordValidation.js +87 -0
  181. package/dist/storage/sqliteSchema.js +173 -1
  182. package/dist/storage/sqliteStore.js +172 -161
  183. package/dist/storage/storageSchema.js +2 -14
  184. package/dist/storage/storageVersions.js +1 -1
  185. package/dist/storage/storeRpc.js +16 -7
  186. package/dist/storage/taskCatalog.js +123 -0
  187. package/dist/storage/taskStore.js +5 -7
  188. package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
  189. package/dist/task/archiveDiagnostics.js +1 -0
  190. package/dist/task/archivePreflight.js +124 -0
  191. package/dist/task/completionReadiness.js +5 -24
  192. package/dist/task/draftPlan.js +0 -53
  193. package/dist/task/nextAction.js +7 -13
  194. package/dist/task/publicationAdoption.js +56 -0
  195. package/dist/task/publicationReference.js +10 -0
  196. package/dist/task/remoteDelivery.js +31 -16
  197. package/dist/task/remoteDeliveryService.js +89 -0
  198. package/dist/task/taskActivation.js +2 -25
  199. package/dist/task/taskActivationService.js +0 -2
  200. package/dist/task/taskRecordReference.js +0 -1
  201. package/dist/task/taskSubmission.js +3 -14
  202. package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
  203. package/dist/telemetry/sqliteTelemetryStore.js +49 -49
  204. package/dist/telemetry/telemetryWiring.js +4 -3
  205. package/dist/tmux/tmuxManager.js +29 -20
  206. package/dist/verification/gateArtifact.js +15 -24
  207. package/dist/verification/gateArtifactStore.js +14 -7
  208. package/dist/verification/verificationGateService.js +29 -88
  209. package/dist/verification/verificationPlan.js +27 -72
  210. package/dist/web/assets/client/app.js +92 -17
  211. package/dist/web/assets/client/components.js +55 -13
  212. package/dist/web/assets/client/i18n.js +72 -4
  213. package/dist/web/assets/client/taskSurface.js +6 -5
  214. package/dist/web/assets/client/view.js +35 -7
  215. package/dist/web/assets/shell.js +6 -0
  216. package/dist/web/assets/styles/layout.js +7 -0
  217. package/dist/web/webServer.js +12 -3
  218. package/dist/web/webSnapshot.js +14 -85
  219. package/dist/web/webTaskSurface.js +17 -46
  220. package/dist/workItem/workItem.js +1 -12
  221. package/dist/workspace/cleanupInspection.js +63 -0
  222. package/dist/workspace/workItemChangeSetManager.js +110 -50
  223. package/docs/agent-result-consumption.md +4 -0
  224. package/docs/agent-result-consumption.zh-CN.md +3 -0
  225. package/docs/agent-runtime-drivers.md +7 -0
  226. package/docs/agent-runtime-drivers.zh-CN.md +5 -0
  227. package/docs/architecture/README.md +2 -0
  228. package/docs/architecture/README.zh-CN.md +3 -1
  229. package/docs/architecture/capabilities-and-resources.md +42 -5
  230. package/docs/architecture/capabilities-and-resources.zh-CN.md +33 -3
  231. package/docs/managed-turn-and-session-runtime.md +121 -1
  232. package/docs/managed-turn-and-session-runtime.zh-CN.md +96 -2
  233. package/docs/observability/README.md +62 -0
  234. package/docs/observability/README.zh-CN.md +47 -0
  235. package/docs/plugin-sdk.md +4 -2
  236. package/docs/project-refresh.md +77 -0
  237. package/docs/project-refresh.zh-CN.md +59 -0
  238. package/docs/provider-retry.md +70 -0
  239. package/docs/provider-runtime.md +3 -1
  240. package/docs/provider-runtime.zh-CN.md +4 -2
  241. package/docs/release-workflow.md +190 -7
  242. package/docs/release-workflow.zh-CN.md +139 -5
  243. package/docs/roles-and-configuration.md +29 -0
  244. package/docs/roles-and-configuration.zh-CN.md +21 -0
  245. package/docs/sqlite-control-plane-design.md +16 -3
  246. package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
  247. package/docs/task-dag-semantics.md +3 -3
  248. package/docs/task-dag-semantics.zh-CN.md +2 -2
  249. package/docs/task-delivery.md +223 -17
  250. package/docs/task-delivery.zh-CN.md +171 -14
  251. package/docs/task-discovery.md +101 -0
  252. package/docs/task-discovery.zh-CN.md +85 -0
  253. package/docs/testing/verification-levels.md +161 -9
  254. package/docs/testing/verification-levels.zh-CN.md +116 -9
  255. package/i18n/README.zh-CN.md +13 -7
  256. package/package.json +1 -1
  257. package/skills/yui-leader/SKILL.md +5 -0
  258. package/skills/yui-leader/references/execution.md +3 -2
  259. package/skills/yui-leader/references/integration.md +9 -5
  260. package/skills/yui-leader/references/planning.md +9 -3
  261. package/skills/yui-operator/SKILL.md +24 -8
  262. package/skills/yui-reviewer/SKILL.md +4 -0
  263. package/skills/yui-runtime/SKILL.md +17 -2
  264. package/skills/yui-runtime/references/publication.md +26 -4
  265. package/skills/yui-runtime/references/recovery.md +71 -0
  266. package/dist/agent/agentRegistry.js +0 -10
  267. package/dist/agentRun/runIdentity.js +0 -22
  268. package/dist/commands/deliveryGuardPreflight.js +0 -30
  269. package/dist/commands/taskIntegrationQueueCommands.js +0 -226
  270. package/dist/commands/taskOverviewCommand.js +0 -376
  271. package/dist/completion/completionWizard.js +0 -125
  272. package/dist/context/dispatchContext.js +0 -110
  273. package/dist/context/wakeNotification.js +0 -144
  274. package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
  275. package/dist/integration/integrationQueueEntry.js +0 -191
  276. package/dist/integration/integrationQueueService.js +0 -810
  277. package/dist/release/fakeReleasePorts.js +0 -55
  278. package/dist/runtime/sessionOwnerRegistry.js +0 -137
  279. package/dist/task/deliveryGuard.js +0 -226
  280. /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
@@ -3,9 +3,9 @@ import { chmodSync, existsSync, lstatSync, mkdirSync, readFileSync, renameSync,
3
3
  import { dirname, isAbsolute, join, resolve } from "node:path";
4
4
  import { dataError } from "../errors/cliError.js";
5
5
  import { activationBlock, activationEnd, activationIsAutomatic, activationStart, completionMarker, managedCompletionScript } from "./completionState.js";
6
- export function installCompletion(store, shell, installation, env, identity, activate) {
6
+ export function installCompletion(store, shell, installation, env, activate) {
7
7
  validateInstallation(installation);
8
- writeManagedScript(shell, installation.scriptPath, identity);
8
+ writeManagedScript(shell, installation.scriptPath);
9
9
  store.transaction((tx) => {
10
10
  const config = tx.getConfig();
11
11
  tx.saveConfig({
@@ -16,20 +16,20 @@ export function installCompletion(store, shell, installation, env, identity, act
16
16
  }
17
17
  });
18
18
  });
19
- if (!activationIsAutomatic(shell, installation, env, identity) && activate) {
20
- writeActivationBlock(shell, installation, identity);
19
+ if (!activationIsAutomatic(shell, installation, env) && activate) {
20
+ writeActivationBlock(shell, installation);
21
21
  }
22
22
  }
23
- export function uninstallCompletion(store, shell, identity) {
23
+ export function uninstallCompletion(store, shell) {
24
24
  store.transaction((tx) => {
25
25
  const config = tx.getConfig();
26
26
  const installation = config.completionInstallations?.[shell];
27
27
  if (installation === undefined)
28
28
  return;
29
- assertManagedScriptRemovable(shell, installation.scriptPath, identity);
30
- assertActivationRemovable(shell, installation, identity);
31
- removeManagedScript(shell, installation.scriptPath, identity);
32
- removeActivationBlock(shell, installation, identity);
29
+ assertManagedScriptRemovable(shell, installation.scriptPath);
30
+ assertActivationRemovable(shell, installation);
31
+ removeManagedScript(shell, installation.scriptPath);
32
+ removeActivationBlock(shell, installation);
33
33
  const installations = { ...config.completionInstallations };
34
34
  delete installations[shell];
35
35
  if (Object.keys(installations).length > 0) {
@@ -40,17 +40,17 @@ export function uninstallCompletion(store, shell, identity) {
40
40
  tx.saveConfig(withoutInstallations);
41
41
  });
42
42
  }
43
- function assertManagedScriptRemovable(shell, path, identity) {
43
+ function assertManagedScriptRemovable(shell, path) {
44
44
  if (!existsSync(path))
45
45
  return;
46
46
  const stat = lstatSync(path);
47
47
  if (!stat.isFile()
48
48
  || stat.isSymbolicLink()
49
- || !readFileSync(path, "utf8").startsWith(completionMarker(shell, identity))) {
49
+ || !readFileSync(path, "utf8").startsWith(completionMarker(shell))) {
50
50
  throw dataError(`Refusing to remove unmanaged completion script: ${path}`);
51
51
  }
52
52
  }
53
- function assertActivationRemovable(shell, installation, identity) {
53
+ function assertActivationRemovable(shell, installation) {
54
54
  const path = installation.activationPath;
55
55
  if (!existsSync(path))
56
56
  return;
@@ -59,25 +59,25 @@ function assertActivationRemovable(shell, installation, identity) {
59
59
  throw dataError(`Refusing to modify unsafe activation file: ${path}`);
60
60
  }
61
61
  const contents = readFileSync(path, "utf8");
62
- const starts = occurrences(contents, activationStart(shell, identity));
63
- const ends = occurrences(contents, activationEnd(shell, identity));
62
+ const starts = occurrences(contents, activationStart(shell));
63
+ const ends = occurrences(contents, activationEnd(shell));
64
64
  if (starts !== ends || starts > 1) {
65
65
  throw dataError(`Refusing to remove ambiguous Yui activation block: ${path}`);
66
66
  }
67
67
  }
68
- function writeManagedScript(shell, path, identity) {
68
+ function writeManagedScript(shell, path) {
69
69
  if (existsSync(path)) {
70
70
  const stat = lstatSync(path);
71
71
  if (!stat.isFile() || stat.isSymbolicLink()) {
72
72
  throw dataError(`Refusing to overwrite unsafe completion script: ${path}`);
73
73
  }
74
- if (!readFileSync(path, "utf8").startsWith(completionMarker(shell, identity))) {
74
+ if (!readFileSync(path, "utf8").startsWith(completionMarker(shell))) {
75
75
  throw dataError(`Refusing to overwrite unmanaged completion script: ${path}`);
76
76
  }
77
77
  }
78
- writeAtomic(path, managedCompletionScript(shell, identity), 0o644);
78
+ writeAtomic(path, managedCompletionScript(shell), 0o644);
79
79
  }
80
- function writeActivationBlock(shell, installation, identity) {
80
+ function writeActivationBlock(shell, installation) {
81
81
  const path = installation.activationPath;
82
82
  let contents = "";
83
83
  let mode = 0o644;
@@ -89,31 +89,31 @@ function writeActivationBlock(shell, installation, identity) {
89
89
  contents = readFileSync(path, "utf8");
90
90
  mode = stat.mode & 0o777;
91
91
  }
92
- const start = activationStart(shell, identity);
93
- const end = activationEnd(shell, identity);
92
+ const start = activationStart(shell);
93
+ const end = activationEnd(shell);
94
94
  const starts = occurrences(contents, start);
95
95
  const ends = occurrences(contents, end);
96
96
  if (starts > 1 || ends > 1 || starts !== ends) {
97
97
  throw dataError(`Refusing to modify ambiguous Yui activation block: ${path}`);
98
98
  }
99
- const block = activationBlock(shell, installation, identity);
99
+ const block = activationBlock(shell, installation);
100
100
  const next = starts === 1
101
101
  ? replaceManagedBlock(contents, start, end, block)
102
102
  : `${contents}${contents.length === 0 || contents.endsWith("\n") ? "" : "\n"}${block}\n`;
103
103
  writeAtomic(path, next, mode);
104
104
  }
105
- function removeManagedScript(shell, path, identity) {
105
+ function removeManagedScript(shell, path) {
106
106
  if (!existsSync(path))
107
107
  return;
108
108
  const stat = lstatSync(path);
109
109
  if (!stat.isFile()
110
110
  || stat.isSymbolicLink()
111
- || !readFileSync(path, "utf8").startsWith(completionMarker(shell, identity))) {
111
+ || !readFileSync(path, "utf8").startsWith(completionMarker(shell))) {
112
112
  throw dataError(`Refusing to remove unmanaged completion script: ${path}`);
113
113
  }
114
114
  rmSync(path);
115
115
  }
116
- function removeActivationBlock(shell, installation, identity) {
116
+ function removeActivationBlock(shell, installation) {
117
117
  const path = installation.activationPath;
118
118
  if (!existsSync(path))
119
119
  return;
@@ -122,8 +122,8 @@ function removeActivationBlock(shell, installation, identity) {
122
122
  throw dataError(`Refusing to modify unsafe activation file: ${path}`);
123
123
  }
124
124
  const contents = readFileSync(path, "utf8");
125
- const start = activationStart(shell, identity);
126
- const end = activationEnd(shell, identity);
125
+ const start = activationStart(shell);
126
+ const end = activationEnd(shell);
127
127
  const starts = occurrences(contents, start);
128
128
  const ends = occurrences(contents, end);
129
129
  if (starts !== ends || starts > 1) {
@@ -10,14 +10,14 @@ export function currentCompletionShell(env) {
10
10
  ? value
11
11
  : undefined;
12
12
  }
13
- export function suggestedCompletionInstallation(shell, env, identity) {
13
+ export function suggestedCompletionInstallation(shell, env) {
14
14
  const home = absoluteEnvRoot("HOME", env.HOME);
15
15
  if (shell === "bash") {
16
16
  const data = env.XDG_DATA_HOME === undefined
17
17
  ? join(home, ".local", "share")
18
18
  : absoluteEnvRoot("XDG_DATA_HOME", env.XDG_DATA_HOME);
19
19
  return {
20
- scriptPath: join(data, "bash-completion", "completions", identity),
20
+ scriptPath: join(data, "bash-completion", "completions", "yui"),
21
21
  activationPath: join(home, ".bashrc")
22
22
  };
23
23
  }
@@ -26,7 +26,7 @@ export function suggestedCompletionInstallation(shell, env, identity) {
26
26
  ? home
27
27
  : absoluteEnvRoot("ZDOTDIR", env.ZDOTDIR);
28
28
  return {
29
- scriptPath: join(zshRoot, ".zfunc", `_${identity}`),
29
+ scriptPath: join(zshRoot, ".zfunc", "_yui"),
30
30
  activationPath: join(zshRoot, ".zshrc")
31
31
  };
32
32
  }
@@ -34,11 +34,11 @@ export function suggestedCompletionInstallation(shell, env, identity) {
34
34
  ? join(home, ".config")
35
35
  : absoluteEnvRoot("XDG_CONFIG_HOME", env.XDG_CONFIG_HOME);
36
36
  return {
37
- scriptPath: join(config, "fish", "completions", `${identity}.fish`),
37
+ scriptPath: join(config, "fish", "completions", "yui.fish"),
38
38
  activationPath: join(config, "fish", "config.fish")
39
39
  };
40
40
  }
41
- export function inspectCompletionStates(config, env, identity) {
41
+ export function inspectCompletionStates(config, env) {
42
42
  const current = currentCompletionShell(env);
43
43
  return COMPLETION_SHELLS.map((shell) => {
44
44
  const installation = config.completionInstallations?.[shell];
@@ -50,8 +50,8 @@ export function inspectCompletionStates(config, env, identity) {
50
50
  current: shell === current
51
51
  };
52
52
  }
53
- const installed = completionScriptIsCurrent(shell, installation, identity)
54
- && completionActivationIsCurrent(shell, installation, env, identity);
53
+ const installed = completionScriptIsCurrent(shell, installation)
54
+ && completionActivationIsCurrent(shell, installation, env);
55
55
  return {
56
56
  shell,
57
57
  status: installed ? "Installed" : "Needs repair",
@@ -78,46 +78,46 @@ export function renderCompletionStateTable(states, width = defaultTableWidth())
78
78
  state.installation?.scriptPath ?? ""
79
79
  ]), width);
80
80
  }
81
- export function managedCompletionScript(shell, identity) {
82
- return `${completionMarker(shell, identity)}\n${renderCompletion(shell, identity)}`;
81
+ export function managedCompletionScript(shell) {
82
+ return `${completionMarker(shell)}\n${renderCompletion(shell)}`;
83
83
  }
84
- export function completionMarker(shell, identity) {
85
- return `# yui-completion: managed shell=${shell} identity=${identity} format=1`;
84
+ export function completionMarker(shell) {
85
+ return `# yui-completion: managed shell=${shell} identity=yui format=1`;
86
86
  }
87
- export function activationBlock(shell, installation, identity) {
88
- const start = activationStart(shell, identity);
89
- const end = activationEnd(shell, identity);
87
+ export function activationBlock(shell, installation) {
88
+ const start = activationStart(shell);
89
+ const end = activationEnd(shell);
90
90
  const source = `source ${shellQuote(installation.scriptPath)}`;
91
91
  const functionName = basename(installation.scriptPath);
92
92
  const body = shell === "zsh"
93
- ? `fpath=(${shellQuote(dirname(installation.scriptPath))} $fpath)\nautoload -Uz compinit\n(( $+functions[compdef] )) || compinit\nautoload -Uz -- ${shellQuote(functionName)}\ncompdef ${shellQuote(functionName)} ${shellQuote(identity)}`
93
+ ? `fpath=(${shellQuote(dirname(installation.scriptPath))} $fpath)\nautoload -Uz compinit\n(( $+functions[compdef] )) || compinit\nautoload -Uz -- ${shellQuote(functionName)}\ncompdef ${shellQuote(functionName)} 'yui'`
94
94
  : source;
95
95
  return `${start}\n${body}\n${end}`;
96
96
  }
97
- export function activationStart(shell, identity) {
98
- return `# >>> yui completion shell=${shell} identity=${identity} >>>`;
97
+ export function activationStart(shell) {
98
+ return `# >>> yui completion shell=${shell} identity=yui >>>`;
99
99
  }
100
- export function activationEnd(shell, identity) {
101
- return `# <<< yui completion shell=${shell} identity=${identity} <<<`;
100
+ export function activationEnd(shell) {
101
+ return `# <<< yui completion shell=${shell} identity=yui <<<`;
102
102
  }
103
- export function activationIsAutomatic(shell, installation, env, identity) {
103
+ export function activationIsAutomatic(shell, installation, env) {
104
104
  return shell === "fish"
105
- && installation.scriptPath === suggestedCompletionInstallation(shell, env, identity).scriptPath;
105
+ && installation.scriptPath === suggestedCompletionInstallation(shell, env).scriptPath;
106
106
  }
107
107
  export function shellQuote(value) {
108
108
  return `'${value.replaceAll("'", "'\\''")}'`;
109
109
  }
110
- export function completionScriptIsCurrent(shell, installation, identity) {
110
+ export function completionScriptIsCurrent(shell, installation) {
111
111
  return safeRegularFile(installation.scriptPath)
112
- && readFileSync(installation.scriptPath, "utf8") === managedCompletionScript(shell, identity);
112
+ && readFileSync(installation.scriptPath, "utf8") === managedCompletionScript(shell);
113
113
  }
114
- export function completionActivationIsCurrent(shell, installation, env, identity) {
115
- if (activationIsAutomatic(shell, installation, env, identity))
114
+ export function completionActivationIsCurrent(shell, installation, env) {
115
+ if (activationIsAutomatic(shell, installation, env))
116
116
  return true;
117
117
  if (!safeRegularFile(installation.activationPath))
118
118
  return false;
119
119
  const contents = readFileSync(installation.activationPath, "utf8");
120
- const block = activationBlock(shell, installation, identity);
120
+ const block = activationBlock(shell, installation);
121
121
  return contents.split(block).length === 2;
122
122
  }
123
123
  function safeRegularFile(path) {
@@ -4,21 +4,19 @@ import { COMPLETION_SHELLS, completionActivationIsCurrent, completionScriptIsCur
4
4
  export class FileCompletionManager {
5
5
  #store;
6
6
  #env;
7
- #identity;
8
- constructor(store, env, identity) {
7
+ constructor(store, env) {
9
8
  this.#store = store;
10
9
  this.#env = { ...env };
11
- this.#identity = identity;
12
10
  }
13
11
  inspect() {
14
12
  const config = this.#store.getConfig();
15
13
  const currentShell = currentCompletionShell(this.#env);
16
14
  const states = COMPLETION_SHELLS.map((shell) => {
17
- const suggested = suggestedCompletionInstallation(shell, this.#env, this.#identity);
15
+ const suggested = suggestedCompletionInstallation(shell, this.#env);
18
16
  const stored = config.completionInstallations?.[shell];
19
17
  const installation = stored ?? suggested;
20
- const scriptCurrent = completionScriptIsCurrent(shell, installation, this.#identity);
21
- const activationCurrent = completionActivationIsCurrent(shell, installation, this.#env, this.#identity);
18
+ const scriptCurrent = completionScriptIsCurrent(shell, installation);
19
+ const activationCurrent = completionActivationIsCurrent(shell, installation, this.#env);
22
20
  const status = scriptCurrent && activationCurrent
23
21
  ? "Installed"
24
22
  : stored === undefined ? "Not installed" : "Needs repair";
@@ -37,15 +35,12 @@ export class FileCompletionManager {
37
35
  });
38
36
  });
39
37
  return Object.freeze({
40
- identity: this.#identity,
38
+ identity: "yui",
41
39
  ...(currentShell === undefined ? {} : { currentShell }),
42
40
  states: Object.freeze(states)
43
41
  });
44
42
  }
45
43
  install(request) {
46
- installCompletion(this.#store, request.shell, request.installation, this.#env, this.#identity, request.activate);
44
+ installCompletion(this.#store, request.shell, request.installation, this.#env, request.activate);
47
45
  }
48
46
  }
49
- export function resolveCliIdentity(env) {
50
- return env.YUI_CLI_NAME === "yui-dev" ? "yui-dev" : "yui";
51
- }
@@ -14,8 +14,6 @@ export const CONFIG_DEFINITIONS = Object.freeze([
14
14
  { key: "runtime-health", domain: "runtime", property: "runtimeHealth", label: "Runtime health thresholds", summary: "Quiet, checkpoint-overdue, and read-only diagnostic thresholds in seconds; values must be strictly increasing (defaults: 300/900/1800).", takesEffect: "CLI and Web projections immediately; scheduler diagnostics after the Controller restarts." },
15
15
  { key: "agent-launch-inactivity-timeout-seconds", domain: "runtime", property: "agentLaunchInactivityTimeoutSeconds", label: "Agent launch inactivity timeout", summary: "Maximum launch silence before startup fails, 15-3600 seconds (default: 300).", takesEffect: "After the Controller restarts." },
16
16
  { key: "delivery-timeout-seconds", domain: "runtime", property: "deliveryTimeoutSeconds", label: "Delivery timeout", summary: "Total control-plane delivery retry budget, 5-600 seconds (default: 120).", takesEffect: "After the Controller restarts; internal retry cadence remains automatic." },
17
- { key: "leader-next-action", domain: "workflow", property: "leaderNextActionMode", label: "Leader next-action mode", summary: "Leader next-action mode: display, warn, or enforce (default: display).", takesEffect: "The next Leader next-action projection or gate." },
18
- { key: "leader-semantic-budget-runs", domain: "workflow", property: "leaderSemanticBudgetRuns", label: "Leader semantic budget", summary: "Consecutive completed Leader AgentRuns without durable delivery progress before warning, 1-20 (default: 3).", takesEffect: "The next Leader delivery guard evaluation." },
19
17
  { key: "review", domain: "workflow", property: "review", label: "Review", summary: "Optional global WorkItem review rule: a configured Role plus always, leader, or final trigger. Setup leaves it disabled so the Leader may review directly or delegate selectively.", takesEffect: "The next Candidate snapshot; in-flight Candidates and ReviewRounds keep their policy." },
20
18
  { key: "resources-gc-mode", domain: "resources", property: "resourcesGcMode", label: "Resources GC mode", summary: "Resource GC mode: report or quarantine (default: report).", takesEffect: "The next resource GC operation." },
21
19
  { key: "resources-gc-auto-quarantine", domain: "resources", property: "resourcesGcAutoQuarantine", label: "Resources GC auto-quarantine", summary: "Automatically quarantine eligible terminal Task resources (default: false).", takesEffect: "The next eligible automatic resource GC operation." },
@@ -0,0 +1,14 @@
1
+ export const DEFAULT_TIME_ZONE = "Asia/Shanghai";
2
+ export function resolveTimeZone(value) {
3
+ const timeZone = value ?? DEFAULT_TIME_ZONE;
4
+ if (typeof timeZone !== "string" || timeZone.trim() !== timeZone || timeZone.length === 0) {
5
+ throw new TypeError("timeZone must be a valid IANA timezone.");
6
+ }
7
+ try {
8
+ new Intl.DateTimeFormat("en", { timeZone }).format(0);
9
+ }
10
+ catch {
11
+ throw new TypeError("timeZone must be a valid IANA timezone.");
12
+ }
13
+ return timeZone;
14
+ }
@@ -49,31 +49,6 @@ export function reconciliationIntervalMilliseconds(value) {
49
49
  }
50
50
  return seconds * 1_000;
51
51
  }
52
- /**
53
- * Issue 07 (Leader convergence) feature mode. `display` only shows the
54
- * read-only next-action decision support; `warn` additionally reports
55
- * duplicate deliveries and semantic-budget warnings; `enforce` hard-blocks
56
- * only exact duplicates, while semantic-budget exhaustion remains a warning
57
- * because it must not override Leader judgment. The mode is additive and
58
- * optional — Homes without it keep the `display` default, so no config
59
- * migration is required.
60
- */
61
- export const LEADER_NEXT_ACTION_MODES = ["display", "warn", "enforce"];
62
- export const DEFAULT_LEADER_NEXT_ACTION_MODE = "display";
63
- export function resolveLeaderNextActionMode(value) {
64
- if (value === undefined || value === null)
65
- return DEFAULT_LEADER_NEXT_ACTION_MODE;
66
- if (typeof value !== "string") {
67
- throw new TypeError("leaderNextActionMode must be display, warn, or enforce.");
68
- }
69
- const normalized = value.trim().toLowerCase();
70
- if (normalized.length === 0)
71
- return DEFAULT_LEADER_NEXT_ACTION_MODE;
72
- if (!LEADER_NEXT_ACTION_MODES.includes(normalized)) {
73
- throw new TypeError("leaderNextActionMode must be display, warn, or enforce.");
74
- }
75
- return normalized;
76
- }
77
52
  // ── Executable paths ──────────────────────────────────────────────────────
78
53
  export function resolveTmuxBin(value) {
79
54
  if (value === undefined || value === null)
@@ -115,7 +90,6 @@ export const DEFAULT_CONTROLLER_TASK_CONCURRENCY = 4;
115
90
  export const MAX_CONTROLLER_TASK_CONCURRENCY = 32;
116
91
  export const DEFAULT_AGENT_LAUNCH_INACTIVITY_TIMEOUT_SECONDS = 300;
117
92
  export const DEFAULT_DELIVERY_TIMEOUT_SECONDS = 120;
118
- export const DEFAULT_LEADER_SEMANTIC_BUDGET_RUNS = 3;
119
93
  export const DEFAULT_TMUX_HISTORY_LIMIT = 100_000;
120
94
  export function resolveRuntimeHealth(configured) {
121
95
  if (configured === undefined || configured === null)
@@ -141,9 +115,6 @@ export function resolveAgentLaunchInactivityTimeoutSeconds(value) {
141
115
  export function resolveDeliveryTimeoutSeconds(value) {
142
116
  return resolveBoundedPositiveInteger(value, DEFAULT_DELIVERY_TIMEOUT_SECONDS, 5, 600, "deliveryTimeoutSeconds");
143
117
  }
144
- export function resolveLeaderSemanticBudgetRuns(value) {
145
- return resolveBoundedPositiveInteger(value, DEFAULT_LEADER_SEMANTIC_BUDGET_RUNS, 1, 20, "leaderSemanticBudgetRuns");
146
- }
147
118
  export function resolveTmuxHistoryLimit(value) {
148
119
  return resolveBoundedPositiveInteger(value, DEFAULT_TMUX_HISTORY_LIMIT, 1_000, 1_000_000, "tmuxHistoryLimit");
149
120
  }
@@ -1,3 +1,4 @@
1
+ import { providerRetryProjection } from "../runtime/providerRetry.js";
1
2
  import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
2
3
  import { TASK_COMPLETION_PUBLISHED_TREE_AUTHORIZED_EVENT } from "../task/publicationReference.js";
3
4
  import { RUN_INPUT_MAX_DELTAS } from "./runInputContract.js";
@@ -277,8 +278,7 @@ export function buildRunContextPack(store, taskId, runId) {
277
278
  }
278
279
  /** Reads the Task's current in-flight execution, outside the frozen contract. */
279
280
  function readLiveTaskState(store, taskId) {
280
- const activeRuns = store.listRuns(taskId)
281
- .filter((run) => run.status === "active")
281
+ const activeRuns = store.listActiveRuns(taskId)
282
282
  .map((run) => Object.freeze({
283
283
  runId: run.id,
284
284
  roleName: run.roleName,
@@ -287,7 +287,7 @@ function readLiveTaskState(store, taskId) {
287
287
  ...(run.reviewRoundId === undefined ? {} : { reviewRoundId: run.reviewRoundId })
288
288
  }));
289
289
  const activeTaskReviews = store.listReviewRounds(taskId)
290
- .filter((round) => ((round.scope ?? "work-item") === "task"
290
+ .filter((round) => (round.scope === "task"
291
291
  && (round.status === "pending" || round.status === "running")))
292
292
  .map((round) => Object.freeze({
293
293
  reviewRoundId: round.id,
@@ -295,6 +295,8 @@ function readLiveTaskState(store, taskId) {
295
295
  status: round.status
296
296
  }));
297
297
  return Object.freeze({
298
+ providerRetries: Object.freeze(store.listRoleSessionSets(taskId).flatMap(set => set.providerBinding?.retry === undefined
299
+ ? [] : [{ roleName: set.owner.roleName, retry: providerRetryProjection(set.providerBinding) }])),
298
300
  activeRuns: Object.freeze(activeRuns),
299
301
  activeTaskReviews: Object.freeze(activeTaskReviews)
300
302
  });
@@ -0,0 +1,185 @@
1
+ import { usageError } from "../errors/cliError.js";
2
+ import { contextContentDigest } from "./contextSnapshot.js";
3
+ import { materialize, resolveContextReader } from "./taskContext.js";
4
+ import { CATALOG_ATTENTION } from "../storage/taskCatalog.js";
5
+ const TASK_STATUSES = ["draft", "active", "completed", "cancelled", "archived"];
6
+ const PAGE_BYTES = 32 * 1024;
7
+ const SUMMARY_BYTES = 512;
8
+ const MAX_LIMIT = 100;
9
+ export function parseTaskCatalogOptions(args) {
10
+ const values = new Map();
11
+ let all = false;
12
+ for (let i = 0; i < args.length; i++) {
13
+ const key = args[i];
14
+ if (key === "--all" && !all) {
15
+ all = true;
16
+ continue;
17
+ }
18
+ if (!["--limit", "--cursor", "--status", "--project", "--search", "--attention"].includes(key)
19
+ || values.has(key) || args[i + 1] === undefined || args[i + 1].startsWith("--")) {
20
+ throw usageError("Task list expects [--all] [--status <status>] [--project <id>] [--search <text>] [--attention <category>] [--limit <1..100>] [--cursor <cursor>].");
21
+ }
22
+ values.set(key, args[++i]);
23
+ }
24
+ const status = values.get("--status");
25
+ if (status !== undefined && !TASK_STATUSES.includes(status)) {
26
+ throw usageError("Unknown Task catalog status.");
27
+ }
28
+ const attention = values.get("--attention");
29
+ if (attention !== undefined && !CATALOG_ATTENTION.includes(attention)) {
30
+ throw usageError(`Task catalog attention must be one of: ${CATALOG_ATTENTION.join(", ")}.`);
31
+ }
32
+ const limit = values.has("--limit") ? Number(values.get("--limit")) : 20;
33
+ if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_LIMIT)
34
+ throw usageError("Task catalog limit must be between 1 and 100.");
35
+ const search = values.get("--search")?.trim();
36
+ const project = values.get("--project")?.trim();
37
+ if ((search?.length ?? 0) > 256 || (project?.length ?? 0) > 256 || project === "") {
38
+ throw usageError("Task catalog filters exceed their text limit.");
39
+ }
40
+ return {
41
+ all: all || status === "archived", limit,
42
+ ...(status === undefined ? {} : { status: status }),
43
+ ...(attention === undefined ? {} : { attention: attention }),
44
+ ...(search ? { search } : {}),
45
+ ...(project === undefined ? {} : { project }),
46
+ ...(values.has("--cursor") ? { cursor: values.get("--cursor") } : {})
47
+ };
48
+ }
49
+ /** Task-local Leaders discover only their Task. Assignment Roles use their
50
+ * existing Context pack: whole-Task counts would leak unrelated assignments. */
51
+ export function taskCatalogScope(store, environment) {
52
+ const caller = resolveContextReader(store, environment);
53
+ if (caller !== undefined && caller.roleName !== "leader") {
54
+ throw usageError("Task discovery is unavailable to an Assignment-scoped Role; use its authorized Run Context.");
55
+ }
56
+ return caller?.taskId;
57
+ }
58
+ export function readTaskCatalog(store, options, environment = {}) {
59
+ return store.transaction(reader => {
60
+ const taskId = taskCatalogScope(reader, environment);
61
+ const { cursor: rawCursor, ...filters } = options;
62
+ if (!Number.isSafeInteger(filters.limit) || filters.limit < 1 || filters.limit > MAX_LIMIT) {
63
+ throw usageError("Task catalog limit must be between 1 and 100.");
64
+ }
65
+ const scope = contextContentDigest({ taskId: taskId ?? null, ...filters });
66
+ const cursor = rawCursor === undefined ? undefined : decodeCursor(rawCursor, scope);
67
+ const facts = reader.queryTaskCatalog({
68
+ ...filters, ...(taskId === undefined ? {} : { taskId }),
69
+ ...(cursor === undefined ? {} : { after: cursor.after, through: cursor.through }),
70
+ limit: options.limit + 1
71
+ });
72
+ const refs = new Map();
73
+ const getRef = (id) => {
74
+ let ref = refs.get(id);
75
+ if (ref === undefined) {
76
+ const task = reader.getTask(id);
77
+ if (task === null)
78
+ throw usageError("Task catalog changed during the read; retry.");
79
+ ref = { taskId: id, ...materialize("task", id, task).ref };
80
+ refs.set(id, ref);
81
+ }
82
+ return ref;
83
+ };
84
+ const attention = Object.fromEntries(CATALOG_ATTENTION.map(kind => {
85
+ const source = facts.attention[kind];
86
+ const selected = [];
87
+ for (const id of source.taskIds) {
88
+ const ref = getRef(id);
89
+ if (bytes([...selected, ref]) <= 2048)
90
+ selected.push(ref);
91
+ }
92
+ return [kind, {
93
+ count: source.count,
94
+ unit: kind === "executionSignals" ? "signals" : "records",
95
+ refs: selected,
96
+ // Samples identify affected Tasks, not individual records. Count and
97
+ // sample size therefore must never be subtracted from one another.
98
+ taskCount: source.taskCount,
99
+ omittedTaskRefs: source.taskCount - selected.length,
100
+ query: { all: options.all, attention: kind }
101
+ }];
102
+ }));
103
+ const response = {
104
+ view: "compact",
105
+ scope: { taskId: taskId ?? null, archived: options.all ? "included" : "excluded",
106
+ attention: "authorized-catalog-before-filters", executionSignals: "raw-inspection-candidates-not-execution-status" },
107
+ filters, total: facts.total, counts: facts.counts, attention,
108
+ tasks: [],
109
+ nextCursor: null,
110
+ consistency: "current-per-page; refresh for membership changes; new creation keys after the upper bound excluded",
111
+ limits: { pageBytes: PAGE_BYTES, summaryBytes: SUMMARY_BYTES, maxTasks: options.limit, attentionTaskRefsPerCategory: 4 }
112
+ };
113
+ const through = cursor?.through ?? facts.through;
114
+ const continuation = (after) => through === null ? null
115
+ : Buffer.from(JSON.stringify({ version: 1, scope, after, through })).toString("base64url");
116
+ for (const row of facts.rows.slice(0, options.limit)) {
117
+ const ref = getRef(row.id);
118
+ const summary = row.summary === null ? null : truncateUtf8(row.summary, SUMMARY_BYTES);
119
+ const title = truncateUtf8(row.title, 256);
120
+ // SQL already limits these fields. Compare against the selected original
121
+ // only when necessary; length-at-limit is conservatively marked omitted.
122
+ const item = {
123
+ id: row.id, title, status: row.status, createdAt: row.createdAt, updatedAt: row.updatedAt,
124
+ summary, summaryStatus: row.summaryPresent ? "available" : "missing",
125
+ omitted: { title: row.title.length >= 256 || title !== row.title,
126
+ summary: row.summary !== null && (row.summary.length >= 512 || summary !== row.summary) },
127
+ counts: { workItems: row.workItems, activeRuns: row.activeRuns },
128
+ attention: Object.fromEntries(CATALOG_ATTENTION.map(kind => [kind, row[kind]])),
129
+ ref
130
+ };
131
+ const next = continuation({ id: row.id, createdAt: row.createdAt });
132
+ if (bytes({ ok: true, data: { ...response, tasks: [...response.tasks, item], nextCursor: next } }) + 1 > PAGE_BYTES)
133
+ break;
134
+ response.tasks.push(item);
135
+ }
136
+ if (response.tasks.length < facts.rows.length) {
137
+ const last = response.tasks.at(-1);
138
+ if (last === undefined)
139
+ throw usageError("Task catalog identity/attention metadata exceeds the page byte budget.");
140
+ response.nextCursor = continuation({ id: last.id, createdAt: last.createdAt });
141
+ }
142
+ if (bytes({ ok: true, data: response }) + 1 > PAGE_BYTES)
143
+ throw usageError("Task catalog metadata exceeds the page byte budget.");
144
+ return response;
145
+ });
146
+ }
147
+ export function renderTaskCatalog(result) {
148
+ return [
149
+ `Tasks (compact): ${result.tasks.length} shown; ${result.total} matching`,
150
+ ...result.tasks.map(task => `${task.id}\t${task.status}\t${task.title}\n ${task.summary ?? "(summary missing)"}`),
151
+ `Attention across catalog: ${CATALOG_ATTENTION.map(kind => `${kind}=${result.attention[kind].count}`).join(", ")}`,
152
+ "Expand: yui task context <task-id>; inspect the Task ref for original requirements.",
153
+ ...(result.nextCursor === null ? [] : [`Next cursor: ${result.nextCursor}`])
154
+ ].join("\n") + "\n";
155
+ }
156
+ function bytes(value) { return Buffer.byteLength(JSON.stringify(value)); }
157
+ function truncateUtf8(value, max) {
158
+ let result = "";
159
+ let size = 0;
160
+ for (const point of value) {
161
+ size += Buffer.byteLength(point);
162
+ if (size > max)
163
+ break;
164
+ result += point;
165
+ }
166
+ return result;
167
+ }
168
+ function decodeCursor(raw, scope) {
169
+ try {
170
+ if (raw.length > 4096 || !/^[A-Za-z0-9_-]+$/.test(raw))
171
+ throw new Error();
172
+ const parsed = JSON.parse(Buffer.from(raw, "base64url").toString("utf8"));
173
+ const validKey = (key) => key && typeof key.id === "string" && key.id.length > 0
174
+ && typeof key.createdAt === "string" && Number.isFinite(Date.parse(key.createdAt));
175
+ if (parsed.version !== 1 || parsed.scope !== scope || !validKey(parsed.after) || !validKey(parsed.through)
176
+ || parsed.after.createdAt > parsed.through.createdAt
177
+ || (parsed.after.createdAt === parsed.through.createdAt
178
+ && Buffer.compare(Buffer.from(parsed.after.id), Buffer.from(parsed.through.id)) > 0))
179
+ throw new Error();
180
+ return parsed;
181
+ }
182
+ catch {
183
+ throw usageError("Invalid Task catalog cursor or cursor belongs to another scope/filter.");
184
+ }
185
+ }