v0.3.93 0192a2e

agent_start.go RawHistoryFolder

1package core
2
3import "context"
4
5// BaseSystem returns the prompt before BeforeStart replacements. Hosts editing
6// instructions should rebuild from this value, then call SetSystem.
7func (a *Agent) BaseSystem() string {
8	a.mu.Lock()
9	defer a.mu.Unlock()
10	return a.baseSystemLocked()
11}
12
13func (a *Agent) baseSystemLocked() string {
14	if a.startPrepared && a.System == a.startSystem {
15		return a.startBase
16	}
17	return a.System
18}
19
20// SetSystem replaces the unmodified prompt and invalidates its prepared value,
21// even when the replacement is identical. A pending BeforeStart result is
22// discarded and preparation repeats with the new base prompt.
23func (a *Agent) SetSystem(system string) {
24	a.mu.Lock()
25	defer a.mu.Unlock()
26	a.startGeneration++
27	a.startPrepared = false
28	a.System = system
29}
30
31func (a *Agent) resetStartLocked() {
32	if a.startPrepared && a.System == a.startSystem {
33		a.System = a.startBase
34	}
35	a.startGeneration++
36	a.startPrepared = false
37}
38
39// startCurrentLocked reports whether the effective prompt matches the current
40// runtime. Callers hold mu when checking it and when consuming that prompt.
41func (a *Agent) startCurrentLocked() bool {
42	return a.startPrepared && a.System == a.startSystem && a.Model == a.startModel && a.SessionID == a.startSession
43}
44
45func (a *Agent) prepareStart(ctx context.Context) error {
46	for {
47		if err := ctx.Err(); err != nil {
48			return err
49		}
50		if a.BeforeStart == nil {
51			return nil
52		}
53		a.mu.Lock()
54		if a.startCurrentLocked() {
55			a.mu.Unlock()
56			return nil
57		}
58		base := a.baseSystemLocked()
59		generation := a.startGeneration
60		original, model, session := a.System, a.Model, a.SessionID
61		a.mu.Unlock()
62
63		// Do not hold the agent lock while invoking an external hook: prompt
64		// rebuilds, context inspection, and cancellation must remain responsive.
65		system := a.BeforeStart(ctx, base)
66		if err := ctx.Err(); err != nil {
67			return err
68		}
69		a.mu.Lock()
70		if generation != a.startGeneration || original != a.System || model != a.Model || session != a.SessionID {
71			// A reset can retain exactly the same prompt string. Comparing the
72			// generation, not just its contents, prevents stale results from
73			// becoming current after that reset (including an A-B-A rebuild).
74			a.mu.Unlock()
75			continue
76		}
77		a.System = system
78		a.startBase, a.startSystem = base, system
79		a.startModel, a.startSession = model, session
80		a.startPrepared = true
81		a.mu.Unlock()
82		return nil
83	}
84}

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