[codex] scope bridge terminal env refresh to worker startup (#1031)

* fix(bridge): refresh terminal env from profile config on profile switch

Profile switching changes HERMES_HOME but the TERMINAL_* environment
variables (TERMINAL_ENV, TERMINAL_SSH_HOST, etc.) still point to the
root config's terminal settings set at gateway startup.

Add _refresh_terminal_env() that re-reads terminal config from the
active profile's config.yaml and sets the corresponding TERMINAL_* env
vars. Call it in:
- AgentPool.get_or_create(): when creating a session for a profile
- AgentPool._run_chat(): before agent execution, inside _profile_env

Errors are handled gracefully: YAML parse failures log to stderr,
terminal_tool cache invalidation failures are silently ignored, and
missing config files are skipped without error.

* fix(bridge): refresh terminal env in worker profile setup

_set_worker_profile_env() handles broker-spawned worker subprocesses
that are isolated per profile. The worker inherits TERMINAL_* env vars
from the broker (root config), and _profile_env() is a no-op in worker
mode, so terminal config was never refreshed for non-default profiles.

Adding _refresh_terminal_env() here means each worker subprocess reads
its own profile's config.yaml terminal section on startup, solving
profile-isolated terminal backends (e.g. SSH per profile).

* fix bridge terminal env refresh scope

* refresh worker profile env for new agents

* avoid bridge worker restart for channel config

* align config controller bridge restart tests

---------

Co-authored-by: GoldenFish123321 <goldfishx@gmail.com>
Co-authored-by: GoldenFishX <golden_fish@foxmail.com>
This commit is contained in:
ekko
2026-05-26 00:15:27 +08:00
committed by GitHub
co-authored by GoldenFish123321 GoldenFishX
parent 689237f0fd
commit e686f0277a
3 changed files with 83 additions and 18 deletions
@@ -1,7 +1,6 @@
import { readFile } from 'fs/promises'
import { join } from 'path'
import { getActiveProfileName, getProfileDir } from '../../services/hermes/hermes-profile'
import { AgentBridgeClient } from '../../services/hermes/agent-bridge'
import { restartGatewayForProfile } from '../../services/hermes/gateway-autostart'
import { saveEnvValueForProfile } from '../../services/config-helpers'
import { logger } from '../../services/logger'
@@ -84,15 +83,6 @@ function deepMerge(target: Record<string, any>, source: Record<string, any>): Re
return target
}
async function destroyBridgeProfile(profile: string): Promise<void> {
try {
const result = await new AgentBridgeClient({ connectRetryMs: 0, timeoutMs: 5000 }).destroyProfile(profile)
logger.info('[config] destroyed bridge sessions after gateway restart profile=%s destroyed=%s', profile, result.destroyed)
} catch (err) {
logger.warn(err, '[config] failed to destroy bridge sessions after gateway restart profile=%s', profile)
}
}
async function readEnvPlatforms(profile: string): Promise<Record<string, any>> {
try {
const raw = await readFile(envPath(profile), 'utf-8')
@@ -159,13 +149,12 @@ export async function updateConfig(ctx: any) {
},
})
// Platform adapters still run through Hermes gateway; restart it so channel
// config changes (Feishu/Weixin/etc.) are applied, then refresh bridge sessions.
// Platform adapters run through Hermes gateway; restart it so channel
// config changes (Feishu/Weixin/etc.) are applied.
if (restart !== false && PLATFORM_SECTIONS.has(section)) {
try {
const restartResult = await restartGatewayForProfile(profile)
logger.info('[config] gateway restarted after config update section=%s profile=%s result=%j', section, profile, restartResult)
await destroyBridgeProfile(profile)
} catch (err) {
logger.error(err, 'Gateway restart failed')
ctx.status = 500
@@ -227,12 +216,11 @@ export async function updateCredentials(ctx: any) {
},
})
// Platform adapters still run through Hermes gateway; restart it so channel
// credentials are applied, then refresh bridge sessions.
// Platform adapters run through Hermes gateway; restart it so channel
// credentials are applied.
try {
const restartResult = await restartGatewayForProfile(profile)
logger.info('[config] gateway restarted after credentials update platform=%s profile=%s result=%j', platform, profile, restartResult)
await destroyBridgeProfile(profile)
} catch (err) {
logger.error(err, 'Gateway restart failed')
ctx.status = 500