@love-moon/app-sdk 0.3.2

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,364 @@
1
+ import * as react_jsx_runtime from 'react/jsx-runtime';
2
+ import { ReactNode } from 'react';
3
+
4
+ /**
5
+ * Message: one entry in a task's chat transcript.
6
+ *
7
+ * Roles:
8
+ * - 'user' — message sent by the user (or the SDK on their behalf).
9
+ * - 'sdk' — message sent by an SDK / CLI / app integration (still
10
+ * appears in the chat as a user-side bubble).
11
+ * - 'assistant' — AI reply.
12
+ * - 'system' — system-emitted notice (rare; renders muted).
13
+ *
14
+ * `role` is intentionally open-vocabulary; new backend roles render as
15
+ * a generic bubble.
16
+ */
17
+ interface Message {
18
+ id: string;
19
+ taskId: string;
20
+ role: MessageRole | string;
21
+ content: string;
22
+ metadata: Record<string, unknown> | null;
23
+ attachments: Attachment[];
24
+ createdAt: string;
25
+ }
26
+ type MessageRole = 'user' | 'sdk' | 'assistant' | 'system';
27
+ interface Attachment {
28
+ id: string;
29
+ filename: string;
30
+ mimeType: string;
31
+ sizeBytes: number;
32
+ /** Resolvable URL or relative path the host must turn into a fetchable URL. */
33
+ url: string;
34
+ }
35
+ interface SendMessageInput {
36
+ content: string;
37
+ /** Idempotency key. When omitted, the SDK auto-generates a UUID. */
38
+ clientRequestId?: string;
39
+ metadata?: Record<string, unknown>;
40
+ /** Optional attachments to include with this message; must be pre-uploaded. */
41
+ attachmentIds?: string[];
42
+ /** Role override; defaults to 'sdk'. */
43
+ role?: MessageRole;
44
+ }
45
+
46
+ /**
47
+ * Runtime status pushed by the daemon while a task is in progress.
48
+ * Mirrors `task_runtime_status` envelope on /ws/app.
49
+ */
50
+ interface RuntimeStatus {
51
+ taskId: string;
52
+ /** High-level phase: 'idle' | 'thinking' | 'tool_call' | 'awaiting_user' | 'done'. */
53
+ state: RuntimeState;
54
+ phase?: string | null;
55
+ source?: string | null;
56
+ /** Short status line shown next to the AI avatar ("Reading file X..."). */
57
+ statusLine?: string | null;
58
+ /** Final status line shown when the reply finishes. */
59
+ statusDoneLine?: string | null;
60
+ replyPreview?: string | null;
61
+ replyTo?: string | null;
62
+ replyInProgress?: boolean;
63
+ backend?: string | null;
64
+ threadId?: string | null;
65
+ daemon?: string | null;
66
+ pid?: number | null;
67
+ sessionId?: string | null;
68
+ sessionFilePath?: string | null;
69
+ tokenUsagePercent?: number | null;
70
+ contextUsagePercent?: number | null;
71
+ createdAt?: string | null;
72
+ }
73
+ type RuntimeState = 'idle' | 'thinking' | 'tool_call' | 'awaiting_user' | 'done' | string;
74
+
75
+ /**
76
+ * ChatEvent: the minimal set of events the chat widget needs to render.
77
+ *
78
+ * This is intentionally a small union — the widget should not have to
79
+ * pattern-match dozens of envelope types. The default REST/WS adapter
80
+ * funnels raw `/ws/app` envelopes into ChatEvents; custom adapters can do the
81
+ * same translation against any wire format.
82
+ */
83
+ /**
84
+ * Error shape carried inside `task_failed` ChatEvents and `error`
85
+ * StreamReplyDeltas.
86
+ *
87
+ * `details` and `cause` are optional, free-form passthroughs of the original
88
+ * SDK error's structured fields — useful for surfacing request IDs / server
89
+ * payloads in host UIs without depending on the SDK error class directly.
90
+ */
91
+ interface ChatEventError {
92
+ code: string;
93
+ message: string;
94
+ details?: unknown;
95
+ cause?: unknown;
96
+ }
97
+ type ChatEvent = {
98
+ type: 'message_appended';
99
+ message: Message;
100
+ } | {
101
+ type: 'message_updated';
102
+ message: Message;
103
+ } | {
104
+ type: 'runtime_status';
105
+ status: RuntimeStatus;
106
+ } | {
107
+ type: 'task_finished';
108
+ taskId: string;
109
+ } | {
110
+ type: 'task_failed';
111
+ taskId: string;
112
+ error: ChatEventError;
113
+ } | {
114
+ type: 'connection_state';
115
+ state: 'connected' | 'reconnecting' | 'offline';
116
+ };
117
+
118
+ /**
119
+ * ChatAdapter: the contract between the React widget and *something* that
120
+ * can talk to a Conductor-shaped backend. The widget calls these methods;
121
+ * implementations decide how to translate to HTTP / WS / GraphQL / in-process
122
+ * calls.
123
+ *
124
+ * The default implementation `createRestAdapter` (in `/react`) talks to a
125
+ * BFF that mirrors Conductor's REST shape. Hosts using a custom wire format
126
+ * just implement this interface directly.
127
+ */
128
+ interface ChatAdapter {
129
+ fetchHistory(taskId: string, opts?: {
130
+ beforeId?: string;
131
+ limit?: number;
132
+ signal?: AbortSignal;
133
+ }): Promise<{
134
+ messages: Message[];
135
+ hasMoreBefore: boolean;
136
+ oldestMessageId: string | null;
137
+ }>;
138
+ subscribe(taskId: string, handler: (event: ChatEvent) => void): {
139
+ unsubscribe(): void;
140
+ };
141
+ sendMessage(taskId: string, input: SendMessageInput): Promise<Message>;
142
+ interrupt(taskId: string, opts: {
143
+ targetReplyTo: string;
144
+ }): Promise<void>;
145
+ /** Optional. Adapters that don't support attachments may omit. */
146
+ uploadAttachment?(taskId: string, file: File | Blob, opts?: {
147
+ signal?: AbortSignal;
148
+ filename?: string;
149
+ }): Promise<{
150
+ id: string;
151
+ url: string;
152
+ }>;
153
+ }
154
+
155
+ interface ChatViewProps {
156
+ taskId: string;
157
+ adapter: ChatAdapter;
158
+ /** i18n strings; falls back to English. */
159
+ labels?: Partial<ChatViewLabels>;
160
+ /** Inline theme overrides. Maps to CSS variables on the root element. */
161
+ theme?: ChatViewTheme;
162
+ /** Force layout regardless of viewport. Default 'auto'. */
163
+ layout?: 'desktop' | 'mobile' | 'auto';
164
+ /**
165
+ * Whether to show "via {appDisplayName}" chip on bubbles with audit origin.
166
+ * Default `false`. The main Conductor app, which renders other users'
167
+ * app-created messages, sets this to `true`. Third-party hosts embedding
168
+ * the widget in their own app should leave it `false` to avoid the
169
+ * "via <self>" self-reference loop.
170
+ */
171
+ showAppOriginChip?: boolean;
172
+ onError?: (error: unknown) => void;
173
+ /** Extra className applied to the root container. */
174
+ className?: string;
175
+ /** Disable send/interact (e.g. when task is read-only). */
176
+ readOnly?: boolean;
177
+ }
178
+ interface ChatViewLabels {
179
+ statusThinking: string;
180
+ statusToolCall: string;
181
+ statusAwaitingUser: string;
182
+ statusDone: string;
183
+ inputPlaceholder: string;
184
+ send: string;
185
+ interrupt: string;
186
+ restart: string;
187
+ loadEarlier: string;
188
+ }
189
+ interface ChatViewTheme {
190
+ accent?: string;
191
+ background?: string;
192
+ bubbleUser?: string;
193
+ bubbleAssistant?: string;
194
+ /** Arbitrary CSS variable override (key without `--` prefix). */
195
+ [cssVarKey: string]: string | undefined;
196
+ }
197
+ declare function ChatView(props: ChatViewProps): react_jsx_runtime.JSX.Element;
198
+
199
+ interface MessageListProps {
200
+ labels: ChatViewLabels;
201
+ }
202
+ declare function MessageList({ labels }: MessageListProps): react_jsx_runtime.JSX.Element;
203
+
204
+ interface MessageInputProps {
205
+ labels: ChatViewLabels;
206
+ /** Disable the send button (useful while a turn is in progress). */
207
+ disabled?: boolean;
208
+ }
209
+ declare function MessageInput({ labels, disabled }: MessageInputProps): react_jsx_runtime.JSX.Element;
210
+
211
+ interface RuntimeStatusBarProps {
212
+ labels: ChatViewLabels;
213
+ }
214
+ declare function RuntimeStatusBar({ labels }: RuntimeStatusBarProps): react_jsx_runtime.JSX.Element;
215
+
216
+ interface ChatState {
217
+ messages: Message[];
218
+ /** Optimistically inserted, awaiting server echo. Keyed by clientRequestId. */
219
+ pendingByClientId: Record<string, Message>;
220
+ runtime: RuntimeStatus | null;
221
+ connectionState: 'connected' | 'reconnecting' | 'offline';
222
+ hasMoreBefore: boolean;
223
+ oldestMessageId: string | null;
224
+ loadingHistory: boolean;
225
+ error: ChatError | null;
226
+ /** ID of the latest assistant reply, used for interrupt targeting. */
227
+ latestReplyId: string | null;
228
+ }
229
+ /**
230
+ * Error captured into chat state. Carries the structured fields from
231
+ * `ConductorAppError` and from `ChatEventError` (task_failed envelopes) so
232
+ * the host UI can surface request IDs, server payloads, and the original
233
+ * cause without depending on the SDK error class directly.
234
+ *
235
+ * `cause` is also carried for parity with `ChatEventError` — terminal errors
236
+ * surfaced via WS (e.g. iterator-synthesised task_failed) include an `Error`
237
+ * instance in `cause` so callers walking the cause chain recover the stack.
238
+ */
239
+ interface ChatError {
240
+ code: string;
241
+ message: string;
242
+ status?: number;
243
+ requestId?: string;
244
+ details?: unknown;
245
+ cause?: unknown;
246
+ }
247
+ interface ChatContextValue {
248
+ state: ChatState;
249
+ taskId: string;
250
+ adapter: ChatAdapter;
251
+ send: (content: string, opts?: {
252
+ metadata?: Record<string, unknown>;
253
+ }) => Promise<void>;
254
+ interrupt: () => Promise<void>;
255
+ loadEarlier: () => Promise<void>;
256
+ }
257
+ declare function useChat(): ChatContextValue;
258
+ interface ChatProviderProps {
259
+ taskId: string;
260
+ adapter: ChatAdapter;
261
+ onError?: (error: unknown) => void;
262
+ children: ReactNode;
263
+ }
264
+ declare function ChatProvider(props: ChatProviderProps): react_jsx_runtime.JSX.Element;
265
+
266
+ /**
267
+ * createRestAdapter: default `ChatAdapter` that talks to a host BFF exposing
268
+ * Conductor-shaped REST endpoints + an SSE event stream.
269
+ *
270
+ * Expected BFF surface (all relative to `baseUrl`, all returning JSON):
271
+ *
272
+ * GET /tasks/:taskId/messages?pagination=1&limit=N&before_id=...
273
+ * → { messages, pagination: { has_more_before, oldest_message_id } }
274
+ * POST /tasks/:taskId/messages
275
+ * body: { content, role?, clientRequestId?, metadata? }
276
+ * → Message
277
+ * POST /tasks/:taskId/interrupt
278
+ * body: { target_reply_to }
279
+ * → 200
280
+ * GET /tasks/:taskId/events
281
+ * → text/event-stream emitting `data: <ChatEvent JSON>\n\n`
282
+ *
283
+ * Auth: optionally a Bearer token via `authToken` (string or async fn) sent
284
+ * on REST calls. Cookies are honored by default via `credentials: 'include'`
285
+ * (configurable). EventSource doesn't support custom headers in the
286
+ * standard browser API — for token-based SSE auth, hosts should put the
287
+ * token in the URL or rely on cookies.
288
+ */
289
+
290
+ interface RestAdapterOptions {
291
+ /** BFF base URL (relative or absolute), e.g. `/api/conductor`. */
292
+ baseUrl: string;
293
+ /** Bearer token (string or async provider). Optional — omit for cookie-based auth. */
294
+ authToken?: string | (() => Promise<string>);
295
+ /**
296
+ * Whether to send credentials (cookies) for REST + SSE. Defaults to
297
+ * 'include' (assume same-origin BFF or CORS configured to allow creds).
298
+ */
299
+ credentials?: RequestCredentials;
300
+ /** Override fetch (SSR, testing). Defaults to `globalThis.fetch`. */
301
+ fetch?: typeof globalThis.fetch;
302
+ /**
303
+ * Override EventSource constructor. Lets hosts inject `eventsource` polyfill
304
+ * for environments without native EventSource (Node-side rendering).
305
+ * Defaults to `globalThis.EventSource`.
306
+ */
307
+ eventSource?: typeof globalThis.EventSource;
308
+ /** Per-request timeout in ms. Default 30_000. */
309
+ timeoutMs?: number;
310
+ }
311
+ declare function createRestAdapter(options: RestAdapterOptions): ChatAdapter;
312
+
313
+ /**
314
+ * Task: a single AI conversation within a project.
315
+ *
316
+ * Mirrors the shape returned by Conductor's `/api/tasks/[taskId]` REST endpoint
317
+ * (camelCase normalized). All fields are present after a successful read; the
318
+ * `null`-able fields reflect cases where the backend hasn't populated them yet
319
+ * (e.g. a task created via SDK before its daemon picks it up).
320
+ */
321
+ interface Task {
322
+ id: string;
323
+ projectId: string;
324
+ title: string;
325
+ status: TaskStatus;
326
+ /** Backend execution engine ("claude_code" / "codex" / etc.) or null when not selected yet. */
327
+ backendType: string | null;
328
+ /** AI session id assigned by the daemon; null until the daemon attaches. */
329
+ sessionId: string | null;
330
+ sessionFilePath: string | null;
331
+ /** ISO 8601 timestamps. Always present after the task is persisted. */
332
+ createdAt: string;
333
+ updatedAt: string;
334
+ }
335
+ type TaskStatus = 'pending' | 'running' | 'finished' | 'failed' | 'cancelled' | string;
336
+
337
+ /**
338
+ * ConductorAppError: every error thrown out of the SDK is one of these.
339
+ * Callers can `if (err instanceof ConductorAppError) switch (err.code)`.
340
+ *
341
+ * `code` is open-vocabulary for forward compat; the SDK promises to never
342
+ * remove existing codes, only add new ones, and to bump the minor version
343
+ * when a new code is introduced.
344
+ */
345
+ declare class ConductorAppError extends Error {
346
+ readonly name = "ConductorAppError";
347
+ readonly code: ConductorErrorCode | string;
348
+ readonly status?: number;
349
+ readonly details?: unknown;
350
+ readonly requestId?: string;
351
+ constructor(args: {
352
+ code: ConductorErrorCode | string;
353
+ message: string;
354
+ status?: number;
355
+ details?: unknown;
356
+ requestId?: string;
357
+ cause?: unknown;
358
+ });
359
+ }
360
+ type ConductorErrorCode = 'unauthorized' | 'forbidden' | 'token_revoked' | 'invalid_input' | 'project_not_found' | 'task_not_found' | 'message_not_found' | 'daemon_offline' | 'workspace_path_conflict' | 'binding_validation_failed' | 'task_not_running' | 'task_type_not_messageable' | 'task_type_not_interruptible' | 'task_fire_owner_offline' | 'network_error' | 'timeout' | 'rate_limited' | 'server_error' | 'subscribe_failed' | 'stream_aborted';
361
+ /** Helper: type-guard for callers that don't want `instanceof`. */
362
+ declare function isConductorAppError(error: unknown): error is ConductorAppError;
363
+
364
+ export { type Attachment, type ChatAdapter, type ChatEvent, ChatProvider, type ChatState, ChatView, type ChatViewLabels, type ChatViewProps, type ChatViewTheme, ConductorAppError, type Message, MessageInput, MessageList, type MessageRole, type RestAdapterOptions, type RuntimeState, type RuntimeStatus, RuntimeStatusBar, type SendMessageInput, type Task, type TaskStatus, createRestAdapter, isConductorAppError, useChat };