@tanstack/ai 0.46.0 → 0.47.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/esm/activities/chat/index.d.ts +36 -11
- package/dist/esm/activities/chat/index.js +440 -79
- package/dist/esm/activities/chat/index.js.map +1 -1
- package/dist/esm/activities/chat/messages.d.ts +1 -0
- package/dist/esm/activities/chat/messages.js +12 -7
- package/dist/esm/activities/chat/messages.js.map +1 -1
- package/dist/esm/activities/chat/middleware/builder.d.ts +7 -2
- package/dist/esm/activities/chat/middleware/builder.js.map +1 -1
- package/dist/esm/activities/chat/middleware/compose.d.ts +10 -3
- package/dist/esm/activities/chat/middleware/compose.js +55 -0
- package/dist/esm/activities/chat/middleware/compose.js.map +1 -1
- package/dist/esm/activities/chat/middleware/define.d.ts +6 -3
- package/dist/esm/activities/chat/middleware/define.js.map +1 -1
- package/dist/esm/activities/chat/middleware/generic-interrupts.d.ts +13 -0
- package/dist/esm/activities/chat/middleware/generic-interrupts.js +8 -0
- package/dist/esm/activities/chat/middleware/generic-interrupts.js.map +1 -0
- package/dist/esm/activities/chat/middleware/index.d.ts +4 -1
- package/dist/esm/activities/chat/middleware/types.d.ts +54 -3
- package/dist/esm/activities/chat/middleware/types.js +16 -0
- package/dist/esm/activities/chat/middleware/types.js.map +1 -0
- package/dist/esm/activities/chat/stream/processor.js +18 -5
- package/dist/esm/activities/chat/stream/processor.js.map +1 -1
- package/dist/esm/adapter-internals.d.ts +6 -0
- package/dist/esm/adapter-internals.js +4 -1
- package/dist/esm/client.d.ts +4 -0
- package/dist/esm/client.js +3 -1
- package/dist/esm/client.js.map +1 -1
- package/dist/esm/generic-interrupt-continuation.d.ts +45 -0
- package/dist/esm/generic-interrupt-continuation.js +80 -0
- package/dist/esm/generic-interrupt-continuation.js.map +1 -0
- package/dist/esm/index.d.ts +6 -1
- package/dist/esm/index.js +4 -1
- package/dist/esm/interrupt-definition.d.ts +113 -0
- package/dist/esm/interrupt-definition.js +169 -0
- package/dist/esm/interrupt-definition.js.map +1 -0
- package/dist/esm/interrupt-resume.d.ts +3 -0
- package/dist/esm/interrupt-resume.js +77 -16
- package/dist/esm/interrupt-resume.js.map +1 -1
- package/dist/esm/interrupts.d.ts +12 -3
- package/dist/esm/interrupts.js.map +1 -1
- package/dist/esm/types.d.ts +11 -3
- package/dist/esm/utilities/chat-params.js +10 -1
- package/dist/esm/utilities/chat-params.js.map +1 -1
- package/package.json +3 -3
- package/skills/ai-core/media-generation/SKILL.md +4 -1
- package/skills/ai-core/middleware/SKILL.md +53 -44
- package/skills/ai-core/structured-outputs/SKILL.md +59 -55
- package/skills/ai-core/tool-calling/SKILL.md +54 -1
- package/src/activities/chat/index.ts +1030 -211
- package/src/activities/chat/messages.ts +11 -3
- package/src/activities/chat/middleware/builder.ts +29 -4
- package/src/activities/chat/middleware/compose.ts +95 -5
- package/src/activities/chat/middleware/define.ts +13 -3
- package/src/activities/chat/middleware/generic-interrupts.ts +26 -0
- package/src/activities/chat/middleware/index.ts +15 -0
- package/src/activities/chat/middleware/types.ts +127 -2
- package/src/activities/chat/stream/processor.ts +21 -0
- package/src/adapter-internals.ts +20 -0
- package/src/client.ts +20 -0
- package/src/generic-interrupt-continuation.ts +162 -0
- package/src/index.ts +34 -0
- package/src/interrupt-definition.ts +581 -0
- package/src/interrupt-resume.ts +156 -25
- package/src/interrupts.ts +13 -3
- package/src/types.ts +11 -3
- package/src/utilities/chat-params.ts +16 -3
package/src/interrupt-resume.ts
CHANGED
|
@@ -25,6 +25,10 @@ import type {
|
|
|
25
25
|
ChatMiddlewareConfig,
|
|
26
26
|
ChatResumeToolState,
|
|
27
27
|
} from './activities/chat/middleware/types'
|
|
28
|
+
import type {
|
|
29
|
+
GenericInterruptRequest,
|
|
30
|
+
InterruptDefinition,
|
|
31
|
+
} from './interrupt-definition'
|
|
28
32
|
import type { Interrupt, RunAgentResumeItem } from './types'
|
|
29
33
|
|
|
30
34
|
/**
|
|
@@ -46,6 +50,10 @@ export interface PendingInterruptResumeRecord {
|
|
|
46
50
|
interruptId: string
|
|
47
51
|
payload: unknown
|
|
48
52
|
binding: InterruptBinding
|
|
53
|
+
/** Present for a first-party generic interrupt. */
|
|
54
|
+
genericRequest?: GenericInterruptRequest<
|
|
55
|
+
InterruptDefinition<any, any, any, any>
|
|
56
|
+
>
|
|
49
57
|
}
|
|
50
58
|
|
|
51
59
|
export interface ValidateInterruptResumeBatchInput {
|
|
@@ -164,6 +172,17 @@ function runtimeTool(
|
|
|
164
172
|
return tools.find((tool) => tool.name === name) as RuntimeTool | undefined
|
|
165
173
|
}
|
|
166
174
|
|
|
175
|
+
async function parseSchemaValue(
|
|
176
|
+
schema: unknown,
|
|
177
|
+
value: unknown,
|
|
178
|
+
): Promise<{ success: true; data: unknown } | { success: false }> {
|
|
179
|
+
if (!isStandardSchema(schema)) return { success: true, data: value }
|
|
180
|
+
const result = await validateWithStandardSchema<unknown>(schema, value)
|
|
181
|
+
return result.success
|
|
182
|
+
? { success: true, data: result.data }
|
|
183
|
+
: { success: false }
|
|
184
|
+
}
|
|
185
|
+
|
|
167
186
|
function descriptorResponseSchema(
|
|
168
187
|
record: PendingInterruptResumeRecord,
|
|
169
188
|
): unknown {
|
|
@@ -219,9 +238,14 @@ function validateDescriptorSchema(
|
|
|
219
238
|
errors: Array<InterruptSubmissionError>,
|
|
220
239
|
): unknown {
|
|
221
240
|
const schema = descriptorResponseSchema(record)
|
|
241
|
+
const responseSchemaHash = binding.responseSchemaHash
|
|
242
|
+
if (schema === undefined && responseSchemaHash === undefined) {
|
|
243
|
+
return undefined
|
|
244
|
+
}
|
|
222
245
|
if (
|
|
223
246
|
schema === undefined ||
|
|
224
|
-
|
|
247
|
+
responseSchemaHash === undefined ||
|
|
248
|
+
schemaHash(schema) !== responseSchemaHash
|
|
225
249
|
) {
|
|
226
250
|
errors.push(
|
|
227
251
|
interruptItemError(
|
|
@@ -276,12 +300,24 @@ export async function validateInterruptResumeBatch(
|
|
|
276
300
|
}
|
|
277
301
|
}
|
|
278
302
|
|
|
303
|
+
const pendingGenerics = input.pending.filter(
|
|
304
|
+
(record) => record.binding.kind === 'generic',
|
|
305
|
+
)
|
|
306
|
+
const genericBatchSatisfied =
|
|
307
|
+
pendingGenerics.length > 0 &&
|
|
308
|
+
pendingGenerics.every((record) => resumeById.has(record.interruptId))
|
|
309
|
+
|
|
279
310
|
let incomplete = false
|
|
280
311
|
for (const record of input.pending) {
|
|
281
312
|
const errors = group(record.interruptId)
|
|
282
313
|
const entry = resumeById.get(record.interruptId)
|
|
283
314
|
const binding = record.binding
|
|
284
315
|
if (!entry) {
|
|
316
|
+
// Client tools that share a generic interrupt batch wait for
|
|
317
|
+
// `toolResume`. `continue` re-emits them; `cancel` / `stop` skip them.
|
|
318
|
+
if (genericBatchSatisfied && binding.kind === 'client-tool-execution') {
|
|
319
|
+
continue
|
|
320
|
+
}
|
|
285
321
|
incomplete = true
|
|
286
322
|
errors.push(
|
|
287
323
|
interruptItemError(
|
|
@@ -307,19 +343,29 @@ export async function validateInterruptResumeBatch(
|
|
|
307
343
|
),
|
|
308
344
|
)
|
|
309
345
|
}
|
|
310
|
-
if (
|
|
311
|
-
binding.expiresAt
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
)
|
|
322
|
-
)
|
|
346
|
+
if (binding.expiresAt !== undefined) {
|
|
347
|
+
const expiresAt = Date.parse(binding.expiresAt)
|
|
348
|
+
if (!Number.isFinite(expiresAt)) {
|
|
349
|
+
errors.push(
|
|
350
|
+
interruptItemError(
|
|
351
|
+
input,
|
|
352
|
+
record.interruptId,
|
|
353
|
+
'invalid-payload',
|
|
354
|
+
`Interrupt ${record.interruptId} has an invalid expiresAt.`,
|
|
355
|
+
{ source: 'server' },
|
|
356
|
+
),
|
|
357
|
+
)
|
|
358
|
+
} else if (expiresAt <= (input.now ?? Date.now())) {
|
|
359
|
+
errors.push(
|
|
360
|
+
interruptItemError(
|
|
361
|
+
input,
|
|
362
|
+
record.interruptId,
|
|
363
|
+
'expired',
|
|
364
|
+
`Interrupt ${record.interruptId} has expired.`,
|
|
365
|
+
{ source: 'server' },
|
|
366
|
+
),
|
|
367
|
+
)
|
|
368
|
+
}
|
|
323
369
|
}
|
|
324
370
|
|
|
325
371
|
const responseSchema = validateDescriptorSchema(
|
|
@@ -342,6 +388,28 @@ export async function validateInterruptResumeBatch(
|
|
|
342
388
|
continue
|
|
343
389
|
}
|
|
344
390
|
if (binding.kind === 'generic') {
|
|
391
|
+
const genericRequest = record.genericRequest
|
|
392
|
+
if (genericRequest !== undefined) {
|
|
393
|
+
const batchIndex = binding.batchIndex
|
|
394
|
+
if (
|
|
395
|
+
binding.definitionId !== genericRequest.definition.id ||
|
|
396
|
+
binding.key !== genericRequest.key ||
|
|
397
|
+
binding.interruptId !== record.interruptId ||
|
|
398
|
+
batchIndex === undefined ||
|
|
399
|
+
!Number.isInteger(batchIndex) ||
|
|
400
|
+
batchIndex < 0
|
|
401
|
+
) {
|
|
402
|
+
errors.push(
|
|
403
|
+
interruptItemError(
|
|
404
|
+
input,
|
|
405
|
+
record.interruptId,
|
|
406
|
+
'stale',
|
|
407
|
+
`Generic interrupt ${record.interruptId} has stale definition metadata.`,
|
|
408
|
+
{ source: 'server' },
|
|
409
|
+
),
|
|
410
|
+
)
|
|
411
|
+
}
|
|
412
|
+
}
|
|
345
413
|
if (entry.status === 'cancelled') {
|
|
346
414
|
if (entry.payload !== undefined) {
|
|
347
415
|
errors.push(
|
|
@@ -353,6 +421,16 @@ export async function validateInterruptResumeBatch(
|
|
|
353
421
|
),
|
|
354
422
|
)
|
|
355
423
|
}
|
|
424
|
+
} else if (genericRequest !== undefined) {
|
|
425
|
+
await pushSchemaIssues({
|
|
426
|
+
request: input,
|
|
427
|
+
errors,
|
|
428
|
+
interruptId: record.interruptId,
|
|
429
|
+
schema: genericRequest.definition.responseSchema,
|
|
430
|
+
value: entry.payload,
|
|
431
|
+
code: 'invalid-payload',
|
|
432
|
+
label: `Interrupt ${record.interruptId} payload is invalid`,
|
|
433
|
+
})
|
|
356
434
|
} else if (responseSchema !== undefined) {
|
|
357
435
|
await pushSchemaIssues({
|
|
358
436
|
request: input,
|
|
@@ -616,16 +694,42 @@ export async function validateInterruptResumeBatch(
|
|
|
616
694
|
if (!entry) continue
|
|
617
695
|
const binding = record.binding
|
|
618
696
|
if (binding.kind === 'generic') {
|
|
619
|
-
|
|
620
|
-
record.interruptId,
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
697
|
+
if (entry.status !== 'resolved') {
|
|
698
|
+
genericInterrupts.set(record.interruptId, {
|
|
699
|
+
interruptId: record.interruptId,
|
|
700
|
+
status: 'cancelled',
|
|
701
|
+
})
|
|
702
|
+
continue
|
|
703
|
+
}
|
|
704
|
+
if (record.genericRequest === undefined) {
|
|
705
|
+
genericInterrupts.set(record.interruptId, {
|
|
706
|
+
interruptId: record.interruptId,
|
|
707
|
+
status: 'resolved',
|
|
708
|
+
payload: entry.payload,
|
|
709
|
+
})
|
|
710
|
+
continue
|
|
711
|
+
}
|
|
712
|
+
const parsed = await parseSchemaValue(
|
|
713
|
+
record.genericRequest.definition.responseSchema,
|
|
714
|
+
entry.payload,
|
|
628
715
|
)
|
|
716
|
+
if (!parsed.success) {
|
|
717
|
+
return {
|
|
718
|
+
errors: [
|
|
719
|
+
interruptItemError(
|
|
720
|
+
input,
|
|
721
|
+
record.interruptId,
|
|
722
|
+
'invalid-payload',
|
|
723
|
+
`Interrupt ${record.interruptId} payload is invalid.`,
|
|
724
|
+
),
|
|
725
|
+
],
|
|
726
|
+
}
|
|
727
|
+
}
|
|
728
|
+
genericInterrupts.set(record.interruptId, {
|
|
729
|
+
interruptId: record.interruptId,
|
|
730
|
+
status: 'resolved',
|
|
731
|
+
payload: parsed.data,
|
|
732
|
+
})
|
|
629
733
|
continue
|
|
630
734
|
}
|
|
631
735
|
if (entry.status === 'cancelled') {
|
|
@@ -711,17 +815,44 @@ export function readUnopenedInterruptBinding(
|
|
|
711
815
|
const interruptId = stringField(raw, 'interruptId')
|
|
712
816
|
const responseSchemaHash = stringField(raw, 'responseSchemaHash')
|
|
713
817
|
const expiresAt = stringField(raw, 'expiresAt')
|
|
714
|
-
if (!interruptId ||
|
|
818
|
+
if (!interruptId || responseSchemaHash === '') return undefined
|
|
819
|
+
if (expiresAt !== undefined && !Number.isFinite(Date.parse(expiresAt))) {
|
|
820
|
+
return undefined
|
|
821
|
+
}
|
|
715
822
|
const v = INTERRUPT_BINDING_VERSION
|
|
716
823
|
if (kind === 'generic') {
|
|
824
|
+
const definitionId = stringField(raw, 'definitionId')
|
|
825
|
+
const key = stringField(raw, 'key')
|
|
826
|
+
const batchIndex = raw['batchIndex']
|
|
827
|
+
const payloadSchemaHash = stringField(raw, 'payloadSchemaHash')
|
|
828
|
+
const hasFirstPartyFields =
|
|
829
|
+
definitionId !== undefined ||
|
|
830
|
+
key !== undefined ||
|
|
831
|
+
batchIndex !== undefined ||
|
|
832
|
+
payloadSchemaHash !== undefined
|
|
833
|
+
if (
|
|
834
|
+
hasFirstPartyFields &&
|
|
835
|
+
(!definitionId ||
|
|
836
|
+
!key ||
|
|
837
|
+
typeof batchIndex !== 'number' ||
|
|
838
|
+
!Number.isInteger(batchIndex) ||
|
|
839
|
+
batchIndex < 0)
|
|
840
|
+
) {
|
|
841
|
+
return undefined
|
|
842
|
+
}
|
|
717
843
|
return {
|
|
718
844
|
v,
|
|
719
845
|
kind,
|
|
720
846
|
interruptId,
|
|
721
|
-
responseSchemaHash,
|
|
847
|
+
...(responseSchemaHash ? { responseSchemaHash } : {}),
|
|
722
848
|
...(expiresAt ? { expiresAt } : {}),
|
|
849
|
+
...(definitionId ? { definitionId } : {}),
|
|
850
|
+
...(key ? { key } : {}),
|
|
851
|
+
...(typeof batchIndex === 'number' ? { batchIndex } : {}),
|
|
852
|
+
...(payloadSchemaHash ? { payloadSchemaHash } : {}),
|
|
723
853
|
}
|
|
724
854
|
}
|
|
855
|
+
if (!responseSchemaHash) return undefined
|
|
725
856
|
const toolName = stringField(raw, 'toolName')
|
|
726
857
|
const toolCallId = stringField(raw, 'toolCallId')
|
|
727
858
|
if (!toolName || !toolCallId) return undefined
|
package/src/interrupts.ts
CHANGED
|
@@ -84,12 +84,15 @@ interface InterruptBindingBase {
|
|
|
84
84
|
interruptId: string
|
|
85
85
|
interruptedRunId: string
|
|
86
86
|
generation: number
|
|
87
|
-
responseSchemaHash: string
|
|
88
87
|
expiresAt?: string
|
|
89
88
|
}
|
|
90
89
|
|
|
90
|
+
interface ResponseSchemaInterruptBindingBase extends InterruptBindingBase {
|
|
91
|
+
responseSchemaHash: string
|
|
92
|
+
}
|
|
93
|
+
|
|
91
94
|
export type InterruptBinding =
|
|
92
|
-
| (
|
|
95
|
+
| (ResponseSchemaInterruptBindingBase & {
|
|
93
96
|
kind: 'tool-approval'
|
|
94
97
|
toolName: string
|
|
95
98
|
toolCallId: string
|
|
@@ -97,7 +100,7 @@ export type InterruptBinding =
|
|
|
97
100
|
inputSchemaHash: string
|
|
98
101
|
approvalSchemaHash: string
|
|
99
102
|
})
|
|
100
|
-
| (
|
|
103
|
+
| (ResponseSchemaInterruptBindingBase & {
|
|
101
104
|
kind: 'client-tool-execution'
|
|
102
105
|
toolName: string
|
|
103
106
|
toolCallId: string
|
|
@@ -105,6 +108,13 @@ export type InterruptBinding =
|
|
|
105
108
|
})
|
|
106
109
|
| (InterruptBindingBase & {
|
|
107
110
|
kind: 'generic'
|
|
111
|
+
/** Omitted when the generic interrupt accepts an unvalidated response. */
|
|
112
|
+
responseSchemaHash?: string
|
|
113
|
+
/** Present only for a first-party generic interrupt. */
|
|
114
|
+
definitionId?: string
|
|
115
|
+
key?: string
|
|
116
|
+
batchIndex?: number
|
|
117
|
+
payloadSchemaHash?: string
|
|
108
118
|
})
|
|
109
119
|
|
|
110
120
|
export type UnopenedInterruptBinding = InterruptBinding extends infer TBinding
|
package/src/types.ts
CHANGED
|
@@ -369,6 +369,12 @@ export interface ModelMessage<
|
|
|
369
369
|
toolCalls?: Array<ToolCall>
|
|
370
370
|
toolCallId?: string
|
|
371
371
|
thinking?: Array<{ content: string; signature?: string }>
|
|
372
|
+
/**
|
|
373
|
+
* Completed structured output represented by this assistant message.
|
|
374
|
+
* `content` remains the provider-facing JSON text; this field preserves the
|
|
375
|
+
* typed UI part across persistence and message conversion.
|
|
376
|
+
*/
|
|
377
|
+
structuredOutput?: StructuredOutputPart
|
|
372
378
|
/**
|
|
373
379
|
* Optional stable message id. Providers ignore it; it exists so a persisted
|
|
374
380
|
* transcript can retain the streaming `messageId` and survive the
|
|
@@ -1028,8 +1034,7 @@ export interface TextOptions<
|
|
|
1028
1034
|
|
|
1029
1035
|
/**
|
|
1030
1036
|
* AG-UI interrupt resume responses supplied by the client on a follow-up run.
|
|
1031
|
-
*
|
|
1032
|
-
* upstream-native interrupts.
|
|
1037
|
+
* A first-party generic item carries the original request in `metadata`.
|
|
1033
1038
|
*/
|
|
1034
1039
|
resume?: Array<RunAgentResumeItem>
|
|
1035
1040
|
|
|
@@ -1128,7 +1133,10 @@ export type Interrupt = AGUIInterrupt
|
|
|
1128
1133
|
|
|
1129
1134
|
export type RunFinishedOutcome = AGUIRunFinishedOutcome
|
|
1130
1135
|
|
|
1131
|
-
export type RunAgentResumeItem = AGUIResumeEntry
|
|
1136
|
+
export type RunAgentResumeItem = AGUIResumeEntry & {
|
|
1137
|
+
/** AG-UI resume metadata. First-party generic requests ride here. */
|
|
1138
|
+
metadata?: Record<string, unknown>
|
|
1139
|
+
}
|
|
1132
1140
|
|
|
1133
1141
|
/**
|
|
1134
1142
|
* Emitted when a run completes successfully.
|
|
@@ -2,7 +2,6 @@ import { AGUIError } from '@ag-ui/core'
|
|
|
2
2
|
import type {
|
|
3
3
|
Context as AGUIContext,
|
|
4
4
|
Message as AGUIMessage,
|
|
5
|
-
ResumeEntry as AGUIResumeEntry,
|
|
6
5
|
Role as AGUIRole,
|
|
7
6
|
} from '@ag-ui/core'
|
|
8
7
|
import type {
|
|
@@ -22,6 +21,7 @@ const KNOWN_PART_TYPES = new Set([
|
|
|
22
21
|
'tool-call',
|
|
23
22
|
'tool-result',
|
|
24
23
|
'thinking',
|
|
24
|
+
'structured-output',
|
|
25
25
|
])
|
|
26
26
|
|
|
27
27
|
function isValidParts(value: unknown): value is Array<{ type: string }> {
|
|
@@ -30,6 +30,10 @@ function isValidParts(value: unknown): value is Array<{ type: string }> {
|
|
|
30
30
|
if (!p || typeof p !== 'object') return false
|
|
31
31
|
const type = (p as { type?: unknown }).type
|
|
32
32
|
if (typeof type !== 'string' || !KNOWN_PART_TYPES.has(type)) return false
|
|
33
|
+
if (type === 'structured-output') {
|
|
34
|
+
const raw = (p as { raw?: unknown }).raw
|
|
35
|
+
if (raw !== undefined && typeof raw !== 'string') return false
|
|
36
|
+
}
|
|
33
37
|
}
|
|
34
38
|
return true
|
|
35
39
|
}
|
|
@@ -173,20 +177,29 @@ function validateContext(value: unknown, index: number): AGUIContext {
|
|
|
173
177
|
}
|
|
174
178
|
}
|
|
175
179
|
|
|
176
|
-
function validateResumeEntry(
|
|
180
|
+
function validateResumeEntry(
|
|
181
|
+
value: unknown,
|
|
182
|
+
index: number,
|
|
183
|
+
): RunAgentResumeItem {
|
|
177
184
|
const at = `resume[${index}]`
|
|
178
185
|
if (!isRecord(value)) invalidBody(`${at} must be an object`)
|
|
179
186
|
const status = value.status
|
|
180
187
|
if (status !== 'resolved' && status !== 'cancelled') {
|
|
181
188
|
invalidBody(`${at}.status must be "resolved" or "cancelled"`)
|
|
182
189
|
}
|
|
183
|
-
const entry:
|
|
190
|
+
const entry: RunAgentResumeItem = {
|
|
184
191
|
interruptId: requireString(value.interruptId, `${at}.interruptId`),
|
|
185
192
|
status,
|
|
186
193
|
}
|
|
187
194
|
// Omit the key entirely when absent, matching the optional-field shape the
|
|
188
195
|
// schema produced.
|
|
189
196
|
if (value.payload !== undefined) entry.payload = value.payload
|
|
197
|
+
if (value.metadata !== undefined) {
|
|
198
|
+
if (!isRecord(value.metadata)) {
|
|
199
|
+
invalidBody(`${at}.metadata must be an object`)
|
|
200
|
+
}
|
|
201
|
+
entry.metadata = value.metadata
|
|
202
|
+
}
|
|
190
203
|
return entry
|
|
191
204
|
}
|
|
192
205
|
|