@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
|
@@ -15,14 +15,12 @@ export function parseTaskCatalogOptions(args) {
|
|
|
15
15
|
all = true;
|
|
16
16
|
continue;
|
|
17
17
|
}
|
|
18
|
-
if (!["--
|
|
18
|
+
if (!["--limit", "--cursor", "--status", "--project", "--search", "--attention"].includes(key)
|
|
19
19
|
|| values.has(key) || args[i + 1] === undefined || args[i + 1].startsWith("--")) {
|
|
20
|
-
throw usageError("
|
|
20
|
+
throw usageError("Task list expects [--all] [--status <status>] [--project <id>] [--search <text>] [--attention <category>] [--limit <1..100>] [--cursor <cursor>].");
|
|
21
21
|
}
|
|
22
22
|
values.set(key, args[++i]);
|
|
23
23
|
}
|
|
24
|
-
if (values.get("--view") !== "compact")
|
|
25
|
-
throw usageError("Task list view must be compact.");
|
|
26
24
|
const status = values.get("--status");
|
|
27
25
|
if (status !== undefined && !TASK_STATUSES.includes(status)) {
|
|
28
26
|
throw usageError("Unknown Task catalog status.");
|
|
@@ -99,7 +97,7 @@ export function readTaskCatalog(store, options, environment = {}) {
|
|
|
99
97
|
// sample size therefore must never be subtracted from one another.
|
|
100
98
|
taskCount: source.taskCount,
|
|
101
99
|
omittedTaskRefs: source.taskCount - selected.length,
|
|
102
|
-
query: {
|
|
100
|
+
query: { all: options.all, attention: kind }
|
|
103
101
|
}];
|
|
104
102
|
}));
|
|
105
103
|
const response = {
|
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { runExecutionObservation } from "../agentRun/agentRun.js";
|
|
2
|
+
import { isGitArtifactRefString, parseGitArtifactRef } from "../artifacts/gitArtifactRef.js";
|
|
3
|
+
import { taskNotFound, usageError } from "../errors/cliError.js";
|
|
4
|
+
import { expandTaskMessageResult } from "../message/message.js";
|
|
2
5
|
import { resolveManagedTaskReader } from "../runtime/managedCaller.js";
|
|
6
|
+
import { providerRetryProjection } from "../runtime/providerRetry.js";
|
|
7
|
+
import { projectTaskRemoteDeliveryFromStore } from "../task/remoteDeliveryService.js";
|
|
8
|
+
import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
|
|
3
9
|
import { contextContentDigest } from "./contextSnapshot.js";
|
|
4
10
|
import { buildRunContextPack } from "./runContextPack.js";
|
|
5
11
|
import { sourceRunContextValue } from "./sourceRunContext.js";
|
|
6
|
-
import { expandTaskMessageResult } from "../message/message.js";
|
|
7
|
-
import { managedWorkspaceKey } from "../worktree/managedWorkspace.js";
|
|
8
|
-
import { runExecutionObservation } from "../agentRun/agentRun.js";
|
|
9
|
-
import { isGitArtifactRefString, parseGitArtifactRef } from "../artifacts/gitArtifactRef.js";
|
|
10
|
-
import { projectTaskRemoteDeliveryFromStore } from "../commands/taskRemoteDeliveryCommand.js";
|
|
11
|
-
import { providerRetryProjection } from "../runtime/providerRetry.js";
|
|
12
12
|
const MAX_RECORDS = 256;
|
|
13
13
|
const MAX_VALUE_BYTES = 4096;
|
|
14
14
|
const MAX_PAGE_BYTES = 128 * 1024;
|
|
@@ -19,66 +19,74 @@ const MAX_ATTENTION_REFS = 8;
|
|
|
19
19
|
* Every public path checks the same scope before reading, counting or paging.
|
|
20
20
|
*/
|
|
21
21
|
export function readTaskContext(store, taskId, environment = {}) {
|
|
22
|
-
return store.
|
|
23
|
-
const
|
|
22
|
+
return store.readTransaction((reader) => {
|
|
23
|
+
const scope = authorizeContext(reader, taskId, environment);
|
|
24
|
+
const sources = authorizedSources(reader, scope);
|
|
25
|
+
const count = sources.reduce((total, source) => total + source.count, 0);
|
|
24
26
|
const cursor = currentCursor(reader, taskId);
|
|
25
27
|
// Reserve a small, classified view before ordinary history consumes the
|
|
26
28
|
// page. Counts and samples come from the exact same authorized read.
|
|
27
|
-
const attention = summarizeAttention(
|
|
29
|
+
const attention = summarizeAttention(reader, taskId, scope.allow);
|
|
28
30
|
let bytes = Buffer.byteLength(JSON.stringify(attention));
|
|
29
31
|
const records = [];
|
|
30
|
-
|
|
31
|
-
for (const
|
|
32
|
-
const
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
32
|
+
let runtimeEvents;
|
|
33
|
+
page: for (const source of sources)
|
|
34
|
+
for (const entry of source.read()) {
|
|
35
|
+
if (records.length >= MAX_RECORDS)
|
|
36
|
+
break page;
|
|
37
|
+
if (entry.ref.store === "run")
|
|
38
|
+
runtimeEvents ??= reader.listEventsByType(taskId, ["runtime.agent-error"]);
|
|
39
|
+
const valueBytes = Buffer.byteLength(JSON.stringify(entry.value));
|
|
40
|
+
const content = valueBytes > MAX_VALUE_BYTES
|
|
41
|
+
? { ref: entry.ref, summary: summarize(entry.value), omitted: true }
|
|
42
|
+
: { ...entry, omitted: false };
|
|
43
|
+
const record = { ...content, ...(entry.ref.store === "run" ? {
|
|
44
|
+
execution: runExecutionObservation(entry.value, reader.getTaskRoleSessionSet(taskId, entry.value.roleName)?.providerBinding, runtimeEvents)
|
|
45
|
+
} : {}), ...(entry.ref.store === "role" ? {
|
|
46
|
+
providerRetry: providerRetryProjection(reader.getTaskRoleSessionSet(taskId, entry.value.name)?.providerBinding)
|
|
47
|
+
} : {}) };
|
|
48
|
+
const size = Buffer.byteLength(JSON.stringify(record));
|
|
49
|
+
if (bytes + size > MAX_PAGE_BYTES)
|
|
50
|
+
break page;
|
|
51
|
+
records.push(record);
|
|
52
|
+
bytes += size;
|
|
53
|
+
}
|
|
47
54
|
return {
|
|
48
55
|
taskId, coreCursor: encode(cursor), throughCursor: encode(cursor),
|
|
49
56
|
attention,
|
|
50
|
-
records, count
|
|
51
|
-
omitted: { records:
|
|
57
|
+
records, count,
|
|
58
|
+
omitted: { records: count - records.length, values: records.filter((r) => r.omitted).length },
|
|
52
59
|
observations: [],
|
|
53
60
|
limits: { records: MAX_RECORDS, valueBytes: MAX_VALUE_BYTES, pageBytes: MAX_PAGE_BYTES,
|
|
54
61
|
attentionRefsPerCategory: MAX_ATTENTION_REFS, attentionRefBytesPerCategory: MAX_VALUE_BYTES }
|
|
55
62
|
};
|
|
56
63
|
});
|
|
57
64
|
}
|
|
58
|
-
function summarizeAttention(
|
|
65
|
+
function summarizeAttention(store, taskId, allow) {
|
|
59
66
|
const group = () => ({ count: 0, refs: [], omittedRefs: 0 });
|
|
60
67
|
const attention = {
|
|
61
68
|
openInputs: group(), pendingOperations: group(), unknownOperations: group()
|
|
62
69
|
};
|
|
63
|
-
const
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
continue;
|
|
70
|
+
for (const [category, family, statuses] of [
|
|
71
|
+
["openInputs", "input-request", ["open"]],
|
|
72
|
+
["pendingOperations", "job", ["queued", "running"]],
|
|
73
|
+
["unknownOperations", "job", ["unknown-needs-attention"]]
|
|
74
|
+
]) {
|
|
75
|
+
const page = store.queryContextRecords(taskId, {
|
|
76
|
+
family, statuses, ids: allowedIds(allow, family), limit: MAX_ATTENTION_REFS
|
|
77
|
+
});
|
|
72
78
|
const summary = attention[category];
|
|
73
|
-
summary.count
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
summary.
|
|
79
|
+
summary.count = page.count;
|
|
80
|
+
let bytes = 0;
|
|
81
|
+
for (const { id, value } of page.records) {
|
|
82
|
+
const ref = materialize(family, id, value).ref;
|
|
83
|
+
const size = Buffer.byteLength(JSON.stringify(ref)) + 1;
|
|
84
|
+
if (bytes + size > MAX_VALUE_BYTES)
|
|
85
|
+
break;
|
|
86
|
+
summary.refs.push(ref);
|
|
87
|
+
bytes += size;
|
|
81
88
|
}
|
|
89
|
+
summary.omittedRefs = summary.count - summary.refs.length;
|
|
82
90
|
}
|
|
83
91
|
return attention;
|
|
84
92
|
}
|
|
@@ -87,11 +95,10 @@ function summarizeAttention(entries) {
|
|
|
87
95
|
* A continuation carries the first page's fixed upper event bound.
|
|
88
96
|
*/
|
|
89
97
|
export function readTaskContextDelta(store, taskId, input, environment = {}) {
|
|
90
|
-
return store.
|
|
98
|
+
return store.readTransaction((reader) => {
|
|
91
99
|
const { allow } = authorizeContext(reader, taskId, environment);
|
|
92
|
-
const history = reader.listEvents(taskId);
|
|
93
100
|
const start = decode(input.after, taskId);
|
|
94
|
-
const now = currentCursor(reader, taskId
|
|
101
|
+
const now = currentCursor(reader, taskId);
|
|
95
102
|
const continuation = input.continuation === undefined ? undefined : decodePage(input.continuation, taskId);
|
|
96
103
|
const bound = continuation ?? now;
|
|
97
104
|
const after = continuation?.after ?? start.sequence;
|
|
@@ -103,13 +110,14 @@ export function readTaskContextDelta(store, taskId, input, environment = {}) {
|
|
|
103
110
|
if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_EVENTS) {
|
|
104
111
|
throw usageError(`Context delta limit must be between 1 and ${MAX_EVENTS}.`);
|
|
105
112
|
}
|
|
106
|
-
const
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
113
|
+
const selected = reader.queryContextRecords(taskId, {
|
|
114
|
+
family: "task-event", ids: allowedIds(allow, "task-event"),
|
|
115
|
+
afterSequence: after, throughSequence: bound.sequence, limit
|
|
116
|
+
});
|
|
110
117
|
const page = [];
|
|
111
118
|
let bytes = 0;
|
|
112
|
-
for (const
|
|
119
|
+
for (const { value } of selected.records) {
|
|
120
|
+
const event = value;
|
|
113
121
|
const entry = materialize("task-event", event.id, event);
|
|
114
122
|
const record = Buffer.byteLength(JSON.stringify(event)) > MAX_VALUE_BYTES
|
|
115
123
|
? { ref: entry.ref, omitted: true }
|
|
@@ -121,10 +129,10 @@ export function readTaskContextDelta(store, taskId, input, environment = {}) {
|
|
|
121
129
|
bytes += size;
|
|
122
130
|
}
|
|
123
131
|
const last = page.at(-1)?.ref.refId;
|
|
124
|
-
const more =
|
|
132
|
+
const more = selected.count > page.length;
|
|
125
133
|
const through = { taskId, sequence: bound.sequence, revision: bound.revision };
|
|
126
134
|
return {
|
|
127
|
-
taskId, events: page, count:
|
|
135
|
+
taskId, events: page, count: selected.count, throughCursor: encode(through),
|
|
128
136
|
...(more && last !== undefined
|
|
129
137
|
? { continuation: encode({ ...through, after: sequence(last) }) } : {}),
|
|
130
138
|
observations: []
|
|
@@ -132,7 +140,7 @@ export function readTaskContextDelta(store, taskId, input, environment = {}) {
|
|
|
132
140
|
});
|
|
133
141
|
}
|
|
134
142
|
export function inspectTaskContext(store, taskId, selector, environment = {}) {
|
|
135
|
-
return store.
|
|
143
|
+
return store.readTransaction((reader) => {
|
|
136
144
|
const scope = authorizeContext(reader, taskId, environment);
|
|
137
145
|
const value = isAllowed(scope.allow, selector.store, selector.refId)
|
|
138
146
|
? inspectValue(reader, scope, selector) : null;
|
|
@@ -152,7 +160,7 @@ export function inspectTaskContext(store, taskId, selector, environment = {}) {
|
|
|
152
160
|
: undefined;
|
|
153
161
|
const response = { ...entry, ...(expansion !== undefined && "result" in expansion
|
|
154
162
|
? { result: expansion.result } : {}),
|
|
155
|
-
...(selector.store === "run" ? { execution: runExecutionObservation(value, reader.getTaskRoleSessionSet(taskId, value.roleName)?.providerBinding, reader.
|
|
163
|
+
...(selector.store === "run" ? { execution: runExecutionObservation(value, reader.getTaskRoleSessionSet(taskId, value.roleName)?.providerBinding, reader.listEventsByType(taskId, ["runtime.agent-error"])) } : {}),
|
|
156
164
|
coreCursor: encode(currentCursor(reader, taskId)) };
|
|
157
165
|
if (Buffer.byteLength(JSON.stringify(response)) > MAX_INSPECT_BYTES) {
|
|
158
166
|
throw usageError("Expanded Context exceeds the bounded inspect limit.", undefined, { ref: entry.ref, maxBytes: MAX_INSPECT_BYTES });
|
|
@@ -161,7 +169,7 @@ export function inspectTaskContext(store, taskId, selector, environment = {}) {
|
|
|
161
169
|
});
|
|
162
170
|
}
|
|
163
171
|
export function listContextMessages(store, taskId, environment = {}) {
|
|
164
|
-
return store.
|
|
172
|
+
return store.readTransaction((reader) => {
|
|
165
173
|
const { allow } = authorizeContext(reader, taskId, environment);
|
|
166
174
|
return reader.listMessages(taskId)
|
|
167
175
|
.filter((message) => isAllowed(allow, "task-message", message.id))
|
|
@@ -236,63 +244,45 @@ function authorizeContext(store, taskId, environment) {
|
|
|
236
244
|
allow = new Set(pack.authority.readableRefs.map((ref) => `${ref.store}:${ref.refId}`));
|
|
237
245
|
allow.add(`role:${caller.roleName}`);
|
|
238
246
|
allow.add(`turn:${caller.currentRunId}`);
|
|
239
|
-
//
|
|
240
|
-
//
|
|
241
|
-
//
|
|
242
|
-
// holds no Assignment), so recognize it explicitly: the shared block below
|
|
243
|
-
// must never leak it, and the steer-delta block authorizes it only to its own
|
|
244
|
-
// recipient. `interruptThen.reusedInput` is the interrupt-then composition
|
|
245
|
-
// whose carried input can itself be a steer.
|
|
246
|
-
const isSteerMessage = (message) => message.inputControl?.action === "steer"
|
|
247
|
-
|| message.interruptThen?.reusedInput?.action === "steer";
|
|
248
|
-
// A frozen Assignment is not a cutoff for the Task's current user intent.
|
|
249
|
-
// Untargeted human/Operator messages are shared Task requirements; scoped
|
|
250
|
-
// messages, other Roles' results, and steers still require an explicit grant.
|
|
251
|
-
const sharedMessageIds = new Set(store.listMessages(taskId)
|
|
252
|
-
.filter(message => (message.kind === "user" || message.kind === "operator")
|
|
253
|
-
&& message.recipient === undefined && message.workItemId === undefined
|
|
254
|
-
&& message.runId === undefined && !isSteerMessage(message))
|
|
255
|
-
.map(message => message.id));
|
|
256
|
-
for (const id of sharedMessageIds)
|
|
257
|
-
allow.add(`task-message:${id}`);
|
|
258
|
-
for (const event of store.listEvents(taskId)) {
|
|
259
|
-
if (event.type.startsWith("message.") && sharedMessageIds.has(event.payload.messageId)) {
|
|
260
|
-
allow.add(`task-event:${event.id}`);
|
|
261
|
-
}
|
|
262
|
-
}
|
|
263
|
-
// A steer targets this Role's exact current native Turn (decision-3 §9,
|
|
264
|
-
// message-5 gap E). Unlike a queued or addressed Message — which reaches the
|
|
265
|
-
// Role through a new AgentRun's frozen Context pack — a steer is pushed into
|
|
266
|
-
// the *current* turn and is never captured by any frozen snapshot. So the
|
|
267
|
-
// steer header directs the recipient to reconcile the input "through your
|
|
268
|
-
// authorized Context read path"; that path must therefore resolve the exact
|
|
269
|
-
// steer Message it names. Authorize the steers addressed to this caller's
|
|
270
|
-
// exact current Assignment scope as a live delta — the same way untargeted
|
|
271
|
-
// user intent is layered on — WITHOUT expanding the frozen Assignment's
|
|
272
|
-
// readableRefs. Scope is the tightest signal a steer carries (it never gains
|
|
273
|
-
// a continuation runId), so a steer for a different WorkItem/Round is not
|
|
274
|
-
// authorized, and only genuine steers gain this read (an ordinary addressed
|
|
275
|
-
// Message still requires its own frozen delta).
|
|
247
|
+
// Shared current user intent and this exact Assignment's live steers augment
|
|
248
|
+
// the frozen refs. Query identities only: another Role's results, ordinary
|
|
249
|
+
// addressed messages and Leader steers remain outside this scope.
|
|
276
250
|
const run = store.getRun(taskId, caller.currentRunId);
|
|
277
|
-
const
|
|
278
|
-
.
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
&& message.recipient.reviewRoundId === run?.reviewRoundId)
|
|
282
|
-
.map(message => message.id));
|
|
283
|
-
for (const id of steerMessageIds)
|
|
251
|
+
const current = store.contextInputReferences(taskId, {
|
|
252
|
+
roleName: caller.roleName, workItemId: run?.workItemId, reviewRoundId: run?.reviewRoundId
|
|
253
|
+
});
|
|
254
|
+
for (const id of current.messages)
|
|
284
255
|
allow.add(`task-message:${id}`);
|
|
285
|
-
for (const
|
|
286
|
-
|
|
287
|
-
|
|
256
|
+
for (const id of current.events)
|
|
257
|
+
allow.add(`task-event:${id}`);
|
|
258
|
+
if (run?.workItemId !== undefined) {
|
|
259
|
+
for (const job of store.listDurableJobs(taskId)) {
|
|
260
|
+
if (job.owner.kind === "work-item" && job.owner.workItemId === run.workItemId)
|
|
261
|
+
allow.add(`job:${job.id}`);
|
|
288
262
|
}
|
|
289
263
|
}
|
|
290
264
|
}
|
|
291
265
|
return { task, allow, caller };
|
|
292
266
|
}
|
|
267
|
+
/** Independent record commands share Context's exact read boundary, not a
|
|
268
|
+
* second role-based approximation of Assignment visibility. */
|
|
269
|
+
export function contextRecordReader(store, taskId, environment = {}) {
|
|
270
|
+
const { allow } = authorizeContext(store, taskId, environment);
|
|
271
|
+
return (family, id) => isAllowed(allow, family, id);
|
|
272
|
+
}
|
|
273
|
+
export function assertContextRecordReadable(store, taskId, family, id, environment = {}) {
|
|
274
|
+
if (!contextRecordReader(store, taskId, environment)(family, id)) {
|
|
275
|
+
throw usageError("Context reference is outside the caller's Assignment.");
|
|
276
|
+
}
|
|
277
|
+
}
|
|
293
278
|
function isAllowed(allow, family, id) {
|
|
294
279
|
return allow === undefined || allow.has(`${family}:${id}`);
|
|
295
280
|
}
|
|
281
|
+
function allowedIds(allow, family) {
|
|
282
|
+
const prefix = `${family}:`;
|
|
283
|
+
return allow === undefined ? undefined : [...allow]
|
|
284
|
+
.filter(ref => ref.startsWith(prefix)).map(ref => ref.slice(prefix.length));
|
|
285
|
+
}
|
|
296
286
|
function roleProfile(role) {
|
|
297
287
|
return {
|
|
298
288
|
name: role.name, defaultAccess: role.defaultAccess, description: role.description,
|
|
@@ -341,7 +331,10 @@ function inspectValue(store, { task, allow, caller }, { store: family, refId })
|
|
|
341
331
|
return store.getWorkItem(taskId, parts[0])?.candidates
|
|
342
332
|
.find((candidate) => candidate.id === parts[1]) ?? null;
|
|
343
333
|
}
|
|
344
|
-
case "task-message":
|
|
334
|
+
case "task-message":
|
|
335
|
+
case "publication":
|
|
336
|
+
case "task-event":
|
|
337
|
+
return store.queryContextRecords(taskId, { family, ids: [refId], limit: 1 }).records[0]?.value ?? null;
|
|
345
338
|
case "run": return store.getRun(taskId, refId);
|
|
346
339
|
case "source-run": {
|
|
347
340
|
if (!allow?.has(`source-turn:${refId}`))
|
|
@@ -393,19 +386,27 @@ function inspectValue(store, { task, allow, caller }, { store: family, refId })
|
|
|
393
386
|
case "task-decision": return store.getDecision(taskId, refId);
|
|
394
387
|
case "task-milestone": return store.getMilestone(taskId, refId);
|
|
395
388
|
case "job": return store.getDurableJob(taskId, refId);
|
|
396
|
-
case "publication": return store.listPublicationReferences(taskId).find((item) => item.id === refId) ?? null;
|
|
397
|
-
case "task-event": return store.listEvents(taskId).find((event) => event.id === refId) ?? null;
|
|
398
389
|
default: return null;
|
|
399
390
|
}
|
|
400
391
|
}
|
|
401
|
-
function
|
|
402
|
-
const
|
|
403
|
-
const
|
|
392
|
+
function authorizedSources(store, { task, allow, caller }) {
|
|
393
|
+
const taskId = task.id;
|
|
394
|
+
const sources = [];
|
|
404
395
|
const add = (family, id, value) => {
|
|
405
396
|
if (value !== null && isAllowed(allow, family, id)) {
|
|
406
|
-
|
|
397
|
+
sources.push({ count: 1, read: () => [materialize(family, id, value)] });
|
|
407
398
|
}
|
|
408
399
|
};
|
|
400
|
+
const query = (family, options = {}) => {
|
|
401
|
+
const selection = { ...options, family, ids: allowedIds(allow, family) };
|
|
402
|
+
const { count } = store.queryContextRecords(taskId, { ...selection, limit: 0 });
|
|
403
|
+
sources.push({ count, *read() {
|
|
404
|
+
for (const { id, value } of store.queryContextRecords(taskId, {
|
|
405
|
+
...selection, limit: MAX_RECORDS
|
|
406
|
+
}).records)
|
|
407
|
+
yield materialize(family, id, value);
|
|
408
|
+
} });
|
|
409
|
+
};
|
|
409
410
|
add("task", taskId, task);
|
|
410
411
|
add("task-brief", taskId, store.getTaskBrief(taskId));
|
|
411
412
|
if (allow === undefined)
|
|
@@ -425,65 +426,52 @@ function authorizedEntries(store, taskId, environment) {
|
|
|
425
426
|
}
|
|
426
427
|
}
|
|
427
428
|
// Current responsibilities and unanswered inputs precede historical bulk.
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
for (const
|
|
431
|
-
add("work-item",
|
|
432
|
-
if (allow?.has(`accepted-work-item:${item.id}`))
|
|
433
|
-
add("accepted-work-item", item.id, item);
|
|
434
|
-
for (const candidate of [...item.candidates].reverse())
|
|
435
|
-
add("candidate", `${item.id}/${candidate.id}`, candidate);
|
|
429
|
+
query("input-request", { statuses: ["open"] });
|
|
430
|
+
query("work-item");
|
|
431
|
+
for (const id of allowedIds(allow, "accepted-work-item") ?? []) {
|
|
432
|
+
add("accepted-work-item", id, store.getWorkItem(taskId, id));
|
|
436
433
|
}
|
|
434
|
+
query("candidate");
|
|
437
435
|
// File artifacts are not enumerated into the synchronous Context listing: the
|
|
438
436
|
// core cursor does not cover file edits and an ambient directory index here
|
|
439
437
|
// would be the forbidden update-time mirror (§3.7). Current artifacts are
|
|
440
438
|
// listed on demand via the async `artifact.list` capability; frozen Candidate
|
|
441
439
|
// artifacts remain reachable as commit-pinned pointers under their Candidate.
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
for (const workspace of store.listManagedWorkspaces(taskId))
|
|
445
|
-
add("managed-workspace", managedWorkspaceKey(workspace.owner), workspace);
|
|
440
|
+
query("environment-preparation");
|
|
441
|
+
query("managed-workspace");
|
|
446
442
|
if (caller?.currentRunId !== undefined) {
|
|
447
443
|
add("managed-workspace", `${taskId}/${caller.roleName}`, store.getRun(taskId, caller.currentRunId)?.workspace ?? null);
|
|
448
444
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
add("source-run", run.id, sourceRunContextValue(run));
|
|
445
|
+
query("change-set");
|
|
446
|
+
query("task-message", { descending: true });
|
|
447
|
+
query("run", { descending: true });
|
|
448
|
+
for (const id of allowedIds(allow, "source-turn") ?? []) {
|
|
449
|
+
const run = store.getRun(taskId, id);
|
|
450
|
+
if (run !== null)
|
|
451
|
+
add("source-run", id, sourceRunContextValue(run));
|
|
457
452
|
}
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
for (const request of store.listInputRequests(taskId).filter((r) => r.status !== "open"))
|
|
461
|
-
add("input-request", request.id, request);
|
|
453
|
+
query("review-round", { descending: true });
|
|
454
|
+
query("input-request", { excludeStatuses: ["open"] });
|
|
462
455
|
if (allow === undefined) {
|
|
463
456
|
add("mailbox", "task", store.getWorkMailbox({ kind: "task", taskId }));
|
|
464
457
|
for (const role of store.listRoles(taskId)) {
|
|
465
458
|
add("mailbox", `role:${role.name}`, store.getWorkMailbox({ kind: "role", taskId, roleName: role.name }));
|
|
466
459
|
}
|
|
467
460
|
}
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
for (const publication of store.listPublicationReferences(taskId))
|
|
475
|
-
add("publication", publication.id, publication);
|
|
476
|
-
for (const event of store.listEvents(taskId).reverse())
|
|
477
|
-
add("task-event", event.id, event);
|
|
478
|
-
return entries;
|
|
461
|
+
query("task-decision");
|
|
462
|
+
query("task-milestone");
|
|
463
|
+
query("job");
|
|
464
|
+
query("publication");
|
|
465
|
+
query("task-event", { descending: true });
|
|
466
|
+
return sources;
|
|
479
467
|
}
|
|
480
468
|
export function materialize(store, refId, value) {
|
|
481
469
|
const digest = contextContentDigest(value);
|
|
482
470
|
const record = value;
|
|
483
471
|
return { ref: { store, refId, revision: String(record.revision ?? record.updatedAt ?? record.createdAt ?? digest), digest }, value };
|
|
484
472
|
}
|
|
485
|
-
function currentCursor(store, taskId
|
|
486
|
-
return { taskId, sequence:
|
|
473
|
+
function currentCursor(store, taskId) {
|
|
474
|
+
return { taskId, sequence: store.latestEventSequence(taskId), revision: store.getStateRevision() };
|
|
487
475
|
}
|
|
488
476
|
function sequence(id) {
|
|
489
477
|
const result = /^event-(\d+)$/.exec(id);
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { operationalTaskRecords } from "../task/taskRecordRetirement.js";
|
|
2
|
+
/** Exact results named by a notification delta, including Runs older than its cursor. */
|
|
3
|
+
export function referencedWakeRunIds(runs, allEvents, terminalEvents) {
|
|
4
|
+
const runsById = new Map(operationalTaskRecords(runs, allEvents, "run").map(run => [run.id, run]));
|
|
5
|
+
return [...new Set(terminalEvents.flatMap(event => {
|
|
6
|
+
if (!["run.completed", "run.failed", "run.cancelled"].includes(event.type))
|
|
7
|
+
return [];
|
|
8
|
+
const runId = event.payload.runId;
|
|
9
|
+
return runId !== undefined && runsById.has(runId) ? [runId] : [];
|
|
10
|
+
}))];
|
|
11
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { usageError } from "../errors/cliError.js";
|
|
2
|
+
import { resolveTaskAgentCapabilities } from "../executor/taskAgentCapabilities.js";
|
|
3
|
+
import { requireManagedGlobalCaller } from "../runtime/managedCaller.js";
|
|
4
|
+
const READER_FIELDS = [
|
|
5
|
+
"YUI_SESSION_SCOPE", "YUI_TASK_ID", "YUI_ROLE", "YUI_NATIVE_SESSION_ID",
|
|
6
|
+
"YUI_AGENT_ID", "YUI_ADAPTER_ID", "YUI_WORKSPACE", "CODEX_THREAD_ID"
|
|
7
|
+
];
|
|
8
|
+
/** Transport only the existing reader identity, never the caller's credentials. */
|
|
9
|
+
export function capabilityReaderIdentity(environment) {
|
|
10
|
+
if (environment.YUI_SESSION_SCOPE === undefined) {
|
|
11
|
+
if (["YUI_ROLE", "YUI_AGENT_ID", "YUI_NATIVE_SESSION_ID"].some(key => environment[key] !== undefined)) {
|
|
12
|
+
throw usageError("Managed reader identity is incomplete.");
|
|
13
|
+
}
|
|
14
|
+
return {};
|
|
15
|
+
}
|
|
16
|
+
return Object.fromEntries(READER_FIELDS.flatMap(key => environment[key] === undefined ? [] : [[key, environment[key]]]));
|
|
17
|
+
}
|
|
18
|
+
/** Metadata discovery uses the Controller's launch environment. No start,
|
|
19
|
+
* retry, credential forwarding or Role mutation belongs to this read port. */
|
|
20
|
+
export async function readTaskAgentCapabilities(store, catalogs, params) {
|
|
21
|
+
if (!params || typeof params !== "object" || Array.isArray(params)) {
|
|
22
|
+
throw usageError("Task capability query parameters are invalid.");
|
|
23
|
+
}
|
|
24
|
+
const input = params;
|
|
25
|
+
if (Object.keys(input).some(key => !["taskId", "roleName", "errorId", "refresh", "reader"].includes(key))
|
|
26
|
+
|| typeof input.taskId !== "string" || input.taskId.length === 0
|
|
27
|
+
|| typeof input.roleName !== "string" || input.roleName.length === 0
|
|
28
|
+
|| (input.errorId !== undefined && (typeof input.errorId !== "string" || input.errorId.length === 0))
|
|
29
|
+
|| (input.refresh !== undefined && typeof input.refresh !== "boolean")
|
|
30
|
+
|| !input.reader || typeof input.reader !== "object" || Array.isArray(input.reader)) {
|
|
31
|
+
throw usageError("Task capability query parameters are invalid.");
|
|
32
|
+
}
|
|
33
|
+
const reader = {};
|
|
34
|
+
for (const [key, value] of Object.entries(input.reader)) {
|
|
35
|
+
if (!READER_FIELDS.includes(key) || typeof value !== "string") {
|
|
36
|
+
throw usageError("Task capability reader contains unsupported fields.");
|
|
37
|
+
}
|
|
38
|
+
reader[key] = value;
|
|
39
|
+
}
|
|
40
|
+
if (Object.keys(reader).length > 0 && reader.YUI_SESSION_SCOPE !== "task"
|
|
41
|
+
&& reader.YUI_SESSION_SCOPE !== "global")
|
|
42
|
+
throw usageError("Managed reader scope is invalid.");
|
|
43
|
+
if (reader.YUI_SESSION_SCOPE === "global") {
|
|
44
|
+
const caller = requireManagedGlobalCaller(store, reader);
|
|
45
|
+
if (caller.roleName !== "operator")
|
|
46
|
+
throw usageError("Only the Operator may query another Role's native configuration.");
|
|
47
|
+
}
|
|
48
|
+
const request = resolveTaskAgentCapabilities({
|
|
49
|
+
taskId: input.taskId, roleName: input.roleName,
|
|
50
|
+
...(input.errorId === undefined ? {} : { errorId: input.errorId }),
|
|
51
|
+
refresh: input.refresh === true,
|
|
52
|
+
}, store, reader);
|
|
53
|
+
const resolved = await catalogs.resolve(request.input);
|
|
54
|
+
const result = {
|
|
55
|
+
...resolved, context: { ...request.context, environmentSource: "controller" }
|
|
56
|
+
};
|
|
57
|
+
return result;
|
|
58
|
+
}
|
|
@@ -238,17 +238,12 @@ export class AgentRuntimeObserver {
|
|
|
238
238
|
}
|
|
239
239
|
activeSources() {
|
|
240
240
|
const result = [];
|
|
241
|
-
const
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
.sort(numericCompare)
|
|
248
|
-
.map((taskId) => this.store.getTask(taskId))
|
|
249
|
-
.filter((task) => (task !== null
|
|
250
|
-
&& task.status === "active"
|
|
251
|
-
&& task.executionGate.state === "enabled"));
|
|
241
|
+
const activeTasks = [...new Set(this.store.listActiveTaskIds())]
|
|
242
|
+
.sort(numericCompare)
|
|
243
|
+
.map((taskId) => this.store.getTask(taskId))
|
|
244
|
+
.filter((task) => (task !== null
|
|
245
|
+
&& task.status === "active"
|
|
246
|
+
&& task.executionGate.state === "enabled"));
|
|
252
247
|
for (const task of activeTasks) {
|
|
253
248
|
// A Task still incurs one O(E) event projection. Group those observations
|
|
254
249
|
// by AgentRun and sort each group once so every active AgentRun can reuse the same
|