@sentry/junior-plugin-api 0.115.0 → 0.116.1

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,44 +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();
274
- var toolApprovalModeSchema = z5.enum(["auto", "review", "approve"]);
296
+ var toolApprovalModeSchema = z6.enum(["auto", "review", "approve"]);
275
297
  function formatZodPath(path) {
276
298
  return path.length > 0 ? path.map(String).join(".") : "root";
277
299
  }
@@ -293,7 +315,7 @@ function createZodTool(definition, helperName) {
293
315
  let modelInputSchema;
294
316
  let modelOutputSchema;
295
317
  try {
296
- modelInputSchema = z5.toJSONSchema(inputSchema);
318
+ modelInputSchema = z6.toJSONSchema(inputSchema);
297
319
  } catch (error) {
298
320
  throw new TypeError(
299
321
  `${helperName}() inputSchema must be representable as JSON Schema.`,
@@ -301,7 +323,7 @@ function createZodTool(definition, helperName) {
301
323
  );
302
324
  }
303
325
  try {
304
- modelOutputSchema = z5.toJSONSchema(outputSchema);
326
+ modelOutputSchema = z6.toJSONSchema(outputSchema);
305
327
  } catch (error) {
306
328
  throw new TypeError(
307
329
  `${helperName}() outputSchema must be representable as JSON Schema.`,
@@ -337,74 +359,74 @@ function definePluginTool(definition) {
337
359
  }
338
360
 
339
361
  // src/operations.ts
340
- import { z as z6 } from "zod";
341
- var pluginApiRouteRequestContextSchema = z6.object({
342
- auth: z6.object({
343
- user: z6.object({
344
- email: z6.string().nullable().optional(),
345
- emailVerified: z6.boolean().optional(),
346
- 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()
347
369
  }).strict()
348
370
  }).strict(),
349
371
  pluginName: nonBlankStringSchema
350
372
  }).strict();
351
373
 
352
374
  // src/credentials.ts
353
- import { z as z7 } from "zod";
354
- var pluginProviderNameSchema = z7.string().regex(/^[a-z][a-z0-9-]*$/);
355
- var pluginGrantNameSchema = z7.string().regex(/^[a-z][a-z0-9.-]*$/);
356
- var pluginGrantAccessSchema = z7.union([
357
- z7.literal("read"),
358
- 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")
359
381
  ]);
360
- var pluginAuthorizationSchema = z7.object({
382
+ var pluginAuthorizationSchema = z8.object({
361
383
  provider: pluginProviderNameSchema,
362
384
  scope: nonBlankStringSchema.optional(),
363
- type: z7.literal("oauth")
385
+ type: z8.literal("oauth")
364
386
  }).strict();
365
- var pluginProviderAccountSchema = z7.object({
387
+ var pluginProviderAccountSchema = z8.object({
366
388
  id: nonBlankStringSchema,
367
389
  label: nonBlankStringSchema.optional(),
368
390
  url: nonBlankStringSchema.optional()
369
391
  }).strict();
370
- var pluginStoredTokensSchema = z7.object({
392
+ var pluginStoredTokensSchema = z8.object({
371
393
  account: pluginProviderAccountSchema.optional(),
372
394
  accessToken: nonBlankStringSchema,
373
- expiresAt: z7.number().finite().optional(),
395
+ expiresAt: z8.number().finite().optional(),
374
396
  refreshToken: nonBlankStringSchema,
375
- refreshTokenExpiresAt: z7.number().finite().optional(),
397
+ refreshTokenExpiresAt: z8.number().finite().optional(),
376
398
  scope: nonBlankStringSchema.optional()
377
399
  }).strict();
378
- var pluginGrantSchema = z7.object({
400
+ var pluginGrantSchema = z8.object({
379
401
  access: pluginGrantAccessSchema,
380
402
  leaseScope: nonBlankStringSchema.optional(),
381
403
  name: pluginGrantNameSchema,
382
404
  reason: nonBlankStringSchema.optional(),
383
- requirements: z7.array(nonBlankStringSchema).min(1).optional()
405
+ requirements: z8.array(nonBlankStringSchema).min(1).optional()
384
406
  }).strict();
385
- var pluginCredentialHeaderTransformSchema = z7.object({
386
- domain: z7.string().min(1),
387
- 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)
388
410
  }).strict();
389
- var pluginCredentialLeaseSchema = z7.object({
411
+ var pluginCredentialLeaseSchema = z8.object({
390
412
  account: pluginProviderAccountSchema.optional(),
391
413
  authorization: pluginAuthorizationSchema.optional(),
392
- expiresAt: z7.string().refine((value) => Number.isFinite(Date.parse(value))),
393
- headerTransforms: z7.array(pluginCredentialHeaderTransformSchema).min(1)
414
+ expiresAt: z8.string().refine((value) => Number.isFinite(Date.parse(value))),
415
+ headerTransforms: z8.array(pluginCredentialHeaderTransformSchema).min(1)
394
416
  }).strict();
395
- var pluginCredentialResultSchema = z7.discriminatedUnion("type", [
396
- z7.object({
417
+ var pluginCredentialResultSchema = z8.discriminatedUnion("type", [
418
+ z8.object({
397
419
  lease: pluginCredentialLeaseSchema,
398
- type: z7.literal("lease")
420
+ type: z8.literal("lease")
399
421
  }).strict(),
400
- z7.object({
422
+ z8.object({
401
423
  authorization: pluginAuthorizationSchema.optional(),
402
424
  message: nonBlankStringSchema,
403
- type: z7.literal("needed")
425
+ type: z8.literal("needed")
404
426
  }).strict(),
405
- z7.object({
427
+ z8.object({
406
428
  message: nonBlankStringSchema,
407
- type: z7.literal("unavailable")
429
+ type: z8.literal("unavailable")
408
430
  }).strict()
409
431
  ]);
410
432
  var EgressAuthRequired = class extends Error {
@@ -467,6 +489,7 @@ export {
467
489
  EgressPolicyDenied,
468
490
  PluginToolInputError,
469
491
  actorSchema,
492
+ conversationAnnotationInputSchema,
470
493
  createLocalSource,
471
494
  createSlackSource,
472
495
  defineJuniorPlugin,
@@ -499,6 +522,7 @@ export {
499
522
  pluginToolResultSchema,
500
523
  promptMessageSchema,
501
524
  resourceEventSchema,
525
+ resourceLinkAnnotationSchema,
502
526
  slackActorSchema,
503
527
  slackDestinationSchema,
504
528
  slackSourceSchema,
@@ -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";
@@ -124,10 +125,41 @@ export declare const toolApprovalModeSchema: z.ZodEnum<{
124
125
  approve: "approve";
125
126
  }>;
126
127
  export type ToolApprovalMode = z.output<typeof toolApprovalModeSchema>;
127
- export interface PluginToolDefinition<TInput = unknown, TOutput = unknown> {
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> {
128
150
  /** Optional declared approval mode; omission delegates to core defaults. */
129
151
  approvalMode?: ToolApprovalMode;
130
- annotations?: unknown;
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> {
131
163
  description: string;
132
164
  executionMode?: unknown;
133
165
  inputSchema: unknown;
@@ -137,7 +169,7 @@ export interface PluginToolDefinition<TInput = unknown, TOutput = unknown> {
137
169
  * Returning `undefined` suppresses private result capture.
138
170
  */
139
171
  privateTraceResult?(result: TOutput): unknown;
140
- prepareArguments?: (args: unknown) => unknown;
172
+ prepareArguments?: (args: unknown) => TInput;
141
173
  /**
142
174
  * @deprecated Put tool-selection and usage guidance directly in `description`
143
175
  * and parameter descriptions. Retained for compatibility; may be removed in a
@@ -186,6 +218,7 @@ interface BaseToolRegistrationHookContext extends PluginContext {
186
218
  * Do not parse as Slack unless the value starts with `slack:`.
187
219
  */
188
220
  conversationId?: string;
221
+ annotations?: PluginAnnotations;
189
222
  embedder: PluginEmbedder;
190
223
  egress: PluginEgress;
191
224
  model: PluginModel;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-plugin-api",
3
- "version": "0.115.0",
3
+ "version": "0.116.1",
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";
@@ -143,10 +144,46 @@ export const toolApprovalModeSchema = z.enum(["auto", "review", "approve"]);
143
144
 
144
145
  export type ToolApprovalMode = z.output<typeof toolApprovalModeSchema>;
145
146
 
146
- export interface PluginToolDefinition<TInput = unknown, TOutput = unknown> {
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> {
147
170
  /** Optional declared approval mode; omission delegates to core defaults. */
148
171
  approvalMode?: ToolApprovalMode;
149
- annotations?: unknown;
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> {
150
187
  description: string;
151
188
  executionMode?: unknown;
152
189
  inputSchema: unknown;
@@ -156,7 +193,7 @@ export interface PluginToolDefinition<TInput = unknown, TOutput = unknown> {
156
193
  * Returning `undefined` suppresses private result capture.
157
194
  */
158
195
  privateTraceResult?(result: TOutput): unknown;
159
- prepareArguments?: (args: unknown) => unknown;
196
+ prepareArguments?: (args: unknown) => TInput;
160
197
  /**
161
198
  * @deprecated Put tool-selection and usage guidance directly in `description`
162
199
  * and parameter descriptions. Retained for compatibility; may be removed in a
@@ -310,6 +347,7 @@ interface BaseToolRegistrationHookContext extends PluginContext {
310
347
  * Do not parse as Slack unless the value starts with `slack:`.
311
348
  */
312
349
  conversationId?: string;
350
+ annotations?: PluginAnnotations;
313
351
  embedder: PluginEmbedder;
314
352
  egress: PluginEgress;
315
353
  model: PluginModel;