detached 41cac35

fix(subagents): redact tool result snapshots

commit
41cac35a205969cf5bb66c3ba66cc1989e6689bb
author
brice · · 2026-08-11 03:48 UTC
parent
997f27c
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
3 files · +5 -33
M packages/agent/subagents/persist.go +1 -3
@@ -790,9 +790,7 @@ func replayEventTranscript(a *Agent, ev Event) {
790790 a.appendTranscript("tool: " + name)
791791 }
792792 case "tool_result", EventToolFinished:
793- if text := eventContentText(ev.Data); text != "" {
794- a.appendTranscript("tool result: " + truncate(strings.ReplaceAll(text, "\n", " "), 240))
795- }
793+ a.appendTranscript("tool result: completed")
796794 case "stdout":
797795 if txt, _ := ev.Data["text"].(string); txt != "" {
798796 a.appendTranscript(txt)
@@ -790,9 +790,7 @@ func replayEventTranscript(a *Agent, ev Event) {
790 a.appendTranscript("tool: " + name)790 a.appendTranscript("tool: " + name)
791 }791 }
792 case "tool_result", EventToolFinished:792 case "tool_result", EventToolFinished:
793- if text := eventContentText(ev.Data); text != "" {793+ a.appendTranscript("tool result: completed")
794- a.appendTranscript("tool result: " + truncate(strings.ReplaceAll(text, "\n", " "), 240))
795- }
796 case "stdout":794 case "stdout":
797 if txt, _ := ev.Data["text"].(string); txt != "" {795 if txt, _ := ev.Data["text"].(string); txt != "" {
798 a.appendTranscript(txt)796 a.appendTranscript(txt)
M packages/agent/subagents/runner.go +3 -29
@@ -1009,9 +1009,9 @@ func applyEventToSink(ev Event, sink Sink) {
10091009 sink.Transcript("tool: " + name)
10101010 }
10111011 case "tool_result", EventToolFinished:
1012- if text := eventContentText(ev.Data); text != "" {
1013- sink.Transcript("tool result: " + truncate(strings.ReplaceAll(text, "\n", " "), 240))
1014- }
1012+ // Tool output can contain credentials, terminal controls, and arbitrary
1013+ // binary text. The dashboard only needs to show that the call completed.
1014+ sink.Transcript("tool result: completed")
10151015 sink.Activity("idle")
10161016 case "turn_end", EventTurnResult, EventAgentIdle:
10171017 sink.Activity("idle")
@@ -1028,32 +1028,6 @@ func applyEventToSink(ev Event, sink Sink) {
10281028 }
10291029 }
10301030
1031-// eventContentText extracts the human-readable text carried by a tool-result
1032-// event. The event stream is permissive across provider adapters, so accept
1033-// both the canonical block array and simple string payloads.
1034-func eventContentText(data map[string]any) string {
1035- for _, key := range []string{"content", "output", "text"} {
1036- switch value := data[key].(type) {
1037- case string:
1038- if value != "" {
1039- return value
1040- }
1041- case []any:
1042- var text []string
1043- for _, item := range value {
1044- block, _ := item.(map[string]any)
1045- if value, _ := block["text"].(string); value != "" {
1046- text = append(text, value)
1047- }
1048- }
1049- if len(text) > 0 {
1050- return strings.Join(text, "\n")
1051- }
1052- }
1053- }
1054- return ""
1055-}
1056-
10571031 // RunnerFunc adapts a plain function into a Runner. Useful for tests
10581032 // and for callers who don't need their own type.
10591033 type RunnerFunc func(ctx context.Context, sink Sink) error
@@ -1009,9 +1009,9 @@ func applyEventToSink(ev Event, sink Sink) {
1009 sink.Transcript("tool: " + name)1009 sink.Transcript("tool: " + name)
1010 }1010 }
1011 case "tool_result", EventToolFinished:1011 case "tool_result", EventToolFinished:
1012- if text := eventContentText(ev.Data); text != "" {1012+ // Tool output can contain credentials, terminal controls, and arbitrary
1013- sink.Transcript("tool result: " + truncate(strings.ReplaceAll(text, "\n", " "), 240))1013+ // binary text. The dashboard only needs to show that the call completed.
1014- }1014+ sink.Transcript("tool result: completed")
1015 sink.Activity("idle")1015 sink.Activity("idle")
1016 case "turn_end", EventTurnResult, EventAgentIdle:1016 case "turn_end", EventTurnResult, EventAgentIdle:
1017 sink.Activity("idle")1017 sink.Activity("idle")
@@ -1028,32 +1028,6 @@ func applyEventToSink(ev Event, sink Sink) {
1028 }1028 }
1029 }1029 }
1030 1030
1031-// eventContentText extracts the human-readable text carried by a tool-result
1032-// event. The event stream is permissive across provider adapters, so accept
1033-// both the canonical block array and simple string payloads.
1034-func eventContentText(data map[string]any) string {
1035- for _, key := range []string{"content", "output", "text"} {
1036- switch value := data[key].(type) {
1037- case string:
1038- if value != "" {
1039- return value
1040- }
1041- case []any:
1042- var text []string
1043- for _, item := range value {
1044- block, _ := item.(map[string]any)
1045- if value, _ := block["text"].(string); value != "" {
1046- text = append(text, value)
1047- }
1048- }
1049- if len(text) > 0 {
1050- return strings.Join(text, "\n")
1051- }
1052- }
1053- }
1054- return ""
1055-}
1056-
1057 // RunnerFunc adapts a plain function into a Runner. Useful for tests1031 // RunnerFunc adapts a plain function into a Runner. Useful for tests
1058 // and for callers who don't need their own type.1032 // and for callers who don't need their own type.
1059 type RunnerFunc func(ctx context.Context, sink Sink) error1033 type RunnerFunc func(ctx context.Context, sink Sink) error
M packages/agent/subagents/runner_test.go +1 -1
@@ -808,7 +808,7 @@ func TestApplyEventToSinkProjectsToolActivityIntoTranscript(t *testing.T) {
808808 "content": []any{map[string]any{"text": "command output"}},
809809 }},
810810 }
811- want := []string{"tool: bash", "tool result: command output"}
811+ want := []string{"tool: bash", "tool result: completed"}
812812
813813 live := &Agent{}
814814 for _, event := range events {
@@ -808,7 +808,7 @@ func TestApplyEventToSinkProjectsToolActivityIntoTranscript(t *testing.T) {
808 "content": []any{map[string]any{"text": "command output"}},808 "content": []any{map[string]any{"text": "command output"}},
809 }},809 }},
810 }810 }
811- want := []string{"tool: bash", "tool result: command output"}811+ want := []string{"tool: bash", "tool result: completed"}
812 812
813 live := &Agent{}813 live := &Agent{}
814 for _, event := range events {814 for _, event := range events {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 477

↑↓ moveenter openesc close