@sentry/junior 0.235.0 → 0.237.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-DS7k0ZWY.js → agent-hooks-otHGbhBx.js} +20 -9
- package/dist/{agent-runner-BlDI1zV2.js → agent-runner-DMAsnPb9.js} +456 -120
- package/dist/api/schema.d.ts +223 -2
- package/dist/api/schema.js +5 -4
- package/dist/api.d.ts +8 -2
- package/dist/api.js +404 -47
- package/dist/{app-pGsR__aT.d.ts → app-MEd7wbF0.d.ts} +82 -48
- package/dist/app.d.ts +3 -3
- package/dist/app.js +162 -119
- package/dist/{types-Du-HbEun.js → automation-CrQGQ2MT.js} +306 -34
- package/dist/{briefs-C3jhszOz.js → briefs-DDA1ugXk.js} +4 -4
- package/dist/{catalog-runtime-B9u_3-GW.js → catalog-runtime-DlXuhrmk.js} +1 -1
- package/dist/{checkpoint-DLcJ5mKL.js → checkpoint-BlIIiHG5.js} +16 -9
- package/dist/cli/chat.d.ts +1 -1
- package/dist/cli/chat.js +14 -14
- package/dist/cli/check.js +4 -4
- package/dist/cli/main.js +1 -1
- package/dist/cli/plugins.d.ts +1 -1
- package/dist/cli/plugins.js +4 -4
- package/dist/cli/snapshot-create.js +3 -3
- package/dist/cli/upgrade.d.ts +1 -1
- package/dist/cli/upgrade.js +5 -5
- package/dist/{client-BSnNHO0R.js → client-CPMbTmJU.js} +2 -2
- package/dist/{config-Bmw4xTPK.js → config-CxpPG6nR.js} +57 -17
- package/dist/{conversation-C_aYrISI.js → conversation-9Fxa1Zbq.js} +13 -1
- package/dist/{conversation-privacy-BV2dtba2.js → conversation-privacy-Dbkdvozu.js} +1 -1
- package/dist/{credential-sync-CukzQJ1x.js → credential-sync-D9yGg3q5.js} +14 -14
- package/dist/{executor-B9ksWZCv.js → executor-BNxfDS1V.js} +39 -6
- package/dist/{images-CEXcQ0Id.js → images-D6Y3FJDQ.js} +3 -3
- package/dist/instrumentation.js +2 -2
- package/dist/{job-queue-CY1L2YRn.js → job-queue-Dq5_zt_l.js} +1 -1
- package/dist/{logging-F--48Q7Q.js → logging-rB_ngE_R.js} +1 -1
- package/dist/nitro.d.ts +2 -2
- package/dist/nitro.js +5 -5
- package/dist/{oauth-authorization-LasSG3Qc.js → oauth-authorization-5T1S1v_1.js} +1 -1
- package/dist/{oauth-callback-server-D07ZzVMs.js → oauth-callback-server-Csw7RssK.js} +2 -2
- package/dist/{pending-auth-DrG9Mkje.js → pending-auth-moCA2mTc.js} +1 -1
- package/dist/{plugins-BT2l4We3.d.ts → plugins-CjfUP5gJ.d.ts} +1 -0
- package/dist/{profile-DZ2DUdd1.js → profile-D6apIG2N.js} +4 -4
- package/dist/{prompt-BM_5mAU3.js → prompt-Ctwy6hSI.js} +1 -1
- package/dist/queue-B5SnqRln.d.ts +25 -0
- package/dist/{registry-_JLY0x7b.js → registry-zfTzTHe6.js} +38 -2
- package/dist/{resolve-Cuoj3MML.js → resolve-sJtTzuBO.js} +2 -2
- package/dist/{runner-BfelmbMv.js → runner-DkCk8cbc.js} +9 -9
- package/dist/{sandbox-egress-signals-D5QUIO7Z.js → sandbox-egress-signals-tSQ8OEcw.js} +1 -1
- package/dist/{schema-CrdDxGbx.js → schema-C5bgOpRg.js} +3 -2
- package/dist/{skills-DAegjk5Y.js → skills-BVpr_Gnv.js} +3 -3
- package/dist/{spawn-DCfV7zGK.js → spawn-CJhmyE4E.js} +2 -2
- package/dist/{task-queue-GcuFtzor.js → task-queue-BAiZ7_CF.js} +16 -10
- package/dist/{task-runner-B0-P_3aq.js → task-runner-Bh8XONXT.js} +9 -9
- package/dist/{thread-state-TezcEhuH.js → thread-state-BnQln4jO.js} +5 -5
- package/dist/{turn-failure-response-Drhj_-t4.js → turn-failure-response-Ba7_kItn.js} +8 -8
- package/dist/{turn-lifecycle-BpIW3zOx.js → turn-lifecycle-D6CTbkb9.js} +1 -1
- package/dist/{turn-session-routing-Dg8GDgyc.js → turn-session-routing-DNevs9H7.js} +1 -1
- package/dist/{validation-DAkYeKDT.js → validation-BNPhuAvt.js} +2 -3
- package/dist/{version-9CB4Z-J4.js → version-EQ4MWuUJ.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/{web-authorization-Cu33voNg.js → web-authorization-DDt4FZld.js} +6 -6
- package/dist/{web-input-B3k0J7WB.js → web-input-C56ivZuH.js} +86 -102
- package/dist/web-message-id-xPI_EFSC.js +239 -0
- package/dist/{work-PmkvcY9l.js → work-DJ7hb9Ul.js} +7 -7
- package/dist/{worker-COcaq2J6.js → worker-DiiV8Lba.js} +5 -5
- package/migrations/0045_conversation_forks.sql +3 -0
- package/migrations/0046_automation_versions.sql +71 -0
- package/migrations/0047_user_timezone.sql +1 -0
- package/migrations/meta/0045_snapshot.json +3503 -0
- package/migrations/meta/0046_snapshot.json +3707 -0
- package/migrations/meta/0047_snapshot.json +3713 -0
- package/migrations/meta/_journal.json +22 -1
- package/package.json +4 -4
- package/dist/web-message-id-CVVCTWUb.js +0 -464
- package/dist/{package-discovery-7a9YqLhE.js → package-discovery-BVvSnQBB.js} +1 -1
- package/dist/{plugin-module-BKGOXca2.js → plugin-module-CVvEUSMH.js} +1 -1
|
@@ -1,27 +1,27 @@
|
|
|
1
1
|
import { t as __exportAll } from "./rolldown-runtime-B4FMCO8f.js";
|
|
2
|
-
import { b as getGatewayApiKey, c as hasCompactedConversationContext, d as unwrapCurrentInstruction, f as escapeXml, g as getProviderErrorAttributes, h as findProviderError, i as completeText, l as isCompactionSummaryText, m as createProviderError, n as GEN_AI_SERVER_ADDRESS, o as resolveGatewayModel, p as ProviderError, r as completeObject, s as COMPACTION_SUMMARY_PREFIX, t as GEN_AI_PROVIDER_NAME, u as renderCurrentInstruction, v as isProviderRetryError, x as resolveGatewayCredential, y as MISSING_GATEWAY_CREDENTIALS_ERROR } from "./client-
|
|
3
|
-
import { C as withLogContext, F as normalizeIdentityEmail, I as isRecord$3, L as toOptionalNumber, R as toOptionalString, a as extractGenAiUsageSummary, b as setTags, c as getLogContextAttributesForSpan, d as logException, f as logInfo, g as serializeGenAiAttribute, i as extractGenAiUsageAttributes, l as getTracePropagationHeaders, m as normalizeGenAiFinishReason, p as logWarn, s as getLogContextAttributes, u as logError, v as setSpanAttributes, w as withSpan, x as summarizeMessageText, y as setSpanStatus } from "./logging-
|
|
2
|
+
import { b as getGatewayApiKey, c as hasCompactedConversationContext, d as unwrapCurrentInstruction, f as escapeXml, g as getProviderErrorAttributes, h as findProviderError, i as completeText, l as isCompactionSummaryText, m as createProviderError, n as GEN_AI_SERVER_ADDRESS, o as resolveGatewayModel, p as ProviderError, r as completeObject, s as COMPACTION_SUMMARY_PREFIX, t as GEN_AI_PROVIDER_NAME, u as renderCurrentInstruction, v as isProviderRetryError, x as resolveGatewayCredential, y as MISSING_GATEWAY_CREDENTIALS_ERROR } from "./client-CPMbTmJU.js";
|
|
3
|
+
import { C as withLogContext, F as normalizeIdentityEmail, I as isRecord$3, L as toOptionalNumber, R as toOptionalString, a as extractGenAiUsageSummary, b as setTags, c as getLogContextAttributesForSpan, d as logException, f as logInfo, g as serializeGenAiAttribute, i as extractGenAiUsageAttributes, l as getTracePropagationHeaders, m as normalizeGenAiFinishReason, p as logWarn, s as getLogContextAttributes, u as logError, v as setSpanAttributes, w as withSpan, x as summarizeMessageText, y as setSpanStatus } from "./logging-rB_ngE_R.js";
|
|
4
4
|
import { m as startInactiveSpan, n as captureMessage, v as withActiveSpan } from "./sentry-CgWZM_kV.js";
|
|
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-
|
|
6
|
-
import {
|
|
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-Dbkdvozu.js";
|
|
6
|
+
import { i as getChatConfig, n as botConfig, o as getSlackBotToken, v as normalizeSlackEmojiName } from "./config-CxpPG6nR.js";
|
|
7
7
|
import { a as formatModelProfiles, c as profileConfig, i as formatModelProfile, l as TURN_REASONING_LEVELS, n as ModelProfileNotConfiguredError, o as modelIdForProfile, s as modelProfileSchema } from "./model-profile-BwU8mqwD.js";
|
|
8
8
|
import { b as parseSlackMessageTs, d as parseStoredSlackActor, 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-0Et9iVSB.js";
|
|
9
|
-
import { $ as readActorIdentity, $t as getConversationMessageSearchStore, A as getWorkspaceSnapshotBuilder, B as SlackActionError, C as WorkspaceValidationError, D as setWorkspaceSnapshotBuild, E as setWorkspaceSnapshot, G as isConversationScopedChannel, H as getFilePermalink, K as isDmChannel$1, M as workspaceRepoCheckoutPath, N as resolveChannelCapabilities, O as SANDBOX_RUNTIME, Qt as getConversationEventStore, S as updateWorkspace, T as loadSnapshotsForProfile, Ut as saveObjectAnnotations, V as downloadPrivateSlackFile, W as getSlackClient, Wt as createPluginAnnotations, Y as withSlackRetries, _ as deleteWorkspace, _t as getDashboardConversationLink, at as RESOURCE_WATCH_TOOL_SOURCE, b as listWorkspaces, c as getPluginTools, ct as cancelWatch, dt as createWatch, en as getConversationStore, ft as deliverWatch, g as createWorkspace, gt as JUNIOR_THREAD_STATE_TTL_MS, it as RESOURCE_SUBSCRIPTION_MAX_TTL_MS, j as tryWorkspaceRepoCheckoutPath, k as deleteWorkspaceSnapshotBuilders, l as getPluginUserPromptContributions, ln as addAgentTurnUsage, mt as listWatches, n as applyPluginFormatMarkdown, nn as getSqlExecutor, nt as getSlackToolContext, ot as STOP_WATCHING_TOOL_NAME, pn as hasAgentTurnUsage, pt as findMatchingWatches, q as normalizeSlackConversationId, r as createPluginHookRunner, rt as RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS, s as getPluginSystemPromptContributions, st as cancelSubscriptions, tn as getDb, u as getPlugins, ut as createTimerWatch, v as getWorkspace, w as invalidateReadySnapshot, wt as createPluginLogger, xn as
|
|
9
|
+
import { $ as readActorIdentity, $t as getConversationMessageSearchStore, A as getWorkspaceSnapshotBuilder, B as SlackActionError, C as WorkspaceValidationError, Cn as sameDestination$1, D as setWorkspaceSnapshotBuild, E as setWorkspaceSnapshot, G as isConversationScopedChannel, H as getFilePermalink, K as isDmChannel$1, M as workspaceRepoCheckoutPath, N as resolveChannelCapabilities, O as SANDBOX_RUNTIME, Qt as getConversationEventStore, S as updateWorkspace, T as loadSnapshotsForProfile, Ut as saveObjectAnnotations, V as downloadPrivateSlackFile, W as getSlackClient, Wt as createPluginAnnotations, Y as withSlackRetries, _ as deleteWorkspace, _n as saveUserTimezone, _t as getDashboardConversationLink, at as RESOURCE_WATCH_TOOL_SOURCE, b as listWorkspaces, c as getPluginTools, ct as cancelWatch, dt as createWatch, en as getConversationStore, ft as deliverWatch, g as createWorkspace, gn as readUserTimezone, gt as JUNIOR_THREAD_STATE_TTL_MS, it as RESOURCE_SUBSCRIPTION_MAX_TTL_MS, j as tryWorkspaceRepoCheckoutPath, k as deleteWorkspaceSnapshotBuilders, l as getPluginUserPromptContributions, ln as addAgentTurnUsage, mt as listWatches, n as applyPluginFormatMarkdown, nn as getSqlExecutor, nt as getSlackToolContext, ot as STOP_WATCHING_TOOL_NAME, pn as hasAgentTurnUsage, pt as findMatchingWatches, q as normalizeSlackConversationId, r as createPluginHookRunner, rt as RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS, s as getPluginSystemPromptContributions, st as cancelSubscriptions, tn as getDb, u as getPlugins, ut as createTimerWatch, v as getWorkspace, w as invalidateReadySnapshot, wt as createPluginLogger, xn as parseDestination, y as getWorkspaceByName, yt as getDashboardTaskLink, zt as matchSlackAttachments } from "./agent-hooks-otHGbhBx.js";
|
|
10
10
|
import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
|
|
11
|
-
import {
|
|
12
|
-
import { F as
|
|
13
|
-
import { a as listReferenceFiles, c as soulPathCandidates, l as worldPathCandidates } from "./package-discovery-
|
|
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-
|
|
15
|
-
import { n as pluginCatalogRuntime } from "./catalog-runtime-
|
|
16
|
-
import { a as parseSkillInvocation, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-
|
|
17
|
-
import {
|
|
18
|
-
import { A as SANDBOX_DATA_ROOT, C as getDefaultRedisStateAdapterFor, 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$2, 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 { n as canReusePendingAuthLink, t as abandonReplacedPendingAuth } from "./pending-auth-
|
|
20
|
-
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-
|
|
21
|
-
import { r as EVENT_AUTHOR_ID } from "./prompt-
|
|
22
|
-
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-
|
|
23
|
-
import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-
|
|
24
|
-
import {
|
|
11
|
+
import { A as juniorArtifacts, D as juniorConversations, O as juniorIdentities, S as juniorConversationAnnotations, T as juniorAttachments, _ as juniorConversationBriefs, a as juniorSchedulerRuns, f as AGENT_INVOCATION_MAILBOX_STATUSES, l as juniorStats, o as juniorSchedulerTasks, s as juniorAutomationVersions, u as juniorEventAutomations } from "./executor-BNxfDS1V.js";
|
|
12
|
+
import { F as isVisionImageMediaType, G as removedCardSchema, L as briefLinkSchema, R as briefOutcomeStatusSchema, at as sameActorIdentity, it as instructionProvenanceFor, nt as contextProvenance, q as KNOWN_CONVERSATION_EVENT_TYPES, rt as instructionActors, z as conversationBriefSchema } from "./conversation-9Fxa1Zbq.js";
|
|
13
|
+
import { a as listReferenceFiles, c as soulPathCandidates, l as worldPathCandidates } from "./package-discovery-BVvSnQBB.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-zfTzTHe6.js";
|
|
15
|
+
import { n as pluginCatalogRuntime } from "./catalog-runtime-DlXuhrmk.js";
|
|
16
|
+
import { a as parseSkillInvocation, i as parseSkillFile, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-BVpr_Gnv.js";
|
|
17
|
+
import { C as isToolResultError, D as stripRuntimeTurnContext, E as retainRuntimeTurnContext, H as recordAuthorizationRequested, I as loadTurnRoute, K as recordToolExecutionStarted, L as openConversationProjection, M as loadConnectedMcpProviders, N as loadConversationProjection, O as trimTrailingAssistantMessages, R as recordAgentsInstructionsUpdated, S as isContinuablePiBoundary, T as normalizeToolNameFromResult, U as recordGuardianActionReviewed, W as recordMcpProviderConnected, _ as getTerminalAssistantMessages, _t as historyItemFromPiMessage, d as TurnSliceLimitExceededError, f as assertTurnToolCallLimit, g as getPiMessageRole, gt as getAgentContextInputLimitTokens, h as extractAssistantText, ht as getAgentContextCompactionTriggerTokens, k as TURN_CONTEXT_TAG, m as isTurnExecutionLimitExceededError, mt as estimateTextTokens, n as loadTurnCheckpoint, nt as resolveTaskTitle, q as recordTurnRoute, r as saveTurnCheckpoint, t as continuableMessages, tt as fallbackShortTitle, v as getUserMessageInstructionText, vt as piMessageFromHistoryItem, w as isToolResultMessage, x as isAssistantMessage, y as hasRuntimeTurnContext, z as recordAttachmentsDelivered } from "./checkpoint-BlIIiHG5.js";
|
|
18
|
+
import { A as SANDBOX_DATA_ROOT, C as getDefaultRedisStateAdapterFor, 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$2, 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-D6apIG2N.js";
|
|
19
|
+
import { n as canReusePendingAuthLink, t as abandonReplacedPendingAuth } from "./pending-auth-moCA2mTc.js";
|
|
20
|
+
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-5T1S1v_1.js";
|
|
21
|
+
import { r as EVENT_AUTHOR_ID } from "./prompt-Ctwy6hSI.js";
|
|
22
|
+
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-sJtTzuBO.js";
|
|
23
|
+
import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-Dq5_zt_l.js";
|
|
24
|
+
import { A as eventAutomationSchema, D as scheduledRunSchema, E as scheduledAutomationSchema, M as compileScheduleIntent, N as isValidTimeZone$1, O as automationInstructionToolSchema, P as scheduleIntentSchema, R as getZonedDateTimeParts, S as automationVersionSchema, T as SCHEDULED_AUTOMATION_SYSTEM_ACTOR, j as ScheduleIntentError, k as automationTitleSchema, z as taskOutcomeInputSchema } from "./automation-CrQGQ2MT.js";
|
|
25
25
|
import { n as privateTraceResultAttributes } from "./private-trace-result-BLvBjy_e.js";
|
|
26
26
|
import { streamSimple } from "@earendil-works/pi-ai/compat";
|
|
27
27
|
import { createGatewayProvider } from "@ai-sdk/gateway";
|
|
@@ -397,6 +397,15 @@ async function collectFiles(params) {
|
|
|
397
397
|
//#region src/chat/repository-instructions.ts
|
|
398
398
|
const AGENTS_FILENAME = "AGENTS.md";
|
|
399
399
|
const MAX_REPOSITORY_INSTRUCTIONS_BYTES = 32768;
|
|
400
|
+
/** Repository skill directories, in priority order, relative to the Git root. */
|
|
401
|
+
const REPOSITORY_SKILL_ROOTS = [".agents/skills", ".claude/skills"];
|
|
402
|
+
const REPOSITORY_SKILLS_HEADER = [
|
|
403
|
+
"## Repository skills",
|
|
404
|
+
"",
|
|
405
|
+
"These skills come from the repository, not from `<available-skills>`. `loadSkill` cannot load them.",
|
|
406
|
+
"Before work that matches a skill description, read its `SKILL.md` with `readFile` and follow it.",
|
|
407
|
+
"Resolve relative paths in a skill against the skill directory."
|
|
408
|
+
].join("\n");
|
|
400
409
|
const AGENTS_REPLACEMENT_NOTICE = "These AGENTS.md instructions replace all previously provided AGENTS.md instructions.";
|
|
401
410
|
const AGENTS_REMOVAL_NOTICE = "The previously provided AGENTS.md instructions no longer apply.";
|
|
402
411
|
async function pathExists(fs, target) {
|
|
@@ -446,20 +455,53 @@ async function readInstructionFile(fs, filePath) {
|
|
|
446
455
|
throw error;
|
|
447
456
|
}
|
|
448
457
|
}
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
458
|
+
async function readOptionalDirectory(fs, directory) {
|
|
459
|
+
try {
|
|
460
|
+
return await fs.readdir(directory);
|
|
461
|
+
} catch (error) {
|
|
462
|
+
if (isMissingPathError(error)) return [];
|
|
463
|
+
throw error;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
/** Read model-invocable skills from the repository skill directories. */
|
|
467
|
+
async function readRepositorySkills(fs, gitRoot) {
|
|
468
|
+
const roots = REPOSITORY_SKILL_ROOTS.map((root) => path.posix.join(gitRoot, root));
|
|
469
|
+
const listings = await Promise.all(roots.map(async (root) => await readOptionalDirectory(fs, root)));
|
|
470
|
+
const candidates = /* @__PURE__ */ new Map();
|
|
471
|
+
listings.forEach((entries, index) => {
|
|
472
|
+
for (const entry of [...entries].sort()) if (!candidates.has(entry)) candidates.set(entry, path.posix.join(roots[index], entry));
|
|
473
|
+
});
|
|
474
|
+
return (await Promise.all([...candidates].map(async ([name, skillDirectory]) => {
|
|
475
|
+
if (!await directoryExists(fs, skillDirectory)) return;
|
|
476
|
+
const filePath = path.posix.join(skillDirectory, "SKILL.md");
|
|
477
|
+
const raw = await readInstructionFile(fs, filePath);
|
|
478
|
+
const parsed = raw ? parseSkillFile(raw, name) : void 0;
|
|
479
|
+
if (!parsed?.ok || parsed.skill.disableModelInvocation) return;
|
|
480
|
+
return {
|
|
481
|
+
description: parsed.skill.description.replace(/\s+/g, " ").trim(),
|
|
482
|
+
name,
|
|
483
|
+
path: filePath
|
|
484
|
+
};
|
|
485
|
+
}))).filter((skill) => Boolean(skill)).sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
|
|
486
|
+
}
|
|
487
|
+
function sourceBytes(sources) {
|
|
488
|
+
return sources.reduce((total, source) => total + Buffer.byteLength(source.content, "utf8"), 0);
|
|
489
|
+
}
|
|
490
|
+
function skillListBytes(skills) {
|
|
491
|
+
return skills.reduce((total, skill) => total + repositorySkillBytes(skill), 0);
|
|
492
|
+
}
|
|
493
|
+
/** Read root-to-cwd AGENTS.md files within the byte budget. */
|
|
494
|
+
async function readAgentsSources(args) {
|
|
453
495
|
const directories = [];
|
|
454
496
|
let current = args.cwd;
|
|
455
497
|
for (;;) {
|
|
456
498
|
directories.push(current);
|
|
457
|
-
if (current === gitRoot) break;
|
|
499
|
+
if (current === args.gitRoot) break;
|
|
458
500
|
current = path.posix.dirname(current);
|
|
459
501
|
}
|
|
460
502
|
directories.reverse();
|
|
461
503
|
const sources = [];
|
|
462
|
-
let remaining = Math.max(0, args.maxBytes
|
|
504
|
+
let remaining = Math.max(0, args.maxBytes);
|
|
463
505
|
for (const directory of directories) {
|
|
464
506
|
if (remaining <= 0) break;
|
|
465
507
|
const filePath = path.posix.join(directory, AGENTS_FILENAME);
|
|
@@ -473,34 +515,69 @@ async function resolveRepositoryInstructions(args) {
|
|
|
473
515
|
});
|
|
474
516
|
remaining -= Buffer.byteLength(bounded, "utf8");
|
|
475
517
|
}
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
518
|
+
return sources;
|
|
519
|
+
}
|
|
520
|
+
/** Read repository skills that fit the byte budget, keeping each entry whole. */
|
|
521
|
+
async function readRepositorySkillsWithinBudget(args) {
|
|
522
|
+
const skills = [];
|
|
523
|
+
let remaining = args.maxBytes;
|
|
524
|
+
if (remaining <= 0) return skills;
|
|
525
|
+
for (const skill of await readRepositorySkills(args.fs, args.gitRoot)) {
|
|
526
|
+
const bytes = repositorySkillBytes(skill);
|
|
527
|
+
if (bytes > remaining) break;
|
|
528
|
+
skills.push(skill);
|
|
529
|
+
remaining -= bytes;
|
|
530
|
+
}
|
|
531
|
+
return skills;
|
|
532
|
+
}
|
|
533
|
+
function buildRepositoryInstructions(directory, sources, skills) {
|
|
534
|
+
if (sources.length === 0 && skills.length === 0) return;
|
|
482
535
|
return {
|
|
483
|
-
directory
|
|
484
|
-
fingerprint
|
|
536
|
+
directory,
|
|
537
|
+
fingerprint: createHash("sha256").update(JSON.stringify({
|
|
538
|
+
directory,
|
|
539
|
+
sources,
|
|
540
|
+
...skills.length > 0 ? { skills } : void 0
|
|
541
|
+
})).digest("hex"),
|
|
542
|
+
...skills.length > 0 ? { skills } : void 0,
|
|
485
543
|
sources,
|
|
486
|
-
text
|
|
544
|
+
text: formatRepositoryInstructions(sources, skills)
|
|
487
545
|
};
|
|
488
546
|
}
|
|
489
547
|
function formatInstructionSources(sources) {
|
|
490
548
|
if (sources.length === 1) return sources[0].content;
|
|
491
549
|
return sources.map((source) => `## ${path.posix.dirname(source.path)}\n\n${source.content}`).join("\n\n");
|
|
492
550
|
}
|
|
551
|
+
function formatRepositorySkill(skill) {
|
|
552
|
+
return `- \`${skill.name}\` (\`${skill.path}\`): ${skill.description}`;
|
|
553
|
+
}
|
|
554
|
+
function repositorySkillBytes(skill) {
|
|
555
|
+
return Buffer.byteLength(formatRepositorySkill(skill), "utf8") + 1;
|
|
556
|
+
}
|
|
557
|
+
function formatRepositoryInstructions(sources, skills) {
|
|
558
|
+
const sections = [];
|
|
559
|
+
if (sources.length > 0) sections.push(formatInstructionSources(sources));
|
|
560
|
+
if (skills.length > 0) sections.push([
|
|
561
|
+
REPOSITORY_SKILLS_HEADER,
|
|
562
|
+
"",
|
|
563
|
+
...skills.map(formatRepositorySkill)
|
|
564
|
+
].join("\n"));
|
|
565
|
+
return sections.join("\n\n");
|
|
566
|
+
}
|
|
493
567
|
/** Combine AGENTS.md bundles from one or more repository directories. */
|
|
494
568
|
function mergeRepositoryInstructions(bundles) {
|
|
495
569
|
if (bundles.length === 0) return;
|
|
496
570
|
if (bundles.length === 1) return bundles[0];
|
|
497
571
|
const sources = bundles.flatMap((bundle) => bundle.sources);
|
|
498
|
-
const
|
|
572
|
+
const skills = bundles.flatMap((bundle) => bundle.skills ?? []);
|
|
573
|
+
const text = formatRepositoryInstructions(sources, skills);
|
|
499
574
|
return {
|
|
500
575
|
fingerprint: createHash("sha256").update(JSON.stringify({
|
|
501
576
|
directories: bundles.map((bundle) => bundle.directory ?? null),
|
|
502
|
-
sources
|
|
577
|
+
sources,
|
|
578
|
+
...skills.length > 0 ? { skills } : void 0
|
|
503
579
|
})).digest("hex"),
|
|
580
|
+
...skills.length > 0 ? { skills } : void 0,
|
|
504
581
|
sources,
|
|
505
582
|
text
|
|
506
583
|
};
|
|
@@ -509,17 +586,33 @@ function mergeRepositoryInstructions(bundles) {
|
|
|
509
586
|
async function resolveRepositoryInstructionsForDirectories(args) {
|
|
510
587
|
const uniqueDirectories = [...new Set(args.directories.filter((directory) => directory === SANDBOX_WORKSPACE_ROOT || directory.startsWith(`${SANDBOX_WORKSPACE_ROOT}/`)))].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
|
|
511
588
|
let remaining = MAX_REPOSITORY_INSTRUCTIONS_BYTES;
|
|
512
|
-
const
|
|
589
|
+
const repositories = [];
|
|
513
590
|
for (const directory of uniqueDirectories) {
|
|
514
|
-
|
|
515
|
-
|
|
591
|
+
const gitRoot = await findGitRoot(args.fs, directory);
|
|
592
|
+
if (!gitRoot) continue;
|
|
593
|
+
const sources = await readAgentsSources({
|
|
516
594
|
cwd: directory,
|
|
517
595
|
fs: args.fs,
|
|
596
|
+
gitRoot,
|
|
597
|
+
maxBytes: remaining
|
|
598
|
+
});
|
|
599
|
+
remaining -= sourceBytes(sources);
|
|
600
|
+
repositories.push({
|
|
601
|
+
directory,
|
|
602
|
+
gitRoot,
|
|
603
|
+
sources
|
|
604
|
+
});
|
|
605
|
+
}
|
|
606
|
+
const bundles = [];
|
|
607
|
+
for (const repository of repositories) {
|
|
608
|
+
const skills = await readRepositorySkillsWithinBudget({
|
|
609
|
+
fs: args.fs,
|
|
610
|
+
gitRoot: repository.gitRoot,
|
|
518
611
|
maxBytes: remaining
|
|
519
612
|
});
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
613
|
+
remaining -= skillListBytes(skills);
|
|
614
|
+
const bundle = buildRepositoryInstructions(repository.directory, repository.sources, skills);
|
|
615
|
+
if (bundle) bundles.push(bundle);
|
|
523
616
|
}
|
|
524
617
|
return mergeRepositoryInstructions(bundles);
|
|
525
618
|
}
|
|
@@ -3095,6 +3188,10 @@ function normalizeSlackStatusText(text) {
|
|
|
3095
3188
|
if (!trimmed) return "";
|
|
3096
3189
|
return truncateStatusText(trimmed.replace(/(?:\.\s*)+$/, "").trim());
|
|
3097
3190
|
}
|
|
3191
|
+
/** Render a Slack user mention, which Slack shows as the person's name. */
|
|
3192
|
+
function slackMention(userId) {
|
|
3193
|
+
return `<@${userId}>`;
|
|
3194
|
+
}
|
|
3098
3195
|
//#endregion
|
|
3099
3196
|
//#region src/chat/slack/oauth-authorization-message.ts
|
|
3100
3197
|
/** Present private OAuth authorization with a clear action and text fallback. */
|
|
@@ -3984,12 +4081,14 @@ function renderTaskInput(args) {
|
|
|
3984
4081
|
const instructions = args.instructions.trim();
|
|
3985
4082
|
if (!instructions) throw new Error("Task instructions are required");
|
|
3986
4083
|
const about = args.about?.trim();
|
|
4084
|
+
const creator = args.creator?.trim();
|
|
3987
4085
|
const lines = [
|
|
3988
4086
|
"[task]",
|
|
3989
4087
|
"",
|
|
3990
4088
|
"This is a task, not a message from a person.",
|
|
3991
4089
|
"",
|
|
3992
4090
|
...about ? [`About: ${oneLine(about)}`] : [],
|
|
4091
|
+
...creator ? [`Created by: ${oneLine(creator)}. Where the instructions say "me" or "my", write this mention.`] : [],
|
|
3993
4092
|
`Instructions: ${instructions}`
|
|
3994
4093
|
];
|
|
3995
4094
|
const guidance = args.guidance?.trim();
|
|
@@ -8742,7 +8841,7 @@ var PluginMcpClient = class {
|
|
|
8742
8841
|
return [...this.listedTools];
|
|
8743
8842
|
});
|
|
8744
8843
|
}
|
|
8745
|
-
async callTool(name, args) {
|
|
8844
|
+
async callTool(name, args, signal) {
|
|
8746
8845
|
return await this.withSessionRecovery(async () => {
|
|
8747
8846
|
const client = await this.getClient();
|
|
8748
8847
|
const mcp = this.plugin.manifest.mcp;
|
|
@@ -8759,7 +8858,7 @@ var PluginMcpClient = class {
|
|
|
8759
8858
|
const result = await this.wrapAuth(() => client.callTool({
|
|
8760
8859
|
name,
|
|
8761
8860
|
arguments: args ?? {}
|
|
8762
|
-
}), "call_tool");
|
|
8861
|
+
}, void 0, signal ? { signal } : void 0), "call_tool");
|
|
8763
8862
|
await this.syncTransportSessionId();
|
|
8764
8863
|
return result;
|
|
8765
8864
|
});
|
|
@@ -9143,7 +9242,7 @@ var McpToolManager = class {
|
|
|
9143
9242
|
const argumentAttribute = serializeMcpPayload(resolvedArgs, conversationPrivacy, { privateMetadata: true });
|
|
9144
9243
|
return await withSpan(`execute_tool ${managedToolName}`, "gen_ai.execute_tool", {}, async () => {
|
|
9145
9244
|
try {
|
|
9146
|
-
const result = await client.callTool(tool.name, resolvedArgs);
|
|
9245
|
+
const result = await client.callTool(tool.name, resolvedArgs, options?.signal);
|
|
9147
9246
|
if ("isError" in result && result.isError) throw new McpToolError(extractMcpErrorMessage(result));
|
|
9148
9247
|
const resultAttribute = serializeMcpPayload(result, conversationPrivacy, { privateMetadata: true });
|
|
9149
9248
|
if (resultAttribute) setSpanAttributes({ "gen_ai.tool.call.result": resultAttribute });
|
|
@@ -9503,6 +9602,7 @@ function createCallMcpToolTool(mcpToolManager) {
|
|
|
9503
9602
|
}
|
|
9504
9603
|
const result = await mcpTool.execute(resolveMcpArguments(input), {
|
|
9505
9604
|
conversationPrivacy: options?.conversationPrivacy ?? "private",
|
|
9605
|
+
...options?.signal ? { signal: options.signal } : void 0,
|
|
9506
9606
|
...options?.toolCallId ? { toolCallId: options.toolCallId } : void 0
|
|
9507
9607
|
});
|
|
9508
9608
|
return {
|
|
@@ -10262,6 +10362,7 @@ var AutomationConflictError = class extends ToolInputError {
|
|
|
10262
10362
|
this.name = "AutomationConflictError";
|
|
10263
10363
|
}
|
|
10264
10364
|
};
|
|
10365
|
+
/** Sort object keys so equal values produce equal JSON. */
|
|
10265
10366
|
function canonicalJson$1(value) {
|
|
10266
10367
|
if (Array.isArray(value)) return value.map(canonicalJson$1);
|
|
10267
10368
|
if (value && typeof value === "object") return Object.fromEntries(Object.entries(value).sort(([a], [b]) => a.localeCompare(b)).map(([key, child]) => [key, canonicalJson$1(child)]));
|
|
@@ -10276,6 +10377,70 @@ function requireAutomationRevision(task, revision) {
|
|
|
10276
10377
|
if (!task || automationRevision(task) !== revision) throw new AutomationConflictError();
|
|
10277
10378
|
}
|
|
10278
10379
|
//#endregion
|
|
10380
|
+
//#region src/chat/automations/versions.ts
|
|
10381
|
+
/** Automation versions keep each saved definition so people can see how it changed. */
|
|
10382
|
+
/** Return the versioned part of an Automation. Lifecycle and run state are excluded. */
|
|
10383
|
+
function automationDefinition(task) {
|
|
10384
|
+
const common = {
|
|
10385
|
+
title: task.title?.trim() || null,
|
|
10386
|
+
instruction: task.task.text,
|
|
10387
|
+
credentialMode: task.credentialMode,
|
|
10388
|
+
destination: task.destination,
|
|
10389
|
+
outcomes: task.outcomes
|
|
10390
|
+
};
|
|
10391
|
+
return "schedule" in task ? {
|
|
10392
|
+
...common,
|
|
10393
|
+
schedule: task.schedule
|
|
10394
|
+
} : {
|
|
10395
|
+
...common,
|
|
10396
|
+
trigger: task.trigger
|
|
10397
|
+
};
|
|
10398
|
+
}
|
|
10399
|
+
/** Compare definition values after key sorting. */
|
|
10400
|
+
function sameDefinitionValue(left, right) {
|
|
10401
|
+
return JSON.stringify(canonicalJson$1(left)) === JSON.stringify(canonicalJson$1(right));
|
|
10402
|
+
}
|
|
10403
|
+
/**
|
|
10404
|
+
* Save a version when the definition changed. Call this in the same
|
|
10405
|
+
* transaction and lock as the Automation write. Without `current`, the
|
|
10406
|
+
* Automation is new, so the creator saves version 1.
|
|
10407
|
+
*/
|
|
10408
|
+
async function recordAutomationVersion(db, kind, task, current, editedBy) {
|
|
10409
|
+
const definition = automationDefinition(task);
|
|
10410
|
+
if (current && sameDefinitionValue(automationDefinition(current), definition)) return;
|
|
10411
|
+
await db.insert(juniorAutomationVersions).values({
|
|
10412
|
+
kind,
|
|
10413
|
+
automationId: task.id,
|
|
10414
|
+
version: sql`(
|
|
10415
|
+
SELECT coalesce(max(${juniorAutomationVersions.version}), 0) + 1
|
|
10416
|
+
FROM ${juniorAutomationVersions}
|
|
10417
|
+
WHERE ${juniorAutomationVersions.kind} = ${kind}
|
|
10418
|
+
AND ${juniorAutomationVersions.automationId} = ${task.id}
|
|
10419
|
+
)`,
|
|
10420
|
+
createdAtMs: Date.now(),
|
|
10421
|
+
editedBy: editedBy ?? (current ? null : task.createdBy),
|
|
10422
|
+
definition
|
|
10423
|
+
});
|
|
10424
|
+
}
|
|
10425
|
+
function parseVersionRow(kind, row) {
|
|
10426
|
+
return automationVersionSchema.parse({
|
|
10427
|
+
kind,
|
|
10428
|
+
version: row.version,
|
|
10429
|
+
createdAt: new Date(row.createdAtMs).toISOString(),
|
|
10430
|
+
editedBy: row.editedBy,
|
|
10431
|
+
definition: row.definition
|
|
10432
|
+
});
|
|
10433
|
+
}
|
|
10434
|
+
/** Read one saved definition. */
|
|
10435
|
+
async function readAutomationVersion(db, kind, automationId, version) {
|
|
10436
|
+
const [row] = await db.select().from(juniorAutomationVersions).where(and(eq(juniorAutomationVersions.kind, kind), eq(juniorAutomationVersions.automationId, automationId), eq(juniorAutomationVersions.version, version))).limit(1);
|
|
10437
|
+
return row ? parseVersionRow(kind, row) : void 0;
|
|
10438
|
+
}
|
|
10439
|
+
/** Read saved definitions, newest first. */
|
|
10440
|
+
async function listAutomationVersions(db, kind, automationId, limit) {
|
|
10441
|
+
return (await db.select().from(juniorAutomationVersions).where(and(eq(juniorAutomationVersions.kind, kind), eq(juniorAutomationVersions.automationId, automationId))).orderBy(desc(juniorAutomationVersions.version)).limit(limit)).map((row) => parseVersionRow(kind, row));
|
|
10442
|
+
}
|
|
10443
|
+
//#endregion
|
|
10279
10444
|
//#region src/chat/event-automations/store.ts
|
|
10280
10445
|
const retainedEventAutomationSchema = eventAutomationSchema.extend({
|
|
10281
10446
|
destination: slackDestinationSchema,
|
|
@@ -10327,61 +10492,84 @@ async function getEventAutomation(db, id) {
|
|
|
10327
10492
|
async function createEventAutomation(db, task) {
|
|
10328
10493
|
const parsed = eventAutomationSchema.parse(eventAutomationJsonPayload(task));
|
|
10329
10494
|
const title = task.title?.trim() || null;
|
|
10330
|
-
|
|
10331
|
-
|
|
10332
|
-
|
|
10333
|
-
|
|
10334
|
-
|
|
10335
|
-
|
|
10336
|
-
|
|
10337
|
-
|
|
10338
|
-
|
|
10339
|
-
|
|
10340
|
-
|
|
10341
|
-
|
|
10342
|
-
|
|
10343
|
-
|
|
10344
|
-
|
|
10345
|
-
|
|
10346
|
-
|
|
10347
|
-
|
|
10348
|
-
|
|
10349
|
-
|
|
10350
|
-
|
|
10351
|
-
|
|
10352
|
-
|
|
10353
|
-
|
|
10354
|
-
|
|
10355
|
-
|
|
10356
|
-
|
|
10357
|
-
|
|
10495
|
+
return db.transaction(async (tx) => {
|
|
10496
|
+
const inserted = await tx.insert(juniorEventAutomations).values({
|
|
10497
|
+
id: parsed.id,
|
|
10498
|
+
teamId: parsed.destination.teamId,
|
|
10499
|
+
namespace: parsed.trigger.namespace,
|
|
10500
|
+
identifier: parsed.trigger.identifier,
|
|
10501
|
+
createdAtMs: parsed.createdAtMs,
|
|
10502
|
+
status: "active",
|
|
10503
|
+
title,
|
|
10504
|
+
task: parsed
|
|
10505
|
+
}).onConflictDoNothing().returning({
|
|
10506
|
+
status: juniorEventAutomations.status,
|
|
10507
|
+
task: juniorEventAutomations.task,
|
|
10508
|
+
title: juniorEventAutomations.title
|
|
10509
|
+
});
|
|
10510
|
+
if (inserted[0]) {
|
|
10511
|
+
const created = parseEventAutomationRow(inserted[0]);
|
|
10512
|
+
await recordAutomationVersion(tx, "event", created, void 0);
|
|
10513
|
+
return created;
|
|
10514
|
+
}
|
|
10515
|
+
const existing = await getEventAutomation(tx, parsed.id);
|
|
10516
|
+
if (!existing) return {
|
|
10517
|
+
...parsed,
|
|
10518
|
+
status: "active",
|
|
10519
|
+
...title ? { title } : void 0
|
|
10520
|
+
};
|
|
10521
|
+
if (existing.status !== "deleted") return existing;
|
|
10522
|
+
const rows = await tx.update(juniorEventAutomations).set({
|
|
10523
|
+
teamId: parsed.destination.teamId,
|
|
10524
|
+
namespace: parsed.trigger.namespace,
|
|
10525
|
+
identifier: parsed.trigger.identifier,
|
|
10526
|
+
status: "active",
|
|
10527
|
+
title,
|
|
10528
|
+
task: parsed
|
|
10529
|
+
}).where(and(eq(juniorEventAutomations.id, parsed.id), eq(juniorEventAutomations.status, "deleted"))).returning({
|
|
10530
|
+
status: juniorEventAutomations.status,
|
|
10531
|
+
task: juniorEventAutomations.task,
|
|
10532
|
+
title: juniorEventAutomations.title
|
|
10533
|
+
});
|
|
10534
|
+
if (!rows[0]) return await getEventAutomation(tx, parsed.id) ?? {
|
|
10535
|
+
...parsed,
|
|
10536
|
+
status: "active",
|
|
10537
|
+
...title ? { title } : void 0
|
|
10538
|
+
};
|
|
10539
|
+
const revived = parseEventAutomationRow(rows[0]);
|
|
10540
|
+
await recordAutomationVersion(tx, "event", revived, void 0);
|
|
10541
|
+
return revived;
|
|
10358
10542
|
});
|
|
10359
|
-
return rows[0] ? parseEventAutomationRow(rows[0]) : await getEventAutomation(db, parsed.id) ?? {
|
|
10360
|
-
...parsed,
|
|
10361
|
-
status: "active",
|
|
10362
|
-
...title ? { title } : void 0
|
|
10363
|
-
};
|
|
10364
10543
|
}
|
|
10365
|
-
/**
|
|
10366
|
-
|
|
10367
|
-
|
|
10368
|
-
|
|
10369
|
-
|
|
10370
|
-
return saveEventAutomation(tx, task);
|
|
10371
|
-
});
|
|
10544
|
+
/**
|
|
10545
|
+
* Replace an existing non-deleted event automation under a row lock.
|
|
10546
|
+
* Pass `editedBy` when a person changes the definition so its version names them.
|
|
10547
|
+
*/
|
|
10548
|
+
async function saveEventAutomation(db, task, expectedRevision, editedBy) {
|
|
10372
10549
|
const parsed = eventAutomationSchema.parse(eventAutomationJsonPayload(task));
|
|
10373
10550
|
const title = task.title?.trim() || null;
|
|
10374
|
-
|
|
10375
|
-
|
|
10376
|
-
|
|
10377
|
-
|
|
10378
|
-
|
|
10379
|
-
|
|
10380
|
-
|
|
10381
|
-
|
|
10382
|
-
|
|
10551
|
+
return db.transaction(async (tx) => {
|
|
10552
|
+
const rows = await tx.select({
|
|
10553
|
+
status: juniorEventAutomations.status,
|
|
10554
|
+
task: juniorEventAutomations.task,
|
|
10555
|
+
title: juniorEventAutomations.title
|
|
10556
|
+
}).from(juniorEventAutomations).where(eq(juniorEventAutomations.id, parsed.id)).for("update");
|
|
10557
|
+
const current = rows[0] ? parseEventAutomationRow(rows[0]) : void 0;
|
|
10558
|
+
if (expectedRevision !== void 0) requireAutomationRevision(current, expectedRevision);
|
|
10559
|
+
if (!current || current.status === "deleted") return void 0;
|
|
10560
|
+
const saved = parseEventAutomationRow((await tx.update(juniorEventAutomations).set({
|
|
10561
|
+
namespace: parsed.trigger.namespace,
|
|
10562
|
+
identifier: parsed.trigger.identifier,
|
|
10563
|
+
title,
|
|
10564
|
+
task: parsed
|
|
10565
|
+
}).where(eq(juniorEventAutomations.id, parsed.id)).returning({
|
|
10566
|
+
status: juniorEventAutomations.status,
|
|
10567
|
+
task: juniorEventAutomations.task,
|
|
10568
|
+
title: juniorEventAutomations.title
|
|
10569
|
+
}))[0]);
|
|
10570
|
+
await recordAutomationVersion(tx, "event", saved, current, editedBy);
|
|
10571
|
+
return saved;
|
|
10383
10572
|
});
|
|
10384
|
-
return rows[0] ? parseEventAutomationRow(rows[0]) : void 0;
|
|
10385
10573
|
}
|
|
10386
10574
|
/** Mark one existing event automation deleted while retaining the row for history. */
|
|
10387
10575
|
async function deleteEventAutomation(db, id) {
|
|
@@ -10697,7 +10885,7 @@ function createEventAutomationTool(context, catalog) {
|
|
|
10697
10885
|
executionMode: "sequential",
|
|
10698
10886
|
description: "Create a durable event automation in the active Slack channel or DM, never a thread. It 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.",
|
|
10699
10887
|
inputSchema: z.object({
|
|
10700
|
-
instruction:
|
|
10888
|
+
instruction: automationInstructionToolSchema,
|
|
10701
10889
|
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(),
|
|
10702
10890
|
trigger,
|
|
10703
10891
|
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(),
|
|
@@ -10908,7 +11096,7 @@ function createUpdateEventAutomationTool(context, catalog) {
|
|
|
10908
11096
|
inputSchema: z.object({
|
|
10909
11097
|
automationId: z.string().min(1),
|
|
10910
11098
|
title: automationTitleSchema.optional(),
|
|
10911
|
-
instruction:
|
|
11099
|
+
instruction: automationInstructionToolSchema.nullable().optional(),
|
|
10912
11100
|
trigger: registeredEventAutomationTriggerSchema(catalog).nullable().optional(),
|
|
10913
11101
|
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(),
|
|
10914
11102
|
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()
|
|
@@ -10936,7 +11124,11 @@ function createUpdateEventAutomationTool(context, catalog) {
|
|
|
10936
11124
|
outcomes: input.outcomes ?? void 0,
|
|
10937
11125
|
credentialMode: input.credentialMode ?? void 0
|
|
10938
11126
|
}, isCreator, catalog);
|
|
10939
|
-
const saved = await saveEventAutomation(getDb(), next, automationRevision(current)
|
|
11127
|
+
const saved = await saveEventAutomation(getDb(), next, automationRevision(current), {
|
|
11128
|
+
slackUserId: actor.userId,
|
|
11129
|
+
...actor.fullName ? { fullName: actor.fullName } : void 0,
|
|
11130
|
+
...actor.userName ? { userName: actor.userName } : void 0
|
|
11131
|
+
});
|
|
10940
11132
|
if (!saved) throw new ToolInputError("Event automation was not found.");
|
|
10941
11133
|
return eventAutomationToolResult(context.conversationId, saved, catalog, actor.userId);
|
|
10942
11134
|
}
|
|
@@ -11090,10 +11282,11 @@ async function listScheduledAutomations(db) {
|
|
|
11090
11282
|
async function listScheduledAutomationsForTeam(db, teamId) {
|
|
11091
11283
|
return await readListedScheduledAutomations(db, teamId);
|
|
11092
11284
|
}
|
|
11093
|
-
async function writeScheduledAutomation(db, task, current) {
|
|
11285
|
+
async function writeScheduledAutomation(db, task, current, editedBy) {
|
|
11094
11286
|
if (current?.status === "blocked" && task.status === "active" && typeof task.nextRunAtMs === "number" && Number.isFinite(task.nextRunAtMs)) await db.delete(juniorSchedulerRuns).where(and(eq(juniorSchedulerRuns.id, scheduledRunId(task.id, task.nextRunAtMs)), eq(juniorSchedulerRuns.status, "blocked")));
|
|
11095
11287
|
if (task.status === "deleted" && current?.status !== "deleted") await skipPendingRunsForDeletedTask(db, task);
|
|
11096
11288
|
await upsertScheduledAutomation(db, task);
|
|
11289
|
+
await recordAutomationVersion(db, "scheduled", task, current, editedBy);
|
|
11097
11290
|
}
|
|
11098
11291
|
async function skipPendingRunsForDeletedTask(db, task) {
|
|
11099
11292
|
const errorMessage = `Scheduled automation ${task.id} was deleted before the run started.`;
|
|
@@ -11123,13 +11316,16 @@ async function createScheduledAutomation(db, task) {
|
|
|
11123
11316
|
return next;
|
|
11124
11317
|
});
|
|
11125
11318
|
}
|
|
11126
|
-
/**
|
|
11127
|
-
|
|
11319
|
+
/**
|
|
11320
|
+
* Save a scheduled automation and clear its blocked occurrence on reactivation.
|
|
11321
|
+
* Pass `editedBy` when a person changes the definition so its version names them.
|
|
11322
|
+
*/
|
|
11323
|
+
async function saveScheduledAutomation(db, task, expectedRevision, editedBy) {
|
|
11128
11324
|
const next = requireStoredTask(task);
|
|
11129
11325
|
await withScheduledAutomationLock(db, task.id, async (tx) => {
|
|
11130
11326
|
const current = await readScheduledAutomation(tx, task.id);
|
|
11131
11327
|
if (expectedRevision !== void 0) requireAutomationRevision(current, expectedRevision);
|
|
11132
|
-
await writeScheduledAutomation(tx, next, current);
|
|
11328
|
+
await writeScheduledAutomation(tx, next, current, editedBy);
|
|
11133
11329
|
});
|
|
11134
11330
|
return next;
|
|
11135
11331
|
}
|
|
@@ -11174,6 +11370,76 @@ function logScheduledAutomationRunSkipped(task, run, extras = {}) {
|
|
|
11174
11370
|
logInfo("scheduled_automation.run.skipped", scheduledAutomationRunAttributes(task, run, extras));
|
|
11175
11371
|
}
|
|
11176
11372
|
//#endregion
|
|
11373
|
+
//#region src/chat/slack/user.ts
|
|
11374
|
+
const slackUserInfoSchema = z.object({
|
|
11375
|
+
ok: z.literal(true),
|
|
11376
|
+
user: z.object({
|
|
11377
|
+
name: z.string().nullish(),
|
|
11378
|
+
tz: z.string().nullish(),
|
|
11379
|
+
profile: z.object({
|
|
11380
|
+
email: z.string().nullish(),
|
|
11381
|
+
real_name: z.string().nullish()
|
|
11382
|
+
}).optional()
|
|
11383
|
+
})
|
|
11384
|
+
});
|
|
11385
|
+
const USER_CACHE_TTL_MS = 3e5;
|
|
11386
|
+
const userCache = /* @__PURE__ */ new Map();
|
|
11387
|
+
function userCacheKey(teamId, userId) {
|
|
11388
|
+
return `${teamId}:${userId}`;
|
|
11389
|
+
}
|
|
11390
|
+
function readFromCache(teamId, userId) {
|
|
11391
|
+
const hit = userCache.get(userCacheKey(teamId, userId));
|
|
11392
|
+
if (!hit) return null;
|
|
11393
|
+
if (hit.expiresAt < Date.now()) {
|
|
11394
|
+
userCache.delete(userCacheKey(teamId, userId));
|
|
11395
|
+
return null;
|
|
11396
|
+
}
|
|
11397
|
+
return hit.value;
|
|
11398
|
+
}
|
|
11399
|
+
function writeToCache(teamId, userId, value) {
|
|
11400
|
+
userCache.set(userCacheKey(teamId, userId), {
|
|
11401
|
+
value,
|
|
11402
|
+
expiresAt: Date.now() + USER_CACHE_TTL_MS
|
|
11403
|
+
});
|
|
11404
|
+
}
|
|
11405
|
+
/** Fetch Slack user profile info with in-memory TTL cache to avoid repeated API calls. */
|
|
11406
|
+
async function lookupSlackUser(teamId, userId) {
|
|
11407
|
+
if (!teamId || !userId) return null;
|
|
11408
|
+
const cached = readFromCache(teamId, userId);
|
|
11409
|
+
if (cached) return cached;
|
|
11410
|
+
const token = getSlackBotToken();
|
|
11411
|
+
if (!token) return null;
|
|
11412
|
+
try {
|
|
11413
|
+
const response = await fetch(`https://slack.com/api/users.info?user=${encodeURIComponent(userId)}`, { headers: { authorization: `Bearer ${token}` } });
|
|
11414
|
+
if (!response.ok) {
|
|
11415
|
+
logWarn("slack.user.lookup.failed", {
|
|
11416
|
+
"enduser.id": userId,
|
|
11417
|
+
"app.slack.team_id": teamId,
|
|
11418
|
+
"http.response.status_code": response.status
|
|
11419
|
+
});
|
|
11420
|
+
return null;
|
|
11421
|
+
}
|
|
11422
|
+
const parsed = slackUserInfoSchema.safeParse(await response.json());
|
|
11423
|
+
if (!parsed.success) return null;
|
|
11424
|
+
const payload = parsed.data;
|
|
11425
|
+
const result = {
|
|
11426
|
+
userName: payload.user.name?.trim() || void 0,
|
|
11427
|
+
fullName: payload.user.profile?.real_name?.trim() || void 0,
|
|
11428
|
+
email: payload.user.profile?.email?.trim() || void 0,
|
|
11429
|
+
timezone: payload.user.tz?.trim() || void 0
|
|
11430
|
+
};
|
|
11431
|
+
writeToCache(teamId, userId, result);
|
|
11432
|
+
return result;
|
|
11433
|
+
} catch (error) {
|
|
11434
|
+
logWarn("slack.user.lookup.failed", {
|
|
11435
|
+
"enduser.id": userId,
|
|
11436
|
+
"app.slack.team_id": teamId,
|
|
11437
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
11438
|
+
});
|
|
11439
|
+
return null;
|
|
11440
|
+
}
|
|
11441
|
+
}
|
|
11442
|
+
//#endregion
|
|
11177
11443
|
//#region src/chat/scheduled-automations/identity.ts
|
|
11178
11444
|
const SLACK_USER_ID_PATTERN = /^[UW][A-Z0-9]{5,}$/;
|
|
11179
11445
|
function clean(value) {
|
|
@@ -11425,6 +11691,19 @@ function normalizeStatus(value) {
|
|
|
11425
11691
|
function getDefaultScheduleTimezone() {
|
|
11426
11692
|
return process.env.JUNIOR_TIMEZONE?.trim() || DEFAULT_SCHEDULE_TIMEZONE;
|
|
11427
11693
|
}
|
|
11694
|
+
/**
|
|
11695
|
+
* Pick the timezone for a new schedule when the user names none: the
|
|
11696
|
+
* creator's Slack profile, then the saved user timezone, then the install
|
|
11697
|
+
* default. A valid Slack timezone is saved on the linked user.
|
|
11698
|
+
*/
|
|
11699
|
+
async function resolveCreatorScheduleTimezone(args) {
|
|
11700
|
+
const timezone = (await lookupSlackUser(args.teamId, args.slackUserId))?.timezone;
|
|
11701
|
+
if (timezone && isValidTimeZone$1(timezone)) {
|
|
11702
|
+
if (args.userId) await saveUserTimezone(args.db, args.userId, timezone, args.nowMs);
|
|
11703
|
+
return timezone;
|
|
11704
|
+
}
|
|
11705
|
+
return (args.userId ? await readUserTimezone(args.db, args.userId) : void 0) ?? getDefaultScheduleTimezone();
|
|
11706
|
+
}
|
|
11428
11707
|
//#endregion
|
|
11429
11708
|
//#region src/chat/scheduled-automations/tools/create.ts
|
|
11430
11709
|
/** Create a tool that stores a scheduled automation for the active Slack context. */
|
|
@@ -11440,7 +11719,7 @@ function createSlackScheduleCreateAutomationTool(context) {
|
|
|
11440
11719
|
description: "Create a one-time or recurring Junior task in the active Slack channel. Store the requested future work; do not perform it or check its service access now unless the user also asks for that.",
|
|
11441
11720
|
executionMode: "sequential",
|
|
11442
11721
|
inputSchema: z.object({
|
|
11443
|
-
instruction:
|
|
11722
|
+
instruction: automationInstructionToolSchema,
|
|
11444
11723
|
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(),
|
|
11445
11724
|
schedule: scheduleIntentSchema.describe("When the task runs. The scheduler computes the exact next run from this intent and the server clock."),
|
|
11446
11725
|
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(),
|
|
@@ -11467,13 +11746,21 @@ function createSlackScheduleCreateAutomationTool(context) {
|
|
|
11467
11746
|
if (!sameDestination(existing, destination) || existing.createdBy.slackUserId !== actor.slackUserId) throwToolInputError("Scheduled automation operation identity is invalid.");
|
|
11468
11747
|
return scheduleAutomationToolResult(context.conversationId, existing, actor.slackUserId);
|
|
11469
11748
|
}
|
|
11470
|
-
const
|
|
11749
|
+
const creator = await context.users.resolveActor();
|
|
11750
|
+
const identity = creator?.identity;
|
|
11471
11751
|
if (!identity || identity.provider !== "slack" || identity.providerTenantId !== destination.teamId || identity.providerSubjectId !== actor.slackUserId) throwToolInputError("Scheduled automation creator identity is unavailable.");
|
|
11472
11752
|
const nowMs = context.now?.() ?? Date.now();
|
|
11753
|
+
const defaultTimezone = input.schedule.timezone ?? await resolveCreatorScheduleTimezone({
|
|
11754
|
+
db,
|
|
11755
|
+
nowMs,
|
|
11756
|
+
slackUserId: actor.slackUserId,
|
|
11757
|
+
teamId: destination.teamId,
|
|
11758
|
+
...creator.user ? { userId: creator.user.id } : void 0
|
|
11759
|
+
});
|
|
11473
11760
|
let compiled;
|
|
11474
11761
|
try {
|
|
11475
11762
|
compiled = compileScheduleIntent({
|
|
11476
|
-
defaultTimezone
|
|
11763
|
+
defaultTimezone,
|
|
11477
11764
|
intent: input.schedule,
|
|
11478
11765
|
nowMs
|
|
11479
11766
|
});
|
|
@@ -11730,7 +12017,7 @@ function createSlackScheduleUpdateAutomationTool(context) {
|
|
|
11730
12017
|
inputSchema: z.object({
|
|
11731
12018
|
automationId: z.string().min(1).describe("Scheduled automation ID returned by slackScheduleListAutomations."),
|
|
11732
12019
|
title: automationTitleSchema.optional(),
|
|
11733
|
-
instruction:
|
|
12020
|
+
instruction: automationInstructionToolSchema.optional(),
|
|
11734
12021
|
schedule: scheduleIntentSchema.describe("Complete replacement schedule. Omit to keep it unchanged.").nullable().optional(),
|
|
11735
12022
|
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(),
|
|
11736
12023
|
status: z.enum(["active", "blocked"]).describe("Set active to resume a blocked task, or blocked to stop dispatch.").optional(),
|
|
@@ -11777,7 +12064,7 @@ function createSlackScheduleUpdateAutomationTool(context) {
|
|
|
11777
12064
|
next.outcomes = input.outcomes === void 0 ? moveTaskOutcomes(lookup.outcomes, lookup.destination, requestedDestination) : await resolveTaskOutcomes(input.outcomes, requestedDestination, lookup.createdBy.slackUserId);
|
|
11778
12065
|
}
|
|
11779
12066
|
if (!changingDestination && !instructionChanged && !input.schedule && input.title === void 0 && 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);
|
|
11780
|
-
const committed = await saveScheduledAutomation(db, next, automationRevision(lookup));
|
|
12067
|
+
const committed = await saveScheduledAutomation(db, next, automationRevision(lookup), actor);
|
|
11781
12068
|
if (changingDestination) logInfo("scheduled_automation.move.completed", scheduledAutomationAttributes(committed));
|
|
11782
12069
|
return scheduleAutomationToolResult(context.conversationId, committed, actor.slackUserId);
|
|
11783
12070
|
}
|
|
@@ -14651,9 +14938,6 @@ function explicitUserLookupError(error) {
|
|
|
14651
14938
|
if (error.code === "invalid_arguments") return `Slack rejected the user lookup arguments (${error.apiError ?? error.code}).`;
|
|
14652
14939
|
if (error.code === "feature_unavailable") return "User lookup is not available for this workspace or app installation.";
|
|
14653
14940
|
}
|
|
14654
|
-
function slackMention(userId) {
|
|
14655
|
-
return `<@${userId}>`;
|
|
14656
|
-
}
|
|
14657
14941
|
function asMatch(user) {
|
|
14658
14942
|
return {
|
|
14659
14943
|
...user,
|
|
@@ -17617,6 +17901,36 @@ function createSearchToolsTool(catalogTools) {
|
|
|
17617
17901
|
}
|
|
17618
17902
|
//#endregion
|
|
17619
17903
|
//#region src/chat/tool-support/pi-tool-adapter.ts
|
|
17904
|
+
/**
|
|
17905
|
+
* How long a preempted tool may take to return its own aborted result. Keep it
|
|
17906
|
+
* below the agent's abort settle grace, so the parked turn sees the result.
|
|
17907
|
+
*/
|
|
17908
|
+
const TOOL_ABORT_SETTLE_GRACE_MS = 2e3;
|
|
17909
|
+
/**
|
|
17910
|
+
* Stop waiting for a tool shortly after the host preempts it. A tool that
|
|
17911
|
+
* ignores the signal must not hold the turn past its deadline, or the parked
|
|
17912
|
+
* turn loses the attempt. Work that keeps running is detached; its outcome is
|
|
17913
|
+
* unknown.
|
|
17914
|
+
*/
|
|
17915
|
+
async function untilPreempted(work, signal) {
|
|
17916
|
+
if (!signal) return await work;
|
|
17917
|
+
let timer;
|
|
17918
|
+
let onAbort = () => {};
|
|
17919
|
+
const preempted = new Promise((_, reject) => {
|
|
17920
|
+
onAbort = () => {
|
|
17921
|
+
timer = setTimeout(() => reject(signal.reason), TOOL_ABORT_SETTLE_GRACE_MS);
|
|
17922
|
+
};
|
|
17923
|
+
if (signal.aborted) onAbort();
|
|
17924
|
+
else signal.addEventListener("abort", onAbort, { once: true });
|
|
17925
|
+
});
|
|
17926
|
+
try {
|
|
17927
|
+
return await Promise.race([work, preempted]);
|
|
17928
|
+
} finally {
|
|
17929
|
+
clearTimeout(timer);
|
|
17930
|
+
signal.removeEventListener("abort", onAbort);
|
|
17931
|
+
work.catch(() => void 0);
|
|
17932
|
+
}
|
|
17933
|
+
}
|
|
17620
17934
|
/** Wrap tool definitions into Pi Agent tool objects with logging, validation, and sandbox execution. */
|
|
17621
17935
|
function createPiAgentTools(tools, sandbox, spanContext, onStatus, sandboxTools, pluginAuthOrchestration, onToolCall, agentHooks, conversationPrivacy, onToolResult, actionReview) {
|
|
17622
17936
|
const plannedTools = planToolExposure(tools);
|
|
@@ -17678,17 +17992,17 @@ function createPiAgentTools(tools, sandbox, spanContext, onStatus, sandboxTools,
|
|
|
17678
17992
|
}
|
|
17679
17993
|
const sandboxInput = buildSandboxInput(toolName, executionInput);
|
|
17680
17994
|
const isSandbox = Boolean(sandboxTools?.supports(toolName));
|
|
17681
|
-
const result = isSandbox ?
|
|
17995
|
+
const result = await untilPreempted(isSandbox ? sandboxTools.execute({
|
|
17682
17996
|
toolName,
|
|
17683
17997
|
input: sandboxInput,
|
|
17684
17998
|
...signal ? { signal } : void 0,
|
|
17685
17999
|
...toolName === "grep" || toolName === "findFiles" ? { setToolCallSpanAttributes: setSpanAttributes } : void 0
|
|
17686
|
-
}) :
|
|
18000
|
+
}) : Promise.resolve(toolDef.execute(executionInput, {
|
|
17687
18001
|
experimental_context: sandbox,
|
|
17688
18002
|
...signal ? { signal } : void 0,
|
|
17689
18003
|
conversationPrivacy: effectiveConversationPrivacy,
|
|
17690
18004
|
toolCallId
|
|
17691
|
-
});
|
|
18005
|
+
})), signal);
|
|
17692
18006
|
const normalized = normalizeToolResult(result, {
|
|
17693
18007
|
requireStructuredResult: Boolean(toolDef.outputSchema),
|
|
17694
18008
|
toolName
|
|
@@ -18068,17 +18382,24 @@ function clientAllowsRedirectUri(clientInformation, callbackUrl) {
|
|
|
18068
18382
|
if (!Array.isArray(redirectUris)) return true;
|
|
18069
18383
|
return redirectUris.includes(callbackUrl);
|
|
18070
18384
|
}
|
|
18385
|
+
function requireEnv(name) {
|
|
18386
|
+
const value = process.env[name]?.trim();
|
|
18387
|
+
if (!value) throw new Error(`MCP OAuth client env var ${name} is unset`);
|
|
18388
|
+
return value;
|
|
18389
|
+
}
|
|
18071
18390
|
var StateBackedMcpOAuthClientProvider = class {
|
|
18072
18391
|
authSessionId;
|
|
18073
18392
|
callbackUrl;
|
|
18074
18393
|
sessionContext;
|
|
18075
18394
|
runCredentialMutation;
|
|
18395
|
+
oauthClient;
|
|
18076
18396
|
clientMetadata;
|
|
18077
|
-
constructor(authSessionId, callbackUrl, sessionContext, runCredentialMutation) {
|
|
18397
|
+
constructor(authSessionId, callbackUrl, sessionContext, runCredentialMutation, oauthClient) {
|
|
18078
18398
|
this.authSessionId = authSessionId;
|
|
18079
18399
|
this.callbackUrl = callbackUrl;
|
|
18080
18400
|
this.sessionContext = sessionContext;
|
|
18081
18401
|
this.runCredentialMutation = runCredentialMutation;
|
|
18402
|
+
this.oauthClient = oauthClient;
|
|
18082
18403
|
this.clientMetadata = createClientMetadata(callbackUrl);
|
|
18083
18404
|
}
|
|
18084
18405
|
get redirectUrl() {
|
|
@@ -18088,6 +18409,10 @@ var StateBackedMcpOAuthClientProvider = class {
|
|
|
18088
18409
|
return this.authSessionId;
|
|
18089
18410
|
}
|
|
18090
18411
|
async clientInformation() {
|
|
18412
|
+
if (this.oauthClient) return {
|
|
18413
|
+
client_id: requireEnv(this.oauthClient.clientIdEnv),
|
|
18414
|
+
client_secret: requireEnv(this.oauthClient.clientSecretEnv)
|
|
18415
|
+
};
|
|
18091
18416
|
const session = await this.getCredentialContext();
|
|
18092
18417
|
const clientInformation = (await getMcpStoredOAuthCredentials(session.userId, session.provider))?.clientInformation;
|
|
18093
18418
|
if (!clientInformation) return;
|
|
@@ -18118,6 +18443,8 @@ var StateBackedMcpOAuthClientProvider = class {
|
|
|
18118
18443
|
});
|
|
18119
18444
|
}
|
|
18120
18445
|
async redirectToAuthorization(authorizationUrl) {
|
|
18446
|
+
if (this.oauthClient?.scope) authorizationUrl.searchParams.set("scope", this.oauthClient.scope);
|
|
18447
|
+
for (const [key, value] of Object.entries(this.oauthClient?.authorizeParams ?? {})) authorizationUrl.searchParams.set(key, value);
|
|
18121
18448
|
const existing = await getMcpAuthSession(this.authSessionId);
|
|
18122
18449
|
if (existing?.authorizationUrl && existing.authorizationUrl !== authorizationUrl.toString()) throw new Error("MCP OAuth authorization attempt is already initialized");
|
|
18123
18450
|
await this.ensureSession({ authorizationUrl: authorizationUrl.toString() });
|
|
@@ -18211,7 +18538,7 @@ function requirePluginWithMcp(provider) {
|
|
|
18211
18538
|
}
|
|
18212
18539
|
/** Create one immutable provider authorization attempt for the active turn. */
|
|
18213
18540
|
async function createMcpOAuthClientProvider(input) {
|
|
18214
|
-
requirePluginWithMcp(input.provider);
|
|
18541
|
+
const plugin = requirePluginWithMcp(input.provider);
|
|
18215
18542
|
const baseUrl = resolveBaseUrl();
|
|
18216
18543
|
if (!baseUrl) throw new Error("Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)");
|
|
18217
18544
|
return new StateBackedMcpOAuthClientProvider(input.createAuthorizationState ? await input.createAuthorizationState() : randomUUID(), `${baseUrl}${getMcpOAuthCallbackPath(input.provider)}`, {
|
|
@@ -18226,7 +18553,7 @@ async function createMcpOAuthClientProvider(input) {
|
|
|
18226
18553
|
...input.threadTs ? { threadTs: input.threadTs } : void 0,
|
|
18227
18554
|
...input.toolChannelId ? { toolChannelId: input.toolChannelId } : void 0,
|
|
18228
18555
|
...input.configuration ? { configuration: input.configuration } : void 0
|
|
18229
|
-
});
|
|
18556
|
+
}, void 0, plugin.manifest.mcp?.oauthClient);
|
|
18230
18557
|
}
|
|
18231
18558
|
/** Exchange a callback code while guarding every shared credential mutation. */
|
|
18232
18559
|
async function finalizeMcpAuthorization(provider, authSessionId, authorizationCode, runCredentialMutation) {
|
|
@@ -18237,7 +18564,7 @@ async function finalizeMcpAuthorization(provider, authSessionId, authorizationCo
|
|
|
18237
18564
|
if (session.provider !== provider) throw new Error(`MCP auth session provider mismatch: expected "${provider}", got "${session.provider}"`);
|
|
18238
18565
|
const baseUrl = resolveBaseUrl();
|
|
18239
18566
|
if (!baseUrl) throw new Error("Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)");
|
|
18240
|
-
const authProvider = new StateBackedMcpOAuthClientProvider(authSessionId, `${baseUrl}${getMcpOAuthCallbackPath(provider)}`, void 0, runCredentialMutation);
|
|
18567
|
+
const authProvider = new StateBackedMcpOAuthClientProvider(authSessionId, `${baseUrl}${getMcpOAuthCallbackPath(provider)}`, void 0, runCredentialMutation, mcp.oauthClient);
|
|
18241
18568
|
const headers = mcp.headers ? resolvePluginHeaderEnvRefs(provider, mcp.headers, "MCP header") : void 0;
|
|
18242
18569
|
let providerStatus;
|
|
18243
18570
|
const transport = new StreamableHTTPClientTransport(new URL(mcp.url), {
|
|
@@ -18431,6 +18758,10 @@ function defaultGrantForProvider(input) {
|
|
|
18431
18758
|
}
|
|
18432
18759
|
};
|
|
18433
18760
|
}
|
|
18761
|
+
function hasGenericCredentials(provider) {
|
|
18762
|
+
const manifest = pluginCatalogRuntime.getDefinition(provider)?.manifest;
|
|
18763
|
+
return Boolean(manifest?.credentials || manifest?.apiHeaders);
|
|
18764
|
+
}
|
|
18434
18765
|
function oauthAuthorizationForProvider(provider) {
|
|
18435
18766
|
const oauth = pluginCatalogRuntime.getOAuthConfig(provider);
|
|
18436
18767
|
return oauth ? {
|
|
@@ -18453,6 +18784,8 @@ function assertLeaseTransformsOwnedByProvider(provider, lease) {
|
|
|
18453
18784
|
*
|
|
18454
18785
|
* GitHub GraphQL and other plugin-owned APIs may need body-aware grant choices;
|
|
18455
18786
|
* providers without hooks use a simple read/write default based on HTTP method.
|
|
18787
|
+
* A hook plugin that also declares generic credentials can return no grant to
|
|
18788
|
+
* use that default for one request.
|
|
18456
18789
|
*/
|
|
18457
18790
|
async function selectSandboxEgressGrant(input) {
|
|
18458
18791
|
if (!hasEgressCredentialHooks(input.provider)) return defaultGrantForProvider(input);
|
|
@@ -18463,7 +18796,10 @@ async function selectSandboxEgressGrant(input) {
|
|
|
18463
18796
|
method: input.method,
|
|
18464
18797
|
upstreamUrl: input.upstreamUrl
|
|
18465
18798
|
});
|
|
18466
|
-
if (!pluginGrant)
|
|
18799
|
+
if (!pluginGrant) {
|
|
18800
|
+
if (hasGenericCredentials(input.provider)) return defaultGrantForProvider(input);
|
|
18801
|
+
throw new Error(`Plugin "${input.provider}" grantForEgress must return a grant for sandbox egress`);
|
|
18802
|
+
}
|
|
18467
18803
|
return {
|
|
18468
18804
|
source: "plugin",
|
|
18469
18805
|
grant: pluginGrant
|
|
@@ -21476,7 +21812,7 @@ function createAgentRunner(execute, options) {
|
|
|
21476
21812
|
} } : void 0
|
|
21477
21813
|
};
|
|
21478
21814
|
if (run.instruction.storedAttachments?.length) {
|
|
21479
|
-
const { loadInputImages } = await import("./images-
|
|
21815
|
+
const { loadInputImages } = await import("./images-D6Y3FJDQ.js").then((n) => n.n);
|
|
21480
21816
|
const storage = nextRun.environment?.attachmentStorage;
|
|
21481
21817
|
if (!storage) throw new Error("Attachment storage is unavailable.");
|
|
21482
21818
|
nextRun.instruction = {
|
|
@@ -21527,4 +21863,4 @@ async function runAgentWithTimeout(agentRunner, run, timeoutMs) {
|
|
|
21527
21863
|
}
|
|
21528
21864
|
}
|
|
21529
21865
|
//#endregion
|
|
21530
|
-
export {
|
|
21866
|
+
export { effectiveTaskOutcomes as $, requestConversationWork as $t, requestAttachmentDeletion as A, addReactionToMessage as An, ackMessages as At, logScheduledAutomationRunSkipped as B, AuthorizationFlowDisabledError as Bn, countPendingConversationMessages as Bt, isExperimentalFeatureEnabled as C, startOAuthFlow as Cn, processWorkspaceSnapshotJob as Ct, renderBlockText as D, normalizeSlackStatusText as Dn, getEventCatalog as Dt, renderAttachmentText as E, normalizeSlackReplyMarkdown as En, isEventMailboxMetadata as Et, resolveSlackChannelIdFromThreadId as F, createUserTokenStore as Fn, checkInConversationWork as Ft, readScheduledAutomation as G, getConversationWorkState as Gt, listScheduledAutomations as H, getConfigDefaults as Hn, drainConversationMailbox as Ht, getConversationInfo as I, buildSteeringPiMessage as In, clearConsumedConversationWake as It, scheduledRunId as J, recordAttemptFailure as Jt, saveScheduledAutomation as K, hasRunnableConversationWork as Kt, listThreadReplies as L, RetryableDeliveryError as Ln, clearExpiredConversationLease as Lt, parseSlackThreadId as M, removeReactionFromMessage as Mn, appendAndEnqueueInboundMessage as Mt, readSlackRawMessageContext as N, slackEntitySchema as Nn, beginConversationResume as Nt, collectAttachmentGarbage as O, slackMention as On, eventGuidance as Ot, resolveSlackChannelIdFromMessage as P, slackExternalRefIdSchema as Pn, cancelHumanFacingPendingMessages as Pt, eventAutomationTriggerAvailable as Q, requestAnotherSlice as Qt, editScheduledAutomation as R, actorFromRun as Rn, completeConversationStop as Rt, getExperimentalFeatures as S, resolveBaseUrl as Sn, getMcpProviderErrorAttributes as St, lookupSlackUserProfile as T, formatSlackLink as Tn, EVENT_WAIT_MS as Tt, listScheduledAutomationsForTeam as U, setConfigDefaults as Un, ensureConversationWake as Ut, scheduledAutomationRunAttributes as V, shouldEmitDevAgentTrace as Vn, deadLetterAttempt as Vt, parseScheduledAutomationRow as W, getConversation as Wt, editEventAutomation as X, releaseConversationWork as Xt, withScheduledAutomationLock as Y, recordConversationRetry as Yt, AutomationEditError as Z, removeActiveConversation as Zt, resolvePluginOAuthAccount as _, consumeSandboxEgressPermissionDeniedSignal as _n, agentBindingSchema as _t, executeCredentialedEgressRequest as a, hasRunnableConversationWork$1 as an, getEventAutomation as at, unpublishedArtifactHeaders as b, formatProviderLabel as bn, AgentInvocationBusyError as bt, deleteMcpAuthSessionsForUserProvider as c, renderTaskInput as cn, saveEventAutomation as ct, getMcpAuthSession as d, resolveSandboxEgressProviderForHost as dn, listAutomationVersions as dt, startConversationWork as en, resolveTaskOutcomes as et, getMcpStoredOAuthCredentials as f, normalizeSandboxEgressTracePropagationDomains as fn, readAutomationVersion as ft, readStats as g, consumeSandboxEgressAuthRequiredSignal as gn, requireAutomationRevision as gt, latestProgressStatus as h, clearSandboxEgressSignals as hn, automationRevision as ht, createContextCompactor as i, hasConversationStop as in, findMatchingEventAutomations as it, storeAttachment as j, postSlackMessage as jn, appendAndEnqueueExclusiveInboundMessage as jt, readLiveAttachment as k, truncateStatusText as kn, pluginEventCatalog as kt, deleteMcpServerSessionId as l, maybePostAutomatedTurnLimitNotice as ln, setEventAutomationStatus as lt, PluginCredentialFailureError as m, SANDBOX_EGRESS_PROXY_PATH as mn, AutomationConflictError as mt, runAgentWithTimeout as n, CONVERSATION_WORK_CHECK_IN_INTERVAL_MS as nn, deleteEventAutomation as nt, finalizeMcpAuthorization as o, isFinalAttempt as on, listDeletedEventAutomationsCreatedBy as ot, putMcpStoredOAuthCredentials as p, shouldPropagateSandboxEgressTrace as pn, sameDefinitionValue as pt, saveScheduledAutomationInLock as q, listActiveConversationIds as qt, executeAgentRun as r, CONVERSATION_WORK_STALE_ENQUEUE_MS as rn, eventAutomationBelongsToUser as rt, deleteMcpAuthSession as s, isInvalidConversationRecordError as sn, parseEventAutomationRow as st, createAgentRunner as t, stopConversationWork as tn, collectEventAutomationMatchKeys as tt, deleteMcpStoredOAuthCredentials as u, recordFinishedTurnForAutomatedLimit as un, automationDefinition as ut, publicArtifactHeaders as v, parseSandboxEgressCredentialToken as vn, agentInvocationSchema as vt, setExperimentalFeatures as w, escapeSlackMrkdwnText as wn, ingestEvent as wt, experimental_exports as x, parseOAuthStatePayload as xn, AgentInvocationLimitError as xt, readPublicArtifact as y, sandboxEgressSignalsResponseSchema as yn, createAgentInvocationSchema as yt, lookupSlackUser as z, toolInvocationDestination as zn, completeConversationWork as zt };
|