@pinecall/room 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +283 -0
  3. package/dist/index.d.ts +9 -0
  4. package/dist/index.d.ts.map +1 -0
  5. package/dist/index.js +4 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/log/fold.d.ts +23 -0
  8. package/dist/log/fold.d.ts.map +1 -0
  9. package/dist/log/fold.js +40 -0
  10. package/dist/log/fold.js.map +1 -0
  11. package/dist/log/follow.d.ts +24 -0
  12. package/dist/log/follow.d.ts.map +1 -0
  13. package/dist/log/follow.js +96 -0
  14. package/dist/log/follow.js.map +1 -0
  15. package/dist/log/sse.d.ts +13 -0
  16. package/dist/log/sse.d.ts.map +1 -0
  17. package/dist/log/sse.js +50 -0
  18. package/dist/log/sse.js.map +1 -0
  19. package/dist/react/index.d.ts +13 -0
  20. package/dist/react/index.d.ts.map +1 -0
  21. package/dist/react/index.js +65 -0
  22. package/dist/react/index.js.map +1 -0
  23. package/dist/room.d.ts +54 -0
  24. package/dist/room.d.ts.map +1 -0
  25. package/dist/room.js +283 -0
  26. package/dist/room.js.map +1 -0
  27. package/dist/rows.d.ts +37 -0
  28. package/dist/rows.d.ts.map +1 -0
  29. package/dist/rows.js +89 -0
  30. package/dist/rows.js.map +1 -0
  31. package/dist/seat/join.d.ts +35 -0
  32. package/dist/seat/join.d.ts.map +1 -0
  33. package/dist/seat/join.js +64 -0
  34. package/dist/seat/join.js.map +1 -0
  35. package/dist/seat/livekit.d.ts +51 -0
  36. package/dist/seat/livekit.d.ts.map +1 -0
  37. package/dist/seat/livekit.js +8 -0
  38. package/dist/seat/livekit.js.map +1 -0
  39. package/dist/seat/sink.d.ts +11 -0
  40. package/dist/seat/sink.d.ts.map +1 -0
  41. package/dist/seat/sink.js +23 -0
  42. package/dist/seat/sink.js.map +1 -0
  43. package/dist/server/index.d.ts +48 -0
  44. package/dist/server/index.d.ts.map +1 -0
  45. package/dist/server/index.js +73 -0
  46. package/dist/server/index.js.map +1 -0
  47. package/dist/store.d.ts +20 -0
  48. package/dist/store.d.ts.map +1 -0
  49. package/dist/store.js +55 -0
  50. package/dist/store.js.map +1 -0
  51. package/package.json +66 -0
@@ -0,0 +1,96 @@
1
+ /** Follow a call's log over fetch: from where it was, through a dropped stream, to the score. */
2
+ import { TERMINAL_EVENT } from "@pinecall/protocol";
3
+ import { SseParser } from "./sse.js";
4
+ // A dropped stream is opened again after this, doubling to the ceiling: a gateway restarting is
5
+ // seconds, and a page hammering it every 100 ms would only make those seconds longer.
6
+ const RECONNECT_MS = 500;
7
+ const RECONNECT_CEILING_MS = 8_000;
8
+ // The relay answers 204 to a sealed call whose cursor is at the end: nothing more will be said.
9
+ const NOTHING_MORE = 204;
10
+ /** Open the log at `url`. Returns the call that stops it; stopping twice is stopping once. */
11
+ export function followLog(url, reader, fetcher) {
12
+ const controller = new AbortController();
13
+ let lastId = null;
14
+ let backoff = RECONNECT_MS;
15
+ let ours = true;
16
+ const stop = () => {
17
+ ours = false;
18
+ controller.abort();
19
+ };
20
+ const end = (ending) => {
21
+ stop();
22
+ reader.onEnded(ending);
23
+ };
24
+ const again = async () => {
25
+ reader.onConnection("reconnecting");
26
+ await slept(backoff, controller.signal);
27
+ backoff = Math.min(backoff * 2, RECONNECT_CEILING_MS);
28
+ };
29
+ const read = async (body) => {
30
+ const parser = new SseParser();
31
+ const decoder = new TextDecoder();
32
+ const chunks = body.getReader();
33
+ for (;;) {
34
+ const { value, done } = await chunks.read();
35
+ if (done)
36
+ return;
37
+ for (const message of parser.feed(decoder.decode(value, { stream: true }))) {
38
+ if (message.id !== null)
39
+ lastId = message.id;
40
+ reader.onMessage(message);
41
+ if (!ours)
42
+ return;
43
+ if (message.event === TERMINAL_EVENT)
44
+ return end({ kind: "scored" });
45
+ }
46
+ }
47
+ };
48
+ const connect = async () => {
49
+ while (ours) {
50
+ let answer;
51
+ try {
52
+ answer = await fetcher(url, {
53
+ headers: { accept: "text/event-stream", ...(lastId === null ? {} : { "last-event-id": lastId }) },
54
+ signal: controller.signal,
55
+ });
56
+ }
57
+ catch {
58
+ if (!ours)
59
+ return;
60
+ await again();
61
+ continue;
62
+ }
63
+ if (!ours)
64
+ return;
65
+ if (answer.status === NOTHING_MORE)
66
+ return end({ kind: "sealed" });
67
+ if (!answer.ok || answer.body === null)
68
+ return end({ kind: "refused", status: answer.status });
69
+ reader.onConnection("live");
70
+ backoff = RECONNECT_MS;
71
+ try {
72
+ await read(answer.body);
73
+ }
74
+ catch {
75
+ // The body broke mid-stream: the same as it ending early, below.
76
+ }
77
+ if (!ours)
78
+ return;
79
+ // The body ended without the score: the relay went away mid-call. Resume where we were.
80
+ await again();
81
+ }
82
+ };
83
+ reader.onConnection("connecting");
84
+ void connect();
85
+ return stop;
86
+ }
87
+ function slept(ms, signal) {
88
+ return new Promise((wake) => {
89
+ const timer = setTimeout(wake, ms);
90
+ signal.addEventListener("abort", () => {
91
+ clearTimeout(timer);
92
+ wake();
93
+ }, { once: true });
94
+ });
95
+ }
96
+ //# sourceMappingURL=follow.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"follow.js","sourceRoot":"","sources":["../../src/log/follow.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ /** The SSE parser under the log follow: text in, in chunks; every complete message out, in order. */
2
+ /** One SSE message, as the wire framed it: the id line, the event line, the data lines joined. */
3
+ export interface SseMessage {
4
+ id: string | null;
5
+ event: string | null;
6
+ data: string;
7
+ }
8
+ /** Feed SSE text in chunks; every complete message comes out, in order. */
9
+ export declare class SseParser {
10
+ #private;
11
+ feed(chunk: string): SseMessage[];
12
+ }
13
+ //# sourceMappingURL=sse.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.d.ts","sourceRoot":"","sources":["../../src/log/sse.ts"],"names":[],"mappings":"AAAA,qGAAqG;AAErG,kGAAkG;AAClG,MAAM,WAAW,UAAU;IACzB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;CACd;AAOD,2EAA2E;AAC3E,qBAAa,SAAS;;IAMpB,IAAI,CAAC,KAAK,EAAE,MAAM,GAAG,UAAU,EAAE,CAWhC;CAoBF"}
@@ -0,0 +1,50 @@
1
+ /** The SSE parser under the log follow: text in, in chunks; every complete message out, in order. */
2
+ // The log is a fetch whose body is read line by line, not an EventSource: an EventSource cannot say
3
+ // what it was answered, so a sealed call (204) and a refused one (401) would look like a dropped
4
+ // one, and it reconnects on a schedule of its own. The same frames, the same `Last-Event-ID` on a
5
+ // reconnect — sent by the follow instead of by the browser. The parser (the console's, as it is
6
+ // there) is pure and tested on its own.
7
+ /** Feed SSE text in chunks; every complete message comes out, in order. */
8
+ export class SseParser {
9
+ #buffer = "";
10
+ #id = null;
11
+ #event = null;
12
+ #data = [];
13
+ feed(chunk) {
14
+ this.#buffer += chunk;
15
+ const messages = [];
16
+ let at;
17
+ while ((at = this.#buffer.search(/\r\n|\n|\r/)) !== -1) {
18
+ const line = this.#buffer.slice(0, at);
19
+ this.#buffer = this.#buffer.slice(at + (this.#buffer.startsWith("\r\n", at) ? 2 : 1));
20
+ const done = this.#line(line);
21
+ if (done !== null)
22
+ messages.push(done);
23
+ }
24
+ return messages;
25
+ }
26
+ // A blank line ends a message; a line starting with `:` is a comment; otherwise `field: value`.
27
+ #line(line) {
28
+ if (line === "") {
29
+ if (this.#data.length === 0 && this.#event === null && this.#id === null)
30
+ return null;
31
+ const message = { id: this.#id, event: this.#event, data: this.#data.join("\n") };
32
+ this.#event = null;
33
+ this.#data = [];
34
+ return message;
35
+ }
36
+ if (line.startsWith(":"))
37
+ return null;
38
+ const colon = line.indexOf(":");
39
+ const field = colon === -1 ? line : line.slice(0, colon);
40
+ const value = colon === -1 ? "" : line.slice(colon + 1).replace(/^ /, "");
41
+ if (field === "id")
42
+ this.#id = value;
43
+ else if (field === "event")
44
+ this.#event = value;
45
+ else if (field === "data")
46
+ this.#data.push(value);
47
+ return null;
48
+ }
49
+ }
50
+ //# sourceMappingURL=sse.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"sse.js","sourceRoot":"","sources":["../../src/log/sse.ts"],"names":[],"mappings":"AAAA,qGAAqG;AASrG,oGAAoG;AACpG,iGAAiG;AACjG,kGAAkG;AAClG,gGAAgG;AAChG,wCAAwC;AACxC,2EAA2E;AAC3E,MAAM,OAAO,SAAS;IACpB,OAAO,GAAG,EAAE,CAAC;IACb,GAAG,GAAkB,IAAI,CAAC;IAC1B,MAAM,GAAkB,IAAI,CAAC;IAC7B,KAAK,GAAa,EAAE,CAAC;IAErB,IAAI,CAAC,KAAa;QAChB,IAAI,CAAC,OAAO,IAAI,KAAK,CAAC;QACtB,MAAM,QAAQ,GAAiB,EAAE,CAAC;QAClC,IAAI,EAAU,CAAC;QACf,OAAO,CAAC,EAAE,GAAG,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;YACvD,MAAM,IAAI,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;YACvC,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,UAAU,CAAC,MAAM,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YACtF,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC9B,IAAI,IAAI,KAAK,IAAI;gBAAE,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACzC,CAAC;QACD,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,gGAAgG;IAChG,KAAK,CAAC,IAAY;QAChB,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YAChB,IAAI,IAAI,CAAC,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,IAAI,CAAC,MAAM,KAAK,IAAI,IAAI,IAAI,CAAC,GAAG,KAAK,IAAI;gBAAE,OAAO,IAAI,CAAC;YACtF,MAAM,OAAO,GAAe,EAAE,EAAE,EAAE,IAAI,CAAC,GAAG,EAAE,KAAK,EAAE,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;YAC9F,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC;YACnB,IAAI,CAAC,KAAK,GAAG,EAAE,CAAC;YAChB,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,IAAI,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YAAE,OAAO,IAAI,CAAC;QACtC,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAChC,MAAM,KAAK,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACzD,MAAM,KAAK,GAAG,KAAK,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;QAC1E,IAAI,KAAK,KAAK,IAAI;YAAE,IAAI,CAAC,GAAG,GAAG,KAAK,CAAC;aAChC,IAAI,KAAK,KAAK,OAAO;YAAE,IAAI,CAAC,MAAM,GAAG,KAAK,CAAC;aAC3C,IAAI,KAAK,KAAK,MAAM;YAAE,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAClD,OAAO,IAAI,CAAC;IACd,CAAC;CACF"}
@@ -0,0 +1,13 @@
1
+ /** @pinecall/room/react: a store read the way React reads one, and one room per mounted component. */
2
+ import { type RoomOptions, type RoomState, type RoomStore, type Store } from "../index.js";
3
+ /** The store's state, re-rendering when it changes. Any store of this package. */
4
+ export declare function useStore<T>(store: Store<T>): T;
5
+ /** What `useRoom` hands a component: the state, and the verbs to change it. */
6
+ export type RoomHandle = RoomState & Pick<RoomStore, "start" | "send" | "callMe" | "leave" | "playSound">;
7
+ /**
8
+ * One room for as long as the component is mounted, closed when it unmounts. The options are read
9
+ * when they are used, never when the room was made: a `tokens` written inline, new every render,
10
+ * is the one that runs.
11
+ */
12
+ export declare function useRoom(options: RoomOptions): RoomHandle;
13
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,sGAAsG;AAItG,OAAO,EAAQ,KAAK,WAAW,EAAE,KAAK,SAAS,EAAE,KAAK,SAAS,EAAE,KAAK,KAAK,EAAE,MAAM,aAAa,CAAC;AAEjG,kFAAkF;AAClF,wBAAgB,QAAQ,CAAC,CAAC,EAAE,KAAK,EAAE,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAI9C;AAED,+EAA+E;AAC/E,MAAM,MAAM,UAAU,GAAG,SAAS,GAAG,IAAI,CAAC,SAAS,EAAE,OAAO,GAAG,MAAM,GAAG,QAAQ,GAAG,OAAO,GAAG,WAAW,CAAC,CAAC;AAE1G;;;;GAIG;AACH,wBAAgB,OAAO,CAAC,OAAO,EAAE,WAAW,GAAG,UAAU,CAuBxD"}
@@ -0,0 +1,65 @@
1
+ /** @pinecall/room/react: a store read the way React reads one, and one room per mounted component. */
2
+ import { useCallback, useEffect, useRef, useState, useSyncExternalStore } from "react";
3
+ import { room } from "../index.js";
4
+ /** The store's state, re-rendering when it changes. Any store of this package. */
5
+ export function useStore(store) {
6
+ const subscribe = useCallback((changed) => store.subscribe(changed), [store]);
7
+ const read = () => store.state;
8
+ return useSyncExternalStore(subscribe, read, read);
9
+ }
10
+ /**
11
+ * One room for as long as the component is mounted, closed when it unmounts. The options are read
12
+ * when they are used, never when the room was made: a `tokens` written inline, new every render,
13
+ * is the one that runs.
14
+ */
15
+ export function useRoom(options) {
16
+ const latest = useRef(options);
17
+ useEffect(() => {
18
+ latest.current = options;
19
+ });
20
+ const [store, setStore] = useState(() => room(readingLatest(latest)));
21
+ const open = useRef(true);
22
+ useEffect(() => {
23
+ // StrictMode unmounts and mounts again with the same state: the room the first mount closed
24
+ // is replaced once, rather than handed back closed.
25
+ if (!open.current) {
26
+ open.current = true;
27
+ setStore(room(readingLatest(latest)));
28
+ return;
29
+ }
30
+ return () => {
31
+ open.current = false;
32
+ store.close();
33
+ };
34
+ }, [store]);
35
+ const state = useStore(store);
36
+ const { start, send, callMe, leave, playSound } = store;
37
+ return { ...state, start, send, callMe, leave, playSound };
38
+ }
39
+ /** Options whose every field is the one the component rendered last. */
40
+ function readingLatest(latest) {
41
+ return {
42
+ get tokens() {
43
+ return latest.current.tokens;
44
+ },
45
+ get log() {
46
+ return latest.current.log;
47
+ },
48
+ get callMe() {
49
+ return latest.current.callMe;
50
+ },
51
+ get linger() {
52
+ return latest.current.linger;
53
+ },
54
+ get fetch() {
55
+ return latest.current.fetch;
56
+ },
57
+ get livekit() {
58
+ return latest.current.livekit;
59
+ },
60
+ get onSkipped() {
61
+ return latest.current.onSkipped;
62
+ },
63
+ };
64
+ }
65
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/react/index.ts"],"names":[],"mappings":"AAAA,sGAAsG;AAEtG,OAAO,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,OAAO,CAAC;AAEvF,OAAO,EAAE,IAAI,EAAgE,MAAM,aAAa,CAAC;AAEjG,kFAAkF;AAClF,MAAM,UAAU,QAAQ,CAAI,KAAe;IACzC,MAAM,SAAS,GAAG,WAAW,CAAC,CAAC,OAAmB,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAC1F,MAAM,IAAI,GAAG,GAAM,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC;IAClC,OAAO,oBAAoB,CAAC,SAAS,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AACrD,CAAC;AAKD;;;;GAIG;AACH,MAAM,UAAU,OAAO,CAAC,OAAoB;IAC1C,MAAM,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,CAAC;IAC/B,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,CAAC,OAAO,GAAG,OAAO,CAAC;IAC3B,CAAC,CAAC,CAAC;IACH,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;IACtE,MAAM,IAAI,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;IAC1B,SAAS,CAAC,GAAG,EAAE;QACb,4FAA4F;QAC5F,oDAAoD;QACpD,IAAI,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAClB,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;YACpB,QAAQ,CAAC,IAAI,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC;YACtC,OAAO;QACT,CAAC;QACD,OAAO,GAAG,EAAE;YACV,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;YACrB,KAAK,CAAC,KAAK,EAAE,CAAC;QAChB,CAAC,CAAC;IACJ,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IACZ,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAC9B,MAAM,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,GAAG,KAAK,CAAC;IACxD,OAAO,EAAE,GAAG,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC;AAC7D,CAAC;AAED,wEAAwE;AACxE,SAAS,aAAa,CAAC,MAAyC;IAC9D,OAAO;QACL,IAAI,MAAM;YACR,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAC/B,CAAC;QACD,IAAI,GAAG;YACL,OAAO,MAAM,CAAC,OAAO,CAAC,GAAG,CAAC;QAC5B,CAAC;QACD,IAAI,MAAM;YACR,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAC/B,CAAC;QACD,IAAI,MAAM;YACR,OAAO,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC;QAC/B,CAAC;QACD,IAAI,KAAK;YACP,OAAO,MAAM,CAAC,OAAO,CAAC,KAAK,CAAC;QAC9B,CAAC;QACD,IAAI,OAAO;YACT,OAAO,MAAM,CAAC,OAAO,CAAC,OAAO,CAAC;QAChC,CAAC;QACD,IAAI,SAAS;YACX,OAAO,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC;QAClC,CAAC;KACF,CAAC;AACJ,CAAC"}
package/dist/room.d.ts ADDED
@@ -0,0 +1,54 @@
1
+ /** One conversation, as the page sees it: the phases, the verbs, and the seat and the log joined in one store. */
2
+ import { type Entry, type State } from "@pinecall/protocol";
3
+ import { type Connection } from "./log/follow.js";
4
+ import { type Speaking } from "./seat/join.js";
5
+ import { type LivekitModule } from "./seat/livekit.js";
6
+ import type { Minted } from "./server/index.js";
7
+ import { type Store } from "./store.js";
8
+ export type { Minted };
9
+ /** Where the conversation is. `ringing` is a phone call nobody has answered yet. */
10
+ export type Phase = "idle" | "opening" | "ringing" | "live" | "ended" | "failed";
11
+ /** Spoken, written, or on the visitor's own phone. The same agent every way. */
12
+ export type Mode = "talk" | "chat" | "phone";
13
+ /** Everything the page draws from. Every field is the truth at the moment it was published. */
14
+ export interface RoomState {
15
+ phase: Phase;
16
+ mode: Mode | null;
17
+ call: string;
18
+ /** The sentence to show when phase is "failed". */
19
+ error: string;
20
+ /** The call as the protocol folds it: turns, tools, the agent's state, the cost once it is known. */
21
+ log: State;
22
+ entries: Entry[];
23
+ connection: Connection;
24
+ /** True when the browser has not been told it may make a sound: ask for a click. */
25
+ wantsSound: boolean;
26
+ speaking: Speaking;
27
+ }
28
+ export interface RoomOptions {
29
+ /** A ticket for a seat, from the tenant's own server. The page never holds a key. */
30
+ tokens: (scope: "talk" | "chat") => Promise<Minted>;
31
+ /** The URL the call's log is relayed at by the tenant's server. */
32
+ log: (call: string, minted?: Minted) => string;
33
+ /** Ask the tenant's server to have the agent call `to`. */
34
+ callMe?: ((to: string) => Promise<{
35
+ call: string;
36
+ }>) | undefined;
37
+ /** How long the log is read after the seat closes, waiting for the score. Default 60 000 ms: a worker
38
+ * notices a caller has gone some twenty seconds after the fact, and the score comes after that. */
39
+ linger?: number | undefined;
40
+ fetch?: typeof fetch | undefined;
41
+ livekit?: (() => Promise<LivekitModule>) | undefined;
42
+ /** An entry of the log this copy of the wire could not read. The rest of the call still draws. */
43
+ onSkipped?: ((why: string) => void) | undefined;
44
+ }
45
+ export interface RoomStore extends Store<RoomState> {
46
+ start(mode: "talk" | "chat"): Promise<void>;
47
+ send(text: string): Promise<void>;
48
+ callMe(to: string): Promise<void>;
49
+ leave(): Promise<void>;
50
+ playSound(): void;
51
+ }
52
+ /** A room for one visitor. Nothing opens until a verb is called. */
53
+ export declare function room(options: RoomOptions): RoomStore;
54
+ //# sourceMappingURL=room.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room.d.ts","sourceRoot":"","sources":["../src/room.ts"],"names":[],"mappings":"AAAA,kHAAkH;AASlH,OAAO,EAAgB,KAAK,KAAK,EAAE,KAAK,KAAK,EAAE,MAAM,oBAAoB,CAAC;AAG1E,OAAO,EAAa,KAAK,UAAU,EAAe,MAAM,iBAAiB,CAAC;AAE1E,OAAO,EAAuB,KAAK,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AACpE,OAAO,EAAe,KAAK,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,mBAAmB,CAAC;AAChD,OAAO,EAAQ,KAAK,KAAK,EAAE,MAAM,YAAY,CAAC;AAE9C,YAAY,EAAE,MAAM,EAAE,CAAC;AAEvB,oFAAoF;AACpF,MAAM,MAAM,KAAK,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,CAAC;AAEjF,gFAAgF;AAChF,MAAM,MAAM,IAAI,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;AAE7C,+FAA+F;AAC/F,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,KAAK,CAAC;IACb,IAAI,EAAE,IAAI,GAAG,IAAI,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,mDAAmD;IACnD,KAAK,EAAE,MAAM,CAAC;IACd,qGAAqG;IACrG,GAAG,EAAE,KAAK,CAAC;IACX,OAAO,EAAE,KAAK,EAAE,CAAC;IACjB,UAAU,EAAE,UAAU,CAAC;IACvB,oFAAoF;IACpF,UAAU,EAAE,OAAO,CAAC;IACpB,QAAQ,EAAE,QAAQ,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,qFAAqF;IACrF,MAAM,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,CAAC;IACpD,mEAAmE;IACnE,GAAG,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,KAAK,MAAM,CAAC;IAC/C,2DAA2D;IAC3D,MAAM,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC;QAAE,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC,GAAG,SAAS,CAAC;IACjE;uGACmG;IACnG,MAAM,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC5B,KAAK,CAAC,EAAE,OAAO,KAAK,GAAG,SAAS,CAAC;IACjC,OAAO,CAAC,EAAE,CAAC,MAAM,OAAO,CAAC,aAAa,CAAC,CAAC,GAAG,SAAS,CAAC;IACrD,kGAAkG;IAClG,SAAS,CAAC,EAAE,CAAC,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC,GAAG,SAAS,CAAC;CACjD;AAED,MAAM,WAAW,SAAU,SAAQ,KAAK,CAAC,SAAS,CAAC;IACjD,KAAK,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC5C,IAAI,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAClC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,SAAS,IAAI,IAAI,CAAC;CACnB;AAkBD,oEAAoE;AACpE,wBAAgB,IAAI,CAAC,OAAO,EAAE,WAAW,GAAG,SAAS,CAuOpD"}
package/dist/room.js ADDED
@@ -0,0 +1,283 @@
1
+ /** One conversation, as the page sees it: the phases, the verbs, and the seat and the log joined in one store. */
2
+ //
3
+ // Two things happen at once and they are not the same thing. LiveKit carries the audio and the
4
+ // typed lines — the room the visitor joins with a ticket the tenant's own server minted, so the
5
+ // org's key never reaches a browser. The call's LOG, relayed by that same server, carries what the
6
+ // agent is actually doing: the turns, every tool it called and what came back, its declared state.
7
+ // The log is folded by the protocol's own reducer, so what the page draws is what the console
8
+ // draws, from the same bytes. A phone call has no seat at all: the log is the whole of it.
9
+ import { initialState } from "@pinecall/protocol";
10
+ import { LogFold } from "./log/fold.js";
11
+ import { followLog } from "./log/follow.js";
12
+ import { joinSeat } from "./seat/join.js";
13
+ import { loadLivekit } from "./seat/livekit.js";
14
+ import { hiddenSink } from "./seat/sink.js";
15
+ import { cell } from "./store.js";
16
+ const LINGER_MS = 60_000;
17
+ // A seat asked to close is given this long. A network that has gone does not hold the page up.
18
+ const PARTING_MS = 3_000;
19
+ const QUIET = { agent: false, user: false, level: 0 };
20
+ const NO_MICROPHONE = "I could not get to your microphone. Check the browser's permission, or write instead.";
21
+ const NO_CALL_ME = "room() was given no callMe: the call is placed by your server";
22
+ /** A room for one visitor. Nothing opens until a verb is called. */
23
+ export function room(options) {
24
+ const store = cell(idle());
25
+ const { publish } = store;
26
+ const parting = new Set();
27
+ let now = null;
28
+ let closed = false;
29
+ const busy = () => ["opening", "ringing", "live"].includes(store.state.phase);
30
+ const current = (c) => now === c;
31
+ // The follow and the seat down, now. Returns the seat's leaving, for a caller that waits on it.
32
+ const shut = (c) => {
33
+ c.stopFollow?.();
34
+ c.stopFollow = null;
35
+ if (c.linger !== null)
36
+ clearTimeout(c.linger);
37
+ c.linger = null;
38
+ const seat = c.seat;
39
+ c.seat = null;
40
+ return seat === null ? Promise.resolve() : seat.leave().catch(() => undefined);
41
+ };
42
+ // A verb's first move, read from the store and never from a closure: a second click while the
43
+ // first is still opening is nothing, and an old conversation is shut before a new one opens.
44
+ const begin = (mode) => {
45
+ if (closed || busy())
46
+ return null;
47
+ if (now !== null)
48
+ void shut(now);
49
+ const fold = new LogFold((why) => options.onSkipped?.(why));
50
+ const c = { mode, fold, seat: null, stopFollow: null, linger: null };
51
+ now = c;
52
+ publish({ ...idle(), phase: "opening", mode, connection: "connecting" });
53
+ return c;
54
+ };
55
+ const fail = (c, error) => {
56
+ if (!current(c))
57
+ return;
58
+ void shut(c);
59
+ publish({ phase: "failed", error, connection: "ended", wantsSound: false, speaking: QUIET });
60
+ };
61
+ // The call is over for the visitor. The seat goes now; the log is read a while longer, because
62
+ // the agent's last words, the cost and the score are entries that arrive after the room closed.
63
+ const end = (c) => {
64
+ publish({ phase: "ended", wantsSound: false, speaking: QUIET });
65
+ const seat = c.seat;
66
+ c.seat = null;
67
+ if (c.stopFollow !== null && c.linger === null) {
68
+ c.linger = setTimeout(() => {
69
+ c.linger = null;
70
+ c.stopFollow?.();
71
+ c.stopFollow = null;
72
+ publish({ connection: "ended" });
73
+ }, options.linger ?? LINGER_MS);
74
+ }
75
+ return seat === null ? Promise.resolve() : seat.leave().catch(() => undefined);
76
+ };
77
+ const heard = (c, message) => {
78
+ if (c.fold.take(message) === null)
79
+ return;
80
+ store.paint(() => c.fold.snapshot());
81
+ const { status, room: seats } = c.fold.now;
82
+ const phase = store.state.phase;
83
+ if (status === "ended" && (phase === "opening" || phase === "ringing" || phase === "live"))
84
+ void end(c);
85
+ // A phone call has no seat to say it was answered: the log does. A seat decides for itself.
86
+ if (c.mode === "phone" && status === "active" && phase === "ringing")
87
+ publish({ phase: "live" });
88
+ if (c.mode === "phone" && store.state.phase === "live") {
89
+ const speaking = (kinds) => seats?.participants.some((one) => kinds.includes(one.kind) && one.speaking) ?? false;
90
+ say({ agent: speaking(["agent"]), user: speaking(["caller", "sip"]), level: 0 });
91
+ }
92
+ };
93
+ const ended = (c, ending) => {
94
+ c.stopFollow = null;
95
+ if (c.linger !== null)
96
+ clearTimeout(c.linger);
97
+ c.linger = null;
98
+ const phase = store.state.phase;
99
+ const waiting = c.mode === "phone" && (phase === "ringing" || phase === "live");
100
+ if (ending.kind === "refused" && waiting) {
101
+ publish({ connection: "ended", phase: "failed", error: `the log answered ${ending.status}`, speaking: QUIET });
102
+ }
103
+ else if (ending.kind === "sealed" && c.mode === "phone" && phase === "ringing") {
104
+ publish({ connection: "ended", phase: "ended" });
105
+ }
106
+ else {
107
+ publish({ connection: "ended" });
108
+ }
109
+ };
110
+ const follow = (c, url) => {
111
+ c.stopFollow = followLog(url, {
112
+ onMessage: (message) => {
113
+ if (current(c))
114
+ heard(c, message);
115
+ },
116
+ onConnection: (connection) => {
117
+ if (current(c))
118
+ publish({ connection });
119
+ },
120
+ onEnded: (ending) => {
121
+ if (current(c))
122
+ ended(c, ending);
123
+ },
124
+ }, options.fetch ?? fetch);
125
+ };
126
+ const say = (speaking) => {
127
+ const was = store.state.speaking;
128
+ if (was.agent === speaking.agent && was.user === speaking.user && was.level === speaking.level)
129
+ return;
130
+ publish({ speaking });
131
+ };
132
+ const seated = async (c, mode, minted) => {
133
+ let livekit;
134
+ try {
135
+ livekit = await (options.livekit ?? loadLivekit)();
136
+ }
137
+ catch (refused) {
138
+ return fail(c, messageOf(refused));
139
+ }
140
+ if (!current(c) || store.state.phase !== "opening")
141
+ return;
142
+ let seat;
143
+ try {
144
+ seat = await joinSeat({
145
+ livekit,
146
+ mode,
147
+ server_url: minted.server_url,
148
+ participant_token: minted.participant_token,
149
+ sink: hiddenSink(),
150
+ on: {
151
+ disconnected: () => {
152
+ if (!current(c))
153
+ return;
154
+ c.seat = null;
155
+ if (busy())
156
+ void end(c);
157
+ },
158
+ speaking: (speaking) => {
159
+ if (current(c))
160
+ say(speaking);
161
+ },
162
+ wantsSound: (wantsSound) => {
163
+ if (current(c))
164
+ publish({ wantsSound });
165
+ },
166
+ },
167
+ });
168
+ }
169
+ catch (refused) {
170
+ return fail(c, mode === "talk" ? NO_MICROPHONE : messageOf(refused));
171
+ }
172
+ // Left, closed or ended by the log while the room was joining: the seat is not wanted.
173
+ if (!current(c) || store.state.phase !== "opening") {
174
+ void seat.leave().catch(() => undefined);
175
+ return;
176
+ }
177
+ c.seat = seat;
178
+ publish({ phase: "live", wantsSound: !seat.canPlaybackAudio() });
179
+ };
180
+ return {
181
+ get state() {
182
+ return store.state;
183
+ },
184
+ subscribe: store.subscribe,
185
+ async start(mode) {
186
+ const c = begin(mode);
187
+ if (c === null)
188
+ return;
189
+ let minted;
190
+ try {
191
+ minted = await options.tokens(mode);
192
+ }
193
+ catch (refused) {
194
+ return fail(c, messageOf(refused));
195
+ }
196
+ if (!current(c) || store.state.phase !== "opening")
197
+ return;
198
+ publish({ call: minted.call });
199
+ // The call id exists before the room is joined, so the log is followed from its first entry
200
+ // rather than from wherever the room happened to open.
201
+ follow(c, options.log(minted.call, minted));
202
+ await seated(c, mode, minted);
203
+ },
204
+ async callMe(to) {
205
+ const c = begin("phone");
206
+ if (c === null)
207
+ return;
208
+ const dial = options.callMe;
209
+ if (dial === undefined)
210
+ return fail(c, NO_CALL_ME);
211
+ let placed;
212
+ try {
213
+ placed = await dial(to);
214
+ }
215
+ catch (refused) {
216
+ return fail(c, messageOf(refused));
217
+ }
218
+ if (!current(c) || store.state.phase !== "opening")
219
+ return;
220
+ publish({ call: placed.call, phase: "ringing" });
221
+ follow(c, options.log(placed.call));
222
+ },
223
+ async send(text) {
224
+ if (text.trim() === "")
225
+ return;
226
+ const seat = now?.seat ?? null;
227
+ if (store.state.phase !== "live" || seat === null)
228
+ throw new Error("not in a call");
229
+ await seat.send(text);
230
+ },
231
+ async leave() {
232
+ const c = now;
233
+ if (c === null || !busy())
234
+ return;
235
+ const leaving = end(c);
236
+ let wake = () => { };
237
+ const waited = new Promise((done) => {
238
+ const timer = setTimeout(done, PARTING_MS);
239
+ wake = () => {
240
+ clearTimeout(timer);
241
+ done();
242
+ };
243
+ });
244
+ parting.add(wake);
245
+ await Promise.race([leaving, waited]);
246
+ wake();
247
+ parting.delete(wake);
248
+ },
249
+ playSound() {
250
+ now?.seat?.playSound();
251
+ publish({ wantsSound: false });
252
+ },
253
+ close() {
254
+ if (closed)
255
+ return;
256
+ closed = true;
257
+ if (now !== null)
258
+ void shut(now);
259
+ now = null;
260
+ for (const wake of parting)
261
+ wake();
262
+ parting.clear();
263
+ store.close();
264
+ },
265
+ };
266
+ }
267
+ function idle() {
268
+ return {
269
+ phase: "idle",
270
+ mode: null,
271
+ call: "",
272
+ error: "",
273
+ log: initialState(),
274
+ entries: [],
275
+ connection: "ended",
276
+ wantsSound: false,
277
+ speaking: QUIET,
278
+ };
279
+ }
280
+ function messageOf(refused) {
281
+ return refused instanceof Error ? refused.message : String(refused);
282
+ }
283
+ //# sourceMappingURL=room.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"room.js","sourceRoot":"","sources":["../src/room.ts"],"names":[],"mappings":"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"}