@sentry/junior 0.222.0 → 0.224.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-hooks-DkriF84n.js → agent-hooks-DNLBwmsh.js} +60 -16
- package/dist/{agent-7kKtDe-6.js → agent-runner-9g6TQk9y.js} +879 -69
- package/dist/api/schema.d.ts +160 -11
- package/dist/api/schema.js +2 -2
- package/dist/api.js +16 -16
- package/dist/{app-C0zbsnr7.d.ts → app-BbSD6EtD.d.ts} +1 -1
- package/dist/app.d.ts +1 -1
- package/dist/app.js +1598 -69
- package/dist/{briefs-CNH1ztn5.js → briefs-CjXnGKzZ.js} +6 -6
- package/dist/{catalog-runtime-XH_LFA_7.js → catalog-runtime-CVZ9TEuK.js} +1 -1
- package/dist/{checkpoint-CRod8es5.js → checkpoint-Ceck_fLO.js} +12 -9
- package/dist/cli/chat.js +14 -15
- 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.js +3 -3
- package/dist/{client-CcSboWx_.js → client-B27ufhzl.js} +56 -6
- package/dist/{config-CGYv-YPE.js → config-BAhl7-RC.js} +6 -7
- package/dist/{conversation-BBDt88di.js → conversation-1HUoZr2l.js} +33 -5
- package/dist/{conversation-privacy-BGjdDVWD.js → conversation-privacy-C-4JQzKD.js} +1 -1
- package/dist/credential-sync-BXHIxbtS.js +940 -0
- package/dist/{execution-stats-D97ePWuM.js → execution-stats-DikPOxJ1.js} +3 -3
- package/dist/{executor-BLBZ-UB8.js → executor-BG2Armby.js} +1 -1
- package/dist/{history-CaV8Ww7M.d.ts → history-yaSjGMD_.d.ts} +18 -0
- package/dist/instrumentation.js +2 -2
- package/dist/{job-queue-_8gxwDzt.js → job-queue-D4oAVrL9.js} +1 -1
- package/dist/{logging-qmsMdU0y.js → logging-iY0ssa2q.js} +1 -1
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +3 -3
- package/dist/{pending-auth-_1Ry-_Bw.js → oauth-authorization-BIijVS2N.js} +6 -48
- package/dist/{oauth-callback-server-Mjfm46A5.js → oauth-callback-server-DPkK3kzi.js} +4 -4
- package/dist/{profile-DbG8AlNd.js → profile-6KWhg_-U.js} +5 -9
- package/dist/{prompt-B8rCQI7K.js → prompt-90miy8VV.js} +1 -1
- package/dist/{registry-DGyQCsiy.js → registry-Ctgdjt-W.js} +1 -1
- package/dist/{resolve-D5SqDbCA.js → resolve-CB45ZukQ.js} +2 -2
- package/dist/{runner-BRAFjJhe.js → runner-BwKCqoF3.js} +9 -10
- package/dist/{sandbox-egress-signals-CdI1aVHT.js → sandbox-egress-signals-Cmy8ODat.js} +1 -1
- package/dist/{schema-CTOODa3L.js → schema-RpbW5Od2.js} +1 -1
- package/dist/{short-title-CB2fxEyx.js → short-title-xEkZwkcV.js} +2 -2
- package/dist/{skills-BoS3b8cg.js → skills-c2bMDVRS.js} +2 -2
- package/dist/{spawn-BXCU1udr.js → spawn-Cg6lyjE7.js} +2 -2
- package/dist/{stats-qUXQZq-w.js → stats-DP9zl3Jg.js} +19 -17
- package/dist/{task-queue-Di_q8922.js → task-queue-SL4uLfBB.js} +3 -3
- package/dist/{task-runner-4rCppQMo.js → task-runner-fntmnqzx.js} +9 -84
- package/dist/{thread-state-2Paza8DV.js → thread-state-Wfr0GdKS.js} +5 -5
- package/dist/{auth-pause-state-3OT67TA8.js → turn-failure-response-DLjSEm-R.js} +74 -84
- package/dist/{turn-lifecycle-f-y0Irpi.js → turn-lifecycle-5_jvLqRz.js} +1 -1
- package/dist/turn-session-routing-CQaysLoe.js +77 -0
- package/dist/{validation-Be9lSORU.js → validation-CnZkCN7o.js} +1 -1
- package/dist/{version-BydrOZMQ.js → version-C6lA-h_o.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/{web-authorization-BdfOkTuG.js → web-authorization-Bbx999_q.js} +1 -1
- package/dist/{web-input-DegYUyKU.js → web-input-BoE1xE-K.js} +7 -7
- package/dist/{work-Citqpnfc.js → work-4Nhz9mVv.js} +52 -10
- package/dist/{worker-Bn_HFyvM.js → worker-CdWPsNL2.js} +5 -5
- package/package.json +4 -4
- package/dist/agent-runner-D0ImXS7U.js +0 -679
- package/dist/credential-sync-BfzGNruT.js +0 -2757
- package/dist/experimental-CIXf7Uye.js +0 -46
- package/dist/turn-execution--F7a9kEU.js +0 -47
|
@@ -1,28 +1,28 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { C as
|
|
1
|
+
import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
|
|
2
|
+
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-B27ufhzl.js";
|
|
3
|
+
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-iY0ssa2q.js";
|
|
3
4
|
import { m as startInactiveSpan, v as withActiveSpan } from "./sentry-CgWZM_kV.js";
|
|
4
|
-
import { a as toCanonicalInputMessage, c as toGenAiPayloadMetadata, i as runWithConversationPrivacy, l as toGenAiPayloadTraceAttributes, o as toCanonicalOutputMessage, r as resolveConversationPrivacy, s as toGenAiMessagesTraceAttributes, t as canExposeConversationPayload } from "./conversation-privacy-
|
|
5
|
-
import { h as normalizeSlackEmojiName, n as botConfig } from "./config-
|
|
5
|
+
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-C-4JQzKD.js";
|
|
6
|
+
import { h as normalizeSlackEmojiName, n as botConfig } from "./config-BAhl7-RC.js";
|
|
6
7
|
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
8
|
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
|
|
9
|
+
import { A as tryWorkspaceRepoCheckoutPath, B as downloadPrivateSlackFile, C as invalidateReadySnapshot, D as SANDBOX_RUNTIME, E as setWorkspaceSnapshotBuild, Ft as saveObjectAnnotations, G as isDmChannel$1, Gt as getConversationMessageSearchStore, It as createPluginAnnotations, J as withSlackRetries, Jt as getSqlExecutor, K as normalizeSlackConversationId, Kt as getConversationStore, M as resolveChannelCapabilities, O as deleteWorkspaceSnapshotBuilders, Q as readActorIdentity, S as WorkspaceValidationError, T as setWorkspaceSnapshot, U as getSlackClient, V as getFilePermalink, W as isConversationScopedChannel, Wt as getConversationEventStore, _ as deleteWorkspace, at as STOP_WATCHING_TOOL_NAME, b as listWorkspaces, bt as createPluginLogger, c as getPluginTools, dt as findMatchingWatches, fn as parseDestination, 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, n as applyPluginFormatMarkdown, nn as addAgentTurnUsage, nt as RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS, on as hasAgentTurnUsage, ot as cancelSubscriptions, pt as JUNIOR_THREAD_STATE_TTL_MS, qt as getDb, 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-DNLBwmsh.js";
|
|
9
10
|
import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
|
|
10
|
-
import { D as juniorIdentities, E as juniorConversations, a as juniorSchedulerRuns, d as AGENT_INVOCATION_MAILBOX_STATUSES, g as juniorConversationBriefs, k as juniorArtifacts, w as juniorAttachments, x as juniorConversationAnnotations } from "./executor-
|
|
11
|
-
import { F as
|
|
11
|
+
import { D as juniorIdentities, E as juniorConversations, a as juniorSchedulerRuns, d as AGENT_INVOCATION_MAILBOX_STATUSES, g as juniorConversationBriefs, k as juniorArtifacts, w as juniorAttachments, x as juniorConversationAnnotations } from "./executor-BG2Armby.js";
|
|
12
|
+
import { F as briefLinkSchema, I as briefOutcomeStatusSchema, J as instructionActors, L as conversationBriefSchema, X as sameActorIdentity, Y as instructionProvenanceFor, j as messageCardRefSchema, q as contextProvenance, z as KNOWN_CONVERSATION_EVENT_TYPES } from "./conversation-1HUoZr2l.js";
|
|
12
13
|
import { a as listReferenceFiles, c as soulPathCandidates, l as worldPathCandidates } from "./package-discovery-7a9YqLhE.js";
|
|
13
|
-
import { a as resolveAuthTokenPlaceholder, c as fetchWithBoundedOAuthErrorBodies, d as CredentialUnavailableError, i as resolvePluginHeaderEnvRefs, l as getOAuthProviderErrorAttributes, o as resolvePluginCommandEnv, s as OAuthProviderError } 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 {
|
|
17
|
-
import { n as generateShortTitle, r as resolveTaskTitle, t as fallbackShortTitle } from "./short-title-
|
|
18
|
-
import { A as
|
|
19
|
-
import { $ as workspaceSnapshotFinishedEvent, B as createEventAutomation, C as SCHEDULED_AUTOMATION_SYSTEM_ACTOR, D as eventAutomationMatchesDestination, E as eventAutomationListToolResultSchema, F as effectiveTaskOutcomes, Gt as makeStructuredToolOutput, I as moveTaskOutcomes, Jt as ToolInputError, 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 injectContinuationToolName, V as deleteEventAutomation, W as getEventAutomation, Wt as juniorToolOutputSchema, 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-
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
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-D5SqDbCA.js";
|
|
14
|
+
import { a as resolveAuthTokenPlaceholder, c as fetchWithBoundedOAuthErrorBodies, d as CredentialUnavailableError, i as resolvePluginHeaderEnvRefs, l as getOAuthProviderErrorAttributes, o as resolvePluginCommandEnv, s as OAuthProviderError } from "./registry-Ctgdjt-W.js";
|
|
15
|
+
import { n as pluginCatalogRuntime } from "./catalog-runtime-CVZ9TEuK.js";
|
|
16
|
+
import { a as parseSkillInvocation, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-c2bMDVRS.js";
|
|
17
|
+
import { C as isToolResultError, D as stripRuntimeTurnContext, E as retainRuntimeTurnContext, F as loadTurnRoute, G as recordToolExecutionStarted, H as recordGuardianActionReviewed, I as openConversationProjection, K as recordTurnRoute, L as recordAgentsInstructionsUpdated, M as loadConnectedMcpProviders, N as loadConversationProjection, O as trimTrailingAssistantMessages, R as recordAttachmentsDelivered, S as isContinuablePiBoundary, T as normalizeToolNameFromResult, U as recordMcpProviderConnected, V as recordAuthorizationRequested, _ as getTerminalAssistantMessages, d as TurnSliceLimitExceededError, dt as getAgentContextCompactionTriggerTokens, f as assertTurnToolCallLimit, ft as getAgentContextInputLimitTokens, g as getPiMessageRole, h as extractAssistantText, i as abandonTurnRecord, k as TURN_CONTEXT_TAG, m as isTurnExecutionLimitExceededError, mt as piMessageFromHistoryItem, n as loadTurnCheckpoint, pt as historyItemFromPiMessage, r as saveTurnCheckpoint, t as continuableMessages, ut as estimateTextTokens, v as getUserMessageInstructionText, w as isToolResultMessage, x as isAssistantMessage, y as hasRuntimeTurnContext } from "./checkpoint-Ceck_fLO.js";
|
|
18
|
+
import { n as generateShortTitle, r as resolveTaskTitle, t as fallbackShortTitle } from "./short-title-xEkZwkcV.js";
|
|
19
|
+
import { A as SANDBOX_DATA_ROOT, D as fenceLock, E as acquireActiveLock, F as sandboxSkillFile, I as sandboxSkillPathResolution, M as SANDBOX_SKILLS_ROOT, N as SANDBOX_WORKSPACE_ROOT, O as withLock, P as sandboxSkillDir, _ as workspaceSnapshotNotReadyUserMessage, a as isAbortError, b as buildNonInteractiveShellScript, c as isSandboxCommandStreamInterruptedError, d as isSnapshottingError, f as throwSandboxOperationError, g as isWorkspaceSnapshotNotReadyError, h as getWorkspaceSnapshotNotReadyError, i as ensureDockerDaemon, j as SANDBOX_REPOS_ROOT, k as sleep$1, 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-6KWhg_-U.js";
|
|
20
|
+
import { $ as workspaceSnapshotFinishedEvent, B as createEventAutomation, C as SCHEDULED_AUTOMATION_SYSTEM_ACTOR, D as eventAutomationMatchesDestination, E as eventAutomationListToolResultSchema, F as effectiveTaskOutcomes, Gt as makeStructuredToolOutput, I as moveTaskOutcomes, Jt as ToolInputError, 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 injectContinuationToolName, V as deleteEventAutomation, W as getEventAutomation, Wt as juniorToolOutputSchema, 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, qt as buildDeterministicTurnId, 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-DP9zl3Jg.js";
|
|
21
|
+
import { a as NO_REPLY_MARKER, c as CooperativeTurnYieldError, i as sanitizeAssistantText, l as TurnInputCommitLostError, n as decideReply, o as isNoReplyMarker, p as isTurnInputCommitLostError, r as getAssistantReplyText, t as authorizationId } from "./oauth-authorization-BIijVS2N.js";
|
|
22
|
+
import { r as EVENT_AUTHOR_ID } from "./prompt-90miy8VV.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-CB45ZukQ.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-D4oAVrL9.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";
|
|
@@ -31,9 +31,9 @@ import path from "node:path";
|
|
|
31
31
|
import { isDeepStrictEqual } from "node:util";
|
|
32
32
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
33
33
|
import { z } from "zod";
|
|
34
|
-
import { createHash, createHmac, randomUUID, timingSafeEqual } from "node:crypto";
|
|
34
|
+
import { createHash, createHmac, randomBytes, 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, eventSchema, missingToolAnnotationKeys, pluginAuthorizationSchema, pluginCredentialHeaderTransformSchema, pluginCredentialResultSchema, pluginGrantSchema, pluginProviderAccountSchema, pluginStoredTokensSchema, sourceSchema, stableEventMatchKey, watchResultSchema } from "@sentry/junior-plugin-api";
|
|
36
|
+
import { EgressAuthRequired, EgressPolicyDenied, PluginToolInputError, actorSchema, destinationSchema, eventSchema, missingToolAnnotationKeys, ownedObjectAnnotationSchema, 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";
|
|
@@ -1140,6 +1140,73 @@ var AuthorizationFlowDisabledError = class extends Error {
|
|
|
1140
1140
|
}
|
|
1141
1141
|
};
|
|
1142
1142
|
//#endregion
|
|
1143
|
+
//#region src/chat/agent/types.ts
|
|
1144
|
+
/** Resume the agent turn after a transient or ambiguous delivery failure. */
|
|
1145
|
+
var RetryableDeliveryError = class extends Error {
|
|
1146
|
+
constructor(cause) {
|
|
1147
|
+
super("Assistant delivery was transient or ambiguous", { cause });
|
|
1148
|
+
this.name = "RetryableDeliveryError";
|
|
1149
|
+
}
|
|
1150
|
+
};
|
|
1151
|
+
/** Return whether one optional agent capability is disabled for this run. */
|
|
1152
|
+
function isAgentRunFeatureDisabled(disabledFeatures, feature) {
|
|
1153
|
+
return disabledFeatures?.includes(feature) ?? false;
|
|
1154
|
+
}
|
|
1155
|
+
/** Resolve the explicit actor, dispatch actor, or credential actor for this run. */
|
|
1156
|
+
function actorFromRun(run) {
|
|
1157
|
+
if (run.dispatch?.actor) return run.dispatch.actor;
|
|
1158
|
+
if (run.actor) return run.actor;
|
|
1159
|
+
if (run.credentialContext && !("type" in run.credentialContext.actor)) return run.credentialContext.actor;
|
|
1160
|
+
}
|
|
1161
|
+
/** Reject contradictory provider coordinates before the run touches state. */
|
|
1162
|
+
function assertRunConsistency(run) {
|
|
1163
|
+
const { destination, source } = run;
|
|
1164
|
+
switch (source.kind) {
|
|
1165
|
+
case "slack":
|
|
1166
|
+
if (destination.platform !== "slack") throw new TypeError("Run source and destination platforms do not match");
|
|
1167
|
+
if (source.teamId !== destination.teamId) throw new TypeError("Slack source and destination teams do not match");
|
|
1168
|
+
break;
|
|
1169
|
+
case "local":
|
|
1170
|
+
if (destination.platform !== "local") throw new TypeError("Run source and destination platforms do not match");
|
|
1171
|
+
if (source.conversationId !== destination.conversationId) throw new TypeError("Source and destination conversation IDs do not match");
|
|
1172
|
+
if (run.surface !== "internal" && destination.conversationId !== run.conversationId) throw new TypeError("Source, destination, and run conversation IDs do not match");
|
|
1173
|
+
break;
|
|
1174
|
+
case "web":
|
|
1175
|
+
if (run.surface !== "internal" && source.conversationId !== run.conversationId) throw new TypeError("Web source and run conversation IDs do not match");
|
|
1176
|
+
switch (destination.platform) {
|
|
1177
|
+
case "local":
|
|
1178
|
+
if (source.conversationId !== destination.conversationId) throw new TypeError("Source and destination conversation IDs do not match");
|
|
1179
|
+
if (run.surface !== "internal" && destination.conversationId !== run.conversationId) throw new TypeError("Source, destination, and run conversation IDs do not match");
|
|
1180
|
+
}
|
|
1181
|
+
break;
|
|
1182
|
+
case "event":
|
|
1183
|
+
case "scheduled_automation":
|
|
1184
|
+
case "event_automation":
|
|
1185
|
+
case "plugin_dispatch":
|
|
1186
|
+
case "agent_invocation": return;
|
|
1187
|
+
}
|
|
1188
|
+
const actor = run.dispatch?.actor ?? run.actor;
|
|
1189
|
+
if (!actor || actor.platform === "system") return;
|
|
1190
|
+
if (actor.platform !== source.kind) throw new TypeError(`Actor platform "${actor.platform}" does not match Source kind "${source.kind}"`);
|
|
1191
|
+
if (actor.platform === "slack" && source.kind === "slack" && actor.teamId !== source.teamId) throw new TypeError("Slack Actor team does not match Source team");
|
|
1192
|
+
}
|
|
1193
|
+
/** Route tool side effects to the tool channel when one overrides the destination. */
|
|
1194
|
+
function toolInvocationDestination(run) {
|
|
1195
|
+
if (run.destination.platform !== "slack" || !run.toolChannelId) return run.destination;
|
|
1196
|
+
return {
|
|
1197
|
+
platform: "slack",
|
|
1198
|
+
teamId: run.destination.teamId,
|
|
1199
|
+
channelId: run.toolChannelId
|
|
1200
|
+
};
|
|
1201
|
+
}
|
|
1202
|
+
/** Infer the run surface when the caller did not state one. */
|
|
1203
|
+
function surfaceFromRun(run) {
|
|
1204
|
+
if (run.surface) return run.surface;
|
|
1205
|
+
if (run.source.kind === "slack") return "slack";
|
|
1206
|
+
if (run.source.kind === "web") return "api";
|
|
1207
|
+
return "internal";
|
|
1208
|
+
}
|
|
1209
|
+
//#endregion
|
|
1143
1210
|
//#region src/chat/tool-support/action-review-history.ts
|
|
1144
1211
|
const MAX_VISIBLE_HISTORY_CHARS = 12e3;
|
|
1145
1212
|
const priorRejectionSchema = z.object({
|
|
@@ -1483,7 +1550,7 @@ const TOOL_POLICY_RULES = [
|
|
|
1483
1550
|
"- For user-provided URLs, use `webFetch`; for discovery, use `webSearch` then fetch/read promising sources; for current time/date context, use `systemTime`.",
|
|
1484
1551
|
"- 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.",
|
|
1485
1552
|
"- 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.",
|
|
1486
|
-
"-
|
|
1553
|
+
"- Cards returned by tools attach to your next visible reply automatically. Explain the outcome; do not repeat the card fields or draw a card in text.",
|
|
1487
1554
|
"- 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.",
|
|
1488
1555
|
"- 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.",
|
|
1489
1556
|
"- 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.",
|
|
@@ -2524,6 +2591,570 @@ function formatSandboxCommandResult(params) {
|
|
|
2524
2591
|
});
|
|
2525
2592
|
}
|
|
2526
2593
|
//#endregion
|
|
2594
|
+
//#region src/chat/slack/outbound.ts
|
|
2595
|
+
const MAX_SLACK_MESSAGE_TEXT_CHARS = 4e4;
|
|
2596
|
+
function requireSlackConversationId(channelId, action) {
|
|
2597
|
+
const normalized = normalizeSlackConversationId(channelId);
|
|
2598
|
+
if (!normalized) throw new Error(`${action} requires a valid channel ID`);
|
|
2599
|
+
return normalized;
|
|
2600
|
+
}
|
|
2601
|
+
function requireSlackThreadTimestamp(threadTs, action) {
|
|
2602
|
+
const normalized = threadTs.trim();
|
|
2603
|
+
if (!normalized) throw new Error(`${action} requires a thread timestamp`);
|
|
2604
|
+
return normalized;
|
|
2605
|
+
}
|
|
2606
|
+
function requireSlackMessageTimestamp(timestamp, action) {
|
|
2607
|
+
const normalized = parseSlackMessageTs(timestamp);
|
|
2608
|
+
if (!normalized) throw new Error(`${action} requires a target message timestamp`);
|
|
2609
|
+
return normalized;
|
|
2610
|
+
}
|
|
2611
|
+
function requireSlackMessageText(text, action) {
|
|
2612
|
+
if (text.trim().length === 0) throw new Error(`${action} requires non-empty text`);
|
|
2613
|
+
if (text.length > MAX_SLACK_MESSAGE_TEXT_CHARS) throw new Error(`${action} text exceeds Slack's 40000 character truncation limit`);
|
|
2614
|
+
return text;
|
|
2615
|
+
}
|
|
2616
|
+
/** Resolve a Slack message permalink without making read-only workflows fail on lookup errors. */
|
|
2617
|
+
async function getSlackMessagePermalink(args) {
|
|
2618
|
+
try {
|
|
2619
|
+
return (await withSlackRetries(() => getSlackClient().chat.getPermalink({
|
|
2620
|
+
channel: args.channelId,
|
|
2621
|
+
message_ts: args.messageTs
|
|
2622
|
+
}), 3, {
|
|
2623
|
+
action: "chat.getPermalink",
|
|
2624
|
+
idempotent: true,
|
|
2625
|
+
spanAttributes: {
|
|
2626
|
+
"app.slack.channel_id": args.channelId,
|
|
2627
|
+
"app.slack.message_ts": args.messageTs
|
|
2628
|
+
}
|
|
2629
|
+
})).permalink;
|
|
2630
|
+
} catch {
|
|
2631
|
+
return;
|
|
2632
|
+
}
|
|
2633
|
+
}
|
|
2634
|
+
/** Post Slack `mrkdwn` text to a conversation or thread via the shared outbound boundary. */
|
|
2635
|
+
async function postSlackMessage(input) {
|
|
2636
|
+
const channelId = requireSlackConversationId(input.channelId, "Slack message posting");
|
|
2637
|
+
const text = requireSlackMessageText(input.text, "Slack message posting");
|
|
2638
|
+
const threadTs = input.threadTs ? requireSlackThreadTimestamp(input.threadTs, "Slack thread message posting") : void 0;
|
|
2639
|
+
const response = await withSlackRetries(() => getSlackClient().chat.postMessage({
|
|
2640
|
+
channel: channelId,
|
|
2641
|
+
text,
|
|
2642
|
+
unfurl_links: false,
|
|
2643
|
+
unfurl_media: false,
|
|
2644
|
+
...input.blocks?.length ? { blocks: input.blocks } : void 0,
|
|
2645
|
+
...input.entities?.length ? { metadata: { entities: input.entities } } : void 0,
|
|
2646
|
+
...threadTs ? { thread_ts: threadTs } : void 0
|
|
2647
|
+
}), 3, {
|
|
2648
|
+
action: "chat.postMessage",
|
|
2649
|
+
spanAttributes: {
|
|
2650
|
+
"app.slack.channel_id": channelId,
|
|
2651
|
+
...threadTs ? { "app.slack.thread_ts": threadTs } : void 0
|
|
2652
|
+
}
|
|
2653
|
+
});
|
|
2654
|
+
const messageTs = parseSlackMessageTs(response.ts);
|
|
2655
|
+
if (!messageTs) throw new Error("Slack message posted without ts");
|
|
2656
|
+
return {
|
|
2657
|
+
ts: messageTs,
|
|
2658
|
+
...input.includePermalink ? { permalink: await getSlackMessagePermalink({
|
|
2659
|
+
channelId,
|
|
2660
|
+
messageTs
|
|
2661
|
+
}) } : void 0
|
|
2662
|
+
};
|
|
2663
|
+
}
|
|
2664
|
+
/**
|
|
2665
|
+
* Post an ephemeral Slack message. Delivery is best-effort on Slack's side, but
|
|
2666
|
+
* request validation and Web API behavior are centralized here.
|
|
2667
|
+
*/
|
|
2668
|
+
async function postSlackEphemeralMessage(input) {
|
|
2669
|
+
const channelId = requireSlackConversationId(input.channelId, "Slack ephemeral message posting");
|
|
2670
|
+
const userId = parseSlackUserId(input.userId);
|
|
2671
|
+
if (!userId) throw new Error("Slack ephemeral message posting requires a user ID");
|
|
2672
|
+
const text = requireSlackMessageText(input.text, "Slack ephemeral message posting");
|
|
2673
|
+
const threadTs = input.threadTs ? requireSlackThreadTimestamp(input.threadTs, "Slack ephemeral thread message posting") : void 0;
|
|
2674
|
+
return { messageTs: (await withSlackRetries(() => getSlackClient().chat.postEphemeral({
|
|
2675
|
+
channel: channelId,
|
|
2676
|
+
user: userId,
|
|
2677
|
+
text,
|
|
2678
|
+
...input.blocks?.length ? { blocks: input.blocks } : void 0,
|
|
2679
|
+
...threadTs ? { thread_ts: threadTs } : void 0
|
|
2680
|
+
}), 3, {
|
|
2681
|
+
action: "chat.postEphemeral",
|
|
2682
|
+
spanAttributes: {
|
|
2683
|
+
"app.slack.channel_id": channelId,
|
|
2684
|
+
"app.slack.user_id": userId,
|
|
2685
|
+
...threadTs ? { "app.slack.thread_ts": threadTs } : void 0
|
|
2686
|
+
}
|
|
2687
|
+
})).message_ts };
|
|
2688
|
+
}
|
|
2689
|
+
/** Upload files into a Slack conversation or thread via the shared outbound file boundary. */
|
|
2690
|
+
async function uploadFilesToConversation(input) {
|
|
2691
|
+
const channelId = requireSlackConversationId(input.channelId, "Slack file upload");
|
|
2692
|
+
const threadTs = input.threadTs ? requireSlackThreadTimestamp(input.threadTs, "Slack file upload") : void 0;
|
|
2693
|
+
if (input.files.length === 0) throw new Error("Slack file upload requires at least one file");
|
|
2694
|
+
const fileUploads = input.files.map((file) => {
|
|
2695
|
+
const filename = file.filename.trim();
|
|
2696
|
+
if (!filename) throw new Error("Slack file upload requires every file to have a filename");
|
|
2697
|
+
return {
|
|
2698
|
+
file: file.data,
|
|
2699
|
+
filename
|
|
2700
|
+
};
|
|
2701
|
+
});
|
|
2702
|
+
return { files: ((await withSlackRetries(() => getSlackClient().filesUploadV2({
|
|
2703
|
+
channel_id: channelId,
|
|
2704
|
+
...threadTs ? { thread_ts: threadTs } : void 0,
|
|
2705
|
+
file_uploads: fileUploads
|
|
2706
|
+
}), 3, {
|
|
2707
|
+
action: "filesUploadV2",
|
|
2708
|
+
spanAttributes: {
|
|
2709
|
+
"app.slack.channel_id": channelId,
|
|
2710
|
+
...threadTs ? { "app.slack.thread_ts": threadTs } : void 0
|
|
2711
|
+
}
|
|
2712
|
+
})).files ?? []).flatMap((completion) => (completion.files ?? []).map((file) => file.id ? { id: file.id } : {})) };
|
|
2713
|
+
}
|
|
2714
|
+
/** Add a reaction to a Slack message, treating `already_reacted` as idempotent success. */
|
|
2715
|
+
async function addReactionToMessage(input) {
|
|
2716
|
+
const channelId = requireSlackConversationId(input.channelId, "Slack reaction");
|
|
2717
|
+
const timestamp = requireSlackMessageTimestamp(input.timestamp, "Slack reaction");
|
|
2718
|
+
const emoji = normalizeSlackEmojiName(input.emoji);
|
|
2719
|
+
if (!emoji) throw new Error("Slack reaction requires a valid emoji alias name");
|
|
2720
|
+
try {
|
|
2721
|
+
await withSlackRetries(() => getSlackClient().reactions.add({
|
|
2722
|
+
channel: channelId,
|
|
2723
|
+
timestamp,
|
|
2724
|
+
name: emoji
|
|
2725
|
+
}), 3, {
|
|
2726
|
+
action: "reactions.add",
|
|
2727
|
+
idempotent: true,
|
|
2728
|
+
spanAttributes: {
|
|
2729
|
+
"app.slack.channel_id": channelId,
|
|
2730
|
+
"app.slack.message_ts": timestamp,
|
|
2731
|
+
"app.slack.reaction": emoji
|
|
2732
|
+
}
|
|
2733
|
+
});
|
|
2734
|
+
} catch (error) {
|
|
2735
|
+
if (error instanceof SlackActionError && error.code === "already_reacted") return { ok: true };
|
|
2736
|
+
throw error;
|
|
2737
|
+
}
|
|
2738
|
+
return { ok: true };
|
|
2739
|
+
}
|
|
2740
|
+
/** Remove a reaction from a Slack message, treating `no_reaction` as idempotent success. */
|
|
2741
|
+
async function removeReactionFromMessage(input) {
|
|
2742
|
+
const channelId = requireSlackConversationId(input.channelId, "Slack reaction removal");
|
|
2743
|
+
const timestamp = requireSlackMessageTimestamp(input.timestamp, "Slack reaction removal");
|
|
2744
|
+
const emoji = normalizeSlackEmojiName(input.emoji);
|
|
2745
|
+
if (!emoji) throw new Error("Slack reaction removal requires a valid emoji alias name");
|
|
2746
|
+
try {
|
|
2747
|
+
await withSlackRetries(() => getSlackClient().reactions.remove({
|
|
2748
|
+
channel: channelId,
|
|
2749
|
+
timestamp,
|
|
2750
|
+
name: emoji
|
|
2751
|
+
}), 3, {
|
|
2752
|
+
action: "reactions.remove",
|
|
2753
|
+
idempotent: true,
|
|
2754
|
+
spanAttributes: {
|
|
2755
|
+
"app.slack.channel_id": channelId,
|
|
2756
|
+
"app.slack.message_ts": timestamp,
|
|
2757
|
+
"app.slack.reaction": emoji
|
|
2758
|
+
}
|
|
2759
|
+
});
|
|
2760
|
+
} catch (error) {
|
|
2761
|
+
if (error instanceof SlackActionError && error.code === "no_reaction") return { ok: true };
|
|
2762
|
+
throw error;
|
|
2763
|
+
}
|
|
2764
|
+
return { ok: true };
|
|
2765
|
+
}
|
|
2766
|
+
//#endregion
|
|
2767
|
+
//#region src/chat/slack/status-format.ts
|
|
2768
|
+
const SLACK_STATUS_MAX_LENGTH = 50;
|
|
2769
|
+
/** Truncate a status string to fit Slack's 50-char assistant status limit. */
|
|
2770
|
+
function truncateStatusText(text) {
|
|
2771
|
+
const trimmed = text.trim();
|
|
2772
|
+
if (!trimmed) return "";
|
|
2773
|
+
if (trimmed.length <= SLACK_STATUS_MAX_LENGTH) return trimmed;
|
|
2774
|
+
return `${trimmed.slice(0, 47).trimEnd()}...`;
|
|
2775
|
+
}
|
|
2776
|
+
//#endregion
|
|
2777
|
+
//#region src/chat/slack/mrkdwn.ts
|
|
2778
|
+
/** Escape dynamic text for Slack mrkdwn without changing intended formatting. */
|
|
2779
|
+
function escapeSlackMrkdwnText(text) {
|
|
2780
|
+
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
2781
|
+
}
|
|
2782
|
+
/** Escape a URL for Slack explicit link syntax while preserving query semantics. */
|
|
2783
|
+
function escapeSlackLinkUrl(url) {
|
|
2784
|
+
return url.replaceAll("&", "&").replaceAll("<", "%3C").replaceAll(">", "%3E");
|
|
2785
|
+
}
|
|
2786
|
+
/** Build a Slack explicit link from dynamic URL and label values. */
|
|
2787
|
+
function formatSlackLink(url, label) {
|
|
2788
|
+
return `<${escapeSlackLinkUrl(url)}|${escapeSlackMrkdwnText(label)}>`;
|
|
2789
|
+
}
|
|
2790
|
+
function readInlineCodeSpan(line, start) {
|
|
2791
|
+
if (line[start] !== "`") return;
|
|
2792
|
+
let n = 1;
|
|
2793
|
+
while (line[start + n] === "`") n++;
|
|
2794
|
+
const marker = "`".repeat(n);
|
|
2795
|
+
let search = start + n;
|
|
2796
|
+
while (search < line.length) {
|
|
2797
|
+
const close = line.indexOf(marker, search);
|
|
2798
|
+
if (close === -1) return;
|
|
2799
|
+
const after = close + n;
|
|
2800
|
+
if (line[after] !== "`") return {
|
|
2801
|
+
text: line.slice(start, after),
|
|
2802
|
+
end: after
|
|
2803
|
+
};
|
|
2804
|
+
search = after + 1;
|
|
2805
|
+
}
|
|
2806
|
+
}
|
|
2807
|
+
function readExistingSlackAngleToken(line, start) {
|
|
2808
|
+
if (line[start] !== "<") return;
|
|
2809
|
+
const close = line.indexOf(">", start + 1);
|
|
2810
|
+
if (close === -1) return;
|
|
2811
|
+
const body = line.slice(start + 1, close);
|
|
2812
|
+
if (/^(?:https?:\/\/|@|#|!)/.test(body)) return {
|
|
2813
|
+
text: line.slice(start, close + 1),
|
|
2814
|
+
end: close + 1
|
|
2815
|
+
};
|
|
2816
|
+
}
|
|
2817
|
+
function readMarkdownLink(line, start) {
|
|
2818
|
+
if (line[start] !== "[") return;
|
|
2819
|
+
const labelEnd = line.indexOf("](", start + 1);
|
|
2820
|
+
if (labelEnd === -1) return;
|
|
2821
|
+
const destStart = labelEnd + 2;
|
|
2822
|
+
if (!line.startsWith("http://", destStart) && !line.startsWith("https://", destStart)) return;
|
|
2823
|
+
const closeParens = line.indexOf(")", destStart);
|
|
2824
|
+
if (closeParens === -1) return;
|
|
2825
|
+
return {
|
|
2826
|
+
text: line.slice(start, closeParens + 1),
|
|
2827
|
+
end: closeParens + 1
|
|
2828
|
+
};
|
|
2829
|
+
}
|
|
2830
|
+
function hasUnmatchedClosingParen(text) {
|
|
2831
|
+
let balance = 0;
|
|
2832
|
+
for (const ch of text) if (ch === "(") balance++;
|
|
2833
|
+
else if (ch === ")") balance--;
|
|
2834
|
+
return balance < 0;
|
|
2835
|
+
}
|
|
2836
|
+
function readBareUrl(line, start) {
|
|
2837
|
+
let end = start;
|
|
2838
|
+
while (end < line.length) {
|
|
2839
|
+
const ch = line[end];
|
|
2840
|
+
if (/\s/.test(ch) || ch === "<" || ch === ">" || ch === "\"" || ch === "`" || ch === "|" || ch === "*") break;
|
|
2841
|
+
end++;
|
|
2842
|
+
}
|
|
2843
|
+
if (end === start) return;
|
|
2844
|
+
let raw = line.slice(start, end);
|
|
2845
|
+
let suffix = "";
|
|
2846
|
+
const peel = () => {
|
|
2847
|
+
suffix = raw.slice(-1) + suffix;
|
|
2848
|
+
raw = raw.slice(0, -1);
|
|
2849
|
+
};
|
|
2850
|
+
const shouldPeel = () => raw.endsWith("_") || /[.,!?;:]$/.test(raw) || raw.endsWith(")") && hasUnmatchedClosingParen(raw);
|
|
2851
|
+
while (raw.length > 0 && shouldPeel()) peel();
|
|
2852
|
+
if (!/^https?:\/\/.+/.test(raw)) return;
|
|
2853
|
+
return {
|
|
2854
|
+
url: raw,
|
|
2855
|
+
suffix,
|
|
2856
|
+
end
|
|
2857
|
+
};
|
|
2858
|
+
}
|
|
2859
|
+
/** Wrap bare http(s) URLs on a single line as Slack explicit `<url>` links. */
|
|
2860
|
+
function wrapBareUrlsOnLine(line) {
|
|
2861
|
+
let result = "";
|
|
2862
|
+
let i = 0;
|
|
2863
|
+
while (i < line.length) {
|
|
2864
|
+
const codeSpan = readInlineCodeSpan(line, i);
|
|
2865
|
+
if (codeSpan) {
|
|
2866
|
+
result += codeSpan.text;
|
|
2867
|
+
i = codeSpan.end;
|
|
2868
|
+
continue;
|
|
2869
|
+
}
|
|
2870
|
+
const angleToken = readExistingSlackAngleToken(line, i);
|
|
2871
|
+
if (angleToken) {
|
|
2872
|
+
result += angleToken.text;
|
|
2873
|
+
i = angleToken.end;
|
|
2874
|
+
continue;
|
|
2875
|
+
}
|
|
2876
|
+
const mdLink = readMarkdownLink(line, i);
|
|
2877
|
+
if (mdLink) {
|
|
2878
|
+
result += mdLink.text;
|
|
2879
|
+
i = mdLink.end;
|
|
2880
|
+
continue;
|
|
2881
|
+
}
|
|
2882
|
+
if (line.startsWith("https://", i) || line.startsWith("http://", i)) {
|
|
2883
|
+
const parsed = readBareUrl(line, i);
|
|
2884
|
+
if (parsed) {
|
|
2885
|
+
result += `<${parsed.url}>${parsed.suffix}`;
|
|
2886
|
+
i = parsed.end;
|
|
2887
|
+
continue;
|
|
2888
|
+
}
|
|
2889
|
+
}
|
|
2890
|
+
result += line[i];
|
|
2891
|
+
i++;
|
|
2892
|
+
}
|
|
2893
|
+
return result;
|
|
2894
|
+
}
|
|
2895
|
+
/**
|
|
2896
|
+
* Pre-wrap bare http(s) URLs outside fenced code blocks as Slack explicit
|
|
2897
|
+
* links, preventing Slack's auto-linker from consuming adjacent formatting
|
|
2898
|
+
* markers into the URL.
|
|
2899
|
+
*
|
|
2900
|
+
* Uses the same fence-toggle rule as `ensureBlockSpacing` so both passes
|
|
2901
|
+
* agree on which lines are code.
|
|
2902
|
+
*/
|
|
2903
|
+
function wrapBareUrls(text) {
|
|
2904
|
+
const lines = text.split("\n");
|
|
2905
|
+
const out = [];
|
|
2906
|
+
let inCodeBlock = false;
|
|
2907
|
+
for (const line of lines) {
|
|
2908
|
+
if (line.trimStart().startsWith("```")) {
|
|
2909
|
+
inCodeBlock = !inCodeBlock;
|
|
2910
|
+
out.push(line);
|
|
2911
|
+
continue;
|
|
2912
|
+
}
|
|
2913
|
+
out.push(inCodeBlock ? line : wrapBareUrlsOnLine(line));
|
|
2914
|
+
}
|
|
2915
|
+
return out.join("\n");
|
|
2916
|
+
}
|
|
2917
|
+
/** Insert blank lines between content blocks so Slack renders them with visual separation. */
|
|
2918
|
+
function ensureBlockSpacing(text) {
|
|
2919
|
+
const codeBlockPattern = /^```/;
|
|
2920
|
+
const listItemPattern = /^[-*•]\s|^\d+\.\s/;
|
|
2921
|
+
const lines = text.split("\n");
|
|
2922
|
+
const result = [];
|
|
2923
|
+
let inCodeBlock = false;
|
|
2924
|
+
for (let i = 0; i < lines.length; i++) {
|
|
2925
|
+
const line = lines[i];
|
|
2926
|
+
if (codeBlockPattern.test(line.trimStart())) {
|
|
2927
|
+
if (!inCodeBlock) {
|
|
2928
|
+
const prev = result.length > 0 ? result[result.length - 1] : void 0;
|
|
2929
|
+
if (prev !== void 0 && prev.trim() !== "") result.push("");
|
|
2930
|
+
}
|
|
2931
|
+
inCodeBlock = !inCodeBlock;
|
|
2932
|
+
result.push(line);
|
|
2933
|
+
continue;
|
|
2934
|
+
}
|
|
2935
|
+
if (inCodeBlock) {
|
|
2936
|
+
result.push(line);
|
|
2937
|
+
continue;
|
|
2938
|
+
}
|
|
2939
|
+
const prev = result.length > 0 ? result[result.length - 1] : void 0;
|
|
2940
|
+
if (prev !== void 0 && prev.trim() !== "" && line.trim() !== "" && !(listItemPattern.test(prev.trimStart()) && listItemPattern.test(line.trimStart()))) result.push("");
|
|
2941
|
+
result.push(line);
|
|
2942
|
+
}
|
|
2943
|
+
return result.join("\n");
|
|
2944
|
+
}
|
|
2945
|
+
/**
|
|
2946
|
+
* Normalize model-authored Slack markdown for delivery via `markdown_text`
|
|
2947
|
+
* or `{ type: "markdown" }` blocks.
|
|
2948
|
+
*
|
|
2949
|
+
* Plugins may first rewrite ordinary Markdown. Core then applies Slack-only
|
|
2950
|
+
* delivery shaping: bare URL wrapping and block spacing. Slack reply delivery
|
|
2951
|
+
* owns chunking and continuation markers separately.
|
|
2952
|
+
*/
|
|
2953
|
+
function normalizeSlackReplyMarkdown(text) {
|
|
2954
|
+
let normalized = applyPluginFormatMarkdown(text).replace(/\r\n?/g, "\n").replace(/[ \t]+$/gm, "");
|
|
2955
|
+
normalized = wrapBareUrls(normalized);
|
|
2956
|
+
normalized = ensureBlockSpacing(normalized);
|
|
2957
|
+
return normalized.replace(/\n{3,}/g, "\n\n").trim();
|
|
2958
|
+
}
|
|
2959
|
+
/** Normalize assistant status text before handing it to Slack. */
|
|
2960
|
+
function normalizeSlackStatusText(text) {
|
|
2961
|
+
const trimmed = text.trim();
|
|
2962
|
+
if (!trimmed) return "";
|
|
2963
|
+
return truncateStatusText(trimmed.replace(/(?:\.\s*)+$/, "").trim());
|
|
2964
|
+
}
|
|
2965
|
+
//#endregion
|
|
2966
|
+
//#region src/chat/slack/oauth-authorization-message.ts
|
|
2967
|
+
/** Present private OAuth authorization with a clear action and text fallback. */
|
|
2968
|
+
function buildSlackOAuthAuthorizationMessage(args) {
|
|
2969
|
+
return {
|
|
2970
|
+
text: `${formatSlackLink(args.authorizationUrl, args.label)}. ${args.completionText}`,
|
|
2971
|
+
blocks: [{
|
|
2972
|
+
type: "section",
|
|
2973
|
+
text: {
|
|
2974
|
+
type: "mrkdwn",
|
|
2975
|
+
text: `*${escapeSlackMrkdwnText(args.label)}*\n${escapeSlackMrkdwnText(args.completionText)}`
|
|
2976
|
+
},
|
|
2977
|
+
accessory: {
|
|
2978
|
+
type: "button",
|
|
2979
|
+
text: {
|
|
2980
|
+
type: "plain_text",
|
|
2981
|
+
text: "Connect"
|
|
2982
|
+
},
|
|
2983
|
+
style: "primary",
|
|
2984
|
+
url: args.authorizationUrl,
|
|
2985
|
+
action_id: "oauth_connect"
|
|
2986
|
+
}
|
|
2987
|
+
}]
|
|
2988
|
+
};
|
|
2989
|
+
}
|
|
2990
|
+
//#endregion
|
|
2991
|
+
//#region src/chat/oauth-flow.ts
|
|
2992
|
+
const OAUTH_STATE_TTL_MS = 6e5;
|
|
2993
|
+
function optionalString(value) {
|
|
2994
|
+
return typeof value === "string" ? value : void 0;
|
|
2995
|
+
}
|
|
2996
|
+
/** Parse OAuth callback state that was persisted before a provider redirect. */
|
|
2997
|
+
function parseOAuthStatePayload(value) {
|
|
2998
|
+
if (!isRecord$2(value)) return;
|
|
2999
|
+
if (typeof value.userId !== "string" || typeof value.provider !== "string") return;
|
|
3000
|
+
const actor = value.actor === void 0 ? void 0 : actorSchema.safeParse(value.actor);
|
|
3001
|
+
if (value.actor !== void 0 && (!actor || !actor.success)) return;
|
|
3002
|
+
const destination = parseDestination(value.destination);
|
|
3003
|
+
if (value.destination !== void 0 && !destination) return;
|
|
3004
|
+
const source = value.source === void 0 ? void 0 : sourceSchema.safeParse(value.source);
|
|
3005
|
+
if (value.source !== void 0 && (!source || !source.success)) return;
|
|
3006
|
+
return {
|
|
3007
|
+
userId: value.userId,
|
|
3008
|
+
provider: value.provider,
|
|
3009
|
+
...actor?.success ? { actor: actor.data } : void 0,
|
|
3010
|
+
...optionalString(value.channelId) ? { channelId: optionalString(value.channelId) } : void 0,
|
|
3011
|
+
...destination ? { destination } : void 0,
|
|
3012
|
+
...source?.success ? { source: source.data } : void 0,
|
|
3013
|
+
...optionalString(value.threadTs) ? { threadTs: optionalString(value.threadTs) } : void 0,
|
|
3014
|
+
...optionalString(value.resumeConversationId) ? { resumeConversationId: optionalString(value.resumeConversationId) } : void 0,
|
|
3015
|
+
...optionalString(value.resumeSessionId) ? { resumeSessionId: optionalString(value.resumeSessionId) } : void 0,
|
|
3016
|
+
...optionalString(value.scope) ? { scope: optionalString(value.scope) } : void 0
|
|
3017
|
+
};
|
|
3018
|
+
}
|
|
3019
|
+
/** Return the manifest-owned display label for a provider. */
|
|
3020
|
+
function formatProviderLabel(provider) {
|
|
3021
|
+
const displayName = pluginCatalogRuntime.getDisplayName(provider);
|
|
3022
|
+
if (!displayName) throw new Error(`Unknown plugin provider display name: "${provider}"`);
|
|
3023
|
+
return displayName;
|
|
3024
|
+
}
|
|
3025
|
+
/** Resolve the public base URL from environment variables (JUNIOR_BASE_URL or Vercel). */
|
|
3026
|
+
function resolveBaseUrl() {
|
|
3027
|
+
const explicit = process.env.JUNIOR_BASE_URL?.trim();
|
|
3028
|
+
if (explicit) return explicit;
|
|
3029
|
+
const vercelProd = process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim();
|
|
3030
|
+
if (vercelProd) return `https://${vercelProd}`;
|
|
3031
|
+
const vercelUrl = process.env.VERCEL_URL?.trim();
|
|
3032
|
+
if (vercelUrl) return `https://${vercelUrl}`;
|
|
3033
|
+
}
|
|
3034
|
+
/**
|
|
3035
|
+
* Authorization links must only be visible to the requesting user.
|
|
3036
|
+
* Try in-context private delivery first, then fall back to a DM.
|
|
3037
|
+
*/
|
|
3038
|
+
async function deliverPrivateMessage(input) {
|
|
3039
|
+
let client;
|
|
3040
|
+
try {
|
|
3041
|
+
client = getSlackClient();
|
|
3042
|
+
} catch {
|
|
3043
|
+
logWarn("oauth.private_delivery.skipped", { "app.reason": "missing_bot_token" });
|
|
3044
|
+
return false;
|
|
3045
|
+
}
|
|
3046
|
+
if (input.channelId) try {
|
|
3047
|
+
if (isDmChannel$1(input.channelId)) await postSlackMessage({
|
|
3048
|
+
channelId: input.channelId,
|
|
3049
|
+
text: input.text,
|
|
3050
|
+
blocks: input.blocks,
|
|
3051
|
+
threadTs: input.threadTs
|
|
3052
|
+
});
|
|
3053
|
+
else await postSlackEphemeralMessage({
|
|
3054
|
+
channelId: input.channelId,
|
|
3055
|
+
userId: input.userId,
|
|
3056
|
+
text: input.text,
|
|
3057
|
+
blocks: input.blocks,
|
|
3058
|
+
threadTs: input.threadTs
|
|
3059
|
+
});
|
|
3060
|
+
return "in_context";
|
|
3061
|
+
} catch (error) {
|
|
3062
|
+
logWarn("oauth.private_delivery.failed", {
|
|
3063
|
+
"app.slack.error": error instanceof Error ? error.message : String(error),
|
|
3064
|
+
"app.slack.channel": input.channelId
|
|
3065
|
+
});
|
|
3066
|
+
}
|
|
3067
|
+
try {
|
|
3068
|
+
const dmChannelId = (await withSlackRetries(() => client.conversations.open({ users: input.userId }), 3, {
|
|
3069
|
+
action: "conversations.open",
|
|
3070
|
+
spanAttributes: { "app.slack.user_id": input.userId }
|
|
3071
|
+
})).channel?.id;
|
|
3072
|
+
if (!dmChannelId) {
|
|
3073
|
+
logWarn("oauth.dm.fallback.failed", { "app.reason": "no_dm_channel_id" });
|
|
3074
|
+
return false;
|
|
3075
|
+
}
|
|
3076
|
+
await postSlackMessage({
|
|
3077
|
+
channelId: dmChannelId,
|
|
3078
|
+
text: input.text,
|
|
3079
|
+
blocks: input.blocks
|
|
3080
|
+
});
|
|
3081
|
+
return "fallback_dm";
|
|
3082
|
+
} catch (error) {
|
|
3083
|
+
logWarn("oauth.dm.fallback.failed", { "app.slack.error": error instanceof Error ? error.message : String(error) });
|
|
3084
|
+
return false;
|
|
3085
|
+
}
|
|
3086
|
+
}
|
|
3087
|
+
/** Present one OAuth request through the active surface's private delivery. */
|
|
3088
|
+
async function deliverOAuthAuthorization(request, input) {
|
|
3089
|
+
if (input.authorization) {
|
|
3090
|
+
await input.authorization.deliver(request);
|
|
3091
|
+
return "in_context";
|
|
3092
|
+
}
|
|
3093
|
+
return await deliverPrivateMessage({
|
|
3094
|
+
channelId: input.channelId,
|
|
3095
|
+
threadTs: input.threadTs,
|
|
3096
|
+
userId: input.userId,
|
|
3097
|
+
...buildSlackOAuthAuthorizationMessage(request)
|
|
3098
|
+
});
|
|
3099
|
+
}
|
|
3100
|
+
/** Initiate an OAuth authorization code flow for a provider and deliver the auth link to the user. */
|
|
3101
|
+
async function startOAuthFlow(provider, input) {
|
|
3102
|
+
const providerConfig = pluginCatalogRuntime.getOAuthConfig(provider);
|
|
3103
|
+
if (!providerConfig) return {
|
|
3104
|
+
ok: false,
|
|
3105
|
+
error: `Provider "${provider}" does not support OAuth authorization`
|
|
3106
|
+
};
|
|
3107
|
+
const clientId = process.env[providerConfig.clientIdEnv]?.trim();
|
|
3108
|
+
if (!clientId) return {
|
|
3109
|
+
ok: false,
|
|
3110
|
+
error: `Missing ${providerConfig.clientIdEnv} environment variable`
|
|
3111
|
+
};
|
|
3112
|
+
const baseUrl = resolveBaseUrl();
|
|
3113
|
+
if (!baseUrl) return {
|
|
3114
|
+
ok: false,
|
|
3115
|
+
error: "Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)"
|
|
3116
|
+
};
|
|
3117
|
+
const state = input.authorization ? await input.authorization.createState() : randomBytes(32).toString("hex");
|
|
3118
|
+
const requestedScope = input.scope ?? providerConfig.scope;
|
|
3119
|
+
await getStateAdapter().set(`oauth-state:${state}`, {
|
|
3120
|
+
userId: input.actorId,
|
|
3121
|
+
provider,
|
|
3122
|
+
...input.actor ? { actor: input.actor } : void 0,
|
|
3123
|
+
...input.channelId ? { channelId: input.channelId } : void 0,
|
|
3124
|
+
...input.destination ? { destination: input.destination } : void 0,
|
|
3125
|
+
...input.source ? { source: input.source } : void 0,
|
|
3126
|
+
...input.threadTs ? { threadTs: input.threadTs } : void 0,
|
|
3127
|
+
...input.resumeConversationId ? { resumeConversationId: input.resumeConversationId } : void 0,
|
|
3128
|
+
...input.resumeSessionId ? { resumeSessionId: input.resumeSessionId } : void 0,
|
|
3129
|
+
...requestedScope ? { scope: requestedScope } : void 0
|
|
3130
|
+
}, OAUTH_STATE_TTL_MS);
|
|
3131
|
+
const authorizeParams = new URLSearchParams({
|
|
3132
|
+
client_id: clientId,
|
|
3133
|
+
state,
|
|
3134
|
+
redirect_uri: `${baseUrl}${providerConfig.callbackPath}`,
|
|
3135
|
+
response_type: "code"
|
|
3136
|
+
});
|
|
3137
|
+
if (requestedScope) authorizeParams.set("scope", requestedScope);
|
|
3138
|
+
for (const [key, value] of Object.entries(providerConfig.authorizeParams ?? {})) authorizeParams.set(key, value);
|
|
3139
|
+
logInfo("jr_rpc.oauth.started", {
|
|
3140
|
+
"app.credential.provider": provider,
|
|
3141
|
+
...input.activeSkillName ? { "app.skill.name": input.activeSkillName } : void 0
|
|
3142
|
+
});
|
|
3143
|
+
return {
|
|
3144
|
+
ok: true,
|
|
3145
|
+
delivery: await deliverOAuthAuthorization({
|
|
3146
|
+
authorizationUrl: `${providerConfig.authorizeEndpoint}?${authorizeParams.toString()}`,
|
|
3147
|
+
label: `Connect to ${formatProviderLabel(provider)}`,
|
|
3148
|
+
completionText: input.resumeSessionId ? "Once you've authorized, Junior will continue automatically." : "Once you've authorized, you'll see a confirmation in Slack."
|
|
3149
|
+
}, {
|
|
3150
|
+
authorization: input.authorization,
|
|
3151
|
+
channelId: input.channelId,
|
|
3152
|
+
threadTs: input.threadTs,
|
|
3153
|
+
userId: input.actorId
|
|
3154
|
+
})
|
|
3155
|
+
};
|
|
3156
|
+
}
|
|
3157
|
+
//#endregion
|
|
2527
3158
|
//#region src/chat/sandbox/egress/schemas.ts
|
|
2528
3159
|
const finiteNumberSchema = z.number().refine(Number.isFinite);
|
|
2529
3160
|
const httpStatusSchema = z.number().int().min(100).max(599);
|
|
@@ -6824,13 +7455,16 @@ var McpToolManager = class {
|
|
|
6824
7455
|
providerContent,
|
|
6825
7456
|
...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : void 0
|
|
6826
7457
|
};
|
|
6827
|
-
await this.options.onToolSuccess?.({
|
|
7458
|
+
const cards = await this.options.onToolSuccess?.({
|
|
6828
7459
|
arguments: resolvedArgs,
|
|
6829
7460
|
provider: plugin.manifest.name,
|
|
6830
7461
|
...result.structuredContent !== void 0 ? { structuredContent: result.structuredContent } : void 0,
|
|
6831
7462
|
toolName: tool.name
|
|
6832
7463
|
});
|
|
6833
|
-
return
|
|
7464
|
+
return cards?.length ? {
|
|
7465
|
+
...successResult,
|
|
7466
|
+
cards
|
|
7467
|
+
} : successResult;
|
|
6834
7468
|
} catch (error) {
|
|
6835
7469
|
if (error instanceof McpAuthorizationRequiredError && await this.handleAuthorizationRequired(plugin.manifest.name, error)) {
|
|
6836
7470
|
const parkedContent = [{
|
|
@@ -7148,6 +7782,7 @@ function createCallMcpToolTool(mcpToolManager) {
|
|
|
7148
7782
|
}
|
|
7149
7783
|
};
|
|
7150
7784
|
},
|
|
7785
|
+
outputSchema: z.object({ objectCards: z.array(ownedObjectAnnotationSchema) }),
|
|
7151
7786
|
execute: async (input, options) => {
|
|
7152
7787
|
const { tool_name } = input;
|
|
7153
7788
|
const provider = parseMcpProviderFromToolName(tool_name);
|
|
@@ -7167,10 +7802,14 @@ function createCallMcpToolTool(mcpToolManager) {
|
|
|
7167
7802
|
});
|
|
7168
7803
|
throw new McpToolError(missingToolMessage(tool_name, provider));
|
|
7169
7804
|
}
|
|
7170
|
-
|
|
7805
|
+
const result = await mcpTool.execute(resolveMcpArguments(input), {
|
|
7171
7806
|
conversationPrivacy: options?.conversationPrivacy ?? "private",
|
|
7172
7807
|
...options?.toolCallId ? { toolCallId: options.toolCallId } : void 0
|
|
7173
|
-
})
|
|
7808
|
+
});
|
|
7809
|
+
return {
|
|
7810
|
+
content: result.content,
|
|
7811
|
+
details: { objectCards: result.cards ?? [] }
|
|
7812
|
+
};
|
|
7174
7813
|
}
|
|
7175
7814
|
});
|
|
7176
7815
|
}
|
|
@@ -7928,7 +8567,7 @@ function createEventAutomationTool(context, catalog) {
|
|
|
7928
8567
|
const existing = await getEventAutomation(db, id);
|
|
7929
8568
|
if (existing) {
|
|
7930
8569
|
if (!eventAutomationMatchesDestination(existing, destination) || existing.createdBy.slackUserId !== actor.userId) throw new ToolInputError("Event automation operation identity is invalid.");
|
|
7931
|
-
if (existing.status !== "deleted") return eventAutomationToolResult(existing, catalog, actor.userId);
|
|
8570
|
+
if (existing.status !== "deleted") return eventAutomationToolResult(context.conversationId, existing, catalog, actor.userId);
|
|
7932
8571
|
}
|
|
7933
8572
|
const title = await resolveTaskTitle({
|
|
7934
8573
|
completeText,
|
|
@@ -7958,7 +8597,7 @@ function createEventAutomationTool(context, catalog) {
|
|
|
7958
8597
|
...match ? { match } : void 0
|
|
7959
8598
|
}
|
|
7960
8599
|
};
|
|
7961
|
-
return eventAutomationToolResult(await createEventAutomation(db, task), catalog, actor.userId);
|
|
8600
|
+
return eventAutomationToolResult(context.conversationId, await createEventAutomation(db, task), catalog, actor.userId);
|
|
7962
8601
|
}
|
|
7963
8602
|
});
|
|
7964
8603
|
}
|
|
@@ -7977,13 +8616,24 @@ function createDeleteEventAutomationTool(context, catalog) {
|
|
|
7977
8616
|
executionMode: "sequential",
|
|
7978
8617
|
description: "Delete an event automation.",
|
|
7979
8618
|
inputSchema: z.object({ automationId: z.string().min(1) }).strict(),
|
|
7980
|
-
outputSchema: eventAutomationToolResultSchema.omit({
|
|
8619
|
+
outputSchema: eventAutomationToolResultSchema.omit({ objectCards: true }).extend({ removedCards: z.array(messageCardRefSchema) }),
|
|
7981
8620
|
async execute({ automationId }) {
|
|
7982
8621
|
const { actor } = requireEventAutomationSlackContext(context);
|
|
7983
8622
|
const current = await writableEventAutomation(context, automationId);
|
|
7984
8623
|
const deleted = await deleteEventAutomation(getDb(), current.id);
|
|
7985
8624
|
if (!deleted) throw new ToolInputError("Event automation was not found.");
|
|
7986
|
-
|
|
8625
|
+
await createPluginAnnotations({
|
|
8626
|
+
conversationId: context.conversationId,
|
|
8627
|
+
db: getDb(),
|
|
8628
|
+
plugin: "junior"
|
|
8629
|
+
}).remove("object", automationId);
|
|
8630
|
+
return {
|
|
8631
|
+
automation: compactEventAutomation(deleted, catalog, actor.userId),
|
|
8632
|
+
removedCards: [{
|
|
8633
|
+
plugin: "junior",
|
|
8634
|
+
key: automationId
|
|
8635
|
+
}]
|
|
8636
|
+
};
|
|
7987
8637
|
}
|
|
7988
8638
|
});
|
|
7989
8639
|
}
|
|
@@ -8088,7 +8738,7 @@ function createUpdateEventAutomationTool(context, catalog) {
|
|
|
8088
8738
|
}
|
|
8089
8739
|
const saved = await saveEventAutomation(getDb(), next);
|
|
8090
8740
|
if (!saved) throw new ToolInputError("Event automation was not found.");
|
|
8091
|
-
return eventAutomationToolResult(saved, catalog, actor.userId);
|
|
8741
|
+
return eventAutomationToolResult(context.conversationId, saved, catalog, actor.userId);
|
|
8092
8742
|
}
|
|
8093
8743
|
});
|
|
8094
8744
|
}
|
|
@@ -8674,7 +9324,7 @@ const compactTaskResultSchema = z.object({
|
|
|
8674
9324
|
}).strict();
|
|
8675
9325
|
const scheduleAutomationToolResultSchema = juniorToolOutputSchema.extend({
|
|
8676
9326
|
automation: compactTaskResultSchema,
|
|
8677
|
-
|
|
9327
|
+
objectCards: z.array(ownedObjectAnnotationSchema).optional()
|
|
8678
9328
|
}).strict();
|
|
8679
9329
|
const scheduleListToolResultSchema = juniorToolOutputSchema.extend({
|
|
8680
9330
|
automations: z.array(compactTaskResultSchema),
|
|
@@ -8792,23 +9442,27 @@ function compactTask(task, requesterSlackUserId) {
|
|
|
8792
9442
|
});
|
|
8793
9443
|
}
|
|
8794
9444
|
/** Build the structured result shared by single-task scheduler tools. */
|
|
8795
|
-
function scheduleAutomationToolResult(task, requesterSlackUserId) {
|
|
9445
|
+
async function scheduleAutomationToolResult(conversationId, task, requesterSlackUserId) {
|
|
8796
9446
|
const automation = compactTask(task, requesterSlackUserId);
|
|
9447
|
+
const title = automation.title ?? fallbackShortTitle(automation.instruction, "Scheduled automation");
|
|
9448
|
+
const annotation = {
|
|
9449
|
+
kind: "object",
|
|
9450
|
+
objectType: "automation",
|
|
9451
|
+
label: title,
|
|
9452
|
+
key: task.id,
|
|
9453
|
+
title,
|
|
9454
|
+
url: automation.dashboardUrl,
|
|
9455
|
+
description: automation.instruction,
|
|
9456
|
+
trigger: task.schedule.kind === "one_off" && task.nextRunAtMs !== void 0 ? `${new Intl.DateTimeFormat("en-US", {
|
|
9457
|
+
dateStyle: "medium",
|
|
9458
|
+
timeStyle: "short",
|
|
9459
|
+
timeZone: task.schedule.timezone
|
|
9460
|
+
}).format(task.nextRunAtMs)} · ${task.schedule.timezone}` : automation.schedule,
|
|
9461
|
+
warning: automation.status === "blocked" ? automation.statusReason ?? "This automation is blocked." : automation.status === "completed" ? "This automation has completed." : void 0
|
|
9462
|
+
};
|
|
8797
9463
|
return {
|
|
8798
9464
|
automation,
|
|
8799
|
-
|
|
8800
|
-
kind: "automation",
|
|
8801
|
-
id: task.id,
|
|
8802
|
-
title: automation.title ?? fallbackShortTitle(automation.instruction, "Scheduled automation"),
|
|
8803
|
-
url: automation.dashboardUrl,
|
|
8804
|
-
instruction: automation.instruction,
|
|
8805
|
-
trigger: task.schedule.kind === "one_off" && task.nextRunAtMs !== void 0 ? `${new Intl.DateTimeFormat("en-US", {
|
|
8806
|
-
dateStyle: "medium",
|
|
8807
|
-
timeStyle: "short",
|
|
8808
|
-
timeZone: task.schedule.timezone
|
|
8809
|
-
}).format(task.nextRunAtMs)} · ${task.schedule.timezone}` : automation.schedule,
|
|
8810
|
-
warning: automation.status === "blocked" ? automation.statusReason ?? "This automation is blocked." : automation.status === "completed" ? "This automation has completed." : null
|
|
8811
|
-
}]
|
|
9465
|
+
objectCards: await saveObjectAnnotations(conversationId, "junior", [annotation])
|
|
8812
9466
|
};
|
|
8813
9467
|
}
|
|
8814
9468
|
/** Build the structured result for listing scheduler tools. */
|
|
@@ -8879,7 +9533,7 @@ function createSlackScheduleCreateAutomationTool(context) {
|
|
|
8879
9533
|
const existing = await readScheduledAutomation(db, id);
|
|
8880
9534
|
if (existing) {
|
|
8881
9535
|
if (!sameDestination(existing, destination) || existing.createdBy.slackUserId !== actor.slackUserId) throwToolInputError("Scheduled automation operation identity is invalid.");
|
|
8882
|
-
return scheduleAutomationToolResult(existing, actor.slackUserId);
|
|
9536
|
+
return scheduleAutomationToolResult(context.conversationId, existing, actor.slackUserId);
|
|
8883
9537
|
}
|
|
8884
9538
|
const identity = (await context.users.resolveActor())?.identity;
|
|
8885
9539
|
if (!identity || identity.provider !== "slack" || identity.providerTenantId !== destination.teamId || identity.providerSubjectId !== actor.slackUserId) throwToolInputError("Scheduled automation creator identity is unavailable.");
|
|
@@ -8922,7 +9576,7 @@ function createSlackScheduleCreateAutomationTool(context) {
|
|
|
8922
9576
|
const committed = await createScheduledAutomation(db, task);
|
|
8923
9577
|
if (!sameDestination(committed, destination) || committed.createdBy.slackUserId !== actor.slackUserId) throwToolInputError("Scheduled automation operation identity is invalid.");
|
|
8924
9578
|
logInfo("scheduled_automation.create.completed", scheduledAutomationAttributes(committed));
|
|
8925
|
-
return scheduleAutomationToolResult(committed, actor.slackUserId);
|
|
9579
|
+
return scheduleAutomationToolResult(context.conversationId, committed, actor.slackUserId);
|
|
8926
9580
|
}
|
|
8927
9581
|
});
|
|
8928
9582
|
}
|
|
@@ -8941,7 +9595,7 @@ function createSlackScheduleDeleteAutomationTool(context) {
|
|
|
8941
9595
|
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.",
|
|
8942
9596
|
executionMode: "sequential",
|
|
8943
9597
|
inputSchema: z.object({ automationId: z.string().min(1).describe("ID of the task to delete. Must be from this active Slack conversation.") }),
|
|
8944
|
-
outputSchema: scheduleAutomationToolResultSchema,
|
|
9598
|
+
outputSchema: scheduleAutomationToolResultSchema.extend({ removedCards: z.array(messageCardRefSchema) }),
|
|
8945
9599
|
execute: async ({ automationId }) => {
|
|
8946
9600
|
const next = {
|
|
8947
9601
|
...await getWritableTask({
|
|
@@ -8954,7 +9608,18 @@ function createSlackScheduleDeleteAutomationTool(context) {
|
|
|
8954
9608
|
runNowAtMs: void 0
|
|
8955
9609
|
};
|
|
8956
9610
|
await saveScheduledAutomation(getDb(), next);
|
|
8957
|
-
|
|
9611
|
+
await createPluginAnnotations({
|
|
9612
|
+
conversationId: context.conversationId,
|
|
9613
|
+
db: getDb(),
|
|
9614
|
+
plugin: "junior"
|
|
9615
|
+
}).remove("object", automationId);
|
|
9616
|
+
return {
|
|
9617
|
+
automation: compactTask(next, context.actor?.userId),
|
|
9618
|
+
removedCards: [{
|
|
9619
|
+
plugin: "junior",
|
|
9620
|
+
key: automationId
|
|
9621
|
+
}]
|
|
9622
|
+
};
|
|
8958
9623
|
}
|
|
8959
9624
|
});
|
|
8960
9625
|
}
|
|
@@ -9170,10 +9835,10 @@ function createSlackScheduleUpdateAutomationTool(context) {
|
|
|
9170
9835
|
if (title) next.title = title;
|
|
9171
9836
|
else delete next.title;
|
|
9172
9837
|
}
|
|
9173
|
-
if (!changingDestination && !instructionChanged && !compiled && status === void 0 && (input.credentialMode === void 0 || input.credentialMode === null || input.credentialMode === lookup.credentialMode) && input.outcomes === void 0 && moveDestination) return scheduleAutomationToolResult(lookup, actor.slackUserId);
|
|
9838
|
+
if (!changingDestination && !instructionChanged && !compiled && status === void 0 && (input.credentialMode === void 0 || input.credentialMode === null || input.credentialMode === lookup.credentialMode) && input.outcomes === void 0 && moveDestination) return scheduleAutomationToolResult(context.conversationId, lookup, actor.slackUserId);
|
|
9174
9839
|
const committed = await saveScheduledAutomation(db, next);
|
|
9175
9840
|
if (changingDestination) logInfo("scheduled_automation.move.completed", scheduledAutomationAttributes(committed));
|
|
9176
|
-
return scheduleAutomationToolResult(committed, actor.slackUserId);
|
|
9841
|
+
return scheduleAutomationToolResult(context.conversationId, committed, actor.slackUserId);
|
|
9177
9842
|
}
|
|
9178
9843
|
});
|
|
9179
9844
|
}
|
|
@@ -9182,6 +9847,7 @@ function createSlackScheduleUpdateAutomationTool(context) {
|
|
|
9182
9847
|
function scheduledAutomationToolContext(context) {
|
|
9183
9848
|
if (context.source.kind !== "slack" || context.destination.platform !== "slack" || context.actor?.platform !== "slack" || !context.resolveActorIdentity) return;
|
|
9184
9849
|
return {
|
|
9850
|
+
conversationId: context.conversationId,
|
|
9185
9851
|
actor: context.actor,
|
|
9186
9852
|
source: context.source,
|
|
9187
9853
|
users: { resolveActor: context.resolveActorIdentity },
|
|
@@ -10185,13 +10851,12 @@ function createSendFilesTool(context, state, materializeFile, attachments) {
|
|
|
10185
10851
|
files: materializedFiles,
|
|
10186
10852
|
storage: attachments.storage
|
|
10187
10853
|
}) : [];
|
|
10188
|
-
const uploads = materializedFiles.map((file) => ({
|
|
10189
|
-
data: file.data,
|
|
10190
|
-
filename: file.filename
|
|
10191
|
-
}));
|
|
10192
10854
|
await uploadFilesToConversation({
|
|
10193
10855
|
channelId: activeChannelId,
|
|
10194
|
-
files:
|
|
10856
|
+
files: materializedFiles.map((file) => ({
|
|
10857
|
+
data: file.data,
|
|
10858
|
+
filename: file.filename
|
|
10859
|
+
})),
|
|
10195
10860
|
...threadTs ? { threadTs } : void 0
|
|
10196
10861
|
});
|
|
10197
10862
|
const delivered = stored.map((attachment, index) => {
|
|
@@ -13767,10 +14432,51 @@ function buildSandboxInput(toolName, params) {
|
|
|
13767
14432
|
return params;
|
|
13768
14433
|
}
|
|
13769
14434
|
//#endregion
|
|
13770
|
-
//#region src/chat/
|
|
13771
|
-
|
|
13772
|
-
|
|
13773
|
-
|
|
14435
|
+
//#region src/chat/services/pending-auth.ts
|
|
14436
|
+
const AUTH_LINK_REUSE_WINDOW_MS = 6e5;
|
|
14437
|
+
const NON_REQUEST_SKIPPED_REASON_PREFIXES = ["directed_to_other_party:", "side_conversation:"];
|
|
14438
|
+
function isSkippedNonRequest(reason) {
|
|
14439
|
+
return NON_REQUEST_SKIPPED_REASON_PREFIXES.some((prefix) => reason?.startsWith(prefix));
|
|
14440
|
+
}
|
|
14441
|
+
/** Decide whether the same agent-run session can reuse its fresh auth link. */
|
|
14442
|
+
function canReusePendingAuthLink(args) {
|
|
14443
|
+
const { pendingAuth } = args;
|
|
14444
|
+
if (!pendingAuth) return false;
|
|
14445
|
+
return pendingAuth.kind === args.kind && pendingAuth.provider === args.provider && pendingAuth.actorId === args.actorId && pendingAuth.scope === args.scope && pendingAuth.sessionId === args.sessionId && pendingAuth.linkSentAtMs + AUTH_LINK_REUSE_WINDOW_MS > (args.nowMs ?? Date.now());
|
|
14446
|
+
}
|
|
14447
|
+
function getConversationPendingAuth(args) {
|
|
14448
|
+
const pendingAuth = args.conversation.processing.pendingAuth;
|
|
14449
|
+
if (!pendingAuth) return;
|
|
14450
|
+
if (pendingAuth.kind !== args.kind || pendingAuth.provider !== args.provider || pendingAuth.actorId !== args.actorId || pendingAuth.scope !== args.scope) return;
|
|
14451
|
+
return pendingAuth;
|
|
14452
|
+
}
|
|
14453
|
+
function clearPendingAuth(conversation, sessionId) {
|
|
14454
|
+
if (!conversation.processing.pendingAuth) return;
|
|
14455
|
+
if (sessionId && conversation.processing.pendingAuth.sessionId !== sessionId) return;
|
|
14456
|
+
conversation.processing.pendingAuth = void 0;
|
|
14457
|
+
}
|
|
14458
|
+
/** Mark the prior blocked turn abandoned after a new auth attempt replaces it. */
|
|
14459
|
+
async function abandonReplacedPendingAuth(args) {
|
|
14460
|
+
if (args.previousPendingAuth && args.previousPendingAuth.sessionId !== args.nextPendingAuth.sessionId && args.conversationId) await abandonTurnRecord({
|
|
14461
|
+
conversationId: args.conversationId,
|
|
14462
|
+
turnId: args.previousPendingAuth.sessionId,
|
|
14463
|
+
errorMessage: "Abandoned by a newer auth-blocked request in the same conversation."
|
|
14464
|
+
});
|
|
14465
|
+
}
|
|
14466
|
+
/**
|
|
14467
|
+
* Decide whether an auth callback still belongs to the latest real human
|
|
14468
|
+
* request, ignoring bot-authored and passive bystander rows while treating
|
|
14469
|
+
* failed human turns and opt-outs as newer freshness blockers.
|
|
14470
|
+
*/
|
|
14471
|
+
function isPendingAuthLatestRequest(conversation, pendingAuth) {
|
|
14472
|
+
for (let index = conversation.messages.length - 1; index >= 0; index -= 1) {
|
|
14473
|
+
const message = conversation.messages[index];
|
|
14474
|
+
if (message?.role !== "user") continue;
|
|
14475
|
+
if (message.author?.isBot) continue;
|
|
14476
|
+
if (isSkippedNonRequest(message.meta?.skippedReason)) continue;
|
|
14477
|
+
return buildDeterministicTurnId(message.id) === pendingAuth.sessionId;
|
|
14478
|
+
}
|
|
14479
|
+
return false;
|
|
13774
14480
|
}
|
|
13775
14481
|
//#endregion
|
|
13776
14482
|
//#region src/chat/services/plugin-auth-orchestration.ts
|
|
@@ -15216,12 +15922,11 @@ function createMcpAuthOrchestration(input) {
|
|
|
15216
15922
|
const authSession = await getMcpAuthSession(authSessionId);
|
|
15217
15923
|
if (!authSession?.authorizationUrl) throw new Error(`Missing MCP authorization URL for plugin "${provider}"`);
|
|
15218
15924
|
await recordPendingAuth(nextPendingAuth);
|
|
15219
|
-
|
|
15925
|
+
if (!await deliverOAuthAuthorization({
|
|
15220
15926
|
authorizationUrl: authSession.authorizationUrl,
|
|
15221
|
-
label: `
|
|
15927
|
+
label: `Connect to ${providerLabel}`,
|
|
15222
15928
|
completionText: "Once you've authorized, Junior will continue automatically."
|
|
15223
|
-
}
|
|
15224
|
-
if (!await deliverOAuthAuthorization(authorizationRequest, {
|
|
15929
|
+
}, {
|
|
15225
15930
|
authorization: input.authorization,
|
|
15226
15931
|
channelId: authSession.channelId,
|
|
15227
15932
|
threadTs: authSession.threadTs,
|
|
@@ -16395,7 +17100,7 @@ async function wireAgentTools(args) {
|
|
|
16395
17100
|
authProviderFactory: mcpAuth.authProviderFactory,
|
|
16396
17101
|
onAuthorizationRequired: mcpAuth.onAuthorizationRequired,
|
|
16397
17102
|
onToolSuccess: async (input) => {
|
|
16398
|
-
await pluginHooks.afterMcpTool({
|
|
17103
|
+
return await pluginHooks.afterMcpTool({
|
|
16399
17104
|
...input,
|
|
16400
17105
|
conversationId: args.run.conversationId
|
|
16401
17106
|
});
|
|
@@ -17362,7 +18067,7 @@ function applyHandoff(args) {
|
|
|
17362
18067
|
* assistant message through the delivery port; the result carries diagnostics,
|
|
17363
18068
|
* artifacts, and transcript state for destination-owned completion.
|
|
17364
18069
|
*/
|
|
17365
|
-
const AGENT_ABORT_SETTLE_GRACE_MS = 5e3;
|
|
18070
|
+
const AGENT_ABORT_SETTLE_GRACE_MS$1 = 5e3;
|
|
17366
18071
|
async function readRunConversationVisibility(conversationId) {
|
|
17367
18072
|
const conversationStore = getConversationStore();
|
|
17368
18073
|
const conversation = await conversationStore.get({ conversationId });
|
|
@@ -18091,7 +18796,7 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
|
|
|
18091
18796
|
"app.ai.turn_timeout_ms": turnTimeoutBudgetMs,
|
|
18092
18797
|
"app.ai.turn_deadline_remaining_ms": Math.max(0, turnDeadlineAtMs - Date.now())
|
|
18093
18798
|
});
|
|
18094
|
-
if (!await waitForAbortSettlement(run, AGENT_ABORT_SETTLE_GRACE_MS)) logWarn("agent.turn.abort_settle.timed_out", { "app.ai.abort_settle_grace_ms": AGENT_ABORT_SETTLE_GRACE_MS });
|
|
18799
|
+
if (!await waitForAbortSettlement(run, AGENT_ABORT_SETTLE_GRACE_MS$1)) logWarn("agent.turn.abort_settle.timed_out", { "app.ai.abort_settle_grace_ms": AGENT_ABORT_SETTLE_GRACE_MS$1 });
|
|
18095
18800
|
runResume.captureResumeSnapshot(runResume.getResumeSnapshot(currentAgentMessages()));
|
|
18096
18801
|
}
|
|
18097
18802
|
const pendingAuthPause = getPendingAuthPause();
|
|
@@ -18313,4 +19018,109 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
|
|
|
18313
19018
|
}
|
|
18314
19019
|
}
|
|
18315
19020
|
//#endregion
|
|
18316
|
-
|
|
19021
|
+
//#region src/chat/experimental.ts
|
|
19022
|
+
var experimental_exports = /* @__PURE__ */ __exportAll({
|
|
19023
|
+
EXPERIMENTAL_FEATURES: () => EXPERIMENTAL_FEATURES,
|
|
19024
|
+
getExperimentalFeatures: () => getExperimentalFeatures,
|
|
19025
|
+
isExperimentalFeatureEnabled: () => isExperimentalFeatureEnabled,
|
|
19026
|
+
setExperimentalFeatures: () => setExperimentalFeatures
|
|
19027
|
+
});
|
|
19028
|
+
/**
|
|
19029
|
+
* Known unstable product features that must be opted into explicitly.
|
|
19030
|
+
* Add new keys here as features graduate from private experiments; remove them
|
|
19031
|
+
* once they become stable defaults.
|
|
19032
|
+
*/
|
|
19033
|
+
const EXPERIMENTAL_FEATURES = ["passive-routing", "subagents"];
|
|
19034
|
+
const EXPERIMENTAL_FEATURE_SET = new Set(EXPERIMENTAL_FEATURES);
|
|
19035
|
+
let configuredExperimental = {};
|
|
19036
|
+
function isExperimentalFeature(value) {
|
|
19037
|
+
return EXPERIMENTAL_FEATURE_SET.has(value);
|
|
19038
|
+
}
|
|
19039
|
+
/** Replace app-level experimental opt-ins and return the previous setting. */
|
|
19040
|
+
function setExperimentalFeatures(config) {
|
|
19041
|
+
const previous = { ...configuredExperimental };
|
|
19042
|
+
if (config === void 0) {
|
|
19043
|
+
configuredExperimental = {};
|
|
19044
|
+
return previous;
|
|
19045
|
+
}
|
|
19046
|
+
const next = {};
|
|
19047
|
+
for (const [rawName, enabled] of Object.entries(config)) {
|
|
19048
|
+
if (enabled === void 0) continue;
|
|
19049
|
+
if (typeof enabled !== "boolean") throw new Error(`experimental.${rawName} must be a boolean when provided`);
|
|
19050
|
+
if (!isExperimentalFeature(rawName)) throw new Error(`experimental.${rawName} is not a known experimental feature. Known features: ${EXPERIMENTAL_FEATURES.join(", ")}`);
|
|
19051
|
+
next[rawName] = enabled;
|
|
19052
|
+
}
|
|
19053
|
+
configuredExperimental = next;
|
|
19054
|
+
return previous;
|
|
19055
|
+
}
|
|
19056
|
+
/** Return a copy of the app-level experimental opt-ins configured by createApp(). */
|
|
19057
|
+
function getExperimentalFeatures() {
|
|
19058
|
+
return { ...configuredExperimental };
|
|
19059
|
+
}
|
|
19060
|
+
/** Return whether one experimental feature is enabled via createApp({ experimental }). */
|
|
19061
|
+
function isExperimentalFeatureEnabled(feature) {
|
|
19062
|
+
return configuredExperimental[feature] === true;
|
|
19063
|
+
}
|
|
19064
|
+
//#endregion
|
|
19065
|
+
//#region src/chat/runtime/agent-runner.ts
|
|
19066
|
+
const AGENT_ABORT_SETTLE_GRACE_MS = 5e3;
|
|
19067
|
+
/** Compose the agent executor with stable host dependencies. */
|
|
19068
|
+
function createAgentRunner(execute, options) {
|
|
19069
|
+
const attachmentStorage = options?.attachmentStorage;
|
|
19070
|
+
const streamFn = options?.streamFn;
|
|
19071
|
+
const tracePropagation = options?.tracePropagation;
|
|
19072
|
+
const bindSpawnAgent = options?.bindSpawnAgent;
|
|
19073
|
+
const canBindSpawn = Boolean(bindSpawnAgent) && isExperimentalFeatureEnabled("subagents");
|
|
19074
|
+
return { run: async (run) => {
|
|
19075
|
+
const spawnAgent = bindSpawnAgent && canBindSpawn && !isAgentRunFeatureDisabled(run.disabledFeatures, "subagents") ? bindSpawnAgent(run) : void 0;
|
|
19076
|
+
return await execute({
|
|
19077
|
+
...run,
|
|
19078
|
+
environment: {
|
|
19079
|
+
...run.environment,
|
|
19080
|
+
attachmentStorage: run.environment?.attachmentStorage ?? attachmentStorage,
|
|
19081
|
+
sandboxTracePropagation: run.environment?.sandboxTracePropagation ?? tracePropagation
|
|
19082
|
+
},
|
|
19083
|
+
...spawnAgent ? { durability: {
|
|
19084
|
+
...run.durability,
|
|
19085
|
+
spawnAgent
|
|
19086
|
+
} } : void 0
|
|
19087
|
+
}, streamFn);
|
|
19088
|
+
} };
|
|
19089
|
+
}
|
|
19090
|
+
async function waitForAgentSettlement(runPromise) {
|
|
19091
|
+
let timeoutId;
|
|
19092
|
+
const settleGrace = new Promise((resolve) => {
|
|
19093
|
+
timeoutId = setTimeout(resolve, AGENT_ABORT_SETTLE_GRACE_MS);
|
|
19094
|
+
timeoutId.unref?.();
|
|
19095
|
+
});
|
|
19096
|
+
await Promise.race([runPromise.then(() => void 0, () => void 0), settleGrace]);
|
|
19097
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
19098
|
+
}
|
|
19099
|
+
/** Abort a timed-out agent run and wait briefly for its work to settle. */
|
|
19100
|
+
async function runAgentWithTimeout(agentRunner, run, timeoutMs) {
|
|
19101
|
+
if (typeof timeoutMs !== "number") return await agentRunner.run(run);
|
|
19102
|
+
const timeoutError = /* @__PURE__ */ new Error(`executeAgentRun timed out after ${timeoutMs}ms`);
|
|
19103
|
+
const abortController = new AbortController();
|
|
19104
|
+
const signal = run.signal ? AbortSignal.any([run.signal, abortController.signal]) : abortController.signal;
|
|
19105
|
+
const runPromise = agentRunner.run({
|
|
19106
|
+
...run,
|
|
19107
|
+
signal
|
|
19108
|
+
});
|
|
19109
|
+
let timeoutId;
|
|
19110
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
19111
|
+
timeoutId = setTimeout(() => {
|
|
19112
|
+
abortController.abort(timeoutError);
|
|
19113
|
+
reject(timeoutError);
|
|
19114
|
+
}, timeoutMs);
|
|
19115
|
+
});
|
|
19116
|
+
try {
|
|
19117
|
+
return await Promise.race([runPromise, timeoutPromise]);
|
|
19118
|
+
} catch (error) {
|
|
19119
|
+
if (abortController.signal.aborted) await waitForAgentSettlement(runPromise);
|
|
19120
|
+
throw error;
|
|
19121
|
+
} finally {
|
|
19122
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
19123
|
+
}
|
|
19124
|
+
}
|
|
19125
|
+
//#endregion
|
|
19126
|
+
export { clearSandboxEgressSignals as $, renderAttachmentText as A, AgentInvocationLimitError as B, resolvePluginOAuthAccount as C, shouldEmitDevAgentTrace as Ct, lookupSlackUserProfile as D, unpublishedArtifactHeaders as E, getNextRunAtMs as F, isEventMailboxMetadata as G, processWorkspaceSnapshotJob as H, agentBindingSchema as I, recordFinishedTurnForAutomatedLimit as J, renderTaskInput as K, agentInvocationSchema as L, listThreadReplies as M, logScheduledAutomationRunSkipped as N, readAttachmentVisionSummary as O, scheduledAutomationRunAttributes as P, SANDBOX_EGRESS_PROXY_PATH as Q, createAgentInvocationSchema as R, latestProgressStatus as S, AuthorizationFlowDisabledError as St, readPublicArtifact as T, setConfigDefaults as Tt, ingestEvent as U, getMcpProviderErrorAttributes as V, EVENT_WAIT_MS as W, normalizeSandboxEgressTracePropagationDomains as X, resolveSandboxEgressProviderForHost as Y, shouldPropagateSandboxEgressTrace as Z, putMcpStoredOAuthCredentials as _, buildSteeringPiMessage as _t, isExperimentalFeatureEnabled as a, parseOAuthStatePayload as at, getConversationPendingAuth as b, actorFromRun as bt, createContextCompactor as c, escapeSlackMrkdwnText as ct, deleteMcpAuthSession as d, normalizeSlackStatusText as dt, consumeSandboxEgressAuthRequiredSignal as et, deleteMcpAuthSessionsForUserProvider as f, truncateStatusText as ft, getMcpStoredOAuthCredentials as g, createUserTokenStore as gt, getMcpAuthSession as h, removeReactionFromMessage as ht, getExperimentalFeatures as i, formatProviderLabel as it, renderBlockText as j, recordSlackAttachment as k, executeCredentialedEgressRequest as l, formatSlackLink as lt, deleteMcpStoredOAuthCredentials as m, postSlackMessage as mt, runAgentWithTimeout as n, parseSandboxEgressCredentialToken as nt, setExperimentalFeatures as o, resolveBaseUrl as ot, deleteMcpServerSessionId as p, addReactionToMessage as pt, maybePostAutomatedTurnLimitNotice as q, experimental_exports as r, sandboxEgressSignalsResponseSchema as rt, executeAgentRun as s, startOAuthFlow as st, createAgentRunner as t, consumeSandboxEgressPermissionDeniedSignal as tt, finalizeMcpAuthorization as u, normalizeSlackReplyMarkdown as ut, PluginCredentialFailureError as v, isVisionImageMediaType as vt, publicArtifactHeaders as w, getConfigDefaults as wt, isPendingAuthLatestRequest as x, toolInvocationDestination as xt, clearPendingAuth as y, RetryableDeliveryError as yt, AgentInvocationBusyError as z };
|