@nekro-nxt/contracts 0.1.0 → 0.3.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,10 +33,27 @@ 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,
41
+ CommunitySourceSchema,
42
+ CommunityEndpointSchema,
43
+ CommunityInstalledSchema,
44
+ CommunityMyExtensionSchema,
45
+ CommunityReviewReportSchema,
39
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'
40
57
  import {
41
58
  DshNxtHostUiSchema,
42
59
  EMPTY_EXTENSION_UI_CONTRIBUTIONS,
@@ -49,6 +66,9 @@ import {
49
66
  HostUiPageGeometryEvidenceSchema,
50
67
  HostUiPageEntrySchema,
51
68
  HostUiPermissionDeclarationSchema,
69
+ ExtensionPanelAnchorSchema,
70
+ ExtensionPermissionsSchema,
71
+ ExtensionProvideSchema,
52
72
  } from './extension-ui.js'
53
73
 
54
74
  const EmptyParamsSchema = z.object({}).strict()
@@ -209,6 +229,32 @@ const AgentModelSchema = z
209
229
  })
210
230
  .strict()
211
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
+
212
258
  export const ImageUnderstandingPolicyApiSchema = z
213
259
  .object({
214
260
  history: z
@@ -388,7 +434,15 @@ export const HostSnapshotMessageSchema = z
388
434
  channelId: ChannelIdSchema,
389
435
  role: z.enum(['member', 'agent', 'system']),
390
436
  parts: z.array(SnapshotMessagePartSchema),
391
- sender: z.object({ memberId: ChannelMemberIdSchema, displayName: z.string().optional() }).strict().optional(),
437
+ sender: z
438
+ .object({
439
+ memberId: ChannelMemberIdSchema,
440
+ displayName: z.string().optional(),
441
+ /** The sender is the account of this agent on this host, answering the channel through another connection. */
442
+ localAgentId: AgentIdSchema.optional(),
443
+ })
444
+ .strict()
445
+ .optional(),
392
446
  mentionedConnectionAccount: z.boolean().optional(),
393
447
  activityKey: AdapterActivityKeySchema.optional(),
394
448
  targetLogicalMessageId: LogicalMessageIdSchema.optional(),
@@ -548,6 +602,19 @@ export const ChannelRuntimeStepSchema = z
548
602
  })
549
603
  .strict()
550
604
 
605
+ /** One message that entered the model's input during a turn: channel admissions and host or DSH injections. */
606
+ export const ChannelRuntimeInputSchema = z
607
+ .object({
608
+ messageId: z.string().min(1),
609
+ /** DSH `user/message` source kind, e.g. `nekro-nxt-channel` or `nekro-nxt-handoff`. */
610
+ source: z.string().min(1),
611
+ /** Channel Events carried by a channel admission. */
612
+ eventCount: z.number().int().positive().optional(),
613
+ preview: z.string().optional(),
614
+ at: z.number().int().safe().nonnegative().optional(),
615
+ })
616
+ .strict()
617
+
551
618
  export const ChannelRuntimeTurnSchema = z
552
619
  .object({
553
620
  turn: z.number().int().nonnegative(),
@@ -563,6 +630,8 @@ export const ChannelRuntimeTurnSchema = z
563
630
  endedAt: z.number().int().safe().nonnegative().optional(),
564
631
  /** Newest Channel Event of the admission that opened this turn; absent for non-channel turns. */
565
632
  triggerEventId: ChannelEventIdSchema.optional(),
633
+ /** Messages that entered the model's input during the turn, in log order; absent when there were none. */
634
+ inputs: z.array(ChannelRuntimeInputSchema).optional(),
566
635
  })
567
636
  .strict()
568
637
 
@@ -673,6 +742,62 @@ export const ChannelRuntimeSseDataSchema = ChannelRuntimeProjectionSchema.extend
673
742
  truncated: z.boolean().optional(),
674
743
  }).strict()
675
744
 
745
+ export const ChannelRuntimeContextSchema = z
746
+ .object({
747
+ available: z.boolean(),
748
+ route: z
749
+ .object({
750
+ provider: z.string(),
751
+ model: z.string(),
752
+ contextWindow: z.number().int().positive().optional(),
753
+ reasoningEffort: z.string().optional(),
754
+ temperature: z.number().optional(),
755
+ maxTokens: z.number().int().positive().optional(),
756
+ })
757
+ .strict()
758
+ .optional(),
759
+ instructions: z.array(
760
+ z.object({ role: z.enum(['system', 'developer']), text: z.string(), truncated: z.boolean() }).strict(),
761
+ ),
762
+ tools: z.array(
763
+ z.object({ name: z.string(), description: z.string().optional(), parameters: z.string().optional() }).strict(),
764
+ ),
765
+ changes: z.array(
766
+ z
767
+ .object({
768
+ at: z.number().nonnegative().optional(),
769
+ reason: z.enum(['initial', 'resume', 'change', 'series']),
770
+ toolCount: z.number().int().nonnegative(),
771
+ })
772
+ .strict(),
773
+ ),
774
+ })
775
+ .strict()
776
+
777
+ /** A channel's own instructions for whoever answers it, with recent earlier versions to restore. */
778
+ export const ChannelPromptViewSchema = z
779
+ .object({
780
+ document: PromptDocumentV1Schema,
781
+ /** The agent may not rewrite a locked prompt. */
782
+ locked: z.boolean(),
783
+ /** 0 before the first save; send it back as `expectedRevision`. */
784
+ revision: z.number().int().nonnegative(),
785
+ maxChars: z.number().int().positive(),
786
+ updatedBy: z.enum(['admin', 'agent']).optional(),
787
+ updatedAt: z.number().int().nonnegative().optional(),
788
+ revisions: z.array(
789
+ z
790
+ .object({
791
+ revision: z.number().int().positive(),
792
+ document: PromptDocumentV1Schema,
793
+ updatedBy: z.enum(['admin', 'agent']),
794
+ updatedAt: z.number().int().nonnegative(),
795
+ })
796
+ .strict(),
797
+ ),
798
+ })
799
+ .strict()
800
+
676
801
  export type ChannelRuntimePhase = z.output<typeof ChannelRuntimePhaseSchema>
677
802
  export type ChannelRuntimeUsage = z.output<typeof ChannelRuntimeUsageSchema>
678
803
  export type ChannelRuntimeOccupancy = z.output<typeof ChannelRuntimeOccupancySchema>
@@ -681,6 +806,9 @@ export type ChannelRuntimeCache = z.output<typeof ChannelRuntimeCacheSchema>
681
806
  export type ChannelRuntimePerformanceSample = z.output<typeof ChannelRuntimePerformanceSampleSchema>
682
807
  export type ChannelRuntimePerformance = z.output<typeof ChannelRuntimePerformanceSchema>
683
808
  export type ChannelRuntimeProjection = z.output<typeof ChannelRuntimeProjectionSchema>
809
+ export type ChannelRuntimeContext = z.output<typeof ChannelRuntimeContextSchema>
810
+ export type ChannelRuntimeInput = z.output<typeof ChannelRuntimeInputSchema>
811
+ export type ChannelPromptView = z.output<typeof ChannelPromptViewSchema>
684
812
  export type ChannelRuntimeSseData = z.output<typeof ChannelRuntimeSseDataSchema>
685
813
  export type ChannelFactSseData = z.output<typeof ChannelFactSseDataSchema>
686
814
 
@@ -1069,6 +1197,8 @@ export const HostSnapshotSchema = z
1069
1197
  boundAgentId: AgentIdSchema.optional(),
1070
1198
  runtimePhase: ChannelRuntimePhaseSchema.default('idle'),
1071
1199
  activity: ChannelActivitySummarySchema.default({ unreadCount: 0, unreadCapped: false }),
1200
+ /** Other agents on this host that answer the same platform channel through another connection; absent when none. */
1201
+ localAgentIds: z.array(AgentIdSchema).min(1).optional(),
1072
1202
  bindings: z.array(
1073
1203
  z
1074
1204
  .object({
@@ -1076,6 +1206,8 @@ export const HostSnapshotSchema = z
1076
1206
  agentId: AgentIdSchema,
1077
1207
  triggerPolicy: TriggerPolicySchema,
1078
1208
  processingFeedback: z.enum(['auto', 'off']).default('auto'),
1209
+ /** Absent means `observe`. */
1210
+ localAgentMessages: z.enum(['observe', 'trigger']).optional(),
1079
1211
  activityTriggerOverrides: ActivityTriggerOverridesSchema.default({}),
1080
1212
  boundAt: z.number().int().safe().nonnegative(),
1081
1213
  })
@@ -1155,7 +1287,8 @@ export const HostSnapshotSchema = z
1155
1287
  z
1156
1288
  .object({
1157
1289
  id: ExtensionIdSchema,
1158
- scope: z.enum(['agent', 'host-adapter', 'host-ui']),
1290
+ /** What the latest Revision provides; listing and filtering only. */
1291
+ provides: z.array(ExtensionProvideSchema),
1159
1292
  slug: NonEmptyStringSchema,
1160
1293
  displayName: z.string(),
1161
1294
  description: z.string(),
@@ -1167,9 +1300,21 @@ export const HostSnapshotSchema = z
1167
1300
  revisionNumber: z.number().int().positive(),
1168
1301
  format: z.enum(['current', 'unavailable']).optional(),
1169
1302
  ui: ExtensionUiContributionsSchema.default(EMPTY_EXTENSION_UI_CONTRIBUTIONS),
1170
- configSchema: ConfigSchemaDocumentSchema.optional(),
1303
+ /** The host instance's configuration form (`config.host`). */
1304
+ hostConfigSchema: ConfigSchemaDocumentSchema.optional(),
1305
+ /** Each enabled agent's configuration form (`config.agent`). */
1306
+ agentConfigSchema: ConfigSchemaDocumentSchema.optional(),
1307
+ /** 扩展包自带图标的读取路径(同源、按摘要寻址、可长期缓存);没有图标时缺省。 */
1308
+ iconUrl: z
1309
+ .string()
1310
+ .regex(/^\/api\/extensions\/[^/]+\/revisions\/[^/]+\/icon\/[a-f0-9]{64}\.(?:svg|png|webp)$/u)
1311
+ .optional(),
1171
1312
  createdAt: z.number().int().safe().nonnegative(),
1172
- scope: z.enum(['agent', 'host-adapter', 'host-ui']),
1313
+ /** 从社区安装的保存记录的来源;本机保存与文件导入没有来源。 */
1314
+ source: CommunitySourceSchema.optional(),
1315
+ provides: z.array(ExtensionProvideSchema),
1316
+ /** Whether this Revision can be enabled for agents (agent-layer contributions or capabilities). */
1317
+ agentLayer: z.boolean(),
1173
1318
  contributions: z.array(z.string()),
1174
1319
  verification: z
1175
1320
  .object({
@@ -1187,12 +1332,11 @@ export const HostSnapshotSchema = z
1187
1332
  renderedPages: z.array(HostPageContributionSchema).max(8).optional(),
1188
1333
  usedUiComponents: z.array(HostUiKitComponentNameSchema).optional(),
1189
1334
  pageGeometry: z.array(HostUiPageGeometryEvidenceSchema).max(8).optional(),
1190
- permissions: HostUiPermissionDeclarationSchema.optional(),
1191
- permissionDigest: z
1192
- .string()
1193
- .regex(/^[a-f0-9]{64}$/u)
1194
- .optional(),
1195
- permissionApprovalRequired: z.boolean().optional(),
1335
+ permissions: ExtensionPermissionsSchema.optional(),
1336
+ /** Installing this Revision on the machine, against the current host grant. */
1337
+ hostPermission: ExtensionPermissionRequirementSchema.optional(),
1338
+ /** Enabling this Revision for an agent that has no grant yet. */
1339
+ agentPermission: ExtensionPermissionRequirementSchema.optional(),
1196
1340
  adapter: z
1197
1341
  .object({
1198
1342
  apiVersion: z.literal(2),
@@ -1246,11 +1390,15 @@ export const HostSnapshotSchema = z
1246
1390
  })
1247
1391
  .strict(),
1248
1392
  ),
1393
+ /** Present only while some agent's enable or disable request is waiting or has failed. */
1394
+ activationTransitions: z.array(ExtensionActivationTransitionSchema).optional(),
1249
1395
  installation: z
1250
1396
  .object({
1251
1397
  extensionRevisionId: ExtensionRevisionIdSchema,
1252
1398
  installedAt: z.number().int().safe().nonnegative(),
1399
+ /** Secret fields are removed; `configuredSecrets` lists those the Host stores. */
1253
1400
  config: JsonValueSchema.default({}),
1401
+ configuredSecrets: z.array(z.string()).optional(),
1254
1402
  runtime: z
1255
1403
  .object({
1256
1404
  status: z.enum(['active', 'restore-failed', 'dispose-failed']),
@@ -1261,14 +1409,8 @@ export const HostSnapshotSchema = z
1261
1409
  })
1262
1410
  .strict()
1263
1411
  .optional(),
1264
- hostUiPermission: z
1265
- .object({
1266
- declaration: HostUiPermissionDeclarationSchema,
1267
- permissionDigest: z.string().regex(/^[a-f0-9]{64}$/u),
1268
- approvalRequired: z.boolean(),
1269
- })
1270
- .strict()
1271
- .optional(),
1412
+ /** Installing the latest Revision on this machine, against the current host grant. */
1413
+ hostPermission: ExtensionPermissionRequirementSchema.optional(),
1272
1414
  hostClientDiagnostic: z
1273
1415
  .object({
1274
1416
  revisionId: ExtensionRevisionIdSchema,
@@ -1611,6 +1753,22 @@ export const LlmProviderCredentialSchema = z
1611
1753
  .object({ configured: z.boolean(), source: z.string().optional(), writable: z.boolean() })
1612
1754
  .strict()
1613
1755
 
1756
+ /**
1757
+ * The latest connection test that still describes the provider's saved address, protocol and API key. Tests made
1758
+ * before one of those changed are dropped. Never carries a key, request or response body.
1759
+ */
1760
+ export const LlmProviderTestResultSchema = z
1761
+ .object({
1762
+ /** Epoch milliseconds when the test finished. */
1763
+ at: z.number().int().nonnegative(),
1764
+ ok: z.boolean(),
1765
+ /** Short failure reason shown to the user; absent when the test passed. */
1766
+ message: z.string().max(240).optional(),
1767
+ /** The model the test asked. */
1768
+ model: NonEmptyStringSchema.optional(),
1769
+ })
1770
+ .strict()
1771
+
1614
1772
  export const LlmProviderViewSchema = z
1615
1773
  .object({
1616
1774
  provider: NonEmptyStringSchema,
@@ -1629,6 +1787,8 @@ export const LlmProviderViewSchema = z
1629
1787
  modelsCustomized: z.boolean(),
1630
1788
  /** The provider's adapter can list the models its endpoint offers. */
1631
1789
  discoverable: z.boolean(),
1790
+ /** `null` when the current configuration was never tested; absent from hosts that do not record tests. */
1791
+ lastTest: LlmProviderTestResultSchema.nullable().optional(),
1632
1792
  })
1633
1793
  .strict()
1634
1794
 
@@ -1708,7 +1868,7 @@ export const ExtensionImportInspectionSchema = z
1708
1868
  revisionId: ExtensionRevisionIdSchema,
1709
1869
  slug: NonEmptyStringSchema,
1710
1870
  displayName: NonEmptyStringSchema,
1711
- scope: z.enum(['agent', 'host-adapter', 'host-ui']),
1871
+ provides: z.array(ExtensionProvideSchema),
1712
1872
  idempotent: z.boolean(),
1713
1873
  slugConflict: z.boolean(),
1714
1874
  })
@@ -1768,6 +1928,8 @@ export type AgentRevisionHistory = z.output<typeof AgentRevisionHistorySchema>
1768
1928
  const channelParam = z.object({ channelId: ChannelIdSchema }).strict()
1769
1929
  const connectionParam = z.object({ connectionId: ConnectionIdSchema }).strict()
1770
1930
  const agentExtensionParam = z.object({ agentId: AgentIdSchema, extensionId: ExtensionIdSchema }).strict()
1931
+
1932
+ const PermissionApprovalSchema = z.object({ permissionDigest: z.string().regex(/^[a-f0-9]{64}$/u) }).strict()
1771
1933
  const extensionParam = z.object({ extensionId: ExtensionIdSchema }).strict()
1772
1934
  const dshPluginPackageParam = z.object({ packageId: DshPluginPackageIdSchema }).strict()
1773
1935
  const dshPluginEntryParam = z.object({ entryId: DshPluginEntryIdSchema }).strict()
@@ -2103,6 +2265,8 @@ export const HostApiContracts = {
2103
2265
  channelId: ChannelIdSchema,
2104
2266
  triggerPolicy: TriggerPolicySchema,
2105
2267
  processingFeedback: z.enum(['auto', 'off']).optional(),
2268
+ /** Messages from another local agent's account: `observe` keeps them as context unless they @ this agent. */
2269
+ localAgentMessages: z.enum(['observe', 'trigger']).optional(),
2106
2270
  activityTriggerOverrides: ActivityTriggerOverridesSchema.optional(),
2107
2271
  })
2108
2272
  .strict(),
@@ -2112,6 +2276,7 @@ export const HostApiContracts = {
2112
2276
  agentId: AgentIdSchema,
2113
2277
  triggerPolicy: TriggerPolicySchema,
2114
2278
  processingFeedback: z.enum(['auto', 'off']).default('auto'),
2279
+ localAgentMessages: z.enum(['observe', 'trigger']).optional(),
2115
2280
  activityTriggerOverrides: ActivityTriggerOverridesSchema.default({}),
2116
2281
  boundAt: z.number().int().safe().nonnegative(),
2117
2282
  })
@@ -2208,6 +2373,57 @@ export const HostApiContracts = {
2208
2373
  .strict(),
2209
2374
  error: HostApiErrorSchema,
2210
2375
  }),
2376
+ getChannelRuntimeContext: defineContract({
2377
+ method: 'GET',
2378
+ path: '/api/channels/:channelId/runtime/context',
2379
+ params: channelParam,
2380
+ request: NoRequestBodySchema,
2381
+ // What the live session sends the model: route, rendered instructions and tool schemas. Read on demand; the runtime
2382
+ // projection pushed over SSE never carries it. `available: false` when no session of the channel is in memory.
2383
+ response: ChannelRuntimeContextSchema,
2384
+ error: HostApiErrorSchema,
2385
+ }),
2386
+ getChannelRuntimeInput: defineContract({
2387
+ method: 'GET',
2388
+ path: '/api/channels/:channelId/runtime/inputs/:messageId',
2389
+ params: z.object({ channelId: ChannelIdSchema, messageId: z.string().trim().min(1).max(200) }).strict(),
2390
+ request: NoRequestBodySchema,
2391
+ // The full text of one input message as the model received it; the runtime projection only carries a preview.
2392
+ // `available: false` when the session has left memory.
2393
+ response: z
2394
+ .object({
2395
+ messageId: z.string(),
2396
+ available: z.boolean(),
2397
+ source: z.string().optional(),
2398
+ text: z.string().optional(),
2399
+ truncated: z.boolean(),
2400
+ })
2401
+ .strict(),
2402
+ error: HostApiErrorSchema,
2403
+ }),
2404
+ getChannelPrompt: defineContract({
2405
+ method: 'GET',
2406
+ path: '/api/channels/:channelId/prompt',
2407
+ params: channelParam,
2408
+ request: NoRequestBodySchema,
2409
+ response: ChannelPromptViewSchema,
2410
+ error: HostApiErrorSchema,
2411
+ }),
2412
+ updateChannelPrompt: defineContract({
2413
+ method: 'PUT',
2414
+ path: '/api/channels/:channelId/prompt',
2415
+ params: channelParam,
2416
+ request: z
2417
+ .object({
2418
+ document: PromptDocumentV1Schema,
2419
+ locked: z.boolean(),
2420
+ /** The revision the edit started from; a newer stored revision rejects the save. */
2421
+ expectedRevision: z.number().int().nonnegative(),
2422
+ })
2423
+ .strict(),
2424
+ response: ChannelPromptViewSchema,
2425
+ error: HostApiErrorSchema,
2426
+ }),
2211
2427
  resetChannelContext: defineContract({
2212
2428
  invalidatesSnapshot: true,
2213
2429
  method: 'POST',
@@ -2521,7 +2737,11 @@ export const HostApiContracts = {
2521
2737
  params: z
2522
2738
  .object({
2523
2739
  query: z.string().trim().max(80).optional(),
2524
- scope: z.enum(['agent', 'host-adapter', 'host-ui']).optional(),
2740
+ provides: ExtensionProvideSchema.optional(),
2741
+ /** 只列社区后台认定的官方扩展。 */
2742
+ official: z.enum(['1']).optional(),
2743
+ /** 排序方式,默认最近更新;游标只在同一种排序下有效。 */
2744
+ sort: CommunityExtensionSortSchema.optional(),
2525
2745
  cursor: z.string().max(200).optional(),
2526
2746
  })
2527
2747
  .strict(),
@@ -2539,6 +2759,84 @@ export const HostApiContracts = {
2539
2759
  error: HostApiErrorSchema,
2540
2760
  }),
2541
2761
  /** 下载社区发布并按包摘要校验,结果与本地文件导入的检查相同,再经 `commitExtensionImport` 确认。 */
2762
+ getCommunityEndpoint: defineContract({
2763
+ method: 'GET',
2764
+ path: '/api/community/endpoint',
2765
+ params: EmptyParamsSchema,
2766
+ request: NoRequestBodySchema,
2767
+ response: CommunityEndpointSchema,
2768
+ error: HostApiErrorSchema,
2769
+ }),
2770
+ /** 修改社区地址;`url: null` 恢复为环境变量或默认地址。非加密的局域网地址需要确认风险。 */
2771
+ updateCommunityEndpoint: defineContract({
2772
+ method: 'PUT',
2773
+ path: '/api/community/endpoint',
2774
+ params: EmptyParamsSchema,
2775
+ request: z.object({ url: z.string().max(300).nullable(), acknowledgeInsecure: z.boolean() }).strict(),
2776
+ response: CommunityEndpointSchema,
2777
+ error: HostApiErrorSchema,
2778
+ }),
2779
+ testCommunityEndpoint: defineContract({
2780
+ timeoutMs: 30_000,
2781
+ method: 'POST',
2782
+ path: '/api/community/endpoint/test',
2783
+ params: EmptyParamsSchema,
2784
+ request: z.object({ url: z.string().max(300), acknowledgeInsecure: z.boolean() }).strict(),
2785
+ response: z
2786
+ .object({
2787
+ ok: z.boolean(),
2788
+ message: z.string(),
2789
+ name: z.string().optional(),
2790
+ environment: z.enum(['production', 'staging', 'development']).optional(),
2791
+ })
2792
+ .strict(),
2793
+ error: HostApiErrorSchema,
2794
+ }),
2795
+ listCommunityInstalled: defineContract({
2796
+ timeoutMs: 30_000,
2797
+ method: 'GET',
2798
+ path: '/api/community/installed',
2799
+ params: z.object({ refresh: z.enum(['1']).optional() }).strict(),
2800
+ request: NoRequestBodySchema,
2801
+ response: CommunityInstalledSchema,
2802
+ error: HostApiErrorSchema,
2803
+ }),
2804
+ listCommunityMine: defineContract({
2805
+ timeoutMs: 30_000,
2806
+ method: 'GET',
2807
+ path: '/api/community/mine',
2808
+ params: EmptyParamsSchema,
2809
+ request: NoRequestBodySchema,
2810
+ response: z.object({ items: z.array(CommunityMyExtensionSchema) }).strict(),
2811
+ error: HostApiErrorSchema,
2812
+ }),
2813
+ getCommunityReleaseReview: defineContract({
2814
+ timeoutMs: 30_000,
2815
+ method: 'GET',
2816
+ path: '/api/community/releases/:releaseId/review',
2817
+ params: z.object({ releaseId: CommunityReleaseIdSchema }).strict(),
2818
+ request: NoRequestBodySchema,
2819
+ response: CommunityReviewReportSchema,
2820
+ error: HostApiErrorSchema,
2821
+ }),
2822
+ requestCommunityReview: defineContract({
2823
+ timeoutMs: 30_000,
2824
+ method: 'POST',
2825
+ path: '/api/community/releases/:releaseId/review',
2826
+ params: z.object({ releaseId: CommunityReleaseIdSchema }).strict(),
2827
+ request: NoRequestBodySchema,
2828
+ response: z.object({ ok: z.literal(true) }).strict(),
2829
+ error: HostApiErrorSchema,
2830
+ }),
2831
+ withdrawCommunityRelease: defineContract({
2832
+ timeoutMs: 30_000,
2833
+ method: 'POST',
2834
+ path: '/api/community/releases/:releaseId/withdraw',
2835
+ params: z.object({ releaseId: CommunityReleaseIdSchema }).strict(),
2836
+ request: NoRequestBodySchema,
2837
+ response: z.object({ ok: z.literal(true) }).strict(),
2838
+ error: HostApiErrorSchema,
2839
+ }),
2542
2840
  importCommunityRelease: defineContract({
2543
2841
  timeoutMs: 120_000,
2544
2842
  method: 'POST',
@@ -2558,6 +2856,8 @@ export const HostApiContracts = {
2558
2856
  extensionId: ExtensionIdSchema,
2559
2857
  revisionId: ExtensionRevisionIdSchema,
2560
2858
  notes: z.string().max(2000),
2859
+ /** 作者填写的条目信息;省略的字段不改动社区上已有的内容。 */
2860
+ listing: CommunityListingInputSchema.optional(),
2561
2861
  })
2562
2862
  .strict(),
2563
2863
  response: z
@@ -2571,6 +2871,114 @@ export const HostApiContracts = {
2571
2871
  .strict(),
2572
2872
  error: HostApiErrorSchema,
2573
2873
  }),
2874
+ listCommunityPersonas: defineContract({
2875
+ timeoutMs: 30_000,
2876
+ method: 'GET',
2877
+ path: '/api/community/personas',
2878
+ params: z
2879
+ .object({
2880
+ query: z.string().trim().min(1).max(200).optional(),
2881
+ tag: z.string().trim().min(1).max(40).optional(),
2882
+ official: z.literal('1').optional(),
2883
+ cursor: z.string().min(1).max(500).optional(),
2884
+ })
2885
+ .strict(),
2886
+ request: NoRequestBodySchema,
2887
+ response: z.object({ items: z.array(CommunityPersonaSummarySchema), nextCursor: z.string().nullable() }).strict(),
2888
+ error: HostApiErrorSchema,
2889
+ }),
2890
+ getCommunityPersona: defineContract({
2891
+ timeoutMs: 30_000,
2892
+ method: 'GET',
2893
+ path: '/api/community/personas/:personaId',
2894
+ params: z.object({ personaId: CommunityPersonaIdSchema }).strict(),
2895
+ request: NoRequestBodySchema,
2896
+ response: CommunityPersonaDetailSchema,
2897
+ error: HostApiErrorSchema,
2898
+ }),
2899
+ /**
2900
+ * 把社区人设装进本机:新建一个智能体,或为现有智能体保存一次只换设定(及可选名称)的新配置。头像下载后作为
2901
+ * 智能体头像;下载失败不影响安装。`expectedRevisionId` 是用户看过的修订,社区已更新时拒绝,避免装入未看过的内容。
2902
+ */
2903
+ installCommunityPersona: defineContract({
2904
+ invalidatesSnapshot: true,
2905
+ timeoutMs: 60_000,
2906
+ method: 'POST',
2907
+ path: '/api/community/personas/:personaId/install',
2908
+ params: z.object({ personaId: CommunityPersonaIdSchema }).strict(),
2909
+ request: z.discriminatedUnion('target', [
2910
+ z
2911
+ .object({
2912
+ target: z.literal('new'),
2913
+ expectedRevisionId: CommunityPersonaRevisionIdSchema,
2914
+ displayName: z.string().trim().min(1).max(80),
2915
+ model: AgentModelSchema,
2916
+ useAvatar: z.boolean(),
2917
+ })
2918
+ .strict(),
2919
+ z
2920
+ .object({
2921
+ target: z.literal('replace'),
2922
+ expectedRevisionId: CommunityPersonaRevisionIdSchema,
2923
+ agentId: AgentIdSchema,
2924
+ expectedCurrentRevisionId: AgentRevisionIdSchema,
2925
+ /** 同时改名;省略时保留原名称。 */
2926
+ displayName: z.string().trim().min(1).max(80).optional(),
2927
+ useAvatar: z.boolean(),
2928
+ })
2929
+ .strict(),
2930
+ ]),
2931
+ response: z
2932
+ .object({
2933
+ agentId: AgentIdSchema,
2934
+ channelId: ChannelIdSchema.nullable(),
2935
+ currentRevisionId: AgentRevisionIdSchema,
2936
+ avatar: z.enum(['applied', 'none', 'failed']),
2937
+ })
2938
+ .strict(),
2939
+ error: HostApiErrorSchema,
2940
+ }),
2941
+ listCommunityMyPersonas: defineContract({
2942
+ timeoutMs: 30_000,
2943
+ method: 'GET',
2944
+ path: '/api/community/mine/personas',
2945
+ params: EmptyParamsSchema,
2946
+ request: NoRequestBodySchema,
2947
+ response: CommunityMyPersonasSchema,
2948
+ error: HostApiErrorSchema,
2949
+ }),
2950
+ /** 把智能体的设定分享到社区;带 `personaId` 时更新自己已发布的人设(生成新修订并重新审查)。 */
2951
+ publishCommunityPersona: defineContract({
2952
+ timeoutMs: 60_000,
2953
+ method: 'POST',
2954
+ path: '/api/community/personas/publish',
2955
+ params: EmptyParamsSchema,
2956
+ request: z
2957
+ .object({
2958
+ agentId: AgentIdSchema,
2959
+ personaId: CommunityPersonaIdSchema.optional(),
2960
+ name: z.string().trim().min(1).max(80),
2961
+ summary: z.string().trim().min(1).max(200),
2962
+ description: z.string().max(20_000),
2963
+ tags: z.array(z.string().trim().min(1).max(40)).max(16),
2964
+ persona: z.string().trim().min(1).max(COMMUNITY_PERSONA_MAX_LENGTH),
2965
+ notes: z.string().max(2000).optional(),
2966
+ /** 页面缩放后的头像(Base64);省略时不上传头像,更新时保留社区上的原头像。 */
2967
+ avatar: z
2968
+ .object({
2969
+ mediaType: z.enum(COMMUNITY_PERSONA_AVATAR_TYPES),
2970
+ base64: z
2971
+ .string()
2972
+ .min(1)
2973
+ .max(Math.ceil((512 * 1024 * 4) / 3) + 4),
2974
+ })
2975
+ .strict()
2976
+ .optional(),
2977
+ })
2978
+ .strict(),
2979
+ response: CommunityMyPersonaSchema,
2980
+ error: HostApiErrorSchema,
2981
+ }),
2574
2982
  testMcpServer: defineContract({
2575
2983
  timeoutMs: 30_000,
2576
2984
  method: 'POST',
@@ -3027,8 +3435,19 @@ export const HostApiContracts = {
3027
3435
  method: 'POST',
3028
3436
  path: '/api/extensions/:extensionId/revisions/:revisionId/call',
3029
3437
  params: extensionRevisionParam,
3438
+ /**
3439
+ * A panel's call to its extension's host instance. The Host checks the anchor (the agent has the extension
3440
+ * enabled; the channel or connection exists) and hands the extension the resolved caller.
3441
+ */
3030
3442
  request: z
3031
- .object({ agentId: AgentIdSchema, method: NonEmptyStringSchema, input: JsonValueSchema.optional() })
3443
+ .object({
3444
+ /** Absent: the call comes from the extension itself, like a page call. */
3445
+ anchor: z.object({ kind: ExtensionPanelAnchorSchema, id: NonEmptyStringSchema }).strict().optional(),
3446
+ /** The agent the panel is shown for; must be the responding agent of an anchored channel. */
3447
+ agentId: AgentIdSchema.optional(),
3448
+ method: NonEmptyStringSchema,
3449
+ input: JsonValueSchema.optional(),
3450
+ })
3032
3451
  .strict(),
3033
3452
  response: z.object({ value: JsonValueSchema }).strict(),
3034
3453
  error: HostApiErrorSchema,
@@ -3063,12 +3482,16 @@ export const HostApiContracts = {
3063
3482
  request: z
3064
3483
  .object({
3065
3484
  revisionId: ExtensionRevisionIdSchema,
3066
- permissionApproval: z
3067
- .object({ permissionDigest: z.string().regex(/^[a-f0-9]{64}$/u) })
3068
- .strict()
3069
- .optional(),
3485
+ /** Approval of the agent-layer capabilities. */
3486
+ permissionApproval: PermissionApprovalSchema.optional(),
3487
+ /** Approval of the host layer when enabling also installs or switches the extension on this machine. */
3488
+ hostPermissionApproval: PermissionApprovalSchema.optional(),
3070
3489
  })
3071
3490
  .strict(),
3491
+ /**
3492
+ * The committed Activation, or — while the agent is still replying — `pending` with the accepted request; it then
3493
+ * takes effect at the agent's next safe gap and its progress appears in the snapshot's `activationTransitions`.
3494
+ */
3072
3495
  response: z
3073
3496
  .object({
3074
3497
  activation: z
@@ -3080,9 +3503,12 @@ export const HostApiContracts = {
3080
3503
  configuredSecrets: z.array(z.string()).optional(),
3081
3504
  activatedAt: z.number().int().safe().nonnegative(),
3082
3505
  })
3083
- .strict(),
3506
+ .strict()
3507
+ .optional(),
3508
+ pending: ExtensionActivationTransitionSchema.optional(),
3084
3509
  })
3085
- .strict(),
3510
+ .strict()
3511
+ .refine((value) => (value.activation === undefined) !== (value.pending === undefined), '启用结果必须二选一。'),
3086
3512
  error: HostApiErrorSchema,
3087
3513
  }),
3088
3514
  deactivateExtension: defineContract({
@@ -3091,7 +3517,8 @@ export const HostApiContracts = {
3091
3517
  path: '/api/agents/:agentId/extensions/:extensionId/activation',
3092
3518
  params: agentExtensionParam,
3093
3519
  request: NoRequestBodySchema,
3094
- response: z.object({ disabled: z.literal(true) }).strict(),
3520
+ /** `pending` is present when the agent is still replying; the Activation is removed at its next safe gap. */
3521
+ response: z.object({ disabled: z.literal(true), pending: ExtensionActivationTransitionSchema.optional() }).strict(),
3095
3522
  error: HostApiErrorSchema,
3096
3523
  }),
3097
3524
  updateExtensionActivationConfig: defineContract({
@@ -3115,8 +3542,14 @@ export const HostApiContracts = {
3115
3542
  method: 'PUT',
3116
3543
  path: '/api/extensions/:extensionId/installation/config',
3117
3544
  params: extensionParam,
3118
- request: z.object({ config: JsonValueSchema }).strict(),
3119
- response: z.object({ config: JsonValueSchema }).strict(),
3545
+ request: z
3546
+ .object({
3547
+ config: JsonValueSchema,
3548
+ /** Write-only credential drafts keyed by secret field; an absent or empty draft keeps the stored one. */
3549
+ secrets: z.record(z.string(), z.string().max(8192)).optional(),
3550
+ })
3551
+ .strict(),
3552
+ response: z.object({ config: JsonValueSchema, configuredSecrets: z.array(z.string()).optional() }).strict(),
3120
3553
  error: HostApiErrorSchema,
3121
3554
  }),
3122
3555
  installHostExtension: defineContract({
@@ -3128,10 +3561,10 @@ export const HostApiContracts = {
3128
3561
  request: z
3129
3562
  .object({
3130
3563
  revisionId: ExtensionRevisionIdSchema,
3131
- permissionApproval: z
3132
- .object({ permissionDigest: z.string().regex(/^[a-f0-9]{64}$/u) })
3133
- .strict()
3134
- .optional(),
3564
+ /** Approval of the host layer (browser permissions and `permissions.host`). */
3565
+ permissionApproval: PermissionApprovalSchema.optional(),
3566
+ /** Approval of expanded agent-layer capabilities for the agents that move to this Revision. */
3567
+ agentPermissionApproval: PermissionApprovalSchema.optional(),
3135
3568
  })
3136
3569
  .strict(),
3137
3570
  response: z