@zq-silk/yui 0.15.11 → 0.16.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (280) hide show
  1. package/ARCHITECTURE.md +11 -6
  2. package/ARCHITECTURE.zh-CN.md +8 -4
  3. package/README.md +13 -5
  4. package/dist/agentRun/agentRun.js +3 -0
  5. package/dist/artifacts/artifactCommitLock.js +16 -55
  6. package/dist/artifacts/taskArtifactRepository.js +3 -3
  7. package/dist/cli/agentConfigurationPicker.js +1 -1
  8. package/dist/cli/commandCatalog.js +92 -48
  9. package/dist/cli/completion.js +18 -18
  10. package/dist/cli/completionWizard.js +1 -1
  11. package/dist/cli/dynamicCompletion.js +1 -1
  12. package/dist/cli/interactionPolicy.js +11 -3
  13. package/dist/cli/invocationAuthority.js +64 -0
  14. package/dist/cli/managedDiagnostics.js +2 -2
  15. package/dist/cli/parseRepeatable.js +35 -0
  16. package/dist/cli/updatePorts.js +26 -5
  17. package/dist/cli.js +1303 -1235
  18. package/dist/commands/agentCommands.js +4 -4
  19. package/dist/commands/capabilityCommands.js +1 -1
  20. package/dist/commands/configCommands.js +6 -57
  21. package/dist/commands/configOverview.js +2 -2
  22. package/dist/commands/durableJobCommands.js +12 -6
  23. package/dist/commands/executionAuditCommands.js +10 -0
  24. package/dist/commands/globalRoleCommands.js +78 -29
  25. package/dist/commands/grantCommands.js +0 -3
  26. package/dist/commands/jobCommands.js +1 -4
  27. package/dist/commands/operatorCommands.js +3 -3
  28. package/dist/commands/projectCommands.js +45 -25
  29. package/dist/commands/resourcesCommands.js +10 -40
  30. package/dist/commands/roleConfiguration.js +2 -6
  31. package/dist/commands/taskActivationCommands.js +4 -8
  32. package/dist/commands/taskAgentCapabilities.js +9 -0
  33. package/dist/commands/taskCommandSupport.js +155 -0
  34. package/dist/commands/taskCommandTypes.js +1 -0
  35. package/dist/commands/taskCommands.js +355 -732
  36. package/dist/commands/taskCompletionGate.js +1 -1
  37. package/dist/commands/taskExecutionCommands.js +1 -1
  38. package/dist/commands/taskFactCommands.js +325 -0
  39. package/dist/commands/taskInputCommands.js +12 -42
  40. package/dist/commands/taskIntegrationCommands.js +20 -58
  41. package/dist/commands/taskNextActionCommand.js +7 -6
  42. package/dist/commands/taskPublicationAdoptCommand.js +127 -0
  43. package/dist/commands/taskPublicationCommands.js +12 -3
  44. package/dist/commands/taskPublicationVerifyCommand.js +24 -40
  45. package/dist/commands/taskRemoteDeliveryCommand.js +5 -86
  46. package/dist/commands/taskUpstreamCommands.js +19 -8
  47. package/dist/commands/workflowCommands.js +6 -1
  48. package/dist/completion/completionInstaller.js +26 -26
  49. package/dist/completion/completionState.js +26 -26
  50. package/dist/completion/fileCompletionManager.js +6 -11
  51. package/dist/config/configCatalog.js +0 -2
  52. package/dist/config/timeZone.js +14 -0
  53. package/dist/config/yuiConfig.js +0 -29
  54. package/dist/context/runContextPack.js +5 -3
  55. package/dist/context/taskCatalog.js +185 -0
  56. package/dist/context/taskContext.js +152 -151
  57. package/dist/context/wakeRunReferences.js +11 -0
  58. package/dist/controller/agentCapabilities.js +58 -0
  59. package/dist/controller/agentRuntimeObserver.js +6 -11
  60. package/dist/controller/clientRuntime.js +22 -14
  61. package/dist/controller/controller.js +20 -24
  62. package/dist/controller/fileSchedulerStoreAdapter.js +191 -225
  63. package/dist/controller/globalInputDelivery.js +13 -0
  64. package/dist/controller/jobClient.js +4 -4
  65. package/dist/controller/jobControl.js +60 -25
  66. package/dist/controller/jobSupervisor.js +4 -4
  67. package/dist/controller/providerRetryAdmission.js +100 -0
  68. package/dist/controller/providerRetryDelivery.js +218 -0
  69. package/dist/controller/runtime.js +85 -43
  70. package/dist/controller/runtimeEventProcessor.js +0 -5
  71. package/dist/controller/sessionOwnerReconciliation.js +5 -0
  72. package/dist/controller/taskAgentError.js +35 -0
  73. package/dist/coordination/workMailboxQueue.js +2 -24
  74. package/dist/core/controllerClient.js +2 -2
  75. package/dist/core/fileLockOwner.js +74 -0
  76. package/dist/decision/decision.js +17 -0
  77. package/dist/doctor/doctor.js +18 -9
  78. package/dist/event/taskEvent.js +12 -0
  79. package/dist/execution/workItemExecutionProjection.js +2 -2
  80. package/dist/executor/agentCapabilityConfig.js +43 -0
  81. package/dist/executor/agentConfigurationCatalog.js +49 -11
  82. package/dist/executor/agentExecutor.js +5 -8
  83. package/dist/executor/fileRoleLaunchPlanner.js +11 -53
  84. package/dist/executor/taskAgentCapabilities.js +64 -0
  85. package/dist/integration/deliveryObligation.js +1 -69
  86. package/dist/integration/gitIntegrationService.js +79 -90
  87. package/dist/integration/integrationAttempt.js +3 -14
  88. package/dist/integration/integrationSourceApplication.js +4 -12
  89. package/dist/integration/manifestTags.js +2 -2
  90. package/dist/job/durableJob.js +4 -25
  91. package/dist/job/jobAssignmentScope.js +22 -0
  92. package/dist/job/jobOperation.js +16 -0
  93. package/dist/job/jobRunner.js +2 -1
  94. package/dist/job/stepDirectory.js +15 -0
  95. package/dist/kernel/builtinCapabilities.js +15 -11
  96. package/dist/kernel/kernelPorts.js +1 -17
  97. package/dist/lifecycle/exactRunTerminalization.js +5 -10
  98. package/dist/message/globalProviderRetry.js +15 -0
  99. package/dist/message/inputControlResolution.js +3 -4
  100. package/dist/message/message.js +17 -20
  101. package/dist/message/messageContinuation.js +1 -1
  102. package/dist/milestone/milestone.js +11 -0
  103. package/dist/observability/executionAudit.js +19 -0
  104. package/dist/observability/orchestrationMetrics.js +24 -1
  105. package/dist/observability/runtimeIdentity.js +1 -17
  106. package/dist/output/agentConfigurationPresentation.js +2 -0
  107. package/dist/output/timePresentation.js +1 -14
  108. package/dist/plugins/pluginService.js +12 -4
  109. package/dist/release/releaseWorkflowPorts.js +45 -96
  110. package/dist/release/runtimeRelease.js +9 -15
  111. package/dist/repository/gitWorkspace.js +371 -105
  112. package/dist/repository/projectMaintenanceLock.js +98 -82
  113. package/dist/repository/taskBaseFreshness.js +1 -1
  114. package/dist/repository/taskWorkspaceCoordinator.js +82 -144
  115. package/dist/repository/taskWorkspacePreparer.js +113 -47
  116. package/dist/repository/workspaceCleanupInspection.js +187 -0
  117. package/dist/resources/autoResourceGc.js +4 -77
  118. package/dist/resources/liveReferences.js +7 -54
  119. package/dist/resources/resourceDiscovery.js +5 -118
  120. package/dist/resources/resourceGc.js +233 -181
  121. package/dist/resources/resourceRegistrar.js +5 -4
  122. package/dist/resources/resourceRegistry.js +3 -37
  123. package/dist/resources/resourceRegistryStore.js +13 -36
  124. package/dist/resources/sqliteResourceRegistry.js +43 -26
  125. package/dist/review/deltaRecheck.js +1 -1
  126. package/dist/review/reviewAcceptance.js +1 -1
  127. package/dist/review/reviewDecision.js +1 -1
  128. package/dist/review/reviewRound.js +8 -7
  129. package/dist/review/taskFinalReviewContractResolution.js +1 -1
  130. package/dist/runtime/acpProtocol.js +4 -51
  131. package/dist/runtime/acpSession.js +10 -75
  132. package/dist/runtime/acpSessionConfiguration.js +1 -8
  133. package/dist/runtime/agentEndpoint.js +3 -2
  134. package/dist/runtime/agentError.js +5 -3
  135. package/dist/runtime/agentFailureContext.js +43 -0
  136. package/dist/runtime/agentHost.js +42 -25
  137. package/dist/runtime/builtinAgentErrorMappers.js +91 -0
  138. package/dist/runtime/codexAppServerRuntime.js +34 -3
  139. package/dist/runtime/codexInteractiveHost.js +2 -2
  140. package/dist/runtime/index.js +0 -1
  141. package/dist/runtime/managedCaller.js +21 -1
  142. package/dist/runtime/providerControl.js +5 -1
  143. package/dist/runtime/providerErrors.js +38 -0
  144. package/dist/runtime/providerRetry.js +198 -0
  145. package/dist/runtime/providerRuntimeIdentity.js +28 -2
  146. package/dist/runtime/runtimeObservation.js +0 -5
  147. package/dist/runtime/runtimeSessionCandidate.js +2 -3
  148. package/dist/runtime/sessionOwnerIdentity.js +1 -1
  149. package/dist/runtime/sessionTokenMetrics.js +15 -5
  150. package/dist/runtime/structuredProviderHost.js +10 -43
  151. package/dist/runtime/taskUsageMetrics.js +275 -0
  152. package/dist/runtime/tmuxAdapters.js +6 -8
  153. package/dist/scheduler/activeRoleRunDelivery.js +19 -7
  154. package/dist/scheduler/leaderWakeupProcessor.js +24 -6
  155. package/dist/scheduler/operatorEvent.js +12 -20
  156. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  157. package/dist/scheduler/ports.js +5 -8
  158. package/dist/scheduler/roleRunLiveness.js +4 -4
  159. package/dist/scheduler/roleRunStall.js +16 -23
  160. package/dist/scheduler/taskExecutionProjection.js +49 -36
  161. package/dist/scheduler/taskObservabilityProjection.js +6 -45
  162. package/dist/scheduler/taskWake.js +5 -10
  163. package/dist/scheduler/wakeReason.js +2 -0
  164. package/dist/scheduler/wakeupQueue.js +2 -4
  165. package/dist/setup/setupCommand.js +1 -1
  166. package/dist/storage/contextRecords.js +106 -0
  167. package/dist/storage/currentTaskStore.js +1 -1
  168. package/dist/storage/homeLayout.js +13 -17
  169. package/dist/storage/migrations/agentFailureContext.js +22 -0
  170. package/dist/storage/migrations/currentInputContract.js +86 -0
  171. package/dist/storage/migrations/currentRuntimeContract.js +228 -0
  172. package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
  173. package/dist/storage/migrations/integrationContinuation.js +5 -4
  174. package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
  175. package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
  176. package/dist/storage/migrations/verificationPlanV1.js +162 -0
  177. package/dist/storage/migrations/verificationPolicy.js +74 -0
  178. package/dist/storage/migrations/workItemHistory.js +46 -0
  179. package/dist/storage/persistenceWorker.js +12 -4
  180. package/dist/storage/recordValidation.js +87 -0
  181. package/dist/storage/sqliteSchema.js +173 -1
  182. package/dist/storage/sqliteStore.js +172 -161
  183. package/dist/storage/storageSchema.js +2 -14
  184. package/dist/storage/storageVersions.js +1 -1
  185. package/dist/storage/storeRpc.js +16 -7
  186. package/dist/storage/taskCatalog.js +123 -0
  187. package/dist/storage/taskStore.js +5 -7
  188. package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
  189. package/dist/task/archiveDiagnostics.js +1 -0
  190. package/dist/task/archivePreflight.js +124 -0
  191. package/dist/task/completionReadiness.js +5 -24
  192. package/dist/task/draftPlan.js +0 -53
  193. package/dist/task/nextAction.js +7 -13
  194. package/dist/task/publicationAdoption.js +56 -0
  195. package/dist/task/publicationReference.js +10 -0
  196. package/dist/task/remoteDelivery.js +31 -16
  197. package/dist/task/remoteDeliveryService.js +89 -0
  198. package/dist/task/taskActivation.js +2 -25
  199. package/dist/task/taskActivationService.js +0 -2
  200. package/dist/task/taskRecordReference.js +0 -1
  201. package/dist/task/taskSubmission.js +3 -14
  202. package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
  203. package/dist/telemetry/sqliteTelemetryStore.js +49 -49
  204. package/dist/telemetry/telemetryWiring.js +4 -3
  205. package/dist/tmux/tmuxManager.js +29 -20
  206. package/dist/verification/gateArtifact.js +15 -24
  207. package/dist/verification/gateArtifactStore.js +14 -7
  208. package/dist/verification/verificationGateService.js +29 -88
  209. package/dist/verification/verificationPlan.js +27 -72
  210. package/dist/web/assets/client/app.js +92 -17
  211. package/dist/web/assets/client/components.js +55 -13
  212. package/dist/web/assets/client/i18n.js +72 -4
  213. package/dist/web/assets/client/taskSurface.js +6 -5
  214. package/dist/web/assets/client/view.js +35 -7
  215. package/dist/web/assets/shell.js +6 -0
  216. package/dist/web/assets/styles/layout.js +7 -0
  217. package/dist/web/webServer.js +12 -3
  218. package/dist/web/webSnapshot.js +14 -85
  219. package/dist/web/webTaskSurface.js +17 -46
  220. package/dist/workItem/workItem.js +1 -12
  221. package/dist/workspace/cleanupInspection.js +63 -0
  222. package/dist/workspace/workItemChangeSetManager.js +110 -50
  223. package/docs/agent-result-consumption.md +4 -0
  224. package/docs/agent-result-consumption.zh-CN.md +3 -0
  225. package/docs/agent-runtime-drivers.md +7 -0
  226. package/docs/agent-runtime-drivers.zh-CN.md +5 -0
  227. package/docs/architecture/README.md +2 -0
  228. package/docs/architecture/README.zh-CN.md +3 -1
  229. package/docs/architecture/capabilities-and-resources.md +42 -5
  230. package/docs/architecture/capabilities-and-resources.zh-CN.md +33 -3
  231. package/docs/managed-turn-and-session-runtime.md +121 -1
  232. package/docs/managed-turn-and-session-runtime.zh-CN.md +96 -2
  233. package/docs/observability/README.md +62 -0
  234. package/docs/observability/README.zh-CN.md +47 -0
  235. package/docs/plugin-sdk.md +4 -2
  236. package/docs/project-refresh.md +77 -0
  237. package/docs/project-refresh.zh-CN.md +59 -0
  238. package/docs/provider-retry.md +70 -0
  239. package/docs/provider-runtime.md +3 -1
  240. package/docs/provider-runtime.zh-CN.md +4 -2
  241. package/docs/release-workflow.md +190 -7
  242. package/docs/release-workflow.zh-CN.md +139 -5
  243. package/docs/roles-and-configuration.md +29 -0
  244. package/docs/roles-and-configuration.zh-CN.md +21 -0
  245. package/docs/sqlite-control-plane-design.md +16 -3
  246. package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
  247. package/docs/task-dag-semantics.md +3 -3
  248. package/docs/task-dag-semantics.zh-CN.md +2 -2
  249. package/docs/task-delivery.md +223 -17
  250. package/docs/task-delivery.zh-CN.md +171 -14
  251. package/docs/task-discovery.md +101 -0
  252. package/docs/task-discovery.zh-CN.md +85 -0
  253. package/docs/testing/verification-levels.md +161 -9
  254. package/docs/testing/verification-levels.zh-CN.md +116 -9
  255. package/i18n/README.zh-CN.md +13 -7
  256. package/package.json +1 -1
  257. package/skills/yui-leader/SKILL.md +5 -0
  258. package/skills/yui-leader/references/execution.md +3 -2
  259. package/skills/yui-leader/references/integration.md +9 -5
  260. package/skills/yui-leader/references/planning.md +9 -3
  261. package/skills/yui-operator/SKILL.md +24 -8
  262. package/skills/yui-reviewer/SKILL.md +4 -0
  263. package/skills/yui-runtime/SKILL.md +17 -2
  264. package/skills/yui-runtime/references/publication.md +26 -4
  265. package/skills/yui-runtime/references/recovery.md +71 -0
  266. package/dist/agent/agentRegistry.js +0 -10
  267. package/dist/agentRun/runIdentity.js +0 -22
  268. package/dist/commands/deliveryGuardPreflight.js +0 -30
  269. package/dist/commands/taskIntegrationQueueCommands.js +0 -226
  270. package/dist/commands/taskOverviewCommand.js +0 -376
  271. package/dist/completion/completionWizard.js +0 -125
  272. package/dist/context/dispatchContext.js +0 -110
  273. package/dist/context/wakeNotification.js +0 -144
  274. package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
  275. package/dist/integration/integrationQueueEntry.js +0 -191
  276. package/dist/integration/integrationQueueService.js +0 -810
  277. package/dist/release/fakeReleasePorts.js +0 -55
  278. package/dist/runtime/sessionOwnerRegistry.js +0 -137
  279. package/dist/task/deliveryGuard.js +0 -226
  280. /package/dist/{commands/taskActor.js → task/taskAuthority.js} +0 -0
@@ -1,12 +1,14 @@
1
- import { usageError, taskNotFound } from "../errors/cliError.js";
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
12
  const MAX_RECORDS = 256;
11
13
  const MAX_VALUE_BYTES = 4096;
12
14
  const MAX_PAGE_BYTES = 128 * 1024;
@@ -17,64 +19,74 @@ const MAX_ATTENTION_REFS = 8;
17
19
  * Every public path checks the same scope before reading, counting or paging.
18
20
  */
19
21
  export function readTaskContext(store, taskId, environment = {}) {
20
- return store.transaction((reader) => {
21
- const entries = authorizedEntries(reader, taskId, environment);
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);
22
26
  const cursor = currentCursor(reader, taskId);
23
27
  // Reserve a small, classified view before ordinary history consumes the
24
28
  // page. Counts and samples come from the exact same authorized read.
25
- const attention = summarizeAttention(entries);
29
+ const attention = summarizeAttention(reader, taskId, scope.allow);
26
30
  let bytes = Buffer.byteLength(JSON.stringify(attention));
27
31
  const records = [];
28
- const runtimeEvents = entries.some((entry) => entry.ref.store === "run") ? reader.listEvents(taskId) : [];
29
- for (const entry of entries) {
30
- const valueBytes = Buffer.byteLength(JSON.stringify(entry.value));
31
- const content = valueBytes > MAX_VALUE_BYTES
32
- ? { ref: entry.ref, summary: summarize(entry.value), omitted: true }
33
- : { ...entry, omitted: false };
34
- const record = { ...content, ...(entry.ref.store === "run" ? {
35
- execution: runExecutionObservation(entry.value, reader.getTaskRoleSessionSet(taskId, entry.value.roleName)?.providerBinding, runtimeEvents)
36
- } : {}) };
37
- const size = Buffer.byteLength(JSON.stringify(record));
38
- if (records.length >= MAX_RECORDS || bytes + size > MAX_PAGE_BYTES)
39
- break;
40
- records.push(record);
41
- bytes += size;
42
- }
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
+ }
43
54
  return {
44
55
  taskId, coreCursor: encode(cursor), throughCursor: encode(cursor),
45
56
  attention,
46
- records, count: entries.length,
47
- omitted: { records: entries.length - records.length, values: records.filter((r) => r.omitted).length },
57
+ records, count,
58
+ omitted: { records: count - records.length, values: records.filter((r) => r.omitted).length },
48
59
  observations: [],
49
60
  limits: { records: MAX_RECORDS, valueBytes: MAX_VALUE_BYTES, pageBytes: MAX_PAGE_BYTES,
50
61
  attentionRefsPerCategory: MAX_ATTENTION_REFS, attentionRefBytesPerCategory: MAX_VALUE_BYTES }
51
62
  };
52
63
  });
53
64
  }
54
- function summarizeAttention(entries) {
65
+ function summarizeAttention(store, taskId, allow) {
55
66
  const group = () => ({ count: 0, refs: [], omittedRefs: 0 });
56
67
  const attention = {
57
68
  openInputs: group(), pendingOperations: group(), unknownOperations: group()
58
69
  };
59
- const bytes = { openInputs: 0, pendingOperations: 0, unknownOperations: 0 };
60
- for (const entry of entries) {
61
- const status = entry.value.status;
62
- const category = entry.ref.store === "input-request" && status === "open" ? "openInputs"
63
- : entry.ref.store === "job" && status === "unknown-needs-attention" ? "unknownOperations"
64
- : entry.ref.store === "job" && (status === "queued" || status === "running") ? "pendingOperations"
65
- : undefined;
66
- if (category === undefined)
67
- 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
+ });
68
78
  const summary = attention[category];
69
- summary.count++;
70
- const size = Buffer.byteLength(JSON.stringify(entry.ref)) + 1;
71
- if (summary.refs.length < MAX_ATTENTION_REFS && bytes[category] + size <= MAX_VALUE_BYTES) {
72
- summary.refs.push(entry.ref);
73
- bytes[category] += size;
74
- }
75
- else {
76
- summary.omittedRefs++;
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;
77
88
  }
89
+ summary.omittedRefs = summary.count - summary.refs.length;
78
90
  }
79
91
  return attention;
80
92
  }
@@ -83,11 +95,10 @@ function summarizeAttention(entries) {
83
95
  * A continuation carries the first page's fixed upper event bound.
84
96
  */
85
97
  export function readTaskContextDelta(store, taskId, input, environment = {}) {
86
- return store.transaction((reader) => {
98
+ return store.readTransaction((reader) => {
87
99
  const { allow } = authorizeContext(reader, taskId, environment);
88
- const history = reader.listEvents(taskId);
89
100
  const start = decode(input.after, taskId);
90
- const now = currentCursor(reader, taskId, history);
101
+ const now = currentCursor(reader, taskId);
91
102
  const continuation = input.continuation === undefined ? undefined : decodePage(input.continuation, taskId);
92
103
  const bound = continuation ?? now;
93
104
  const after = continuation?.after ?? start.sequence;
@@ -99,13 +110,14 @@ export function readTaskContextDelta(store, taskId, input, environment = {}) {
99
110
  if (!Number.isSafeInteger(limit) || limit < 1 || limit > MAX_EVENTS) {
100
111
  throw usageError(`Context delta limit must be between 1 and ${MAX_EVENTS}.`);
101
112
  }
102
- const events = history
103
- .filter((event) => isAllowed(allow, "task-event", event.id)
104
- && sequence(event.id) > after && sequence(event.id) <= bound.sequence)
105
- .sort((a, b) => sequence(a.id) - sequence(b.id));
113
+ const selected = reader.queryContextRecords(taskId, {
114
+ family: "task-event", ids: allowedIds(allow, "task-event"),
115
+ afterSequence: after, throughSequence: bound.sequence, limit
116
+ });
106
117
  const page = [];
107
118
  let bytes = 0;
108
- for (const event of events) {
119
+ for (const { value } of selected.records) {
120
+ const event = value;
109
121
  const entry = materialize("task-event", event.id, event);
110
122
  const record = Buffer.byteLength(JSON.stringify(event)) > MAX_VALUE_BYTES
111
123
  ? { ref: entry.ref, omitted: true }
@@ -117,10 +129,10 @@ export function readTaskContextDelta(store, taskId, input, environment = {}) {
117
129
  bytes += size;
118
130
  }
119
131
  const last = page.at(-1)?.ref.refId;
120
- const more = events.length > page.length;
132
+ const more = selected.count > page.length;
121
133
  const through = { taskId, sequence: bound.sequence, revision: bound.revision };
122
134
  return {
123
- taskId, events: page, count: events.length, throughCursor: encode(through),
135
+ taskId, events: page, count: selected.count, throughCursor: encode(through),
124
136
  ...(more && last !== undefined
125
137
  ? { continuation: encode({ ...through, after: sequence(last) }) } : {}),
126
138
  observations: []
@@ -128,7 +140,7 @@ export function readTaskContextDelta(store, taskId, input, environment = {}) {
128
140
  });
129
141
  }
130
142
  export function inspectTaskContext(store, taskId, selector, environment = {}) {
131
- return store.transaction((reader) => {
143
+ return store.readTransaction((reader) => {
132
144
  const scope = authorizeContext(reader, taskId, environment);
133
145
  const value = isAllowed(scope.allow, selector.store, selector.refId)
134
146
  ? inspectValue(reader, scope, selector) : null;
@@ -148,7 +160,7 @@ export function inspectTaskContext(store, taskId, selector, environment = {}) {
148
160
  : undefined;
149
161
  const response = { ...entry, ...(expansion !== undefined && "result" in expansion
150
162
  ? { result: expansion.result } : {}),
151
- ...(selector.store === "run" ? { execution: runExecutionObservation(value, reader.getTaskRoleSessionSet(taskId, value.roleName)?.providerBinding, reader.listEvents(taskId)) } : {}),
163
+ ...(selector.store === "run" ? { execution: runExecutionObservation(value, reader.getTaskRoleSessionSet(taskId, value.roleName)?.providerBinding, reader.listEventsByType(taskId, ["runtime.agent-error"])) } : {}),
152
164
  coreCursor: encode(currentCursor(reader, taskId)) };
153
165
  if (Buffer.byteLength(JSON.stringify(response)) > MAX_INSPECT_BYTES) {
154
166
  throw usageError("Expanded Context exceeds the bounded inspect limit.", undefined, { ref: entry.ref, maxBytes: MAX_INSPECT_BYTES });
@@ -157,7 +169,7 @@ export function inspectTaskContext(store, taskId, selector, environment = {}) {
157
169
  });
158
170
  }
159
171
  export function listContextMessages(store, taskId, environment = {}) {
160
- return store.transaction((reader) => {
172
+ return store.readTransaction((reader) => {
161
173
  const { allow } = authorizeContext(reader, taskId, environment);
162
174
  return reader.listMessages(taskId)
163
175
  .filter((message) => isAllowed(allow, "task-message", message.id))
@@ -201,10 +213,9 @@ export async function withContextObservations(core, providers, timeoutMs = 250)
201
213
  }));
202
214
  return { ...core, observations };
203
215
  }
204
- function authorizeContext(store, taskId, environment) {
216
+ /** Shared current/historical Session read identity; discovery may only narrow it. */
217
+ export function resolveContextReader(store, environment) {
205
218
  const caller = resolveManagedTaskReader(store, environment);
206
- if (caller !== undefined && caller.taskId !== taskId)
207
- throw usageError("Context is outside the caller's Task.");
208
219
  if (caller === undefined && environment.YUI_SESSION_SCOPE === "global" && environment.YUI_ROLE !== "operator") {
209
220
  throw usageError("Only Operator may read Task context from a global Session.");
210
221
  }
@@ -216,6 +227,12 @@ function authorizeContext(store, taskId, environment) {
216
227
  if (environment.YUI_SESSION_SCOPE !== undefined && !["task", "global"].includes(environment.YUI_SESSION_SCOPE)) {
217
228
  throw usageError("Incomplete managed Context caller identity.");
218
229
  }
230
+ return caller;
231
+ }
232
+ function authorizeContext(store, taskId, environment) {
233
+ const caller = resolveContextReader(store, environment);
234
+ if (caller !== undefined && caller.taskId !== taskId)
235
+ throw usageError("Context is outside the caller's Task.");
219
236
  const task = store.getTask(taskId);
220
237
  if (task === null)
221
238
  throw taskNotFound(taskId);
@@ -227,63 +244,45 @@ function authorizeContext(store, taskId, environment) {
227
244
  allow = new Set(pack.authority.readableRefs.map((ref) => `${ref.store}:${ref.refId}`));
228
245
  allow.add(`role:${caller.roleName}`);
229
246
  allow.add(`turn:${caller.currentRunId}`);
230
- // A steer targets exactly one Role's current native Turn and is never shared
231
- // Task intent (decision-3 §9). A Leader steer in particular stores as an
232
- // untargeted `user` Message (no recipient/WorkItem/Run scope, since a Leader
233
- // holds no Assignment), so recognize it explicitly: the shared block below
234
- // must never leak it, and the steer-delta block authorizes it only to its own
235
- // recipient. `interruptThen.reusedInput` is the interrupt-then composition
236
- // whose carried input can itself be a steer.
237
- const isSteerMessage = (message) => message.inputControl?.action === "steer"
238
- || message.interruptThen?.reusedInput?.action === "steer";
239
- // A frozen Assignment is not a cutoff for the Task's current user intent.
240
- // Untargeted human/Operator messages are shared Task requirements; scoped
241
- // messages, other Roles' results, and steers still require an explicit grant.
242
- const sharedMessageIds = new Set(store.listMessages(taskId)
243
- .filter(message => (message.kind === "user" || message.kind === "operator")
244
- && message.recipient === undefined && message.workItemId === undefined
245
- && message.runId === undefined && !isSteerMessage(message))
246
- .map(message => message.id));
247
- for (const id of sharedMessageIds)
248
- allow.add(`task-message:${id}`);
249
- for (const event of store.listEvents(taskId)) {
250
- if (event.type.startsWith("message.") && sharedMessageIds.has(event.payload.messageId)) {
251
- allow.add(`task-event:${event.id}`);
252
- }
253
- }
254
- // A steer targets this Role's exact current native Turn (decision-3 §9,
255
- // message-5 gap E). Unlike a queued or addressed Message — which reaches the
256
- // Role through a new AgentRun's frozen Context pack — a steer is pushed into
257
- // the *current* turn and is never captured by any frozen snapshot. So the
258
- // steer header directs the recipient to reconcile the input "through your
259
- // authorized Context read path"; that path must therefore resolve the exact
260
- // steer Message it names. Authorize the steers addressed to this caller's
261
- // exact current Assignment scope as a live delta — the same way untargeted
262
- // user intent is layered on — WITHOUT expanding the frozen Assignment's
263
- // readableRefs. Scope is the tightest signal a steer carries (it never gains
264
- // a continuation runId), so a steer for a different WorkItem/Round is not
265
- // authorized, and only genuine steers gain this read (an ordinary addressed
266
- // 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.
267
250
  const run = store.getRun(taskId, caller.currentRunId);
268
- const steerMessageIds = new Set(store.listMessages(taskId)
269
- .filter(message => isSteerMessage(message)
270
- && message.recipient?.roleName === caller.roleName
271
- && message.recipient.workItemId === run?.workItemId
272
- && message.recipient.reviewRoundId === run?.reviewRoundId)
273
- .map(message => message.id));
274
- 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)
275
255
  allow.add(`task-message:${id}`);
276
- for (const event of store.listEvents(taskId)) {
277
- if (event.type.startsWith("message.") && steerMessageIds.has(event.payload.messageId)) {
278
- allow.add(`task-event:${event.id}`);
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}`);
279
262
  }
280
263
  }
281
264
  }
282
265
  return { task, allow, caller };
283
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
+ }
284
278
  function isAllowed(allow, family, id) {
285
279
  return allow === undefined || allow.has(`${family}:${id}`);
286
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
+ }
287
286
  function roleProfile(role) {
288
287
  return {
289
288
  name: role.name, defaultAccess: role.defaultAccess, description: role.description,
@@ -299,6 +298,8 @@ function inspectValue(store, { task, allow, caller }, { store: family, refId })
299
298
  switch (family) {
300
299
  case "task": return refId === taskId ? task : null;
301
300
  case "task-brief": return refId === taskId ? store.getTaskBrief(taskId) : null;
301
+ case "remote-delivery": return refId === taskId && allow === undefined
302
+ ? projectTaskRemoteDeliveryFromStore(store, task) : null;
302
303
  case "role": return store.getRole(taskId, refId);
303
304
  case "role-profile": {
304
305
  const role = store.getRole(taskId, refId);
@@ -330,7 +331,10 @@ function inspectValue(store, { task, allow, caller }, { store: family, refId })
330
331
  return store.getWorkItem(taskId, parts[0])?.candidates
331
332
  .find((candidate) => candidate.id === parts[1]) ?? null;
332
333
  }
333
- case "task-message": return store.listMessages(taskId).find((message) => message.id === refId) ?? null;
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;
334
338
  case "run": return store.getRun(taskId, refId);
335
339
  case "source-run": {
336
340
  if (!allow?.has(`source-turn:${refId}`))
@@ -382,21 +386,31 @@ function inspectValue(store, { task, allow, caller }, { store: family, refId })
382
386
  case "task-decision": return store.getDecision(taskId, refId);
383
387
  case "task-milestone": return store.getMilestone(taskId, refId);
384
388
  case "job": return store.getDurableJob(taskId, refId);
385
- case "publication": return store.listPublicationReferences(taskId).find((item) => item.id === refId) ?? null;
386
- case "task-event": return store.listEvents(taskId).find((event) => event.id === refId) ?? null;
387
389
  default: return null;
388
390
  }
389
391
  }
390
- function authorizedEntries(store, taskId, environment) {
391
- const { task, allow, caller } = authorizeContext(store, taskId, environment);
392
- const entries = [];
392
+ function authorizedSources(store, { task, allow, caller }) {
393
+ const taskId = task.id;
394
+ const sources = [];
393
395
  const add = (family, id, value) => {
394
396
  if (value !== null && isAllowed(allow, family, id)) {
395
- entries.push(materialize(family, id, value));
397
+ sources.push({ count: 1, read: () => [materialize(family, id, value)] });
396
398
  }
397
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
+ };
398
410
  add("task", taskId, task);
399
411
  add("task-brief", taskId, store.getTaskBrief(taskId));
412
+ if (allow === undefined)
413
+ add("remote-delivery", taskId, projectTaskRemoteDeliveryFromStore(store, task));
400
414
  for (const role of store.listRoles(taskId)) {
401
415
  add("role", role.name, role);
402
416
  add("role-profile", role.name, roleProfile(role));
@@ -412,65 +426,52 @@ function authorizedEntries(store, taskId, environment) {
412
426
  }
413
427
  }
414
428
  // Current responsibilities and unanswered inputs precede historical bulk.
415
- for (const request of store.listInputRequests(taskId).filter((r) => r.status === "open"))
416
- add("input-request", request.id, request);
417
- for (const item of store.listWorkItems(taskId).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))) {
418
- add("work-item", item.id, item);
419
- if (allow?.has(`accepted-work-item:${item.id}`))
420
- add("accepted-work-item", item.id, item);
421
- for (const candidate of [...item.candidates].reverse())
422
- 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));
423
433
  }
434
+ query("candidate");
424
435
  // File artifacts are not enumerated into the synchronous Context listing: the
425
436
  // core cursor does not cover file edits and an ambient directory index here
426
437
  // would be the forbidden update-time mirror (§3.7). Current artifacts are
427
438
  // listed on demand via the async `artifact.list` capability; frozen Candidate
428
439
  // artifacts remain reachable as commit-pinned pointers under their Candidate.
429
- for (const preparation of store.listEnvironmentPreparations(taskId))
430
- add("environment-preparation", preparation.id, preparation);
431
- for (const workspace of store.listManagedWorkspaces(taskId))
432
- add("managed-workspace", managedWorkspaceKey(workspace.owner), workspace);
440
+ query("environment-preparation");
441
+ query("managed-workspace");
433
442
  if (caller?.currentRunId !== undefined) {
434
443
  add("managed-workspace", `${taskId}/${caller.roleName}`, store.getRun(taskId, caller.currentRunId)?.workspace ?? null);
435
444
  }
436
- for (const changeSet of store.listChangeSets(taskId))
437
- add("change-set", changeSet.id, changeSet);
438
- for (const message of store.listMessages(taskId).reverse())
439
- add("task-message", message.id, message);
440
- for (const run of store.listRuns(taskId).reverse()) {
441
- add("run", run.id, run);
442
- if (allow?.has(`source-turn:${run.id}`))
443
- 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));
444
452
  }
445
- for (const round of store.listReviewRounds(taskId).reverse())
446
- add("review-round", round.id, round);
447
- for (const request of store.listInputRequests(taskId).filter((r) => r.status !== "open"))
448
- add("input-request", request.id, request);
453
+ query("review-round", { descending: true });
454
+ query("input-request", { excludeStatuses: ["open"] });
449
455
  if (allow === undefined) {
450
456
  add("mailbox", "task", store.getWorkMailbox({ kind: "task", taskId }));
451
457
  for (const role of store.listRoles(taskId)) {
452
458
  add("mailbox", `role:${role.name}`, store.getWorkMailbox({ kind: "role", taskId, roleName: role.name }));
453
459
  }
454
460
  }
455
- for (const decision of store.listDecisions(taskId))
456
- add("task-decision", decision.id, decision);
457
- for (const milestone of store.listMilestones(taskId))
458
- add("task-milestone", milestone.id, milestone);
459
- for (const job of store.listDurableJobs(taskId))
460
- add("job", job.id, job);
461
- for (const publication of store.listPublicationReferences(taskId))
462
- add("publication", publication.id, publication);
463
- for (const event of store.listEvents(taskId).reverse())
464
- add("task-event", event.id, event);
465
- 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;
466
467
  }
467
- function materialize(store, refId, value) {
468
+ export function materialize(store, refId, value) {
468
469
  const digest = contextContentDigest(value);
469
470
  const record = value;
470
471
  return { ref: { store, refId, revision: String(record.revision ?? record.updatedAt ?? record.createdAt ?? digest), digest }, value };
471
472
  }
472
- function currentCursor(store, taskId, events = store.listEvents(taskId)) {
473
- return { taskId, sequence: events.reduce((max, e) => Math.max(max, sequence(e.id)), 0), revision: store.getStateRevision() };
473
+ function currentCursor(store, taskId) {
474
+ return { taskId, sequence: store.latestEventSequence(taskId), revision: store.getStateRevision() };
474
475
  }
475
476
  function sequence(id) {
476
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 indexedTaskIds = this.store.listActiveTaskIds?.();
242
- // Stores without the indexed projection remain useful for deterministic
243
- // tests. The production SQLite store discovers only its indexed hot set.
244
- const activeTasks = indexedTaskIds === undefined
245
- ? this.store.listTasks().filter((task) => (task.status === "active" && task.executionGate.state === "enabled"))
246
- : [...new Set(indexedTaskIds)]
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