bmux_presenter_plugin_api 0.0.1-alpha.4

BPDL contracts for the optional BMUX presentation plugin
// Contract only. The implementation calls the authoritative runtime Host directly.
plugin bmux.presenter version 1;
capability PRESENTER_VIEW = bmux.presenter.view.v2;
capability PRESENTER_MANAGE = bmux.presenter.manage.v1;
capability PRESENTER_MANAGE_TRUSTED = bmux.presenter.manage.v2;

@capability(PRESENTER_VIEW)
interface presenter-view-v2 {
    enum interaction-mode { receive-only, submit-only, shared, independent }
    record cursor { run_id: string, deck_revision: u64, revision: u64 }
    record participant { id: string, cursor: cursor, mode: interaction-mode, next_action_id: u64? }
    // JSON documents use bmux_presenter::Deck::public() and runtime mirror/asset
    // models. Notes are an explicit, separately authorized projection, never Deck JSON.
    record snapshot {
        cursor: cursor,
        public_deck_json: bytes,
        slide_id: string?,
        reveal_step: u32,
        notes: string?,
        surfaces_json: bytes,
        assets_json: bytes,
    }
    variant poll { unchanged { cursor: cursor }, snapshot { resync: bool, snapshot: snapshot } }
    variant action {
        next, previous, first, last,
        go-to { slide_id: string },
        demo-input { surface_id: string, surface_revision: u64, x: u16, y: u16, input: string },
        effect { name: string, input: string },
    }
    record acknowledgment { action_id: u64, cursor: cursor, outcome: string }
    record asset-chunk { id: string, offset: u64, total: u64, data: bytes }
    enum error { authentication, unauthorized, protocol-version, limit, stale, not-found, invalid, action-conflict, outcome-evicted, action-sequence, effect-uncertain }
    command authenticate(host_id: string, credential: string?, mode: interaction-mode) -> result<participant, error>;
    // No wait holds an unbounded service invocation; callers poll at bounded cadence.
    command poll(host_id: string, participant_id: string, cursor: cursor?) -> result<poll, error>;
    command snapshot(host_id: string, participant_id: string) -> result<snapshot, error>;
    command submit(host_id: string, participant_id: string, action_id: u64, expected: cursor, action: action) -> result<acknowledgment, error>;
    command asset(host_id: string, participant_id: string, id: string, offset: u64, length: u32) -> result<asset-chunk, error>;
    command leave(host_id: string, participant_id: string) -> unit;
}

@capability(PRESENTER_MANAGE)
interface presenter-host-v1 {
    record host-info { id: string, run_id: string, source: string }
    // Paths resolve only in the managing owner's server process. This is privileged
    // management, not a viewer operation or an arbitrary remote document fetch.
    command start(source: string, public_read: bool, presenter_credential: string, audience_credential: string?, live_reload: bool) -> result<host-info, string>;
    command list() -> list<host-info>;
    command reload(host_id: string) -> result<unit, string>;
    command stop(host_id: string) -> result<unit, string>;
}

// Explicit approval is a new versioned contract. V1 creation stays untrusted.
@capability(PRESENTER_MANAGE_TRUSTED)
interface presenter-host-v2 {
    command start(source: string, public_read: bool, presenter_credential: string, audience_credential: string?, live_reload: bool, allow_playbooks: bool) -> result<presenter-host-v1.host-info, string>;
}