Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
Menu
Open sidebar
OpenDAS
ollama
Commits
53a5a9e9
Unverified
Commit
53a5a9e9
authored
Jan 08, 2026
by
Parth Sareen
Committed by
GitHub
Jan 08, 2026
Browse files
x: redesign agent UI with minimal styling (#13650)
parent
e30e08a7
Changes
2
Show whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
95 additions
and
135 deletions
+95
-135
x/agent/approval.go
x/agent/approval.go
+72
-113
x/cmd/run.go
x/cmd/run.go
+23
-22
No files found.
x/agent/approval.go
View file @
53a5a9e9
...
...
@@ -37,6 +37,25 @@ var optionLabels = []string{
"3. Deny"
,
}
// toolDisplayNames maps internal tool names to human-readable display names.
var
toolDisplayNames
=
map
[
string
]
string
{
"bash"
:
"Bash"
,
"web_search"
:
"Web Search"
,
}
// ToolDisplayName returns the human-readable display name for a tool.
func
ToolDisplayName
(
toolName
string
)
string
{
if
displayName
,
ok
:=
toolDisplayNames
[
toolName
];
ok
{
return
displayName
}
// Default: capitalize first letter and replace underscores with spaces
name
:=
strings
.
ReplaceAll
(
toolName
,
"_"
,
" "
)
if
len
(
name
)
>
0
{
return
strings
.
ToUpper
(
name
[
:
1
])
+
name
[
1
:
]
}
return
toolName
}
// autoAllowCommands are commands that are always allowed without prompting.
// These are zero-risk, read-only commands.
var
autoAllowCommands
=
map
[
string
]
bool
{
...
...
@@ -509,11 +528,12 @@ func (a *ApprovalManager) RequestApproval(toolName string, args map[string]any)
// formatToolDisplay creates the display string for a tool call.
func
formatToolDisplay
(
toolName
string
,
args
map
[
string
]
any
)
string
{
var
sb
strings
.
Builder
displayName
:=
ToolDisplayName
(
toolName
)
// For bash, show command directly
if
toolName
==
"bash"
{
if
cmd
,
ok
:=
args
[
"command"
]
.
(
string
);
ok
{
sb
.
WriteString
(
fmt
.
Sprintf
(
"Tool: %s
\n
"
,
tool
Name
))
sb
.
WriteString
(
fmt
.
Sprintf
(
"Tool: %s
\n
"
,
display
Name
))
sb
.
WriteString
(
fmt
.
Sprintf
(
"Command: %s"
,
cmd
))
return
sb
.
String
()
}
...
...
@@ -522,7 +542,7 @@ func formatToolDisplay(toolName string, args map[string]any) string {
// For web search, show query and internet notice
if
toolName
==
"web_search"
{
if
query
,
ok
:=
args
[
"query"
]
.
(
string
);
ok
{
sb
.
WriteString
(
fmt
.
Sprintf
(
"Tool: %s
\n
"
,
tool
Name
))
sb
.
WriteString
(
fmt
.
Sprintf
(
"Tool: %s
\n
"
,
display
Name
))
sb
.
WriteString
(
fmt
.
Sprintf
(
"Query: %s
\n
"
,
query
))
sb
.
WriteString
(
"Uses internet via ollama.com"
)
return
sb
.
String
()
...
...
@@ -530,7 +550,7 @@ func formatToolDisplay(toolName string, args map[string]any) string {
}
// Generic display
sb
.
WriteString
(
fmt
.
Sprintf
(
"Tool: %s"
,
tool
Name
))
sb
.
WriteString
(
fmt
.
Sprintf
(
"Tool: %s"
,
display
Name
))
if
len
(
args
)
>
0
{
sb
.
WriteString
(
"
\n
Arguments: "
)
first
:=
true
...
...
@@ -724,7 +744,7 @@ func wrapText(text string, maxWidth int) []string {
// getHintLines returns the hint text wrapped to terminal width
func
getHintLines
(
state
*
selectorState
)
[]
string
{
hint
:=
"
↑/↓ navigate
,
E
nter confirm, 1-3 quick,
C
trl+
C
cancel"
hint
:=
"
up/down select
,
e
nter confirm, 1-3 quick
select
,
c
trl+
c
cancel"
if
state
.
termWidth
>=
len
(
hint
)
+
1
{
return
[]
string
{
hint
}
}
...
...
@@ -734,86 +754,60 @@ func getHintLines(state *selectorState) []string {
// calculateTotalLines calculates how many lines the selector will use
func
calculateTotalLines
(
state
*
selectorState
)
int
{
toolLines
:=
wrapTex
t
(
state
.
toolDisplay
,
state
.
innerWidth
)
toolLines
:=
strings
.
Spli
t
(
state
.
toolDisplay
,
"
\n
"
)
hintLines
:=
getHintLines
(
state
)
//
top border + (
warning line if applicable) + tool lines +
separator
+ options + b
ottom border
+ hint lines
// warning line
(
if applicable) + tool lines +
blank line
+ options + b
lank line
+ hint lines
warningLines
:=
0
if
state
.
isWarning
{
warningLines
=
1
warningLines
=
2
// warning line + blank line after
}
return
1
+
warningLines
+
len
(
toolLines
)
+
1
+
len
(
optionLabels
)
+
1
+
len
(
hintLines
)
return
warningLines
+
len
(
toolLines
)
+
1
+
len
(
optionLabels
)
+
1
+
len
(
hintLines
)
}
// renderSelectorBox renders the
complete selector
box
// renderSelectorBox renders the
selector (minimal, no
box
)
func
renderSelectorBox
(
state
*
selectorState
)
{
toolLines
:=
wrapTex
t
(
state
.
toolDisplay
,
state
.
innerWidth
)
toolLines
:=
strings
.
Spli
t
(
state
.
toolDisplay
,
"
\n
"
)
hintLines
:=
getHintLines
(
state
)
// Use red for warning (outside cwd), cyan for normal
boxColor
:=
"
\0
33[36m"
// cyan
// Draw warning line if needed
if
state
.
isWarning
{
boxColor
=
"
\0
33[91m"
// bright red
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[1mwarning:
\0
33[0m command targets paths outside project
\0
33[K
\r\n
"
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[K
\r\n
"
)
// blank line after warning
}
// Draw box top
fmt
.
Fprintf
(
os
.
Stderr
,
"%s┌%s┐
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
strings
.
Repeat
(
"─"
,
state
.
boxWidth
-
2
))
// Draw warning line if needed (inside the box)
if
state
.
isWarning
{
warning
:=
"!! OUTSIDE PROJECT !!"
padding
:=
(
state
.
innerWidth
-
len
(
warning
))
/
2
if
padding
<
0
{
padding
=
0
}
fmt
.
Fprintf
(
os
.
Stderr
,
"%s│
\0
33[0m %s%s%s %s│
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
strings
.
Repeat
(
" "
,
padding
),
warning
,
strings
.
Repeat
(
" "
,
state
.
innerWidth
-
len
(
warning
)
-
padding
),
boxColor
)
}
// Draw tool info
// Draw tool info (plain white)
for
_
,
line
:=
range
toolLines
{
fmt
.
Fprintf
(
os
.
Stderr
,
"%s
│
\0
33[0m %-*s %s│
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
state
.
innerWidth
,
line
,
boxColor
)
fmt
.
Fprintf
(
os
.
Stderr
,
"%s
\0
33[K
\r\n
"
,
line
)
}
//
Draw
separator
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s├%s┤
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
strings
.
Repeat
(
"─"
,
state
.
boxWidth
-
2
)
)
//
Blank line
separator
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[K
\r\n
"
)
// Draw options
with numbers (Deny option includes reason input)
// Draw options
for
i
,
label
:=
range
optionLabels
{
if
i
==
2
{
// Deny option
- show with reason input beside i
t
if
i
==
2
{
// Deny option
with inpu
t
denyLabel
:=
"3. Deny: "
availableWidth
:=
state
.
innerWidth
-
2
-
len
(
denyLabel
)
if
availableWidth
<
5
{
availableWidth
=
5
}
inputDisplay
:=
state
.
denyReason
if
len
(
inputDisplay
)
>
availableWidth
{
inputDisplay
=
inputDisplay
[
len
(
inputDisplay
)
-
availableWidth
:
]
}
if
i
==
state
.
selected
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s│
\0
33[0m
\0
33[1
;32m>
%s
\0
33[0m%
-*s %s│
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
denyLabel
,
availableWidth
,
inputDisplay
,
boxColor
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[1
m
%s
\0
33[0m%
s
\0
33[K
\r\n
"
,
denyLabel
,
inputDisplay
)
}
else
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s│
\0
33[0m
\0
33[
90
m%s
\0
33[0m%
-*s %s│
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
denyLabel
,
availableWidth
,
inputDisplay
,
boxColor
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
37
m%s
\0
33[0m%
s
\0
33[K
\r\n
"
,
denyLabel
,
inputDisplay
)
}
}
else
{
displayLabel
:=
label
if
len
(
displayLabel
)
>
state
.
innerWidth
-
2
{
displayLabel
=
displayLabel
[
:
state
.
innerWidth
-
5
]
+
"..."
}
if
i
==
state
.
selected
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s│
\0
33[0m
\0
33[1;32m> %-*s
\0
33[
0m
%s
│
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
state
.
innerWidth
-
2
,
displayLabel
,
boxColor
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
1m
%s
\0
33[0m
\0
33[K
\r\n
"
,
label
)
}
else
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s│
\0
33[
0m %-*s
%s
│
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
state
.
innerWidth
-
2
,
displayLabel
,
boxColor
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
37m
%s
\0
33[0m
\0
33[K
\r\n
"
,
label
)
}
}
}
//
Draw box bottom
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s└%s┘
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
strings
.
Repeat
(
"─"
,
state
.
boxWidth
-
2
)
)
//
Blank line before hint
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[K
\r\n
"
)
// Draw hint (
may be multiple lines
)
// Draw hint (
dark grey
)
for
i
,
line
:=
range
hintLines
{
if
i
==
len
(
hintLines
)
-
1
{
// Last line - no newline
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[90m%s
\0
33[0m
\0
33[K"
,
line
)
}
else
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[90m%s
\0
33[0m
\0
33[K
\r\n
"
,
line
)
...
...
@@ -825,50 +819,33 @@ func renderSelectorBox(state *selectorState) {
func
updateSelectorOptions
(
state
*
selectorState
)
{
hintLines
:=
getHintLines
(
state
)
// Use red for warning (outside cwd), cyan for normal
boxColor
:=
"
\0
33[36m"
// cyan
if
state
.
isWarning
{
boxColor
=
"
\0
33[91m"
// bright red
}
// Move up to the first option line
// Cursor is at end of last hint line, need to go up:
// (hint lines - 1) + 1 (b
ottom border
) + numOptions
// (hint lines - 1) + 1 (b
lank line
) + numOptions
linesToMove
:=
len
(
hintLines
)
-
1
+
1
+
len
(
optionLabels
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[%dA
\r
"
,
linesToMove
)
// Redraw options
(Deny option includes reason input)
// Redraw options
for
i
,
label
:=
range
optionLabels
{
if
i
==
2
{
// Deny option
denyLabel
:=
"3. Deny: "
availableWidth
:=
state
.
innerWidth
-
2
-
len
(
denyLabel
)
if
availableWidth
<
5
{
availableWidth
=
5
}
inputDisplay
:=
state
.
denyReason
if
len
(
inputDisplay
)
>
availableWidth
{
inputDisplay
=
inputDisplay
[
len
(
inputDisplay
)
-
availableWidth
:
]
}
if
i
==
state
.
selected
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s│
\0
33[0m
\0
33[1
;32m>
%s
\0
33[0m%
-*s %s│
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
denyLabel
,
availableWidth
,
inputDisplay
,
boxColor
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[1
m
%s
\0
33[0m%
s
\0
33[K
\r\n
"
,
denyLabel
,
inputDisplay
)
}
else
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s│
\0
33[0m
\0
33[
90
m%s
\0
33[0m%
-*s %s│
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
denyLabel
,
availableWidth
,
inputDisplay
,
boxColor
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
37
m%s
\0
33[0m%
s
\0
33[K
\r\n
"
,
denyLabel
,
inputDisplay
)
}
}
else
{
displayLabel
:=
label
if
len
(
displayLabel
)
>
state
.
innerWidth
-
2
{
displayLabel
=
displayLabel
[
:
state
.
innerWidth
-
5
]
+
"..."
}
if
i
==
state
.
selected
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s│
\0
33[0m
\0
33[1;32m> %-*s
\0
33[
0m
%s
│
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
state
.
innerWidth
-
2
,
displayLabel
,
boxColor
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
1m
%s
\0
33[0m
\0
33[K
\r\n
"
,
label
)
}
else
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s│
\0
33[
0m %-*s
%s
│
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
state
.
innerWidth
-
2
,
displayLabel
,
boxColor
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
37m
%s
\0
33[0m
\0
33[K
\r\n
"
,
label
)
}
}
}
//
Redraw bottom and
hint
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s└%s┘
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
strings
.
Repeat
(
"─"
,
state
.
boxWidth
-
2
)
)
//
Blank line +
hint
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[K
\r\n
"
)
for
i
,
line
:=
range
hintLines
{
if
i
==
len
(
hintLines
)
-
1
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[90m%s
\0
33[0m
\0
33[K"
,
line
)
...
...
@@ -882,36 +859,23 @@ func updateSelectorOptions(state *selectorState) {
func
updateReasonInput
(
state
*
selectorState
)
{
hintLines
:=
getHintLines
(
state
)
// Use red for warning (outside cwd), cyan for normal
boxColor
:=
"
\0
33[36m"
// cyan
if
state
.
isWarning
{
boxColor
=
"
\0
33[91m"
// bright red
}
// Move up to the Deny line (3rd option, index 2)
// Cursor is at end of last hint line, need to go up:
// (hint lines - 1) + 1 (b
ottom border
) + 1 (Deny is last option)
// (hint lines - 1) + 1 (b
lank line
) + 1 (Deny is last option)
linesToMove
:=
len
(
hintLines
)
-
1
+
1
+
1
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[%dA
\r
"
,
linesToMove
)
// Redraw Deny line with reason
denyLabel
:=
"3. Deny: "
availableWidth
:=
state
.
innerWidth
-
2
-
len
(
denyLabel
)
if
availableWidth
<
5
{
availableWidth
=
5
}
inputDisplay
:=
state
.
denyReason
if
len
(
inputDisplay
)
>
availableWidth
{
inputDisplay
=
inputDisplay
[
len
(
inputDisplay
)
-
availableWidth
:
]
}
if
state
.
selected
==
2
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s│
\0
33[0m
\0
33[1
;32m>
%s
\0
33[0m%
-*s %s│
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
denyLabel
,
availableWidth
,
inputDisplay
,
boxColor
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[1
m
%s
\0
33[0m%
s
\0
33[K
\r\n
"
,
denyLabel
,
inputDisplay
)
}
else
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s│
\0
33[0m
\0
33[
90
m%s
\0
33[0m%
-*s %s│
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
denyLabel
,
availableWidth
,
inputDisplay
,
boxColor
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
37
m%s
\0
33[0m%
s
\0
33[K
\r\n
"
,
denyLabel
,
inputDisplay
)
}
//
Redraw bottom and
hint
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s└%s┘
\0
33[0m
\0
33[K
\r\n
"
,
boxColor
,
strings
.
Repeat
(
"─"
,
state
.
boxWidth
-
2
)
)
//
Blank line +
hint
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[K
\r\n
"
)
for
i
,
line
:=
range
hintLines
{
if
i
==
len
(
hintLines
)
-
1
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[90m%s
\0
33[0m
\0
33[K"
,
line
)
...
...
@@ -935,11 +899,10 @@ func clearSelectorBox(state *selectorState) {
// fallbackApproval handles approval when terminal control isn't available.
func
(
a
*
ApprovalManager
)
fallbackApproval
(
toolDisplay
string
)
(
ApprovalResult
,
error
)
{
fmt
.
Fprintln
(
os
.
Stderr
)
fmt
.
Fprintln
(
os
.
Stderr
,
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
)
fmt
.
Fprintln
(
os
.
Stderr
,
toolDisplay
)
fmt
.
Fprintln
(
os
.
Stderr
,
"━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━"
)
fmt
.
Fprintln
(
os
.
Stderr
)
fmt
.
Fprintln
(
os
.
Stderr
,
"[1] Execute once [2] Always allow [3] Deny"
)
fmt
.
Fprint
(
os
.
Stderr
,
"
C
hoice: "
)
fmt
.
Fprint
(
os
.
Stderr
,
"
c
hoice: "
)
var
input
string
fmt
.
Scanln
(
&
input
)
...
...
@@ -982,19 +945,16 @@ func (a *ApprovalManager) AllowedTools() []string {
// FormatApprovalResult returns a formatted string showing the approval result.
func
FormatApprovalResult
(
toolName
string
,
args
map
[
string
]
any
,
result
ApprovalResult
)
string
{
var
status
string
var
icon
string
var
label
string
displayName
:=
ToolDisplayName
(
toolName
)
switch
result
.
Decision
{
case
ApprovalOnce
:
status
=
"Approved"
icon
=
"
\0
33[32m✓
\0
33[0m"
label
=
"approved"
case
ApprovalAlways
:
status
=
"Always allowed"
icon
=
"
\0
33[32m✓
\0
33[0m"
label
=
"always allowed"
case
ApprovalDeny
:
status
=
"Denied"
icon
=
"
\0
33[31m✗
\0
33[0m"
label
=
"denied"
}
// Format based on tool type
...
...
@@ -1004,7 +964,7 @@ func FormatApprovalResult(toolName string, args map[string]any, result ApprovalR
if
len
(
cmd
)
>
40
{
cmd
=
cmd
[
:
37
]
+
"..."
}
return
fmt
.
Sprintf
(
"
▶ bash: %s [
%s
]
%s"
,
cmd
,
status
,
icon
)
return
fmt
.
Sprintf
(
"
\0
33[1m%s:
\0
33[0m
%s
:
%s"
,
label
,
displayName
,
cmd
)
}
}
...
...
@@ -1014,11 +974,11 @@ func FormatApprovalResult(toolName string, args map[string]any, result ApprovalR
if
len
(
query
)
>
40
{
query
=
query
[
:
37
]
+
"..."
}
return
fmt
.
Sprintf
(
"
▶ web_search: %s [%s] %s"
,
query
,
status
,
icon
)
return
fmt
.
Sprintf
(
"
\0
33[1m%s:
\0
33[0m %s: %s"
,
label
,
displayName
,
query
)
}
}
return
fmt
.
Sprintf
(
"
▶ %s [%s] %s"
,
toolName
,
status
,
icon
)
return
fmt
.
Sprintf
(
"
\0
33[1m%s:
\0
33[0m %s"
,
label
,
displayName
)
}
// FormatDenyResult returns the tool result message when a tool is denied.
...
...
@@ -1049,15 +1009,14 @@ func PromptYesNo(question string) (bool, error) {
renderYesNo
:=
func
()
{
// Move to start of line and clear
fmt
.
Fprintf
(
os
.
Stderr
,
"
\r
\0
33[K"
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[36m%s
\0
33[0m
"
,
question
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s
"
,
question
)
for
i
,
opt
:=
range
options
{
if
i
==
selected
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[1
;32m[
%s
]
\0
33[0m "
,
opt
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[1
m
%s
\0
33[0m
"
,
opt
)
}
else
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
90m
%s
\0
33[0m "
,
opt
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
37m
%s
\0
33[0m
"
,
opt
)
}
}
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[90m(←/→ or y/n, Enter to confirm)
\0
33[0m"
)
}
renderYesNo
()
...
...
x/cmd/run.go
View file @
53a5a9e9
...
...
@@ -91,8 +91,8 @@ func waitForOllamaSignin(ctx context.Context) error {
var
aErr
api
.
AuthorizationError
if
errors
.
As
(
err
,
&
aErr
)
&&
aErr
.
SigninURL
!=
""
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\n
To sign in, navigate to:
\n
"
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[36m%s
\0
33[0m
\n\n
"
,
aErr
.
SigninURL
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[90m
W
aiting for sign in to complete...
\0
33[0m"
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
%s
\n\n
"
,
aErr
.
SigninURL
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[90m
w
aiting for sign in to complete...
\0
33[0m"
)
// Poll until auth succeeds
ticker
:=
time
.
NewTicker
(
2
*
time
.
Second
)
...
...
@@ -106,7 +106,7 @@ func waitForOllamaSignin(ctx context.Context) error {
case
<-
ticker
.
C
:
user
,
whoamiErr
:=
client
.
Whoami
(
ctx
)
if
whoamiErr
==
nil
&&
user
!=
nil
&&
user
.
Name
!=
""
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\r
\0
33[K
\0
33[
32mS
igned in
as %s
\0
33[0m
\n
"
,
user
.
Name
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\r
\0
33[K
\0
33[A
\r
\0
33[K
\0
33[
1ms
igned in
:
\0
33[0m
%s
\n
"
,
user
.
Name
)
return
nil
}
// Still waiting, show dot
...
...
@@ -264,12 +264,12 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) {
var
authErr
api
.
AuthorizationError
if
errors
.
As
(
err
,
&
authErr
)
{
p
.
StopAndClear
()
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
33mAuthentication required to use this cloud model.
\0
33[0m
\n
"
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
1mauth required:
\0
33[0m cloud model requires authentication
\n
"
)
result
,
promptErr
:=
agent
.
PromptYesNo
(
"Sign in to Ollama?"
)
if
promptErr
==
nil
&&
result
{
if
signinErr
:=
waitForOllamaSignin
(
ctx
);
signinErr
==
nil
{
// Retry the chat request
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[90m
R
etrying...
\0
33[0m
\n
"
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[90m
r
etrying...
\0
33[0m
\n
"
)
continue
// Retry the loop
}
}
...
...
@@ -283,11 +283,11 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) {
p
.
StopAndClear
()
if
consecutiveErrors
>=
3
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
31m✗ T
oo many consecutive errors, giving up
\
0
33[0m
\
n
"
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
1merror:
\0
33[0m t
oo many consecutive errors, giving up
\n
"
)
return
nil
,
fmt
.
Errorf
(
"too many consecutive server errors: %s"
,
statusErr
.
ErrorMessage
)
}
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
33m⚠ S
erver error (attempt %d/3): %s
\
0
33[0m
\
n
"
,
consecutiveErrors
,
statusErr
.
ErrorMessage
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
1mwarning:
\0
33[0m s
erver error (attempt %d/3): %s
\n
"
,
consecutiveErrors
,
statusErr
.
ErrorMessage
)
// Include both the model's response and the error so it can learn
assistantContent
:=
fullResponse
.
String
()
...
...
@@ -353,8 +353,8 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) {
if
cmd
,
ok
:=
args
[
"command"
]
.
(
string
);
ok
{
// Check if command is denied (dangerous pattern)
if
denied
,
pattern
:=
agent
.
IsDenied
(
cmd
);
denied
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
9
1m
✗ B
locked:
%s
\0
33[0m
\n
"
,
formatToolShort
(
toolName
,
args
))
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[91m
M
atches dangerous pattern: %s
\
0
33[0m
\
n
"
,
pattern
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[1m
b
locked:
\0
33[0m
%s
\n
"
,
formatToolShort
(
toolName
,
args
))
fmt
.
Fprintf
(
os
.
Stderr
,
"
m
atches dangerous pattern: %s
\n
"
,
pattern
)
toolResults
=
append
(
toolResults
,
api
.
Message
{
Role
:
"tool"
,
Content
:
agent
.
FormatDeniedResult
(
cmd
,
pattern
),
...
...
@@ -365,7 +365,7 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) {
// Check if command is auto-allowed (safe command)
if
agent
.
IsAutoAllowed
(
cmd
)
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
90m▶ A
uto-allowed:
%s
\0
33[0m
\n
"
,
formatToolShort
(
toolName
,
args
))
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
1ma
uto-allowed:
\0
33[0m
%s
\n
"
,
formatToolShort
(
toolName
,
args
))
skipApproval
=
true
}
}
...
...
@@ -375,7 +375,7 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) {
// In yolo mode, skip all approval prompts
if
opts
.
YoloMode
{
if
!
skipApproval
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
90m▶ R
unning:
%s
\0
33[0m
\n
"
,
formatToolShort
(
toolName
,
args
))
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
1mr
unning:
\0
33[0m
%s
\n
"
,
formatToolShort
(
toolName
,
args
))
}
}
else
if
!
skipApproval
&&
!
approval
.
IsAllowed
(
toolName
,
args
)
{
result
,
err
:=
approval
.
RequestApproval
(
toolName
,
args
)
...
...
@@ -405,7 +405,7 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) {
}
}
else
if
!
skipApproval
{
// Already allowed - show running indicator
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
90m▶ R
unning:
%s
\0
33[0m
\n
"
,
formatToolShort
(
toolName
,
args
))
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
1mr
unning:
\0
33[0m
%s
\n
"
,
formatToolShort
(
toolName
,
args
))
}
// Execute the tool
...
...
@@ -414,13 +414,13 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) {
// Check if web search needs authentication
if
errors
.
Is
(
err
,
tools
.
ErrWebSearchAuthRequired
)
{
// Prompt user to sign in
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
33m W
eb search requires authentication
.
\0
33[0m
\n
"
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
1mauth required:
\0
33[0m w
eb search requires authentication
\n
"
)
result
,
promptErr
:=
agent
.
PromptYesNo
(
"Sign in to Ollama?"
)
if
promptErr
==
nil
&&
result
{
// Get signin URL and wait for auth completion
if
signinErr
:=
waitForOllamaSignin
(
ctx
);
signinErr
==
nil
{
// Retry the web search
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[90m
R
etrying web search...
\0
33[0m
\n
"
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[90m
r
etrying web search...
\0
33[0m
\n
"
)
toolResult
,
err
=
toolRegistry
.
Execute
(
call
)
if
err
==
nil
{
goto
toolSuccess
...
...
@@ -428,7 +428,7 @@ func Chat(ctx context.Context, opts RunOptions) (*api.Message, error) {
}
}
}
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
3
1m
E
rror:
%v
\0
33[0m
\n
"
,
err
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[1m
e
rror:
\0
33[0m
%v
\n
"
,
err
)
toolResults
=
append
(
toolResults
,
api
.
Message
{
Role
:
"tool"
,
Content
:
fmt
.
Sprintf
(
"Error: %v"
,
err
),
...
...
@@ -499,17 +499,18 @@ func truncateUTF8(s string, limit int) string {
// formatToolShort returns a short description of a tool call.
func
formatToolShort
(
toolName
string
,
args
map
[
string
]
any
)
string
{
displayName
:=
agent
.
ToolDisplayName
(
toolName
)
if
toolName
==
"bash"
{
if
cmd
,
ok
:=
args
[
"command"
]
.
(
string
);
ok
{
return
fmt
.
Sprintf
(
"
bash
: %s"
,
truncateUTF8
(
cmd
,
50
))
return
fmt
.
Sprintf
(
"
%s
: %s"
,
displayName
,
truncateUTF8
(
cmd
,
50
))
}
}
if
toolName
==
"web_search"
{
if
query
,
ok
:=
args
[
"query"
]
.
(
string
);
ok
{
return
fmt
.
Sprintf
(
"
web_search: %s"
,
truncateUTF8
(
query
,
50
))
return
fmt
.
Sprintf
(
"
%s: %s"
,
displayName
,
truncateUTF8
(
query
,
50
))
}
}
return
tool
Name
return
display
Name
}
// Helper types and functions for display
...
...
@@ -649,7 +650,7 @@ func GenerateInteractive(cmd *cobra.Command, modelName string, wordWrap bool, op
// Check if model supports tools
supportsTools
,
err
:=
checkModelCapabilities
(
cmd
.
Context
(),
modelName
)
if
err
!=
nil
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
33mW
arning:
C
ould not check model capabilities: %v
\
0
33[0m
\
n
"
,
err
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
1mw
arning:
\0
33[0m c
ould not check model capabilities: %v
\n
"
,
err
)
supportsTools
=
false
}
...
...
@@ -658,13 +659,13 @@ func GenerateInteractive(cmd *cobra.Command, modelName string, wordWrap bool, op
if
supportsTools
{
toolRegistry
=
tools
.
DefaultRegistry
()
if
toolRegistry
.
Count
()
>
0
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[90m
T
ools available: %s
\0
33[0m
\n
"
,
strings
.
Join
(
toolRegistry
.
Names
(),
", "
))
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[90m
t
ools available: %s
\0
33[0m
\n
"
,
strings
.
Join
(
toolRegistry
.
Names
(),
", "
))
}
if
yoloMode
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
33m⚠ YOLO
mode
: A
ll tool approvals will be skipped
\
0
33[0m
\
n
"
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
1mwarning:
\0
33[0m yolo
mode
- a
ll tool approvals will be skipped
\n
"
)
}
}
else
{
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
33mNote: M
odel does not support tools - running in chat-only mode
\
0
33[0m
\
n
"
)
fmt
.
Fprintf
(
os
.
Stderr
,
"
\0
33[
1mnote:
\0
33[0m m
odel does not support tools - running in chat-only mode
\n
"
)
}
// Create approval manager for session
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment