maka-agent 0.2.0-dev.25.20260908 → 0.2.0-dev.27.20260910

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 (110) hide show
  1. package/dist/cli-core.js +21 -0
  2. package/dist/mcp-capability-provider.js +4 -1
  3. package/dist/pi-tui-mcp-status.js +2 -2
  4. package/dist/pi-tui-pickers.js +19 -29
  5. package/dist/runtime-host-cli.js +43 -3
  6. package/dist/runtime-host-handoff-surface.js +10 -8
  7. package/dist/runtime-host-lifecycle-transaction.js +28 -3
  8. package/dist/runtime-host-local-source-retirement.js +1 -1
  9. package/dist/runtime-host-setup-command.js +29 -6
  10. package/dist/runtime-host-update-command.js +49 -5
  11. package/dist/session-export-command.js +72 -0
  12. package/dist/tui-autocomplete-layout.js +3 -1
  13. package/dist/tui-copy-catalog.js +6 -3
  14. package/dist/tui-editor-render.js +36 -0
  15. package/dist/tui-mcp-control.js +41 -6
  16. package/native/runtime-host-windows-task-launcher/prebuilds/win32-x64/maka-runtime-host-task-launcher.exe +0 -0
  17. package/node_modules/@maka/core/dist/runtime-event.js +3 -0
  18. package/node_modules/@maka/core/dist/session.js +29 -4
  19. package/node_modules/@maka/core/dist/usage-stats/types.js +2 -0
  20. package/node_modules/@maka/core/dist/workhub-action-result.js +68 -0
  21. package/node_modules/@maka/core/dist/workhub-routing.js +126 -0
  22. package/node_modules/@maka/core/package.json +3 -3
  23. package/node_modules/@maka/eval/dist/toolchain-verification.js +1 -1
  24. package/node_modules/@maka/mcp/dist/form-elicitation.js +213 -0
  25. package/node_modules/@maka/mcp/dist/index.js +337 -71
  26. package/node_modules/@maka/runtime/dist/agent-run.js +10 -1
  27. package/node_modules/@maka/runtime/dist/ai-sdk-turn.js +7 -3
  28. package/node_modules/@maka/runtime/dist/builtin-tools.js +21 -2
  29. package/node_modules/@maka/runtime/dist/goal-continuation.js +5 -1
  30. package/node_modules/@maka/runtime/dist/plugin-agent-service.js +131 -0
  31. package/node_modules/@maka/runtime/dist/plugin-approval-service.js +33 -0
  32. package/node_modules/@maka/runtime/dist/plugin-attachment-service.js +53 -0
  33. package/node_modules/@maka/runtime/dist/plugin-command-service.js +100 -0
  34. package/node_modules/@maka/runtime/dist/plugin-data-services.js +332 -0
  35. package/node_modules/@maka/runtime/dist/plugin-fs-service.js +62 -0
  36. package/node_modules/@maka/runtime/dist/plugin-goal-service.js +61 -0
  37. package/node_modules/@maka/runtime/dist/plugin-invocation-signal.js +24 -0
  38. package/node_modules/@maka/runtime/dist/plugin-llm-service.js +85 -0
  39. package/node_modules/@maka/runtime/dist/plugin-lsp-service.js +94 -0
  40. package/node_modules/@maka/runtime/dist/plugin-scope-registry.js +91 -0
  41. package/node_modules/@maka/runtime/dist/plugin-session-query-service.js +71 -0
  42. package/node_modules/@maka/runtime/dist/plugin-shell-env-service.js +87 -0
  43. package/node_modules/@maka/runtime/dist/plugin-shell-service.js +74 -0
  44. package/node_modules/@maka/runtime/dist/plugin-skill-service.js +93 -0
  45. package/node_modules/@maka/runtime/dist/plugin-system-prompt-service.js +346 -0
  46. package/node_modules/@maka/runtime/dist/plugin-tool-service.js +18 -52
  47. package/node_modules/@maka/runtime/dist/plugin-user-question-service.js +43 -0
  48. package/node_modules/@maka/runtime/dist/plugin-web-service.js +69 -0
  49. package/node_modules/@maka/runtime/dist/runtime-event-read-model.js +20 -1
  50. package/node_modules/@maka/runtime/dist/runtime-kernel.js +120 -13
  51. package/node_modules/@maka/runtime/dist/session-export.js +177 -0
  52. package/node_modules/@maka/runtime/dist/session-manager.js +20 -36
  53. package/node_modules/@maka/runtime/package.json +18 -0
  54. package/node_modules/@maka/runtime-host/dist/client/connection.js +30 -5
  55. package/node_modules/@maka/runtime-host/dist/client/host-handoff-copy.js +83 -18
  56. package/node_modules/@maka/runtime-host/dist/client/host-handoff.js +46 -12
  57. package/node_modules/@maka/runtime-host/dist/client/host-retirement.js +4 -2
  58. package/node_modules/@maka/runtime-host/dist/client/peer-client.js +18 -1
  59. package/node_modules/@maka/runtime-host/dist/operator/setup-frame.js +31 -21
  60. package/node_modules/@maka/runtime-host/dist/protocol/index.js +13 -1
  61. package/node_modules/@maka/runtime-host/dist/protocol/operations.js +3 -2
  62. package/node_modules/@maka/runtime-host/dist/protocol/plugin-platform.js +30 -5
  63. package/node_modules/@maka/runtime-host/dist/protocol/session-continuity.js +1 -0
  64. package/node_modules/@maka/runtime-host/dist/protocol/workhub-coordination.js +90 -188
  65. package/node_modules/@maka/runtime-host/dist/server/access-credential-store.js +4 -0
  66. package/node_modules/@maka/runtime-host/dist/server/canonical-session-projection.js +2 -1
  67. package/node_modules/@maka/runtime-host/dist/server/client-capability-coordinator.js +62 -4
  68. package/node_modules/@maka/runtime-host/dist/server/connection-session.js +28 -8
  69. package/node_modules/@maka/runtime-host/dist/server/daily-review-coordinator.js +112 -56
  70. package/node_modules/@maka/runtime-host/dist/server/execution-composition.js +497 -27
  71. package/node_modules/@maka/runtime-host/dist/server/execution-model-authority.js +74 -0
  72. package/node_modules/@maka/runtime-host/dist/server/failure-diagnostic.js +24 -0
  73. package/node_modules/@maka/runtime-host/dist/server/goal-coordinator.js +7 -3
  74. package/node_modules/@maka/runtime-host/dist/server/host-kernel.js +3 -3
  75. package/node_modules/@maka/runtime-host/dist/server/host-session-availability.js +5 -2
  76. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-recovery.js +4 -3
  77. package/node_modules/@maka/runtime-host/dist/server/hosted-execution-tool-profile.js +64 -4
  78. package/node_modules/@maka/runtime-host/dist/server/interactive-run-composer.js +83 -17
  79. package/node_modules/@maka/runtime-host/dist/server/message-coordinator.js +25 -4
  80. package/node_modules/@maka/runtime-host/dist/server/operation-dispatcher.js +2 -7
  81. package/node_modules/@maka/runtime-host/dist/server/plugin-data-runtime.js +234 -0
  82. package/node_modules/@maka/runtime-host/dist/server/plugin-platform-coordinator.js +6 -0
  83. package/node_modules/@maka/runtime-host/dist/server/plugin-platform.js +12 -0
  84. package/node_modules/@maka/runtime-host/dist/server/root-turn-coordinator.js +289 -96
  85. package/node_modules/@maka/runtime-host/dist/server/runtime-resource-coordinator.js +11 -8
  86. package/node_modules/@maka/runtime-host/dist/server/scheduled-task-coordinator.js +31 -21
  87. package/node_modules/@maka/runtime-host/dist/server/serial-outbound-writer.js +1 -1
  88. package/node_modules/@maka/runtime-host/dist/server/session-catalog-coordinator.js +20 -8
  89. package/node_modules/@maka/runtime-host/dist/server/session-transcript-reader.js +15 -104
  90. package/node_modules/@maka/runtime-host/dist/server/shared-session-transcript.js +3 -0
  91. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-action-gate.js +85 -117
  92. package/node_modules/@maka/runtime-host/dist/server/workhub-coordination-coordinator.js +201 -136
  93. package/node_modules/@maka/storage/dist/agent-run-store.js +60 -3
  94. package/node_modules/@maka/storage/dist/artifact-store.js +40 -3
  95. package/node_modules/@maka/storage/dist/atomic-file-write.js +74 -0
  96. package/node_modules/@maka/storage/dist/context-offload-snapshot.js +13 -6
  97. package/node_modules/@maka/storage/dist/credential-store.js +12 -63
  98. package/node_modules/@maka/storage/dist/execution-stores.js +5 -2
  99. package/node_modules/@maka/storage/dist/mcp-config-store.js +10 -25
  100. package/node_modules/@maka/storage/dist/session-bundle-policy.js +333 -30
  101. package/node_modules/@maka/storage/dist/session-store.js +16 -4
  102. package/node_modules/@maka/storage/dist/settings-store.js +8 -4
  103. package/node_modules/@maka/storage/dist/sqlite-artifact-metadata.js +11 -0
  104. package/node_modules/@maka/storage/dist/sqlite-session-metadata-schema.js +12 -1
  105. package/node_modules/@maka/storage/dist/sqlite-session-metadata-store.js +79 -8
  106. package/node_modules/@maka/storage/dist/stable-storage.js +14 -1
  107. package/node_modules/@maka/storage/package.json +2 -0
  108. package/package.json +1 -1
  109. package/node_modules/@maka/core/dist/workhub-creation-intent.js +0 -990
  110. package/node_modules/@maka/core/dist/workhub-session-resolver.js +0 -50
package/dist/cli-core.js CHANGED
@@ -52,6 +52,8 @@ export function parseMakaCliArgs(argv, version, cliCommand = RELEASE_MAKA_CLI_LA
52
52
  return { kind: 'run', args: argv.slice(1) };
53
53
  if (first === 'activate')
54
54
  return { kind: 'activate', args: argv.slice(1) };
55
+ if (first === 'session-export')
56
+ return { kind: 'session-export', args: argv.slice(1) };
55
57
  if (first === 'eval')
56
58
  return { kind: 'eval', args: argv.slice(1) };
57
59
  if (first === 'update')
@@ -97,6 +99,7 @@ function helpText(cliCommand) {
97
99
  ` ${cliCommand} --acp Serve ACP v1 over stdio (initialize, session/new, session/list)`,
98
100
  ` ${cliCommand} run ... Run one non-interactive model turn`,
99
101
  ` ${cliCommand} activate ... Run one Cloud Session activation and emit JSONL`,
102
+ ` ${cliCommand} session-export --workspace-root <dir> --session <id> --out <file.maka-session>`,
100
103
  ` ${cliCommand} -p ... Alias for ${cliCommand} run`,
101
104
  ` ${cliCommand} eval ... Run one declarative multi-arm experiment`,
102
105
  ` ${cliCommand} update --target <latest|next|version> Update this npm-global CLI and its local Runtime Host`,
@@ -243,6 +246,10 @@ export async function runMakaCli(argv = process.argv.slice(2), options = RELEASE
243
246
  const { runMakaActivationCli } = await import('./activation-command.js');
244
247
  return runMakaActivationCli(command.args);
245
248
  }
249
+ case 'session-export': {
250
+ const { runMakaSessionExportCli } = await import('./session-export-command.js');
251
+ return runMakaSessionExportCli(command.args);
252
+ }
246
253
  case 'eval': {
247
254
  const { configureInstalledEvalBundle } = await import('./eval-bundle-path.js');
248
255
  configureInstalledEvalBundle();
@@ -386,6 +393,8 @@ export async function runMakaCli(argv = process.argv.slice(2), options = RELEASE
386
393
  bindPairingToClient: command.bindPairingToClient,
387
394
  ...(command.repairRootAfterRemount ? { repairRootAfterRemount: true } : {}),
388
395
  updateExisting: command.updateExisting,
396
+ reuseExistingEnvironment: command.reuseExistingEnvironment,
397
+ allowInterruptActiveTasks: command.allowInterruptActiveTasks,
389
398
  ...(command.rootPath ? { rootPath: command.rootPath } : {}),
390
399
  ...(command.projectDirectoryRoots
391
400
  ? { projectDirectoryRoots: command.projectDirectoryRoots }
@@ -485,7 +494,13 @@ export async function runMakaCli(argv = process.argv.slice(2), options = RELEASE
485
494
  defaultRootPath: serviceDataRoots.workspaceRoot,
486
495
  selector: command.selector,
487
496
  expectedTarget: command.expectedTarget,
497
+ ...(command.expectedSourceVersion
498
+ ? { expectedSourceVersion: command.expectedSourceVersion }
499
+ : {}),
488
500
  ...(command.expectedHost ? { expectedHost: command.expectedHost } : {}),
501
+ ...(command.expectedConfigFingerprint
502
+ ? { expectedConfigFingerprint: command.expectedConfigFingerprint }
503
+ : {}),
489
504
  ...(command.managedRootId ? { managedRootId: command.managedRootId } : {}),
490
505
  ...(command.operatorDeploymentId
491
506
  ? { operatorDeploymentId: command.operatorDeploymentId }
@@ -503,7 +518,13 @@ export async function runMakaCli(argv = process.argv.slice(2), options = RELEASE
503
518
  ...(sourcePackageIntegrity ? { sourcePackageIntegrity } : {}),
504
519
  version,
505
520
  expectedTarget: command.expectedTarget,
521
+ ...(command.expectedSourceVersion
522
+ ? { expectedSourceVersion: command.expectedSourceVersion }
523
+ : {}),
506
524
  ...(command.expectedHost ? { expectedHost: command.expectedHost } : {}),
525
+ ...(command.expectedConfigFingerprint
526
+ ? { expectedConfigFingerprint: command.expectedConfigFingerprint }
527
+ : {}),
507
528
  ...(command.managedRootId ? { managedRootId: command.managedRootId } : {}),
508
529
  ...(command.operatorDeploymentId
509
530
  ? { operatorDeploymentId: command.operatorDeploymentId }
@@ -73,7 +73,10 @@ export function createMcpCapabilityProvider(manager) {
73
73
  if (!binding)
74
74
  throw new Error('MCP capability is not part of the published snapshot');
75
75
  await options.accept({ kind: 'none' });
76
- return projectMcpResult(await manager.callTool(binding, frame.arguments, { signal: options.signal }));
76
+ return projectMcpResult(await manager.callTool(binding, frame.arguments, {
77
+ signal: options.signal,
78
+ requestInteraction: options.requestInteraction,
79
+ }));
77
80
  },
78
81
  };
79
82
  }
@@ -210,7 +210,7 @@ export class McpManagementOverlay {
210
210
  return;
211
211
  const protocol = matchesKey(data, '1')
212
212
  ? 'legacy'
213
- : matchesKey(data, '2')
213
+ : matchesKey(data, '2') || matchesKey(data, 'enter')
214
214
  ? 'auto'
215
215
  : matchesKey(data, '3')
216
216
  ? '2026-07-28'
@@ -371,7 +371,7 @@ export class McpManagementOverlay {
371
371
  return [ansi.bold(editor.transportTitle), '', '1 stdio', '2 Streamable HTTP'];
372
372
  }
373
373
  if (this.phase.kind === 'protocol') {
374
- return [ansi.bold(editor.protocolTitle), '', '1 legacy', '2 auto', '3 2026-07-28'];
374
+ return [ansi.bold(editor.protocolTitle), '', '1 legacy', '2 auto (Enter)', '3 2026-07-28'];
375
375
  }
376
376
  if (this.phase.kind === 'confirm_add') {
377
377
  return confirmAddDocument(this.phase.draft, this.input.locale);
@@ -21,6 +21,7 @@ import { defineUiMessageCatalog, formatUiMessage, resolveUiMessageCatalog, } fro
21
21
  import { providerDefaultsOf, validateSlug, } from '@maka/core/llm-connections';
22
22
  import { CONNECTION_NAME_MAX_LENGTH } from '@maka/core/runtime-policy';
23
23
  import { ansi, editorTheme, selectListTheme, stripAnsi } from './tui-ansi.js';
24
+ import { stripUnfocusedCursorStyle } from './tui-editor-render.js';
24
25
  import { TUI_COPY_RESOURCES } from './tui-copy-catalog.js';
25
26
  const TUI_PICKER_COPY = resolveUiMessageCatalog(defineUiMessageCatalog()(TUI_COPY_RESOURCES.pickers));
26
27
  export function onboardingFailureMessage(failure, locale) {
@@ -433,22 +434,13 @@ export class UserQuestionOverlay {
433
434
  return assemble(cappedTitle, optionRows.map((rows) => clampRowsWithEllipsis(rows, perOption, safeWidth)));
434
435
  }
435
436
  renderInputRow(width) {
436
- const prefix = this.onInputRow ? '→ ' : ' ';
437
- const contentWidth = Math.max(1, width - USER_QUESTION_ROW_PREFIX_WIDTH);
438
- // Focused only while the input row is highlighted: that both shows the block
439
- // cursor and emits the hardware-cursor marker (#1064) so IME candidate windows
440
- // anchor to the edited text instead of the terminal bottom.
437
+ const marker = this.onInputRow ? '→' : ' ';
438
+ // Focus controls the IME marker and our cursor-visibility adapter (#1064).
441
439
  this.editor.focused = this.onInputRow;
442
440
  if (!this.onInputRow && this.editor.getText().length === 0) {
443
- return [padLine(`${prefix}${ansi.dim(this.input.placeholder)}`, width)];
441
+ return [padLine(`${marker} ${ansi.dim(this.input.placeholder)}`, width)];
444
442
  }
445
- // Drop the editor's own top/bottom border rows; keep just its content lines
446
- // so the answer reads as one row of the list.
447
- const editorLines = this.editor.render(contentWidth).slice(1, -1);
448
- if (editorLines.length === 0) {
449
- return [padLine(`${prefix}${ansi.dim(this.input.placeholder)}`, width)];
450
- }
451
- return editorLines.map((line, index) => padLine(`${index === 0 ? prefix : ' '}${line}`, width));
443
+ return renderFieldRow(this.editor, marker, width);
452
444
  }
453
445
  }
454
446
  export function modelPickerItems(currentModel, models, locale) {
@@ -750,14 +742,7 @@ export class ModelSearchOverlay {
750
742
  ];
751
743
  }
752
744
  renderFieldRow(editor, label, width) {
753
- const prefix = `${label} `;
754
- const prefixWidth = visibleWidth(prefix);
755
- const contentWidth = Math.max(1, width - prefixWidth);
756
- const editorLines = editor.render(contentWidth).slice(1, -1);
757
- if (editorLines.length === 0) {
758
- return [padLine(prefix, width)];
759
- }
760
- return editorLines.map((line, index) => padLine(`${index === 0 ? prefix : ' '.repeat(prefixWidth)}${line}`, width));
745
+ return renderFieldRow(editor, label, width);
761
746
  }
762
747
  }
763
748
  /**
@@ -873,6 +858,18 @@ function padLine(text, width) {
873
858
  const trimmed = visibleWidth(text) > safeWidth ? truncateToWidth(text, safeWidth, '') : text;
874
859
  return `${trimmed}${' '.repeat(Math.max(0, safeWidth - visibleWidth(trimmed)))}`;
875
860
  }
861
+ function renderFieldRow(editor, label, width) {
862
+ const prefix = `${label} `;
863
+ const prefixWidth = visibleWidth(prefix);
864
+ const contentWidth = Math.max(1, width - prefixWidth);
865
+ // These field editors have no autocomplete rows. Keep Editor's wrapping and
866
+ // scrolling, but omit its top/bottom borders.
867
+ const lines = editor.render(contentWidth).slice(1, -1);
868
+ const editorLines = stripUnfocusedCursorStyle(lines, editor.focused);
869
+ if (editorLines.length === 0)
870
+ return [padLine(prefix, width)];
871
+ return editorLines.map((line, index) => padLine(`${index === 0 ? prefix : ' '.repeat(prefixWidth)}${line}`, width));
872
+ }
876
873
  function keyEntryHint(copy, hasConnection, returnsTo) {
877
874
  const action = hasConnection ? 'reuse' : 'enter';
878
875
  return copy.keyHints[action][returnsTo];
@@ -1546,13 +1543,6 @@ export class OnboardingWizard {
1546
1543
  ];
1547
1544
  }
1548
1545
  renderFieldRow(editor, label, width) {
1549
- const prefix = `${label} `;
1550
- const prefixWidth = visibleWidth(prefix);
1551
- const contentWidth = Math.max(1, width - prefixWidth);
1552
- const editorLines = editor.render(contentWidth).slice(1, -1);
1553
- if (editorLines.length === 0) {
1554
- return [padLine(prefix, width)];
1555
- }
1556
- return editorLines.map((line, index) => padLine(`${index === 0 ? prefix : ' '.repeat(prefixWidth)}${line}`, width));
1546
+ return renderFieldRow(editor, label, width);
1557
1547
  }
1558
1548
  }
@@ -363,6 +363,8 @@ function parseSetupCommand(argv) {
363
363
  let bindPairingToClient = false;
364
364
  let repairRootAfterRemount = false;
365
365
  let updateExisting = false;
366
+ let reuseExistingEnvironment = false;
367
+ let allowInterruptActiveTasks = false;
366
368
  let clientDataRoot;
367
369
  let enableDirectPeer = false;
368
370
  const coordinationRelays = [];
@@ -418,6 +420,16 @@ function parseSetupCommand(argv) {
418
420
  return error('Duplicate --repair-root-after-remount');
419
421
  repairRootAfterRemount = true;
420
422
  },
423
+ '--allow-interrupt-active-tasks': () => {
424
+ if (allowInterruptActiveTasks)
425
+ return error('Duplicate --allow-interrupt-active-tasks');
426
+ allowInterruptActiveTasks = true;
427
+ },
428
+ '--reuse-existing-environment': () => {
429
+ if (reuseExistingEnvironment)
430
+ return error('Duplicate --reuse-existing-environment');
431
+ reuseExistingEnvironment = true;
432
+ },
421
433
  '--update-existing': () => {
422
434
  if (updateExisting)
423
435
  return error('Duplicate --update-existing');
@@ -427,6 +439,17 @@ function parseSetupCommand(argv) {
427
439
  });
428
440
  if ('kind' in options)
429
441
  return options;
442
+ if (reuseExistingEnvironment &&
443
+ (lifecycle !== 'on_demand' ||
444
+ updateExisting ||
445
+ enableDirectPeer ||
446
+ deferPairingCommit ||
447
+ bindPairingToClient)) {
448
+ return error('--reuse-existing-environment requires on-demand local setup without update or remote pairing options');
449
+ }
450
+ if (allowInterruptActiveTasks && !updateExisting) {
451
+ return error('--allow-interrupt-active-tasks requires --update-existing');
452
+ }
430
453
  if (!principalId || !/^[A-Za-z0-9_.:-]{1,128}$/u.test(principalId)) {
431
454
  return error('runtime-host setup requires a valid --principal');
432
455
  }
@@ -448,6 +471,8 @@ function parseSetupCommand(argv) {
448
471
  ...(bindPairingToClient ? { bindPairingToClient: true } : {}),
449
472
  ...(repairRootAfterRemount ? { repairRootAfterRemount: true } : {}),
450
473
  ...(updateExisting ? { updateExisting: true } : {}),
474
+ ...(reuseExistingEnvironment ? { reuseExistingEnvironment: true } : {}),
475
+ ...(allowInterruptActiveTasks ? { allowInterruptActiveTasks: true } : {}),
451
476
  ...(clientDataRoot ? { clientDataRoot } : {}),
452
477
  ...(enableDirectPeer ? { directPeer: { coordinationRelays } } : {}),
453
478
  };
@@ -522,6 +547,7 @@ function parseServiceManagementCommand(argv) {
522
547
  let updateTarget;
523
548
  let expectedHost;
524
549
  let expectedConfigFingerprint;
550
+ let expectedSourceVersion;
525
551
  const flagOptions = action === 'uninstall'
526
552
  ? {
527
553
  '--retain-managed-deployment': () => {
@@ -580,6 +606,17 @@ function parseServiceManagementCommand(argv) {
580
606
  },
581
607
  }
582
608
  : {}),
609
+ ...(action === 'update'
610
+ ? {
611
+ '--expected-source-version': (value) => {
612
+ if (expectedSourceVersion !== undefined)
613
+ return error('Duplicate --expected-source-version');
614
+ if (!isProductReleaseVersion(value))
615
+ return error('--expected-source-version must be an exact package version');
616
+ expectedSourceVersion = value;
617
+ },
618
+ }
619
+ : {}),
583
620
  ...(action === 'update' || action === 'restart'
584
621
  ? {
585
622
  '--expected-host-json': (value) => {
@@ -592,7 +629,7 @@ function parseServiceManagementCommand(argv) {
592
629
  },
593
630
  }
594
631
  : {}),
595
- ...(action === 'configure'
632
+ ...(action === 'configure' || action === 'update'
596
633
  ? {
597
634
  '--expected-config-fingerprint': (value) => {
598
635
  if (expectedConfigFingerprint !== undefined) {
@@ -674,8 +711,9 @@ function parseServiceManagementCommand(argv) {
674
711
  };
675
712
  }
676
713
  if (action === 'update') {
677
- if (expectedHost && !options.managedRootId) {
678
- return error('--expected-host-json requires --managed-root-id');
714
+ if ((expectedHost || expectedSourceVersion || expectedConfigFingerprint) &&
715
+ !options.managedRootId) {
716
+ return error('Observed Host and deployment fences require --managed-root-id');
679
717
  }
680
718
  const selector = updateTarget === undefined ? undefined : parseUpdateSelector(updateTarget, 'update');
681
719
  if (selector && 'kind' in selector && selector.kind === 'error')
@@ -697,6 +735,8 @@ function parseServiceManagementCommand(argv) {
697
735
  : {}),
698
736
  expectedTarget: options.expectedTarget,
699
737
  ...(expectedHost ? { expectedHost } : {}),
738
+ ...(expectedConfigFingerprint ? { expectedConfigFingerprint } : {}),
739
+ ...(expectedSourceVersion ? { expectedSourceVersion } : {}),
700
740
  ...(selector ? { selector } : {}),
701
741
  ...(allowManualUpdate ? { allowManualUpdate: true } : {}),
702
742
  ...(allowInterruptActiveTasks ? { allowInterruptActiveTasks: true } : {}),
@@ -60,7 +60,7 @@ export function createCliHostHandoffSurface(locale, input = process.stdin, outpu
60
60
  '\n');
61
61
  if (view.actions.length === 0)
62
62
  return;
63
- const options = copy.actions.map(({ action, label }) => `${action === 'interrupt' ? 'stop' : action === 'retry' ? 'r' : 'Enter'}: ${label}`);
63
+ const options = copy.actions.map(({ action, label }) => `${action === 'interrupt' ? 'stop' : action === 'retry' ? 'r' : action === 'replace' ? 'update' : 'Enter'}: ${label}`);
64
64
  // Create the reader only after the explanation is visible, with the
65
65
  // final action prompt already installed. Never draw readline's default >.
66
66
  const reader = createInterface({
@@ -82,13 +82,15 @@ export function createCliHostHandoffSurface(locale, input = process.stdin, outpu
82
82
  if (readline !== reader || current?.revision !== view.revision)
83
83
  return;
84
84
  const answer = line.trim().toLowerCase();
85
- const action = answer === 'r'
86
- ? 'retry'
87
- : answer === 'stop'
88
- ? 'interrupt'
89
- : answer === ''
90
- ? 'cancel'
91
- : undefined;
85
+ const action = answer === 'update'
86
+ ? 'replace'
87
+ : answer === 'r'
88
+ ? 'retry'
89
+ : answer === 'stop'
90
+ ? 'interrupt'
91
+ : answer === ''
92
+ ? 'cancel'
93
+ : undefined;
92
94
  if (!action || !view.actions.includes(action)) {
93
95
  reader.prompt();
94
96
  return;
@@ -178,9 +178,6 @@ export async function retireRuntimeHostLifecycleOwner(input) {
178
178
  }
179
179
  async function retireRuntimeHostLifecycleOwnerWithSignal(input, signal) {
180
180
  signal?.throwIfAborted();
181
- if (input.expectedOwner && !input.supervisor) {
182
- throw new RuntimeHostLifecycleTransactionError('owner_changed', 'A Runtime Host identity fence requires a supervised deployment');
183
- }
184
181
  const capability = await resolveExistingStorageRoot({
185
182
  path: input.rootPath,
186
183
  kind: 'interactive',
@@ -189,6 +186,9 @@ async function retireRuntimeHostLifecycleOwnerWithSignal(input, signal) {
189
186
  const idleOwner = await tryAcquireStateRootOwner(capability);
190
187
  if (idleOwner) {
191
188
  try {
189
+ if (input.expectedOwner && !input.supervisor) {
190
+ throw new RuntimeHostLifecycleTransactionError('owner_changed', 'The observed Runtime Host has exited');
191
+ }
192
192
  if (input.expectedOwner && input.supervisor) {
193
193
  const status = await input.supervisor.status();
194
194
  assertExpectedSupervisorOwner(input.expectedOwner, status);
@@ -212,6 +212,21 @@ async function retireRuntimeHostLifecycleOwnerWithSignal(input, signal) {
212
212
  });
213
213
  assertExpectedRuntimeHostOwner(input.expectedOwner, 'registration' in connected ? connected.registration : undefined);
214
214
  if (connected.kind !== 'connected') {
215
+ if (connected.kind === 'incompatible' &&
216
+ connected.registration?.rootId === input.rootId &&
217
+ connected.registration.lifecycleMode === 'ephemeral' &&
218
+ input.expectedOwner &&
219
+ !input.supervisor &&
220
+ input.prepareSourceRetirement) {
221
+ signal?.throwIfAborted();
222
+ const prepared = await input.prepareSourceRetirement(signal);
223
+ if (prepared === 'active_work')
224
+ return { kind: 'active_tasks' };
225
+ if (prepared === 'prepared') {
226
+ return waitForRuntimeHostLifecycleOwner(capability, input.timeoutMs ?? 45_000);
227
+ }
228
+ throw new RuntimeHostLifecycleTransactionError('transition_failed', 'The installed source package cannot retire this Runtime Host');
229
+ }
215
230
  if (input.supervisor) {
216
231
  const status = await input.supervisor.status();
217
232
  assertExpectedSupervisorOwner(input.expectedOwner, status);
@@ -316,6 +331,10 @@ export async function replaceRuntimeHostLifecycle(input) {
316
331
  const retirement = await retireRuntimeHostLifecycleOwner({
317
332
  rootPath: desired.root.path,
318
333
  rootId: desired.root.id,
334
+ ...(input.deps.connectExisting ? { connectExisting: input.deps.connectExisting } : {}),
335
+ ...(input.prepareSourceRetirement
336
+ ? { prepareSourceRetirement: input.prepareSourceRetirement }
337
+ : {}),
319
338
  ...(input.retirementSupervisor
320
339
  ? { supervisor: input.retirementSupervisor }
321
340
  : currentProvider
@@ -345,6 +364,12 @@ export async function replaceRuntimeHostLifecycle(input) {
345
364
  }
346
365
  }
347
366
  catch (activationError) {
367
+ // An on-demand update may have allowed its successor to open the State Root
368
+ // before readiness failed. Keep that successor authoritative; configuring an
369
+ // existing package and supervised updates retain their rollback behavior.
370
+ if (input.operation === 'update' && desired.lifecycle.mode === 'on_demand') {
371
+ throw new RuntimeHostLifecycleTransactionError('recovery_failed', 'The updated Runtime Host could not become ready. Its deployment is retained; retry activation or repair it without restoring older code.', { cause: activationError });
372
+ }
348
373
  const rollback = current
349
374
  ? {
350
375
  ...current,
@@ -74,7 +74,7 @@ export function launchRuntimeHostLocalSourceRetirement(input) {
74
74
  : []),
75
75
  ];
76
76
  return new Promise((resolve, reject) => {
77
- const child = spawn(process.execPath, args, {
77
+ const child = spawn(input.sourceNodePath ?? process.execPath, args, {
78
78
  // fd 4 inherits the existing owner-authority lease. The source helper
79
79
  // holds it only while the authenticated retirement request is in flight.
80
80
  stdio: ['ignore', 'ignore', 'inherit', 'ignore', input.inheritableAuthorityLeaseFd],
@@ -23,7 +23,7 @@ import { isDeepStrictEqual } from 'node:util';
23
23
  import { truncateUtf8 } from '@maka/core/diagnostic-log';
24
24
  import { generalizedErrorMessage } from '@maka/core/redaction';
25
25
  import { activateRuntimeHostManagedDeployment, connectRemoteRuntimeHost, ensureRuntimeHostPeerIdentity, RuntimeHostOperationError, } from '@maka/runtime-host/client';
26
- import { RuntimeHostManagedDeploymentError as RuntimeHostDeploymentAuthorityError, encodeRuntimeHostSetupFrame, isSha512PackageIntegrity, resolveRuntimeHostManagedDeployment, runtimeHostManagedOperatorCommand, RUNTIME_HOST_SETUP_ERROR_CODE_MAX_BYTES, RUNTIME_HOST_SETUP_ERROR_MESSAGE_MAX_BYTES, } from '@maka/runtime-host/operator';
26
+ import { RuntimeHostManagedDeploymentError as RuntimeHostDeploymentAuthorityError, encodeRuntimeHostSetupFrame, isSha512PackageIntegrity, resolveRuntimeHostManagedDeployment, resolveRuntimeHostManagedDeploymentAuthority, runtimeHostManagedOperatorCommand, RUNTIME_HOST_SETUP_ERROR_CODE_MAX_BYTES, RUNTIME_HOST_SETUP_ERROR_MESSAGE_MAX_BYTES, } from '@maka/runtime-host/operator';
27
27
  import { INTERACTIVE_RUNTIME_HOST_COMPOSITION_ID, RUNTIME_HOST_PROTOCOL_VERSION, } from '@maka/runtime-host/protocol';
28
28
  import { prepareRuntimeHostAccessCredential, replaceRuntimeHostAccessCredential, revokeRuntimeHostAccessCredential, RuntimeHostAccessUnavailableError, } from './runtime-host-access-command.js';
29
29
  import { convergeRuntimeHostManagedOperator, verifyRuntimeHostManagedOperator, isRuntimeHostDevelopmentPackageVersion, openRuntimeHostManagedPackageDeployment, prepareRuntimeHostManagedPackageDeployment, pruneRuntimeHostManagedPackages, removeRuntimeHostManagedDeployment, resolveRuntimeHostManagedPackageCliPath, resolveRuntimeHostManagedControlRoot, resolveRuntimeHostManagedDeploymentRoot, restoreRuntimeHostLegacyManagedOperator, RuntimeHostManagedDeploymentError, } from './runtime-host-managed-deployment.js';
@@ -107,7 +107,7 @@ export async function runRuntimeHostSetupCli(options, overrides = {}) {
107
107
  try {
108
108
  const rootId = await resolveRuntimeHostSetupRootId(options);
109
109
  const controlRoot = resolveRuntimeHostManagedControlRoot(rootId);
110
- await withRuntimeHostManagedServiceDeploymentLock(options.clientDataRoot, () => withRuntimeHostManagedServiceLifecycleLock(options.clientDataRoot, () => withRuntimeHostManagedServiceDeploymentLock(controlRoot, () => withRuntimeHostManagedServiceLifecycleLock(controlRoot, () => runRuntimeHostSetupLocked(options, deps, emit), SETUP_LOCK_TIMEOUT_MS), SETUP_LOCK_TIMEOUT_MS), SETUP_LOCK_TIMEOUT_MS), SETUP_LOCK_TIMEOUT_MS);
110
+ await withRuntimeHostManagedServiceDeploymentLock(options.clientDataRoot, () => withRuntimeHostManagedServiceLifecycleLock(options.clientDataRoot, () => withRuntimeHostManagedServiceDeploymentLock(controlRoot, () => withRuntimeHostManagedServiceLifecycleLock(controlRoot, () => runRuntimeHostSetupLocked(options, deps, emit, rootId), SETUP_LOCK_TIMEOUT_MS), SETUP_LOCK_TIMEOUT_MS), SETUP_LOCK_TIMEOUT_MS), SETUP_LOCK_TIMEOUT_MS);
111
111
  return 0;
112
112
  }
113
113
  catch (error) {
@@ -138,7 +138,30 @@ async function readOptionalLegacyServiceConfig(clientDataRoot) {
138
138
  throw error;
139
139
  }
140
140
  }
141
- async function runRuntimeHostSetupLocked(options, deps, emit) {
141
+ async function runRuntimeHostSetupLocked(options, deps, emit, rootId) {
142
+ if (options.reuseExistingEnvironment) {
143
+ if (options.lifecycle !== 'on_demand' || options.updateExisting) {
144
+ throw new RuntimeHostSetupError('invalid_setup', 'Environment discovery cannot replace a deployment');
145
+ }
146
+ const existing = await resolveRuntimeHostManagedDeploymentAuthority(rootId);
147
+ if (existing) {
148
+ const { config, capability } = await resolveRuntimeHostManagedDeployment(rootId);
149
+ assertCanonicalSetupTarget(options.expectedTarget, rootId, capability.canonicalPath);
150
+ assertExpectedDeploymentGeneration(options.expectedTarget, config);
151
+ // The binding comes from canonical authority. The installed operator validates
152
+ // its own projection when connected; discovery must not rewrite an older launcher.
153
+ emit({
154
+ kind: 'existing_environment',
155
+ version: config.launch.package.version,
156
+ serviceId: rootId,
157
+ deploymentId: config.deploymentId,
158
+ rootId,
159
+ rootPath: capability.canonicalPath,
160
+ operator: runtimeHostManagedOperatorCommand(config, process.platform === 'win32' ? 'win32' : 'posix'),
161
+ });
162
+ return;
163
+ }
164
+ }
142
165
  if (options.lifecycle === 'on_demand') {
143
166
  await runRuntimeHostOnDemandSetupLocked(options, deps, emit);
144
167
  return;
@@ -260,7 +283,7 @@ async function runRuntimeHostSupervisedSetupLocked(options, deps, emit) {
260
283
  .then(() => undefined),
261
284
  }
262
285
  : {}),
263
- allowInterruptActiveTasks: Boolean(current && packageChanged && options.updateExisting),
286
+ allowInterruptActiveTasks: options.allowInterruptActiveTasks === true,
264
287
  deps: lifecycleDeps,
265
288
  });
266
289
  if (replacement.kind === 'active_tasks') {
@@ -561,7 +584,7 @@ async function runRuntimeHostOnDemandSetupLocked(options, deps, emit) {
561
584
  activateDesired: async () => {
562
585
  await deps.activateDesired({ rootId: capability.rootId });
563
586
  },
564
- allowInterruptActiveTasks: Boolean(current && packageChanged && options.updateExisting),
587
+ allowInterruptActiveTasks: options.allowInterruptActiveTasks === true,
565
588
  deps: lifecycleDeps,
566
589
  });
567
590
  if (replacement.kind === 'active_tasks') {
@@ -826,7 +849,7 @@ function createEmitter(json, deps) {
826
849
  if (frame.kind === 'progress') {
827
850
  deps.writeOutput(`${humanPhase(frame.phase)}\n`);
828
851
  }
829
- else if (frame.kind === 'complete') {
852
+ else if (frame.kind === 'complete' || frame.kind === 'existing_environment') {
830
853
  deps.writeOutput(`${JSON.stringify(frame, null, 2)}\n`);
831
854
  }
832
855
  else {
@@ -20,13 +20,14 @@ import { spawn } from 'node:child_process';
20
20
  import { dirname, join, resolve } from 'node:path';
21
21
  import { truncateUtf8 } from '@maka/core/diagnostic-log';
22
22
  import { activateRuntimeHostManagedDeployment } from '@maka/runtime-host/client';
23
- import { createRuntimeHostLegacyPosixOperatorCommand, decodeRuntimeHostServiceManagementFrame, encodeRuntimeHostServiceManagementFrame, RUNTIME_HOST_SERVICE_ERROR_CODE_MAX_BYTES, RUNTIME_HOST_SERVICE_ERROR_MESSAGE_MAX_BYTES, RUNTIME_HOST_OPERATOR_ACCESS_MANAGEMENT_CAPABILITY, RUNTIME_HOST_OPERATOR_CAPABILITY_REQUEST_ENV, RUNTIME_HOST_OPERATOR_PROCESS_LIFETIME_LOCK_CAPABILITY, runtimeHostOperatorInvocation, RuntimeHostManagedDeploymentError as RuntimeHostDeploymentAuthorityError, } from '@maka/runtime-host/operator';
24
- import { assertRuntimeHostManagedOperatorConfig, assertRuntimeHostManagedOperatorDeployment, convergeRuntimeHostManagedOperator, verifyRuntimeHostManagedOperator, openRuntimeHostManagedPackageDeployment, prepareRuntimeHostManagedPackageDeployment, pruneRuntimeHostManagedPackages, resolveRuntimeHostManagedControlRoot, resolveRuntimeHostManagedPackageCliPath, RuntimeHostManagedDeploymentError, } from './runtime-host-managed-deployment.js';
25
- import { manageRuntimeHostService, replaceRuntimeHostManagedService, resolveRuntimeHostManagedServiceId, RuntimeHostServiceManagerError, verifyRuntimeHostManagedServiceReady, withRuntimeHostManagedServiceDeploymentLock, withRuntimeHostManagedServiceLegacyOperatorLeases, withRuntimeHostManagedServiceLifecycleLock, } from './runtime-host-service-manager.js';
23
+ import { compareProductReleaseVersions, createRuntimeHostLegacyPosixOperatorCommand, decodeRuntimeHostServiceManagementFrame, encodeRuntimeHostServiceManagementFrame, RUNTIME_HOST_SERVICE_ERROR_CODE_MAX_BYTES, RUNTIME_HOST_SERVICE_ERROR_MESSAGE_MAX_BYTES, RUNTIME_HOST_OPERATOR_ACCESS_MANAGEMENT_CAPABILITY, RUNTIME_HOST_OPERATOR_CAPABILITY_REQUEST_ENV, RUNTIME_HOST_OPERATOR_PROCESS_LIFETIME_LOCK_CAPABILITY, runtimeHostOperatorInvocation, RuntimeHostManagedDeploymentError as RuntimeHostDeploymentAuthorityError, } from '@maka/runtime-host/operator';
24
+ import { isRuntimeHostDevelopmentPackageVersion, assertRuntimeHostManagedOperatorConfig, assertRuntimeHostManagedOperatorDeployment, convergeRuntimeHostManagedOperator, verifyRuntimeHostManagedOperator, openRuntimeHostManagedPackageDeployment, prepareRuntimeHostManagedPackageDeployment, pruneRuntimeHostManagedPackages, resolveRuntimeHostManagedControlRoot, resolveRuntimeHostManagedPackageCliPath, RuntimeHostManagedDeploymentError, } from './runtime-host-managed-deployment.js';
25
+ import { runtimeHostManagedServiceConfigFingerprint, manageRuntimeHostService, replaceRuntimeHostManagedService, resolveRuntimeHostManagedServiceId, RuntimeHostServiceManagerError, verifyRuntimeHostManagedServiceReady, withRuntimeHostManagedServiceDeploymentLock, withRuntimeHostManagedServiceLegacyOperatorLeases, withRuntimeHostManagedServiceLifecycleLock, } from './runtime-host-service-manager.js';
26
26
  import { createPlatformRuntimeHostServiceBackend, resolveRuntimeHostLifecycleProvider, runtimeHostServiceSummary, } from './runtime-host-service-management-command.js';
27
27
  import { resolveManagedRuntimeHostUpdateSelection, RuntimeHostUpdateDiscoveryError, } from './runtime-host-update-discovery.js';
28
28
  import { RuntimeHostUpdatePackageError, withRuntimeHostRegistryUpdatePackage, } from './runtime-host-update-package.js';
29
29
  import { canDiscardRuntimeHostLifecycleDesiredArtifacts, convergeRuntimeHostLifecycleControlProjection, replaceRuntimeHostLifecycle, resolveRecoverableRuntimeHostManagedDeployment, RuntimeHostLifecycleTransactionError, verifyRuntimeHostLifecycleProjection, } from './runtime-host-lifecycle-transaction.js';
30
+ import { launchRuntimeHostLocalSourceRetirement } from './runtime-host-local-source-retirement.js';
30
31
  import { manageRuntimeHostManagedLifecycle } from './runtime-host-managed-lifecycle-manager.js';
31
32
  const OPERATOR_TIMEOUT_MS = 2 * 60_000;
32
33
  const OPERATOR_OUTPUT_MAX_BYTES = 256 * 1024;
@@ -53,6 +54,7 @@ export async function runManagedRuntimeHostUpdateCli(options, overrides = {}, fr
53
54
  openDeployment: openRuntimeHostManagedPackageDeployment,
54
55
  prepareDeployment: prepareRuntimeHostManagedPackageDeployment,
55
56
  prunePackages: pruneRuntimeHostManagedPackages,
57
+ retireSource: launchRuntimeHostLocalSourceRetirement,
56
58
  activateDesired: (input) => activateRuntimeHostManagedDeployment(input, {
57
59
  reconcileActivation: async () => undefined,
58
60
  }),
@@ -379,7 +381,7 @@ export async function runManagedRuntimeHostUpdateCli(options, overrides = {}, fr
379
381
  async function runCanonicalRuntimeHostUpdate(options, deps, emit) {
380
382
  let staged;
381
383
  try {
382
- return await deps.withDeploymentLock(resolveRuntimeHostManagedControlRoot(options.managedRootId), async () => {
384
+ return await deps.withDeploymentLock(resolveRuntimeHostManagedControlRoot(options.managedRootId), async (inheritableAuthorityLeaseFd) => {
383
385
  await deps.canonical.assertOperatorDeployment(options.managedRootId, options.operatorDeploymentId, process.argv[1] ?? '');
384
386
  const rejection = await deps.revalidateSelection();
385
387
  if (rejection) {
@@ -395,6 +397,10 @@ async function runCanonicalRuntimeHostUpdate(options, deps, emit) {
395
397
  throw new RuntimeHostServiceManagerError('not_installed', 'The managed Runtime Host deployment is not installed');
396
398
  }
397
399
  const current = recovered.config;
400
+ if (options.expectedSourceVersion &&
401
+ current.launch.package.version !== options.expectedSourceVersion) {
402
+ throw new RuntimeHostServiceManagerError('target_mismatch', 'The installed Runtime Host package changed; check it again before updating');
403
+ }
398
404
  await deps.canonical.convergeControlProjection(current, lifecycleDeps);
399
405
  await deps.canonical.verifyProjection(current, lifecycleDeps);
400
406
  deps.canonical.assertOperatorConfig(current, options.operatorDeploymentId, process.argv[1] ?? '');
@@ -406,6 +412,21 @@ async function runCanonicalRuntimeHostUpdate(options, deps, emit) {
406
412
  cliPath: process.argv[1] ?? '',
407
413
  expectedTarget: options.expectedTarget,
408
414
  }, { resolveProvider: resolveRuntimeHostLifecycleProvider });
415
+ if (options.expectedConfigFingerprint &&
416
+ (!currentStatus.service.config ||
417
+ runtimeHostManagedServiceConfigFingerprint(currentStatus.service.config) !==
418
+ options.expectedConfigFingerprint)) {
419
+ throw new RuntimeHostServiceManagerError('target_mismatch', 'The managed Runtime Host configuration changed; check it again before updating');
420
+ }
421
+ const developmentArtifact = isRuntimeHostDevelopmentPackageVersion(options.version) ||
422
+ isRuntimeHostDevelopmentPackageVersion(current.launch.package.version);
423
+ if (developmentArtifact && !options.sourcePackageIntegrity) {
424
+ throw new RuntimeHostServiceManagerError('target_mismatch', 'Development deployments require an explicitly selected source artifact; source hashes do not establish release order');
425
+ }
426
+ if (!developmentArtifact &&
427
+ compareProductReleaseVersions(options.version, current.launch.package.version) < 0) {
428
+ throw new RuntimeHostServiceManagerError('target_mismatch', 'This update would downgrade the shared Runtime Host. Update the Client instead.');
429
+ }
409
430
  const targetIntegrity = options.sourcePackageIntegrity ??
410
431
  options.registrySelection?.integrity ??
411
432
  (options.version === current.launch.package.version
@@ -468,15 +489,38 @@ async function runCanonicalRuntimeHostUpdate(options, deps, emit) {
468
489
  },
469
490
  };
470
491
  emit(progress('retiring', current.launch.package.version, options.version));
471
- emit(progress('replacing', current.launch.package.version, options.version));
472
492
  const replacement = await deps.canonical.replaceLifecycle({
473
493
  operation: 'update',
494
+ validateRetiredState: async () => {
495
+ emit(progress('replacing', current.launch.package.version, options.version));
496
+ },
474
497
  current,
475
498
  desired,
476
499
  allowInterruptActiveTasks: options.allowInterruptActiveTasks ?? false,
477
500
  ...(options.expectedHost ? { expectedOwner: options.expectedHost } : {}),
478
501
  ...(desired.lifecycle.mode === 'on_demand'
479
502
  ? {
503
+ ...(options.expectedHost
504
+ ? {
505
+ prepareSourceRetirement: (signal) => {
506
+ if (inheritableAuthorityLeaseFd === undefined) {
507
+ throw new RuntimeHostServiceManagerError('target_mismatch', 'Source-package retirement requires the deployment authority lease');
508
+ }
509
+ return deps.retireSource({
510
+ sourceCliPath: currentCliPath,
511
+ sourceNodePath: current.launch.nodePath,
512
+ rootPath: current.root.path,
513
+ expectedRootId: current.root.id,
514
+ expectedHostEpoch: options.expectedHost.hostEpoch,
515
+ activeWorkPolicy: options.allowInterruptActiveTasks
516
+ ? 'interrupt_active_work'
517
+ : 'refuse_active_work',
518
+ inheritableAuthorityLeaseFd,
519
+ ...(signal ? { signal } : {}),
520
+ });
521
+ },
522
+ }
523
+ : {}),
480
524
  activateDesired: async () => {
481
525
  await deps.activateDesired({ rootId: options.managedRootId });
482
526
  },