maka-agent 0.2.0-dev.22.20260905 → 0.2.0-dev.24.20260907

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 (194) hide show
  1. package/dist/acp/maka-acp-agent.js +2 -1
  2. package/dist/acp/session-configuration.js +143 -0
  3. package/dist/acp/session-registry.js +90 -4
  4. package/dist/cli-core.js +2 -0
  5. package/dist/pi-transcript.js +5 -12
  6. package/dist/pi-tui-runner.js +74 -4
  7. package/dist/runtime-host-cli-context.js +167 -78
  8. package/dist/runtime-host-cli-installation.js +8 -2
  9. package/dist/runtime-host-cli.js +31 -1
  10. package/dist/runtime-host-handoff-surface.js +81 -0
  11. package/dist/runtime-host-installed-update-bootstrap.js +13 -0
  12. package/dist/runtime-host-installed-update-coordinator.js +33 -0
  13. package/dist/runtime-host-lifecycle-transaction.js +9 -1
  14. package/dist/runtime-host-local-handoff.js +37 -4
  15. package/dist/runtime-host-local-managed-activation.js +84 -0
  16. package/dist/runtime-host-local-source-retirement.js +18 -2
  17. package/dist/runtime-host-managed-lifecycle-manager.js +1 -0
  18. package/dist/runtime-host-operator-retirement.js +50 -0
  19. package/dist/runtime-host-service-manager.js +3 -0
  20. package/dist/runtime-host-session-driver.js +4 -25
  21. package/dist/runtime-host-session-update.js +62 -0
  22. package/dist/runtime-host-tui-command.js +38 -73
  23. package/dist/runtime-host-tui-context.js +1 -0
  24. package/dist/runtime-host-tui-owner.js +226 -0
  25. package/dist/tui-copy-catalog.js +140 -3
  26. package/dist/tui-host-owner-copy.js +24 -0
  27. package/native/runtime-host-peer/prebuilds/darwin-arm64/maka_runtime_host_peer.node +0 -0
  28. package/native/runtime-host-peer/prebuilds/linux-arm64/maka_runtime_host_peer.node +0 -0
  29. package/native/runtime-host-peer/prebuilds/linux-x64/maka_runtime_host_peer.node +0 -0
  30. package/native/runtime-host-peer/prebuilds/win32-x64/maka_runtime_host_peer.node +0 -0
  31. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  32. package/node_modules/@maka/core/dist/bot-events.js +18 -63
  33. package/node_modules/@maka/core/dist/canonical-runtime-event.js +3 -1
  34. package/node_modules/@maka/core/dist/model-call-attempt.js +0 -82
  35. package/node_modules/@maka/core/dist/model-call-usage-projection.js +0 -186
  36. package/node_modules/@maka/core/dist/model-failure.js +40 -0
  37. package/node_modules/@maka/core/dist/model-metadata.generated.js +4 -4
  38. package/node_modules/@maka/core/dist/model-metadata.js +3 -3
  39. package/node_modules/@maka/core/dist/model-web-search.js +5 -4
  40. package/node_modules/@maka/core/dist/provider-registry.js +45 -4
  41. package/node_modules/@maka/core/dist/record-schema.js +22 -2
  42. package/node_modules/@maka/core/dist/redaction.js +41 -58
  43. package/node_modules/@maka/core/dist/runtime-boundary.js +31 -11
  44. package/node_modules/@maka/core/dist/runtime-event.js +37 -6
  45. package/node_modules/@maka/core/dist/runtime-handoff.js +98 -0
  46. package/node_modules/@maka/core/dist/runtime-invocation.js +1 -1
  47. package/node_modules/@maka/core/dist/runtime-logical-execution.js +112 -0
  48. package/node_modules/@maka/core/dist/session-trace.js +5 -1
  49. package/node_modules/@maka/core/dist/session.js +54 -52
  50. package/node_modules/@maka/core/dist/slash-command-catalog.js +2 -0
  51. package/node_modules/@maka/core/dist/tool-result-archive-evidence.js +20 -0
  52. package/node_modules/@maka/core/dist/usage-ledger-merge.js +7 -9
  53. package/node_modules/@maka/core/dist/usage-record-schema.js +26 -35
  54. package/node_modules/@maka/core/dist/workhub-creation-intent.js +34 -20
  55. package/node_modules/@maka/core/package.json +4 -0
  56. package/node_modules/@maka/eval/harbor/egress-proxy/entrypoint.sh +2 -1
  57. package/node_modules/@maka/mcp/dist/index.js +131 -1
  58. package/node_modules/@maka/runtime/dist/agent-run.js +272 -105
  59. package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +13 -5
  60. package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +109 -34
  61. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +155 -134
  62. package/node_modules/@maka/runtime/dist/bots/base-adapter.js +17 -0
  63. package/node_modules/@maka/runtime/dist/bots/bot-test.js +38 -22
  64. package/node_modules/@maka/runtime/dist/bots/dingtalk-bridge.js +7 -5
  65. package/node_modules/@maka/runtime/dist/bots/discord-bridge.js +4 -4
  66. package/node_modules/@maka/runtime/dist/bots/feishu-bridge.js +4 -5
  67. package/node_modules/@maka/runtime/dist/bots/qq-bridge.js +6 -4
  68. package/node_modules/@maka/runtime/dist/bots/slack-bridge.js +3 -4
  69. package/node_modules/@maka/runtime/dist/bots/telegram-bridge.js +4 -5
  70. package/node_modules/@maka/runtime/dist/bots/wechat-bridge.js +17 -19
  71. package/node_modules/@maka/runtime/dist/bots/wecom-bridge.js +5 -6
  72. package/node_modules/@maka/runtime/dist/bots/ws-bridge-base.js +2 -3
  73. package/node_modules/@maka/runtime/dist/context-budget.js +14 -9
  74. package/node_modules/@maka/runtime/dist/conversation-copy.js +5 -4
  75. package/node_modules/@maka/runtime/dist/goal-continuation.js +36 -4
  76. package/node_modules/@maka/runtime/dist/history-compact-checkpoint.js +18 -9
  77. package/node_modules/@maka/runtime/dist/history-compact-summarizer.js +30 -22
  78. package/node_modules/@maka/runtime/dist/history-compaction.js +29 -4
  79. package/node_modules/@maka/runtime/dist/ledger-tool-result-archive-reader.js +106 -0
  80. package/node_modules/@maka/runtime/dist/message-authority.js +9 -0
  81. package/node_modules/@maka/runtime/dist/model-adapter.js +19 -98
  82. package/node_modules/@maka/runtime/dist/model-factory.js +19 -81
  83. package/node_modules/@maka/runtime/dist/model-fetcher.js +7 -0
  84. package/node_modules/@maka/runtime/dist/model-runtime.js +85 -111
  85. package/node_modules/@maka/runtime/dist/native-web-search-tool.js +2 -1
  86. package/node_modules/@maka/runtime/dist/openai-codex-history-compactor.js +2 -2
  87. package/node_modules/@maka/runtime/dist/opencode-session-header.js +4 -2
  88. package/node_modules/@maka/runtime/dist/provider-error-classification.js +84 -123
  89. package/node_modules/@maka/runtime/dist/provider-request-telemetry.js +68 -108
  90. package/node_modules/@maka/runtime/dist/run-handoff-gate.js +97 -0
  91. package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +82 -54
  92. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +117 -159
  93. package/node_modules/@maka/runtime/dist/runtime-kernel.js +150 -112
  94. package/node_modules/@maka/runtime/dist/runtime-ledger-repair.js +160 -79
  95. package/node_modules/@maka/runtime/dist/runtime-read-model.js +30 -89
  96. package/node_modules/@maka/runtime/dist/runtime-resume.js +80 -4
  97. package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +5 -0
  98. package/node_modules/@maka/runtime/dist/session-manager.js +171 -288
  99. package/node_modules/@maka/runtime/dist/session-projection-helpers.js +0 -27
  100. package/node_modules/@maka/runtime/dist/session-trace-projection.js +3 -30
  101. package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +3 -4
  102. package/node_modules/@maka/runtime/dist/test-connection.js +16 -16
  103. package/node_modules/@maka/runtime/dist/tool-result-archive-encoding.js +38 -0
  104. package/node_modules/@maka/runtime/dist/tool-runtime.js +3 -61
  105. package/node_modules/@maka/runtime/package.json +1 -0
  106. package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +35 -0
  107. package/node_modules/@maka/runtime-host/dist/candidate-entry.js +4 -1
  108. package/node_modules/@maka/runtime-host/dist/client/connect-or-spawn.js +9 -1
  109. package/node_modules/@maka/runtime-host/dist/client/connection.js +3 -0
  110. package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +156 -0
  111. package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +273 -0
  112. package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +26 -5
  113. package/node_modules/@maka/runtime-host/dist/client/index.js +3 -1
  114. package/node_modules/@maka/runtime-host/dist/client/launcher.js +3 -0
  115. package/node_modules/@maka/runtime-host/dist/client/peer-client.js +13 -0
  116. package/node_modules/@maka/runtime-host/dist/client/registered-host-termination.js +0 -59
  117. package/node_modules/@maka/runtime-host/dist/client/remote-compatibility-error.js +2 -0
  118. package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +27 -5
  119. package/node_modules/@maka/runtime-host/dist/operator/index.js +2 -1
  120. package/node_modules/@maka/runtime-host/dist/operator/retirement-cancellation.js +20 -0
  121. package/node_modules/@maka/runtime-host/dist/peer-mesh/node.js +28 -16
  122. package/node_modules/@maka/runtime-host/dist/protocol/external-session.js +4 -0
  123. package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +29 -12
  124. package/node_modules/@maka/runtime-host/dist/protocol/index.js +37 -1
  125. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +2 -0
  126. package/node_modules/@maka/runtime-host/dist/protocol/session-collaboration.js +28 -1
  127. package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +40 -20
  128. package/node_modules/@maka/runtime-host/dist/protocol/session-transcript.js +0 -5
  129. package/node_modules/@maka/runtime-host/dist/protocol/session-turns.js +29 -58
  130. package/node_modules/@maka/runtime-host/dist/protocol/turn.js +11 -1
  131. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +33 -0
  132. package/node_modules/@maka/runtime-host/dist/server/access-authority.js +26 -1
  133. package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +6 -1
  134. package/node_modules/@maka/runtime-host/dist/server/canonical-turn-snapshot.js +19 -4
  135. package/node_modules/@maka/runtime-host/dist/server/canonical-usage-reader.js +24 -11
  136. package/node_modules/@maka/runtime-host/dist/server/connection-session.js +7 -6
  137. package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +7 -4
  138. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +168 -28
  139. package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +5 -5
  140. package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +3 -2
  141. package/node_modules/@maka/runtime-host/dist/server/external-session-coordinator.js +6 -3
  142. package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +40 -1
  143. package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +108 -23
  144. package/node_modules/@maka/runtime-host/dist/server/host-residency-registry.js +13 -7
  145. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-admission-registry.js +3 -1
  146. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +124 -62
  147. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +85 -8
  148. package/node_modules/@maka/runtime-host/dist/server/root-admission-owner.js +7 -0
  149. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +344 -71
  150. package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +37 -2
  151. package/node_modules/@maka/runtime-host/dist/server/serial-outbound-writer.js +45 -3
  152. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +201 -12
  153. package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +87 -25
  154. package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +8 -55
  155. package/node_modules/@maka/runtime-host/dist/server/session-sidecar-purge.js +1 -16
  156. package/node_modules/@maka/runtime-host/dist/server/session-transcript-pager.js +0 -1
  157. package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +460 -70
  158. package/node_modules/@maka/runtime-host/dist/server/shared-session-transcript.js +1 -1
  159. package/node_modules/@maka/runtime-host/dist/server/storage-maintenance.js +109 -0
  160. package/node_modules/@maka/runtime-host/dist/server/usage-pricing-coordinator.js +4 -11
  161. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +44 -15
  162. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +6 -2
  163. package/node_modules/@maka/storage/dist/agent-run-store.js +44 -0
  164. package/node_modules/@maka/storage/dist/artifact-store.js +21 -10
  165. package/node_modules/@maka/storage/dist/artifact-stores.js +1 -1
  166. package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +0 -4
  167. package/node_modules/@maka/storage/dist/codex-session-adapter.js +0 -2
  168. package/node_modules/@maka/storage/dist/context-offload-snapshot.js +325 -0
  169. package/node_modules/@maka/storage/dist/conversation-operational-state.js +4 -0
  170. package/node_modules/@maka/storage/dist/execution-stores.js +11 -7
  171. package/node_modules/@maka/storage/dist/file-session-repository.js +1012 -0
  172. package/node_modules/@maka/storage/dist/model-call-ledger.js +199 -32
  173. package/node_modules/@maka/storage/dist/model-call-usage-sql.js +136 -0
  174. package/node_modules/@maka/storage/dist/opencode-session-adapter.js +0 -4
  175. package/node_modules/@maka/storage/dist/operational-state-backup.js +9 -5
  176. package/node_modules/@maka/storage/dist/operational-state-store-public.js +1 -1
  177. package/node_modules/@maka/storage/dist/operational-state-store.js +5 -3
  178. package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
  179. package/node_modules/@maka/storage/dist/runtime-transcript-query.js +308 -0
  180. package/node_modules/@maka/storage/dist/session-bundle-policy.js +24 -4
  181. package/node_modules/@maka/storage/dist/session-message-projection.js +15 -0
  182. package/node_modules/@maka/storage/dist/session-repository.js +767 -0
  183. package/node_modules/@maka/storage/dist/session-store.js +25 -99
  184. package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +10 -3
  185. package/node_modules/@maka/storage/dist/sqlite-context-offload-store.js +24 -8
  186. package/node_modules/@maka/storage/dist/sqlite-core-execution-schema.js +17 -1
  187. package/node_modules/@maka/storage/dist/sqlite-runtime-schema.js +44 -1
  188. package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +129 -25
  189. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +109 -892
  190. package/node_modules/@maka/storage/dist/sqlite-usage-schema.js +200 -20
  191. package/node_modules/@maka/storage/dist/tool-result-archive-evidence.js +111 -0
  192. package/node_modules/@maka/storage/dist/usage-stores.js +6 -2
  193. package/node_modules/@maka/storage/package.json +2 -0
  194. package/package.json +1 -1
@@ -16,13 +16,16 @@
16
16
  * specific language governing permissions and limitations
17
17
  * under the License.
18
18
  */
19
+ import { activateLocalManagedRuntimeHost } from './runtime-host-local-managed-activation.js';
19
20
  import { randomUUID } from 'node:crypto';
20
21
  import { join } from 'node:path';
21
22
  import { NO_REAL_CONNECTION_CODE } from '@maka/core/connection-error-copy';
22
- import { connectOrSpawnRuntimeHost, connectRuntimeHostProfile, createClientRuntimeHostProfileCatalog, createRuntimeHostPeerClientFromEnvironment, createRuntimeHostReconnectingConnection, loadOrCreateRuntimeHostClientInstanceId, LOCAL_RUNTIME_HOST_PROFILE, readRuntimeHostConnectionCatalog, RuntimeHostPermanentReconnectError, runtimeHostStartupError, } from '@maka/runtime-host/client';
23
- import { INTERACTIVE_RUNTIME_HOST_COMPOSITION_ID, RUNTIME_HOST_COMPATIBILITY_EPOCH, RUNTIME_HOST_PROTOCOL_VERSION, } from '@maka/runtime-host/protocol';
24
- import { readLocalHostDeploymentRecord } from '@maka/runtime-host/operator';
23
+ import { connectOrSpawnRuntimeHost, connectRuntimeHost, connectRuntimeHostProfile, createClientRuntimeHostProfileCatalog, createRuntimeHostPeerClientFromEnvironment, createRuntimeHostReconnectingConnection, loadOrCreateRuntimeHostClientInstanceId, LOCAL_RUNTIME_HOST_PROFILE, readRuntimeHostConnectionCatalog, RuntimeHostPermanentReconnectError, RuntimeHostRemoteCompatibilityError, runHostHandoff, runtimeHostStartupError, } from '@maka/runtime-host/client';
24
+ import { INTERACTIVE_RUNTIME_HOST_COMPOSITION_ID, RUNTIME_HOST_PROTOCOL_VERSION, } from '@maka/runtime-host/protocol';
25
+ import { readLocalHostDeploymentRecord, resolveRuntimeHostManagedDeploymentAuthority, } from '@maka/runtime-host/operator';
25
26
  import { resolveMakaClientDataRoot } from '@maka/storage/workspace-root';
27
+ import { isTemporaryNpxInstallation, resolveRuntimeHostNpmGlobalInstallation, } from './runtime-host-cli-installation.js';
28
+ import { restartRuntimeHostNpmGlobalDeployment, runtimeHostNpmGlobalSourceRetirementAvailable, } from './runtime-host-local-handoff.js';
26
29
  /**
27
30
  * The mode a new Session starts in belongs to the Host: `session.create`
28
31
  * falls back to `chatDefaults.permissionMode` in the Runtime Policy whenever a
@@ -42,17 +45,6 @@ import { resolveMakaClientDataRoot } from '@maka/storage/workspace-root';
42
45
  export async function readHostChatDefaultPermissionMode(connection) {
43
46
  return (await connection.request('runtime.policy.query', {})).policy.chatDefaults.permissionMode;
44
47
  }
45
- export class RuntimeHostCliConflictError extends RuntimeHostPermanentReconnectError {
46
- handshake;
47
- registration;
48
- code = 'RUNTIME_HOST_RESTART_REQUIRED';
49
- constructor(handshake, registration) {
50
- super(formatRuntimeHostCliConflict(handshake, registration));
51
- this.handshake = handshake;
52
- this.registration = registration;
53
- this.name = 'RuntimeHostCliConflictError';
54
- }
55
- }
56
48
  export async function connectRuntimeHostCli(input, overrides = {}) {
57
49
  const context = await connectRuntimeHostCliConnection(input, overrides);
58
50
  try {
@@ -67,12 +59,19 @@ export async function connectRuntimeHostCli(input, overrides = {}) {
67
59
  export async function connectRuntimeHostCliConnection(input, overrides = {}) {
68
60
  const deps = {
69
61
  connectOrSpawn: connectOrSpawnRuntimeHost,
62
+ activateLocalManagedHost: activateLocalManagedRuntimeHost,
63
+ connectActivatedHost: connectRuntimeHost,
70
64
  connectProfile: connectRuntimeHostProfile,
71
65
  readConnectionCatalog: readRuntimeHostConnectionCatalog,
72
66
  loadClientInstanceId: loadOrCreateRuntimeHostClientInstanceId,
73
67
  executionCandidateEntrypoint: new URL(import.meta.resolve('@maka/runtime-host/execution-candidate-main')),
74
68
  readDeploymentRecord: readLocalHostDeploymentRecord,
69
+ resolveManagedAuthority: resolveRuntimeHostManagedDeploymentAuthority,
75
70
  createPeerClient: createRuntimeHostPeerClientFromEnvironment,
71
+ resolveInstallation: resolveRuntimeHostNpmGlobalInstallation,
72
+ isTemporaryNpxInstallation,
73
+ restartDeployment: restartRuntimeHostNpmGlobalDeployment,
74
+ sourceRetirementAvailable: runtimeHostNpmGlobalSourceRetirementAvailable,
76
75
  ...overrides,
77
76
  };
78
77
  const resolvedProfile = await resolveHostProfile(input, deps);
@@ -83,45 +82,171 @@ export async function connectRuntimeHostCliConnection(input, overrides = {}) {
83
82
  const peerClient = profile.kind === 'remote' && profile.transport.kind === 'libp2p-direct'
84
83
  ? deps.createPeerClient()
85
84
  : undefined;
85
+ // A temporary package is evidence for invocation lifetime, never deployment
86
+ // authority. This only guards candidates we create; using an existing Host
87
+ // leaves that Host's ownership and lifetime unchanged.
88
+ const invocationOwned = profile.kind === 'local' && (await deps.isTemporaryNpxInstallation());
86
89
  const connectInput = {
87
90
  rootPath: input.rootPath,
88
91
  protocol: { min: RUNTIME_HOST_PROTOCOL_VERSION, max: RUNTIME_HOST_PROTOCOL_VERSION },
89
92
  clientInstanceId,
90
93
  compositionId: INTERACTIVE_RUNTIME_HOST_COMPOSITION_ID,
91
94
  candidateEntrypoint: deps.executionCandidateEntrypoint,
95
+ ...(invocationOwned ? { closeOnLauncherExit: true } : {}),
92
96
  };
93
97
  const connect = async (signal, sshInteraction = 'batch') => {
94
- if (profile.kind !== 'local') {
95
- return deps.connectProfile({
96
- profile,
97
- ...(resolvedProfile.credential ? { credential: resolvedProfile.credential } : {}),
98
- clientInstanceId,
99
- sshInteraction,
100
- ...(peerClient ? { peerClient } : {}),
98
+ const observe = async () => {
99
+ if (profile.kind !== 'local') {
100
+ try {
101
+ const connection = await deps.connectProfile({
102
+ profile,
103
+ ...(resolvedProfile.credential ? { credential: resolvedProfile.credential } : {}),
104
+ clientInstanceId,
105
+ sshInteraction,
106
+ ...(peerClient ? { peerClient } : {}),
107
+ ...(signal ? { signal } : {}),
108
+ });
109
+ return { kind: 'ready', value: connection };
110
+ }
111
+ catch (error) {
112
+ if (!(error instanceof RuntimeHostRemoteCompatibilityError))
113
+ throw error;
114
+ return {
115
+ kind: 'blocked',
116
+ blocker: {
117
+ identity: JSON.stringify([profile, error.hostEpoch, error.details]),
118
+ target: {
119
+ name: profile.name,
120
+ location: 'remote',
121
+ rootId: profile.rootId,
122
+ hostEpoch: error.hostEpoch,
123
+ },
124
+ reason: 'upgrade',
125
+ mayExitNaturally: false,
126
+ diagnostic: error.message,
127
+ },
128
+ };
129
+ }
130
+ }
131
+ let connected = await deps.connectOrSpawn({
132
+ ...connectInput,
101
133
  ...(signal ? { signal } : {}),
102
134
  });
103
- }
104
- const connected = await deps.connectOrSpawn({
105
- ...connectInput,
106
- ...(signal ? { signal } : {}),
107
- });
108
- if (connected.kind === 'incompatible') {
109
- throw new RuntimeHostCliConflictError(connected.handshake, connected.registration);
110
- }
111
- if (connected.kind === 'upgrade_required') {
112
- throw new RuntimeHostPermanentReconnectError('RUNTIME_HOST_RESTART_REQUIRED: An older Runtime Host build is still running. Restart it, or wait for its background work to finish.');
113
- }
114
- if (connected.kind === 'failed') {
115
- throw runtimeHostStartupError(connected.reason, connected.diagnostic);
116
- }
117
- if (connected.registration.generation?.startsWith('npm-global-handoff:')) {
118
- const record = await deps.readDeploymentRecord(connected.registration.rootId);
119
- if (record?.state.kind !== 'owned' || record.state.owner.kind !== 'cli') {
120
- await connected.connection.close().catch(() => undefined);
121
- throw new RuntimeHostPermanentReconnectError('RUNTIME_HOST_RECOVERY_REQUIRED: The staged local Runtime Host is Ready, but its installation ownership was not durably committed.');
135
+ if (connected.kind === 'failed' && connected.reason === 'managed_root_requires_operator') {
136
+ await deps.activateLocalManagedHost({
137
+ rootPath: input.rootPath,
138
+ ...(signal ? { signal } : {}),
139
+ });
140
+ signal?.throwIfAborted();
141
+ // The installed operator owns launch. Compatibility is still projected
142
+ // by the same handoff journey as an already-running managed Host.
143
+ const activated = await deps.connectActivatedHost(connectInput);
144
+ if (activated.kind === 'unavailable' || activated.kind === 'draining') {
145
+ throw new Error(`The installed Runtime Host was activated but the local CLI could not join it (${activated.kind === 'unavailable' ? activated.reason : activated.kind}). Use a compatible CLI or update the managed Host through its configured operator.`);
146
+ }
147
+ if (activated.kind === 'connected' && signal?.aborted) {
148
+ await activated.connection.close();
149
+ signal.throwIfAborted();
150
+ }
151
+ connected = activated;
122
152
  }
123
- }
124
- return connected.connection;
153
+ if (connected.kind === 'incompatible' || connected.kind === 'upgrade_required') {
154
+ const managed = await deps.resolveManagedAuthority(connected.registration.rootId);
155
+ const record = managed
156
+ ? undefined
157
+ : await deps.readDeploymentRecord(connected.registration.rootId);
158
+ let replacement;
159
+ let installation;
160
+ // On-demand managed Hosts are also ephemeral processes. Their durable
161
+ // operator authority, not the process lifetime label, decides who may
162
+ // replace them (including when the Host was already running).
163
+ if (!managed && connected.registration.lifecycleMode === 'ephemeral') {
164
+ try {
165
+ installation = await deps.resolveInstallation();
166
+ }
167
+ catch {
168
+ /* No persistent installation authority. */
169
+ }
170
+ const owner = record?.state.kind === 'handoff' ? record.state.from : record?.state.owner;
171
+ if (installation &&
172
+ (!owner ||
173
+ (owner.kind === installation.owner.kind &&
174
+ owner.installationId === installation.owner.installationId))) {
175
+ const expectedInstallation = installation;
176
+ const canInterrupt = record
177
+ ? await deps
178
+ .sourceRetirementAvailable({
179
+ rootId: connected.registration.rootId,
180
+ owner: installation.owner,
181
+ source: record.state.selected,
182
+ })
183
+ .catch(() => false)
184
+ : false;
185
+ replacement = {
186
+ kind: record?.state.kind === 'handoff' ? 'repair' : 'replace',
187
+ canReplaceIdle: true,
188
+ canInterrupt,
189
+ execute: async (activeWorkPolicy, _progress, _consent, attemptSignal) => {
190
+ const result = await deps.restartDeployment({
191
+ rootPath: input.rootPath,
192
+ registration: connected.registration,
193
+ activeWorkPolicy,
194
+ expectedInstallation,
195
+ ...(attemptSignal ? { signal: attemptSignal } : {}),
196
+ });
197
+ if (result.kind === 'completed')
198
+ return { kind: 'completed' };
199
+ if (result.kind === 'active_work')
200
+ return { kind: 'active_work' };
201
+ if (result.kind === 'changed' ||
202
+ result.kind === 'rejected' ||
203
+ result.kind === 'operator_required')
204
+ return { kind: 'changed' };
205
+ return {
206
+ kind: 'recovery_required',
207
+ diagnostic: `Local service recovery is required at ${result.phase}`,
208
+ };
209
+ },
210
+ };
211
+ }
212
+ }
213
+ return {
214
+ kind: 'blocked',
215
+ blocker: {
216
+ identity: JSON.stringify([
217
+ connected.registration,
218
+ managed?.record,
219
+ record,
220
+ installation,
221
+ ]),
222
+ target: {
223
+ name: profile.name,
224
+ location: 'local',
225
+ rootId: connected.registration.rootId,
226
+ hostEpoch: connected.registration.hostEpoch,
227
+ },
228
+ reason: record?.state.kind === 'handoff' ? 'repair' : 'upgrade',
229
+ ...(connected.handshake?.activity ? { activity: connected.handshake.activity } : {}),
230
+ mayExitNaturally: !managed &&
231
+ connected.registration.lifecycleMode === 'ephemeral' &&
232
+ connected.handshake?.replacement === 'wait_for_idle_exit',
233
+ ...(replacement ? { replacement } : {}),
234
+ },
235
+ };
236
+ }
237
+ if (connected.kind === 'failed') {
238
+ throw runtimeHostStartupError(connected.reason, connected.diagnostic);
239
+ }
240
+ if (connected.registration.generation?.startsWith('npm-global-handoff:')) {
241
+ const record = await deps.readDeploymentRecord(connected.registration.rootId);
242
+ if (record?.state.kind !== 'owned' || record.state.owner.kind !== 'cli') {
243
+ await connected.connection.close().catch(() => undefined);
244
+ throw new RuntimeHostPermanentReconnectError('RUNTIME_HOST_RECOVERY_REQUIRED: The staged local Runtime Host is Ready, but its installation ownership was not durably committed.');
245
+ }
246
+ }
247
+ return { kind: 'ready', value: connected.connection };
248
+ };
249
+ return runHostHandoff({ observe, signal, openSurface: input.handoffSurface });
125
250
  };
126
251
  let initialConnection;
127
252
  let connection;
@@ -222,42 +347,6 @@ async function resolveHostProfile(input, deps) {
222
347
  const catalog = deps.profileCatalog ?? createClientRuntimeHostProfileCatalog(root);
223
348
  return catalog.resolve(input.profileId);
224
349
  }
225
- function formatRuntimeHostCliConflict(handshake, registration) {
226
- const lines = [
227
- 'RUNTIME_HOST_RESTART_REQUIRED: An older Runtime Host is still running and cannot accept this client.',
228
- `Local Runtime Host: PID ${registration.pid}; lifecycle ${registration.lifecycleMode ?? 'unknown'}; compatibility epoch ${registration.compatibilityEpoch}.`,
229
- ];
230
- if (registration.lifecycleMode === 'ephemeral') {
231
- lines.push('The ephemeral Host is not currently idle and cannot be replaced by this Client.');
232
- }
233
- else if (registration.lifecycleMode === 'service') {
234
- lines.push('This service Host is managed by its operator and cannot be replaced by this Client.');
235
- }
236
- else {
237
- lines.push('This Host cannot be replaced by this Client.');
238
- }
239
- if (handshake.compatibilityEpoch < RUNTIME_HOST_COMPATIBILITY_EPOCH) {
240
- lines.push(registration.lifecycleMode === 'service'
241
- ? 'Use the service operator to inspect or upgrade the Host.'
242
- : 'Use a previous compatible Maka build to inspect the Host and finish or clear any retained work. Stop the Host only after deciding that interruption is safe.');
243
- }
244
- else {
245
- lines.push(`Host protocol ${handshake.protocolMin}-${handshake.protocolMax}; CLI protocol ${RUNTIME_HOST_PROTOCOL_VERSION}.`);
246
- }
247
- return lines.join('\n');
248
- }
249
- export function shouldRetryRuntimeHostConflict(answer) {
250
- const normalized = answer.trim().toLowerCase();
251
- return normalized === 'w' || normalized === 'wait';
252
- }
253
- export function resolveRuntimeHostCliConflictDecision(answer, canRestart) {
254
- const normalized = answer.trim().toLowerCase();
255
- if (canRestart && (normalized === 'r' || normalized === 'restart'))
256
- return 'restart';
257
- if (normalized === 'w' || normalized === 'wait')
258
- return 'wait';
259
- return 'cancel';
260
- }
261
350
  export function resolveRuntimeHostCliTarget(catalog, input = {}) {
262
351
  const defaultTarget = catalog.defaultTarget;
263
352
  const connection = input.connectionSlug
@@ -76,9 +76,15 @@ export async function resolveRuntimeHostNpmGlobalInstallation(options = {}, over
76
76
  observedRelease: { version: manifest.version, packageRoot, cliPath },
77
77
  };
78
78
  }
79
- export async function isTemporaryNpxInstallation(path, input) {
79
+ export async function isTemporaryNpxInstallation(path = fileURLToPath(new URL('../', import.meta.url)), input = { environment: process.env, homeDir: homedir() }) {
80
80
  const canonicalPath = await realpath(path).catch(() => resolve(path));
81
- const cacheRoots = await Promise.all([input.environment.npm_config_cache, join(input.homeDir, '.npm')].flatMap((root) => root ? [realpath(resolve(root, '_npx')).catch(() => resolve(root, '_npx'))] : []));
81
+ const defaultCache = (input.platform ?? process.platform) === 'win32'
82
+ ? join(input.environment.LOCALAPPDATA || input.homeDir, 'npm-cache')
83
+ : join(input.homeDir, '.npm');
84
+ const cacheRoots = await Promise.all([
85
+ input.environment.npm_config_cache ?? input.environment.NPM_CONFIG_CACHE,
86
+ defaultCache,
87
+ ].flatMap((root) => root ? [realpath(resolve(root, '_npx')).catch(() => resolve(root, '_npx'))] : []));
82
88
  return cacheRoots.some((root) => isWithin(root, canonicalPath));
83
89
  }
84
90
  async function readPackageManifest(url) {
@@ -136,6 +136,10 @@ function parseLocalUpdateApply(argv) {
136
136
  '--target-version',
137
137
  '--target-integrity',
138
138
  '--target-compatibility',
139
+ '--expected-root-id',
140
+ '--expected-deployment-revision',
141
+ '--expected-owner-installation-id',
142
+ '--expected-host-epoch',
139
143
  ]);
140
144
  for (let index = 0; index < argv.length; index += 1) {
141
145
  const argument = argv[index];
@@ -195,6 +199,28 @@ function parseLocalUpdateApply(argv) {
195
199
  (!Number.isSafeInteger(targetCompatibility) || targetCompatibility <= 0)) {
196
200
  return error('runtime-host local-update-apply compatibility is invalid');
197
201
  }
202
+ const expectedValues = [
203
+ '--expected-root-id',
204
+ '--expected-deployment-revision',
205
+ '--expected-owner-installation-id',
206
+ '--expected-host-epoch',
207
+ ].map((name) => values.get(name));
208
+ let expectedSource;
209
+ if (expectedValues.some((value) => value !== undefined)) {
210
+ if (!expectedValues.every((value) => value !== undefined && isSafeIdentity(value))) {
211
+ return error('runtime-host local-update-apply requires one complete expected source identity');
212
+ }
213
+ const [rootId, deploymentRevision, ownerInstallationId, hostEpoch] = expectedValues;
214
+ if (!/^[a-f0-9]{64}$/u.test(rootId)) {
215
+ return error('runtime-host local-update-apply expected Root identity is invalid');
216
+ }
217
+ expectedSource = {
218
+ rootId: rootId,
219
+ deploymentRevision: deploymentRevision,
220
+ ownerInstallationId: ownerInstallationId,
221
+ hostEpoch: hostEpoch,
222
+ };
223
+ }
198
224
  return {
199
225
  kind: 'runtime-host-local-update-apply',
200
226
  rootPath,
@@ -206,6 +232,7 @@ function parseLocalUpdateApply(argv) {
206
232
  targetIntegrity,
207
233
  ...(targetCompatibility === undefined ? {} : { targetCompatibility }),
208
234
  allowInterruptActiveTasks,
235
+ ...(expectedSource ? { expectedSource } : {}),
209
236
  };
210
237
  }
211
238
  function parseLocalUpdateActivate(argv) {
@@ -553,7 +580,7 @@ function parseServiceManagementCommand(argv) {
553
580
  },
554
581
  }
555
582
  : {}),
556
- ...(action === 'update'
583
+ ...(action === 'update' || action === 'restart'
557
584
  ? {
558
585
  '--expected-host-json': (value) => {
559
586
  if (expectedHost !== undefined)
@@ -675,10 +702,13 @@ function parseServiceManagementCommand(argv) {
675
702
  ...(allowInterruptActiveTasks ? { allowInterruptActiveTasks: true } : {}),
676
703
  };
677
704
  }
705
+ if (expectedHost && !options.managedRootId)
706
+ return error('--expected-host-json requires --managed-root-id');
678
707
  return {
679
708
  kind: 'runtime-host-service-manage',
680
709
  action,
681
710
  ...options,
711
+ ...(expectedHost ? { expectedHost } : {}),
682
712
  ...(clientDataRoot ? { clientDataRoot } : {}),
683
713
  ...(options.managedRootId ? { managedRootId: options.managedRootId } : {}),
684
714
  ...(retainManagedDeployment ? { retainManagedDeployment: true } : {}),
@@ -0,0 +1,81 @@
1
+ /*
2
+ * Licensed to the Apache Software Foundation (ASF) under one
3
+ * or more contributor license agreements. See the NOTICE file
4
+ * distributed with this work for additional information
5
+ * regarding copyright ownership. The ASF licenses this file
6
+ * to you under the Apache License, Version 2.0 (the
7
+ * "License"); you may not use this file except in compliance
8
+ * with the License. You may obtain a copy of the License at
9
+ *
10
+ * http://www.apache.org/licenses/LICENSE-2.0
11
+ *
12
+ * Unless required by applicable law or agreed to in writing,
13
+ * software distributed under the License is distributed on an
14
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15
+ * KIND, either express or implied. See the License for the
16
+ * specific language governing permissions and limitations
17
+ * under the License.
18
+ */
19
+ import { createInterface } from 'node:readline';
20
+ import { stripVTControlCharacters } from 'node:util';
21
+ import { formatHostHandoff, } from '@maka/runtime-host/client';
22
+ /** One readline lifetime renders the shared decision; it never selects replacement policy. */
23
+ export function createCliHostHandoffSurface(locale, input = process.stdin, output = process.stderr) {
24
+ return (submit) => {
25
+ const readline = createInterface({ input, output, terminal: output.isTTY === true });
26
+ let current;
27
+ let closed = false;
28
+ let ended = false;
29
+ const cancel = () => {
30
+ if (current)
31
+ submit(current.revision, 'cancel');
32
+ };
33
+ readline.on('SIGINT', cancel);
34
+ readline.on('close', () => {
35
+ ended = true;
36
+ if (!closed)
37
+ cancel();
38
+ });
39
+ readline.on('line', (line) => {
40
+ if (!current)
41
+ return;
42
+ const answer = line.trim().toLowerCase();
43
+ const action = answer === 'r' ? 'retry' : answer === 'stop' ? 'interrupt' : 'cancel';
44
+ if (!current.actions.includes(action)) {
45
+ readline.prompt();
46
+ return;
47
+ }
48
+ submit(current.revision, action);
49
+ });
50
+ return {
51
+ update(view) {
52
+ if (closed || current?.revision === view.revision)
53
+ return;
54
+ current = view;
55
+ if (ended) {
56
+ cancel();
57
+ return;
58
+ }
59
+ // Discard partially typed consent when the observed target/consequences change.
60
+ if (output.isTTY)
61
+ readline.write(null, { ctrl: true, name: 'u' });
62
+ const copy = formatHostHandoff(view, locale);
63
+ const text = [copy.title, copy.description, copy.detail, view.diagnostic]
64
+ .filter(Boolean)
65
+ .join('\n');
66
+ output.write('\n' +
67
+ stripVTControlCharacters(text).replace(/[\p{Cc}\p{Cf}]/gu, (character) => character === '\n' ? character : '\uFFFD') +
68
+ '\n');
69
+ if (view.actions.length === 0)
70
+ return;
71
+ const options = copy.actions.map(({ action, label }) => `${action === 'interrupt' ? 'stop' : action === 'retry' ? 'r' : 'Enter'}: ${label}`);
72
+ readline.setPrompt(options.join(' · ') + ' > ');
73
+ readline.prompt();
74
+ },
75
+ close() {
76
+ closed = true;
77
+ readline.close();
78
+ },
79
+ };
80
+ };
81
+ }
@@ -64,6 +64,7 @@ export async function runRuntimeHostInstalledUpdateBootstrap(input, overrides =
64
64
  ? {}
65
65
  : { targetCompatibility: target.compatibility }),
66
66
  allowInterruptActiveTasks: input.allowInterruptActiveTasks,
67
+ ...(input.expectedSource ? { expectedSource: input.expectedSource } : {}),
67
68
  });
68
69
  }
69
70
  finally {
@@ -94,6 +95,18 @@ function launchCoordinator(input) {
94
95
  ? []
95
96
  : ['--target-compatibility', String(input.targetCompatibility)]),
96
97
  ...(input.allowInterruptActiveTasks ? ['--allow-interrupt-active-tasks'] : []),
98
+ ...(input.expectedSource
99
+ ? [
100
+ '--expected-root-id',
101
+ input.expectedSource.rootId,
102
+ '--expected-deployment-revision',
103
+ input.expectedSource.deploymentRevision,
104
+ '--expected-owner-installation-id',
105
+ input.expectedSource.ownerInstallationId,
106
+ '--expected-host-epoch',
107
+ input.expectedSource.hostEpoch,
108
+ ]
109
+ : []),
97
110
  ];
98
111
  return new Promise((resolve, reject) => {
99
112
  const child = spawn(process.execPath, args, { stdio: 'inherit', windowsHide: false });
@@ -22,6 +22,7 @@ import { homedir } from 'node:os';
22
22
  import { dirname, join } from 'node:path';
23
23
  import { pathToFileURL } from 'node:url';
24
24
  import { connectExistingRuntimeHost, prepareConnectedRuntimeHostRetirement, waitForRuntimeHostReady, } from '@maka/runtime-host/client';
25
+ import { resolveRuntimeHostManagedDeploymentAuthority, } from '@maka/runtime-host/operator';
25
26
  import { INTERACTIVE_RUNTIME_HOST_COMPOSITION_ID, RUNTIME_HOST_PROTOCOL_VERSION, } from '@maka/runtime-host/protocol';
26
27
  import { resolveStorageRoot } from '@maka/storage/root-authority';
27
28
  import { prepareRuntimeHostNpmGlobalStagedDeployment, reconcilePreparedRuntimeHostNpmGlobalDeployment, } from './runtime-host-local-handoff.js';
@@ -35,6 +36,7 @@ export async function runRuntimeHostInstalledUpdateCoordinator(input, authorityO
35
36
  const deps = {
36
37
  resolveInstallation: resolveRuntimeHostNpmGlobalInstallation,
37
38
  resolveRoot: resolveStorageRoot,
39
+ resolveManagedAuthority: resolveRuntimeHostManagedDeploymentAuthority,
38
40
  connectExisting: connectExistingRuntimeHost,
39
41
  waitForReady: waitForRuntimeHostReady,
40
42
  prepareRetirement: prepareConnectedRuntimeHostRetirement,
@@ -54,6 +56,14 @@ export async function runRuntimeHostInstalledUpdateCoordinator(input, authorityO
54
56
  throw new Error(`The installed Maka release changed from ${input.currentVersion} to ${installation.observedRelease.version} before update coordination`);
55
57
  }
56
58
  const root = await deps.resolveRoot({ path: input.rootPath, kind: 'interactive' });
59
+ if (input.expectedSource &&
60
+ (root.rootId !== input.expectedSource.rootId ||
61
+ installation.owner.installationId !== input.expectedSource.ownerInstallationId)) {
62
+ throw new Error('The observed local Host installation owner changed before update.');
63
+ }
64
+ if (await deps.resolveManagedAuthority(root.rootId)) {
65
+ throw new Error('The managed Runtime Host must be updated through its installed operator.');
66
+ }
57
67
  const transactionId = updateTransactionId(root.rootId, installation, input.target);
58
68
  return deps.withArchive(input.target, input.archivePath, async ({ packageRoot, archivePath }) => {
59
69
  const staged = await deps.prepareStaged({
@@ -90,7 +100,19 @@ export async function runRuntimeHostInstalledUpdateCoordinator(input, authorityO
90
100
  return 'target_present';
91
101
  };
92
102
  const prepare = async (inheritableAuthorityLeaseFd) => {
103
+ if (await deps.resolveManagedAuthority(root.rootId)) {
104
+ throw new Error('The managed Runtime Host must be updated through its installed operator.');
105
+ }
93
106
  observation = await observeCurrentHost(input.rootPath, root.rootId, deps);
107
+ // This callback runs under the existing deployment-authority lease.
108
+ // Do not turn the TUI's consent for one observed Host into retirement of
109
+ // a successor that appeared while the archive/coordinator was prepared.
110
+ if (input.expectedSource &&
111
+ observation.registration?.hostEpoch !== input.expectedSource.hostEpoch) {
112
+ await observation.connection?.close();
113
+ observation = {};
114
+ throw new Error('The observed Runtime Host changed before update retirement.');
115
+ }
94
116
  if (observation.registration && observation.registration.lifecycleMode !== 'ephemeral') {
95
117
  throw new Error('Only an ephemeral local Runtime Host can be updated by this CLI');
96
118
  }
@@ -149,6 +171,17 @@ export async function runRuntimeHostInstalledUpdateCoordinator(input, authorityO
149
171
  : 'refuse_active_work',
150
172
  installation,
151
173
  staged,
174
+ ...(input.expectedSource
175
+ ? {
176
+ expectedOwner: {
177
+ revision: input.expectedSource.deploymentRevision,
178
+ owner: {
179
+ kind: 'cli',
180
+ installationId: input.expectedSource.ownerInstallationId,
181
+ },
182
+ },
183
+ }
184
+ : {}),
152
185
  }, {
153
186
  prepareUnownedHostCutover: (_rootId, _target, _staged, _policy, leaseFd) => prepare(leaseFd),
154
187
  prepareHostCutover: (_rootId, _selected, _target, _staged, _policy, leaseFd) => prepare(leaseFd),
@@ -17,6 +17,7 @@
17
17
  * under the License.
18
18
  */
19
19
  import { randomUUID } from 'node:crypto';
20
+ import { withOperatorRetirementCancellation } from './runtime-host-operator-retirement.js';
20
21
  import { isDeepStrictEqual } from 'node:util';
21
22
  import { resolveExistingStorageRoot, tryAcquireStateRootOwner, } from '@maka/storage/root-authority';
22
23
  import { connectExistingRuntimeHost, prepareConnectedRuntimeHostRetirement, RuntimeHostOperationError, RuntimeHostRequestInterruptedError, waitForRuntimeHostReady, } from '@maka/runtime-host/client';
@@ -173,6 +174,10 @@ function assertRecoveryTarget(expected, rootId, record) {
173
174
  }
174
175
  }
175
176
  export async function retireRuntimeHostLifecycleOwner(input) {
177
+ return withOperatorRetirementCancellation((signal) => retireRuntimeHostLifecycleOwnerWithSignal(input, signal));
178
+ }
179
+ async function retireRuntimeHostLifecycleOwnerWithSignal(input, signal) {
180
+ signal?.throwIfAborted();
176
181
  if (input.expectedOwner && !input.supervisor) {
177
182
  throw new RuntimeHostLifecycleTransactionError('owner_changed', 'A Runtime Host identity fence requires a supervised deployment');
178
183
  }
@@ -188,6 +193,7 @@ export async function retireRuntimeHostLifecycleOwner(input) {
188
193
  const status = await input.supervisor.status();
189
194
  assertExpectedSupervisorOwner(input.expectedOwner, status);
190
195
  }
196
+ signal?.throwIfAborted();
191
197
  if (input.retireIdleSupervisor !== false)
192
198
  await input.supervisor?.retire();
193
199
  return { kind: 'retired', owner: idleOwner };
@@ -212,6 +218,7 @@ export async function retireRuntimeHostLifecycleOwner(input) {
212
218
  if (status.active && status.pid !== null) {
213
219
  if (!input.allowInterruptActiveTasks)
214
220
  return { kind: 'active_tasks' };
221
+ signal?.throwIfAborted();
215
222
  await input.supervisor.retire();
216
223
  return waitForRuntimeHostLifecycleOwner(capability, input.timeoutMs ?? 45_000);
217
224
  }
@@ -234,7 +241,7 @@ export async function retireRuntimeHostLifecycleOwner(input) {
234
241
  // The exact Root owner and canonical supervisor now agree while the deployment lock is held.
235
242
  // This admits retirement of that deployment; a later same-deployment restart is not a new
236
243
  // authority, but it must not acquire the Root before the supervisor is retired.
237
- const prepared = await prepareConnectedRuntimeHostRetirement(connected.connection, input.allowInterruptActiveTasks ? 'interrupt_active_work' : 'refuse_active_work');
244
+ const prepared = await prepareConnectedRuntimeHostRetirement(connected.connection, input.allowInterruptActiveTasks ? 'interrupt_active_work' : 'refuse_active_work', undefined, signal);
238
245
  if (prepared.kind === 'active_tasks')
239
246
  return prepared;
240
247
  if (prepared.pid !== diagnostics.pid) {
@@ -242,6 +249,7 @@ export async function retireRuntimeHostLifecycleOwner(input) {
242
249
  }
243
250
  }
244
251
  catch (error) {
252
+ signal?.throwIfAborted();
245
253
  if (!isRuntimeHostRetirementUnavailable(error) || !input.supervisor)
246
254
  throw error;
247
255
  if (!input.allowInterruptActiveTasks)