[codex] fix media skill profile auth and run events (#965)

* fix media skill profile auth and run events

* test bridge run profile context
This commit is contained in:
ekko
2026-05-24 12:52:14 +08:00
committed by GitHub
parent 3e8f84aa65
commit 634a622934
20 changed files with 368 additions and 97 deletions
@@ -948,7 +948,7 @@ class AgentPool:
def _tool_progress_callback(self, session_id: str):
def callback(event_type, function_name=None, preview=None, function_args=None, **kwargs):
if event_type in (None, "tool.started", "tool.completed"):
if event_type in (None, "tool.started", "tool.completed") or str(event_type or "").startswith("subagent."):
print(
"[hermes_bridge] tool_progress_callback "
f"session={session_id} event={event_type} tool={function_name} "
@@ -964,6 +964,18 @@ class AgentPool:
})
return
if str(event_type or "").startswith("subagent."):
payload = {
"event": str(event_type),
"tool_name": str(function_name) if function_name else "",
"text": str(preview) if preview is not None else "",
"args": _jsonable(function_args) if function_args else {},
}
for key, value in kwargs.items():
payload[str(key)] = _jsonable(value)
self._append_event(session_id, payload)
return
if event_type == "_thinking":
text = function_name
if text:
@@ -473,6 +473,11 @@ class AgentClient {
return { ...block, text: `${routedPrefix}\n\n原始消息:${text || msg.content}` }
})
: `${routedPrefix}\n\n原始消息:${stripMentionRoutingTokens(msg.content, this.name) || msg.content}`
const runContext = [
`[Current Hermes profile: ${this.profile}]`,
'When calling Hermes Web UI endpoints from tools or skills, include the current Hermes profile as the X-Hermes-Profile header if the endpoint supports profile-scoped behavior.',
].join('\n')
instructions = instructions ? `${runContext}\n${instructions}` : runContext
const bridgeInput: AgentBridgeMessage = isContentBlockArray(input)
? await convertContentBlocksForAgent(input)
: input
@@ -127,11 +127,6 @@ export async function markAbortCompleted(
}
state.events = []
replaceState(sessionMap, sessionId, 'abort.completed', {
event: 'abort.completed',
run_id: runId,
synced: true,
})
emitToSession(nsp, socket, sessionId, 'abort.completed', {
event: 'abort.completed',
run_id: runId,
@@ -105,6 +105,7 @@ export async function handleApiRun(
sessionMap.set(session_id, state)
}
state.isWorking = true
state.events = []
state.profile = profile
state.source = 'api_server'
state.activeRunMarker = runMarker
@@ -128,10 +128,12 @@ export async function handleBridgeRun(
if (resolvedProvider && sessionRow.provider !== resolvedProvider) updates.provider = resolvedProvider
if (Object.keys(updates).length > 0) updateSession(session_id, updates)
}
if (sessionRow?.workspace) {
const workspaceCtx = `[Current working directory: ${sessionRow.workspace}]`
fullInstructions = `\n${workspaceCtx}\n${fullInstructions}`
}
const runContext = [
`[Current Hermes profile: ${profile}]`,
sessionRow?.workspace ? `[Current working directory: ${sessionRow.workspace}]` : '',
'When calling Hermes Web UI endpoints from tools or skills, include the current Hermes profile as the X-Hermes-Profile header if the endpoint supports profile-scoped behavior.',
].filter(Boolean).join('\n')
fullInstructions = `\n${runContext}\n${fullInstructions}`
const runMarker = `cli_run_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`
const now = Math.floor(Date.now() / 1000)
@@ -145,6 +147,7 @@ export async function handleBridgeRun(
state.isWorking = true
state.isAborting = false
state.events = []
state.profile = profile
state.source = 'cli'
state.activeRunMarker = runMarker
@@ -491,6 +494,38 @@ async function applyBridgeChunkAsync(
}
pushState(sessionMap, sessionId, 'tool.completed', payload)
emit('tool.completed', payload)
} else if (evType?.startsWith('subagent.')) {
const payload = {
event: evType,
run_id: chunk.run_id,
subagent_id: ev.subagent_id,
parent_id: ev.parent_id,
depth: ev.depth,
task_index: ev.task_index,
task_count: ev.task_count,
goal: ev.goal,
model: ev.model,
toolsets: ev.toolsets,
tool_count: ev.tool_count,
tool: ev.tool_name,
name: ev.tool_name,
preview: ev.text || ev.summary || ev.tool_preview || '',
text: ev.text || '',
status: ev.status,
summary: ev.summary,
duration: ev.duration_seconds,
duration_seconds: ev.duration_seconds,
input_tokens: ev.input_tokens,
output_tokens: ev.output_tokens,
reasoning_tokens: ev.reasoning_tokens,
api_calls: ev.api_calls,
cost_usd: ev.cost_usd,
files_read: ev.files_read,
files_written: ev.files_written,
output_tail: ev.output_tail,
}
pushState(sessionMap, sessionId, evType, payload)
emit(evType, payload)
} else if (evType === 'turn.boundary') {
flushBridgePendingToDb(state, sessionId, runMarker)
} else if (evType === 'reasoning.delta' || evType === 'thinking.delta') {
@@ -168,6 +168,7 @@ export class ChatRunSocket {
logger.info('[chat-run-socket] queued run for session %s (queue: %d)', data.session_id, state.queue.length)
return
}
state.events = []
state.isWorking = true
state.profile = runProfile
state.source = source