@sentry/junior 0.205.0 → 0.207.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.
- package/dist/{agent-D4nu8MQV.js → agent-CmQlX68D.js} +603 -394
- package/dist/{agent-hooks-DXHpTJ4m.js → agent-hooks-BBX9p6aG.js} +1171 -100
- package/dist/{agent-runner-C04colea.js → agent-runner-CQD4HKK2.js} +9 -9
- package/dist/api/schema.d.ts +116 -39
- package/dist/api/schema.js +3 -3
- package/dist/api.d.ts +1 -1
- package/dist/api.js +182 -649
- package/dist/{app-Cb776lL8.d.ts → app-DfNrn_gS.d.ts} +9 -2
- package/dist/app.d.ts +1 -1
- package/dist/app.js +205 -233
- package/dist/{auth-pause-state-C2P3guS1.js → auth-pause-state-uv8on6cT.js} +7 -7
- package/dist/briefs-BXEmjD9D.js +398 -0
- package/dist/{catalog-runtime-CXw3asJD.js → catalog-runtime-1BZEOR8J.js} +1 -1
- package/dist/{checkpoint-CrViJ8Ps.js → checkpoint-B-JO5F3c.js} +9 -8
- package/dist/cli/chat.js +14 -12
- package/dist/cli/check.js +3 -3
- package/dist/cli/init.js +1 -0
- package/dist/cli/main.js +5 -1
- package/dist/cli/plugins.js +3 -3
- package/dist/cli/run.d.ts +2 -1
- package/dist/cli/run.js +2 -1
- package/dist/cli/snapshot-create.js +3 -3
- package/dist/cli/upgrade.d.ts +1 -1
- package/dist/cli/upgrade.js +3 -3
- package/dist/{config-BOtJQ7Pc.js → client-Bp297GA0.js} +6 -369
- package/dist/config-G2gv97R1.js +386 -0
- package/dist/{context-B45_WqVX.js → context-Bplcgypl.js} +13 -13
- package/dist/{schema-Rn8-XEaN.js → conversation-AA4R1VUn.js} +580 -558
- package/dist/{conversation-privacy-BZRA6Pes.js → conversation-privacy-BUfel19D.js} +1 -1
- package/dist/{credential-sync-Bep34IB_.js → credential-sync-D04NWqMR.js} +21 -20
- package/dist/{db-BeyXKJOI.d.ts → db-D74z7BZH.d.ts} +1 -1
- package/dist/{execution-stats-B6lgWxCK.js → execution-stats-Ds4bSvam.js} +77 -77
- package/dist/{executor-BlUhFL7_.js → executor-BZu1Ta06.js} +41 -13
- package/dist/instrumentation.js +2 -2
- package/dist/{job-queue-DLMqIK74.js → job-queue-rnAPfUhP.js} +1 -1
- package/dist/{logging-BMJzdDlP.js → logging-Dx0S5k2z.js} +1 -1
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +3 -3
- package/dist/{oauth-callback-server-WwG5_Zgm.js → oauth-callback-server-xGpwURIj.js} +2 -2
- package/dist/{pending-auth-CUbXq06G.js → pending-auth-DIZ4iZjS.js} +2 -2
- package/dist/{profile-T0tXE4zu.js → profile-d-fKypxo.js} +4 -3
- package/dist/prompt-x-0ZtTyb.js +552 -0
- package/dist/{registry-BA0__8ai.js → registry-CWKdLCTG.js} +2 -2
- package/dist/{resolve-bvD1ORdv.js → resolve-BhRB1s2A.js} +2 -2
- package/dist/{runner-BXYxfIUL.js → runner-DNml3owD.js} +10 -10
- package/dist/{sandbox-egress-signals-ngEPjSEW.js → sandbox-egress-signals-DUIl3-gW.js} +1 -1
- package/dist/schema-CwTeb-BF.js +559 -0
- package/dist/{skills-DJOlYdBs.js → skills-D9WesdJJ.js} +2 -2
- package/dist/{spawn-h0R1bTEo.js → spawn-Eh_3-s0M.js} +2 -2
- package/dist/{stats-3YZn-Swi.js → stats-CFlUGqVA.js} +292 -268
- package/dist/{store-C5qxF2ac.d.ts → store-Det8MQ9g.d.ts} +1 -1
- package/dist/{task-queue-CoXwd2eq.js → task-queue-D8I1_5wP.js} +3 -3
- package/dist/{task-runner-B2_mJZj0.js → task-runner-DO79gdho.js} +18 -18
- package/dist/{thread-state-CqZ1m-es.js → thread-state-DD6BA4xz.js} +5 -5
- package/dist/{turn-execution-fI6PZ0iI.js → turn-execution-BSIHxmQJ.js} +3 -3
- package/dist/{turn-lifecycle-CcnwrOcp.js → turn-lifecycle-DSBK4Ars.js} +1 -1
- package/dist/{usage-B5SfMb3H.js → usage-WRXkNOHp.js} +4 -125
- package/dist/{validation-DfWcvhuS.js → validation-BbHZ4r4S.js} +1 -1
- package/dist/{version-oD-oPrbO.js → version-C71UcRWw.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/web-authorization-C73gJV7l.js +38 -0
- package/dist/{web-input-BOuq3oKK.js → web-input-Ce1FcNIM.js} +8 -8
- package/dist/{work-B91LQKHr.js → work-BEzDU_2x.js} +9 -9
- package/dist/{worker-BcRRTe6k.js → worker-MK2ubOv-.js} +5 -4
- package/migrations/0040_conversation_briefs.sql +17 -0
- package/migrations/0041_automation_outcomes_backfill.sql +27 -0
- package/migrations/meta/0040_snapshot.json +3516 -0
- package/migrations/meta/0041_snapshot.json +3516 -0
- package/migrations/meta/_journal.json +14 -0
- package/package.json +4 -4
- package/dist/history-CqTuSAdI.js +0 -441
- package/dist/usage-schema-CKaDkH_j.js +0 -20
- package/dist/web-authorization-Crbzlmzo.js +0 -76
package/dist/app.js
CHANGED
|
@@ -1,47 +1,48 @@
|
|
|
1
|
-
import { $ as
|
|
2
|
-
import {
|
|
3
|
-
import { A as JUNIOR_WORKSPACE_SNAPSHOT_JOB_CALLBACK_ROUTE, C as withSpan, D as JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE, F as isRecord$1, I as toOptionalNumber, L as toOptionalString, S as withLogContext, _ as setSpanAttributes, d as logInfo, f as logWarn, g as setSentryUser, l as logError, n as createChatSdkLogger, o as getActiveTraceId, r as createRequestContext, t as buildTurnFailureResponse, u as logException, v as setSpanStatus, x as withContext, y as setTags } from "./logging-
|
|
1
|
+
import { $ as shouldEmitDevAgentTrace, B as resolveSandboxEgressProviderForHost, C as renderBlockText, D as getNextRunAtMs, E as scheduledAutomationRunAttributes, F as ingestEvent, G as consumeSandboxEgressAuthRequiredSignal, H as shouldPropagateSandboxEgressTrace, I as isEventMailboxMetadata, K as consumeSandboxEgressPermissionDeniedSignal, L as renderTaskInput, P as processWorkspaceSnapshotJob, Q as AuthorizationFlowDisabledError, R as maybePostAutomatedTurnLimitNotice, S as renderAttachmentText, T as logScheduledAutomationRunSkipped, U as SANDBOX_EGRESS_PROXY_PATH, V as normalizeSandboxEgressTracePropagationDomains, W as clearSandboxEgressSignals, X as buildSteeringPiMessage, Y as createUserTokenStore, Z as isVisionImageMediaType, _ as readPublicArtifact, b as readAttachmentVisionSummary, c as deleteMcpStoredOAuthCredentials, et as getConfigDefaults, f as PluginCredentialFailureError, g as publicArtifactHeaders, m as latestReportedProgress, n as createContextCompactor, o as deleteMcpAuthSessionsForUserProvider, q as parseSandboxEgressCredentialToken, r as executeCredentialedEgressRequest, s as deleteMcpServerSessionId, t as executeAgentRun, tt as setConfigDefaults, v as unpublishedArtifactHeaders, w as listThreadReplies, x as recordSlackAttachment, z as recordFinishedTurnForAutomatedLimit } from "./agent-CmQlX68D.js";
|
|
2
|
+
import { b as isProviderRetryError, i as completeText, m as escapeXml, r as completeObject } from "./client-Bp297GA0.js";
|
|
3
|
+
import { A as JUNIOR_WORKSPACE_SNAPSHOT_JOB_CALLBACK_ROUTE, C as withSpan, D as JUNIOR_PLUGIN_TASK_CALLBACK_ROUTE, F as isRecord$1, I as toOptionalNumber, L as toOptionalString, S as withLogContext, _ as setSpanAttributes, d as logInfo, f as logWarn, g as setSentryUser, l as logError, n as createChatSdkLogger, o as getActiveTraceId, r as createRequestContext, t as buildTurnFailureResponse, u as logException, v as setSpanStatus, x as withContext, y as setTags } from "./logging-Dx0S5k2z.js";
|
|
4
4
|
import { r as continueTrace } from "./sentry-CD3SdJl-.js";
|
|
5
|
-
import { t as JUNIOR_VERSION } from "./version-
|
|
5
|
+
import { t as JUNIOR_VERSION } from "./version-C71UcRWw.js";
|
|
6
|
+
import { a as getChatConfig, c as getSlackClientId, d as getSlackSigningSecret, f as setBotModelConfig, i as configureFunctionMaxDurationSeconds, l as getSlackClientSecret, m as setSlackReactionConfig, n as botConfig, p as setProfiles, s as getSlackBotToken, u as getSlackReactionConfig } from "./config-G2gv97R1.js";
|
|
6
7
|
import { a as modelIdForProfile, n as defaultModelId } from "./model-profile-CZmVoGdX.js";
|
|
7
|
-
import { _ as parseSlackMessageTs, a as createActor, c as isActorUserId, o as createSlackActor, p as parseSlackChannelId, s as createSlackResumeActor, t as credentialContextForActor, u as parseActorUserId } from "./context-
|
|
8
|
-
import {
|
|
8
|
+
import { _ as parseSlackMessageTs, a as createActor, c as isActorUserId, o as createSlackActor, p as parseSlackChannelId, s as createSlackResumeActor, t as credentialContextForActor, u as parseActorUserId } from "./context-Bplcgypl.js";
|
|
9
|
+
import { B as downloadPrivateSlackFile, F as bindSlackDirectCredentialSubject, Ft as getConversationStore, G as isDmChannel, I as verifyEventAutomationCredentialSubject, It as getDb, K as normalizeSlackConversationId, Kt as deleteProviderIdentityForSlackUser, L as verifyScheduledAutomationCredentialSubject, Lt as getSqlExecutor, N as bindEventAutomationCredentialSubject, Nt as getConversationEventStore, P as bindScheduledAutomationCredentialSubject, R as verifySlackDirectCredentialSubject, Tt as resolveRootVisibility, U as getSlackClient, Vt as withConversationMutationLock, X as runWithWorkspaceTeamId, Y as getWorkspaceTeamId, Yt as createSlackDestination, Zt as requireSlackDestination, _t as createPluginState, a as getPluginRoutes, bt as createPluginLogger, ct as collectEventMatchKeys, d as setPlugins, dt as findMatchingWatches, f as validatePlugins, gt as setDashboardConversationLinkOptions, h as setBriefsConfig, i as getPluginApiRoutes, ot as cancelSubscriptions, pt as JUNIOR_THREAD_STATE_TTL_MS, q as runWithSlackInstallationToken, u as getPlugins, z as SlackActionError, zt as withConversationEventLock } from "./agent-hooks-BBX9p6aG.js";
|
|
9
10
|
import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import { n as pluginCatalogRuntime } from "./catalog-runtime-
|
|
13
|
-
import { $ as persistConversationMessages, F as commitMessages, J as recordSubscribedReplyRoute, L as loadConversationProjection, Mt as trimTrailingAssistantMessages, P as commitAcceptedReply, Q as hydrateConversationMessages, R as loadProjection, Z as appendConversationMessages, a as completeTurnRecord, at as createConversationMemoryService, c as getTurnRecordForResume, ct as isHumanConversationMessage, d as persistWithRetry, dt as recordDeliveredAssistantMessage, et as projectConversationMessages, ft as turnHasReply, i as abandonTurnRecord, it as buildConversationContext, jt as stripRuntimeTurnContext, l as listTurnSummaries, lt as markConversationMessage, m as buildTurnErrorResponse, mt as estimateTextTokens, n as loadTurnCheckpoint, nt as resolveConversationTitle, o as failTurnRecord, ot as estimateConversationContextTokens, pt as upsertConversationMessage, r as saveTurnCheckpoint, rt as appendThreadContextMessages, s as getTurnRecord, st as getConversationMessageSlackTs, tt as persistConversationMessageSummaries, u as recordTurnSummary, ut as normalizeConversationText } from "./checkpoint-
|
|
14
|
-
import { E as acquireActiveLock, O as withActiveLock, T as MUTATION_LOCK_TTL_MS$1, k as withLock$1, w as getStateAdapter } from "./profile-
|
|
15
|
-
import { Dt as hasRunnableConversationWork, Et as getConversationWorkState, F as effectiveTaskOutcomes, Gt as buildDeterministicAssistantMessageId, It as stopConversationWork, Kt as buildDeterministicTurnId, Mt as removeActiveConversation, Ot as listActiveConversationIds, Pt as requestConversationWork, Rt as CONVERSATION_WORK_STALE_ENQUEUE_MS, S as
|
|
16
|
-
import { S as startActiveTurn, _ as isTurnInputCommitLostError, b as markTurnCompleted, d as ConversationTurnBoundaryError, f as CooperativeTurnYieldError, g as isCooperativeTurnYieldError, h as getConversationTurnBoundaryError, m as TurnInputDeferredError, p as TurnInputCommitLostError, r as clearPendingAuth, s as getAssistantReplyText, v as isTurnInputDeferredError, x as markTurnFailed, y as markTurnClosed } from "./pending-auth-
|
|
17
|
-
import { h as postSlackMessage, i as formatProviderLabel, s as startOAuthFlow, t as createAgentRunner, v as RetryableDeliveryError } from "./agent-runner-
|
|
18
|
-
import {
|
|
11
|
+
import { _ as juniorConversationEvents, a as juniorSchedulerRuns, f as juniorAgentBindings, g as juniorConversationBriefs, o as juniorSchedulerTasks, p as juniorAgentInvocations, w as juniorConversations } from "./executor-BZu1Ta06.js";
|
|
12
|
+
import { G as instructionProvenanceFor } from "./conversation-AA4R1VUn.js";
|
|
13
|
+
import { n as pluginCatalogRuntime } from "./catalog-runtime-1BZEOR8J.js";
|
|
14
|
+
import { $ as persistConversationMessages, F as commitMessages, J as recordSubscribedReplyRoute, L as loadConversationProjection, Mt as trimTrailingAssistantMessages, P as commitAcceptedReply, Q as hydrateConversationMessages, R as loadProjection, Z as appendConversationMessages, a as completeTurnRecord, at as createConversationMemoryService, c as getTurnRecordForResume, ct as isHumanConversationMessage, d as persistWithRetry, dt as recordDeliveredAssistantMessage, et as projectConversationMessages, ft as turnHasReply, i as abandonTurnRecord, it as buildConversationContext, jt as stripRuntimeTurnContext, l as listTurnSummaries, lt as markConversationMessage, m as buildTurnErrorResponse, mt as estimateTextTokens, n as loadTurnCheckpoint, nt as resolveConversationTitle, o as failTurnRecord, ot as estimateConversationContextTokens, pt as upsertConversationMessage, r as saveTurnCheckpoint, rt as appendThreadContextMessages, s as getTurnRecord, st as getConversationMessageSlackTs, tt as persistConversationMessageSummaries, u as recordTurnSummary, ut as normalizeConversationText } from "./checkpoint-B-JO5F3c.js";
|
|
15
|
+
import { E as acquireActiveLock, O as withActiveLock, T as MUTATION_LOCK_TTL_MS$1, k as withLock$1, w as getStateAdapter } from "./profile-d-fKypxo.js";
|
|
16
|
+
import { Dt as hasRunnableConversationWork, Et as getConversationWorkState, F as effectiveTaskOutcomes, Gt as buildDeterministicAssistantMessageId, It as stopConversationWork, Kt as buildDeterministicTurnId, Mt as removeActiveConversation, Ot as listActiveConversationIds, Pt as requestConversationWork, Rt as CONVERSATION_WORK_STALE_ENQUEUE_MS, S as withScheduledAutomationLock, Tt as getConversation, U as findMatchingEventAutomations, Vt as isInvalidConversationRecordError, X as getEventCatalog, a as requestAttachmentDeletion, b as saveScheduledAutomationInLock, c as parseSlackThreadId, g as listScheduledAutomationsForTeam, h as listScheduledAutomations, o as storeAttachment, pt as appendAndEnqueueInboundMessage, r as collectAttachmentGarbage, tt as eventGuidance, v as readScheduledAutomation, vt as clearExpiredConversationLease, w as scheduledRunSchema, wt as ensureConversationWake, x as scheduledRunId, z as collectEventAutomationMatchKeys, zt as hasRunnableConversationWork$1 } from "./stats-CFlUGqVA.js";
|
|
17
|
+
import { S as startActiveTurn, _ as isTurnInputCommitLostError, b as markTurnCompleted, d as ConversationTurnBoundaryError, f as CooperativeTurnYieldError, g as isCooperativeTurnYieldError, h as getConversationTurnBoundaryError, m as TurnInputDeferredError, p as TurnInputCommitLostError, r as clearPendingAuth, s as getAssistantReplyText, v as isTurnInputDeferredError, x as markTurnFailed, y as markTurnClosed } from "./pending-auth-DIZ4iZjS.js";
|
|
18
|
+
import { h as postSlackMessage, i as formatProviderLabel, s as startOAuthFlow, t as createAgentRunner, v as RetryableDeliveryError } from "./agent-runner-CQD4HKK2.js";
|
|
19
|
+
import { a as EVENT_SYSTEM_ACTOR, i as EVENT_MESSAGE_AUTHOR, o as isEventConversationMessage } from "./prompt-x-0ZtTyb.js";
|
|
20
|
+
import { f as setSandboxResourceConfig, u as getSandboxResourceConfig } from "./resolve-BhRB1s2A.js";
|
|
19
21
|
import { l as conversationQueueMessageSchema, n as resolveConversationWorkQueueTopic, r as verifyConversationQueueMessage, t as getVercelConversationWorkQueue, u as isConversationQueueMessageRejectedError } from "./vercel-queue-DIkVcfK4.js";
|
|
20
|
-
import { a as getTurnRequestDeadline, i as verifyWorkspaceSnapshotJobMessage, t as WORKSPACE_SNAPSHOT_JOB_QUEUE_TOPIC } from "./job-queue-
|
|
22
|
+
import { a as getTurnRequestDeadline, i as verifyWorkspaceSnapshotJobMessage, t as WORKSPACE_SNAPSHOT_JOB_QUEUE_TOPIC } from "./job-queue-rnAPfUhP.js";
|
|
21
23
|
import { i as setExperimentalFeatures, n as getExperimentalFeatures, r as isExperimentalFeatureEnabled } from "./experimental-CIXf7Uye.js";
|
|
22
|
-
import { n as validatePluginRegistrations, t as validatePluginEgressCredentialHooks } from "./validation-
|
|
24
|
+
import { n as validatePluginRegistrations, t as validatePluginEgressCredentialHooks } from "./validation-BbHZ4r4S.js";
|
|
23
25
|
import { a as pluginRuntimeRegistrationsFromPluginSet, n as pluginCatalogConfigFromEnv, r as pluginCatalogConfigFromPluginSet, t as defineJuniorPlugins } from "./plugins-egKWjgue.js";
|
|
24
|
-
import { C as createVercelAttachmentStorage, S as legacyWebMailboxMetadataSchema, a as webActorFromEmail, g as conversationVisibilityFromSlackChannelType, h as resolveConversationDestination, i as recordWebConversationActivity, n as conversationTurnIdForMessage, o as readConversationAccessFromSql, t as appendAndEnqueueWebMessage, v as resolveSlackChannelTypeFromMessage, w as GET$3, x as lookupSlackUser, y as resolveSlackConversationContext } from "./web-input-
|
|
25
|
-
import { u as
|
|
26
|
-
import { _ as markDispatchCompleted, a as claimDispatchMailboxAppend, c as getDispatchConversationId, d as getDispatchTurnId, f as getPluginDispatchProjection, g as markDispatchBlocked, h as markDispatchAwaitingResume, i as resolveTurnSessionRouting, l as getDispatchInputMessageId, m as listPendingDispatchMailboxAppends, n as scheduleSessionCompletedPluginTasks, o as confirmDispatchMailboxAppend, p as isTerminalDispatchStatus, s as createOrGetDispatch, t as processPluginTask, u as getDispatchRecord, v as markDispatchFailed, y as markDispatchRunning } from "./task-runner-
|
|
27
|
-
import { i as resolveAgentInvocationId, o as listPendingAgentInvocationMailboxAppends, r as enqueueAgentInvocation, t as createAgentInvocationWorker } from "./work-
|
|
28
|
-
import { n as getTurnLifecycle, t as ConversationTurnLifecycleService } from "./turn-lifecycle-
|
|
29
|
-
import { n as executeTurn, t as AgentRunError } from "./turn-execution-
|
|
30
|
-
import { a as persistThreadState, i as persistThreadRuntimeState, n as getPersistedSandboxState, o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState, t as getLocationConfigurationService } from "./thread-state-
|
|
31
|
-
import { a as requireTurnFailureEventId, d as buildDeliveredTurnStatePatch, i as getAgentTurnDiagnosticsAttributes, l as getTurnUserReplyAttachmentContext, n as persistAuthPauseTurnState, r as finalizeFailedTurnReplyWithEvent, s as getTurnUserMessage, t as completeAuthPauseTurn, u as getTurnUserSlackMessageTs } from "./auth-pause-state-
|
|
32
|
-
import { a as registerVercelPluginTaskDevConsumer, n as createVercelPluginTaskCallback, s as queueCallback } from "./task-queue-
|
|
33
|
-
import { C as isSlackTitlePermissionError, D as createSlackAdapterAssistantStatusSession, E as splitSlackReplyText, S as isRetryableSlackPostError, T as buildSlackOutputMessage, _ as getThreadId, a as GET$4, b as getSlackApiErrorCode, c as wakePausedTurn, d as shouldKeepProcessingReactionForToolInvocation, f as startProcessingReaction, g as getRunId, h as getMessageTs, i as publishAppHomeView, l as resumeSlackTurn, m as getChannelId, o as createPausedTurns, p as getAssistantThreadContext, r as GET$5, s as getPausedTurnRequest, t as receiveLocalOAuthCredential, u as buildAuthPauseResponse, v as getThreadTs, w as sendSlackReply, x as getSlackErrorObservabilityAttributes, y as stripLeadingBotMention } from "./credential-sync-
|
|
34
|
-
import { n as deleteWebAuthorization, t as createWebAuthorization } from "./web-authorization-
|
|
35
|
-
import { t as processConversationWork } from "./worker-
|
|
36
|
-
import { t as bindSpawnAgent } from "./spawn-
|
|
26
|
+
import { C as createVercelAttachmentStorage, S as legacyWebMailboxMetadataSchema, a as webActorFromEmail, g as conversationVisibilityFromSlackChannelType, h as resolveConversationDestination, i as recordWebConversationActivity, n as conversationTurnIdForMessage, o as readConversationAccessFromSql, t as appendAndEnqueueWebMessage, v as resolveSlackChannelTypeFromMessage, w as GET$3, x as lookupSlackUser, y as resolveSlackConversationContext } from "./web-input-Ce1FcNIM.js";
|
|
27
|
+
import { u as recordAutomationExecution } from "./execution-stats-Ds4bSvam.js";
|
|
28
|
+
import { _ as markDispatchCompleted, a as claimDispatchMailboxAppend, c as getDispatchConversationId, d as getDispatchTurnId, f as getPluginDispatchProjection, g as markDispatchBlocked, h as markDispatchAwaitingResume, i as resolveTurnSessionRouting, l as getDispatchInputMessageId, m as listPendingDispatchMailboxAppends, n as scheduleSessionCompletedPluginTasks, o as confirmDispatchMailboxAppend, p as isTerminalDispatchStatus, s as createOrGetDispatch, t as processPluginTask, u as getDispatchRecord, v as markDispatchFailed, y as markDispatchRunning } from "./task-runner-DO79gdho.js";
|
|
29
|
+
import { i as resolveAgentInvocationId, o as listPendingAgentInvocationMailboxAppends, r as enqueueAgentInvocation, t as createAgentInvocationWorker } from "./work-BEzDU_2x.js";
|
|
30
|
+
import { n as getTurnLifecycle, t as ConversationTurnLifecycleService } from "./turn-lifecycle-DSBK4Ars.js";
|
|
31
|
+
import { n as executeTurn, t as AgentRunError } from "./turn-execution-BSIHxmQJ.js";
|
|
32
|
+
import { a as persistThreadState, i as persistThreadRuntimeState, n as getPersistedSandboxState, o as persistThreadStateById, r as getPersistedThreadState, s as coerceThreadConversationState, t as getLocationConfigurationService } from "./thread-state-DD6BA4xz.js";
|
|
33
|
+
import { a as requireTurnFailureEventId, d as buildDeliveredTurnStatePatch, i as getAgentTurnDiagnosticsAttributes, l as getTurnUserReplyAttachmentContext, n as persistAuthPauseTurnState, r as finalizeFailedTurnReplyWithEvent, s as getTurnUserMessage, t as completeAuthPauseTurn, u as getTurnUserSlackMessageTs } from "./auth-pause-state-uv8on6cT.js";
|
|
34
|
+
import { a as registerVercelPluginTaskDevConsumer, n as createVercelPluginTaskCallback, s as queueCallback } from "./task-queue-D8I1_5wP.js";
|
|
35
|
+
import { C as isSlackTitlePermissionError, D as createSlackAdapterAssistantStatusSession, E as splitSlackReplyText, S as isRetryableSlackPostError, T as buildSlackOutputMessage, _ as getThreadId, a as GET$4, b as getSlackApiErrorCode, c as wakePausedTurn, d as shouldKeepProcessingReactionForToolInvocation, f as startProcessingReaction, g as getRunId, h as getMessageTs, i as publishAppHomeView, l as resumeSlackTurn, m as getChannelId, o as createPausedTurns, p as getAssistantThreadContext, r as GET$5, s as getPausedTurnRequest, t as receiveLocalOAuthCredential, u as buildAuthPauseResponse, v as getThreadTs, w as sendSlackReply, x as getSlackErrorObservabilityAttributes, y as stripLeadingBotMention } from "./credential-sync-D04NWqMR.js";
|
|
36
|
+
import { n as deleteWebAuthorization, t as createWebAuthorization } from "./web-authorization-C73gJV7l.js";
|
|
37
|
+
import { t as processConversationWork } from "./worker-MK2ubOv-.js";
|
|
38
|
+
import { t as bindSpawnAgent } from "./spawn-Eh_3-s0M.js";
|
|
37
39
|
import { createRequire } from "node:module";
|
|
38
40
|
import { pathToFileURL } from "node:url";
|
|
39
41
|
import { Hono } from "hono";
|
|
40
42
|
import { z } from "zod";
|
|
41
43
|
import { createHash, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
|
|
42
44
|
import { and, asc, eq, inArray, isNotNull, isNull, lte, or, sql } from "drizzle-orm";
|
|
43
|
-
import {
|
|
44
|
-
import { alias } from "drizzle-orm/pg-core";
|
|
45
|
+
import { EVENT_SUMMARY_MAX_LENGTH, EVENT_TEXT_MAX_LENGTH, createEventSource, createLocalSource, createSlackSource, createWebSource, dispatchOptionsSchema, eventSchema, isSlackDestination, userSchema } from "@sentry/junior-plugin-api";
|
|
45
46
|
import { setTimeout as setTimeout$1 } from "node:timers/promises";
|
|
46
47
|
import { createRemoteJWKSet, decodeJwt, jwtVerify } from "jose";
|
|
47
48
|
import { ChannelImpl, Message, ThreadImpl, stringifyMarkdown } from "chat";
|
|
@@ -430,10 +431,10 @@ function validateDispatchOptions(options) {
|
|
|
430
431
|
/** Verify runtime-owned access requirements for delegated dispatch credentials. */
|
|
431
432
|
async function verifyDispatchCredentialSubjectAccess(options, plugin) {
|
|
432
433
|
if (!options.credentialSubject) return;
|
|
433
|
-
if (!(options.credentialSubject.allowedWhen === "scheduled-
|
|
434
|
+
if (!(options.credentialSubject.allowedWhen === "scheduled-automation" ? verifyScheduledAutomationCredentialSubject({
|
|
434
435
|
plugin,
|
|
435
436
|
subject: options.credentialSubject
|
|
436
|
-
}) : options.credentialSubject.allowedWhen === "event-
|
|
437
|
+
}) : options.credentialSubject.allowedWhen === "event-automation" ? verifyEventAutomationCredentialSubject({
|
|
437
438
|
plugin,
|
|
438
439
|
subject: options.credentialSubject
|
|
439
440
|
}) : verifySlackDirectCredentialSubject({
|
|
@@ -452,7 +453,7 @@ function bindDispatchCredentialSubject(options, plugin) {
|
|
|
452
453
|
source: { kind: "plugin_dispatch" }
|
|
453
454
|
};
|
|
454
455
|
if ("binding" in credentialSubject) throw new Error("Dispatch credentialSubject binding is runtime-owned");
|
|
455
|
-
const boundSubject = credentialSubject.allowedWhen === "scheduled-
|
|
456
|
+
const boundSubject = credentialSubject.allowedWhen === "scheduled-automation" ? bindScheduledAutomationCredentialSubject({
|
|
456
457
|
plugin,
|
|
457
458
|
subject: credentialSubject
|
|
458
459
|
}) : bindSlackDirectCredentialSubject({
|
|
@@ -517,12 +518,12 @@ function createHeartbeatContext(args) {
|
|
|
517
518
|
}
|
|
518
519
|
};
|
|
519
520
|
}
|
|
520
|
-
/** Create and enqueue one core-owned event
|
|
521
|
-
async function
|
|
521
|
+
/** Create and enqueue one core-owned event automation dispatch. */
|
|
522
|
+
async function dispatchEventAutomation(args) {
|
|
522
523
|
const plugin = "junior";
|
|
523
524
|
const { credentialSubject, outcomes, ...unboundOptions } = args.options;
|
|
524
525
|
validateDispatchOptions({ ...unboundOptions });
|
|
525
|
-
const boundSubject = credentialSubject ?
|
|
526
|
+
const boundSubject = credentialSubject ? bindEventAutomationCredentialSubject({
|
|
526
527
|
plugin,
|
|
527
528
|
subject: credentialSubject
|
|
528
529
|
}) : void 0;
|
|
@@ -531,7 +532,7 @@ async function dispatchEventTask(args) {
|
|
|
531
532
|
...unboundOptions,
|
|
532
533
|
...outcomes !== void 0 ? { outcomes } : void 0,
|
|
533
534
|
...boundSubject ? { credentialSubject: boundSubject } : void 0,
|
|
534
|
-
source: { kind: "
|
|
535
|
+
source: { kind: "event_automation" }
|
|
535
536
|
};
|
|
536
537
|
return await dispatch({
|
|
537
538
|
conversationWorkQueue: args.conversationWorkQueue,
|
|
@@ -540,12 +541,12 @@ async function dispatchEventTask(args) {
|
|
|
540
541
|
plugin
|
|
541
542
|
});
|
|
542
543
|
}
|
|
543
|
-
/** Create and enqueue one core-owned scheduled
|
|
544
|
-
async function
|
|
544
|
+
/** Create and enqueue one core-owned scheduled automation dispatch. */
|
|
545
|
+
async function dispatchScheduledAutomation(args) {
|
|
545
546
|
const plugin = "scheduler";
|
|
546
547
|
const { credentialSubject, outcomes, ...unboundOptions } = args.options;
|
|
547
548
|
validateDispatchOptions({ ...unboundOptions });
|
|
548
|
-
const boundSubject = credentialSubject ?
|
|
549
|
+
const boundSubject = credentialSubject ? bindScheduledAutomationCredentialSubject({
|
|
549
550
|
plugin,
|
|
550
551
|
subject: credentialSubject
|
|
551
552
|
}) : void 0;
|
|
@@ -554,7 +555,7 @@ async function dispatchScheduledTask(args) {
|
|
|
554
555
|
...unboundOptions,
|
|
555
556
|
...outcomes !== void 0 ? { outcomes } : void 0,
|
|
556
557
|
...boundSubject ? { credentialSubject: boundSubject } : void 0,
|
|
557
|
-
source: { kind: "
|
|
558
|
+
source: { kind: "scheduled_automation" }
|
|
558
559
|
};
|
|
559
560
|
return await dispatch({
|
|
560
561
|
conversationWorkQueue: args.conversationWorkQueue,
|
|
@@ -563,15 +564,15 @@ async function dispatchScheduledTask(args) {
|
|
|
563
564
|
plugin
|
|
564
565
|
});
|
|
565
566
|
}
|
|
566
|
-
/** Read the bounded outcome for one core-owned scheduled
|
|
567
|
-
async function
|
|
567
|
+
/** Read the bounded outcome for one core-owned scheduled automation dispatch. */
|
|
568
|
+
async function getScheduledAutomationDispatch(id) {
|
|
568
569
|
return await getPluginDispatchProjection({
|
|
569
570
|
plugin: "scheduler",
|
|
570
571
|
id
|
|
571
572
|
});
|
|
572
573
|
}
|
|
573
574
|
//#endregion
|
|
574
|
-
//#region src/chat/scheduled-
|
|
575
|
+
//#region src/chat/scheduled-automations/runs.ts
|
|
575
576
|
const PENDING_CLAIM_STALE_MS = 6e4;
|
|
576
577
|
const MISSED_RUN_MAX_AGE_MS = 864e5;
|
|
577
578
|
const INCOMPLETE_RUN_STATUSES = ["pending", "running"];
|
|
@@ -615,9 +616,9 @@ async function listIncompleteRunsForTaskIds(db, taskIds) {
|
|
|
615
616
|
if (taskIds.length === 0) return [];
|
|
616
617
|
return (await db.select({ record: juniorSchedulerRuns.record }).from(juniorSchedulerRuns).where(and(inArray(juniorSchedulerRuns.taskId, taskIds), inArray(juniorSchedulerRuns.status, [...INCOMPLETE_RUN_STATUSES]))).orderBy(asc(juniorSchedulerRuns.scheduledForMs), asc(juniorSchedulerRuns.id))).map(parseScheduledRunRow).filter(present);
|
|
617
618
|
}
|
|
618
|
-
/** List incomplete runs for retained scheduled
|
|
619
|
+
/** List incomplete runs for retained scheduled automations. */
|
|
619
620
|
async function listIncompleteScheduledRuns(db) {
|
|
620
|
-
return await listIncompleteRunsForTaskIds(db, (await
|
|
621
|
+
return await listIncompleteRunsForTaskIds(db, (await listScheduledAutomations(db)).map((task) => task.id));
|
|
621
622
|
}
|
|
622
623
|
function getDueRunAtMs(task, nowMs) {
|
|
623
624
|
if (typeof task.runNowAtMs === "number" && Number.isFinite(task.runNowAtMs) && task.runNowAtMs <= nowMs) return task.runNowAtMs;
|
|
@@ -653,7 +654,7 @@ function taskDedupeFingerprint(task) {
|
|
|
653
654
|
}
|
|
654
655
|
async function findStaleRecoveryCanonicalTask(db, task) {
|
|
655
656
|
const fingerprint = taskDedupeFingerprint(task);
|
|
656
|
-
return (await
|
|
657
|
+
return (await listScheduledAutomationsForTeam(db, task.destination.teamId)).filter((candidate) => candidate.id !== task.id && candidate.status === "active" && (candidate.createdAtMs < task.createdAtMs || candidate.createdAtMs === task.createdAtMs && candidate.id < task.id) && taskDedupeFingerprint(candidate) === fingerprint).at(0);
|
|
657
658
|
}
|
|
658
659
|
function statusAfterTerminalOccurrence(args) {
|
|
659
660
|
if (args.outcome === "blocked") return "blocked";
|
|
@@ -665,7 +666,7 @@ async function skipMissedRun(db, args) {
|
|
|
665
666
|
const current = args.task;
|
|
666
667
|
if (current.status !== "active" || getDueRunAtMs(current, args.nowMs) !== args.scheduledForMs) return;
|
|
667
668
|
const duplicateOf = await findStaleRecoveryCanonicalTask(db, current);
|
|
668
|
-
const errorMessage = duplicateOf ? `Duplicate stale scheduled
|
|
669
|
+
const errorMessage = duplicateOf ? `Duplicate stale scheduled automation was skipped without dispatch. Canonical task: ${duplicateOf.id}.` : "Scheduled occurrence was more than 24 hours late and was skipped without dispatch.";
|
|
669
670
|
const skipped = {
|
|
670
671
|
id: scheduledRunId(current.id, args.scheduledForMs),
|
|
671
672
|
attempt: 1,
|
|
@@ -681,7 +682,7 @@ async function skipMissedRun(db, args) {
|
|
|
681
682
|
let nextRunAtMs;
|
|
682
683
|
if (!duplicateOf) nextRunAtMs = isRunNow && current.nextRunAtMs !== args.scheduledForMs ? current.nextRunAtMs : current.schedule.kind === "recurring" ? getNextRunAtMs(current, args.scheduledForMs, args.nowMs) : void 0;
|
|
683
684
|
const status = statusAfterTerminalOccurrence({ nextRunAtMs });
|
|
684
|
-
await
|
|
685
|
+
await saveScheduledAutomationInLock(db, {
|
|
685
686
|
...current,
|
|
686
687
|
nextRunAtMs,
|
|
687
688
|
runNowAtMs: isRunNow ? void 0 : current.runNowAtMs,
|
|
@@ -702,8 +703,8 @@ async function claimDueScheduledRun(db, args) {
|
|
|
702
703
|
await tx.execute(sql`SELECT pg_advisory_xact_lock(hashtext('junior:scheduler:claim'))`);
|
|
703
704
|
const rows = await tx.select({ id: juniorSchedulerTasks.id }).from(juniorSchedulerTasks).where(and(eq(juniorSchedulerTasks.status, "active"), or(and(isNotNull(juniorSchedulerTasks.runNowAtMs), lte(juniorSchedulerTasks.runNowAtMs, args.nowMs)), and(isNotNull(juniorSchedulerTasks.nextRunAtMs), lte(juniorSchedulerTasks.nextRunAtMs, args.nowMs))))).orderBy(asc(juniorSchedulerTasks.createdAtMs), asc(juniorSchedulerTasks.id));
|
|
704
705
|
for (const row of rows) {
|
|
705
|
-
const run = await
|
|
706
|
-
const task = await
|
|
706
|
+
const run = await withScheduledAutomationLock(tx, row.id, async (taskTx) => {
|
|
707
|
+
const task = await readScheduledAutomation(taskTx, row.id);
|
|
707
708
|
if (!task || task.status !== "active") return void 0;
|
|
708
709
|
const scheduledForMs = getDueRunAtMs(task, args.nowMs);
|
|
709
710
|
if (scheduledForMs === void 0) return void 0;
|
|
@@ -745,7 +746,7 @@ async function claimDueScheduledRun(db, args) {
|
|
|
745
746
|
if (run) return run;
|
|
746
747
|
}
|
|
747
748
|
});
|
|
748
|
-
for (const skipped of skippedRuns)
|
|
749
|
+
for (const skipped of skippedRuns) logScheduledAutomationRunSkipped(skipped.task, skipped.run, { "app.task.run.error": skipped.errorMessage });
|
|
749
750
|
return claimed;
|
|
750
751
|
}
|
|
751
752
|
async function updateRun(db, runId, update) {
|
|
@@ -808,15 +809,15 @@ async function markScheduledRunSkipped(db, args) {
|
|
|
808
809
|
status: "skipped"
|
|
809
810
|
} : void 0);
|
|
810
811
|
}
|
|
811
|
-
/** Advance a scheduled
|
|
812
|
-
async function
|
|
813
|
-
await
|
|
814
|
-
const current = await
|
|
812
|
+
/** Advance a scheduled automation after a terminal run under its task lock. */
|
|
813
|
+
async function advanceScheduledAutomationAfterRun(db, args) {
|
|
814
|
+
await withScheduledAutomationLock(db, args.run.taskId, async (tx) => {
|
|
815
|
+
const current = await readScheduledAutomation(tx, args.run.taskId);
|
|
815
816
|
if (!current || current.status === "deleted" || current.status === "completed") return;
|
|
816
817
|
if (current.runNowAtMs === args.run.scheduledForMs) {
|
|
817
818
|
let nextRunAtMs = current.nextRunAtMs;
|
|
818
819
|
if (args.status !== "blocked" && typeof current.nextRunAtMs === "number" && current.nextRunAtMs <= args.run.scheduledForMs) nextRunAtMs = getNextRunAtMs(current, current.nextRunAtMs, args.nowMs);
|
|
819
|
-
await
|
|
820
|
+
await saveScheduledAutomationInLock(tx, {
|
|
820
821
|
...current,
|
|
821
822
|
lastRunAtMs: args.run.scheduledForMs,
|
|
822
823
|
nextRunAtMs,
|
|
@@ -832,7 +833,7 @@ async function advanceScheduledTaskAfterRun(db, args) {
|
|
|
832
833
|
return;
|
|
833
834
|
}
|
|
834
835
|
if (current.status !== "active" || current.nextRunAtMs !== args.run.scheduledForMs) {
|
|
835
|
-
await
|
|
836
|
+
await saveScheduledAutomationInLock(tx, {
|
|
836
837
|
...current,
|
|
837
838
|
lastRunAtMs: args.run.scheduledForMs,
|
|
838
839
|
updatedAtMs: args.nowMs
|
|
@@ -840,7 +841,7 @@ async function advanceScheduledTaskAfterRun(db, args) {
|
|
|
840
841
|
return;
|
|
841
842
|
}
|
|
842
843
|
const nextRunAtMs = args.status === "blocked" ? void 0 : getNextRunAtMs(current, args.run.scheduledForMs, args.nowMs);
|
|
843
|
-
await
|
|
844
|
+
await saveScheduledAutomationInLock(tx, {
|
|
844
845
|
...current,
|
|
845
846
|
lastRunAtMs: args.run.scheduledForMs,
|
|
846
847
|
nextRunAtMs,
|
|
@@ -854,8 +855,8 @@ async function advanceScheduledTaskAfterRun(db, args) {
|
|
|
854
855
|
});
|
|
855
856
|
}
|
|
856
857
|
//#endregion
|
|
857
|
-
//#region src/chat/scheduled-
|
|
858
|
-
const
|
|
858
|
+
//#region src/chat/scheduled-automations/heartbeat.ts
|
|
859
|
+
const SCHEDULED_AUTOMATION_HEARTBEAT_LIMIT = 10;
|
|
859
860
|
const SCHEDULE_FREQUENCY_COPY = {
|
|
860
861
|
daily: {
|
|
861
862
|
label: "Daily",
|
|
@@ -877,7 +878,7 @@ const SCHEDULE_FREQUENCY_COPY = {
|
|
|
877
878
|
function singleLineMetadataValue(value) {
|
|
878
879
|
return value.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim();
|
|
879
880
|
}
|
|
880
|
-
/** Render the due scheduled
|
|
881
|
+
/** Render the due scheduled automation as plain agent input. */
|
|
881
882
|
function buildDispatchInput(task) {
|
|
882
883
|
return renderTaskInput({
|
|
883
884
|
instructions: task.task.text,
|
|
@@ -885,7 +886,7 @@ function buildDispatchInput(task) {
|
|
|
885
886
|
});
|
|
886
887
|
}
|
|
887
888
|
function buildDispatchMetadata(args) {
|
|
888
|
-
if (!args.task.task.text?.trim()) throw new Error("Scheduled
|
|
889
|
+
if (!args.task.task.text?.trim()) throw new Error("Scheduled automation text is required");
|
|
889
890
|
return {
|
|
890
891
|
runId: args.run.id,
|
|
891
892
|
schedule: singleLineMetadataValue(args.task.schedule.description),
|
|
@@ -903,31 +904,31 @@ function buildDispatchMetadata(args) {
|
|
|
903
904
|
}
|
|
904
905
|
function replyAttribution$1(task) {
|
|
905
906
|
if (task.schedule.kind === "one_off") return {
|
|
906
|
-
label: "Scheduled
|
|
907
|
+
label: "Scheduled automation",
|
|
907
908
|
detail: "One-time"
|
|
908
909
|
};
|
|
909
910
|
const recurrence = task.schedule.recurrence;
|
|
910
911
|
if (!recurrence) return {
|
|
911
|
-
label: "Scheduled
|
|
912
|
+
label: "Scheduled automation",
|
|
912
913
|
detail: "Recurring"
|
|
913
914
|
};
|
|
914
915
|
const frequency = SCHEDULE_FREQUENCY_COPY[recurrence.frequency];
|
|
915
916
|
return recurrence.interval === 1 ? {
|
|
916
|
-
label: "Scheduled
|
|
917
|
+
label: "Scheduled automation",
|
|
917
918
|
detail: frequency.label
|
|
918
919
|
} : {
|
|
919
|
-
label: "Scheduled
|
|
920
|
+
label: "Scheduled automation",
|
|
920
921
|
detail: `Every ${recurrence.interval} ${frequency.unit}s`
|
|
921
922
|
};
|
|
922
923
|
}
|
|
923
924
|
function shouldSkipRun(task, run) {
|
|
924
|
-
if (task.status === "deleted") return `Scheduled
|
|
925
|
-
if (task.status !== "active") return `Scheduled
|
|
926
|
-
if (task.nextRunAtMs !== run.scheduledForMs && task.runNowAtMs !== run.scheduledForMs) return `Scheduled
|
|
925
|
+
if (task.status === "deleted") return `Scheduled automation ${task.id} was deleted before the run started.`;
|
|
926
|
+
if (task.status !== "active") return `Scheduled automation ${task.id} was ${task.status} before the run started.`;
|
|
927
|
+
if (task.nextRunAtMs !== run.scheduledForMs && task.runNowAtMs !== run.scheduledForMs) return `Scheduled automation ${task.id} no longer targets ${new Date(run.scheduledForMs).toISOString()}.`;
|
|
927
928
|
}
|
|
928
929
|
async function recordScheduledExecution(args) {
|
|
929
930
|
const dispatchId = args.run.dispatchId;
|
|
930
|
-
await
|
|
931
|
+
await recordAutomationExecution("scheduled", args.run.taskId, {
|
|
931
932
|
...dispatchId ? { conversationId: getDispatchConversationId({ id: dispatchId }) } : void 0,
|
|
932
933
|
executionId: args.run.id,
|
|
933
934
|
nowMs: args.nowMs,
|
|
@@ -935,7 +936,7 @@ async function recordScheduledExecution(args) {
|
|
|
935
936
|
});
|
|
936
937
|
}
|
|
937
938
|
async function logRunOutcome(args) {
|
|
938
|
-
const task = args.task ?? await
|
|
939
|
+
const task = args.task ?? await readScheduledAutomation(args.db, args.run.taskId);
|
|
939
940
|
if (!task) {
|
|
940
941
|
logInfo(args.eventName, {
|
|
941
942
|
"app.task.id": args.run.taskId,
|
|
@@ -949,7 +950,7 @@ async function logRunOutcome(args) {
|
|
|
949
950
|
});
|
|
950
951
|
return;
|
|
951
952
|
}
|
|
952
|
-
logInfo(args.eventName,
|
|
953
|
+
logInfo(args.eventName, scheduledAutomationRunAttributes(task, args.run, args.extras));
|
|
953
954
|
}
|
|
954
955
|
async function applyDispatchResult(args) {
|
|
955
956
|
if (args.dispatch.status === "completed") {
|
|
@@ -960,7 +961,7 @@ async function applyDispatchResult(args) {
|
|
|
960
961
|
startedAtMs: args.run.startedAtMs
|
|
961
962
|
});
|
|
962
963
|
if (!completed) return false;
|
|
963
|
-
await
|
|
964
|
+
await advanceScheduledAutomationAfterRun(args.db, {
|
|
964
965
|
nowMs: args.nowMs,
|
|
965
966
|
run: args.run,
|
|
966
967
|
status: "completed"
|
|
@@ -971,7 +972,7 @@ async function applyDispatchResult(args) {
|
|
|
971
972
|
status: "completed"
|
|
972
973
|
});
|
|
973
974
|
await logRunOutcome({
|
|
974
|
-
eventName: "
|
|
975
|
+
eventName: "scheduled_automation.run.completed",
|
|
975
976
|
run: completed,
|
|
976
977
|
db: args.db
|
|
977
978
|
});
|
|
@@ -985,7 +986,7 @@ async function applyDispatchResult(args) {
|
|
|
985
986
|
startedAtMs: args.run.startedAtMs
|
|
986
987
|
});
|
|
987
988
|
if (!blocked) return false;
|
|
988
|
-
await
|
|
989
|
+
await advanceScheduledAutomationAfterRun(args.db, {
|
|
989
990
|
errorMessage: blocked.errorMessage,
|
|
990
991
|
nowMs: args.nowMs,
|
|
991
992
|
run: args.run,
|
|
@@ -997,7 +998,7 @@ async function applyDispatchResult(args) {
|
|
|
997
998
|
status: "blocked"
|
|
998
999
|
});
|
|
999
1000
|
await logRunOutcome({
|
|
1000
|
-
eventName: "
|
|
1001
|
+
eventName: "scheduled_automation.run.blocked",
|
|
1001
1002
|
run: blocked,
|
|
1002
1003
|
db: args.db,
|
|
1003
1004
|
extras: blocked.errorMessage ? { "app.task.run.error": blocked.errorMessage } : void 0
|
|
@@ -1012,7 +1013,7 @@ async function applyDispatchResult(args) {
|
|
|
1012
1013
|
startedAtMs: args.run.startedAtMs
|
|
1013
1014
|
});
|
|
1014
1015
|
if (!failed) return false;
|
|
1015
|
-
await
|
|
1016
|
+
await advanceScheduledAutomationAfterRun(args.db, {
|
|
1016
1017
|
errorMessage: failed.errorMessage,
|
|
1017
1018
|
nowMs: args.nowMs,
|
|
1018
1019
|
run: args.run,
|
|
@@ -1024,7 +1025,7 @@ async function applyDispatchResult(args) {
|
|
|
1024
1025
|
status: "failed"
|
|
1025
1026
|
});
|
|
1026
1027
|
await logRunOutcome({
|
|
1027
|
-
eventName: "
|
|
1028
|
+
eventName: "scheduled_automation.run.failed",
|
|
1028
1029
|
run: failed,
|
|
1029
1030
|
db: args.db,
|
|
1030
1031
|
extras: failed.errorMessage ? { "app.task.run.error": failed.errorMessage } : void 0
|
|
@@ -1045,7 +1046,7 @@ async function finishClaimedRun(args) {
|
|
|
1045
1046
|
startedAtMs: args.run.startedAtMs
|
|
1046
1047
|
});
|
|
1047
1048
|
if (!finished) return;
|
|
1048
|
-
await
|
|
1049
|
+
await advanceScheduledAutomationAfterRun(args.db, {
|
|
1049
1050
|
errorMessage: args.errorMessage,
|
|
1050
1051
|
nowMs: args.nowMs,
|
|
1051
1052
|
run: args.run,
|
|
@@ -1057,24 +1058,24 @@ async function finishClaimedRun(args) {
|
|
|
1057
1058
|
status: args.status
|
|
1058
1059
|
});
|
|
1059
1060
|
await logRunOutcome({
|
|
1060
|
-
eventName: args.status === "blocked" ? "
|
|
1061
|
+
eventName: args.status === "blocked" ? "scheduled_automation.run.blocked" : "scheduled_automation.run.failed",
|
|
1061
1062
|
run: finished,
|
|
1062
1063
|
db: args.db,
|
|
1063
1064
|
extras: { "app.task.run.error": args.errorMessage }
|
|
1064
1065
|
});
|
|
1065
1066
|
}
|
|
1066
1067
|
/** Reconcile completed dispatches and enqueue a bounded batch of due tasks. */
|
|
1067
|
-
async function
|
|
1068
|
+
async function runScheduledAutomationHeartbeat(args) {
|
|
1068
1069
|
const db = getDb();
|
|
1069
1070
|
let processedCount = 0;
|
|
1070
1071
|
let dispatchCount = 0;
|
|
1071
1072
|
for (const run of await listIncompleteScheduledRuns(db)) {
|
|
1072
1073
|
if (!run.dispatchId) continue;
|
|
1073
|
-
const dispatch = await
|
|
1074
|
+
const dispatch = await getScheduledAutomationDispatch(run.dispatchId);
|
|
1074
1075
|
if (!dispatch) {
|
|
1075
1076
|
await finishClaimedRun({
|
|
1076
1077
|
db,
|
|
1077
|
-
errorMessage: "Scheduled
|
|
1078
|
+
errorMessage: "Scheduled automation dispatch record is missing.",
|
|
1078
1079
|
nowMs: args.nowMs,
|
|
1079
1080
|
run,
|
|
1080
1081
|
status: "failed"
|
|
@@ -1088,25 +1089,25 @@ async function runScheduledTaskHeartbeat(args) {
|
|
|
1088
1089
|
run
|
|
1089
1090
|
})) processedCount += 1;
|
|
1090
1091
|
}
|
|
1091
|
-
for (let index = processedCount; index <
|
|
1092
|
+
for (let index = processedCount; index < SCHEDULED_AUTOMATION_HEARTBEAT_LIMIT; index += 1) {
|
|
1092
1093
|
const run = await claimDueScheduledRun(db, { nowMs: args.nowMs });
|
|
1093
1094
|
if (!run) break;
|
|
1094
|
-
const task = await
|
|
1095
|
+
const task = await readScheduledAutomation(db, run.taskId);
|
|
1095
1096
|
if (!task) {
|
|
1096
1097
|
const failed = await markScheduledRunFailed(db, {
|
|
1097
1098
|
completedAtMs: args.nowMs,
|
|
1098
|
-
errorMessage: `Scheduled
|
|
1099
|
+
errorMessage: `Scheduled automation ${run.taskId} was not found`,
|
|
1099
1100
|
runId: run.id
|
|
1100
1101
|
});
|
|
1101
1102
|
if (failed) await logRunOutcome({
|
|
1102
|
-
eventName: "
|
|
1103
|
+
eventName: "scheduled_automation.run.failed",
|
|
1103
1104
|
run: failed,
|
|
1104
1105
|
db,
|
|
1105
|
-
extras: { "app.task.run.error": `Scheduled
|
|
1106
|
+
extras: { "app.task.run.error": `Scheduled automation ${run.taskId} was not found` }
|
|
1106
1107
|
});
|
|
1107
1108
|
continue;
|
|
1108
1109
|
}
|
|
1109
|
-
logInfo("
|
|
1110
|
+
logInfo("scheduled_automation.run.claimed", scheduledAutomationRunAttributes(task, run));
|
|
1110
1111
|
const skippedReason = shouldSkipRun(task, run);
|
|
1111
1112
|
if (skippedReason) {
|
|
1112
1113
|
const skipped = await markScheduledRunSkipped(db, {
|
|
@@ -1114,7 +1115,7 @@ async function runScheduledTaskHeartbeat(args) {
|
|
|
1114
1115
|
errorMessage: skippedReason,
|
|
1115
1116
|
runId: run.id
|
|
1116
1117
|
});
|
|
1117
|
-
if (skipped)
|
|
1118
|
+
if (skipped) logScheduledAutomationRunSkipped(task, skipped, { "app.task.run.error": skippedReason });
|
|
1118
1119
|
continue;
|
|
1119
1120
|
}
|
|
1120
1121
|
let metadata;
|
|
@@ -1127,7 +1128,7 @@ async function runScheduledTaskHeartbeat(args) {
|
|
|
1127
1128
|
} catch (error) {
|
|
1128
1129
|
await finishClaimedRun({
|
|
1129
1130
|
db,
|
|
1130
|
-
errorMessage: error instanceof Error ? `Scheduled
|
|
1131
|
+
errorMessage: error instanceof Error ? `Scheduled automation dispatch metadata could not be built: ${error.message}` : "Scheduled automation dispatch metadata could not be built.",
|
|
1131
1132
|
nowMs: args.nowMs,
|
|
1132
1133
|
run,
|
|
1133
1134
|
status: "blocked"
|
|
@@ -1136,7 +1137,7 @@ async function runScheduledTaskHeartbeat(args) {
|
|
|
1136
1137
|
}
|
|
1137
1138
|
let dispatch;
|
|
1138
1139
|
try {
|
|
1139
|
-
dispatch = await
|
|
1140
|
+
dispatch = await dispatchScheduledAutomation({
|
|
1140
1141
|
conversationWorkQueue: args.conversationWorkQueue,
|
|
1141
1142
|
nowMs: args.nowMs,
|
|
1142
1143
|
options: {
|
|
@@ -1144,7 +1145,7 @@ async function runScheduledTaskHeartbeat(args) {
|
|
|
1144
1145
|
...task.credentialMode === "creator" ? { credentialSubject: {
|
|
1145
1146
|
type: "user",
|
|
1146
1147
|
userId: task.createdBy.slackUserId,
|
|
1147
|
-
allowedWhen: "scheduled-
|
|
1148
|
+
allowedWhen: "scheduled-automation",
|
|
1148
1149
|
taskId: task.id
|
|
1149
1150
|
} } : void 0,
|
|
1150
1151
|
destination: task.destination,
|
|
@@ -1158,7 +1159,7 @@ async function runScheduledTaskHeartbeat(args) {
|
|
|
1158
1159
|
} catch (error) {
|
|
1159
1160
|
await finishClaimedRun({
|
|
1160
1161
|
db,
|
|
1161
|
-
errorMessage: error instanceof Error ? `Scheduled
|
|
1162
|
+
errorMessage: error instanceof Error ? `Scheduled automation dispatch could not be created: ${error.message}` : "Scheduled automation dispatch could not be created.",
|
|
1162
1163
|
nowMs: args.nowMs,
|
|
1163
1164
|
run,
|
|
1164
1165
|
status: "blocked"
|
|
@@ -1171,7 +1172,7 @@ async function runScheduledTaskHeartbeat(args) {
|
|
|
1171
1172
|
nowMs: args.nowMs,
|
|
1172
1173
|
runId: run.id
|
|
1173
1174
|
});
|
|
1174
|
-
logInfo("
|
|
1175
|
+
logInfo("scheduled_automation.run.dispatched", scheduledAutomationRunAttributes(task, dispatched ?? {
|
|
1175
1176
|
...run,
|
|
1176
1177
|
dispatchId: dispatch.id
|
|
1177
1178
|
}, { "app.dispatch.id": dispatch.id }));
|
|
@@ -1276,13 +1277,13 @@ async function runHeartbeat(args) {
|
|
|
1276
1277
|
nowMs: args.nowMs
|
|
1277
1278
|
});
|
|
1278
1279
|
try {
|
|
1279
|
-
const dispatchCount = await
|
|
1280
|
+
const dispatchCount = await runScheduledAutomationHeartbeat({
|
|
1280
1281
|
conversationWorkQueue: queue,
|
|
1281
1282
|
nowMs: args.nowMs
|
|
1282
1283
|
});
|
|
1283
|
-
if (dispatchCount > 0) logInfo("
|
|
1284
|
+
if (dispatchCount > 0) logInfo("scheduled_automations.heartbeat.dispatched", { "app.dispatch.count": dispatchCount });
|
|
1284
1285
|
} catch (error) {
|
|
1285
|
-
logException(error, "
|
|
1286
|
+
logException(error, "scheduled_automations.heartbeat.failed");
|
|
1286
1287
|
}
|
|
1287
1288
|
await runPluginHeartbeats({
|
|
1288
1289
|
conversationWorkQueue: queue,
|
|
@@ -1316,47 +1317,6 @@ async function GET$2(request, waitUntil, options = {}) {
|
|
|
1316
1317
|
return new Response("Accepted", { status: 202 });
|
|
1317
1318
|
}
|
|
1318
1319
|
//#endregion
|
|
1319
|
-
//#region src/chat/conversations/sql/privacy.ts
|
|
1320
|
-
const privacyRoot = alias(juniorConversations, "privacy_root");
|
|
1321
|
-
/** Accept only a structurally valid persisted root as privacy authority. */
|
|
1322
|
-
async function readRootCandidate(executor, conversationId) {
|
|
1323
|
-
const row = (await executor.db().select({
|
|
1324
|
-
requestedConversationId: juniorConversations.conversationId,
|
|
1325
|
-
requestedParentConversationId: juniorConversations.parentConversationId,
|
|
1326
|
-
requestedRootConversationId: juniorConversations.rootConversationId,
|
|
1327
|
-
rootConversationId: privacyRoot.conversationId,
|
|
1328
|
-
rootDestinationId: privacyRoot.destinationId,
|
|
1329
|
-
rootParentConversationId: privacyRoot.parentConversationId,
|
|
1330
|
-
rootRootConversationId: privacyRoot.rootConversationId
|
|
1331
|
-
}).from(juniorConversations).leftJoin(privacyRoot, eq(privacyRoot.conversationId, juniorConversations.rootConversationId)).where(eq(juniorConversations.conversationId, conversationId)))[0];
|
|
1332
|
-
return row?.requestedRootConversationId !== null && (row?.requestedParentConversationId !== null || row?.requestedRootConversationId === row?.requestedConversationId) && row?.rootConversationId === row.requestedRootConversationId && row.rootRootConversationId === row.rootConversationId && row.rootParentConversationId === null && row.rootDestinationId !== null ? {
|
|
1333
|
-
destinationId: row.rootDestinationId,
|
|
1334
|
-
rootConversationId: row.rootConversationId
|
|
1335
|
-
} : void 0;
|
|
1336
|
-
}
|
|
1337
|
-
async function readCandidateVisibility(executor, candidate) {
|
|
1338
|
-
const destinations = await executor.db().select({ visibility: juniorDestinations.visibility }).from(juniorDestinations).where(eq(juniorDestinations.id, candidate.destinationId)).for("share");
|
|
1339
|
-
return {
|
|
1340
|
-
rootConversationId: candidate.rootConversationId,
|
|
1341
|
-
visibility: destinations[0]?.visibility ?? null
|
|
1342
|
-
};
|
|
1343
|
-
}
|
|
1344
|
-
/**
|
|
1345
|
-
* Resolve a conversation's privacy authority from its persisted root.
|
|
1346
|
-
*
|
|
1347
|
-
* Missing or structurally invalid root metadata fails closed. The root
|
|
1348
|
-
* destination is locked so callers that keep the transaction open receive a
|
|
1349
|
-
* stable visibility decision.
|
|
1350
|
-
*/
|
|
1351
|
-
async function resolveRootVisibility(executor, conversationId) {
|
|
1352
|
-
const candidate = await readRootCandidate(executor, conversationId);
|
|
1353
|
-
if (!candidate) return {
|
|
1354
|
-
rootConversationId: conversationId,
|
|
1355
|
-
visibility: null
|
|
1356
|
-
};
|
|
1357
|
-
return readCandidateVisibility(executor, candidate);
|
|
1358
|
-
}
|
|
1359
|
-
//#endregion
|
|
1360
1320
|
//#region src/chat/conversations/sql/purge.ts
|
|
1361
1321
|
/** Discover a root and its current descendants via `parent_conversation_id`. */
|
|
1362
1322
|
async function discoverConversationTree(executor, root) {
|
|
@@ -1423,14 +1383,20 @@ async function selectExpiredRoots(executor, args) {
|
|
|
1423
1383
|
)
|
|
1424
1384
|
or (
|
|
1425
1385
|
${juniorDestinations.visibility} is distinct from 'public'
|
|
1426
|
-
and
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1386
|
+
and (
|
|
1387
|
+
exists (
|
|
1388
|
+
select 1 from junior_conversation_briefs briefs
|
|
1389
|
+
where briefs.conversation_id = tree.conversation_id
|
|
1390
|
+
)
|
|
1391
|
+
or exists (
|
|
1392
|
+
select 1 from junior_conversations metadata
|
|
1393
|
+
where metadata.conversation_id = tree.conversation_id
|
|
1394
|
+
and (
|
|
1395
|
+
metadata.title is not null
|
|
1396
|
+
or metadata.channel_name is not null
|
|
1397
|
+
or metadata.actor_json is not null
|
|
1398
|
+
)
|
|
1399
|
+
)
|
|
1434
1400
|
)
|
|
1435
1401
|
)
|
|
1436
1402
|
)`;
|
|
@@ -1493,9 +1459,11 @@ async function purgeConversationTree(executor, args) {
|
|
|
1493
1459
|
await executor.db().delete(juniorAgentInvocations).where(or(inArray(juniorAgentInvocations.parentConversationId, ids), inArray(juniorAgentInvocations.childConversationId, ids)));
|
|
1494
1460
|
await executor.db().delete(juniorAgentBindings).where(or(inArray(juniorAgentBindings.parentConversationId, ids), inArray(juniorAgentBindings.childConversationId, ids)));
|
|
1495
1461
|
await requestAttachmentDeletion(executor, ids, args.nowMs);
|
|
1462
|
+
const scrubMetadata = args.retention ? !isPublic : resolvedScrubMetadata;
|
|
1463
|
+
if (scrubMetadata) await executor.db().delete(juniorConversationBriefs).where(inArray(juniorConversationBriefs.conversationId, ids));
|
|
1496
1464
|
await executor.db().update(juniorConversations).set({
|
|
1497
1465
|
transcriptPurgedAt: new Date(args.nowMs),
|
|
1498
|
-
...
|
|
1466
|
+
...scrubMetadata ? {
|
|
1499
1467
|
title: null,
|
|
1500
1468
|
channelName: null,
|
|
1501
1469
|
actor: null
|
|
@@ -2503,7 +2471,7 @@ function createSlackTurnRuntime(deps) {
|
|
|
2503
2471
|
/** Unsubscribe the thread and cancel resource watches for a stop decision. */
|
|
2504
2472
|
const maybeHandleThreadOptOutDecision = async (args) => {
|
|
2505
2473
|
if (!args.decision?.shouldUnsubscribe) return false;
|
|
2506
|
-
await deps.
|
|
2474
|
+
await deps.cancelWatches({ conversationId: args.thread.id });
|
|
2507
2475
|
await args.thread.unsubscribe();
|
|
2508
2476
|
await args.beforeFirstResponsePost?.();
|
|
2509
2477
|
await args.thread.post(THREAD_OPTOUT_ACK);
|
|
@@ -3167,9 +3135,9 @@ function createSlackTurnRuntime(deps) {
|
|
|
3167
3135
|
* after no deployed Turn cursor can omit either field.
|
|
3168
3136
|
*/
|
|
3169
3137
|
function turnInputFactsFromConversationMessage(message, args) {
|
|
3170
|
-
if (
|
|
3171
|
-
actor: args.savedActor ??
|
|
3172
|
-
author: message.author ??
|
|
3138
|
+
if (isEventConversationMessage(message)) return {
|
|
3139
|
+
actor: args.savedActor ?? EVENT_SYSTEM_ACTOR,
|
|
3140
|
+
author: message.author ?? EVENT_MESSAGE_AUTHOR,
|
|
3173
3141
|
source: args.savedSource ?? (args.location ? createWebSource(args.conversationId, args.visibility === "private" ? "private" : "public") : createLocalSource(args.conversationId))
|
|
3174
3142
|
};
|
|
3175
3143
|
const actor = args.savedActor ?? createActor(message.author, {
|
|
@@ -3188,8 +3156,8 @@ function sourceFromTurnInput(args) {
|
|
|
3188
3156
|
if (args.source.kind === "web") return createWebSource(args.conversationId, args.visibility === "private" ? "private" : "public");
|
|
3189
3157
|
return args.source;
|
|
3190
3158
|
}
|
|
3191
|
-
function
|
|
3192
|
-
return Boolean(actors?.some((actor) => actor.platform === "system" && actor.name ===
|
|
3159
|
+
function hasEventActor(actors) {
|
|
3160
|
+
return Boolean(actors?.some((actor) => actor.platform === "system" && actor.name === EVENT_SYSTEM_ACTOR.name));
|
|
3193
3161
|
}
|
|
3194
3162
|
/** Return the active web Turn for one Conversation, if it has one. */
|
|
3195
3163
|
async function getActiveConversationTurnId(conversationId) {
|
|
@@ -3200,10 +3168,10 @@ async function getActiveConversationTurnId(conversationId) {
|
|
|
3200
3168
|
const record = await getTurnRecord(conversationId, turnId);
|
|
3201
3169
|
return record && (record.source?.kind === "web" || !record.source && record.surface === "api") && !record.dispatchId && (record.state === "paused" || record.state === "running") ? turnId : void 0;
|
|
3202
3170
|
}
|
|
3203
|
-
/** Return the active web or
|
|
3171
|
+
/** Return the active web or event mailbox Turn. */
|
|
3204
3172
|
async function getActiveMailboxTurnId(conversationId) {
|
|
3205
3173
|
const summaries = await listTurnSummaries(conversationId);
|
|
3206
|
-
const active = (await Promise.all(summaries.filter((summary) => !summary.dispatchId && (summary.state === "paused" || summary.state === "running")).map((summary) => getTurnRecord(conversationId, summary.turnId)))).filter((record) => record && !record.dispatchId && (record.source?.kind === "web" || record.source?.kind === "
|
|
3174
|
+
const active = (await Promise.all(summaries.filter((summary) => !summary.dispatchId && (summary.state === "paused" || summary.state === "running")).map((summary) => getTurnRecord(conversationId, summary.turnId)))).filter((record) => record && !record.dispatchId && (record.source?.kind === "web" || record.source?.kind === "event" || !record.source && (record.surface === "api" || hasEventActor(record.actors))) && (record.state === "paused" || record.state === "running"));
|
|
3207
3175
|
if (active.length > 1) throw new Error(`Conversation ${conversationId} has multiple active mailbox Turns`);
|
|
3208
3176
|
return active[0]?.turnId;
|
|
3209
3177
|
}
|
|
@@ -3245,27 +3213,27 @@ function parseWebMessages(messages) {
|
|
|
3245
3213
|
};
|
|
3246
3214
|
});
|
|
3247
3215
|
}
|
|
3248
|
-
function
|
|
3216
|
+
function parseEventMessages(messages) {
|
|
3249
3217
|
if (messages.length === 0) return [];
|
|
3250
|
-
if (!messages.every((message) => message.source === "
|
|
3218
|
+
if (!messages.every((message) => message.source === "event" && isEventMailboxMetadata(message.input.metadata))) return [];
|
|
3251
3219
|
return messages.map((message) => {
|
|
3252
|
-
if (!
|
|
3220
|
+
if (!isEventMailboxMetadata(message.input.metadata)) throw new Error("Event has invalid metadata");
|
|
3253
3221
|
return {
|
|
3254
|
-
actor:
|
|
3255
|
-
author:
|
|
3222
|
+
actor: EVENT_SYSTEM_ACTOR,
|
|
3223
|
+
author: EVENT_MESSAGE_AUTHOR,
|
|
3256
3224
|
message,
|
|
3257
|
-
source:
|
|
3258
|
-
eventKey: message.input.metadata.
|
|
3259
|
-
eventType: message.input.metadata.
|
|
3260
|
-
identifier: message.input.metadata.
|
|
3261
|
-
namespace: message.input.metadata.
|
|
3225
|
+
source: createEventSource({
|
|
3226
|
+
eventKey: message.input.metadata.event.eventKey,
|
|
3227
|
+
eventType: message.input.metadata.event.eventType,
|
|
3228
|
+
identifier: message.input.metadata.event.identifier,
|
|
3229
|
+
namespace: message.input.metadata.event.namespace
|
|
3262
3230
|
})
|
|
3263
3231
|
};
|
|
3264
3232
|
});
|
|
3265
3233
|
}
|
|
3266
3234
|
/**
|
|
3267
|
-
* Select web or
|
|
3268
|
-
* Saved Actor data identifies a resumed
|
|
3235
|
+
* Select web or event work from new mailbox input or a saved Turn.
|
|
3236
|
+
* Saved Actor data identifies a resumed event Turn.
|
|
3269
3237
|
*/
|
|
3270
3238
|
async function resolveMailboxTurnWork(context) {
|
|
3271
3239
|
const batch = parseWebMessages(context.attempt.messages);
|
|
@@ -3273,7 +3241,7 @@ async function resolveMailboxTurnWork(context) {
|
|
|
3273
3241
|
kind: "mailbox",
|
|
3274
3242
|
batch
|
|
3275
3243
|
};
|
|
3276
|
-
const resourceBatch =
|
|
3244
|
+
const resourceBatch = parseEventMessages(context.attempt.messages);
|
|
3277
3245
|
if (resourceBatch.length > 0) return {
|
|
3278
3246
|
kind: "mailbox",
|
|
3279
3247
|
batch: resourceBatch
|
|
@@ -3688,8 +3656,8 @@ const slackConversationMessageMetadataBaseSchema = z.object({
|
|
|
3688
3656
|
thread: serializedThreadSchema
|
|
3689
3657
|
});
|
|
3690
3658
|
const slackConversationMessageMetadataSchema = z.union([slackConversationMessageMetadataBaseSchema.strict(), slackConversationMessageMetadataBaseSchema.extend({
|
|
3691
|
-
kind: z.literal("
|
|
3692
|
-
|
|
3659
|
+
kind: z.literal("event"),
|
|
3660
|
+
event: z.object({
|
|
3693
3661
|
eventKey: z.string(),
|
|
3694
3662
|
eventType: z.string(),
|
|
3695
3663
|
namespace: z.string(),
|
|
@@ -6802,7 +6770,7 @@ function createSlackRuntime(options) {
|
|
|
6802
6770
|
return {
|
|
6803
6771
|
...createSlackTurnRuntime({
|
|
6804
6772
|
assistantUserName: botConfig.userName,
|
|
6805
|
-
|
|
6773
|
+
cancelWatches: cancelSubscriptions,
|
|
6806
6774
|
getBotUserId: () => options.getSlackAdapter().botUserId,
|
|
6807
6775
|
modelId: defaultModelId(botConfig),
|
|
6808
6776
|
now: options.now ?? (() => Date.now()),
|
|
@@ -7004,7 +6972,7 @@ async function resolveSlackResumeUserActor(args) {
|
|
|
7004
6972
|
* Sources, in order:
|
|
7005
6973
|
* 1. Actor saved on the Turn.
|
|
7006
6974
|
* 2. Actor and credentials supplied by dispatch or OAuth.
|
|
7007
|
-
* 3. Legacy
|
|
6975
|
+
* 3. Legacy Event or Slack Message data.
|
|
7008
6976
|
*
|
|
7009
6977
|
* TODO(dcramer): Remove the routing and Message Actor fallbacks after no
|
|
7010
6978
|
* deployed Turn cursor can omit Actor.
|
|
@@ -7019,7 +6987,7 @@ async function resolveResumeExecutionIdentity(args) {
|
|
|
7019
6987
|
} : void 0;
|
|
7020
6988
|
}
|
|
7021
6989
|
let actor = args.actor ?? routing?.actor;
|
|
7022
|
-
if (!actor &&
|
|
6990
|
+
if (!actor && isEventConversationMessage(args.userMessage)) actor = EVENT_SYSTEM_ACTOR;
|
|
7023
6991
|
if (!actor && args.userMessage.author?.userId) actor = await resolveSlackResumeUserActor({
|
|
7024
6992
|
conversationId: args.conversationId,
|
|
7025
6993
|
teamId: args.teamId,
|
|
@@ -7440,7 +7408,7 @@ async function completeCancelledConversationTurn(args) {
|
|
|
7440
7408
|
await args.acknowledge();
|
|
7441
7409
|
}
|
|
7442
7410
|
/**
|
|
7443
|
-
* Create the shared mailbox worker for web and
|
|
7411
|
+
* Create the shared mailbox worker for web and event Turns.
|
|
7444
7412
|
*/
|
|
7445
7413
|
function createConversationTurnWorker(agentRunner, deliverMessage) {
|
|
7446
7414
|
return async (context, resolved) => {
|
|
@@ -7452,7 +7420,7 @@ function createConversationTurnWorker(agentRunner, deliverMessage) {
|
|
|
7452
7420
|
let userMessageId = "";
|
|
7453
7421
|
let startedAtMs = Date.now();
|
|
7454
7422
|
let inputMessageIds = [];
|
|
7455
|
-
let
|
|
7423
|
+
let event;
|
|
7456
7424
|
const storedConversation = await getConversationStore().get({ conversationId: context.conversationId });
|
|
7457
7425
|
const mailboxDestination = resolved.kind === "mailbox" ? resolved.batch[0]?.message.destination : void 0;
|
|
7458
7426
|
const destination = resolveConversationDestination({
|
|
@@ -7467,7 +7435,7 @@ function createConversationTurnWorker(agentRunner, deliverMessage) {
|
|
|
7467
7435
|
turnId = buildDeterministicTurnId(userMessageId);
|
|
7468
7436
|
inputMessageIds = resolved.batch.map((entry) => entry.message.inboundMessageId);
|
|
7469
7437
|
turnInputFacts = first;
|
|
7470
|
-
if (
|
|
7438
|
+
if (isEventMailboxMetadata(first.message.input.metadata)) event = first.message.input.metadata.event;
|
|
7471
7439
|
} else turnId = resolved.turnId;
|
|
7472
7440
|
const persisted = await getPersistedThreadState(context.conversationId);
|
|
7473
7441
|
const conversation = coerceThreadConversationState(persisted);
|
|
@@ -7476,11 +7444,11 @@ function createConversationTurnWorker(agentRunner, deliverMessage) {
|
|
|
7476
7444
|
conversationId: context.conversationId
|
|
7477
7445
|
});
|
|
7478
7446
|
const savedTurn = isResume ? await getTurnRecord(context.conversationId, turnId) : void 0;
|
|
7479
|
-
let
|
|
7447
|
+
let savedMessageIsEvent = false;
|
|
7480
7448
|
if (isResume) {
|
|
7481
7449
|
const userMessage = getTurnUserMessage(conversation, turnId);
|
|
7482
7450
|
if (!userMessage) throw new Error(`Unable to locate the persisted user message for Turn "${turnId}"`);
|
|
7483
|
-
|
|
7451
|
+
savedMessageIsEvent = isEventConversationMessage(userMessage);
|
|
7484
7452
|
turnInputFacts = turnInputFactsFromConversationMessage(userMessage, {
|
|
7485
7453
|
conversationId: context.conversationId,
|
|
7486
7454
|
location: storedConversation?.location,
|
|
@@ -7502,7 +7470,7 @@ function createConversationTurnWorker(agentRunner, deliverMessage) {
|
|
|
7502
7470
|
});
|
|
7503
7471
|
const webActor = actor.platform === "web" ? actor : void 0;
|
|
7504
7472
|
const conversationLocation = storedConversation?.location;
|
|
7505
|
-
const deliverToProvider = Boolean(conversationLocation) && (source.kind === "
|
|
7473
|
+
const deliverToProvider = Boolean(conversationLocation) && (source.kind === "event" || savedMessageIsEvent);
|
|
7506
7474
|
const surface = deliverToProvider ? "slack" : "api";
|
|
7507
7475
|
return await withLogContext({
|
|
7508
7476
|
conversationId: context.conversationId,
|
|
@@ -7554,9 +7522,9 @@ function createConversationTurnWorker(agentRunner, deliverMessage) {
|
|
|
7554
7522
|
explicitMention: true,
|
|
7555
7523
|
replied: false,
|
|
7556
7524
|
...source.kind === "web" ? { source: "web" } : void 0,
|
|
7557
|
-
...
|
|
7558
|
-
eventType:
|
|
7559
|
-
...
|
|
7525
|
+
...event ? {
|
|
7526
|
+
eventType: event.eventType,
|
|
7527
|
+
...event.trustedSummary ? { trustedSummary: event.trustedSummary } : void 0
|
|
7560
7528
|
} : void 0
|
|
7561
7529
|
}
|
|
7562
7530
|
});
|
|
@@ -7828,7 +7796,7 @@ function createConversationTurnWorker(agentRunner, deliverMessage) {
|
|
|
7828
7796
|
* Deliver system Turn output to Slack without a webhook Message.
|
|
7829
7797
|
*
|
|
7830
7798
|
* TODO(dcramer): Replace this Location-taking function with Delivery bound to one
|
|
7831
|
-
* Location after
|
|
7799
|
+
* Location after Event work supplies Delivery before the Run.
|
|
7832
7800
|
*/
|
|
7833
7801
|
function createSlackSystemTurnDelivery(args) {
|
|
7834
7802
|
return async ({ conversationId, location, text }) => {
|
|
@@ -8009,60 +7977,60 @@ async function publicArtifactGET(args) {
|
|
|
8009
7977
|
}
|
|
8010
7978
|
}
|
|
8011
7979
|
//#endregion
|
|
8012
|
-
//#region src/chat/event-
|
|
7980
|
+
//#region src/chat/event-automations/ingest.ts
|
|
8013
7981
|
/**
|
|
8014
|
-
* Owns
|
|
7982
|
+
* Owns event matching and durable event-automation dispatch.
|
|
8015
7983
|
*
|
|
8016
|
-
* Each matching
|
|
7984
|
+
* Each matching automation is independently idempotent. Aggregate failures propagate
|
|
8017
7985
|
* so the provider can retry the original delivery.
|
|
8018
7986
|
*/
|
|
8019
|
-
/** Bind provider delivery identity to one
|
|
8020
|
-
function
|
|
8021
|
-
return `event-
|
|
7987
|
+
/** Bind provider delivery identity to one automation's durable dispatch. */
|
|
7988
|
+
function eventAutomationDispatchKey(taskId, namespace, eventKey) {
|
|
7989
|
+
return `event-automation:${createHash("sha256").update(`${taskId}\0${namespace}\0${eventKey}`).digest("hex").slice(0, 32)}`;
|
|
8022
7990
|
}
|
|
8023
7991
|
function oneLine(value) {
|
|
8024
7992
|
return value.replace(/[\r\n]+/g, " ").replace(/\s+/g, " ").trim().slice(0, 512);
|
|
8025
7993
|
}
|
|
8026
|
-
/** Compact destination-visible context for event-
|
|
7994
|
+
/** Compact destination-visible context for event-automation replies. */
|
|
8027
7995
|
function replyAttribution(task) {
|
|
8028
7996
|
const detail = oneLine(task.trigger.label).slice(0, 128).trim();
|
|
8029
7997
|
return detail ? {
|
|
8030
|
-
label: "Event
|
|
7998
|
+
label: "Event automation",
|
|
8031
7999
|
detail
|
|
8032
|
-
} : { label: "Event
|
|
8000
|
+
} : { label: "Event automation" };
|
|
8033
8001
|
}
|
|
8034
|
-
/** Render plain agent input for one matching event
|
|
8002
|
+
/** Render plain agent input for one matching event automation. */
|
|
8035
8003
|
function eventInput(task, event) {
|
|
8036
|
-
const guidance =
|
|
8004
|
+
const guidance = eventGuidance(getEventCatalog(), event.namespace, task.trigger.resourceType, event.eventType);
|
|
8037
8005
|
return renderTaskInput({
|
|
8038
8006
|
about: task.trigger.label,
|
|
8039
8007
|
instructions: task.task.text,
|
|
8040
8008
|
outcomes: effectiveTaskOutcomes(task.outcomes, task.destination),
|
|
8041
8009
|
guidance,
|
|
8042
8010
|
trustedSummary: event.trustedSummary,
|
|
8043
|
-
trustedSummaryMaxLength:
|
|
8011
|
+
trustedSummaryMaxLength: EVENT_SUMMARY_MAX_LENGTH,
|
|
8044
8012
|
verifiedDetails: event.data,
|
|
8045
8013
|
externalText: event.untrustedText,
|
|
8046
|
-
externalTextMaxLength:
|
|
8014
|
+
externalTextMaxLength: EVENT_TEXT_MAX_LENGTH
|
|
8047
8015
|
});
|
|
8048
8016
|
}
|
|
8049
|
-
/** Match a normalized
|
|
8050
|
-
async function
|
|
8051
|
-
const event =
|
|
8017
|
+
/** Match a normalized event and dispatch every matching automation. */
|
|
8018
|
+
async function ingestEventAutomations(input, options) {
|
|
8019
|
+
const event = eventSchema.parse(input);
|
|
8052
8020
|
const db = getDb();
|
|
8053
8021
|
const nowMs = options.nowMs ?? Date.now();
|
|
8054
|
-
const tasks = await
|
|
8022
|
+
const tasks = await findMatchingEventAutomations(db, event, options.teamId);
|
|
8055
8023
|
let dispatched = 0;
|
|
8056
8024
|
const errors = [];
|
|
8057
8025
|
for (const task of tasks) try {
|
|
8058
|
-
const idempotencyKey =
|
|
8026
|
+
const idempotencyKey = eventAutomationDispatchKey(task.id, event.namespace, event.eventKey);
|
|
8059
8027
|
const credentialSubject = task.credentialMode === "creator" ? {
|
|
8060
8028
|
type: "user",
|
|
8061
8029
|
userId: task.createdBy.slackUserId,
|
|
8062
|
-
allowedWhen: "event-
|
|
8030
|
+
allowedWhen: "event-automation",
|
|
8063
8031
|
taskId: task.id
|
|
8064
8032
|
} : void 0;
|
|
8065
|
-
if ((await
|
|
8033
|
+
if ((await dispatchEventAutomation({
|
|
8066
8034
|
conversationWorkQueue: options.queue,
|
|
8067
8035
|
nowMs,
|
|
8068
8036
|
options: {
|
|
@@ -8071,7 +8039,7 @@ async function ingestEventTasks(input, options) {
|
|
|
8071
8039
|
destination: task.destination,
|
|
8072
8040
|
destinationVisibility: task.destinationVisibility,
|
|
8073
8041
|
input: eventInput(task, event),
|
|
8074
|
-
metadata: {
|
|
8042
|
+
metadata: { eventAutomationId: task.id },
|
|
8075
8043
|
replyAttribution: replyAttribution(task),
|
|
8076
8044
|
outcomes: effectiveTaskOutcomes(task.outcomes, task.destination)
|
|
8077
8045
|
}
|
|
@@ -8079,19 +8047,19 @@ async function ingestEventTasks(input, options) {
|
|
|
8079
8047
|
} catch (error) {
|
|
8080
8048
|
errors.push(error);
|
|
8081
8049
|
}
|
|
8082
|
-
if (errors.length > 0) throw new AggregateError(errors, "Failed to dispatch one or more event
|
|
8050
|
+
if (errors.length > 0) throw new AggregateError(errors, "Failed to dispatch one or more event automations");
|
|
8083
8051
|
return { dispatched };
|
|
8084
8052
|
}
|
|
8085
8053
|
//#endregion
|
|
8086
|
-
//#region src/chat/
|
|
8054
|
+
//#region src/chat/events/workspace.ts
|
|
8087
8055
|
/**
|
|
8088
|
-
* Resolve the single-bot Slack team id for event-
|
|
8056
|
+
* Resolve the single-bot Slack team id for event-automation indexing.
|
|
8089
8057
|
*
|
|
8090
|
-
*
|
|
8091
|
-
* until that store is conversation-owned.
|
|
8058
|
+
* Watches do not use this. Event automations still key by destination team
|
|
8059
|
+
* until that store is conversation-owned. Event delivery itself is
|
|
8092
8060
|
* not gated on Slack.
|
|
8093
8061
|
*/
|
|
8094
|
-
function
|
|
8062
|
+
function createEventTeamIdResolver() {
|
|
8095
8063
|
let pending;
|
|
8096
8064
|
return async () => {
|
|
8097
8065
|
if (!getSlackBotToken()) return void 0;
|
|
@@ -8107,49 +8075,49 @@ function createResourceEventTeamIdResolver() {
|
|
|
8107
8075
|
};
|
|
8108
8076
|
}
|
|
8109
8077
|
//#endregion
|
|
8110
|
-
//#region src/chat/
|
|
8111
|
-
/** Build the core
|
|
8112
|
-
function
|
|
8113
|
-
const
|
|
8078
|
+
//#region src/chat/events/app-publisher.ts
|
|
8079
|
+
/** Build the core event publisher used by plugin routes. */
|
|
8080
|
+
function createEventAppPublisher(args) {
|
|
8081
|
+
const resolveEventAutomationTeamId = createEventTeamIdResolver();
|
|
8114
8082
|
return {
|
|
8115
8083
|
async hasMatch(event) {
|
|
8116
8084
|
const work = args.conversationWork();
|
|
8117
|
-
const
|
|
8118
|
-
const [subscriptions, tasks] = await Promise.all([
|
|
8085
|
+
const eventAutomationTeamId = await resolveEventAutomationTeamId();
|
|
8086
|
+
const [subscriptions, tasks] = await Promise.all([findMatchingWatches({
|
|
8119
8087
|
data: event.data,
|
|
8120
8088
|
eventType: event.eventType,
|
|
8121
8089
|
namespace: event.namespace,
|
|
8122
8090
|
identifier: event.identifier,
|
|
8123
8091
|
state: work.state
|
|
8124
|
-
}),
|
|
8092
|
+
}), eventAutomationTeamId ? findMatchingEventAutomations(getDb(), event, eventAutomationTeamId) : Promise.resolve([])]);
|
|
8125
8093
|
return subscriptions.length > 0 || tasks.length > 0;
|
|
8126
8094
|
},
|
|
8127
8095
|
async neededMatchKeys(input) {
|
|
8128
8096
|
const work = args.conversationWork();
|
|
8129
|
-
const
|
|
8130
|
-
const [watchKeys, taskKeys] = await Promise.all([
|
|
8097
|
+
const eventAutomationTeamId = await resolveEventAutomationTeamId();
|
|
8098
|
+
const [watchKeys, taskKeys] = await Promise.all([collectEventMatchKeys({
|
|
8131
8099
|
eventTypes: input.eventTypes,
|
|
8132
8100
|
identifiers: input.identifiers,
|
|
8133
8101
|
namespace: input.namespace,
|
|
8134
8102
|
state: work.state
|
|
8135
|
-
}),
|
|
8103
|
+
}), eventAutomationTeamId ? collectEventAutomationMatchKeys(getDb(), {
|
|
8136
8104
|
eventTypes: input.eventTypes,
|
|
8137
8105
|
identifiers: input.identifiers,
|
|
8138
8106
|
namespace: input.namespace,
|
|
8139
|
-
teamId:
|
|
8107
|
+
teamId: eventAutomationTeamId
|
|
8140
8108
|
}) : Promise.resolve([])]);
|
|
8141
8109
|
return [.../* @__PURE__ */ new Set([...watchKeys, ...taskKeys])].sort();
|
|
8142
8110
|
},
|
|
8143
8111
|
async publish(event) {
|
|
8144
8112
|
const work = args.conversationWork();
|
|
8145
8113
|
const queue = work.queue ?? getVercelConversationWorkQueue();
|
|
8146
|
-
const
|
|
8147
|
-
await Promise.all([
|
|
8114
|
+
const eventAutomationTeamId = await resolveEventAutomationTeamId();
|
|
8115
|
+
await Promise.all([ingestEvent(event, {
|
|
8148
8116
|
queue,
|
|
8149
8117
|
state: work.state
|
|
8150
|
-
}),
|
|
8118
|
+
}), eventAutomationTeamId ? ingestEventAutomations(event, {
|
|
8151
8119
|
queue,
|
|
8152
|
-
teamId:
|
|
8120
|
+
teamId: eventAutomationTeamId
|
|
8153
8121
|
}) : Promise.resolve()]);
|
|
8154
8122
|
}
|
|
8155
8123
|
};
|
|
@@ -10034,6 +10002,8 @@ function dashboardHostRoutePaths(dashboard, authenticatedRoutes = []) {
|
|
|
10034
10002
|
`${pagePath("locations")}/*`,
|
|
10035
10003
|
peoplePath,
|
|
10036
10004
|
`${peoplePath}/*`,
|
|
10005
|
+
pagePath("automations"),
|
|
10006
|
+
`${pagePath("automations")}/*`,
|
|
10037
10007
|
pagePath("tasks"),
|
|
10038
10008
|
`${pagePath("tasks")}/*`,
|
|
10039
10009
|
pagePath("memories"),
|
|
@@ -10062,8 +10032,8 @@ function dashboardHostRoutePaths(dashboard, authenticatedRoutes = []) {
|
|
|
10062
10032
|
"/api/plugin-reports",
|
|
10063
10033
|
"/api/user-pages",
|
|
10064
10034
|
"/api/user-pages/*",
|
|
10065
|
-
"/api/
|
|
10066
|
-
"/api/
|
|
10035
|
+
"/api/automations",
|
|
10036
|
+
"/api/automations/*",
|
|
10067
10037
|
"/api/skills",
|
|
10068
10038
|
"/api/code",
|
|
10069
10039
|
"/api/stats",
|
|
@@ -10169,6 +10139,7 @@ async function createApp(options) {
|
|
|
10169
10139
|
const previousSlackReactionConfig = getSlackReactionConfig();
|
|
10170
10140
|
const previousSandboxResources = getSandboxResourceConfig();
|
|
10171
10141
|
const previousExperimentalFeatures = getExperimentalFeatures();
|
|
10142
|
+
const previousBriefsConfig = setBriefsConfig(options?.briefs);
|
|
10172
10143
|
const previousDashboardLinkOptions = setDashboardConversationLinkOptions(dashboard);
|
|
10173
10144
|
const restoreRuntimeConfig = () => {
|
|
10174
10145
|
pluginCatalogRuntime.setConfig(previousPluginCatalogConfig);
|
|
@@ -10178,11 +10149,12 @@ async function createApp(options) {
|
|
|
10178
10149
|
setSlackReactionConfig(previousSlackReactionConfig);
|
|
10179
10150
|
setSandboxResourceConfig(previousSandboxResources);
|
|
10180
10151
|
setExperimentalFeatures(previousExperimentalFeatures);
|
|
10152
|
+
setBriefsConfig(previousBriefsConfig);
|
|
10181
10153
|
setDashboardConversationLinkOptions(previousDashboardLinkOptions);
|
|
10182
10154
|
};
|
|
10183
10155
|
let pluginRoutes = [];
|
|
10184
10156
|
let pluginApiRoutes = [];
|
|
10185
|
-
const
|
|
10157
|
+
const events = createEventAppPublisher({ conversationWork: () => getConversationWorkOptions() });
|
|
10186
10158
|
let sandboxEgressTracePropagationDomains = [];
|
|
10187
10159
|
try {
|
|
10188
10160
|
sandboxEgressTracePropagationDomains = normalizeSandboxEgressTracePropagationDomains(options?.sandbox?.egressTracePropagationDomains);
|
|
@@ -10198,7 +10170,7 @@ async function createApp(options) {
|
|
|
10198
10170
|
validatePluginRegistrations(configuredPlugins?.registrations ?? []);
|
|
10199
10171
|
validatePluginEgressCredentialHooks(configuredPlugins?.registrations ?? []);
|
|
10200
10172
|
}
|
|
10201
|
-
pluginRoutes = getPluginRoutes({
|
|
10173
|
+
pluginRoutes = getPluginRoutes({ events });
|
|
10202
10174
|
if (dashboard && !dashboard.disabled) pluginApiRoutes = getPluginApiRoutes();
|
|
10203
10175
|
} catch (error) {
|
|
10204
10176
|
restoreRuntimeConfig();
|