@zq-silk/yui 0.15.6 → 0.15.8

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 (282) hide show
  1. package/ARCHITECTURE.md +192 -399
  2. package/README.md +127 -1149
  3. package/dist/agent/adapterCatalog.js +15 -2
  4. package/dist/agent/agent.js +23 -3
  5. package/dist/agent/argumentPolicy.js +7 -1
  6. package/dist/agent/connectionPlan.js +62 -0
  7. package/dist/agent/executionComponents.js +158 -0
  8. package/dist/agent/launchEnvironment.js +31 -3
  9. package/dist/agent/managedRuntimeEnvironment.js +3 -5
  10. package/dist/{turn/turn.js → agentRun/agentRun.js} +166 -109
  11. package/dist/{turn/turnIdentity.js → agentRun/runIdentity.js} +4 -4
  12. package/dist/brief/taskBrief.js +12 -0
  13. package/dist/cli/agentConfigurationPicker.js +13 -0
  14. package/dist/cli/commandCatalog.js +167 -70
  15. package/dist/cli/interactionCandidates.js +5 -5
  16. package/dist/cli/interactionPolicy.js +38 -8
  17. package/dist/cli/invocationRouter.js +1 -1
  18. package/dist/cli/managedDiagnostics.js +28 -0
  19. package/dist/cli/operatorWizard.js +1 -7
  20. package/dist/cli/roleOptionOrder.js +27 -0
  21. package/dist/cli/roleWizard.js +50 -14
  22. package/dist/cli/updateOrchestrator.js +1 -1
  23. package/dist/cli/updatePorts.js +3 -4
  24. package/dist/cli.js +188 -95
  25. package/dist/commands/agentCommands.js +72 -14
  26. package/dist/commands/capabilityCommands.js +57 -0
  27. package/dist/commands/configCommands.js +20 -20
  28. package/dist/commands/deliveryGuardPreflight.js +2 -2
  29. package/dist/commands/executionAuditCommands.js +24 -24
  30. package/dist/commands/globalRoleCommands.js +1 -1
  31. package/dist/commands/grantCommands.js +4 -4
  32. package/dist/commands/operatorCommands.js +1 -7
  33. package/dist/commands/projectCommands.js +4 -4
  34. package/dist/commands/resourcesCommands.js +2 -2
  35. package/dist/commands/roleConfiguration.js +25 -5
  36. package/dist/commands/roleRuntimeGuard.js +4 -5
  37. package/dist/commands/sessionCommands.js +3 -7
  38. package/dist/commands/taskActivationCommands.js +259 -0
  39. package/dist/commands/taskActor.js +28 -49
  40. package/dist/commands/taskCommands.js +1216 -730
  41. package/dist/commands/taskContextCommand.js +39 -583
  42. package/dist/commands/taskExecutionCommands.js +32 -32
  43. package/dist/commands/taskInputCommands.js +40 -104
  44. package/dist/commands/taskIntegrationCommands.js +3 -2
  45. package/dist/commands/taskIntegrationQueueCommands.js +1 -1
  46. package/dist/commands/taskNextActionCommand.js +8 -8
  47. package/dist/commands/taskOverviewCommand.js +33 -45
  48. package/dist/commands/taskRemoteDeliveryCommand.js +2 -2
  49. package/dist/commands/taskRoleRuntimeStatus.js +133 -102
  50. package/dist/commands/telemetryCommands.js +36 -38
  51. package/dist/config/configCatalog.js +4 -4
  52. package/dist/config/yuiConfig.js +8 -8
  53. package/dist/context/contextSnapshot.js +10 -10
  54. package/dist/context/dispatchContext.js +11 -11
  55. package/dist/context/roleSessionContext.js +6 -3
  56. package/dist/context/{turnContextPack.js → runContextPack.js} +146 -81
  57. package/dist/context/{turnInputContract.js → runInputContract.js} +73 -60
  58. package/dist/context/sessionBootstrapManifest.js +21 -2
  59. package/dist/context/sourceRunContext.js +30 -0
  60. package/dist/context/taskContext.js +458 -0
  61. package/dist/context/wakeNotification.js +27 -27
  62. package/dist/controller/agentRuntimeObserver.js +21 -24
  63. package/dist/controller/capabilityBridge.js +77 -0
  64. package/dist/controller/clientRuntime.js +65 -92
  65. package/dist/controller/controller.js +127 -188
  66. package/dist/controller/fileSchedulerStoreAdapter.js +787 -887
  67. package/dist/controller/jobControl.js +54 -85
  68. package/dist/controller/resourceInventory.js +8 -27
  69. package/dist/controller/resourceInventoryLinux.js +12 -13
  70. package/dist/controller/runtime.js +530 -476
  71. package/dist/controller/runtimeEventInbox.js +55 -25
  72. package/dist/controller/runtimeEventProcessor.js +22 -31
  73. package/dist/controller/{runtimeHookTurnFence.js → runtimeHookRunFence.js} +91 -115
  74. package/dist/controller/runtimeLaunchCoordinator.js +80 -426
  75. package/dist/controller/runtimeObservationHook.js +14 -18
  76. package/dist/controller/sessionNotify.js +16 -24
  77. package/dist/controller/sessionOwnerReconciliation.js +168 -50
  78. package/dist/controller/structuredProviderObservation.js +138 -99
  79. package/dist/coordination/workMailbox.js +3 -3
  80. package/dist/coordination/workMailboxQueue.js +36 -33
  81. package/dist/core/boundedRpc.js +8 -1
  82. package/dist/core/controllerClient.js +20 -1
  83. package/dist/core/controllerServer.js +4 -4
  84. package/dist/doctor/doctor.js +13 -2
  85. package/dist/domain/agentResultTransport.js +9 -9
  86. package/dist/execution/codexThreadNaming.js +2 -8
  87. package/dist/execution/executionHealth.js +51 -63
  88. package/dist/execution/reviewMainRun.js +137 -0
  89. package/dist/execution/workItemExecution.js +28 -29
  90. package/dist/execution/workItemExecutionProjection.js +99 -107
  91. package/dist/execution/workItemMainRun.js +141 -0
  92. package/dist/executor/agentAdapter.js +227 -20
  93. package/dist/executor/agentConfigurationCatalog.js +126 -4
  94. package/dist/executor/agentConfigurationProbe.js +162 -4
  95. package/dist/executor/agentExecutor.js +79 -78
  96. package/dist/executor/effectiveLaunch.js +105 -18
  97. package/dist/executor/executorRegistry.js +29 -44
  98. package/dist/executor/fileRoleLaunchPlanner.js +229 -154
  99. package/dist/executor/workspacePreflightClassification.js +16 -16
  100. package/dist/grant/capabilityGrant.js +6 -3
  101. package/dist/input/inputRequest.js +12 -10
  102. package/dist/integration/gitIntegrationService.js +4 -11
  103. package/dist/integration/integrationQueueService.js +4 -4
  104. package/dist/interaction/operatorPresentation.js +1 -1
  105. package/dist/kernel/builtinCapabilities.js +423 -0
  106. package/dist/kernel/capabilityRegistry.js +314 -0
  107. package/dist/kernel/capabilitySchema.js +91 -0
  108. package/dist/kernel/instanceHost.js +18 -1
  109. package/dist/kernel/kernelPorts.js +4 -1
  110. package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
  111. package/dist/lifecycle/exactRunTerminalization.js +449 -0
  112. package/dist/message/message.js +62 -6
  113. package/dist/message/messageContinuation.js +204 -0
  114. package/dist/observability/executionAudit.js +70 -72
  115. package/dist/observability/faultClassification.js +2 -2
  116. package/dist/observability/orchestrationMetrics.js +8 -8
  117. package/dist/operator/operatorSessionHistory.js +1 -7
  118. package/dist/output/agentConfigurationPresentation.js +8 -3
  119. package/dist/output/agentRunConfigurationPresentation.js +128 -0
  120. package/dist/output/rolePresentation.js +54 -3
  121. package/dist/plugins/pluginChild.js +104 -0
  122. package/dist/plugins/pluginIntent.js +26 -0
  123. package/dist/plugins/pluginInterpreter.js +43 -0
  124. package/dist/plugins/pluginPackage.js +101 -0
  125. package/dist/plugins/pluginProcess.js +112 -0
  126. package/dist/plugins/pluginService.js +380 -0
  127. package/dist/profile/agentProfile.js +1 -1
  128. package/dist/repository/gitWorkspace.js +26 -4
  129. package/dist/repository/project.js +19 -4
  130. package/dist/repository/taskBaseFreshness.js +13 -13
  131. package/dist/repository/taskWorkspaceCoordinator.js +20 -27
  132. package/dist/repository/taskWorkspacePreparer.js +344 -83
  133. package/dist/resources/autoResourceGc.js +3 -3
  134. package/dist/resources/liveReferences.js +3 -3
  135. package/dist/resources/projectResource.js +123 -0
  136. package/dist/resources/projectResourceService.js +421 -0
  137. package/dist/resources/resourceDiscovery.js +6 -6
  138. package/dist/resources/resourceGc.js +1 -1
  139. package/dist/resources/resourceRegistrar.js +1 -1
  140. package/dist/resources/resourceTypes.js +1 -1
  141. package/dist/review/deltaRecheck.js +3 -3
  142. package/dist/review/reviewAcceptance.js +16 -16
  143. package/dist/review/reviewDecision.js +7 -7
  144. package/dist/review/reviewRound.js +21 -20
  145. package/dist/review/reviewerAvailability.js +2 -2
  146. package/dist/role/role.js +51 -7
  147. package/dist/role/taskRoleUpdate.js +30 -0
  148. package/dist/runtime/acpProtocol.js +425 -0
  149. package/dist/runtime/acpSession.js +731 -0
  150. package/dist/runtime/acpSessionConfiguration.js +260 -0
  151. package/dist/runtime/agentDriver.js +30 -11
  152. package/dist/runtime/agentEndpoint.js +278 -0
  153. package/dist/runtime/agentEndpointIdentity.js +86 -0
  154. package/dist/runtime/agentEndpointOwnership.js +239 -0
  155. package/dist/runtime/agentError.js +2 -10
  156. package/dist/runtime/agentHost.js +565 -314
  157. package/dist/runtime/agentRunConfiguration.js +258 -0
  158. package/dist/runtime/builtinAgentDrivers.js +134 -18
  159. package/dist/runtime/builtinAgentErrorMappers.js +55 -3
  160. package/dist/runtime/builtinTranscriptUsage.js +1 -1
  161. package/dist/runtime/claude-process-owner +0 -0
  162. package/dist/runtime/codexAppServerRuntime.js +38 -30
  163. package/dist/runtime/codexInteractiveHost.js +41 -6
  164. package/dist/runtime/continuationManager.js +2 -6
  165. package/dist/runtime/executionEnvironment.js +30 -0
  166. package/dist/runtime/firstProgressAdvisory.js +11 -11
  167. package/dist/runtime/index.js +4 -3
  168. package/dist/runtime/jsonLineChannel.js +109 -0
  169. package/dist/runtime/launchBroker.js +91 -16
  170. package/dist/runtime/launchDiagnostics.js +2 -2
  171. package/dist/runtime/lifecycleReservation.js +10 -18
  172. package/dist/runtime/managedCaller.js +61 -17
  173. package/dist/runtime/nativeSessionControl.js +102 -0
  174. package/dist/runtime/ports.js +6 -21
  175. package/dist/runtime/processExitObservation.js +8 -7
  176. package/dist/runtime/promptEnvelope.js +17 -6
  177. package/dist/runtime/providerContinuation.js +3 -9
  178. package/dist/runtime/providerContinuationReconciliationService.js +4 -13
  179. package/dist/runtime/providerControl.js +2 -7
  180. package/dist/runtime/providerRuntimeIdentity.js +110 -222
  181. package/dist/runtime/providerRuntimeReconciler.js +5 -9
  182. package/dist/runtime/runtimeBinding.js +0 -1
  183. package/dist/runtime/runtimeContinuationProjection.js +4 -7
  184. package/dist/runtime/runtimeDeadlines.js +9 -0
  185. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  186. package/dist/runtime/runtimeObservation.js +29 -65
  187. package/dist/runtime/runtimeProjection.js +43 -51
  188. package/dist/runtime/runtimeSessionCandidate.js +1 -3
  189. package/dist/runtime/sessionLaunchRequest.js +3 -7
  190. package/dist/runtime/sessionOwnerIdentity.js +7 -54
  191. package/dist/runtime/sessionOwnerRegistry.js +22 -17
  192. package/dist/runtime/sessionReconciliation.js +4 -8
  193. package/dist/runtime/sessionTerminationGuard.js +70 -259
  194. package/dist/runtime/sessionTokenMetrics.js +5 -16
  195. package/dist/runtime/structuredProviderHost.js +237 -117
  196. package/dist/runtime/taskRuntimeIsolation.js +39 -122
  197. package/dist/runtime/tmuxAdapters.js +39 -86
  198. package/dist/scheduler/activeRoleRunDelivery.js +354 -0
  199. package/dist/scheduler/leaderWakeupProcessor.js +75 -266
  200. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  201. package/dist/scheduler/ports.js +80 -9
  202. package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
  203. package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
  204. package/dist/scheduler/taskExecutionProjection.js +120 -124
  205. package/dist/scheduler/taskObservabilityProjection.js +29 -29
  206. package/dist/scheduler/taskWake.js +11 -4
  207. package/dist/scheduler/wakeReason.js +9 -1
  208. package/dist/setup/setupCommand.js +1 -0
  209. package/dist/storage/migrations/agentRunContract.js +159 -0
  210. package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
  211. package/dist/storage/sqliteSchema.js +431 -5
  212. package/dist/storage/sqliteStore.js +375 -220
  213. package/dist/storage/storageVersions.js +1 -1
  214. package/dist/storage/storeRpc.js +10 -5
  215. package/dist/storage/taskStore.js +13 -11
  216. package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
  217. package/dist/surface/surfaceContributions.js +102 -0
  218. package/dist/task/completionReadiness.js +32 -6
  219. package/dist/task/deliveryGuard.js +16 -16
  220. package/dist/task/draftPlan.js +72 -12
  221. package/dist/task/nextAction.js +144 -128
  222. package/dist/task/remoteDelivery.js +6 -6
  223. package/dist/task/task.js +184 -18
  224. package/dist/task/taskActivation.js +301 -0
  225. package/dist/task/taskActivationService.js +392 -0
  226. package/dist/task/taskRecordReference.js +5 -4
  227. package/dist/task/taskRecordRetirement.js +1 -1
  228. package/dist/telemetry/sqliteTelemetryStore.js +55 -68
  229. package/dist/telemetry/telemetryConfig.js +14 -14
  230. package/dist/telemetry/telemetryWiring.js +2 -2
  231. package/dist/web/assets/assetManifest.js +2 -0
  232. package/dist/web/assets/client/app.js +120 -20
  233. package/dist/web/assets/client/components.js +87 -54
  234. package/dist/web/assets/client/i18n.js +83 -41
  235. package/dist/web/assets/client/markdown.js +1 -1
  236. package/dist/web/assets/client/taskSurface.js +353 -0
  237. package/dist/web/assets/client/view.js +49 -44
  238. package/dist/web/assets/shell.js +1 -1
  239. package/dist/web/assets/styles/cards.js +22 -4
  240. package/dist/web/controllerWeb.js +60 -0
  241. package/dist/web/webMutation.js +28 -0
  242. package/dist/web/webServer.js +118 -8
  243. package/dist/web/webSnapshot.js +81 -74
  244. package/dist/web/webTaskSurface.js +64 -0
  245. package/dist/workItem/dependencyGate.js +1 -1
  246. package/dist/workItem/workItem.js +80 -48
  247. package/dist/workspace/workItemChangeSetManager.js +16 -9
  248. package/docs/agent-result-consumption.md +94 -0
  249. package/docs/agent-runtime-drivers.md +91 -0
  250. package/docs/architecture/README.md +38 -0
  251. package/docs/architecture/capabilities-and-resources.md +79 -0
  252. package/docs/managed-turn-and-session-runtime.md +222 -0
  253. package/docs/observability/README.md +81 -0
  254. package/docs/plugin-sdk.md +290 -0
  255. package/docs/provider-runtime.md +163 -0
  256. package/docs/release-workflow.md +303 -0
  257. package/docs/roles-and-configuration.md +113 -0
  258. package/docs/sqlite-control-plane-design.md +76 -0
  259. package/docs/task-dag-semantics.md +57 -0
  260. package/docs/task-delivery.md +103 -0
  261. package/docs/task-local-identity.md +6 -6
  262. package/docs/testing/verification-levels.md +86 -0
  263. package/i18n/README.zh-CN.md +98 -739
  264. package/package.json +5 -5
  265. package/skills/yui-leader/SKILL.md +130 -103
  266. package/skills/yui-leader/references/integration.md +39 -0
  267. package/skills/yui-leader/references/replicated-execution.md +42 -0
  268. package/skills/yui-leader/references/task-plugins.md +33 -0
  269. package/skills/yui-operator/SKILL.md +30 -59
  270. package/skills/yui-reviewer/SKILL.md +35 -36
  271. package/skills/yui-runtime/SKILL.md +88 -24
  272. package/skills/yui-runtime/references/publication.md +22 -0
  273. package/skills/yui-runtime/references/recovery.md +64 -0
  274. package/skills/yui-worker/SKILL.md +37 -39
  275. package/dist/cli/roleOptionCatalog.js +0 -68
  276. package/dist/context/sourceTurnContext.js +0 -30
  277. package/dist/execution/reviewMainTurn.js +0 -161
  278. package/dist/execution/workItemMainTurn.js +0 -164
  279. package/dist/lifecycle/exactTurnTerminalization.js +0 -407
  280. package/dist/runtime/preallocatedNativeSession.js +0 -13
  281. package/dist/runtime/runtimeStopReceipt.js +0 -42
  282. package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
@@ -0,0 +1,314 @@
1
+ import { capabilitySchemaError, checkCapabilitySchema } from "./capabilitySchema.js";
2
+ import { checkSurfaceDescriptors } from "../surface/surfaceContributions.js";
3
+ /** Trusted owners may accompany a failed operation with its current read model.
4
+ * This is diagnostic data, not an operation receipt or an authority token. */
5
+ export class CapabilityExecutionError extends Error {
6
+ value;
7
+ constructor(message, value) {
8
+ super(message);
9
+ this.value = structuredClone(value);
10
+ }
11
+ }
12
+ const effectRank = { query: 0, "local-mutation": 1, "external-operation": 2 };
13
+ const reserved = new Set(["yui", "task", "context", "config", "job", "resource", "plugin", "runtime", "grant", "capability", "artifact", "environment", "project"]);
14
+ /** One rebuildable descriptor view over the composition root's existing Host.
15
+ * Only the trusted root owns this object. Extensions receive a bound invocation
16
+ * port, never the registry/Host or an actor-selecting call interface. */
17
+ export class CapabilityRegistry {
18
+ host;
19
+ authorize;
20
+ #descriptors = [];
21
+ #coreProviders = new Set();
22
+ constructor(host, authorize, builtins = []) {
23
+ this.host = host;
24
+ this.authorize = authorize;
25
+ this.#publish(builtins, true);
26
+ builtins.forEach((entry) => this.#coreProviders.add(entry.provider.id));
27
+ }
28
+ /** Publish a complete provider generation atomically after owner initialization.
29
+ * Failure leaves all existing descriptors intact. Host lifecycle remains owned
30
+ * by the root: publish replacement, then detach old generation when appropriate. */
31
+ register(descriptors) {
32
+ this.#publish(descriptors, false);
33
+ }
34
+ /** Data-only candidate check before author initialization; no acquisition or
35
+ * publication. Repeat immediately before publishing after any async work. */
36
+ checkRegistration(context, descriptors) {
37
+ const prepared = this.#prepare(descriptors, false);
38
+ if (!prepared.length)
39
+ throw new Error("Plugin must contribute capabilities.");
40
+ const available = [
41
+ ...this.search(context).filter((entry) => entry.provider.id !== prepared[0].provider.id),
42
+ ...prepared
43
+ ];
44
+ const check = (entry, path) => {
45
+ this.authorize(context, entry);
46
+ if (path.has(entry))
47
+ throw new Error("Required capability dependency cycle.");
48
+ for (const dependency of entry.required ?? []) {
49
+ const matches = available.filter((candidate) => candidate.name === dependency.name
50
+ && candidate.contractVersion === dependency.contractVersion && candidate.unavailable === undefined);
51
+ if (matches.length !== 1)
52
+ throw new Error(`Required capability is missing or ambiguous: ${dependency.name}.`);
53
+ check(matches[0], new Set([...path, entry]));
54
+ }
55
+ };
56
+ prepared.forEach((entry) => check(entry, new Set()));
57
+ }
58
+ disable(providerId) {
59
+ this.#descriptors = this.#descriptors.filter((entry) => entry.provider.id !== providerId);
60
+ }
61
+ search(context, query = "") {
62
+ const visibility = this.authorize(context);
63
+ const authorized = this.#descriptors.filter((entry) => visible(entry.scope, visibility))
64
+ .filter((entry) => {
65
+ try {
66
+ this.authorize(context, entry);
67
+ return true;
68
+ }
69
+ catch {
70
+ return false;
71
+ }
72
+ });
73
+ return authorized.filter((entry) => `${entry.name} ${entry.summary}`.toLowerCase().includes(query.toLowerCase()))
74
+ .map((entry) => {
75
+ const unavailable = this.#unavailable(entry, authorized, new Set());
76
+ return unavailable === undefined ? entry : Object.freeze({ ...entry, unavailable });
77
+ });
78
+ }
79
+ describe(context, request) {
80
+ try {
81
+ const candidates = this.search(context).filter((entry) => entry.name === request.name
82
+ && (request.contractVersion === undefined || request.contractVersion === entry.contractVersion)
83
+ && (request.providerId === undefined || request.providerId === entry.provider.id));
84
+ if (!candidates.length)
85
+ return result("unavailable", "No authorized compatible Provider is available.");
86
+ const available = candidates.filter((entry) => entry.unavailable === undefined);
87
+ if (!available.length)
88
+ return {
89
+ ...result("unavailable", candidates.map((entry) => `${entry.provider.id}: ${entry.unavailable}`).join("; ")),
90
+ candidates
91
+ };
92
+ if (available.length > 1)
93
+ return { ...result("ambiguous", "Select an explicit Provider and contract version."), candidates };
94
+ const selected = available[0];
95
+ return {
96
+ ...result("value"), value: selected, candidates, provider: selected.provider,
97
+ selection: request.providerId === undefined ? "unique" : "explicit"
98
+ };
99
+ }
100
+ catch {
101
+ return result("denied", "Current call authority is unavailable.");
102
+ }
103
+ }
104
+ call(context, request) {
105
+ return this.#call(context, request, "external-operation");
106
+ }
107
+ async #call(context, request, ceiling, permissions) {
108
+ // Freeze one input snapshot across validation/acquisition and async nesting.
109
+ try {
110
+ request = deepFreeze(structuredClone(request));
111
+ }
112
+ catch {
113
+ return result("invalid", "Capability request must be cloneable data.");
114
+ }
115
+ const resolved = this.describe(context, request);
116
+ if (resolved.kind !== "value")
117
+ return resolved;
118
+ const descriptor = resolved.value;
119
+ const origin = { provider: descriptor.provider, selection: resolved.selection };
120
+ if (effectRank[descriptor.effect] > effectRank[ceiling]) {
121
+ return { ...result("denied", "Nested call exceeds the parent's effect boundary."), ...origin };
122
+ }
123
+ if (permissions !== undefined && descriptor.requiredPermissions.some((permission) => !permissions.includes(permission))) {
124
+ return { ...result("denied", "Nested call exceeds the parent's declared permissions."), ...origin };
125
+ }
126
+ const inputError = capabilitySchemaError(descriptor.inputSchema, request.input);
127
+ if (inputError)
128
+ return { ...result("invalid", inputError), ...origin };
129
+ if (descriptor.effect !== "query" && (typeof request.requestId !== "string" || !request.requestId.trim())) {
130
+ return { ...result("invalid", "Effectful calls require requestId."), ...origin };
131
+ }
132
+ try {
133
+ this.authorize(context, descriptor, request.input);
134
+ }
135
+ catch {
136
+ return { ...result("denied", "Current call authority was revoked."), ...origin };
137
+ }
138
+ const operations = [];
139
+ let effect = "none";
140
+ const observe = (operation) => {
141
+ operations.push(structuredClone(operation));
142
+ effect = accumulatedEffect(effect, operation.effect);
143
+ };
144
+ let entered = false;
145
+ try {
146
+ const value = await this.host.use(descriptor.provider, async (implementation) => {
147
+ entered = true;
148
+ let open = true;
149
+ const children = [];
150
+ try {
151
+ return await implementation.invoke(descriptor.name, request.input, Object.freeze({
152
+ context, requestId: request.requestId,
153
+ observe: (operation) => {
154
+ if (!open)
155
+ throw new Error("Capability invocation has ended.");
156
+ observe(operation);
157
+ },
158
+ call: (nested) => {
159
+ if (!open)
160
+ return Promise.resolve(result("denied", "Capability invocation has ended."));
161
+ const child = this.#call(context, nested, descriptor.effect, descriptor.requiredPermissions).then((outcome) => {
162
+ outcome.operations.forEach(observe);
163
+ effect = accumulatedEffect(effect, outcome.effect);
164
+ return outcome;
165
+ });
166
+ children.push(child);
167
+ return child;
168
+ }
169
+ }));
170
+ }
171
+ finally {
172
+ open = false;
173
+ // A wrapper throwing early must not detach its already-issued child
174
+ // operations from the returned evidence or release the parent handle.
175
+ await Promise.all(children);
176
+ }
177
+ });
178
+ if (descriptor.effect === "local-mutation")
179
+ effect = accumulatedEffect(effect, "confirmed");
180
+ // An external implementation without an owner's operation reference is not
181
+ // proof of success. Preserve uncertainty rather than return a value success.
182
+ if (descriptor.effect === "external-operation" && !operations.length) {
183
+ return { kind: "failed", detail: "External implementation returned no operation evidence.", effect: "possible", operations, ...origin };
184
+ }
185
+ let output;
186
+ try {
187
+ output = JSON.parse(JSON.stringify(value, (_key, item) => {
188
+ if ((typeof item === "number" && !Number.isFinite(item))
189
+ || typeof item === "bigint" || typeof item === "function" || typeof item === "symbol") {
190
+ throw new Error("Output contains a non-JSON value.");
191
+ }
192
+ return item;
193
+ }));
194
+ }
195
+ catch {
196
+ if (descriptor.effect !== "query")
197
+ effect = accumulatedEffect(effect, "possible");
198
+ return { kind: "invalid", detail: "Output is not JSON serializable.", effect, operations, ...origin };
199
+ }
200
+ const outputError = capabilitySchemaError(descriptor.outputSchema, output);
201
+ if (outputError) {
202
+ if (descriptor.effect !== "query")
203
+ effect = accumulatedEffect(effect, "possible");
204
+ return { kind: "invalid", detail: `Output ${outputError}`, effect, operations, ...origin };
205
+ }
206
+ return { kind: operations.length ? "operation" : "value", value: output, effect, operations, ...origin };
207
+ }
208
+ catch (error) {
209
+ // An unrelated observation (including a historical queued Job with none)
210
+ // cannot prove that a failing effectful wrapper performed no other action.
211
+ // Preserve the owner's precise facts while reporting wrapper uncertainty.
212
+ if (entered && descriptor.effect !== "query")
213
+ effect = accumulatedEffect(effect, "possible");
214
+ return {
215
+ kind: entered ? "failed" : "unavailable",
216
+ detail: error instanceof Error ? error.message : "Capability invocation failed.",
217
+ ...(error instanceof CapabilityExecutionError ? { value: error.value } : {}),
218
+ effect, operations, ...origin
219
+ };
220
+ }
221
+ }
222
+ #unavailable(entry, authorized, visiting) {
223
+ if (entry.unavailable !== undefined)
224
+ return entry.unavailable;
225
+ if (!this.host.isAvailable(entry.provider))
226
+ return "Provider implementation is not available.";
227
+ if (visiting.has(entry))
228
+ return "Required capability dependency cycle.";
229
+ const path = new Set([...visiting, entry]);
230
+ // Only declared exact name/version edges are checked. No installation,
231
+ // priority policy, version solver or persisted dependency state is involved.
232
+ const missing = entry.required?.filter((dependency) => !authorized.some((candidate) => (candidate.name === dependency.name && candidate.contractVersion === dependency.contractVersion
233
+ && this.#unavailable(candidate, authorized, path) === undefined)));
234
+ if (missing?.length)
235
+ return `Missing available required capabilities: ${missing.map((dependency) => `${dependency.name}@${dependency.contractVersion}`).join(", ")}.`;
236
+ return undefined;
237
+ }
238
+ #publish(descriptors, core) {
239
+ if (!descriptors.length)
240
+ return;
241
+ const prepared = this.#prepare(descriptors, core);
242
+ const provider = prepared[0].provider;
243
+ // Validate acquisition before publishing; no implementation is invoked.
244
+ if (prepared.some((entry) => !entry.unavailable)) {
245
+ const handle = this.host.acquire(provider);
246
+ try {
247
+ if (typeof handle.value?.invoke !== "function")
248
+ throw new Error("Provider has no capability implementation.");
249
+ }
250
+ finally {
251
+ void handle.release();
252
+ }
253
+ }
254
+ this.#descriptors = [
255
+ ...this.#descriptors.filter((entry) => entry.provider.id !== provider.id),
256
+ ...prepared.map((entry) => deepFreeze(entry))
257
+ ];
258
+ }
259
+ #prepare(descriptors, core) {
260
+ if (!descriptors.length)
261
+ return [];
262
+ const prepared = structuredClone(descriptors);
263
+ const provider = prepared[0].provider;
264
+ if (!core && this.#coreProviders.has(provider.id))
265
+ throw new Error("Core Provider identity is reserved.");
266
+ const names = new Set();
267
+ for (const entry of prepared) {
268
+ if (!/^[a-z][a-z0-9-]*(?:\.[a-z][a-z0-9-]*)+$/u.test(entry.name)
269
+ || !entry.contractVersion?.trim() || !entry.source?.trim()
270
+ || !Object.hasOwn(effectRank, entry.effect)
271
+ || !Array.isArray(entry.requiredPermissions)
272
+ || entry.requiredPermissions.some((permission) => typeof permission !== "string" || !permission.trim())) {
273
+ throw new Error("Invalid capability descriptor.");
274
+ }
275
+ if (!core && reserved.has(entry.name.split(".")[0]))
276
+ throw new Error("Core capability namespace is reserved.");
277
+ if (!["global", "project", "task"].includes(entry.scope.kind)
278
+ || (entry.scope.kind !== "global" && !entry.scope.id?.trim()))
279
+ throw new Error("Invalid capability scope.");
280
+ if (entry.provider.id !== provider.id || entry.provider.generation !== provider.generation) {
281
+ throw new Error("Publish one complete Provider generation at a time.");
282
+ }
283
+ const key = `${entry.name}@${entry.contractVersion}`;
284
+ if (names.has(key))
285
+ throw new Error(`Duplicate capability: ${key}.`);
286
+ names.add(key);
287
+ checkCapabilitySchema(entry.inputSchema);
288
+ checkCapabilitySchema(entry.outputSchema);
289
+ checkSurfaceDescriptors(entry);
290
+ for (const dependency of entry.required ?? []) {
291
+ if (!dependency.name?.trim() || !dependency.contractVersion?.trim())
292
+ throw new Error("Invalid required capability.");
293
+ }
294
+ }
295
+ return [...prepared];
296
+ }
297
+ }
298
+ function visible(scope, visibility) {
299
+ return scope.kind === "global" || (scope.kind === "task"
300
+ ? visibility.taskIds.includes(scope.id) : visibility.projectIds.includes(scope.id));
301
+ }
302
+ function result(kind, detail) {
303
+ return { kind, effect: "none", operations: [], ...(detail === undefined ? {} : { detail }) };
304
+ }
305
+ function accumulatedEffect(a, b) {
306
+ return a === "confirmed" || b === "confirmed" ? "confirmed" : a === "possible" || b === "possible" ? "possible" : "none";
307
+ }
308
+ function deepFreeze(value) {
309
+ if (value !== null && typeof value === "object") {
310
+ Object.values(value).forEach(deepFreeze);
311
+ Object.freeze(value);
312
+ }
313
+ return value;
314
+ }
@@ -0,0 +1,91 @@
1
+ import { isDeepStrictEqual } from "node:util";
2
+ export function checkCapabilitySchema(schema) {
3
+ if (typeof schema !== "object" || schema === null || Array.isArray(schema)) {
4
+ throw new Error("Capability schema must be an object.");
5
+ }
6
+ const keywords = new Set([
7
+ "type", "properties", "required", "additionalProperties", "items",
8
+ "enum", "const", "anyOf", "minLength", "minItems"
9
+ ]);
10
+ for (const key of Object.keys(schema)) {
11
+ if (!keywords.has(key))
12
+ throw new Error(`Unsupported schema keyword: ${key}.`);
13
+ }
14
+ if (schema.type !== undefined
15
+ && !["object", "array", "string", "number", "integer", "boolean", "null"].includes(schema.type)) {
16
+ throw new Error("Unsupported schema type.");
17
+ }
18
+ if (schema.required !== undefined && (!Array.isArray(schema.required)
19
+ || schema.required.some((key) => typeof key !== "string")))
20
+ throw new Error("Invalid schema required.");
21
+ for (const bound of [schema.minLength, schema.minItems]) {
22
+ if (bound !== undefined && (!Number.isSafeInteger(bound) || bound < 0))
23
+ throw new Error("Invalid schema bound.");
24
+ }
25
+ if (schema.enum !== undefined && (!Array.isArray(schema.enum) || schema.enum.length === 0)) {
26
+ throw new Error("Invalid schema enum.");
27
+ }
28
+ if (schema.anyOf !== undefined) {
29
+ if (!Array.isArray(schema.anyOf) || !schema.anyOf.length)
30
+ throw new Error("Invalid schema anyOf.");
31
+ schema.anyOf.forEach(checkCapabilitySchema);
32
+ }
33
+ if (schema.properties !== undefined) {
34
+ if (typeof schema.properties !== "object" || schema.properties === null || Array.isArray(schema.properties)) {
35
+ throw new Error("Invalid schema properties.");
36
+ }
37
+ Object.values(schema.properties).forEach(checkCapabilitySchema);
38
+ }
39
+ if (schema.items !== undefined)
40
+ checkCapabilitySchema(schema.items);
41
+ if (schema.additionalProperties !== undefined && typeof schema.additionalProperties !== "boolean") {
42
+ checkCapabilitySchema(schema.additionalProperties);
43
+ }
44
+ }
45
+ export function capabilitySchemaError(schema, value, path = "$") {
46
+ if ("const" in schema && !isDeepStrictEqual(value, schema.const))
47
+ return `${path}: unexpected value.`;
48
+ if (schema.enum && !schema.enum.some((item) => isDeepStrictEqual(item, value)))
49
+ return `${path}: not in enum.`;
50
+ if (schema.anyOf && !schema.anyOf.some((branch) => capabilitySchemaError(branch, value) === undefined)) {
51
+ return `${path}: no matching schema branch.`;
52
+ }
53
+ const type = value === null ? "null" : Array.isArray(value) ? "array" : typeof value;
54
+ if (schema.type !== undefined && (schema.type === "integer"
55
+ ? typeof value !== "number" || !Number.isSafeInteger(value)
56
+ : type !== schema.type))
57
+ return `${path}: expected ${schema.type}.`;
58
+ if (typeof value === "number" && !Number.isFinite(value))
59
+ return `${path}: non-finite number.`;
60
+ if (typeof value === "string" && schema.minLength !== undefined && value.length < schema.minLength) {
61
+ return `${path}: string too short.`;
62
+ }
63
+ if (Array.isArray(value)) {
64
+ if (schema.minItems !== undefined && value.length < schema.minItems)
65
+ return `${path}: array too short.`;
66
+ if (schema.items) {
67
+ for (const [index, item] of value.entries()) {
68
+ const error = capabilitySchemaError(schema.items, item, `${path}[${index}]`);
69
+ if (error)
70
+ return error;
71
+ }
72
+ }
73
+ }
74
+ else if (type === "object") {
75
+ const record = value;
76
+ for (const key of schema.required ?? []) {
77
+ if (!Object.hasOwn(record, key))
78
+ return `${path}.${key}: required.`;
79
+ }
80
+ for (const [key, item] of Object.entries(record)) {
81
+ const property = Object.hasOwn(schema.properties ?? {}, key) ? schema.properties[key] : undefined;
82
+ if (!property && schema.additionalProperties === false)
83
+ return `${path}.${key}: unexpected property.`;
84
+ const child = property ?? (typeof schema.additionalProperties === "object" ? schema.additionalProperties : undefined);
85
+ const error = child && capabilitySchemaError(child, item, `${path}.${key}`);
86
+ if (error)
87
+ return error;
88
+ }
89
+ }
90
+ return undefined;
91
+ }
@@ -14,11 +14,27 @@ export class InstanceHost {
14
14
  void drained.catch(() => undefined);
15
15
  const ref = Object.freeze({ ...implementation });
16
16
  this.#instances.set(key, {
17
- implementation: ref, value, references: 0, detached: false,
17
+ implementation: ref, value, references: 0, detached: false, disposed: false,
18
18
  disposers: [...ownedDisposers], drained, resolve, reject
19
19
  });
20
20
  return ref;
21
21
  }
22
+ /** Read-only availability for the rebuildable capability directory. This is
23
+ * not an acquisition or a grant; acquire still checks immediately before use. */
24
+ isAvailable(implementation) {
25
+ const instance = this.#instances.get(implementationKey(implementation));
26
+ return !this.#closed && instance !== undefined && !instance.detached;
27
+ }
28
+ /** Only the Host reports actual reference/disposal facts. Metadata consumers
29
+ * must not persist these observations as independently writable state. */
30
+ inspect(providerId) {
31
+ return [...this.#instances.values()].filter((instance) => instance.implementation.id === providerId && !instance.disposed)
32
+ .map((instance) => Object.freeze({
33
+ implementation: instance.implementation,
34
+ references: instance.references,
35
+ accepting: !this.#closed && !instance.detached
36
+ }));
37
+ }
22
38
  acquire(implementation) {
23
39
  const instance = this.#instances.get(implementationKey(implementation));
24
40
  if (this.#closed || instance === undefined || instance.detached) {
@@ -80,6 +96,7 @@ export class InstanceHost {
80
96
  // Re-attaching a generation must not resurrect an old reference.
81
97
  instance.value = undefined;
82
98
  instance.disposers = [];
99
+ instance.disposed = true;
83
100
  if (errors.length)
84
101
  throw new AggregateError(errors, "Instance cleanup failed.");
85
102
  })();
@@ -1,19 +1,22 @@
1
1
  import { createDurableJobControl } from "../controller/jobControl.js";
2
2
  import { JOB_RUNNER_IMPLEMENTATION } from "../job/durableJob.js";
3
3
  import { InstanceHost } from "./instanceHost.js";
4
+ import { createBuiltinCapabilities } from "./builtinCapabilities.js";
4
5
  /** Called once by the existing Controller root. Does not open a Store, start
5
6
  * another Controller, or provide arbitrary persistence to plugin code.
6
7
  * T02 registers contributions on this Host and wraps this same Job control.
7
8
  */
8
- export function createKernelPorts(store, runner) {
9
+ export function createKernelPorts(store, runner, signal = () => undefined, contextProviders = []) {
9
10
  const host = new InstanceHost();
10
11
  const runnerImplementation = host.attach(JOB_RUNNER_IMPLEMENTATION, runner);
11
12
  const runnerHandle = host.acquire(runnerImplementation);
12
13
  const jobImplementation = host.attach({ id: "yui:job-control", generation: "1" }, createDurableJobControl(store));
13
14
  // The Controller is a long-lived consumer of this exact implementation.
14
15
  const jobHandle = host.acquire(jobImplementation);
16
+ const capabilities = createBuiltinCapabilities(host, store, jobHandle.value, signal, contextProviders);
15
17
  return {
16
18
  host,
19
+ capabilities,
17
20
  jobImplementation,
18
21
  runnerImplementation,
19
22
  runner: runnerHandle.value,