@sentry/junior 0.28.0 → 0.29.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/app.js CHANGED
@@ -6,11 +6,17 @@ import {
6
6
  parseSkillInvocation
7
7
  } from "./chunk-ICIRAL6Y.js";
8
8
  import {
9
+ GEN_AI_PROVIDER_NAME,
10
+ MISSING_GATEWAY_CREDENTIALS_ERROR,
9
11
  SANDBOX_DATA_ROOT,
10
12
  SANDBOX_SKILLS_ROOT,
11
13
  SANDBOX_WORKSPACE_ROOT,
12
14
  botConfig,
13
15
  buildNonInteractiveShellScript,
16
+ completeObject,
17
+ completeText,
18
+ getGatewayApiKey,
19
+ getPiGatewayApiKeyOverride,
14
20
  getRuntimeDependencyProfileHash,
15
21
  getRuntimeMetadata,
16
22
  getSlackBotToken,
@@ -20,19 +26,18 @@ import {
20
26
  getStateAdapter,
21
27
  getVercelSandboxCredentials,
22
28
  isSnapshotMissingError,
29
+ resolveGatewayModel,
23
30
  resolveRuntimeDependencySnapshot,
24
31
  runNonInteractiveCommand,
25
32
  sandboxSkillDir,
26
- sandboxSkillFile,
27
- toOptionalTrimmed
28
- } from "./chunk-375D5V4U.js";
33
+ sandboxSkillFile
34
+ } from "./chunk-LEYD42MR.js";
29
35
  import {
30
36
  CredentialUnavailableError,
31
37
  buildOAuthTokenRequest,
32
38
  createChatSdkLogger,
33
39
  createPluginBroker,
34
40
  createRequestContext,
35
- extractGenAiUsageAttributes,
36
41
  extractGenAiUsageSummary,
37
42
  getActiveTraceId,
38
43
  getPluginDefinition,
@@ -2066,232 +2071,6 @@ function getTurnUserReplyAttachmentContext(message) {
2066
2071
  };
2067
2072
  }
2068
2073
 
2069
- // src/chat/pi/client.ts
2070
- import {
2071
- completeSimple,
2072
- getEnvApiKey,
2073
- getModels,
2074
- registerApiProvider
2075
- } from "@mariozechner/pi-ai";
2076
- import {
2077
- streamAnthropic,
2078
- streamSimpleAnthropic
2079
- } from "@mariozechner/pi-ai/anthropic";
2080
- registerApiProvider({
2081
- api: "anthropic-messages",
2082
- stream: streamAnthropic,
2083
- streamSimple: streamSimpleAnthropic
2084
- });
2085
- var GATEWAY_PROVIDER = "vercel-ai-gateway";
2086
- var GEN_AI_PROVIDER_NAME = GATEWAY_PROVIDER;
2087
- var GEN_AI_OPERATION_CHAT = "chat";
2088
- var MISSING_GATEWAY_CREDENTIALS_ERROR = "Missing AI gateway credentials (AI_GATEWAY_API_KEY or VERCEL_OIDC_TOKEN)";
2089
- function getGatewayApiKey() {
2090
- return toOptionalTrimmed(getEnvApiKey("vercel-ai-gateway")) ?? toOptionalTrimmed(process.env.VERCEL_OIDC_TOKEN);
2091
- }
2092
- function getPiGatewayApiKeyOverride() {
2093
- return toOptionalTrimmed(process.env.VERCEL_OIDC_TOKEN);
2094
- }
2095
- function extractText(message) {
2096
- return (message.content ?? []).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text ?? "").join("").trim();
2097
- }
2098
- function parseJsonCandidate(text) {
2099
- const trimmed = text.trim();
2100
- if (!trimmed) return void 0;
2101
- try {
2102
- return JSON.parse(trimmed);
2103
- } catch {
2104
- const fencedBlocks = [
2105
- ...trimmed.matchAll(/```(?:json)?\s*([\s\S]*?)\s*```/gi)
2106
- ];
2107
- for (const block of fencedBlocks) {
2108
- try {
2109
- return JSON.parse(block[1]);
2110
- } catch {
2111
- }
2112
- }
2113
- const openBraceIndex = trimmed.indexOf("{");
2114
- if (openBraceIndex >= 0) {
2115
- let depth = 0;
2116
- let inString = false;
2117
- let escaped = false;
2118
- for (let index = openBraceIndex; index < trimmed.length; index += 1) {
2119
- const char = trimmed[index];
2120
- if (inString) {
2121
- if (escaped) {
2122
- escaped = false;
2123
- continue;
2124
- }
2125
- if (char === "\\") {
2126
- escaped = true;
2127
- continue;
2128
- }
2129
- if (char === '"') {
2130
- inString = false;
2131
- }
2132
- continue;
2133
- }
2134
- if (char === '"') {
2135
- inString = true;
2136
- continue;
2137
- }
2138
- if (char === "{") {
2139
- depth += 1;
2140
- continue;
2141
- }
2142
- if (char === "}") {
2143
- depth -= 1;
2144
- if (depth === 0) {
2145
- const slice = trimmed.slice(openBraceIndex, index + 1);
2146
- try {
2147
- return JSON.parse(slice);
2148
- } catch {
2149
- break;
2150
- }
2151
- }
2152
- }
2153
- }
2154
- }
2155
- return void 0;
2156
- }
2157
- }
2158
- function resolveGatewayModel(modelId) {
2159
- const models = getModels(GATEWAY_PROVIDER);
2160
- const matched = models.find((model) => model.id === modelId);
2161
- if (!matched) {
2162
- throw new Error(`Unknown AI Gateway model id: ${modelId}`);
2163
- }
2164
- return matched;
2165
- }
2166
- async function completeText(params) {
2167
- const model = resolveGatewayModel(params.modelId);
2168
- const apiKey = getPiGatewayApiKeyOverride();
2169
- const requestMessagesAttribute = serializeGenAiAttribute(params.messages);
2170
- const systemInstructionsAttribute = params.system ? serializeGenAiAttribute([{ type: "text", content: params.system }]) : void 0;
2171
- const startAttributes = {
2172
- "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
2173
- "gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
2174
- "gen_ai.request.model": params.modelId,
2175
- ...systemInstructionsAttribute ? { "gen_ai.system_instructions": systemInstructionsAttribute } : {},
2176
- ...requestMessagesAttribute ? { "gen_ai.input.messages": requestMessagesAttribute } : {},
2177
- "app.ai.auth_mode": apiKey ? "oidc" : "api_key",
2178
- ...params.thinkingLevel ? { "app.ai.reasoning_effort": params.thinkingLevel } : {}
2179
- };
2180
- setSpanAttributes(startAttributes);
2181
- const message = await completeSimple(
2182
- model,
2183
- {
2184
- systemPrompt: params.system,
2185
- messages: params.messages
2186
- },
2187
- {
2188
- ...apiKey ? { apiKey } : {},
2189
- temperature: params.temperature,
2190
- maxTokens: params.maxTokens,
2191
- reasoning: params.thinkingLevel,
2192
- signal: params.signal,
2193
- metadata: params.metadata
2194
- }
2195
- );
2196
- const outputText = extractText(message);
2197
- const outputMessagesAttribute = serializeGenAiAttribute([
2198
- {
2199
- role: "assistant",
2200
- content: outputText ? [{ type: "text", text: outputText }] : []
2201
- }
2202
- ]);
2203
- const usageAttributes = extractGenAiUsageAttributes(message);
2204
- const endAttributes = {
2205
- "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
2206
- "gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
2207
- "gen_ai.request.model": params.modelId,
2208
- ...outputMessagesAttribute ? { "gen_ai.output.messages": outputMessagesAttribute } : {},
2209
- ...usageAttributes,
2210
- ...message.stopReason ? { "gen_ai.response.finish_reasons": [message.stopReason] } : {},
2211
- ...params.thinkingLevel ? { "app.ai.reasoning_effort": params.thinkingLevel } : {}
2212
- };
2213
- setSpanAttributes(endAttributes);
2214
- if (message.stopReason === "error") {
2215
- const providerMessage = message.errorMessage?.trim() || "Unknown provider error";
2216
- logWarn(
2217
- "ai_completion_provider_error",
2218
- {},
2219
- {
2220
- "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
2221
- "gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
2222
- "gen_ai.request.model": params.modelId,
2223
- "error.message": providerMessage
2224
- },
2225
- "AI completion returned provider error"
2226
- );
2227
- throw new Error(`AI provider error: ${providerMessage}`);
2228
- }
2229
- return {
2230
- message,
2231
- text: outputText
2232
- };
2233
- }
2234
- async function completeObject(params) {
2235
- const startedAt = Date.now();
2236
- let text = "";
2237
- try {
2238
- ({ text } = await completeText({
2239
- modelId: params.modelId,
2240
- system: params.system,
2241
- thinkingLevel: params.thinkingLevel,
2242
- temperature: params.temperature,
2243
- maxTokens: params.maxTokens,
2244
- signal: params.signal,
2245
- metadata: params.metadata,
2246
- messages: [
2247
- {
2248
- role: "user",
2249
- content: params.prompt,
2250
- timestamp: Date.now()
2251
- }
2252
- ]
2253
- }));
2254
- } catch (error) {
2255
- logException(
2256
- error,
2257
- "ai_completion_failed",
2258
- {},
2259
- {
2260
- "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
2261
- "gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
2262
- "gen_ai.request.model": params.modelId,
2263
- "app.ai.duration_ms": Date.now() - startedAt
2264
- },
2265
- "AI object completion failed"
2266
- );
2267
- throw error;
2268
- }
2269
- const candidate = parseJsonCandidate(text);
2270
- const parsed = params.schema.safeParse(candidate);
2271
- if (!parsed.success) {
2272
- const preview = text.length > 400 ? `${text.slice(0, 400)}...` : text;
2273
- logWarn(
2274
- "ai_completion_schema_parse_failed",
2275
- {},
2276
- {
2277
- "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
2278
- "gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
2279
- "gen_ai.request.model": params.modelId,
2280
- "app.ai.duration_ms": Date.now() - startedAt,
2281
- "app.ai.response_preview": preview
2282
- },
2283
- "AI object completion schema parse failed"
2284
- );
2285
- throw new Error(
2286
- `Model did not return valid JSON for schema: ${parsed.error.message}. Raw response: ${preview}`
2287
- );
2288
- }
2289
- return {
2290
- object: parsed.data,
2291
- text
2292
- };
2293
- }
2294
-
2295
2074
  // src/chat/slack/message.ts
2296
2075
  function getSlackMessageTs(message) {
2297
2076
  if (message.id.endsWith(":message_changed_mention") && message.raw && typeof message.raw === "object") {
@@ -5071,11 +4850,11 @@ function createReadFileTool() {
5071
4850
  import { Type as Type6 } from "@sinclair/typebox";
5072
4851
  function createReportProgressTool() {
5073
4852
  return tool({
5074
- description: "Update the user-visible assistant loading message with a short progress phase. For every non-trivial turn, call this early with the initial major work phase, then call it again only when the major phase meaningfully changes. Use concrete labels like Searching docs, Reviewing results, or Running checks. Skip trivial direct answers, generic filler, and minor substeps.",
4853
+ description: "Update the user-visible assistant loading message with a short progress phase. For every non-trivial turn, call this early with the initial major work phase, then call it again only when the major phase meaningfully changes. Messages must be written in sentence case with a present-participle verb (e.g. 'Searching docs', 'Reviewing results', 'Running checks'). Skip trivial direct answers, generic filler, and minor substeps.",
5075
4854
  inputSchema: Type6.Object({
5076
4855
  message: Type6.String({
5077
4856
  minLength: 1,
5078
- description: "Short user-facing progress message. The UI truncates it if needed."
4857
+ description: "Short user-facing progress message."
5079
4858
  })
5080
4859
  })
5081
4860
  });
@@ -8846,7 +8625,7 @@ function isExecutionEscapeResponse(text) {
8846
8625
  if (!trimmed) return false;
8847
8626
  return isExecutionDeferralResponse(trimmed) || isToolAccessDisclaimerResponse(trimmed);
8848
8627
  }
8849
- function parseJsonCandidate2(text) {
8628
+ function parseJsonCandidate(text) {
8850
8629
  const trimmed = text.trim();
8851
8630
  if (!trimmed) return void 0;
8852
8631
  try {
@@ -8874,7 +8653,7 @@ function isToolPayloadShape(payload) {
8874
8653
  return false;
8875
8654
  }
8876
8655
  function isRawToolPayloadResponse(text) {
8877
- const parsed = parseJsonCandidate2(text);
8656
+ const parsed = parseJsonCandidate(text);
8878
8657
  if (Array.isArray(parsed)) {
8879
8658
  return parsed.some((entry) => isToolPayloadShape(entry));
8880
8659
  }
@@ -10213,7 +9992,7 @@ async function generateAssistantReply(messageText, context = {}) {
10213
9992
  let completedAssistantTurn = false;
10214
9993
  try {
10215
9994
  if (resumedFromCheckpoint) {
10216
- agent.replaceMessages(existingCheckpoint.piMessages);
9995
+ agent.state.messages = existingCheckpoint.piMessages;
10217
9996
  }
10218
9997
  beforeMessageCount = agent.state.messages.length;
10219
9998
  await withSpan(
@@ -10862,11 +10641,10 @@ function buildSlackReplyFooter(args) {
10862
10641
  value: formatSlackDuration(durationMs)
10863
10642
  });
10864
10643
  }
10865
- const traceId = args.traceId?.trim();
10866
- if (traceId) {
10644
+ if (args.thinkingLevel) {
10867
10645
  items.push({
10868
- label: "Trace",
10869
- value: traceId
10646
+ label: "Thinking",
10647
+ value: args.thinkingLevel
10870
10648
  });
10871
10649
  }
10872
10650
  return items.length > 0 ? { items } : void 0;
@@ -11189,7 +10967,7 @@ async function resumeSlackTurn(args) {
11189
10967
  const footer = buildSlackReplyFooter({
11190
10968
  conversationId: args.replyContext?.correlation?.conversationId ?? lockKey,
11191
10969
  durationMs: reply.diagnostics.durationMs,
11192
- traceId: getActiveTraceId(),
10970
+ thinkingLevel: reply.diagnostics.thinkingLevel,
11193
10971
  usage: reply.diagnostics.usage
11194
10972
  });
11195
10973
  await postSlackApiReplyPosts({
@@ -14258,7 +14036,7 @@ function createReplyToThread(deps) {
14258
14036
  const replyFooter = buildSlackReplyFooter({
14259
14037
  conversationId,
14260
14038
  durationMs: reply.diagnostics.durationMs,
14261
- traceId: getActiveTraceId(),
14039
+ thinkingLevel: reply.diagnostics.thinkingLevel,
14262
14040
  usage: reply.diagnostics.usage
14263
14041
  });
14264
14042
  const shouldUseSlackFooter = Boolean(replyFooter) && Boolean(channelId && threadTs) && thread.adapter?.name === "slack";
@@ -1,6 +1,11 @@
1
1
  import {
2
+ extractGenAiUsageAttributes,
2
3
  getPluginRuntimeDependencies,
3
4
  getPluginRuntimePostinstall,
5
+ logException,
6
+ logWarn,
7
+ serializeGenAiAttribute,
8
+ setSpanAttributes,
4
9
  withSpan
5
10
  } from "./chunk-RZJDO55D.js";
6
11
 
@@ -8,6 +13,9 @@ import {
8
13
  import { createMemoryState } from "@chat-adapter/state-memory";
9
14
  import { createRedisState } from "@chat-adapter/state-redis";
10
15
 
16
+ // src/chat/config.ts
17
+ import { getModel } from "@mariozechner/pi-ai";
18
+
11
19
  // src/chat/optional-string.ts
12
20
  function toOptionalTrimmed(value) {
13
21
  if (!value) {
@@ -17,6 +25,233 @@ function toOptionalTrimmed(value) {
17
25
  return trimmed.length > 0 ? trimmed : void 0;
18
26
  }
19
27
 
28
+ // src/chat/pi/client.ts
29
+ import {
30
+ completeSimple,
31
+ getEnvApiKey,
32
+ getModels,
33
+ registerApiProvider
34
+ } from "@mariozechner/pi-ai";
35
+ import {
36
+ streamAnthropic,
37
+ streamSimpleAnthropic
38
+ } from "@mariozechner/pi-ai/anthropic";
39
+ registerApiProvider({
40
+ api: "anthropic-messages",
41
+ stream: streamAnthropic,
42
+ streamSimple: streamSimpleAnthropic
43
+ });
44
+ var GATEWAY_PROVIDER = "vercel-ai-gateway";
45
+ var GEN_AI_PROVIDER_NAME = GATEWAY_PROVIDER;
46
+ var GEN_AI_OPERATION_CHAT = "chat";
47
+ var MISSING_GATEWAY_CREDENTIALS_ERROR = "Missing AI gateway credentials (AI_GATEWAY_API_KEY or VERCEL_OIDC_TOKEN)";
48
+ function getGatewayApiKey() {
49
+ return toOptionalTrimmed(getEnvApiKey("vercel-ai-gateway")) ?? toOptionalTrimmed(process.env.VERCEL_OIDC_TOKEN);
50
+ }
51
+ function getPiGatewayApiKeyOverride() {
52
+ return toOptionalTrimmed(process.env.VERCEL_OIDC_TOKEN);
53
+ }
54
+ function extractText(message) {
55
+ return (message.content ?? []).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text ?? "").join("").trim();
56
+ }
57
+ function parseJsonCandidate(text) {
58
+ const trimmed = text.trim();
59
+ if (!trimmed) return void 0;
60
+ try {
61
+ return JSON.parse(trimmed);
62
+ } catch {
63
+ const fencedBlocks = [
64
+ ...trimmed.matchAll(/```(?:json)?\s*([\s\S]*?)\s*```/gi)
65
+ ];
66
+ for (const block of fencedBlocks) {
67
+ try {
68
+ return JSON.parse(block[1]);
69
+ } catch {
70
+ }
71
+ }
72
+ const openBraceIndex = trimmed.indexOf("{");
73
+ if (openBraceIndex >= 0) {
74
+ let depth = 0;
75
+ let inString = false;
76
+ let escaped = false;
77
+ for (let index = openBraceIndex; index < trimmed.length; index += 1) {
78
+ const char = trimmed[index];
79
+ if (inString) {
80
+ if (escaped) {
81
+ escaped = false;
82
+ continue;
83
+ }
84
+ if (char === "\\") {
85
+ escaped = true;
86
+ continue;
87
+ }
88
+ if (char === '"') {
89
+ inString = false;
90
+ }
91
+ continue;
92
+ }
93
+ if (char === '"') {
94
+ inString = true;
95
+ continue;
96
+ }
97
+ if (char === "{") {
98
+ depth += 1;
99
+ continue;
100
+ }
101
+ if (char === "}") {
102
+ depth -= 1;
103
+ if (depth === 0) {
104
+ const slice = trimmed.slice(openBraceIndex, index + 1);
105
+ try {
106
+ return JSON.parse(slice);
107
+ } catch {
108
+ break;
109
+ }
110
+ }
111
+ }
112
+ }
113
+ }
114
+ return void 0;
115
+ }
116
+ }
117
+ function resolveGatewayModel(modelId) {
118
+ const matched = getModels(GATEWAY_PROVIDER).find(
119
+ (model) => model.id === modelId
120
+ );
121
+ if (!matched) {
122
+ throw new Error(`Unknown AI Gateway model id: ${modelId}`);
123
+ }
124
+ return matched;
125
+ }
126
+ async function completeText(params) {
127
+ const model = resolveGatewayModel(params.modelId);
128
+ const apiKey = getPiGatewayApiKeyOverride();
129
+ const requestMessagesAttribute = serializeGenAiAttribute(params.messages);
130
+ const systemInstructionsAttribute = params.system ? serializeGenAiAttribute([{ type: "text", content: params.system }]) : void 0;
131
+ const startAttributes = {
132
+ "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
133
+ "gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
134
+ "gen_ai.request.model": params.modelId,
135
+ ...systemInstructionsAttribute ? { "gen_ai.system_instructions": systemInstructionsAttribute } : {},
136
+ ...requestMessagesAttribute ? { "gen_ai.input.messages": requestMessagesAttribute } : {},
137
+ "app.ai.auth_mode": apiKey ? "oidc" : "api_key",
138
+ ...params.thinkingLevel ? { "app.ai.reasoning_effort": params.thinkingLevel } : {}
139
+ };
140
+ setSpanAttributes(startAttributes);
141
+ const message = await completeSimple(
142
+ model,
143
+ {
144
+ systemPrompt: params.system,
145
+ messages: params.messages
146
+ },
147
+ {
148
+ ...apiKey ? { apiKey } : {},
149
+ temperature: params.temperature,
150
+ maxTokens: params.maxTokens,
151
+ reasoning: params.thinkingLevel,
152
+ signal: params.signal,
153
+ metadata: params.metadata
154
+ }
155
+ );
156
+ const outputText = extractText(message);
157
+ const outputMessagesAttribute = serializeGenAiAttribute([
158
+ {
159
+ role: "assistant",
160
+ content: outputText ? [{ type: "text", text: outputText }] : []
161
+ }
162
+ ]);
163
+ const usageAttributes = extractGenAiUsageAttributes(message);
164
+ const endAttributes = {
165
+ "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
166
+ "gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
167
+ "gen_ai.request.model": params.modelId,
168
+ ...outputMessagesAttribute ? { "gen_ai.output.messages": outputMessagesAttribute } : {},
169
+ ...usageAttributes,
170
+ ...message.stopReason ? { "gen_ai.response.finish_reasons": [message.stopReason] } : {},
171
+ ...params.thinkingLevel ? { "app.ai.reasoning_effort": params.thinkingLevel } : {}
172
+ };
173
+ setSpanAttributes(endAttributes);
174
+ if (message.stopReason === "error") {
175
+ const providerMessage = message.errorMessage?.trim() || "Unknown provider error";
176
+ logWarn(
177
+ "ai_completion_provider_error",
178
+ {},
179
+ {
180
+ "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
181
+ "gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
182
+ "gen_ai.request.model": params.modelId,
183
+ "error.message": providerMessage
184
+ },
185
+ "AI completion returned provider error"
186
+ );
187
+ throw new Error(`AI provider error: ${providerMessage}`);
188
+ }
189
+ return {
190
+ message,
191
+ text: outputText
192
+ };
193
+ }
194
+ async function completeObject(params) {
195
+ const startedAt = Date.now();
196
+ let text = "";
197
+ try {
198
+ ({ text } = await completeText({
199
+ modelId: params.modelId,
200
+ system: params.system,
201
+ thinkingLevel: params.thinkingLevel,
202
+ temperature: params.temperature,
203
+ maxTokens: params.maxTokens,
204
+ signal: params.signal,
205
+ metadata: params.metadata,
206
+ messages: [
207
+ {
208
+ role: "user",
209
+ content: params.prompt,
210
+ timestamp: Date.now()
211
+ }
212
+ ]
213
+ }));
214
+ } catch (error) {
215
+ logException(
216
+ error,
217
+ "ai_completion_failed",
218
+ {},
219
+ {
220
+ "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
221
+ "gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
222
+ "gen_ai.request.model": params.modelId,
223
+ "app.ai.duration_ms": Date.now() - startedAt
224
+ },
225
+ "AI object completion failed"
226
+ );
227
+ throw error;
228
+ }
229
+ const candidate = parseJsonCandidate(text);
230
+ const parsed = params.schema.safeParse(candidate);
231
+ if (!parsed.success) {
232
+ const preview = text.length > 400 ? `${text.slice(0, 400)}...` : text;
233
+ logWarn(
234
+ "ai_completion_schema_parse_failed",
235
+ {},
236
+ {
237
+ "gen_ai.provider.name": GEN_AI_PROVIDER_NAME,
238
+ "gen_ai.operation.name": GEN_AI_OPERATION_CHAT,
239
+ "gen_ai.request.model": params.modelId,
240
+ "app.ai.duration_ms": Date.now() - startedAt,
241
+ "app.ai.response_preview": preview
242
+ },
243
+ "AI object completion schema parse failed"
244
+ );
245
+ throw new Error(
246
+ `Model did not return valid JSON for schema: ${parsed.error.message}. Raw response: ${preview}`
247
+ );
248
+ }
249
+ return {
250
+ object: parsed.data,
251
+ text
252
+ };
253
+ }
254
+
20
255
  // src/chat/config.ts
21
256
  var MIN_AGENT_TURN_TIMEOUT_MS = 10 * 1e3;
22
257
  var DEFAULT_AGENT_TURN_TIMEOUT_MS = 12 * 60 * 1e3;
@@ -79,15 +314,26 @@ function parseLoadingMessages(rawValue) {
79
314
  return value.trim();
80
315
  });
81
316
  }
317
+ var DEFAULT_MODEL_ID = getModel("vercel-ai-gateway", "openai/gpt-5.4").id;
318
+ var DEFAULT_FAST_MODEL_ID = getModel(
319
+ "vercel-ai-gateway",
320
+ "openai/gpt-5.4-mini"
321
+ ).id;
322
+ function validateGatewayModelId(raw) {
323
+ const trimmed = toOptionalTrimmed(raw);
324
+ if (trimmed === void 0) return void 0;
325
+ resolveGatewayModel(trimmed);
326
+ return trimmed;
327
+ }
82
328
  function readBotConfig(env) {
83
329
  const functionMaxDurationSeconds = resolveFunctionMaxDurationSeconds(env);
84
330
  const maxTurnTimeoutMs = resolveMaxTurnTimeoutMs(functionMaxDurationSeconds);
85
331
  return {
86
332
  userName: env.JUNIOR_BOT_NAME ?? "junior",
87
- modelId: env.AI_MODEL ?? "openai/gpt-5.4",
88
- fastModelId: env.AI_FAST_MODEL ?? env.AI_MODEL ?? "openai/gpt-5.4-mini",
333
+ modelId: validateGatewayModelId(env.AI_MODEL) ?? DEFAULT_MODEL_ID,
334
+ fastModelId: validateGatewayModelId(env.AI_FAST_MODEL ?? env.AI_MODEL) ?? DEFAULT_FAST_MODEL_ID,
89
335
  loadingMessages: parseLoadingMessages(env.JUNIOR_LOADING_MESSAGES),
90
- visionModelId: toOptionalTrimmed(env.AI_VISION_MODEL),
336
+ visionModelId: validateGatewayModelId(env.AI_VISION_MODEL),
91
337
  turnTimeoutMs: parseAgentTurnTimeoutMs(
92
338
  env.AGENT_TURN_TIMEOUT_MS,
93
339
  maxTurnTimeoutMs
@@ -842,7 +1088,13 @@ function isSnapshotMissingError(error) {
842
1088
  }
843
1089
 
844
1090
  export {
845
- toOptionalTrimmed,
1091
+ GEN_AI_PROVIDER_NAME,
1092
+ MISSING_GATEWAY_CREDENTIALS_ERROR,
1093
+ getGatewayApiKey,
1094
+ getPiGatewayApiKeyOverride,
1095
+ resolveGatewayModel,
1096
+ completeText,
1097
+ completeObject,
846
1098
  botConfig,
847
1099
  getSlackBotToken,
848
1100
  getSlackSigningSecret,
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  disconnectStateAdapter,
3
3
  resolveRuntimeDependencySnapshot
4
- } from "../chunk-375D5V4U.js";
4
+ } from "../chunk-LEYD42MR.js";
5
5
  import {
6
6
  getPluginProviders,
7
7
  getPluginRuntimeDependencies,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sentry/junior",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -25,8 +25,8 @@
25
25
  "@chat-adapter/state-memory": "4.26.0",
26
26
  "@chat-adapter/state-redis": "4.26.0",
27
27
  "@logtape/logtape": "^2.0.5",
28
- "@mariozechner/pi-agent-core": "0.59.0",
29
- "@mariozechner/pi-ai": "0.59.0",
28
+ "@mariozechner/pi-agent-core": "0.68.1",
29
+ "@mariozechner/pi-ai": "0.68.1",
30
30
  "@modelcontextprotocol/sdk": "1.29.0",
31
31
  "@sinclair/typebox": "^0.34.49",
32
32
  "@slack/web-api": "^7.15.1",