maka-agent 0.2.0-dev.23.20260906 → 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.
- package/dist/acp/maka-acp-agent.js +2 -1
- package/dist/acp/session-configuration.js +143 -0
- package/dist/acp/session-registry.js +90 -4
- package/dist/cli-core.js +2 -0
- package/dist/pi-transcript.js +5 -12
- package/dist/pi-tui-runner.js +74 -4
- package/dist/runtime-host-cli-context.js +167 -78
- package/dist/runtime-host-cli-installation.js +8 -2
- package/dist/runtime-host-cli.js +31 -1
- package/dist/runtime-host-handoff-surface.js +81 -0
- package/dist/runtime-host-installed-update-bootstrap.js +13 -0
- package/dist/runtime-host-installed-update-coordinator.js +33 -0
- package/dist/runtime-host-lifecycle-transaction.js +9 -1
- package/dist/runtime-host-local-handoff.js +37 -4
- package/dist/runtime-host-local-managed-activation.js +84 -0
- package/dist/runtime-host-local-source-retirement.js +18 -2
- package/dist/runtime-host-managed-lifecycle-manager.js +1 -0
- package/dist/runtime-host-operator-retirement.js +50 -0
- package/dist/runtime-host-service-manager.js +3 -0
- package/dist/runtime-host-session-driver.js +4 -25
- package/dist/runtime-host-session-update.js +62 -0
- package/dist/runtime-host-tui-command.js +38 -73
- package/dist/runtime-host-tui-context.js +1 -0
- package/dist/runtime-host-tui-owner.js +226 -0
- package/dist/tui-copy-catalog.js +140 -3
- package/dist/tui-host-owner-copy.js +24 -0
- package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
- package/node_modules/@maka/core/dist/canonical-runtime-event.js +3 -1
- package/node_modules/@maka/core/dist/model-failure.js +40 -0
- package/node_modules/@maka/core/dist/model-metadata.generated.js +4 -4
- package/node_modules/@maka/core/dist/model-metadata.js +3 -3
- package/node_modules/@maka/core/dist/model-web-search.js +5 -4
- package/node_modules/@maka/core/dist/provider-registry.js +45 -4
- package/node_modules/@maka/core/dist/record-schema.js +9 -2
- package/node_modules/@maka/core/dist/runtime-boundary.js +31 -11
- package/node_modules/@maka/core/dist/runtime-event.js +37 -6
- package/node_modules/@maka/core/dist/runtime-handoff.js +98 -0
- package/node_modules/@maka/core/dist/runtime-invocation.js +1 -1
- package/node_modules/@maka/core/dist/runtime-logical-execution.js +112 -0
- package/node_modules/@maka/core/dist/session.js +54 -52
- package/node_modules/@maka/core/dist/slash-command-catalog.js +2 -0
- package/node_modules/@maka/core/dist/tool-result-archive-evidence.js +20 -0
- package/node_modules/@maka/core/dist/usage-record-schema.js +26 -35
- package/node_modules/@maka/core/package.json +4 -0
- package/node_modules/@maka/mcp/dist/index.js +131 -1
- package/node_modules/@maka/runtime/dist/agent-run.js +272 -105
- package/node_modules/@maka/runtime/dist/ai-sdk-backend.js +13 -5
- package/node_modules/@maka/runtime/dist/ai-sdk-compaction.js +109 -34
- package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +127 -133
- package/node_modules/@maka/runtime/dist/context-budget.js +14 -9
- package/node_modules/@maka/runtime/dist/conversation-copy.js +5 -4
- package/node_modules/@maka/runtime/dist/goal-continuation.js +36 -4
- package/node_modules/@maka/runtime/dist/history-compact-checkpoint.js +18 -9
- package/node_modules/@maka/runtime/dist/history-compact-summarizer.js +30 -22
- package/node_modules/@maka/runtime/dist/history-compaction.js +29 -4
- package/node_modules/@maka/runtime/dist/ledger-tool-result-archive-reader.js +106 -0
- package/node_modules/@maka/runtime/dist/message-authority.js +9 -0
- package/node_modules/@maka/runtime/dist/model-adapter.js +19 -98
- package/node_modules/@maka/runtime/dist/model-factory.js +19 -81
- package/node_modules/@maka/runtime/dist/model-fetcher.js +7 -0
- package/node_modules/@maka/runtime/dist/model-runtime.js +85 -111
- package/node_modules/@maka/runtime/dist/native-web-search-tool.js +2 -1
- package/node_modules/@maka/runtime/dist/openai-codex-history-compactor.js +2 -2
- package/node_modules/@maka/runtime/dist/opencode-session-header.js +4 -2
- package/node_modules/@maka/runtime/dist/provider-error-classification.js +84 -123
- package/node_modules/@maka/runtime/dist/provider-request-telemetry.js +68 -108
- package/node_modules/@maka/runtime/dist/run-handoff-gate.js +97 -0
- package/node_modules/@maka/runtime/dist/runtime-event-backfill.js +82 -54
- package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +117 -159
- package/node_modules/@maka/runtime/dist/runtime-kernel.js +150 -112
- package/node_modules/@maka/runtime/dist/runtime-ledger-repair.js +160 -79
- package/node_modules/@maka/runtime/dist/runtime-read-model.js +30 -89
- package/node_modules/@maka/runtime/dist/runtime-resume.js +80 -4
- package/node_modules/@maka/runtime/dist/session-event-runtime-mapper.js +5 -0
- package/node_modules/@maka/runtime/dist/session-manager.js +171 -288
- package/node_modules/@maka/runtime/dist/session-projection-helpers.js +0 -27
- package/node_modules/@maka/runtime/dist/subscription-model-fetch.js +3 -4
- package/node_modules/@maka/runtime/dist/test-connection.js +16 -16
- package/node_modules/@maka/runtime/dist/tool-result-archive-encoding.js +38 -0
- package/node_modules/@maka/runtime/dist/tool-runtime.js +3 -61
- package/node_modules/@maka/runtime/package.json +1 -0
- package/node_modules/@maka/runtime-host/dist/adapter/session-projector.js +35 -0
- package/node_modules/@maka/runtime-host/dist/client/connect-or-spawn.js +9 -1
- package/node_modules/@maka/runtime-host/dist/client/connection.js +3 -0
- package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +156 -0
- package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +273 -0
- package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +26 -5
- package/node_modules/@maka/runtime-host/dist/client/index.js +2 -0
- package/node_modules/@maka/runtime-host/dist/client/launcher.js +3 -0
- package/node_modules/@maka/runtime-host/dist/client/remote-compatibility-error.js +2 -0
- package/node_modules/@maka/runtime-host/dist/client/session-subscription.js +27 -5
- package/node_modules/@maka/runtime-host/dist/operator/index.js +2 -1
- package/node_modules/@maka/runtime-host/dist/operator/retirement-cancellation.js +20 -0
- package/node_modules/@maka/runtime-host/dist/protocol/host-status.js +21 -12
- package/node_modules/@maka/runtime-host/dist/protocol/index.js +25 -1
- package/node_modules/@maka/runtime-host/dist/protocol/operations.js +1 -0
- package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +40 -20
- package/node_modules/@maka/runtime-host/dist/protocol/session-transcript.js +0 -5
- package/node_modules/@maka/runtime-host/dist/protocol/session-turns.js +29 -58
- package/node_modules/@maka/runtime-host/dist/protocol/turn.js +11 -1
- package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +1 -0
- package/node_modules/@maka/runtime-host/dist/server/canonical-turn-snapshot.js +19 -4
- package/node_modules/@maka/runtime-host/dist/server/connection-session.js +7 -6
- package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +100 -24
- package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +5 -5
- package/node_modules/@maka/runtime-host/dist/server/execution-model-composition.js +3 -2
- package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +40 -1
- package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +96 -17
- package/node_modules/@maka/runtime-host/dist/server/host-residency-registry.js +13 -7
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-admission-registry.js +3 -1
- package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +124 -62
- package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +76 -6
- package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +325 -73
- package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +37 -2
- package/node_modules/@maka/runtime-host/dist/server/serial-outbound-writer.js +45 -3
- package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +50 -7
- package/node_modules/@maka/runtime-host/dist/server/session-continuity-coordinator.js +87 -25
- package/node_modules/@maka/runtime-host/dist/server/session-revision-coordinator.js +8 -55
- package/node_modules/@maka/runtime-host/dist/server/session-sidecar-purge.js +1 -16
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-pager.js +0 -1
- package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +460 -70
- package/node_modules/@maka/runtime-host/dist/server/shared-session-transcript.js +1 -1
- package/node_modules/@maka/runtime-host/dist/server/storage-maintenance.js +109 -0
- package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +0 -1
- package/node_modules/@maka/storage/dist/artifact-store.js +21 -10
- package/node_modules/@maka/storage/dist/artifact-stores.js +1 -1
- package/node_modules/@maka/storage/dist/claude-code-session-adapter.js +0 -4
- package/node_modules/@maka/storage/dist/codex-session-adapter.js +0 -2
- package/node_modules/@maka/storage/dist/context-offload-snapshot.js +325 -0
- package/node_modules/@maka/storage/dist/execution-stores.js +9 -7
- package/node_modules/@maka/storage/dist/file-session-repository.js +1012 -0
- package/node_modules/@maka/storage/dist/opencode-session-adapter.js +0 -4
- package/node_modules/@maka/storage/dist/operational-state-backup.js +9 -5
- package/node_modules/@maka/storage/dist/runtime-event-persistence.js +1 -0
- package/node_modules/@maka/storage/dist/runtime-transcript-query.js +308 -0
- package/node_modules/@maka/storage/dist/session-bundle-policy.js +24 -4
- package/node_modules/@maka/storage/dist/session-message-projection.js +15 -0
- package/node_modules/@maka/storage/dist/session-repository.js +767 -0
- package/node_modules/@maka/storage/dist/session-store.js +25 -99
- package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +10 -3
- package/node_modules/@maka/storage/dist/sqlite-context-offload-store.js +24 -8
- package/node_modules/@maka/storage/dist/sqlite-core-execution-schema.js +7 -1
- package/node_modules/@maka/storage/dist/sqlite-runtime-schema.js +44 -1
- package/node_modules/@maka/storage/dist/sqlite-runtime-store.js +129 -25
- package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +109 -892
- package/node_modules/@maka/storage/dist/tool-result-archive-evidence.js +111 -0
- package/node_modules/@maka/storage/package.json +2 -0
- package/package.json +1 -1
|
@@ -20,7 +20,8 @@ import { createHash, randomUUID } from 'node:crypto';
|
|
|
20
20
|
import { realpath, stat } from 'node:fs/promises';
|
|
21
21
|
import { homedir } from 'node:os';
|
|
22
22
|
import { dirname, join, posix, resolve, win32 } from 'node:path';
|
|
23
|
-
import {
|
|
23
|
+
import { isDeepStrictEqual } from 'node:util';
|
|
24
|
+
import { claimLocalHostProcessDeployment, handoffLocalHostProcessDeployment, readLocalHostDeploymentRecord, resolveRuntimeHostManagedDeploymentAuthority, } from '@maka/runtime-host/operator';
|
|
24
25
|
import { compareProductReleaseVersions } from '@maka/runtime-host/operator/update-package-evidence';
|
|
25
26
|
import { connectExistingRuntimeHost } from '@maka/runtime-host/client';
|
|
26
27
|
import { INTERACTIVE_RUNTIME_HOST_COMPOSITION_ID, RUNTIME_HOST_PROTOCOL_VERSION, } from '@maka/runtime-host/protocol';
|
|
@@ -53,6 +54,7 @@ export class RuntimeHostLocalHandoffError extends Error {
|
|
|
53
54
|
* retain only the bounded true-idle takeover adapter.
|
|
54
55
|
*/
|
|
55
56
|
export async function restartRuntimeHostNpmGlobalDeployment(input, authorityOptions = {}, overrides = {}) {
|
|
57
|
+
input.signal?.throwIfAborted();
|
|
56
58
|
if (input.registration.lifecycleMode !== 'ephemeral') {
|
|
57
59
|
return {
|
|
58
60
|
kind: 'operator_required',
|
|
@@ -64,6 +66,7 @@ export async function restartRuntimeHostNpmGlobalDeployment(input, authorityOpti
|
|
|
64
66
|
withPackage: withRuntimeHostRegistryUpdatePackage,
|
|
65
67
|
prepareDeployment: prepareRuntimeHostPackageDeployment,
|
|
66
68
|
readRecord: readLocalHostDeploymentRecord,
|
|
69
|
+
resolveManagedAuthority: resolveRuntimeHostManagedDeploymentAuthority,
|
|
67
70
|
claim: claimLocalHostProcessDeployment,
|
|
68
71
|
handoff: handoffLocalHostProcessDeployment,
|
|
69
72
|
resolveCandidate: resolveRuntimeHostRegistryUpdateCandidate,
|
|
@@ -72,7 +75,13 @@ export async function restartRuntimeHostNpmGlobalDeployment(input, authorityOpti
|
|
|
72
75
|
retireSource: launchRuntimeHostLocalSourceRetirement,
|
|
73
76
|
...overrides,
|
|
74
77
|
};
|
|
78
|
+
if (await deps.resolveManagedAuthority(input.registration.rootId)) {
|
|
79
|
+
return { kind: 'operator_required', reason: 'unowned_host' };
|
|
80
|
+
}
|
|
75
81
|
const installation = await deps.resolveInstallation(input.installationOptions);
|
|
82
|
+
if (input.expectedInstallation && !isDeepStrictEqual(installation, input.expectedInstallation)) {
|
|
83
|
+
return { kind: 'changed' };
|
|
84
|
+
}
|
|
76
85
|
const current = await deps.readRecord(input.registration.rootId, authorityOptions);
|
|
77
86
|
const sourceOwner = current?.state.kind === 'handoff' ? current.state.from : current?.state.owner;
|
|
78
87
|
if (sourceOwner && !sameOwner(sourceOwner, installation.owner)) {
|
|
@@ -130,9 +139,13 @@ export async function restartRuntimeHostNpmGlobalDeployment(input, authorityOpti
|
|
|
130
139
|
return 'target_present';
|
|
131
140
|
};
|
|
132
141
|
const prepare = async (rootId, selectedDeployment, stagedTarget, inheritableAuthorityLeaseFd) => {
|
|
142
|
+
input.signal?.throwIfAborted();
|
|
133
143
|
if (rootId !== input.registration.rootId) {
|
|
134
144
|
throw new RuntimeHostLocalHandoffError('root_changed', 'The local Runtime Host State Root changed before restart');
|
|
135
145
|
}
|
|
146
|
+
if (await deps.resolveManagedAuthority(rootId)) {
|
|
147
|
+
throw new Error('The managed Runtime Host requires its operator to perform replacement');
|
|
148
|
+
}
|
|
136
149
|
const observed = await deps.connectExisting({
|
|
137
150
|
rootPath: input.rootPath,
|
|
138
151
|
protocol: { min: RUNTIME_HOST_PROTOCOL_VERSION, max: RUNTIME_HOST_PROTOCOL_VERSION },
|
|
@@ -179,6 +192,7 @@ export async function restartRuntimeHostNpmGlobalDeployment(input, authorityOpti
|
|
|
179
192
|
expectedHostEpoch: registration.hostEpoch,
|
|
180
193
|
activeWorkPolicy,
|
|
181
194
|
inheritableAuthorityLeaseFd,
|
|
195
|
+
...(input.signal ? { signal: input.signal } : {}),
|
|
182
196
|
});
|
|
183
197
|
if (retired === 'active_work')
|
|
184
198
|
return { kind: 'active_work' };
|
|
@@ -286,6 +300,12 @@ export async function reconcilePreparedRuntimeHostNpmGlobalDeployment(request, l
|
|
|
286
300
|
return request.staged;
|
|
287
301
|
};
|
|
288
302
|
const current = await overrides.readRecord(request.rootId, authorityOptions);
|
|
303
|
+
if (request.expectedOwner &&
|
|
304
|
+
(current?.state.kind !== 'owned' ||
|
|
305
|
+
current.revision !== request.expectedOwner.revision ||
|
|
306
|
+
!sameOwner(current.state.owner, request.expectedOwner.owner))) {
|
|
307
|
+
throw new RuntimeHostLocalHandoffError('source_owner_mismatch', 'The observed local Host owner changed before update reconciliation');
|
|
308
|
+
}
|
|
289
309
|
if (!current) {
|
|
290
310
|
return overrides.claim({
|
|
291
311
|
rootId: request.rootId,
|
|
@@ -297,9 +317,12 @@ export async function reconcilePreparedRuntimeHostNpmGlobalDeployment(request, l
|
|
|
297
317
|
}
|
|
298
318
|
return overrides.handoff({
|
|
299
319
|
rootId: request.rootId,
|
|
300
|
-
|
|
320
|
+
// The same captured CAS token is checked again by begin_handoff under
|
|
321
|
+
// the authority lock. Never substitute a newly observed owner revision.
|
|
322
|
+
expectedRevision: request.expectedOwner?.revision ?? current.revision,
|
|
301
323
|
transactionId: current.state.kind === 'handoff' ? current.state.transactionId : request.transactionId,
|
|
302
|
-
from:
|
|
324
|
+
from: request.expectedOwner?.owner ??
|
|
325
|
+
(current.state.kind === 'handoff' ? current.state.from : current.state.owner),
|
|
303
326
|
to: request.installation.owner,
|
|
304
327
|
target: request.target,
|
|
305
328
|
activeWorkPolicy: request.activeWorkPolicy,
|
|
@@ -331,7 +354,7 @@ export async function prepareRuntimeHostNpmGlobalStagedDeployment(input, pathOpt
|
|
|
331
354
|
launchGeneration: launchGeneration(input.transactionId, input.target),
|
|
332
355
|
};
|
|
333
356
|
}
|
|
334
|
-
async function openRuntimeHostNpmGlobalStagedDeployment(input, pathOptions = {}) {
|
|
357
|
+
export async function openRuntimeHostNpmGlobalStagedDeployment(input, pathOptions = {}) {
|
|
335
358
|
const deploymentRoot = resolveRuntimeHostLocalCliDeploymentRoot(input.rootId, input.owner, pathOptions);
|
|
336
359
|
const staged = await openRuntimeHostPackageDeployment({
|
|
337
360
|
deploymentRoot,
|
|
@@ -379,6 +402,16 @@ async function requireCandidateEntrypoint(packageRoot) {
|
|
|
379
402
|
}
|
|
380
403
|
return candidate;
|
|
381
404
|
}
|
|
405
|
+
/** Read-only capability check against the same verified source package the transaction uses. */
|
|
406
|
+
export async function runtimeHostNpmGlobalSourceRetirementAvailable(input) {
|
|
407
|
+
const source = await openRuntimeHostNpmGlobalStagedDeployment({
|
|
408
|
+
rootId: input.rootId,
|
|
409
|
+
owner: input.owner,
|
|
410
|
+
target: input.source,
|
|
411
|
+
transactionId: 'source-capability-probe',
|
|
412
|
+
}, input.deploymentPathOptions);
|
|
413
|
+
return hasSourceRetirementHelper(source);
|
|
414
|
+
}
|
|
382
415
|
async function hasSourceRetirementHelper(source) {
|
|
383
416
|
try {
|
|
384
417
|
return (await stat(join(dirname(source.cliPath), 'runtime-host-local-source-retirement.js'))).isFile();
|
|
@@ -0,0 +1,84 @@
|
|
|
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 { execFile } from 'node:child_process';
|
|
20
|
+
import { access } from 'node:fs/promises';
|
|
21
|
+
import { join } from 'node:path';
|
|
22
|
+
import { promisify } from 'node:util';
|
|
23
|
+
import { resolveStorageRoot } from '@maka/storage/root-authority';
|
|
24
|
+
import { runtimeHostStartupError } from '@maka/runtime-host/client';
|
|
25
|
+
import { decodeRuntimeHostActivationFrame, readRuntimeHostManagedDeploymentConfig, RUNTIME_HOST_ACTIVATION_FRAME_MAX_BYTES, runtimeHostManagedOperatorCommand, runtimeHostOperatorInvocation, } from '@maka/runtime-host/operator';
|
|
26
|
+
const run = promisify(execFile);
|
|
27
|
+
/** Invoke the installed owner; never launch the calling CLI's candidate as the owner. */
|
|
28
|
+
export async function activateLocalManagedRuntimeHost(input) {
|
|
29
|
+
input.signal?.throwIfAborted();
|
|
30
|
+
const capability = await resolveStorageRoot({ path: input.rootPath, kind: 'interactive' });
|
|
31
|
+
const config = await readRuntimeHostManagedDeploymentConfig(capability);
|
|
32
|
+
if (!config || config.lifecycle.mode !== 'on_demand') {
|
|
33
|
+
throw runtimeHostStartupError('managed_root_requires_operator');
|
|
34
|
+
}
|
|
35
|
+
const operator = runtimeHostManagedOperatorCommand(config, process.platform === 'win32' ? 'win32' : 'posix');
|
|
36
|
+
let invocation = runtimeHostOperatorInvocation(operator, [
|
|
37
|
+
'activate',
|
|
38
|
+
'--framed',
|
|
39
|
+
'--root-id',
|
|
40
|
+
capability.rootId,
|
|
41
|
+
]);
|
|
42
|
+
// Older installations have only the stable POSIX operator executable.
|
|
43
|
+
try {
|
|
44
|
+
await access(operator.modulePath);
|
|
45
|
+
}
|
|
46
|
+
catch (error) {
|
|
47
|
+
if (error.code !== 'ENOENT' || process.platform === 'win32') {
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
invocation = {
|
|
51
|
+
executable: join(config.deploymentRoot, 'operator'),
|
|
52
|
+
args: invocation.args.slice(1),
|
|
53
|
+
};
|
|
54
|
+
}
|
|
55
|
+
const result = await run(invocation.executable, [...invocation.args], {
|
|
56
|
+
encoding: 'utf8',
|
|
57
|
+
timeout: 120_000,
|
|
58
|
+
killSignal: 'SIGKILL',
|
|
59
|
+
maxBuffer: RUNTIME_HOST_ACTIVATION_FRAME_MAX_BYTES + 256,
|
|
60
|
+
...(input.signal ? { signal: input.signal } : {}),
|
|
61
|
+
}).catch((error) => {
|
|
62
|
+
input.signal?.throwIfAborted();
|
|
63
|
+
// Operators report a framed diagnostic and exit nonzero on expected failures.
|
|
64
|
+
if (error instanceof Error && 'stdout' in error && typeof error.stdout === 'string') {
|
|
65
|
+
const frame = decodeSingleActivationFrame(error.stdout);
|
|
66
|
+
if (frame?.kind === 'error')
|
|
67
|
+
throw new Error(frame.error.message, { cause: error });
|
|
68
|
+
}
|
|
69
|
+
throw error;
|
|
70
|
+
});
|
|
71
|
+
input.signal?.throwIfAborted();
|
|
72
|
+
const frame = decodeSingleActivationFrame(result.stdout);
|
|
73
|
+
if (!frame)
|
|
74
|
+
throw new Error('The installed Runtime Host operator returned an invalid activation result');
|
|
75
|
+
if (frame.kind === 'error')
|
|
76
|
+
throw new Error(frame.error.message);
|
|
77
|
+
if (frame.rootId !== capability.rootId || frame.deploymentId !== config.deploymentId) {
|
|
78
|
+
throw new Error('The installed Runtime Host operator activated a different deployment');
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function decodeSingleActivationFrame(stdout) {
|
|
82
|
+
const line = stdout.replace(/\r?\n$/u, '');
|
|
83
|
+
return /[\r\n]/u.test(line) ? undefined : decodeRuntimeHostActivationFrame(line);
|
|
84
|
+
}
|
|
@@ -58,6 +58,7 @@ export async function runRuntimeHostLocalSourceRetirement(input, overrides = {})
|
|
|
58
58
|
}
|
|
59
59
|
}
|
|
60
60
|
export function launchRuntimeHostLocalSourceRetirement(input) {
|
|
61
|
+
input.signal?.throwIfAborted();
|
|
61
62
|
const args = [
|
|
62
63
|
input.sourceCliPath,
|
|
63
64
|
'runtime-host',
|
|
@@ -81,9 +82,24 @@ export function launchRuntimeHostLocalSourceRetirement(input) {
|
|
|
81
82
|
killSignal: 'SIGKILL',
|
|
82
83
|
windowsHide: false,
|
|
83
84
|
});
|
|
84
|
-
|
|
85
|
+
// Only this retirement client is stopped, never the parent that owns the
|
|
86
|
+
// durable deployment transaction. Wait for its fd/connection to close.
|
|
87
|
+
const cancel = () => {
|
|
88
|
+
child.kill('SIGKILL');
|
|
89
|
+
};
|
|
90
|
+
input.signal?.addEventListener('abort', cancel, { once: true });
|
|
91
|
+
if (input.signal?.aborted)
|
|
92
|
+
cancel();
|
|
93
|
+
let spawnError;
|
|
94
|
+
child.once('error', (error) => {
|
|
95
|
+
spawnError = error;
|
|
96
|
+
});
|
|
85
97
|
child.once('close', (code, signal) => {
|
|
86
|
-
|
|
98
|
+
input.signal?.removeEventListener('abort', cancel);
|
|
99
|
+
if (spawnError) {
|
|
100
|
+
reject(spawnError);
|
|
101
|
+
}
|
|
102
|
+
else if (signal) {
|
|
87
103
|
reject(new Error(`Maka source retirement helper exited on ${signal}`));
|
|
88
104
|
}
|
|
89
105
|
else if (code === 0) {
|
|
@@ -77,6 +77,7 @@ export async function manageRuntimeHostManagedLifecycle(rootId, input, dependenc
|
|
|
77
77
|
}
|
|
78
78
|
if (input.action === 'restart') {
|
|
79
79
|
const retirement = await retireRuntimeHostLifecycleOwner({
|
|
80
|
+
...(input.expectedHost ? { expectedOwner: input.expectedHost } : {}),
|
|
80
81
|
rootPath: config.root.path,
|
|
81
82
|
rootId,
|
|
82
83
|
supervisor: provider.supervisor,
|
|
@@ -0,0 +1,50 @@
|
|
|
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 { RUNTIME_HOST_OPERATOR_RETIREMENT_CANCELLATION_ENV } from '@maka/runtime-host/operator';
|
|
20
|
+
/** Consume the operator's one-shot cancellation channel only around reversible retirement. */
|
|
21
|
+
export function withOperatorRetirementCancellation(operation) {
|
|
22
|
+
if (process.env[RUNTIME_HOST_OPERATOR_RETIREMENT_CANCELLATION_ENV] !== '1') {
|
|
23
|
+
return operation(undefined);
|
|
24
|
+
}
|
|
25
|
+
// Recovery may retire another owner later. It must finish independently of
|
|
26
|
+
// the original user's cancellation, and activators must not inherit this channel.
|
|
27
|
+
delete process.env[RUNTIME_HOST_OPERATOR_RETIREMENT_CANCELLATION_ENV];
|
|
28
|
+
return withRetirementCancellation(process.stdin, operation);
|
|
29
|
+
}
|
|
30
|
+
export async function withRetirementCancellation(input, operation) {
|
|
31
|
+
const controller = new AbortController();
|
|
32
|
+
const cancel = () => controller.abort(new Error('Runtime Host retirement was cancelled'));
|
|
33
|
+
input.once('end', cancel);
|
|
34
|
+
input.once('error', cancel);
|
|
35
|
+
input.once('close', cancel);
|
|
36
|
+
input.resume();
|
|
37
|
+
if (input.readableEnded || input.destroyed)
|
|
38
|
+
cancel();
|
|
39
|
+
try {
|
|
40
|
+
return await operation(controller.signal);
|
|
41
|
+
}
|
|
42
|
+
finally {
|
|
43
|
+
input.removeListener('end', cancel);
|
|
44
|
+
input.removeListener('error', cancel);
|
|
45
|
+
input.removeListener('close', cancel);
|
|
46
|
+
input.pause();
|
|
47
|
+
// A parent's still-open cancellation pipe must not keep the completed CLI alive.
|
|
48
|
+
input.unref?.();
|
|
49
|
+
}
|
|
50
|
+
}
|
|
@@ -59,6 +59,9 @@ function runtimeHostServiceManagerDeps(overrides) {
|
|
|
59
59
|
};
|
|
60
60
|
}
|
|
61
61
|
export async function manageRuntimeHostService(input, backend, overrides = {}) {
|
|
62
|
+
if (input.expectedHost) {
|
|
63
|
+
throw new RuntimeHostServiceManagerError('target_mismatch', 'An exact Host fence requires the canonical managed deployment operator');
|
|
64
|
+
}
|
|
62
65
|
const deps = runtimeHostServiceManagerDeps(overrides);
|
|
63
66
|
const configPath = resolveRuntimeHostManagedServiceConfigPath(input.clientDataRoot);
|
|
64
67
|
const configDirectory = dirname(configPath);
|
|
@@ -30,6 +30,7 @@ import { isRuntimeHostTerminalTurn as isTerminalTurn } from '@maka/runtime-host/
|
|
|
30
30
|
import { projectSessionCatalogSummary, readRuntimeHostResources, readRuntimeHostSessions, RuntimeHostOperationError, RuntimeHostRequestInterruptedError, } from '@maka/runtime-host/client';
|
|
31
31
|
import { SESSION_TRANSCRIPT_BOOTSTRAP_MAX_BYTES, } from '@maka/runtime-host/protocol';
|
|
32
32
|
import { RuntimeHostSessionChannel } from './runtime-host-session-channel.js';
|
|
33
|
+
import { getRuntimeHostSession, requireRuntimeHostSessionProjection as requireSession, updateRuntimeHostSession, } from './runtime-host-session-update.js';
|
|
33
34
|
import { inspectSessionResumeAvailability, skillInvocationBlockedMessage, } from './session-driver.js';
|
|
34
35
|
import { cwdRank, firstLine, inspectGitCwdChanges, resolveMoveCwd, } from './session-driver-policy.js';
|
|
35
36
|
const decodeStoredMessage = (value) => decodePersistedStoredMessage(markPersisted(value));
|
|
@@ -502,7 +503,7 @@ class RuntimeHostMakaSessionDriverImpl {
|
|
|
502
503
|
sessionId,
|
|
503
504
|
expectedRevision: current.revision,
|
|
504
505
|
patch: { name },
|
|
505
|
-
}));
|
|
506
|
+
}), { operation: 'session.metadata.update' });
|
|
506
507
|
return session.name;
|
|
507
508
|
}
|
|
508
509
|
async #moveSession(rawCwd) {
|
|
@@ -602,7 +603,7 @@ class RuntimeHostMakaSessionDriverImpl {
|
|
|
602
603
|
sessionId,
|
|
603
604
|
expectedRevision: current.revision,
|
|
604
605
|
workspace: { kind: 'host_path', path: cwd },
|
|
605
|
-
}));
|
|
606
|
+
}), { operation: 'session.workspace.relocate' });
|
|
606
607
|
}
|
|
607
608
|
async listRewindTargets() {
|
|
608
609
|
if (!this.#sessionId)
|
|
@@ -1073,7 +1074,7 @@ class RuntimeHostMakaSessionDriverImpl {
|
|
|
1073
1074
|
sessionId,
|
|
1074
1075
|
expectedRevision: current.revision,
|
|
1075
1076
|
patch,
|
|
1076
|
-
}));
|
|
1077
|
+
}), { operation: 'session.configuration.update' });
|
|
1077
1078
|
}
|
|
1078
1079
|
#adoptConfiguration(session) {
|
|
1079
1080
|
this.#model = session.model;
|
|
@@ -1369,12 +1370,6 @@ function workspaceTargetForCreate(current, input, location) {
|
|
|
1369
1370
|
}
|
|
1370
1371
|
return current.target;
|
|
1371
1372
|
}
|
|
1372
|
-
async function getRuntimeHostSession(connection, sessionId) {
|
|
1373
|
-
const result = await connection.request('session.catalog.query', { kind: 'get', sessionId });
|
|
1374
|
-
if (result.kind !== 'session')
|
|
1375
|
-
throw new Error('Runtime Host returned an invalid Session lookup');
|
|
1376
|
-
return result.session === null ? null : requireSession(result.session);
|
|
1377
|
-
}
|
|
1378
1373
|
function representableSession(item) {
|
|
1379
1374
|
return 'kind' in item ? [] : [item];
|
|
1380
1375
|
}
|
|
@@ -1426,11 +1421,6 @@ function visibleTranscriptMessages(messages, hiddenThroughTurnId) {
|
|
|
1426
1421
|
}
|
|
1427
1422
|
return boundary < 0 ? messages : messages.slice(boundary + 1);
|
|
1428
1423
|
}
|
|
1429
|
-
function requireSession(item) {
|
|
1430
|
-
if (!('kind' in item))
|
|
1431
|
-
return item;
|
|
1432
|
-
throw new Error(`Runtime Host Session is not representable by this CLI: ${item.id}`);
|
|
1433
|
-
}
|
|
1434
1424
|
function inspectRuntimeHostSessionResumeAvailability(summary, location) {
|
|
1435
1425
|
if (!summary.cwd) {
|
|
1436
1426
|
return Promise.resolve({ available: false, reason: 'Missing working directory' });
|
|
@@ -1445,17 +1435,6 @@ async function assertSessionResumeAvailable(summary, location) {
|
|
|
1445
1435
|
throw new Error(summary.cwd ? `Session cwd no longer exists: ${summary.cwd}` : availability.reason);
|
|
1446
1436
|
}
|
|
1447
1437
|
}
|
|
1448
|
-
async function updateRuntimeHostSession(connection, sessionId, update) {
|
|
1449
|
-
for (let attempt = 0; attempt < MAX_CATALOG_ATTEMPTS; attempt += 1) {
|
|
1450
|
-
const current = await getRuntimeHostSession(connection, sessionId);
|
|
1451
|
-
if (!current)
|
|
1452
|
-
throw new Error(`Session not found: ${sessionId}`);
|
|
1453
|
-
const result = await update(current);
|
|
1454
|
-
if (result.kind === 'committed')
|
|
1455
|
-
return requireSession(result.session);
|
|
1456
|
-
}
|
|
1457
|
-
throw new Error(`Session kept changing while updating: ${sessionId}`);
|
|
1458
|
-
}
|
|
1459
1438
|
async function loadCurrentMessages(connection, sessionId) {
|
|
1460
1439
|
const subscription = await connection.openSessionSubscription({
|
|
1461
1440
|
sessionId,
|
|
@@ -0,0 +1,62 @@
|
|
|
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
|
+
const MAX_UPDATE_ATTEMPTS = 3;
|
|
20
|
+
export class RuntimeHostSessionUpdateError extends Error {
|
|
21
|
+
operation;
|
|
22
|
+
reason;
|
|
23
|
+
sessionId;
|
|
24
|
+
attempts;
|
|
25
|
+
name = 'RuntimeHostSessionUpdateError';
|
|
26
|
+
constructor(operation, reason, sessionId, attempts) {
|
|
27
|
+
super(`Runtime Host Session update failed for session ${sessionId}: ${reason}`);
|
|
28
|
+
this.operation = operation;
|
|
29
|
+
this.reason = reason;
|
|
30
|
+
this.sessionId = sessionId;
|
|
31
|
+
this.attempts = attempts;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
export async function getRuntimeHostSession(connection, sessionId) {
|
|
35
|
+
const result = await connection.request('session.catalog.query', { kind: 'get', sessionId });
|
|
36
|
+
if (result.kind !== 'session') {
|
|
37
|
+
throw new RuntimeHostSessionUpdateError('session.catalog.query', 'invalid_projection', sessionId);
|
|
38
|
+
}
|
|
39
|
+
return result.session === null
|
|
40
|
+
? null
|
|
41
|
+
: requireRuntimeHostSessionProjection(result.session, 'session.catalog.query');
|
|
42
|
+
}
|
|
43
|
+
export async function updateRuntimeHostSession(connection, sessionId, update, options) {
|
|
44
|
+
for (let attempt = 0; attempt < MAX_UPDATE_ATTEMPTS; attempt += 1) {
|
|
45
|
+
options.assertRequestAllowed?.();
|
|
46
|
+
const current = await getRuntimeHostSession(connection, sessionId);
|
|
47
|
+
if (!current) {
|
|
48
|
+
throw new RuntimeHostSessionUpdateError('session.catalog.query', 'not_found', sessionId);
|
|
49
|
+
}
|
|
50
|
+
options.assertRequestAllowed?.();
|
|
51
|
+
const result = await update(current);
|
|
52
|
+
if (result.kind === 'committed') {
|
|
53
|
+
return requireRuntimeHostSessionProjection(result.session, options.operation);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
throw new RuntimeHostSessionUpdateError(options.operation, 'revision_conflict', sessionId, MAX_UPDATE_ATTEMPTS);
|
|
57
|
+
}
|
|
58
|
+
export function requireRuntimeHostSessionProjection(session, operation = 'session.catalog.query') {
|
|
59
|
+
if (!('kind' in session))
|
|
60
|
+
return session;
|
|
61
|
+
throw new RuntimeHostSessionUpdateError(operation, 'unsupported_session_projection', session.id);
|
|
62
|
+
}
|
|
@@ -17,21 +17,23 @@
|
|
|
17
17
|
* under the License.
|
|
18
18
|
*/
|
|
19
19
|
import { parseNoRealConnectionError } from '@maka/core/connection-error-copy';
|
|
20
|
-
import { createInterface } from 'node:readline/promises';
|
|
21
20
|
import { SessionActivityRegistry } from '@maka/runtime/goal-turn-lifecycle';
|
|
22
|
-
import { readRuntimeHostConnectionCatalog } from '@maka/runtime-host/client';
|
|
21
|
+
import { readRuntimeHostConnectionCatalog, HostHandoffCancelledError, } from '@maka/runtime-host/client';
|
|
23
22
|
import { runtimeHostProfileUsesHostWorkspace } from '@maka/runtime-host/profile-kind';
|
|
24
23
|
import { createForeignSessionStore } from '@maka/storage/foreign-session-store';
|
|
25
24
|
import { formatMakaResumeHint } from './cli-invocation.js';
|
|
26
|
-
import { connectRuntimeHostCli
|
|
27
|
-
import {
|
|
28
|
-
import { restartRuntimeHostNpmGlobalDeployment } from './runtime-host-local-handoff.js';
|
|
25
|
+
import { connectRuntimeHostCli } from './runtime-host-cli-context.js';
|
|
26
|
+
import { createCliHostHandoffSurface } from './runtime-host-handoff-surface.js';
|
|
29
27
|
import { createRuntimeHostOnboardingSurface } from './runtime-host-onboarding.js';
|
|
30
28
|
import { runMakaPiTui } from './pi-tui-runner.js';
|
|
31
29
|
import { createRuntimeHostTuiContext } from './runtime-host-tui-context.js';
|
|
30
|
+
import { describeTuiHost, prepareTuiHostOwnerAction } from './runtime-host-tui-owner.js';
|
|
31
|
+
import { getTuiHostOwnerCopy } from './tui-host-owner-copy.js';
|
|
32
32
|
export async function runRuntimeHostTui(input) {
|
|
33
|
+
const ownerCopy = getTuiHostOwnerCopy(input.locale);
|
|
33
34
|
const foreignSessions = createForeignSessionStore();
|
|
34
35
|
const contextInput = {
|
|
36
|
+
...(process.stdin.isTTY ? { handoffSurface: createCliHostHandoffSurface(input.locale) } : {}),
|
|
35
37
|
clientDataRoot: input.clientDataRoot,
|
|
36
38
|
rootPath: input.workspaceRoot,
|
|
37
39
|
cwd: input.cwd,
|
|
@@ -41,11 +43,11 @@ export async function runRuntimeHostTui(input) {
|
|
|
41
43
|
};
|
|
42
44
|
let context;
|
|
43
45
|
try {
|
|
44
|
-
context = await
|
|
45
|
-
if (!context)
|
|
46
|
-
return 1;
|
|
46
|
+
context = await createRuntimeHostTuiContext(contextInput);
|
|
47
47
|
}
|
|
48
48
|
catch (error) {
|
|
49
|
+
if (error instanceof HostHandoffCancelledError)
|
|
50
|
+
return 1;
|
|
49
51
|
if (!isMissingDefaultConnection(error) || input.resumeSessionId)
|
|
50
52
|
throw error;
|
|
51
53
|
const configured = await runFirstRunOnboarding(input.clientDataRoot, input.workspaceRoot, input.cwd, input.locale, input.hostProfileId);
|
|
@@ -53,6 +55,8 @@ export async function runRuntimeHostTui(input) {
|
|
|
53
55
|
throw error;
|
|
54
56
|
context = await createRuntimeHostTuiContext(contextInput);
|
|
55
57
|
}
|
|
58
|
+
let ownerAction;
|
|
59
|
+
let contextOpen = true;
|
|
56
60
|
try {
|
|
57
61
|
await runMakaPiTui({
|
|
58
62
|
driver: context.driver,
|
|
@@ -79,6 +83,27 @@ export async function runRuntimeHostTui(input) {
|
|
|
79
83
|
onboarding: context.onboarding,
|
|
80
84
|
...(context.mcp ? { mcp: context.mcp } : {}),
|
|
81
85
|
recap: context.recap,
|
|
86
|
+
hostControl: {
|
|
87
|
+
status: () => describeTuiHost({ ...context, locale: input.locale }),
|
|
88
|
+
prepare: async (action, confirm) => {
|
|
89
|
+
if (ownerAction || !contextOpen)
|
|
90
|
+
throw new Error(ownerCopy.pending);
|
|
91
|
+
const execute = await prepareTuiHostOwnerAction({
|
|
92
|
+
profile: context.profile,
|
|
93
|
+
connection: context.connection,
|
|
94
|
+
rootPath: input.workspaceRoot,
|
|
95
|
+
locale: input.locale,
|
|
96
|
+
action,
|
|
97
|
+
confirm,
|
|
98
|
+
});
|
|
99
|
+
if (!execute)
|
|
100
|
+
return false;
|
|
101
|
+
if (!contextOpen || ownerAction)
|
|
102
|
+
throw new Error(ownerCopy.disconnected);
|
|
103
|
+
ownerAction = execute;
|
|
104
|
+
return true;
|
|
105
|
+
},
|
|
106
|
+
},
|
|
82
107
|
...(runtimeHostProfileUsesHostWorkspace(context.profile.kind)
|
|
83
108
|
? {
|
|
84
109
|
sessionListScope: 'all',
|
|
@@ -103,82 +128,22 @@ export async function runRuntimeHostTui(input) {
|
|
|
103
128
|
});
|
|
104
129
|
if (hint)
|
|
105
130
|
process.stdout.write(`${hint}\n`);
|
|
106
|
-
return 0;
|
|
107
131
|
}
|
|
108
132
|
finally {
|
|
133
|
+
contextOpen = false;
|
|
109
134
|
await context.driver.cleanupOwnedSideConversations().catch(() => undefined);
|
|
110
135
|
await context.close();
|
|
111
136
|
}
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
while (true) {
|
|
116
|
-
try {
|
|
117
|
-
return await createRuntimeHostTuiContext(input);
|
|
118
|
-
}
|
|
119
|
-
catch (error) {
|
|
120
|
-
if (!(error instanceof RuntimeHostCliConflictError) || !process.stdin.isTTY)
|
|
121
|
-
throw error;
|
|
122
|
-
process.stderr.write(`${error.message}\n`);
|
|
123
|
-
const canRestart = error.registration.lifecycleMode === 'ephemeral' &&
|
|
124
|
-
!blockedRestartEpochs.has(error.registration.hostEpoch) &&
|
|
125
|
-
(await isPersistentNpmGlobalCli());
|
|
126
|
-
const readline = createInterface({ input: process.stdin, output: process.stderr });
|
|
127
|
-
let decision;
|
|
128
|
-
try {
|
|
129
|
-
const answer = await readline.question(canRestart
|
|
130
|
-
? 'Restart this local Host if it is idle, wait for it to exit, or cancel? [r/w/C] '
|
|
131
|
-
: 'Wait only if the existing Host is expected to exit, or cancel? [w/C] ');
|
|
132
|
-
decision = resolveRuntimeHostCliConflictDecision(answer, canRestart);
|
|
133
|
-
}
|
|
134
|
-
finally {
|
|
135
|
-
readline.close();
|
|
136
|
-
}
|
|
137
|
-
if (decision === 'cancel')
|
|
138
|
-
return null;
|
|
139
|
-
if (decision === 'restart') {
|
|
140
|
-
const result = await restartRuntimeHostNpmGlobalDeployment({
|
|
141
|
-
rootPath: input.rootPath,
|
|
142
|
-
registration: error.registration,
|
|
143
|
-
});
|
|
144
|
-
if (result.kind === 'completed')
|
|
145
|
-
continue;
|
|
146
|
-
if (result.kind === 'active_work') {
|
|
147
|
-
blockedRestartEpochs.add(error.registration.hostEpoch);
|
|
148
|
-
process.stderr.write('The existing Runtime Host still owns active or durable work and was not interrupted.\n');
|
|
149
|
-
continue;
|
|
150
|
-
}
|
|
151
|
-
if (result.kind === 'operator_required') {
|
|
152
|
-
blockedRestartEpochs.add(error.registration.hostEpoch);
|
|
153
|
-
continue;
|
|
154
|
-
}
|
|
155
|
-
if (result.kind === 'rejected')
|
|
156
|
-
continue;
|
|
157
|
-
throw new Error(`Local Runtime Host restart requires recovery at ${result.phase}`, {
|
|
158
|
-
cause: result.cause,
|
|
159
|
-
});
|
|
160
|
-
}
|
|
161
|
-
await waitForHostRetry();
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
async function isPersistentNpmGlobalCli() {
|
|
166
|
-
try {
|
|
167
|
-
await resolveRuntimeHostNpmGlobalInstallation();
|
|
168
|
-
return true;
|
|
169
|
-
}
|
|
170
|
-
catch {
|
|
171
|
-
return false;
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
function waitForHostRetry() {
|
|
175
|
-
return new Promise((resolve) => setTimeout(resolve, 2_000));
|
|
137
|
+
// No reconnecting client remains to respawn a stopped Host, and an updater
|
|
138
|
+
// can use the restored terminal without competing with the TUI renderer.
|
|
139
|
+
return ownerAction ? ownerAction() : 0;
|
|
176
140
|
}
|
|
177
141
|
async function runFirstRunOnboarding(clientDataRoot, rootPath, cwd, locale, hostProfileId) {
|
|
178
142
|
const connected = await connectRuntimeHostCli({
|
|
179
143
|
clientDataRoot,
|
|
180
144
|
rootPath,
|
|
181
145
|
interactiveSsh: true,
|
|
146
|
+
...(process.stdin.isTTY ? { handoffSurface: createCliHostHandoffSurface(locale) } : {}),
|
|
182
147
|
...(hostProfileId ? { profileId: hostProfileId } : {}),
|
|
183
148
|
});
|
|
184
149
|
try {
|
|
@@ -34,6 +34,7 @@ export async function createRuntimeHostTuiContext(input) {
|
|
|
34
34
|
clientDataRoot: input.clientDataRoot,
|
|
35
35
|
rootPath: input.rootPath,
|
|
36
36
|
interactiveSsh: true,
|
|
37
|
+
...(input.handoffSurface ? { handoffSurface: input.handoffSurface } : {}),
|
|
37
38
|
...(input.hostProfileId ? { profileId: input.hostProfileId } : {}),
|
|
38
39
|
});
|
|
39
40
|
const connection = connected.connection;
|