@arbidocs/sdk 0.3.15 → 0.3.17

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.
package/README.md CHANGED
@@ -1,130 +1,107 @@
1
1
  # @arbidocs/sdk
2
2
 
3
- High-level TypeScript SDK for the ARBI platform. Wraps authentication, workspace encryption, and all API operations into a single `Arbi` class.
3
+ TypeScript SDK for the ARBI platform. Handles authentication, workspace encryption, and streaming AI queries.
4
4
 
5
- ## Install
5
+ ## Quickstart
6
6
 
7
7
  ```bash
8
8
  npm install @arbidocs/sdk
9
9
  ```
10
10
 
11
- For Node.js, also install the IndexedDB polyfill:
12
-
13
- ```bash
14
- npm install fake-indexeddb
15
- ```
16
-
17
- ## Quick Start
18
-
19
11
  ```typescript
20
- import 'fake-indexeddb/auto' // Node.js only
21
- import { Arbi } from '@arbidocs/sdk'
12
+ import { Arbi } from '@arbidocs/sdk/browser'
22
13
 
23
- const arbi = new Arbi({ url: 'https://arbi.mycompany.com' })
14
+ const arbi = new Arbi({ url: 'https://www.arbidocs.com' })
24
15
 
25
- // Login
16
+ // Login and select a workspace
26
17
  await arbi.login('user@example.com', 'password')
27
-
28
- // Pick a workspace
29
18
  const workspaces = await arbi.workspaces.list()
30
19
  await arbi.selectWorkspace(workspaces[0].external_id)
31
20
 
32
- // Use it
21
+ // Ask a streaming question about your documents
33
22
  const docs = await arbi.documents.list()
34
- const tags = await arbi.tags.list()
35
- const conversations = await arbi.conversations.list()
36
- ```
37
-
38
- ## Streaming AI Queries
39
-
40
- ```typescript
41
- const result = await arbi.assistant.query('What does section 3 say?', {
23
+ await arbi.assistant.query('Summarize this document', {
42
24
  docIds: [docs[0].external_id],
43
25
  onToken: (token) => process.stdout.write(token),
44
- onStreamStart: ({ assistant_message_ext_id }) => {
45
- console.log('Stream started:', assistant_message_ext_id)
46
- },
47
- onComplete: () => console.log('\nDone'),
48
26
  })
49
27
  ```
50
28
 
51
- ## Session Recovery
52
-
53
- After login, the SDK automatically persists your signing key (encrypted) in IndexedDB. You can also recover a session using the raw private key:
29
+ For Node.js, install `fake-indexeddb` and import it first:
54
30
 
55
31
  ```typescript
56
- // Save the key after first login (e.g. the recovery key download)
57
- const signingKeyBase64 = '...'
58
-
59
- // Later, recover without password:
60
- await arbi.loginWithKey('user@example.com', signingKeyBase64)
32
+ import 'fake-indexeddb/auto'
33
+ import { Arbi } from '@arbidocs/sdk'
61
34
  ```
62
35
 
63
- ## API Reference
64
-
65
- ### Constructor
36
+ ## Lifecycle
66
37
 
67
38
  ```typescript
68
- const arbi = new Arbi({
69
- url: 'https://arbi.mycompany.com', // Required: backend URL
70
- deploymentDomain: 'arbi.mycompany.com', // Optional: defaults to URL hostname
71
- credentials: 'omit', // Optional: 'omit' | 'include' | 'same-origin'
72
- })
73
- ```
39
+ const arbi = new Arbi({ url: 'https://www.arbidocs.com' })
74
40
 
75
- ### Lifecycle
41
+ await arbi.login(email, password) // or arbi.loginWithKey(email, keyBase64)
42
+ await arbi.selectWorkspace(workspaceId)
43
+ // ... use the SDK ...
44
+ await arbi.logout()
45
+ ```
76
46
 
77
- | Method | Description |
78
- |--------|-------------|
79
- | `login(email, password)` | Authenticate with email and password |
80
- | `loginWithKey(email, signingKeyBase64)` | Authenticate with a stored signing key |
81
- | `selectWorkspace(workspaceId)` | Select a workspace (decrypts workspace key) |
82
- | `logout()` | Log out and securely clear key material |
47
+ | Property | Description |
48
+ |----------|-------------|
49
+ | `arbi.isLoggedIn` | Whether the user is authenticated |
50
+ | `arbi.hasWorkspace` | Whether a workspace is selected |
83
51
 
84
- ### Properties
52
+ ## Operations
85
53
 
86
- | Property | Type | Description |
87
- |----------|------|-------------|
88
- | `isLoggedIn` | `boolean` | Whether the user is authenticated |
89
- | `hasWorkspace` | `boolean` | Whether a workspace is selected |
54
+ | Namespace | Key methods |
55
+ |-----------|-------------|
56
+ | `arbi.workspaces` | `list()`, `create()`, `update()`, `delete()`, `listUsers()`, `addUsers()`, `removeUsers()` |
57
+ | `arbi.documents` | `list()`, `get()`, `uploadFile()`, `uploadUrl()`, `update()`, `delete()`, `download()` |
58
+ | `arbi.conversations` | `list()`, `getThreads()`, `updateTitle()`, `share()`, `delete()` |
59
+ | `arbi.assistant` | `query(question, options)`, `retrieve(query, docIds)`, `respond(messageId, answer)` |
60
+ | `arbi.tags` | `list()`, `create()`, `update()`, `delete()` |
61
+ | `arbi.doctags` | `assign()`, `remove()`, `generate()` |
62
+ | `arbi.contacts` | `list()`, `add()`, `remove()` |
63
+ | `arbi.health` | `check()`, `models()`, `mcpTools()` |
90
64
 
91
- ### Operations
65
+ ## Streaming AI queries
92
66
 
93
- All operations are namespaced and require a workspace to be selected (except `workspaces` and `health`).
67
+ ```typescript
68
+ const result = await arbi.assistant.query('What does section 3 say?', {
69
+ docIds: [docId],
70
+ previousResponseId: lastMessageId, // for multi-turn conversations
71
+ onToken: (token) => { /* append to UI */ },
72
+ onStreamStart: ({ assistant_message_ext_id }) => { /* save for follow-ups */ },
73
+ onAgentStep: (step) => { /* agent tool usage */ },
74
+ onError: (message) => { /* handle error */ },
75
+ onComplete: () => { /* done */ },
76
+ })
94
77
 
95
- | Namespace | Methods |
96
- |-----------|---------|
97
- | `arbi.workspaces` | `list()`, `create()`, `update()`, `delete()`, `listUsers()`, `addUsers()`, `removeUsers()`, `setUserRole()` |
98
- | `arbi.documents` | `list()`, `get()`, `upload()`, `update()`, `delete()` |
99
- | `arbi.conversations` | `list()`, `getThreads()`, `updateTitle()`, `share()`, `delete()` |
100
- | `arbi.assistant` | `query(question, options)`, `retrieve(messageExtId)` |
101
- | `arbi.tags` | `list()`, `create()`, `update()`, `delete()` |
102
- | `arbi.doctags` | `assign()`, `remove()` |
103
- | `arbi.contacts` | `list()`, `search()` |
104
- | `arbi.health` | `check()` |
78
+ // result.text full accumulated response
79
+ // result.assistantMessageExtId — use as previousResponseId for follow-ups
80
+ ```
105
81
 
106
- ### Browser Entry Point
82
+ ## Session recovery
107
83
 
108
- For browser environments (no Node.js APIs like `fs`), import from the browser entry:
84
+ The SDK persists the signing key (encrypted) in IndexedDB. Recover a session without a password:
109
85
 
110
86
  ```typescript
111
- import { Arbi } from '@arbidocs/sdk/browser'
87
+ await arbi.loginWithKey('user@example.com', signingKeyBase64)
112
88
  ```
113
89
 
90
+ The SDK also auto-recovers on 401 responses using the stored key.
91
+
114
92
  ## Security
115
93
 
116
- - **Zero-knowledge auth**: Passwords never leave the client. Ed25519 keys are derived locally via Argon2id.
117
- - **E2E workspace encryption**: Each workspace has a symmetric key, wrapped with your public key.
118
- - **Encrypted key storage**: Private keys in IndexedDB are encrypted with a non-extractable Web Crypto AES-GCM key.
119
- - **Key zeroing on logout**: Signing and session keys are scrubbed from memory on logout.
94
+ - **Zero-knowledge auth** passwords never leave the client
95
+ - **E2E workspace encryption** symmetric keys wrapped with your public key
96
+ - **Encrypted key storage** private keys in IndexedDB encrypted with non-extractable AES-GCM
97
+ - **Key zeroing on logout** signing and session keys scrubbed from memory
120
98
 
121
- ## Examples
99
+ ## React
122
100
 
123
- - **[Node.js Hello World](../../examples/node-hello-world/)**Login, list docs, ask a streaming question (~40 lines)
124
- - **[React Chat App](../../examples/react-chat/)** — Login form, workspace selector, document picker, streaming chat
101
+ For React apps, use `@arbidocs/react` instead it wraps this SDK with hooks and a context provider. See the [@arbidocs/react README](../arbi-react/README.md).
125
102
 
126
103
  ## Requirements
127
104
 
128
105
  - **Browser**: Any modern browser with Web Crypto API
129
- - **Node.js**: v18+ (needs `fetch`, `FormData`, `Blob` globals). Install `fake-indexeddb`.
106
+ - **Node.js**: v18+ (install `fake-indexeddb`)
130
107
  - **TypeScript**: 5.0+ (optional)
@@ -114,6 +114,10 @@ interface ConfigStore {
114
114
  saveChatSession(session: ChatSession): void;
115
115
  updateChatSession(updates: Partial<ChatSession>): void;
116
116
  clearChatSession(): void;
117
+ /** Persist last response metadata for citation browsing (optional). */
118
+ saveLastMetadata?(metadata: unknown): void;
119
+ /** Load last response metadata for citation browsing (optional). */
120
+ loadLastMetadata?(): unknown | null;
117
121
  }
118
122
 
119
123
  /**
@@ -218,7 +222,8 @@ type AgentStepEvent = components['schemas']['AgentStepEvent'];
218
222
  type UserInputRequestEvent = components['schemas']['UserInputRequestEvent'];
219
223
  type ArtifactEvent = components['schemas']['ArtifactEvent'];
220
224
  type UserMessageEvent = components['schemas']['UserMessageEvent'];
221
- type MessageMetadataPayload = components['schemas']['MessageMetadataPayload'];
225
+ type MessageQueuedEvent = components['schemas']['MessageQueuedEvent'];
226
+ type MessageMetadataPayload$1 = components['schemas']['MessageMetadataPayload'];
222
227
  type ResponseUsage = components['schemas']['ResponseUsage'];
223
228
  type OutputTokensDetails = components['schemas']['OutputTokensDetails'];
224
229
  type TokenBudgetContext = components['schemas']['TokenBudgetContext'];
@@ -273,8 +278,9 @@ interface SSEStreamCallbacks {
273
278
  onContentPartAdded?: (data: ResponseContentPartAddedEvent) => void;
274
279
  onAgentStep?: (data: AgentStepEvent) => void;
275
280
  onError?: (message: string) => void;
281
+ onMessageQueued?: (data: MessageQueuedEvent) => void;
276
282
  onUserMessage?: (data: UserMessageEvent) => void;
277
- onMetadata?: (data: MessageMetadataPayload) => void;
283
+ onMetadata?: (data: MessageMetadataPayload$1) => void;
278
284
  onUserInputRequest?: (data: UserInputRequestEvent) => void;
279
285
  onArtifact?: (data: ArtifactEvent) => void;
280
286
  onElapsedTime?: (t: number) => void;
@@ -292,7 +298,7 @@ interface SSEStreamResult {
292
298
  toolCallCount: number;
293
299
  errors: string[];
294
300
  userMessage: UserMessageEvent | null;
295
- metadata: MessageMetadataPayload | null;
301
+ metadata: MessageMetadataPayload$1 | null;
296
302
  artifacts: ArtifactEvent[];
297
303
  usage: ResponseUsage | null;
298
304
  /** Token budget context snapshot from response.completed. */
@@ -308,6 +314,15 @@ interface SSEStreamResult {
308
314
  * both the streaming callbacks and the final result.
309
315
  */
310
316
  declare function streamSSE(response: Response, callbacks?: SSEStreamCallbacks): Promise<SSEStreamResult>;
317
+ /**
318
+ * Format a human-readable summary line from a completed SSE stream result.
319
+ *
320
+ * Returns a string like:
321
+ * "3 steps (2 tool calls) · 1,234 tokens · 500/8,000 context · 2.5s"
322
+ *
323
+ * Returns empty string if there's nothing to report.
324
+ */
325
+ declare function formatStreamSummary(result: SSEStreamResult, elapsedTime?: number | null): string;
311
326
  /**
312
327
  * Consume an entire SSE stream without streaming callbacks.
313
328
  * Convenience alias for `streamSSE(response)`.
@@ -424,6 +439,66 @@ type UserInfo = {
424
439
  */
425
440
  declare function formatUserName(user: UserInfo | null | undefined): string;
426
441
 
442
+ /**
443
+ * Citation resolution utilities — shared by CLI and TUI.
444
+ *
445
+ * Parses citation data from SSE metadata (`MessageMetadataPayload`) and
446
+ * resolves chunk references into displayable citation summaries and passages.
447
+ *
448
+ * Citation data flow:
449
+ * 1. `tools.model_citations.tool_responses` maps citation number → CitationData
450
+ * (contains chunk_ids, statement, offsets)
451
+ * 2. `tools.retrieval_chunk.tool_responses` and `tools.retrieval_full_context.tool_responses`
452
+ * map document file name → Chunk[] (contains content and metadata)
453
+ * 3. This module joins those two data sets to produce resolved citations.
454
+ */
455
+
456
+ type MessageMetadataPayload = components['schemas']['MessageMetadataPayload'];
457
+ type CitationData$2 = components['schemas']['CitationData'];
458
+ type Chunk$1 = components['schemas']['Chunk'];
459
+ /** A fully resolved citation with its source chunks. */
460
+ interface ResolvedCitation {
461
+ /** Citation number as a string (e.g. "1", "2") */
462
+ citationNum: string;
463
+ /** Raw citation data from model_citations */
464
+ citationData: CitationData$2;
465
+ /** Resolved chunks matching the citation's chunk_ids */
466
+ chunks: Chunk$1[];
467
+ }
468
+ /** A compact citation summary for display in lists. */
469
+ interface CitationSummary {
470
+ citationNum: string;
471
+ statement: string;
472
+ docTitle: string;
473
+ pageNumber: number | null;
474
+ chunkCount: number;
475
+ }
476
+ /**
477
+ * Resolve all citations from SSE metadata into full citation objects.
478
+ *
479
+ * Reads `tools.model_citations.tool_responses` for citation→chunk_id mappings,
480
+ * then builds a chunk lookup from `retrieval_chunk` and `retrieval_full_context`
481
+ * tool responses to resolve each chunk_id to its full Chunk object.
482
+ *
483
+ * Returns citations sorted by citation number (ascending).
484
+ */
485
+ declare function resolveCitations(metadata: MessageMetadataPayload | null): ResolvedCitation[];
486
+ /**
487
+ * Produce compact summaries for display in citation lists.
488
+ * Uses the first chunk of each citation for doc title and page number.
489
+ */
490
+ declare function summarizeCitations(resolved: ResolvedCitation[]): CitationSummary[];
491
+ /**
492
+ * Count the number of citations in metadata.
493
+ * Returns 0 if metadata is null or has no citations.
494
+ */
495
+ declare function countCitations(metadata: MessageMetadataPayload | null): number;
496
+ /**
497
+ * Convert citation markdown to plain text with superscript-style numbers.
498
+ * Replaces `[text](#cite-N)` with `text[N]`.
499
+ */
500
+ declare function stripCitationMarkdown(text: string): string;
501
+
427
502
  /**
428
503
  * Document operations — browser-safe functions.
429
504
  *
@@ -907,7 +982,7 @@ interface QueryOptions {
907
982
  /** Called when a user message event is received */
908
983
  onUserMessage?: (data: UserMessageEvent) => void;
909
984
  /** Called when message metadata is received */
910
- onMetadata?: (data: MessageMetadataPayload) => void;
985
+ onMetadata?: (data: MessageMetadataPayload$1) => void;
911
986
  /** Called when the agent requests user input */
912
987
  onUserInputRequest?: (data: UserInputRequestEvent) => void;
913
988
  /** Called when an artifact event is received */
@@ -3231,4 +3306,4 @@ declare namespace responses {
3231
3306
  export { type responses_SubmitBackgroundQueryOptions as SubmitBackgroundQueryOptions, responses_extractResponseText as extractResponseText, responses_getResponse as getResponse, responses_submitBackgroundQuery as submitBackgroundQuery };
3232
3307
  }
3233
3308
 
3234
- export { connectWithReconnect as $, type AuthHeaders as A, type SSEStreamCallbacks as B, type ConfigStore as C, type DocumentWaiter as D, type SSEStreamResult as E, type FormattedWsMessage as F, type SSEStreamStartData as G, type UserInfo as H, type UserInputRequestEvent as I, type UserMessageEvent as J, type WsConnection as K, LIFECYCLE_LABELS as L, type MessageLevel as M, agentconfig as N, type OutputTokensDetails as O, assistant as P, type QueryOptions as Q, type ReconnectOptions as R, type SSEEvent as S, TOOL_LABELS as T, type UploadBatchResult as U, authenticatedFetch as V, type WorkspaceContext as W, buildRetrievalChunkTool as X, buildRetrievalFullContextTool as Y, buildRetrievalTocTool as Z, connectWebSocket as _, type CliConfig as a, consumeSSEStream as a0, contacts as a1, conversations as a2, createAuthenticatedClient as a3, createDocumentWaiter as a4, dm as a5, doctags as a6, documents as a7, files as a8, formatAgentStepLabel as a9, formatFileSize as aa, formatUserName as ab, formatWorkspaceChoices as ac, formatWsMessage as ad, generateEncryptedWorkspaceKey as ae, getErrorCode as af, getErrorMessage as ag, health as ah, parseSSEEvents as ai, performPasswordLogin as aj, requireData as ak, requireOk as al, resolveAuth as am, resolveWorkspace as an, responses as ao, selectWorkspace as ap, selectWorkspaceById as aq, settings as ar, streamSSE as as, tags as at, workspaces as au, type CliCredentials as b, type ChatSession as c, type UploadOptions as d, type UploadResult as e, type AgentStepEvent as f, Arbi as g, ArbiApiError as h, ArbiError as i, type ArbiOptions as j, type ArtifactEvent as k, type AuthContext as l, type AuthenticatedClient as m, type ConnectOptions as n, type DocumentWaiterOptions as o, type MessageMetadataPayload as p, type ReconnectableWsConnection as q, type ResponseCompletedEvent as r, type ResponseContentPartAddedEvent as s, type ResponseCreatedEvent as t, type ResponseFailedEvent as u, type ResponseOutputItemAddedEvent as v, type ResponseOutputItemDoneEvent as w, type ResponseOutputTextDeltaEvent as x, type ResponseOutputTextDoneEvent as y, type ResponseUsage as z };
3309
+ export { buildRetrievalFullContextTool as $, type AuthHeaders as A, type ResponseOutputTextDeltaEvent as B, type ConfigStore as C, type DocumentWaiter as D, type ResponseOutputTextDoneEvent as E, type FormattedWsMessage as F, type ResponseUsage as G, type SSEStreamCallbacks as H, type SSEStreamResult as I, type SSEStreamStartData as J, type UserInfo as K, LIFECYCLE_LABELS as L, type MessageLevel as M, type UserInputRequestEvent as N, type OutputTokensDetails as O, type UserMessageEvent as P, type QueryOptions as Q, type ReconnectOptions as R, type SSEEvent as S, TOOL_LABELS as T, type UploadBatchResult as U, type WsConnection as V, type WorkspaceContext as W, agentconfig as X, assistant as Y, authenticatedFetch as Z, buildRetrievalChunkTool as _, type CliConfig as a, buildRetrievalTocTool as a0, connectWebSocket as a1, connectWithReconnect as a2, consumeSSEStream as a3, contacts as a4, conversations as a5, countCitations as a6, createAuthenticatedClient as a7, createDocumentWaiter as a8, dm as a9, summarizeCitations as aA, tags as aB, workspaces as aC, doctags as aa, documents as ab, files as ac, formatAgentStepLabel as ad, formatFileSize as ae, formatStreamSummary as af, formatUserName as ag, formatWorkspaceChoices as ah, formatWsMessage as ai, generateEncryptedWorkspaceKey as aj, getErrorCode as ak, getErrorMessage as al, health as am, parseSSEEvents as an, performPasswordLogin as ao, requireData as ap, requireOk as aq, resolveAuth as ar, resolveCitations as as, resolveWorkspace as at, responses as au, selectWorkspace as av, selectWorkspaceById as aw, settings as ax, streamSSE as ay, stripCitationMarkdown as az, type CliCredentials as b, type ChatSession as c, type UploadOptions as d, type UploadResult as e, type AgentStepEvent as f, Arbi as g, ArbiApiError as h, ArbiError as i, type ArbiOptions as j, type ArtifactEvent as k, type AuthContext as l, type AuthenticatedClient as m, type CitationSummary as n, type ConnectOptions as o, type DocumentWaiterOptions as p, type MessageMetadataPayload$1 as q, type MessageQueuedEvent as r, type ReconnectableWsConnection as s, type ResolvedCitation as t, type ResponseCompletedEvent as u, type ResponseContentPartAddedEvent as v, type ResponseCreatedEvent as w, type ResponseFailedEvent as x, type ResponseOutputItemAddedEvent as y, type ResponseOutputItemDoneEvent as z };
@@ -114,6 +114,10 @@ interface ConfigStore {
114
114
  saveChatSession(session: ChatSession): void;
115
115
  updateChatSession(updates: Partial<ChatSession>): void;
116
116
  clearChatSession(): void;
117
+ /** Persist last response metadata for citation browsing (optional). */
118
+ saveLastMetadata?(metadata: unknown): void;
119
+ /** Load last response metadata for citation browsing (optional). */
120
+ loadLastMetadata?(): unknown | null;
117
121
  }
118
122
 
119
123
  /**
@@ -218,7 +222,8 @@ type AgentStepEvent = components['schemas']['AgentStepEvent'];
218
222
  type UserInputRequestEvent = components['schemas']['UserInputRequestEvent'];
219
223
  type ArtifactEvent = components['schemas']['ArtifactEvent'];
220
224
  type UserMessageEvent = components['schemas']['UserMessageEvent'];
221
- type MessageMetadataPayload = components['schemas']['MessageMetadataPayload'];
225
+ type MessageQueuedEvent = components['schemas']['MessageQueuedEvent'];
226
+ type MessageMetadataPayload$1 = components['schemas']['MessageMetadataPayload'];
222
227
  type ResponseUsage = components['schemas']['ResponseUsage'];
223
228
  type OutputTokensDetails = components['schemas']['OutputTokensDetails'];
224
229
  type TokenBudgetContext = components['schemas']['TokenBudgetContext'];
@@ -273,8 +278,9 @@ interface SSEStreamCallbacks {
273
278
  onContentPartAdded?: (data: ResponseContentPartAddedEvent) => void;
274
279
  onAgentStep?: (data: AgentStepEvent) => void;
275
280
  onError?: (message: string) => void;
281
+ onMessageQueued?: (data: MessageQueuedEvent) => void;
276
282
  onUserMessage?: (data: UserMessageEvent) => void;
277
- onMetadata?: (data: MessageMetadataPayload) => void;
283
+ onMetadata?: (data: MessageMetadataPayload$1) => void;
278
284
  onUserInputRequest?: (data: UserInputRequestEvent) => void;
279
285
  onArtifact?: (data: ArtifactEvent) => void;
280
286
  onElapsedTime?: (t: number) => void;
@@ -292,7 +298,7 @@ interface SSEStreamResult {
292
298
  toolCallCount: number;
293
299
  errors: string[];
294
300
  userMessage: UserMessageEvent | null;
295
- metadata: MessageMetadataPayload | null;
301
+ metadata: MessageMetadataPayload$1 | null;
296
302
  artifacts: ArtifactEvent[];
297
303
  usage: ResponseUsage | null;
298
304
  /** Token budget context snapshot from response.completed. */
@@ -308,6 +314,15 @@ interface SSEStreamResult {
308
314
  * both the streaming callbacks and the final result.
309
315
  */
310
316
  declare function streamSSE(response: Response, callbacks?: SSEStreamCallbacks): Promise<SSEStreamResult>;
317
+ /**
318
+ * Format a human-readable summary line from a completed SSE stream result.
319
+ *
320
+ * Returns a string like:
321
+ * "3 steps (2 tool calls) · 1,234 tokens · 500/8,000 context · 2.5s"
322
+ *
323
+ * Returns empty string if there's nothing to report.
324
+ */
325
+ declare function formatStreamSummary(result: SSEStreamResult, elapsedTime?: number | null): string;
311
326
  /**
312
327
  * Consume an entire SSE stream without streaming callbacks.
313
328
  * Convenience alias for `streamSSE(response)`.
@@ -424,6 +439,66 @@ type UserInfo = {
424
439
  */
425
440
  declare function formatUserName(user: UserInfo | null | undefined): string;
426
441
 
442
+ /**
443
+ * Citation resolution utilities — shared by CLI and TUI.
444
+ *
445
+ * Parses citation data from SSE metadata (`MessageMetadataPayload`) and
446
+ * resolves chunk references into displayable citation summaries and passages.
447
+ *
448
+ * Citation data flow:
449
+ * 1. `tools.model_citations.tool_responses` maps citation number → CitationData
450
+ * (contains chunk_ids, statement, offsets)
451
+ * 2. `tools.retrieval_chunk.tool_responses` and `tools.retrieval_full_context.tool_responses`
452
+ * map document file name → Chunk[] (contains content and metadata)
453
+ * 3. This module joins those two data sets to produce resolved citations.
454
+ */
455
+
456
+ type MessageMetadataPayload = components['schemas']['MessageMetadataPayload'];
457
+ type CitationData$2 = components['schemas']['CitationData'];
458
+ type Chunk$1 = components['schemas']['Chunk'];
459
+ /** A fully resolved citation with its source chunks. */
460
+ interface ResolvedCitation {
461
+ /** Citation number as a string (e.g. "1", "2") */
462
+ citationNum: string;
463
+ /** Raw citation data from model_citations */
464
+ citationData: CitationData$2;
465
+ /** Resolved chunks matching the citation's chunk_ids */
466
+ chunks: Chunk$1[];
467
+ }
468
+ /** A compact citation summary for display in lists. */
469
+ interface CitationSummary {
470
+ citationNum: string;
471
+ statement: string;
472
+ docTitle: string;
473
+ pageNumber: number | null;
474
+ chunkCount: number;
475
+ }
476
+ /**
477
+ * Resolve all citations from SSE metadata into full citation objects.
478
+ *
479
+ * Reads `tools.model_citations.tool_responses` for citation→chunk_id mappings,
480
+ * then builds a chunk lookup from `retrieval_chunk` and `retrieval_full_context`
481
+ * tool responses to resolve each chunk_id to its full Chunk object.
482
+ *
483
+ * Returns citations sorted by citation number (ascending).
484
+ */
485
+ declare function resolveCitations(metadata: MessageMetadataPayload | null): ResolvedCitation[];
486
+ /**
487
+ * Produce compact summaries for display in citation lists.
488
+ * Uses the first chunk of each citation for doc title and page number.
489
+ */
490
+ declare function summarizeCitations(resolved: ResolvedCitation[]): CitationSummary[];
491
+ /**
492
+ * Count the number of citations in metadata.
493
+ * Returns 0 if metadata is null or has no citations.
494
+ */
495
+ declare function countCitations(metadata: MessageMetadataPayload | null): number;
496
+ /**
497
+ * Convert citation markdown to plain text with superscript-style numbers.
498
+ * Replaces `[text](#cite-N)` with `text[N]`.
499
+ */
500
+ declare function stripCitationMarkdown(text: string): string;
501
+
427
502
  /**
428
503
  * Document operations — browser-safe functions.
429
504
  *
@@ -907,7 +982,7 @@ interface QueryOptions {
907
982
  /** Called when a user message event is received */
908
983
  onUserMessage?: (data: UserMessageEvent) => void;
909
984
  /** Called when message metadata is received */
910
- onMetadata?: (data: MessageMetadataPayload) => void;
985
+ onMetadata?: (data: MessageMetadataPayload$1) => void;
911
986
  /** Called when the agent requests user input */
912
987
  onUserInputRequest?: (data: UserInputRequestEvent) => void;
913
988
  /** Called when an artifact event is received */
@@ -3231,4 +3306,4 @@ declare namespace responses {
3231
3306
  export { type responses_SubmitBackgroundQueryOptions as SubmitBackgroundQueryOptions, responses_extractResponseText as extractResponseText, responses_getResponse as getResponse, responses_submitBackgroundQuery as submitBackgroundQuery };
3232
3307
  }
3233
3308
 
3234
- export { connectWithReconnect as $, type AuthHeaders as A, type SSEStreamCallbacks as B, type ConfigStore as C, type DocumentWaiter as D, type SSEStreamResult as E, type FormattedWsMessage as F, type SSEStreamStartData as G, type UserInfo as H, type UserInputRequestEvent as I, type UserMessageEvent as J, type WsConnection as K, LIFECYCLE_LABELS as L, type MessageLevel as M, agentconfig as N, type OutputTokensDetails as O, assistant as P, type QueryOptions as Q, type ReconnectOptions as R, type SSEEvent as S, TOOL_LABELS as T, type UploadBatchResult as U, authenticatedFetch as V, type WorkspaceContext as W, buildRetrievalChunkTool as X, buildRetrievalFullContextTool as Y, buildRetrievalTocTool as Z, connectWebSocket as _, type CliConfig as a, consumeSSEStream as a0, contacts as a1, conversations as a2, createAuthenticatedClient as a3, createDocumentWaiter as a4, dm as a5, doctags as a6, documents as a7, files as a8, formatAgentStepLabel as a9, formatFileSize as aa, formatUserName as ab, formatWorkspaceChoices as ac, formatWsMessage as ad, generateEncryptedWorkspaceKey as ae, getErrorCode as af, getErrorMessage as ag, health as ah, parseSSEEvents as ai, performPasswordLogin as aj, requireData as ak, requireOk as al, resolveAuth as am, resolveWorkspace as an, responses as ao, selectWorkspace as ap, selectWorkspaceById as aq, settings as ar, streamSSE as as, tags as at, workspaces as au, type CliCredentials as b, type ChatSession as c, type UploadOptions as d, type UploadResult as e, type AgentStepEvent as f, Arbi as g, ArbiApiError as h, ArbiError as i, type ArbiOptions as j, type ArtifactEvent as k, type AuthContext as l, type AuthenticatedClient as m, type ConnectOptions as n, type DocumentWaiterOptions as o, type MessageMetadataPayload as p, type ReconnectableWsConnection as q, type ResponseCompletedEvent as r, type ResponseContentPartAddedEvent as s, type ResponseCreatedEvent as t, type ResponseFailedEvent as u, type ResponseOutputItemAddedEvent as v, type ResponseOutputItemDoneEvent as w, type ResponseOutputTextDeltaEvent as x, type ResponseOutputTextDoneEvent as y, type ResponseUsage as z };
3309
+ export { buildRetrievalFullContextTool as $, type AuthHeaders as A, type ResponseOutputTextDeltaEvent as B, type ConfigStore as C, type DocumentWaiter as D, type ResponseOutputTextDoneEvent as E, type FormattedWsMessage as F, type ResponseUsage as G, type SSEStreamCallbacks as H, type SSEStreamResult as I, type SSEStreamStartData as J, type UserInfo as K, LIFECYCLE_LABELS as L, type MessageLevel as M, type UserInputRequestEvent as N, type OutputTokensDetails as O, type UserMessageEvent as P, type QueryOptions as Q, type ReconnectOptions as R, type SSEEvent as S, TOOL_LABELS as T, type UploadBatchResult as U, type WsConnection as V, type WorkspaceContext as W, agentconfig as X, assistant as Y, authenticatedFetch as Z, buildRetrievalChunkTool as _, type CliConfig as a, buildRetrievalTocTool as a0, connectWebSocket as a1, connectWithReconnect as a2, consumeSSEStream as a3, contacts as a4, conversations as a5, countCitations as a6, createAuthenticatedClient as a7, createDocumentWaiter as a8, dm as a9, summarizeCitations as aA, tags as aB, workspaces as aC, doctags as aa, documents as ab, files as ac, formatAgentStepLabel as ad, formatFileSize as ae, formatStreamSummary as af, formatUserName as ag, formatWorkspaceChoices as ah, formatWsMessage as ai, generateEncryptedWorkspaceKey as aj, getErrorCode as ak, getErrorMessage as al, health as am, parseSSEEvents as an, performPasswordLogin as ao, requireData as ap, requireOk as aq, resolveAuth as ar, resolveCitations as as, resolveWorkspace as at, responses as au, selectWorkspace as av, selectWorkspaceById as aw, settings as ax, streamSSE as ay, stripCitationMarkdown as az, type CliCredentials as b, type ChatSession as c, type UploadOptions as d, type UploadResult as e, type AgentStepEvent as f, Arbi as g, ArbiApiError as h, ArbiError as i, type ArbiOptions as j, type ArtifactEvent as k, type AuthContext as l, type AuthenticatedClient as m, type CitationSummary as n, type ConnectOptions as o, type DocumentWaiterOptions as p, type MessageMetadataPayload$1 as q, type MessageQueuedEvent as r, type ReconnectableWsConnection as s, type ResolvedCitation as t, type ResponseCompletedEvent as u, type ResponseContentPartAddedEvent as v, type ResponseCreatedEvent as w, type ResponseFailedEvent as x, type ResponseOutputItemAddedEvent as y, type ResponseOutputItemDoneEvent as z };
package/dist/browser.cjs CHANGED
@@ -389,6 +389,14 @@ async function streamSSE(response, callbacks = {}) {
389
389
  callbacks.onError?.(message);
390
390
  },
391
391
  // ARBI-specific events (dot-prefixed from server)
392
+ "arbi.message_queued": (raw) => {
393
+ const data = JSON.parse(raw);
394
+ if (data.user_message) {
395
+ userMessage = data.user_message;
396
+ callbacks.onUserMessage?.(data.user_message);
397
+ }
398
+ callbacks.onMessageQueued?.(data);
399
+ },
392
400
  "arbi.agent_step": (raw) => {
393
401
  const data = JSON.parse(raw);
394
402
  const label = formatAgentStepLabel(data);
@@ -574,6 +582,66 @@ function formatUserName(user) {
574
582
  return [user.given_name, user.family_name].filter(Boolean).join(" ");
575
583
  }
576
584
 
585
+ // src/citations.ts
586
+ function resolveCitations(metadata) {
587
+ if (!metadata?.tools) return [];
588
+ const tools = metadata.tools;
589
+ const modelCitations = tools.model_citations;
590
+ const citationMap = modelCitations?.tool_responses;
591
+ if (!citationMap || Object.keys(citationMap).length === 0) return [];
592
+ const chunkLookup = buildChunkLookup(tools);
593
+ const resolved = [];
594
+ for (const [citationNum, citationData] of Object.entries(citationMap)) {
595
+ const chunks = [];
596
+ for (const chunkId of citationData.chunk_ids ?? []) {
597
+ const chunk = chunkLookup.get(chunkId);
598
+ if (chunk) chunks.push(chunk);
599
+ }
600
+ resolved.push({ citationNum, citationData, chunks });
601
+ }
602
+ resolved.sort((a, b) => Number(a.citationNum) - Number(b.citationNum));
603
+ return resolved;
604
+ }
605
+ function summarizeCitations(resolved) {
606
+ return resolved.map((r) => {
607
+ const firstChunk = r.chunks[0];
608
+ return {
609
+ citationNum: r.citationNum,
610
+ statement: r.citationData.statement ?? "",
611
+ docTitle: firstChunk?.metadata?.doc_title ?? "Unknown document",
612
+ pageNumber: firstChunk?.metadata?.page_number ?? null,
613
+ chunkCount: r.chunks.length
614
+ };
615
+ });
616
+ }
617
+ function countCitations(metadata) {
618
+ if (!metadata?.tools) return 0;
619
+ const tools = metadata.tools;
620
+ const modelCitations = tools.model_citations;
621
+ const responses = modelCitations?.tool_responses;
622
+ return responses ? Object.keys(responses).length : 0;
623
+ }
624
+ function stripCitationMarkdown(text) {
625
+ return text.replace(/\[([^\]]+)\]\(#cite-(\d+)\)/g, "$1[$2]");
626
+ }
627
+ function buildChunkLookup(tools) {
628
+ const lookup = /* @__PURE__ */ new Map();
629
+ for (const toolName of ["retrieval_chunk", "retrieval_full_context"]) {
630
+ const tool = tools[toolName];
631
+ if (!tool?.tool_responses) continue;
632
+ for (const chunks of Object.values(tool.tool_responses)) {
633
+ if (!Array.isArray(chunks)) continue;
634
+ for (const chunk of chunks) {
635
+ const id = chunk.metadata?.chunk_ext_id;
636
+ if (id && !lookup.has(id)) {
637
+ lookup.set(id, chunk);
638
+ }
639
+ }
640
+ }
641
+ }
642
+ return lookup;
643
+ }
644
+
577
645
  // src/operations/documents.ts
578
646
  var documents_exports = {};
579
647
  __export(documents_exports, {
@@ -1271,6 +1339,19 @@ var Arbi = class {
1271
1339
  lr.serverSessionKey,
1272
1340
  signingPrivateKeyBase64
1273
1341
  );
1342
+ const workspaceKeyHeader = client.session.getWorkspaceKeyHeader();
1343
+ if (workspaceKeyHeader) {
1344
+ const { data: openResult, error: openError } = await client.fetch.POST(
1345
+ "/v1/workspace/{workspace_ext_id}/open",
1346
+ {
1347
+ params: { path: { workspace_ext_id: ws.external_id } },
1348
+ body: { workspace_key: workspaceKeyHeader }
1349
+ }
1350
+ );
1351
+ if (!openError && openResult?.access_token) {
1352
+ client.session.setAccessToken(openResult.access_token);
1353
+ }
1354
+ }
1274
1355
  this.currentWorkspaceId = ws.external_id;
1275
1356
  }
1276
1357
  /** Log out and clear internal state. */
@@ -1571,6 +1652,7 @@ exports.connectWithReconnect = connectWithReconnect;
1571
1652
  exports.consumeSSEStream = consumeSSEStream;
1572
1653
  exports.contacts = contacts_exports;
1573
1654
  exports.conversations = conversations_exports;
1655
+ exports.countCitations = countCitations;
1574
1656
  exports.createAuthenticatedClient = createAuthenticatedClient;
1575
1657
  exports.dm = dm_exports;
1576
1658
  exports.doctags = doctags_exports;
@@ -1587,12 +1669,15 @@ exports.performPasswordLogin = performPasswordLogin;
1587
1669
  exports.requireData = requireData;
1588
1670
  exports.requireOk = requireOk;
1589
1671
  exports.resolveAuth = resolveAuth;
1672
+ exports.resolveCitations = resolveCitations;
1590
1673
  exports.resolveWorkspace = resolveWorkspace;
1591
1674
  exports.responses = responses_exports;
1592
1675
  exports.selectWorkspace = selectWorkspace;
1593
1676
  exports.selectWorkspaceById = selectWorkspaceById;
1594
1677
  exports.settings = settings_exports;
1595
1678
  exports.streamSSE = streamSSE;
1679
+ exports.stripCitationMarkdown = stripCitationMarkdown;
1680
+ exports.summarizeCitations = summarizeCitations;
1596
1681
  exports.tags = tags_exports;
1597
1682
  exports.workspaces = workspaces_exports;
1598
1683
  //# sourceMappingURL=browser.cjs.map