@sentry/junior 0.206.0 → 0.208.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-C9yuiZUF.js → agent-BfxfHsf1.js} +375 -346
- package/dist/{agent-hooks-DNjlz2A-.js → agent-hooks-CuGjLjmR.js} +224 -97
- package/dist/{agent-runner-E67lFrDG.js → agent-runner-Dz1PMKP0.js} +9 -9
- package/dist/api/schema.d.ts +39 -39
- package/dist/api/schema.js +2 -2
- package/dist/api.d.ts +1 -1
- package/dist/api.js +154 -154
- package/dist/{app-AxGmJHwK.d.ts → app-DfNrn_gS.d.ts} +2 -2
- package/dist/app.d.ts +1 -1
- package/dist/app.js +185 -183
- package/dist/{auth-pause-state-MVZ6xvEK.js → auth-pause-state-Bjoh8wCy.js} +7 -7
- package/dist/{briefs-CvJFQvhJ.js → briefs-q_onrGmf.js} +3 -3
- package/dist/{catalog-runtime-TBR8n4e1.js → catalog-runtime-D8YUMNhb.js} +1 -1
- package/dist/{checkpoint-BY47jJCB.js → checkpoint-FKMcINix.js} +8 -8
- package/dist/cli/chat.js +13 -13
- package/dist/cli/check.js +3 -3
- package/dist/cli/main.js +1 -1
- package/dist/cli/plugins.js +3 -3
- 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/{client-ceE7NtvC.js → client-bRyPqwAV.js} +4 -4
- package/dist/{config-CIE83sR2.js → config-EtF2uEaW.js} +6 -6
- package/dist/{context-B45_WqVX.js → context-Bplcgypl.js} +13 -13
- package/dist/{conversation-privacy-Auyq26XQ.js → conversation-privacy-whGaV2TG.js} +1 -1
- package/dist/{credential-sync-BO33aNQd.js → credential-sync-DM-EE1ey.js} +21 -21
- package/dist/{db-BeyXKJOI.d.ts → db-D74z7BZH.d.ts} +1 -1
- package/dist/{execution-stats-SiS2FLcv.js → execution-stats-C-f8zwMU.js} +77 -77
- package/dist/{executor-BfbYPW63.js → executor-BW5qp0jC.js} +43 -14
- package/dist/instrumentation.js +2 -2
- package/dist/{job-queue-CyTc24Es.js → job-queue-BIHNIEIX.js} +1 -1
- package/dist/{logging-D37bayDc.js → logging-sNS84Yuu.js} +1 -1
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +3 -3
- package/dist/{oauth-callback-server-BtfOeHTZ.js → oauth-callback-server-Co6D_yXP.js} +2 -2
- package/dist/{pending-auth-BPUzcs7n.js → pending-auth-CRJCt9Ux.js} +2 -2
- package/dist/{profile-CSixooTi.js → profile-BfLMVhWo.js} +4 -4
- package/dist/{prompt-3gqNY1Ka.js → prompt-x-0ZtTyb.js} +14 -14
- package/dist/{registry-DiR0-xRg.js → registry-Cyww_jnC.js} +2 -2
- package/dist/{resolve-Bn5q__9b.js → resolve-BApIFQXX.js} +2 -2
- package/dist/{runner-DFx3dIJH.js → runner-C9okx4GL.js} +10 -10
- package/dist/{sandbox-egress-signals-bwvjihxA.js → sandbox-egress-signals-BHfx0esj.js} +1 -1
- package/dist/{schema-8GXFfclK.js → schema-CwTeb-BF.js} +39 -39
- package/dist/{skills-BhM5qyCF.js → skills-DhL_wXEp.js} +2 -2
- package/dist/{spawn-Dk0PqdH6.js → spawn-msnzfE8_.js} +2 -2
- package/dist/{stats-DKRP4rsY.js → stats-Cq5w_Awk.js} +292 -268
- package/dist/{store-C5qxF2ac.d.ts → store-Det8MQ9g.d.ts} +1 -1
- package/dist/{task-queue-CdpTvu4R.js → task-queue-JfmWmpjS.js} +3 -3
- package/dist/{task-runner-alpjVSxQ.js → task-runner-Cm0gXoLH.js} +15 -15
- package/dist/{thread-state-CRK7aA41.js → thread-state-lw2QwQ-y.js} +5 -5
- package/dist/{turn-execution-XtyzIHog.js → turn-execution-C7K6lQp1.js} +3 -3
- package/dist/{turn-lifecycle-VbwZLfBv.js → turn-lifecycle-DVlJLwB0.js} +1 -1
- package/dist/{usage-D4A48Bdl.js → usage-BMuAlKIW.js} +2 -2
- package/dist/{validation-jn_46o4U.js → validation-BI-r3iQA.js} +1 -1
- package/dist/{version-DcfJxB4z.js → version-s6smK6lP.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/{web-authorization-CsfLSfEs.js → web-authorization-C9MxPvXX.js} +1 -1
- package/dist/{web-input-BPp_sc7t.js → web-input-CwVlaLd0.js} +7 -109
- package/dist/{work-Di-ubSGg.js → work-BK0W1QAt.js} +9 -9
- package/dist/{worker-Cu_D5Mr8.js → worker-D-WWKHQD.js} +5 -5
- package/migrations/0041_automation_outcomes_backfill.sql +27 -0
- package/migrations/meta/0041_snapshot.json +3516 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +5 -5
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import { C as resolveGatewayCredential, S as getGatewayApiKey, _ as findProviderError, b as isProviderRetryError, c as COMPACTION_SUMMARY_PREFIX, d as isCompactionSummaryText, f as renderCurrentInstruction, g as createProviderError, h as ProviderError, i as completeText, l as MODEL_HANDOFF_SUMMARY_PREFIX, m as escapeXml, n as GEN_AI_SERVER_ADDRESS, o as resolveGatewayModel, p as unwrapCurrentInstruction, r as completeObject, s as ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX, t as GEN_AI_PROVIDER_NAME, u as hasCompactedConversationContext, v as getProviderErrorAttributes, x as MISSING_GATEWAY_CREDENTIALS_ERROR } from "./client-
|
|
2
|
-
import { C as withSpan, F as isRecord$2, P as normalizeIdentityEmail, S as withLogContext, _ as setSpanAttributes, a as extractGenAiUsageSummary, b as summarizeMessageText, c as getTracePropagationHeaders, d as logInfo, f as logWarn, h as serializeGenAiAttribute, i as extractGenAiUsageAttributes, l as logError, p as normalizeGenAiFinishReason, s as getLogContextAttributesForSpan, u as logException, v as setSpanStatus, y as setTags } from "./logging-
|
|
1
|
+
import { C as resolveGatewayCredential, S as getGatewayApiKey, _ as findProviderError, b as isProviderRetryError, c as COMPACTION_SUMMARY_PREFIX, d as isCompactionSummaryText, f as renderCurrentInstruction, g as createProviderError, h as ProviderError, i as completeText, l as MODEL_HANDOFF_SUMMARY_PREFIX, m as escapeXml, n as GEN_AI_SERVER_ADDRESS, o as resolveGatewayModel, p as unwrapCurrentInstruction, r as completeObject, s as ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX, t as GEN_AI_PROVIDER_NAME, u as hasCompactedConversationContext, v as getProviderErrorAttributes, x as MISSING_GATEWAY_CREDENTIALS_ERROR } from "./client-bRyPqwAV.js";
|
|
2
|
+
import { C as withSpan, F as isRecord$2, P as normalizeIdentityEmail, S as withLogContext, _ as setSpanAttributes, a as extractGenAiUsageSummary, b as summarizeMessageText, c as getTracePropagationHeaders, d as logInfo, f as logWarn, h as serializeGenAiAttribute, i as extractGenAiUsageAttributes, l as logError, p as normalizeGenAiFinishReason, s as getLogContextAttributesForSpan, u as logException, v as setSpanStatus, y as setTags } from "./logging-sNS84Yuu.js";
|
|
3
3
|
import { _ as withActiveSpan, m as startInactiveSpan } from "./sentry-CD3SdJl-.js";
|
|
4
|
-
import { a as toCanonicalInputMessage, c as toGenAiPayloadMetadata, i as runWithConversationPrivacy, l as toGenAiPayloadTraceAttributes, o as toCanonicalOutputMessage, r as resolveConversationPrivacy, s as toGenAiMessagesTraceAttributes, t as canExposeConversationPayload } from "./conversation-privacy-
|
|
5
|
-
import { h as normalizeSlackEmojiName, n as botConfig } from "./config-
|
|
4
|
+
import { a as toCanonicalInputMessage, c as toGenAiPayloadMetadata, i as runWithConversationPrivacy, l as toGenAiPayloadTraceAttributes, o as toCanonicalOutputMessage, r as resolveConversationPrivacy, s as toGenAiMessagesTraceAttributes, t as canExposeConversationPayload } from "./conversation-privacy-whGaV2TG.js";
|
|
5
|
+
import { h as normalizeSlackEmojiName, n as botConfig } from "./config-EtF2uEaW.js";
|
|
6
6
|
import { a as modelIdForProfile, c as TURN_REASONING_LEVELS, i as formatModelProfiles, o as modelProfileSchema, r as formatModelProfile, s as profileConfig, t as ModelProfileNotConfiguredError } from "./model-profile-CZmVoGdX.js";
|
|
7
|
-
import { _ as parseSlackMessageTs, g as parseSlackUserId, h as parseSlackTeamId, i as credentialUserSubjectId, l as isUserActor, m as parseSlackChannelReferenceId, n as credentialContextSchema, p as parseSlackChannelId, t as credentialContextForActor } from "./context-
|
|
8
|
-
import { A as tryWorkspaceRepoCheckoutPath, B as downloadPrivateSlackFile, C as invalidateReadySnapshot, D as SANDBOX_RUNTIME, E as setWorkspaceSnapshotBuild,
|
|
7
|
+
import { _ as parseSlackMessageTs, g as parseSlackUserId, h as parseSlackTeamId, i as credentialUserSubjectId, l as isUserActor, m as parseSlackChannelReferenceId, n as credentialContextSchema, p as parseSlackChannelId, t as credentialContextForActor } from "./context-Bplcgypl.js";
|
|
8
|
+
import { A as tryWorkspaceRepoCheckoutPath, B as downloadPrivateSlackFile, C as invalidateReadySnapshot, D as SANDBOX_RUNTIME, E as setWorkspaceSnapshotBuild, Gt as getDb, Ht as getConversationEventStore, J as withSlackRetries, K as normalizeSlackConversationId, Kt as getSqlExecutor, M as resolveChannelCapabilities, O as deleteWorkspaceSnapshotBuilders, Q as readActorIdentity, S as WorkspaceValidationError, T as setWorkspaceSnapshot, U as getSlackClient, Ut as getConversationMessageSearchStore, V as getFilePermalink, W as isConversationScopedChannel, Wt as getConversationStore, _ as deleteWorkspace, an as parseDestination, at as STOP_WATCHING_TOOL_NAME, b as listWorkspaces, bt as createPluginLogger, c as getPluginTools, dt as findMatchingWatches, ft as listWatches, g as createWorkspace, ht as getDashboardTaskLink, it as RESOURCE_WATCH_TOOL_SOURCE, j as workspaceRepoCheckoutPath, k as getWorkspaceSnapshotBuilder, l as getPluginUserPromptContributions, lt as createWatch, mt as getDashboardConversationLink, nt as RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS, ot as cancelSubscriptions, pt as JUNIOR_THREAD_STATE_TTL_MS, r as createPluginHookRunner, s as getPluginSystemPromptContributions, st as cancelWatch, tt as getSlackToolContext, u as getPlugins, ut as deliverWatch, v as getWorkspace, w as loadSnapshotsForProfile, x as updateWorkspace, y as getWorkspaceByName, z as SlackActionError } from "./agent-hooks-CuGjLjmR.js";
|
|
9
9
|
import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
|
|
10
|
-
import { D as juniorArtifacts, S as juniorAttachments, T as juniorIdentities, a as juniorSchedulerRuns, d as AGENT_INVOCATION_MAILBOX_STATUSES, g as juniorConversationBriefs, w as juniorConversations, y as juniorConversationAnnotations } from "./executor-
|
|
10
|
+
import { D as juniorArtifacts, S as juniorAttachments, T as juniorIdentities, a as juniorSchedulerRuns, d as AGENT_INVOCATION_MAILBOX_STATUSES, g as juniorConversationBriefs, w as juniorConversations, y as juniorConversationAnnotations } from "./executor-BW5qp0jC.js";
|
|
11
11
|
import { A as briefLinkSchema, F as KNOWN_CONVERSATION_EVENT_TYPES, G as instructionProvenanceFor, K as sameActorIdentity, M as conversationBriefSchema, U as contextProvenance, W as instructionActors, j as briefOutcomeStatusSchema } from "./conversation-AA4R1VUn.js";
|
|
12
12
|
import { a as listReferenceFiles, c as soulPathCandidates, l as worldPathCandidates } from "./package-discovery-7a9YqLhE.js";
|
|
13
|
-
import { a as resolvePluginCommandEnv, c as getOAuthProviderErrorAttributes, i as resolveAuthTokenPlaceholder, o as OAuthProviderError, s as fetchWithBoundedOAuthErrorBodies, u as CredentialUnavailableError } from "./registry-
|
|
14
|
-
import { n as pluginCatalogRuntime } from "./catalog-runtime-
|
|
15
|
-
import { a as parseSkillInvocation, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-
|
|
16
|
-
import { A as normalizeToLf$1, At as retainRuntimeTurnContext, B as openConversationProjection, C as DEFAULT_SEARCH_COMMAND_TIMEOUT_MS, Ct as hasRuntimeTurnContext, D as getRipgrepSearchLocation, Dt as isToolResultError, E as collectFiles, Et as isContinuablePiBoundary, G as recordAuthorizationRequested, H as recordAttachmentsDelivered, I as loadConnectedMcpProviders, K as recordGuardianActionReviewed, L as loadConversationProjection, M as resolveWorkspacePath, Mt as trimTrailingAssistantMessages, N as truncateText$1, Nt as TURN_CONTEXT_TAG, O as isMissingPathError, Ot as isToolResultMessage, S as resolveRepositoryInstructionsForDirectories, St as getUserMessageInstructionText, T as RIPGREP_EXCLUDED_GLOBS, Tt as isAssistantMessage, V as recordAgentsInstructionsUpdated, X as recordTurnRoute, Y as recordToolExecutionStarted, _ as AGENTS_REPLACEMENT_NOTICE, _t as historyItemFromPiMessage, b as listRepositoryDirectories, bt as getPiMessageRole, f as TurnSliceLimitExceededError, g as AGENTS_REMOVAL_NOTICE, gt as getAgentContextInputLimitTokens, h as isTurnExecutionLimitExceededError, ht as getAgentContextCompactionTriggerTokens, j as positiveInteger$1, jt as stripRuntimeTurnContext, k as missingPathSearchResult, kt as normalizeToolNameFromResult, mt as estimateTextTokens, n as loadTurnCheckpoint, p as assertTurnToolCallLimit, q as recordMcpProviderConnected, r as saveTurnCheckpoint, t as continuableMessages, v as buildAgentsInstructionsMessage, vt as piMessageFromHistoryItem, w as MAX_TEXT_CHARS, x as renderAgentsInstructions, xt as getTerminalAssistantMessages, y as findVisibleAgentsInstructions, yt as extractAssistantText, z as loadTurnRoute } from "./checkpoint-
|
|
17
|
-
import { a as hasAgentTurnUsage, c as generateShortTitle, l as resolveTaskTitle, o as ToolInputError, t as addAgentTurnUsage } from "./usage-
|
|
18
|
-
import { A as sleep$1, D as fenceLock, E as acquireActiveLock, F as sandboxSkillDir, I as sandboxSkillFile, L as sandboxSkillPathResolution, M as SANDBOX_REPOS_ROOT, N as SANDBOX_SKILLS_ROOT, P as SANDBOX_WORKSPACE_ROOT, _ as workspaceSnapshotNotReadyUserMessage, a as isAbortError, b as buildNonInteractiveShellScript, c as isSandboxCommandStreamInterruptedError, d as isSnapshottingError, f as throwSandboxOperationError, g as isWorkspaceSnapshotNotReadyError, h as getWorkspaceSnapshotNotReadyError, i as ensureDockerDaemon, j as SANDBOX_DATA_ROOT, k as withLock, l as isSandboxMissingError, m as WorkspaceSnapshotNotReadyError, n as hash, o as isAlreadyExistsError, p as wrapSandboxSetupError, r as isStale, s as isSandboxApiTransientError, t as create, u as isSandboxUnavailableError, v as getVercelSandboxCredentials, w as getStateAdapter, x as runNonInteractiveCommand, y as buildCommandScript } from "./profile-
|
|
19
|
-
import { $ as workspaceSnapshotFinishedEvent, B as
|
|
20
|
-
import { _ as isTurnInputCommitLostError, c as sanitizeAssistantText, f as CooperativeTurnYieldError, l as NO_REPLY_MARKER, n as canReusePendingAuthLink, o as decideReply, p as TurnInputCommitLostError, s as getAssistantReplyText, t as abandonReplacedPendingAuth, u as isNoReplyMarker } from "./pending-auth-
|
|
21
|
-
import { C as toolInvocationDestination, S as surfaceFromRun, _ as uploadFilesToConversation, b as assertRunConsistency, h as postSlackMessage, i as formatProviderLabel, m as getSlackMessagePermalink, o as resolveBaseUrl, p as addReactionToMessage, r as deliverOAuthAuthorization, s as startOAuthFlow, v as RetryableDeliveryError, x as isAgentRunFeatureDisabled, y as actorFromRun } from "./agent-runner-
|
|
22
|
-
import { r as
|
|
23
|
-
import { a as createSandboxSession, c as postinstall, d as getSandboxResources, i as bindSandboxFileSystem, l as setSnapshotSpanAttributes, n as isMissingError, o as stopSession, r as resolve$1, s as dependencies } from "./resolve-
|
|
13
|
+
import { a as resolvePluginCommandEnv, c as getOAuthProviderErrorAttributes, i as resolveAuthTokenPlaceholder, o as OAuthProviderError, s as fetchWithBoundedOAuthErrorBodies, u as CredentialUnavailableError } from "./registry-Cyww_jnC.js";
|
|
14
|
+
import { n as pluginCatalogRuntime } from "./catalog-runtime-D8YUMNhb.js";
|
|
15
|
+
import { a as parseSkillInvocation, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-DhL_wXEp.js";
|
|
16
|
+
import { A as normalizeToLf$1, At as retainRuntimeTurnContext, B as openConversationProjection, C as DEFAULT_SEARCH_COMMAND_TIMEOUT_MS, Ct as hasRuntimeTurnContext, D as getRipgrepSearchLocation, Dt as isToolResultError, E as collectFiles, Et as isContinuablePiBoundary, G as recordAuthorizationRequested, H as recordAttachmentsDelivered, I as loadConnectedMcpProviders, K as recordGuardianActionReviewed, L as loadConversationProjection, M as resolveWorkspacePath, Mt as trimTrailingAssistantMessages, N as truncateText$1, Nt as TURN_CONTEXT_TAG, O as isMissingPathError, Ot as isToolResultMessage, S as resolveRepositoryInstructionsForDirectories, St as getUserMessageInstructionText, T as RIPGREP_EXCLUDED_GLOBS, Tt as isAssistantMessage, V as recordAgentsInstructionsUpdated, X as recordTurnRoute, Y as recordToolExecutionStarted, _ as AGENTS_REPLACEMENT_NOTICE, _t as historyItemFromPiMessage, b as listRepositoryDirectories, bt as getPiMessageRole, f as TurnSliceLimitExceededError, g as AGENTS_REMOVAL_NOTICE, gt as getAgentContextInputLimitTokens, h as isTurnExecutionLimitExceededError, ht as getAgentContextCompactionTriggerTokens, j as positiveInteger$1, jt as stripRuntimeTurnContext, k as missingPathSearchResult, kt as normalizeToolNameFromResult, mt as estimateTextTokens, n as loadTurnCheckpoint, p as assertTurnToolCallLimit, q as recordMcpProviderConnected, r as saveTurnCheckpoint, t as continuableMessages, v as buildAgentsInstructionsMessage, vt as piMessageFromHistoryItem, w as MAX_TEXT_CHARS, x as renderAgentsInstructions, xt as getTerminalAssistantMessages, y as findVisibleAgentsInstructions, yt as extractAssistantText, z as loadTurnRoute } from "./checkpoint-FKMcINix.js";
|
|
17
|
+
import { a as hasAgentTurnUsage, c as generateShortTitle, l as resolveTaskTitle, o as ToolInputError, t as addAgentTurnUsage } from "./usage-BMuAlKIW.js";
|
|
18
|
+
import { A as sleep$1, D as fenceLock, E as acquireActiveLock, F as sandboxSkillDir, I as sandboxSkillFile, L as sandboxSkillPathResolution, M as SANDBOX_REPOS_ROOT, N as SANDBOX_SKILLS_ROOT, P as SANDBOX_WORKSPACE_ROOT, _ as workspaceSnapshotNotReadyUserMessage, a as isAbortError, b as buildNonInteractiveShellScript, c as isSandboxCommandStreamInterruptedError, d as isSnapshottingError, f as throwSandboxOperationError, g as isWorkspaceSnapshotNotReadyError, h as getWorkspaceSnapshotNotReadyError, i as ensureDockerDaemon, j as SANDBOX_DATA_ROOT, k as withLock, l as isSandboxMissingError, m as WorkspaceSnapshotNotReadyError, n as hash, o as isAlreadyExistsError, p as wrapSandboxSetupError, r as isStale, s as isSandboxApiTransientError, t as create, u as isSandboxUnavailableError, v as getVercelSandboxCredentials, w as getStateAdapter, x as runNonInteractiveCommand, y as buildCommandScript } from "./profile-BfLMVhWo.js";
|
|
19
|
+
import { $ as workspaceSnapshotFinishedEvent, B as createEventAutomation, C as SCHEDULED_AUTOMATION_SYSTEM_ACTOR, D as eventAutomationMatchesDestination, E as eventAutomationListToolResultSchema, F as effectiveTaskOutcomes, Ht as injectContinuationToolName, I as moveTaskOutcomes, L as resolveTaskOutcomes, M as requireEventAutomationSlackContext, N as requireSupportedEventAutomationTrigger, O as eventAutomationToolResult, P as writableEventAutomation, Q as WORKSPACE_SNAPSHOT_READY_EVENT, R as taskOutcomeInputSchema, T as compactEventAutomation, Ut as juniorToolOutputSchema, V as deleteEventAutomation, W as getEventAutomation, Wt as makeStructuredToolOutput, X as getEventCatalog, Y as saveEventAutomation, Z as WORKSPACE_SNAPSHOT_FAILED_EVENT, at as normalizeCatalogEventIdentifier, c as parseSlackThreadId, ct as registeredEventTypeSchema, et as workspaceSnapshotWatch, f as createScheduledAutomation, g as listScheduledAutomationsForTeam, i as readLiveAttachment, it as hasPluginEventCatalogEntries, j as registeredEventAutomationTriggerSchema, k as eventAutomationToolResultSchema, lt as registeredResourceTypeSchema, nt as eventNamespaceSchema, ot as pluginSupportsEvent, pt as appendAndEnqueueInboundMessage, q as listEventAutomationsForTeam, rt as hasEventCatalogEntries, s as storeAttachments, st as registeredEventMatchSchema, t as incrementStat, tt as eventGuidance, ut as requireSupportedEventMatch, v as readScheduledAutomation, y as saveScheduledAutomation } from "./stats-Cq5w_Awk.js";
|
|
20
|
+
import { _ as isTurnInputCommitLostError, c as sanitizeAssistantText, f as CooperativeTurnYieldError, l as NO_REPLY_MARKER, n as canReusePendingAuthLink, o as decideReply, p as TurnInputCommitLostError, s as getAssistantReplyText, t as abandonReplacedPendingAuth, u as isNoReplyMarker } from "./pending-auth-CRJCt9Ux.js";
|
|
21
|
+
import { C as toolInvocationDestination, S as surfaceFromRun, _ as uploadFilesToConversation, b as assertRunConsistency, h as postSlackMessage, i as formatProviderLabel, m as getSlackMessagePermalink, o as resolveBaseUrl, p as addReactionToMessage, r as deliverOAuthAuthorization, s as startOAuthFlow, v as RetryableDeliveryError, x as isAgentRunFeatureDisabled, y as actorFromRun } from "./agent-runner-Dz1PMKP0.js";
|
|
22
|
+
import { r as EVENT_AUTHOR_ID } from "./prompt-x-0ZtTyb.js";
|
|
23
|
+
import { a as createSandboxSession, c as postinstall, d as getSandboxResources, i as bindSandboxFileSystem, l as setSnapshotSpanAttributes, n as isMissingError, o as stopSession, r as resolve$1, s as dependencies } from "./resolve-BApIFQXX.js";
|
|
24
24
|
import { t as getVercelConversationWorkQueue } from "./vercel-queue-DIkVcfK4.js";
|
|
25
|
-
import { a as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-
|
|
25
|
+
import { a as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-BIHNIEIX.js";
|
|
26
26
|
import { n as privateTraceResultAttributes } from "./private-trace-result-BLvBjy_e.js";
|
|
27
27
|
import { streamSimple } from "@earendil-works/pi-ai/compat";
|
|
28
28
|
import { createGatewayProvider } from "@ai-sdk/gateway";
|
|
@@ -33,7 +33,7 @@ import { AsyncLocalStorage } from "node:async_hooks";
|
|
|
33
33
|
import { z } from "zod";
|
|
34
34
|
import { createHash, createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
35
35
|
import { and, desc, eq, gt, gte, inArray, isNull, lt, ne, notExists, or, sql } from "drizzle-orm";
|
|
36
|
-
import { EgressAuthRequired, EgressPolicyDenied, PluginToolInputError, actorSchema, destinationSchema, missingToolAnnotationKeys, pluginAuthorizationSchema, pluginCredentialHeaderTransformSchema, pluginCredentialResultSchema, pluginGrantSchema, pluginProviderAccountSchema, pluginStoredTokensSchema,
|
|
36
|
+
import { EgressAuthRequired, EgressPolicyDenied, PluginToolInputError, actorSchema, destinationSchema, eventSchema, missingToolAnnotationKeys, pluginAuthorizationSchema, pluginCredentialHeaderTransformSchema, pluginCredentialResultSchema, pluginGrantSchema, pluginProviderAccountSchema, pluginStoredTokensSchema, sourceSchema, stableEventMatchKey, watchResultSchema } from "@sentry/junior-plugin-api";
|
|
37
37
|
import { alias } from "drizzle-orm/pg-core";
|
|
38
38
|
import { Agent, estimateContextTokens } from "@earendil-works/pi-agent-core";
|
|
39
39
|
import fs from "node:fs/promises";
|
|
@@ -405,21 +405,22 @@ function buildTurnResult(input) {
|
|
|
405
405
|
"app.ai.generated_files": input.generatedFileCount
|
|
406
406
|
});
|
|
407
407
|
const usedPrimaryText = Boolean(rawPrimaryText);
|
|
408
|
+
const suppressedPrimaryText = Boolean(rawPrimaryText && !noReplyRequested && !primaryText);
|
|
408
409
|
let outcome;
|
|
409
410
|
if (isProviderError) outcome = "provider_error";
|
|
411
|
+
else if (suppressedPrimaryText) outcome = "execution_failure";
|
|
410
412
|
else if (primaryText || completedWithoutTerminalText) outcome = "success";
|
|
411
413
|
else outcome = "execution_failure";
|
|
412
|
-
const resolvedOutcome = Boolean(rawPrimaryText && !noReplyRequested && !primaryText) ? "execution_failure" : outcome;
|
|
413
414
|
if (shouldTrace) logInfo("agent.message.generated", {
|
|
414
415
|
"app.message.kind": "assistant_outbound",
|
|
415
416
|
"app.message.length": primaryText.length,
|
|
416
417
|
"app.message.output": summarizeMessageText(primaryText),
|
|
417
|
-
"app.ai.outcome":
|
|
418
|
+
"app.ai.outcome": outcome,
|
|
418
419
|
"app.ai.assistant_messages": assistantMessages.length,
|
|
419
420
|
...stopReason ? { "gen_ai.response.finish_reasons": [stopReason] } : void 0
|
|
420
421
|
});
|
|
421
422
|
const resolvedDiagnostics = {
|
|
422
|
-
outcome
|
|
423
|
+
outcome,
|
|
423
424
|
modelId,
|
|
424
425
|
assistantMessageCount: assistantMessages.length,
|
|
425
426
|
reasoningLevel: executionProfile.reasoningLevel,
|
|
@@ -431,7 +432,7 @@ function buildTurnResult(input) {
|
|
|
431
432
|
usage,
|
|
432
433
|
stopReason,
|
|
433
434
|
errorMessage,
|
|
434
|
-
providerError:
|
|
435
|
+
providerError: outcome === "provider_error" && errorMessage ? createProviderError(errorMessage, {
|
|
435
436
|
modelId,
|
|
436
437
|
retryable: lastAssistant !== void 0 && isAssistantMessage(lastAssistant) && isRetryableAssistantError(lastAssistant)
|
|
437
438
|
}) : void 0
|
|
@@ -1146,12 +1147,12 @@ const TOOL_POLICY_RULES = [
|
|
|
1146
1147
|
"- After changing files, name the changed paths and summarize the completed result in the final answer.",
|
|
1147
1148
|
"- If a sandbox-backed tool reports that sandbox execution is unavailable, treat that as a blocker for local file/shell inspection; do not pretend host files were inspected.",
|
|
1148
1149
|
"- For user-provided URLs, use `webFetch`; for discovery, use `webSearch` then fetch/read promising sources; for current time/date context, use `systemTime`.",
|
|
1149
|
-
"- When
|
|
1150
|
-
"- When a tool result includes a subscription, those events are already watched; do not call
|
|
1151
|
-
"- Use
|
|
1152
|
-
"- Event
|
|
1153
|
-
"- Event
|
|
1154
|
-
"- Scheduled
|
|
1150
|
+
"- When searchEventTypes is exposed, use it only when the user asks what events are supported or the required resource type or event name is unclear. It discovers options but does not watch a resource or create an automation. When explaining how results can be used, distinguish temporary current-thread watches from durable event automations.",
|
|
1151
|
+
"- When a tool result includes a subscription, those events are already watched; do not call watchEvents for them. When a tool result includes a subscribable resource with suggestedEvents, use watchEvents only for those remaining events that serve the current intent. If suggestedEvents is empty or omitted, do not invent a watch. Do not create scheduled polling tasks for events a watch can deliver. Write a concise intent summary, and tell the user when the temporary watch expires. Stop only the requested watch by id unless the user explicitly asks to stop every watch in the thread.",
|
|
1152
|
+
"- Use createEventAutomation only when the user explicitly asks for an event automation or durable whenever-this-happens-do-X automation. Ordinary watch, notify, and tell-me-when requests use watchEvents. When an event automation's resource and events are known, create it without redundant confirmation.",
|
|
1153
|
+
"- Event automations make the automation creator's connected credentials available by default when the requested work needs user-bound authorization. Do not ask for separate confirmation merely to use credentials needed for the requested work. On creation, omit credentialMode for the creator default and set system only when the creator explicitly requires it. For later changes, creator always means the automation's original createdBy actor, never the current requester. If the requester is not that creator, do not attempt to enable creator credential use or suggest that confirmation could authorize it.",
|
|
1154
|
+
"- Event automations list for the current destination, not one thread. Public automations can also be updated or deleted by automation id from another destination in the same workspace. When listing them, use createdBy to explain creator-only credential changes and warn when trigger.available is false; an unavailable automation remains stored but cannot receive events until its plugin event is enabled again.",
|
|
1155
|
+
"- Scheduled automations make the automation creator's connected credentials available by default when the requested work needs user-bound authorization. Do not ask for separate confirmation merely to use credentials needed for the requested work. On creation, omit credentialMode for the creator default and set system only when the creator explicitly requires it. For later changes, creator always means the automation's original createdBy actor, never the current requester. If the requester is not that creator, do not attempt to enable creator credential use or suggest that confirmation could authorize it.",
|
|
1155
1156
|
"- When another model profile fits the task better, call `handoff` before substantial work. Follow the profile list and selection rules in the tool description.",
|
|
1156
1157
|
"- Run `jr-rpc config get|set|unset|list` for provider defaults and `jr-rpc plugins list` for installed plugin introspection as standalone bash commands; do not chain them with `cd`, `&&`, pipes, or provider commands.",
|
|
1157
1158
|
"- If the first result is empty, stale, ambiguous, or incomplete, try a focused alternate query, path, command, or source before concluding the answer cannot be verified."
|
|
@@ -1267,14 +1268,14 @@ function formatSourceLines(source) {
|
|
|
1267
1268
|
...source.messageTs ? [`- source.message_ts: ${escapeXml(source.messageTs)}`] : [],
|
|
1268
1269
|
...source.threadTs ? [`- source.thread_ts: ${escapeXml(source.threadTs)}`] : []
|
|
1269
1270
|
];
|
|
1270
|
-
case "
|
|
1271
|
-
"- source.kind:
|
|
1271
|
+
case "event": return [
|
|
1272
|
+
"- source.kind: event",
|
|
1272
1273
|
`- source.namespace: ${escapeXml(source.namespace)}`,
|
|
1273
1274
|
`- source.identifier: ${escapeXml(source.identifier)}`,
|
|
1274
1275
|
`- source.event_type: ${escapeXml(source.eventType)}`
|
|
1275
1276
|
];
|
|
1276
|
-
case "
|
|
1277
|
-
case "
|
|
1277
|
+
case "scheduled_automation":
|
|
1278
|
+
case "event_automation":
|
|
1278
1279
|
case "plugin_dispatch":
|
|
1279
1280
|
case "agent_invocation": return [`- source.kind: ${source.kind}`];
|
|
1280
1281
|
}
|
|
@@ -2490,8 +2491,9 @@ async function prepareWorkspaceRepositories(params) {
|
|
|
2490
2491
|
const { sandbox, workspace, signal } = params;
|
|
2491
2492
|
signal?.throwIfAborted();
|
|
2492
2493
|
await params.applyNetworkPolicy(sandbox);
|
|
2493
|
-
await params.prepareRepositories?.(sandbox, workspace, signal);
|
|
2494
|
+
const finalize = await params.prepareRepositories?.(sandbox, workspace, signal);
|
|
2494
2495
|
if (params.removeCredentialRoute) await sandbox.update({ networkPolicy: "allow-all" });
|
|
2496
|
+
await finalize?.();
|
|
2495
2497
|
}
|
|
2496
2498
|
/** Return bounded setup output for a Workspace failure. */
|
|
2497
2499
|
function workspaceSetupFailureDetail(input) {
|
|
@@ -2614,7 +2616,7 @@ function emptyState(nowMs) {
|
|
|
2614
2616
|
/** True when this Source starts an automated Turn rather than a user Turn. */
|
|
2615
2617
|
function isAutomatedTurnSource(source) {
|
|
2616
2618
|
if (!source) return false;
|
|
2617
|
-
return source.kind === "
|
|
2619
|
+
return source.kind === "event" || source.kind === "event_automation" || source.kind === "scheduled_automation" || source.kind === "plugin_dispatch" || source.kind === "agent_invocation";
|
|
2618
2620
|
}
|
|
2619
2621
|
/** Plain notice when automatic updates stop until the user replies. */
|
|
2620
2622
|
function buildAutomatedTurnLimitResponse(args) {
|
|
@@ -2826,15 +2828,15 @@ function renderTaskInput(args) {
|
|
|
2826
2828
|
return lines.join("\n");
|
|
2827
2829
|
}
|
|
2828
2830
|
//#endregion
|
|
2829
|
-
//#region src/chat/
|
|
2831
|
+
//#region src/chat/events/notification.ts
|
|
2830
2832
|
/**
|
|
2831
2833
|
* Render the runtime-owned conversation message for a subscribed event.
|
|
2832
2834
|
*
|
|
2833
2835
|
* Keep this short: facts the model cannot reconstruct, plus a one-line handling
|
|
2834
2836
|
* contract. Stable delivery rules live in runtime and docs, not this prompt.
|
|
2835
2837
|
*/
|
|
2836
|
-
function
|
|
2837
|
-
const guidance =
|
|
2838
|
+
function renderEventNotificationText(subscription, event) {
|
|
2839
|
+
const guidance = eventGuidance(getEventCatalog(), event.namespace, subscription.resourceType, event.eventType);
|
|
2838
2840
|
return renderTaskInput({
|
|
2839
2841
|
about: subscription.label,
|
|
2840
2842
|
instructions: subscription.intent,
|
|
@@ -2844,21 +2846,21 @@ function renderResourceEventNotificationText(subscription, event) {
|
|
|
2844
2846
|
externalText: event.untrustedText
|
|
2845
2847
|
});
|
|
2846
2848
|
}
|
|
2847
|
-
/** Whether mailbox metadata is a plain
|
|
2848
|
-
function
|
|
2849
|
+
/** Whether mailbox metadata is a plain event wake. */
|
|
2850
|
+
function isEventMailboxMetadata(value) {
|
|
2849
2851
|
if (!value || typeof value !== "object" || Array.isArray(value)) return false;
|
|
2850
2852
|
const record = value;
|
|
2851
|
-
if (record.kind !== "
|
|
2852
|
-
const
|
|
2853
|
-
if (!
|
|
2854
|
-
const fields =
|
|
2853
|
+
if (record.kind !== "event") return false;
|
|
2854
|
+
const event = record.event;
|
|
2855
|
+
if (!event || typeof event !== "object" || Array.isArray(event)) return false;
|
|
2856
|
+
const fields = event;
|
|
2855
2857
|
return typeof fields.eventKey === "string" && typeof fields.eventType === "string" && typeof fields.namespace === "string" && typeof fields.identifier === "string" && typeof fields.subscriptionId === "string" && (fields.trustedSummary === void 0 || typeof fields.trustedSummary === "string");
|
|
2856
2858
|
}
|
|
2857
2859
|
/** Build plain conversation mailbox input for one matched watch. */
|
|
2858
|
-
function
|
|
2860
|
+
function createEventInboundMessage(input) {
|
|
2859
2861
|
const metadata = {
|
|
2860
|
-
kind: "
|
|
2861
|
-
|
|
2862
|
+
kind: "event",
|
|
2863
|
+
event: {
|
|
2862
2864
|
eventKey: input.event.eventKey,
|
|
2863
2865
|
eventType: input.event.eventType,
|
|
2864
2866
|
namespace: input.event.namespace,
|
|
@@ -2870,25 +2872,25 @@ function createResourceEventInboundMessage(input) {
|
|
|
2870
2872
|
return {
|
|
2871
2873
|
conversationId: input.subscription.conversationId,
|
|
2872
2874
|
createdAtMs: input.event.occurredAtMs,
|
|
2873
|
-
inboundMessageId: `
|
|
2875
|
+
inboundMessageId: `event:${input.subscription.id}:${input.event.eventKey}`,
|
|
2874
2876
|
delivery: "defer",
|
|
2875
|
-
source: "
|
|
2877
|
+
source: "event",
|
|
2876
2878
|
receivedAtMs: input.receivedAtMs ?? Date.now(),
|
|
2877
2879
|
input: {
|
|
2878
2880
|
text: input.text,
|
|
2879
|
-
authorId:
|
|
2881
|
+
authorId: EVENT_AUTHOR_ID,
|
|
2880
2882
|
metadata
|
|
2881
2883
|
}
|
|
2882
2884
|
};
|
|
2883
2885
|
}
|
|
2884
2886
|
/**
|
|
2885
|
-
* Enqueue a
|
|
2887
|
+
* Enqueue a event as normal conversation mailbox input.
|
|
2886
2888
|
*
|
|
2887
2889
|
* The watch only names the conversation. Destination stays on the conversation
|
|
2888
2890
|
* and is applied when the worker runs. After too many automated Turns with no
|
|
2889
2891
|
* user Turn, later wakes stay quiet until a user message clears the pause.
|
|
2890
2892
|
*/
|
|
2891
|
-
async function
|
|
2893
|
+
async function enqueueEventNotification(args) {
|
|
2892
2894
|
const maxTurns = botConfig.maxConsecutiveAutomatedTurns;
|
|
2893
2895
|
const decision = await admitAutomatedTurn({
|
|
2894
2896
|
conversationId: args.subscription.conversationId,
|
|
@@ -2897,12 +2899,12 @@ async function enqueueResourceEventNotification(args) {
|
|
|
2897
2899
|
state: args.state
|
|
2898
2900
|
});
|
|
2899
2901
|
if (decision.status === "paused") {
|
|
2900
|
-
logInfo("
|
|
2902
|
+
logInfo("events.automated_turn_limit.paused", {
|
|
2901
2903
|
conversationId: args.subscription.conversationId,
|
|
2902
2904
|
"app.automated_turn_limit.consecutive": decision.consecutiveAutomatedTurns,
|
|
2903
2905
|
"app.automated_turn_limit.max": maxTurns,
|
|
2904
|
-
"app.
|
|
2905
|
-
"app.
|
|
2906
|
+
"app.event.event_type": args.event.eventType,
|
|
2907
|
+
"app.event.namespace": args.event.namespace
|
|
2906
2908
|
});
|
|
2907
2909
|
if (decision.shouldPostNotice) await postAutomatedTurnLimitNoticeForConversation({
|
|
2908
2910
|
conversationId: args.subscription.conversationId,
|
|
@@ -2911,9 +2913,9 @@ async function enqueueResourceEventNotification(args) {
|
|
|
2911
2913
|
});
|
|
2912
2914
|
return { status: "duplicate" };
|
|
2913
2915
|
}
|
|
2914
|
-
const text =
|
|
2916
|
+
const text = renderEventNotificationText(args.subscription, args.event);
|
|
2915
2917
|
return await appendAndEnqueueInboundMessage({
|
|
2916
|
-
message:
|
|
2918
|
+
message: createEventInboundMessage({
|
|
2917
2919
|
event: args.event,
|
|
2918
2920
|
subscription: {
|
|
2919
2921
|
conversationId: args.subscription.conversationId,
|
|
@@ -2926,12 +2928,12 @@ async function enqueueResourceEventNotification(args) {
|
|
|
2926
2928
|
});
|
|
2927
2929
|
}
|
|
2928
2930
|
//#endregion
|
|
2929
|
-
//#region src/chat/
|
|
2930
|
-
/** Match a normalized
|
|
2931
|
-
async function
|
|
2932
|
-
const event =
|
|
2931
|
+
//#region src/chat/events/ingest.ts
|
|
2932
|
+
/** Match a normalized event and enqueue notifications into conversations. */
|
|
2933
|
+
async function ingestEvent(input, options) {
|
|
2934
|
+
const event = eventSchema.parse(input);
|
|
2933
2935
|
const nowMs = options.nowMs ?? Date.now();
|
|
2934
|
-
const subscriptions = await
|
|
2936
|
+
const subscriptions = await findMatchingWatches({
|
|
2935
2937
|
data: event.data,
|
|
2936
2938
|
eventType: event.eventType,
|
|
2937
2939
|
nowMs,
|
|
@@ -2943,7 +2945,7 @@ async function ingestResourceEvent(input, options) {
|
|
|
2943
2945
|
const errors = [];
|
|
2944
2946
|
const waitDeadlineMs = Date.now() + 1e4;
|
|
2945
2947
|
for (const subscription of subscriptions) try {
|
|
2946
|
-
if (await
|
|
2948
|
+
if (await deliverWatch({
|
|
2947
2949
|
data: event.data,
|
|
2948
2950
|
eventType: event.eventType,
|
|
2949
2951
|
namespace: event.namespace,
|
|
@@ -2954,7 +2956,7 @@ async function ingestResourceEvent(input, options) {
|
|
|
2954
2956
|
subscription,
|
|
2955
2957
|
waitDeadlineMs,
|
|
2956
2958
|
deliver: async (current) => {
|
|
2957
|
-
return (await
|
|
2959
|
+
return (await enqueueEventNotification({
|
|
2958
2960
|
event,
|
|
2959
2961
|
queue: options.queue,
|
|
2960
2962
|
state: options.state,
|
|
@@ -2965,7 +2967,7 @@ async function ingestResourceEvent(input, options) {
|
|
|
2965
2967
|
} catch (error) {
|
|
2966
2968
|
errors.push(error);
|
|
2967
2969
|
}
|
|
2968
|
-
if (errors.length > 0) throw new AggregateError(errors, "Failed to deliver one or more
|
|
2970
|
+
if (errors.length > 0) throw new AggregateError(errors, "Failed to deliver one or more watches");
|
|
2969
2971
|
return { enqueued };
|
|
2970
2972
|
}
|
|
2971
2973
|
//#endregion
|
|
@@ -3361,7 +3363,7 @@ const SNAPSHOT_BUILD_SYSTEM_ACTOR = {
|
|
|
3361
3363
|
/** Leave time to send the next job before this request ends. */
|
|
3362
3364
|
const JOB_STOP_BUFFER_MS = 4e4;
|
|
3363
3365
|
async function publishFinishedEvent(input) {
|
|
3364
|
-
await
|
|
3366
|
+
await ingestEvent(workspaceSnapshotFinishedEvent(input), { queue: getVercelConversationWorkQueue() });
|
|
3365
3367
|
}
|
|
3366
3368
|
function createSnapshotBuildHelpers() {
|
|
3367
3369
|
const credentials = credentialContextForActor(SNAPSHOT_BUILD_SYSTEM_ACTOR);
|
|
@@ -3375,9 +3377,7 @@ function createSnapshotBuildHelpers() {
|
|
|
3375
3377
|
await sandbox.update({ networkPolicy });
|
|
3376
3378
|
return networkPolicy;
|
|
3377
3379
|
};
|
|
3378
|
-
const prepareRepositories = async (sandbox, target, signal) =>
|
|
3379
|
-
await pluginHooks.prepareWorkspace(sandbox, target.repos, signal);
|
|
3380
|
-
};
|
|
3380
|
+
const prepareRepositories = async (sandbox, target, signal) => await pluginHooks.prepareWorkspace(sandbox, target.repos, signal);
|
|
3381
3381
|
return {
|
|
3382
3382
|
applyNetworkPolicy,
|
|
3383
3383
|
prepareRepositories
|
|
@@ -3887,7 +3887,14 @@ function createSandboxRuntime(options) {
|
|
|
3887
3887
|
let networkPolicyKey;
|
|
3888
3888
|
try {
|
|
3889
3889
|
networkPolicyKey = await applyNetworkPolicy(createdSandbox);
|
|
3890
|
-
if (workspace)
|
|
3890
|
+
if (workspace) {
|
|
3891
|
+
const finalize = await options.onWorkspacePrepare?.(createdSandbox, workspace, signal);
|
|
3892
|
+
if (options.createNetworkPolicy) {
|
|
3893
|
+
await createdSandbox.update({ networkPolicy: "allow-all" });
|
|
3894
|
+
networkPolicyKey = void 0;
|
|
3895
|
+
}
|
|
3896
|
+
await finalize?.();
|
|
3897
|
+
}
|
|
3891
3898
|
await prepareSandbox(createdSandbox);
|
|
3892
3899
|
} catch (error) {
|
|
3893
3900
|
if (workspace) await stopSession(createdSandbox);
|
|
@@ -7144,7 +7151,7 @@ function createSpawnAgentTool(spawnAgent) {
|
|
|
7144
7151
|
});
|
|
7145
7152
|
}
|
|
7146
7153
|
//#endregion
|
|
7147
|
-
//#region src/chat/tools/list-
|
|
7154
|
+
//#region src/chat/tools/list-watches.ts
|
|
7148
7155
|
const listedResourceWatchSchema = z.object({
|
|
7149
7156
|
id: z.string().min(1),
|
|
7150
7157
|
label: z.string().min(1),
|
|
@@ -7158,8 +7165,8 @@ const listedResourceWatchSchema = z.object({
|
|
|
7158
7165
|
}).strict();
|
|
7159
7166
|
const resultDataSchema$2 = z.object({ subscriptions: z.array(listedResourceWatchSchema) }).strict();
|
|
7160
7167
|
const outputSchema$5 = juniorToolOutputSchema.merge(resultDataSchema$2);
|
|
7161
|
-
/** Create the tool that lists active
|
|
7162
|
-
function
|
|
7168
|
+
/** Create the tool that lists active watches for this conversation. */
|
|
7169
|
+
function createListWatchesTool(context) {
|
|
7163
7170
|
return zodTool({
|
|
7164
7171
|
annotations: {
|
|
7165
7172
|
destructiveHint: false,
|
|
@@ -7167,13 +7174,13 @@ function createListResourceEventSubscriptionsTool(context) {
|
|
|
7167
7174
|
openWorldHint: false,
|
|
7168
7175
|
readOnlyHint: true
|
|
7169
7176
|
},
|
|
7170
|
-
description: "List active
|
|
7177
|
+
description: "List active watches for the current conversation.",
|
|
7171
7178
|
exposure: "deferred",
|
|
7172
7179
|
source: RESOURCE_WATCH_TOOL_SOURCE,
|
|
7173
7180
|
inputSchema: z.object({}).strict(),
|
|
7174
7181
|
outputSchema: outputSchema$5,
|
|
7175
7182
|
async execute() {
|
|
7176
|
-
return { subscriptions: (await
|
|
7183
|
+
return { subscriptions: (await listWatches({ conversationId: context.conversationId })).map((subscription) => ({
|
|
7177
7184
|
id: subscription.id,
|
|
7178
7185
|
label: subscription.label,
|
|
7179
7186
|
identifier: subscription.identifier,
|
|
@@ -7188,7 +7195,7 @@ function createListResourceEventSubscriptionsTool(context) {
|
|
|
7188
7195
|
});
|
|
7189
7196
|
}
|
|
7190
7197
|
//#endregion
|
|
7191
|
-
//#region src/chat/tools/search-
|
|
7198
|
+
//#region src/chat/tools/search-event-types.ts
|
|
7192
7199
|
const DEFAULT_SEARCH_RESULTS = 10;
|
|
7193
7200
|
const MAX_SEARCH_RESULTS = 20;
|
|
7194
7201
|
const searchedResourceTypeSchema = z.object({
|
|
@@ -7224,8 +7231,8 @@ function searchableResourceTypes(catalog) {
|
|
|
7224
7231
|
...resourceType.matchFields ? { matchFields: resourceType.matchFields } : void 0
|
|
7225
7232
|
}))).sort((left, right) => left.namespace.localeCompare(right.namespace) || left.type.localeCompare(right.type));
|
|
7226
7233
|
}
|
|
7227
|
-
/** Create the read-only tool that searches enabled
|
|
7228
|
-
function
|
|
7234
|
+
/** Create the read-only tool that searches enabled event types. */
|
|
7235
|
+
function createSearchEventTypesTool(catalog) {
|
|
7229
7236
|
return zodTool({
|
|
7230
7237
|
annotations: {
|
|
7231
7238
|
destructiveHint: false,
|
|
@@ -7233,9 +7240,9 @@ function createSearchResourceEventTypesTool(catalog) {
|
|
|
7233
7240
|
openWorldHint: false,
|
|
7234
7241
|
readOnlyHint: true
|
|
7235
7242
|
},
|
|
7236
|
-
description: "Search the
|
|
7243
|
+
description: "Search the event types currently enabled without creating anything. Includes plugin resources and core Workspace snapshot events. Use watchEvents for temporary updates in the current conversation; use createEventAutomation for durable automation. This tool does not watch a resource, create an automation, or list concrete resources.",
|
|
7237
7244
|
inputSchema: z.object({
|
|
7238
|
-
query: z.string().nullable().describe("Optional terms matching a namespace, resource type, or event name. Empty lists all enabled
|
|
7245
|
+
query: z.string().nullable().describe("Optional terms matching a namespace, resource type, or event name. Empty lists all enabled event types.").optional(),
|
|
7239
7246
|
namespace: eventNamespaceSchema(catalog).nullable().describe("Optional enabled plugin namespace to search within.").optional(),
|
|
7240
7247
|
maxResults: z.number().int().min(1).max(MAX_SEARCH_RESULTS).nullable().describe("Maximum matching resource types to return.").optional()
|
|
7241
7248
|
}).strict(),
|
|
@@ -7264,13 +7271,13 @@ function createSearchResourceEventTypesTool(catalog) {
|
|
|
7264
7271
|
});
|
|
7265
7272
|
}
|
|
7266
7273
|
//#endregion
|
|
7267
|
-
//#region src/chat/tools/stop-
|
|
7274
|
+
//#region src/chat/tools/stop-watches.ts
|
|
7268
7275
|
const resultDataSchema$1 = z.object({
|
|
7269
7276
|
watching_status: z.literal("stopped"),
|
|
7270
7277
|
stoppedIds: z.array(z.string().min(1))
|
|
7271
7278
|
}).strict();
|
|
7272
7279
|
const outputSchema$3 = juniorToolOutputSchema.merge(resultDataSchema$1);
|
|
7273
|
-
/** Create the tool that stops
|
|
7280
|
+
/** Create the tool that stops watches for this conversation. */
|
|
7274
7281
|
function createStopWatchingResourcesTool(context) {
|
|
7275
7282
|
return zodTool({
|
|
7276
7283
|
annotations: {
|
|
@@ -7279,7 +7286,7 @@ function createStopWatchingResourcesTool(context) {
|
|
|
7279
7286
|
openWorldHint: false,
|
|
7280
7287
|
readOnlyHint: false
|
|
7281
7288
|
},
|
|
7282
|
-
description: "Stop one
|
|
7289
|
+
description: "Stop one watch in the current conversation by id. Omit id only when the user explicitly asks to stop every watch in this conversation. Infer terse stop requests from context, inspect active watches when the target is unclear, and call this tool before confirming that watching stopped.",
|
|
7283
7290
|
exposure: "deferred",
|
|
7284
7291
|
source: RESOURCE_WATCH_TOOL_SOURCE,
|
|
7285
7292
|
inputSchema: z.object({ id: z.string().min(1).nullable().optional() }).strict(),
|
|
@@ -7287,14 +7294,14 @@ function createStopWatchingResourcesTool(context) {
|
|
|
7287
7294
|
async execute({ id }) {
|
|
7288
7295
|
let stoppedIds;
|
|
7289
7296
|
if (id) {
|
|
7290
|
-
const stopped = await
|
|
7297
|
+
const stopped = await cancelWatch({
|
|
7291
7298
|
conversationId: context.conversationId,
|
|
7292
7299
|
id
|
|
7293
7300
|
});
|
|
7294
|
-
if (!stopped) throw new ToolInputError("
|
|
7301
|
+
if (!stopped) throw new ToolInputError("Watch was not found in the current conversation.");
|
|
7295
7302
|
stoppedIds = [stopped.id];
|
|
7296
7303
|
} else {
|
|
7297
|
-
const subscriptions = await
|
|
7304
|
+
const subscriptions = await listWatches({ conversationId: context.conversationId });
|
|
7298
7305
|
await cancelSubscriptions({ conversationId: context.conversationId });
|
|
7299
7306
|
stoppedIds = subscriptions.map((subscription) => subscription.id);
|
|
7300
7307
|
}
|
|
@@ -7306,7 +7313,7 @@ function createStopWatchingResourcesTool(context) {
|
|
|
7306
7313
|
});
|
|
7307
7314
|
}
|
|
7308
7315
|
//#endregion
|
|
7309
|
-
//#region src/chat/tools/watch-
|
|
7316
|
+
//#region src/chat/tools/watch-events.ts
|
|
7310
7317
|
function inputSchema(catalog) {
|
|
7311
7318
|
return z.object({
|
|
7312
7319
|
identifier: z.string().trim().min(1).describe("Plugin-defined resource identifier."),
|
|
@@ -7314,7 +7321,7 @@ function inputSchema(catalog) {
|
|
|
7314
7321
|
resourceType: registeredResourceTypeSchema(catalog).describe("Plugin-defined resource type."),
|
|
7315
7322
|
label: z.string().trim().min(1).max(500).describe("Human-readable resource label."),
|
|
7316
7323
|
events: z.array(registeredEventTypeSchema(catalog)).min(1).describe("High-signal event names to deliver to this conversation when they occur."),
|
|
7317
|
-
match:
|
|
7324
|
+
match: registeredEventMatchSchema(),
|
|
7318
7325
|
intent: z.string().trim().min(1).max(4e3).describe("Concise reason this conversation wants these events, used when an event arrives."),
|
|
7319
7326
|
ttlMs: z.coerce.number().describe("How long to keep the subscription active. Defaults to 14 days and must not exceed 30 days.").optional()
|
|
7320
7327
|
}).strict().superRefine((subscription, context) => {
|
|
@@ -7354,11 +7361,11 @@ function cleanStrings(values) {
|
|
|
7354
7361
|
function ttlMs(input) {
|
|
7355
7362
|
if (input.ttlMs === void 0) return RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS;
|
|
7356
7363
|
if (!Number.isFinite(input.ttlMs) || input.ttlMs <= 0) throw new ToolInputError("ttlMs must be a positive finite number");
|
|
7357
|
-
if (input.ttlMs > 2592e6) throw new ToolInputError("
|
|
7364
|
+
if (input.ttlMs > 2592e6) throw new ToolInputError("Watches cannot exceed 30 days");
|
|
7358
7365
|
return input.ttlMs;
|
|
7359
7366
|
}
|
|
7360
|
-
/** Create the tool that temporarily watches
|
|
7361
|
-
function
|
|
7367
|
+
/** Create the tool that temporarily watches events in one conversation. */
|
|
7368
|
+
function createWatchEventsTool(context, catalog) {
|
|
7362
7369
|
return zodTool({
|
|
7363
7370
|
annotations: {
|
|
7364
7371
|
destructiveHint: false,
|
|
@@ -7366,7 +7373,7 @@ function createWatchResourceEventsTool(context, catalog) {
|
|
|
7366
7373
|
openWorldHint: true,
|
|
7367
7374
|
readOnlyHint: false
|
|
7368
7375
|
},
|
|
7369
|
-
description: "Watch one plugin resource in the current conversation for a limited time; matching events return to this conversation as updates. Use for watch, notify, or tell-me-when requests. This does not create an event
|
|
7376
|
+
description: "Watch one plugin resource in the current conversation for a limited time; matching events return to this conversation as updates. Use for watch, notify, or tell-me-when requests. This does not create an event automation or execute a durable automation instruction. Prefer a subscribable tool result when available.",
|
|
7370
7377
|
inputSchema: inputSchema(catalog),
|
|
7371
7378
|
outputSchema: outputSchema$2,
|
|
7372
7379
|
async execute(input) {
|
|
@@ -7374,13 +7381,13 @@ function createWatchResourceEventsTool(context, catalog) {
|
|
|
7374
7381
|
for (const eventType of events) if (!pluginSupportsEvent(catalog, input.namespace, input.resourceType, eventType)) throw new ToolInputError(`Resource type "${input.namespace}:${input.resourceType}" does not support event "${eventType}".`);
|
|
7375
7382
|
const intent = input.intent.trim();
|
|
7376
7383
|
if (!intent) throw new ToolInputError("intent is required");
|
|
7377
|
-
const match =
|
|
7384
|
+
const match = requireSupportedEventMatch(catalog, {
|
|
7378
7385
|
match: input.match,
|
|
7379
7386
|
namespace: input.namespace,
|
|
7380
7387
|
resourceType: input.resourceType
|
|
7381
7388
|
});
|
|
7382
7389
|
const nowMs = Date.now();
|
|
7383
|
-
const subscription = await
|
|
7390
|
+
const subscription = await createWatch({
|
|
7384
7391
|
conversationId: context.conversationId,
|
|
7385
7392
|
events,
|
|
7386
7393
|
expiresAtMs: nowMs + ttlMs(input),
|
|
@@ -7388,7 +7395,7 @@ function createWatchResourceEventsTool(context, catalog) {
|
|
|
7388
7395
|
label: input.label.trim(),
|
|
7389
7396
|
...match ? { match } : void 0,
|
|
7390
7397
|
namespace: input.namespace.trim(),
|
|
7391
|
-
identifier:
|
|
7398
|
+
identifier: normalizeCatalogEventIdentifier(catalog, input.namespace, input.identifier),
|
|
7392
7399
|
resourceType: input.resourceType.trim()
|
|
7393
7400
|
});
|
|
7394
7401
|
return {
|
|
@@ -7410,24 +7417,24 @@ function createWatchResourceEventsTool(context, catalog) {
|
|
|
7410
7417
|
});
|
|
7411
7418
|
}
|
|
7412
7419
|
//#endregion
|
|
7413
|
-
//#region src/chat/tools/
|
|
7420
|
+
//#region src/chat/tools/events.ts
|
|
7414
7421
|
/** Build the complete resource-watch tool set for this runtime context. */
|
|
7415
|
-
function
|
|
7422
|
+
function createEventTools(context, catalog) {
|
|
7416
7423
|
return {
|
|
7417
|
-
...
|
|
7418
|
-
|
|
7419
|
-
|
|
7424
|
+
...hasEventCatalogEntries(catalog) ? {
|
|
7425
|
+
searchEventTypes: createSearchEventTypesTool(catalog),
|
|
7426
|
+
watchEvents: createWatchEventsTool(context, catalog)
|
|
7420
7427
|
} : void 0,
|
|
7421
|
-
|
|
7428
|
+
listWatches: createListWatchesTool(context),
|
|
7422
7429
|
stopWatchingResources: createStopWatchingResourcesTool(context)
|
|
7423
7430
|
};
|
|
7424
7431
|
}
|
|
7425
7432
|
//#endregion
|
|
7426
|
-
//#region src/chat/tools/create-event-
|
|
7427
|
-
/** Build a retry-stable
|
|
7428
|
-
function
|
|
7433
|
+
//#region src/chat/tools/create-event-automation.ts
|
|
7434
|
+
/** Build a retry-stable automation id scoped to actor, destination, and tool call. */
|
|
7435
|
+
function buildEventAutomationId(args) {
|
|
7429
7436
|
const toolCallId = args.toolCallId?.trim();
|
|
7430
|
-
if (!toolCallId) throw new Error("Event
|
|
7437
|
+
if (!toolCallId) throw new Error("Event automation creation requires a tool-call identity.");
|
|
7431
7438
|
return `evt_${createHash("sha256").update(JSON.stringify({
|
|
7432
7439
|
actor: args.userId,
|
|
7433
7440
|
channel: args.channelId,
|
|
@@ -7435,9 +7442,9 @@ function buildEventTaskId(args) {
|
|
|
7435
7442
|
team: args.teamId
|
|
7436
7443
|
})).digest("hex").slice(0, 32)}`;
|
|
7437
7444
|
}
|
|
7438
|
-
/** Create the core tool that stores an event
|
|
7439
|
-
function
|
|
7440
|
-
const trigger =
|
|
7445
|
+
/** Create the core tool that stores an event automation. */
|
|
7446
|
+
function createEventAutomationTool(context, catalog) {
|
|
7447
|
+
const trigger = registeredEventAutomationTriggerSchema(catalog);
|
|
7441
7448
|
return zodTool({
|
|
7442
7449
|
approvalMode: "review",
|
|
7443
7450
|
annotations: {
|
|
@@ -7447,12 +7454,12 @@ function createEventTaskTool(context, catalog) {
|
|
|
7447
7454
|
readOnlyHint: false
|
|
7448
7455
|
},
|
|
7449
7456
|
executionMode: "sequential",
|
|
7450
|
-
description: "Create a durable event
|
|
7457
|
+
description: "Create a durable event automation that executes the supplied instruction for every matching event. Use for whenever-this-happens-do-X automation; ordinary watch, notify, or tell-me-when requests use watchEvents instead. The automation may use the creator's connected credentials. Prefer a subscribable tool result when available.",
|
|
7451
7458
|
inputSchema: z.object({
|
|
7452
|
-
|
|
7459
|
+
instruction: z.string().trim().min(1).max(4e3),
|
|
7453
7460
|
title: z.string().trim().min(1).max(60).nullable().describe("Optional short display title. Omit or use null to generate one from the task instruction.").optional(),
|
|
7454
7461
|
trigger,
|
|
7455
|
-
outcomes: z.array(taskOutcomeInputSchema).max(5).describe("
|
|
7462
|
+
outcomes: z.array(taskOutcomeInputSchema).max(5).describe("Successful work is silent by default. Omit this field or use an empty list when the Automation should act through tools without a status message. Add send_message only when the user asks for a notification, post, digest, summary, or other visible result.").optional(),
|
|
7456
7463
|
credentialMode: z.enum(["creator", "system"]).nullable().describe("Use creator to make the task creator's connected credentials available, or system when the creator says not to use them. Omit or use null for the creator default.").optional()
|
|
7457
7464
|
}).strict(),
|
|
7458
7465
|
prepareArguments(args) {
|
|
@@ -7461,25 +7468,25 @@ function createEventTaskTool(context, catalog) {
|
|
|
7461
7468
|
if (prepared.credentialMode === "creator" || prepared.credentialMode === null) delete prepared.credentialMode;
|
|
7462
7469
|
return prepared;
|
|
7463
7470
|
},
|
|
7464
|
-
outputSchema:
|
|
7471
|
+
outputSchema: eventAutomationToolResultSchema,
|
|
7465
7472
|
async execute(input, options) {
|
|
7466
|
-
const { actor, destination, source } =
|
|
7467
|
-
const match =
|
|
7468
|
-
const id =
|
|
7473
|
+
const { actor, destination, source } = requireEventAutomationSlackContext(context);
|
|
7474
|
+
const match = requireSupportedEventAutomationTrigger(catalog, input.trigger);
|
|
7475
|
+
const id = buildEventAutomationId({
|
|
7469
7476
|
channelId: destination.channelId,
|
|
7470
7477
|
teamId: destination.teamId,
|
|
7471
7478
|
toolCallId: options.toolCallId,
|
|
7472
7479
|
userId: actor.userId
|
|
7473
7480
|
});
|
|
7474
7481
|
const db = getDb();
|
|
7475
|
-
const existing = await
|
|
7482
|
+
const existing = await getEventAutomation(db, id);
|
|
7476
7483
|
if (existing) {
|
|
7477
|
-
if (!
|
|
7478
|
-
if (existing.status !== "deleted") return
|
|
7484
|
+
if (!eventAutomationMatchesDestination(existing, destination) || existing.createdBy.slackUserId !== actor.userId) throw new ToolInputError("Event automation operation identity is invalid.");
|
|
7485
|
+
if (existing.status !== "deleted") return eventAutomationToolResult(existing, catalog, actor.userId);
|
|
7479
7486
|
}
|
|
7480
7487
|
const title = await resolveTaskTitle({
|
|
7481
7488
|
completeText,
|
|
7482
|
-
instruction: input.
|
|
7489
|
+
instruction: input.instruction,
|
|
7483
7490
|
title: input.title
|
|
7484
7491
|
});
|
|
7485
7492
|
const task = {
|
|
@@ -7494,25 +7501,25 @@ function createEventTaskTool(context, catalog) {
|
|
|
7494
7501
|
credentialMode: input.credentialMode ?? "creator",
|
|
7495
7502
|
destination,
|
|
7496
7503
|
outcomes: await resolveTaskOutcomes(input.outcomes, destination, actor.userId),
|
|
7497
|
-
task: { text: input.
|
|
7504
|
+
task: { text: input.instruction },
|
|
7498
7505
|
...title ? { title } : void 0,
|
|
7499
7506
|
trigger: {
|
|
7500
7507
|
namespace: input.trigger.namespace,
|
|
7501
|
-
identifier:
|
|
7508
|
+
identifier: normalizeCatalogEventIdentifier(catalog, input.trigger.namespace, input.trigger.identifier),
|
|
7502
7509
|
resourceType: input.trigger.resourceType,
|
|
7503
7510
|
label: input.trigger.label,
|
|
7504
7511
|
events: [...new Set(input.trigger.events)],
|
|
7505
7512
|
...match ? { match } : void 0
|
|
7506
7513
|
}
|
|
7507
7514
|
};
|
|
7508
|
-
return
|
|
7515
|
+
return eventAutomationToolResult(await createEventAutomation(db, task), catalog, actor.userId);
|
|
7509
7516
|
}
|
|
7510
7517
|
});
|
|
7511
7518
|
}
|
|
7512
7519
|
//#endregion
|
|
7513
|
-
//#region src/chat/tools/delete-event-
|
|
7514
|
-
/** Create the core tool that deletes an event
|
|
7515
|
-
function
|
|
7520
|
+
//#region src/chat/tools/delete-event-automation.ts
|
|
7521
|
+
/** Create the core tool that deletes an event automation. */
|
|
7522
|
+
function createDeleteEventAutomationTool(context, catalog) {
|
|
7516
7523
|
return zodTool({
|
|
7517
7524
|
approvalMode: "review",
|
|
7518
7525
|
annotations: {
|
|
@@ -7522,22 +7529,23 @@ function createDeleteEventTaskTool(context, catalog) {
|
|
|
7522
7529
|
readOnlyHint: false
|
|
7523
7530
|
},
|
|
7524
7531
|
executionMode: "sequential",
|
|
7525
|
-
description: "Delete an event
|
|
7526
|
-
inputSchema: z.object({
|
|
7527
|
-
outputSchema:
|
|
7528
|
-
async execute({
|
|
7529
|
-
const
|
|
7530
|
-
const
|
|
7531
|
-
|
|
7532
|
-
|
|
7532
|
+
description: "Delete an event automation.",
|
|
7533
|
+
inputSchema: z.object({ automationId: z.string().min(1) }).strict(),
|
|
7534
|
+
outputSchema: eventAutomationToolResultSchema,
|
|
7535
|
+
async execute({ automationId }) {
|
|
7536
|
+
const { actor } = requireEventAutomationSlackContext(context);
|
|
7537
|
+
const current = await writableEventAutomation(context, automationId);
|
|
7538
|
+
const deleted = await deleteEventAutomation(getDb(), current.id);
|
|
7539
|
+
if (!deleted) throw new ToolInputError("Event automation was not found.");
|
|
7540
|
+
return eventAutomationToolResult(deleted, catalog, actor.userId);
|
|
7533
7541
|
}
|
|
7534
7542
|
});
|
|
7535
7543
|
}
|
|
7536
7544
|
//#endregion
|
|
7537
|
-
//#region src/chat/tools/list-event-
|
|
7538
|
-
const
|
|
7539
|
-
/** Create the core tool that lists event
|
|
7540
|
-
function
|
|
7545
|
+
//#region src/chat/tools/list-event-automations.ts
|
|
7546
|
+
const MAX_LISTED_EVENT_AUTOMATIONS = 50;
|
|
7547
|
+
/** Create the core tool that lists event automations for this destination. */
|
|
7548
|
+
function createListEventAutomationsTool(context, catalog) {
|
|
7541
7549
|
return zodTool({
|
|
7542
7550
|
annotations: {
|
|
7543
7551
|
destructiveHint: false,
|
|
@@ -7545,30 +7553,30 @@ function createListEventTasksTool(context, catalog) {
|
|
|
7545
7553
|
openWorldHint: false,
|
|
7546
7554
|
readOnlyHint: true
|
|
7547
7555
|
},
|
|
7548
|
-
description: "List event
|
|
7556
|
+
description: "List event automations for the current destination. Each result says whether the requester is the creator and whether its trigger is available.",
|
|
7549
7557
|
inputSchema: z.object({}).strict(),
|
|
7550
|
-
outputSchema:
|
|
7558
|
+
outputSchema: eventAutomationListToolResultSchema,
|
|
7551
7559
|
async execute() {
|
|
7552
|
-
const { destination } =
|
|
7553
|
-
const matching = (await
|
|
7554
|
-
const
|
|
7560
|
+
const { actor, destination } = requireEventAutomationSlackContext(context);
|
|
7561
|
+
const matching = (await listEventAutomationsForTeam(getDb(), destination.teamId)).filter((task) => eventAutomationMatchesDestination(task, destination));
|
|
7562
|
+
const automations = matching.slice(0, MAX_LISTED_EVENT_AUTOMATIONS).map((task) => compactEventAutomation(task, catalog, actor.userId));
|
|
7555
7563
|
return {
|
|
7556
|
-
|
|
7557
|
-
truncated: matching.length >
|
|
7564
|
+
automations,
|
|
7565
|
+
truncated: matching.length > automations.length
|
|
7558
7566
|
};
|
|
7559
7567
|
}
|
|
7560
7568
|
});
|
|
7561
7569
|
}
|
|
7562
7570
|
//#endregion
|
|
7563
|
-
//#region src/chat/tools/update-event-
|
|
7571
|
+
//#region src/chat/tools/update-event-automation.ts
|
|
7564
7572
|
/** Return whether an edit changes the task's executable event source. */
|
|
7565
|
-
function
|
|
7573
|
+
function changesEventAutomationTrigger(current, next) {
|
|
7566
7574
|
const currentEvents = [...current.events].sort();
|
|
7567
7575
|
const nextEvents = [...next.events].sort();
|
|
7568
|
-
return current.namespace !== next.namespace || current.identifier !== next.identifier || currentEvents.length !== nextEvents.length || currentEvents.some((event, index) => event !== nextEvents[index]) ||
|
|
7576
|
+
return current.namespace !== next.namespace || current.identifier !== next.identifier || currentEvents.length !== nextEvents.length || currentEvents.some((event, index) => event !== nextEvents[index]) || stableEventMatchKey(current.match) !== stableEventMatchKey(next.match);
|
|
7569
7577
|
}
|
|
7570
|
-
/** Create the core tool that updates an event
|
|
7571
|
-
function
|
|
7578
|
+
/** Create the core tool that updates an event automation. */
|
|
7579
|
+
function createUpdateEventAutomationTool(context, catalog) {
|
|
7572
7580
|
return zodTool({
|
|
7573
7581
|
approvalMode: "review",
|
|
7574
7582
|
annotations: {
|
|
@@ -7578,44 +7586,44 @@ function createUpdateEventTaskTool(context, catalog) {
|
|
|
7578
7586
|
readOnlyHint: false
|
|
7579
7587
|
},
|
|
7580
7588
|
executionMode: "sequential",
|
|
7581
|
-
description: "Update the instruction, registered trigger, or credential use for an event
|
|
7589
|
+
description: "Update the instruction, registered trigger, or credential use for an event automation.",
|
|
7582
7590
|
inputSchema: z.object({
|
|
7583
|
-
|
|
7584
|
-
|
|
7585
|
-
trigger:
|
|
7591
|
+
automationId: z.string().min(1),
|
|
7592
|
+
instruction: z.string().trim().min(1).max(4e3).nullable().optional(),
|
|
7593
|
+
trigger: registeredEventAutomationTriggerSchema(catalog).nullable().optional(),
|
|
7586
7594
|
outcomes: z.array(taskOutcomeInputSchema).max(5).nullable().describe("Replacement messages to send after successful work. Use an empty list to send nothing. Omit or use null to leave unchanged.").optional(),
|
|
7587
7595
|
credentialMode: z.enum(["system", "creator"]).nullable().describe("Set creator to make the task's original creator credentials available, or system to disable them. Creator always means the task's createdBy actor, never the current requester. Only that original creator may enable creator mode. Omit or use null to leave unchanged.").optional()
|
|
7588
7596
|
}).strict(),
|
|
7589
7597
|
prepareArguments(args) {
|
|
7590
|
-
const { credentialMode, outcomes,
|
|
7598
|
+
const { credentialMode, outcomes, instruction, trigger, ...prepared } = args;
|
|
7591
7599
|
return {
|
|
7592
7600
|
...prepared,
|
|
7593
|
-
...
|
|
7601
|
+
...instruction != null ? { instruction } : void 0,
|
|
7594
7602
|
...trigger != null ? { trigger } : void 0,
|
|
7595
7603
|
...outcomes != null ? { outcomes } : void 0,
|
|
7596
7604
|
...credentialMode != null ? { credentialMode } : void 0
|
|
7597
7605
|
};
|
|
7598
7606
|
},
|
|
7599
|
-
outputSchema:
|
|
7607
|
+
outputSchema: eventAutomationToolResultSchema,
|
|
7600
7608
|
async execute(input) {
|
|
7601
|
-
const current = await
|
|
7602
|
-
const match = input.trigger ?
|
|
7603
|
-
const { actor } =
|
|
7609
|
+
const current = await writableEventAutomation(context, input.automationId);
|
|
7610
|
+
const match = input.trigger ? requireSupportedEventAutomationTrigger(catalog, input.trigger) : void 0;
|
|
7611
|
+
const { actor } = requireEventAutomationSlackContext(context);
|
|
7604
7612
|
const isCreator = actor.userId === current.createdBy.slackUserId;
|
|
7605
|
-
if (input.credentialMode === "creator" && !isCreator) throw new ToolInputError("Only the event
|
|
7606
|
-
if (input.outcomes != null && !isCreator) throw new ToolInputError("Only the event
|
|
7607
|
-
if (input.
|
|
7613
|
+
if (input.credentialMode === "creator" && !isCreator) throw new ToolInputError("Only the event automation creator can enable creator credential use.");
|
|
7614
|
+
if (input.outcomes != null && !isCreator) throw new ToolInputError("Only the event automation creator can change message destinations.");
|
|
7615
|
+
if (input.instruction === void 0 && input.trigger === void 0 && input.outcomes == null && input.credentialMode == null) throw new ToolInputError("Event automation update requires a change.");
|
|
7608
7616
|
const nextTrigger = input.trigger ? {
|
|
7609
7617
|
namespace: input.trigger.namespace,
|
|
7610
|
-
identifier:
|
|
7618
|
+
identifier: normalizeCatalogEventIdentifier(catalog, input.trigger.namespace, input.trigger.identifier),
|
|
7611
7619
|
resourceType: input.trigger.resourceType,
|
|
7612
7620
|
label: input.trigger.label,
|
|
7613
7621
|
events: [...new Set(input.trigger.events)],
|
|
7614
7622
|
...match ? { match } : void 0
|
|
7615
7623
|
} : current.trigger;
|
|
7616
|
-
const nextInstruction = input.
|
|
7624
|
+
const nextInstruction = input.instruction != null ? input.instruction : current.task.text;
|
|
7617
7625
|
const instructionChanged = nextInstruction !== current.task.text;
|
|
7618
|
-
const changesExecution = instructionChanged ||
|
|
7626
|
+
const changesExecution = instructionChanged || changesEventAutomationTrigger(current.trigger, nextTrigger);
|
|
7619
7627
|
const next = {
|
|
7620
7628
|
...current,
|
|
7621
7629
|
credentialMode: changesExecution && !isCreator ? "system" : input.credentialMode ?? current.credentialMode,
|
|
@@ -7632,26 +7640,26 @@ function createUpdateEventTaskTool(context, catalog) {
|
|
|
7632
7640
|
if (title) next.title = title;
|
|
7633
7641
|
else delete next.title;
|
|
7634
7642
|
}
|
|
7635
|
-
const saved = await
|
|
7636
|
-
if (!saved) throw new ToolInputError("Event
|
|
7637
|
-
return
|
|
7643
|
+
const saved = await saveEventAutomation(getDb(), next);
|
|
7644
|
+
if (!saved) throw new ToolInputError("Event automation was not found.");
|
|
7645
|
+
return eventAutomationToolResult(saved, catalog, actor.userId);
|
|
7638
7646
|
}
|
|
7639
7647
|
});
|
|
7640
7648
|
}
|
|
7641
7649
|
//#endregion
|
|
7642
|
-
//#region src/chat/tools/event-
|
|
7643
|
-
/** Build event
|
|
7644
|
-
function
|
|
7650
|
+
//#region src/chat/tools/event-automations.ts
|
|
7651
|
+
/** Build event automation tools for an interactive Slack actor. */
|
|
7652
|
+
function createEventAutomationTools(context, catalog) {
|
|
7645
7653
|
if (context.source.kind !== "slack" || context.destination.platform !== "slack" || context.actor?.platform !== "slack") return {};
|
|
7646
7654
|
return {
|
|
7647
|
-
...
|
|
7648
|
-
|
|
7649
|
-
|
|
7650
|
-
|
|
7655
|
+
...hasPluginEventCatalogEntries(catalog) ? { createEventAutomation: createEventAutomationTool(context, catalog) } : void 0,
|
|
7656
|
+
listEventAutomations: createListEventAutomationsTool(context, catalog),
|
|
7657
|
+
updateEventAutomation: createUpdateEventAutomationTool(context, catalog),
|
|
7658
|
+
deleteEventAutomation: createDeleteEventAutomationTool(context, catalog)
|
|
7651
7659
|
};
|
|
7652
7660
|
}
|
|
7653
7661
|
//#endregion
|
|
7654
|
-
//#region src/chat/scheduled-
|
|
7662
|
+
//#region src/chat/scheduled-automations/cadence.ts
|
|
7655
7663
|
const FORMATTERS = /* @__PURE__ */ new Map();
|
|
7656
7664
|
function getFormatter(timezone) {
|
|
7657
7665
|
const existing = FORMATTERS.get(timezone);
|
|
@@ -7894,7 +7902,7 @@ function getNextRunAtMs(task, scheduledForMs, afterMs = scheduledForMs) {
|
|
|
7894
7902
|
});
|
|
7895
7903
|
}
|
|
7896
7904
|
//#endregion
|
|
7897
|
-
//#region src/chat/scheduled-
|
|
7905
|
+
//#region src/chat/scheduled-automations/schedule-intent.ts
|
|
7898
7906
|
/**
|
|
7899
7907
|
* Owns model-facing schedule intent and materializes it against the trusted
|
|
7900
7908
|
* server clock into the canonical schedule persisted by core.
|
|
@@ -7941,9 +7949,9 @@ const recurringScheduleIntentSchema = z.object({
|
|
|
7941
7949
|
interval: z.number().int().positive().max(365).describe("Repeat every N frequency units. Defaults to 1.").nullable().optional(),
|
|
7942
7950
|
time: localTimeSchema.describe("Local wall-clock time for each occurrence in HH:MM format."),
|
|
7943
7951
|
weekdays: z.array(weekdaySchema).min(1).max(7).describe("Required for weekly schedules.").nullable().optional(),
|
|
7944
|
-
|
|
7952
|
+
dayOfMonth: z.number().int().min(1).max(31).describe("Required for monthly and yearly schedules.").nullable().optional(),
|
|
7945
7953
|
month: z.number().int().min(1).max(12).describe("Required for yearly schedules, where January is 1.").nullable().optional(),
|
|
7946
|
-
|
|
7954
|
+
startDate: localDateSchema.describe("Optional local calendar date that anchors the recurrence. Omit to start with the next matching occurrence.").nullable().optional(),
|
|
7947
7955
|
timezone: timezoneSchema
|
|
7948
7956
|
}).strict().superRefine((schedule, context) => {
|
|
7949
7957
|
if (schedule.frequency === "weekly" && schedule.weekdays == null) context.addIssue({
|
|
@@ -7951,10 +7959,10 @@ const recurringScheduleIntentSchema = z.object({
|
|
|
7951
7959
|
message: "Weekly schedules require weekdays.",
|
|
7952
7960
|
path: ["weekdays"]
|
|
7953
7961
|
});
|
|
7954
|
-
if ((schedule.frequency === "monthly" || schedule.frequency === "yearly") && schedule.
|
|
7962
|
+
if ((schedule.frequency === "monthly" || schedule.frequency === "yearly") && schedule.dayOfMonth == null) context.addIssue({
|
|
7955
7963
|
code: "custom",
|
|
7956
|
-
message: `${schedule.frequency} schedules require
|
|
7957
|
-
path: ["
|
|
7964
|
+
message: `${schedule.frequency} schedules require dayOfMonth.`,
|
|
7965
|
+
path: ["dayOfMonth"]
|
|
7958
7966
|
});
|
|
7959
7967
|
if (schedule.frequency === "yearly" && schedule.month == null) context.addIssue({
|
|
7960
7968
|
code: "custom",
|
|
@@ -7966,10 +7974,10 @@ const recurringScheduleIntentSchema = z.object({
|
|
|
7966
7974
|
message: "weekdays applies only to weekly schedules.",
|
|
7967
7975
|
path: ["weekdays"]
|
|
7968
7976
|
});
|
|
7969
|
-
if (schedule.frequency !== "monthly" && schedule.frequency !== "yearly" && schedule.
|
|
7977
|
+
if (schedule.frequency !== "monthly" && schedule.frequency !== "yearly" && schedule.dayOfMonth != null) context.addIssue({
|
|
7970
7978
|
code: "custom",
|
|
7971
|
-
message: "
|
|
7972
|
-
path: ["
|
|
7979
|
+
message: "dayOfMonth applies only to monthly or yearly schedules.",
|
|
7980
|
+
path: ["dayOfMonth"]
|
|
7973
7981
|
});
|
|
7974
7982
|
if (schedule.frequency !== "yearly" && schedule.month != null) context.addIssue({
|
|
7975
7983
|
code: "custom",
|
|
@@ -8036,8 +8044,8 @@ function recurringDescription(schedule, timezone) {
|
|
|
8036
8044
|
const cadence = interval === 1 ? cadenceUnit : `${interval} ${cadenceUnit}s`;
|
|
8037
8045
|
let detail = "";
|
|
8038
8046
|
if (schedule.frequency === "weekly") detail = ` on ${formatWeekdays(schedule.weekdays)}`;
|
|
8039
|
-
else if (schedule.frequency === "monthly") detail = ` on day ${schedule.
|
|
8040
|
-
else if (schedule.frequency === "yearly") detail = ` on ${String(schedule.month).padStart(2, "0")}-${String(schedule.
|
|
8047
|
+
else if (schedule.frequency === "monthly") detail = ` on day ${schedule.dayOfMonth}`;
|
|
8048
|
+
else if (schedule.frequency === "yearly") detail = ` on ${String(schedule.month).padStart(2, "0")}-${String(schedule.dayOfMonth).padStart(2, "0")}`;
|
|
8041
8049
|
return `Every ${cadence}${detail} at ${schedule.time} (${timezone})`;
|
|
8042
8050
|
}
|
|
8043
8051
|
var ScheduleIntentError = class extends Error {};
|
|
@@ -8073,13 +8081,13 @@ function compileScheduleIntent(args) {
|
|
|
8073
8081
|
const recurrence = {
|
|
8074
8082
|
frequency: args.intent.frequency,
|
|
8075
8083
|
interval: args.intent.interval ?? 1,
|
|
8076
|
-
startDate: args.intent.
|
|
8084
|
+
startDate: args.intent.startDate ?? localDateAt(args.nowMs, timezone),
|
|
8077
8085
|
time: parseLocalTime(args.intent.time),
|
|
8078
8086
|
...args.intent.frequency === "weekly" ? { weekdays: [...new Set(args.intent.weekdays.map((weekday) => WEEKDAY_INDEX[weekday]))].sort((left, right) => left - right) } : void 0,
|
|
8079
|
-
...args.intent.frequency === "monthly" || args.intent.frequency === "yearly" ? { dayOfMonth: args.intent.
|
|
8087
|
+
...args.intent.frequency === "monthly" || args.intent.frequency === "yearly" ? { dayOfMonth: args.intent.dayOfMonth ?? void 0 } : void 0,
|
|
8080
8088
|
...args.intent.frequency === "yearly" ? { month: args.intent.month ?? void 0 } : void 0
|
|
8081
8089
|
};
|
|
8082
|
-
const searchRecurrence = args.intent.
|
|
8090
|
+
const searchRecurrence = args.intent.startDate ? recurrence : {
|
|
8083
8091
|
...recurrence,
|
|
8084
8092
|
interval: 1
|
|
8085
8093
|
};
|
|
@@ -8089,7 +8097,7 @@ function compileScheduleIntent(args) {
|
|
|
8089
8097
|
timezone
|
|
8090
8098
|
});
|
|
8091
8099
|
if (nextRunAtMs === void 0) throw new ScheduleIntentError("The recurring schedule has no valid future occurrence.");
|
|
8092
|
-
const materializedRecurrence = args.intent.
|
|
8100
|
+
const materializedRecurrence = args.intent.startDate ? recurrence : {
|
|
8093
8101
|
...recurrence,
|
|
8094
8102
|
startDate: localDateAt(nextRunAtMs, timezone)
|
|
8095
8103
|
};
|
|
@@ -8104,10 +8112,10 @@ function compileScheduleIntent(args) {
|
|
|
8104
8112
|
};
|
|
8105
8113
|
}
|
|
8106
8114
|
//#endregion
|
|
8107
|
-
//#region src/chat/scheduled-
|
|
8108
|
-
/** Safe structured telemetry attributes for scheduled-
|
|
8109
|
-
/** Correlation attributes shared by scheduled-
|
|
8110
|
-
function
|
|
8115
|
+
//#region src/chat/scheduled-automations/telemetry.ts
|
|
8116
|
+
/** Safe structured telemetry attributes for scheduled-automation lifecycle events. */
|
|
8117
|
+
/** Correlation attributes shared by scheduled-automation create and run events. */
|
|
8118
|
+
function scheduledAutomationAttributes(task, extras = {}) {
|
|
8111
8119
|
return {
|
|
8112
8120
|
"app.task.id": task.id,
|
|
8113
8121
|
"app.task.type": "scheduled",
|
|
@@ -8124,9 +8132,9 @@ function scheduledTaskAttributes(task, extras = {}) {
|
|
|
8124
8132
|
...extras
|
|
8125
8133
|
};
|
|
8126
8134
|
}
|
|
8127
|
-
/** Correlation attributes for one scheduled-
|
|
8128
|
-
function
|
|
8129
|
-
return
|
|
8135
|
+
/** Correlation attributes for one scheduled-automation run transition. */
|
|
8136
|
+
function scheduledAutomationRunAttributes(task, run, extras = {}) {
|
|
8137
|
+
return scheduledAutomationAttributes(task, {
|
|
8130
8138
|
"app.task.run.id": run.id,
|
|
8131
8139
|
"app.task.run.status": run.status,
|
|
8132
8140
|
"app.task.run.scheduled_for": new Date(run.scheduledForMs).toISOString(),
|
|
@@ -8135,12 +8143,12 @@ function scheduledTaskRunAttributes(task, run, extras = {}) {
|
|
|
8135
8143
|
...extras
|
|
8136
8144
|
});
|
|
8137
8145
|
}
|
|
8138
|
-
/** Emit the lifecycle event for one skipped scheduled-
|
|
8139
|
-
function
|
|
8140
|
-
logInfo("
|
|
8146
|
+
/** Emit the lifecycle event for one skipped scheduled-automation run. */
|
|
8147
|
+
function logScheduledAutomationRunSkipped(task, run, extras = {}) {
|
|
8148
|
+
logInfo("scheduled_automation.run.skipped", scheduledAutomationRunAttributes(task, run, extras));
|
|
8141
8149
|
}
|
|
8142
8150
|
//#endregion
|
|
8143
|
-
//#region src/chat/scheduled-
|
|
8151
|
+
//#region src/chat/scheduled-automations/identity.ts
|
|
8144
8152
|
const SLACK_USER_ID_PATTERN = /^[UW][A-Z0-9]{5,}$/;
|
|
8145
8153
|
function clean(value) {
|
|
8146
8154
|
const normalized = value?.trim();
|
|
@@ -8156,9 +8164,9 @@ function cleanDisplay(value, slackUserId) {
|
|
|
8156
8164
|
return SLACK_USER_ID_PATTERN.test(normalized) ? void 0 : normalized;
|
|
8157
8165
|
}
|
|
8158
8166
|
/** Keep scheduler creator metadata from promoting Slack IDs into display names. */
|
|
8159
|
-
function
|
|
8167
|
+
function sanitizeScheduledAutomationPrincipal(principal) {
|
|
8160
8168
|
const slackUserId = cleanSlackUserId(principal.slackUserId);
|
|
8161
|
-
if (!slackUserId) throw new Error("Scheduled
|
|
8169
|
+
if (!slackUserId) throw new Error("Scheduled automation principal requires a Slack user id");
|
|
8162
8170
|
const fullName = cleanDisplay(principal.fullName, slackUserId);
|
|
8163
8171
|
const userName = cleanDisplay(principal.userName, slackUserId);
|
|
8164
8172
|
return {
|
|
@@ -8168,28 +8176,32 @@ function sanitizeScheduledTaskPrincipal(principal) {
|
|
|
8168
8176
|
};
|
|
8169
8177
|
}
|
|
8170
8178
|
//#endregion
|
|
8171
|
-
//#region src/chat/scheduled-
|
|
8179
|
+
//#region src/chat/scheduled-automations/tool-support.ts
|
|
8172
8180
|
const TASK_ID_PREFIX = "sched";
|
|
8173
8181
|
const DEFAULT_SCHEDULE_TIMEZONE = "America/Los_Angeles";
|
|
8174
8182
|
const compactTaskResultSchema = z.object({
|
|
8175
8183
|
id: z.string(),
|
|
8184
|
+
title: z.string().nullable(),
|
|
8176
8185
|
status: z.enum([
|
|
8177
8186
|
"active",
|
|
8178
8187
|
"blocked",
|
|
8179
8188
|
"completed",
|
|
8180
8189
|
"deleted"
|
|
8181
8190
|
]),
|
|
8182
|
-
|
|
8191
|
+
statusReason: z.string().nullable(),
|
|
8192
|
+
createdAt: z.string().datetime(),
|
|
8193
|
+
updatedAt: z.string().datetime(),
|
|
8194
|
+
originalRequest: z.string().nullable(),
|
|
8195
|
+
instruction: z.string(),
|
|
8183
8196
|
schedule: z.string(),
|
|
8184
8197
|
timezone: z.string(),
|
|
8185
8198
|
recurrence: z.unknown().nullable(),
|
|
8186
|
-
|
|
8199
|
+
nextRunAt: z.string().nullable(),
|
|
8187
8200
|
destination: z.object({
|
|
8188
|
-
|
|
8189
|
-
|
|
8190
|
-
channel_id: z.string().min(1)
|
|
8201
|
+
channel: z.string().min(1),
|
|
8202
|
+
thread: z.string().min(1).nullable()
|
|
8191
8203
|
}).strict(),
|
|
8192
|
-
|
|
8204
|
+
conversationAccess: z.object({
|
|
8193
8205
|
audience: z.enum([
|
|
8194
8206
|
"direct",
|
|
8195
8207
|
"group",
|
|
@@ -8197,22 +8209,29 @@ const compactTaskResultSchema = z.object({
|
|
|
8197
8209
|
]),
|
|
8198
8210
|
visibility: z.enum(["private", "public"])
|
|
8199
8211
|
}).strict(),
|
|
8200
|
-
|
|
8201
|
-
|
|
8202
|
-
|
|
8203
|
-
|
|
8204
|
-
|
|
8205
|
-
}).strict()
|
|
8206
|
-
|
|
8207
|
-
|
|
8208
|
-
|
|
8212
|
+
credentialMode: z.enum(["system", "creator"]),
|
|
8213
|
+
isCreator: z.boolean(),
|
|
8214
|
+
createdBy: z.object({
|
|
8215
|
+
name: z.string().min(1).nullable(),
|
|
8216
|
+
username: z.string().min(1).nullable()
|
|
8217
|
+
}).strict(),
|
|
8218
|
+
outcomes: z.array(z.object({
|
|
8219
|
+
action: z.literal("send_message"),
|
|
8220
|
+
destination: z.object({
|
|
8221
|
+
channel: z.string().min(1),
|
|
8222
|
+
thread: z.string().min(1).nullable()
|
|
8223
|
+
}).strict()
|
|
8224
|
+
}).strict()).max(5),
|
|
8225
|
+
dashboardUrl: z.string().url().nullable(),
|
|
8226
|
+
lastRunAt: z.string().nullable(),
|
|
8227
|
+
runNowAt: z.string().nullable()
|
|
8209
8228
|
}).strict();
|
|
8229
|
+
const scheduleAutomationToolResultSchema = juniorToolOutputSchema.extend({ automation: compactTaskResultSchema }).strict();
|
|
8210
8230
|
const scheduleListToolResultSchema = juniorToolOutputSchema.extend({
|
|
8211
|
-
|
|
8212
|
-
tasks: z.array(compactTaskResultSchema),
|
|
8231
|
+
automations: z.array(compactTaskResultSchema),
|
|
8213
8232
|
truncated: z.boolean()
|
|
8214
8233
|
}).strict();
|
|
8215
|
-
/** Normalize scheduled-
|
|
8234
|
+
/** Normalize scheduled-automation validation failures into the core tool contract. */
|
|
8216
8235
|
function throwToolInputError(error) {
|
|
8217
8236
|
throw new ToolInputError(error);
|
|
8218
8237
|
}
|
|
@@ -8241,7 +8260,7 @@ function requireActor(context, destination) {
|
|
|
8241
8260
|
if (context.actor?.platform !== "slack" || context.actor.teamId !== destination.teamId) throwToolInputError("No active Slack actor context is available.");
|
|
8242
8261
|
const userId = context.actor?.userId?.trim();
|
|
8243
8262
|
if (!userId || userId.toLowerCase() === "unknown") throwToolInputError("No active Slack actor context is available.");
|
|
8244
|
-
return
|
|
8263
|
+
return sanitizeScheduledAutomationPrincipal({
|
|
8245
8264
|
slackUserId: userId,
|
|
8246
8265
|
...context.actor?.userName ? { userName: context.actor.userName } : void 0,
|
|
8247
8266
|
...context.actor?.fullName ? { fullName: context.actor.fullName } : void 0
|
|
@@ -8273,54 +8292,65 @@ function sameDestination(task, destination) {
|
|
|
8273
8292
|
/** Look up a mutable task only after enforcing active-conversation ownership. */
|
|
8274
8293
|
async function getWritableTask(args) {
|
|
8275
8294
|
const destination = requireActiveConversation(args.context);
|
|
8276
|
-
const task = await
|
|
8277
|
-
if (!task || task.status === "deleted") throwToolInputError("Scheduled
|
|
8278
|
-
if (!sameDestination(task, destination)) throwToolInputError("Scheduled
|
|
8295
|
+
const task = await readScheduledAutomation(getDb(), args.taskId);
|
|
8296
|
+
if (!task || task.status === "deleted") throwToolInputError("Scheduled automation was not found in the active Slack conversation.");
|
|
8297
|
+
if (!sameDestination(task, destination)) throwToolInputError("Scheduled automation can only be managed from the Slack destination where it currently delivers.");
|
|
8279
8298
|
return task;
|
|
8280
8299
|
}
|
|
8281
|
-
/** Project scheduled
|
|
8282
|
-
function compactTask(task) {
|
|
8300
|
+
/** Project scheduled automation state into the stable model-facing result shape. */
|
|
8301
|
+
function compactTask(task, requesterSlackUserId) {
|
|
8283
8302
|
return compactTaskResultSchema.parse({
|
|
8284
8303
|
id: task.id,
|
|
8304
|
+
title: task.title?.trim() || null,
|
|
8285
8305
|
status: task.status,
|
|
8286
|
-
|
|
8306
|
+
statusReason: task.statusReason ?? null,
|
|
8307
|
+
createdAt: new Date(task.createdAtMs).toISOString(),
|
|
8308
|
+
updatedAt: new Date(task.updatedAtMs).toISOString(),
|
|
8309
|
+
originalRequest: task.originalRequest ?? null,
|
|
8310
|
+
instruction: task.task.text,
|
|
8287
8311
|
schedule: task.schedule.description,
|
|
8288
8312
|
timezone: task.schedule.timezone,
|
|
8289
8313
|
recurrence: task.schedule.recurrence ? {
|
|
8290
8314
|
frequency: task.schedule.recurrence.frequency,
|
|
8291
8315
|
interval: task.schedule.recurrence.interval,
|
|
8292
|
-
|
|
8316
|
+
startDate: task.schedule.recurrence.startDate,
|
|
8293
8317
|
time: task.schedule.recurrence.time,
|
|
8294
8318
|
weekdays: task.schedule.recurrence.weekdays,
|
|
8295
8319
|
month: task.schedule.recurrence.month,
|
|
8296
|
-
|
|
8320
|
+
dayOfMonth: task.schedule.recurrence.dayOfMonth
|
|
8297
8321
|
} : null,
|
|
8298
|
-
|
|
8322
|
+
nextRunAt: task.nextRunAtMs ? new Date(task.nextRunAtMs).toISOString() : null,
|
|
8299
8323
|
destination: {
|
|
8300
|
-
|
|
8301
|
-
|
|
8302
|
-
|
|
8324
|
+
channel: task.destination.channelId,
|
|
8325
|
+
thread: task.destination.threadTs ?? null
|
|
8326
|
+
},
|
|
8327
|
+
conversationAccess: task.conversationAccess,
|
|
8328
|
+
credentialMode: task.credentialMode,
|
|
8329
|
+
isCreator: task.createdBy.slackUserId === requesterSlackUserId,
|
|
8330
|
+
createdBy: {
|
|
8331
|
+
name: task.createdBy.fullName ?? null,
|
|
8332
|
+
username: task.createdBy.userName ?? null
|
|
8303
8333
|
},
|
|
8304
|
-
|
|
8305
|
-
|
|
8306
|
-
|
|
8307
|
-
|
|
8308
|
-
|
|
8309
|
-
|
|
8334
|
+
outcomes: effectiveTaskOutcomes(task.outcomes, task.destination).map((outcome) => ({
|
|
8335
|
+
action: outcome.action,
|
|
8336
|
+
destination: {
|
|
8337
|
+
channel: outcome.destination.channelId,
|
|
8338
|
+
thread: outcome.destination.threadTs ?? null
|
|
8339
|
+
}
|
|
8340
|
+
})),
|
|
8341
|
+
dashboardUrl: getDashboardTaskLink(task.id) ?? null,
|
|
8342
|
+
lastRunAt: task.lastRunAtMs ? new Date(task.lastRunAtMs).toISOString() : null,
|
|
8343
|
+
runNowAt: task.runNowAtMs ? new Date(task.runNowAtMs).toISOString() : null
|
|
8310
8344
|
});
|
|
8311
8345
|
}
|
|
8312
8346
|
/** Build the structured result shared by single-task scheduler tools. */
|
|
8313
|
-
function
|
|
8314
|
-
return {
|
|
8315
|
-
target,
|
|
8316
|
-
task
|
|
8317
|
-
};
|
|
8347
|
+
function scheduleAutomationToolResult(task, requesterSlackUserId) {
|
|
8348
|
+
return { automation: compactTask(task, requesterSlackUserId) };
|
|
8318
8349
|
}
|
|
8319
8350
|
/** Build the structured result for listing scheduler tools. */
|
|
8320
8351
|
function scheduleListToolResult(args) {
|
|
8321
8352
|
return {
|
|
8322
|
-
|
|
8323
|
-
tasks: args.tasks,
|
|
8353
|
+
automations: args.automations,
|
|
8324
8354
|
truncated: args.truncated
|
|
8325
8355
|
};
|
|
8326
8356
|
}
|
|
@@ -8346,9 +8376,9 @@ function getDefaultScheduleTimezone() {
|
|
|
8346
8376
|
return process.env.JUNIOR_TIMEZONE?.trim() || DEFAULT_SCHEDULE_TIMEZONE;
|
|
8347
8377
|
}
|
|
8348
8378
|
//#endregion
|
|
8349
|
-
//#region src/chat/scheduled-
|
|
8350
|
-
/** Create a tool that stores a scheduled
|
|
8351
|
-
function
|
|
8379
|
+
//#region src/chat/scheduled-automations/tools/create.ts
|
|
8380
|
+
/** Create a tool that stores a scheduled automation for the active Slack context. */
|
|
8381
|
+
function createSlackScheduleCreateAutomationTool(context) {
|
|
8352
8382
|
return zodTool({
|
|
8353
8383
|
approvalMode: "review",
|
|
8354
8384
|
annotations: {
|
|
@@ -8360,19 +8390,19 @@ function createSlackScheduleCreateTaskTool(context) {
|
|
|
8360
8390
|
description: "Create a one-time or recurring Junior task in the active Slack conversation when the user asks Junior to do work later or repeatedly.",
|
|
8361
8391
|
executionMode: "sequential",
|
|
8362
8392
|
inputSchema: z.object({
|
|
8363
|
-
|
|
8393
|
+
instruction: z.string().min(1).max(4e3),
|
|
8364
8394
|
title: z.string().trim().min(1).max(60).nullable().describe("Optional short display title. Omit or use null to generate one from the task instruction.").optional(),
|
|
8365
8395
|
schedule: scheduleIntentSchema.describe("When the task runs. The scheduler computes the exact next run from this intent and the server clock."),
|
|
8366
|
-
outcomes: z.array(taskOutcomeInputSchema).max(5).describe("
|
|
8367
|
-
|
|
8396
|
+
outcomes: z.array(taskOutcomeInputSchema).max(5).describe("Successful work is silent by default. Omit this field or use an empty list when the Automation should act through tools without a status message. Add send_message only when the user asks for a reminder, post, digest, summary, or other visible result.").optional(),
|
|
8397
|
+
credentialMode: z.enum(["creator", "system"]).nullable().describe("Use creator to make the task creator's connected credentials available, or system when the creator says not to use them. Omit or use null for the creator default.").optional()
|
|
8368
8398
|
}).strict(),
|
|
8369
8399
|
prepareArguments(args) {
|
|
8370
8400
|
const prepared = { ...args };
|
|
8371
8401
|
if (prepared.title == null) delete prepared.title;
|
|
8372
|
-
if (prepared.
|
|
8402
|
+
if (prepared.credentialMode === "creator" || prepared.credentialMode === null) delete prepared.credentialMode;
|
|
8373
8403
|
return prepared;
|
|
8374
8404
|
},
|
|
8375
|
-
outputSchema:
|
|
8405
|
+
outputSchema: scheduleAutomationToolResultSchema,
|
|
8376
8406
|
execute: async (input, options) => {
|
|
8377
8407
|
const destination = requireActiveConversation(context);
|
|
8378
8408
|
const actor = requireActor(context, destination);
|
|
@@ -8382,13 +8412,13 @@ function createSlackScheduleCreateTaskTool(context) {
|
|
|
8382
8412
|
toolCallId: options.toolCallId
|
|
8383
8413
|
});
|
|
8384
8414
|
const db = getDb();
|
|
8385
|
-
const existing = await
|
|
8415
|
+
const existing = await readScheduledAutomation(db, id);
|
|
8386
8416
|
if (existing) {
|
|
8387
|
-
if (!sameDestination(existing, destination) || existing.createdBy.slackUserId !== actor.slackUserId) throwToolInputError("Scheduled
|
|
8388
|
-
return
|
|
8417
|
+
if (!sameDestination(existing, destination) || existing.createdBy.slackUserId !== actor.slackUserId) throwToolInputError("Scheduled automation operation identity is invalid.");
|
|
8418
|
+
return scheduleAutomationToolResult(existing, actor.slackUserId);
|
|
8389
8419
|
}
|
|
8390
8420
|
const identity = (await context.users.resolveActor())?.identity;
|
|
8391
|
-
if (!identity || identity.provider !== "slack" || identity.providerTenantId !== destination.teamId || identity.providerSubjectId !== actor.slackUserId) throwToolInputError("Scheduled
|
|
8421
|
+
if (!identity || identity.provider !== "slack" || identity.providerTenantId !== destination.teamId || identity.providerSubjectId !== actor.slackUserId) throwToolInputError("Scheduled automation creator identity is unavailable.");
|
|
8392
8422
|
const nowMs = context.now?.() ?? Date.now();
|
|
8393
8423
|
let compiled;
|
|
8394
8424
|
try {
|
|
@@ -8404,7 +8434,7 @@ function createSlackScheduleCreateTaskTool(context) {
|
|
|
8404
8434
|
const conversationAccess = getConversationAccess(destination, context.source);
|
|
8405
8435
|
const title = await resolveTaskTitle({
|
|
8406
8436
|
completeText,
|
|
8407
|
-
instruction: input.
|
|
8437
|
+
instruction: input.instruction,
|
|
8408
8438
|
title: input.title
|
|
8409
8439
|
});
|
|
8410
8440
|
const task = {
|
|
@@ -8414,28 +8444,28 @@ function createSlackScheduleCreateTaskTool(context) {
|
|
|
8414
8444
|
createdBy: actor,
|
|
8415
8445
|
creatorIdentityId: identity.id,
|
|
8416
8446
|
conversationAccess,
|
|
8417
|
-
credentialMode: input.
|
|
8447
|
+
credentialMode: input.credentialMode ?? "creator",
|
|
8418
8448
|
destination,
|
|
8419
|
-
executionActor:
|
|
8449
|
+
executionActor: SCHEDULED_AUTOMATION_SYSTEM_ACTOR,
|
|
8420
8450
|
nextRunAtMs: compiled.nextRunAtMs,
|
|
8421
8451
|
originalRequest: context.userText,
|
|
8422
8452
|
schedule: compiled.schedule,
|
|
8423
8453
|
status: "active",
|
|
8424
8454
|
outcomes: await resolveTaskOutcomes(input.outcomes, destination, actor.slackUserId),
|
|
8425
|
-
task: { text: input.
|
|
8455
|
+
task: { text: input.instruction },
|
|
8426
8456
|
...title ? { title } : void 0
|
|
8427
8457
|
};
|
|
8428
|
-
const committed = await
|
|
8429
|
-
if (!sameDestination(committed, destination) || committed.createdBy.slackUserId !== actor.slackUserId) throwToolInputError("Scheduled
|
|
8430
|
-
logInfo("
|
|
8431
|
-
return
|
|
8458
|
+
const committed = await createScheduledAutomation(db, task);
|
|
8459
|
+
if (!sameDestination(committed, destination) || committed.createdBy.slackUserId !== actor.slackUserId) throwToolInputError("Scheduled automation operation identity is invalid.");
|
|
8460
|
+
logInfo("scheduled_automation.create.completed", scheduledAutomationAttributes(committed));
|
|
8461
|
+
return scheduleAutomationToolResult(committed, actor.slackUserId);
|
|
8432
8462
|
}
|
|
8433
8463
|
});
|
|
8434
8464
|
}
|
|
8435
8465
|
//#endregion
|
|
8436
|
-
//#region src/chat/scheduled-
|
|
8437
|
-
/** Create a tool that removes a scheduled
|
|
8438
|
-
function
|
|
8466
|
+
//#region src/chat/scheduled-automations/tools/delete.ts
|
|
8467
|
+
/** Create a tool that removes a scheduled automation from the active Slack conversation. */
|
|
8468
|
+
function createSlackScheduleDeleteAutomationTool(context) {
|
|
8439
8469
|
return zodTool({
|
|
8440
8470
|
approvalMode: "review",
|
|
8441
8471
|
annotations: {
|
|
@@ -8446,21 +8476,21 @@ function createSlackScheduleDeleteTaskTool(context) {
|
|
|
8446
8476
|
},
|
|
8447
8477
|
description: "Delete one scheduled Junior task from the active Slack conversation. Use only task IDs returned for this conversation. Do not delete schedules from threads, other channels, or another user's DM.",
|
|
8448
8478
|
executionMode: "sequential",
|
|
8449
|
-
inputSchema: z.object({
|
|
8450
|
-
outputSchema:
|
|
8451
|
-
execute: async ({
|
|
8479
|
+
inputSchema: z.object({ automationId: z.string().min(1).describe("ID of the task to delete. Must be from this active Slack conversation.") }),
|
|
8480
|
+
outputSchema: scheduleAutomationToolResultSchema,
|
|
8481
|
+
execute: async ({ automationId }) => {
|
|
8452
8482
|
const next = {
|
|
8453
8483
|
...await getWritableTask({
|
|
8454
8484
|
context,
|
|
8455
|
-
taskId:
|
|
8485
|
+
taskId: automationId
|
|
8456
8486
|
}),
|
|
8457
8487
|
updatedAtMs: Date.now(),
|
|
8458
8488
|
status: "deleted",
|
|
8459
8489
|
nextRunAtMs: void 0,
|
|
8460
8490
|
runNowAtMs: void 0
|
|
8461
8491
|
};
|
|
8462
|
-
await
|
|
8463
|
-
return
|
|
8492
|
+
await saveScheduledAutomation(getDb(), next);
|
|
8493
|
+
return scheduleAutomationToolResult(next, context.actor?.userId);
|
|
8464
8494
|
}
|
|
8465
8495
|
});
|
|
8466
8496
|
}
|
|
@@ -8499,7 +8529,7 @@ function parseRequiredSlackUserIdParam(field, value) {
|
|
|
8499
8529
|
};
|
|
8500
8530
|
}
|
|
8501
8531
|
//#endregion
|
|
8502
|
-
//#region src/chat/scheduled-
|
|
8532
|
+
//#region src/chat/scheduled-automations/tools/list.ts
|
|
8503
8533
|
function taskMatchesQuery(task, query) {
|
|
8504
8534
|
const normalized = query.toLowerCase();
|
|
8505
8535
|
return [
|
|
@@ -8510,10 +8540,10 @@ function taskMatchesQuery(task, query) {
|
|
|
8510
8540
|
task.status
|
|
8511
8541
|
].some((value) => value?.toLowerCase().includes(normalized));
|
|
8512
8542
|
}
|
|
8513
|
-
/** Create a tool that lists scheduled
|
|
8514
|
-
function
|
|
8543
|
+
/** Create a tool that lists scheduled automations for the active conversation or the requester. */
|
|
8544
|
+
function createSlackScheduleListAutomationsTool(context) {
|
|
8515
8545
|
return zodTool({
|
|
8516
|
-
description: "List scheduled
|
|
8546
|
+
description: "List scheduled automations in this Slack conversation. Each result says whether the requester is the creator. To find one of the requester's automations elsewhere in the workspace, pass channel_id and/or query.",
|
|
8517
8547
|
annotations: {
|
|
8518
8548
|
destructiveHint: false,
|
|
8519
8549
|
idempotentHint: true,
|
|
@@ -8522,7 +8552,7 @@ function createSlackScheduleListTasksTool(context) {
|
|
|
8522
8552
|
},
|
|
8523
8553
|
inputSchema: z.object({
|
|
8524
8554
|
channel_id: slackChannelIdParam("Current destination channel ID to filter by. Use the ID from a channel mention; do not guess.").nullable().optional(),
|
|
8525
|
-
query: z.string().trim().min(1).max(200).nullable().describe("Text to match in the requester's scheduled
|
|
8555
|
+
query: z.string().trim().min(1).max(200).nullable().describe("Text to match in the requester's scheduled automations.").optional()
|
|
8526
8556
|
}).strict(),
|
|
8527
8557
|
outputSchema: scheduleListToolResultSchema,
|
|
8528
8558
|
execute: async (input) => {
|
|
@@ -8536,7 +8566,7 @@ function createSlackScheduleListTasksTool(context) {
|
|
|
8536
8566
|
}
|
|
8537
8567
|
const query = input.query?.trim() || void 0;
|
|
8538
8568
|
const crossConversation = Boolean(query) || channelId !== void 0 && channelId !== destination.channelId;
|
|
8539
|
-
const matching = (await
|
|
8569
|
+
const matching = (await listScheduledAutomationsForTeam(getDb(), destination.teamId)).filter((task) => {
|
|
8540
8570
|
if (task.destination.platform !== "slack") return false;
|
|
8541
8571
|
if (task.destination.teamId !== destination.teamId) return false;
|
|
8542
8572
|
if (task.status === "completed") return false;
|
|
@@ -8546,19 +8576,18 @@ function createSlackScheduleListTasksTool(context) {
|
|
|
8546
8576
|
if (query && !taskMatchesQuery(task, query)) return false;
|
|
8547
8577
|
return true;
|
|
8548
8578
|
}).sort((left, right) => right.createdAtMs - left.createdAtMs || right.id.localeCompare(left.id));
|
|
8549
|
-
const visible = matching.slice(0, 50).map(compactTask);
|
|
8579
|
+
const visible = matching.slice(0, 50).map((task) => compactTask(task, actor.slackUserId));
|
|
8550
8580
|
return scheduleListToolResult({
|
|
8551
|
-
|
|
8552
|
-
tasks: visible,
|
|
8581
|
+
automations: visible,
|
|
8553
8582
|
truncated: matching.length > visible.length
|
|
8554
8583
|
});
|
|
8555
8584
|
}
|
|
8556
8585
|
});
|
|
8557
8586
|
}
|
|
8558
8587
|
//#endregion
|
|
8559
|
-
//#region src/chat/scheduled-
|
|
8560
|
-
/** Create a tool that marks an existing scheduled
|
|
8561
|
-
function
|
|
8588
|
+
//#region src/chat/scheduled-automations/tools/run-now.ts
|
|
8589
|
+
/** Create a tool that marks an existing scheduled automation due immediately. */
|
|
8590
|
+
function createSlackScheduleRunAutomationNowTool(context) {
|
|
8562
8591
|
return zodTool({
|
|
8563
8592
|
approvalMode: "review",
|
|
8564
8593
|
annotations: {
|
|
@@ -8567,31 +8596,31 @@ function createSlackScheduleRunTaskNowTool(context) {
|
|
|
8567
8596
|
openWorldHint: true,
|
|
8568
8597
|
readOnlyHint: false
|
|
8569
8598
|
},
|
|
8570
|
-
description: "Queue an existing active scheduled Junior task to run as soon as possible, without changing its cadence. Use when the user asks to run an existing scheduled
|
|
8599
|
+
description: "Queue an existing active scheduled Junior task to run as soon as possible, without changing its cadence. Use when the user asks to run an existing scheduled automation now. Use only task IDs returned for this conversation.",
|
|
8571
8600
|
executionMode: "sequential",
|
|
8572
|
-
inputSchema: z.object({
|
|
8573
|
-
outputSchema:
|
|
8574
|
-
execute: async ({
|
|
8601
|
+
inputSchema: z.object({ automationId: z.string().min(1).describe("ID of the active task to run now. Must be from this active Slack conversation.") }),
|
|
8602
|
+
outputSchema: scheduleAutomationToolResultSchema,
|
|
8603
|
+
execute: async ({ automationId }) => {
|
|
8575
8604
|
const lookup = await getWritableTask({
|
|
8576
8605
|
context,
|
|
8577
|
-
taskId:
|
|
8606
|
+
taskId: automationId
|
|
8578
8607
|
});
|
|
8579
|
-
if (lookup.status !== "active") throwToolInputError("Scheduled
|
|
8608
|
+
if (lookup.status !== "active") throwToolInputError("Scheduled automation must be active before it can be run now.");
|
|
8580
8609
|
const nowMs = Date.now();
|
|
8581
8610
|
const next = {
|
|
8582
8611
|
...lookup,
|
|
8583
8612
|
updatedAtMs: nowMs,
|
|
8584
8613
|
runNowAtMs: nowMs
|
|
8585
8614
|
};
|
|
8586
|
-
await
|
|
8587
|
-
return
|
|
8615
|
+
await saveScheduledAutomation(getDb(), next);
|
|
8616
|
+
return scheduleAutomationToolResult(next, context.actor?.userId);
|
|
8588
8617
|
}
|
|
8589
8618
|
});
|
|
8590
8619
|
}
|
|
8591
8620
|
//#endregion
|
|
8592
|
-
//#region src/chat/scheduled-
|
|
8593
|
-
/** Create a tool that edits a scheduled
|
|
8594
|
-
function
|
|
8621
|
+
//#region src/chat/scheduled-automations/tools/update.ts
|
|
8622
|
+
/** Create a tool that edits a scheduled automation, including moving it here. */
|
|
8623
|
+
function createSlackScheduleUpdateAutomationTool(context) {
|
|
8595
8624
|
return zodTool({
|
|
8596
8625
|
approvalMode: "review",
|
|
8597
8626
|
annotations: {
|
|
@@ -8600,37 +8629,37 @@ function createSlackScheduleUpdateTaskTool(context) {
|
|
|
8600
8629
|
openWorldHint: true,
|
|
8601
8630
|
readOnlyHint: false
|
|
8602
8631
|
},
|
|
8603
|
-
description: "Update a scheduled
|
|
8632
|
+
description: "Update a scheduled automation. To move the requester's task into this Slack conversation, set destination to \"here\".",
|
|
8604
8633
|
executionMode: "sequential",
|
|
8605
8634
|
inputSchema: z.object({
|
|
8606
|
-
|
|
8607
|
-
|
|
8635
|
+
automationId: z.string().min(1).describe("Scheduled automation ID returned by slackScheduleListAutomations."),
|
|
8636
|
+
instruction: z.string().min(1).max(4e3).optional(),
|
|
8608
8637
|
schedule: scheduleIntentSchema.describe("Complete replacement schedule. Omit to keep it unchanged.").nullable().optional(),
|
|
8609
8638
|
outcomes: z.array(taskOutcomeInputSchema).max(5).describe("Replacement messages to send after successful work. Use an empty list to send nothing. Omit to keep unchanged.").optional(),
|
|
8610
8639
|
status: z.enum(["active", "blocked"]).describe("Set active to resume a blocked task, or blocked to stop dispatch.").optional(),
|
|
8611
8640
|
destination: z.literal("here").nullable().describe("Set to \"here\" to move the creator's task into this Slack conversation. Omit to keep its destination.").optional(),
|
|
8612
|
-
|
|
8641
|
+
credentialMode: z.enum(["system", "creator"]).nullable().describe("Use creator credentials or system credentials. Only the task creator may enable creator mode. Omit to keep unchanged.").optional()
|
|
8613
8642
|
}).strict(),
|
|
8614
|
-
outputSchema:
|
|
8643
|
+
outputSchema: scheduleAutomationToolResultSchema,
|
|
8615
8644
|
execute: async (input) => {
|
|
8616
8645
|
const activeDestination = requireActiveConversation(context);
|
|
8617
8646
|
const actor = requireActor(context, activeDestination);
|
|
8618
8647
|
const db = getDb();
|
|
8619
|
-
const lookup = await
|
|
8620
|
-
if (!lookup || lookup.status === "deleted") throwToolInputError("Scheduled
|
|
8621
|
-
if (lookup.status === "completed") throwToolInputError("Completed scheduled
|
|
8622
|
-
if (lookup.destination.platform !== "slack") throwToolInputError("Scheduled
|
|
8623
|
-
if (lookup.destination.teamId !== activeDestination.teamId) throwToolInputError("Scheduled
|
|
8648
|
+
const lookup = await readScheduledAutomation(db, input.automationId);
|
|
8649
|
+
if (!lookup || lookup.status === "deleted") throwToolInputError("Scheduled automation was not found.");
|
|
8650
|
+
if (lookup.status === "completed") throwToolInputError("Completed scheduled automations cannot be updated. Create a new automation instead.");
|
|
8651
|
+
if (lookup.destination.platform !== "slack") throwToolInputError("Scheduled automation destination is invalid.");
|
|
8652
|
+
if (lookup.destination.teamId !== activeDestination.teamId) throwToolInputError("Scheduled automations can only be managed within the same Slack workspace.");
|
|
8624
8653
|
const moveHere = input.destination === "here";
|
|
8625
8654
|
const alreadyHere = sameDestination(lookup, activeDestination);
|
|
8626
8655
|
const isCreator = actor.slackUserId === lookup.createdBy.slackUserId;
|
|
8627
|
-
if (!alreadyHere && !moveHere) throwToolInputError("Scheduled
|
|
8628
|
-
if (moveHere && !isCreator) throwToolInputError("Only the scheduled
|
|
8629
|
-
if (input.
|
|
8630
|
-
if (input.outcomes !== void 0 && !isCreator) throwToolInputError("Only the scheduled
|
|
8656
|
+
if (!alreadyHere && !moveHere) throwToolInputError("Scheduled automation can only be managed from the Slack destination where it currently delivers. Set destination to \"here\" to move it into this conversation.");
|
|
8657
|
+
if (moveHere && !isCreator) throwToolInputError("Only the scheduled automation creator can move this task.");
|
|
8658
|
+
if (input.credentialMode === "creator" && !isCreator) throwToolInputError("Only the scheduled automation creator can enable creator credential use.");
|
|
8659
|
+
if (input.outcomes !== void 0 && !isCreator) throwToolInputError("Only the scheduled automation creator can change message destinations.");
|
|
8631
8660
|
const changingDestination = moveHere && !alreadyHere;
|
|
8632
8661
|
if (changingDestination) {
|
|
8633
|
-
if ((await db.select({ id: juniorSchedulerRuns.id }).from(juniorSchedulerRuns).where(and(eq(juniorSchedulerRuns.taskId, lookup.id), inArray(juniorSchedulerRuns.status, ["pending", "running"]))).limit(1)).length > 0) throwToolInputError("Scheduled
|
|
8662
|
+
if ((await db.select({ id: juniorSchedulerRuns.id }).from(juniorSchedulerRuns).where(and(eq(juniorSchedulerRuns.taskId, lookup.id), inArray(juniorSchedulerRuns.status, ["pending", "running"]))).limit(1)).length > 0) throwToolInputError("Scheduled automation cannot be moved while an occurrence is already running. Try again after the current run finishes.");
|
|
8634
8663
|
}
|
|
8635
8664
|
const nowMs = context.now?.() ?? Date.now();
|
|
8636
8665
|
let compiled;
|
|
@@ -8647,10 +8676,10 @@ function createSlackScheduleUpdateTaskTool(context) {
|
|
|
8647
8676
|
const nextRunAtMs = compiled?.nextRunAtMs ?? lookup.nextRunAtMs;
|
|
8648
8677
|
const status = normalizeStatus(input.status);
|
|
8649
8678
|
if (input.status && !status) throwToolInputError("status must be active or blocked.");
|
|
8650
|
-
if (status === "active" && !nextRunAtMs) throwToolInputError("Active scheduled
|
|
8679
|
+
if (status === "active" && !nextRunAtMs) throwToolInputError("Active scheduled automations require a schedule with a future occurrence.");
|
|
8651
8680
|
const nextStatus = status ?? lookup.status;
|
|
8652
|
-
const credentialMode = input.
|
|
8653
|
-
const nextInstruction = input.
|
|
8681
|
+
const credentialMode = input.instruction !== void 0 && input.instruction !== lookup.task.text && !isCreator ? "system" : input.credentialMode ?? lookup.credentialMode;
|
|
8682
|
+
const nextInstruction = input.instruction !== void 0 ? input.instruction : lookup.task.text;
|
|
8654
8683
|
const instructionChanged = nextInstruction !== lookup.task.text;
|
|
8655
8684
|
const nextDestination = changingDestination ? activeDestination : lookup.destination;
|
|
8656
8685
|
const next = {
|
|
@@ -8676,16 +8705,16 @@ function createSlackScheduleUpdateTaskTool(context) {
|
|
|
8676
8705
|
if (title) next.title = title;
|
|
8677
8706
|
else delete next.title;
|
|
8678
8707
|
}
|
|
8679
|
-
if (!changingDestination && !instructionChanged && !compiled && status === void 0 && (input.
|
|
8680
|
-
const committed = await
|
|
8681
|
-
if (changingDestination) logInfo("
|
|
8682
|
-
return
|
|
8708
|
+
if (!changingDestination && !instructionChanged && !compiled && status === void 0 && (input.credentialMode === void 0 || input.credentialMode === null || input.credentialMode === lookup.credentialMode) && input.outcomes === void 0 && moveHere) return scheduleAutomationToolResult(lookup, actor.slackUserId);
|
|
8709
|
+
const committed = await saveScheduledAutomation(db, next);
|
|
8710
|
+
if (changingDestination) logInfo("scheduled_automation.move.completed", scheduledAutomationAttributes(committed));
|
|
8711
|
+
return scheduleAutomationToolResult(committed, actor.slackUserId);
|
|
8683
8712
|
}
|
|
8684
8713
|
});
|
|
8685
8714
|
}
|
|
8686
8715
|
//#endregion
|
|
8687
|
-
//#region src/chat/tools/scheduled-
|
|
8688
|
-
function
|
|
8716
|
+
//#region src/chat/tools/scheduled-automations.ts
|
|
8717
|
+
function scheduledAutomationToolContext(context) {
|
|
8689
8718
|
if (context.source.kind !== "slack" || context.destination.platform !== "slack" || context.actor?.platform !== "slack" || !context.resolveActorIdentity) return;
|
|
8690
8719
|
return {
|
|
8691
8720
|
actor: context.actor,
|
|
@@ -8694,16 +8723,16 @@ function scheduledTaskToolContext(context) {
|
|
|
8694
8723
|
...context.userText ? { userText: context.userText } : void 0
|
|
8695
8724
|
};
|
|
8696
8725
|
}
|
|
8697
|
-
/** Build scheduled-
|
|
8698
|
-
function
|
|
8699
|
-
const taskContext =
|
|
8726
|
+
/** Build scheduled-automation tools for an interactive Slack actor. */
|
|
8727
|
+
function createScheduledAutomationTools(context) {
|
|
8728
|
+
const taskContext = scheduledAutomationToolContext(context);
|
|
8700
8729
|
if (!taskContext) return {};
|
|
8701
8730
|
return {
|
|
8702
|
-
|
|
8703
|
-
|
|
8704
|
-
|
|
8705
|
-
|
|
8706
|
-
|
|
8731
|
+
slackScheduleCreateAutomation: createSlackScheduleCreateAutomationTool(taskContext),
|
|
8732
|
+
slackScheduleListAutomations: createSlackScheduleListAutomationsTool(taskContext),
|
|
8733
|
+
slackScheduleUpdateAutomation: createSlackScheduleUpdateAutomationTool(taskContext),
|
|
8734
|
+
slackScheduleDeleteAutomation: createSlackScheduleDeleteAutomationTool(taskContext),
|
|
8735
|
+
slackScheduleRunAutomationNow: createSlackScheduleRunAutomationNowTool(taskContext)
|
|
8707
8736
|
};
|
|
8708
8737
|
}
|
|
8709
8738
|
//#endregion
|
|
@@ -12770,7 +12799,7 @@ async function tryRecordWorkspaceSwitchStat(workspace) {
|
|
|
12770
12799
|
}
|
|
12771
12800
|
async function stopWorkspaceSnapshotWatch(input) {
|
|
12772
12801
|
try {
|
|
12773
|
-
await
|
|
12802
|
+
await cancelWatch({
|
|
12774
12803
|
conversationId: input.conversationId,
|
|
12775
12804
|
id: input.subscriptionId
|
|
12776
12805
|
});
|
|
@@ -12967,7 +12996,7 @@ function createWorkspaceTools(context) {
|
|
|
12967
12996
|
outputSchema: juniorToolOutputSchema.extend({
|
|
12968
12997
|
workspace: workspaceSchema,
|
|
12969
12998
|
status: z.enum(["ready", "building"]),
|
|
12970
|
-
subscription:
|
|
12999
|
+
subscription: watchResultSchema.optional().describe("Present when the snapshot ready and failed events are already watched for this conversation.")
|
|
12971
13000
|
}),
|
|
12972
13001
|
async execute({ name }, options) {
|
|
12973
13002
|
const workspace = await getWorkspaceByName(getDb(), name);
|
|
@@ -12977,7 +13006,7 @@ function createWorkspaceTools(context) {
|
|
|
12977
13006
|
workspaceName: workspace.name
|
|
12978
13007
|
});
|
|
12979
13008
|
const conversationId = context.conversationId.trim();
|
|
12980
|
-
const subscription = conversationId ? await
|
|
13009
|
+
const subscription = conversationId ? await createWatch({
|
|
12981
13010
|
conversationId,
|
|
12982
13011
|
events: [WORKSPACE_SNAPSHOT_READY_EVENT, WORKSPACE_SNAPSHOT_FAILED_EVENT],
|
|
12983
13012
|
expiresAtMs: Date.now() + RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS,
|
|
@@ -13042,7 +13071,7 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
|
|
|
13042
13071
|
const slackContext = getSlackToolContext(context);
|
|
13043
13072
|
const slackLocationCapabilities = slackContext ? resolveChannelCapabilities(slackContext.locationChannelId) : void 0;
|
|
13044
13073
|
const canSendFilesToActiveConversation = Boolean(slackContext && slackLocationCapabilities?.canSendFiles);
|
|
13045
|
-
const
|
|
13074
|
+
const eventCatalog = getEventCatalog();
|
|
13046
13075
|
const tools = {
|
|
13047
13076
|
...options.includeLoadSkill === false ? void 0 : { loadSkill: createLoadSkillTool(availableSkills, { onSkillLoaded: hooks.onSkillLoaded }) },
|
|
13048
13077
|
reportProgress: createReportProgressTool(),
|
|
@@ -13056,9 +13085,9 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
|
|
|
13056
13085
|
writeFile: createWriteFileTool(),
|
|
13057
13086
|
webSearch: createWebSearchTool(botConfig.webSearchModelId, hooks.toolOverrides?.webSearch),
|
|
13058
13087
|
webFetch: createWebFetchTool(hooks, { canSendFilesToActiveConversation }),
|
|
13059
|
-
...
|
|
13060
|
-
...
|
|
13061
|
-
...
|
|
13088
|
+
...createEventTools(context, eventCatalog),
|
|
13089
|
+
...createEventAutomationTools(context, eventCatalog),
|
|
13090
|
+
...createScheduledAutomationTools(context),
|
|
13062
13091
|
...createWorkspaceTools(context)
|
|
13063
13092
|
};
|
|
13064
13093
|
tools.searchConversationEvents = createSearchConversationEventsTool(context);
|
|
@@ -15434,7 +15463,7 @@ Assess the exact action's intrinsic risk and whether the user's intent authorize
|
|
|
15434
15463
|
## Data Exfiltration
|
|
15435
15464
|
- Sending private data, organization data, secrets, or credentials to an untrusted destination is high or critical risk.
|
|
15436
15465
|
- A user-requested read, query, or upload to a trusted destination is not exfiltration by default.
|
|
15437
|
-
- The active Junior conversation is not an untrusted destination merely because it is a public or multi-user channel. For a scheduled
|
|
15466
|
+
- The active Junior conversation is not an untrusted destination merely because it is a public or multi-user channel. For a scheduled automation or event automation, a request by the current actor for scoped work that needs their connected service access authorizes routine service-native reads and posting the requested result to the task's stored Slack destination. It does not authorize exposing credential values, broad data dumps, or delivery to another destination.
|
|
15438
15467
|
- Using an existing credential to authenticate a user-requested action is not credential exfiltration by itself.
|
|
15439
15468
|
- Ordinary product context such as internal system names, repository names, or workflow descriptions is not private organization data by itself, including on an authorized public write destination.
|
|
15440
15469
|
- Deny disclosure of secrets, credentials, or private organization data to an untrusted destination even when the user requested it.
|
|
@@ -15728,9 +15757,9 @@ function resolveToolRuntimeRoute(args) {
|
|
|
15728
15757
|
actor: args.actor?.platform === "web" ? args.actor : void 0,
|
|
15729
15758
|
source: args.run.source
|
|
15730
15759
|
};
|
|
15731
|
-
case "
|
|
15732
|
-
case "
|
|
15733
|
-
case "
|
|
15760
|
+
case "event":
|
|
15761
|
+
case "scheduled_automation":
|
|
15762
|
+
case "event_automation":
|
|
15734
15763
|
case "plugin_dispatch":
|
|
15735
15764
|
case "agent_invocation": return {
|
|
15736
15765
|
destination,
|
|
@@ -17698,4 +17727,4 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
|
|
|
17698
17727
|
}
|
|
17699
17728
|
}
|
|
17700
17729
|
//#endregion
|
|
17701
|
-
export { shouldEmitDevAgentTrace as $, createAgentInvocationSchema as A, resolveSandboxEgressProviderForHost as B, renderBlockText as C, getNextRunAtMs as D,
|
|
17730
|
+
export { shouldEmitDevAgentTrace as $, createAgentInvocationSchema as A, resolveSandboxEgressProviderForHost as B, renderBlockText as C, getNextRunAtMs as D, scheduledAutomationRunAttributes as E, ingestEvent as F, consumeSandboxEgressAuthRequiredSignal as G, shouldPropagateSandboxEgressTrace as H, isEventMailboxMetadata as I, sandboxEgressSignalsResponseSchema as J, consumeSandboxEgressPermissionDeniedSignal as K, renderTaskInput as L, AgentInvocationLimitError as M, getMcpProviderErrorAttributes as N, agentBindingSchema as O, processWorkspaceSnapshotJob as P, AuthorizationFlowDisabledError as Q, maybePostAutomatedTurnLimitNotice as R, renderAttachmentText as S, logScheduledAutomationRunSkipped as T, SANDBOX_EGRESS_PROXY_PATH as U, normalizeSandboxEgressTracePropagationDomains as V, clearSandboxEgressSignals as W, buildSteeringPiMessage as X, createUserTokenStore as Y, isVisionImageMediaType as Z, readPublicArtifact as _, deleteMcpAuthSession as a, readAttachmentVisionSummary as b, deleteMcpStoredOAuthCredentials as c, putMcpStoredOAuthCredentials as d, getConfigDefaults as et, PluginCredentialFailureError as f, publicArtifactHeaders as g, resolvePluginOAuthAccount as h, finalizeMcpAuthorization as i, AgentInvocationBusyError as j, agentInvocationSchema as k, getMcpAuthSession as l, latestReportedProgress as m, createContextCompactor as n, deleteMcpAuthSessionsForUserProvider as o, authorizationId as p, parseSandboxEgressCredentialToken as q, executeCredentialedEgressRequest as r, deleteMcpServerSessionId as s, executeAgentRun as t, setConfigDefaults as tt, getMcpStoredOAuthCredentials as u, unpublishedArtifactHeaders as v, listThreadReplies as w, recordSlackAttachment as x, lookupSlackUserProfile as y, recordFinishedTurnForAutomatedLimit as z };
|