@sentry/junior 0.240.0 → 0.242.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 (56) hide show
  1. package/dist/{admin-D6fRPRgC.js → admin-C54kvHVC.js} +1 -1
  2. package/dist/{agent-hooks-JFbgedMZ.js → agent-hooks-BVttjZvb.js} +1952 -1952
  3. package/dist/{agent-runner-0IjJxsL1.js → agent-runner-BTgnxJ7g.js} +336 -1125
  4. package/dist/api/schema.d.ts +9 -0
  5. package/dist/api/schema.js +2 -2
  6. package/dist/api.js +21 -21
  7. package/dist/app.js +104 -31
  8. package/dist/{automation-B5_c8hEb.js → automation-gznOMaQ2.js} +11 -2
  9. package/dist/{briefs-BGWTp-nz.js → briefs-CKUsv9F1.js} +2 -2
  10. package/dist/{catalog-runtime-DdigNl7s.js → catalog-runtime-C2YkfMZz.js} +1 -1
  11. package/dist/cli/chat.js +12 -12
  12. package/dist/cli/check.js +3 -3
  13. package/dist/cli/main.js +2 -2
  14. package/dist/cli/plugins.js +4 -4
  15. package/dist/cli/snapshot-create.js +3 -3
  16. package/dist/cli/upgrade.js +3 -3
  17. package/dist/{client-Cn73E7br.js → client-D51G-Jdg.js} +2 -2
  18. package/dist/{config-B0S86yw9.js → config-C6Uu3Re9.js} +2 -2
  19. package/dist/{conversation-privacy-BKlX7D_R.js → conversation-privacy-CzL47nXs.js} +1 -1
  20. package/dist/{credential-sync-DeNQm7st.js → credential-sync-DvDaE-Uj.js} +14 -14
  21. package/dist/{executor-BQ-A5nov.js → executor-CGUukwus.js} +4 -2
  22. package/dist/{images-C7OKd6uy.js → images-DSxZt5vk.js} +2 -2
  23. package/dist/instrumentation.js +2 -2
  24. package/dist/{job-queue-DgXLqoeB.js → job-queue-hLylM9cy.js} +1 -1
  25. package/dist/{logging-BSNF167N.js → logging-BdZ_mK3q.js} +1 -1
  26. package/dist/nitro.js +3 -3
  27. package/dist/{oauth-authorization-BM7g61od.js → oauth-authorization-BokpeHv-.js} +1 -1
  28. package/dist/{oauth-callback-server-Dw-BwI6x.js → oauth-callback-server-C3BOSmT_.js} +2 -2
  29. package/dist/{pending-auth-Chn_EA1_.js → pending-auth-DOzL4D24.js} +2 -2
  30. package/dist/{profile-3t9H_IwL.js → profile-2gPzzMib.js} +4 -4
  31. package/dist/{registry-DLZlAhdP.js → registry-XJkXDNK2.js} +1 -1
  32. package/dist/{resolve-9_cIPQsq.js → resolve-DhcdtDt6.js} +2 -2
  33. package/dist/{runner-C_eUbFyp.js → runner-CGyrnIPm.js} +9 -9
  34. package/dist/{sandbox-egress-signals-C1ycxTNA.js → sandbox-egress-signals-D6F3OZJP.js} +1 -1
  35. package/dist/{schema-C4XwHTY4.js → schema-Cabm4jcj.js} +1 -1
  36. package/dist/{skills-Dqgjr_Tl.js → skills-C6PqcBPU.js} +2 -2
  37. package/dist/{spawn-BfEr3t7b.js → spawn-DL9x3T1o.js} +2 -2
  38. package/dist/{task-queue-CuFZDOXn.js → task-queue-vZnBGu9B.js} +3 -3
  39. package/dist/{task-runner-c7gwV8dj.js → task-runner-C-c0t5F5.js} +54 -12
  40. package/dist/{checkpoint-C1ZnSz7r.js → tasks-EqCtmUJ2.js} +1056 -14
  41. package/dist/{thread-state-exTu-G1w.js → thread-state-B8IKs_J8.js} +6 -6
  42. package/dist/{turn-failure-response-DFp99ziF.js → turn-failure-response-BS7OO1BE.js} +9 -8
  43. package/dist/{turn-lifecycle-C4TjuOzG.js → turn-lifecycle-Cxrt8qMD.js} +1 -1
  44. package/dist/{turn-session-routing-Vpg8jUD1.js → turn-session-routing-BlGt2AUb.js} +1 -1
  45. package/dist/{validation-D2lULEG9.js → validation-C8hci6pj.js} +1 -1
  46. package/dist/{version-BjBMCc2o.js → version-CjnHQ8aY.js} +1 -1
  47. package/dist/version.js +1 -1
  48. package/dist/{viewer-XE3nFqKM.js → viewer-BOgEy5zC.js} +3 -3
  49. package/dist/{web-authorization-8E8IrWOj.js → web-authorization-DSMV5tMK.js} +6 -6
  50. package/dist/{web-input-DF4GGJrf.js → web-input-TD5P9jdo.js} +135 -129
  51. package/dist/{work-Dt_JjfbX.js → work-D39OAwP1.js} +7 -7
  52. package/dist/{worker-BrbnLmD3.js → worker-CWiLgCWH.js} +5 -5
  53. package/migrations/0049_event_automation_status_reason.sql +1 -0
  54. package/migrations/meta/0049_snapshot.json +3581 -0
  55. package/migrations/meta/_journal.json +8 -1
  56. package/package.json +5 -5
@@ -1,29 +1,29 @@
1
- import { b as getGatewayApiKey, c as hasCompactedConversationContext, d as unwrapCurrentInstruction, f as escapeXml, g as getProviderErrorAttributes, h as findProviderError, i as completeText, l as isCompactionSummaryText, m as createProviderError, n as GEN_AI_SERVER_ADDRESS, o as resolveGatewayModel, p as ProviderError, r as completeObject, s as COMPACTION_SUMMARY_PREFIX, t as GEN_AI_PROVIDER_NAME, u as renderCurrentInstruction, v as isProviderRetryError, x as resolveGatewayCredential, y as MISSING_GATEWAY_CREDENTIALS_ERROR } from "./client-Cn73E7br.js";
2
- import { C as withLogContext, F as normalizeIdentityEmail, I as isRecord$3, L as toOptionalNumber, R as toOptionalString, a as extractGenAiUsageSummary, b as setTags, c as getLogContextAttributesForSpan, d as logException, f as logInfo, g as serializeGenAiAttribute, i as extractGenAiUsageAttributes, l as getTracePropagationHeaders, m as normalizeGenAiFinishReason, p as logWarn, s as getLogContextAttributes, u as logError, v as setSpanAttributes, w as withSpan, x as summarizeMessageText, y as setSpanStatus } from "./logging-BSNF167N.js";
3
- import { m as startInactiveSpan, n as captureMessage, 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-BKlX7D_R.js";
5
- import { i as getChatConfig, n as botConfig, v as normalizeSlackEmojiName } from "./config-B0S86yw9.js";
1
+ import { b as getGatewayApiKey, c as hasCompactedConversationContext, d as unwrapCurrentInstruction, f as escapeXml, g as getProviderErrorAttributes, h as findProviderError, i as completeText, l as isCompactionSummaryText, m as createProviderError, n as GEN_AI_SERVER_ADDRESS, o as resolveGatewayModel, p as ProviderError, r as completeObject, s as COMPACTION_SUMMARY_PREFIX, t as GEN_AI_PROVIDER_NAME, u as renderCurrentInstruction, v as isProviderRetryError, x as resolveGatewayCredential, y as MISSING_GATEWAY_CREDENTIALS_ERROR } from "./client-D51G-Jdg.js";
2
+ import { C as withLogContext, F as normalizeIdentityEmail, I as isRecord$3, L as toOptionalNumber, R as toOptionalString, a as extractGenAiUsageSummary, b as setTags, c as getLogContextAttributesForSpan, d as logException, f as logInfo, g as serializeGenAiAttribute, i as extractGenAiUsageAttributes, l as getTracePropagationHeaders, m as normalizeGenAiFinishReason, p as logWarn, u as logError, v as setSpanAttributes, w as withSpan, x as summarizeMessageText, y as setSpanStatus } from "./logging-BdZ_mK3q.js";
3
+ 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-CzL47nXs.js";
5
+ import { i as getChatConfig, n as botConfig, v as normalizeSlackEmojiName } from "./config-C6Uu3Re9.js";
6
6
  import { a as formatModelProfiles, c as profileConfig, i as formatModelProfile, l as TURN_REASONING_LEVELS, n as ModelProfileNotConfiguredError, o as modelIdForProfile, s as modelProfileSchema } from "./model-profile-BwU8mqwD.js";
7
7
  import { a as isUserActor, d as parseSlackTeamId, f as parseSlackUserId, g as parseSlackMessageTs, l as parseSlackChannelId, s as parseStoredSlackActor, u as parseSlackChannelReferenceId } from "./actor-BTtLaeWy.js";
8
- import { F as sameDestination$1, N as parseDestination, T as hasAgentTurnUsage, c as getConversationEventStore, d as getDb, f as getSqlExecutor, l as getConversationMessageSearchStore, r as readActorIdentity, u as getConversationStore, x as addAgentTurnUsage } from "./viewer-XE3nFqKM.js";
8
+ import { F as sameDestination$1, N as parseDestination, T as hasAgentTurnUsage, c as getConversationEventStore, d as getDb, f as getSqlExecutor, l as getConversationMessageSearchStore, r as readActorIdentity, u as getConversationStore, x as addAgentTurnUsage } from "./viewer-BOgEy5zC.js";
9
9
  import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
10
- import { A as juniorArtifacts, D as juniorConversations, O as juniorIdentities, S as juniorConversationAnnotations, T as juniorAttachments, _ as juniorConversationBriefs, a as juniorSchedulerRuns, f as AGENT_INVOCATION_MAILBOX_STATUSES, l as juniorStats, o as juniorSchedulerTasks, s as juniorAutomationVersions, u as juniorEventAutomations } from "./executor-BQ-A5nov.js";
10
+ import { A as juniorArtifacts, D as juniorConversations, O as juniorIdentities, S as juniorConversationAnnotations, T as juniorAttachments, _ as juniorConversationBriefs, a as juniorSchedulerRuns, f as AGENT_INVOCATION_MAILBOX_STATUSES, l as juniorStats } from "./executor-CGUukwus.js";
11
11
  import { d as instructionActors, f as instructionProvenanceFor, p as sameActorIdentity, t as KNOWN_CONVERSATION_EVENT_TYPES, u as contextProvenance } from "./history-BLTUgFYO.js";
12
12
  import { a as listReferenceFiles, c as soulPathCandidates, l as worldPathCandidates } from "./package-discovery-BVvSnQBB.js";
13
- import { a as resolveAuthTokenPlaceholder, c as fetchWithBoundedOAuthErrorBodies, d as credentialContextForActor, f as credentialContextSchema, h as CredentialUnavailableError, i as resolvePluginHeaderEnvRefs, l as getOAuthProviderErrorAttributes, m as credentialUserSubjectId, o as resolvePluginCommandEnv, p as credentialSubjectSchema, s as OAuthProviderError } from "./registry-DLZlAhdP.js";
14
- import { n as pluginCatalogRuntime } from "./catalog-runtime-DdigNl7s.js";
15
- import { a as parseSkillInvocation, i as parseSkillFile, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-Dqgjr_Tl.js";
16
- import { C as isToolResultError, D as stripRuntimeTurnContext, E as retainRuntimeTurnContext, H as recordAuthorizationRequested, I as loadTurnRoute, K as recordToolExecutionStarted, L as openConversationProjection, M as loadConnectedMcpProviders, N as loadConversationProjection, O as trimTrailingAssistantMessages, R as recordAgentsInstructionsUpdated, S as isContinuablePiBoundary, T as normalizeToolNameFromResult, U as recordGuardianActionReviewed, W as recordMcpProviderConnected, _ as getTerminalAssistantMessages, _t as historyItemFromPiMessage, d as TurnSliceLimitExceededError, f as assertTurnToolCallLimit, g as getPiMessageRole, gt as getAgentContextInputLimitTokens, h as extractAssistantText, ht as getAgentContextCompactionTriggerTokens, k as TURN_CONTEXT_TAG, m as isTurnExecutionLimitExceededError, mt as estimateTextTokens, n as loadTurnCheckpoint, nt as resolveTaskTitle, q as recordTurnRoute, r as saveTurnCheckpoint, t as continuableMessages, tt as fallbackShortTitle, v as getUserMessageInstructionText, vt as piMessageFromHistoryItem, w as isToolResultMessage, x as isAssistantMessage, y as hasRuntimeTurnContext, z as recordAttachmentsDelivered } from "./checkpoint-C1ZnSz7r.js";
17
- import { A as getWorkspaceSnapshotBuilder, B as cancelWatch, Bt as saveObjectAnnotations, C as WorkspaceValidationError, D as setWorkspaceSnapshotBuild, E as setWorkspaceSnapshot, F as RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS, G as findMatchingWatches, Gt as resolveUserTimezone, H as createTimerWatch, I as RESOURCE_SUBSCRIPTION_MAX_TTL_MS, It as matchSlackAttachments, K as listWatches, L as RESOURCE_WATCH_TOOL_SOURCE, M as workspaceRepoCheckoutPath, N as resolveChannelCapabilities, O as SANDBOX_RUNTIME, P as getSlackToolContext, R as STOP_WATCHING_TOOL_NAME, S as updateWorkspace, T as loadSnapshotsForProfile, U as createWatch, Vt as createPluginAnnotations, W as deliverWatch, Wt as defaultTimezone, _ as deleteWorkspace, at as getSlackClient, b as listWorkspaces, c as getPluginTools, ct as normalizeSlackConversationId, et as verifyTimerWatchCredentialSubject, g as createWorkspace, gt as getDashboardTaskLink, j as tryWorkspaceRepoCheckoutPath, k as deleteWorkspaceSnapshotBuilders, l as getPluginUserPromptContributions, mt as getDashboardConversationLink, n as applyPluginFormatMarkdown, nt as downloadPrivateSlackFile, ot as isConversationScopedChannel, pt as JUNIOR_THREAD_STATE_TTL_MS, r as createPluginHookRunner, rt as getFilePermalink, s as getPluginSystemPromptContributions, st as isDmChannel$1, tt as SlackActionError, u as getPlugins, ut as withSlackRetries, v as getWorkspace, w as invalidateReadySnapshot, xt as createPluginLogger, y as getWorkspaceByName, z as cancelSubscriptions } from "./agent-hooks-JFbgedMZ.js";
13
+ import { a as resolveAuthTokenPlaceholder, c as fetchWithBoundedOAuthErrorBodies, d as credentialContextForActor, f as credentialContextSchema, h as CredentialUnavailableError, i as resolvePluginHeaderEnvRefs, l as getOAuthProviderErrorAttributes, m as credentialUserSubjectId, o as resolvePluginCommandEnv, p as credentialSubjectSchema, s as OAuthProviderError } from "./registry-XJkXDNK2.js";
14
+ import { n as pluginCatalogRuntime } from "./catalog-runtime-C2YkfMZz.js";
15
+ import { a as parseSkillInvocation, i as parseSkillFile, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-C6PqcBPU.js";
16
+ import { A as formatSlackLink, Ct as loadConversationProjection, D as automationRevision, Dt as openConversationProjection, Et as loadTurnRoute, F as addReactionToMessage, G as loadTurnCheckpoint, Gt as fallbackShortTitle, I as getSlackMessagePermalink, It as recordToolExecutionStarted, K as saveTurnCheckpoint, Kt as resolveTaskTitle, L as openSlackDirectMessage, Lt as recordTurnRoute, Mt as recordAuthorizationRequested, N as slackMention, Nt as recordGuardianActionReviewed, Ot as recordAgentsInstructionsUpdated, Pt as recordMcpProviderConnected, R as postSlackEphemeralMessage, St as loadConnectedMcpProviders, V as uploadFilesToConversation, W as continuableMessages, _t as trimTrailingAssistantMessages, a as readScheduledAutomation, an as getAgentContextCompactionTriggerTokens, at as getPiMessageRole, b as saveEventAutomation, cn as piMessageFromHistoryItem, ct as hasRuntimeTurnContext, dt as isContinuablePiBoundary, et as TurnSliceLimitExceededError, f as createEventAutomation, ft as isToolResultError, g as getEventAutomation, gt as stripRuntimeTurnContext, ht as retainRuntimeTurnContext, in as estimateTextTokens, it as extractAssistantText, k as escapeSlackMrkdwnText, kt as recordAttachmentsDelivered, mt as normalizeToolNameFromResult, o as saveScheduledAutomation, on as getAgentContextInputLimitTokens, ot as getTerminalAssistantMessages, p as deleteEventAutomation, pt as isToolResultMessage, r as listScheduledAutomationsForTeam, rt as isTurnExecutionLimitExceededError, sn as historyItemFromPiMessage, st as getUserMessageInstructionText, t as createScheduledAutomation, tt as assertTurnToolCallLimit, ut as isAssistantMessage, v as listEventAutomationsForTeam, vt as TURN_CONTEXT_TAG, x as setEventAutomationStatus, yt as ToolInputError, z as postSlackMessage } from "./tasks-EqCtmUJ2.js";
17
+ import { A as getWorkspaceSnapshotBuilder, B as cancelWatch, Bt as isConversationScopedChannel, C as WorkspaceValidationError, D as setWorkspaceSnapshotBuild, E as setWorkspaceSnapshot, F as RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS, Ft as SlackActionError, G as findMatchingWatches, H as createTimerWatch, Ht as normalizeSlackConversationId, I as RESOURCE_SUBSCRIPTION_MAX_TTL_MS, It as downloadPrivateSlackFile, K as listWatches, L as RESOURCE_WATCH_TOOL_SOURCE, Lt as getFilePermalink, M as workspaceRepoCheckoutPath, Mt as defaultTimezone, N as resolveChannelCapabilities, Nt as resolveUserTimezone, O as SANDBOX_RUNTIME, Ot as saveObjectAnnotations, P as getSlackToolContext, R as STOP_WATCHING_TOOL_NAME, S as updateWorkspace, T as loadSnapshotsForProfile, U as createWatch, Vt as isDmChannel$1, W as deliverWatch, Wt as withSlackRetries, _ as deleteWorkspace, b as listWorkspaces, c as getPluginTools, et as verifyTimerWatchCredentialSubject, g as createWorkspace, it as getDashboardTaskLink, j as tryWorkspaceRepoCheckoutPath, k as deleteWorkspaceSnapshotBuilders, kt as createPluginAnnotations, l as getPluginUserPromptContributions, lt as createPluginLogger, nt as getDashboardConversationLink, r as createPluginHookRunner, s as getPluginSystemPromptContributions, tt as JUNIOR_THREAD_STATE_TTL_MS, u as getPlugins, v as getWorkspace, w as invalidateReadySnapshot, wt as matchSlackAttachments, y as getWorkspaceByName, z as cancelSubscriptions, zt as getSlackClient } from "./agent-hooks-BVttjZvb.js";
18
18
  import { F as isVisionImageMediaType, G as removedCardSchema, L as briefLinkSchema, R as briefOutcomeStatusSchema, z as conversationBriefSchema } from "./conversation-DwD-vquD.js";
19
- import { A as SANDBOX_DATA_ROOT, C as getDefaultRedisStateAdapterFor, 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$2, 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-3t9H_IwL.js";
20
- import { n as canReusePendingAuthLink, t as abandonReplacedPendingAuth } from "./pending-auth-Chn_EA1_.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-BM7g61od.js";
19
+ import { A as SANDBOX_DATA_ROOT, C as getDefaultRedisStateAdapterFor, 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$2, 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-2gPzzMib.js";
20
+ import { n as canReusePendingAuthLink, t as abandonReplacedPendingAuth } from "./pending-auth-DOzL4D24.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-BokpeHv-.js";
22
22
  import { i as isValidTimeZone, r as getZonedDateTimeParts } from "./cadence-D00SCzT1.js";
23
23
  import { r as EVENT_AUTHOR_ID } from "./prompt-CMU-B4FA.js";
24
- 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-9_cIPQsq.js";
25
- import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-DgXLqoeB.js";
26
- import { A as eventAutomationSchema, D as scheduledRunSchema, E as scheduledAutomationSchema, F as taskOutcomeInputSchema, M as compileScheduleIntent, N as scheduleIntentSchema, O as automationInstructionToolSchema, S as automationVersionSchema, T as SCHEDULED_AUTOMATION_SYSTEM_ACTOR, j as ScheduleIntentError, k as automationTitleSchema } from "./automation-B5_c8hEb.js";
24
+ 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-DhcdtDt6.js";
25
+ import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-hLylM9cy.js";
26
+ import { F as taskOutcomeInputSchema, M as compileScheduleIntent, N as scheduleIntentSchema, O as automationInstructionToolSchema, T as SCHEDULED_AUTOMATION_SYSTEM_ACTOR, j as ScheduleIntentError, k as automationTitleSchema } from "./automation-gznOMaQ2.js";
27
27
  import { r as isExperimentalFeatureEnabled } from "./experimental-Qn9rKLgx.js";
28
28
  import { n as privateTraceResultAttributes } from "./private-trace-result-BLvBjy_e.js";
29
29
  import { streamSimple } from "@earendil-works/pi-ai/compat";
@@ -34,8 +34,8 @@ import { isDeepStrictEqual } from "node:util";
34
34
  import { AsyncLocalStorage } from "node:async_hooks";
35
35
  import { z } from "zod";
36
36
  import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
37
- import { and, asc, desc, eq, gt, gte, inArray, isNotNull, isNull, lt, lte, ne, notExists, notInArray, or, sql } from "drizzle-orm";
38
- import { EgressAuthRequired, EgressPolicyDenied, PluginToolInputError, actorSchema, destinationSchema, eventMatchSchema, eventMatches, eventSchema, missingToolAnnotationKeys, normalizeEventIdentifier, objectTypeSchema, ownedObjectAnnotationSchema, pluginAuthorizationSchema, pluginCredentialHeaderTransformSchema, pluginCredentialResultSchema, pluginGrantSchema, pluginProviderAccountSchema, pluginStoredTokensSchema, slackDestinationSchema, sourceSchema, stableEventMatchKey, taskOutcomeSchema, watchResultSchema } from "@sentry/junior-plugin-api";
37
+ import { and, asc, desc, eq, gt, gte, inArray, isNotNull, isNull, lt, lte, ne, notExists, or, sql } from "drizzle-orm";
38
+ import { EgressAuthRequired, EgressPolicyDenied, PluginToolInputError, actorSchema, destinationSchema, eventMatchSchema, eventSchema, missingToolAnnotationKeys, normalizeEventIdentifier, objectTypeSchema, ownedObjectAnnotationSchema, pluginAuthorizationSchema, pluginCredentialHeaderTransformSchema, pluginCredentialResultSchema, pluginGrantSchema, pluginProviderAccountSchema, pluginStoredTokensSchema, sourceSchema, stableEventMatchKey, watchResultSchema } from "@sentry/junior-plugin-api";
39
39
  import { alias } from "drizzle-orm/pg-core";
40
40
  import { Agent, estimateContextTokens } from "@earendil-works/pi-agent-core";
41
41
  import fs from "node:fs/promises";
@@ -222,15 +222,6 @@ var SkillSandbox = class {
222
222
  }
223
223
  };
224
224
  //#endregion
225
- //#region src/chat/tools/execution/tool-input-error.ts
226
- /** Thrown when a tool fails due to invalid model/user input, not a system error. */
227
- var ToolInputError = class extends Error {
228
- constructor(message, options) {
229
- super(message, options);
230
- this.name = "ToolInputError";
231
- }
232
- };
233
- //#endregion
234
225
  //#region src/chat/tools/sandbox/file-utils.ts
235
226
  const MAX_TEXT_CHARS = 6e4;
236
227
  const RIPGREP_EXCLUDED_GLOBS = ["!**/.git/**", "!**/node_modules/**"];
@@ -267,7 +258,8 @@ function isMissingPathError(error) {
267
258
  }
268
259
  /** Reject an expected missing search/list path through the tool-error channel. */
269
260
  function missingPathSearchResult(params) {
270
- throw new ToolInputError(`Path not found: ${params.displayPath ?? params.missingPath ?? params.path}`);
261
+ const textPath = params.displayPath ?? params.missingPath ?? params.path;
262
+ throw new ToolInputError(`Path not found: ${textPath}`);
271
263
  }
272
264
  function escapeRegExp(value) {
273
265
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
@@ -811,10 +803,102 @@ function shouldEmitDevAgentTrace() {
811
803
  return process.env.NODE_ENV === "development";
812
804
  }
813
805
  //#endregion
806
+ //#region src/chat/automation-result.ts
807
+ /**
808
+ * Automation run result.
809
+ *
810
+ * A Scheduled automation or Event automation run ends with one declared
811
+ * result. The run does not deliver its final assistant text. The work owner
812
+ * reads the declared result and applies the stored outcomes.
813
+ */
814
+ /** Model-facing tool that ends an Automation run. */
815
+ const FINISH_AUTOMATION_RUN_TOOL_NAME = "finishAutomationRun";
816
+ /** One declared result for an Automation run. */
817
+ const automationResultSchema = z.discriminatedUnion("result", [
818
+ z.object({
819
+ result: z.literal("send_message"),
820
+ message: z.string().transform(sanitizeAssistantText).pipe(z.string().min(1))
821
+ }),
822
+ z.object({
823
+ result: z.literal("no_action"),
824
+ reason: z.string().trim().min(1)
825
+ }),
826
+ z.object({
827
+ result: z.literal("misconfigured"),
828
+ reason: z.string().trim().min(1)
829
+ })
830
+ ]);
831
+ /**
832
+ * Return whether the runtime delivers the final assistant text of a run. It
833
+ * does not for a dispatch with no outcomes. It does not for an Automation
834
+ * run, which posts only its declared message, after the run.
835
+ */
836
+ function deliversFinalText(dispatch) {
837
+ return dispatch?.outcomes?.length !== 0 && !dispatch?.declaresResult;
838
+ }
839
+ /**
840
+ * Return the text to post after a run ends, if any. A chat Turn already
841
+ * delivered a successful reply, so it posts only its failure reply. An
842
+ * Automation run posts only a declared message. It never posts a failure
843
+ * reply to its outcomes. The failed execution shows on the Automation.
844
+ */
845
+ function finishedRunReply(result, dispatch) {
846
+ if (!dispatch?.declaresResult) return result.diagnostics.outcome === "success" ? void 0 : result.text;
847
+ return result.diagnostics.outcome === "success" && result.automation?.result === "send_message" && dispatch.outcomes?.length !== 0 ? result.text : void 0;
848
+ }
849
+ /**
850
+ * Read the last successful declared result from agent history items.
851
+ *
852
+ * Pass the whole run history, not only the current slice. An Automation run
853
+ * owns its dispatch Conversation, and a resumed slice must see a result that
854
+ * an earlier slice already saved.
855
+ */
856
+ function readAutomationResult(messages) {
857
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
858
+ const message = messages[index];
859
+ if (!isToolResultMessage(message) || isToolResultError(message) || normalizeToolNameFromResult(message) !== "finishAutomationRun") continue;
860
+ const parsed = automationResultSchema.safeParse(message.details);
861
+ if (parsed.success) return parsed.data;
862
+ }
863
+ }
864
+ /**
865
+ * Map one finished agent run to its dispatch outcome. A declared
866
+ * `misconfigured` result becomes a blocked dispatch with the declared reason.
867
+ */
868
+ function runDispatchOutcome(result) {
869
+ if (result.diagnostics.outcome !== "success") return {
870
+ errorMessage: result.diagnostics.errorMessage ?? `Agent turn ended with ${result.diagnostics.outcome}.`,
871
+ outcome: "failed"
872
+ };
873
+ if (result.automation?.result === "misconfigured") return {
874
+ errorMessage: result.automation.reason,
875
+ outcome: "blocked"
876
+ };
877
+ return { outcome: "completed" };
878
+ }
879
+ const MISSING_RESULT_REMINDER = "This automation run has not ended. Your text was not delivered. Call `finishAutomationRun` now with one result.";
880
+ /**
881
+ * Return agent history plus a reminder when the model stopped without a
882
+ * declared result, or undefined when the run already declared one.
883
+ */
884
+ function remindMissingAutomationResult(messages) {
885
+ const last = messages.at(-1);
886
+ if (!isAssistantMessage(last) || last.stopReason !== "stop" || readAutomationResult(messages)) return;
887
+ return [...messages, {
888
+ role: "user",
889
+ content: [{
890
+ type: "text",
891
+ text: MISSING_RESULT_REMINDER
892
+ }],
893
+ timestamp: Date.now()
894
+ }];
895
+ }
896
+ //#endregion
814
897
  //#region src/chat/services/turn-result.ts
815
898
  /** Process raw agent messages into a structured AgentRunResult. */
816
899
  function buildTurnResult(input) {
817
- const { newMessages, toolCalls, sandboxRef, durationMs, shouldTrace, usage, executionProfile, modelId } = input;
900
+ if (input.requireAutomationResult) return buildAutomationTurnResult(input);
901
+ const { newMessages, sandboxRef, shouldTrace } = input;
818
902
  const toolResults = newMessages.filter(isToolResultMessage);
819
903
  const assistantMessages = newMessages.filter(isAssistantMessage);
820
904
  const terminalAssistantMessages = getTerminalAssistantMessages(newMessages);
@@ -857,29 +941,68 @@ function buildTurnResult(input) {
857
941
  "app.ai.assistant_messages": assistantMessages.length,
858
942
  ...stopReason ? { "gen_ai.response.finish_reasons": [stopReason] } : void 0
859
943
  });
860
- const resolvedDiagnostics = {
944
+ return {
945
+ text: primaryText,
946
+ sandboxRef,
947
+ piMessages: input.piMessages,
948
+ diagnostics: buildDiagnostics(input, {
949
+ outcome,
950
+ usedPrimaryText,
951
+ stopReason,
952
+ errorMessage,
953
+ lastAssistant
954
+ })
955
+ };
956
+ }
957
+ function buildDiagnostics(input, result) {
958
+ const { outcome, errorMessage, lastAssistant } = result;
959
+ const toolResults = input.newMessages.filter(isToolResultMessage);
960
+ return {
861
961
  outcome,
862
- modelId,
863
- assistantMessageCount: assistantMessages.length,
864
- reasoningLevel: executionProfile.reasoningLevel,
865
- toolCalls,
962
+ modelId: input.modelId,
963
+ assistantMessageCount: input.newMessages.filter(isAssistantMessage).length,
964
+ reasoningLevel: input.executionProfile.reasoningLevel,
965
+ toolCalls: input.toolCalls,
866
966
  toolResultCount: toolResults.length,
867
- toolErrorCount,
868
- usedPrimaryText,
869
- durationMs,
870
- usage,
871
- stopReason,
967
+ toolErrorCount: toolResults.filter((toolResult) => toolResult.isError).length,
968
+ usedPrimaryText: result.usedPrimaryText,
969
+ durationMs: input.durationMs,
970
+ usage: input.usage,
971
+ stopReason: result.stopReason,
872
972
  errorMessage,
873
973
  providerError: outcome === "provider_error" && errorMessage ? createProviderError(errorMessage, {
874
- modelId,
875
- retryable: lastAssistant !== void 0 && isAssistantMessage(lastAssistant) && isRetryableAssistantError(lastAssistant)
974
+ modelId: input.modelId,
975
+ retryable: isAssistantMessage(lastAssistant) && isRetryableAssistantError(lastAssistant)
876
976
  }) : void 0
877
977
  };
978
+ }
979
+ /**
980
+ * Build the result of an Automation run from its declared result. Final
981
+ * assistant text is not used. A run without a declared result fails.
982
+ */
983
+ function buildAutomationTurnResult(input) {
984
+ const lastAssistant = input.newMessages.filter(isAssistantMessage).at(-1);
985
+ const stopReason = lastAssistant?.stopReason;
986
+ const automation = readAutomationResult(input.piMessages ?? input.newMessages);
987
+ const outcome = automation ? "success" : stopReason === "error" ? "provider_error" : "execution_failure";
988
+ const errorMessage = outcome === "provider_error" ? lastAssistant?.errorMessage : outcome === "execution_failure" ? "Automation run ended without a declared result." : void 0;
989
+ if (input.shouldTrace) logInfo("agent.message.generated", {
990
+ "app.message.kind": "automation_result",
991
+ "app.ai.outcome": outcome,
992
+ "app.automation.result": automation?.result ?? "missing"
993
+ });
878
994
  return {
879
- text: primaryText,
880
- sandboxRef,
995
+ text: automation?.result === "send_message" ? automation.message : "",
996
+ ...automation ? { automation } : void 0,
997
+ sandboxRef: input.sandboxRef,
881
998
  piMessages: input.piMessages,
882
- diagnostics: resolvedDiagnostics
999
+ diagnostics: buildDiagnostics(input, {
1000
+ outcome,
1001
+ usedPrimaryText: automation?.result === "send_message",
1002
+ stopReason,
1003
+ errorMessage,
1004
+ lastAssistant
1005
+ })
883
1006
  };
884
1007
  }
885
1008
  //#endregion
@@ -1742,11 +1865,19 @@ const SLACK_ACTION_RULES = [
1742
1865
  "- Ambient reaction requests target the current inbound message; do not ask for a message reference.",
1743
1866
  `- When no visible thread reply is requested or useful, keep tool-calling messages text-free and make the final message exactly ${NO_REPLY_MARKER}.`
1744
1867
  ];
1868
+ const AUTOMATION_RUN_RULES = [
1869
+ "- This run comes from a stored automation, not from a person. The stored instruction is the job, and its creator already approved it.",
1870
+ "- Nobody reads this run while it executes. Do not ask questions, ask for approval, or offer options. Infer conservatively and do the work now.",
1871
+ "- Check every condition in the instruction before any action with side effects. If a condition is not met or cannot be verified, stop before you act.",
1872
+ "- A tool result with `timed_out: true` means that attempt did not finish. Before you retry work that may have side effects, inspect authoritative state and do not repeat a mutation that already applied.",
1873
+ `- Assistant text is never delivered. End the run with \`${FINISH_AUTOMATION_RUN_TOOL_NAME}\`. Do not post the result with another tool.`
1874
+ ];
1745
1875
  const SAFETY_RULES = [
1746
1876
  "- Stay within the user's request and the runtime's available capabilities; do not pursue independent goals, persistence, replication, credential gathering, or access expansion.",
1747
1877
  "- Respect stop, pause, audit, and approval boundaries. Do not bypass safeguards or persuade the user to weaken them.",
1748
1878
  "- Do not change system prompts, tool policies, security settings, credentials, or runtime configuration unless the user explicitly requests that exact administrative action and an available tool permits it."
1749
1879
  ];
1880
+ const AUTOMATION_FAILURE_RULES = ["- Keep commands, error output, raw tool payloads, and internal routing metadata out of the message.", `- When a failure stops the job, put its cause in the \`${FINISH_AUTOMATION_RUN_TOOL_NAME}\` reason, not in the message.`];
1750
1881
  const FAILURE_RULES = [
1751
1882
  "- For tool/runtime failures, run the named check before diagnosing and report the exact failed command plus stderr/exit code.",
1752
1883
  "- If a fact cannot be verified after focused checks, say what you checked and what blocked a stronger answer.",
@@ -1759,21 +1890,33 @@ function renderRuleSection(tag, lines) {
1759
1890
  `</${tag}>`
1760
1891
  ].join("\n");
1761
1892
  }
1762
- function buildBehaviorSection(platform) {
1763
- const sections = [
1893
+ function buildBehaviorSection(platform, mode) {
1894
+ const runSections = mode === "automation" ? [renderRuleSection("automation-run", AUTOMATION_RUN_RULES)] : [
1895
+ renderRuleSection("task-execution", TASK_EXECUTION_RULES),
1896
+ renderRuleSection("conversation", CONVERSATION_RULES),
1897
+ ...platform === "slack" ? [renderRuleSection("slack-actions", SLACK_ACTION_RULES)] : []
1898
+ ];
1899
+ const automation = mode === "automation";
1900
+ return [
1764
1901
  renderRuleSection("tool-policy", TOOL_POLICY_RULES),
1765
- renderRuleSection("tool-call-style", TOOL_CALL_STYLE_RULES),
1902
+ ...automation ? [] : [renderRuleSection("tool-call-style", TOOL_CALL_STYLE_RULES)],
1766
1903
  renderRuleSection("skill-policy", SKILL_POLICY_RULES),
1767
1904
  renderRuleSection("planning", PLANNING_RULES),
1768
- renderRuleSection("task-execution", TASK_EXECUTION_RULES),
1769
- renderRuleSection("conversation", CONVERSATION_RULES),
1905
+ ...runSections,
1770
1906
  renderRuleSection("safety", SAFETY_RULES),
1771
- renderRuleSection("failure-handling", FAILURE_RULES)
1772
- ];
1773
- if (platform === "slack") sections.splice(6, 0, renderRuleSection("slack-actions", SLACK_ACTION_RULES));
1774
- return sections.join("\n\n");
1907
+ renderRuleSection("failure-handling", automation ? AUTOMATION_FAILURE_RULES : FAILURE_RULES)
1908
+ ].join("\n\n");
1909
+ }
1910
+ function buildAutomationOutputSection(platform) {
1911
+ return [
1912
+ platform === "slack" ? `<output format="slack-markdown">` : `<output format="markdown">`,
1913
+ "- Follow any format in the instruction. Otherwise keep the message as short as the job allows, and do not describe the run.",
1914
+ platform === "slack" ? "- Use Slack-flavored Markdown: **bold** section labels, `code`, [text](url) links, bullet lists, and fenced code blocks. No hash-prefixed headings and no tables." : "- Use concise Markdown: short paragraphs, bullets, links, and fenced code blocks.",
1915
+ "</output>"
1916
+ ].join("\n");
1775
1917
  }
1776
- function buildOutputSection(platform) {
1918
+ function buildOutputSection(platform, mode) {
1919
+ if (mode === "automation") return buildAutomationOutputSection(platform);
1777
1920
  if (platform === "local") return [
1778
1921
  `<output format="markdown">`,
1779
1922
  "- Start with the answer or result, not internal process narration.",
@@ -1841,13 +1984,22 @@ function formatDestinationLines(destination) {
1841
1984
  `- destination.channel_id: ${escapeXml(destination.channelId)}`
1842
1985
  ];
1843
1986
  }
1987
+ function formatOutcomeLines(outcomes) {
1988
+ if (!outcomes) return ["- dispatch.outcomes: send_message to the destination below (legacy default)"];
1989
+ if (outcomes.length === 0) return ["- dispatch.outcomes: none; successful work posts nothing"];
1990
+ return outcomes.map((outcome, index) => {
1991
+ const { channelId, threadTs } = outcome.destination;
1992
+ const recipient = isDmChannel$1(channelId) ? "the creator's direct message" : "a slack channel";
1993
+ const target = [`channel_id=${escapeXml(channelId)}`, ...threadTs ? [`thread_ts=${escapeXml(threadTs)}`] : []].join(" ");
1994
+ return `- dispatch.outcome.${index + 1}: ${outcome.action} to ${recipient} (${target})`;
1995
+ });
1996
+ }
1844
1997
  function buildDispatchSection(params) {
1845
1998
  if (!params) return null;
1846
1999
  const metadataLines = Object.entries(params.metadata ?? {}).sort(([left], [right]) => left.localeCompare(right)).map(([key, value]) => `- dispatch.metadata.${escapeXml(key)}: ${escapeXml(value)}`);
1847
2000
  return renderTag("dispatch", [
1848
2001
  "- dispatch.execution: execute the dispatched input now",
1849
- "- dispatch.delivery: the runtime delivers the final answer to the destination",
1850
- "- dispatch.delivery_rule: do not request or require a separate posting tool just to deliver the final answer",
2002
+ ...params.declaresResult ? formatOutcomeLines(params.outcomes) : ["- dispatch.delivery: the runtime delivers the final answer to the destination", "- dispatch.delivery_rule: do not request or require a separate posting tool just to deliver the final answer"],
1851
2003
  ...params.actor ? [`- dispatch.actor.platform: ${escapeXml(params.actor.platform)}`, `- dispatch.actor.name: ${escapeXml(params.actor.name)}`] : [],
1852
2004
  ...params.plugin ? [`- dispatch.plugin: ${escapeXml(params.plugin)}`] : [],
1853
2005
  ...formatSourceLines(params.source),
@@ -1897,23 +2049,29 @@ function buildPluginSystemPromptContributions(contributions) {
1897
2049
  for (const contribution of contributions) lines.push(` <plugin-contribution plugin="${escapeXml(contribution.pluginName)}" id="${escapeXml(contribution.id)}">`, escapeXml(contribution.text.trim()), " </plugin-contribution>");
1898
2050
  return renderTagBlock("plugin-system-context", lines.join("\n"));
1899
2051
  }
1900
- function buildStaticSystemPrompt(platform) {
2052
+ function buildStaticSystemPrompt(platform, mode) {
1901
2053
  return [
1902
2054
  platform === "slack" ? SLACK_HEADER : LOCAL_HEADER,
1903
2055
  buildIdentitySection(platform),
1904
2056
  buildPersonalitySection(),
1905
2057
  buildWorldSection(),
1906
- buildBehaviorSection(platform),
1907
- buildOutputSection(platform)
2058
+ buildBehaviorSection(platform, mode),
2059
+ buildOutputSection(platform, mode)
1908
2060
  ].filter((section) => Boolean(section)).join("\n\n");
1909
2061
  }
1910
2062
  const STATIC_SYSTEM_PROMPTS = {
1911
- local: buildStaticSystemPrompt("local"),
1912
- slack: buildStaticSystemPrompt("slack")
2063
+ automation: {
2064
+ local: buildStaticSystemPrompt("local", "automation"),
2065
+ slack: buildStaticSystemPrompt("slack", "automation")
2066
+ },
2067
+ conversation: {
2068
+ local: buildStaticSystemPrompt("local", "conversation"),
2069
+ slack: buildStaticSystemPrompt("slack", "conversation")
2070
+ }
1913
2071
  };
1914
- /** Return the fixed instructions shared by every Conversation and Turn. */
1915
- function buildSystemPrompt(platform) {
1916
- return STATIC_SYSTEM_PROMPTS[platform];
2072
+ /** Return the fixed instructions for one platform and prompt mode. */
2073
+ function buildSystemPrompt(platform, mode = "conversation") {
2074
+ return STATIC_SYSTEM_PROMPTS[mode][platform];
1917
2075
  }
1918
2076
  /** Build volatile runtime context that belongs in the user turn, not the system prompt. */
1919
2077
  function buildTurnContextPrompt(params) {
@@ -2200,9 +2358,10 @@ async function assemblePrompt(args) {
2200
2358
  userContentParts: requestContentParts
2201
2359
  }) : void 0;
2202
2360
  const needsBootstrapContextForPrompt = shouldPromptAgent && !replayedPrompt && !hasRuntimeTurnContext(promptHistoryMessages);
2203
- const platform = args.run.delivery && args.run.location?.provider === "slack" ? "slack" : "local";
2361
+ const promptMode = args.run.dispatch?.declaresResult ? "automation" : "conversation";
2362
+ const platform = (args.run.delivery || promptMode === "automation") && args.run.location?.provider === "slack" ? "slack" : "local";
2204
2363
  const pluginSystemPrompt = buildPluginSystemPromptContributions(await getPluginSystemPromptContributions(platform));
2205
- const baseInstructions = [buildSystemPrompt(platform), pluginSystemPrompt].filter((section) => Boolean(section)).join("\n\n");
2364
+ const baseInstructions = [buildSystemPrompt(platform, promptMode), pluginSystemPrompt].filter((section) => Boolean(section)).join("\n\n");
2206
2365
  const pluginUserPromptContributions = !shouldPromptAgent || replayedPrompt ? [] : await getPluginUserPromptContributions({
2207
2366
  context: args.toolRuntimeContext,
2208
2367
  turnId: args.turnId
@@ -2661,541 +2820,6 @@ function formatSandboxCommandResult(params) {
2661
2820
  });
2662
2821
  }
2663
2822
  //#endregion
2664
- //#region src/chat/slack/post-warning.ts
2665
- const codes = /* @__PURE__ */ new Set([
2666
- "invalid_metadata_format",
2667
- "invalid_metadata_schema",
2668
- "metadata_too_large",
2669
- "invalid_arguments",
2670
- "invalid_blocks",
2671
- "missing_scope",
2672
- "missing_charset",
2673
- "superfluous_charset",
2674
- "message_truncated"
2675
- ]);
2676
- const pathFields = /* @__PURE__ */ new Set([
2677
- "metadata",
2678
- "event_type",
2679
- "event_payload",
2680
- "entities",
2681
- "entity_type",
2682
- "entity_payload",
2683
- "external_ref",
2684
- "id",
2685
- "type",
2686
- "url",
2687
- "attributes",
2688
- "title",
2689
- "text",
2690
- "display_id",
2691
- "display_type",
2692
- "product_name",
2693
- "fields",
2694
- "status",
2695
- "custom_fields",
2696
- "key",
2697
- "label",
2698
- "value",
2699
- "long",
2700
- "format",
2701
- "link",
2702
- "display_order",
2703
- "app_unfurl_url",
2704
- "app_id",
2705
- "created_by",
2706
- "created_at",
2707
- "updated_by",
2708
- "updated_at",
2709
- "full_size_preview",
2710
- "preview_url",
2711
- "mime_type"
2712
- ]);
2713
- const diagnosticWords = new Set(`WARN ERROR Message message metadata was incorrectly formatted The the will be
2714
- ignored as a result For for event entity refer to following errors error
2715
- missing required field fields property properties additional unexpected unknown
2716
- unsupported invalid expected received actual type types must should match matches
2717
- matching does not is are has have of at in on and or an only allowed allow
2718
- value values string number integer boolean object array null minimum maximum
2719
- length min max items item empty nonempty too long short large small size limit
2720
- exceeded exceeds less greater than equal valid format schema validation failed
2721
- failure cannot contain contains found one any all none enum pattern definition
2722
- defined constraint constraints satisfy satisfies with without provided specified
2723
- unrecognized unrecognised recognized recognised extraneous disallowed duplicate
2724
- unique malformed nested root exactly needs need requires requirement supported
2725
- json-pointer input different schema_uri schema_id schemas additionalProperties
2726
- minLength maxLength minItems maxItems anyOf oneOf allOf`.split(/\s+/));
2727
- function summarizeDiagnostic(value) {
2728
- return value.slice(0, 2e3).replace(/\b(?:https?:\/\/|www\.)[^\s"'`<>]+/gi, "[value]").replace(/\/[^\s"'`[\],;()]*|[\p{L}\p{N}\p{M}_]+(?:[-./:@=+%~][\p{L}\p{N}\p{M}_]+)*|[^\s\w[\]{}():,;."'`/*=<>!?+-]/gu, (token) => {
2729
- if (token.startsWith("/")) return token.split("/").slice(0, 17).map((part, index) => index === 0 ? "" : pathFields.has(part) ? part : "*").join("/");
2730
- return codes.has(token) || pathFields.has(token) || diagnosticWords.has(token) ? token : "[value]";
2731
- }).replace(/\s+/g, " ").slice(0, 2e3);
2732
- }
2733
- /** Report Slack warnings once, on the request span, without retrying the post. */
2734
- function captureSlackPostWarning(response, attributes) {
2735
- const warnings = response.response_metadata?.warnings ?? [];
2736
- const messages = response.response_metadata?.messages ?? [];
2737
- const knownCodes = [...new Set(warnings.slice(0, 20).filter((code) => codes.has(code)))].sort();
2738
- const diagnostics = {
2739
- "app.slack.warning_count": warnings.length,
2740
- "app.slack.response_message_count": messages.length,
2741
- "app.slack.diagnostic_codes": knownCodes
2742
- };
2743
- setSpanAttributes(diagnostics);
2744
- if (!warnings.length && !messages.length) return;
2745
- captureMessage("Slack chat.postMessage returned warnings", {
2746
- level: "warning",
2747
- fingerprint: ["slack.chat.postMessage.warning", ...knownCodes.length ? knownCodes : ["unknown"]],
2748
- tags: { "app.slack.method": "chat.postMessage" },
2749
- extra: {
2750
- ...getLogContextAttributes(),
2751
- ...attributes,
2752
- ...diagnostics,
2753
- "app.slack.response_diagnostics": {
2754
- warnings: warnings.slice(0, 20).map(summarizeDiagnostic),
2755
- messages: messages.slice(0, 20).map(summarizeDiagnostic)
2756
- }
2757
- }
2758
- });
2759
- }
2760
- //#endregion
2761
- //#region src/chat/slack/work-object.ts
2762
- const url = z.url({ protocol: /^https?$/ });
2763
- const textField = z.strictObject({
2764
- type: z.literal("string"),
2765
- key: z.string().min(1),
2766
- label: z.string().min(1),
2767
- value: z.string(),
2768
- long: z.boolean().optional(),
2769
- format: z.literal("markdown").optional(),
2770
- link: url.optional()
2771
- }).refine((field) => !field.format || !field.link, { message: "Slack string fields cannot combine format and link" });
2772
- const customField = z.discriminatedUnion("type", [textField, z.strictObject({
2773
- type: z.literal("slack#/types/timestamp"),
2774
- key: z.string().min(1),
2775
- label: z.string().min(1),
2776
- value: z.number().int(),
2777
- link: url.optional()
2778
- })]);
2779
- const payload = z.strictObject({
2780
- attributes: z.strictObject({
2781
- title: z.strictObject({ text: z.string().min(1) }),
2782
- display_id: z.string().optional(),
2783
- display_type: z.string().optional(),
2784
- product_name: z.string().optional(),
2785
- product_icon: z.strictObject({
2786
- url,
2787
- alt_text: z.string().min(1)
2788
- }).optional()
2789
- }),
2790
- custom_fields: z.array(customField).optional(),
2791
- display_order: z.array(z.string()).optional()
2792
- });
2793
- /** Limit IDs to Junior's safe subset; Slack silently rejects JSON punctuation. */
2794
- const slackExternalRefIdSchema = z.string().min(1).regex(/^[A-Za-z0-9_-]+$/, "Unsupported Slack external reference ID");
2795
- const entity = z.strictObject({
2796
- external_ref: z.strictObject({
2797
- id: slackExternalRefIdSchema,
2798
- type: z.string().min(1).optional()
2799
- }),
2800
- url
2801
- });
2802
- /** Validate notification and detail metadata before Slack can ignore bad fields. */
2803
- const slackEntitySchema = z.discriminatedUnion("entity_type", [entity.extend({
2804
- entity_type: z.literal("slack#/entities/item"),
2805
- entity_payload: payload.extend({ fields: z.never().optional() })
2806
- }), entity.extend({
2807
- entity_type: z.literal("slack#/entities/task"),
2808
- entity_payload: payload.extend({ fields: z.strictObject({ status: z.strictObject({ value: z.string() }).optional() }).optional() })
2809
- })]);
2810
- //#endregion
2811
- //#region src/chat/slack/outbound.ts
2812
- const MAX_SLACK_MESSAGE_TEXT_CHARS = 4e4;
2813
- function requireSlackConversationId(channelId, action) {
2814
- const normalized = normalizeSlackConversationId(channelId);
2815
- if (!normalized) throw new Error(`${action} requires a valid channel ID`);
2816
- return normalized;
2817
- }
2818
- function requireSlackThreadTimestamp(threadTs, action) {
2819
- const normalized = threadTs.trim();
2820
- if (!normalized) throw new Error(`${action} requires a thread timestamp`);
2821
- return normalized;
2822
- }
2823
- function requireSlackMessageTimestamp(timestamp, action) {
2824
- const normalized = parseSlackMessageTs(timestamp);
2825
- if (!normalized) throw new Error(`${action} requires a target message timestamp`);
2826
- return normalized;
2827
- }
2828
- function requireSlackMessageText(text, action) {
2829
- if (text.trim().length === 0) throw new Error(`${action} requires non-empty text`);
2830
- if (text.length > MAX_SLACK_MESSAGE_TEXT_CHARS) throw new Error(`${action} text exceeds Slack's 40000 character truncation limit`);
2831
- return text;
2832
- }
2833
- /** Resolve a Slack message permalink without making read-only workflows fail on lookup errors. */
2834
- async function getSlackMessagePermalink(args) {
2835
- try {
2836
- return (await withSlackRetries(() => getSlackClient().chat.getPermalink({
2837
- channel: args.channelId,
2838
- message_ts: args.messageTs
2839
- }), 3, {
2840
- action: "chat.getPermalink",
2841
- idempotent: true,
2842
- spanAttributes: {
2843
- "app.slack.channel_id": args.channelId,
2844
- "app.slack.message_ts": args.messageTs
2845
- }
2846
- })).permalink;
2847
- } catch {
2848
- return;
2849
- }
2850
- }
2851
- /** Post Slack `mrkdwn` text to a conversation or thread via the shared outbound boundary. */
2852
- async function postSlackMessage(input) {
2853
- const channelId = requireSlackConversationId(input.channelId, "Slack message posting");
2854
- const text = requireSlackMessageText(input.text, "Slack message posting");
2855
- const entities = input.entities?.length ? slackEntitySchema.array().parse(input.entities) : void 0;
2856
- const threadTs = input.threadTs ? requireSlackThreadTimestamp(input.threadTs, "Slack thread message posting") : void 0;
2857
- const attributes = {
2858
- "app.slack.channel_id": channelId,
2859
- ...threadTs ? { "app.slack.thread_ts": threadTs } : void 0,
2860
- "app.slack.work_object.count": entities?.length ?? 0
2861
- };
2862
- const response = await withSlackRetries(async () => {
2863
- const response = await getSlackClient().chat.postMessage({
2864
- channel: channelId,
2865
- text,
2866
- unfurl_links: false,
2867
- unfurl_media: false,
2868
- ...input.blocks?.length ? { blocks: input.blocks } : void 0,
2869
- ...entities ? { metadata: { entities } } : void 0,
2870
- ...threadTs ? { thread_ts: threadTs } : void 0
2871
- });
2872
- const messageId = parseSlackMessageTs(response.ts);
2873
- setSpanAttributes({ "messaging.message.id": messageId });
2874
- captureSlackPostWarning(response, {
2875
- ...attributes,
2876
- "messaging.message.id": messageId
2877
- });
2878
- return response;
2879
- }, 3, {
2880
- action: "chat.postMessage",
2881
- attributes
2882
- });
2883
- const messageTs = parseSlackMessageTs(response.ts);
2884
- if (!messageTs) throw new Error("Slack message posted without ts");
2885
- return {
2886
- ts: messageTs,
2887
- ...input.includePermalink ? { permalink: await getSlackMessagePermalink({
2888
- channelId,
2889
- messageTs
2890
- }) } : void 0
2891
- };
2892
- }
2893
- /**
2894
- * Post an ephemeral Slack message. Delivery is best-effort on Slack's side, but
2895
- * request validation and Web API behavior are centralized here.
2896
- */
2897
- async function postSlackEphemeralMessage(input) {
2898
- const channelId = requireSlackConversationId(input.channelId, "Slack ephemeral message posting");
2899
- const userId = parseSlackUserId(input.userId);
2900
- if (!userId) throw new Error("Slack ephemeral message posting requires a user ID");
2901
- const text = requireSlackMessageText(input.text, "Slack ephemeral message posting");
2902
- const threadTs = input.threadTs ? requireSlackThreadTimestamp(input.threadTs, "Slack ephemeral thread message posting") : void 0;
2903
- return { messageTs: (await withSlackRetries(() => getSlackClient().chat.postEphemeral({
2904
- channel: channelId,
2905
- user: userId,
2906
- text,
2907
- ...input.blocks?.length ? { blocks: input.blocks } : void 0,
2908
- ...threadTs ? { thread_ts: threadTs } : void 0
2909
- }), 3, {
2910
- action: "chat.postEphemeral",
2911
- spanAttributes: {
2912
- "app.slack.channel_id": channelId,
2913
- "app.slack.user_id": userId,
2914
- ...threadTs ? { "app.slack.thread_ts": threadTs } : void 0
2915
- }
2916
- })).message_ts };
2917
- }
2918
- /** Upload files into a Slack conversation or thread via the shared outbound file boundary. */
2919
- async function uploadFilesToConversation(input) {
2920
- const channelId = requireSlackConversationId(input.channelId, "Slack file upload");
2921
- const threadTs = input.threadTs ? requireSlackThreadTimestamp(input.threadTs, "Slack file upload") : void 0;
2922
- if (input.files.length === 0) throw new Error("Slack file upload requires at least one file");
2923
- const fileUploads = input.files.map((file) => {
2924
- const filename = file.filename.trim();
2925
- if (!filename) throw new Error("Slack file upload requires every file to have a filename");
2926
- return {
2927
- file: file.data,
2928
- filename
2929
- };
2930
- });
2931
- return { files: ((await withSlackRetries(() => getSlackClient().filesUploadV2({
2932
- channel_id: channelId,
2933
- ...threadTs ? { thread_ts: threadTs } : void 0,
2934
- file_uploads: fileUploads
2935
- }), 3, {
2936
- action: "filesUploadV2",
2937
- spanAttributes: {
2938
- "app.slack.channel_id": channelId,
2939
- ...threadTs ? { "app.slack.thread_ts": threadTs } : void 0
2940
- }
2941
- })).files ?? []).flatMap((completion) => (completion.files ?? []).map((file) => file.id ? { id: file.id } : {})) };
2942
- }
2943
- /** Add a reaction to a Slack message, treating `already_reacted` as idempotent success. */
2944
- async function addReactionToMessage(input) {
2945
- const channelId = requireSlackConversationId(input.channelId, "Slack reaction");
2946
- const timestamp = requireSlackMessageTimestamp(input.timestamp, "Slack reaction");
2947
- const emoji = normalizeSlackEmojiName(input.emoji);
2948
- if (!emoji) throw new Error("Slack reaction requires a valid emoji alias name");
2949
- try {
2950
- await withSlackRetries(() => getSlackClient(input.timeoutMs === void 0 ? void 0 : { timeoutMs: input.timeoutMs }).reactions.add({
2951
- channel: channelId,
2952
- timestamp,
2953
- name: emoji
2954
- }), input.timeoutMs === void 0 ? 3 : 1, {
2955
- action: "reactions.add",
2956
- idempotent: true,
2957
- spanAttributes: {
2958
- "app.slack.channel_id": channelId,
2959
- "app.slack.message_ts": timestamp,
2960
- "app.slack.reaction": emoji
2961
- }
2962
- });
2963
- } catch (error) {
2964
- if (error instanceof SlackActionError && error.code === "already_reacted") return { ok: true };
2965
- throw error;
2966
- }
2967
- return { ok: true };
2968
- }
2969
- /** Remove a reaction from a Slack message, treating `no_reaction` as idempotent success. */
2970
- async function removeReactionFromMessage(input) {
2971
- const channelId = requireSlackConversationId(input.channelId, "Slack reaction removal");
2972
- const timestamp = requireSlackMessageTimestamp(input.timestamp, "Slack reaction removal");
2973
- const emoji = normalizeSlackEmojiName(input.emoji);
2974
- if (!emoji) throw new Error("Slack reaction removal requires a valid emoji alias name");
2975
- try {
2976
- await withSlackRetries(() => getSlackClient().reactions.remove({
2977
- channel: channelId,
2978
- timestamp,
2979
- name: emoji
2980
- }), 3, {
2981
- action: "reactions.remove",
2982
- idempotent: true,
2983
- spanAttributes: {
2984
- "app.slack.channel_id": channelId,
2985
- "app.slack.message_ts": timestamp,
2986
- "app.slack.reaction": emoji
2987
- }
2988
- });
2989
- } catch (error) {
2990
- if (error instanceof SlackActionError && error.code === "no_reaction") return { ok: true };
2991
- throw error;
2992
- }
2993
- return { ok: true };
2994
- }
2995
- //#endregion
2996
- //#region src/chat/slack/status-format.ts
2997
- const SLACK_STATUS_MAX_LENGTH = 50;
2998
- /** Truncate a status string to fit Slack's 50-char assistant status limit. */
2999
- function truncateStatusText(text) {
3000
- const trimmed = text.trim();
3001
- if (!trimmed) return "";
3002
- if (trimmed.length <= SLACK_STATUS_MAX_LENGTH) return trimmed;
3003
- return `${trimmed.slice(0, 47).trimEnd()}...`;
3004
- }
3005
- //#endregion
3006
- //#region src/chat/slack/mrkdwn.ts
3007
- /** Escape dynamic text for Slack mrkdwn without changing intended formatting. */
3008
- function escapeSlackMrkdwnText(text) {
3009
- return text.replaceAll("&", "&amp;").replaceAll("<", "&lt;").replaceAll(">", "&gt;");
3010
- }
3011
- /** Escape a URL for Slack explicit link syntax while preserving query semantics. */
3012
- function escapeSlackLinkUrl(url) {
3013
- return url.replaceAll("&", "&amp;").replaceAll("<", "%3C").replaceAll(">", "%3E");
3014
- }
3015
- /** Build a Slack explicit link from dynamic URL and label values. */
3016
- function formatSlackLink(url, label) {
3017
- return `<${escapeSlackLinkUrl(url)}|${escapeSlackMrkdwnText(label)}>`;
3018
- }
3019
- function readInlineCodeSpan(line, start) {
3020
- if (line[start] !== "`") return;
3021
- let n = 1;
3022
- while (line[start + n] === "`") n++;
3023
- const marker = "`".repeat(n);
3024
- let search = start + n;
3025
- while (search < line.length) {
3026
- const close = line.indexOf(marker, search);
3027
- if (close === -1) return;
3028
- const after = close + n;
3029
- if (line[after] !== "`") return {
3030
- text: line.slice(start, after),
3031
- end: after
3032
- };
3033
- search = after + 1;
3034
- }
3035
- }
3036
- function readExistingSlackAngleToken(line, start) {
3037
- if (line[start] !== "<") return;
3038
- const close = line.indexOf(">", start + 1);
3039
- if (close === -1) return;
3040
- const body = line.slice(start + 1, close);
3041
- if (/^(?:https?:\/\/|@|#|!)/.test(body)) return {
3042
- text: line.slice(start, close + 1),
3043
- end: close + 1
3044
- };
3045
- }
3046
- function readMarkdownLink(line, start) {
3047
- if (line[start] !== "[") return;
3048
- const labelEnd = line.indexOf("](", start + 1);
3049
- if (labelEnd === -1) return;
3050
- const destStart = labelEnd + 2;
3051
- if (!line.startsWith("http://", destStart) && !line.startsWith("https://", destStart)) return;
3052
- const closeParens = line.indexOf(")", destStart);
3053
- if (closeParens === -1) return;
3054
- return {
3055
- text: line.slice(start, closeParens + 1),
3056
- end: closeParens + 1
3057
- };
3058
- }
3059
- function hasUnmatchedClosingParen(text) {
3060
- let balance = 0;
3061
- for (const ch of text) if (ch === "(") balance++;
3062
- else if (ch === ")") balance--;
3063
- return balance < 0;
3064
- }
3065
- function readBareUrl(line, start) {
3066
- let end = start;
3067
- while (end < line.length) {
3068
- const ch = line[end];
3069
- if (/\s/.test(ch) || ch === "<" || ch === ">" || ch === "\"" || ch === "`" || ch === "|" || ch === "*") break;
3070
- end++;
3071
- }
3072
- if (end === start) return;
3073
- let raw = line.slice(start, end);
3074
- let suffix = "";
3075
- const peel = () => {
3076
- suffix = raw.slice(-1) + suffix;
3077
- raw = raw.slice(0, -1);
3078
- };
3079
- const shouldPeel = () => raw.endsWith("_") || /[.,!?;:]$/.test(raw) || raw.endsWith(")") && hasUnmatchedClosingParen(raw);
3080
- while (raw.length > 0 && shouldPeel()) peel();
3081
- if (!/^https?:\/\/.+/.test(raw)) return;
3082
- return {
3083
- url: raw,
3084
- suffix,
3085
- end
3086
- };
3087
- }
3088
- /** Wrap bare http(s) URLs on a single line as Slack explicit `<url>` links. */
3089
- function wrapBareUrlsOnLine(line) {
3090
- let result = "";
3091
- let i = 0;
3092
- while (i < line.length) {
3093
- const codeSpan = readInlineCodeSpan(line, i);
3094
- if (codeSpan) {
3095
- result += codeSpan.text;
3096
- i = codeSpan.end;
3097
- continue;
3098
- }
3099
- const angleToken = readExistingSlackAngleToken(line, i);
3100
- if (angleToken) {
3101
- result += angleToken.text;
3102
- i = angleToken.end;
3103
- continue;
3104
- }
3105
- const mdLink = readMarkdownLink(line, i);
3106
- if (mdLink) {
3107
- result += mdLink.text;
3108
- i = mdLink.end;
3109
- continue;
3110
- }
3111
- if (line.startsWith("https://", i) || line.startsWith("http://", i)) {
3112
- const parsed = readBareUrl(line, i);
3113
- if (parsed) {
3114
- result += `<${parsed.url}>${parsed.suffix}`;
3115
- i = parsed.end;
3116
- continue;
3117
- }
3118
- }
3119
- result += line[i];
3120
- i++;
3121
- }
3122
- return result;
3123
- }
3124
- /**
3125
- * Pre-wrap bare http(s) URLs outside fenced code blocks as Slack explicit
3126
- * links, preventing Slack's auto-linker from consuming adjacent formatting
3127
- * markers into the URL.
3128
- *
3129
- * Uses the same fence-toggle rule as `ensureBlockSpacing` so both passes
3130
- * agree on which lines are code.
3131
- */
3132
- function wrapBareUrls(text) {
3133
- const lines = text.split("\n");
3134
- const out = [];
3135
- let inCodeBlock = false;
3136
- for (const line of lines) {
3137
- if (line.trimStart().startsWith("```")) {
3138
- inCodeBlock = !inCodeBlock;
3139
- out.push(line);
3140
- continue;
3141
- }
3142
- out.push(inCodeBlock ? line : wrapBareUrlsOnLine(line));
3143
- }
3144
- return out.join("\n");
3145
- }
3146
- /** Insert blank lines between content blocks so Slack renders them with visual separation. */
3147
- function ensureBlockSpacing(text) {
3148
- const codeBlockPattern = /^```/;
3149
- const listItemPattern = /^[-*•]\s|^\d+\.\s/;
3150
- const lines = text.split("\n");
3151
- const result = [];
3152
- let inCodeBlock = false;
3153
- for (let i = 0; i < lines.length; i++) {
3154
- const line = lines[i];
3155
- if (codeBlockPattern.test(line.trimStart())) {
3156
- if (!inCodeBlock) {
3157
- const prev = result.length > 0 ? result[result.length - 1] : void 0;
3158
- if (prev !== void 0 && prev.trim() !== "") result.push("");
3159
- }
3160
- inCodeBlock = !inCodeBlock;
3161
- result.push(line);
3162
- continue;
3163
- }
3164
- if (inCodeBlock) {
3165
- result.push(line);
3166
- continue;
3167
- }
3168
- const prev = result.length > 0 ? result[result.length - 1] : void 0;
3169
- if (prev !== void 0 && prev.trim() !== "" && line.trim() !== "" && !(listItemPattern.test(prev.trimStart()) && listItemPattern.test(line.trimStart()))) result.push("");
3170
- result.push(line);
3171
- }
3172
- return result.join("\n");
3173
- }
3174
- /**
3175
- * Normalize model-authored Slack markdown for delivery via `markdown_text`
3176
- * or `{ type: "markdown" }` blocks.
3177
- *
3178
- * Plugins may first rewrite ordinary Markdown. Core then applies Slack-only
3179
- * delivery shaping: bare URL wrapping and block spacing. Slack reply delivery
3180
- * owns chunking and continuation markers separately.
3181
- */
3182
- function normalizeSlackReplyMarkdown(text) {
3183
- let normalized = applyPluginFormatMarkdown(text).replace(/\r\n?/g, "\n").replace(/[ \t]+$/gm, "");
3184
- normalized = wrapBareUrls(normalized);
3185
- normalized = ensureBlockSpacing(normalized);
3186
- return normalized.replace(/\n{3,}/g, "\n\n").trim();
3187
- }
3188
- /** Normalize assistant status text before handing it to Slack. */
3189
- function normalizeSlackStatusText(text) {
3190
- const trimmed = text.trim();
3191
- if (!trimmed) return "";
3192
- return truncateStatusText(trimmed.replace(/(?:\.\s*)+$/, "").trim());
3193
- }
3194
- /** Render a Slack user mention, which Slack shows as the person's name. */
3195
- function slackMention(userId) {
3196
- return `<@${userId}>`;
3197
- }
3198
- //#endregion
3199
2823
  //#region src/chat/slack/oauth-authorization-message.ts
3200
2824
  const ACCOUNT_SCOPE_NOTE = "This connects only your account. Junior sees only what you can see.";
3201
2825
  /** Present private OAuth authorization with a clear action and text fallback. */
@@ -4064,10 +3688,13 @@ async function maybePostAutomatedTurnLimitNotice(args) {
4064
3688
  }
4065
3689
  //#endregion
4066
3690
  //#region src/chat/task-input.ts
4067
- /** Shared closing lines for the stored outcomes. */
3691
+ /**
3692
+ * Shared closing lines. Automations pass outcomes and end with a declared
3693
+ * result. Watches pass no outcomes and reply in the Conversation.
3694
+ */
4068
3695
  function replyContractLines(outcomes) {
4069
- if (outcomes?.length === 0) return ["Do the work without writing a status message.", "No successful output will be delivered."];
4070
- return ["When you reply, follow any reply format in the instructions.", "Briefly report what you did or what is needed next."];
3696
+ if (!outcomes) return ["When you reply, follow any reply format in the instructions.", "Briefly report what you did or what is needed next."];
3697
+ return [`End with \`${FINISH_AUTOMATION_RUN_TOOL_NAME}\`.`];
4071
3698
  }
4072
3699
  function oneLine(value) {
4073
3700
  return value.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
@@ -4092,7 +3719,7 @@ function renderTaskInput(args) {
4092
3719
  "This is a task, not a message from a person.",
4093
3720
  "",
4094
3721
  ...about ? [`About: ${oneLine(about)}`] : [],
4095
- ...creator ? [`Created by: ${oneLine(creator)}. Where the instructions say "me" or "my", write this mention.`] : [],
3722
+ ...creator ? [`Created by: ${oneLine(creator)}. "Me" and "my" in the instructions mean this person: mention them in the message. If the instructions do not say "me" or "my", do not mention this person.`] : [],
4096
3723
  `Instructions: ${instructions}`
4097
3724
  ];
4098
3725
  const guidance = args.guidance?.trim();
@@ -10089,6 +9716,44 @@ function createSpawnAgentTool(spawnAgent) {
10089
9716
  });
10090
9717
  }
10091
9718
  //#endregion
9719
+ //#region src/chat/tools/runtime/finish-automation-run.ts
9720
+ const RESULT_GUIDANCE = ["Use `no_action` when the run worked and nothing should be posted, such as when a condition in the instruction is not met, or when a temporary failure stopped the work.", "Use `misconfigured` when the run cannot do its job until its creator changes the automation: a provider, tool, account, or access that the job needs is missing, a target no longer exists, or the instruction cannot be done. Do not use it for a temporary failure or a condition that is not met."].join(" ");
9721
+ /** Create the tool that ends one Automation run with a declared result. */
9722
+ function createFinishAutomationRunTool(options) {
9723
+ const results = options.sendsMessage ? [
9724
+ "send_message",
9725
+ "no_action",
9726
+ "misconfigured"
9727
+ ] : ["no_action", "misconfigured"];
9728
+ return zodTool({
9729
+ annotations: {
9730
+ destructiveHint: false,
9731
+ idempotentHint: true,
9732
+ openWorldHint: false,
9733
+ readOnlyHint: false
9734
+ },
9735
+ description: options.sendsMessage ? `End this automation run. Use \`send_message\` with the finished reminder, digest, alert, or answer; the runtime posts it to the stored destinations. ${RESULT_GUIDANCE}` : `End this automation run. This automation posts nothing. ${RESULT_GUIDANCE}`,
9736
+ inputSchema: z.object({
9737
+ result: z.enum(results),
9738
+ ...options.sendsMessage ? { message: z.string().optional().describe("Required for send_message. The text to post.") } : void 0,
9739
+ reason: z.string().optional().describe("Required for no_action and misconfigured. One sentence for the creator.")
9740
+ }),
9741
+ outputSchema: juniorToolOutputSchema.extend({ result: z.enum([
9742
+ "send_message",
9743
+ "no_action",
9744
+ "misconfigured"
9745
+ ]) }),
9746
+ execute: async (input) => {
9747
+ const parsed = automationResultSchema.safeParse(input);
9748
+ if (!parsed.success) throw new ToolInputError(input.result === "send_message" ? "send_message requires a non-empty message." : `${input.result} requires a non-empty reason.`);
9749
+ return parsed.data.result === "send_message" && isNoReplyMarker(parsed.data.message) ? {
9750
+ result: "no_action",
9751
+ reason: "The message was the no-reply marker."
9752
+ } : parsed.data;
9753
+ }
9754
+ });
9755
+ }
9756
+ //#endregion
10092
9757
  //#region src/chat/tools/list-watches.ts
10093
9758
  const listedResourceWatchSchema = z.object({
10094
9759
  id: z.string().min(1),
@@ -10433,305 +10098,6 @@ function requireRequesterMention(instruction, actor) {
10433
10098
  if (namesPerson(instruction, actor.fullName)) throw new ToolInputError(`The instruction names ${actor.fullName} without a Slack mention. Write ${mention} for this person.`);
10434
10099
  }
10435
10100
  //#endregion
10436
- //#region src/chat/automations/revision.ts
10437
- /** Storage revisions prevent stale edits from replacing newer values. */
10438
- var AutomationConflictError = class extends ToolInputError {
10439
- constructor() {
10440
- super("This Automation changed after it was loaded. Reload it before saving.");
10441
- this.name = "AutomationConflictError";
10442
- }
10443
- };
10444
- /** Sort object keys so equal values produce equal JSON. */
10445
- function canonicalJson$1(value) {
10446
- if (Array.isArray(value)) return value.map(canonicalJson$1);
10447
- if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, child]) => [key, canonicalJson$1(child)]));
10448
- return value;
10449
- }
10450
- /** Identify the stored values being edited, including lifecycle and creator authority. */
10451
- function automationRevision(task) {
10452
- return createHash("sha256").update(JSON.stringify(canonicalJson$1(task))).digest("hex");
10453
- }
10454
- /** Reject a stale edit while the storage owner holds the write lock. */
10455
- function requireAutomationRevision(task, revision) {
10456
- if (!task || automationRevision(task) !== revision) throw new AutomationConflictError();
10457
- }
10458
- //#endregion
10459
- //#region src/chat/automations/versions.ts
10460
- /** Automation versions keep each saved definition so people can see how it changed. */
10461
- /** Return the versioned part of an Automation. Lifecycle and run state are excluded. */
10462
- function automationDefinition(task) {
10463
- const common = {
10464
- title: task.title?.trim() || null,
10465
- instruction: task.task.text,
10466
- credentialMode: task.credentialMode,
10467
- destination: task.destination,
10468
- outcomes: task.outcomes
10469
- };
10470
- return "schedule" in task ? {
10471
- ...common,
10472
- schedule: task.schedule
10473
- } : {
10474
- ...common,
10475
- trigger: task.trigger
10476
- };
10477
- }
10478
- /** Compare definition values after key sorting. */
10479
- function sameDefinitionValue(left, right) {
10480
- return JSON.stringify(canonicalJson$1(left)) === JSON.stringify(canonicalJson$1(right));
10481
- }
10482
- /**
10483
- * Save a version when the definition changed. Call this in the same
10484
- * transaction and lock as the Automation write. Without `current`, the
10485
- * Automation is new, so the creator saves version 1.
10486
- */
10487
- async function recordAutomationVersion(db, kind, task, current, editedBy) {
10488
- const definition = automationDefinition(task);
10489
- if (current && sameDefinitionValue(automationDefinition(current), definition)) return;
10490
- await db.insert(juniorAutomationVersions).values({
10491
- kind,
10492
- automationId: task.id,
10493
- version: sql`(
10494
- SELECT coalesce(max(${juniorAutomationVersions.version}), 0) + 1
10495
- FROM ${juniorAutomationVersions}
10496
- WHERE ${juniorAutomationVersions.kind} = ${kind}
10497
- AND ${juniorAutomationVersions.automationId} = ${task.id}
10498
- )`,
10499
- createdAtMs: Date.now(),
10500
- editedBy: editedBy ?? (current ? null : task.createdBy),
10501
- definition
10502
- });
10503
- }
10504
- function parseVersionRow(kind, row) {
10505
- return automationVersionSchema.parse({
10506
- kind,
10507
- version: row.version,
10508
- createdAt: new Date(row.createdAtMs).toISOString(),
10509
- editedBy: row.editedBy,
10510
- definition: row.definition
10511
- });
10512
- }
10513
- /** Read one saved definition. */
10514
- async function readAutomationVersion(db, kind, automationId, version) {
10515
- const [row] = await db.select().from(juniorAutomationVersions).where(and(eq(juniorAutomationVersions.kind, kind), eq(juniorAutomationVersions.automationId, automationId), eq(juniorAutomationVersions.version, version))).limit(1);
10516
- return row ? parseVersionRow(kind, row) : void 0;
10517
- }
10518
- /** Read saved definitions, newest first. */
10519
- async function listAutomationVersions(db, kind, automationId, limit) {
10520
- return (await db.select().from(juniorAutomationVersions).where(and(eq(juniorAutomationVersions.kind, kind), eq(juniorAutomationVersions.automationId, automationId))).orderBy(desc(juniorAutomationVersions.version)).limit(limit)).map((row) => parseVersionRow(kind, row));
10521
- }
10522
- //#endregion
10523
- //#region src/chat/event-automations/store.ts
10524
- const retainedEventAutomationSchema = eventAutomationSchema.extend({
10525
- destination: slackDestinationSchema,
10526
- outcomes: z.array(taskOutcomeSchema).max(5)
10527
- });
10528
- /** JSON task payload must not carry SQL-backed columns. */
10529
- function eventAutomationJsonPayload(task) {
10530
- const { status: _status, title: _title, ...payload } = task;
10531
- return payload;
10532
- }
10533
- /** Decode a retained event Automation with its SQL-backed fields. */
10534
- function parseEventAutomationRow(row) {
10535
- const raw = row.task && typeof row.task === "object" ? { ...row.task } : row.task;
10536
- if (raw && typeof raw === "object" && "title" in raw) delete raw.title;
10537
- if (raw && typeof raw === "object" && !("outcomes" in raw) && "destination" in raw) raw.outcomes = [{
10538
- action: "send_message",
10539
- destination: raw.destination
10540
- }];
10541
- const retained = retainedEventAutomationSchema.parse(raw);
10542
- const { threadTs: _threadTs, ...destination } = retained.destination;
10543
- const title = row.title?.trim();
10544
- return {
10545
- ...retained,
10546
- destination,
10547
- outcomes: retained.outcomes.map((outcome) => {
10548
- const { threadTs: _threadTs, ...destination } = outcome.destination;
10549
- return {
10550
- ...outcome,
10551
- destination
10552
- };
10553
- }),
10554
- status: row.status ?? "active",
10555
- ...title ? { title } : void 0
10556
- };
10557
- }
10558
- function activeEventAutomationWhere() {
10559
- return ne(juniorEventAutomations.status, "deleted");
10560
- }
10561
- /** Read one event automation by id, including deleted rows when present. */
10562
- async function getEventAutomation(db, id) {
10563
- const rows = await db.select({
10564
- status: juniorEventAutomations.status,
10565
- task: juniorEventAutomations.task,
10566
- title: juniorEventAutomations.title
10567
- }).from(juniorEventAutomations).where(eq(juniorEventAutomations.id, id)).limit(1);
10568
- return rows[0] ? parseEventAutomationRow(rows[0]) : void 0;
10569
- }
10570
- /** Create one retry-stable event automation, or revive a deleted row with the new payload. */
10571
- async function createEventAutomation(db, task) {
10572
- const parsed = eventAutomationSchema.parse(eventAutomationJsonPayload(task));
10573
- const title = task.title?.trim() || null;
10574
- return db.transaction(async (tx) => {
10575
- const inserted = await tx.insert(juniorEventAutomations).values({
10576
- id: parsed.id,
10577
- teamId: parsed.destination.teamId,
10578
- namespace: parsed.trigger.namespace,
10579
- identifier: parsed.trigger.identifier,
10580
- createdAtMs: parsed.createdAtMs,
10581
- status: "active",
10582
- title,
10583
- task: parsed
10584
- }).onConflictDoNothing().returning({
10585
- status: juniorEventAutomations.status,
10586
- task: juniorEventAutomations.task,
10587
- title: juniorEventAutomations.title
10588
- });
10589
- if (inserted[0]) {
10590
- const created = parseEventAutomationRow(inserted[0]);
10591
- await recordAutomationVersion(tx, "event", created, void 0);
10592
- return created;
10593
- }
10594
- const existing = await getEventAutomation(tx, parsed.id);
10595
- if (!existing) return {
10596
- ...parsed,
10597
- status: "active",
10598
- ...title ? { title } : void 0
10599
- };
10600
- if (existing.status !== "deleted") return existing;
10601
- const rows = await tx.update(juniorEventAutomations).set({
10602
- teamId: parsed.destination.teamId,
10603
- namespace: parsed.trigger.namespace,
10604
- identifier: parsed.trigger.identifier,
10605
- status: "active",
10606
- title,
10607
- task: parsed
10608
- }).where(and(eq(juniorEventAutomations.id, parsed.id), eq(juniorEventAutomations.status, "deleted"))).returning({
10609
- status: juniorEventAutomations.status,
10610
- task: juniorEventAutomations.task,
10611
- title: juniorEventAutomations.title
10612
- });
10613
- if (!rows[0]) return await getEventAutomation(tx, parsed.id) ?? {
10614
- ...parsed,
10615
- status: "active",
10616
- ...title ? { title } : void 0
10617
- };
10618
- const revived = parseEventAutomationRow(rows[0]);
10619
- await recordAutomationVersion(tx, "event", revived, void 0);
10620
- return revived;
10621
- });
10622
- }
10623
- /**
10624
- * Replace an existing non-deleted event automation under a row lock.
10625
- * Pass `editedBy` when a person changes the definition so its version names them.
10626
- */
10627
- async function saveEventAutomation(db, task, expectedRevision, editedBy) {
10628
- const parsed = eventAutomationSchema.parse(eventAutomationJsonPayload(task));
10629
- const title = task.title?.trim() || null;
10630
- return db.transaction(async (tx) => {
10631
- const rows = await tx.select({
10632
- status: juniorEventAutomations.status,
10633
- task: juniorEventAutomations.task,
10634
- title: juniorEventAutomations.title
10635
- }).from(juniorEventAutomations).where(eq(juniorEventAutomations.id, parsed.id)).for("update");
10636
- const current = rows[0] ? parseEventAutomationRow(rows[0]) : void 0;
10637
- if (expectedRevision !== void 0) requireAutomationRevision(current, expectedRevision);
10638
- if (!current || current.status === "deleted") return void 0;
10639
- const saved = parseEventAutomationRow((await tx.update(juniorEventAutomations).set({
10640
- namespace: parsed.trigger.namespace,
10641
- identifier: parsed.trigger.identifier,
10642
- title,
10643
- task: parsed
10644
- }).where(eq(juniorEventAutomations.id, parsed.id)).returning({
10645
- status: juniorEventAutomations.status,
10646
- task: juniorEventAutomations.task,
10647
- title: juniorEventAutomations.title
10648
- }))[0]);
10649
- await recordAutomationVersion(tx, "event", saved, current, editedBy);
10650
- return saved;
10651
- });
10652
- }
10653
- /** Mark one existing event automation deleted while retaining the row for history. */
10654
- async function deleteEventAutomation(db, id) {
10655
- const rows = await db.update(juniorEventAutomations).set({ status: "deleted" }).where(and(eq(juniorEventAutomations.id, id), activeEventAutomationWhere())).returning({
10656
- status: juniorEventAutomations.status,
10657
- task: juniorEventAutomations.task,
10658
- title: juniorEventAutomations.title
10659
- });
10660
- return rows[0] ? parseEventAutomationRow(rows[0]) : void 0;
10661
- }
10662
- /** List live event automations in one Slack workspace. */
10663
- async function listEventAutomationsForTeam(db, teamId) {
10664
- return (await db.select({
10665
- status: juniorEventAutomations.status,
10666
- task: juniorEventAutomations.task,
10667
- title: juniorEventAutomations.title
10668
- }).from(juniorEventAutomations).where(and(eq(juniorEventAutomations.teamId, teamId), activeEventAutomationWhere())).orderBy(asc(juniorEventAutomations.createdAtMs), asc(juniorEventAutomations.id))).map(parseEventAutomationRow);
10669
- }
10670
- function viewerSlackIdentities(user) {
10671
- return user.identities.filter((identity) => identity.provider === "slack" && Boolean(identity.providerTenantId) && Boolean(identity.providerSubjectId));
10672
- }
10673
- /** Return whether one event automation was created by a viewer-linked Slack identity. */
10674
- function eventAutomationBelongsToUser(task, user) {
10675
- return viewerSlackIdentities(user).some((identity) => identity.providerTenantId === task.destination.teamId && identity.providerSubjectId === task.createdBy.slackUserId);
10676
- }
10677
- /**
10678
- * List deleted event automations created by one user, newest first.
10679
- * Used only to keep historical runs after the live automation list drops the row.
10680
- */
10681
- async function listDeletedEventAutomationsCreatedBy(db, user, limit) {
10682
- const identities = viewerSlackIdentities(user);
10683
- const ownership = or(...identities.map((identity) => and(eq(juniorEventAutomations.teamId, identity.providerTenantId), sql`${juniorEventAutomations.task}->'createdBy'->>'slackUserId' = ${identity.providerSubjectId}`)));
10684
- if (!ownership) return [];
10685
- return (await db.select({
10686
- status: juniorEventAutomations.status,
10687
- task: juniorEventAutomations.task,
10688
- title: juniorEventAutomations.title
10689
- }).from(juniorEventAutomations).where(and(ownership, eq(juniorEventAutomations.status, "deleted"))).orderBy(desc(juniorEventAutomations.createdAtMs), desc(juniorEventAutomations.id)).limit(limit)).map(parseEventAutomationRow);
10690
- }
10691
- /**
10692
- * Collect match keys from live event automations for these identifiers and event types.
10693
- * Does not evaluate match values — only reports keys that filters use.
10694
- */
10695
- async function collectEventAutomationMatchKeys(db, input) {
10696
- const eventTypes = new Set(input.eventTypes.map((eventType) => eventType.trim()).filter(Boolean));
10697
- const identifiers = [...new Set(input.identifiers.map((value) => value.trim()).filter(Boolean))];
10698
- if (eventTypes.size === 0 || identifiers.length === 0) return [];
10699
- const rows = await db.select({
10700
- status: juniorEventAutomations.status,
10701
- task: juniorEventAutomations.task,
10702
- title: juniorEventAutomations.title
10703
- }).from(juniorEventAutomations).where(and(eq(juniorEventAutomations.teamId, input.teamId), eq(juniorEventAutomations.namespace, input.namespace), inArray(juniorEventAutomations.identifier, identifiers), eq(juniorEventAutomations.status, "active"))).orderBy(asc(juniorEventAutomations.createdAtMs), asc(juniorEventAutomations.id));
10704
- const keys = /* @__PURE__ */ new Set();
10705
- for (const row of rows) {
10706
- const task = parseEventAutomationRow(row);
10707
- if (!task.trigger.events.some((eventType) => eventTypes.has(eventType))) continue;
10708
- for (const key of Object.keys(task.trigger.match ?? {})) keys.add(key);
10709
- }
10710
- return [...keys].sort();
10711
- }
10712
- /** Find every live task matching one normalized event. */
10713
- async function findMatchingEventAutomations(db, event, teamId) {
10714
- return (await db.select({
10715
- status: juniorEventAutomations.status,
10716
- task: juniorEventAutomations.task,
10717
- title: juniorEventAutomations.title
10718
- }).from(juniorEventAutomations).where(and(eq(juniorEventAutomations.teamId, teamId), eq(juniorEventAutomations.namespace, event.namespace), eq(juniorEventAutomations.identifier, event.identifier), eq(juniorEventAutomations.status, "active"))).orderBy(asc(juniorEventAutomations.createdAtMs), asc(juniorEventAutomations.id))).map(parseEventAutomationRow).filter((task) => task.trigger.events.includes(event.eventType) && eventMatches(task.trigger.match, event.data));
10719
- }
10720
- /** Change lifecycle under the same row lock as edits, without changing credentials. */
10721
- async function setEventAutomationStatus(db, id, status, revision) {
10722
- return db.transaction(async (tx) => {
10723
- const rows = await tx.select().from(juniorEventAutomations).where(eq(juniorEventAutomations.id, id)).for("update");
10724
- const current = rows[0] ? parseEventAutomationRow(rows[0]) : void 0;
10725
- requireAutomationRevision(current, revision);
10726
- if (!current || current.status === "deleted") throw new Error("Automation no longer exists.");
10727
- await tx.update(juniorEventAutomations).set({ status }).where(eq(juniorEventAutomations.id, id));
10728
- return {
10729
- ...current,
10730
- status
10731
- };
10732
- });
10733
- }
10734
- //#endregion
10735
10101
  //#region src/chat/task-outcomes.ts
10736
10102
  /** Resolve explicit message outcomes to the Slack Destinations stored on an Automation. */
10737
10103
  async function resolveTaskOutcomes(outcomes, currentDestination, creatorSlackUserId) {
@@ -10745,9 +10111,7 @@ async function resolveTaskOutcomes(outcomes, currentDestination, creatorSlackUse
10745
10111
  });
10746
10112
  continue;
10747
10113
  }
10748
- const userId = creatorSlackUserId;
10749
- const channelId = (await withSlackRetries(() => getSlackClient().conversations.open({ users: userId }), 3, { action: "conversations.open" })).channel?.id;
10750
- if (!channelId) throw new Error("Slack did not return a direct message destination.");
10114
+ const channelId = await openSlackDirectMessage(creatorSlackUserId);
10751
10115
  resolved.push({
10752
10116
  action: "send_message",
10753
10117
  destination: {
@@ -10782,9 +10146,11 @@ const compactEventAutomationResultSchema = z.object({
10782
10146
  id: z.string().min(1),
10783
10147
  status: z.enum([
10784
10148
  "active",
10149
+ "blocked",
10785
10150
  "paused",
10786
10151
  "deleted"
10787
10152
  ]),
10153
+ statusReason: z.string().nullable(),
10788
10154
  title: z.string().min(1).nullable(),
10789
10155
  dashboardUrl: z.string().url().nullable(),
10790
10156
  instruction: z.string().min(1),
@@ -10885,6 +10251,7 @@ function compactEventAutomation(task, catalog, requesterSlackUserId) {
10885
10251
  return compactEventAutomationResultSchema.parse({
10886
10252
  id: task.id,
10887
10253
  status: task.status,
10254
+ statusReason: task.statusReason ?? null,
10888
10255
  title: task.title?.trim() || null,
10889
10256
  dashboardUrl: getDashboardTaskLink(task.id) ?? null,
10890
10257
  instruction: task.task.text,
@@ -11173,23 +10540,25 @@ function createUpdateEventAutomationTool(context, catalog) {
11173
10540
  readOnlyHint: false
11174
10541
  },
11175
10542
  executionMode: "sequential",
11176
- description: "Update the instruction, registered trigger, or credential use for an event automation.",
10543
+ description: "Update the instruction, registered trigger, credential use, or status of an event automation.",
11177
10544
  inputSchema: z.object({
11178
10545
  automationId: z.string().min(1),
11179
10546
  title: automationTitleSchema.optional(),
11180
10547
  instruction: automationInstructionToolSchema.nullable().optional(),
11181
10548
  trigger: registeredEventAutomationTriggerSchema(catalog).nullable().optional(),
11182
10549
  outcomes: z.array(taskOutcomeInputSchema).max(5).nullable().describe("Replacement messages to send after successful work. Use an empty list to send nothing. Omit or use null to leave unchanged.").optional(),
11183
- credentialMode: z.enum(["system", "creator"]).nullable().describe("Set creator to make the task's original creator credentials available, or system to disable them. Creator always means the task's createdBy actor, never the current requester. Only that original creator may enable creator mode. Omit or use null to leave unchanged.").optional()
10550
+ credentialMode: z.enum(["system", "creator"]).nullable().describe("Set creator to make the task's original creator credentials available, or system to disable them. Creator always means the task's createdBy actor, never the current requester. Only that original creator may enable creator mode. Omit or use null to leave unchanged.").optional(),
10551
+ status: z.enum(["active", "paused"]).nullable().describe("Set active to resume a paused or blocked automation, or paused to stop it. Only the creator can change the status. Omit or use null to leave unchanged.").optional()
11184
10552
  }).strict(),
11185
10553
  prepareArguments(args) {
11186
- const { credentialMode, outcomes, instruction, trigger, ...prepared } = args;
10554
+ const { credentialMode, outcomes, instruction, trigger, status, ...prepared } = args;
11187
10555
  return {
11188
10556
  ...prepared,
11189
10557
  ...instruction != null ? { instruction } : void 0,
11190
10558
  ...trigger != null ? { trigger } : void 0,
11191
10559
  ...outcomes != null ? { outcomes } : void 0,
11192
- ...credentialMode != null ? { credentialMode } : void 0
10560
+ ...credentialMode != null ? { credentialMode } : void 0,
10561
+ ...status != null ? { status } : void 0
11193
10562
  };
11194
10563
  },
11195
10564
  outputSchema: eventAutomationToolResultSchema,
@@ -11197,21 +10566,28 @@ function createUpdateEventAutomationTool(context, catalog) {
11197
10566
  const current = await writableEventAutomation(context, input.automationId);
11198
10567
  const { actor } = requireEventAutomationSlackContext(context);
11199
10568
  const isCreator = actor.userId === current.createdBy.slackUserId;
11200
- if (input.title === void 0 && input.instruction === void 0 && input.trigger === void 0 && input.outcomes == null && input.credentialMode == null) throw new ToolInputError("Event automation update requires a change.");
10569
+ const edits = input.title !== void 0 || input.instruction !== void 0 || input.trigger !== void 0 || input.outcomes != null || input.credentialMode != null;
10570
+ if (!edits && input.status == null) throw new ToolInputError("Event automation update requires a change.");
10571
+ if (input.status != null && !isCreator) throw new ToolInputError("Only the creator of this event automation can pause or resume it.");
11201
10572
  if (input.instruction != null && input.instruction !== current.task.text) requireRequesterMention(input.instruction, actor);
11202
- const next = await editEventAutomation(current, {
11203
- title: input.title,
11204
- instruction: input.instruction ?? void 0,
11205
- trigger: input.trigger ?? void 0,
11206
- outcomes: input.outcomes ?? void 0,
11207
- credentialMode: input.credentialMode ?? void 0
11208
- }, isCreator, catalog);
11209
- const saved = await saveEventAutomation(getDb(), next, automationRevision(current), {
11210
- slackUserId: actor.userId,
11211
- ...actor.fullName ? { fullName: actor.fullName } : void 0,
11212
- ...actor.userName ? { userName: actor.userName } : void 0
11213
- });
11214
- if (!saved) throw new ToolInputError("Event automation was not found.");
10573
+ let saved = current;
10574
+ if (edits) {
10575
+ const next = await editEventAutomation(current, {
10576
+ title: input.title,
10577
+ instruction: input.instruction ?? void 0,
10578
+ trigger: input.trigger ?? void 0,
10579
+ outcomes: input.outcomes ?? void 0,
10580
+ credentialMode: input.credentialMode ?? void 0
10581
+ }, isCreator, catalog);
10582
+ const edited = await saveEventAutomation(getDb(), next, automationRevision(current), {
10583
+ slackUserId: actor.userId,
10584
+ ...actor.fullName ? { fullName: actor.fullName } : void 0,
10585
+ ...actor.userName ? { userName: actor.userName } : void 0
10586
+ });
10587
+ if (!edited) throw new ToolInputError("Event automation was not found.");
10588
+ saved = edited;
10589
+ }
10590
+ if (input.status != null) saved = await setEventAutomationStatus(getDb(), saved.id, input.status, automationRevision(saved));
11215
10591
  return eventAutomationToolResult(context.conversationId, saved, catalog, actor.userId);
11216
10592
  }
11217
10593
  });
@@ -11229,193 +10605,6 @@ function createEventAutomationTools(context, catalog) {
11229
10605
  };
11230
10606
  }
11231
10607
  //#endregion
11232
- //#region src/chat/scheduled-automations/tasks.ts
11233
- const SCHEDULER_KEY_PREFIX = "junior:scheduler";
11234
- const retainedScheduledAutomationSchema = scheduledAutomationSchema.omit({
11235
- creatorIdentityId: true,
11236
- title: true
11237
- }).extend({
11238
- destination: slackDestinationSchema,
11239
- outcomes: z.array(taskOutcomeSchema).max(5),
11240
- version: z.number().optional()
11241
- }).strict();
11242
- /** Decode a retained scheduler task row and reject invalid routing context. */
11243
- function parseScheduledAutomationRow(row) {
11244
- const record = z.record(z.string(), z.unknown()).safeParse(row.record);
11245
- if (!record.success) return void 0;
11246
- const { creatorIdentityId: legacyCreatorIdentityId, title: legacyTitle, ...retained } = record.data;
11247
- const current = retained.outcomes === void 0 && retained.destination !== void 0 ? {
11248
- ...retained,
11249
- outcomes: [{
11250
- action: "send_message",
11251
- destination: retained.destination
11252
- }]
11253
- } : retained;
11254
- const parsed = retainedScheduledAutomationSchema.safeParse(current);
11255
- if (!parsed.success) return void 0;
11256
- const { status, version: _version, ...retainedTask } = parsed.data;
11257
- const destination = {
11258
- platform: "slack",
11259
- teamId: retainedTask.destination.teamId,
11260
- channelId: retainedTask.destination.channelId
11261
- };
11262
- const task = {
11263
- ...retainedTask,
11264
- destination,
11265
- outcomes: retainedTask.outcomes.map((outcome) => ({
11266
- ...outcome,
11267
- destination: {
11268
- platform: "slack",
11269
- teamId: outcome.destination.teamId,
11270
- channelId: outcome.destination.channelId
11271
- }
11272
- }))
11273
- };
11274
- const fallbackIdentity = row.creatorIdentityId === null ? z.string().safeParse(legacyCreatorIdentityId) : void 0;
11275
- const creatorIdentityId = row.creatorIdentityId ?? (fallbackIdentity?.success ? fallbackIdentity.data : void 0);
11276
- if (creatorIdentityId === void 0) return void 0;
11277
- const fallbackTitle = row.title === null ? z.string().safeParse(legacyTitle) : void 0;
11278
- const title = (row.title !== null ? row.title : fallbackTitle?.success ? fallbackTitle.data : void 0)?.trim() || void 0;
11279
- return {
11280
- ...task,
11281
- creatorIdentityId,
11282
- status,
11283
- ...title ? { title } : void 0
11284
- };
11285
- }
11286
- /** Keep scan paths from returning current or legacy tombstones. */
11287
- function isListedScheduledAutomation(task) {
11288
- return task !== void 0 && task.status !== "deleted";
11289
- }
11290
- /** Build the stable run id for one task occurrence. */
11291
- function scheduledRunId(taskId, scheduledForMs) {
11292
- return `${taskId}:${scheduledForMs}`;
11293
- }
11294
- /** Acquire the deployed advisory lock for one scheduled automation. */
11295
- function withScheduledAutomationLock(db, taskId, callback) {
11296
- return db.transaction(async (tx) => {
11297
- await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext(${`${SCHEDULER_KEY_PREFIX}:task:${taskId}:lock`}))`);
11298
- return await callback(tx);
11299
- });
11300
- }
11301
- function requireStoredTask(task) {
11302
- const parsed = scheduledAutomationSchema.safeParse(task);
11303
- if (!parsed.success) throw new Error("Scheduled automation routing context is invalid.");
11304
- const { title, ...current } = parsed.data;
11305
- const normalizedTitle = title?.trim() || void 0;
11306
- return {
11307
- ...current,
11308
- ...normalizedTitle ? { title: normalizedTitle } : void 0
11309
- };
11310
- }
11311
- function scheduledAutomationJsonRecord(task) {
11312
- const { title: _title, ...record } = task;
11313
- return record;
11314
- }
11315
- async function upsertScheduledAutomation(db, task) {
11316
- const title = task.title?.trim() || null;
11317
- await db.insert(juniorSchedulerTasks).values({
11318
- createdAtMs: task.createdAtMs,
11319
- creatorIdentityId: task.creatorIdentityId,
11320
- creatorSlackUserId: task.createdBy.slackUserId,
11321
- id: task.id,
11322
- nextRunAtMs: task.nextRunAtMs,
11323
- record: scheduledAutomationJsonRecord(task),
11324
- runNowAtMs: task.runNowAtMs,
11325
- status: task.status,
11326
- teamId: task.destination.teamId,
11327
- title
11328
- }).onConflictDoUpdate({
11329
- target: juniorSchedulerTasks.id,
11330
- set: {
11331
- createdAtMs: sql`excluded.created_at_ms`,
11332
- creatorIdentityId: sql`excluded.creator_identity_id`,
11333
- creatorSlackUserId: sql`excluded.creator_slack_user_id`,
11334
- nextRunAtMs: sql`excluded.next_run_at_ms`,
11335
- record: sql`excluded.record`,
11336
- runNowAtMs: sql`excluded.run_now_at_ms`,
11337
- status: sql`excluded.status`,
11338
- teamId: sql`excluded.team_id`,
11339
- title: sql`excluded.title`
11340
- }
11341
- });
11342
- }
11343
- /** Read and decode one scheduled automation. */
11344
- async function readScheduledAutomation(db, taskId) {
11345
- const rows = await db.select({
11346
- creatorIdentityId: juniorSchedulerTasks.creatorIdentityId,
11347
- record: juniorSchedulerTasks.record,
11348
- title: juniorSchedulerTasks.title
11349
- }).from(juniorSchedulerTasks).where(eq(juniorSchedulerTasks.id, taskId)).limit(1);
11350
- return rows[0] ? parseScheduledAutomationRow(rows[0]) : void 0;
11351
- }
11352
- async function readListedScheduledAutomations(db, teamId) {
11353
- return (await db.select({
11354
- creatorIdentityId: juniorSchedulerTasks.creatorIdentityId,
11355
- record: juniorSchedulerTasks.record,
11356
- title: juniorSchedulerTasks.title
11357
- }).from(juniorSchedulerTasks).where(and(notInArray(juniorSchedulerTasks.status, ["deleted"]), teamId === void 0 ? void 0 : eq(juniorSchedulerTasks.teamId, teamId))).orderBy(asc(juniorSchedulerTasks.createdAtMs), asc(juniorSchedulerTasks.id))).map(parseScheduledAutomationRow).filter(isListedScheduledAutomation);
11358
- }
11359
- /** List decoded scheduled automations in stable creation order. */
11360
- async function listScheduledAutomations(db) {
11361
- return await readListedScheduledAutomations(db);
11362
- }
11363
- /** List decoded scheduled automations for one team in stable creation order. */
11364
- async function listScheduledAutomationsForTeam(db, teamId) {
11365
- return await readListedScheduledAutomations(db, teamId);
11366
- }
11367
- async function writeScheduledAutomation(db, task, current, editedBy) {
11368
- if (current?.status === "blocked" && task.status === "active" && typeof task.nextRunAtMs === "number" && Number.isFinite(task.nextRunAtMs)) await db.delete(juniorSchedulerRuns).where(and(eq(juniorSchedulerRuns.id, scheduledRunId(task.id, task.nextRunAtMs)), eq(juniorSchedulerRuns.status, "blocked")));
11369
- if (task.status === "deleted" && current?.status !== "deleted") await skipPendingRunsForDeletedTask(db, task);
11370
- await upsertScheduledAutomation(db, task);
11371
- await recordAutomationVersion(db, "scheduled", task, current, editedBy);
11372
- }
11373
- async function skipPendingRunsForDeletedTask(db, task) {
11374
- const errorMessage = `Scheduled automation ${task.id} was deleted before the run started.`;
11375
- const rows = await db.select({ record: juniorSchedulerRuns.record }).from(juniorSchedulerRuns).where(and(eq(juniorSchedulerRuns.taskId, task.id), eq(juniorSchedulerRuns.status, "pending")));
11376
- for (const row of rows) {
11377
- const parsed = scheduledRunSchema.safeParse(row.record);
11378
- if (!parsed.success || parsed.data.status !== "pending") continue;
11379
- const next = scheduledRunSchema.parse({
11380
- ...parsed.data,
11381
- completedAtMs: task.updatedAtMs,
11382
- errorMessage,
11383
- status: "skipped"
11384
- });
11385
- await db.update(juniorSchedulerRuns).set({
11386
- record: next,
11387
- status: next.status
11388
- }).where(and(eq(juniorSchedulerRuns.id, next.id), eq(juniorSchedulerRuns.status, "pending")));
11389
- }
11390
- }
11391
- /** Create a scheduled automation once under its retry-stable task lock. */
11392
- async function createScheduledAutomation(db, task) {
11393
- const next = requireStoredTask(task);
11394
- return await withScheduledAutomationLock(db, task.id, async (tx) => {
11395
- const current = await readScheduledAutomation(tx, task.id);
11396
- if (current) return current;
11397
- await writeScheduledAutomation(tx, next, void 0);
11398
- return next;
11399
- });
11400
- }
11401
- /**
11402
- * Save a scheduled automation and clear its blocked occurrence on reactivation.
11403
- * Pass `editedBy` when a person changes the definition so its version names them.
11404
- */
11405
- async function saveScheduledAutomation(db, task, expectedRevision, editedBy) {
11406
- const next = requireStoredTask(task);
11407
- await withScheduledAutomationLock(db, task.id, async (tx) => {
11408
- const current = await readScheduledAutomation(tx, task.id);
11409
- if (expectedRevision !== void 0) requireAutomationRevision(current, expectedRevision);
11410
- await writeScheduledAutomation(tx, next, current, editedBy);
11411
- });
11412
- return next;
11413
- }
11414
- /** Save a scheduled automation inside a task lock already held by the caller. */
11415
- async function saveScheduledAutomationInLock(db, task, current) {
11416
- await writeScheduledAutomation(db, requireStoredTask(task), current);
11417
- }
11418
- //#endregion
11419
10608
  //#region src/chat/scheduled-automations/telemetry.ts
11420
10609
  /** Safe structured telemetry attributes for scheduled-automation lifecycle events. */
11421
10610
  /** Correlation attributes shared by scheduled-automation create and run events. */
@@ -11772,7 +10961,7 @@ function createSlackScheduleCreateAutomationTool(context) {
11772
10961
  instruction: input.instruction,
11773
10962
  title: input.title
11774
10963
  });
11775
- const committed = await createScheduledAutomation(db, {
10964
+ const task = {
11776
10965
  id,
11777
10966
  createdAtMs: nowMs,
11778
10967
  updatedAtMs: nowMs,
@@ -11789,7 +10978,8 @@ function createSlackScheduleCreateAutomationTool(context) {
11789
10978
  outcomes: await resolveTaskOutcomes(input.outcomes, destination, actor.slackUserId),
11790
10979
  task: { text: input.instruction },
11791
10980
  ...title ? { title } : void 0
11792
- });
10981
+ };
10982
+ const committed = await createScheduledAutomation(db, task);
11793
10983
  if (!sameDestination(committed, destination) || committed.createdBy.slackUserId !== actor.slackUserId) throwToolInputError("Scheduled automation operation identity is invalid.");
11794
10984
  logInfo("scheduled_automation.create.completed", scheduledAutomationAttributes(committed));
11795
10985
  return scheduleAutomationToolResult(context.conversationId, committed, actor.slackUserId);
@@ -15036,7 +14226,7 @@ async function resolveStoredProvider(teamId, provider, query) {
15036
14226
  /** Create the tool that resolves people by identity provider and query. */
15037
14227
  function createUserLookupTool(teamId, providers) {
15038
14228
  return zodTool({
15039
- description: "Look up people by identity provider and query. Pass an enabled `provider` and its provider subject ID or handle. The `slack` provider also accepts email and name searches. Returns Slack `mention` values (`<@U…>`) for the current workspace. Use a mention only when one person clearly matches; otherwise ask which person the user means.",
14229
+ description: "Look up people by identity provider and query. Pass an enabled `provider` and its provider subject ID or handle. For `slack`, pass the person's Slack user ID (`U…`) whenever the conversation shows one, such as the requester's `author_id`, a participant's `actor_id`, or a `<@U…>` mention. The `slack` provider also accepts email and name searches. Use name search only for people with no known ID, because it can miss people in large workspaces. Returns Slack `mention` values (`<@U…>`) for the current workspace. Use a mention only when one person clearly matches; otherwise ask which person the user means.",
15040
14230
  annotations: {
15041
14231
  destructiveHint: false,
15042
14232
  idempotentHint: true,
@@ -15045,7 +14235,7 @@ function createUserLookupTool(teamId, providers) {
15045
14235
  },
15046
14236
  inputSchema: z.object({
15047
14237
  provider: z.enum(providers).describe("Enabled identity provider to query."),
15048
- query: z.string().trim().min(1).describe("Provider subject ID, handle, Slack email, or Slack name.")
14238
+ query: z.string().trim().min(1).describe("Provider subject ID, handle, Slack email, or Slack name. Prefer a Slack user ID (U…) when one is known.")
15049
14239
  }).strict(),
15050
14240
  outputSchema: juniorToolOutputSchema,
15051
14241
  execute: async ({ provider, query }) => {
@@ -16939,6 +16129,7 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
16939
16129
  }
16940
16130
  if (context.handoff) tools.handoff = createHandoffTool(context.handoff);
16941
16131
  if (context.spawnAgent) tools.spawnAgent = createSpawnAgentTool(context.spawnAgent);
16132
+ if (context.automation) tools[FINISH_AUTOMATION_RUN_TOOL_NAME] = createFinishAutomationRunTool(context.automation);
16942
16133
  if (context.mcpToolManager) {
16943
16134
  tools.searchMcpTools = createSearchMcpToolsTool(context.mcpToolManager);
16944
16135
  tools.callMcpTool = createCallMcpToolTool(context.mcpToolManager);
@@ -19894,7 +19085,8 @@ async function wireAgentTools(args) {
19894
19085
  surface: args.surface,
19895
19086
  ...args.currentActor ? { resolveActorIdentity: async () => await readActorIdentity(args.currentActor) } : void 0,
19896
19087
  ...args.durability.spawnAgent ? { spawnAgent: args.durability.spawnAgent } : void 0,
19897
- ...args.requestHandoff ? { handoff: args.requestHandoff } : void 0
19088
+ ...args.requestHandoff ? { handoff: args.requestHandoff } : void 0,
19089
+ ...args.run.dispatch?.declaresResult ? { automation: { sendsMessage: args.run.dispatch.outcomes?.length !== 0 } } : void 0
19898
19090
  };
19899
19091
  const toolRoute = resolveToolRuntimeRoute({
19900
19092
  actor: args.currentActor,
@@ -20727,7 +19919,11 @@ async function compactActiveContextIfNeeded(args, deps) {
20727
19919
  //#endregion
20728
19920
  //#region src/chat/agent/handoff.ts
20729
19921
  /** Tools that must be the only tool call in their assistant message. */
20730
- const EXCLUSIVE_TOOL_NAMES = [HANDOFF_TOOL_NAME, "switchWorkspace"];
19922
+ const EXCLUSIVE_TOOL_NAMES = [
19923
+ HANDOFF_TOOL_NAME,
19924
+ "switchWorkspace",
19925
+ FINISH_AUTOMATION_RUN_TOOL_NAME
19926
+ ];
20731
19927
  /** Return the exclusive tool name when one is present among the calls. */
20732
19928
  function exclusiveToolName(toolCalls) {
20733
19929
  for (const name of EXCLUSIVE_TOOL_NAMES) if (toolCalls.some((call) => call.name === name)) return name;
@@ -20926,6 +20122,7 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
20926
20122
  const conversationId = run.conversationId;
20927
20123
  const turnId = run.turnId;
20928
20124
  const runId = run.runId;
20125
+ const requireAutomationResult = run.dispatch?.declaresResult === true;
20929
20126
  const input = {
20930
20127
  actor: run.instruction.actor,
20931
20128
  includeConversationContextWithPiMessages: run.instruction.includeConversationContextWithHistory,
@@ -21450,11 +20647,13 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
21450
20647
  };
21451
20648
  }
21452
20649
  },
21453
- afterToolCall: async ({ result, toolCall }, signal) => {
20650
+ afterToolCall: async ({ isError, result, toolCall }, signal) => {
21454
20651
  const timedOutResult = runResume.timedOut && signal?.aborted ? projectTimedOutToolResult(result) : void 0;
21455
20652
  const sourceResult = timedOutResult ?? result;
21456
20653
  const projectedResult = wiring.projectActionReviewResult(toolCall.id, sourceResult);
21457
- return timedOutResult || projectedResult !== result ? projectedResult : void 0;
20654
+ const override = timedOutResult || projectedResult !== result ? projectedResult : void 0;
20655
+ if (toolCall.name === "finishAutomationRun" && !isError && !override) return { terminate: true };
20656
+ return override;
21458
20657
  },
21459
20658
  prepareNextTurnWithContext: async (nextTurn, hookSignal) => {
21460
20659
  try {
@@ -21503,6 +20702,7 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
21503
20702
  runResume.setBeforeMessageCount(agent.state.messages.length);
21504
20703
  if (shouldPromptAgent) runResume.setTurnStartMessageIndex(agent.state.messages.length);
21505
20704
  const authPauseOutcome = await withSpan(`invoke_agent ${botConfig.userName}`, "gen_ai.invoke_agent", spanContext, async () => {
20705
+ if (requireAutomationResult && readAutomationResult(agent.state.messages)) return;
21506
20706
  const promptTimestamp = restoredPromptTimestamp ?? Date.now();
21507
20707
  const contextMessage = shouldPromptAgent && promptContextContentParts.length > 0 ? {
21508
20708
  role: "user",
@@ -21620,6 +20820,7 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
21620
20820
  run = shouldPromptAgent && !capacityUpdate ? agent.prompt(freshPromptMessage) : agent.continue();
21621
20821
  let discardedRetryUsage;
21622
20822
  let actionConfirmationRetryUsed = false;
20823
+ let automationResultReminderUsed = !requireAutomationResult;
21623
20824
  let providerRetryAttempt = 0;
21624
20825
  let emptyOutputAttempt = 0;
21625
20826
  const prepareRetry = async (messages) => {
@@ -21659,6 +20860,15 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
21659
20860
  run = agent.continue();
21660
20861
  continue;
21661
20862
  }
20863
+ const automationResultReminder = automationResultReminderUsed ? void 0 : remindMissingAutomationResult(agent.state.messages);
20864
+ if (automationResultReminder) {
20865
+ automationResultReminderUsed = true;
20866
+ agent.state.messages = automationResultReminder;
20867
+ await runResume.persistSafeBoundary(automationResultReminder);
20868
+ logWarn("agent.turn.automation_result.reminded", {});
20869
+ run = agent.continue();
20870
+ continue;
20871
+ }
21662
20872
  const emptyOutputContinuation = nextEmptyOutputContinuation({
21663
20873
  attempt: emptyOutputAttempt,
21664
20874
  lastAssistant,
@@ -21725,7 +20935,8 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
21725
20935
  usage: turnUsage,
21726
20936
  executionProfile: turnRoute,
21727
20937
  assistantUserName: botConfig.userName,
21728
- modelId: activeModelId
20938
+ modelId: activeModelId,
20939
+ requireAutomationResult
21729
20940
  })
21730
20941
  };
21731
20942
  } catch (error) {
@@ -21818,7 +21029,7 @@ function createAgentRunner(execute, options) {
21818
21029
  } } : void 0
21819
21030
  };
21820
21031
  if (run.instruction.storedAttachments?.length) {
21821
- const { loadInputImages } = await import("./images-C7OKd6uy.js").then((n) => n.n);
21032
+ const { loadInputImages } = await import("./images-DSxZt5vk.js").then((n) => n.n);
21822
21033
  const storage = nextRun.environment?.attachmentStorage;
21823
21034
  if (!storage) throw new Error("Attachment storage is unavailable.");
21824
21035
  nextRun.instruction = {
@@ -21834,4 +21045,4 @@ function createAgentRunner(execute, options) {
21834
21045
  } };
21835
21046
  }
21836
21047
  //#endregion
21837
- export { findMatchingEventAutomations as $, CONVERSATION_WORK_STALE_ENQUEUE_MS as $t, resolveSlackChannelIdFromMessage as A, slackExternalRefIdSchema as An, cancelHumanFacingPendingMessages as At, readScheduledAutomation as B, getConversation as Bt, renderBlockText as C, normalizeSlackStatusText as Cn, getEventCatalog as Ct, storeAttachment as D, postSlackMessage as Dn, appendAndEnqueueExclusiveInboundMessage as Dt, requestAttachmentDeletion as E, addReactionToMessage as En, ackMessages as Et, logScheduledAutomationRunSkipped as F, toolInvocationDestination as Fn, completeConversationWork as Ft, editEventAutomation as G, recordConversationRetry as Gt, saveScheduledAutomationInLock as H, hasRunnableConversationWork as Ht, scheduledAutomationRunAttributes as I, AuthorizationFlowDisabledError as In, countPendingConversationMessages as It, effectiveTaskOutcomes as J, requestAnotherSlice as Jt, AutomationEditError as K, releaseConversationWork as Kt, listScheduledAutomations as L, shouldEmitDevAgentTrace as Ln, deadLetterAttempt as Lt, getConversationInfo as M, buildSteeringPiMessage as Mn, clearConsumedConversationWake as Mt, listThreadReplies as N, RetryableDeliveryError as Nn, clearExpiredConversationLease as Nt, parseSlackThreadId as O, removeReactionFromMessage as On, appendAndEnqueueInboundMessage as Ot, editScheduledAutomation as P, actorFromRun as Pn, completeConversationStop as Pt, eventAutomationBelongsToUser as Q, CONVERSATION_WORK_CHECK_IN_INTERVAL_MS as Qt, listScheduledAutomationsForTeam as R, getConfigDefaults as Rn, drainConversationMailbox as Rt, renderAttachmentText as S, normalizeSlackReplyMarkdown as Sn, timerWatchCredentialSubject as St, readLiveAttachment as T, truncateStatusText as Tn, pluginEventCatalog as Tt, scheduledRunId as U, listActiveConversationIds as Ut, saveScheduledAutomation as V, getConversationWorkState as Vt, withScheduledAutomationLock as W, recordAttemptFailure as Wt, collectEventAutomationMatchKeys as X, startConversationWork as Xt, resolveTaskOutcomes as Y, requestConversationWork as Yt, deleteEventAutomation as Z, stopConversationWork as Zt, resolvePluginOAuthAccount as _, parseOAuthStatePayload as _n, getMcpProviderErrorAttributes as _t, buildAuthPauseResponse as a, maybePostAutomatedTurnLimitNotice as an, automationDefinition as at, unpublishedArtifactHeaders as b, escapeSlackMrkdwnText as bn, EVENT_WAIT_MS as bt, deleteMcpAuthSessionsForUserProvider as c, normalizeSandboxEgressTracePropagationDomains as cn, sameDefinitionValue as ct, getMcpAuthSession as d, clearSandboxEgressSignals as dn, requireAutomationRevision as dt, hasConversationStop as en, getEventAutomation as et, getMcpStoredOAuthCredentials as f, consumeSandboxEgressAuthRequiredSignal as fn, agentBindingSchema as ft, readStats as g, formatProviderLabel as gn, AgentInvocationLimitError as gt, latestProgressStatus as h, sandboxEgressSignalsResponseSchema as hn, AgentInvocationBusyError as ht, executeCredentialedEgressRequest as i, renderTaskInput as in, setEventAutomationStatus as it, resolveSlackChannelIdFromThreadId as j, createUserTokenStore as jn, checkInConversationWork as jt, readSlackRawMessageContext as k, slackEntitySchema as kn, beginConversationResume as kt, deleteMcpServerSessionId as l, shouldPropagateSandboxEgressTrace as ln, AutomationConflictError as lt, PluginCredentialFailureError as m, parseSandboxEgressCredentialToken as mn, createAgentInvocationSchema as mt, executeAgentRun as n, isFinalAttempt as nn, parseEventAutomationRow as nt, finalizeMcpAuthorization as o, recordFinishedTurnForAutomatedLimit as on, listAutomationVersions as ot, putMcpStoredOAuthCredentials as p, consumeSandboxEgressPermissionDeniedSignal as pn, agentInvocationSchema as pt, eventAutomationTriggerAvailable as q, removeActiveConversation as qt, createContextCompactor as r, isInvalidConversationRecordError as rn, saveEventAutomation as rt, deleteMcpAuthSession as s, resolveSandboxEgressProviderForHost as sn, readAutomationVersion as st, createAgentRunner as t, hasRunnableConversationWork$1 as tn, listDeletedEventAutomationsCreatedBy as tt, deleteMcpStoredOAuthCredentials as u, SANDBOX_EGRESS_PROXY_PATH as un, automationRevision as ut, publicArtifactHeaders as v, resolveBaseUrl as vn, processWorkspaceSnapshotJob as vt, collectAttachmentGarbage as w, slackMention as wn, eventGuidance as wt, lookupSlackUserProfile as x, formatSlackLink as xn, isEventMailboxMetadata as xt, readPublicArtifact as y, startOAuthFlow as yn, ingestEvent as yt, parseScheduledAutomationRow as z, setConfigDefaults as zn, ensureConversationWake as zt };
21048
+ export { getEventCatalog as $, actorFromRun as $t, resolveSlackChannelIdFromMessage as A, hasConversationStop as At, effectiveTaskOutcomes as B, SANDBOX_EGRESS_PROXY_PATH as Bt, renderBlockText as C, removeActiveConversation as Ct, storeAttachment as D, stopConversationWork as Dt, requestAttachmentDeletion as E, startConversationWork as Et, logScheduledAutomationRunSkipped as F, maybePostAutomatedTurnLimitNotice as Ft, AgentInvocationBusyError as G, sandboxEgressSignalsResponseSchema as Gt, agentBindingSchema as H, consumeSandboxEgressAuthRequiredSignal as Ht, scheduledAutomationRunAttributes as I, recordFinishedTurnForAutomatedLimit as It, processWorkspaceSnapshotJob as J, resolveBaseUrl as Jt, AgentInvocationLimitError as K, formatProviderLabel as Kt, editEventAutomation as L, resolveSandboxEgressProviderForHost as Lt, getConversationInfo as M, isFinalAttempt as Mt, listThreadReplies as N, isInvalidConversationRecordError as Nt, parseSlackThreadId as O, CONVERSATION_WORK_CHECK_IN_INTERVAL_MS as Ot, editScheduledAutomation as P, renderTaskInput as Pt, timerWatchCredentialSubject as Q, RetryableDeliveryError as Qt, AutomationEditError as R, normalizeSandboxEgressTracePropagationDomains as Rt, renderAttachmentText as S, releaseConversationWork as St, readLiveAttachment as T, requestConversationWork as Tt, agentInvocationSchema as U, consumeSandboxEgressPermissionDeniedSignal as Ut, resolveTaskOutcomes as V, clearSandboxEgressSignals as Vt, createAgentInvocationSchema as W, parseSandboxEgressCredentialToken as Wt, EVENT_WAIT_MS as X, createUserTokenStore as Xt, ingestEvent as Y, startOAuthFlow as Yt, isEventMailboxMetadata as Z, buildSteeringPiMessage as Zt, resolvePluginOAuthAccount as _, getConversationWorkState as _t, buildAuthPauseResponse as a, shouldEmitDevAgentTrace as an, beginConversationResume as at, unpublishedArtifactHeaders as b, recordAttemptFailure as bt, deleteMcpAuthSessionsForUserProvider as c, clearConsumedConversationWake as ct, getMcpAuthSession as d, completeConversationWork as dt, toolInvocationDestination as en, eventGuidance as et, getMcpStoredOAuthCredentials as f, countPendingConversationMessages as ft, readStats as g, getConversation as gt, latestProgressStatus as h, ensureConversationWake as ht, executeCredentialedEgressRequest as i, runDispatchOutcome as in, appendAndEnqueueInboundMessage as it, resolveSlackChannelIdFromThreadId as j, hasRunnableConversationWork$1 as jt, readSlackRawMessageContext as k, CONVERSATION_WORK_STALE_ENQUEUE_MS as kt, deleteMcpServerSessionId as l, clearExpiredConversationLease as lt, PluginCredentialFailureError as m, drainConversationMailbox as mt, executeAgentRun as n, deliversFinalText as nn, ackMessages as nt, finalizeMcpAuthorization as o, getConfigDefaults as on, cancelHumanFacingPendingMessages as ot, putMcpStoredOAuthCredentials as p, deadLetterAttempt as pt, getMcpProviderErrorAttributes as q, parseOAuthStatePayload as qt, createContextCompactor as r, finishedRunReply as rn, appendAndEnqueueExclusiveInboundMessage as rt, deleteMcpAuthSession as s, setConfigDefaults as sn, checkInConversationWork as st, createAgentRunner as t, AuthorizationFlowDisabledError as tn, pluginEventCatalog as tt, deleteMcpStoredOAuthCredentials as u, completeConversationStop as ut, publicArtifactHeaders as v, hasRunnableConversationWork as vt, collectAttachmentGarbage as w, requestAnotherSlice as wt, lookupSlackUserProfile as x, recordConversationRetry as xt, readPublicArtifact as y, listActiveConversationIds as yt, eventAutomationTriggerAvailable as z, shouldPropagateSandboxEgressTrace as zt };