@sentry/junior-plugin-api 0.198.0 → 0.199.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 +2021 -19
- package/package.json +4 -5
- package/dist/annotations.d.ts +0 -75
- package/dist/cli.d.ts +0 -39
- package/dist/code.d.ts +0 -37
- package/dist/context.d.ts +0 -158
- package/dist/conversation-events.d.ts +0 -80
- package/dist/credentials.d.ts +0 -184
- package/dist/dispatch.d.ts +0 -16
- package/dist/egress-policy.d.ts +0 -9
- package/dist/hooks.d.ts +0 -43
- package/dist/manifest.d.ts +0 -76
- package/dist/operations.d.ts +0 -153
- package/dist/prompt.d.ts +0 -56
- package/dist/registration.d.ts +0 -28
- package/dist/resource-events.d.ts +0 -128
- package/dist/schemas.d.ts +0 -261
- package/dist/state.d.ts +0 -10
- package/dist/tasks.d.ts +0 -250
- package/dist/tools.d.ts +0 -351
- package/dist/user-pages.d.ts +0 -97
- package/src/annotations.ts +0 -78
- package/src/cli.ts +0 -54
- package/src/code.ts +0 -37
- package/src/context.ts +0 -241
- package/src/conversation-events.ts +0 -136
- package/src/credentials.ts +0 -221
- package/src/dispatch.ts +0 -31
- package/src/egress-policy.ts +0 -15
- package/src/hooks.ts +0 -118
- package/src/index.ts +0 -29
- package/src/manifest.ts +0 -89
- package/src/operations.ts +0 -199
- package/src/prompt.ts +0 -99
- package/src/registration.ts +0 -173
- package/src/resource-events.ts +0 -320
- package/src/schemas.ts +0 -348
- package/src/state.ts +0 -15
- package/src/tasks.ts +0 -105
- package/src/tools.ts +0 -579
- package/src/user-pages.ts +0 -130
package/src/resource-events.ts
DELETED
|
@@ -1,320 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
export const RESOURCE_EVENT_SUMMARY_MAX_LENGTH = 4_000;
|
|
4
|
-
export const RESOURCE_EVENT_TEXT_MAX_LENGTH = 8_000;
|
|
5
|
-
export const RESOURCE_EVENT_DATA_MAX_KEYS = 32;
|
|
6
|
-
export const RESOURCE_EVENT_DATA_MAX_JSON_BYTES = 4_000;
|
|
7
|
-
export const RESOURCE_EVENT_GUIDANCE_MAX_LENGTH = 1_000;
|
|
8
|
-
|
|
9
|
-
/** Small trusted data from the plugin. The agent should not look these up again. */
|
|
10
|
-
export const resourceEventDataSchema = z
|
|
11
|
-
.record(z.string(), z.unknown())
|
|
12
|
-
.superRefine((value, context) => {
|
|
13
|
-
const keys = Object.keys(value);
|
|
14
|
-
if (keys.length > RESOURCE_EVENT_DATA_MAX_KEYS) {
|
|
15
|
-
context.addIssue({
|
|
16
|
-
code: "custom",
|
|
17
|
-
message: `Resource event data may include at most ${RESOURCE_EVENT_DATA_MAX_KEYS} keys.`,
|
|
18
|
-
});
|
|
19
|
-
}
|
|
20
|
-
const jsonBytes = new TextEncoder().encode(
|
|
21
|
-
JSON.stringify(value),
|
|
22
|
-
).byteLength;
|
|
23
|
-
if (jsonBytes > RESOURCE_EVENT_DATA_MAX_JSON_BYTES) {
|
|
24
|
-
context.addIssue({
|
|
25
|
-
code: "custom",
|
|
26
|
-
message: `Resource event data may be at most ${RESOURCE_EVENT_DATA_MAX_JSON_BYTES} JSON bytes.`,
|
|
27
|
-
});
|
|
28
|
-
}
|
|
29
|
-
});
|
|
30
|
-
|
|
31
|
-
/** Canonical dotted event type published and selected across plugins. */
|
|
32
|
-
export const resourceEventTypeSchema = z
|
|
33
|
-
.string()
|
|
34
|
-
.regex(/^[a-z][a-z0-9_-]*(?:\.[a-z][a-z0-9_-]*)+$/)
|
|
35
|
-
.describe(
|
|
36
|
-
"Canonical dotted event type, such as issue.closed or pull_request.review.changes_requested.",
|
|
37
|
-
);
|
|
38
|
-
|
|
39
|
-
export const resourceTypeSchema = z
|
|
40
|
-
.string()
|
|
41
|
-
.regex(/^[a-z][a-z0-9_]*$/)
|
|
42
|
-
.describe(
|
|
43
|
-
"Canonical plugin-defined resource type, such as issue or pull_request.",
|
|
44
|
-
);
|
|
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
|
-
|
|
156
|
-
export const pluginResourceEventTypeSchema = z
|
|
157
|
-
.object({
|
|
158
|
-
type: resourceTypeSchema,
|
|
159
|
-
supportedEvents: z.array(resourceEventTypeSchema).min(1),
|
|
160
|
-
suggestedEvents: z.array(resourceEventTypeSchema).optional(),
|
|
161
|
-
matchFields: resourceEventMatchFieldsSchema.optional(),
|
|
162
|
-
guidance: z
|
|
163
|
-
.record(
|
|
164
|
-
resourceEventTypeSchema,
|
|
165
|
-
z.string().trim().min(1).max(RESOURCE_EVENT_GUIDANCE_MAX_LENGTH),
|
|
166
|
-
)
|
|
167
|
-
.optional(),
|
|
168
|
-
})
|
|
169
|
-
.strict()
|
|
170
|
-
.superRefine((resourceType, context) => {
|
|
171
|
-
const supported = new Set<string>();
|
|
172
|
-
resourceType.supportedEvents.forEach((eventType, index) => {
|
|
173
|
-
if (supported.has(eventType)) {
|
|
174
|
-
context.addIssue({
|
|
175
|
-
code: "custom",
|
|
176
|
-
message: `Duplicate supported resource event type "${eventType}".`,
|
|
177
|
-
path: ["supportedEvents", index],
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
supported.add(eventType);
|
|
181
|
-
});
|
|
182
|
-
for (const eventType of Object.keys(resourceType.guidance ?? {})) {
|
|
183
|
-
if (!supported.has(eventType)) {
|
|
184
|
-
context.addIssue({
|
|
185
|
-
code: "custom",
|
|
186
|
-
message: `Guidance resource event type "${eventType}" is not supported.`,
|
|
187
|
-
path: ["guidance", eventType],
|
|
188
|
-
});
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
const suggested = new Set<string>();
|
|
192
|
-
resourceType.suggestedEvents?.forEach((eventType, index) => {
|
|
193
|
-
if (suggested.has(eventType)) {
|
|
194
|
-
context.addIssue({
|
|
195
|
-
code: "custom",
|
|
196
|
-
message: `Duplicate suggested resource event type "${eventType}".`,
|
|
197
|
-
path: ["suggestedEvents", index],
|
|
198
|
-
});
|
|
199
|
-
}
|
|
200
|
-
if (!supported.has(eventType)) {
|
|
201
|
-
context.addIssue({
|
|
202
|
-
code: "custom",
|
|
203
|
-
message: `Suggested resource event type "${eventType}" is not supported.`,
|
|
204
|
-
path: ["suggestedEvents", index],
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
suggested.add(eventType);
|
|
208
|
-
});
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
export const pluginResourceEventsSchema = z
|
|
212
|
-
.object({
|
|
213
|
-
resourceTypes: z.array(pluginResourceEventTypeSchema).min(1),
|
|
214
|
-
isEnabled: z.function({ input: [], output: z.boolean() }).optional(),
|
|
215
|
-
normalizeIdentifier: z
|
|
216
|
-
.function({ input: [z.string()], output: z.string() })
|
|
217
|
-
.optional(),
|
|
218
|
-
})
|
|
219
|
-
.strict()
|
|
220
|
-
.superRefine((registration, context) => {
|
|
221
|
-
const seen = new Set<string>();
|
|
222
|
-
registration.resourceTypes.forEach((resourceType, index) => {
|
|
223
|
-
if (seen.has(resourceType.type)) {
|
|
224
|
-
context.addIssue({
|
|
225
|
-
code: "custom",
|
|
226
|
-
message: `Duplicate resource type "${resourceType.type}".`,
|
|
227
|
-
path: ["resourceTypes", index, "type"],
|
|
228
|
-
});
|
|
229
|
-
}
|
|
230
|
-
seen.add(resourceType.type);
|
|
231
|
-
});
|
|
232
|
-
});
|
|
233
|
-
|
|
234
|
-
export type PluginResourceEventType = z.output<
|
|
235
|
-
typeof pluginResourceEventTypeSchema
|
|
236
|
-
>;
|
|
237
|
-
export type PluginResourceEvents = z.output<typeof pluginResourceEventsSchema>;
|
|
238
|
-
|
|
239
|
-
/** Apply a plugin's identifier convention at resource-event boundaries. */
|
|
240
|
-
export function normalizeResourceEventIdentifier(
|
|
241
|
-
registration: Pick<PluginResourceEvents, "normalizeIdentifier"> | undefined,
|
|
242
|
-
identifier: string,
|
|
243
|
-
): string {
|
|
244
|
-
const trimmed = identifier.trim();
|
|
245
|
-
const normalized = (
|
|
246
|
-
registration?.normalizeIdentifier?.(trimmed) ?? trimmed
|
|
247
|
-
).trim();
|
|
248
|
-
if (!normalized) {
|
|
249
|
-
throw new Error("Resource event identifier must not normalize to empty");
|
|
250
|
-
}
|
|
251
|
-
return normalized;
|
|
252
|
-
}
|
|
253
|
-
|
|
254
|
-
export const subscribableResourceSchema = z
|
|
255
|
-
.object({
|
|
256
|
-
identifier: z.string().min(1),
|
|
257
|
-
label: z.string().min(1),
|
|
258
|
-
namespace: z.string().min(1),
|
|
259
|
-
suggestedEvents: z.array(resourceEventTypeSchema).optional(),
|
|
260
|
-
supportedEvents: z.array(resourceEventTypeSchema),
|
|
261
|
-
type: resourceTypeSchema,
|
|
262
|
-
})
|
|
263
|
-
.strict();
|
|
264
|
-
|
|
265
|
-
export type SubscribableResource = z.output<typeof subscribableResourceSchema>;
|
|
266
|
-
|
|
267
|
-
/** Result returned after a temporary resource subscription is created. */
|
|
268
|
-
export const resourceEventSubscriptionResultSchema = z
|
|
269
|
-
.object({
|
|
270
|
-
events: z.array(resourceEventTypeSchema).min(1),
|
|
271
|
-
id: z.string().min(1),
|
|
272
|
-
})
|
|
273
|
-
.strict();
|
|
274
|
-
|
|
275
|
-
export type ResourceEventSubscriptionResult = z.output<
|
|
276
|
-
typeof resourceEventSubscriptionResultSchema
|
|
277
|
-
>;
|
|
278
|
-
|
|
279
|
-
export const resourceEventInputSchema = z
|
|
280
|
-
.object({
|
|
281
|
-
eventKey: z.string().min(1),
|
|
282
|
-
eventType: resourceEventTypeSchema,
|
|
283
|
-
identifier: z.string().min(1),
|
|
284
|
-
occurredAtMs: z.number().finite(),
|
|
285
|
-
terminal: z.boolean().optional(),
|
|
286
|
-
trustedSummary: z
|
|
287
|
-
.string()
|
|
288
|
-
.min(1)
|
|
289
|
-
.transform((value) => value.slice(0, RESOURCE_EVENT_SUMMARY_MAX_LENGTH)),
|
|
290
|
-
/** Trusted structured facts. Prefer ids and urls over long prose. */
|
|
291
|
-
data: resourceEventDataSchema.optional(),
|
|
292
|
-
untrustedText: z
|
|
293
|
-
.string()
|
|
294
|
-
.transform((value) => value.slice(0, RESOURCE_EVENT_TEXT_MAX_LENGTH))
|
|
295
|
-
.optional(),
|
|
296
|
-
})
|
|
297
|
-
.strict();
|
|
298
|
-
|
|
299
|
-
export type ResourceEventData = z.output<typeof resourceEventDataSchema>;
|
|
300
|
-
export type ResourceEventInput = z.output<typeof resourceEventInputSchema>;
|
|
301
|
-
|
|
302
|
-
export const resourceEventSchema = resourceEventInputSchema.extend({
|
|
303
|
-
namespace: z.string().min(1),
|
|
304
|
-
});
|
|
305
|
-
|
|
306
|
-
export type ResourceEvent = z.output<typeof resourceEventSchema>;
|
|
307
|
-
|
|
308
|
-
export interface ResourceEventPublisher {
|
|
309
|
-
/** Publish one normalized event under the owning plugin's namespace. */
|
|
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[]>;
|
|
320
|
-
}
|
package/src/schemas.ts
DELETED
|
@@ -1,348 +0,0 @@
|
|
|
1
|
-
import { z } from "zod";
|
|
2
|
-
|
|
3
|
-
const slackTeamIdSchema = z.string().regex(/^T[A-Z0-9]+$/);
|
|
4
|
-
const slackConversationIdSchema = z.string().regex(/^(C|G|D)[A-Z0-9]+$/);
|
|
5
|
-
const localConversationIdSchema = z
|
|
6
|
-
.string()
|
|
7
|
-
.regex(/^local:[a-z0-9_-]+:[a-z0-9][a-z0-9_-]*$/);
|
|
8
|
-
/** Exact non-blank actor user id that rejects the sentinel "unknown". */
|
|
9
|
-
export const actorUserIdSchema = z
|
|
10
|
-
.string()
|
|
11
|
-
.min(1)
|
|
12
|
-
.refine(
|
|
13
|
-
(value) => value === value.trim() && value.toLowerCase() !== "unknown",
|
|
14
|
-
);
|
|
15
|
-
const exactActorUserIdSchema = actorUserIdSchema;
|
|
16
|
-
|
|
17
|
-
export const nonBlankStringSchema = z
|
|
18
|
-
.string()
|
|
19
|
-
.refine((value) => value.trim().length > 0);
|
|
20
|
-
const exactNonBlankStringSchema = nonBlankStringSchema.refine(
|
|
21
|
-
(value) => value === value.trim(),
|
|
22
|
-
);
|
|
23
|
-
|
|
24
|
-
/** Fields shared by every Location. */
|
|
25
|
-
const baseLocationSchema = z
|
|
26
|
-
.object({
|
|
27
|
-
/** Stable SQL identity used by Location configuration and reporting. */
|
|
28
|
-
id: nonBlankStringSchema,
|
|
29
|
-
provider: exactNonBlankStringSchema,
|
|
30
|
-
})
|
|
31
|
-
.strict();
|
|
32
|
-
|
|
33
|
-
/** Complete Slack Location associated with a Conversation. */
|
|
34
|
-
export const slackLocationSchema = baseLocationSchema
|
|
35
|
-
.extend({
|
|
36
|
-
provider: z.literal("slack"),
|
|
37
|
-
teamId: slackTeamIdSchema,
|
|
38
|
-
channelId: slackConversationIdSchema,
|
|
39
|
-
threadTs: exactNonBlankStringSchema.optional(),
|
|
40
|
-
})
|
|
41
|
-
.strict();
|
|
42
|
-
|
|
43
|
-
/** Complete Location types supported by Junior. */
|
|
44
|
-
export const locationSchema = z.discriminatedUnion("provider", [
|
|
45
|
-
slackLocationSchema,
|
|
46
|
-
]);
|
|
47
|
-
|
|
48
|
-
/** Runtime platform names supported by plugin public contracts. */
|
|
49
|
-
export const platformSchema = z.enum(["slack", "local"]);
|
|
50
|
-
|
|
51
|
-
/** Runtime source visibility visible to plugins. */
|
|
52
|
-
export const sourceVisibilitySchema = z.enum(["public", "private"]);
|
|
53
|
-
|
|
54
|
-
/** Provider-neutral visibility of a routed destination. */
|
|
55
|
-
export const destinationVisibilitySchema = z.enum(["public", "private"]);
|
|
56
|
-
|
|
57
|
-
const slackAddressSchema = z
|
|
58
|
-
.object({
|
|
59
|
-
platform: z.literal("slack"),
|
|
60
|
-
teamId: slackTeamIdSchema,
|
|
61
|
-
channelId: slackConversationIdSchema,
|
|
62
|
-
})
|
|
63
|
-
.strict();
|
|
64
|
-
|
|
65
|
-
/** Runtime-owned Slack address for routing future work or side effects. */
|
|
66
|
-
export const slackDestinationSchema = slackAddressSchema;
|
|
67
|
-
|
|
68
|
-
/** Runtime-owned local CLI conversation address. */
|
|
69
|
-
export const localDestinationSchema = z
|
|
70
|
-
.object({
|
|
71
|
-
platform: z.literal("local"),
|
|
72
|
-
conversationId: localConversationIdSchema,
|
|
73
|
-
})
|
|
74
|
-
.strict();
|
|
75
|
-
|
|
76
|
-
/** Runtime-owned provider-neutral address for routing future work or side effects. */
|
|
77
|
-
export const destinationSchema = z.discriminatedUnion("platform", [
|
|
78
|
-
slackDestinationSchema,
|
|
79
|
-
localDestinationSchema,
|
|
80
|
-
]);
|
|
81
|
-
|
|
82
|
-
/** Runtime-owned Slack input Source. */
|
|
83
|
-
export const slackSourceSchema = z
|
|
84
|
-
.object({
|
|
85
|
-
kind: z.literal("slack"),
|
|
86
|
-
teamId: slackTeamIdSchema,
|
|
87
|
-
channelId: slackConversationIdSchema,
|
|
88
|
-
visibility: sourceVisibilitySchema,
|
|
89
|
-
messageTs: nonBlankStringSchema.optional(),
|
|
90
|
-
threadTs: nonBlankStringSchema.optional(),
|
|
91
|
-
})
|
|
92
|
-
.strict();
|
|
93
|
-
|
|
94
|
-
/** Runtime-owned local CLI input Source. */
|
|
95
|
-
export const localSourceSchema = z
|
|
96
|
-
.object({
|
|
97
|
-
kind: z.literal("local"),
|
|
98
|
-
visibility: z.literal("private"),
|
|
99
|
-
conversationId: localConversationIdSchema,
|
|
100
|
-
})
|
|
101
|
-
.strict();
|
|
102
|
-
|
|
103
|
-
/** Runtime-owned dashboard input Source. */
|
|
104
|
-
export const webSourceSchema = z
|
|
105
|
-
.object({
|
|
106
|
-
kind: z.literal("web"),
|
|
107
|
-
visibility: sourceVisibilitySchema,
|
|
108
|
-
// Web can continue any existing conversation id, including Slack roots.
|
|
109
|
-
conversationId: exactNonBlankStringSchema,
|
|
110
|
-
})
|
|
111
|
-
.strict();
|
|
112
|
-
|
|
113
|
-
/** Runtime-owned Resource event input Source. */
|
|
114
|
-
export const resourceEventSourceSchema = z
|
|
115
|
-
.object({
|
|
116
|
-
kind: z.literal("resource_event"),
|
|
117
|
-
eventKey: exactNonBlankStringSchema,
|
|
118
|
-
eventType: exactNonBlankStringSchema,
|
|
119
|
-
identifier: exactNonBlankStringSchema,
|
|
120
|
-
namespace: exactNonBlankStringSchema,
|
|
121
|
-
})
|
|
122
|
-
.strict();
|
|
123
|
-
|
|
124
|
-
/** Runtime-owned Scheduled task input Source. */
|
|
125
|
-
export const scheduledTaskSourceSchema = z
|
|
126
|
-
.object({ kind: z.literal("scheduled_task") })
|
|
127
|
-
.strict();
|
|
128
|
-
|
|
129
|
-
/** Runtime-owned Event task input Source. */
|
|
130
|
-
export const eventTaskSourceSchema = z
|
|
131
|
-
.object({ kind: z.literal("event_task") })
|
|
132
|
-
.strict();
|
|
133
|
-
|
|
134
|
-
/** Runtime-owned Plugin dispatch input Source. */
|
|
135
|
-
export const pluginDispatchSourceSchema = z
|
|
136
|
-
.object({ kind: z.literal("plugin_dispatch") })
|
|
137
|
-
.strict();
|
|
138
|
-
|
|
139
|
-
/** Runtime-owned Agent invocation input Source. */
|
|
140
|
-
export const agentInvocationSourceSchema = z
|
|
141
|
-
.object({ kind: z.literal("agent_invocation") })
|
|
142
|
-
.strict();
|
|
143
|
-
|
|
144
|
-
const currentSourceSchema = z.discriminatedUnion("kind", [
|
|
145
|
-
slackSourceSchema,
|
|
146
|
-
localSourceSchema,
|
|
147
|
-
webSourceSchema,
|
|
148
|
-
resourceEventSourceSchema,
|
|
149
|
-
scheduledTaskSourceSchema,
|
|
150
|
-
eventTaskSourceSchema,
|
|
151
|
-
pluginDispatchSourceSchema,
|
|
152
|
-
agentInvocationSourceSchema,
|
|
153
|
-
]);
|
|
154
|
-
|
|
155
|
-
function normalizeStoredSource(value: unknown): unknown {
|
|
156
|
-
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
157
|
-
return value;
|
|
158
|
-
}
|
|
159
|
-
const source = value as Record<string, unknown>;
|
|
160
|
-
if ("kind" in source || !("platform" in source)) {
|
|
161
|
-
return value;
|
|
162
|
-
}
|
|
163
|
-
if (
|
|
164
|
-
source.platform !== "slack" &&
|
|
165
|
-
source.platform !== "local" &&
|
|
166
|
-
source.platform !== "web"
|
|
167
|
-
) {
|
|
168
|
-
return value;
|
|
169
|
-
}
|
|
170
|
-
const { location: _location, platform: kind, ...fields } = source;
|
|
171
|
-
return { ...fields, kind };
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
/** Runtime-owned input Source. Stored `platform` and Location fields normalize on read. */
|
|
175
|
-
// TODO(dcramer): Remove stored Source normalization after deployed SQL, Redis,
|
|
176
|
-
// queue, and OAuth values can only contain `kind` and no Location copy.
|
|
177
|
-
export const sourceSchema = z.preprocess(
|
|
178
|
-
normalizeStoredSource,
|
|
179
|
-
currentSourceSchema,
|
|
180
|
-
);
|
|
181
|
-
|
|
182
|
-
/** Stable user credential subject shape accepted from plugins. */
|
|
183
|
-
export const pluginCredentialSubjectSchema = z.discriminatedUnion(
|
|
184
|
-
"allowedWhen",
|
|
185
|
-
[
|
|
186
|
-
z
|
|
187
|
-
.object({
|
|
188
|
-
type: z.literal("user"),
|
|
189
|
-
userId: exactActorUserIdSchema,
|
|
190
|
-
allowedWhen: z.literal("private-direct-conversation"),
|
|
191
|
-
})
|
|
192
|
-
.strict(),
|
|
193
|
-
z
|
|
194
|
-
.object({
|
|
195
|
-
type: z.literal("user"),
|
|
196
|
-
userId: exactActorUserIdSchema,
|
|
197
|
-
allowedWhen: z.literal("scheduled-task"),
|
|
198
|
-
taskId: exactNonBlankStringSchema,
|
|
199
|
-
})
|
|
200
|
-
.strict(),
|
|
201
|
-
],
|
|
202
|
-
);
|
|
203
|
-
|
|
204
|
-
/** Shared exact actor profile fields for platform-scoped actors. */
|
|
205
|
-
const actorProfileSchema = {
|
|
206
|
-
email: nonBlankStringSchema.optional(),
|
|
207
|
-
fullName: nonBlankStringSchema.optional(),
|
|
208
|
-
userId: exactActorUserIdSchema,
|
|
209
|
-
userName: nonBlankStringSchema.optional(),
|
|
210
|
-
};
|
|
211
|
-
|
|
212
|
-
export const slackActorSchema = z
|
|
213
|
-
.object({
|
|
214
|
-
...actorProfileSchema,
|
|
215
|
-
platform: z.literal("slack"),
|
|
216
|
-
teamId: slackTeamIdSchema,
|
|
217
|
-
})
|
|
218
|
-
.strict();
|
|
219
|
-
|
|
220
|
-
export const localActorSchema = z
|
|
221
|
-
.object({
|
|
222
|
-
...actorProfileSchema,
|
|
223
|
-
platform: z.literal("local"),
|
|
224
|
-
})
|
|
225
|
-
.strict();
|
|
226
|
-
|
|
227
|
-
export const webActorSchema = z
|
|
228
|
-
.object({
|
|
229
|
-
...actorProfileSchema,
|
|
230
|
-
platform: z.literal("web"),
|
|
231
|
-
})
|
|
232
|
-
.strict();
|
|
233
|
-
|
|
234
|
-
export const systemActorSchema = z
|
|
235
|
-
.object({
|
|
236
|
-
platform: z.literal("system"),
|
|
237
|
-
name: exactActorUserIdSchema,
|
|
238
|
-
})
|
|
239
|
-
.strict();
|
|
240
|
-
|
|
241
|
-
// TODO(dcramer): Separate actor kind from provider platform.
|
|
242
|
-
// System actors should not use `platform: "system"`.
|
|
243
|
-
/** Runtime-provided actor identity visible to plugin hooks. */
|
|
244
|
-
export const actorSchema = z.discriminatedUnion("platform", [
|
|
245
|
-
slackActorSchema,
|
|
246
|
-
localActorSchema,
|
|
247
|
-
webActorSchema,
|
|
248
|
-
systemActorSchema,
|
|
249
|
-
]);
|
|
250
|
-
|
|
251
|
-
/** Core-owned provider account linked to a user when verified. */
|
|
252
|
-
export const identitySchema = z
|
|
253
|
-
.object({
|
|
254
|
-
displayName: nonBlankStringSchema.optional(),
|
|
255
|
-
handle: nonBlankStringSchema.optional(),
|
|
256
|
-
id: exactNonBlankStringSchema,
|
|
257
|
-
provider: exactNonBlankStringSchema,
|
|
258
|
-
providerSubjectId: exactNonBlankStringSchema,
|
|
259
|
-
providerTenantId: exactNonBlankStringSchema.optional(),
|
|
260
|
-
})
|
|
261
|
-
.strict();
|
|
262
|
-
|
|
263
|
-
/** Core-owned person with every linked provider identity. */
|
|
264
|
-
export const userSchema = z
|
|
265
|
-
.object({
|
|
266
|
-
displayName: nonBlankStringSchema.optional(),
|
|
267
|
-
email: nonBlankStringSchema.max(320),
|
|
268
|
-
id: exactNonBlankStringSchema,
|
|
269
|
-
identities: z.array(identitySchema).max(100),
|
|
270
|
-
})
|
|
271
|
-
.strict();
|
|
272
|
-
|
|
273
|
-
const dispatchMetadataSchema = z
|
|
274
|
-
.record(z.string(), z.string())
|
|
275
|
-
.superRefine((metadata, ctx) => {
|
|
276
|
-
const entries = Object.entries(metadata);
|
|
277
|
-
if (entries.length > 20) {
|
|
278
|
-
ctx.addIssue({
|
|
279
|
-
code: z.ZodIssueCode.custom,
|
|
280
|
-
message: "Dispatch metadata has too many keys",
|
|
281
|
-
});
|
|
282
|
-
return;
|
|
283
|
-
}
|
|
284
|
-
for (const [key, value] of entries) {
|
|
285
|
-
if (!key.trim()) {
|
|
286
|
-
ctx.addIssue({
|
|
287
|
-
code: z.ZodIssueCode.custom,
|
|
288
|
-
message: "Dispatch metadata values must be strings",
|
|
289
|
-
path: [key],
|
|
290
|
-
});
|
|
291
|
-
continue;
|
|
292
|
-
}
|
|
293
|
-
if (key.length > 128) {
|
|
294
|
-
ctx.addIssue({
|
|
295
|
-
code: z.ZodIssueCode.custom,
|
|
296
|
-
message: "Dispatch metadata key exceeds the maximum length",
|
|
297
|
-
path: [key],
|
|
298
|
-
});
|
|
299
|
-
}
|
|
300
|
-
if (/[\r\n]/.test(key)) {
|
|
301
|
-
ctx.addIssue({
|
|
302
|
-
code: z.ZodIssueCode.custom,
|
|
303
|
-
message: "Dispatch metadata keys must be single-line strings",
|
|
304
|
-
path: [key],
|
|
305
|
-
});
|
|
306
|
-
}
|
|
307
|
-
if (/[\r\n]/.test(value)) {
|
|
308
|
-
ctx.addIssue({
|
|
309
|
-
code: z.ZodIssueCode.custom,
|
|
310
|
-
message: "Dispatch metadata values must be single-line strings",
|
|
311
|
-
path: [key],
|
|
312
|
-
});
|
|
313
|
-
}
|
|
314
|
-
if (value.length > 512) {
|
|
315
|
-
ctx.addIssue({
|
|
316
|
-
code: z.ZodIssueCode.custom,
|
|
317
|
-
message: "Dispatch metadata value exceeds the maximum length",
|
|
318
|
-
path: [key],
|
|
319
|
-
});
|
|
320
|
-
}
|
|
321
|
-
}
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
/** Compact destination-visible context explaining what produced a reply. */
|
|
325
|
-
export const replyAttributionSchema = z
|
|
326
|
-
.object({
|
|
327
|
-
label: exactNonBlankStringSchema
|
|
328
|
-
.pipe(z.string().max(48))
|
|
329
|
-
.refine((value) => !/[\r\n]/.test(value)),
|
|
330
|
-
detail: exactNonBlankStringSchema
|
|
331
|
-
.pipe(z.string().max(128))
|
|
332
|
-
.refine((value) => !/[\r\n]/.test(value))
|
|
333
|
-
.optional(),
|
|
334
|
-
})
|
|
335
|
-
.strict();
|
|
336
|
-
|
|
337
|
-
/** Plugin dispatch request accepted by Junior core. */
|
|
338
|
-
export const dispatchOptionsSchema = z
|
|
339
|
-
.object({
|
|
340
|
-
idempotencyKey: nonBlankStringSchema.pipe(z.string().max(512)),
|
|
341
|
-
credentialSubject: pluginCredentialSubjectSchema.optional(),
|
|
342
|
-
destination: slackDestinationSchema,
|
|
343
|
-
destinationVisibility: destinationVisibilitySchema,
|
|
344
|
-
input: nonBlankStringSchema.pipe(z.string().max(32_000)),
|
|
345
|
-
metadata: dispatchMetadataSchema.optional(),
|
|
346
|
-
replyAttribution: replyAttributionSchema.optional(),
|
|
347
|
-
})
|
|
348
|
-
.strict();
|
package/src/state.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
export interface PluginState {
|
|
2
|
-
delete(key: string): Promise<void>;
|
|
3
|
-
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
4
|
-
set(key: string, value: unknown, ttlMs?: number): Promise<void>;
|
|
5
|
-
setIfNotExists(key: string, value: unknown, ttlMs?: number): Promise<boolean>;
|
|
6
|
-
withLock<T>(
|
|
7
|
-
key: string,
|
|
8
|
-
ttlMs: number,
|
|
9
|
-
callback: () => Promise<T>,
|
|
10
|
-
): Promise<T>;
|
|
11
|
-
}
|
|
12
|
-
|
|
13
|
-
export interface PluginReadState {
|
|
14
|
-
get<T = unknown>(key: string): Promise<T | undefined>;
|
|
15
|
-
}
|