@sentry/junior-plugin-api 0.181.1 → 0.183.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/code.d.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { z } from "zod";
2
+ export declare const codeChangeStateSchema: z.ZodEnum<{
3
+ open: "open";
4
+ closed: "closed";
5
+ merged: "merged";
6
+ }>;
7
+ export type CodeChangeState = z.output<typeof codeChangeStateSchema>;
8
+ export declare const codeChangeInputSchema: z.ZodObject<{
9
+ closedAt: z.ZodOptional<z.ZodDate>;
10
+ conversationIds: z.ZodDefault<z.ZodArray<z.ZodString>>;
11
+ mergedAt: z.ZodOptional<z.ZodDate>;
12
+ number: z.ZodNumber;
13
+ openedAt: z.ZodDate;
14
+ providerId: z.ZodString;
15
+ repository: z.ZodObject<{
16
+ name: z.ZodString;
17
+ providerId: z.ZodString;
18
+ url: z.ZodOptional<z.ZodString>;
19
+ }, z.core.$strict>;
20
+ state: z.ZodEnum<{
21
+ open: "open";
22
+ closed: "closed";
23
+ merged: "merged";
24
+ }>;
25
+ title: z.ZodOptional<z.ZodString>;
26
+ updatedAt: z.ZodDate;
27
+ url: z.ZodOptional<z.ZodString>;
28
+ }, z.core.$strict>;
29
+ export type CodeChangeInput = z.input<typeof codeChangeInputSchema>;
30
+ /** Write code changes from a plugin to Junior's code records. */
31
+ export interface CodeChangePublisher {
32
+ associateConversations(input: {
33
+ conversationIds: string[];
34
+ providerId: string;
35
+ }): Promise<void>;
36
+ record(input: CodeChangeInput): Promise<void>;
37
+ }
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./annotations";
2
+ export * from "./code";
2
3
  export * from "./conversation-events";
3
4
  export * from "./schemas";
4
5
  export * from "./context";
package/dist/index.js CHANGED
@@ -33,10 +33,31 @@ var conversationSidebarAnnotationSchema = z.object({
33
33
  label: z.string().trim().min(1).max(256)
34
34
  }).strict();
35
35
 
36
- // src/conversation-events.ts
36
+ // src/code.ts
37
37
  import { z as z2 } from "zod";
38
- var conversationEventNameSchema = z2.string().trim().min(1).max(64).regex(/^[a-z][a-z0-9_]*$/);
39
- var conversationEventIconSchema = z2.enum([
38
+ var codeChangeStateSchema = z2.enum(["closed", "merged", "open"]);
39
+ var codeChangeInputSchema = z2.object({
40
+ closedAt: z2.date().optional(),
41
+ conversationIds: z2.array(z2.string().min(1)).default([]),
42
+ mergedAt: z2.date().optional(),
43
+ number: z2.number().int().positive(),
44
+ openedAt: z2.date(),
45
+ providerId: z2.string().min(1),
46
+ repository: z2.object({
47
+ name: z2.string().min(1),
48
+ providerId: z2.string().min(1),
49
+ url: z2.string().url().optional()
50
+ }).strict(),
51
+ state: codeChangeStateSchema,
52
+ title: z2.string().min(1).optional(),
53
+ updatedAt: z2.date(),
54
+ url: z2.string().url().optional()
55
+ }).strict();
56
+
57
+ // src/conversation-events.ts
58
+ import { z as z3 } from "zod";
59
+ var conversationEventNameSchema = z3.string().trim().min(1).max(64).regex(/^[a-z][a-z0-9_]*$/);
60
+ var conversationEventIconSchema = z3.enum([
40
61
  "activity",
41
62
  "brain",
42
63
  "calendar",
@@ -48,22 +69,22 @@ var conversationEventIconSchema = z2.enum([
48
69
  "sparkles",
49
70
  "warning"
50
71
  ]);
51
- var conversationEventDetailSchema = z2.object({
52
- content: z2.string().min(1).max(32 * 1024).optional(),
53
- description: z2.string().trim().min(1).max(2e3).optional(),
54
- metadata: z2.array(z2.string().trim().min(1).max(120)).max(8).optional(),
55
- title: z2.string().trim().min(1).max(4e3)
56
- }).strict();
57
- var conversationEventPresentationSchema = z2.object({
58
- details: z2.array(conversationEventDetailSchema).max(100).optional(),
72
+ var conversationEventDetailSchema = z3.object({
73
+ content: z3.string().min(1).max(32 * 1024).optional(),
74
+ description: z3.string().trim().min(1).max(2e3).optional(),
75
+ metadata: z3.array(z3.string().trim().min(1).max(120)).max(8).optional(),
76
+ title: z3.string().trim().min(1).max(4e3)
77
+ }).strict();
78
+ var conversationEventPresentationSchema = z3.object({
79
+ details: z3.array(conversationEventDetailSchema).max(100).optional(),
59
80
  icon: conversationEventIconSchema.optional(),
60
- preview: z2.string().trim().min(1).max(500).optional(),
61
- title: z2.string().trim().min(1).max(120)
81
+ preview: z3.string().trim().min(1).max(500).optional(),
82
+ title: z3.string().trim().min(1).max(120)
62
83
  }).strict();
63
84
  function defineConversationEvent(definition) {
64
- const identity = z2.object({
85
+ const identity = z3.object({
65
86
  name: conversationEventNameSchema,
66
- version: z2.number().int().positive()
87
+ version: z3.number().int().positive()
67
88
  }).strict().parse({ name: definition.name, version: definition.version });
68
89
  let eventDefinition;
69
90
  const createEvent = (data) => ({
@@ -86,32 +107,32 @@ function defineConversationEvent(definition) {
86
107
  }
87
108
 
88
109
  // src/schemas.ts
89
- import { z as z3 } from "zod";
90
- var slackTeamIdSchema = z3.string().regex(/^T[A-Z0-9]+$/);
91
- var slackConversationIdSchema = z3.string().regex(/^(C|G|D)[A-Z0-9]+$/);
92
- var localConversationIdSchema = z3.string().regex(/^local:[a-z0-9_-]+:[a-z0-9][a-z0-9_-]*$/);
93
- var actorUserIdSchema = z3.string().min(1).refine(
110
+ import { z as z4 } from "zod";
111
+ var slackTeamIdSchema = z4.string().regex(/^T[A-Z0-9]+$/);
112
+ var slackConversationIdSchema = z4.string().regex(/^(C|G|D)[A-Z0-9]+$/);
113
+ var localConversationIdSchema = z4.string().regex(/^local:[a-z0-9_-]+:[a-z0-9][a-z0-9_-]*$/);
114
+ var actorUserIdSchema = z4.string().min(1).refine(
94
115
  (value) => value === value.trim() && value.toLowerCase() !== "unknown"
95
116
  );
96
117
  var exactActorUserIdSchema = actorUserIdSchema;
97
- var nonBlankStringSchema = z3.string().refine((value) => value.trim().length > 0);
118
+ var nonBlankStringSchema = z4.string().refine((value) => value.trim().length > 0);
98
119
  var exactNonBlankStringSchema = nonBlankStringSchema.refine(
99
120
  (value) => value === value.trim()
100
121
  );
101
- var platformSchema = z3.enum(["slack", "local"]);
102
- var sourceVisibilitySchema = z3.enum(["public", "private"]);
103
- var destinationVisibilitySchema = z3.enum(["public", "private"]);
104
- var slackAddressSchema = z3.object({
105
- platform: z3.literal("slack"),
122
+ var platformSchema = z4.enum(["slack", "local"]);
123
+ var sourceVisibilitySchema = z4.enum(["public", "private"]);
124
+ var destinationVisibilitySchema = z4.enum(["public", "private"]);
125
+ var slackAddressSchema = z4.object({
126
+ platform: z4.literal("slack"),
106
127
  teamId: slackTeamIdSchema,
107
128
  channelId: slackConversationIdSchema
108
129
  }).strict();
109
130
  var slackDestinationSchema = slackAddressSchema;
110
- var localDestinationSchema = z3.object({
111
- platform: z3.literal("local"),
131
+ var localDestinationSchema = z4.object({
132
+ platform: z4.literal("local"),
112
133
  conversationId: localConversationIdSchema
113
134
  }).strict();
114
- var destinationSchema = z3.discriminatedUnion("platform", [
135
+ var destinationSchema = z4.discriminatedUnion("platform", [
115
136
  slackDestinationSchema,
116
137
  localDestinationSchema
117
138
  ]);
@@ -120,34 +141,34 @@ var slackSourceSchema = slackAddressSchema.extend({
120
141
  messageTs: nonBlankStringSchema.optional(),
121
142
  threadTs: nonBlankStringSchema.optional()
122
143
  }).strict();
123
- var localSourceSchema = z3.object({
124
- platform: z3.literal("local"),
125
- visibility: z3.literal("private"),
144
+ var localSourceSchema = z4.object({
145
+ platform: z4.literal("local"),
146
+ visibility: z4.literal("private"),
126
147
  conversationId: localConversationIdSchema
127
148
  }).strict();
128
- var webSourceSchema = z3.object({
129
- platform: z3.literal("web"),
149
+ var webSourceSchema = z4.object({
150
+ platform: z4.literal("web"),
130
151
  visibility: sourceVisibilitySchema,
131
152
  // Web can continue any existing conversation id, including Slack roots.
132
153
  conversationId: exactNonBlankStringSchema
133
154
  }).strict();
134
- var sourceSchema = z3.discriminatedUnion("platform", [
155
+ var sourceSchema = z4.discriminatedUnion("platform", [
135
156
  slackSourceSchema,
136
157
  localSourceSchema,
137
158
  webSourceSchema
138
159
  ]);
139
- var pluginCredentialSubjectSchema = z3.discriminatedUnion(
160
+ var pluginCredentialSubjectSchema = z4.discriminatedUnion(
140
161
  "allowedWhen",
141
162
  [
142
- z3.object({
143
- type: z3.literal("user"),
163
+ z4.object({
164
+ type: z4.literal("user"),
144
165
  userId: exactActorUserIdSchema,
145
- allowedWhen: z3.literal("private-direct-conversation")
166
+ allowedWhen: z4.literal("private-direct-conversation")
146
167
  }).strict(),
147
- z3.object({
148
- type: z3.literal("user"),
168
+ z4.object({
169
+ type: z4.literal("user"),
149
170
  userId: exactActorUserIdSchema,
150
- allowedWhen: z3.literal("scheduled-task"),
171
+ allowedWhen: z4.literal("scheduled-task"),
151
172
  taskId: exactNonBlankStringSchema
152
173
  }).strict()
153
174
  ]
@@ -158,30 +179,30 @@ var actorProfileSchema = {
158
179
  userId: exactActorUserIdSchema,
159
180
  userName: nonBlankStringSchema.optional()
160
181
  };
161
- var slackActorSchema = z3.object({
182
+ var slackActorSchema = z4.object({
162
183
  ...actorProfileSchema,
163
- platform: z3.literal("slack"),
184
+ platform: z4.literal("slack"),
164
185
  teamId: slackTeamIdSchema
165
186
  }).strict();
166
- var localActorSchema = z3.object({
187
+ var localActorSchema = z4.object({
167
188
  ...actorProfileSchema,
168
- platform: z3.literal("local")
189
+ platform: z4.literal("local")
169
190
  }).strict();
170
- var webActorSchema = z3.object({
191
+ var webActorSchema = z4.object({
171
192
  ...actorProfileSchema,
172
- platform: z3.literal("web")
193
+ platform: z4.literal("web")
173
194
  }).strict();
174
- var systemActorSchema = z3.object({
175
- platform: z3.literal("system"),
195
+ var systemActorSchema = z4.object({
196
+ platform: z4.literal("system"),
176
197
  name: exactActorUserIdSchema
177
198
  }).strict();
178
- var actorSchema = z3.discriminatedUnion("platform", [
199
+ var actorSchema = z4.discriminatedUnion("platform", [
179
200
  slackActorSchema,
180
201
  localActorSchema,
181
202
  webActorSchema,
182
203
  systemActorSchema
183
204
  ]);
184
- var identitySchema = z3.object({
205
+ var identitySchema = z4.object({
185
206
  displayName: nonBlankStringSchema.optional(),
186
207
  handle: nonBlankStringSchema.optional(),
187
208
  id: exactNonBlankStringSchema,
@@ -189,17 +210,17 @@ var identitySchema = z3.object({
189
210
  providerSubjectId: exactNonBlankStringSchema,
190
211
  providerTenantId: exactNonBlankStringSchema.optional()
191
212
  }).strict();
192
- var userSchema = z3.object({
213
+ var userSchema = z4.object({
193
214
  displayName: nonBlankStringSchema.optional(),
194
215
  email: nonBlankStringSchema.max(320),
195
216
  id: exactNonBlankStringSchema,
196
- identities: z3.array(identitySchema).max(100)
217
+ identities: z4.array(identitySchema).max(100)
197
218
  }).strict();
198
- var dispatchMetadataSchema = z3.record(z3.string(), z3.string()).superRefine((metadata, ctx) => {
219
+ var dispatchMetadataSchema = z4.record(z4.string(), z4.string()).superRefine((metadata, ctx) => {
199
220
  const entries = Object.entries(metadata);
200
221
  if (entries.length > 20) {
201
222
  ctx.addIssue({
202
- code: z3.ZodIssueCode.custom,
223
+ code: z4.ZodIssueCode.custom,
203
224
  message: "Dispatch metadata has too many keys"
204
225
  });
205
226
  return;
@@ -207,7 +228,7 @@ var dispatchMetadataSchema = z3.record(z3.string(), z3.string()).superRefine((me
207
228
  for (const [key, value] of entries) {
208
229
  if (!key.trim()) {
209
230
  ctx.addIssue({
210
- code: z3.ZodIssueCode.custom,
231
+ code: z4.ZodIssueCode.custom,
211
232
  message: "Dispatch metadata values must be strings",
212
233
  path: [key]
213
234
  });
@@ -215,44 +236,44 @@ var dispatchMetadataSchema = z3.record(z3.string(), z3.string()).superRefine((me
215
236
  }
216
237
  if (key.length > 128) {
217
238
  ctx.addIssue({
218
- code: z3.ZodIssueCode.custom,
239
+ code: z4.ZodIssueCode.custom,
219
240
  message: "Dispatch metadata key exceeds the maximum length",
220
241
  path: [key]
221
242
  });
222
243
  }
223
244
  if (/[\r\n]/.test(key)) {
224
245
  ctx.addIssue({
225
- code: z3.ZodIssueCode.custom,
246
+ code: z4.ZodIssueCode.custom,
226
247
  message: "Dispatch metadata keys must be single-line strings",
227
248
  path: [key]
228
249
  });
229
250
  }
230
251
  if (/[\r\n]/.test(value)) {
231
252
  ctx.addIssue({
232
- code: z3.ZodIssueCode.custom,
253
+ code: z4.ZodIssueCode.custom,
233
254
  message: "Dispatch metadata values must be single-line strings",
234
255
  path: [key]
235
256
  });
236
257
  }
237
258
  if (value.length > 512) {
238
259
  ctx.addIssue({
239
- code: z3.ZodIssueCode.custom,
260
+ code: z4.ZodIssueCode.custom,
240
261
  message: "Dispatch metadata value exceeds the maximum length",
241
262
  path: [key]
242
263
  });
243
264
  }
244
265
  }
245
266
  });
246
- var replyAttributionSchema = z3.object({
247
- label: exactNonBlankStringSchema.pipe(z3.string().max(48)).refine((value) => !/[\r\n]/.test(value)),
248
- detail: exactNonBlankStringSchema.pipe(z3.string().max(128)).refine((value) => !/[\r\n]/.test(value)).optional()
267
+ var replyAttributionSchema = z4.object({
268
+ label: exactNonBlankStringSchema.pipe(z4.string().max(48)).refine((value) => !/[\r\n]/.test(value)),
269
+ detail: exactNonBlankStringSchema.pipe(z4.string().max(128)).refine((value) => !/[\r\n]/.test(value)).optional()
249
270
  }).strict();
250
- var dispatchOptionsSchema = z3.object({
251
- idempotencyKey: nonBlankStringSchema.pipe(z3.string().max(512)),
271
+ var dispatchOptionsSchema = z4.object({
272
+ idempotencyKey: nonBlankStringSchema.pipe(z4.string().max(512)),
252
273
  credentialSubject: pluginCredentialSubjectSchema.optional(),
253
274
  destination: slackDestinationSchema,
254
275
  destinationVisibility: destinationVisibilitySchema,
255
- input: nonBlankStringSchema.pipe(z3.string().max(32e3)),
276
+ input: nonBlankStringSchema.pipe(z4.string().max(32e3)),
256
277
  metadata: dispatchMetadataSchema.optional(),
257
278
  replyAttribution: replyAttributionSchema.optional(),
258
279
  source: sourceSchema
@@ -305,15 +326,15 @@ function isSlackDestination(destination) {
305
326
  }
306
327
 
307
328
  // src/prompt.ts
308
- import { z as z4 } from "zod";
309
- var promptContextKindSchema = z4.string().trim().min(1).max(64).regex(/^[a-z][a-z0-9_-]*$/);
310
- var promptMessageSchema = z4.object({
311
- text: z4.string().trim().min(1).max(8e3)
329
+ import { z as z5 } from "zod";
330
+ var promptContextKindSchema = z5.string().trim().min(1).max(64).regex(/^[a-z][a-z0-9_-]*$/);
331
+ var promptMessageSchema = z5.object({
332
+ text: z5.string().trim().min(1).max(8e3)
312
333
  }).strict();
313
- var promptContextSchema = z4.object({
334
+ var promptContextSchema = z5.object({
314
335
  kind: promptContextKindSchema,
315
- version: z4.number().int().positive(),
316
- content: z4.record(z4.string(), z4.unknown())
336
+ version: z5.number().int().positive(),
337
+ content: z5.record(z5.string(), z5.unknown())
317
338
  }).strict();
318
339
  function definePromptContext(definition) {
319
340
  const identity = promptContextSchema.pick({ kind: true, version: true }).parse({ kind: definition.kind, version: definition.version });
@@ -327,13 +348,13 @@ function definePromptContext(definition) {
327
348
  }
328
349
 
329
350
  // src/resource-events.ts
330
- import { z as z5 } from "zod";
351
+ import { z as z6 } from "zod";
331
352
  var RESOURCE_EVENT_SUMMARY_MAX_LENGTH = 4e3;
332
353
  var RESOURCE_EVENT_TEXT_MAX_LENGTH = 8e3;
333
354
  var RESOURCE_EVENT_DATA_MAX_KEYS = 32;
334
355
  var RESOURCE_EVENT_DATA_MAX_JSON_BYTES = 4e3;
335
356
  var RESOURCE_EVENT_GUIDANCE_MAX_LENGTH = 1e3;
336
- var resourceEventDataSchema = z5.record(z5.string(), z5.unknown()).superRefine((value, context) => {
357
+ var resourceEventDataSchema = z6.record(z6.string(), z6.unknown()).superRefine((value, context) => {
337
358
  const keys = Object.keys(value);
338
359
  if (keys.length > RESOURCE_EVENT_DATA_MAX_KEYS) {
339
360
  context.addIssue({
@@ -351,19 +372,99 @@ var resourceEventDataSchema = z5.record(z5.string(), z5.unknown()).superRefine((
351
372
  });
352
373
  }
353
374
  });
354
- var resourceEventTypeSchema = z5.string().regex(/^[a-z][a-z0-9_-]*(?:\.[a-z][a-z0-9_-]*)+$/).describe(
375
+ var resourceEventTypeSchema = z6.string().regex(/^[a-z][a-z0-9_-]*(?:\.[a-z][a-z0-9_-]*)+$/).describe(
355
376
  "Canonical dotted event type, such as issue.closed or pull_request.review.changes_requested."
356
377
  );
357
- var resourceTypeSchema = z5.string().regex(/^[a-z][a-z0-9_]*$/).describe(
378
+ var resourceTypeSchema = z6.string().regex(/^[a-z][a-z0-9_]*$/).describe(
358
379
  "Canonical plugin-defined resource type, such as issue or pull_request."
359
380
  );
360
- var pluginResourceEventTypeSchema = z5.object({
381
+ var RESOURCE_EVENT_MATCH_FIELD_NAME = /^[a-z][a-zA-Z0-9]*$/;
382
+ var resourceEventMatchFieldSchema = z6.object({
383
+ kind: z6.enum(["boolean", "string", "number"]),
384
+ description: z6.string().trim().min(1).max(200),
385
+ enum: z6.array(z6.string().min(1)).min(1).optional()
386
+ }).strict().superRefine((field, context) => {
387
+ if (field.enum && field.kind !== "string") {
388
+ context.addIssue({
389
+ code: "custom",
390
+ message: 'Resource event match field enum requires kind "string".',
391
+ path: ["enum"]
392
+ });
393
+ }
394
+ });
395
+ var resourceEventMatchFieldsSchema = z6.record(z6.string().regex(RESOURCE_EVENT_MATCH_FIELD_NAME), resourceEventMatchFieldSchema).superRefine((fields, context) => {
396
+ if (Object.keys(fields).length > RESOURCE_EVENT_DATA_MAX_KEYS) {
397
+ context.addIssue({
398
+ code: "custom",
399
+ message: `Resource event match keys may include at most ${RESOURCE_EVENT_DATA_MAX_KEYS} keys.`
400
+ });
401
+ }
402
+ });
403
+ var resourceEventMatchSchema = z6.record(
404
+ z6.string().regex(RESOURCE_EVENT_MATCH_FIELD_NAME),
405
+ z6.union([
406
+ z6.boolean(),
407
+ z6.number().finite(),
408
+ z6.string().min(1),
409
+ z6.array(z6.union([z6.number().finite(), z6.string().min(1)])).min(1)
410
+ ])
411
+ ).superRefine((match, context) => {
412
+ if (Object.keys(match).length > RESOURCE_EVENT_DATA_MAX_KEYS) {
413
+ context.addIssue({
414
+ code: "custom",
415
+ message: `Resource event match may include at most ${RESOURCE_EVENT_DATA_MAX_KEYS} keys.`
416
+ });
417
+ }
418
+ for (const [key, value] of Object.entries(match)) {
419
+ if (!Array.isArray(value)) continue;
420
+ if (value.some((entry) => typeof entry === "boolean")) {
421
+ context.addIssue({
422
+ code: "custom",
423
+ message: `Resource event match field "${key}" cannot use a boolean list.`,
424
+ path: [key]
425
+ });
426
+ }
427
+ }
428
+ });
429
+ function stableMatchValue(value) {
430
+ if (!Array.isArray(value)) return value;
431
+ return [...value].sort((left, right) => {
432
+ if (typeof left === "number" && typeof right === "number") {
433
+ return left - right;
434
+ }
435
+ return String(left).localeCompare(String(right));
436
+ });
437
+ }
438
+ function stableResourceEventMatchKey(match) {
439
+ if (!match || Object.keys(match).length === 0) return "";
440
+ return JSON.stringify(
441
+ Object.fromEntries(
442
+ Object.entries(match).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => [key, stableMatchValue(value)])
443
+ )
444
+ );
445
+ }
446
+ function resourceEventMatches(match, data) {
447
+ if (!match || Object.keys(match).length === 0) return true;
448
+ if (!data) return false;
449
+ for (const [key, expected] of Object.entries(match)) {
450
+ const actual = data[key];
451
+ if (actual === void 0) return false;
452
+ if (Array.isArray(expected)) {
453
+ if (!expected.some((value) => Object.is(value, actual))) return false;
454
+ continue;
455
+ }
456
+ if (!Object.is(expected, actual)) return false;
457
+ }
458
+ return true;
459
+ }
460
+ var pluginResourceEventTypeSchema = z6.object({
361
461
  type: resourceTypeSchema,
362
- supportedEvents: z5.array(resourceEventTypeSchema).min(1),
363
- suggestedEvents: z5.array(resourceEventTypeSchema).optional(),
364
- guidance: z5.record(
462
+ supportedEvents: z6.array(resourceEventTypeSchema).min(1),
463
+ suggestedEvents: z6.array(resourceEventTypeSchema).optional(),
464
+ matchFields: resourceEventMatchFieldsSchema.optional(),
465
+ guidance: z6.record(
365
466
  resourceEventTypeSchema,
366
- z5.string().trim().min(1).max(RESOURCE_EVENT_GUIDANCE_MAX_LENGTH)
467
+ z6.string().trim().min(1).max(RESOURCE_EVENT_GUIDANCE_MAX_LENGTH)
367
468
  ).optional()
368
469
  }).strict().superRefine((resourceType, context) => {
369
470
  const supported = /* @__PURE__ */ new Set();
@@ -405,10 +506,10 @@ var pluginResourceEventTypeSchema = z5.object({
405
506
  suggested.add(eventType);
406
507
  });
407
508
  });
408
- var pluginResourceEventsSchema = z5.object({
409
- resourceTypes: z5.array(pluginResourceEventTypeSchema).min(1),
410
- isEnabled: z5.function({ input: [], output: z5.boolean() }).optional(),
411
- normalizeIdentifier: z5.function({ input: [z5.string()], output: z5.string() }).optional()
509
+ var pluginResourceEventsSchema = z6.object({
510
+ resourceTypes: z6.array(pluginResourceEventTypeSchema).min(1),
511
+ isEnabled: z6.function({ input: [], output: z6.boolean() }).optional(),
512
+ normalizeIdentifier: z6.function({ input: [z6.string()], output: z6.string() }).optional()
412
513
  }).strict().superRefine((registration, context) => {
413
514
  const seen = /* @__PURE__ */ new Set();
414
515
  registration.resourceTypes.forEach((resourceType, index) => {
@@ -430,62 +531,62 @@ function normalizeResourceEventIdentifier(registration, identifier) {
430
531
  }
431
532
  return normalized;
432
533
  }
433
- var subscribableResourceSchema = z5.object({
434
- identifier: z5.string().min(1),
435
- label: z5.string().min(1),
436
- namespace: z5.string().min(1),
437
- suggestedEvents: z5.array(resourceEventTypeSchema).optional(),
438
- supportedEvents: z5.array(resourceEventTypeSchema),
534
+ var subscribableResourceSchema = z6.object({
535
+ identifier: z6.string().min(1),
536
+ label: z6.string().min(1),
537
+ namespace: z6.string().min(1),
538
+ suggestedEvents: z6.array(resourceEventTypeSchema).optional(),
539
+ supportedEvents: z6.array(resourceEventTypeSchema),
439
540
  type: resourceTypeSchema
440
541
  }).strict();
441
- var resourceEventSubscriptionResultSchema = z5.object({
442
- events: z5.array(resourceEventTypeSchema).min(1),
443
- id: z5.string().min(1)
542
+ var resourceEventSubscriptionResultSchema = z6.object({
543
+ events: z6.array(resourceEventTypeSchema).min(1),
544
+ id: z6.string().min(1)
444
545
  }).strict();
445
- var resourceEventInputSchema = z5.object({
446
- eventKey: z5.string().min(1),
546
+ var resourceEventInputSchema = z6.object({
547
+ eventKey: z6.string().min(1),
447
548
  eventType: resourceEventTypeSchema,
448
- identifier: z5.string().min(1),
449
- occurredAtMs: z5.number().finite(),
450
- terminal: z5.boolean().optional(),
451
- trustedSummary: z5.string().min(1).transform((value) => value.slice(0, RESOURCE_EVENT_SUMMARY_MAX_LENGTH)),
549
+ identifier: z6.string().min(1),
550
+ occurredAtMs: z6.number().finite(),
551
+ terminal: z6.boolean().optional(),
552
+ trustedSummary: z6.string().min(1).transform((value) => value.slice(0, RESOURCE_EVENT_SUMMARY_MAX_LENGTH)),
452
553
  /** Trusted structured facts. Prefer ids and urls over long prose. */
453
554
  data: resourceEventDataSchema.optional(),
454
- untrustedText: z5.string().transform((value) => value.slice(0, RESOURCE_EVENT_TEXT_MAX_LENGTH)).optional()
555
+ untrustedText: z6.string().transform((value) => value.slice(0, RESOURCE_EVENT_TEXT_MAX_LENGTH)).optional()
455
556
  }).strict();
456
557
  var resourceEventSchema = resourceEventInputSchema.extend({
457
- namespace: z5.string().min(1)
558
+ namespace: z6.string().min(1)
458
559
  });
459
560
 
460
561
  // src/tasks.ts
461
- import { z as z6 } from "zod";
462
- var pluginRunTranscriptProvenanceSchema = z6.object({
463
- authority: z6.enum(["instruction", "context"]),
562
+ import { z as z7 } from "zod";
563
+ var pluginRunTranscriptProvenanceSchema = z7.object({
564
+ authority: z7.enum(["instruction", "context"]),
464
565
  actor: actorSchema.optional()
465
566
  }).strict();
466
- var pluginRunTranscriptEntrySchema = z6.discriminatedUnion("type", [
467
- z6.object({
468
- type: z6.literal("message"),
469
- role: z6.enum(["user", "assistant"]),
470
- text: z6.string().min(1),
567
+ var pluginRunTranscriptEntrySchema = z7.discriminatedUnion("type", [
568
+ z7.object({
569
+ type: z7.literal("message"),
570
+ role: z7.enum(["user", "assistant"]),
571
+ text: z7.string().min(1),
471
572
  provenance: pluginRunTranscriptProvenanceSchema.optional(),
472
- isRunActor: z6.boolean().optional()
573
+ isRunActor: z7.boolean().optional()
473
574
  }).strict(),
474
- z6.object({
475
- type: z6.literal("toolResult"),
476
- toolName: z6.string().min(1),
477
- isError: z6.boolean(),
478
- text: z6.string().min(1).optional()
575
+ z7.object({
576
+ type: z7.literal("toolResult"),
577
+ toolName: z7.string().min(1),
578
+ isError: z7.boolean(),
579
+ text: z7.string().min(1).optional()
479
580
  }).strict()
480
581
  ]);
481
- var pluginRunContextSchema = z6.object({
582
+ var pluginRunContextSchema = z7.object({
482
583
  /** User linked to the Actor, when known. */
483
- actorUserId: z6.string().min(1).optional(),
484
- completedAtMs: z6.number().finite(),
485
- conversationId: z6.string().min(1),
584
+ actorUserId: z7.string().min(1).optional(),
585
+ completedAtMs: z7.number().finite(),
586
+ conversationId: z7.string().min(1),
486
587
  destination: destinationSchema,
487
588
  /** Location associated with this Conversation. */
488
- locationId: z6.string().min(1).optional(),
589
+ locationId: z7.string().min(1).optional(),
489
590
  /**
490
591
  * All distinct actors annotated on this run's committed instruction-authority
491
592
  * messages, in first-seen order. Attribution provenance only, never an
@@ -494,43 +595,43 @@ var pluginRunContextSchema = z6.object({
494
595
  * exceed the actors visible in the transcript slice. Usually `[run.actor]`;
495
596
  * possibly empty for system runs with no human instructions.
496
597
  */
497
- actors: z6.array(actorSchema),
598
+ actors: z7.array(actorSchema),
498
599
  /**
499
600
  * The single actor this run executes as. Absent only for actor-less legacy
500
601
  * system records, so authority-sensitive plugins must fail closed.
501
602
  */
502
603
  actor: actorSchema.optional(),
503
- runId: z6.string().min(1),
604
+ runId: z7.string().min(1),
504
605
  source: sourceSchema,
505
- transcript: z6.array(pluginRunTranscriptEntrySchema)
606
+ transcript: z7.array(pluginRunTranscriptEntrySchema)
506
607
  }).strict();
507
608
 
508
609
  // src/tools.ts
509
- import { z as z7 } from "zod";
610
+ import { z as z8 } from "zod";
510
611
  var PluginToolInputError = class extends Error {
511
612
  constructor(message, options) {
512
613
  super(message, options);
513
614
  this.name = "PluginToolInputError";
514
615
  }
515
616
  };
516
- var pluginToolContentSchema = z7.discriminatedUnion("type", [
517
- z7.object({ type: z7.literal("text"), text: z7.string() }).strict(),
518
- z7.object({
519
- type: z7.literal("image"),
520
- data: z7.string(),
521
- mimeType: z7.string()
617
+ var pluginToolContentSchema = z8.discriminatedUnion("type", [
618
+ z8.object({ type: z8.literal("text"), text: z8.string() }).strict(),
619
+ z8.object({
620
+ type: z8.literal("image"),
621
+ data: z8.string(),
622
+ mimeType: z8.string()
522
623
  }).strict()
523
624
  ]);
524
- var pluginToolContinuationSchema = z7.object({
525
- arguments: z7.record(z7.string(), z7.unknown()),
526
- reason: z7.string().min(1).optional()
625
+ var pluginToolContinuationSchema = z8.object({
626
+ arguments: z8.record(z8.string(), z8.unknown()),
627
+ reason: z8.string().min(1).optional()
527
628
  }).strict();
528
- var pluginToolOutputSchema = z7.object({
529
- target: z7.string().min(1).optional(),
530
- truncated: z7.boolean().optional(),
629
+ var pluginToolOutputSchema = z8.object({
630
+ target: z8.string().min(1).optional(),
631
+ truncated: z8.boolean().optional(),
531
632
  continuation: pluginToolContinuationSchema.optional()
532
633
  }).passthrough();
533
- var toolApprovalModeSchema = z7.enum(["auto", "review", "approve"]);
634
+ var toolApprovalModeSchema = z8.enum(["auto", "review", "approve"]);
534
635
  var REQUIRED_TOOL_ANNOTATION_KEYS = [
535
636
  "destructiveHint",
536
637
  "idempotentHint",
@@ -566,7 +667,7 @@ function createZodTool(definition, helperName) {
566
667
  let modelInputSchema;
567
668
  let modelOutputSchema;
568
669
  try {
569
- modelInputSchema = z7.toJSONSchema(inputSchema);
670
+ modelInputSchema = z8.toJSONSchema(inputSchema);
570
671
  } catch (error) {
571
672
  throw new TypeError(
572
673
  `${helperName}() inputSchema must be representable as JSON Schema.`,
@@ -574,7 +675,7 @@ function createZodTool(definition, helperName) {
574
675
  );
575
676
  }
576
677
  try {
577
- modelOutputSchema = z7.toJSONSchema(outputSchema);
678
+ modelOutputSchema = z8.toJSONSchema(outputSchema);
578
679
  } catch (error) {
579
680
  throw new TypeError(
580
681
  `${helperName}() outputSchema must be representable as JSON Schema.`,
@@ -600,7 +701,7 @@ function createZodTool(definition, helperName) {
600
701
  );
601
702
  if (isPluginToolOutputEnvelope(result)) {
602
703
  return {
603
- content: z7.array(pluginToolContentSchema).parse(result.content),
704
+ content: z8.array(pluginToolContentSchema).parse(result.content),
604
705
  details: outputSchema.parse(result.details)
605
706
  };
606
707
  }
@@ -617,76 +718,76 @@ function definePluginTool(definition) {
617
718
  }
618
719
 
619
720
  // src/operations.ts
620
- import { z as z8 } from "zod";
621
- var pluginApiRouteRequestContextSchema = z8.object({
622
- auth: z8.object({
623
- user: z8.object({
624
- email: z8.string().nullable().optional(),
625
- emailVerified: z8.boolean().optional(),
626
- name: z8.string().nullable().optional()
721
+ import { z as z9 } from "zod";
722
+ var pluginApiRouteRequestContextSchema = z9.object({
723
+ auth: z9.object({
724
+ user: z9.object({
725
+ email: z9.string().nullable().optional(),
726
+ emailVerified: z9.boolean().optional(),
727
+ name: z9.string().nullable().optional()
627
728
  }).strict()
628
729
  }).strict(),
629
730
  pluginName: nonBlankStringSchema
630
731
  }).strict();
631
732
 
632
733
  // src/credentials.ts
633
- import { z as z9 } from "zod";
634
- var pluginProviderNameSchema = z9.string().regex(/^[a-z][a-z0-9-]*$/);
635
- var pluginGrantNameSchema = z9.string().regex(/^[a-z][a-z0-9.-]*$/);
636
- var pluginGrantAccessSchema = z9.union([
637
- z9.literal("read"),
638
- z9.literal("write")
734
+ import { z as z10 } from "zod";
735
+ var pluginProviderNameSchema = z10.string().regex(/^[a-z][a-z0-9-]*$/);
736
+ var pluginGrantNameSchema = z10.string().regex(/^[a-z][a-z0-9.-]*$/);
737
+ var pluginGrantAccessSchema = z10.union([
738
+ z10.literal("read"),
739
+ z10.literal("write")
639
740
  ]);
640
- var pluginAuthorizationSchema = z9.object({
741
+ var pluginAuthorizationSchema = z10.object({
641
742
  provider: pluginProviderNameSchema,
642
743
  scope: nonBlankStringSchema.optional(),
643
- type: z9.literal("oauth")
744
+ type: z10.literal("oauth")
644
745
  }).strict();
645
- var pluginProviderAccountSchema = z9.object({
746
+ var pluginProviderAccountSchema = z10.object({
646
747
  displayName: nonBlankStringSchema.optional(),
647
748
  handle: nonBlankStringSchema.optional(),
648
749
  id: nonBlankStringSchema,
649
750
  label: nonBlankStringSchema.optional(),
650
751
  url: nonBlankStringSchema.optional()
651
752
  }).strict();
652
- var pluginStoredTokensSchema = z9.object({
753
+ var pluginStoredTokensSchema = z10.object({
653
754
  account: pluginProviderAccountSchema.optional(),
654
755
  accessToken: nonBlankStringSchema,
655
- expiresAt: z9.number().finite().optional(),
756
+ expiresAt: z10.number().finite().optional(),
656
757
  refreshToken: nonBlankStringSchema,
657
- refreshTokenExpiresAt: z9.number().finite().optional(),
758
+ refreshTokenExpiresAt: z10.number().finite().optional(),
658
759
  scope: nonBlankStringSchema.optional()
659
760
  }).strict();
660
- var pluginGrantSchema = z9.object({
761
+ var pluginGrantSchema = z10.object({
661
762
  access: pluginGrantAccessSchema,
662
763
  leaseScope: nonBlankStringSchema.optional(),
663
764
  name: pluginGrantNameSchema,
664
765
  reason: nonBlankStringSchema.optional(),
665
- requirements: z9.array(nonBlankStringSchema).min(1).optional()
766
+ requirements: z10.array(nonBlankStringSchema).min(1).optional()
666
767
  }).strict();
667
- var pluginCredentialHeaderTransformSchema = z9.object({
668
- domain: z9.string().min(1),
669
- headers: z9.record(z9.string(), z9.string()).refine((headers) => Object.keys(headers).length > 0)
768
+ var pluginCredentialHeaderTransformSchema = z10.object({
769
+ domain: z10.string().min(1),
770
+ headers: z10.record(z10.string(), z10.string()).refine((headers) => Object.keys(headers).length > 0)
670
771
  }).strict();
671
- var pluginCredentialLeaseSchema = z9.object({
772
+ var pluginCredentialLeaseSchema = z10.object({
672
773
  account: pluginProviderAccountSchema.optional(),
673
774
  authorization: pluginAuthorizationSchema.optional(),
674
- expiresAt: z9.string().refine((value) => Number.isFinite(Date.parse(value))),
675
- headerTransforms: z9.array(pluginCredentialHeaderTransformSchema).min(1)
775
+ expiresAt: z10.string().refine((value) => Number.isFinite(Date.parse(value))),
776
+ headerTransforms: z10.array(pluginCredentialHeaderTransformSchema).min(1)
676
777
  }).strict();
677
- var pluginCredentialResultSchema = z9.discriminatedUnion("type", [
678
- z9.object({
778
+ var pluginCredentialResultSchema = z10.discriminatedUnion("type", [
779
+ z10.object({
679
780
  lease: pluginCredentialLeaseSchema,
680
- type: z9.literal("lease")
781
+ type: z10.literal("lease")
681
782
  }).strict(),
682
- z9.object({
783
+ z10.object({
683
784
  authorization: pluginAuthorizationSchema.optional(),
684
785
  message: nonBlankStringSchema,
685
- type: z9.literal("needed")
786
+ type: z10.literal("needed")
686
787
  }).strict(),
687
- z9.object({
788
+ z10.object({
688
789
  message: nonBlankStringSchema,
689
- type: z9.literal("unavailable")
790
+ type: z10.literal("unavailable")
690
791
  }).strict()
691
792
  ]);
692
793
  var EgressAuthRequired = class extends Error {
@@ -822,44 +923,44 @@ function defineJuniorPlugin(plugin) {
822
923
  }
823
924
 
824
925
  // src/user-pages.ts
825
- import { z as z10 } from "zod";
926
+ import { z as z11 } from "zod";
826
927
  var userPageIdSchema = nonBlankStringSchema.max(64).regex(/^[a-z][a-z0-9-]*$/);
827
928
  var userPageLabelSchema = nonBlankStringSchema.max(80);
828
929
  var userPageDescriptionSchema = nonBlankStringSchema.max(500);
829
- var userPageNavigationSchema = z10.enum(["primary", "profile"]);
830
- var pluginUserPageMetricSchema = z10.object({
930
+ var userPageNavigationSchema = z11.enum(["primary", "profile"]);
931
+ var pluginUserPageMetricSchema = z11.object({
831
932
  detail: nonBlankStringSchema.max(500).optional(),
832
933
  label: nonBlankStringSchema.max(80),
833
- tone: z10.enum(["good", "neutral", "warning"]).optional(),
934
+ tone: z11.enum(["good", "neutral", "warning"]).optional(),
834
935
  value: nonBlankStringSchema.max(120)
835
936
  }).strict();
836
- var pluginUserPageMetadataSchema = z10.object({
937
+ var pluginUserPageMetadataSchema = z11.object({
837
938
  label: nonBlankStringSchema.max(80),
838
939
  value: nonBlankStringSchema.max(500)
839
940
  }).strict();
840
- var pluginUserPageActionSchema = z10.object({
941
+ var pluginUserPageActionSchema = z11.object({
841
942
  confirmation: nonBlankStringSchema.max(500).optional(),
842
943
  href: nonBlankStringSchema.max(500).regex(/^\/api\/plugins\/[a-z][a-z0-9-]*(?:\/|$)/),
843
944
  label: nonBlankStringSchema.max(80),
844
- method: z10.literal("DELETE"),
845
- tone: z10.enum(["danger", "neutral"]).optional()
945
+ method: z11.literal("DELETE"),
946
+ tone: z11.enum(["danger", "neutral"]).optional()
846
947
  }).strict();
847
- var pluginUserPageRecordSchema = z10.object({
848
- actions: z10.array(pluginUserPageActionSchema).max(4).optional(),
948
+ var pluginUserPageRecordSchema = z11.object({
949
+ actions: z11.array(pluginUserPageActionSchema).max(4).optional(),
849
950
  description: nonBlankStringSchema.max(1e3).optional(),
850
951
  id: nonBlankStringSchema.max(128),
851
- metadata: z10.array(pluginUserPageMetadataSchema).max(8).optional(),
952
+ metadata: z11.array(pluginUserPageMetadataSchema).max(8).optional(),
852
953
  title: nonBlankStringSchema.max(4e3)
853
954
  }).strict();
854
- var pluginUserPageContentSchema = z10.object({
955
+ var pluginUserPageContentSchema = z11.object({
855
956
  emptyText: nonBlankStringSchema.max(500).optional(),
856
- metrics: z10.array(pluginUserPageMetricSchema).max(6).optional(),
957
+ metrics: z11.array(pluginUserPageMetricSchema).max(6).optional(),
857
958
  nextCursor: nonBlankStringSchema.max(1e3).optional(),
858
- records: z10.array(pluginUserPageRecordSchema).max(100),
959
+ records: z11.array(pluginUserPageRecordSchema).max(100),
859
960
  searchPlaceholder: nonBlankStringSchema.max(120).optional(),
860
- type: z10.literal("list")
961
+ type: z11.literal("list")
861
962
  }).strict();
862
- var pluginUserPageLinkSchema = z10.object({
963
+ var pluginUserPageLinkSchema = z11.object({
863
964
  description: userPageDescriptionSchema,
864
965
  id: userPageIdSchema,
865
966
  label: userPageLabelSchema,
@@ -867,14 +968,14 @@ var pluginUserPageLinkSchema = z10.object({
867
968
  pluginDisplayName: nonBlankStringSchema.max(200),
868
969
  pluginName: nonBlankStringSchema.max(100)
869
970
  }).strict();
870
- var pluginUserPageLinksSchema = z10.array(pluginUserPageLinkSchema);
971
+ var pluginUserPageLinksSchema = z11.array(pluginUserPageLinkSchema);
871
972
  var pluginUserPageCursorSchema = nonBlankStringSchema.max(1e3).optional();
872
973
  var pluginUserPageFilterSchema = nonBlankStringSchema.max(64).optional();
873
- var pluginUserPageQuerySchema = z10.string().trim().max(200).optional();
874
- var pluginUserPageInputSchema = z10.object({
974
+ var pluginUserPageQuerySchema = z11.string().trim().max(200).optional();
975
+ var pluginUserPageInputSchema = z11.object({
875
976
  cursor: pluginUserPageCursorSchema,
876
977
  filter: pluginUserPageFilterSchema,
877
- limit: z10.number().int().min(1).max(50),
978
+ limit: z11.number().int().min(1).max(50),
878
979
  query: pluginUserPageQuerySchema
879
980
  }).strict();
880
981
  export {
@@ -889,6 +990,8 @@ export {
889
990
  RESOURCE_EVENT_TEXT_MAX_LENGTH,
890
991
  actorSchema,
891
992
  actorUserIdSchema,
993
+ codeChangeInputSchema,
994
+ codeChangeStateSchema,
892
995
  conversationAnnotationInputSchema,
893
996
  conversationEventIconSchema,
894
997
  conversationEventPresentationSchema,
@@ -945,6 +1048,10 @@ export {
945
1048
  replyAttributionSchema,
946
1049
  resourceEventDataSchema,
947
1050
  resourceEventInputSchema,
1051
+ resourceEventMatchFieldSchema,
1052
+ resourceEventMatchFieldsSchema,
1053
+ resourceEventMatchSchema,
1054
+ resourceEventMatches,
948
1055
  resourceEventSchema,
949
1056
  resourceEventSubscriptionResultSchema,
950
1057
  resourceEventTypeSchema,
@@ -955,6 +1062,7 @@ export {
955
1062
  slackSourceSchema,
956
1063
  sourceSchema,
957
1064
  sourceVisibilitySchema,
1065
+ stableResourceEventMatchKey,
958
1066
  subscribableResourceSchema,
959
1067
  systemActorSchema,
960
1068
  toolApprovalModeSchema,
@@ -5,6 +5,7 @@ import type { PluginReadState, PluginState } from "./state";
5
5
  import type { ResourceEventPublisher } from "./resource-events";
6
6
  import type { PluginConversationAnnotations } from "./annotations";
7
7
  import type { PluginConversationEventStats } from "./conversation-events";
8
+ import type { CodeChangePublisher } from "./code";
8
9
  export interface HeartbeatHookContext extends PluginContext {
9
10
  agent: {
10
11
  dispatch(options: DispatchOptions): Promise<DispatchResult>;
@@ -120,6 +121,8 @@ export type PluginRouteApp = {
120
121
  };
121
122
  export interface RouteRegistrationHookContext extends PluginContext {
122
123
  annotations: PluginConversationAnnotations;
124
+ /** Provider-neutral write boundary for Junior's native code index. */
125
+ codeChanges: CodeChangePublisher;
123
126
  /** Core-owned delivery boundary for provider webhook events. */
124
127
  resourceEvents: ResourceEventPublisher;
125
128
  }
@@ -4,15 +4,51 @@ export declare const RESOURCE_EVENT_TEXT_MAX_LENGTH = 8000;
4
4
  export declare const RESOURCE_EVENT_DATA_MAX_KEYS = 32;
5
5
  export declare const RESOURCE_EVENT_DATA_MAX_JSON_BYTES = 4000;
6
6
  export declare const RESOURCE_EVENT_GUIDANCE_MAX_LENGTH = 1000;
7
- /** Small trusted facts from the plugin. The agent should not look these up again. */
7
+ /** Small trusted data from the plugin. The agent should not look these up again. */
8
8
  export declare const resourceEventDataSchema: z.ZodRecord<z.ZodString, z.ZodUnknown>;
9
9
  /** Canonical dotted event type published and selected across plugins. */
10
10
  export declare const resourceEventTypeSchema: z.ZodString;
11
11
  export declare const resourceTypeSchema: z.ZodString;
12
+ /** One exact value a watch or event task may require on trusted event data. */
13
+ export declare const resourceEventMatchFieldSchema: z.ZodObject<{
14
+ kind: z.ZodEnum<{
15
+ string: "string";
16
+ number: "number";
17
+ boolean: "boolean";
18
+ }>;
19
+ description: z.ZodString;
20
+ enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
21
+ }, z.core.$strict>;
22
+ export declare const resourceEventMatchFieldsSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
23
+ kind: z.ZodEnum<{
24
+ string: "string";
25
+ number: "number";
26
+ boolean: "boolean";
27
+ }>;
28
+ description: z.ZodString;
29
+ enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
30
+ }, z.core.$strict>>;
31
+ /** Exact trusted values required before a watch or event task runs. */
32
+ export declare const resourceEventMatchSchema: z.ZodRecord<z.ZodString, z.ZodUnion<readonly [z.ZodBoolean, z.ZodNumber, z.ZodString, z.ZodArray<z.ZodUnion<readonly [z.ZodNumber, z.ZodString]>>]>>;
33
+ export type ResourceEventMatch = z.output<typeof resourceEventMatchSchema>;
34
+ export type ResourceEventMatchFields = z.output<typeof resourceEventMatchFieldsSchema>;
35
+ /** Stable JSON for one match object. List order does not matter. */
36
+ export declare function stableResourceEventMatchKey(match: ResourceEventMatch | undefined): string;
37
+ /** Return whether trusted event data matches one exact match object. */
38
+ export declare function resourceEventMatches(match: ResourceEventMatch | undefined, data: ResourceEventData | undefined): boolean;
12
39
  export declare const pluginResourceEventTypeSchema: z.ZodObject<{
13
40
  type: z.ZodString;
14
41
  supportedEvents: z.ZodArray<z.ZodString>;
15
42
  suggestedEvents: z.ZodOptional<z.ZodArray<z.ZodString>>;
43
+ matchFields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
44
+ kind: z.ZodEnum<{
45
+ string: "string";
46
+ number: "number";
47
+ boolean: "boolean";
48
+ }>;
49
+ description: z.ZodString;
50
+ enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
51
+ }, z.core.$strict>>>;
16
52
  guidance: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
17
53
  }, z.core.$strict>;
18
54
  export declare const pluginResourceEventsSchema: z.ZodObject<{
@@ -20,6 +56,15 @@ export declare const pluginResourceEventsSchema: z.ZodObject<{
20
56
  type: z.ZodString;
21
57
  supportedEvents: z.ZodArray<z.ZodString>;
22
58
  suggestedEvents: z.ZodOptional<z.ZodArray<z.ZodString>>;
59
+ matchFields: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
60
+ kind: z.ZodEnum<{
61
+ string: "string";
62
+ number: "number";
63
+ boolean: "boolean";
64
+ }>;
65
+ description: z.ZodString;
66
+ enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
67
+ }, z.core.$strict>>>;
23
68
  guidance: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
24
69
  }, z.core.$strict>>;
25
70
  isEnabled: z.ZodOptional<z.ZodFunction<z.ZodTuple<readonly [], null>, z.ZodBoolean>>;
@@ -71,4 +116,13 @@ export type ResourceEvent = z.output<typeof resourceEventSchema>;
71
116
  export interface ResourceEventPublisher {
72
117
  /** Publish one normalized event under the owning plugin's namespace. */
73
118
  publish(event: ResourceEventInput): Promise<void>;
119
+ /**
120
+ * Return match keys used by active watches or event tasks for these
121
+ * identifiers and event types. Plugins use this to load optional trusted
122
+ * data only when a filter needs it.
123
+ */
124
+ neededMatchKeys?(input: {
125
+ eventTypes: string[];
126
+ identifiers: string[];
127
+ }): Promise<string[]>;
74
128
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior-plugin-api",
3
- "version": "0.181.1",
3
+ "version": "0.183.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
package/src/code.ts ADDED
@@ -0,0 +1,37 @@
1
+ import { z } from "zod";
2
+
3
+ export const codeChangeStateSchema = z.enum(["closed", "merged", "open"]);
4
+ export type CodeChangeState = z.output<typeof codeChangeStateSchema>;
5
+
6
+ export const codeChangeInputSchema = z
7
+ .object({
8
+ closedAt: z.date().optional(),
9
+ conversationIds: z.array(z.string().min(1)).default([]),
10
+ mergedAt: z.date().optional(),
11
+ number: z.number().int().positive(),
12
+ openedAt: z.date(),
13
+ providerId: z.string().min(1),
14
+ repository: z
15
+ .object({
16
+ name: z.string().min(1),
17
+ providerId: z.string().min(1),
18
+ url: z.string().url().optional(),
19
+ })
20
+ .strict(),
21
+ state: codeChangeStateSchema,
22
+ title: z.string().min(1).optional(),
23
+ updatedAt: z.date(),
24
+ url: z.string().url().optional(),
25
+ })
26
+ .strict();
27
+
28
+ export type CodeChangeInput = z.input<typeof codeChangeInputSchema>;
29
+
30
+ /** Write code changes from a plugin to Junior's code records. */
31
+ export interface CodeChangePublisher {
32
+ associateConversations(input: {
33
+ conversationIds: string[];
34
+ providerId: string;
35
+ }): Promise<void>;
36
+ record(input: CodeChangeInput): Promise<void>;
37
+ }
package/src/index.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export * from "./annotations";
2
+ export * from "./code";
2
3
  export * from "./conversation-events";
3
4
  export * from "./schemas";
4
5
  export * from "./context";
package/src/operations.ts CHANGED
@@ -6,6 +6,7 @@ import type { PluginReadState, PluginState } from "./state";
6
6
  import type { ResourceEventPublisher } from "./resource-events";
7
7
  import type { PluginConversationAnnotations } from "./annotations";
8
8
  import type { PluginConversationEventStats } from "./conversation-events";
9
+ import type { CodeChangePublisher } from "./code";
9
10
 
10
11
  export interface HeartbeatHookContext extends PluginContext {
11
12
  agent: {
@@ -153,6 +154,8 @@ export type PluginRouteApp = {
153
154
 
154
155
  export interface RouteRegistrationHookContext extends PluginContext {
155
156
  annotations: PluginConversationAnnotations;
157
+ /** Provider-neutral write boundary for Junior's native code index. */
158
+ codeChanges: CodeChangePublisher;
156
159
  /** Core-owned delivery boundary for provider webhook events. */
157
160
  resourceEvents: ResourceEventPublisher;
158
161
  }
@@ -6,7 +6,7 @@ export const RESOURCE_EVENT_DATA_MAX_KEYS = 32;
6
6
  export const RESOURCE_EVENT_DATA_MAX_JSON_BYTES = 4_000;
7
7
  export const RESOURCE_EVENT_GUIDANCE_MAX_LENGTH = 1_000;
8
8
 
9
- /** Small trusted facts from the plugin. The agent should not look these up again. */
9
+ /** Small trusted data from the plugin. The agent should not look these up again. */
10
10
  export const resourceEventDataSchema = z
11
11
  .record(z.string(), z.unknown())
12
12
  .superRefine((value, context) => {
@@ -43,11 +43,122 @@ export const resourceTypeSchema = z
43
43
  "Canonical plugin-defined resource type, such as issue or pull_request.",
44
44
  );
45
45
 
46
+ const RESOURCE_EVENT_MATCH_FIELD_NAME =
47
+ /^[a-z][a-zA-Z0-9]*$/;
48
+
49
+ /** One exact value a watch or event task may require on trusted event data. */
50
+ export const resourceEventMatchFieldSchema = z
51
+ .object({
52
+ kind: z.enum(["boolean", "string", "number"]),
53
+ description: z.string().trim().min(1).max(200),
54
+ enum: z.array(z.string().min(1)).min(1).optional(),
55
+ })
56
+ .strict()
57
+ .superRefine((field, context) => {
58
+ if (field.enum && field.kind !== "string") {
59
+ context.addIssue({
60
+ code: "custom",
61
+ message: 'Resource event match field enum requires kind "string".',
62
+ path: ["enum"],
63
+ });
64
+ }
65
+ });
66
+
67
+ export const resourceEventMatchFieldsSchema = z
68
+ .record(z.string().regex(RESOURCE_EVENT_MATCH_FIELD_NAME), resourceEventMatchFieldSchema)
69
+ .superRefine((fields, context) => {
70
+ if (Object.keys(fields).length > RESOURCE_EVENT_DATA_MAX_KEYS) {
71
+ context.addIssue({
72
+ code: "custom",
73
+ message: `Resource event match keys may include at most ${RESOURCE_EVENT_DATA_MAX_KEYS} keys.`,
74
+ });
75
+ }
76
+ });
77
+
78
+ /** Exact trusted values required before a watch or event task runs. */
79
+ export const resourceEventMatchSchema = z
80
+ .record(
81
+ z.string().regex(RESOURCE_EVENT_MATCH_FIELD_NAME),
82
+ z.union([
83
+ z.boolean(),
84
+ z.number().finite(),
85
+ z.string().min(1),
86
+ z.array(z.union([z.number().finite(), z.string().min(1)])).min(1),
87
+ ]),
88
+ )
89
+ .superRefine((match, context) => {
90
+ if (Object.keys(match).length > RESOURCE_EVENT_DATA_MAX_KEYS) {
91
+ context.addIssue({
92
+ code: "custom",
93
+ message: `Resource event match may include at most ${RESOURCE_EVENT_DATA_MAX_KEYS} keys.`,
94
+ });
95
+ }
96
+ for (const [key, value] of Object.entries(match)) {
97
+ if (!Array.isArray(value)) continue;
98
+ if (value.some((entry) => typeof entry === "boolean")) {
99
+ context.addIssue({
100
+ code: "custom",
101
+ message: `Resource event match field "${key}" cannot use a boolean list.`,
102
+ path: [key],
103
+ });
104
+ }
105
+ }
106
+ });
107
+
108
+ export type ResourceEventMatch = z.output<typeof resourceEventMatchSchema>;
109
+ export type ResourceEventMatchFields = z.output<
110
+ typeof resourceEventMatchFieldsSchema
111
+ >;
112
+
113
+ function stableMatchValue(value: ResourceEventMatch[string]): unknown {
114
+ if (!Array.isArray(value)) return value;
115
+ return [...value].sort((left, right) => {
116
+ if (typeof left === "number" && typeof right === "number") {
117
+ return left - right;
118
+ }
119
+ return String(left).localeCompare(String(right));
120
+ });
121
+ }
122
+
123
+ /** Stable JSON for one match object. List order does not matter. */
124
+ export function stableResourceEventMatchKey(
125
+ match: ResourceEventMatch | undefined,
126
+ ): string {
127
+ if (!match || Object.keys(match).length === 0) return "";
128
+ return JSON.stringify(
129
+ Object.fromEntries(
130
+ Object.entries(match)
131
+ .sort(([left], [right]) => left.localeCompare(right))
132
+ .map(([key, value]) => [key, stableMatchValue(value)]),
133
+ ),
134
+ );
135
+ }
136
+
137
+ /** Return whether trusted event data matches one exact match object. */
138
+ export function resourceEventMatches(
139
+ match: ResourceEventMatch | undefined,
140
+ data: ResourceEventData | undefined,
141
+ ): boolean {
142
+ if (!match || Object.keys(match).length === 0) return true;
143
+ if (!data) return false;
144
+ for (const [key, expected] of Object.entries(match)) {
145
+ const actual = data[key];
146
+ if (actual === undefined) return false;
147
+ if (Array.isArray(expected)) {
148
+ if (!expected.some((value) => Object.is(value, actual))) return false;
149
+ continue;
150
+ }
151
+ if (!Object.is(expected, actual)) return false;
152
+ }
153
+ return true;
154
+ }
155
+
46
156
  export const pluginResourceEventTypeSchema = z
47
157
  .object({
48
158
  type: resourceTypeSchema,
49
159
  supportedEvents: z.array(resourceEventTypeSchema).min(1),
50
160
  suggestedEvents: z.array(resourceEventTypeSchema).optional(),
161
+ matchFields: resourceEventMatchFieldsSchema.optional(),
51
162
  guidance: z
52
163
  .record(
53
164
  resourceEventTypeSchema,
@@ -197,4 +308,13 @@ export type ResourceEvent = z.output<typeof resourceEventSchema>;
197
308
  export interface ResourceEventPublisher {
198
309
  /** Publish one normalized event under the owning plugin's namespace. */
199
310
  publish(event: ResourceEventInput): Promise<void>;
311
+ /**
312
+ * Return match keys used by active watches or event tasks for these
313
+ * identifiers and event types. Plugins use this to load optional trusted
314
+ * data only when a filter needs it.
315
+ */
316
+ neededMatchKeys?(input: {
317
+ eventTypes: string[];
318
+ identifiers: string[];
319
+ }): Promise<string[]>;
200
320
  }