@tanstack/ai 0.61.0 → 0.63.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.
- package/README.md +1 -0
- package/dist/esm/activities/chat/agents/define-agent.d.ts +17 -5
- package/dist/esm/activities/chat/agents/define-agent.js.map +1 -1
- package/dist/esm/activities/chat/agents/spawn.d.ts +2 -0
- package/dist/esm/activities/chat/agents/spawn.js +8 -5
- package/dist/esm/activities/chat/agents/spawn.js.map +1 -1
- package/dist/esm/activities/chat/index.js +156 -57
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.js +30 -18
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +1 -0
- package/dist/esm/activities/chat/middleware/types.js.map +1 -1
- package/dist/esm/activities/chat/stream/message-updaters.d.ts +2 -2
- package/dist/esm/activities/chat/stream/message-updaters.js +2 -1
- package/dist/esm/activities/chat/stream/message-updaters.js.map +1 -1
- package/dist/esm/activities/chat/stream/processor.d.ts +11 -9
- package/dist/esm/activities/chat/stream/processor.js +38 -18
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-calls.d.ts +16 -2
- package/dist/esm/activities/chat/tools/tool-calls.js +55 -16
- package/dist/esm/activities/chat/tools/tool-calls.js.map +1 -1
- package/dist/esm/activities/chat/tools/tool-definition.d.ts +4 -0
- package/dist/esm/activities/chat/tools/tool-definition.js +4 -0
- package/dist/esm/activities/chat/tools/tool-definition.js.map +1 -1
- package/dist/esm/activities/evaluate/adapter.d.ts +4 -0
- package/dist/esm/activities/evaluate/adapter.js.map +1 -1
- package/dist/esm/activities/evaluate/index.d.ts +4 -0
- package/dist/esm/activities/evaluate/index.js +3 -1
- package/dist/esm/activities/evaluate/index.js.map +1 -1
- package/dist/esm/client.d.ts +1 -1
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/index.d.ts +1 -1
- package/dist/esm/index.js +2 -2
- package/dist/esm/interrupt-resume.js +29 -4
- package/dist/esm/interrupt-resume.js.map +1 -1
- package/dist/esm/middlewares/otel.d.ts +5 -2
- package/dist/esm/middlewares/otel.js +114 -0
- package/dist/esm/middlewares/otel.js.map +1 -1
- package/dist/esm/types.d.ts +42 -1
- package/dist/esm/utilities/ag-ui-wire.js +5 -3
- package/dist/esm/utilities/ag-ui-wire.js.map +1 -1
- package/dist/esm/utilities/tool-result.d.ts +2 -1
- package/dist/esm/utilities/tool-result.js +4 -1
- package/dist/esm/utilities/tool-result.js.map +1 -1
- package/package.json +3 -3
- package/skills/ai-core/chat-experience/SKILL.md +120 -0
- package/skills/ai-core/tool-calling/SKILL.md +103 -0
- package/src/activities/chat/agents/define-agent.ts +20 -3
- package/src/activities/chat/agents/spawn.ts +20 -12
- package/src/activities/chat/index.ts +235 -62
- package/src/activities/chat/messages.ts +40 -5
- package/src/activities/chat/middleware/types.ts +1 -0
- package/src/activities/chat/stream/message-updaters.ts +3 -0
- package/src/activities/chat/stream/processor.ts +57 -27
- package/src/activities/chat/tools/tool-calls.ts +98 -9
- package/src/activities/chat/tools/tool-definition.ts +8 -0
- package/src/activities/evaluate/adapter.ts +4 -0
- package/src/activities/evaluate/index.ts +6 -0
- package/src/client.ts +1 -0
- package/src/index.ts +1 -0
- package/src/interrupt-resume.ts +55 -4
- package/src/middlewares/otel.ts +161 -3
- package/src/types.ts +38 -1
- package/src/utilities/ag-ui-wire.ts +12 -1
- package/src/utilities/tool-result.ts +7 -1
package/src/middlewares/otel.ts
CHANGED
|
@@ -108,7 +108,9 @@ export interface OtelMiddlewareOptions {
|
|
|
108
108
|
meter?: Meter
|
|
109
109
|
/**
|
|
110
110
|
* When `true`, prompt and completion content is attached to iteration spans
|
|
111
|
-
* as `gen_ai.*.message` / `gen_ai.choice` events.
|
|
111
|
+
* as `gen_ai.*.message` / `gen_ai.choice` events. Media spans get the
|
|
112
|
+
* prompt, input media and output (URLs or transcript text) as
|
|
113
|
+
* `gen_ai.input.messages` / `gen_ai.output.messages`. Defaults to `false` so
|
|
112
114
|
* that PII never lands on a span by accident.
|
|
113
115
|
*/
|
|
114
116
|
captureContent?: boolean
|
|
@@ -121,7 +123,8 @@ export interface OtelMiddlewareOptions {
|
|
|
121
123
|
redact?: (text: string) => string
|
|
122
124
|
/**
|
|
123
125
|
* Maximum characters kept in the per-iteration assistant text buffer used
|
|
124
|
-
* to emit `gen_ai.choice` events
|
|
126
|
+
* to emit `gen_ai.choice` events, and in each text part of a media span's
|
|
127
|
+
* input and output. Extra characters are truncated with a
|
|
125
128
|
* trailing `"…"` marker. Defaults to 100 000. Set to `0` to disable the
|
|
126
129
|
* cap. Exporters typically truncate long attribute values anyway.
|
|
127
130
|
*/
|
|
@@ -221,6 +224,111 @@ function serializeContent(content: unknown): string {
|
|
|
221
224
|
return parts.join(' ')
|
|
222
225
|
}
|
|
223
226
|
|
|
227
|
+
type InputPart =
|
|
228
|
+
| { type: 'text'; content: string }
|
|
229
|
+
| { type: 'uri'; modality: string; uri: string; mime_type?: string }
|
|
230
|
+
| { type: 'file'; modality: string; file_id: string; mime_type?: string }
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Structured form of `ContentPart[]` for `gen_ai.input.messages`, using the
|
|
234
|
+
* OTel GenAI semconv part shapes. URL and file-handle media keep their
|
|
235
|
+
* reference; inline bytes (and `data:` URLs) stay a `[type]` placeholder so
|
|
236
|
+
* they never blow attribute size limits. `redact` runs on text parts only.
|
|
237
|
+
*/
|
|
238
|
+
function serializeParts(
|
|
239
|
+
content: Array<unknown>,
|
|
240
|
+
redact: (text: string) => string,
|
|
241
|
+
): Array<InputPart> {
|
|
242
|
+
const parts: Array<InputPart> = []
|
|
243
|
+
for (const part of content) {
|
|
244
|
+
if (!part || typeof part !== 'object') continue
|
|
245
|
+
const p = part as {
|
|
246
|
+
type?: string
|
|
247
|
+
text?: string
|
|
248
|
+
content?: string
|
|
249
|
+
source?: { type?: string; value?: string; mimeType?: string }
|
|
250
|
+
}
|
|
251
|
+
if (p.type === 'text') {
|
|
252
|
+
parts.push({
|
|
253
|
+
type: 'text',
|
|
254
|
+
content: redact((p.text ?? p.content ?? '').toString()),
|
|
255
|
+
})
|
|
256
|
+
continue
|
|
257
|
+
}
|
|
258
|
+
const modality = p.type ?? 'unknown'
|
|
259
|
+
const { type, value, mimeType } = p.source ?? {}
|
|
260
|
+
const mime = mimeType ? { mime_type: mimeType } : {}
|
|
261
|
+
if (type === 'url' && value && !value.startsWith('data:')) {
|
|
262
|
+
parts.push({ type: 'uri', modality, uri: value, ...mime })
|
|
263
|
+
} else if (type === 'file' && value) {
|
|
264
|
+
parts.push({ type: 'file', modality, file_id: value, ...mime })
|
|
265
|
+
} else {
|
|
266
|
+
parts.push({ type: 'text', content: `[${modality}]` })
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
return parts
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/** A media reference as a content part, so `serializeParts` can map it. */
|
|
273
|
+
function mediaPart(modality: string, url: unknown): unknown {
|
|
274
|
+
return {
|
|
275
|
+
type: modality,
|
|
276
|
+
source: typeof url === 'string' ? { type: 'url', value: url } : undefined,
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
/**
|
|
281
|
+
* Content parts for a media call's inputs, read from `artifactInputs`: the
|
|
282
|
+
* prompt (a string or `MediaPrompt` parts), TTS `text`, and transcription
|
|
283
|
+
* `audio`. Audio is a reference only when it is an http(s) URL; a base64
|
|
284
|
+
* string, `File` or `Blob` becomes a placeholder.
|
|
285
|
+
*/
|
|
286
|
+
function mediaInputParts(inputs: unknown): Array<unknown> {
|
|
287
|
+
if (!inputs || typeof inputs !== 'object') return []
|
|
288
|
+
const { prompt, text, audio } = inputs as Record<string, unknown>
|
|
289
|
+
const parts: Array<unknown> = []
|
|
290
|
+
for (const value of [prompt, text]) {
|
|
291
|
+
if (typeof value === 'string') parts.push({ type: 'text', content: value })
|
|
292
|
+
else if (Array.isArray(value)) parts.push(...value)
|
|
293
|
+
}
|
|
294
|
+
if (audio !== undefined) {
|
|
295
|
+
const url =
|
|
296
|
+
typeof audio === 'string' && /^https?:\/\//.test(audio) ? audio : null
|
|
297
|
+
parts.push(mediaPart('audio', url))
|
|
298
|
+
}
|
|
299
|
+
return parts
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
/**
|
|
303
|
+
* Content parts for a media call's result: generated image, audio or video
|
|
304
|
+
* URLs, and transcript text. Base64 output becomes a placeholder.
|
|
305
|
+
*/
|
|
306
|
+
function mediaOutputParts(
|
|
307
|
+
activity: GenerationActivity,
|
|
308
|
+
result: unknown,
|
|
309
|
+
): Array<unknown> {
|
|
310
|
+
if (!result || typeof result !== 'object') return []
|
|
311
|
+
const r = result as Record<string, unknown>
|
|
312
|
+
const parts: Array<unknown> = []
|
|
313
|
+
if (Array.isArray(r.images)) {
|
|
314
|
+
for (const image of r.images) {
|
|
315
|
+
parts.push(mediaPart('image', (image as { url?: unknown } | null)?.url))
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
// `TTSResult.audio` is a base64 string; `AudioGenerationResult.audio` is a
|
|
319
|
+
// `{ url } | { b64Json }` source.
|
|
320
|
+
if (r.audio !== undefined) {
|
|
321
|
+
parts.push(mediaPart('audio', (r.audio as { url?: unknown } | null)?.url))
|
|
322
|
+
}
|
|
323
|
+
// Only video puts its asset on a top-level `url`. A world `url` is a viewer
|
|
324
|
+
// page, not media.
|
|
325
|
+
if (activity === 'video' && typeof r.url === 'string') {
|
|
326
|
+
parts.push(mediaPart('video', r.url))
|
|
327
|
+
}
|
|
328
|
+
if (typeof r.text === 'string') parts.push({ type: 'text', content: r.text })
|
|
329
|
+
return parts
|
|
330
|
+
}
|
|
331
|
+
|
|
224
332
|
function messageEventName(role: string): string {
|
|
225
333
|
switch (role) {
|
|
226
334
|
case 'user':
|
|
@@ -340,6 +448,29 @@ export function otelMiddleware(
|
|
|
340
448
|
})
|
|
341
449
|
}
|
|
342
450
|
|
|
451
|
+
// Media prompts and transcripts are single strings, so cap each text part
|
|
452
|
+
// with `maxContentLength` the same way the chat completion buffer is capped.
|
|
453
|
+
const redactMediaText = (text: string): string =>
|
|
454
|
+
redactContent(
|
|
455
|
+
maxContentLength > 0 && text.length > maxContentLength
|
|
456
|
+
? text.slice(0, maxContentLength) + '…'
|
|
457
|
+
: text,
|
|
458
|
+
)
|
|
459
|
+
|
|
460
|
+
const setMediaMessages = (
|
|
461
|
+
span: Span,
|
|
462
|
+
direction: 'input' | 'output',
|
|
463
|
+
parts: Array<unknown>,
|
|
464
|
+
): void => {
|
|
465
|
+
const content = serializeParts(parts, redactMediaText)
|
|
466
|
+
if (content.length === 0) return
|
|
467
|
+
const json = JSON.stringify([
|
|
468
|
+
{ role: direction === 'input' ? 'user' : 'assistant', content },
|
|
469
|
+
])
|
|
470
|
+
span.setAttribute(`gen_ai.${direction}.messages`, json)
|
|
471
|
+
span.setAttribute(`langfuse.observation.${direction}`, json)
|
|
472
|
+
}
|
|
473
|
+
|
|
343
474
|
const startMediaSpan = (ctx: GenerationMiddlewareContext): void => {
|
|
344
475
|
safeCall('otel.onStart', () => {
|
|
345
476
|
const operationName = OPERATION_NAME[ctx.activity]
|
|
@@ -365,6 +496,22 @@ export function otelMiddleware(
|
|
|
365
496
|
)
|
|
366
497
|
if (enriched) span.setAttributes(enriched)
|
|
367
498
|
mediaSpans.set(ctx, span)
|
|
499
|
+
|
|
500
|
+
if (captureContent) {
|
|
501
|
+
setMediaMessages(span, 'input', mediaInputParts(ctx.artifactInputs))
|
|
502
|
+
// Observe the result through a transform: the terminal hooks never
|
|
503
|
+
// see it. Returns `undefined`, so the result is left unchanged.
|
|
504
|
+
ctx.resultTransforms.push((result) => {
|
|
505
|
+
safeCall('otel.captureOutput', () =>
|
|
506
|
+
setMediaMessages(
|
|
507
|
+
span,
|
|
508
|
+
'output',
|
|
509
|
+
mediaOutputParts(ctx.activity, result),
|
|
510
|
+
),
|
|
511
|
+
)
|
|
512
|
+
return undefined
|
|
513
|
+
})
|
|
514
|
+
}
|
|
368
515
|
})
|
|
369
516
|
}
|
|
370
517
|
|
|
@@ -581,7 +728,12 @@ export function otelMiddleware(
|
|
|
581
728
|
// Also emit the current GenAI-semconv attribute form
|
|
582
729
|
// (`gen_ai.input.messages`) — backends like PostHog read prompt
|
|
583
730
|
// content from this attribute, not from span events.
|
|
584
|
-
|
|
731
|
+
// Multimodal messages keep their parts structured so image / audio /
|
|
732
|
+
// video / document references survive into the trace (#1525).
|
|
733
|
+
const inputMessages: Array<{
|
|
734
|
+
role: string
|
|
735
|
+
content: string | Array<InputPart>
|
|
736
|
+
}> = []
|
|
585
737
|
for (const sys of systemPromptContents) {
|
|
586
738
|
inputMessages.push({
|
|
587
739
|
role: 'system',
|
|
@@ -589,6 +741,12 @@ export function otelMiddleware(
|
|
|
589
741
|
})
|
|
590
742
|
}
|
|
591
743
|
for (const m of config.messages) {
|
|
744
|
+
if (Array.isArray(m.content)) {
|
|
745
|
+
const parts = serializeParts(m.content, redactContent)
|
|
746
|
+
if (parts.length === 0) continue
|
|
747
|
+
inputMessages.push({ role: m.role, content: parts })
|
|
748
|
+
continue
|
|
749
|
+
}
|
|
592
750
|
const body = serializeContent(m.content)
|
|
593
751
|
if (body.length === 0) continue
|
|
594
752
|
inputMessages.push({
|
package/src/types.ts
CHANGED
|
@@ -100,6 +100,9 @@ export type ToolResultState =
|
|
|
100
100
|
| 'complete' // Result is complete
|
|
101
101
|
| 'error' // Error occurred
|
|
102
102
|
|
|
103
|
+
/** Why a tool result ended without executing successfully. */
|
|
104
|
+
export type ToolResultOutcome = 'cancelled' | 'denied'
|
|
105
|
+
|
|
103
106
|
export type ToolOutputState = 'output-available' | 'output-error'
|
|
104
107
|
|
|
105
108
|
/**
|
|
@@ -196,6 +199,13 @@ export interface ToolCall<TMetadata = unknown> extends Omit<
|
|
|
196
199
|
metadata?: TMetadata
|
|
197
200
|
}
|
|
198
201
|
|
|
202
|
+
/** One source link from a provider-executed web search. */
|
|
203
|
+
export interface ProviderExecutedToolSource {
|
|
204
|
+
url: string
|
|
205
|
+
title?: string
|
|
206
|
+
pageAge?: string
|
|
207
|
+
}
|
|
208
|
+
|
|
199
209
|
/**
|
|
200
210
|
* Convention for tool-call `metadata` that marks a call as **provider-executed**
|
|
201
211
|
* — run by the provider's own infrastructure (e.g. Anthropic `web_search` /
|
|
@@ -209,10 +219,12 @@ export interface ToolCall<TMetadata = unknown> extends Omit<
|
|
|
209
219
|
*
|
|
210
220
|
* Provider-specific payloads live under a namespaced key (e.g. `anthropic`),
|
|
211
221
|
* keeping this convention opaque to the framework core. The index signature
|
|
212
|
-
* preserves those per-adapter fields.
|
|
222
|
+
* preserves those per-adapter fields. `sources` is the normalized list of
|
|
223
|
+
* links a web search used, shared across providers.
|
|
213
224
|
*/
|
|
214
225
|
export interface ProviderExecutedToolMetadata {
|
|
215
226
|
providerExecuted?: boolean
|
|
227
|
+
sources?: Array<ProviderExecutedToolSource>
|
|
216
228
|
[key: string]: unknown
|
|
217
229
|
}
|
|
218
230
|
|
|
@@ -442,6 +454,8 @@ export interface ToolResultPart {
|
|
|
442
454
|
toolCallId: string
|
|
443
455
|
content: string | Array<ContentPart>
|
|
444
456
|
state: ToolResultState
|
|
457
|
+
/** Set when the user or middleware cancelled or denied the tool call; state remains `error`. */
|
|
458
|
+
outcome?: ToolResultOutcome
|
|
445
459
|
error?: string // Error message if state is "error"
|
|
446
460
|
metadata?: Record<string, unknown>
|
|
447
461
|
createdAt?: Date
|
|
@@ -560,6 +574,12 @@ export interface TanStackMessageMetadata {
|
|
|
560
574
|
model?: string
|
|
561
575
|
/** Parent chat run that produced this assistant message. */
|
|
562
576
|
runId?: string
|
|
577
|
+
/**
|
|
578
|
+
* The chat run that produced this assistant message. `withPersistence` sets
|
|
579
|
+
* `id`. `reconstructChat` with `includeRuns: true` adds the finished run's
|
|
580
|
+
* timings, in epoch ms.
|
|
581
|
+
*/
|
|
582
|
+
run?: { id: string; startedAt?: number; finishedAt?: number }
|
|
563
583
|
/** Card data on a child wire message. See `uiMessagesToWire`. */
|
|
564
584
|
subagent?: SubagentWireInfo
|
|
565
585
|
/** Thinking signature for a `role: 'reasoning'` fan-out message. */
|
|
@@ -571,6 +591,8 @@ export interface TanStackMessageMetadata {
|
|
|
571
591
|
createdAt?: string
|
|
572
592
|
content?: Array<ContentPart>
|
|
573
593
|
}
|
|
594
|
+
/** Outcome of a cancelled or denied tool result; when present, the UI state is `error`. */
|
|
595
|
+
toolResultOutcome?: ToolResultOutcome
|
|
574
596
|
structuredOutput?: {
|
|
575
597
|
status?: 'streaming' | 'complete' | 'error'
|
|
576
598
|
partial?: unknown
|
|
@@ -679,6 +701,15 @@ export interface EmitCustomEventOptions {
|
|
|
679
701
|
batch?: boolean
|
|
680
702
|
}
|
|
681
703
|
|
|
704
|
+
/**
|
|
705
|
+
* The user's answer to an `mcp_input` interrupt.
|
|
706
|
+
* `resolved` carries the `payload` from `resolveInterrupt`.
|
|
707
|
+
* `cancelled` means the user called `cancel()`.
|
|
708
|
+
*/
|
|
709
|
+
export type ToolInputResponse =
|
|
710
|
+
| { status: 'resolved'; payload: unknown }
|
|
711
|
+
| { status: 'cancelled' }
|
|
712
|
+
|
|
682
713
|
/**
|
|
683
714
|
* Context passed to tool execute functions, providing capabilities like
|
|
684
715
|
* emitting custom events during execution.
|
|
@@ -693,6 +724,12 @@ export type ToolExecutionContext<TContext = unknown> =
|
|
|
693
724
|
* e.g. MCP `callTool` — should forward this to cancel in-flight work.
|
|
694
725
|
*/
|
|
695
726
|
abortSignal?: AbortSignal
|
|
727
|
+
/**
|
|
728
|
+
* The answer to the input request that this tool call raised in the
|
|
729
|
+
* previous run. It is set only when the run resumes an `mcp_input`
|
|
730
|
+
* interrupt for this tool call.
|
|
731
|
+
*/
|
|
732
|
+
inputResponse?: ToolInputResponse
|
|
696
733
|
/**
|
|
697
734
|
* Emit a custom event during tool execution.
|
|
698
735
|
* Events are streamed to the client in real-time as AG-UI CUSTOM events.
|
|
@@ -14,6 +14,7 @@ import type {
|
|
|
14
14
|
StructuredOutputPart,
|
|
15
15
|
SubagentPart,
|
|
16
16
|
TanStackMessageMetadata,
|
|
17
|
+
ToolResultOutcome,
|
|
17
18
|
UIMessage,
|
|
18
19
|
UIResourcePart,
|
|
19
20
|
} from '../types'
|
|
@@ -49,6 +50,7 @@ function rebuiltToolMetadata(
|
|
|
49
50
|
id: string | undefined,
|
|
50
51
|
content: string | null | Array<ContentPart>,
|
|
51
52
|
anchorOwnsUiResources = false,
|
|
53
|
+
outcome?: ToolResultOutcome,
|
|
52
54
|
): MetadataRecord | undefined {
|
|
53
55
|
const source: MetadataRecord = isRecord(metadata) ? metadata : {}
|
|
54
56
|
const tanstack = isRecord(source.tanstack) ? { ...source.tanstack } : {}
|
|
@@ -61,7 +63,11 @@ function rebuiltToolMetadata(
|
|
|
61
63
|
}
|
|
62
64
|
const result = {
|
|
63
65
|
...source,
|
|
64
|
-
tanstack: {
|
|
66
|
+
tanstack: {
|
|
67
|
+
...tanstack,
|
|
68
|
+
...(outcome !== undefined && { toolResultOutcome: outcome }),
|
|
69
|
+
toolResult,
|
|
70
|
+
},
|
|
65
71
|
}
|
|
66
72
|
return Object.keys(result).length ? result : undefined
|
|
67
73
|
}
|
|
@@ -262,6 +268,7 @@ export function uiMessagesToWire(
|
|
|
262
268
|
part.id,
|
|
263
269
|
part.content,
|
|
264
270
|
true,
|
|
271
|
+
part.outcome,
|
|
265
272
|
)
|
|
266
273
|
wire.push({
|
|
267
274
|
role: 'tool',
|
|
@@ -306,6 +313,8 @@ export function uiMessagesToWire(
|
|
|
306
313
|
undefined,
|
|
307
314
|
undefined,
|
|
308
315
|
result,
|
|
316
|
+
false,
|
|
317
|
+
part.approval?.approved === false ? 'denied' : undefined,
|
|
309
318
|
),
|
|
310
319
|
})
|
|
311
320
|
}
|
|
@@ -385,6 +394,8 @@ function messageMetadata(
|
|
|
385
394
|
tanstack.model = previousTanstack.model
|
|
386
395
|
if (previousTanstack?.runId !== undefined)
|
|
387
396
|
tanstack.runId = previousTanstack.runId
|
|
397
|
+
if (previousTanstack?.run?.id !== undefined)
|
|
398
|
+
tanstack.run = { id: previousTanstack.run.id }
|
|
388
399
|
if (previousTanstack?.signature !== undefined)
|
|
389
400
|
tanstack.signature = previousTanstack.signature
|
|
390
401
|
const createdAt = coerceCreatedAt(msg.createdAt)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ContentPart } from '../types'
|
|
1
|
+
import type { ContentPart, ToolResultOutcome } from '../types'
|
|
2
2
|
|
|
3
3
|
const CONTENT_PART_TYPES = new Set([
|
|
4
4
|
'text',
|
|
@@ -8,6 +8,12 @@ const CONTENT_PART_TYPES = new Set([
|
|
|
8
8
|
'document',
|
|
9
9
|
])
|
|
10
10
|
|
|
11
|
+
export function isToolResultOutcome(
|
|
12
|
+
value: unknown,
|
|
13
|
+
): value is ToolResultOutcome {
|
|
14
|
+
return value === 'cancelled' || value === 'denied'
|
|
15
|
+
}
|
|
16
|
+
|
|
11
17
|
/**
|
|
12
18
|
* Structural check for a single `ContentPart`. A text part must carry a string
|
|
13
19
|
* `content`. Every other part carries a source with a string `value`; a file
|