@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.
Files changed (236) hide show
  1. package/ARCHITECTURE.md +3 -2
  2. package/ARCHITECTURE.zh-CN.md +3 -2
  3. package/dist/artifacts/artifactCommitLock.js +16 -55
  4. package/dist/artifacts/taskArtifactRepository.js +3 -3
  5. package/dist/cli/agentConfigurationPicker.js +1 -1
  6. package/dist/cli/commandCatalog.js +57 -41
  7. package/dist/cli/completion.js +18 -18
  8. package/dist/cli/completionWizard.js +1 -1
  9. package/dist/cli/dynamicCompletion.js +1 -1
  10. package/dist/cli/interactionPolicy.js +7 -3
  11. package/dist/cli/invocationAuthority.js +64 -0
  12. package/dist/cli/managedDiagnostics.js +1 -1
  13. package/dist/cli/parseRepeatable.js +35 -0
  14. package/dist/cli/updatePorts.js +26 -5
  15. package/dist/cli.js +1280 -1243
  16. package/dist/commands/agentCommands.js +4 -4
  17. package/dist/commands/capabilityCommands.js +1 -1
  18. package/dist/commands/configCommands.js +6 -57
  19. package/dist/commands/configOverview.js +2 -2
  20. package/dist/commands/durableJobCommands.js +12 -6
  21. package/dist/commands/globalRoleCommands.js +53 -29
  22. package/dist/commands/grantCommands.js +0 -3
  23. package/dist/commands/jobCommands.js +1 -4
  24. package/dist/commands/operatorCommands.js +3 -3
  25. package/dist/commands/projectCommands.js +1 -10
  26. package/dist/commands/resourcesCommands.js +10 -40
  27. package/dist/commands/roleConfiguration.js +2 -6
  28. package/dist/commands/taskActivationCommands.js +4 -8
  29. package/dist/commands/taskAgentCapabilities.js +9 -0
  30. package/dist/commands/taskCommandSupport.js +155 -0
  31. package/dist/commands/taskCommandTypes.js +1 -0
  32. package/dist/commands/taskCommands.js +271 -710
  33. package/dist/commands/taskCompletionGate.js +1 -1
  34. package/dist/commands/taskExecutionCommands.js +1 -1
  35. package/dist/commands/taskFactCommands.js +325 -0
  36. package/dist/commands/taskInputCommands.js +12 -42
  37. package/dist/commands/taskIntegrationCommands.js +17 -57
  38. package/dist/commands/taskNextActionCommand.js +7 -6
  39. package/dist/commands/taskPublicationAdoptCommand.js +3 -3
  40. package/dist/commands/taskPublicationCommands.js +1 -1
  41. package/dist/commands/taskPublicationVerifyCommand.js +1 -1
  42. package/dist/commands/taskRemoteDeliveryCommand.js +2 -94
  43. package/dist/commands/taskUpstreamCommands.js +19 -8
  44. package/dist/commands/workflowCommands.js +6 -1
  45. package/dist/completion/completionInstaller.js +26 -26
  46. package/dist/completion/completionState.js +26 -26
  47. package/dist/completion/fileCompletionManager.js +6 -11
  48. package/dist/config/configCatalog.js +0 -2
  49. package/dist/config/timeZone.js +14 -0
  50. package/dist/config/yuiConfig.js +0 -29
  51. package/dist/context/runContextPack.js +2 -3
  52. package/dist/context/taskCatalog.js +3 -5
  53. package/dist/context/taskContext.js +139 -151
  54. package/dist/context/wakeRunReferences.js +11 -0
  55. package/dist/controller/agentCapabilities.js +58 -0
  56. package/dist/controller/agentRuntimeObserver.js +6 -11
  57. package/dist/controller/clientRuntime.js +22 -14
  58. package/dist/controller/controller.js +9 -22
  59. package/dist/controller/fileSchedulerStoreAdapter.js +117 -212
  60. package/dist/controller/jobClient.js +4 -4
  61. package/dist/controller/jobControl.js +60 -25
  62. package/dist/controller/jobSupervisor.js +4 -4
  63. package/dist/controller/providerRetryDelivery.js +7 -7
  64. package/dist/controller/runtime.js +51 -44
  65. package/dist/controller/runtimeEventProcessor.js +0 -5
  66. package/dist/controller/sessionOwnerReconciliation.js +5 -0
  67. package/dist/controller/taskAgentError.js +35 -0
  68. package/dist/coordination/workMailboxQueue.js +2 -24
  69. package/dist/core/controllerClient.js +2 -2
  70. package/dist/core/fileLockOwner.js +74 -0
  71. package/dist/decision/decision.js +17 -0
  72. package/dist/doctor/doctor.js +18 -9
  73. package/dist/event/taskEvent.js +12 -0
  74. package/dist/execution/workItemExecutionProjection.js +2 -2
  75. package/dist/executor/agentCapabilityConfig.js +43 -0
  76. package/dist/executor/agentConfigurationCatalog.js +49 -11
  77. package/dist/executor/agentExecutor.js +5 -8
  78. package/dist/executor/fileRoleLaunchPlanner.js +11 -53
  79. package/dist/executor/taskAgentCapabilities.js +64 -0
  80. package/dist/integration/deliveryObligation.js +1 -69
  81. package/dist/integration/gitIntegrationService.js +60 -84
  82. package/dist/integration/integrationAttempt.js +3 -14
  83. package/dist/integration/integrationSourceApplication.js +4 -12
  84. package/dist/integration/manifestTags.js +2 -2
  85. package/dist/job/durableJob.js +4 -25
  86. package/dist/job/jobAssignmentScope.js +22 -0
  87. package/dist/job/jobOperation.js +16 -0
  88. package/dist/job/jobRunner.js +2 -1
  89. package/dist/job/stepDirectory.js +15 -0
  90. package/dist/kernel/builtinCapabilities.js +15 -11
  91. package/dist/kernel/kernelPorts.js +1 -17
  92. package/dist/lifecycle/exactRunTerminalization.js +1 -9
  93. package/dist/message/inputControlResolution.js +3 -4
  94. package/dist/message/message.js +17 -20
  95. package/dist/message/messageContinuation.js +1 -1
  96. package/dist/milestone/milestone.js +11 -0
  97. package/dist/observability/orchestrationMetrics.js +24 -1
  98. package/dist/observability/runtimeIdentity.js +1 -17
  99. package/dist/output/agentConfigurationPresentation.js +2 -0
  100. package/dist/output/timePresentation.js +1 -14
  101. package/dist/plugins/pluginService.js +12 -4
  102. package/dist/release/releaseWorkflowPorts.js +45 -96
  103. package/dist/release/runtimeRelease.js +9 -15
  104. package/dist/repository/projectMaintenanceLock.js +23 -64
  105. package/dist/repository/taskBaseFreshness.js +1 -1
  106. package/dist/repository/taskWorkspaceCoordinator.js +13 -22
  107. package/dist/repository/taskWorkspacePreparer.js +29 -25
  108. package/dist/repository/workspaceCleanupInspection.js +5 -5
  109. package/dist/resources/autoResourceGc.js +4 -77
  110. package/dist/resources/liveReferences.js +7 -54
  111. package/dist/resources/resourceDiscovery.js +5 -118
  112. package/dist/resources/resourceGc.js +233 -181
  113. package/dist/resources/resourceRegistrar.js +5 -4
  114. package/dist/resources/resourceRegistry.js +3 -37
  115. package/dist/resources/resourceRegistryStore.js +13 -36
  116. package/dist/resources/sqliteResourceRegistry.js +43 -26
  117. package/dist/review/deltaRecheck.js +1 -1
  118. package/dist/review/reviewAcceptance.js +1 -1
  119. package/dist/review/reviewDecision.js +1 -1
  120. package/dist/review/reviewRound.js +8 -7
  121. package/dist/review/taskFinalReviewContractResolution.js +1 -1
  122. package/dist/runtime/acpProtocol.js +4 -51
  123. package/dist/runtime/acpSession.js +10 -75
  124. package/dist/runtime/acpSessionConfiguration.js +1 -8
  125. package/dist/runtime/agentEndpoint.js +3 -2
  126. package/dist/runtime/agentFailureContext.js +43 -0
  127. package/dist/runtime/agentHost.js +15 -15
  128. package/dist/runtime/codexInteractiveHost.js +2 -2
  129. package/dist/runtime/index.js +0 -1
  130. package/dist/runtime/managedCaller.js +21 -1
  131. package/dist/runtime/providerErrors.js +38 -0
  132. package/dist/runtime/runtimeObservation.js +0 -5
  133. package/dist/runtime/runtimeSessionCandidate.js +2 -3
  134. package/dist/runtime/sessionOwnerIdentity.js +1 -1
  135. package/dist/runtime/structuredProviderHost.js +4 -41
  136. package/dist/runtime/tmuxAdapters.js +6 -8
  137. package/dist/scheduler/activeRoleRunDelivery.js +7 -7
  138. package/dist/scheduler/leaderWakeupProcessor.js +19 -6
  139. package/dist/scheduler/operatorEvent.js +12 -20
  140. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  141. package/dist/scheduler/ports.js +5 -8
  142. package/dist/scheduler/roleRunLiveness.js +4 -4
  143. package/dist/scheduler/roleRunStall.js +16 -23
  144. package/dist/scheduler/taskExecutionProjection.js +25 -33
  145. package/dist/scheduler/taskObservabilityProjection.js +0 -1
  146. package/dist/scheduler/taskWake.js +5 -10
  147. package/dist/scheduler/wakeReason.js +2 -0
  148. package/dist/scheduler/wakeupQueue.js +2 -4
  149. package/dist/setup/setupCommand.js +1 -1
  150. package/dist/storage/contextRecords.js +106 -0
  151. package/dist/storage/currentTaskStore.js +1 -1
  152. package/dist/storage/homeLayout.js +13 -17
  153. package/dist/storage/migrations/agentFailureContext.js +22 -0
  154. package/dist/storage/migrations/currentInputContract.js +86 -0
  155. package/dist/storage/migrations/currentRuntimeContract.js +228 -0
  156. package/dist/storage/migrations/historicalVerificationPlan.js +35 -0
  157. package/dist/storage/migrations/integrationContinuation.js +5 -4
  158. package/dist/storage/migrations/narrowAgentFailureContext.js +65 -0
  159. package/dist/storage/migrations/notificationOnlyWakes.js +74 -0
  160. package/dist/storage/migrations/verificationPlanV1.js +162 -0
  161. package/dist/storage/migrations/verificationPolicy.js +74 -0
  162. package/dist/storage/migrations/workItemHistory.js +46 -0
  163. package/dist/storage/persistenceWorker.js +12 -4
  164. package/dist/storage/recordValidation.js +87 -0
  165. package/dist/storage/sqliteSchema.js +142 -1
  166. package/dist/storage/sqliteStore.js +156 -162
  167. package/dist/storage/storageSchema.js +2 -14
  168. package/dist/storage/storageVersions.js +1 -1
  169. package/dist/storage/storeRpc.js +15 -7
  170. package/dist/storage/taskStore.js +3 -7
  171. package/dist/storage/upgrade/upgradeOrchestrator.js +25 -37
  172. package/dist/task/completionReadiness.js +5 -24
  173. package/dist/task/draftPlan.js +0 -53
  174. package/dist/task/nextAction.js +7 -13
  175. package/dist/task/remoteDeliveryService.js +89 -0
  176. package/dist/task/taskActivation.js +2 -25
  177. package/dist/task/taskActivationService.js +0 -2
  178. package/dist/task/taskRecordReference.js +0 -1
  179. package/dist/task/taskSubmission.js +3 -14
  180. package/dist/telemetry/sqliteTelemetryBatch.js +29 -0
  181. package/dist/telemetry/sqliteTelemetryStore.js +49 -49
  182. package/dist/telemetry/telemetryWiring.js +4 -3
  183. package/dist/tmux/tmuxManager.js +29 -20
  184. package/dist/verification/gateArtifact.js +15 -24
  185. package/dist/verification/gateArtifactStore.js +14 -7
  186. package/dist/verification/verificationGateService.js +29 -88
  187. package/dist/verification/verificationPlan.js +27 -72
  188. package/dist/web/assets/client/app.js +1 -1
  189. package/dist/web/assets/client/taskSurface.js +4 -4
  190. package/dist/web/webServer.js +4 -6
  191. package/dist/web/webSnapshot.js +3 -83
  192. package/dist/web/webTaskSurface.js +17 -46
  193. package/dist/workItem/workItem.js +1 -12
  194. package/docs/architecture/capabilities-and-resources.md +14 -2
  195. package/docs/architecture/capabilities-and-resources.zh-CN.md +11 -1
  196. package/docs/managed-turn-and-session-runtime.md +83 -10
  197. package/docs/managed-turn-and-session-runtime.zh-CN.md +65 -11
  198. package/docs/plugin-sdk.md +4 -2
  199. package/docs/provider-runtime.md +3 -1
  200. package/docs/provider-runtime.zh-CN.md +4 -2
  201. package/docs/release-workflow.md +190 -7
  202. package/docs/release-workflow.zh-CN.md +139 -5
  203. package/docs/roles-and-configuration.md +29 -0
  204. package/docs/roles-and-configuration.zh-CN.md +21 -0
  205. package/docs/sqlite-control-plane-design.md +16 -3
  206. package/docs/sqlite-control-plane-design.zh-CN.md +13 -2
  207. package/docs/task-dag-semantics.md +3 -3
  208. package/docs/task-dag-semantics.zh-CN.md +2 -2
  209. package/docs/task-delivery.md +91 -5
  210. package/docs/task-delivery.zh-CN.md +72 -4
  211. package/docs/task-discovery.md +10 -11
  212. package/docs/task-discovery.zh-CN.md +8 -9
  213. package/docs/testing/verification-levels.md +145 -9
  214. package/docs/testing/verification-levels.zh-CN.md +103 -8
  215. package/package.json +1 -1
  216. package/skills/yui-leader/SKILL.md +5 -0
  217. package/skills/yui-leader/references/integration.md +9 -5
  218. package/skills/yui-leader/references/planning.md +9 -3
  219. package/skills/yui-operator/SKILL.md +15 -10
  220. package/skills/yui-runtime/SKILL.md +6 -6
  221. package/skills/yui-runtime/references/recovery.md +47 -0
  222. package/dist/agent/agentRegistry.js +0 -10
  223. package/dist/agentRun/runIdentity.js +0 -22
  224. package/dist/commands/deliveryGuardPreflight.js +0 -30
  225. package/dist/commands/taskIntegrationQueueCommands.js +0 -226
  226. package/dist/commands/taskOverviewCommand.js +0 -377
  227. package/dist/completion/completionWizard.js +0 -125
  228. package/dist/context/dispatchContext.js +0 -110
  229. package/dist/context/wakeNotification.js +0 -144
  230. package/dist/integration/integrationCheckEvidenceReuse.js +0 -53
  231. package/dist/integration/integrationQueueEntry.js +0 -191
  232. package/dist/integration/integrationQueueService.js +0 -810
  233. package/dist/release/fakeReleasePorts.js +0 -55
  234. package/dist/runtime/sessionOwnerRegistry.js +0 -137
  235. package/dist/task/deliveryGuard.js +0 -226
  236. /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 (!["--view", "--limit", "--cursor", "--status", "--project", "--search", "--attention"].includes(key)
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("Compact Task list expects --view compact [--all] [--status <status>] [--project <id>] [--search <text>] [--attention <category>] [--limit <1..100>] [--cursor <cursor>].");
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: { view: "compact", all: options.all, attention: kind }
100
+ query: { all: options.all, attention: kind }
103
101
  }];
104
102
  }));
105
103
  const response = {
@@ -1,14 +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
- 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.transaction((reader) => {
23
- 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);
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(entries);
29
+ const attention = summarizeAttention(reader, taskId, scope.allow);
28
30
  let bytes = Buffer.byteLength(JSON.stringify(attention));
29
31
  const records = [];
30
- const runtimeEvents = entries.some((entry) => entry.ref.store === "run") ? reader.listEvents(taskId) : [];
31
- for (const entry of entries) {
32
- const valueBytes = Buffer.byteLength(JSON.stringify(entry.value));
33
- const content = valueBytes > MAX_VALUE_BYTES
34
- ? { ref: entry.ref, summary: summarize(entry.value), omitted: true }
35
- : { ...entry, omitted: false };
36
- const record = { ...content, ...(entry.ref.store === "run" ? {
37
- execution: runExecutionObservation(entry.value, reader.getTaskRoleSessionSet(taskId, entry.value.roleName)?.providerBinding, runtimeEvents)
38
- } : {}), ...(entry.ref.store === "role" ? {
39
- providerRetry: providerRetryProjection(reader.getTaskRoleSessionSet(taskId, entry.value.name)?.providerBinding)
40
- } : {}) };
41
- const size = Buffer.byteLength(JSON.stringify(record));
42
- if (records.length >= MAX_RECORDS || bytes + size > MAX_PAGE_BYTES)
43
- break;
44
- records.push(record);
45
- bytes += size;
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: entries.length,
51
- 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 },
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(entries) {
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 bytes = { openInputs: 0, pendingOperations: 0, unknownOperations: 0 };
64
- for (const entry of entries) {
65
- const status = entry.value.status;
66
- const category = entry.ref.store === "input-request" && status === "open" ? "openInputs"
67
- : entry.ref.store === "job" && status === "unknown-needs-attention" ? "unknownOperations"
68
- : entry.ref.store === "job" && (status === "queued" || status === "running") ? "pendingOperations"
69
- : undefined;
70
- if (category === undefined)
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
- const size = Buffer.byteLength(JSON.stringify(entry.ref)) + 1;
75
- if (summary.refs.length < MAX_ATTENTION_REFS && bytes[category] + size <= MAX_VALUE_BYTES) {
76
- summary.refs.push(entry.ref);
77
- bytes[category] += size;
78
- }
79
- else {
80
- 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;
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.transaction((reader) => {
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, history);
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 events = history
107
- .filter((event) => isAllowed(allow, "task-event", event.id)
108
- && sequence(event.id) > after && sequence(event.id) <= bound.sequence)
109
- .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
+ });
110
117
  const page = [];
111
118
  let bytes = 0;
112
- for (const event of events) {
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 = events.length > page.length;
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: events.length, throughCursor: encode(through),
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.transaction((reader) => {
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.listEvents(taskId)) } : {}),
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.transaction((reader) => {
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
- // A steer targets exactly one Role's current native Turn and is never shared
240
- // Task intent (decision-3 §9). A Leader steer in particular stores as an
241
- // untargeted `user` Message (no recipient/WorkItem/Run scope, since a Leader
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 steerMessageIds = new Set(store.listMessages(taskId)
278
- .filter(message => isSteerMessage(message)
279
- && message.recipient?.roleName === caller.roleName
280
- && message.recipient.workItemId === run?.workItemId
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 event of store.listEvents(taskId)) {
286
- if (event.type.startsWith("message.") && steerMessageIds.has(event.payload.messageId)) {
287
- 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}`);
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": 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;
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 authorizedEntries(store, taskId, environment) {
402
- const { task, allow, caller } = authorizeContext(store, taskId, environment);
403
- const entries = [];
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
- entries.push(materialize(family, id, value));
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
- for (const request of store.listInputRequests(taskId).filter((r) => r.status === "open"))
429
- add("input-request", request.id, request);
430
- for (const item of store.listWorkItems(taskId).sort((a, b) => b.updatedAt.localeCompare(a.updatedAt))) {
431
- add("work-item", item.id, 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
- for (const preparation of store.listEnvironmentPreparations(taskId))
443
- add("environment-preparation", preparation.id, preparation);
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
- for (const changeSet of store.listChangeSets(taskId))
450
- add("change-set", changeSet.id, changeSet);
451
- for (const message of store.listMessages(taskId).reverse())
452
- add("task-message", message.id, message);
453
- for (const run of store.listRuns(taskId).reverse()) {
454
- add("run", run.id, run);
455
- if (allow?.has(`source-turn:${run.id}`))
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
- for (const round of store.listReviewRounds(taskId).reverse())
459
- add("review-round", round.id, round);
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
- for (const decision of store.listDecisions(taskId))
469
- add("task-decision", decision.id, decision);
470
- for (const milestone of store.listMilestones(taskId))
471
- add("task-milestone", milestone.id, milestone);
472
- for (const job of store.listDurableJobs(taskId))
473
- add("job", job.id, job);
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, events = store.listEvents(taskId)) {
486
- 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() };
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 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