@adhdev/daemon-core 0.8.52 → 0.8.53
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/commands/cli-manager.d.ts +8 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.js +896 -299
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +887 -300
- package/dist/index.mjs.map +1 -1
- package/dist/providers/acp-provider-instance.d.ts +2 -1
- package/dist/providers/cli-provider-instance.d.ts +3 -1
- package/dist/providers/cli-script-results.d.ts +8 -0
- package/dist/providers/contracts.d.ts +58 -9
- package/dist/providers/control-effects.d.ts +4 -1
- package/dist/providers/io-contracts.d.ts +91 -0
- package/dist/providers/provider-schema.d.ts +5 -0
- package/dist/session-host/runtime-surface.d.ts +16 -0
- package/dist/session-host/startup-restore-policy.d.ts +1 -0
- package/dist/shared-types.d.ts +6 -0
- package/dist/types.d.ts +3 -3
- package/node_modules/@adhdev/session-host-core/dist/index.d.mts +16 -1
- package/node_modules/@adhdev/session-host-core/dist/index.d.ts +16 -1
- package/node_modules/@adhdev/session-host-core/dist/index.js +59 -0
- package/node_modules/@adhdev/session-host-core/dist/index.js.map +1 -1
- package/node_modules/@adhdev/session-host-core/dist/index.mjs +54 -0
- package/node_modules/@adhdev/session-host-core/dist/index.mjs.map +1 -1
- package/node_modules/@adhdev/session-host-core/package.json +1 -1
- package/package.json +1 -1
- package/src/commands/cdp-commands.ts +27 -0
- package/src/commands/chat-commands.ts +7 -2
- package/src/commands/cli-manager.ts +9 -5
- package/src/commands/handler.ts +1 -9
- package/src/commands/stream-commands.ts +26 -36
- package/src/daemon/dev-server.ts +4 -18
- package/src/index.d.ts +3 -0
- package/src/index.ts +12 -1
- package/src/providers/acp-provider-instance.ts +156 -14
- package/src/providers/cli-provider-instance.ts +54 -5
- package/src/providers/cli-script-results.ts +39 -0
- package/src/providers/contracts.ts +72 -19
- package/src/providers/control-effects.ts +86 -1
- package/src/providers/io-contracts.ts +340 -0
- package/src/providers/provider-loader.ts +18 -13
- package/src/providers/provider-schema.ts +154 -0
- package/src/session-host/runtime-surface.ts +80 -0
- package/src/session-host/startup-restore-policy.d.ts +1 -0
- package/src/session-host/startup-restore-policy.js +7 -0
- package/src/session-host/startup-restore-policy.ts +7 -0
- package/src/shared-types.ts +6 -0
- package/src/status/builders.ts +5 -81
- package/src/types.ts +3 -3
|
@@ -33,7 +33,22 @@ export interface ReadChatResult {
|
|
|
33
33
|
}
|
|
34
34
|
|
|
35
35
|
import type { ChatMessage } from '../types.js';
|
|
36
|
-
|
|
36
|
+
import {
|
|
37
|
+
flattenMessageParts,
|
|
38
|
+
normalizeInputEnvelope,
|
|
39
|
+
normalizeMessageParts,
|
|
40
|
+
} from './io-contracts.js';
|
|
41
|
+
export {
|
|
42
|
+
flattenMessageParts,
|
|
43
|
+
normalizeInputEnvelope,
|
|
44
|
+
normalizeMessageParts,
|
|
45
|
+
} from './io-contracts.js';
|
|
46
|
+
import type {
|
|
47
|
+
InputEnvelope,
|
|
48
|
+
InputPart,
|
|
49
|
+
MessagePart,
|
|
50
|
+
} from './io-contracts.js';
|
|
51
|
+
export type { ChatMessage, InputEnvelope, InputPart, MessagePart };
|
|
37
52
|
|
|
38
53
|
export type AgentStatus =
|
|
39
54
|
| 'idle'
|
|
@@ -52,7 +67,7 @@ export interface ModalInfo {
|
|
|
52
67
|
|
|
53
68
|
export interface ProviderEffectMessage {
|
|
54
69
|
role?: 'system' | 'assistant' | 'user';
|
|
55
|
-
content: string |
|
|
70
|
+
content: string | MessagePart[];
|
|
56
71
|
kind?: string;
|
|
57
72
|
senderName?: string;
|
|
58
73
|
}
|
|
@@ -71,7 +86,7 @@ export interface ProviderEffectNotification {
|
|
|
71
86
|
level?: 'info' | 'success' | 'warning';
|
|
72
87
|
channels?: ProviderNotificationChannel[];
|
|
73
88
|
preferenceKey?: ProviderNotificationPreferenceKey;
|
|
74
|
-
bubbleContent?: string |
|
|
89
|
+
bubbleContent?: string | MessagePart[];
|
|
75
90
|
}
|
|
76
91
|
|
|
77
92
|
export interface ProviderEffect {
|
|
@@ -87,9 +102,9 @@ export interface ProviderEffect {
|
|
|
87
102
|
notification?: ProviderEffectNotification;
|
|
88
103
|
}
|
|
89
104
|
|
|
90
|
-
// ───
|
|
105
|
+
// ─── Legacy ACP ContentBlock Types (compatibility adapter) ─────────────────
|
|
91
106
|
// Based on ACP SDK v0.16.1 schema types.
|
|
92
|
-
//
|
|
107
|
+
// Internal runtime code should prefer MessagePart/InputEnvelope from io-contracts.ts.
|
|
93
108
|
|
|
94
109
|
/**
|
|
95
110
|
* ContentBlock — ACP ContentBlock union type
|
|
@@ -192,29 +207,25 @@ export interface ToolCallLocation {
|
|
|
192
207
|
|
|
193
208
|
// ─── Content Helpers ────────────────────────────────────
|
|
194
209
|
|
|
195
|
-
/** Normalize content
|
|
196
|
-
export function normalizeContent(content: string | ContentBlock[]):
|
|
197
|
-
|
|
198
|
-
return [{ type: 'text', text: content }];
|
|
199
|
-
}
|
|
200
|
-
return content;
|
|
210
|
+
/** Normalize content into canonical message parts */
|
|
211
|
+
export function normalizeContent(content: string | MessagePart[] | ContentBlock[]): MessagePart[] {
|
|
212
|
+
return normalizeMessageParts(content);
|
|
201
213
|
}
|
|
202
214
|
|
|
203
|
-
/** Flatten
|
|
204
|
-
export function flattenContent(content: string | ContentBlock[]): string {
|
|
215
|
+
/** Flatten canonical/legacy content into a plain-text fallback string */
|
|
216
|
+
export function flattenContent(content: string | MessagePart[] | ContentBlock[]): string {
|
|
205
217
|
if (typeof content === 'string') return content;
|
|
206
|
-
return content
|
|
207
|
-
.filter((b): b is TextBlock => b.type === 'text')
|
|
208
|
-
.map(b => b.text)
|
|
209
|
-
.join('\n');
|
|
218
|
+
return flattenMessageParts(normalizeMessageParts(content));
|
|
210
219
|
}
|
|
211
220
|
|
|
212
|
-
/** SendMessage params —
|
|
221
|
+
/** SendMessage params — canonical input envelope with legacy text/prompt compatibility */
|
|
213
222
|
export interface SendMessageParams {
|
|
214
223
|
/** Shortcut: text-only message */
|
|
215
224
|
text?: string;
|
|
216
|
-
/** Rich content blocks (ACP ContentBlock[]) */
|
|
225
|
+
/** Rich content blocks (legacy ACP ContentBlock[]) */
|
|
217
226
|
prompt?: ContentBlock[];
|
|
227
|
+
/** Canonical multipart runtime input */
|
|
228
|
+
input?: InputEnvelope;
|
|
218
229
|
}
|
|
219
230
|
|
|
220
231
|
// ─── sendMessage() return value ────────────────────────
|
|
@@ -332,6 +343,12 @@ export interface ProviderModule {
|
|
|
332
343
|
icon?: string;
|
|
333
344
|
/** Display name (short name) */
|
|
334
345
|
displayName?: string;
|
|
346
|
+
/** Provider-definition version maintained in adhdev-providers */
|
|
347
|
+
providerVersion?: string;
|
|
348
|
+
/** Inventory/support status label maintained in adhdev-providers */
|
|
349
|
+
status?: string;
|
|
350
|
+
/** Inventory/support detail string maintained in adhdev-providers */
|
|
351
|
+
details?: string;
|
|
335
352
|
/** Install instructions (shown when command is missing) */
|
|
336
353
|
install?: string;
|
|
337
354
|
/** Custom version detection command (e.g. 'cursor --version', 'claude -v') */
|
|
@@ -390,6 +407,14 @@ export interface ProviderModule {
|
|
|
390
407
|
shell?: boolean;
|
|
391
408
|
env?: Record<string, string>;
|
|
392
409
|
};
|
|
410
|
+
/** Delay before submitting typed CLI input (provider-specific TUI tuning) */
|
|
411
|
+
sendDelayMs?: number;
|
|
412
|
+
/** Submit key used after typing into CLI PTY (default: carriage return) */
|
|
413
|
+
sendKey?: string;
|
|
414
|
+
/** How the CLI adapter decides when to submit typed input */
|
|
415
|
+
submitStrategy?: 'wait_for_echo' | 'immediate';
|
|
416
|
+
/** Keep this provider out of the upstream auto-updated bundle */
|
|
417
|
+
disableUpstream?: boolean;
|
|
393
418
|
approvalKeys?: Record<number, string>;
|
|
394
419
|
patterns?: {
|
|
395
420
|
prompt?: RegExp[];
|
|
@@ -469,6 +494,14 @@ export interface ProviderModule {
|
|
|
469
494
|
// ─── ACP Authentication (auth method definitions) ───
|
|
470
495
|
/** ACP agent auth methods (multiple supported — in priority order) */
|
|
471
496
|
auth?: AcpAuthMethod[];
|
|
497
|
+
|
|
498
|
+
// ─── Contract version / capability declaration ───
|
|
499
|
+
contractVersion?: number;
|
|
500
|
+
capabilities?: {
|
|
501
|
+
input?: { multipart?: boolean; mediaTypes?: Array<'text' | 'image' | 'audio' | 'video' | 'resource'> };
|
|
502
|
+
output?: { richContent?: boolean; mediaTypes?: Array<'text' | 'image' | 'audio' | 'video' | 'resource'> };
|
|
503
|
+
controls?: { typedResults?: boolean };
|
|
504
|
+
};
|
|
472
505
|
}
|
|
473
506
|
|
|
474
507
|
export interface ProviderResumeCapability {
|
|
@@ -674,6 +707,26 @@ export interface ProviderControlOption {
|
|
|
674
707
|
group?: string;
|
|
675
708
|
}
|
|
676
709
|
|
|
710
|
+
export interface ControlListResult {
|
|
711
|
+
options: ProviderControlOption[];
|
|
712
|
+
currentValue?: string | number | boolean;
|
|
713
|
+
error?: string;
|
|
714
|
+
}
|
|
715
|
+
|
|
716
|
+
export interface ControlSetResult {
|
|
717
|
+
ok: boolean;
|
|
718
|
+
currentValue?: string | number | boolean;
|
|
719
|
+
effects?: ProviderEffect[];
|
|
720
|
+
error?: string;
|
|
721
|
+
}
|
|
722
|
+
|
|
723
|
+
export interface ControlInvokeResult {
|
|
724
|
+
ok: boolean;
|
|
725
|
+
currentValue?: string | number | boolean;
|
|
726
|
+
effects?: ProviderEffect[];
|
|
727
|
+
error?: string;
|
|
728
|
+
}
|
|
729
|
+
|
|
677
730
|
/**
|
|
678
731
|
* ProviderControlDef — A single interactive control declared by a provider.
|
|
679
732
|
*
|
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type {
|
|
2
|
+
ControlInvokeResult,
|
|
3
|
+
ControlListResult,
|
|
4
|
+
ControlSetResult,
|
|
5
|
+
ProviderControlDef,
|
|
6
|
+
ProviderControlOption,
|
|
7
|
+
ProviderEffect,
|
|
8
|
+
} from './contracts.js';
|
|
2
9
|
|
|
3
10
|
export type ProviderControlValue = string | number | boolean;
|
|
4
11
|
|
|
@@ -101,6 +108,84 @@ export function normalizeProviderEffects(data: any): ProviderEffect[] {
|
|
|
101
108
|
return effects;
|
|
102
109
|
}
|
|
103
110
|
|
|
111
|
+
export function normalizeControlListResult(data: any): ControlListResult {
|
|
112
|
+
if (data && typeof data === 'object' && Array.isArray(data.options)) {
|
|
113
|
+
return {
|
|
114
|
+
options: normalizeControlOptions(data.options),
|
|
115
|
+
...(isScalarControlValue(data.currentValue) ? { currentValue: data.currentValue } : {}),
|
|
116
|
+
...(typeof data.error === 'string' ? { error: data.error } : {}),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const rawOptions = Array.isArray(data?.models)
|
|
121
|
+
? data.models
|
|
122
|
+
: Array.isArray(data?.modes)
|
|
123
|
+
? data.modes
|
|
124
|
+
: Array.isArray(data?.options)
|
|
125
|
+
? data.options
|
|
126
|
+
: [];
|
|
127
|
+
const options = normalizeControlOptions(rawOptions);
|
|
128
|
+
return {
|
|
129
|
+
options,
|
|
130
|
+
...(isScalarControlValue(data?.current) ? { currentValue: data.current } : {}),
|
|
131
|
+
...(isScalarControlValue(data?.currentValue) ? { currentValue: data.currentValue } : {}),
|
|
132
|
+
...(typeof data?.error === 'string' ? { error: data.error } : {}),
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function normalizeControlSetResult(data: any): ControlSetResult {
|
|
137
|
+
const currentValue = isScalarControlValue(data?.currentValue)
|
|
138
|
+
? data.currentValue
|
|
139
|
+
: (isScalarControlValue(data?.value) ? data.value : undefined);
|
|
140
|
+
return {
|
|
141
|
+
ok: data?.ok === true || data?.success === true,
|
|
142
|
+
...(currentValue !== undefined ? { currentValue } : {}),
|
|
143
|
+
...(Array.isArray(data?.effects) ? { effects: normalizeProviderEffects(data) } : {}),
|
|
144
|
+
...(typeof data?.error === 'string' ? { error: data.error } : {}),
|
|
145
|
+
};
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
export function normalizeControlInvokeResult(data: any): ControlInvokeResult {
|
|
149
|
+
const currentValue = isScalarControlValue(data?.currentValue)
|
|
150
|
+
? data.currentValue
|
|
151
|
+
: (isScalarControlValue(data?.value) ? data.value : undefined);
|
|
152
|
+
return {
|
|
153
|
+
ok: data?.ok === true || data?.success === true,
|
|
154
|
+
...(currentValue !== undefined ? { currentValue } : {}),
|
|
155
|
+
...(Array.isArray(data?.effects) ? { effects: normalizeProviderEffects(data) } : {}),
|
|
156
|
+
...(typeof data?.error === 'string' ? { error: data.error } : {}),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function normalizeControlOptions(options: unknown[]): ProviderControlOption[] {
|
|
161
|
+
return options
|
|
162
|
+
.map((option) => normalizeControlOption(option))
|
|
163
|
+
.filter((option): option is ProviderControlOption => !!option);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
function normalizeControlOption(option: unknown): ProviderControlOption | null {
|
|
167
|
+
if (typeof option === 'string') {
|
|
168
|
+
return { value: option, label: option };
|
|
169
|
+
}
|
|
170
|
+
if (!option || typeof option !== 'object') return null;
|
|
171
|
+
const record = option as Record<string, unknown>;
|
|
172
|
+
const value = typeof record.value === 'string'
|
|
173
|
+
? record.value
|
|
174
|
+
: (typeof record.id === 'string' ? record.id : null);
|
|
175
|
+
if (!value) return null;
|
|
176
|
+
const label = typeof record.label === 'string'
|
|
177
|
+
? record.label
|
|
178
|
+
: (typeof record.name === 'string' ? record.name : value);
|
|
179
|
+
const normalized: ProviderControlOption = { value, label };
|
|
180
|
+
if (typeof record.description === 'string') normalized.description = record.description;
|
|
181
|
+
if (typeof record.group === 'string') normalized.group = record.group;
|
|
182
|
+
return normalized;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function isScalarControlValue(value: unknown): value is ProviderControlValue {
|
|
186
|
+
return typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean';
|
|
187
|
+
}
|
|
188
|
+
|
|
104
189
|
function normalizeControlValue(value: any): ProviderControlValue {
|
|
105
190
|
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
|
|
106
191
|
return value;
|
|
@@ -0,0 +1,340 @@
|
|
|
1
|
+
import type { ContentAnnotations } from './contracts.js'
|
|
2
|
+
|
|
3
|
+
export type InputPart =
|
|
4
|
+
| TextInputPart
|
|
5
|
+
| ImageInputPart
|
|
6
|
+
| AudioInputPart
|
|
7
|
+
| VideoInputPart
|
|
8
|
+
| ResourceInputPart
|
|
9
|
+
|
|
10
|
+
export interface TextInputPart {
|
|
11
|
+
type: 'text'
|
|
12
|
+
text: string
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface ImageInputPart {
|
|
16
|
+
type: 'image'
|
|
17
|
+
mimeType: string
|
|
18
|
+
uri?: string
|
|
19
|
+
data?: string
|
|
20
|
+
alt?: string
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface AudioInputPart {
|
|
24
|
+
type: 'audio'
|
|
25
|
+
mimeType: string
|
|
26
|
+
uri?: string
|
|
27
|
+
data?: string
|
|
28
|
+
transcript?: string
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface VideoInputPart {
|
|
32
|
+
type: 'video'
|
|
33
|
+
mimeType: string
|
|
34
|
+
uri?: string
|
|
35
|
+
data?: string
|
|
36
|
+
posterUri?: string
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export interface ResourceInputPart {
|
|
40
|
+
type: 'resource'
|
|
41
|
+
uri: string
|
|
42
|
+
mimeType?: string
|
|
43
|
+
name?: string
|
|
44
|
+
text?: string
|
|
45
|
+
data?: string
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export interface InputEnvelope {
|
|
49
|
+
parts: InputPart[]
|
|
50
|
+
textFallback: string
|
|
51
|
+
metadata?: {
|
|
52
|
+
source?: 'dashboard' | 'shortcut_api' | 'provider_script' | 'session_replay'
|
|
53
|
+
clientTimestamp?: number
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export type MessagePart =
|
|
58
|
+
| TextMessagePart
|
|
59
|
+
| ImageMessagePart
|
|
60
|
+
| AudioMessagePart
|
|
61
|
+
| VideoMessagePart
|
|
62
|
+
| ResourceLinkMessagePart
|
|
63
|
+
| ResourceMessagePart
|
|
64
|
+
|
|
65
|
+
export interface TextMessagePart {
|
|
66
|
+
type: 'text'
|
|
67
|
+
text: string
|
|
68
|
+
annotations?: ContentAnnotations
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface ImageMessagePart {
|
|
72
|
+
type: 'image'
|
|
73
|
+
mimeType: string
|
|
74
|
+
uri?: string
|
|
75
|
+
data?: string
|
|
76
|
+
annotations?: ContentAnnotations
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
export interface AudioMessagePart {
|
|
80
|
+
type: 'audio'
|
|
81
|
+
mimeType: string
|
|
82
|
+
uri?: string
|
|
83
|
+
data?: string
|
|
84
|
+
transcript?: string
|
|
85
|
+
annotations?: ContentAnnotations
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
export interface VideoMessagePart {
|
|
89
|
+
type: 'video'
|
|
90
|
+
mimeType: string
|
|
91
|
+
uri?: string
|
|
92
|
+
data?: string
|
|
93
|
+
posterUri?: string
|
|
94
|
+
annotations?: ContentAnnotations
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
export interface ResourceLinkMessagePart {
|
|
98
|
+
type: 'resource_link'
|
|
99
|
+
uri: string
|
|
100
|
+
name: string
|
|
101
|
+
mimeType?: string
|
|
102
|
+
size?: number
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface ResourceMessagePart {
|
|
106
|
+
type: 'resource'
|
|
107
|
+
resource: {
|
|
108
|
+
uri: string
|
|
109
|
+
mimeType?: string | null
|
|
110
|
+
text?: string
|
|
111
|
+
blob?: string
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export function normalizeInputEnvelope(input: unknown): InputEnvelope {
|
|
116
|
+
const normalized = normalizeInputEnvelopePayload(input)
|
|
117
|
+
const textFallback = normalized.textFallback ?? flattenInputParts(normalized.parts)
|
|
118
|
+
return {
|
|
119
|
+
parts: normalized.parts,
|
|
120
|
+
textFallback,
|
|
121
|
+
...(normalized.metadata ? { metadata: normalized.metadata } : {}),
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function normalizeMessageParts(content: unknown): MessagePart[] {
|
|
126
|
+
if (typeof content === 'string') return [{ type: 'text', text: content }]
|
|
127
|
+
if (!Array.isArray(content)) {
|
|
128
|
+
if (content && typeof content === 'object' && typeof (content as { text?: unknown }).text === 'string') {
|
|
129
|
+
return [{ type: 'text', text: String((content as { text: string }).text) }]
|
|
130
|
+
}
|
|
131
|
+
return []
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
const parts: MessagePart[] = []
|
|
135
|
+
for (const raw of content) {
|
|
136
|
+
if (typeof raw === 'string') {
|
|
137
|
+
parts.push({ type: 'text', text: raw })
|
|
138
|
+
continue
|
|
139
|
+
}
|
|
140
|
+
if (!raw || typeof raw !== 'object') continue
|
|
141
|
+
const part = normalizeMessagePartObject(raw)
|
|
142
|
+
if (part) parts.push(part)
|
|
143
|
+
}
|
|
144
|
+
return parts
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
export function flattenMessageParts(parts: MessagePart[]): string {
|
|
148
|
+
return parts
|
|
149
|
+
.map((part) => {
|
|
150
|
+
if (part.type === 'text') return part.text
|
|
151
|
+
if (part.type === 'resource') return part.resource.text || ''
|
|
152
|
+
return ''
|
|
153
|
+
})
|
|
154
|
+
.filter((value) => value.length > 0)
|
|
155
|
+
.join('\n')
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function normalizeInputEnvelopePayload(input: unknown): Omit<InputEnvelope, 'textFallback'> & { textFallback?: string } {
|
|
159
|
+
if (typeof input === 'string') {
|
|
160
|
+
return { parts: [{ type: 'text', text: input }], textFallback: input }
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (!input || typeof input !== 'object') {
|
|
164
|
+
return { parts: [], textFallback: '' }
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const record = input as Record<string, unknown>
|
|
168
|
+
const nestedInput = record.input
|
|
169
|
+
if (nestedInput && typeof nestedInput === 'object') {
|
|
170
|
+
const nested = nestedInput as Record<string, unknown>
|
|
171
|
+
return {
|
|
172
|
+
parts: normalizeInputParts(nested.parts ?? nested.prompt),
|
|
173
|
+
textFallback: typeof nested.textFallback === 'string' ? nested.textFallback : undefined,
|
|
174
|
+
metadata: normalizeInputMetadata(nested.metadata),
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
const directText = typeof record.text === 'string'
|
|
179
|
+
? record.text
|
|
180
|
+
: (typeof record.message === 'string' ? record.message : undefined)
|
|
181
|
+
if (directText !== undefined) {
|
|
182
|
+
return { parts: [{ type: 'text', text: directText }], textFallback: directText }
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const directParts = normalizeInputParts(record.parts ?? record.prompt)
|
|
186
|
+
return {
|
|
187
|
+
parts: directParts,
|
|
188
|
+
textFallback: typeof record.textFallback === 'string' ? record.textFallback : undefined,
|
|
189
|
+
metadata: normalizeInputMetadata(record.metadata),
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function normalizeInputMetadata(value: unknown): InputEnvelope['metadata'] | undefined {
|
|
194
|
+
if (!value || typeof value !== 'object') return undefined
|
|
195
|
+
const record = value as Record<string, unknown>
|
|
196
|
+
const metadata: InputEnvelope['metadata'] = {}
|
|
197
|
+
if (record.source === 'dashboard' || record.source === 'shortcut_api' || record.source === 'provider_script' || record.source === 'session_replay') {
|
|
198
|
+
metadata.source = record.source
|
|
199
|
+
}
|
|
200
|
+
if (typeof record.clientTimestamp === 'number' && Number.isFinite(record.clientTimestamp)) {
|
|
201
|
+
metadata.clientTimestamp = record.clientTimestamp
|
|
202
|
+
}
|
|
203
|
+
return Object.keys(metadata).length > 0 ? metadata : undefined
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function normalizeInputParts(value: unknown): InputPart[] {
|
|
207
|
+
if (!Array.isArray(value)) return []
|
|
208
|
+
const parts: InputPart[] = []
|
|
209
|
+
for (const raw of value) {
|
|
210
|
+
if (typeof raw === 'string') {
|
|
211
|
+
parts.push({ type: 'text', text: raw })
|
|
212
|
+
continue
|
|
213
|
+
}
|
|
214
|
+
if (!raw || typeof raw !== 'object') continue
|
|
215
|
+
const part = normalizeInputPartObject(raw)
|
|
216
|
+
if (part) parts.push(part)
|
|
217
|
+
}
|
|
218
|
+
return parts
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
function normalizeInputPartObject(raw: Record<string, unknown>): InputPart | null {
|
|
222
|
+
const type = raw.type
|
|
223
|
+
if (type === 'text' && typeof raw.text === 'string') {
|
|
224
|
+
return { type, text: raw.text }
|
|
225
|
+
}
|
|
226
|
+
if (type === 'image' && typeof raw.mimeType === 'string') {
|
|
227
|
+
return {
|
|
228
|
+
type,
|
|
229
|
+
mimeType: raw.mimeType,
|
|
230
|
+
...(typeof raw.uri === 'string' ? { uri: raw.uri } : {}),
|
|
231
|
+
...(typeof raw.data === 'string' ? { data: raw.data } : {}),
|
|
232
|
+
...(typeof raw.alt === 'string' ? { alt: raw.alt } : {}),
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
if (type === 'audio' && typeof raw.mimeType === 'string') {
|
|
236
|
+
return {
|
|
237
|
+
type,
|
|
238
|
+
mimeType: raw.mimeType,
|
|
239
|
+
...(typeof raw.uri === 'string' ? { uri: raw.uri } : {}),
|
|
240
|
+
...(typeof raw.data === 'string' ? { data: raw.data } : {}),
|
|
241
|
+
...(typeof raw.transcript === 'string' ? { transcript: raw.transcript } : {}),
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
if (type === 'video' && typeof raw.mimeType === 'string') {
|
|
245
|
+
return {
|
|
246
|
+
type,
|
|
247
|
+
mimeType: raw.mimeType,
|
|
248
|
+
...(typeof raw.uri === 'string' ? { uri: raw.uri } : {}),
|
|
249
|
+
...(typeof raw.data === 'string' ? { data: raw.data } : {}),
|
|
250
|
+
...(typeof raw.posterUri === 'string' ? { posterUri: raw.posterUri } : {}),
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
if (type === 'resource' && typeof raw.uri === 'string') {
|
|
254
|
+
return {
|
|
255
|
+
type,
|
|
256
|
+
uri: raw.uri,
|
|
257
|
+
...(typeof raw.mimeType === 'string' ? { mimeType: raw.mimeType } : {}),
|
|
258
|
+
...(typeof raw.name === 'string' ? { name: raw.name } : {}),
|
|
259
|
+
...(typeof raw.text === 'string' ? { text: raw.text } : {}),
|
|
260
|
+
...(typeof raw.data === 'string' ? { data: raw.data } : {}),
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
if (type === 'resource_link' && typeof raw.uri === 'string') {
|
|
264
|
+
return {
|
|
265
|
+
type: 'resource',
|
|
266
|
+
uri: raw.uri,
|
|
267
|
+
...(typeof raw.mimeType === 'string' ? { mimeType: raw.mimeType } : {}),
|
|
268
|
+
...(typeof raw.name === 'string' ? { name: raw.name } : {}),
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
return null
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
function normalizeMessagePartObject(raw: Record<string, unknown>): MessagePart | null {
|
|
275
|
+
const type = raw.type
|
|
276
|
+
if (type === 'text' && typeof raw.text === 'string') {
|
|
277
|
+
return { type, text: raw.text }
|
|
278
|
+
}
|
|
279
|
+
if (type === 'image' && typeof raw.mimeType === 'string') {
|
|
280
|
+
return {
|
|
281
|
+
type,
|
|
282
|
+
mimeType: raw.mimeType,
|
|
283
|
+
...(typeof raw.uri === 'string' ? { uri: raw.uri } : {}),
|
|
284
|
+
...(typeof raw.data === 'string' ? { data: raw.data } : {}),
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
if (type === 'audio' && typeof raw.mimeType === 'string') {
|
|
288
|
+
return {
|
|
289
|
+
type,
|
|
290
|
+
mimeType: raw.mimeType,
|
|
291
|
+
...(typeof raw.uri === 'string' ? { uri: raw.uri } : {}),
|
|
292
|
+
...(typeof raw.data === 'string' ? { data: raw.data } : {}),
|
|
293
|
+
...(typeof raw.transcript === 'string' ? { transcript: raw.transcript } : {}),
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
if (type === 'video' && typeof raw.mimeType === 'string') {
|
|
297
|
+
return {
|
|
298
|
+
type,
|
|
299
|
+
mimeType: raw.mimeType,
|
|
300
|
+
...(typeof raw.uri === 'string' ? { uri: raw.uri } : {}),
|
|
301
|
+
...(typeof raw.data === 'string' ? { data: raw.data } : {}),
|
|
302
|
+
...(typeof raw.posterUri === 'string' ? { posterUri: raw.posterUri } : {}),
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (type === 'resource_link' && typeof raw.uri === 'string' && typeof raw.name === 'string') {
|
|
306
|
+
return {
|
|
307
|
+
type,
|
|
308
|
+
uri: raw.uri,
|
|
309
|
+
name: raw.name,
|
|
310
|
+
...(typeof raw.mimeType === 'string' ? { mimeType: raw.mimeType } : {}),
|
|
311
|
+
...(typeof raw.size === 'number' ? { size: raw.size } : {}),
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
if (type === 'resource' && raw.resource && typeof raw.resource === 'object') {
|
|
315
|
+
const resource = raw.resource as Record<string, unknown>
|
|
316
|
+
if (typeof resource.uri !== 'string') return null
|
|
317
|
+
return {
|
|
318
|
+
type,
|
|
319
|
+
resource: {
|
|
320
|
+
uri: resource.uri,
|
|
321
|
+
...(typeof resource.mimeType === 'string' || resource.mimeType === null ? { mimeType: resource.mimeType as string | null } : {}),
|
|
322
|
+
...(typeof resource.text === 'string' ? { text: resource.text } : {}),
|
|
323
|
+
...(typeof resource.blob === 'string' ? { blob: resource.blob } : {}),
|
|
324
|
+
},
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
return null
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function flattenInputParts(parts: InputPart[]): string {
|
|
331
|
+
return parts
|
|
332
|
+
.map((part) => {
|
|
333
|
+
if (part.type === 'text') return part.text
|
|
334
|
+
if (part.type === 'audio') return part.transcript || ''
|
|
335
|
+
if (part.type === 'resource') return part.text || ''
|
|
336
|
+
return ''
|
|
337
|
+
})
|
|
338
|
+
.filter((value) => value.length > 0)
|
|
339
|
+
.join('\n')
|
|
340
|
+
}
|
|
@@ -29,6 +29,7 @@ import type {
|
|
|
29
29
|
ProviderSettingSchema,
|
|
30
30
|
ResolvedProvider,
|
|
31
31
|
} from './contracts.js';
|
|
32
|
+
import { validateProviderDefinition } from './provider-schema.js';
|
|
32
33
|
|
|
33
34
|
interface ProviderAvailabilityState {
|
|
34
35
|
installed: boolean;
|
|
@@ -1288,20 +1289,24 @@ export class ProviderLoader {
|
|
|
1288
1289
|
extensionIdPattern?: RegExp | string;
|
|
1289
1290
|
};
|
|
1290
1291
|
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
...providerFields,
|
|
1302
|
-
...(extensionIdPattern instanceof RegExp ? { extensionIdPattern } : {}),
|
|
1303
|
-
};
|
|
1292
|
+
// Restore RegExp fields from JSON (extensionIdPattern)
|
|
1293
|
+
if (typeof mod.extensionIdPattern === 'string') {
|
|
1294
|
+
const flags = mod.extensionIdPattern_flags || '';
|
|
1295
|
+
mod.extensionIdPattern = new RegExp(mod.extensionIdPattern, flags);
|
|
1296
|
+
}
|
|
1297
|
+
const { extensionIdPattern_flags, extensionIdPattern, ...providerFields } = mod;
|
|
1298
|
+
const normalizedProvider: ProviderModule = {
|
|
1299
|
+
...providerFields,
|
|
1300
|
+
...(extensionIdPattern instanceof RegExp ? { extensionIdPattern } : {}),
|
|
1301
|
+
};
|
|
1304
1302
|
|
|
1303
|
+
const validation = validateProviderDefinition(normalizedProvider);
|
|
1304
|
+
for (const warning of validation.warnings) {
|
|
1305
|
+
this.log(`⚠ ${jsonPath}: ${warning}`);
|
|
1306
|
+
}
|
|
1307
|
+
if (validation.errors.length > 0) {
|
|
1308
|
+
this.log(`⚠ Invalid provider at ${jsonPath}: ${validation.errors.join('; ')}`);
|
|
1309
|
+
} else {
|
|
1305
1310
|
// Load scripts.js if exists (IDE/Extension)
|
|
1306
1311
|
// Skip for compatibility-format providers — scripts loaded lazily in resolve()
|
|
1307
1312
|
const hasCompatibility = Array.isArray(normalizedProvider.compatibility);
|