mirror of
https://github.com/multica-ai/multica.git
synced 2026-07-05 21:39:54 +02:00
PR #1744 fixed literal `\n\n` rendering for the CLI surfaces (`issue create / update --description`, `issue comment add --content`) but the agent-completion path bypasses the CLI entirely: the daemon POSTs the agent's stdout to `/api/daemon/tasks/:id/complete`, and `TaskService. CompleteTask` writes `payload.Output` straight into `createAgentComment` and `CreateChatMessage` without decoding. Models (e.g. Codex) routinely emit Python/JSON-style `\n` literals in their final output, which then land in the DB as the 4-char escape sequence and render as one wall of text in the issue/chat panel — exactly the bug report in #1820. - Move `unescapeFlagText` from `server/cmd/multica/cmd_issue.go` to `server/internal/util/text.go` as `UnescapeBackslashEscapes` so the CLI and the service layer share one implementation. The full contract-boundary test suite moves with it. - Apply `UnescapeBackslashEscapes` to `payload.Output` before it reaches `createAgentComment` and `CreateChatMessage` in `TaskService.CompleteTask`. Same `\n / \r / \t / \\` decoding as the CLI; other escape sequences (`\d`, `\w`, `\u`, etc.) pass through verbatim so regex/format strings in agent output survive. Closes #1820
49 lines
1.3 KiB
Go
49 lines
1.3 KiB
Go
package util
|
|
|
|
import "strings"
|
|
|
|
// UnescapeBackslashEscapes decodes the common backslash escape sequences
|
|
// (\n, \r, \t, \\) that LLM agents routinely emit as 4-character literals
|
|
// because Python/JSON-style string conventions are their default. The same
|
|
// helper is used by the CLI to fix bash-double-quote bodies (where the shell
|
|
// doesn't expand \n) and by the daemon-task completion path to fix raw agent
|
|
// stdout that arrives with literal `\n\n` between paragraphs.
|
|
//
|
|
// Only \n / \r / \t / \\ are decoded. Other escape sequences (\d, \w, \s,
|
|
// \u, \0, \", etc.) pass through verbatim so regex literals and printf
|
|
// format strings survive without surprise mutation. Callers that need the
|
|
// literal 4-char sequence intact should bypass this helper entirely (the CLI
|
|
// exposes --content-stdin / --description-stdin for that case).
|
|
func UnescapeBackslashEscapes(s string) string {
|
|
if !strings.ContainsRune(s, '\\') {
|
|
return s
|
|
}
|
|
var b strings.Builder
|
|
b.Grow(len(s))
|
|
for i := 0; i < len(s); i++ {
|
|
c := s[i]
|
|
if c == '\\' && i+1 < len(s) {
|
|
switch s[i+1] {
|
|
case 'n':
|
|
b.WriteByte('\n')
|
|
i++
|
|
continue
|
|
case 'r':
|
|
b.WriteByte('\r')
|
|
i++
|
|
continue
|
|
case 't':
|
|
b.WriteByte('\t')
|
|
i++
|
|
continue
|
|
case '\\':
|
|
b.WriteByte('\\')
|
|
i++
|
|
continue
|
|
}
|
|
}
|
|
b.WriteByte(c)
|
|
}
|
|
return b.String()
|
|
}
|