Skip to content

Commit 8570e16

Browse files
committed
fix(cli): don't block opencode tool results on the trace toast
Show the trace toast without awaiting it, so a slow or missing TUI never holds back the tool result, and only append the model relay when the shell tool returned string output, since an aborted tool may carry none. Assisted-by: Claude Code Signed-off-by: Miguel Martinez Trivino <miguel@chainloop.dev> Chainloop-Trace-Sessions: a3931907-c590-40a1-90c6-2c29aa204404
1 parent 393493b commit 8570e16

4 files changed

Lines changed: 32 additions & 16 deletions

File tree

‎.opencode/plugins/chainloop-trace.ts‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export const ChainloopTrace: Plugin = async ({ $, client }) => {
7676
if (event.type === "session.created") {
7777
const sessionID = event.properties?.info?.id ?? ""
7878
const res = await fire("session-start", { session_id: sessionID, hook_event_name: "session.created" })
79-
if (res.message) await toast(res.message)
79+
if (res.message) void toast(res.message)
8080
}
8181
if (event.type === "session.deleted") {
8282
const sessionID = event.properties?.info?.id ?? ""
@@ -112,9 +112,13 @@ export const ChainloopTrace: Plugin = async ({ $, client }) => {
112112
// A shell command may have been a git push, whose pre-push hook
113113
// attested the session and left a link to it. Show it on both
114114
// channels: the toast reaches the user now, the tool output reaches
115-
// the model, whose reply outlives the toast.
116-
if (res.message) await toast(res.message)
117-
if (res.relayToModel) output.output = output.output + "\n\n" + res.relayToModel
115+
// the model, whose reply outlives the toast. The toast is not awaited
116+
// so a slow TUI never holds back the tool result, and an aborted tool
117+
// may carry no output to append to.
118+
if (res.message) void toast(res.message)
119+
if (res.relayToModel && typeof output?.output === "string") {
120+
output.output = output.output + "\n\n" + res.relayToModel
121+
}
118122
return
119123
}
120124
if (!fileWritingTools.includes(input.tool)) return

‎app/cli/internal/trace/opencode/hooks.go‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -135,7 +135,7 @@ export const ChainloopTrace: Plugin = async ({ $, client }) => {
135135
if (event.type === "session.created") {
136136
const sessionID = event.properties?.info?.id ?? ""
137137
const res = await fire("session-start", { session_id: sessionID, hook_event_name: "session.created" })
138-
if (res.message) await toast(res.message)
138+
if (res.message) void toast(res.message)
139139
}
140140
{{SessionEndBlock}}
141141
},
@@ -168,9 +168,13 @@ export const ChainloopTrace: Plugin = async ({ $, client }) => {
168168
// A shell command may have been a git push, whose pre-push hook
169169
// attested the session and left a link to it. Show it on both
170170
// channels: the toast reaches the user now, the tool output reaches
171-
// the model, whose reply outlives the toast.
172-
if (res.message) await toast(res.message)
173-
if (res.relayToModel) output.output = output.output + "\n\n" + res.relayToModel
171+
// the model, whose reply outlives the toast. The toast is not awaited
172+
// so a slow TUI never holds back the tool result, and an aborted tool
173+
// may carry no output to append to.
174+
if (res.message) void toast(res.message)
175+
if (res.relayToModel && typeof output?.output === "string") {
176+
output.output = output.output + "\n\n" + res.relayToModel
177+
}
174178
return
175179
}
176180
if (!fileWritingTools.includes(input.tool)) return

‎app/cli/internal/trace/opencode/testdata/plugin_full.ts‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export const ChainloopTrace: Plugin = async ({ $, client }) => {
7676
if (event.type === "session.created") {
7777
const sessionID = event.properties?.info?.id ?? ""
7878
const res = await fire("session-start", { session_id: sessionID, hook_event_name: "session.created" })
79-
if (res.message) await toast(res.message)
79+
if (res.message) void toast(res.message)
8080
}
8181
if (event.type === "session.deleted") {
8282
const sessionID = event.properties?.info?.id ?? ""
@@ -112,9 +112,13 @@ export const ChainloopTrace: Plugin = async ({ $, client }) => {
112112
// A shell command may have been a git push, whose pre-push hook
113113
// attested the session and left a link to it. Show it on both
114114
// channels: the toast reaches the user now, the tool output reaches
115-
// the model, whose reply outlives the toast.
116-
if (res.message) await toast(res.message)
117-
if (res.relayToModel) output.output = output.output + "\n\n" + res.relayToModel
115+
// the model, whose reply outlives the toast. The toast is not awaited
116+
// so a slow TUI never holds back the tool result, and an aborted tool
117+
// may carry no output to append to.
118+
if (res.message) void toast(res.message)
119+
if (res.relayToModel && typeof output?.output === "string") {
120+
output.output = output.output + "\n\n" + res.relayToModel
121+
}
118122
return
119123
}
120124
if (!fileWritingTools.includes(input.tool)) return

‎app/cli/internal/trace/opencode/testdata/plugin_tracerun.ts‎

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export const ChainloopTrace: Plugin = async ({ $, client }) => {
7676
if (event.type === "session.created") {
7777
const sessionID = event.properties?.info?.id ?? ""
7878
const res = await fire("session-start", { session_id: sessionID, hook_event_name: "session.created" })
79-
if (res.message) await toast(res.message)
79+
if (res.message) void toast(res.message)
8080
}
8181
},
8282
"tool.execute.before": async (input, output) => {
@@ -108,9 +108,13 @@ export const ChainloopTrace: Plugin = async ({ $, client }) => {
108108
// A shell command may have been a git push, whose pre-push hook
109109
// attested the session and left a link to it. Show it on both
110110
// channels: the toast reaches the user now, the tool output reaches
111-
// the model, whose reply outlives the toast.
112-
if (res.message) await toast(res.message)
113-
if (res.relayToModel) output.output = output.output + "\n\n" + res.relayToModel
111+
// the model, whose reply outlives the toast. The toast is not awaited
112+
// so a slow TUI never holds back the tool result, and an aborted tool
113+
// may carry no output to append to.
114+
if (res.message) void toast(res.message)
115+
if (res.relayToModel && typeof output?.output === "string") {
116+
output.output = output.output + "\n\n" + res.relayToModel
117+
}
114118
return
115119
}
116120
if (!fileWritingTools.includes(input.tool)) return

0 commit comments

Comments
 (0)