@sentry/junior-plugin-api 0.266.0 → 0.268.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +121 -11
- package/dist/index.js +200 -26
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1072,6 +1072,10 @@ export declare const eventMatchFieldSchema: z.ZodObject<{
|
|
|
1072
1072
|
description: z.ZodString;
|
|
1073
1073
|
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1074
1074
|
list: z.ZodOptional<z.ZodLiteral<true>>;
|
|
1075
|
+
case: z.ZodOptional<z.ZodEnum<{
|
|
1076
|
+
lower: "lower";
|
|
1077
|
+
upper: "upper";
|
|
1078
|
+
}>>;
|
|
1075
1079
|
events: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1076
1080
|
}, z.core.$strict>;
|
|
1077
1081
|
export declare const eventMatchFieldsSchema: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
@@ -1083,6 +1087,10 @@ export declare const eventMatchFieldsSchema: z.ZodRecord<z.ZodString, z.ZodObjec
|
|
|
1083
1087
|
description: z.ZodString;
|
|
1084
1088
|
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1085
1089
|
list: z.ZodOptional<z.ZodLiteral<true>>;
|
|
1090
|
+
case: z.ZodOptional<z.ZodEnum<{
|
|
1091
|
+
lower: "lower";
|
|
1092
|
+
upper: "upper";
|
|
1093
|
+
}>>;
|
|
1086
1094
|
events: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1087
1095
|
}, z.core.$strict>>;
|
|
1088
1096
|
/** Exact trusted values required before a watch or event automation runs. */
|
|
@@ -1098,6 +1106,15 @@ export type EventMatchFields = z.output<typeof eventMatchFieldsSchema>;
|
|
|
1098
1106
|
* different values.
|
|
1099
1107
|
*/
|
|
1100
1108
|
export declare function mergeEventMatchFields(...maps: readonly EventMatchFields[]): EventMatchFields;
|
|
1109
|
+
/**
|
|
1110
|
+
* Merge the match fields of every resource type of one source, for a trigger
|
|
1111
|
+
* with no identifier. Such a trigger can get the events of every resource
|
|
1112
|
+
* type. A field with no `events` list is set on every event of its own
|
|
1113
|
+
* resource type, so it gets that list before the merge. This throws like
|
|
1114
|
+
* `mergeEventMatchFields` when two resource types describe one field
|
|
1115
|
+
* differently.
|
|
1116
|
+
*/
|
|
1117
|
+
export declare function anyResourceMatchFields(resourceTypes: readonly Pick<PluginEventType, "matchFields" | "supportedEvents">[]): EventMatchFields;
|
|
1101
1118
|
/** Stable JSON for one match object. List order does not matter. */
|
|
1102
1119
|
export declare function stableEventMatchKey(match: EventMatch | undefined): string;
|
|
1103
1120
|
/**
|
|
@@ -1107,6 +1124,12 @@ export declare function stableEventMatchKey(match: EventMatch | undefined): stri
|
|
|
1107
1124
|
* match value.
|
|
1108
1125
|
*/
|
|
1109
1126
|
export declare function eventMatches(match: EventMatch | undefined, data: EventData | undefined): boolean;
|
|
1127
|
+
/**
|
|
1128
|
+
* Return a match with each text value in the case that its field declares,
|
|
1129
|
+
* so a saved filter has the same case as event data. Values of other fields
|
|
1130
|
+
* do not change.
|
|
1131
|
+
*/
|
|
1132
|
+
export declare function normalizeEventMatch(fields: EventMatchFields, match: EventMatch | undefined): EventMatch | undefined;
|
|
1110
1133
|
/** The identifier shape of one resource type. Plain JSON, so API responses can carry it. */
|
|
1111
1134
|
export declare const eventIdentifierSchema: z.ZodObject<{
|
|
1112
1135
|
format: z.ZodString;
|
|
@@ -1129,10 +1152,38 @@ export declare const pluginEventTypeSchema: z.ZodObject<{
|
|
|
1129
1152
|
description: z.ZodString;
|
|
1130
1153
|
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1131
1154
|
list: z.ZodOptional<z.ZodLiteral<true>>;
|
|
1155
|
+
case: z.ZodOptional<z.ZodEnum<{
|
|
1156
|
+
lower: "lower";
|
|
1157
|
+
upper: "upper";
|
|
1158
|
+
}>>;
|
|
1132
1159
|
events: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1133
1160
|
}, z.core.$strict>>>;
|
|
1134
|
-
guidance: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodString>>;
|
|
1135
1161
|
}, z.core.$strict>;
|
|
1162
|
+
/**
|
|
1163
|
+
* The event reach of one event type: which event automation triggers can
|
|
1164
|
+
* select it.
|
|
1165
|
+
*
|
|
1166
|
+
* - `"all"`: a trigger can run for every resource of the source, with no
|
|
1167
|
+
* identifier and no filter. Use it only for events that carry status facts
|
|
1168
|
+
* and cause no side effects.
|
|
1169
|
+
* - `{ anyOf }`: a trigger with no identifier must set at least one of these
|
|
1170
|
+
* filters. Use it when an event carries private text, such as titles or
|
|
1171
|
+
* comments, or makes Junior act, such as add a reaction.
|
|
1172
|
+
* - `"resource"`: only a trigger for one resource, with an identifier, can
|
|
1173
|
+
* select the event.
|
|
1174
|
+
*/
|
|
1175
|
+
export declare const eventReachSchema: z.ZodUnion<readonly [z.ZodLiteral<"all">, z.ZodLiteral<"resource">, z.ZodObject<{
|
|
1176
|
+
anyOf: z.ZodArray<z.ZodString>;
|
|
1177
|
+
}, z.core.$strict>]>;
|
|
1178
|
+
export type EventReach = z.output<typeof eventReachSchema>;
|
|
1179
|
+
/** What a source declares once for one of its event types. */
|
|
1180
|
+
export declare const pluginEventDeclarationSchema: z.ZodObject<{
|
|
1181
|
+
reach: z.ZodUnion<readonly [z.ZodLiteral<"all">, z.ZodLiteral<"resource">, z.ZodObject<{
|
|
1182
|
+
anyOf: z.ZodArray<z.ZodString>;
|
|
1183
|
+
}, z.core.$strict>]>;
|
|
1184
|
+
guidance: z.ZodOptional<z.ZodString>;
|
|
1185
|
+
}, z.core.$strict>;
|
|
1186
|
+
export type PluginEventDeclaration = z.output<typeof pluginEventDeclarationSchema>;
|
|
1136
1187
|
export declare const pluginEventsSchema: z.ZodObject<{
|
|
1137
1188
|
resourceTypes: z.ZodArray<z.ZodObject<{
|
|
1138
1189
|
type: z.ZodString;
|
|
@@ -1151,9 +1202,18 @@ export declare const pluginEventsSchema: z.ZodObject<{
|
|
|
1151
1202
|
description: z.ZodString;
|
|
1152
1203
|
enum: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1153
1204
|
list: z.ZodOptional<z.ZodLiteral<true>>;
|
|
1205
|
+
case: z.ZodOptional<z.ZodEnum<{
|
|
1206
|
+
lower: "lower";
|
|
1207
|
+
upper: "upper";
|
|
1208
|
+
}>>;
|
|
1154
1209
|
events: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
1155
1210
|
}, z.core.$strict>>>;
|
|
1156
|
-
|
|
1211
|
+
}, z.core.$strict>>;
|
|
1212
|
+
events: z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
1213
|
+
reach: z.ZodUnion<readonly [z.ZodLiteral<"all">, z.ZodLiteral<"resource">, z.ZodObject<{
|
|
1214
|
+
anyOf: z.ZodArray<z.ZodString>;
|
|
1215
|
+
}, z.core.$strict>]>;
|
|
1216
|
+
guidance: z.ZodOptional<z.ZodString>;
|
|
1157
1217
|
}, z.core.$strict>>;
|
|
1158
1218
|
isEnabled: z.ZodOptional<z.ZodFunction<z.ZodTuple<readonly [], null>, z.ZodBoolean>>;
|
|
1159
1219
|
normalizeIdentifier: z.ZodOptional<z.ZodFunction<z.ZodTuple<readonly [z.ZodString], null>, z.ZodString>>;
|
|
@@ -1259,10 +1319,17 @@ export interface EventPublisher {
|
|
|
1259
1319
|
publish(event: EventInput): Promise<void>;
|
|
1260
1320
|
/**
|
|
1261
1321
|
* Return match keys used by active watches or event automations for these
|
|
1262
|
-
* identifiers and event types
|
|
1263
|
-
* data only when a
|
|
1322
|
+
* identifiers and event types, and by event automations with no
|
|
1323
|
+
* identifier. Plugins use this to load optional trusted data only when a
|
|
1324
|
+
* filter needs it.
|
|
1325
|
+
*
|
|
1326
|
+
* Pass `data` with values that every copy of the events sets and that the
|
|
1327
|
+
* plugin already has, such as `repo`. An event automation with no
|
|
1328
|
+
* identifier whose filters on those keys fail adds no keys, so a trigger
|
|
1329
|
+
* for one repository does not load data for events of other repositories.
|
|
1264
1330
|
*/
|
|
1265
1331
|
neededMatchKeys?(input: {
|
|
1332
|
+
data?: EventData;
|
|
1266
1333
|
eventTypes: string[];
|
|
1267
1334
|
identifiers: string[];
|
|
1268
1335
|
}): Promise<string[]>;
|
|
@@ -1273,34 +1340,70 @@ export interface EventTriggerProblem {
|
|
|
1273
1340
|
/**
|
|
1274
1341
|
* - `unknown_resource_type`: the source has no such enabled resource type.
|
|
1275
1342
|
* - `unknown_event`: the resource type has no such event.
|
|
1343
|
+
* - `identifier_required`: a trigger with no identifier selects an event
|
|
1344
|
+
* that cannot run for every resource.
|
|
1345
|
+
* - `filter_required`: a trigger with no identifier selects an event whose
|
|
1346
|
+
* reach names filters, and the trigger sets none of them.
|
|
1276
1347
|
* - `unknown_field`: the resource type has no such filter. Saved triggers
|
|
1277
1348
|
* get this after a plugin renames or removes a filter.
|
|
1278
1349
|
* - `field_not_set`: a selected event never sets the filter, so the filter
|
|
1279
1350
|
* never matches that event.
|
|
1280
1351
|
* - `invalid_value`: the filter value has the wrong type or is not allowed.
|
|
1281
1352
|
*/
|
|
1282
|
-
reason: "unknown_resource_type" | "unknown_event" | "unknown_field" | "field_not_set" | "invalid_value";
|
|
1353
|
+
reason: "unknown_resource_type" | "unknown_event" | "identifier_required" | "filter_required" | "unknown_field" | "field_not_set" | "invalid_value";
|
|
1283
1354
|
/** The filter with the problem. Missing for a resource type or event problem. */
|
|
1284
1355
|
field?: string;
|
|
1285
1356
|
/** Plain words for the person or agent who changes the selection. */
|
|
1286
1357
|
message: string;
|
|
1287
1358
|
}
|
|
1288
|
-
/**
|
|
1359
|
+
/**
|
|
1360
|
+
* The selection to check: one resource type, its events, and its filters.
|
|
1361
|
+
* Leave out the resource type for a trigger with no identifier. It runs for
|
|
1362
|
+
* every resource of the source that its filters match.
|
|
1363
|
+
*/
|
|
1289
1364
|
export interface EventTriggerSelection {
|
|
1290
|
-
resourceType
|
|
1365
|
+
resourceType?: string;
|
|
1291
1366
|
events: readonly string[];
|
|
1292
1367
|
match?: EventMatch;
|
|
1293
1368
|
}
|
|
1369
|
+
/** The enabled resource types of one source, and the reach of each of its events. */
|
|
1370
|
+
export interface EventTriggerSource {
|
|
1371
|
+
resourceTypes: readonly Pick<PluginEventType, "type" | "matchFields" | "supportedEvents">[];
|
|
1372
|
+
events: Readonly<Record<string, {
|
|
1373
|
+
reach: EventReach;
|
|
1374
|
+
}>>;
|
|
1375
|
+
/**
|
|
1376
|
+
* The filters of every resource type, for a trigger with no identifier.
|
|
1377
|
+
* Merge them once with `anyResourceMatchFields` when the catalog is built,
|
|
1378
|
+
* not for each check.
|
|
1379
|
+
*/
|
|
1380
|
+
anyResourceFields: EventMatchFields;
|
|
1381
|
+
}
|
|
1382
|
+
/** The events of a source that a trigger with no identifier can select. */
|
|
1383
|
+
export declare function anyResourceEvents(source: EventTriggerSource): string[];
|
|
1294
1384
|
/** Return the selected events that never set one filter field. */
|
|
1295
1385
|
export declare function eventsWithoutMatchField(field: Pick<EventMatchField, "events">, events: readonly string[]): string[];
|
|
1386
|
+
/**
|
|
1387
|
+
* Return the events and filters that a selection can use: those of its
|
|
1388
|
+
* resource type, or with no resource type, the events whose reach is not
|
|
1389
|
+
* `"resource"`, with the filters of every resource type. Return nothing when the source
|
|
1390
|
+
* has no such resource type or cannot run for every resource.
|
|
1391
|
+
*/
|
|
1392
|
+
export declare function eventTriggerChoices(source: EventTriggerSource, selection: Pick<EventTriggerSelection, "resourceType">): {
|
|
1393
|
+
events: readonly string[];
|
|
1394
|
+
fields: EventMatchFields;
|
|
1395
|
+
} | undefined;
|
|
1296
1396
|
/**
|
|
1297
1397
|
* List why a selection cannot work with the resource types of one source. An
|
|
1298
1398
|
* empty list means the resource type exists, every selected event is
|
|
1299
1399
|
* supported, and every selected event can match every filter. A filter on a
|
|
1300
1400
|
* list field (`list: true`) matches when the event list includes the value.
|
|
1401
|
+
* A selection with no resource type can use only events whose reach is not
|
|
1402
|
+
* `"resource"`, with the filters of every resource type, and must set one of
|
|
1403
|
+
* the filters that the reach of each selected event names.
|
|
1301
1404
|
* Set `inputKeys` for the agent, so messages name the `match.<key>` input.
|
|
1302
1405
|
*/
|
|
1303
|
-
export declare function eventTriggerProblems(
|
|
1406
|
+
export declare function eventTriggerProblems(source: EventTriggerSource, selection: EventTriggerSelection, options?: {
|
|
1304
1407
|
inputKeys?: boolean;
|
|
1305
1408
|
}): EventTriggerProblem[];
|
|
1306
1409
|
/** How people see one Event trigger. Each entry is one line of plain text. */
|
|
@@ -1321,10 +1424,10 @@ export declare function eventTypeText(eventType: string): string;
|
|
|
1321
1424
|
export declare function eventMatchFilterText(key: string, field: EventMatchField | undefined, value: EventMatch[string]): string;
|
|
1322
1425
|
/**
|
|
1323
1426
|
* Describe a selection for the dashboard and Slack, so both show the same
|
|
1324
|
-
* words. Pass the
|
|
1325
|
-
*
|
|
1427
|
+
* words. Pass the selection's source, as for `eventTriggerProblems`. Its
|
|
1428
|
+
* problems are the messages of that check.
|
|
1326
1429
|
*/
|
|
1327
|
-
export declare function describeEventTrigger(
|
|
1430
|
+
export declare function describeEventTrigger(source: EventTriggerSource, selection: EventTriggerSelection): EventTriggerDescription;
|
|
1328
1431
|
/** Join filters into one line. Every filter must match, so join with "and". */
|
|
1329
1432
|
export declare function eventTriggerFiltersText(filters: readonly string[]): string;
|
|
1330
1433
|
/**
|
|
@@ -2065,6 +2168,8 @@ export interface SandboxPrepareHookContext extends PluginContext {
|
|
|
2065
2168
|
export interface BeforeToolExecuteHookContext extends PluginContext {
|
|
2066
2169
|
decision: PluginDecision;
|
|
2067
2170
|
env: PluginEnv;
|
|
2171
|
+
/** Same directory as `PluginSandbox.juniorRoot` from `sandboxPrepare`. */
|
|
2172
|
+
juniorRoot: string;
|
|
2068
2173
|
actor?: Actor;
|
|
2069
2174
|
/** All actors who contributed instructions to the run so far; see `multi-actor-runs.md`. */
|
|
2070
2175
|
actors?: Actor[];
|
|
@@ -2423,6 +2528,11 @@ export interface PluginHooks {
|
|
|
2423
2528
|
*/
|
|
2424
2529
|
conversationTitleContext?(ctx: ConversationTitleContextHookContext): Promise<string[] | undefined> | string[] | undefined;
|
|
2425
2530
|
conversationSidebar?(ctx: ConversationSidebarHookContext): Promise<ConversationSidebarResult> | ConversationSidebarResult;
|
|
2531
|
+
/**
|
|
2532
|
+
* Return static guidance for the system prompt. Return the same text for one
|
|
2533
|
+
* deployment and platform, so the system prompt stays cacheable. Put text
|
|
2534
|
+
* that changes per request in `userPrompt`. See `policies/system-prompt.md`.
|
|
2535
|
+
*/
|
|
2426
2536
|
systemPrompt?(ctx: SystemPromptContext): Promise<PromptMessage[]> | PromptMessage[];
|
|
2427
2537
|
userPrompt?(ctx: UserPromptContext): Promise<UserPromptContribution[] | undefined> | UserPromptContribution[] | undefined;
|
|
2428
2538
|
beforeToolExecute?(ctx: BeforeToolExecuteHookContext): Promise<void> | void;
|
package/dist/index.js
CHANGED
|
@@ -684,6 +684,11 @@ const eventMatchFieldSchema = z.object({
|
|
|
684
684
|
*/
|
|
685
685
|
list: z.literal(true).optional(),
|
|
686
686
|
/**
|
|
687
|
+
* The case of the text in event data. Core changes a saved filter value
|
|
688
|
+
* to this case, so `getsentry/Junior` still matches `getsentry/junior`.
|
|
689
|
+
*/
|
|
690
|
+
case: z.enum(["lower", "upper"]).optional(),
|
|
691
|
+
/**
|
|
687
692
|
* The events that set this field. Leave it out when every event of the
|
|
688
693
|
* resource type sets it. A filter on this field never matches other
|
|
689
694
|
* events, so a trigger that selects them is rejected.
|
|
@@ -695,6 +700,11 @@ const eventMatchFieldSchema = z.object({
|
|
|
695
700
|
message: "Event match field enum requires kind \"string\".",
|
|
696
701
|
path: ["enum"]
|
|
697
702
|
});
|
|
703
|
+
if (field.case && field.kind !== "string") context.addIssue({
|
|
704
|
+
code: "custom",
|
|
705
|
+
message: "Event match field case requires kind \"string\".",
|
|
706
|
+
path: ["case"]
|
|
707
|
+
});
|
|
698
708
|
if (field.list && field.kind === "boolean") context.addIssue({
|
|
699
709
|
code: "custom",
|
|
700
710
|
message: "Event match field list cannot use kind \"boolean\".",
|
|
@@ -746,7 +756,7 @@ function mergeEventMatchFields(...maps) {
|
|
|
746
756
|
merged[key] = field;
|
|
747
757
|
continue;
|
|
748
758
|
}
|
|
749
|
-
if (current.kind !== field.kind || current.list !== field.list || !sameValues(current.enum, field.enum)) throw new Error(`Match field "${key}" has a different kind, enum, or list in two maps.`);
|
|
759
|
+
if (current.kind !== field.kind || current.list !== field.list || current.case !== field.case || !sameValues(current.enum, field.enum)) throw new Error(`Match field "${key}" has a different kind, case, enum, or list in two maps.`);
|
|
750
760
|
if (!current.events || !field.events) throw new Error(`Match field "${key}" is in two maps, so each copy must list its events.`);
|
|
751
761
|
merged[key] = {
|
|
752
762
|
...current,
|
|
@@ -755,6 +765,20 @@ function mergeEventMatchFields(...maps) {
|
|
|
755
765
|
}
|
|
756
766
|
return eventMatchFieldsSchema.parse(merged);
|
|
757
767
|
}
|
|
768
|
+
/**
|
|
769
|
+
* Merge the match fields of every resource type of one source, for a trigger
|
|
770
|
+
* with no identifier. Such a trigger can get the events of every resource
|
|
771
|
+
* type. A field with no `events` list is set on every event of its own
|
|
772
|
+
* resource type, so it gets that list before the merge. This throws like
|
|
773
|
+
* `mergeEventMatchFields` when two resource types describe one field
|
|
774
|
+
* differently.
|
|
775
|
+
*/
|
|
776
|
+
function anyResourceMatchFields(resourceTypes) {
|
|
777
|
+
return mergeEventMatchFields(...resourceTypes.map((resourceType) => Object.fromEntries(Object.entries(resourceType.matchFields ?? {}).map(([key, field]) => [key, field.events ? field : {
|
|
778
|
+
...field,
|
|
779
|
+
events: [...resourceType.supportedEvents]
|
|
780
|
+
}]))));
|
|
781
|
+
}
|
|
758
782
|
function stableMatchValue(value) {
|
|
759
783
|
if (!Array.isArray(value)) return value;
|
|
760
784
|
return [...value].sort((left, right) => {
|
|
@@ -784,6 +808,20 @@ function eventMatches(match, data) {
|
|
|
784
808
|
}
|
|
785
809
|
return true;
|
|
786
810
|
}
|
|
811
|
+
/**
|
|
812
|
+
* Return a match with each text value in the case that its field declares,
|
|
813
|
+
* so a saved filter has the same case as event data. Values of other fields
|
|
814
|
+
* do not change.
|
|
815
|
+
*/
|
|
816
|
+
function normalizeEventMatch(fields, match) {
|
|
817
|
+
if (!match) return void 0;
|
|
818
|
+
const toCase = (key, value) => {
|
|
819
|
+
const fieldCase = fields[key]?.case;
|
|
820
|
+
if (typeof value !== "string" || !fieldCase) return value;
|
|
821
|
+
return fieldCase === "lower" ? value.toLowerCase() : value.toUpperCase();
|
|
822
|
+
};
|
|
823
|
+
return Object.fromEntries(Object.entries(match).map(([key, value]) => [key, Array.isArray(value) ? value.map((entry) => toCase(key, entry)) : typeof value === "boolean" ? value : toCase(key, value)]));
|
|
824
|
+
}
|
|
787
825
|
function isRegExpSource(value) {
|
|
788
826
|
try {
|
|
789
827
|
new RegExp(value);
|
|
@@ -804,8 +842,7 @@ const pluginEventTypeSchema = z.object({
|
|
|
804
842
|
identifier: eventIdentifierSchema.optional(),
|
|
805
843
|
supportedEvents: z.array(eventTypeSchema).min(1),
|
|
806
844
|
suggestedEvents: z.array(eventTypeSchema).optional(),
|
|
807
|
-
matchFields: eventMatchFieldsSchema.optional()
|
|
808
|
-
guidance: z.record(eventTypeSchema, z.string().trim().min(1).max(EVENT_GUIDANCE_MAX_LENGTH)).optional()
|
|
845
|
+
matchFields: eventMatchFieldsSchema.optional()
|
|
809
846
|
}).strict().superRefine((resourceType, context) => {
|
|
810
847
|
const supported = /* @__PURE__ */ new Set();
|
|
811
848
|
resourceType.supportedEvents.forEach((eventType, index) => {
|
|
@@ -816,11 +853,6 @@ const pluginEventTypeSchema = z.object({
|
|
|
816
853
|
});
|
|
817
854
|
supported.add(eventType);
|
|
818
855
|
});
|
|
819
|
-
for (const eventType of Object.keys(resourceType.guidance ?? {})) if (!supported.has(eventType)) context.addIssue({
|
|
820
|
-
code: "custom",
|
|
821
|
-
message: `Guidance event type "${eventType}" is not supported.`,
|
|
822
|
-
path: ["guidance", eventType]
|
|
823
|
-
});
|
|
824
856
|
const suggested = /* @__PURE__ */ new Set();
|
|
825
857
|
resourceType.suggestedEvents?.forEach((eventType, index) => {
|
|
826
858
|
if (suggested.has(eventType)) context.addIssue({
|
|
@@ -848,8 +880,41 @@ const pluginEventTypeSchema = z.object({
|
|
|
848
880
|
});
|
|
849
881
|
});
|
|
850
882
|
});
|
|
883
|
+
/**
|
|
884
|
+
* The event reach of one event type: which event automation triggers can
|
|
885
|
+
* select it.
|
|
886
|
+
*
|
|
887
|
+
* - `"all"`: a trigger can run for every resource of the source, with no
|
|
888
|
+
* identifier and no filter. Use it only for events that carry status facts
|
|
889
|
+
* and cause no side effects.
|
|
890
|
+
* - `{ anyOf }`: a trigger with no identifier must set at least one of these
|
|
891
|
+
* filters. Use it when an event carries private text, such as titles or
|
|
892
|
+
* comments, or makes Junior act, such as add a reaction.
|
|
893
|
+
* - `"resource"`: only a trigger for one resource, with an identifier, can
|
|
894
|
+
* select the event.
|
|
895
|
+
*/
|
|
896
|
+
const eventReachSchema = z.union([
|
|
897
|
+
z.literal("all"),
|
|
898
|
+
z.literal("resource"),
|
|
899
|
+
z.object({ anyOf: z.array(z.string().regex(EVENT_MATCH_FIELD_NAME)).min(1) }).strict()
|
|
900
|
+
]);
|
|
901
|
+
/** What a source declares once for one of its event types. */
|
|
902
|
+
const pluginEventDeclarationSchema = z.object({
|
|
903
|
+
reach: eventReachSchema,
|
|
904
|
+
/**
|
|
905
|
+
* App guidance that the agent gets with each Event of this type, for every
|
|
906
|
+
* Watch and event automation.
|
|
907
|
+
*/
|
|
908
|
+
guidance: z.string().trim().min(1).max(EVENT_GUIDANCE_MAX_LENGTH).optional()
|
|
909
|
+
}).strict();
|
|
851
910
|
const pluginEventsSchema = z.object({
|
|
852
911
|
resourceTypes: z.array(pluginEventTypeSchema).min(1),
|
|
912
|
+
/**
|
|
913
|
+
* One declaration for each event type that a resource type supports. An
|
|
914
|
+
* event that two resource types share has one declaration, so its reach
|
|
915
|
+
* and guidance are the same for both.
|
|
916
|
+
*/
|
|
917
|
+
events: z.record(eventTypeSchema, pluginEventDeclarationSchema),
|
|
853
918
|
isEnabled: z.function({
|
|
854
919
|
input: [],
|
|
855
920
|
output: z.boolean()
|
|
@@ -872,6 +937,41 @@ const pluginEventsSchema = z.object({
|
|
|
872
937
|
});
|
|
873
938
|
seen.add(resourceType.type);
|
|
874
939
|
});
|
|
940
|
+
const supported = new Set(registration.resourceTypes.flatMap((resourceType) => resourceType.supportedEvents));
|
|
941
|
+
for (const eventType of supported) if (!registration.events[eventType]) context.addIssue({
|
|
942
|
+
code: "custom",
|
|
943
|
+
message: `Event type "${eventType}" has no declaration in events.`,
|
|
944
|
+
path: ["events"]
|
|
945
|
+
});
|
|
946
|
+
let fields;
|
|
947
|
+
try {
|
|
948
|
+
fields = anyResourceMatchFields(registration.resourceTypes);
|
|
949
|
+
} catch (error) {
|
|
950
|
+
context.addIssue({
|
|
951
|
+
code: "custom",
|
|
952
|
+
message: error.message,
|
|
953
|
+
path: ["resourceTypes"]
|
|
954
|
+
});
|
|
955
|
+
return;
|
|
956
|
+
}
|
|
957
|
+
for (const [eventType, declaration] of Object.entries(registration.events)) {
|
|
958
|
+
if (!supported.has(eventType)) context.addIssue({
|
|
959
|
+
code: "custom",
|
|
960
|
+
message: `Declared event type "${eventType}" is not supported.`,
|
|
961
|
+
path: ["events", eventType]
|
|
962
|
+
});
|
|
963
|
+
if (typeof declaration.reach === "string") continue;
|
|
964
|
+
for (const filter of declaration.reach.anyOf) if (!fields[filter]?.events?.includes(eventType)) context.addIssue({
|
|
965
|
+
code: "custom",
|
|
966
|
+
message: `Reach filter "${filter}" is not set on "${eventType}".`,
|
|
967
|
+
path: [
|
|
968
|
+
"events",
|
|
969
|
+
eventType,
|
|
970
|
+
"reach",
|
|
971
|
+
"anyOf"
|
|
972
|
+
]
|
|
973
|
+
});
|
|
974
|
+
}
|
|
875
975
|
});
|
|
876
976
|
/** Apply a plugin's identifier convention at event boundaries. */
|
|
877
977
|
function normalizeEventIdentifier(registration, identifier) {
|
|
@@ -928,6 +1028,10 @@ const eventInputSchema = z.object({
|
|
|
928
1028
|
}).strict();
|
|
929
1029
|
const eventSchema = eventInputSchema.extend({ namespace: z.string().min(1) });
|
|
930
1030
|
// src/event-triggers.ts
|
|
1031
|
+
/** The events of a source that a trigger with no identifier can select. */
|
|
1032
|
+
function anyResourceEvents(source) {
|
|
1033
|
+
return Object.entries(source.events).filter(([, declaration]) => declaration.reach !== "resource").map(([eventType]) => eventType).sort();
|
|
1034
|
+
}
|
|
931
1035
|
function quoted(values) {
|
|
932
1036
|
return values.map((value) => `"${value}"`).join(", ");
|
|
933
1037
|
}
|
|
@@ -950,38 +1054,85 @@ function valueProblem(filter, field, value) {
|
|
|
950
1054
|
const notAllowed = allowed ? values.find((entry) => !allowed.includes(String(entry))) : void 0;
|
|
951
1055
|
if (allowed && notAllowed !== void 0) return `${filter} cannot be "${notAllowed}". Use one of: ${quoted(allowed)}.`;
|
|
952
1056
|
}
|
|
1057
|
+
/** The events and filters that one selection can use. */
|
|
1058
|
+
function selectionScope(source, selection) {
|
|
1059
|
+
if (selection.resourceType === void 0) {
|
|
1060
|
+
const events = anyResourceEvents(source);
|
|
1061
|
+
if (events.length === 0) return {
|
|
1062
|
+
reason: "identifier_required",
|
|
1063
|
+
message: "This source cannot run for every resource. Set a resource type and an identifier."
|
|
1064
|
+
};
|
|
1065
|
+
return {
|
|
1066
|
+
events,
|
|
1067
|
+
fields: source.anyResourceFields,
|
|
1068
|
+
owner: "source"
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
const resourceType = source.resourceTypes.find((candidate) => candidate.type === selection.resourceType);
|
|
1072
|
+
if (!resourceType) return {
|
|
1073
|
+
reason: "unknown_resource_type",
|
|
1074
|
+
message: `"${selection.resourceType}" is not an enabled resource type. Use one of: ${quoted(source.resourceTypes.map((candidate) => candidate.type))}.`
|
|
1075
|
+
};
|
|
1076
|
+
return {
|
|
1077
|
+
events: resourceType.supportedEvents,
|
|
1078
|
+
fields: resourceType.matchFields ?? {},
|
|
1079
|
+
owner: "resource type"
|
|
1080
|
+
};
|
|
1081
|
+
}
|
|
1082
|
+
/**
|
|
1083
|
+
* Return the events and filters that a selection can use: those of its
|
|
1084
|
+
* resource type, or with no resource type, the events whose reach is not
|
|
1085
|
+
* `"resource"`, with the filters of every resource type. Return nothing when the source
|
|
1086
|
+
* has no such resource type or cannot run for every resource.
|
|
1087
|
+
*/
|
|
1088
|
+
function eventTriggerChoices(source, selection) {
|
|
1089
|
+
if (source.resourceTypes.length === 0) return void 0;
|
|
1090
|
+
const scope = selectionScope(source, selection);
|
|
1091
|
+
return "reason" in scope ? void 0 : {
|
|
1092
|
+
events: scope.events,
|
|
1093
|
+
fields: scope.fields
|
|
1094
|
+
};
|
|
1095
|
+
}
|
|
953
1096
|
/**
|
|
954
1097
|
* List why a selection cannot work with the resource types of one source. An
|
|
955
1098
|
* empty list means the resource type exists, every selected event is
|
|
956
1099
|
* supported, and every selected event can match every filter. A filter on a
|
|
957
1100
|
* list field (`list: true`) matches when the event list includes the value.
|
|
1101
|
+
* A selection with no resource type can use only events whose reach is not
|
|
1102
|
+
* `"resource"`, with the filters of every resource type, and must set one of
|
|
1103
|
+
* the filters that the reach of each selected event names.
|
|
958
1104
|
* Set `inputKeys` for the agent, so messages name the `match.<key>` input.
|
|
959
1105
|
*/
|
|
960
|
-
function eventTriggerProblems(
|
|
961
|
-
|
|
962
|
-
if (!resourceType) return [{
|
|
1106
|
+
function eventTriggerProblems(source, selection, options = {}) {
|
|
1107
|
+
if (source.resourceTypes.length === 0) return [{
|
|
963
1108
|
reason: "unknown_resource_type",
|
|
964
|
-
message:
|
|
1109
|
+
message: "The plugin for this source is not enabled."
|
|
965
1110
|
}];
|
|
1111
|
+
const scope = selectionScope(source, selection);
|
|
1112
|
+
if ("reason" in scope) return [scope];
|
|
1113
|
+
const filterName = (key) => options.inputKeys ? `Filter "${key}" (match.${key})` : `Filter "${key}"`;
|
|
966
1114
|
const problems = [];
|
|
967
|
-
const events = selection.events.filter((eventType) =>
|
|
1115
|
+
const events = selection.events.filter((eventType) => scope.events.includes(eventType));
|
|
968
1116
|
for (const eventType of selection.events) {
|
|
969
1117
|
if (events.includes(eventType)) continue;
|
|
970
|
-
|
|
1118
|
+
const anySupports = source.resourceTypes.some((resourceType) => resourceType.supportedEvents.includes(eventType));
|
|
1119
|
+
problems.push(selection.resourceType === void 0 && anySupports ? {
|
|
1120
|
+
reason: "identifier_required",
|
|
1121
|
+
message: `"${eventType}" cannot run for every resource. Set a resource type and an identifier, or use one of: ${quoted(scope.events)}.`
|
|
1122
|
+
} : {
|
|
971
1123
|
reason: "unknown_event",
|
|
972
|
-
message: `"${eventType}" is not an event for this
|
|
1124
|
+
message: `"${eventType}" is not an event for this ${scope.owner}. Use one of: ${quoted(scope.events)}.`
|
|
973
1125
|
});
|
|
974
1126
|
}
|
|
975
|
-
const
|
|
976
|
-
const fieldNames = Object.keys(fields);
|
|
1127
|
+
const fieldNames = Object.keys(scope.fields);
|
|
977
1128
|
for (const [key, value] of Object.entries(selection.match ?? {})) {
|
|
978
|
-
const filter =
|
|
979
|
-
const field = fields[key];
|
|
1129
|
+
const filter = filterName(key);
|
|
1130
|
+
const field = scope.fields[key];
|
|
980
1131
|
if (!field) {
|
|
981
1132
|
problems.push({
|
|
982
1133
|
reason: "unknown_field",
|
|
983
1134
|
field: key,
|
|
984
|
-
message: fieldNames.length === 0 ? `${filter} does not exist. This
|
|
1135
|
+
message: fieldNames.length === 0 ? `${filter} does not exist. This ${scope.owner} has no filters, so remove it.` : `${filter} does not exist for this ${scope.owner}. Remove it, or use one of: ${quoted(fieldNames)}.`
|
|
985
1136
|
});
|
|
986
1137
|
continue;
|
|
987
1138
|
}
|
|
@@ -998,8 +1149,31 @@ function eventTriggerProblems(resourceTypes, selection, options = {}) {
|
|
|
998
1149
|
message: invalid
|
|
999
1150
|
});
|
|
1000
1151
|
}
|
|
1152
|
+
if (selection.resourceType === void 0) problems.push(...requiredFilterProblems(source, events, selection, options));
|
|
1001
1153
|
return problems;
|
|
1002
1154
|
}
|
|
1155
|
+
/** Name the filters that the selected events need and the trigger does not set. */
|
|
1156
|
+
function requiredFilterProblems(source, events, selection, options) {
|
|
1157
|
+
const missing = /* @__PURE__ */ new Map();
|
|
1158
|
+
for (const eventType of events) {
|
|
1159
|
+
const reach = source.events[eventType]?.reach;
|
|
1160
|
+
if (typeof reach !== "object") continue;
|
|
1161
|
+
if (reach.anyOf.some((filter) => selection.match?.[filter] !== void 0)) continue;
|
|
1162
|
+
const key = [...reach.anyOf].sort().join(",");
|
|
1163
|
+
const entry = missing.get(key) ?? {
|
|
1164
|
+
anyOf: reach.anyOf,
|
|
1165
|
+
events: []
|
|
1166
|
+
};
|
|
1167
|
+
entry.events.push(eventType);
|
|
1168
|
+
missing.set(key, entry);
|
|
1169
|
+
}
|
|
1170
|
+
const inputKey = (filter) => options.inputKeys ? ` (match.${filter})` : "";
|
|
1171
|
+
return [...missing.values()].map(({ anyOf, events: requiredBy }) => ({
|
|
1172
|
+
reason: "filter_required",
|
|
1173
|
+
field: anyOf[0],
|
|
1174
|
+
message: anyOf.length === 1 ? `This trigger needs a "${anyOf[0]}" filter${inputKey(anyOf[0])}, because ${quoted(requiredBy)} cannot run for every resource.` : `This trigger needs one of these filters: ${anyOf.map((filter) => `"${filter}"${inputKey(filter)}`).join(", ")}, because ${quoted(requiredBy)} cannot run for every resource.`
|
|
1175
|
+
}));
|
|
1176
|
+
}
|
|
1003
1177
|
/** Write one event type in plain words: `pull_request.opened` is "pull request opened". */
|
|
1004
1178
|
function eventTypeText(eventType) {
|
|
1005
1179
|
return eventType.replaceAll(/[._]/g, " ");
|
|
@@ -1014,15 +1188,15 @@ function eventMatchFilterText(key, field, value) {
|
|
|
1014
1188
|
}
|
|
1015
1189
|
/**
|
|
1016
1190
|
* Describe a selection for the dashboard and Slack, so both show the same
|
|
1017
|
-
* words. Pass the
|
|
1018
|
-
*
|
|
1191
|
+
* words. Pass the selection's source, as for `eventTriggerProblems`. Its
|
|
1192
|
+
* problems are the messages of that check.
|
|
1019
1193
|
*/
|
|
1020
|
-
function describeEventTrigger(
|
|
1021
|
-
const fields =
|
|
1194
|
+
function describeEventTrigger(source, selection) {
|
|
1195
|
+
const fields = eventTriggerChoices(source, selection)?.fields ?? {};
|
|
1022
1196
|
return {
|
|
1023
1197
|
events: selection.events.map(eventTypeText),
|
|
1024
1198
|
filters: Object.entries(selection.match ?? {}).map(([key, value]) => eventMatchFilterText(key, fields[key], value)),
|
|
1025
|
-
problems: eventTriggerProblems(
|
|
1199
|
+
problems: eventTriggerProblems(source, selection).map((problem) => problem.message)
|
|
1026
1200
|
};
|
|
1027
1201
|
}
|
|
1028
1202
|
/** Join filters into one line. Every filter must match, so join with "and". */
|
|
@@ -1552,4 +1726,4 @@ const pluginUserPageInputSchema = z.object({
|
|
|
1552
1726
|
limit: z.number().int().min(1).max(50),
|
|
1553
1727
|
query: pluginUserPageQuerySchema
|
|
1554
1728
|
}).strict();
|
|
1555
|
-
export { EVENT_DATA_MAX_JSON_BYTES, EVENT_DATA_MAX_KEYS, EVENT_GUIDANCE_MAX_LENGTH, EVENT_SUMMARY_MAX_LENGTH, EVENT_TEXT_MAX_LENGTH, EgressAuthRequired, EgressPolicyDenied, PluginToolInputError, REQUIRED_TOOL_ANNOTATION_KEYS, actorSchema, actorUserIdSchema, agentInvocationSourceSchema, codeChangeInputSchema, codeChangeStateSchema, conversationAnnotationInputSchema, conversationEventIconSchema, conversationEventPresentationSchema, conversationSidebarAnnotationSchema, conversationSidebarIconSchema, createEventSource, createLocalSource, createSlackSource, createWebSource, defineConversationEvent, defineJuniorPlugin, definePluginTool, definePromptContext, describeEventTrigger, destinationSchema, destinationVisibilitySchema, dispatchOptionsSchema, enforceEgressPolicy, eventAutomationSourceSchema, eventDataSchema, eventIdentifierSchema, eventInputSchema, eventMatchFieldSchema, eventMatchFieldsSchema, eventMatchFilterText, eventMatchSchema, eventMatches, eventPersonSchema, eventSchema, eventSourceSchema, eventTriggerFiltersText, eventTriggerProblems, eventTriggerText, eventTypeSchema, eventTypeText, eventsWithoutMatchField, getSourceKey, identitySchema, isPrivateSource, isSlackDestination, localActorSchema, localDestinationSchema, localSourceSchema, locationSchema, mergeEventMatchFields, missingToolAnnotationKeys, nonBlankStringSchema, normalizeEventIdentifier, objectAnnotationSchema, objectFactFields, objectFactsSchema, objectIconPath, objectIconPaths, objectPresentation, objectTypeSchema, ownedObjectAnnotationSchema, parseShellCommands, platformSchema, pluginApiRouteRequestContextSchema, pluginAuthorizationSchema, pluginCredentialHeaderTransformSchema, pluginCredentialLeaseSchema, pluginCredentialResultSchema, pluginCredentialSubjectSchema, pluginDispatchSourceSchema, pluginEventTypeSchema, pluginEventsSchema, pluginGrantSchema, pluginProviderAccountSchema, pluginRunContextSchema, pluginRunTranscriptEntrySchema, pluginRunTranscriptProvenanceSchema, pluginStoredTokensSchema, pluginToolContentSchema, pluginToolContinuationSchema, pluginToolOutputSchema, pluginUserPageContentSchema, pluginUserPageCursorSchema, pluginUserPageFilterSchema, pluginUserPageInputSchema, pluginUserPageLinkSchema, pluginUserPageLinksSchema, pluginUserPageQuerySchema, promptContextSchema, promptMessageSchema, replyAttributionSchema, resourceLinkAnnotationSchema, resourceTypeSchema, scheduledAutomationSourceSchema, slackActorSchema, slackDestinationSchema, slackLocationSchema, slackSourceSchema, sourceSchema, sourceVisibilitySchema, stableEventMatchKey, subscribableResourceSchema, systemActorSchema, taskOutcomeSchema, toolApprovalModeSchema, userSchema, watchResultSchema, webActorSchema, webSourceSchema, zodTool };
|
|
1729
|
+
export { EVENT_DATA_MAX_JSON_BYTES, EVENT_DATA_MAX_KEYS, EVENT_GUIDANCE_MAX_LENGTH, EVENT_SUMMARY_MAX_LENGTH, EVENT_TEXT_MAX_LENGTH, EgressAuthRequired, EgressPolicyDenied, PluginToolInputError, REQUIRED_TOOL_ANNOTATION_KEYS, actorSchema, actorUserIdSchema, agentInvocationSourceSchema, anyResourceEvents, anyResourceMatchFields, codeChangeInputSchema, codeChangeStateSchema, conversationAnnotationInputSchema, conversationEventIconSchema, conversationEventPresentationSchema, conversationSidebarAnnotationSchema, conversationSidebarIconSchema, createEventSource, createLocalSource, createSlackSource, createWebSource, defineConversationEvent, defineJuniorPlugin, definePluginTool, definePromptContext, describeEventTrigger, destinationSchema, destinationVisibilitySchema, dispatchOptionsSchema, enforceEgressPolicy, eventAutomationSourceSchema, eventDataSchema, eventIdentifierSchema, eventInputSchema, eventMatchFieldSchema, eventMatchFieldsSchema, eventMatchFilterText, eventMatchSchema, eventMatches, eventPersonSchema, eventReachSchema, eventSchema, eventSourceSchema, eventTriggerChoices, eventTriggerFiltersText, eventTriggerProblems, eventTriggerText, eventTypeSchema, eventTypeText, eventsWithoutMatchField, getSourceKey, identitySchema, isPrivateSource, isSlackDestination, localActorSchema, localDestinationSchema, localSourceSchema, locationSchema, mergeEventMatchFields, missingToolAnnotationKeys, nonBlankStringSchema, normalizeEventIdentifier, normalizeEventMatch, objectAnnotationSchema, objectFactFields, objectFactsSchema, objectIconPath, objectIconPaths, objectPresentation, objectTypeSchema, ownedObjectAnnotationSchema, parseShellCommands, platformSchema, pluginApiRouteRequestContextSchema, pluginAuthorizationSchema, pluginCredentialHeaderTransformSchema, pluginCredentialLeaseSchema, pluginCredentialResultSchema, pluginCredentialSubjectSchema, pluginDispatchSourceSchema, pluginEventDeclarationSchema, pluginEventTypeSchema, pluginEventsSchema, pluginGrantSchema, pluginProviderAccountSchema, pluginRunContextSchema, pluginRunTranscriptEntrySchema, pluginRunTranscriptProvenanceSchema, pluginStoredTokensSchema, pluginToolContentSchema, pluginToolContinuationSchema, pluginToolOutputSchema, pluginUserPageContentSchema, pluginUserPageCursorSchema, pluginUserPageFilterSchema, pluginUserPageInputSchema, pluginUserPageLinkSchema, pluginUserPageLinksSchema, pluginUserPageQuerySchema, promptContextSchema, promptMessageSchema, replyAttributionSchema, resourceLinkAnnotationSchema, resourceTypeSchema, scheduledAutomationSourceSchema, slackActorSchema, slackDestinationSchema, slackLocationSchema, slackSourceSchema, sourceSchema, sourceVisibilitySchema, stableEventMatchKey, subscribableResourceSchema, systemActorSchema, taskOutcomeSchema, toolApprovalModeSchema, userSchema, watchResultSchema, webActorSchema, webSourceSchema, zodTool };
|