v0.3.71 0dbbc58

github_copilot_request.go RawHistoryFolder

1package provider
2
3import (
4	"context"
5	"strings"
6)
7
8type copilotRequestKey struct{}
9type copilotRequestMetadata struct {
10	initiator string
11	vision    bool
12}
13
14type copilotClient struct {
15	router *modelRouter
16}
17
18func (c *copilotClient) Name() string { return "github-copilot" }
19
20func (c *copilotClient) Stream(ctx context.Context, req Request) (<-chan Event, error) {
21	metadata := copilotRequestMetadata{initiator: "user"}
22	if len(req.Messages) > 0 && req.Messages[len(req.Messages)-1].Role != RoleUser {
23		metadata.initiator = "agent"
24	}
25	for _, message := range req.Messages {
26		if message.Role == RoleUser || message.Role == RoleTool {
27			metadata.vision = metadata.vision || copilotHasImages(message.Content)
28		}
29	}
30	// These endpoints perform their own thinking; they reject reasoning_effort.
31	api := copilotModelAPI(req.Model)
32	if model, err := FindModel(c.Name(), req.Model); err == nil && model.API != "" {
33		api = model.API
34	}
35	if api == APICompletions {
36		req.Reasoning = ""
37	}
38	ctx = context.WithValue(ctx, copilotRequestKey{}, metadata)
39	// Use family routing for custom models that have not supplied an API tag.
40	if model, err := FindModel(c.Name(), req.Model); err != nil || model.API == "" {
41		return c.router.byAPI[api].Stream(ctx, req)
42	}
43	return c.router.Stream(ctx, req)
44}
45
46func copilotHasImages(blocks []Content) bool {
47	for _, block := range blocks {
48		switch b := block.(type) {
49		case ImageBlock:
50			return true
51		case ToolResultBlock:
52			if copilotHasImages(b.Content) {
53				return true
54			}
55		}
56	}
57	return false
58}
59
60func copilotModelAPI(id string) string {
61	switch {
62	case strings.HasPrefix(id, "claude-"):
63		return APIAnthropicMessages
64	case strings.HasPrefix(id, "gpt-"), strings.HasPrefix(id, "grok-"), strings.HasPrefix(id, "mai-"), strings.HasPrefix(id, "oswe"):
65		return APIResponses
66	default:
67		return APICompletions
68	}
69}
70
71func configureCopilotModel(m *Model) {
72	m.API = copilotModelAPI(m.ID)
73	if m.API == APIAnthropicMessages && !strings.HasPrefix(m.ID, "claude-haiku-") {
74		m.AdaptiveThinking = true
75	}
76	if m.API == APICompletions {
77		m.ReasoningLevelMap = map[string]string{"minimum": "", "low": "", "medium": "", "high": "", "xhigh": "", "max": ""}
78	}
79	// Responses defaults expose xhigh, but these models only support low/medium/high.
80	if m.ID == "gpt-5-mini" || m.ID == "grok-4.5" || strings.HasPrefix(m.ID, "mai-") {
81		m.ReasoningLevelMap = map[string]string{"xhigh": "", "max": ""}
82	}
83}

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 476

↑↓ moveenter openesc close