fix: show operation details in OpenCode tool records
Preserve OpenCode operation titles in main-session tool summaries, including permission waits, failures and interrupted calls. Keep title trimming and length limits aligned with subagent records. Update tool display documentation and include the activity-menu wording in UI-DESIGN.md. Validation: 132 focused tests passed; main-process typecheck and changed-file lint passed. Full-suite checks were affected by concurrent activity-menu changes and a test timeout. Release note: OpenCode main-session tool records now show file paths, commands or operation descriptions beside tool names, matching subagent records.
This commit is contained in:
@@ -2517,7 +2517,11 @@ describe("OpenCodeRuntime embedded permission mediation", () => {
|
||||
await runtime.dispose();
|
||||
});
|
||||
|
||||
it("keeps non-Task tools with Task-shaped input as generic tools", async () => {
|
||||
it.each([
|
||||
[" Custom delegate ", "Custom delegate"],
|
||||
["", "OpenCode 工具:custom_delegate"],
|
||||
["x".repeat(300), "x".repeat(240)],
|
||||
])("keeps generic tool titles bounded: %s", async (title, summary) => {
|
||||
const setup = runClient([
|
||||
{
|
||||
id: "custom-tool-completed",
|
||||
@@ -2536,7 +2540,7 @@ describe("OpenCodeRuntime embedded permission mediation", () => {
|
||||
prompt: "Inspect the complete source tree.",
|
||||
},
|
||||
output: "Custom tool result.",
|
||||
title: "Custom delegate",
|
||||
title,
|
||||
metadata: {},
|
||||
time: { start: 1, end: 2 },
|
||||
},
|
||||
@@ -2559,12 +2563,73 @@ describe("OpenCodeRuntime embedded permission mediation", () => {
|
||||
callId: "call-custom-1",
|
||||
name: "custom_delegate",
|
||||
state: "completed",
|
||||
summary,
|
||||
}),
|
||||
);
|
||||
expect(events.some((event) => event.type === "subagent")).toBe(false);
|
||||
await runtime.dispose();
|
||||
});
|
||||
|
||||
it.each(["error", "interrupted"])("retains the running tool title after %s", async (ending) => {
|
||||
const part = {
|
||||
id: "part-read-1",
|
||||
callID: "call-read-1",
|
||||
type: "tool" as const,
|
||||
tool: "read",
|
||||
};
|
||||
const setup = runClient([
|
||||
{
|
||||
id: "read-running",
|
||||
type: "message.part.updated",
|
||||
properties: {
|
||||
sessionID: "session-1",
|
||||
part: {
|
||||
...part,
|
||||
state: {
|
||||
status: "running",
|
||||
input: { filePath: "README.md" },
|
||||
title: "README.md",
|
||||
time: { start: 1 },
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
...(ending === "error" ? [{
|
||||
id: "read-error",
|
||||
type: "message.part.updated" as const,
|
||||
properties: {
|
||||
sessionID: "session-1",
|
||||
part: {
|
||||
...part,
|
||||
state: {
|
||||
status: "error" as const,
|
||||
input: { filePath: "README.md" },
|
||||
error: "File not found",
|
||||
time: { start: 1, end: 2 },
|
||||
},
|
||||
},
|
||||
},
|
||||
}] : []),
|
||||
]);
|
||||
const runtime = embeddedRuntime(setup.client);
|
||||
const events: RuntimeEvent[] = [];
|
||||
await expect((async () => {
|
||||
for await (const event of runtime.run({
|
||||
requestId: "3f496642-f47d-4e0a-8944-a32c77b0d6ef",
|
||||
conversationId: "conversation-1",
|
||||
prompt: "Read README.md",
|
||||
workMode: "execute",
|
||||
}, new AbortController().signal)) {
|
||||
events.push(event);
|
||||
}
|
||||
})()).rejects.toThrow();
|
||||
expect(events.filter((event) => event.type === "tool")).toEqual([
|
||||
expect.objectContaining({ state: "running", summary: "README.md" }),
|
||||
expect.objectContaining({ state: "failed", summary: "README.md" }),
|
||||
]);
|
||||
await runtime.dispose();
|
||||
});
|
||||
|
||||
it("does not impose a default wall-clock deadline on OpenCode runs", async () => {
|
||||
vi.useFakeTimers();
|
||||
try {
|
||||
|
||||
@@ -2356,6 +2356,7 @@ export class OpenCodeRuntime implements AgentRuntime {
|
||||
string,
|
||||
{
|
||||
name: string;
|
||||
summary: string;
|
||||
state: "pending" | "running" | "completed" | "failed";
|
||||
input?: string;
|
||||
output?: string;
|
||||
@@ -2605,6 +2606,11 @@ export class OpenCodeRuntime implements AgentRuntime {
|
||||
throw new Error("OpenCode 工具调用 ID 格式无效");
|
||||
}
|
||||
const toolName = part.tool.slice(0, 200);
|
||||
const title = "title" in part.state ? part.state.title : undefined;
|
||||
const summary =
|
||||
(typeof title === "string" ? title.trim().slice(0, 240) : "") ||
|
||||
toolStates.get(callId)?.summary ||
|
||||
`OpenCode 工具:${toolName}`;
|
||||
const nativeControlTool = toolName.trim().toLowerCase() === "question";
|
||||
const state =
|
||||
part.state.status === "error" ? "failed" : part.state.status;
|
||||
@@ -2636,6 +2642,7 @@ export class OpenCodeRuntime implements AgentRuntime {
|
||||
: undefined;
|
||||
toolStates.set(callId, {
|
||||
name: toolName,
|
||||
summary,
|
||||
state,
|
||||
...(input ? { input } : {}),
|
||||
...(output ? { output } : {}),
|
||||
@@ -2657,7 +2664,7 @@ export class OpenCodeRuntime implements AgentRuntime {
|
||||
callId,
|
||||
name: toolName,
|
||||
state,
|
||||
summary: `OpenCode 工具:${toolName}`,
|
||||
summary,
|
||||
...(input ? { input } : {}),
|
||||
...(output ? { output } : {}),
|
||||
...(error ? { error } : {}),
|
||||
@@ -2811,14 +2818,15 @@ export class OpenCodeRuntime implements AgentRuntime {
|
||||
permissionRequest.tool?.callID ?? permissionRequest.id;
|
||||
const toolName = permissionRequest.permission.slice(0, 200);
|
||||
if (permissionSessionId === sessionId) {
|
||||
toolStates.set(callId, { name: toolName, state: "pending" });
|
||||
const summary = toolStates.get(callId)?.summary ?? `OpenCode 工具:${toolName}`;
|
||||
toolStates.set(callId, { name: toolName, summary, state: "pending" });
|
||||
yield {
|
||||
requestId: request.requestId,
|
||||
type: "tool",
|
||||
callId,
|
||||
name: toolName,
|
||||
state: "pending",
|
||||
summary: `OpenCode 工具:${toolName}`,
|
||||
summary,
|
||||
};
|
||||
}
|
||||
const allowKnowledge =
|
||||
@@ -2939,7 +2947,7 @@ export class OpenCodeRuntime implements AgentRuntime {
|
||||
callId,
|
||||
name: tool.name,
|
||||
state: "failed",
|
||||
summary: `OpenCode 工具:${tool.name}`,
|
||||
summary: tool.summary,
|
||||
...(tool.input ? { input: tool.input } : {}),
|
||||
...(tool.output ? { output: tool.output } : {}),
|
||||
...(tool.error ? { error: tool.error } : {}),
|
||||
|
||||
Reference in New Issue
Block a user