@sentry/junior-plugin-api 0.74.0 → 0.75.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts CHANGED
@@ -1,875 +1,11 @@
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 localConversationIdSchema = z
6
- .string()
7
- .regex(/^local:[a-z0-9_-]+:[a-z0-9][a-z0-9_-]*$/);
8
- const exactActorUserIdSchema = z
9
- .string()
10
- .min(1)
11
- .refine(
12
- (value) => value === value.trim() && value.toLowerCase() !== "unknown",
13
- );
14
- const nonBlankStringSchema = z
15
- .string()
16
- .refine((value) => value.trim().length > 0);
17
-
18
- /** Runtime-owned Slack address for routing future work or side effects. */
19
- export const slackDestinationSchema = z
20
- .object({
21
- platform: z.literal("slack"),
22
- teamId: slackTeamIdSchema,
23
- channelId: slackConversationIdSchema,
24
- })
25
- .strict();
26
-
27
- /** Runtime-owned local CLI conversation address. */
28
- export const localDestinationSchema = z
29
- .object({
30
- platform: z.literal("local"),
31
- conversationId: localConversationIdSchema,
32
- })
33
- .strict();
34
-
35
- /** Runtime-owned provider-neutral address for routing future work or side effects. */
36
- export const destinationSchema = z.discriminatedUnion("platform", [
37
- slackDestinationSchema,
38
- localDestinationSchema,
39
- ]);
40
-
41
- /** Runtime-owned Slack coordinates for the inbound invocation. */
42
- export const slackSourceSchema = z
43
- .object({
44
- platform: z.literal("slack"),
45
- teamId: slackTeamIdSchema,
46
- channelId: slackConversationIdSchema,
47
- messageTs: nonBlankStringSchema.optional(),
48
- threadTs: nonBlankStringSchema.optional(),
49
- })
50
- .strict();
51
-
52
- /** Runtime-owned local CLI coordinates for the inbound invocation. */
53
- export const localSourceSchema = localDestinationSchema;
54
-
55
- /** Runtime-owned provider-neutral coordinates for the inbound invocation. */
56
- export const sourceSchema = z.discriminatedUnion("platform", [
57
- slackSourceSchema,
58
- localSourceSchema,
59
- ]);
60
-
61
- /** Stable user credential subject shape accepted from plugins. */
62
- export const agentPluginCredentialSubjectSchema = z
63
- .object({
64
- type: z.literal("user"),
65
- userId: exactActorUserIdSchema,
66
- allowedWhen: z.literal("private-direct-conversation"),
67
- })
68
- .strict();
69
-
70
- /** Shared exact actor profile fields for platform-scoped requesters. */
71
- const requesterProfileSchema = {
72
- email: nonBlankStringSchema.optional(),
73
- fullName: nonBlankStringSchema.optional(),
74
- userId: exactActorUserIdSchema,
75
- userName: nonBlankStringSchema.optional(),
76
- };
77
-
78
- export const slackRequesterSchema = z
79
- .object({
80
- ...requesterProfileSchema,
81
- platform: z.literal("slack"),
82
- teamId: slackTeamIdSchema,
83
- })
84
- .strict();
85
-
86
- export const localRequesterSchema = z
87
- .object({
88
- ...requesterProfileSchema,
89
- platform: z.literal("local"),
90
- })
91
- .strict();
92
-
93
- /** Runtime-provided requester identity visible to plugin hooks. */
94
- export const requesterSchema = z.discriminatedUnion("platform", [
95
- slackRequesterSchema,
96
- localRequesterSchema,
97
- ]);
98
-
99
- const dispatchMetadataSchema = z
100
- .record(z.string(), z.string())
101
- .superRefine((metadata, ctx) => {
102
- const entries = Object.entries(metadata);
103
- if (entries.length > 20) {
104
- ctx.addIssue({
105
- code: z.ZodIssueCode.custom,
106
- message: "Dispatch metadata has too many keys",
107
- });
108
- return;
109
- }
110
- for (const [key, value] of entries) {
111
- if (!key.trim()) {
112
- ctx.addIssue({
113
- code: z.ZodIssueCode.custom,
114
- message: "Dispatch metadata values must be strings",
115
- path: [key],
116
- });
117
- continue;
118
- }
119
- if (key.length > 128) {
120
- ctx.addIssue({
121
- code: z.ZodIssueCode.custom,
122
- message: "Dispatch metadata key exceeds the maximum length",
123
- path: [key],
124
- });
125
- }
126
- if (value.length > 512) {
127
- ctx.addIssue({
128
- code: z.ZodIssueCode.custom,
129
- message: "Dispatch metadata value exceeds the maximum length",
130
- path: [key],
131
- });
132
- }
133
- }
134
- });
135
-
136
- /** Plugin dispatch request accepted by Junior core. */
137
- export const dispatchOptionsSchema = z
138
- .object({
139
- idempotencyKey: nonBlankStringSchema.pipe(z.string().max(512)),
140
- credentialSubject: agentPluginCredentialSubjectSchema.optional(),
141
- destination: slackDestinationSchema,
142
- input: nonBlankStringSchema.pipe(z.string().max(32_000)),
143
- metadata: dispatchMetadataSchema.optional(),
144
- })
145
- .strict();
146
-
147
- export type Requester = z.output<typeof requesterSchema>;
148
- export type SlackRequester = z.output<typeof slackRequesterSchema>;
149
- export type LocalRequester = z.output<typeof localRequesterSchema>;
150
- export type Source = z.output<typeof sourceSchema>;
151
- export type SlackSource = Extract<Source, { platform: "slack" }>;
152
- export type LocalSource = Extract<Source, { platform: "local" }>;
153
-
154
- export interface AgentPluginMetadata {
155
- name: string;
156
- }
157
-
158
- export interface AgentPluginEnv {
159
- get(key: string): string | undefined;
160
- set(key: string, value: string): void;
161
- }
162
-
163
- export interface AgentPluginDecision {
164
- deny(message: string): void;
165
- replaceInput(input: Record<string, unknown>): void;
166
- }
167
-
168
- export interface AgentPluginLogger {
169
- error(message: string, metadata?: Record<string, unknown>): void;
170
- info(message: string, metadata?: Record<string, unknown>): void;
171
- warn(message: string, metadata?: Record<string, unknown>): void;
172
- }
173
-
174
- /** Thrown when a plugin tool rejects invalid model or user input. */
175
- export class AgentPluginToolInputError extends Error {
176
- constructor(message: string, options?: { cause?: unknown }) {
177
- super(message, options);
178
- this.name = "AgentPluginToolInputError";
179
- }
180
- }
181
-
182
- export interface AgentPluginContext {
183
- log: AgentPluginLogger;
184
- plugin: AgentPluginMetadata;
185
- }
186
-
187
- interface BaseInvocationContext {
188
- /**
189
- * Opaque Junior conversation/session identity for this invocation.
190
- * Interactive Slack turns use `slack:{channelId}:{threadTs}`.
191
- */
192
- conversationId?: string;
193
- }
194
-
195
- export interface SlackInvocationContext extends BaseInvocationContext {
196
- /** Runtime-owned default outbound destination for this invocation, if any. */
197
- destination?: SlackDestination;
198
- requester?: SlackRequester;
199
- /** Runtime-owned source where the invocation came from. */
200
- source: SlackSource;
201
- }
202
-
203
- export interface LocalInvocationContext extends BaseInvocationContext {
204
- /** Runtime-owned default outbound destination for this invocation, if any. */
205
- destination?: LocalDestination;
206
- requester?: LocalRequester;
207
- /** Runtime-owned source where the invocation came from. */
208
- source: LocalSource;
209
- }
210
-
211
- export type InvocationContext = LocalInvocationContext | SlackInvocationContext;
212
-
213
- export interface AgentPluginSandbox {
214
- juniorRoot: string;
215
- root: string;
216
- readFile(path: string): Promise<Uint8Array | null>;
217
- run(input: {
218
- args?: string[];
219
- cmd: string;
220
- cwd?: string;
221
- env?: Record<string, string>;
222
- sudo?: boolean;
223
- }): Promise<{
224
- exitCode: number;
225
- stderr: string;
226
- stdout: string;
227
- }>;
228
- writeFile(input: {
229
- content: string | Uint8Array;
230
- mode?: number;
231
- path: string;
232
- }): Promise<void>;
233
- }
234
-
235
- export interface SandboxPrepareHookContext extends AgentPluginContext {
236
- requester?: Requester;
237
- sandbox: AgentPluginSandbox;
238
- }
239
-
240
- export interface BeforeToolExecuteHookContext extends AgentPluginContext {
241
- decision: AgentPluginDecision;
242
- env: AgentPluginEnv;
243
- requester?: Requester;
244
- tool: {
245
- input: Record<string, unknown>;
246
- name: string;
247
- };
248
- }
249
-
250
- export type AgentPluginToolExecute<TInput = unknown> = {
251
- bivarianceHack(
252
- input: TInput,
253
- options: { experimental_context?: unknown },
254
- ): Promise<unknown> | unknown;
255
- }["bivarianceHack"];
256
-
257
- export interface AgentPluginToolDefinition<TInput = unknown> {
258
- annotations?: unknown;
259
- description: string;
260
- executionMode?: unknown;
261
- inputSchema: unknown;
262
- prepareArguments?: (args: unknown) => unknown;
263
- /**
264
- * @deprecated Put tool-selection and usage guidance directly in `description`
265
- * and parameter descriptions. Retained for compatibility; may be removed in a
266
- * future major version.
267
- */
268
- promptGuidelines?: string[];
269
- /**
270
- * @deprecated Put tool-selection and usage guidance directly in `description`
271
- * and parameter descriptions. Retained for compatibility; may be removed in a
272
- * future major version.
273
- */
274
- promptSnippet?: string;
275
- execute?: AgentPluginToolExecute<TInput>;
276
- }
277
-
278
- export interface SlackToolRegistrationHookContext {
279
- /**
280
- * Capabilities of the source Slack conversation exposed to this plugin.
281
- * Recomputed from `source.channelId`, not from `destination`.
282
- */
283
- channelCapabilities: {
284
- canAddReactions: boolean;
285
- canCreateCanvas: boolean;
286
- canPostToChannel: boolean;
287
- };
288
- credentialSubject?: AgentPluginCredentialSubject;
289
- }
290
-
291
- interface BaseToolRegistrationHookContext extends AgentPluginContext {
292
- /**
293
- * Opaque Junior conversation/session identity for this turn.
294
- * Interactive Slack turns use `slack:{channelId}:{threadTs}`.
295
- * Scheduled/API turns use an internal id such as `agent-dispatch:{id}`.
296
- * Do not parse as Slack unless the value starts with `slack:`.
297
- */
298
- conversationId?: string;
299
- state: AgentPluginState;
300
- userText?: string;
301
- }
302
-
303
- interface SlackToolRegistrationContext
304
- extends BaseToolRegistrationHookContext, SlackInvocationContext {
305
- slack: SlackToolRegistrationHookContext;
306
- }
307
-
308
- interface LocalToolRegistrationContext
309
- extends BaseToolRegistrationHookContext, LocalInvocationContext {
310
- slack?: never;
311
- }
312
-
313
- export type ToolRegistrationHookContext =
314
- | LocalToolRegistrationContext
315
- | SlackToolRegistrationContext;
316
-
317
- export type AgentPluginCredentialSubject = z.output<
318
- typeof agentPluginCredentialSubjectSchema
319
- >;
320
-
321
- export type Destination = z.output<typeof destinationSchema>;
322
-
323
- export type SlackDestination = Extract<Destination, { platform: "slack" }>;
324
-
325
- export type LocalDestination = Extract<Destination, { platform: "local" }>;
326
-
327
- /** Narrow a runtime destination to the Slack-specific address shape. */
328
- export function isSlackDestination(
329
- destination: Destination | undefined,
330
- ): destination is SlackDestination {
331
- return destination?.platform === "slack";
332
- }
333
-
334
- export type DispatchOptions = z.output<typeof dispatchOptionsSchema>;
335
-
336
- export interface DispatchResult {
337
- id: string;
338
- status: "created" | "already_exists";
339
- }
340
-
341
- export interface Dispatch {
342
- errorMessage?: string;
343
- id: string;
344
- resultMessageTs?: string;
345
- status:
346
- | "pending"
347
- | "running"
348
- | "awaiting_resume"
349
- | "completed"
350
- | "failed"
351
- | "blocked";
352
- }
353
-
354
- export interface AgentPluginState {
355
- delete(key: string): Promise<void>;
356
- get<T = unknown>(key: string): Promise<T | undefined>;
357
- set(key: string, value: unknown, ttlMs?: number): Promise<void>;
358
- setIfNotExists(key: string, value: unknown, ttlMs?: number): Promise<boolean>;
359
- withLock<T>(
360
- key: string,
361
- ttlMs: number,
362
- callback: () => Promise<T>,
363
- ): Promise<T>;
364
- }
365
-
366
- export interface AgentPluginReadState {
367
- get<T = unknown>(key: string): Promise<T | undefined>;
368
- }
369
-
370
- export type AgentPluginConversationStatus =
371
- | "active"
372
- | "completed"
373
- | "failed"
374
- | "hung"
375
- | "superseded";
376
-
377
- export interface AgentPluginConversationSummary {
378
- channelName?: string;
379
- conversationId: string;
380
- displayTitle: string;
381
- lastActivityAt: string;
382
- lastUpdatedAt: string;
383
- source?: "api" | "internal" | "local" | "plugin" | "scheduler" | "slack";
384
- status: AgentPluginConversationStatus;
385
- }
386
-
387
- export interface AgentPluginConversations {
388
- listRecent(options?: {
389
- limit?: number;
390
- }): Promise<AgentPluginConversationSummary[]>;
391
- }
392
-
393
- export interface HeartbeatHookContext extends AgentPluginContext {
394
- agent: {
395
- dispatch(options: DispatchOptions): Promise<DispatchResult>;
396
- get(id: string): Promise<Dispatch | undefined>;
397
- };
398
- nowMs: number;
399
- state: AgentPluginState;
400
- }
401
-
402
- export interface HeartbeatResult {
403
- dispatchCount?: number;
404
- }
405
-
406
- export type PluginOperationalTone = "danger" | "good" | "neutral" | "warning";
407
-
408
- export interface PluginOperationalMetric {
409
- label: string;
410
- tone?: PluginOperationalTone;
411
- value: string;
412
- }
413
-
414
- export interface PluginOperationalField {
415
- key: string;
416
- label: string;
417
- }
418
-
419
- export interface PluginOperationalRecord {
420
- id: string;
421
- tone?: PluginOperationalTone;
422
- values: Record<string, string>;
423
- }
424
-
425
- export interface PluginOperationalRecordSet {
426
- fields?: PluginOperationalField[];
427
- emptyText?: string;
428
- records?: PluginOperationalRecord[];
429
- title: string;
430
- }
431
-
432
- export interface PluginOperationalReportContent {
433
- generatedAt?: string;
434
- metrics?: PluginOperationalMetric[];
435
- recordSets?: PluginOperationalRecordSet[];
436
- title?: string;
437
- }
438
-
439
- export interface PluginOperationalReport extends PluginOperationalReportContent {
440
- pluginName: string;
441
- }
442
-
443
- export interface OperationalReportHookContext extends AgentPluginContext {
444
- conversations: AgentPluginConversations;
445
- nowMs: number;
446
- state: AgentPluginReadState;
447
- }
448
-
449
- export type AgentPluginRouteMethod =
450
- | "GET"
451
- | "POST"
452
- | "PUT"
453
- | "PATCH"
454
- | "DELETE"
455
- | "HEAD"
456
- | "OPTIONS"
457
- | "ALL";
458
-
459
- export type AgentPluginRouteHandler = {
460
- bivarianceHack(request: Request): Promise<Response> | Response;
461
- }["bivarianceHack"];
462
-
463
- export interface AgentPluginRoute {
464
- handler: AgentPluginRouteHandler;
465
- method?: AgentPluginRouteMethod | AgentPluginRouteMethod[];
466
- path: string;
467
- }
468
-
469
- export interface RouteRegistrationHookContext extends AgentPluginContext {}
470
-
471
- export interface SlackConversationLink {
472
- url: string;
473
- }
474
-
475
- export interface SlackConversationLinkHookContext extends AgentPluginContext {
476
- conversationId: string;
477
- }
478
-
479
- const agentPluginProviderNameSchema = z.string().regex(/^[a-z][a-z0-9-]*$/);
480
- const agentPluginGrantNameSchema = z.string().regex(/^[a-z][a-z0-9.-]*$/);
481
- const agentPluginGrantAccessSchema = z.union([
482
- z.literal("read"),
483
- z.literal("write"),
484
- ]);
485
-
486
- /** Runtime schema for provider authorization a plugin may request. */
487
- export const agentPluginAuthorizationSchema = z
488
- .object({
489
- provider: agentPluginProviderNameSchema,
490
- scope: nonBlankStringSchema.optional(),
491
- type: z.literal("oauth"),
492
- })
493
- .strict();
494
-
495
- /** Runtime schema for a provider account attached to stored OAuth tokens. */
496
- export const agentPluginProviderAccountSchema = z
497
- .object({
498
- id: nonBlankStringSchema,
499
- label: nonBlankStringSchema.optional(),
500
- url: nonBlankStringSchema.optional(),
501
- })
502
- .strict();
503
-
504
- /** Runtime schema for a plugin-defined outbound credential grant. */
505
- export const agentPluginGrantSchema = z
506
- .object({
507
- access: agentPluginGrantAccessSchema,
508
- name: agentPluginGrantNameSchema,
509
- reason: nonBlankStringSchema.optional(),
510
- requirements: z.array(nonBlankStringSchema).min(1).optional(),
511
- })
512
- .strict();
513
-
514
- /** Runtime schema for plugin-issued header mutations. */
515
- export const agentPluginCredentialHeaderTransformSchema = z
516
- .object({
517
- domain: z.string().min(1),
518
- headers: z
519
- .record(z.string(), z.string())
520
- .refine((headers) => Object.keys(headers).length > 0),
521
- })
522
- .strict();
523
-
524
- /** Runtime schema for a short-lived plugin-issued credential lease. */
525
- export const agentPluginCredentialLeaseSchema = z
526
- .object({
527
- account: agentPluginProviderAccountSchema.optional(),
528
- authorization: agentPluginAuthorizationSchema.optional(),
529
- expiresAt: z.string().refine((value) => Number.isFinite(Date.parse(value))),
530
- headerTransforms: z
531
- .array(agentPluginCredentialHeaderTransformSchema)
532
- .min(1),
533
- })
534
- .strict();
535
-
536
- /** Runtime schema for the result returned by a plugin credential hook. */
537
- export const agentPluginCredentialResultSchema = z.discriminatedUnion("type", [
538
- z
539
- .object({
540
- lease: agentPluginCredentialLeaseSchema,
541
- type: z.literal("lease"),
542
- })
543
- .strict(),
544
- z
545
- .object({
546
- authorization: agentPluginAuthorizationSchema.optional(),
547
- message: nonBlankStringSchema,
548
- type: z.literal("needed"),
549
- })
550
- .strict(),
551
- z
552
- .object({
553
- message: nonBlankStringSchema,
554
- type: z.literal("unavailable"),
555
- })
556
- .strict(),
557
- ]);
558
-
559
- export type AgentPluginGrantAccess = z.output<
560
- typeof agentPluginGrantAccessSchema
561
- >;
562
-
563
- /** Provider authorization Junior can start when a plugin-owned grant is missing. */
564
- export type AgentPluginAuthorization = z.output<
565
- typeof agentPluginAuthorizationSchema
566
- >;
567
-
568
- /** Interrupt sandbox egress so Junior can start provider authorization. */
569
- export class EgressAuthRequired extends Error {
570
- authorization?: AgentPluginAuthorization;
571
-
572
- constructor(
573
- message: string,
574
- options?: {
575
- authorization?: AgentPluginAuthorization;
576
- cause?: unknown;
577
- },
578
- ) {
579
- super(message, { cause: options?.cause });
580
- this.name = "EgressAuthRequired";
581
- this.authorization = options?.authorization;
582
- }
583
- }
584
-
585
- /** Provider account identity resolved by a plugin OAuth hook. */
586
- export type AgentPluginProviderAccount = z.output<
587
- typeof agentPluginProviderAccountSchema
588
- >;
589
-
590
- /** Plugin-defined grant required before Junior can forward one outbound request. */
591
- export type AgentPluginGrant = z.output<typeof agentPluginGrantSchema>;
592
-
593
- /** Request details available while selecting the grant for sandbox egress. */
594
- export interface AgentPluginEgressRequest {
595
- /** Capped request body text when the host exposes it for provider-specific grant classification. */
596
- bodyText?: string;
597
- method: string;
598
- url: string;
599
- }
600
-
601
- export interface EgressHookContext extends AgentPluginContext {
602
- request: AgentPluginEgressRequest;
603
- }
604
-
605
- export interface AgentPluginEgressResponse {
606
- /** Snapshot of upstream response headers; mutations do not affect pass-through. */
607
- headers: Headers;
608
- readText(maxBytes: number): Promise<string | undefined>;
609
- status: number;
610
- }
611
-
612
- export interface EgressResponseHookContext extends AgentPluginContext {
613
- grant: AgentPluginGrant;
614
- permissionDenied(message: string): void;
615
- request: Omit<AgentPluginEgressRequest, "bodyText">;
616
- response: AgentPluginEgressResponse;
617
- }
618
-
619
- /** Header mutations a plugin-issued credential lease may apply to owned domains. */
620
- export type AgentPluginCredentialHeaderTransform = z.output<
621
- typeof agentPluginCredentialHeaderTransformSchema
622
- >;
623
-
624
- /** Short-lived credential headers issued by a plugin for a selected grant. */
625
- export type AgentPluginCredentialLease = z.output<
626
- typeof agentPluginCredentialLeaseSchema
627
- >;
628
-
629
- export type AgentPluginCredentialResult = z.output<
630
- typeof agentPluginCredentialResultSchema
631
- >;
632
-
633
- export type AgentPluginCredentialActor =
634
- | {
635
- type: "system";
636
- id: string;
637
- }
638
- | {
639
- type: "user";
640
- userId: string;
641
- };
642
-
643
- export interface AgentPluginResolvedCredentialUser {
644
- type: "user";
645
- userId: string;
646
- }
647
-
648
- export interface AgentPluginStoredTokens {
649
- account?: AgentPluginProviderAccount;
650
- accessToken: string;
651
- expiresAt?: number;
652
- refreshToken: string;
653
- scope?: string;
654
- }
655
-
656
- export interface AgentPluginUserTokenSlot {
657
- get(): Promise<AgentPluginStoredTokens | undefined>;
658
- set(tokens: AgentPluginStoredTokens): Promise<void>;
659
- userId: string;
660
- }
661
-
662
- export interface AgentPluginTokenStore {
663
- credentialSubject?: AgentPluginUserTokenSlot;
664
- currentUser?: AgentPluginUserTokenSlot;
665
- }
666
-
667
- export interface ResolveOAuthAccountHookContext extends AgentPluginContext {
668
- tokens: AgentPluginStoredTokens;
669
- }
670
-
671
- export interface IssueCredentialHookContext extends AgentPluginContext {
672
- actor: AgentPluginCredentialActor;
673
- credentialSubject?: AgentPluginResolvedCredentialUser;
674
- grant: AgentPluginGrant;
675
- tokens: AgentPluginTokenStore;
676
- }
677
-
678
- export interface AgentPluginHooks {
679
- sandboxPrepare?(ctx: SandboxPrepareHookContext): Promise<void> | void;
680
- beforeToolExecute?(ctx: BeforeToolExecuteHookContext): Promise<void> | void;
681
- grantForEgress?(
682
- ctx: EgressHookContext,
683
- ): Promise<AgentPluginGrant | undefined> | AgentPluginGrant | undefined;
684
- issueCredential?(
685
- ctx: IssueCredentialHookContext,
686
- ): Promise<AgentPluginCredentialResult> | AgentPluginCredentialResult;
687
- onEgressResponse?(ctx: EgressResponseHookContext): Promise<void> | void;
688
- resolveOAuthAccount?(
689
- ctx: ResolveOAuthAccountHookContext,
690
- ):
691
- | Promise<AgentPluginProviderAccount | undefined>
692
- | AgentPluginProviderAccount
693
- | undefined;
694
- routes?(ctx: RouteRegistrationHookContext): AgentPluginRoute[];
695
- tools?(
696
- ctx: ToolRegistrationHookContext,
697
- ): Record<string, AgentPluginToolDefinition>;
698
- heartbeat?(
699
- ctx: HeartbeatHookContext,
700
- ): Promise<HeartbeatResult | void> | HeartbeatResult | void;
701
- operationalReport?(
702
- ctx: OperationalReportHookContext,
703
- ):
704
- | Promise<PluginOperationalReportContent | undefined>
705
- | PluginOperationalReportContent
706
- | undefined;
707
- slackConversationLink?(
708
- ctx: SlackConversationLinkHookContext,
709
- ): SlackConversationLink | undefined;
710
- }
711
-
712
- export interface JuniorPluginOAuthConfig {
713
- authorizeEndpoint: string;
714
- authorizeParams?: Record<string, string>;
715
- clientIdEnv: string;
716
- clientSecretEnv: string;
717
- scope?: string;
718
- /**
719
- * Treat a provider token response with `scope: ""` like an omitted scope and
720
- * fall back to the requested scope string when storing the token.
721
- *
722
- * Enable this only for providers whose token responses cannot report OAuth
723
- * scopes even though Junior needs a local requested-scope string for
724
- * reauthorization checks. The built-in GitHub App plugin enables this because
725
- * GitHub App user-to-server tokens always return an empty scope value — their
726
- * effective access is enforced by GitHub App permissions, installation
727
- * repository access, and the requesting user's own access, not OAuth scopes.
728
- *
729
- * Do not enable this for standard OAuth providers where an explicit empty
730
- * `scope` means the provider granted no scopes.
731
- */
732
- treatEmptyScopeAsUnreported?: boolean;
733
- tokenAuthMethod?: "body" | "basic";
734
- tokenEndpoint: string;
735
- tokenExtraHeaders?: Record<string, string>;
736
- }
737
-
738
- export interface JuniorPluginOAuthBearerCredentials {
739
- apiHeaders?: Record<string, string>;
740
- authTokenEnv: string;
741
- authTokenPlaceholder?: string;
742
- domains: string[];
743
- type: "oauth-bearer";
744
- }
745
-
746
- export type JuniorPluginCredentials = JuniorPluginOAuthBearerCredentials;
747
-
748
- export interface JuniorPluginNpmRuntimeDependency {
749
- package: string;
750
- type: "npm";
751
- version: string;
752
- }
753
-
754
- export interface JuniorPluginSystemRuntimeDependency {
755
- package: string;
756
- type: "system";
757
- }
758
-
759
- export interface JuniorPluginSystemRuntimeDependencyFromUrl {
760
- sha256: string;
761
- type: "system";
762
- url: string;
763
- }
764
-
765
- export type JuniorPluginRuntimeDependency =
766
- | JuniorPluginNpmRuntimeDependency
767
- | JuniorPluginSystemRuntimeDependency
768
- | JuniorPluginSystemRuntimeDependencyFromUrl;
769
-
770
- export interface JuniorPluginRuntimePostinstallCommand {
771
- args?: string[];
772
- cmd: string;
773
- sudo?: boolean;
774
- }
775
-
776
- export interface JuniorPluginMcpConfig {
777
- allowedTools?: string[];
778
- headers?: Record<string, string>;
779
- transport: "http";
780
- url: string;
781
- }
782
-
783
- export interface JuniorPluginEnvVarDeclaration {
784
- default?: string;
785
- exposeToCommandEnv?: boolean;
786
- }
787
-
788
- export interface JuniorPluginManifest {
789
- apiHeaders?: Record<string, string>;
790
- capabilities?: string[];
791
- commandEnv?: Record<string, string>;
792
- configKeys?: string[];
793
- credentials?: JuniorPluginCredentials;
794
- description: string;
795
- displayName: string;
796
- domains?: string[];
797
- envVars?: Record<string, JuniorPluginEnvVarDeclaration>;
798
- mcp?: JuniorPluginMcpConfig;
799
- name: string;
800
- oauth?: JuniorPluginOAuthConfig;
801
- runtimeDependencies?: JuniorPluginRuntimeDependency[];
802
- runtimePostinstall?: JuniorPluginRuntimePostinstallCommand[];
803
- target?: {
804
- commandFlags?: string[];
805
- configKey: string;
806
- type: string;
807
- };
808
- }
809
-
810
- export type JuniorPluginRegistrationInput = {
811
- hooks?: AgentPluginHooks;
812
- legacyStatePrefixes?: string[];
813
- manifest: JuniorPluginManifest;
814
- name?: string;
815
- packageName?: string;
816
- };
817
-
818
- export interface JuniorPluginRegistration extends JuniorPluginRegistrationInput {
819
- name: string;
820
- }
821
-
822
- const PLUGIN_NAME_RE = /^[a-z][a-z0-9-]*$/;
823
-
824
- /** Define one Junior plugin registration for app and build-time wiring. */
825
- export function defineJuniorPlugin(
826
- plugin: JuniorPluginRegistrationInput,
827
- ): JuniorPluginRegistration {
828
- if ("pluginConfig" in plugin) {
829
- throw new Error(
830
- "pluginConfig is no longer supported. Put runtime metadata in manifest and state prefixes on the plugin registration.",
831
- );
832
- }
833
- const manifest = plugin.manifest;
834
- if (!manifest) {
835
- throw new Error(
836
- "defineJuniorPlugin() requires a manifest. Use a package name string in defineJuniorPlugins([...]) for plugin.yaml packages.",
837
- );
838
- }
839
- const name = plugin.name ?? manifest.name;
840
- if (!name) {
841
- throw new Error(
842
- "Junior plugin registrations must include name or manifest.name.",
843
- );
844
- }
845
- if (!PLUGIN_NAME_RE.test(name)) {
846
- throw new Error(
847
- `Junior plugin registration name "${name}" must be a lowercase plugin identifier.`,
848
- );
849
- }
850
- if (
851
- typeof manifest.displayName !== "string" ||
852
- !manifest.displayName.trim()
853
- ) {
854
- throw new Error(
855
- `Junior plugin "${name}" manifest.displayName is required.`,
856
- );
857
- }
858
- if (
859
- typeof manifest.description !== "string" ||
860
- !manifest.description.trim()
861
- ) {
862
- throw new Error(
863
- `Junior plugin "${name}" manifest.description is required.`,
864
- );
865
- }
866
- if (plugin.name && manifest.name && plugin.name !== manifest.name) {
867
- throw new Error(
868
- `Junior plugin registration name "${plugin.name}" must match manifest.name "${manifest.name}".`,
869
- );
870
- }
871
- return {
872
- ...plugin,
873
- name,
874
- };
875
- }
1
+ export * from "./schemas";
2
+ export * from "./context";
3
+ export * from "./state";
4
+ export * from "./dispatch";
5
+ export * from "./database";
6
+ export * from "./tools";
7
+ export * from "./operations";
8
+ export * from "./credentials";
9
+ export * from "./hooks";
10
+ export * from "./manifest";
11
+ export * from "./registration";