Share verified reading context announcements with Remote

This commit is contained in:
ak47 committed 2026-11-05 20:39:19 -05:00
1 parent 8631c5a601
commit 9ac33560dc
7 files changed
+287 -20

No files matched your search

+51 -11
View File
@@ -17,6 +17,19 @@ func (worker *speechWorker) context_messages(ancestors []atspi.Snapshot) []strin
}
func (worker *speechWorker) context_messages_for(ancestors []atspi.Snapshot, sayAll bool) []string {
var messages []string
for _, message := range worker.context_announcements_for(ancestors, sayAll) {
messages = append(messages, message.text)
}
return messages
}
type readingContextMessage struct {
text string
leaving bool
}
func (worker *speechWorker) context_announcements_for(ancestors []atspi.Snapshot, sayAll bool) []readingContextMessage {
if ancestors == nil || worker.reading == nil {
return nil
}
@@ -28,6 +41,9 @@ func (worker *speechWorker) context_messages_for(ancestors []atspi.Snapshot, say
}
previous := worker.speechContext
worker.speechContext = current
// A new capture replaces both labels and their authority. Callers without
// an admitted local request cannot authorize later cached exit labels.
worker.speechContextObservation = nil
settings := worker.reading()
contains := func(values []atspi.Snapshot, candidate atspi.Snapshot) bool {
for _, item := range values {
@@ -37,9 +53,9 @@ func (worker *speechWorker) context_messages_for(ancestors []atspi.Snapshot, say
}
return false
}
var messages []string
var messages []readingContextMessage
panelMessages := make(map[string]bool)
appendMessage := func(item atspi.Snapshot, message string) {
appendMessage := func(item atspi.Snapshot, message string, leaving bool) {
// Keep every container identity for later transitions, but say an
// identical panel label only once in this transition.
if presentation.ContextType(item) == "panel" {
@@ -48,7 +64,7 @@ func (worker *speechWorker) context_messages_for(ancestors []atspi.Snapshot, say
}
panelMessages[message] = true
}
messages = append(messages, message)
messages = append(messages, readingContextMessage{text: message, leaving: leaving})
}
enabled := presentation.ContextEnabled
verbosity := 0
@@ -62,13 +78,13 @@ func (worker *speechWorker) context_messages_for(ancestors []atspi.Snapshot, say
// Article labels can contain an entire chat message. Python announces
// them on entry only; repeating the label on exit obscures the next one.
if item.Role != "article" && !contains(current, item) && enabled(presentation.ContextType(item), settings) {
appendMessage(item, "Leaving "+presentation.ContextLabel(item)+".")
appendMessage(item, "Leaving "+presentation.ContextLabel(item)+".", true)
}
}
for index := len(current) - 1; index >= 0; index-- {
item := current[index]
if !contains(previous, item) && enabled(presentation.ContextType(item), settings) {
appendMessage(item, presentation.ContextCountLabel(item, verbosity))
appendMessage(item, presentation.ContextCountLabel(item, verbosity), false)
}
}
return messages
@@ -77,9 +93,10 @@ func (worker *speechWorker) context_messages_for(ancestors []atspi.Snapshot, say
func (worker *speechWorker) deliver_context(ctx context.Context, request speechRequest) error {
if request.ResetContext {
worker.speechContext = nil
worker.speechContextObservation = nil
return nil
}
return worker.deliver_context_for(ctx, request.Context, false)
return worker.deliver_context_for(ctx, request, request.Context, false)
}
func (browser *browserModeController) speech_context_enabled() bool {
@@ -98,16 +115,39 @@ func (queue contextSpeechQueue) Enqueue(request speechRequest) {
queue.speechEnqueuer.Enqueue(request)
}
func (worker *speechWorker) deliver_context_for(ctx context.Context, ancestors []atspi.Snapshot, sayAll bool) error {
for _, message := range worker.context_messages_for(ancestors, sayAll) {
worker.logDelivery(ctx, speechRequest{Role: "system", Cause: "reading-context", Append: true}, message, -1)
func (worker *speechWorker) deliver_context_for(ctx context.Context, request speechRequest, ancestors []atspi.Snapshot, sayAll bool) error {
previousObservation := worker.speechContextObservation
messages := worker.context_announcements_for(ancestors, sayAll)
if ancestors != nil && worker.reading != nil {
// Source classification precedes presentation filtering. Independently
// check every retained context, including its unfiltered ancestry.
eligible := exportable_speech(request)
for _, ancestor := range ancestors {
eligible = eligible && accessibility_speech_origin(ancestor) == speechOriginLocal
}
if eligible {
worker.speechContextObservation = request.observation
}
}
for _, message := range messages {
delivery := request
delivery.Role, delivery.Cause, delivery.Append = "system", "reading-context", true
delivery.Character, delivery.CustomEcho = false, false
delivery.observation = worker.speechContextObservation
if message.leaving && delivery.observation != nil {
// Cached labels retain their original lease. New consent may admit
// current content, but cannot adopt labels from an earlier session.
delivery.observation = previousObservation
}
worker.logDelivery(ctx, delivery, message.text, -1)
worker.observe_speech(ctx, delivery, message.text, false)
if voiced, ok := worker.speaker.(interface {
SpeakVoice(context.Context, string, string) error
}); ok {
if err := voiced.SpeakVoice(ctx, message, "system"); err != nil {
if err := voiced.SpeakVoice(ctx, message.text, "system"); err != nil {
return err
}
} else if err := worker.speaker.Speak(ctx, message); err != nil {
} else if err := worker.speaker.Speak(ctx, message.text); err != nil {
return err
}
}