Files
gnoma/internal/context/compact.go
vikingowl cb2d63d06f feat: Ollama/gemma4 compat — /init flow, stream filter, safety fixes
provider/openai:
- Fix doubled tool call args (argsComplete flag): Ollama sends complete
  args in the first streaming chunk then repeats them as delta, causing
  doubled JSON and 400 errors in elfs
- Handle fs: prefix (gemma4 uses fs:grep instead of fs.grep)
- Add Reasoning field support for Ollama thinking output

cmd/gnoma:
- Early TTY detection so logger is created with correct destination
  before any component gets a reference to it (fixes slog WARN bleed
  into TUI textarea)

permission:
- Exempt spawn_elfs and agent tools from safety scanner: elf prompt
  text may legitimately mention .env/.ssh/credentials patterns and
  should not be blocked

tui/app:
- /init retry chain: no-tool-calls → spawn_elfs nudge → write nudge
  (ask for plain text output) → TUI fallback write from streamBuf
- looksLikeAgentsMD + extractMarkdownDoc: validate and clean fallback
  content before writing (reject refusals, strip narrative preambles)
- Collapse thinking output to 3 lines; ctrl+o to expand (live stream
  and committed messages)
- Stream-level filter for model pseudo-tool-call blocks: suppresses
  <<tool_code>>...</tool_code>> and <<function_call>>...<tool_call|>
  from entering streamBuf across chunk boundaries
- sanitizeAssistantText regex covers both block formats
- Reset streamFilterClose at every turn start
2026-04-05 19:24:51 +02:00

35 lines
1.0 KiB
Go

package context
import "somegit.dev/Owlibou/gnoma/internal/message"
// safeSplitPoint adjusts a compaction split index to avoid orphaning tool
// results. If history[target] is a tool-result message, it walks backward
// until it finds a message that is not a tool result, so the assistant message
// that issued the tool calls stays in the "recent" window alongside its results.
//
// target is the index of the first message to keep in the recent window.
// Returns an adjusted index guaranteed to keep tool-call/tool-result pairs together.
func safeSplitPoint(history []message.Message, target int) int {
if target <= 0 || len(history) == 0 {
return 0
}
if target >= len(history) {
target = len(history) - 1
}
idx := target
for idx > 0 && hasToolResults(history[idx]) {
idx--
}
return idx
}
// hasToolResults reports whether msg contains any ContentToolResult blocks.
func hasToolResults(msg message.Message) bool {
for _, c := range msg.Content {
if c.Type == message.ContentToolResult {
return true
}
}
return false
}