@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 +19 -241
- package/dist/{chunk-375D5V4U.js → chunk-LEYD42MR.js} +256 -4
- package/dist/cli/snapshot-warmup.js +1 -1
- package/package.json +3 -3
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
|
-
|
|
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.
|
|
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.
|
|
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
|
|
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 =
|
|
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.
|
|
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
|
-
|
|
10866
|
-
if (traceId) {
|
|
10644
|
+
if (args.thinkingLevel) {
|
|
10867
10645
|
items.push({
|
|
10868
|
-
label: "
|
|
10869
|
-
value:
|
|
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
|
-
|
|
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
|
-
|
|
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 ??
|
|
88
|
-
fastModelId: env.AI_FAST_MODEL ?? env.AI_MODEL ??
|
|
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:
|
|
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
|
-
|
|
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,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior",
|
|
3
|
-
"version": "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.
|
|
29
|
-
"@mariozechner/pi-ai": "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",
|