@zq-silk/yui 0.15.12 → 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 +3 -2
- package/ARCHITECTURE.zh-CN.md +3 -2
- 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 +57 -41
- 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 +7 -3
- package/dist/cli/invocationAuthority.js +64 -0
- package/dist/cli/managedDiagnostics.js +1 -1
- package/dist/cli/parseRepeatable.js +35 -0
- package/dist/cli/updatePorts.js +26 -5
- package/dist/cli.js +1280 -1243
- 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/globalRoleCommands.js +53 -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 +1 -10
- 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 +271 -710
- 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 +17 -57
- package/dist/commands/taskNextActionCommand.js +7 -6
- package/dist/commands/taskPublicationAdoptCommand.js +3 -3
- package/dist/commands/taskPublicationCommands.js +1 -1
- package/dist/commands/taskPublicationVerifyCommand.js +1 -1
- package/dist/commands/taskRemoteDeliveryCommand.js +2 -94
- 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 +2 -3
- package/dist/context/taskCatalog.js +3 -5
- package/dist/context/taskContext.js +139 -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 +9 -22
- package/dist/controller/fileSchedulerStoreAdapter.js +117 -212
- package/dist/controller/jobClient.js +4 -4
- package/dist/controller/jobControl.js +60 -25
- package/dist/controller/jobSupervisor.js +4 -4
- package/dist/controller/providerRetryDelivery.js +7 -7
- package/dist/controller/runtime.js +51 -44
- 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 +60 -84
- 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 +1 -9
- 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/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/projectMaintenanceLock.js +23 -64
- package/dist/repository/taskBaseFreshness.js +1 -1
- package/dist/repository/taskWorkspaceCoordinator.js +13 -22
- package/dist/repository/taskWorkspacePreparer.js +29 -25
- package/dist/repository/workspaceCleanupInspection.js +5 -5
- 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/agentFailureContext.js +43 -0
- package/dist/runtime/agentHost.js +15 -15
- package/dist/runtime/codexInteractiveHost.js +2 -2
- package/dist/runtime/index.js +0 -1
- package/dist/runtime/managedCaller.js +21 -1
- package/dist/runtime/providerErrors.js +38 -0
- package/dist/runtime/runtimeObservation.js +0 -5
- package/dist/runtime/runtimeSessionCandidate.js +2 -3
- package/dist/runtime/sessionOwnerIdentity.js +1 -1
- package/dist/runtime/structuredProviderHost.js +4 -41
- package/dist/runtime/tmuxAdapters.js +6 -8
- package/dist/scheduler/activeRoleRunDelivery.js +7 -7
- package/dist/scheduler/leaderWakeupProcessor.js +19 -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 +25 -33
- package/dist/scheduler/taskObservabilityProjection.js +0 -1
- 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 +142 -1
- package/dist/storage/sqliteStore.js +156 -162
- package/dist/storage/storageSchema.js +2 -14
- package/dist/storage/storageVersions.js +1 -1
- package/dist/storage/storeRpc.js +15 -7
- package/dist/storage/taskStore.js +3 -7
- package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
- package/dist/task/completionReadiness.js +5 -24
- package/dist/task/draftPlan.js +0 -53
- package/dist/task/nextAction.js +7 -13
- 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 +1 -1
- package/dist/web/assets/client/taskSurface.js +4 -4
- package/dist/web/webServer.js +4 -6
- package/dist/web/webSnapshot.js +3 -83
- package/dist/web/webTaskSurface.js +17 -46
- package/dist/workItem/workItem.js +1 -12
- package/docs/architecture/capabilities-and-resources.md +14 -2
- package/docs/architecture/capabilities-and-resources.zh-CN.md +11 -1
- package/docs/managed-turn-and-session-runtime.md +83 -10
- package/docs/managed-turn-and-session-runtime.zh-CN.md +65 -11
- package/docs/plugin-sdk.md +4 -2
- 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 +91 -5
- package/docs/task-delivery.zh-CN.md +72 -4
- package/docs/task-discovery.md +10 -11
- package/docs/task-discovery.zh-CN.md +8 -9
- package/docs/testing/verification-levels.md +145 -9
- package/docs/testing/verification-levels.zh-CN.md +103 -8
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +5 -0
- package/skills/yui-leader/references/integration.md +9 -5
- package/skills/yui-leader/references/planning.md +9 -3
- package/skills/yui-operator/SKILL.md +15 -10
- package/skills/yui-runtime/SKILL.md +6 -6
- package/skills/yui-runtime/references/recovery.md +47 -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 -377
- 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
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
import { resolveLeaderNextActionMode, resolveLeaderSemanticBudgetRuns } from "../config/yuiConfig.js";
|
|
2
|
-
import { usageError } from "../errors/cliError.js";
|
|
3
|
-
import { detectDeliveryDuplicates, evaluateDeliveryGuard, evaluateSemanticBudget, formatDeliveryDuplicate } from "../task/deliveryGuard.js";
|
|
4
|
-
export function runDeliveryGuardPreflight(store, taskId, intent, options = {}) {
|
|
5
|
-
const mode = resolveLeaderNextActionMode(store.getConfig().leaderNextActionMode);
|
|
6
|
-
if (mode === "display")
|
|
7
|
-
return { warnings: [] };
|
|
8
|
-
const facts = store.readNextActionFacts(taskId);
|
|
9
|
-
if (facts === null)
|
|
10
|
-
return { warnings: [] };
|
|
11
|
-
const outcome = evaluateDeliveryGuard(detectDeliveryDuplicates(facts, intent), mode);
|
|
12
|
-
const warnings = outcome.warnings.map(formatDeliveryDuplicate);
|
|
13
|
-
if (outcome.blocked !== null) {
|
|
14
|
-
throw usageError(`${formatDeliveryDuplicate(outcome.blocked)} `
|
|
15
|
-
+ "Reuse the existing proof, or set leader.nextActionMode=warn to proceed with a warning.");
|
|
16
|
-
}
|
|
17
|
-
if (options.budget === true) {
|
|
18
|
-
const budget = evaluateSemanticBudget(facts, resolveLeaderSemanticBudgetRuns(store.getConfig().leaderSemanticBudgetRuns));
|
|
19
|
-
if (budget.exhausted) {
|
|
20
|
-
warnings.push(`Semantic progress budget: ${budget.reason}`);
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return { warnings };
|
|
24
|
-
}
|
|
25
|
-
/** Prepend guard warnings to a command's normal output. */
|
|
26
|
-
export function withGuardWarnings(preflight, output) {
|
|
27
|
-
if (preflight.warnings.length === 0)
|
|
28
|
-
return output;
|
|
29
|
-
return `${preflight.warnings.map((warning) => `Warning: ${warning}\n`).join("")}${output}`;
|
|
30
|
-
}
|
|
@@ -1,226 +0,0 @@
|
|
|
1
|
-
import { usageError } from "../errors/cliError.js";
|
|
2
|
-
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
3
|
-
import { resolveProject } from "../repository/project.js";
|
|
4
|
-
import { NodeGitWorkspace } from "../repository/gitWorkspace.js";
|
|
5
|
-
import { enqueueIntegrationQueueEntry, processIntegrationQueue, reconcileIntegrationQueueEntry, requeueIntegrationQueueEntry, supersedeIntegrationQueueEntry } from "../integration/integrationQueueService.js";
|
|
6
|
-
import { assertTaskDeliveryAuthority as taskLocalActor } from "./taskActor.js";
|
|
7
|
-
import { parseRepeatable } from "./taskIntegrationCommands.js";
|
|
8
|
-
import { resolveTaskRecordReference } from "../task/taskRecordReference.js";
|
|
9
|
-
/**
|
|
10
|
-
* `yui task integration queue` — the serialized merge queue. Only terminal
|
|
11
|
-
* Candidates explicitly enqueued enter the queue; the queue never blocks
|
|
12
|
-
* parallel development, it orders exact-commit integration.
|
|
13
|
-
*/
|
|
14
|
-
export async function runTaskIntegrationQueueCommand(args, store, home, options = {}) {
|
|
15
|
-
const [command, ...rest] = args;
|
|
16
|
-
if (command === "enqueue")
|
|
17
|
-
return enqueue(rest, store, options, home);
|
|
18
|
-
if (command === "list")
|
|
19
|
-
return list(rest, store);
|
|
20
|
-
if (command === "show")
|
|
21
|
-
return show(rest, store, options.environment);
|
|
22
|
-
if (command === "process")
|
|
23
|
-
return process(rest, store, home, options);
|
|
24
|
-
if (command === "supersede")
|
|
25
|
-
return supersede(rest, store, options, home);
|
|
26
|
-
if (command === "requeue")
|
|
27
|
-
return requeue(rest, store, options, home);
|
|
28
|
-
if (command === "reconcile")
|
|
29
|
-
return reconcile(rest, store, options, home);
|
|
30
|
-
throw usageError(command === undefined
|
|
31
|
-
? "Task Integration queue command is required."
|
|
32
|
-
: `Unknown command: task integration queue ${command}`);
|
|
33
|
-
}
|
|
34
|
-
async function enqueue(args, store, options, home) {
|
|
35
|
-
const usage = "Task Integration queue enqueue usage: yui task integration queue enqueue <task> --project <project> --change-set <id> [--target <ref>] [--check <command> ...].";
|
|
36
|
-
const parsed = parseRepeatable(args, new Set(["--check"]), new Set(["--project", "--change-set", "--target"]), usage);
|
|
37
|
-
if (parsed.positionals.length !== 1)
|
|
38
|
-
throw usageError(usage);
|
|
39
|
-
const task = requireActiveTask(store, parsed.positionals[0]);
|
|
40
|
-
requireTaskControlActor(store, options.environment, task.id, home);
|
|
41
|
-
const projectRef = parsed.one.get("--project");
|
|
42
|
-
const changeSetId = parsed.one.get("--change-set");
|
|
43
|
-
if (projectRef === undefined || changeSetId === undefined)
|
|
44
|
-
throw usageError(usage);
|
|
45
|
-
const projectId = resolveProjectId(store, projectRef);
|
|
46
|
-
const result = await enqueueIntegrationQueueEntry({
|
|
47
|
-
store,
|
|
48
|
-
taskId: task.id,
|
|
49
|
-
projectId,
|
|
50
|
-
changeSetId,
|
|
51
|
-
targetRef: parsed.one.get("--target"),
|
|
52
|
-
checkCommands: parsed.many.get("--check") ?? [],
|
|
53
|
-
now: options.now
|
|
54
|
-
});
|
|
55
|
-
const label = {
|
|
56
|
-
queued: "Enqueued",
|
|
57
|
-
"already-queued": "Already queued",
|
|
58
|
-
"already-committed": "Already committed",
|
|
59
|
-
converged: "Converged"
|
|
60
|
-
}[result.outcome];
|
|
61
|
-
return {
|
|
62
|
-
output: `${label} ${result.entry.changeSetId} as ${result.entry.id} (${result.entry.status})\n`,
|
|
63
|
-
data: result
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
function list(args, store) {
|
|
67
|
-
const usage = "Task Integration queue list usage: yui task integration queue list <task> [--project <project>].";
|
|
68
|
-
const parsed = parseRepeatable(args, new Set(), new Set(["--project"]), usage);
|
|
69
|
-
if (parsed.positionals.length !== 1)
|
|
70
|
-
throw usageError(usage);
|
|
71
|
-
const task = requireActiveTask(store, parsed.positionals[0]);
|
|
72
|
-
const projectRef = parsed.one.get("--project");
|
|
73
|
-
const projectId = projectRef === undefined ? undefined : resolveProjectId(store, projectRef);
|
|
74
|
-
// The store already returns entries in numeric id order; trust it instead of
|
|
75
|
-
// re-sorting with a lexicographic compare (which yields 1, 10, 2, ...).
|
|
76
|
-
const entries = store.listIntegrationQueueEntries(task.id)
|
|
77
|
-
.filter((entry) => projectId === undefined || entry.projectId === projectId);
|
|
78
|
-
const output = entries.length === 0
|
|
79
|
-
? "Integration queue is empty.\n"
|
|
80
|
-
: `${renderTable(`Integration queue: ${task.id}`, [
|
|
81
|
-
{ header: "Entry", minWidth: 12, maxWidth: 24 },
|
|
82
|
-
{ header: "Project", minWidth: 8, maxWidth: 20 },
|
|
83
|
-
{ header: "ChangeSet", minWidth: 10, maxWidth: 20 },
|
|
84
|
-
{ header: "Target", minWidth: 8, maxWidth: 30 },
|
|
85
|
-
{ header: "Status", minWidth: 8, maxWidth: 16 }
|
|
86
|
-
], entries.map((entry) => [
|
|
87
|
-
entry.id,
|
|
88
|
-
entry.projectId,
|
|
89
|
-
entry.changeSetId,
|
|
90
|
-
entry.targetRef,
|
|
91
|
-
entry.status
|
|
92
|
-
]), defaultTableWidth())}\n`;
|
|
93
|
-
return { output, data: { entries } };
|
|
94
|
-
}
|
|
95
|
-
function show(args, store, environment) {
|
|
96
|
-
const usage = "Task Integration queue show usage: yui task integration queue show <task>/<entry>.";
|
|
97
|
-
if (args.length !== 1)
|
|
98
|
-
throw usageError(usage);
|
|
99
|
-
const entry = requireQueueEntry(store, args[0], environment);
|
|
100
|
-
const lines = [
|
|
101
|
-
`Integration queue entry: ${entry.id}`,
|
|
102
|
-
`Task: ${entry.taskId}`,
|
|
103
|
-
`Project: ${entry.projectId}`,
|
|
104
|
-
`ChangeSet: ${entry.changeSetId}`,
|
|
105
|
-
`Target: ${entry.targetRef}`,
|
|
106
|
-
`Status: ${entry.status}`,
|
|
107
|
-
`Target before: ${entry.targetBefore ?? "-"}`,
|
|
108
|
-
`Target after: ${entry.targetAfter ?? "-"}`,
|
|
109
|
-
`Integration: ${entry.integrationAttemptId ?? "-"}`,
|
|
110
|
-
`Affected paths: ${entry.affectedPaths === undefined ? "-" : entry.affectedPaths.join(", ")}`,
|
|
111
|
-
`Evidence: ${entry.evidenceRefs.length === 0 ? "-" : entry.evidenceRefs.join(", ")}`,
|
|
112
|
-
`Conflict: ${entry.conflictSummary ?? "-"}`,
|
|
113
|
-
`Superseded: ${entry.supersedeReason ?? "-"}`
|
|
114
|
-
];
|
|
115
|
-
return { output: `${lines.join("\n")}\n`, data: { entry } };
|
|
116
|
-
}
|
|
117
|
-
async function process(args, store, home, options) {
|
|
118
|
-
const usage = "Task Integration queue process usage: yui task integration queue process <task> [--project <project>] [--limit <n>].";
|
|
119
|
-
const parsed = parseRepeatable(args, new Set(), new Set(["--project", "--limit"]), usage);
|
|
120
|
-
if (parsed.positionals.length !== 1)
|
|
121
|
-
throw usageError(usage);
|
|
122
|
-
const task = requireActiveTask(store, parsed.positionals[0]);
|
|
123
|
-
requireTaskControlActor(store, options.environment, task.id, home);
|
|
124
|
-
const limitValue = parsed.one.get("--limit");
|
|
125
|
-
const limit = limitValue === undefined ? undefined : Number.parseInt(limitValue, 10);
|
|
126
|
-
if (limitValue !== undefined && (!Number.isSafeInteger(limit) || limit < 1)) {
|
|
127
|
-
throw usageError(`--limit must be a positive integer: ${limitValue}.`);
|
|
128
|
-
}
|
|
129
|
-
const projectRef = parsed.one.get("--project");
|
|
130
|
-
const projectId = projectRef === undefined ? undefined : resolveProjectId(store, projectRef);
|
|
131
|
-
const processed = await processIntegrationQueue(store, home, task.id, {
|
|
132
|
-
projectId,
|
|
133
|
-
limit,
|
|
134
|
-
now: options.now,
|
|
135
|
-
environment: options.environment
|
|
136
|
-
});
|
|
137
|
-
const lines = processed.map(({ entry, result }) => {
|
|
138
|
-
if (result.status === "committed") {
|
|
139
|
-
return `${entry.id} committed ${entry.changeSetId} -> ${entry.targetAfter ?? "-"}`;
|
|
140
|
-
}
|
|
141
|
-
if (result.status === "blocked" || result.status === "conflicted") {
|
|
142
|
-
return `${entry.id} conflicted ${entry.changeSetId}: ${entry.conflictSummary ?? "blocked"}`;
|
|
143
|
-
}
|
|
144
|
-
return `${entry.id} conflicted ${entry.changeSetId}: ${entry.conflictSummary ?? "gate failure"}`;
|
|
145
|
-
});
|
|
146
|
-
const output = processed.length === 0
|
|
147
|
-
? "Integration queue: nothing to process.\n"
|
|
148
|
-
: `${lines.join("\n")}\n`;
|
|
149
|
-
return { output, data: { processed } };
|
|
150
|
-
}
|
|
151
|
-
function supersede(args, store, options, home) {
|
|
152
|
-
const usage = "Task Integration queue supersede usage: yui task integration queue supersede <task>/<entry> --reason <text>.";
|
|
153
|
-
const parsed = parseRepeatable(args, new Set(), new Set(["--reason"]), usage);
|
|
154
|
-
if (parsed.positionals.length !== 1)
|
|
155
|
-
throw usageError(usage);
|
|
156
|
-
const entry = requireQueueEntry(store, parsed.positionals[0], options.environment);
|
|
157
|
-
const actor = requireTaskControlActor(store, options.environment, entry.taskId, home);
|
|
158
|
-
const reason = parsed.one.get("--reason");
|
|
159
|
-
if (reason === undefined)
|
|
160
|
-
throw usageError(usage);
|
|
161
|
-
const superseded = supersedeIntegrationQueueEntry(store, entry.taskId, entry.id, reason, actor, options.now ?? (() => new Date()));
|
|
162
|
-
return {
|
|
163
|
-
output: `Superseded ${superseded.id}: ${reason}\n`,
|
|
164
|
-
data: { entry: superseded }
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
function requeue(args, store, options, home) {
|
|
168
|
-
const usage = "Task Integration queue requeue usage: yui task integration queue requeue <task>/<entry>.";
|
|
169
|
-
if (args.length !== 1)
|
|
170
|
-
throw usageError(usage);
|
|
171
|
-
const entry = requireQueueEntry(store, args[0], options.environment);
|
|
172
|
-
const actor = requireTaskControlActor(store, options.environment, entry.taskId, home);
|
|
173
|
-
const waiting = requeueIntegrationQueueEntry(store, entry.taskId, entry.id, actor, options.now ?? (() => new Date()));
|
|
174
|
-
return {
|
|
175
|
-
output: `Requeued ${waiting.id}; it will be processed again on the next queue run\n`,
|
|
176
|
-
data: { entry: waiting }
|
|
177
|
-
};
|
|
178
|
-
}
|
|
179
|
-
async function reconcile(args, store, options, home) {
|
|
180
|
-
const usage = "Task Integration queue reconcile usage: yui task integration queue reconcile <task>/<entry>.";
|
|
181
|
-
if (args.length !== 1)
|
|
182
|
-
throw usageError(usage);
|
|
183
|
-
const entry = requireQueueEntry(store, args[0], options.environment);
|
|
184
|
-
requireTaskControlActor(store, options.environment, entry.taskId, home);
|
|
185
|
-
const committed = await reconcileIntegrationQueueEntry(store, entry.taskId, entry.id, new NodeGitWorkspace(), options.now ?? (() => new Date()));
|
|
186
|
-
return {
|
|
187
|
-
output: `Reconciled ${committed.id} as committed -> ${committed.targetAfter ?? "-"}\n`,
|
|
188
|
-
data: { entry: committed }
|
|
189
|
-
};
|
|
190
|
-
}
|
|
191
|
-
function requireActiveTask(store, taskId) {
|
|
192
|
-
const task = store.getTask(taskId);
|
|
193
|
-
if (task === null)
|
|
194
|
-
throw usageError(`Task not found: ${taskId}.`);
|
|
195
|
-
if (task.status !== "active") {
|
|
196
|
-
throw usageError(`Task is not active: ${task.id}/${task.status}.`);
|
|
197
|
-
}
|
|
198
|
-
return task;
|
|
199
|
-
}
|
|
200
|
-
function resolveProjectId(store, reference) {
|
|
201
|
-
const project = resolveProject(store.listProjects(), reference);
|
|
202
|
-
if (project === null)
|
|
203
|
-
throw usageError(`Project not found: ${reference}.`);
|
|
204
|
-
return project.id;
|
|
205
|
-
}
|
|
206
|
-
function requireQueueEntry(store, value, environment) {
|
|
207
|
-
let reference;
|
|
208
|
-
try {
|
|
209
|
-
reference = resolveTaskRecordReference(value, {
|
|
210
|
-
kind: "integrationQueue",
|
|
211
|
-
contextTaskId: environment?.YUI_TASK_ID,
|
|
212
|
-
label: "Integration queue entry"
|
|
213
|
-
});
|
|
214
|
-
}
|
|
215
|
-
catch (error) {
|
|
216
|
-
throw usageError(error instanceof Error ? error.message : String(error));
|
|
217
|
-
}
|
|
218
|
-
const entry = store.getIntegrationQueueEntry(reference.taskId, reference.localId);
|
|
219
|
-
if (entry === null) {
|
|
220
|
-
throw usageError(`Integration queue entry not found: ${reference.taskId}/${reference.localId}.`);
|
|
221
|
-
}
|
|
222
|
-
return entry;
|
|
223
|
-
}
|
|
224
|
-
function requireTaskControlActor(store, environment, taskId, home) {
|
|
225
|
-
return taskLocalActor(store, environment, taskId);
|
|
226
|
-
}
|
|
@@ -1,377 +0,0 @@
|
|
|
1
|
-
import { usageError } from "../errors/cliError.js";
|
|
2
|
-
import { isRoleRunStalled, latestStallProgressAt } from "../scheduler/roleRunStall.js";
|
|
3
|
-
import { formatTimestamp } from "../output/timePresentation.js";
|
|
4
|
-
import { pendingWakeupProjection } from "../storage/taskStore.js";
|
|
5
|
-
import { defaultTableWidth, renderTable } from "../output/table.js";
|
|
6
|
-
import { projectTaskExecutionFromFacts } from "../scheduler/taskExecutionProjection.js";
|
|
7
|
-
import { resolveRuntimeHealth } from "../config/yuiConfig.js";
|
|
8
|
-
import { projectNextAction } from "../task/nextAction.js";
|
|
9
|
-
import { projectTaskRemoteDeliveryFromStore } from "./taskRemoteDeliveryCommand.js";
|
|
10
|
-
import { formatUsageMetric } from "../runtime/taskUsageMetrics.js";
|
|
11
|
-
export function parseTaskListOptions(args) {
|
|
12
|
-
const allowed = new Set(["--all", "--verbose"]);
|
|
13
|
-
if (args.some((argument) => !allowed.has(argument))
|
|
14
|
-
|| new Set(args).size !== args.length) {
|
|
15
|
-
throw usageError("Task list usage: yui task list [--all] [--verbose].");
|
|
16
|
-
}
|
|
17
|
-
return {
|
|
18
|
-
all: args.includes("--all"),
|
|
19
|
-
verbose: args.includes("--verbose")
|
|
20
|
-
};
|
|
21
|
-
}
|
|
22
|
-
export function buildTaskOverview(store, options, now = new Date(), taskId) {
|
|
23
|
-
const runtimeHealthPolicy = resolveRuntimeHealth(store.getConfig().runtimeHealth);
|
|
24
|
-
const tasks = (taskId === undefined ? store.listTasks() : [store.getTask(taskId)].filter((task) => task !== null))
|
|
25
|
-
.filter((task) => options.all || task.status !== "archived")
|
|
26
|
-
.map((task) => buildTaskOverviewEntry(task, store, now, runtimeHealthPolicy));
|
|
27
|
-
return { tasks };
|
|
28
|
-
}
|
|
29
|
-
export function renderTaskOverview(result, options, timeZone, width = defaultTableWidth()) {
|
|
30
|
-
if (result.tasks.length === 0)
|
|
31
|
-
return "No tasks found.\n";
|
|
32
|
-
const title = options.all ? "Tasks (all)" : "Tasks (unarchived)";
|
|
33
|
-
const output = renderTable(title, [
|
|
34
|
-
{ header: "Task", minWidth: 8, maxWidth: 20 },
|
|
35
|
-
{ header: "Title", minWidth: 10, maxWidth: 46 },
|
|
36
|
-
{ header: "Lifecycle", minWidth: 9, maxWidth: 14 },
|
|
37
|
-
{ header: "Leader", minWidth: 12, maxWidth: 48 },
|
|
38
|
-
{ header: "Summary updated", minWidth: 16, maxWidth: 28 },
|
|
39
|
-
{ header: "Work", minWidth: 8, maxWidth: 38 },
|
|
40
|
-
{ header: "Blockers", minWidth: 9, maxWidth: 42 },
|
|
41
|
-
{ header: "Execution", minWidth: 14, maxWidth: 34 },
|
|
42
|
-
{ header: "Delivery", minWidth: 12, maxWidth: 28 },
|
|
43
|
-
{ header: "Next action / owner", minWidth: 16, maxWidth: 42 }
|
|
44
|
-
], result.tasks.map((task) => [
|
|
45
|
-
task.id,
|
|
46
|
-
task.title,
|
|
47
|
-
task.status,
|
|
48
|
-
leaderCell(task),
|
|
49
|
-
task.summaryUpdatedAt === null
|
|
50
|
-
? "missing"
|
|
51
|
-
: formatTimestamp(task.summaryUpdatedAt, timeZone),
|
|
52
|
-
workCell(task.work.counts),
|
|
53
|
-
blockersCell(task.blockers),
|
|
54
|
-
`${task.execution.status} / ${task.execution.owner}`,
|
|
55
|
-
deliveryCell(task.remoteDelivery),
|
|
56
|
-
nextCell(task.next)
|
|
57
|
-
]), width);
|
|
58
|
-
if (!options.verbose)
|
|
59
|
-
return `${output}\n`;
|
|
60
|
-
return `${output}\n\n${renderVerboseDetails(result.tasks, timeZone)}\n`;
|
|
61
|
-
}
|
|
62
|
-
function buildTaskOverviewEntry(task, store, now, runtimeHealthPolicy) {
|
|
63
|
-
const brief = store.getTaskBrief(task.id);
|
|
64
|
-
const roles = store.listRoles(task.id);
|
|
65
|
-
const leaderRole = roles.find((role) => role.name === "leader") ?? null;
|
|
66
|
-
const workItems = store.listWorkItems(task.id);
|
|
67
|
-
const inputRequests = store.listInputRequests(task.id);
|
|
68
|
-
const openInputRequests = inputRequests.filter((request) => request.status === "open");
|
|
69
|
-
const runs = store.listRuns(task.id);
|
|
70
|
-
const events = store.listEvents(task.id);
|
|
71
|
-
const leaderFailure = store.getLeaderFailure(task.id);
|
|
72
|
-
const leaderMailbox = store.getWorkMailbox({ kind: "role", taskId: task.id, roleName: "leader" });
|
|
73
|
-
const pendingWakeup = pendingWakeupProjection(leaderMailbox);
|
|
74
|
-
const reviewRounds = store.listReviewRounds(task.id);
|
|
75
|
-
const changeSets = store.listChangeSets(task.id);
|
|
76
|
-
const integrations = store.listIntegrationAttempts(task.id);
|
|
77
|
-
const roleSessions = roles.flatMap((role) => {
|
|
78
|
-
const session = store.getRoleSession(task.id, role.name);
|
|
79
|
-
return session === null ? [] : [{ roleName: role.name, ...session }];
|
|
80
|
-
});
|
|
81
|
-
const providerRuns = new Map(roles.map((role) => [role.name, store.getTaskRoleSessionSet(task.id, role.name)?.providerBinding?.run]));
|
|
82
|
-
const runDelivery = Object.fromEntries(runs.map((run) => {
|
|
83
|
-
const native = providerRuns.get(run.roleName);
|
|
84
|
-
return [run.id, native?.runId === run.id ? native.status : "unobserved"];
|
|
85
|
-
}));
|
|
86
|
-
const attention = collectAttention(task, runs, events, leaderFailure);
|
|
87
|
-
const blockers = collectBlockers(workItems, openInputRequests, attention);
|
|
88
|
-
const leader = {
|
|
89
|
-
role: "leader",
|
|
90
|
-
roleStatus: leaderRole === null
|
|
91
|
-
? "missing"
|
|
92
|
-
: providerRuns.get("leader")?.status === "accepted" ? "running"
|
|
93
|
-
: roleSessions.some((session) => session.roleName === "leader") ? "unknown" : "idle",
|
|
94
|
-
summary: brief?.leaderSummary ?? null,
|
|
95
|
-
currentFocus: brief?.currentFocus ?? null,
|
|
96
|
-
updatedAt: brief?.updatedAt ?? null,
|
|
97
|
-
updatedBy: brief?.updatedBy ?? null,
|
|
98
|
-
summaryStatus: brief === null ? "missing" : "available"
|
|
99
|
-
};
|
|
100
|
-
const counts = countWorkItems(workItems);
|
|
101
|
-
const runtimeRuns = runs
|
|
102
|
-
.filter((run) => run.status === "active")
|
|
103
|
-
.map((run) => ({
|
|
104
|
-
id: run.id,
|
|
105
|
-
roleName: run.roleName,
|
|
106
|
-
status: run.status,
|
|
107
|
-
runtime: runDelivery[run.id],
|
|
108
|
-
createdAt: run.createdAt,
|
|
109
|
-
updatedAt: run.updatedAt
|
|
110
|
-
}));
|
|
111
|
-
const runtime = {
|
|
112
|
-
activeRuns: runtimeRuns,
|
|
113
|
-
activeRunCount: runtimeRuns.length,
|
|
114
|
-
pendingDeliveryCount: runtimeRuns.filter((run) => run.runtime !== "accepted").length
|
|
115
|
-
};
|
|
116
|
-
// Fold the execution projection from the facts already read above instead of
|
|
117
|
-
// reading the store a second time for the same unchanged revision.
|
|
118
|
-
const execution = projectTaskExecutionFromFacts({
|
|
119
|
-
task,
|
|
120
|
-
roles,
|
|
121
|
-
runs,
|
|
122
|
-
runDelivery,
|
|
123
|
-
workItems,
|
|
124
|
-
inputRequests,
|
|
125
|
-
reviewRounds,
|
|
126
|
-
changeSets,
|
|
127
|
-
integrations,
|
|
128
|
-
events,
|
|
129
|
-
brief,
|
|
130
|
-
pendingWakeup,
|
|
131
|
-
leaderMailbox,
|
|
132
|
-
leaderFailure,
|
|
133
|
-
roleSessions,
|
|
134
|
-
contextSnapshots: store.listContextSnapshots(task.id),
|
|
135
|
-
now,
|
|
136
|
-
runtimeHealthPolicy
|
|
137
|
-
});
|
|
138
|
-
const nextActionFacts = store.readNextActionFacts(task.id);
|
|
139
|
-
if (nextActionFacts === null) {
|
|
140
|
-
throw new Error(`Task disappeared while building its overview: ${task.id}.`);
|
|
141
|
-
}
|
|
142
|
-
const next = overviewNextAction(task, workItems, execution, projectNextAction({
|
|
143
|
-
...nextActionFacts,
|
|
144
|
-
currentTaskReviewCandidate: null,
|
|
145
|
-
executionGroups: execution.executionGroups
|
|
146
|
-
}));
|
|
147
|
-
const persistedCandidate = task.status === "active" || task.status === "cancelled"
|
|
148
|
-
? taskRemoteDeliveryCandidate(task)
|
|
149
|
-
: null;
|
|
150
|
-
const remoteDelivery = projectTaskRemoteDeliveryFromStore(store, task, persistedCandidate);
|
|
151
|
-
return {
|
|
152
|
-
...task,
|
|
153
|
-
brief,
|
|
154
|
-
leader,
|
|
155
|
-
leaderSummary: leader.summary,
|
|
156
|
-
currentFocus: leader.currentFocus,
|
|
157
|
-
summaryUpdatedAt: leader.updatedAt,
|
|
158
|
-
summaryUpdatedBy: leader.updatedBy,
|
|
159
|
-
summaryStatus: leader.summaryStatus,
|
|
160
|
-
work: { counts, items: workItems },
|
|
161
|
-
input: { open: openInputRequests, openCount: openInputRequests.length },
|
|
162
|
-
attention,
|
|
163
|
-
attentionCount: attention.length,
|
|
164
|
-
blockers,
|
|
165
|
-
next,
|
|
166
|
-
nextAction: next?.action ?? null,
|
|
167
|
-
nextOwner: next?.owner ?? null,
|
|
168
|
-
runtime,
|
|
169
|
-
execution,
|
|
170
|
-
remoteDelivery
|
|
171
|
-
};
|
|
172
|
-
}
|
|
173
|
-
function collectAttention(task, runs, events, failure) {
|
|
174
|
-
const items = [];
|
|
175
|
-
if (failure !== null) {
|
|
176
|
-
items.push({
|
|
177
|
-
kind: "leader-recovery",
|
|
178
|
-
id: `leader-recovery:${task.id}`,
|
|
179
|
-
status: "failed",
|
|
180
|
-
owner: "operator",
|
|
181
|
-
summary: failure.message,
|
|
182
|
-
updatedAt: failure.lastFailedAt
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
for (const run of runs) {
|
|
186
|
-
if (run.roleName !== "leader"
|
|
187
|
-
|| run.status !== "active"
|
|
188
|
-
|| !isRoleRunStalled(events, run.id))
|
|
189
|
-
continue;
|
|
190
|
-
const progressAt = latestStallProgressAt(events, run.id) ?? run.updatedAt;
|
|
191
|
-
items.push({
|
|
192
|
-
kind: "leader-stalled",
|
|
193
|
-
id: `leader-stall:${run.id}:${progressAt}`,
|
|
194
|
-
status: "needs-attention",
|
|
195
|
-
owner: "operator",
|
|
196
|
-
summary: `AgentRun ${run.id} for ${run.roleName} has no durable progress after ${progressAt}.`,
|
|
197
|
-
updatedAt: progressAt,
|
|
198
|
-
runId: run.id,
|
|
199
|
-
roleName: run.roleName
|
|
200
|
-
});
|
|
201
|
-
}
|
|
202
|
-
return items.sort((left, right) => (Date.parse(left.updatedAt) - Date.parse(right.updatedAt)
|
|
203
|
-
|| left.id.localeCompare(right.id)));
|
|
204
|
-
}
|
|
205
|
-
function collectBlockers(workItems, openInputRequests, attention) {
|
|
206
|
-
const blockers = [];
|
|
207
|
-
const workById = new Map(workItems.map((item) => [item.id, item]));
|
|
208
|
-
for (const item of workItems) {
|
|
209
|
-
if ((item.status === "open" && item.currentCandidateId !== undefined)) {
|
|
210
|
-
blockers.push({
|
|
211
|
-
kind: "work",
|
|
212
|
-
type: "work",
|
|
213
|
-
id: item.id,
|
|
214
|
-
status: item.status,
|
|
215
|
-
owner: "leader",
|
|
216
|
-
action: "accept-work-item",
|
|
217
|
-
summary: `Work Item ${item.id} is awaiting Leader acceptance.`
|
|
218
|
-
});
|
|
219
|
-
continue;
|
|
220
|
-
}
|
|
221
|
-
if (item.status !== "open")
|
|
222
|
-
continue;
|
|
223
|
-
const dependencies = item.dependsOn.filter((dependency) => (workById.get(dependency)?.status !== "accepted"));
|
|
224
|
-
if (dependencies.length === 0)
|
|
225
|
-
continue;
|
|
226
|
-
blockers.push({
|
|
227
|
-
kind: "work",
|
|
228
|
-
type: "work",
|
|
229
|
-
id: item.id,
|
|
230
|
-
status: item.status,
|
|
231
|
-
owner: item.assignee ?? "leader",
|
|
232
|
-
action: "unblock-work-item",
|
|
233
|
-
summary: `Work Item ${item.id} is waiting on ${dependencies.join(", ")}.`
|
|
234
|
-
});
|
|
235
|
-
}
|
|
236
|
-
for (const request of openInputRequests) {
|
|
237
|
-
blockers.push({
|
|
238
|
-
kind: "input",
|
|
239
|
-
type: "input",
|
|
240
|
-
id: request.id,
|
|
241
|
-
status: request.status,
|
|
242
|
-
owner: "user",
|
|
243
|
-
action: "answer-input",
|
|
244
|
-
summary: request.question,
|
|
245
|
-
blockedRefs: request.blockedRefs
|
|
246
|
-
});
|
|
247
|
-
}
|
|
248
|
-
for (const item of attention) {
|
|
249
|
-
blockers.push({
|
|
250
|
-
kind: "attention",
|
|
251
|
-
type: "attention",
|
|
252
|
-
id: item.id,
|
|
253
|
-
status: item.status,
|
|
254
|
-
owner: item.owner,
|
|
255
|
-
action: item.kind === "leader-recovery"
|
|
256
|
-
? "inspect-leader-recovery"
|
|
257
|
-
: "inspect-leader-stall",
|
|
258
|
-
summary: item.summary
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
return blockers;
|
|
262
|
-
}
|
|
263
|
-
function overviewNextAction(task, workItems, execution, action) {
|
|
264
|
-
if (task.status !== "active" && task.status !== "draft")
|
|
265
|
-
return null;
|
|
266
|
-
const primary = action.refs[0];
|
|
267
|
-
const workItem = action.refs
|
|
268
|
-
.filter(({ kind }) => kind === "work-item")
|
|
269
|
-
.map(({ id }) => workItems.find((item) => item.id === id))
|
|
270
|
-
.find((item) => item !== undefined);
|
|
271
|
-
const kind = primary?.kind === "input-request"
|
|
272
|
-
? "input"
|
|
273
|
-
: primary?.kind === "work-item"
|
|
274
|
-
? "work"
|
|
275
|
-
: action.kind.includes("attention") || action.kind.includes("inconsistency")
|
|
276
|
-
? "attention"
|
|
277
|
-
: "execution";
|
|
278
|
-
return {
|
|
279
|
-
action: action.kind,
|
|
280
|
-
owner: action.kind === "resolve-input"
|
|
281
|
-
? "user"
|
|
282
|
-
: workItem?.assignee ?? execution.owner,
|
|
283
|
-
kind,
|
|
284
|
-
...(primary === undefined ? {} : { id: primary.id }),
|
|
285
|
-
summary: action.reason
|
|
286
|
-
};
|
|
287
|
-
}
|
|
288
|
-
function countWorkItems(items) {
|
|
289
|
-
const counts = {
|
|
290
|
-
total: items.length,
|
|
291
|
-
open: 0,
|
|
292
|
-
accepted: 0,
|
|
293
|
-
retired: 0
|
|
294
|
-
};
|
|
295
|
-
for (const item of items)
|
|
296
|
-
counts[item.status] += 1;
|
|
297
|
-
return counts;
|
|
298
|
-
}
|
|
299
|
-
function leaderCell(task) {
|
|
300
|
-
const status = task.leader.roleStatus;
|
|
301
|
-
if (task.leader.summaryStatus === "missing")
|
|
302
|
-
return `${status} · missing summary`;
|
|
303
|
-
const values = [task.leader.summary, task.leader.currentFocus]
|
|
304
|
-
.filter((value) => value !== null && value.length > 0);
|
|
305
|
-
return values.length === 0
|
|
306
|
-
? `${status} · summary unavailable`
|
|
307
|
-
: compactText(`${status} · ${values.join(" · ")}`);
|
|
308
|
-
}
|
|
309
|
-
function workCell(counts) {
|
|
310
|
-
if (counts.total === 0)
|
|
311
|
-
return "none";
|
|
312
|
-
const details = Object.keys(counts)
|
|
313
|
-
.filter((status) => status !== "total" && counts[status] > 0)
|
|
314
|
-
.map((status) => `${status}:${counts[status]}`);
|
|
315
|
-
return `${counts.total} total${details.length === 0 ? "" : ` (${details.join(", ")})`}`;
|
|
316
|
-
}
|
|
317
|
-
function blockersCell(blockers) {
|
|
318
|
-
if (blockers.length === 0)
|
|
319
|
-
return "none";
|
|
320
|
-
return blockers.map((blocker) => `${blocker.kind}:${blocker.id}`).join(", ");
|
|
321
|
-
}
|
|
322
|
-
function nextCell(next) {
|
|
323
|
-
return next === null ? "—" : `${next.action} / ${next.owner}`;
|
|
324
|
-
}
|
|
325
|
-
function deliveryCell(delivery) {
|
|
326
|
-
if (delivery.status === "none")
|
|
327
|
-
return "none";
|
|
328
|
-
return `${delivery.status} · ${delivery.mergedProjectCount}/${delivery.codeProjectCount} merged`
|
|
329
|
-
+ (delivery.allMerged
|
|
330
|
-
? ` · ${delivery.verifiedProjectCount}/${delivery.codeProjectCount} verified`
|
|
331
|
-
: "");
|
|
332
|
-
}
|
|
333
|
-
function taskRemoteDeliveryCandidate(task) {
|
|
334
|
-
const projects = task.projectBindings.flatMap(({ projectId, currentCommit }) => (currentCommit === undefined ? [] : [{ projectId, commit: currentCommit }]));
|
|
335
|
-
return projects.length === task.projectBindings.length
|
|
336
|
-
? { projects }
|
|
337
|
-
: null;
|
|
338
|
-
}
|
|
339
|
-
function compactText(value) {
|
|
340
|
-
const oneLine = value.replace(/\s+/g, " ").trim();
|
|
341
|
-
return oneLine.length <= 240 ? oneLine : `${oneLine.slice(0, 237)}...`;
|
|
342
|
-
}
|
|
343
|
-
function renderVerboseDetails(tasks, timeZone) {
|
|
344
|
-
return [
|
|
345
|
-
"Task details",
|
|
346
|
-
...tasks.flatMap((task) => [
|
|
347
|
-
`${task.id}: ${task.title}`,
|
|
348
|
-
` Description: ${task.description ?? "—"}`,
|
|
349
|
-
` Current focus: ${task.currentFocus ?? "missing"}`,
|
|
350
|
-
` Leader summary: ${task.leaderSummary ?? "missing"}`,
|
|
351
|
-
` Summary updated by: ${task.summaryUpdatedBy ?? "unknown"}`,
|
|
352
|
-
` Summary updated at: ${task.summaryUpdatedAt === null
|
|
353
|
-
? "missing"
|
|
354
|
-
: formatTimestamp(task.summaryUpdatedAt, timeZone)}`,
|
|
355
|
-
` Execution: ${task.execution.status} (${task.execution.owner}); ${task.execution.summary}`,
|
|
356
|
-
` Remote delivery: ${deliveryCell(task.remoteDelivery)}; source=${task.remoteDelivery.source}${task.remoteDelivery.provisional ? " (provisional)" : ""}`,
|
|
357
|
-
` Monitoring: ${task.execution.monitoring}; attention: ${task.execution.attention.length}`,
|
|
358
|
-
` DAG: ${task.execution.observability.dag.nodes.length} nodes, ${task.execution.observability.dag.edges.length} edges; ready=${task.execution.observability.dag.readyIds.join(", ") || "none"}; blocked=${task.execution.observability.dag.blockedIds.join(", ") || "none"}`,
|
|
359
|
-
` Observed usage (Task lifetime, observed sources only): tokens=${formatUsageMetric(task.execution.observability.cost.tokens)}; tools=${formatUsageMetric(task.execution.observability.cost.toolCalls)}; elapsed=${formatUsageMetric(task.execution.observability.cost.elapsedSeconds, "s")}; native execution sum=${formatUsageMetric(task.execution.observability.cost.executionSeconds, "s")}; retries=${task.execution.observability.cost.retryCount}`,
|
|
360
|
-
` Context: snapshots=${task.execution.observability.context.snapshotCount}; bytes=${task.execution.observability.context.totalBytes ?? "partial"}; compression=unavailable`,
|
|
361
|
-
` Session tokens: ${task.execution.observability.sessionTokens.length === 0
|
|
362
|
-
? "unobserved"
|
|
363
|
-
: task.execution.observability.sessionTokens.map(({ roleName, metrics }) => {
|
|
364
|
-
const total = metrics.cumulativeTotal.status === "observed"
|
|
365
|
-
? metrics.cumulativeTotal.totalTokens
|
|
366
|
-
: "unobserved";
|
|
367
|
-
const maximum = metrics.maximumRequestInput.status === "observed"
|
|
368
|
-
? metrics.maximumRequestInput.inputTokens
|
|
369
|
-
: "unobserved";
|
|
370
|
-
return `${roleName}: total=${total}, max-request-input=${maximum}`;
|
|
371
|
-
}).join("; ")}`,
|
|
372
|
-
` Projects: ${task.projectBindings.length === 0
|
|
373
|
-
? "none"
|
|
374
|
-
: task.projectBindings.map(({ directory, projectId, baseRef }) => (`${directory} (${projectId} @ ${baseRef})`)).join(", ")}`
|
|
375
|
-
])
|
|
376
|
-
].join("\n");
|
|
377
|
-
}
|