@possibl/rcrt-sdk 0.1.2 → 0.1.3

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 (87) hide show
  1. package/CHANGELOG.md +23 -73
  2. package/LICENSE +21 -0
  3. package/README.md +3 -20
  4. package/dist/auth.d.ts +45 -0
  5. package/dist/auth.d.ts.map +1 -0
  6. package/{src/auth.ts → dist/auth.js} +9 -24
  7. package/dist/auth.js.map +1 -0
  8. package/dist/authn.d.ts +84 -0
  9. package/dist/authn.d.ts.map +1 -0
  10. package/dist/authn.js +75 -0
  11. package/dist/authn.js.map +1 -0
  12. package/dist/breadcrumbs.d.ts +32 -0
  13. package/dist/breadcrumbs.d.ts.map +1 -0
  14. package/dist/breadcrumbs.js +96 -0
  15. package/dist/breadcrumbs.js.map +1 -0
  16. package/dist/cards.d.ts +28 -0
  17. package/dist/cards.d.ts.map +1 -0
  18. package/dist/cards.js +106 -0
  19. package/dist/cards.js.map +1 -0
  20. package/dist/chat.d.ts +50 -0
  21. package/dist/chat.d.ts.map +1 -0
  22. package/dist/chat.js +58 -0
  23. package/dist/chat.js.map +1 -0
  24. package/dist/client.d.ts +45 -0
  25. package/dist/client.d.ts.map +1 -0
  26. package/dist/client.js +69 -0
  27. package/dist/client.js.map +1 -0
  28. package/dist/errors.d.ts +32 -0
  29. package/dist/errors.d.ts.map +1 -0
  30. package/dist/errors.js +76 -0
  31. package/dist/errors.js.map +1 -0
  32. package/dist/generated/conformance.d.ts +48 -0
  33. package/dist/generated/conformance.d.ts.map +1 -0
  34. package/dist/generated/conformance.js +24 -0
  35. package/dist/generated/conformance.js.map +1 -0
  36. package/dist/generated/index.d.ts +34 -0
  37. package/dist/generated/index.d.ts.map +1 -0
  38. package/dist/generated/index.js +34 -0
  39. package/dist/generated/index.js.map +1 -0
  40. package/dist/generated/openapi.d.ts +3900 -0
  41. package/dist/generated/openapi.d.ts.map +1 -0
  42. package/dist/generated/openapi.js +6 -0
  43. package/dist/generated/openapi.js.map +1 -0
  44. package/dist/grants.d.ts +41 -0
  45. package/dist/grants.d.ts.map +1 -0
  46. package/dist/grants.js +50 -0
  47. package/dist/grants.js.map +1 -0
  48. package/dist/index.d.ts +23 -0
  49. package/dist/index.d.ts.map +1 -0
  50. package/dist/index.js +18 -0
  51. package/dist/index.js.map +1 -0
  52. package/dist/internal/fetch.d.ts +41 -0
  53. package/dist/internal/fetch.d.ts.map +1 -0
  54. package/dist/internal/fetch.js +106 -0
  55. package/dist/internal/fetch.js.map +1 -0
  56. package/dist/internal/sse.d.ts +82 -0
  57. package/dist/internal/sse.d.ts.map +1 -0
  58. package/dist/internal/sse.js +161 -0
  59. package/dist/internal/sse.js.map +1 -0
  60. package/dist/types/breadcrumb.d.ts +70 -0
  61. package/dist/types/breadcrumb.d.ts.map +1 -0
  62. package/dist/types/breadcrumb.js +8 -0
  63. package/dist/types/breadcrumb.js.map +1 -0
  64. package/dist/types/card.d.ts +251 -0
  65. package/dist/types/card.d.ts.map +1 -0
  66. package/dist/types/card.js +10 -0
  67. package/dist/types/card.js.map +1 -0
  68. package/dist/types/index.d.ts +3 -0
  69. package/dist/types/index.d.ts.map +1 -0
  70. package/{src/types/index.ts → dist/types/index.js} +1 -0
  71. package/dist/types/index.js.map +1 -0
  72. package/package.json +35 -6
  73. package/src/authn.ts +0 -159
  74. package/src/breadcrumbs.ts +0 -111
  75. package/src/capabilities.ts +0 -93
  76. package/src/cards.ts +0 -109
  77. package/src/chat.ts +0 -83
  78. package/src/client.ts +0 -97
  79. package/src/errors.ts +0 -101
  80. package/src/files.ts +0 -135
  81. package/src/grants.ts +0 -99
  82. package/src/index.ts +0 -103
  83. package/src/internal/fetch.ts +0 -133
  84. package/src/internal/sse.ts +0 -236
  85. package/src/sessions.ts +0 -110
  86. package/src/types/breadcrumb.ts +0 -77
  87. package/src/types/card.ts +0 -298
@@ -1,236 +0,0 @@
1
- /**
2
- * SSE client with automatic reconnection + typed event dispatch.
3
- *
4
- * Works in three environments:
5
- * - Browsers: uses the built-in `EventSource`. Auth goes via query
6
- * params because `EventSource` can't set custom headers.
7
- * - Node 18+: uses the built-in `EventSource` (Node 22+) or a
8
- * pluggable polyfill (see `config.eventSource`).
9
- * - React Native: install `react-native-sse` and pass its
10
- * constructor as `config.eventSource`. Custom headers work there.
11
- *
12
- * Subscribers get typed events. Streams auto-reconnect with
13
- * exponential backoff unless `close()` was called explicitly.
14
- */
15
-
16
- import type { Breadcrumb } from '../types/breadcrumb.js';
17
-
18
- export interface SseConnectConfig {
19
- apiUrl: string;
20
- path: string;
21
- tenantId: string | null;
22
- getToken: () => Promise<string>;
23
- /** Override the EventSource implementation. Required in React Native. */
24
- eventSource?: EventSourceConstructor;
25
- /** Max reconnect delay in ms. Default 30000. */
26
- maxBackoffMs?: number;
27
- /** Extra query params (e.g. `?last_event_id=...`). */
28
- query?: Record<string, string>;
29
- /** Disable the query-param token fallback; requires a custom EventSource impl. */
30
- useHeaderAuth?: boolean;
31
- }
32
-
33
- export interface SseDelta {
34
- delta: string;
35
- agent_id: string;
36
- is_final: boolean;
37
- }
38
-
39
- export interface SseThought {
40
- steps: string[];
41
- agent_id?: string;
42
- }
43
-
44
- export interface SseAction {
45
- tool_name: string;
46
- status: 'running' | 'completed' | 'failed';
47
- request_id?: string;
48
- }
49
-
50
- export interface SseError {
51
- code?: string;
52
- message?: string;
53
- fatal?: boolean;
54
- }
55
-
56
- export interface SseHandlers {
57
- onConnected?: (payload: { session_id?: string; user_id?: string }) => void;
58
- onDelta?: (data: SseDelta) => void;
59
- onMessage?: (data: Breadcrumb) => void;
60
- onThought?: (data: SseThought) => void;
61
- onAction?: (data: SseAction) => void;
62
- onError?: (data: SseError, isFatal: boolean) => void;
63
- /** Fires any time the underlying SSE connection opens. */
64
- onOpen?: () => void;
65
- /** Fires any time the underlying SSE connection closes (before a reconnect attempt). */
66
- onClose?: () => void;
67
- }
68
-
69
- type EventSourceConstructor = new (url: string, init?: EventSourceInit & { headers?: Record<string, string> }) => EventSourceLike;
70
-
71
- interface EventSourceLike {
72
- readonly readyState: 0 | 1 | 2;
73
- close(): void;
74
- addEventListener(type: string, listener: (ev: MessageEvent | Event) => void): void;
75
- onerror: ((ev: Event) => void) | null;
76
- onopen: ((ev: Event) => void) | null;
77
- }
78
-
79
- export interface SseConnection {
80
- /** Close the stream. No more reconnects. */
81
- close(): void;
82
- /** Current state — for UI reconnection indicators. */
83
- readonly state: 'connecting' | 'open' | 'closed';
84
- }
85
-
86
- export function connect(config: SseConnectConfig, handlers: SseHandlers): SseConnection {
87
- const maxBackoff = config.maxBackoffMs ?? 30_000;
88
- let backoff = 1_000;
89
- let closed = false;
90
- let es: EventSourceLike | null = null;
91
- let state: 'connecting' | 'open' | 'closed' = 'connecting';
92
- let reconnectTimer: ReturnType<typeof setTimeout> | null = null;
93
-
94
- async function open(): Promise<void> {
95
- if (closed) return;
96
-
97
- const token = await config.getToken();
98
- const u = new URL(config.apiUrl.replace(/\/$/, '') + config.path);
99
- if (config.query) {
100
- for (const [k, v] of Object.entries(config.query)) {
101
- u.searchParams.set(k, v);
102
- }
103
- }
104
-
105
- const EventSourceCtor: EventSourceConstructor = (config.eventSource
106
- ?? (globalThis as unknown as { EventSource?: EventSourceConstructor }).EventSource) as EventSourceConstructor;
107
- if (!EventSourceCtor) {
108
- handlers.onError?.({ message: 'No EventSource implementation available. Pass config.eventSource for React Native.', fatal: true }, true);
109
- closed = true;
110
- state = 'closed';
111
- return;
112
- }
113
-
114
- // Prefer header auth when the EventSource impl supports it (React
115
- // Native with `react-native-sse`, Node 22+, any custom
116
- // `config.eventSource` that accepts a `headers` init). The browser's
117
- // built-in EventSource constructor has NO support for custom
118
- // headers — there's no proposal in the WHATWG spec to change that —
119
- // so when we're stuck with it we fall back to passing the bearer
120
- // token and tenant ID as query params.
121
- //
122
- // Security note (Sonar hotspot review): tokens in URLs are a
123
- // documented smell — they can leak via proxy logs, HTTP Referer
124
- // headers, and browser history. We mitigate:
125
- // * TLS is assumed (RCRT's api-gateway rejects plaintext HTTP).
126
- // * Firebase ID tokens are short-lived (60 min) and auto-rotated.
127
- // A leaked token in a server log expires fast; it can't be used
128
- // to escalate outside the scope Firebase already granted.
129
- // * `tk_*` workspace API keys are long-lived and should NEVER be
130
- // passed through this path. Callers holding a tk_ key are
131
- // server-side and must set `useHeaderAuth: true`. The SDK docs
132
- // and the TokenProvider interface make this explicit.
133
- // * This fallback only triggers for SSE subscriptions. The regular
134
- // fetch-based surface (`src/internal/fetch.ts`) always uses the
135
- // Authorization header.
136
- // Without this fallback the SDK cannot open SSE connections from a
137
- // browser at all, which would break every chat UI built on RCRT.
138
- const init: EventSourceInit & { headers?: Record<string, string> } = {};
139
- if (config.useHeaderAuth) {
140
- init.headers = {
141
- Authorization: `Bearer ${token}`,
142
- ...(config.tenantId ? { 'X-Tenant-ID': config.tenantId } : {}),
143
- };
144
- } else {
145
- u.searchParams.set('token', token); // NOSONAR - required for browser EventSource; see block comment above
146
- if (config.tenantId) u.searchParams.set('tenant_id', config.tenantId);
147
- }
148
-
149
- try {
150
- es = new EventSourceCtor(u.toString(), init);
151
- } catch (err) {
152
- handlers.onError?.({ message: (err as Error).message, fatal: true }, true);
153
- scheduleReconnect();
154
- return;
155
- }
156
-
157
- es.onopen = () => {
158
- backoff = 1_000;
159
- state = 'open';
160
- handlers.onOpen?.();
161
- };
162
-
163
- es.addEventListener('connected', (e) => {
164
- const data = tryJson((e as MessageEvent).data);
165
- handlers.onConnected?.(data as { session_id?: string; user_id?: string });
166
- });
167
-
168
- es.addEventListener('delta', (e) => {
169
- const data = tryJson<SseDelta>((e as MessageEvent).data);
170
- if (data) handlers.onDelta?.(data);
171
- });
172
-
173
- es.addEventListener('message', (e) => {
174
- const data = tryJson<Breadcrumb>((e as MessageEvent).data);
175
- if (data) handlers.onMessage?.(data);
176
- });
177
-
178
- es.addEventListener('thought', (e) => {
179
- const data = tryJson<SseThought>((e as MessageEvent).data);
180
- if (data) handlers.onThought?.(data);
181
- });
182
-
183
- es.addEventListener('action', (e) => {
184
- const data = tryJson<SseAction>((e as MessageEvent).data);
185
- if (data) handlers.onAction?.(data);
186
- });
187
-
188
- es.addEventListener('error', (e) => {
189
- const data = tryJson<SseError>((e as MessageEvent | undefined)?.data ?? '');
190
- handlers.onError?.(data ?? { message: 'stream error' }, false);
191
- });
192
-
193
- es.onerror = () => {
194
- state = 'closed';
195
- handlers.onClose?.();
196
- es?.close();
197
- es = null;
198
- scheduleReconnect();
199
- };
200
- }
201
-
202
- function scheduleReconnect() {
203
- if (closed) return;
204
- state = 'connecting';
205
- const delay = backoff + Math.floor(Math.random() * 500);
206
- backoff = Math.min(maxBackoff, backoff * 2);
207
- reconnectTimer = setTimeout(() => {
208
- reconnectTimer = null;
209
- void open();
210
- }, delay);
211
- }
212
-
213
- void open();
214
-
215
- return {
216
- close() {
217
- closed = true;
218
- state = 'closed';
219
- if (reconnectTimer) clearTimeout(reconnectTimer);
220
- es?.close();
221
- es = null;
222
- },
223
- get state() {
224
- return state;
225
- },
226
- };
227
- }
228
-
229
- function tryJson<T = unknown>(raw: unknown): T | undefined {
230
- if (typeof raw !== 'string' || !raw) return undefined;
231
- try {
232
- return JSON.parse(raw) as T;
233
- } catch {
234
- return undefined;
235
- }
236
- }
package/src/sessions.ts DELETED
@@ -1,110 +0,0 @@
1
- /**
2
- * Sessions module — multi-agent / multi-participant session metadata.
3
- *
4
- * The `chat.send()` + `chat.sessionStream()` flow gives you the
5
- * per-turn read/write loop. This module is for everything around the
6
- * session: who's in it (participants — agents AND users), and the
7
- * cross-session graph view (constellation).
8
- *
9
- * See `packages/docs/guides/03-chat-and-sse.md` for how sessions
10
- * relate to breadcrumbs.
11
- */
12
-
13
- import type { FetchContext } from './internal/fetch.js';
14
- import { request } from './internal/fetch.js';
15
- import { ApiError } from './errors.js';
16
-
17
- export interface SessionParticipant {
18
- /** Either `agent` (id is an agent name like `life-coordinator`) or `user` (id is a UUID). */
19
- type: 'agent' | 'user';
20
- id: string;
21
- /** Human-friendly label. May be undefined for never-renamed participants. */
22
- name?: string;
23
- invited_by?: string;
24
- joined_at?: string;
25
- }
26
-
27
- export interface ConstellationNode {
28
- session_id: string;
29
- title?: string;
30
- topics?: string[];
31
- message_count?: number;
32
- last_active?: string;
33
- primary_agent?: string;
34
- participant_ids?: string[];
35
- }
36
-
37
- export interface ConstellationEdge {
38
- /** Two session ids the constellation maps as related. */
39
- a: string;
40
- b: string;
41
- /** Reason for the relationship — e.g. `shared_topic:health`, `shared_participant:life-coordinator`. */
42
- reason: string;
43
- }
44
-
45
- export interface ConstellationData {
46
- nodes: ConstellationNode[];
47
- edges: ConstellationEdge[];
48
- }
49
-
50
- export class SessionsModule {
51
- constructor(private readonly ctx: FetchContext) {}
52
-
53
- /**
54
- * `GET /v1/sessions/constellation` — graph view across all the
55
- * caller's sessions, grouped by topic / shared participant.
56
- *
57
- * Returns `null` when the constellation endpoint is unavailable
58
- * or the user has zero sessions to graph.
59
- */
60
- async getConstellation(limit: number = 50): Promise<ConstellationData | null> {
61
- try {
62
- return await request<ConstellationData>(this.ctx, '/v1/sessions/constellation', {
63
- query: { limit },
64
- });
65
- } catch (err) {
66
- if (err instanceof ApiError && (err.status === 404 || err.status === 204)) {
67
- return null;
68
- }
69
- throw err;
70
- }
71
- }
72
-
73
- /** `GET /v1/sessions/{id}/participants` — agents + users currently in the session. */
74
- async listParticipants(sessionId: string): Promise<SessionParticipant[]> {
75
- const res = await request<{ participants: SessionParticipant[] } | SessionParticipant[]>(
76
- this.ctx,
77
- `/v1/sessions/${sessionId}/participants`,
78
- );
79
- return Array.isArray(res) ? res : (res.participants ?? []);
80
- }
81
-
82
- /**
83
- * `POST /v1/sessions/{id}/participants` — invite an agent into the
84
- * session. The agent will see subsequent turns and may respond.
85
- */
86
- async addParticipant(
87
- sessionId: string,
88
- agentId: string,
89
- options: { invitedBy?: string } = {},
90
- ): Promise<void> {
91
- await request<void>(this.ctx, `/v1/sessions/${sessionId}/participants`, {
92
- method: 'POST',
93
- body: { agent_id: agentId, invited_by: options.invitedBy },
94
- });
95
- }
96
-
97
- /** `DELETE /v1/sessions/{id}/participants/{agent_id}` — drop an agent from the session. */
98
- async removeParticipant(sessionId: string, agentId: string): Promise<void> {
99
- try {
100
- await request<void>(
101
- this.ctx,
102
- `/v1/sessions/${sessionId}/participants/${agentId}`,
103
- { method: 'DELETE' },
104
- );
105
- } catch (err) {
106
- if (err instanceof ApiError && err.status === 404) return; // idempotent
107
- throw err;
108
- }
109
- }
110
- }
@@ -1,77 +0,0 @@
1
- /**
2
- * Breadcrumb — the fundamental record of RCRT.
3
- *
4
- * Every meaningful state change in the system is a breadcrumb. See
5
- * `packages/docs/concepts/02-primitives.md` for the mental model.
6
- */
7
-
8
- export type ActorType = 'user' | 'agent' | 'tool' | 'system';
9
-
10
- export interface Actor {
11
- type: ActorType;
12
- /** For users: the user UUID. For agents: the agent id. For tools: the tool name. */
13
- id: string;
14
- }
15
-
16
- export interface Breadcrumb {
17
- id: string;
18
- tenant_id: string;
19
- name: string;
20
- title: string;
21
- content: Record<string, unknown>;
22
- /** AND-semantics when queried. Prefix conventions: `interpret:*`, `service:*`, `session:*`, `user:*`, etc. */
23
- tags: string[];
24
- /** Breadcrumbs this derived from (SAPL provenance chain). */
25
- parent_ids: string[];
26
- /** Optimistic locking. PATCH must include the current value. */
27
- version: number;
28
- created_by: Actor;
29
- created_at: string;
30
- updated_at: string;
31
- deleted_at: string | null;
32
- /** Optional embedding for semantic search. */
33
- embedding?: number[];
34
- /** Relative TTL — e.g. "24h", "30d". */
35
- ttl?: string;
36
- }
37
-
38
- export interface CreateBreadcrumbRequest {
39
- name?: string;
40
- title: string;
41
- content: Record<string, unknown>;
42
- tags?: string[];
43
- parent_ids?: string[];
44
- created_by?: Actor;
45
- ttl?: string;
46
- /** If a breadcrumb with the same name + tags exists, update it rather than inserting a new row. */
47
- upsert?: boolean;
48
- }
49
-
50
- export interface UpdateBreadcrumbRequest {
51
- /** Required. Optimistic locking fails with 409 Conflict otherwise. */
52
- version: number;
53
- title?: string;
54
- content?: Record<string, unknown>;
55
- tags?: string[];
56
- parent_ids?: string[];
57
- ttl?: string;
58
- }
59
-
60
- export interface BreadcrumbResponse {
61
- breadcrumb: Breadcrumb;
62
- action?: 'created' | 'updated';
63
- }
64
-
65
- export interface QueryByTagsOptions {
66
- /** Max rows to return. Server max is 200. */
67
- limit?: number;
68
- offset?: number;
69
- /** Exact name match. */
70
- name?: string;
71
- order?: 'newest' | 'oldest';
72
- }
73
-
74
- export interface SemanticSearchOptions {
75
- limit?: number;
76
- tags?: string[];
77
- }