@sentropic/h2a-runtime 0.94.3 → 0.96.0

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 (49) hide show
  1. package/dist/central-mcp.d.ts +28 -0
  2. package/dist/central-mcp.d.ts.map +1 -0
  3. package/dist/central-mcp.js +204 -0
  4. package/dist/central-mcp.js.map +1 -0
  5. package/dist/cli-help-groups.d.ts.map +1 -1
  6. package/dist/cli-help-groups.js +1 -0
  7. package/dist/cli-help-groups.js.map +1 -1
  8. package/dist/config.d.ts +12 -1
  9. package/dist/config.d.ts.map +1 -1
  10. package/dist/config.js +13 -1
  11. package/dist/config.js.map +1 -1
  12. package/dist/index.d.ts +4 -0
  13. package/dist/index.d.ts.map +1 -1
  14. package/dist/index.js +195 -1
  15. package/dist/index.js.map +1 -1
  16. package/dist/native-host.d.ts +16 -0
  17. package/dist/native-host.d.ts.map +1 -1
  18. package/dist/native-host.js +41 -0
  19. package/dist/native-host.js.map +1 -1
  20. package/dist/native-restart.d.ts +74 -0
  21. package/dist/native-restart.d.ts.map +1 -0
  22. package/dist/native-restart.js +182 -0
  23. package/dist/native-restart.js.map +1 -0
  24. package/dist/native-terminal/client.d.ts +4 -2
  25. package/dist/native-terminal/client.d.ts.map +1 -1
  26. package/dist/native-terminal/client.js +10 -2
  27. package/dist/native-terminal/client.js.map +1 -1
  28. package/dist/native-terminal/host.d.ts +41 -19
  29. package/dist/native-terminal/host.d.ts.map +1 -1
  30. package/dist/native-terminal/host.js +102 -6
  31. package/dist/native-terminal/host.js.map +1 -1
  32. package/dist/native-terminal/op.d.ts.map +1 -1
  33. package/dist/native-terminal/op.js +128 -1
  34. package/dist/native-terminal/op.js.map +1 -1
  35. package/dist/native-terminal/protocol.d.ts +1 -1
  36. package/dist/native-terminal/protocol.d.ts.map +1 -1
  37. package/dist/native-terminal/protocol.js +2 -0
  38. package/dist/native-terminal/protocol.js.map +1 -1
  39. package/dist/native-terminal/server.d.ts.map +1 -1
  40. package/dist/native-terminal/server.js +30 -1
  41. package/dist/native-terminal/server.js.map +1 -1
  42. package/dist/native-terminal/socket-path.d.ts +6 -0
  43. package/dist/native-terminal/socket-path.d.ts.map +1 -1
  44. package/dist/native-terminal/socket-path.js +5 -1
  45. package/dist/native-terminal/socket-path.js.map +1 -1
  46. package/dist/native-terminal/supervisor.d.ts.map +1 -1
  47. package/dist/native-terminal/supervisor.js +71 -7
  48. package/dist/native-terminal/supervisor.js.map +1 -1
  49. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -9,6 +9,7 @@ import { Command, Help } from "commander";
9
9
  import { applyRuntimeHelpGroups, groupRuntimeHelpItems, } from "./cli-help-groups.js";
10
10
  import { attach, createRemoteSession, getRemoteSession, listRemoteSessions, refreshRemoteSession, renameRemoteSession, sessionTerminalHealth, stopRemoteSession, } from "./attach.js";
11
11
  import { authHeaders, clearDefaultRemote, getDefaultRemote, getDefaultTarget, getDefaultTools, getH2aConfig, getJobMaxAgeHours, getMaxConcurrent, getLlmMeshRuntimeConfig, getTmuxProfileConfig, getTunnel, resolveConfigPath, setDefaultRemote, setDefaultTarget, migrateConfigHomeIfNeeded, setDefaultTools, setLlmMeshRuntimeConfig, setTmuxProfileConfig, setToken, setTunnel, } from "./config.js";
12
+ import { prepareCentralMcpForLaunch, prepareCentralMcpForRestore, } from "./central-mcp.js";
12
13
  import { ensureConnected, stopTunnel } from "./tunnel.js";
13
14
  import { detectToolAuth, KNOWN_TOOLS, partitionTools } from "./auth-tools.js";
14
15
  import { transmittedSecrets, secretsSummary } from "./secrets.js";
@@ -18,7 +19,8 @@ import { syncConversation } from "./sync.js";
18
19
  import { attachLocalSession, attachPodTmux, capturePane, capturePaneVisible, clearPaneComposer, conductorRunning, currentTmuxSessionName, currentTmuxSessionIs, ensureManagedTmuxProfile, ensureHeadlessTerminal, existingLocalSessionSlugs, fanoutLabels, findLocalSession, killLocalSession, localSessionGatewayEnvStatus, listLocalSessions, listH2aStatusSurfaces, listLocalSessionsWithDiagnostics, localSessionIdle, localSessionPanePid, localSessionName, paneProcessObservation, paneTreeCpuMs, paneWorkerAttribution, paneWorkerAttributionFromView, paneWorkerPid, pasteLiteralBlock, submitPane, installH2aStatusSurface, managedSessionCandidates, parseManagedSessionName, openH2aStatusWindow, persistLaunchContext, readLaunchContext, readFleetProcView, relaunchInSession, resolveAgentPane, resolveAgentPaneForInstance, resolveLocalSession, sessionRelaunchSafety, runLocalCliForeground, sendKeysLiteral, sessionAttached, sessionAttachedCount, setLocalSessionDisplayName, slugify, cleanupHeadlessPromptFile, startH2aWindow, startH2aWindowVerified, startHeadlessSession, startLocalSession, tmuxAvailable, uninstallH2aStatusSurface, } from "./tmux.js";
19
20
  import { deliverInitialPrompt, sleepSync, } from "./prompt-delivery.js";
20
21
  import { buildLaunchContext } from "./launch-context.js";
21
- import { attachNativeSession, killNativeSessionTree, nativeHostAvailable, nativePromptDeliveryDeps, nativeSessionLiveness, nativeSessionPid, nativeTreeCpuMs, nativeWorkerPid, resolveSessionHostKind, startNativeH2aSidecar, startNativeHeadlessSession, startNativeSession, } from "./native-host.js";
22
+ import { attachNativeSession, driveNativeInstruction, killNativeSessionIfIncarnation, killNativeSessionTree, nativeHostAvailable, nativePromptDeliveryDeps, nativeSessionLiveness, nativeSessionPid, nativeSessionState, nativeTreeCpuMs, nativeWorkerPid, resolveSessionHostKind, startNativeH2aSidecar, startNativeHeadlessSession, startNativeSession, } from "./native-host.js";
23
+ import { executeNativeRestart, NativeRestartError, } from "./native-restart.js";
22
24
  import { migrateTmuxNames } from "./tmux-name-migration.js";
23
25
  import { projectStatusForH2a } from "./status-projection.js";
24
26
  import { buildAgentLaunchArgs, isAgentLaunchEffort, isAgentLaunchProfile, } from "./agent-launch-args.js";
@@ -74,6 +76,7 @@ const KNOWN_PROFILE_HELP = `${CLI_PROFILES.join(", ")} (aliases: claude-code, an
74
76
  export const packageName = "@sentropic/h2a-runtime";
75
77
  export const H2A_RUNTIME_CLI_API_VERSION = 1;
76
78
  export { run } from "./run.js";
79
+ export { executeNativeRestart, nativeMcpRelaunchInstruction, NativeRestartError, } from "./native-restart.js";
77
80
  export { AGENT_LAUNCH_EFFORTS, AGENT_LAUNCH_PROFILES, AGENT_LAUNCH_PROMPT_MAX_BYTES, assertAgentLaunchModel, assertAgentLaunchPrompt, buildAgentLaunchArgs, isAgentLaunchEffort, isAgentLaunchProfile, } from "./agent-launch-args.js";
78
81
  export { attach, createRemoteSession, getRemoteSession, listRemoteSessions, refreshRemoteSession, renameRemoteSession, stopRemoteSession, } from "./attach.js";
79
82
  export { inspectProfileAuth } from "./auth-diagnostics.js";
@@ -1401,6 +1404,64 @@ async function injectLlmMeshGatewayEnv(mode = "auto", allowDirectFallback = true
1401
1404
  }
1402
1405
  return meshEnv.ANTHROPIC_BASE_URL;
1403
1406
  }
1407
+ function captureAnthropicGatewayEnvironment() {
1408
+ return {
1409
+ ...(process.env.ANTHROPIC_BASE_URL !== undefined
1410
+ ? { ANTHROPIC_BASE_URL: process.env.ANTHROPIC_BASE_URL }
1411
+ : {}),
1412
+ ...(process.env.ANTHROPIC_AUTH_TOKEN !== undefined
1413
+ ? { ANTHROPIC_AUTH_TOKEN: process.env.ANTHROPIC_AUTH_TOKEN }
1414
+ : {}),
1415
+ ...(process.env.ANTHROPIC_API_KEY !== undefined
1416
+ ? { ANTHROPIC_API_KEY: process.env.ANTHROPIC_API_KEY }
1417
+ : {}),
1418
+ };
1419
+ }
1420
+ async function prepareNativeRestart(session, mode) {
1421
+ if (mode === "gateway" && !profileUsesLlmMeshGateway(session.profile)) {
1422
+ throw new Error("gateway is unsupported for this profile");
1423
+ }
1424
+ const original = captureAnthropicGatewayEnvironment();
1425
+ const restore = replaceAnthropicGatewayEnvironment(process.env, original);
1426
+ try {
1427
+ const effectiveMode = gatewayModeForProfile(session.profile, mode);
1428
+ await injectLlmMeshGatewayEnv(effectiveMode, effectiveMode !== "gateway", session.name);
1429
+ const useBare = shouldUseClaudeBare(session.profile);
1430
+ return {
1431
+ command: localCliCommand(session.profile),
1432
+ args: session.convId
1433
+ ? localResumeArgs(session.profile, session.convId, { bare: useBare })
1434
+ : localStartArgs(session.profile, { bare: useBare }),
1435
+ gatewayEnvironment: captureAnthropicGatewayEnvironment(),
1436
+ };
1437
+ }
1438
+ finally {
1439
+ restore();
1440
+ }
1441
+ }
1442
+ function renderNativeRestartResult(result, json) {
1443
+ if (json) {
1444
+ process.stdout.write(`${JSON.stringify(result)}\n`);
1445
+ return;
1446
+ }
1447
+ for (const row of result.sessions) {
1448
+ if (row.state === "completed") {
1449
+ if (row.restarted) {
1450
+ process.stderr.write(`[h2a] restarted native CLI ${row.id} (gateway=${row.gatewayMode})\n`);
1451
+ }
1452
+ if (row.instructionSubmitted) {
1453
+ process.stderr.write(`[h2a] submitted MCP relaunch/attach instruction to ${row.id}; MCP health is not yet proven\n`);
1454
+ }
1455
+ continue;
1456
+ }
1457
+ if (row.state === "failed") {
1458
+ process.stderr.write(`[h2a] restart failed for ${row.id} (${row.failure ?? "unknown"})\n`);
1459
+ }
1460
+ else {
1461
+ process.stderr.write(`[h2a] restart did not touch ${row.id} because an earlier session failed\n`);
1462
+ }
1463
+ }
1464
+ }
1404
1465
  export async function prepareStructuredGateway(mode, inject = (selectedMode) => injectLlmMeshGatewayEnv(selectedMode, selectedMode !== "gateway")) {
1405
1466
  const gateway = await inject(mode);
1406
1467
  if (mode === "gateway" && !gateway) {
@@ -4062,6 +4123,14 @@ export async function main(argv) {
4062
4123
  process.exitCode = 2;
4063
4124
  return;
4064
4125
  }
4126
+ try {
4127
+ await prepareCentralMcpForLaunch({ root: cwd, profile, cwd });
4128
+ }
4129
+ catch (error) {
4130
+ process.stderr.write(`[h2a] ${error.message}\n`);
4131
+ process.exitCode = 1;
4132
+ return;
4133
+ }
4065
4134
  const started = [];
4066
4135
  for (const label of labels) {
4067
4136
  const clientSessionId = localSessionName(slugify(label ?? cwd));
@@ -5773,6 +5842,121 @@ export async function main(argv) {
5773
5842
  process.stderr.write("[h2a] wake-request watch stopped\n");
5774
5843
  });
5775
5844
  // ---------------------------------------------------------------------------
5845
+ // restart — explicit native CLI recreation or guarded live MCP instruction
5846
+ // ---------------------------------------------------------------------------
5847
+ program
5848
+ .command("restart [session]")
5849
+ .description("Restart one live managed native-PTY CLI, or every live managed native CLI with --all. --gw requires the explicit value on|off. With --relaunch-mcp alone, keep the CLI alive and submit one guarded MCP relaunch/attach instruction through the native drive backchannel.")
5850
+ .option("--all", "target every live managed native CLI session")
5851
+ .option("--gw <on|off>", "restart with llm-mesh gateway explicitly on or off")
5852
+ .option("--relaunch-mcp <name>", "submit a bounded instruction to relaunch or attach this MCP server; submission does not prove MCP health")
5853
+ .option("--json", "emit a versioned, credential-free result")
5854
+ .action(async (session, opts) => {
5855
+ try {
5856
+ const registry = loadRegistry();
5857
+ if (registry.state === "unknown") {
5858
+ throw new NativeRestartError("registry-unreadable", `local registry is unreadable (${registry.reason})`);
5859
+ }
5860
+ const candidates = registry.entries.map((entry) => ({
5861
+ id: entry.id,
5862
+ ...(entry.label !== undefined ? { label: entry.label } : {}),
5863
+ name: entry.tmuxSession ?? localSessionName(entry.id),
5864
+ kind: entry.kind,
5865
+ ...(entry.role !== undefined ? { role: entry.role } : {}),
5866
+ ...(entry.endedAt !== undefined ? { endedAt: entry.endedAt } : {}),
5867
+ profile: entry.tool,
5868
+ cwd: entry.cwd,
5869
+ ...(entry.convId !== undefined ? { convId: entry.convId } : {}),
5870
+ ...(entry.sessionClass !== undefined
5871
+ ? { sessionClass: entry.sessionClass }
5872
+ : {}),
5873
+ ...(entry.gatewayMode !== undefined
5874
+ ? { gatewayMode: entry.gatewayMode }
5875
+ : {}),
5876
+ }));
5877
+ const result = await executeNativeRestart({
5878
+ ...(session !== undefined ? { target: session } : {}),
5879
+ ...(opts.all === true ? { all: true } : {}),
5880
+ ...(opts.gw !== undefined
5881
+ ? { gateway: opts.gw }
5882
+ : {}),
5883
+ ...(opts.relaunchMcp !== undefined
5884
+ ? { relaunchMcp: opts.relaunchMcp }
5885
+ : {}),
5886
+ }, {
5887
+ listSessions: () => candidates,
5888
+ snapshot: (candidate) => {
5889
+ const probe = nativeSessionState(candidate.name);
5890
+ if (probe.state === "unknown")
5891
+ return { state: "unknown" };
5892
+ if (probe.state === "absent" ||
5893
+ probe.session.status !== "running") {
5894
+ return { state: "dead" };
5895
+ }
5896
+ return {
5897
+ state: "live",
5898
+ generation: probe.session.generation,
5899
+ incarnation: probe.session.incarnation,
5900
+ controlled: probe.session.controlled ?? true,
5901
+ };
5902
+ },
5903
+ prepare: (candidate, _snapshot, mode) => prepareNativeRestart(candidate, mode),
5904
+ restart: (candidate, snapshot, mode, prepared) => {
5905
+ if (!killNativeSessionIfIncarnation(candidate.name, snapshot.generation, snapshot.incarnation)) {
5906
+ throw new Error("fenced native stop failed");
5907
+ }
5908
+ const restore = replaceAnthropicGatewayEnvironment(process.env, prepared.gatewayEnvironment);
5909
+ try {
5910
+ const relaunched = startNativeSession(candidate.profile, prepared.command, candidate.cwd, prepared.args, candidate.id, {
5911
+ ...(candidate.convId !== undefined
5912
+ ? { resumeId: candidate.convId }
5913
+ : {}),
5914
+ sessionClass: candidate.sessionClass ?? "human",
5915
+ refuseExisting: true,
5916
+ });
5917
+ enrollFromRun({
5918
+ profile: candidate.profile,
5919
+ slug: candidate.id,
5920
+ tmuxSession: relaunched.name,
5921
+ hostKind: "local-native",
5922
+ pid: relaunched.pid,
5923
+ cwd: candidate.cwd,
5924
+ sessionClass: candidate.sessionClass ?? "human",
5925
+ ...(candidate.convId !== undefined
5926
+ ? { convId: candidate.convId }
5927
+ : {}),
5928
+ ...(mode !== "auto" ? { gatewayMode: mode } : {}),
5929
+ });
5930
+ }
5931
+ finally {
5932
+ restore();
5933
+ }
5934
+ },
5935
+ drive: (candidate, instruction) => driveNativeInstruction(candidate.id, instruction),
5936
+ });
5937
+ renderNativeRestartResult(result, opts.json === true);
5938
+ if (!result.ok)
5939
+ process.exitCode = 1;
5940
+ }
5941
+ catch (error) {
5942
+ const known = error instanceof NativeRestartError;
5943
+ const code = known ? error.code : "restart-internal";
5944
+ const message = known ? error.message : "restart failed unexpectedly";
5945
+ if (opts.json === true) {
5946
+ process.stdout.write(`${JSON.stringify({
5947
+ kind: "h2a.restart.result",
5948
+ version: 1,
5949
+ ok: false,
5950
+ error: { code, message },
5951
+ })}\n`);
5952
+ }
5953
+ else {
5954
+ process.stderr.write(`[h2a] restart: ${message}\n`);
5955
+ }
5956
+ process.exitCode = 1;
5957
+ }
5958
+ });
5959
+ // ---------------------------------------------------------------------------
5776
5960
  // relaunch — recover idle sessions, or explicitly force-recreate one/all
5777
5961
  // ---------------------------------------------------------------------------
5778
5962
  program
@@ -6511,6 +6695,14 @@ export async function main(argv) {
6511
6695
  }
6512
6696
  if (forceGateway)
6513
6697
  restoreOpts.forceGateway = forceGateway;
6698
+ try {
6699
+ await prepareCentralMcpForRestore({ root: process.cwd() });
6700
+ }
6701
+ catch (error) {
6702
+ process.stderr.write(`[h2a] ${error.message}\n`);
6703
+ process.exitCode = 1;
6704
+ return;
6705
+ }
6514
6706
  if (needRemote) {
6515
6707
  const url = getConfiguredRemote();
6516
6708
  await ensureConnected(url);
@@ -7368,6 +7560,7 @@ if (isEntryPoint()) {
7368
7560
  export { main as dispatchH2a };
7369
7561
  /** @deprecated Compatibility for older h2a cores; new cores require dispatchH2a. */
7370
7562
  export { main as dispatch };
7563
+ export { ensureCentralMcp } from "./central-mcp.js";
7371
7564
  /**
7372
7565
  * h2a-facing surface: the read-only remote-agents projection consumed by the
7373
7566
  * objective-loop tick's *agents adapter* in `@sentropic/h2a` (via lazy
@@ -7382,6 +7575,7 @@ export { projectStatusForH2a };
7382
7575
  export { NativeTerminalClient } from "./native-terminal/client.js";
7383
7576
  export { NativeTerminalHost } from "./native-terminal/host.js";
7384
7577
  export { NativeTerminalHostSupervisor } from "./native-terminal/supervisor.js";
7578
+ export { assertOwnedByCurrentUser, sameNativeTerminalSocket } from "./native-terminal/socket-path.js";
7385
7579
  /**
7386
7580
  * Status-bar writer surface, consumed by `@sentropic/h2a`'s
7387
7581
  * `status --write-bars` loop via lazy `import()`: enumerate the sessions whose