@octavus/docs 0.0.1

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 (62) hide show
  1. package/LICENSE +22 -0
  2. package/content/01-getting-started/01-introduction.md +93 -0
  3. package/content/01-getting-started/02-quickstart.md +268 -0
  4. package/content/01-getting-started/_meta.md +4 -0
  5. package/content/02-server-sdk/01-overview.md +133 -0
  6. package/content/02-server-sdk/02-sessions.md +164 -0
  7. package/content/02-server-sdk/03-tools.md +242 -0
  8. package/content/02-server-sdk/04-streaming.md +211 -0
  9. package/content/02-server-sdk/_meta.md +4 -0
  10. package/content/03-client-sdk/01-overview.md +148 -0
  11. package/content/03-client-sdk/02-messages.md +207 -0
  12. package/content/03-client-sdk/03-streaming.md +215 -0
  13. package/content/03-client-sdk/04-execution-blocks.md +231 -0
  14. package/content/03-client-sdk/_meta.md +4 -0
  15. package/content/04-protocol/01-overview.md +142 -0
  16. package/content/04-protocol/02-input-resources.md +200 -0
  17. package/content/04-protocol/03-triggers.md +209 -0
  18. package/content/04-protocol/04-tools.md +255 -0
  19. package/content/04-protocol/05-handlers.md +251 -0
  20. package/content/04-protocol/06-agent-config.md +215 -0
  21. package/content/04-protocol/_meta.md +4 -0
  22. package/content/05-api-reference/01-overview.md +129 -0
  23. package/content/05-api-reference/02-sessions.md +232 -0
  24. package/content/05-api-reference/03-agents.md +222 -0
  25. package/content/05-api-reference/_meta.md +4 -0
  26. package/dist/chunk-2YMRODFE.js +421 -0
  27. package/dist/chunk-2YMRODFE.js.map +1 -0
  28. package/dist/chunk-6JQ3OMGF.js +421 -0
  29. package/dist/chunk-6JQ3OMGF.js.map +1 -0
  30. package/dist/chunk-ESGSYVGK.js +421 -0
  31. package/dist/chunk-ESGSYVGK.js.map +1 -0
  32. package/dist/chunk-GDCTM2SV.js +421 -0
  33. package/dist/chunk-GDCTM2SV.js.map +1 -0
  34. package/dist/chunk-J26MLMLN.js +421 -0
  35. package/dist/chunk-J26MLMLN.js.map +1 -0
  36. package/dist/chunk-LWYMRXBF.js +421 -0
  37. package/dist/chunk-LWYMRXBF.js.map +1 -0
  38. package/dist/chunk-NFVJQNDP.js +421 -0
  39. package/dist/chunk-NFVJQNDP.js.map +1 -0
  40. package/dist/chunk-SCKIOGKI.js +421 -0
  41. package/dist/chunk-SCKIOGKI.js.map +1 -0
  42. package/dist/chunk-VWPQ6ORV.js +421 -0
  43. package/dist/chunk-VWPQ6ORV.js.map +1 -0
  44. package/dist/chunk-WUNFFJ32.js +421 -0
  45. package/dist/chunk-WUNFFJ32.js.map +1 -0
  46. package/dist/chunk-XVSMRXBJ.js +421 -0
  47. package/dist/chunk-XVSMRXBJ.js.map +1 -0
  48. package/dist/content.d.ts +37 -0
  49. package/dist/content.js +17 -0
  50. package/dist/content.js.map +1 -0
  51. package/dist/docs.json +173 -0
  52. package/dist/index.d.ts +2 -0
  53. package/dist/index.js +11 -0
  54. package/dist/index.js.map +1 -0
  55. package/dist/search-index.json +1 -0
  56. package/dist/search.d.ts +19 -0
  57. package/dist/search.js +30 -0
  58. package/dist/search.js.map +1 -0
  59. package/dist/sections.json +213 -0
  60. package/dist/types-BNRNBPDE.d.ts +28 -0
  61. package/dist/types-BltYGlWI.d.ts +36 -0
  62. package/package.json +52 -0
@@ -0,0 +1,207 @@
1
+ ---
2
+ title: Messages
3
+ description: Working with message state in the Client SDK.
4
+ ---
5
+
6
+ # Messages
7
+
8
+ Messages represent the conversation history. The Client SDK tracks messages automatically and provides structured access to their content.
9
+
10
+ ## Message Structure
11
+
12
+ ```typescript
13
+ interface Message {
14
+ id: string;
15
+ role: 'user' | 'assistant' | 'system';
16
+ content: string;
17
+ parts?: MessagePart[];
18
+ toolCalls?: ToolCallWithDescription[];
19
+ thinking?: string;
20
+ visible?: boolean;
21
+ createdAt: Date;
22
+ }
23
+ ```
24
+
25
+ ### Message Parts
26
+
27
+ For rich content display, use the `parts` array which preserves content ordering:
28
+
29
+ ```typescript
30
+ interface MessagePart {
31
+ type: 'text' | 'thinking' | 'tool-call';
32
+ visible: boolean;
33
+ content?: string; // For text and thinking
34
+ toolCall?: ToolCallInfo; // For tool-call
35
+ thread?: string; // For non-main thread content
36
+ }
37
+ ```
38
+
39
+ ## Adding User Messages
40
+
41
+ ```tsx
42
+ const { addUserMessage, triggerAction } = useOctavusChat({...});
43
+
44
+ async function sendMessage(text: string) {
45
+ // 1. Add message to UI immediately
46
+ addUserMessage(text);
47
+
48
+ // 2. Trigger the agent
49
+ await triggerAction('user-message', { USER_MESSAGE: text });
50
+ }
51
+ ```
52
+
53
+ **Important**: `addUserMessage` only updates UI state. You must call `triggerAction` to actually send the message to the agent.
54
+
55
+ ## Rendering Messages
56
+
57
+ ### Basic Rendering
58
+
59
+ ```tsx
60
+ function MessageList({ messages }: { messages: Message[] }) {
61
+ return (
62
+ <div className="space-y-4">
63
+ {messages.map((msg) => (
64
+ <div
65
+ key={msg.id}
66
+ className={msg.role === 'user' ? 'text-right' : 'text-left'}
67
+ >
68
+ <div className="inline-block p-3 rounded-lg">
69
+ {msg.content}
70
+ </div>
71
+ </div>
72
+ ))}
73
+ </div>
74
+ );
75
+ }
76
+ ```
77
+
78
+ ### Rich Rendering with Parts
79
+
80
+ For messages with tool calls and thinking, render parts in order:
81
+
82
+ ```tsx
83
+ function MessageContent({ message }: { message: Message }) {
84
+ if (!message.parts) {
85
+ return <p>{message.content}</p>;
86
+ }
87
+
88
+ return (
89
+ <div className="space-y-2">
90
+ {message.parts.map((part, i) => {
91
+ if (!part.visible) return null;
92
+
93
+ switch (part.type) {
94
+ case 'text':
95
+ return <p key={i}>{part.content}</p>;
96
+
97
+ case 'thinking':
98
+ return (
99
+ <details key={i} className="text-gray-500">
100
+ <summary>Thinking...</summary>
101
+ <pre className="text-sm">{part.content}</pre>
102
+ </details>
103
+ );
104
+
105
+ case 'tool-call':
106
+ return (
107
+ <div key={i} className="bg-gray-100 p-2 rounded text-sm">
108
+ 🔧 {part.toolCall?.name}
109
+ {part.toolCall?.status === 'completed' && ' ✓'}
110
+ </div>
111
+ );
112
+
113
+ default:
114
+ return null;
115
+ }
116
+ })}
117
+ </div>
118
+ );
119
+ }
120
+ ```
121
+
122
+ ## Tool Calls in Messages
123
+
124
+ Tool calls include status, arguments, and results:
125
+
126
+ ```typescript
127
+ interface ToolCallInfo {
128
+ id: string;
129
+ name: string;
130
+ description?: string;
131
+ arguments: Record<string, unknown>;
132
+ status: 'pending' | 'in_progress' | 'completed' | 'error';
133
+ result?: unknown;
134
+ error?: string;
135
+ }
136
+ ```
137
+
138
+ ### Rendering Tool Calls
139
+
140
+ ```tsx
141
+ function ToolCallCard({ toolCall }: { toolCall: ToolCallInfo }) {
142
+ return (
143
+ <div className="border rounded p-3">
144
+ <div className="flex items-center gap-2">
145
+ <span className="text-lg">🔧</span>
146
+ <span className="font-medium">{toolCall.description || toolCall.name}</span>
147
+ <StatusBadge status={toolCall.status} />
148
+ </div>
149
+
150
+ {toolCall.status === 'completed' && toolCall.result && (
151
+ <pre className="mt-2 text-xs bg-gray-50 p-2 rounded">
152
+ {JSON.stringify(toolCall.result, null, 2)}
153
+ </pre>
154
+ )}
155
+
156
+ {toolCall.status === 'error' && (
157
+ <p className="mt-2 text-red-500 text-sm">{toolCall.error}</p>
158
+ )}
159
+ </div>
160
+ );
161
+ }
162
+ ```
163
+
164
+ ## Session Restore
165
+
166
+ When restoring a session, pass existing messages:
167
+
168
+ ```tsx
169
+ // Fetch session state from your backend
170
+ const sessionState = await fetchSession(sessionId);
171
+
172
+ // Convert ChatMessage[] to Message[]
173
+ const initialMessages = sessionState.messages
174
+ .filter(msg => msg.visible !== false)
175
+ .map(msg => ({
176
+ id: msg.id,
177
+ role: msg.role,
178
+ content: msg.content,
179
+ parts: msg.parts,
180
+ toolCalls: msg.toolCalls,
181
+ thinking: msg.reasoning,
182
+ createdAt: new Date(msg.createdAt),
183
+ }));
184
+
185
+ // Pass to hook
186
+ const { messages } = useOctavusChat({
187
+ initialMessages,
188
+ onTrigger: ...
189
+ });
190
+ ```
191
+
192
+ ## Message Callbacks
193
+
194
+ Get notified when messages are added:
195
+
196
+ ```tsx
197
+ useOctavusChat({
198
+ onTrigger: ...,
199
+ onMessage: (message) => {
200
+ console.log('New message:', message.role, message.content);
201
+
202
+ // Analytics, logging, etc.
203
+ trackMessage(message);
204
+ },
205
+ });
206
+ ```
207
+
@@ -0,0 +1,215 @@
1
+ ---
2
+ title: Streaming
3
+ description: Building streaming UIs with the Client SDK.
4
+ ---
5
+
6
+ # Streaming
7
+
8
+ The Client SDK provides real-time access to streaming content, enabling responsive UIs that update as the agent generates responses.
9
+
10
+ ## Streaming State
11
+
12
+ ```tsx
13
+ const {
14
+ streamingText, // Current visible text being streamed
15
+ streamingParts, // Structured parts being streamed
16
+ thinkingText, // Current thinking content
17
+ status, // 'idle' | 'loading' | 'streaming' | 'error'
18
+ isLoading, // true during loading or streaming
19
+ } = useOctavusChat({...});
20
+ ```
21
+
22
+ ## Basic Streaming UI
23
+
24
+ ```tsx
25
+ function Chat() {
26
+ const { messages, streamingText, isLoading } = useOctavusChat({...});
27
+
28
+ return (
29
+ <div>
30
+ {/* Completed messages */}
31
+ {messages.map((msg) => (
32
+ <div key={msg.id}>{msg.content}</div>
33
+ ))}
34
+
35
+ {/* Currently streaming */}
36
+ {streamingText && (
37
+ <div className="animate-pulse">
38
+ {streamingText}
39
+ <span className="inline-block w-2 h-4 bg-gray-400 ml-1" />
40
+ </div>
41
+ )}
42
+ </div>
43
+ );
44
+ }
45
+ ```
46
+
47
+ ## Streaming Parts
48
+
49
+ For rich streaming UIs, use `streamingParts`:
50
+
51
+ ```tsx
52
+ function StreamingContent() {
53
+ const { streamingParts } = useOctavusChat({...});
54
+
55
+ return (
56
+ <div>
57
+ {streamingParts.map((part, i) => {
58
+ switch (part.type) {
59
+ case 'text':
60
+ return <span key={i}>{part.content}</span>;
61
+
62
+ case 'thinking':
63
+ return (
64
+ <div key={i} className="text-gray-500 italic">
65
+ 💭 {part.content}
66
+ </div>
67
+ );
68
+
69
+ case 'tool-call':
70
+ return (
71
+ <div key={i} className="flex items-center gap-2">
72
+ <Spinner />
73
+ <span>{part.toolCall?.description}</span>
74
+ </div>
75
+ );
76
+ }
77
+ })}
78
+ </div>
79
+ );
80
+ }
81
+ ```
82
+
83
+ ## Thinking Indicator
84
+
85
+ Show when the model is "thinking" (extended reasoning):
86
+
87
+ ```tsx
88
+ function ThinkingIndicator() {
89
+ const { thinkingText, status } = useOctavusChat({...});
90
+
91
+ if (!thinkingText || status !== 'streaming') {
92
+ return null;
93
+ }
94
+
95
+ return (
96
+ <div className="bg-purple-50 p-3 rounded-lg">
97
+ <div className="flex items-center gap-2 text-purple-600">
98
+ <Brain className="w-4 h-4 animate-pulse" />
99
+ <span className="font-medium">Thinking...</span>
100
+ </div>
101
+ <p className="mt-2 text-sm text-gray-600 line-clamp-3">
102
+ {thinkingText}
103
+ </p>
104
+ </div>
105
+ );
106
+ }
107
+ ```
108
+
109
+ ## Status Indicator
110
+
111
+ ```tsx
112
+ function StatusIndicator() {
113
+ const { status } = useOctavusChat({...});
114
+
115
+ switch (status) {
116
+ case 'idle':
117
+ return null;
118
+ case 'loading':
119
+ return <div>Starting...</div>;
120
+ case 'streaming':
121
+ return <div>Agent is responding...</div>;
122
+ case 'error':
123
+ return <div className="text-red-500">Something went wrong</div>;
124
+ }
125
+ }
126
+ ```
127
+
128
+ ## Handling Stream Completion
129
+
130
+ ```tsx
131
+ useOctavusChat({
132
+ onTrigger: ...,
133
+ onDone: () => {
134
+ console.log('Stream completed');
135
+ // Scroll to bottom, play sound, etc.
136
+ },
137
+ onError: (error) => {
138
+ console.error('Stream error:', error);
139
+ toast.error('Failed to get response');
140
+ },
141
+ });
142
+ ```
143
+
144
+ ## Streaming with Tool Calls
145
+
146
+ When tools are called, streaming continues after tool execution:
147
+
148
+ ```
149
+ 1. User sends message
150
+ 2. streamingText starts filling: "Let me look that up..."
151
+ 3. Tool call starts (visible in streamingParts)
152
+ 4. Tool executes (handled by server-sdk)
153
+ 5. streamingText continues: "I found your account..."
154
+ 6. Stream completes, message finalized
155
+ ```
156
+
157
+ ```tsx
158
+ function ChatWithTools() {
159
+ const { messages, streamingText, streamingParts } = useOctavusChat({...});
160
+
161
+ // Find active tool call
162
+ const activeToolCall = streamingParts.find(
163
+ p => p.type === 'tool-call' && p.toolCall?.status === 'in_progress'
164
+ );
165
+
166
+ return (
167
+ <div>
168
+ {messages.map(msg => <Message key={msg.id} message={msg} />)}
169
+
170
+ {/* Show streaming text */}
171
+ {streamingText && <p>{streamingText}</p>}
172
+
173
+ {/* Show active tool */}
174
+ {activeToolCall && (
175
+ <div className="flex items-center gap-2 text-blue-600">
176
+ <Spinner />
177
+ {activeToolCall.toolCall?.description}
178
+ </div>
179
+ )}
180
+ </div>
181
+ );
182
+ }
183
+ ```
184
+
185
+ ## Non-Main Thread Content
186
+
187
+ Named threads (like summarization) stream separately:
188
+
189
+ ```tsx
190
+ function StreamingContent() {
191
+ const { streamingParts } = useOctavusChat({...});
192
+
193
+ // Group by thread
194
+ const mainParts = streamingParts.filter(p => !p.thread || p.thread === 'main');
195
+ const otherParts = streamingParts.filter(p => p.thread && p.thread !== 'main');
196
+
197
+ return (
198
+ <div>
199
+ {/* Main conversation */}
200
+ <div>{mainParts.map(renderPart)}</div>
201
+
202
+ {/* Named thread (e.g., summarization) */}
203
+ {otherParts.length > 0 && (
204
+ <div className="bg-orange-50 p-3 rounded mt-4">
205
+ <div className="text-orange-600 font-medium">
206
+ Processing in background...
207
+ </div>
208
+ {otherParts.map(renderPart)}
209
+ </div>
210
+ )}
211
+ </div>
212
+ );
213
+ }
214
+ ```
215
+
@@ -0,0 +1,231 @@
1
+ ---
2
+ title: Execution Blocks
3
+ description: Tracking agent execution progress with the Client SDK.
4
+ ---
5
+
6
+ # Execution Blocks
7
+
8
+ Execution blocks let you show users what the agent is doing. Each block represents a step in the agent's execution flow.
9
+
10
+ ## Block Structure
11
+
12
+ ```typescript
13
+ interface ExecutionBlock {
14
+ id: string;
15
+ name: string; // Human-readable name from protocol
16
+ type: string; // Block type (next-message, tool-call, etc.)
17
+ status: BlockStatus; // pending | running | completed | error
18
+ display: DisplayMode; // hidden | name | description | stream
19
+ description?: string;
20
+ outputToChat: boolean; // Whether output goes to main chat
21
+ thread?: string; // For named threads
22
+ streamingText: string; // Current streaming content
23
+ thinking?: string; // Extended reasoning content
24
+ toolCalls: ToolCallWithDescription[];
25
+ startedAt: Date;
26
+ completedAt?: Date;
27
+ }
28
+ ```
29
+
30
+ ## Accessing Blocks
31
+
32
+ ```tsx
33
+ const { executionBlocks, onBlockStart, onBlockEnd } = useOctavusChat({
34
+ onTrigger: ...,
35
+ onBlockStart: (block) => {
36
+ console.log('Block started:', block.name);
37
+ },
38
+ onBlockEnd: (block) => {
39
+ console.log('Block completed:', block.name, block.status);
40
+ },
41
+ });
42
+ ```
43
+
44
+ ## Showing Execution Progress
45
+
46
+ ```tsx
47
+ function ExecutionProgress() {
48
+ const { executionBlocks, status } = useOctavusChat({...});
49
+
50
+ if (status !== 'streaming' || executionBlocks.length === 0) {
51
+ return null;
52
+ }
53
+
54
+ return (
55
+ <div className="space-y-2">
56
+ {executionBlocks.map((block) => (
57
+ <ExecutionBlockCard key={block.id} block={block} />
58
+ ))}
59
+ </div>
60
+ );
61
+ }
62
+
63
+ function ExecutionBlockCard({ block }: { block: ExecutionBlock }) {
64
+ return (
65
+ <div className="border rounded p-3">
66
+ <div className="flex items-center gap-2">
67
+ <BlockStatusIcon status={block.status} />
68
+ <span className="font-medium">{block.name}</span>
69
+ </div>
70
+
71
+ {block.description && (
72
+ <p className="text-sm text-gray-500 mt-1">{block.description}</p>
73
+ )}
74
+
75
+ {/* Show streaming content for stream display mode */}
76
+ {block.display === 'stream' && block.streamingText && (
77
+ <div className="mt-2 text-sm">
78
+ {block.streamingText}
79
+ </div>
80
+ )}
81
+
82
+ {/* Show tool calls */}
83
+ {block.toolCalls.length > 0 && (
84
+ <div className="mt-2 space-y-1">
85
+ {block.toolCalls.map((tc) => (
86
+ <div key={tc.id} className="text-sm flex items-center gap-1">
87
+ 🔧 {tc.description || tc.name}
88
+ {tc.status === 'completed' && ' ✓'}
89
+ {tc.status === 'error' && ' ✗'}
90
+ </div>
91
+ ))}
92
+ </div>
93
+ )}
94
+ </div>
95
+ );
96
+ }
97
+ ```
98
+
99
+ ## Display Modes
100
+
101
+ Blocks have different display modes that control visibility:
102
+
103
+ | Mode | Description | UI Recommendation |
104
+ |------|-------------|-------------------|
105
+ | `hidden` | Not shown to user | Don't render |
106
+ | `name` | Shows block name | Show name only |
107
+ | `description` | Shows description | Show name + description |
108
+ | `stream` | Streams content | Show full streaming content |
109
+
110
+ ```tsx
111
+ function ExecutionBlockCard({ block }: { block: ExecutionBlock }) {
112
+ // Hidden blocks aren't sent to client, but check just in case
113
+ if (block.display === 'hidden') {
114
+ return null;
115
+ }
116
+
117
+ return (
118
+ <div className="border rounded p-3">
119
+ {/* Always show name */}
120
+ <div className="font-medium">{block.name}</div>
121
+
122
+ {/* Show description if display mode is description or stream */}
123
+ {(block.display === 'description' || block.display === 'stream') &&
124
+ block.description && (
125
+ <p className="text-sm text-gray-500">{block.description}</p>
126
+ )}
127
+
128
+ {/* Show content if display mode is stream */}
129
+ {block.display === 'stream' && (
130
+ <div className="mt-2">
131
+ {block.streamingText}
132
+ </div>
133
+ )}
134
+ </div>
135
+ );
136
+ }
137
+ ```
138
+
139
+ ## Named Threads
140
+
141
+ Blocks can belong to named threads (like "summary"). Use the `thread` property:
142
+
143
+ ```tsx
144
+ function ExecutionProgress() {
145
+ const { executionBlocks } = useOctavusChat({...});
146
+
147
+ // Group by thread
148
+ const mainBlocks = executionBlocks.filter(b => !b.thread || b.thread === 'main');
149
+ const namedThreads = new Map<string, ExecutionBlock[]>();
150
+
151
+ executionBlocks.forEach(b => {
152
+ if (b.thread && b.thread !== 'main') {
153
+ if (!namedThreads.has(b.thread)) {
154
+ namedThreads.set(b.thread, []);
155
+ }
156
+ namedThreads.get(b.thread)!.push(b);
157
+ }
158
+ });
159
+
160
+ return (
161
+ <div>
162
+ {/* Main execution */}
163
+ <div className="space-y-2">
164
+ {mainBlocks.map(b => <BlockCard key={b.id} block={b} />)}
165
+ </div>
166
+
167
+ {/* Named threads */}
168
+ {Array.from(namedThreads.entries()).map(([thread, blocks]) => (
169
+ <div key={thread} className="mt-4 bg-orange-50 p-3 rounded">
170
+ <div className="text-orange-600 font-medium mb-2">
171
+ Thread: {thread}
172
+ </div>
173
+ {blocks.map(b => <BlockCard key={b.id} block={b} />)}
174
+ </div>
175
+ ))}
176
+ </div>
177
+ );
178
+ }
179
+ ```
180
+
181
+ ## Block Status Icons
182
+
183
+ ```tsx
184
+ function BlockStatusIcon({ status }: { status: BlockStatus }) {
185
+ switch (status) {
186
+ case 'pending':
187
+ return <Circle className="w-4 h-4 text-gray-400" />;
188
+ case 'running':
189
+ return <Loader className="w-4 h-4 text-blue-500 animate-spin" />;
190
+ case 'completed':
191
+ return <CheckCircle className="w-4 h-4 text-green-500" />;
192
+ case 'error':
193
+ return <XCircle className="w-4 h-4 text-red-500" />;
194
+ }
195
+ }
196
+ ```
197
+
198
+ ## Example: Support Chat with Escalation
199
+
200
+ When a user clicks "Talk to Human", the agent runs multiple blocks:
201
+
202
+ ```tsx
203
+ function SupportChatProgress() {
204
+ const { executionBlocks } = useOctavusChat({...});
205
+
206
+ // Show blocks while streaming
207
+ return (
208
+ <div className="space-y-2">
209
+ {executionBlocks.map((block) => (
210
+ <div key={block.id} className="flex items-center gap-2">
211
+ <BlockStatusIcon status={block.status} />
212
+ <span>{getBlockLabel(block)}</span>
213
+ </div>
214
+ ))}
215
+ </div>
216
+ );
217
+ }
218
+
219
+ function getBlockLabel(block: ExecutionBlock): string {
220
+ // Use description if available, otherwise name
221
+ return block.description || block.name;
222
+ }
223
+
224
+ // Example output during escalation:
225
+ // ✓ Serialize conversation
226
+ // ✓ Start summary thread
227
+ // ● Summarizing your conversation (streaming...)
228
+ // ○ Creating a support ticket
229
+ // ○ Respond with ticket info
230
+ ```
231
+
@@ -0,0 +1,4 @@
1
+ ---
2
+ title: Client SDK
3
+ description: Frontend integration with @octavus/client-sdk for React applications.
4
+ ---