@nekro-nxt/contracts 0.0.0-stage → 0.2.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.
@@ -0,0 +1,3468 @@
1
+ import { z } from 'zod'
2
+ import { McpServerFormSchema } from './extension-capabilities.js'
3
+ import { HostUpgradeSummarySchema, RuntimeCompatibilityDiagnosticSchema } from './runtime-compatibility.js'
4
+ import {
5
+ AgentIdSchema,
6
+ AgentRevisionIdSchema,
7
+ AuthoringAttemptIdSchema,
8
+ AuthoringTaskIdSchema,
9
+ AssetIdSchema,
10
+ ChannelEventIdSchema,
11
+ AdapterActivityKeySchema,
12
+ ChannelIdSchema,
13
+ ChannelMemberIdSchema,
14
+ ConnectionIdSchema,
15
+ ConnectionEventIdSchema,
16
+ DshPluginEntryIdSchema,
17
+ DshPluginPackageIdSchema,
18
+ ExtensionIdSchema,
19
+ ExtensionRevisionIdSchema,
20
+ HostUiPageInstanceIdSchema,
21
+ EpisodeIdSchema,
22
+ JsonValueSchema,
23
+ LogicalMessageIdSchema,
24
+ RichExtensionSchema,
25
+ RichTargetUrlSchema,
26
+ OutboundIntentIdSchema,
27
+ PlatformIdentityIdSchema,
28
+ PromptDocumentV1Schema,
29
+ promptDocumentPlainText,
30
+ } from './domain.js'
31
+ import { ClientNotificationSchema, ManagementDeviceIdSchema, ManagementDeviceViewSchema } from './management-api.js'
32
+ import { ConfigSchemaDocumentSchema } from './config-schema.js'
33
+ import {
34
+ CommunityExtensionDetailSchema,
35
+ CommunityExtensionSummarySchema,
36
+ CommunityReleaseIdSchema,
37
+ CommunityReviewStatusSchema,
38
+ CommunityStatusSchema,
39
+ CommunitySourceSchema,
40
+ CommunityEndpointSchema,
41
+ CommunityInstalledSchema,
42
+ CommunityMyExtensionSchema,
43
+ CommunityReviewReportSchema,
44
+ } from './community.js'
45
+ import {
46
+ DshNxtHostUiSchema,
47
+ EMPTY_EXTENSION_UI_CONTRIBUTIONS,
48
+ ExtensionUiContributionsSchema,
49
+ PanelContributionSchema,
50
+ RichKindSchema,
51
+ HostPageContributionSchema,
52
+ HostIconNameSchema,
53
+ HostUiKitComponentNameSchema,
54
+ HostUiPageGeometryEvidenceSchema,
55
+ HostUiPageEntrySchema,
56
+ HostUiPermissionDeclarationSchema,
57
+ } from './extension-ui.js'
58
+
59
+ const EmptyParamsSchema = z.object({}).strict()
60
+ const NoRequestBodySchema = z.undefined()
61
+ const NonEmptyStringSchema = z.string().trim().min(1)
62
+ const DynamicIdSchema = NonEmptyStringSchema
63
+
64
+ const DynamicAttemptStatusSchema = z.enum([
65
+ 'awaiting-approval',
66
+ 'starting-host',
67
+ 'client-pending',
68
+ 'running',
69
+ 'waiting',
70
+ 'rejected',
71
+ 'failed',
72
+ 'cancelled',
73
+ 'stopped',
74
+ ])
75
+
76
+ const DynamicHalfStateSchema = z
77
+ .object({
78
+ status: z.enum(['absent', 'pending', 'stopped', 'running', 'waiting', 'failed']),
79
+ waitingFor: z.array(z.string()),
80
+ error: z.string().optional(),
81
+ })
82
+ .strict()
83
+
84
+ const DynamicAttemptErrorSchema = z
85
+ .object({
86
+ phase: z.enum(['approval', 'host-load', 'host-apply', 'client-load', 'client-apply', 'client-render']),
87
+ message: z.string(),
88
+ stack: z.string().optional(),
89
+ pluginId: DynamicIdSchema,
90
+ packageId: DynamicIdSchema,
91
+ pluginRunId: DynamicIdSchema,
92
+ })
93
+ .strict()
94
+
95
+ const DynamicLatestRunSchema = z
96
+ .object({
97
+ pluginRunId: DynamicIdSchema,
98
+ packageId: DynamicIdSchema,
99
+ mode: z.enum(['run', 'update']),
100
+ status: DynamicAttemptStatusSchema,
101
+ approvalRequestId: DynamicIdSchema.optional(),
102
+ requiresApproval: z.boolean().optional(),
103
+ host: DynamicHalfStateSchema,
104
+ client: DynamicHalfStateSchema,
105
+ error: DynamicAttemptErrorSchema.optional(),
106
+ })
107
+ .strict()
108
+
109
+ const AuthoringTaskStatusSchema = z.enum([
110
+ 'working',
111
+ 'awaiting-approval',
112
+ 'running',
113
+ 'ready',
114
+ 'repairing',
115
+ 'failed',
116
+ 'interrupted',
117
+ 'stopped',
118
+ 'completed',
119
+ ])
120
+
121
+ const AuthoringAttemptStateSchema = z.enum([
122
+ 'drafting',
123
+ 'preflight-failed',
124
+ 'awaiting-approval',
125
+ 'starting-host',
126
+ 'loading-client',
127
+ 'verifying',
128
+ 'active',
129
+ 'failed',
130
+ 'rejected',
131
+ 'stopped',
132
+ ])
133
+
134
+ const AuthoringAttemptSummarySchema = z
135
+ .object({
136
+ id: AuthoringAttemptIdSchema,
137
+ ordinal: z.number().int().positive(),
138
+ name: z.string(),
139
+ purpose: z.string(),
140
+ state: AuthoringAttemptStateSchema,
141
+ riskDigest: z.string().regex(/^[a-f0-9]{64}$/u),
142
+ host: DynamicHalfStateSchema,
143
+ client: DynamicHalfStateSchema,
144
+ error: z
145
+ .object({
146
+ phase: z.enum([
147
+ 'preflight',
148
+ 'approval',
149
+ 'host-load',
150
+ 'host-apply',
151
+ 'client-load',
152
+ 'client-apply',
153
+ 'client-render',
154
+ 'verification',
155
+ 'settlement',
156
+ 'restore',
157
+ ]),
158
+ message: z.string(),
159
+ repairable: z.boolean(),
160
+ })
161
+ .strict()
162
+ .optional(),
163
+ createdAt: z.number().int().nonnegative(),
164
+ settledAt: z.number().int().nonnegative().optional(),
165
+ })
166
+ .strict()
167
+
168
+ const AuthoringTaskSummarySchema = z
169
+ .object({
170
+ id: AuthoringTaskIdSchema,
171
+ agentId: AgentIdSchema,
172
+ channelId: ChannelIdSchema,
173
+ episodeId: EpisodeIdSchema,
174
+ title: z.string(),
175
+ requirementSummary: z.string(),
176
+ status: AuthoringTaskStatusSchema,
177
+ approvalPolicy: z.enum(['risk-stable', 'fully-automatic']),
178
+ approvedRiskDigest: z
179
+ .string()
180
+ .regex(/^[a-f0-9]{64}$/u)
181
+ .optional(),
182
+ revision: z.number().int().positive(),
183
+ activeAttempt: AuthoringAttemptSummarySchema.optional(),
184
+ candidateAttempt: AuthoringAttemptSummarySchema.optional(),
185
+ /** Latest attempt that still carries product verification evidence; it can be restored as the candidate. */
186
+ verifiedAttempt: AuthoringAttemptSummarySchema.optional(),
187
+ createdAt: z.number().int().nonnegative(),
188
+ updatedAt: z.number().int().nonnegative(),
189
+ })
190
+ .strict()
191
+ const ConnectionAliasInputSchema = z
192
+ .string()
193
+ .trim()
194
+ .max(80)
195
+ .transform((value) => value || undefined)
196
+ const ConnectionAliasOutputSchema = z.string().trim().min(1).max(80)
197
+
198
+ export const HostApiErrorSchema = z
199
+ .object({
200
+ error: z
201
+ .object({
202
+ code: NonEmptyStringSchema,
203
+ message: z.string(),
204
+ })
205
+ .strict(),
206
+ })
207
+ .strict()
208
+
209
+ const AgentModelSchema = z
210
+ .object({
211
+ provider: NonEmptyStringSchema,
212
+ model: NonEmptyStringSchema,
213
+ reasoningEffort: NonEmptyStringSchema.optional(),
214
+ })
215
+ .strict()
216
+
217
+ export const ImageUnderstandingPolicyApiSchema = z
218
+ .object({
219
+ history: z
220
+ .object({
221
+ mode: z.literal('persistent-distinct'),
222
+ detail: z.enum(['low', 'auto', 'high']),
223
+ restoreAfterCompaction: z
224
+ .object({
225
+ recentMessages: z.number().int().min(1).max(100),
226
+ maxImages: z.number().int().min(1).max(50),
227
+ })
228
+ .strict(),
229
+ })
230
+ .strict(),
231
+ textModel: z.discriminatedUnion('mode', [
232
+ z.object({ mode: z.literal('disabled') }).strict(),
233
+ z
234
+ .object({
235
+ mode: z.literal('auxiliary'),
236
+ model: AgentModelSchema,
237
+ maxTokens: z.number().int().min(256).max(8192),
238
+ })
239
+ .strict(),
240
+ ]),
241
+ })
242
+ .strict()
243
+
244
+ const AgentCapabilitiesSchema = z
245
+ .object({
246
+ subagents: z.boolean(),
247
+ fileTools: z.boolean(),
248
+ webSearch: z.boolean(),
249
+ dynamicCreation: z.boolean(),
250
+ developmentShell: z.boolean(),
251
+ unrestrictedFileAccess: z.boolean(),
252
+ /** Absent in older clients means on. */
253
+ scheduledTasks: z.boolean().default(true),
254
+ })
255
+ .strict()
256
+
257
+ export const DynamicClientApprovalPolicySchema = z.enum(['manual', 'automatic'])
258
+
259
+ export const NotificationEventKeySchema = z.enum(['dynamic-client-approval-requested'])
260
+ export type NotificationEventKey = z.output<typeof NotificationEventKeySchema>
261
+
262
+ export const NotificationSettingsViewSchema = z
263
+ .object({
264
+ revision: z.number().int().positive().optional(),
265
+ system: z.object({ enabled: z.boolean() }).strict(),
266
+ bark: z
267
+ .object({
268
+ enabled: z.boolean(),
269
+ serverUrl: z.url(),
270
+ deviceKeyConfigured: z.boolean(),
271
+ })
272
+ .strict(),
273
+ events: z.record(NotificationEventKeySchema, z.boolean()),
274
+ })
275
+ .strict()
276
+
277
+ export const UpdateNotificationSettingsRequestSchema = z
278
+ .object({
279
+ expectedRevision: z.number().int().positive().optional(),
280
+ system: z.object({ enabled: z.boolean() }).strict(),
281
+ bark: z
282
+ .object({
283
+ enabled: z.boolean(),
284
+ serverUrl: z.url(),
285
+ deviceKey: z.string().trim().min(1).max(2048).optional(),
286
+ clearDeviceKey: z.boolean().optional(),
287
+ })
288
+ .strict(),
289
+ events: z.record(NotificationEventKeySchema, z.boolean()),
290
+ })
291
+ .strict()
292
+ .refine((value) => !(value.bark.deviceKey !== undefined && value.bark.clearDeviceKey === true), {
293
+ message: '不能同时更新并清除 Bark Device Key。',
294
+ path: ['bark'],
295
+ })
296
+
297
+ export const TestBarkNotificationRequestSchema = z
298
+ .object({ serverUrl: z.url(), deviceKey: z.string().trim().min(1).max(2048).optional() })
299
+ .strict()
300
+
301
+ export const ClientNotificationFeedResponseSchema = z
302
+ .object({
303
+ cursor: z.number().int().nonnegative(),
304
+ notifications: z.array(ClientNotificationSchema),
305
+ })
306
+ .strict()
307
+
308
+ const AgentRevisionRequestContentSchema = z
309
+ .object({
310
+ displayName: z.string().trim().min(1).max(80),
311
+ persona: z
312
+ .string()
313
+ .max(64 * 1024)
314
+ .default(''),
315
+ personaDocument: PromptDocumentV1Schema.optional(),
316
+ model: AgentModelSchema,
317
+ imagePolicy: ImageUnderstandingPolicyApiSchema.optional(),
318
+ dynamicClientApprovalPolicy: DynamicClientApprovalPolicySchema.optional(),
319
+ })
320
+ .strict()
321
+
322
+ const validatePersonaDocumentProjection = (
323
+ value: z.output<typeof AgentRevisionRequestContentSchema>,
324
+ context: z.core.$RefinementCtx<z.output<typeof AgentRevisionRequestContentSchema>>,
325
+ ): void => {
326
+ if (value.personaDocument !== undefined && promptDocumentPlainText(value.personaDocument) !== value.persona) {
327
+ context.addIssue({
328
+ code: 'custom',
329
+ message: '人设文本必须与结构化人设文档一致。',
330
+ path: ['personaDocument'],
331
+ input: value,
332
+ })
333
+ }
334
+ }
335
+
336
+ const CreateAgentRequestSchema = AgentRevisionRequestContentSchema.extend({
337
+ capabilities: AgentCapabilitiesSchema.optional(),
338
+ }).superRefine((value, context) => {
339
+ validatePersonaDocumentProjection(value, context)
340
+ })
341
+
342
+ const ReviseAgentRequestSchema = AgentRevisionRequestContentSchema.extend({
343
+ expectedCurrentRevisionId: AgentRevisionIdSchema,
344
+ }).superRefine((value, context) => {
345
+ validatePersonaDocumentProjection(value, context)
346
+ })
347
+
348
+ // The default that fills `scheduledTasks` for older clients must not turn it on in a partial update.
349
+ const UpdateAgentCapabilitiesRequestSchema = AgentCapabilitiesSchema.partial()
350
+ .extend({ scheduledTasks: z.boolean().optional() })
351
+ .strict()
352
+ .refine((value) => Object.values(value).some((entry) => entry !== undefined), '至少提供一个能力。')
353
+
354
+ const TriggerPolicySchema = z.enum(['always', 'mentioned-or-replied', 'command', 'observe-only'])
355
+
356
+ export const WorkTreeOrderSchema = z
357
+ .object({
358
+ agentIds: z.array(AgentIdSchema),
359
+ channelIdsByAgent: z.record(AgentIdSchema, z.array(ChannelIdSchema)),
360
+ unboundChannelIds: z.array(ChannelIdSchema),
361
+ })
362
+ .strict()
363
+
364
+ export type WorkTreeOrder = z.output<typeof WorkTreeOrderSchema>
365
+
366
+ const SnapshotMessagePartSchema = z.discriminatedUnion('type', [
367
+ z.object({ type: z.literal('text'), text: z.string() }).strict(),
368
+ z
369
+ .object({ type: z.literal('mention'), memberId: ChannelMemberIdSchema, displayName: z.string().optional() })
370
+ .strict(),
371
+ z.object({ type: z.literal('image'), assetId: AssetIdSchema, alt: z.string().optional() }).strict(),
372
+ z.object({ type: z.literal('file'), assetId: AssetIdSchema, name: z.string().optional() }).strict(),
373
+ z.object({ type: z.literal('audio'), assetId: AssetIdSchema }).strict(),
374
+ z.object({ type: z.literal('quote'), messageId: LogicalMessageIdSchema }).strict(),
375
+ z
376
+ .object({
377
+ type: z.literal('rich'),
378
+ adapterKey: z.string().trim().min(1).max(64),
379
+ kind: z.string().trim().min(1).max(64),
380
+ summary: z.string().trim().min(1).max(500),
381
+ title: z.string().trim().min(1).max(200).optional(),
382
+ source: z.string().trim().min(1).max(80).optional(),
383
+ targetUrl: RichTargetUrlSchema.optional(),
384
+ previewAssetId: AssetIdSchema.optional(),
385
+ extension: RichExtensionSchema.optional(),
386
+ })
387
+ .strict(),
388
+ ])
389
+
390
+ export const HostSnapshotMessageSchema = z
391
+ .object({
392
+ id: z.union([ChannelEventIdSchema, OutboundIntentIdSchema]),
393
+ channelId: ChannelIdSchema,
394
+ role: z.enum(['member', 'agent', 'system']),
395
+ parts: z.array(SnapshotMessagePartSchema),
396
+ sender: z.object({ memberId: ChannelMemberIdSchema, displayName: z.string().optional() }).strict().optional(),
397
+ mentionedConnectionAccount: z.boolean().optional(),
398
+ activityKey: AdapterActivityKeySchema.optional(),
399
+ targetLogicalMessageId: LogicalMessageIdSchema.optional(),
400
+ occurredAt: z.number().finite(),
401
+ deliveryState: z.enum(['planned', 'sending', 'sent', 'partially-sent', 'failed', 'unknown']).optional(),
402
+ /** Latest administrator resolution of an unsettled delivery; history keeps the original receipt state. */
403
+ deliveryResolution: z
404
+ .object({
405
+ action: z.enum(['retry', 'confirm-delivered']),
406
+ resolvedAt: z.number().int().safe().nonnegative(),
407
+ })
408
+ .strict()
409
+ .optional(),
410
+ origin: z.enum(['admin-console']).optional(),
411
+ /** What the agent's inbound hooks decided for this message, when anything differs from the default. */
412
+ inboundHook: z
413
+ .object({
414
+ trigger: z.enum(['default', 'suppress', 'force']),
415
+ hidden: z.boolean(),
416
+ annotation: z.string().optional(),
417
+ extensions: z.array(z.string()),
418
+ problems: z.array(z.string()).optional(),
419
+ })
420
+ .strict()
421
+ .optional(),
422
+ /** A scheduled task came due: a Host fact shown as a notice, not a member message. */
423
+ scheduledTask: z
424
+ .object({ label: z.string(), extensionName: z.string().optional(), note: z.string().optional() })
425
+ .strict()
426
+ .optional(),
427
+ })
428
+ .strict()
429
+
430
+ export type HostSnapshotMessage = z.output<typeof HostSnapshotMessageSchema>
431
+
432
+ export const HostConnectionEventSchema = z
433
+ .object({
434
+ id: ConnectionEventIdSchema,
435
+ connectionId: ConnectionIdSchema,
436
+ activityKey: AdapterActivityKeySchema,
437
+ summary: z.string(),
438
+ actor: z.object({ identityId: PlatformIdentityIdSchema, displayName: z.string().optional() }).strict().optional(),
439
+ subject: z.object({ identityId: PlatformIdentityIdSchema, displayName: z.string().optional() }).strict().optional(),
440
+ occurredAt: z.number().int().safe().nonnegative(),
441
+ })
442
+ .strict()
443
+
444
+ export type HostConnectionEvent = z.output<typeof HostConnectionEventSchema>
445
+
446
+ const PlatformUserChannelPreviewSchema = z
447
+ .object({
448
+ id: ChannelIdSchema,
449
+ displayName: z.string().trim().min(1).max(120).optional(),
450
+ kind: z.enum(['internal', 'direct', 'group']),
451
+ })
452
+ .strict()
453
+
454
+ const PlatformUserFacetAdapterSchema = z
455
+ .object({ key: NonEmptyStringSchema, displayName: NonEmptyStringSchema, userCount: z.number().int().nonnegative() })
456
+ .strict()
457
+
458
+ const PlatformUserFacetConnectionSchema = z
459
+ .object({
460
+ id: ConnectionIdSchema,
461
+ adapterKey: NonEmptyStringSchema,
462
+ displayName: NonEmptyStringSchema,
463
+ userCount: z.number().int().nonnegative(),
464
+ })
465
+ .strict()
466
+
467
+ export const PlatformUserListResponseSchema = z
468
+ .object({
469
+ total: z.number().int().nonnegative(),
470
+ items: z.array(
471
+ z
472
+ .object({
473
+ identityId: PlatformIdentityIdSchema,
474
+ displayName: z.string().trim().min(1).max(120).optional(),
475
+ adapter: z.object({ key: NonEmptyStringSchema, displayName: NonEmptyStringSchema }).strict(),
476
+ connection: z.object({ id: ConnectionIdSchema, displayName: NonEmptyStringSchema }).strict(),
477
+ activeChannelCount: z.number().int().nonnegative(),
478
+ channelPreview: z.array(PlatformUserChannelPreviewSchema).max(3),
479
+ historicalOnly: z.boolean(),
480
+ })
481
+ .strict(),
482
+ ),
483
+ facets: z
484
+ .object({
485
+ adapters: z.array(PlatformUserFacetAdapterSchema),
486
+ connections: z.array(PlatformUserFacetConnectionSchema),
487
+ })
488
+ .strict(),
489
+ nextCursor: PlatformIdentityIdSchema.optional(),
490
+ })
491
+ .strict()
492
+
493
+ export type PlatformUserListResponse = z.output<typeof PlatformUserListResponseSchema>
494
+
495
+ export const ChannelFactSseItemSchema = z
496
+ .object({
497
+ kind: z.enum(['inbound', 'outbound']),
498
+ sourceId: z.union([ChannelEventIdSchema, OutboundIntentIdSchema]),
499
+ message: HostSnapshotMessageSchema,
500
+ })
501
+ .strict()
502
+
503
+ export const ChannelFactSseDataSchema = z
504
+ .object({
505
+ channelId: ChannelIdSchema,
506
+ revision: z.number().int().positive(),
507
+ items: z.array(ChannelFactSseItemSchema).min(1),
508
+ })
509
+ .strict()
510
+
511
+ export const ChannelRuntimePhaseSchema = z.enum(['idle', 'thinking', 'using-tool', 'waiting-input', 'unavailable'])
512
+
513
+ export const ChannelRuntimeUsageSchema = z
514
+ .object({
515
+ inputTokens: z.number().int().nonnegative(),
516
+ outputTokens: z.number().int().nonnegative(),
517
+ totalTokens: z.number().int().nonnegative().optional(),
518
+ cacheReadTokens: z.number().int().nonnegative().optional(),
519
+ cacheWriteTokens: z.number().int().nonnegative().optional(),
520
+ reasoningTokens: z.number().int().nonnegative().optional(),
521
+ })
522
+ .strict()
523
+
524
+ export const ChannelRuntimeToolSchema = z
525
+ .object({
526
+ callId: NonEmptyStringSchema,
527
+ name: NonEmptyStringSchema,
528
+ displayName: NonEmptyStringSchema,
529
+ state: z.enum(['running', 'succeeded', 'failed']),
530
+ inputPreview: z.string().optional(),
531
+ resultPreview: z.string().optional(),
532
+ wroteToChannel: z.boolean().optional(),
533
+ deliveryState: z.enum(['sent', 'partially-sent', 'failed', 'unknown']).optional(),
534
+ durationMs: z.number().int().nonnegative().optional(),
535
+ })
536
+ .strict()
537
+
538
+ export const ChannelRuntimeStepSchema = z
539
+ .object({
540
+ step: z.number().int().nonnegative(),
541
+ tools: z.array(ChannelRuntimeToolSchema),
542
+ internalOutput: z
543
+ .object({
544
+ kind: z.literal('internal-output'),
545
+ text: z.string().optional(),
546
+ reasoning: z.string().optional(),
547
+ })
548
+ .strict()
549
+ .optional(),
550
+ durationMs: z.number().int().nonnegative().optional(),
551
+ firstTokenMs: z.number().int().nonnegative().optional(),
552
+ usage: ChannelRuntimeUsageSchema.optional(),
553
+ })
554
+ .strict()
555
+
556
+ export const ChannelRuntimeTurnSchema = z
557
+ .object({
558
+ turn: z.number().int().nonnegative(),
559
+ state: z.enum(['in-progress', 'completed', 'unreplied', 'aborted', 'error', 'max-tokens', 'interrupted']),
560
+ producedReply: z.boolean(),
561
+ responseState: z.enum(['not-required', 'pending', 'sent', 'finished', 'protocol-failed', 'interrupted']),
562
+ error: z.object({ code: NonEmptyStringSchema, message: z.string() }).strict().optional(),
563
+ steps: z.array(ChannelRuntimeStepSchema),
564
+ durationMs: z.number().int().nonnegative().optional(),
565
+ /** Session log time the turn opened. */
566
+ startedAt: z.number().int().safe().nonnegative().optional(),
567
+ /** Session log time the turn closed; absent while it is still running. */
568
+ endedAt: z.number().int().safe().nonnegative().optional(),
569
+ /** Newest Channel Event of the admission that opened this turn; absent for non-channel turns. */
570
+ triggerEventId: ChannelEventIdSchema.optional(),
571
+ })
572
+ .strict()
573
+
574
+ export const ChannelRuntimeOccupancySchema = z
575
+ .object({
576
+ projectedTokens: z.number().int().nonnegative(),
577
+ contextWindow: z.number().int().positive(),
578
+ breakdown: z
579
+ .object({
580
+ systemTokens: z.number().int().nonnegative(),
581
+ toolsTokens: z.number().int().nonnegative(),
582
+ messageTokens: z.number().int().nonnegative(),
583
+ })
584
+ .strict()
585
+ .optional(),
586
+ })
587
+ .strict()
588
+
589
+ export const ChannelRuntimeCacheSampleSchema = z
590
+ .object({
591
+ turn: z.number().int().nonnegative(),
592
+ step: z.number().int().nonnegative(),
593
+ at: z.number().nonnegative().optional(),
594
+ uncachedInputTokens: z.number().int().nonnegative(),
595
+ cacheReadTokens: z.number().int().nonnegative().optional(),
596
+ cacheWriteTokens: z.number().int().nonnegative().optional(),
597
+ })
598
+ .strict()
599
+
600
+ export const ChannelRuntimeCacheSchema = z
601
+ .object({
602
+ scope: z.literal('episode'),
603
+ aggregate: z
604
+ .object({
605
+ usageRequestCount: z.number().int().nonnegative(),
606
+ observedRequestCount: z.number().int().nonnegative(),
607
+ shareRequestCount: z.number().int().nonnegative(),
608
+ hitRequestCount: z.number().int().nonnegative(),
609
+ uncachedInputTokens: z.number().int().nonnegative(),
610
+ cacheReadTokens: z.number().int().nonnegative(),
611
+ cacheWriteTokens: z.number().int().nonnegative(),
612
+ averageRequestReadShare: z.number().min(0).max(1).optional(),
613
+ })
614
+ .strict(),
615
+ recent: z
616
+ .object({
617
+ windowSize: z.number().int().positive(),
618
+ samples: z.array(ChannelRuntimeCacheSampleSchema),
619
+ })
620
+ .strict(),
621
+ })
622
+ .strict()
623
+
624
+ export const ChannelRuntimePerformanceSampleSchema = z
625
+ .object({
626
+ turn: z.number().int().nonnegative(),
627
+ step: z.number().int().nonnegative(),
628
+ at: z.number().nonnegative().optional(),
629
+ firstTokenMs: z.number().int().nonnegative().optional(),
630
+ decodeMs: z.number().int().nonnegative().optional(),
631
+ outputTokens: z.number().int().nonnegative().optional(),
632
+ })
633
+ .strict()
634
+
635
+ export const ChannelRuntimePerformanceSchema = z
636
+ .object({
637
+ scope: z.literal('episode'),
638
+ aggregate: z
639
+ .object({
640
+ steps: z.number().int().nonnegative(),
641
+ llmMs: z.number().nonnegative(),
642
+ toolMs: z.number().nonnegative(),
643
+ ttftMs: z.number().nonnegative(),
644
+ ttftSteps: z.number().int().nonnegative(),
645
+ decodeMs: z.number().nonnegative(),
646
+ decodeTokens: z.number().int().nonnegative(),
647
+ decodeSteps: z.number().int().nonnegative(),
648
+ retryCount: z.number().int().nonnegative(),
649
+ retryDelayMs: z.number().nonnegative(),
650
+ })
651
+ .strict(),
652
+ recent: z
653
+ .object({
654
+ windowSize: z.number().int().positive(),
655
+ samples: z.array(ChannelRuntimePerformanceSampleSchema),
656
+ })
657
+ .strict(),
658
+ })
659
+ .strict()
660
+
661
+ export const ChannelRuntimeProjectionSchema = z
662
+ .object({
663
+ channelId: ChannelIdSchema,
664
+ agentId: AgentIdSchema.optional(),
665
+ episodeId: EpisodeIdSchema.optional(),
666
+ phase: ChannelRuntimePhaseSchema,
667
+ summary: z.string(),
668
+ pendingInjectCount: z.number().int().nonnegative(),
669
+ occupancy: ChannelRuntimeOccupancySchema.optional(),
670
+ cache: ChannelRuntimeCacheSchema.optional(),
671
+ performance: ChannelRuntimePerformanceSchema.optional(),
672
+ turns: z.array(ChannelRuntimeTurnSchema),
673
+ })
674
+ .strict()
675
+
676
+ export const ChannelRuntimeSseDataSchema = ChannelRuntimeProjectionSchema.extend({
677
+ revision: z.number().int().positive(),
678
+ truncated: z.boolean().optional(),
679
+ }).strict()
680
+
681
+ export type ChannelRuntimePhase = z.output<typeof ChannelRuntimePhaseSchema>
682
+ export type ChannelRuntimeUsage = z.output<typeof ChannelRuntimeUsageSchema>
683
+ export type ChannelRuntimeOccupancy = z.output<typeof ChannelRuntimeOccupancySchema>
684
+ export type ChannelRuntimeCacheSample = z.output<typeof ChannelRuntimeCacheSampleSchema>
685
+ export type ChannelRuntimeCache = z.output<typeof ChannelRuntimeCacheSchema>
686
+ export type ChannelRuntimePerformanceSample = z.output<typeof ChannelRuntimePerformanceSampleSchema>
687
+ export type ChannelRuntimePerformance = z.output<typeof ChannelRuntimePerformanceSchema>
688
+ export type ChannelRuntimeProjection = z.output<typeof ChannelRuntimeProjectionSchema>
689
+ export type ChannelRuntimeSseData = z.output<typeof ChannelRuntimeSseDataSchema>
690
+ export type ChannelFactSseData = z.output<typeof ChannelFactSseDataSchema>
691
+
692
+ const AdapterConnectionCreationSchema = z
693
+ .object({
694
+ mode: z.enum(['schema-form', 'qr-login']),
695
+ actionLabel: z.string().trim().min(1).optional(),
696
+ pendingLabel: z.string().trim().min(1).optional(),
697
+ })
698
+ .strict()
699
+
700
+ const AdapterConnectionDescriptorSchema = z
701
+ .object({
702
+ key: NonEmptyStringSchema,
703
+ displayName: z.string(),
704
+ description: z.string(),
705
+ provisioning: z.enum(['user-created', 'system-singleton']),
706
+ aliasEditable: z.boolean(),
707
+ channelDiscovery: z.enum(['host-created', 'adapter-observed']),
708
+ channelKinds: z.array(z.enum(['internal', 'direct', 'group'])).min(1),
709
+ activities: z.array(
710
+ z
711
+ .object({
712
+ key: AdapterActivityKeySchema,
713
+ scope: z.enum(['channel', 'connection']),
714
+ displayName: z.string().trim().min(1),
715
+ description: z.string().trim().min(1),
716
+ icon: HostIconNameSchema.optional(),
717
+ triggerable: z.boolean(),
718
+ channelKinds: z.array(z.enum(['internal', 'direct', 'group'])).optional(),
719
+ })
720
+ .strict(),
721
+ ),
722
+ features: z
723
+ .object({
724
+ processingFeedback: z
725
+ .object({ channelKinds: z.array(z.enum(['direct', 'group'])).min(1) })
726
+ .strict()
727
+ .optional(),
728
+ })
729
+ .strict(),
730
+ diagnostics: z.object({ receive: z.boolean(), send: z.boolean() }).strict(),
731
+ creation: AdapterConnectionCreationSchema.optional(),
732
+ /** Typed platform actions the Adapter offers to agent extensions (`ctx.nxt.platform`). */
733
+ platformActions: z
734
+ .array(
735
+ z
736
+ .object({
737
+ name: z.string().regex(/^[a-z0-9_]{1,64}$/u),
738
+ title: z.string(),
739
+ description: z.string(),
740
+ risk: z.enum(['low', 'admin']),
741
+ channelKinds: z.array(z.enum(['direct', 'group'])).min(1),
742
+ parameters: z
743
+ .object({
744
+ type: z.literal('object'),
745
+ properties: z.record(z.string(), JsonValueSchema),
746
+ required: z.array(z.string()).optional(),
747
+ })
748
+ .strict(),
749
+ })
750
+ .strict(),
751
+ )
752
+ .optional(),
753
+ rawApi: z.object({ description: z.string() }).strict().optional(),
754
+ configSchema: ConfigSchemaDocumentSchema,
755
+ })
756
+ .strict()
757
+
758
+ const AdapterCapabilityStateSchema = z
759
+ .object({
760
+ state: z.enum(['available', 'disabled', 'unsupported', 'degraded', 'unknown']),
761
+ reason: z.string().optional(),
762
+ })
763
+ .strict()
764
+
765
+ const ActivityTriggerOverridesSchema = z.record(AdapterActivityKeySchema, z.boolean())
766
+
767
+ export const HostSyncCursorSchema = z
768
+ .object({
769
+ epoch: z.string().regex(/^[a-zA-Z0-9_-]{1,100}$/u),
770
+ sequence: z.number().int().nonnegative().max(Number.MAX_SAFE_INTEGER),
771
+ })
772
+ .strict()
773
+
774
+ export const AgentAppearanceSchema = z
775
+ .object({
776
+ hue: z.number().int().min(0).max(359).optional(),
777
+ avatarAssetId: AssetIdSchema.optional(),
778
+ })
779
+ .strict()
780
+
781
+ export type AgentAppearance = z.output<typeof AgentAppearanceSchema>
782
+
783
+ /** Upper bound of a reported unread count; `unreadCapped` marks that more exist. */
784
+ export const CHANNEL_UNREAD_COUNT_CAP = 99
785
+
786
+ export const ChannelActivitySummarySchema = z
787
+ .object({
788
+ lastActivityAt: z.number().int().safe().nonnegative().optional(),
789
+ lastMessage: z
790
+ .object({
791
+ role: z.enum(['member', 'agent', 'admin', 'system']),
792
+ author: z.string().max(120),
793
+ preview: z.string().max(160),
794
+ occurredAt: z.number().int().safe().nonnegative(),
795
+ })
796
+ .strict()
797
+ .optional(),
798
+ unreadCount: z.number().int().nonnegative().max(CHANNEL_UNREAD_COUNT_CAP),
799
+ unreadCapped: z.boolean(),
800
+ })
801
+ .strict()
802
+
803
+ export type ChannelActivitySummary = z.output<typeof ChannelActivitySummarySchema>
804
+
805
+ export const AttentionItemSchema = z
806
+ .object({
807
+ /** Stable fingerprint; a recurrence of the same condition gets a new fingerprint. */
808
+ id: z.string().min(1).max(200),
809
+ kind: z.enum([
810
+ 'delivery-unconfirmed',
811
+ 'delivery-failed',
812
+ 'turn-failed',
813
+ 'authoring-approval',
814
+ 'connection-unhealthy',
815
+ 'agent-model-unavailable',
816
+ 'agent-vision-unavailable',
817
+ ]),
818
+ severity: z.enum(['critical', 'warning', 'info']),
819
+ subject: z
820
+ .object({
821
+ kind: z.enum(['outbound', 'channel', 'authoring-task', 'connection', 'agent']),
822
+ id: NonEmptyStringSchema,
823
+ })
824
+ .strict(),
825
+ related: z
826
+ .object({
827
+ channelId: ChannelIdSchema.optional(),
828
+ agentId: AgentIdSchema.optional(),
829
+ connectionId: ConnectionIdSchema.optional(),
830
+ taskId: AuthoringTaskIdSchema.optional(),
831
+ outboundId: OutboundIntentIdSchema.optional(),
832
+ })
833
+ .strict(),
834
+ title: z.string().min(1).max(120),
835
+ detail: z.string().max(400),
836
+ action: z
837
+ .object({
838
+ kind: z.enum(['open-channel', 'resolve-delivery', 'open-authoring-task', 'open-connection', 'open-agent']),
839
+ label: z.string().min(1).max(40),
840
+ })
841
+ .strict(),
842
+ occurredAt: z.number().int().safe().nonnegative(),
843
+ })
844
+ .strict()
845
+
846
+ export type AttentionItem = z.output<typeof AttentionItemSchema>
847
+
848
+ export const AttentionListSchema = z
849
+ .object({
850
+ revision: z.string().min(1),
851
+ items: z.array(AttentionItemSchema).max(200),
852
+ })
853
+ .strict()
854
+
855
+ export const ChannelPendingContextSchema = z
856
+ .object({
857
+ channelId: ChannelIdSchema,
858
+ episodeId: EpisodeIdSchema.optional(),
859
+ items: z.array(
860
+ z
861
+ .object({
862
+ admissionId: NonEmptyStringSchema,
863
+ /** `next-step`: injected at the next safe step boundary; `next-turn`: waits for a later turn. */
864
+ target: z.enum(['next-step', 'next-turn']),
865
+ events: z.array(
866
+ z
867
+ .object({
868
+ eventId: ChannelEventIdSchema,
869
+ author: z.string().max(120),
870
+ preview: z.string().max(160),
871
+ receivedAt: z.number().int().safe().nonnegative(),
872
+ })
873
+ .strict(),
874
+ ),
875
+ })
876
+ .strict(),
877
+ ),
878
+ })
879
+ .strict()
880
+
881
+ export type ChannelPendingContext = z.output<typeof ChannelPendingContextSchema>
882
+
883
+ export const ACTIVITY_MAX_WINDOW_MS = 24 * 60 * 60 * 1000
884
+ export const ACTIVITY_MIN_BUCKET_MS = 60 * 1000
885
+ export const ACTIVITY_MAX_BUCKETS = 288
886
+
887
+ const ActivityDurationSchema = z.string().regex(/^[1-9][0-9]{0,4}[mh]$/u)
888
+
889
+ export const activityDurationMs = (value: string): number => {
890
+ const amount = Number(value.slice(0, -1))
891
+ return value.endsWith('h') ? amount * 60 * 60 * 1000 : amount * 60 * 1000
892
+ }
893
+
894
+ export const ActivityQuerySchema = z
895
+ .object({
896
+ window: ActivityDurationSchema.default('2h'),
897
+ bucket: ActivityDurationSchema.default('5m'),
898
+ })
899
+ .strict()
900
+ .superRefine((value, context) => {
901
+ const window = activityDurationMs(value.window)
902
+ const bucket = activityDurationMs(value.bucket)
903
+ if (window > ACTIVITY_MAX_WINDOW_MS)
904
+ context.addIssue({ code: 'custom', path: ['window'], message: '统计窗口不能超过 24 小时。' })
905
+ if (bucket < ACTIVITY_MIN_BUCKET_MS)
906
+ context.addIssue({ code: 'custom', path: ['bucket'], message: '统计间隔不能小于 1 分钟。' })
907
+ if (bucket > window) context.addIssue({ code: 'custom', path: ['bucket'], message: '统计间隔不能大于统计窗口。' })
908
+ if (window / bucket > ACTIVITY_MAX_BUCKETS) {
909
+ context.addIssue({ code: 'custom', path: ['bucket'], message: `统计桶不能超过 ${ACTIVITY_MAX_BUCKETS} 个。` })
910
+ }
911
+ })
912
+
913
+ export const ChannelActivitySeriesSchema = z
914
+ .object({
915
+ from: z.number().int().safe().nonnegative(),
916
+ to: z.number().int().safe().nonnegative(),
917
+ bucketMs: z.number().int().positive(),
918
+ channels: z.array(
919
+ z
920
+ .object({
921
+ channelId: ChannelIdSchema,
922
+ counts: z.array(z.number().int().nonnegative()).max(ACTIVITY_MAX_BUCKETS),
923
+ total: z.number().int().nonnegative(),
924
+ })
925
+ .strict(),
926
+ ),
927
+ })
928
+ .strict()
929
+
930
+ export type ChannelActivitySeries = z.output<typeof ChannelActivitySeriesSchema>
931
+
932
+ export const ScheduledTaskIdSchema = z.string().regex(/^job_[0-9A-Za-z]+$/u)
933
+
934
+ /**
935
+ * One scheduled task of an agent in one channel. `chat` tasks come from the agent's scheduling tools; `declared` and
936
+ * `runtime` tasks belong to an extension (Manifest plan or `ctx.nxt.jobs`).
937
+ */
938
+ export const ScheduledTaskSchema = z
939
+ .object({
940
+ id: ScheduledTaskIdSchema,
941
+ agentId: AgentIdSchema,
942
+ channelId: ChannelIdSchema,
943
+ source: z.enum(['chat', 'declared', 'runtime']),
944
+ extensionId: ExtensionIdSchema.optional(),
945
+ extensionName: z.string().optional(),
946
+ label: z.string(),
947
+ note: z.string().optional(),
948
+ schedule: z.discriminatedUnion('kind', [
949
+ z.object({ kind: z.literal('once'), at: z.number().int().safe() }).strict(),
950
+ z.object({ kind: z.literal('cron'), cron: z.string(), timezone: z.string() }).strict(),
951
+ ]),
952
+ /** `inactive`: the owning extension is disabled for this agent, so the task stays silent. */
953
+ state: z.enum(['scheduled', 'paused', 'finished', 'inactive']),
954
+ nextRunAt: z.number().int().safe().optional(),
955
+ lastFiredAt: z.number().int().safe().optional(),
956
+ createdAt: z.number().int().safe(),
957
+ })
958
+ .strict()
959
+
960
+ export type ScheduledTask = z.output<typeof ScheduledTaskSchema>
961
+
962
+ export const HostSnapshotSchema = z
963
+ .object({
964
+ upgrade: HostUpgradeSummarySchema.optional(),
965
+ cursor: HostSyncCursorSchema,
966
+ diagnosticsSampledAt: z.number().int().nonnegative(),
967
+ productMetadata: z
968
+ .object({
969
+ displayName: NonEmptyStringSchema,
970
+ organizationName: NonEmptyStringSchema,
971
+ version: NonEmptyStringSchema,
972
+ releaseId: NonEmptyStringSchema,
973
+ repositoryUrl: z.url(),
974
+ licenseSpdx: NonEmptyStringSchema.nullable(),
975
+ dshVersion: NonEmptyStringSchema.optional(),
976
+ })
977
+ .strict()
978
+ .optional(),
979
+ models: z.array(
980
+ z
981
+ .object({
982
+ provider: NonEmptyStringSchema,
983
+ providerName: NonEmptyStringSchema,
984
+ id: NonEmptyStringSchema,
985
+ name: z.string(),
986
+ description: z.string().optional(),
987
+ inputModalities: z.array(z.string()).optional(),
988
+ })
989
+ .strict(),
990
+ ),
991
+ capabilityAvailability: z
992
+ .object({
993
+ subagents: z.object({ available: z.boolean() }).strict(),
994
+ webSearch: z
995
+ .object({
996
+ provider: z.literal('deepseek-official'),
997
+ available: z.boolean(),
998
+ credentialConfigured: z.boolean(),
999
+ credentialReference: NonEmptyStringSchema,
1000
+ maxUsesPerCall: z.number().int().nonnegative(),
1001
+ maxResultsPerCall: z.number().int().nonnegative(),
1002
+ timeoutMs: z.number().int().nonnegative(),
1003
+ })
1004
+ .strict(),
1005
+ })
1006
+ .strict(),
1007
+ connectionAdapters: z.array(AdapterConnectionDescriptorSchema),
1008
+ notificationSettings: NotificationSettingsViewSchema,
1009
+ agents: z.array(
1010
+ z
1011
+ .object({
1012
+ id: AgentIdSchema,
1013
+ displayName: z.string(),
1014
+ persona: z.string(),
1015
+ personaDocument: PromptDocumentV1Schema,
1016
+ model: AgentModelSchema,
1017
+ capabilities: AgentCapabilitiesSchema,
1018
+ imagePolicy: ImageUnderstandingPolicyApiSchema,
1019
+ dynamicClientApprovalPolicy: DynamicClientApprovalPolicySchema,
1020
+ imageDiagnostics: z
1021
+ .object({
1022
+ route: z
1023
+ .object({
1024
+ mode: z.enum(['direct', 'delegated', 'unavailable']),
1025
+ provider: z.string().optional(),
1026
+ model: z.string().optional(),
1027
+ })
1028
+ .strict(),
1029
+ activeSessions: z.number().int().nonnegative(),
1030
+ residentImages: z.number().int().nonnegative(),
1031
+ duplicateImagesSkipped: z.number().int().nonnegative(),
1032
+ lastInspection: z
1033
+ .object({
1034
+ mode: z.enum(['direct', 'delegated']),
1035
+ imageCount: z.number().int().nonnegative(),
1036
+ provider: z.string().optional(),
1037
+ model: z.string().optional(),
1038
+ cacheHit: z.boolean(),
1039
+ usage: ChannelRuntimeUsageSchema.optional(),
1040
+ errorCode: z.string().optional(),
1041
+ })
1042
+ .strict()
1043
+ .optional(),
1044
+ lastRestoration: z
1045
+ .object({
1046
+ compactionId: z.string(),
1047
+ candidateCount: z.number().int().nonnegative(),
1048
+ restoredCount: z.number().int().nonnegative(),
1049
+ skippedCount: z.number().int().nonnegative(),
1050
+ error: z.string().optional(),
1051
+ })
1052
+ .strict()
1053
+ .optional(),
1054
+ blockers: z.array(z.string()),
1055
+ })
1056
+ .strict(),
1057
+ currentRevisionId: AgentRevisionIdSchema,
1058
+ runtimeStatus: z.enum(['idle', 'running']),
1059
+ runtimePhase: ChannelRuntimePhaseSchema.default('idle'),
1060
+ createdAt: z.number().finite(),
1061
+ channels: z.array(ChannelIdSchema),
1062
+ appearance: AgentAppearanceSchema.default({}),
1063
+ })
1064
+ .strict(),
1065
+ ),
1066
+ channels: z.array(
1067
+ z
1068
+ .object({
1069
+ id: ChannelIdSchema,
1070
+ connectionId: ConnectionIdSchema,
1071
+ platformChannelId: NonEmptyStringSchema,
1072
+ kind: z.enum(['internal', 'group', 'direct']),
1073
+ displayName: z.string().optional(),
1074
+ boundAgentId: AgentIdSchema.optional(),
1075
+ runtimePhase: ChannelRuntimePhaseSchema.default('idle'),
1076
+ activity: ChannelActivitySummarySchema.default({ unreadCount: 0, unreadCapped: false }),
1077
+ bindings: z.array(
1078
+ z
1079
+ .object({
1080
+ channelId: ChannelIdSchema,
1081
+ agentId: AgentIdSchema,
1082
+ triggerPolicy: TriggerPolicySchema,
1083
+ processingFeedback: z.enum(['auto', 'off']).default('auto'),
1084
+ activityTriggerOverrides: ActivityTriggerOverridesSchema.default({}),
1085
+ boundAt: z.number().int().safe().nonnegative(),
1086
+ })
1087
+ .strict(),
1088
+ ),
1089
+ })
1090
+ .strict(),
1091
+ ),
1092
+ workTreeOrder: WorkTreeOrderSchema.default({
1093
+ agentIds: [],
1094
+ channelIdsByAgent: {},
1095
+ unboundChannelIds: [],
1096
+ }),
1097
+ messages: z.array(HostSnapshotMessageSchema),
1098
+ connections: z.array(
1099
+ z
1100
+ .object({
1101
+ id: ConnectionIdSchema,
1102
+ adapterKey: NonEmptyStringSchema,
1103
+ alias: ConnectionAliasOutputSchema.optional(),
1104
+ activityTriggerDefaults: z.array(AdapterActivityKeySchema).default([]),
1105
+ status: z
1106
+ .object({
1107
+ state: z.enum(['stopped', 'connecting', 'connected', 'reconnecting', 'failed']),
1108
+ message: z.string().optional(),
1109
+ credentialConfigured: z.boolean(),
1110
+ proactiveSend: z.boolean(),
1111
+ accountReference: z.string().optional(),
1112
+ implementation: z
1113
+ .object({
1114
+ name: z.string().optional(),
1115
+ version: z.string().optional(),
1116
+ protocolVersion: z.string().optional(),
1117
+ })
1118
+ .strict()
1119
+ .optional(),
1120
+ optionalCapabilities: z
1121
+ .record(z.string(), z.enum(['unknown', 'available', 'unsupported', 'degraded']))
1122
+ .optional(),
1123
+ activities: z.record(z.string(), AdapterCapabilityStateSchema).default({}),
1124
+ processingFeedback: AdapterCapabilityStateSchema.optional(),
1125
+ })
1126
+ .strict()
1127
+ .default({ state: 'stopped', credentialConfigured: false, proactiveSend: false, activities: {} }),
1128
+ channelCount: z.number().int().nonnegative(),
1129
+ knownChannels: z.array(
1130
+ z.object({ id: ChannelIdSchema, name: z.string(), kind: z.enum(['internal', 'group', 'direct']) }).strict(),
1131
+ ),
1132
+ lastInbound: z
1133
+ .object({
1134
+ channelId: ChannelIdSchema,
1135
+ platformMessageId: NonEmptyStringSchema,
1136
+ receivedAt: z.number().int().safe().nonnegative(),
1137
+ })
1138
+ .strict()
1139
+ .optional(),
1140
+ receiveTest: z.lazy(() => ConnectionTestResultSchema).optional(),
1141
+ sendTest: z.lazy(() => ConnectionTestResultSchema).optional(),
1142
+ configuration: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])).default({}),
1143
+ })
1144
+ .strict(),
1145
+ ),
1146
+ archivedConnections: z
1147
+ .array(
1148
+ z
1149
+ .object({
1150
+ id: ConnectionIdSchema,
1151
+ adapterKey: NonEmptyStringSchema,
1152
+ alias: ConnectionAliasOutputSchema.optional(),
1153
+ channelCount: z.number().int().nonnegative(),
1154
+ archivedAt: z.number().int().safe().nonnegative(),
1155
+ })
1156
+ .strict(),
1157
+ )
1158
+ .default([]),
1159
+ extensions: z.array(
1160
+ z
1161
+ .object({
1162
+ id: ExtensionIdSchema,
1163
+ scope: z.enum(['agent', 'host-adapter', 'host-ui']),
1164
+ slug: NonEmptyStringSchema,
1165
+ displayName: z.string(),
1166
+ description: z.string(),
1167
+ createdByAgentId: AgentIdSchema.optional(),
1168
+ revisions: z.array(
1169
+ z
1170
+ .object({
1171
+ id: ExtensionRevisionIdSchema,
1172
+ revisionNumber: z.number().int().positive(),
1173
+ format: z.enum(['current', 'unavailable']).optional(),
1174
+ ui: ExtensionUiContributionsSchema.default(EMPTY_EXTENSION_UI_CONTRIBUTIONS),
1175
+ configSchema: ConfigSchemaDocumentSchema.optional(),
1176
+ createdAt: z.number().int().safe().nonnegative(),
1177
+ /** 从社区安装的保存记录的来源;本机保存与文件导入没有来源。 */
1178
+ source: CommunitySourceSchema.optional(),
1179
+ scope: z.enum(['agent', 'host-adapter', 'host-ui']),
1180
+ contributions: z.array(z.string()),
1181
+ verification: z
1182
+ .object({
1183
+ verifiedAt: z.number().int().nonnegative(),
1184
+ dshVersion: z.string(),
1185
+ contractVersion: z.string(),
1186
+ hostBuilt: z.boolean(),
1187
+ clientBuilt: z.boolean(),
1188
+ buildKey: z.string(),
1189
+ toolInvocationCount: z.number().int().nonnegative(),
1190
+ rpcMethods: z.array(z.string()),
1191
+ renderedPanels: z.array(z.string()).default([]),
1192
+ renderedToolViews: z.array(z.string()).default([]),
1193
+ renderedMessageRenderers: z.array(z.string()).default([]),
1194
+ renderedPages: z.array(HostPageContributionSchema).max(8).optional(),
1195
+ usedUiComponents: z.array(HostUiKitComponentNameSchema).optional(),
1196
+ pageGeometry: z.array(HostUiPageGeometryEvidenceSchema).max(8).optional(),
1197
+ permissions: HostUiPermissionDeclarationSchema.optional(),
1198
+ permissionDigest: z
1199
+ .string()
1200
+ .regex(/^[a-f0-9]{64}$/u)
1201
+ .optional(),
1202
+ permissionApprovalRequired: z.boolean().optional(),
1203
+ adapter: z
1204
+ .object({
1205
+ apiVersion: z.literal(2),
1206
+ key: NonEmptyStringSchema,
1207
+ descriptorDigest: z.string().regex(/^[a-f0-9]{64}$/u),
1208
+ registered: z.boolean(),
1209
+ started: z.boolean(),
1210
+ stopped: z.boolean(),
1211
+ inboundCommitted: z.boolean(),
1212
+ outboundReceipt: z.enum(['sent', 'failed', 'unknown']),
1213
+ })
1214
+ .strict()
1215
+ .optional(),
1216
+ })
1217
+ .strict()
1218
+ .optional(),
1219
+ })
1220
+ .strict(),
1221
+ ),
1222
+ activations: z.array(
1223
+ z
1224
+ .object({
1225
+ agentId: AgentIdSchema,
1226
+ extensionRevisionId: ExtensionRevisionIdSchema,
1227
+ /** Secret fields are removed; `configuredSecrets` lists those the Host stores. */
1228
+ config: JsonValueSchema,
1229
+ configuredSecrets: z.array(z.string()).optional(),
1230
+ activatedAt: z.number().int().safe().nonnegative(),
1231
+ runtime: z
1232
+ .object({
1233
+ status: z.enum(['active', 'restore-failed', 'dispose-failed']),
1234
+ message: z.string().optional(),
1235
+ observedAt: z.number().int().safe().nonnegative(),
1236
+ })
1237
+ .optional(),
1238
+ /** Latest connection attempt of each MCP server this Activation declares. */
1239
+ mcpServers: z
1240
+ .array(
1241
+ z
1242
+ .object({
1243
+ name: z.string(),
1244
+ state: z.enum(['connecting', 'connected', 'unavailable', 'missing-credentials']),
1245
+ toolCount: z.number().int().nonnegative().optional(),
1246
+ missing: z.array(z.string()).optional(),
1247
+ message: z.string().optional(),
1248
+ observedAt: z.number().int().safe().nonnegative(),
1249
+ })
1250
+ .strict(),
1251
+ )
1252
+ .optional(),
1253
+ })
1254
+ .strict(),
1255
+ ),
1256
+ installation: z
1257
+ .object({
1258
+ extensionRevisionId: ExtensionRevisionIdSchema,
1259
+ installedAt: z.number().int().safe().nonnegative(),
1260
+ config: JsonValueSchema.default({}),
1261
+ runtime: z
1262
+ .object({
1263
+ status: z.enum(['active', 'restore-failed', 'dispose-failed']),
1264
+ message: z.string().optional(),
1265
+ observedAt: z.number().int().safe().nonnegative(),
1266
+ })
1267
+ .optional(),
1268
+ })
1269
+ .strict()
1270
+ .optional(),
1271
+ hostUiPermission: z
1272
+ .object({
1273
+ declaration: HostUiPermissionDeclarationSchema,
1274
+ permissionDigest: z.string().regex(/^[a-f0-9]{64}$/u),
1275
+ approvalRequired: z.boolean(),
1276
+ })
1277
+ .strict()
1278
+ .optional(),
1279
+ hostClientDiagnostic: z
1280
+ .object({
1281
+ revisionId: ExtensionRevisionIdSchema,
1282
+ status: z.enum(['loaded', 'failed']),
1283
+ message: z.string().optional(),
1284
+ observedAt: z.number().int().safe().nonnegative(),
1285
+ })
1286
+ .strict()
1287
+ .optional(),
1288
+ clientDiagnostics: z.array(
1289
+ z
1290
+ .object({
1291
+ agentId: AgentIdSchema,
1292
+ revisionId: ExtensionRevisionIdSchema,
1293
+ status: z.enum(['loaded', 'failed']),
1294
+ message: z.string().optional(),
1295
+ observedAt: z.number().int().nonnegative(),
1296
+ })
1297
+ .strict(),
1298
+ ),
1299
+ })
1300
+ .strict(),
1301
+ ),
1302
+ hostUi: z
1303
+ .object({
1304
+ preferencesRevision: z.number().int().nonnegative(),
1305
+ pages: z.array(HostUiPageEntrySchema),
1306
+ })
1307
+ .strict()
1308
+ .default({ preferencesRevision: 0, pages: [] }),
1309
+ dynamic: z.array(
1310
+ z
1311
+ .object({
1312
+ agentId: AgentIdSchema,
1313
+ episodeId: EpisodeIdSchema,
1314
+ pluginId: DynamicIdSchema,
1315
+ packageId: DynamicIdSchema.optional(),
1316
+ currentPackageId: DynamicIdSchema.optional(),
1317
+ nextPackageId: DynamicIdSchema.optional(),
1318
+ approvalRequestId: DynamicIdSchema.optional(),
1319
+ status: DynamicAttemptStatusSchema,
1320
+ activeRun: z.object({ pluginRunId: DynamicIdSchema, packageId: DynamicIdSchema }).strict().optional(),
1321
+ latestRun: DynamicLatestRunSchema.optional(),
1322
+ packages: z.array(
1323
+ z
1324
+ .object({
1325
+ packageId: DynamicIdSchema,
1326
+ name: z.string(),
1327
+ purpose: z.string(),
1328
+ hasHostHalf: z.boolean(),
1329
+ hasClientHalf: z.boolean(),
1330
+ })
1331
+ .strict(),
1332
+ ),
1333
+ policy: z
1334
+ .object({
1335
+ turn: z.number().int().nonnegative(),
1336
+ primaryPluginId: DynamicIdSchema.optional(),
1337
+ consecutiveFailures: z.number().int().nonnegative(),
1338
+ repeatedFingerprintCount: z.number().int().nonnegative(),
1339
+ lastErrorFingerprint: z.string().optional(),
1340
+ blockedReason: z.string().optional(),
1341
+ })
1342
+ .strict(),
1343
+ })
1344
+ .strict(),
1345
+ ),
1346
+ authoringTasks: z.array(AuthoringTaskSummarySchema).default([]),
1347
+ scheduledTasks: z.array(ScheduledTaskSchema).default([]),
1348
+ })
1349
+ .strict()
1350
+
1351
+ const MessageInputPartSchema = z.discriminatedUnion('type', [
1352
+ z.object({ type: z.literal('text'), text: z.string().trim().min(1) }).strict(),
1353
+ z.object({ type: z.literal('mention'), memberId: ChannelMemberIdSchema }).strict(),
1354
+ ])
1355
+
1356
+ const SendChannelMessageRequestSchema = z
1357
+ .object({
1358
+ parts: z.array(MessageInputPartSchema).min(1),
1359
+ clientEventId: NonEmptyStringSchema.optional(),
1360
+ senderMemberId: ChannelMemberIdSchema.optional(),
1361
+ })
1362
+ .strict()
1363
+ .refine(
1364
+ (body) => body.parts.every((part) => part.type !== 'text' || [...part.text].length <= 64 * 1024),
1365
+ 'Message text must not exceed 64 KiB.',
1366
+ )
1367
+
1368
+ const DynamicErrorDetailsSchema = z.object({ message: z.string(), stack: z.string().optional() }).strict()
1369
+ const DynamicRenderFailureSchema = z
1370
+ .object({
1371
+ slot: NonEmptyStringSchema,
1372
+ message: z.string(),
1373
+ stack: z.string().optional(),
1374
+ abdicated: z.boolean(),
1375
+ })
1376
+ .strict()
1377
+ const DynamicRunResolutionSchema = z.discriminatedUnion('ok', [
1378
+ z.object({ ok: z.literal(true), pluginRunId: DynamicIdSchema, waitingFor: z.array(z.string()).optional() }).strict(),
1379
+ z
1380
+ .object({
1381
+ ok: z.literal(false),
1382
+ reason: z.enum(['rejected', 'host-half-failed', 'client-half-failed']),
1383
+ pluginRunId: DynamicIdSchema.optional(),
1384
+ startedHere: z.boolean().optional(),
1385
+ message: z.string().optional(),
1386
+ stack: z.string().optional(),
1387
+ })
1388
+ .strict(),
1389
+ ])
1390
+
1391
+ const DynamicRunResponseSchema = z.discriminatedUnion('ok', [
1392
+ z
1393
+ .object({
1394
+ ok: z.literal(true),
1395
+ status: z.enum(['awaiting-approval', 'starting', 'running']),
1396
+ pluginId: DynamicIdSchema,
1397
+ packageId: DynamicIdSchema,
1398
+ pluginRunId: DynamicIdSchema,
1399
+ waitingFor: z.array(z.string()),
1400
+ clientWaitingFor: z.array(z.string()).optional(),
1401
+ currentPackageId: DynamicIdSchema.optional(),
1402
+ nextPackageId: DynamicIdSchema.optional(),
1403
+ mode: z.enum(['run', 'update']),
1404
+ })
1405
+ .strict(),
1406
+ z
1407
+ .object({
1408
+ ok: z.literal(false),
1409
+ reason: z.enum([
1410
+ 'plugin-missing',
1411
+ 'package-missing',
1412
+ 'invalid-mode',
1413
+ 'transition-in-flight',
1414
+ 'host-half-failed',
1415
+ 'client-half-failed',
1416
+ 'rejected',
1417
+ 'cancelled',
1418
+ 'not-running',
1419
+ ]),
1420
+ message: z.string(),
1421
+ stack: z.string().optional(),
1422
+ })
1423
+ .strict(),
1424
+ ])
1425
+
1426
+ const DynamicInventoryRowSchema = z
1427
+ .object({
1428
+ pluginId: DynamicIdSchema,
1429
+ agentId: NonEmptyStringSchema,
1430
+ packages: z.array(
1431
+ z
1432
+ .object({
1433
+ packageId: DynamicIdSchema,
1434
+ name: z.string(),
1435
+ purpose: z.string(),
1436
+ hasHostHalf: z.boolean(),
1437
+ hasClientHalf: z.boolean(),
1438
+ })
1439
+ .strict(),
1440
+ ),
1441
+ currentPackageId: DynamicIdSchema.optional(),
1442
+ nextPackageId: DynamicIdSchema.optional(),
1443
+ activeRun: z.object({ pluginRunId: DynamicIdSchema, packageId: DynamicIdSchema }).strict().optional(),
1444
+ latestRun: DynamicLatestRunSchema.optional(),
1445
+ })
1446
+ .strict()
1447
+
1448
+ export const DshPluginCatalogEntrySchema = z
1449
+ .object({
1450
+ packageName: NonEmptyStringSchema,
1451
+ packageVersion: NonEmptyStringSchema,
1452
+ origin: z.enum(['builtin', 'profile', 'dynamic', 'installed']),
1453
+ settingsNamespaces: z.array(NonEmptyStringSchema),
1454
+ loadError: z.object({ code: NonEmptyStringSchema, message: NonEmptyStringSchema }).strict().optional(),
1455
+ packageId: DshPluginPackageIdSchema.optional(),
1456
+ installSource: z.enum(['registry', 'tarball', 'imported']).optional(),
1457
+ installedAt: z.number().int().safe().nonnegative().optional(),
1458
+ clientUiDetected: z.boolean().optional(),
1459
+ hostUi: DshNxtHostUiSchema.optional(),
1460
+ approvedBuilds: z.array(NonEmptyStringSchema).optional(),
1461
+ entries: z
1462
+ .array(
1463
+ z
1464
+ .object({
1465
+ id: DshPluginEntryIdSchema,
1466
+ entryKey: NonEmptyStringSchema,
1467
+ moduleName: NonEmptyStringSchema,
1468
+ suggestedScope: z.enum(['host', 'agent']),
1469
+ selectedScope: z.enum(['host', 'agent']).optional(),
1470
+ config: JsonValueSchema,
1471
+ activations: z.array(
1472
+ z
1473
+ .object({
1474
+ targetKey: NonEmptyStringSchema,
1475
+ target: z.enum(['host', 'agent']),
1476
+ agentId: AgentIdSchema.optional(),
1477
+ activatedAt: z.number().int().safe().nonnegative(),
1478
+ diagnostic: z
1479
+ .object({
1480
+ status: z.enum(['active', 'load-failed', 'restore-failed', 'dispose-failed']),
1481
+ phase: z.enum(['import', 'apply', 'update', 'dispose', 'restore']),
1482
+ message: z.string().optional(),
1483
+ observedAt: z.number().int().safe().nonnegative(),
1484
+ })
1485
+ .optional(),
1486
+ })
1487
+ .strict(),
1488
+ ),
1489
+ })
1490
+ .strict(),
1491
+ )
1492
+ .optional(),
1493
+ })
1494
+ .strict()
1495
+
1496
+ export const DshSettingsNamespaceSchema = z
1497
+ .object({
1498
+ ns: NonEmptyStringSchema,
1499
+ schema: JsonValueSchema,
1500
+ resolved: JsonValueSchema,
1501
+ base: JsonValueSchema.optional(),
1502
+ user: JsonValueSchema.optional(),
1503
+ applies: z.enum(['live', 'restart']),
1504
+ secrets: z.array(z.object({ path: z.array(z.string()), set: z.boolean() }).strict()),
1505
+ revision: z.number().int().nonnegative(),
1506
+ writable: z.boolean(),
1507
+ owner: z.object({ packageName: NonEmptyStringSchema, packageVersion: NonEmptyStringSchema }).strict().optional(),
1508
+ })
1509
+ .strict()
1510
+
1511
+ export const DshSettingsPathSchema = z.array(z.string().min(1).max(200)).min(1).max(32)
1512
+
1513
+ export const DshSettingsPathOperationSchema = z.discriminatedUnion('op', [
1514
+ z.object({ op: z.literal('set'), path: DshSettingsPathSchema, value: JsonValueSchema }).strict(),
1515
+ z.object({ op: z.literal('unset'), path: DshSettingsPathSchema }).strict(),
1516
+ ])
1517
+
1518
+ export const DshSettingsMutationRequestSchema = z
1519
+ .object({
1520
+ expectedRevision: z.number().int().nonnegative(),
1521
+ ops: z.array(DshSettingsPathOperationSchema).min(1).max(128),
1522
+ })
1523
+ .strict()
1524
+
1525
+ export const DshCredentialRefSchema = z
1526
+ .string()
1527
+ .regex(/^[A-Za-z_][A-Za-z0-9_]*$/u)
1528
+ .max(200)
1529
+
1530
+ export const DshCredentialViewSchema = z
1531
+ .object({ configured: z.boolean(), source: z.string().optional(), writable: z.boolean() })
1532
+ .strict()
1533
+
1534
+ export const DshSettingsChangedSseDataSchema = z
1535
+ .object({ namespace: NonEmptyStringSchema, revision: z.number().int().nonnegative() })
1536
+ .strict()
1537
+
1538
+ export const DshCredentialsChangedSseDataSchema = z.object({ ref: DshCredentialRefSchema }).strict()
1539
+
1540
+ export const DynamicChangedSseDataSchema = z.object({ agentId: AgentIdSchema }).strict()
1541
+
1542
+ export const HostSseStatusDataSchema = z
1543
+ .object({
1544
+ ok: z.boolean(),
1545
+ message: z.string(),
1546
+ replay: z.enum(['none', 'complete', 'expired']).optional(),
1547
+ })
1548
+ .strict()
1549
+
1550
+ export const DshPluginOperationSseDataSchema = z
1551
+ .object({
1552
+ operationId: z.string().uuid(),
1553
+ kind: z.enum(['inspect', 'install']),
1554
+ phase: z.enum(['download', 'dependencies', 'build-scripts', 'validation', 'publish']),
1555
+ status: z.enum(['running', 'done', 'failed']),
1556
+ message: z.string(),
1557
+ })
1558
+ .strict()
1559
+
1560
+ export const HostSseEventSchema = z.discriminatedUnion('event', [
1561
+ z.object({ event: z.literal('snapshot-changed'), data: z.object({ changed: z.literal(true) }).strict() }).strict(),
1562
+ z.object({ event: z.literal('channel-fact'), data: ChannelFactSseDataSchema }).strict(),
1563
+ z.object({ event: z.literal('connection-fact'), data: HostConnectionEventSchema }).strict(),
1564
+ z.object({ event: z.literal('runtime'), data: ChannelRuntimeSseDataSchema }).strict(),
1565
+ z.object({ event: z.literal('extensions-changed'), data: z.object({ changed: z.literal(true) }).strict() }).strict(),
1566
+ z.object({ event: z.literal('dsh-plugins-changed'), data: z.object({ changed: z.literal(true) }).strict() }).strict(),
1567
+ z.object({ event: z.literal('dsh-plugin-operation'), data: DshPluginOperationSseDataSchema }).strict(),
1568
+ z.object({ event: z.literal('dynamic-changed'), data: DynamicChangedSseDataSchema }).strict(),
1569
+ z.object({ event: z.literal('dsh-settings-changed'), data: DshSettingsChangedSseDataSchema }).strict(),
1570
+ z.object({ event: z.literal('dsh-credentials-changed'), data: DshCredentialsChangedSseDataSchema }).strict(),
1571
+ z.object({ event: z.literal('status'), data: HostSseStatusDataSchema }).strict(),
1572
+ z
1573
+ .object({ event: z.literal('attention-changed'), data: z.object({ revision: z.string().min(1) }).strict() })
1574
+ .strict(),
1575
+ z
1576
+ .object({
1577
+ event: z.literal('binding-change'),
1578
+ data: z
1579
+ .object({
1580
+ operationId: NonEmptyStringSchema,
1581
+ channelId: ChannelIdSchema,
1582
+ kind: z.enum(['bind', 'replace', 'clear']),
1583
+ step: NonEmptyStringSchema,
1584
+ status: z.enum(['running', 'skipped', 'done', 'failed']),
1585
+ message: z.string(),
1586
+ })
1587
+ .strict(),
1588
+ })
1589
+ .strict(),
1590
+ ])
1591
+
1592
+ export type HostSseEvent = z.output<typeof HostSseEventSchema>
1593
+
1594
+ /** Request content a model accepts; `image` enables native vision for agents using the model. */
1595
+ export const LlmModelInputModalitiesSchema = z.array(z.enum(['text', 'image'])).min(1)
1596
+
1597
+ export const LlmProviderModelSchema = z
1598
+ .object({
1599
+ id: NonEmptyStringSchema,
1600
+ name: NonEmptyStringSchema,
1601
+ contextWindow: z.number().int().positive().optional(),
1602
+ maxTokens: z.number().int().positive().optional(),
1603
+ inputModalities: LlmModelInputModalitiesSchema.optional(),
1604
+ })
1605
+ .strict()
1606
+
1607
+ export const LlmDiscoveredModelSchema = z
1608
+ .object({
1609
+ id: NonEmptyStringSchema,
1610
+ name: NonEmptyStringSchema.optional(),
1611
+ contextWindow: z.number().int().positive().optional(),
1612
+ maxTokens: z.number().int().positive().optional(),
1613
+ inputModalities: LlmModelInputModalitiesSchema.optional(),
1614
+ })
1615
+ .strict()
1616
+
1617
+ export const LlmProviderCredentialSchema = z
1618
+ .object({ configured: z.boolean(), source: z.string().optional(), writable: z.boolean() })
1619
+ .strict()
1620
+
1621
+ export const LlmProviderViewSchema = z
1622
+ .object({
1623
+ provider: NonEmptyStringSchema,
1624
+ displayName: z.string(),
1625
+ settingsNs: NonEmptyStringSchema,
1626
+ settingsPath: z.array(NonEmptyStringSchema),
1627
+ settingsRevision: z.number().int().nonnegative(),
1628
+ declared: z.boolean(),
1629
+ active: z.boolean(),
1630
+ configured: z.boolean(),
1631
+ baseURL: z.string().optional(),
1632
+ api: NonEmptyStringSchema.optional(),
1633
+ credential: LlmProviderCredentialSchema.optional(),
1634
+ models: z.array(LlmProviderModelSchema),
1635
+ /** The model list was edited on this host; built-in providers can restore their own catalog. */
1636
+ modelsCustomized: z.boolean(),
1637
+ /** The provider's adapter can list the models its endpoint offers. */
1638
+ discoverable: z.boolean(),
1639
+ })
1640
+ .strict()
1641
+
1642
+ export const LlmProviderSettingsSchema = z
1643
+ .object({
1644
+ writable: z.boolean(),
1645
+ protocols: z.array(NonEmptyStringSchema),
1646
+ providers: z.array(LlmProviderViewSchema),
1647
+ })
1648
+ .strict()
1649
+
1650
+ export const ConnectionTestResultSchema = z.discriminatedUnion('status', [
1651
+ z
1652
+ .object({
1653
+ status: z.literal('received'),
1654
+ channelId: ChannelIdSchema,
1655
+ platformMessageId: NonEmptyStringSchema,
1656
+ })
1657
+ .strict(),
1658
+ z
1659
+ .object({
1660
+ status: z.literal('sent'),
1661
+ channelId: ChannelIdSchema,
1662
+ platformMessageId: NonEmptyStringSchema.optional(),
1663
+ })
1664
+ .strict(),
1665
+ z
1666
+ .object({
1667
+ status: z.enum(['waiting-for-message', 'needs-channel', 'needs-target', 'not-connected']),
1668
+ message: z.string(),
1669
+ })
1670
+ .strict(),
1671
+ z
1672
+ .object({
1673
+ status: z.literal('failed'),
1674
+ kind: NonEmptyStringSchema,
1675
+ message: z.string(),
1676
+ retryAfterMs: z.number().int().nonnegative().optional(),
1677
+ })
1678
+ .strict(),
1679
+ ])
1680
+
1681
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'PATCH' | 'DELETE'
1682
+ type AnySchema = z.ZodType
1683
+
1684
+ export interface HostApiContract<
1685
+ Method extends HttpMethod = HttpMethod,
1686
+ Params extends AnySchema = AnySchema,
1687
+ Request extends AnySchema = AnySchema,
1688
+ Response extends AnySchema = AnySchema,
1689
+ ErrorSchema extends AnySchema = AnySchema,
1690
+ > {
1691
+ readonly method: Method
1692
+ readonly timeoutMs?: number
1693
+ readonly responseFormat?: 'json' | 'bytes'
1694
+ readonly invalidatesSnapshot?: boolean
1695
+ encodeRequest?(input: z.output<Request>): {
1696
+ readonly body: string | Uint8Array
1697
+ readonly headers: Readonly<Record<string, string>>
1698
+ }
1699
+ readonly path: string
1700
+ readonly params: Params
1701
+ readonly request: Request
1702
+ readonly response: Response
1703
+ readonly error: ErrorSchema
1704
+ readonly parseParams: (input: unknown) => z.output<Params>
1705
+ readonly parseRequest: (input: unknown) => z.output<Request>
1706
+ readonly parseResponse: (input: unknown) => z.output<Response>
1707
+ readonly parseError: (input: unknown) => z.output<ErrorSchema>
1708
+ }
1709
+
1710
+ /** 扩展包导入检查的结果:本地文件与社区下载共用同一确认步骤。 */
1711
+ export const ExtensionImportInspectionSchema = z
1712
+ .object({
1713
+ token: NonEmptyStringSchema,
1714
+ extensionId: ExtensionIdSchema,
1715
+ revisionId: ExtensionRevisionIdSchema,
1716
+ slug: NonEmptyStringSchema,
1717
+ displayName: NonEmptyStringSchema,
1718
+ scope: z.enum(['agent', 'host-adapter', 'host-ui']),
1719
+ idempotent: z.boolean(),
1720
+ slugConflict: z.boolean(),
1721
+ })
1722
+ .strict()
1723
+
1724
+ const defineContract = <
1725
+ const Method extends HttpMethod,
1726
+ Params extends AnySchema,
1727
+ Request extends AnySchema,
1728
+ Response extends AnySchema,
1729
+ >(
1730
+ contract: Omit<
1731
+ HostApiContract<Method, Params, Request, Response, typeof HostApiErrorSchema>,
1732
+ 'parseParams' | 'parseRequest' | 'parseResponse' | 'parseError'
1733
+ >,
1734
+ ): HostApiContract<Method, Params, Request, Response, typeof HostApiErrorSchema> => ({
1735
+ ...contract,
1736
+ parseParams: (input) => contract.params.parse(input),
1737
+ parseRequest: (input) => contract.request.parse(input),
1738
+ parseResponse: (input) => contract.response.parse(input),
1739
+ parseError: (input) => contract.error.parse(input),
1740
+ })
1741
+
1742
+ const agentParam = z.object({ agentId: AgentIdSchema }).strict()
1743
+ const agentRevisionParam = z.object({ agentId: AgentIdSchema, revisionId: AgentRevisionIdSchema }).strict()
1744
+
1745
+ export const AgentRevisionChangedFieldSchema = z.enum([
1746
+ 'name',
1747
+ 'persona',
1748
+ 'model',
1749
+ 'capabilities',
1750
+ 'imagePolicy',
1751
+ 'approvalPolicy',
1752
+ ])
1753
+
1754
+ export const AgentRevisionHistorySchema = z
1755
+ .object({
1756
+ agentId: AgentIdSchema,
1757
+ currentRevisionId: AgentRevisionIdSchema,
1758
+ /** Newest first. `changedFields` compares each Revision with the one numbered just before it. */
1759
+ revisions: z.array(
1760
+ z
1761
+ .object({
1762
+ id: AgentRevisionIdSchema,
1763
+ revision: z.number().int().positive(),
1764
+ createdAt: z.number().int().nonnegative(),
1765
+ displayName: z.string(),
1766
+ model: z.object({ provider: z.string(), model: z.string() }).strict(),
1767
+ changedFields: z.array(AgentRevisionChangedFieldSchema),
1768
+ current: z.boolean(),
1769
+ })
1770
+ .strict(),
1771
+ ),
1772
+ })
1773
+ .strict()
1774
+ export type AgentRevisionHistory = z.output<typeof AgentRevisionHistorySchema>
1775
+ const channelParam = z.object({ channelId: ChannelIdSchema }).strict()
1776
+ const connectionParam = z.object({ connectionId: ConnectionIdSchema }).strict()
1777
+ const agentExtensionParam = z.object({ agentId: AgentIdSchema, extensionId: ExtensionIdSchema }).strict()
1778
+ const extensionParam = z.object({ extensionId: ExtensionIdSchema }).strict()
1779
+ const dshPluginPackageParam = z.object({ packageId: DshPluginPackageIdSchema }).strict()
1780
+ const dshPluginEntryParam = z.object({ entryId: DshPluginEntryIdSchema }).strict()
1781
+ const extensionRevisionParam = z
1782
+ .object({ extensionId: ExtensionIdSchema, revisionId: ExtensionRevisionIdSchema })
1783
+ .strict()
1784
+ const dshSettingsParam = z.object({ namespace: NonEmptyStringSchema }).strict()
1785
+ const dshCredentialParam = z.object({ ref: DshCredentialRefSchema }).strict()
1786
+ const llmProviderParam = z.object({ provider: NonEmptyStringSchema }).strict()
1787
+ const authoringTaskParam = z.object({ taskId: AuthoringTaskIdSchema }).strict()
1788
+ const scheduledTaskParam = z.object({ taskId: ScheduledTaskIdSchema }).strict()
1789
+ const authoringAttemptParam = z.object({ taskId: AuthoringTaskIdSchema, attemptId: AuthoringAttemptIdSchema }).strict()
1790
+
1791
+ const DshPluginInstallInspectionSchema = z
1792
+ .object({
1793
+ token: NonEmptyStringSchema,
1794
+ operationId: z.string().uuid().optional(),
1795
+ packageName: NonEmptyStringSchema,
1796
+ packageVersion: NonEmptyStringSchema,
1797
+ packageDigest: z.string().regex(/^[a-f0-9]{64}$/u),
1798
+ lockfileDigest: z.string().regex(/^[a-f0-9]{64}$/u),
1799
+ blockedBuilds: z.array(NonEmptyStringSchema),
1800
+ clientUiDetected: z.boolean(),
1801
+ hostUi: DshNxtHostUiSchema.optional(),
1802
+ entries: z.array(
1803
+ z
1804
+ .object({
1805
+ entryKey: NonEmptyStringSchema,
1806
+ moduleName: NonEmptyStringSchema,
1807
+ suggestedScope: z.enum(['host', 'agent']),
1808
+ })
1809
+ .strict(),
1810
+ ),
1811
+ })
1812
+ .strict()
1813
+
1814
+ const BinaryUploadRequestSchema = z
1815
+ .object({
1816
+ bytes: z.instanceof(Uint8Array),
1817
+ fileName: z.string().max(255).optional(),
1818
+ operationId: z.string().uuid().optional(),
1819
+ })
1820
+ .strict()
1821
+ const encodeBinaryUpload = (input: z.output<typeof BinaryUploadRequestSchema>) => ({
1822
+ body: input.bytes,
1823
+ headers: {
1824
+ 'content-type': 'application/octet-stream',
1825
+ ...(input.fileName === undefined ? {} : { 'x-file-name': input.fileName }),
1826
+ ...(input.operationId === undefined ? {} : { 'x-operation-id': input.operationId }),
1827
+ },
1828
+ })
1829
+
1830
+ export const HostApiContracts = {
1831
+ retryRuntimeCompatibility: defineContract({
1832
+ method: 'POST',
1833
+ path: '/api/runtime/compatibility/retry',
1834
+ params: EmptyParamsSchema,
1835
+ request: z
1836
+ .object({ objectKind: RuntimeCompatibilityDiagnosticSchema.shape.objectKind, objectId: z.string().min(1) })
1837
+ .strict(),
1838
+ response: z.object({ diagnostics: z.array(RuntimeCompatibilityDiagnosticSchema) }).strict(),
1839
+ error: HostApiErrorSchema,
1840
+ invalidatesSnapshot: true,
1841
+ }),
1842
+ snapshot: defineContract({
1843
+ method: 'GET',
1844
+ path: '/api/snapshot',
1845
+ params: EmptyParamsSchema,
1846
+ request: NoRequestBodySchema,
1847
+ response: HostSnapshotSchema,
1848
+ error: HostApiErrorSchema,
1849
+ }),
1850
+ getAuthoringTask: defineContract({
1851
+ method: 'GET',
1852
+ path: '/api/authoring/tasks/:taskId',
1853
+ params: authoringTaskParam,
1854
+ request: NoRequestBodySchema,
1855
+ response: z
1856
+ .object({
1857
+ task: AuthoringTaskSummarySchema,
1858
+ attempts: z.array(AuthoringAttemptSummarySchema),
1859
+ /** Secret fields of the latest candidate and which have a test value for this task's dynamic runs. */
1860
+ testSecrets: z
1861
+ .object({
1862
+ fields: z.array(z.object({ key: z.string(), title: z.string() }).strict()),
1863
+ configured: z.array(z.string()),
1864
+ })
1865
+ .strict()
1866
+ .optional(),
1867
+ events: z.array(
1868
+ z
1869
+ .object({
1870
+ sequence: z.number().int().positive(),
1871
+ kind: z.string(),
1872
+ attemptId: AuthoringAttemptIdSchema.optional(),
1873
+ payload: JsonValueSchema,
1874
+ createdAt: z.number().int().nonnegative(),
1875
+ })
1876
+ .strict(),
1877
+ ),
1878
+ })
1879
+ .strict(),
1880
+ error: HostApiErrorSchema,
1881
+ }),
1882
+ decideAuthoringAttempt: defineContract({
1883
+ invalidatesSnapshot: true,
1884
+ method: 'POST',
1885
+ path: '/api/authoring/tasks/:taskId/attempts/:attemptId/decision',
1886
+ params: authoringAttemptParam,
1887
+ request: z
1888
+ .object({ expectedRevision: z.number().int().positive(), approved: z.boolean(), approveRiskStable: z.boolean() })
1889
+ .strict(),
1890
+ response: z
1891
+ .object({ accepted: z.boolean(), taskRevision: z.number().int().positive(), executionRequired: z.boolean() })
1892
+ .strict(),
1893
+ error: HostApiErrorSchema,
1894
+ }),
1895
+ restoreAuthoringAttempt: defineContract({
1896
+ invalidatesSnapshot: true,
1897
+ method: 'POST',
1898
+ path: '/api/authoring/tasks/:taskId/attempts/:attemptId/restore',
1899
+ params: authoringAttemptParam,
1900
+ request: z.object({ expectedRevision: z.number().int().positive() }).strict(),
1901
+ response: AuthoringTaskSummarySchema,
1902
+ error: HostApiErrorSchema,
1903
+ }),
1904
+ stopAuthoringTask: defineContract({
1905
+ invalidatesSnapshot: true,
1906
+ method: 'POST',
1907
+ path: '/api/authoring/tasks/:taskId/stop',
1908
+ params: authoringTaskParam,
1909
+ request: z.object({ expectedRevision: z.number().int().positive() }).strict(),
1910
+ response: AuthoringTaskSummarySchema,
1911
+ error: HostApiErrorSchema,
1912
+ }),
1913
+ setAuthoringTestSecrets: defineContract({
1914
+ method: 'PUT',
1915
+ path: '/api/authoring/tasks/:taskId/test-secrets',
1916
+ params: authoringTaskParam,
1917
+ request: z.object({ secrets: z.record(z.string(), z.string().max(8192)) }).strict(),
1918
+ response: z.object({ configured: z.array(z.string()) }).strict(),
1919
+ error: HostApiErrorSchema,
1920
+ }),
1921
+ deleteAuthoringTask: defineContract({
1922
+ invalidatesSnapshot: true,
1923
+ method: 'DELETE',
1924
+ path: '/api/authoring/tasks/:taskId',
1925
+ params: authoringTaskParam,
1926
+ request: NoRequestBodySchema,
1927
+ response: z.object({ deleted: z.literal(true) }).strict(),
1928
+ error: HostApiErrorSchema,
1929
+ }),
1930
+ pauseScheduledTask: defineContract({
1931
+ invalidatesSnapshot: true,
1932
+ method: 'POST',
1933
+ path: '/api/scheduled-tasks/:taskId/pause',
1934
+ params: scheduledTaskParam,
1935
+ request: NoRequestBodySchema,
1936
+ response: ScheduledTaskSchema,
1937
+ error: HostApiErrorSchema,
1938
+ }),
1939
+ resumeScheduledTask: defineContract({
1940
+ invalidatesSnapshot: true,
1941
+ method: 'POST',
1942
+ path: '/api/scheduled-tasks/:taskId/resume',
1943
+ params: scheduledTaskParam,
1944
+ request: NoRequestBodySchema,
1945
+ response: ScheduledTaskSchema,
1946
+ error: HostApiErrorSchema,
1947
+ }),
1948
+ runScheduledTask: defineContract({
1949
+ invalidatesSnapshot: true,
1950
+ method: 'POST',
1951
+ path: '/api/scheduled-tasks/:taskId/run',
1952
+ params: scheduledTaskParam,
1953
+ request: NoRequestBodySchema,
1954
+ /** `woke: false` when the owning extension's due-job handler let this run pass without waking the agent. */
1955
+ response: z.object({ task: ScheduledTaskSchema, woke: z.boolean() }).strict(),
1956
+ error: HostApiErrorSchema,
1957
+ }),
1958
+ deleteScheduledTask: defineContract({
1959
+ invalidatesSnapshot: true,
1960
+ method: 'DELETE',
1961
+ path: '/api/scheduled-tasks/:taskId',
1962
+ params: scheduledTaskParam,
1963
+ request: NoRequestBodySchema,
1964
+ response: z.object({ deleted: z.literal(true) }).strict(),
1965
+ error: HostApiErrorSchema,
1966
+ }),
1967
+ listClientNotifications: defineContract({
1968
+ method: 'GET',
1969
+ path: '/api/client-notifications',
1970
+ params: z.object({ cursor: z.number().int().nonnegative().optional() }).strict(),
1971
+ request: NoRequestBodySchema,
1972
+ response: ClientNotificationFeedResponseSchema,
1973
+ error: HostApiErrorSchema,
1974
+ }),
1975
+ updateNotificationSettings: defineContract({
1976
+ invalidatesSnapshot: true,
1977
+ method: 'PUT',
1978
+ path: '/api/settings/notifications',
1979
+ params: EmptyParamsSchema,
1980
+ request: UpdateNotificationSettingsRequestSchema,
1981
+ response: NotificationSettingsViewSchema,
1982
+ error: HostApiErrorSchema,
1983
+ }),
1984
+ testBarkNotification: defineContract({
1985
+ method: 'POST',
1986
+ path: '/api/settings/notifications/test',
1987
+ params: EmptyParamsSchema,
1988
+ request: TestBarkNotificationRequestSchema,
1989
+ response: z.object({ sent: z.literal(true) }).strict(),
1990
+ error: HostApiErrorSchema,
1991
+ }),
1992
+ testSystemNotification: defineContract({
1993
+ method: 'POST',
1994
+ path: '/api/settings/notifications/test-system',
1995
+ params: EmptyParamsSchema,
1996
+ request: NoRequestBodySchema,
1997
+ response: z.object({ published: z.literal(true) }).strict(),
1998
+ error: HostApiErrorSchema,
1999
+ }),
2000
+ listPlatformUsers: defineContract({
2001
+ method: 'GET',
2002
+ path: '/api/platform-users',
2003
+ params: z
2004
+ .object({
2005
+ query: z.string().trim().max(80).optional(),
2006
+ adapterKey: z.string().trim().min(1).max(120).optional(),
2007
+ connectionId: ConnectionIdSchema.optional(),
2008
+ cursor: PlatformIdentityIdSchema.optional(),
2009
+ limit: z.number().int().min(1).max(100).default(50),
2010
+ })
2011
+ .strict(),
2012
+ request: NoRequestBodySchema,
2013
+ response: PlatformUserListResponseSchema,
2014
+ error: HostApiErrorSchema,
2015
+ }),
2016
+ listConnectionEvents: defineContract({
2017
+ method: 'GET',
2018
+ path: '/api/connections/:connectionId/events',
2019
+ params: z
2020
+ .object({
2021
+ connectionId: ConnectionIdSchema,
2022
+ beforeReceivedAt: z.number().int().safe().nonnegative().optional(),
2023
+ beforeId: ConnectionEventIdSchema.optional(),
2024
+ limit: z.number().int().min(1).max(100).default(50),
2025
+ })
2026
+ .strict(),
2027
+ request: NoRequestBodySchema,
2028
+ response: z.object({ events: z.array(HostConnectionEventSchema), hasMore: z.boolean() }).strict(),
2029
+ error: HostApiErrorSchema,
2030
+ }),
2031
+ createAgent: defineContract({
2032
+ invalidatesSnapshot: true,
2033
+ method: 'POST',
2034
+ path: '/api/agents',
2035
+ params: EmptyParamsSchema,
2036
+ request: CreateAgentRequestSchema,
2037
+ response: z
2038
+ .object({ agentId: AgentIdSchema, channelId: ChannelIdSchema, connectionId: ConnectionIdSchema })
2039
+ .strict(),
2040
+ error: HostApiErrorSchema,
2041
+ }),
2042
+ reviseAgent: defineContract({
2043
+ invalidatesSnapshot: true,
2044
+ method: 'POST',
2045
+ path: '/api/agents/:agentId/revision',
2046
+ params: agentParam,
2047
+ request: ReviseAgentRequestSchema,
2048
+ response: z.object({ currentRevisionId: AgentRevisionIdSchema }).strict(),
2049
+ error: HostApiErrorSchema,
2050
+ }),
2051
+ updateAgentCapabilities: defineContract({
2052
+ invalidatesSnapshot: true,
2053
+ method: 'POST',
2054
+ path: '/api/agents/:agentId/capabilities',
2055
+ params: agentParam,
2056
+ request: UpdateAgentCapabilitiesRequestSchema,
2057
+ response: z.object({ currentRevisionId: AgentRevisionIdSchema, capabilities: AgentCapabilitiesSchema }).strict(),
2058
+ error: HostApiErrorSchema,
2059
+ }),
2060
+ deleteAgent: defineContract({
2061
+ invalidatesSnapshot: true,
2062
+ method: 'DELETE',
2063
+ path: '/api/agents/:agentId',
2064
+ params: agentParam,
2065
+ request: z
2066
+ .object({
2067
+ expectedCurrentRevisionId: AgentRevisionIdSchema,
2068
+ confirmationName: z.string().min(1).max(80),
2069
+ deleteAutoCreatedBuiltInChannels: z.boolean().default(true),
2070
+ /** Also remove `workspaces/<agentId>/` (files, development shell output, creation candidates). */
2071
+ deleteWorkspace: z.boolean().default(false),
2072
+ })
2073
+ .strict(),
2074
+ response: z
2075
+ .object({
2076
+ agentId: AgentIdSchema,
2077
+ deleted: z.literal(true),
2078
+ unboundChannelIds: z.array(ChannelIdSchema),
2079
+ deletedChannelIds: z.array(ChannelIdSchema),
2080
+ })
2081
+ .strict(),
2082
+ error: HostApiErrorSchema,
2083
+ }),
2084
+ createInternalChannel: defineContract({
2085
+ invalidatesSnapshot: true,
2086
+ method: 'POST',
2087
+ path: '/api/channels',
2088
+ params: EmptyParamsSchema,
2089
+ request: z.object({ displayName: z.string().trim().min(1).max(120) }).strict(),
2090
+ response: z.object({ channelId: ChannelIdSchema, connectionId: ConnectionIdSchema }).strict(),
2091
+ error: HostApiErrorSchema,
2092
+ }),
2093
+ deleteChannel: defineContract({
2094
+ invalidatesSnapshot: true,
2095
+ method: 'DELETE',
2096
+ path: '/api/channels/:channelId',
2097
+ params: channelParam,
2098
+ request: z.object({ expectedBoundAgentId: AgentIdSchema.nullable() }).strict(),
2099
+ response: z.object({ channelId: ChannelIdSchema, deleted: z.literal(true) }).strict(),
2100
+ error: HostApiErrorSchema,
2101
+ }),
2102
+ createBinding: defineContract({
2103
+ invalidatesSnapshot: true,
2104
+ method: 'POST',
2105
+ path: '/api/bindings',
2106
+ params: EmptyParamsSchema,
2107
+ request: z
2108
+ .object({
2109
+ agentId: AgentIdSchema,
2110
+ channelId: ChannelIdSchema,
2111
+ triggerPolicy: TriggerPolicySchema,
2112
+ processingFeedback: z.enum(['auto', 'off']).optional(),
2113
+ activityTriggerOverrides: ActivityTriggerOverridesSchema.optional(),
2114
+ })
2115
+ .strict(),
2116
+ response: z
2117
+ .object({
2118
+ channelId: ChannelIdSchema,
2119
+ agentId: AgentIdSchema,
2120
+ triggerPolicy: TriggerPolicySchema,
2121
+ processingFeedback: z.enum(['auto', 'off']).default('auto'),
2122
+ activityTriggerOverrides: ActivityTriggerOverridesSchema.default({}),
2123
+ boundAt: z.number().int().safe().nonnegative(),
2124
+ })
2125
+ .strict(),
2126
+ error: HostApiErrorSchema,
2127
+ }),
2128
+ clearBinding: defineContract({
2129
+ invalidatesSnapshot: true,
2130
+ method: 'DELETE',
2131
+ path: '/api/bindings/:channelId',
2132
+ params: channelParam,
2133
+ request: NoRequestBodySchema,
2134
+ response: z.object({ channelId: ChannelIdSchema, cleared: z.literal(true) }).strict(),
2135
+ error: HostApiErrorSchema,
2136
+ }),
2137
+ putWorkTreeOrder: defineContract({
2138
+ invalidatesSnapshot: true,
2139
+ method: 'PUT',
2140
+ path: '/api/work-tree-order',
2141
+ params: EmptyParamsSchema,
2142
+ request: WorkTreeOrderSchema,
2143
+ response: WorkTreeOrderSchema,
2144
+ error: HostApiErrorSchema,
2145
+ }),
2146
+ listChannelMessages: defineContract({
2147
+ method: 'GET',
2148
+ path: '/api/channels/:channelId/messages',
2149
+ params: z
2150
+ .object({
2151
+ channelId: ChannelIdSchema,
2152
+ limit: z.number().int().min(1).max(100),
2153
+ beforeOccurredAt: z.number().int().safe().nonnegative().optional(),
2154
+ beforeSourceId: NonEmptyStringSchema.optional(),
2155
+ })
2156
+ .strict()
2157
+ .refine(
2158
+ (value) => (value.beforeOccurredAt === undefined) === (value.beforeSourceId === undefined),
2159
+ '频道历史游标必须完整提供。',
2160
+ ),
2161
+ request: NoRequestBodySchema,
2162
+ response: z
2163
+ .object({ messages: z.array(HostSnapshotMessageSchema), hasMore: z.boolean(), cursor: HostSyncCursorSchema })
2164
+ .strict(),
2165
+ error: HostApiErrorSchema,
2166
+ }),
2167
+ sendChannelMessage: defineContract({
2168
+ method: 'POST',
2169
+ path: '/api/channels/:channelId/messages',
2170
+ params: channelParam,
2171
+ request: SendChannelMessageRequestSchema,
2172
+ response: z
2173
+ .object({
2174
+ inserted: z.boolean(),
2175
+ channelEventId: ChannelEventIdSchema.optional(),
2176
+ outboundIntentId: OutboundIntentIdSchema.optional(),
2177
+ })
2178
+ .strict(),
2179
+ error: HostApiErrorSchema,
2180
+ }),
2181
+ renameChannel: defineContract({
2182
+ invalidatesSnapshot: true,
2183
+ method: 'POST',
2184
+ path: '/api/channels/:channelId/display-name',
2185
+ params: channelParam,
2186
+ request: z.object({ displayName: z.string().trim().min(1).max(120) }).strict(),
2187
+ response: z.object({ channelId: ChannelIdSchema, displayName: z.string().trim().min(1).max(120) }).strict(),
2188
+ error: HostApiErrorSchema,
2189
+ }),
2190
+ getChannelRuntime: defineContract({
2191
+ method: 'GET',
2192
+ path: '/api/channels/:channelId/runtime',
2193
+ params: channelParam,
2194
+ request: NoRequestBodySchema,
2195
+ response: ChannelRuntimeProjectionSchema.extend({ cursor: HostSyncCursorSchema }),
2196
+ error: HostApiErrorSchema,
2197
+ }),
2198
+ getChannelToolCall: defineContract({
2199
+ method: 'GET',
2200
+ path: '/api/channels/:channelId/runtime/tools/:callId',
2201
+ params: z.object({ channelId: ChannelIdSchema, callId: z.string().trim().min(1).max(200) }).strict(),
2202
+ request: NoRequestBodySchema,
2203
+ // The full arguments and result of one tool call in the live session; the runtime projection only carries previews.
2204
+ // `available: false` when the session has left memory: the step keeps its preview, which is not an error.
2205
+ response: z
2206
+ .object({
2207
+ callId: z.string(),
2208
+ available: z.boolean(),
2209
+ name: z.string().optional(),
2210
+ input: z.string().optional(),
2211
+ result: z.string().optional(),
2212
+ inputTruncated: z.boolean(),
2213
+ resultTruncated: z.boolean(),
2214
+ })
2215
+ .strict(),
2216
+ error: HostApiErrorSchema,
2217
+ }),
2218
+ resetChannelContext: defineContract({
2219
+ invalidatesSnapshot: true,
2220
+ method: 'POST',
2221
+ path: '/api/channels/:channelId/context-reset',
2222
+ params: channelParam,
2223
+ request: z
2224
+ .object({
2225
+ mode: z.enum(['clear', 'compact']),
2226
+ expectedEpisodeId: EpisodeIdSchema,
2227
+ })
2228
+ .strict(),
2229
+ response: z
2230
+ .object({
2231
+ mode: z.enum(['clear', 'compact']),
2232
+ closedEpisodeId: EpisodeIdSchema,
2233
+ nextEpisodeId: EpisodeIdSchema.optional(),
2234
+ })
2235
+ .strict(),
2236
+ error: HostApiErrorSchema,
2237
+ }),
2238
+ createConnection: defineContract({
2239
+ invalidatesSnapshot: true,
2240
+ method: 'POST',
2241
+ path: '/api/connections',
2242
+ params: EmptyParamsSchema,
2243
+ request: z
2244
+ .object({
2245
+ adapterKey: NonEmptyStringSchema,
2246
+ alias: ConnectionAliasInputSchema.optional(),
2247
+ configuration: z.record(z.string(), JsonValueSchema).default({}),
2248
+ credentials: z.record(z.string(), z.string().max(16 * 1024)).default({}),
2249
+ })
2250
+ .strict(),
2251
+ response: z.object({ connectionId: ConnectionIdSchema, adapterKey: NonEmptyStringSchema }).strict(),
2252
+ error: HostApiErrorSchema,
2253
+ }),
2254
+ startConnectionLogin: defineContract({
2255
+ method: 'POST',
2256
+ path: '/api/connection-logins',
2257
+ params: EmptyParamsSchema,
2258
+ request: z
2259
+ .object({
2260
+ adapterKey: NonEmptyStringSchema,
2261
+ alias: ConnectionAliasInputSchema.optional(),
2262
+ connectionId: ConnectionIdSchema.optional(),
2263
+ })
2264
+ .strict(),
2265
+ response: z
2266
+ .object({
2267
+ loginId: NonEmptyStringSchema,
2268
+ adapterKey: NonEmptyStringSchema,
2269
+ status: z.enum(['pending', 'scanned', 'confirmed', 'expired', 'failed', 'cancelled']),
2270
+ qrCodeUrl: NonEmptyStringSchema,
2271
+ message: z.string().optional(),
2272
+ })
2273
+ .strict(),
2274
+ error: HostApiErrorSchema,
2275
+ }),
2276
+ getConnectionLogin: defineContract({
2277
+ method: 'GET',
2278
+ path: '/api/connection-logins/:loginId',
2279
+ params: z.object({ loginId: NonEmptyStringSchema }).strict(),
2280
+ request: NoRequestBodySchema,
2281
+ response: z
2282
+ .object({
2283
+ loginId: NonEmptyStringSchema,
2284
+ status: z.enum(['pending', 'scanned', 'confirmed', 'expired', 'failed', 'cancelled']),
2285
+ qrCodeUrl: NonEmptyStringSchema.optional(),
2286
+ connectionId: ConnectionIdSchema.optional(),
2287
+ adapterKey: NonEmptyStringSchema,
2288
+ message: z.string().optional(),
2289
+ })
2290
+ .strict(),
2291
+ error: HostApiErrorSchema,
2292
+ }),
2293
+ cancelConnectionLogin: defineContract({
2294
+ method: 'DELETE',
2295
+ path: '/api/connection-logins/:loginId',
2296
+ params: z.object({ loginId: NonEmptyStringSchema }).strict(),
2297
+ request: NoRequestBodySchema,
2298
+ response: z
2299
+ .object({
2300
+ loginId: NonEmptyStringSchema,
2301
+ status: z.literal('cancelled'),
2302
+ })
2303
+ .strict(),
2304
+ error: HostApiErrorSchema,
2305
+ }),
2306
+ updateConnectionAlias: defineContract({
2307
+ invalidatesSnapshot: true,
2308
+ method: 'POST',
2309
+ path: '/api/connections/:connectionId/alias',
2310
+ params: connectionParam,
2311
+ request: z.object({ alias: z.string().trim().max(80) }).strict(),
2312
+ response: z
2313
+ .object({
2314
+ connectionId: ConnectionIdSchema,
2315
+ alias: ConnectionAliasOutputSchema.optional(),
2316
+ })
2317
+ .strict(),
2318
+ error: HostApiErrorSchema,
2319
+ }),
2320
+ updateConnectionActivityTriggerDefaults: defineContract({
2321
+ invalidatesSnapshot: true,
2322
+ method: 'POST',
2323
+ path: '/api/connections/:connectionId/activity-trigger-defaults',
2324
+ params: connectionParam,
2325
+ request: z.object({ activityKeys: z.array(AdapterActivityKeySchema) }).strict(),
2326
+ response: z.object({ connectionId: ConnectionIdSchema, activityKeys: z.array(AdapterActivityKeySchema) }).strict(),
2327
+ error: HostApiErrorSchema,
2328
+ }),
2329
+ updateConnectionConfiguration: defineContract({
2330
+ invalidatesSnapshot: true,
2331
+ method: 'POST',
2332
+ path: '/api/connections/:connectionId/configuration',
2333
+ params: connectionParam,
2334
+ request: z.object({ configuration: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])) }).strict(),
2335
+ response: z
2336
+ .object({
2337
+ connectionId: ConnectionIdSchema,
2338
+ configuration: z.record(z.string(), z.union([z.string(), z.number(), z.boolean()])),
2339
+ })
2340
+ .strict(),
2341
+ error: HostApiErrorSchema,
2342
+ }),
2343
+ deleteConnection: defineContract({
2344
+ invalidatesSnapshot: true,
2345
+ method: 'DELETE',
2346
+ path: '/api/connections/:connectionId',
2347
+ params: connectionParam,
2348
+ request: z.object({ deleteChannelData: z.boolean() }).strict(),
2349
+ response: z.object({ connectionId: ConnectionIdSchema, archived: z.boolean() }).strict(),
2350
+ error: HostApiErrorSchema,
2351
+ }),
2352
+ restoreConnection: defineContract({
2353
+ invalidatesSnapshot: true,
2354
+ method: 'POST',
2355
+ path: '/api/connections/:connectionId/restore',
2356
+ params: connectionParam,
2357
+ request: NoRequestBodySchema,
2358
+ response: z.object({ connectionId: ConnectionIdSchema, restored: z.literal(true) }).strict(),
2359
+ error: HostApiErrorSchema,
2360
+ }),
2361
+ dshPlugins: defineContract({
2362
+ method: 'GET',
2363
+ path: '/api/dsh/plugins',
2364
+ params: EmptyParamsSchema,
2365
+ request: NoRequestBodySchema,
2366
+ response: z.object({ plugins: z.array(DshPluginCatalogEntrySchema) }).strict(),
2367
+ error: HostApiErrorSchema,
2368
+ }),
2369
+ inspectDshPluginTarball: defineContract({
2370
+ method: 'POST',
2371
+ timeoutMs: 300_000,
2372
+ path: '/api/dsh/plugin-installs/inspect-tarball',
2373
+ params: EmptyParamsSchema,
2374
+ request: BinaryUploadRequestSchema,
2375
+ encodeRequest: encodeBinaryUpload,
2376
+ response: DshPluginInstallInspectionSchema,
2377
+ error: HostApiErrorSchema,
2378
+ }),
2379
+ exportExtensionRevision: defineContract({
2380
+ method: 'GET',
2381
+ timeoutMs: 300_000,
2382
+ path: '/api/extensions/:extensionId/revisions/:revisionId/export',
2383
+ params: extensionRevisionParam,
2384
+ request: NoRequestBodySchema,
2385
+ responseFormat: 'bytes',
2386
+ response: z.instanceof(Uint8Array),
2387
+ error: HostApiErrorSchema,
2388
+ }),
2389
+ inspectDshPluginInstall: defineContract({
2390
+ timeoutMs: 300_000,
2391
+ method: 'POST',
2392
+ path: '/api/dsh/plugin-installs/inspect',
2393
+ params: EmptyParamsSchema,
2394
+ request: z.object({ spec: NonEmptyStringSchema.max(500), operationId: z.string().uuid().optional() }).strict(),
2395
+ response: DshPluginInstallInspectionSchema,
2396
+ error: HostApiErrorSchema,
2397
+ }),
2398
+ commitDshPluginInstall: defineContract({
2399
+ invalidatesSnapshot: true,
2400
+ timeoutMs: 300_000,
2401
+ method: 'POST',
2402
+ path: '/api/dsh/plugin-installs',
2403
+ params: EmptyParamsSchema,
2404
+ request: z
2405
+ .object({
2406
+ token: NonEmptyStringSchema,
2407
+ approvedBuilds: z.array(NonEmptyStringSchema).default([]),
2408
+ operationId: z.string().uuid().optional(),
2409
+ })
2410
+ .strict(),
2411
+ response: z.object({ packageId: DshPluginPackageIdSchema, operationId: z.string().uuid().optional() }).strict(),
2412
+ error: HostApiErrorSchema,
2413
+ }),
2414
+ inspectDshPluginEntryConfig: defineContract({
2415
+ method: 'POST',
2416
+ path: '/api/dsh/plugin-entries/:entryId/config/inspect',
2417
+ params: dshPluginEntryParam,
2418
+ request: NoRequestBodySchema,
2419
+ response: z.discriminatedUnion('mode', [
2420
+ z.object({ mode: z.literal('schema'), schema: JsonValueSchema }).strict(),
2421
+ z.object({ mode: z.literal('json') }).strict(),
2422
+ z.object({ mode: z.literal('incompatible'), reason: NonEmptyStringSchema }).strict(),
2423
+ ]),
2424
+ error: HostApiErrorSchema,
2425
+ }),
2426
+ activateDshPluginEntry: defineContract({
2427
+ method: 'PUT',
2428
+ path: '/api/dsh/plugin-entries/:entryId/activation',
2429
+ params: dshPluginEntryParam,
2430
+ request: z
2431
+ .object({
2432
+ target: z.enum(['host', 'agent']),
2433
+ agentId: AgentIdSchema.optional(),
2434
+ config: JsonValueSchema,
2435
+ permissionApproval: z
2436
+ .object({ permissionDigest: z.string().regex(/^[a-f0-9]{64}$/u) })
2437
+ .strict()
2438
+ .optional(),
2439
+ })
2440
+ .strict(),
2441
+ response: z.object({ targetKey: NonEmptyStringSchema }).strict(),
2442
+ error: HostApiErrorSchema,
2443
+ }),
2444
+ deactivateDshPluginEntry: defineContract({
2445
+ method: 'DELETE',
2446
+ path: '/api/dsh/plugin-entries/:entryId/activation',
2447
+ params: dshPluginEntryParam,
2448
+ request: z.object({ targetKey: NonEmptyStringSchema }).strict(),
2449
+ response: z.object({ disabled: z.literal(true) }).strict(),
2450
+ error: HostApiErrorSchema,
2451
+ }),
2452
+ removeDshPluginPackage: defineContract({
2453
+ invalidatesSnapshot: true,
2454
+ method: 'DELETE',
2455
+ path: '/api/dsh/plugin-installs/:packageId',
2456
+ params: dshPluginPackageParam,
2457
+ request: NoRequestBodySchema,
2458
+ response: z.object({ removed: z.literal(true) }).strict(),
2459
+ error: HostApiErrorSchema,
2460
+ }),
2461
+ inspectExtensionImport: defineContract({
2462
+ timeoutMs: 300_000,
2463
+ method: 'POST',
2464
+ path: '/api/extensions/imports/inspect',
2465
+ params: EmptyParamsSchema,
2466
+ request: BinaryUploadRequestSchema,
2467
+ encodeRequest: encodeBinaryUpload,
2468
+ response: ExtensionImportInspectionSchema,
2469
+ error: HostApiErrorSchema,
2470
+ }),
2471
+ /**
2472
+ * Management-edge endpoints: they exist only when the Server is reached through its management edge (a management
2473
+ * key is set); clients tell that from the edge's CSRF cookie.
2474
+ */
2475
+ listManagementDevices: defineContract({
2476
+ method: 'GET',
2477
+ path: '/api/management/devices',
2478
+ params: EmptyParamsSchema,
2479
+ request: NoRequestBodySchema,
2480
+ response: z.object({ devices: z.array(ManagementDeviceViewSchema) }).strict(),
2481
+ error: HostApiErrorSchema,
2482
+ }),
2483
+ revokeManagementDevice: defineContract({
2484
+ method: 'DELETE',
2485
+ path: '/api/management/devices/:deviceId',
2486
+ params: z.object({ deviceId: ManagementDeviceIdSchema }).strict(),
2487
+ request: NoRequestBodySchema,
2488
+ response: z.object({ revoked: z.boolean() }).strict(),
2489
+ error: HostApiErrorSchema,
2490
+ }),
2491
+ managementLogout: defineContract({
2492
+ method: 'DELETE',
2493
+ path: '/api/management/session',
2494
+ params: EmptyParamsSchema,
2495
+ request: NoRequestBodySchema,
2496
+ response: z.object({ authenticated: z.literal(false) }).strict(),
2497
+ error: HostApiErrorSchema,
2498
+ }),
2499
+ getCommunityStatus: defineContract({
2500
+ method: 'GET',
2501
+ path: '/api/community/status',
2502
+ params: EmptyParamsSchema,
2503
+ request: NoRequestBodySchema,
2504
+ response: CommunityStatusSchema,
2505
+ error: HostApiErrorSchema,
2506
+ }),
2507
+ startCommunityLogin: defineContract({
2508
+ method: 'POST',
2509
+ path: '/api/community/login',
2510
+ params: EmptyParamsSchema,
2511
+ /** 浏览器当前访问本实例使用的来源;社区授权完成后回到 `<returnOrigin>/community/callback`。 */
2512
+ request: z.object({ returnOrigin: z.string().url().max(300) }).strict(),
2513
+ response: z.object({ authorizeUrl: z.string().url() }).strict(),
2514
+ error: HostApiErrorSchema,
2515
+ }),
2516
+ communityLogout: defineContract({
2517
+ method: 'DELETE',
2518
+ path: '/api/community/login',
2519
+ params: EmptyParamsSchema,
2520
+ request: NoRequestBodySchema,
2521
+ response: CommunityStatusSchema,
2522
+ error: HostApiErrorSchema,
2523
+ }),
2524
+ listCommunityExtensions: defineContract({
2525
+ timeoutMs: 30_000,
2526
+ method: 'GET',
2527
+ path: '/api/community/extensions',
2528
+ params: z
2529
+ .object({
2530
+ query: z.string().trim().max(80).optional(),
2531
+ scope: z.enum(['agent', 'host-adapter', 'host-ui']).optional(),
2532
+ cursor: z.string().max(200).optional(),
2533
+ })
2534
+ .strict(),
2535
+ request: NoRequestBodySchema,
2536
+ response: z.object({ items: z.array(CommunityExtensionSummarySchema), nextCursor: z.string().nullable() }).strict(),
2537
+ error: HostApiErrorSchema,
2538
+ }),
2539
+ getCommunityExtension: defineContract({
2540
+ timeoutMs: 30_000,
2541
+ method: 'GET',
2542
+ path: '/api/community/extensions/:extensionId',
2543
+ params: z.object({ extensionId: ExtensionIdSchema }).strict(),
2544
+ request: NoRequestBodySchema,
2545
+ response: CommunityExtensionDetailSchema,
2546
+ error: HostApiErrorSchema,
2547
+ }),
2548
+ /** 下载社区发布并按包摘要校验,结果与本地文件导入的检查相同,再经 `commitExtensionImport` 确认。 */
2549
+ getCommunityEndpoint: defineContract({
2550
+ method: 'GET',
2551
+ path: '/api/community/endpoint',
2552
+ params: EmptyParamsSchema,
2553
+ request: NoRequestBodySchema,
2554
+ response: CommunityEndpointSchema,
2555
+ error: HostApiErrorSchema,
2556
+ }),
2557
+ /** 修改社区地址;`url: null` 恢复为环境变量或默认地址。非加密的局域网地址需要确认风险。 */
2558
+ updateCommunityEndpoint: defineContract({
2559
+ method: 'PUT',
2560
+ path: '/api/community/endpoint',
2561
+ params: EmptyParamsSchema,
2562
+ request: z.object({ url: z.string().max(300).nullable(), acknowledgeInsecure: z.boolean() }).strict(),
2563
+ response: CommunityEndpointSchema,
2564
+ error: HostApiErrorSchema,
2565
+ }),
2566
+ testCommunityEndpoint: defineContract({
2567
+ timeoutMs: 30_000,
2568
+ method: 'POST',
2569
+ path: '/api/community/endpoint/test',
2570
+ params: EmptyParamsSchema,
2571
+ request: z.object({ url: z.string().max(300), acknowledgeInsecure: z.boolean() }).strict(),
2572
+ response: z
2573
+ .object({
2574
+ ok: z.boolean(),
2575
+ message: z.string(),
2576
+ name: z.string().optional(),
2577
+ environment: z.enum(['production', 'staging', 'development']).optional(),
2578
+ })
2579
+ .strict(),
2580
+ error: HostApiErrorSchema,
2581
+ }),
2582
+ listCommunityInstalled: defineContract({
2583
+ timeoutMs: 30_000,
2584
+ method: 'GET',
2585
+ path: '/api/community/installed',
2586
+ params: z.object({ refresh: z.enum(['1']).optional() }).strict(),
2587
+ request: NoRequestBodySchema,
2588
+ response: CommunityInstalledSchema,
2589
+ error: HostApiErrorSchema,
2590
+ }),
2591
+ listCommunityMine: defineContract({
2592
+ timeoutMs: 30_000,
2593
+ method: 'GET',
2594
+ path: '/api/community/mine',
2595
+ params: EmptyParamsSchema,
2596
+ request: NoRequestBodySchema,
2597
+ response: z.object({ items: z.array(CommunityMyExtensionSchema) }).strict(),
2598
+ error: HostApiErrorSchema,
2599
+ }),
2600
+ getCommunityReleaseReview: defineContract({
2601
+ timeoutMs: 30_000,
2602
+ method: 'GET',
2603
+ path: '/api/community/releases/:releaseId/review',
2604
+ params: z.object({ releaseId: CommunityReleaseIdSchema }).strict(),
2605
+ request: NoRequestBodySchema,
2606
+ response: CommunityReviewReportSchema,
2607
+ error: HostApiErrorSchema,
2608
+ }),
2609
+ requestCommunityReview: defineContract({
2610
+ timeoutMs: 30_000,
2611
+ method: 'POST',
2612
+ path: '/api/community/releases/:releaseId/review',
2613
+ params: z.object({ releaseId: CommunityReleaseIdSchema }).strict(),
2614
+ request: NoRequestBodySchema,
2615
+ response: z.object({ ok: z.literal(true) }).strict(),
2616
+ error: HostApiErrorSchema,
2617
+ }),
2618
+ withdrawCommunityRelease: defineContract({
2619
+ timeoutMs: 30_000,
2620
+ method: 'POST',
2621
+ path: '/api/community/releases/:releaseId/withdraw',
2622
+ params: z.object({ releaseId: CommunityReleaseIdSchema }).strict(),
2623
+ request: NoRequestBodySchema,
2624
+ response: z.object({ ok: z.literal(true) }).strict(),
2625
+ error: HostApiErrorSchema,
2626
+ }),
2627
+ importCommunityRelease: defineContract({
2628
+ timeoutMs: 120_000,
2629
+ method: 'POST',
2630
+ path: '/api/community/releases/:releaseId/import',
2631
+ params: z.object({ releaseId: CommunityReleaseIdSchema }).strict(),
2632
+ request: NoRequestBodySchema,
2633
+ response: ExtensionImportInspectionSchema,
2634
+ error: HostApiErrorSchema,
2635
+ }),
2636
+ publishToCommunity: defineContract({
2637
+ timeoutMs: 120_000,
2638
+ method: 'POST',
2639
+ path: '/api/community/publish',
2640
+ params: EmptyParamsSchema,
2641
+ request: z
2642
+ .object({
2643
+ extensionId: ExtensionIdSchema,
2644
+ revisionId: ExtensionRevisionIdSchema,
2645
+ notes: z.string().max(2000),
2646
+ })
2647
+ .strict(),
2648
+ response: z
2649
+ .object({
2650
+ releaseId: CommunityReleaseIdSchema,
2651
+ reviewStatus: CommunityReviewStatusSchema,
2652
+ pageUrl: z.string().url(),
2653
+ reportUrl: z.string().url(),
2654
+ findings: z.array(z.object({ severity: z.string(), title: z.string() }).strict()),
2655
+ })
2656
+ .strict(),
2657
+ error: HostApiErrorSchema,
2658
+ }),
2659
+ testMcpServer: defineContract({
2660
+ timeoutMs: 30_000,
2661
+ method: 'POST',
2662
+ path: '/api/mcp-servers/test',
2663
+ params: EmptyParamsSchema,
2664
+ request: z.object({ server: McpServerFormSchema }).strict(),
2665
+ response: z
2666
+ .object({
2667
+ ok: z.boolean(),
2668
+ message: z.string(),
2669
+ serverName: z.string().optional(),
2670
+ tools: z.array(z.object({ name: z.string(), description: z.string().optional() }).strict()),
2671
+ })
2672
+ .strict(),
2673
+ error: HostApiErrorSchema,
2674
+ }),
2675
+ createMcpExtension: defineContract({
2676
+ invalidatesSnapshot: true,
2677
+ timeoutMs: 120_000,
2678
+ method: 'POST',
2679
+ path: '/api/mcp-servers',
2680
+ params: EmptyParamsSchema,
2681
+ request: z
2682
+ .object({
2683
+ displayName: z.string().trim().min(1).max(80),
2684
+ description: z.string().trim().max(500).default(''),
2685
+ server: McpServerFormSchema,
2686
+ })
2687
+ .strict(),
2688
+ response: z
2689
+ .object({
2690
+ extensionId: ExtensionIdSchema,
2691
+ revisionId: ExtensionRevisionIdSchema,
2692
+ /** Credential config fields created for the form's secret rows, by row name. */
2693
+ secretFields: z.array(z.object({ key: z.string(), name: z.string() }).strict()),
2694
+ })
2695
+ .strict(),
2696
+ error: HostApiErrorSchema,
2697
+ }),
2698
+ commitExtensionImport: defineContract({
2699
+ invalidatesSnapshot: true,
2700
+ timeoutMs: 300_000,
2701
+ method: 'POST',
2702
+ path: '/api/extensions/imports/:token/commit',
2703
+ params: z.object({ token: NonEmptyStringSchema }).strict(),
2704
+ request: z.object({ localSlug: z.string().trim().min(3).max(64).optional() }).strict(),
2705
+ response: z
2706
+ .object({ extensionId: ExtensionIdSchema, revisionId: ExtensionRevisionIdSchema, idempotent: z.boolean() })
2707
+ .strict(),
2708
+ error: HostApiErrorSchema,
2709
+ }),
2710
+ dshSettings: defineContract({
2711
+ method: 'GET',
2712
+ path: '/api/dsh/settings',
2713
+ params: EmptyParamsSchema,
2714
+ request: NoRequestBodySchema,
2715
+ response: z.object({ namespaces: z.array(DshSettingsNamespaceSchema) }).strict(),
2716
+ error: HostApiErrorSchema,
2717
+ }),
2718
+ dshSettingsMutate: defineContract({
2719
+ invalidatesSnapshot: true,
2720
+ method: 'POST',
2721
+ path: '/api/dsh/settings/:namespace/mutate',
2722
+ params: dshSettingsParam,
2723
+ request: DshSettingsMutationRequestSchema,
2724
+ response: DshSettingsNamespaceSchema,
2725
+ error: HostApiErrorSchema,
2726
+ }),
2727
+ dshCredentialsDescribe: defineContract({
2728
+ method: 'POST',
2729
+ path: '/api/dsh/credentials/describe',
2730
+ params: EmptyParamsSchema,
2731
+ request: z.object({ refs: z.array(DshCredentialRefSchema).max(64) }).strict(),
2732
+ response: z.object({ credentials: z.record(z.string(), DshCredentialViewSchema) }).strict(),
2733
+ error: HostApiErrorSchema,
2734
+ }),
2735
+ dshCredentialSet: defineContract({
2736
+ invalidatesSnapshot: true,
2737
+ method: 'PUT',
2738
+ path: '/api/dsh/credentials/:ref',
2739
+ params: dshCredentialParam,
2740
+ request: z
2741
+ .object({
2742
+ value: z
2743
+ .string()
2744
+ .min(1)
2745
+ .max(64 * 1024),
2746
+ })
2747
+ .strict(),
2748
+ response: DshCredentialViewSchema,
2749
+ error: HostApiErrorSchema,
2750
+ }),
2751
+ dshCredentialUnset: defineContract({
2752
+ invalidatesSnapshot: true,
2753
+ method: 'DELETE',
2754
+ path: '/api/dsh/credentials/:ref',
2755
+ params: dshCredentialParam,
2756
+ request: NoRequestBodySchema,
2757
+ response: DshCredentialViewSchema,
2758
+ error: HostApiErrorSchema,
2759
+ }),
2760
+ llmProviders: defineContract({
2761
+ method: 'GET',
2762
+ path: '/api/llm/providers',
2763
+ params: EmptyParamsSchema,
2764
+ request: NoRequestBodySchema,
2765
+ response: LlmProviderSettingsSchema,
2766
+ error: HostApiErrorSchema,
2767
+ }),
2768
+ llmDiscoverModels: defineContract({
2769
+ method: 'POST',
2770
+ path: '/api/llm/discover-models',
2771
+ params: EmptyParamsSchema,
2772
+ request: z
2773
+ .object({
2774
+ provider: NonEmptyStringSchema.optional(),
2775
+ settingsNs: NonEmptyStringSchema.optional(),
2776
+ baseURL: z.url().optional(),
2777
+ api: NonEmptyStringSchema.optional(),
2778
+ apiKey: z
2779
+ .string()
2780
+ .min(1)
2781
+ .max(64 * 1024)
2782
+ .optional(),
2783
+ })
2784
+ .strict(),
2785
+ response: z.object({ models: z.array(LlmDiscoveredModelSchema) }).strict(),
2786
+ error: HostApiErrorSchema,
2787
+ }),
2788
+ llmTestProvider: defineContract({
2789
+ method: 'POST',
2790
+ path: '/api/llm/test-provider',
2791
+ params: EmptyParamsSchema,
2792
+ request: z
2793
+ .object({
2794
+ provider: NonEmptyStringSchema,
2795
+ model: NonEmptyStringSchema,
2796
+ settingsNs: NonEmptyStringSchema.optional(),
2797
+ apiKey: z
2798
+ .string()
2799
+ .min(1)
2800
+ .max(64 * 1024)
2801
+ .optional(),
2802
+ baseURL: z.url().optional(),
2803
+ api: NonEmptyStringSchema.optional(),
2804
+ models: z.array(LlmDiscoveredModelSchema).optional(),
2805
+ })
2806
+ .strict(),
2807
+ response: z.object({ provider: NonEmptyStringSchema, model: NonEmptyStringSchema }).strict(),
2808
+ error: HostApiErrorSchema,
2809
+ }),
2810
+ llmProviderRemovalImpact: defineContract({
2811
+ method: 'GET',
2812
+ path: '/api/llm/providers/:provider/removal-impact',
2813
+ params: llmProviderParam,
2814
+ request: NoRequestBodySchema,
2815
+ response: z
2816
+ .object({
2817
+ provider: NonEmptyStringSchema,
2818
+ displayName: NonEmptyStringSchema,
2819
+ declared: z.boolean(),
2820
+ expectedRevision: z.number().int().nonnegative(),
2821
+ models: z.array(NonEmptyStringSchema),
2822
+ blockedReason: z.string(),
2823
+ references: z.array(
2824
+ z
2825
+ .object({
2826
+ agentId: AgentIdSchema,
2827
+ displayName: NonEmptyStringSchema,
2828
+ model: NonEmptyStringSchema,
2829
+ role: z.enum(['primary', 'vision']),
2830
+ scope: z.enum(['configuration', 'context']),
2831
+ channelId: ChannelIdSchema.optional(),
2832
+ channelName: z.string().optional(),
2833
+ episodeStatus: z.enum(['opening', 'active']).optional(),
2834
+ })
2835
+ .strict(),
2836
+ ),
2837
+ })
2838
+ .strict(),
2839
+ error: HostApiErrorSchema,
2840
+ }),
2841
+ llmRemoveProvider: defineContract({
2842
+ invalidatesSnapshot: true,
2843
+ method: 'DELETE',
2844
+ path: '/api/llm/providers/:provider',
2845
+ params: llmProviderParam,
2846
+ request: z.object({ expectedRevision: z.number().int().nonnegative() }).strict(),
2847
+ response: LlmProviderSettingsSchema,
2848
+ error: HostApiErrorSchema,
2849
+ }),
2850
+ llmRestoreProviderModels: defineContract({
2851
+ invalidatesSnapshot: true,
2852
+ method: 'POST',
2853
+ path: '/api/llm/providers/:provider/restore-models',
2854
+ params: llmProviderParam,
2855
+ request: z.object({ expectedRevision: z.number().int().nonnegative() }).strict(),
2856
+ response: LlmProviderSettingsSchema,
2857
+ error: HostApiErrorSchema,
2858
+ }),
2859
+ llmSaveProvider: defineContract({
2860
+ invalidatesSnapshot: true,
2861
+ method: 'POST',
2862
+ path: '/api/llm/providers/:provider',
2863
+ params: llmProviderParam,
2864
+ request: z
2865
+ .object({
2866
+ expectedRevision: z.number().int().nonnegative(),
2867
+ apiKey: z
2868
+ .string()
2869
+ .min(1)
2870
+ .max(64 * 1024)
2871
+ .optional(),
2872
+ displayName: z.string().trim().min(1).max(120).optional(),
2873
+ baseURL: z.url().optional(),
2874
+ api: NonEmptyStringSchema.optional(),
2875
+ models: z.array(LlmDiscoveredModelSchema).optional(),
2876
+ })
2877
+ .strict(),
2878
+ response: LlmProviderSettingsSchema,
2879
+ error: HostApiErrorSchema,
2880
+ }),
2881
+ testConnection: defineContract({
2882
+ invalidatesSnapshot: true,
2883
+ method: 'POST',
2884
+ path: '/api/connections/:connectionId/test',
2885
+ params: connectionParam,
2886
+ request: z.object({ direction: z.enum(['send', 'receive']), channelId: ChannelIdSchema.optional() }).strict(),
2887
+ response: ConnectionTestResultSchema,
2888
+ error: HostApiErrorSchema,
2889
+ }),
2890
+ dynamicInventory: defineContract({
2891
+ method: 'POST',
2892
+ path: '/api/dynamic/:agentId/inventory',
2893
+ params: agentParam,
2894
+ request: z.object({ episodeId: EpisodeIdSchema }).strict(),
2895
+ response: z.object({ rows: z.array(DynamicInventoryRowSchema) }).strict(),
2896
+ error: HostApiErrorSchema,
2897
+ }),
2898
+ dynamicApprove: defineContract({
2899
+ invalidatesSnapshot: true,
2900
+ method: 'POST',
2901
+ path: '/api/dynamic/:agentId/approve',
2902
+ params: agentParam,
2903
+ request: z
2904
+ .object({ episodeId: EpisodeIdSchema, requestId: DynamicIdSchema, pluginRunId: DynamicIdSchema.optional() })
2905
+ .strict(),
2906
+ response: z.object({ accepted: z.boolean() }).strict(),
2907
+ error: HostApiErrorSchema,
2908
+ }),
2909
+ dynamicDecline: defineContract({
2910
+ invalidatesSnapshot: true,
2911
+ method: 'POST',
2912
+ path: '/api/dynamic/:agentId/decline',
2913
+ params: agentParam,
2914
+ request: z
2915
+ .object({ episodeId: EpisodeIdSchema, requestId: DynamicIdSchema, pluginRunId: DynamicIdSchema.optional() })
2916
+ .strict(),
2917
+ response: z.object({ accepted: z.boolean() }).strict(),
2918
+ error: HostApiErrorSchema,
2919
+ }),
2920
+ dynamicInvoke: defineContract({
2921
+ method: 'POST',
2922
+ path: '/api/dynamic/:agentId/invoke',
2923
+ params: agentParam,
2924
+ request: z
2925
+ .object({
2926
+ episodeId: EpisodeIdSchema,
2927
+ pluginId: DynamicIdSchema,
2928
+ pluginRunId: DynamicIdSchema,
2929
+ method: NonEmptyStringSchema,
2930
+ args: JsonValueSchema.optional(),
2931
+ })
2932
+ .strict(),
2933
+ response: z.discriminatedUnion('ok', [
2934
+ z.object({ ok: z.literal(true), value: JsonValueSchema }).strict(),
2935
+ z.object({ ok: z.literal(false), message: z.string() }).strict(),
2936
+ ]),
2937
+ error: HostApiErrorSchema,
2938
+ }),
2939
+ dynamicRunHostHalf: defineContract({
2940
+ method: 'POST',
2941
+ path: '/api/dynamic/:agentId/run-host-half',
2942
+ params: agentParam,
2943
+ request: z
2944
+ .object({
2945
+ episodeId: EpisodeIdSchema,
2946
+ pluginId: DynamicIdSchema,
2947
+ packageId: DynamicIdSchema,
2948
+ mode: z.enum(['run', 'update']),
2949
+ requestId: DynamicIdSchema.nullable().optional(),
2950
+ approveFutureVersions: z.boolean().default(false),
2951
+ })
2952
+ .strict(),
2953
+ response: z.discriminatedUnion('ok', [
2954
+ z
2955
+ .object({
2956
+ ok: z.literal(true),
2957
+ pluginId: DynamicIdSchema,
2958
+ packageId: DynamicIdSchema,
2959
+ pluginRunId: DynamicIdSchema,
2960
+ waitingFor: z.array(z.string()),
2961
+ startedHere: z.boolean(),
2962
+ })
2963
+ .strict(),
2964
+ DynamicErrorDetailsSchema.extend({ ok: z.literal(false) }),
2965
+ ]),
2966
+ error: HostApiErrorSchema,
2967
+ }),
2968
+ dynamicSettleUserRun: defineContract({
2969
+ method: 'POST',
2970
+ path: '/api/dynamic/:agentId/settle-user-run',
2971
+ params: agentParam,
2972
+ request: z
2973
+ .object({ episodeId: EpisodeIdSchema, pluginId: DynamicIdSchema, resolution: DynamicRunResolutionSchema })
2974
+ .strict(),
2975
+ response: DynamicRunResponseSchema,
2976
+ error: HostApiErrorSchema,
2977
+ }),
2978
+ dynamicGetClientCode: defineContract({
2979
+ method: 'POST',
2980
+ path: '/api/dynamic/:agentId/get-client-code',
2981
+ params: agentParam,
2982
+ request: z.object({ episodeId: EpisodeIdSchema, pluginId: DynamicIdSchema, pluginRunId: DynamicIdSchema }).strict(),
2983
+ response: z
2984
+ .object({
2985
+ code: z.string(),
2986
+ name: z.string(),
2987
+ pluginId: DynamicIdSchema,
2988
+ packageId: DynamicIdSchema,
2989
+ pluginRunId: DynamicIdSchema,
2990
+ /** What the candidate declared; preview data hooks enforce exactly what the saved Revision will. */
2991
+ permissions: HostUiPermissionDeclarationSchema.default({ permissions: [], networkOrigins: [] }),
2992
+ })
2993
+ .strict(),
2994
+ error: HostApiErrorSchema,
2995
+ }),
2996
+ dynamicReportRenderFailure: defineContract({
2997
+ method: 'POST',
2998
+ path: '/api/dynamic/:agentId/report-render-failure',
2999
+ params: agentParam,
3000
+ request: z
3001
+ .object({
3002
+ episodeId: EpisodeIdSchema,
3003
+ pluginId: DynamicIdSchema,
3004
+ pluginRunId: DynamicIdSchema,
3005
+ failure: DynamicRenderFailureSchema,
3006
+ })
3007
+ .strict(),
3008
+ response: z.object({ ok: z.literal(true) }).strict(),
3009
+ error: HostApiErrorSchema,
3010
+ }),
3011
+ dynamicReportGuardFailure: defineContract({
3012
+ method: 'POST',
3013
+ path: '/api/dynamic/:agentId/report-guard-failure',
3014
+ params: agentParam,
3015
+ request: z
3016
+ .object({
3017
+ episodeId: EpisodeIdSchema,
3018
+ pluginId: DynamicIdSchema,
3019
+ pluginRunId: DynamicIdSchema,
3020
+ message: z.string().min(1).max(4096),
3021
+ stack: z
3022
+ .string()
3023
+ .max(16 * 1024)
3024
+ .optional(),
3025
+ })
3026
+ .strict(),
3027
+ response: z.object({ ok: z.literal(true) }).strict(),
3028
+ error: HostApiErrorSchema,
3029
+ }),
3030
+ dynamicReportClientVerification: defineContract({
3031
+ method: 'POST',
3032
+ path: '/api/dynamic/:agentId/report-client-verification',
3033
+ params: agentParam,
3034
+ request: z
3035
+ .object({
3036
+ episodeId: EpisodeIdSchema,
3037
+ pluginId: DynamicIdSchema,
3038
+ packageId: DynamicIdSchema,
3039
+ pluginRunId: DynamicIdSchema,
3040
+ renderedPanels: z.array(PanelContributionSchema).max(16).default([]),
3041
+ renderedToolViews: z.array(z.string().trim().min(1).max(64)).max(32).default([]),
3042
+ renderedMessageRenderers: z.array(RichKindSchema).max(32).default([]),
3043
+ renderedPages: z.array(HostPageContributionSchema).max(8).default([]),
3044
+ usedUiComponents: z
3045
+ .array(HostUiKitComponentNameSchema)
3046
+ .max(HostUiKitComponentNameSchema.options.length)
3047
+ .default([]),
3048
+ pageGeometry: z.array(HostUiPageGeometryEvidenceSchema).max(8).default([]),
3049
+ navigationEntries: z.array(z.string().trim().min(1).max(64)).max(8).default([]),
3050
+ })
3051
+ .strict()
3052
+ .refine(
3053
+ (value) =>
3054
+ value.renderedPanels.length > 0 ||
3055
+ value.renderedToolViews.length > 0 ||
3056
+ value.renderedMessageRenderers.length > 0 ||
3057
+ value.renderedPages.length > 0,
3058
+ {
3059
+ message: '动态 Client 必须真实渲染至少一个面板、工具视图、富消息渲染器或页面。',
3060
+ },
3061
+ ),
3062
+ response: z.object({ ok: z.literal(true) }).strict(),
3063
+ error: HostApiErrorSchema,
3064
+ }),
3065
+ saveExtensionFromDynamic: defineContract({
3066
+ invalidatesSnapshot: true,
3067
+ timeoutMs: 300_000,
3068
+ method: 'POST',
3069
+ path: '/api/extensions/save-from-dynamic',
3070
+ params: EmptyParamsSchema,
3071
+ request: z.union([
3072
+ z
3073
+ .object({
3074
+ taskId: AuthoringTaskIdSchema,
3075
+ attemptId: AuthoringAttemptIdSchema,
3076
+ displayName: z.string().trim().min(1).max(80),
3077
+ slug: z
3078
+ .string()
3079
+ .trim()
3080
+ .regex(/^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/u),
3081
+ description: z.string().max(500).default(''),
3082
+ targetExtensionId: ExtensionIdSchema.optional(),
3083
+ })
3084
+ .strict(),
3085
+ z
3086
+ .object({
3087
+ agentId: AgentIdSchema,
3088
+ episodeId: EpisodeIdSchema,
3089
+ pluginId: DynamicIdSchema,
3090
+ packageId: DynamicIdSchema,
3091
+ displayName: z.string().trim().min(1).max(80),
3092
+ slug: z
3093
+ .string()
3094
+ .trim()
3095
+ .regex(/^[a-z0-9][a-z0-9-]{1,62}[a-z0-9]$/u),
3096
+ description: z.string().max(500).default(''),
3097
+ targetExtensionId: ExtensionIdSchema.optional(),
3098
+ })
3099
+ .strict(),
3100
+ ]),
3101
+ response: z
3102
+ .object({
3103
+ extensionId: ExtensionIdSchema,
3104
+ revisionId: ExtensionRevisionIdSchema,
3105
+ activation: z.literal('inactive'),
3106
+ installation: z.literal('uninstalled').optional(),
3107
+ })
3108
+ .strict(),
3109
+ error: HostApiErrorSchema,
3110
+ }),
3111
+ extensionClientCall: defineContract({
3112
+ method: 'POST',
3113
+ path: '/api/extensions/:extensionId/revisions/:revisionId/call',
3114
+ params: extensionRevisionParam,
3115
+ request: z
3116
+ .object({ agentId: AgentIdSchema, method: NonEmptyStringSchema, input: JsonValueSchema.optional() })
3117
+ .strict(),
3118
+ response: z.object({ value: JsonValueSchema }).strict(),
3119
+ error: HostApiErrorSchema,
3120
+ }),
3121
+ extensionClientDiagnostic: defineContract({
3122
+ method: 'POST',
3123
+ path: '/api/extensions/:extensionId/revisions/:revisionId/client-diagnostic',
3124
+ params: extensionRevisionParam,
3125
+ request: z
3126
+ .object({
3127
+ agentId: AgentIdSchema,
3128
+ status: z.enum(['loaded', 'failed']),
3129
+ message: z.string().max(4096).optional(),
3130
+ })
3131
+ .strict(),
3132
+ response: z.object({ accepted: z.literal(true) }).strict(),
3133
+ error: HostApiErrorSchema,
3134
+ }),
3135
+ hostExtensionClientDiagnostic: defineContract({
3136
+ method: 'POST',
3137
+ path: '/api/extensions/:extensionId/revisions/:revisionId/host-client-diagnostic',
3138
+ params: extensionRevisionParam,
3139
+ request: z.object({ status: z.enum(['loaded', 'failed']), message: z.string().max(4096).optional() }).strict(),
3140
+ response: z.object({ accepted: z.literal(true) }).strict(),
3141
+ error: HostApiErrorSchema,
3142
+ }),
3143
+ activateExtension: defineContract({
3144
+ invalidatesSnapshot: true,
3145
+ method: 'POST',
3146
+ path: '/api/agents/:agentId/extensions/:extensionId/activation',
3147
+ params: agentExtensionParam,
3148
+ request: z
3149
+ .object({
3150
+ revisionId: ExtensionRevisionIdSchema,
3151
+ permissionApproval: z
3152
+ .object({ permissionDigest: z.string().regex(/^[a-f0-9]{64}$/u) })
3153
+ .strict()
3154
+ .optional(),
3155
+ })
3156
+ .strict(),
3157
+ response: z
3158
+ .object({
3159
+ activation: z
3160
+ .object({
3161
+ agentId: AgentIdSchema,
3162
+ extensionId: ExtensionIdSchema,
3163
+ extensionRevisionId: ExtensionRevisionIdSchema,
3164
+ config: JsonValueSchema,
3165
+ configuredSecrets: z.array(z.string()).optional(),
3166
+ activatedAt: z.number().int().safe().nonnegative(),
3167
+ })
3168
+ .strict(),
3169
+ })
3170
+ .strict(),
3171
+ error: HostApiErrorSchema,
3172
+ }),
3173
+ deactivateExtension: defineContract({
3174
+ invalidatesSnapshot: true,
3175
+ method: 'DELETE',
3176
+ path: '/api/agents/:agentId/extensions/:extensionId/activation',
3177
+ params: agentExtensionParam,
3178
+ request: NoRequestBodySchema,
3179
+ response: z.object({ disabled: z.literal(true) }).strict(),
3180
+ error: HostApiErrorSchema,
3181
+ }),
3182
+ updateExtensionActivationConfig: defineContract({
3183
+ invalidatesSnapshot: true,
3184
+ method: 'PUT',
3185
+ path: '/api/agents/:agentId/extensions/:extensionId/activation/config',
3186
+ params: agentExtensionParam,
3187
+ request: z
3188
+ .object({
3189
+ config: JsonValueSchema,
3190
+ /** Write-only credential drafts keyed by secret field; an absent or empty draft keeps the stored one. */
3191
+ secrets: z.record(z.string(), z.string().max(8192)).optional(),
3192
+ })
3193
+ .strict(),
3194
+ response: z.object({ config: JsonValueSchema, configuredSecrets: z.array(z.string()).optional() }).strict(),
3195
+ error: HostApiErrorSchema,
3196
+ }),
3197
+ updateHostExtensionConfig: defineContract({
3198
+ invalidatesSnapshot: true,
3199
+ timeoutMs: 300_000,
3200
+ method: 'PUT',
3201
+ path: '/api/extensions/:extensionId/installation/config',
3202
+ params: extensionParam,
3203
+ request: z.object({ config: JsonValueSchema }).strict(),
3204
+ response: z.object({ config: JsonValueSchema }).strict(),
3205
+ error: HostApiErrorSchema,
3206
+ }),
3207
+ installHostExtension: defineContract({
3208
+ invalidatesSnapshot: true,
3209
+ timeoutMs: 300_000,
3210
+ method: 'PUT',
3211
+ path: '/api/extensions/:extensionId/installation',
3212
+ params: extensionParam,
3213
+ request: z
3214
+ .object({
3215
+ revisionId: ExtensionRevisionIdSchema,
3216
+ permissionApproval: z
3217
+ .object({ permissionDigest: z.string().regex(/^[a-f0-9]{64}$/u) })
3218
+ .strict()
3219
+ .optional(),
3220
+ })
3221
+ .strict(),
3222
+ response: z
3223
+ .object({
3224
+ installation: z
3225
+ .object({
3226
+ extensionId: ExtensionIdSchema,
3227
+ extensionRevisionId: ExtensionRevisionIdSchema,
3228
+ installedAt: z.number().int().safe().nonnegative(),
3229
+ config: JsonValueSchema,
3230
+ })
3231
+ .strict(),
3232
+ })
3233
+ .strict(),
3234
+ error: HostApiErrorSchema,
3235
+ }),
3236
+ uninstallHostExtension: defineContract({
3237
+ invalidatesSnapshot: true,
3238
+ timeoutMs: 300_000,
3239
+ method: 'DELETE',
3240
+ path: '/api/extensions/:extensionId/installation',
3241
+ params: extensionParam,
3242
+ request: NoRequestBodySchema,
3243
+ response: z.object({ uninstalled: z.literal(true) }).strict(),
3244
+ error: HostApiErrorSchema,
3245
+ }),
3246
+ updateHostUiPagePreferences: defineContract({
3247
+ invalidatesSnapshot: true,
3248
+ method: 'PUT',
3249
+ path: '/api/host-ui/page-preferences',
3250
+ params: EmptyParamsSchema,
3251
+ request: z
3252
+ .object({
3253
+ expectedRevision: z.number().int().nonnegative(),
3254
+ entries: z.array(z.object({ pageInstanceId: HostUiPageInstanceIdSchema, visible: z.boolean() }).strict()),
3255
+ })
3256
+ .strict(),
3257
+ response: z.object({ revision: z.number().int().positive() }).strict(),
3258
+ error: HostApiErrorSchema,
3259
+ }),
3260
+ callHostUiPage: defineContract({
3261
+ method: 'POST',
3262
+ path: '/api/host-ui/pages/:pageInstanceId/call',
3263
+ params: z.object({ pageInstanceId: HostUiPageInstanceIdSchema }).strict(),
3264
+ request: z.object({ method: NonEmptyStringSchema, input: JsonValueSchema.default({}) }).strict(),
3265
+ response: z.object({ value: JsonValueSchema }).strict(),
3266
+ error: HostApiErrorSchema,
3267
+ }),
3268
+ reportHostUiPageDiagnostic: defineContract({
3269
+ method: 'POST',
3270
+ path: '/api/host-ui/pages/:pageInstanceId/diagnostic',
3271
+ params: z.object({ pageInstanceId: HostUiPageInstanceIdSchema }).strict(),
3272
+ request: z
3273
+ .object({
3274
+ status: z.enum(['ready', 'load-failed', 'navigation-failed', 'rpc-failed']),
3275
+ message: z.string().max(2000).optional(),
3276
+ })
3277
+ .strict(),
3278
+ response: z.object({ recorded: z.literal(true) }).strict(),
3279
+ error: HostApiErrorSchema,
3280
+ }),
3281
+ markChannelRead: defineContract({
3282
+ method: 'POST',
3283
+ path: '/api/channels/:channelId/read',
3284
+ params: channelParam,
3285
+ request: z
3286
+ .object({
3287
+ /** Defaults to the newest inbound fact visible now. */
3288
+ upTo: z
3289
+ .object({ occurredAt: z.number().int().safe().nonnegative(), sourceId: ChannelEventIdSchema })
3290
+ .strict()
3291
+ .optional(),
3292
+ })
3293
+ .strict(),
3294
+ response: z.object({ channelId: ChannelIdSchema, activity: ChannelActivitySummarySchema }).strict(),
3295
+ error: HostApiErrorSchema,
3296
+ }),
3297
+ listAttention: defineContract({
3298
+ method: 'GET',
3299
+ path: '/api/attention',
3300
+ params: EmptyParamsSchema,
3301
+ request: NoRequestBodySchema,
3302
+ response: AttentionListSchema,
3303
+ error: HostApiErrorSchema,
3304
+ }),
3305
+ dismissAttention: defineContract({
3306
+ method: 'POST',
3307
+ path: '/api/attention/:attentionId/dismiss',
3308
+ params: z.object({ attentionId: z.string().min(1).max(200) }).strict(),
3309
+ request: NoRequestBodySchema,
3310
+ response: z.object({ dismissed: z.literal(true), revision: z.string().min(1) }).strict(),
3311
+ error: HostApiErrorSchema,
3312
+ }),
3313
+ getChannelPending: defineContract({
3314
+ method: 'GET',
3315
+ path: '/api/channels/:channelId/pending',
3316
+ params: channelParam,
3317
+ request: NoRequestBodySchema,
3318
+ response: ChannelPendingContextSchema,
3319
+ error: HostApiErrorSchema,
3320
+ }),
3321
+ stopChannelTask: defineContract({
3322
+ method: 'POST',
3323
+ path: '/api/channels/:channelId/stop',
3324
+ params: channelParam,
3325
+ request: z.object({ expectedEpisodeId: EpisodeIdSchema.optional() }).strict(),
3326
+ response: z
3327
+ .object({
3328
+ result: z.enum(['stopped', 'idle']),
3329
+ episodeId: EpisodeIdSchema.optional(),
3330
+ /** Admitted messages that were not yet consumed and remain queued for the next turn. */
3331
+ retainedPending: z.number().int().nonnegative(),
3332
+ })
3333
+ .strict(),
3334
+ error: HostApiErrorSchema,
3335
+ }),
3336
+ resolveOutbound: defineContract({
3337
+ method: 'POST',
3338
+ path: '/api/outbound/:outboundId/resolve',
3339
+ params: z.object({ outboundId: OutboundIntentIdSchema }).strict(),
3340
+ request: z.object({ action: z.enum(['retry', 'confirm-delivered']) }).strict(),
3341
+ response: z
3342
+ .object({
3343
+ outboundId: OutboundIntentIdSchema,
3344
+ action: z.enum(['retry', 'confirm-delivered']),
3345
+ deliveryState: z.enum(['planned', 'sending', 'sent', 'partially-sent', 'failed', 'unknown']),
3346
+ resolvedAt: z.number().int().safe().nonnegative(),
3347
+ })
3348
+ .strict(),
3349
+ error: HostApiErrorSchema,
3350
+ }),
3351
+ getChannelActivity: defineContract({
3352
+ method: 'GET',
3353
+ path: '/api/activity',
3354
+ params: ActivityQuerySchema,
3355
+ request: NoRequestBodySchema,
3356
+ response: ChannelActivitySeriesSchema,
3357
+ error: HostApiErrorSchema,
3358
+ }),
3359
+ listAgentRevisions: defineContract({
3360
+ method: 'GET',
3361
+ path: '/api/agents/:agentId/revisions',
3362
+ params: agentParam,
3363
+ request: NoRequestBodySchema,
3364
+ response: AgentRevisionHistorySchema,
3365
+ error: HostApiErrorSchema,
3366
+ }),
3367
+ restoreAgentRevision: defineContract({
3368
+ invalidatesSnapshot: true,
3369
+ method: 'POST',
3370
+ path: '/api/agents/:agentId/revisions/:revisionId/restore',
3371
+ params: agentRevisionParam,
3372
+ request: z.object({ expectedCurrentRevisionId: AgentRevisionIdSchema }).strict(),
3373
+ response: z.object({ currentRevisionId: AgentRevisionIdSchema }).strict(),
3374
+ error: HostApiErrorSchema,
3375
+ }),
3376
+ updateAgentAppearance: defineContract({
3377
+ invalidatesSnapshot: true,
3378
+ method: 'PATCH',
3379
+ path: '/api/agents/:agentId/appearance',
3380
+ params: agentParam,
3381
+ request: z
3382
+ .object({
3383
+ hue: z.number().int().min(0).max(359).nullable().optional(),
3384
+ /** `null` removes the avatar; uploading uses `uploadAgentAvatar`. */
3385
+ avatarAssetId: z.null().optional(),
3386
+ })
3387
+ .strict(),
3388
+ response: z.object({ agentId: AgentIdSchema, appearance: AgentAppearanceSchema }).strict(),
3389
+ error: HostApiErrorSchema,
3390
+ }),
3391
+ uploadAgentAvatar: defineContract({
3392
+ invalidatesSnapshot: true,
3393
+ method: 'POST',
3394
+ path: '/api/agents/:agentId/avatar',
3395
+ params: agentParam,
3396
+ request: BinaryUploadRequestSchema,
3397
+ encodeRequest: encodeBinaryUpload,
3398
+ response: z.object({ agentId: AgentIdSchema, appearance: AgentAppearanceSchema }).strict(),
3399
+ error: HostApiErrorSchema,
3400
+ }),
3401
+ deleteLocalExtension: defineContract({
3402
+ invalidatesSnapshot: true,
3403
+ method: 'DELETE',
3404
+ path: '/api/extensions/:extensionId',
3405
+ params: extensionParam,
3406
+ request: NoRequestBodySchema,
3407
+ response: z.object({ deleted: z.literal(true) }).strict(),
3408
+ error: HostApiErrorSchema,
3409
+ }),
3410
+ } as const
3411
+
3412
+ export type HostApiContractName = keyof typeof HostApiContracts
3413
+ export type HostApiParams<Name extends HostApiContractName> = z.input<(typeof HostApiContracts)[Name]['params']>
3414
+ export type HostApiRequest<Name extends HostApiContractName> = z.input<(typeof HostApiContracts)[Name]['request']>
3415
+ export type HostApiResponse<Name extends HostApiContractName> = z.output<(typeof HostApiContracts)[Name]['response']>
3416
+ export type HostApiContractParams<Contract> = Contract extends {
3417
+ readonly params: z.ZodType<unknown, infer Input>
3418
+ }
3419
+ ? Input
3420
+ : never
3421
+ export type HostApiContractRequest<Contract> = Contract extends {
3422
+ readonly request: z.ZodType<unknown, infer Input>
3423
+ }
3424
+ ? Input
3425
+ : never
3426
+ export type HostApiContractResponse<Contract> = Contract extends {
3427
+ readonly response: z.ZodType<infer Output, unknown>
3428
+ }
3429
+ ? Output
3430
+ : never
3431
+
3432
+ const pathParameterPattern = /:([A-Za-z][A-Za-z0-9]*)/gu
3433
+
3434
+ /** Build the transport URL only after all path/query parameters pass the shared schema. */
3435
+ const buildValidatedHostApiPath = (
3436
+ contract: { readonly path: string; readonly params: AnySchema },
3437
+ input: unknown,
3438
+ ): string => {
3439
+ const params = z.record(z.string(), z.unknown()).parse(contract.params.parse(input))
3440
+ const consumed = new Set<string>()
3441
+ const pathname = contract.path.replace(pathParameterPattern, (_placeholder, key: string) => {
3442
+ const value = params[key]
3443
+ if (typeof value !== 'string') throw new TypeError(`Host API path parameter ${key} must be a string.`)
3444
+ consumed.add(key)
3445
+ return encodeURIComponent(value)
3446
+ })
3447
+ const query = new URLSearchParams()
3448
+ for (const [key, value] of Object.entries(params)) {
3449
+ if (consumed.has(key) || value === undefined) continue
3450
+ if (typeof value !== 'string' && typeof value !== 'number' && typeof value !== 'boolean') {
3451
+ throw new TypeError(`Host API query parameter ${key} must be a string, number, or boolean.`)
3452
+ }
3453
+ query.set(key, String(value))
3454
+ }
3455
+ const suffix = query.toString()
3456
+ return suffix ? `${pathname}?${suffix}` : pathname
3457
+ }
3458
+
3459
+ export function buildHostApiContractPath<Params extends AnySchema>(
3460
+ contract: { readonly path: string; readonly params: Params },
3461
+ input: unknown,
3462
+ ): string {
3463
+ return buildValidatedHostApiPath(contract, input)
3464
+ }
3465
+
3466
+ export function buildHostApiPath<Name extends HostApiContractName>(name: Name, input: HostApiParams<Name>): string {
3467
+ return buildValidatedHostApiPath(HostApiContracts[name], input)
3468
+ }