Support Codex 0.134 approvals
Use available approval decisions from the app-server schema, preserve structured policy decisions in callbacks, and keep approval rendering aligned with normal tool-call output. Also simplify thread commands, clear stale active turns more carefully, and update command/help docs.
This commit is contained in:
@@ -87,6 +87,26 @@ func SummaryDetailsRawHTMLLimited(summary, detailsHTML string, limit int) string
|
||||
return SummaryDetailsRawHTML(summary, EscapeHTML(suffix))
|
||||
}
|
||||
|
||||
func SummaryRawHTMLSections(summary string, sections []string) string {
|
||||
summary = strings.TrimSpace(summary)
|
||||
sections = nonEmptyHTML(sections)
|
||||
var parts []string
|
||||
if summary != "" {
|
||||
parts = append(parts, EscapeHTML(summary))
|
||||
}
|
||||
for _, section := range sections {
|
||||
parts = append(parts, ExpandableQuoteRawHTML(section))
|
||||
}
|
||||
return strings.Join(parts, "\n")
|
||||
}
|
||||
|
||||
func SummaryRawHTMLSectionsLimited(summary string, sections []string, limit int) string {
|
||||
if limit <= 0 {
|
||||
limit = TelegramHTMLMessageLimit
|
||||
}
|
||||
return FitHTMLMessage(SummaryRawHTMLSections(summary, sections), limit)
|
||||
}
|
||||
|
||||
func CodeBlockHTML(language, text string) string {
|
||||
text = strings.TrimSpace(text)
|
||||
if text == "" {
|
||||
@@ -197,7 +217,16 @@ func FitHTMLMessage(htmlText string, limit int) string {
|
||||
return truncateHTMLText(htmlText, limit)
|
||||
}
|
||||
|
||||
contentRunes := []rune(strings.TrimSpace(html.UnescapeString(content)))
|
||||
prefix, body, codeLanguage, codeBlock := splitSafeQuotePrefix(content)
|
||||
contentRunes := []rune(strings.TrimSpace(html.UnescapeString(body)))
|
||||
if len(contentRunes) == 0 {
|
||||
replacement := prefix + EscapeHTML(truncatedQuote)
|
||||
if replacement == content {
|
||||
return summaryOnlyHTML(htmlText, limit)
|
||||
}
|
||||
htmlText = htmlText[:contentStart] + replacement + htmlText[contentEnd:]
|
||||
continue
|
||||
}
|
||||
over := len([]rune(htmlText)) - limit
|
||||
keep := len(contentRunes) - over - 80
|
||||
if keep < 0 {
|
||||
@@ -210,7 +239,10 @@ func FitHTMLMessage(htmlText string, limit int) string {
|
||||
if keep > 0 {
|
||||
replacementText = strings.TrimSpace(string(contentRunes[:keep])) + "\n" + truncatedQuote
|
||||
}
|
||||
replacement := EscapeHTML(replacementText)
|
||||
replacement := prefix + EscapeHTML(replacementText)
|
||||
if codeBlock {
|
||||
replacement = prefix + CodeBlockHTML(codeLanguage, replacementText)
|
||||
}
|
||||
if replacement == content {
|
||||
return summaryOnlyHTML(htmlText, limit)
|
||||
}
|
||||
@@ -219,6 +251,81 @@ func FitHTMLMessage(htmlText string, limit int) string {
|
||||
return htmlText
|
||||
}
|
||||
|
||||
func splitSafeQuotePrefix(content string) (prefix, body, codeLanguage string, codeBlock bool) {
|
||||
content = strings.TrimSpace(content)
|
||||
prefix, body = splitLeadingBoldLabel(content)
|
||||
body = strings.TrimSpace(body)
|
||||
if language, code, ok := splitSingleCodeBlock(body); ok {
|
||||
return prefix, code, language, true
|
||||
}
|
||||
return prefix, body, "", false
|
||||
}
|
||||
|
||||
func splitLeadingBoldLabel(content string) (string, string) {
|
||||
if !strings.HasPrefix(content, "<b>") {
|
||||
return "", content
|
||||
}
|
||||
end := strings.Index(content, "</b>")
|
||||
if end < 0 {
|
||||
return "", content
|
||||
}
|
||||
labelEnd := end + len("</b>")
|
||||
label := content[:labelEnd]
|
||||
if !strings.HasSuffix(label, ":</b>") {
|
||||
return "", content
|
||||
}
|
||||
afterLabel := content[labelEnd:]
|
||||
if strings.HasPrefix(afterLabel, " <pre>") {
|
||||
return label + " ", strings.TrimLeft(afterLabel, " ")
|
||||
}
|
||||
if strings.HasPrefix(afterLabel, "\n<pre>") {
|
||||
return label + " ", strings.TrimLeft(afterLabel, "\n")
|
||||
}
|
||||
if strings.HasPrefix(afterLabel, " ") {
|
||||
lineEnd := strings.Index(afterLabel, "\n")
|
||||
if lineEnd < 0 {
|
||||
return content + "\n", ""
|
||||
}
|
||||
prefixEnd := labelEnd + lineEnd + 1
|
||||
return content[:prefixEnd], strings.TrimLeft(content[prefixEnd:], "\n")
|
||||
}
|
||||
rest := strings.TrimLeft(afterLabel, " \n")
|
||||
return label + " ", rest
|
||||
}
|
||||
|
||||
func splitSingleCodeBlock(content string) (string, string, bool) {
|
||||
const preOpen = "<pre>"
|
||||
const preClose = "</pre>"
|
||||
const codeClose = "</code>"
|
||||
if !strings.HasPrefix(content, preOpen+"<code ") || !strings.HasSuffix(content, codeClose+preClose) {
|
||||
return "", "", false
|
||||
}
|
||||
codeStart := len(preOpen)
|
||||
tagEnd := strings.Index(content[codeStart:], ">")
|
||||
if tagEnd < 0 {
|
||||
return "", "", false
|
||||
}
|
||||
tagEnd += codeStart
|
||||
tag := content[codeStart : tagEnd+1]
|
||||
const classPrefix = `class="language-`
|
||||
classStart := strings.Index(tag, classPrefix)
|
||||
if classStart < 0 {
|
||||
return "", "", false
|
||||
}
|
||||
classStart += len(classPrefix)
|
||||
classEnd := strings.Index(tag[classStart:], `"`)
|
||||
if classEnd < 0 {
|
||||
return "", "", false
|
||||
}
|
||||
language := tag[classStart : classStart+classEnd]
|
||||
bodyStart := tagEnd + 1
|
||||
bodyEnd := len(content) - len(codeClose+preClose)
|
||||
if bodyEnd < bodyStart {
|
||||
return "", "", false
|
||||
}
|
||||
return safeCodeLanguage(language), html.UnescapeString(content[bodyStart:bodyEnd]), true
|
||||
}
|
||||
|
||||
func largestBlockquoteContent(htmlText, open, close string) (int, int, string) {
|
||||
bestStart := -1
|
||||
bestEnd := -1
|
||||
@@ -270,15 +377,19 @@ func truncateHTMLText(htmlText string, limit int) string {
|
||||
limit = TelegramHTMLMessageLimit
|
||||
}
|
||||
suffix := "\n...[truncated]"
|
||||
runes := []rune(htmlText)
|
||||
if len(runes) <= limit {
|
||||
if len([]rune(htmlText)) <= limit {
|
||||
return htmlText
|
||||
}
|
||||
plain := stripSimpleHTML(htmlText)
|
||||
runes := []rune(plain)
|
||||
keep := limit - len([]rune(suffix))
|
||||
if keep < 0 {
|
||||
keep = 0
|
||||
}
|
||||
return string(runes[:keep]) + suffix
|
||||
if keep > len(runes) {
|
||||
keep = len(runes)
|
||||
}
|
||||
return EscapeHTML(strings.TrimSpace(string(runes[:keep]))) + EscapeHTML(suffix)
|
||||
}
|
||||
|
||||
func ChunkText(text string, max int) []string {
|
||||
@@ -321,9 +432,13 @@ func ParseApprovalCallbackData(data string) (int64, string, bool) {
|
||||
return 0, "", false
|
||||
}
|
||||
switch parts[2] {
|
||||
case "accept", "acceptForSession", "decline", "cancel", "details":
|
||||
case "accept", "acceptForSession", "acceptWithExecpolicyAmendment", "decline", "cancel", "details":
|
||||
return id, parts[2], true
|
||||
default:
|
||||
if strings.HasPrefix(parts[2], "networkPolicy") {
|
||||
index, err := strconv.Atoi(strings.TrimPrefix(parts[2], "networkPolicy"))
|
||||
return id, parts[2], err == nil && index >= 0
|
||||
}
|
||||
return 0, "", false
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user