@sentry/junior 0.232.0 → 0.234.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 (55) hide show
  1. package/dist/{agent-hooks-n9qfV697.js → agent-hooks-BcH6E48y.js} +89 -12
  2. package/dist/{agent-runner-D_FVvrwA.js → agent-runner-DUkGlK89.js} +224 -768
  3. package/dist/api/schema.d.ts +538 -2
  4. package/dist/api/schema.js +4 -4
  5. package/dist/api.js +230 -19
  6. package/dist/app.js +31 -21
  7. package/dist/{briefs-B7wDx-LM.js → briefs-lyAQbDxJ.js} +4 -4
  8. package/dist/{catalog-runtime-FU8TmK-W.js → catalog-runtime-BSgsPVnr.js} +1 -1
  9. package/dist/{checkpoint-DuA1tIcd.js → checkpoint-BNfXlq_1.js} +7 -7
  10. package/dist/cli/chat.js +13 -13
  11. package/dist/cli/check.js +3 -3
  12. package/dist/cli/main.js +1 -1
  13. package/dist/cli/plugins.js +3 -3
  14. package/dist/cli/snapshot-create.js +3 -3
  15. package/dist/cli/upgrade.js +3 -3
  16. package/dist/{client-BMqP14EE.js → client-BX5lJRqW.js} +2 -2
  17. package/dist/{config-Q7ayKDYh.js → config-DuCzUoCo.js} +2 -2
  18. package/dist/{conversation-DsuDtoYR.js → conversation-CPjHYOV5.js} +13 -2
  19. package/dist/{conversation-privacy-DNNsgprf.js → conversation-privacy-CSxvdSoV.js} +1 -1
  20. package/dist/{credential-sync-DbgAGIhd.js → credential-sync-BO4DfH9a.js} +13 -13
  21. package/dist/{executor-DDonSWxn.js → executor--jWf-Nnm.js} +1 -1
  22. package/dist/{images-Bj8PcvZ5.js → images-BaL19wFC.js} +3 -3
  23. package/dist/instrumentation.js +2 -2
  24. package/dist/{job-queue-Dqc7qQbR.js → job-queue-cwHVOwVZ.js} +1 -1
  25. package/dist/{logging-X4U7SInw.js → logging-Cox1Mgcv.js} +1 -1
  26. package/dist/nitro.js +3 -3
  27. package/dist/{oauth-authorization-CetcZcwQ.js → oauth-authorization-CC8a1JKP.js} +1 -1
  28. package/dist/{oauth-callback-server-o34WSoGm.js → oauth-callback-server-Z7QSt416.js} +2 -2
  29. package/dist/{pending-auth-XoxEa4O5.js → pending-auth-DQhC3eSR.js} +1 -1
  30. package/dist/{profile-DzqqgpDo.js → profile-BdApelHS.js} +4 -4
  31. package/dist/{prompt-D5RPLy92.js → prompt-3D0sSbTC.js} +1 -1
  32. package/dist/{registry-RrIfDnkS.js → registry-DiY6B8CN.js} +1 -1
  33. package/dist/{resolve-Ccu6uGtp.js → resolve-VpJoiDpc.js} +2 -2
  34. package/dist/{runner-DDQCEV1P.js → runner-BIMegnPE.js} +9 -9
  35. package/dist/{sandbox-egress-signals-CT3xOtNG.js → sandbox-egress-signals-Cbtxe492.js} +1 -1
  36. package/dist/{schema-B-Taar1W.js → schema-CxwnlTDN.js} +4 -117
  37. package/dist/{skills-NB_I9BuR.js → skills-WttpPJUy.js} +2 -2
  38. package/dist/{spawn-DmwIhiNu.js → spawn-DhfMKt4y.js} +2 -2
  39. package/dist/{task-queue-B5CERq_E.js → task-queue-CZqUNJD0.js} +3 -3
  40. package/dist/{task-runner-COivEGS3.js → task-runner-BZaqoUCT.js} +16 -13
  41. package/dist/{thread-state-C7yyfiVM.js → thread-state-8tXZhu0y.js} +5 -5
  42. package/dist/{turn-failure-response-D990svo-.js → turn-failure-response-DFJ9L9NQ.js} +8 -8
  43. package/dist/{turn-lifecycle-C-LUNJC6.js → turn-lifecycle-WbpTUQ_r.js} +1 -1
  44. package/dist/{turn-session-routing-CoF0GbaY.js → turn-session-routing--EhI76rH.js} +1 -1
  45. package/dist/types-Du-HbEun.js +621 -0
  46. package/dist/{validation-f0y71ety.js → validation-0F9IgG3b.js} +1 -1
  47. package/dist/{version-CkRTIRkV.js → version-A7-RRBOE.js} +1 -1
  48. package/dist/version.js +1 -1
  49. package/dist/{web-authorization-BBwQsNCZ.js → web-authorization-BJggXofy.js} +6 -6
  50. package/dist/{web-input-Bkn-JGWd.js → web-input-B-siN5Dr.js} +212 -164
  51. package/dist/web-message-id-CVVCTWUb.js +464 -0
  52. package/dist/{work-vWPq9P5x.js → work-CNc0ejGG.js} +7 -7
  53. package/dist/{worker-BgZog5Jp.js → worker-BHbqpUAu.js} +5 -5
  54. package/package.json +5 -5
  55. package/dist/web-message-id-xPI_EFSC.js +0 -239
@@ -1,33 +1,34 @@
1
- import { $t as recordFinishedTurnForAutomatedLimit, B as isListedScheduledAutomation, Cn as createUserTokenStore, D as renderBlockText, E as renderAttachmentText, F as resolveSlackChannelIdFromThreadId, G as readScheduledAutomation, I as listThreadReplies, K as saveScheduledAutomation, M as parseSlackThreadId, N as readSlackRawMessageContext, On as AuthorizationFlowDisabledError, P as resolveSlackChannelIdFromMessage, Pt as getConversationWorkState, Q as effectiveTaskOutcomes, Qt as maybePostAutomatedTurnLimitNotice, Tn as RetryableDeliveryError, V as listPublicScheduledAutomationsForTeams, W as parseScheduledAutomationRow, Z as eventAutomationTriggerAvailable, _n as truncateStatusText, _t as getEventCatalog, a as isExperimentalFeatureEnabled, at as listEventAutomationsCreatedBy, bn as removeReactionFromMessage, bt as appendAndEnqueueExclusiveInboundMessage, c as createContextCompactor, et as deleteEventAutomation, gn as normalizeSlackStatusText, gt as isEventMailboxMetadata, hn as normalizeSlackReplyMarkdown, ht as EVENT_WAIT_MS, it as listDeletedEventAutomationsCreatedBy, j as storeAttachment, kn as shouldEmitDevAgentTrace, mn as formatSlackLink, ot as listPublicEventAutomationsForTeams, pn as escapeSlackMrkdwnText, rt as getEventAutomation, s as executeAgentRun, t as createAgentRunner, tt as eventAutomationBelongsToUser, v as PluginCredentialFailureError, vn as addReactionToMessage, wn as buildSteeringPiMessage, xt as appendAndEnqueueInboundMessage, y as latestProgressStatus, yn as postSlackMessage } from "./agent-runner-D_FVvrwA.js";
2
- import { f as escapeXml, i as completeText, r as completeObject, v as isProviderRetryError } from "./client-BMqP14EE.js";
3
- import { C as withLogContext, I as isRecord, L as toOptionalNumber, R as toOptionalString, _ as setSentryUser, b as setTags, d as logException, f as logInfo, n as createChatSdkLogger, o as getActiveTraceId, p as logWarn, t as buildTurnFailureResponse, u as logError, v as setSpanAttributes, w as withSpan } from "./logging-X4U7SInw.js";
4
- import { a as getChatConfig, c as getSlackClientId, d as getSlackSigningSecret, h as normalizeSlackEmojiName, l as getSlackClientSecret, n as botConfig, s as getSlackBotToken } from "./config-Q7ayKDYh.js";
1
+ import { $ as deleteEventAutomation, Cn as postSlackMessage, D as renderBlockText, Dn as createUserTokenStore, E as renderAttachmentText, F as resolveSlackChannelIdFromThreadId, G as saveScheduledAutomationInLock, H as parseScheduledAutomationRow, I as listThreadReplies, M as parseSlackThreadId, Mn as AuthorizationFlowDisabledError, N as readSlackRawMessageContext, Nn as shouldEmitDevAgentTrace, On as buildSteeringPiMessage, P as resolveSlackChannelIdFromMessage, Rt as getConversationWorkState, Sn as addReactionToMessage, St as pluginEventCatalog, Tt as appendAndEnqueueInboundMessage, U as readScheduledAutomation, X as eventAutomationTriggerAvailable, Z as effectiveTaskOutcomes, _n as escapeSlackMrkdwnText, a as isExperimentalFeatureEnabled, bn as normalizeSlackStatusText, bt as getEventCatalog, c as createContextCompactor, et as eventAutomationBelongsToUser, it as parseEventAutomationRow, j as storeAttachment, kn as RetryableDeliveryError, nn as maybePostAutomatedTurnLimitNotice, nt as getEventAutomation, q as withScheduledAutomationLock, rn as recordFinishedTurnForAutomatedLimit, rt as listDeletedEventAutomationsCreatedBy, s as executeAgentRun, t as createAgentRunner, v as PluginCredentialFailureError, vn as formatSlackLink, vt as EVENT_WAIT_MS, wn as removeReactionFromMessage, wt as appendAndEnqueueExclusiveInboundMessage, xn as truncateStatusText, y as latestProgressStatus, yn as normalizeSlackReplyMarkdown, yt as isEventMailboxMetadata } from "./agent-runner-DUkGlK89.js";
2
+ import { f as escapeXml, i as completeText, r as completeObject, v as isProviderRetryError } from "./client-BX5lJRqW.js";
3
+ import { C as withLogContext, I as isRecord, L as toOptionalNumber, R as toOptionalString, _ as setSentryUser, b as setTags, d as logException, f as logInfo, n as createChatSdkLogger, o as getActiveTraceId, p as logWarn, t as buildTurnFailureResponse, u as logError, v as setSpanAttributes, w as withSpan } from "./logging-Cox1Mgcv.js";
4
+ import { a as getChatConfig, c as getSlackClientId, d as getSlackSigningSecret, h as normalizeSlackEmojiName, l as getSlackClientSecret, n as botConfig, s as getSlackBotToken } from "./config-DuCzUoCo.js";
5
5
  import { o as modelIdForProfile, r as defaultModelId } from "./model-profile-BwU8mqwD.js";
6
6
  import { a as createActor, b as parseSlackMessageTs, c as isActorUserId, l as isUserActor, o as createSlackActor, p as parseSlackChannelId, s as createSlackResumeActor, t as credentialContextForActor, u as parseActorUserId } from "./context-0Et9iVSB.js";
7
- import { $t as getConversationStore, B as downloadPrivateSlackFile, Bt as recordSlackAttachment, G as isDmChannel, H as getHeaderString, It as buildSentryConversationUrl, K as normalizeSlackConversationId, Lt as getSlackFileId, U as getSlackClient, Zt as getConversationEventStore, _n as createSlackDestination, _t as getDashboardObjectIconLink, en as getDb, gt as getDashboardConversationLink, ht as JUNIOR_THREAD_STATE_TTL_MS, o as getPluginSlackConversationLink, ot as cancelSubscriptions, q as runWithSlackInstallationToken, tn as getSqlExecutor, yn as requireSlackDestination, z as SlackActionError, zt as readAttachmentVisionSummary } from "./agent-hooks-n9qfV697.js";
7
+ import { $t as getConversationStore, B as downloadPrivateSlackFile, Bt as recordSlackAttachment, G as isDmChannel, H as getHeaderString, It as buildSentryConversationUrl, K as normalizeSlackConversationId, Lt as getSlackFileId, U as getSlackClient, Zt as getConversationEventStore, _n as createSlackDestination, _t as getDashboardObjectIconLink, en as getDb, gt as getDashboardConversationLink, ht as JUNIOR_THREAD_STATE_TTL_MS, o as getPluginSlackConversationLink, ot as cancelSubscriptions, q as runWithSlackInstallationToken, tn as getSqlExecutor, yn as requireSlackDestination, z as SlackActionError, zt as readAttachmentVisionSummary } from "./agent-hooks-BcH6E48y.js";
8
8
  import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
9
- import { D as juniorIdentities, O as juniorUsers, o as juniorSchedulerTasks } from "./executor-DDonSWxn.js";
10
- import { N as isVisionImageMediaType, j as readMessageAttachments, nt as instructionProvenanceFor } from "./conversation-DsuDtoYR.js";
11
- import { $ as fallbackShortTitle, A as commitAcceptedReply, B as recordAuthorizationCompleted, J as hydrateConversationMessages, N as loadConversationProjection, O as trimTrailingAssistantMessages, P as loadProjection, Q as resolveConversationTitle, W as recordSubscribedReplyRoute, Y as persistConversationMessages, Z as persistConversationMessageSummaries, a as completeTurnRecord, at as estimateConversationContextTokens, c as listTurnSummaries, ct as markConversationMessage, dt as turnHasReply, ft as upsertConversationMessage, i as abandonTurnRecord, it as createConversationMemoryService, j as commitMessages, l as recordTurnSummary, lt as normalizeConversationText, n as loadTurnCheckpoint, nt as appendThreadContextMessages, o as failTurnRecord, ot as getConversationMessageSlackTs, p as buildTurnErrorResponse, pt as estimateTextTokens, r as saveTurnCheckpoint, rt as buildConversationContext, s as getTurnRecord, st as isHumanConversationMessage, u as persistWithRetry, ut as recordDeliveredAssistantMessage } from "./checkpoint-DuA1tIcd.js";
12
- import { E as acquireActiveLock, O as withLock, T as MUTATION_LOCK_TTL_MS, w as getStateAdapter } from "./profile-DzqqgpDo.js";
13
- import { a as isPendingAuthLatestRequest, o as buildDeterministicAssistantMessageId, r as clearPendingAuth, s as buildDeterministicTurnId } from "./pending-auth-XoxEa4O5.js";
14
- import { _ as markTurnFailed, c as CooperativeTurnYieldError, d as getConversationTurnBoundaryError, f as isCooperativeTurnYieldError, g as markTurnCompleted, h as markTurnClosed, l as TurnInputCommitLostError, m as isTurnInputDeferredError, p as isTurnInputCommitLostError, r as getAssistantReplyText, s as ConversationTurnBoundaryError, u as TurnInputDeferredError, v as startActiveTurn } from "./oauth-authorization-CetcZcwQ.js";
15
- import { a as EVENT_SYSTEM_ACTOR, i as EVENT_MESSAGE_AUTHOR, o as isEventConversationMessage } from "./prompt-D5RPLy92.js";
16
- import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline } from "./job-queue-Dqc7qQbR.js";
17
- import { c as sumUtcHoursIntoSixHours, m as healthReportSchema, t as webMessageId } from "./web-message-id-xPI_EFSC.js";
18
- import { C as readAutomationExecutionStatusDays, D as readAutomationRuns, E as readAutomationExecutions, S as readAutomationExecutionHours, T as readAutomationExecutionSummaries, _ as markDispatchFailed, a as confirmDispatchMailboxAppend, b as readAutomationExecutionByConversationId, c as getDispatchInputMessageId, f as isTerminalDispatchStatus, g as markDispatchCompleted, h as markDispatchBlocked, i as claimDispatchMailboxAppend, l as getDispatchRecord, m as markDispatchAwaitingResume, n as scheduleSessionCompletedPluginTasks, s as getDispatchConversationId, t as processPluginTask, u as getDispatchTurnId, v as markDispatchRunning, w as readAutomationExecutionStatusHours, x as readAutomationExecutionDays, y as emptyAutomationRunWindows } from "./task-runner-COivEGS3.js";
19
- import { i as resolveAgentInvocationId, o as AgentRunError, s as executeTurn, t as createAgentInvocationWorker } from "./work-vWPq9P5x.js";
20
- import { n as getTurnLifecycle, t as ConversationTurnLifecycleService } from "./turn-lifecycle-C-LUNJC6.js";
21
- import { a as persistThreadState, c as configValueSchema, i as persistThreadRuntimeState, n as getPersistedSandboxState, o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState, t as getLocationConfigurationService } from "./thread-state-C7yyfiVM.js";
22
- import { a as getPausedTurnRequest, c as getTurnAuthorization, i as createPausedTurns, n as deleteWebAuthorization, s as wakePausedTurn, t as createWebAuthorization } from "./web-authorization-BBwQsNCZ.js";
23
- import { n as resolveTurnSessionRouting } from "./turn-session-routing-CoF0GbaY.js";
24
- import { t as bindSpawnAgent } from "./spawn-DmwIhiNu.js";
25
- import { a as persistAuthPauseTurnState, c as getTurnUserSlackMessageTs, d as loadPendingMessageCards, i as completeAuthPauseTurn, l as buildDeliveredTurnStatePatch, n as getAgentTurnDiagnosticsAttributes, o as getTurnUserMessage, r as requireTurnFailureEventId, s as getTurnUserReplyAttachmentContext, t as finalizeFailedTurnReplyWithEvent, u as getInterruptionMarker } from "./turn-failure-response-D990svo-.js";
26
- import { r as storeInputImages } from "./images-Bj8PcvZ5.js";
9
+ import { D as juniorIdentities, O as juniorUsers, l as juniorEventAutomations, o as juniorSchedulerTasks, s as juniorAutomationExecutions } from "./executor--jWf-Nnm.js";
10
+ import { N as isVisionImageMediaType, j as readMessageAttachments, nt as instructionProvenanceFor } from "./conversation-CPjHYOV5.js";
11
+ import { $ as fallbackShortTitle, A as commitAcceptedReply, B as recordAuthorizationCompleted, J as hydrateConversationMessages, N as loadConversationProjection, O as trimTrailingAssistantMessages, P as loadProjection, Q as resolveConversationTitle, W as recordSubscribedReplyRoute, Y as persistConversationMessages, Z as persistConversationMessageSummaries, a as completeTurnRecord, at as getConversationMessageSlackTs, c as listTurnSummaries, ct as normalizeConversationText, dt as upsertConversationMessage, ft as estimateTextTokens, i as abandonTurnRecord, it as estimateConversationContextTokens, j as commitMessages, l as recordTurnSummary, lt as recordDeliveredAssistantMessage, n as loadTurnCheckpoint, nt as buildConversationContext, o as failTurnRecord, ot as isHumanConversationMessage, p as buildTurnErrorResponse, r as saveTurnCheckpoint, rt as createConversationMemoryService, s as getTurnRecord, st as markConversationMessage, tt as appendThreadContextMessages, u as persistWithRetry, ut as turnHasReply } from "./checkpoint-BNfXlq_1.js";
12
+ import { E as acquireActiveLock, O as withLock, T as MUTATION_LOCK_TTL_MS, w as getStateAdapter } from "./profile-BdApelHS.js";
13
+ import { a as isPendingAuthLatestRequest, o as buildDeterministicAssistantMessageId, r as clearPendingAuth, s as buildDeterministicTurnId } from "./pending-auth-DQhC3eSR.js";
14
+ import { _ as markTurnFailed, c as CooperativeTurnYieldError, d as getConversationTurnBoundaryError, f as isCooperativeTurnYieldError, g as markTurnCompleted, h as markTurnClosed, l as TurnInputCommitLostError, m as isTurnInputDeferredError, p as isTurnInputCommitLostError, r as getAssistantReplyText, s as ConversationTurnBoundaryError, u as TurnInputDeferredError, v as startActiveTurn } from "./oauth-authorization-CC8a1JKP.js";
15
+ import { a as EVENT_SYSTEM_ACTOR, i as EVENT_MESSAGE_AUTHOR, o as isEventConversationMessage } from "./prompt-3D0sSbTC.js";
16
+ import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline } from "./job-queue-cwHVOwVZ.js";
17
+ import { P as healthReportSchema, S as automationListQuerySchema, c as sumUtcHoursIntoSixHours, t as webMessageId } from "./web-message-id-CVVCTWUb.js";
18
+ import { C as readAutomationExecutionStatusDays, D as readAutomationRuns, E as readAutomationExecutions, S as readAutomationExecutionHours, T as readAutomationExecutionSummaries, _ as markDispatchFailed, a as confirmDispatchMailboxAppend, b as readAutomationExecutionByConversationId, c as getDispatchInputMessageId, f as isTerminalDispatchStatus, g as markDispatchCompleted, h as markDispatchBlocked, i as claimDispatchMailboxAppend, l as getDispatchRecord, m as markDispatchAwaitingResume, n as scheduleSessionCompletedPluginTasks, s as getDispatchConversationId, t as processPluginTask, u as getDispatchTurnId, v as markDispatchRunning, w as readAutomationExecutionStatusHours, x as readAutomationExecutionDays, y as emptyAutomationRunWindows } from "./task-runner-BZaqoUCT.js";
19
+ import { i as resolveAgentInvocationId, o as AgentRunError, s as executeTurn, t as createAgentInvocationWorker } from "./work-CNc0ejGG.js";
20
+ import { n as getTurnLifecycle, t as ConversationTurnLifecycleService } from "./turn-lifecycle-WbpTUQ_r.js";
21
+ import { a as persistThreadState, c as configValueSchema, i as persistThreadRuntimeState, n as getPersistedSandboxState, o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState, t as getLocationConfigurationService } from "./thread-state-8tXZhu0y.js";
22
+ import { a as getPausedTurnRequest, c as getTurnAuthorization, i as createPausedTurns, n as deleteWebAuthorization, s as wakePausedTurn, t as createWebAuthorization } from "./web-authorization-BJggXofy.js";
23
+ import { n as resolveTurnSessionRouting } from "./turn-session-routing--EhI76rH.js";
24
+ import { t as bindSpawnAgent } from "./spawn-DhfMKt4y.js";
25
+ import { a as persistAuthPauseTurnState, c as getTurnUserSlackMessageTs, d as loadPendingMessageCards, i as completeAuthPauseTurn, l as buildDeliveredTurnStatePatch, n as getAgentTurnDiagnosticsAttributes, o as getTurnUserMessage, r as requireTurnFailureEventId, s as getTurnUserReplyAttachmentContext, t as finalizeFailedTurnReplyWithEvent, u as getInterruptionMarker } from "./turn-failure-response-DFJ9L9NQ.js";
26
+ import { r as storeInputImages } from "./images-BaL19wFC.js";
27
27
  import { z } from "zod";
28
28
  import { createHash } from "node:crypto";
29
- import { and, desc, eq, inArray, lt, notInArray, or, sql } from "drizzle-orm";
29
+ import { and, asc, count, desc, eq, exists, inArray, ne, or, sql } from "drizzle-orm";
30
30
  import { createEventSource, createLocalSource, createSlackSource, createWebSource, localDestinationSchema, objectFactFields, objectPresentation, objectTypeSchema } from "@sentry/junior-plugin-api";
31
+ import { unionAll } from "drizzle-orm/pg-core";
31
32
  import { del, get, put } from "@vercel/blob";
32
33
  import { Message, ThreadImpl, stringifyMarkdown } from "chat";
33
34
  import { createSlackAdapter } from "@chat-adapter/slack";
@@ -3237,99 +3238,143 @@ function recordSkippedConversationMessage(args) {
3237
3238
  });
3238
3239
  }
3239
3240
  //#endregion
3240
- //#region src/chat/scheduled-automations/personal.ts
3241
- const cursorSchema = z.object({
3241
+ //#region src/chat/automations/collection.ts
3242
+ /**
3243
+ * The web collection applies access before counts, filter options, and paging.
3244
+ * Keep its ownership rules aligned with the direct Automation reader. A public
3245
+ * Destination only grants access inside a viewer's linked Slack workspaces.
3246
+ */
3247
+ function viewerAutomationCollection(user) {
3248
+ const db = getDb();
3249
+ const identities = user.identities.filter((identity) => identity.provider === "slack");
3250
+ const teamIds = identities.flatMap((identity) => identity.providerTenantId ? [identity.providerTenantId] : []);
3251
+ const scheduled = juniorSchedulerTasks;
3252
+ const event = juniorEventAutomations;
3253
+ const destination = juniorDestinations;
3254
+ const scheduledOwned = inArray(scheduled.creatorIdentityId, user.identities.map((identity) => identity.id));
3255
+ const eventOwned = or(...identities.map((identity) => and(eq(event.teamId, identity.providerTenantId ?? ""), sql`${event.task}->'createdBy'->>'slackUserId' = ${identity.providerSubjectId}`))) ?? sql`false`;
3256
+ const publicDestination = sql`coalesce(${destination.visibility} = 'public', false)`;
3257
+ const scheduledPublic = and(inArray(scheduled.teamId, teamIds), publicDestination, ne(scheduled.status, "completed"));
3258
+ const eventPublic = and(inArray(event.teamId, teamIds), publicDestination);
3259
+ function fields(record, teamId) {
3260
+ return {
3261
+ creator: sql`${teamId} || ':' || (${record}->'createdBy'->>'slackUserId')`.as("creator"),
3262
+ creatorLabel: sql`coalesce(nullif(trim(${record}->'createdBy'->>'fullName'), ''), nullif(trim(${record}->'createdBy'->>'userName'), ''), ${record}->'createdBy'->>'slackUserId')`.as("creator_label"),
3263
+ destination: sql`${teamId} || ':' || (${record}->'destination'->>'channelId')`.as("destination"),
3264
+ destinationLabel: sql`coalesce(nullif(${destination.displayName}, ''), ${record}->'destination'->>'channelId')`.as("destination_label"),
3265
+ isPublic: publicDestination.as("is_public"),
3266
+ instruction: sql`${record}->'task'->>'text'`.as("instruction")
3267
+ };
3268
+ }
3269
+ function destinationJoin(record, teamId) {
3270
+ return and(eq(destination.provider, "slack"), sql`${destination.providerTenantId} = ${teamId}`, sql`${destination.providerDestinationId} = ${record}->'destination'->>'channelId'`);
3271
+ }
3272
+ const available = or(...Object.entries(pluginEventCatalog(getEventCatalog())).map(([namespace, registration]) => {
3273
+ const events = registration.resourceTypes.flatMap((resource) => resource.supportedEvents);
3274
+ return and(eq(event.namespace, namespace), sql`${event.task}->'trigger'->'events' <@ ${JSON.stringify(events)}::jsonb`);
3275
+ })) ?? sql`false`;
3276
+ const executions = juniorAutomationExecutions;
3277
+ function lastRunNeedsAttention(kind, id) {
3278
+ return sql`coalesce((select ${executions.status} in ('failed', 'blocked') from ${executions}
3279
+ where ${executions.kind} = ${kind} and ${executions.namespace} = 'junior' and ${executions.automationId} = ${id}
3280
+ order by ${executions.executedAtMs} desc, ${executions.executionId} desc limit 1), false)`;
3281
+ }
3282
+ return unionAll(db.select({
3283
+ kind: sql`'scheduled'`.as("kind"),
3284
+ id: scheduled.id,
3285
+ title: sql`${scheduled.title}`.as("title"),
3286
+ createdAtMs: scheduled.createdAtMs,
3287
+ state: sql`${scheduled.status}`.as("state"),
3288
+ unavailable: sql`false`.as("unavailable"),
3289
+ attention: sql`${scheduled.status} not in ('paused', 'completed') and (${scheduled.status} = 'blocked' or ${lastRunNeedsAttention("scheduled", scheduled.id)})`.as("attention"),
3290
+ owned: sql`${scheduledOwned}`.as("owned"),
3291
+ resource: sql`''`.as("resource"),
3292
+ ...fields(scheduled.record, scheduled.teamId)
3293
+ }).from(scheduled).leftJoin(destination, destinationJoin(scheduled.record, scheduled.teamId)).where(and(inArray(scheduled.status, [
3294
+ "active",
3295
+ "blocked",
3296
+ "paused",
3297
+ "completed"
3298
+ ]), or(scheduledOwned, scheduledPublic))), db.select({
3299
+ kind: sql`'event'`.as("kind"),
3300
+ id: event.id,
3301
+ title: sql`${event.title}`.as("title"),
3302
+ createdAtMs: event.createdAtMs,
3303
+ state: sql`${event.status}`.as("state"),
3304
+ unavailable: sql`not (${available})`.as("unavailable"),
3305
+ attention: sql`${event.status} <> 'paused' and (not (${available}) or ${lastRunNeedsAttention("event", event.id)})`.as("attention"),
3306
+ owned: sql`${eventOwned}`.as("owned"),
3307
+ resource: sql`concat_ws(' ', ${event.identifier}, ${event.task}->'trigger'->>'label', ${event.namespace})`.as("resource"),
3308
+ ...fields(event.task, event.teamId)
3309
+ }).from(event).leftJoin(destination, destinationJoin(event.task, event.teamId)).where(and(ne(event.status, "deleted"), or(eventOwned, eventPublic)))).as("accessible_automations");
3310
+ }
3311
+ /** Read an SQL page and complete, access-scoped counts and filter options. */
3312
+ async function readAutomationCollection(user, input) {
3313
+ const db = getDb();
3314
+ const collection = viewerAutomationCollection(user);
3315
+ const where = and(input.scope === "mine" ? eq(collection.owned, true) : input.scope === "public" ? eq(collection.isPublic, true) : input.scope === "attention" ? eq(collection.attention, true) : void 0, input.type === "all" ? void 0 : eq(collection.kind, input.type), input.state === "all" ? void 0 : input.state === "unavailable" ? eq(collection.unavailable, true) : eq(collection.state, input.state), input.creator ? eq(collection.creator, input.creator) : void 0, input.destination ? eq(collection.destination, input.destination) : void 0, input.q ? sql`strpos(lower(concat_ws(' ', ${collection.title}, ${collection.instruction}, ${collection.resource})), ${input.q.toLowerCase()}) > 0` : void 0);
3316
+ const [totals, counts, creators, destinations] = await Promise.all([
3317
+ db.select({ total: count() }).from(collection).where(where),
3318
+ db.select({
3319
+ all: count(),
3320
+ mine: sql`count(*) filter (where ${collection.owned})::int`,
3321
+ public: sql`count(*) filter (where ${collection.isPublic})::int`,
3322
+ private: sql`count(*) filter (where not ${collection.isPublic})::int`
3323
+ }).from(collection),
3324
+ db.select({
3325
+ value: collection.creator,
3326
+ label: sql`min(${collection.creatorLabel})`
3327
+ }).from(collection).groupBy(collection.creator).orderBy(sql`min(${collection.creatorLabel})`, collection.creator),
3328
+ db.select({
3329
+ value: collection.destination,
3330
+ label: sql`min(${collection.destinationLabel})`
3331
+ }).from(collection).groupBy(collection.destination).orderBy(sql`min(${collection.destinationLabel})`, collection.destination)
3332
+ ]);
3333
+ const total = totals[0].total;
3334
+ const page = Math.min(input.page, Math.max(1, Math.ceil(total / input.pageSize)));
3335
+ const order = input.sort === "oldest" ? asc(collection.createdAtMs) : input.sort === "title" ? asc(sql`lower(coalesce(nullif(trim(${collection.title}), ''), ${collection.instruction}))`) : desc(collection.createdAtMs);
3336
+ return {
3337
+ rows: await db.select({
3338
+ id: collection.id,
3339
+ kind: collection.kind
3340
+ }).from(collection).where(where).orderBy(order, desc(collection.id), asc(collection.kind)).limit(input.pageSize).offset((page - 1) * input.pageSize),
3341
+ total,
3342
+ page,
3343
+ pageSize: input.pageSize,
3344
+ counts: counts[0],
3345
+ creators,
3346
+ destinations
3347
+ };
3348
+ }
3349
+ z.object({
3242
3350
  createdAtMs: z.number().finite(),
3243
3351
  id: z.string().min(1),
3244
3352
  query: z.string().max(200).optional(),
3245
3353
  version: z.literal(1)
3246
3354
  }).strict();
3247
- var InvalidScheduledAutomationCursorError = class extends Error {
3248
- constructor() {
3249
- super("Scheduled automation cursor is invalid.");
3250
- this.name = "InvalidScheduledAutomationCursorError";
3251
- }
3252
- };
3253
3355
  var PersonalScheduledAutomationNotFoundError = class extends Error {
3254
3356
  constructor() {
3255
3357
  super("Scheduled automation was not found.");
3256
3358
  this.name = "PersonalScheduledAutomationNotFoundError";
3257
3359
  }
3258
3360
  };
3259
- function normalizeQuery(query) {
3260
- return query?.trim().toLowerCase() || void 0;
3261
- }
3262
- function decodeCursor(value, query) {
3263
- if (!value) return void 0;
3264
- try {
3265
- const parsed = cursorSchema.parse(JSON.parse(Buffer.from(value, "base64url").toString("utf8")));
3266
- if (parsed.query !== query) throw new InvalidScheduledAutomationCursorError();
3267
- return {
3268
- createdAtMs: parsed.createdAtMs,
3269
- id: parsed.id
3270
- };
3271
- } catch {
3272
- throw new InvalidScheduledAutomationCursorError();
3273
- }
3274
- }
3275
- function encodeCursor(task, query) {
3276
- return Buffer.from(JSON.stringify({
3277
- createdAtMs: task.createdAtMs,
3278
- id: task.id,
3279
- ...query ? { query } : void 0,
3280
- version: 1
3281
- }), "utf8").toString("base64url");
3282
- }
3283
3361
  /** Delete one scheduled automation created by the viewer. */
3284
3362
  async function deleteViewerScheduledAutomation(db, user, id, nowMs = Date.now()) {
3285
- const identityIds = new Set(user.identities.map((identity) => identity.id));
3286
- const task = await readScheduledAutomation(db, id);
3287
- if (!task || task.status === "deleted" || !identityIds.has(task.creatorIdentityId)) throw new PersonalScheduledAutomationNotFoundError();
3288
- await saveScheduledAutomation(db, {
3289
- ...task,
3290
- nextRunAtMs: void 0,
3291
- runNowAtMs: void 0,
3292
- status: "deleted",
3293
- updatedAtMs: nowMs
3363
+ await withScheduledAutomationLock(db, id, async (tx) => {
3364
+ const identityIds = new Set(user.identities.map((identity) => identity.id));
3365
+ const task = await readScheduledAutomation(tx, id);
3366
+ if (!task || task.status === "deleted" || !identityIds.has(task.creatorIdentityId)) throw new PersonalScheduledAutomationNotFoundError();
3367
+ await saveScheduledAutomationInLock(tx, {
3368
+ ...task,
3369
+ nextRunAtMs: void 0,
3370
+ runNowAtMs: void 0,
3371
+ status: "deleted",
3372
+ updatedAtMs: nowMs
3373
+ }, task);
3294
3374
  });
3295
3375
  }
3296
- /** List scheduled automations created by the viewer with a stable SQL cursor. */
3297
- async function listViewerScheduledAutomations(db, user, input) {
3298
- const identityIds = user.identities.map((identity) => identity.id);
3299
- if (identityIds.length === 0) return { automations: [] };
3300
- const query = normalizeQuery(input.query);
3301
- const cursor = decodeCursor(input.cursor, query);
3302
- const automations = [];
3303
- let before = cursor;
3304
- const fetchLimit = input.limit + 1;
3305
- while (automations.length < fetchLimit) {
3306
- const cursorFilter = before ? or(lt(juniorSchedulerTasks.createdAtMs, before.createdAtMs), and(eq(juniorSchedulerTasks.createdAtMs, before.createdAtMs), lt(juniorSchedulerTasks.id, before.id))) : void 0;
3307
- const search = query ? sql`strpos(lower(coalesce(${juniorSchedulerTasks.title}, ${juniorSchedulerTasks.record}->'task'->>'text')), ${query}) > 0` : void 0;
3308
- const rows = await db.select({
3309
- createdAtMs: juniorSchedulerTasks.createdAtMs,
3310
- creatorIdentityId: juniorSchedulerTasks.creatorIdentityId,
3311
- id: juniorSchedulerTasks.id,
3312
- record: juniorSchedulerTasks.record,
3313
- title: juniorSchedulerTasks.title
3314
- }).from(juniorSchedulerTasks).where(and(notInArray(juniorSchedulerTasks.status, ["deleted", "paused"]), inArray(juniorSchedulerTasks.creatorIdentityId, identityIds), cursorFilter, search)).orderBy(desc(juniorSchedulerTasks.createdAtMs), desc(juniorSchedulerTasks.id)).limit(fetchLimit);
3315
- automations.push(...rows.map(parseScheduledAutomationRow).filter(isListedScheduledAutomation));
3316
- if (rows.length < fetchLimit) break;
3317
- const last = rows.at(-1);
3318
- before = {
3319
- createdAtMs: last.createdAtMs,
3320
- id: last.id
3321
- };
3322
- }
3323
- const page = automations.slice(0, input.limit);
3324
- return {
3325
- automations: page,
3326
- ...automations.length > input.limit && page.length > 0 ? { nextCursor: encodeCursor(page.at(-1), query) } : void 0
3327
- };
3328
- }
3329
3376
  //#endregion
3330
3377
  //#region src/chat/automations/read.ts
3331
- const TASK_LIST_LIMIT = 100;
3332
- const TASK_FETCH_LIMIT = 101;
3333
3378
  const TASK_EXECUTION_LIST_LIMIT = 100;
3334
3379
  function creatorLabel(creator) {
3335
3380
  return creator.fullName?.trim() || (creator.userName?.trim() ? `@${creator.userName.trim()}` : "") || creator.slackUserId;
@@ -3337,7 +3382,7 @@ function creatorLabel(creator) {
3337
3382
  function creatorKey(teamId, slackUserId) {
3338
3383
  return `${teamId}:${slackUserId}`;
3339
3384
  }
3340
- async function creatorProfileEmails(candidates) {
3385
+ async function creatorProfiles(candidates) {
3341
3386
  const selectors = new Map(candidates.map(({ task }) => {
3342
3387
  const teamId = task.destination.teamId;
3343
3388
  const slackUserId = task.createdBy.slackUserId;
@@ -3348,11 +3393,16 @@ async function creatorProfileEmails(candidates) {
3348
3393
  }));
3349
3394
  if (selectors.size === 0) return /* @__PURE__ */ new Map();
3350
3395
  const rows = await getDb().select({
3396
+ avatarUrl: juniorIdentities.avatarUrl,
3351
3397
  email: juniorUsers.primaryEmailNormalized,
3398
+ emailVerified: juniorIdentities.emailVerified,
3352
3399
  slackUserId: juniorIdentities.providerSubjectId,
3353
3400
  teamId: juniorIdentities.providerTenantId
3354
- }).from(juniorIdentities).innerJoin(juniorUsers, eq(juniorUsers.id, juniorIdentities.userId)).where(and(eq(juniorIdentities.kind, "user"), eq(juniorIdentities.provider, "slack"), eq(juniorIdentities.emailVerified, true), or(...[...selectors.values()].map((selector) => and(eq(juniorIdentities.providerTenantId, selector.teamId), eq(juniorIdentities.providerSubjectId, selector.slackUserId))))));
3355
- return new Map(rows.map((row) => [creatorKey(row.teamId, row.slackUserId), row.email]));
3401
+ }).from(juniorIdentities).leftJoin(juniorUsers, eq(juniorUsers.id, juniorIdentities.userId)).where(and(eq(juniorIdentities.kind, "user"), eq(juniorIdentities.provider, "slack"), or(...[...selectors.values()].map((selector) => and(eq(juniorIdentities.providerTenantId, selector.teamId), eq(juniorIdentities.providerSubjectId, selector.slackUserId))))));
3402
+ return new Map(rows.map((row) => [creatorKey(row.teamId, row.slackUserId), {
3403
+ email: row.emailVerified ? row.email ?? void 0 : void 0,
3404
+ avatarUrl: row.avatarUrl ?? void 0
3405
+ }]));
3356
3406
  }
3357
3407
  function destinationKey(destination) {
3358
3408
  return `${destination.teamId}:${destination.channelId}`;
@@ -3386,20 +3436,22 @@ async function destinationDetails(destinations) {
3386
3436
  }
3387
3437
  function executionSummaryFields(stats) {
3388
3438
  return {
3439
+ lastRunStatus: stats?.lastRunStatus,
3389
3440
  ...stats?.lastConversationId ? { lastConversationId: stats.lastConversationId } : void 0,
3390
3441
  ...stats?.lastExecutedAtMs ? { lastRunAt: new Date(stats.lastExecutedAtMs).toISOString() } : void 0,
3391
3442
  runs: stats?.runs ?? emptyAutomationRunWindows(),
3392
3443
  totalRuns: stats?.totalRuns ?? 0
3393
3444
  };
3394
3445
  }
3395
- function scheduledAutomationSummary(task, ownedByViewer, destination, stats, createdByEmail) {
3446
+ function scheduledAutomationSummary(task, ownedByViewer, destination, stats, creator) {
3396
3447
  if (task.status === "deleted") throw new Error("Deleted scheduled automations cannot enter the Automations view");
3397
- const nextRunAtMs = task.runNowAtMs ?? task.nextRunAtMs;
3448
+ const nextRunAtMs = task.status === "active" ? task.runNowAtMs ?? task.nextRunAtMs : void 0;
3398
3449
  const instruction = displayText(task.task.text, "Untitled scheduled automation");
3399
3450
  return {
3400
3451
  createdAt: new Date(task.createdAtMs).toISOString(),
3401
3452
  createdBy: creatorLabel(task.createdBy),
3402
- ...createdByEmail ? { createdByEmail } : void 0,
3453
+ createdByEmail: creator?.email,
3454
+ createdByAvatarUrl: creator?.avatarUrl,
3403
3455
  destination: {
3404
3456
  channelId: task.destination.channelId,
3405
3457
  label: destination.label,
@@ -3414,16 +3466,20 @@ function scheduledAutomationSummary(task, ownedByViewer, destination, stats, cre
3414
3466
  ownedByViewer,
3415
3467
  schedule: displayText(task.schedule.description, "Schedule unavailable"),
3416
3468
  status: task.status,
3469
+ statusReason: task.statusReason,
3470
+ timezone: task.schedule.timezone,
3471
+ credentialMode: task.credentialMode,
3417
3472
  outcomes: effectiveTaskOutcomes(task.outcomes, task.destination),
3418
3473
  title: taskDisplayTitle(task.title, instruction, "Untitled scheduled automation")
3419
3474
  };
3420
3475
  }
3421
- function eventAutomationSummary(task, ownedByViewer, destination, stats, createdByEmail) {
3476
+ function eventAutomationSummary(task, ownedByViewer, destination, stats, creator) {
3422
3477
  const instruction = task.task.text;
3423
3478
  return {
3424
3479
  createdAt: new Date(task.createdAtMs).toISOString(),
3425
3480
  createdBy: creatorLabel(task.createdBy),
3426
- ...createdByEmail ? { createdByEmail } : void 0,
3481
+ createdByEmail: creator?.email,
3482
+ createdByAvatarUrl: creator?.avatarUrl,
3427
3483
  destination: {
3428
3484
  channelId: task.destination.channelId,
3429
3485
  label: destination.label,
@@ -3431,6 +3487,7 @@ function eventAutomationSummary(task, ownedByViewer, destination, stats, created
3431
3487
  visibility: destination.visibility
3432
3488
  },
3433
3489
  events: task.trigger.events,
3490
+ match: task.trigger.match,
3434
3491
  id: task.id,
3435
3492
  instruction,
3436
3493
  kind: "event",
@@ -3438,8 +3495,10 @@ function eventAutomationSummary(task, ownedByViewer, destination, stats, created
3438
3495
  ownedByViewer,
3439
3496
  resource: `${task.trigger.label} · ${task.trigger.identifier}`,
3440
3497
  source: task.trigger.namespace,
3498
+ credentialMode: task.credentialMode,
3441
3499
  outcomes: effectiveTaskOutcomes(task.outcomes, task.destination),
3442
3500
  title: taskDisplayTitle(task.title, instruction, "Untitled event automation"),
3501
+ status: task.status === "paused" ? "paused" : "active",
3443
3502
  triggerAvailable: eventAutomationTriggerAvailable(task, getEventCatalog())
3444
3503
  };
3445
3504
  }
@@ -3460,7 +3519,6 @@ async function resolveViewerTaskCandidate(user, kind, id) {
3460
3519
  return {
3461
3520
  kind: "scheduled",
3462
3521
  ownedByViewer,
3463
- publicToViewer,
3464
3522
  task
3465
3523
  };
3466
3524
  }
@@ -3473,7 +3531,6 @@ async function resolveViewerTaskCandidate(user, kind, id) {
3473
3531
  return {
3474
3532
  kind: "event",
3475
3533
  ownedByViewer,
3476
- publicToViewer,
3477
3534
  task
3478
3535
  };
3479
3536
  }
@@ -3484,20 +3541,19 @@ async function readViewerAutomationSummary(user, id) {
3484
3541
  return automationSummaryForCandidate(candidate);
3485
3542
  }
3486
3543
  async function automationSummaryForCandidate(candidate) {
3487
- const [destinations, creatorEmails, stats] = await Promise.all([
3544
+ const [destinations, creators, stats] = await Promise.all([
3488
3545
  destinationDetails([candidate.task.destination]),
3489
- creatorProfileEmails([candidate]),
3490
- readAutomationExecutionSummaries(candidate.kind, "junior")
3546
+ creatorProfiles([candidate]),
3547
+ readAutomationExecutionSummaries(candidate.kind, "junior", { automationIds: [candidate.task.id] })
3491
3548
  ]);
3492
3549
  const destination = destinations.get(destinationKey(candidate.task.destination)) ?? {
3493
3550
  label: `Channel ${candidate.task.destination.channelId}`,
3494
3551
  visibility: "private"
3495
3552
  };
3496
- const createdByEmail = creatorEmails.get(creatorKey(candidate.task.destination.teamId, candidate.task.createdBy.slackUserId));
3497
- if (candidate.kind === "scheduled") return scheduledAutomationSummary(candidate.task, candidate.ownedByViewer, destination, stats.get(candidate.task.id), createdByEmail);
3498
- return eventAutomationSummary(candidate.task, candidate.ownedByViewer, destination, stats.get(candidate.task.id), createdByEmail);
3553
+ const creator = creators.get(creatorKey(candidate.task.destination.teamId, candidate.task.createdBy.slackUserId));
3554
+ if (candidate.kind === "scheduled") return scheduledAutomationSummary(candidate.task, candidate.ownedByViewer, destination, stats.get(candidate.task.id), creator);
3555
+ return eventAutomationSummary(candidate.task, candidate.ownedByViewer, destination, stats.get(candidate.task.id), creator);
3499
3556
  }
3500
- /** Read viewer-owned and public-workspace automations as one bounded newest-first projection. */
3501
3557
  function emptyAutomationExecutionDay(date) {
3502
3558
  return {
3503
3559
  costUsd: 0,
@@ -3528,67 +3584,59 @@ function automationExecutionStatusSixHours(hours, nowMs = Date.now()) {
3528
3584
  nowMs
3529
3585
  });
3530
3586
  }
3531
- /** List one viewer's scheduled and event automations, optionally filtered by title or instruction search. */
3532
- async function readViewerAutomations(user, input = {}) {
3587
+ /** Read a page from the full accessible Automation collection. */
3588
+ async function readViewerAutomations(user, input = automationListQuerySchema.parse({})) {
3589
+ const { rows, ...page } = await readAutomationCollection(user, input);
3533
3590
  const db = getDb();
3534
- const query = input.q?.trim().toLowerCase() || void 0;
3591
+ const ids = rows.map((row) => row.id);
3592
+ const [scheduled, events] = ids.length ? await Promise.all([db.select().from(juniorSchedulerTasks).where(inArray(juniorSchedulerTasks.id, ids)), db.select().from(juniorEventAutomations).where(inArray(juniorEventAutomations.id, ids))]) : [[], []];
3535
3593
  const identityIds = new Set(user.identities.map((identity) => identity.id));
3536
- const teamIds = viewerTeamIds(user);
3537
- const [scheduledPage, publicScheduled, eventAutomations, publicEventAutomations] = await Promise.all([
3538
- listViewerScheduledAutomations(db, user, {
3539
- limit: TASK_FETCH_LIMIT,
3540
- query
3541
- }),
3542
- listPublicScheduledAutomationsForTeams(db, teamIds, {
3543
- limit: TASK_FETCH_LIMIT,
3544
- query
3545
- }),
3546
- listEventAutomationsCreatedBy(db, user, TASK_FETCH_LIMIT, query),
3547
- listPublicEventAutomationsForTeams(db, teamIds, TASK_FETCH_LIMIT, query)
3548
- ]);
3549
- const candidatesById = /* @__PURE__ */ new Map();
3550
- const publicScheduledIds = new Set(publicScheduled.map((task) => task.id));
3551
- const publicEventAutomationIds = new Set(publicEventAutomations.map((task) => task.id));
3552
- for (const task of [...scheduledPage.automations, ...publicScheduled]) candidatesById.set(`scheduled:${task.id}`, {
3553
- kind: "scheduled",
3554
- ownedByViewer: identityIds.has(task.creatorIdentityId),
3555
- publicToViewer: publicScheduledIds.has(task.id),
3556
- task
3557
- });
3558
- for (const task of [...eventAutomations, ...publicEventAutomations]) candidatesById.set(`event:${task.id}`, {
3559
- kind: "event",
3560
- ownedByViewer: eventAutomationBelongsToUser(task, user),
3561
- publicToViewer: publicEventAutomationIds.has(task.id),
3562
- task
3594
+ const candidates = /* @__PURE__ */ new Map();
3595
+ for (const row of scheduled) {
3596
+ const task = parseScheduledAutomationRow(row);
3597
+ if (task && task.status !== "deleted") candidates.set(`scheduled:${task.id}`, {
3598
+ kind: "scheduled",
3599
+ task,
3600
+ ownedByViewer: identityIds.has(task.creatorIdentityId)
3601
+ });
3602
+ }
3603
+ for (const row of events) {
3604
+ const task = parseEventAutomationRow(row);
3605
+ if (task.status !== "deleted") candidates.set(`event:${task.id}`, {
3606
+ kind: "event",
3607
+ task,
3608
+ ownedByViewer: eventAutomationBelongsToUser(task, user)
3609
+ });
3610
+ }
3611
+ const selected = rows.flatMap((row) => {
3612
+ const candidate = candidates.get(`${row.kind}:${row.id}`);
3613
+ return candidate ? [candidate] : [];
3563
3614
  });
3564
- const candidates = [...candidatesById.values()].sort((left, right) => right.task.createdAtMs - left.task.createdAtMs || right.task.id.localeCompare(left.task.id));
3565
- const ownedCandidates = candidates.filter((candidate) => candidate.ownedByViewer);
3566
- const publicCandidates = candidates.filter((candidate) => candidate.publicToViewer);
3567
- const selectedCandidates = /* @__PURE__ */ new Set([...ownedCandidates.slice(0, TASK_LIST_LIMIT), ...publicCandidates.slice(0, TASK_LIST_LIMIT)]);
3568
- const selected = candidates.filter((candidate) => selectedCandidates.has(candidate));
3569
- const [destinations, creatorEmails] = await Promise.all([destinationDetails(selected.map(({ task }) => task.destination)), creatorProfileEmails(selected)]);
3615
+ const [destinations, creators] = await Promise.all([destinationDetails(selected.map(({ task }) => task.destination)), creatorProfiles(selected)]);
3570
3616
  const visible = selected.filter((candidate) => candidate.ownedByViewer || destinations.get(destinationKey(candidate.task.destination))?.visibility === "public");
3617
+ const collection = viewerAutomationCollection(user);
3618
+ const access = exists(getDb().select({ id: collection.id }).from(collection).where(and(eq(collection.id, juniorAutomationExecutions.automationId), eq(collection.kind, juniorAutomationExecutions.kind), eq(juniorAutomationExecutions.namespace, "junior"))));
3571
3619
  const [executionDays, executionHours, scheduledStats, eventStats] = await Promise.all([
3572
- readAutomationExecutionDays(),
3573
- readAutomationExecutionHours(),
3574
- readAutomationExecutionSummaries("scheduled", "junior"),
3575
- readAutomationExecutionSummaries("event", "junior")
3620
+ readAutomationExecutionDays(90, { access }),
3621
+ readAutomationExecutionHours(168, { access }),
3622
+ readAutomationExecutionSummaries("scheduled", "junior", { automationIds: ids }),
3623
+ readAutomationExecutionSummaries("event", "junior", { automationIds: ids })
3576
3624
  ]);
3577
3625
  const automations = visible.map((candidate) => {
3578
3626
  const destination = destinations.get(destinationKey(candidate.task.destination)) ?? {
3579
3627
  label: `Channel ${candidate.task.destination.channelId}`,
3580
3628
  visibility: "private"
3581
3629
  };
3582
- const createdByEmail = creatorEmails.get(creatorKey(candidate.task.destination.teamId, candidate.task.createdBy.slackUserId));
3583
- if (candidate.kind === "scheduled") return scheduledAutomationSummary(candidate.task, candidate.ownedByViewer, destination, scheduledStats.get(candidate.task.id), createdByEmail);
3584
- return eventAutomationSummary(candidate.task, candidate.ownedByViewer, destination, eventStats.get(candidate.task.id), createdByEmail);
3630
+ const creator = creators.get(creatorKey(candidate.task.destination.teamId, candidate.task.createdBy.slackUserId));
3631
+ if (candidate.kind === "scheduled") return scheduledAutomationSummary(candidate.task, candidate.ownedByViewer, destination, scheduledStats.get(candidate.task.id), creator);
3632
+ return eventAutomationSummary(candidate.task, candidate.ownedByViewer, destination, eventStats.get(candidate.task.id), creator);
3585
3633
  });
3586
3634
  return {
3587
3635
  executionDays,
3588
3636
  executionHours,
3589
3637
  executionSixHours: automationExecutionSixHours(executionHours),
3590
3638
  automations,
3591
- truncated: ownedCandidates.length > TASK_LIST_LIMIT || publicCandidates.length > TASK_LIST_LIMIT
3639
+ ...page
3592
3640
  };
3593
3641
  }
3594
3642
  /** Load deleted scheduled automations the viewer still owns, newest first. */
@@ -3625,14 +3673,14 @@ function addDeletedAutomationRun(args, automationTitles, automations) {
3625
3673
  */
3626
3674
  async function readViewerAutomationRuns(user) {
3627
3675
  const [taskList, deletedScheduled, deletedEvent] = await Promise.all([
3628
- readViewerAutomations(user),
3676
+ getDb().select().from(viewerAutomationCollection(user)),
3629
3677
  readDeletedOwnedScheduledAutomations(user),
3630
3678
  readDeletedOwnedEventAutomations(user)
3631
3679
  ]);
3632
3680
  const automationTitles = /* @__PURE__ */ new Map();
3633
3681
  const automations = [];
3634
- for (const task of taskList.automations) {
3635
- automationTitles.set(`${task.kind}:${task.id}`, task.title);
3682
+ for (const task of taskList) {
3683
+ automationTitles.set(`${task.kind}:${task.id}`, taskDisplayTitle(task.title ?? void 0, task.instruction, `Untitled ${task.kind} automation`));
3636
3684
  automations.push({
3637
3685
  kind: task.kind,
3638
3686
  automationId: task.id