@nekro-nxt/contracts 0.5.0 → 0.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.mjs +1044 -99
- 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 +54 -2
- package/lib/types/extension-capabilities.d.ts.map +1 -1
- package/lib/types/extension-ui.d.ts +42 -0
- package/lib/types/extension-ui.d.ts.map +1 -1
- package/lib/types/host-api.d.ts +2304 -218
- 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 +70 -3
- package/src/extension-ui.ts +3 -0
- package/src/host-api.ts +681 -85
- 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
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { AgentIdSchema, ChannelIdSchema, CredentialIdSchema, ExtensionIdSchema } from './domain.js'
|
|
3
|
+
import { ExtensionNetworkDomainSchema } from './extension-capabilities.js'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Named credentials (智能体扩展配置与凭据 C1–C5): one entry is added once and reused by extensions, MCP servers and
|
|
7
|
+
* commands. The value is write-only; every view below carries metadata only. Accounts on platforms such as GitHub are
|
|
8
|
+
* linked accounts (`linked-accounts.ts`), granted per agent, not credentials.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export const CREDENTIAL_NAME_MAX_CHARS = 64
|
|
12
|
+
export const CREDENTIAL_DESCRIPTION_MAX_CHARS = 500
|
|
13
|
+
export const CREDENTIAL_VALUE_MAX_CHARS = 16_384
|
|
14
|
+
/** Values shorter than this are not hidden from tool results; the form warns when one is saved. */
|
|
15
|
+
export const CREDENTIAL_REDACTABLE_MIN_CHARS = 8
|
|
16
|
+
|
|
17
|
+
/** How a credential reaches code: `request` only as a placeholder filled by `nxt.http.fetch`; `program` as the value. */
|
|
18
|
+
export const CredentialUsageSchema = z.enum(['request', 'program'])
|
|
19
|
+
export type CredentialUsage = z.output<typeof CredentialUsageSchema>
|
|
20
|
+
|
|
21
|
+
/** Agents that may use a credential: every agent, or the listed ones. */
|
|
22
|
+
export const CredentialAgentScopeSchema = z.union([
|
|
23
|
+
z.object({ all: z.literal(true) }).strict(),
|
|
24
|
+
z.object({ agentIds: z.array(AgentIdSchema).max(256) }).strict(),
|
|
25
|
+
])
|
|
26
|
+
export type CredentialAgentScope = z.output<typeof CredentialAgentScopeSchema>
|
|
27
|
+
|
|
28
|
+
/** Shown in redaction markers (`[凭据:名称]`), so brackets and control characters are not allowed. */
|
|
29
|
+
export const CredentialNameSchema = z
|
|
30
|
+
.string()
|
|
31
|
+
.trim()
|
|
32
|
+
.min(1, '名称不能为空。')
|
|
33
|
+
.max(CREDENTIAL_NAME_MAX_CHARS, `名称最多 ${CREDENTIAL_NAME_MAX_CHARS} 个字符。`)
|
|
34
|
+
// eslint-disable-next-line no-control-regex -- Control characters are exactly what the name must not contain.
|
|
35
|
+
.regex(/^[^\u0000-\u001f\u007f[\]]+$/u, '名称不能包含方括号或控制字符。')
|
|
36
|
+
|
|
37
|
+
export const CredentialDescriptionSchema = z
|
|
38
|
+
.string()
|
|
39
|
+
.trim()
|
|
40
|
+
.max(CREDENTIAL_DESCRIPTION_MAX_CHARS, `说明最多 ${CREDENTIAL_DESCRIPTION_MAX_CHARS} 个字符。`)
|
|
41
|
+
|
|
42
|
+
export const CredentialDomainsSchema = z.array(ExtensionNetworkDomainSchema).max(32)
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Names a credential's environment variable cannot take: the shell, the tools' own configuration, and the variables
|
|
46
|
+
* linked accounts give commands (`GH_TOKEN` and the `gh` settings).
|
|
47
|
+
*/
|
|
48
|
+
const RESERVED_COMMAND_ENV = new Set([
|
|
49
|
+
'PATH',
|
|
50
|
+
'HOME',
|
|
51
|
+
'USER',
|
|
52
|
+
'LOGNAME',
|
|
53
|
+
'SHELL',
|
|
54
|
+
'PWD',
|
|
55
|
+
'OLDPWD',
|
|
56
|
+
'TMPDIR',
|
|
57
|
+
'TEMP',
|
|
58
|
+
'TMP',
|
|
59
|
+
'LANG',
|
|
60
|
+
'TERM',
|
|
61
|
+
'NODE_OPTIONS',
|
|
62
|
+
'NODE_PATH',
|
|
63
|
+
'GH_TOKEN',
|
|
64
|
+
'GH_CONFIG_DIR',
|
|
65
|
+
'GH_PROMPT_DISABLED',
|
|
66
|
+
'GH_NO_UPDATE_NOTIFIER',
|
|
67
|
+
'GH_NO_EXTENSION_UPDATE_NOTIFIER',
|
|
68
|
+
'GIT_TERMINAL_PROMPT',
|
|
69
|
+
'GIT_CONFIG_COUNT',
|
|
70
|
+
])
|
|
71
|
+
const RESERVED_COMMAND_ENV_PREFIXES = ['LC_', 'LD_', 'DYLD_', 'GIT_CONFIG_', 'NEKRO_', 'DSH_']
|
|
72
|
+
|
|
73
|
+
/** The environment variable a program credential reaches commands under. */
|
|
74
|
+
export const CredentialEnvNameSchema = z
|
|
75
|
+
.string()
|
|
76
|
+
.trim()
|
|
77
|
+
.max(64)
|
|
78
|
+
.regex(/^[A-Z_][A-Z0-9_]*$/u, '环境变量名只能使用大写字母、数字和下划线,且不能以数字开头。')
|
|
79
|
+
.refine(
|
|
80
|
+
(name) =>
|
|
81
|
+
!RESERVED_COMMAND_ENV.has(name) && !RESERVED_COMMAND_ENV_PREFIXES.some((prefix) => name.startsWith(prefix)),
|
|
82
|
+
'这个环境变量名由系统使用,请换一个。',
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
export const CredentialValueSchema = z.string().min(1).max(CREDENTIAL_VALUE_MAX_CHARS)
|
|
86
|
+
|
|
87
|
+
/** What uses one credential right now: extension secret fields bound to it and agents whose commands receive it. */
|
|
88
|
+
export const CredentialUsersSchema = z
|
|
89
|
+
.object({
|
|
90
|
+
extensions: z.array(
|
|
91
|
+
z
|
|
92
|
+
.object({
|
|
93
|
+
extensionId: ExtensionIdSchema,
|
|
94
|
+
name: z.string(),
|
|
95
|
+
/** Agents whose own configuration binds it; empty when only the host configuration does. */
|
|
96
|
+
agentIds: z.array(AgentIdSchema),
|
|
97
|
+
host: z.boolean(),
|
|
98
|
+
})
|
|
99
|
+
.strict(),
|
|
100
|
+
),
|
|
101
|
+
/** Agents with commands enabled whose commands receive it. */
|
|
102
|
+
commandAgents: z.array(AgentIdSchema),
|
|
103
|
+
})
|
|
104
|
+
.strict()
|
|
105
|
+
export type CredentialUsers = z.output<typeof CredentialUsersSchema>
|
|
106
|
+
|
|
107
|
+
export const CredentialViewSchema = z
|
|
108
|
+
.object({
|
|
109
|
+
id: CredentialIdSchema,
|
|
110
|
+
name: CredentialNameSchema,
|
|
111
|
+
description: z.string(),
|
|
112
|
+
usage: CredentialUsageSchema,
|
|
113
|
+
domains: z.array(z.string()),
|
|
114
|
+
agentScope: CredentialAgentScopeSchema,
|
|
115
|
+
autoUse: z.boolean(),
|
|
116
|
+
/** Program credentials: the environment variable agents' commands receive it under, or `null` when they do not. */
|
|
117
|
+
envName: z.string().nullable(),
|
|
118
|
+
/** Agents an administrator let use it while automatic use is off. */
|
|
119
|
+
approvedAgentIds: z.array(AgentIdSchema),
|
|
120
|
+
/** False when the stored value is too short to hide from tool results, or cannot be read. */
|
|
121
|
+
redactable: z.boolean(),
|
|
122
|
+
/** False when the stored value cannot be read any more; the entry stays so it can be replaced. */
|
|
123
|
+
available: z.boolean(),
|
|
124
|
+
usedBy: CredentialUsersSchema,
|
|
125
|
+
createdAt: z.number().int().nonnegative(),
|
|
126
|
+
updatedAt: z.number().int().nonnegative(),
|
|
127
|
+
lastUsedAt: z.number().int().nonnegative().nullable(),
|
|
128
|
+
})
|
|
129
|
+
.strict()
|
|
130
|
+
export type CredentialView = z.output<typeof CredentialViewSchema>
|
|
131
|
+
|
|
132
|
+
/** A model provider listed with the credentials; `open` lets agents and extensions call it through the host. */
|
|
133
|
+
export const ModelProviderAccessSchema = z
|
|
134
|
+
.object({
|
|
135
|
+
provider: z.string().min(1),
|
|
136
|
+
name: z.string(),
|
|
137
|
+
open: z.boolean(),
|
|
138
|
+
})
|
|
139
|
+
.strict()
|
|
140
|
+
export type ModelProviderAccess = z.output<typeof ModelProviderAccessSchema>
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* An agent asked for a credential that does not exist yet. The settings link pre-fills the add form; saving a
|
|
144
|
+
* credential with this name wakes the agent in the channel it asked from.
|
|
145
|
+
*/
|
|
146
|
+
export const CredentialRequestViewSchema = z
|
|
147
|
+
.object({
|
|
148
|
+
id: z.string().min(1).max(64),
|
|
149
|
+
name: CredentialNameSchema,
|
|
150
|
+
purpose: z.string(),
|
|
151
|
+
domains: z.array(z.string()),
|
|
152
|
+
usage: CredentialUsageSchema,
|
|
153
|
+
agentId: AgentIdSchema,
|
|
154
|
+
channelId: ChannelIdSchema.nullable(),
|
|
155
|
+
createdAt: z.number().int().nonnegative(),
|
|
156
|
+
/** Settings route that opens the add form pre-filled, see {@link credentialRequestRoute}. */
|
|
157
|
+
route: z.string(),
|
|
158
|
+
})
|
|
159
|
+
.strict()
|
|
160
|
+
export type CredentialRequestView = z.output<typeof CredentialRequestViewSchema>
|
|
161
|
+
|
|
162
|
+
export const CredentialsViewSchema = z
|
|
163
|
+
.object({
|
|
164
|
+
credentials: z.array(CredentialViewSchema),
|
|
165
|
+
modelProviders: z.array(ModelProviderAccessSchema),
|
|
166
|
+
requests: z.array(CredentialRequestViewSchema),
|
|
167
|
+
})
|
|
168
|
+
.strict()
|
|
169
|
+
export type CredentialsView = z.output<typeof CredentialsViewSchema>
|
|
170
|
+
|
|
171
|
+
export const CreateCredentialRequestSchema = z
|
|
172
|
+
.object({
|
|
173
|
+
name: CredentialNameSchema,
|
|
174
|
+
description: CredentialDescriptionSchema.default(''),
|
|
175
|
+
agentScope: CredentialAgentScopeSchema.default({ all: true }),
|
|
176
|
+
autoUse: z.boolean().default(true),
|
|
177
|
+
usage: CredentialUsageSchema.default('request'),
|
|
178
|
+
domains: CredentialDomainsSchema.default([]),
|
|
179
|
+
/** Program credentials only. */
|
|
180
|
+
envName: CredentialEnvNameSchema.optional(),
|
|
181
|
+
value: CredentialValueSchema,
|
|
182
|
+
/** The agent request this form was opened from, if any. */
|
|
183
|
+
requestId: z.string().min(1).max(64).optional(),
|
|
184
|
+
})
|
|
185
|
+
.strict()
|
|
186
|
+
export type CreateCredentialRequest = z.input<typeof CreateCredentialRequestSchema>
|
|
187
|
+
|
|
188
|
+
/** Metadata changes; omitted fields keep their value. */
|
|
189
|
+
export const UpdateCredentialRequestSchema = z
|
|
190
|
+
.object({
|
|
191
|
+
name: CredentialNameSchema.optional(),
|
|
192
|
+
description: CredentialDescriptionSchema.optional(),
|
|
193
|
+
usage: CredentialUsageSchema.optional(),
|
|
194
|
+
domains: CredentialDomainsSchema.optional(),
|
|
195
|
+
agentScope: CredentialAgentScopeSchema.optional(),
|
|
196
|
+
autoUse: z.boolean().optional(),
|
|
197
|
+
/** `null` stops giving it to commands; only program credentials take one. */
|
|
198
|
+
envName: CredentialEnvNameSchema.nullable().optional(),
|
|
199
|
+
/** Replaces the agents allowed to use it while automatic use is off, for example to withdraw an approval. */
|
|
200
|
+
approvedAgentIds: z.array(AgentIdSchema).max(256).optional(),
|
|
201
|
+
})
|
|
202
|
+
.strict()
|
|
203
|
+
export type UpdateCredentialRequest = z.output<typeof UpdateCredentialRequestSchema>
|
|
204
|
+
|
|
205
|
+
export const ReplaceCredentialValueRequestSchema = z.object({ value: CredentialValueSchema }).strict()
|
|
206
|
+
export type ReplaceCredentialValueRequest = z.output<typeof ReplaceCredentialValueRequestSchema>
|
|
207
|
+
|
|
208
|
+
/** Removing a credential that is in use needs `confirm`; without it the result lists who uses it. */
|
|
209
|
+
export const RemoveCredentialRequestSchema = z.object({ confirm: z.boolean().default(false) }).strict()
|
|
210
|
+
|
|
211
|
+
export const RemoveCredentialResultSchema = z.discriminatedUnion('removed', [
|
|
212
|
+
z.object({ removed: z.literal(true), view: CredentialsViewSchema }).strict(),
|
|
213
|
+
z.object({ removed: z.literal(false), usedBy: CredentialUsersSchema }).strict(),
|
|
214
|
+
])
|
|
215
|
+
export type RemoveCredentialResult = z.output<typeof RemoveCredentialResultSchema>
|
|
216
|
+
|
|
217
|
+
export const CredentialConsumerKindSchema = z.enum(['extension', 'command', 'mcp', 'model'])
|
|
218
|
+
|
|
219
|
+
/** One recorded use. The newest 500 per credential are kept, none older than 90 days. */
|
|
220
|
+
export const CredentialUseSchema = z
|
|
221
|
+
.object({
|
|
222
|
+
at: z.number().int().nonnegative(),
|
|
223
|
+
agentId: AgentIdSchema.nullable(),
|
|
224
|
+
channelId: ChannelIdSchema.nullable(),
|
|
225
|
+
requesterMemberId: z.string().nullable(),
|
|
226
|
+
consumerKind: CredentialConsumerKindSchema,
|
|
227
|
+
consumerId: z.string(),
|
|
228
|
+
/** Extension display name or MCP server name, when it is still known. */
|
|
229
|
+
consumerName: z.string().nullable(),
|
|
230
|
+
targetHost: z.string().nullable(),
|
|
231
|
+
})
|
|
232
|
+
.strict()
|
|
233
|
+
export type CredentialUse = z.output<typeof CredentialUseSchema>
|
|
234
|
+
|
|
235
|
+
export const CredentialUsesViewSchema = z.object({ uses: z.array(CredentialUseSchema) }).strict()
|
|
236
|
+
|
|
237
|
+
export const SetModelProviderAccessRequestSchema = z.object({ open: z.boolean() }).strict()
|
|
238
|
+
|
|
239
|
+
/**
|
|
240
|
+
* The settings route that opens the add form pre-filled from an agent's request: the credential section with `add=1`
|
|
241
|
+
* plus `name`, `purpose`, `domains` (comma separated), `usage` and `request` (the request id) as query parameters.
|
|
242
|
+
*/
|
|
243
|
+
export const credentialRequestRoute = (request: {
|
|
244
|
+
readonly id: string
|
|
245
|
+
readonly name: string
|
|
246
|
+
readonly purpose: string
|
|
247
|
+
readonly domains: readonly string[]
|
|
248
|
+
readonly usage: CredentialUsage
|
|
249
|
+
}): string => {
|
|
250
|
+
const query = new URLSearchParams({ add: '1', name: request.name, request: request.id, usage: request.usage })
|
|
251
|
+
if (request.purpose !== '') query.set('purpose', request.purpose)
|
|
252
|
+
if (request.domains.length > 0) query.set('domains', request.domains.join(','))
|
|
253
|
+
return `/settings/credentials?${query.toString()}`
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
/** Secret fields of an extension configuration layer bound to a named credential, by field. */
|
|
257
|
+
export const BoundCredentialsSchema = z.record(z.string(), CredentialIdSchema)
|
|
258
|
+
|
|
259
|
+
/** Binds secret fields to named credentials; `null` removes the binding and leaves the field unset. */
|
|
260
|
+
export const CredentialBindingsRequestSchema = z.record(z.string(), CredentialIdSchema.nullable())
|
package/src/domain.ts
CHANGED
|
@@ -18,6 +18,15 @@ export const MessageDeliverySummarySchema = z
|
|
|
18
18
|
)
|
|
19
19
|
.max(20),
|
|
20
20
|
omittedIssues: z.number().int().nonnegative().optional(),
|
|
21
|
+
/** Extensions that changed the message before it was sent, the content they produced and what they appended. */
|
|
22
|
+
processing: z
|
|
23
|
+
.object({
|
|
24
|
+
processedBy: z.array(z.string()),
|
|
25
|
+
parts: z.array(z.unknown()).optional(),
|
|
26
|
+
followUps: z.array(z.string()).optional(),
|
|
27
|
+
})
|
|
28
|
+
.strict()
|
|
29
|
+
.optional(),
|
|
21
30
|
})
|
|
22
31
|
.strict()
|
|
23
32
|
|
|
@@ -50,9 +59,13 @@ export const HostUiPageInstanceIdSchema = brandedId('hup', 'HostUiPageInstanceId
|
|
|
50
59
|
export const AuthoringTaskIdSchema = brandedId('aut', 'AuthoringTaskId')
|
|
51
60
|
export const AuthoringAttemptIdSchema = brandedId('aua', 'AuthoringAttemptId')
|
|
52
61
|
export const SubConversationIdSchema = brandedId('cnv', 'SubConversationId')
|
|
62
|
+
export const CredentialIdSchema = brandedId('crd', 'CredentialId')
|
|
63
|
+
export const LinkedAccountIdSchema = brandedId('acc', 'LinkedAccountId')
|
|
53
64
|
|
|
54
65
|
export type AgentId = z.infer<typeof AgentIdSchema>
|
|
55
66
|
export type SubConversationId = z.infer<typeof SubConversationIdSchema>
|
|
67
|
+
export type CredentialId = z.infer<typeof CredentialIdSchema>
|
|
68
|
+
export type LinkedAccountId = z.infer<typeof LinkedAccountIdSchema>
|
|
56
69
|
export type AgentRevisionId = z.infer<typeof AgentRevisionIdSchema>
|
|
57
70
|
export type ConnectionId = z.infer<typeof ConnectionIdSchema>
|
|
58
71
|
export type ConnectionEventId = z.infer<typeof ConnectionEventIdSchema>
|
|
@@ -228,8 +241,8 @@ export interface DshPluginActivationRecord {
|
|
|
228
241
|
export interface DshPluginDiagnosticRecord {
|
|
229
242
|
readonly entryId: DshPluginEntryId
|
|
230
243
|
readonly targetKey: string
|
|
231
|
-
readonly status: 'active' | 'load-failed' | 'restore-failed' | 'dispose-failed'
|
|
232
|
-
readonly phase: 'import' | 'apply' | 'update' | 'dispose' | 'restore'
|
|
244
|
+
readonly status: 'active' | 'pending' | 'load-failed' | 'restore-failed' | 'dispose-failed'
|
|
245
|
+
readonly phase: 'import' | 'dependency' | 'apply' | 'update' | 'dispose' | 'restore'
|
|
233
246
|
readonly message?: string
|
|
234
247
|
readonly observedAt: number
|
|
235
248
|
}
|
|
@@ -6,8 +6,13 @@ 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`).
|
|
9
11
|
*/
|
|
10
|
-
export const EXTENSION_SDK_LEVEL =
|
|
12
|
+
export const EXTENSION_SDK_LEVEL = 13
|
|
13
|
+
|
|
14
|
+
/** Outbound processing needs this level; packages that declare `permissions.agent.outbound` declare it. */
|
|
15
|
+
export const EXTENSION_OUTBOUND_SDK_LEVEL = 13
|
|
11
16
|
|
|
12
17
|
/** Lineage, `dataVersion` and administrator-only commands need this level; packages that carry them declare it. */
|
|
13
18
|
export const EXTENSION_GOVERNANCE_SDK_LEVEL = 10
|
|
@@ -23,6 +28,8 @@ export const EXTENSION_SDK_RELEASES: readonly { readonly level: number; readonly
|
|
|
23
28
|
{ level: 9, version: '0.6.0' },
|
|
24
29
|
{ level: 10, version: '0.6.0' },
|
|
25
30
|
{ level: 11, version: '0.6.0' },
|
|
31
|
+
{ level: 12, version: '0.7.0' },
|
|
32
|
+
{ level: 13, version: '0.7.0' },
|
|
26
33
|
]
|
|
27
34
|
|
|
28
35
|
/** The oldest NekroNXT version that supports `level`; undefined for a level no release supports yet. */
|
|
@@ -128,6 +135,32 @@ export const ExtensionInboundHookCapabilitySchema = z
|
|
|
128
135
|
.strict()
|
|
129
136
|
export type ExtensionInboundHookCapability = z.output<typeof ExtensionInboundHookCapabilitySchema>
|
|
130
137
|
|
|
138
|
+
export const EXTENSION_OUTBOUND_MAX_TIMEOUT_MS = 15_000
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* Outbound processing of the agent's own messages. `read` sees each message after it was delivered; `transform` may
|
|
142
|
+
* replace its content or append messages before it is sent; `cancel` may stop it. `onTimeout` decides what happens
|
|
143
|
+
* when the handler times out or throws before sending: `send` (default) sends the message as it is.
|
|
144
|
+
*/
|
|
145
|
+
export const ExtensionOutboundCapabilitySchema = z
|
|
146
|
+
.object({
|
|
147
|
+
read: z.literal(true).optional(),
|
|
148
|
+
transform: z.literal(true).optional(),
|
|
149
|
+
cancel: z.literal(true).optional(),
|
|
150
|
+
timeoutMs: z.number().int().min(100).max(EXTENSION_OUTBOUND_MAX_TIMEOUT_MS),
|
|
151
|
+
onTimeout: z.enum(['send', 'cancel']).optional(),
|
|
152
|
+
})
|
|
153
|
+
.strict()
|
|
154
|
+
.refine(
|
|
155
|
+
(value) => value.read === true || value.transform === true || value.cancel === true,
|
|
156
|
+
'outbound 至少声明 read、transform 或 cancel 中的一项。',
|
|
157
|
+
)
|
|
158
|
+
export type ExtensionOutboundCapability = z.output<typeof ExtensionOutboundCapabilitySchema>
|
|
159
|
+
|
|
160
|
+
/** Whether the extension takes part before sending (and so in the agent's processing order). */
|
|
161
|
+
export const outboundProcessesBeforeSend = (outbound: ExtensionOutboundCapability | undefined): boolean =>
|
|
162
|
+
outbound?.transform === true || outbound?.cancel === true
|
|
163
|
+
|
|
131
164
|
export const EXTENSION_JOB_MAX_RUNTIME = 50
|
|
132
165
|
|
|
133
166
|
export const ExtensionDeclaredJobSchema = z
|
|
@@ -255,10 +288,14 @@ export const ExtensionModelsCapabilitySchema = z
|
|
|
255
288
|
.strict()
|
|
256
289
|
export type ExtensionModelsCapability = z.output<typeof ExtensionModelsCapabilitySchema>
|
|
257
290
|
|
|
258
|
-
/**
|
|
291
|
+
/**
|
|
292
|
+
* A header or environment value: literal text, the current value of a `meta.role: 'secret'` config field, or a named
|
|
293
|
+
* credential (by id or name) that the agent may use.
|
|
294
|
+
*/
|
|
259
295
|
export const ExtensionMcpValueSchema = z.union([
|
|
260
296
|
z.string().max(4096),
|
|
261
297
|
z.object({ secret: z.string().trim().min(1).max(64) }).strict(),
|
|
298
|
+
z.object({ credential: z.string().trim().min(1).max(64) }).strict(),
|
|
262
299
|
])
|
|
263
300
|
export type ExtensionMcpValue = z.output<typeof ExtensionMcpValueSchema>
|
|
264
301
|
|
|
@@ -315,6 +352,8 @@ const McpFormValueSchema = z
|
|
|
315
352
|
name: z.string().regex(/^[A-Za-z_][A-Za-z0-9_-]{0,127}$/u, '名称只能包含字母、数字、下划线和连字符。'),
|
|
316
353
|
value: z.string().max(4096),
|
|
317
354
|
secret: z.boolean().default(false),
|
|
355
|
+
/** Takes the value from this named credential instead; `value` and `secret` are then ignored. */
|
|
356
|
+
credential: z.string().trim().min(1).max(64).optional(),
|
|
318
357
|
})
|
|
319
358
|
.strict()
|
|
320
359
|
|
|
@@ -344,7 +383,7 @@ export const mcpSecretFields = (mcp: ExtensionMcpCapability | undefined): readon
|
|
|
344
383
|
...new Set(
|
|
345
384
|
(mcp?.servers ?? []).flatMap((server) =>
|
|
346
385
|
Object.values(server.transport === 'stdio' ? server.env : server.headers).flatMap((value) =>
|
|
347
|
-
typeof value === 'string' ? [] : [value.secret],
|
|
386
|
+
typeof value === 'string' || !('secret' in value) ? [] : [value.secret],
|
|
348
387
|
),
|
|
349
388
|
),
|
|
350
389
|
),
|
|
@@ -373,6 +412,7 @@ export const ExtensionCapabilitiesSchema = z
|
|
|
373
412
|
.strict()
|
|
374
413
|
.optional(),
|
|
375
414
|
inboundHook: ExtensionInboundHookCapabilitySchema.optional(),
|
|
415
|
+
outbound: ExtensionOutboundCapabilitySchema.optional(),
|
|
376
416
|
jobs: ExtensionJobsCapabilitySchema.optional(),
|
|
377
417
|
platform: ExtensionPlatformCapabilitySchema.optional(),
|
|
378
418
|
llm: ExtensionLlmCapabilitySchema.optional(),
|
|
@@ -484,6 +524,18 @@ export const extensionCapabilitiesExpand = (
|
|
|
484
524
|
) {
|
|
485
525
|
return true
|
|
486
526
|
}
|
|
527
|
+
const outbound = next.outbound
|
|
528
|
+
const previousOutbound = previous?.outbound
|
|
529
|
+
if (
|
|
530
|
+
outbound !== undefined &&
|
|
531
|
+
(previousOutbound === undefined ||
|
|
532
|
+
(outbound.read === true && previousOutbound.read !== true) ||
|
|
533
|
+
(outbound.transform === true && previousOutbound.transform !== true) ||
|
|
534
|
+
(outbound.cancel === true && previousOutbound.cancel !== true) ||
|
|
535
|
+
(outbound.onTimeout === 'cancel' && previousOutbound.onTimeout !== 'cancel'))
|
|
536
|
+
) {
|
|
537
|
+
return true
|
|
538
|
+
}
|
|
487
539
|
if (
|
|
488
540
|
next.jobs?.runtime !== undefined &&
|
|
489
541
|
(previous?.jobs?.runtime === undefined || next.jobs.runtime.maxActive > previous.jobs.runtime.maxActive)
|
|
@@ -598,6 +650,21 @@ export const summarizeExtensionCapabilities = (
|
|
|
598
650
|
...(effects === '' ? {} : { detail: `可以${effects}` }),
|
|
599
651
|
})
|
|
600
652
|
}
|
|
653
|
+
const outbound = capabilities.outbound
|
|
654
|
+
if (outbound !== undefined) {
|
|
655
|
+
if (outbound.transform === true) {
|
|
656
|
+
items.push({ key: 'outbound.transform', risk: 'high', label: '在智能体的消息发出前修改内容或追加消息' })
|
|
657
|
+
}
|
|
658
|
+
if (outbound.cancel === true) {
|
|
659
|
+
items.push({ key: 'outbound.cancel', risk: 'high', label: '取消智能体要发出的消息' })
|
|
660
|
+
}
|
|
661
|
+
if (outbound.read === true) {
|
|
662
|
+
items.push({ key: 'outbound.read', risk: 'sensitive', label: '查看智能体发出的每条消息' })
|
|
663
|
+
}
|
|
664
|
+
if (outbound.onTimeout === 'cancel' && (outbound.transform === true || outbound.cancel === true)) {
|
|
665
|
+
items.push({ key: 'outbound.onTimeout', risk: 'high', label: '这个扩展出错时,智能体的消息会发不出去' })
|
|
666
|
+
}
|
|
667
|
+
}
|
|
601
668
|
const jobs = capabilities.jobs
|
|
602
669
|
if (jobs?.declared !== undefined && jobs.declared.length > 0) {
|
|
603
670
|
items.push({
|
package/src/extension-ui.ts
CHANGED
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
HostLayerCapabilitiesSchema,
|
|
5
5
|
hostLayerAsCapabilities,
|
|
6
6
|
EXTENSION_SDK_BASE_LEVEL,
|
|
7
|
+
EXTENSION_OUTBOUND_SDK_LEVEL,
|
|
7
8
|
} from './extension-capabilities.js'
|
|
8
9
|
import { ExtensionIdSchema, ExtensionRevisionIdSchema, HostUiPageInstanceIdSchema } from './domain.js'
|
|
9
10
|
import { ConfigSchemaDocumentSchema } from './config-schema.js'
|
|
@@ -554,6 +555,8 @@ export const requiredExtensionSdkLevel = (
|
|
|
554
555
|
permissions?.host?.index === undefined && permissions?.agent?.index === undefined ? undefined : '检索索引',
|
|
555
556
|
permissions?.host?.models === undefined ? undefined : '本机层模型调用',
|
|
556
557
|
].filter((name): name is string => name !== undefined)
|
|
558
|
+
if (permissions?.agent?.outbound !== undefined)
|
|
559
|
+
return { level: EXTENSION_OUTBOUND_SDK_LEVEL, capabilities: ['出站处理'] }
|
|
557
560
|
return level8.length > 0
|
|
558
561
|
? { level: 8, capabilities: [...new Set(level8)] }
|
|
559
562
|
: { level: EXTENSION_SDK_BASE_LEVEL, capabilities: [] }
|