Enable isolated Remote speech and protect reading exports
Attach incoming software speech to reader sessions with lease checks, local output priority and isolated cancellation. Preserve every document fragment source for privacy checks and export reviewed browse current-line reading, spelling and phonetic output. Verified focused reading and Remote race tests, complete review and external Remote race suites, private relay integration, speech regressions, affected-package vet, reader build and Tentacles make check. Audible speech, physical input/Braille and live interoperability remain unverified.
This commit is contained in:
1 parent
ac67236eca
commit
49be8309fe
27 files changed
+986
-54
No files matched your search
@@ -20,6 +20,9 @@ import (
|
||||
type RemoteSpeechDelivery struct {
|
||||
Context context.Context
|
||||
Lease tentaclewire.RemoteLease
|
||||
// Received is the reader's monotonic admission time of the first frame.
|
||||
// Local output can discard old mailbox/assembly work before it is dequeued.
|
||||
Received time.Time
|
||||
// Speeches is one complete effect/utterance, including all IPC fragments.
|
||||
// The owner applies priority once and must not deliver a partial assembly.
|
||||
Speeches []tentaclewire.RemoteSpeech
|
||||
@@ -30,13 +33,14 @@ type RemoteSpeechDelivery struct {
|
||||
|
||||
type remoteSpeechBatch struct {
|
||||
id uint64
|
||||
received time.Time
|
||||
speeches []tentaclewire.RemoteSpeech
|
||||
bytes int
|
||||
}
|
||||
|
||||
// EnableRemoteSpeech installs one bounded mailbox, explicitly from reader code.
|
||||
// Without an owner, remote.present is unavailable. No production speech worker
|
||||
// is bound yet. The owner selects Session.Done as well as this channel.
|
||||
// Without an owner, remote.present is unavailable. The owner selects session
|
||||
// shutdown as well as this channel and joins its backend cleanup.
|
||||
func (session *Session) EnableRemoteSpeech() <-chan RemoteSpeechDelivery {
|
||||
session.mutex.Lock()
|
||||
defer session.mutex.Unlock()
|
||||
@@ -103,7 +107,7 @@ func (session *Session) remote_present_locked(params json.RawMessage) (any, *RPC
|
||||
if request.Utterance != 0 && request.Utterance != request.Sequence {
|
||||
return nil, &RPCError{Code: -32602, Message: "Invalid utterance assembly"}
|
||||
}
|
||||
batch = &remoteSpeechBatch{id: request.Utterance}
|
||||
batch = &remoteSpeechBatch{id: request.Utterance, received: time.Now()}
|
||||
} else if request.Utterance != batch.id || request.Speech.Priority != batch.speeches[0].Priority {
|
||||
// No interleaved speech, cancel, pause, or changed priority may expose
|
||||
// an incomplete utterance. Heartbeats may still advance the sequence.
|
||||
@@ -125,7 +129,7 @@ func (session *Session) remote_present_locked(params json.RawMessage) (any, *RPC
|
||||
}
|
||||
access.speechBatch = nil
|
||||
var released sync.Once
|
||||
delivery := RemoteSpeechDelivery{Context: access.ctx, Lease: lease, Speeches: batch.speeches, Release: func() {
|
||||
delivery := RemoteSpeechDelivery{Context: access.ctx, Lease: lease, Received: batch.received, Speeches: batch.speeches, Release: func() {
|
||||
released.Do(func() {
|
||||
session.mutex.Lock()
|
||||
defer session.mutex.Unlock()
|
||||
|
||||
@@ -178,6 +178,7 @@ func TestRemoteSpeechSDKAtomicUtterance(t *testing.T) {
|
||||
if _, code := remoteCall(t, session, "remote.present", request); code != 0 {
|
||||
t.Fatal(code)
|
||||
}
|
||||
firstAdmitted := time.Now()
|
||||
request.Sequence++
|
||||
if _, code := remoteCall(t, session, "remote.heartbeat", request.RemoteOperation); code != 0 {
|
||||
t.Fatal("heartbeat could not run during assembly", code)
|
||||
@@ -210,6 +211,9 @@ func TestRemoteSpeechSDKAtomicUtterance(t *testing.T) {
|
||||
t.Fatal(code)
|
||||
}
|
||||
delivery := <-deliveries
|
||||
if delivery.Received.IsZero() || delivery.Received.After(firstAdmitted) {
|
||||
t.Fatal("finalization refreshed the original assembly admission time")
|
||||
}
|
||||
if !reflect.DeepEqual(delivery.Speeches, frames) || delivery.Lease != lease || delivery.Context.Err() != nil {
|
||||
t.Fatal("assembly changed semantics or ownership")
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user