@ateam-ai-mobile/core 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.
@@ -0,0 +1,389 @@
1
+ import * as zustand from 'zustand';
2
+
3
+ /**
4
+ * Storage adapter contracts. The core is platform-agnostic (no Expo, no
5
+ * AsyncStorage imports) — hosts or @ateam-ai-mobile/ui plug in concrete implementations.
6
+ */
7
+ /** Outgoing attachment as sent to /api/chat (base64 payload). */
8
+ interface OutgoingAttachment {
9
+ data: string;
10
+ mimeType: string;
11
+ name: string;
12
+ }
13
+ /** Attachment as rendered in a message bubble (local or remote preview URI). */
14
+ interface AttachmentPreview {
15
+ mimeType: string;
16
+ name: string;
17
+ previewUri?: string;
18
+ }
19
+ /**
20
+ * Persists outgoing attachments keyed by jobId so image previews survive
21
+ * app restarts (/api/conversation does not echo user attachments back).
22
+ */
23
+ interface AttachmentCache {
24
+ /** Resolve once the index is loaded — get() before this may return undefined. */
25
+ ready(): Promise<void>;
26
+ save(jobId: string, attachments: OutgoingAttachment[]): Promise<void>;
27
+ get(jobId: string): AttachmentPreview[] | undefined;
28
+ }
29
+
30
+ /**
31
+ * SDK configuration — the single injection point between the host app and
32
+ * the ADAS core. The host owns authentication, identity, and URL discovery;
33
+ * the SDK reads everything through the config registered via initAdas().
34
+ *
35
+ * There is intentionally ONE active config per JS context (module singleton):
36
+ * the chat store, WebSocket, and API client below it are app-wide singletons
37
+ * too, and a second tenant/backend inside one app is not a supported v1 case.
38
+ */
39
+
40
+ /** Result of a host-driven token refresh. Mirrors the semantics the API
41
+ * client depends on: only 'auth_failed' logs the user out; 'transient'
42
+ * (network blip, 5xx) keeps the session so the next call can retry. */
43
+ interface RefreshOutcome {
44
+ ok: 'ok' | 'auth_failed' | 'transient';
45
+ status: number;
46
+ reason?: string;
47
+ }
48
+ /**
49
+ * Host-implemented auth bridge. The SDK never shows login UI and never
50
+ * stores credentials — the host authenticates its own users and hands the
51
+ * SDK a way to read/refresh the ADAS JWT it minted for them (via the
52
+ * server-to-server token exchange).
53
+ */
54
+ interface TokenProvider {
55
+ /** Current ADAS JWT, or null when signed out. Called on every request —
56
+ * keep it synchronous and cheap (read from memory, not disk). */
57
+ getToken(): string | null;
58
+ /** Called on 401. Host should mint/refresh the ADAS token and return the
59
+ * outcome; 'ok' makes the SDK retry the failed request once. */
60
+ refresh(): Promise<RefreshOutcome>;
61
+ /** Auth is genuinely dead (refresh rejected, or retry still 401). Host
62
+ * should treat this as sign-out for the embedded experience. */
63
+ onAuthFailure?: () => void;
64
+ }
65
+ interface AdasUser {
66
+ actorId: string;
67
+ name?: string;
68
+ email?: string;
69
+ }
70
+ interface AdasConfig {
71
+ /** Core API base URL, e.g. https://app.ateam-ai.com */
72
+ coreUrl: string;
73
+ /** Voice service base URL (optional — only voice features need it). */
74
+ voiceUrl?: string;
75
+ tokenProvider: TokenProvider;
76
+ /** Tenant slug — informational/telemetry only. The backend derives the
77
+ * tenant from the JWT; the SDK never sends a tenant header. */
78
+ tenant?: string;
79
+ /** Current end user as known to the host. actorId is used as the chat
80
+ * reply context; without it, replies route to 'unknown'. */
81
+ getUser?: () => AdasUser | null;
82
+ /** Optional persistence for outgoing image/file attachments so previews
83
+ * survive app restarts. No-op when omitted (previews live in memory for
84
+ * the session only). @ateam-ai-mobile/ui ships an Expo/RN implementation. */
85
+ attachmentCache?: AttachmentCache;
86
+ }
87
+ /** Register the SDK configuration. Call once at app start, before any other
88
+ * SDK API. Calling again replaces the config (e.g. env switch) — combine
89
+ * with chat.clearChat() and closeChatStream() for a clean swap. */
90
+ declare function initAdas(config: AdasConfig): void;
91
+ declare function isAdasInitialized(): boolean;
92
+ declare function getConfig(): AdasConfig;
93
+ declare function getCoreApiUrl(): string;
94
+ declare function getCoreWsUrl(): string;
95
+ declare function getVoiceApiUrl(): string;
96
+ declare function getTenant(): string;
97
+ declare function getUser(): AdasUser | null;
98
+ declare function getToken(): string | null;
99
+
100
+ type FetchOptions = RequestInit & {
101
+ baseUrl?: 'core' | 'voice';
102
+ };
103
+ /**
104
+ * Base API client with JWT injection and 401 refresh handling.
105
+ *
106
+ * Auth failure semantics (preserved from the reference app): a transient
107
+ * 5xx or network blip on the RETRY after a successful refresh must NOT be
108
+ * treated as sign-out. notifyAuthFailure() fires only when auth is genuinely
109
+ * broken:
110
+ * - Refresh itself was rejected with 401/403 → auth failure
111
+ * - Retry with the fresh JWT still returns 401/403 → auth failure
112
+ * Anything else throws but keeps the session intact — the caller may retry
113
+ * on the next user action.
114
+ */
115
+ declare function apiFetch<T = unknown>(path: string, options?: FetchOptions): Promise<T>;
116
+ /**
117
+ * Fetch with raw response (non-JSON responses, streaming bodies).
118
+ */
119
+ declare function apiFetchRaw(path: string, options?: FetchOptions): Promise<Response>;
120
+
121
+ /**
122
+ * Call a tool on a specific connector via POST /api/connectors/:id/call.
123
+ *
124
+ * NOTE: intentionally bypasses the /mcp proxy (which routes to adas-mcp).
125
+ * adas-mcp only knows skill-generated tools, NOT raw connector tools like
126
+ * triggers.list — the direct connector API is the reliable path.
127
+ */
128
+ declare function connectorCall(connectorId: string, tool: string, args?: Record<string, any>): Promise<{
129
+ ok: boolean;
130
+ result: any;
131
+ error?: string;
132
+ }>;
133
+ /** Raw JSON-RPC passthrough to /mcp. Returns the full JSON-RPC response. */
134
+ declare function mcpFetch(body: any): Promise<any>;
135
+ /** JSON-RPC tools/call to /mcp (for core tools like cp.fe_api). */
136
+ declare function mcpToolCall(toolName: string, args?: Record<string, any>): Promise<any>;
137
+ /** Post a plugin command result back to Core. */
138
+ declare function postCommandResult(correlationId: string, result: any, error?: string): Promise<void>;
139
+ /**
140
+ * Unwrap MCP JSON-RPC text content → parsed business data.
141
+ * Server returns: { content: [{ type: "text", text: "{...}" }] }
142
+ */
143
+ declare function unwrapMcpResult(result: any): any;
144
+
145
+ interface ToolCall {
146
+ name: string;
147
+ args: Record<string, unknown>;
148
+ result: unknown;
149
+ skill?: string;
150
+ }
151
+ interface JobProgress {
152
+ jobId: string;
153
+ /** Actor that owns the running chain — needed when restoring active work. */
154
+ actorId?: string;
155
+ status: 'running' | 'done' | 'error' | 'paused' | 'unknown';
156
+ subStatus?: string;
157
+ phase?: string;
158
+ iteration?: number;
159
+ /** Active skill slug at this point in the chain (e.g. "messaging-agent") */
160
+ skillSlug?: string;
161
+ /** Most recent tool being invoked by the active skill */
162
+ currentTool?: string;
163
+ /** Wall-clock timestamp when the chain started — used for elapsed timer */
164
+ chainStartedAt?: number;
165
+ resultMessage?: string;
166
+ /** UI Action chips to attach to the assistant bubble when this job finishes. */
167
+ resultActions?: UIAction[];
168
+ /** Live progress from adas_emit_progress() — latest event from a running fat tool */
169
+ liveProgress?: {
170
+ message: string;
171
+ step?: number;
172
+ total?: number;
173
+ };
174
+ pendingQuestion?: {
175
+ inputId: string;
176
+ question: string;
177
+ inputType: 'text' | 'choice' | 'confirm';
178
+ options?: string[];
179
+ placeholder?: string;
180
+ };
181
+ }
182
+ type MessageAttachment = AttachmentPreview;
183
+ /**
184
+ * UI Action chip — emitted by the polisher LLM, validated by cp.uiActions_api.
185
+ */
186
+ interface UIAction {
187
+ intent: 'view_entity' | 'suggested_action' | 'quick_toggle' | 'cross_plugin';
188
+ label: string;
189
+ description?: string;
190
+ icon?: string;
191
+ /** Short plugin id (e.g. "triggers-panel") — what the LLM picks from the inventory. */
192
+ target: string;
193
+ /** Full plugin id — added by validator, used by the host to navigate. */
194
+ pluginId?: string;
195
+ /** Already-substituted iframe URL — used by web; mobile ignores it. */
196
+ resolvedUrl?: string;
197
+ /** Intent-specific payload. */
198
+ payload: Record<string, any>;
199
+ }
200
+ interface Message {
201
+ id: string;
202
+ role: 'user' | 'assistant' | 'system';
203
+ content: string;
204
+ timestamp: Date;
205
+ toolCalls?: ToolCall[];
206
+ jobProgress?: JobProgress;
207
+ /** Skill that handled this turn */
208
+ skillSlug?: string;
209
+ /** Attached images/files */
210
+ attachments?: MessageAttachment[];
211
+ /** UI Action chips beneath assistant bubbles */
212
+ actions?: UIAction[];
213
+ /** Chain id from /api/conversation — used to attach live progress to the
214
+ * correct persisted turn instead of fabricating a duplicate bubble. */
215
+ chainId?: string;
216
+ /** Turn variant. 'notification' = proactive/system-initiated. */
217
+ kind?: 'notification' | string;
218
+ }
219
+
220
+ interface ChatState {
221
+ messages: Message[];
222
+ loading: boolean;
223
+ error: string | null;
224
+ activeJobId: string | null;
225
+ conversationLoaded: boolean;
226
+ /** Load conversation history from Core */
227
+ loadConversation: () => Promise<void>;
228
+ /** Send a message via POST /api/chat — creates a job, streams progress */
229
+ sendMessage: (text: string, skillSlug?: string, attachments?: OutgoingAttachment[]) => Promise<void>;
230
+ /** Respond to a paused sys.askUser question */
231
+ respondToQuestion: (jobId: string, inputId: string, answer: string) => Promise<void>;
232
+ /** Update in-flight job progress (called by the stream handler) */
233
+ updateJobProgress: (jobId: string, progress: Partial<JobProgress>) => void;
234
+ /** Reset chat (also re-fetches history on next mount — used on auth switch) */
235
+ clearChat: () => void;
236
+ /** Start a fresh empty chat without re-fetching history */
237
+ newChat: () => void;
238
+ }
239
+ /**
240
+ * Sentinel delimiters used to embed structured table data inside a flat
241
+ * message string. UI layers split on these and render the contents with a
242
+ * native table component instead of markdown tables — markdown tables look
243
+ * terrible on narrow phone screens.
244
+ *
245
+ * Unicode Private Use Area chars (U+E100/U+E101) so they never collide with
246
+ * anything the model could legitimately emit.
247
+ */
248
+ declare const TABLE_BLOCK_OPEN = "\uE100";
249
+ declare const TABLE_BLOCK_CLOSE = "\uE101";
250
+ /**
251
+ * Extract display text from a conversation turn's assistant blocks.
252
+ * Handles multiple block kinds (text, code, json, markdown, table, html).
253
+ */
254
+ declare function blocksToText(blocks?: Array<{
255
+ kind: string;
256
+ content: any;
257
+ }>): string;
258
+ declare const useChatStore: zustand.UseBoundStore<zustand.StoreApi<ChatState>>;
259
+ /** Tear down the live connection (env/auth switch, SDK unmount). */
260
+ declare function closeChatStream(): void;
261
+
262
+ /**
263
+ * Lightweight event emitter bridging the chat job stream → plugin bridges
264
+ * (React Native has no Node 'events' module).
265
+ *
266
+ * Events: 'plugin-command' (raw command), 'focus-plugin' ({ pluginId, props }),
267
+ * 'plugin-close' ({ pluginId }), 'prepare-chat-message' ({ text }).
268
+ */
269
+ type Listener = (data: any) => void;
270
+ interface BufferedCommand {
271
+ pluginId: string;
272
+ command: string;
273
+ args?: any;
274
+ correlationId?: string;
275
+ _bufferedAt: number;
276
+ }
277
+ declare class PluginCommandEmitter {
278
+ private listeners;
279
+ private recent;
280
+ on(event: string, fn: Listener): () => void;
281
+ off(event: string, fn: Listener): void;
282
+ emit(event: string, data: any): void;
283
+ /** Pop and return any plugin-commands targeted at `pluginId` that were
284
+ * emitted within the last RECENT_TTL_MS. Used by the native bridge on
285
+ * mount so the surface-dispatcher path doesn't drop the first command. */
286
+ drainPluginCommands(pluginId: string): BufferedCommand[];
287
+ }
288
+ /** App-wide bus for plugin commands arriving over the chat stream. */
289
+ declare const pluginCommandEmitter: PluginCommandEmitter;
290
+
291
+ declare const colors: {
292
+ readonly dark: {
293
+ readonly bgPrimary: "#0a0e14";
294
+ readonly bgSecondary: "#111820";
295
+ readonly bgTertiary: "#1a2230";
296
+ readonly bgInput: "#0d1219";
297
+ readonly bgHover: "#1e2a3a";
298
+ readonly border: "rgba(255, 255, 255, 0.08)";
299
+ readonly borderHover: "rgba(255, 255, 255, 0.15)";
300
+ readonly textPrimary: "#e8ecf1";
301
+ readonly textSecondary: "#8b95a5";
302
+ readonly textMuted: "#5a6577";
303
+ readonly accent: "#2563eb";
304
+ readonly accentHover: "#3b82f6";
305
+ readonly accentSoft: "rgba(37, 99, 235, 0.15)";
306
+ readonly green: "#16a34a";
307
+ readonly greenSoft: "rgba(22, 163, 74, 0.15)";
308
+ readonly red: "#dc2626";
309
+ readonly redSoft: "rgba(220, 38, 38, 0.15)";
310
+ readonly amber: "#f59e0b";
311
+ readonly amberSoft: "rgba(245, 158, 11, 0.15)";
312
+ readonly purple: "#8b5cf6";
313
+ readonly purpleSoft: "rgba(139, 92, 246, 0.15)";
314
+ readonly userBubble: "#2563eb";
315
+ readonly botBubble: "#1a2230";
316
+ readonly botBubbleBorder: "rgba(255, 255, 255, 0.08)";
317
+ };
318
+ readonly light: {
319
+ readonly bgPrimary: "#ffffff";
320
+ readonly bgSecondary: "#f8f9fa";
321
+ readonly bgTertiary: "#f1f3f5";
322
+ readonly bgInput: "#ffffff";
323
+ readonly bgHover: "#e9ecef";
324
+ readonly border: "rgba(0, 0, 0, 0.08)";
325
+ readonly borderHover: "rgba(0, 0, 0, 0.15)";
326
+ readonly textPrimary: "#1a1a2e";
327
+ readonly textSecondary: "#6b7280";
328
+ readonly textMuted: "#9ca3af";
329
+ readonly accent: "#2563eb";
330
+ readonly accentHover: "#1d4ed8";
331
+ readonly accentSoft: "rgba(37, 99, 235, 0.1)";
332
+ readonly green: "#16a34a";
333
+ readonly greenSoft: "rgba(22, 163, 74, 0.1)";
334
+ readonly red: "#dc2626";
335
+ readonly redSoft: "rgba(220, 38, 38, 0.1)";
336
+ readonly amber: "#f59e0b";
337
+ readonly amberSoft: "rgba(245, 158, 11, 0.1)";
338
+ readonly purple: "#8b5cf6";
339
+ readonly purpleSoft: "rgba(139, 92, 246, 0.1)";
340
+ readonly userBubble: "#2563eb";
341
+ readonly botBubble: "#f1f3f5";
342
+ readonly botBubbleBorder: "rgba(0, 0, 0, 0.08)";
343
+ };
344
+ };
345
+ declare const ada: {
346
+ readonly paper: "#FAF6EE";
347
+ readonly paperSoft: "#F2EADC";
348
+ readonly paperDeep: "#EBE0CC";
349
+ readonly ink: "#1a1410";
350
+ readonly inkSoft: "#5a4d3f";
351
+ readonly inkMuted: "#9a8870";
352
+ readonly border: "rgba(26, 20, 16, 0.08)";
353
+ readonly borderStrong: "rgba(26, 20, 16, 0.16)";
354
+ readonly orange: "#FF7A28";
355
+ readonly orangeDeep: "#B8410A";
356
+ readonly orangeSoft: "rgba(255, 122, 40, 0.14)";
357
+ readonly userBubble: "#1a1410";
358
+ readonly userBubbleText: "#FAF6EE";
359
+ readonly botBubble: "#F2EADC";
360
+ readonly botBubbleBorder: "rgba(26, 20, 16, 0.06)";
361
+ readonly botBubbleText: "#1a1410";
362
+ };
363
+ declare const spacing: {
364
+ readonly xs: 4;
365
+ readonly sm: 8;
366
+ readonly md: 12;
367
+ readonly lg: 16;
368
+ readonly xl: 20;
369
+ readonly xxl: 24;
370
+ readonly xxxl: 32;
371
+ };
372
+ declare const fontSize: {
373
+ readonly xs: 11;
374
+ readonly sm: 13;
375
+ readonly md: 15;
376
+ readonly lg: 17;
377
+ readonly xl: 20;
378
+ readonly xxl: 24;
379
+ readonly title: 28;
380
+ };
381
+ declare const borderRadius: {
382
+ readonly sm: 8;
383
+ readonly md: 12;
384
+ readonly lg: 16;
385
+ readonly xl: 20;
386
+ readonly full: 9999;
387
+ };
388
+
389
+ export { type AdasConfig, type AdasUser, type AttachmentCache, type AttachmentPreview, type BufferedCommand, type JobProgress, type Message, type MessageAttachment, type OutgoingAttachment, PluginCommandEmitter, type RefreshOutcome, TABLE_BLOCK_CLOSE, TABLE_BLOCK_OPEN, type TokenProvider, type ToolCall, type UIAction, ada, apiFetch, apiFetchRaw, blocksToText, borderRadius, closeChatStream, colors, connectorCall, fontSize, getConfig, getCoreApiUrl, getCoreWsUrl, getTenant, getToken, getUser, getVoiceApiUrl, initAdas, isAdasInitialized, mcpFetch, mcpToolCall, pluginCommandEmitter, postCommandResult, spacing, unwrapMcpResult, useChatStore };