@zq-silk/yui 0.13.9 → 0.14.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 +69 -428
- package/README.md +108 -298
- package/dist/agent/managedRuntimeEnvironment.js +2 -2
- package/dist/cli/commandCatalog.js +39 -77
- package/dist/cli/interactionCandidates.js +4 -4
- package/dist/cli/interactionPolicy.js +7 -10
- package/dist/cli/invocationRouter.js +1 -1
- package/dist/cli/updateCommand.js +6 -27
- package/dist/cli/updateOrchestrator.js +63 -484
- package/dist/cli/updatePorts.js +15 -333
- package/dist/cli/upgradeCommand.js +8 -65
- package/dist/cli.js +57 -242
- package/dist/commands/configCommands.js +11 -62
- package/dist/commands/executionAuditCommands.js +23 -23
- package/dist/commands/globalRoleCommands.js +2 -7
- package/dist/commands/jobCommands.js +1 -4
- package/dist/commands/projectCommands.js +5 -5
- package/dist/commands/releaseCommands.js +3 -10
- package/dist/commands/resourcesCommands.js +2 -2
- package/dist/commands/sessionCommands.js +2 -2
- package/dist/commands/taskActor.js +31 -39
- package/dist/commands/taskChangeSetCommands.js +5 -12
- package/dist/commands/taskCommands.js +464 -1249
- package/dist/commands/taskContextCommand.js +35 -36
- package/dist/commands/taskExecutionCommands.js +33 -46
- package/dist/commands/taskInputCommands.js +20 -42
- package/dist/commands/taskNextActionCommand.js +9 -9
- package/dist/commands/taskOverviewCommand.js +56 -95
- package/dist/commands/taskRoleRuntimeStatus.js +77 -107
- package/dist/commands/taskUpstreamCommands.js +3 -3
- package/dist/commands/taskWorkspaceCommands.js +7 -79
- package/dist/commands/telemetryCommands.js +32 -124
- package/dist/commands/workflowCommands.js +3 -3
- package/dist/completion/fileCompletionManager.js +1 -1
- package/dist/config/configCatalog.js +4 -6
- package/dist/config/yuiConfig.js +6 -43
- package/dist/context/dispatchContext.js +14 -30
- package/dist/context/roleSessionContext.js +2 -2
- package/dist/context/sessionBootstrapManifest.js +2 -2
- package/dist/context/{runContextPack.js → turnContextPack.js} +58 -73
- package/dist/context/turnInputContract.js +204 -0
- package/dist/context/wakeNotification.js +2 -2
- package/dist/controller/agentRuntimeObserver.js +18 -18
- package/dist/controller/clientRuntime.js +13 -20
- package/dist/controller/controller.js +89 -116
- package/dist/controller/fileSchedulerStoreAdapter.js +511 -742
- package/dist/controller/jobControl.js +17 -17
- package/dist/controller/resourceInventory.js +6 -6
- package/dist/controller/resourceInventoryLinux.js +38 -59
- package/dist/controller/resourceInventoryRpc.js +3 -3
- package/dist/controller/resourceInventoryWorker.js +1 -1
- package/dist/controller/runtime.js +46 -53
- package/dist/controller/runtimeEventInbox.js +23 -31
- package/dist/controller/runtimeEventProcessor.js +15 -15
- package/dist/controller/{runtimeHookRunFence.js → runtimeHookTurnFence.js} +87 -81
- package/dist/controller/runtimeLaunchCoordinator.js +3 -3
- package/dist/controller/runtimeObservationHook.js +11 -8
- package/dist/controller/sessionNotify.js +11 -11
- package/dist/controller/structuredProviderObservation.js +89 -16
- package/dist/coordination/workMailbox.js +309 -579
- package/dist/coordination/workMailboxQueue.js +5 -6
- package/dist/core/controllerClient.js +1 -91
- package/dist/core/controllerServer.js +3 -6
- package/dist/doctor/doctor.js +51 -314
- package/dist/execution/candidateConvergence.js +3 -3
- package/dist/execution/executionGroup.js +32 -33
- package/dist/execution/executionHealth.js +36 -38
- package/dist/execution/resourceBroker.js +12 -12
- package/dist/executor/agentExecutor.js +30 -265
- package/dist/executor/effectiveLaunch.js +9 -19
- package/dist/executor/executorRegistry.js +43 -17
- package/dist/executor/fileRoleLaunchPlanner.js +56 -56
- package/dist/executor/launchPlan.js +3 -3
- package/dist/executor/workspacePreflightClassification.js +16 -16
- package/dist/grant/capabilityGrant.js +20 -25
- package/dist/input/inputRequest.js +12 -12
- package/dist/integration/changeSet.js +9 -13
- package/dist/integration/changeSetManifest.js +1 -1
- package/dist/integration/gitIntegrationService.js +2 -2
- package/dist/integration/integrationQueueService.js +5 -21
- package/dist/integration/overlapDiagnostics.js +2 -2
- package/dist/interaction/operatorPresentation.js +1 -1
- package/dist/job/jobRunner.js +1 -1
- package/dist/lifecycle/canonicalLifecycleEvent.js +25 -25
- package/dist/lifecycle/{exactRunTerminalization.js → exactTurnTerminalization.js} +116 -232
- package/dist/message/message.js +6 -6
- package/dist/observability/executionAudit.js +74 -86
- package/dist/observability/faultClassification.js +7 -24
- package/dist/observability/orchestrationMetrics.js +5 -5
- package/dist/observability/runtimeIdentity.js +34 -101
- package/dist/profile/agentProfile.js +1 -1
- package/dist/release/cliHomeReleaseFence.js +3 -3
- package/dist/release/releaseWorkflowEngine.js +6 -10
- package/dist/release/releaseWorkflowPorts.js +2 -2
- package/dist/release/workflowFileLock.js +1 -1
- package/dist/repository/checkoutSwap.js +1 -1
- package/dist/repository/gitWorkspace.js +0 -13
- package/dist/repository/project.js +1 -1
- package/dist/repository/projectMaintenanceLock.js +1 -2
- package/dist/repository/taskBaseFreshness.js +13 -13
- package/dist/repository/taskWorkspaceCoordinator.js +21 -25
- package/dist/repository/taskWorkspaceIdentity.js +8 -21
- package/dist/repository/taskWorkspacePreparer.js +63 -620
- package/dist/resources/autoResourceGc.js +3 -3
- package/dist/resources/liveReferences.js +1 -1
- package/dist/resources/resourceGc.js +1 -1
- package/dist/resources/sqliteResourceRegistry.js +1 -1
- package/dist/review/reviewDecision.js +7 -7
- package/dist/review/reviewFindingLedger.js +1 -1
- package/dist/review/reviewOutcomeClassifier.js +35 -50
- package/dist/review/reviewRound.js +14 -25
- package/dist/review/reviewerAvailability.js +4 -4
- package/dist/review/taskFinalReviewContractResolution.js +41 -0
- package/dist/role/role.js +6 -3
- package/dist/runtime/agentDriver.js +4 -0
- package/dist/runtime/agentHost.js +151 -36
- package/dist/runtime/builtinAgentDrivers.js +79 -2
- package/dist/runtime/codexAppServerRuntime.js +122 -0
- package/dist/runtime/continuationManager.js +3 -5
- package/dist/runtime/exactControlPlane.js +17 -42
- package/dist/runtime/firstProgressAdvisory.js +1 -1
- package/dist/runtime/index.js +2 -2
- package/dist/runtime/launchDiagnostics.js +2 -2
- package/dist/runtime/lifecycleReservation.js +4 -4
- package/dist/runtime/processExitObservation.js +1 -1
- package/dist/runtime/processExitOutbox.js +0 -32
- package/dist/runtime/promptEnvelope.js +9 -9
- package/dist/runtime/providerContinuation.js +2 -2
- package/dist/runtime/providerContinuationReconciliationService.js +2 -2
- package/dist/runtime/providerRuntimeIdentity.js +76 -24
- package/dist/runtime/recentTurnIds.js +38 -0
- package/dist/runtime/runtimeContinuationProjection.js +2 -2
- package/dist/runtime/runtimeHealthPolicy.js +1 -1
- package/dist/runtime/runtimeObservation.js +58 -21
- package/dist/runtime/runtimeProjection.js +19 -27
- package/dist/runtime/sessionLaunchRequest.js +2 -2
- package/dist/runtime/structuredProviderHost.js +177 -12
- package/dist/runtime/tmuxAdapters.js +46 -11
- package/dist/scheduler/activeRoleTurnDelivery.js +245 -0
- package/dist/scheduler/leaderWakeupProcessor.js +147 -445
- package/dist/scheduler/operatorEvent.js +4 -6
- package/dist/scheduler/resourceQueueProjection.js +71 -0
- package/dist/scheduler/{roleRunLiveness.js → roleTurnLiveness.js} +24 -28
- package/dist/scheduler/{roleRunStall.js → roleTurnStall.js} +162 -247
- package/dist/scheduler/taskExecutionProjection.js +99 -99
- package/dist/scheduler/taskObservabilityProjection.js +10 -10
- package/dist/scheduler/taskWake.js +4 -4
- package/dist/scheduler/wakeReason.js +6 -10
- package/dist/scheduler/wakeupQueue.js +0 -9
- package/dist/setup/setupCommand.js +2 -2
- package/dist/storage/currentTaskStore.js +46 -0
- package/dist/storage/persistenceWorker.js +1 -1
- package/dist/storage/sqliteSchema.js +81 -485
- package/dist/storage/sqliteStore.js +206 -365
- package/dist/storage/storageSchema.js +1 -15
- package/dist/storage/storageVersions.js +8 -9
- package/dist/storage/storeRpc.js +10 -34
- package/dist/storage/taskStore.js +63 -3415
- package/dist/storage/upgrade/recordVersions.js +55 -149
- package/dist/storage/upgrade/upgradeOrchestrator.js +89 -1752
- package/dist/task/completionReadiness.js +5 -5
- package/dist/task/deliveryGuard.js +10 -10
- package/dist/task/nextAction.js +74 -77
- package/dist/task/task.js +2 -4
- package/dist/task/taskRecordReference.js +3 -3
- package/dist/task/taskRecordRetirement.js +1 -1
- package/dist/telemetry/sqliteTelemetryStore.js +53 -53
- package/dist/telemetry/telemetryConfig.js +14 -14
- package/dist/telemetry/telemetryWiring.js +8 -14
- package/dist/turn/turn.js +356 -0
- package/dist/{run/runIdentity.js → turn/turnIdentity.js} +4 -4
- package/dist/verification/gateArtifactStore.js +2 -2
- package/dist/web/assets/client/app.js +1 -1
- package/dist/web/assets/client/components.js +29 -54
- package/dist/web/assets/client/i18n.js +17 -37
- package/dist/web/assets/client/view.js +37 -37
- package/dist/web/assets/shell.js +1 -1
- package/dist/web/assets/styles/cards.js +5 -5
- package/dist/web/assets/styles/widgets.js +1 -2
- package/dist/web/webSnapshot.js +65 -60
- package/dist/workItem/workItem.js +11 -11
- package/dist/workspace/workItemChangeSetManager.js +1 -1
- package/docs/task-local-identity.md +5 -5
- package/i18n/README.zh-CN.md +41 -54
- package/package.json +1 -1
- package/skills/yui-leader/SKILL.md +85 -83
- package/skills/yui-operator/SKILL.md +16 -16
- package/skills/yui-reviewer/SKILL.md +10 -10
- package/skills/yui-runtime/SKILL.md +17 -18
- package/skills/yui-worker/SKILL.md +16 -18
- package/dist/context/runContextContract.js +0 -162
- package/dist/executor/turnCompletion.js +0 -126
- package/dist/review/taskFinalReviewContractEvent.js +0 -1
- package/dist/review/taskFinalReviewContractRebind.js +0 -367
- package/dist/run/agentRun.js +0 -356
- package/dist/run/rejectedYieldAttempt.js +0 -221
- package/dist/run/runControlRequest.js +0 -50
- package/dist/run/yieldReceipt.js +0 -65
- package/dist/runtime/turnCompletion.js +0 -3
- package/dist/scheduler/actionability.js +0 -323
- package/dist/scheduler/activeRoleRunDelivery.js +0 -868
- package/dist/scheduler/activeTaskProgress.js +0 -126
- package/dist/storage/compatibleTaskStore.js +0 -248
- package/dist/storage/migration/baseline.js +0 -78
- package/dist/storage/migration/classifier.js +0 -51
- package/dist/storage/migration/compatibleCodec.js +0 -53
- package/dist/storage/migration/engine.js +0 -147
- package/dist/storage/migration/index.js +0 -33
- package/dist/storage/migration/planner.js +0 -154
- package/dist/storage/migration/productionRegistry.js +0 -3931
- package/dist/storage/migration/registry.js +0 -169
- package/dist/storage/migration/report.js +0 -54
- package/dist/storage/migration/types.js +0 -31
- package/dist/storage/upgrade/homeClassification.js +0 -353
- package/dist/storage/upgrade/homeMigrationTarget.js +0 -595
- package/dist/storage/upgrade/migrationReceipt.js +0 -67
- package/dist/storage/upgrade/offlineUpgradeInventory.js +0 -453
- package/dist/storage/upgrade/productionMigrationRegistry.js +0 -6
- package/dist/storage/upgrade/pseudoLayoutRepair.js +0 -241
- package/dist/storage/upgrade/recordVersionScan.js +0 -176
- package/dist/storage/upgrade/sqliteFileSet.js +0 -50
- package/dist/storage/upgrade/sqliteMigrationTarget.js +0 -375
- package/dist/storage/upgrade/sqliteRecordMigrationTarget.js +0 -305
- package/dist/storage/upgrade/sqliteStateMigration.js +0 -918
- package/dist/storage/upgrade/switchProgress.js +0 -80
- package/dist/storage/upgrade/upgradeReceipt.js +0 -161
- package/dist/storage/upgradeCoordination.js +0 -187
- package/dist/storage/upgradeFence.js +0 -366
- package/dist/telemetry/telemetryCompaction.js +0 -252
package/dist/run/yieldReceipt.js
DELETED
|
@@ -1,65 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { requireIdentity, requireText, requireTimestamp } from "../domain/validation.js";
|
|
3
|
-
const DIGEST_PREFIX_LENGTH = 16;
|
|
4
|
-
function canonicalDigestValue(value) {
|
|
5
|
-
if (value === null || typeof value !== "object")
|
|
6
|
-
return JSON.stringify(value) ?? "null";
|
|
7
|
-
if (Array.isArray(value)) {
|
|
8
|
-
return `[${value.map(canonicalDigestValue).join(",")}]`;
|
|
9
|
-
}
|
|
10
|
-
const entries = Object.entries(value)
|
|
11
|
-
.filter(([, entry]) => entry !== undefined)
|
|
12
|
-
.sort(([left], [right]) => (left < right ? -1 : left > right ? 1 : 0));
|
|
13
|
-
return `{${entries
|
|
14
|
-
.map(([key, entry]) => `${JSON.stringify(key)}:${canonicalDigestValue(entry)}`)
|
|
15
|
-
.join(",")}}`;
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Computes the terminal outcome digest. The canonical form sorts object keys
|
|
19
|
-
* and drops `undefined` fields so the same outcome always hashes identically
|
|
20
|
-
* regardless of object construction order.
|
|
21
|
-
*/
|
|
22
|
-
export function computeYieldOutcomeDigest(input) {
|
|
23
|
-
return createHash("sha256")
|
|
24
|
-
.update(canonicalDigestValue(input))
|
|
25
|
-
.digest("hex");
|
|
26
|
-
}
|
|
27
|
-
export function formatYieldReceiptRequestId(taskId, runId, outcomeDigest) {
|
|
28
|
-
return `yield:${taskId}:${runId}:${outcomeDigest.slice(0, DIGEST_PREFIX_LENGTH)}`;
|
|
29
|
-
}
|
|
30
|
-
export function formatYieldReceiptId(taskId, runId, outcomeDigest) {
|
|
31
|
-
return `yield-receipt:${taskId}:${runId}:${outcomeDigest.slice(0, DIGEST_PREFIX_LENGTH)}`;
|
|
32
|
-
}
|
|
33
|
-
export function createYieldReceipt(taskId, runId, outcome, now) {
|
|
34
|
-
const outcomeDigest = computeYieldOutcomeDigest(outcome);
|
|
35
|
-
return validateYieldReceipt({
|
|
36
|
-
schemaVersion: 1,
|
|
37
|
-
requestId: formatYieldReceiptRequestId(taskId, runId, outcomeDigest),
|
|
38
|
-
receiptId: formatYieldReceiptId(taskId, runId, outcomeDigest),
|
|
39
|
-
outcomeDigest,
|
|
40
|
-
committedAt: now.toISOString()
|
|
41
|
-
});
|
|
42
|
-
}
|
|
43
|
-
export function validateYieldReceipt(value) {
|
|
44
|
-
if (value.schemaVersion !== 1) {
|
|
45
|
-
throw new Error("Yield receipt must use schemaVersion 1.");
|
|
46
|
-
}
|
|
47
|
-
requireIdentity(value.requestId, "Yield receipt requestId");
|
|
48
|
-
requireIdentity(value.receiptId, "Yield receipt receiptId");
|
|
49
|
-
requireText(value.outcomeDigest, "Yield receipt outcomeDigest");
|
|
50
|
-
requireTimestamp(value.committedAt, "Yield receipt committedAt");
|
|
51
|
-
return value;
|
|
52
|
-
}
|
|
53
|
-
/**
|
|
54
|
-
* Matches a presented outcome against a terminal Run's committed receipt.
|
|
55
|
-
* The same digest replays the receipt; a different digest fails closed.
|
|
56
|
-
*/
|
|
57
|
-
export function matchYieldReceipt(receipt, outcome) {
|
|
58
|
-
if (receipt === undefined)
|
|
59
|
-
return null;
|
|
60
|
-
const presented = computeYieldOutcomeDigest(outcome);
|
|
61
|
-
if (presented === receipt.outcomeDigest) {
|
|
62
|
-
return { kind: "replayed", receipt };
|
|
63
|
-
}
|
|
64
|
-
return { kind: "digest-mismatch", existing: receipt };
|
|
65
|
-
}
|
|
@@ -1,323 +0,0 @@
|
|
|
1
|
-
import { createHash } from "node:crypto";
|
|
2
|
-
import { currentWorkItemExecutionGroup } from "../workItem/workItem.js";
|
|
3
|
-
import { isDurableJobTerminal } from "../job/durableJob.js";
|
|
4
|
-
import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
5
|
-
import { executionStageSpendClosed, observedExecutionResourceUsage, projectExecutionStageResources } from "../execution/resourceBroker.js";
|
|
6
|
-
/**
|
|
7
|
-
* Canonical SHA-256 digest over the normalized actionable facts. Pure and
|
|
8
|
-
* deterministic: the same facts always produce the same digest regardless of
|
|
9
|
-
* collection order.
|
|
10
|
-
*/
|
|
11
|
-
export function computeActionabilityDigest(input) {
|
|
12
|
-
const facts = [...input.facts]
|
|
13
|
-
.map((fact) => ({ key: fact.key, value: fact.value }))
|
|
14
|
-
.sort((left, right) => (left.key < right.key ? -1 : left.key > right.key ? 1 : 0));
|
|
15
|
-
const canonical = JSON.stringify({
|
|
16
|
-
taskId: input.taskId,
|
|
17
|
-
taskStatus: input.taskStatus,
|
|
18
|
-
facts
|
|
19
|
-
});
|
|
20
|
-
return createHash("sha256").update(canonical).digest("hex");
|
|
21
|
-
}
|
|
22
|
-
const TERMINAL_WORK_ITEM_STATUSES = new Set(["completed", "retired"]);
|
|
23
|
-
const CONSUMED_INTEGRATION_STATUSES = new Set(["committed", "superseded"]);
|
|
24
|
-
/** Exact durable Broker queue shared by admission and wake actionability. */
|
|
25
|
-
export function projectQueuedResourceLaneIdentities(store, now) {
|
|
26
|
-
return store.listActiveTaskIds().flatMap((taskId) => {
|
|
27
|
-
const taskRuns = store.listAgentRuns(taskId);
|
|
28
|
-
const taskEvents = store.listEvents(taskId);
|
|
29
|
-
const workItemLanes = store.listWorkItems(taskId).flatMap((item) => {
|
|
30
|
-
if (item.status !== "running")
|
|
31
|
-
return [];
|
|
32
|
-
const group = currentWorkItemExecutionGroup(item);
|
|
33
|
-
if (group === undefined || group.resolution !== undefined)
|
|
34
|
-
return [];
|
|
35
|
-
if (group.stage !== undefined) {
|
|
36
|
-
const resources = projectExecutionStageResources({
|
|
37
|
-
group,
|
|
38
|
-
stageGroups: item.executionGroups,
|
|
39
|
-
usage: observedExecutionResourceUsage({
|
|
40
|
-
group,
|
|
41
|
-
stageGroups: item.executionGroups,
|
|
42
|
-
runs: taskRuns,
|
|
43
|
-
events: taskEvents
|
|
44
|
-
}),
|
|
45
|
-
now
|
|
46
|
-
});
|
|
47
|
-
if (executionStageSpendClosed(resources))
|
|
48
|
-
return [];
|
|
49
|
-
}
|
|
50
|
-
return group.lanes.flatMap((lane) => {
|
|
51
|
-
if (lane.status !== "pending"
|
|
52
|
-
|| lane.effective === undefined
|
|
53
|
-
|| lane.runId !== undefined)
|
|
54
|
-
return [];
|
|
55
|
-
return [{
|
|
56
|
-
taskId,
|
|
57
|
-
workItemId: item.id,
|
|
58
|
-
executionGroupId: group.id,
|
|
59
|
-
executionLaneId: lane.id,
|
|
60
|
-
providerId: lane.effective.adapterId,
|
|
61
|
-
agentId: lane.effective.agentId,
|
|
62
|
-
...(lane.effective.model === undefined ? {} : { model: lane.effective.model }),
|
|
63
|
-
requestedAt: lane.updatedAt
|
|
64
|
-
}];
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
const reviewLanes = store.listReviewRounds(taskId).flatMap((round) => {
|
|
68
|
-
if (round.status !== "pending" && round.status !== "running")
|
|
69
|
-
return [];
|
|
70
|
-
const group = round.executionGroup;
|
|
71
|
-
if (group === undefined || group.resolution !== undefined)
|
|
72
|
-
return [];
|
|
73
|
-
return group.lanes.flatMap((lane) => {
|
|
74
|
-
if (lane.status !== "pending"
|
|
75
|
-
|| lane.effective === undefined
|
|
76
|
-
|| lane.runId !== undefined)
|
|
77
|
-
return [];
|
|
78
|
-
return [{
|
|
79
|
-
taskId,
|
|
80
|
-
...(round.workItemId === undefined ? {} : { workItemId: round.workItemId }),
|
|
81
|
-
executionGroupId: group.id,
|
|
82
|
-
executionLaneId: lane.id,
|
|
83
|
-
providerId: lane.effective.adapterId,
|
|
84
|
-
agentId: lane.effective.agentId,
|
|
85
|
-
...(lane.effective.model === undefined ? {} : { model: lane.effective.model }),
|
|
86
|
-
requestedAt: lane.updatedAt
|
|
87
|
-
}];
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
return [...workItemLanes, ...reviewLanes];
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
/**
|
|
94
|
-
* True only when the Task still owns a Resource-Broker-queued Lane that a
|
|
95
|
-
* later Leader dispatch may start. Terminal owners retain their immutable
|
|
96
|
-
* Group history, but can never reserve live queue capacity.
|
|
97
|
-
*/
|
|
98
|
-
export function hasDispatchableQueuedResourceLane(store, taskId) {
|
|
99
|
-
const workItemGroups = (store.listWorkItems?.(taskId) ?? [])
|
|
100
|
-
.filter(({ status }) => status === "running")
|
|
101
|
-
.flatMap(({ executionGroups }) => executionGroups);
|
|
102
|
-
const reviewGroups = (store.listReviewRounds?.(taskId) ?? [])
|
|
103
|
-
.filter(({ status }) => status === "pending" || status === "running")
|
|
104
|
-
.flatMap(({ executionGroup }) => executionGroup === undefined ? [] : [executionGroup]);
|
|
105
|
-
return [...workItemGroups, ...reviewGroups].some((group) => (group.resolution === undefined
|
|
106
|
-
&& group.lanes.some((lane) => (lane.status === "pending"
|
|
107
|
-
&& lane.effective !== undefined
|
|
108
|
-
&& lane.runId === undefined))));
|
|
109
|
-
}
|
|
110
|
-
/**
|
|
111
|
-
* Fold a Task's durable records into the normalized actionable facts. This
|
|
112
|
-
* function only reads; it never starts a Controller, queues a wake, writes a
|
|
113
|
-
* Message, or mutates any record.
|
|
114
|
-
*
|
|
115
|
-
* Included facts (each changes only when the durable record changes):
|
|
116
|
-
* - Active Runs: the ownership/liveness picture. A Run starting or ending
|
|
117
|
-
* changes the digest so a lost executor is detected.
|
|
118
|
-
* - Non-terminal/failed WorkItems: pending, running, awaiting-acceptance, and
|
|
119
|
-
* failed items still need Leader or Worker action.
|
|
120
|
-
* - Non-completed ReviewRounds: pending/running/failed rounds still need
|
|
121
|
-
* Leader disposition or a Reviewer.
|
|
122
|
-
* - Unresolved IntegrationAttempts: running/validating/blocked/failed attempts
|
|
123
|
-
* the Leader may need to resolve.
|
|
124
|
-
* - Terminal DurableJobs: new terminal results the Leader should observe.
|
|
125
|
-
* - InputRequests: open and answered/cancelled requests.
|
|
126
|
-
* - User directives explicitly marked `wakePolicy=leader`.
|
|
127
|
-
*
|
|
128
|
-
* Excluded: pure revision growth, progress/heartbeat events, repeated scans of
|
|
129
|
-
* an unchanged blocker, read-only status requests, and waits already owned by
|
|
130
|
-
* an active Worker/Reviewer/Job (the active-Run facts above capture ownership).
|
|
131
|
-
*/
|
|
132
|
-
export function collectTaskActionability(store, taskId, now = new Date()) {
|
|
133
|
-
const task = store.getTask(taskId);
|
|
134
|
-
if (task === null) {
|
|
135
|
-
throw new Error(`Task not found for actionability projection: ${taskId}.`);
|
|
136
|
-
}
|
|
137
|
-
const facts = [];
|
|
138
|
-
const events = store.listEvents?.(taskId) ?? [];
|
|
139
|
-
for (const run of operationalTaskRecords(store.listAgentRuns(taskId), events, "agent-run").filter((candidate) => candidate.status === "active")) {
|
|
140
|
-
facts.push({
|
|
141
|
-
key: `active-run:${run.id}`,
|
|
142
|
-
value: [
|
|
143
|
-
run.roleName,
|
|
144
|
-
run.workItemId ?? "",
|
|
145
|
-
run.reviewRoundId ?? "",
|
|
146
|
-
run.updatedAt
|
|
147
|
-
].join("|")
|
|
148
|
-
});
|
|
149
|
-
}
|
|
150
|
-
const workItems = store.listWorkItems?.(taskId) ?? [];
|
|
151
|
-
const reviewRounds = store.listReviewRounds?.(taskId) ?? [];
|
|
152
|
-
for (const item of workItems) {
|
|
153
|
-
if (TERMINAL_WORK_ITEM_STATUSES.has(item.status))
|
|
154
|
-
continue;
|
|
155
|
-
facts.push({
|
|
156
|
-
key: `work-item:${item.id}`,
|
|
157
|
-
value: `${item.status}|${item.updatedAt}`
|
|
158
|
-
});
|
|
159
|
-
for (const group of item.executionGroups) {
|
|
160
|
-
if (group.resolution !== undefined || group.stage?.resources === undefined)
|
|
161
|
-
continue;
|
|
162
|
-
facts.push({
|
|
163
|
-
key: `resource-deadline:${group.id}`,
|
|
164
|
-
value: now.getTime() >= Date.parse(group.stage.resources.deadlineAt)
|
|
165
|
-
? "reached"
|
|
166
|
-
: "open"
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
}
|
|
170
|
-
// A Resource-Broker-queued Lane is durable but deliberately has no active
|
|
171
|
-
// Run. Include the global capacity and fair-queue projection only for Tasks
|
|
172
|
-
// that are actually queued, so either capacity or an older reservation being
|
|
173
|
-
// released changes their digest without polling or a second scheduler.
|
|
174
|
-
const queueProjectionAvailable = store.listActiveTaskIds !== undefined
|
|
175
|
-
&& store.listWorkItems !== undefined
|
|
176
|
-
&& store.listReviewRounds !== undefined
|
|
177
|
-
&& store.listEvents !== undefined;
|
|
178
|
-
const queuedResourceLanes = queueProjectionAvailable
|
|
179
|
-
? projectQueuedResourceLaneIdentities({
|
|
180
|
-
listActiveTaskIds: () => store.listActiveTaskIds(),
|
|
181
|
-
listAgentRuns: (candidateTaskId) => store.listAgentRuns(candidateTaskId),
|
|
182
|
-
listWorkItems: (candidateTaskId) => store.listWorkItems(candidateTaskId),
|
|
183
|
-
listReviewRounds: (candidateTaskId) => store.listReviewRounds(candidateTaskId),
|
|
184
|
-
listEvents: (candidateTaskId) => store.listEvents(candidateTaskId)
|
|
185
|
-
}, now)
|
|
186
|
-
: [];
|
|
187
|
-
const hasQueuedResourceLane = queueProjectionAvailable
|
|
188
|
-
? queuedResourceLanes.some((lane) => lane.taskId === taskId)
|
|
189
|
-
: hasDispatchableQueuedResourceLane(store, taskId);
|
|
190
|
-
if (hasQueuedResourceLane && store.listActiveTaskIds !== undefined) {
|
|
191
|
-
const activeResourceScopes = store.listActiveTaskIds().flatMap((activeTaskId) => (operationalTaskRecords(store.listAgentRuns(activeTaskId), store.listEvents?.(activeTaskId) ?? [], "agent-run").flatMap((run) => (run.status !== "active"
|
|
192
|
-
|| run.executionGroupId === undefined
|
|
193
|
-
|| run.executionLaneId === undefined
|
|
194
|
-
? []
|
|
195
|
-
: [
|
|
196
|
-
"home",
|
|
197
|
-
`task:${activeTaskId}`,
|
|
198
|
-
...(run.workItemId === undefined
|
|
199
|
-
? []
|
|
200
|
-
: [`work-item:${activeTaskId}/${run.workItemId}`]),
|
|
201
|
-
`group:${activeTaskId}/${run.executionGroupId}`,
|
|
202
|
-
`provider:${run.effective.adapterId}`,
|
|
203
|
-
`agent:${run.effective.agentId}`,
|
|
204
|
-
`model:${run.effective.adapterId}/${run.effective.model ?? "default"}`
|
|
205
|
-
]))));
|
|
206
|
-
const activeResourceCounts = new Map();
|
|
207
|
-
for (const scope of activeResourceScopes) {
|
|
208
|
-
activeResourceCounts.set(scope, (activeResourceCounts.get(scope) ?? 0) + 1);
|
|
209
|
-
}
|
|
210
|
-
facts.push({
|
|
211
|
-
key: "resource-broker:active-capacity",
|
|
212
|
-
value: [...activeResourceCounts]
|
|
213
|
-
.sort(([left], [right]) => left.localeCompare(right))
|
|
214
|
-
.map(([scope, count]) => `${scope}=${count}`)
|
|
215
|
-
.join("|")
|
|
216
|
-
});
|
|
217
|
-
if (queueProjectionAvailable) {
|
|
218
|
-
facts.push({
|
|
219
|
-
key: "resource-broker:fair-queue",
|
|
220
|
-
value: queuedResourceLanes
|
|
221
|
-
.map((lane) => [
|
|
222
|
-
lane.requestedAt,
|
|
223
|
-
lane.taskId,
|
|
224
|
-
lane.workItemId ?? "",
|
|
225
|
-
lane.executionGroupId,
|
|
226
|
-
lane.executionLaneId,
|
|
227
|
-
lane.providerId,
|
|
228
|
-
lane.agentId,
|
|
229
|
-
lane.model ?? "default"
|
|
230
|
-
].join("|"))
|
|
231
|
-
.sort()
|
|
232
|
-
.join("\n")
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
}
|
|
236
|
-
for (const round of reviewRounds) {
|
|
237
|
-
if (round.status === "completed")
|
|
238
|
-
continue;
|
|
239
|
-
facts.push({
|
|
240
|
-
key: `review:${round.id}`,
|
|
241
|
-
value: `${round.status}|${round.endedAt ?? round.createdAt}`
|
|
242
|
-
});
|
|
243
|
-
}
|
|
244
|
-
for (const attempt of store.listIntegrationAttempts?.(taskId) ?? []) {
|
|
245
|
-
if (CONSUMED_INTEGRATION_STATUSES.has(attempt.status))
|
|
246
|
-
continue;
|
|
247
|
-
facts.push({
|
|
248
|
-
key: `integration:${attempt.id}`,
|
|
249
|
-
value: `${attempt.status}|${attempt.updatedAt}`
|
|
250
|
-
});
|
|
251
|
-
}
|
|
252
|
-
for (const job of store.listDurableJobs?.(taskId) ?? []) {
|
|
253
|
-
if (!isDurableJobTerminal(job.status))
|
|
254
|
-
continue;
|
|
255
|
-
facts.push({
|
|
256
|
-
key: `durable-job:${job.id}`,
|
|
257
|
-
value: `${job.status}|${job.terminalAt ?? job.updatedAt}`
|
|
258
|
-
});
|
|
259
|
-
}
|
|
260
|
-
for (const request of store.listInputRequests?.(taskId) ?? []) {
|
|
261
|
-
facts.push({
|
|
262
|
-
key: `input:${request.id}`,
|
|
263
|
-
value: `${request.status}|${request.updatedAt}`
|
|
264
|
-
});
|
|
265
|
-
}
|
|
266
|
-
for (const message of operationalTaskRecords(store.listMessages?.(taskId) ?? [], events, "message")) {
|
|
267
|
-
if (message.wakePolicy !== "leader")
|
|
268
|
-
continue;
|
|
269
|
-
facts.push({
|
|
270
|
-
key: `directive:${message.id}`,
|
|
271
|
-
value: message.createdAt
|
|
272
|
-
});
|
|
273
|
-
}
|
|
274
|
-
return { taskId, taskStatus: task.status, facts };
|
|
275
|
-
}
|
|
276
|
-
/**
|
|
277
|
-
* Derive the Leader Run disposition from the Task execution projection status.
|
|
278
|
-
* This is machine-derived at yield time so the Scheduler does not depend on
|
|
279
|
-
* free-text Leader summaries.
|
|
280
|
-
*/
|
|
281
|
-
export function deriveLeaderRunDisposition(projectionStatus, taskStatus) {
|
|
282
|
-
if (taskStatus !== "active")
|
|
283
|
-
return "completed";
|
|
284
|
-
switch (projectionStatus) {
|
|
285
|
-
case "blocked":
|
|
286
|
-
return "blocked";
|
|
287
|
-
case "waiting-on-agents":
|
|
288
|
-
case "waiting-user":
|
|
289
|
-
return "waiting";
|
|
290
|
-
case "completed":
|
|
291
|
-
case "retired":
|
|
292
|
-
case "archived":
|
|
293
|
-
return "completed";
|
|
294
|
-
default:
|
|
295
|
-
return "progress";
|
|
296
|
-
}
|
|
297
|
-
}
|
|
298
|
-
/**
|
|
299
|
-
* Decide whether a `task-orphaned` scan should create a new Leader Run.
|
|
300
|
-
*
|
|
301
|
-
* Suppression applies only when the last Leader Run ended `waiting` or
|
|
302
|
-
* `blocked` and its `observedActionabilityDigest` equals the current digest.
|
|
303
|
-
* A `progress`/`completed` disposition, a missing digest, or a changed digest
|
|
304
|
-
* always wakes. Computation errors fail open.
|
|
305
|
-
*/
|
|
306
|
-
export function decideOrphanWake(input) {
|
|
307
|
-
const { currentDigest, lastLeaderRun } = input;
|
|
308
|
-
if (lastLeaderRun === null)
|
|
309
|
-
return { kind: "wake", digest: currentDigest };
|
|
310
|
-
if (lastLeaderRun.status === "active")
|
|
311
|
-
return { kind: "wake", digest: currentDigest };
|
|
312
|
-
const disposition = lastLeaderRun.disposition;
|
|
313
|
-
if (disposition !== "waiting" && disposition !== "blocked") {
|
|
314
|
-
return { kind: "wake", digest: currentDigest };
|
|
315
|
-
}
|
|
316
|
-
const observed = lastLeaderRun.observedActionabilityDigest;
|
|
317
|
-
if (observed === undefined)
|
|
318
|
-
return { kind: "wake", digest: currentDigest };
|
|
319
|
-
if (observed === currentDigest) {
|
|
320
|
-
return { kind: "suppress", digest: currentDigest, observedDigest: observed };
|
|
321
|
-
}
|
|
322
|
-
return { kind: "wake", digest: currentDigest };
|
|
323
|
-
}
|