Publish Claude Code Binaries / Stage and publish binaries (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-arm64 (push) Canceled after 0s
Publish Tutti App Runtime / Build darwin-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build linux-amd64 (push) Canceled after 0s
Publish Tutti App Runtime / Build windows-amd64 (push) Canceled after 0s
Sync Tutti Agent Skills / sync (push) Canceled after 0s
Windows Agent Adapters / Test Windows agent adapters (push) Canceled after 0s
Windows Daemon Adapters / Test Windows daemon adapters (push) Canceled after 0s
Publish Tutti App Runtime / Publish runtime catalog (push) Canceled after 0s
164 lines
3.9 KiB
Go
164 lines
3.9 KiB
Go
// Frozen title helpers preserve the historical migration transform without
|
|
// making store-sqlite depend on the Host application layer. Live title
|
|
// derivation belongs exclusively to packages/agent/host.
|
|
package storesqlite
|
|
|
|
import (
|
|
"strings"
|
|
"unicode/utf8"
|
|
|
|
canonical "github.com/tutti-os/tutti/packages/agent/store-sqlite/canonical"
|
|
)
|
|
|
|
const maxMigratedSessionTitleRunes = 120
|
|
|
|
func normalizeMigratedSessionTitle(value string) string {
|
|
value = strings.TrimSpace(value)
|
|
if value == "" {
|
|
return ""
|
|
}
|
|
|
|
var output strings.Builder
|
|
for index := 0; index < len(value); {
|
|
labelStart, hrefStart, hrefEnd, ok := migratedTitleMarkdownLinkAt(value, index)
|
|
if !ok {
|
|
output.WriteByte(value[index])
|
|
index++
|
|
continue
|
|
}
|
|
output.WriteString(unescapeMigratedTitleMarkdownLabel(value[labelStart : hrefStart-2]))
|
|
index = hrefEnd + 1
|
|
}
|
|
return strings.Join(strings.Fields(strings.TrimSpace(output.String())), " ")
|
|
}
|
|
|
|
func deriveMigratedSessionTitle(currentTitle string, visiblePrompt string) string {
|
|
if strings.TrimSpace(currentTitle) != "" {
|
|
return ""
|
|
}
|
|
title := normalizeMigratedSessionTitle(visiblePrompt)
|
|
if title == "" {
|
|
return ""
|
|
}
|
|
if utf8.RuneCountInString(title) <= maxMigratedSessionTitleRunes {
|
|
return title
|
|
}
|
|
const suffix = "..."
|
|
runes := []rune(title)
|
|
suffixRunes := utf8.RuneCountInString(suffix)
|
|
return strings.TrimSpace(string(runes[:maxMigratedSessionTitleRunes-suffixRunes])) + suffix
|
|
}
|
|
|
|
func isLegacyMigratedSessionTitlePlaceholder(value string, provider string, targetAliases ...string) bool {
|
|
title := normalizeMigratedTitleIdentity(value)
|
|
provider = normalizeMigratedTitleIdentity(provider)
|
|
if title == "" || title == provider {
|
|
return true
|
|
}
|
|
for _, candidate := range targetAliases {
|
|
if title == normalizeMigratedTitleIdentity(candidate) {
|
|
return true
|
|
}
|
|
}
|
|
descriptor, ok := canonical.FindProviderIdentity(provider)
|
|
if !ok {
|
|
return false
|
|
}
|
|
for _, candidate := range append(
|
|
[]string{descriptor.ID, descriptor.DisplayName},
|
|
descriptor.Aliases...,
|
|
) {
|
|
if title == normalizeMigratedTitleIdentity(candidate) {
|
|
return true
|
|
}
|
|
}
|
|
return false
|
|
}
|
|
|
|
func normalizeMigratedTitleIdentity(value string) string {
|
|
return strings.ToLower(strings.TrimSpace(value))
|
|
}
|
|
|
|
func migratedTitleMarkdownLinkAt(value string, start int) (labelStart, hrefStart, hrefEnd int, ok bool) {
|
|
if start >= len(value) || value[start] != '[' {
|
|
return 0, 0, 0, false
|
|
}
|
|
labelEnd := findMigratedTitleUnescaped(value, start+1, ']')
|
|
if labelEnd < 0 || labelEnd+1 >= len(value) || value[labelEnd+1] != '(' {
|
|
return 0, 0, 0, false
|
|
}
|
|
hrefEnd = findMigratedTitleBalancedHrefEnd(value, labelEnd+2)
|
|
if hrefEnd < 0 {
|
|
return 0, 0, 0, false
|
|
}
|
|
return start + 1, labelEnd + 2, hrefEnd, true
|
|
}
|
|
|
|
func findMigratedTitleUnescaped(value string, start int, target byte) int {
|
|
escaped := false
|
|
for index := start; index < len(value); index++ {
|
|
if escaped {
|
|
escaped = false
|
|
continue
|
|
}
|
|
if value[index] == '\\' {
|
|
escaped = true
|
|
continue
|
|
}
|
|
if value[index] == target {
|
|
return index
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func findMigratedTitleBalancedHrefEnd(value string, start int) int {
|
|
depth := 0
|
|
escaped := false
|
|
for index := start; index < len(value); index++ {
|
|
if escaped {
|
|
escaped = false
|
|
continue
|
|
}
|
|
switch value[index] {
|
|
case '\\':
|
|
escaped = true
|
|
case '(':
|
|
depth++
|
|
case ')':
|
|
if depth == 0 {
|
|
return index
|
|
}
|
|
depth--
|
|
}
|
|
}
|
|
return -1
|
|
}
|
|
|
|
func unescapeMigratedTitleMarkdownLabel(value string) string {
|
|
var output strings.Builder
|
|
escaped := false
|
|
for index := 0; index < len(value); index++ {
|
|
char := value[index]
|
|
if escaped {
|
|
if strings.ContainsRune(`\\[]()`, rune(char)) {
|
|
output.WriteByte(char)
|
|
} else {
|
|
output.WriteByte('\\')
|
|
output.WriteByte(char)
|
|
}
|
|
escaped = false
|
|
continue
|
|
}
|
|
if char == '\\' {
|
|
escaped = true
|
|
continue
|
|
}
|
|
output.WriteByte(char)
|
|
}
|
|
if escaped {
|
|
output.WriteByte('\\')
|
|
}
|
|
return output.String()
|
|
}
|