| 264 | liveContextSize map[string]int |
| 265 | } |
| 266 | |
| 267 | func New(cfg *config.Config, cli *llm.Client, projectDir, version string) Model { |
| 268 | ta := newPromptInput() |
| 269 | |
| 270 | // Fixed dark style: WithAutoStyle queries the terminal (OSC 11) before |
| 271 | // bubbletea grabs raw stdin, so the reply bytes leak into the textarea as |
| 272 | // "1;rgb:1e1e/1e1e/1e1e" garbage. Dev containers are dark: no query, no leak. |
| 273 | r, _ := glamour.NewTermRenderer(glamour.WithStandardStyle("dark"), glamour.WithWordWrap(defaultWidth-4)) |
| 274 | |
| 275 | sp := spinner.New() |
| 276 | sp.Spinner = spinner.MiniDot |
| 277 | sp.Style = styleSpinner |
| 278 | |
| 279 | m := Model{ |
| 280 | Version: version, |
| 281 | cfg: cfg, |
| 282 | cli: cli, |
| 283 | system: buildSystem(projectDir), |
| 284 | ta: ta, |
| 285 | renderer: r, |
| 286 | spinner: sp, |
| 287 | connected: true, // optimistic until the first ping proves otherwise |
| 288 | // width/height left at 0; View() returns "" until the first |
| 289 | // WindowSizeMsg, so we don't flash an 80×24 frame then resize. |
| 290 | streaming: new(strings.Builder), |
| 291 | scroll: new(strings.Builder), |
| 292 | reasoning: new(strings.Builder), |
| 293 | histIdx: -1, |
| 294 | liveContextSize: map[string]int{}, |
| 295 | } |
| 296 | // Record the active backend + budget once, before any turn, so a shared log |
| 297 | // names exactly which model/endpoint/context window produced the behaviour. |
| 298 | // Gated on dbgEnabled so the profile derefs run only when logging is on; |
| 299 | // off (the default) means New behaves exactly as before. |
| 300 | if dbgEnabled() { |
| 301 | dbgWriteSession(version, cfg.Active, cfg.ActiveProfile().LLM, cfg.ActiveURL(), |
| 302 | m.activeContextSize(), chmctx.Tokens(m.system), |
| 303 | []string{tools.BashName, tools.ReadFileName, tools.WriteFileName, tools.EditFileName}) |
| 304 | } |
| 305 | // Seed prompt history from .codehamr/history so ↑ recalls prompts from |
| 306 | // earlier sessions. Loaded entries carry no chip metadata (the on-disk |
| 307 | // format stores expanded text only), so a recalled multi-line paste |
| 308 | // appears uncollapsed, the right tradeoff for a cat-friendly history file. |
| 309 | m.promptHistory = loadPromptHistory(cfg.Dir) |
| 310 | return m |
| 311 | } |
| 312 | |
| 313 | // activeContextSize returns the context window the packer should aim at: the |
| 314 | // live server-reported value for the active profile if known, else the on-disk |