@zq-silk/yui 0.15.7 → 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 (281) 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 +157 -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 +178 -95
  25. package/dist/commands/agentCommands.js +72 -14
  26. package/dist/commands/capabilityCommands.js +9 -6
  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 +1188 -710
  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 +17 -6
  64. package/dist/controller/clientRuntime.js +65 -92
  65. package/dist/controller/controller.js +114 -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 +529 -479
  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 +247 -12
  106. package/dist/kernel/capabilityRegistry.js +64 -18
  107. package/dist/kernel/instanceHost.js +12 -1
  108. package/dist/kernel/kernelPorts.js +2 -2
  109. package/dist/lifecycle/canonicalLifecycleEvent.js +44 -49
  110. package/dist/lifecycle/exactRunTerminalization.js +449 -0
  111. package/dist/message/message.js +62 -6
  112. package/dist/message/messageContinuation.js +204 -0
  113. package/dist/observability/executionAudit.js +70 -72
  114. package/dist/observability/faultClassification.js +2 -2
  115. package/dist/observability/orchestrationMetrics.js +8 -8
  116. package/dist/operator/operatorSessionHistory.js +1 -7
  117. package/dist/output/agentConfigurationPresentation.js +8 -3
  118. package/dist/output/agentRunConfigurationPresentation.js +128 -0
  119. package/dist/output/rolePresentation.js +54 -3
  120. package/dist/plugins/pluginChild.js +104 -0
  121. package/dist/plugins/pluginIntent.js +26 -0
  122. package/dist/plugins/pluginInterpreter.js +43 -0
  123. package/dist/plugins/pluginPackage.js +101 -0
  124. package/dist/plugins/pluginProcess.js +112 -0
  125. package/dist/plugins/pluginService.js +380 -0
  126. package/dist/profile/agentProfile.js +1 -1
  127. package/dist/repository/gitWorkspace.js +26 -4
  128. package/dist/repository/project.js +19 -4
  129. package/dist/repository/taskBaseFreshness.js +13 -13
  130. package/dist/repository/taskWorkspaceCoordinator.js +20 -27
  131. package/dist/repository/taskWorkspacePreparer.js +344 -83
  132. package/dist/resources/autoResourceGc.js +3 -3
  133. package/dist/resources/liveReferences.js +3 -3
  134. package/dist/resources/projectResource.js +123 -0
  135. package/dist/resources/projectResourceService.js +421 -0
  136. package/dist/resources/resourceDiscovery.js +6 -6
  137. package/dist/resources/resourceGc.js +1 -1
  138. package/dist/resources/resourceRegistrar.js +1 -1
  139. package/dist/resources/resourceTypes.js +1 -1
  140. package/dist/review/deltaRecheck.js +3 -3
  141. package/dist/review/reviewAcceptance.js +16 -16
  142. package/dist/review/reviewDecision.js +7 -7
  143. package/dist/review/reviewRound.js +21 -20
  144. package/dist/review/reviewerAvailability.js +2 -2
  145. package/dist/role/role.js +51 -7
  146. package/dist/role/taskRoleUpdate.js +30 -0
  147. package/dist/runtime/acpProtocol.js +425 -0
  148. package/dist/runtime/acpSession.js +731 -0
  149. package/dist/runtime/acpSessionConfiguration.js +260 -0
  150. package/dist/runtime/agentDriver.js +30 -11
  151. package/dist/runtime/agentEndpoint.js +278 -0
  152. package/dist/runtime/agentEndpointIdentity.js +86 -0
  153. package/dist/runtime/agentEndpointOwnership.js +239 -0
  154. package/dist/runtime/agentError.js +2 -10
  155. package/dist/runtime/agentHost.js +565 -314
  156. package/dist/runtime/agentRunConfiguration.js +258 -0
  157. package/dist/runtime/builtinAgentDrivers.js +134 -18
  158. package/dist/runtime/builtinAgentErrorMappers.js +55 -3
  159. package/dist/runtime/builtinTranscriptUsage.js +1 -1
  160. package/dist/runtime/claude-process-owner +0 -0
  161. package/dist/runtime/codexAppServerRuntime.js +38 -30
  162. package/dist/runtime/codexInteractiveHost.js +41 -6
  163. package/dist/runtime/continuationManager.js +2 -6
  164. package/dist/runtime/executionEnvironment.js +30 -0
  165. package/dist/runtime/firstProgressAdvisory.js +11 -11
  166. package/dist/runtime/index.js +4 -3
  167. package/dist/runtime/jsonLineChannel.js +109 -0
  168. package/dist/runtime/launchBroker.js +91 -16
  169. package/dist/runtime/launchDiagnostics.js +2 -2
  170. package/dist/runtime/lifecycleReservation.js +10 -18
  171. package/dist/runtime/managedCaller.js +61 -17
  172. package/dist/runtime/nativeSessionControl.js +102 -0
  173. package/dist/runtime/ports.js +6 -21
  174. package/dist/runtime/processExitObservation.js +8 -7
  175. package/dist/runtime/promptEnvelope.js +17 -6
  176. package/dist/runtime/providerContinuation.js +3 -9
  177. package/dist/runtime/providerContinuationReconciliationService.js +4 -13
  178. package/dist/runtime/providerControl.js +2 -7
  179. package/dist/runtime/providerRuntimeIdentity.js +110 -222
  180. package/dist/runtime/providerRuntimeReconciler.js +5 -9
  181. package/dist/runtime/runtimeBinding.js +0 -1
  182. package/dist/runtime/runtimeContinuationProjection.js +4 -7
  183. package/dist/runtime/runtimeDeadlines.js +9 -0
  184. package/dist/runtime/runtimeHealthPolicy.js +1 -1
  185. package/dist/runtime/runtimeObservation.js +29 -65
  186. package/dist/runtime/runtimeProjection.js +43 -51
  187. package/dist/runtime/runtimeSessionCandidate.js +1 -3
  188. package/dist/runtime/sessionLaunchRequest.js +3 -7
  189. package/dist/runtime/sessionOwnerIdentity.js +7 -54
  190. package/dist/runtime/sessionOwnerRegistry.js +22 -17
  191. package/dist/runtime/sessionReconciliation.js +4 -8
  192. package/dist/runtime/sessionTerminationGuard.js +70 -259
  193. package/dist/runtime/sessionTokenMetrics.js +5 -16
  194. package/dist/runtime/structuredProviderHost.js +237 -117
  195. package/dist/runtime/taskRuntimeIsolation.js +39 -122
  196. package/dist/runtime/tmuxAdapters.js +39 -86
  197. package/dist/scheduler/activeRoleRunDelivery.js +354 -0
  198. package/dist/scheduler/leaderWakeupProcessor.js +75 -266
  199. package/dist/scheduler/operatorInputNotificationProcessor.js +1 -1
  200. package/dist/scheduler/ports.js +80 -9
  201. package/dist/scheduler/{roleTurnLiveness.js → roleRunLiveness.js} +26 -30
  202. package/dist/scheduler/{roleTurnStall.js → roleRunStall.js} +128 -139
  203. package/dist/scheduler/taskExecutionProjection.js +120 -124
  204. package/dist/scheduler/taskObservabilityProjection.js +29 -29
  205. package/dist/scheduler/taskWake.js +11 -4
  206. package/dist/scheduler/wakeReason.js +9 -1
  207. package/dist/setup/setupCommand.js +1 -0
  208. package/dist/storage/migrations/agentRunContract.js +159 -0
  209. package/dist/storage/migrations/removeRuntimeGeneration.js +207 -0
  210. package/dist/storage/sqliteSchema.js +431 -5
  211. package/dist/storage/sqliteStore.js +375 -220
  212. package/dist/storage/storageVersions.js +1 -1
  213. package/dist/storage/storeRpc.js +10 -5
  214. package/dist/storage/taskStore.js +13 -11
  215. package/dist/storage/upgrade/upgradeOrchestrator.js +5 -7
  216. package/dist/surface/surfaceContributions.js +102 -0
  217. package/dist/task/completionReadiness.js +32 -6
  218. package/dist/task/deliveryGuard.js +16 -16
  219. package/dist/task/draftPlan.js +72 -12
  220. package/dist/task/nextAction.js +144 -128
  221. package/dist/task/remoteDelivery.js +6 -6
  222. package/dist/task/task.js +184 -18
  223. package/dist/task/taskActivation.js +301 -0
  224. package/dist/task/taskActivationService.js +392 -0
  225. package/dist/task/taskRecordReference.js +5 -4
  226. package/dist/task/taskRecordRetirement.js +1 -1
  227. package/dist/telemetry/sqliteTelemetryStore.js +55 -68
  228. package/dist/telemetry/telemetryConfig.js +14 -14
  229. package/dist/telemetry/telemetryWiring.js +2 -2
  230. package/dist/web/assets/assetManifest.js +2 -0
  231. package/dist/web/assets/client/app.js +120 -20
  232. package/dist/web/assets/client/components.js +87 -54
  233. package/dist/web/assets/client/i18n.js +83 -41
  234. package/dist/web/assets/client/markdown.js +1 -1
  235. package/dist/web/assets/client/taskSurface.js +353 -0
  236. package/dist/web/assets/client/view.js +49 -44
  237. package/dist/web/assets/shell.js +1 -1
  238. package/dist/web/assets/styles/cards.js +22 -4
  239. package/dist/web/controllerWeb.js +60 -0
  240. package/dist/web/webMutation.js +28 -0
  241. package/dist/web/webServer.js +118 -8
  242. package/dist/web/webSnapshot.js +81 -74
  243. package/dist/web/webTaskSurface.js +64 -0
  244. package/dist/workItem/dependencyGate.js +1 -1
  245. package/dist/workItem/workItem.js +80 -48
  246. package/dist/workspace/workItemChangeSetManager.js +16 -9
  247. package/docs/agent-result-consumption.md +94 -0
  248. package/docs/agent-runtime-drivers.md +91 -0
  249. package/docs/architecture/README.md +38 -0
  250. package/docs/architecture/capabilities-and-resources.md +79 -0
  251. package/docs/managed-turn-and-session-runtime.md +222 -0
  252. package/docs/observability/README.md +81 -0
  253. package/docs/plugin-sdk.md +290 -0
  254. package/docs/provider-runtime.md +163 -0
  255. package/docs/release-workflow.md +303 -0
  256. package/docs/roles-and-configuration.md +113 -0
  257. package/docs/sqlite-control-plane-design.md +76 -0
  258. package/docs/task-dag-semantics.md +57 -0
  259. package/docs/task-delivery.md +103 -0
  260. package/docs/task-local-identity.md +6 -6
  261. package/docs/testing/verification-levels.md +86 -0
  262. package/i18n/README.zh-CN.md +98 -739
  263. package/package.json +2 -2
  264. package/skills/yui-leader/SKILL.md +130 -103
  265. package/skills/yui-leader/references/integration.md +39 -0
  266. package/skills/yui-leader/references/replicated-execution.md +42 -0
  267. package/skills/yui-leader/references/task-plugins.md +33 -0
  268. package/skills/yui-operator/SKILL.md +30 -59
  269. package/skills/yui-reviewer/SKILL.md +35 -36
  270. package/skills/yui-runtime/SKILL.md +88 -24
  271. package/skills/yui-runtime/references/publication.md +22 -0
  272. package/skills/yui-runtime/references/recovery.md +64 -0
  273. package/skills/yui-worker/SKILL.md +37 -39
  274. package/dist/cli/roleOptionCatalog.js +0 -68
  275. package/dist/context/sourceTurnContext.js +0 -30
  276. package/dist/execution/reviewMainTurn.js +0 -161
  277. package/dist/execution/workItemMainTurn.js +0 -164
  278. package/dist/lifecycle/exactTurnTerminalization.js +0 -407
  279. package/dist/runtime/preallocatedNativeSession.js +0 -13
  280. package/dist/runtime/runtimeStopReceipt.js +0 -42
  281. package/dist/scheduler/activeRoleTurnDelivery.js +0 -315
@@ -98,14 +98,14 @@ function collectTaskStatuses(store) {
98
98
  return statuses;
99
99
  }
100
100
  /**
101
- * Workspace paths claimed by active durable Jobs. An active Turn still
101
+ * Workspace paths claimed by active durable Jobs. An active AgentRun still
102
102
  * holds its workspace even after the managed workspace record is gone, so
103
- * those paths stay protected until the Turn finishes.
103
+ * those paths stay protected until the AgentRun finishes.
104
104
  */
105
105
  function collectActiveWorkspaceOwnerPaths(store) {
106
106
  const paths = [];
107
107
  for (const task of store.listTasks()) {
108
- for (const run of store.listTurns(task.id)) {
108
+ for (const run of store.listRuns(task.id)) {
109
109
  if (run.status !== "active")
110
110
  continue;
111
111
  const workspace = run.workspace;
@@ -142,7 +142,7 @@ export async function scanLiveReferences(input) {
142
142
  }
143
143
  }
144
144
  }
145
- // 6. Active durable workspace owners (e.g. an active Turn launch).
145
+ // 6. Active durable workspace owners (e.g. an active AgentRun launch).
146
146
  const activeOwners = input.ports?.activeWorkspaceOwners !== undefined
147
147
  ? input.ports.activeWorkspaceOwners()
148
148
  : [];
@@ -232,7 +232,7 @@ function readActiveReleaseClaims(home) {
232
232
  }
233
233
  }
234
234
  /**
235
- * Read Session owner records (`runtime/session-owners/<runtimeGenerationId>.json`). A
235
+ * Read Session owner records (`runtime/session-owners/<pid-startIdentity>.json`). A
236
236
  * record whose Provider root is physically alive with a matching start
237
237
  * identity protects its runtime root. A dead PID is stale (reconciliation
238
238
  * removes those records); an identity conflict or unreadable record fails
@@ -301,7 +301,7 @@ function readSessionOwnerClaims(home) {
301
301
  if (typeof record.runtimeRoot === "string" && record.runtimeRoot.length > 0) {
302
302
  claims.push({
303
303
  path: resolve(record.runtimeRoot),
304
- token: `session-owner:${typeof record.runtimeGenerationId === "string" ? record.runtimeGenerationId : entry.name}`
304
+ token: `session-owner:${pid}-${startIdentity}`
305
305
  });
306
306
  }
307
307
  }
@@ -0,0 +1,123 @@
1
+ import { createHash } from "node:crypto";
2
+ import { isAbsolute, resolve } from "node:path";
3
+ import { requireIdentity, requireText, requireTimestamp } from "../domain/validation.js";
4
+ export function validateExecutionEnvironmentSnapshot(value) {
5
+ if (!value || typeof value !== "object")
6
+ throw new Error("Execution environment must be an object.");
7
+ requireIdentity(value.taskId, "Execution environment Task");
8
+ requireIdentity(value.preparationId, "Execution environment preparation");
9
+ if (value.environmentRef !== `${value.taskId}/${value.preparationId}`) {
10
+ throw new Error("Execution environment reference does not match its preparation.");
11
+ }
12
+ if (!["read", "write"].includes(value.access) || value.isolation !== "trusted-local" || !value.directory) {
13
+ throw new Error("Execution environment requires a trusted-local directory and explicit access.");
14
+ }
15
+ requireText(value.directory.path, "Execution environment path");
16
+ if (!isAbsolute(value.directory.path) || resolve(value.directory.path) !== value.directory.path) {
17
+ throw new Error("Execution environment path must be absolute and normalized.");
18
+ }
19
+ requireText(value.directory.device, "Execution environment device");
20
+ requireText(value.directory.inode, "Execution environment inode");
21
+ if (!["preparation", "user"].includes(value.directory.ownership))
22
+ throw new Error("Invalid execution directory owner.");
23
+ return value;
24
+ }
25
+ export function contentDigest(content) {
26
+ return createHash("sha256").update(content, "utf8").digest("hex");
27
+ }
28
+ export function validateArtifact(artifact) {
29
+ if (artifact.schemaVersion !== 1)
30
+ throw new Error("Artifact schemaVersion must be 1.");
31
+ requireIdentity(artifact.id, "Artifact id");
32
+ requireIdentity(artifact.taskId, "Artifact Task");
33
+ requireText(artifact.displayName, "Artifact name");
34
+ requireText(artifact.mediaType, "Artifact media type");
35
+ requireText(artifact.provenance, "Artifact provenance");
36
+ requireTimestamp(artifact.createdAt, "Artifact timestamp");
37
+ if (artifact.kind === "content" || artifact.kind === "receipt") {
38
+ if (typeof artifact.content !== "string" || Buffer.byteLength(artifact.content) > 8 * 1024 * 1024) {
39
+ throw new Error("Artifact content must be UTF-8 text of at most 8 MiB.");
40
+ }
41
+ if (artifact.digest !== contentDigest(artifact.content))
42
+ throw new Error("Artifact content digest mismatch.");
43
+ if (artifact.kind === "receipt") {
44
+ requireIdentity(artifact.jobId, "Artifact Job");
45
+ requireText(artifact.receiptRef, "Artifact receipt");
46
+ }
47
+ }
48
+ else if (artifact.kind === "external-version") {
49
+ requireIdentity(artifact.resourceId, "Artifact resource");
50
+ requireText(artifact.version, "Artifact external version");
51
+ requireText(artifact.verification, "Artifact version verification");
52
+ }
53
+ else if (artifact.kind === "reference") {
54
+ requireText(artifact.locator, "Reference locator");
55
+ requireTimestamp(artifact.observedAt, "Reference observation");
56
+ }
57
+ else
58
+ throw new Error("Unknown Artifact kind.");
59
+ return artifact;
60
+ }
61
+ export function stableArtifactRef(artifact) {
62
+ validateArtifact(artifact);
63
+ if (artifact.kind === "reference")
64
+ throw new Error("Reference material is not a fixed result.");
65
+ return { taskId: artifact.taskId, artifactId: artifact.id, kind: artifact.kind,
66
+ ...("digest" in artifact ? { digest: artifact.digest } : {}) };
67
+ }
68
+ /** Listings expose locators and provenance without replaying every body. */
69
+ export function artifactSummary(artifact) {
70
+ if ("content" in artifact) {
71
+ const { content, ...summary } = artifact;
72
+ return { ...summary, contentBytes: Buffer.byteLength(content, "utf8") };
73
+ }
74
+ return artifact;
75
+ }
76
+ export function validateLocalResource(resource) {
77
+ if (resource.schemaVersion !== 1 || resource.kind !== "local-directory" || resource.ownership !== "user") {
78
+ throw new Error("Invalid local Resource record.");
79
+ }
80
+ requireIdentity(resource.id, "Resource id");
81
+ requireText(resource.displayName, "Resource name");
82
+ requireText(resource.path, "Resource path");
83
+ requireText(resource.device, "Resource device");
84
+ requireText(resource.inode, "Resource inode");
85
+ requireTimestamp(resource.createdAt, "Resource timestamp");
86
+ return resource;
87
+ }
88
+ export function validateEnvironmentPreparation(record) {
89
+ if (record.schemaVersion !== 1 || !["prepared", "adopted", "released"].includes(record.disposition)) {
90
+ throw new Error("Invalid Environment preparation.");
91
+ }
92
+ requireIdentity(record.id, "Preparation id");
93
+ requireIdentity(record.taskId, "Preparation Task");
94
+ requireText(record.intentDigest, "Preparation intent");
95
+ if (!Array.isArray(record.resourceRefs) || new Set(record.resourceRefs).size !== record.resourceRefs.length) {
96
+ throw new Error("Preparation resources must be unique.");
97
+ }
98
+ record.resourceRefs.forEach((id) => requireIdentity(id, "Preparation resource"));
99
+ if (!["read", "write"].includes(record.access))
100
+ throw new Error("Invalid preparation access.");
101
+ if (!["none", "trusted-local"].includes(record.isolation)
102
+ || (record.directory === undefined) !== (record.isolation === "none")) {
103
+ throw new Error("Environment isolation must describe the actual local preparation.");
104
+ }
105
+ if ((record.directory === undefined) !== (record.environmentRef === undefined)) {
106
+ throw new Error("Environment reference requires an actual directory.");
107
+ }
108
+ if (record.directory) {
109
+ requireText(record.directory.path, "Environment path");
110
+ requireText(record.directory.device, "Environment device");
111
+ requireText(record.directory.inode, "Environment inode");
112
+ if (!["preparation", "user"].includes(record.directory.ownership))
113
+ throw new Error("Invalid directory owner.");
114
+ }
115
+ requireTimestamp(record.createdAt, "Preparation timestamp");
116
+ requireTimestamp(record.updatedAt, "Preparation update");
117
+ if (record.releaseEvidence !== undefined) {
118
+ if (record.disposition !== "released")
119
+ throw new Error("Release evidence requires a released preparation.");
120
+ requireText(record.releaseEvidence, "Release evidence");
121
+ }
122
+ return record;
123
+ }
@@ -0,0 +1,421 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import { lstatSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmdirSync, statSync } from "node:fs";
3
+ import { join, relative, resolve, isAbsolute } from "node:path";
4
+ import { checkGrant, recordGrantUse } from "../grant/capabilityGrant.js";
5
+ import { requireIdentity, requireText, requireTimestamp } from "../domain/validation.js";
6
+ import { validateProject } from "../repository/project.js";
7
+ import { updateRole } from "../role/role.js";
8
+ import { saveTaskRoleUpdate } from "../role/taskRoleUpdate.js";
9
+ import { assertRoleRuntimeMutationAllowed } from "../commands/roleRuntimeGuard.js";
10
+ import { assertProviderConversationReplaceable, currentProviderConversation } from "../runtime/providerRuntimeIdentity.js";
11
+ import { projectProviderContinuations } from "../runtime/runtimeContinuationProjection.js";
12
+ import { contentDigest, validateArtifact, stableArtifactRef, validateExecutionEnvironmentSnapshot } from "./projectResource.js";
13
+ export function validateArtifactInput(value) {
14
+ if (!value || typeof value !== "object" || Array.isArray(value))
15
+ throw new Error("Artifact input must be an object.");
16
+ const input = value;
17
+ requireText(input.displayName, "Artifact name");
18
+ requireText(input.provenance, "Artifact provenance");
19
+ if (input.mediaType !== undefined)
20
+ requireText(input.mediaType, "Artifact media type");
21
+ switch (input.kind) {
22
+ case "content":
23
+ if (typeof input.content !== "string" || Buffer.byteLength(input.content) > 8 * 1024 * 1024) {
24
+ throw new Error("Artifact content must be UTF-8 text of at most 8 MiB.");
25
+ }
26
+ break;
27
+ case "receipt":
28
+ requireIdentity(input.jobId, "Artifact Job");
29
+ requireText(input.receiptRef, "Artifact receipt");
30
+ break;
31
+ case "external-version":
32
+ requireIdentity(input.resourceId, "Artifact resource");
33
+ requireText(input.version, "Artifact external version");
34
+ requireText(input.verification, "Artifact version verification");
35
+ break;
36
+ case "reference":
37
+ requireText(input.locator, "Reference locator");
38
+ requireTimestamp(input.observedAt, "Reference observation");
39
+ break;
40
+ default: throw new Error("Unknown Artifact kind.");
41
+ }
42
+ return input;
43
+ }
44
+ /** Trusted typed owner; the public caller is authenticated by the existing
45
+ * capability boundary. No new Store, worker, scheduling or Git authority. */
46
+ export function createProjectResources(store, now = () => new Date()) {
47
+ const task = (taskId) => {
48
+ const value = store.getTask(taskId);
49
+ if (!value)
50
+ throw new Error(`Task not found: ${taskId}.`);
51
+ return value;
52
+ };
53
+ const intent = (taskId) => {
54
+ const value = task(taskId);
55
+ return contentDigest(JSON.stringify({
56
+ // Git's moving delivery pointers do not change Resource intent.
57
+ projectBindings: value.projectBindings.map(({ projectId, directory, baseRef }) => ({ projectId, directory, baseRef })),
58
+ projects: value.projectBindings.map(({ projectId }) => {
59
+ const project = store.getProject(projectId);
60
+ return { projectId, resources: project?.resourceRefs, providers: project?.defaultCapabilityProviders };
61
+ })
62
+ }));
63
+ };
64
+ const openTask = (taskId) => {
65
+ const value = task(taskId);
66
+ if (value.status !== "draft" && value.status !== "active")
67
+ throw new Error("Task is not open for environment adoption.");
68
+ return value;
69
+ };
70
+ const resource = (resourceId) => {
71
+ const value = store.getLocalResource(resourceId);
72
+ if (!value)
73
+ throw new Error(`Local resource not found: ${resourceId}.`);
74
+ return value;
75
+ };
76
+ const grantFor = (taskId, resourceId, access, reservation, reservedOnly = false) => {
77
+ const target = resource(resourceId);
78
+ const grant = store.listCapabilityGrants(taskId).find((candidate) => {
79
+ // A Project scope is visibility, not an exhaustive Resource grant. A
80
+ // concrete resource bound is mandatory even for a broadly named action.
81
+ if (!candidate.parameterBounds.resourceId?.includes(resourceId))
82
+ return false;
83
+ if (reservedOnly && (reservation === undefined || !candidate.useReservations.includes(reservation)))
84
+ return false;
85
+ if (candidate.scope.homePath !== undefined && resolve(candidate.scope.homePath) !== target.path)
86
+ return false;
87
+ if (candidate.scope.projectIds?.some((id) => !task(taskId).projectBindings.some((binding) => binding.projectId === id)))
88
+ return false;
89
+ return checkGrant(candidate, { action: `resource.local.${access}`, params: { resourceId } }, now(), { skipUsesCheck: reservation !== undefined && candidate.useReservations.includes(reservation) }).allowed;
90
+ });
91
+ if (!grant)
92
+ throw new Error(`Resource grant unavailable: ${resourceId}/${access}.`);
93
+ return grant;
94
+ };
95
+ const preparation = (taskId, id) => {
96
+ const value = store.getEnvironmentPreparation(taskId, id);
97
+ if (!value)
98
+ throw new Error(`Environment preparation not found: ${taskId}/${id}.`);
99
+ return value;
100
+ };
101
+ const assertLocalWriteOwner = (path, access) => {
102
+ if (access !== "write")
103
+ return;
104
+ const protectedPaths = [
105
+ realpathSync(store.rootDirectory()),
106
+ ...store.listProjects().map((project) => project.path),
107
+ ...store.listTasks().flatMap((value) => store.listManagedWorkspaces(value.id)
108
+ .flatMap((workspace) => [workspace.root, ...workspace.entries.map((entry) => entry.path)]))
109
+ ];
110
+ if (protectedPaths.some((protectedPath) => overlaps(path, currentOrAbsentPath(protectedPath)))) {
111
+ throw new Error("Local resource overlaps a Yui Home or managed Git workspace; use its existing owner and Git operations.");
112
+ }
113
+ };
114
+ const resolveExecutionEnvironment = (taskId, id) => {
115
+ openTask(taskId);
116
+ const value = preparation(taskId, id);
117
+ if (value.disposition !== "adopted")
118
+ throw new Error("Execution environment must be adopted and not released.");
119
+ if (value.intentDigest !== intent(taskId))
120
+ throw new Error("Task resource/configuration intent changed; prepare again.");
121
+ if (!value.directory || !value.environmentRef || value.isolation !== "trusted-local") {
122
+ throw new Error("Empty preparation has no native execution directory; select a directory environment or managed workspace.");
123
+ }
124
+ assertDirectory(value.directory);
125
+ if (value.directory.ownership === "user")
126
+ assertLocalWriteOwner(value.directory.path, value.access);
127
+ for (const resourceId of value.resourceRefs) {
128
+ // Adoption already charged the bounded grant. Launch/resume may recheck
129
+ // that reservation, but must neither invent nor consume another use.
130
+ grantFor(taskId, resourceId, value.access, id, true);
131
+ const target = resource(resourceId);
132
+ assertDirectory(target);
133
+ if (target.path !== value.directory.path || target.device !== value.directory.device || target.inode !== value.directory.inode) {
134
+ throw new Error("Execution environment no longer matches its registered Resource.");
135
+ }
136
+ }
137
+ return validateExecutionEnvironmentSnapshot({
138
+ taskId, preparationId: id, environmentRef: value.environmentRef,
139
+ access: value.access, isolation: value.isolation, directory: { ...value.directory }
140
+ });
141
+ };
142
+ return {
143
+ resolveExecutionEnvironment,
144
+ bindEnvironment(taskId, roleName, preparationId, source = { source: "environment.bind" }) {
145
+ return store.transaction((tx) => {
146
+ openTask(taskId);
147
+ const role = tx.getRole(taskId, roleName);
148
+ if (!role)
149
+ throw new Error(`Role not found: ${taskId}/${roleName}.`);
150
+ assertRoleRuntimeMutationAllowed(tx, { scope: "task", taskId, roleName }, "environment binding");
151
+ const executionEnvironment = preparationId === null ? null : resolveExecutionEnvironment(taskId, preparationId);
152
+ const timestamp = now();
153
+ const updated = updateRole(role, { executionEnvironment }, timestamp);
154
+ // Desired configuration only: active native Sessions retain their
155
+ // immutable actual snapshot until explicitly ended.
156
+ saveTaskRoleUpdate(tx, role, updated, timestamp, source);
157
+ return updated;
158
+ });
159
+ },
160
+ saveArtifact(taskId, input) {
161
+ task(taskId);
162
+ validateArtifactInput(input);
163
+ const base = { schemaVersion: 1, taskId, id: `artifact-${randomUUID()}`,
164
+ displayName: input.displayName, mediaType: input.mediaType ?? "text/plain",
165
+ provenance: input.provenance, createdAt: now().toISOString() };
166
+ let artifact;
167
+ if (input.kind === "content") {
168
+ artifact = { ...base, kind: input.kind, content: input.content, digest: contentDigest(input.content) };
169
+ }
170
+ else if (input.kind === "receipt") {
171
+ const job = store.getDurableJob(taskId, input.jobId);
172
+ if (!job?.operation.receiptRefs.includes(input.receiptRef))
173
+ throw new Error("Receipt is not recorded by this Task's Job.");
174
+ const path = realpathSync(input.receiptRef);
175
+ if (!within(realpathSync(job.artifactsLocator), path))
176
+ throw new Error("Receipt is outside the Job artifact directory.");
177
+ const content = readText(path);
178
+ artifact = { ...base, kind: input.kind, jobId: job.id, receiptRef: input.receiptRef,
179
+ content, digest: contentDigest(content) };
180
+ }
181
+ else if (input.kind === "external-version") {
182
+ // Recording supplied version evidence is not an external resource read.
183
+ // Actual resource operations must separately pass their owner's grant.
184
+ artifact = { ...base, kind: input.kind, resourceId: input.resourceId,
185
+ version: input.version, verification: input.verification };
186
+ }
187
+ else {
188
+ artifact = { ...base, kind: input.kind, locator: input.locator, observedAt: input.observedAt };
189
+ }
190
+ store.saveArtifact(validateArtifact(artifact));
191
+ return artifact;
192
+ },
193
+ resultRefs(taskId, artifactIds) {
194
+ return artifactIds.map((id) => {
195
+ const artifact = store.getArtifact(taskId, id);
196
+ if (!artifact)
197
+ throw new Error(`Artifact not found: ${taskId}/${id}.`);
198
+ return stableArtifactRef(artifact);
199
+ });
200
+ },
201
+ gitResult(taskId, changeSetId) {
202
+ const changeSet = store.getChangeSet(taskId, changeSetId);
203
+ if (!changeSet)
204
+ throw new Error("ChangeSet not found in this Task.");
205
+ return {
206
+ kind: "external-version", resourceId: changeSet.projectId,
207
+ version: changeSet.headCommit,
208
+ verification: { taskId, changeSetId: changeSet.id, baseCommit: changeSet.baseCommit },
209
+ // This is a projection of the existing fixed Git result, not another
210
+ // mutable repository/worktree/Integration record.
211
+ changeSet
212
+ };
213
+ },
214
+ registerLocalDirectory(displayName, path) {
215
+ const identity = directoryIdentity(path);
216
+ return store.transaction((tx) => {
217
+ const existing = tx.listLocalResources().find((value) => value.device === identity.device && value.inode === identity.inode);
218
+ if (existing)
219
+ return existing;
220
+ const value = { schemaVersion: 1, id: `resource-${randomUUID()}`,
221
+ kind: "local-directory", displayName: requireText(displayName, "Resource name"),
222
+ ...identity, ownership: "user", createdAt: now().toISOString() };
223
+ tx.saveLocalResource(value);
224
+ return value;
225
+ });
226
+ },
227
+ readLocalResource(taskId, resourceId) {
228
+ grantFor(taskId, resourceId, "read");
229
+ return resource(resourceId);
230
+ },
231
+ projectContext(taskId, projectId) {
232
+ if (!task(taskId).projectBindings.some((binding) => binding.projectId === projectId))
233
+ throw new Error("Project is outside the Task context.");
234
+ const project = store.getProject(projectId);
235
+ if (!project)
236
+ throw new Error("Project not found.");
237
+ return { id: project.id, name: project.name, knowledge: project.knowledge,
238
+ resourceRefs: project.resourceRefs, defaultCapabilityProviders: project.defaultCapabilityProviders };
239
+ },
240
+ configureProject(projectId, resourceRefs, defaultCapabilityProviders) {
241
+ return store.transaction((tx) => {
242
+ const project = tx.getProject(projectId);
243
+ if (!project || project.status !== "active")
244
+ throw new Error("Active Project not found.");
245
+ resourceRefs.forEach(resource);
246
+ const next = validateProject({ ...project, resourceRefs, defaultCapabilityProviders, updatedAt: now().toISOString() });
247
+ tx.saveProject(next);
248
+ return next;
249
+ });
250
+ },
251
+ prepare(taskId, plan) {
252
+ openTask(taskId);
253
+ let directory;
254
+ const id = `preparation-${randomUUID()}`;
255
+ const access = plan.kind === "local" ? plan.access : "write";
256
+ const resourceRefs = plan.kind === "local" ? [plan.resourceId] : [];
257
+ if (plan.kind === "local") {
258
+ grantFor(taskId, plan.resourceId, access);
259
+ const target = resource(plan.resourceId);
260
+ assertDirectory(target);
261
+ assertLocalWriteOwner(target.path, access);
262
+ directory = { path: target.path, device: target.device, inode: target.inode, ownership: "user" };
263
+ }
264
+ else if (plan.kind === "scratch") {
265
+ const parent = join(store.rootDirectory(), "environments");
266
+ mkdirSync(parent, { recursive: true, mode: 0o700 });
267
+ if (realpathSync(parent) !== resolve(parent))
268
+ throw new Error("Environment root must not be a symlink.");
269
+ directory = { ...directoryIdentity(mkdtempSync(join(parent, `${id}-`))), ownership: "preparation" };
270
+ }
271
+ try {
272
+ return store.transaction((tx) => {
273
+ openTask(taskId);
274
+ const timestamp = now().toISOString();
275
+ const value = { schemaVersion: 1, id, taskId,
276
+ disposition: "prepared", intentDigest: intent(taskId), resourceRefs, access,
277
+ isolation: directory ? "trusted-local" : "none",
278
+ ...(directory ? { directory, environmentRef: `${taskId}/${id}` } : {}),
279
+ createdAt: timestamp, updatedAt: timestamp };
280
+ tx.saveEnvironmentPreparation(value);
281
+ return value;
282
+ });
283
+ }
284
+ catch (error) {
285
+ if (directory?.ownership === "preparation") {
286
+ // Only the exact newly-created empty directory is eligible. Never
287
+ // recursively remove a partial/unknown external preparation.
288
+ try {
289
+ assertDirectory(directory);
290
+ rmdirSync(directory.path);
291
+ }
292
+ catch (cleanup) {
293
+ throw new AggregateError([error, cleanup], `Unadopted environment retained: ${directory.path}`);
294
+ }
295
+ }
296
+ throw error;
297
+ }
298
+ },
299
+ adopt(taskId, id) {
300
+ return store.transaction((tx) => {
301
+ openTask(taskId);
302
+ const value = preparation(taskId, id);
303
+ if (value.disposition === "released")
304
+ throw new Error("Environment was released.");
305
+ if (value.intentDigest !== intent(taskId))
306
+ throw new Error("Task resource/configuration intent changed; prepare again.");
307
+ if (value.directory)
308
+ assertDirectory(value.directory);
309
+ if (value.directory?.ownership === "user")
310
+ assertLocalWriteOwner(value.directory.path, value.access);
311
+ const grants = value.resourceRefs.map((resourceId) => grantFor(taskId, resourceId, value.access, id));
312
+ if (value.disposition === "adopted")
313
+ return value;
314
+ if (value.directory) {
315
+ for (const otherTask of tx.listTasks()) {
316
+ const conflict = tx.listEnvironmentPreparations(otherTask.id).some((other) => other.disposition === "adopted" && other.directory
317
+ && (value.access === "write" || other.access === "write")
318
+ && overlaps(other.directory.path, value.directory.path));
319
+ if (conflict)
320
+ throw new Error("Resource conflicts with an adopted environment; inspect its use or choose isolated scratch.");
321
+ }
322
+ }
323
+ grants.forEach((grant) => tx.saveCapabilityGrant(taskId, recordGrantUse(grant, now(), id)));
324
+ const adopted = { ...value, disposition: "adopted", updatedAt: now().toISOString() };
325
+ tx.saveEnvironmentPreparation(adopted);
326
+ return adopted;
327
+ });
328
+ },
329
+ release(taskId, id, evidence) {
330
+ return store.transaction((tx) => {
331
+ const value = preparation(taskId, id);
332
+ if (value.disposition === "released")
333
+ return value;
334
+ if (value.disposition === "adopted" && value.directory) {
335
+ requireText(evidence?.quiescence ?? "", "Actual quiescence evidence");
336
+ const references = (environment) => environment?.taskId === taskId && environment.preparationId === id;
337
+ const sessionSets = tx.listRoleSessionSets(taskId);
338
+ const continuations = projectProviderContinuations(tx.listEvents(taskId));
339
+ for (const set of sessionSets) {
340
+ const users = [...Object.values(set.sessions), ...(set.history ?? [])]
341
+ .filter((session) => references(session.effective.executionEnvironment));
342
+ if (set.providerBinding != null && users.some((session) => session.nativeSessionId === currentProviderConversation(set.providerBinding).conversationId)) {
343
+ // A dead Host cannot prove that the native daemon accepted
344
+ // nothing or that a detached input has finished.
345
+ assertProviderConversationReplaceable(set.providerBinding);
346
+ }
347
+ if (continuations.some((continuation) => continuation.roleName === set.owner.roleName
348
+ && (continuation.execution !== "quiescent"
349
+ || continuation.observation !== "exact" || continuation.identityConflict)
350
+ && (users.some((session) => session.agentId === continuation.identity.accountScope
351
+ && session.nativeSessionId === continuation.identity.conversationId)
352
+ || references(tx.getRun(taskId, continuation.runId)?.effective.executionEnvironment)))) {
353
+ throw new Error("Environment still has a live or unknown native continuation.");
354
+ }
355
+ }
356
+ if (value.directory && (tx.listRuns(taskId).some((run) => run.status === "active"
357
+ && (references(run.effective.executionEnvironment)
358
+ || (run.workspace && overlaps(run.workspace.root, value.directory.path))))
359
+ || sessionSets.some((set) => [...Object.values(set.sessions), ...(set.history ?? [])].some((session) => session.status === "active" && references(session.effective.executionEnvironment)))
360
+ || tx.listDurableJobs(taskId).some((job) => ["queued", "running", "unknown-needs-attention"].includes(job.status)
361
+ && overlaps(job.workspace, value.directory.path))))
362
+ throw new Error("Environment still has a live or unknown execution reference.");
363
+ }
364
+ if (value.directory?.ownership === "preparation") {
365
+ assertDirectory(value.directory);
366
+ const parent = realpathSync(join(store.rootDirectory(), "environments"));
367
+ if (!within(parent, value.directory.path) || !value.directory.path.startsWith(join(parent, `${id}-`))) {
368
+ throw new Error("Preparation directory is outside its owned root.");
369
+ }
370
+ // Content must first be saved or explicitly handled by its owner.
371
+ // Nonempty directories are retained with ENOTEMPTY, never force-deleted.
372
+ rmdirSync(value.directory.path);
373
+ }
374
+ const released = { ...value, disposition: "released", updatedAt: now().toISOString(),
375
+ ...(evidence === undefined ? {} : { releaseEvidence: requireText(evidence.quiescence, "Actual quiescence evidence") }) };
376
+ tx.saveEnvironmentPreparation(released);
377
+ return released;
378
+ });
379
+ }
380
+ };
381
+ }
382
+ function directoryIdentity(path) {
383
+ const canonical = realpathSync(resolve(path));
384
+ const stat = statSync(canonical, { bigint: true });
385
+ if (!stat.isDirectory())
386
+ throw new Error("Resource must be a local directory.");
387
+ return { path: canonical, device: String(stat.dev), inode: String(stat.ino) };
388
+ }
389
+ function assertDirectory(expected) {
390
+ if (lstatSync(expected.path).isSymbolicLink())
391
+ throw new Error("Directory identity changed to a symlink.");
392
+ const current = directoryIdentity(expected.path);
393
+ if (current.path !== expected.path || current.device !== expected.device || current.inode !== expected.inode) {
394
+ throw new Error("Directory identity changed; retained for owner inspection.");
395
+ }
396
+ }
397
+ function within(parent, path) {
398
+ const value = relative(parent, path);
399
+ return value !== "" && value !== ".." && !value.startsWith("../") && !isAbsolute(value);
400
+ }
401
+ function overlaps(left, right) {
402
+ return left === right || within(left, right) || within(right, left);
403
+ }
404
+ function currentOrAbsentPath(path) {
405
+ try {
406
+ return realpathSync(path);
407
+ }
408
+ catch (error) {
409
+ // Historical workspace references may outlive a removed directory. Retain
410
+ // that exact address; never manufacture a replacement or ignore other errors.
411
+ if (error.code === "ENOENT")
412
+ return resolve(path);
413
+ throw error;
414
+ }
415
+ }
416
+ function readText(path) {
417
+ const stat = statSync(path);
418
+ if (!stat.isFile() || stat.size > 8 * 1024 * 1024)
419
+ throw new Error("Receipt must be a file of at most 8 MiB.");
420
+ return readFileSync(path, "utf8");
421
+ }
@@ -179,8 +179,8 @@ function discoverRuntimeArtifacts(home, input) {
179
179
  for (const launchEntry of safeReaddir(join(taskRoot, ownerEntry.name))) {
180
180
  if (!launchEntry.isDirectory())
181
181
  continue;
182
- const generationRoot = join(taskRoot, ownerEntry.name, launchEntry.name);
183
- const marker = readTaskRuntimeMarker(generationRoot);
182
+ const runtimeRoot = join(taskRoot, ownerEntry.name, launchEntry.name);
183
+ const marker = readTaskRuntimeMarker(runtimeRoot);
184
184
  const owner = marker === undefined
185
185
  ? { home, taskId, basis: "naming-convention" }
186
186
  : {
@@ -193,9 +193,9 @@ function discoverRuntimeArtifacts(home, input) {
193
193
  discovered.push({
194
194
  record: createResourceRecord({
195
195
  kind: "runtime-artifact",
196
- path: resolve(generationRoot),
196
+ path: resolve(runtimeRoot),
197
197
  owner,
198
- ...(sizeOf(generationRoot) === undefined ? {} : { sizeBytes: sizeOf(generationRoot) }),
198
+ ...(sizeOf(runtimeRoot) === undefined ? {} : { sizeBytes: sizeOf(runtimeRoot) }),
199
199
  cleanliness: "n/a",
200
200
  activeRefs: [],
201
201
  disposition: "active"
@@ -352,9 +352,9 @@ function readDeploymentGitMetadata(path) {
352
352
  return { repositoryPath: path };
353
353
  }
354
354
  }
355
- function readTaskRuntimeMarker(generationRoot) {
355
+ function readTaskRuntimeMarker(runtimeRoot) {
356
356
  try {
357
- const marker = JSON.parse(readFileSync(join(generationRoot, ".yui-task-runtime-owner.json"), "utf8"));
357
+ const marker = JSON.parse(readFileSync(join(runtimeRoot, ".yui-task-runtime-owner.json"), "utf8"));
358
358
  const owner = marker.descriptor?.workspace?.owner;
359
359
  if (owner === undefined || owner === null)
360
360
  return {};
@@ -125,7 +125,7 @@ function classifyDiscoveredResources(discovered, registry, scan, input, home) {
125
125
  }
126
126
  /**
127
127
  * Collect active workspace owner path fragments from durable state.
128
- * These protect resources that an active Session, Job, or Turn still
128
+ * These protect resources that an active Session, Job, or AgentRun still
129
129
  * references even when the Task itself is terminal.
130
130
  */
131
131
  function collectActiveWorkspaceOwners(input) {
@@ -68,7 +68,7 @@ export class ResourceRegistrar {
68
68
  #registerTaskRuntimeIsolation(descriptor) {
69
69
  const owner = ownerFromManagedWorkspace(this.#home, descriptor.workspace.owner);
70
70
  this.#save([
71
- descriptor.roots.generation,
71
+ descriptor.roots.runtime,
72
72
  descriptor.roots.data,
73
73
  descriptor.roots.cache,
74
74
  descriptor.roots.temporary
@@ -30,7 +30,7 @@ export function isReleasable(record) {
30
30
  }
31
31
  /** Terminal Task statuses: a terminal owner keeps no active runtime claim. */
32
32
  export function isTerminalTaskStatus(status) {
33
- return status === "completed" || status === "retired" || status === "archived";
33
+ return status === "completed" || status === "cancelled" || status === "archived";
34
34
  }
35
35
  /**
36
36
  * The immutable release namespace owned by the package installer (Issue 02).