@sentry/junior 0.7.0 → 0.9.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.
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  POST
3
- } from "../chunk-QHKQ2AWX.js";
4
- import "../chunk-PY4AI2GZ.js";
3
+ } from "../chunk-I3DYWLM6.js";
4
+ import "../chunk-ZW4OVKF5.js";
5
5
  export {
6
6
  POST
7
7
  };
@@ -2,7 +2,7 @@ import {
2
2
  discoverInstalledPluginPackageContent,
3
3
  discoverNodeModulesDirs,
4
4
  isDirectory
5
- } from "./chunk-Z5E25LRN.js";
5
+ } from "./chunk-KCLEEKYX.js";
6
6
 
7
7
  // src/next-config.ts
8
8
  import { createRequire } from "module";
@@ -0,0 +1,15 @@
1
+ import {
2
+ bot,
3
+ createNormalizingStream,
4
+ slackRuntime
5
+ } from "./chunk-DIMXJUSL.js";
6
+ import "./chunk-VM3CPAZF.js";
7
+ import "./chunk-IJVZEV3K.js";
8
+ import "./chunk-ZBWWHP6Q.js";
9
+ import "./chunk-KCLEEKYX.js";
10
+ import "./chunk-ZW4OVKF5.js";
11
+ export {
12
+ bot,
13
+ createNormalizingStream,
14
+ slackRuntime
15
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -27,16 +27,17 @@
27
27
  "@chat-adapter/slack": "4.20.2",
28
28
  "@chat-adapter/state-memory": "4.20.2",
29
29
  "@chat-adapter/state-redis": "4.20.2",
30
- "@mariozechner/pi-agent-core": "^0.56.3",
31
- "@mariozechner/pi-ai": "^0.56.3",
30
+ "@mariozechner/pi-agent-core": "^0.59.0",
31
+ "@mariozechner/pi-ai": "^0.59.0",
32
+ "@modelcontextprotocol/sdk": "1.27.1",
32
33
  "@sinclair/typebox": "^0.34.48",
33
- "@slack/web-api": "^7.14.1",
34
- "@vercel/queue": "^0.1.3",
35
- "@vercel/sandbox": "^1.8.0",
34
+ "@slack/web-api": "^7.15.0",
35
+ "@vercel/queue": "^0.1.4",
36
+ "@vercel/sandbox": "^1.8.1",
36
37
  "ai": "^6.0.116",
37
38
  "bash-tool": "^1.3.15",
38
39
  "chat": "4.20.2",
39
- "just-bash": "^2.12.0",
40
+ "just-bash": "^2.13.1",
40
41
  "node-html-markdown": "^2.0.0",
41
42
  "yaml": "^2.8.2",
42
43
  "zod": "^4.3.6"
@@ -48,26 +49,23 @@
48
49
  "react-dom": ">=19.0.0"
49
50
  },
50
51
  "devDependencies": {
51
- "@sentry/nextjs": "^10.42.0",
52
+ "@sentry/nextjs": "^10.44.0",
52
53
  "@types/node": "^25.3.5",
53
54
  "@types/react": "^19.2.14",
54
55
  "@types/react-dom": "^19.2.3",
55
- "msw": "^2.12.10",
56
- "next": "^16.1.6",
56
+ "msw": "^2.12.13",
57
+ "next": "^16.1.7",
57
58
  "react": "^19.2.4",
58
59
  "react-dom": "^19.2.4",
59
60
  "tsup": "^8.5.1",
60
61
  "typescript": "^5.9.3",
61
- "vercel": "^50.28.0",
62
- "vitest": "^4.0.18",
63
- "vitest-evals": "^0.6.0"
62
+ "vercel": "^50.32.5",
63
+ "vitest": "^4.1.0"
64
64
  },
65
65
  "scripts": {
66
66
  "build": "tsup",
67
67
  "test": "pnpm run test:slack-boundary && vitest run",
68
68
  "test:watch": "vitest",
69
- "preevals": "pnpm run test:slack-boundary",
70
- "evals": "JUNIOR_STATE_ADAPTER=memory pnpm exec vitest run -c vitest.evals.config.ts",
71
69
  "test:slack-boundary": "node scripts/check-slack-test-boundary.mjs",
72
70
  "typecheck": "tsc --noEmit",
73
71
  "skills:check": "node scripts/check-skills.mjs"
@@ -1,19 +0,0 @@
1
- import {
2
- appSlackRuntime,
3
- bot,
4
- createNormalizingStream,
5
- resetBotDepsForTests,
6
- setBotDepsForTests
7
- } from "./chunk-JRKU55W5.js";
8
- import "./chunk-KT5HARSN.js";
9
- import "./chunk-RKOO42TW.js";
10
- import "./chunk-Z5E25LRN.js";
11
- import "./chunk-PY4AI2GZ.js";
12
- import "./chunk-VW26MOSO.js";
13
- export {
14
- appSlackRuntime,
15
- bot,
16
- createNormalizingStream,
17
- resetBotDepsForTests,
18
- setBotDepsForTests
19
- };
@@ -1,330 +0,0 @@
1
- import {
2
- appSlackRuntime,
3
- createQueueCallbackHandler,
4
- downloadPrivateSlackFile,
5
- getThreadMessageTopic,
6
- removeReactionFromMessage
7
- } from "./chunk-JRKU55W5.js";
8
- import {
9
- acquireQueueMessageProcessingOwnership,
10
- completeQueueMessageProcessingOwnership,
11
- failQueueMessageProcessingOwnership,
12
- getQueueMessageProcessingState,
13
- getStateAdapter,
14
- refreshQueueMessageProcessingOwnership
15
- } from "./chunk-RKOO42TW.js";
16
- import {
17
- createRequestContext,
18
- logError,
19
- logException,
20
- logInfo,
21
- logWarn,
22
- setSpanStatus,
23
- withContext,
24
- withSpan
25
- } from "./chunk-PY4AI2GZ.js";
26
-
27
- // src/chat/queue/process-thread-message.ts
28
- import { Message, ThreadImpl } from "chat";
29
-
30
- // src/chat/thread-runtime/process-thread-message-runtime.ts
31
- function rehydrateAttachmentFetchers(payload) {
32
- for (const attachment of payload.message.attachments) {
33
- if (!attachment.fetchData && attachment.url) {
34
- attachment.fetchData = () => downloadPrivateSlackFile(attachment.url);
35
- }
36
- }
37
- }
38
- async function processThreadMessageRuntime(args) {
39
- const runtimePayload = {
40
- message: args.message,
41
- thread: args.thread
42
- };
43
- rehydrateAttachmentFetchers(runtimePayload);
44
- if (args.kind === "new_mention") {
45
- await appSlackRuntime.handleNewMention(args.thread, args.message, {
46
- beforeFirstResponsePost: args.beforeFirstResponsePost
47
- });
48
- return;
49
- }
50
- if (args.kind === "subscribed_reply") {
51
- await appSlackRuntime.handleSubscribedMessage(args.thread, args.message, {
52
- beforeFirstResponsePost: args.beforeFirstResponsePost,
53
- preApprovedReply: true
54
- });
55
- return;
56
- }
57
- await appSlackRuntime.handleSubscribedMessage(args.thread, args.message, {
58
- beforeFirstResponsePost: args.beforeFirstResponsePost
59
- });
60
- }
61
-
62
- // src/chat/queue/process-thread-message.ts
63
- var stateAdapterConnected = false;
64
- function isSerializedThread(thread) {
65
- return typeof thread === "object" && thread !== null && thread._type === "chat:Thread";
66
- }
67
- function isSerializedMessage(message) {
68
- return typeof message === "object" && message !== null && message._type === "chat:Message";
69
- }
70
- function getPayloadChannelId(payload) {
71
- return payload.thread.channelId;
72
- }
73
- function getPayloadUserId(payload) {
74
- return payload.message.author?.userId;
75
- }
76
- function createMessageOwnerToken() {
77
- return `msg-${Date.now()}-${Math.random().toString(16).slice(2)}`;
78
- }
79
- var QueueMessageOwnershipError = class extends Error {
80
- constructor(stage, dedupKey) {
81
- super(
82
- `Queue message ownership lost during ${stage} for dedupKey=${dedupKey}`
83
- );
84
- this.name = "QueueMessageOwnershipError";
85
- }
86
- };
87
- var defaultProcessQueuedThreadMessageDeps = {
88
- clearProcessingReaction: async ({ channelId, timestamp }) => {
89
- await removeReactionFromMessage({
90
- channelId,
91
- timestamp,
92
- emoji: "eyes"
93
- });
94
- },
95
- logInfo,
96
- logWarn,
97
- processRuntime: processThreadMessageRuntime
98
- };
99
- function deserializeThread(thread) {
100
- if (isSerializedThread(thread)) {
101
- return ThreadImpl.fromJSON(thread);
102
- }
103
- return thread;
104
- }
105
- function deserializeMessage(message) {
106
- if (isSerializedMessage(message)) {
107
- return Message.fromJSON(message);
108
- }
109
- return message;
110
- }
111
- async function logThreadMessageFailure(payload, errorMessage) {
112
- logError(
113
- "queue_message_failed",
114
- {
115
- slackThreadId: payload.normalizedThreadId,
116
- slackChannelId: getPayloadChannelId(payload),
117
- slackUserId: getPayloadUserId(payload)
118
- },
119
- {
120
- "messaging.message.id": payload.message.id,
121
- "app.queue.message_kind": payload.kind,
122
- "app.queue.message_id": payload.queueMessageId,
123
- "error.message": errorMessage
124
- },
125
- "Queue message processing failed"
126
- );
127
- }
128
- async function processQueuedThreadMessage(payload, deps = defaultProcessQueuedThreadMessageDeps) {
129
- const existingMessageState = await getQueueMessageProcessingState(
130
- payload.dedupKey
131
- );
132
- if (existingMessageState?.status === "completed") {
133
- deps.logInfo(
134
- "queue_message_skipped_completed",
135
- {
136
- slackThreadId: payload.normalizedThreadId,
137
- slackChannelId: getPayloadChannelId(payload),
138
- slackUserId: getPayloadUserId(payload)
139
- },
140
- {
141
- "messaging.message.id": payload.message.id,
142
- "app.queue.message_kind": payload.kind,
143
- "app.queue.message_id": payload.queueMessageId,
144
- "app.queue.processing_state": existingMessageState.status
145
- },
146
- "Skipping queue message because it is already completed"
147
- );
148
- return;
149
- }
150
- const ownerToken = createMessageOwnerToken();
151
- const claimResult = await acquireQueueMessageProcessingOwnership({
152
- rawKey: payload.dedupKey,
153
- ownerToken,
154
- queueMessageId: payload.queueMessageId
155
- });
156
- if (claimResult === "blocked") {
157
- deps.logInfo(
158
- "queue_message_skipped_blocked",
159
- {
160
- slackThreadId: payload.normalizedThreadId,
161
- slackChannelId: getPayloadChannelId(payload),
162
- slackUserId: getPayloadUserId(payload)
163
- },
164
- {
165
- "messaging.message.id": payload.message.id,
166
- "app.queue.message_kind": payload.kind,
167
- "app.queue.message_id": payload.queueMessageId,
168
- "app.queue.claim_result": claimResult,
169
- "app.queue.processing_state": "processing"
170
- },
171
- "Skipping queue message because another worker owns it"
172
- );
173
- return;
174
- }
175
- const threadWasSerialized = isSerializedThread(payload.thread);
176
- if (threadWasSerialized && !stateAdapterConnected) {
177
- await getStateAdapter().connect();
178
- stateAdapterConnected = true;
179
- }
180
- const runtimePayload = {
181
- ...payload,
182
- thread: deserializeThread(payload.thread),
183
- message: deserializeMessage(payload.message)
184
- };
185
- let reactionCleared = false;
186
- const clearProcessingReaction = async () => {
187
- if (reactionCleared) {
188
- return;
189
- }
190
- reactionCleared = true;
191
- try {
192
- await deps.clearProcessingReaction({
193
- channelId: runtimePayload.thread.channelId,
194
- timestamp: runtimePayload.message.id
195
- });
196
- } catch (error) {
197
- const errorMessage = error instanceof Error ? error.message : String(error);
198
- deps.logWarn(
199
- "queue_processing_reaction_clear_failed",
200
- {
201
- slackThreadId: payload.normalizedThreadId,
202
- slackChannelId: getPayloadChannelId(payload),
203
- slackUserId: getPayloadUserId(payload)
204
- },
205
- {
206
- "messaging.message.id": payload.message.id,
207
- "app.queue.message_kind": payload.kind,
208
- "app.queue.message_id": payload.queueMessageId,
209
- "error.message": errorMessage
210
- },
211
- "Failed to remove processing reaction after queue turn completion"
212
- );
213
- }
214
- };
215
- try {
216
- const refreshed = await refreshQueueMessageProcessingOwnership({
217
- rawKey: payload.dedupKey,
218
- ownerToken,
219
- queueMessageId: payload.queueMessageId
220
- });
221
- if (!refreshed) {
222
- throw new QueueMessageOwnershipError("refresh", payload.dedupKey);
223
- }
224
- await deps.processRuntime({
225
- kind: runtimePayload.kind,
226
- thread: runtimePayload.thread,
227
- message: runtimePayload.message
228
- });
229
- await clearProcessingReaction();
230
- const completed = await completeQueueMessageProcessingOwnership({
231
- rawKey: payload.dedupKey,
232
- ownerToken,
233
- queueMessageId: payload.queueMessageId
234
- });
235
- if (!completed) {
236
- throw new QueueMessageOwnershipError("complete", payload.dedupKey);
237
- }
238
- } catch (error) {
239
- const errorMessage = error instanceof Error ? error.message : String(error);
240
- await clearProcessingReaction();
241
- await logThreadMessageFailure(payload, errorMessage);
242
- const failed = await failQueueMessageProcessingOwnership({
243
- rawKey: payload.dedupKey,
244
- ownerToken,
245
- errorMessage,
246
- queueMessageId: payload.queueMessageId
247
- });
248
- if (!failed && !(error instanceof QueueMessageOwnershipError)) {
249
- throw new Error(
250
- `Failed to persist queue message failure state for dedupKey=${payload.dedupKey}: ${errorMessage}`
251
- );
252
- }
253
- throw error;
254
- }
255
- }
256
-
257
- // src/handlers/queue-callback.ts
258
- var callbackHandler = createQueueCallbackHandler(
259
- async (message, metadata) => {
260
- if (metadata.topicName === getThreadMessageTopic()) {
261
- const payload = {
262
- ...message,
263
- queueMessageId: metadata.messageId
264
- };
265
- logInfo(
266
- "queue_callback_received",
267
- {
268
- slackThreadId: payload.normalizedThreadId,
269
- slackChannelId: payload.thread.channelId,
270
- slackUserId: payload.message.author?.userId
271
- },
272
- {
273
- "messaging.message.id": payload.message.id,
274
- "app.queue.message_kind": payload.kind,
275
- "app.queue.message_id": payload.queueMessageId,
276
- "app.queue.delivery_count": metadata.deliveryCount,
277
- "app.queue.topic": metadata.topicName
278
- },
279
- "Received queue callback payload"
280
- );
281
- await withSpan(
282
- "queue.process_message",
283
- "queue.process_message",
284
- {
285
- slackThreadId: payload.normalizedThreadId,
286
- slackChannelId: payload.thread.channelId,
287
- slackUserId: payload.message.author?.userId
288
- },
289
- async () => {
290
- await processQueuedThreadMessage(payload);
291
- },
292
- {
293
- "messaging.message.id": payload.message.id,
294
- "app.queue.message_kind": payload.kind,
295
- "app.queue.message_id": payload.queueMessageId,
296
- "app.queue.delivery_count": metadata.deliveryCount,
297
- "app.queue.topic": metadata.topicName
298
- }
299
- );
300
- return;
301
- }
302
- throw new Error(`Unexpected queue topic: ${metadata.topicName}`);
303
- }
304
- );
305
- async function POST(request) {
306
- const requestContext = createRequestContext(request, { platform: "queue" });
307
- return withContext(requestContext, async () => {
308
- try {
309
- const response = await callbackHandler(request);
310
- setSpanStatus(response.status >= 500 ? "error" : "ok");
311
- return response;
312
- } catch (error) {
313
- const message = error instanceof Error ? error.message : String(error);
314
- logError(
315
- "queue_callback_failed",
316
- {},
317
- {
318
- "error.message": message
319
- },
320
- "Queue callback processing failed"
321
- );
322
- logException(error, "queue_callback_failed");
323
- throw error;
324
- }
325
- });
326
- }
327
-
328
- export {
329
- POST
330
- };
@@ -1,164 +0,0 @@
1
- // src/chat/skill-frontmatter.ts
2
- import { z } from "zod";
3
- import { parse as parseYaml } from "yaml";
4
- var FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
5
- var SKILL_NAME_RE = /^[a-z0-9-]+$/;
6
- var CAPABILITY_TOKEN_RE = /^[a-z0-9]+(?:\.[a-z0-9-]+)+$/;
7
- var MAX_NAME_LENGTH = 64;
8
- var MAX_DESCRIPTION_LENGTH = 1024;
9
- var MAX_COMPATIBILITY_LENGTH = 500;
10
- function hasAngleBrackets(value) {
11
- return value.includes("<") || value.includes(">");
12
- }
13
- function validateSkillName(name) {
14
- if (!name) return "name must not be empty";
15
- if (name.length > MAX_NAME_LENGTH)
16
- return `name must be <= ${MAX_NAME_LENGTH} characters`;
17
- if (!SKILL_NAME_RE.test(name))
18
- return "name must contain only lowercase letters, digits, and hyphens";
19
- if (name.startsWith("-") || name.endsWith("-"))
20
- return "name must not start or end with a hyphen";
21
- if (name.includes("--")) return "name must not contain consecutive hyphens";
22
- return null;
23
- }
24
- function createTokenFieldSchema(fieldName, example) {
25
- return z.string({
26
- error: `Frontmatter field "${fieldName}" must be a string when present`
27
- }).superRefine((value, ctx) => {
28
- const tokens = value.split(/\s+/).map((token) => token.trim()).filter((token) => token.length > 0);
29
- for (const token of tokens) {
30
- if (!CAPABILITY_TOKEN_RE.test(token)) {
31
- ctx.addIssue({
32
- code: z.ZodIssueCode.custom,
33
- message: `${fieldName} token "${token}" is invalid; expected dotted lowercase tokens (for example "${example}")`
34
- });
35
- return;
36
- }
37
- }
38
- });
39
- }
40
- function parseTokenList(value) {
41
- if (typeof value !== "string") {
42
- return void 0;
43
- }
44
- const tokens = value.split(/\s+/).map((token) => token.trim()).filter((token) => token.length > 0);
45
- return tokens.length > 0 ? tokens : void 0;
46
- }
47
- var skillFrontmatterSchema = z.object({
48
- name: z.string({ error: 'Frontmatter field "name" must be a string' }).superRefine((value, ctx) => {
49
- const nameError = validateSkillName(value);
50
- if (nameError) {
51
- ctx.addIssue({
52
- code: z.ZodIssueCode.custom,
53
- message: nameError
54
- });
55
- }
56
- }),
57
- description: z.string({ error: 'Frontmatter field "description" must be a string' }).superRefine((value, ctx) => {
58
- if (!value.trim()) {
59
- ctx.addIssue({
60
- code: z.ZodIssueCode.custom,
61
- message: "description must not be empty"
62
- });
63
- return;
64
- }
65
- if (value.length > MAX_DESCRIPTION_LENGTH) {
66
- ctx.addIssue({
67
- code: z.ZodIssueCode.custom,
68
- message: `description must be <= ${MAX_DESCRIPTION_LENGTH} characters`
69
- });
70
- return;
71
- }
72
- if (hasAngleBrackets(value)) {
73
- ctx.addIssue({
74
- code: z.ZodIssueCode.custom,
75
- message: 'description must not contain "<" or ">"'
76
- });
77
- }
78
- }),
79
- metadata: z.record(z.string(), z.unknown(), {
80
- error: 'Frontmatter field "metadata" must be an object when present'
81
- }).optional(),
82
- compatibility: z.string({
83
- error: 'Frontmatter field "compatibility" must be a string when present'
84
- }).superRefine((value, ctx) => {
85
- if (value.length > MAX_COMPATIBILITY_LENGTH) {
86
- ctx.addIssue({
87
- code: z.ZodIssueCode.custom,
88
- message: `compatibility must be <= ${MAX_COMPATIBILITY_LENGTH} characters`
89
- });
90
- }
91
- }).optional(),
92
- license: z.string({
93
- error: 'Frontmatter field "license" must be a string when present'
94
- }).optional(),
95
- "allowed-tools": z.string({
96
- error: 'Frontmatter field "allowed-tools" must be a string when present'
97
- }).optional(),
98
- "requires-capabilities": createTokenFieldSchema(
99
- "requires-capabilities",
100
- "github.issues.write"
101
- ).optional(),
102
- "uses-config": createTokenFieldSchema(
103
- "uses-config",
104
- "github.repo"
105
- ).optional()
106
- }).passthrough();
107
- function stripFrontmatter(raw) {
108
- return raw.replace(FRONTMATTER_RE, "").trim();
109
- }
110
- function parseSkillFile(raw, expectedName) {
111
- const match = FRONTMATTER_RE.exec(raw);
112
- if (!match) {
113
- return { ok: false, error: "Missing YAML frontmatter at start of file" };
114
- }
115
- let parsed;
116
- try {
117
- parsed = parseYaml(match[1]);
118
- } catch (error) {
119
- return {
120
- ok: false,
121
- error: `Invalid YAML frontmatter: ${error instanceof Error ? error.message : String(error)}`
122
- };
123
- }
124
- if (!parsed || typeof parsed !== "object" || Array.isArray(parsed)) {
125
- return { ok: false, error: "Frontmatter must be a YAML object" };
126
- }
127
- const result = skillFrontmatterSchema.safeParse(parsed);
128
- if (!result.success) {
129
- return {
130
- ok: false,
131
- error: result.error.issues[0]?.message ?? "Invalid YAML frontmatter"
132
- };
133
- }
134
- if (expectedName && result.data.name !== expectedName) {
135
- return {
136
- ok: false,
137
- error: `name "${result.data.name}" must match directory "${expectedName}"`
138
- };
139
- }
140
- const allowedTools = parseTokenList(result.data["allowed-tools"]);
141
- const requiresCapabilities = parseTokenList(
142
- result.data["requires-capabilities"]
143
- );
144
- const usesConfig = parseTokenList(result.data["uses-config"]);
145
- return {
146
- ok: true,
147
- skill: {
148
- name: result.data.name,
149
- description: result.data.description,
150
- body: stripFrontmatter(raw),
151
- ...result.data.metadata ? { metadata: result.data.metadata } : {},
152
- ...result.data.compatibility !== void 0 ? { compatibility: result.data.compatibility } : {},
153
- ...result.data.license !== void 0 ? { license: result.data.license } : {},
154
- ...allowedTools ? { allowedTools } : {},
155
- ...requiresCapabilities ? { requiresCapabilities } : {},
156
- ...usesConfig ? { usesConfig } : {}
157
- }
158
- };
159
- }
160
-
161
- export {
162
- stripFrontmatter,
163
- parseSkillFile
164
- };