Improve codex activity log tool-call summaries
This commit is contained in:
@@ -73,6 +73,29 @@ describe("curateAgentActivity", () => {
|
||||
expect(result).toContain("[Shell] npm test");
|
||||
});
|
||||
|
||||
it("renders terminal tool calls as one-line command summaries", () => {
|
||||
const timeline: AgentTimelineItem[] = [
|
||||
toolCallItem({
|
||||
callId: "terminal-1",
|
||||
name: "terminal",
|
||||
detail: {
|
||||
type: "plain_text",
|
||||
label: `skills/paseo-chat/bin/chat.sh post --room storage-revamp --body $'first line
|
||||
|
||||
second line'`,
|
||||
icon: "square_terminal",
|
||||
},
|
||||
}),
|
||||
];
|
||||
|
||||
const result = curateAgentActivity(timeline);
|
||||
|
||||
expect(result).toContain(
|
||||
"[Terminal] skills/paseo-chat/bin/chat.sh post --room storage-revamp --body $'first line second line'",
|
||||
);
|
||||
expect(result).not.toContain("[Interacted with terminal]");
|
||||
});
|
||||
|
||||
it("does not infer summary from raw input when detail is missing", () => {
|
||||
const timeline: AgentTimelineItem[] = [
|
||||
toolCallItem({
|
||||
|
||||
@@ -4,6 +4,7 @@ import { buildToolCallDisplayModel } from "../../shared/tool-call-display.js";
|
||||
|
||||
const DEFAULT_MAX_ITEMS = 40;
|
||||
const MAX_TOOL_INPUT_CHARS = 400;
|
||||
const MAX_TOOL_SUMMARY_CHARS = 200;
|
||||
|
||||
function appendText(buffer: string, text: string): string {
|
||||
const normalized = text.trim();
|
||||
@@ -45,6 +46,20 @@ function formatToolInputJson(input: unknown): string | null {
|
||||
}
|
||||
}
|
||||
|
||||
function formatToolSummary(summary: string | undefined): string | null {
|
||||
if (typeof summary !== "string") {
|
||||
return null;
|
||||
}
|
||||
const normalized = summary.replace(/\s+/g, " ").trim();
|
||||
if (!normalized) {
|
||||
return null;
|
||||
}
|
||||
if (normalized.length <= MAX_TOOL_SUMMARY_CHARS) {
|
||||
return normalized;
|
||||
}
|
||||
return `${normalized.slice(0, MAX_TOOL_SUMMARY_CHARS - 3)}...`;
|
||||
}
|
||||
|
||||
function hasNonEmptyObject(value: unknown): boolean {
|
||||
return (
|
||||
typeof value === "object" &&
|
||||
@@ -215,7 +230,7 @@ export function curateAgentActivity(
|
||||
metadata: item.metadata,
|
||||
});
|
||||
const displayName = display.displayName;
|
||||
const summary = display.summary;
|
||||
const summary = formatToolSummary(display.summary);
|
||||
if (isLikelyExternalToolName(item.name) && inputJson) {
|
||||
lines.push(`[${displayName}] ${inputJson}`);
|
||||
break;
|
||||
|
||||
@@ -2039,6 +2039,7 @@ class CodexAppServerAgentSession implements AgentSession {
|
||||
private pendingCommandOutputDeltas = new Map<string, string[]>();
|
||||
private pendingFileChangeOutputDeltas = new Map<string, string[]>();
|
||||
private terminalCommandByProcessId = new Map<string, string>();
|
||||
private pendingUnlabeledTerminalInteractions = new Set<string>();
|
||||
private emittedTerminalInteractionKeys = new Set<string>();
|
||||
private emittedExecCommandStartedCallIds = new Set<string>();
|
||||
private emittedExecCommandCompletedCallIds = new Set<string>();
|
||||
@@ -3000,12 +3001,16 @@ class CodexAppServerAgentSession implements AgentSession {
|
||||
if (!this.shouldEmitTerminalInteractionKey(interactionKey)) {
|
||||
return;
|
||||
}
|
||||
const command =
|
||||
(parsed.processId ? this.terminalCommandByProcessId.get(parsed.processId) : undefined) ??
|
||||
null;
|
||||
if (!command && parsed.processId) {
|
||||
this.pendingUnlabeledTerminalInteractions.add(parsed.processId);
|
||||
}
|
||||
const timelineItem = mapCodexTerminalInteractionToToolCall({
|
||||
processId: parsed.processId,
|
||||
fallbackCallId: parsed.callId,
|
||||
command:
|
||||
(parsed.processId ? this.terminalCommandByProcessId.get(parsed.processId) : undefined) ??
|
||||
null,
|
||||
command,
|
||||
});
|
||||
this.emitEvent({ type: "timeline", provider: CODEX_PROVIDER, item: timelineItem });
|
||||
return;
|
||||
@@ -3217,6 +3222,18 @@ class CodexAppServerAgentSession implements AgentSession {
|
||||
return;
|
||||
}
|
||||
this.terminalCommandByProcessId.set(processId, displayCommand);
|
||||
if (!this.pendingUnlabeledTerminalInteractions.has(processId)) {
|
||||
return;
|
||||
}
|
||||
this.pendingUnlabeledTerminalInteractions.delete(processId);
|
||||
this.emitEvent({
|
||||
type: "timeline",
|
||||
provider: CODEX_PROVIDER,
|
||||
item: mapCodexTerminalInteractionToToolCall({
|
||||
processId,
|
||||
command: displayCommand,
|
||||
}),
|
||||
});
|
||||
}
|
||||
|
||||
private shouldEmitTerminalInteractionKey(key: string): boolean {
|
||||
|
||||
@@ -119,7 +119,7 @@ describe("shared tool-call display mapping", () => {
|
||||
});
|
||||
|
||||
expect(display).toEqual({
|
||||
displayName: "Interacted with terminal",
|
||||
displayName: "Terminal",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -136,7 +136,7 @@ describe("shared tool-call display mapping", () => {
|
||||
});
|
||||
|
||||
expect(display).toEqual({
|
||||
displayName: "Interacted with terminal",
|
||||
displayName: "Terminal",
|
||||
summary: "npm run test",
|
||||
});
|
||||
});
|
||||
|
||||
@@ -129,7 +129,7 @@ function buildUnknownDetailOverride(input: ToolCallDisplayInput): DetailDisplay
|
||||
}
|
||||
if (lowerName === "terminal") {
|
||||
return {
|
||||
displayName: "Interacted with terminal",
|
||||
displayName: "Terminal",
|
||||
summary: input.detail.type === "plain_text" ? readString(input.detail.label) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user