feat: expand companion platform, device controls, and memoir workflows
ci / verify (push) Canceled after 0s

Add customer and operator login, BLE provisioning, device fleet controls, call memory, memoir generation, and refreshed family and maintenance portals. Include API contracts, migrations, firmware sources, tests, assets, and deployment documentation.
This commit is contained in:
Codex Local
2026-10-08 10:06:44 +08:00
parent 98fb2965d0
commit bf1233d5df
861 changed files with 94614 additions and 3209 deletions
+165
View File
@@ -0,0 +1,165 @@
package extraction
import (
"context"
"encoding/json"
"errors"
"fmt"
"time"
"data-terminal-cloud/internal/outbox"
"github.com/jackc/pgx/v5"
)
const DirectStoryEvent = "call.story_extraction_requested"
type directStorySnapshot struct {
extractionSnapshot
revision, epoch int64
ready, processed, reviewed bool
}
func loadDirectStorySnapshot(ctx context.Context, q queryer, sid string) (directStorySnapshot, error) {
var v directStorySnapshot
err := q.QueryRow(ctx, `SELECT c.tenant_id::text,c.subject_id::text,c.call_state,c.transcript_revision,c.privacy_epoch,
c.ended_at IS NOT NULL AND c.transcript_state IN ('COMPLETE','PARTIAL'),call_story_authorized(c.id),
EXISTS(SELECT 1 FROM call_story_extraction e WHERE e.session_id=c.id AND e.transcript_revision=c.transcript_revision AND e.privacy_epoch=c.privacy_epoch),
EXISTS(SELECT 1 FROM story_draft sd WHERE sd.call_session_id=c.id AND (sd.status IN ('IN_REVIEW','CONFIRMED','REJECTED')
OR (sd.status='CANDIDATE' AND EXISTS(SELECT 1 FROM fact_candidate fc WHERE fc.story_id=sd.id AND (fc.version>1 OR fc.status<>'CANDIDATE')))))
FROM call_session c WHERE c.id=$1::uuid AND c.budget_snapshot->>'protocol'='DIRECT-CHAT-1'`, sid).Scan(
&v.tenantID, &v.subjectID, &v.status, &v.revision, &v.epoch, &v.ready, &v.storyExtractionAllowed, &v.processed, &v.reviewed)
if errors.Is(err, pgx.ErrNoRows) {
return v, nil
}
if err != nil {
return v, err
}
v.exists = true
if !v.storyExtractionAllowed || !v.ready || v.processed {
return v, nil
}
rows, err := q.Query(ctx, `SELECT u.utterance_id,u.text_content,u.interrupted,u.source_revision
FROM call_utterance u JOIN call_session c ON c.id=u.session_id WHERE u.session_id=$1::uuid AND u.speaker='USER'
AND u.text_content<>'[deleted]' AND length(trim(u.text_content))>0
AND NOT EXISTS(SELECT 1 FROM memory_source_suppression ms CROSS JOIN LATERAL jsonb_array_elements(ms.source_refs) r
WHERE ms.subject_id=c.subject_id AND r->>'session_id'=c.id::text AND (r->>'utterance_id' IS NULL OR r->>'utterance_id'=u.utterance_id))
ORDER BY u.source_at,u.utterance_id`, sid)
if err != nil {
return v, err
}
defer rows.Close()
for rows.Next() {
var item turn
if err = rows.Scan(&item.id, &item.text, &item.interrupted, &item.sourceRevision); err != nil {
return v, err
}
item.publicID, item.speaker = item.id, "subject"
v.turns = append(v.turns, item)
}
if err = rows.Err(); err != nil {
return v, err
}
v.digest = snapshotDigest(v.extractionSnapshot)
return v, nil
}
// HandleDirectStory shares the existing story generator and review tables, but
// never turns provider summaries or assistant speech into the elder's story.
func (p *Processor) HandleDirectStory(ctx context.Context, event outbox.Event) error {
var request struct {
GenerationVersion int64 `json:"generation_version"`
PrivacyEpoch int64 `json:"privacy_epoch"`
}
if json.Unmarshal(event.Payload, &request) != nil || request.GenerationVersion <= 0 || request.PrivacyEpoch < 0 {
return errors.New("invalid direct story extraction request")
}
snapshot, err := loadDirectStorySnapshot(ctx, p.pool, event.AggregateID)
if err != nil {
return err
}
if !snapshot.exists || !snapshot.storyExtractionAllowed || snapshot.processed || snapshot.revision != request.GenerationVersion || snapshot.epoch != request.PrivacyEpoch {
return nil
}
if !snapshot.ready {
return outbox.Retryable(ErrConversationContentNotReady)
}
var generated GenerationResult
var previous []PreviousStory
multi := false
if g, ok := p.generator.(interface{ SupportsEventStories() bool }); ok {
multi = g.SupportsEventStories()
}
outcome := "EMPTY"
if snapshot.reviewed && !multi {
outcome = "REVIEWED"
} else if len(snapshot.turns) > 0 {
generated, previous, err = p.generateEventsSafely(ctx, snapshot.extractionSnapshot, time.Now().In(time.FixedZone("CST", 8*3600)).Format("2006-01-02"))
if err != nil {
return err
}
outcome = "NO_STORY"
if generated.Meaningful && generated.StoryWorthy && generated.AllowStory {
outcome = "STORY"
}
}
// The external model call has no open transaction. Lock privacy first, then
// the call, and re-read in a new statement after any lock wait.
tx, err := p.pool.Begin(ctx)
if err != nil {
return err
}
defer tx.Rollback(context.Background())
if outcome != "REVIEWED" && (generated.Stories != nil || generated.DailyEvents != nil) {
if err = lockStoryContext(ctx, tx, snapshot.extractionSnapshot, previous); err != nil {
return err
}
}
var locked string
if err = tx.QueryRow(ctx, `SELECT subject_id::text FROM memory_user_state WHERE subject_id=$1::uuid FOR UPDATE`, snapshot.subjectID).Scan(&locked); err != nil {
return err
}
if err = tx.QueryRow(ctx, `SELECT id::text FROM call_session WHERE id=$1::uuid FOR UPDATE`, event.AggregateID).Scan(&locked); err != nil {
return err
}
current, err := loadDirectStorySnapshot(ctx, tx, event.AggregateID)
if err != nil {
return err
}
if !current.exists || !current.storyExtractionAllowed || current.processed || current.revision != request.GenerationVersion || current.epoch != request.PrivacyEpoch {
return nil
}
if !current.ready {
return outbox.Retryable(ErrConversationContentNotReady)
}
if current.reviewed && !multi {
outcome = "REVIEWED"
} else if current.digest != snapshot.digest {
return outbox.Retryable(ErrConversationContentChanged)
}
if outcome != "REVIEWED" {
// Late final subtitles replace only untouched candidates. Human edits,
// confirmations and rejections are never overwritten by background work.
if _, err = tx.Exec(ctx, `UPDATE fact_candidate SET status='SUPERSEDED',version=version+1,updated_at=now() WHERE story_id IN
(SELECT id FROM story_draft WHERE call_session_id=$1::uuid AND status='CANDIDATE' AND generation_version<$2)`, event.AggregateID, current.revision); err != nil {
return err
}
if _, err = tx.Exec(ctx, `UPDATE story_draft SET status='SUPERSEDED',version=version+1,updated_at=now()
WHERE call_session_id=$1::uuid AND status='CANDIDATE' AND generation_version<$2`, event.AggregateID, current.revision); err != nil {
return err
}
if generated.Stories != nil || generated.DailyEvents != nil {
if err = storeEventStories(ctx, tx, "", event.AggregateID, "", current.epoch, current.extractionSnapshot, generated, current.revision, previous); err != nil {
return err
}
} else if outcome == "STORY" {
if err = storeStoryCandidate(ctx, tx, "", event.AggregateID, current.epoch, current.extractionSnapshot, generated, current.revision); err != nil {
return err
}
}
}
if _, err = tx.Exec(ctx, `INSERT INTO call_story_extraction(session_id,transcript_revision,privacy_epoch,outcome,model_ref,prompt_version)
VALUES($1::uuid,$2,$3,$4,$5,$6) ON CONFLICT DO NOTHING`, event.AggregateID, current.revision, current.epoch, outcome, generated.ModelRef, generated.PromptVersion); err != nil {
return fmt.Errorf("store direct story extraction result: %w", err)
}
return tx.Commit(ctx)
}