@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
@@ -1,4 +1,5 @@
1
1
  import { createHash } from "node:crypto";
2
+ import { isAbsolute, relative, resolve, sep } from "node:path";
2
3
  import { requireIdentity, requirePositiveInteger, requireText } from "../domain/validation.js";
3
4
  /**
4
5
  * Issue 08: the per-Project VerificationPlan contract.
@@ -10,7 +11,7 @@ import { requireIdentity, requirePositiveInteger, requireText } from "../domain/
10
11
  * explicit `--check <shell>` path, which is marked unstructured and is not
11
12
  * reusable across stages.
12
13
  */
13
- export const VERIFICATION_PLAN_SCHEMA_VERSION = 1;
14
+ export const VERIFICATION_PLAN_SCHEMA_VERSION = 2;
14
15
  export const VERIFICATION_PLAN_KIND = "verification-plan";
15
16
  /** The reserved knowledge marker that carries a Project's VerificationPlan. */
16
17
  export const VERIFICATION_PLAN_KNOWLEDGE_KIND = VERIFICATION_PLAN_KIND;
@@ -22,25 +23,21 @@ export function normalizeVerificationPlan(raw) {
22
23
  if (record.kind !== VERIFICATION_PLAN_KIND) {
23
24
  throw new Error(`VerificationPlan kind must be "${VERIFICATION_PLAN_KIND}".`);
24
25
  }
25
- const schemaVersion = record.schemaVersion ?? VERIFICATION_PLAN_SCHEMA_VERSION;
26
+ const schemaVersion = record.schemaVersion;
26
27
  if (schemaVersion !== VERIFICATION_PLAN_SCHEMA_VERSION) {
27
28
  throw new Error(`VerificationPlan schemaVersion must be ${VERIFICATION_PLAN_SCHEMA_VERSION}.`);
28
29
  }
29
- const mode = record.mode ?? "record";
30
- if (mode !== "record" && mode !== "reuse" && mode !== "enforce") {
31
- throw new Error(`VerificationPlan mode is invalid: ${String(mode)}.`);
32
- }
30
+ if (Object.hasOwn(record, "mode"))
31
+ throw new Error("VerificationPlan mode is retired; request an explicit rerun on the operation.");
32
+ if (Object.hasOwn(record, "l1"))
33
+ throw new Error("VerificationPlan l1 is retired; declare current checks in l2.");
33
34
  const plan = {
34
35
  schemaVersion: VERIFICATION_PLAN_SCHEMA_VERSION,
35
36
  kind: VERIFICATION_PLAN_KIND,
36
37
  id: requireIdentity(record.id, "VerificationPlan id"),
37
38
  version: requireText(record.version, "VerificationPlan version"),
38
- mode,
39
39
  toolchain: normalizeToolchain(record.toolchain),
40
40
  bootstrap: normalizeSteps(record.bootstrap, "bootstrap"),
41
- l1: {
42
- categories: normalizeL1Categories(record.l1)
43
- },
44
41
  l2: {
45
42
  steps: normalizeSteps(record.l2?.steps, "l2")
46
43
  },
@@ -126,34 +123,6 @@ function normalizeStepEnv(raw, label) {
126
123
  }
127
124
  return Object.freeze(env);
128
125
  }
129
- function normalizeL1Categories(raw) {
130
- if (raw === undefined)
131
- return Object.freeze([]);
132
- if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
133
- throw new Error("VerificationPlan l1 must be an object.");
134
- }
135
- const categoriesRaw = raw.categories;
136
- if (!Array.isArray(categoriesRaw)) {
137
- throw new Error("VerificationPlan l1 categories must be an array.");
138
- }
139
- const ids = new Set();
140
- return Object.freeze(categoriesRaw.map((entry) => {
141
- if (typeof entry !== "object" || entry === null || Array.isArray(entry)) {
142
- throw new Error("VerificationPlan l1 category must be an object.");
143
- }
144
- const record = entry;
145
- const id = requireIdentity(record.id, "VerificationPlan l1 category id");
146
- if (ids.has(id)) {
147
- throw new Error(`VerificationPlan l1 category ids must be unique: ${id}.`);
148
- }
149
- ids.add(id);
150
- return Object.freeze({
151
- id,
152
- paths: normalizedTextList(record.paths, `VerificationPlan l1 category ${id} paths`),
153
- checks: normalizeSteps(record.checks, `l1 ${id}`)
154
- });
155
- }));
156
- }
157
126
  function normalizedTextList(raw, label) {
158
127
  if (!Array.isArray(raw)) {
159
128
  throw new Error(`${label} must be an array.`);
@@ -169,16 +138,18 @@ function normalizedTextList(raw, label) {
169
138
  }
170
139
  /**
171
140
  * The stable digest of the gate contract. It deliberately excludes the
172
- * rollout `mode`, retention window, and documentation fields: changing the
173
- * rollout switch must not invalidate proven gate evidence.
141
+ * retention window and documentation fields. Execution semantics belong to
142
+ * the identity so evidence from a prior contract is never silently reused.
174
143
  */
175
144
  export function verificationPlanDigest(plan) {
176
145
  const canonical = canonicalJson({
146
+ // Old artifacts may have skipped shell commands or run in the wrong cwd.
147
+ // Keep that history, but never reuse it as proof under corrected semantics.
148
+ executionContract: "workspace-argv-or-shell/clean-candidate/l2-only/v5",
177
149
  id: plan.id,
178
150
  version: plan.version,
179
151
  toolchain: plan.toolchain,
180
152
  bootstrap: plan.bootstrap,
181
- l1: plan.l1,
182
153
  l2: plan.l2,
183
154
  ...(plan.l3 === undefined ? {} : { l3: plan.l3 })
184
155
  });
@@ -208,46 +179,30 @@ export function toolchainDigest(plan, toolchain) {
208
179
  });
209
180
  return createHash("sha256").update(canonical).digest("hex");
210
181
  }
211
- /**
212
- * Select the L1 checks for a change: every category whose path prefixes match
213
- * a changed path. A category with no path prefixes matches nothing (it must
214
- * be selected explicitly by the caller).
215
- */
216
- export function selectL1Checks(plan, changedPaths) {
217
- const selected = new Map();
218
- for (const category of plan.l1.categories) {
219
- if (category.paths.length === 0)
220
- continue;
221
- const matches = changedPaths.some((path) => category.paths.some((prefix) => path === prefix || path.startsWith(`${prefix}/`)));
222
- if (!matches)
223
- continue;
224
- for (const check of category.checks) {
225
- if (!selected.has(check.name))
226
- selected.set(check.name, check);
227
- }
228
- }
229
- return Object.freeze([...selected.values()]);
230
- }
231
182
  /** Bootstrap steps as DurableJob steps, named `bootstrap-N`. */
232
- export function planBootstrapJobSteps(plan) {
233
- return plan.bootstrap.map((step, index) => toDurableJobStep(step, `bootstrap-${index + 1}`));
183
+ export function planBootstrapJobSteps(plan, workspace) {
184
+ return plan.bootstrap.map((step, index) => toDurableJobStep(step, `bootstrap-${index + 1}`, workspace));
234
185
  }
235
186
  /** L2 gate steps as DurableJob steps, named `gate-N`. */
236
- export function planL2JobSteps(plan) {
237
- return plan.l2.steps.map((step, index) => toDurableJobStep(step, `gate-${index + 1}`));
187
+ export function planL2JobSteps(plan, workspace) {
188
+ return plan.l2.steps.map((step, index) => toDurableJobStep(step, `gate-${index + 1}`, workspace));
238
189
  }
239
- /** L1 steps as DurableJob steps, named `l1-N`. */
240
- export function planL1JobSteps(steps) {
241
- return steps.map((step, index) => toDurableJobStep(step, `l1-${index + 1}`));
242
- }
243
- function toDurableJobStep(step, name) {
190
+ function toDurableJobStep(step, name, workspace) {
191
+ if (!isAbsolute(workspace))
192
+ throw new Error("Verification workspace must be absolute.");
193
+ const cwd = step.cwd === undefined ? undefined : resolve(workspace, step.cwd);
194
+ const nested = cwd === undefined ? "" : relative(workspace, cwd);
195
+ if (step.cwd !== undefined && (isAbsolute(step.cwd) || nested === ".."
196
+ || nested.startsWith(`..${sep}`) || isAbsolute(nested))) {
197
+ throw new Error(`Verification step cwd is outside its workspace: ${step.name}.`);
198
+ }
244
199
  return {
245
200
  name,
246
201
  // The shell-equivalent command stays as the human-readable fallback and
247
202
  // the coverage-matching string; argv is the executable form.
248
203
  command: step.shell === true ? step.argv.join(" ") : shellQuote(step.argv),
249
- argv: step.argv,
250
- ...(step.cwd === undefined ? {} : { cwd: step.cwd }),
204
+ ...(step.shell === true ? {} : { argv: step.argv }),
205
+ ...(cwd === undefined ? {} : { cwd }),
251
206
  ...(step.env === undefined ? {} : { env: step.env })
252
207
  };
253
208
  }
@@ -20,6 +20,10 @@ const elements = {
20
20
  search: document.querySelector("#search"),
21
21
  filters: document.querySelector("#status-filters"),
22
22
  tasks: document.querySelector("#task-list"),
23
+ catalogNext: document.querySelector("#catalog-next"),
24
+ catalogReset: document.querySelector("#catalog-reset"),
25
+ catalogCount: document.querySelector("#catalog-count"),
26
+ catalogAttentionReset: document.querySelector("#catalog-attention-reset"),
23
27
  detail: document.querySelector("#detail"),
24
28
  mainCol: document.querySelector(".main-col"),
25
29
  topbar: document.querySelector(".topbar"),
@@ -40,6 +44,13 @@ const state = {
40
44
  tasks: [],
41
45
  counts: null,
42
46
  attention: [],
47
+ catalogAttention: null,
48
+ catalogScope: null,
49
+ catalogAll: false,
50
+ catalogTotal: 0,
51
+ catalogCursor: null,
52
+ nextCursor: null,
53
+ attentionFilter: null,
43
54
  generatedAt: null,
44
55
  filter: "all",
45
56
  query: "",
@@ -48,6 +59,7 @@ const state = {
48
59
  detailKey: null
49
60
  };
50
61
  const VALID_FILTERS = ["all", "active", "draft", "completed", "cancelled", "archived"];
62
+ const VALID_ATTENTION = ["openInputs", "pendingOperations", "unknownOperations", "executionSignals"];
51
63
  let terminalSession = null;
52
64
  let terminalStateKey = "terminal.closed";
53
65
  const submittedRequests = new Set();
@@ -120,6 +132,10 @@ function syncUrlFromState(options) {
120
132
  else params.delete("filter");
121
133
  if (state.query) params.set("q", state.query);
122
134
  else params.delete("q");
135
+ if (state.attentionFilter) params.set("attention", state.attentionFilter);
136
+ else params.delete("attention");
137
+ if (state.catalogAll) params.set("all", "true");
138
+ else params.delete("all");
123
139
  applyUrl(params, options);
124
140
  }
125
141
 
@@ -228,9 +244,16 @@ function renderCurrentDetail(force) {
228
244
  function renderDynamicContent() {
229
245
  renderFilters(elements.filters, state, i18n.t, function (filter) {
230
246
  state.filter = filter;
247
+ state.attentionFilter = null;
248
+ state.catalogAll = false;
231
249
  syncUrlFromState({ replace: false });
232
- renderDynamicContent();
250
+ resetCatalog();
233
251
  });
252
+ elements.catalogNext.disabled = !state.nextCursor || refreshing;
253
+ elements.catalogCount.textContent = i18n.t("catalog.count")
254
+ .replace("{shown}", String(state.tasks.length)).replace("{total}", String(state.catalogTotal))
255
+ + (state.attentionFilter ? " · " + i18n.t("catalog." + state.attentionFilter) : "");
256
+ elements.catalogAttentionReset.hidden = !state.attentionFilter;
234
257
  const savedTaskScroll = elements.tasks.scrollTop;
235
258
  renderTasks(elements.tasks, state, i18n.t, i18n.getLocale(), selectTask);
236
259
  elements.tasks.scrollTop = savedTaskScroll;
@@ -463,9 +486,17 @@ async function answerInput(input, answer) {
463
486
  }
464
487
 
465
488
  let refreshing = false;
489
+ let catalogRequest = 0;
490
+ function resetCatalog() {
491
+ state.catalogCursor = null;
492
+ state.nextCursor = null;
493
+ refreshDashboard();
494
+ }
466
495
  async function refreshDashboard(options) {
467
- if (refreshing) return;
496
+ if (refreshing && options && options.quiet) return;
497
+ const request = ++catalogRequest;
468
498
  refreshing = true;
499
+ elements.catalogNext.disabled = true;
469
500
  const quiet = options && options.quiet;
470
501
  if (!quiet) {
471
502
  elements.refresh.disabled = true;
@@ -473,17 +504,29 @@ async function refreshDashboard(options) {
473
504
  }
474
505
  const previousInputs = state.counts ? state.counts.openInputs : null;
475
506
  try {
476
- const dashboard = await requestJson("/api/dashboard");
507
+ const query = new URLSearchParams();
508
+ if (state.catalogAll) query.set("all", "true");
509
+ if (state.filter !== "all") query.set("status", state.filter);
510
+ if (state.query.trim()) query.set("search", state.query.trim());
511
+ if (state.attentionFilter) query.set("attention", state.attentionFilter);
512
+ if (state.catalogCursor) query.set("cursor", state.catalogCursor);
513
+ const dashboard = await requestJson("/api/dashboard?" + query.toString());
514
+ if (request !== catalogRequest) return;
477
515
  state.tasks = dashboard.tasks;
478
- state.counts = dashboard.counts;
479
- state.attention = dashboard.attention || [];
480
- state.generatedAt = dashboard.generatedAt;
481
- if (state.selected && !state.tasks.some(function (task) { return task.id === state.selected; })) {
482
- clearSelection();
483
- } else {
484
- renderDynamicContent();
485
- }
486
- if (previousInputs !== null && dashboard.counts.openInputs > previousInputs) {
516
+ state.counts = { ...dashboard.counts, openInputs: dashboard.attention.openInputs.count };
517
+ state.catalogAttention = dashboard.attention;
518
+ state.catalogScope = dashboard.scope;
519
+ state.catalogTotal = dashboard.total;
520
+ state.nextCursor = dashboard.nextCursor;
521
+ state.attention = dashboard.attention.openInputs.refs.map(function (ref) {
522
+ const task = state.tasks.find(function (entry) { return entry.id === ref.taskId; });
523
+ return { taskId: ref.taskId, taskTitle: task ? task.title : ref.taskId };
524
+ });
525
+ state.generatedAt = new Date().toISOString();
526
+ // A selected Task may live on another page or outside current filters.
527
+ // Only its detail endpoint can establish that it no longer exists.
528
+ renderDynamicContent();
529
+ if (previousInputs !== null && state.counts.openInputs > previousInputs) {
487
530
  showToast(i18n.t("input.new"));
488
531
  }
489
532
  if (state.selected) {
@@ -492,13 +535,17 @@ async function refreshDashboard(options) {
492
535
  }
493
536
  }
494
537
  } catch {
538
+ if (request !== catalogRequest) return;
495
539
  if (!quiet) {
496
540
  renderError(elements.tasks, i18n.t("errors.dashboard"));
497
541
  showToast(i18n.t("errors.dashboard"));
498
542
  }
499
543
  } finally {
500
- refreshing = false;
501
- if (!quiet) elements.refresh.disabled = false;
544
+ if (request === catalogRequest) {
545
+ refreshing = false;
546
+ elements.refresh.disabled = false;
547
+ elements.catalogNext.disabled = !state.nextCursor;
548
+ }
502
549
  }
503
550
  }
504
551
 
@@ -650,12 +697,37 @@ if (elements.detailTabs) {
650
697
  });
651
698
  }
652
699
 
700
+ let catalogSearchTimer = null;
653
701
  elements.search.addEventListener("input", function () {
654
702
  state.query = elements.search.value;
655
703
  syncUrlFromState({ replace: true });
656
- const savedTaskScroll = elements.tasks.scrollTop;
657
- renderTasks(elements.tasks, state, i18n.t, i18n.getLocale(), selectTask);
658
- elements.tasks.scrollTop = savedTaskScroll;
704
+ window.clearTimeout(catalogSearchTimer);
705
+ catalogSearchTimer = window.setTimeout(resetCatalog, 200);
706
+ });
707
+ elements.catalogNext.addEventListener("click", function () {
708
+ if (!state.nextCursor) return;
709
+ state.catalogCursor = state.nextCursor;
710
+ refreshDashboard();
711
+ });
712
+ elements.catalogReset.addEventListener("click", function () {
713
+ resetCatalog();
714
+ });
715
+ elements.catalogAttentionReset.addEventListener("click", function () {
716
+ state.attentionFilter = null;
717
+ state.catalogAll = false;
718
+ syncUrlFromState({ replace: false });
719
+ resetCatalog();
720
+ });
721
+ elements.detail.addEventListener("click", function (event) {
722
+ const button = event.target.closest("[data-catalog-attention]");
723
+ if (!button) return;
724
+ state.attentionFilter = button.dataset.catalogAttention;
725
+ state.catalogAll = state.catalogScope && state.catalogScope.archived === "included";
726
+ state.filter = "all";
727
+ state.query = "";
728
+ elements.search.value = "";
729
+ syncUrlFromState({ replace: false });
730
+ resetCatalog();
659
731
  });
660
732
  elements.refresh.addEventListener("click", function () { refreshDashboard(); });
661
733
  elements.operatorTerminal.addEventListener("click", function () {
@@ -753,6 +825,8 @@ function applyStateFromUrl() {
753
825
  }
754
826
  const query = params.get("q");
755
827
  state.query = query || "";
828
+ state.attentionFilter = VALID_ATTENTION.includes(params.get("attention")) ? params.get("attention") : null;
829
+ state.catalogAll = params.get("all") === "true";
756
830
  if (elements.search) elements.search.value = state.query;
757
831
  const taskId = params.get("task");
758
832
  if (taskId) {
@@ -767,6 +841,7 @@ function applyStateFromUrl() {
767
841
 
768
842
  window.addEventListener("popstate", function () {
769
843
  applyStateFromUrl();
844
+ resetCatalog();
770
845
  });
771
846
 
772
847
  i18n.subscribe(function () {
@@ -228,17 +228,36 @@ export function workItemExecutionCard(projection, t) {
228
228
  return card;
229
229
  }
230
230
 
231
+ export function usageMetricText(metric, t, suffix) {
232
+ if (!metric || metric.value == null) return t("detail.unobserved");
233
+ return metric.value + (suffix || "") + (metric.status === "partial" ? " · " + t("detail.partial") : "");
234
+ }
235
+
236
+ function usageTile(label, metric, t, suffix) {
237
+ const tile = metricTile(label, usageMetricText(metric, t, suffix));
238
+ if (metric?.reasons?.length) {
239
+ tile.append(node("small", "muted", metric.reasons.map(function (reason) {
240
+ return t("usage.reason." + reason);
241
+ }).join("; ")));
242
+ }
243
+ return tile;
244
+ }
245
+
231
246
  export function observabilityMetricCard(observability, t) {
232
247
  if (!observability) return null;
233
248
  const card = node("div", "observability-metrics");
234
249
  const cost = observability.cost || {};
235
250
  const context = observability.context || {};
236
- card.append(metricTile(t("detail.tokens"), cost.tokens + (cost.tokensObservable === false ? "*" : "")));
237
- card.append(metricTile(t("detail.toolCalls"), cost.toolCalls + (cost.toolCallsObservable === false ? "*" : "")));
238
- card.append(metricTile(t("detail.wallClock"), cost.wallClockSeconds + "s"));
251
+ card.append(usageTile(t("detail.tokens"), cost.tokens, t));
252
+ card.append(usageTile(t("detail.toolCalls"), cost.toolCalls, t));
253
+ card.append(usageTile(t("detail.elapsed"), cost.elapsedSeconds, t, "s"));
254
+ card.append(usageTile(t("detail.executionSum"), cost.executionSeconds, t, "s"));
239
255
  card.append(metricTile(t("detail.ready"), (observability.dag?.readyIds || []).length, { hot: true }));
240
256
  card.append(metricTile(t("detail.contextSnapshots"), context.snapshotCount));
241
257
  const contextMeta = node("div", "record-meta observability-context-meta");
258
+ contextMeta.append(node("span", "", t("detail.usageScope")));
259
+ contextMeta.append(node("span", "", t("detail.observedThrough") + " · "
260
+ + (cost.observedThrough ? formatDateTime(cost.observedThrough) : t("detail.unobserved"))));
242
261
  contextMeta.append(node("span", "", t("detail.contextBytes") + " · "
243
262
  + (context.totalBytes === null ? t("detail.partial") : context.totalBytes + " B")));
244
263
  contextMeta.append(node("span", "", t("detail.compression") + " · " + t("detail.unavailable")));
@@ -534,14 +553,9 @@ export function workItemCard(item, titles, t, locale, actions, taskId) {
534
553
  if (item.observability) {
535
554
  const observability = item.observability;
536
555
  const metrics = node("div", "record-meta work-item-observability");
537
- metrics.append(node("span", "", t("detail.cost") + " · "
538
- + (observability.cost.tokensObservable
539
- ? observability.cost.tokens + " tokens"
540
- : t("detail.unobserved"))));
541
- metrics.append(node("span", "", observability.cost.toolCallsObservable
542
- ? observability.cost.toolCalls + " tools"
543
- : t("detail.unobserved")));
544
- metrics.append(node("span", "", observability.cost.wallClockSeconds + "s"));
556
+ metrics.append(usageTile(t("detail.tokens"), observability.cost.tokens, t));
557
+ metrics.append(usageTile(t("detail.toolCalls"), observability.cost.toolCalls, t));
558
+ metrics.append(usageTile(t("detail.executionSum"), observability.cost.executionSeconds, t, "s"));
545
559
  metrics.append(node("span", "", t("detail.contextSnapshots") + " · "
546
560
  + observability.context.snapshotCount));
547
561
  metrics.append(node("span", "", t("detail.results") + " · "
@@ -745,6 +759,30 @@ export function roleCard(role, task, t, locale, actions) {
745
759
  card.append(eff);
746
760
  }
747
761
 
762
+ if (role.providerRetry) {
763
+ const retry = role.providerRetry;
764
+ const retryMeta = node("p", "record-meta");
765
+ const zh = locale.startsWith("zh");
766
+ const labels = zh ? {
767
+ waiting: "等待 Provider 重试", "in-flight": "Provider 重试已提交",
768
+ recovered: "Provider 已恢复", cancelled: "自动重试已取消",
769
+ exhausted: "自动重试预算耗尽", "needs-attention": "需要检查 Provider",
770
+ "delivery-unknown": "投递结果未知,禁止重放"
771
+ } : {
772
+ waiting: "Waiting for Provider retry", "in-flight": "Provider retry submitted",
773
+ recovered: "Provider recovered", cancelled: "Automatic retry cancelled",
774
+ exhausted: "Automatic retry budget exhausted", "needs-attention": "Provider needs attention",
775
+ "delivery-unknown": "Delivery unknown; no replay"
776
+ };
777
+ retryMeta.textContent = (labels[retry.status] || retry.status) + " · "
778
+ + (zh && retry.category === "rate-limit" ? "临时限流" : retry.category) + " · "
779
+ + retry.attempts + "/" + retry.limit
780
+ + (retry.status === "waiting" ? " · " + new Date(retry.nextEligibleAt).toLocaleString(locale) : "")
781
+ + " · " + (zh ? "原成果保留" : "Existing work preserved")
782
+ + (retry.reason ? " · " + retry.reason : "");
783
+ card.append(retryMeta);
784
+ }
785
+
748
786
  if (role.sessionTokens) {
749
787
  const tokenMeta = node("div", "record-meta");
750
788
  const cumulative = role.sessionTokens.cumulativeTotal || {};
@@ -933,10 +971,14 @@ export function taskCard(task, hasAttention, state, t, locale, onSelect) {
933
971
  }
934
972
 
935
973
  // One signal only: an open input (needs the user) outranks a running count.
936
- if (hasAttention) {
937
- const badge = node("span", "task-signal is-input", String(task.openInputCount || 1));
974
+ if (hasAttention || (task.attention && task.attention.openInputs > 0)) {
975
+ const badge = node("span", "task-signal is-input", String(task.attention ? task.attention.openInputs : task.openInputCount || 1));
938
976
  badge.title = t("stats.inputs");
939
977
  button.append(badge);
978
+ } else if (task.attention && task.attention.executionSignals > 0) {
979
+ const badge = node("span", "task-signal", String(task.attention.executionSignals));
980
+ badge.title = t("catalog.executionSignals");
981
+ button.append(badge);
940
982
  } else if (task.workItems && task.workItems.running > 0) {
941
983
  const badge = node("span", "task-signal is-running", String(task.workItems.running));
942
984
  badge.title = t("stats.running");
@@ -1,6 +1,20 @@
1
1
  export const I18N_SCRIPT = `
2
2
  const messages = {
3
3
  "en": {
4
+ "catalog.first": "First page",
5
+ "catalog.all": "All tasks",
6
+ "catalog.clearAttention": "Clear attention filter",
7
+ "catalog.next": "Next page",
8
+ "catalog.count": "{shown} shown / {total} matching",
9
+ "catalog.attention": "Across the catalog",
10
+ "catalog.tasks": "tasks",
11
+ "catalog.openInputs": "Open inputs",
12
+ "catalog.inspectInputs": "Open the Open inputs category to inspect all affected tasks.",
13
+ "catalog.pendingOperations": "Pending operations",
14
+ "catalog.unknownOperations": "Unknown operations",
15
+ "catalog.executionSignals": "Execution facts to inspect",
16
+ "catalog.signalsHelp": "Counts cover the catalog, not just this page. Execution facts include live runs, open work and recovery signals; they do not imply failure. Open a task for its precise execution state.",
17
+ "catalog.pageScope": "Task groups below show this page only. Use Next page or search to discover more.",
4
18
  "a11y.skip": "Skip to the main panel",
5
19
  "actions.answer": "Answer",
6
20
  "actions.back": "Back to task list",
@@ -45,7 +59,9 @@ const messages = {
45
59
  "delivery.pending": "Awaiting delivery",
46
60
  "remoteDelivery.none": "No code delivery",
47
61
  "remoteDelivery.unavailable": "Exact heads unavailable",
48
- "remoteDelivery.pending": "Pending merge",
62
+ "remoteDelivery.pending": "Not delivered",
63
+ "remoteDelivery.uncovered": "Merged PR/MR, acceptance not covered",
64
+ "remoteDelivery.unverified": "Covering merge, not verified",
49
65
  "remoteDelivery.partial": "Partially merged",
50
66
  "remoteDelivery.merged": "Merged",
51
67
  "detail.acceptance": "Acceptance criteria",
@@ -67,7 +83,25 @@ const messages = {
67
83
  "detail.sessionTotalTokens": "Session total tokens",
68
84
  "detail.maximumRequestInputTokens": "Maximum request input",
69
85
  "detail.toolCalls": "Tool calls",
70
- "detail.wallClock": "Wall clock",
86
+ "detail.elapsed": "Task elapsed (including planning/waiting)",
87
+ "detail.executionSum": "Observed native execution sum",
88
+ "detail.usageScope": "Task lifetime · observed sources only · not billing totals",
89
+ "detail.observedThrough": "Evidence through",
90
+ "usage.reason.child-usage-overlap-unproven": "Child usage may overlap parent",
91
+ "usage.reason.source-coverage-partial": "Provider source coverage is partial",
92
+ "usage.reason.native-session-role-overlap": "Multiple Roles share the same native counter",
93
+ "usage.reason.session-usage-unavailable": "Session usage is missing or not safely countable",
94
+ "usage.reason.cumulative-not-allocatable-to-work-item": "Session cumulative usage cannot be allocated to this WorkItem",
95
+ "usage.reason.request-run-attribution-conflict": "Request Run ownership is ambiguous",
96
+ "usage.reason.no-attributable-request-usage": "No exactly attributable request usage",
97
+ "usage.reason.session-identity-missing": "Exact Session identity is missing",
98
+ "usage.reason.run-usage-unobserved": "Some Run usage is unobserved",
99
+ "usage.reason.counter-overflow": "Counter exceeds safe numeric range",
100
+ "usage.reason.no-token-evidence": "No token evidence",
101
+ "usage.reason.tool-history-compacted": "Tool history is compacted; retained calls only",
102
+ "usage.reason.nonzero-session-baseline": "Initial nonzero Session counter excluded",
103
+ "usage.reason.task-lifecycle-time-missing": "Task start/end evidence is missing",
104
+ "usage.reason.native-turn-history-compacted": "Complete retained Turns only; live/incomplete Turns excluded",
71
105
  "detail.ready": "Ready",
72
106
  "detail.contextSnapshots": "Context snapshots",
73
107
  "detail.contextBytes": "Context bytes",
@@ -390,6 +424,20 @@ const messages = {
390
424
  "review.scope.task": "Task"
391
425
  },
392
426
  "zh-CN": {
427
+ "catalog.first": "回到首页",
428
+ "catalog.all": "全部任务",
429
+ "catalog.clearAttention": "清除关注分类筛选",
430
+ "catalog.next": "下一页",
431
+ "catalog.count": "显示 {shown} 项 / 匹配 {total} 项",
432
+ "catalog.attention": "整个目录的关注事实",
433
+ "catalog.tasks": "个任务",
434
+ "catalog.openInputs": "未解决输入",
435
+ "catalog.inspectInputs": "打开“未解决输入”分类,查看全部相关任务。",
436
+ "catalog.pendingOperations": "进行中的操作",
437
+ "catalog.unknownOperations": "结果未知的操作",
438
+ "catalog.executionSignals": "可检查的执行事实",
439
+ "catalog.signalsHelp": "计数覆盖整个目录,不限当前页。执行事实包含活跃运行、未完成工作及恢复信号,不表示执行失败;打开任务查看精确执行状态。",
440
+ "catalog.pageScope": "以下任务分组仅展示当前页;可翻页或搜索发现更多任务。",
393
441
  "a11y.skip": "跳到主面板",
394
442
  "actions.answer": "提交回答",
395
443
  "actions.back": "返回任务列表",
@@ -434,7 +482,9 @@ const messages = {
434
482
  "delivery.pending": "等待送达",
435
483
  "remoteDelivery.none": "无代码交付",
436
484
  "remoteDelivery.unavailable": "精确 head 不可用",
437
- "remoteDelivery.pending": "待合入",
485
+ "remoteDelivery.pending": "尚未交付",
486
+ "remoteDelivery.uncovered": "PR/MR 已合并,未覆盖验收",
487
+ "remoteDelivery.unverified": "已覆盖合并,尚未验证",
438
488
  "remoteDelivery.partial": "部分合入",
439
489
  "remoteDelivery.merged": "已合入",
440
490
  "detail.acceptance": "验收标准",
@@ -456,7 +506,25 @@ const messages = {
456
506
  "detail.sessionTotalTokens": "Session 累计 Token",
457
507
  "detail.maximumRequestInputTokens": "单次请求最大输入",
458
508
  "detail.toolCalls": "工具调用",
459
- "detail.wallClock": "墙钟时间",
509
+ "detail.elapsed": "任务历时(含规划和等待)",
510
+ "detail.executionSum": "已观测原生执行累计",
511
+ "detail.usageScope": "Task 全生命周期 · 仅已观测来源 · 非账单总量",
512
+ "detail.observedThrough": "证据截止",
513
+ "usage.reason.child-usage-overlap-unproven": "子执行用量可能与父统计重叠",
514
+ "usage.reason.source-coverage-partial": "Provider 来源覆盖不完整",
515
+ "usage.reason.native-session-role-overlap": "多个 Role 共享同一原生计数器",
516
+ "usage.reason.session-usage-unavailable": "Session 用量缺失或无法安全计数",
517
+ "usage.reason.cumulative-not-allocatable-to-work-item": "Session 累计用量不能分配给此 WorkItem",
518
+ "usage.reason.request-run-attribution-conflict": "请求的 Run 归属不明确",
519
+ "usage.reason.no-attributable-request-usage": "没有精确归属的请求用量",
520
+ "usage.reason.session-identity-missing": "缺少精确 Session 身份",
521
+ "usage.reason.run-usage-unobserved": "部分 Run 没有用量证据",
522
+ "usage.reason.counter-overflow": "计数超过安全数值范围",
523
+ "usage.reason.no-token-evidence": "没有 token 证据",
524
+ "usage.reason.tool-history-compacted": "工具历史已压缩,仅计保留调用",
525
+ "usage.reason.nonzero-session-baseline": "已排除 Session 首次非零累计基线",
526
+ "usage.reason.task-lifecycle-time-missing": "缺少 Task 起止证据",
527
+ "usage.reason.native-turn-history-compacted": "仅计起止完整的保留 Turn,不含运行中或证据缺失的 Turn",
460
528
  "detail.ready": "可就绪",
461
529
  "detail.contextSnapshots": "上下文快照",
462
530
  "detail.contextBytes": "上下文字节",
@@ -383,7 +383,8 @@ export function renderTaskSurface(container, data, t, locale, actions) {
383
383
  const active = values("run").find((run) => run.roleName === role.name && run.status === "active");
384
384
  const incomplete = core.omitted.records > 0 || records("run").some((entry) => entry.omitted);
385
385
  roles.append(roleCard({
386
- ...role, status: active || incomplete ? "unknown" : "idle",
386
+ ...role, providerRetry: entry.providerRetry,
387
+ status: entry.providerRetry?.status === "waiting" ? "waiting" : active || incomplete ? "unknown" : "idle",
387
388
  effectiveLaunch: active ? active.effective : null,
388
389
  launchDrift: active && active.effective.sourceDesiredRevision !== role.launchRevision
389
390
  }, task, t, locale, actions));
@@ -526,11 +527,11 @@ function renderSubmissionFacets(container, submission, say) {
526
527
  const step = submission.nextStep;
527
528
  if (step) {
528
529
  const nextText = step.kind === "activate-manually"
529
- ? say("Next: activate this Task explicitly (yui task activate " + step.taskId + ").",
530
- "下一步:显式激活该 Task(yui task activate " + step.taskId + ")。")
530
+ ? say("Next: request activation with an explicit environment (yui task activation request " + step.taskId + " --request-id <id> --environment <plan>).",
531
+ "下一步:明确环境并请求激活(yui task activation request " + step.taskId + " --request-id <id> --environment <plan>)。")
531
532
  : step.kind === "start-execution"
532
- ? say("Next: start execution first, then activate (" + step.taskId + ").",
533
- "下一步:先启动执行,再激活(" + step.taskId + ")。")
533
+ ? say("Next: start execution first, then request activation (" + step.taskId + ").",
534
+ "下一步:先启动执行,再请求激活(" + step.taskId + ")。")
534
535
  : step.kind === "await-pending-activation"
535
536
  ? say("Next: waiting on the pending activation " + step.activationRef + ".",
536
537
  "下一步:等待待处理的激活 " + step.activationRef + "。")