@nekro-nxt/contracts 0.3.0 → 0.5.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/README.md +2 -0
- package/dist/index.mjs +2512 -78
- package/dist/index.mjs.map +1 -1
- package/lib/types/commands.d.ts +408 -0
- package/lib/types/commands.d.ts.map +1 -0
- package/lib/types/community-feedback-host.d.ts +442 -0
- package/lib/types/community-feedback-host.d.ts.map +1 -0
- package/lib/types/community-feedback.d.ts +1072 -0
- package/lib/types/community-feedback.d.ts.map +1 -0
- package/lib/types/community-personas.d.ts +4 -0
- package/lib/types/community-personas.d.ts.map +1 -1
- package/lib/types/community.d.ts +251 -13
- package/lib/types/community.d.ts.map +1 -1
- package/lib/types/domain.d.ts +33 -0
- package/lib/types/domain.d.ts.map +1 -1
- package/lib/types/extension-capabilities.d.ts +105 -3
- package/lib/types/extension-capabilities.d.ts.map +1 -1
- package/lib/types/extension-ui.d.ts +86 -7
- package/lib/types/extension-ui.d.ts.map +1 -1
- package/lib/types/host-api.d.ts +11109 -3874
- package/lib/types/host-api.d.ts.map +1 -1
- package/lib/types/index.d.ts +4 -0
- package/lib/types/index.d.ts.map +1 -1
- package/lib/types/logs.d.ts +355 -0
- package/lib/types/logs.d.ts.map +1 -0
- package/package.json +1 -1
- package/src/commands.ts +366 -0
- package/src/community-feedback-host.ts +121 -0
- package/src/community-feedback.ts +289 -0
- package/src/community.ts +56 -1
- package/src/domain.ts +59 -6
- package/src/extension-capabilities.ts +159 -8
- package/src/extension-ui.ts +40 -0
- package/src/host-api.ts +1540 -67
- package/src/index.ts +5 -0
- package/src/logs.ts +138 -0
- package/src/management-api.ts +1 -1
|
@@ -3,9 +3,34 @@ import { z } from 'zod'
|
|
|
3
3
|
/**
|
|
4
4
|
* Host capability level this build implements. A Revision that declares `requires.sdk` above it is rejected at
|
|
5
5
|
* import with an "upgrade NekroNXT" message instead of an opaque schema error. Bump when a Manifest capability
|
|
6
|
-
* ships; never reuse a level for a different meaning. Level 7 is Manifest V7 (host instance + agent attachments)
|
|
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
|
+
* 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
|
+
* migration hook and administrator-only commands. Level 11 lists the pictures of an inbound message to inbound hooks.
|
|
7
9
|
*/
|
|
8
|
-
export const EXTENSION_SDK_LEVEL =
|
|
10
|
+
export const EXTENSION_SDK_LEVEL = 11
|
|
11
|
+
|
|
12
|
+
/** Lineage, `dataVersion` and administrator-only commands need this level; packages that carry them declare it. */
|
|
13
|
+
export const EXTENSION_GOVERNANCE_SDK_LEVEL = 10
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* The first NekroNXT version of each capability level, so users read "needs NekroNXT 0.5.0" rather than a level
|
|
17
|
+
* number. Add the row for a new level in the change that raises EXTENSION_SDK_LEVEL; a test keeps the two in step.
|
|
18
|
+
*/
|
|
19
|
+
export const EXTENSION_SDK_RELEASES: readonly { readonly level: number; readonly version: string }[] = [
|
|
20
|
+
{ level: 6, version: '0.3.0' },
|
|
21
|
+
{ level: 7, version: '0.4.0' },
|
|
22
|
+
{ level: 8, version: '0.5.0' },
|
|
23
|
+
{ level: 9, version: '0.6.0' },
|
|
24
|
+
{ level: 10, version: '0.6.0' },
|
|
25
|
+
{ level: 11, version: '0.6.0' },
|
|
26
|
+
]
|
|
27
|
+
|
|
28
|
+
/** The oldest NekroNXT version that supports `level`; undefined for a level no release supports yet. */
|
|
29
|
+
export const extensionSdkVersion = (level: number): string | undefined =>
|
|
30
|
+
EXTENSION_SDK_RELEASES.find((release) => release.level >= level)?.version
|
|
31
|
+
|
|
32
|
+
/** Manifest V7 itself; extensions that use nothing newer need this level. */
|
|
33
|
+
export const EXTENSION_SDK_BASE_LEVEL = 7
|
|
9
34
|
|
|
10
35
|
export const ExtensionRequiresSchema = z.object({ sdk: z.number().int().min(1).max(1000) }).strict()
|
|
11
36
|
export type ExtensionRequires = z.output<typeof ExtensionRequiresSchema>
|
|
@@ -150,6 +175,86 @@ export const ExtensionPlatformCapabilitySchema = z
|
|
|
150
175
|
.strict()
|
|
151
176
|
export type ExtensionPlatformCapability = z.output<typeof ExtensionPlatformCapabilitySchema>
|
|
152
177
|
|
|
178
|
+
export const EXTENSION_ASSET_LIBRARY_DEFAULT_QUOTA_BYTES = 2 * 1024 * 1024 * 1024
|
|
179
|
+
export const EXTENSION_ASSET_LIBRARY_MAX_QUOTA_BYTES = 32 * 1024 * 1024 * 1024
|
|
180
|
+
|
|
181
|
+
export const ExtensionAssetLibraryCapabilitySchema = z
|
|
182
|
+
.object({
|
|
183
|
+
quotaBytes: z
|
|
184
|
+
.number()
|
|
185
|
+
.int()
|
|
186
|
+
.min(1024 * 1024)
|
|
187
|
+
.max(EXTENSION_ASSET_LIBRARY_MAX_QUOTA_BYTES)
|
|
188
|
+
.optional(),
|
|
189
|
+
})
|
|
190
|
+
.strict()
|
|
191
|
+
export type ExtensionAssetLibraryCapability = z.output<typeof ExtensionAssetLibraryCapabilitySchema>
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* `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.
|
|
196
|
+
*/
|
|
197
|
+
export const ExtensionAssetsCapabilitySchema = z
|
|
198
|
+
.object({
|
|
199
|
+
write: z.literal(true).optional(),
|
|
200
|
+
library: ExtensionAssetLibraryCapabilitySchema.optional(),
|
|
201
|
+
})
|
|
202
|
+
.strict()
|
|
203
|
+
.refine((value) => value.write !== undefined || value.library !== undefined, 'assets 至少声明 write 或 library。')
|
|
204
|
+
export type ExtensionAssetsCapability = z.output<typeof ExtensionAssetsCapabilitySchema>
|
|
205
|
+
|
|
206
|
+
const IndexNameSchema = z
|
|
207
|
+
.string()
|
|
208
|
+
.trim()
|
|
209
|
+
.regex(/^[a-z][a-z0-9_]{0,31}$/u, '索引、字段和过滤项名称只能使用小写字母、数字和下划线,以字母开头。')
|
|
210
|
+
|
|
211
|
+
export const EXTENSION_INDEX_MAX_TEXT_FIELDS = 4
|
|
212
|
+
export const EXTENSION_INDEX_MAX_FILTERS = 4
|
|
213
|
+
|
|
214
|
+
/**
|
|
215
|
+
* One searchable collection. Text fields are matched by keywords (and vectors when the Host has them enabled); weights
|
|
216
|
+
* rank keyword matches. Filter fields hold short keyword values, possibly several per document.
|
|
217
|
+
*/
|
|
218
|
+
export const ExtensionIndexCollectionSchema = z
|
|
219
|
+
.object({
|
|
220
|
+
name: IndexNameSchema,
|
|
221
|
+
fields: z
|
|
222
|
+
.array(z.object({ name: IndexNameSchema, weight: z.number().min(0.1).max(10).default(1) }).strict())
|
|
223
|
+
.min(1)
|
|
224
|
+
.max(EXTENSION_INDEX_MAX_TEXT_FIELDS),
|
|
225
|
+
filters: z.array(IndexNameSchema).max(EXTENSION_INDEX_MAX_FILTERS).default([]),
|
|
226
|
+
})
|
|
227
|
+
.strict()
|
|
228
|
+
.superRefine((value, context) => {
|
|
229
|
+
const names = [...value.fields.map(({ name }) => name), ...value.filters]
|
|
230
|
+
if (new Set(names).size !== names.length) {
|
|
231
|
+
context.addIssue({ code: 'custom', message: '同一索引里的字段与过滤项不能重名。' })
|
|
232
|
+
}
|
|
233
|
+
})
|
|
234
|
+
export type ExtensionIndexCollection = z.output<typeof ExtensionIndexCollectionSchema>
|
|
235
|
+
|
|
236
|
+
export const ExtensionIndexCapabilitySchema = z
|
|
237
|
+
.object({
|
|
238
|
+
collections: z
|
|
239
|
+
.array(ExtensionIndexCollectionSchema)
|
|
240
|
+
.min(1)
|
|
241
|
+
.max(4)
|
|
242
|
+
.refine((value) => new Set(value.map(({ name }) => name)).size === value.length, '索引名称不能重复。'),
|
|
243
|
+
})
|
|
244
|
+
.strict()
|
|
245
|
+
export type ExtensionIndexCapability = z.output<typeof ExtensionIndexCapabilitySchema>
|
|
246
|
+
|
|
247
|
+
export const EXTENSION_MODEL_MAX_CALLS_PER_MINUTE = 120
|
|
248
|
+
|
|
249
|
+
/** Calls to a model the user picks from the Host's configured models, with images allowed; host layer only. */
|
|
250
|
+
export const ExtensionModelsCapabilitySchema = z
|
|
251
|
+
.object({
|
|
252
|
+
maxCallsPerMinute: z.number().int().min(1).max(EXTENSION_MODEL_MAX_CALLS_PER_MINUTE),
|
|
253
|
+
maxOutputTokens: z.number().int().min(64).max(EXTENSION_LLM_MAX_OUTPUT_TOKENS),
|
|
254
|
+
})
|
|
255
|
+
.strict()
|
|
256
|
+
export type ExtensionModelsCapability = z.output<typeof ExtensionModelsCapabilitySchema>
|
|
257
|
+
|
|
153
258
|
/** A header or environment value: literal text, or the current value of a `meta.role: 'secret'` config field. */
|
|
154
259
|
export const ExtensionMcpValueSchema = z.union([
|
|
155
260
|
z.string().max(4096),
|
|
@@ -259,10 +364,10 @@ export const ExtensionCapabilitiesSchema = z
|
|
|
259
364
|
.object({
|
|
260
365
|
network: ExtensionNetworkCapabilitySchema.optional(),
|
|
261
366
|
storage: ExtensionStorageCapabilitySchema.optional(),
|
|
262
|
-
assets:
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
367
|
+
assets: ExtensionAssetsCapabilitySchema.optional(),
|
|
368
|
+
index: ExtensionIndexCapabilitySchema.optional(),
|
|
369
|
+
/** Host layer only; the agent layer uses `llm`, which runs on the agent's own model. */
|
|
370
|
+
models: ExtensionModelsCapabilitySchema.optional(),
|
|
266
371
|
history: z
|
|
267
372
|
.object({ read: z.literal(true) })
|
|
268
373
|
.strict()
|
|
@@ -292,6 +397,11 @@ export const HostLayerCapabilitiesSchema = z
|
|
|
292
397
|
.object({ quotaBytes: z.number().int().min(1024).max(EXTENSION_STORAGE_MAX_QUOTA_BYTES).optional() })
|
|
293
398
|
.strict()
|
|
294
399
|
.optional(),
|
|
400
|
+
/** The same library agent attachments use; the host instance lists and imports into it. */
|
|
401
|
+
assets: z.object({ library: ExtensionAssetLibraryCapabilitySchema }).strict().optional(),
|
|
402
|
+
/** The same collections agent attachments declare; pages search and maintain them. */
|
|
403
|
+
index: ExtensionIndexCapabilitySchema.optional(),
|
|
404
|
+
models: ExtensionModelsCapabilitySchema.optional(),
|
|
295
405
|
})
|
|
296
406
|
.strict()
|
|
297
407
|
export type HostLayerCapabilities = z.output<typeof HostLayerCapabilitiesSchema>
|
|
@@ -310,6 +420,9 @@ export const hostLayerAsCapabilities = (host: HostLayerCapabilities | undefined)
|
|
|
310
420
|
...(host.storage.quotaBytes === undefined ? {} : { quotaBytes: host.storage.quotaBytes }),
|
|
311
421
|
},
|
|
312
422
|
}),
|
|
423
|
+
...(host.assets === undefined ? {} : { assets: { library: host.assets.library } }),
|
|
424
|
+
...(host.index === undefined ? {} : { index: host.index }),
|
|
425
|
+
...(host.models === undefined ? {} : { models: host.models }),
|
|
313
426
|
}
|
|
314
427
|
|
|
315
428
|
const NETWORK_MODE_RANK = { domains: 0, config: 1, unrestricted: 2 } as const
|
|
@@ -344,7 +457,16 @@ export const extensionCapabilitiesExpand = (
|
|
|
344
457
|
if (next === undefined) return false
|
|
345
458
|
if (networkExpands(previous?.network, next.network)) return true
|
|
346
459
|
if (next.storage?.scopes.some((scope) => !(previous?.storage?.scopes ?? []).includes(scope))) return true
|
|
347
|
-
if (next.assets !== undefined && previous?.assets === undefined) return true
|
|
460
|
+
if (next.assets?.write !== undefined && previous?.assets?.write === undefined) return true
|
|
461
|
+
if (next.assets?.library !== undefined && previous?.assets?.library === undefined) return true
|
|
462
|
+
if (
|
|
463
|
+
next.models !== undefined &&
|
|
464
|
+
(previous?.models === undefined ||
|
|
465
|
+
next.models.maxCallsPerMinute > previous.models.maxCallsPerMinute ||
|
|
466
|
+
next.models.maxOutputTokens > previous.models.maxOutputTokens)
|
|
467
|
+
) {
|
|
468
|
+
return true
|
|
469
|
+
}
|
|
348
470
|
if (next.history !== undefined && previous?.history === undefined) return true
|
|
349
471
|
const platformKey = ({ adapter, action }: { readonly adapter: string; readonly action: string }) =>
|
|
350
472
|
`${adapter}:${action}`
|
|
@@ -381,6 +503,11 @@ export const extensionCapabilitiesExpand = (
|
|
|
381
503
|
return false
|
|
382
504
|
}
|
|
383
505
|
|
|
506
|
+
const formatLibraryQuota = (bytes: number): string =>
|
|
507
|
+
bytes >= 1024 * 1024 * 1024
|
|
508
|
+
? `${+(bytes / 1024 / 1024 / 1024).toFixed(1)} GB`
|
|
509
|
+
: `${Math.round(bytes / 1024 / 1024)} MB`
|
|
510
|
+
|
|
384
511
|
/** Capability risk tiers shown on the approval page (§7 of the capability decision). */
|
|
385
512
|
export type ExtensionCapabilityRisk = 'normal' | 'sensitive' | 'high'
|
|
386
513
|
|
|
@@ -428,8 +555,32 @@ export const summarizeExtensionCapabilities = (
|
|
|
428
555
|
label: shared ? '保存数据,并在启用它的智能体之间共享' : '为这个智能体保存数据',
|
|
429
556
|
})
|
|
430
557
|
}
|
|
431
|
-
if (capabilities.assets !== undefined)
|
|
558
|
+
if (capabilities.assets?.write !== undefined)
|
|
432
559
|
items.push({ key: 'assets', risk: 'normal', label: '在当前频道生成图片或文件' })
|
|
560
|
+
if (capabilities.assets?.library !== undefined) {
|
|
561
|
+
items.push({
|
|
562
|
+
key: 'assets.library',
|
|
563
|
+
risk: 'sensitive',
|
|
564
|
+
label: '把频道里的图片收进扩展资源库,之后可以在其他频道使用',
|
|
565
|
+
detail: `最多 ${formatLibraryQuota(capabilities.assets.library.quotaBytes ?? EXTENSION_ASSET_LIBRARY_DEFAULT_QUOTA_BYTES)}`,
|
|
566
|
+
})
|
|
567
|
+
}
|
|
568
|
+
if (capabilities.index !== undefined) {
|
|
569
|
+
items.push({
|
|
570
|
+
key: 'index',
|
|
571
|
+
risk: 'normal',
|
|
572
|
+
label: '建立可搜索的索引',
|
|
573
|
+
detail: capabilities.index.collections.map(({ name }) => name).join('、'),
|
|
574
|
+
})
|
|
575
|
+
}
|
|
576
|
+
if (capabilities.models !== undefined) {
|
|
577
|
+
items.push({
|
|
578
|
+
key: 'models',
|
|
579
|
+
risk: 'sensitive',
|
|
580
|
+
label: '使用本机已配置的模型,可以发送图片',
|
|
581
|
+
detail: `每分钟最多 ${capabilities.models.maxCallsPerMinute} 次,计入所选模型的用量`,
|
|
582
|
+
})
|
|
583
|
+
}
|
|
433
584
|
if (capabilities.history !== undefined)
|
|
434
585
|
items.push({ key: 'history', risk: 'normal', label: '读取当前频道的聊天记录' })
|
|
435
586
|
const hook = capabilities.inboundHook
|
package/src/extension-ui.ts
CHANGED
|
@@ -3,6 +3,7 @@ import {
|
|
|
3
3
|
ExtensionCapabilitiesSchema,
|
|
4
4
|
HostLayerCapabilitiesSchema,
|
|
5
5
|
hostLayerAsCapabilities,
|
|
6
|
+
EXTENSION_SDK_BASE_LEVEL,
|
|
6
7
|
} from './extension-capabilities.js'
|
|
7
8
|
import { ExtensionIdSchema, ExtensionRevisionIdSchema, HostUiPageInstanceIdSchema } from './domain.js'
|
|
8
9
|
import { ConfigSchemaDocumentSchema } from './config-schema.js'
|
|
@@ -252,6 +253,26 @@ export const ExtensionPermissionsSchema = z
|
|
|
252
253
|
})
|
|
253
254
|
.strict()
|
|
254
255
|
.superRefine(checkBrowserPermissions)
|
|
256
|
+
.superRefine((value, context) => {
|
|
257
|
+
if (value.agent?.models !== undefined) {
|
|
258
|
+
context.addIssue({
|
|
259
|
+
code: 'custom',
|
|
260
|
+
path: ['agent', 'models'],
|
|
261
|
+
message: 'models 只能在 permissions.host 中声明;智能体层使用 llm 调用智能体自己的模型。',
|
|
262
|
+
})
|
|
263
|
+
}
|
|
264
|
+
// Both layers read and write the same collections, so a name must mean one definition.
|
|
265
|
+
for (const collection of value.agent?.index?.collections ?? []) {
|
|
266
|
+
const host = value.host?.index?.collections.find(({ name }) => name === collection.name)
|
|
267
|
+
if (host !== undefined && JSON.stringify(host) !== JSON.stringify(collection)) {
|
|
268
|
+
context.addIssue({
|
|
269
|
+
code: 'custom',
|
|
270
|
+
path: ['agent', 'index'],
|
|
271
|
+
message: `索引 ${collection.name} 在 permissions.host 与 permissions.agent 中的定义不一致。`,
|
|
272
|
+
})
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
})
|
|
255
276
|
|
|
256
277
|
export type ExtensionPermissions = z.output<typeof ExtensionPermissionsSchema>
|
|
257
278
|
|
|
@@ -518,3 +539,22 @@ export const EXTENSION_DATA_HOOK_PERMISSIONS = {
|
|
|
518
539
|
} as const satisfies Readonly<Record<string, HostUiPermission>>
|
|
519
540
|
|
|
520
541
|
export type ExtensionDataHookName = keyof typeof EXTENSION_DATA_HOOK_PERMISSIONS
|
|
542
|
+
|
|
543
|
+
/**
|
|
544
|
+
* The capability level a set of permissions needs: Manifest V7 itself, or the level that introduced the newest
|
|
545
|
+
* capability it declares. Packaging and import compare it with `requires.sdk`, so an extension cannot claim a level
|
|
546
|
+
* too old for what it uses.
|
|
547
|
+
*/
|
|
548
|
+
export const requiredExtensionSdkLevel = (
|
|
549
|
+
permissions: ExtensionPermissions | undefined,
|
|
550
|
+
): { readonly level: number; readonly capabilities: readonly string[] } => {
|
|
551
|
+
const level8 = [
|
|
552
|
+
permissions?.host?.assets === undefined ? undefined : '扩展资源库',
|
|
553
|
+
permissions?.agent?.assets?.library === undefined ? undefined : '扩展资源库',
|
|
554
|
+
permissions?.host?.index === undefined && permissions?.agent?.index === undefined ? undefined : '检索索引',
|
|
555
|
+
permissions?.host?.models === undefined ? undefined : '本机层模型调用',
|
|
556
|
+
].filter((name): name is string => name !== undefined)
|
|
557
|
+
return level8.length > 0
|
|
558
|
+
? { level: 8, capabilities: [...new Set(level8)] }
|
|
559
|
+
: { level: EXTENSION_SDK_BASE_LEVEL, capabilities: [] }
|
|
560
|
+
}
|