@zq-silk/yui 0.15.9 → 0.15.12

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 (160) hide show
  1. package/ARCHITECTURE.md +8 -4
  2. package/ARCHITECTURE.zh-CN.md +5 -2
  3. package/README.md +13 -5
  4. package/dist/agent/launchEnvironment.js +7 -0
  5. package/dist/agentRun/agentRun.js +3 -0
  6. package/dist/cli/commandCatalog.js +64 -16
  7. package/dist/cli/interactionPolicy.js +7 -3
  8. package/dist/cli/managedDiagnostics.js +1 -1
  9. package/dist/cli/updateOrchestrator.js +24 -1
  10. package/dist/cli/updatePorts.js +7 -3
  11. package/dist/cli/upgradeCommand.js +42 -2
  12. package/dist/cli.js +381 -107
  13. package/dist/commands/executionAuditCommands.js +10 -0
  14. package/dist/commands/globalRoleCommands.js +339 -4
  15. package/dist/commands/projectCommands.js +50 -22
  16. package/dist/commands/releaseCommands.js +18 -0
  17. package/dist/commands/taskActor.js +25 -0
  18. package/dist/commands/taskCommands.js +586 -96
  19. package/dist/commands/taskIntegrationCommands.js +19 -39
  20. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  21. package/dist/commands/taskOverviewCommand.js +4 -3
  22. package/dist/commands/taskPublicationAdoptCommand.js +127 -0
  23. package/dist/commands/taskPublicationCommands.js +11 -2
  24. package/dist/commands/taskPublicationVerifyCommand.js +23 -39
  25. package/dist/commands/taskRemoteDeliveryCommand.js +22 -11
  26. package/dist/commands/taskRoleRuntimeStatus.js +35 -0
  27. package/dist/context/runContextPack.js +3 -0
  28. package/dist/context/taskCatalog.js +187 -0
  29. package/dist/context/taskContext.js +55 -6
  30. package/dist/controller/agentHostObservation.js +155 -0
  31. package/dist/controller/clientRuntime.js +17 -2
  32. package/dist/controller/controller.js +14 -2
  33. package/dist/controller/fileSchedulerStoreAdapter.js +519 -25
  34. package/dist/controller/globalInputDelivery.js +132 -0
  35. package/dist/controller/jobControl.js +6 -2
  36. package/dist/controller/providerRetryAdmission.js +100 -0
  37. package/dist/controller/providerRetryDelivery.js +218 -0
  38. package/dist/controller/resourceInventory.js +14 -4
  39. package/dist/controller/resourceInventoryLinux.js +2 -6
  40. package/dist/controller/runtime.js +117 -7
  41. package/dist/controller/runtimeEventInbox.js +32 -3
  42. package/dist/controller/runtimeEventProcessor.js +26 -6
  43. package/dist/controller/runtimeHookRunFence.js +75 -19
  44. package/dist/controller/structuredProviderObservation.js +133 -70
  45. package/dist/coordination/workMailboxQueue.js +5 -0
  46. package/dist/execution/workItemExecutionProjection.js +1 -1
  47. package/dist/executor/agentExecutor.js +64 -4
  48. package/dist/executor/executorRegistry.js +3 -0
  49. package/dist/executor/fileRoleLaunchPlanner.js +78 -118
  50. package/dist/integration/deliveryObligation.js +2 -1
  51. package/dist/integration/gitIntegrationService.js +329 -386
  52. package/dist/integration/integrationAttempt.js +30 -4
  53. package/dist/integration/integrationQueueService.js +7 -7
  54. package/dist/integration/integrationSourceApplication.js +323 -0
  55. package/dist/lifecycle/exactRunTerminalization.js +4 -1
  56. package/dist/message/globalInterrupt.js +33 -0
  57. package/dist/message/globalProviderRetry.js +15 -0
  58. package/dist/message/inputControlResolution.js +106 -0
  59. package/dist/message/message.js +367 -0
  60. package/dist/message/messageContinuation.js +126 -3
  61. package/dist/message/taskInterrupt.js +34 -0
  62. package/dist/observability/executionAudit.js +19 -0
  63. package/dist/observability/orchestrationMetrics.js +1 -1
  64. package/dist/release/releaseHandover.js +22 -0
  65. package/dist/release/releaseWorkflowPorts.js +15 -7
  66. package/dist/repository/gitWorkspace.js +430 -107
  67. package/dist/repository/projectMaintenanceLock.js +75 -18
  68. package/dist/repository/taskWorkspaceCoordinator.js +182 -101
  69. package/dist/repository/taskWorkspacePreparer.js +205 -72
  70. package/dist/repository/workItemCandidateSnapshot.js +34 -0
  71. package/dist/repository/workspaceCleanupInspection.js +187 -0
  72. package/dist/resources/resourceDiscovery.js +3 -2
  73. package/dist/runtime/agentError.js +5 -3
  74. package/dist/runtime/agentHost.js +179 -82
  75. package/dist/runtime/agentHostCompatibility.js +127 -0
  76. package/dist/runtime/agentHostProtocol.js +53 -0
  77. package/dist/runtime/builtinAgentErrorMappers.js +91 -0
  78. package/dist/runtime/codexAppServerRuntime.js +34 -3
  79. package/dist/runtime/executionEnvironment.js +0 -19
  80. package/dist/runtime/launchBroker.js +6 -0
  81. package/dist/runtime/providerControl.js +5 -1
  82. package/dist/runtime/providerRetry.js +198 -0
  83. package/dist/runtime/providerRuntimeIdentity.js +28 -2
  84. package/dist/runtime/sessionReconciliation.js +4 -4
  85. package/dist/runtime/sessionTokenMetrics.js +15 -5
  86. package/dist/runtime/structuredProviderHost.js +6 -2
  87. package/dist/runtime/taskRuntimeIsolation.js +30 -6
  88. package/dist/runtime/taskUsageMetrics.js +275 -0
  89. package/dist/runtime/tmuxAdapters.js +5 -3
  90. package/dist/scheduler/activeRoleRunDelivery.js +12 -0
  91. package/dist/scheduler/leaderWakeupProcessor.js +5 -0
  92. package/dist/scheduler/operatorEvent.js +4 -0
  93. package/dist/scheduler/taskExecutionProjection.js +38 -6
  94. package/dist/scheduler/taskObservabilityProjection.js +6 -44
  95. package/dist/scheduler/wakeReason.js +7 -1
  96. package/dist/scheduler/wakeupQueue.js +2 -0
  97. package/dist/setup/setupCommand.js +26 -8
  98. package/dist/storage/homeLayout.js +130 -0
  99. package/dist/storage/migrations/collapseWorktreeLayout.js +963 -0
  100. package/dist/storage/migrations/integrationContinuation.js +104 -0
  101. package/dist/storage/migrations/unifyHomeLayout.js +925 -0
  102. package/dist/storage/sqliteSchema.js +167 -4
  103. package/dist/storage/sqliteStore.js +57 -1
  104. package/dist/storage/storageVersions.js +1 -1
  105. package/dist/storage/storeRpc.js +2 -0
  106. package/dist/storage/taskCatalog.js +123 -0
  107. package/dist/storage/taskStore.js +2 -0
  108. package/dist/storage/upgrade/upgradeOrchestrator.js +95 -2
  109. package/dist/task/archiveDiagnostics.js +129 -0
  110. package/dist/task/archivePreflight.js +124 -0
  111. package/dist/task/nextAction.js +44 -11
  112. package/dist/task/publicationAdoption.js +56 -0
  113. package/dist/task/publicationReference.js +10 -0
  114. package/dist/task/remoteDelivery.js +31 -16
  115. package/dist/web/assets/client/app.js +147 -17
  116. package/dist/web/assets/client/components.js +56 -13
  117. package/dist/web/assets/client/i18n.js +78 -4
  118. package/dist/web/assets/client/taskSurface.js +108 -1
  119. package/dist/web/assets/client/view.js +39 -8
  120. package/dist/web/assets/shell.js +29 -0
  121. package/dist/web/assets/styles/layout.js +8 -1
  122. package/dist/web/assets/styles/widgets.js +12 -0
  123. package/dist/web/webServer.js +131 -4
  124. package/dist/web/webSnapshot.js +16 -6
  125. package/dist/web/webTaskSurface.js +222 -5
  126. package/dist/workspace/cleanupInspection.js +63 -0
  127. package/dist/workspace/workItemChangeSetManager.js +111 -35
  128. package/docs/agent-result-consumption.md +4 -0
  129. package/docs/agent-result-consumption.zh-CN.md +3 -0
  130. package/docs/agent-runtime-drivers.md +7 -0
  131. package/docs/agent-runtime-drivers.zh-CN.md +5 -0
  132. package/docs/architecture/README.md +2 -0
  133. package/docs/architecture/README.zh-CN.md +3 -1
  134. package/docs/architecture/capabilities-and-resources.md +30 -5
  135. package/docs/architecture/capabilities-and-resources.zh-CN.md +23 -3
  136. package/docs/managed-turn-and-session-runtime.md +47 -0
  137. package/docs/managed-turn-and-session-runtime.zh-CN.md +40 -0
  138. package/docs/observability/README.md +62 -0
  139. package/docs/observability/README.zh-CN.md +47 -0
  140. package/docs/project-refresh.md +77 -0
  141. package/docs/project-refresh.zh-CN.md +59 -0
  142. package/docs/provider-retry.md +70 -0
  143. package/docs/release-workflow.md +39 -0
  144. package/docs/release-workflow.zh-CN.md +29 -0
  145. package/docs/sqlite-control-plane-design.md +223 -1
  146. package/docs/task-delivery.md +133 -13
  147. package/docs/task-delivery.zh-CN.md +99 -10
  148. package/docs/task-discovery.md +102 -0
  149. package/docs/task-discovery.zh-CN.md +86 -0
  150. package/docs/testing/verification-levels.md +40 -0
  151. package/docs/testing/verification-levels.zh-CN.md +23 -0
  152. package/i18n/README.zh-CN.md +13 -7
  153. package/package.json +1 -1
  154. package/skills/yui-leader/references/execution.md +154 -51
  155. package/skills/yui-leader/references/integration.md +52 -2
  156. package/skills/yui-operator/SKILL.md +19 -3
  157. package/skills/yui-reviewer/SKILL.md +4 -0
  158. package/skills/yui-runtime/SKILL.md +42 -0
  159. package/skills/yui-runtime/references/publication.md +42 -0
  160. package/skills/yui-runtime/references/recovery.md +24 -0
@@ -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
 
@@ -131,6 +147,8 @@ function detailActions() {
131
147
  sendMessage: (taskId, body, requestId, intent) => submitMutation(taskId + "/messages",
132
148
  "/api/tasks/" + encodeURIComponent(taskId) + "/messages",
133
149
  { body, requestId, ...(intent === undefined ? {} : { intent }) }),
150
+ controlInput: (taskId, payload, requestId) => submitMutation(taskId + "/control/" + requestId,
151
+ "/api/tasks/" + encodeURIComponent(taskId) + "/control", payload),
134
152
  updateTask: (taskId, patch, requestId) => submitMutation(taskId + "/metadata",
135
153
  "/api/tasks/" + encodeURIComponent(taskId) + "/metadata", { patch, requestId }),
136
154
  panels: (taskId) => requestJson("/api/tasks/" + encodeURIComponent(taskId) + "/panels",
@@ -226,9 +244,16 @@ function renderCurrentDetail(force) {
226
244
  function renderDynamicContent() {
227
245
  renderFilters(elements.filters, state, i18n.t, function (filter) {
228
246
  state.filter = filter;
247
+ state.attentionFilter = null;
248
+ state.catalogAll = false;
229
249
  syncUrlFromState({ replace: false });
230
- renderDynamicContent();
250
+ resetCatalog();
231
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;
232
257
  const savedTaskScroll = elements.tasks.scrollTop;
233
258
  renderTasks(elements.tasks, state, i18n.t, i18n.getLocale(), selectTask);
234
259
  elements.tasks.scrollTop = savedTaskScroll;
@@ -461,9 +486,17 @@ async function answerInput(input, answer) {
461
486
  }
462
487
 
463
488
  let refreshing = false;
489
+ let catalogRequest = 0;
490
+ function resetCatalog() {
491
+ state.catalogCursor = null;
492
+ state.nextCursor = null;
493
+ refreshDashboard();
494
+ }
464
495
  async function refreshDashboard(options) {
465
- if (refreshing) return;
496
+ if (refreshing && options && options.quiet) return;
497
+ const request = ++catalogRequest;
466
498
  refreshing = true;
499
+ elements.catalogNext.disabled = true;
467
500
  const quiet = options && options.quiet;
468
501
  if (!quiet) {
469
502
  elements.refresh.disabled = true;
@@ -471,17 +504,29 @@ async function refreshDashboard(options) {
471
504
  }
472
505
  const previousInputs = state.counts ? state.counts.openInputs : null;
473
506
  try {
474
- const dashboard = await requestJson("/api/dashboard");
507
+ const query = new URLSearchParams({ view: "compact" });
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;
475
515
  state.tasks = dashboard.tasks;
476
- state.counts = dashboard.counts;
477
- state.attention = dashboard.attention || [];
478
- state.generatedAt = dashboard.generatedAt;
479
- if (state.selected && !state.tasks.some(function (task) { return task.id === state.selected; })) {
480
- clearSelection();
481
- } else {
482
- renderDynamicContent();
483
- }
484
- 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) {
485
530
  showToast(i18n.t("input.new"));
486
531
  }
487
532
  if (state.selected) {
@@ -490,13 +535,17 @@ async function refreshDashboard(options) {
490
535
  }
491
536
  }
492
537
  } catch {
538
+ if (request !== catalogRequest) return;
493
539
  if (!quiet) {
494
540
  renderError(elements.tasks, i18n.t("errors.dashboard"));
495
541
  showToast(i18n.t("errors.dashboard"));
496
542
  }
497
543
  } finally {
498
- refreshing = false;
499
- 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
+ }
500
549
  }
501
550
  }
502
551
 
@@ -648,20 +697,98 @@ if (elements.detailTabs) {
648
697
  });
649
698
  }
650
699
 
700
+ let catalogSearchTimer = null;
651
701
  elements.search.addEventListener("input", function () {
652
702
  state.query = elements.search.value;
653
703
  syncUrlFromState({ replace: true });
654
- const savedTaskScroll = elements.tasks.scrollTop;
655
- renderTasks(elements.tasks, state, i18n.t, i18n.getLocale(), selectTask);
656
- 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();
657
731
  });
658
732
  elements.refresh.addEventListener("click", function () { refreshDashboard(); });
659
733
  elements.operatorTerminal.addEventListener("click", function () {
660
734
  openTerminal({ scope: "global", roleName: "operator" });
661
735
  });
736
+ const globalDialog = document.querySelector("#global-input-dialog");
737
+ const globalForm = document.querySelector("#global-input-form");
738
+ const globalRole = document.querySelector("#global-input-role");
739
+ const globalAction = document.querySelector("#global-input-action");
740
+ const globalBody = document.querySelector("#global-input-body");
741
+ const globalTarget = document.querySelector("#global-input-target");
742
+ const globalThen = document.querySelector("#global-input-then");
743
+ const globalSubmit = document.querySelector("#global-input-submit");
744
+ const globalReceipt = document.querySelector("#global-input-receipt");
745
+ const globalState = document.querySelector("#global-input-state");
746
+ document.querySelector("#global-input-open").addEventListener("click", () => globalDialog.showModal());
747
+ document.querySelector("#global-input-close").addEventListener("click", () => globalDialog.close());
748
+ globalAction.addEventListener("change", () => {
749
+ document.querySelector("#global-input-body-label").hidden = globalAction.value === "interrupt";
750
+ document.querySelector("#global-input-target-label").hidden = globalAction.value === "queue";
751
+ document.querySelector("#global-input-then-label").hidden = globalAction.value !== "interrupt";
752
+ globalBody.required = globalAction.value !== "interrupt";
753
+ globalTarget.required = globalAction.value !== "queue";
754
+ });
755
+ document.querySelector("#global-input-inspect").addEventListener("click", async () => {
756
+ try {
757
+ const facts = await requestJson("/api/roles/" + encodeURIComponent(globalRole.value.trim()) + "/control");
758
+ globalState.textContent = JSON.stringify(facts, null, 2);
759
+ globalTarget.value = facts.turn?.nativeTurnId || facts.turn?.attemptId || "";
760
+ } catch (error) { globalState.textContent = error.message; }
761
+ });
762
+ globalForm.addEventListener("submit", async event => {
763
+ event.preventDefault();
764
+ if (globalSubmit.disabled) return;
765
+ globalSubmit.disabled = true;
766
+ const role = globalRole.value.trim();
767
+ const action = globalAction.value;
768
+ const requestId = crypto.randomUUID();
769
+ const input = { action, requestId };
770
+ if (action !== "interrupt") input.body = globalBody.value;
771
+ if (action !== "queue") input.expectedTarget = globalTarget.value.trim();
772
+ if (action === "interrupt" && globalThen.value.trim()) input.thenMessage = globalThen.value.trim();
773
+ globalReceipt.textContent = "Waiting for receipt · 等待回执 · " + requestId;
774
+ try {
775
+ const receipt = await submitMutation("global/" + role, "/api/roles/" + encodeURIComponent(role) + "/control", input);
776
+ const status = receipt.steer || receipt.interrupt || receipt.delivery || {};
777
+ globalReceipt.textContent = JSON.stringify(receipt);
778
+ const unknown = ["pending", "delivery-unknown", "steer-unknown", "interrupt-unknown"].includes(status.state)
779
+ || status.code === "DELIVERY_UNKNOWN";
780
+ globalSubmit.disabled = unknown;
781
+ if (!unknown && !status.code) globalBody.value = "";
782
+ } catch (error) {
783
+ globalReceipt.textContent = error.disposition === "not-submitted" ? error.message
784
+ : "Outcome unknown; read state before acting. 结果未知,请先读取状态,不要重发。 · " + requestId;
785
+ globalSubmit.disabled = error.disposition !== "not-submitted";
786
+ }
787
+ });
662
788
  elements.detailBack.addEventListener("click", clearSelection);
663
789
  elements.terminalClose.addEventListener("click", closeTerminalPanel);
664
790
  document.addEventListener("keydown", function (event) {
791
+ if (globalDialog.open) return;
665
792
  const active = document.activeElement;
666
793
  const typing = active && ["INPUT", "SELECT", "TEXTAREA"].includes(active.tagName);
667
794
  if (event.key === "Escape" && terminalPanelOpen()) {
@@ -698,6 +825,8 @@ function applyStateFromUrl() {
698
825
  }
699
826
  const query = params.get("q");
700
827
  state.query = query || "";
828
+ state.attentionFilter = VALID_ATTENTION.includes(params.get("attention")) ? params.get("attention") : null;
829
+ state.catalogAll = params.get("all") === "true";
701
830
  if (elements.search) elements.search.value = state.query;
702
831
  const taskId = params.get("task");
703
832
  if (taskId) {
@@ -712,6 +841,7 @@ function applyStateFromUrl() {
712
841
 
713
842
  window.addEventListener("popstate", function () {
714
843
  applyStateFromUrl();
844
+ resetCatalog();
715
845
  });
716
846
 
717
847
  i18n.subscribe(function () {
@@ -107,6 +107,7 @@ const EXEC_STATUS_TONE = {
107
107
  "attention": "is-danger",
108
108
  "progressing-with-attention": "is-warning",
109
109
  "blocked": "is-danger",
110
+ "conflicted": "is-warning",
110
111
  "working": "is-active",
111
112
  "completed": "is-muted",
112
113
  "retired": "is-muted",
@@ -227,17 +228,36 @@ export function workItemExecutionCard(projection, t) {
227
228
  return card;
228
229
  }
229
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
+
230
246
  export function observabilityMetricCard(observability, t) {
231
247
  if (!observability) return null;
232
248
  const card = node("div", "observability-metrics");
233
249
  const cost = observability.cost || {};
234
250
  const context = observability.context || {};
235
- card.append(metricTile(t("detail.tokens"), cost.tokens + (cost.tokensObservable === false ? "*" : "")));
236
- card.append(metricTile(t("detail.toolCalls"), cost.toolCalls + (cost.toolCallsObservable === false ? "*" : "")));
237
- 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"));
238
255
  card.append(metricTile(t("detail.ready"), (observability.dag?.readyIds || []).length, { hot: true }));
239
256
  card.append(metricTile(t("detail.contextSnapshots"), context.snapshotCount));
240
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"))));
241
261
  contextMeta.append(node("span", "", t("detail.contextBytes") + " · "
242
262
  + (context.totalBytes === null ? t("detail.partial") : context.totalBytes + " B")));
243
263
  contextMeta.append(node("span", "", t("detail.compression") + " · " + t("detail.unavailable")));
@@ -533,14 +553,9 @@ export function workItemCard(item, titles, t, locale, actions, taskId) {
533
553
  if (item.observability) {
534
554
  const observability = item.observability;
535
555
  const metrics = node("div", "record-meta work-item-observability");
536
- metrics.append(node("span", "", t("detail.cost") + " · "
537
- + (observability.cost.tokensObservable
538
- ? observability.cost.tokens + " tokens"
539
- : t("detail.unobserved"))));
540
- metrics.append(node("span", "", observability.cost.toolCallsObservable
541
- ? observability.cost.toolCalls + " tools"
542
- : t("detail.unobserved")));
543
- 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"));
544
559
  metrics.append(node("span", "", t("detail.contextSnapshots") + " · "
545
560
  + observability.context.snapshotCount));
546
561
  metrics.append(node("span", "", t("detail.results") + " · "
@@ -744,6 +759,30 @@ export function roleCard(role, task, t, locale, actions) {
744
759
  card.append(eff);
745
760
  }
746
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
+
747
786
  if (role.sessionTokens) {
748
787
  const tokenMeta = node("div", "record-meta");
749
788
  const cumulative = role.sessionTokens.cumulativeTotal || {};
@@ -932,10 +971,14 @@ export function taskCard(task, hasAttention, state, t, locale, onSelect) {
932
971
  }
933
972
 
934
973
  // One signal only: an open input (needs the user) outranks a running count.
935
- if (hasAttention) {
936
- 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));
937
976
  badge.title = t("stats.inputs");
938
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);
939
982
  } else if (task.workItems && task.workItems.running > 0) {
940
983
  const badge = node("span", "task-signal is-running", String(task.workItems.running));
941
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",
@@ -238,6 +272,7 @@ const messages = {
238
272
  "stats.running": "running",
239
273
  "status.active": "Active",
240
274
  "status.all": "All",
275
+ "filter.unarchived": "Unarchived",
241
276
  "status.archived": "Archived",
242
277
  "status.completed": "Completed",
243
278
  "status.draft": "Draft",
@@ -305,6 +340,7 @@ const messages = {
305
340
  "workExec.candidateStatus.none": "None",
306
341
  "workExec.candidateStatus.observed": "Observed",
307
342
  "workExec.candidateStatus.unknown": "Unknown",
343
+ "workExec.action.execute-directly": "Leader executes directly",
308
344
  "workExec.action.dispatch-work": "Dispatch work",
309
345
  "workExec.action.wait-for-lanes": "Wait for Lanes",
310
346
  "workExec.action.retry-or-settle-lanes": "Retry or settle exact Lanes",
@@ -344,6 +380,7 @@ const messages = {
344
380
  "exec.action.inspect-attention": "Inspect attention",
345
381
  "exec.action.recover-leader": "Recover leader",
346
382
  "exec.action.resolve-blocker": "Resolve blocker",
383
+ "exec.action.resolve-integration-conflicts": "Resolve Integration conflicts",
347
384
  "exec.action.complete-task": "Complete task",
348
385
  "exec.action.none": "—",
349
386
  "exec.attention.leader-recovery": "Leader recovery",
@@ -387,6 +424,20 @@ const messages = {
387
424
  "review.scope.task": "Task"
388
425
  },
389
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": "以下任务分组仅展示当前页;可翻页或搜索发现更多任务。",
390
441
  "a11y.skip": "跳到主面板",
391
442
  "actions.answer": "提交回答",
392
443
  "actions.back": "返回任务列表",
@@ -431,7 +482,9 @@ const messages = {
431
482
  "delivery.pending": "等待送达",
432
483
  "remoteDelivery.none": "无代码交付",
433
484
  "remoteDelivery.unavailable": "精确 head 不可用",
434
- "remoteDelivery.pending": "待合入",
485
+ "remoteDelivery.pending": "尚未交付",
486
+ "remoteDelivery.uncovered": "PR/MR 已合并,未覆盖验收",
487
+ "remoteDelivery.unverified": "已覆盖合并,尚未验证",
435
488
  "remoteDelivery.partial": "部分合入",
436
489
  "remoteDelivery.merged": "已合入",
437
490
  "detail.acceptance": "验收标准",
@@ -453,7 +506,25 @@ const messages = {
453
506
  "detail.sessionTotalTokens": "Session 累计 Token",
454
507
  "detail.maximumRequestInputTokens": "单次请求最大输入",
455
508
  "detail.toolCalls": "工具调用",
456
- "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",
457
528
  "detail.ready": "可就绪",
458
529
  "detail.contextSnapshots": "上下文快照",
459
530
  "detail.contextBytes": "上下文字节",
@@ -624,6 +695,7 @@ const messages = {
624
695
  "stats.running": "运行中",
625
696
  "status.active": "进行中",
626
697
  "status.all": "全部",
698
+ "filter.unarchived": "未归档",
627
699
  "status.archived": "已归档",
628
700
  "status.completed": "已完成",
629
701
  "status.draft": "草稿",
@@ -691,6 +763,7 @@ const messages = {
691
763
  "workExec.candidateStatus.none": "无",
692
764
  "workExec.candidateStatus.observed": "已观测",
693
765
  "workExec.candidateStatus.unknown": "未知",
766
+ "workExec.action.execute-directly": "Leader 直接执行",
694
767
  "workExec.action.dispatch-work": "派发工作",
695
768
  "workExec.action.wait-for-lanes": "等待 Lane",
696
769
  "workExec.action.retry-or-settle-lanes": "重试或结算指定 Lane",
@@ -730,6 +803,7 @@ const messages = {
730
803
  "exec.action.inspect-attention": "检查注意项",
731
804
  "exec.action.recover-leader": "恢复负责人",
732
805
  "exec.action.resolve-blocker": "解决阻塞",
806
+ "exec.action.resolve-integration-conflicts": "解决集成冲突",
733
807
  "exec.action.complete-task": "完成任务",
734
808
  "exec.action.none": "—",
735
809
  "exec.attention.leader-recovery": "负责人恢复",