v0.11.4 020512d

context_recovery.go RawHistoryFolder

1package modes
2
3import (
4	"context"
5	"fmt"
6
7	"github.com/bnema/zut/packages/core"
8	"github.com/bnema/zut/packages/provider"
9)
10
11// ContextRecoveryOptions configures the host-owned, one-shot recovery for a
12// provider context-overflow error. It deliberately does not add a terminal
13// continuation policy to core.Agent.
14type ContextRecoveryOptions struct {
15	// PersistCompaction records the compacted transcript before the existing
16	// prompt is continued. A persistence failure restores the pre-compaction
17	// transcript and prevents the continuation request.
18	PersistCompaction func([]provider.Message) error
19
20	// SuppressInitialOverflowEvent hides the recoverable first-attempt
21	// EvTurnEnd overflow from sinks whose protocol cannot retract a terminal
22	// error frame, such as JSONL. A retry overflow is never suppressed.
23	SuppressInitialOverflowEvent bool
24}
25
26// ContextRecoveryResult describes a completed host-level context recovery.
27type ContextRecoveryResult struct {
28	// OutputStart is the first message to append after a persisted compaction.
29	// When Compacted is false, it is the message count before Prompt.
30	OutputStart int
31	Compacted   bool
32}
33
34// PromptWithContextRecovery appends prompt and runs it once. If that provider
35// request exceeds its context window, it compacts the existing transcript and
36// continues the already-appended prompt exactly once. Cancellation, a
37// one-message transcript, a compaction failure, and a second overflow remain
38// terminal outcomes.
39func PromptWithContextRecovery(ctx context.Context, ag *core.Agent, prompt string, images []provider.ImageBlock, sink func(core.AgentEvent), options ContextRecoveryOptions) (result ContextRecoveryResult, err error) {
40	result.OutputStart = len(ag.Messages())
41	if sink == nil {
42		sink = func(core.AgentEvent) {}
43	}
44
45	firstSink := sink
46	if options.SuppressInitialOverflowEvent {
47		firstSink = func(event core.AgentEvent) {
48			if turnEnd, ok := event.(core.EvTurnEnd); ok && provider.IsContextOverflowError(turnEnd.Err) {
49				return
50			}
51			sink(event)
52		}
53	}
54
55	if err = ag.Prompt(ctx, prompt, images, firstSink); err == nil || ctx.Err() != nil || !provider.IsContextOverflowError(err) {
56		return result, err
57	}
58
59	messages := ag.Messages()
60	if len(messages) <= 1 {
61		// The failed prompt is the only message. Summarizing it would replace
62		// the user's task with an LLM-generated paraphrase.
63		return result, err
64	}
65	keepTail := min(4, len(messages)-1)
66	if _, compactErr := ag.Compact(ctx, keepTail, nil); compactErr != nil {
67		return result, fmt.Errorf("compact transcript after initial overflow: %w", compactErr)
68	}
69
70	compacted := ag.Messages()
71	if options.PersistCompaction != nil {
72		if persistErr := options.PersistCompaction(compacted); persistErr != nil {
73			// The session did not receive the compaction checkpoint, so restore
74			// the original history plus the already-appended prompt rather than
75			// let a later transcript flush make the session inconsistent.
76			ag.SetMessages(messages)
77			return result, fmt.Errorf("persist compacted transcript: %w", persistErr)
78		}
79	}
80
81	result.OutputStart = len(compacted)
82	result.Compacted = true
83	return result, ag.Continue(ctx, sink)
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