summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMariano Uvalle <[email protected]>2025-08-15 04:16:40 -0700
committerGitHub <[email protected]>2025-08-15 06:16:40 -0500
commit0befc5d602dc6306c86a450b1956c66543162a82 (patch)
tree8ce6542fee47e1b877aac2413e72d6d4495e5774
parent8355ee2061a8f4f6f8e0450dfe4c6aa91b29ab90 (diff)
downloadopencode-0befc5d602dc6306c86a450b1956c66543162a82.tar.gz
opencode-0befc5d602dc6306c86a450b1956c66543162a82.zip
Feat: Render tool metadata after permission rejection. (#1949)
Signed-off-by: jmug <[email protected]>
-rw-r--r--packages/opencode/src/permission/index.ts7
-rw-r--r--packages/opencode/src/session/index.ts1
-rw-r--r--packages/opencode/src/session/message-v2.ts1
-rw-r--r--packages/sdk/go/session.go12
-rw-r--r--packages/sdk/js/src/gen/types.gen.ts3
-rw-r--r--packages/tui/internal/components/chat/message.go19
6 files changed, 34 insertions, 9 deletions
diff --git a/packages/opencode/src/permission/index.ts b/packages/opencode/src/permission/index.ts
index 98f9c3fff..b84081ae9 100644
--- a/packages/opencode/src/permission/index.ts
+++ b/packages/opencode/src/permission/index.ts
@@ -62,7 +62,7 @@ export namespace Permission {
async (state) => {
for (const pending of Object.values(state.pending)) {
for (const item of Object.values(pending)) {
- item.reject(new RejectedError(item.info.sessionID, item.info.id, item.info.callID))
+ item.reject(new RejectedError(item.info.sessionID, item.info.id, item.info.callID, item.info.metadata))
}
}
},
@@ -105,7 +105,7 @@ export namespace Permission {
}).then((x) => x.status)
) {
case "deny":
- throw new RejectedError(info.sessionID, info.id, info.callID)
+ throw new RejectedError(info.sessionID, info.id, info.callID, info.metadata)
case "allow":
return
}
@@ -131,7 +131,7 @@ export namespace Permission {
if (!match) return
delete pending[input.sessionID][input.permissionID]
if (input.response === "reject") {
- match.reject(new RejectedError(input.sessionID, input.permissionID, match.info.callID))
+ match.reject(new RejectedError(input.sessionID, input.permissionID, match.info.callID, match.info.metadata))
return
}
match.resolve()
@@ -156,6 +156,7 @@ export namespace Permission {
public readonly sessionID: string,
public readonly permissionID: string,
public readonly toolCallID?: string,
+ public readonly metadata?: Record<string, any>,
) {
super(`The user rejected permission to use this specific tool call. You may try again with different parameters.`)
}
diff --git a/packages/opencode/src/session/index.ts b/packages/opencode/src/session/index.ts
index 741f965f9..843b1f02d 100644
--- a/packages/opencode/src/session/index.ts
+++ b/packages/opencode/src/session/index.ts
@@ -1268,6 +1268,7 @@ export namespace Session {
status: "error",
input: value.input,
error: (value.error as any).toString(),
+ metadata: value.error instanceof Permission.RejectedError ? value.error.metadata : undefined,
time: {
start: match.state.time.start,
end: Date.now(),
diff --git a/packages/opencode/src/session/message-v2.ts b/packages/opencode/src/session/message-v2.ts
index 6fabd5219..be09d31d8 100644
--- a/packages/opencode/src/session/message-v2.ts
+++ b/packages/opencode/src/session/message-v2.ts
@@ -64,6 +64,7 @@ export namespace MessageV2 {
status: z.literal("error"),
input: z.record(z.any()),
error: z.string(),
+ metadata: z.record(z.any()).optional(),
time: z.object({
start: z.number(),
end: z.number(),
diff --git a/packages/sdk/go/session.go b/packages/sdk/go/session.go
index 76a9d46fb..c6f197a71 100644
--- a/packages/sdk/go/session.go
+++ b/packages/sdk/go/session.go
@@ -2023,17 +2023,19 @@ func (r toolStateCompletedTimeJSON) RawJSON() string {
}
type ToolStateError struct {
- Error string `json:"error,required"`
- Input map[string]interface{} `json:"input,required"`
- Status ToolStateErrorStatus `json:"status,required"`
- Time ToolStateErrorTime `json:"time,required"`
- JSON toolStateErrorJSON `json:"-"`
+ Error string `json:"error,required"`
+ Input map[string]interface{} `json:"input,required"`
+ Metadata map[string]interface{} `json:"metadata"`
+ Status ToolStateErrorStatus `json:"status,required"`
+ Time ToolStateErrorTime `json:"time,required"`
+ JSON toolStateErrorJSON `json:"-"`
}
// toolStateErrorJSON contains the JSON metadata for the struct [ToolStateError]
type toolStateErrorJSON struct {
Error apijson.Field
Input apijson.Field
+ Metadata apijson.Field
Status apijson.Field
Time apijson.Field
raw string
diff --git a/packages/sdk/js/src/gen/types.gen.ts b/packages/sdk/js/src/gen/types.gen.ts
index 59294057f..dbd69bcb6 100644
--- a/packages/sdk/js/src/gen/types.gen.ts
+++ b/packages/sdk/js/src/gen/types.gen.ts
@@ -347,6 +347,9 @@ export type ToolStateError = {
input: {
[key: string]: unknown
}
+ metadata: {
+ [key: string]: unknown
+ }
error: string
time: {
start: number
diff --git a/packages/tui/internal/components/chat/message.go b/packages/tui/internal/components/chat/message.go
index 188dfee87..c72ad6e24 100644
--- a/packages/tui/internal/components/chat/message.go
+++ b/packages/tui/internal/components/chat/message.go
@@ -554,6 +554,17 @@ func renderToolDetails(
title := renderToolTitle(toolCall, width)
title = style.Render(title)
content := title + "\n" + body
+
+ if toolCall.State.Status == opencode.ToolPartStateStatusError {
+ errorStyle := styles.NewStyle().
+ Background(backgroundColor).
+ Foreground(t.Error()).
+ Padding(1, 2).
+ Width(width - 4)
+ errorContent := errorStyle.Render(toolCall.State.Error)
+ content += "\n" + errorContent
+ }
+
if permissionContent != "" {
permissionContent = styles.NewStyle().
Background(backgroundColor).
@@ -652,11 +663,17 @@ func renderToolDetails(
}
if error != "" {
- body = styles.NewStyle().
+ errorContent := styles.NewStyle().
Width(width - 6).
Foreground(t.Error()).
Background(backgroundColor).
Render(error)
+
+ if body == "" {
+ body = errorContent
+ } else {
+ body += "\n\n" + errorContent
+ }
}
if body == "" && error == "" && result != nil {