// Run from the repository root: go run ./docs/examples/room-agent // This example uses temporary SQLite storage and no network or providers. package main import ( "context" "encoding/json" "errors" "fmt" "os" "path/filepath" "slices" "time" "git.soma.salesforce.com/chatbots/realtime-media-conductor/rmc" "git.soma.salesforce.com/chatbots/realtime-media-conductor/storage/sqlite" ) func main() { if err := run(); err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } } func run() error { ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second) defer cancel() directory, err := os.MkdirTemp("", "rmc-room-agent-example-") if err != nil { return err } defer os.RemoveAll(directory) store, err := sqlite.Open(filepath.Join(directory, "example.db")) if err != nil { return err } registry := rmc.NewRegistry() if err := registry.RegisterRoomAgent(MessageStatsAgent{}); err != nil { _ = store.Close() return err } runtime := rmc.NewRuntime(store, rmc.WithRegistry(registry)) defer runtime.Close() // Cancels/drains the agent, then closes storage. // Only this trusted host bootstrap uses Runtime and Store directly. room, err := runtime.CreateRoom(ctx, "message-stats-example") if err != nil { return err } for _, channel := range []rmc.Channel{ {ID: "chat", Type: "text", Direction: "input", Status: "open"}, {ID: "measurements", Type: "json", Direction: "output", Status: "open"}, } { if err := runtime.AddChannel(ctx, room.ID, channel); err != nil { return err } } token, hash, err := rmc.IssueToken() if err != nil { return err } participant := rmc.Participant{ ID: "example.client", Role: "customer", Grants: []rmc.Grant{ {Action: "event:publish", Resource: "chat"}, {Action: "event:subscribe", Resource: "measurements"}, }, } if err := store.AddParticipant(ctx, room.ID, participant, hash); err != nil { return err } session, err := runtime.OpenSession(ctx, room.ID, token) if err != nil { return err } subscription, err := session.Subscribe(ctx, rmc.SubscribeRequest{ Selector: rmc.EventSelector{ChannelIDs: []string{"measurements"}}, }) if err != nil { return err } defer subscription.Close() input, err := session.Publish(ctx, rmc.Publication{ ClientEventID: "one-logical-message", ChannelID: "chat", Type: "text.message.committed", Payload: json.RawMessage(`{"text":"Can you check the shipment?"}`), Visibility: rmc.Visibility{ParticipantIDs: []string{"example.client", "example.message-stats"}}, }) if err != nil { return err } for { select { case <-ctx.Done(): return fmt.Errorf("waiting for the derived event: %w", ctx.Err()) case delivery, ok := <-subscription.Deliveries: if !ok { if err := subscription.Err(); err != nil { return err } return errors.New("subscription ended before the derived event") } if delivery.Event == nil { // A filtered scan checkpoint. continue } event := delivery.Event var result struct { SourceEventID string `json:"source_event_id"` WordCount int `json:"word_count"` } if err := json.Unmarshal(event.Payload, &result); err != nil { return err } if event.Type != "demo.message.measured" || event.ActorID != "example.message-stats" || event.CausationID != input.ID || result.SourceEventID != input.ID || result.WordCount != 5 || event.Visibility.Audience != "" || !slices.Equal(event.Visibility.ParticipantIDs, input.Visibility.ParticipantIDs) { return errors.New("derived event did not match the example contract") } fmt.Println("PASS: scoped client published a message; embedded agent emitted an authorized derived event") fmt.Println("PASS: source reference, agent identity, restricted visibility, and word count verified") return nil } } }