@nekro-nxt/contracts 0.0.0-stage → 0.1.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/LICENSE +21 -0
- package/README.md +6 -2
- package/dist/index.mjs +4475 -0
- package/dist/index.mjs.map +1 -0
- package/lib/types/community.d.ts +226 -0
- package/lib/types/community.d.ts.map +1 -0
- package/lib/types/config-schema.d.ts +133 -0
- package/lib/types/config-schema.d.ts.map +1 -0
- package/lib/types/domain.d.ts +291 -0
- package/lib/types/domain.d.ts.map +1 -0
- package/lib/types/extension-capabilities.d.ts +277 -0
- package/lib/types/extension-capabilities.d.ts.map +1 -0
- package/lib/types/extension-ui.d.ts +789 -0
- package/lib/types/extension-ui.d.ts.map +1 -0
- package/lib/types/host-api.d.ts +9120 -0
- package/lib/types/host-api.d.ts.map +1 -0
- package/lib/types/index.d.ts +9 -0
- package/lib/types/index.d.ts.map +1 -0
- package/lib/types/management-api.d.ts +108 -0
- package/lib/types/management-api.d.ts.map +1 -0
- package/lib/types/runtime-compatibility.d.ts +72 -0
- package/lib/types/runtime-compatibility.d.ts.map +1 -0
- package/package.json +40 -3
- package/src/community.ts +120 -0
- package/src/config-schema.ts +351 -0
- package/src/domain.ts +365 -0
- package/src/extension-capabilities.ts +473 -0
- package/src/extension-ui.ts +433 -0
- package/src/host-api.ts +3383 -0
- package/src/index.ts +8 -0
- package/src/management-api.ts +168 -0
- package/src/runtime-compatibility.ts +28 -0
package/src/domain.ts
ADDED
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
const brandedId = <Prefix extends string, Brand extends string>(prefix: Prefix, brand: Brand) =>
|
|
4
|
+
z
|
|
5
|
+
.string()
|
|
6
|
+
.regex(new RegExp(`^${prefix}_[0-9A-Za-z]+$`), `${brand} has an invalid format.`)
|
|
7
|
+
.brand(brand)
|
|
8
|
+
|
|
9
|
+
export const AgentIdSchema = brandedId('agt', 'AgentId')
|
|
10
|
+
export const AgentRevisionIdSchema = brandedId('arev', 'AgentRevisionId')
|
|
11
|
+
export const ConnectionIdSchema = brandedId('con', 'ConnectionId')
|
|
12
|
+
export const ConnectionEventIdSchema = brandedId('cev', 'ConnectionEventId')
|
|
13
|
+
export const ChannelIdSchema = brandedId('chn', 'ChannelId')
|
|
14
|
+
export const ChannelEventIdSchema = brandedId('evt', 'ChannelEventId')
|
|
15
|
+
export const PlatformIdentityIdSchema = brandedId('pid', 'PlatformIdentityId')
|
|
16
|
+
export const ChannelMemberIdSchema = brandedId('mbr', 'ChannelMemberId')
|
|
17
|
+
export const AdmissionIdSchema = brandedId('adm', 'AdmissionId')
|
|
18
|
+
export const OutboundIntentIdSchema = brandedId('out', 'OutboundIntentId')
|
|
19
|
+
export const LogicalMessageIdSchema = brandedId('msg', 'LogicalMessageId')
|
|
20
|
+
export const PhysicalDeliveryIdSchema = brandedId('phy', 'PhysicalDeliveryId')
|
|
21
|
+
export const AssetIdSchema = brandedId('ast', 'AssetId')
|
|
22
|
+
export const EpisodeIdSchema = brandedId('eps', 'EpisodeId')
|
|
23
|
+
export const EpisodeHandoffIdSchema = brandedId('hof', 'EpisodeHandoffId')
|
|
24
|
+
export const ExtensionIdSchema = brandedId('ext', 'ExtensionId')
|
|
25
|
+
export const ExtensionRevisionIdSchema = brandedId('xrv', 'ExtensionRevisionId')
|
|
26
|
+
export const DshPluginPackageIdSchema = brandedId('dsp', 'DshPluginPackageId')
|
|
27
|
+
export const DshPluginEntryIdSchema = brandedId('dse', 'DshPluginEntryId')
|
|
28
|
+
export const HostUiPageInstanceIdSchema = brandedId('hup', 'HostUiPageInstanceId')
|
|
29
|
+
export const AuthoringTaskIdSchema = brandedId('aut', 'AuthoringTaskId')
|
|
30
|
+
export const AuthoringAttemptIdSchema = brandedId('aua', 'AuthoringAttemptId')
|
|
31
|
+
|
|
32
|
+
export type AgentId = z.infer<typeof AgentIdSchema>
|
|
33
|
+
export type AgentRevisionId = z.infer<typeof AgentRevisionIdSchema>
|
|
34
|
+
export type ConnectionId = z.infer<typeof ConnectionIdSchema>
|
|
35
|
+
export type ConnectionEventId = z.infer<typeof ConnectionEventIdSchema>
|
|
36
|
+
export type ChannelId = z.infer<typeof ChannelIdSchema>
|
|
37
|
+
export type ChannelEventId = z.infer<typeof ChannelEventIdSchema>
|
|
38
|
+
export type PlatformIdentityId = z.infer<typeof PlatformIdentityIdSchema>
|
|
39
|
+
export type ChannelMemberId = z.infer<typeof ChannelMemberIdSchema>
|
|
40
|
+
export type AdmissionId = z.infer<typeof AdmissionIdSchema>
|
|
41
|
+
export type OutboundIntentId = z.infer<typeof OutboundIntentIdSchema>
|
|
42
|
+
export type LogicalMessageId = z.infer<typeof LogicalMessageIdSchema>
|
|
43
|
+
export type PhysicalDeliveryId = z.infer<typeof PhysicalDeliveryIdSchema>
|
|
44
|
+
export type AssetId = z.infer<typeof AssetIdSchema>
|
|
45
|
+
export type EpisodeId = z.infer<typeof EpisodeIdSchema>
|
|
46
|
+
export type EpisodeHandoffId = z.infer<typeof EpisodeHandoffIdSchema>
|
|
47
|
+
export type ExtensionId = z.infer<typeof ExtensionIdSchema>
|
|
48
|
+
export type ExtensionRevisionId = z.infer<typeof ExtensionRevisionIdSchema>
|
|
49
|
+
export type DshPluginPackageId = z.infer<typeof DshPluginPackageIdSchema>
|
|
50
|
+
export type DshPluginEntryId = z.infer<typeof DshPluginEntryIdSchema>
|
|
51
|
+
export type HostUiPageInstanceId = z.infer<typeof HostUiPageInstanceIdSchema>
|
|
52
|
+
export type AuthoringTaskId = z.infer<typeof AuthoringTaskIdSchema>
|
|
53
|
+
export type AuthoringAttemptId = z.infer<typeof AuthoringAttemptIdSchema>
|
|
54
|
+
|
|
55
|
+
export const AdapterActivityKeySchema = z
|
|
56
|
+
.string()
|
|
57
|
+
.trim()
|
|
58
|
+
.min(1)
|
|
59
|
+
.max(128)
|
|
60
|
+
.regex(/^[a-z][a-z0-9]*(?:[.-][a-z0-9]+)*$/u, 'Adapter activity key has an invalid format.')
|
|
61
|
+
|
|
62
|
+
export type AdapterActivityKey = z.infer<typeof AdapterActivityKeySchema>
|
|
63
|
+
|
|
64
|
+
const PromptTextSegmentSchema = z.object({ type: z.literal('text'), text: z.string() }).strict()
|
|
65
|
+
|
|
66
|
+
const PromptReferenceLabelSchema = z.string().trim().min(1).max(120)
|
|
67
|
+
|
|
68
|
+
const PromptReferenceSegmentSchema = z.discriminatedUnion('kind', [
|
|
69
|
+
z
|
|
70
|
+
.object({
|
|
71
|
+
type: z.literal('reference'),
|
|
72
|
+
kind: z.literal('platform-user'),
|
|
73
|
+
targetId: PlatformIdentityIdSchema,
|
|
74
|
+
labelSnapshot: PromptReferenceLabelSchema,
|
|
75
|
+
})
|
|
76
|
+
.strict(),
|
|
77
|
+
z
|
|
78
|
+
.object({
|
|
79
|
+
type: z.literal('reference'),
|
|
80
|
+
kind: z.literal('channel'),
|
|
81
|
+
targetId: ChannelIdSchema,
|
|
82
|
+
labelSnapshot: PromptReferenceLabelSchema,
|
|
83
|
+
})
|
|
84
|
+
.strict(),
|
|
85
|
+
z
|
|
86
|
+
.object({
|
|
87
|
+
type: z.literal('reference'),
|
|
88
|
+
kind: z.literal('extension'),
|
|
89
|
+
targetId: ExtensionIdSchema,
|
|
90
|
+
labelSnapshot: PromptReferenceLabelSchema,
|
|
91
|
+
})
|
|
92
|
+
.strict(),
|
|
93
|
+
])
|
|
94
|
+
|
|
95
|
+
export const PromptSegmentSchema = z.union([PromptTextSegmentSchema, PromptReferenceSegmentSchema])
|
|
96
|
+
|
|
97
|
+
export const PROMPT_DOCUMENT_MAX_REFERENCES = 128
|
|
98
|
+
export const PROMPT_DOCUMENT_MAX_PLAIN_TEXT_LENGTH = 64 * 1024
|
|
99
|
+
|
|
100
|
+
export const PromptDocumentV1Schema = z
|
|
101
|
+
.object({ version: z.literal(1), segments: z.array(PromptSegmentSchema) })
|
|
102
|
+
.strict()
|
|
103
|
+
.superRefine((document, context) => {
|
|
104
|
+
const referenceCount = document.segments.filter((segment) => segment.type === 'reference').length
|
|
105
|
+
if (referenceCount > PROMPT_DOCUMENT_MAX_REFERENCES) {
|
|
106
|
+
context.addIssue({
|
|
107
|
+
code: 'custom',
|
|
108
|
+
message: `Prompt document must not contain more than ${PROMPT_DOCUMENT_MAX_REFERENCES} references.`,
|
|
109
|
+
path: ['segments'],
|
|
110
|
+
})
|
|
111
|
+
}
|
|
112
|
+
const plainTextLength = document.segments.reduce(
|
|
113
|
+
(length, segment) => length + (segment.type === 'text' ? segment.text.length : segment.labelSnapshot.length + 1),
|
|
114
|
+
0,
|
|
115
|
+
)
|
|
116
|
+
if (plainTextLength > PROMPT_DOCUMENT_MAX_PLAIN_TEXT_LENGTH) {
|
|
117
|
+
context.addIssue({
|
|
118
|
+
code: 'custom',
|
|
119
|
+
message: `Prompt document plain-text projection must not exceed ${PROMPT_DOCUMENT_MAX_PLAIN_TEXT_LENGTH} characters.`,
|
|
120
|
+
path: ['segments'],
|
|
121
|
+
})
|
|
122
|
+
}
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
export type PromptSegment = z.output<typeof PromptSegmentSchema>
|
|
126
|
+
export type PromptDocumentV1 = z.output<typeof PromptDocumentV1Schema>
|
|
127
|
+
|
|
128
|
+
export const promptDocumentFromText = (text: string): PromptDocumentV1 =>
|
|
129
|
+
normalizePromptDocument({ version: 1, segments: text.length === 0 ? [] : [{ type: 'text', text }] })
|
|
130
|
+
|
|
131
|
+
export const promptDocumentPlainText = (document: PromptDocumentV1): string =>
|
|
132
|
+
document.segments.map((segment) => (segment.type === 'text' ? segment.text : `@${segment.labelSnapshot}`)).join('')
|
|
133
|
+
|
|
134
|
+
export const normalizePromptDocument = (input: unknown): PromptDocumentV1 => {
|
|
135
|
+
const parsed = PromptDocumentV1Schema.parse(input)
|
|
136
|
+
const segments: PromptSegment[] = []
|
|
137
|
+
for (const segment of parsed.segments) {
|
|
138
|
+
if (segment.type === 'text') {
|
|
139
|
+
if (segment.text.length === 0) continue
|
|
140
|
+
const previous = segments.at(-1)
|
|
141
|
+
if (previous?.type === 'text') {
|
|
142
|
+
segments[segments.length - 1] = { type: 'text', text: `${previous.text}${segment.text}` }
|
|
143
|
+
} else {
|
|
144
|
+
segments.push(segment)
|
|
145
|
+
}
|
|
146
|
+
continue
|
|
147
|
+
}
|
|
148
|
+
segments.push(segment)
|
|
149
|
+
}
|
|
150
|
+
return PromptDocumentV1Schema.parse({ version: 1, segments })
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export type JsonValue = null | boolean | number | string | JsonValue[] | { readonly [key: string]: JsonValue }
|
|
154
|
+
|
|
155
|
+
export const JsonValueSchema = z.json()
|
|
156
|
+
|
|
157
|
+
export function parseJsonValue(input: unknown): JsonValue {
|
|
158
|
+
return JsonValueSchema.parse(input)
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Package identity and source for one DSH extension visible in the current Host. */
|
|
162
|
+
export interface DshPluginCatalogEntry {
|
|
163
|
+
readonly packageName: string
|
|
164
|
+
readonly packageVersion: string
|
|
165
|
+
readonly origin: 'builtin' | 'profile' | 'dynamic' | 'installed'
|
|
166
|
+
readonly settingsNamespaces: readonly string[]
|
|
167
|
+
readonly loadError?: { readonly code: string; readonly message: string }
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
export type DshPluginInstallSource = 'registry' | 'tarball' | 'imported'
|
|
171
|
+
export type DshPluginActivationScope = 'host' | 'agent'
|
|
172
|
+
|
|
173
|
+
export interface DshPluginPackageRecord {
|
|
174
|
+
readonly id: DshPluginPackageId
|
|
175
|
+
readonly packageName: string
|
|
176
|
+
readonly packageVersion: string
|
|
177
|
+
readonly source: DshPluginInstallSource
|
|
178
|
+
readonly packageDigest: string
|
|
179
|
+
readonly integrity?: string
|
|
180
|
+
readonly lockfileDigest: string
|
|
181
|
+
readonly manifest: JsonValue
|
|
182
|
+
readonly approvedBuilds: readonly string[]
|
|
183
|
+
readonly installedAt: number
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
export interface DshPluginEntryRecord {
|
|
187
|
+
readonly id: DshPluginEntryId
|
|
188
|
+
readonly packageId: DshPluginPackageId
|
|
189
|
+
readonly entryKey: string
|
|
190
|
+
readonly moduleName: string
|
|
191
|
+
readonly suggestedScope: DshPluginActivationScope
|
|
192
|
+
readonly selectedScope?: DshPluginActivationScope
|
|
193
|
+
readonly config: JsonValue
|
|
194
|
+
readonly createdAt: number
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export interface DshPluginActivationRecord {
|
|
198
|
+
readonly entryId: DshPluginEntryId
|
|
199
|
+
readonly targetKey: string
|
|
200
|
+
readonly target: DshPluginActivationScope
|
|
201
|
+
readonly agentId?: AgentId
|
|
202
|
+
readonly activatedAt: number
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export interface DshPluginDiagnosticRecord {
|
|
206
|
+
readonly entryId: DshPluginEntryId
|
|
207
|
+
readonly targetKey: string
|
|
208
|
+
readonly status: 'active' | 'load-failed' | 'restore-failed' | 'dispose-failed'
|
|
209
|
+
readonly phase: 'import' | 'apply' | 'update' | 'dispose' | 'restore'
|
|
210
|
+
readonly message?: string
|
|
211
|
+
readonly observedAt: number
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
export interface DshSettingsNamespaceView {
|
|
215
|
+
readonly ns: string
|
|
216
|
+
readonly schema: unknown
|
|
217
|
+
readonly resolved: unknown
|
|
218
|
+
readonly base?: unknown
|
|
219
|
+
readonly user?: unknown
|
|
220
|
+
readonly applies: 'live' | 'restart'
|
|
221
|
+
readonly secrets: readonly { readonly path: readonly string[]; readonly set: boolean }[]
|
|
222
|
+
readonly revision: number
|
|
223
|
+
readonly writable: boolean
|
|
224
|
+
readonly owner?: { readonly packageName: string; readonly packageVersion: string }
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
export type DshSettingsPathOperation =
|
|
228
|
+
| { readonly op: 'set'; readonly path: readonly string[]; readonly value: unknown }
|
|
229
|
+
| { readonly op: 'unset'; readonly path: readonly string[] }
|
|
230
|
+
|
|
231
|
+
export interface DshSettingsMutationRequest {
|
|
232
|
+
readonly expectedRevision: number
|
|
233
|
+
readonly ops: readonly DshSettingsPathOperation[]
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
export interface DshCredentialView {
|
|
237
|
+
readonly configured: boolean
|
|
238
|
+
readonly source?: string
|
|
239
|
+
readonly writable: boolean
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
const utf8ByteLength = (value: string): number => new TextEncoder().encode(value).byteLength
|
|
243
|
+
|
|
244
|
+
export const RICH_EXTENSION_MAX_BYTES = 64 * 1024
|
|
245
|
+
|
|
246
|
+
export const RichExtensionSchema = JsonValueSchema.refine(
|
|
247
|
+
(value) => utf8ByteLength(JSON.stringify(value)) <= RICH_EXTENSION_MAX_BYTES,
|
|
248
|
+
`rich extension must not exceed ${RICH_EXTENSION_MAX_BYTES} bytes.`,
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
export const RichTargetUrlSchema = z
|
|
252
|
+
.url()
|
|
253
|
+
.max(2048)
|
|
254
|
+
.refine((value) => {
|
|
255
|
+
const protocol = new URL(value).protocol
|
|
256
|
+
return protocol === 'http:' || protocol === 'https:'
|
|
257
|
+
}, 'rich target URL must use HTTP or HTTPS.')
|
|
258
|
+
|
|
259
|
+
export const MessagePartSchema = z.discriminatedUnion('type', [
|
|
260
|
+
z.object({ type: z.literal('text'), text: z.string() }).strict(),
|
|
261
|
+
z.object({ type: z.literal('mention'), memberId: ChannelMemberIdSchema }).strict(),
|
|
262
|
+
z.object({ type: z.literal('image'), assetId: AssetIdSchema, alt: z.string().optional() }).strict(),
|
|
263
|
+
z.object({ type: z.literal('file'), assetId: AssetIdSchema, name: z.string().optional() }).strict(),
|
|
264
|
+
z.object({ type: z.literal('audio'), assetId: AssetIdSchema }).strict(),
|
|
265
|
+
z.object({ type: z.literal('quote'), messageId: LogicalMessageIdSchema }).strict(),
|
|
266
|
+
z
|
|
267
|
+
.object({
|
|
268
|
+
type: z.literal('rich'),
|
|
269
|
+
adapterKey: z.string().trim().min(1).max(64),
|
|
270
|
+
kind: z.string().trim().min(1).max(64),
|
|
271
|
+
summary: z.string().trim().min(1).max(500),
|
|
272
|
+
title: z.string().trim().min(1).max(200).optional(),
|
|
273
|
+
source: z.string().trim().min(1).max(80).optional(),
|
|
274
|
+
targetUrl: RichTargetUrlSchema.optional(),
|
|
275
|
+
previewAssetId: AssetIdSchema.optional(),
|
|
276
|
+
extension: RichExtensionSchema.optional(),
|
|
277
|
+
})
|
|
278
|
+
.strict(),
|
|
279
|
+
])
|
|
280
|
+
|
|
281
|
+
export type MessagePart = z.infer<typeof MessagePartSchema>
|
|
282
|
+
|
|
283
|
+
export const MessagePartsSchema = z.array(MessagePartSchema)
|
|
284
|
+
export const NonEmptyMessagePartsSchema = MessagePartsSchema.min(1)
|
|
285
|
+
|
|
286
|
+
export function parseMessageParts(input: unknown): MessagePart[] {
|
|
287
|
+
return MessagePartsSchema.parse(input)
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
const collectRichAssetIds = (value: JsonValue | undefined, into: AssetId[]): void => {
|
|
291
|
+
if (value === null || value === undefined || typeof value === 'boolean' || typeof value === 'number') return
|
|
292
|
+
if (typeof value === 'string') return
|
|
293
|
+
if (Array.isArray(value)) {
|
|
294
|
+
for (const item of value) collectRichAssetIds(item, into)
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
for (const [key, child] of Object.entries(value)) {
|
|
298
|
+
if ((key === 'imageAssetId' || key === 'previewAssetId') && typeof child === 'string') {
|
|
299
|
+
const parsed = AssetIdSchema.safeParse(child)
|
|
300
|
+
if (parsed.success) into.push(parsed.data)
|
|
301
|
+
} else {
|
|
302
|
+
collectRichAssetIds(child, into)
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
export const messagePartAssetIds = (part: MessagePart): readonly AssetId[] => {
|
|
308
|
+
if (part.type === 'image' || part.type === 'file' || part.type === 'audio') return [part.assetId]
|
|
309
|
+
if (part.type !== 'rich') return []
|
|
310
|
+
const ids: AssetId[] = []
|
|
311
|
+
if (part.previewAssetId) ids.push(part.previewAssetId)
|
|
312
|
+
collectRichAssetIds(part.extension, ids)
|
|
313
|
+
return ids
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export const messagePartAssetId = (part: MessagePart): AssetId | undefined => messagePartAssetIds(part)[0]
|
|
317
|
+
|
|
318
|
+
const asJsonRecord = (value: JsonValue | undefined): Readonly<Record<string, JsonValue>> | undefined =>
|
|
319
|
+
value !== null && typeof value === 'object' && !Array.isArray(value) ? value : undefined
|
|
320
|
+
|
|
321
|
+
const richItemLine = (value: JsonValue): string | undefined => {
|
|
322
|
+
const item = asJsonRecord(value)
|
|
323
|
+
if (!item) return undefined
|
|
324
|
+
const sender = typeof item['sender'] === 'string' ? item['sender'] : undefined
|
|
325
|
+
const text = typeof item['text'] === 'string' ? item['text'] : undefined
|
|
326
|
+
const card = asJsonRecord(item['card'])
|
|
327
|
+
const cardSummary =
|
|
328
|
+
typeof card?.['summary'] === 'string'
|
|
329
|
+
? card['summary']
|
|
330
|
+
: typeof card?.['title'] === 'string'
|
|
331
|
+
? card['title']
|
|
332
|
+
: undefined
|
|
333
|
+
const imageName = typeof item['imageName'] === 'string' ? item['imageName'] : undefined
|
|
334
|
+
const imageText = typeof item['imageAssetId'] === 'string' ? `[图片${imageName ? ` ${imageName}` : ''}]` : undefined
|
|
335
|
+
const body = [text, cardSummary ? `[卡片] ${cardSummary}` : undefined, imageText]
|
|
336
|
+
.filter((part): part is string => Boolean(part && part.length > 0))
|
|
337
|
+
.join(' ')
|
|
338
|
+
if (!sender && !body) return undefined
|
|
339
|
+
return sender ? `${sender}:${body}` : body
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
/** Flatten a rich part for search and model context. */
|
|
343
|
+
export const richPartContextText = (part: Extract<MessagePart, { type: 'rich' }>): string => {
|
|
344
|
+
const header = [part.summary, part.title, part.source].filter(
|
|
345
|
+
(value, index, all): value is string =>
|
|
346
|
+
typeof value === 'string' && value.length > 0 && all.indexOf(value) === index,
|
|
347
|
+
)
|
|
348
|
+
const items = asJsonRecord(part.extension)?.['items']
|
|
349
|
+
const itemLines = Array.isArray(items)
|
|
350
|
+
? items.flatMap((item) => {
|
|
351
|
+
const line = richItemLine(item)
|
|
352
|
+
return line === undefined ? [] : [line]
|
|
353
|
+
})
|
|
354
|
+
: []
|
|
355
|
+
return [...header, ...itemLines].join('\n')
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
export const messagePartsSearchText = (parts: readonly MessagePart[]): string =>
|
|
359
|
+
parts
|
|
360
|
+
.flatMap((part) => {
|
|
361
|
+
if (part.type === 'text') return [part.text]
|
|
362
|
+
if (part.type === 'rich') return [richPartContextText(part)]
|
|
363
|
+
return []
|
|
364
|
+
})
|
|
365
|
+
.join('\n')
|