@sentry/junior-plugin-api 0.114.0 → 0.116.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.
@@ -0,0 +1,44 @@
1
+ import { z } from "zod";
2
+ export declare const resourceLinkAnnotationSchema: z.ZodObject<{
3
+ kind: z.ZodLiteral<"resource_link">;
4
+ key: z.ZodString;
5
+ label: z.ZodString;
6
+ url: z.ZodString;
7
+ description: z.ZodOptional<z.ZodString>;
8
+ status: z.ZodOptional<z.ZodEnum<{
9
+ open: "open";
10
+ draft: "draft";
11
+ closed: "closed";
12
+ merged: "merged";
13
+ warning: "warning";
14
+ }>>;
15
+ }, z.core.$strict>;
16
+ /** Core-known annotation shapes that plugins may attach to a conversation. */
17
+ export declare const conversationAnnotationInputSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
18
+ kind: z.ZodLiteral<"resource_link">;
19
+ key: z.ZodString;
20
+ label: z.ZodString;
21
+ url: z.ZodString;
22
+ description: z.ZodOptional<z.ZodString>;
23
+ status: z.ZodOptional<z.ZodEnum<{
24
+ open: "open";
25
+ draft: "draft";
26
+ closed: "closed";
27
+ merged: "merged";
28
+ warning: "warning";
29
+ }>>;
30
+ }, z.core.$strict>], "kind">;
31
+ export type ConversationAnnotationInput = z.output<typeof conversationAnnotationInputSchema>;
32
+ export type ConversationAnnotation = ConversationAnnotationInput & {
33
+ plugin: string;
34
+ createdAt: string;
35
+ updatedAt: string;
36
+ };
37
+ export interface PluginAnnotations {
38
+ upsert(annotation: ConversationAnnotationInput): Promise<void>;
39
+ remove(kind: ConversationAnnotationInput["kind"], key: string): Promise<void>;
40
+ list(): Promise<ConversationAnnotation[]>;
41
+ }
42
+ export interface PluginConversationAnnotations {
43
+ forConversation(conversationId: string): PluginAnnotations;
44
+ }
package/dist/index.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./annotations";
1
2
  export * from "./schemas";
2
3
  export * from "./context";
3
4
  export * from "./state";
package/dist/index.js CHANGED
@@ -1,29 +1,51 @@
1
- // src/schemas.ts
1
+ // src/annotations.ts
2
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 localConversationIdSchema = z.string().regex(/^local:[a-z0-9_-]+:[a-z0-9][a-z0-9_-]*$/);
6
- var exactActorUserIdSchema = z.string().min(1).refine(
3
+ function usesHttpProtocol(value) {
4
+ try {
5
+ const protocol = new URL(value).protocol;
6
+ return protocol === "http:" || protocol === "https:";
7
+ } catch {
8
+ return false;
9
+ }
10
+ }
11
+ var resourceLinkAnnotationSchema = z.object({
12
+ kind: z.literal("resource_link"),
13
+ key: z.string().trim().min(1).max(256),
14
+ label: z.string().trim().min(1).max(256),
15
+ url: z.string().url().max(2048).refine(usesHttpProtocol, "URL must use HTTP or HTTPS."),
16
+ description: z.string().trim().min(1).max(512).optional(),
17
+ status: z.enum(["open", "draft", "closed", "merged", "warning"]).optional()
18
+ }).strict();
19
+ var conversationAnnotationInputSchema = z.discriminatedUnion("kind", [
20
+ resourceLinkAnnotationSchema
21
+ ]);
22
+
23
+ // src/schemas.ts
24
+ import { z as z2 } from "zod";
25
+ var slackTeamIdSchema = z2.string().regex(/^T[A-Z0-9]+$/);
26
+ var slackConversationIdSchema = z2.string().regex(/^(C|G|D)[A-Z0-9]+$/);
27
+ var localConversationIdSchema = z2.string().regex(/^local:[a-z0-9_-]+:[a-z0-9][a-z0-9_-]*$/);
28
+ var exactActorUserIdSchema = z2.string().min(1).refine(
7
29
  (value) => value === value.trim() && value.toLowerCase() !== "unknown"
8
30
  );
9
- var nonBlankStringSchema = z.string().refine((value) => value.trim().length > 0);
31
+ var nonBlankStringSchema = z2.string().refine((value) => value.trim().length > 0);
10
32
  var exactNonBlankStringSchema = nonBlankStringSchema.refine(
11
33
  (value) => value === value.trim()
12
34
  );
13
- var platformSchema = z.enum(["slack", "local"]);
14
- var sourceTypeSchema = z.enum(["pub", "priv"]);
15
- var destinationVisibilitySchema = z.enum(["public", "private"]);
16
- var slackAddressSchema = z.object({
17
- platform: z.literal("slack"),
35
+ var platformSchema = z2.enum(["slack", "local"]);
36
+ var sourceTypeSchema = z2.enum(["pub", "priv"]);
37
+ var destinationVisibilitySchema = z2.enum(["public", "private"]);
38
+ var slackAddressSchema = z2.object({
39
+ platform: z2.literal("slack"),
18
40
  teamId: slackTeamIdSchema,
19
41
  channelId: slackConversationIdSchema
20
42
  }).strict();
21
43
  var slackDestinationSchema = slackAddressSchema;
22
- var localDestinationSchema = z.object({
23
- platform: z.literal("local"),
44
+ var localDestinationSchema = z2.object({
45
+ platform: z2.literal("local"),
24
46
  conversationId: localConversationIdSchema
25
47
  }).strict();
26
- var destinationSchema = z.discriminatedUnion("platform", [
48
+ var destinationSchema = z2.discriminatedUnion("platform", [
27
49
  slackDestinationSchema,
28
50
  localDestinationSchema
29
51
  ]);
@@ -32,27 +54,27 @@ var slackSourceSchema = slackAddressSchema.extend({
32
54
  messageTs: nonBlankStringSchema.optional(),
33
55
  threadTs: nonBlankStringSchema.optional()
34
56
  }).strict();
35
- var localSourceSchema = z.object({
36
- platform: z.literal("local"),
37
- type: z.literal("priv"),
57
+ var localSourceSchema = z2.object({
58
+ platform: z2.literal("local"),
59
+ type: z2.literal("priv"),
38
60
  conversationId: localConversationIdSchema
39
61
  }).strict();
40
- var sourceSchema = z.discriminatedUnion("platform", [
62
+ var sourceSchema = z2.discriminatedUnion("platform", [
41
63
  slackSourceSchema,
42
64
  localSourceSchema
43
65
  ]);
44
- var pluginCredentialSubjectSchema = z.discriminatedUnion(
66
+ var pluginCredentialSubjectSchema = z2.discriminatedUnion(
45
67
  "allowedWhen",
46
68
  [
47
- z.object({
48
- type: z.literal("user"),
69
+ z2.object({
70
+ type: z2.literal("user"),
49
71
  userId: exactActorUserIdSchema,
50
- allowedWhen: z.literal("private-direct-conversation")
72
+ allowedWhen: z2.literal("private-direct-conversation")
51
73
  }).strict(),
52
- z.object({
53
- type: z.literal("user"),
74
+ z2.object({
75
+ type: z2.literal("user"),
54
76
  userId: exactActorUserIdSchema,
55
- allowedWhen: z.literal("scheduled-task"),
77
+ allowedWhen: z2.literal("scheduled-task"),
56
78
  taskId: exactNonBlankStringSchema
57
79
  }).strict()
58
80
  ]
@@ -63,29 +85,29 @@ var actorProfileSchema = {
63
85
  userId: exactActorUserIdSchema,
64
86
  userName: nonBlankStringSchema.optional()
65
87
  };
66
- var slackActorSchema = z.object({
88
+ var slackActorSchema = z2.object({
67
89
  ...actorProfileSchema,
68
- platform: z.literal("slack"),
90
+ platform: z2.literal("slack"),
69
91
  teamId: slackTeamIdSchema
70
92
  }).strict();
71
- var localActorSchema = z.object({
93
+ var localActorSchema = z2.object({
72
94
  ...actorProfileSchema,
73
- platform: z.literal("local")
95
+ platform: z2.literal("local")
74
96
  }).strict();
75
- var systemActorSchema = z.object({
76
- platform: z.literal("system"),
97
+ var systemActorSchema = z2.object({
98
+ platform: z2.literal("system"),
77
99
  name: exactActorUserIdSchema
78
100
  }).strict();
79
- var actorSchema = z.discriminatedUnion("platform", [
101
+ var actorSchema = z2.discriminatedUnion("platform", [
80
102
  slackActorSchema,
81
103
  localActorSchema,
82
104
  systemActorSchema
83
105
  ]);
84
- var dispatchMetadataSchema = z.record(z.string(), z.string()).superRefine((metadata, ctx) => {
106
+ var dispatchMetadataSchema = z2.record(z2.string(), z2.string()).superRefine((metadata, ctx) => {
85
107
  const entries = Object.entries(metadata);
86
108
  if (entries.length > 20) {
87
109
  ctx.addIssue({
88
- code: z.ZodIssueCode.custom,
110
+ code: z2.ZodIssueCode.custom,
89
111
  message: "Dispatch metadata has too many keys"
90
112
  });
91
113
  return;
@@ -93,7 +115,7 @@ var dispatchMetadataSchema = z.record(z.string(), z.string()).superRefine((metad
93
115
  for (const [key, value] of entries) {
94
116
  if (!key.trim()) {
95
117
  ctx.addIssue({
96
- code: z.ZodIssueCode.custom,
118
+ code: z2.ZodIssueCode.custom,
97
119
  message: "Dispatch metadata values must be strings",
98
120
  path: [key]
99
121
  });
@@ -101,40 +123,40 @@ var dispatchMetadataSchema = z.record(z.string(), z.string()).superRefine((metad
101
123
  }
102
124
  if (key.length > 128) {
103
125
  ctx.addIssue({
104
- code: z.ZodIssueCode.custom,
126
+ code: z2.ZodIssueCode.custom,
105
127
  message: "Dispatch metadata key exceeds the maximum length",
106
128
  path: [key]
107
129
  });
108
130
  }
109
131
  if (/[\r\n]/.test(key)) {
110
132
  ctx.addIssue({
111
- code: z.ZodIssueCode.custom,
133
+ code: z2.ZodIssueCode.custom,
112
134
  message: "Dispatch metadata keys must be single-line strings",
113
135
  path: [key]
114
136
  });
115
137
  }
116
138
  if (/[\r\n]/.test(value)) {
117
139
  ctx.addIssue({
118
- code: z.ZodIssueCode.custom,
140
+ code: z2.ZodIssueCode.custom,
119
141
  message: "Dispatch metadata values must be single-line strings",
120
142
  path: [key]
121
143
  });
122
144
  }
123
145
  if (value.length > 512) {
124
146
  ctx.addIssue({
125
- code: z.ZodIssueCode.custom,
147
+ code: z2.ZodIssueCode.custom,
126
148
  message: "Dispatch metadata value exceeds the maximum length",
127
149
  path: [key]
128
150
  });
129
151
  }
130
152
  }
131
153
  });
132
- var dispatchOptionsSchema = z.object({
133
- idempotencyKey: nonBlankStringSchema.pipe(z.string().max(512)),
154
+ var dispatchOptionsSchema = z2.object({
155
+ idempotencyKey: nonBlankStringSchema.pipe(z2.string().max(512)),
134
156
  credentialSubject: pluginCredentialSubjectSchema.optional(),
135
157
  destination: slackDestinationSchema,
136
158
  destinationVisibility: destinationVisibilitySchema,
137
- input: nonBlankStringSchema.pipe(z.string().max(32e3)),
159
+ input: nonBlankStringSchema.pipe(z2.string().max(32e3)),
138
160
  metadata: dispatchMetadataSchema.optional(),
139
161
  source: sourceSchema
140
162
  }).strict();
@@ -175,56 +197,56 @@ function isSlackDestination(destination) {
175
197
  }
176
198
 
177
199
  // src/prompt.ts
178
- import { z as z2 } from "zod";
179
- var promptMessageSchema = z2.object({
180
- text: z2.string().trim().min(1).max(8e3)
200
+ import { z as z3 } from "zod";
201
+ var promptMessageSchema = z3.object({
202
+ text: z3.string().trim().min(1).max(8e3)
181
203
  }).strict();
182
204
 
183
205
  // src/resource-events.ts
184
- import { z as z3 } from "zod";
185
- var subscribableResourceSchema = z3.object({
186
- label: z3.string().min(1),
187
- provider: z3.string().min(1),
188
- resourceRef: z3.string().min(1),
189
- suggestedEvents: z3.array(z3.string().min(1)).optional(),
190
- supportedEvents: z3.array(z3.string().min(1)),
191
- type: z3.string().min(1)
206
+ import { z as z4 } from "zod";
207
+ var subscribableResourceSchema = z4.object({
208
+ label: z4.string().min(1),
209
+ provider: z4.string().min(1),
210
+ resourceRef: z4.string().min(1),
211
+ suggestedEvents: z4.array(z4.string().min(1)).optional(),
212
+ supportedEvents: z4.array(z4.string().min(1)),
213
+ type: z4.string().min(1)
192
214
  }).strict();
193
- var resourceEventSchema = z3.object({
194
- eventKey: z3.string().min(1),
195
- eventType: z3.string().min(1),
196
- occurredAtMs: z3.number().finite(),
197
- provider: z3.string().min(1),
198
- resourceRef: z3.string().min(1),
199
- terminal: z3.boolean().optional(),
200
- trustedSummary: z3.string().min(1),
201
- untrustedText: z3.string().optional()
215
+ var resourceEventSchema = z4.object({
216
+ eventKey: z4.string().min(1),
217
+ eventType: z4.string().min(1),
218
+ occurredAtMs: z4.number().finite(),
219
+ provider: z4.string().min(1),
220
+ resourceRef: z4.string().min(1),
221
+ terminal: z4.boolean().optional(),
222
+ trustedSummary: z4.string().min(1),
223
+ untrustedText: z4.string().optional()
202
224
  }).strict();
203
225
 
204
226
  // src/tasks.ts
205
- import { z as z4 } from "zod";
206
- var pluginRunTranscriptProvenanceSchema = z4.object({
207
- authority: z4.enum(["instruction", "context"]),
227
+ import { z as z5 } from "zod";
228
+ var pluginRunTranscriptProvenanceSchema = z5.object({
229
+ authority: z5.enum(["instruction", "context"]),
208
230
  actor: actorSchema.optional()
209
231
  }).strict();
210
- var pluginRunTranscriptEntrySchema = z4.discriminatedUnion("type", [
211
- z4.object({
212
- type: z4.literal("message"),
213
- role: z4.enum(["user", "assistant"]),
214
- text: z4.string().min(1),
232
+ var pluginRunTranscriptEntrySchema = z5.discriminatedUnion("type", [
233
+ z5.object({
234
+ type: z5.literal("message"),
235
+ role: z5.enum(["user", "assistant"]),
236
+ text: z5.string().min(1),
215
237
  provenance: pluginRunTranscriptProvenanceSchema.optional(),
216
- isRunActor: z4.boolean().optional()
238
+ isRunActor: z5.boolean().optional()
217
239
  }).strict(),
218
- z4.object({
219
- type: z4.literal("toolResult"),
220
- toolName: z4.string().min(1),
221
- isError: z4.boolean(),
222
- text: z4.string().min(1).optional()
240
+ z5.object({
241
+ type: z5.literal("toolResult"),
242
+ toolName: z5.string().min(1),
243
+ isError: z5.boolean(),
244
+ text: z5.string().min(1).optional()
223
245
  }).strict()
224
246
  ]);
225
- var pluginRunContextSchema = z4.object({
226
- completedAtMs: z4.number().finite(),
227
- conversationId: z4.string().min(1),
247
+ var pluginRunContextSchema = z5.object({
248
+ completedAtMs: z5.number().finite(),
249
+ conversationId: z5.string().min(1),
228
250
  destination: destinationSchema,
229
251
  /**
230
252
  * All distinct actors annotated on this run's committed instruction-authority
@@ -234,43 +256,44 @@ var pluginRunContextSchema = z4.object({
234
256
  * exceed the actors visible in the transcript slice. Usually `[run.actor]`;
235
257
  * possibly empty for system runs with no human instructions.
236
258
  */
237
- actors: z4.array(actorSchema),
259
+ actors: z5.array(actorSchema),
238
260
  /**
239
261
  * The single actor this run executes as. Absent only for actor-less legacy
240
262
  * system records, so authority-sensitive plugins must fail closed.
241
263
  */
242
264
  actor: actorSchema.optional(),
243
- runId: z4.string().min(1),
265
+ runId: z5.string().min(1),
244
266
  source: sourceSchema,
245
- transcript: z4.array(pluginRunTranscriptEntrySchema)
267
+ transcript: z5.array(pluginRunTranscriptEntrySchema)
246
268
  }).strict();
247
269
 
248
270
  // src/tools.ts
249
- import { z as z5 } from "zod";
271
+ import { z as z6 } from "zod";
250
272
  var PluginToolInputError = class extends Error {
251
273
  constructor(message, options) {
252
274
  super(message, options);
253
275
  this.name = "PluginToolInputError";
254
276
  }
255
277
  };
256
- var pluginToolContinuationSchema = z5.object({
257
- arguments: z5.record(z5.string(), z5.unknown()),
258
- reason: z5.string().min(1).optional()
278
+ var pluginToolContinuationSchema = z6.object({
279
+ arguments: z6.record(z6.string(), z6.unknown()),
280
+ reason: z6.string().min(1).optional()
259
281
  }).strict();
260
- var pluginToolErrorSchema = z5.object({
261
- kind: z5.string().min(1),
262
- message: z5.string().min(1),
263
- retryable: z5.boolean().optional()
282
+ var pluginToolErrorSchema = z6.object({
283
+ kind: z6.string().min(1),
284
+ message: z6.string().min(1),
285
+ retryable: z6.boolean().optional()
264
286
  }).strict();
265
- var pluginToolResultSchema = z5.object({
266
- ok: z5.boolean(),
267
- status: z5.enum(["success", "error"]),
268
- target: z5.string().min(1).optional(),
269
- data: z5.unknown().optional(),
270
- truncated: z5.boolean().optional(),
287
+ var pluginToolResultSchema = z6.object({
288
+ ok: z6.boolean(),
289
+ status: z6.enum(["success", "error"]),
290
+ target: z6.string().min(1).optional(),
291
+ data: z6.unknown().optional(),
292
+ truncated: z6.boolean().optional(),
271
293
  continuation: pluginToolContinuationSchema.optional(),
272
- error: z5.union([pluginToolErrorSchema, z5.string()]).optional()
294
+ error: z6.union([pluginToolErrorSchema, z6.string()]).optional()
273
295
  }).passthrough();
296
+ var toolApprovalModeSchema = z6.enum(["auto", "review", "approve"]);
274
297
  function formatZodPath(path) {
275
298
  return path.length > 0 ? path.map(String).join(".") : "root";
276
299
  }
@@ -292,7 +315,7 @@ function createZodTool(definition, helperName) {
292
315
  let modelInputSchema;
293
316
  let modelOutputSchema;
294
317
  try {
295
- modelInputSchema = z5.toJSONSchema(inputSchema);
318
+ modelInputSchema = z6.toJSONSchema(inputSchema);
296
319
  } catch (error) {
297
320
  throw new TypeError(
298
321
  `${helperName}() inputSchema must be representable as JSON Schema.`,
@@ -300,7 +323,7 @@ function createZodTool(definition, helperName) {
300
323
  );
301
324
  }
302
325
  try {
303
- modelOutputSchema = z5.toJSONSchema(outputSchema);
326
+ modelOutputSchema = z6.toJSONSchema(outputSchema);
304
327
  } catch (error) {
305
328
  throw new TypeError(
306
329
  `${helperName}() outputSchema must be representable as JSON Schema.`,
@@ -336,74 +359,74 @@ function definePluginTool(definition) {
336
359
  }
337
360
 
338
361
  // src/operations.ts
339
- import { z as z6 } from "zod";
340
- var pluginApiRouteRequestContextSchema = z6.object({
341
- auth: z6.object({
342
- user: z6.object({
343
- email: z6.string().nullable().optional(),
344
- emailVerified: z6.boolean().optional(),
345
- name: z6.string().nullable().optional()
362
+ import { z as z7 } from "zod";
363
+ var pluginApiRouteRequestContextSchema = z7.object({
364
+ auth: z7.object({
365
+ user: z7.object({
366
+ email: z7.string().nullable().optional(),
367
+ emailVerified: z7.boolean().optional(),
368
+ name: z7.string().nullable().optional()
346
369
  }).strict()
347
370
  }).strict(),
348
371
  pluginName: nonBlankStringSchema
349
372
  }).strict();
350
373
 
351
374
  // src/credentials.ts
352
- import { z as z7 } from "zod";
353
- var pluginProviderNameSchema = z7.string().regex(/^[a-z][a-z0-9-]*$/);
354
- var pluginGrantNameSchema = z7.string().regex(/^[a-z][a-z0-9.-]*$/);
355
- var pluginGrantAccessSchema = z7.union([
356
- z7.literal("read"),
357
- z7.literal("write")
375
+ import { z as z8 } from "zod";
376
+ var pluginProviderNameSchema = z8.string().regex(/^[a-z][a-z0-9-]*$/);
377
+ var pluginGrantNameSchema = z8.string().regex(/^[a-z][a-z0-9.-]*$/);
378
+ var pluginGrantAccessSchema = z8.union([
379
+ z8.literal("read"),
380
+ z8.literal("write")
358
381
  ]);
359
- var pluginAuthorizationSchema = z7.object({
382
+ var pluginAuthorizationSchema = z8.object({
360
383
  provider: pluginProviderNameSchema,
361
384
  scope: nonBlankStringSchema.optional(),
362
- type: z7.literal("oauth")
385
+ type: z8.literal("oauth")
363
386
  }).strict();
364
- var pluginProviderAccountSchema = z7.object({
387
+ var pluginProviderAccountSchema = z8.object({
365
388
  id: nonBlankStringSchema,
366
389
  label: nonBlankStringSchema.optional(),
367
390
  url: nonBlankStringSchema.optional()
368
391
  }).strict();
369
- var pluginStoredTokensSchema = z7.object({
392
+ var pluginStoredTokensSchema = z8.object({
370
393
  account: pluginProviderAccountSchema.optional(),
371
394
  accessToken: nonBlankStringSchema,
372
- expiresAt: z7.number().finite().optional(),
395
+ expiresAt: z8.number().finite().optional(),
373
396
  refreshToken: nonBlankStringSchema,
374
- refreshTokenExpiresAt: z7.number().finite().optional(),
397
+ refreshTokenExpiresAt: z8.number().finite().optional(),
375
398
  scope: nonBlankStringSchema.optional()
376
399
  }).strict();
377
- var pluginGrantSchema = z7.object({
400
+ var pluginGrantSchema = z8.object({
378
401
  access: pluginGrantAccessSchema,
379
402
  leaseScope: nonBlankStringSchema.optional(),
380
403
  name: pluginGrantNameSchema,
381
404
  reason: nonBlankStringSchema.optional(),
382
- requirements: z7.array(nonBlankStringSchema).min(1).optional()
405
+ requirements: z8.array(nonBlankStringSchema).min(1).optional()
383
406
  }).strict();
384
- var pluginCredentialHeaderTransformSchema = z7.object({
385
- domain: z7.string().min(1),
386
- headers: z7.record(z7.string(), z7.string()).refine((headers) => Object.keys(headers).length > 0)
407
+ var pluginCredentialHeaderTransformSchema = z8.object({
408
+ domain: z8.string().min(1),
409
+ headers: z8.record(z8.string(), z8.string()).refine((headers) => Object.keys(headers).length > 0)
387
410
  }).strict();
388
- var pluginCredentialLeaseSchema = z7.object({
411
+ var pluginCredentialLeaseSchema = z8.object({
389
412
  account: pluginProviderAccountSchema.optional(),
390
413
  authorization: pluginAuthorizationSchema.optional(),
391
- expiresAt: z7.string().refine((value) => Number.isFinite(Date.parse(value))),
392
- headerTransforms: z7.array(pluginCredentialHeaderTransformSchema).min(1)
414
+ expiresAt: z8.string().refine((value) => Number.isFinite(Date.parse(value))),
415
+ headerTransforms: z8.array(pluginCredentialHeaderTransformSchema).min(1)
393
416
  }).strict();
394
- var pluginCredentialResultSchema = z7.discriminatedUnion("type", [
395
- z7.object({
417
+ var pluginCredentialResultSchema = z8.discriminatedUnion("type", [
418
+ z8.object({
396
419
  lease: pluginCredentialLeaseSchema,
397
- type: z7.literal("lease")
420
+ type: z8.literal("lease")
398
421
  }).strict(),
399
- z7.object({
422
+ z8.object({
400
423
  authorization: pluginAuthorizationSchema.optional(),
401
424
  message: nonBlankStringSchema,
402
- type: z7.literal("needed")
425
+ type: z8.literal("needed")
403
426
  }).strict(),
404
- z7.object({
427
+ z8.object({
405
428
  message: nonBlankStringSchema,
406
- type: z7.literal("unavailable")
429
+ type: z8.literal("unavailable")
407
430
  }).strict()
408
431
  ]);
409
432
  var EgressAuthRequired = class extends Error {
@@ -466,6 +489,7 @@ export {
466
489
  EgressPolicyDenied,
467
490
  PluginToolInputError,
468
491
  actorSchema,
492
+ conversationAnnotationInputSchema,
469
493
  createLocalSource,
470
494
  createSlackSource,
471
495
  defineJuniorPlugin,
@@ -498,6 +522,7 @@ export {
498
522
  pluginToolResultSchema,
499
523
  promptMessageSchema,
500
524
  resourceEventSchema,
525
+ resourceLinkAnnotationSchema,
501
526
  slackActorSchema,
502
527
  slackDestinationSchema,
503
528
  slackSourceSchema,
@@ -505,5 +530,6 @@ export {
505
530
  sourceTypeSchema,
506
531
  subscribableResourceSchema,
507
532
  systemActorSchema,
533
+ toolApprovalModeSchema,
508
534
  zodTool
509
535
  };
@@ -53,7 +53,6 @@ export interface PluginEnvVarDeclaration {
53
53
  }
54
54
  export interface PluginManifest {
55
55
  apiHeaders?: Record<string, string>;
56
- capabilities?: string[];
57
56
  commandEnv?: Record<string, string>;
58
57
  configKeys?: string[];
59
58
  credentials?: PluginCredentials;
@@ -3,6 +3,7 @@ import type { PluginContext } from "./context";
3
3
  import type { Dispatch, DispatchOptions, DispatchResult } from "./dispatch";
4
4
  import type { PluginReadState, PluginState } from "./state";
5
5
  import type { ResourceEventPublisher } from "./resource-events";
6
+ import type { PluginConversationAnnotations } from "./annotations";
6
7
  export interface HeartbeatHookContext extends PluginContext {
7
8
  agent: {
8
9
  dispatch(options: DispatchOptions): Promise<DispatchResult>;
@@ -83,6 +84,7 @@ export type PluginRouteApp = {
83
84
  fetch(request: Request, context?: PluginApiRouteRequestContext): Promise<Response> | Response;
84
85
  };
85
86
  export interface RouteRegistrationHookContext extends PluginContext {
87
+ annotations: PluginConversationAnnotations;
86
88
  /** Core-owned delivery boundary for provider webhook events. */
87
89
  resourceEvents: ResourceEventPublisher;
88
90
  }
package/dist/tools.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import type { PluginContext, LocalInvocationContext, PluginEmbedder, PluginModel, Actor, SlackInvocationContext } from "./context";
2
2
  import type { PluginCredentialSubject } from "./credentials";
3
+ import type { PluginAnnotations } from "./annotations";
3
4
  import type { SlackConversationLink } from "./operations";
4
5
  import type { PluginState } from "./state";
5
6
  import { z, type ZodType, type ZodTypeAny } from "zod";
@@ -108,8 +109,57 @@ export type PluginToolResult = z.output<typeof pluginToolResultSchema>;
108
109
  export type PluginToolExecute<TInput = unknown, TOutput = unknown> = {
109
110
  bivarianceHack(input: TInput, options: PluginToolExecuteOptions): Promise<TOutput> | TOutput;
110
111
  }["bivarianceHack"];
111
- export interface PluginToolDefinition<TInput = unknown, TOutput = unknown> {
112
- annotations?: unknown;
112
+ /**
113
+ * Tool-declared approval mode.
114
+ *
115
+ * `auto` delegates to core policy, `review` enters approval review, and
116
+ * `approve` permits execution without review. Omission delegates to core
117
+ * defaults. These values do not select the reviewer.
118
+ *
119
+ * This is declaration metadata only; current tool execution is unchanged.
120
+ * TODO(#1053): Enforce effective approval modes before tool execution.
121
+ */
122
+ export declare const toolApprovalModeSchema: z.ZodEnum<{
123
+ auto: "auto";
124
+ review: "review";
125
+ approve: "approve";
126
+ }>;
127
+ export type ToolApprovalMode = z.output<typeof toolApprovalModeSchema>;
128
+ /**
129
+ * Reviewer signals describing a tool's side-effect behavior.
130
+ *
131
+ * These hints follow the MCP tool annotation contract. Approval review may use
132
+ * them as signals, but they never grant authority or override deterministic
133
+ * authorization.
134
+ */
135
+ export interface ToolAnnotations {
136
+ [key: string]: unknown;
137
+ destructiveHint?: boolean;
138
+ idempotentHint?: boolean;
139
+ openWorldHint?: boolean;
140
+ readOnlyHint?: boolean;
141
+ title?: string;
142
+ }
143
+ /**
144
+ * Canonical approval metadata declared by core and plugin tools.
145
+ *
146
+ * This metadata is declaration-only until #1053 adds approval enforcement.
147
+ * Current tool execution is unchanged.
148
+ */
149
+ export interface ToolApprovalMetadata<TInput = unknown> {
150
+ /** Optional declared approval mode; omission delegates to core defaults. */
151
+ approvalMode?: ToolApprovalMode;
152
+ annotations?: ToolAnnotations;
153
+ /**
154
+ * Describe the exact validated invocation for future approval presentation.
155
+ *
156
+ * Core owns authoritative tool, actor, source, destination, conversation,
157
+ * credential, and input data. This description adds domain-specific context
158
+ * only and is never an authorization grant.
159
+ */
160
+ describeProposal?(input: TInput): string;
161
+ }
162
+ export interface PluginToolDefinition<TInput = unknown, TOutput = unknown> extends ToolApprovalMetadata<TInput> {
113
163
  description: string;
114
164
  executionMode?: unknown;
115
165
  inputSchema: unknown;
@@ -119,7 +169,7 @@ export interface PluginToolDefinition<TInput = unknown, TOutput = unknown> {
119
169
  * Returning `undefined` suppresses private result capture.
120
170
  */
121
171
  privateTraceResult?(result: TOutput): unknown;
122
- prepareArguments?: (args: unknown) => unknown;
172
+ prepareArguments?: (args: unknown) => TInput;
123
173
  /**
124
174
  * @deprecated Put tool-selection and usage guidance directly in `description`
125
175
  * and parameter descriptions. Retained for compatibility; may be removed in a
@@ -168,6 +218,7 @@ interface BaseToolRegistrationHookContext extends PluginContext {
168
218
  * Do not parse as Slack unless the value starts with `slack:`.
169
219
  */
170
220
  conversationId?: string;
221
+ annotations?: PluginAnnotations;
171
222
  embedder: PluginEmbedder;
172
223
  egress: PluginEgress;
173
224
  model: PluginModel;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-plugin-api",
3
- "version": "0.114.0",
3
+ "version": "0.116.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -0,0 +1,46 @@
1
+ import { z } from "zod";
2
+
3
+ function usesHttpProtocol(value: string): boolean {
4
+ try {
5
+ const protocol = new URL(value).protocol;
6
+ return protocol === "http:" || protocol === "https:";
7
+ } catch {
8
+ return false;
9
+ }
10
+ }
11
+
12
+ export const resourceLinkAnnotationSchema = z
13
+ .object({
14
+ kind: z.literal("resource_link"),
15
+ key: z.string().trim().min(1).max(256),
16
+ label: z.string().trim().min(1).max(256),
17
+ url: z
18
+ .string()
19
+ .url()
20
+ .max(2_048)
21
+ .refine(usesHttpProtocol, "URL must use HTTP or HTTPS."),
22
+ description: z.string().trim().min(1).max(512).optional(),
23
+ status: z.enum(["open", "draft", "closed", "merged", "warning"]).optional(),
24
+ })
25
+ .strict();
26
+
27
+ /** Core-known annotation shapes that plugins may attach to a conversation. */
28
+ export const conversationAnnotationInputSchema = z.discriminatedUnion("kind", [
29
+ resourceLinkAnnotationSchema,
30
+ ]);
31
+ export type ConversationAnnotationInput = z.output<
32
+ typeof conversationAnnotationInputSchema
33
+ >;
34
+ export type ConversationAnnotation = ConversationAnnotationInput & {
35
+ plugin: string;
36
+ createdAt: string;
37
+ updatedAt: string;
38
+ };
39
+ export interface PluginAnnotations {
40
+ upsert(annotation: ConversationAnnotationInput): Promise<void>;
41
+ remove(kind: ConversationAnnotationInput["kind"], key: string): Promise<void>;
42
+ list(): Promise<ConversationAnnotation[]>;
43
+ }
44
+ export interface PluginConversationAnnotations {
45
+ forConversation(conversationId: string): PluginAnnotations;
46
+ }
package/src/index.ts CHANGED
@@ -1,3 +1,4 @@
1
+ export * from "./annotations";
1
2
  export * from "./schemas";
2
3
  export * from "./context";
3
4
  export * from "./state";
package/src/manifest.ts CHANGED
@@ -66,7 +66,6 @@ export interface PluginEnvVarDeclaration {
66
66
 
67
67
  export interface PluginManifest {
68
68
  apiHeaders?: Record<string, string>;
69
- capabilities?: string[];
70
69
  commandEnv?: Record<string, string>;
71
70
  configKeys?: string[];
72
71
  credentials?: PluginCredentials;
package/src/operations.ts CHANGED
@@ -4,6 +4,7 @@ import type { Dispatch, DispatchOptions, DispatchResult } from "./dispatch";
4
4
  import { nonBlankStringSchema } from "./schemas";
5
5
  import type { PluginReadState, PluginState } from "./state";
6
6
  import type { ResourceEventPublisher } from "./resource-events";
7
+ import type { PluginConversationAnnotations } from "./annotations";
7
8
 
8
9
  export interface HeartbeatHookContext extends PluginContext {
9
10
  agent: {
@@ -113,6 +114,7 @@ export type PluginRouteApp = {
113
114
  };
114
115
 
115
116
  export interface RouteRegistrationHookContext extends PluginContext {
117
+ annotations: PluginConversationAnnotations;
116
118
  /** Core-owned delivery boundary for provider webhook events. */
117
119
  resourceEvents: ResourceEventPublisher;
118
120
  }
package/src/tools.ts CHANGED
@@ -7,6 +7,7 @@ import type {
7
7
  SlackInvocationContext,
8
8
  } from "./context";
9
9
  import type { PluginCredentialSubject } from "./credentials";
10
+ import type { PluginAnnotations } from "./annotations";
10
11
  import type { SlackConversationLink } from "./operations";
11
12
  import type { PluginState } from "./state";
12
13
  import { z, type ZodType, type ZodTypeAny } from "zod";
@@ -129,8 +130,60 @@ export type PluginToolExecute<TInput = unknown, TOutput = unknown> = {
129
130
  ): Promise<TOutput> | TOutput;
130
131
  }["bivarianceHack"];
131
132
 
132
- export interface PluginToolDefinition<TInput = unknown, TOutput = unknown> {
133
- annotations?: unknown;
133
+ /**
134
+ * Tool-declared approval mode.
135
+ *
136
+ * `auto` delegates to core policy, `review` enters approval review, and
137
+ * `approve` permits execution without review. Omission delegates to core
138
+ * defaults. These values do not select the reviewer.
139
+ *
140
+ * This is declaration metadata only; current tool execution is unchanged.
141
+ * TODO(#1053): Enforce effective approval modes before tool execution.
142
+ */
143
+ export const toolApprovalModeSchema = z.enum(["auto", "review", "approve"]);
144
+
145
+ export type ToolApprovalMode = z.output<typeof toolApprovalModeSchema>;
146
+
147
+ /**
148
+ * Reviewer signals describing a tool's side-effect behavior.
149
+ *
150
+ * These hints follow the MCP tool annotation contract. Approval review may use
151
+ * them as signals, but they never grant authority or override deterministic
152
+ * authorization.
153
+ */
154
+ export interface ToolAnnotations {
155
+ [key: string]: unknown;
156
+ destructiveHint?: boolean;
157
+ idempotentHint?: boolean;
158
+ openWorldHint?: boolean;
159
+ readOnlyHint?: boolean;
160
+ title?: string;
161
+ }
162
+
163
+ /**
164
+ * Canonical approval metadata declared by core and plugin tools.
165
+ *
166
+ * This metadata is declaration-only until #1053 adds approval enforcement.
167
+ * Current tool execution is unchanged.
168
+ */
169
+ export interface ToolApprovalMetadata<TInput = unknown> {
170
+ /** Optional declared approval mode; omission delegates to core defaults. */
171
+ approvalMode?: ToolApprovalMode;
172
+ annotations?: ToolAnnotations;
173
+ /**
174
+ * Describe the exact validated invocation for future approval presentation.
175
+ *
176
+ * Core owns authoritative tool, actor, source, destination, conversation,
177
+ * credential, and input data. This description adds domain-specific context
178
+ * only and is never an authorization grant.
179
+ */
180
+ describeProposal?(input: TInput): string;
181
+ }
182
+
183
+ export interface PluginToolDefinition<
184
+ TInput = unknown,
185
+ TOutput = unknown,
186
+ > extends ToolApprovalMetadata<TInput> {
134
187
  description: string;
135
188
  executionMode?: unknown;
136
189
  inputSchema: unknown;
@@ -140,7 +193,7 @@ export interface PluginToolDefinition<TInput = unknown, TOutput = unknown> {
140
193
  * Returning `undefined` suppresses private result capture.
141
194
  */
142
195
  privateTraceResult?(result: TOutput): unknown;
143
- prepareArguments?: (args: unknown) => unknown;
196
+ prepareArguments?: (args: unknown) => TInput;
144
197
  /**
145
198
  * @deprecated Put tool-selection and usage guidance directly in `description`
146
199
  * and parameter descriptions. Retained for compatibility; may be removed in a
@@ -294,6 +347,7 @@ interface BaseToolRegistrationHookContext extends PluginContext {
294
347
  * Do not parse as Slack unless the value starts with `slack:`.
295
348
  */
296
349
  conversationId?: string;
350
+ annotations?: PluginAnnotations;
297
351
  embedder: PluginEmbedder;
298
352
  egress: PluginEgress;
299
353
  model: PluginModel;