@sentry/junior-plugin-api 0.68.0 → 0.70.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.d.ts +238 -34
- package/dist/index.js +132 -2
- package/package.json +4 -1
- package/src/index.ts +350 -38
package/dist/index.d.ts
CHANGED
|
@@ -1,9 +1,40 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
/** Runtime-owned provider-neutral address for routing future work or side effects. */
|
|
3
|
+
export declare const destinationSchema: z.ZodObject<{
|
|
4
|
+
platform: z.ZodLiteral<"slack">;
|
|
5
|
+
teamId: z.ZodString;
|
|
6
|
+
channelId: z.ZodString;
|
|
7
|
+
}, z.core.$strict>;
|
|
8
|
+
/** Stable user credential subject shape accepted from plugins. */
|
|
9
|
+
export declare const agentPluginCredentialSubjectSchema: z.ZodObject<{
|
|
10
|
+
type: z.ZodLiteral<"user">;
|
|
11
|
+
userId: z.ZodString;
|
|
12
|
+
allowedWhen: z.ZodLiteral<"private-direct-conversation">;
|
|
13
|
+
}, z.core.$strict>;
|
|
14
|
+
/** Runtime-provided requester identity visible to plugin hooks. */
|
|
15
|
+
export declare const agentPluginRequesterSchema: z.ZodObject<{
|
|
16
|
+
userId: z.ZodOptional<z.ZodString>;
|
|
17
|
+
userName: z.ZodOptional<z.ZodString>;
|
|
18
|
+
fullName: z.ZodOptional<z.ZodString>;
|
|
19
|
+
email: z.ZodOptional<z.ZodString>;
|
|
20
|
+
}, z.core.$strict>;
|
|
21
|
+
/** Plugin dispatch request accepted by Junior core. */
|
|
22
|
+
export declare const dispatchOptionsSchema: z.ZodObject<{
|
|
23
|
+
idempotencyKey: z.ZodPipe<z.ZodString, z.ZodString>;
|
|
24
|
+
credentialSubject: z.ZodOptional<z.ZodObject<{
|
|
25
|
+
type: z.ZodLiteral<"user">;
|
|
26
|
+
userId: z.ZodString;
|
|
27
|
+
allowedWhen: z.ZodLiteral<"private-direct-conversation">;
|
|
28
|
+
}, z.core.$strict>>;
|
|
29
|
+
destination: z.ZodObject<{
|
|
30
|
+
platform: z.ZodLiteral<"slack">;
|
|
31
|
+
teamId: z.ZodString;
|
|
32
|
+
channelId: z.ZodString;
|
|
33
|
+
}, z.core.$strict>;
|
|
34
|
+
input: z.ZodPipe<z.ZodString, z.ZodString>;
|
|
35
|
+
metadata: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
36
|
+
}, z.core.$strict>;
|
|
37
|
+
export type AgentPluginRequester = z.output<typeof agentPluginRequesterSchema>;
|
|
7
38
|
export interface AgentPluginMetadata {
|
|
8
39
|
name: string;
|
|
9
40
|
}
|
|
@@ -20,7 +51,7 @@ export interface AgentPluginLogger {
|
|
|
20
51
|
info(message: string, metadata?: Record<string, unknown>): void;
|
|
21
52
|
warn(message: string, metadata?: Record<string, unknown>): void;
|
|
22
53
|
}
|
|
23
|
-
/** Thrown when a
|
|
54
|
+
/** Thrown when a plugin tool rejects invalid model or user input. */
|
|
24
55
|
export declare class AgentPluginToolInputError extends Error {
|
|
25
56
|
constructor(message: string, options?: {
|
|
26
57
|
cause?: unknown;
|
|
@@ -90,13 +121,36 @@ export interface AgentPluginToolDefinition<TInput = unknown> {
|
|
|
90
121
|
execute?: AgentPluginToolExecute<TInput>;
|
|
91
122
|
}
|
|
92
123
|
export interface ToolRegistrationHookContext extends AgentPluginContext {
|
|
124
|
+
/**
|
|
125
|
+
* Capabilities of `channelId` — the raw conversation channel exposed to
|
|
126
|
+
* this plugin. Recomputed from `channelId`, not from `destination`.
|
|
127
|
+
*/
|
|
93
128
|
channelCapabilities?: {
|
|
94
129
|
canAddReactions: boolean;
|
|
95
130
|
canCreateCanvas: boolean;
|
|
96
131
|
canPostToChannel: boolean;
|
|
97
132
|
};
|
|
133
|
+
/**
|
|
134
|
+
* The raw Slack channel ID for this conversation — the DM or channel where
|
|
135
|
+
* this turn is happening, without any assistant-context-source override.
|
|
136
|
+
* Use this as the stable binding key for state scoped to a Slack conversation.
|
|
137
|
+
* `channelCapabilities` describes this channel.
|
|
138
|
+
*/
|
|
98
139
|
channelId?: string;
|
|
140
|
+
/**
|
|
141
|
+
* Opaque Junior conversation/session identity for this turn.
|
|
142
|
+
* Interactive Slack turns use `slack:{channelId}:{threadTs}`.
|
|
143
|
+
* Scheduled/API turns use an internal id such as `agent-dispatch:{id}`.
|
|
144
|
+
* Do not parse as Slack unless the value starts with `slack:`.
|
|
145
|
+
*/
|
|
146
|
+
conversationId?: string;
|
|
99
147
|
credentialSubject?: AgentPluginCredentialSubject;
|
|
148
|
+
/**
|
|
149
|
+
* Runtime-owned destination suitable for future autonomous dispatch. For
|
|
150
|
+
* Slack, this is the raw conversation channel, not a thread timestamp or
|
|
151
|
+
* assistant-context source channel.
|
|
152
|
+
*/
|
|
153
|
+
destination?: Destination;
|
|
100
154
|
messageTs?: string;
|
|
101
155
|
requester?: AgentPluginRequester;
|
|
102
156
|
state: AgentPluginState;
|
|
@@ -104,22 +158,9 @@ export interface ToolRegistrationHookContext extends AgentPluginContext {
|
|
|
104
158
|
threadTs?: string;
|
|
105
159
|
userText?: string;
|
|
106
160
|
}
|
|
107
|
-
export
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
allowedWhen: "private-direct-conversation";
|
|
111
|
-
}
|
|
112
|
-
export interface DispatchOptions {
|
|
113
|
-
credentialSubject?: AgentPluginCredentialSubject;
|
|
114
|
-
destination: {
|
|
115
|
-
platform: "slack";
|
|
116
|
-
teamId: string;
|
|
117
|
-
channelId: string;
|
|
118
|
-
};
|
|
119
|
-
idempotencyKey: string;
|
|
120
|
-
input: string;
|
|
121
|
-
metadata?: Record<string, string>;
|
|
122
|
-
}
|
|
161
|
+
export type AgentPluginCredentialSubject = z.output<typeof agentPluginCredentialSubjectSchema>;
|
|
162
|
+
export type Destination = z.output<typeof destinationSchema>;
|
|
163
|
+
export type DispatchOptions = z.output<typeof dispatchOptionsSchema>;
|
|
123
164
|
export interface DispatchResult {
|
|
124
165
|
id: string;
|
|
125
166
|
status: "created" | "already_exists";
|
|
@@ -202,9 +243,166 @@ export interface SlackConversationLink {
|
|
|
202
243
|
export interface SlackConversationLinkHookContext extends AgentPluginContext {
|
|
203
244
|
conversationId: string;
|
|
204
245
|
}
|
|
246
|
+
declare const agentPluginGrantAccessSchema: z.ZodUnion<readonly [z.ZodLiteral<"read">, z.ZodLiteral<"write">]>;
|
|
247
|
+
/** Runtime schema for provider authorization a plugin may request. */
|
|
248
|
+
export declare const agentPluginAuthorizationSchema: z.ZodObject<{
|
|
249
|
+
provider: z.ZodString;
|
|
250
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
251
|
+
type: z.ZodLiteral<"oauth">;
|
|
252
|
+
}, z.core.$strict>;
|
|
253
|
+
/** Runtime schema for a provider account attached to stored OAuth tokens. */
|
|
254
|
+
export declare const agentPluginProviderAccountSchema: z.ZodObject<{
|
|
255
|
+
id: z.ZodString;
|
|
256
|
+
label: z.ZodOptional<z.ZodString>;
|
|
257
|
+
url: z.ZodOptional<z.ZodString>;
|
|
258
|
+
}, z.core.$strict>;
|
|
259
|
+
/** Runtime schema for a plugin-defined outbound credential grant. */
|
|
260
|
+
export declare const agentPluginGrantSchema: z.ZodObject<{
|
|
261
|
+
access: z.ZodUnion<readonly [z.ZodLiteral<"read">, z.ZodLiteral<"write">]>;
|
|
262
|
+
name: z.ZodString;
|
|
263
|
+
reason: z.ZodOptional<z.ZodString>;
|
|
264
|
+
requirements: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
265
|
+
}, z.core.$strict>;
|
|
266
|
+
/** Runtime schema for plugin-issued header mutations. */
|
|
267
|
+
export declare const agentPluginCredentialHeaderTransformSchema: z.ZodObject<{
|
|
268
|
+
domain: z.ZodString;
|
|
269
|
+
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
270
|
+
}, z.core.$strict>;
|
|
271
|
+
/** Runtime schema for a short-lived plugin-issued credential lease. */
|
|
272
|
+
export declare const agentPluginCredentialLeaseSchema: z.ZodObject<{
|
|
273
|
+
account: z.ZodOptional<z.ZodObject<{
|
|
274
|
+
id: z.ZodString;
|
|
275
|
+
label: z.ZodOptional<z.ZodString>;
|
|
276
|
+
url: z.ZodOptional<z.ZodString>;
|
|
277
|
+
}, z.core.$strict>>;
|
|
278
|
+
authorization: z.ZodOptional<z.ZodObject<{
|
|
279
|
+
provider: z.ZodString;
|
|
280
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
281
|
+
type: z.ZodLiteral<"oauth">;
|
|
282
|
+
}, z.core.$strict>>;
|
|
283
|
+
expiresAt: z.ZodString;
|
|
284
|
+
headerTransforms: z.ZodArray<z.ZodObject<{
|
|
285
|
+
domain: z.ZodString;
|
|
286
|
+
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
287
|
+
}, z.core.$strict>>;
|
|
288
|
+
}, z.core.$strict>;
|
|
289
|
+
/** Runtime schema for the result returned by a plugin credential hook. */
|
|
290
|
+
export declare const agentPluginCredentialResultSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
291
|
+
lease: z.ZodObject<{
|
|
292
|
+
account: z.ZodOptional<z.ZodObject<{
|
|
293
|
+
id: z.ZodString;
|
|
294
|
+
label: z.ZodOptional<z.ZodString>;
|
|
295
|
+
url: z.ZodOptional<z.ZodString>;
|
|
296
|
+
}, z.core.$strict>>;
|
|
297
|
+
authorization: z.ZodOptional<z.ZodObject<{
|
|
298
|
+
provider: z.ZodString;
|
|
299
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
300
|
+
type: z.ZodLiteral<"oauth">;
|
|
301
|
+
}, z.core.$strict>>;
|
|
302
|
+
expiresAt: z.ZodString;
|
|
303
|
+
headerTransforms: z.ZodArray<z.ZodObject<{
|
|
304
|
+
domain: z.ZodString;
|
|
305
|
+
headers: z.ZodRecord<z.ZodString, z.ZodString>;
|
|
306
|
+
}, z.core.$strict>>;
|
|
307
|
+
}, z.core.$strict>;
|
|
308
|
+
type: z.ZodLiteral<"lease">;
|
|
309
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
310
|
+
authorization: z.ZodOptional<z.ZodObject<{
|
|
311
|
+
provider: z.ZodString;
|
|
312
|
+
scope: z.ZodOptional<z.ZodString>;
|
|
313
|
+
type: z.ZodLiteral<"oauth">;
|
|
314
|
+
}, z.core.$strict>>;
|
|
315
|
+
message: z.ZodString;
|
|
316
|
+
type: z.ZodLiteral<"needed">;
|
|
317
|
+
}, z.core.$strict>, z.ZodObject<{
|
|
318
|
+
message: z.ZodString;
|
|
319
|
+
type: z.ZodLiteral<"unavailable">;
|
|
320
|
+
}, z.core.$strict>], "type">;
|
|
321
|
+
export type AgentPluginGrantAccess = z.output<typeof agentPluginGrantAccessSchema>;
|
|
322
|
+
/** Provider authorization Junior can start when a plugin-owned grant is missing. */
|
|
323
|
+
export type AgentPluginAuthorization = z.output<typeof agentPluginAuthorizationSchema>;
|
|
324
|
+
/** Interrupt sandbox egress so Junior can start provider authorization. */
|
|
325
|
+
export declare class EgressAuthRequired extends Error {
|
|
326
|
+
authorization?: AgentPluginAuthorization;
|
|
327
|
+
constructor(message: string, options?: {
|
|
328
|
+
authorization?: AgentPluginAuthorization;
|
|
329
|
+
cause?: unknown;
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
/** Provider account identity resolved by a plugin OAuth hook. */
|
|
333
|
+
export type AgentPluginProviderAccount = z.output<typeof agentPluginProviderAccountSchema>;
|
|
334
|
+
/** Plugin-defined grant required before Junior can forward one outbound request. */
|
|
335
|
+
export type AgentPluginGrant = z.output<typeof agentPluginGrantSchema>;
|
|
336
|
+
/** Request details available while selecting the grant for sandbox egress. */
|
|
337
|
+
export interface AgentPluginEgressRequest {
|
|
338
|
+
/** Capped request body text when the host exposes it for provider-specific grant classification. */
|
|
339
|
+
bodyText?: string;
|
|
340
|
+
method: string;
|
|
341
|
+
url: string;
|
|
342
|
+
}
|
|
343
|
+
export interface EgressHookContext extends AgentPluginContext {
|
|
344
|
+
request: AgentPluginEgressRequest;
|
|
345
|
+
}
|
|
346
|
+
export interface AgentPluginEgressResponse {
|
|
347
|
+
/** Snapshot of upstream response headers; mutations do not affect pass-through. */
|
|
348
|
+
headers: Headers;
|
|
349
|
+
readText(maxBytes: number): Promise<string | undefined>;
|
|
350
|
+
status: number;
|
|
351
|
+
}
|
|
352
|
+
export interface EgressResponseHookContext extends AgentPluginContext {
|
|
353
|
+
grant: AgentPluginGrant;
|
|
354
|
+
permissionDenied(message: string): void;
|
|
355
|
+
request: Omit<AgentPluginEgressRequest, "bodyText">;
|
|
356
|
+
response: AgentPluginEgressResponse;
|
|
357
|
+
}
|
|
358
|
+
/** Header mutations a plugin-issued credential lease may apply to owned domains. */
|
|
359
|
+
export type AgentPluginCredentialHeaderTransform = z.output<typeof agentPluginCredentialHeaderTransformSchema>;
|
|
360
|
+
/** Short-lived credential headers issued by a plugin for a selected grant. */
|
|
361
|
+
export type AgentPluginCredentialLease = z.output<typeof agentPluginCredentialLeaseSchema>;
|
|
362
|
+
export type AgentPluginCredentialResult = z.output<typeof agentPluginCredentialResultSchema>;
|
|
363
|
+
export type AgentPluginCredentialActor = {
|
|
364
|
+
type: "system";
|
|
365
|
+
id: string;
|
|
366
|
+
} | {
|
|
367
|
+
type: "user";
|
|
368
|
+
userId: string;
|
|
369
|
+
};
|
|
370
|
+
export interface AgentPluginResolvedCredentialUser {
|
|
371
|
+
type: "user";
|
|
372
|
+
userId: string;
|
|
373
|
+
}
|
|
374
|
+
export interface AgentPluginStoredTokens {
|
|
375
|
+
account?: AgentPluginProviderAccount;
|
|
376
|
+
accessToken: string;
|
|
377
|
+
expiresAt?: number;
|
|
378
|
+
refreshToken: string;
|
|
379
|
+
scope?: string;
|
|
380
|
+
}
|
|
381
|
+
export interface AgentPluginUserTokenSlot {
|
|
382
|
+
get(): Promise<AgentPluginStoredTokens | undefined>;
|
|
383
|
+
set(tokens: AgentPluginStoredTokens): Promise<void>;
|
|
384
|
+
userId: string;
|
|
385
|
+
}
|
|
386
|
+
export interface AgentPluginTokenStore {
|
|
387
|
+
credentialSubject?: AgentPluginUserTokenSlot;
|
|
388
|
+
currentUser?: AgentPluginUserTokenSlot;
|
|
389
|
+
}
|
|
390
|
+
export interface ResolveOAuthAccountHookContext extends AgentPluginContext {
|
|
391
|
+
tokens: AgentPluginStoredTokens;
|
|
392
|
+
}
|
|
393
|
+
export interface IssueCredentialHookContext extends AgentPluginContext {
|
|
394
|
+
actor: AgentPluginCredentialActor;
|
|
395
|
+
credentialSubject?: AgentPluginResolvedCredentialUser;
|
|
396
|
+
grant: AgentPluginGrant;
|
|
397
|
+
tokens: AgentPluginTokenStore;
|
|
398
|
+
}
|
|
205
399
|
export interface AgentPluginHooks {
|
|
206
400
|
sandboxPrepare?(ctx: SandboxPrepareHookContext): Promise<void> | void;
|
|
207
401
|
beforeToolExecute?(ctx: BeforeToolExecuteHookContext): Promise<void> | void;
|
|
402
|
+
grantForEgress?(ctx: EgressHookContext): Promise<AgentPluginGrant | undefined> | AgentPluginGrant | undefined;
|
|
403
|
+
issueCredential?(ctx: IssueCredentialHookContext): Promise<AgentPluginCredentialResult> | AgentPluginCredentialResult;
|
|
404
|
+
onEgressResponse?(ctx: EgressResponseHookContext): Promise<void> | void;
|
|
405
|
+
resolveOAuthAccount?(ctx: ResolveOAuthAccountHookContext): Promise<AgentPluginProviderAccount | undefined> | AgentPluginProviderAccount | undefined;
|
|
208
406
|
routes?(ctx: RouteRegistrationHookContext): AgentPluginRoute[];
|
|
209
407
|
tools?(ctx: ToolRegistrationHookContext): Record<string, AgentPluginToolDefinition>;
|
|
210
408
|
heartbeat?(ctx: HeartbeatHookContext): Promise<HeartbeatResult | void> | HeartbeatResult | void;
|
|
@@ -217,6 +415,21 @@ export interface JuniorPluginOAuthConfig {
|
|
|
217
415
|
clientIdEnv: string;
|
|
218
416
|
clientSecretEnv: string;
|
|
219
417
|
scope?: string;
|
|
418
|
+
/**
|
|
419
|
+
* Treat a provider token response with `scope: ""` like an omitted scope and
|
|
420
|
+
* fall back to the requested scope string when storing the token.
|
|
421
|
+
*
|
|
422
|
+
* Enable this only for providers whose token responses cannot report OAuth
|
|
423
|
+
* scopes even though Junior needs a local requested-scope string for
|
|
424
|
+
* reauthorization checks. The built-in GitHub App plugin enables this because
|
|
425
|
+
* GitHub App user-to-server tokens always return an empty scope value — their
|
|
426
|
+
* effective access is enforced by GitHub App permissions, installation
|
|
427
|
+
* repository access, and the requesting user's own access, not OAuth scopes.
|
|
428
|
+
*
|
|
429
|
+
* Do not enable this for standard OAuth providers where an explicit empty
|
|
430
|
+
* `scope` means the provider granted no scopes.
|
|
431
|
+
*/
|
|
432
|
+
treatEmptyScopeAsUnreported?: boolean;
|
|
220
433
|
tokenAuthMethod?: "body" | "basic";
|
|
221
434
|
tokenEndpoint: string;
|
|
222
435
|
tokenExtraHeaders?: Record<string, string>;
|
|
@@ -228,17 +441,7 @@ export interface JuniorPluginOAuthBearerCredentials {
|
|
|
228
441
|
domains: string[];
|
|
229
442
|
type: "oauth-bearer";
|
|
230
443
|
}
|
|
231
|
-
export
|
|
232
|
-
apiHeaders?: Record<string, string>;
|
|
233
|
-
appIdEnv: string;
|
|
234
|
-
authTokenEnv: string;
|
|
235
|
-
authTokenPlaceholder?: string;
|
|
236
|
-
domains: string[];
|
|
237
|
-
installationIdEnv: string;
|
|
238
|
-
privateKeyEnv: string;
|
|
239
|
-
type: "github-app";
|
|
240
|
-
}
|
|
241
|
-
export type JuniorPluginCredentials = JuniorPluginOAuthBearerCredentials | JuniorPluginGitHubAppCredentials;
|
|
444
|
+
export type JuniorPluginCredentials = JuniorPluginOAuthBearerCredentials;
|
|
242
445
|
export interface JuniorPluginNpmRuntimeDependency {
|
|
243
446
|
package: string;
|
|
244
447
|
type: "npm";
|
|
@@ -301,3 +504,4 @@ export interface JuniorPluginRegistration extends JuniorPluginRegistrationInput
|
|
|
301
504
|
}
|
|
302
505
|
/** Define one Junior plugin registration for app and build-time wiring. */
|
|
303
506
|
export declare function defineJuniorPlugin(plugin: JuniorPluginRegistrationInput): JuniorPluginRegistration;
|
|
507
|
+
export {};
|
package/dist/index.js
CHANGED
|
@@ -1,15 +1,134 @@
|
|
|
1
1
|
// src/index.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var slackTeamIdSchema = z.string().regex(/^T[A-Z0-9]+$/);
|
|
4
|
+
var slackConversationIdSchema = z.string().regex(/^(C|G|D)[A-Z0-9]+$/);
|
|
5
|
+
var exactActorUserIdSchema = z.string().min(1).refine(
|
|
6
|
+
(value) => value === value.trim() && value.toLowerCase() !== "unknown"
|
|
7
|
+
);
|
|
8
|
+
var nonBlankStringSchema = z.string().refine((value) => value.trim().length > 0);
|
|
9
|
+
var destinationSchema = z.object({
|
|
10
|
+
platform: z.literal("slack"),
|
|
11
|
+
teamId: slackTeamIdSchema,
|
|
12
|
+
channelId: slackConversationIdSchema
|
|
13
|
+
}).strict();
|
|
14
|
+
var agentPluginCredentialSubjectSchema = z.object({
|
|
15
|
+
type: z.literal("user"),
|
|
16
|
+
userId: exactActorUserIdSchema,
|
|
17
|
+
allowedWhen: z.literal("private-direct-conversation")
|
|
18
|
+
}).strict();
|
|
19
|
+
var agentPluginRequesterSchema = z.object({
|
|
20
|
+
userId: exactActorUserIdSchema.optional(),
|
|
21
|
+
userName: nonBlankStringSchema.optional(),
|
|
22
|
+
fullName: nonBlankStringSchema.optional(),
|
|
23
|
+
email: nonBlankStringSchema.optional()
|
|
24
|
+
}).strict();
|
|
25
|
+
var dispatchMetadataSchema = z.record(z.string(), z.string()).superRefine((metadata, ctx) => {
|
|
26
|
+
const entries = Object.entries(metadata);
|
|
27
|
+
if (entries.length > 20) {
|
|
28
|
+
ctx.addIssue({
|
|
29
|
+
code: z.ZodIssueCode.custom,
|
|
30
|
+
message: "Dispatch metadata has too many keys"
|
|
31
|
+
});
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
for (const [key, value] of entries) {
|
|
35
|
+
if (!key.trim()) {
|
|
36
|
+
ctx.addIssue({
|
|
37
|
+
code: z.ZodIssueCode.custom,
|
|
38
|
+
message: "Dispatch metadata values must be strings",
|
|
39
|
+
path: [key]
|
|
40
|
+
});
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
if (key.length > 128) {
|
|
44
|
+
ctx.addIssue({
|
|
45
|
+
code: z.ZodIssueCode.custom,
|
|
46
|
+
message: "Dispatch metadata key exceeds the maximum length",
|
|
47
|
+
path: [key]
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
if (value.length > 512) {
|
|
51
|
+
ctx.addIssue({
|
|
52
|
+
code: z.ZodIssueCode.custom,
|
|
53
|
+
message: "Dispatch metadata value exceeds the maximum length",
|
|
54
|
+
path: [key]
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
var dispatchOptionsSchema = z.object({
|
|
60
|
+
idempotencyKey: nonBlankStringSchema.pipe(z.string().max(512)),
|
|
61
|
+
credentialSubject: agentPluginCredentialSubjectSchema.optional(),
|
|
62
|
+
destination: destinationSchema,
|
|
63
|
+
input: nonBlankStringSchema.pipe(z.string().max(32e3)),
|
|
64
|
+
metadata: dispatchMetadataSchema.optional()
|
|
65
|
+
}).strict();
|
|
2
66
|
var AgentPluginToolInputError = class extends Error {
|
|
3
67
|
constructor(message, options) {
|
|
4
68
|
super(message, options);
|
|
5
69
|
this.name = "AgentPluginToolInputError";
|
|
6
70
|
}
|
|
7
71
|
};
|
|
72
|
+
var agentPluginProviderNameSchema = z.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
73
|
+
var agentPluginGrantNameSchema = z.string().regex(/^[a-z][a-z0-9.-]*$/);
|
|
74
|
+
var agentPluginGrantAccessSchema = z.union([
|
|
75
|
+
z.literal("read"),
|
|
76
|
+
z.literal("write")
|
|
77
|
+
]);
|
|
78
|
+
var agentPluginAuthorizationSchema = z.object({
|
|
79
|
+
provider: agentPluginProviderNameSchema,
|
|
80
|
+
scope: nonBlankStringSchema.optional(),
|
|
81
|
+
type: z.literal("oauth")
|
|
82
|
+
}).strict();
|
|
83
|
+
var agentPluginProviderAccountSchema = z.object({
|
|
84
|
+
id: nonBlankStringSchema,
|
|
85
|
+
label: nonBlankStringSchema.optional(),
|
|
86
|
+
url: nonBlankStringSchema.optional()
|
|
87
|
+
}).strict();
|
|
88
|
+
var agentPluginGrantSchema = z.object({
|
|
89
|
+
access: agentPluginGrantAccessSchema,
|
|
90
|
+
name: agentPluginGrantNameSchema,
|
|
91
|
+
reason: nonBlankStringSchema.optional(),
|
|
92
|
+
requirements: z.array(nonBlankStringSchema).min(1).optional()
|
|
93
|
+
}).strict();
|
|
94
|
+
var agentPluginCredentialHeaderTransformSchema = z.object({
|
|
95
|
+
domain: z.string().min(1),
|
|
96
|
+
headers: z.record(z.string(), z.string()).refine((headers) => Object.keys(headers).length > 0)
|
|
97
|
+
}).strict();
|
|
98
|
+
var agentPluginCredentialLeaseSchema = z.object({
|
|
99
|
+
account: agentPluginProviderAccountSchema.optional(),
|
|
100
|
+
authorization: agentPluginAuthorizationSchema.optional(),
|
|
101
|
+
expiresAt: z.string().refine((value) => Number.isFinite(Date.parse(value))),
|
|
102
|
+
headerTransforms: z.array(agentPluginCredentialHeaderTransformSchema).min(1)
|
|
103
|
+
}).strict();
|
|
104
|
+
var agentPluginCredentialResultSchema = z.discriminatedUnion("type", [
|
|
105
|
+
z.object({
|
|
106
|
+
lease: agentPluginCredentialLeaseSchema,
|
|
107
|
+
type: z.literal("lease")
|
|
108
|
+
}).strict(),
|
|
109
|
+
z.object({
|
|
110
|
+
authorization: agentPluginAuthorizationSchema.optional(),
|
|
111
|
+
message: nonBlankStringSchema,
|
|
112
|
+
type: z.literal("needed")
|
|
113
|
+
}).strict(),
|
|
114
|
+
z.object({
|
|
115
|
+
message: nonBlankStringSchema,
|
|
116
|
+
type: z.literal("unavailable")
|
|
117
|
+
}).strict()
|
|
118
|
+
]);
|
|
119
|
+
var EgressAuthRequired = class extends Error {
|
|
120
|
+
authorization;
|
|
121
|
+
constructor(message, options) {
|
|
122
|
+
super(message, { cause: options?.cause });
|
|
123
|
+
this.name = "EgressAuthRequired";
|
|
124
|
+
this.authorization = options?.authorization;
|
|
125
|
+
}
|
|
126
|
+
};
|
|
8
127
|
var PLUGIN_NAME_RE = /^[a-z][a-z0-9-]*$/;
|
|
9
128
|
function defineJuniorPlugin(plugin) {
|
|
10
129
|
if ("pluginConfig" in plugin) {
|
|
11
130
|
throw new Error(
|
|
12
|
-
"pluginConfig is no longer supported. Put runtime metadata in manifest and
|
|
131
|
+
"pluginConfig is no longer supported. Put runtime metadata in manifest and state prefixes on the plugin registration."
|
|
13
132
|
);
|
|
14
133
|
}
|
|
15
134
|
const manifest = plugin.manifest;
|
|
@@ -46,5 +165,16 @@ function defineJuniorPlugin(plugin) {
|
|
|
46
165
|
}
|
|
47
166
|
export {
|
|
48
167
|
AgentPluginToolInputError,
|
|
49
|
-
|
|
168
|
+
EgressAuthRequired,
|
|
169
|
+
agentPluginAuthorizationSchema,
|
|
170
|
+
agentPluginCredentialHeaderTransformSchema,
|
|
171
|
+
agentPluginCredentialLeaseSchema,
|
|
172
|
+
agentPluginCredentialResultSchema,
|
|
173
|
+
agentPluginCredentialSubjectSchema,
|
|
174
|
+
agentPluginGrantSchema,
|
|
175
|
+
agentPluginProviderAccountSchema,
|
|
176
|
+
agentPluginRequesterSchema,
|
|
177
|
+
defineJuniorPlugin,
|
|
178
|
+
destinationSchema,
|
|
179
|
+
dispatchOptionsSchema
|
|
50
180
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior-plugin-api",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.70.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -21,6 +21,9 @@
|
|
|
21
21
|
"dist",
|
|
22
22
|
"src"
|
|
23
23
|
],
|
|
24
|
+
"dependencies": {
|
|
25
|
+
"zod": "^4.4.3"
|
|
26
|
+
},
|
|
24
27
|
"devDependencies": {
|
|
25
28
|
"oxlint": "^1.66.0",
|
|
26
29
|
"tsup": "^8.5.1",
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,94 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
|
|
3
|
+
const slackTeamIdSchema = z.string().regex(/^T[A-Z0-9]+$/);
|
|
4
|
+
const slackConversationIdSchema = z.string().regex(/^(C|G|D)[A-Z0-9]+$/);
|
|
5
|
+
const exactActorUserIdSchema = z
|
|
6
|
+
.string()
|
|
7
|
+
.min(1)
|
|
8
|
+
.refine(
|
|
9
|
+
(value) => value === value.trim() && value.toLowerCase() !== "unknown",
|
|
10
|
+
);
|
|
11
|
+
const nonBlankStringSchema = z
|
|
12
|
+
.string()
|
|
13
|
+
.refine((value) => value.trim().length > 0);
|
|
14
|
+
|
|
15
|
+
/** Runtime-owned provider-neutral address for routing future work or side effects. */
|
|
16
|
+
export const destinationSchema = z
|
|
17
|
+
.object({
|
|
18
|
+
platform: z.literal("slack"),
|
|
19
|
+
teamId: slackTeamIdSchema,
|
|
20
|
+
channelId: slackConversationIdSchema,
|
|
21
|
+
})
|
|
22
|
+
.strict();
|
|
23
|
+
|
|
24
|
+
/** Stable user credential subject shape accepted from plugins. */
|
|
25
|
+
export const agentPluginCredentialSubjectSchema = z
|
|
26
|
+
.object({
|
|
27
|
+
type: z.literal("user"),
|
|
28
|
+
userId: exactActorUserIdSchema,
|
|
29
|
+
allowedWhen: z.literal("private-direct-conversation"),
|
|
30
|
+
})
|
|
31
|
+
.strict();
|
|
32
|
+
|
|
33
|
+
/** Runtime-provided requester identity visible to plugin hooks. */
|
|
34
|
+
export const agentPluginRequesterSchema = z
|
|
35
|
+
.object({
|
|
36
|
+
userId: exactActorUserIdSchema.optional(),
|
|
37
|
+
userName: nonBlankStringSchema.optional(),
|
|
38
|
+
fullName: nonBlankStringSchema.optional(),
|
|
39
|
+
email: nonBlankStringSchema.optional(),
|
|
40
|
+
})
|
|
41
|
+
.strict();
|
|
42
|
+
|
|
43
|
+
const dispatchMetadataSchema = z
|
|
44
|
+
.record(z.string(), z.string())
|
|
45
|
+
.superRefine((metadata, ctx) => {
|
|
46
|
+
const entries = Object.entries(metadata);
|
|
47
|
+
if (entries.length > 20) {
|
|
48
|
+
ctx.addIssue({
|
|
49
|
+
code: z.ZodIssueCode.custom,
|
|
50
|
+
message: "Dispatch metadata has too many keys",
|
|
51
|
+
});
|
|
52
|
+
return;
|
|
53
|
+
}
|
|
54
|
+
for (const [key, value] of entries) {
|
|
55
|
+
if (!key.trim()) {
|
|
56
|
+
ctx.addIssue({
|
|
57
|
+
code: z.ZodIssueCode.custom,
|
|
58
|
+
message: "Dispatch metadata values must be strings",
|
|
59
|
+
path: [key],
|
|
60
|
+
});
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (key.length > 128) {
|
|
64
|
+
ctx.addIssue({
|
|
65
|
+
code: z.ZodIssueCode.custom,
|
|
66
|
+
message: "Dispatch metadata key exceeds the maximum length",
|
|
67
|
+
path: [key],
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
if (value.length > 512) {
|
|
71
|
+
ctx.addIssue({
|
|
72
|
+
code: z.ZodIssueCode.custom,
|
|
73
|
+
message: "Dispatch metadata value exceeds the maximum length",
|
|
74
|
+
path: [key],
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
/** Plugin dispatch request accepted by Junior core. */
|
|
81
|
+
export const dispatchOptionsSchema = z
|
|
82
|
+
.object({
|
|
83
|
+
idempotencyKey: nonBlankStringSchema.pipe(z.string().max(512)),
|
|
84
|
+
credentialSubject: agentPluginCredentialSubjectSchema.optional(),
|
|
85
|
+
destination: destinationSchema,
|
|
86
|
+
input: nonBlankStringSchema.pipe(z.string().max(32_000)),
|
|
87
|
+
metadata: dispatchMetadataSchema.optional(),
|
|
88
|
+
})
|
|
89
|
+
.strict();
|
|
90
|
+
|
|
91
|
+
export type AgentPluginRequester = z.output<typeof agentPluginRequesterSchema>;
|
|
7
92
|
|
|
8
93
|
export interface AgentPluginMetadata {
|
|
9
94
|
name: string;
|
|
@@ -25,7 +110,7 @@ export interface AgentPluginLogger {
|
|
|
25
110
|
warn(message: string, metadata?: Record<string, unknown>): void;
|
|
26
111
|
}
|
|
27
112
|
|
|
28
|
-
/** Thrown when a
|
|
113
|
+
/** Thrown when a plugin tool rejects invalid model or user input. */
|
|
29
114
|
export class AgentPluginToolInputError extends Error {
|
|
30
115
|
constructor(message: string, options?: { cause?: unknown }) {
|
|
31
116
|
super(message, options);
|
|
@@ -104,13 +189,36 @@ export interface AgentPluginToolDefinition<TInput = unknown> {
|
|
|
104
189
|
}
|
|
105
190
|
|
|
106
191
|
export interface ToolRegistrationHookContext extends AgentPluginContext {
|
|
192
|
+
/**
|
|
193
|
+
* Capabilities of `channelId` — the raw conversation channel exposed to
|
|
194
|
+
* this plugin. Recomputed from `channelId`, not from `destination`.
|
|
195
|
+
*/
|
|
107
196
|
channelCapabilities?: {
|
|
108
197
|
canAddReactions: boolean;
|
|
109
198
|
canCreateCanvas: boolean;
|
|
110
199
|
canPostToChannel: boolean;
|
|
111
200
|
};
|
|
201
|
+
/**
|
|
202
|
+
* The raw Slack channel ID for this conversation — the DM or channel where
|
|
203
|
+
* this turn is happening, without any assistant-context-source override.
|
|
204
|
+
* Use this as the stable binding key for state scoped to a Slack conversation.
|
|
205
|
+
* `channelCapabilities` describes this channel.
|
|
206
|
+
*/
|
|
112
207
|
channelId?: string;
|
|
208
|
+
/**
|
|
209
|
+
* Opaque Junior conversation/session identity for this turn.
|
|
210
|
+
* Interactive Slack turns use `slack:{channelId}:{threadTs}`.
|
|
211
|
+
* Scheduled/API turns use an internal id such as `agent-dispatch:{id}`.
|
|
212
|
+
* Do not parse as Slack unless the value starts with `slack:`.
|
|
213
|
+
*/
|
|
214
|
+
conversationId?: string;
|
|
113
215
|
credentialSubject?: AgentPluginCredentialSubject;
|
|
216
|
+
/**
|
|
217
|
+
* Runtime-owned destination suitable for future autonomous dispatch. For
|
|
218
|
+
* Slack, this is the raw conversation channel, not a thread timestamp or
|
|
219
|
+
* assistant-context source channel.
|
|
220
|
+
*/
|
|
221
|
+
destination?: Destination;
|
|
114
222
|
messageTs?: string;
|
|
115
223
|
requester?: AgentPluginRequester;
|
|
116
224
|
state: AgentPluginState;
|
|
@@ -119,23 +227,13 @@ export interface ToolRegistrationHookContext extends AgentPluginContext {
|
|
|
119
227
|
userText?: string;
|
|
120
228
|
}
|
|
121
229
|
|
|
122
|
-
export
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
allowedWhen: "private-direct-conversation";
|
|
126
|
-
}
|
|
230
|
+
export type AgentPluginCredentialSubject = z.output<
|
|
231
|
+
typeof agentPluginCredentialSubjectSchema
|
|
232
|
+
>;
|
|
127
233
|
|
|
128
|
-
export
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
platform: "slack";
|
|
132
|
-
teamId: string;
|
|
133
|
-
channelId: string;
|
|
134
|
-
};
|
|
135
|
-
idempotencyKey: string;
|
|
136
|
-
input: string;
|
|
137
|
-
metadata?: Record<string, string>;
|
|
138
|
-
}
|
|
234
|
+
export type Destination = z.output<typeof destinationSchema>;
|
|
235
|
+
|
|
236
|
+
export type DispatchOptions = z.output<typeof dispatchOptionsSchema>;
|
|
139
237
|
|
|
140
238
|
export interface DispatchResult {
|
|
141
239
|
id: string;
|
|
@@ -256,9 +354,221 @@ export interface SlackConversationLinkHookContext extends AgentPluginContext {
|
|
|
256
354
|
conversationId: string;
|
|
257
355
|
}
|
|
258
356
|
|
|
357
|
+
const agentPluginProviderNameSchema = z.string().regex(/^[a-z][a-z0-9-]*$/);
|
|
358
|
+
const agentPluginGrantNameSchema = z.string().regex(/^[a-z][a-z0-9.-]*$/);
|
|
359
|
+
const agentPluginGrantAccessSchema = z.union([
|
|
360
|
+
z.literal("read"),
|
|
361
|
+
z.literal("write"),
|
|
362
|
+
]);
|
|
363
|
+
|
|
364
|
+
/** Runtime schema for provider authorization a plugin may request. */
|
|
365
|
+
export const agentPluginAuthorizationSchema = z
|
|
366
|
+
.object({
|
|
367
|
+
provider: agentPluginProviderNameSchema,
|
|
368
|
+
scope: nonBlankStringSchema.optional(),
|
|
369
|
+
type: z.literal("oauth"),
|
|
370
|
+
})
|
|
371
|
+
.strict();
|
|
372
|
+
|
|
373
|
+
/** Runtime schema for a provider account attached to stored OAuth tokens. */
|
|
374
|
+
export const agentPluginProviderAccountSchema = z
|
|
375
|
+
.object({
|
|
376
|
+
id: nonBlankStringSchema,
|
|
377
|
+
label: nonBlankStringSchema.optional(),
|
|
378
|
+
url: nonBlankStringSchema.optional(),
|
|
379
|
+
})
|
|
380
|
+
.strict();
|
|
381
|
+
|
|
382
|
+
/** Runtime schema for a plugin-defined outbound credential grant. */
|
|
383
|
+
export const agentPluginGrantSchema = z
|
|
384
|
+
.object({
|
|
385
|
+
access: agentPluginGrantAccessSchema,
|
|
386
|
+
name: agentPluginGrantNameSchema,
|
|
387
|
+
reason: nonBlankStringSchema.optional(),
|
|
388
|
+
requirements: z.array(nonBlankStringSchema).min(1).optional(),
|
|
389
|
+
})
|
|
390
|
+
.strict();
|
|
391
|
+
|
|
392
|
+
/** Runtime schema for plugin-issued header mutations. */
|
|
393
|
+
export const agentPluginCredentialHeaderTransformSchema = z
|
|
394
|
+
.object({
|
|
395
|
+
domain: z.string().min(1),
|
|
396
|
+
headers: z
|
|
397
|
+
.record(z.string(), z.string())
|
|
398
|
+
.refine((headers) => Object.keys(headers).length > 0),
|
|
399
|
+
})
|
|
400
|
+
.strict();
|
|
401
|
+
|
|
402
|
+
/** Runtime schema for a short-lived plugin-issued credential lease. */
|
|
403
|
+
export const agentPluginCredentialLeaseSchema = z
|
|
404
|
+
.object({
|
|
405
|
+
account: agentPluginProviderAccountSchema.optional(),
|
|
406
|
+
authorization: agentPluginAuthorizationSchema.optional(),
|
|
407
|
+
expiresAt: z.string().refine((value) => Number.isFinite(Date.parse(value))),
|
|
408
|
+
headerTransforms: z
|
|
409
|
+
.array(agentPluginCredentialHeaderTransformSchema)
|
|
410
|
+
.min(1),
|
|
411
|
+
})
|
|
412
|
+
.strict();
|
|
413
|
+
|
|
414
|
+
/** Runtime schema for the result returned by a plugin credential hook. */
|
|
415
|
+
export const agentPluginCredentialResultSchema = z.discriminatedUnion("type", [
|
|
416
|
+
z
|
|
417
|
+
.object({
|
|
418
|
+
lease: agentPluginCredentialLeaseSchema,
|
|
419
|
+
type: z.literal("lease"),
|
|
420
|
+
})
|
|
421
|
+
.strict(),
|
|
422
|
+
z
|
|
423
|
+
.object({
|
|
424
|
+
authorization: agentPluginAuthorizationSchema.optional(),
|
|
425
|
+
message: nonBlankStringSchema,
|
|
426
|
+
type: z.literal("needed"),
|
|
427
|
+
})
|
|
428
|
+
.strict(),
|
|
429
|
+
z
|
|
430
|
+
.object({
|
|
431
|
+
message: nonBlankStringSchema,
|
|
432
|
+
type: z.literal("unavailable"),
|
|
433
|
+
})
|
|
434
|
+
.strict(),
|
|
435
|
+
]);
|
|
436
|
+
|
|
437
|
+
export type AgentPluginGrantAccess = z.output<
|
|
438
|
+
typeof agentPluginGrantAccessSchema
|
|
439
|
+
>;
|
|
440
|
+
|
|
441
|
+
/** Provider authorization Junior can start when a plugin-owned grant is missing. */
|
|
442
|
+
export type AgentPluginAuthorization = z.output<
|
|
443
|
+
typeof agentPluginAuthorizationSchema
|
|
444
|
+
>;
|
|
445
|
+
|
|
446
|
+
/** Interrupt sandbox egress so Junior can start provider authorization. */
|
|
447
|
+
export class EgressAuthRequired extends Error {
|
|
448
|
+
authorization?: AgentPluginAuthorization;
|
|
449
|
+
|
|
450
|
+
constructor(
|
|
451
|
+
message: string,
|
|
452
|
+
options?: {
|
|
453
|
+
authorization?: AgentPluginAuthorization;
|
|
454
|
+
cause?: unknown;
|
|
455
|
+
},
|
|
456
|
+
) {
|
|
457
|
+
super(message, { cause: options?.cause });
|
|
458
|
+
this.name = "EgressAuthRequired";
|
|
459
|
+
this.authorization = options?.authorization;
|
|
460
|
+
}
|
|
461
|
+
}
|
|
462
|
+
|
|
463
|
+
/** Provider account identity resolved by a plugin OAuth hook. */
|
|
464
|
+
export type AgentPluginProviderAccount = z.output<
|
|
465
|
+
typeof agentPluginProviderAccountSchema
|
|
466
|
+
>;
|
|
467
|
+
|
|
468
|
+
/** Plugin-defined grant required before Junior can forward one outbound request. */
|
|
469
|
+
export type AgentPluginGrant = z.output<typeof agentPluginGrantSchema>;
|
|
470
|
+
|
|
471
|
+
/** Request details available while selecting the grant for sandbox egress. */
|
|
472
|
+
export interface AgentPluginEgressRequest {
|
|
473
|
+
/** Capped request body text when the host exposes it for provider-specific grant classification. */
|
|
474
|
+
bodyText?: string;
|
|
475
|
+
method: string;
|
|
476
|
+
url: string;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
export interface EgressHookContext extends AgentPluginContext {
|
|
480
|
+
request: AgentPluginEgressRequest;
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
export interface AgentPluginEgressResponse {
|
|
484
|
+
/** Snapshot of upstream response headers; mutations do not affect pass-through. */
|
|
485
|
+
headers: Headers;
|
|
486
|
+
readText(maxBytes: number): Promise<string | undefined>;
|
|
487
|
+
status: number;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
export interface EgressResponseHookContext extends AgentPluginContext {
|
|
491
|
+
grant: AgentPluginGrant;
|
|
492
|
+
permissionDenied(message: string): void;
|
|
493
|
+
request: Omit<AgentPluginEgressRequest, "bodyText">;
|
|
494
|
+
response: AgentPluginEgressResponse;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
/** Header mutations a plugin-issued credential lease may apply to owned domains. */
|
|
498
|
+
export type AgentPluginCredentialHeaderTransform = z.output<
|
|
499
|
+
typeof agentPluginCredentialHeaderTransformSchema
|
|
500
|
+
>;
|
|
501
|
+
|
|
502
|
+
/** Short-lived credential headers issued by a plugin for a selected grant. */
|
|
503
|
+
export type AgentPluginCredentialLease = z.output<
|
|
504
|
+
typeof agentPluginCredentialLeaseSchema
|
|
505
|
+
>;
|
|
506
|
+
|
|
507
|
+
export type AgentPluginCredentialResult = z.output<
|
|
508
|
+
typeof agentPluginCredentialResultSchema
|
|
509
|
+
>;
|
|
510
|
+
|
|
511
|
+
export type AgentPluginCredentialActor =
|
|
512
|
+
| {
|
|
513
|
+
type: "system";
|
|
514
|
+
id: string;
|
|
515
|
+
}
|
|
516
|
+
| {
|
|
517
|
+
type: "user";
|
|
518
|
+
userId: string;
|
|
519
|
+
};
|
|
520
|
+
|
|
521
|
+
export interface AgentPluginResolvedCredentialUser {
|
|
522
|
+
type: "user";
|
|
523
|
+
userId: string;
|
|
524
|
+
}
|
|
525
|
+
|
|
526
|
+
export interface AgentPluginStoredTokens {
|
|
527
|
+
account?: AgentPluginProviderAccount;
|
|
528
|
+
accessToken: string;
|
|
529
|
+
expiresAt?: number;
|
|
530
|
+
refreshToken: string;
|
|
531
|
+
scope?: string;
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
export interface AgentPluginUserTokenSlot {
|
|
535
|
+
get(): Promise<AgentPluginStoredTokens | undefined>;
|
|
536
|
+
set(tokens: AgentPluginStoredTokens): Promise<void>;
|
|
537
|
+
userId: string;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
export interface AgentPluginTokenStore {
|
|
541
|
+
credentialSubject?: AgentPluginUserTokenSlot;
|
|
542
|
+
currentUser?: AgentPluginUserTokenSlot;
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
export interface ResolveOAuthAccountHookContext extends AgentPluginContext {
|
|
546
|
+
tokens: AgentPluginStoredTokens;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
export interface IssueCredentialHookContext extends AgentPluginContext {
|
|
550
|
+
actor: AgentPluginCredentialActor;
|
|
551
|
+
credentialSubject?: AgentPluginResolvedCredentialUser;
|
|
552
|
+
grant: AgentPluginGrant;
|
|
553
|
+
tokens: AgentPluginTokenStore;
|
|
554
|
+
}
|
|
555
|
+
|
|
259
556
|
export interface AgentPluginHooks {
|
|
260
557
|
sandboxPrepare?(ctx: SandboxPrepareHookContext): Promise<void> | void;
|
|
261
558
|
beforeToolExecute?(ctx: BeforeToolExecuteHookContext): Promise<void> | void;
|
|
559
|
+
grantForEgress?(
|
|
560
|
+
ctx: EgressHookContext,
|
|
561
|
+
): Promise<AgentPluginGrant | undefined> | AgentPluginGrant | undefined;
|
|
562
|
+
issueCredential?(
|
|
563
|
+
ctx: IssueCredentialHookContext,
|
|
564
|
+
): Promise<AgentPluginCredentialResult> | AgentPluginCredentialResult;
|
|
565
|
+
onEgressResponse?(ctx: EgressResponseHookContext): Promise<void> | void;
|
|
566
|
+
resolveOAuthAccount?(
|
|
567
|
+
ctx: ResolveOAuthAccountHookContext,
|
|
568
|
+
):
|
|
569
|
+
| Promise<AgentPluginProviderAccount | undefined>
|
|
570
|
+
| AgentPluginProviderAccount
|
|
571
|
+
| undefined;
|
|
262
572
|
routes?(ctx: RouteRegistrationHookContext): AgentPluginRoute[];
|
|
263
573
|
tools?(
|
|
264
574
|
ctx: ToolRegistrationHookContext,
|
|
@@ -283,6 +593,21 @@ export interface JuniorPluginOAuthConfig {
|
|
|
283
593
|
clientIdEnv: string;
|
|
284
594
|
clientSecretEnv: string;
|
|
285
595
|
scope?: string;
|
|
596
|
+
/**
|
|
597
|
+
* Treat a provider token response with `scope: ""` like an omitted scope and
|
|
598
|
+
* fall back to the requested scope string when storing the token.
|
|
599
|
+
*
|
|
600
|
+
* Enable this only for providers whose token responses cannot report OAuth
|
|
601
|
+
* scopes even though Junior needs a local requested-scope string for
|
|
602
|
+
* reauthorization checks. The built-in GitHub App plugin enables this because
|
|
603
|
+
* GitHub App user-to-server tokens always return an empty scope value — their
|
|
604
|
+
* effective access is enforced by GitHub App permissions, installation
|
|
605
|
+
* repository access, and the requesting user's own access, not OAuth scopes.
|
|
606
|
+
*
|
|
607
|
+
* Do not enable this for standard OAuth providers where an explicit empty
|
|
608
|
+
* `scope` means the provider granted no scopes.
|
|
609
|
+
*/
|
|
610
|
+
treatEmptyScopeAsUnreported?: boolean;
|
|
286
611
|
tokenAuthMethod?: "body" | "basic";
|
|
287
612
|
tokenEndpoint: string;
|
|
288
613
|
tokenExtraHeaders?: Record<string, string>;
|
|
@@ -296,20 +621,7 @@ export interface JuniorPluginOAuthBearerCredentials {
|
|
|
296
621
|
type: "oauth-bearer";
|
|
297
622
|
}
|
|
298
623
|
|
|
299
|
-
export
|
|
300
|
-
apiHeaders?: Record<string, string>;
|
|
301
|
-
appIdEnv: string;
|
|
302
|
-
authTokenEnv: string;
|
|
303
|
-
authTokenPlaceholder?: string;
|
|
304
|
-
domains: string[];
|
|
305
|
-
installationIdEnv: string;
|
|
306
|
-
privateKeyEnv: string;
|
|
307
|
-
type: "github-app";
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
export type JuniorPluginCredentials =
|
|
311
|
-
| JuniorPluginOAuthBearerCredentials
|
|
312
|
-
| JuniorPluginGitHubAppCredentials;
|
|
624
|
+
export type JuniorPluginCredentials = JuniorPluginOAuthBearerCredentials;
|
|
313
625
|
|
|
314
626
|
export interface JuniorPluginNpmRuntimeDependency {
|
|
315
627
|
package: string;
|
|
@@ -392,7 +704,7 @@ export function defineJuniorPlugin(
|
|
|
392
704
|
): JuniorPluginRegistration {
|
|
393
705
|
if ("pluginConfig" in plugin) {
|
|
394
706
|
throw new Error(
|
|
395
|
-
"pluginConfig is no longer supported. Put runtime metadata in manifest and
|
|
707
|
+
"pluginConfig is no longer supported. Put runtime metadata in manifest and state prefixes on the plugin registration.",
|
|
396
708
|
);
|
|
397
709
|
}
|
|
398
710
|
const manifest = plugin.manifest;
|