@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.
- package/ARCHITECTURE.md +11 -6
- package/ARCHITECTURE.zh-CN.md +8 -4
- package/README.md +13 -5
- package/dist/agentRun/agentRun.js +3 -0
- package/dist/artifacts/artifactCommitLock.js +16 -55
- package/dist/artifacts/taskArtifactRepository.js +3 -3
- package/dist/cli/agentConfigurationPicker.js +1 -1
- package/dist/cli/commandCatalog.js +92 -48
- package/dist/cli/completion.js +18 -18
- package/dist/cli/completionWizard.js +1 -1
- package/dist/cli/dynamicCompletion.js +1 -1
- package/dist/cli/interactionPolicy.js +11 -3
- package/dist/cli/invocationAuthority.js +64 -0
- package/dist/cli/managedDiagnostics.js +2 -2
- package/dist/cli/parseRepeatable.js +35 -0
- package/dist/cli/updatePorts.js +26 -5
- package/dist/cli.js +1303 -1235
- package/dist/commands/agentCommands.js +4 -4
- package/dist/commands/capabilityCommands.js +1 -1
- package/dist/commands/configCommands.js +6 -57
- package/dist/commands/configOverview.js +2 -2
- package/dist/commands/durableJobCommands.js +12 -6
- package/dist/commands/executionAuditCommands.js +10 -0
- package/dist/commands/globalRoleCommands.js +78 -29
- package/dist/commands/grantCommands.js +0 -3
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/operatorCommands.js +3 -3
- package/dist/commands/projectCommands.js +45 -25
- package/dist/commands/resourcesCommands.js +10 -40
- package/dist/commands/roleConfiguration.js +2 -6
- package/dist/commands/taskActivationCommands.js +4 -8
- package/dist/commands/taskAgentCapabilities.js +9 -0
- package/dist/commands/taskCommandSupport.js +155 -0
- package/dist/commands/taskCommandTypes.js +1 -0
- package/dist/commands/taskCommands.js +355 -732
- package/dist/commands/taskCompletionGate.js +1 -1
- package/dist/commands/taskExecutionCommands.js +1 -1
- package/dist/commands/taskFactCommands.js +325 -0
- package/dist/commands/taskInputCommands.js +12 -42
- package/dist/commands/taskIntegrationCommands.js +20 -58
- package/dist/commands/taskNextActionCommand.js +7 -6
- package/dist/commands/taskPublicationAdoptCommand.js +127 -0
- package/dist/commands/taskPublicationCommands.js +12 -3
- package/dist/commands/taskPublicationVerifyCommand.js +24 -40
- package/dist/commands/taskRemoteDeliveryCommand.js +5 -86
- package/dist/commands/taskUpstreamCommands.js +19 -8
- package/dist/commands/workflowCommands.js +6 -1
- package/dist/completion/completionInstaller.js +26 -26
- package/dist/completion/completionState.js +26 -26
- package/dist/completion/fileCompletionManager.js +6 -11
- package/dist/config/configCatalog.js +0 -2
- package/dist/config/timeZone.js +14 -0
- package/dist/config/yuiConfig.js +0 -29
- package/dist/context/runContextPack.js +5 -3
- package/dist/context/taskCatalog.js +185 -0
- package/dist/context/taskContext.js +152 -151
- package/dist/context/wakeRunReferences.js +11 -0
- package/dist/controller/agentCapabilities.js +58 -0
- package/dist/controller/agentRuntimeObserver.js +6 -11
- package/dist/controller/clientRuntime.js +22 -14
- package/dist/controller/controller.js +20 -24
- package/dist/controller/fileSchedulerStoreAdapter.js +191 -225
- package/dist/controller/globalInputDelivery.js +13 -0
- package/dist/controller/jobClient.js +4 -4
- package/dist/controller/jobControl.js +60 -25
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/providerRetryAdmission.js +100 -0
- package/dist/controller/providerRetryDelivery.js +218 -0
- package/dist/controller/runtime.js +85 -43
- package/dist/controller/runtimeEventProcessor.js +0 -5
- package/dist/controller/sessionOwnerReconciliation.js +5 -0
- package/dist/controller/taskAgentError.js +35 -0
- package/dist/coordination/workMailboxQueue.js +2 -24
- package/dist/core/controllerClient.js +2 -2
- package/dist/core/fileLockOwner.js +74 -0
- package/dist/decision/decision.js +17 -0
- package/dist/doctor/doctor.js +18 -9
- package/dist/event/taskEvent.js +12 -0
- package/dist/execution/workItemExecutionProjection.js +2 -2
- package/dist/executor/agentCapabilityConfig.js +43 -0
- package/dist/executor/agentConfigurationCatalog.js +49 -11
- package/dist/executor/agentExecutor.js +5 -8
- package/dist/executor/fileRoleLaunchPlanner.js +11 -53
- package/dist/executor/taskAgentCapabilities.js +64 -0
- package/dist/integration/deliveryObligation.js +1 -69
- package/dist/integration/gitIntegrationService.js +79 -90
- package/dist/integration/integrationAttempt.js +3 -14
- package/dist/integration/integrationSourceApplication.js +4 -12
- package/dist/integration/manifestTags.js +2 -2
- package/dist/job/durableJob.js +4 -25
- package/dist/job/jobAssignmentScope.js +22 -0
- package/dist/job/jobOperation.js +16 -0
- package/dist/job/jobRunner.js +2 -1
- package/dist/job/stepDirectory.js +15 -0
- package/dist/kernel/builtinCapabilities.js +15 -11
- package/dist/kernel/kernelPorts.js +1 -17
- package/dist/lifecycle/exactRunTerminalization.js +5 -10
- package/dist/message/globalProviderRetry.js +15 -0
- package/dist/message/inputControlResolution.js +3 -4
- package/dist/message/message.js +17 -20
- package/dist/message/messageContinuation.js +1 -1
- package/dist/milestone/milestone.js +11 -0
- package/dist/observability/executionAudit.js +19 -0
- package/dist/observability/orchestrationMetrics.js +24 -1
- package/dist/observability/runtimeIdentity.js +1 -17
- package/dist/output/agentConfigurationPresentation.js +2 -0
- package/dist/output/timePresentation.js +1 -14
- package/dist/plugins/pluginService.js +12 -4
- package/dist/release/releaseWorkflowPorts.js +45 -96
- package/dist/release/runtimeRelease.js +9 -15
- package/dist/repository/gitWorkspace.js +371 -105
- package/dist/repository/projectMaintenanceLock.js +98 -82
- package/dist/repository/taskBaseFreshness.js +1 -1
- package/dist/repository/taskWorkspaceCoordinator.js +82 -144
- package/dist/repository/taskWorkspacePreparer.js +113 -47
- package/dist/repository/workspaceCleanupInspection.js +187 -0
- package/dist/resources/autoResourceGc.js +4 -77
- package/dist/resources/liveReferences.js +7 -54
- package/dist/resources/resourceDiscovery.js +5 -118
- package/dist/resources/resourceGc.js +233 -181
- package/dist/resources/resourceRegistrar.js +5 -4
- package/dist/resources/resourceRegistry.js +3 -37
- package/dist/resources/resourceRegistryStore.js +13 -36
- package/dist/resources/sqliteResourceRegistry.js +43 -26
- package/dist/review/deltaRecheck.js +1 -1
- package/dist/review/reviewAcceptance.js +1 -1
- package/dist/review/reviewDecision.js +1 -1
- package/dist/review/reviewRound.js +8 -7
- package/dist/review/taskFinalReviewContractResolution.js +1 -1
- package/dist/runtime/acpProtocol.js +4 -51
- package/dist/runtime/acpSession.js +10 -75
- package/dist/runtime/acpSessionConfiguration.js +1 -8
- package/dist/runtime/agentEndpoint.js +3 -2
- package/dist/runtime/agentError.js +5 -3
- package/dist/runtime/agentFailureContext.js +43 -0
- package/dist/runtime/agentHost.js +42 -25
- package/dist/runtime/builtinAgentErrorMappers.js +91 -0
- package/dist/runtime/codexAppServerRuntime.js +34 -3
- package/dist/runtime/codexInteractiveHost.js +2 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/managedCaller.js +21 -1
- package/dist/runtime/providerControl.js +5 -1
- package/dist/runtime/providerErrors.js +38 -0
- package/dist/runtime/providerRetry.js +198 -0
- package/dist/runtime/providerRuntimeIdentity.js +28 -2
- package/dist/runtime/runtimeObservation.js +0 -5
- package/dist/runtime/runtimeSessionCandidate.js +2 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/sessionTokenMetrics.js +15 -5
- package/dist/runtime/structuredProviderHost.js +10 -43
- package/dist/runtime/taskUsageMetrics.js +275 -0
- package/dist/runtime/tmuxAdapters.js +6 -8
- package/dist/scheduler/activeRoleRunDelivery.js +19 -7
- package/dist/scheduler/leaderWakeupProcessor.js +24 -6
- package/dist/scheduler/operatorEvent.js +12 -20
- package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
- package/dist/scheduler/ports.js +5 -8
- package/dist/scheduler/roleRunLiveness.js +4 -4
- package/dist/scheduler/roleRunStall.js +16 -23
- package/dist/scheduler/taskExecutionProjection.js +49 -36
- package/dist/scheduler/taskObservabilityProjection.js +6 -45
- package/dist/scheduler/taskWake.js +5 -10
- package/dist/scheduler/wakeReason.js +2 -0
- package/dist/scheduler/wakeupQueue.js +2 -4
- package/dist/setup/setupCommand.js +1 -1
- package/dist/storage/contextRecords.js +106 -0
- package/dist/storage/currentTaskStore.js +1 -1
- package/dist/storage/homeLayout.js +13 -17
- package/dist/storage/migrations/agentFailureContext.js +22 -0
- package/dist/storage/migrations/currentInputContract.js +86 -0
- package/dist/storage/migrations/currentRuntimeContract.js +228 -0
- package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
- package/dist/storage/migrations/integrationContinuation.js +5 -4
- package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
- package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
- package/dist/storage/migrations/verificationPlanV1.js +162 -0
- package/dist/storage/migrations/verificationPolicy.js +74 -0
- package/dist/storage/migrations/workItemHistory.js +46 -0
- package/dist/storage/persistenceWorker.js +12 -4
- package/dist/storage/recordValidation.js +87 -0
- package/dist/storage/sqliteSchema.js +173 -1
- package/dist/storage/sqliteStore.js +172 -161
- package/dist/storage/storageSchema.js +2 -14
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +16 -7
- package/dist/storage/taskCatalog.js +123 -0
- package/dist/storage/taskStore.js +5 -7
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
- package/dist/task/archiveDiagnostics.js +1 -0
- package/dist/task/archivePreflight.js +124 -0
- package/dist/task/completionReadiness.js +5 -24
- package/dist/task/draftPlan.js +0 -53
- package/dist/task/nextAction.js +7 -13
- package/dist/task/publicationAdoption.js +56 -0
- package/dist/task/publicationReference.js +10 -0
- package/dist/task/remoteDelivery.js +31 -16
- package/dist/task/remoteDeliveryService.js +89 -0
- package/dist/task/taskActivation.js +2 -25
- package/dist/task/taskActivationService.js +0 -2
- package/dist/task/taskRecordReference.js +0 -1
- package/dist/task/taskSubmission.js +3 -14
- package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
- package/dist/telemetry/sqliteTelemetryStore.js +49 -49
- package/dist/telemetry/telemetryWiring.js +4 -3
- package/dist/tmux/tmuxManager.js +29 -20
- package/dist/verification/gateArtifact.js +15 -24
- package/dist/verification/gateArtifactStore.js +14 -7
- package/dist/verification/verificationGateService.js +29 -88
- package/dist/verification/verificationPlan.js +27 -72
- 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 +6 -5
- 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 +12 -3
- package/dist/web/webSnapshot.js +14 -85
- package/dist/web/webTaskSurface.js +17 -46
- package/dist/workItem/workItem.js +1 -12
- 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 +42 -5
- package/docs/architecture/capabilities-and-resources.zh-CN.md +33 -3
- package/docs/managed-turn-and-session-runtime.md +121 -1
- package/docs/managed-turn-and-session-runtime.zh-CN.md +96 -2
- package/docs/observability/README.md +62 -0
- package/docs/observability/README.zh-CN.md +47 -0
- package/docs/plugin-sdk.md +4 -2
- 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/provider-runtime.md +3 -1
- package/docs/provider-runtime.zh-CN.md +4 -2
- package/docs/release-workflow.md +190 -7
- package/docs/release-workflow.zh-CN.md +139 -5
- package/docs/roles-and-configuration.md +29 -0
- package/docs/roles-and-configuration.zh-CN.md +21 -0
- package/docs/sqlite-control-plane-design.md +16 -3
- package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
- package/docs/task-dag-semantics.md +3 -3
- package/docs/task-dag-semantics.zh-CN.md +2 -2
- package/docs/task-delivery.md +223 -17
- package/docs/task-delivery.zh-CN.md +171 -14
- package/docs/task-discovery.md +101 -0
- package/docs/task-discovery.zh-CN.md +85 -0
- package/docs/testing/verification-levels.md +161 -9
- package/docs/testing/verification-levels.zh-CN.md +116 -9
- package/i18n/README.zh-CN.md +13 -7
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -0
- package/skills/yui-leader/references/execution.md +3 -2
- package/skills/yui-leader/references/integration.md +9 -5
- package/skills/yui-leader/references/planning.md +9 -3
- package/skills/yui-operator/SKILL.md +24 -8
- 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 +71 -0
- package/dist/agent/agentRegistry.js +0 -10
- package/dist/agentRun/runIdentity.js +0 -22
- package/dist/commands/deliveryGuardPreflight.js +0 -30
- package/dist/commands/taskIntegrationQueueCommands.js +0 -226
- package/dist/commands/taskOverviewCommand.js +0 -376
- package/dist/completion/completionWizard.js +0 -125
- package/dist/context/dispatchContext.js +0 -110
- package/dist/context/wakeNotification.js +0 -144
- package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
- package/dist/integration/integrationQueueEntry.js +0 -191
- package/dist/integration/integrationQueueService.js +0 -810
- package/dist/release/fakeReleasePorts.js +0 -55
- package/dist/runtime/sessionOwnerRegistry.js +0 -137
- package/dist/task/deliveryGuard.js +0 -226
- /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
|
@@ -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 {
|
|
9
|
+
import { 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,11 @@ 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 = buildWebTaskCatalog(store, parseTaskCatalogOptions([...query].flatMap(([key, value]) => key === "all"
|
|
242
|
+
? (value === "true" ? ["--all"] : ["--invalid-all"])
|
|
243
|
+
: [`--${key}`, value])));
|
|
244
|
+
sendJson(response, 200, snapshot, method === "HEAD");
|
|
240
245
|
}
|
|
241
246
|
else if (pathname.startsWith("/api/tasks/")) {
|
|
242
247
|
const taskId = decodeURIComponent(pathname.slice("/api/tasks/".length));
|
|
@@ -250,6 +255,10 @@ async function handleHttpRequest(request, response, store, dependencies, token,
|
|
|
250
255
|
}
|
|
251
256
|
}
|
|
252
257
|
catch (error) {
|
|
258
|
+
if (error instanceof CliError) {
|
|
259
|
+
sendJson(response, 400, { error: error.message }, method === "HEAD");
|
|
260
|
+
return;
|
|
261
|
+
}
|
|
253
262
|
if (error instanceof URIError) {
|
|
254
263
|
sendJson(response, 400, { error: "Invalid URL encoding." }, method === "HEAD");
|
|
255
264
|
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,56 +11,8 @@ 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";
|
|
12
|
-
export function
|
|
13
|
-
return store.transaction(
|
|
14
|
-
const statusCounts = {
|
|
15
|
-
draft: 0,
|
|
16
|
-
active: 0,
|
|
17
|
-
completed: 0,
|
|
18
|
-
cancelled: 0,
|
|
19
|
-
archived: 0
|
|
20
|
-
};
|
|
21
|
-
const projectNames = new Map(reader.listProjects().map((project) => [project.id, project.name]));
|
|
22
|
-
let openInputs = 0;
|
|
23
|
-
const attention = [];
|
|
24
|
-
const tasks = reader.listTasks().map((task) => {
|
|
25
|
-
statusCounts[task.status] += 1;
|
|
26
|
-
const taskOpenInputs = reader.listInputRequests(task.id)
|
|
27
|
-
.filter((request) => request.status === "open").length;
|
|
28
|
-
if (task.status !== "archived")
|
|
29
|
-
openInputs += taskOpenInputs;
|
|
30
|
-
const taskOpen = reader.listInputRequests(task.id)
|
|
31
|
-
.filter((request) => request.status === "open");
|
|
32
|
-
for (const request of task.status === "archived" ? [] : taskOpen) {
|
|
33
|
-
attention.push({ taskId: task.id, taskTitle: task.title, request });
|
|
34
|
-
}
|
|
35
|
-
const events = reader.listEvents?.(task.id) ?? [];
|
|
36
|
-
const needsAttentionCount = task.status === "archived" ? 0 : reader.listRuns(task.id)
|
|
37
|
-
.filter((run) => run.status === "active" && isRoleRunStalled(events, run.id))
|
|
38
|
-
.length;
|
|
39
|
-
const execution = buildTaskExecutionProjection(reader, task.id, task, now);
|
|
40
|
-
const names = task.projectBindings.flatMap(({ projectId }) => {
|
|
41
|
-
const name = projectNames.get(projectId);
|
|
42
|
-
return name === undefined ? [] : [name];
|
|
43
|
-
});
|
|
44
|
-
return {
|
|
45
|
-
...task,
|
|
46
|
-
...(names.length === 0 ? {} : { projectNames: names }),
|
|
47
|
-
workItems: countWorkItems(reader.listWorkItems(task.id)),
|
|
48
|
-
roleCount: reader.listRoles(task.id).length,
|
|
49
|
-
openInputCount: taskOpenInputs,
|
|
50
|
-
needsAttentionCount,
|
|
51
|
-
execution,
|
|
52
|
-
executionStatus: execution?.status ?? null
|
|
53
|
-
};
|
|
54
|
-
}).sort(compareDashboardTasks);
|
|
55
|
-
return {
|
|
56
|
-
generatedAt: now.toISOString(),
|
|
57
|
-
counts: { total: tasks.length, ...statusCounts, openInputs },
|
|
58
|
-
attention: attention.sort(compareAttention),
|
|
59
|
-
tasks
|
|
60
|
-
};
|
|
61
|
-
});
|
|
14
|
+
export function buildWebTaskCatalog(store, options) {
|
|
15
|
+
return store.transaction(reader => readTaskCatalog(reader, options));
|
|
62
16
|
}
|
|
63
17
|
export function buildWebTaskDetail(store, taskId, now = new Date()) {
|
|
64
18
|
return store.transaction((reader) => {
|
|
@@ -72,7 +26,7 @@ export function buildWebTaskDetail(store, taskId, now = new Date()) {
|
|
|
72
26
|
return name === undefined ? [] : [name];
|
|
73
27
|
});
|
|
74
28
|
const runs = reader.listRuns(taskId);
|
|
75
|
-
const events = reader.listEvents
|
|
29
|
+
const events = reader.listEvents(taskId);
|
|
76
30
|
const retiredMessageIds = retiredTaskRecordIds(events, "message");
|
|
77
31
|
const needsAttentionRuns = runs
|
|
78
32
|
.filter((run) => run.status === "active" && isRoleRunStalled(events, run.id))
|
|
@@ -95,12 +49,15 @@ export function buildWebTaskDetail(store, taskId, now = new Date()) {
|
|
|
95
49
|
const activeSession = sessions?.sessions[activeRun?.effective.agentId ?? sessions.activeAgentId];
|
|
96
50
|
const effectiveLaunch = activeRun?.effective ?? activeSession?.effective ?? null;
|
|
97
51
|
const delivery = sessions?.providerBinding?.run?.status;
|
|
52
|
+
const providerRetry = providerRetryProjection(sessions?.providerBinding);
|
|
98
53
|
return {
|
|
99
54
|
...role,
|
|
55
|
+
providerRetry,
|
|
100
56
|
// An open record is not proof that its Agent is running.
|
|
101
|
-
status:
|
|
102
|
-
: delivery === "
|
|
103
|
-
:
|
|
57
|
+
status: providerRetry?.status === "waiting" ? "waiting"
|
|
58
|
+
: delivery === "accepted" ? "running"
|
|
59
|
+
: delivery === "submitting" || delivery === "deferred" ? "waiting"
|
|
60
|
+
: activeSession === undefined && activeRun === undefined ? "idle" : "unknown",
|
|
104
61
|
sessionTokens: projectSessionTokenMetrics(events, resolveSessionTokenIdentity(activeSession === undefined
|
|
105
62
|
? null
|
|
106
63
|
: { taskId, roleName: role.name, ...activeSession })),
|
|
@@ -120,7 +77,7 @@ export function buildWebTaskDetail(store, taskId, now = new Date()) {
|
|
|
120
77
|
&& effectiveLaunch.sourceDesiredRevision !== role.launchRevision
|
|
121
78
|
};
|
|
122
79
|
});
|
|
123
|
-
const execution = buildTaskExecutionProjection(reader, taskId,
|
|
80
|
+
const execution = buildTaskExecutionProjection(reader, taskId, now);
|
|
124
81
|
if (execution === null)
|
|
125
82
|
return null;
|
|
126
83
|
const remoteDelivery = webRemoteDelivery(reader, task);
|
|
@@ -158,10 +115,11 @@ export function buildWebTaskDetail(store, taskId, now = new Date()) {
|
|
|
158
115
|
function webRemoteDelivery(reader, task) {
|
|
159
116
|
return projectTaskRemoteDelivery({
|
|
160
117
|
task,
|
|
161
|
-
events: reader.listEvents
|
|
118
|
+
events: reader.listEvents(task.id),
|
|
162
119
|
publications: reader.listPublicationReferences(task.id),
|
|
163
120
|
managedWorkspaces: reader.listManagedWorkspaces(task.id),
|
|
164
121
|
runs: reader.listRuns(task.id),
|
|
122
|
+
integrations: reader.listIntegrationAttempts(task.id),
|
|
165
123
|
currentCandidate: null
|
|
166
124
|
});
|
|
167
125
|
}
|
|
@@ -258,32 +216,3 @@ function latestStallField(events, runId, field) {
|
|
|
258
216
|
.sort((left, right) => Date.parse(right.createdAt) - Date.parse(left.createdAt))[0];
|
|
259
217
|
return stalled?.payload[field];
|
|
260
218
|
}
|
|
261
|
-
function countWorkItems(items) {
|
|
262
|
-
const counts = {
|
|
263
|
-
total: items.length,
|
|
264
|
-
open: 0,
|
|
265
|
-
accepted: 0,
|
|
266
|
-
retired: 0
|
|
267
|
-
};
|
|
268
|
-
const mutable = counts;
|
|
269
|
-
for (const item of items) {
|
|
270
|
-
mutable[item.status] += 1;
|
|
271
|
-
}
|
|
272
|
-
return counts;
|
|
273
|
-
}
|
|
274
|
-
function compareDashboardTasks(left, right) {
|
|
275
|
-
const statusOrder = {
|
|
276
|
-
active: 0,
|
|
277
|
-
draft: 1,
|
|
278
|
-
completed: 2,
|
|
279
|
-
cancelled: 3,
|
|
280
|
-
archived: 4
|
|
281
|
-
};
|
|
282
|
-
return statusOrder[left.status] - statusOrder[right.status]
|
|
283
|
-
|| Date.parse(right.updatedAt) - Date.parse(left.updatedAt)
|
|
284
|
-
|| left.id.localeCompare(right.id);
|
|
285
|
-
}
|
|
286
|
-
function compareAttention(left, right) {
|
|
287
|
-
return Date.parse(left.request.createdAt) - Date.parse(right.request.createdAt)
|
|
288
|
-
|| left.request.id.localeCompare(right.request.id);
|
|
289
|
-
}
|
|
@@ -1,37 +1,17 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import { readTaskContext, readTaskContextDelta,
|
|
1
|
+
import { applyGlobalInputControl } from "../commands/globalRoleCommands.js";
|
|
2
|
+
import { applyTaskInputControl, sendTaskMessageCommand, updateTaskMetadataCommand } from "../commands/taskCommands.js";
|
|
3
|
+
import { runTaskInputCommand } from "../commands/taskInputCommands.js";
|
|
4
|
+
import { inspectTaskContext, readTaskContext, readTaskContextDelta, withContextObservations } from "../context/taskContext.js";
|
|
5
5
|
import { BUILTIN_CAPABILITIES } from "../kernel/builtinCapabilities.js";
|
|
6
6
|
import { capabilitySchemaError } from "../kernel/capabilitySchema.js";
|
|
7
|
-
import {
|
|
7
|
+
import { recordGlobalInterruptResult, recordGlobalSteerResult } from "../message/globalInterrupt.js";
|
|
8
|
+
import { recordTaskInterruptResult } from "../message/taskInterrupt.js";
|
|
9
|
+
import { AGENT_HOST_CONTROL_PROTOCOL, foldInterruptLiveReceipt, foldSteerLiveReceipt, sendAgentHostCancelControl, sendAgentHostSteerControl } from "../runtime/agentHost.js";
|
|
8
10
|
import { webLocalMutation, WebRequestRejected } from "./webMutation.js";
|
|
9
|
-
import { runTaskInputCommand } from "../commands/taskInputCommands.js";
|
|
10
|
-
import { sendAgentHostSteerControl, sendAgentHostCancelControl, AGENT_HOST_CONTROL_PROTOCOL, foldSteerLiveReceipt, foldInterruptLiveReceipt } from "../runtime/agentHost.js";
|
|
11
11
|
const DEFAULT_WEB_HOST_CONTROL = {
|
|
12
12
|
steer: sendAgentHostSteerControl,
|
|
13
13
|
cancel: sendAgentHostCancelControl
|
|
14
14
|
};
|
|
15
|
-
/** The store-only CLI argv for the shared application-layer primitive
|
|
16
|
-
* (decision-3 §7). The Web surface never re-implements the queue/steer/interrupt
|
|
17
|
-
* decisions; it drives the exact same command the CLI drives. */
|
|
18
|
-
function controlArgv(taskId, input) {
|
|
19
|
-
if (input.action === "queue") {
|
|
20
|
-
return ["message", "queue", taskId, input.body, "--request-id", input.requestId,
|
|
21
|
-
...(input.to === undefined ? [] : ["--to", input.to]),
|
|
22
|
-
...(input.workItem === undefined ? [] : ["--work-item", input.workItem]),
|
|
23
|
-
...(input.reviewRound === undefined ? [] : ["--review-round", input.reviewRound])];
|
|
24
|
-
}
|
|
25
|
-
if (input.action === "steer") {
|
|
26
|
-
return ["message", "steer", taskId, input.body, "--request-id", input.requestId,
|
|
27
|
-
"--expected-target", input.expectedTarget, "--to", input.to,
|
|
28
|
-
...(input.workItem === undefined ? [] : ["--work-item", input.workItem]),
|
|
29
|
-
...(input.reviewRound === undefined ? [] : ["--review-round", input.reviewRound])];
|
|
30
|
-
}
|
|
31
|
-
return ["role", "interrupt", taskId, input.role, "--expected-target", input.expectedTarget,
|
|
32
|
-
...(input.thenMessage === undefined ? [] : ["--then-message", input.thenMessage]),
|
|
33
|
-
"--request-id", input.requestId];
|
|
34
|
-
}
|
|
35
15
|
/** A queue is delivered to the Leader mailbox only when it is unaddressed or
|
|
36
16
|
* addressed to the Leader; an addressed Worker/Reviewer queue goes to the Task
|
|
37
17
|
* mailbox. A steer/interrupt is a live control op, so it only reconciles the
|
|
@@ -53,12 +33,8 @@ export function createWebTaskSurface(store, options = {}, observations = [], hos
|
|
|
53
33
|
// Notifications are after the outer transaction. Their failure must not be
|
|
54
34
|
// reclassified as a rejection of a mutation that already committed.
|
|
55
35
|
const notify = (taskId, leader = false) => {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
? { kind: "role", taskId, roleName: "leader" } : { kind: "task", taskId });
|
|
59
|
-
}
|
|
60
|
-
else
|
|
61
|
-
options.runtime?.notifyStateChanged(taskId);
|
|
36
|
+
void options.runtime?.notifyMailboxChanged(leader
|
|
37
|
+
? { kind: "role", taskId, roleName: "leader" } : { kind: "task", taskId });
|
|
62
38
|
};
|
|
63
39
|
return {
|
|
64
40
|
globalState: (roleName) => {
|
|
@@ -79,20 +55,15 @@ export function createWebTaskSurface(store, options = {}, observations = [], hos
|
|
|
79
55
|
};
|
|
80
56
|
},
|
|
81
57
|
globalControl: async (roleName, input) => {
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
...(input.thenMessage === undefined ? [] : ["--then-message", input.thenMessage])]
|
|
85
|
-
: ["message", input.action, roleName, input.body, "--request-id", input.requestId,
|
|
86
|
-
...(input.action === "steer" ? ["--expected-target", input.expectedTarget] : [])];
|
|
87
|
-
const result = webLocalMutation(store, tx => runGlobalRoleCommand(argv, tx, {
|
|
88
|
-
env: {}, yuiHome: options.yuiHome, jsonOutput: true
|
|
58
|
+
const result = webLocalMutation(store, tx => applyGlobalInputControl(roleName, input, tx, {
|
|
59
|
+
env: {}, yuiHome: options.yuiHome
|
|
89
60
|
}));
|
|
90
|
-
if (
|
|
61
|
+
if (result.kind === "output") {
|
|
91
62
|
if (input.action === "queue")
|
|
92
|
-
void options.runtime?.notifyMailboxChanged
|
|
63
|
+
void options.runtime?.notifyMailboxChanged({
|
|
93
64
|
kind: "global-role-runtime", roleName
|
|
94
65
|
});
|
|
95
|
-
return { action: input.action, ...
|
|
66
|
+
return { action: input.action, ...result.data };
|
|
96
67
|
}
|
|
97
68
|
if (result.kind !== "input-steer" && result.kind !== "input-interrupt") {
|
|
98
69
|
throw new WebRequestRejected("Global input cannot perform a Session lifecycle operation.");
|
|
@@ -138,7 +109,7 @@ export function createWebTaskSurface(store, options = {}, observations = [], hos
|
|
|
138
109
|
message: (taskId, body, intent, requestId) => {
|
|
139
110
|
// Preserve the submission's intent and frozen receipt separately from
|
|
140
111
|
// queue/steer controls; an omitted intent still means discuss.
|
|
141
|
-
const { message, task, queuedForLeader, feedback } = webLocalMutation(store, (tx) => sendTaskMessageCommand(tx, taskId, body,
|
|
112
|
+
const { message, task, queuedForLeader, feedback } = webLocalMutation(store, (tx) => sendTaskMessageCommand(tx, taskId, body, commandOptions, undefined, intent, requestId));
|
|
142
113
|
notify(taskId, queuedForLeader);
|
|
143
114
|
return { record: message, revision: message.createdAt,
|
|
144
115
|
disposition: queuedForLeader ? "queued" : "saved",
|
|
@@ -149,7 +120,7 @@ export function createWebTaskSurface(store, options = {}, observations = [], hos
|
|
|
149
120
|
/**
|
|
150
121
|
* The decision-3 three-action input-control path for the local-user Web
|
|
151
122
|
* surface. Its store-only phase is the identical shared application-layer
|
|
152
|
-
* primitive the CLI uses
|
|
123
|
+
* typed primitive the CLI uses, run inside `webLocalMutation`
|
|
153
124
|
* so a rejected input is provably not-submitted. A ready steer/interrupt
|
|
154
125
|
* returns a live intent; the single Agent Host edge then runs OUTSIDE the
|
|
155
126
|
* transaction exactly as cli.ts performs it — never a fallback, retarget, or
|
|
@@ -158,7 +129,7 @@ export function createWebTaskSurface(store, options = {}, observations = [], hos
|
|
|
158
129
|
* the durable Message is retained (decision-3 §1/§3/§5, message-5 gap F).
|
|
159
130
|
*/
|
|
160
131
|
control: async (taskId, input) => {
|
|
161
|
-
const execution = webLocalMutation(store, (tx) =>
|
|
132
|
+
const execution = webLocalMutation(store, (tx) => applyTaskInputControl(taskId, input, tx, commandOptions));
|
|
162
133
|
if (execution.kind === "output") {
|
|
163
134
|
// A queue receipt, or a steer/interrupt that was saved-but-not-delivered
|
|
164
135
|
// or an idempotent replay: fully durable, no live edge, exact disposition.
|
|
@@ -319,8 +319,7 @@ export function validateWorkItem(workItem) {
|
|
|
319
319
|
"updatedAt",
|
|
320
320
|
"endedAt",
|
|
321
321
|
"acceptedCandidateId",
|
|
322
|
-
"currentCandidateId"
|
|
323
|
-
"historicalState"
|
|
322
|
+
"currentCandidateId"
|
|
324
323
|
], "WorkItem");
|
|
325
324
|
if (workItem.schemaVersion !== 15)
|
|
326
325
|
throw new Error("WorkItem must use schemaVersion 15.");
|
|
@@ -397,16 +396,6 @@ export function validateWorkItem(workItem) {
|
|
|
397
396
|
if (workItem.currentCandidateId !== undefined
|
|
398
397
|
&& !candidateIds.has(workItem.currentCandidateId))
|
|
399
398
|
throw new Error("Current Candidate is missing.");
|
|
400
|
-
if (workItem.historicalState !== undefined) {
|
|
401
|
-
const historical = workItem.historicalState;
|
|
402
|
-
if (!["pending", "running", "awaiting_acceptance", "completed", "failed", "retired"].includes(historical.status)) {
|
|
403
|
-
throw new Error("Historical Work Item status is invalid.");
|
|
404
|
-
}
|
|
405
|
-
if (historical.outcome !== undefined)
|
|
406
|
-
requireText(historical.outcome, "Historical outcome");
|
|
407
|
-
if (historical.endedAt !== undefined)
|
|
408
|
-
requireTimestamp(historical.endedAt, "Historical endedAt");
|
|
409
|
-
}
|
|
410
399
|
if (workItem.outcome !== undefined)
|
|
411
400
|
requireText(workItem.outcome, "Work item outcome");
|
|
412
401
|
requireTimestamp(workItem.createdAt, "Work Item createdAt");
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { isAbsolute, join, relative } from "node:path";
|
|
2
|
+
export class CleanupInspectionError extends Error {
|
|
3
|
+
checks;
|
|
4
|
+
constructor(checks) {
|
|
5
|
+
super(checks.map(renderCleanupCheck).join("\n"));
|
|
6
|
+
this.checks = checks;
|
|
7
|
+
this.name = "CleanupInspectionError";
|
|
8
|
+
}
|
|
9
|
+
}
|
|
10
|
+
export function renderCleanupCheck(check) {
|
|
11
|
+
return `[${check.reason}] ${check.resource}: ${check.detail}`
|
|
12
|
+
+ ` Expected=${JSON.stringify(check.expected)}; observed=${JSON.stringify(check.observed)}.`
|
|
13
|
+
+ (check.actions.length === 0 ? "" : ` Inspect/resolve: ${check.actions.join("; ")}.`);
|
|
14
|
+
}
|
|
15
|
+
/** Do not forward arbitrary Git stderr, native arguments, or foreign paths. */
|
|
16
|
+
export function cleanupCheckFromError(error, resource, sources, actions) {
|
|
17
|
+
if (error instanceof CleanupInspectionError) {
|
|
18
|
+
return error.checks.map(check => ({ ...check, resource, sources, actions }));
|
|
19
|
+
}
|
|
20
|
+
let cause = error;
|
|
21
|
+
let errorCode;
|
|
22
|
+
for (let depth = 0; depth < 4 && cause instanceof Error; depth += 1) {
|
|
23
|
+
const code = cause.code;
|
|
24
|
+
if ((typeof code === "string" && /^[A-Z][A-Z0-9_]{0,63}$/.test(code))
|
|
25
|
+
|| (typeof code === "number" && Number.isSafeInteger(code))) {
|
|
26
|
+
errorCode = code;
|
|
27
|
+
break;
|
|
28
|
+
}
|
|
29
|
+
cause = cause.cause;
|
|
30
|
+
}
|
|
31
|
+
return [{ resource, reason: "inspection-unavailable", status: "unknown",
|
|
32
|
+
detail: "The resource could not be inspected; no safe cleanup conclusion is available.",
|
|
33
|
+
expected: "readable owned resource", observed: errorCode === undefined ? "unavailable" : { errorCode }, sources, actions }];
|
|
34
|
+
}
|
|
35
|
+
export function cleanupFailure(reason, detail, expected, observed, status = "blocked") {
|
|
36
|
+
throw new CleanupInspectionError([{ resource: "git-workspace", reason, status,
|
|
37
|
+
detail, expected, observed, sources: [], actions: [] }]);
|
|
38
|
+
}
|
|
39
|
+
/** Describe exact differences without disclosing paths outside this Task.
|
|
40
|
+
* Legacy locations are displayed only for this Task's exact branch identity;
|
|
41
|
+
* displaying a historical location is not accepting it as migration proof.
|
|
42
|
+
*/
|
|
43
|
+
export function workspacePathValue(workspace, path) {
|
|
44
|
+
const inside = (root) => {
|
|
45
|
+
const value = relative(root, path);
|
|
46
|
+
return value !== ".." && !value.startsWith("../") && !isAbsolute(value) ? value || "." : undefined;
|
|
47
|
+
};
|
|
48
|
+
const marker = `/workspaces/tasks/${workspace.owner.taskId}/`;
|
|
49
|
+
const index = workspace.root.lastIndexOf(marker);
|
|
50
|
+
if (index >= 0) {
|
|
51
|
+
const home = workspace.root.slice(0, index);
|
|
52
|
+
const taskPath = inside(join(home, "workspaces", "tasks", workspace.owner.taskId));
|
|
53
|
+
if (taskPath !== undefined)
|
|
54
|
+
return `<task>/${taskPath}`;
|
|
55
|
+
const legacyPath = inside(join(home, "workspaces", "worktree"));
|
|
56
|
+
const parts = legacyPath?.split("/");
|
|
57
|
+
const taskSegment = workspace.entries.find(e => e.access === "write")?.branch.split("/")[1];
|
|
58
|
+
if (parts?.length === 3 && parts[1] === taskSegment)
|
|
59
|
+
return `<legacy-worktree>/${legacyPath}`;
|
|
60
|
+
}
|
|
61
|
+
const local = inside(workspace.root);
|
|
62
|
+
return local === undefined ? "[outside authorized Task paths]" : `<owner>/${local}`;
|
|
63
|
+
}
|