mirror of
https://github.com/wu736139669/hapi.git
synced 2026-11-08 01:43:07 +00:00
feat(codex): refine tool activity display
This commit is contained in:
@@ -287,19 +287,75 @@ function sanitizeUnhandledNotificationLogValue(value: unknown, depth: number = 0
|
||||
return result;
|
||||
}
|
||||
|
||||
function normalizeCollabAgentToolName(value: unknown): string | null {
|
||||
function normalizeCodexAgentToolName(value: unknown): string | null {
|
||||
const raw = asString(value);
|
||||
if (!raw) return null;
|
||||
|
||||
const normalized = raw.trim().toLowerCase().replace(/[\s_-]/g, '');
|
||||
if (normalized === 'spawnagent' || normalized === 'spawn') return 'spawn_agent';
|
||||
if (normalized === 'sendinput' || normalized === 'sendmessage') return 'send_input';
|
||||
if (normalized === 'sendinput') return 'send_input';
|
||||
if (normalized === 'sendmessage') return 'send_message';
|
||||
if (normalized === 'resumeagent' || normalized === 'resume') return 'resume_agent';
|
||||
if (normalized === 'followuptask' || normalized === 'assigntask') return 'followup_task';
|
||||
if (normalized === 'waitagent' || normalized === 'wait') return 'wait_agent';
|
||||
if (normalized === 'closeagent' || normalized === 'close') return 'close_agent';
|
||||
if (normalized === 'interruptagent' || normalized === 'interrupt') return 'interrupt_agent';
|
||||
if (normalized === 'listagents') return 'list_agents';
|
||||
return null;
|
||||
}
|
||||
|
||||
function normalizeCollabAgentToolName(value: unknown): string | null {
|
||||
const toolName = normalizeCodexAgentToolName(value);
|
||||
return toolName === 'spawn_agent'
|
||||
|| toolName === 'send_input'
|
||||
|| toolName === 'resume_agent'
|
||||
|| toolName === 'wait_agent'
|
||||
|| toolName === 'close_agent'
|
||||
? toolName
|
||||
: null;
|
||||
}
|
||||
|
||||
function parseRawToolInput(value: unknown): unknown {
|
||||
if (typeof value !== 'string') return value ?? {};
|
||||
try {
|
||||
return JSON.parse(value) as unknown;
|
||||
} catch {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
|
||||
const MULTI_AGENT_V1_NAMESPACE = 'multi_agent_v1';
|
||||
|
||||
function isRawMultiAgentV2Call(
|
||||
toolName: string,
|
||||
input: unknown,
|
||||
namespace: unknown
|
||||
): boolean {
|
||||
// V1 already has richer collabAgentToolCall lifecycle items. V2 uses the
|
||||
// configurable namespace ("collaboration" by default), so only the fixed
|
||||
// V1 namespace can be excluded here.
|
||||
if (asString(namespace) === MULTI_AGENT_V1_NAMESPACE) return false;
|
||||
|
||||
if (
|
||||
toolName === 'send_message'
|
||||
|| toolName === 'followup_task'
|
||||
|| toolName === 'interrupt_agent'
|
||||
|| toolName === 'list_agents'
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const inputRecord = asRecord(input);
|
||||
if (toolName === 'spawn_agent') {
|
||||
return Boolean(asString(inputRecord?.task_name ?? inputRecord?.taskName));
|
||||
}
|
||||
if (toolName === 'wait_agent') {
|
||||
return !Array.isArray(inputRecord?.targets);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function extractStringArray(value: unknown): string[] {
|
||||
return Array.isArray(value)
|
||||
? value.filter((entry): entry is string => typeof entry === 'string' && entry.length > 0)
|
||||
@@ -443,6 +499,8 @@ export class AppServerEventConverter {
|
||||
private readonly lastAgentMessageDeltaByItemId = new Map<string, string>();
|
||||
private readonly lastReasoningDeltaByItemId = new Map<string, string>();
|
||||
private readonly lastCommandOutputDeltaByItemId = new Map<string, string>();
|
||||
private readonly rawAgentToolCallIds = new Set<string>();
|
||||
private readonly rawAgentToolNames = new Map<string, string>();
|
||||
|
||||
private handleWrappedCodexEvent(paramsRecord: Record<string, unknown>): ConvertedEvent[] | null {
|
||||
const msg = asRecord(paramsRecord.msg);
|
||||
@@ -794,6 +852,50 @@ export class AppServerEventConverter {
|
||||
return events;
|
||||
}
|
||||
|
||||
if (method === 'rawResponseItem/completed') {
|
||||
const item = asRecord(paramsRecord.item);
|
||||
if (!item) return events;
|
||||
|
||||
const itemType = normalizeItemType(item.type);
|
||||
const callId = asString(item.call_id ?? item.callId);
|
||||
if (!itemType || !callId) return events;
|
||||
|
||||
if (itemType === 'functioncall') {
|
||||
const toolName = normalizeCodexAgentToolName(item.name);
|
||||
const input = parseRawToolInput(item.arguments);
|
||||
if (
|
||||
!toolName
|
||||
|| !isRawMultiAgentV2Call(toolName, input, item.namespace)
|
||||
|| this.rawAgentToolCallIds.has(callId)
|
||||
) return events;
|
||||
|
||||
this.rawAgentToolCallIds.add(callId);
|
||||
this.rawAgentToolNames.set(callId, toolName);
|
||||
events.push(scoped({
|
||||
type: 'codex_tool_call_begin',
|
||||
call_id: callId,
|
||||
name: toolName,
|
||||
input
|
||||
}));
|
||||
return events;
|
||||
}
|
||||
|
||||
if (itemType === 'functioncalloutput') {
|
||||
const toolName = this.rawAgentToolNames.get(callId);
|
||||
if (!toolName) return events;
|
||||
|
||||
this.rawAgentToolNames.delete(callId);
|
||||
events.push(scoped({
|
||||
type: 'codex_tool_call_end',
|
||||
call_id: callId,
|
||||
name: toolName,
|
||||
output: item.output,
|
||||
is_error: false
|
||||
}));
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
if (method === 'item/agentMessage/delta') {
|
||||
const itemId = extractItemId(paramsRecord);
|
||||
const delta = asString(paramsRecord.delta ?? paramsRecord.text ?? paramsRecord.message);
|
||||
@@ -1012,6 +1114,7 @@ export class AppServerEventConverter {
|
||||
}
|
||||
|
||||
if (itemType === 'collabagenttoolcall') {
|
||||
if (this.rawAgentToolCallIds.has(itemId)) return events;
|
||||
const toolName = normalizeCollabAgentToolName(item.tool ?? item.name);
|
||||
if (!toolName) return events;
|
||||
|
||||
@@ -1092,5 +1195,7 @@ export class AppServerEventConverter {
|
||||
this.lastAgentMessageDeltaByItemId.clear();
|
||||
this.lastReasoningDeltaByItemId.clear();
|
||||
this.lastCommandOutputDeltaByItemId.clear();
|
||||
this.rawAgentToolCallIds.clear();
|
||||
this.rawAgentToolNames.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user