@nekro-nxt/contracts 0.5.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +1433 -194
- package/dist/index.mjs.map +1 -1
- package/lib/types/community.d.ts +9 -0
- package/lib/types/community.d.ts.map +1 -1
- package/lib/types/config-schema.d.ts +16 -2
- package/lib/types/config-schema.d.ts.map +1 -1
- package/lib/types/credentials.d.ts +328 -0
- package/lib/types/credentials.d.ts.map +1 -0
- package/lib/types/domain.d.ts +11 -2
- package/lib/types/domain.d.ts.map +1 -1
- package/lib/types/extension-capabilities.d.ts +151 -5
- package/lib/types/extension-capabilities.d.ts.map +1 -1
- package/lib/types/extension-ui.d.ts +117 -6
- package/lib/types/extension-ui.d.ts.map +1 -1
- package/lib/types/host-api.d.ts +3494 -603
- package/lib/types/host-api.d.ts.map +1 -1
- package/lib/types/index.d.ts +3 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/linked-accounts.d.ts +100 -0
- package/lib/types/linked-accounts.d.ts.map +1 -0
- package/lib/types/response-timing.d.ts +60 -0
- package/lib/types/response-timing.d.ts.map +1 -0
- package/lib/types/runtime-compatibility.d.ts +2 -2
- package/package.json +1 -1
- package/src/community.ts +7 -0
- package/src/config-schema.ts +33 -2
- package/src/credentials.ts +260 -0
- package/src/domain.ts +15 -2
- package/src/extension-capabilities.ts +260 -8
- package/src/extension-ui.ts +16 -1
- package/src/host-api.ts +911 -167
- package/src/index.ts +3 -0
- package/src/linked-accounts.ts +77 -0
- package/src/management-api.ts +1 -1
- package/src/response-timing.ts +77 -0
|
@@ -6,8 +6,19 @@ import { z } from 'zod'
|
|
|
6
6
|
* ships; never reuse a level for a different meaning. Level 7 is Manifest V7 (host instance + agent attachments); level 8 adds the asset library, image info, tool result
|
|
7
7
|
* images, the search index and host-layer model calls. Level 9 adds the structured logger and explicit chat commands. Level 10 adds version lineage, the data version with its
|
|
8
8
|
* migration hook and administrator-only commands. Level 11 lists the pictures of an inbound message to inbound hooks.
|
|
9
|
+
* Level 12 adds credential placeholders (`secrets.placeholder`) that `http.fetch` fills for the credential's domains.
|
|
10
|
+
* Level 13 adds outbound processing (`harness.onOutbound`, `permissions.agent.outbound`).
|
|
11
|
+
* Level 14 adds work files (`files`), reading Asset bytes (`assets.read`), high-detail tool images (`toolImages`),
|
|
12
|
+
* long-running tools (`timeoutSeconds`, `execute(args, { signal, progress })`), media processing (`media`), command
|
|
13
|
+
* profiles (`commands`) and speech to text (`speech`).
|
|
9
14
|
*/
|
|
10
|
-
export const EXTENSION_SDK_LEVEL =
|
|
15
|
+
export const EXTENSION_SDK_LEVEL = 14
|
|
16
|
+
|
|
17
|
+
/** Work files, Asset reads, high-detail tool images, tool time limits, media, commands and speech need this level. */
|
|
18
|
+
export const EXTENSION_FILES_SDK_LEVEL = 14
|
|
19
|
+
|
|
20
|
+
/** Outbound processing needs this level; packages that declare `permissions.agent.outbound` declare it. */
|
|
21
|
+
export const EXTENSION_OUTBOUND_SDK_LEVEL = 13
|
|
11
22
|
|
|
12
23
|
/** Lineage, `dataVersion` and administrator-only commands need this level; packages that carry them declare it. */
|
|
13
24
|
export const EXTENSION_GOVERNANCE_SDK_LEVEL = 10
|
|
@@ -23,6 +34,9 @@ export const EXTENSION_SDK_RELEASES: readonly { readonly level: number; readonly
|
|
|
23
34
|
{ level: 9, version: '0.6.0' },
|
|
24
35
|
{ level: 10, version: '0.6.0' },
|
|
25
36
|
{ level: 11, version: '0.6.0' },
|
|
37
|
+
{ level: 12, version: '0.7.0' },
|
|
38
|
+
{ level: 13, version: '0.7.0' },
|
|
39
|
+
{ level: 14, version: '0.8.0' },
|
|
26
40
|
]
|
|
27
41
|
|
|
28
42
|
/** The oldest NekroNXT version that supports `level`; undefined for a level no release supports yet. */
|
|
@@ -128,6 +142,32 @@ export const ExtensionInboundHookCapabilitySchema = z
|
|
|
128
142
|
.strict()
|
|
129
143
|
export type ExtensionInboundHookCapability = z.output<typeof ExtensionInboundHookCapabilitySchema>
|
|
130
144
|
|
|
145
|
+
export const EXTENSION_OUTBOUND_MAX_TIMEOUT_MS = 15_000
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Outbound processing of the agent's own messages. `read` sees each message after it was delivered; `transform` may
|
|
149
|
+
* replace its content or append messages before it is sent; `cancel` may stop it. `onTimeout` decides what happens
|
|
150
|
+
* when the handler times out or throws before sending: `send` (default) sends the message as it is.
|
|
151
|
+
*/
|
|
152
|
+
export const ExtensionOutboundCapabilitySchema = z
|
|
153
|
+
.object({
|
|
154
|
+
read: z.literal(true).optional(),
|
|
155
|
+
transform: z.literal(true).optional(),
|
|
156
|
+
cancel: z.literal(true).optional(),
|
|
157
|
+
timeoutMs: z.number().int().min(100).max(EXTENSION_OUTBOUND_MAX_TIMEOUT_MS),
|
|
158
|
+
onTimeout: z.enum(['send', 'cancel']).optional(),
|
|
159
|
+
})
|
|
160
|
+
.strict()
|
|
161
|
+
.refine(
|
|
162
|
+
(value) => value.read === true || value.transform === true || value.cancel === true,
|
|
163
|
+
'outbound 至少声明 read、transform 或 cancel 中的一项。',
|
|
164
|
+
)
|
|
165
|
+
export type ExtensionOutboundCapability = z.output<typeof ExtensionOutboundCapabilitySchema>
|
|
166
|
+
|
|
167
|
+
/** Whether the extension takes part before sending (and so in the agent's processing order). */
|
|
168
|
+
export const outboundProcessesBeforeSend = (outbound: ExtensionOutboundCapability | undefined): boolean =>
|
|
169
|
+
outbound?.transform === true || outbound?.cancel === true
|
|
170
|
+
|
|
131
171
|
export const EXTENSION_JOB_MAX_RUNTIME = 50
|
|
132
172
|
|
|
133
173
|
export const ExtensionDeclaredJobSchema = z
|
|
@@ -192,17 +232,92 @@ export type ExtensionAssetLibraryCapability = z.output<typeof ExtensionAssetLibr
|
|
|
192
232
|
|
|
193
233
|
/**
|
|
194
234
|
* `write` creates Assets in the current channel; `library` keeps Assets in the extension's own library so they can be
|
|
195
|
-
* used in any channel later.
|
|
235
|
+
* used in any channel later; `read` reads the bytes of Assets the current channel can see or the library holds.
|
|
196
236
|
*/
|
|
197
237
|
export const ExtensionAssetsCapabilitySchema = z
|
|
198
238
|
.object({
|
|
199
239
|
write: z.literal(true).optional(),
|
|
200
240
|
library: ExtensionAssetLibraryCapabilitySchema.optional(),
|
|
241
|
+
read: z.literal(true).optional(),
|
|
201
242
|
})
|
|
202
243
|
.strict()
|
|
203
|
-
.refine(
|
|
244
|
+
.refine(
|
|
245
|
+
(value) => value.write !== undefined || value.library !== undefined || value.read !== undefined,
|
|
246
|
+
'assets 至少声明 write、library 或 read。',
|
|
247
|
+
)
|
|
204
248
|
export type ExtensionAssetsCapability = z.output<typeof ExtensionAssetsCapabilitySchema>
|
|
205
249
|
|
|
250
|
+
export const EXTENSION_FILES_DEFAULT_TEMP_QUOTA_BYTES = 1024 * 1024 * 1024
|
|
251
|
+
export const EXTENSION_FILES_DEFAULT_CACHE_QUOTA_BYTES = 1024 * 1024 * 1024
|
|
252
|
+
export const EXTENSION_FILES_MAX_QUOTA_BYTES = 16 * 1024 * 1024 * 1024
|
|
253
|
+
|
|
254
|
+
const FilesQuotaSchema = z
|
|
255
|
+
.number()
|
|
256
|
+
.int()
|
|
257
|
+
.min(1024 * 1024)
|
|
258
|
+
.max(EXTENSION_FILES_MAX_QUOTA_BYTES)
|
|
259
|
+
|
|
260
|
+
/**
|
|
261
|
+
* Work files passed between services by `fileId`. Temporary files expire a day after they are written; cache entries
|
|
262
|
+
* stay until the cache quota is full, then the least recently used go first.
|
|
263
|
+
*/
|
|
264
|
+
export const ExtensionFilesCapabilitySchema = z
|
|
265
|
+
.object({ tempQuotaBytes: FilesQuotaSchema.optional(), cacheQuotaBytes: FilesQuotaSchema.optional() })
|
|
266
|
+
.strict()
|
|
267
|
+
export type ExtensionFilesCapability = z.output<typeof ExtensionFilesCapabilitySchema>
|
|
268
|
+
|
|
269
|
+
/** Tool result pictures with `detail: 'high'` reach the model at up to 2048 px on the long side. */
|
|
270
|
+
export const ExtensionToolImagesCapabilitySchema = z.object({ highDetail: z.literal(true) }).strict()
|
|
271
|
+
export type ExtensionToolImagesCapability = z.output<typeof ExtensionToolImagesCapabilitySchema>
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Add-ons (附加功能) whose capability an extension can use. The ids match `AddonIdSchema` in host-api; they are
|
|
275
|
+
* repeated here so manifests validate without it.
|
|
276
|
+
*/
|
|
277
|
+
export type ExtensionAddonId = 'media' | 'speech'
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* Command profiles extensions may run (`nxt.commands`, 附加功能 F7), and the add-on that provides each program. A new
|
|
281
|
+
* program needs a profile on the server and a row here.
|
|
282
|
+
*/
|
|
283
|
+
export const ExtensionCommandProfileIdSchema = z.enum(['ffmpeg', 'ffprobe'], {
|
|
284
|
+
message: '命令档案只能是 ffmpeg、ffprobe。',
|
|
285
|
+
})
|
|
286
|
+
export type ExtensionCommandProfileId = z.output<typeof ExtensionCommandProfileIdSchema>
|
|
287
|
+
|
|
288
|
+
export const EXTENSION_COMMAND_PROFILES: Readonly<Record<ExtensionCommandProfileId, ExtensionAddonId>> = {
|
|
289
|
+
ffmpeg: 'media',
|
|
290
|
+
ffprobe: 'media',
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/** Programs run through their command profiles: confined to the call's own files, without network. */
|
|
294
|
+
export const ExtensionCommandsCapabilitySchema = z
|
|
295
|
+
.object({
|
|
296
|
+
profiles: z
|
|
297
|
+
.array(ExtensionCommandProfileIdSchema)
|
|
298
|
+
.min(1)
|
|
299
|
+
.max(ExtensionCommandProfileIdSchema.options.length)
|
|
300
|
+
.refine((value) => new Set(value).size === value.length, '命令档案不能重复。'),
|
|
301
|
+
})
|
|
302
|
+
.strict()
|
|
303
|
+
export type ExtensionCommandsCapability = z.output<typeof ExtensionCommandsCapabilitySchema>
|
|
304
|
+
|
|
305
|
+
export const EXTENSION_SPEECH_MAX_CALLS_PER_MINUTE = 60
|
|
306
|
+
export const EXTENSION_SPEECH_DEFAULT_CALLS_PER_MINUTE = 10
|
|
307
|
+
|
|
308
|
+
/** Speech to text with the provider chosen in 设置 → 附加功能, which may be a cloud service. */
|
|
309
|
+
export const ExtensionSpeechCapabilitySchema = z
|
|
310
|
+
.object({
|
|
311
|
+
maxCallsPerMinute: z
|
|
312
|
+
.number()
|
|
313
|
+
.int()
|
|
314
|
+
.min(1)
|
|
315
|
+
.max(EXTENSION_SPEECH_MAX_CALLS_PER_MINUTE)
|
|
316
|
+
.default(EXTENSION_SPEECH_DEFAULT_CALLS_PER_MINUTE),
|
|
317
|
+
})
|
|
318
|
+
.strict()
|
|
319
|
+
export type ExtensionSpeechCapability = z.output<typeof ExtensionSpeechCapabilitySchema>
|
|
320
|
+
|
|
206
321
|
const IndexNameSchema = z
|
|
207
322
|
.string()
|
|
208
323
|
.trim()
|
|
@@ -255,10 +370,14 @@ export const ExtensionModelsCapabilitySchema = z
|
|
|
255
370
|
.strict()
|
|
256
371
|
export type ExtensionModelsCapability = z.output<typeof ExtensionModelsCapabilitySchema>
|
|
257
372
|
|
|
258
|
-
/**
|
|
373
|
+
/**
|
|
374
|
+
* A header or environment value: literal text, the current value of a `meta.role: 'secret'` config field, or a named
|
|
375
|
+
* credential (by id or name) that the agent may use.
|
|
376
|
+
*/
|
|
259
377
|
export const ExtensionMcpValueSchema = z.union([
|
|
260
378
|
z.string().max(4096),
|
|
261
379
|
z.object({ secret: z.string().trim().min(1).max(64) }).strict(),
|
|
380
|
+
z.object({ credential: z.string().trim().min(1).max(64) }).strict(),
|
|
262
381
|
])
|
|
263
382
|
export type ExtensionMcpValue = z.output<typeof ExtensionMcpValueSchema>
|
|
264
383
|
|
|
@@ -315,6 +434,8 @@ const McpFormValueSchema = z
|
|
|
315
434
|
name: z.string().regex(/^[A-Za-z_][A-Za-z0-9_-]{0,127}$/u, '名称只能包含字母、数字、下划线和连字符。'),
|
|
316
435
|
value: z.string().max(4096),
|
|
317
436
|
secret: z.boolean().default(false),
|
|
437
|
+
/** Takes the value from this named credential instead; `value` and `secret` are then ignored. */
|
|
438
|
+
credential: z.string().trim().min(1).max(64).optional(),
|
|
318
439
|
})
|
|
319
440
|
.strict()
|
|
320
441
|
|
|
@@ -344,7 +465,7 @@ export const mcpSecretFields = (mcp: ExtensionMcpCapability | undefined): readon
|
|
|
344
465
|
...new Set(
|
|
345
466
|
(mcp?.servers ?? []).flatMap((server) =>
|
|
346
467
|
Object.values(server.transport === 'stdio' ? server.env : server.headers).flatMap((value) =>
|
|
347
|
-
typeof value === 'string' ? [] : [value.secret],
|
|
468
|
+
typeof value === 'string' || !('secret' in value) ? [] : [value.secret],
|
|
348
469
|
),
|
|
349
470
|
),
|
|
350
471
|
),
|
|
@@ -365,6 +486,12 @@ export const ExtensionCapabilitiesSchema = z
|
|
|
365
486
|
network: ExtensionNetworkCapabilitySchema.optional(),
|
|
366
487
|
storage: ExtensionStorageCapabilitySchema.optional(),
|
|
367
488
|
assets: ExtensionAssetsCapabilitySchema.optional(),
|
|
489
|
+
files: ExtensionFilesCapabilitySchema.optional(),
|
|
490
|
+
toolImages: ExtensionToolImagesCapabilitySchema.optional(),
|
|
491
|
+
/** `nxt.media`: probe, frames, audio and convert on the media add-on. */
|
|
492
|
+
media: z.literal(true).optional(),
|
|
493
|
+
commands: ExtensionCommandsCapabilitySchema.optional(),
|
|
494
|
+
speech: ExtensionSpeechCapabilitySchema.optional(),
|
|
368
495
|
index: ExtensionIndexCapabilitySchema.optional(),
|
|
369
496
|
/** Host layer only; the agent layer uses `llm`, which runs on the agent's own model. */
|
|
370
497
|
models: ExtensionModelsCapabilitySchema.optional(),
|
|
@@ -373,6 +500,7 @@ export const ExtensionCapabilitiesSchema = z
|
|
|
373
500
|
.strict()
|
|
374
501
|
.optional(),
|
|
375
502
|
inboundHook: ExtensionInboundHookCapabilitySchema.optional(),
|
|
503
|
+
outbound: ExtensionOutboundCapabilitySchema.optional(),
|
|
376
504
|
jobs: ExtensionJobsCapabilitySchema.optional(),
|
|
377
505
|
platform: ExtensionPlatformCapabilitySchema.optional(),
|
|
378
506
|
llm: ExtensionLlmCapabilitySchema.optional(),
|
|
@@ -397,8 +525,19 @@ export const HostLayerCapabilitiesSchema = z
|
|
|
397
525
|
.object({ quotaBytes: z.number().int().min(1024).max(EXTENSION_STORAGE_MAX_QUOTA_BYTES).optional() })
|
|
398
526
|
.strict()
|
|
399
527
|
.optional(),
|
|
400
|
-
/**
|
|
401
|
-
|
|
528
|
+
/**
|
|
529
|
+
* The same library agent attachments use; the host instance lists and imports into it. `read` reads the bytes of
|
|
530
|
+
* library Assets.
|
|
531
|
+
*/
|
|
532
|
+
assets: z
|
|
533
|
+
.object({ library: ExtensionAssetLibraryCapabilitySchema.optional(), read: z.literal(true).optional() })
|
|
534
|
+
.strict()
|
|
535
|
+
.refine((value) => value.library !== undefined || value.read !== undefined, 'assets 至少声明 library 或 read。')
|
|
536
|
+
.optional(),
|
|
537
|
+
files: ExtensionFilesCapabilitySchema.optional(),
|
|
538
|
+
media: z.literal(true).optional(),
|
|
539
|
+
commands: ExtensionCommandsCapabilitySchema.optional(),
|
|
540
|
+
speech: ExtensionSpeechCapabilitySchema.optional(),
|
|
402
541
|
/** The same collections agent attachments declare; pages search and maintain them. */
|
|
403
542
|
index: ExtensionIndexCapabilitySchema.optional(),
|
|
404
543
|
models: ExtensionModelsCapabilitySchema.optional(),
|
|
@@ -420,7 +559,18 @@ export const hostLayerAsCapabilities = (host: HostLayerCapabilities | undefined)
|
|
|
420
559
|
...(host.storage.quotaBytes === undefined ? {} : { quotaBytes: host.storage.quotaBytes }),
|
|
421
560
|
},
|
|
422
561
|
}),
|
|
423
|
-
...(host.assets === undefined
|
|
562
|
+
...(host.assets === undefined
|
|
563
|
+
? {}
|
|
564
|
+
: {
|
|
565
|
+
assets: {
|
|
566
|
+
...(host.assets.library === undefined ? {} : { library: host.assets.library }),
|
|
567
|
+
...(host.assets.read === undefined ? {} : { read: host.assets.read }),
|
|
568
|
+
},
|
|
569
|
+
}),
|
|
570
|
+
...(host.files === undefined ? {} : { files: host.files }),
|
|
571
|
+
...(host.media === undefined ? {} : { media: host.media }),
|
|
572
|
+
...(host.commands === undefined ? {} : { commands: host.commands }),
|
|
573
|
+
...(host.speech === undefined ? {} : { speech: host.speech }),
|
|
424
574
|
...(host.index === undefined ? {} : { index: host.index }),
|
|
425
575
|
...(host.models === undefined ? {} : { models: host.models }),
|
|
426
576
|
}
|
|
@@ -459,6 +609,19 @@ export const extensionCapabilitiesExpand = (
|
|
|
459
609
|
if (next.storage?.scopes.some((scope) => !(previous?.storage?.scopes ?? []).includes(scope))) return true
|
|
460
610
|
if (next.assets?.write !== undefined && previous?.assets?.write === undefined) return true
|
|
461
611
|
if (next.assets?.library !== undefined && previous?.assets?.library === undefined) return true
|
|
612
|
+
if (next.assets?.read !== undefined && previous?.assets?.read === undefined) return true
|
|
613
|
+
if (next.files !== undefined && previous?.files === undefined) return true
|
|
614
|
+
if (next.toolImages !== undefined && previous?.toolImages === undefined) return true
|
|
615
|
+
if (next.media !== undefined && previous?.media === undefined) return true
|
|
616
|
+
if ((next.commands?.profiles ?? []).some((profile) => !(previous?.commands?.profiles ?? []).includes(profile))) {
|
|
617
|
+
return true
|
|
618
|
+
}
|
|
619
|
+
if (
|
|
620
|
+
next.speech !== undefined &&
|
|
621
|
+
(previous?.speech === undefined || next.speech.maxCallsPerMinute > previous.speech.maxCallsPerMinute)
|
|
622
|
+
) {
|
|
623
|
+
return true
|
|
624
|
+
}
|
|
462
625
|
if (
|
|
463
626
|
next.models !== undefined &&
|
|
464
627
|
(previous?.models === undefined ||
|
|
@@ -484,6 +647,18 @@ export const extensionCapabilitiesExpand = (
|
|
|
484
647
|
) {
|
|
485
648
|
return true
|
|
486
649
|
}
|
|
650
|
+
const outbound = next.outbound
|
|
651
|
+
const previousOutbound = previous?.outbound
|
|
652
|
+
if (
|
|
653
|
+
outbound !== undefined &&
|
|
654
|
+
(previousOutbound === undefined ||
|
|
655
|
+
(outbound.read === true && previousOutbound.read !== true) ||
|
|
656
|
+
(outbound.transform === true && previousOutbound.transform !== true) ||
|
|
657
|
+
(outbound.cancel === true && previousOutbound.cancel !== true) ||
|
|
658
|
+
(outbound.onTimeout === 'cancel' && previousOutbound.onTimeout !== 'cancel'))
|
|
659
|
+
) {
|
|
660
|
+
return true
|
|
661
|
+
}
|
|
487
662
|
if (
|
|
488
663
|
next.jobs?.runtime !== undefined &&
|
|
489
664
|
(previous?.jobs?.runtime === undefined || next.jobs.runtime.maxActive > previous.jobs.runtime.maxActive)
|
|
@@ -516,6 +691,25 @@ export interface ExtensionCapabilitySummaryItem {
|
|
|
516
691
|
readonly risk: ExtensionCapabilityRisk
|
|
517
692
|
readonly label: string
|
|
518
693
|
readonly detail?: string
|
|
694
|
+
/** For a high-risk capability with limits: what it stays within, shown where the user accepts the risk. */
|
|
695
|
+
readonly scope?: string
|
|
696
|
+
}
|
|
697
|
+
|
|
698
|
+
/**
|
|
699
|
+
* Add-ons the declared capabilities need (附加功能 F13), in a stable order: `media` needs 媒体处理, `speech` needs
|
|
700
|
+
* 语音识别, and each command profile needs the add-on that provides its program. Pass both layers, host-layer ones
|
|
701
|
+
* through {@link hostLayerAsCapabilities}.
|
|
702
|
+
*/
|
|
703
|
+
export const requiredExtensionAddons = (
|
|
704
|
+
...layers: readonly (ExtensionCapabilities | undefined)[]
|
|
705
|
+
): readonly ExtensionAddonId[] => {
|
|
706
|
+
const needed = new Set<ExtensionAddonId>()
|
|
707
|
+
for (const capabilities of layers) {
|
|
708
|
+
if (capabilities?.media !== undefined) needed.add('media')
|
|
709
|
+
if (capabilities?.speech !== undefined) needed.add('speech')
|
|
710
|
+
for (const profile of capabilities?.commands?.profiles ?? []) needed.add(EXTENSION_COMMAND_PROFILES[profile])
|
|
711
|
+
}
|
|
712
|
+
return (['media', 'speech'] as const).filter((id) => needed.has(id))
|
|
519
713
|
}
|
|
520
714
|
|
|
521
715
|
/**
|
|
@@ -565,6 +759,49 @@ export const summarizeExtensionCapabilities = (
|
|
|
565
759
|
detail: `最多 ${formatLibraryQuota(capabilities.assets.library.quotaBytes ?? EXTENSION_ASSET_LIBRARY_DEFAULT_QUOTA_BYTES)}`,
|
|
566
760
|
})
|
|
567
761
|
}
|
|
762
|
+
if (capabilities.assets?.read !== undefined) {
|
|
763
|
+
items.push(
|
|
764
|
+
options.layer === 'host'
|
|
765
|
+
? { key: 'assets.read', risk: 'normal', label: '读取扩展资源库里的文件' }
|
|
766
|
+
: { key: 'assets.read', risk: 'sensitive', label: '读取当前频道里的图片和文件' },
|
|
767
|
+
)
|
|
768
|
+
}
|
|
769
|
+
if (capabilities.files !== undefined) {
|
|
770
|
+
items.push({
|
|
771
|
+
key: 'files',
|
|
772
|
+
risk: 'normal',
|
|
773
|
+
label: '在本机存放临时文件和缓存',
|
|
774
|
+
detail: `临时文件最多 ${formatLibraryQuota(capabilities.files.tempQuotaBytes ?? EXTENSION_FILES_DEFAULT_TEMP_QUOTA_BYTES)},缓存最多 ${formatLibraryQuota(capabilities.files.cacheQuotaBytes ?? EXTENSION_FILES_DEFAULT_CACHE_QUOTA_BYTES)}`,
|
|
775
|
+
})
|
|
776
|
+
}
|
|
777
|
+
if (capabilities.toolImages !== undefined) {
|
|
778
|
+
items.push({
|
|
779
|
+
key: 'toolImages',
|
|
780
|
+
risk: 'normal',
|
|
781
|
+
label: '以高细节向智能体展示图片',
|
|
782
|
+
detail: '计入智能体用量',
|
|
783
|
+
})
|
|
784
|
+
}
|
|
785
|
+
if (capabilities.media !== undefined) {
|
|
786
|
+
items.push({ key: 'media', risk: 'normal', label: '处理音频、视频和图片' })
|
|
787
|
+
}
|
|
788
|
+
if (capabilities.commands !== undefined) {
|
|
789
|
+
items.push({
|
|
790
|
+
key: 'commands',
|
|
791
|
+
risk: 'high',
|
|
792
|
+
label: '在本机运行程序',
|
|
793
|
+
detail: capabilities.commands.profiles.join('、'),
|
|
794
|
+
scope: '只能读写交给它的文件,不能联网',
|
|
795
|
+
})
|
|
796
|
+
}
|
|
797
|
+
if (capabilities.speech !== undefined) {
|
|
798
|
+
items.push({
|
|
799
|
+
key: 'speech',
|
|
800
|
+
risk: 'sensitive',
|
|
801
|
+
label: '把音频转成文字',
|
|
802
|
+
detail: `音频可能发给「设置 → 附加功能」里选择的语音识别服务,每分钟最多 ${capabilities.speech.maxCallsPerMinute} 次`,
|
|
803
|
+
})
|
|
804
|
+
}
|
|
568
805
|
if (capabilities.index !== undefined) {
|
|
569
806
|
items.push({
|
|
570
807
|
key: 'index',
|
|
@@ -598,6 +835,21 @@ export const summarizeExtensionCapabilities = (
|
|
|
598
835
|
...(effects === '' ? {} : { detail: `可以${effects}` }),
|
|
599
836
|
})
|
|
600
837
|
}
|
|
838
|
+
const outbound = capabilities.outbound
|
|
839
|
+
if (outbound !== undefined) {
|
|
840
|
+
if (outbound.transform === true) {
|
|
841
|
+
items.push({ key: 'outbound.transform', risk: 'high', label: '在智能体的消息发出前修改内容或追加消息' })
|
|
842
|
+
}
|
|
843
|
+
if (outbound.cancel === true) {
|
|
844
|
+
items.push({ key: 'outbound.cancel', risk: 'high', label: '取消智能体要发出的消息' })
|
|
845
|
+
}
|
|
846
|
+
if (outbound.read === true) {
|
|
847
|
+
items.push({ key: 'outbound.read', risk: 'sensitive', label: '查看智能体发出的每条消息' })
|
|
848
|
+
}
|
|
849
|
+
if (outbound.onTimeout === 'cancel' && (outbound.transform === true || outbound.cancel === true)) {
|
|
850
|
+
items.push({ key: 'outbound.onTimeout', risk: 'high', label: '这个扩展出错时,智能体的消息会发不出去' })
|
|
851
|
+
}
|
|
852
|
+
}
|
|
601
853
|
const jobs = capabilities.jobs
|
|
602
854
|
if (jobs?.declared !== undefined && jobs.declared.length > 0) {
|
|
603
855
|
items.push({
|
package/src/extension-ui.ts
CHANGED
|
@@ -4,6 +4,8 @@ import {
|
|
|
4
4
|
HostLayerCapabilitiesSchema,
|
|
5
5
|
hostLayerAsCapabilities,
|
|
6
6
|
EXTENSION_SDK_BASE_LEVEL,
|
|
7
|
+
EXTENSION_FILES_SDK_LEVEL,
|
|
8
|
+
EXTENSION_OUTBOUND_SDK_LEVEL,
|
|
7
9
|
} from './extension-capabilities.js'
|
|
8
10
|
import { ExtensionIdSchema, ExtensionRevisionIdSchema, HostUiPageInstanceIdSchema } from './domain.js'
|
|
9
11
|
import { ConfigSchemaDocumentSchema } from './config-schema.js'
|
|
@@ -548,12 +550,25 @@ export type ExtensionDataHookName = keyof typeof EXTENSION_DATA_HOOK_PERMISSIONS
|
|
|
548
550
|
export const requiredExtensionSdkLevel = (
|
|
549
551
|
permissions: ExtensionPermissions | undefined,
|
|
550
552
|
): { readonly level: number; readonly capabilities: readonly string[] } => {
|
|
553
|
+
const level14 = [
|
|
554
|
+
permissions?.host?.files === undefined && permissions?.agent?.files === undefined ? undefined : '工作文件',
|
|
555
|
+
permissions?.host?.assets?.read === undefined && permissions?.agent?.assets?.read === undefined
|
|
556
|
+
? undefined
|
|
557
|
+
: '读取频道文件',
|
|
558
|
+
permissions?.agent?.toolImages === undefined ? undefined : '高细节图片',
|
|
559
|
+
permissions?.host?.media === undefined && permissions?.agent?.media === undefined ? undefined : '媒体处理',
|
|
560
|
+
permissions?.host?.commands === undefined && permissions?.agent?.commands === undefined ? undefined : '运行程序',
|
|
561
|
+
permissions?.host?.speech === undefined && permissions?.agent?.speech === undefined ? undefined : '语音识别',
|
|
562
|
+
].filter((name): name is string => name !== undefined)
|
|
563
|
+
if (level14.length > 0) return { level: EXTENSION_FILES_SDK_LEVEL, capabilities: level14 }
|
|
551
564
|
const level8 = [
|
|
552
|
-
permissions?.host?.assets === undefined ? undefined : '扩展资源库',
|
|
565
|
+
permissions?.host?.assets?.library === undefined ? undefined : '扩展资源库',
|
|
553
566
|
permissions?.agent?.assets?.library === undefined ? undefined : '扩展资源库',
|
|
554
567
|
permissions?.host?.index === undefined && permissions?.agent?.index === undefined ? undefined : '检索索引',
|
|
555
568
|
permissions?.host?.models === undefined ? undefined : '本机层模型调用',
|
|
556
569
|
].filter((name): name is string => name !== undefined)
|
|
570
|
+
if (permissions?.agent?.outbound !== undefined)
|
|
571
|
+
return { level: EXTENSION_OUTBOUND_SDK_LEVEL, capabilities: ['出站处理'] }
|
|
557
572
|
return level8.length > 0
|
|
558
573
|
? { level: 8, capabilities: [...new Set(level8)] }
|
|
559
574
|
: { level: EXTENSION_SDK_BASE_LEVEL, capabilities: [] }
|