@sentry/junior 0.68.0 → 0.70.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 +1779 -746
- package/dist/build/virtual-config.d.ts +2 -2
- package/dist/chat/agent-dispatch/heartbeat.d.ts +2 -2
- package/dist/chat/agent-dispatch/store.d.ts +4 -1
- package/dist/chat/agent-dispatch/types.d.ts +2 -4
- package/dist/chat/agent-dispatch/validation.d.ts +3 -2
- package/dist/chat/credentials/context.d.ts +49 -24
- package/dist/chat/credentials/user-token-store.d.ts +6 -0
- package/dist/chat/destination.d.ts +12 -0
- package/dist/chat/ingress/message-router.d.ts +1 -1
- package/dist/chat/mcp/auth-store.d.ts +2 -0
- package/dist/chat/mcp/oauth.d.ts +2 -0
- package/dist/chat/oauth-flow.d.ts +7 -0
- package/dist/chat/plugins/agent-hooks.d.ts +9 -9
- package/dist/chat/plugins/auth/auth-token-placeholder.d.ts +2 -2
- package/dist/chat/plugins/auth/oauth-request.d.ts +3 -1
- package/dist/chat/plugins/credential-hooks.d.ts +53 -0
- package/dist/chat/plugins/logging.d.ts +1 -1
- package/dist/chat/plugins/state.d.ts +1 -1
- package/dist/chat/plugins/types.d.ts +19 -23
- package/dist/chat/respond.d.ts +2 -0
- package/dist/chat/runtime/reply-executor.d.ts +3 -1
- package/dist/chat/runtime/slack-runtime.d.ts +8 -3
- package/dist/chat/sandbox/egress-credentials.d.ts +34 -0
- package/dist/chat/sandbox/egress-schemas.d.ts +105 -0
- package/dist/chat/sandbox/egress-session.d.ts +17 -17
- package/dist/chat/sandbox/sandbox.d.ts +3 -0
- package/dist/chat/sandbox/session.d.ts +1 -0
- package/dist/chat/services/mcp-auth-orchestration.d.ts +2 -0
- package/dist/chat/services/pending-auth.d.ts +2 -0
- package/dist/chat/services/plugin-auth-orchestration.d.ts +2 -0
- package/dist/chat/services/provider-retry.d.ts +13 -4
- package/dist/chat/services/timeout-resume.d.ts +2 -0
- package/dist/chat/services/turn-session-record.d.ts +6 -0
- package/dist/chat/slack/attachment-fetchers.d.ts +11 -0
- package/dist/chat/state/conversation-details.d.ts +46 -0
- package/dist/chat/state/conversation.d.ts +1 -0
- package/dist/chat/state/turn-session.d.ts +4 -3
- package/dist/chat/task-execution/queue.d.ts +2 -0
- package/dist/chat/task-execution/store.d.ts +5 -0
- package/dist/chat/task-execution/vercel-callback.d.ts +4 -0
- package/dist/chat/task-execution/vercel-queue.d.ts +2 -0
- package/dist/chat/task-execution/worker.d.ts +4 -2
- package/dist/chat/tools/slack/context.d.ts +3 -0
- package/dist/chat/tools/types.d.ts +21 -2
- package/dist/chunk-76YMBKW7.js +326 -0
- package/dist/{chunk-PIVOJIUD.js → chunk-B5HKWWQB.js} +9 -5
- package/dist/chunk-BBXYXOJW.js +1858 -0
- package/dist/{chunk-V47RLIO2.js → chunk-GT67ZWZQ.js} +4 -4
- package/dist/{chunk-UQQSW7QB.js → chunk-HOGQL2H6.js} +197 -343
- package/dist/{chunk-75UZ4JLC.js → chunk-IGLNC5H6.js} +21 -9
- package/dist/{chunk-EBVQXCD2.js → chunk-JS4HURDT.js} +362 -280
- package/dist/chunk-R62YWUNO.js +264 -0
- package/dist/{chunk-OIIXZOOC.js → chunk-UXG6TU2U.js} +311 -2015
- package/dist/cli/check.js +4 -4
- package/dist/cli/init.js +18 -1
- package/dist/cli/snapshot-warmup.js +5 -4
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +21 -19
- package/dist/plugins.d.ts +2 -2
- package/dist/reporting.d.ts +8 -4
- package/dist/reporting.js +72 -29
- package/package.json +6 -4
- package/dist/chat/plugins/auth/github-app-broker.d.ts +0 -4
- package/dist/chat/plugins/github-permissions.d.ts +0 -11
- package/dist/chat/queue/thread-message-dispatcher.d.ts +0 -33
- package/dist/chunk-KVZL5NZS.js +0 -519
|
@@ -6,7 +6,7 @@ import {
|
|
|
6
6
|
setSpanAttributes,
|
|
7
7
|
toOptionalString,
|
|
8
8
|
withSpan
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-BBXYXOJW.js";
|
|
10
10
|
|
|
11
11
|
// src/chat/slack/context.ts
|
|
12
12
|
function toTrimmedSlackString(value) {
|
|
@@ -230,6 +230,316 @@ import {
|
|
|
230
230
|
streamAnthropic,
|
|
231
231
|
streamSimpleAnthropic
|
|
232
232
|
} from "@earendil-works/pi-ai/anthropic";
|
|
233
|
+
|
|
234
|
+
// src/chat/turn-context-tag.ts
|
|
235
|
+
var TURN_CONTEXT_TAG = "runtime-turn-context";
|
|
236
|
+
|
|
237
|
+
// src/chat/respond-helpers.ts
|
|
238
|
+
var MAX_INLINE_ATTACHMENT_BASE64_CHARS = 12e4;
|
|
239
|
+
var RUNTIME_TURN_CONTEXT_START = `<${TURN_CONTEXT_TAG}>`;
|
|
240
|
+
function getSessionIdentifiers(context) {
|
|
241
|
+
return {
|
|
242
|
+
conversationId: context.correlation?.conversationId ?? context.correlation?.threadId ?? context.correlation?.runId,
|
|
243
|
+
sessionId: context.correlation?.turnId
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
function isExecutionDeferralResponse(text) {
|
|
247
|
+
return /\b(want me to proceed|do you want me to proceed|shall i proceed|can i proceed|should i proceed|let me do that now|give me a moment|tag me again|fresh invocation)\b/i.test(
|
|
248
|
+
text
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
function isToolAccessDisclaimerResponse(text) {
|
|
252
|
+
return /\b(i (don't|do not) have access to (active )?tool|tool results came back empty|prior results .* empty|cannot access .*tool|need to (run|load) .*tool .* first)\b/i.test(
|
|
253
|
+
text
|
|
254
|
+
);
|
|
255
|
+
}
|
|
256
|
+
function isExecutionEscapeResponse(text) {
|
|
257
|
+
const trimmed = text.trim();
|
|
258
|
+
if (!trimmed) return false;
|
|
259
|
+
return isExecutionDeferralResponse(trimmed) || isToolAccessDisclaimerResponse(trimmed);
|
|
260
|
+
}
|
|
261
|
+
function parseJsonCandidate(text) {
|
|
262
|
+
const trimmed = text.trim();
|
|
263
|
+
if (!trimmed) return void 0;
|
|
264
|
+
try {
|
|
265
|
+
return JSON.parse(trimmed);
|
|
266
|
+
} catch {
|
|
267
|
+
const fenced = trimmed.match(/^```(?:json)?\s*([\s\S]*?)\s*```$/i);
|
|
268
|
+
if (!fenced) return void 0;
|
|
269
|
+
try {
|
|
270
|
+
return JSON.parse(fenced[1]);
|
|
271
|
+
} catch {
|
|
272
|
+
return void 0;
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
function isToolPayloadShape(payload) {
|
|
277
|
+
if (!payload || typeof payload !== "object") return false;
|
|
278
|
+
const record = payload;
|
|
279
|
+
const type = typeof record.type === "string" ? record.type.toLowerCase() : "";
|
|
280
|
+
if (type.startsWith("tool-")) return true;
|
|
281
|
+
if (type === "tool_use" || type === "tool_call" || type === "tool_result" || type === "tool_error")
|
|
282
|
+
return true;
|
|
283
|
+
const hasToolName = typeof record.toolName === "string" || typeof record.name === "string";
|
|
284
|
+
const hasToolInput = Object.prototype.hasOwnProperty.call(record, "input") || Object.prototype.hasOwnProperty.call(record, "args");
|
|
285
|
+
if (hasToolName && hasToolInput) return true;
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
function isRawToolPayloadResponse(text) {
|
|
289
|
+
const parsed = parseJsonCandidate(text);
|
|
290
|
+
if (Array.isArray(parsed)) {
|
|
291
|
+
return parsed.some((entry) => isToolPayloadShape(entry));
|
|
292
|
+
}
|
|
293
|
+
if (isToolPayloadShape(parsed)) {
|
|
294
|
+
return true;
|
|
295
|
+
}
|
|
296
|
+
const compact = text.replace(/\s+/g, " ");
|
|
297
|
+
return /"type"\s*:\s*"tool[-_](use|call|result|error)"/i.test(compact);
|
|
298
|
+
}
|
|
299
|
+
function toObservablePromptPart(part) {
|
|
300
|
+
if (part.type === "text") {
|
|
301
|
+
return {
|
|
302
|
+
type: "text",
|
|
303
|
+
text: part.text
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
return {
|
|
307
|
+
type: "image",
|
|
308
|
+
mimeType: part.mimeType,
|
|
309
|
+
data: `[omitted:${part.data.length}]`
|
|
310
|
+
};
|
|
311
|
+
}
|
|
312
|
+
function summarizeMessageText(text) {
|
|
313
|
+
const normalized = text.trim().replace(/\s+/g, " ");
|
|
314
|
+
if (!normalized) {
|
|
315
|
+
return "[empty]";
|
|
316
|
+
}
|
|
317
|
+
return normalized.length > 1200 ? `${normalized.slice(0, 1200)}...` : normalized;
|
|
318
|
+
}
|
|
319
|
+
function isStructuredThreadContext(context) {
|
|
320
|
+
return /^<thread-(compactions|transcript)>/.test(context);
|
|
321
|
+
}
|
|
322
|
+
function renderThreadContextForPrompt(context) {
|
|
323
|
+
if (isStructuredThreadContext(context)) {
|
|
324
|
+
return context;
|
|
325
|
+
}
|
|
326
|
+
return ["<thread-background>", context, "</thread-background>"].join("\n");
|
|
327
|
+
}
|
|
328
|
+
function buildUserTurnText(userInput, conversationContext) {
|
|
329
|
+
const trimmedContext = conversationContext?.trim();
|
|
330
|
+
if (!trimmedContext) {
|
|
331
|
+
return userInput;
|
|
332
|
+
}
|
|
333
|
+
return [
|
|
334
|
+
renderThreadContextForPrompt(trimmedContext),
|
|
335
|
+
"",
|
|
336
|
+
"<current-instruction>",
|
|
337
|
+
userInput,
|
|
338
|
+
"</current-instruction>"
|
|
339
|
+
].join("\n");
|
|
340
|
+
}
|
|
341
|
+
function encodeNonImageAttachmentForPrompt(attachment) {
|
|
342
|
+
const base64 = attachment.data.toString("base64");
|
|
343
|
+
const wasTruncated = base64.length > MAX_INLINE_ATTACHMENT_BASE64_CHARS;
|
|
344
|
+
const encodedPayload = wasTruncated ? `${base64.slice(0, MAX_INLINE_ATTACHMENT_BASE64_CHARS)}...` : base64;
|
|
345
|
+
return [
|
|
346
|
+
"<attachment>",
|
|
347
|
+
`filename: ${attachment.filename ?? "unnamed"}`,
|
|
348
|
+
`media_type: ${attachment.mediaType}`,
|
|
349
|
+
"encoding: base64",
|
|
350
|
+
`truncated: ${wasTruncated ? "true" : "false"}`,
|
|
351
|
+
"<data_base64>",
|
|
352
|
+
encodedPayload,
|
|
353
|
+
"</data_base64>",
|
|
354
|
+
"</attachment>"
|
|
355
|
+
].join("\n");
|
|
356
|
+
}
|
|
357
|
+
function isToolResultMessage(value) {
|
|
358
|
+
return typeof value === "object" && value !== null && value.role === "toolResult";
|
|
359
|
+
}
|
|
360
|
+
function normalizeToolNameFromResult(result) {
|
|
361
|
+
if (!result || typeof result !== "object") return void 0;
|
|
362
|
+
const record = result;
|
|
363
|
+
if (typeof record.toolName === "string" && record.toolName.length > 0) {
|
|
364
|
+
return record.toolName;
|
|
365
|
+
}
|
|
366
|
+
if (typeof record.name === "string" && record.name.length > 0) {
|
|
367
|
+
return record.name;
|
|
368
|
+
}
|
|
369
|
+
return void 0;
|
|
370
|
+
}
|
|
371
|
+
function isToolResultError(result) {
|
|
372
|
+
if (!result || typeof result !== "object") return false;
|
|
373
|
+
return Boolean(result.isError);
|
|
374
|
+
}
|
|
375
|
+
function isAssistantMessage(value) {
|
|
376
|
+
return typeof value === "object" && value !== null && value.role === "assistant";
|
|
377
|
+
}
|
|
378
|
+
function getPiMessageRole(value) {
|
|
379
|
+
if (!value || typeof value !== "object") {
|
|
380
|
+
return void 0;
|
|
381
|
+
}
|
|
382
|
+
const role = value.role;
|
|
383
|
+
return typeof role === "string" ? role : void 0;
|
|
384
|
+
}
|
|
385
|
+
function getUserMessageContent(message) {
|
|
386
|
+
const record = message;
|
|
387
|
+
return record.role === "user" && Array.isArray(record.content) ? record.content : void 0;
|
|
388
|
+
}
|
|
389
|
+
function isRuntimeTurnContextPart(part, marker) {
|
|
390
|
+
return part !== null && typeof part === "object" && part.type === "text" && typeof part.text === "string" && part.text.startsWith(marker);
|
|
391
|
+
}
|
|
392
|
+
function prependRuntimeTurnContext(message, turnContextPrompt) {
|
|
393
|
+
const content = getUserMessageContent(message);
|
|
394
|
+
if (!content) {
|
|
395
|
+
return void 0;
|
|
396
|
+
}
|
|
397
|
+
const contextIndex = content.findIndex(
|
|
398
|
+
(part) => isRuntimeTurnContextPart(part, RUNTIME_TURN_CONTEXT_START)
|
|
399
|
+
);
|
|
400
|
+
if (contextIndex >= 0) {
|
|
401
|
+
return void 0;
|
|
402
|
+
}
|
|
403
|
+
return {
|
|
404
|
+
...message,
|
|
405
|
+
content: [{ type: "text", text: turnContextPrompt }, ...content]
|
|
406
|
+
};
|
|
407
|
+
}
|
|
408
|
+
function prependMissingRuntimeTurnContext(messages, turnContextPrompt) {
|
|
409
|
+
if (hasRuntimeTurnContext(messages)) {
|
|
410
|
+
return messages;
|
|
411
|
+
}
|
|
412
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
413
|
+
const updated = prependRuntimeTurnContext(
|
|
414
|
+
messages[index],
|
|
415
|
+
turnContextPrompt
|
|
416
|
+
);
|
|
417
|
+
if (!updated) {
|
|
418
|
+
continue;
|
|
419
|
+
}
|
|
420
|
+
const nextMessages = [...messages];
|
|
421
|
+
nextMessages[index] = updated;
|
|
422
|
+
return nextMessages;
|
|
423
|
+
}
|
|
424
|
+
return [
|
|
425
|
+
...messages,
|
|
426
|
+
{
|
|
427
|
+
role: "user",
|
|
428
|
+
content: [{ type: "text", text: turnContextPrompt }],
|
|
429
|
+
timestamp: Date.now()
|
|
430
|
+
}
|
|
431
|
+
];
|
|
432
|
+
}
|
|
433
|
+
function hasRuntimeTurnContext(messages) {
|
|
434
|
+
return messages.some(
|
|
435
|
+
(message) => getUserMessageContent(message)?.some(
|
|
436
|
+
(part) => isRuntimeTurnContextPart(part, RUNTIME_TURN_CONTEXT_START)
|
|
437
|
+
)
|
|
438
|
+
);
|
|
439
|
+
}
|
|
440
|
+
function stripRuntimeTurnContext(messages) {
|
|
441
|
+
return messages.flatMap((message) => {
|
|
442
|
+
const content = getUserMessageContent(message);
|
|
443
|
+
if (!content) {
|
|
444
|
+
return [message];
|
|
445
|
+
}
|
|
446
|
+
const nextContent = content.filter(
|
|
447
|
+
(part) => !isRuntimeTurnContextPart(part, RUNTIME_TURN_CONTEXT_START)
|
|
448
|
+
);
|
|
449
|
+
if (nextContent.length === content.length) {
|
|
450
|
+
return [message];
|
|
451
|
+
}
|
|
452
|
+
if (nextContent.length === 0) {
|
|
453
|
+
return [];
|
|
454
|
+
}
|
|
455
|
+
return [{ ...message, content: nextContent }];
|
|
456
|
+
});
|
|
457
|
+
}
|
|
458
|
+
function extractAssistantText(message) {
|
|
459
|
+
const content = message.content ?? [];
|
|
460
|
+
return content.filter(
|
|
461
|
+
(part) => part.type === "text" && typeof part.text === "string"
|
|
462
|
+
).map((part) => part.text).join("\n");
|
|
463
|
+
}
|
|
464
|
+
function getTerminalAssistantMessages(messages) {
|
|
465
|
+
let lastToolResultIndex = -1;
|
|
466
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
467
|
+
if (isToolResultMessage(messages[index])) {
|
|
468
|
+
lastToolResultIndex = index;
|
|
469
|
+
break;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
return messages.slice(lastToolResultIndex + 1).filter(isAssistantMessage);
|
|
473
|
+
}
|
|
474
|
+
function upsertActiveSkill(activeSkills, next) {
|
|
475
|
+
const existing = activeSkills.find((skill) => skill.name === next.name);
|
|
476
|
+
if (existing) {
|
|
477
|
+
existing.body = next.body;
|
|
478
|
+
existing.description = next.description;
|
|
479
|
+
existing.skillPath = next.skillPath;
|
|
480
|
+
existing.allowedTools = next.allowedTools;
|
|
481
|
+
existing.pluginProvider = next.pluginProvider;
|
|
482
|
+
return;
|
|
483
|
+
}
|
|
484
|
+
activeSkills.push(next);
|
|
485
|
+
}
|
|
486
|
+
function trimTrailingAssistantMessages(messages) {
|
|
487
|
+
let end = messages.length;
|
|
488
|
+
while (end > 0 && getPiMessageRole(messages[end - 1]) === "assistant") {
|
|
489
|
+
end -= 1;
|
|
490
|
+
}
|
|
491
|
+
return end === messages.length ? [...messages] : messages.slice(0, end);
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
// src/chat/services/provider-retry.ts
|
|
495
|
+
var PROVIDER_RETRY_DELAYS_MS = [2e3, 4e3, 8e3];
|
|
496
|
+
var PROVIDER_ERROR_PREFIX = "AI provider error:";
|
|
497
|
+
var PROVIDER_RETRY_ERROR_NAME = "ProviderRetryError";
|
|
498
|
+
var NON_RETRYABLE_PROVIDER_ERROR_PATTERNS = [
|
|
499
|
+
/invalid.?api.?key|no api key|authentication|authorization|permission|forbidden|credential/i,
|
|
500
|
+
/context.?length|context.?window/i,
|
|
501
|
+
/content.?policy|validation|bad request|\b(?:400|401|403)\b/i,
|
|
502
|
+
/unsupported model|invalid model|unknown ai gateway model|unknown model|mismatched api/i,
|
|
503
|
+
/usage limit|monthly usage limit|available balance|insufficient.?quota|out of budget|quota exceeded|billing/i
|
|
504
|
+
];
|
|
505
|
+
function providerMessage(error) {
|
|
506
|
+
return (error instanceof Error ? error.message : String(error)).trim();
|
|
507
|
+
}
|
|
508
|
+
function createProviderError(error) {
|
|
509
|
+
const message = providerMessage(error);
|
|
510
|
+
const displayMessage = `${PROVIDER_ERROR_PREFIX} ${message || "Unknown provider error"}`;
|
|
511
|
+
const providerError = new Error(displayMessage, { cause: error });
|
|
512
|
+
if (message && isRetryableProviderFailure(message)) {
|
|
513
|
+
providerError.name = PROVIDER_RETRY_ERROR_NAME;
|
|
514
|
+
}
|
|
515
|
+
return providerError;
|
|
516
|
+
}
|
|
517
|
+
function isProviderRetryError(error) {
|
|
518
|
+
return error instanceof Error && error.name === PROVIDER_RETRY_ERROR_NAME;
|
|
519
|
+
}
|
|
520
|
+
function isRetryableProviderFailure(errorMessage) {
|
|
521
|
+
return !NON_RETRYABLE_PROVIDER_ERROR_PATTERNS.some(
|
|
522
|
+
(pattern) => pattern.test(errorMessage)
|
|
523
|
+
);
|
|
524
|
+
}
|
|
525
|
+
function nextProviderRetry(args) {
|
|
526
|
+
const delayMs = PROVIDER_RETRY_DELAYS_MS[args.attempt];
|
|
527
|
+
const errorMessage = args.lastAssistant?.errorMessage?.trim();
|
|
528
|
+
if (delayMs === void 0 || args.lastAssistant?.stopReason !== "error" || !errorMessage || !isRetryableProviderFailure(errorMessage)) {
|
|
529
|
+
return void 0;
|
|
530
|
+
}
|
|
531
|
+
const messages = trimTrailingAssistantMessages(args.messages);
|
|
532
|
+
if (messages.length === args.messages.length) {
|
|
533
|
+
return void 0;
|
|
534
|
+
}
|
|
535
|
+
const tailRole = getPiMessageRole(messages.at(-1));
|
|
536
|
+
if (tailRole !== "user" && tailRole !== "toolResult") {
|
|
537
|
+
return void 0;
|
|
538
|
+
}
|
|
539
|
+
return { delayMs, messages };
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
// src/chat/pi/client.ts
|
|
233
543
|
registerApiProvider({
|
|
234
544
|
api: "anthropic-messages",
|
|
235
545
|
stream: streamAnthropic,
|
|
@@ -250,7 +560,7 @@ function getPiGatewayApiKeyOverride() {
|
|
|
250
560
|
function extractText(message) {
|
|
251
561
|
return (message.content ?? []).filter((part) => part.type === "text" && typeof part.text === "string").map((part) => part.text ?? "").join("").trim();
|
|
252
562
|
}
|
|
253
|
-
function
|
|
563
|
+
function parseJsonCandidate2(text) {
|
|
254
564
|
const trimmed = text.trim();
|
|
255
565
|
if (!trimmed) return void 0;
|
|
256
566
|
try {
|
|
@@ -357,21 +667,26 @@ async function completeText(params) {
|
|
|
357
667
|
"gen_ai.chat",
|
|
358
668
|
logContextFromMetadata(params.modelId, params.metadata),
|
|
359
669
|
async () => {
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
670
|
+
let message;
|
|
671
|
+
try {
|
|
672
|
+
message = await completeSimple(
|
|
673
|
+
model,
|
|
674
|
+
{
|
|
675
|
+
systemPrompt: params.system,
|
|
676
|
+
messages: params.messages
|
|
677
|
+
},
|
|
678
|
+
{
|
|
679
|
+
...apiKey ? { apiKey } : {},
|
|
680
|
+
temperature: params.temperature,
|
|
681
|
+
maxTokens: params.maxTokens,
|
|
682
|
+
reasoning: params.thinkingLevel,
|
|
683
|
+
signal: params.signal,
|
|
684
|
+
metadata: params.metadata
|
|
685
|
+
}
|
|
686
|
+
);
|
|
687
|
+
} catch (error) {
|
|
688
|
+
throw createProviderError(error);
|
|
689
|
+
}
|
|
375
690
|
const outputText = extractText(message);
|
|
376
691
|
const outputMessagesAttribute = serializeGenAiAttribute(
|
|
377
692
|
messageAttributeMode === "metadata" ? [
|
|
@@ -401,17 +716,17 @@ async function completeText(params) {
|
|
|
401
716
|
};
|
|
402
717
|
setSpanAttributes(endAttributes);
|
|
403
718
|
if (message.stopReason === "error") {
|
|
404
|
-
const
|
|
719
|
+
const providerMessage2 = message.errorMessage?.trim() || "Unknown provider error";
|
|
405
720
|
logWarn(
|
|
406
721
|
"ai_completion_provider_error",
|
|
407
722
|
{},
|
|
408
723
|
{
|
|
409
724
|
...baseAttributes,
|
|
410
|
-
"exception.message":
|
|
725
|
+
"exception.message": providerMessage2
|
|
411
726
|
},
|
|
412
727
|
"AI completion returned provider error"
|
|
413
728
|
);
|
|
414
|
-
throw
|
|
729
|
+
throw createProviderError(providerMessage2);
|
|
415
730
|
}
|
|
416
731
|
return {
|
|
417
732
|
message,
|
|
@@ -454,6 +769,9 @@ async function completeObject(params) {
|
|
|
454
769
|
]
|
|
455
770
|
}));
|
|
456
771
|
} catch (error) {
|
|
772
|
+
if (isProviderRetryError(error)) {
|
|
773
|
+
throw error;
|
|
774
|
+
}
|
|
457
775
|
logException(
|
|
458
776
|
error,
|
|
459
777
|
"ai_completion_failed",
|
|
@@ -467,7 +785,7 @@ async function completeObject(params) {
|
|
|
467
785
|
);
|
|
468
786
|
throw error;
|
|
469
787
|
}
|
|
470
|
-
const candidate =
|
|
788
|
+
const candidate = parseJsonCandidate2(text);
|
|
471
789
|
const parsed = params.schema.safeParse(candidate);
|
|
472
790
|
if (!parsed.success) {
|
|
473
791
|
const preview = text.length > 400 ? `${text.slice(0, 400)}...` : text;
|
|
@@ -727,255 +1045,6 @@ function setSlackReactionConfig(overrides) {
|
|
|
727
1045
|
}
|
|
728
1046
|
}
|
|
729
1047
|
|
|
730
|
-
// src/chat/state/adapter.ts
|
|
731
|
-
import { createMemoryState } from "@chat-adapter/state-memory";
|
|
732
|
-
import { createRedisState } from "@chat-adapter/state-redis";
|
|
733
|
-
var ACTIVE_LOCK_TTL_MS = 9e4;
|
|
734
|
-
var ACTIVE_LOCK_HEARTBEAT_MS = 3e4;
|
|
735
|
-
var stateAdapter;
|
|
736
|
-
var redisStateAdapter;
|
|
737
|
-
function createPrefixedStateAdapter(base, prefix) {
|
|
738
|
-
const prefixed = (value) => `${prefix}:${value}`;
|
|
739
|
-
const unprefixed = (value) => value.startsWith(`${prefix}:`) ? value.slice(prefix.length + 1) : value;
|
|
740
|
-
const prefixLock = (lock) => ({
|
|
741
|
-
...lock,
|
|
742
|
-
threadId: prefixed(lock.threadId)
|
|
743
|
-
});
|
|
744
|
-
const unprefixLock = (lock) => ({
|
|
745
|
-
...lock,
|
|
746
|
-
threadId: unprefixed(lock.threadId)
|
|
747
|
-
});
|
|
748
|
-
return {
|
|
749
|
-
appendToList: (key, value, options) => base.appendToList(prefixed(key), value, options),
|
|
750
|
-
connect: () => base.connect(),
|
|
751
|
-
disconnect: () => base.disconnect(),
|
|
752
|
-
subscribe: (threadId) => base.subscribe(prefixed(threadId)),
|
|
753
|
-
unsubscribe: (threadId) => base.unsubscribe(prefixed(threadId)),
|
|
754
|
-
isSubscribed: (threadId) => base.isSubscribed(prefixed(threadId)),
|
|
755
|
-
acquireLock: async (threadId, ttlMs) => {
|
|
756
|
-
const lock = await base.acquireLock(prefixed(threadId), ttlMs);
|
|
757
|
-
return lock ? unprefixLock(lock) : null;
|
|
758
|
-
},
|
|
759
|
-
releaseLock: (lock) => base.releaseLock(prefixLock(lock)),
|
|
760
|
-
extendLock: async (lock, ttlMs) => {
|
|
761
|
-
const prefixedLock = prefixLock(lock);
|
|
762
|
-
const extended = await base.extendLock(prefixedLock, ttlMs);
|
|
763
|
-
if (extended) {
|
|
764
|
-
lock.expiresAt = prefixedLock.expiresAt;
|
|
765
|
-
}
|
|
766
|
-
return extended;
|
|
767
|
-
},
|
|
768
|
-
forceReleaseLock: (threadId) => base.forceReleaseLock(prefixed(threadId)),
|
|
769
|
-
enqueue: (threadId, entry, maxSize) => base.enqueue(prefixed(threadId), entry, maxSize),
|
|
770
|
-
dequeue: (threadId) => base.dequeue(prefixed(threadId)),
|
|
771
|
-
queueDepth: (threadId) => base.queueDepth(prefixed(threadId)),
|
|
772
|
-
get: (key) => base.get(prefixed(key)),
|
|
773
|
-
getList: (key) => base.getList(prefixed(key)),
|
|
774
|
-
set: (key, value, ttlMs) => base.set(prefixed(key), value, ttlMs),
|
|
775
|
-
setIfNotExists: (key, value, ttlMs) => base.setIfNotExists(prefixed(key), value, ttlMs),
|
|
776
|
-
delete: (key) => base.delete(prefixed(key))
|
|
777
|
-
};
|
|
778
|
-
}
|
|
779
|
-
function createQueuedStateAdapter(base, options) {
|
|
780
|
-
const heartbeats = /* @__PURE__ */ new Map();
|
|
781
|
-
const effectiveLockTtlMs = (ttlMs) => Math.max(ttlMs, ACTIVE_LOCK_TTL_MS);
|
|
782
|
-
const shouldHeartbeatLock = (ttlMs) => ttlMs <= ACTIVE_LOCK_TTL_MS;
|
|
783
|
-
const heartbeatKey = (lock) => `${lock.threadId}:${lock.token}`;
|
|
784
|
-
const stopHeartbeatByKey = (key) => {
|
|
785
|
-
const heartbeat = heartbeats.get(key);
|
|
786
|
-
if (!heartbeat) {
|
|
787
|
-
return;
|
|
788
|
-
}
|
|
789
|
-
clearInterval(heartbeat.timer);
|
|
790
|
-
heartbeats.delete(key);
|
|
791
|
-
};
|
|
792
|
-
const stopHeartbeat = (lock) => {
|
|
793
|
-
stopHeartbeatByKey(heartbeatKey(lock));
|
|
794
|
-
};
|
|
795
|
-
const stopHeartbeatsForThread = (threadId) => {
|
|
796
|
-
for (const [key, heartbeat] of heartbeats) {
|
|
797
|
-
if (heartbeat.lock.threadId === threadId) {
|
|
798
|
-
stopHeartbeatByKey(key);
|
|
799
|
-
}
|
|
800
|
-
}
|
|
801
|
-
};
|
|
802
|
-
const stopAllHeartbeats = () => {
|
|
803
|
-
for (const key of heartbeats.keys()) {
|
|
804
|
-
stopHeartbeatByKey(key);
|
|
805
|
-
}
|
|
806
|
-
};
|
|
807
|
-
const runHeartbeat = async (key) => {
|
|
808
|
-
const heartbeat = heartbeats.get(key);
|
|
809
|
-
if (!heartbeat || heartbeat.inFlight) {
|
|
810
|
-
return;
|
|
811
|
-
}
|
|
812
|
-
heartbeat.inFlight = true;
|
|
813
|
-
try {
|
|
814
|
-
if (Date.now() - heartbeat.startedAtMs >= options.activeLockMaxAgeMs) {
|
|
815
|
-
stopHeartbeatByKey(key);
|
|
816
|
-
return;
|
|
817
|
-
}
|
|
818
|
-
const extended = await base.extendLock(heartbeat.lock, heartbeat.ttlMs);
|
|
819
|
-
if (!extended) {
|
|
820
|
-
stopHeartbeatByKey(key);
|
|
821
|
-
return;
|
|
822
|
-
}
|
|
823
|
-
heartbeat.lock.expiresAt = Date.now() + heartbeat.ttlMs;
|
|
824
|
-
} catch {
|
|
825
|
-
} finally {
|
|
826
|
-
const current = heartbeats.get(key);
|
|
827
|
-
if (current === heartbeat) {
|
|
828
|
-
current.inFlight = false;
|
|
829
|
-
}
|
|
830
|
-
}
|
|
831
|
-
};
|
|
832
|
-
const startOrUpdateHeartbeat = (lock, ttlMs) => {
|
|
833
|
-
const key = heartbeatKey(lock);
|
|
834
|
-
const existing = heartbeats.get(key);
|
|
835
|
-
if (existing) {
|
|
836
|
-
existing.ttlMs = ttlMs;
|
|
837
|
-
return;
|
|
838
|
-
}
|
|
839
|
-
const timer = setInterval(() => {
|
|
840
|
-
void runHeartbeat(key);
|
|
841
|
-
}, ACTIVE_LOCK_HEARTBEAT_MS);
|
|
842
|
-
timer.unref?.();
|
|
843
|
-
heartbeats.set(key, {
|
|
844
|
-
inFlight: false,
|
|
845
|
-
lock,
|
|
846
|
-
startedAtMs: Date.now(),
|
|
847
|
-
timer,
|
|
848
|
-
ttlMs
|
|
849
|
-
});
|
|
850
|
-
};
|
|
851
|
-
const acquireLock = async (threadId, ttlMs) => {
|
|
852
|
-
const effectiveTtlMs = effectiveLockTtlMs(ttlMs);
|
|
853
|
-
const lock = await base.acquireLock(threadId, effectiveTtlMs);
|
|
854
|
-
if (lock && shouldHeartbeatLock(ttlMs)) {
|
|
855
|
-
startOrUpdateHeartbeat(lock, effectiveTtlMs);
|
|
856
|
-
}
|
|
857
|
-
return lock;
|
|
858
|
-
};
|
|
859
|
-
return {
|
|
860
|
-
appendToList: (key, value, options2) => base.appendToList(key, value, options2),
|
|
861
|
-
connect: () => base.connect(),
|
|
862
|
-
disconnect: async () => {
|
|
863
|
-
stopAllHeartbeats();
|
|
864
|
-
await base.disconnect();
|
|
865
|
-
},
|
|
866
|
-
subscribe: (threadId) => base.subscribe(threadId),
|
|
867
|
-
unsubscribe: (threadId) => base.unsubscribe(threadId),
|
|
868
|
-
isSubscribed: (threadId) => base.isSubscribed(threadId),
|
|
869
|
-
acquireLock,
|
|
870
|
-
releaseLock: async (lock) => {
|
|
871
|
-
stopHeartbeat(lock);
|
|
872
|
-
await base.releaseLock(lock);
|
|
873
|
-
},
|
|
874
|
-
extendLock: async (lock, ttlMs) => {
|
|
875
|
-
const effectiveTtlMs = effectiveLockTtlMs(ttlMs);
|
|
876
|
-
const extended = await base.extendLock(lock, effectiveTtlMs);
|
|
877
|
-
if (extended) {
|
|
878
|
-
lock.expiresAt = Date.now() + effectiveTtlMs;
|
|
879
|
-
if (shouldHeartbeatLock(ttlMs)) {
|
|
880
|
-
startOrUpdateHeartbeat(lock, effectiveTtlMs);
|
|
881
|
-
} else {
|
|
882
|
-
stopHeartbeat(lock);
|
|
883
|
-
}
|
|
884
|
-
} else {
|
|
885
|
-
stopHeartbeat(lock);
|
|
886
|
-
}
|
|
887
|
-
return extended;
|
|
888
|
-
},
|
|
889
|
-
forceReleaseLock: async (threadId) => {
|
|
890
|
-
stopHeartbeatsForThread(threadId);
|
|
891
|
-
await base.forceReleaseLock(threadId);
|
|
892
|
-
},
|
|
893
|
-
enqueue: (threadId, entry, maxSize) => base.enqueue(threadId, entry, maxSize),
|
|
894
|
-
dequeue: (threadId) => base.dequeue(threadId),
|
|
895
|
-
queueDepth: (threadId) => base.queueDepth(threadId),
|
|
896
|
-
get: (key) => base.get(key),
|
|
897
|
-
getList: (key) => base.getList(key),
|
|
898
|
-
set: (key, value, ttlMs) => base.set(key, value, ttlMs),
|
|
899
|
-
setIfNotExists: (key, value, ttlMs) => base.setIfNotExists(key, value, ttlMs),
|
|
900
|
-
delete: (key) => base.delete(key)
|
|
901
|
-
};
|
|
902
|
-
}
|
|
903
|
-
function withOptionalPrefix(base, prefix) {
|
|
904
|
-
return prefix ? createPrefixedStateAdapter(base, prefix) : base;
|
|
905
|
-
}
|
|
906
|
-
function createStateAdapter() {
|
|
907
|
-
const config = getChatConfig();
|
|
908
|
-
const activeLockMaxAgeMs = config.bot.turnTimeoutMs + ACTIVE_LOCK_TTL_MS;
|
|
909
|
-
if (config.state.adapter === "memory") {
|
|
910
|
-
redisStateAdapter = void 0;
|
|
911
|
-
return createQueuedStateAdapter(
|
|
912
|
-
withOptionalPrefix(createMemoryState(), config.state.keyPrefix),
|
|
913
|
-
{ activeLockMaxAgeMs }
|
|
914
|
-
);
|
|
915
|
-
}
|
|
916
|
-
if (!config.state.redisUrl) {
|
|
917
|
-
throw new Error("REDIS_URL is required for durable Slack thread state");
|
|
918
|
-
}
|
|
919
|
-
const redisState = createRedisState({
|
|
920
|
-
url: config.state.redisUrl
|
|
921
|
-
});
|
|
922
|
-
redisStateAdapter = redisState;
|
|
923
|
-
return createQueuedStateAdapter(
|
|
924
|
-
withOptionalPrefix(redisState, config.state.keyPrefix),
|
|
925
|
-
{ activeLockMaxAgeMs }
|
|
926
|
-
);
|
|
927
|
-
}
|
|
928
|
-
function getOptionalRedisStateAdapter() {
|
|
929
|
-
getStateAdapter();
|
|
930
|
-
return redisStateAdapter;
|
|
931
|
-
}
|
|
932
|
-
async function getConnectedStateContext() {
|
|
933
|
-
const adapter = getStateAdapter();
|
|
934
|
-
await adapter.connect();
|
|
935
|
-
return {
|
|
936
|
-
redisStateAdapter: getOptionalRedisStateAdapter(),
|
|
937
|
-
stateAdapter: adapter
|
|
938
|
-
};
|
|
939
|
-
}
|
|
940
|
-
function getStateAdapter() {
|
|
941
|
-
if (!stateAdapter) {
|
|
942
|
-
stateAdapter = createStateAdapter();
|
|
943
|
-
}
|
|
944
|
-
return stateAdapter;
|
|
945
|
-
}
|
|
946
|
-
async function disconnectStateAdapter() {
|
|
947
|
-
if (!stateAdapter) {
|
|
948
|
-
return;
|
|
949
|
-
}
|
|
950
|
-
try {
|
|
951
|
-
await stateAdapter.disconnect();
|
|
952
|
-
} finally {
|
|
953
|
-
stateAdapter = void 0;
|
|
954
|
-
redisStateAdapter = void 0;
|
|
955
|
-
}
|
|
956
|
-
}
|
|
957
|
-
|
|
958
|
-
// src/chat/sandbox/paths.ts
|
|
959
|
-
function normalizeWorkspaceRoot(input) {
|
|
960
|
-
const candidate = (input ?? "").trim();
|
|
961
|
-
if (!candidate) {
|
|
962
|
-
return "/vercel/sandbox";
|
|
963
|
-
}
|
|
964
|
-
const normalized = candidate.replace(/\/+$/, "");
|
|
965
|
-
return normalized.startsWith("/") ? normalized : `/${normalized}`;
|
|
966
|
-
}
|
|
967
|
-
var SANDBOX_WORKSPACE_ROOT = normalizeWorkspaceRoot(
|
|
968
|
-
process.env.VERCEL_SANDBOX_WORKSPACE_DIR
|
|
969
|
-
);
|
|
970
|
-
var SANDBOX_SKILLS_ROOT = `${SANDBOX_WORKSPACE_ROOT}/skills`;
|
|
971
|
-
var SANDBOX_DATA_ROOT = `${SANDBOX_WORKSPACE_ROOT}/data`;
|
|
972
|
-
function sandboxSkillDir(skillName) {
|
|
973
|
-
return `${SANDBOX_SKILLS_ROOT}/${skillName}`;
|
|
974
|
-
}
|
|
975
|
-
function sandboxSkillFile(skillName) {
|
|
976
|
-
return `${sandboxSkillDir(skillName)}/SKILL.md`;
|
|
977
|
-
}
|
|
978
|
-
|
|
979
1048
|
export {
|
|
980
1049
|
toOptionalTrimmed,
|
|
981
1050
|
parseSlackThreadId,
|
|
@@ -988,6 +1057,28 @@ export {
|
|
|
988
1057
|
toGenAiPayloadMetadata,
|
|
989
1058
|
toGenAiPayloadTraceAttributes,
|
|
990
1059
|
toGenAiMessagesTraceAttributes,
|
|
1060
|
+
TURN_CONTEXT_TAG,
|
|
1061
|
+
getSessionIdentifiers,
|
|
1062
|
+
isExecutionEscapeResponse,
|
|
1063
|
+
isRawToolPayloadResponse,
|
|
1064
|
+
toObservablePromptPart,
|
|
1065
|
+
summarizeMessageText,
|
|
1066
|
+
buildUserTurnText,
|
|
1067
|
+
encodeNonImageAttachmentForPrompt,
|
|
1068
|
+
isToolResultMessage,
|
|
1069
|
+
normalizeToolNameFromResult,
|
|
1070
|
+
isToolResultError,
|
|
1071
|
+
isAssistantMessage,
|
|
1072
|
+
getPiMessageRole,
|
|
1073
|
+
prependMissingRuntimeTurnContext,
|
|
1074
|
+
hasRuntimeTurnContext,
|
|
1075
|
+
stripRuntimeTurnContext,
|
|
1076
|
+
extractAssistantText,
|
|
1077
|
+
getTerminalAssistantMessages,
|
|
1078
|
+
upsertActiveSkill,
|
|
1079
|
+
trimTrailingAssistantMessages,
|
|
1080
|
+
isProviderRetryError,
|
|
1081
|
+
nextProviderRetry,
|
|
991
1082
|
GEN_AI_PROVIDER_NAME,
|
|
992
1083
|
GEN_AI_SERVER_ADDRESS,
|
|
993
1084
|
GEN_AI_SERVER_PORT,
|
|
@@ -1007,14 +1098,5 @@ export {
|
|
|
1007
1098
|
getSlackClientSecret,
|
|
1008
1099
|
getRuntimeMetadata,
|
|
1009
1100
|
getSlackReactionConfig,
|
|
1010
|
-
setSlackReactionConfig
|
|
1011
|
-
ACTIVE_LOCK_TTL_MS,
|
|
1012
|
-
getConnectedStateContext,
|
|
1013
|
-
getStateAdapter,
|
|
1014
|
-
disconnectStateAdapter,
|
|
1015
|
-
SANDBOX_WORKSPACE_ROOT,
|
|
1016
|
-
SANDBOX_SKILLS_ROOT,
|
|
1017
|
-
SANDBOX_DATA_ROOT,
|
|
1018
|
-
sandboxSkillDir,
|
|
1019
|
-
sandboxSkillFile
|
|
1101
|
+
setSlackReactionConfig
|
|
1020
1102
|
};
|