palbase 0.6.2 → 0.7.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 (65) hide show
  1. package/dist/ai/index.js +2 -14
  2. package/dist/backend/engine.js +2 -2
  3. package/dist/backend/env.js +1 -1
  4. package/dist/backend/index.js +8 -8
  5. package/dist/backend/stack.js +1 -1
  6. package/dist/backend/test.js +2 -2
  7. package/dist/chunks/3ALGILOX.js +2 -0
  8. package/dist/chunks/{GVFZM4BI.js → 5ZJCEQV5.js} +3 -3
  9. package/dist/chunks/AIIKXCWN.js +2 -0
  10. package/dist/chunks/{53FS342F.js → BKVU4I5R.js} +2 -2
  11. package/dist/chunks/ELEE7XO2.js +18 -0
  12. package/dist/chunks/{DRTZ33XF.js → G3BK7L3J.js} +2 -2
  13. package/dist/chunks/G7E26S3Y.js +13 -0
  14. package/dist/chunks/H2EPK74V.js +20 -0
  15. package/dist/chunks/{VP7V7YRH.js → HEXIIGWS.js} +2 -2
  16. package/dist/chunks/{IYCW6PWG.js → JS3TTUHS.js} +2 -2
  17. package/dist/chunks/{SUX2BJG4.js → KDSWK3XW.js} +1 -1
  18. package/dist/chunks/{U4OZ4HOF.js → L3NTI4AZ.js} +2 -2
  19. package/dist/chunks/LEBVOSG6.js +4 -0
  20. package/dist/chunks/OIA7KLRW.js +2 -0
  21. package/dist/chunks/{AYY4J3B2.js → PJVWGUIT.js} +1 -1
  22. package/dist/chunks/{A3CDCPXX.js → SU7BP6VF.js} +1 -1
  23. package/dist/chunks/{KLANM6M6.js → X3MTLJRV.js} +1 -1
  24. package/dist/chunks/{SXH5AIQX.js → ZBOJEH7K.js} +2 -2
  25. package/dist/cli.js +133 -133
  26. package/dist/refuse/ai.js +1 -1
  27. package/dist/refuse/backend.js +1 -1
  28. package/dist/refuse/server.js +1 -1
  29. package/dist/server/index.js +2 -2
  30. package/dist/server/testing.js +2 -2
  31. package/dist/web/index.js +2 -2
  32. package/dist/web/palbe.js +2 -2
  33. package/dist/web/paywall.js +2 -2
  34. package/dist/web/ssr.js +2 -2
  35. package/dist/web/vite.js +2 -2
  36. package/package.json +2 -2
  37. package/types/ai/src/ai.d.ts +2 -4
  38. package/types/ai/src/chats.d.ts +40 -10
  39. package/types/ai/src/index.d.ts +2 -2
  40. package/types/ai/src/tools.d.ts +3 -8
  41. package/types/backend/src/container.d.ts +2 -2
  42. package/types/backend/src/engine.d.ts +1 -4
  43. package/types/backend/src/gateways.d.ts +250 -0
  44. package/types/backend/src/index.d.ts +2 -2
  45. package/types/backend/src/openapi.d.ts +3 -6
  46. package/types/backend/src/runtime.d.ts +39 -2
  47. package/types/internal/src/chat.d.ts +7 -39
  48. package/types/internal/src/modules/ai.d.ts +68 -35
  49. package/types/internal/src/modules/realtime.d.ts +9 -1
  50. package/types/web/src/backends.d.ts +19 -12
  51. package/types/web/src/chat.d.ts +118 -0
  52. package/types/web/src/client.d.ts +0 -3
  53. package/types/web/src/gateways.d.ts +89 -0
  54. package/types/web/src/index.d.ts +3 -3
  55. package/types/web/src/socket.d.ts +18 -2
  56. package/dist/chunks/2RG3I7LX.js +0 -6
  57. package/dist/chunks/A76UZBAF.js +0 -2
  58. package/dist/chunks/AD5LBZAB.js +0 -4
  59. package/dist/chunks/DRGYTL6T.js +0 -20
  60. package/dist/chunks/IVEAUFGE.js +0 -13
  61. package/dist/chunks/KL7FIRII.js +0 -2
  62. package/types/backend/src/channels.d.ts +0 -81
  63. package/types/backend/src/rooms.d.ts +0 -178
  64. package/types/web/src/ai.d.ts +0 -224
  65. package/types/web/src/rooms.d.ts +0 -61
@@ -1,4 +1,5 @@
1
1
  import { AsyncLocalStorage } from 'node:async_hooks';
2
+ import { type AI } from '../../ai/src/index.js';
2
3
  import type { PalbaseServer } from '../../server/src/index.js';
3
4
  import type { AtomicDatabase as AtomicOf, EnvTypedDatabase } from 'palgres';
4
5
  import type { Schemas, Tables } from './db-env.js';
@@ -29,8 +30,43 @@ export declare function scoped<T extends object>(name: string, impl: T): T;
29
30
  */
30
31
  export declare function serviceOf<T extends object>(name: string, key: PropertyKey, impl: T): T;
31
32
  export declare const requestScope: AsyncLocalStorage<RequestScope>;
32
- /** The project's modules: auth, storage, notify, secrets, flags, analytics, realtime, machines. */
33
- export declare const Palbase: PalbaseServer;
33
+ type GatewayEventsOf<G> = G extends {
34
+ readonly events: infer E;
35
+ } ? E : Record<never, never>;
36
+ type GatewayPayloadOf<S> = S extends {
37
+ readonly '~standard': {
38
+ readonly types?: {
39
+ readonly input: infer I;
40
+ };
41
+ };
42
+ } ? I : unknown;
43
+ /** A gateway's channel, to send its events to (Palbase.gateways.to). */
44
+ export interface GatewayTarget<G> {
45
+ /**
46
+ * `event` to every member of the channel, its payload as the gateway's
47
+ * static events type it. Sent in the order emitted, never thrown: what could
48
+ * not be sent is in the app's log. A durable gateway keeps it.
49
+ */
50
+ emit<K extends keyof GatewayEventsOf<G> & string>(event: K, payload: GatewayPayloadOf<GatewayEventsOf<G>[K]>): void;
51
+ /** The platform writes `source` into the channel from now on: a chat's stream (Palbase.ai.chats.events(id)). */
52
+ pipe(source: {
53
+ pipe(topic: string): Promise<void>;
54
+ }): Promise<void>;
55
+ }
56
+ /** The app's gateways, from any service: Palbase.gateways.to(ChatGateway, { chatId }).emit('token', { text }). */
57
+ export interface GatewaysApi {
58
+ to<G extends Function>(gateway: G, params: Readonly<Record<string, string>>): GatewayTarget<G>;
59
+ }
60
+ /**
61
+ * The project as the app's server reaches it: its modules — auth, storage, notify, secrets, flags, analytics,
62
+ * realtime, machines — its models and agents (Palbase.ai: ask, generate, stream, embed, tools, agents, chats), and
63
+ * the app's gateways (Palbase.gateways).
64
+ */
65
+ export type PalbaseApp = Omit<PalbaseServer, 'ai'> & {
66
+ readonly ai: AI;
67
+ readonly gateways: GatewaysApi;
68
+ };
69
+ export declare const Palbase: PalbaseApp;
34
70
  /**
35
71
  * The request's database, as v2's: `Database.public.<table>`, `$query`,
36
72
  * `$transaction`, `$atomic`, `$asService()`. It is the request's own
@@ -54,3 +90,4 @@ export declare const Log: {
54
90
  warn: (message: string, fields?: Record<string, unknown>) => void;
55
91
  error: (message: string, fields?: Record<string, unknown>) => void;
56
92
  };
93
+ export {};
@@ -88,46 +88,14 @@ export interface AgentEvent {
88
88
  reason?: string;
89
89
  [field: string]: unknown;
90
90
  }
91
- /** The chat's live voice: what was said either way (transcript), when it began and ended, and a task it handed over. */
92
- export interface VoiceEvent {
93
- type: 'voice';
94
- seq: number;
95
- voice_session_id: string;
96
- kind: 'transcript' | 'started' | 'closed' | 'delegated';
97
- /** A transcript's: the member speaking (user) or the voice answering (assistant). */
98
- role?: 'user' | 'assistant';
99
- text?: string;
100
- /** A delegated task's: answer it with the voice's speak and this id. */
101
- delegation_id?: string;
102
- task?: string;
103
- reason?: string;
104
- seconds?: number;
105
- [field: string]: unknown;
106
- }
107
- export type ChatEvent = MessageEvent | TurnEvent | TextEvent | ResetEvent | ToolEvent | AgentEvent | VoiceEvent;
108
- /** Where a reader is: what it hands the stream to go on from there. */
109
- export interface ChatPosition {
110
- /** The id of the last journal event held. */
111
- after?: number;
112
- /** The response whose answer is being read, and the bytes of it held. */
113
- response?: string;
114
- offset?: number;
115
- }
116
- export interface FollowChatOptions extends ChatPosition {
117
- signal?: AbortSignal;
118
- /** How long a stream that keeps being cut is followed again before the reader is told (default 2 minutes). */
119
- resumeFor?: number;
120
- /** A stream with no byte for this long is taken for cut (default 45 s; the module's heartbeat is more often). */
121
- idleFor?: number;
122
- }
123
- /** The stream's events; its position is where a reader that stops now would go on from. */
124
- export interface ChatStream extends AsyncIterable<ChatEvent> {
125
- readonly position: ChatPosition;
126
- }
127
- /** Opens the stream from a point: the generated client's followChat, through the caller's transport. */
128
- export type OpenChatStream = (query: ChatPosition, signal: AbortSignal) => Promise<globalThis.Response>;
91
+ export type ChatEvent = MessageEvent | TurnEvent | TextEvent | ResetEvent | ToolEvent | AgentEvent;
129
92
  /** A chat refusal of the module's, said in words its caller can act on; anything else (or one said already) as it was. */
130
93
  export declare function chatRefusal(e: unknown): unknown;
131
94
  /** The same promise, its chat refusals said as chatRefusal says them. */
132
95
  export declare function told<T>(p: Promise<T>): Promise<T>;
133
- export declare function followChat(open: OpenChatStream, options?: FollowChatOptions): ChatStream;
96
+ /**
97
+ * One part of a chat's channel (the payload of its "chat" event) as a chat
98
+ * event — undefined for one this runtime does not read: a newer module's
99
+ * part, or one that is not a part at all.
100
+ */
101
+ export declare function chatPart(payload: unknown): ChatEvent | undefined;
@@ -21,10 +21,6 @@ export type AgentDefinition = {
21
21
  * The project's machine that defines the agent at each turn.
22
22
  */
23
23
  backend?: string;
24
- /**
25
- * The app's users start chats with this named agent themselves (when the project lets them talk to its agents): an agent the server alone uses is not one they reach.
26
- */
27
- clients?: boolean;
28
24
  /**
29
25
  * Its instructions.
30
26
  */
@@ -68,7 +64,7 @@ export type Chat = {
68
64
  */
69
65
  created_at: string;
70
66
  /**
71
- * The user who started it; null: the project's server.
67
+ * Null: the project's server started it, as it starts every chat (a user's id: a chat a user started before chats were the server's alone).
72
68
  */
73
69
  created_by: string | null;
74
70
  /**
@@ -153,6 +149,28 @@ export type ChatMessage = {
153
149
  */
154
150
  text: string;
155
151
  };
152
+ /**
153
+ * Where a chat's stream is written.
154
+ */
155
+ export type ChatPipe = {
156
+ /**
157
+ * A channel of the project's realtime (not one of the platform's: roles:, flags:, purchases:, messaging:, debug:, nor phoenix, realtime's heartbeat).
158
+ */
159
+ topic: string;
160
+ };
161
+ /**
162
+ * A chat's pipe.
163
+ */
164
+ export type ChatPipeResult = {
165
+ /**
166
+ * The chat's id.
167
+ */
168
+ chat_id: string;
169
+ /**
170
+ * The channel its stream is written into.
171
+ */
172
+ topic: string;
173
+ };
156
174
  /**
157
175
  * One repository of a chat's workspace.
158
176
  */
@@ -167,11 +185,11 @@ export type ChatRepo = {
167
185
  repo: string;
168
186
  };
169
187
  /**
170
- * A chat to start.
188
+ * A chat to start: the project's server's alone.
171
189
  */
172
190
  export type ChatRequest = {
173
191
  /**
174
- * The agent it talks to: a named agent of the project's — or, from the server with a definition of the chat's own, its name for it.
192
+ * The agent it talks to: a named agent of the project's — or, with a definition of the chat's own, the server's name for it.
175
193
  */
176
194
  agent: string;
177
195
  definition?: AgentDefinition;
@@ -185,10 +203,6 @@ export type ChatRequest = {
185
203
  metadata?: {
186
204
  [key: string]: string;
187
205
  };
188
- /**
189
- * A user who starts a chat is in it as its owner, under this name.
190
- */
191
- name?: string;
192
206
  /**
193
207
  * Its title (256 bytes at most).
194
208
  */
@@ -229,6 +243,10 @@ export type DecisionRequest = {
229
243
  * Yes, or no.
230
244
  */
231
245
  approve: boolean;
246
+ /**
247
+ * The member the project's server acts as: held to that member's rules — their turn (403 not_your_turn) — and the decision is theirs (the server's alone: a user sending it is answered 400; one who is not a member of the chat, 403 not_a_member). Left out, the server acts as itself.
248
+ */
249
+ as?: string;
232
250
  /**
233
251
  * Why (told to the agent; 1024 bytes at most).
234
252
  */
@@ -634,7 +652,7 @@ export type MessagePage = {
634
652
  */
635
653
  export type MessageRequest = {
636
654
  /**
637
- * The member the server writes as (the server's alone).
655
+ * The member the server writes as (the server's alone: a user sending it is answered 400; one who is not a member of the chat, 403 not_a_member): the message is theirs, and counts toward their messages a minute (429 too_many_messages, as theirs would).
638
656
  */
639
657
  as?: string;
640
658
  budget?: TurnBudget;
@@ -928,6 +946,10 @@ export type RunMessageAccepted = {
928
946
  * A message for a subagent at work.
929
947
  */
930
948
  export type RunMessageRequest = {
949
+ /**
950
+ * The member the project's server acts as: held to that member's rules — their messages a minute (429 too_many_messages) — and the message is theirs (the server's alone: a user sending it is answered 400; one who is not a member of the chat, 403 not_a_member). Left out, the server acts as itself.
951
+ */
952
+ as?: string;
931
953
  /**
932
954
  * The writer's id for it.
933
955
  */
@@ -942,7 +964,7 @@ export type RunMessageRequest = {
942
964
  */
943
965
  export type Settings = {
944
966
  /**
945
- * Whether the app's users talk to the project's agents with their own session: start a chat with a named agent, write to the chats they are in.
967
+ * Whether the app's users talk to the project's agents with their own session: read and write to the chats they are in (the project's server starts them).
946
968
  */
947
969
  clients: boolean;
948
970
  /**
@@ -1051,6 +1073,10 @@ export type ToolContent = {
1051
1073
  * What an app's tool returned (1 MiB of text and 4 images at most).
1052
1074
  */
1053
1075
  export type ToolOutput = {
1076
+ /**
1077
+ * The member the project's server acts as: held to that member's rules — their turn (403 not_your_turn) (the server's alone: a user sending it is answered 400; one who is not a member of the chat, 403 not_a_member). Left out, the server acts as itself.
1078
+ */
1079
+ as?: string;
1054
1080
  /**
1055
1081
  * The tool failed; the output says why.
1056
1082
  */
@@ -1322,7 +1348,7 @@ export declare function client(t: Transport): {
1322
1348
  /**
1323
1349
  * Name an agent
1324
1350
  *
1325
- * An agent a chat started from the app — or by name from the server — talks to: its definition, or the backend machine that defines it at each turn.
1351
+ * An agent a chat the server starts by its name talks to: its definition, or the backend machine that defines it at each turn.
1326
1352
  */
1327
1353
  putAgent(input: {
1328
1354
  path: {
@@ -1356,7 +1382,7 @@ export declare function client(t: Transport): {
1356
1382
  /**
1357
1383
  * Start a chat
1358
1384
  *
1359
- * The server starts a chat with a named agent or a definition of its own, and names its members; a signed-in user (when the project lets them) starts one with a named agent, as its owner.
1385
+ * The project's server's alone: its backend, with the secret key, starts a chat with a named agent or a definition of its own, and names its members. A signed-in user is answered 403 server_only, whatever the project lets them (settings.clients is what they do in the chats they are in).
1360
1386
  */
1361
1387
  createChat(input: {
1362
1388
  body: ChatRequest; /** Stops the call: the request in flight, a wait before a retry, a proof-of-work being solved. */
@@ -1387,7 +1413,7 @@ export declare function client(t: Transport): {
1387
1413
  /**
1388
1414
  * Decide a tool call
1389
1415
  *
1390
- * A tool call that waits for a person's yes: approved it is made; denied, the agent is told, with the reason, and goes on.
1416
+ * A tool call that waits for a person's yes: approved it is made; denied, the agent is told, with the reason, and goes on. Decided by the member whose message began the turn (403 not_your_turn for any other) — or by the project's server, for any turn, or as that member (as).
1391
1417
  */
1392
1418
  decideChatApproval(input: {
1393
1419
  path: {
@@ -1440,7 +1466,7 @@ export declare function client(t: Transport): {
1440
1466
  /**
1441
1467
  * Write to a chat
1442
1468
  *
1443
- * A message for the agent: a turn of its own when none runs; while one does, at its next step (next) or cutting the step it is in (now). The same client_id again is the same message. A user sends user_messages_per_minute a minute (429 too_many_messages); past the history the project keeps a message is refused (409 history_full).
1469
+ * A message for the agent: a turn of its own when none runs; while one does, at its next step (next) or cutting the step it is in (now). The same client_id again is the same message. A user sends user_messages_per_minute a minute — and so does the server writing as them (429 too_many_messages); past the history the project keeps a message is refused (409 history_full).
1444
1470
  */
1445
1471
  sendChatMessage(input: {
1446
1472
  path: {
@@ -1449,10 +1475,33 @@ export declare function client(t: Transport): {
1449
1475
  body: MessageRequest; /** Stops the call: the request in flight, a wait before a retry, a proof-of-work being solved. */
1450
1476
  signal?: AbortSignal;
1451
1477
  }): Promise<Reply<200, ChatMessage> | Reply<202, ChatMessage>>;
1478
+ /**
1479
+ * Pipe the chat into a channel
1480
+ *
1481
+ * Writes the chat's stream into a channel of the project's realtime from now on — a gateway's, durable — as the platform: the members' messages, the turns, the answer as it is written (gathered into a few writes a second), the tools and subagents, each the channel's event `chat` {type, id?, data}, under an id of its own so a retry is kept once. The types: `message` (a ChatMessage, as the agent got it), `turn` ({response_id, status: running|waiting|completed|failed|canceled|timed_out|budget_exceeded, user_id, solo, waiting: {reason: approval|tool_output|subagents|message, text_offset, approval_id, tool_call_id, tool_name, input}, error}), `text` ({response_id, offset, text}: the agent's answer as it is written, at its byte offset), `reset` ({response_id, offset}: discard the answer past offset), `tool` ({response_id, run_id, call_id, name, status: running|awaiting_output|awaiting_approval|approved|denied|expired|completed|failed|canceled, text_offset, input, output, image_count, is_error, approval_id, agent_response_id, …}), `agent` ({run_id, response_id, parent_response_id, name, required, status, text_offset}: a subagent at work), `voice` ({voice_session_id, kind: transcript|started|closed|delegated, role: user|assistant, text, delegation_id, task}: the chat's live voice — what was said either way), and `seen` (the journal moved past what was handed already). Every type but text and reset has the journal's sequence as its id; text_offset says where in the answer a tool or subagent part happened. The project's backend is not in the stream: the gateway's members hear the chat from realtime, and one that was away is given what it missed there. Put again with the same channel it goes on where it is — one realtime refused is tried again; with another, from the chat's start, and nothing more is written into the one before. The project's server's alone.
1482
+ */
1483
+ putChatPipe(input: {
1484
+ path: {
1485
+ chat_id: string;
1486
+ };
1487
+ body: ChatPipe; /** Stops the call: the request in flight, a wait before a retry, a proof-of-work being solved. */
1488
+ signal?: AbortSignal;
1489
+ }): Promise<Reply<200, ChatPipeResult>>;
1490
+ /**
1491
+ * Take the chat's pipe away
1492
+ *
1493
+ * The chat's stream is written nowhere from now on. The project's server's alone.
1494
+ */
1495
+ deleteChatPipe(input: {
1496
+ path: {
1497
+ chat_id: string;
1498
+ }; /** Stops the call: the request in flight, a wait before a retry, a proof-of-work being solved. */
1499
+ signal?: AbortSignal;
1500
+ }): Promise<Reply<204, undefined>>;
1452
1501
  /**
1453
1502
  * Write to a subagent at work
1454
1503
  *
1455
- * A message for one of the agent's subagents, at its next step.
1504
+ * A message for one of the agent's subagents, at its next step: a message of the chat like any other — its writer's (or the member's the server writes as) messages a minute (429 too_many_messages), the history the project keeps (409 history_full).
1456
1505
  */
1457
1506
  sendSubagentMessage(input: {
1458
1507
  path: {
@@ -1473,26 +1522,10 @@ export declare function client(t: Transport): {
1473
1522
  }; /** Stops the call: the request in flight, a wait before a retry, a proof-of-work being solved. */
1474
1523
  signal?: AbortSignal;
1475
1524
  }): Promise<Reply<200, StopResult>>;
1476
- /**
1477
- * Follow a chat
1478
- *
1479
- * What happens in the chat as typed parts, as it happens.
1480
- */
1481
- followChat(input: {
1482
- path: {
1483
- chat_id: string;
1484
- };
1485
- query?: {
1486
- after?: number; /** The response whose answer the reader was reading. */
1487
- response?: string; /** The bytes of that answer the reader holds. */
1488
- offset?: number;
1489
- }; /** Stops the call: the request in flight, a wait before a retry, a proof-of-work being solved. */
1490
- signal?: AbortSignal;
1491
- }): Promise<Reply<200, Response>>;
1492
1525
  /**
1493
1526
  * Answer an app's tool call
1494
1527
  *
1495
- * The answer of a client tool's call, made by the app's code: the turn waiting for it goes on. The first answer is the one.
1528
+ * The answer of a client tool's call, made by the app's code: the turn waiting for it goes on. The first answer is the one. Given by the app of the member whose message began the turn (403 not_your_turn for any other) — or by the project's server, for any turn, or as that member (as).
1496
1529
  */
1497
1530
  answerChatToolCall(input: {
1498
1531
  path: {
@@ -56,6 +56,14 @@ export type ChannelRule = {
56
56
  * A public channel's subscribers may write its state.
57
57
  */
58
58
  state_write?: boolean;
59
+ /**
60
+ * The channel keeps its broadcasts, numbered, and a member that was away is given what it missed, in order and once, when it rejoins after the number it had (phx_join config.log.after; the join's reply says the number it starts after, log_seq). A broadcast's number (seq) is its channel's own — the time it was written, in microseconds, rising in the channel — and says nothing of other channels. Kept a day unless log_keep_seconds says how long. What a project's durable channels write is held to a budget an hour, each message weighed by the hours it is kept: past it a durable broadcast is refused (log_budget) until the next hour.
61
+ */
62
+ durable?: boolean;
63
+ /**
64
+ * How long a durable channel keeps its broadcasts, in seconds: 1 to 604800 (a week). Naming it makes the channel durable.
65
+ */
66
+ log_keep_seconds?: number;
59
67
  };
60
68
  export type ChannelSubscribers = {
61
69
  /**
@@ -255,7 +263,7 @@ export declare function client(t: Transport): {
255
263
  /**
256
264
  * Broadcast as the server
257
265
  *
258
- * Publishes events on the project's channels, to every subscriber on every replica, as the project's server (1 to 100 events). Delivered at most once, like every broadcast.
266
+ * Publishes events on the project's channels, to every subscriber on every replica, as the project's server (1 to 100 events). Delivered at most once, like every broadcast — but on a durable channel, kept and given to its members in order and once. A durable channel's event past the project's hourly budget is refused with 429 log_budget and when to try again (Retry-After); the events before it were published.
259
267
  */
260
268
  broadcast(input: {
261
269
  body: BroadcastBatch; /** Stops the call: the request in flight, a wait before a retry, a proof-of-work being solved. */
@@ -1,6 +1,6 @@
1
1
  import type { Transport } from '../../internal/src/transport.js';
2
2
  import { BackendError } from './backend-error.js';
3
- import { type RoomDescriptor, type RoomEvent, type RoomHandle } from './rooms.js';
3
+ import { type GatewayDescriptor, type GatewayEvent, type JoinedGateway, type JoinOptions } from './gateways.js';
4
4
  import type { RealtimeSocket } from './socket.js';
5
5
  export interface CallOptions {
6
6
  headers?: Record<string, string>;
@@ -59,22 +59,29 @@ export interface BackendBase {
59
59
  */
60
60
  call<O = unknown>(name: string, input?: unknown, options?: CallOptions): Promise<O>;
61
61
  /**
62
- * Joins a room the backend declared with `@Room` — `room` is the generated
63
- * module's, `params` its pattern's `{param}` values: resolves once the
64
- * backend let the user in, with the handle its events arrive on and its
65
- * messages go through; rejects with a RoomError (its reason) when it did not.
62
+ * Joins a gateway the backend declared with `@Gateway` — `gateway` is the
63
+ * generated module's, `params` its pattern's `{param}` values: resolves once
64
+ * the backend let the user in, with the handle its events arrive on and its
65
+ * messages go through (each answered with its ack); rejects with a
66
+ * GatewayError (its reason) when it did not. A durable gateway's handle is
67
+ * sent what it keeps first, unless `{ history: false }`; a chat gateway's
68
+ * holds the chat its server piped there (`handle.chat`).
66
69
  *
67
- * const room = await pb.api.join(AiSessionIdRoom, { sessionId });
68
- * room.on('token', (t) => transcript.push(t.text));
69
- * await room.send('say', { text: 'hello' });
70
- * room.leave();
70
+ * const chat = await pb.api.join(ChatGateway, { chatId });
71
+ * chat.chat.subscribe((s) => render(s.items));
72
+ * const { id } = await chat.send('say', { text: 'hello' });
73
+ * chat.leave();
71
74
  */
72
- join<E extends RoomEvent, S, P extends readonly string[]>(room: RoomDescriptor<E, S, P>, params: Record<P[number], string>): Promise<RoomHandle<E, S>>;
75
+ join<E extends GatewayEvent, S, R, P extends readonly string[], C extends boolean = false>(gateway: GatewayDescriptor<E, S, R, P, C>, params: Record<P[number], string>, options?: JoinOptions): Promise<JoinedGateway<E, S, R, C>>;
73
76
  }
74
77
  /** What every client createClient makes inherits: one getter per registered backend name. */
75
78
  export declare const clientBase: object;
76
- /** Gives a client made by createClient its project's origin, its way to the machines, its project's transport (an upload's way, through storage) and its socket (a room's). */
77
- export declare function bindMachines(client: object, project: string, machine: (url: string) => Transport, storage: Transport, socket: RealtimeSocket): void;
79
+ /**
80
+ * Gives a client made by createClient its project's origin, its way to the
81
+ * machines, its project's transport (an upload's way, through storage), its
82
+ * socket (a gateway's) and its signed-in user (a chat gateway's own turns).
83
+ */
84
+ export declare function bindMachines(client: object, project: string, machine: (url: string) => Transport, storage: Transport, socket: RealtimeSocket, me?: () => string | undefined): void;
78
85
  /**
79
86
  * Registers a backend under `client` for the clients of `project` (the
80
87
  * address createClient is given) — the machine `machine`, at `url` — called
@@ -0,0 +1,118 @@
1
+ import { type AgentEvent, type ChatEvent, type ToolEvent, type TurnEvent } from '../../internal/src/chat.js';
2
+ export type { AgentEvent, ChatEvent, ToolEvent, TurnEvent } from '../../internal/src/chat.js';
3
+ /** A member's (or the server's) message, as the agent got it. */
4
+ export interface ChatMessageView {
5
+ /** The writer's id for it: the same id is the same message. */
6
+ clientId: string;
7
+ id: string | null;
8
+ text: string;
9
+ /** The member who wrote it; null: the project's server. */
10
+ author: string | null;
11
+ authorName: string;
12
+ delivery: string;
13
+ /** pending until the agent has it, delivered once it does, failed when it could not be given. */
14
+ status: 'pending' | 'delivered' | 'failed';
15
+ /** Why it failed: the module's code. */
16
+ error: string | null;
17
+ /** The turn it started or reached. */
18
+ responseId: string | null;
19
+ createdAt: string;
20
+ /** The signed-in user wrote it. */
21
+ mine: boolean;
22
+ }
23
+ /** A piece of an answer, in order: its text, split where a tool was called or a subagent handed work. */
24
+ export type ChatPart = {
25
+ type: 'text';
26
+ text: string;
27
+ } | {
28
+ type: 'tool';
29
+ tool: ToolEvent;
30
+ } | {
31
+ type: 'agent';
32
+ agent: AgentEvent;
33
+ };
34
+ /** One answer of the agent's (or of one of its subagents). */
35
+ export interface ChatResponse {
36
+ id: string;
37
+ status: TurnEvent['status'] | null;
38
+ waiting: TurnEvent['waiting'];
39
+ error: TurnEvent['error'];
40
+ /** The whole answer so far. */
41
+ text: string;
42
+ parts: ChatPart[];
43
+ /** A subagent's: its run, its name, and the response that handed it the work. */
44
+ runId: string | null;
45
+ agent: string | null;
46
+ parentId: string | null;
47
+ /** The member whose turn it is (a subagent's: its turn's); null: the project's server's, or the engine's. */
48
+ startedBy: string | null;
49
+ /** The signed-in user's own turn. */
50
+ mine: boolean;
51
+ }
52
+ export type ChatItem = {
53
+ type: 'message';
54
+ message: ChatMessageView;
55
+ } | {
56
+ type: 'response';
57
+ response: ChatResponse;
58
+ };
59
+ export interface ChatState {
60
+ /** The conversation in order: the members' messages and the agent's answers (a subagent's is in its parent's parts). */
61
+ items: ChatItem[];
62
+ messages: ChatMessageView[];
63
+ /** Every answer, the subagents' too, as they began. */
64
+ responses: ChatResponse[];
65
+ /** The last turn's state. */
66
+ turn: TurnEvent | null;
67
+ /**
68
+ * What waits for the signed-in user: the tool calls of their own turns
69
+ * waiting for their yes, and the client tool calls of their own turns
70
+ * waiting for their app's answer — each said back through the gateway's
71
+ * own messages, its backend deciding. Another member's turn is theirs.
72
+ */
73
+ waiting: {
74
+ approvals: ToolEvent[];
75
+ outputs: ToolEvent[];
76
+ };
77
+ }
78
+ /** A gateway's chat: its state now, and told each time it changes. */
79
+ export interface GatewayChat extends AsyncIterable<ChatState> {
80
+ /** The chat as the channel holds it now: the same object until it changes. */
81
+ readonly state: ChatState;
82
+ /** Told each time the state changes (at most once a microtask); returns the unsubscribe. */
83
+ subscribe(listener: (state: ChatState) => void): () => void;
84
+ }
85
+ /**
86
+ * A chat folded from its channel's parts. It holds no connection: a part is
87
+ * handed to take() as the channel gives it — once, in order — and the state
88
+ * is built from them; me is the signed-in user, read as the state is built.
89
+ */
90
+ export declare class ChatLog implements GatewayChat {
91
+ private readonly me;
92
+ private readonly messages;
93
+ private readonly responses;
94
+ private readonly order;
95
+ private turn;
96
+ private readonly calls;
97
+ private readonly listeners;
98
+ /** The iterations waiting for a change: woken when the gateway is left. */
99
+ private readonly enders;
100
+ private snapshot;
101
+ private told;
102
+ private ended;
103
+ constructor(me?: () => string | undefined);
104
+ get state(): ChatState;
105
+ subscribe(listener: (state: ChatState) => void): () => void;
106
+ /** The state now, then each time it changes, until the gateway is left. */
107
+ [Symbol.asyncIterator](): AsyncGenerator<ChatState, void, undefined>;
108
+ /** The gateway was left: an iteration ends, and no part is taken any more. */
109
+ end(): void;
110
+ /** One payload of the channel's "chat" event; one that is no part this runtime reads is let be. */
111
+ takePayload(payload: unknown): void;
112
+ take(e: ChatEvent): void;
113
+ private heard;
114
+ private place;
115
+ private response;
116
+ private changed;
117
+ private build;
118
+ }
@@ -5,7 +5,6 @@ import { type RefreshMode, type SessionStore } from './session.js';
5
5
  import { RealtimeSocket } from './socket.js';
6
6
  import { Analytics, type AnalyticsOptions } from './analytics.js';
7
7
  import { Purchases } from './purchases.js';
8
- import { AI } from './ai.js';
9
8
  export interface ClientOptions {
10
9
  /** The project's address: https://<ref>.<domain> */
11
10
  url: string;
@@ -63,7 +62,5 @@ export interface PalbaseClient {
63
62
  analytics: Analytics;
64
63
  /** What the signed-in customer has (live, onCustomerInfo), the paywall's catalog, and buying on the web. */
65
64
  purchases: Purchases;
66
- /** The project's agents, for the signed-in user (when the project lets its users talk to them): their chats, followed as typed parts. */
67
- ai: AI;
68
65
  }
69
66
  export declare function createClient(opts: ClientOptions): PalbaseClient;
@@ -0,0 +1,89 @@
1
+ import { type GatewayChat } from './chat.js';
2
+ import type { RealtimeSocket } from './socket.js';
3
+ /** One event of a gateway, as a generated module's union has it. */
4
+ export type GatewayEvent = {
5
+ event: string;
6
+ payload: unknown;
7
+ };
8
+ /**
9
+ * A gateway, as the generator writes one. `send`, `reply` and `event` carry
10
+ * types alone — their values are never read: `gateway.send('say', { … })` is
11
+ * checked against the message's shape and resolves with its ack's, and a
12
+ * handler's event against the gateway's events.
13
+ */
14
+ export interface GatewayDescriptor<E extends GatewayEvent, S, R = Record<keyof S, unknown>, P extends readonly string[] = readonly string[], C extends boolean = false> {
15
+ /** The backend's class: `ChatGateway`. */
16
+ name?: string;
17
+ /** The channel pattern the backend declared: `chat:{chatId}`. */
18
+ pattern: string;
19
+ /** It keeps what it sends: a join is sent what it keeps, and a dropped socket misses nothing. */
20
+ durable?: boolean;
21
+ /** Its server pipes a chat into it: the handle holds the chat's state (`handle.chat`). */
22
+ chat?: C;
23
+ /** The `{param}` names, in the pattern's order: a join takes exactly these. */
24
+ params: P;
25
+ /** The declared event names: what a handle listens to. */
26
+ events: readonly string[];
27
+ /** The declared message names: what a handle sends. */
28
+ messages: readonly string[];
29
+ send: S;
30
+ reply?: R;
31
+ event: E;
32
+ }
33
+ /** How a gateway is joined. */
34
+ export interface JoinOptions {
35
+ /** A durable gateway's: sent what it keeps first (true, the default), or only what comes after the join (false). */
36
+ history?: boolean;
37
+ }
38
+ /** What `on` returns: called, it stops that callback (`off` does the same). */
39
+ export type GatewaySubscription = () => void;
40
+ /** A live gateway. Hold it for as long as the view is on screen, and `leave` it when the view goes: an occupied gateway is one its backend is still running for. */
41
+ export interface GatewayHandle<E extends GatewayEvent, S, R = Record<keyof S, unknown>> {
42
+ /** The channel this handle is on: `chat:42`. */
43
+ readonly topic: string;
44
+ /** A durable gateway's number of the last event the handle was sent: what a dropped socket joins again after. */
45
+ readonly seq: number | undefined;
46
+ /** Every event the gateway declares, as a union to switch over. */
47
+ on(handler: (event: E) => void): GatewaySubscription;
48
+ /** One event by its name, its payload typed. */
49
+ on<K extends E['event']>(event: K, handler: (payload: Extract<E, {
50
+ event: K;
51
+ }>['payload']) => void): GatewaySubscription;
52
+ /** Stops one callback. */
53
+ off(subscription: GatewaySubscription): void;
54
+ /**
55
+ * Sends one declared message and waits for the gateway's backend to take
56
+ * it: resolves with its ack (what its @Subscribe returned), rejects with a
57
+ * GatewayError — its reason — when it did not.
58
+ */
59
+ send<K extends keyof S & string>(message: K, payload: S[K]): Promise<K extends keyof R ? R[K] : unknown>;
60
+ /** Drops every callback of this handle and lets go of the gateway; the channel is left with its last handle. */
61
+ leave(): void;
62
+ }
63
+ /** A chat gateway's handle: the chat its server piped into the channel, as its state. */
64
+ export interface ChatGatewayHandle<E extends GatewayEvent, S, R = Record<keyof S, unknown>> extends GatewayHandle<E, S, R> {
65
+ /** The chat from its start (the channel keeps it), and each change as the platform writes it. */
66
+ readonly chat: GatewayChat;
67
+ }
68
+ /** What a join resolves with: a chat gateway's handle holds its chat. */
69
+ export type JoinedGateway<E extends GatewayEvent, S, R, C> = [C] extends [true] ? ChatGatewayHandle<E, S, R> : GatewayHandle<E, S, R>;
70
+ /**
71
+ * Why a gateway did not do what was asked. `reason` is the server's: for a join,
72
+ * `unauthorized` (the backend did not let the user in) or `unavailable` (it
73
+ * could not be asked: try again); for a message, the reason the backend gave,
74
+ * `forbidden` when it gave none or the user may only listen, `rate_limited`,
75
+ * `unavailable`. When no answer came it is `no reply` or `socket closed` —
76
+ * the message may have been taken — and `not joined` when nothing was sent:
77
+ * the handle left, its socket closed (a sign-out, another user's sign-in), or
78
+ * the socket dropped and is being opened again.
79
+ */
80
+ export declare class GatewayError extends Error {
81
+ readonly reason: string;
82
+ constructor(message: string, reason: string);
83
+ }
84
+ /**
85
+ * Joins `gateway` on `socket`: resolves once the backend let the user in,
86
+ * rejects with a GatewayError when it did not. me is the signed-in user: a
87
+ * chat's turns and messages that are theirs.
88
+ */
89
+ export declare function joinGateway<E extends GatewayEvent, S, R, P extends readonly string[], C extends boolean = false>(socket: RealtimeSocket, gateway: GatewayDescriptor<E, S, R, P, C>, params: Record<P[number], string>, options?: JoinOptions, me?: () => string | undefined): Promise<JoinedGateway<E, S, R, C>>;
@@ -1,9 +1,9 @@
1
1
  export { createClient, type ClientOptions, type PalbaseClient } from './client.js';
2
2
  export type { Auth, CredentialsLike, SignInResult, User } from './auth.js';
3
3
  export { localStore, memoryStore, SessionChangedError, type AuthEvent, type RefreshMode, type Session, type SessionStore } from './session.js';
4
- export { Channel, RealtimeSocket, type PushReply } from './socket.js';
4
+ export { Channel, RealtimeSocket, type ChannelOptions, type PushReply } from './socket.js';
5
5
  export { Analytics, type AnalyticsOptions } from './analytics.js';
6
- export { AI, ChatSession, type AgentEvent, type Chat, type ChatEvent, type ChatItem, type ChatMessageView, type ChatOptions, type ChatPart, type ChatResponse, type ChatState, type ChatVoice, type ClientToolHandlers, type ToolEvent, type ToolResult, type TurnEvent, type VoiceEvent, } from './ai.js';
6
+ export type { AgentEvent, ChatEvent, ChatItem, ChatMessageView, ChatPart, ChatResponse, ChatState, GatewayChat, ToolEvent, TurnEvent } from './chat.js';
7
7
  export { Purchases, type CustomerInfo, type CustomerInfoListener, type MergeGrant, type Offerings } from './purchases.js';
8
8
  export type { Paywall, PaywallRenderOptions } from './paywall/placement.js';
9
9
  export type { PaywallAttribute } from './paywall/attributes.js';
@@ -11,5 +11,5 @@ export type { PaywallPackage, PaywallView } from './paywall/render.js';
11
11
  export { PalbaseError } from '../../internal/src/transport.js';
12
12
  export { BackendError, isBackendError, type BackendErrorKind, type FieldError } from './backend-error.js';
13
13
  export type { BackendBase, CallOptions, UploadOptions, UploadProgress } from './backends.js';
14
- export { RoomError, type RoomDescriptor, type RoomEvent, type RoomHandle, type RoomSubscription } from './rooms.js';
14
+ export { GatewayError, type ChatGatewayHandle, type GatewayDescriptor, type GatewayEvent, type GatewayHandle, type GatewaySubscription, type JoinedGateway, type JoinOptions } from './gateways.js';
15
15
  export type { RetryPolicy } from '../../internal/src/retry.js';