@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.
Files changed (67) hide show
  1. package/dist/app.js +1779 -746
  2. package/dist/build/virtual-config.d.ts +2 -2
  3. package/dist/chat/agent-dispatch/heartbeat.d.ts +2 -2
  4. package/dist/chat/agent-dispatch/store.d.ts +4 -1
  5. package/dist/chat/agent-dispatch/types.d.ts +2 -4
  6. package/dist/chat/agent-dispatch/validation.d.ts +3 -2
  7. package/dist/chat/credentials/context.d.ts +49 -24
  8. package/dist/chat/credentials/user-token-store.d.ts +6 -0
  9. package/dist/chat/destination.d.ts +12 -0
  10. package/dist/chat/ingress/message-router.d.ts +1 -1
  11. package/dist/chat/mcp/auth-store.d.ts +2 -0
  12. package/dist/chat/mcp/oauth.d.ts +2 -0
  13. package/dist/chat/oauth-flow.d.ts +7 -0
  14. package/dist/chat/plugins/agent-hooks.d.ts +9 -9
  15. package/dist/chat/plugins/auth/auth-token-placeholder.d.ts +2 -2
  16. package/dist/chat/plugins/auth/oauth-request.d.ts +3 -1
  17. package/dist/chat/plugins/credential-hooks.d.ts +53 -0
  18. package/dist/chat/plugins/logging.d.ts +1 -1
  19. package/dist/chat/plugins/state.d.ts +1 -1
  20. package/dist/chat/plugins/types.d.ts +19 -23
  21. package/dist/chat/respond.d.ts +2 -0
  22. package/dist/chat/runtime/reply-executor.d.ts +3 -1
  23. package/dist/chat/runtime/slack-runtime.d.ts +8 -3
  24. package/dist/chat/sandbox/egress-credentials.d.ts +34 -0
  25. package/dist/chat/sandbox/egress-schemas.d.ts +105 -0
  26. package/dist/chat/sandbox/egress-session.d.ts +17 -17
  27. package/dist/chat/sandbox/sandbox.d.ts +3 -0
  28. package/dist/chat/sandbox/session.d.ts +1 -0
  29. package/dist/chat/services/mcp-auth-orchestration.d.ts +2 -0
  30. package/dist/chat/services/pending-auth.d.ts +2 -0
  31. package/dist/chat/services/plugin-auth-orchestration.d.ts +2 -0
  32. package/dist/chat/services/provider-retry.d.ts +13 -4
  33. package/dist/chat/services/timeout-resume.d.ts +2 -0
  34. package/dist/chat/services/turn-session-record.d.ts +6 -0
  35. package/dist/chat/slack/attachment-fetchers.d.ts +11 -0
  36. package/dist/chat/state/conversation-details.d.ts +46 -0
  37. package/dist/chat/state/conversation.d.ts +1 -0
  38. package/dist/chat/state/turn-session.d.ts +4 -3
  39. package/dist/chat/task-execution/queue.d.ts +2 -0
  40. package/dist/chat/task-execution/store.d.ts +5 -0
  41. package/dist/chat/task-execution/vercel-callback.d.ts +4 -0
  42. package/dist/chat/task-execution/vercel-queue.d.ts +2 -0
  43. package/dist/chat/task-execution/worker.d.ts +4 -2
  44. package/dist/chat/tools/slack/context.d.ts +3 -0
  45. package/dist/chat/tools/types.d.ts +21 -2
  46. package/dist/chunk-76YMBKW7.js +326 -0
  47. package/dist/{chunk-PIVOJIUD.js → chunk-B5HKWWQB.js} +9 -5
  48. package/dist/chunk-BBXYXOJW.js +1858 -0
  49. package/dist/{chunk-V47RLIO2.js → chunk-GT67ZWZQ.js} +4 -4
  50. package/dist/{chunk-UQQSW7QB.js → chunk-HOGQL2H6.js} +197 -343
  51. package/dist/{chunk-75UZ4JLC.js → chunk-IGLNC5H6.js} +21 -9
  52. package/dist/{chunk-EBVQXCD2.js → chunk-JS4HURDT.js} +362 -280
  53. package/dist/chunk-R62YWUNO.js +264 -0
  54. package/dist/{chunk-OIIXZOOC.js → chunk-UXG6TU2U.js} +311 -2015
  55. package/dist/cli/check.js +4 -4
  56. package/dist/cli/init.js +18 -1
  57. package/dist/cli/snapshot-warmup.js +5 -4
  58. package/dist/nitro.d.ts +1 -1
  59. package/dist/nitro.js +21 -19
  60. package/dist/plugins.d.ts +2 -2
  61. package/dist/reporting.d.ts +8 -4
  62. package/dist/reporting.js +72 -29
  63. package/package.json +6 -4
  64. package/dist/chat/plugins/auth/github-app-broker.d.ts +0 -4
  65. package/dist/chat/plugins/github-permissions.d.ts +0 -11
  66. package/dist/chat/queue/thread-message-dispatcher.d.ts +0 -33
  67. package/dist/chunk-KVZL5NZS.js +0 -519
@@ -6,7 +6,7 @@ import {
6
6
  setSpanAttributes,
7
7
  toOptionalString,
8
8
  withSpan
9
- } from "./chunk-OIIXZOOC.js";
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 parseJsonCandidate(text) {
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
- const message = await completeSimple(
361
- model,
362
- {
363
- systemPrompt: params.system,
364
- messages: params.messages
365
- },
366
- {
367
- ...apiKey ? { apiKey } : {},
368
- temperature: params.temperature,
369
- maxTokens: params.maxTokens,
370
- reasoning: params.thinkingLevel,
371
- signal: params.signal,
372
- metadata: params.metadata
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 providerMessage = message.errorMessage?.trim() || "Unknown provider error";
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": providerMessage
725
+ "exception.message": providerMessage2
411
726
  },
412
727
  "AI completion returned provider error"
413
728
  );
414
- throw new Error(`AI provider error: ${providerMessage}`);
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 = parseJsonCandidate(text);
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
  };