pi-agent: Fix final integrated review

This commit is contained in:
proxyctl coordinator
2026-07-18 02:37:54 +08:00
parent ed8b527f1f
commit 652d7eba2b
18 changed files with 566 additions and 393 deletions
+79 -53
View File
@@ -56,32 +56,57 @@ func defaultModelOptions() modelOptions {
}
type presentationState struct {
snapshot app.Snapshot
page page
width int
height int
selected int
profileOffset int
profileDetails int
profileDelete *profileDeleteConfirmation
snapshot app.Snapshot
page page
terminal terminalDimensions
dashboard dashboardPresentationState
profile profilePresentationState
form formPresentationState
help helpPresentationState
operation operationPresentationState
}
type terminalDimensions struct {
width int
height int
}
type dashboardPresentationState struct {
selectedTargetID string
dashboardFocus dashboardPane
focus dashboardPane
showUnavailable bool
detailsOffset int
driftResolutionTargetID string
picker profilePickerState
form formState
help helpDialogState
confirmation *app.Confirmation
confirmationDialog *confirmationDialog
status string
operationStatus app.OperationStatus
currentError *operationError
errorExpanded bool
errorOffset int
exitDeferred bool
operationActive bool
operationProgress map[string]app.TargetProgressPhase
}
type profilePresentationState struct {
selected int
listOffset int
detailsOffset int
deletion *profileDeleteConfirmation
status string
}
type formPresentationState struct {
state formState
}
type helpPresentationState struct {
dialog helpDialogState
}
type operationPresentationState struct {
confirmation *app.Confirmation
confirmationDialog *confirmationDialog
status app.OperationStatus
currentError *operationError
errorExpanded bool
errorOffset int
exitDeferred bool
active bool
progress map[string]app.TargetProgressPhase
spinnerFrame string
}
// terminalPresentation is the terminal-presentation Module. Application
@@ -116,7 +141,7 @@ func (p terminalPresentation) Render(state presentationState) string {
// semanticTheme style is applied; otherwise an attacker could inject one of
// the same allowlisted SGR sequences that the theme legitimately emits.
state = sanitizePresentationState(state)
width, height := state.width, state.height
width, height := state.terminal.width, state.terminal.height
if width <= 0 {
width = 80
}
@@ -138,7 +163,7 @@ func (p terminalPresentation) Render(state presentationState) string {
default:
page = p.mainView(state, width, height)
}
if state.help.top(state.confirmation) {
if state.help.dialog.top(state.operation.confirmation) {
help := p.helpDialogView(state)
// Retain the underlying page header so Help is visibly contextual and
// modal rather than a replacement navigation destination.
@@ -151,8 +176,8 @@ func (p terminalPresentation) Render(state presentationState) string {
page.dashboard = nil
}
page.status = renderStatus(state)
page.statusExpanded = state.errorExpanded && state.currentError != nil
page.statusOffset = state.errorOffset
page.statusExpanded = state.operation.errorExpanded && state.operation.currentError != nil
page.statusOffset = state.operation.errorOffset
page = redactTerminalPage(page)
// Untrusted values were redacted and stripped of controls before semantic
// styling. The final pass therefore removes unexpected terminal controls
@@ -162,17 +187,18 @@ func (p terminalPresentation) Render(state presentationState) string {
func renderStatus(state presentationState) string {
var lines []string
if state.exitDeferred {
operation := state.operation
if operation.exitDeferred {
lines = append(lines, "Exit requested — waiting for the current mutation to finish safely")
} else if state.operationStatus.Summary != "" {
lines = append(lines, state.operationStatus.Summary)
} else if state.status != "" {
lines = append(lines, state.status)
} else if operation.status.Summary != "" {
lines = append(lines, operation.status.Summary)
} else if state.profile.status != "" {
lines = append(lines, state.profile.status)
}
if state.currentError != nil {
errorText := "Current error: " + state.currentError.status.Summary
if state.errorExpanded && state.currentError.detail != "" {
errorText += "\n" + state.currentError.detail
if operation.currentError != nil {
errorText := "Current error: " + operation.currentError.status.Summary
if operation.errorExpanded && operation.currentError.detail != "" {
errorText += "\n" + operation.currentError.detail
}
lines = append(lines, errorText)
}
@@ -236,35 +262,35 @@ func sanitizePresentationState(state presentationState) presentationState {
// Apply that stronger policy to every status/error ingress before styling.
sanitizeStatus = sanitizeUntrustedUserinfoText
}
state.status = sanitizeStatus(state.status)
state.operationStatus.Summary = sanitizeStatus(state.operationStatus.Summary)
if state.currentError != nil {
current := *state.currentError
state.profile.status = sanitizeStatus(state.profile.status)
state.operation.status.Summary = sanitizeStatus(state.operation.status.Summary)
if state.operation.currentError != nil {
current := *state.operation.currentError
current.status.Summary = sanitizeStatus(current.status.Summary)
current.detail = sanitizeStatus(current.detail)
state.currentError = &current
state.operation.currentError = &current
}
if state.confirmation != nil {
confirmation := sanitizePresentationConfirmation(*state.confirmation)
state.confirmation = &confirmation
if state.operation.confirmation != nil {
confirmation := sanitizePresentationConfirmation(*state.operation.confirmation)
state.operation.confirmation = &confirmation
}
if state.confirmationDialog != nil {
dialog := *state.confirmationDialog
if state.operation.confirmationDialog != nil {
dialog := *state.operation.confirmationDialog
dialog.request = sanitizePresentationConfirmation(dialog.request)
state.confirmationDialog = &dialog
state.operation.confirmationDialog = &dialog
}
if state.profileDelete != nil {
confirmation := *state.profileDelete
if state.profile.deletion != nil {
confirmation := *state.profile.deletion
confirmation.name = sanitizeUntrustedText(confirmation.name)
confirmation.dialog.request = sanitizePresentationConfirmation(confirmation.dialog.request)
state.profileDelete = &confirmation
state.profile.deletion = &confirmation
}
state.picker.selectedName = sanitizeUntrustedText(state.picker.selectedName)
searchPosition := state.picker.search.Position()
state.picker.search.SetValue(sanitizeUntrustedText(state.picker.search.Value()))
state.picker.search.SetCursor(searchPosition)
state.form = sanitizePresentationForm(state.form)
state.dashboard.picker.selectedName = sanitizeUntrustedText(state.dashboard.picker.selectedName)
searchPosition := state.dashboard.picker.search.Position()
state.dashboard.picker.search.SetValue(sanitizeUntrustedText(state.dashboard.picker.search.Value()))
state.dashboard.picker.search.SetCursor(searchPosition)
state.form.state = sanitizePresentationForm(state.form.state)
return state
}