fix(desktop): preserve implicit default local route

This commit is contained in:
Hermes Agent
2026-09-24 19:36:22 -05:00
committed by brooklyn!
parent 76460be82d
commit b50bb77ec5
5 changed files with 25 additions and 41 deletions

View File

@@ -745,18 +745,21 @@ test('registry local route: per-profile override wins when global remote is also
assert.deepEqual(route, { delegate: true, poolKey: 'research' })
})
test('registry local route: a concrete remote-only profile is refused on the forced-local branch, including default', () => {
test('registry local route: a concrete remote-only profile is refused on the forced-local branch', () => {
// globalRemote still force-locals a profile that exists on this machine
// (This device must not dial the remote). A profile that exists only on the
// remote must not spawn a local child — including default, which the
// ordinary existence guard exempts and would otherwise start silently.
// (This device must not dial the remote). A named profile that exists only
// on the remote must not spawn a local child.
const named = resolveRegistryLocalRoute('inbox', { globalRemote: true, localProfileExists: false })
const fallback = resolveRegistryLocalRoute('default', { globalRemote: true, localProfileExists: false })
assert.match(String(named.refuse ?? ''), /Profile "inbox" no longer exists/)
assert.equal(named.delegate, false)
assert.match(String(fallback.refuse ?? ''), /Profile "default" no longer exists/)
assert.equal(fallback.delegate, false)
// default is $HERMES_HOME, not profiles/default: a profiles/default probe
// reports absent, but This device -> default must still open locally.
assert.deepEqual(resolveRegistryLocalRoute('default', { globalRemote: true, localProfileExists: false }), {
delegate: false,
poolKey: 'conn:local::default'
})
const present = resolveRegistryLocalRoute('research', { globalRemote: true, localProfileExists: true })

View File

@@ -220,9 +220,7 @@ export interface RegistryLocalRoute {
delegate: boolean
/** Pool key for the forced-local child when not delegating. */
poolKey: string
/** Set when a concrete remote-only profile must not spawn a local child.
* Includes `default`: the ordinary existence guard exempts it, so a
* forced-local default spawn would otherwise succeed with no error. */
/** Set when a concrete remote-only profile must not spawn a local child. */
refuse?: string
}
@@ -526,8 +524,9 @@ function normalizedSshTarget(route: { host?: unknown; port?: unknown; user?: unk
* the BARE profile key by design, and that slot may already hold the v1
* route's REMOTE descriptor — so the forced-local child pools under the
* `conn:local::<profile>` form instead (colons are invalid in profile names,
* so it cannot collide). A concrete profile that does not exist on this
* machine, including default, is refused instead of spawned. A per-profile
* so it cannot collide). A concrete named profile that does not exist on
* this machine is refused instead of spawned. `default` is `$HERMES_HOME`
* itself, so it always exists here and is never refused. A per-profile
* remote override still delegates to the legacy profile route.
*/
export function resolveRegistryLocalRoute(
@@ -551,12 +550,13 @@ export function resolveRegistryLocalRoute(
if (opts.globalRemote) {
const poolKey = `${backendScopePrefix(LOCAL_CONNECTION_ID)}${profileKey}`
// A concrete profile that does not exist on this machine is remote-only.
// Spawning it locally is the #90477 loop (and, for default, a silent
// success — the ordinary guard exempts default). Refuse instead. An
// unprofiled call is enumeration, not a dial, and a profile that exists
// locally still force-locals so "This device" does not dial the remote.
if (concrete && opts.localProfileExists === false) {
// A concrete named profile that does not exist on this machine is
// remote-only. Spawning it locally is the #90477 loop, so refuse. An
// unprofiled call is enumeration, not a dial. `default` lives at
// $HERMES_HOME, not profiles/default, so This device -> default always
// force-locals. A profile that exists locally still force-locals so
// "This device" does not dial the remote.
if (concrete && profileKey !== 'default' && opts.localProfileExists === false) {
return { delegate: false, poolKey, refuse: `Profile "${profileKey}" no longer exists.` }
}

View File

@@ -11676,11 +11676,8 @@ async function spawnPoolBackend(
// here, and logging "Starting" first left an orphaned line with no READY
// and no exit — the exact undiagnosable burst signature in remote-gateway
// user bundles (Aug 2026, Dash's report).
assertLocalProfileCanStart(
profile,
profileDeletionGate,
key => directoryExists(path.join(HERMES_HOME, 'profiles', key)),
{ allowImplicitDefault: !opts.forceLocal }
assertLocalProfileCanStart(profile, profileDeletionGate, key =>
directoryExists(path.join(HERMES_HOME, 'profiles', key))
)
rememberLog(`Starting Hermes backend for profile "${profile}" via ${backend.label}`)

View File

@@ -140,16 +140,6 @@ test('assertLocalProfileCanStart rejects a delayed retry after the profile direc
assert.doesNotThrow(() => assertLocalProfileCanStart('selena', gate, profile => profile === 'selena'))
})
test('assertLocalProfileCanStart does not exempt default when a forced-local spawn asks', () => {
const gate = new ProfileDeletionGate()
assert.throws(
() => assertLocalProfileCanStart('default', gate, () => false, { allowImplicitDefault: false }),
/Profile "default" no longer exists/
)
assert.doesNotThrow(() => assertLocalProfileCanStart('default', gate, () => true, { allowImplicitDefault: false }))
})
test('localProfilePoolKeys returns every local process scope for one profile', () => {
assert.deepEqual(localProfilePoolKeys('Selena'), ['selena', 'conn:local::selena'])
assert.deepEqual(localProfilePoolKeys(''), [])

View File

@@ -184,8 +184,7 @@ export class ProfileDeletionGate {
export function assertLocalProfileCanStart(
profile: unknown,
gate: ProfileDeletionGate,
profileDirectoryExists: (profile: string) => boolean,
opts: { allowImplicitDefault?: boolean } = {}
profileDirectoryExists: (profile: string) => boolean
): void {
const key = String(profile ?? '')
.trim()
@@ -193,12 +192,7 @@ export function assertLocalProfileCanStart(
gate.assertCanStart(key)
// `default` is `$HERMES_HOME`, not `profiles/default`, so the ordinary local
// start exempts it. A forced-local spawn must not: that exemption is how a
// remote-only default starts a local agent with no error (#90477).
const implicitDefault = opts.allowImplicitDefault !== false && key === 'default'
if (key && !implicitDefault && !profileDirectoryExists(key)) {
if (key && key !== 'default' && !profileDirectoryExists(key)) {
throw new Error(`Profile "${key}" no longer exists.`)
}
}