@nekro-nxt/contracts 0.2.0 → 0.4.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
@@ -33,6 +33,8 @@ import { ConfigSchemaDocumentSchema } from './config-schema.js'
33
33
  import {
34
34
  CommunityExtensionDetailSchema,
35
35
  CommunityExtensionSummarySchema,
36
+ CommunityExtensionSortSchema,
37
+ CommunityListingInputSchema,
36
38
  CommunityReleaseIdSchema,
37
39
  CommunityReviewStatusSchema,
38
40
  CommunityStatusSchema,
@@ -42,6 +44,16 @@ import {
42
44
  CommunityMyExtensionSchema,
43
45
  CommunityReviewReportSchema,
44
46
  } from './community.js'
47
+ import {
48
+ COMMUNITY_PERSONA_AVATAR_TYPES,
49
+ COMMUNITY_PERSONA_MAX_LENGTH,
50
+ CommunityMyPersonaSchema,
51
+ CommunityMyPersonasSchema,
52
+ CommunityPersonaDetailSchema,
53
+ CommunityPersonaIdSchema,
54
+ CommunityPersonaRevisionIdSchema,
55
+ CommunityPersonaSummarySchema,
56
+ } from './community-personas.js'
45
57
  import {
46
58
  DshNxtHostUiSchema,
47
59
  EMPTY_EXTENSION_UI_CONTRIBUTIONS,
@@ -54,6 +66,9 @@ import {
54
66
  HostUiPageGeometryEvidenceSchema,
55
67
  HostUiPageEntrySchema,
56
68
  HostUiPermissionDeclarationSchema,
69
+ ExtensionPanelAnchorSchema,
70
+ ExtensionPermissionsSchema,
71
+ ExtensionProvideSchema,
57
72
  } from './extension-ui.js'
58
73
 
59
74
  const EmptyParamsSchema = z.object({}).strict()
@@ -214,6 +229,32 @@ const AgentModelSchema = z
214
229
  })
215
230
  .strict()
216
231
 
232
+ /**
233
+ * An enable or disable request of one agent extension that has not settled: `waiting` until the agent reaches a safe
234
+ * gap (its current reply ends), or `failed` with the reason until the next request replaces it.
235
+ */
236
+ /** What one approval asks the user to grant and whether the current grant already covers it. */
237
+ export const ExtensionPermissionRequirementSchema = z
238
+ .object({
239
+ declaration: HostUiPermissionDeclarationSchema,
240
+ permissionDigest: z.string().regex(/^[a-f0-9]{64}$/u),
241
+ approvalRequired: z.boolean(),
242
+ })
243
+ .strict()
244
+
245
+ export type ExtensionPermissionRequirement = z.output<typeof ExtensionPermissionRequirementSchema>
246
+
247
+ export const ExtensionActivationTransitionSchema = z
248
+ .object({
249
+ agentId: AgentIdSchema,
250
+ target: z.enum(['enabled', 'disabled']),
251
+ extensionRevisionId: ExtensionRevisionIdSchema.optional(),
252
+ state: z.enum(['waiting', 'failed']),
253
+ message: z.string().optional(),
254
+ since: z.number().int().safe().nonnegative(),
255
+ })
256
+ .strict()
257
+
217
258
  export const ImageUnderstandingPolicyApiSchema = z
218
259
  .object({
219
260
  history: z
@@ -251,6 +292,10 @@ const AgentCapabilitiesSchema = z
251
292
  unrestrictedFileAccess: z.boolean(),
252
293
  /** Absent in older clients means on. */
253
294
  scheduledTasks: z.boolean().default(true),
295
+ /** Tools are called from one TypeScript program (`run_code`); takes effect only with `developmentShell`. */
296
+ codeRun: z.boolean().default(false),
297
+ /** Network for commands; absent in older clients means on. */
298
+ commandNetwork: z.boolean().default(true),
254
299
  })
255
300
  .strict()
256
301
 
@@ -345,9 +390,14 @@ const ReviseAgentRequestSchema = AgentRevisionRequestContentSchema.extend({
345
390
  validatePersonaDocumentProjection(value, context)
346
391
  })
347
392
 
348
- // The default that fills `scheduledTasks` for older clients must not turn it on in a partial update.
393
+ // The defaults that fill `scheduledTasks`, `codeRun` and `commandNetwork` for older clients must not reset them in a
394
+ // partial update.
349
395
  const UpdateAgentCapabilitiesRequestSchema = AgentCapabilitiesSchema.partial()
350
- .extend({ scheduledTasks: z.boolean().optional() })
396
+ .extend({
397
+ scheduledTasks: z.boolean().optional(),
398
+ codeRun: z.boolean().optional(),
399
+ commandNetwork: z.boolean().optional(),
400
+ })
351
401
  .strict()
352
402
  .refine((value) => Object.values(value).some((entry) => entry !== undefined), '至少提供一个能力。')
353
403
 
@@ -393,7 +443,15 @@ export const HostSnapshotMessageSchema = z
393
443
  channelId: ChannelIdSchema,
394
444
  role: z.enum(['member', 'agent', 'system']),
395
445
  parts: z.array(SnapshotMessagePartSchema),
396
- sender: z.object({ memberId: ChannelMemberIdSchema, displayName: z.string().optional() }).strict().optional(),
446
+ sender: z
447
+ .object({
448
+ memberId: ChannelMemberIdSchema,
449
+ displayName: z.string().optional(),
450
+ /** The sender is the account of this agent on this host, answering the channel through another connection. */
451
+ localAgentId: AgentIdSchema.optional(),
452
+ })
453
+ .strict()
454
+ .optional(),
397
455
  mentionedConnectionAccount: z.boolean().optional(),
398
456
  activityKey: AdapterActivityKeySchema.optional(),
399
457
  targetLogicalMessageId: LogicalMessageIdSchema.optional(),
@@ -521,17 +579,25 @@ export const ChannelRuntimeUsageSchema = z
521
579
  })
522
580
  .strict()
523
581
 
582
+ const channelRuntimeToolFields = {
583
+ callId: NonEmptyStringSchema,
584
+ name: NonEmptyStringSchema,
585
+ displayName: NonEmptyStringSchema,
586
+ state: z.enum(['running', 'succeeded', 'failed']),
587
+ inputPreview: z.string().optional(),
588
+ resultPreview: z.string().optional(),
589
+ wroteToChannel: z.boolean().optional(),
590
+ deliveryState: z.enum(['sent', 'partially-sent', 'failed', 'unknown']).optional(),
591
+ durationMs: z.number().int().nonnegative().optional(),
592
+ }
593
+
524
594
  export const ChannelRuntimeToolSchema = z
525
595
  .object({
526
- callId: NonEmptyStringSchema,
527
- name: NonEmptyStringSchema,
528
- displayName: NonEmptyStringSchema,
529
- state: z.enum(['running', 'succeeded', 'failed']),
530
- inputPreview: z.string().optional(),
531
- resultPreview: z.string().optional(),
532
- wroteToChannel: z.boolean().optional(),
533
- deliveryState: z.enum(['sent', 'partially-sent', 'failed', 'unknown']).optional(),
534
- durationMs: z.number().int().nonnegative().optional(),
596
+ ...channelRuntimeToolFields,
597
+ /** Source of a `run_code` program. */
598
+ code: z.string().optional(),
599
+ /** Tools a `run_code` program called, in the order they started. */
600
+ children: z.array(z.object(channelRuntimeToolFields).strict()).optional(),
535
601
  })
536
602
  .strict()
537
603
 
@@ -553,6 +619,19 @@ export const ChannelRuntimeStepSchema = z
553
619
  })
554
620
  .strict()
555
621
 
622
+ /** One message that entered the model's input during a turn: channel admissions and host or DSH injections. */
623
+ export const ChannelRuntimeInputSchema = z
624
+ .object({
625
+ messageId: z.string().min(1),
626
+ /** DSH `user/message` source kind, e.g. `nekro-nxt-channel` or `nekro-nxt-handoff`. */
627
+ source: z.string().min(1),
628
+ /** Channel Events carried by a channel admission. */
629
+ eventCount: z.number().int().positive().optional(),
630
+ preview: z.string().optional(),
631
+ at: z.number().int().safe().nonnegative().optional(),
632
+ })
633
+ .strict()
634
+
556
635
  export const ChannelRuntimeTurnSchema = z
557
636
  .object({
558
637
  turn: z.number().int().nonnegative(),
@@ -568,6 +647,8 @@ export const ChannelRuntimeTurnSchema = z
568
647
  endedAt: z.number().int().safe().nonnegative().optional(),
569
648
  /** Newest Channel Event of the admission that opened this turn; absent for non-channel turns. */
570
649
  triggerEventId: ChannelEventIdSchema.optional(),
650
+ /** Messages that entered the model's input during the turn, in log order; absent when there were none. */
651
+ inputs: z.array(ChannelRuntimeInputSchema).optional(),
571
652
  })
572
653
  .strict()
573
654
 
@@ -583,6 +664,8 @@ export const ChannelRuntimeOccupancySchema = z
583
664
  })
584
665
  .strict()
585
666
  .optional(),
667
+ /** Pictures in the conversation; their tokens are not in the breakdown, so they make up most of the rest. */
668
+ imageCount: z.number().int().nonnegative().optional(),
586
669
  })
587
670
  .strict()
588
671
 
@@ -658,6 +741,54 @@ export const ChannelRuntimePerformanceSchema = z
658
741
  })
659
742
  .strict()
660
743
 
744
+ /** What the agent remembered or looked up, newest first; the admin sees it, the model does not. */
745
+ export const ChannelMemoryActivitySchema = z.discriminatedUnion('kind', [
746
+ z
747
+ .object({
748
+ kind: z.literal('backlog-folded'),
749
+ at: z.number().nonnegative().optional(),
750
+ foldedCount: z.number().int().nonnegative(),
751
+ shownCount: z.number().int().nonnegative(),
752
+ foldedImageCount: z.number().int().nonnegative(),
753
+ })
754
+ .strict(),
755
+ z
756
+ .object({
757
+ kind: z.literal('history-search'),
758
+ at: z.number().nonnegative().optional(),
759
+ query: z.string(),
760
+ hits: z.number().int().nonnegative(),
761
+ sender: z.string().optional(),
762
+ })
763
+ .strict(),
764
+ z
765
+ .object({
766
+ kind: z.literal('notes-updated'),
767
+ at: z.number().nonnegative().optional(),
768
+ revision: z.number().int().nonnegative(),
769
+ chars: z.number().int().nonnegative(),
770
+ })
771
+ .strict(),
772
+ z
773
+ .object({
774
+ kind: z.literal('member-notes-updated'),
775
+ at: z.number().nonnegative().optional(),
776
+ memberId: z.string(),
777
+ name: z.string().optional(),
778
+ chars: z.number().int().nonnegative(),
779
+ })
780
+ .strict(),
781
+ z
782
+ .object({
783
+ kind: z.literal('idle-review'),
784
+ at: z.number().nonnegative().optional(),
785
+ quietMinutes: z.number().int().nonnegative(),
786
+ })
787
+ .strict(),
788
+ ])
789
+
790
+ export type ChannelMemoryActivity = z.output<typeof ChannelMemoryActivitySchema>
791
+
661
792
  export const ChannelRuntimeProjectionSchema = z
662
793
  .object({
663
794
  channelId: ChannelIdSchema,
@@ -669,6 +800,7 @@ export const ChannelRuntimeProjectionSchema = z
669
800
  occupancy: ChannelRuntimeOccupancySchema.optional(),
670
801
  cache: ChannelRuntimeCacheSchema.optional(),
671
802
  performance: ChannelRuntimePerformanceSchema.optional(),
803
+ memory: z.array(ChannelMemoryActivitySchema).optional(),
672
804
  turns: z.array(ChannelRuntimeTurnSchema),
673
805
  })
674
806
  .strict()
@@ -678,6 +810,201 @@ export const ChannelRuntimeSseDataSchema = ChannelRuntimeProjectionSchema.extend
678
810
  truncated: z.boolean().optional(),
679
811
  }).strict()
680
812
 
813
+ export const ChannelRuntimeContextSchema = z
814
+ .object({
815
+ available: z.boolean(),
816
+ route: z
817
+ .object({
818
+ provider: z.string(),
819
+ model: z.string(),
820
+ contextWindow: z.number().int().positive().optional(),
821
+ reasoningEffort: z.string().optional(),
822
+ temperature: z.number().optional(),
823
+ maxTokens: z.number().int().positive().optional(),
824
+ })
825
+ .strict()
826
+ .optional(),
827
+ instructions: z.array(
828
+ z.object({ role: z.enum(['system', 'developer']), text: z.string(), truncated: z.boolean() }).strict(),
829
+ ),
830
+ tools: z.array(
831
+ z.object({ name: z.string(), description: z.string().optional(), parameters: z.string().optional() }).strict(),
832
+ ),
833
+ changes: z.array(
834
+ z
835
+ .object({
836
+ at: z.number().nonnegative().optional(),
837
+ reason: z.enum(['initial', 'resume', 'change', 'series']),
838
+ toolCount: z.number().int().nonnegative(),
839
+ })
840
+ .strict(),
841
+ ),
842
+ })
843
+ .strict()
844
+
845
+ const ChannelPromptPartSchema = z
846
+ .object({
847
+ document: PromptDocumentV1Schema,
848
+ /** Notes only: the agent may not rewrite them. */
849
+ locked: z.boolean(),
850
+ /** 0 before the first save; send it back as `expectedRevision`. */
851
+ revision: z.number().int().nonnegative(),
852
+ maxChars: z.number().int().positive(),
853
+ updatedBy: z.enum(['admin', 'agent']).optional(),
854
+ updatedAt: z.number().int().nonnegative().optional(),
855
+ revisions: z.array(
856
+ z
857
+ .object({
858
+ revision: z.number().int().positive(),
859
+ document: PromptDocumentV1Schema,
860
+ updatedBy: z.enum(['admin', 'agent']),
861
+ updatedAt: z.number().int().nonnegative(),
862
+ })
863
+ .strict(),
864
+ ),
865
+ })
866
+ .strict()
867
+
868
+ /**
869
+ * What the agent answering a channel reads about it, each part with recent earlier versions to restore: the admin's
870
+ * instructions (system prompt) and the agent's own notes (runtime context).
871
+ */
872
+ export const ChannelPromptViewSchema = z
873
+ .object({ instructions: ChannelPromptPartSchema, notes: ChannelPromptPartSchema })
874
+ .strict()
875
+
876
+ /**
877
+ * How much of a channel's unread backlog reaches the agent at once, and when it looks back on its own. Uncertain
878
+ * trade-offs stay per channel: a busy meme group and a long tabletop session want different budgets.
879
+ */
880
+ export const ChannelContextPolicySchema = z
881
+ .object({
882
+ /** Text characters of a backlog read message by message; older messages collapse into one line. 0: no limit. */
883
+ backlogTextChars: z.number().int().nonnegative().max(1_000_000),
884
+ /** Backlog images shown as pictures, newest first; the rest stay references the agent can open. */
885
+ backlogImages: z.number().int().nonnegative().max(20),
886
+ /** Quiet minutes after which the agent reviews the conversation and updates its notes. 0: off. */
887
+ idleReviewMinutes: z
888
+ .number()
889
+ .int()
890
+ .nonnegative()
891
+ .max(24 * 60),
892
+ })
893
+ .strict()
894
+
895
+ export type ChannelContextPolicy = z.output<typeof ChannelContextPolicySchema>
896
+
897
+ export const DEFAULT_CHANNEL_CONTEXT_POLICY: ChannelContextPolicy = Object.freeze({
898
+ backlogTextChars: 40_000,
899
+ backlogImages: 6,
900
+ idleReviewMinutes: 45,
901
+ })
902
+
903
+ export const ChannelContextPolicyViewSchema = z
904
+ .object({
905
+ policy: ChannelContextPolicySchema,
906
+ defaults: ChannelContextPolicySchema,
907
+ /** False while the channel follows the defaults. */
908
+ custom: z.boolean(),
909
+ })
910
+ .strict()
911
+
912
+ export type ChannelContextPolicyView = z.output<typeof ChannelContextPolicyViewSchema>
913
+
914
+ /** Whether a newer release exists on the build's own channel; development builds are never compared. */
915
+ export const ProductUpdateStatusSchema = z
916
+ .object({
917
+ channel: z.enum(['stable', 'preview', 'development']),
918
+ currentVersion: z.string(),
919
+ /** Short commit of a release build. */
920
+ currentCommit: z.string().optional(),
921
+ state: z.enum(['unknown', 'up-to-date', 'available', 'failed', 'unsupported']),
922
+ latest: z
923
+ .object({
924
+ version: z.string(),
925
+ commit: z.string().optional(),
926
+ url: z.string().url(),
927
+ publishedAt: z.number().int().nonnegative().optional(),
928
+ })
929
+ .strict()
930
+ .optional(),
931
+ checkedAt: z.number().int().nonnegative().optional(),
932
+ error: z.string().optional(),
933
+ autoCheck: z.boolean(),
934
+ })
935
+ .strict()
936
+
937
+ export type ProductUpdateStatus = z.output<typeof ProductUpdateStatusSchema>
938
+
939
+ /** Pause between consecutive messages an agent sends to one channel; longer messages wait a little longer. */
940
+ export const MessagePacingSchema = z.enum(['off', 'fast', 'normal', 'slow'])
941
+
942
+ export type MessagePacing = z.output<typeof MessagePacingSchema>
943
+
944
+ export const DEFAULT_MESSAGE_PACING: MessagePacing = 'normal'
945
+
946
+ export const MessagePacingSettingsSchema = z.object({ pacing: MessagePacingSchema }).strict()
947
+
948
+ /** `keyword`: SQLite keyword search only; `builtin`: keywords fused with the built-in semantic model. */
949
+ export const RetrievalModeSchema = z.enum(['keyword', 'builtin'])
950
+ export type RetrievalMode = z.output<typeof RetrievalModeSchema>
951
+
952
+ /** Search used by extension indexes (表情包与扩展素材能力 §6.4). */
953
+ export const RetrievalStatusSchema = z
954
+ .object({
955
+ mode: RetrievalModeSchema,
956
+ builtin: z
957
+ .object({
958
+ model: z.string(),
959
+ /** False where no inference runtime is published for this system and processor. */
960
+ supported: z.boolean(),
961
+ downloadBytes: z.number().int().nonnegative(),
962
+ install: z.discriminatedUnion('state', [
963
+ z.object({ state: z.literal('not-installed') }).strict(),
964
+ z
965
+ .object({
966
+ state: z.literal('downloading'),
967
+ receivedBytes: z.number().int().nonnegative(),
968
+ totalBytes: z.number().int().nonnegative(),
969
+ })
970
+ .strict(),
971
+ z.object({ state: z.literal('installed') }).strict(),
972
+ z.object({ state: z.literal('failed'), error: z.string() }).strict(),
973
+ ]),
974
+ })
975
+ .strict(),
976
+ vectors: z
977
+ .object({
978
+ documents: z.number().int().nonnegative(),
979
+ embedded: z.number().int().nonnegative(),
980
+ state: z.enum(['idle', 'running', 'ready']),
981
+ error: z.string().optional(),
982
+ })
983
+ .strict(),
984
+ })
985
+ .strict()
986
+ export type RetrievalStatus = z.output<typeof RetrievalStatusSchema>
987
+
988
+ /** What the agent keeps about individual members of one channel; each note comes with that member's messages. */
989
+ export const ChannelMemberNotesViewSchema = z
990
+ .object({
991
+ notes: z.array(
992
+ z
993
+ .object({
994
+ memberId: ChannelMemberIdSchema,
995
+ displayName: z.string().optional(),
996
+ text: z.string(),
997
+ updatedBy: z.enum(['admin', 'agent']),
998
+ updatedAt: z.number().int().nonnegative(),
999
+ })
1000
+ .strict(),
1001
+ ),
1002
+ maxChars: z.number().int().positive(),
1003
+ })
1004
+ .strict()
1005
+
1006
+ export type ChannelMemberNotesView = z.output<typeof ChannelMemberNotesViewSchema>
1007
+
681
1008
  export type ChannelRuntimePhase = z.output<typeof ChannelRuntimePhaseSchema>
682
1009
  export type ChannelRuntimeUsage = z.output<typeof ChannelRuntimeUsageSchema>
683
1010
  export type ChannelRuntimeOccupancy = z.output<typeof ChannelRuntimeOccupancySchema>
@@ -686,6 +1013,9 @@ export type ChannelRuntimeCache = z.output<typeof ChannelRuntimeCacheSchema>
686
1013
  export type ChannelRuntimePerformanceSample = z.output<typeof ChannelRuntimePerformanceSampleSchema>
687
1014
  export type ChannelRuntimePerformance = z.output<typeof ChannelRuntimePerformanceSchema>
688
1015
  export type ChannelRuntimeProjection = z.output<typeof ChannelRuntimeProjectionSchema>
1016
+ export type ChannelRuntimeContext = z.output<typeof ChannelRuntimeContextSchema>
1017
+ export type ChannelRuntimeInput = z.output<typeof ChannelRuntimeInputSchema>
1018
+ export type ChannelPromptView = z.output<typeof ChannelPromptViewSchema>
689
1019
  export type ChannelRuntimeSseData = z.output<typeof ChannelRuntimeSseDataSchema>
690
1020
  export type ChannelFactSseData = z.output<typeof ChannelFactSseDataSchema>
691
1021
 
@@ -716,6 +1046,7 @@ const AdapterConnectionDescriptorSchema = z
716
1046
  icon: HostIconNameSchema.optional(),
717
1047
  triggerable: z.boolean(),
718
1048
  channelKinds: z.array(z.enum(['internal', 'direct', 'group'])).optional(),
1049
+ directedAt: z.enum(['member', 'message']).optional(),
719
1050
  })
720
1051
  .strict(),
721
1052
  ),
@@ -959,6 +1290,27 @@ export const ScheduledTaskSchema = z
959
1290
 
960
1291
  export type ScheduledTask = z.output<typeof ScheduledTaskSchema>
961
1292
 
1293
+ /** The Node that runs `run_code` programs, and the download the desktop app offers when there is none. */
1294
+ export const CodeRunAvailabilitySchema = z
1295
+ .object({
1296
+ available: z.boolean(),
1297
+ source: z.enum(['host', 'system', 'downloaded']).optional(),
1298
+ version: z.string().optional(),
1299
+ download: z
1300
+ .object({
1301
+ version: z.string(),
1302
+ state: z.enum(['idle', 'downloading', 'failed']),
1303
+ receivedBytes: z.number().int().nonnegative().optional(),
1304
+ totalBytes: z.number().int().nonnegative().optional(),
1305
+ error: z.string().optional(),
1306
+ })
1307
+ .strict()
1308
+ .optional(),
1309
+ })
1310
+ .strict()
1311
+
1312
+ export type CodeRunAvailability = z.output<typeof CodeRunAvailabilitySchema>
1313
+
962
1314
  export const HostSnapshotSchema = z
963
1315
  .object({
964
1316
  upgrade: HostUpgradeSummarySchema.optional(),
@@ -991,6 +1343,10 @@ export const HostSnapshotSchema = z
991
1343
  capabilityAvailability: z
992
1344
  .object({
993
1345
  subagents: z.object({ available: z.boolean() }).strict(),
1346
+ /** Optional in older hosts, which never offer `run_code`. */
1347
+ codeRun: CodeRunAvailabilitySchema.optional(),
1348
+ /** What this host can confine for commands beyond file writes; optional in older hosts. */
1349
+ commandConfinement: z.object({ readScope: z.boolean(), networkControl: z.boolean() }).strict().optional(),
994
1350
  webSearch: z
995
1351
  .object({
996
1352
  provider: z.literal('deepseek-official'),
@@ -1074,6 +1430,8 @@ export const HostSnapshotSchema = z
1074
1430
  boundAgentId: AgentIdSchema.optional(),
1075
1431
  runtimePhase: ChannelRuntimePhaseSchema.default('idle'),
1076
1432
  activity: ChannelActivitySummarySchema.default({ unreadCount: 0, unreadCapped: false }),
1433
+ /** Other agents on this host that answer the same platform channel through another connection; absent when none. */
1434
+ localAgentIds: z.array(AgentIdSchema).min(1).optional(),
1077
1435
  bindings: z.array(
1078
1436
  z
1079
1437
  .object({
@@ -1081,6 +1439,8 @@ export const HostSnapshotSchema = z
1081
1439
  agentId: AgentIdSchema,
1082
1440
  triggerPolicy: TriggerPolicySchema,
1083
1441
  processingFeedback: z.enum(['auto', 'off']).default('auto'),
1442
+ /** Absent means `observe`. */
1443
+ localAgentMessages: z.enum(['observe', 'trigger']).optional(),
1084
1444
  activityTriggerOverrides: ActivityTriggerOverridesSchema.default({}),
1085
1445
  boundAt: z.number().int().safe().nonnegative(),
1086
1446
  })
@@ -1160,7 +1520,8 @@ export const HostSnapshotSchema = z
1160
1520
  z
1161
1521
  .object({
1162
1522
  id: ExtensionIdSchema,
1163
- scope: z.enum(['agent', 'host-adapter', 'host-ui']),
1523
+ /** What the latest Revision provides; listing and filtering only. */
1524
+ provides: z.array(ExtensionProvideSchema),
1164
1525
  slug: NonEmptyStringSchema,
1165
1526
  displayName: z.string(),
1166
1527
  description: z.string(),
@@ -1172,11 +1533,21 @@ export const HostSnapshotSchema = z
1172
1533
  revisionNumber: z.number().int().positive(),
1173
1534
  format: z.enum(['current', 'unavailable']).optional(),
1174
1535
  ui: ExtensionUiContributionsSchema.default(EMPTY_EXTENSION_UI_CONTRIBUTIONS),
1175
- configSchema: ConfigSchemaDocumentSchema.optional(),
1536
+ /** The host instance's configuration form (`config.host`). */
1537
+ hostConfigSchema: ConfigSchemaDocumentSchema.optional(),
1538
+ /** Each enabled agent's configuration form (`config.agent`). */
1539
+ agentConfigSchema: ConfigSchemaDocumentSchema.optional(),
1540
+ /** 扩展包自带图标的读取路径(同源、按摘要寻址、可长期缓存);没有图标时缺省。 */
1541
+ iconUrl: z
1542
+ .string()
1543
+ .regex(/^\/api\/extensions\/[^/]+\/revisions\/[^/]+\/icon\/[a-f0-9]{64}\.(?:svg|png|webp)$/u)
1544
+ .optional(),
1176
1545
  createdAt: z.number().int().safe().nonnegative(),
1177
- /** 从社区安装的保存记录的来源;本机保存与文件导入没有来源。 */
1546
+ /** 从社区安装的版本的来源;本机保存与文件导入没有来源。 */
1178
1547
  source: CommunitySourceSchema.optional(),
1179
- scope: z.enum(['agent', 'host-adapter', 'host-ui']),
1548
+ provides: z.array(ExtensionProvideSchema),
1549
+ /** Whether this Revision can be enabled for agents (agent-layer contributions or capabilities). */
1550
+ agentLayer: z.boolean(),
1180
1551
  contributions: z.array(z.string()),
1181
1552
  verification: z
1182
1553
  .object({
@@ -1194,12 +1565,11 @@ export const HostSnapshotSchema = z
1194
1565
  renderedPages: z.array(HostPageContributionSchema).max(8).optional(),
1195
1566
  usedUiComponents: z.array(HostUiKitComponentNameSchema).optional(),
1196
1567
  pageGeometry: z.array(HostUiPageGeometryEvidenceSchema).max(8).optional(),
1197
- permissions: HostUiPermissionDeclarationSchema.optional(),
1198
- permissionDigest: z
1199
- .string()
1200
- .regex(/^[a-f0-9]{64}$/u)
1201
- .optional(),
1202
- permissionApprovalRequired: z.boolean().optional(),
1568
+ permissions: ExtensionPermissionsSchema.optional(),
1569
+ /** Installing this Revision on the machine, against the current host grant. */
1570
+ hostPermission: ExtensionPermissionRequirementSchema.optional(),
1571
+ /** Enabling this Revision for an agent that has no grant yet. */
1572
+ agentPermission: ExtensionPermissionRequirementSchema.optional(),
1203
1573
  adapter: z
1204
1574
  .object({
1205
1575
  apiVersion: z.literal(2),
@@ -1253,11 +1623,15 @@ export const HostSnapshotSchema = z
1253
1623
  })
1254
1624
  .strict(),
1255
1625
  ),
1626
+ /** Present only while some agent's enable or disable request is waiting or has failed. */
1627
+ activationTransitions: z.array(ExtensionActivationTransitionSchema).optional(),
1256
1628
  installation: z
1257
1629
  .object({
1258
1630
  extensionRevisionId: ExtensionRevisionIdSchema,
1259
1631
  installedAt: z.number().int().safe().nonnegative(),
1632
+ /** Secret fields are removed; `configuredSecrets` lists those the Host stores. */
1260
1633
  config: JsonValueSchema.default({}),
1634
+ configuredSecrets: z.array(z.string()).optional(),
1261
1635
  runtime: z
1262
1636
  .object({
1263
1637
  status: z.enum(['active', 'restore-failed', 'dispose-failed']),
@@ -1268,14 +1642,8 @@ export const HostSnapshotSchema = z
1268
1642
  })
1269
1643
  .strict()
1270
1644
  .optional(),
1271
- hostUiPermission: z
1272
- .object({
1273
- declaration: HostUiPermissionDeclarationSchema,
1274
- permissionDigest: z.string().regex(/^[a-f0-9]{64}$/u),
1275
- approvalRequired: z.boolean(),
1276
- })
1277
- .strict()
1278
- .optional(),
1645
+ /** Installing the latest Revision on this machine, against the current host grant. */
1646
+ hostPermission: ExtensionPermissionRequirementSchema.optional(),
1279
1647
  hostClientDiagnostic: z
1280
1648
  .object({
1281
1649
  revisionId: ExtensionRevisionIdSchema,
@@ -1618,6 +1986,22 @@ export const LlmProviderCredentialSchema = z
1618
1986
  .object({ configured: z.boolean(), source: z.string().optional(), writable: z.boolean() })
1619
1987
  .strict()
1620
1988
 
1989
+ /**
1990
+ * The latest connection test that still describes the provider's saved address, protocol and API key. Tests made
1991
+ * before one of those changed are dropped. Never carries a key, request or response body.
1992
+ */
1993
+ export const LlmProviderTestResultSchema = z
1994
+ .object({
1995
+ /** Epoch milliseconds when the test finished. */
1996
+ at: z.number().int().nonnegative(),
1997
+ ok: z.boolean(),
1998
+ /** Short failure reason shown to the user; absent when the test passed. */
1999
+ message: z.string().max(240).optional(),
2000
+ /** The model the test asked. */
2001
+ model: NonEmptyStringSchema.optional(),
2002
+ })
2003
+ .strict()
2004
+
1621
2005
  export const LlmProviderViewSchema = z
1622
2006
  .object({
1623
2007
  provider: NonEmptyStringSchema,
@@ -1636,6 +2020,8 @@ export const LlmProviderViewSchema = z
1636
2020
  modelsCustomized: z.boolean(),
1637
2021
  /** The provider's adapter can list the models its endpoint offers. */
1638
2022
  discoverable: z.boolean(),
2023
+ /** `null` when the current configuration was never tested; absent from hosts that do not record tests. */
2024
+ lastTest: LlmProviderTestResultSchema.nullable().optional(),
1639
2025
  })
1640
2026
  .strict()
1641
2027
 
@@ -1715,7 +2101,7 @@ export const ExtensionImportInspectionSchema = z
1715
2101
  revisionId: ExtensionRevisionIdSchema,
1716
2102
  slug: NonEmptyStringSchema,
1717
2103
  displayName: NonEmptyStringSchema,
1718
- scope: z.enum(['agent', 'host-adapter', 'host-ui']),
2104
+ provides: z.array(ExtensionProvideSchema),
1719
2105
  idempotent: z.boolean(),
1720
2106
  slugConflict: z.boolean(),
1721
2107
  })
@@ -1775,6 +2161,8 @@ export type AgentRevisionHistory = z.output<typeof AgentRevisionHistorySchema>
1775
2161
  const channelParam = z.object({ channelId: ChannelIdSchema }).strict()
1776
2162
  const connectionParam = z.object({ connectionId: ConnectionIdSchema }).strict()
1777
2163
  const agentExtensionParam = z.object({ agentId: AgentIdSchema, extensionId: ExtensionIdSchema }).strict()
2164
+
2165
+ const PermissionApprovalSchema = z.object({ permissionDigest: z.string().regex(/^[a-f0-9]{64}$/u) }).strict()
1778
2166
  const extensionParam = z.object({ extensionId: ExtensionIdSchema }).strict()
1779
2167
  const dshPluginPackageParam = z.object({ packageId: DshPluginPackageIdSchema }).strict()
1780
2168
  const dshPluginEntryParam = z.object({ entryId: DshPluginEntryIdSchema }).strict()
@@ -2110,6 +2498,8 @@ export const HostApiContracts = {
2110
2498
  channelId: ChannelIdSchema,
2111
2499
  triggerPolicy: TriggerPolicySchema,
2112
2500
  processingFeedback: z.enum(['auto', 'off']).optional(),
2501
+ /** Messages from another local agent's account: `observe` keeps them as context unless they @ this agent. */
2502
+ localAgentMessages: z.enum(['observe', 'trigger']).optional(),
2113
2503
  activityTriggerOverrides: ActivityTriggerOverridesSchema.optional(),
2114
2504
  })
2115
2505
  .strict(),
@@ -2119,6 +2509,7 @@ export const HostApiContracts = {
2119
2509
  agentId: AgentIdSchema,
2120
2510
  triggerPolicy: TriggerPolicySchema,
2121
2511
  processingFeedback: z.enum(['auto', 'off']).default('auto'),
2512
+ localAgentMessages: z.enum(['observe', 'trigger']).optional(),
2122
2513
  activityTriggerOverrides: ActivityTriggerOverridesSchema.default({}),
2123
2514
  boundAt: z.number().int().safe().nonnegative(),
2124
2515
  })
@@ -2215,6 +2606,183 @@ export const HostApiContracts = {
2215
2606
  .strict(),
2216
2607
  error: HostApiErrorSchema,
2217
2608
  }),
2609
+ getChannelRuntimeContext: defineContract({
2610
+ method: 'GET',
2611
+ path: '/api/channels/:channelId/runtime/context',
2612
+ params: channelParam,
2613
+ request: NoRequestBodySchema,
2614
+ // What the live session sends the model: route, rendered instructions and tool schemas. Read on demand; the runtime
2615
+ // projection pushed over SSE never carries it. `available: false` when no session of the channel is in memory.
2616
+ response: ChannelRuntimeContextSchema,
2617
+ error: HostApiErrorSchema,
2618
+ }),
2619
+ getChannelRuntimeInput: defineContract({
2620
+ method: 'GET',
2621
+ path: '/api/channels/:channelId/runtime/inputs/:messageId',
2622
+ params: z.object({ channelId: ChannelIdSchema, messageId: z.string().trim().min(1).max(200) }).strict(),
2623
+ request: NoRequestBodySchema,
2624
+ // The full text of one input message as the model received it; the runtime projection only carries a preview.
2625
+ // `available: false` when the session has left memory.
2626
+ response: z
2627
+ .object({
2628
+ messageId: z.string(),
2629
+ available: z.boolean(),
2630
+ source: z.string().optional(),
2631
+ text: z.string().optional(),
2632
+ truncated: z.boolean(),
2633
+ })
2634
+ .strict(),
2635
+ error: HostApiErrorSchema,
2636
+ }),
2637
+ getChannelPrompt: defineContract({
2638
+ method: 'GET',
2639
+ path: '/api/channels/:channelId/prompt',
2640
+ params: channelParam,
2641
+ request: NoRequestBodySchema,
2642
+ response: ChannelPromptViewSchema,
2643
+ error: HostApiErrorSchema,
2644
+ }),
2645
+ updateChannelPrompt: defineContract({
2646
+ method: 'PUT',
2647
+ path: '/api/channels/:channelId/prompt',
2648
+ params: channelParam,
2649
+ request: z
2650
+ .object({
2651
+ kind: z.enum(['instructions', 'notes']),
2652
+ document: PromptDocumentV1Schema,
2653
+ /** Notes only; ignored for instructions. */
2654
+ locked: z.boolean(),
2655
+ /** The revision the edit started from; a newer stored revision rejects the save. */
2656
+ expectedRevision: z.number().int().nonnegative(),
2657
+ })
2658
+ .strict(),
2659
+ response: ChannelPromptViewSchema,
2660
+ error: HostApiErrorSchema,
2661
+ }),
2662
+ getProductUpdates: defineContract({
2663
+ method: 'GET',
2664
+ path: '/api/product/updates',
2665
+ params: EmptyParamsSchema,
2666
+ request: NoRequestBodySchema,
2667
+ response: ProductUpdateStatusSchema,
2668
+ error: HostApiErrorSchema,
2669
+ }),
2670
+ checkProductUpdates: defineContract({
2671
+ method: 'POST',
2672
+ path: '/api/product/updates/check',
2673
+ params: EmptyParamsSchema,
2674
+ request: NoRequestBodySchema,
2675
+ response: ProductUpdateStatusSchema,
2676
+ error: HostApiErrorSchema,
2677
+ }),
2678
+ updateProductUpdateSettings: defineContract({
2679
+ method: 'PUT',
2680
+ path: '/api/product/updates/settings',
2681
+ params: EmptyParamsSchema,
2682
+ request: z.object({ autoCheck: z.boolean() }).strict(),
2683
+ response: ProductUpdateStatusSchema,
2684
+ error: HostApiErrorSchema,
2685
+ }),
2686
+ getMessagePacingSettings: defineContract({
2687
+ method: 'GET',
2688
+ path: '/api/settings/message-pacing',
2689
+ params: EmptyParamsSchema,
2690
+ request: NoRequestBodySchema,
2691
+ response: MessagePacingSettingsSchema,
2692
+ error: HostApiErrorSchema,
2693
+ }),
2694
+ updateMessagePacingSettings: defineContract({
2695
+ method: 'PUT',
2696
+ path: '/api/settings/message-pacing',
2697
+ params: EmptyParamsSchema,
2698
+ request: MessagePacingSettingsSchema,
2699
+ response: MessagePacingSettingsSchema,
2700
+ error: HostApiErrorSchema,
2701
+ }),
2702
+ getRetrievalStatus: defineContract({
2703
+ method: 'GET',
2704
+ path: '/api/settings/retrieval',
2705
+ params: EmptyParamsSchema,
2706
+ request: NoRequestBodySchema,
2707
+ response: RetrievalStatusSchema,
2708
+ error: HostApiErrorSchema,
2709
+ }),
2710
+ updateRetrievalMode: defineContract({
2711
+ method: 'PUT',
2712
+ path: '/api/settings/retrieval',
2713
+ params: EmptyParamsSchema,
2714
+ request: z.object({ mode: RetrievalModeSchema }).strict(),
2715
+ response: RetrievalStatusSchema,
2716
+ error: HostApiErrorSchema,
2717
+ }),
2718
+ downloadRetrievalModel: defineContract({
2719
+ method: 'POST',
2720
+ path: '/api/settings/retrieval/model/download',
2721
+ params: EmptyParamsSchema,
2722
+ request: NoRequestBodySchema,
2723
+ response: RetrievalStatusSchema,
2724
+ error: HostApiErrorSchema,
2725
+ }),
2726
+ removeRetrievalModel: defineContract({
2727
+ method: 'DELETE',
2728
+ path: '/api/settings/retrieval/model',
2729
+ params: EmptyParamsSchema,
2730
+ request: NoRequestBodySchema,
2731
+ response: RetrievalStatusSchema,
2732
+ error: HostApiErrorSchema,
2733
+ }),
2734
+ downloadCodeRunNode: defineContract({
2735
+ invalidatesSnapshot: true,
2736
+ method: 'POST',
2737
+ path: '/api/runtimes/code-run-node/download',
2738
+ params: EmptyParamsSchema,
2739
+ request: NoRequestBodySchema,
2740
+ response: CodeRunAvailabilitySchema,
2741
+ error: HostApiErrorSchema,
2742
+ }),
2743
+ removeCodeRunNode: defineContract({
2744
+ invalidatesSnapshot: true,
2745
+ method: 'DELETE',
2746
+ path: '/api/runtimes/code-run-node',
2747
+ params: EmptyParamsSchema,
2748
+ request: NoRequestBodySchema,
2749
+ response: CodeRunAvailabilitySchema,
2750
+ error: HostApiErrorSchema,
2751
+ }),
2752
+ getChannelMemberNotes: defineContract({
2753
+ method: 'GET',
2754
+ path: '/api/channels/:channelId/member-notes',
2755
+ params: channelParam,
2756
+ request: NoRequestBodySchema,
2757
+ response: ChannelMemberNotesViewSchema,
2758
+ error: HostApiErrorSchema,
2759
+ }),
2760
+ updateChannelMemberNote: defineContract({
2761
+ method: 'PUT',
2762
+ path: '/api/channels/:channelId/member-notes/:memberId',
2763
+ params: z.object({ channelId: ChannelIdSchema, memberId: ChannelMemberIdSchema }).strict(),
2764
+ /** An empty text forgets the member. */
2765
+ request: z.object({ text: z.string().max(2000) }).strict(),
2766
+ response: ChannelMemberNotesViewSchema,
2767
+ error: HostApiErrorSchema,
2768
+ }),
2769
+ getChannelContextPolicy: defineContract({
2770
+ method: 'GET',
2771
+ path: '/api/channels/:channelId/context-policy',
2772
+ params: channelParam,
2773
+ request: NoRequestBodySchema,
2774
+ response: ChannelContextPolicyViewSchema,
2775
+ error: HostApiErrorSchema,
2776
+ }),
2777
+ updateChannelContextPolicy: defineContract({
2778
+ method: 'PUT',
2779
+ path: '/api/channels/:channelId/context-policy',
2780
+ params: channelParam,
2781
+ /** `null` returns the channel to the defaults. */
2782
+ request: z.object({ policy: ChannelContextPolicySchema.nullable() }).strict(),
2783
+ response: ChannelContextPolicyViewSchema,
2784
+ error: HostApiErrorSchema,
2785
+ }),
2218
2786
  resetChannelContext: defineContract({
2219
2787
  invalidatesSnapshot: true,
2220
2788
  method: 'POST',
@@ -2528,7 +3096,11 @@ export const HostApiContracts = {
2528
3096
  params: z
2529
3097
  .object({
2530
3098
  query: z.string().trim().max(80).optional(),
2531
- scope: z.enum(['agent', 'host-adapter', 'host-ui']).optional(),
3099
+ provides: ExtensionProvideSchema.optional(),
3100
+ /** 只列社区后台认定的官方扩展。 */
3101
+ official: z.enum(['1']).optional(),
3102
+ /** 排序方式,默认最近更新;游标只在同一种排序下有效。 */
3103
+ sort: CommunityExtensionSortSchema.optional(),
2532
3104
  cursor: z.string().max(200).optional(),
2533
3105
  })
2534
3106
  .strict(),
@@ -2643,6 +3215,8 @@ export const HostApiContracts = {
2643
3215
  extensionId: ExtensionIdSchema,
2644
3216
  revisionId: ExtensionRevisionIdSchema,
2645
3217
  notes: z.string().max(2000),
3218
+ /** 作者填写的条目信息;省略的字段不改动社区上已有的内容。 */
3219
+ listing: CommunityListingInputSchema.optional(),
2646
3220
  })
2647
3221
  .strict(),
2648
3222
  response: z
@@ -2656,6 +3230,114 @@ export const HostApiContracts = {
2656
3230
  .strict(),
2657
3231
  error: HostApiErrorSchema,
2658
3232
  }),
3233
+ listCommunityPersonas: defineContract({
3234
+ timeoutMs: 30_000,
3235
+ method: 'GET',
3236
+ path: '/api/community/personas',
3237
+ params: z
3238
+ .object({
3239
+ query: z.string().trim().min(1).max(200).optional(),
3240
+ tag: z.string().trim().min(1).max(40).optional(),
3241
+ official: z.literal('1').optional(),
3242
+ cursor: z.string().min(1).max(500).optional(),
3243
+ })
3244
+ .strict(),
3245
+ request: NoRequestBodySchema,
3246
+ response: z.object({ items: z.array(CommunityPersonaSummarySchema), nextCursor: z.string().nullable() }).strict(),
3247
+ error: HostApiErrorSchema,
3248
+ }),
3249
+ getCommunityPersona: defineContract({
3250
+ timeoutMs: 30_000,
3251
+ method: 'GET',
3252
+ path: '/api/community/personas/:personaId',
3253
+ params: z.object({ personaId: CommunityPersonaIdSchema }).strict(),
3254
+ request: NoRequestBodySchema,
3255
+ response: CommunityPersonaDetailSchema,
3256
+ error: HostApiErrorSchema,
3257
+ }),
3258
+ /**
3259
+ * 把社区人设装进本机:新建一个智能体,或为现有智能体保存一次只换设定(及可选名称)的新配置。头像下载后作为
3260
+ * 智能体头像;下载失败不影响安装。`expectedRevisionId` 是用户看过的修订,社区已更新时拒绝,避免装入未看过的内容。
3261
+ */
3262
+ installCommunityPersona: defineContract({
3263
+ invalidatesSnapshot: true,
3264
+ timeoutMs: 60_000,
3265
+ method: 'POST',
3266
+ path: '/api/community/personas/:personaId/install',
3267
+ params: z.object({ personaId: CommunityPersonaIdSchema }).strict(),
3268
+ request: z.discriminatedUnion('target', [
3269
+ z
3270
+ .object({
3271
+ target: z.literal('new'),
3272
+ expectedRevisionId: CommunityPersonaRevisionIdSchema,
3273
+ displayName: z.string().trim().min(1).max(80),
3274
+ model: AgentModelSchema,
3275
+ useAvatar: z.boolean(),
3276
+ })
3277
+ .strict(),
3278
+ z
3279
+ .object({
3280
+ target: z.literal('replace'),
3281
+ expectedRevisionId: CommunityPersonaRevisionIdSchema,
3282
+ agentId: AgentIdSchema,
3283
+ expectedCurrentRevisionId: AgentRevisionIdSchema,
3284
+ /** 同时改名;省略时保留原名称。 */
3285
+ displayName: z.string().trim().min(1).max(80).optional(),
3286
+ useAvatar: z.boolean(),
3287
+ })
3288
+ .strict(),
3289
+ ]),
3290
+ response: z
3291
+ .object({
3292
+ agentId: AgentIdSchema,
3293
+ channelId: ChannelIdSchema.nullable(),
3294
+ currentRevisionId: AgentRevisionIdSchema,
3295
+ avatar: z.enum(['applied', 'none', 'failed']),
3296
+ })
3297
+ .strict(),
3298
+ error: HostApiErrorSchema,
3299
+ }),
3300
+ listCommunityMyPersonas: defineContract({
3301
+ timeoutMs: 30_000,
3302
+ method: 'GET',
3303
+ path: '/api/community/mine/personas',
3304
+ params: EmptyParamsSchema,
3305
+ request: NoRequestBodySchema,
3306
+ response: CommunityMyPersonasSchema,
3307
+ error: HostApiErrorSchema,
3308
+ }),
3309
+ /** 把智能体的设定分享到社区;带 `personaId` 时更新自己已发布的人设(生成新修订并重新审查)。 */
3310
+ publishCommunityPersona: defineContract({
3311
+ timeoutMs: 60_000,
3312
+ method: 'POST',
3313
+ path: '/api/community/personas/publish',
3314
+ params: EmptyParamsSchema,
3315
+ request: z
3316
+ .object({
3317
+ agentId: AgentIdSchema,
3318
+ personaId: CommunityPersonaIdSchema.optional(),
3319
+ name: z.string().trim().min(1).max(80),
3320
+ summary: z.string().trim().min(1).max(200),
3321
+ description: z.string().max(20_000),
3322
+ tags: z.array(z.string().trim().min(1).max(40)).max(16),
3323
+ persona: z.string().trim().min(1).max(COMMUNITY_PERSONA_MAX_LENGTH),
3324
+ notes: z.string().max(2000).optional(),
3325
+ /** 页面缩放后的头像(Base64);省略时不上传头像,更新时保留社区上的原头像。 */
3326
+ avatar: z
3327
+ .object({
3328
+ mediaType: z.enum(COMMUNITY_PERSONA_AVATAR_TYPES),
3329
+ base64: z
3330
+ .string()
3331
+ .min(1)
3332
+ .max(Math.ceil((512 * 1024 * 4) / 3) + 4),
3333
+ })
3334
+ .strict()
3335
+ .optional(),
3336
+ })
3337
+ .strict(),
3338
+ response: CommunityMyPersonaSchema,
3339
+ error: HostApiErrorSchema,
3340
+ }),
2659
3341
  testMcpServer: defineContract({
2660
3342
  timeoutMs: 30_000,
2661
3343
  method: 'POST',
@@ -3112,8 +3794,19 @@ export const HostApiContracts = {
3112
3794
  method: 'POST',
3113
3795
  path: '/api/extensions/:extensionId/revisions/:revisionId/call',
3114
3796
  params: extensionRevisionParam,
3797
+ /**
3798
+ * A panel's call to its extension's host instance. The Host checks the anchor (the agent has the extension
3799
+ * enabled; the channel or connection exists) and hands the extension the resolved caller.
3800
+ */
3115
3801
  request: z
3116
- .object({ agentId: AgentIdSchema, method: NonEmptyStringSchema, input: JsonValueSchema.optional() })
3802
+ .object({
3803
+ /** Absent: the call comes from the extension itself, like a page call. */
3804
+ anchor: z.object({ kind: ExtensionPanelAnchorSchema, id: NonEmptyStringSchema }).strict().optional(),
3805
+ /** The agent the panel is shown for; must be the responding agent of an anchored channel. */
3806
+ agentId: AgentIdSchema.optional(),
3807
+ method: NonEmptyStringSchema,
3808
+ input: JsonValueSchema.optional(),
3809
+ })
3117
3810
  .strict(),
3118
3811
  response: z.object({ value: JsonValueSchema }).strict(),
3119
3812
  error: HostApiErrorSchema,
@@ -3148,12 +3841,16 @@ export const HostApiContracts = {
3148
3841
  request: z
3149
3842
  .object({
3150
3843
  revisionId: ExtensionRevisionIdSchema,
3151
- permissionApproval: z
3152
- .object({ permissionDigest: z.string().regex(/^[a-f0-9]{64}$/u) })
3153
- .strict()
3154
- .optional(),
3844
+ /** Approval of the agent-layer capabilities. */
3845
+ permissionApproval: PermissionApprovalSchema.optional(),
3846
+ /** Approval of the host layer when enabling also installs or switches the extension on this machine. */
3847
+ hostPermissionApproval: PermissionApprovalSchema.optional(),
3155
3848
  })
3156
3849
  .strict(),
3850
+ /**
3851
+ * The committed Activation, or — while the agent is still replying — `pending` with the accepted request; it then
3852
+ * takes effect at the agent's next safe gap and its progress appears in the snapshot's `activationTransitions`.
3853
+ */
3157
3854
  response: z
3158
3855
  .object({
3159
3856
  activation: z
@@ -3165,9 +3862,12 @@ export const HostApiContracts = {
3165
3862
  configuredSecrets: z.array(z.string()).optional(),
3166
3863
  activatedAt: z.number().int().safe().nonnegative(),
3167
3864
  })
3168
- .strict(),
3865
+ .strict()
3866
+ .optional(),
3867
+ pending: ExtensionActivationTransitionSchema.optional(),
3169
3868
  })
3170
- .strict(),
3869
+ .strict()
3870
+ .refine((value) => (value.activation === undefined) !== (value.pending === undefined), '启用结果必须二选一。'),
3171
3871
  error: HostApiErrorSchema,
3172
3872
  }),
3173
3873
  deactivateExtension: defineContract({
@@ -3176,7 +3876,8 @@ export const HostApiContracts = {
3176
3876
  path: '/api/agents/:agentId/extensions/:extensionId/activation',
3177
3877
  params: agentExtensionParam,
3178
3878
  request: NoRequestBodySchema,
3179
- response: z.object({ disabled: z.literal(true) }).strict(),
3879
+ /** `pending` is present when the agent is still replying; the Activation is removed at its next safe gap. */
3880
+ response: z.object({ disabled: z.literal(true), pending: ExtensionActivationTransitionSchema.optional() }).strict(),
3180
3881
  error: HostApiErrorSchema,
3181
3882
  }),
3182
3883
  updateExtensionActivationConfig: defineContract({
@@ -3200,8 +3901,14 @@ export const HostApiContracts = {
3200
3901
  method: 'PUT',
3201
3902
  path: '/api/extensions/:extensionId/installation/config',
3202
3903
  params: extensionParam,
3203
- request: z.object({ config: JsonValueSchema }).strict(),
3204
- response: z.object({ config: JsonValueSchema }).strict(),
3904
+ request: z
3905
+ .object({
3906
+ config: JsonValueSchema,
3907
+ /** Write-only credential drafts keyed by secret field; an absent or empty draft keeps the stored one. */
3908
+ secrets: z.record(z.string(), z.string().max(8192)).optional(),
3909
+ })
3910
+ .strict(),
3911
+ response: z.object({ config: JsonValueSchema, configuredSecrets: z.array(z.string()).optional() }).strict(),
3205
3912
  error: HostApiErrorSchema,
3206
3913
  }),
3207
3914
  installHostExtension: defineContract({
@@ -3213,10 +3920,10 @@ export const HostApiContracts = {
3213
3920
  request: z
3214
3921
  .object({
3215
3922
  revisionId: ExtensionRevisionIdSchema,
3216
- permissionApproval: z
3217
- .object({ permissionDigest: z.string().regex(/^[a-f0-9]{64}$/u) })
3218
- .strict()
3219
- .optional(),
3923
+ /** Approval of the host layer (browser permissions and `permissions.host`). */
3924
+ permissionApproval: PermissionApprovalSchema.optional(),
3925
+ /** Approval of expanded agent-layer capabilities for the agents that move to this Revision. */
3926
+ agentPermissionApproval: PermissionApprovalSchema.optional(),
3220
3927
  })
3221
3928
  .strict(),
3222
3929
  response: z