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:
Codex
2026-05-28 09:39:40 +00:00
parent e9dd840111
commit 2b0da9f508
9 changed files with 813 additions and 147 deletions

View File

@@ -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
}
}