@adhdev/daemon-core 0.8.65 → 0.8.66
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/index.js +937 -625
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +937 -625
- package/dist/index.mjs.map +1 -1
- package/dist/providers/extension-provider-instance.d.ts +5 -3
- package/dist/providers/provider-input-support.d.ts +9 -0
- package/dist/providers/read-chat-contract.d.ts +2 -0
- package/node_modules/@adhdev/session-host-core/package.json +2 -2
- package/package.json +2 -2
- package/src/agent-stream/provider-adapter.ts +15 -9
- package/src/cli-adapters/provider-cli-adapter.ts +4 -0
- package/src/commands/chat-commands.ts +75 -26
- package/src/commands/cli-manager.ts +7 -0
- package/src/commands/stream-commands.ts +2 -2
- package/src/providers/acp-provider-instance.ts +39 -55
- package/src/providers/cli-provider-instance.ts +2 -0
- package/src/providers/control-effects.ts +21 -29
- package/src/providers/extension-provider-instance.ts +35 -6
- package/src/providers/ide-provider-instance.ts +2 -1
- package/src/providers/provider-input-support.ts +71 -0
- package/src/providers/provider-schema.ts +60 -1
- package/src/providers/read-chat-contract.ts +136 -0
|
@@ -343,20 +343,49 @@ export class ExtensionProviderInstance implements ProviderInstance {
|
|
|
343
343
|
}
|
|
344
344
|
}
|
|
345
345
|
|
|
346
|
+
private buildSyntheticTurnKey(message: any, occurrence: number): string {
|
|
347
|
+
const role = typeof message?.role === 'string' ? message.role : '';
|
|
348
|
+
const kind = typeof message?.kind === 'string' ? message.kind : '';
|
|
349
|
+
const senderName = typeof message?.senderName === 'string' ? message.senderName : '';
|
|
350
|
+
const content = flattenContent(message?.content)
|
|
351
|
+
.replace(/\s+/g, ' ')
|
|
352
|
+
.trim()
|
|
353
|
+
.slice(0, 500);
|
|
354
|
+
return `${role}|${kind}|${senderName}|${content}|${occurrence}`;
|
|
355
|
+
}
|
|
356
|
+
|
|
346
357
|
/**
|
|
347
|
-
* Assign stable receivedAt to extension messages.
|
|
348
|
-
* Same
|
|
349
|
-
*
|
|
358
|
+
* Assign stable receivedAt / synthetic _turnKey to extension messages.
|
|
359
|
+
* Same transcript should keep the same identity across polling cycles and
|
|
360
|
+
* stream resets, while repeated identical text later in the transcript still
|
|
361
|
+
* produces a distinct completion marker via the occurrence suffix.
|
|
350
362
|
*/
|
|
351
363
|
private assignReceivedAt(messages: any[]): any[] {
|
|
352
364
|
const now = Date.now();
|
|
353
365
|
const nextHashes = new Map<string, number>();
|
|
366
|
+
const occurrenceByBaseKey = new Map<string, number>();
|
|
354
367
|
|
|
355
368
|
for (const msg of messages) {
|
|
356
|
-
const
|
|
357
|
-
|
|
369
|
+
const explicitTurnKey = typeof msg?._turnKey === 'string' && msg._turnKey.trim()
|
|
370
|
+
? msg._turnKey.trim()
|
|
371
|
+
: '';
|
|
372
|
+
const explicitId = typeof msg?.id === 'string' && msg.id.trim()
|
|
373
|
+
? `id:${msg.id.trim()}`
|
|
374
|
+
: '';
|
|
375
|
+
const explicitIndex = typeof msg?.index === 'number' && Number.isFinite(msg.index)
|
|
376
|
+
? `idx:${msg.index}`
|
|
377
|
+
: '';
|
|
378
|
+
const baseKey = explicitTurnKey || explicitId || explicitIndex || `${msg?.role || ''}:${flattenContent(msg?.content || '').slice(0, 500)}`;
|
|
379
|
+
const occurrence = (occurrenceByBaseKey.get(baseKey) || 0) + 1;
|
|
380
|
+
occurrenceByBaseKey.set(baseKey, occurrence);
|
|
381
|
+
const syntheticTurnKey = explicitTurnKey || explicitId || explicitIndex || this.buildSyntheticTurnKey(msg, occurrence);
|
|
382
|
+
if (!explicitTurnKey && !explicitId && !explicitIndex) {
|
|
383
|
+
msg._turnKey = syntheticTurnKey;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const prevTime = this.prevMessageHashes.get(syntheticTurnKey);
|
|
358
387
|
msg.receivedAt = prevTime || now;
|
|
359
|
-
nextHashes.set(
|
|
388
|
+
nextHashes.set(syntheticTurnKey, msg.receivedAt);
|
|
360
389
|
}
|
|
361
390
|
|
|
362
391
|
this.prevMessageHashes = nextHashes;
|
|
@@ -18,6 +18,7 @@ import { StatusMonitor } from './status-monitor.js';
|
|
|
18
18
|
import { ChatHistoryWriter } from '../config/chat-history.js';
|
|
19
19
|
import { LOG } from '../logging/logger.js';
|
|
20
20
|
import { buildPersistedProviderEffectMessage, normalizeProviderEffects } from './control-effects.js';
|
|
21
|
+
import { validateReadChatResultPayload } from './read-chat-contract.js';
|
|
21
22
|
import type { ChatMessage } from '../types.js';
|
|
22
23
|
import { formatAutoApprovalMessage, pickApprovalButton } from './approval-utils.js';
|
|
23
24
|
import { mergeProviderPatchState, resolveProviderStateSurface } from './provider-patch-state.js';
|
|
@@ -311,7 +312,7 @@ export class IdeProviderInstance implements ProviderInstance {
|
|
|
311
312
|
}
|
|
312
313
|
|
|
313
314
|
if (!raw || typeof raw !== 'object') return;
|
|
314
|
-
const chat = raw as ReadChatPayload;
|
|
315
|
+
const chat = validateReadChatResultPayload(raw, `${this.type} readChat`) as ReadChatPayload;
|
|
315
316
|
|
|
316
317
|
// Modal filter
|
|
317
318
|
let { activeModal } = chat;
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { InputEnvelope, ProviderModule } from './contracts.js'
|
|
2
|
+
|
|
3
|
+
type InputMediaType = 'text' | 'image' | 'audio' | 'video' | 'resource'
|
|
4
|
+
|
|
5
|
+
const VALID_INPUT_MEDIA_TYPES = new Set<InputMediaType>(['text', 'image', 'audio', 'video', 'resource'])
|
|
6
|
+
|
|
7
|
+
function getProviderLabel(provider?: Pick<ProviderModule, 'name' | 'type'> | null): string {
|
|
8
|
+
return provider?.name || provider?.type || 'This provider'
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
function hasNonEmptyFallbackText(input: InputEnvelope): boolean {
|
|
12
|
+
return typeof input.textFallback === 'string' && input.textFallback.trim().length > 0
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getRequestedInputMediaTypes(input: InputEnvelope): InputMediaType[] {
|
|
16
|
+
const types = new Set<InputMediaType>()
|
|
17
|
+
if (hasNonEmptyFallbackText(input) && !input.parts.some((part) => part.type === 'text')) {
|
|
18
|
+
types.add('text')
|
|
19
|
+
}
|
|
20
|
+
for (const part of input.parts) {
|
|
21
|
+
if (VALID_INPUT_MEDIA_TYPES.has(part.type as InputMediaType)) {
|
|
22
|
+
types.add(part.type as InputMediaType)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return Array.from(types)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function getEffectiveSemanticPartCount(input: InputEnvelope): number {
|
|
29
|
+
let count = input.parts.length
|
|
30
|
+
if (hasNonEmptyFallbackText(input) && !input.parts.some((part) => part.type === 'text')) {
|
|
31
|
+
count += 1
|
|
32
|
+
}
|
|
33
|
+
return count
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function assertTextOnlyInput(provider: Pick<ProviderModule, 'name' | 'type'> | null | undefined, input: InputEnvelope): void {
|
|
37
|
+
const unsupported = getRequestedInputMediaTypes(input).filter((type) => type !== 'text')
|
|
38
|
+
if (unsupported.length === 0) return
|
|
39
|
+
const label = getProviderLabel(provider)
|
|
40
|
+
const suffix = unsupported.length === 1 ? '' : 's'
|
|
41
|
+
throw new Error(`${label} only supports text input; unsupported input type${suffix}: ${unsupported.join(', ')}`)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export function getDeclaredProviderInputSupport(provider?: Pick<ProviderModule, 'capabilities'> | null): {
|
|
45
|
+
multipart: boolean
|
|
46
|
+
mediaTypes: Set<InputMediaType>
|
|
47
|
+
} {
|
|
48
|
+
const rawMediaTypes = Array.isArray(provider?.capabilities?.input?.mediaTypes)
|
|
49
|
+
? provider?.capabilities?.input?.mediaTypes.filter((type): type is InputMediaType => VALID_INPUT_MEDIA_TYPES.has(type as InputMediaType))
|
|
50
|
+
: []
|
|
51
|
+
|
|
52
|
+
return {
|
|
53
|
+
multipart: provider?.capabilities?.input?.multipart === true,
|
|
54
|
+
mediaTypes: new Set<InputMediaType>(rawMediaTypes.length > 0 ? rawMediaTypes : ['text']),
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function assertProviderSupportsDeclaredInput(provider: Pick<ProviderModule, 'name' | 'type' | 'capabilities'> | null | undefined, input: InputEnvelope): void {
|
|
59
|
+
const label = getProviderLabel(provider)
|
|
60
|
+
const support = getDeclaredProviderInputSupport(provider)
|
|
61
|
+
const requestedTypes = getRequestedInputMediaTypes(input)
|
|
62
|
+
const unsupported = requestedTypes.filter((type) => !support.mediaTypes.has(type))
|
|
63
|
+
if (unsupported.length > 0) {
|
|
64
|
+
const suffix = unsupported.length === 1 ? '' : 's'
|
|
65
|
+
throw new Error(`${label} does not support input type${suffix}: ${unsupported.join(', ')}`)
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
if (getEffectiveSemanticPartCount(input) > 1 && !support.multipart) {
|
|
69
|
+
throw new Error(`${label} does not support multipart input`)
|
|
70
|
+
}
|
|
71
|
+
}
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { ProviderControlDef, ProviderControlType, ProviderModule } from './contracts.js'
|
|
2
2
|
|
|
3
|
+
const VALID_CAPABILITY_MEDIA_TYPES = new Set(['text', 'image', 'audio', 'video', 'resource'])
|
|
4
|
+
|
|
3
5
|
const KNOWN_PROVIDER_FIELDS = new Set<string>([
|
|
4
6
|
'type',
|
|
5
7
|
'name',
|
|
@@ -50,6 +52,7 @@ const KNOWN_PROVIDER_FIELDS = new Set<string>([
|
|
|
50
52
|
'sendDelayMs',
|
|
51
53
|
'sendKey',
|
|
52
54
|
'submitStrategy',
|
|
55
|
+
'timeouts',
|
|
53
56
|
'disableUpstream',
|
|
54
57
|
])
|
|
55
58
|
|
|
@@ -88,6 +91,7 @@ export function validateProviderDefinition(raw: unknown): ProviderValidationResu
|
|
|
88
91
|
}
|
|
89
92
|
|
|
90
93
|
const category = provider.category
|
|
94
|
+
const controls = Array.isArray(provider.controls) ? provider.controls : []
|
|
91
95
|
if ((category === 'cli' || category === 'acp')) {
|
|
92
96
|
const spawn = provider.spawn
|
|
93
97
|
const command = spawn && typeof spawn === 'object'
|
|
@@ -110,13 +114,68 @@ export function validateProviderDefinition(raw: unknown): ProviderValidationResu
|
|
|
110
114
|
warnings.push('Extension providers should have extensionId')
|
|
111
115
|
}
|
|
112
116
|
|
|
113
|
-
|
|
117
|
+
validateCapabilities(provider as unknown as ProviderModule, controls, errors)
|
|
118
|
+
|
|
119
|
+
for (const control of controls) {
|
|
114
120
|
validateControl(control as ProviderControlDef, errors)
|
|
115
121
|
}
|
|
116
122
|
|
|
117
123
|
return { errors, warnings }
|
|
118
124
|
}
|
|
119
125
|
|
|
126
|
+
function validateCapabilities(provider: ProviderModule, controls: ProviderControlDef[], errors: string[]): void {
|
|
127
|
+
const capabilities = provider.capabilities
|
|
128
|
+
if (provider.contractVersion === 2) {
|
|
129
|
+
if (!capabilities || typeof capabilities !== 'object') {
|
|
130
|
+
errors.push('contractVersion 2 providers must declare capabilities')
|
|
131
|
+
return
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
if (!capabilities || typeof capabilities !== 'object') {
|
|
135
|
+
return
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const input = capabilities.input
|
|
139
|
+
if (!input || typeof input !== 'object') {
|
|
140
|
+
errors.push('capabilities.input is required')
|
|
141
|
+
} else {
|
|
142
|
+
if (typeof input.multipart !== 'boolean') {
|
|
143
|
+
errors.push('capabilities.input.multipart must be boolean')
|
|
144
|
+
}
|
|
145
|
+
if (!Array.isArray(input.mediaTypes) || input.mediaTypes.length === 0) {
|
|
146
|
+
errors.push('capabilities.input.mediaTypes must be a non-empty array')
|
|
147
|
+
} else if (input.mediaTypes.some((type) => typeof type !== 'string' || !VALID_CAPABILITY_MEDIA_TYPES.has(type))) {
|
|
148
|
+
errors.push(`capabilities.input.mediaTypes must only include: ${Array.from(VALID_CAPABILITY_MEDIA_TYPES).join(', ')}`)
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
const output = capabilities.output
|
|
153
|
+
if (!output || typeof output !== 'object') {
|
|
154
|
+
errors.push('capabilities.output is required')
|
|
155
|
+
} else {
|
|
156
|
+
if (typeof output.richContent !== 'boolean') {
|
|
157
|
+
errors.push('capabilities.output.richContent must be boolean')
|
|
158
|
+
}
|
|
159
|
+
if (!Array.isArray(output.mediaTypes) || output.mediaTypes.length === 0) {
|
|
160
|
+
errors.push('capabilities.output.mediaTypes must be a non-empty array')
|
|
161
|
+
} else if (output.mediaTypes.some((type) => typeof type !== 'string' || !VALID_CAPABILITY_MEDIA_TYPES.has(type))) {
|
|
162
|
+
errors.push(`capabilities.output.mediaTypes must only include: ${Array.from(VALID_CAPABILITY_MEDIA_TYPES).join(', ')}`)
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const controlCapabilities = capabilities.controls
|
|
167
|
+
if (!controlCapabilities || typeof controlCapabilities !== 'object') {
|
|
168
|
+
errors.push('capabilities.controls is required')
|
|
169
|
+
return
|
|
170
|
+
}
|
|
171
|
+
if (typeof controlCapabilities.typedResults !== 'boolean') {
|
|
172
|
+
errors.push('capabilities.controls.typedResults must be boolean')
|
|
173
|
+
}
|
|
174
|
+
if (controls.length > 0 && controlCapabilities.typedResults !== true) {
|
|
175
|
+
errors.push('providers declaring controls must set capabilities.controls.typedResults=true')
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
120
179
|
function validateControl(control: ProviderControlDef, errors: string[]): void {
|
|
121
180
|
if (!control || typeof control !== 'object') {
|
|
122
181
|
errors.push('controls: each control must be an object')
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import type { MessagePart, ModalInfo, ReadChatResult } from './contracts.js'
|
|
2
|
+
import { normalizeMessageParts } from './contracts.js'
|
|
3
|
+
import type { ChatMessage } from '../types.js'
|
|
4
|
+
|
|
5
|
+
const VALID_STATUSES = ['idle', 'generating', 'waiting_approval', 'error', 'panel_hidden', 'streaming', 'long_generating'] as const
|
|
6
|
+
const VALID_ROLES = ['user', 'assistant', 'system', 'human'] as const
|
|
7
|
+
|
|
8
|
+
type ValidStatus = typeof VALID_STATUSES[number]
|
|
9
|
+
type ValidRole = typeof VALID_ROLES[number]
|
|
10
|
+
|
|
11
|
+
function isPlainObject(value: unknown): value is Record<string, unknown> {
|
|
12
|
+
return !!value && typeof value === 'object' && !Array.isArray(value)
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function isFiniteNumber(value: unknown): value is number {
|
|
16
|
+
return typeof value === 'number' && Number.isFinite(value)
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function validateStatus(status: unknown, source: string): ValidStatus {
|
|
20
|
+
if (typeof status !== 'string' || !VALID_STATUSES.includes(status as ValidStatus)) {
|
|
21
|
+
throw new Error(`${source}: status must be one of ${VALID_STATUSES.join(', ')}`)
|
|
22
|
+
}
|
|
23
|
+
return status as ValidStatus
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function validateRole(role: unknown, source: string, index: number): ValidRole {
|
|
27
|
+
if (typeof role !== 'string' || !VALID_ROLES.includes(role as ValidRole)) {
|
|
28
|
+
throw new Error(`${source}: messages[${index}].role must be one of ${VALID_ROLES.join(', ')}`)
|
|
29
|
+
}
|
|
30
|
+
return role as ValidRole
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function validateMessageContent(content: unknown, source: string, index: number): string | MessagePart[] {
|
|
34
|
+
if (typeof content === 'string') return content
|
|
35
|
+
if (Array.isArray(content)) return normalizeMessageParts(content as any)
|
|
36
|
+
throw new Error(`${source}: messages[${index}].content must be a string or structured content array`)
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function validateMessage(message: unknown, source: string, index: number): ChatMessage {
|
|
40
|
+
if (!isPlainObject(message)) {
|
|
41
|
+
throw new Error(`${source}: messages[${index}] must be an object`)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const normalized: ChatMessage = {
|
|
45
|
+
role: validateRole(message.role, source, index),
|
|
46
|
+
content: validateMessageContent(message.content, source, index),
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
if (typeof message.kind === 'string') normalized.kind = message.kind as any
|
|
50
|
+
if (typeof message.id === 'string') normalized.id = message.id
|
|
51
|
+
if (isFiniteNumber(message.index)) normalized.index = message.index
|
|
52
|
+
if (isFiniteNumber(message.timestamp)) normalized.timestamp = message.timestamp
|
|
53
|
+
if (isFiniteNumber(message.receivedAt)) normalized.receivedAt = message.receivedAt
|
|
54
|
+
if (Array.isArray(message.toolCalls)) normalized.toolCalls = message.toolCalls as any
|
|
55
|
+
if (isPlainObject(message.meta)) normalized.meta = message.meta as any
|
|
56
|
+
if (typeof message.senderName === 'string') normalized.senderName = message.senderName
|
|
57
|
+
if (typeof (message as any)._type === 'string') normalized._type = (message as any)._type
|
|
58
|
+
if (typeof (message as any)._sub === 'string') normalized._sub = (message as any)._sub
|
|
59
|
+
|
|
60
|
+
return normalized
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function validateModal(activeModal: unknown, status: ValidStatus, source: string): ModalInfo | null | undefined {
|
|
64
|
+
if (activeModal == null) {
|
|
65
|
+
if (status === 'waiting_approval') {
|
|
66
|
+
throw new Error(`${source}: waiting_approval status requires activeModal with buttons`)
|
|
67
|
+
}
|
|
68
|
+
return activeModal === null ? null : undefined
|
|
69
|
+
}
|
|
70
|
+
if (!isPlainObject(activeModal)) {
|
|
71
|
+
throw new Error(`${source}: activeModal must be an object when provided`)
|
|
72
|
+
}
|
|
73
|
+
if (typeof activeModal.message !== 'string') {
|
|
74
|
+
throw new Error(`${source}: activeModal.message must be a string`)
|
|
75
|
+
}
|
|
76
|
+
if (!Array.isArray(activeModal.buttons) || activeModal.buttons.some((button) => typeof button !== 'string' || !button.trim())) {
|
|
77
|
+
throw new Error(`${source}: activeModal.buttons must be a non-empty string array`)
|
|
78
|
+
}
|
|
79
|
+
const normalized: ModalInfo = {
|
|
80
|
+
message: activeModal.message,
|
|
81
|
+
buttons: activeModal.buttons.map((button) => button.trim()),
|
|
82
|
+
}
|
|
83
|
+
if (isFiniteNumber(activeModal.width)) normalized.width = activeModal.width
|
|
84
|
+
if (isFiniteNumber(activeModal.height)) normalized.height = activeModal.height
|
|
85
|
+
return normalized
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function validateControlValues(controlValues: unknown, source: string): Record<string, string | number | boolean> | undefined {
|
|
89
|
+
if (controlValues === undefined) return undefined
|
|
90
|
+
if (!isPlainObject(controlValues)) {
|
|
91
|
+
throw new Error(`${source}: controlValues must be an object when provided`)
|
|
92
|
+
}
|
|
93
|
+
const normalized: Record<string, string | number | boolean> = {}
|
|
94
|
+
for (const [key, value] of Object.entries(controlValues)) {
|
|
95
|
+
if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {
|
|
96
|
+
throw new Error(`${source}: controlValues.${key} must be string, number, or boolean`)
|
|
97
|
+
}
|
|
98
|
+
normalized[key] = value
|
|
99
|
+
}
|
|
100
|
+
return normalized
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export function validateReadChatResultPayload(raw: unknown, source = 'read_chat'): ReadChatResult & Record<string, unknown> {
|
|
104
|
+
if (!isPlainObject(raw)) {
|
|
105
|
+
throw new Error(`${source}: payload must be an object`)
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
const status = validateStatus(raw.status, source)
|
|
109
|
+
if (!Array.isArray(raw.messages)) {
|
|
110
|
+
throw new Error(`${source}: messages must be an array`)
|
|
111
|
+
}
|
|
112
|
+
const messages = raw.messages.map((message, index) => validateMessage(message, source, index))
|
|
113
|
+
const activeModal = validateModal(raw.activeModal, status, source)
|
|
114
|
+
const controlValues = validateControlValues(raw.controlValues, source)
|
|
115
|
+
|
|
116
|
+
const normalized: ReadChatResult & Record<string, unknown> = {
|
|
117
|
+
status: status as any,
|
|
118
|
+
messages,
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (activeModal !== undefined) normalized.activeModal = activeModal
|
|
122
|
+
if (typeof raw.id === 'string') normalized.id = raw.id
|
|
123
|
+
if (typeof raw.title === 'string') normalized.title = raw.title
|
|
124
|
+
if (typeof raw.agentType === 'string') normalized.agentType = raw.agentType
|
|
125
|
+
if (typeof raw.agentName === 'string') normalized.agentName = raw.agentName
|
|
126
|
+
if (typeof raw.extensionId === 'string') normalized.extensionId = raw.extensionId
|
|
127
|
+
if (typeof raw.inputContent === 'string') normalized.inputContent = raw.inputContent
|
|
128
|
+
if (typeof raw.isVisible === 'boolean') normalized.isVisible = raw.isVisible
|
|
129
|
+
if (typeof raw.isWelcomeScreen === 'boolean') normalized.isWelcomeScreen = raw.isWelcomeScreen
|
|
130
|
+
if (controlValues) normalized.controlValues = controlValues
|
|
131
|
+
if (raw.summaryMetadata !== undefined) normalized.summaryMetadata = raw.summaryMetadata as any
|
|
132
|
+
if (Array.isArray(raw.effects)) normalized.effects = raw.effects as any
|
|
133
|
+
if (typeof raw.providerSessionId === 'string') normalized.providerSessionId = raw.providerSessionId
|
|
134
|
+
|
|
135
|
+
return normalized
|
|
136
|
+
}
|