diff --git a/src/runtime/preconditions.ts b/src/runtime/preconditions.ts index 25923b3..5ce41f0 100644 --- a/src/runtime/preconditions.ts +++ b/src/runtime/preconditions.ts @@ -73,7 +73,7 @@ export async function runPreflightChecks( reason: hermesVersionCheck.reason, versionOutput: hermesPresence.stdout, }, - guidance: `Upgrade Hermes Agent to ${CONTRACT_METADATA.minimumHermesReleaseTag} / ${CONTRACT_METADATA.minimumHermesVersion} or newer, then rerun ${CONTRACT_METADATA.publicEntrypoint}.`, + guidance: `Upgrade Hermes Agent to ${CONTRACT_METADATA.minimumHermesReleaseTag} / ${CONTRACT_METADATA.minimumHermesVersion} or newer, then rerun ${CONTRACT_METADATA.publicEntrypoint}. Run \`hermes update\` first; for pip installs, use \`pip install --upgrade hermes-agent\` or \`uv pip install --upgrade hermes-agent\`.`, message: hermesVersionCheck.reason === "unparseable" ? "Hermes returned a version string that the helper cannot validate against the supported latest-only contract." diff --git a/test/cli.test.ts b/test/cli.test.ts index dce394e..82f9bb5 100644 --- a/test/cli.test.ts +++ b/test/cli.test.ts @@ -189,6 +189,51 @@ test("missing Hermes on PATH aborts before the command can continue", async () = assert.equal(stderr.contents, ""); }); +test("unsupported Hermes versions print concrete upgrade commands", async () => { + const harness = await createHermesIntegrationHarness({ + fixture: "clean-home", + }); + const stdout = createBufferWriter(); + const stderr = createBufferWriter(); + + try { + await harness.installFakeHermesOnPath({ + versionOutput: "hermes-agent 0.11.0", + }); + + const result = await run([], { + dependencies: harness.createDependencies({ + runtime: { + osRelease: "6.8.0", + platform: "linux", + stdinIsTTY: true, + stdoutIsTTY: true, + }, + }), + stderr, + stdout, + }); + + assert.equal(result.exitCode, 1); + assert.equal(result.result?.status, "failure"); + assert.equal(result.result?.code, "unsupported_hermes_version"); + assert.match(stdout.contents, /GonkaGate onboarding failed\./); + assert.match( + stdout.contents, + /Hermes Agent 0\.11\.0 is below the supported floor 0\.14\.0 \(v2026\.5\.16\)\./, + ); + assert.match(stdout.contents, /Run `hermes update` first/); + assert.match(stdout.contents, /pip install --upgrade hermes-agent/); + assert.match(stdout.contents, /uv pip install --upgrade hermes-agent/); + assert.equal(stderr.contents, ""); + assert.deepEqual(await harness.readFakeHermesInvocations(), [ + ["--version"], + ]); + } finally { + await harness.cleanup(); + } +}); + test("missing TTY aborts before the helper calls Hermes", async () => { const harness = await createHermesIntegrationHarness({ fixture: "clean-home",