@nekro-nxt/contracts 0.5.0 → 0.6.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,
@@ -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,7 @@ 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
+ /** Linked accounts this agent's commands act as, one per platform; absent in older clients means none. */
340
369
  commandAccounts: z.array(CommandAccountGrantSchema).max(8).default([]),
341
370
  /**
342
371
  * What the agent may do with extensions without the console: `manual` only develops and test-runs, `confirm` asks
@@ -345,12 +374,23 @@ const AgentCapabilitiesSchema = z
345
374
  extensionAutonomy: z.enum(['manual', 'confirm', 'autonomous']).default('manual'),
346
375
  /** Whose requests make the agent develop or change extensions: anyone in the channel, or administrators only. */
347
376
  extensionRequesters: z.enum(['everyone', 'admins']).default('everyone'),
377
+ /**
378
+ * Whose requests make the agent change extension configuration. With `everyone`, members may only have it change
379
+ * ordinary fields of its own configuration; network fields, the host layer and credentials still need an
380
+ * administrator.
381
+ */
382
+ extensionConfigRequesters: z.enum(['everyone', 'admins']).default('admins'),
348
383
  })
349
384
  .strict()
350
385
 
351
386
  export const DynamicClientApprovalPolicySchema = z.enum(['manual', 'automatic'])
352
387
 
353
- export const NotificationEventKeySchema = z.enum(['dynamic-client-approval-requested', 'community-feedback-update'])
388
+ export const NotificationEventKeySchema = z.enum([
389
+ 'dynamic-client-approval-requested',
390
+ 'community-feedback-update',
391
+ 'credential-requested',
392
+ 'extension-notification',
393
+ ])
354
394
  export type NotificationEventKey = z.output<typeof NotificationEventKeySchema>
355
395
 
356
396
  export const NotificationSettingsViewSchema = z
@@ -368,6 +408,8 @@ export const NotificationSettingsViewSchema = z
368
408
  .object({
369
409
  'dynamic-client-approval-requested': z.boolean(),
370
410
  'community-feedback-update': z.boolean().default(true),
411
+ 'credential-requested': z.boolean().default(true),
412
+ 'extension-notification': z.boolean().default(true),
371
413
  })
372
414
  .strict(),
373
415
  })
@@ -386,7 +428,12 @@ export const UpdateNotificationSettingsRequestSchema = z
386
428
  })
387
429
  .strict(),
388
430
  events: z
389
- .object({ 'dynamic-client-approval-requested': z.boolean(), 'community-feedback-update': z.boolean().optional() })
431
+ .object({
432
+ 'dynamic-client-approval-requested': z.boolean(),
433
+ 'community-feedback-update': z.boolean().optional(),
434
+ 'credential-requested': z.boolean().optional(),
435
+ 'extension-notification': z.boolean().optional(),
436
+ })
390
437
  .strict(),
391
438
  })
392
439
  .strict()
@@ -406,6 +453,122 @@ export const ClientNotificationFeedResponseSchema = z
406
453
  })
407
454
  .strict()
408
455
 
456
+ /** Which model writes one kind of compaction summary: that kind's default, or a model chosen for this agent. */
457
+ export const AgentCompactionModelChoiceSchema = z.discriminatedUnion('mode', [
458
+ z.object({ mode: z.literal('follow') }).strict(),
459
+ z.object({ mode: z.literal('custom'), model: AgentModelSchema }).strict(),
460
+ ])
461
+
462
+ /**
463
+ * In-session compaction follows the main model by default, so the replayed conversation hits the provider's cache;
464
+ * compaction into a new session follows the host's compaction model.
465
+ */
466
+ export const AgentCompactionPolicySchema = z
467
+ .object({ inSession: AgentCompactionModelChoiceSchema, newSession: AgentCompactionModelChoiceSchema })
468
+ .strict()
469
+
470
+ /** The host's idle compaction threshold, summary model and wait limit. */
471
+ export const CompactionSettingsSchema = z
472
+ .object({
473
+ /** Minimum context occupancy for idle rollover; older settings inherit 50%. */
474
+ idleThresholdPercent: z.number().int().min(1).max(100).default(50),
475
+ newSessionModel: z.discriminatedUnion('mode', [
476
+ z.object({ mode: z.literal('main') }).strict(),
477
+ z.object({ mode: z.literal('custom'), model: AgentModelSchema }).strict(),
478
+ ]),
479
+ /** Past this the new session starts with a fallback note instead of a summary. */
480
+ timeoutSeconds: z.number().int().min(30).max(900),
481
+ })
482
+ .strict()
483
+
484
+ export type CompactionSettings = z.output<typeof CompactionSettingsSchema>
485
+
486
+ export const DEFAULT_COMPACTION_SETTINGS: CompactionSettings = {
487
+ idleThresholdPercent: 50,
488
+ newSessionModel: { mode: 'main' },
489
+ timeoutSeconds: 180,
490
+ }
491
+
492
+ export const FALLBACK_MODEL_LIMIT = 3
493
+
494
+ /**
495
+ * Models an agent switches to, in order, when its main model has used up its quota or stays unavailable after the
496
+ * usual retries. Empty leaves the main model alone, as before fallback models existed.
497
+ */
498
+ export const AgentFallbackModelsSchema = z
499
+ .array(AgentModelSchema)
500
+ .max(FALLBACK_MODEL_LIMIT)
501
+ .refine(
502
+ (models) => new Set(models.map((model) => `${model.provider}\0${model.model}`)).size === models.length,
503
+ '备用模型不能重复。',
504
+ )
505
+
506
+ /** Why a channel conversation left its main model. */
507
+ export const ModelFallbackReasonSchema = z.enum(['quota', 'rate-limit', 'unavailable'])
508
+ export type ModelFallbackReason = z.output<typeof ModelFallbackReasonSchema>
509
+
510
+ /** How long a model that failed is passed over before conversations try it again. */
511
+ export const ModelFallbackSettingsSchema = z
512
+ .object({
513
+ /** After rate limits, server errors, timeouts or empty replies that outlasted the usual retries. */
514
+ unavailableCooldownMinutes: z.number().int().min(1).max(240),
515
+ /** After the quota or balance ran out. */
516
+ quotaCooldownMinutes: z.number().int().min(5).max(1440),
517
+ })
518
+ .strict()
519
+
520
+ export type ModelFallbackSettings = z.output<typeof ModelFallbackSettingsSchema>
521
+
522
+ export const DEFAULT_MODEL_FALLBACK_SETTINGS: ModelFallbackSettings = {
523
+ unavailableCooldownMinutes: 10,
524
+ quotaCooldownMinutes: 60,
525
+ }
526
+
527
+ /** The name an agent passes to open a sub-conversation on its main model; no candidate may take it. */
528
+ export const SUB_CONVERSATION_MAIN_MODEL = '主模型'
529
+
530
+ export const SUB_CONVERSATION_MODEL_LIMITS = { candidates: 8, name: 20, guidance: 200 } as const
531
+
532
+ /**
533
+ * Models an agent may open sub-conversations with, each named so the agent can pick it and described so it knows
534
+ * when to. Without a `default`, a sub-conversation opened without naming a model follows the main model.
535
+ */
536
+ export const AgentSubConversationModelsSchema = z
537
+ .object({
538
+ candidates: z
539
+ .array(
540
+ z
541
+ .object({
542
+ // Names are listed to the agent separated by commas, so they carry none and no line breaks.
543
+ name: z
544
+ .string()
545
+ .trim()
546
+ .min(1, '请填写子对话模型的名称。')
547
+ .max(SUB_CONVERSATION_MODEL_LIMITS.name)
548
+ .regex(/^[^\s,,、]+(?: [^\s,,、]+)*$/u, '子对话模型的名称不能含换行、逗号或顿号。')
549
+ .refine((name) => name !== SUB_CONVERSATION_MAIN_MODEL, `「${SUB_CONVERSATION_MAIN_MODEL}」是保留名称。`),
550
+ model: AgentModelSchema,
551
+ guidance: z.string().trim().max(SUB_CONVERSATION_MODEL_LIMITS.guidance),
552
+ })
553
+ .strict(),
554
+ )
555
+ .max(SUB_CONVERSATION_MODEL_LIMITS.candidates),
556
+ default: z.string().trim().min(1).optional(),
557
+ })
558
+ .strict()
559
+ .superRefine((policy, context) => {
560
+ const names = policy.candidates.map((candidate) => candidate.name)
561
+ if (new Set(names).size !== names.length)
562
+ context.addIssue({ code: 'custom', message: '子对话模型的名称不能重复。' })
563
+ if (policy.default !== undefined && !names.includes(policy.default)) {
564
+ context.addIssue({ code: 'custom', message: '默认的子对话模型不在候选里。' })
565
+ }
566
+ })
567
+
568
+ export type AgentSubConversationModels = z.output<typeof AgentSubConversationModelsSchema>
569
+
570
+ export const NO_SUB_CONVERSATION_MODELS: AgentSubConversationModels = { candidates: [] }
571
+
409
572
  const AgentRevisionRequestContentSchema = z
410
573
  .object({
411
574
  displayName: z.string().trim().min(1).max(80),
@@ -417,6 +580,10 @@ const AgentRevisionRequestContentSchema = z
417
580
  model: AgentModelSchema,
418
581
  imagePolicy: ImageUnderstandingPolicyApiSchema.optional(),
419
582
  dynamicClientApprovalPolicy: DynamicClientApprovalPolicySchema.optional(),
583
+ compaction: AgentCompactionPolicySchema.optional(),
584
+ subConversationModels: AgentSubConversationModelsSchema.optional(),
585
+ fallbackModels: AgentFallbackModelsSchema.optional(),
586
+ aliases: AgentAliasesSchema.optional(),
420
587
  })
421
588
  .strict()
422
589
 
@@ -457,11 +624,12 @@ const UpdateAgentCapabilitiesRequestSchema = AgentCapabilitiesSchema.partial()
457
624
  commandAccounts: z.array(CommandAccountGrantSchema).max(8).optional(),
458
625
  extensionAutonomy: z.enum(['manual', 'confirm', 'autonomous']).optional(),
459
626
  extensionRequesters: z.enum(['everyone', 'admins']).optional(),
627
+ extensionConfigRequesters: z.enum(['everyone', 'admins']).optional(),
460
628
  })
461
629
  .strict()
462
630
  .refine((value) => Object.values(value).some((entry) => entry !== undefined), '至少提供一个能力。')
463
631
 
464
- const TriggerPolicySchema = z.enum(['always', 'mentioned-or-replied', 'command', 'observe-only'])
632
+ const TriggerPolicySchema = z.enum(['always', 'mentioned-or-replied', 'smart', 'command', 'observe-only'])
465
633
 
466
634
  export const WorkTreeOrderSchema = z
467
635
  .object({
@@ -473,6 +641,22 @@ export const WorkTreeOrderSchema = z
473
641
 
474
642
  export type WorkTreeOrder = z.output<typeof WorkTreeOrderSchema>
475
643
 
644
+ /** The group key of 「未接线」 in `collapsedGroupIds`; agent groups use the agent id. */
645
+ export const UNBOUND_CHANNEL_GROUP_ID = 'unbound'
646
+
647
+ /**
648
+ * How the console arranges its channel list: pinned channels in their order and the groups it has collapsed. Shared
649
+ * by every device signed in to the console; it never changes bindings.
650
+ */
651
+ export const ChannelListViewSchema = z
652
+ .object({
653
+ pinnedChannelIds: z.array(ChannelIdSchema).max(200),
654
+ collapsedGroupIds: z.array(z.union([AgentIdSchema, z.literal(UNBOUND_CHANNEL_GROUP_ID)])).max(500),
655
+ })
656
+ .strict()
657
+
658
+ export type ChannelListView = z.output<typeof ChannelListViewSchema>
659
+
476
660
  const SnapshotMessagePartSchema = z.discriminatedUnion('type', [
477
661
  z.object({ type: z.literal('text'), text: z.string() }).strict(),
478
662
  z
@@ -547,11 +731,29 @@ export const HostSnapshotMessageSchema = z
547
731
  })
548
732
  .strict()
549
733
  .optional(),
734
+ /** Extensions that processed the agent's message before it was sent; `followUp` marks a message one appended. */
735
+ outboundProcessing: z
736
+ .object({
737
+ extensions: z.array(z.string()),
738
+ rewritten: z.boolean(),
739
+ followUp: z.literal(true).optional(),
740
+ })
741
+ .strict()
742
+ .optional(),
550
743
  /** A scheduled task came due: a Host fact shown as a notice, not a member message. */
551
744
  scheduledTask: z
552
745
  .object({ label: z.string(), extensionName: z.string().optional(), note: z.string().optional() })
553
746
  .strict()
554
747
  .optional(),
748
+ /** The agent finished waiting for someone to finish speaking: a Host fact shown as a notice. */
749
+ responseWait: z
750
+ .object({
751
+ displayName: z.string().optional(),
752
+ cause: z.enum(['quiet', 'limit']),
753
+ waitedSeconds: z.number().int().nonnegative(),
754
+ })
755
+ .strict()
756
+ .optional(),
555
757
  })
556
758
  .strict()
557
759
 
@@ -638,7 +840,14 @@ export const ChannelFactSseDataSchema = z
638
840
  })
639
841
  .strict()
640
842
 
641
- export const ChannelRuntimePhaseSchema = z.enum(['idle', 'thinking', 'using-tool', 'waiting-input', 'unavailable'])
843
+ export const ChannelRuntimePhaseSchema = z.enum([
844
+ 'idle',
845
+ 'thinking',
846
+ 'using-tool',
847
+ 'compacting',
848
+ 'waiting-input',
849
+ 'unavailable',
850
+ ])
642
851
 
643
852
  export const ChannelRuntimeUsageSchema = z
644
853
  .object({
@@ -659,7 +868,8 @@ const channelRuntimeToolFields = {
659
868
  inputPreview: z.string().optional(),
660
869
  resultPreview: z.string().optional(),
661
870
  wroteToChannel: z.boolean().optional(),
662
- deliveryState: z.enum(['sent', 'partially-sent', 'failed', 'unknown']).optional(),
871
+ /** `cancelled`: an extension stopped the message before it was sent. */
872
+ deliveryState: z.enum(['sent', 'partially-sent', 'failed', 'unknown', 'cancelled']).optional(),
663
873
  durationMs: z.number().int().nonnegative().optional(),
664
874
  }
665
875
 
@@ -868,6 +1078,21 @@ export const ChannelRuntimeProjectionSchema = z
868
1078
  episodeId: EpisodeIdSchema.optional(),
869
1079
  phase: ChannelRuntimePhaseSchema,
870
1080
  summary: z.string(),
1081
+ /** A compaction under way: inside the current session, or before a new session opens. */
1082
+ compaction: z
1083
+ .object({ kind: z.enum(['in-session', 'new-session']), startedAt: z.number().int().nonnegative() })
1084
+ .strict()
1085
+ .optional(),
1086
+ /** The conversation runs on a fallback model; it tries the main model again at a new message after `retryMainAt`. */
1087
+ modelFallback: z
1088
+ .object({
1089
+ model: AgentModelSchema,
1090
+ reason: ModelFallbackReasonSchema,
1091
+ since: z.number().int().nonnegative(),
1092
+ retryMainAt: z.number().int().nonnegative(),
1093
+ })
1094
+ .strict()
1095
+ .optional(),
871
1096
  pendingInjectCount: z.number().int().nonnegative(),
872
1097
  occupancy: ChannelRuntimeOccupancySchema.optional(),
873
1098
  cache: ChannelRuntimeCacheSchema.optional(),
@@ -1038,6 +1263,8 @@ export const SubConversationSchema = z
1038
1263
  /** What it handed back to the channel's conversation when it ended. */
1039
1264
  summary: z.string().max(4000).optional(),
1040
1265
  authoringTaskId: AuthoringTaskIdSchema.optional(),
1266
+ /** The model a model-driven sub-conversation talks in; absent for scripts and ones opened before it was kept. */
1267
+ model: AgentModelSchema.optional(),
1041
1268
  createdAt: z.number().int().nonnegative(),
1042
1269
  closedAt: z.number().int().nonnegative().optional(),
1043
1270
  })
@@ -1270,11 +1497,21 @@ export const AttentionItemSchema = z
1270
1497
  'agent-vision-unavailable',
1271
1498
  'logs-unavailable',
1272
1499
  'community-feedback',
1500
+ 'extension-update',
1273
1501
  ]),
1274
1502
  severity: z.enum(['critical', 'warning', 'info']),
1275
1503
  subject: z
1276
1504
  .object({
1277
- kind: z.enum(['outbound', 'channel', 'authoring-task', 'connection', 'agent', 'logs', 'community-feedback']),
1505
+ kind: z.enum([
1506
+ 'outbound',
1507
+ 'channel',
1508
+ 'authoring-task',
1509
+ 'connection',
1510
+ 'agent',
1511
+ 'logs',
1512
+ 'community-feedback',
1513
+ 'extension-update',
1514
+ ]),
1278
1515
  id: NonEmptyStringSchema,
1279
1516
  })
1280
1517
  .strict(),
@@ -1299,6 +1536,7 @@ export const AttentionItemSchema = z
1299
1536
  'open-agent',
1300
1537
  'open-logs',
1301
1538
  'open-community-feedback',
1539
+ 'open-extension-updates',
1302
1540
  ]),
1303
1541
  label: z.string().min(1).max(40),
1304
1542
  })
@@ -1452,53 +1690,41 @@ export type GithubCliAvailability = z.output<typeof GithubCliAvailabilitySchema>
1452
1690
  export const VerificationBrowserAvailabilitySchema = CodeRunAvailabilitySchema
1453
1691
  export type VerificationBrowserAvailability = z.output<typeof VerificationBrowserAvailabilitySchema>
1454
1692
 
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
1693
+ /** A platform's own program that linked accounts work through, such as the GitHub CLI. */
1694
+ export const LinkedAccountPlatformToolSchema = z
1460
1695
  .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),
1696
+ /** The runtime download routes it is managed by (`downloadGithubCli`, `removeGithubCli`). */
1697
+ id: z.enum(['github-cli']),
1698
+ name: z.string(),
1699
+ availability: GithubCliAvailabilitySchema,
1470
1700
  })
1471
1701
  .strict()
1472
1702
 
1473
- export type CommandAccount = z.output<typeof CommandAccountSchema>
1474
-
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()
1477
-
1478
- export const CommandAccountsViewSchema = z
1703
+ /** 设置 → 关联账号: every platform the host supports, with its accounts and tools. */
1704
+ export const LinkedAccountsViewSchema = z
1479
1705
  .object({
1480
- accounts: z.array(CommandAccountSchema),
1706
+ platforms: z.array(
1707
+ z
1708
+ .object({
1709
+ id: LinkedAccountPlatformIdSchema,
1710
+ name: z.string(),
1711
+ addMethods: z.array(LinkedAccountMethodSchema),
1712
+ tool: LinkedAccountPlatformToolSchema.optional(),
1713
+ accounts: z.array(LinkedAccountViewSchema),
1714
+ })
1715
+ .strict(),
1716
+ ),
1481
1717
  /** Logins found in the local GitHub CLI; empty when it is missing or not logged in. */
1482
- localLogins: z.array(LocalGithubLoginSchema),
1718
+ localGithubLogins: z.array(LocalGithubLoginSchema),
1483
1719
  })
1484
1720
  .strict()
1721
+ export type LinkedAccountsView = z.output<typeof LinkedAccountsViewSchema>
1485
1722
 
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(),
1723
+ export const RemoveLinkedAccountResultSchema = z.discriminatedUnion('removed', [
1724
+ z.object({ removed: z.literal(true), view: LinkedAccountsViewSchema }).strict(),
1725
+ z.object({ removed: z.literal(false), grantedAgentIds: z.array(AgentIdSchema) }).strict(),
1499
1726
  ])
1500
-
1501
- export type AddCommandAccountRequest = z.output<typeof AddCommandAccountRequestSchema>
1727
+ export type RemoveLinkedAccountResult = z.output<typeof RemoveLinkedAccountResultSchema>
1502
1728
 
1503
1729
  /** An extension action an agent asked an administrator to confirm, or one it took on its own. */
1504
1730
  export const ExtensionRequestSchema = z
@@ -1547,6 +1773,14 @@ export const HostSnapshotSchema = z
1547
1773
  name: z.string(),
1548
1774
  description: z.string().optional(),
1549
1775
  inputModalities: z.array(z.string()).optional(),
1776
+ /** Selectable thinking levels, least to most; absent when the model offers no choice. */
1777
+ reasoning: z
1778
+ .object({
1779
+ efforts: z.array(z.object({ id: NonEmptyStringSchema, name: z.string() }).strict()).min(1),
1780
+ defaultEffort: NonEmptyStringSchema.optional(),
1781
+ })
1782
+ .strict()
1783
+ .optional(),
1550
1784
  })
1551
1785
  .strict(),
1552
1786
  ),
@@ -1577,8 +1811,6 @@ export const HostSnapshotSchema = z
1577
1811
  .strict(),
1578
1812
  connectionAdapters: z.array(AdapterConnectionDescriptorSchema),
1579
1813
  notificationSettings: NotificationSettingsViewSchema,
1580
- /** Optional in older hosts, which have no account library. */
1581
- commandAccounts: z.array(CommandAccountSchema).optional(),
1582
1814
  agents: z.array(
1583
1815
  z
1584
1816
  .object({
@@ -1590,6 +1822,19 @@ export const HostSnapshotSchema = z
1590
1822
  capabilities: AgentCapabilitiesSchema,
1591
1823
  imagePolicy: ImageUnderstandingPolicyApiSchema,
1592
1824
  dynamicClientApprovalPolicy: DynamicClientApprovalPolicySchema,
1825
+ /** Optional for older hosts, whose agents always follow both defaults. */
1826
+ compaction: AgentCompactionPolicySchema.optional(),
1827
+ /** Optional for older hosts, whose agents offer no candidates. */
1828
+ subConversationModels: AgentSubConversationModelsSchema.optional(),
1829
+ /** Optional for older hosts, whose agents have no fallback models. */
1830
+ fallbackModels: AgentFallbackModelsSchema.optional(),
1831
+ /** Optional for older hosts, whose agents have no aliases. */
1832
+ aliases: AgentAliasesSchema.optional(),
1833
+ /**
1834
+ * Enabled extensions that process the agent's messages before they are sent, in the order they run. Absent
1835
+ * when none does.
1836
+ */
1837
+ outboundOrder: z.array(ExtensionIdSchema).optional(),
1593
1838
  imageDiagnostics: z
1594
1839
  .object({
1595
1840
  route: z
@@ -1663,6 +1908,8 @@ export const HostSnapshotSchema = z
1663
1908
  /** Absent means `observe`. */
1664
1909
  localAgentMessages: z.enum(['observe', 'trigger']).optional(),
1665
1910
  activityTriggerOverrides: ActivityTriggerOverridesSchema.default({}),
1911
+ /** Absent on older hosts; missing keys use the defaults. */
1912
+ responseTiming: ResponseTimingSettingsSchema.optional(),
1666
1913
  boundAt: z.number().int().safe().nonnegative(),
1667
1914
  })
1668
1915
  .strict(),
@@ -1675,6 +1922,7 @@ export const HostSnapshotSchema = z
1675
1922
  channelIdsByAgent: {},
1676
1923
  unboundChannelIds: [],
1677
1924
  }),
1925
+ channelListView: ChannelListViewSchema.default({ pinnedChannelIds: [], collapsedGroupIds: [] }),
1678
1926
  messages: z.array(HostSnapshotMessageSchema),
1679
1927
  connections: z.array(
1680
1928
  z
@@ -1684,6 +1932,7 @@ export const HostSnapshotSchema = z
1684
1932
  alias: ConnectionAliasOutputSchema.optional(),
1685
1933
  activityTriggerDefaults: z.array(AdapterActivityKeySchema).default([]),
1686
1934
  configuredSecrets: z.array(z.string()).optional(),
1935
+ boundCredentials: BoundCredentialsSchema.optional(),
1687
1936
  status: z
1688
1937
  .object({
1689
1938
  state: z.enum(['stopped', 'connecting', 'connected', 'reconnecting', 'failed']),
@@ -1856,6 +2105,7 @@ export const HostSnapshotSchema = z
1856
2105
  /** Secret fields are removed; `configuredSecrets` lists those the Host stores. */
1857
2106
  config: JsonValueSchema,
1858
2107
  configuredSecrets: z.array(z.string()).optional(),
2108
+ boundCredentials: BoundCredentialsSchema.optional(),
1859
2109
  activatedAt: z.number().int().safe().nonnegative(),
1860
2110
  runtime: z
1861
2111
  .object({
@@ -1891,6 +2141,7 @@ export const HostSnapshotSchema = z
1891
2141
  /** Secret fields are removed; `configuredSecrets` lists those the Host stores. */
1892
2142
  config: JsonValueSchema.default({}),
1893
2143
  configuredSecrets: z.array(z.string()).optional(),
2144
+ boundCredentials: BoundCredentialsSchema.optional(),
1894
2145
  runtime: z
1895
2146
  .object({
1896
2147
  status: z.enum(['active', 'restore-failed', 'dispose-failed']),
@@ -2099,6 +2350,8 @@ export const DshPluginCatalogEntrySchema = z
2099
2350
  loadError: z.object({ code: NonEmptyStringSchema, message: NonEmptyStringSchema }).strict().optional(),
2100
2351
  packageId: DshPluginPackageIdSchema.optional(),
2101
2352
  installSource: z.enum(['registry', 'tarball', 'imported']).optional(),
2353
+ /** Published by the DSH maintainers on npm for the DSH release this build runs. */
2354
+ official: z.boolean().optional(),
2102
2355
  installedAt: z.number().int().safe().nonnegative().optional(),
2103
2356
  clientUiDetected: z.boolean().optional(),
2104
2357
  hostUi: DshNxtHostUiSchema.optional(),
@@ -2113,6 +2366,8 @@ export const DshPluginCatalogEntrySchema = z
2113
2366
  suggestedScope: z.enum(['host', 'agent']),
2114
2367
  selectedScope: z.enum(['host', 'agent']).optional(),
2115
2368
  config: JsonValueSchema,
2369
+ /** Host services this official entry declared and received; known once it has loaded. */
2370
+ grantedServices: z.array(NonEmptyStringSchema).optional(),
2116
2371
  activations: z.array(
2117
2372
  z
2118
2373
  .object({
@@ -2122,9 +2377,13 @@ export const DshPluginCatalogEntrySchema = z
2122
2377
  activatedAt: z.number().int().safe().nonnegative(),
2123
2378
  diagnostic: z
2124
2379
  .object({
2125
- status: z.enum(['active', 'load-failed', 'restore-failed', 'dispose-failed']),
2126
- phase: z.enum(['import', 'apply', 'update', 'dispose', 'restore']),
2380
+ status: z.enum(['active', 'pending', 'load-failed', 'restore-failed', 'dispose-failed']),
2381
+ phase: z.enum(['import', 'dependency', 'apply', 'update', 'dispose', 'restore']),
2127
2382
  message: z.string().optional(),
2383
+ /** Services a pending entry still waits for. */
2384
+ waitingFor: z.array(NonEmptyStringSchema).optional(),
2385
+ /** The waited-for services this entry can never receive, since only official plugins get them. */
2386
+ restrictedServices: z.array(NonEmptyStringSchema).optional(),
2128
2387
  observedAt: z.number().int().safe().nonnegative(),
2129
2388
  })
2130
2389
  .optional(),
@@ -2239,6 +2498,9 @@ export type HostSseEvent = z.output<typeof HostSseEventSchema>
2239
2498
  /** Request content a model accepts; `image` enables native vision for agents using the model. */
2240
2499
  export const LlmModelInputModalitiesSchema = z.array(z.enum(['text', 'image'])).min(1)
2241
2500
 
2501
+ /** Thinking levels a model offers, least to most; the ids are the adapter's own. */
2502
+ export const LlmModelReasoningEffortsSchema = z.array(NonEmptyStringSchema).min(1)
2503
+
2242
2504
  export const LlmProviderModelSchema = z
2243
2505
  .object({
2244
2506
  id: NonEmptyStringSchema,
@@ -2246,6 +2508,7 @@ export const LlmProviderModelSchema = z
2246
2508
  contextWindow: z.number().int().positive().optional(),
2247
2509
  maxTokens: z.number().int().positive().optional(),
2248
2510
  inputModalities: LlmModelInputModalitiesSchema.optional(),
2511
+ reasoningEfforts: LlmModelReasoningEffortsSchema.optional(),
2249
2512
  })
2250
2513
  .strict()
2251
2514
 
@@ -2256,6 +2519,7 @@ export const LlmDiscoveredModelSchema = z
2256
2519
  contextWindow: z.number().int().positive().optional(),
2257
2520
  maxTokens: z.number().int().positive().optional(),
2258
2521
  inputModalities: LlmModelInputModalitiesSchema.optional(),
2522
+ reasoningEfforts: LlmModelReasoningEffortsSchema.optional(),
2259
2523
  })
2260
2524
  .strict()
2261
2525
 
@@ -2299,6 +2563,60 @@ export const LlmProviderViewSchema = z
2299
2563
  discoverable: z.boolean(),
2300
2564
  /** `null` when the current configuration was never tested; absent from hosts that do not record tests. */
2301
2565
  lastTest: LlmProviderTestResultSchema.nullable().optional(),
2566
+ /** Present when the provider can sign in with a subscription account instead of an API key. */
2567
+ subscription: z
2568
+ .object({
2569
+ label: NonEmptyStringSchema,
2570
+ /** The route uses the signed-in account rather than an API key. */
2571
+ active: z.boolean(),
2572
+ accountLabel: z.string().nullable(),
2573
+ })
2574
+ .strict()
2575
+ .optional(),
2576
+ })
2577
+ .strict()
2578
+
2579
+ export const SubscriptionStatusSchema = z
2580
+ .object({
2581
+ provider: NonEmptyStringSchema,
2582
+ label: NonEmptyStringSchema,
2583
+ usageUrl: z.url(),
2584
+ available: z.boolean(),
2585
+ mode: z.enum(['unconfigured', 'api-key', 'subscription']),
2586
+ activeAccountId: z.string().nullable(),
2587
+ accounts: z.array(
2588
+ z
2589
+ .object({
2590
+ id: NonEmptyStringSchema,
2591
+ label: NonEmptyStringSchema,
2592
+ email: z.string().nullable(),
2593
+ createdAt: z.number().int().nonnegative(),
2594
+ })
2595
+ .strict(),
2596
+ ),
2597
+ login: z
2598
+ .object({
2599
+ id: NonEmptyStringSchema,
2600
+ status: z.enum(['running', 'authorized', 'cancelled', 'failed']),
2601
+ notices: z.array(
2602
+ z.object({ message: z.string(), url: z.string().optional(), code: z.string().optional() }).strict(),
2603
+ ),
2604
+ prompt: z
2605
+ .object({
2606
+ id: NonEmptyStringSchema,
2607
+ kind: z.enum(['text', 'secret', 'select']),
2608
+ message: z.string(),
2609
+ placeholder: z.string().optional(),
2610
+ options: z
2611
+ .array(z.object({ id: z.string(), label: z.string(), description: z.string().optional() }).strict())
2612
+ .optional(),
2613
+ })
2614
+ .strict()
2615
+ .nullable(),
2616
+ error: z.string().nullable(),
2617
+ })
2618
+ .strict()
2619
+ .nullable(),
2302
2620
  })
2303
2621
  .strict()
2304
2622
 
@@ -2403,6 +2721,8 @@ const defineContract = <
2403
2721
  })
2404
2722
 
2405
2723
  const agentParam = z.object({ agentId: AgentIdSchema }).strict()
2724
+ const credentialParam = z.object({ credentialId: CredentialIdSchema }).strict()
2725
+ const linkedAccountParam = z.object({ accountId: LinkedAccountIdSchema }).strict()
2406
2726
  const agentRevisionParam = z.object({ agentId: AgentIdSchema, revisionId: AgentRevisionIdSchema }).strict()
2407
2727
 
2408
2728
  export const AgentRevisionChangedFieldSchema = z.enum([
@@ -2412,6 +2732,10 @@ export const AgentRevisionChangedFieldSchema = z.enum([
2412
2732
  'capabilities',
2413
2733
  'imagePolicy',
2414
2734
  'approvalPolicy',
2735
+ 'compaction',
2736
+ 'subConversationModels',
2737
+ 'fallbackModels',
2738
+ 'aliases',
2415
2739
  ])
2416
2740
 
2417
2741
  export const AgentRevisionHistorySchema = z
@@ -2577,6 +2901,41 @@ export const AdministratorSchema = z
2577
2901
  .strict()
2578
2902
 
2579
2903
  const extensionParam = z.object({ extensionId: ExtensionIdSchema }).strict()
2904
+
2905
+ export const ExtensionConfigVersionSourceSchema = z.enum(['console', 'agent', 'chat-admin', 'revert', 'auto-revert'])
2906
+ export type ExtensionConfigVersionSource = z.output<typeof ExtensionConfigVersionSourceSchema>
2907
+
2908
+ /**
2909
+ * One saved state of an extension's host configuration or of one agent's configuration. Versions are numbered per
2910
+ * extension, layer and agent; reverting writes an old one again as a new version. Secret fields only show whether
2911
+ * they were set and which named credential they were bound to.
2912
+ */
2913
+ export const ExtensionConfigVersionSchema = z
2914
+ .object({
2915
+ id: z.number().int().positive(),
2916
+ extensionId: ExtensionIdSchema,
2917
+ layer: z.enum(['host', 'agent']),
2918
+ agentId: AgentIdSchema.nullable(),
2919
+ version: z.number().int().positive(),
2920
+ config: JsonValueSchema,
2921
+ configuredSecrets: z.array(z.string()).optional(),
2922
+ boundCredentials: z.record(z.string(), CredentialIdSchema).optional(),
2923
+ source: ExtensionConfigVersionSourceSchema,
2924
+ actorAgentId: AgentIdSchema.nullable(),
2925
+ requesterMemberId: z.string().nullable(),
2926
+ reason: z.string().nullable(),
2927
+ createdAt: z.number().int().nonnegative(),
2928
+ })
2929
+ .strict()
2930
+ export type ExtensionConfigVersion = z.output<typeof ExtensionConfigVersionSchema>
2931
+
2932
+ const extensionConfigVersionParam = z
2933
+ .object({
2934
+ extensionId: ExtensionIdSchema,
2935
+ /** Decimal text of the version id; the path builder only takes strings. */
2936
+ versionId: z.string().regex(/^[1-9]\d{0,15}$/u),
2937
+ })
2938
+ .strict()
2580
2939
  const dshPluginPackageParam = z.object({ packageId: DshPluginPackageIdSchema }).strict()
2581
2940
  const dshPluginEntryParam = z.object({ entryId: DshPluginEntryIdSchema }).strict()
2582
2941
  const extensionRevisionParam = z
@@ -2830,11 +3189,20 @@ export const HostApiContracts = {
2830
3189
  .object({
2831
3190
  task: AuthoringTaskSummarySchema,
2832
3191
  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
3192
+ /**
3193
+ * Test configuration of this task's dynamic runs: fields of the latest candidate the user can type, which
3194
+ * credentials are set, the user's values and the values the agent provided.
3195
+ */
3196
+ testConfig: z
2835
3197
  .object({
2836
- fields: z.array(z.object({ key: z.string(), title: z.string() }).strict()),
3198
+ fields: z.array(
3199
+ z.object({ key: z.string(), title: z.string(), kind: z.enum(['secret', 'string', 'number']) }).strict(),
3200
+ ),
2837
3201
  configured: z.array(z.string()),
3202
+ values: z.record(z.string(), z.union([z.string(), z.number()])),
3203
+ agentValues: z.record(z.string(), JsonValueSchema),
3204
+ /** Secret fields bound to a named credential, by the user or else by the agent. */
3205
+ boundCredentials: BoundCredentialsSchema.optional(),
2838
3206
  })
2839
3207
  .strict()
2840
3208
  .optional(),
@@ -2884,12 +3252,26 @@ export const HostApiContracts = {
2884
3252
  response: AuthoringTaskSummarySchema,
2885
3253
  error: HostApiErrorSchema,
2886
3254
  }),
2887
- setAuthoringTestSecrets: defineContract({
3255
+ setAuthoringTestConfig: defineContract({
2888
3256
  method: 'PUT',
2889
3257
  path: '/api/authoring/tasks/:taskId/test-secrets',
2890
3258
  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(),
3259
+ request: z
3260
+ .object({
3261
+ secrets: z.record(z.string(), z.string().max(8192)),
3262
+ /** Text and number values; an empty string goes back to the agent's value or the default. */
3263
+ values: z.record(z.string(), z.union([z.string().max(8192), z.number()])).optional(),
3264
+ /** Secret fields to bind to named credentials instead of a typed value; `null` leaves the field unset. */
3265
+ credentialBindings: CredentialBindingsRequestSchema.optional(),
3266
+ })
3267
+ .strict(),
3268
+ response: z
3269
+ .object({
3270
+ configured: z.array(z.string()),
3271
+ values: z.record(z.string(), z.union([z.string(), z.number()])),
3272
+ boundCredentials: BoundCredentialsSchema.optional(),
3273
+ })
3274
+ .strict(),
2893
3275
  error: HostApiErrorSchema,
2894
3276
  }),
2895
3277
  deleteAuthoringTask: defineContract({
@@ -3087,6 +3469,8 @@ export const HostApiContracts = {
3087
3469
  /** Messages from another local agent's account: `observe` keeps them as context unless they @ this agent. */
3088
3470
  localAgentMessages: z.enum(['observe', 'trigger']).optional(),
3089
3471
  activityTriggerOverrides: ActivityTriggerOverridesSchema.optional(),
3472
+ /** Omitted keeps the binding's current timing; `{}` resets every field to its default. */
3473
+ responseTiming: ResponseTimingSettingsSchema.optional(),
3090
3474
  })
3091
3475
  .strict(),
3092
3476
  response: z
@@ -3098,6 +3482,7 @@ export const HostApiContracts = {
3098
3482
  processingFeedback: z.enum(['auto', 'off']).default('auto'),
3099
3483
  localAgentMessages: z.enum(['observe', 'trigger']).optional(),
3100
3484
  activityTriggerOverrides: ActivityTriggerOverridesSchema.default({}),
3485
+ responseTiming: ResponseTimingSettingsSchema.optional(),
3101
3486
  boundAt: z.number().int().safe().nonnegative(),
3102
3487
  })
3103
3488
  .strict(),
@@ -3121,6 +3506,23 @@ export const HostApiContracts = {
3121
3506
  response: WorkTreeOrderSchema,
3122
3507
  error: HostApiErrorSchema,
3123
3508
  }),
3509
+ getChannelListView: defineContract({
3510
+ method: 'GET',
3511
+ path: '/api/channel-list-view',
3512
+ params: EmptyParamsSchema,
3513
+ request: NoRequestBodySchema,
3514
+ response: ChannelListViewSchema,
3515
+ error: HostApiErrorSchema,
3516
+ }),
3517
+ updateChannelListView: defineContract({
3518
+ invalidatesSnapshot: true,
3519
+ method: 'PUT',
3520
+ path: '/api/channel-list-view',
3521
+ params: EmptyParamsSchema,
3522
+ request: ChannelListViewSchema,
3523
+ response: ChannelListViewSchema,
3524
+ error: HostApiErrorSchema,
3525
+ }),
3124
3526
  listChannelMessages: defineContract({
3125
3527
  method: 'GET',
3126
3528
  path: '/api/channels/:channelId/messages',
@@ -3369,6 +3771,39 @@ export const HostApiContracts = {
3369
3771
  response: MessagePacingSettingsSchema,
3370
3772
  error: HostApiErrorSchema,
3371
3773
  }),
3774
+ getCompactionSettings: defineContract({
3775
+ method: 'GET',
3776
+ path: '/api/settings/compaction',
3777
+ params: EmptyParamsSchema,
3778
+ request: NoRequestBodySchema,
3779
+ response: CompactionSettingsSchema,
3780
+ error: HostApiErrorSchema,
3781
+ }),
3782
+ updateCompactionSettings: defineContract({
3783
+ invalidatesSnapshot: true,
3784
+ method: 'PUT',
3785
+ path: '/api/settings/compaction',
3786
+ params: EmptyParamsSchema,
3787
+ request: CompactionSettingsSchema,
3788
+ response: CompactionSettingsSchema,
3789
+ error: HostApiErrorSchema,
3790
+ }),
3791
+ getModelFallbackSettings: defineContract({
3792
+ method: 'GET',
3793
+ path: '/api/settings/model-fallback',
3794
+ params: EmptyParamsSchema,
3795
+ request: NoRequestBodySchema,
3796
+ response: ModelFallbackSettingsSchema,
3797
+ error: HostApiErrorSchema,
3798
+ }),
3799
+ updateModelFallbackSettings: defineContract({
3800
+ method: 'PUT',
3801
+ path: '/api/settings/model-fallback',
3802
+ params: EmptyParamsSchema,
3803
+ request: ModelFallbackSettingsSchema,
3804
+ response: ModelFallbackSettingsSchema,
3805
+ error: HostApiErrorSchema,
3806
+ }),
3372
3807
  getUpgradeBackupSettings: defineContract({
3373
3808
  method: 'GET',
3374
3809
  path: '/api/settings/upgrade-backup',
@@ -3473,39 +3908,116 @@ export const HostApiContracts = {
3473
3908
  response: VerificationBrowserAvailabilitySchema,
3474
3909
  error: HostApiErrorSchema,
3475
3910
  }),
3476
- getCommandAccounts: defineContract({
3911
+ getLinkedAccounts: defineContract({
3477
3912
  method: 'GET',
3478
- path: '/api/settings/command-accounts',
3913
+ path: '/api/settings/linked-accounts',
3479
3914
  params: EmptyParamsSchema,
3480
3915
  request: NoRequestBodySchema,
3481
- response: CommandAccountsViewSchema,
3916
+ response: LinkedAccountsViewSchema,
3482
3917
  error: HostApiErrorSchema,
3483
3918
  }),
3484
- addCommandAccount: defineContract({
3919
+ addLinkedAccount: defineContract({
3485
3920
  invalidatesSnapshot: true,
3486
3921
  method: 'POST',
3487
- path: '/api/settings/command-accounts',
3922
+ path: '/api/settings/linked-accounts',
3488
3923
  params: EmptyParamsSchema,
3489
- request: AddCommandAccountRequestSchema,
3490
- response: CommandAccountsViewSchema,
3924
+ request: AddLinkedAccountRequestSchema,
3925
+ response: LinkedAccountsViewSchema,
3491
3926
  error: HostApiErrorSchema,
3492
3927
  }),
3493
- replaceCommandAccountToken: defineContract({
3928
+ replaceLinkedAccountToken: defineContract({
3494
3929
  invalidatesSnapshot: true,
3495
3930
  method: 'PUT',
3496
- path: '/api/settings/command-accounts/:accountId',
3497
- params: z.object({ accountId: NonEmptyStringSchema.max(64) }).strict(),
3498
- request: AddCommandAccountRequestSchema,
3499
- response: CommandAccountsViewSchema,
3931
+ path: '/api/settings/linked-accounts/:accountId/token',
3932
+ params: linkedAccountParam,
3933
+ request: ReplaceLinkedAccountTokenRequestSchema,
3934
+ response: LinkedAccountsViewSchema,
3500
3935
  error: HostApiErrorSchema,
3501
3936
  }),
3502
- removeCommandAccount: defineContract({
3937
+ removeLinkedAccount: defineContract({
3503
3938
  invalidatesSnapshot: true,
3939
+ method: 'POST',
3940
+ path: '/api/settings/linked-accounts/:accountId/remove',
3941
+ params: linkedAccountParam,
3942
+ request: RemoveLinkedAccountRequestSchema,
3943
+ response: RemoveLinkedAccountResultSchema,
3944
+ error: HostApiErrorSchema,
3945
+ }),
3946
+ getLinkedAccountUses: defineContract({
3947
+ method: 'GET',
3948
+ path: '/api/settings/linked-accounts/:accountId/uses',
3949
+ params: linkedAccountParam,
3950
+ request: NoRequestBodySchema,
3951
+ response: LinkedAccountUsesViewSchema,
3952
+ error: HostApiErrorSchema,
3953
+ }),
3954
+ getCredentials: defineContract({
3955
+ method: 'GET',
3956
+ path: '/api/settings/credentials',
3957
+ params: EmptyParamsSchema,
3958
+ request: NoRequestBodySchema,
3959
+ response: CredentialsViewSchema,
3960
+ error: HostApiErrorSchema,
3961
+ }),
3962
+ createCredential: defineContract({
3963
+ invalidatesSnapshot: true,
3964
+ method: 'POST',
3965
+ path: '/api/settings/credentials',
3966
+ params: EmptyParamsSchema,
3967
+ request: CreateCredentialRequestSchema,
3968
+ response: CredentialsViewSchema,
3969
+ error: HostApiErrorSchema,
3970
+ }),
3971
+ updateCredential: defineContract({
3972
+ invalidatesSnapshot: true,
3973
+ method: 'PATCH',
3974
+ path: '/api/settings/credentials/:credentialId',
3975
+ params: credentialParam,
3976
+ request: UpdateCredentialRequestSchema,
3977
+ response: CredentialsViewSchema,
3978
+ error: HostApiErrorSchema,
3979
+ }),
3980
+ replaceCredentialValue: defineContract({
3981
+ invalidatesSnapshot: true,
3982
+ method: 'PUT',
3983
+ path: '/api/settings/credentials/:credentialId/value',
3984
+ params: credentialParam,
3985
+ request: ReplaceCredentialValueRequestSchema,
3986
+ response: CredentialsViewSchema,
3987
+ error: HostApiErrorSchema,
3988
+ }),
3989
+ removeCredential: defineContract({
3990
+ invalidatesSnapshot: true,
3991
+ method: 'POST',
3992
+ path: '/api/settings/credentials/:credentialId/remove',
3993
+ params: credentialParam,
3994
+ request: RemoveCredentialRequestSchema,
3995
+ response: RemoveCredentialResultSchema,
3996
+ error: HostApiErrorSchema,
3997
+ }),
3998
+ getCredentialUses: defineContract({
3999
+ method: 'GET',
4000
+ path: '/api/settings/credentials/:credentialId/uses',
4001
+ params: credentialParam,
4002
+ request: NoRequestBodySchema,
4003
+ response: CredentialUsesViewSchema,
4004
+ error: HostApiErrorSchema,
4005
+ }),
4006
+ dismissCredentialRequest: defineContract({
3504
4007
  method: 'DELETE',
3505
- path: '/api/settings/command-accounts/:accountId',
3506
- params: z.object({ accountId: NonEmptyStringSchema.max(64) }).strict(),
4008
+ path: '/api/settings/credentials/requests/:requestId',
4009
+ params: z.object({ requestId: NonEmptyStringSchema.max(64) }).strict(),
3507
4010
  request: NoRequestBodySchema,
3508
- response: CommandAccountsViewSchema,
4011
+ response: CredentialsViewSchema,
4012
+ error: HostApiErrorSchema,
4013
+ }),
4014
+ setModelProviderAccess: defineContract({
4015
+ invalidatesSnapshot: true,
4016
+ method: 'PUT',
4017
+ path: '/api/settings/credentials/model-providers/:provider',
4018
+ params: z.object({ provider: NonEmptyStringSchema.max(128) }).strict(),
4019
+ request: SetModelProviderAccessRequestSchema,
4020
+ response: CredentialsViewSchema,
3509
4021
  error: HostApiErrorSchema,
3510
4022
  }),
3511
4023
  getChannelMemberNotes: defineContract({
@@ -4000,19 +4512,24 @@ export const HostApiContracts = {
4000
4512
  /** 排序方式,默认最近更新;游标只在同一种排序下有效。 */
4001
4513
  sort: CommunityExtensionSortSchema.optional(),
4002
4514
  cursor: z.string().max(200).optional(),
4515
+ /** 跳过本机缓存,重新向社区读取。 */
4516
+ refresh: z.enum(['1']).optional(),
4003
4517
  })
4004
4518
  .strict(),
4005
4519
  request: NoRequestBodySchema,
4006
- response: z.object({ items: z.array(CommunityExtensionSummarySchema), nextCursor: z.string().nullable() }).strict(),
4520
+ response: z
4521
+ .object({ items: z.array(CommunityExtensionSummarySchema), nextCursor: z.string().nullable() })
4522
+ .extend(CommunityFreshnessSchema.shape)
4523
+ .strict(),
4007
4524
  error: HostApiErrorSchema,
4008
4525
  }),
4009
4526
  getCommunityExtension: defineContract({
4010
4527
  timeoutMs: 30_000,
4011
4528
  method: 'GET',
4012
4529
  path: '/api/community/extensions/:extensionId',
4013
- params: z.object({ extensionId: ExtensionIdSchema }).strict(),
4530
+ params: z.object({ extensionId: ExtensionIdSchema, refresh: z.enum(['1']).optional() }).strict(),
4014
4531
  request: NoRequestBodySchema,
4015
- response: CommunityExtensionDetailSchema,
4532
+ response: CommunityExtensionDetailSchema.extend(CommunityFreshnessSchema.shape),
4016
4533
  error: HostApiErrorSchema,
4017
4534
  }),
4018
4535
  /** 下载社区发布并按包摘要校验,结果与本地文件导入的检查相同,再经 `commitExtensionImport` 确认。 */
@@ -4139,19 +4656,24 @@ export const HostApiContracts = {
4139
4656
  tag: z.string().trim().min(1).max(40).optional(),
4140
4657
  official: z.literal('1').optional(),
4141
4658
  cursor: z.string().min(1).max(500).optional(),
4659
+ /** 跳过本机缓存,重新向社区读取。 */
4660
+ refresh: z.enum(['1']).optional(),
4142
4661
  })
4143
4662
  .strict(),
4144
4663
  request: NoRequestBodySchema,
4145
- response: z.object({ items: z.array(CommunityPersonaSummarySchema), nextCursor: z.string().nullable() }).strict(),
4664
+ response: z
4665
+ .object({ items: z.array(CommunityPersonaSummarySchema), nextCursor: z.string().nullable() })
4666
+ .extend(CommunityFreshnessSchema.shape)
4667
+ .strict(),
4146
4668
  error: HostApiErrorSchema,
4147
4669
  }),
4148
4670
  getCommunityPersona: defineContract({
4149
4671
  timeoutMs: 30_000,
4150
4672
  method: 'GET',
4151
4673
  path: '/api/community/personas/:personaId',
4152
- params: z.object({ personaId: CommunityPersonaIdSchema }).strict(),
4674
+ params: z.object({ personaId: CommunityPersonaIdSchema, refresh: z.enum(['1']).optional() }).strict(),
4153
4675
  request: NoRequestBodySchema,
4154
- response: CommunityPersonaDetailSchema,
4676
+ response: CommunityPersonaDetailSchema.extend(CommunityFreshnessSchema.shape),
4155
4677
  error: HostApiErrorSchema,
4156
4678
  }),
4157
4679
  /**
@@ -4346,6 +4868,31 @@ export const HostApiContracts = {
4346
4868
  response: LlmProviderSettingsSchema,
4347
4869
  error: HostApiErrorSchema,
4348
4870
  }),
4871
+ llmSubscriptionStatus: defineContract({
4872
+ method: 'GET',
4873
+ path: '/api/llm/subscriptions/:provider',
4874
+ params: llmProviderParam,
4875
+ request: NoRequestBodySchema,
4876
+ response: SubscriptionStatusSchema,
4877
+ error: HostApiErrorSchema,
4878
+ }),
4879
+ llmSubscriptionAction: defineContract({
4880
+ invalidatesSnapshot: true,
4881
+ method: 'POST',
4882
+ path: '/api/llm/subscriptions/:provider',
4883
+ params: llmProviderParam,
4884
+ request: z.discriminatedUnion('action', [
4885
+ z.object({ action: z.literal('use-subscription'), expectedRevision: z.number().int().nonnegative() }).strict(),
4886
+ z.object({ action: z.enum(['login', 'cancel']) }).strict(),
4887
+ z.object({ action: z.literal('answer'), promptId: NonEmptyStringSchema, value: z.string().max(4096) }).strict(),
4888
+ z.object({ action: z.enum(['activate', 'remove']), accountId: NonEmptyStringSchema }).strict(),
4889
+ z
4890
+ .object({ action: z.literal('rename'), accountId: NonEmptyStringSchema, label: z.string().min(1).max(80) })
4891
+ .strict(),
4892
+ ]),
4893
+ response: z.object({ status: SubscriptionStatusSchema, revocationConfirmed: z.boolean().optional() }).strict(),
4894
+ error: HostApiErrorSchema,
4895
+ }),
4349
4896
  llmDiscoverModels: defineContract({
4350
4897
  method: 'POST',
4351
4898
  path: '/api/llm/discover-models',
@@ -4755,6 +5302,7 @@ export const HostApiContracts = {
4755
5302
  extensionRevisionId: ExtensionRevisionIdSchema,
4756
5303
  config: JsonValueSchema,
4757
5304
  configuredSecrets: z.array(z.string()).optional(),
5305
+ boundCredentials: BoundCredentialsSchema.optional(),
4758
5306
  activatedAt: z.number().int().safe().nonnegative(),
4759
5307
  })
4760
5308
  .strict()
@@ -4775,6 +5323,16 @@ export const HostApiContracts = {
4775
5323
  response: z.object({ disabled: z.literal(true), pending: ExtensionActivationTransitionSchema.optional() }).strict(),
4776
5324
  error: HostApiErrorSchema,
4777
5325
  }),
5326
+ putAgentOutboundOrder: defineContract({
5327
+ invalidatesSnapshot: true,
5328
+ method: 'PUT',
5329
+ path: '/api/agents/:agentId/outbound-order',
5330
+ params: agentParam,
5331
+ /** Every enabled extension that processes the agent's messages before sending, in the new order. */
5332
+ request: z.object({ extensionIds: z.array(ExtensionIdSchema).max(64) }).strict(),
5333
+ response: z.object({ extensionIds: z.array(ExtensionIdSchema) }).strict(),
5334
+ error: HostApiErrorSchema,
5335
+ }),
4778
5336
  updateExtensionActivationConfig: defineContract({
4779
5337
  invalidatesSnapshot: true,
4780
5338
  method: 'PUT',
@@ -4785,9 +5343,17 @@ export const HostApiContracts = {
4785
5343
  config: JsonValueSchema,
4786
5344
  /** Write-only credential drafts keyed by secret field; an absent or empty draft keeps the stored one. */
4787
5345
  secrets: z.record(z.string(), z.string().max(8192)).optional(),
5346
+ /** Secret fields to bind to named credentials instead of a typed value; a field takes one or the other. */
5347
+ credentialBindings: CredentialBindingsRequestSchema.optional(),
5348
+ })
5349
+ .strict(),
5350
+ response: z
5351
+ .object({
5352
+ config: JsonValueSchema,
5353
+ configuredSecrets: z.array(z.string()).optional(),
5354
+ boundCredentials: BoundCredentialsSchema.optional(),
4788
5355
  })
4789
5356
  .strict(),
4790
- response: z.object({ config: JsonValueSchema, configuredSecrets: z.array(z.string()).optional() }).strict(),
4791
5357
  error: HostApiErrorSchema,
4792
5358
  }),
4793
5359
  updateHostExtensionConfig: defineContract({
@@ -4801,9 +5367,37 @@ export const HostApiContracts = {
4801
5367
  config: JsonValueSchema,
4802
5368
  /** Write-only credential drafts keyed by secret field; an absent or empty draft keeps the stored one. */
4803
5369
  secrets: z.record(z.string(), z.string().max(8192)).optional(),
5370
+ /** Secret fields to bind to named credentials instead of a typed value; a field takes one or the other. */
5371
+ credentialBindings: CredentialBindingsRequestSchema.optional(),
4804
5372
  })
4805
5373
  .strict(),
4806
- response: z.object({ config: JsonValueSchema, configuredSecrets: z.array(z.string()).optional() }).strict(),
5374
+ response: z
5375
+ .object({
5376
+ config: JsonValueSchema,
5377
+ configuredSecrets: z.array(z.string()).optional(),
5378
+ boundCredentials: BoundCredentialsSchema.optional(),
5379
+ })
5380
+ .strict(),
5381
+ error: HostApiErrorSchema,
5382
+ }),
5383
+ /** Configuration history of one extension, newest first: the host layer and every agent's configuration. */
5384
+ getExtensionConfigVersions: defineContract({
5385
+ method: 'GET',
5386
+ path: '/api/extensions/:extensionId/config-versions',
5387
+ params: extensionParam,
5388
+ request: NoRequestBodySchema,
5389
+ response: z.object({ versions: z.array(ExtensionConfigVersionSchema) }).strict(),
5390
+ error: HostApiErrorSchema,
5391
+ }),
5392
+ /** Writes an earlier configuration again as a new version of the same layer (and agent). */
5393
+ revertExtensionConfig: defineContract({
5394
+ invalidatesSnapshot: true,
5395
+ timeoutMs: 300_000,
5396
+ method: 'POST',
5397
+ path: '/api/extensions/:extensionId/config-versions/:versionId/revert',
5398
+ params: extensionConfigVersionParam,
5399
+ request: z.object({ reason: z.string().trim().max(500).optional() }).strict(),
5400
+ response: z.object({ version: ExtensionConfigVersionSchema }).strict(),
4807
5401
  error: HostApiErrorSchema,
4808
5402
  }),
4809
5403
  installHostExtension: defineContract({
@@ -5243,9 +5837,11 @@ const buildValidatedHostApiPath = (
5243
5837
  const consumed = new Set<string>()
5244
5838
  const pathname = contract.path.replace(pathParameterPattern, (_placeholder, key: string) => {
5245
5839
  const value = params[key]
5246
- if (typeof value !== 'string') throw new TypeError(`Host API path parameter ${key} must be a string.`)
5840
+ if (typeof value !== 'string' && typeof value !== 'number') {
5841
+ throw new TypeError(`Host API path parameter ${key} must be a string or number.`)
5842
+ }
5247
5843
  consumed.add(key)
5248
- return encodeURIComponent(value)
5844
+ return encodeURIComponent(String(value))
5249
5845
  })
5250
5846
  const query = new URLSearchParams()
5251
5847
  for (const [key, value] of Object.entries(params)) {