@nekro-nxt/contracts 0.5.0 → 0.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/host-api.ts CHANGED
@@ -10,7 +10,30 @@ import {
10
10
  hostLogStatusSchema,
11
11
  } from './logs.js'
12
12
  import { McpServerFormSchema } from './extension-capabilities.js'
13
+ import {
14
+ BoundCredentialsSchema,
15
+ CredentialBindingsRequestSchema,
16
+ CreateCredentialRequestSchema,
17
+ CredentialsViewSchema,
18
+ CredentialUsesViewSchema,
19
+ RemoveCredentialRequestSchema,
20
+ RemoveCredentialResultSchema,
21
+ ReplaceCredentialValueRequestSchema,
22
+ SetModelProviderAccessRequestSchema,
23
+ UpdateCredentialRequestSchema,
24
+ } from './credentials.js'
25
+ import {
26
+ AddLinkedAccountRequestSchema,
27
+ LinkedAccountMethodSchema,
28
+ LinkedAccountPlatformIdSchema,
29
+ LinkedAccountUsesViewSchema,
30
+ LinkedAccountViewSchema,
31
+ LocalGithubLoginSchema,
32
+ RemoveLinkedAccountRequestSchema,
33
+ ReplaceLinkedAccountTokenRequestSchema,
34
+ } from './linked-accounts.js'
13
35
  import { HostUpgradeSummarySchema, RuntimeCompatibilityDiagnosticSchema } from './runtime-compatibility.js'
36
+ import { AgentAliasesSchema, ResponseTimingSettingsSchema } from './response-timing.js'
14
37
  import {
15
38
  AgentIdSchema,
16
39
  AgentRevisionIdSchema,
@@ -23,6 +46,8 @@ import {
23
46
  ChannelIdSchema,
24
47
  ChannelMemberIdSchema,
25
48
  ConnectionIdSchema,
49
+ CredentialIdSchema,
50
+ LinkedAccountIdSchema,
26
51
  ConnectionEventIdSchema,
27
52
  DshPluginEntryIdSchema,
28
53
  DshPluginPackageIdSchema,
@@ -45,6 +70,7 @@ import {
45
70
  CommunityExtensionDetailSchema,
46
71
  CommunityExtensionSummarySchema,
47
72
  CommunityExtensionSortSchema,
73
+ CommunityFreshnessSchema,
48
74
  CommunityListingInputSchema,
49
75
  CommunityReleaseIdSchema,
50
76
  CommunityReviewStatusSchema,
@@ -289,7 +315,7 @@ export const ImageUnderstandingPolicyApiSchema = z
289
315
  restoreAfterCompaction: z
290
316
  .object({
291
317
  recentMessages: z.number().int().min(1).max(100),
292
- maxImages: z.number().int().min(1).max(50),
318
+ maxImages: z.number().int().min(0).max(50),
293
319
  })
294
320
  .strict(),
295
321
  })
@@ -307,15 +333,18 @@ export const ImageUnderstandingPolicyApiSchema = z
307
333
  })
308
334
  .strict()
309
335
 
310
- export const CommandAccountServiceSchema = z.enum(['github'])
311
- export type CommandAccountService = z.output<typeof CommandAccountServiceSchema>
312
-
313
336
  export const CommandAccountUsageSchema = z.enum(['read', 'issues', 'pull-requests', 'push'])
337
+ /** What the user wants a granted account used for; told to the agent only. */
338
+ export type CommandAccountUsage = z.output<typeof CommandAccountUsageSchema>
314
339
 
340
+ /**
341
+ * One linked account the agent's commands act as, at most one per platform (`service`). Grants saved before linked
342
+ * accounts name the account without its `acc_` prefix; both forms resolve to the same account.
343
+ */
315
344
  const CommandAccountGrantSchema = z
316
345
  .object({
317
- service: CommandAccountServiceSchema,
318
- accountId: NonEmptyStringSchema.max(64),
346
+ service: LinkedAccountPlatformIdSchema,
347
+ accountId: NonEmptyStringSchema.max(72),
319
348
  /** Told to the agent as guidance, not enforced; empty gives no hint. */
320
349
  usage: z.array(CommandAccountUsageSchema).max(4).default([]),
321
350
  })
@@ -336,7 +365,9 @@ const AgentCapabilitiesSchema = z
336
365
  /** Network for commands; absent in older clients means on. */
337
366
  commandNetwork: z.boolean().default(true),
338
367
  communityFeedbackAuto: z.boolean().default(false),
339
- /** Accounts this agent's commands may act as; absent in older clients means none. */
368
+ /** Voice messages reach the agent with a transcript (附加功能 F10). */
369
+ voiceTranscription: z.boolean().default(false),
370
+ /** Linked accounts this agent's commands act as, one per platform; absent in older clients means none. */
340
371
  commandAccounts: z.array(CommandAccountGrantSchema).max(8).default([]),
341
372
  /**
342
373
  * What the agent may do with extensions without the console: `manual` only develops and test-runs, `confirm` asks
@@ -345,12 +376,23 @@ const AgentCapabilitiesSchema = z
345
376
  extensionAutonomy: z.enum(['manual', 'confirm', 'autonomous']).default('manual'),
346
377
  /** Whose requests make the agent develop or change extensions: anyone in the channel, or administrators only. */
347
378
  extensionRequesters: z.enum(['everyone', 'admins']).default('everyone'),
379
+ /**
380
+ * Whose requests make the agent change extension configuration. With `everyone`, members may only have it change
381
+ * ordinary fields of its own configuration; network fields, the host layer and credentials still need an
382
+ * administrator.
383
+ */
384
+ extensionConfigRequesters: z.enum(['everyone', 'admins']).default('admins'),
348
385
  })
349
386
  .strict()
350
387
 
351
388
  export const DynamicClientApprovalPolicySchema = z.enum(['manual', 'automatic'])
352
389
 
353
- export const NotificationEventKeySchema = z.enum(['dynamic-client-approval-requested', 'community-feedback-update'])
390
+ export const NotificationEventKeySchema = z.enum([
391
+ 'dynamic-client-approval-requested',
392
+ 'community-feedback-update',
393
+ 'credential-requested',
394
+ 'extension-notification',
395
+ ])
354
396
  export type NotificationEventKey = z.output<typeof NotificationEventKeySchema>
355
397
 
356
398
  export const NotificationSettingsViewSchema = z
@@ -368,6 +410,8 @@ export const NotificationSettingsViewSchema = z
368
410
  .object({
369
411
  'dynamic-client-approval-requested': z.boolean(),
370
412
  'community-feedback-update': z.boolean().default(true),
413
+ 'credential-requested': z.boolean().default(true),
414
+ 'extension-notification': z.boolean().default(true),
371
415
  })
372
416
  .strict(),
373
417
  })
@@ -386,7 +430,12 @@ export const UpdateNotificationSettingsRequestSchema = z
386
430
  })
387
431
  .strict(),
388
432
  events: z
389
- .object({ 'dynamic-client-approval-requested': z.boolean(), 'community-feedback-update': z.boolean().optional() })
433
+ .object({
434
+ 'dynamic-client-approval-requested': z.boolean(),
435
+ 'community-feedback-update': z.boolean().optional(),
436
+ 'credential-requested': z.boolean().optional(),
437
+ 'extension-notification': z.boolean().optional(),
438
+ })
390
439
  .strict(),
391
440
  })
392
441
  .strict()
@@ -406,6 +455,122 @@ export const ClientNotificationFeedResponseSchema = z
406
455
  })
407
456
  .strict()
408
457
 
458
+ /** Which model writes one kind of compaction summary: that kind's default, or a model chosen for this agent. */
459
+ export const AgentCompactionModelChoiceSchema = z.discriminatedUnion('mode', [
460
+ z.object({ mode: z.literal('follow') }).strict(),
461
+ z.object({ mode: z.literal('custom'), model: AgentModelSchema }).strict(),
462
+ ])
463
+
464
+ /**
465
+ * In-session compaction follows the main model by default, so the replayed conversation hits the provider's cache;
466
+ * compaction into a new session follows the host's compaction model.
467
+ */
468
+ export const AgentCompactionPolicySchema = z
469
+ .object({ inSession: AgentCompactionModelChoiceSchema, newSession: AgentCompactionModelChoiceSchema })
470
+ .strict()
471
+
472
+ /** The host's idle compaction threshold, summary model and wait limit. */
473
+ export const CompactionSettingsSchema = z
474
+ .object({
475
+ /** Minimum context occupancy for idle rollover; older settings inherit 50%. */
476
+ idleThresholdPercent: z.number().int().min(1).max(100).default(50),
477
+ newSessionModel: z.discriminatedUnion('mode', [
478
+ z.object({ mode: z.literal('main') }).strict(),
479
+ z.object({ mode: z.literal('custom'), model: AgentModelSchema }).strict(),
480
+ ]),
481
+ /** Past this the new session starts with a fallback note instead of a summary. */
482
+ timeoutSeconds: z.number().int().min(30).max(900),
483
+ })
484
+ .strict()
485
+
486
+ export type CompactionSettings = z.output<typeof CompactionSettingsSchema>
487
+
488
+ export const DEFAULT_COMPACTION_SETTINGS: CompactionSettings = {
489
+ idleThresholdPercent: 50,
490
+ newSessionModel: { mode: 'main' },
491
+ timeoutSeconds: 180,
492
+ }
493
+
494
+ export const FALLBACK_MODEL_LIMIT = 3
495
+
496
+ /**
497
+ * Models an agent switches to, in order, when its main model has used up its quota or stays unavailable after the
498
+ * usual retries. Empty leaves the main model alone, as before fallback models existed.
499
+ */
500
+ export const AgentFallbackModelsSchema = z
501
+ .array(AgentModelSchema)
502
+ .max(FALLBACK_MODEL_LIMIT)
503
+ .refine(
504
+ (models) => new Set(models.map((model) => `${model.provider}\0${model.model}`)).size === models.length,
505
+ '备用模型不能重复。',
506
+ )
507
+
508
+ /** Why a channel conversation left its main model. */
509
+ export const ModelFallbackReasonSchema = z.enum(['quota', 'rate-limit', 'unavailable'])
510
+ export type ModelFallbackReason = z.output<typeof ModelFallbackReasonSchema>
511
+
512
+ /** How long a model that failed is passed over before conversations try it again. */
513
+ export const ModelFallbackSettingsSchema = z
514
+ .object({
515
+ /** After rate limits, server errors, timeouts or empty replies that outlasted the usual retries. */
516
+ unavailableCooldownMinutes: z.number().int().min(1).max(240),
517
+ /** After the quota or balance ran out. */
518
+ quotaCooldownMinutes: z.number().int().min(5).max(1440),
519
+ })
520
+ .strict()
521
+
522
+ export type ModelFallbackSettings = z.output<typeof ModelFallbackSettingsSchema>
523
+
524
+ export const DEFAULT_MODEL_FALLBACK_SETTINGS: ModelFallbackSettings = {
525
+ unavailableCooldownMinutes: 10,
526
+ quotaCooldownMinutes: 60,
527
+ }
528
+
529
+ /** The name an agent passes to open a sub-conversation on its main model; no candidate may take it. */
530
+ export const SUB_CONVERSATION_MAIN_MODEL = '主模型'
531
+
532
+ export const SUB_CONVERSATION_MODEL_LIMITS = { candidates: 8, name: 20, guidance: 200 } as const
533
+
534
+ /**
535
+ * Models an agent may open sub-conversations with, each named so the agent can pick it and described so it knows
536
+ * when to. Without a `default`, a sub-conversation opened without naming a model follows the main model.
537
+ */
538
+ export const AgentSubConversationModelsSchema = z
539
+ .object({
540
+ candidates: z
541
+ .array(
542
+ z
543
+ .object({
544
+ // Names are listed to the agent separated by commas, so they carry none and no line breaks.
545
+ name: z
546
+ .string()
547
+ .trim()
548
+ .min(1, '请填写子对话模型的名称。')
549
+ .max(SUB_CONVERSATION_MODEL_LIMITS.name)
550
+ .regex(/^[^\s,,、]+(?: [^\s,,、]+)*$/u, '子对话模型的名称不能含换行、逗号或顿号。')
551
+ .refine((name) => name !== SUB_CONVERSATION_MAIN_MODEL, `「${SUB_CONVERSATION_MAIN_MODEL}」是保留名称。`),
552
+ model: AgentModelSchema,
553
+ guidance: z.string().trim().max(SUB_CONVERSATION_MODEL_LIMITS.guidance),
554
+ })
555
+ .strict(),
556
+ )
557
+ .max(SUB_CONVERSATION_MODEL_LIMITS.candidates),
558
+ default: z.string().trim().min(1).optional(),
559
+ })
560
+ .strict()
561
+ .superRefine((policy, context) => {
562
+ const names = policy.candidates.map((candidate) => candidate.name)
563
+ if (new Set(names).size !== names.length)
564
+ context.addIssue({ code: 'custom', message: '子对话模型的名称不能重复。' })
565
+ if (policy.default !== undefined && !names.includes(policy.default)) {
566
+ context.addIssue({ code: 'custom', message: '默认的子对话模型不在候选里。' })
567
+ }
568
+ })
569
+
570
+ export type AgentSubConversationModels = z.output<typeof AgentSubConversationModelsSchema>
571
+
572
+ export const NO_SUB_CONVERSATION_MODELS: AgentSubConversationModels = { candidates: [] }
573
+
409
574
  const AgentRevisionRequestContentSchema = z
410
575
  .object({
411
576
  displayName: z.string().trim().min(1).max(80),
@@ -417,6 +582,10 @@ const AgentRevisionRequestContentSchema = z
417
582
  model: AgentModelSchema,
418
583
  imagePolicy: ImageUnderstandingPolicyApiSchema.optional(),
419
584
  dynamicClientApprovalPolicy: DynamicClientApprovalPolicySchema.optional(),
585
+ compaction: AgentCompactionPolicySchema.optional(),
586
+ subConversationModels: AgentSubConversationModelsSchema.optional(),
587
+ fallbackModels: AgentFallbackModelsSchema.optional(),
588
+ aliases: AgentAliasesSchema.optional(),
420
589
  })
421
590
  .strict()
422
591
 
@@ -454,14 +623,16 @@ const UpdateAgentCapabilitiesRequestSchema = AgentCapabilitiesSchema.partial()
454
623
  codeRun: z.boolean().optional(),
455
624
  commandNetwork: z.boolean().optional(),
456
625
  communityFeedbackAuto: z.boolean().optional(),
626
+ voiceTranscription: z.boolean().optional(),
457
627
  commandAccounts: z.array(CommandAccountGrantSchema).max(8).optional(),
458
628
  extensionAutonomy: z.enum(['manual', 'confirm', 'autonomous']).optional(),
459
629
  extensionRequesters: z.enum(['everyone', 'admins']).optional(),
630
+ extensionConfigRequesters: z.enum(['everyone', 'admins']).optional(),
460
631
  })
461
632
  .strict()
462
633
  .refine((value) => Object.values(value).some((entry) => entry !== undefined), '至少提供一个能力。')
463
634
 
464
- const TriggerPolicySchema = z.enum(['always', 'mentioned-or-replied', 'command', 'observe-only'])
635
+ const TriggerPolicySchema = z.enum(['always', 'mentioned-or-replied', 'smart', 'command', 'observe-only'])
465
636
 
466
637
  export const WorkTreeOrderSchema = z
467
638
  .object({
@@ -473,6 +644,22 @@ export const WorkTreeOrderSchema = z
473
644
 
474
645
  export type WorkTreeOrder = z.output<typeof WorkTreeOrderSchema>
475
646
 
647
+ /** The group key of 「未接线」 in `collapsedGroupIds`; agent groups use the agent id. */
648
+ export const UNBOUND_CHANNEL_GROUP_ID = 'unbound'
649
+
650
+ /**
651
+ * How the console arranges its channel list: pinned channels in their order and the groups it has collapsed. Shared
652
+ * by every device signed in to the console; it never changes bindings.
653
+ */
654
+ export const ChannelListViewSchema = z
655
+ .object({
656
+ pinnedChannelIds: z.array(ChannelIdSchema).max(200),
657
+ collapsedGroupIds: z.array(z.union([AgentIdSchema, z.literal(UNBOUND_CHANNEL_GROUP_ID)])).max(500),
658
+ })
659
+ .strict()
660
+
661
+ export type ChannelListView = z.output<typeof ChannelListViewSchema>
662
+
476
663
  const SnapshotMessagePartSchema = z.discriminatedUnion('type', [
477
664
  z.object({ type: z.literal('text'), text: z.string() }).strict(),
478
665
  z
@@ -542,16 +729,36 @@ export const HostSnapshotMessageSchema = z
542
729
  trigger: z.enum(['default', 'suppress', 'force']),
543
730
  hidden: z.boolean(),
544
731
  annotation: z.string().optional(),
732
+ /** What NekroNXT itself added for the agent, such as the transcript of a voice message. */
733
+ hostAnnotations: z.array(z.object({ label: z.string(), text: z.string() }).strict()).optional(),
545
734
  extensions: z.array(z.string()),
546
735
  problems: z.array(z.string()).optional(),
547
736
  })
548
737
  .strict()
549
738
  .optional(),
739
+ /** Extensions that processed the agent's message before it was sent; `followUp` marks a message one appended. */
740
+ outboundProcessing: z
741
+ .object({
742
+ extensions: z.array(z.string()),
743
+ rewritten: z.boolean(),
744
+ followUp: z.literal(true).optional(),
745
+ })
746
+ .strict()
747
+ .optional(),
550
748
  /** A scheduled task came due: a Host fact shown as a notice, not a member message. */
551
749
  scheduledTask: z
552
750
  .object({ label: z.string(), extensionName: z.string().optional(), note: z.string().optional() })
553
751
  .strict()
554
752
  .optional(),
753
+ /** The agent finished waiting for someone to finish speaking: a Host fact shown as a notice. */
754
+ responseWait: z
755
+ .object({
756
+ displayName: z.string().optional(),
757
+ cause: z.enum(['quiet', 'limit']),
758
+ waitedSeconds: z.number().int().nonnegative(),
759
+ })
760
+ .strict()
761
+ .optional(),
555
762
  })
556
763
  .strict()
557
764
 
@@ -638,7 +845,14 @@ export const ChannelFactSseDataSchema = z
638
845
  })
639
846
  .strict()
640
847
 
641
- export const ChannelRuntimePhaseSchema = z.enum(['idle', 'thinking', 'using-tool', 'waiting-input', 'unavailable'])
848
+ export const ChannelRuntimePhaseSchema = z.enum([
849
+ 'idle',
850
+ 'thinking',
851
+ 'using-tool',
852
+ 'compacting',
853
+ 'waiting-input',
854
+ 'unavailable',
855
+ ])
642
856
 
643
857
  export const ChannelRuntimeUsageSchema = z
644
858
  .object({
@@ -651,6 +865,12 @@ export const ChannelRuntimeUsageSchema = z
651
865
  })
652
866
  .strict()
653
867
 
868
+ /** The latest progress a running extension tool reported. */
869
+ export const ChannelRuntimeToolProgressSchema = z
870
+ .object({ text: z.string().min(1).max(200), fraction: z.number().min(0).max(1).optional() })
871
+ .strict()
872
+ export type ChannelRuntimeToolProgress = z.output<typeof ChannelRuntimeToolProgressSchema>
873
+
654
874
  const channelRuntimeToolFields = {
655
875
  callId: NonEmptyStringSchema,
656
876
  name: NonEmptyStringSchema,
@@ -659,8 +879,11 @@ const channelRuntimeToolFields = {
659
879
  inputPreview: z.string().optional(),
660
880
  resultPreview: z.string().optional(),
661
881
  wroteToChannel: z.boolean().optional(),
662
- deliveryState: z.enum(['sent', 'partially-sent', 'failed', 'unknown']).optional(),
882
+ /** `cancelled`: an extension stopped the message before it was sent. */
883
+ deliveryState: z.enum(['sent', 'partially-sent', 'failed', 'unknown', 'cancelled']).optional(),
663
884
  durationMs: z.number().int().nonnegative().optional(),
885
+ /** Set while the tool runs and has reported progress. */
886
+ progress: ChannelRuntimeToolProgressSchema.optional(),
664
887
  }
665
888
 
666
889
  export const ChannelRuntimeToolSchema = z
@@ -868,6 +1091,21 @@ export const ChannelRuntimeProjectionSchema = z
868
1091
  episodeId: EpisodeIdSchema.optional(),
869
1092
  phase: ChannelRuntimePhaseSchema,
870
1093
  summary: z.string(),
1094
+ /** A compaction under way: inside the current session, or before a new session opens. */
1095
+ compaction: z
1096
+ .object({ kind: z.enum(['in-session', 'new-session']), startedAt: z.number().int().nonnegative() })
1097
+ .strict()
1098
+ .optional(),
1099
+ /** The conversation runs on a fallback model; it tries the main model again at a new message after `retryMainAt`. */
1100
+ modelFallback: z
1101
+ .object({
1102
+ model: AgentModelSchema,
1103
+ reason: ModelFallbackReasonSchema,
1104
+ since: z.number().int().nonnegative(),
1105
+ retryMainAt: z.number().int().nonnegative(),
1106
+ })
1107
+ .strict()
1108
+ .optional(),
871
1109
  pendingInjectCount: z.number().int().nonnegative(),
872
1110
  occupancy: ChannelRuntimeOccupancySchema.optional(),
873
1111
  cache: ChannelRuntimeCacheSchema.optional(),
@@ -983,6 +1221,20 @@ export const ChannelContextPolicyViewSchema = z
983
1221
 
984
1222
  export type ChannelContextPolicyView = z.output<typeof ChannelContextPolicyViewSchema>
985
1223
 
1224
+ /** One desktop installer of a release; `urls` lists the sources to try in order. */
1225
+ export const ProductDownloadSchema = z
1226
+ .object({
1227
+ platform: z.enum(['mac', 'win', 'linux']),
1228
+ arch: z.enum(['arm64', 'x64']),
1229
+ name: z.string().min(1),
1230
+ size: z.number().int().positive(),
1231
+ sha256: z.string().regex(/^[a-f0-9]{64}$/u),
1232
+ urls: z.array(z.string().url()).min(1),
1233
+ })
1234
+ .strict()
1235
+
1236
+ export type ProductDownload = z.output<typeof ProductDownloadSchema>
1237
+
986
1238
  /** Whether a newer release exists on the build's own channel; development builds are never compared. */
987
1239
  export const ProductUpdateStatusSchema = z
988
1240
  .object({
@@ -997,6 +1249,8 @@ export const ProductUpdateStatusSchema = z
997
1249
  commit: z.string().optional(),
998
1250
  url: z.string().url(),
999
1251
  publishedAt: z.number().int().nonnegative().optional(),
1252
+ /** Desktop installers of this release; the desktop app downloads its own and checks size and digest. */
1253
+ downloads: z.array(ProductDownloadSchema).optional(),
1000
1254
  })
1001
1255
  .strict()
1002
1256
  .optional(),
@@ -1038,6 +1292,8 @@ export const SubConversationSchema = z
1038
1292
  /** What it handed back to the channel's conversation when it ended. */
1039
1293
  summary: z.string().max(4000).optional(),
1040
1294
  authoringTaskId: AuthoringTaskIdSchema.optional(),
1295
+ /** The model a model-driven sub-conversation talks in; absent for scripts and ones opened before it was kept. */
1296
+ model: AgentModelSchema.optional(),
1041
1297
  createdAt: z.number().int().nonnegative(),
1042
1298
  closedAt: z.number().int().nonnegative().optional(),
1043
1299
  })
@@ -1055,30 +1311,10 @@ export const SubConversationSettingsSchema = z.object({ retention: SubConversati
1055
1311
  export const RetrievalModeSchema = z.enum(['keyword', 'builtin'])
1056
1312
  export type RetrievalMode = z.output<typeof RetrievalModeSchema>
1057
1313
 
1058
- /** Search used by extension indexes (表情包与扩展素材能力 §6.4). */
1314
+ /** Search used by extension indexes (表情包与扩展素材能力 §6.4); the model itself is the `retrieval` add-on. */
1059
1315
  export const RetrievalStatusSchema = z
1060
1316
  .object({
1061
1317
  mode: RetrievalModeSchema,
1062
- builtin: z
1063
- .object({
1064
- model: z.string(),
1065
- /** False where no inference runtime is published for this system and processor. */
1066
- supported: z.boolean(),
1067
- downloadBytes: z.number().int().nonnegative(),
1068
- install: z.discriminatedUnion('state', [
1069
- z.object({ state: z.literal('not-installed') }).strict(),
1070
- z
1071
- .object({
1072
- state: z.literal('downloading'),
1073
- receivedBytes: z.number().int().nonnegative(),
1074
- totalBytes: z.number().int().nonnegative(),
1075
- })
1076
- .strict(),
1077
- z.object({ state: z.literal('installed') }).strict(),
1078
- z.object({ state: z.literal('failed'), error: z.string() }).strict(),
1079
- ]),
1080
- })
1081
- .strict(),
1082
1318
  vectors: z
1083
1319
  .object({
1084
1320
  documents: z.number().int().nonnegative(),
@@ -1270,11 +1506,21 @@ export const AttentionItemSchema = z
1270
1506
  'agent-vision-unavailable',
1271
1507
  'logs-unavailable',
1272
1508
  'community-feedback',
1509
+ 'extension-update',
1273
1510
  ]),
1274
1511
  severity: z.enum(['critical', 'warning', 'info']),
1275
1512
  subject: z
1276
1513
  .object({
1277
- kind: z.enum(['outbound', 'channel', 'authoring-task', 'connection', 'agent', 'logs', 'community-feedback']),
1514
+ kind: z.enum([
1515
+ 'outbound',
1516
+ 'channel',
1517
+ 'authoring-task',
1518
+ 'connection',
1519
+ 'agent',
1520
+ 'logs',
1521
+ 'community-feedback',
1522
+ 'extension-update',
1523
+ ]),
1278
1524
  id: NonEmptyStringSchema,
1279
1525
  })
1280
1526
  .strict(),
@@ -1299,10 +1545,19 @@ export const AttentionItemSchema = z
1299
1545
  'open-agent',
1300
1546
  'open-logs',
1301
1547
  'open-community-feedback',
1548
+ 'open-extension-updates',
1302
1549
  ]),
1303
1550
  label: z.string().min(1).max(40),
1304
1551
  })
1305
1552
  .strict(),
1553
+ /**
1554
+ * The shared cause several items come from: a connection (its outage and the deliveries it failed) or an agent
1555
+ * (its failed turns and unusable models). Dismissing by cause keeps it quiet until the cause recovers.
1556
+ */
1557
+ cause: z
1558
+ .object({ key: z.string().min(1).max(200), label: z.string().min(1).max(120) })
1559
+ .strict()
1560
+ .optional(),
1306
1561
  occurredAt: z.number().int().safe().nonnegative(),
1307
1562
  })
1308
1563
  .strict()
@@ -1423,82 +1678,156 @@ export const ScheduledTaskSchema = z
1423
1678
 
1424
1679
  export type ScheduledTask = z.output<typeof ScheduledTaskSchema>
1425
1680
 
1426
- /** The Node that runs `run_code` programs, and the download the desktop app offers when there is none. */
1427
- export const CodeRunAvailabilitySchema = z
1681
+ /** Programs and models NekroNXT installs on request (附加功能); `<data root>/runtimes`, left out of backups. */
1682
+ export const AddonIdSchema = z.enum(['media', 'speech', 'retrieval', 'node', 'github-cli', 'browser'])
1683
+ export type AddonId = z.output<typeof AddonIdSchema>
1684
+
1685
+ /** `extension`: used by extensions; `agent`: used by agents' commands; `host`: used by NekroNXT itself. */
1686
+ export const AddonGroupSchema = z.enum(['extension', 'agent', 'host'])
1687
+ export type AddonGroup = z.output<typeof AddonGroupSchema>
1688
+
1689
+ /**
1690
+ * `installed`: downloaded and ready; `available`: usable without installing (bundled, found on this device, cloud);
1691
+ * `needs-config`: usable once configured (cloud).
1692
+ */
1693
+ export const AddonStateSchema = z.enum([
1694
+ 'not-installed',
1695
+ 'installing',
1696
+ 'installed',
1697
+ 'failed',
1698
+ 'unsupported',
1699
+ 'available',
1700
+ 'needs-config',
1701
+ ])
1702
+ export type AddonState = z.output<typeof AddonStateSchema>
1703
+
1704
+ const AddonProgressSchema = z
1428
1705
  .object({
1429
- available: z.boolean(),
1430
- source: z.enum(['host', 'system', 'downloaded']).optional(),
1706
+ receivedBytes: z.number().int().nonnegative(),
1707
+ totalBytes: z.number().int().nonnegative().optional(),
1708
+ })
1709
+ .strict()
1710
+
1711
+ /** One way an add-on can be provided, such as a program found on this device or one NekroNXT downloads. */
1712
+ export const AddonProviderStatusSchema = z
1713
+ .object({
1714
+ id: NonEmptyStringSchema,
1715
+ title: NonEmptyStringSchema,
1716
+ summary: z.string(),
1717
+ state: AddonStateSchema,
1718
+ /** `bundled`: installed with NekroNXT; `system`: found on this device; `downloaded`: fetched by NekroNXT. */
1719
+ source: z.enum(['bundled', 'system', 'downloaded', 'cloud']).optional(),
1720
+ /** The installed version, or the one an install fetches. */
1431
1721
  version: z.string().optional(),
1432
- download: z
1722
+ resources: z
1433
1723
  .object({
1434
- version: z.string(),
1435
- state: z.enum(['idle', 'downloading', 'failed']),
1436
- receivedBytes: z.number().int().nonnegative().optional(),
1437
- totalBytes: z.number().int().nonnegative().optional(),
1438
- error: z.string().optional(),
1724
+ /** Approximate for this system and processor. */
1725
+ downloadBytes: z.number().int().nonnegative().optional(),
1726
+ /** Measured from its own folder after install. */
1727
+ installedBytes: z.number().int().nonnegative().optional(),
1728
+ memoryBytes: z.number().int().nonnegative().optional(),
1729
+ /** Short differences worth comparing, such as languages or speed. */
1730
+ notes: z.array(z.string()).default([]),
1439
1731
  })
1440
- .strict()
1441
- .optional(),
1732
+ .strict(),
1733
+ /** One entry per component that carries its own license. */
1734
+ licenses: z
1735
+ .array(z.object({ component: z.string().optional(), name: NonEmptyStringSchema, url: z.url() }).strict())
1736
+ .default([]),
1737
+ /** Where downloads come from, for display. */
1738
+ sources: z.array(z.string()).default([]),
1739
+ progress: AddonProgressSchema.optional(),
1740
+ error: z.string().optional(),
1741
+ /** Other add-ons it needs to work. */
1742
+ dependsOn: z.array(AddonIdSchema).default([]),
1743
+ installable: z.boolean(),
1744
+ removable: z.boolean(),
1442
1745
  })
1443
1746
  .strict()
1747
+ export type AddonProviderStatus = z.output<typeof AddonProviderStatusSchema>
1444
1748
 
1445
- export type CodeRunAvailability = z.output<typeof CodeRunAvailabilitySchema>
1749
+ /** One add-on (a capability) as 设置 → 附加功能 shows it; the registry on the server is its only source. */
1750
+ export const AddonStatusSchema = z
1751
+ .object({
1752
+ id: AddonIdSchema,
1753
+ group: AddonGroupSchema,
1754
+ title: NonEmptyStringSchema,
1755
+ summary: z.string(),
1756
+ /** Who relies on it, as labels people recognise. */
1757
+ usedBy: z.array(z.string()),
1758
+ /** The selected provider's state, or, while none can be used, the one closest to usable. */
1759
+ state: AddonStateSchema,
1760
+ progress: AddonProgressSchema.optional(),
1761
+ /** The provider in use; absent while none can be used. */
1762
+ selected: NonEmptyStringSchema.optional(),
1763
+ /** In declared order; providers that do not exist on this device are left out. */
1764
+ providers: z.array(AddonProviderStatusSchema),
1765
+ })
1766
+ .strict()
1767
+ export type AddonStatus = z.output<typeof AddonStatusSchema>
1446
1768
 
1447
- /** The GitHub CLI commands call: found on this machine, or downloaded by NekroNXT where it can. */
1448
- export const GithubCliAvailabilitySchema = CodeRunAvailabilitySchema
1449
- export type GithubCliAvailability = z.output<typeof GithubCliAvailabilitySchema>
1769
+ /** Languages speech recognition can be asked for; `auto` lets the provider tell. */
1770
+ export const SpeechLanguageSchema = z.enum(['auto', 'zh', 'en', 'ja', 'ko', 'yue'])
1771
+ export type SpeechLanguage = z.output<typeof SpeechLanguageSchema>
1450
1772
 
1451
- /** The optional headless Chromium that loads and checks creation candidates when no page is open. */
1452
- export const VerificationBrowserAvailabilitySchema = CodeRunAvailabilitySchema
1453
- export type VerificationBrowserAvailability = z.output<typeof VerificationBrowserAvailabilitySchema>
1773
+ /** The cloud provider of the `speech` add-on: a provider from 设置 → 模型 and the transcription model it serves. */
1774
+ export const SpeechCloudSettingsSchema = z
1775
+ .object({
1776
+ provider: NonEmptyStringSchema,
1777
+ model: z.string().trim().min(1).max(200),
1778
+ /** Used when a caller does not name a language. */
1779
+ language: SpeechLanguageSchema.optional(),
1780
+ })
1781
+ .strict()
1782
+ export type SpeechCloudSettings = z.output<typeof SpeechCloudSettingsSchema>
1454
1783
 
1455
- /**
1456
- * One account in the host's library. The library only keeps accounts; an account takes effect only for the agents
1457
- * whose capabilities grant it, which `usedBy` lists.
1458
- */
1459
- export const CommandAccountSchema = z
1784
+ export const SpeechSettingsSchema = z
1460
1785
  .object({
1461
- id: NonEmptyStringSchema.max(64),
1462
- service: CommandAccountServiceSchema,
1463
- /** The account name on the service. */
1464
- login: NonEmptyStringSchema,
1465
- source: z.enum(['token', 'gh-login']),
1466
- /** Token scopes where the service reports them (classic tokens); absent for fine-grained tokens. */
1467
- scopes: z.array(z.string()).optional(),
1468
- createdAt: z.number().int().nonnegative(),
1469
- usedBy: z.array(AgentIdSchema),
1786
+ cloud: SpeechCloudSettingsSchema.nullable(),
1787
+ /** Configured providers with an OpenAI-compatible address, the ones cloud transcription can use. */
1788
+ providers: z.array(z.object({ provider: NonEmptyStringSchema, displayName: z.string() }).strict()),
1470
1789
  })
1471
1790
  .strict()
1791
+ export type SpeechSettings = z.output<typeof SpeechSettingsSchema>
1472
1792
 
1473
- export type CommandAccount = z.output<typeof CommandAccountSchema>
1793
+ /** Whether a capability that needs an add-on can run now; details live in `addons`. */
1794
+ export const AddonAvailabilitySchema = z.object({ available: z.boolean() }).strict()
1795
+ export type AddonAvailability = z.output<typeof AddonAvailabilitySchema>
1474
1796
 
1475
- /** A login the local `gh` holds, offered as a quick way to add an account. */
1476
- export const LocalGithubLoginSchema = z.object({ login: NonEmptyStringSchema, active: z.boolean() }).strict()
1797
+ /** A platform's own program that linked accounts work through, such as the GitHub CLI. */
1798
+ export const LinkedAccountPlatformToolSchema = z
1799
+ .object({
1800
+ /** The add-on it is installed as (`AddonStatus.id`). */
1801
+ id: z.enum(['github-cli']),
1802
+ name: z.string(),
1803
+ })
1804
+ .strict()
1477
1805
 
1478
- export const CommandAccountsViewSchema = z
1806
+ /** 设置 → 关联账号: every platform the host supports, with its accounts and tools. */
1807
+ export const LinkedAccountsViewSchema = z
1479
1808
  .object({
1480
- accounts: z.array(CommandAccountSchema),
1809
+ platforms: z.array(
1810
+ z
1811
+ .object({
1812
+ id: LinkedAccountPlatformIdSchema,
1813
+ name: z.string(),
1814
+ addMethods: z.array(LinkedAccountMethodSchema),
1815
+ tool: LinkedAccountPlatformToolSchema.optional(),
1816
+ accounts: z.array(LinkedAccountViewSchema),
1817
+ })
1818
+ .strict(),
1819
+ ),
1481
1820
  /** Logins found in the local GitHub CLI; empty when it is missing or not logged in. */
1482
- localLogins: z.array(LocalGithubLoginSchema),
1821
+ localGithubLogins: z.array(LocalGithubLoginSchema),
1483
1822
  })
1484
1823
  .strict()
1824
+ export type LinkedAccountsView = z.output<typeof LinkedAccountsViewSchema>
1485
1825
 
1486
- export type CommandAccountsView = z.output<typeof CommandAccountsViewSchema>
1487
-
1488
- export const AddCommandAccountRequestSchema = z.discriminatedUnion('source', [
1489
- z
1490
- .object({ source: z.literal('token'), service: CommandAccountServiceSchema, token: NonEmptyStringSchema.max(512) })
1491
- .strict(),
1492
- z
1493
- .object({
1494
- source: z.literal('gh-login'),
1495
- service: CommandAccountServiceSchema,
1496
- login: NonEmptyStringSchema.max(128),
1497
- })
1498
- .strict(),
1826
+ export const RemoveLinkedAccountResultSchema = z.discriminatedUnion('removed', [
1827
+ z.object({ removed: z.literal(true), view: LinkedAccountsViewSchema }).strict(),
1828
+ z.object({ removed: z.literal(false), grantedAgentIds: z.array(AgentIdSchema) }).strict(),
1499
1829
  ])
1500
-
1501
- export type AddCommandAccountRequest = z.output<typeof AddCommandAccountRequestSchema>
1830
+ export type RemoveLinkedAccountResult = z.output<typeof RemoveLinkedAccountResultSchema>
1502
1831
 
1503
1832
  /** An extension action an agent asked an administrator to confirm, or one it took on its own. */
1504
1833
  export const ExtensionRequestSchema = z
@@ -1509,7 +1838,7 @@ export const ExtensionRequestSchema = z
1509
1838
  channelId: ChannelIdSchema.nullable(),
1510
1839
  extensionId: ExtensionIdSchema.nullable(),
1511
1840
  summary: z.string(),
1512
- status: z.enum(['pending', 'approved', 'rejected', 'expired', 'completed', 'failed']),
1841
+ status: z.enum(['pending', 'approved', 'rejected', 'expired', 'completed', 'failed', 'superseded', 'withdrawn']),
1513
1842
  autonomous: z.boolean(),
1514
1843
  resultMessage: z.string().nullable(),
1515
1844
  decidedBy: z.string().nullable(),
@@ -1547,18 +1876,28 @@ export const HostSnapshotSchema = z
1547
1876
  name: z.string(),
1548
1877
  description: z.string().optional(),
1549
1878
  inputModalities: z.array(z.string()).optional(),
1879
+ /** Selectable thinking levels, least to most; absent when the model offers no choice. */
1880
+ reasoning: z
1881
+ .object({
1882
+ efforts: z.array(z.object({ id: NonEmptyStringSchema, name: z.string() }).strict()).min(1),
1883
+ defaultEffort: NonEmptyStringSchema.optional(),
1884
+ })
1885
+ .strict()
1886
+ .optional(),
1550
1887
  })
1551
1888
  .strict(),
1552
1889
  ),
1553
1890
  capabilityAvailability: z
1554
1891
  .object({
1555
1892
  subagents: z.object({ available: z.boolean() }).strict(),
1556
- /** Optional in older hosts, which never offer `run_code`. */
1557
- codeRun: CodeRunAvailabilitySchema.optional(),
1558
- /** Optional in older hosts, which never offer command accounts. */
1559
- githubCli: GithubCliAvailabilitySchema.optional(),
1560
- /** Optional in older hosts, which check candidates only in an open page. */
1561
- verificationBrowser: VerificationBrowserAvailabilitySchema.optional(),
1893
+ /** The `node` add-on that `run_code` needs. */
1894
+ codeRun: AddonAvailabilitySchema.optional(),
1895
+ /** The `github-cli` add-on that commands use with a GitHub account. */
1896
+ githubCli: AddonAvailabilitySchema.optional(),
1897
+ /** The `browser` add-on that checks candidates when no page is open. */
1898
+ verificationBrowser: AddonAvailabilitySchema.optional(),
1899
+ /** The `speech` add-on that voice message transcription needs. */
1900
+ speechRecognition: AddonAvailabilitySchema.optional(),
1562
1901
  /** What this host can confine for commands beyond file writes; optional in older hosts. */
1563
1902
  commandConfinement: z.object({ readScope: z.boolean(), networkControl: z.boolean() }).strict().optional(),
1564
1903
  webSearch: z
@@ -1577,8 +1916,6 @@ export const HostSnapshotSchema = z
1577
1916
  .strict(),
1578
1917
  connectionAdapters: z.array(AdapterConnectionDescriptorSchema),
1579
1918
  notificationSettings: NotificationSettingsViewSchema,
1580
- /** Optional in older hosts, which have no account library. */
1581
- commandAccounts: z.array(CommandAccountSchema).optional(),
1582
1919
  agents: z.array(
1583
1920
  z
1584
1921
  .object({
@@ -1590,6 +1927,19 @@ export const HostSnapshotSchema = z
1590
1927
  capabilities: AgentCapabilitiesSchema,
1591
1928
  imagePolicy: ImageUnderstandingPolicyApiSchema,
1592
1929
  dynamicClientApprovalPolicy: DynamicClientApprovalPolicySchema,
1930
+ /** Optional for older hosts, whose agents always follow both defaults. */
1931
+ compaction: AgentCompactionPolicySchema.optional(),
1932
+ /** Optional for older hosts, whose agents offer no candidates. */
1933
+ subConversationModels: AgentSubConversationModelsSchema.optional(),
1934
+ /** Optional for older hosts, whose agents have no fallback models. */
1935
+ fallbackModels: AgentFallbackModelsSchema.optional(),
1936
+ /** Optional for older hosts, whose agents have no aliases. */
1937
+ aliases: AgentAliasesSchema.optional(),
1938
+ /**
1939
+ * Enabled extensions that process the agent's messages before they are sent, in the order they run. Absent
1940
+ * when none does.
1941
+ */
1942
+ outboundOrder: z.array(ExtensionIdSchema).optional(),
1593
1943
  imageDiagnostics: z
1594
1944
  .object({
1595
1945
  route: z
@@ -1663,6 +2013,8 @@ export const HostSnapshotSchema = z
1663
2013
  /** Absent means `observe`. */
1664
2014
  localAgentMessages: z.enum(['observe', 'trigger']).optional(),
1665
2015
  activityTriggerOverrides: ActivityTriggerOverridesSchema.default({}),
2016
+ /** Absent on older hosts; missing keys use the defaults. */
2017
+ responseTiming: ResponseTimingSettingsSchema.optional(),
1666
2018
  boundAt: z.number().int().safe().nonnegative(),
1667
2019
  })
1668
2020
  .strict(),
@@ -1675,6 +2027,7 @@ export const HostSnapshotSchema = z
1675
2027
  channelIdsByAgent: {},
1676
2028
  unboundChannelIds: [],
1677
2029
  }),
2030
+ channelListView: ChannelListViewSchema.default({ pinnedChannelIds: [], collapsedGroupIds: [] }),
1678
2031
  messages: z.array(HostSnapshotMessageSchema),
1679
2032
  connections: z.array(
1680
2033
  z
@@ -1684,6 +2037,7 @@ export const HostSnapshotSchema = z
1684
2037
  alias: ConnectionAliasOutputSchema.optional(),
1685
2038
  activityTriggerDefaults: z.array(AdapterActivityKeySchema).default([]),
1686
2039
  configuredSecrets: z.array(z.string()).optional(),
2040
+ boundCredentials: BoundCredentialsSchema.optional(),
1687
2041
  status: z
1688
2042
  .object({
1689
2043
  state: z.enum(['stopped', 'connecting', 'connected', 'reconnecting', 'failed']),
@@ -1856,6 +2210,7 @@ export const HostSnapshotSchema = z
1856
2210
  /** Secret fields are removed; `configuredSecrets` lists those the Host stores. */
1857
2211
  config: JsonValueSchema,
1858
2212
  configuredSecrets: z.array(z.string()).optional(),
2213
+ boundCredentials: BoundCredentialsSchema.optional(),
1859
2214
  activatedAt: z.number().int().safe().nonnegative(),
1860
2215
  runtime: z
1861
2216
  .object({
@@ -1891,6 +2246,7 @@ export const HostSnapshotSchema = z
1891
2246
  /** Secret fields are removed; `configuredSecrets` lists those the Host stores. */
1892
2247
  config: JsonValueSchema.default({}),
1893
2248
  configuredSecrets: z.array(z.string()).optional(),
2249
+ boundCredentials: BoundCredentialsSchema.optional(),
1894
2250
  runtime: z
1895
2251
  .object({
1896
2252
  status: z.enum(['active', 'restore-failed', 'dispose-failed']),
@@ -1973,6 +2329,7 @@ export const HostSnapshotSchema = z
1973
2329
  authoringTasks: z.array(AuthoringTaskSummarySchema).default([]),
1974
2330
  /** Open sub-conversations, and the most recently ended ones of each channel. */
1975
2331
  subConversations: z.array(SubConversationSchema).default([]),
2332
+ addons: z.array(AddonStatusSchema).default([]),
1976
2333
  scheduledTasks: z.array(ScheduledTaskSchema).default([]),
1977
2334
  /** Extension requests waiting for an administrator. */
1978
2335
  extensionRequests: z.array(ExtensionRequestSchema).default([]),
@@ -2099,6 +2456,10 @@ export const DshPluginCatalogEntrySchema = z
2099
2456
  loadError: z.object({ code: NonEmptyStringSchema, message: NonEmptyStringSchema }).strict().optional(),
2100
2457
  packageId: DshPluginPackageIdSchema.optional(),
2101
2458
  installSource: z.enum(['registry', 'tarball', 'imported']).optional(),
2459
+ /** Published by the DSH maintainers on npm for the DSH release this build runs. */
2460
+ official: z.boolean().optional(),
2461
+ /** Third-party installed plugins: whether an administrator allowed them to submit images to the model. */
2462
+ imageSubmission: z.boolean().optional(),
2102
2463
  installedAt: z.number().int().safe().nonnegative().optional(),
2103
2464
  clientUiDetected: z.boolean().optional(),
2104
2465
  hostUi: DshNxtHostUiSchema.optional(),
@@ -2113,6 +2474,8 @@ export const DshPluginCatalogEntrySchema = z
2113
2474
  suggestedScope: z.enum(['host', 'agent']),
2114
2475
  selectedScope: z.enum(['host', 'agent']).optional(),
2115
2476
  config: JsonValueSchema,
2477
+ /** Host services this official entry declared and received; known once it has loaded. */
2478
+ grantedServices: z.array(NonEmptyStringSchema).optional(),
2116
2479
  activations: z.array(
2117
2480
  z
2118
2481
  .object({
@@ -2122,9 +2485,13 @@ export const DshPluginCatalogEntrySchema = z
2122
2485
  activatedAt: z.number().int().safe().nonnegative(),
2123
2486
  diagnostic: z
2124
2487
  .object({
2125
- status: z.enum(['active', 'load-failed', 'restore-failed', 'dispose-failed']),
2126
- phase: z.enum(['import', 'apply', 'update', 'dispose', 'restore']),
2488
+ status: z.enum(['active', 'pending', 'load-failed', 'restore-failed', 'dispose-failed']),
2489
+ phase: z.enum(['import', 'dependency', 'apply', 'update', 'dispose', 'restore']),
2127
2490
  message: z.string().optional(),
2491
+ /** Services a pending entry still waits for. */
2492
+ waitingFor: z.array(NonEmptyStringSchema).optional(),
2493
+ /** The waited-for services this entry can never receive, since only official plugins get them. */
2494
+ restrictedServices: z.array(NonEmptyStringSchema).optional(),
2128
2495
  observedAt: z.number().int().safe().nonnegative(),
2129
2496
  })
2130
2497
  .optional(),
@@ -2239,6 +2606,9 @@ export type HostSseEvent = z.output<typeof HostSseEventSchema>
2239
2606
  /** Request content a model accepts; `image` enables native vision for agents using the model. */
2240
2607
  export const LlmModelInputModalitiesSchema = z.array(z.enum(['text', 'image'])).min(1)
2241
2608
 
2609
+ /** Thinking levels a model offers, least to most; the ids are the adapter's own. */
2610
+ export const LlmModelReasoningEffortsSchema = z.array(NonEmptyStringSchema).min(1)
2611
+
2242
2612
  export const LlmProviderModelSchema = z
2243
2613
  .object({
2244
2614
  id: NonEmptyStringSchema,
@@ -2246,6 +2616,7 @@ export const LlmProviderModelSchema = z
2246
2616
  contextWindow: z.number().int().positive().optional(),
2247
2617
  maxTokens: z.number().int().positive().optional(),
2248
2618
  inputModalities: LlmModelInputModalitiesSchema.optional(),
2619
+ reasoningEfforts: LlmModelReasoningEffortsSchema.optional(),
2249
2620
  })
2250
2621
  .strict()
2251
2622
 
@@ -2256,6 +2627,7 @@ export const LlmDiscoveredModelSchema = z
2256
2627
  contextWindow: z.number().int().positive().optional(),
2257
2628
  maxTokens: z.number().int().positive().optional(),
2258
2629
  inputModalities: LlmModelInputModalitiesSchema.optional(),
2630
+ reasoningEfforts: LlmModelReasoningEffortsSchema.optional(),
2259
2631
  })
2260
2632
  .strict()
2261
2633
 
@@ -2299,6 +2671,60 @@ export const LlmProviderViewSchema = z
2299
2671
  discoverable: z.boolean(),
2300
2672
  /** `null` when the current configuration was never tested; absent from hosts that do not record tests. */
2301
2673
  lastTest: LlmProviderTestResultSchema.nullable().optional(),
2674
+ /** Present when the provider can sign in with a subscription account instead of an API key. */
2675
+ subscription: z
2676
+ .object({
2677
+ label: NonEmptyStringSchema,
2678
+ /** The route uses the signed-in account rather than an API key. */
2679
+ active: z.boolean(),
2680
+ accountLabel: z.string().nullable(),
2681
+ })
2682
+ .strict()
2683
+ .optional(),
2684
+ })
2685
+ .strict()
2686
+
2687
+ export const SubscriptionStatusSchema = z
2688
+ .object({
2689
+ provider: NonEmptyStringSchema,
2690
+ label: NonEmptyStringSchema,
2691
+ usageUrl: z.url(),
2692
+ available: z.boolean(),
2693
+ mode: z.enum(['unconfigured', 'api-key', 'subscription']),
2694
+ activeAccountId: z.string().nullable(),
2695
+ accounts: z.array(
2696
+ z
2697
+ .object({
2698
+ id: NonEmptyStringSchema,
2699
+ label: NonEmptyStringSchema,
2700
+ email: z.string().nullable(),
2701
+ createdAt: z.number().int().nonnegative(),
2702
+ })
2703
+ .strict(),
2704
+ ),
2705
+ login: z
2706
+ .object({
2707
+ id: NonEmptyStringSchema,
2708
+ status: z.enum(['running', 'authorized', 'cancelled', 'failed']),
2709
+ notices: z.array(
2710
+ z.object({ message: z.string(), url: z.string().optional(), code: z.string().optional() }).strict(),
2711
+ ),
2712
+ prompt: z
2713
+ .object({
2714
+ id: NonEmptyStringSchema,
2715
+ kind: z.enum(['text', 'secret', 'select']),
2716
+ message: z.string(),
2717
+ placeholder: z.string().optional(),
2718
+ options: z
2719
+ .array(z.object({ id: z.string(), label: z.string(), description: z.string().optional() }).strict())
2720
+ .optional(),
2721
+ })
2722
+ .strict()
2723
+ .nullable(),
2724
+ error: z.string().nullable(),
2725
+ })
2726
+ .strict()
2727
+ .nullable(),
2302
2728
  })
2303
2729
  .strict()
2304
2730
 
@@ -2403,6 +2829,8 @@ const defineContract = <
2403
2829
  })
2404
2830
 
2405
2831
  const agentParam = z.object({ agentId: AgentIdSchema }).strict()
2832
+ const credentialParam = z.object({ credentialId: CredentialIdSchema }).strict()
2833
+ const linkedAccountParam = z.object({ accountId: LinkedAccountIdSchema }).strict()
2406
2834
  const agentRevisionParam = z.object({ agentId: AgentIdSchema, revisionId: AgentRevisionIdSchema }).strict()
2407
2835
 
2408
2836
  export const AgentRevisionChangedFieldSchema = z.enum([
@@ -2412,6 +2840,10 @@ export const AgentRevisionChangedFieldSchema = z.enum([
2412
2840
  'capabilities',
2413
2841
  'imagePolicy',
2414
2842
  'approvalPolicy',
2843
+ 'compaction',
2844
+ 'subConversationModels',
2845
+ 'fallbackModels',
2846
+ 'aliases',
2415
2847
  ])
2416
2848
 
2417
2849
  export const AgentRevisionHistorySchema = z
@@ -2441,6 +2873,22 @@ const agentExtensionParam = z.object({ agentId: AgentIdSchema, extensionId: Exte
2441
2873
 
2442
2874
  const PermissionApprovalSchema = z.object({ permissionDigest: z.string().regex(/^[a-f0-9]{64}$/u) }).strict()
2443
2875
 
2876
+ /**
2877
+ * What keeps a version from being cleaned up: in use, enabled for an agent, released to a community, kept by hand,
2878
+ * the start of a branch, the base of an open revision, needed by 回到上一个版本, or named by a pending request.
2879
+ */
2880
+ export const ExtensionRevisionProtectionSchema = z.enum([
2881
+ 'installed',
2882
+ 'active',
2883
+ 'released',
2884
+ 'kept',
2885
+ 'branch',
2886
+ 'revising',
2887
+ 'undo',
2888
+ 'request',
2889
+ ])
2890
+ export type ExtensionRevisionProtection = z.output<typeof ExtensionRevisionProtectionSchema>
2891
+
2444
2892
  /** One version of one line in the lineage view of an extension family. */
2445
2893
  export const ExtensionLineageNodeSchema = z
2446
2894
  .object({
@@ -2452,6 +2900,10 @@ export const ExtensionLineageNodeSchema = z
2452
2900
  createdAt: z.number().int().nonnegative(),
2453
2901
  current: z.boolean(),
2454
2902
  releaseNumbers: z.array(z.number().int().positive()),
2903
+ /** Marked by an administrator to keep. */
2904
+ kept: z.boolean(),
2905
+ /** Why the version cannot be cleaned up; absent when it can. */
2906
+ protectedBy: ExtensionRevisionProtectionSchema.optional(),
2455
2907
  })
2456
2908
  .strict()
2457
2909
 
@@ -2577,6 +3029,41 @@ export const AdministratorSchema = z
2577
3029
  .strict()
2578
3030
 
2579
3031
  const extensionParam = z.object({ extensionId: ExtensionIdSchema }).strict()
3032
+
3033
+ export const ExtensionConfigVersionSourceSchema = z.enum(['console', 'agent', 'chat-admin', 'revert', 'auto-revert'])
3034
+ export type ExtensionConfigVersionSource = z.output<typeof ExtensionConfigVersionSourceSchema>
3035
+
3036
+ /**
3037
+ * One saved state of an extension's host configuration or of one agent's configuration. Versions are numbered per
3038
+ * extension, layer and agent; reverting writes an old one again as a new version. Secret fields only show whether
3039
+ * they were set and which named credential they were bound to.
3040
+ */
3041
+ export const ExtensionConfigVersionSchema = z
3042
+ .object({
3043
+ id: z.number().int().positive(),
3044
+ extensionId: ExtensionIdSchema,
3045
+ layer: z.enum(['host', 'agent']),
3046
+ agentId: AgentIdSchema.nullable(),
3047
+ version: z.number().int().positive(),
3048
+ config: JsonValueSchema,
3049
+ configuredSecrets: z.array(z.string()).optional(),
3050
+ boundCredentials: z.record(z.string(), CredentialIdSchema).optional(),
3051
+ source: ExtensionConfigVersionSourceSchema,
3052
+ actorAgentId: AgentIdSchema.nullable(),
3053
+ requesterMemberId: z.string().nullable(),
3054
+ reason: z.string().nullable(),
3055
+ createdAt: z.number().int().nonnegative(),
3056
+ })
3057
+ .strict()
3058
+ export type ExtensionConfigVersion = z.output<typeof ExtensionConfigVersionSchema>
3059
+
3060
+ const extensionConfigVersionParam = z
3061
+ .object({
3062
+ extensionId: ExtensionIdSchema,
3063
+ /** Decimal text of the version id; the path builder only takes strings. */
3064
+ versionId: z.string().regex(/^[1-9]\d{0,15}$/u),
3065
+ })
3066
+ .strict()
2580
3067
  const dshPluginPackageParam = z.object({ packageId: DshPluginPackageIdSchema }).strict()
2581
3068
  const dshPluginEntryParam = z.object({ entryId: DshPluginEntryIdSchema }).strict()
2582
3069
  const extensionRevisionParam = z
@@ -2830,11 +3317,20 @@ export const HostApiContracts = {
2830
3317
  .object({
2831
3318
  task: AuthoringTaskSummarySchema,
2832
3319
  attempts: z.array(AuthoringAttemptSummarySchema),
2833
- /** Secret fields of the latest candidate and which have a test value for this task's dynamic runs. */
2834
- testSecrets: z
3320
+ /**
3321
+ * Test configuration of this task's dynamic runs: fields of the latest candidate the user can type, which
3322
+ * credentials are set, the user's values and the values the agent provided.
3323
+ */
3324
+ testConfig: z
2835
3325
  .object({
2836
- fields: z.array(z.object({ key: z.string(), title: z.string() }).strict()),
3326
+ fields: z.array(
3327
+ z.object({ key: z.string(), title: z.string(), kind: z.enum(['secret', 'string', 'number']) }).strict(),
3328
+ ),
2837
3329
  configured: z.array(z.string()),
3330
+ values: z.record(z.string(), z.union([z.string(), z.number()])),
3331
+ agentValues: z.record(z.string(), JsonValueSchema),
3332
+ /** Secret fields bound to a named credential, by the user or else by the agent. */
3333
+ boundCredentials: BoundCredentialsSchema.optional(),
2838
3334
  })
2839
3335
  .strict()
2840
3336
  .optional(),
@@ -2884,12 +3380,26 @@ export const HostApiContracts = {
2884
3380
  response: AuthoringTaskSummarySchema,
2885
3381
  error: HostApiErrorSchema,
2886
3382
  }),
2887
- setAuthoringTestSecrets: defineContract({
3383
+ setAuthoringTestConfig: defineContract({
2888
3384
  method: 'PUT',
2889
3385
  path: '/api/authoring/tasks/:taskId/test-secrets',
2890
3386
  params: authoringTaskParam,
2891
- request: z.object({ secrets: z.record(z.string(), z.string().max(8192)) }).strict(),
2892
- response: z.object({ configured: z.array(z.string()) }).strict(),
3387
+ request: z
3388
+ .object({
3389
+ secrets: z.record(z.string(), z.string().max(8192)),
3390
+ /** Text and number values; an empty string goes back to the agent's value or the default. */
3391
+ values: z.record(z.string(), z.union([z.string().max(8192), z.number()])).optional(),
3392
+ /** Secret fields to bind to named credentials instead of a typed value; `null` leaves the field unset. */
3393
+ credentialBindings: CredentialBindingsRequestSchema.optional(),
3394
+ })
3395
+ .strict(),
3396
+ response: z
3397
+ .object({
3398
+ configured: z.array(z.string()),
3399
+ values: z.record(z.string(), z.union([z.string(), z.number()])),
3400
+ boundCredentials: BoundCredentialsSchema.optional(),
3401
+ })
3402
+ .strict(),
2893
3403
  error: HostApiErrorSchema,
2894
3404
  }),
2895
3405
  deleteAuthoringTask: defineContract({
@@ -3087,6 +3597,8 @@ export const HostApiContracts = {
3087
3597
  /** Messages from another local agent's account: `observe` keeps them as context unless they @ this agent. */
3088
3598
  localAgentMessages: z.enum(['observe', 'trigger']).optional(),
3089
3599
  activityTriggerOverrides: ActivityTriggerOverridesSchema.optional(),
3600
+ /** Omitted keeps the binding's current timing; `{}` resets every field to its default. */
3601
+ responseTiming: ResponseTimingSettingsSchema.optional(),
3090
3602
  })
3091
3603
  .strict(),
3092
3604
  response: z
@@ -3098,6 +3610,7 @@ export const HostApiContracts = {
3098
3610
  processingFeedback: z.enum(['auto', 'off']).default('auto'),
3099
3611
  localAgentMessages: z.enum(['observe', 'trigger']).optional(),
3100
3612
  activityTriggerOverrides: ActivityTriggerOverridesSchema.default({}),
3613
+ responseTiming: ResponseTimingSettingsSchema.optional(),
3101
3614
  boundAt: z.number().int().safe().nonnegative(),
3102
3615
  })
3103
3616
  .strict(),
@@ -3121,6 +3634,23 @@ export const HostApiContracts = {
3121
3634
  response: WorkTreeOrderSchema,
3122
3635
  error: HostApiErrorSchema,
3123
3636
  }),
3637
+ getChannelListView: defineContract({
3638
+ method: 'GET',
3639
+ path: '/api/channel-list-view',
3640
+ params: EmptyParamsSchema,
3641
+ request: NoRequestBodySchema,
3642
+ response: ChannelListViewSchema,
3643
+ error: HostApiErrorSchema,
3644
+ }),
3645
+ updateChannelListView: defineContract({
3646
+ invalidatesSnapshot: true,
3647
+ method: 'PUT',
3648
+ path: '/api/channel-list-view',
3649
+ params: EmptyParamsSchema,
3650
+ request: ChannelListViewSchema,
3651
+ response: ChannelListViewSchema,
3652
+ error: HostApiErrorSchema,
3653
+ }),
3124
3654
  listChannelMessages: defineContract({
3125
3655
  method: 'GET',
3126
3656
  path: '/api/channels/:channelId/messages',
@@ -3369,6 +3899,39 @@ export const HostApiContracts = {
3369
3899
  response: MessagePacingSettingsSchema,
3370
3900
  error: HostApiErrorSchema,
3371
3901
  }),
3902
+ getCompactionSettings: defineContract({
3903
+ method: 'GET',
3904
+ path: '/api/settings/compaction',
3905
+ params: EmptyParamsSchema,
3906
+ request: NoRequestBodySchema,
3907
+ response: CompactionSettingsSchema,
3908
+ error: HostApiErrorSchema,
3909
+ }),
3910
+ updateCompactionSettings: defineContract({
3911
+ invalidatesSnapshot: true,
3912
+ method: 'PUT',
3913
+ path: '/api/settings/compaction',
3914
+ params: EmptyParamsSchema,
3915
+ request: CompactionSettingsSchema,
3916
+ response: CompactionSettingsSchema,
3917
+ error: HostApiErrorSchema,
3918
+ }),
3919
+ getModelFallbackSettings: defineContract({
3920
+ method: 'GET',
3921
+ path: '/api/settings/model-fallback',
3922
+ params: EmptyParamsSchema,
3923
+ request: NoRequestBodySchema,
3924
+ response: ModelFallbackSettingsSchema,
3925
+ error: HostApiErrorSchema,
3926
+ }),
3927
+ updateModelFallbackSettings: defineContract({
3928
+ method: 'PUT',
3929
+ path: '/api/settings/model-fallback',
3930
+ params: EmptyParamsSchema,
3931
+ request: ModelFallbackSettingsSchema,
3932
+ response: ModelFallbackSettingsSchema,
3933
+ error: HostApiErrorSchema,
3934
+ }),
3372
3935
  getUpgradeBackupSettings: defineContract({
3373
3936
  method: 'GET',
3374
3937
  path: '/api/settings/upgrade-backup',
@@ -3403,109 +3966,167 @@ export const HostApiContracts = {
3403
3966
  response: RetrievalStatusSchema,
3404
3967
  error: HostApiErrorSchema,
3405
3968
  }),
3406
- downloadRetrievalModel: defineContract({
3407
- method: 'POST',
3408
- path: '/api/settings/retrieval/model/download',
3969
+ getSpeechSettings: defineContract({
3970
+ method: 'GET',
3971
+ path: '/api/settings/speech',
3409
3972
  params: EmptyParamsSchema,
3410
3973
  request: NoRequestBodySchema,
3411
- response: RetrievalStatusSchema,
3974
+ response: SpeechSettingsSchema,
3412
3975
  error: HostApiErrorSchema,
3413
3976
  }),
3414
- removeRetrievalModel: defineContract({
3415
- method: 'DELETE',
3416
- path: '/api/settings/retrieval/model',
3977
+ updateSpeechSettings: defineContract({
3978
+ method: 'PUT',
3979
+ path: '/api/settings/speech',
3980
+ params: EmptyParamsSchema,
3981
+ request: z.object({ cloud: SpeechCloudSettingsSchema.nullable() }).strict(),
3982
+ response: SpeechSettingsSchema,
3983
+ error: HostApiErrorSchema,
3984
+ }),
3985
+ listAddons: defineContract({
3986
+ method: 'GET',
3987
+ path: '/api/addons',
3417
3988
  params: EmptyParamsSchema,
3418
3989
  request: NoRequestBodySchema,
3419
- response: RetrievalStatusSchema,
3990
+ response: z.object({ addons: z.array(AddonStatusSchema) }).strict(),
3420
3991
  error: HostApiErrorSchema,
3421
3992
  }),
3422
- downloadCodeRunNode: defineContract({
3993
+ installAddonProvider: defineContract({
3423
3994
  invalidatesSnapshot: true,
3424
3995
  method: 'POST',
3425
- path: '/api/runtimes/code-run-node/download',
3426
- params: EmptyParamsSchema,
3996
+ path: '/api/addons/:id/providers/:provider/install',
3997
+ params: z.object({ id: AddonIdSchema, provider: NonEmptyStringSchema }).strict(),
3427
3998
  request: NoRequestBodySchema,
3428
- response: CodeRunAvailabilitySchema,
3999
+ response: AddonStatusSchema,
3429
4000
  error: HostApiErrorSchema,
3430
4001
  }),
3431
- removeCodeRunNode: defineContract({
4002
+ removeAddonProvider: defineContract({
3432
4003
  invalidatesSnapshot: true,
3433
4004
  method: 'DELETE',
3434
- path: '/api/runtimes/code-run-node',
3435
- params: EmptyParamsSchema,
4005
+ path: '/api/addons/:id/providers/:provider',
4006
+ params: z.object({ id: AddonIdSchema, provider: NonEmptyStringSchema }).strict(),
3436
4007
  request: NoRequestBodySchema,
3437
- response: CodeRunAvailabilitySchema,
4008
+ response: AddonStatusSchema,
3438
4009
  error: HostApiErrorSchema,
3439
4010
  }),
3440
- downloadGithubCli: defineContract({
4011
+ selectAddonProvider: defineContract({
3441
4012
  invalidatesSnapshot: true,
3442
- method: 'POST',
3443
- path: '/api/runtimes/github-cli/download',
3444
- params: EmptyParamsSchema,
3445
- request: NoRequestBodySchema,
3446
- response: GithubCliAvailabilitySchema,
4013
+ method: 'PUT',
4014
+ path: '/api/addons/:id/selection',
4015
+ params: z.object({ id: AddonIdSchema }).strict(),
4016
+ request: z.object({ provider: NonEmptyStringSchema }).strict(),
4017
+ response: AddonStatusSchema,
3447
4018
  error: HostApiErrorSchema,
3448
4019
  }),
3449
- removeGithubCli: defineContract({
3450
- invalidatesSnapshot: true,
3451
- method: 'DELETE',
3452
- path: '/api/runtimes/github-cli',
4020
+ getLinkedAccounts: defineContract({
4021
+ method: 'GET',
4022
+ path: '/api/settings/linked-accounts',
3453
4023
  params: EmptyParamsSchema,
3454
4024
  request: NoRequestBodySchema,
3455
- response: GithubCliAvailabilitySchema,
4025
+ response: LinkedAccountsViewSchema,
3456
4026
  error: HostApiErrorSchema,
3457
4027
  }),
3458
- downloadVerificationBrowser: defineContract({
4028
+ addLinkedAccount: defineContract({
3459
4029
  invalidatesSnapshot: true,
3460
4030
  method: 'POST',
3461
- path: '/api/runtimes/verification-browser/download',
4031
+ path: '/api/settings/linked-accounts',
3462
4032
  params: EmptyParamsSchema,
3463
- request: NoRequestBodySchema,
3464
- response: VerificationBrowserAvailabilitySchema,
4033
+ request: AddLinkedAccountRequestSchema,
4034
+ response: LinkedAccountsViewSchema,
3465
4035
  error: HostApiErrorSchema,
3466
4036
  }),
3467
- removeVerificationBrowser: defineContract({
4037
+ replaceLinkedAccountToken: defineContract({
3468
4038
  invalidatesSnapshot: true,
3469
- method: 'DELETE',
3470
- path: '/api/runtimes/verification-browser',
3471
- params: EmptyParamsSchema,
4039
+ method: 'PUT',
4040
+ path: '/api/settings/linked-accounts/:accountId/token',
4041
+ params: linkedAccountParam,
4042
+ request: ReplaceLinkedAccountTokenRequestSchema,
4043
+ response: LinkedAccountsViewSchema,
4044
+ error: HostApiErrorSchema,
4045
+ }),
4046
+ removeLinkedAccount: defineContract({
4047
+ invalidatesSnapshot: true,
4048
+ method: 'POST',
4049
+ path: '/api/settings/linked-accounts/:accountId/remove',
4050
+ params: linkedAccountParam,
4051
+ request: RemoveLinkedAccountRequestSchema,
4052
+ response: RemoveLinkedAccountResultSchema,
4053
+ error: HostApiErrorSchema,
4054
+ }),
4055
+ getLinkedAccountUses: defineContract({
4056
+ method: 'GET',
4057
+ path: '/api/settings/linked-accounts/:accountId/uses',
4058
+ params: linkedAccountParam,
3472
4059
  request: NoRequestBodySchema,
3473
- response: VerificationBrowserAvailabilitySchema,
4060
+ response: LinkedAccountUsesViewSchema,
3474
4061
  error: HostApiErrorSchema,
3475
4062
  }),
3476
- getCommandAccounts: defineContract({
4063
+ getCredentials: defineContract({
3477
4064
  method: 'GET',
3478
- path: '/api/settings/command-accounts',
4065
+ path: '/api/settings/credentials',
3479
4066
  params: EmptyParamsSchema,
3480
4067
  request: NoRequestBodySchema,
3481
- response: CommandAccountsViewSchema,
4068
+ response: CredentialsViewSchema,
3482
4069
  error: HostApiErrorSchema,
3483
4070
  }),
3484
- addCommandAccount: defineContract({
4071
+ createCredential: defineContract({
3485
4072
  invalidatesSnapshot: true,
3486
4073
  method: 'POST',
3487
- path: '/api/settings/command-accounts',
4074
+ path: '/api/settings/credentials',
3488
4075
  params: EmptyParamsSchema,
3489
- request: AddCommandAccountRequestSchema,
3490
- response: CommandAccountsViewSchema,
4076
+ request: CreateCredentialRequestSchema,
4077
+ response: CredentialsViewSchema,
4078
+ error: HostApiErrorSchema,
4079
+ }),
4080
+ updateCredential: defineContract({
4081
+ invalidatesSnapshot: true,
4082
+ method: 'PATCH',
4083
+ path: '/api/settings/credentials/:credentialId',
4084
+ params: credentialParam,
4085
+ request: UpdateCredentialRequestSchema,
4086
+ response: CredentialsViewSchema,
3491
4087
  error: HostApiErrorSchema,
3492
4088
  }),
3493
- replaceCommandAccountToken: defineContract({
4089
+ replaceCredentialValue: defineContract({
3494
4090
  invalidatesSnapshot: true,
3495
4091
  method: 'PUT',
3496
- path: '/api/settings/command-accounts/:accountId',
3497
- params: z.object({ accountId: NonEmptyStringSchema.max(64) }).strict(),
3498
- request: AddCommandAccountRequestSchema,
3499
- response: CommandAccountsViewSchema,
4092
+ path: '/api/settings/credentials/:credentialId/value',
4093
+ params: credentialParam,
4094
+ request: ReplaceCredentialValueRequestSchema,
4095
+ response: CredentialsViewSchema,
3500
4096
  error: HostApiErrorSchema,
3501
4097
  }),
3502
- removeCommandAccount: defineContract({
4098
+ removeCredential: defineContract({
3503
4099
  invalidatesSnapshot: true,
4100
+ method: 'POST',
4101
+ path: '/api/settings/credentials/:credentialId/remove',
4102
+ params: credentialParam,
4103
+ request: RemoveCredentialRequestSchema,
4104
+ response: RemoveCredentialResultSchema,
4105
+ error: HostApiErrorSchema,
4106
+ }),
4107
+ getCredentialUses: defineContract({
4108
+ method: 'GET',
4109
+ path: '/api/settings/credentials/:credentialId/uses',
4110
+ params: credentialParam,
4111
+ request: NoRequestBodySchema,
4112
+ response: CredentialUsesViewSchema,
4113
+ error: HostApiErrorSchema,
4114
+ }),
4115
+ dismissCredentialRequest: defineContract({
3504
4116
  method: 'DELETE',
3505
- path: '/api/settings/command-accounts/:accountId',
3506
- params: z.object({ accountId: NonEmptyStringSchema.max(64) }).strict(),
4117
+ path: '/api/settings/credentials/requests/:requestId',
4118
+ params: z.object({ requestId: NonEmptyStringSchema.max(64) }).strict(),
3507
4119
  request: NoRequestBodySchema,
3508
- response: CommandAccountsViewSchema,
4120
+ response: CredentialsViewSchema,
4121
+ error: HostApiErrorSchema,
4122
+ }),
4123
+ setModelProviderAccess: defineContract({
4124
+ invalidatesSnapshot: true,
4125
+ method: 'PUT',
4126
+ path: '/api/settings/credentials/model-providers/:provider',
4127
+ params: z.object({ provider: NonEmptyStringSchema.max(128) }).strict(),
4128
+ request: SetModelProviderAccessRequestSchema,
4129
+ response: CredentialsViewSchema,
3509
4130
  error: HostApiErrorSchema,
3510
4131
  }),
3511
4132
  getChannelMemberNotes: defineContract({
@@ -3782,6 +4403,14 @@ export const HostApiContracts = {
3782
4403
  response: z.object({ disabled: z.literal(true) }).strict(),
3783
4404
  error: HostApiErrorSchema,
3784
4405
  }),
4406
+ setDshPluginImageSubmission: defineContract({
4407
+ method: 'PUT',
4408
+ path: '/api/dsh/plugin-installs/:packageId/image-submission',
4409
+ params: dshPluginPackageParam,
4410
+ request: z.object({ allowed: z.boolean() }).strict(),
4411
+ response: z.object({ allowed: z.boolean() }).strict(),
4412
+ error: HostApiErrorSchema,
4413
+ }),
3785
4414
  removeDshPluginPackage: defineContract({
3786
4415
  invalidatesSnapshot: true,
3787
4416
  method: 'DELETE',
@@ -4000,19 +4629,24 @@ export const HostApiContracts = {
4000
4629
  /** 排序方式,默认最近更新;游标只在同一种排序下有效。 */
4001
4630
  sort: CommunityExtensionSortSchema.optional(),
4002
4631
  cursor: z.string().max(200).optional(),
4632
+ /** 跳过本机缓存,重新向社区读取。 */
4633
+ refresh: z.enum(['1']).optional(),
4003
4634
  })
4004
4635
  .strict(),
4005
4636
  request: NoRequestBodySchema,
4006
- response: z.object({ items: z.array(CommunityExtensionSummarySchema), nextCursor: z.string().nullable() }).strict(),
4637
+ response: z
4638
+ .object({ items: z.array(CommunityExtensionSummarySchema), nextCursor: z.string().nullable() })
4639
+ .extend(CommunityFreshnessSchema.shape)
4640
+ .strict(),
4007
4641
  error: HostApiErrorSchema,
4008
4642
  }),
4009
4643
  getCommunityExtension: defineContract({
4010
4644
  timeoutMs: 30_000,
4011
4645
  method: 'GET',
4012
4646
  path: '/api/community/extensions/:extensionId',
4013
- params: z.object({ extensionId: ExtensionIdSchema }).strict(),
4647
+ params: z.object({ extensionId: ExtensionIdSchema, refresh: z.enum(['1']).optional() }).strict(),
4014
4648
  request: NoRequestBodySchema,
4015
- response: CommunityExtensionDetailSchema,
4649
+ response: CommunityExtensionDetailSchema.extend(CommunityFreshnessSchema.shape),
4016
4650
  error: HostApiErrorSchema,
4017
4651
  }),
4018
4652
  /** 下载社区发布并按包摘要校验,结果与本地文件导入的检查相同,再经 `commitExtensionImport` 确认。 */
@@ -4139,19 +4773,24 @@ export const HostApiContracts = {
4139
4773
  tag: z.string().trim().min(1).max(40).optional(),
4140
4774
  official: z.literal('1').optional(),
4141
4775
  cursor: z.string().min(1).max(500).optional(),
4776
+ /** 跳过本机缓存,重新向社区读取。 */
4777
+ refresh: z.enum(['1']).optional(),
4142
4778
  })
4143
4779
  .strict(),
4144
4780
  request: NoRequestBodySchema,
4145
- response: z.object({ items: z.array(CommunityPersonaSummarySchema), nextCursor: z.string().nullable() }).strict(),
4781
+ response: z
4782
+ .object({ items: z.array(CommunityPersonaSummarySchema), nextCursor: z.string().nullable() })
4783
+ .extend(CommunityFreshnessSchema.shape)
4784
+ .strict(),
4146
4785
  error: HostApiErrorSchema,
4147
4786
  }),
4148
4787
  getCommunityPersona: defineContract({
4149
4788
  timeoutMs: 30_000,
4150
4789
  method: 'GET',
4151
4790
  path: '/api/community/personas/:personaId',
4152
- params: z.object({ personaId: CommunityPersonaIdSchema }).strict(),
4791
+ params: z.object({ personaId: CommunityPersonaIdSchema, refresh: z.enum(['1']).optional() }).strict(),
4153
4792
  request: NoRequestBodySchema,
4154
- response: CommunityPersonaDetailSchema,
4793
+ response: CommunityPersonaDetailSchema.extend(CommunityFreshnessSchema.shape),
4155
4794
  error: HostApiErrorSchema,
4156
4795
  }),
4157
4796
  /**
@@ -4346,6 +4985,31 @@ export const HostApiContracts = {
4346
4985
  response: LlmProviderSettingsSchema,
4347
4986
  error: HostApiErrorSchema,
4348
4987
  }),
4988
+ llmSubscriptionStatus: defineContract({
4989
+ method: 'GET',
4990
+ path: '/api/llm/subscriptions/:provider',
4991
+ params: llmProviderParam,
4992
+ request: NoRequestBodySchema,
4993
+ response: SubscriptionStatusSchema,
4994
+ error: HostApiErrorSchema,
4995
+ }),
4996
+ llmSubscriptionAction: defineContract({
4997
+ invalidatesSnapshot: true,
4998
+ method: 'POST',
4999
+ path: '/api/llm/subscriptions/:provider',
5000
+ params: llmProviderParam,
5001
+ request: z.discriminatedUnion('action', [
5002
+ z.object({ action: z.literal('use-subscription'), expectedRevision: z.number().int().nonnegative() }).strict(),
5003
+ z.object({ action: z.enum(['login', 'cancel']) }).strict(),
5004
+ z.object({ action: z.literal('answer'), promptId: NonEmptyStringSchema, value: z.string().max(4096) }).strict(),
5005
+ z.object({ action: z.enum(['activate', 'remove']), accountId: NonEmptyStringSchema }).strict(),
5006
+ z
5007
+ .object({ action: z.literal('rename'), accountId: NonEmptyStringSchema, label: z.string().min(1).max(80) })
5008
+ .strict(),
5009
+ ]),
5010
+ response: z.object({ status: SubscriptionStatusSchema, revocationConfirmed: z.boolean().optional() }).strict(),
5011
+ error: HostApiErrorSchema,
5012
+ }),
4349
5013
  llmDiscoverModels: defineContract({
4350
5014
  method: 'POST',
4351
5015
  path: '/api/llm/discover-models',
@@ -4755,6 +5419,7 @@ export const HostApiContracts = {
4755
5419
  extensionRevisionId: ExtensionRevisionIdSchema,
4756
5420
  config: JsonValueSchema,
4757
5421
  configuredSecrets: z.array(z.string()).optional(),
5422
+ boundCredentials: BoundCredentialsSchema.optional(),
4758
5423
  activatedAt: z.number().int().safe().nonnegative(),
4759
5424
  })
4760
5425
  .strict()
@@ -4775,6 +5440,16 @@ export const HostApiContracts = {
4775
5440
  response: z.object({ disabled: z.literal(true), pending: ExtensionActivationTransitionSchema.optional() }).strict(),
4776
5441
  error: HostApiErrorSchema,
4777
5442
  }),
5443
+ putAgentOutboundOrder: defineContract({
5444
+ invalidatesSnapshot: true,
5445
+ method: 'PUT',
5446
+ path: '/api/agents/:agentId/outbound-order',
5447
+ params: agentParam,
5448
+ /** Every enabled extension that processes the agent's messages before sending, in the new order. */
5449
+ request: z.object({ extensionIds: z.array(ExtensionIdSchema).max(64) }).strict(),
5450
+ response: z.object({ extensionIds: z.array(ExtensionIdSchema) }).strict(),
5451
+ error: HostApiErrorSchema,
5452
+ }),
4778
5453
  updateExtensionActivationConfig: defineContract({
4779
5454
  invalidatesSnapshot: true,
4780
5455
  method: 'PUT',
@@ -4785,9 +5460,17 @@ export const HostApiContracts = {
4785
5460
  config: JsonValueSchema,
4786
5461
  /** Write-only credential drafts keyed by secret field; an absent or empty draft keeps the stored one. */
4787
5462
  secrets: z.record(z.string(), z.string().max(8192)).optional(),
5463
+ /** Secret fields to bind to named credentials instead of a typed value; a field takes one or the other. */
5464
+ credentialBindings: CredentialBindingsRequestSchema.optional(),
5465
+ })
5466
+ .strict(),
5467
+ response: z
5468
+ .object({
5469
+ config: JsonValueSchema,
5470
+ configuredSecrets: z.array(z.string()).optional(),
5471
+ boundCredentials: BoundCredentialsSchema.optional(),
4788
5472
  })
4789
5473
  .strict(),
4790
- response: z.object({ config: JsonValueSchema, configuredSecrets: z.array(z.string()).optional() }).strict(),
4791
5474
  error: HostApiErrorSchema,
4792
5475
  }),
4793
5476
  updateHostExtensionConfig: defineContract({
@@ -4801,9 +5484,37 @@ export const HostApiContracts = {
4801
5484
  config: JsonValueSchema,
4802
5485
  /** Write-only credential drafts keyed by secret field; an absent or empty draft keeps the stored one. */
4803
5486
  secrets: z.record(z.string(), z.string().max(8192)).optional(),
5487
+ /** Secret fields to bind to named credentials instead of a typed value; a field takes one or the other. */
5488
+ credentialBindings: CredentialBindingsRequestSchema.optional(),
5489
+ })
5490
+ .strict(),
5491
+ response: z
5492
+ .object({
5493
+ config: JsonValueSchema,
5494
+ configuredSecrets: z.array(z.string()).optional(),
5495
+ boundCredentials: BoundCredentialsSchema.optional(),
4804
5496
  })
4805
5497
  .strict(),
4806
- response: z.object({ config: JsonValueSchema, configuredSecrets: z.array(z.string()).optional() }).strict(),
5498
+ error: HostApiErrorSchema,
5499
+ }),
5500
+ /** Configuration history of one extension, newest first: the host layer and every agent's configuration. */
5501
+ getExtensionConfigVersions: defineContract({
5502
+ method: 'GET',
5503
+ path: '/api/extensions/:extensionId/config-versions',
5504
+ params: extensionParam,
5505
+ request: NoRequestBodySchema,
5506
+ response: z.object({ versions: z.array(ExtensionConfigVersionSchema) }).strict(),
5507
+ error: HostApiErrorSchema,
5508
+ }),
5509
+ /** Writes an earlier configuration again as a new version of the same layer (and agent). */
5510
+ revertExtensionConfig: defineContract({
5511
+ invalidatesSnapshot: true,
5512
+ timeoutMs: 300_000,
5513
+ method: 'POST',
5514
+ path: '/api/extensions/:extensionId/config-versions/:versionId/revert',
5515
+ params: extensionConfigVersionParam,
5516
+ request: z.object({ reason: z.string().trim().max(500).optional() }).strict(),
5517
+ response: z.object({ version: ExtensionConfigVersionSchema }).strict(),
4807
5518
  error: HostApiErrorSchema,
4808
5519
  }),
4809
5520
  installHostExtension: defineContract({
@@ -4914,6 +5625,20 @@ export const HostApiContracts = {
4914
5625
  response: z.object({ dismissed: z.literal(true), revision: z.string().min(1) }).strict(),
4915
5626
  error: HostApiErrorSchema,
4916
5627
  }),
5628
+ dismissAttentionBatch: defineContract({
5629
+ method: 'POST',
5630
+ path: '/api/attention/dismiss',
5631
+ params: EmptyParamsSchema,
5632
+ request: z
5633
+ .object({
5634
+ ids: z.array(z.string().min(1).max(200)).max(200),
5635
+ /** Causes to keep quiet until they recover; new items of these causes are not shown meanwhile. */
5636
+ causes: z.array(z.string().min(1).max(200)).max(200),
5637
+ })
5638
+ .strict(),
5639
+ response: z.object({ dismissed: z.literal(true), revision: z.string().min(1) }).strict(),
5640
+ error: HostApiErrorSchema,
5641
+ }),
4917
5642
  getChannelPending: defineContract({
4918
5643
  method: 'GET',
4919
5644
  path: '/api/channels/:channelId/pending',
@@ -5028,6 +5753,23 @@ export const HostApiContracts = {
5028
5753
  response: ExtensionLineageSchema,
5029
5754
  error: HostApiErrorSchema,
5030
5755
  }),
5756
+ setExtensionRevisionKept: defineContract({
5757
+ method: 'POST',
5758
+ path: '/api/extensions/:extensionId/revisions/:revisionId/keep',
5759
+ params: z.object({ extensionId: ExtensionIdSchema, revisionId: ExtensionRevisionIdSchema }).strict(),
5760
+ request: z.object({ kept: z.boolean() }).strict(),
5761
+ response: z.object({ kept: z.boolean() }).strict(),
5762
+ error: HostApiErrorSchema,
5763
+ }),
5764
+ deleteExtensionRevisions: defineContract({
5765
+ invalidatesSnapshot: true,
5766
+ method: 'POST',
5767
+ path: '/api/extensions/:extensionId/revisions/delete',
5768
+ params: extensionParam,
5769
+ request: z.object({ revisionIds: z.array(ExtensionRevisionIdSchema).min(1).max(200) }).strict(),
5770
+ response: z.object({ deleted: z.number().int().nonnegative() }).strict(),
5771
+ error: HostApiErrorSchema,
5772
+ }),
5031
5773
  branchExtension: defineContract({
5032
5774
  invalidatesSnapshot: true,
5033
5775
  timeoutMs: 300_000,
@@ -5243,9 +5985,11 @@ const buildValidatedHostApiPath = (
5243
5985
  const consumed = new Set<string>()
5244
5986
  const pathname = contract.path.replace(pathParameterPattern, (_placeholder, key: string) => {
5245
5987
  const value = params[key]
5246
- if (typeof value !== 'string') throw new TypeError(`Host API path parameter ${key} must be a string.`)
5988
+ if (typeof value !== 'string' && typeof value !== 'number') {
5989
+ throw new TypeError(`Host API path parameter ${key} must be a string or number.`)
5990
+ }
5247
5991
  consumed.add(key)
5248
- return encodeURIComponent(value)
5992
+ return encodeURIComponent(String(value))
5249
5993
  })
5250
5994
  const query = new URLSearchParams()
5251
5995
  for (const [key, value] of Object.entries(params)) {