Fix bridge history, profile models, and Windows gateway handling (#845)

* feat: support profile-aware group chat bridge flows

* feat: route cron jobs through hermes cli

* Fix group chat routing and isolate bridge tests

* Add Grok image-to-video media skill

* Default Grok videos to media directory

* Fix bridge profile fallback and cron repeat clearing

* Refine bridge chat and gateway platform handling

* Filter bridge tool-call text deltas

* Preserve structured bridge chat history

* Prepare beta release build artifacts

* Fix Windows run profile resolution

* Fix Windows path compatibility checks

* Fix profile-scoped model page display

* Hide Windows subprocess windows for jobs and updates

* Hide Windows file backend subprocess windows

* Avoid Windows gateway restart lock conflicts

* Treat Windows gateway lock as running on startup

* Force release Windows gateway lock on restart

* Tighten Windows gateway lock cleanup

* Update chat e2e source expectation

* Bump package version to 0.5.30

---------

Co-authored-by: Codex <codex@openai.com>
This commit is contained in:
ekko
2026-05-19 16:09:59 +08:00
committed by GitHub
co-authored by Codex
parent 3d74d78698
commit 9a9416c99c
129 changed files with 7016 additions and 1837 deletions
@@ -1,13 +1,23 @@
import { setTimeout as delay } from 'timers/promises'
import { createConnection, type Socket } from 'net'
import { tmpdir } from 'os'
import { URL } from 'url'
import { join } from 'path'
import { bridgeLogger } from '../../logger'
import { getActiveProfileName, getProfileDir } from '../hermes-profile'
export const DEFAULT_AGENT_BRIDGE_ENDPOINT = process.platform === 'win32'
? 'tcp://127.0.0.1:18765'
: 'ipc:///tmp/hermes-agent-bridge.sock'
function resolveDefaultAgentBridgeEndpoint(): string {
if (process.env.VITEST) {
return process.platform === 'win32'
? `tcp://127.0.0.1:${28000 + (process.pid % 10000)}`
: `ipc://${join(tmpdir(), `hermes-agent-bridge-test-${process.pid}.sock`)}`
}
return process.platform === 'win32'
? 'tcp://127.0.0.1:18765'
: 'ipc:///tmp/hermes-agent-bridge.sock'
}
export const DEFAULT_AGENT_BRIDGE_ENDPOINT = resolveDefaultAgentBridgeEndpoint()
export const DEFAULT_AGENT_BRIDGE_TIMEOUT_MS = 120000
function envPositiveInt(name: string): number | undefined {
@@ -26,6 +36,7 @@ export interface AgentBridgeOptions {
export interface AgentBridgeRequestOptions {
timeoutMs?: number
serialize?: boolean
}
export interface AgentBridgeChatOptions {
@@ -33,6 +44,9 @@ export interface AgentBridgeChatOptions {
storage_message?: AgentBridgeMessage
model?: string
provider?: string
source?: string
wait?: boolean
timeout?: number
}
export type AgentBridgeMessage =
@@ -298,6 +312,10 @@ export class AgentBridgeClient {
}
}
if (!options.serialize) {
return run()
}
const next = this.lock.then(run, run)
this.lock = next.catch(() => undefined)
return next
@@ -325,6 +343,9 @@ export class AgentBridgeClient {
...(profile ? { profile } : {}),
...(options.model ? { model: options.model } : {}),
...(options.provider ? { provider: options.provider } : {}),
...(options.source ? { source: options.source } : {}),
...(options.wait ? { wait: true } : {}),
...(options.timeout ? { timeout: options.timeout } : {}),
...(options.force_compress ? { force_compress: true } : {}),
})
}
@@ -383,12 +404,22 @@ export class AgentBridgeClient {
return this.request<AgentBridgeRunResult>({ action: 'get_result', run_id: runId }, options)
}
interrupt(sessionId: string, message?: string): Promise<AgentBridgeResponse> {
return this.request({ action: 'interrupt', session_id: sessionId, message })
interrupt(sessionId: string, message?: string, profile?: string): Promise<AgentBridgeResponse> {
return this.request({
action: 'interrupt',
session_id: sessionId,
message,
...(profile ? { profile } : {}),
})
}
steer(sessionId: string, text: string): Promise<AgentBridgeResponse> {
return this.request({ action: 'steer', session_id: sessionId, text })
steer(sessionId: string, text: string, profile?: string): Promise<AgentBridgeResponse> {
return this.request({
action: 'steer',
session_id: sessionId,
text,
...(profile ? { profile } : {}),
})
}
approvalRespond(approvalId: string, choice: string): Promise<AgentBridgeResponse> {
@@ -407,15 +438,27 @@ export class AgentBridgeClient {
}
destroyAll(): Promise<AgentBridgeResponse> {
return this.request({ action: 'destroy_all' })
return this.request({ action: 'destroy_all' }, { serialize: true })
}
getHistory(sessionId: string): Promise<AgentBridgeResponse> {
return this.request({ action: 'get_history', session_id: sessionId })
destroyProfile(profile: string): Promise<AgentBridgeResponse> {
return this.request({ action: 'destroy_profile', profile }, { serialize: true })
}
destroy(sessionId: string): Promise<AgentBridgeResponse> {
return this.request({ action: 'destroy', session_id: sessionId })
getHistory(sessionId: string, profile?: string): Promise<AgentBridgeResponse> {
return this.request({
action: 'get_history',
session_id: sessionId,
...(profile ? { profile } : {}),
})
}
destroy(sessionId: string, profile?: string): Promise<AgentBridgeResponse> {
return this.request({
action: 'destroy',
session_id: sessionId,
...(profile ? { profile } : {}),
})
}
list(): Promise<AgentBridgeResponse> {
@@ -423,7 +466,7 @@ export class AgentBridgeClient {
}
shutdown(): Promise<AgentBridgeResponse> {
return this.request({ action: 'shutdown' })
return this.request({ action: 'shutdown' }, { serialize: true })
}
}