@sentry/junior 0.222.0 → 0.224.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 (61) hide show
  1. package/dist/{agent-hooks-DkriF84n.js → agent-hooks-DNLBwmsh.js} +60 -16
  2. package/dist/{agent-7kKtDe-6.js → agent-runner-9g6TQk9y.js} +879 -69
  3. package/dist/api/schema.d.ts +160 -11
  4. package/dist/api/schema.js +2 -2
  5. package/dist/api.js +16 -16
  6. package/dist/{app-C0zbsnr7.d.ts → app-BbSD6EtD.d.ts} +1 -1
  7. package/dist/app.d.ts +1 -1
  8. package/dist/app.js +1598 -69
  9. package/dist/{briefs-CNH1ztn5.js → briefs-CjXnGKzZ.js} +6 -6
  10. package/dist/{catalog-runtime-XH_LFA_7.js → catalog-runtime-CVZ9TEuK.js} +1 -1
  11. package/dist/{checkpoint-CRod8es5.js → checkpoint-Ceck_fLO.js} +12 -9
  12. package/dist/cli/chat.js +14 -15
  13. package/dist/cli/check.js +3 -3
  14. package/dist/cli/main.js +1 -1
  15. package/dist/cli/plugins.js +3 -3
  16. package/dist/cli/snapshot-create.js +3 -3
  17. package/dist/cli/upgrade.js +3 -3
  18. package/dist/{client-CcSboWx_.js → client-B27ufhzl.js} +56 -6
  19. package/dist/{config-CGYv-YPE.js → config-BAhl7-RC.js} +6 -7
  20. package/dist/{conversation-BBDt88di.js → conversation-1HUoZr2l.js} +33 -5
  21. package/dist/{conversation-privacy-BGjdDVWD.js → conversation-privacy-C-4JQzKD.js} +1 -1
  22. package/dist/credential-sync-BXHIxbtS.js +940 -0
  23. package/dist/{execution-stats-D97ePWuM.js → execution-stats-DikPOxJ1.js} +3 -3
  24. package/dist/{executor-BLBZ-UB8.js → executor-BG2Armby.js} +1 -1
  25. package/dist/{history-CaV8Ww7M.d.ts → history-yaSjGMD_.d.ts} +18 -0
  26. package/dist/instrumentation.js +2 -2
  27. package/dist/{job-queue-_8gxwDzt.js → job-queue-D4oAVrL9.js} +1 -1
  28. package/dist/{logging-qmsMdU0y.js → logging-iY0ssa2q.js} +1 -1
  29. package/dist/nitro.d.ts +1 -1
  30. package/dist/nitro.js +3 -3
  31. package/dist/{pending-auth-_1Ry-_Bw.js → oauth-authorization-BIijVS2N.js} +6 -48
  32. package/dist/{oauth-callback-server-Mjfm46A5.js → oauth-callback-server-DPkK3kzi.js} +4 -4
  33. package/dist/{profile-DbG8AlNd.js → profile-6KWhg_-U.js} +5 -9
  34. package/dist/{prompt-B8rCQI7K.js → prompt-90miy8VV.js} +1 -1
  35. package/dist/{registry-DGyQCsiy.js → registry-Ctgdjt-W.js} +1 -1
  36. package/dist/{resolve-D5SqDbCA.js → resolve-CB45ZukQ.js} +2 -2
  37. package/dist/{runner-BRAFjJhe.js → runner-BwKCqoF3.js} +9 -10
  38. package/dist/{sandbox-egress-signals-CdI1aVHT.js → sandbox-egress-signals-Cmy8ODat.js} +1 -1
  39. package/dist/{schema-CTOODa3L.js → schema-RpbW5Od2.js} +1 -1
  40. package/dist/{short-title-CB2fxEyx.js → short-title-xEkZwkcV.js} +2 -2
  41. package/dist/{skills-BoS3b8cg.js → skills-c2bMDVRS.js} +2 -2
  42. package/dist/{spawn-BXCU1udr.js → spawn-Cg6lyjE7.js} +2 -2
  43. package/dist/{stats-qUXQZq-w.js → stats-DP9zl3Jg.js} +19 -17
  44. package/dist/{task-queue-Di_q8922.js → task-queue-SL4uLfBB.js} +3 -3
  45. package/dist/{task-runner-4rCppQMo.js → task-runner-fntmnqzx.js} +9 -84
  46. package/dist/{thread-state-2Paza8DV.js → thread-state-Wfr0GdKS.js} +5 -5
  47. package/dist/{auth-pause-state-3OT67TA8.js → turn-failure-response-DLjSEm-R.js} +74 -84
  48. package/dist/{turn-lifecycle-f-y0Irpi.js → turn-lifecycle-5_jvLqRz.js} +1 -1
  49. package/dist/turn-session-routing-CQaysLoe.js +77 -0
  50. package/dist/{validation-Be9lSORU.js → validation-CnZkCN7o.js} +1 -1
  51. package/dist/{version-BydrOZMQ.js → version-C6lA-h_o.js} +1 -1
  52. package/dist/version.js +1 -1
  53. package/dist/{web-authorization-BdfOkTuG.js → web-authorization-Bbx999_q.js} +1 -1
  54. package/dist/{web-input-DegYUyKU.js → web-input-BoE1xE-K.js} +7 -7
  55. package/dist/{work-Citqpnfc.js → work-4Nhz9mVv.js} +52 -10
  56. package/dist/{worker-Bn_HFyvM.js → worker-CdWPsNL2.js} +5 -5
  57. package/package.json +4 -4
  58. package/dist/agent-runner-D0ImXS7U.js +0 -679
  59. package/dist/credential-sync-BfzGNruT.js +0 -2757
  60. package/dist/experimental-CIXf7Uye.js +0 -46
  61. package/dist/turn-execution--F7a9kEU.js +0 -47
@@ -1,28 +1,28 @@
1
- import { C as resolveGatewayCredential, S as getGatewayApiKey, _ as findProviderError, b as isProviderRetryError, c as COMPACTION_SUMMARY_PREFIX, d as isCompactionSummaryText, f as renderCurrentInstruction, g as createProviderError, h as ProviderError, i as completeText, l as MODEL_HANDOFF_SUMMARY_PREFIX, m as escapeXml, n as GEN_AI_SERVER_ADDRESS, o as resolveGatewayModel, p as unwrapCurrentInstruction, r as completeObject, s as ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX, t as GEN_AI_PROVIDER_NAME, u as hasCompactedConversationContext, v as getProviderErrorAttributes, x as MISSING_GATEWAY_CREDENTIALS_ERROR } from "./client-CcSboWx_.js";
2
- import { C as withSpan, F as isRecord$2, P as normalizeIdentityEmail, S as withLogContext, _ as setSpanAttributes, a as extractGenAiUsageSummary, b as summarizeMessageText, c as getTracePropagationHeaders, d as logInfo, f as logWarn, h as serializeGenAiAttribute, i as extractGenAiUsageAttributes, l as logError, p as normalizeGenAiFinishReason, s as getLogContextAttributesForSpan, u as logException, v as setSpanStatus, y as setTags } from "./logging-qmsMdU0y.js";
1
+ import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
2
+ import { C as resolveGatewayCredential, S as getGatewayApiKey, _ as findProviderError, b as isProviderRetryError, c as COMPACTION_SUMMARY_PREFIX, d as isCompactionSummaryText, f as renderCurrentInstruction, g as createProviderError, h as ProviderError, i as completeText, l as MODEL_HANDOFF_SUMMARY_PREFIX, m as escapeXml, n as GEN_AI_SERVER_ADDRESS, o as resolveGatewayModel, p as unwrapCurrentInstruction, r as completeObject, s as ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX, t as GEN_AI_PROVIDER_NAME, u as hasCompactedConversationContext, v as getProviderErrorAttributes, x as MISSING_GATEWAY_CREDENTIALS_ERROR } from "./client-B27ufhzl.js";
3
+ import { C as withSpan, F as isRecord$2, P as normalizeIdentityEmail, S as withLogContext, _ as setSpanAttributes, a as extractGenAiUsageSummary, b as summarizeMessageText, c as getTracePropagationHeaders, d as logInfo, f as logWarn, h as serializeGenAiAttribute, i as extractGenAiUsageAttributes, l as logError, p as normalizeGenAiFinishReason, s as getLogContextAttributesForSpan, u as logException, v as setSpanStatus, y as setTags } from "./logging-iY0ssa2q.js";
3
4
  import { m as startInactiveSpan, v as withActiveSpan } from "./sentry-CgWZM_kV.js";
4
- import { a as toCanonicalInputMessage, c as toGenAiPayloadMetadata, i as runWithConversationPrivacy, l as toGenAiPayloadTraceAttributes, o as toCanonicalOutputMessage, r as resolveConversationPrivacy, s as toGenAiMessagesTraceAttributes, t as canExposeConversationPayload } from "./conversation-privacy-BGjdDVWD.js";
5
- import { h as normalizeSlackEmojiName, n as botConfig } from "./config-CGYv-YPE.js";
5
+ import { a as toCanonicalInputMessage, c as toGenAiPayloadMetadata, i as runWithConversationPrivacy, l as toGenAiPayloadTraceAttributes, o as toCanonicalOutputMessage, r as resolveConversationPrivacy, s as toGenAiMessagesTraceAttributes, t as canExposeConversationPayload } from "./conversation-privacy-C-4JQzKD.js";
6
+ import { h as normalizeSlackEmojiName, n as botConfig } from "./config-BAhl7-RC.js";
6
7
  import { a as modelIdForProfile, c as TURN_REASONING_LEVELS, i as formatModelProfiles, o as modelProfileSchema, r as formatModelProfile, s as profileConfig, t as ModelProfileNotConfiguredError } from "./model-profile-CZmVoGdX.js";
7
8
  import { _ as parseSlackMessageTs, g as parseSlackUserId, h as parseSlackTeamId, i as credentialUserSubjectId, l as isUserActor, m as parseSlackChannelReferenceId, n as credentialContextSchema, p as parseSlackChannelId, t as credentialContextForActor } from "./context-Bplcgypl.js";
8
- import { A as tryWorkspaceRepoCheckoutPath, B as downloadPrivateSlackFile, C as invalidateReadySnapshot, D as SANDBOX_RUNTIME, E as setWorkspaceSnapshotBuild, Gt as getDb, Ht as getConversationEventStore, J as withSlackRetries, K as normalizeSlackConversationId, Kt as getSqlExecutor, M as resolveChannelCapabilities, O as deleteWorkspaceSnapshotBuilders, Q as readActorIdentity, S as WorkspaceValidationError, T as setWorkspaceSnapshot, U as getSlackClient, Ut as getConversationMessageSearchStore, V as getFilePermalink, W as isConversationScopedChannel, Wt as getConversationStore, _ as deleteWorkspace, at as STOP_WATCHING_TOOL_NAME, b as listWorkspaces, bt as createPluginLogger, c as getPluginTools, dt as findMatchingWatches, en as addAgentTurnUsage, ft as listWatches, g as createWorkspace, ht as getDashboardTaskLink, in as hasAgentTurnUsage, it as RESOURCE_WATCH_TOOL_SOURCE, j as workspaceRepoCheckoutPath, k as getWorkspaceSnapshotBuilder, l as getPluginUserPromptContributions, lt as createWatch, mt as getDashboardConversationLink, nt as RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS, ot as cancelSubscriptions, pt as JUNIOR_THREAD_STATE_TTL_MS, r as createPluginHookRunner, s as getPluginSystemPromptContributions, st as cancelWatch, tt as getSlackToolContext, u as getPlugins, un as parseDestination, ut as deliverWatch, v as getWorkspace, w as loadSnapshotsForProfile, x as updateWorkspace, y as getWorkspaceByName, z as SlackActionError } from "./agent-hooks-DkriF84n.js";
9
+ import { A as tryWorkspaceRepoCheckoutPath, B as downloadPrivateSlackFile, C as invalidateReadySnapshot, D as SANDBOX_RUNTIME, E as setWorkspaceSnapshotBuild, Ft as saveObjectAnnotations, G as isDmChannel$1, Gt as getConversationMessageSearchStore, It as createPluginAnnotations, J as withSlackRetries, Jt as getSqlExecutor, K as normalizeSlackConversationId, Kt as getConversationStore, M as resolveChannelCapabilities, O as deleteWorkspaceSnapshotBuilders, Q as readActorIdentity, S as WorkspaceValidationError, T as setWorkspaceSnapshot, U as getSlackClient, V as getFilePermalink, W as isConversationScopedChannel, Wt as getConversationEventStore, _ as deleteWorkspace, at as STOP_WATCHING_TOOL_NAME, b as listWorkspaces, bt as createPluginLogger, c as getPluginTools, dt as findMatchingWatches, fn as parseDestination, ft as listWatches, g as createWorkspace, ht as getDashboardTaskLink, it as RESOURCE_WATCH_TOOL_SOURCE, j as workspaceRepoCheckoutPath, k as getWorkspaceSnapshotBuilder, l as getPluginUserPromptContributions, lt as createWatch, mt as getDashboardConversationLink, n as applyPluginFormatMarkdown, nn as addAgentTurnUsage, nt as RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS, on as hasAgentTurnUsage, ot as cancelSubscriptions, pt as JUNIOR_THREAD_STATE_TTL_MS, qt as getDb, r as createPluginHookRunner, s as getPluginSystemPromptContributions, st as cancelWatch, tt as getSlackToolContext, u as getPlugins, ut as deliverWatch, v as getWorkspace, w as loadSnapshotsForProfile, x as updateWorkspace, y as getWorkspaceByName, z as SlackActionError } from "./agent-hooks-DNLBwmsh.js";
9
10
  import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
10
- import { D as juniorIdentities, E as juniorConversations, a as juniorSchedulerRuns, d as AGENT_INVOCATION_MAILBOX_STATUSES, g as juniorConversationBriefs, k as juniorArtifacts, w as juniorAttachments, x as juniorConversationAnnotations } from "./executor-BLBZ-UB8.js";
11
- import { F as conversationBriefSchema, G as contextProvenance, J as sameActorIdentity, K as instructionActors, L as KNOWN_CONVERSATION_EVENT_TYPES, N as briefLinkSchema, P as briefOutcomeStatusSchema, j as automationCardSchema, q as instructionProvenanceFor } from "./conversation-BBDt88di.js";
11
+ import { D as juniorIdentities, E as juniorConversations, a as juniorSchedulerRuns, d as AGENT_INVOCATION_MAILBOX_STATUSES, g as juniorConversationBriefs, k as juniorArtifacts, w as juniorAttachments, x as juniorConversationAnnotations } from "./executor-BG2Armby.js";
12
+ import { F as briefLinkSchema, I as briefOutcomeStatusSchema, J as instructionActors, L as conversationBriefSchema, X as sameActorIdentity, Y as instructionProvenanceFor, j as messageCardRefSchema, q as contextProvenance, z as KNOWN_CONVERSATION_EVENT_TYPES } from "./conversation-1HUoZr2l.js";
12
13
  import { a as listReferenceFiles, c as soulPathCandidates, l as worldPathCandidates } from "./package-discovery-7a9YqLhE.js";
13
- import { a as resolveAuthTokenPlaceholder, c as fetchWithBoundedOAuthErrorBodies, d as CredentialUnavailableError, i as resolvePluginHeaderEnvRefs, l as getOAuthProviderErrorAttributes, o as resolvePluginCommandEnv, s as OAuthProviderError } from "./registry-DGyQCsiy.js";
14
- import { n as pluginCatalogRuntime } from "./catalog-runtime-XH_LFA_7.js";
15
- import { a as parseSkillInvocation, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-BoS3b8cg.js";
16
- import { A as TURN_CONTEXT_TAG, C as isContinuablePiBoundary, D as retainRuntimeTurnContext, E as normalizeToolNameFromResult, H as recordAuthorizationRequested, I as loadTurnRoute, K as recordToolExecutionStarted, L as openConversationProjection, N as loadConnectedMcpProviders, O as stripRuntimeTurnContext, P as loadConversationProjection, R as recordAgentsInstructionsUpdated, S as isAssistantMessage, T as isToolResultMessage, U as recordGuardianActionReviewed, W as recordMcpProviderConnected, _ as getPiMessageRole, b as hasRuntimeTurnContext, dt as estimateTextTokens, f as TurnSliceLimitExceededError, ft as getAgentContextCompactionTriggerTokens, g as extractAssistantText, h as isTurnExecutionLimitExceededError, ht as piMessageFromHistoryItem, k as trimTrailingAssistantMessages, mt as historyItemFromPiMessage, n as loadTurnCheckpoint, p as assertTurnToolCallLimit, pt as getAgentContextInputLimitTokens, q as recordTurnRoute, r as saveTurnCheckpoint, t as continuableMessages, v as getTerminalAssistantMessages, w as isToolResultError, y as getUserMessageInstructionText, z as recordAttachmentsDelivered } from "./checkpoint-CRod8es5.js";
17
- import { n as generateShortTitle, r as resolveTaskTitle, t as fallbackShortTitle } from "./short-title-CB2fxEyx.js";
18
- import { A as sleep$1, D as fenceLock, E as acquireActiveLock, F as sandboxSkillDir, I as sandboxSkillFile, L as sandboxSkillPathResolution, M as SANDBOX_REPOS_ROOT, N as SANDBOX_SKILLS_ROOT, P as SANDBOX_WORKSPACE_ROOT, _ as workspaceSnapshotNotReadyUserMessage, a as isAbortError, b as buildNonInteractiveShellScript, c as isSandboxCommandStreamInterruptedError, d as isSnapshottingError, f as throwSandboxOperationError, g as isWorkspaceSnapshotNotReadyError, h as getWorkspaceSnapshotNotReadyError, i as ensureDockerDaemon, j as SANDBOX_DATA_ROOT, k as withLock, l as isSandboxMissingError, m as WorkspaceSnapshotNotReadyError, n as hash, o as isAlreadyExistsError, p as wrapSandboxSetupError, r as isStale, s as isSandboxApiTransientError, t as create, u as isSandboxUnavailableError, v as getVercelSandboxCredentials, w as getStateAdapter, x as runNonInteractiveCommand, y as buildCommandScript } from "./profile-DbG8AlNd.js";
19
- import { $ as workspaceSnapshotFinishedEvent, B as createEventAutomation, C as SCHEDULED_AUTOMATION_SYSTEM_ACTOR, D as eventAutomationMatchesDestination, E as eventAutomationListToolResultSchema, F as effectiveTaskOutcomes, Gt as makeStructuredToolOutput, I as moveTaskOutcomes, Jt as ToolInputError, L as resolveTaskOutcomes, M as requireEventAutomationSlackContext, N as requireSupportedEventAutomationTrigger, O as eventAutomationToolResult, P as writableEventAutomation, Q as WORKSPACE_SNAPSHOT_READY_EVENT, R as taskOutcomeInputSchema, T as compactEventAutomation, Ut as injectContinuationToolName, V as deleteEventAutomation, W as getEventAutomation, Wt as juniorToolOutputSchema, X as getEventCatalog, Y as saveEventAutomation, Z as WORKSPACE_SNAPSHOT_FAILED_EVENT, at as normalizeCatalogEventIdentifier, c as parseSlackThreadId, ct as registeredEventTypeSchema, et as workspaceSnapshotWatch, f as createScheduledAutomation, g as listScheduledAutomationsForTeam, i as readLiveAttachment, it as hasPluginEventCatalogEntries, j as registeredEventAutomationTriggerSchema, k as eventAutomationToolResultSchema, lt as registeredResourceTypeSchema, nt as eventNamespaceSchema, ot as pluginSupportsEvent, pt as appendAndEnqueueInboundMessage, q as listEventAutomationsForTeam, rt as hasEventCatalogEntries, s as storeAttachments, st as registeredEventMatchSchema, t as incrementStat, tt as eventGuidance, ut as requireSupportedEventMatch, v as readScheduledAutomation, y as saveScheduledAutomation } from "./stats-qUXQZq-w.js";
20
- import { _ as isTurnInputCommitLostError, c as sanitizeAssistantText, f as CooperativeTurnYieldError, l as NO_REPLY_MARKER, n as canReusePendingAuthLink, o as decideReply, p as TurnInputCommitLostError, s as getAssistantReplyText, t as abandonReplacedPendingAuth, u as isNoReplyMarker } from "./pending-auth-_1Ry-_Bw.js";
21
- import { C as toolInvocationDestination, S as surfaceFromRun, _ as uploadFilesToConversation, b as assertRunConsistency, h as postSlackMessage, i as formatProviderLabel, m as getSlackMessagePermalink, o as resolveBaseUrl, p as addReactionToMessage, r as deliverOAuthAuthorization, s as startOAuthFlow, v as RetryableDeliveryError, x as isAgentRunFeatureDisabled, y as actorFromRun } from "./agent-runner-D0ImXS7U.js";
22
- import { r as EVENT_AUTHOR_ID } from "./prompt-B8rCQI7K.js";
23
- import { a as createSandboxSession, c as postinstall, d as getSandboxResources, i as bindSandboxFileSystem, l as setSnapshotSpanAttributes, n as isMissingError, o as stopSession, r as resolve$1, s as dependencies } from "./resolve-D5SqDbCA.js";
14
+ import { a as resolveAuthTokenPlaceholder, c as fetchWithBoundedOAuthErrorBodies, d as CredentialUnavailableError, i as resolvePluginHeaderEnvRefs, l as getOAuthProviderErrorAttributes, o as resolvePluginCommandEnv, s as OAuthProviderError } from "./registry-Ctgdjt-W.js";
15
+ import { n as pluginCatalogRuntime } from "./catalog-runtime-CVZ9TEuK.js";
16
+ import { a as parseSkillInvocation, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-c2bMDVRS.js";
17
+ import { C as isToolResultError, D as stripRuntimeTurnContext, E as retainRuntimeTurnContext, F as loadTurnRoute, G as recordToolExecutionStarted, H as recordGuardianActionReviewed, I as openConversationProjection, K as recordTurnRoute, L as recordAgentsInstructionsUpdated, M as loadConnectedMcpProviders, N as loadConversationProjection, O as trimTrailingAssistantMessages, R as recordAttachmentsDelivered, S as isContinuablePiBoundary, T as normalizeToolNameFromResult, U as recordMcpProviderConnected, V as recordAuthorizationRequested, _ as getTerminalAssistantMessages, d as TurnSliceLimitExceededError, dt as getAgentContextCompactionTriggerTokens, f as assertTurnToolCallLimit, ft as getAgentContextInputLimitTokens, g as getPiMessageRole, h as extractAssistantText, i as abandonTurnRecord, k as TURN_CONTEXT_TAG, m as isTurnExecutionLimitExceededError, mt as piMessageFromHistoryItem, n as loadTurnCheckpoint, pt as historyItemFromPiMessage, r as saveTurnCheckpoint, t as continuableMessages, ut as estimateTextTokens, v as getUserMessageInstructionText, w as isToolResultMessage, x as isAssistantMessage, y as hasRuntimeTurnContext } from "./checkpoint-Ceck_fLO.js";
18
+ import { n as generateShortTitle, r as resolveTaskTitle, t as fallbackShortTitle } from "./short-title-xEkZwkcV.js";
19
+ import { A as SANDBOX_DATA_ROOT, D as fenceLock, E as acquireActiveLock, F as sandboxSkillFile, I as sandboxSkillPathResolution, M as SANDBOX_SKILLS_ROOT, N as SANDBOX_WORKSPACE_ROOT, O as withLock, P as sandboxSkillDir, _ as workspaceSnapshotNotReadyUserMessage, a as isAbortError, b as buildNonInteractiveShellScript, c as isSandboxCommandStreamInterruptedError, d as isSnapshottingError, f as throwSandboxOperationError, g as isWorkspaceSnapshotNotReadyError, h as getWorkspaceSnapshotNotReadyError, i as ensureDockerDaemon, j as SANDBOX_REPOS_ROOT, k as sleep$1, l as isSandboxMissingError, m as WorkspaceSnapshotNotReadyError, n as hash, o as isAlreadyExistsError, p as wrapSandboxSetupError, r as isStale, s as isSandboxApiTransientError, t as create, u as isSandboxUnavailableError, v as getVercelSandboxCredentials, w as getStateAdapter, x as runNonInteractiveCommand, y as buildCommandScript } from "./profile-6KWhg_-U.js";
20
+ import { $ as workspaceSnapshotFinishedEvent, B as createEventAutomation, C as SCHEDULED_AUTOMATION_SYSTEM_ACTOR, D as eventAutomationMatchesDestination, E as eventAutomationListToolResultSchema, F as effectiveTaskOutcomes, Gt as makeStructuredToolOutput, I as moveTaskOutcomes, Jt as ToolInputError, L as resolveTaskOutcomes, M as requireEventAutomationSlackContext, N as requireSupportedEventAutomationTrigger, O as eventAutomationToolResult, P as writableEventAutomation, Q as WORKSPACE_SNAPSHOT_READY_EVENT, R as taskOutcomeInputSchema, T as compactEventAutomation, Ut as injectContinuationToolName, V as deleteEventAutomation, W as getEventAutomation, Wt as juniorToolOutputSchema, X as getEventCatalog, Y as saveEventAutomation, Z as WORKSPACE_SNAPSHOT_FAILED_EVENT, at as normalizeCatalogEventIdentifier, c as parseSlackThreadId, ct as registeredEventTypeSchema, et as workspaceSnapshotWatch, f as createScheduledAutomation, g as listScheduledAutomationsForTeam, i as readLiveAttachment, it as hasPluginEventCatalogEntries, j as registeredEventAutomationTriggerSchema, k as eventAutomationToolResultSchema, lt as registeredResourceTypeSchema, nt as eventNamespaceSchema, ot as pluginSupportsEvent, pt as appendAndEnqueueInboundMessage, q as listEventAutomationsForTeam, qt as buildDeterministicTurnId, rt as hasEventCatalogEntries, s as storeAttachments, st as registeredEventMatchSchema, t as incrementStat, tt as eventGuidance, ut as requireSupportedEventMatch, v as readScheduledAutomation, y as saveScheduledAutomation } from "./stats-DP9zl3Jg.js";
21
+ import { a as NO_REPLY_MARKER, c as CooperativeTurnYieldError, i as sanitizeAssistantText, l as TurnInputCommitLostError, n as decideReply, o as isNoReplyMarker, p as isTurnInputCommitLostError, r as getAssistantReplyText, t as authorizationId } from "./oauth-authorization-BIijVS2N.js";
22
+ import { r as EVENT_AUTHOR_ID } from "./prompt-90miy8VV.js";
23
+ import { a as createSandboxSession, c as postinstall, d as getSandboxResources, i as bindSandboxFileSystem, l as setSnapshotSpanAttributes, n as isMissingError, o as stopSession, r as resolve$1, s as dependencies } from "./resolve-CB45ZukQ.js";
24
24
  import { t as getVercelConversationWorkQueue } from "./vercel-queue-DIkVcfK4.js";
25
- import { a as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-_8gxwDzt.js";
25
+ import { a as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-D4oAVrL9.js";
26
26
  import { n as privateTraceResultAttributes } from "./private-trace-result-BLvBjy_e.js";
27
27
  import { streamSimple } from "@earendil-works/pi-ai/compat";
28
28
  import { createGatewayProvider } from "@ai-sdk/gateway";
@@ -31,9 +31,9 @@ import path from "node:path";
31
31
  import { isDeepStrictEqual } from "node:util";
32
32
  import { AsyncLocalStorage } from "node:async_hooks";
33
33
  import { z } from "zod";
34
- import { createHash, createHmac, randomUUID, timingSafeEqual } from "node:crypto";
34
+ import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
35
35
  import { and, desc, eq, gt, gte, inArray, isNull, lt, ne, notExists, or, sql } from "drizzle-orm";
36
- import { EgressAuthRequired, EgressPolicyDenied, PluginToolInputError, actorSchema, destinationSchema, eventSchema, missingToolAnnotationKeys, pluginAuthorizationSchema, pluginCredentialHeaderTransformSchema, pluginCredentialResultSchema, pluginGrantSchema, pluginProviderAccountSchema, pluginStoredTokensSchema, sourceSchema, stableEventMatchKey, watchResultSchema } from "@sentry/junior-plugin-api";
36
+ import { EgressAuthRequired, EgressPolicyDenied, PluginToolInputError, actorSchema, destinationSchema, eventSchema, missingToolAnnotationKeys, ownedObjectAnnotationSchema, pluginAuthorizationSchema, pluginCredentialHeaderTransformSchema, pluginCredentialResultSchema, pluginGrantSchema, pluginProviderAccountSchema, pluginStoredTokensSchema, sourceSchema, stableEventMatchKey, watchResultSchema } from "@sentry/junior-plugin-api";
37
37
  import { alias } from "drizzle-orm/pg-core";
38
38
  import { Agent, estimateContextTokens } from "@earendil-works/pi-agent-core";
39
39
  import fs from "node:fs/promises";
@@ -1140,6 +1140,73 @@ var AuthorizationFlowDisabledError = class extends Error {
1140
1140
  }
1141
1141
  };
1142
1142
  //#endregion
1143
+ //#region src/chat/agent/types.ts
1144
+ /** Resume the agent turn after a transient or ambiguous delivery failure. */
1145
+ var RetryableDeliveryError = class extends Error {
1146
+ constructor(cause) {
1147
+ super("Assistant delivery was transient or ambiguous", { cause });
1148
+ this.name = "RetryableDeliveryError";
1149
+ }
1150
+ };
1151
+ /** Return whether one optional agent capability is disabled for this run. */
1152
+ function isAgentRunFeatureDisabled(disabledFeatures, feature) {
1153
+ return disabledFeatures?.includes(feature) ?? false;
1154
+ }
1155
+ /** Resolve the explicit actor, dispatch actor, or credential actor for this run. */
1156
+ function actorFromRun(run) {
1157
+ if (run.dispatch?.actor) return run.dispatch.actor;
1158
+ if (run.actor) return run.actor;
1159
+ if (run.credentialContext && !("type" in run.credentialContext.actor)) return run.credentialContext.actor;
1160
+ }
1161
+ /** Reject contradictory provider coordinates before the run touches state. */
1162
+ function assertRunConsistency(run) {
1163
+ const { destination, source } = run;
1164
+ switch (source.kind) {
1165
+ case "slack":
1166
+ if (destination.platform !== "slack") throw new TypeError("Run source and destination platforms do not match");
1167
+ if (source.teamId !== destination.teamId) throw new TypeError("Slack source and destination teams do not match");
1168
+ break;
1169
+ case "local":
1170
+ if (destination.platform !== "local") throw new TypeError("Run source and destination platforms do not match");
1171
+ if (source.conversationId !== destination.conversationId) throw new TypeError("Source and destination conversation IDs do not match");
1172
+ if (run.surface !== "internal" && destination.conversationId !== run.conversationId) throw new TypeError("Source, destination, and run conversation IDs do not match");
1173
+ break;
1174
+ case "web":
1175
+ if (run.surface !== "internal" && source.conversationId !== run.conversationId) throw new TypeError("Web source and run conversation IDs do not match");
1176
+ switch (destination.platform) {
1177
+ case "local":
1178
+ if (source.conversationId !== destination.conversationId) throw new TypeError("Source and destination conversation IDs do not match");
1179
+ if (run.surface !== "internal" && destination.conversationId !== run.conversationId) throw new TypeError("Source, destination, and run conversation IDs do not match");
1180
+ }
1181
+ break;
1182
+ case "event":
1183
+ case "scheduled_automation":
1184
+ case "event_automation":
1185
+ case "plugin_dispatch":
1186
+ case "agent_invocation": return;
1187
+ }
1188
+ const actor = run.dispatch?.actor ?? run.actor;
1189
+ if (!actor || actor.platform === "system") return;
1190
+ if (actor.platform !== source.kind) throw new TypeError(`Actor platform "${actor.platform}" does not match Source kind "${source.kind}"`);
1191
+ if (actor.platform === "slack" && source.kind === "slack" && actor.teamId !== source.teamId) throw new TypeError("Slack Actor team does not match Source team");
1192
+ }
1193
+ /** Route tool side effects to the tool channel when one overrides the destination. */
1194
+ function toolInvocationDestination(run) {
1195
+ if (run.destination.platform !== "slack" || !run.toolChannelId) return run.destination;
1196
+ return {
1197
+ platform: "slack",
1198
+ teamId: run.destination.teamId,
1199
+ channelId: run.toolChannelId
1200
+ };
1201
+ }
1202
+ /** Infer the run surface when the caller did not state one. */
1203
+ function surfaceFromRun(run) {
1204
+ if (run.surface) return run.surface;
1205
+ if (run.source.kind === "slack") return "slack";
1206
+ if (run.source.kind === "web") return "api";
1207
+ return "internal";
1208
+ }
1209
+ //#endregion
1143
1210
  //#region src/chat/tool-support/action-review-history.ts
1144
1211
  const MAX_VISIBLE_HISTORY_CHARS = 12e3;
1145
1212
  const priorRejectionSchema = z.object({
@@ -1483,7 +1550,7 @@ const TOOL_POLICY_RULES = [
1483
1550
  "- For user-provided URLs, use `webFetch`; for discovery, use `webSearch` then fetch/read promising sources; for current time/date context, use `systemTime`.",
1484
1551
  "- When searchEventTypes is exposed, use it only when the user asks what events are supported or the required resource type or event name is unclear. It discovers options but does not watch a resource or create an automation. When explaining how results can be used, distinguish temporary current-thread watches from durable event automations.",
1485
1552
  "- When a tool result includes a subscription, those events are already watched; do not call watchEvents for them. When a tool result includes a subscribable resource with suggestedEvents, use watchEvents only for those remaining events that serve the current intent. If suggestedEvents is empty or omitted, do not invent a watch. Do not create scheduled polling tasks for events a watch can deliver. Write a concise intent summary, and tell the user when the temporary watch expires. Stop only the requested watch by id unless the user explicitly asks to stop every watch in the thread.",
1486
- "- Successful automation changes include cards that the runtime attaches to your next visible reply. Explain what changed; do not repeat the card fields or draw a card in text.",
1553
+ "- Cards returned by tools attach to your next visible reply automatically. Explain the outcome; do not repeat the card fields or draw a card in text.",
1487
1554
  "- Use createEventAutomation only when the user explicitly asks for an event automation or durable whenever-this-happens-do-X automation. Ordinary watch, notify, and tell-me-when requests use watchEvents. When an event automation's resource and events are known, create it without redundant confirmation.",
1488
1555
  "- Event automations make the automation creator's connected credentials available by default when the requested work needs user-bound authorization. Do not ask for separate confirmation merely to use credentials needed for the requested work. On creation, omit credentialMode for the creator default and set system only when the creator explicitly requires it. For later changes, creator always means the automation's original createdBy actor, never the current requester. If the requester is not that creator, do not attempt to enable creator credential use or suggest that confirmation could authorize it.",
1489
1556
  "- Event automations list for the current destination, not one thread. Public automations can also be updated or deleted by automation id from another destination in the same workspace. When listing them, use createdBy to explain creator-only credential changes and warn when trigger.available is false; an unavailable automation remains stored but cannot receive events until its plugin event is enabled again.",
@@ -2524,6 +2591,570 @@ function formatSandboxCommandResult(params) {
2524
2591
  });
2525
2592
  }
2526
2593
  //#endregion
2594
+ //#region src/chat/slack/outbound.ts
2595
+ const MAX_SLACK_MESSAGE_TEXT_CHARS = 4e4;
2596
+ function requireSlackConversationId(channelId, action) {
2597
+ const normalized = normalizeSlackConversationId(channelId);
2598
+ if (!normalized) throw new Error(`${action} requires a valid channel ID`);
2599
+ return normalized;
2600
+ }
2601
+ function requireSlackThreadTimestamp(threadTs, action) {
2602
+ const normalized = threadTs.trim();
2603
+ if (!normalized) throw new Error(`${action} requires a thread timestamp`);
2604
+ return normalized;
2605
+ }
2606
+ function requireSlackMessageTimestamp(timestamp, action) {
2607
+ const normalized = parseSlackMessageTs(timestamp);
2608
+ if (!normalized) throw new Error(`${action} requires a target message timestamp`);
2609
+ return normalized;
2610
+ }
2611
+ function requireSlackMessageText(text, action) {
2612
+ if (text.trim().length === 0) throw new Error(`${action} requires non-empty text`);
2613
+ if (text.length > MAX_SLACK_MESSAGE_TEXT_CHARS) throw new Error(`${action} text exceeds Slack's 40000 character truncation limit`);
2614
+ return text;
2615
+ }
2616
+ /** Resolve a Slack message permalink without making read-only workflows fail on lookup errors. */
2617
+ async function getSlackMessagePermalink(args) {
2618
+ try {
2619
+ return (await withSlackRetries(() => getSlackClient().chat.getPermalink({
2620
+ channel: args.channelId,
2621
+ message_ts: args.messageTs
2622
+ }), 3, {
2623
+ action: "chat.getPermalink",
2624
+ idempotent: true,
2625
+ spanAttributes: {
2626
+ "app.slack.channel_id": args.channelId,
2627
+ "app.slack.message_ts": args.messageTs
2628
+ }
2629
+ })).permalink;
2630
+ } catch {
2631
+ return;
2632
+ }
2633
+ }
2634
+ /** Post Slack `mrkdwn` text to a conversation or thread via the shared outbound boundary. */
2635
+ async function postSlackMessage(input) {
2636
+ const channelId = requireSlackConversationId(input.channelId, "Slack message posting");
2637
+ const text = requireSlackMessageText(input.text, "Slack message posting");
2638
+ const threadTs = input.threadTs ? requireSlackThreadTimestamp(input.threadTs, "Slack thread message posting") : void 0;
2639
+ const response = await withSlackRetries(() => getSlackClient().chat.postMessage({
2640
+ channel: channelId,
2641
+ text,
2642
+ unfurl_links: false,
2643
+ unfurl_media: false,
2644
+ ...input.blocks?.length ? { blocks: input.blocks } : void 0,
2645
+ ...input.entities?.length ? { metadata: { entities: input.entities } } : void 0,
2646
+ ...threadTs ? { thread_ts: threadTs } : void 0
2647
+ }), 3, {
2648
+ action: "chat.postMessage",
2649
+ spanAttributes: {
2650
+ "app.slack.channel_id": channelId,
2651
+ ...threadTs ? { "app.slack.thread_ts": threadTs } : void 0
2652
+ }
2653
+ });
2654
+ const messageTs = parseSlackMessageTs(response.ts);
2655
+ if (!messageTs) throw new Error("Slack message posted without ts");
2656
+ return {
2657
+ ts: messageTs,
2658
+ ...input.includePermalink ? { permalink: await getSlackMessagePermalink({
2659
+ channelId,
2660
+ messageTs
2661
+ }) } : void 0
2662
+ };
2663
+ }
2664
+ /**
2665
+ * Post an ephemeral Slack message. Delivery is best-effort on Slack's side, but
2666
+ * request validation and Web API behavior are centralized here.
2667
+ */
2668
+ async function postSlackEphemeralMessage(input) {
2669
+ const channelId = requireSlackConversationId(input.channelId, "Slack ephemeral message posting");
2670
+ const userId = parseSlackUserId(input.userId);
2671
+ if (!userId) throw new Error("Slack ephemeral message posting requires a user ID");
2672
+ const text = requireSlackMessageText(input.text, "Slack ephemeral message posting");
2673
+ const threadTs = input.threadTs ? requireSlackThreadTimestamp(input.threadTs, "Slack ephemeral thread message posting") : void 0;
2674
+ return { messageTs: (await withSlackRetries(() => getSlackClient().chat.postEphemeral({
2675
+ channel: channelId,
2676
+ user: userId,
2677
+ text,
2678
+ ...input.blocks?.length ? { blocks: input.blocks } : void 0,
2679
+ ...threadTs ? { thread_ts: threadTs } : void 0
2680
+ }), 3, {
2681
+ action: "chat.postEphemeral",
2682
+ spanAttributes: {
2683
+ "app.slack.channel_id": channelId,
2684
+ "app.slack.user_id": userId,
2685
+ ...threadTs ? { "app.slack.thread_ts": threadTs } : void 0
2686
+ }
2687
+ })).message_ts };
2688
+ }
2689
+ /** Upload files into a Slack conversation or thread via the shared outbound file boundary. */
2690
+ async function uploadFilesToConversation(input) {
2691
+ const channelId = requireSlackConversationId(input.channelId, "Slack file upload");
2692
+ const threadTs = input.threadTs ? requireSlackThreadTimestamp(input.threadTs, "Slack file upload") : void 0;
2693
+ if (input.files.length === 0) throw new Error("Slack file upload requires at least one file");
2694
+ const fileUploads = input.files.map((file) => {
2695
+ const filename = file.filename.trim();
2696
+ if (!filename) throw new Error("Slack file upload requires every file to have a filename");
2697
+ return {
2698
+ file: file.data,
2699
+ filename
2700
+ };
2701
+ });
2702
+ return { files: ((await withSlackRetries(() => getSlackClient().filesUploadV2({
2703
+ channel_id: channelId,
2704
+ ...threadTs ? { thread_ts: threadTs } : void 0,
2705
+ file_uploads: fileUploads
2706
+ }), 3, {
2707
+ action: "filesUploadV2",
2708
+ spanAttributes: {
2709
+ "app.slack.channel_id": channelId,
2710
+ ...threadTs ? { "app.slack.thread_ts": threadTs } : void 0
2711
+ }
2712
+ })).files ?? []).flatMap((completion) => (completion.files ?? []).map((file) => file.id ? { id: file.id } : {})) };
2713
+ }
2714
+ /** Add a reaction to a Slack message, treating `already_reacted` as idempotent success. */
2715
+ async function addReactionToMessage(input) {
2716
+ const channelId = requireSlackConversationId(input.channelId, "Slack reaction");
2717
+ const timestamp = requireSlackMessageTimestamp(input.timestamp, "Slack reaction");
2718
+ const emoji = normalizeSlackEmojiName(input.emoji);
2719
+ if (!emoji) throw new Error("Slack reaction requires a valid emoji alias name");
2720
+ try {
2721
+ await withSlackRetries(() => getSlackClient().reactions.add({
2722
+ channel: channelId,
2723
+ timestamp,
2724
+ name: emoji
2725
+ }), 3, {
2726
+ action: "reactions.add",
2727
+ idempotent: true,
2728
+ spanAttributes: {
2729
+ "app.slack.channel_id": channelId,
2730
+ "app.slack.message_ts": timestamp,
2731
+ "app.slack.reaction": emoji
2732
+ }
2733
+ });
2734
+ } catch (error) {
2735
+ if (error instanceof SlackActionError && error.code === "already_reacted") return { ok: true };
2736
+ throw error;
2737
+ }
2738
+ return { ok: true };
2739
+ }
2740
+ /** Remove a reaction from a Slack message, treating `no_reaction` as idempotent success. */
2741
+ async function removeReactionFromMessage(input) {
2742
+ const channelId = requireSlackConversationId(input.channelId, "Slack reaction removal");
2743
+ const timestamp = requireSlackMessageTimestamp(input.timestamp, "Slack reaction removal");
2744
+ const emoji = normalizeSlackEmojiName(input.emoji);
2745
+ if (!emoji) throw new Error("Slack reaction removal requires a valid emoji alias name");
2746
+ try {
2747
+ await withSlackRetries(() => getSlackClient().reactions.remove({
2748
+ channel: channelId,
2749
+ timestamp,
2750
+ name: emoji
2751
+ }), 3, {
2752
+ action: "reactions.remove",
2753
+ idempotent: true,
2754
+ spanAttributes: {
2755
+ "app.slack.channel_id": channelId,
2756
+ "app.slack.message_ts": timestamp,
2757
+ "app.slack.reaction": emoji
2758
+ }
2759
+ });
2760
+ } catch (error) {
2761
+ if (error instanceof SlackActionError && error.code === "no_reaction") return { ok: true };
2762
+ throw error;
2763
+ }
2764
+ return { ok: true };
2765
+ }
2766
+ //#endregion
2767
+ //#region src/chat/slack/status-format.ts
2768
+ const SLACK_STATUS_MAX_LENGTH = 50;
2769
+ /** Truncate a status string to fit Slack's 50-char assistant status limit. */
2770
+ function truncateStatusText(text) {
2771
+ const trimmed = text.trim();
2772
+ if (!trimmed) return "";
2773
+ if (trimmed.length <= SLACK_STATUS_MAX_LENGTH) return trimmed;
2774
+ return `${trimmed.slice(0, 47).trimEnd()}...`;
2775
+ }
2776
+ //#endregion
2777
+ //#region src/chat/slack/mrkdwn.ts
2778
+ /** Escape dynamic text for Slack mrkdwn without changing intended formatting. */
2779
+ function escapeSlackMrkdwnText(text) {
2780
+ return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
2781
+ }
2782
+ /** Escape a URL for Slack explicit link syntax while preserving query semantics. */
2783
+ function escapeSlackLinkUrl(url) {
2784
+ return url.replaceAll("&", "&amp;").replaceAll("<", "%3C").replaceAll(">", "%3E");
2785
+ }
2786
+ /** Build a Slack explicit link from dynamic URL and label values. */
2787
+ function formatSlackLink(url, label) {
2788
+ return `<${escapeSlackLinkUrl(url)}|${escapeSlackMrkdwnText(label)}>`;
2789
+ }
2790
+ function readInlineCodeSpan(line, start) {
2791
+ if (line[start] !== "`") return;
2792
+ let n = 1;
2793
+ while (line[start + n] === "`") n++;
2794
+ const marker = "`".repeat(n);
2795
+ let search = start + n;
2796
+ while (search < line.length) {
2797
+ const close = line.indexOf(marker, search);
2798
+ if (close === -1) return;
2799
+ const after = close + n;
2800
+ if (line[after] !== "`") return {
2801
+ text: line.slice(start, after),
2802
+ end: after
2803
+ };
2804
+ search = after + 1;
2805
+ }
2806
+ }
2807
+ function readExistingSlackAngleToken(line, start) {
2808
+ if (line[start] !== "<") return;
2809
+ const close = line.indexOf(">", start + 1);
2810
+ if (close === -1) return;
2811
+ const body = line.slice(start + 1, close);
2812
+ if (/^(?:https?:\/\/|@|#|!)/.test(body)) return {
2813
+ text: line.slice(start, close + 1),
2814
+ end: close + 1
2815
+ };
2816
+ }
2817
+ function readMarkdownLink(line, start) {
2818
+ if (line[start] !== "[") return;
2819
+ const labelEnd = line.indexOf("](", start + 1);
2820
+ if (labelEnd === -1) return;
2821
+ const destStart = labelEnd + 2;
2822
+ if (!line.startsWith("http://", destStart) && !line.startsWith("https://", destStart)) return;
2823
+ const closeParens = line.indexOf(")", destStart);
2824
+ if (closeParens === -1) return;
2825
+ return {
2826
+ text: line.slice(start, closeParens + 1),
2827
+ end: closeParens + 1
2828
+ };
2829
+ }
2830
+ function hasUnmatchedClosingParen(text) {
2831
+ let balance = 0;
2832
+ for (const ch of text) if (ch === "(") balance++;
2833
+ else if (ch === ")") balance--;
2834
+ return balance < 0;
2835
+ }
2836
+ function readBareUrl(line, start) {
2837
+ let end = start;
2838
+ while (end < line.length) {
2839
+ const ch = line[end];
2840
+ if (/\s/.test(ch) || ch === "<" || ch === ">" || ch === "\"" || ch === "`" || ch === "|" || ch === "*") break;
2841
+ end++;
2842
+ }
2843
+ if (end === start) return;
2844
+ let raw = line.slice(start, end);
2845
+ let suffix = "";
2846
+ const peel = () => {
2847
+ suffix = raw.slice(-1) + suffix;
2848
+ raw = raw.slice(0, -1);
2849
+ };
2850
+ const shouldPeel = () => raw.endsWith("_") || /[.,!?;:]$/.test(raw) || raw.endsWith(")") && hasUnmatchedClosingParen(raw);
2851
+ while (raw.length > 0 && shouldPeel()) peel();
2852
+ if (!/^https?:\/\/.+/.test(raw)) return;
2853
+ return {
2854
+ url: raw,
2855
+ suffix,
2856
+ end
2857
+ };
2858
+ }
2859
+ /** Wrap bare http(s) URLs on a single line as Slack explicit `<url>` links. */
2860
+ function wrapBareUrlsOnLine(line) {
2861
+ let result = "";
2862
+ let i = 0;
2863
+ while (i < line.length) {
2864
+ const codeSpan = readInlineCodeSpan(line, i);
2865
+ if (codeSpan) {
2866
+ result += codeSpan.text;
2867
+ i = codeSpan.end;
2868
+ continue;
2869
+ }
2870
+ const angleToken = readExistingSlackAngleToken(line, i);
2871
+ if (angleToken) {
2872
+ result += angleToken.text;
2873
+ i = angleToken.end;
2874
+ continue;
2875
+ }
2876
+ const mdLink = readMarkdownLink(line, i);
2877
+ if (mdLink) {
2878
+ result += mdLink.text;
2879
+ i = mdLink.end;
2880
+ continue;
2881
+ }
2882
+ if (line.startsWith("https://", i) || line.startsWith("http://", i)) {
2883
+ const parsed = readBareUrl(line, i);
2884
+ if (parsed) {
2885
+ result += `<${parsed.url}>${parsed.suffix}`;
2886
+ i = parsed.end;
2887
+ continue;
2888
+ }
2889
+ }
2890
+ result += line[i];
2891
+ i++;
2892
+ }
2893
+ return result;
2894
+ }
2895
+ /**
2896
+ * Pre-wrap bare http(s) URLs outside fenced code blocks as Slack explicit
2897
+ * links, preventing Slack's auto-linker from consuming adjacent formatting
2898
+ * markers into the URL.
2899
+ *
2900
+ * Uses the same fence-toggle rule as `ensureBlockSpacing` so both passes
2901
+ * agree on which lines are code.
2902
+ */
2903
+ function wrapBareUrls(text) {
2904
+ const lines = text.split("\n");
2905
+ const out = [];
2906
+ let inCodeBlock = false;
2907
+ for (const line of lines) {
2908
+ if (line.trimStart().startsWith("```")) {
2909
+ inCodeBlock = !inCodeBlock;
2910
+ out.push(line);
2911
+ continue;
2912
+ }
2913
+ out.push(inCodeBlock ? line : wrapBareUrlsOnLine(line));
2914
+ }
2915
+ return out.join("\n");
2916
+ }
2917
+ /** Insert blank lines between content blocks so Slack renders them with visual separation. */
2918
+ function ensureBlockSpacing(text) {
2919
+ const codeBlockPattern = /^```/;
2920
+ const listItemPattern = /^[-*•]\s|^\d+\.\s/;
2921
+ const lines = text.split("\n");
2922
+ const result = [];
2923
+ let inCodeBlock = false;
2924
+ for (let i = 0; i < lines.length; i++) {
2925
+ const line = lines[i];
2926
+ if (codeBlockPattern.test(line.trimStart())) {
2927
+ if (!inCodeBlock) {
2928
+ const prev = result.length > 0 ? result[result.length - 1] : void 0;
2929
+ if (prev !== void 0 && prev.trim() !== "") result.push("");
2930
+ }
2931
+ inCodeBlock = !inCodeBlock;
2932
+ result.push(line);
2933
+ continue;
2934
+ }
2935
+ if (inCodeBlock) {
2936
+ result.push(line);
2937
+ continue;
2938
+ }
2939
+ const prev = result.length > 0 ? result[result.length - 1] : void 0;
2940
+ if (prev !== void 0 && prev.trim() !== "" && line.trim() !== "" && !(listItemPattern.test(prev.trimStart()) && listItemPattern.test(line.trimStart()))) result.push("");
2941
+ result.push(line);
2942
+ }
2943
+ return result.join("\n");
2944
+ }
2945
+ /**
2946
+ * Normalize model-authored Slack markdown for delivery via `markdown_text`
2947
+ * or `{ type: "markdown" }` blocks.
2948
+ *
2949
+ * Plugins may first rewrite ordinary Markdown. Core then applies Slack-only
2950
+ * delivery shaping: bare URL wrapping and block spacing. Slack reply delivery
2951
+ * owns chunking and continuation markers separately.
2952
+ */
2953
+ function normalizeSlackReplyMarkdown(text) {
2954
+ let normalized = applyPluginFormatMarkdown(text).replace(/\r\n?/g, "\n").replace(/[ \t]+$/gm, "");
2955
+ normalized = wrapBareUrls(normalized);
2956
+ normalized = ensureBlockSpacing(normalized);
2957
+ return normalized.replace(/\n{3,}/g, "\n\n").trim();
2958
+ }
2959
+ /** Normalize assistant status text before handing it to Slack. */
2960
+ function normalizeSlackStatusText(text) {
2961
+ const trimmed = text.trim();
2962
+ if (!trimmed) return "";
2963
+ return truncateStatusText(trimmed.replace(/(?:\.\s*)+$/, "").trim());
2964
+ }
2965
+ //#endregion
2966
+ //#region src/chat/slack/oauth-authorization-message.ts
2967
+ /** Present private OAuth authorization with a clear action and text fallback. */
2968
+ function buildSlackOAuthAuthorizationMessage(args) {
2969
+ return {
2970
+ text: `${formatSlackLink(args.authorizationUrl, args.label)}. ${args.completionText}`,
2971
+ blocks: [{
2972
+ type: "section",
2973
+ text: {
2974
+ type: "mrkdwn",
2975
+ text: `*${escapeSlackMrkdwnText(args.label)}*\n${escapeSlackMrkdwnText(args.completionText)}`
2976
+ },
2977
+ accessory: {
2978
+ type: "button",
2979
+ text: {
2980
+ type: "plain_text",
2981
+ text: "Connect"
2982
+ },
2983
+ style: "primary",
2984
+ url: args.authorizationUrl,
2985
+ action_id: "oauth_connect"
2986
+ }
2987
+ }]
2988
+ };
2989
+ }
2990
+ //#endregion
2991
+ //#region src/chat/oauth-flow.ts
2992
+ const OAUTH_STATE_TTL_MS = 6e5;
2993
+ function optionalString(value) {
2994
+ return typeof value === "string" ? value : void 0;
2995
+ }
2996
+ /** Parse OAuth callback state that was persisted before a provider redirect. */
2997
+ function parseOAuthStatePayload(value) {
2998
+ if (!isRecord$2(value)) return;
2999
+ if (typeof value.userId !== "string" || typeof value.provider !== "string") return;
3000
+ const actor = value.actor === void 0 ? void 0 : actorSchema.safeParse(value.actor);
3001
+ if (value.actor !== void 0 && (!actor || !actor.success)) return;
3002
+ const destination = parseDestination(value.destination);
3003
+ if (value.destination !== void 0 && !destination) return;
3004
+ const source = value.source === void 0 ? void 0 : sourceSchema.safeParse(value.source);
3005
+ if (value.source !== void 0 && (!source || !source.success)) return;
3006
+ return {
3007
+ userId: value.userId,
3008
+ provider: value.provider,
3009
+ ...actor?.success ? { actor: actor.data } : void 0,
3010
+ ...optionalString(value.channelId) ? { channelId: optionalString(value.channelId) } : void 0,
3011
+ ...destination ? { destination } : void 0,
3012
+ ...source?.success ? { source: source.data } : void 0,
3013
+ ...optionalString(value.threadTs) ? { threadTs: optionalString(value.threadTs) } : void 0,
3014
+ ...optionalString(value.resumeConversationId) ? { resumeConversationId: optionalString(value.resumeConversationId) } : void 0,
3015
+ ...optionalString(value.resumeSessionId) ? { resumeSessionId: optionalString(value.resumeSessionId) } : void 0,
3016
+ ...optionalString(value.scope) ? { scope: optionalString(value.scope) } : void 0
3017
+ };
3018
+ }
3019
+ /** Return the manifest-owned display label for a provider. */
3020
+ function formatProviderLabel(provider) {
3021
+ const displayName = pluginCatalogRuntime.getDisplayName(provider);
3022
+ if (!displayName) throw new Error(`Unknown plugin provider display name: "${provider}"`);
3023
+ return displayName;
3024
+ }
3025
+ /** Resolve the public base URL from environment variables (JUNIOR_BASE_URL or Vercel). */
3026
+ function resolveBaseUrl() {
3027
+ const explicit = process.env.JUNIOR_BASE_URL?.trim();
3028
+ if (explicit) return explicit;
3029
+ const vercelProd = process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim();
3030
+ if (vercelProd) return `https://${vercelProd}`;
3031
+ const vercelUrl = process.env.VERCEL_URL?.trim();
3032
+ if (vercelUrl) return `https://${vercelUrl}`;
3033
+ }
3034
+ /**
3035
+ * Authorization links must only be visible to the requesting user.
3036
+ * Try in-context private delivery first, then fall back to a DM.
3037
+ */
3038
+ async function deliverPrivateMessage(input) {
3039
+ let client;
3040
+ try {
3041
+ client = getSlackClient();
3042
+ } catch {
3043
+ logWarn("oauth.private_delivery.skipped", { "app.reason": "missing_bot_token" });
3044
+ return false;
3045
+ }
3046
+ if (input.channelId) try {
3047
+ if (isDmChannel$1(input.channelId)) await postSlackMessage({
3048
+ channelId: input.channelId,
3049
+ text: input.text,
3050
+ blocks: input.blocks,
3051
+ threadTs: input.threadTs
3052
+ });
3053
+ else await postSlackEphemeralMessage({
3054
+ channelId: input.channelId,
3055
+ userId: input.userId,
3056
+ text: input.text,
3057
+ blocks: input.blocks,
3058
+ threadTs: input.threadTs
3059
+ });
3060
+ return "in_context";
3061
+ } catch (error) {
3062
+ logWarn("oauth.private_delivery.failed", {
3063
+ "app.slack.error": error instanceof Error ? error.message : String(error),
3064
+ "app.slack.channel": input.channelId
3065
+ });
3066
+ }
3067
+ try {
3068
+ const dmChannelId = (await withSlackRetries(() => client.conversations.open({ users: input.userId }), 3, {
3069
+ action: "conversations.open",
3070
+ spanAttributes: { "app.slack.user_id": input.userId }
3071
+ })).channel?.id;
3072
+ if (!dmChannelId) {
3073
+ logWarn("oauth.dm.fallback.failed", { "app.reason": "no_dm_channel_id" });
3074
+ return false;
3075
+ }
3076
+ await postSlackMessage({
3077
+ channelId: dmChannelId,
3078
+ text: input.text,
3079
+ blocks: input.blocks
3080
+ });
3081
+ return "fallback_dm";
3082
+ } catch (error) {
3083
+ logWarn("oauth.dm.fallback.failed", { "app.slack.error": error instanceof Error ? error.message : String(error) });
3084
+ return false;
3085
+ }
3086
+ }
3087
+ /** Present one OAuth request through the active surface's private delivery. */
3088
+ async function deliverOAuthAuthorization(request, input) {
3089
+ if (input.authorization) {
3090
+ await input.authorization.deliver(request);
3091
+ return "in_context";
3092
+ }
3093
+ return await deliverPrivateMessage({
3094
+ channelId: input.channelId,
3095
+ threadTs: input.threadTs,
3096
+ userId: input.userId,
3097
+ ...buildSlackOAuthAuthorizationMessage(request)
3098
+ });
3099
+ }
3100
+ /** Initiate an OAuth authorization code flow for a provider and deliver the auth link to the user. */
3101
+ async function startOAuthFlow(provider, input) {
3102
+ const providerConfig = pluginCatalogRuntime.getOAuthConfig(provider);
3103
+ if (!providerConfig) return {
3104
+ ok: false,
3105
+ error: `Provider "${provider}" does not support OAuth authorization`
3106
+ };
3107
+ const clientId = process.env[providerConfig.clientIdEnv]?.trim();
3108
+ if (!clientId) return {
3109
+ ok: false,
3110
+ error: `Missing ${providerConfig.clientIdEnv} environment variable`
3111
+ };
3112
+ const baseUrl = resolveBaseUrl();
3113
+ if (!baseUrl) return {
3114
+ ok: false,
3115
+ error: "Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)"
3116
+ };
3117
+ const state = input.authorization ? await input.authorization.createState() : randomBytes(32).toString("hex");
3118
+ const requestedScope = input.scope ?? providerConfig.scope;
3119
+ await getStateAdapter().set(`oauth-state:${state}`, {
3120
+ userId: input.actorId,
3121
+ provider,
3122
+ ...input.actor ? { actor: input.actor } : void 0,
3123
+ ...input.channelId ? { channelId: input.channelId } : void 0,
3124
+ ...input.destination ? { destination: input.destination } : void 0,
3125
+ ...input.source ? { source: input.source } : void 0,
3126
+ ...input.threadTs ? { threadTs: input.threadTs } : void 0,
3127
+ ...input.resumeConversationId ? { resumeConversationId: input.resumeConversationId } : void 0,
3128
+ ...input.resumeSessionId ? { resumeSessionId: input.resumeSessionId } : void 0,
3129
+ ...requestedScope ? { scope: requestedScope } : void 0
3130
+ }, OAUTH_STATE_TTL_MS);
3131
+ const authorizeParams = new URLSearchParams({
3132
+ client_id: clientId,
3133
+ state,
3134
+ redirect_uri: `${baseUrl}${providerConfig.callbackPath}`,
3135
+ response_type: "code"
3136
+ });
3137
+ if (requestedScope) authorizeParams.set("scope", requestedScope);
3138
+ for (const [key, value] of Object.entries(providerConfig.authorizeParams ?? {})) authorizeParams.set(key, value);
3139
+ logInfo("jr_rpc.oauth.started", {
3140
+ "app.credential.provider": provider,
3141
+ ...input.activeSkillName ? { "app.skill.name": input.activeSkillName } : void 0
3142
+ });
3143
+ return {
3144
+ ok: true,
3145
+ delivery: await deliverOAuthAuthorization({
3146
+ authorizationUrl: `${providerConfig.authorizeEndpoint}?${authorizeParams.toString()}`,
3147
+ label: `Connect to ${formatProviderLabel(provider)}`,
3148
+ completionText: input.resumeSessionId ? "Once you've authorized, Junior will continue automatically." : "Once you've authorized, you'll see a confirmation in Slack."
3149
+ }, {
3150
+ authorization: input.authorization,
3151
+ channelId: input.channelId,
3152
+ threadTs: input.threadTs,
3153
+ userId: input.actorId
3154
+ })
3155
+ };
3156
+ }
3157
+ //#endregion
2527
3158
  //#region src/chat/sandbox/egress/schemas.ts
2528
3159
  const finiteNumberSchema = z.number().refine(Number.isFinite);
2529
3160
  const httpStatusSchema = z.number().int().min(100).max(599);
@@ -6824,13 +7455,16 @@ var McpToolManager = class {
6824
7455
  providerContent,
6825
7456
  ...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : void 0
6826
7457
  };
6827
- await this.options.onToolSuccess?.({
7458
+ const cards = await this.options.onToolSuccess?.({
6828
7459
  arguments: resolvedArgs,
6829
7460
  provider: plugin.manifest.name,
6830
7461
  ...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : void 0,
6831
7462
  toolName: tool.name
6832
7463
  });
6833
- return successResult;
7464
+ return cards?.length ? {
7465
+ ...successResult,
7466
+ cards
7467
+ } : successResult;
6834
7468
  } catch (error) {
6835
7469
  if (error instanceof McpAuthorizationRequiredError && await this.handleAuthorizationRequired(plugin.manifest.name, error)) {
6836
7470
  const parkedContent = [{
@@ -7148,6 +7782,7 @@ function createCallMcpToolTool(mcpToolManager) {
7148
7782
  }
7149
7783
  };
7150
7784
  },
7785
+ outputSchema: z.object({ objectCards: z.array(ownedObjectAnnotationSchema) }),
7151
7786
  execute: async (input, options) => {
7152
7787
  const { tool_name } = input;
7153
7788
  const provider = parseMcpProviderFromToolName(tool_name);
@@ -7167,10 +7802,14 @@ function createCallMcpToolTool(mcpToolManager) {
7167
7802
  });
7168
7803
  throw new McpToolError(missingToolMessage(tool_name, provider));
7169
7804
  }
7170
- return { content: (await mcpTool.execute(resolveMcpArguments(input), {
7805
+ const result = await mcpTool.execute(resolveMcpArguments(input), {
7171
7806
  conversationPrivacy: options?.conversationPrivacy ?? "private",
7172
7807
  ...options?.toolCallId ? { toolCallId: options.toolCallId } : void 0
7173
- })).content };
7808
+ });
7809
+ return {
7810
+ content: result.content,
7811
+ details: { objectCards: result.cards ?? [] }
7812
+ };
7174
7813
  }
7175
7814
  });
7176
7815
  }
@@ -7928,7 +8567,7 @@ function createEventAutomationTool(context, catalog) {
7928
8567
  const existing = await getEventAutomation(db, id);
7929
8568
  if (existing) {
7930
8569
  if (!eventAutomationMatchesDestination(existing, destination) || existing.createdBy.slackUserId !== actor.userId) throw new ToolInputError("Event automation operation identity is invalid.");
7931
- if (existing.status !== "deleted") return eventAutomationToolResult(existing, catalog, actor.userId);
8570
+ if (existing.status !== "deleted") return eventAutomationToolResult(context.conversationId, existing, catalog, actor.userId);
7932
8571
  }
7933
8572
  const title = await resolveTaskTitle({
7934
8573
  completeText,
@@ -7958,7 +8597,7 @@ function createEventAutomationTool(context, catalog) {
7958
8597
  ...match ? { match } : void 0
7959
8598
  }
7960
8599
  };
7961
- return eventAutomationToolResult(await createEventAutomation(db, task), catalog, actor.userId);
8600
+ return eventAutomationToolResult(context.conversationId, await createEventAutomation(db, task), catalog, actor.userId);
7962
8601
  }
7963
8602
  });
7964
8603
  }
@@ -7977,13 +8616,24 @@ function createDeleteEventAutomationTool(context, catalog) {
7977
8616
  executionMode: "sequential",
7978
8617
  description: "Delete an event automation.",
7979
8618
  inputSchema: z.object({ automationId: z.string().min(1) }).strict(),
7980
- outputSchema: eventAutomationToolResultSchema.omit({ cards: true }),
8619
+ outputSchema: eventAutomationToolResultSchema.omit({ objectCards: true }).extend({ removedCards: z.array(messageCardRefSchema) }),
7981
8620
  async execute({ automationId }) {
7982
8621
  const { actor } = requireEventAutomationSlackContext(context);
7983
8622
  const current = await writableEventAutomation(context, automationId);
7984
8623
  const deleted = await deleteEventAutomation(getDb(), current.id);
7985
8624
  if (!deleted) throw new ToolInputError("Event automation was not found.");
7986
- return { automation: compactEventAutomation(deleted, catalog, actor.userId) };
8625
+ await createPluginAnnotations({
8626
+ conversationId: context.conversationId,
8627
+ db: getDb(),
8628
+ plugin: "junior"
8629
+ }).remove("object", automationId);
8630
+ return {
8631
+ automation: compactEventAutomation(deleted, catalog, actor.userId),
8632
+ removedCards: [{
8633
+ plugin: "junior",
8634
+ key: automationId
8635
+ }]
8636
+ };
7987
8637
  }
7988
8638
  });
7989
8639
  }
@@ -8088,7 +8738,7 @@ function createUpdateEventAutomationTool(context, catalog) {
8088
8738
  }
8089
8739
  const saved = await saveEventAutomation(getDb(), next);
8090
8740
  if (!saved) throw new ToolInputError("Event automation was not found.");
8091
- return eventAutomationToolResult(saved, catalog, actor.userId);
8741
+ return eventAutomationToolResult(context.conversationId, saved, catalog, actor.userId);
8092
8742
  }
8093
8743
  });
8094
8744
  }
@@ -8674,7 +9324,7 @@ const compactTaskResultSchema = z.object({
8674
9324
  }).strict();
8675
9325
  const scheduleAutomationToolResultSchema = juniorToolOutputSchema.extend({
8676
9326
  automation: compactTaskResultSchema,
8677
- cards: z.array(automationCardSchema).optional()
9327
+ objectCards: z.array(ownedObjectAnnotationSchema).optional()
8678
9328
  }).strict();
8679
9329
  const scheduleListToolResultSchema = juniorToolOutputSchema.extend({
8680
9330
  automations: z.array(compactTaskResultSchema),
@@ -8792,23 +9442,27 @@ function compactTask(task, requesterSlackUserId) {
8792
9442
  });
8793
9443
  }
8794
9444
  /** Build the structured result shared by single-task scheduler tools. */
8795
- function scheduleAutomationToolResult(task, requesterSlackUserId) {
9445
+ async function scheduleAutomationToolResult(conversationId, task, requesterSlackUserId) {
8796
9446
  const automation = compactTask(task, requesterSlackUserId);
9447
+ const title = automation.title ?? fallbackShortTitle(automation.instruction, "Scheduled automation");
9448
+ const annotation = {
9449
+ kind: "object",
9450
+ objectType: "automation",
9451
+ label: title,
9452
+ key: task.id,
9453
+ title,
9454
+ url: automation.dashboardUrl,
9455
+ description: automation.instruction,
9456
+ trigger: task.schedule.kind === "one_off" && task.nextRunAtMs !== void 0 ? `${new Intl.DateTimeFormat("en-US", {
9457
+ dateStyle: "medium",
9458
+ timeStyle: "short",
9459
+ timeZone: task.schedule.timezone
9460
+ }).format(task.nextRunAtMs)} · ${task.schedule.timezone}` : automation.schedule,
9461
+ warning: automation.status === "blocked" ? automation.statusReason ?? "This automation is blocked." : automation.status === "completed" ? "This automation has completed." : void 0
9462
+ };
8797
9463
  return {
8798
9464
  automation,
8799
- cards: [{
8800
- kind: "automation",
8801
- id: task.id,
8802
- title: automation.title ?? fallbackShortTitle(automation.instruction, "Scheduled automation"),
8803
- url: automation.dashboardUrl,
8804
- instruction: automation.instruction,
8805
- trigger: task.schedule.kind === "one_off" && task.nextRunAtMs !== void 0 ? `${new Intl.DateTimeFormat("en-US", {
8806
- dateStyle: "medium",
8807
- timeStyle: "short",
8808
- timeZone: task.schedule.timezone
8809
- }).format(task.nextRunAtMs)} · ${task.schedule.timezone}` : automation.schedule,
8810
- warning: automation.status === "blocked" ? automation.statusReason ?? "This automation is blocked." : automation.status === "completed" ? "This automation has completed." : null
8811
- }]
9465
+ objectCards: await saveObjectAnnotations(conversationId, "junior", [annotation])
8812
9466
  };
8813
9467
  }
8814
9468
  /** Build the structured result for listing scheduler tools. */
@@ -8879,7 +9533,7 @@ function createSlackScheduleCreateAutomationTool(context) {
8879
9533
  const existing = await readScheduledAutomation(db, id);
8880
9534
  if (existing) {
8881
9535
  if (!sameDestination(existing, destination) || existing.createdBy.slackUserId !== actor.slackUserId) throwToolInputError("Scheduled automation operation identity is invalid.");
8882
- return scheduleAutomationToolResult(existing, actor.slackUserId);
9536
+ return scheduleAutomationToolResult(context.conversationId, existing, actor.slackUserId);
8883
9537
  }
8884
9538
  const identity = (await context.users.resolveActor())?.identity;
8885
9539
  if (!identity || identity.provider !== "slack" || identity.providerTenantId !== destination.teamId || identity.providerSubjectId !== actor.slackUserId) throwToolInputError("Scheduled automation creator identity is unavailable.");
@@ -8922,7 +9576,7 @@ function createSlackScheduleCreateAutomationTool(context) {
8922
9576
  const committed = await createScheduledAutomation(db, task);
8923
9577
  if (!sameDestination(committed, destination) || committed.createdBy.slackUserId !== actor.slackUserId) throwToolInputError("Scheduled automation operation identity is invalid.");
8924
9578
  logInfo("scheduled_automation.create.completed", scheduledAutomationAttributes(committed));
8925
- return scheduleAutomationToolResult(committed, actor.slackUserId);
9579
+ return scheduleAutomationToolResult(context.conversationId, committed, actor.slackUserId);
8926
9580
  }
8927
9581
  });
8928
9582
  }
@@ -8941,7 +9595,7 @@ function createSlackScheduleDeleteAutomationTool(context) {
8941
9595
  description: "Delete one scheduled Junior task from the active Slack conversation. Use only task IDs returned for this conversation. Do not delete schedules from threads, other channels, or another user's DM.",
8942
9596
  executionMode: "sequential",
8943
9597
  inputSchema: z.object({ automationId: z.string().min(1).describe("ID of the task to delete. Must be from this active Slack conversation.") }),
8944
- outputSchema: scheduleAutomationToolResultSchema,
9598
+ outputSchema: scheduleAutomationToolResultSchema.extend({ removedCards: z.array(messageCardRefSchema) }),
8945
9599
  execute: async ({ automationId }) => {
8946
9600
  const next = {
8947
9601
  ...await getWritableTask({
@@ -8954,7 +9608,18 @@ function createSlackScheduleDeleteAutomationTool(context) {
8954
9608
  runNowAtMs: void 0
8955
9609
  };
8956
9610
  await saveScheduledAutomation(getDb(), next);
8957
- return { automation: compactTask(next, context.actor?.userId) };
9611
+ await createPluginAnnotations({
9612
+ conversationId: context.conversationId,
9613
+ db: getDb(),
9614
+ plugin: "junior"
9615
+ }).remove("object", automationId);
9616
+ return {
9617
+ automation: compactTask(next, context.actor?.userId),
9618
+ removedCards: [{
9619
+ plugin: "junior",
9620
+ key: automationId
9621
+ }]
9622
+ };
8958
9623
  }
8959
9624
  });
8960
9625
  }
@@ -9170,10 +9835,10 @@ function createSlackScheduleUpdateAutomationTool(context) {
9170
9835
  if (title) next.title = title;
9171
9836
  else delete next.title;
9172
9837
  }
9173
- if (!changingDestination && !instructionChanged && !compiled && status === void 0 && (input.credentialMode === void 0 || input.credentialMode === null || input.credentialMode === lookup.credentialMode) && input.outcomes === void 0 && moveDestination) return scheduleAutomationToolResult(lookup, actor.slackUserId);
9838
+ if (!changingDestination && !instructionChanged && !compiled && status === void 0 && (input.credentialMode === void 0 || input.credentialMode === null || input.credentialMode === lookup.credentialMode) && input.outcomes === void 0 && moveDestination) return scheduleAutomationToolResult(context.conversationId, lookup, actor.slackUserId);
9174
9839
  const committed = await saveScheduledAutomation(db, next);
9175
9840
  if (changingDestination) logInfo("scheduled_automation.move.completed", scheduledAutomationAttributes(committed));
9176
- return scheduleAutomationToolResult(committed, actor.slackUserId);
9841
+ return scheduleAutomationToolResult(context.conversationId, committed, actor.slackUserId);
9177
9842
  }
9178
9843
  });
9179
9844
  }
@@ -9182,6 +9847,7 @@ function createSlackScheduleUpdateAutomationTool(context) {
9182
9847
  function scheduledAutomationToolContext(context) {
9183
9848
  if (context.source.kind !== "slack" || context.destination.platform !== "slack" || context.actor?.platform !== "slack" || !context.resolveActorIdentity) return;
9184
9849
  return {
9850
+ conversationId: context.conversationId,
9185
9851
  actor: context.actor,
9186
9852
  source: context.source,
9187
9853
  users: { resolveActor: context.resolveActorIdentity },
@@ -10185,13 +10851,12 @@ function createSendFilesTool(context, state, materializeFile, attachments) {
10185
10851
  files: materializedFiles,
10186
10852
  storage: attachments.storage
10187
10853
  }) : [];
10188
- const uploads = materializedFiles.map((file) => ({
10189
- data: file.data,
10190
- filename: file.filename
10191
- }));
10192
10854
  await uploadFilesToConversation({
10193
10855
  channelId: activeChannelId,
10194
- files: uploads,
10856
+ files: materializedFiles.map((file) => ({
10857
+ data: file.data,
10858
+ filename: file.filename
10859
+ })),
10195
10860
  ...threadTs ? { threadTs } : void 0
10196
10861
  });
10197
10862
  const delivered = stored.map((attachment, index) => {
@@ -13767,10 +14432,51 @@ function buildSandboxInput(toolName, params) {
13767
14432
  return params;
13768
14433
  }
13769
14434
  //#endregion
13770
- //#region src/chat/oauth-authorization.ts
13771
- /** Build the stable event identity shared by authorization request and completion. */
13772
- function authorizationId(args) {
13773
- return `${args.sessionId}:${args.kind}:${args.provider}`;
14435
+ //#region src/chat/services/pending-auth.ts
14436
+ const AUTH_LINK_REUSE_WINDOW_MS = 6e5;
14437
+ const NON_REQUEST_SKIPPED_REASON_PREFIXES = ["directed_to_other_party:", "side_conversation:"];
14438
+ function isSkippedNonRequest(reason) {
14439
+ return NON_REQUEST_SKIPPED_REASON_PREFIXES.some((prefix) => reason?.startsWith(prefix));
14440
+ }
14441
+ /** Decide whether the same agent-run session can reuse its fresh auth link. */
14442
+ function canReusePendingAuthLink(args) {
14443
+ const { pendingAuth } = args;
14444
+ if (!pendingAuth) return false;
14445
+ return pendingAuth.kind === args.kind && pendingAuth.provider === args.provider && pendingAuth.actorId === args.actorId && pendingAuth.scope === args.scope && pendingAuth.sessionId === args.sessionId && pendingAuth.linkSentAtMs + AUTH_LINK_REUSE_WINDOW_MS > (args.nowMs ?? Date.now());
14446
+ }
14447
+ function getConversationPendingAuth(args) {
14448
+ const pendingAuth = args.conversation.processing.pendingAuth;
14449
+ if (!pendingAuth) return;
14450
+ if (pendingAuth.kind !== args.kind || pendingAuth.provider !== args.provider || pendingAuth.actorId !== args.actorId || pendingAuth.scope !== args.scope) return;
14451
+ return pendingAuth;
14452
+ }
14453
+ function clearPendingAuth(conversation, sessionId) {
14454
+ if (!conversation.processing.pendingAuth) return;
14455
+ if (sessionId && conversation.processing.pendingAuth.sessionId !== sessionId) return;
14456
+ conversation.processing.pendingAuth = void 0;
14457
+ }
14458
+ /** Mark the prior blocked turn abandoned after a new auth attempt replaces it. */
14459
+ async function abandonReplacedPendingAuth(args) {
14460
+ if (args.previousPendingAuth && args.previousPendingAuth.sessionId !== args.nextPendingAuth.sessionId && args.conversationId) await abandonTurnRecord({
14461
+ conversationId: args.conversationId,
14462
+ turnId: args.previousPendingAuth.sessionId,
14463
+ errorMessage: "Abandoned by a newer auth-blocked request in the same conversation."
14464
+ });
14465
+ }
14466
+ /**
14467
+ * Decide whether an auth callback still belongs to the latest real human
14468
+ * request, ignoring bot-authored and passive bystander rows while treating
14469
+ * failed human turns and opt-outs as newer freshness blockers.
14470
+ */
14471
+ function isPendingAuthLatestRequest(conversation, pendingAuth) {
14472
+ for (let index = conversation.messages.length - 1; index >= 0; index -= 1) {
14473
+ const message = conversation.messages[index];
14474
+ if (message?.role !== "user") continue;
14475
+ if (message.author?.isBot) continue;
14476
+ if (isSkippedNonRequest(message.meta?.skippedReason)) continue;
14477
+ return buildDeterministicTurnId(message.id) === pendingAuth.sessionId;
14478
+ }
14479
+ return false;
13774
14480
  }
13775
14481
  //#endregion
13776
14482
  //#region src/chat/services/plugin-auth-orchestration.ts
@@ -15216,12 +15922,11 @@ function createMcpAuthOrchestration(input) {
15216
15922
  const authSession = await getMcpAuthSession(authSessionId);
15217
15923
  if (!authSession?.authorizationUrl) throw new Error(`Missing MCP authorization URL for plugin "${provider}"`);
15218
15924
  await recordPendingAuth(nextPendingAuth);
15219
- const authorizationRequest = {
15925
+ if (!await deliverOAuthAuthorization({
15220
15926
  authorizationUrl: authSession.authorizationUrl,
15221
- label: `Click here to link your ${providerLabel} MCP access`,
15927
+ label: `Connect to ${providerLabel}`,
15222
15928
  completionText: "Once you've authorized, Junior will continue automatically."
15223
- };
15224
- if (!await deliverOAuthAuthorization(authorizationRequest, {
15929
+ }, {
15225
15930
  authorization: input.authorization,
15226
15931
  channelId: authSession.channelId,
15227
15932
  threadTs: authSession.threadTs,
@@ -16395,7 +17100,7 @@ async function wireAgentTools(args) {
16395
17100
  authProviderFactory: mcpAuth.authProviderFactory,
16396
17101
  onAuthorizationRequired: mcpAuth.onAuthorizationRequired,
16397
17102
  onToolSuccess: async (input) => {
16398
- await pluginHooks.afterMcpTool({
17103
+ return await pluginHooks.afterMcpTool({
16399
17104
  ...input,
16400
17105
  conversationId: args.run.conversationId
16401
17106
  });
@@ -17362,7 +18067,7 @@ function applyHandoff(args) {
17362
18067
  * assistant message through the delivery port; the result carries diagnostics,
17363
18068
  * artifacts, and transcript state for destination-owned completion.
17364
18069
  */
17365
- const AGENT_ABORT_SETTLE_GRACE_MS = 5e3;
18070
+ const AGENT_ABORT_SETTLE_GRACE_MS$1 = 5e3;
17366
18071
  async function readRunConversationVisibility(conversationId) {
17367
18072
  const conversationStore = getConversationStore();
17368
18073
  const conversation = await conversationStore.get({ conversationId });
@@ -18091,7 +18796,7 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
18091
18796
  "app.ai.turn_timeout_ms": turnTimeoutBudgetMs,
18092
18797
  "app.ai.turn_deadline_remaining_ms": Math.max(0, turnDeadlineAtMs - Date.now())
18093
18798
  });
18094
- if (!await waitForAbortSettlement(run, AGENT_ABORT_SETTLE_GRACE_MS)) logWarn("agent.turn.abort_settle.timed_out", { "app.ai.abort_settle_grace_ms": AGENT_ABORT_SETTLE_GRACE_MS });
18799
+ if (!await waitForAbortSettlement(run, AGENT_ABORT_SETTLE_GRACE_MS$1)) logWarn("agent.turn.abort_settle.timed_out", { "app.ai.abort_settle_grace_ms": AGENT_ABORT_SETTLE_GRACE_MS$1 });
18095
18800
  runResume.captureResumeSnapshot(runResume.getResumeSnapshot(currentAgentMessages()));
18096
18801
  }
18097
18802
  const pendingAuthPause = getPendingAuthPause();
@@ -18313,4 +19018,109 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
18313
19018
  }
18314
19019
  }
18315
19020
  //#endregion
18316
- export { AuthorizationFlowDisabledError as $, createAgentInvocationSchema as A, recordFinishedTurnForAutomatedLimit as B, renderBlockText as C, getNextRunAtMs as D, scheduledAutomationRunAttributes as E, ingestEvent as F, clearSandboxEgressSignals as G, normalizeSandboxEgressTracePropagationDomains as H, EVENT_WAIT_MS as I, parseSandboxEgressCredentialToken as J, consumeSandboxEgressAuthRequiredSignal as K, isEventMailboxMetadata as L, AgentInvocationLimitError as M, getMcpProviderErrorAttributes as N, agentBindingSchema as O, processWorkspaceSnapshotJob as P, isVisionImageMediaType as Q, renderTaskInput as R, renderAttachmentText as S, logScheduledAutomationRunSkipped as T, shouldPropagateSandboxEgressTrace as U, resolveSandboxEgressProviderForHost as V, SANDBOX_EGRESS_PROXY_PATH as W, createUserTokenStore as X, sandboxEgressSignalsResponseSchema as Y, buildSteeringPiMessage as Z, readPublicArtifact as _, deleteMcpAuthSession as a, readAttachmentVisionSummary as b, deleteMcpStoredOAuthCredentials as c, putMcpStoredOAuthCredentials as d, shouldEmitDevAgentTrace as et, PluginCredentialFailureError as f, publicArtifactHeaders as g, resolvePluginOAuthAccount as h, finalizeMcpAuthorization as i, AgentInvocationBusyError as j, agentInvocationSchema as k, getMcpAuthSession as l, latestProgressStatus as m, createContextCompactor as n, setConfigDefaults as nt, deleteMcpAuthSessionsForUserProvider as o, authorizationId as p, consumeSandboxEgressPermissionDeniedSignal as q, executeCredentialedEgressRequest as r, deleteMcpServerSessionId as s, executeAgentRun as t, getConfigDefaults as tt, getMcpStoredOAuthCredentials as u, unpublishedArtifactHeaders as v, listThreadReplies as w, recordSlackAttachment as x, lookupSlackUserProfile as y, maybePostAutomatedTurnLimitNotice as z };
19021
+ //#region src/chat/experimental.ts
19022
+ var experimental_exports = /* @__PURE__ */ __exportAll({
19023
+ EXPERIMENTAL_FEATURES: () => EXPERIMENTAL_FEATURES,
19024
+ getExperimentalFeatures: () => getExperimentalFeatures,
19025
+ isExperimentalFeatureEnabled: () => isExperimentalFeatureEnabled,
19026
+ setExperimentalFeatures: () => setExperimentalFeatures
19027
+ });
19028
+ /**
19029
+ * Known unstable product features that must be opted into explicitly.
19030
+ * Add new keys here as features graduate from private experiments; remove them
19031
+ * once they become stable defaults.
19032
+ */
19033
+ const EXPERIMENTAL_FEATURES = ["passive-routing", "subagents"];
19034
+ const EXPERIMENTAL_FEATURE_SET = new Set(EXPERIMENTAL_FEATURES);
19035
+ let configuredExperimental = {};
19036
+ function isExperimentalFeature(value) {
19037
+ return EXPERIMENTAL_FEATURE_SET.has(value);
19038
+ }
19039
+ /** Replace app-level experimental opt-ins and return the previous setting. */
19040
+ function setExperimentalFeatures(config) {
19041
+ const previous = { ...configuredExperimental };
19042
+ if (config === void 0) {
19043
+ configuredExperimental = {};
19044
+ return previous;
19045
+ }
19046
+ const next = {};
19047
+ for (const [rawName, enabled] of Object.entries(config)) {
19048
+ if (enabled === void 0) continue;
19049
+ if (typeof enabled !== "boolean") throw new Error(`experimental.${rawName} must be a boolean when provided`);
19050
+ if (!isExperimentalFeature(rawName)) throw new Error(`experimental.${rawName} is not a known experimental feature. Known features: ${EXPERIMENTAL_FEATURES.join(", ")}`);
19051
+ next[rawName] = enabled;
19052
+ }
19053
+ configuredExperimental = next;
19054
+ return previous;
19055
+ }
19056
+ /** Return a copy of the app-level experimental opt-ins configured by createApp(). */
19057
+ function getExperimentalFeatures() {
19058
+ return { ...configuredExperimental };
19059
+ }
19060
+ /** Return whether one experimental feature is enabled via createApp({ experimental }). */
19061
+ function isExperimentalFeatureEnabled(feature) {
19062
+ return configuredExperimental[feature] === true;
19063
+ }
19064
+ //#endregion
19065
+ //#region src/chat/runtime/agent-runner.ts
19066
+ const AGENT_ABORT_SETTLE_GRACE_MS = 5e3;
19067
+ /** Compose the agent executor with stable host dependencies. */
19068
+ function createAgentRunner(execute, options) {
19069
+ const attachmentStorage = options?.attachmentStorage;
19070
+ const streamFn = options?.streamFn;
19071
+ const tracePropagation = options?.tracePropagation;
19072
+ const bindSpawnAgent = options?.bindSpawnAgent;
19073
+ const canBindSpawn = Boolean(bindSpawnAgent) && isExperimentalFeatureEnabled("subagents");
19074
+ return { run: async (run) => {
19075
+ const spawnAgent = bindSpawnAgent && canBindSpawn && !isAgentRunFeatureDisabled(run.disabledFeatures, "subagents") ? bindSpawnAgent(run) : void 0;
19076
+ return await execute({
19077
+ ...run,
19078
+ environment: {
19079
+ ...run.environment,
19080
+ attachmentStorage: run.environment?.attachmentStorage ?? attachmentStorage,
19081
+ sandboxTracePropagation: run.environment?.sandboxTracePropagation ?? tracePropagation
19082
+ },
19083
+ ...spawnAgent ? { durability: {
19084
+ ...run.durability,
19085
+ spawnAgent
19086
+ } } : void 0
19087
+ }, streamFn);
19088
+ } };
19089
+ }
19090
+ async function waitForAgentSettlement(runPromise) {
19091
+ let timeoutId;
19092
+ const settleGrace = new Promise((resolve) => {
19093
+ timeoutId = setTimeout(resolve, AGENT_ABORT_SETTLE_GRACE_MS);
19094
+ timeoutId.unref?.();
19095
+ });
19096
+ await Promise.race([runPromise.then(() => void 0, () => void 0), settleGrace]);
19097
+ if (timeoutId) clearTimeout(timeoutId);
19098
+ }
19099
+ /** Abort a timed-out agent run and wait briefly for its work to settle. */
19100
+ async function runAgentWithTimeout(agentRunner, run, timeoutMs) {
19101
+ if (typeof timeoutMs !== "number") return await agentRunner.run(run);
19102
+ const timeoutError = /* @__PURE__ */ new Error(`executeAgentRun timed out after ${timeoutMs}ms`);
19103
+ const abortController = new AbortController();
19104
+ const signal = run.signal ? AbortSignal.any([run.signal, abortController.signal]) : abortController.signal;
19105
+ const runPromise = agentRunner.run({
19106
+ ...run,
19107
+ signal
19108
+ });
19109
+ let timeoutId;
19110
+ const timeoutPromise = new Promise((_, reject) => {
19111
+ timeoutId = setTimeout(() => {
19112
+ abortController.abort(timeoutError);
19113
+ reject(timeoutError);
19114
+ }, timeoutMs);
19115
+ });
19116
+ try {
19117
+ return await Promise.race([runPromise, timeoutPromise]);
19118
+ } catch (error) {
19119
+ if (abortController.signal.aborted) await waitForAgentSettlement(runPromise);
19120
+ throw error;
19121
+ } finally {
19122
+ if (timeoutId) clearTimeout(timeoutId);
19123
+ }
19124
+ }
19125
+ //#endregion
19126
+ export { clearSandboxEgressSignals as $, renderAttachmentText as A, AgentInvocationLimitError as B, resolvePluginOAuthAccount as C, shouldEmitDevAgentTrace as Ct, lookupSlackUserProfile as D, unpublishedArtifactHeaders as E, getNextRunAtMs as F, isEventMailboxMetadata as G, processWorkspaceSnapshotJob as H, agentBindingSchema as I, recordFinishedTurnForAutomatedLimit as J, renderTaskInput as K, agentInvocationSchema as L, listThreadReplies as M, logScheduledAutomationRunSkipped as N, readAttachmentVisionSummary as O, scheduledAutomationRunAttributes as P, SANDBOX_EGRESS_PROXY_PATH as Q, createAgentInvocationSchema as R, latestProgressStatus as S, AuthorizationFlowDisabledError as St, readPublicArtifact as T, setConfigDefaults as Tt, ingestEvent as U, getMcpProviderErrorAttributes as V, EVENT_WAIT_MS as W, normalizeSandboxEgressTracePropagationDomains as X, resolveSandboxEgressProviderForHost as Y, shouldPropagateSandboxEgressTrace as Z, putMcpStoredOAuthCredentials as _, buildSteeringPiMessage as _t, isExperimentalFeatureEnabled as a, parseOAuthStatePayload as at, getConversationPendingAuth as b, actorFromRun as bt, createContextCompactor as c, escapeSlackMrkdwnText as ct, deleteMcpAuthSession as d, normalizeSlackStatusText as dt, consumeSandboxEgressAuthRequiredSignal as et, deleteMcpAuthSessionsForUserProvider as f, truncateStatusText as ft, getMcpStoredOAuthCredentials as g, createUserTokenStore as gt, getMcpAuthSession as h, removeReactionFromMessage as ht, getExperimentalFeatures as i, formatProviderLabel as it, renderBlockText as j, recordSlackAttachment as k, executeCredentialedEgressRequest as l, formatSlackLink as lt, deleteMcpStoredOAuthCredentials as m, postSlackMessage as mt, runAgentWithTimeout as n, parseSandboxEgressCredentialToken as nt, setExperimentalFeatures as o, resolveBaseUrl as ot, deleteMcpServerSessionId as p, addReactionToMessage as pt, maybePostAutomatedTurnLimitNotice as q, experimental_exports as r, sandboxEgressSignalsResponseSchema as rt, executeAgentRun as s, startOAuthFlow as st, createAgentRunner as t, consumeSandboxEgressPermissionDeniedSignal as tt, finalizeMcpAuthorization as u, normalizeSlackReplyMarkdown as ut, PluginCredentialFailureError as v, isVisionImageMediaType as vt, publicArtifactHeaders as w, getConfigDefaults as wt, isPendingAuthLatestRequest as x, toolInvocationDestination as xt, clearPendingAuth as y, RetryableDeliveryError as yt, AgentInvocationBusyError as z };