@zq-silk/yui 0.15.11 → 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.
- package/ARCHITECTURE.md +8 -4
- package/ARCHITECTURE.zh-CN.md +5 -2
- package/README.md +13 -5
- package/dist/agentRun/agentRun.js +3 -0
- package/dist/cli/commandCatalog.js +38 -10
- package/dist/cli/interactionPolicy.js +4 -0
- package/dist/cli/managedDiagnostics.js +1 -1
- package/dist/cli.js +46 -15
- package/dist/commands/executionAuditCommands.js +10 -0
- package/dist/commands/globalRoleCommands.js +27 -2
- package/dist/commands/projectCommands.js +44 -15
- package/dist/commands/taskCommands.js +95 -33
- package/dist/commands/taskIntegrationCommands.js +3 -1
- package/dist/commands/taskOverviewCommand.js +4 -3
- package/dist/commands/taskPublicationAdoptCommand.js +127 -0
- package/dist/commands/taskPublicationCommands.js +11 -2
- package/dist/commands/taskPublicationVerifyCommand.js +23 -39
- package/dist/commands/taskRemoteDeliveryCommand.js +18 -7
- package/dist/context/runContextPack.js +3 -0
- package/dist/context/taskCatalog.js +187 -0
- package/dist/context/taskContext.js +17 -4
- package/dist/controller/controller.js +11 -2
- package/dist/controller/fileSchedulerStoreAdapter.js +80 -19
- package/dist/controller/globalInputDelivery.js +13 -0
- package/dist/controller/providerRetryAdmission.js +100 -0
- package/dist/controller/providerRetryDelivery.js +218 -0
- package/dist/controller/runtime.js +36 -1
- package/dist/integration/gitIntegrationService.js +19 -6
- package/dist/lifecycle/exactRunTerminalization.js +4 -1
- package/dist/message/globalProviderRetry.js +15 -0
- package/dist/observability/executionAudit.js +19 -0
- package/dist/repository/gitWorkspace.js +371 -105
- package/dist/repository/projectMaintenanceLock.js +75 -18
- package/dist/repository/taskWorkspaceCoordinator.js +71 -124
- package/dist/repository/taskWorkspacePreparer.js +86 -24
- package/dist/repository/workspaceCleanupInspection.js +187 -0
- package/dist/runtime/agentError.js +5 -3
- package/dist/runtime/agentHost.js +28 -11
- package/dist/runtime/builtinAgentErrorMappers.js +91 -0
- package/dist/runtime/codexAppServerRuntime.js +34 -3
- package/dist/runtime/providerControl.js +5 -1
- package/dist/runtime/providerRetry.js +198 -0
- package/dist/runtime/providerRuntimeIdentity.js +28 -2
- package/dist/runtime/sessionTokenMetrics.js +15 -5
- package/dist/runtime/structuredProviderHost.js +6 -2
- package/dist/runtime/taskUsageMetrics.js +275 -0
- package/dist/scheduler/activeRoleRunDelivery.js +12 -0
- package/dist/scheduler/leaderWakeupProcessor.js +5 -0
- package/dist/scheduler/taskExecutionProjection.js +26 -5
- package/dist/scheduler/taskObservabilityProjection.js +6 -44
- package/dist/storage/sqliteSchema.js +31 -0
- package/dist/storage/sqliteStore.js +17 -0
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +1 -0
- package/dist/storage/taskCatalog.js +123 -0
- package/dist/storage/taskStore.js +2 -0
- package/dist/task/archiveDiagnostics.js +1 -0
- package/dist/task/archivePreflight.js +124 -0
- package/dist/task/publicationAdoption.js +56 -0
- package/dist/task/publicationReference.js +10 -0
- package/dist/task/remoteDelivery.js +31 -16
- package/dist/web/assets/client/app.js +92 -17
- package/dist/web/assets/client/components.js +55 -13
- package/dist/web/assets/client/i18n.js +72 -4
- package/dist/web/assets/client/taskSurface.js +2 -1
- package/dist/web/assets/client/view.js +35 -7
- package/dist/web/assets/shell.js +6 -0
- package/dist/web/assets/styles/layout.js +7 -0
- package/dist/web/webServer.js +14 -3
- package/dist/web/webSnapshot.js +12 -3
- package/dist/workspace/cleanupInspection.js +63 -0
- package/dist/workspace/workItemChangeSetManager.js +110 -50
- package/docs/agent-result-consumption.md +4 -0
- package/docs/agent-result-consumption.zh-CN.md +3 -0
- package/docs/agent-runtime-drivers.md +7 -0
- package/docs/agent-runtime-drivers.zh-CN.md +5 -0
- package/docs/architecture/README.md +2 -0
- package/docs/architecture/README.zh-CN.md +3 -1
- package/docs/architecture/capabilities-and-resources.md +30 -5
- package/docs/architecture/capabilities-and-resources.zh-CN.md +23 -3
- package/docs/managed-turn-and-session-runtime.md +47 -0
- package/docs/managed-turn-and-session-runtime.zh-CN.md +40 -0
- package/docs/observability/README.md +62 -0
- package/docs/observability/README.zh-CN.md +47 -0
- package/docs/project-refresh.md +77 -0
- package/docs/project-refresh.zh-CN.md +59 -0
- package/docs/provider-retry.md +70 -0
- package/docs/task-delivery.md +133 -13
- package/docs/task-delivery.zh-CN.md +99 -10
- package/docs/task-discovery.md +102 -0
- package/docs/task-discovery.zh-CN.md +86 -0
- package/docs/testing/verification-levels.md +16 -0
- package/docs/testing/verification-levels.zh-CN.md +13 -1
- package/i18n/README.zh-CN.md +13 -7
- package/package.json +1 -1
- package/skills/yui-leader/references/execution.md +3 -2
- package/skills/yui-operator/SKILL.md +13 -2
- package/skills/yui-reviewer/SKILL.md +4 -0
- package/skills/yui-runtime/SKILL.md +17 -2
- package/skills/yui-runtime/references/publication.md +26 -4
- 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
|
|
|
@@ -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
|
-
|
|
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
|
|
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;
|
|
477
515
|
state.tasks = dashboard.tasks;
|
|
478
|
-
state.counts = dashboard.counts;
|
|
479
|
-
state.
|
|
480
|
-
state.
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
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
|
-
|
|
501
|
-
|
|
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
|
-
|
|
657
|
-
|
|
658
|
-
|
|
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(
|
|
237
|
-
card.append(
|
|
238
|
-
card.append(
|
|
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(
|
|
538
|
-
|
|
539
|
-
|
|
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": "
|
|
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.
|
|
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.
|
|
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,
|
|
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));
|
|
@@ -66,12 +66,17 @@ export function renderFilters(container, state, t, onFilter) {
|
|
|
66
66
|
if (!btn) return;
|
|
67
67
|
btn.classList.toggle("is-active", state.filter === status);
|
|
68
68
|
const label = btn.querySelector(".filter-label");
|
|
69
|
-
if (label) label.textContent = status === "all"
|
|
69
|
+
if (label) label.textContent = status === "all"
|
|
70
|
+
? t(state.catalogAll ? "catalog.all" : "filter.unarchived") : translatedStatus(t, "status", status);
|
|
70
71
|
const count = status === "all"
|
|
71
|
-
? (counts.total === undefined ? undefined : counts.total - (counts.archived || 0))
|
|
72
|
+
? (counts.total === undefined ? undefined : counts.total - (state.catalogAll ? 0 : counts.archived || 0))
|
|
72
73
|
: counts[status];
|
|
73
74
|
const badge = btn.querySelector(".filter-count");
|
|
74
75
|
if (badge) {
|
|
76
|
+
if (status === "archived" && state.catalogScope && state.catalogScope.archived === "excluded") {
|
|
77
|
+
badge.hidden = true;
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
75
80
|
if (count !== undefined && count !== null) {
|
|
76
81
|
badge.textContent = String(count);
|
|
77
82
|
badge.hidden = false;
|
|
@@ -83,7 +88,7 @@ export function renderFilters(container, state, t, onFilter) {
|
|
|
83
88
|
}
|
|
84
89
|
|
|
85
90
|
function taskGroupOf(task, attentionIds) {
|
|
86
|
-
if (attentionIds.has(task.id)) return "attention";
|
|
91
|
+
if (attentionIds.has(task.id) || (task.attention && (task.attention.openInputs > 0 || task.attention.unknownOperations > 0))) return "attention";
|
|
87
92
|
if (task.status === "active") return "active";
|
|
88
93
|
if (task.status === "draft") return "draft";
|
|
89
94
|
if (task.status === "cancelled") return "cancelled";
|
|
@@ -107,9 +112,9 @@ export function renderTasks(container, state, t, locale, onSelect) {
|
|
|
107
112
|
const attentionIds = new Set((state.attention || []).map(function (item) { return item.taskId; }));
|
|
108
113
|
const groups = { attention: [], active: [], draft: [], finished: [], cancelled: [], archived: [] };
|
|
109
114
|
(state.tasks || []).forEach(function (task) {
|
|
110
|
-
if (state.filter === "all" && task.status === "archived") return;
|
|
115
|
+
if (state.filter === "all" && !state.catalogAll && task.status === "archived") return;
|
|
111
116
|
if (state.filter !== "all" && task.status !== state.filter) return;
|
|
112
|
-
if (!taskMatchesQuery(task, query, locale)) return;
|
|
117
|
+
if (!state.catalogScope && !taskMatchesQuery(task, query, locale)) return;
|
|
113
118
|
groups[taskGroupOf(task, attentionIds)].push(task);
|
|
114
119
|
});
|
|
115
120
|
|
|
@@ -166,15 +171,33 @@ export function renderOverview(detail, state, t, locale, onSelect) {
|
|
|
166
171
|
);
|
|
167
172
|
wrap.append(rail);
|
|
168
173
|
|
|
174
|
+
if (state.catalogAttention) {
|
|
175
|
+
const catalog = node("section", "overview-block");
|
|
176
|
+
catalog.append(sectionHead(t("catalog.attention")));
|
|
177
|
+
catalog.append(node("p", "", t("catalog.signalsHelp")));
|
|
178
|
+
const groups = node("div", "overview-list");
|
|
179
|
+
Object.entries(state.catalogAttention).forEach(function (entry) {
|
|
180
|
+
const kind = entry[0], value = entry[1];
|
|
181
|
+
const button = node("button", "overview-row",
|
|
182
|
+
t("catalog." + kind) + " · " + value.count + " / " + value.taskCount + " " + t("catalog.tasks"));
|
|
183
|
+
button.type = "button";
|
|
184
|
+
button.dataset.catalogAttention = kind;
|
|
185
|
+
button.disabled = value.taskCount === 0;
|
|
186
|
+
groups.append(button);
|
|
187
|
+
});
|
|
188
|
+
catalog.append(groups);
|
|
189
|
+
wrap.append(catalog);
|
|
190
|
+
}
|
|
169
191
|
const inbox = node("section", "overview-block");
|
|
170
192
|
const inboxHead = sectionHead(t("overview.inbox"), {
|
|
171
|
-
count: (state.attention || []).length
|
|
193
|
+
count: counts ? counts.openInputs : (state.attention || []).length
|
|
172
194
|
});
|
|
173
195
|
inbox.append(inboxHead);
|
|
174
196
|
const items = state.attention || [];
|
|
175
197
|
if (!items.length) {
|
|
176
198
|
const empty = node("div", "inbox-empty");
|
|
177
|
-
empty.append(node("span", "dot"), node("span", "",
|
|
199
|
+
empty.append(node("span", "dot"), node("span", "", counts && counts.openInputs > 0
|
|
200
|
+
? t("catalog.inspectInputs") : t("overview.inboxEmpty")));
|
|
178
201
|
inbox.append(empty);
|
|
179
202
|
} else {
|
|
180
203
|
const list = node("div", "inbox-list");
|
|
@@ -184,6 +207,7 @@ export function renderOverview(detail, state, t, locale, onSelect) {
|
|
|
184
207
|
inbox.append(list);
|
|
185
208
|
}
|
|
186
209
|
wrap.append(inbox);
|
|
210
|
+
if (state.catalogScope) wrap.append(node("p", "", t("catalog.pageScope")));
|
|
187
211
|
|
|
188
212
|
// Tasks whose Task-first projection says they need attention: blocked,
|
|
189
213
|
// recovering, or in an attention state. This replaces the raw stalled-AgentRun
|
|
@@ -335,9 +359,13 @@ export function renderTaskDetail(detail, data, t, locale, actions) {
|
|
|
335
359
|
"muted mono",
|
|
336
360
|
project.directory + " · " + project.coverage + " · "
|
|
337
361
|
+ (project.expectedLocalCommit ? project.expectedLocalCommit.slice(0, 12) : "unknown")
|
|
362
|
+
+ " → " + (project.deliveryLocalCommit ? project.deliveryLocalCommit.slice(0, 12) : "unknown")
|
|
338
363
|
+ " → "
|
|
339
364
|
+ (project.remoteCommit ? project.remoteCommit.slice(0, 12) : "unknown")
|
|
365
|
+
+ " · PR/MR: " + (project.state || "none")
|
|
366
|
+
+ (project.adoption ? " · " + project.adoption.id : "")
|
|
340
367
|
));
|
|
368
|
+
card.append(node("p", "record-copy", project.reason));
|
|
341
369
|
});
|
|
342
370
|
summaryBody.append(card);
|
|
343
371
|
}
|
package/dist/web/assets/shell.js
CHANGED
|
@@ -38,6 +38,12 @@ export const DASHBOARD_HTML = `<!doctype html>
|
|
|
38
38
|
</label>
|
|
39
39
|
<div id="status-filters" class="filters" role="group" aria-label="Filter by status" data-i18n-aria-label="filters.label"></div>
|
|
40
40
|
<div id="task-list" class="task-list" aria-label="Tasks" data-i18n-aria-label="board.title" aria-live="polite"><div class="loading" data-i18n="loading.dashboard">Reading local state…</div></div>
|
|
41
|
+
<div id="catalog-controls" class="catalog-pager" aria-live="polite">
|
|
42
|
+
<button id="catalog-reset" type="button" data-i18n="catalog.first">First page</button>
|
|
43
|
+
<span id="catalog-count"></span>
|
|
44
|
+
<button id="catalog-next" type="button" data-i18n="catalog.next" disabled>Next page</button>
|
|
45
|
+
<button id="catalog-attention-reset" type="button" data-i18n="catalog.clearAttention" hidden>Clear attention filter</button>
|
|
46
|
+
</div>
|
|
41
47
|
<div class="sidebar-foot">
|
|
42
48
|
<div class="sidebar-controls">
|
|
43
49
|
<label class="select-control">
|
|
@@ -19,6 +19,13 @@ body.terminal-active .app-shell{grid-template-columns:var(--sidebar-w) minmax(0,
|
|
|
19
19
|
.task-group-head{display:flex;align-items:baseline;gap:8px;padding:3px 4px 2px}
|
|
20
20
|
.sidebar-foot{flex:none;padding-top:10px;margin-top:8px;display:grid;gap:8px}
|
|
21
21
|
.sidebar-controls{display:grid;grid-template-columns:1fr 1fr;gap:8px;align-items:center}
|
|
22
|
+
.catalog-pager{display:grid;grid-template-columns:1fr 1fr;gap:6px;flex:none;padding-top:10px}
|
|
23
|
+
.catalog-pager span{grid-column:1/-1;grid-row:1;text-align:center;font-size:11px;color:var(--muted)}
|
|
24
|
+
.catalog-pager button{min-height:32px;min-width:0;border:1px solid var(--border);border-radius:6px;background:var(--bg);color:var(--text);font:inherit;font-size:11px;cursor:pointer}
|
|
25
|
+
.catalog-pager button:hover:not(:disabled){border-color:var(--accent)}
|
|
26
|
+
.catalog-pager button:focus-visible{outline:2px solid var(--accent);outline-offset:2px}
|
|
27
|
+
.catalog-pager button:disabled{opacity:.45;cursor:default}
|
|
28
|
+
#catalog-attention-reset{grid-column:1/-1}
|
|
22
29
|
/* Main column = top bar + optional section nav + reading surface */
|
|
23
30
|
.main-col{display:flex;flex-direction:column;min-width:0;min-height:0;height:100vh;overflow-y:auto;scrollbar-gutter:stable}
|
|
24
31
|
.topbar{position:sticky;top:0;z-index:30;flex:none;display:flex;align-items:center;justify-content:space-between;gap:18px;padding:11px max(var(--page-space),calc((100% - 1100px) / 2));background:color-mix(in srgb,var(--bg) 92%,transparent);backdrop-filter:blur(10px);-webkit-backdrop-filter:blur(10px);border-bottom:1px solid var(--border)}
|
package/dist/web/webServer.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { randomBytes, timingSafeEqual } from "node:crypto";
|
|
2
2
|
import { createServer } from "node:http";
|
|
3
3
|
import WebSocket, { WebSocketServer } from "ws";
|
|
4
|
-
import { usageError } from "../errors/cliError.js";
|
|
4
|
+
import { CliError, usageError } from "../errors/cliError.js";
|
|
5
|
+
import { parseTaskCatalogOptions } from "../context/taskCatalog.js";
|
|
5
6
|
import { WebRequestRejected } from "./webMutation.js";
|
|
6
7
|
import { TASK_SUBMISSION_INTENTS } from "../message/message.js";
|
|
7
8
|
import { DASHBOARD_HTML, findWebAsset } from "./assets/assetManifest.js";
|
|
8
|
-
import { buildWebDashboardSnapshot, buildWebTaskDetail } from "./webSnapshot.js";
|
|
9
|
+
import { buildWebDashboardSnapshot, buildWebTaskCatalog, buildWebTaskDetail } from "./webSnapshot.js";
|
|
9
10
|
const MAX_JSON_BODY_BYTES = 16 * 1024;
|
|
10
11
|
const MAX_TERMINAL_MESSAGE_BYTES = 64 * 1024;
|
|
11
12
|
const MAX_TERMINAL_BUFFERED_BYTES = 1024 * 1024;
|
|
@@ -236,7 +237,13 @@ async function handleHttpRequest(request, response, store, dependencies, token,
|
|
|
236
237
|
sendAsset(response, asset, method === "HEAD");
|
|
237
238
|
}
|
|
238
239
|
else if (pathname === "/api/dashboard") {
|
|
239
|
-
|
|
240
|
+
const query = new URL(request.url, "http://localhost").searchParams;
|
|
241
|
+
const snapshot = query.has("view")
|
|
242
|
+
? buildWebTaskCatalog(store, parseTaskCatalogOptions([...query].flatMap(([key, value]) => key === "all"
|
|
243
|
+
? (value === "true" ? ["--all"] : ["--invalid-all"])
|
|
244
|
+
: [`--${key}`, value])))
|
|
245
|
+
: buildWebDashboardSnapshot(store, now());
|
|
246
|
+
sendJson(response, 200, snapshot, method === "HEAD");
|
|
240
247
|
}
|
|
241
248
|
else if (pathname.startsWith("/api/tasks/")) {
|
|
242
249
|
const taskId = decodeURIComponent(pathname.slice("/api/tasks/".length));
|
|
@@ -250,6 +257,10 @@ async function handleHttpRequest(request, response, store, dependencies, token,
|
|
|
250
257
|
}
|
|
251
258
|
}
|
|
252
259
|
catch (error) {
|
|
260
|
+
if (error instanceof CliError) {
|
|
261
|
+
sendJson(response, 400, { error: error.message }, method === "HEAD");
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
253
264
|
if (error instanceof URIError) {
|
|
254
265
|
sendJson(response, 400, { error: "Invalid URL encoding." }, method === "HEAD");
|
|
255
266
|
return;
|
package/dist/web/webSnapshot.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { providerRetryProjection } from "../runtime/providerRetry.js";
|
|
2
|
+
import { readTaskCatalog } from "../context/taskCatalog.js";
|
|
1
3
|
import { isRoleRunStalled, latestRunDurableProgressAt } from "../scheduler/roleRunStall.js";
|
|
2
4
|
import { retiredTaskRecordIds } from "../task/taskRecordRetirement.js";
|
|
3
5
|
import { buildTaskExecutionProjection } from "../scheduler/taskExecutionProjection.js";
|
|
@@ -9,6 +11,9 @@ import { runExecutionObservation } from "../agentRun/agentRun.js";
|
|
|
9
11
|
import { resolveRuntimeHealth } from "../config/yuiConfig.js";
|
|
10
12
|
import { projectSessionTokenMetrics, resolveSessionTokenIdentity } from "../runtime/sessionTokenMetrics.js";
|
|
11
13
|
import { projectTaskRemoteDelivery } from "../task/remoteDelivery.js";
|
|
14
|
+
export function buildWebTaskCatalog(store, options) {
|
|
15
|
+
return store.transaction(reader => readTaskCatalog(reader, options));
|
|
16
|
+
}
|
|
12
17
|
export function buildWebDashboardSnapshot(store, now = new Date()) {
|
|
13
18
|
return store.transaction((reader) => {
|
|
14
19
|
const statusCounts = {
|
|
@@ -95,12 +100,15 @@ export function buildWebTaskDetail(store, taskId, now = new Date()) {
|
|
|
95
100
|
const activeSession = sessions?.sessions[activeRun?.effective.agentId ?? sessions.activeAgentId];
|
|
96
101
|
const effectiveLaunch = activeRun?.effective ?? activeSession?.effective ?? null;
|
|
97
102
|
const delivery = sessions?.providerBinding?.run?.status;
|
|
103
|
+
const providerRetry = providerRetryProjection(sessions?.providerBinding);
|
|
98
104
|
return {
|
|
99
105
|
...role,
|
|
106
|
+
providerRetry,
|
|
100
107
|
// An open record is not proof that its Agent is running.
|
|
101
|
-
status:
|
|
102
|
-
: delivery === "
|
|
103
|
-
:
|
|
108
|
+
status: providerRetry?.status === "waiting" ? "waiting"
|
|
109
|
+
: delivery === "accepted" ? "running"
|
|
110
|
+
: delivery === "submitting" || delivery === "deferred" ? "waiting"
|
|
111
|
+
: activeSession === undefined && activeRun === undefined ? "idle" : "unknown",
|
|
104
112
|
sessionTokens: projectSessionTokenMetrics(events, resolveSessionTokenIdentity(activeSession === undefined
|
|
105
113
|
? null
|
|
106
114
|
: { taskId, roleName: role.name, ...activeSession })),
|
|
@@ -162,6 +170,7 @@ function webRemoteDelivery(reader, task) {
|
|
|
162
170
|
publications: reader.listPublicationReferences(task.id),
|
|
163
171
|
managedWorkspaces: reader.listManagedWorkspaces(task.id),
|
|
164
172
|
runs: reader.listRuns(task.id),
|
|
173
|
+
integrations: reader.listIntegrationAttempts(task.id),
|
|
165
174
|
currentCandidate: null
|
|
166
175
|
});
|
|
167
176
|
}
|