@zq-silk/yui 0.16.1 → 1.0.0-alpha

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 (193) hide show
  1. package/ARCHITECTURE.md +12 -0
  2. package/ARCHITECTURE.zh-CN.md +8 -0
  3. package/README.md +9 -4
  4. package/dist/agent/agent.js +4 -9
  5. package/dist/agent/executionComponents.js +4 -4
  6. package/dist/agent/managedRuntimeEnvironment.js +0 -4
  7. package/dist/agentRun/agentRun.js +13 -23
  8. package/dist/artifacts/managedGit.js +3 -56
  9. package/dist/brief/taskBrief.js +3 -3
  10. package/dist/cli/agentConfigurationPicker.js +24 -21
  11. package/dist/cli/commandCatalog.js +18 -10
  12. package/dist/cli/roleWizard.js +41 -30
  13. package/dist/cli/updateCommand.js +35 -13
  14. package/dist/cli/updateOrchestrator.js +73 -44
  15. package/dist/cli/updatePorts.js +76 -86
  16. package/dist/cli/upgradeCommand.js +4 -8
  17. package/dist/cli.js +39 -33
  18. package/dist/commands/controllerCommands.js +2 -2
  19. package/dist/commands/executionAuditCommands.js +2 -2
  20. package/dist/commands/projectCommands.js +1 -1
  21. package/dist/commands/releaseCommands.js +9 -38
  22. package/dist/commands/taskCommands.js +91 -81
  23. package/dist/commands/taskCompletionGate.js +0 -117
  24. package/dist/commands/taskRoleRuntimeStatus.js +2 -2
  25. package/dist/commands/taskUpstreamCommands.js +93 -54
  26. package/dist/context/runContextPack.js +30 -54
  27. package/dist/context/runInputContract.js +9 -0
  28. package/dist/context/sessionBootstrapManifest.js +11 -19
  29. package/dist/controller/agentHostObservation.js +4 -2
  30. package/dist/controller/agentRuntimeObserver.js +4 -4
  31. package/dist/controller/clientRuntime.js +19 -44
  32. package/dist/controller/controller.js +24 -10
  33. package/dist/controller/fileSchedulerStoreAdapter.js +91 -24
  34. package/dist/controller/globalInputDelivery.js +31 -8
  35. package/dist/controller/globalRuntimeAttention.js +34 -0
  36. package/dist/controller/jobSupervisor.js +3 -3
  37. package/dist/controller/operatorNotification.js +31 -0
  38. package/dist/controller/providerRetryAdmission.js +3 -1
  39. package/dist/controller/providerRetryDelivery.js +136 -119
  40. package/dist/controller/runtime.js +2 -28
  41. package/dist/controller/sessionOwnerReconciliation.js +42 -24
  42. package/dist/controller/structuredProviderObservation.js +28 -1
  43. package/dist/controller/updateReconciliation.js +72 -20
  44. package/dist/coordination/workMailbox.js +4 -4
  45. package/dist/core/controllerIdentity.js +25 -0
  46. package/dist/core/controllerProcessIdentity.js +2 -2
  47. package/dist/core/controllerServer.js +4 -2
  48. package/dist/core/protocol.js +1 -1
  49. package/dist/doctor/doctor.js +2 -1
  50. package/dist/domain/validation.js +6 -0
  51. package/dist/event/taskEvent.js +3 -3
  52. package/dist/execution/workItemExecution.js +2 -2
  53. package/dist/executor/agentAdapter.js +28 -96
  54. package/dist/executor/agentConfigurationCatalog.js +13 -68
  55. package/dist/executor/agentConfigurationFields.js +120 -0
  56. package/dist/executor/agentConfigurationProbe.js +55 -80
  57. package/dist/executor/agentExecutor.js +11 -8
  58. package/dist/executor/effectiveLaunch.js +3 -3
  59. package/dist/executor/executorRegistry.js +0 -9
  60. package/dist/executor/fileRoleLaunchPlanner.js +12 -11
  61. package/dist/grant/capabilityGrant.js +2 -2
  62. package/dist/input/inputRequest.js +4 -4
  63. package/dist/integration/changeSet.js +3 -3
  64. package/dist/integration/integrationAttempt.js +3 -3
  65. package/dist/integration/integrationSourceApplication.js +1 -1
  66. package/dist/interaction/operatorPresentation.js +2 -1
  67. package/dist/job/durableJob.js +1 -1
  68. package/dist/job/jobRunner.js +2 -2
  69. package/dist/message/message.js +8 -6
  70. package/dist/message/messageContinuation.js +8 -9
  71. package/dist/milestone/milestone.js +3 -3
  72. package/dist/observability/executionAudit.js +1 -10
  73. package/dist/observability/runtimeIdentity.js +0 -23
  74. package/dist/output/agentConfigurationPresentation.js +4 -2
  75. package/dist/profile/agentProfile.js +3 -3
  76. package/dist/release/releaseHandover.js +2 -2
  77. package/dist/release/releaseIdempotencyStore.js +0 -23
  78. package/dist/release/releaseWorkflowPorts.js +8 -4
  79. package/dist/release/runtimeRelease.js +9 -7
  80. package/dist/repository/gitWorkspace.js +66 -19
  81. package/dist/repository/project.js +3 -3
  82. package/dist/repository/taskWorkspacePreparer.js +75 -68
  83. package/dist/resources/liveReferences.js +1 -1
  84. package/dist/resources/resourceRegistry.js +90 -44
  85. package/dist/resources/sqliteResourceRegistry.js +5 -7
  86. package/dist/review/reviewRound.js +5 -5
  87. package/dist/role/role.js +7 -8
  88. package/dist/runtime/acpProtocol.js +2 -3
  89. package/dist/runtime/acpSession.js +21 -5
  90. package/dist/runtime/agentDriverObservation.js +1 -1
  91. package/dist/runtime/agentEndpoint.js +4 -1
  92. package/dist/runtime/agentHost.js +25 -65
  93. package/dist/runtime/agentHostCleanup.js +85 -0
  94. package/dist/runtime/agentHostProtocol.js +3 -3
  95. package/dist/runtime/builtinAgentDrivers.js +46 -119
  96. package/dist/runtime/builtinTranscriptObserver.js +10 -6
  97. package/dist/runtime/codexAppServerRuntime.js +55 -89
  98. package/dist/runtime/codexInteractiveHost.js +1 -1
  99. package/dist/runtime/jsonLineChannel.js +35 -7
  100. package/dist/runtime/launchBroker.js +1 -1
  101. package/dist/runtime/processExitObservation.js +1 -1
  102. package/dist/runtime/providerContinuationReconciliationService.js +86 -38
  103. package/dist/runtime/providerRetry.js +15 -0
  104. package/dist/runtime/providerRuntimeIdentity.js +3 -3
  105. package/dist/runtime/providerRuntimeReconciler.js +12 -2
  106. package/dist/runtime/runtimeCoherence.js +7 -3
  107. package/dist/runtime/runtimeObservation.js +3 -3
  108. package/dist/runtime/sessionOwnerIdentity.js +1 -1
  109. package/dist/runtime/structuredProviderHost.js +106 -81
  110. package/dist/runtime/taskRuntimeIsolation.js +3 -3
  111. package/dist/runtime/tmuxAdapters.js +3 -3
  112. package/dist/scheduler/activeRoleRunDelivery.js +5 -1
  113. package/dist/scheduler/operatorInputNotificationProcessor.js +7 -17
  114. package/dist/scheduler/taskWake.js +1 -1
  115. package/dist/storage/baselineSchema.js +606 -0
  116. package/dist/storage/homeLayout.js +5 -16
  117. package/dist/storage/recordValidation.js +16 -4
  118. package/dist/storage/sqliteSchema.js +106 -1741
  119. package/dist/storage/sqliteStore.js +16 -14
  120. package/dist/storage/storageSchema.js +8 -7
  121. package/dist/storage/storageVersions.js +23 -16
  122. package/dist/storage/taskStore.js +3 -29
  123. package/dist/storage/upgrade/upgradeOrchestrator.js +14 -112
  124. package/dist/task/nextAction.js +33 -23
  125. package/dist/task/task.js +17 -8
  126. package/dist/task/taskActivation.js +5 -4
  127. package/dist/telemetry/sqliteTelemetryStore.js +2 -2
  128. package/dist/verification/gateArtifact.js +5 -3
  129. package/dist/verification/verificationPlan.js +6 -7
  130. package/dist/web/assets/assetManifest.js +2 -0
  131. package/dist/web/assets/client/app.js +72 -3
  132. package/dist/web/assets/client/components.js +2 -1
  133. package/dist/web/assets/client/i18n.js +2 -0
  134. package/dist/web/assets/client/taskSummary.js +345 -0
  135. package/dist/web/assets/client/taskSurface.js +44 -26
  136. package/dist/web/assets/client/view.js +33 -0
  137. package/dist/web/assets/shell.js +1 -0
  138. package/dist/web/assets/styles/cards.js +21 -0
  139. package/dist/web/webServer.js +39 -5
  140. package/dist/web/webSessions.js +165 -0
  141. package/dist/web/webSnapshot.js +23 -0
  142. package/dist/web/webTaskSurface.js +23 -0
  143. package/dist/workItem/workItem.js +6 -6
  144. package/dist/workspace/cleanupInspection.js +1 -9
  145. package/dist/worktree/managedWorkspace.js +3 -3
  146. package/docs/managed-turn-and-session-runtime.md +50 -7
  147. package/docs/managed-turn-and-session-runtime.zh-CN.md +36 -5
  148. package/docs/observability/README.md +47 -0
  149. package/docs/observability/README.zh-CN.md +37 -0
  150. package/docs/project-refresh.md +9 -0
  151. package/docs/project-refresh.zh-CN.md +8 -0
  152. package/docs/provider-retry.md +35 -0
  153. package/docs/release-workflow.md +70 -214
  154. package/docs/release-workflow.zh-CN.md +53 -157
  155. package/docs/roles-and-configuration.md +30 -0
  156. package/docs/roles-and-configuration.zh-CN.md +20 -0
  157. package/docs/sqlite-control-plane-design.md +48 -289
  158. package/docs/sqlite-control-plane-design.zh-CN.md +37 -53
  159. package/docs/storage-baseline.md +132 -0
  160. package/docs/storage-baseline.zh-CN.md +106 -0
  161. package/docs/task-delivery.md +110 -2
  162. package/docs/task-delivery.zh-CN.md +82 -2
  163. package/docs/task-discovery.md +9 -0
  164. package/docs/task-discovery.zh-CN.md +6 -0
  165. package/docs/testing/verification-levels.md +42 -164
  166. package/docs/testing/verification-levels.zh-CN.md +29 -115
  167. package/i18n/README.zh-CN.md +7 -4
  168. package/package.json +1 -1
  169. package/skills/yui-leader/SKILL.md +11 -0
  170. package/skills/yui-leader/references/execution.md +22 -0
  171. package/skills/yui-leader/references/planning.md +6 -2
  172. package/skills/yui-operator/SKILL.md +45 -13
  173. package/skills/yui-operator/references/task-delivery.md +99 -0
  174. package/skills/yui-runtime/SKILL.md +5 -3
  175. package/skills/yui-runtime/references/publication.md +56 -5
  176. package/skills/yui-runtime/references/recovery.md +12 -2
  177. package/dist/runtime/agentHostCompatibility.js +0 -127
  178. package/dist/storage/migrations/agentFailureContext.js +0 -22
  179. package/dist/storage/migrations/agentRunContract.js +0 -159
  180. package/dist/storage/migrations/artifactsToGit.js +0 -338
  181. package/dist/storage/migrations/collapseWorktreeLayout.js +0 -963
  182. package/dist/storage/migrations/currentInputContract.js +0 -86
  183. package/dist/storage/migrations/currentRuntimeContract.js +0 -228
  184. package/dist/storage/migrations/historicalVerificationPlan.js +0 -35
  185. package/dist/storage/migrations/integrationContinuation.js +0 -105
  186. package/dist/storage/migrations/narrowAgentFailureContext.js +0 -65
  187. package/dist/storage/migrations/notificationOnlyWakes.js +0 -74
  188. package/dist/storage/migrations/removeRuntimeGeneration.js +0 -207
  189. package/dist/storage/migrations/submitIntent.js +0 -126
  190. package/dist/storage/migrations/unifyHomeLayout.js +0 -925
  191. package/dist/storage/migrations/verificationPlanV1.js +0 -162
  192. package/dist/storage/migrations/verificationPolicy.js +0 -74
  193. package/dist/storage/migrations/workItemHistory.js +0 -46
@@ -25,15 +25,18 @@
25
25
  * mismatch fails closed.
26
26
  */
27
27
  import { spawnSync } from "node:child_process";
28
- import { accessSync, constants, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync } from "node:fs";
28
+ import { accessSync, constants, existsSync, mkdirSync, mkdtempSync, readFileSync, realpathSync, rmSync, statSync } from "node:fs";
29
29
  import { delimiter, dirname, isAbsolute, join, resolve } from "node:path";
30
30
  import { fileURLToPath } from "node:url";
31
31
  import { runtimeError } from "../errors/cliError.js";
32
32
  import { isConcreteVersion } from "../domain/validation.js";
33
+ import { parseControllerIdentity } from "../core/controllerIdentity.js";
34
+ import { isMinorStorageUpgrade, isStorageVersion, storageVersionParts } from "../storage/storageVersions.js";
33
35
  import { STORAGE_DOCTOR_CHECK_NAMES } from "../doctor/doctor.js";
34
36
  import { acquireHandoverLock } from "../release/runtimeRelease.js";
35
37
  import { updateStagingRoot } from "../storage/homeLayout.js";
36
38
  import { resolveYuiHome } from "../storage/taskStore.js";
39
+ import { UpdateControllerReconciliationError } from "../controller/updateReconciliation.js";
37
40
  const PACKAGE_NAME = "@zq-silk/yui";
38
41
  const PACKAGE_SPEC = `${PACKAGE_NAME}@latest`;
39
42
  function resolveExecutable(command, environmentPath) {
@@ -90,20 +93,20 @@ export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
90
93
  beginControllerHandover(home) {
91
94
  return acquireHandoverLock(home).release;
92
95
  },
93
- stage(version) {
96
+ stage(requestedVersion) {
94
97
  // A caller that names a version (the release workflow, which freezes the
95
98
  // exact version in its plan) installs THAT version — never a moving
96
99
  // `latest` that could resolve to a different build than the one the
97
100
  // plan authorized. A non-concrete value fails closed rather than being
98
101
  // interpolated into an install spec. An omitted version keeps the
99
102
  // interactive `yui update` behavior of staging latest.
100
- const spec = version === undefined
103
+ const spec = requestedVersion === undefined
101
104
  ? PACKAGE_SPEC
102
- : isConcreteVersion(version)
103
- ? `${PACKAGE_NAME}@${version.trim()}`
105
+ : isConcreteVersion(requestedVersion)
106
+ ? `${PACKAGE_NAME}@${requestedVersion.trim()}`
104
107
  : null;
105
108
  if (spec === null) {
106
- throw runtimeError(`Refusing to stage a non-concrete version (${String(version)}): only an exact `
109
+ throw runtimeError(`Refusing to stage a non-concrete version (${String(requestedVersion)}): only an exact `
107
110
  + "major.minor.patch version can be pinned for an update.");
108
111
  }
109
112
  // The Home staging parent may not exist yet on a Home that has never
@@ -126,6 +129,10 @@ export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
126
129
  + "nor `yui --json version` returned a concrete version). Refusing to proceed with a "
127
130
  + "`@latest` fallback that could promote a different build than the one preflighted.");
128
131
  }
132
+ if (requestedVersion !== undefined && version !== requestedVersion.trim()) {
133
+ throw runtimeError(`Requested Yui ${requestedVersion.trim()}, but staged Yui ${version}. `
134
+ + "Refusing to activate a different release; the install and storage are unchanged.");
135
+ }
129
136
  // Successful staging transfers cleanup ownership to runUpdate's finally
130
137
  // block. Every assertion, spawn, npm/network, or version-resolution
131
138
  // failure before that handoff removes the throwaway prefix here.
@@ -196,10 +203,9 @@ export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
196
203
  + `but the staged/verified artifact was ${staged.version}. Refusing to trust a `
197
204
  + "different build than the one that passed preflight.");
198
205
  }
199
- // Existing managed Sessions may have been created by an earlier release.
200
- // Retarget those authenticated, Manifest-referenced wrappers to the
201
- // activated control plane before the replacement Controller starts so
202
- // the update cannot strand a live Session.
206
+ // Retarget current Manifest-referenced wrappers to the activated
207
+ // installation before the replacement Controller starts. This relocates
208
+ // the current shell form; it does not convert historical Session wrappers.
203
209
  const sessionCliRefresh = run(activeBinary, ["--json", "internal", "session-cli-refresh"], { cwd: process.cwd(), env: { ...environment, YUI_HOME: home }, shell: false });
204
210
  assertSpawnOk(sessionCliRefresh, "refresh managed Session CLI wrappers");
205
211
  // Retain the exact path used by both doctor and version verification. The
@@ -214,13 +220,15 @@ export function createUpdatePorts(environment, spawn = spawnSync, stagingRoot =
214
220
  },
215
221
  // `runUpdate` is intentionally synchronous because the npm/staged-binary
216
222
  // ports use spawnSync. A short-lived child owns the async, exactly fenced
217
- // reconciliation first; lifecycle capture then uses structured live
223
+ // reconciliation explicitly; lifecycle capture uses only structured live
218
224
  // Controller commands. Tests can replace these seams with deterministic
219
225
  // fakes without touching a real Controller.
220
226
  controllerStatus(home) {
221
- reconcileControllerResourcesForUpdate(home, environment, spawn);
222
227
  return readControllerLifecycle(home, environment, spawn);
223
228
  },
229
+ reconcileController(home) {
230
+ return reconcileControllerResourcesForUpdate(home, environment, spawn);
231
+ },
224
232
  stopController(home, expectedPid) {
225
233
  return stopControllerForUpdate(home, expectedPid, environment, spawn);
226
234
  },
@@ -501,18 +509,16 @@ function restoreControllerIdentity(home, identity, environment, spawn) {
501
509
  "const { spawn } = require('node:child_process');",
502
510
  "const values = process.argv.slice(1);",
503
511
  "const handoverOwnerPid = Number(values.pop());",
504
- "const version = values.pop();",
505
- "const args = JSON.parse(values.pop());",
506
- "const executable = values.pop();",
512
+ "const identity = JSON.parse(values.pop());",
507
513
  "const home = values.pop();",
508
514
  "const runtimeModule = values.pop();",
509
515
  "(async () => {",
510
516
  " const { ensureFileTaskControllerIdentity } = await import(runtimeModule);",
511
- " await ensureFileTaskControllerIdentity(home, { executablePath: executable, args, version }, {",
517
+ " await ensureFileTaskControllerIdentity(home, identity, {",
512
518
  " environment: process.env,",
513
519
  " handoverOwnerPid,",
514
520
  " spawnController: (_home, launchEnv) => {",
515
- " const child = spawn(executable, args, { detached: true, stdio: 'ignore', env: launchEnv });",
521
+ " const child = spawn(identity.executablePath, identity.args, { detached: true, stdio: 'ignore', env: launchEnv });",
516
522
  " child.unref();",
517
523
  " }",
518
524
  " });",
@@ -523,9 +529,7 @@ function restoreControllerIdentity(home, identity, environment, spawn) {
523
529
  helper,
524
530
  UPDATE_CLIENT_RUNTIME_PATH,
525
531
  home,
526
- identity.executablePath,
527
- JSON.stringify(identity.args),
528
- identity.version,
532
+ JSON.stringify(parseControllerIdentity(identity)),
529
533
  String(process.pid)
530
534
  ], { cwd: process.cwd(), env: launchEnvironment, shell: false, stdio: "pipe" });
531
535
  assertSpawnOk(result, "restore the previously running Controller identity");
@@ -593,14 +597,23 @@ function reconcileControllerResourcesForUpdate(home, environment, spawn) {
593
597
  " process.stdout.write(JSON.stringify({ ok: true, data }));",
594
598
  "})().catch((error) => {",
595
599
  " const message = error instanceof Error ? error.message : String(error);",
596
- " process.stderr.write(JSON.stringify({ ok: false, code: 'RUNTIME_ERROR', message }));",
600
+ " process.stderr.write(JSON.stringify({ ok: false, code: 'RUNTIME_ERROR', message, result: error.result }));",
597
601
  " process.exitCode = 5;",
598
602
  "});"
599
603
  ].join(" ");
600
604
  const result = spawn(process.execPath, ["-e", helper, UPDATE_CONTROLLER_RECONCILIATION_PATH, home], { cwd: process.cwd(), env: { ...environment, YUI_HOME: home }, shell: false });
601
605
  if (result.error !== undefined || result.status !== 0) {
602
606
  const detail = structuredErrorMessage(result) ?? result.stderr.toString("utf8").trim();
603
- throw new Error(`Controller reconciliation failed (exit ${result.status ?? "null"})${detail.length === 0 ? "." : `: ${detail}`}`);
607
+ const message = `Controller reconciliation failed (exit ${result.status ?? "null"})${detail.length === 0 ? "." : `: ${detail}`}`;
608
+ let evidence;
609
+ try {
610
+ evidence = JSON.parse(result.stderr.toString("utf8")).result;
611
+ }
612
+ catch { /* Unknown child effects are reported below, never retried here. */ }
613
+ if (isReconciliationResult(evidence, home)) {
614
+ throw new UpdateControllerReconciliationError(message, evidence);
615
+ }
616
+ throw new Error(`${message} Cleanup effects are unknown; inspect the exact Home ${home} before retrying.`);
604
617
  }
605
618
  let parsed;
606
619
  try {
@@ -613,11 +626,19 @@ function reconcileControllerResourcesForUpdate(home, environment, spawn) {
613
626
  }
614
627
  if (!isRecord(parsed)
615
628
  || parsed.ok !== true
616
- || !isRecord(parsed.data)
617
- || !Array.isArray(parsed.data.cleaned)
618
- || parsed.data.cleaned.some((id) => typeof id !== "string")) {
629
+ || !isReconciliationResult(parsed.data, home)) {
619
630
  throw new Error("Controller reconciliation returned an invalid structured result.");
620
631
  }
632
+ return parsed.data;
633
+ }
634
+ function isReconciliationResult(value, home) {
635
+ return isRecord(value) && value.home === resolve(home)
636
+ && Array.isArray(value.cleaned) && value.cleaned.every(id => typeof id === "string")
637
+ && Array.isArray(value.attempts) && value.attempts.every(attempt => (isRecord(attempt) && isRecord(attempt.resource) && typeof attempt.resource.id === "string"
638
+ && ["cleaned", "absent", "unknown"].includes(String(attempt.outcome))
639
+ && (attempt.error === undefined || typeof attempt.error === "string")))
640
+ && Array.isArray(value.remaining) && value.remaining.every(resource => (isRecord(resource) && typeof resource.id === "string"))
641
+ && ["observedAt", "observationError", "lockReleaseError"].every(key => (value[key] === undefined || typeof value[key] === "string"));
621
642
  }
622
643
  function structuredErrorMessage(result) {
623
644
  for (const buffer of [result.stderr, result.stdout]) {
@@ -633,21 +654,6 @@ function structuredErrorMessage(result) {
633
654
  }
634
655
  return undefined;
635
656
  }
636
- function parseControllerIdentity(value) {
637
- if (typeof value.executablePath !== "string"
638
- || value.executablePath.length === 0
639
- || !Array.isArray(value.args)
640
- || value.args.some((arg) => typeof arg !== "string")
641
- || typeof value.version !== "string"
642
- || value.version.length === 0) {
643
- throw new Error("Authenticated Controller identity is malformed; treating ownership as unknown-active.");
644
- }
645
- return {
646
- executablePath: value.executablePath,
647
- args: value.args,
648
- version: value.version
649
- };
650
- }
651
657
  function controllerErrorCodeFromResult(result) {
652
658
  for (const buffer of [result.stdout, result.stderr]) {
653
659
  try {
@@ -752,23 +758,22 @@ function assertActivatedControllerIdentity(identity, activatedBinary, activatedV
752
758
  * entrypoint derivation as the production startup identity check (P1-1, rr23).
753
759
  */
754
760
  export function activatedControllerEntrypoint(activatedBinary) {
755
- let resolvedBinary;
756
- try {
757
- resolvedBinary = realpathSync(activatedBinary);
758
- }
759
- catch {
760
- // `verify` already checked existsSync. Keep the fallback deterministic for
761
- // test seams and fail closed later if the identity does not match it.
762
- resolvedBinary = resolve(activatedBinary);
763
- }
761
+ const resolvedBinary = realpathSync(activatedBinary);
764
762
  const direct = join(dirname(resolvedBinary), "controller", "controllerMain.js");
765
- if (existsSync(direct))
766
- return direct;
767
763
  // npm may expose a non-symlink launcher under <prefix>/bin. Resolve the
768
764
  // package's canonical global layout when it is present.
769
765
  const prefix = resolve(dirname(resolvedBinary), "..");
770
766
  const packageEntrypoint = join(prefix, "lib", "node_modules", PACKAGE_NAME, "dist", "controller", "controllerMain.js");
771
- return existsSync(packageEntrypoint) ? packageEntrypoint : direct;
767
+ for (const candidate of [direct, packageEntrypoint]) {
768
+ if (!existsSync(candidate))
769
+ continue;
770
+ const entrypoint = realpathSync(candidate);
771
+ if (!statSync(entrypoint).isFile()) {
772
+ throw runtimeError(`Controller entrypoint is not a regular file: ${entrypoint}.`);
773
+ }
774
+ return entrypoint;
775
+ }
776
+ throw runtimeError(`Controller entrypoint is missing for the activated binary: ${resolvedBinary}.`);
772
777
  }
773
778
  function interpretPreflight(result) {
774
779
  // Require a valid `{ ok:true, data }` success envelope before trusting any
@@ -787,7 +792,7 @@ function interpretPreflight(result) {
787
792
  }
788
793
  const outcome = typeof data.outcome === "string" ? data.outcome : undefined;
789
794
  // EXIT/OUTCOME CONSISTENCY (P1-2): the one success-class internal preflight
790
- // outcome must exit 0. A user dry-run, legacy direct classification outcome,
795
+ // outcome must exit 0. A user dry-run, a direct classification outcome,
791
796
  // or any other spelling is not this contract and is never promoted to green.
792
797
  if (outcome === "update-preflight" && result.status !== 0) {
793
798
  return {
@@ -818,7 +823,6 @@ function interpretPreflight(result) {
818
823
  action: "Use a staged binary that supports the update-preflight contract; do not force the update."
819
824
  };
820
825
  }
821
- const blockers = parseUpdateBlockers(data.blockers);
822
826
  return {
823
827
  status: "blocked",
824
828
  stage: typeof data.stage === "string" ? data.stage : "preflight",
@@ -826,8 +830,6 @@ function interpretPreflight(result) {
826
830
  action: typeof data.action === "string"
827
831
  ? data.action
828
832
  : "Resolve the reported condition and retry.",
829
- ...(blockers === undefined ? {} : { blockers }),
830
- ...(typeof data.retryCommand === "string" ? { retryCommand: data.retryCommand } : {}),
831
833
  ...(data.sceneUnchanged === true ? { sceneUnchanged: true } : {})
832
834
  };
833
835
  }
@@ -841,9 +843,13 @@ function parseUpdatePreflightResult(data) {
841
843
  const homeClassification = data.classification;
842
844
  if (!isRecord(homeClassification) || !isRecord(homeClassification.classification))
843
845
  return null;
846
+ const from = homeClassification.storageVersion;
847
+ const to = homeClassification.currentStorageVersion;
848
+ if (!isStorageVersion(from) || !isStorageVersion(to))
849
+ return null;
844
850
  const classification = homeClassification.classification;
845
851
  if (data.status === "already-current") {
846
- if (data.stepCount !== 0
852
+ if (data.stepCount !== 0 || from !== to
847
853
  || classification.verdict !== "USABLE"
848
854
  || classification.status !== "current")
849
855
  return null;
@@ -852,8 +858,17 @@ function parseUpdatePreflightResult(data) {
852
858
  if (data.stepCount <= 0
853
859
  || classification.verdict !== "MIGRATABLE"
854
860
  || classification.status !== "migration-ready"
861
+ || !isMinorStorageUpgrade(from, to)
855
862
  || !data.steps.every(isStorageMigrationStep))
856
863
  return null;
864
+ let previous = from;
865
+ for (const step of data.steps) {
866
+ if (step.fromVersion !== previous)
867
+ return null;
868
+ previous = step.toVersion;
869
+ }
870
+ if (previous !== to)
871
+ return null;
857
872
  return { status: "migration-ready", stepCount: data.stepCount };
858
873
  }
859
874
  function interpretStorageMigration(result) {
@@ -889,38 +904,13 @@ function interpretStorageMigration(result) {
889
904
  }
890
905
  function isStorageMigrationStep(value) {
891
906
  return isRecord(value)
892
- && Number.isSafeInteger(value.fromVersion)
893
- && Number.isSafeInteger(value.toVersion)
894
- && value.toVersion === value.fromVersion + 1
907
+ && isStorageVersion(value.fromVersion)
908
+ && isStorageVersion(value.toVersion)
909
+ && isMinorStorageUpgrade(value.fromVersion, value.toVersion)
910
+ && storageVersionParts(value.toVersion).minor === storageVersionParts(value.fromVersion).minor + 1
895
911
  && typeof value.name === "string"
896
912
  && value.name.length > 0;
897
913
  }
898
- function parseUpdateBlockers(value) {
899
- if (value === undefined)
900
- return undefined;
901
- if (!Array.isArray(value))
902
- return undefined;
903
- const parsed = [];
904
- for (const item of value) {
905
- if (!isRecord(item) || typeof item.reason !== "string" || item.reason.length === 0) {
906
- return undefined;
907
- }
908
- const optional = ["taskId", "roleName", "runId", "nativeSessionId"];
909
- if (optional.some((key) => item[key] !== undefined && typeof item[key] !== "string")) {
910
- return undefined;
911
- }
912
- parsed.push({
913
- ...(typeof item.taskId === "string" ? { taskId: item.taskId } : {}),
914
- ...(typeof item.roleName === "string" ? { roleName: item.roleName } : {}),
915
- ...(typeof item.runId === "string" ? { runId: item.runId } : {}),
916
- ...(typeof item.nativeSessionId === "string"
917
- ? { nativeSessionId: item.nativeSessionId }
918
- : {}),
919
- reason: item.reason
920
- });
921
- }
922
- return parsed;
923
- }
924
914
  /**
925
915
  * Extract the validated `data` object from a spawn result's `{ ok: true, data }`
926
916
  * JSON envelope, or `null` when the result is not a trustworthy success envelope.
@@ -1,6 +1,7 @@
1
1
  /** `yui upgrade` plans or applies the supported linear storage migration chain. */
2
2
  import { runStorageUpgrade } from "../storage/upgrade/upgradeOrchestrator.js";
3
3
  import { ensureFileTaskController, ensureFileTaskControllerIdentity, stopFileTaskController } from "../controller/clientRuntime.js";
4
+ import { parseControllerIdentity } from "../core/controllerIdentity.js";
4
5
  import { callController, ControllerClientError } from "../core/controllerClient.js";
5
6
  import { spawn } from "node:child_process";
6
7
  import { runtimeError, usageError } from "../errors/cliError.js";
@@ -99,16 +100,11 @@ async function captureUpgradeController(home) {
99
100
  return undefined;
100
101
  throw error;
101
102
  }
102
- const identity = value;
103
- if (!Number.isSafeInteger(identity.pid) || identity.pid < 1
104
- || typeof identity.executablePath !== "string" || !identity.executablePath
105
- || !Array.isArray(identity.args) || identity.args.some(arg => typeof arg !== "string")
106
- || typeof identity.version !== "string" || !identity.version) {
103
+ const pid = value?.pid;
104
+ if (!Number.isSafeInteger(pid) || pid < 1) {
107
105
  throw runtimeError("Cannot capture the exact Controller for a reversible upgrade preflight.");
108
106
  }
109
- return { pid: identity.pid, identity: {
110
- executablePath: identity.executablePath, args: identity.args, version: identity.version
111
- } };
107
+ return { pid: pid, identity: parseControllerIdentity(value) };
112
108
  }
113
109
  async function runUpdateOwnedUpgrade(home, environment) {
114
110
  const ownerText = environment.YUI_UPDATE_HANDOVER_OWNER_PID;
package/dist/cli.js CHANGED
@@ -45,7 +45,7 @@ import { assertTaskDeliveryAuthority, taskLocalActor } from "./task/taskAuthorit
45
45
  import { runTaskBaseStatusCommand } from "./commands/taskBaseCommands.js";
46
46
  import { runTaskChangeSetCommand } from "./commands/taskChangeSetCommands.js";
47
47
  import { assertWorkItemDependenciesCompletedForCommand, dispatchPreparedReviewRound, failPendingReviewRound, parseTaskArchiveArguments, parseTaskCompletionRequest, planReplicatedWorkItemLanes, preflightTaskCompletion, preserveReviewRoundWorkspace, previewTaskRoleAgentConfigurationMutation, requireWorkItemAssignee, runTaskCommand, validateTaskArchiveRequest } from "./commands/taskCommands.js";
48
- import { reconcileTaskRemoteBaselines, verifyTaskCompletionPublishedTree } from "./commands/taskCompletionGate.js";
48
+ import { verifyTaskCompletionPublishedTree } from "./commands/taskCompletionGate.js";
49
49
  import { finalizeStoppedTaskExecution, parseTaskExecutionStartRequest, parseTaskExecutionStopRequest, startTaskExecutionCommand, stopTaskExecutionCommand } from "./commands/taskExecutionCommands.js";
50
50
  import { runTaskIntegrationCommand } from "./commands/taskIntegrationCommands.js";
51
51
  import { runTaskOverlapCommand } from "./commands/taskOverlapCommands.js";
@@ -100,7 +100,7 @@ import { requireManagedTaskCaller, resolveManagedTaskReader } from "./runtime/ma
100
100
  import { assertRuntimeCoherence } from "./runtime/runtimeCoherence.js";
101
101
  import { runSetupCommand, validateSetupInvocation } from "./setup/setupCommand.js";
102
102
  import { openCurrentTaskStore, validateCurrentTaskStore } from "./storage/currentTaskStore.js";
103
- import { SqliteSchemaMigrationError } from "./storage/sqliteSchema.js";
103
+ import { SqliteSchemaError } from "./storage/sqliteSchema.js";
104
104
  import { inspectStorageSchema } from "./storage/storageSchema.js";
105
105
  import { resolveYuiHome } from "./storage/taskStore.js";
106
106
  import { renderArchiveDiagnostics, taskArchiveDiagnostics } from "./task/archiveDiagnostics.js";
@@ -141,7 +141,7 @@ void main().catch((error) => {
141
141
  });
142
142
  function runtimeFailureMessage(error) {
143
143
  const message = error instanceof Error ? error.message : String(error);
144
- if (!(error instanceof SqliteSchemaMigrationError))
144
+ if (!(error instanceof SqliteSchemaError))
145
145
  return message;
146
146
  try {
147
147
  return `${message}\n${describeCliHomeInvocation({
@@ -225,9 +225,7 @@ export async function main() {
225
225
  if (args[0] === "update") {
226
226
  if (jsonOutput)
227
227
  throw usageError("Update does not support --json.");
228
- if (args.length !== 1)
229
- throw usageError("Update usage: yui update");
230
- process.exitCode = runUpdateCommand();
228
+ process.exitCode = runUpdateCommand(args.slice(1));
231
229
  return;
232
230
  }
233
231
  if (args[0] === "release") {
@@ -544,12 +542,9 @@ export async function main() {
544
542
  validateCurrentTaskStore(home);
545
543
  const controllerMethod = method;
546
544
  const updateHandoverOwner = process.env.YUI_UPDATE_HANDOVER_OWNER_PID;
547
- // A pre-fix updater cannot pass the owner environment variable to the
548
- // activated restart child, but it remains that child's direct parent while
549
- // holding the exact handover lock. Inherit only that OS-backed relationship;
550
- // every unrelated live lock still compares foreign and fails closed.
545
+ // Maintenance children must explicitly name their exact lock owner.
551
546
  const updateHandoverOwnerPid = updateHandoverOwner === undefined
552
- ? (Number.isSafeInteger(process.ppid) && process.ppid > 0 ? process.ppid : undefined)
547
+ ? undefined
553
548
  : Number(updateHandoverOwner);
554
549
  if (updateHandoverOwnerPid !== undefined
555
550
  && (!Number.isSafeInteger(updateHandoverOwnerPid) || updateHandoverOwnerPid < 1)) {
@@ -1384,6 +1379,8 @@ export async function main() {
1384
1379
  }
1385
1380
  let completionSummary;
1386
1381
  let completionPublishedTreeProof;
1382
+ let completionBaseFreshness;
1383
+ let completionWarnings = [];
1387
1384
  if (resolved[1] === "base" && resolved[2] === "status") {
1388
1385
  const result = await runTaskBaseStatusCommand(resolved.slice(3), store);
1389
1386
  emit(result.output, false, result.data);
@@ -1400,8 +1397,8 @@ export async function main() {
1400
1397
  if (resolved[1] === "complete" && resolved[2] !== undefined) {
1401
1398
  const completionRequest = parseTaskCompletionRequest(resolved.slice(2));
1402
1399
  completionSummary = completionRequest.summary;
1403
- const refreshRemote = resolved.includes("--refresh-remote");
1404
- const completion = preflightTaskCompletion(resolved[2], store, {
1400
+ const refreshRemote = completionRequest.refreshRemote;
1401
+ const completion = preflightTaskCompletion(completionRequest.taskId, store, {
1405
1402
  environment: process.env,
1406
1403
  ...(taskFinalReviewContract === undefined
1407
1404
  ? {}
@@ -1412,34 +1409,25 @@ export async function main() {
1412
1409
  // Publication proof resolves its exact commit. Without the flag the
1413
1410
  // command remains offline and preserves the existing proof-first path.
1414
1411
  const refreshedFreshness = refreshRemote
1415
- ? await inspectTaskBaseFreshness(resolved[2], store, { refresh: true })
1412
+ ? await inspectTaskBaseFreshness(completionRequest.taskId, store, { refresh: true })
1416
1413
  : undefined;
1417
1414
  if (completionRequest.acceptedPublishedTreePublicationId !== undefined) {
1418
1415
  completionPublishedTreeProof = await verifyTaskCompletionPublishedTree(completionRequest.taskId, completionRequest.acceptedPublishedTreePublicationId, store);
1419
1416
  }
1420
- const freshness = refreshedFreshness
1421
- ?? await inspectTaskBaseFreshness(resolved[2], store);
1422
- for (const warning of assertTaskBaseFreshnessForCompletion(freshness, {
1417
+ completionBaseFreshness = refreshedFreshness
1418
+ ?? await inspectTaskBaseFreshness(completionRequest.taskId, store);
1419
+ completionWarnings = assertTaskBaseFreshnessForCompletion(completionBaseFreshness, {
1423
1420
  ...(completionPublishedTreeProof === undefined
1424
1421
  ? {}
1425
1422
  : {
1426
1423
  acceptedPublishedTreeProjectId: completionPublishedTreeProof.projectId
1427
1424
  })
1428
- })) {
1425
+ });
1426
+ for (const warning of completionWarnings) {
1429
1427
  process.stderr.write(`Warning: ${warning}\n`);
1430
1428
  }
1431
- // Keep completion offline by default. An explicit refresh is the only
1432
- // path that may fetch and reconcile a moved remote baseline.
1433
- if (refreshRemote) {
1434
- const reconciled = await reconcileTaskRemoteBaselines(resolved[2], store, home, { environment: process.env, jobPort: createControllerIntegrationJobPort(home, { environment: process.env }) });
1435
- if (reconciled.length > 0) {
1436
- const updates = reconciled.map((entry) => (`${entry.projectId}: ${entry.fromCommit} -> ${entry.toCommit} `
1437
- + `(Integration ${entry.integrationId})`)).join("; ");
1438
- throw usageError(`Remote baseline reconciliation advanced Task ${resolved[2]} (${updates}). `
1439
- + "The Task remains active so the Leader can inspect the new authoritative head, "
1440
- + "decide how prior Review evidence applies, and retry task complete.");
1441
- }
1442
- }
1429
+ // Refresh observes remote objects only. Choosing and executing an
1430
+ // upstream Integration belongs to its explicit command, never completion.
1443
1431
  }
1444
1432
  }
1445
1433
  let releaseReviewHandoverLock;
@@ -1628,9 +1616,24 @@ export async function main() {
1628
1616
  emitControlFailure(result.output, failureCode, result.data);
1629
1617
  return;
1630
1618
  }
1631
- emit(`${result.output}${reviewOutput}`, false, reviewData === undefined
1619
+ const commandData = reviewData === undefined
1632
1620
  ? result.data
1633
- : { command: result.data, ...reviewData });
1621
+ : { command: result.data, ...reviewData };
1622
+ const completionData = resolved[1] === "complete" ? {
1623
+ // Review dispatch may have advanced or failed after the command
1624
+ // prepared its Round. Report that observed stage, not preparation.
1625
+ ...(reviewData === undefined ? {} : {
1626
+ stage: reviewRoundFromCommandData(reviewData)?.status === "running"
1627
+ ? "review-running"
1628
+ : "review-blocked",
1629
+ projectHeads: result.data.projectHeads
1630
+ }),
1631
+ baseFreshness: completionBaseFreshness ?? null,
1632
+ warnings: completionWarnings
1633
+ } : undefined;
1634
+ emit(`${result.output}${reviewOutput}`, false, completionData === undefined
1635
+ ? commandData
1636
+ : { ...commandData, ...completionData });
1634
1637
  return;
1635
1638
  }
1636
1639
  if (jsonOutput && result.kind !== "session-stop"
@@ -2431,7 +2434,10 @@ async function executeOperatorSessionControl(control, home, store, runtime, tmux
2431
2434
  await ensureFileTaskController(home, { environment: process.env });
2432
2435
  if (paneRunning
2433
2436
  || (active !== undefined
2434
- && active.status === "active")) {
2437
+ && active.status === "active")
2438
+ || (sessionSet?.providerBinding?.run != null
2439
+ && ["submitting", "accepted", "delivery-unknown"].includes(sessionSet.providerBinding.run.status))
2440
+ || (sessionSet?.providerBinding?.goal != null && sessionSet.providerBinding.goal.status !== "complete")) {
2435
2441
  await runtime.stopGlobalRoleSession(role.name);
2436
2442
  }
2437
2443
  applyOperatorSessionControl(control, store);
@@ -49,7 +49,7 @@ export function renderControllerResourceStatus(snapshot, verbose, width = defaul
49
49
  { header: "Reason", minWidth: 12, maxWidth: 30 }
50
50
  ], visibleDomains.map((domain) => [
51
51
  domain.yuiHome,
52
- domain.domainKind ?? "legacy",
52
+ domain.domainKind ?? "unmarked",
53
53
  domain.liveness === undefined
54
54
  ? "—"
55
55
  : `${domain.liveness}/${domain.disposition ?? "review"}`,
@@ -202,7 +202,7 @@ export function renderRuntimeIdentitySection(input) {
202
202
  ` Source commit ${build.sourceCommit}`,
203
203
  ` Node ${build.nodeVersion} (${build.platform})`,
204
204
  ` Storage version ${storage.storageVersion} (status ${storage.storageStatus}, minimum ${storage.minimumStorageVersion}) · backend ${storage.configuredBackend} · worker ${storage.workerEnabled ? "on" : "off"}`,
205
- ` Store files state.json ${storage.physicalStateJson.present ? "present" : "absent"} · yui.db ${storage.physicalDatabase.present ? "present" : "absent"}${storage.physicalDatabase.wal ? " +WAL" : ""}${storage.physicalDatabase.present && storage.physicalDatabase.health !== UNSUPPORTED ? ` (${storage.physicalDatabase.health})` : ""}`
205
+ ` Store files yui.db ${storage.physicalDatabase.present ? "present" : "absent"}${storage.physicalDatabase.wal ? " +WAL" : ""}${storage.physicalDatabase.present && storage.physicalDatabase.health !== UNSUPPORTED ? ` (${storage.physicalDatabase.health})` : ""}`
206
206
  ];
207
207
  for (const finding of storage.findings) {
208
208
  lines.push(` ! ${finding.severity === "contradiction" ? "CONTRADICTION" : finding.severity === "needs-repair" ? "NEEDS-REPAIR" : "warning"} ${finding.code}: ${finding.message}`, ` remediation: ${finding.remediation}`);
@@ -258,8 +258,8 @@ export function renderExecutionAudit(report, width = defaultTableWidth()) {
258
258
  }
259
259
  if (report.storage.status === "ok" && report.storage.data !== undefined) {
260
260
  const storage = report.storage.data;
261
- lines.push("", `Storage: backend ${storage.backend} · state.json ${formatBytes(storage.stateJsonBytes)} · yui.db ${formatBytes(storage.databaseBytes)}`
262
- + ` · runtime/ ${formatBytes(storage.runtimeDirBytes)} · deployments ${formatBytes(storage.deploymentsBytes)}`);
261
+ lines.push("", `Storage: backend ${storage.backend} · yui.db ${formatBytes(storage.databaseBytes)}`
262
+ + ` · runtime/ ${formatBytes(storage.runtimeDirBytes)}`);
263
263
  }
264
264
  else {
265
265
  lines.push("", ...sectionError("storage", report));
@@ -238,7 +238,7 @@ async function cloneProject(args, store, options) {
238
238
  // A remote-URL binding is Home-managed by default: its canonical repository
239
239
  // lives below the persistent Home, so the runtime never depends on a
240
240
  // user-controlled checkout path. --external is an explicit opt-in to the
241
- // legacy clone-inside-the-workspace mode.
241
+ // user-owned clone-inside-the-workspace mode.
242
242
  let destination;
243
243
  let ownership;
244
244
  if (parsed.external) {
@@ -341,55 +341,26 @@ export function createReleaseActivatePorts(overrides = {}) {
341
341
  }),
342
342
  runPreflight: overrides.runPreflight ?? ((releaseDir, home) => {
343
343
  const cli = join(releaseDir, "dist", "cli.js");
344
- const result = spawnSync(process.execPath, [cli, "doctor", "--json"], {
344
+ // The target owns current-record and database integrity validation.
345
+ // Activation never migrates; a valid but older Home still blocks here.
346
+ const result = spawnSync(process.execPath, [cli, "--json", "upgrade", "--update-preflight"], {
345
347
  env: { ...process.env, YUI_HOME: home, NO_COLOR: "1" },
346
348
  encoding: "utf8",
347
349
  timeout: 60_000
348
350
  });
349
351
  let report;
350
352
  try {
351
- const envelope = JSON.parse(result.stdout);
352
- report = envelope.data;
353
+ report = JSON.parse(result.stdout);
353
354
  }
354
355
  catch (error) {
355
356
  throw new Error(`Release preflight produced no JSON (exit ${result.status}): `
356
357
  + `${messageOf(error)} ${result.stderr.trim()}`);
357
358
  }
358
- // The handover preflight must prove the exact current Home contract.
359
- // Older, newer, malformed, or incomplete Homes all fail closed and are
360
- // never normalized by release activation.
361
- const checks = report?.checks;
362
- if (!Array.isArray(checks)) {
363
- throw new Error("Release preflight report has no storage checks.");
364
- }
365
- const required = ["storage compatibility"];
366
- const failed = required.filter((name) => {
367
- const check = checks.find((candidate) => candidate !== null
368
- && typeof candidate === "object"
369
- && candidate.name === name);
370
- return check === undefined
371
- || check.status !== "ok";
372
- });
373
- if (failed.length > 0) {
374
- throw new Error(`Release preflight storage compatibility checks failed: ${failed.join(", ")}.`);
375
- }
376
- // Doctor proves the target's storage, not the pinned code in existing
377
- // Hosts. Ask the target's upgrade boundary for its independent Host
378
- // protocol proof as well; release activation itself still never migrates.
379
- const hostCheck = spawnSync(process.execPath, [cli, "--json", "upgrade", "--update-preflight"], {
380
- env: { ...process.env, YUI_HOME: home, NO_COLOR: "1" },
381
- encoding: "utf8", timeout: 60_000
382
- });
383
- let preflight;
384
- try {
385
- preflight = JSON.parse(hostCheck.stdout).data;
386
- }
387
- catch {
388
- throw new Error(`Release Host preflight produced no JSON: ${hostCheck.stderr.trim()}`);
389
- }
390
- if (hostCheck.status !== 0 || preflight?.outcome !== "update-preflight"
391
- || preflight.status !== "already-current") {
392
- throw new Error(`Release Host compatibility preflight failed: ${preflight?.message ?? preflight?.status ?? "unknown"}. ${preflight?.action ?? ""}`);
359
+ if (result.status !== 0 || report?.ok !== true
360
+ || report.data?.outcome !== "update-preflight"
361
+ || report.data.status !== "already-current") {
362
+ throw new Error(`Release storage preflight failed: ${report?.data?.message ?? report?.data?.status ?? "invalid response"}. `
363
+ + `${report?.data?.action ?? result.stderr.trim()}`);
393
364
  }
394
365
  }),
395
366
  killOwnedProcess: overrides.killOwnedProcess ?? ((owner) => {