@sentry/junior 0.271.0 → 0.272.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 (71) hide show
  1. package/dist/{adapter-Bqbtr1RV.js → adapter-BDYDi1F9.js} +1 -1
  2. package/dist/{admin-CWBjzyN0.js → admin-Dj7EFycG.js} +1 -1
  3. package/dist/{agent-runner-BiHsH8O_.js → agent-runner-Cl6Usx2Y.js} +320 -271
  4. package/dist/api/schema.d.ts +2 -2
  5. package/dist/api/schema.js +4 -4
  6. package/dist/api.d.ts +1 -1
  7. package/dist/api.js +71 -29
  8. package/dist/{app-C81xfpNa.d.ts → app-DjZblE2J.d.ts} +2 -2
  9. package/dist/app.d.ts +1 -1
  10. package/dist/app.js +29 -36
  11. package/dist/{assistant-reply-DgtX9DaJ.js → assistant-reply-DN2w6JL_.js} +1 -1
  12. package/dist/{automation-Dwh_xcqM.js → automation-CV7g5h3A.js} +1 -1
  13. package/dist/{briefs-0FeSJHQ3.js → briefs-PysAqnjV.js} +4 -4
  14. package/dist/{catalog-runtime-Bw9kjGVu.js → catalog-runtime-BNeCWIaF.js} +1 -1
  15. package/dist/cli/chat.js +14 -14
  16. package/dist/cli/check.js +3 -3
  17. package/dist/cli/main.js +3 -3
  18. package/dist/cli/plugins.js +4 -4
  19. package/dist/cli/snapshot-create.js +4 -4
  20. package/dist/cli/upgrade.d.ts +1 -1
  21. package/dist/cli/upgrade.js +3 -3
  22. package/dist/{client-B0jlQSgO.js → client-FRphcDWW.js} +2 -2
  23. package/dist/{config-DrAeD8sr.js → config-BUP9wjiR.js} +3 -3
  24. package/dist/{connections-BzN2K0G9.js → connections-CesHau9u.js} +10 -10
  25. package/dist/{conversation-AhOVzN_k.js → conversation-BtQzLoK8.js} +12 -1
  26. package/dist/{conversation-privacy-D1NhUxyo.js → conversation-privacy-DiGvA9a3.js} +1 -1
  27. package/dist/{credential-sync-DpNWl4yy.js → credential-sync-D2odJC30.js} +14 -14
  28. package/dist/{credentialed-mh7HBqey.js → credentialed-CgTulRDH.js} +11 -11
  29. package/dist/{db-DSabI7jJ.d.ts → db-BPLmOwXf.d.ts} +11 -1
  30. package/dist/{executor-C2cRw0hZ.js → executor-DGDpyegI.js} +25 -7
  31. package/dist/instrumentation.js +2 -2
  32. package/dist/{job-queue-CEeLYkSj.js → job-queue-CjB97sii.js} +2 -2
  33. package/dist/{list-DjJJ5K0x.js → list-JZUP1yog.js} +35 -51
  34. package/dist/{logging-DuwXUuIY.js → logging-CjkDr-jP.js} +1 -1
  35. package/dist/message-D9y8PKA1.js +186 -0
  36. package/dist/nitro.d.ts +1 -1
  37. package/dist/nitro.js +3 -3
  38. package/dist/{oauth-callback-server-oM5TJt34.js → oauth-callback-server-02Pej6hj.js} +2 -2
  39. package/dist/{prompt-BPSdXYXV.js → prompt-vfceH0c-.js} +1 -1
  40. package/dist/{registry-BPtbAVFo.js → registry-7XgwNgQe.js} +1 -1
  41. package/dist/{resolve-D44G1jJm.js → resolve-CuuxNfWG.js} +3 -3
  42. package/dist/{result-CvP8c5bb.js → result-BRhb6iN1.js} +16 -12
  43. package/dist/{runner-FIGIKvk2.js → runner-DaWCofuS.js} +11 -11
  44. package/dist/{sandbox-egress-signals-C_JzucwP.js → sandbox-egress-signals-Bn6FA1UC.js} +1 -1
  45. package/dist/{schema-DPCvePop.d.ts → schema-CNjAzYOw.d.ts} +15 -3
  46. package/dist/{schema-DKZgO-xf.js → schema-D5VLF4NT.js} +3 -4
  47. package/dist/{service-account-1mJGRAvx.js → service-account-D9vrsi8d.js} +3 -3
  48. package/dist/{service-account-CHO3YXpt.js → service-account-H45LIAG2.js} +3 -3
  49. package/dist/{session-stop-DuUB1rwQ.js → session-stop-HKl0ECX0.js} +50 -24
  50. package/dist/{skills-DHK7L9Eo.js → skills-CsiKu3-g.js} +2 -2
  51. package/dist/{spawn-CKl_ySIq.js → spawn-DqjBxqWh.js} +2 -2
  52. package/dist/{store-BF2XEeQz.js → store-CPF2kzbu.js} +3 -3
  53. package/dist/{store-Di0E4EvU.js → store-DyVKH0QB.js} +47 -30
  54. package/dist/{task-queue-CC7IFtbT.js → task-queue-BbQXCcNX.js} +3 -3
  55. package/dist/{task-runner-DKZtMFwa.js → task-runner-ChzW5E7R.js} +6 -6
  56. package/dist/{thread-state-BQ1T3eeF.js → thread-state-Ds8JTsX6.js} +5 -5
  57. package/dist/{turn-failure-response-B5eSFoHn.js → turn-failure-response-DCMvjsA0.js} +8 -8
  58. package/dist/{turn-lifecycle-CTLEIEOn.js → turn-lifecycle-DM_77q33.js} +1 -1
  59. package/dist/{turn-session-routing-BFVp8TbU.js → turn-session-routing-BV_gX7n3.js} +12 -12
  60. package/dist/{validation-Cjo-3yt3.js → validation-DcGelbBi.js} +1 -1
  61. package/dist/{version-D30MBLLw.js → version-C48azPKq.js} +1 -1
  62. package/dist/version.js +1 -1
  63. package/dist/{viewer-D3jhuP66.js → viewer-BCRuO8Wg.js} +25 -9
  64. package/dist/{work-CL9QwQcl.js → work-zxkmbJAp.js} +27 -11
  65. package/dist/{worker-S2P7IJDI.js → worker-CNNxHF1V.js} +6 -6
  66. package/migrations/0072_conversation_read_positions.sql +10 -0
  67. package/migrations/meta/0072_snapshot.json +4064 -0
  68. package/migrations/meta/_journal.json +7 -0
  69. package/package.json +4 -4
  70. package/dist/message-CRe3WBKc.js +0 -59
  71. package/migrations/AGENTS.md +0 -8
@@ -1,45 +1,45 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-nwUcYXjP.js";
2
- import { $ as saveEventAutomation, A as readAutomationRuns, Ar as trimTrailingAssistantMessages, B as readScheduledAutomation, Br as estimateTextTokens, Bt as recordMcpProviderConnected, C as readAutomationExecutionByConversationId, Cn as loadTurnCheckpoint, Cr as isAssistantMessage, Ct as requireEventIdentifier, D as readAutomationExecutionStatusHours, Dr as normalizeToolNameFromResult, Dt as loadConnectedMcpProviders, E as readAutomationExecutionStatusDays, Er as isToolResultMessage, Fr as buildDeterministicTurnId, Ft as recordAttachmentsDelivered, Hr as getAgentContextInputLimitTokens, Ht as recordToolExecutionStarted, I as createScheduledAutomation, J as deleteEventAutomation, Jr as historyItemFromPiMessage, L as deleteScheduledAutomation, Mr as SESSION_CONTEXT_SECTION_TAG, Mt as loadTurnRoute, Nr as TURN_CONTEXT_TAG, Nt as openConversationProjection, O as readAutomationExecutionSummaries, Or as retainRuntimeTurnContext, Ot as loadConversationProjection, Pt as recordAgentsInstructionsUpdated, Q as parseEventAutomationRow, Qt as resolveTaskTitle, Rt as recordAuthorizationRequested, Sn as continuableMessages, St as registeredResourceTypeSchema, T as readAutomationExecutionHours, Tr as isToolResultError, Ut as recordTurnRoute, V as saveScheduledAutomation, Vr as getAgentContextCompactionTriggerTokens, Wr as isExperimentalFeatureEnabled, X as getEventAutomation, Xr as getConfigDefaults, Yr as piMessageFromHistoryItem, Z as listDeletedEventAutomationsCreatedBy, Zt as fallbackShortTitle, _n as abandonReplacedPendingAuth, _r as extractAssistantText, _t as eventWatchUnavailable, at as listAutomationVersions, br as getUserMessageInstructionText, bt as registeredEventTriggerProblems, c as getDispatchIdForConversation, cr as TURN_LIMIT_WRAP_UP_MESSAGE, dn as PluginCredentialFailureError, dr as TurnToolCallLimitExceededError, et as setEventAutomationStatus, fn as createPluginAuthOrchestration, ft as automationEventCatalog, gn as AuthorizationPauseError, gt as eventNamespaceSchema, hn as AuthorizationFlowDisabledError, hr as CODEMODE_TOOL_NAME, jr as REQUEST_CONTEXT_SECTION_TAG, k as readAutomationExecutions, kr as stripRuntimeTurnContext, kt as loadLastModelProfile, l as getDispatchInputMessageId, lr as TurnBudgetExceededError, lt as automationRevision, mn as AuthPausePersistenceError, mr as isTurnExecutionLimitExceededError, ot as readAutomationVersion, pn as authorizationId, pr as createTurnLimitGate, pt as describeRegisteredEventTrigger, q as createEventAutomation, rt as automationDefinition, st as sameDefinitionValue, t as resolveConversationRouting, u as getDispatchRecord, un as createPluginEgress, ur as TurnSliceLimitExceededError, ut as requireAutomationRevision, vn as canReusePendingAuthLink, vr as getPiMessageRole, vt as hasEventCatalogEntries, w as readAutomationExecutionDays, wn as saveTurnCheckpoint, wr as isContinuablePiBoundary, wt as requireValidEventTrigger, x as emptyAutomationRunWindows, xr as hasRuntimeTurnContext, xt as registeredEventTypeSchema, yr as getTerminalAssistantMessages, yt as registeredEventMatchSchema, z as parseScheduledAutomationRow, zt as recordGuardianActionReviewed } from "./turn-session-routing-BFVp8TbU.js";
3
- import { _ as getProviderErrorAttributes, a as completeText, b as getGatewayApiKey, c as COMPACTION_SUMMARY_PREFIX, d as renderCurrentInstruction, f as unwrapCurrentInstruction, g as findProviderError, h as createProviderError, i as completeObject, l as hasCompactedConversationContext, m as ProviderError, n as GEN_AI_SERVER_ADDRESS, p as escapeXml, s as resolveGatewayModel, t as GEN_AI_PROVIDER_NAME, u as isCompactionSummaryText, x as resolveGatewayCredential, y as isProviderRetryError } from "./client-B0jlQSgO.js";
4
- import { n as botConfig, v as normalizeSlackEmojiName } from "./config-DrAeD8sr.js";
5
- import { B as isRecord$4, C as summarizeMessageText, E as withSpan, H as toOptionalString, S as setTags, T as withLogContext, _ as runBestEffort, a as extractGenAiUsageSummary, b as setSpanAttributes, f as logError, g as normalizeGenAiFinishReason, h as logWarn, i as extractGenAiUsageAttributes, l as getLogContextAttributesForSpan, m as logInfo, p as logException, u as getTracePropagationHeaders, v as serializeGenAiAttribute, x as setSpanStatus, z as normalizeIdentityEmail } from "./logging-DuwXUuIY.js";
2
+ import { $ as saveEventAutomation, A as readAutomationRuns, Ar as trimTrailingAssistantMessages, B as readScheduledAutomation, Br as estimateTextTokens, Bt as recordMcpProviderConnected, C as readAutomationExecutionByConversationId, Cn as loadTurnCheckpoint, Cr as isAssistantMessage, Ct as requireEventIdentifier, D as readAutomationExecutionStatusHours, Dr as normalizeToolNameFromResult, Dt as loadConnectedMcpProviders, E as readAutomationExecutionStatusDays, Er as isToolResultMessage, Fr as buildDeterministicTurnId, Ft as recordAttachmentsDelivered, Hr as getAgentContextInputLimitTokens, Ht as recordToolExecutionStarted, I as createScheduledAutomation, J as deleteEventAutomation, Jr as historyItemFromPiMessage, L as deleteScheduledAutomation, Mr as SESSION_CONTEXT_SECTION_TAG, Mt as loadTurnRoute, Nr as TURN_CONTEXT_TAG, Nt as openConversationProjection, O as readAutomationExecutionSummaries, Or as retainRuntimeTurnContext, Ot as loadConversationProjection, Pt as recordAgentsInstructionsUpdated, Q as parseEventAutomationRow, Qt as resolveTaskTitle, Rt as recordAuthorizationRequested, Sn as continuableMessages, St as registeredResourceTypeSchema, T as readAutomationExecutionHours, Tr as isToolResultError, Ut as recordTurnRoute, V as saveScheduledAutomation, Vr as getAgentContextCompactionTriggerTokens, Wr as isExperimentalFeatureEnabled, X as getEventAutomation, Xr as getConfigDefaults, Yr as piMessageFromHistoryItem, Z as listDeletedEventAutomationsCreatedBy, Zt as fallbackShortTitle, _n as abandonReplacedPendingAuth, _r as extractAssistantText, _t as eventWatchUnavailable, at as listAutomationVersions, br as getUserMessageInstructionText, bt as registeredEventTriggerProblems, c as getDispatchIdForConversation, cr as TURN_LIMIT_WRAP_UP_MESSAGE, dn as PluginCredentialFailureError, dr as TurnToolCallLimitExceededError, et as setEventAutomationStatus, fn as createPluginAuthOrchestration, ft as automationEventCatalog, gn as AuthorizationPauseError, gt as eventNamespaceSchema, hn as AuthorizationFlowDisabledError, hr as CODEMODE_TOOL_NAME, jr as REQUEST_CONTEXT_SECTION_TAG, k as readAutomationExecutions, kr as stripRuntimeTurnContext, kt as loadLastModelProfile, l as getDispatchInputMessageId, lr as TurnBudgetExceededError, lt as automationRevision, mn as AuthPausePersistenceError, mr as isTurnExecutionLimitExceededError, ot as readAutomationVersion, pn as authorizationId, pr as createTurnLimitGate, pt as describeRegisteredEventTrigger, q as createEventAutomation, rt as automationDefinition, st as sameDefinitionValue, t as resolveConversationRouting, u as getDispatchRecord, un as createPluginEgress, ur as TurnSliceLimitExceededError, ut as requireAutomationRevision, vn as canReusePendingAuthLink, vr as getPiMessageRole, vt as hasEventCatalogEntries, w as readAutomationExecutionDays, wn as saveTurnCheckpoint, wr as isContinuablePiBoundary, wt as requireValidEventTrigger, x as emptyAutomationRunWindows, xr as hasRuntimeTurnContext, xt as registeredEventTypeSchema, yr as getTerminalAssistantMessages, yt as registeredEventMatchSchema, z as parseScheduledAutomationRow, zt as recordGuardianActionReviewed } from "./turn-session-routing-BV_gX7n3.js";
3
+ import { _ as getProviderErrorAttributes, a as completeText, b as getGatewayApiKey, c as COMPACTION_SUMMARY_PREFIX, d as renderCurrentInstruction, f as unwrapCurrentInstruction, g as findProviderError, h as createProviderError, i as completeObject, l as hasCompactedConversationContext, m as ProviderError, n as GEN_AI_SERVER_ADDRESS, p as escapeXml, s as resolveGatewayModel, t as GEN_AI_PROVIDER_NAME, u as isCompactionSummaryText, x as resolveGatewayCredential, y as isProviderRetryError } from "./client-FRphcDWW.js";
4
+ import { n as botConfig, v as normalizeSlackEmojiName } from "./config-BUP9wjiR.js";
5
+ import { B as isRecord$4, C as summarizeMessageText, E as withSpan, H as toOptionalString, S as setTags, T as withLogContext, _ as runBestEffort, a as extractGenAiUsageSummary, b as setSpanAttributes, f as logError, g as normalizeGenAiFinishReason, h as logWarn, i as extractGenAiUsageAttributes, l as getLogContextAttributesForSpan, m as logInfo, p as logException, u as getTracePropagationHeaders, v as serializeGenAiAttribute, x as setSpanStatus, z as normalizeIdentityEmail } from "./logging-CjkDr-jP.js";
6
6
  import { g as withActiveSpan, i as getActiveSpan, m as startInactiveSpan } from "./sentry-MDkYAVPV.js";
7
- 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-D1NhUxyo.js";
7
+ 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-DiGvA9a3.js";
8
8
  import { a as formatModelProfile, c as modelProfileSchema, l as profileConfig, n as ModelProfileNotConfiguredError, o as formatModelProfiles, r as boundProfile, s as modelIdForProfile, u as TURN_REASONING_LEVELS } from "./model-profile-B998sjVd.js";
9
9
  import { d as parseSlackChannelReferenceId, f as parseSlackTeamId, h as parseSlackMessageTs, n as createSlackActor, o as isUserActor, p as parseSlackUserId, u as parseSlackChannelId } from "./actor-B4DYygOB.js";
10
- import { C as getSqlExecutor, M as resolveActorIdentityId, Q as sameDestination, S as getDb, U as addAgentTurnUsage, W as agentTurnCostUsd, X as parseDestination, a as readActorIdentityFromSql, b as getConversationMessageSearchStore, f as identityAvatarUrl, h as readUserAvatarUrls, i as readActorIdentity, j as SYSTEM_IDENTITY_ID, q as hasAgentTurnUsage, x as getConversationStore, y as getConversationEventStore } from "./viewer-D3jhuP66.js";
10
+ import { C as getSqlExecutor, M as resolveActorIdentityId, Q as sameDestination, S as getDb, U as addAgentTurnUsage, W as agentTurnCostUsd, X as parseDestination, a as readActorIdentityFromSql, b as getConversationMessageSearchStore, f as identityAvatarUrl, h as readUserAvatarUrls, i as readActorIdentity, j as SYSTEM_IDENTITY_ID, q as hasAgentTurnUsage, x as getConversationStore, y as getConversationEventStore } from "./viewer-BCRuO8Wg.js";
11
11
  import { a as juniorAgentInvocations, c as juniorIdentities, d as juniorDestinations, i as juniorAgentBindings, l as juniorUsers, s as juniorConversations } from "./agent-invocations-C1RNOCs8.js";
12
- import { T as juniorArtifacts, d as juniorAutomations, f as juniorStats, g as juniorConversationBriefs, l as juniorAutomationRuns, s as juniorReviewItemSlackCards, w as juniorAttachments, x as juniorConversationAnnotations } from "./executor-C2cRw0hZ.js";
12
+ import { E as juniorArtifacts, S as juniorConversationAnnotations, T as juniorAttachments, _ as juniorConversationBriefs, d as juniorAutomations, f as juniorStats, l as juniorAutomationRuns, s as juniorReviewItemSlackCards } from "./executor-DGDpyegI.js";
13
13
  import { _ as sameActorIdentity, g as instructionProvenanceFor, h as instructionActors, i as KNOWN_CONVERSATION_EVENT_TYPES, m as contextProvenance, n as detectImageMediaType, p as agentInstructionProvenance, r as isVisionImageMediaType } from "./media-U6Tuz6Mi.js";
14
14
  import { a as listReferenceFiles, c as soulPathCandidates, l as worldPathCandidates } from "./package-discovery-DJBompdd.js";
15
- import { c as fetchWithBoundedOAuthErrorBodies, d as actorLogContext, f as credentialContextForActor, g as credentialUserSubjectId, i as resolvePluginHeaderEnvRefs, l as getOAuthProviderErrorAttributes, s as OAuthProviderError } from "./registry-BPtbAVFo.js";
16
- import { n as pluginCatalogRuntime } from "./catalog-runtime-Bw9kjGVu.js";
17
- import { a as parseSkillInvocation, i as parseSkillFile, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-DHK7L9Eo.js";
18
- import { $ as loadSnapshotsForProfile, $n as sandboxSkillDir, $t as workspaceRepoCheckoutPath, A as updateReviewItemContent, An as normalizeSlackConversationId, B as EVERYONE_ASSIGNEE, Bn as getDashboardTaskLink, Bt as clearSandboxEgressSignals, C as getOpenReviewItemByDedupeKey, Cn as SlackActionError, Ct as openSlackDirectMessage, Dn as getSlackClient, Et as postSlackMessage, Ft as wrapSandboxSetupError, G as deleteWorkspace, Gn as buildNonInteractiveShellScript, H as resolveAssignee, Hn as ToolInputError, Ht as consumeSandboxEgressPermissionDeniedSignal, I as readReviewItemAgentScope, Ir as conversationNoteEvent, It as WorkspaceSnapshotNotReadyError, J as listWorkspaces, Jn as SANDBOX_DATA_ROOT, Jt as resolveSandboxCommandEnvironment, K as getWorkspace, Kn as runNonInteractiveCommand, L as requireReviewItemAssignees, Ln as getDashboardConversationLink, Lt as getWorkspaceSnapshotNotReadyError, Mn as withSlackRetries, Mr as JUNIOR_NATIVE_EVENT_NAMESPACE, Mt as isAbortError, N as canAgentChangeReviewItem, Nn as sleep, Nt as isAlreadyExistsError, O as searchReviewItems, On as isConversationScopedChannel, Or as defaultTimezone, Ot as updateSlackMessage, P as canReadReviewItem, Pt as throwSandboxOperationError, Q as invalidateReadySnapshot, Qn as SANDBOX_WORKSPACE_ROOT, Qt as tryWorkspaceRepoCheckoutPath, R as requireReviewItemLinks, Rt as isWorkspaceSnapshotNotReadyError, S as dismissOpenReviewItems, St as getSlackMessagePermalink, T as insertReviewItem, Tn as getFilePermalink, Tr as createPluginAnnotations, U as resolveAssignees, Un as PluginToolDeniedError, Ut as createSandboxEgressCredentialToken, V as automationCreatorUserId, Vt as consumeSandboxEgressAuthRequiredSignal, W as createWorkspace, Wn as buildCommandScript, X as updateWorkspace, Xn as SANDBOX_REPOS_ROOT, Yn as SANDBOX_JUNIOR_ROOT, Z as WorkspaceValidationError, Zn as SANDBOX_SKILLS_ROOT, Zt as SANDBOX_RUNTIME, an as STOP_WATCHING_TOOL_NAME, at as ensureDockerDaemon, b as closeReviewItem, br as matchSlackAttachments, bt as addReactionToMessage, d as getPluginTools, dn as deliverWatch, en as resolveChannelCapabilities, er as sandboxSkillFile, et as setWorkspaceSnapshot, f as getPluginUserPromptContributions, fn as findMatchingWatches, ft as resolveBaseUrl, gt as normalizeSlackReplyMarkdown, in as RESOURCE_WATCH_TOOL_SOURCE, it as isStale, j as PLUGIN_REVIEW_ITEM_KEY_PREFIX, jn as runWithSlackInstallationToken, kr as resolveUserTimezone, kt as uploadFilesToConversation, ln as createTimerWatch, lt as deliverOAuthAuthorization, mn as listWatches, mt as escapeSlackMrkdwnText, n as createUserTokenStore, nn as RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS, nt as create, o as createPluginHookRunner, on as cancelSubscriptions, ot as getSandboxProvider, p as getPlugins, pn as getWatch, q as getWorkspaceByName, qn as SANDBOX_COMMAND_PATH, qt as buildSandboxEgressNetworkPolicy, r as getOAuthAccountProviders, rn as RESOURCE_SUBSCRIPTION_MAX_TTL_MS, rt as hash, sn as cancelWatch, tn as getSlackToolContext, tr as sandboxSkillPathResolution, tt as setWorkspaceSnapshotBuild, u as getPluginSystemPromptContributions, un as createWatch, ut as formatProviderLabel, vt as slackMention, w as getReviewItem, wn as downloadPrivateSlackFile, wr as saveObjectAnnotations, xt as deleteSlackMessage, zn as getDashboardReviewItemLink, zt as workspaceSnapshotNotReadyUserMessage } from "./credentialed-mh7HBqey.js";
15
+ import { c as fetchWithBoundedOAuthErrorBodies, d as actorLogContext, f as credentialContextForActor, g as credentialUserSubjectId, i as resolvePluginHeaderEnvRefs, l as getOAuthProviderErrorAttributes, s as OAuthProviderError } from "./registry-7XgwNgQe.js";
16
+ import { n as pluginCatalogRuntime } from "./catalog-runtime-BNeCWIaF.js";
17
+ import { a as parseSkillInvocation, i as parseSkillFile, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-CsiKu3-g.js";
18
+ import { $ as loadSnapshotsForProfile, $n as sandboxSkillDir, $t as workspaceRepoCheckoutPath, A as updateReviewItemContent, An as normalizeSlackConversationId, B as EVERYONE_ASSIGNEE, Bn as getDashboardTaskLink, Bt as clearSandboxEgressSignals, C as getOpenReviewItemByDedupeKey, Cn as SlackActionError, Ct as openSlackDirectMessage, Dn as getSlackClient, Et as postSlackMessage, Ft as wrapSandboxSetupError, G as deleteWorkspace, Gn as buildNonInteractiveShellScript, H as resolveAssignee, Hn as ToolInputError, Ht as consumeSandboxEgressPermissionDeniedSignal, I as readReviewItemAgentScope, Ir as conversationNoteEvent, It as WorkspaceSnapshotNotReadyError, J as listWorkspaces, Jn as SANDBOX_DATA_ROOT, Jt as resolveSandboxCommandEnvironment, K as getWorkspace, Kn as runNonInteractiveCommand, L as requireReviewItemAssignees, Ln as getDashboardConversationLink, Lt as getWorkspaceSnapshotNotReadyError, Mn as withSlackRetries, Mr as JUNIOR_NATIVE_EVENT_NAMESPACE, Mt as isAbortError, N as canAgentChangeReviewItem, Nn as sleep, Nt as isAlreadyExistsError, O as searchReviewItems, On as isConversationScopedChannel, Or as defaultTimezone, Ot as updateSlackMessage, P as canReadReviewItem, Pt as throwSandboxOperationError, Q as invalidateReadySnapshot, Qn as SANDBOX_WORKSPACE_ROOT, Qt as tryWorkspaceRepoCheckoutPath, R as requireReviewItemLinks, Rt as isWorkspaceSnapshotNotReadyError, S as dismissOpenReviewItems, St as getSlackMessagePermalink, T as insertReviewItem, Tn as getFilePermalink, Tr as createPluginAnnotations, U as resolveAssignees, Un as PluginToolDeniedError, Ut as createSandboxEgressCredentialToken, V as automationCreatorUserId, Vt as consumeSandboxEgressAuthRequiredSignal, W as createWorkspace, Wn as buildCommandScript, X as updateWorkspace, Xn as SANDBOX_REPOS_ROOT, Yn as SANDBOX_JUNIOR_ROOT, Z as WorkspaceValidationError, Zn as SANDBOX_SKILLS_ROOT, Zt as SANDBOX_RUNTIME, an as STOP_WATCHING_TOOL_NAME, at as ensureDockerDaemon, b as closeReviewItem, br as matchSlackAttachments, bt as addReactionToMessage, d as getPluginTools, dn as deliverWatch, en as resolveChannelCapabilities, er as sandboxSkillFile, et as setWorkspaceSnapshot, f as getPluginUserPromptContributions, fn as findMatchingWatches, ft as resolveBaseUrl, gt as normalizeSlackReplyMarkdown, in as RESOURCE_WATCH_TOOL_SOURCE, it as isStale, j as PLUGIN_REVIEW_ITEM_KEY_PREFIX, jn as runWithSlackInstallationToken, kr as resolveUserTimezone, kt as uploadFilesToConversation, ln as createTimerWatch, lt as deliverOAuthAuthorization, mn as listWatches, mt as escapeSlackMrkdwnText, n as createUserTokenStore, nn as RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS, nt as create, o as createPluginHookRunner, on as cancelSubscriptions, ot as getSandboxProvider, p as getPlugins, pn as getWatch, q as getWorkspaceByName, qn as SANDBOX_COMMAND_PATH, qt as buildSandboxEgressNetworkPolicy, r as getOAuthAccountProviders, rn as RESOURCE_SUBSCRIPTION_MAX_TTL_MS, rt as hash, sn as cancelWatch, tn as getSlackToolContext, tr as sandboxSkillPathResolution, tt as setWorkspaceSnapshotBuild, u as getPluginSystemPromptContributions, un as createWatch, ut as formatProviderLabel, vt as slackMention, w as getReviewItem, wn as downloadPrivateSlackFile, wr as saveObjectAnnotations, xt as deleteSlackMessage, zn as getDashboardReviewItemLink, zt as workspaceSnapshotNotReadyUserMessage } from "./credentialed-CgTulRDH.js";
19
19
  import { a as withLock, i as fenceLock } from "./locks-D-0h111u.js";
20
- import { r as getStateAdapter } from "./adapter-Bqbtr1RV.js";
20
+ import { r as getStateAdapter } from "./adapter-BDYDi1F9.js";
21
21
  import { i as isValidTimeZone, r as getZonedDateTimeParts, t as getFirstRunAtMs } from "./cadence-R7Em4RjE.js";
22
- import { B as removedCardSchema, J as conversationBriefSchema, K as briefLinkSchema, q as briefOutcomeStatusSchema } from "./conversation-AhOVzN_k.js";
23
- import { r as EVENT_AUTHOR_ID } from "./prompt-BPSdXYXV.js";
24
- import { A as AgentMessageQueueFullError, F as agentNameSchema, H as TurnInputCommitLostError, I as AGENT_TYPES, K as isTurnInputCommitLostError, M as AgentNameInUseError, N as CHILD_AGENT_RESULTS, P as PARENT_AGENT_TARGET, V as CooperativeTurnYieldError, _ as getLatestAgentInvocationForConversation, d as getAgentInvocationIdForResult, f as getAgentInvocationMessageId, g as getChildAgentLockName, j as AgentMessageTargetError, k as AgentInvocationLimitError, t as NON_TERMINAL_AGENT_INVOCATION_STATUSES, v as invocationFromRow, y as isTerminalAgentInvocation, z as formatAgentTypes } from "./store-Di0E4EvU.js";
25
- import { a as isNoReplyMarker, i as NO_REPLY_MARKER, n as getAssistantReplyText, r as sanitizeAssistantText, t as decideReply } from "./assistant-reply-DgtX9DaJ.js";
26
- import { A as automationResultSchema$1, F as remindMissingAutomationResult, P as readAutomationResult, _ as WORKSPACE_SNAPSHOT_FAILED_EVENT, a as EVENT_WAIT_MS, b as workspaceSnapshotWatch, d as getEventCatalog, k as FINISH_AUTOMATION_RUN_TOOL_NAME, n as markConversationsStopped, o as enqueueEventNotification, p as requireSlackChannelMessageScope, t as getConversationStoppedAtMs, v as WORKSPACE_SNAPSHOT_READY_EVENT, w as resolveSlackReplyTarget, y as workspaceSnapshotFinishedEvent } from "./session-stop-DuUB1rwQ.js";
22
+ import { J as briefOutcomeStatusSchema, V as removedCardSchema, Y as conversationBriefSchema, q as briefLinkSchema } from "./conversation-BtQzLoK8.js";
23
+ import { r as EVENT_AUTHOR_ID } from "./prompt-vfceH0c-.js";
24
+ import { A as AgentMessagePausedError, F as CHILD_AGENT_RESULTS, I as PARENT_AGENT_TARGET, J as isTurnInputCommitLostError, L as agentNameSchema, M as AgentMessageTargetError, N as AgentNameInUseError, R as AGENT_TYPES, U as CooperativeTurnYieldError, V as formatAgentTypes, W as TurnInputCommitLostError, _ as invocationFromRow, d as getAgentInvocationMessageId, g as getLatestAgentInvocationForConversation, h as getChildAgentLockName, j as AgentMessageQueueFullError, k as AgentInvocationLimitError, t as NON_TERMINAL_AGENT_INVOCATION_STATUSES, u as getAgentInvocationIdForResult, v as isTerminalAgentInvocation } from "./store-DyVKH0QB.js";
25
+ import { a as isNoReplyMarker, i as NO_REPLY_MARKER, n as getAssistantReplyText, r as sanitizeAssistantText, t as decideReply } from "./assistant-reply-DN2w6JL_.js";
26
+ import { E as resolveSlackReplyTarget, L as readAutomationResult, M as FINISH_AUTOMATION_RUN_TOOL_NAME, N as automationResultSchema$1, R as remindMissingAutomationResult, _ as slackChannelMessageTrigger, a as EVENT_WAIT_MS, b as workspaceSnapshotFinishedEvent, d as getEventCatalog, n as markConversationsStopped, o as enqueueEventNotification, p as requireSlackChannelMessageScope, t as getConversationStoppedAtMs, v as WORKSPACE_SNAPSHOT_FAILED_EVENT, x as workspaceSnapshotWatch, y as WORKSPACE_SNAPSHOT_READY_EVENT } from "./session-stop-HKl0ECX0.js";
27
27
  import { C as userCustomizationTextSchema, b as webMessageId, g as formatUserInputAnswer, h as buttonUserInputField, m as USER_INPUT_NOTE, p as USER_INPUT_LIMITS, s as sumUtcHoursIntoSixHours, v as normalizeUserInputAnswer, x as USER_CUSTOMIZATION_MAX_CHARS, y as userInputRequestSchema } from "./reporting-window-DgHgwQB3.js";
28
- import { a as setSnapshotSpanAttributes, i as postinstall, n as resolve$1, r as dependencies, s as getSandboxResources } from "./resolve-D44G1jJm.js";
29
- import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-CEeLYkSj.js";
30
- import { D as stopConversationWork, n as appendAndEnqueueExclusiveInboundMessage, r as appendAndEnqueueInboundMessage } from "./store-BF2XEeQz.js";
31
- import { _ as getMcpProviderErrorAttributes, a as serviceAccountOnly, c as createMcpServiceAccountClientProvider, d as PluginMcpClient, f as McpProviderError, g as getMcpAwareTelemetryMessage, h as getMcpAwareErrorType, i as serviceAccountAllows, l as getMcpServiceAccountCredentials, m as McpToolError, p as McpServiceAccountRequiredError, u as McpAuthorizationRequiredError, v as toMcpProviderError } from "./service-account-CHO3YXpt.js";
32
- import { A as SCHEDULED_AUTOMATION_SYSTEM_ACTOR, D as taskOutcomeInputSchema, E as automationTitleSchema, F as scheduleIntentSchema, I as weekdaySchema, N as ScheduleIntentError, P as compileScheduleIntent, T as automationInstructionToolSchema, k as eventAutomationTriggerSchema, l as automationListQuerySchema } from "./automation-Dwh_xcqM.js";
28
+ import { a as setSnapshotSpanAttributes, i as postinstall, n as resolve$1, r as dependencies, s as getSandboxResources } from "./resolve-CuuxNfWG.js";
29
+ import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-CjB97sii.js";
30
+ import { D as stopConversationWork, n as appendAndEnqueueExclusiveInboundMessage, r as appendAndEnqueueInboundMessage } from "./store-CPF2kzbu.js";
31
+ import { _ as getMcpProviderErrorAttributes, a as serviceAccountOnly, c as createMcpServiceAccountClientProvider, d as PluginMcpClient, f as McpProviderError, g as getMcpAwareTelemetryMessage, h as getMcpAwareErrorType, i as serviceAccountAllows, l as getMcpServiceAccountCredentials, m as McpToolError, p as McpServiceAccountRequiredError, u as McpAuthorizationRequiredError, v as toMcpProviderError } from "./service-account-H45LIAG2.js";
32
+ import { A as SCHEDULED_AUTOMATION_SYSTEM_ACTOR, D as taskOutcomeInputSchema, E as automationTitleSchema, F as scheduleIntentSchema, I as weekdaySchema, N as ScheduleIntentError, P as compileScheduleIntent, T as automationInstructionToolSchema, k as eventAutomationTriggerSchema, l as automationListQuerySchema } from "./automation-CV7g5h3A.js";
33
33
  import { n as privateTraceResultAttributes } from "./private-trace-result-CE7_lEdq.js";
34
34
  import { streamSimple } from "@earendil-works/pi-ai/compat";
35
35
  import { createGatewayProvider } from "@ai-sdk/gateway";
36
- import { generateText } from "ai";
36
+ import { NoImageGeneratedError, generateImage, generateText } from "ai";
37
37
  import path from "node:path";
38
38
  import { isDeepStrictEqual } from "node:util";
39
39
  import { z } from "zod";
40
40
  import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
41
41
  import { and, asc, count, desc, eq, exists, gt, gte, inArray, isNotNull, isNull, lt, lte, ne, notExists, notInArray, or, sql } from "drizzle-orm";
42
- import { PluginToolInputError, createWebSource, eventMatchFieldSchema, eventReachSchema, eventSchema, eventTriggerText, localDestinationSchema, missingToolAnnotationKeys, ownedObjectAnnotationSchema, sourceSchema, stableEventMatchKey, watchResultSchema } from "@sentry/junior-plugin-api";
42
+ import { PluginToolInputError, createWebSource, eventMatchFieldSchema, eventMatchSchema, eventReachSchema, eventSchema, eventTriggerText, localDestinationSchema, missingToolAnnotationKeys, ownedObjectAnnotationSchema, sourceSchema, stableEventMatchKey, watchResultSchema } from "@sentry/junior-plugin-api";
43
43
  import { alias } from "drizzle-orm/pg-core";
44
44
  import { Agent, estimateContextTokens } from "@earendil-works/pi-agent-core";
45
45
  import fs from "node:fs/promises";
@@ -847,7 +847,7 @@ function shouldEmitDevAgentTrace() {
847
847
  return process.env.NODE_ENV === "development";
848
848
  }
849
849
  // src/chat/agent-invocations/blocked-report.ts
850
- /** Blocked report of a child agent. See "Delivery rules" in AGENTS.md. */
850
+ /** Blocked report of a child agent. */
851
851
  /** Model-facing tool that ends a child agent run as blocked. */
852
852
  const REPORT_BLOCKED_TOOL_NAME = "reportBlocked";
853
853
  const blockedReportSchema = z.object({ missing: z.string().trim().min(1) });
@@ -2217,8 +2217,8 @@ function buildTurnContextPrompt(params) {
2217
2217
  /**
2218
2218
  * Conversation notes: short facts that core attaches to a Conversation without
2219
2219
  * starting a Turn. The next Turn shows them to the model in its runtime turn
2220
- * context. A failed Turn also becomes a note for the next Turn. See
2221
- * "Conversation notes" in AGENTS.md.
2220
+ * context. A failed Turn also becomes a note for the next Turn. A note is
2221
+ * context, never user intent, and only core adds one.
2222
2222
  */
2223
2223
  /**
2224
2224
  * Tell the next Turn that a Turn failed. Pi drops a failed assistant message
@@ -6647,7 +6647,6 @@ async function enrichImagePrompt(rawPrompt) {
6647
6647
  async function shrinkImage(image, maxBytes) {
6648
6648
  const { default: sharp } = await import("sharp");
6649
6649
  const { hasAlpha } = await sharp(image.data).metadata();
6650
- const mimeType = hasAlpha ? "image/webp" : "image/jpeg";
6651
6650
  for (const edge of RESIZE_LONGEST_EDGES) {
6652
6651
  const resized = sharp(image.data).rotate().resize({
6653
6652
  width: edge,
@@ -6659,8 +6658,7 @@ async function shrinkImage(image, maxBytes) {
6659
6658
  if (data.byteLength <= maxBytes) return {
6660
6659
  image: {
6661
6660
  path: image.path,
6662
- data,
6663
- mimeType
6661
+ data
6664
6662
  },
6665
6663
  width: info.width,
6666
6664
  height: info.height
@@ -6710,17 +6708,6 @@ function extensionForMediaType$1(mediaType) {
6710
6708
  if (mediaType === "image/gif") return "gif";
6711
6709
  return "bin";
6712
6710
  }
6713
- function parseImageGenerationError(status, body, model) {
6714
- if (!body) return `image generation failed: ${status}`;
6715
- try {
6716
- const message = JSON.parse(body).error?.message?.trim();
6717
- if (!message) return `image generation failed: ${status} ${body}`;
6718
- if (message.includes("not an image model")) return `image generation failed: configured model "${model}" is not an image generation model. Set AI_IMAGE_MODEL to a compatible image model.`;
6719
- return `image generation failed: ${status} ${message}`;
6720
- } catch {
6721
- return `image generation failed: ${status} ${body}`;
6722
- }
6723
- }
6724
6711
  /** Create the image tool with sandbox artifact guidance matched to file-send capability. */
6725
6712
  function createImageGenerateTool(hooks, options, deps = {}) {
6726
6713
  return zodTool({
@@ -6737,8 +6724,10 @@ function createImageGenerateTool(hooks, options, deps = {}) {
6737
6724
  }),
6738
6725
  outputSchema: imageGenerateOutputSchema,
6739
6726
  execute: async ({ prompt, images = [] }) => {
6740
- const fetchImpl = deps.fetch ?? fetch;
6741
- const apiKey = await getGatewayApiKey();
6727
+ const gateway = createGatewayProvider({
6728
+ apiKey: await getGatewayApiKey(),
6729
+ ...deps.fetch ? { fetch: deps.fetch } : void 0
6730
+ });
6742
6731
  const model = options.modelId;
6743
6732
  const { images: inputImages, resized } = await fitImagesToRequest(await Promise.all(images.map(async (path) => ({
6744
6733
  path,
@@ -6749,58 +6738,26 @@ function createImageGenerateTool(hooks, options, deps = {}) {
6749
6738
  resize_notice: "Some input images were too large for the image model, so they were resized first. Tell the user which images were resized."
6750
6739
  } : {};
6751
6740
  const enrichedPrompt = inputImages.length > 0 ? prompt : await enrichImagePrompt(prompt);
6752
- const content = inputImages.length > 0 ? [{
6753
- type: "text",
6754
- text: enrichedPrompt
6755
- }, ...inputImages.map((image) => ({
6756
- type: "image_url",
6757
- image_url: { url: `data:${image.mimeType};base64,${image.data.toString("base64")}` }
6758
- }))] : enrichedPrompt;
6759
- const response = await fetchImpl("https://ai-gateway.vercel.sh/v1/chat/completions", {
6760
- method: "POST",
6761
- headers: {
6762
- "content-type": "application/json",
6763
- authorization: `Bearer ${apiKey}`
6764
- },
6765
- body: JSON.stringify({
6766
- model,
6767
- messages: [{
6768
- role: "user",
6769
- content
6770
- }],
6771
- modalities: ["image"]
6772
- })
6773
- });
6774
- if (!response.ok) {
6775
- const text = await response.text();
6776
- throw new Error(parseImageGenerationError(response.status, text, model));
6777
- }
6778
- const payload = await response.json();
6779
- const uploads = [];
6780
- const generatedImages = payload.choices?.[0]?.message?.images ?? [];
6781
- for (const [index, image] of generatedImages.entries()) {
6782
- let bytes = null;
6783
- let mimeType = "image/png";
6784
- const url = image.image_url?.url;
6785
- if (typeof url === "string" && url.startsWith("data:")) {
6786
- const match = url.match(/^data:([^;,]+);base64,(.+)$/);
6787
- if (!match) continue;
6788
- mimeType = match[1] ?? mimeType;
6789
- bytes = Buffer.from(match[2] ?? "", "base64");
6790
- } else if (typeof url === "string" && url.length > 0) {
6791
- const fetched = await fetchImpl(url);
6792
- if (!fetched.ok) continue;
6793
- mimeType = fetched.headers.get("content-type") ?? mimeType;
6794
- bytes = Buffer.from(await fetched.arrayBuffer());
6795
- }
6796
- if (!bytes) continue;
6797
- const extension = extensionForMediaType$1(mimeType);
6798
- uploads.push({
6799
- data: bytes,
6800
- filename: `generated-image-${Date.now()}-${index + 1}.${extension}`,
6801
- mimeType
6802
- });
6741
+ let generatedImages;
6742
+ try {
6743
+ ({images: generatedImages} = await generateImage({
6744
+ model: gateway.imageModel(model),
6745
+ prompt: inputImages.length > 0 ? {
6746
+ text: enrichedPrompt,
6747
+ images: inputImages.map((image) => image.data)
6748
+ } : enrichedPrompt,
6749
+ n: 1
6750
+ }));
6751
+ } catch (error) {
6752
+ if (NoImageGeneratedError.isInstance(error)) generatedImages = [];
6753
+ else if (error instanceof Error && error.message.includes("not an image model")) throw new Error(`image generation failed: configured model "${model}" is not an image generation model. Set AI_IMAGE_MODEL to an AI Gateway image model.`);
6754
+ else throw error;
6803
6755
  }
6756
+ const uploads = generatedImages.map((image, index) => ({
6757
+ data: Buffer.from(image.uint8Array),
6758
+ filename: `generated-image-${Date.now()}-${index + 1}.${extensionForMediaType$1(image.mediaType)}`,
6759
+ mimeType: image.mediaType
6760
+ }));
6804
6761
  if (uploads.length > 0) {
6805
6762
  const artifactRefs = await hooks.writeGeneratedArtifacts(uploads);
6806
6763
  return {
@@ -7448,10 +7405,13 @@ const messageAgentOutputSchema = juniorToolOutputSchema.extend({
7448
7405
  conversation_id: z.string().min(1).describe("Target conversation ID"),
7449
7406
  delivery: z.enum(["started", "queued"]).describe("started: the child agent starts work on the message now. queued: the target reads the message at its next step or after its current work.")
7450
7407
  });
7451
- /** Create the tool that sends one message to the parent or a child agent. */
7408
+ /**
7409
+ * Create the tool that sends one message to the parent, a child agent, or
7410
+ * another top-level agent.
7411
+ */
7452
7412
  function createMessageAgentTool(messageAgent) {
7453
7413
  return zodTool({
7454
- description: `Send a message to your parent agent or to a child agent that this conversation started. Use it to give an existing child agent more work, an answer, or a correction, and to send your parent agent a finding or a question while you keep working. A message to a child continues its history. If the child is still working, it reads the message at its next step or after its current work, and one result covers both. The result arrives later as a new task in this conversation; do not wait or poll for it. A message to your parent does not end your task. Use spawnAgent only to start a new child agent. Your text is not user approval. A child's action review reads the user's own messages in the root conversation.`,
7414
+ description: `Send a message to your parent agent or to a child agent that this conversation started. Use it to give an existing child agent more work, an answer, or a correction, and to send your parent agent a finding or a question while you keep working. A message to a child continues its history. If the child is still working, it reads the message at its next step or after its current work, and one result covers both. The result arrives later as a new task in this conversation; do not wait or poll for it. A message to your parent does not end your task. A top-level agent (its conversation is not a child agent, such as a Slack thread) can also message another top-level agent by its conversation_id, such as one from slackThreadRead. Both conversations must be public, or both private. A private one also needs the person in this Turn to be a member. The target runs the message as its own task and sends no result. Use spawnAgent only to start a new child agent. Your text is not user approval. A child's action review reads the user's own messages in the root conversation.`,
7455
7415
  exposure: "modelOnly",
7456
7416
  annotations: {
7457
7417
  destructiveHint: false,
@@ -7460,7 +7420,7 @@ function createMessageAgentTool(messageAgent) {
7460
7420
  readOnlyHint: false
7461
7421
  },
7462
7422
  inputSchema: z.object({
7463
- to: z.string().trim().min(1).describe(`"${PARENT_AGENT_TARGET}" for your parent agent, or the name or conversation_id of a child agent from spawnAgent or listAgents`),
7423
+ to: z.string().trim().min(1).describe(`"${PARENT_AGENT_TARGET}" for your parent agent, the name or conversation_id of a child agent from spawnAgent or listAgents, or the conversation_id of another top-level agent`),
7464
7424
  text: z.string().trim().min(1).max(MESSAGE_AGENT_TEXT_MAX_LENGTH).describe("Message text. Include all context the target needs.")
7465
7425
  }).strict(),
7466
7426
  outputSchema: messageAgentOutputSchema,
@@ -7477,6 +7437,7 @@ function createMessageAgentTool(messageAgent) {
7477
7437
  });
7478
7438
  } catch (error) {
7479
7439
  if (error instanceof AgentMessageTargetError) throw new ToolInputError(`${error.message}. Use listAgents to find a child agent.`, { cause: error });
7440
+ if (error instanceof AgentMessagePausedError) throw new ToolInputError(`${error.message}. Do not send it more messages. Tell the user instead.`, { cause: error });
7480
7441
  if (error instanceof AgentMessageQueueFullError) throw new ToolInputError(`${error.message}. Wait for its result, then send one message with everything it needs.`, { cause: error });
7481
7442
  if (error instanceof AgentInvocationLimitError) throw new ToolInputError(`${error.message}. Wait for active children to finish before you give more work.`, { cause: error });
7482
7443
  throw error;
@@ -8390,7 +8351,6 @@ function renderReviewItemMessage(item, change) {
8390
8351
  "",
8391
8352
  item.request && item.answer ? formatUserInputAnswer(item.request, item.answer) : "(no answer)"
8392
8353
  ].join("\n");
8393
- case "completed": return [`${personName$1(change.user)} marked ${label} done.`, ...question].join("\n");
8394
8354
  case "dismissed": return [`${personName$1(change.user)} dismissed ${label}.`, ...question].join("\n");
8395
8355
  case "taken": return [`${personName$1(change.user)} took ${label}. They are now its only assignee.`, ...question].join("\n");
8396
8356
  case "replied": return [
@@ -9019,7 +8979,7 @@ function createSearchEventTypesTool(catalog) {
9019
8979
  openWorldHint: false,
9020
8980
  readOnlyHint: true
9021
8981
  },
9022
- description: "Search the event types currently enabled without creating anything. Use only when the user asks what events are supported or the required resource type or event name is unclear. Includes plugin resources and core Workspace snapshot events. namespaces gives the reach of each event. With reach \"all\" or { anyOf }, an event can start an event automation for every resource of its namespace: leave out identifier and resourceType, and narrow it with match filters from that namespace. For { anyOf }, set at least one of those filters. With reach \"resource\", the automation needs an identifier. Use watchEvents for temporary updates in the current conversation; use createAutomation with an event trigger for durable automation. This tool does not watch a resource, create an automation, or list concrete resources.",
8982
+ description: "Search the event types currently enabled without creating anything. Use only when the user asks what events are supported or the required resource type or event name is unclear. Includes plugin resources and core Workspace snapshot events. namespaces gives the reach of each event. An event automation selects its events with match filters from the matchFields of its namespace. With reach \"all\", match can be left out to run for every resource. With { anyOf }, match must set at least one of those filters. With reach \"resource\", only watchEvents can use the event. Use watchEvents for temporary updates in the current conversation; use createAutomation with an event trigger for durable automation. This tool does not watch a resource, create an automation, or list concrete resources.",
9023
8983
  inputSchema: z.object({
9024
8984
  query: z.string().nullable().describe("Optional terms matching a namespace, resource type, or event name. Resource types that match more terms come first. Empty lists all enabled event types.").optional(),
9025
8985
  namespace: eventNamespaceSchema(catalog).nullable().describe("Optional enabled plugin namespace to search within.").optional(),
@@ -9461,11 +9421,16 @@ function requireCredentialStart(automation, isCreator) {
9461
9421
  if (isCreator || automation.credentialMode !== "creator") return;
9462
9422
  throw new AutomationEditError(`This ${"schedule" in automation ? "scheduled" : "event"} automation uses its creator's credentials. Only the creator can resume it or run it now. Switch it to system credentials first.`, "credentialMode");
9463
9423
  }
9464
- /** Apply shared title, instruction, outcome, and creator credential rules. */
9424
+ /**
9425
+ * Apply shared title, instruction, outcome, and creator credential rules.
9426
+ * Anyone who can manage the Automation can change any field except turning on
9427
+ * creator credentials. A non-creator change to execution or outcomes switches
9428
+ * it to system credentials, so creator credential output stays where the
9429
+ * creator approved.
9430
+ */
9465
9431
  async function editAutomationFields(current, input, isCreator, changesExecution) {
9466
9432
  const kind = "schedule" in current ? "scheduled" : "event";
9467
9433
  if (input.credentialMode === "creator" && !isCreator) throw new AutomationEditError(`Only the ${kind} automation creator can enable creator credential use.`, "credentialMode");
9468
- if (input.outcomes !== void 0 && !isCreator) throw new AutomationEditError(`Only the ${kind} automation creator can change message destinations.`, "outcomes");
9469
9434
  let outcomes = current.outcomes;
9470
9435
  if (input.outcomes !== void 0) {
9471
9436
  for (const outcome of input.outcomes) if (typeof outcome.destination !== "string" && !current.outcomes.some((stored) => isDeepStrictEqual(stored, outcome))) throw new AutomationEditError("Use the current Destination or the creator for a new message outcome.", "outcomes");
@@ -9479,10 +9444,11 @@ async function editAutomationFields(current, input, isCreator, changesExecution)
9479
9444
  destination: outcome.destination
9480
9445
  });
9481
9446
  }
9447
+ const changesOutcomes = !isDeepStrictEqual(outcomes, current.outcomes);
9482
9448
  return {
9483
9449
  title: input.title ?? current.title,
9484
9450
  task: { text: input.instruction ?? current.task.text },
9485
- credentialMode: changesExecution && !isCreator ? "system" : input.credentialMode ?? current.credentialMode,
9451
+ credentialMode: (changesExecution || changesOutcomes) && !isCreator ? "system" : input.credentialMode ?? current.credentialMode,
9486
9452
  outcomes
9487
9453
  };
9488
9454
  }
@@ -9535,14 +9501,32 @@ function registeredEventAutomationTriggerSchema(catalog) {
9535
9501
  const plugins = automationEventCatalog(catalog);
9536
9502
  return z.object({
9537
9503
  namespace: eventNamespaceSchema(plugins),
9538
- identifier: z.string().trim().min(1).max(300).nullable().describe("Exact resource identifier. Use null, with a null resourceType, to run for every resource of the namespace. Only events whose searchEventTypes reach is \"all\" or { anyOf } can run that way, and match must set one of the anyOf filters. When creating, omit it to run for every resource. When updating, omit it to keep the saved identifier.").optional(),
9539
- resourceType: registeredResourceTypeSchema(plugins).nullable().describe("Resource type of the identifier. Use null when identifier is null. When updating, omit it to keep the saved resource type.").optional(),
9540
9504
  label: z.string().trim().min(1).max(500),
9541
9505
  events: z.array(registeredEventTypeSchema(plugins)).min(1),
9542
- match: registeredEventMatchSchema()
9506
+ match: registeredEventMatchSchema().describe("Filters that select the events, from the matchFields of the namespace. Use the match of a subscribable tool result to select one resource, such as { repo, pullRequest } for one pull request. Leave it out to run for every resource, when the reach of each event allows that. A single value must equal the event data. A list means the event value may be any one entry. For a field with list: true, the event value is a list, and it matches when it includes the value or any entry.")
9543
9507
  }).strict();
9544
9508
  }
9545
9509
  /**
9510
+ * Filter changes of an update. A value sets the filter, and null removes it.
9511
+ * A filter that is left out keeps its saved value, so an update cannot make
9512
+ * a trigger broader by leaving out a filter.
9513
+ */
9514
+ const eventMatchChangesSchema = z.record(z.string(), z.union([eventMatchSchema.valueType, z.null()])).optional().describe("Filter changes, from the matchFields of the namespace. A value sets the filter, null removes it, and a filter that is left out keeps its saved value. When the namespace changes, the saved filters are dropped.");
9515
+ /** Build the event trigger that the update tool accepts. */
9516
+ function registeredEventAutomationTriggerChangesSchema(catalog) {
9517
+ return registeredEventAutomationTriggerSchema(catalog).extend({ match: eventMatchChangesSchema });
9518
+ }
9519
+ /**
9520
+ * Apply filter changes to the saved trigger, key by key. A change to another
9521
+ * namespace starts from no filters.
9522
+ */
9523
+ function changedEventMatch(saved, namespace, changes) {
9524
+ const match = { ...namespace === saved.namespace ? saved.match : void 0 };
9525
+ for (const [key, value] of Object.entries(changes ?? {})) if (value === null) delete match[key];
9526
+ else match[key] = value;
9527
+ return Object.keys(match).length > 0 ? match : void 0;
9528
+ }
9529
+ /**
9546
9530
  * Check a new event-automation trigger with the enabled plugins and return
9547
9531
  * it as it is saved. It names a resource type and an identifier, or neither.
9548
9532
  * The identifier gets its plugin's convention. Events and list values lose
@@ -9655,15 +9639,16 @@ function changesEventAutomationTrigger(current, next) {
9655
9639
  async function editEventAutomation(current, input, isCreator, catalog) {
9656
9640
  let trigger = current.trigger;
9657
9641
  if (input.trigger !== void 0) {
9658
- if (changesEventAutomationTrigger(current.trigger, input.trigger) || current.trigger.resourceType !== input.trigger.resourceType) try {
9659
- trigger = requireEventAutomationTrigger(catalog, input.trigger);
9642
+ const inputTrigger = slackChannelMessageTrigger(input.trigger, current.destination.channelId);
9643
+ if (changesEventAutomationTrigger(current.trigger, inputTrigger) || current.trigger.resourceType !== inputTrigger.resourceType) try {
9644
+ trigger = requireEventAutomationTrigger(catalog, inputTrigger);
9660
9645
  } catch (error) {
9661
9646
  if (error instanceof ToolInputError) throw new AutomationEditError(error.message, "trigger");
9662
9647
  throw error;
9663
9648
  }
9664
9649
  else trigger = {
9665
9650
  ...current.trigger,
9666
- label: input.trigger.label
9651
+ label: inputTrigger.label
9667
9652
  };
9668
9653
  }
9669
9654
  const changesExecution = input.instruction !== void 0 && input.instruction !== current.task.text || changesEventAutomationTrigger(current.trigger, trigger);
@@ -10173,7 +10158,7 @@ async function readConversationSourceTask(args) {
10173
10158
  // src/chat/automations/edit.ts
10174
10159
  /**
10175
10160
  * Web edits and lifecycle actions. Anyone who can read an Automation can
10176
- * manage it. Creator-only fields and credential rules still apply.
10161
+ * manage it. Creator credential rules still apply.
10177
10162
  */
10178
10163
  /** Owners and public Destination readers can manage. Others get not found. */
10179
10164
  async function requireEditableAutomation(user, id) {
@@ -10298,7 +10283,7 @@ async function restorableOutcomes(current, outcomes) {
10298
10283
  });
10299
10284
  continue;
10300
10285
  }
10301
- if (current.ownedByViewer && !task.outcomes.some((stored) => sameDefinitionValue(stored, outcome))) {
10286
+ if (!task.outcomes.some((stored) => sameDefinitionValue(stored, outcome))) {
10302
10287
  creatorDm ??= (await resolveTaskOutcomes([{
10303
10288
  action: "send_message",
10304
10289
  destination: "task_creator"
@@ -10778,15 +10763,23 @@ const automationSearchResultSchema = juniorToolOutputSchema.extend({
10778
10763
  automations: z.array(automationResultSchema),
10779
10764
  truncated: z.boolean()
10780
10765
  }).strict();
10766
+ const scheduleTriggerSchema = z.object({
10767
+ type: z.literal("schedule"),
10768
+ schedule: scheduleIntentSchema.describe("When the automation runs. The scheduler computes the exact next run from this intent and the server clock.")
10769
+ }).strict();
10781
10770
  /**
10782
- * Build the trigger input of the create and update tools: a schedule, or the
10783
- * events of an enabled plugin.
10771
+ * Build the trigger input of the create tool: a schedule, or the events of an
10772
+ * enabled plugin.
10784
10773
  */
10785
10774
  function automationTriggerSchema(catalog) {
10786
- return z.discriminatedUnion("type", [z.object({
10787
- type: z.literal("schedule"),
10788
- schedule: scheduleIntentSchema.describe("When the automation runs. The scheduler computes the exact next run from this intent and the server clock.")
10789
- }).strict(), registeredEventAutomationTriggerSchema(catalog).extend({ type: z.literal("event") })]);
10775
+ return z.discriminatedUnion("type", [scheduleTriggerSchema, registeredEventAutomationTriggerSchema(catalog).extend({ type: z.literal("event") })]);
10776
+ }
10777
+ /**
10778
+ * Build the trigger input of the update tool. An event trigger changes its
10779
+ * filters key by key.
10780
+ */
10781
+ function automationTriggerChangesSchema(catalog) {
10782
+ return z.discriminatedUnion("type", [scheduleTriggerSchema, registeredEventAutomationTriggerChangesSchema(catalog).extend({ type: z.literal("event") })]);
10790
10783
  }
10791
10784
  /** Return the Automation kind of a trigger input. */
10792
10785
  function triggerKind(trigger) {
@@ -10980,7 +10973,7 @@ function createAutomationTool(context, catalog) {
10980
10973
  creator: createdBy,
10981
10974
  destinationChannelId: destination.channelId
10982
10975
  });
10983
- const eventTrigger = input.trigger.type === "event" ? requireEventAutomationTrigger(catalog, input.trigger) : void 0;
10976
+ const eventTrigger = input.trigger.type === "event" ? requireEventAutomationTrigger(catalog, slackChannelMessageTrigger(input.trigger, destination.channelId)) : void 0;
10984
10977
  const toolCallId = options.toolCallId?.trim();
10985
10978
  if (!toolCallId) throw new Error("Automation creation requires a tool-call identity.");
10986
10979
  const id = automationIdForToolCall({
@@ -11104,147 +11097,17 @@ function createSearchAutomationsTool(context, catalog) {
11104
11097
  }
11105
11098
  });
11106
11099
  }
11107
- // src/chat/automations/tools/update.ts
11108
- /** Reject a move while an occurrence is pending or running. */
11109
- async function requireNoIncompleteRun(automationId) {
11110
- if ((await getDb().select({ id: juniorAutomationRuns.id }).from(juniorAutomationRuns).where(and(eq(juniorAutomationRuns.automationId, automationId), inArray(juniorAutomationRuns.status, ["pending", "running"]))).limit(1)).length > 0) throw new ToolInputError("Scheduled automation cannot be moved while an occurrence is already running. Try again after the current run finishes.");
11111
- }
11112
- /** Edit and save a scheduled Automation, including a move to this Conversation. */
11113
- async function updateScheduled(context, current, input, editor) {
11114
- const { task, ownedByViewer: isCreator } = current;
11115
- const destination = input.destination ? requireToolDestination(context) : void 0;
11116
- const moving = destination !== void 0 && !sameDestination(task.destination, destination);
11117
- if (destination && !moving && Object.keys(input).every((key) => key === "destination" || key === "credentialMode" && input.credentialMode === task.credentialMode)) return current;
11118
- if (moving) await requireNoIncompleteRun(task.id);
11119
- const next = await editScheduledAutomation(task, {
11120
- title: input.title,
11121
- instruction: input.instruction,
11122
- schedule: input.trigger?.type === "schedule" ? input.trigger.schedule : void 0,
11123
- credentialMode: input.credentialMode,
11124
- outcomes: moving ? void 0 : input.outcomes
11125
- }, isCreator, context.now?.() ?? Date.now());
11126
- if (moving) {
11127
- next.destination = destination;
11128
- next.conversationAccess = getConversationAccess(destination, context.visibility);
11129
- next.outcomes = input.outcomes === void 0 ? moveTaskOutcomes(task.outcomes, task.destination, destination) : await resolveTaskOutcomes(input.outcomes, destination, task.createdBy.slackUserId);
11130
- }
11131
- const saved = await saveScheduledAutomation(getDb(), next, automationRevision(task), editor);
11132
- if (moving) logInfo("scheduled_automation.move.completed", scheduledAutomationAttributes(saved));
11133
- return {
11134
- ...current,
11135
- task: saved
11136
- };
11137
- }
11138
- /** Edit and save an event Automation. */
11139
- async function updateEvent(current, input, editor, catalog) {
11140
- const { task } = current;
11141
- const trigger = input.trigger?.type === "event" ? input.trigger : void 0;
11142
- const next = await editEventAutomation(task, {
11143
- title: input.title,
11144
- instruction: input.instruction,
11145
- trigger: trigger ? {
11146
- ...trigger,
11147
- identifier: trigger.identifier === void 0 ? task.trigger.identifier : trigger.identifier ?? void 0,
11148
- resourceType: trigger.resourceType === void 0 ? task.trigger.resourceType : trigger.resourceType ?? void 0
11149
- } : void 0,
11150
- outcomes: input.outcomes,
11151
- credentialMode: input.credentialMode
11152
- }, current.ownedByViewer, catalog);
11153
- const saved = await saveEventAutomation(getDb(), next, automationRevision(task), editor);
11154
- if (!saved) throw new ToolInputError("Automation was not found.");
11155
- return {
11156
- ...current,
11157
- task: saved
11158
- };
11159
- }
11160
- /** Create the tool that edits, pauses, resumes, or moves one Automation. */
11161
- function createUpdateAutomationTool(context, catalog) {
11162
- const trigger = automationTriggerSchema(catalog);
11163
- return zodTool({
11164
- approvalMode: "review",
11165
- annotations: {
11166
- destructiveHint: true,
11167
- idempotentHint: true,
11168
- openWorldHint: true,
11169
- readOnlyHint: false
11170
- },
11171
- executionMode: "sequential",
11172
- description: "Update the title, instruction, trigger, outcomes, credential use, or status of a scheduled or event automation. Anyone in the workspace can update an automation in a public channel. Automations in private channels and DMs can be updated from there or by their creator. Only the creator can enable creator credentials, change outcomes, or move it. A non-creator who changes the instruction or trigger switches it to system credentials. The trigger type cannot change. Set destination to \"current_conversation\" to move a scheduled automation to the channel or DM of this conversation.",
11173
- inputSchema: z.object({
11174
- automationId: z.string().min(1).describe("ID of the automation, from searchAutomations or its dashboard URL."),
11175
- title: automationTitleSchema.optional(),
11176
- instruction: automationInstructionToolSchema.nullable().optional(),
11177
- trigger: trigger.nullable().describe("Complete replacement trigger of the same type. For an event trigger, omit identifier or resourceType to keep the saved one. Omit or use null to keep the trigger.").optional(),
11178
- 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(),
11179
- credentialMode: z.enum(["system", "creator"]).nullable().describe("Set creator to make the automation creator's credentials available, or system to disable them. Creator always means the automation's createdBy actor, never the current requester. Only that creator may enable creator mode. Omit or use null to leave unchanged.").optional(),
11180
- status: z.enum(["active", "paused"]).nullable().describe("Set active to resume a paused or blocked automation, or paused to stop it. When it uses its creator's credentials, only the creator can resume it. Omit or use null to leave unchanged.").optional(),
11181
- destination: z.literal("current_conversation").nullable().describe("Set to \"current_conversation\" to move the creator's scheduled automation into the channel or DM of this conversation. Omit to keep its destination.").optional()
11182
- }).strict(),
11183
- prepareArguments(args) {
11184
- return Object.fromEntries(Object.entries(args).filter(([, value]) => value != null));
11185
- },
11186
- outputSchema: automationToolResultSchema,
11187
- async execute({ automationId, status: requestedStatus, ...fields }) {
11188
- const input = fields;
11189
- const status = requestedStatus ?? void 0;
11190
- const caller = await requireToolCaller(context);
11191
- const current = await requireToolAutomation(caller, automationId);
11192
- if (input.trigger && triggerKind(input.trigger) !== current.kind) throw new ToolInputError(`This automation runs on ${current.kind === "scheduled" ? "a schedule" : "events"}. The trigger type cannot change. Create a new automation instead.`);
11193
- if (Object.keys(input).length === 0 && status === void 0) throw new ToolInputError("Automation update requires a change.");
11194
- if (current.kind === "scheduled" && current.task.status === "completed") throw new ToolInputError("Completed scheduled automations cannot be updated. Create a new automation instead.");
11195
- if (input.destination && !current.ownedByViewer) throw new ToolInputError("Only the automation creator can move this automation.");
11196
- if (input.destination && current.kind === "event") throw new ToolInputError("Event automations cannot move. Create a new event automation in this conversation instead.");
11197
- const editor = automationEditor(caller.viewer, current);
11198
- if (input.instruction !== void 0 && input.instruction !== current.task.task.text) requireInstructionReferences(input.instruction, {
11199
- creator: current.task.createdBy,
11200
- destinationChannelId: current.task.destination.channelId,
11201
- requester: editor,
11202
- previousInstruction: current.task.task.text
11203
- });
11204
- let saved = current;
11205
- if (Object.keys(input).length > 0) saved = current.kind === "scheduled" ? await updateScheduled(context, current, input, editor) : await updateEvent(current, input, editor, catalog);
11206
- if (status !== void 0 && status !== saved.task.status) saved = await changeAutomationLifecycle(saved, {
11207
- action: status === "paused" ? "pause" : "resume",
11208
- revision: automationRevision(saved.task)
11209
- });
11210
- return automationToolResult(context.conversationId, saved, catalog);
11211
- }
11100
+ // src/chat/conversations/destination-visibility.ts
11101
+ /** Read confirmed visibility from the current signal or persisted destination. */
11102
+ async function resolveDestinationVisibility(args) {
11103
+ if (args.visibility) return args.visibility;
11104
+ if (args.destination.platform === "local") return "private";
11105
+ return await getConversationStore().getDestinationVisibility({
11106
+ provider: "slack",
11107
+ providerDestinationId: args.destination.channelId,
11108
+ providerTenantId: args.destination.teamId
11212
11109
  });
11213
11110
  }
11214
- // src/chat/tools/definition.ts
11215
- /** Distinguish legacy TypeBox schemas from JSON Schema projected from Zod. */
11216
- function isTypeBoxInputSchema(schema) {
11217
- return typeof schema === "object" && schema !== null && Kind in schema;
11218
- }
11219
- /** Tool error text for a call to a tool that is disabled in this Turn. */
11220
- function disabledToolMessage(toolName, reason) {
11221
- return `${toolName} is disabled in this Turn. ${reason} Do not call it again in this Turn.`;
11222
- }
11223
- /**
11224
- * Disable tools for this Turn without removing them from the tool list.
11225
- *
11226
- * The tool adapter rejects their calls, and the Turn context lists them. An
11227
- * unset reason keeps the tools enabled.
11228
- */
11229
- function disableTools(tools, reason) {
11230
- if (!reason) return tools;
11231
- return Object.fromEntries(Object.entries(tools).map(([name, tool]) => [name, {
11232
- ...tool,
11233
- disabledReason: reason
11234
- }]));
11235
- }
11236
- // src/chat/tools/automations.ts
11237
- /** Build the Automation tools. A Turn that no person started cannot use them. */
11238
- function createAutomationTools(context, catalog) {
11239
- const automationContext = automationToolContext(context);
11240
- return disableTools({
11241
- createAutomation: createAutomationTool(automationContext, catalog),
11242
- searchAutomations: createSearchAutomationsTool(automationContext, catalog),
11243
- updateAutomation: createUpdateAutomationTool(automationContext, catalog),
11244
- deleteAutomation: createDeleteAutomationTool(automationContext, catalog),
11245
- runAutomationNow: createRunAutomationNowTool(automationContext, catalog)
11246
- }, automationToolsDisabledReason(context));
11247
- }
11248
11111
  // src/chat/slack/channel.ts
11249
11112
  const replySubtypeSchema = z.object({ subtype: z.string().optional() });
11250
11113
  /** List channel history using Slack-native, pre-validated timestamp bounds. */
@@ -11457,6 +11320,203 @@ async function resolveOptionalSlackChannelRef(input) {
11457
11320
  if (input.defaultChannelId) return { channelId: input.defaultChannelId };
11458
11321
  throw new ToolInputError("Provide `channel_id`, or use this tool in an active Slack channel context.");
11459
11322
  }
11323
+ // src/chat/automations/tools/update.ts
11324
+ /** Reject a move while an occurrence is pending or running. */
11325
+ async function requireNoIncompleteRun(automationId) {
11326
+ if ((await getDb().select({ id: juniorAutomationRuns.id }).from(juniorAutomationRuns).where(and(eq(juniorAutomationRuns.automationId, automationId), inArray(juniorAutomationRuns.status, ["pending", "running"]))).limit(1)).length > 0) throw new ToolInputError("Scheduled automation cannot be moved while an occurrence is already running. Try again after the current run finishes.");
11327
+ }
11328
+ /**
11329
+ * Resolve where a move goes. "current_conversation" is this Conversation. A
11330
+ * channel reference must name a public Slack channel in this workspace, and
11331
+ * Junior joins it so the Automation can post there.
11332
+ */
11333
+ async function requireMoveDestination(context, value) {
11334
+ const here = requireToolDestination(context);
11335
+ if (value === "current_conversation") return {
11336
+ destination: here,
11337
+ visibility: context.visibility
11338
+ };
11339
+ const teamId = parseSlackTeamId(here.teamId);
11340
+ if (!teamId) throw new ToolInputError("Slack workspace is unavailable.");
11341
+ const { channelId, channelName } = await resolveSlackChannelRef({
11342
+ field: "destination",
11343
+ value,
11344
+ teamId
11345
+ });
11346
+ if (channelId === here.channelId) return {
11347
+ destination: here,
11348
+ visibility: context.visibility
11349
+ };
11350
+ const access = await checkSlackChannelReadAccess({
11351
+ currentChannelIds: [],
11352
+ targetChannelId: channelId,
11353
+ teamId
11354
+ });
11355
+ if (!access.allowed) throw new ToolInputError(access.reason === "private" ? "Only a public channel can be named as a destination. To move an automation into a private channel or DM, ask from there." : access.error);
11356
+ if (access.isMember !== true) {
11357
+ const joined = await joinPublicChannelForRead({
11358
+ targetChannelId: channelId,
11359
+ ...channelName ? { channelName } : void 0
11360
+ });
11361
+ if (!joined.ok) throw new ToolInputError(joined.error);
11362
+ }
11363
+ const destination = {
11364
+ platform: "slack",
11365
+ teamId,
11366
+ channelId
11367
+ };
11368
+ const name = access.channelName ?? channelName;
11369
+ await getConversationStore().recordDestinationVisibility({
11370
+ destination,
11371
+ ...name ? { channelName: name } : void 0,
11372
+ visibility: "public"
11373
+ });
11374
+ return {
11375
+ destination,
11376
+ visibility: "public"
11377
+ };
11378
+ }
11379
+ /** Edit and save a scheduled Automation, including a move to a new Destination. */
11380
+ async function updateScheduled(context, current, input, editor) {
11381
+ const { task, ownedByViewer: isCreator } = current;
11382
+ const target = input.destination ? await requireMoveDestination(context, input.destination) : void 0;
11383
+ const destination = target?.destination;
11384
+ const moving = destination !== void 0 && !sameDestination(task.destination, destination);
11385
+ if (destination && !moving && Object.keys(input).every((key) => key === "destination" || key === "credentialMode" && input.credentialMode === task.credentialMode)) return current;
11386
+ if (moving) await requireNoIncompleteRun(task.id);
11387
+ const next = await editScheduledAutomation(task, {
11388
+ title: input.title,
11389
+ instruction: input.instruction,
11390
+ schedule: input.trigger?.type === "schedule" ? input.trigger.schedule : void 0,
11391
+ credentialMode: input.credentialMode,
11392
+ outcomes: moving ? void 0 : input.outcomes
11393
+ }, isCreator, context.now?.() ?? Date.now());
11394
+ if (moving) {
11395
+ next.destination = destination;
11396
+ next.conversationAccess = getConversationAccess(destination, target?.visibility);
11397
+ if (!isCreator) next.credentialMode = "system";
11398
+ next.outcomes = input.outcomes === void 0 ? moveTaskOutcomes(task.outcomes, task.destination, destination) : await resolveTaskOutcomes(input.outcomes, destination, task.createdBy.slackUserId);
11399
+ }
11400
+ const saved = await saveScheduledAutomation(getDb(), next, automationRevision(task), editor);
11401
+ if (moving) logInfo("scheduled_automation.move.completed", scheduledAutomationAttributes(saved));
11402
+ return {
11403
+ ...current,
11404
+ task: saved
11405
+ };
11406
+ }
11407
+ /** Edit and save an event Automation. */
11408
+ async function updateEvent(current, input, editor, catalog) {
11409
+ const { task } = current;
11410
+ const trigger = input.trigger?.type === "event" ? input.trigger : void 0;
11411
+ const next = await editEventAutomation(task, {
11412
+ title: input.title,
11413
+ instruction: input.instruction,
11414
+ trigger: trigger ? {
11415
+ ...trigger,
11416
+ ...trigger.namespace === task.trigger.namespace ? {
11417
+ identifier: task.trigger.identifier,
11418
+ resourceType: task.trigger.resourceType
11419
+ } : void 0,
11420
+ match: changedEventMatch(task.trigger, trigger.namespace, trigger.match)
11421
+ } : void 0,
11422
+ outcomes: input.outcomes,
11423
+ credentialMode: input.credentialMode
11424
+ }, current.ownedByViewer, catalog);
11425
+ const saved = await saveEventAutomation(getDb(), next, automationRevision(task), editor);
11426
+ if (!saved) throw new ToolInputError("Automation was not found.");
11427
+ return {
11428
+ ...current,
11429
+ task: saved
11430
+ };
11431
+ }
11432
+ /** Create the tool that edits, pauses, resumes, or moves one Automation. */
11433
+ function createUpdateAutomationTool(context, catalog) {
11434
+ const trigger = automationTriggerChangesSchema(catalog);
11435
+ return zodTool({
11436
+ approvalMode: "review",
11437
+ annotations: {
11438
+ destructiveHint: true,
11439
+ idempotentHint: true,
11440
+ openWorldHint: true,
11441
+ readOnlyHint: false
11442
+ },
11443
+ executionMode: "sequential",
11444
+ description: "Update the title, instruction, trigger, outcomes, credential use, or status of a scheduled or event automation. Anyone in the workspace can update anything on an automation in a public channel, including its outcomes and destination. Automations in private channels and DMs can be updated from there or by their creator, and only their creator can move them. Only the creator can enable creator credentials. A non-creator who changes the instruction, trigger, outcomes, or destination switches it to system credentials. The trigger type cannot change. Set destination to move a scheduled automation to this conversation or to a public channel.",
11445
+ inputSchema: z.object({
11446
+ automationId: z.string().min(1).describe("ID of the automation, from searchAutomations or its dashboard URL."),
11447
+ title: automationTitleSchema.optional(),
11448
+ instruction: automationInstructionToolSchema.nullable().optional(),
11449
+ trigger: trigger.nullable().describe("Replacement trigger of the same type. For an event trigger, match changes only the filters that it names. Omit or use null to keep the trigger.").optional(),
11450
+ 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(),
11451
+ credentialMode: z.enum(["system", "creator"]).nullable().describe("Set creator to make the automation creator's credentials available, or system to disable them. Creator always means the automation's createdBy actor, never the current requester. Only that creator may enable creator mode. Omit or use null to leave unchanged.").optional(),
11452
+ status: z.enum(["active", "paused"]).nullable().describe("Set active to resume a paused or blocked automation, or paused to stop it. When it uses its creator's credentials, only the creator can resume it. Omit or use null to leave unchanged.").optional(),
11453
+ destination: slackChannelRefParam.nullable().describe("Where to move a scheduled automation. Set \"current_conversation\" for the channel or DM of this conversation, or name a public Slack channel by id, mention, or known name. Omit to keep its destination.").optional()
11454
+ }).strict(),
11455
+ prepareArguments(args) {
11456
+ return Object.fromEntries(Object.entries(args).filter(([, value]) => value != null));
11457
+ },
11458
+ outputSchema: automationToolResultSchema,
11459
+ async execute({ automationId, status: requestedStatus, ...fields }) {
11460
+ const input = fields;
11461
+ const status = requestedStatus ?? void 0;
11462
+ const caller = await requireToolCaller(context);
11463
+ const current = await requireToolAutomation(caller, automationId);
11464
+ if (input.trigger && triggerKind(input.trigger) !== current.kind) throw new ToolInputError(`This automation runs on ${current.kind === "scheduled" ? "a schedule" : "events"}. The trigger type cannot change. Create a new automation instead.`);
11465
+ if (Object.keys(input).length === 0 && status === void 0) throw new ToolInputError("Automation update requires a change.");
11466
+ if (current.kind === "scheduled" && current.task.status === "completed") throw new ToolInputError("Completed scheduled automations cannot be updated. Create a new automation instead.");
11467
+ if (input.destination && current.kind === "event") throw new ToolInputError("Event automations cannot move. Create a new event automation in this conversation instead.");
11468
+ if (input.destination && !current.ownedByViewer && await resolveDestinationVisibility({ destination: current.task.destination }) !== "public") throw new ToolInputError("Only the creator can move an automation out of a private channel or DM.");
11469
+ const editor = automationEditor(caller.viewer, current);
11470
+ if (input.instruction !== void 0 && input.instruction !== current.task.task.text) requireInstructionReferences(input.instruction, {
11471
+ creator: current.task.createdBy,
11472
+ destinationChannelId: current.task.destination.channelId,
11473
+ requester: editor,
11474
+ previousInstruction: current.task.task.text
11475
+ });
11476
+ let saved = current;
11477
+ if (Object.keys(input).length > 0) saved = current.kind === "scheduled" ? await updateScheduled(context, current, input, editor) : await updateEvent(current, input, editor, catalog);
11478
+ if (status !== void 0 && status !== saved.task.status) saved = await changeAutomationLifecycle(saved, {
11479
+ action: status === "paused" ? "pause" : "resume",
11480
+ revision: automationRevision(saved.task)
11481
+ });
11482
+ return automationToolResult(context.conversationId, saved, catalog);
11483
+ }
11484
+ });
11485
+ }
11486
+ // src/chat/tools/definition.ts
11487
+ /** Distinguish legacy TypeBox schemas from JSON Schema projected from Zod. */
11488
+ function isTypeBoxInputSchema(schema) {
11489
+ return typeof schema === "object" && schema !== null && Kind in schema;
11490
+ }
11491
+ /** Tool error text for a call to a tool that is disabled in this Turn. */
11492
+ function disabledToolMessage(toolName, reason) {
11493
+ return `${toolName} is disabled in this Turn. ${reason} Do not call it again in this Turn.`;
11494
+ }
11495
+ /**
11496
+ * Disable tools for this Turn without removing them from the tool list.
11497
+ *
11498
+ * The tool adapter rejects their calls, and the Turn context lists them. An
11499
+ * unset reason keeps the tools enabled.
11500
+ */
11501
+ function disableTools(tools, reason) {
11502
+ if (!reason) return tools;
11503
+ return Object.fromEntries(Object.entries(tools).map(([name, tool]) => [name, {
11504
+ ...tool,
11505
+ disabledReason: reason
11506
+ }]));
11507
+ }
11508
+ // src/chat/tools/automations.ts
11509
+ /** Build the Automation tools. A Turn that no person started cannot use them. */
11510
+ function createAutomationTools(context, catalog) {
11511
+ const automationContext = automationToolContext(context);
11512
+ return disableTools({
11513
+ createAutomation: createAutomationTool(automationContext, catalog),
11514
+ searchAutomations: createSearchAutomationsTool(automationContext, catalog),
11515
+ updateAutomation: createUpdateAutomationTool(automationContext, catalog),
11516
+ deleteAutomation: createDeleteAutomationTool(automationContext, catalog),
11517
+ runAutomationNow: createRunAutomationNowTool(automationContext, catalog)
11518
+ }, automationToolsDisabledReason(context));
11519
+ }
11460
11520
  // src/chat/slack/tools/channel-join.ts
11461
11521
  /** Create a tool that joins one public Slack channel. */
11462
11522
  function createSlackChannelJoinTool(context) {
@@ -12388,7 +12448,6 @@ const SLACK_REVIEW_ITEM_VIEW_ID = "junior_review_item";
12388
12448
  const ACTION_ID_PREFIX = "junior_review_item:";
12389
12449
  const CHOICE_ACTION_ID_PREFIX = `${ACTION_ID_PREFIX}choice:`;
12390
12450
  const ANSWER_ACTION_ID = `${ACTION_ID_PREFIX}answer`;
12391
- const DONE_ACTION_ID = `${ACTION_ID_PREFIX}done`;
12392
12451
  const DISMISS_ACTION_ID = `${ACTION_ID_PREFIX}dismiss`;
12393
12452
  const MAX_BODY_CHARS = 3e3;
12394
12453
  const MAX_CONTEXT_CHARS = 3e3;
@@ -12424,13 +12483,7 @@ function controls(item) {
12424
12483
  text: plainText("Dismiss"),
12425
12484
  value: item.id
12426
12485
  };
12427
- if (item.kind === "notice" || !item.request) return [{
12428
- type: "button",
12429
- action_id: DONE_ACTION_ID,
12430
- style: "primary",
12431
- text: plainText("Done"),
12432
- value: item.id
12433
- }, dismiss];
12486
+ if (item.kind === "notice" || !item.request) return [dismiss];
12434
12487
  const buttonField = buttonUserInputField(item.request);
12435
12488
  if (!buttonField) return [{
12436
12489
  type: "button",
@@ -12475,8 +12528,8 @@ function closedNote(item) {
12475
12528
  /**
12476
12529
  * Render a Review item as one Slack message. An open item shows its
12477
12530
  * controls: one button per choice when one click can answer it, an Answer
12478
- * button that opens a form otherwise, and Done for a notice. A closed item
12479
- * shows a note instead, so nobody can act on it again.
12531
+ * button that opens a form otherwise, and only Dismiss for a notice. A
12532
+ * closed item shows a note instead, so nobody can act on it again.
12480
12533
  */
12481
12534
  function buildSlackReviewItemCard(item) {
12482
12535
  const text = itemText(item);
@@ -12544,10 +12597,6 @@ function parseSlackReviewItemClick(action) {
12544
12597
  kind: "answer",
12545
12598
  itemId: value
12546
12599
  };
12547
- if (action.action_id === DONE_ACTION_ID) return {
12548
- kind: "done",
12549
- itemId: value
12550
- };
12551
12600
  if (action.action_id === DISMISS_ACTION_ID) return {
12552
12601
  kind: "dismiss",
12553
12602
  itemId: value
@@ -21664,4 +21713,4 @@ function createAgentRunner(execute, options) {
21664
21713
  return await execute(nextRun, streamFn);
21665
21714
  } };
21666
21715
  }
21667
- export { effectiveTaskOutcomes as $, verifySlackSignature as A, RetryableDeliveryError as At, activateViewerAutomationVersion as B, syncReviewItemSlackCards as C, conversationSubtreeColumns as Ct, parseSlackReviewItemFormTarget as D, withoutSavedSteeringMessages as Dt, parseSlackReviewItemClick as E, buildSteeringPiMessage as Et, getConversationInfo as F, ViewerTaskNotFoundError as G, deleteViewerTask as H, joinPublicChannel as I, readViewerAutomationRuns as J, readConversationSourceTask as K, listThreadReplies as L, readSlackRawMessageContext as M, toolInvocationDestination as Mt, resolveSlackChannelIdFromMessage as N, readBlockedReport as Nt, parseSlackUserInputSubmission as O, readUserCustomization as Ot, resolveSlackChannelIdFromThreadId as P, shouldEmitDevAgentTrace as Pt, AutomationEditError as Q, logScheduledAutomationRunSkipped as R, readSlackReviewItemCardConversationId as S, conversationSubtree as St, buildSlackReviewItemModal as T, ingestEvent as Tt, readViewerAutomationEdit as U, changeViewerAutomationLifecycle as V, updateViewerAutomation as W, readViewerAutomationVersions as X, readViewerAutomationSummary as Y, readViewerAutomations as Z, unpublishedArtifactHeaders as _, stopConversationSession as _t, finalizeMcpAuthorization as a, conversationTurnIdForMessage as at, renderBlockText as b, listChildAgents as bt, deleteMcpServerSessionId as c, recordWebConversationActivity as ct, getMcpStoredOAuthCredentials as d, decodeInputImages as dt, listOwnedWatches as et, putMcpStoredOAuthCredentials as f, collectAttachmentGarbage as ft, readPublicArtifact as g, stopChildAgentsOf as gt, publicArtifactHeaders as h, storeAttachment as ht, buildAuthPauseResponse as i, appendAndEnqueueWebMessage as it, parseSlackThreadId as j, actorFromRun as jt, runWithSlackInstallation as k, saveUserCustomization as kt, deleteMcpStoredOAuthCredentials as l, webActorFromEmail as lt, readStats as m, requestAttachmentDeletion as mt, executeAgentRun as n, renderReviewItemMessage as nt, deleteMcpAuthSession as o, createAndEnqueueConversation as ot, latestProgressStatus as p, readLiveAttachment as pt, readViewerAutomationExecutions as q, createContextCompactor as r, acceptsWebMessages as rt, deleteMcpAuthSessionsForUserProvider as s, createConversationId as st, createAgentRunner as t, deliverReviewItemChange as tt, getMcpAuthSession as u, resolveConversationDestination as ut, lookupSlackUserProfile as v, recordSubagentEnded as vt, SLACK_REVIEW_ITEM_VIEW_ID as w, processWorkspaceSnapshotJob as wt, createSlackReviewItemShow as x, listUnreportedSiblingAgents as xt, renderAttachmentText as y, recordSubagentStarted as yt, scheduledAutomationRunAttributes as z };
21716
+ export { AutomationEditError as $, verifySlackSignature as A, saveUserCustomization as At, scheduledAutomationRunAttributes as B, syncReviewItemSlackCards as C, conversationSubtree as Ct, parseSlackReviewItemFormTarget as D, buildSteeringPiMessage as Dt, parseSlackReviewItemClick as E, ingestEvent as Et, getConversationInfo as F, shouldEmitDevAgentTrace as Ft, updateViewerAutomation as G, changeViewerAutomationLifecycle as H, joinPublicChannel as I, readViewerAutomationExecutions as J, ViewerTaskNotFoundError as K, listThreadReplies as L, readSlackRawMessageContext as M, actorFromRun as Mt, resolveSlackChannelIdFromMessage as N, toolInvocationDestination as Nt, parseSlackUserInputSubmission as O, withoutSavedSteeringMessages as Ot, resolveSlackChannelIdFromThreadId as P, readBlockedReport as Pt, readViewerAutomations as Q, resolveDestinationVisibility as R, readSlackReviewItemCardConversationId as S, listUnreportedSiblingAgents as St, buildSlackReviewItemModal as T, processWorkspaceSnapshotJob as Tt, deleteViewerTask as U, activateViewerAutomationVersion as V, readViewerAutomationEdit as W, readViewerAutomationSummary as X, readViewerAutomationRuns as Y, readViewerAutomationVersions as Z, unpublishedArtifactHeaders as _, stopChildAgentsOf as _t, finalizeMcpAuthorization as a, appendAndEnqueueWebMessage as at, renderBlockText as b, recordSubagentStarted as bt, deleteMcpServerSessionId as c, createConversationId as ct, getMcpStoredOAuthCredentials as d, resolveConversationDestination as dt, effectiveTaskOutcomes as et, putMcpStoredOAuthCredentials as f, decodeInputImages as ft, readPublicArtifact as g, storeAttachment as gt, publicArtifactHeaders as h, requestAttachmentDeletion as ht, buildAuthPauseResponse as i, acceptsWebMessages as it, parseSlackThreadId as j, RetryableDeliveryError as jt, runWithSlackInstallation as k, readUserCustomization as kt, deleteMcpStoredOAuthCredentials as l, recordWebConversationActivity as lt, readStats as m, readLiveAttachment as mt, executeAgentRun as n, deliverReviewItemChange as nt, deleteMcpAuthSession as o, conversationTurnIdForMessage as ot, latestProgressStatus as p, collectAttachmentGarbage as pt, readConversationSourceTask as q, createContextCompactor as r, renderReviewItemMessage as rt, deleteMcpAuthSessionsForUserProvider as s, createAndEnqueueConversation as st, createAgentRunner as t, listOwnedWatches as tt, getMcpAuthSession as u, webActorFromEmail as ut, lookupSlackUserProfile as v, stopConversationSession as vt, SLACK_REVIEW_ITEM_VIEW_ID as w, conversationSubtreeColumns as wt, createSlackReviewItemShow as x, listChildAgents as xt, renderAttachmentText as y, recordSubagentEnded as yt, logScheduledAutomationRunSkipped as z };