submit commits a user prompt. sendText is the expanded form sent to the LLM (chip labels replaced by their original paste content); echoText is the collapsed form shown in scrollback so the chat doesn't swallow 80 lines of pasted log every turn; entry is the history snapshot replayed by ↑/↓, includi
(sendText, echoText string, entry promptEntry)
| 573 | return m, tea.Sequence(cmds...) |
| 574 | } |
| 575 | |
| 576 | // submit commits a user prompt. sendText is the expanded form sent to the LLM |
| 577 | // (chip labels replaced by their original paste content); echoText is the |
| 578 | // collapsed form shown in scrollback so the chat doesn't swallow 80 lines of |
| 579 | // pasted log every turn; entry is the history snapshot replayed by ↑/↓, |
| 580 | // including chip state. |
| 581 | func (m Model) submit(sendText, echoText string, entry promptEntry) (tea.Model, tea.Cmd) { |
| 582 | // Redact secret-bearing slash commands (/hamrpass <key>) before they reach |
| 583 | // any sink that persists or replays them: the scrollback echo (kept in |
| 584 | // m.scroll, re-emitted verbatim on every resize), the ↑/↓ recall ring, and |
| 585 | // the on-disk .codehamr/history file. Without this, submit leaks the bearer |
| 586 | // token into a second on-disk copy and into UI recall, undermining the |
| 587 | // 0o600 + symlink defences on the key in config.yaml. Raw sendText still |
| 588 | // flows to runSlash so activation works. No-op for non-secret input. |
| 589 | safeText := redactSlash(sendText) |
| 590 | if safeText != sendText { |
| 591 | echoText = safeText |
| 592 | entry = promptEntry{display: safeText} |
| 593 | } |
| 594 | // Echo to scrollback with the same accent ▌ the textarea uses, one visual |
| 595 | // language for "your voice" across live input and history. |
| 596 | m.appendLine(stylePrompt.Render("▌ ") + styleUser.Render(echoText)) |
| 597 | m.promptHistory = append(m.promptHistory, entry) |
| 598 | m.histIdx = -1 |
| 599 | // Persist the (redacted) prompt so ↑ finds it after a restart. Errors are |
| 600 | // swallowed: a transient failure isn't worth derailing submit, and a |
| 601 | // permanent one (read-only .codehamr/) would just be noise on every prompt. |
| 602 | _ = appendPromptHistory(m.cfg.Dir, safeText) |
| 603 | |
| 604 | if strings.HasPrefix(sendText, "/") { |
| 605 | dbgWritef("user_slash", "%s", safeText) |
| 606 | return m.runSlash(sendText) |
| 607 | } |
| 608 | // A new user message is a new goal: drop any in-progress failure streak so |
| 609 | // a stale count can't trip the nudge early. History persists; only the |
| 610 | // counter resets. |
| 611 | m.failKey, m.failStreak = "", 0 |