experimental-a2 0.8.0 → 0.8.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 (94) hide show
  1. package/AGENTS.md +11 -0
  2. package/CHANGELOG.md +8 -0
  3. package/README.md +29 -0
  4. package/docs/guides/07-examples.mdx +56 -0
  5. package/docs/index.mdx +13 -0
  6. package/examples/README.md +15 -0
  7. package/examples/playground/AGENTS.md +11 -0
  8. package/examples/playground/DEPLOY.md +106 -0
  9. package/examples/playground/README.md +19 -0
  10. package/examples/playground/activity-feed.test.ts +10 -0
  11. package/examples/playground/app/agent/[agentId]/agent-client.tsx +376 -0
  12. package/examples/playground/app/agent/[agentId]/page.tsx +29 -0
  13. package/examples/playground/app/agent/events/route.ts +4 -0
  14. package/examples/playground/app/agent/model.ts +3 -0
  15. package/examples/playground/app/agent/new-agent-session.tsx +98 -0
  16. package/examples/playground/app/agent/page.tsx +25 -0
  17. package/examples/playground/app/agent/scheduler/route.ts +5 -0
  18. package/examples/playground/app/agent/server.ts +153 -0
  19. package/examples/playground/app/agent/session.ts +13 -0
  20. package/examples/playground/app/canvas/[canvasId]/canvas-client.tsx +682 -0
  21. package/examples/playground/app/canvas/[canvasId]/canvas-replay.test.ts +68 -0
  22. package/examples/playground/app/canvas/[canvasId]/canvas-replay.ts +19 -0
  23. package/examples/playground/app/canvas/[canvasId]/page.tsx +22 -0
  24. package/examples/playground/app/canvas/[canvasId]/session.ts +19 -0
  25. package/examples/playground/app/canvas/events/route.ts +13 -0
  26. package/examples/playground/app/canvas/model.ts +94 -0
  27. package/examples/playground/app/canvas/open-canvas.tsx +40 -0
  28. package/examples/playground/app/canvas/page.tsx +20 -0
  29. package/examples/playground/app/canvas/server.ts +9 -0
  30. package/examples/playground/app/chat/[chatId]/agent-stream-drawer.test.tsx +118 -0
  31. package/examples/playground/app/chat/[chatId]/agent-stream-drawer.tsx +316 -0
  32. package/examples/playground/app/chat/[chatId]/chat-client.tsx +922 -0
  33. package/examples/playground/app/chat/[chatId]/chat-view.test.ts +152 -0
  34. package/examples/playground/app/chat/[chatId]/chat-view.ts +101 -0
  35. package/examples/playground/app/chat/[chatId]/composer.test.ts +44 -0
  36. package/examples/playground/app/chat/[chatId]/composer.ts +30 -0
  37. package/examples/playground/app/chat/[chatId]/page.tsx +30 -0
  38. package/examples/playground/app/chat/[chatId]/session.ts +7 -0
  39. package/examples/playground/app/chat/events/route.ts +7 -0
  40. package/examples/playground/app/chat/model.test.ts +155 -0
  41. package/examples/playground/app/chat/model.ts +310 -0
  42. package/examples/playground/app/chat/new-conversation.tsx +16 -0
  43. package/examples/playground/app/chat/page.tsx +25 -0
  44. package/examples/playground/app/chat/scheduler/route.ts +5 -0
  45. package/examples/playground/app/chat/server.ts +184 -0
  46. package/examples/playground/app/components/activity-feed.tsx +54 -0
  47. package/examples/playground/app/components/connection-pill.tsx +29 -0
  48. package/examples/playground/app/counter/counter-client.tsx +72 -0
  49. package/examples/playground/app/counter/events/route.ts +4 -0
  50. package/examples/playground/app/counter/model.test.ts +36 -0
  51. package/examples/playground/app/counter/model.ts +31 -0
  52. package/examples/playground/app/counter/page.tsx +24 -0
  53. package/examples/playground/app/counter/server.ts +9 -0
  54. package/examples/playground/app/counter/session.ts +13 -0
  55. package/examples/playground/app/documents/[documentId]/code-editor.tsx +80 -0
  56. package/examples/playground/app/documents/[documentId]/document-client.tsx +525 -0
  57. package/examples/playground/app/documents/[documentId]/page.tsx +23 -0
  58. package/examples/playground/app/documents/[documentId]/session.ts +7 -0
  59. package/examples/playground/app/documents/events/route.ts +4 -0
  60. package/examples/playground/app/documents/model.ts +55 -0
  61. package/examples/playground/app/documents/open-document.tsx +40 -0
  62. package/examples/playground/app/documents/page.tsx +22 -0
  63. package/examples/playground/app/documents/server.ts +9 -0
  64. package/examples/playground/app/globals.css +2078 -0
  65. package/examples/playground/app/layout.tsx +44 -0
  66. package/examples/playground/app/orders/[orderId]/order-client.tsx +140 -0
  67. package/examples/playground/app/orders/[orderId]/page.tsx +29 -0
  68. package/examples/playground/app/orders/[orderId]/session.ts +11 -0
  69. package/examples/playground/app/orders/create/route.ts +30 -0
  70. package/examples/playground/app/orders/events/route.ts +4 -0
  71. package/examples/playground/app/orders/model.ts +79 -0
  72. package/examples/playground/app/orders/new-order-form.tsx +98 -0
  73. package/examples/playground/app/orders/page.tsx +22 -0
  74. package/examples/playground/app/orders/scheduler/route.ts +5 -0
  75. package/examples/playground/app/orders/server.ts +50 -0
  76. package/examples/playground/app/page.tsx +102 -0
  77. package/examples/playground/app/recovery/[recoveryId]/page.tsx +31 -0
  78. package/examples/playground/app/recovery/[recoveryId]/recovery-client.tsx +144 -0
  79. package/examples/playground/app/recovery/[recoveryId]/session.ts +7 -0
  80. package/examples/playground/app/recovery/events/route.ts +3 -0
  81. package/examples/playground/app/recovery/model.ts +55 -0
  82. package/examples/playground/app/recovery/new-recovery-session.tsx +20 -0
  83. package/examples/playground/app/recovery/page.tsx +22 -0
  84. package/examples/playground/app/recovery/scheduler/route.ts +7 -0
  85. package/examples/playground/app/recovery/server.ts +53 -0
  86. package/examples/playground/app/recovery/start/route.ts +41 -0
  87. package/examples/playground/css.d.ts +4 -0
  88. package/examples/playground/lib/store.ts +15 -0
  89. package/examples/playground/next-env.d.ts +5 -0
  90. package/examples/playground/next.config.ts +10 -0
  91. package/examples/playground/package.json +46 -0
  92. package/examples/playground/tsconfig.json +37 -0
  93. package/examples/playground/vercel.json +40 -0
  94. package/package.json +5 -2
@@ -0,0 +1,316 @@
1
+ 'use client'
2
+
3
+ import type { ReactNode } from 'react'
4
+ import { useEffect, useRef, useState } from 'react'
5
+ import type { ChatAgentMessage, ChatAgentStep } from '../model'
6
+ import type { AgentStreamSelection } from './chat-view'
7
+
8
+ const json = (value: unknown): string =>
9
+ JSON.stringify(value, null, 2) ?? String(value)
10
+
11
+ const toolStateLabel = (state: string): string => {
12
+ switch (state) {
13
+ case 'input-streaming':
14
+ return 'streaming input'
15
+ case 'input-available':
16
+ return 'running'
17
+ case 'approval-requested':
18
+ return 'awaiting approval'
19
+ case 'approval-responded':
20
+ return 'approval received'
21
+ case 'output-available':
22
+ return 'completed'
23
+ case 'output-error':
24
+ return 'failed'
25
+ case 'output-denied':
26
+ return 'denied'
27
+ default:
28
+ return state
29
+ }
30
+ }
31
+
32
+ export type AgentStreamStatus =
33
+ 'streaming' | 'completed' | 'failed' | 'interrupted'
34
+
35
+ function StreamPart({
36
+ part,
37
+ position,
38
+ }: {
39
+ part: ChatAgentMessage['parts'][number]
40
+ position: number
41
+ }): ReactNode {
42
+ if (part.type === 'step-start') {
43
+ return (
44
+ <div className="agent-stream-step" role="separator">
45
+ <span>Model step</span>
46
+ </div>
47
+ )
48
+ }
49
+
50
+ if (part.type === 'text' || part.type === 'reasoning') {
51
+ return (
52
+ <section className={`agent-stream-part ${part.type}`}>
53
+ <header>
54
+ <span>{part.type === 'reasoning' ? 'Reasoning' : 'Model text'}</span>
55
+ <span className="agent-stream-part-state">
56
+ {part.state ?? 'done'} · not posted
57
+ </span>
58
+ </header>
59
+ {part.text ? (
60
+ <p>{part.text}</p>
61
+ ) : (
62
+ <p className="hint">
63
+ {part.state === 'streaming'
64
+ ? 'Waiting for content…'
65
+ : 'No text was emitted.'}
66
+ </p>
67
+ )}
68
+ {part.providerMetadata === undefined ? null : (
69
+ <details>
70
+ <summary>Provider metadata</summary>
71
+ <pre>{json(part.providerMetadata)}</pre>
72
+ </details>
73
+ )}
74
+ </section>
75
+ )
76
+ }
77
+
78
+ if (part.type === 'tool-post') {
79
+ const output = 'output' in part ? part.output : undefined
80
+ const errorText = 'errorText' in part ? part.errorText : undefined
81
+ return (
82
+ <section className="agent-stream-part tool">
83
+ <header>
84
+ <span>
85
+ Tool <code>post</code>
86
+ </span>
87
+ <span className="agent-stream-part-state">
88
+ {toolStateLabel(part.state)}
89
+ </span>
90
+ </header>
91
+ <div className="agent-stream-value">
92
+ <span>Input</span>
93
+ <pre>{json(part.input)}</pre>
94
+ </div>
95
+ {output === undefined ? null : (
96
+ <div className="agent-stream-value">
97
+ <span>Result</span>
98
+ <pre>{json(output)}</pre>
99
+ </div>
100
+ )}
101
+ {errorText === undefined ? null : <p className="error">{errorText}</p>}
102
+ </section>
103
+ )
104
+ }
105
+
106
+ if (part.type === 'source-url') {
107
+ return (
108
+ <section className="agent-stream-part source">
109
+ <header>
110
+ <span>Source</span>
111
+ </header>
112
+ <a href={part.url} target="_blank" rel="noreferrer">
113
+ {part.title ?? part.url}
114
+ </a>
115
+ </section>
116
+ )
117
+ }
118
+
119
+ if (part.type === 'source-document') {
120
+ return (
121
+ <section className="agent-stream-part source">
122
+ <header>
123
+ <span>Document source</span>
124
+ </header>
125
+ <p>{part.title}</p>
126
+ <code>{part.filename ?? part.mediaType}</code>
127
+ </section>
128
+ )
129
+ }
130
+
131
+ if (part.type === 'file' || part.type === 'reasoning-file') {
132
+ return (
133
+ <section className="agent-stream-part source">
134
+ <header>
135
+ <span>{part.type === 'file' ? 'File' : 'Reasoning file'}</span>
136
+ </header>
137
+ <a href={part.url} target="_blank" rel="noreferrer">
138
+ {'filename' in part && part.filename ? part.filename : part.mediaType}
139
+ </a>
140
+ </section>
141
+ )
142
+ }
143
+
144
+ return (
145
+ <section className="agent-stream-part extended">
146
+ <header>
147
+ <span>{part.type}</span>
148
+ <span className="agent-stream-part-state">part {position + 1}</span>
149
+ </header>
150
+ <pre>{json(part)}</pre>
151
+ </section>
152
+ )
153
+ }
154
+
155
+ function StepMetadata({ step }: { step: ChatAgentStep }): ReactNode {
156
+ return (
157
+ <article className="agent-stream-generation">
158
+ <header>
159
+ <code>{step.model}</code>
160
+ <span>{step.finishReason}</span>
161
+ </header>
162
+ <dl>
163
+ <dt>Generation</dt>
164
+ <dd>
165
+ <code>{step.generationId}</code>
166
+ </dd>
167
+ <dt>Completed</dt>
168
+ <dd>{new Date(step.completedAt).toLocaleString()}</dd>
169
+ <dt>Tokens</dt>
170
+ <dd>{(step.usage.totalTokens ?? 0).toLocaleString()}</dd>
171
+ </dl>
172
+ <details>
173
+ <summary>Full AI SDK usage</summary>
174
+ <pre>{json(step.usage)}</pre>
175
+ </details>
176
+ </article>
177
+ )
178
+ }
179
+
180
+ export function AgentStreamDrawer({
181
+ responseMessageId,
182
+ selection,
183
+ summary,
184
+ status,
185
+ onClose,
186
+ }: {
187
+ responseMessageId: string | null
188
+ selection: AgentStreamSelection | null
189
+ summary: string | null
190
+ status: AgentStreamStatus
191
+ onClose: () => void
192
+ }): ReactNode {
193
+ const dialogRef = useRef<HTMLDialogElement>(null)
194
+ const [rawOpen, setRawOpen] = useState(false)
195
+
196
+ useEffect(() => {
197
+ const dialog = dialogRef.current
198
+ if (!dialog) return
199
+ if (responseMessageId && !dialog.open) dialog.showModal()
200
+ if (!responseMessageId && dialog.open) dialog.close()
201
+ }, [responseMessageId])
202
+
203
+ useEffect(() => setRawOpen(false), [responseMessageId])
204
+
205
+ return (
206
+ <dialog
207
+ id="agent-stream-drawer"
208
+ ref={dialogRef}
209
+ className="agent-stream-drawer"
210
+ aria-labelledby="agent-stream-title"
211
+ onCancel={(event) => {
212
+ event.preventDefault()
213
+ onClose()
214
+ }}
215
+ onKeyDown={(event) => {
216
+ if (event.key === 'Escape') {
217
+ event.preventDefault()
218
+ onClose()
219
+ }
220
+ }}
221
+ onClose={onClose}
222
+ onClick={(event) => {
223
+ if (event.target === event.currentTarget) onClose()
224
+ }}
225
+ >
226
+ <div className="agent-stream-drawer-shell">
227
+ <header className="agent-stream-drawer-header">
228
+ <div>
229
+ <p className="eyebrow">Private model output</p>
230
+ <h2 id="agent-stream-title">Agent stream</h2>
231
+ <p>{summary ?? 'Waiting for stream metadata…'}</p>
232
+ </div>
233
+ <button
234
+ type="button"
235
+ className="secondary compact agent-stream-close"
236
+ aria-label="Close agent stream"
237
+ autoFocus
238
+ onClick={onClose}
239
+ >
240
+ Close
241
+ </button>
242
+ </header>
243
+
244
+ <div className="agent-stream-drawer-body">
245
+ <div className="agent-stream-identity">
246
+ <span
247
+ className={`badge${
248
+ status === 'streaming'
249
+ ? ' live'
250
+ : status === 'failed' || status === 'interrupted'
251
+ ? ' cut'
252
+ : ' done'
253
+ }`}
254
+ >
255
+ {status}
256
+ </span>
257
+ {responseMessageId ? <code>{responseMessageId}</code> : null}
258
+ </div>
259
+
260
+ {selection === null ? (
261
+ <p className="agent-stream-empty">The stream is starting…</p>
262
+ ) : (
263
+ <>
264
+ <section className="agent-stream-section">
265
+ <h3>Ordered UI message parts</h3>
266
+ <p className="hint">
267
+ This is the complete projected stream. Only successful{' '}
268
+ <code>post</code> calls appear in the room.
269
+ </p>
270
+ <div className="agent-stream-parts">
271
+ {selection.parts.length === 0 ? (
272
+ <p className="agent-stream-empty">
273
+ No stream parts have arrived yet.
274
+ </p>
275
+ ) : (
276
+ selection.parts.map((part, position) => (
277
+ <StreamPart
278
+ key={`${responseMessageId ?? 'stream'}:${position}`}
279
+ part={part}
280
+ position={position}
281
+ />
282
+ ))
283
+ )}
284
+ </div>
285
+ </section>
286
+
287
+ <section className="agent-stream-section">
288
+ <h3>Generation metadata</h3>
289
+ {selection.metadata?.steps.length ? (
290
+ <div className="agent-stream-generations">
291
+ {selection.metadata.steps.map((step) => (
292
+ <StepMetadata key={step.generationId} step={step} />
293
+ ))}
294
+ </div>
295
+ ) : (
296
+ <p className="agent-stream-empty">
297
+ No model step has completed yet.
298
+ </p>
299
+ )}
300
+ </section>
301
+
302
+ <details
303
+ className="agent-stream-raw"
304
+ open={rawOpen}
305
+ onToggle={(event) => setRawOpen(event.currentTarget.open)}
306
+ >
307
+ <summary>Raw extended UIMessage</summary>
308
+ {rawOpen ? <pre>{json(selection.message)}</pre> : null}
309
+ </details>
310
+ </>
311
+ )}
312
+ </div>
313
+ </div>
314
+ </dialog>
315
+ )
316
+ }