@sentry/junior 0.244.0 → 0.246.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/{admin-CF9lQg0M.js → admin-Bh-MYw-j.js} +1 -1
  2. package/dist/{agent-hooks-Cj_rSeWd.js → agent-hooks-DpklSWZg.js} +10 -10
  3. package/dist/{agent-runner-DtEtbaOb.js → agent-runner-DSKpHjHW.js} +568 -191
  4. package/dist/api/schema.d.ts +86 -3
  5. package/dist/api/schema.js +3 -3
  6. package/dist/api.js +372 -245
  7. package/dist/{app-DRLh-8zs.d.ts → app-BYB7szGb.d.ts} +2 -0
  8. package/dist/app.d.ts +1 -1
  9. package/dist/app.js +179 -146
  10. package/dist/{briefs-BMzTiGJO.js → briefs-cZ5vWqP_.js} +4 -4
  11. package/dist/{catalog-runtime-CpxhLwZG.js → catalog-runtime-CTaQXYqS.js} +1 -1
  12. package/dist/cli/chat.js +17 -13
  13. package/dist/cli/check.js +3 -3
  14. package/dist/cli/main.js +2 -2
  15. package/dist/cli/plugins.js +4 -4
  16. package/dist/cli/snapshot-create.js +3 -3
  17. package/dist/cli/upgrade.js +3 -3
  18. package/dist/{client-BVaGEb1-.js → client--xtsAxKP.js} +2 -2
  19. package/dist/{config-q9xXVN_5.js → config-BK53Ip5C.js} +11 -2
  20. package/dist/{conversation-BxvgH64Q.js → conversation-CvBG57Io.js} +11 -1
  21. package/dist/{conversation-privacy-Dyh_63Ee.js → conversation-privacy-BDBFw6Ws.js} +1 -1
  22. package/dist/{credential-sync-Bsn-6m16.js → credential-sync-BiuhC9Ds.js} +14 -14
  23. package/dist/{executor-DA7eHhmC.js → executor-bmmuRY-w.js} +2 -2
  24. package/dist/{images-CvMreau5.js → images-DZliDtSH.js} +3 -3
  25. package/dist/instrumentation.js +2 -2
  26. package/dist/{job-queue-mVyF9jgv.js → job-queue-Cz3M7gg4.js} +1 -1
  27. package/dist/{logging-z7_w-KN_.js → logging-DVtMYOdD.js} +1 -1
  28. package/dist/message-BSN-Ma5f.js +317 -0
  29. package/dist/nitro.d.ts +1 -1
  30. package/dist/nitro.js +3 -3
  31. package/dist/{oauth-authorization-Db1yGVvG.js → oauth-authorization-By_MVRDC.js} +1 -1
  32. package/dist/{oauth-callback-server-_MfOtSxS.js → oauth-callback-server-BrUo6w1e.js} +2 -2
  33. package/dist/{pending-auth-OndKPW4M.js → pending-auth-BuQLZ6dK.js} +2 -2
  34. package/dist/{profile-yeKVNECl.js → profile-BevMNSlr.js} +4 -4
  35. package/dist/{prompt-CX36ZlbV.js → prompt-C9Q9cjD3.js} +1 -1
  36. package/dist/{registry-DvzzL8-v.js → registry-C2rSEio1.js} +1 -1
  37. package/dist/{resolve-4u5gEU04.js → resolve-qPhrznTf.js} +2 -2
  38. package/dist/{runner-Y6A6pww4.js → runner-CNh5eMWx.js} +10 -10
  39. package/dist/{sandbox-egress-signals-D7AOkuoe.js → sandbox-egress-signals-BnDEKu8d.js} +1 -1
  40. package/dist/{schema-Dyyi9oRc.js → schema-BWMRg8JS.js} +28 -4
  41. package/dist/{skills-DFduSvuO.js → skills-C6zJbudq.js} +2 -2
  42. package/dist/{spawn-DYg7jhhR.js → spawn-C3efZSx3.js} +2 -2
  43. package/dist/{task-queue-BQ0vnumm.js → task-queue-BX4vG6uA.js} +3 -3
  44. package/dist/{task-runner-D4jvUpFV.js → task-runner-T51kll0U.js} +10 -10
  45. package/dist/{tasks-DBZAgTx7.js → tasks-B9OIjl2K.js} +82 -11
  46. package/dist/{thread-state-hj70P2Cp.js → thread-state-AGZtVyXp.js} +6 -6
  47. package/dist/{turn-failure-response-S2pwtR8b.js → turn-failure-response-DJ8KU2bo.js} +9 -9
  48. package/dist/{turn-lifecycle-CNFOMAJq.js → turn-lifecycle-DZJkJ95q.js} +1 -1
  49. package/dist/{turn-session-routing-1eTgv6ns.js → turn-session-routing-mRqLbMZW.js} +1 -1
  50. package/dist/{validation-DcsBjzdi.js → validation-Dg0Ihxzo.js} +1 -1
  51. package/dist/{version-DVCHJYcY.js → version-Deub4try.js} +1 -1
  52. package/dist/version.js +1 -1
  53. package/dist/{viewer-BBnCMR1g.js → viewer-D5hbx6TE.js} +3 -3
  54. package/dist/{web-authorization-CTltRLsB.js → web-authorization-DCxixz5S.js} +6 -6
  55. package/dist/{web-input-CIsRGF07.js → web-input-0sorYVLh.js} +74 -187
  56. package/dist/{work-fje9VXgQ.js → work-rm2X7jj4.js} +13 -6
  57. package/dist/{worker-Byd2Et9r.js → worker-bnGsAOP3.js} +5 -5
  58. package/package.json +4 -4
@@ -1,28 +1,28 @@
1
- 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-BVaGEb1-.js";
2
- 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, u as logError, v as setSpanAttributes, w as withSpan, x as summarizeMessageText, y as setSpanStatus } from "./logging-z7_w-KN_.js";
1
+ 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--xtsAxKP.js";
2
+ 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, u as logError, v as setSpanAttributes, w as withSpan, x as summarizeMessageText, y as setSpanStatus } from "./logging-DVtMYOdD.js";
3
3
  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-Dyh_63Ee.js";
5
- import { i as getChatConfig, n as botConfig, v as normalizeSlackEmojiName } from "./config-q9xXVN_5.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-BDBFw6Ws.js";
5
+ import { i as getChatConfig, n as botConfig, v as normalizeSlackEmojiName } from "./config-BK53Ip5C.js";
6
6
  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";
7
7
  import { a as isUserActor, d as parseSlackTeamId, f as parseSlackUserId, g as parseSlackMessageTs, l as parseSlackChannelId, s as parseStoredSlackActor, u as parseSlackChannelReferenceId } from "./actor-BTtLaeWy.js";
8
- import { F as sameDestination$1, N as parseDestination, T as hasAgentTurnUsage, c as getConversationEventStore, d as getDb, f as getSqlExecutor, l as getConversationMessageSearchStore, r as readActorIdentity, u as getConversationStore, x as addAgentTurnUsage } from "./viewer-BBnCMR1g.js";
9
- import { a as juniorAgentInvocations, c as juniorIdentities, d as juniorDestinations, i as juniorAgentBindings, n as AGENT_INVOCATION_PARENT_MAILBOX_STATUSES, r as AGENT_INVOCATION_STATUSES, s as juniorConversations, t as AGENT_INVOCATION_MAILBOX_STATUSES } from "./agent-invocations-BO6ddZax.js";
10
- import { C as juniorArtifacts, S as juniorAttachments, a as juniorSchedulerRuns, l as juniorStats, m as juniorConversationBriefs, y as juniorConversationAnnotations } from "./executor-DA7eHhmC.js";
8
+ import { F as sameDestination$1, N as parseDestination, T as hasAgentTurnUsage, c as getConversationEventStore, d as getDb, f as getSqlExecutor, l as getConversationMessageSearchStore, r as readActorIdentity, u as getConversationStore, x as addAgentTurnUsage } from "./viewer-D5hbx6TE.js";
9
+ import { a as juniorAgentInvocations, c as juniorIdentities, d as juniorDestinations, i as juniorAgentBindings, n as AGENT_INVOCATION_PARENT_MAILBOX_STATUSES, s as juniorConversations, t as AGENT_INVOCATION_MAILBOX_STATUSES } from "./agent-invocations-BO6ddZax.js";
10
+ import { C as juniorArtifacts, S as juniorAttachments, a as juniorSchedulerRuns, l as juniorStats, m as juniorConversationBriefs, y as juniorConversationAnnotations } from "./executor-bmmuRY-w.js";
11
11
  import { d as instructionActors, f as instructionProvenanceFor, p as sameActorIdentity, t as KNOWN_CONVERSATION_EVENT_TYPES, u as contextProvenance } from "./history-Nte1oVed.js";
12
12
  import { a as listReferenceFiles, c as soulPathCandidates, l as worldPathCandidates } from "./package-discovery-BVvSnQBB.js";
13
- import { a as resolveAuthTokenPlaceholder, c as fetchWithBoundedOAuthErrorBodies, d as credentialContextForActor, f as credentialContextSchema, h as CredentialUnavailableError, i as resolvePluginHeaderEnvRefs, l as getOAuthProviderErrorAttributes, m as credentialUserSubjectId, o as resolvePluginCommandEnv, p as credentialSubjectSchema, s as OAuthProviderError } from "./registry-DvzzL8-v.js";
14
- import { n as pluginCatalogRuntime } from "./catalog-runtime-CpxhLwZG.js";
15
- import { a as parseSkillInvocation, i as parseSkillFile, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-DFduSvuO.js";
16
- import { A as formatSlackLink, At as recordAttachmentsDelivered, Ct as loadConversationProjection, D as automationRevision, Dt as loadTurnRoute, F as addReactionToMessage, Ft as recordMcpProviderConnected, G as loadTurnCheckpoint, I as getSlackMessagePermalink, K as saveTurnCheckpoint, Kt as fallbackShortTitle, L as openSlackDirectMessage, Lt as recordToolExecutionStarted, N as slackMention, Nt as recordAuthorizationRequested, Ot as openConversationProjection, Pt as recordGuardianActionReviewed, R as postSlackEphemeralMessage, Rt as recordTurnRoute, St as loadConnectedMcpProviders, V as uploadFilesToConversation, W as continuableMessages, _t as trimTrailingAssistantMessages, a as readScheduledAutomation, an as estimateTextTokens, at as getPiMessageRole, b as saveEventAutomation, cn as historyItemFromPiMessage, ct as hasRuntimeTurnContext, dt as isContinuablePiBoundary, et as TurnSliceLimitExceededError, f as createEventAutomation, ft as isToolResultError, g as getEventAutomation, gt as stripRuntimeTurnContext, ht as retainRuntimeTurnContext, it as extractAssistantText, k as escapeSlackMrkdwnText, kt as recordAgentsInstructionsUpdated, ln as piMessageFromHistoryItem, mt as normalizeToolNameFromResult, o as saveScheduledAutomation, on as getAgentContextCompactionTriggerTokens, ot as getTerminalAssistantMessages, p as deleteEventAutomation, pt as isToolResultMessage, qt as resolveTaskTitle, r as listScheduledAutomationsForTeam, rt as isTurnExecutionLimitExceededError, sn as getAgentContextInputLimitTokens, st as getUserMessageInstructionText, t as createScheduledAutomation, tt as assertTurnToolCallLimit, ut as isAssistantMessage, v as listEventAutomationsForTeam, vt as TURN_CONTEXT_TAG, wt as loadLastModelProfile, x as setEventAutomationStatus, yt as ToolInputError, z as postSlackMessage } from "./tasks-DBZAgTx7.js";
17
- import { A as getWorkspaceSnapshotBuilder, B as cancelWatch, Bt as isConversationScopedChannel, C as WorkspaceValidationError, D as setWorkspaceSnapshotBuild, E as setWorkspaceSnapshot, F as RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS, Ft as SlackActionError, G as findMatchingWatches, H as createTimerWatch, Ht as normalizeSlackConversationId, I as RESOURCE_SUBSCRIPTION_MAX_TTL_MS, It as downloadPrivateSlackFile, K as listWatches, L as RESOURCE_WATCH_TOOL_SOURCE, Lt as getFilePermalink, M as workspaceRepoCheckoutPath, Mt as defaultTimezone, N as resolveChannelCapabilities, Nt as resolveUserTimezone, O as SANDBOX_RUNTIME, Ot as saveObjectAnnotations, P as getSlackToolContext, R as STOP_WATCHING_TOOL_NAME, S as updateWorkspace, T as loadSnapshotsForProfile, U as createWatch, Vt as isDmChannel$1, W as deliverWatch, Wt as withSlackRetries, _ as deleteWorkspace, b as listWorkspaces, c as getPluginTools, et as verifyTimerWatchCredentialSubject, g as createWorkspace, it as getDashboardTaskLink, j as tryWorkspaceRepoCheckoutPath, k as deleteWorkspaceSnapshotBuilders, kt as createPluginAnnotations, l as getPluginUserPromptContributions, lt as createPluginLogger, nt as getDashboardConversationLink, r as createPluginHookRunner, s as getPluginSystemPromptContributions, tt as JUNIOR_THREAD_STATE_TTL_MS, u as getPlugins, v as getWorkspace, w as invalidateReadySnapshot, wt as matchSlackAttachments, y as getWorkspaceByName, z as cancelSubscriptions, zt as getSlackClient } from "./agent-hooks-Cj_rSeWd.js";
18
- import { F as isVisionImageMediaType, G as removedCardSchema, L as briefLinkSchema, R as briefOutcomeStatusSchema, z as conversationBriefSchema } from "./conversation-BxvgH64Q.js";
19
- 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-yeKVNECl.js";
20
- import { n as canReusePendingAuthLink, t as abandonReplacedPendingAuth } from "./pending-auth-OndKPW4M.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-Db1yGVvG.js";
13
+ import { a as resolveAuthTokenPlaceholder, c as fetchWithBoundedOAuthErrorBodies, d as credentialContextForActor, f as credentialContextSchema, h as CredentialUnavailableError, i as resolvePluginHeaderEnvRefs, l as getOAuthProviderErrorAttributes, m as credentialUserSubjectId, o as resolvePluginCommandEnv, p as credentialSubjectSchema, s as OAuthProviderError } from "./registry-C2rSEio1.js";
14
+ import { n as pluginCatalogRuntime } from "./catalog-runtime-CTaQXYqS.js";
15
+ import { a as parseSkillInvocation, i as parseSkillFile, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-C6zJbudq.js";
16
+ import { At as openConversationProjection, B as postSlackEphemeralMessage, Bt as recordTurnRoute, Et as loadLastModelProfile, F as slackMention, Ft as recordAuthorizationRequested, It as recordGuardianActionReviewed, J as saveTurnCheckpoint, Jt as fallbackShortTitle, K as continuableMessages, L as addReactionToMessage, Lt as recordMcpProviderConnected, M as formatSlackLink, Mt as recordAttachmentsDelivered, R as getSlackMessagePermalink, Tt as loadConversationProjection, U as uploadFilesToConversation, V as postSlackMessage, Yt as resolveTaskTitle, _t as retainRuntimeTurnContext, a as readScheduledAutomation, at as isTurnExecutionLimitExceededError, b as saveEventAutomation, bt as TURN_CONTEXT_TAG, cn as getAgentContextCompactionTriggerTokens, ct as getTerminalAssistantMessages, dn as piMessageFromHistoryItem, f as createEventAutomation, ft as isAssistantMessage, g as getEventAutomation, gt as normalizeToolNameFromResult, ht as isToolResultMessage, j as escapeSlackMrkdwnText, jt as recordAgentsInstructionsUpdated, k as automationRevision, kt as loadTurnRoute, ln as getAgentContextInputLimitTokens, lt as getUserMessageInstructionText, mt as isToolResultError, nt as TurnSliceLimitExceededError, o as saveScheduledAutomation, ot as extractAssistantText, p as deleteEventAutomation, pt as isContinuablePiBoundary, q as loadTurnCheckpoint, r as listScheduledAutomationsForTeam, rt as assertTurnToolCallLimit, sn as estimateTextTokens, st as getPiMessageRole, t as createScheduledAutomation, un as historyItemFromPiMessage, ut as hasRuntimeTurnContext, v as listEventAutomationsForTeam, vt as stripRuntimeTurnContext, wt as loadConnectedMcpProviders, x as setEventAutomationStatus, xt as ToolInputError, yt as trimTrailingAssistantMessages, z as openSlackDirectMessage, zt as recordToolExecutionStarted } from "./tasks-B9OIjl2K.js";
17
+ import { A as getWorkspaceSnapshotBuilder, B as cancelWatch, Bt as isConversationScopedChannel, C as WorkspaceValidationError, D as setWorkspaceSnapshotBuild, E as setWorkspaceSnapshot, F as RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS, Ft as SlackActionError, G as findMatchingWatches, H as createTimerWatch, Ht as normalizeSlackConversationId, I as RESOURCE_SUBSCRIPTION_MAX_TTL_MS, It as downloadPrivateSlackFile, K as listWatches, L as RESOURCE_WATCH_TOOL_SOURCE, Lt as getFilePermalink, M as workspaceRepoCheckoutPath, Mt as defaultTimezone, N as resolveChannelCapabilities, Nt as resolveUserTimezone, O as SANDBOX_RUNTIME, Ot as saveObjectAnnotations, P as getSlackToolContext, R as STOP_WATCHING_TOOL_NAME, S as updateWorkspace, T as loadSnapshotsForProfile, U as createWatch, Vt as isDmChannel$1, W as deliverWatch, Wt as withSlackRetries, _ as deleteWorkspace, b as listWorkspaces, c as getPluginTools, et as verifyTimerWatchCredentialSubject, g as createWorkspace, it as getDashboardTaskLink, j as tryWorkspaceRepoCheckoutPath, k as deleteWorkspaceSnapshotBuilders, kt as createPluginAnnotations, l as getPluginUserPromptContributions, lt as createPluginLogger, nt as getDashboardConversationLink, r as createPluginHookRunner, s as getPluginSystemPromptContributions, tt as JUNIOR_THREAD_STATE_TTL_MS, u as getPlugins, v as getWorkspace, w as invalidateReadySnapshot, wt as matchSlackAttachments, y as getWorkspaceByName, z as cancelSubscriptions, zt as getSlackClient } from "./agent-hooks-DpklSWZg.js";
18
+ import { B as conversationBriefSchema, I as isVisionImageMediaType, K as removedCardSchema, R as briefLinkSchema, z as briefOutcomeStatusSchema } from "./conversation-CvBG57Io.js";
19
+ 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-BevMNSlr.js";
20
+ import { n as canReusePendingAuthLink, t as abandonReplacedPendingAuth } from "./pending-auth-BuQLZ6dK.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-By_MVRDC.js";
22
22
  import { i as isValidTimeZone, r as getZonedDateTimeParts } from "./cadence-D00SCzT1.js";
23
- import { r as EVENT_AUTHOR_ID } from "./prompt-CX36ZlbV.js";
24
- 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-4u5gEU04.js";
25
- import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-mVyF9jgv.js";
23
+ import { r as EVENT_AUTHOR_ID } from "./prompt-C9Q9cjD3.js";
24
+ 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-qPhrznTf.js";
25
+ import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-Cz3M7gg4.js";
26
26
  import { F as taskOutcomeInputSchema, M as compileScheduleIntent, N as scheduleIntentSchema, O as automationInstructionToolSchema, T as SCHEDULED_AUTOMATION_SYSTEM_ACTOR, j as ScheduleIntentError, k as automationTitleSchema } from "./automation-gznOMaQ2.js";
27
27
  import { r as isExperimentalFeatureEnabled } from "./experimental-Qn9rKLgx.js";
28
28
  import { n as privateTraceResultAttributes } from "./private-trace-result-BLvBjy_e.js";
@@ -34,7 +34,7 @@ import { isDeepStrictEqual } from "node:util";
34
34
  import { AsyncLocalStorage } from "node:async_hooks";
35
35
  import { z } from "zod";
36
36
  import { createHash, createHmac, randomBytes, randomUUID, timingSafeEqual } from "node:crypto";
37
- import { and, asc, count, desc, eq, gt, gte, inArray, isNotNull, isNull, lt, lte, ne, notExists, or, sql } from "drizzle-orm";
37
+ import { and, asc, count, countDistinct, desc, eq, gt, gte, inArray, isNotNull, isNull, lt, lte, ne, notExists, or, sql } from "drizzle-orm";
38
38
  import { EgressAuthRequired, EgressPolicyDenied, PluginToolInputError, actorSchema, destinationSchema, eventMatchSchema, eventSchema, missingToolAnnotationKeys, normalizeEventIdentifier, objectTypeSchema, ownedObjectAnnotationSchema, pluginAuthorizationSchema, pluginCredentialHeaderTransformSchema, pluginCredentialResultSchema, pluginGrantSchema, pluginProviderAccountSchema, pluginStoredTokensSchema, sourceSchema, stableEventMatchKey, watchResultSchema } from "@sentry/junior-plugin-api";
39
39
  import { alias } from "drizzle-orm/pg-core";
40
40
  import { Agent, estimateContextTokens } from "@earendil-works/pi-agent-core";
@@ -895,6 +895,26 @@ function remindMissingAutomationResult(messages) {
895
895
  }];
896
896
  }
897
897
  //#endregion
898
+ //#region src/chat/agent-invocations/blocked-report.ts
899
+ /** Blocked report of a child agent. See "Blocked Reports" in README.md. */
900
+ /** Model-facing tool that ends a child agent run as blocked. */
901
+ const REPORT_BLOCKED_TOOL_NAME = "reportBlocked";
902
+ const blockedReportSchema = z.object({ missing: z.string().trim().min(1) });
903
+ /**
904
+ * Read the last successful blocked report from the new messages of one run.
905
+ *
906
+ * Do not pass the whole history. A named child keeps its history, so a report
907
+ * from an earlier agent invocation must not end the current one.
908
+ */
909
+ function readBlockedReport(messages) {
910
+ for (let index = messages.length - 1; index >= 0; index -= 1) {
911
+ const message = messages[index];
912
+ if (!isToolResultMessage(message) || isToolResultError(message) || normalizeToolNameFromResult(message) !== "reportBlocked") continue;
913
+ const parsed = blockedReportSchema.safeParse(message.details);
914
+ if (parsed.success) return parsed.data;
915
+ }
916
+ }
917
+ //#endregion
898
918
  //#region src/chat/services/turn-result.ts
899
919
  /** Process raw agent messages into a structured AgentRunResult. */
900
920
  function buildTurnResult(input) {
@@ -903,6 +923,7 @@ function buildTurnResult(input) {
903
923
  const toolResults = newMessages.filter(isToolResultMessage);
904
924
  const assistantMessages = newMessages.filter(isAssistantMessage);
905
925
  const terminalAssistantMessages = getTerminalAssistantMessages(newMessages);
926
+ const blocked = readBlockedReport(newMessages);
906
927
  const rawPrimaryText = sanitizeAssistantText(terminalAssistantMessages.map((message) => extractAssistantText(message)).join("\n\n")).trim();
907
928
  const primaryText = terminalAssistantMessages.map((message) => decideReply(message)).filter((output) => output.kind === "deliver").map((output) => output.text).join("\n\n");
908
929
  const terminalTexts = terminalAssistantMessages.map((message) => sanitizeAssistantText(extractAssistantText(message)));
@@ -922,7 +943,7 @@ function buildTurnResult(input) {
922
943
  };
923
944
  if (!isProviderError) logInfo("ai.no_reply_marker.accepted", markerAttributes);
924
945
  }
925
- if (!primaryText && !completedWithoutTerminalText && !isProviderError) logWarn("ai.model_response.empty", {
946
+ if (!primaryText && !completedWithoutTerminalText && !blocked && !isProviderError) logWarn("ai.model_response.empty", {
926
947
  "app.ai.tool_results": toolResults.length,
927
948
  "app.ai.tool_error_results": toolErrorCount,
928
949
  "app.ai.generated_files": input.generatedFileCount
@@ -932,7 +953,7 @@ function buildTurnResult(input) {
932
953
  let outcome;
933
954
  if (isProviderError) outcome = "provider_error";
934
955
  else if (suppressedPrimaryText) outcome = "execution_failure";
935
- else if (primaryText || completedWithoutTerminalText) outcome = "success";
956
+ else if (primaryText || completedWithoutTerminalText || blocked) outcome = "success";
936
957
  else outcome = "execution_failure";
937
958
  if (shouldTrace) logInfo("agent.message.generated", {
938
959
  "app.message.kind": "assistant_outbound",
@@ -944,6 +965,7 @@ function buildTurnResult(input) {
944
965
  });
945
966
  return {
946
967
  text: primaryText,
968
+ ...blocked ? { blocked } : void 0,
947
969
  sandboxRef,
948
970
  piMessages: input.piMessages,
949
971
  diagnostics: buildDiagnostics(input, {
@@ -1873,12 +1895,19 @@ const AUTOMATION_RUN_RULES = [
1873
1895
  "- A tool result with `timed_out: true` means that attempt did not finish. Before you retry work that may have side effects, inspect authoritative state and do not repeat a mutation that already applied.",
1874
1896
  `- Assistant text is never delivered. End the run with \`${FINISH_AUTOMATION_RUN_TOOL_NAME}\`. Do not post the result with another tool.`
1875
1897
  ];
1898
+ const CHILD_AGENT_RUN_RULES = [
1899
+ "- This run is a task that a parent agent gave you. The task is the job. The parent agent reads your final reply; no person reads this run.",
1900
+ "- Do the work now. Do not ask questions, ask for approval, or offer options.",
1901
+ `- Your final text reply goes to the parent as finished work. When the task cannot be finished, end the run with \`${REPORT_BLOCKED_TOOL_NAME}\` instead.`,
1902
+ "- A tool result with `timed_out: true` means that attempt did not finish. Before you retry work that may have side effects, inspect authoritative state and do not repeat a mutation that already applied."
1903
+ ];
1876
1904
  const SAFETY_RULES = [
1877
1905
  "- Stay within the user's request and the runtime's available capabilities; do not pursue independent goals, persistence, replication, credential gathering, or access expansion.",
1878
1906
  "- Respect stop, pause, audit, and approval boundaries. Do not bypass safeguards or persuade the user to weaken them.",
1879
1907
  "- Do not change system prompts, tool policies, security settings, credentials, or runtime configuration unless the user explicitly requests that exact administrative action and an available tool permits it."
1880
1908
  ];
1881
1909
  const AUTOMATION_FAILURE_RULES = ["- Keep commands, error output, raw tool payloads, and internal routing metadata out of the message.", `- When a failure stops the job, put its cause in the \`${FINISH_AUTOMATION_RUN_TOOL_NAME}\` reason, not in the message.`];
1910
+ const CHILD_AGENT_FAILURE_RULES = [`- When a failure or a missing part stops the task, put what you checked and what is missing in \`${REPORT_BLOCKED_TOOL_NAME}\`, not in a text reply.`, "- Keep raw tool payloads and internal routing metadata out of the final reply."];
1882
1911
  const FAILURE_RULES = [
1883
1912
  "- For tool/runtime failures, run the named check before diagnosing and report the exact failed command plus stderr/exit code.",
1884
1913
  "- If a fact cannot be verified after focused checks, say what you checked and what blocked a stronger answer.",
@@ -1892,7 +1921,7 @@ function renderRuleSection(tag, lines) {
1892
1921
  ].join("\n");
1893
1922
  }
1894
1923
  function buildBehaviorSection(platform, mode) {
1895
- const runSections = mode === "automation" ? [renderRuleSection("automation-run", AUTOMATION_RUN_RULES)] : [
1924
+ const runSections = mode === "automation" ? [renderRuleSection("automation-run", AUTOMATION_RUN_RULES)] : mode === "child_agent" ? [renderRuleSection("child-agent-run", CHILD_AGENT_RUN_RULES)] : [
1896
1925
  renderRuleSection("task-execution", TASK_EXECUTION_RULES),
1897
1926
  renderRuleSection("conversation", CONVERSATION_RULES),
1898
1927
  ...platform === "slack" ? [renderRuleSection("slack-actions", SLACK_ACTION_RULES)] : []
@@ -1900,12 +1929,12 @@ function buildBehaviorSection(platform, mode) {
1900
1929
  const automation = mode === "automation";
1901
1930
  return [
1902
1931
  renderRuleSection("tool-policy", TOOL_POLICY_RULES),
1903
- ...automation ? [] : [renderRuleSection("tool-call-style", TOOL_CALL_STYLE_RULES)],
1932
+ ...mode !== "conversation" ? [] : [renderRuleSection("tool-call-style", TOOL_CALL_STYLE_RULES)],
1904
1933
  renderRuleSection("skill-policy", SKILL_POLICY_RULES),
1905
1934
  renderRuleSection("planning", PLANNING_RULES),
1906
1935
  ...runSections,
1907
1936
  renderRuleSection("safety", SAFETY_RULES),
1908
- renderRuleSection("failure-handling", automation ? AUTOMATION_FAILURE_RULES : FAILURE_RULES)
1937
+ renderRuleSection("failure-handling", automation ? AUTOMATION_FAILURE_RULES : mode === "child_agent" ? CHILD_AGENT_FAILURE_RULES : FAILURE_RULES)
1909
1938
  ].join("\n\n");
1910
1939
  }
1911
1940
  function buildAutomationOutputSection(platform) {
@@ -1918,6 +1947,13 @@ function buildAutomationOutputSection(platform) {
1918
1947
  }
1919
1948
  function buildOutputSection(platform, mode) {
1920
1949
  if (mode === "automation") return buildAutomationOutputSection(platform);
1950
+ if (mode === "child_agent") return [
1951
+ `<output format="markdown">`,
1952
+ "- Start with the result, not internal process narration.",
1953
+ "- Use concise Markdown: short paragraphs, bullets, links, and fenced code blocks.",
1954
+ "- Reply with text only when the task is finished.",
1955
+ "</output>"
1956
+ ].join("\n");
1921
1957
  if (platform === "local") return [
1922
1958
  `<output format="markdown">`,
1923
1959
  "- Start with the answer or result, not internal process narration.",
@@ -2071,6 +2107,10 @@ const STATIC_SYSTEM_PROMPTS = {
2071
2107
  local: buildStaticSystemPrompt("local", "automation"),
2072
2108
  slack: buildStaticSystemPrompt("slack", "automation")
2073
2109
  },
2110
+ child_agent: {
2111
+ local: buildStaticSystemPrompt("local", "child_agent"),
2112
+ slack: buildStaticSystemPrompt("slack", "child_agent")
2113
+ },
2074
2114
  conversation: {
2075
2115
  local: buildStaticSystemPrompt("local", "conversation"),
2076
2116
  slack: buildStaticSystemPrompt("slack", "conversation")
@@ -2365,7 +2405,7 @@ async function assemblePrompt(args) {
2365
2405
  userContentParts: requestContentParts
2366
2406
  }) : void 0;
2367
2407
  const needsBootstrapContextForPrompt = shouldPromptAgent && !replayedPrompt && !hasRuntimeTurnContext(promptHistoryMessages);
2368
- const promptMode = args.run.dispatch?.declaresResult ? "automation" : "conversation";
2408
+ const promptMode = args.run.dispatch?.declaresResult ? "automation" : args.run.source.kind === "agent_invocation" ? "child_agent" : "conversation";
2369
2409
  const platform = (args.run.delivery || promptMode === "automation") && args.run.location?.provider === "slack" ? "slack" : "local";
2370
2410
  const pluginSystemPrompt = buildPluginSystemPromptContributions(await getPluginSystemPromptContributions(platform));
2371
2411
  const baseInstructions = [buildSystemPrompt(platform, promptMode), pluginSystemPrompt].filter((section) => Boolean(section)).join("\n\n");
@@ -5154,22 +5194,14 @@ function hasEventCatalogEntries(catalog) {
5154
5194
  return Object.keys(catalog).length > 0;
5155
5195
  }
5156
5196
  /**
5157
- * True when at least one plugin namespace is enabled.
5158
- *
5159
- * Core Workspace snapshot events stay in the catalog for search and temporary
5160
- * watches. Durable event automations still require a plugin publisher.
5161
- */
5162
- function hasPluginEventCatalogEntries(catalog) {
5163
- return Object.keys(pluginEventCatalog(catalog)).length > 0;
5164
- }
5165
- /**
5166
5197
  * Catalog entries that durable event automations may select.
5167
5198
  *
5168
5199
  * Core Workspace snapshot events only wake temporary watches. Snapshot finish
5169
5200
  * does not run event-automation ingest, so those namespaces stay out of create/update
5170
- * schemas and trigger availability.
5201
+ * schemas and trigger availability. The core `slack` namespace stays in: Slack
5202
+ * ingress runs event-automation ingest for channel messages.
5171
5203
  */
5172
- function pluginEventCatalog(catalog) {
5204
+ function automationEventCatalog(catalog) {
5173
5205
  return Object.fromEntries(Object.entries(catalog).filter(([namespace]) => namespace !== CORE_EVENT_NAMESPACE));
5174
5206
  }
5175
5207
  function enumSchema(values, unavailableMessage) {
@@ -5301,23 +5333,100 @@ function workspaceSnapshotFinishedEvent(input) {
5301
5333
  return event;
5302
5334
  }
5303
5335
  //#endregion
5336
+ //#region src/chat/slack/channel-message-events.ts
5337
+ /** Core-owned event namespace for Slack Events. Not a plugin publisher. */
5338
+ const SLACK_EVENT_NAMESPACE = "slack";
5339
+ /** The trigger identifier that names the channel of the automation. */
5340
+ const SLACK_CURRENT_CHANNEL_IDENTIFIER = "current_channel";
5341
+ const CHANNEL_RESOURCE_TYPE = "channel";
5342
+ const MESSAGE_POSTED_EVENT = "message.posted";
5343
+ const CHANNEL_ID_PATTERN = /^[CG][A-Z0-9]{2,}$/;
5344
+ /** Event registration for new top-level Slack channel messages. */
5345
+ function slackChannelMessageEvents() {
5346
+ return { resourceTypes: [{
5347
+ type: CHANNEL_RESOURCE_TYPE,
5348
+ identifier: {
5349
+ format: `"${SLACK_CURRENT_CHANNEL_IDENTIFIER}" for the channel where the automation is created`,
5350
+ pattern: /^(?:current_channel|[CG][A-Z0-9]{2,})$/
5351
+ },
5352
+ supportedEvents: [MESSAGE_POSTED_EVENT],
5353
+ suggestedEvents: [],
5354
+ guidance: { [MESSAGE_POSTED_EVENT]: "A person posted a new top-level message in this channel. A reply goes in that message's thread. If the instructions do not apply to this message, end with no action." }
5355
+ }] };
5356
+ }
5357
+ /**
5358
+ * Bind a Slack channel-message automation to its own channel.
5359
+ *
5360
+ * Resolve `current_channel` to the destination channel and reject other
5361
+ * channels, so private channel text cannot reach another channel.
5362
+ */
5363
+ function requireSlackChannelMessageScope(automation) {
5364
+ if (automation.trigger.namespace !== "slack") return automation;
5365
+ const channelId = automation.destination.channelId;
5366
+ if (!CHANNEL_ID_PATTERN.test(channelId)) throw new ToolInputError("A Slack channel-message automation works only in a channel, not in a DM.");
5367
+ const identifier = automation.trigger.identifier === "current_channel" ? channelId : automation.trigger.identifier;
5368
+ if (identifier !== channelId) throw new ToolInputError(`A Slack channel-message automation can watch only the channel where it posts. Use identifier "${SLACK_CURRENT_CHANNEL_IDENTIFIER}", or create the automation in the channel to watch.`);
5369
+ return {
5370
+ ...automation,
5371
+ credentialMode: "system",
5372
+ trigger: {
5373
+ ...automation.trigger,
5374
+ identifier
5375
+ }
5376
+ };
5377
+ }
5378
+ /**
5379
+ * Build the Event for one new top-level channel message, or nothing.
5380
+ *
5381
+ * Thread replies and bot posts, including Slackbot, do not count. Bots can
5382
+ * post often and use up the run limit of the automation.
5383
+ */
5384
+ function slackChannelMessageEvent(input) {
5385
+ if (input.botId || input.subtype === "bot_message" || input.userId === "USLACKBOT") return;
5386
+ if (input.threadTs && input.threadTs !== input.ts) return void 0;
5387
+ if (!CHANNEL_ID_PATTERN.test(input.channelId)) return void 0;
5388
+ return {
5389
+ eventKey: `${input.channelId}:${input.ts}`,
5390
+ eventType: MESSAGE_POSTED_EVENT,
5391
+ identifier: input.channelId,
5392
+ occurredAtMs: Math.floor(Number(input.ts) * 1e3),
5393
+ trustedSummary: `<@${input.userId}> posted a new message in <#${input.channelId}>.`,
5394
+ data: {
5395
+ channelId: input.channelId,
5396
+ messageTs: input.ts,
5397
+ userId: input.userId
5398
+ },
5399
+ ...input.text?.trim() ? { untrustedText: input.text } : void 0
5400
+ };
5401
+ }
5402
+ /** Return the source message thread for a channel-message run. */
5403
+ function slackChannelMessageReplyThreadTs(event) {
5404
+ if (event.namespace !== "slack") return void 0;
5405
+ const messageTs = event.data?.messageTs;
5406
+ return typeof messageTs === "string" ? messageTs : void 0;
5407
+ }
5408
+ //#endregion
5304
5409
  //#region src/chat/events/runtime-catalog.ts
5305
5410
  /**
5306
5411
  * Enabled event registrations for search, guidance, and tool schemas.
5307
5412
  *
5308
- * Includes core Workspace snapshot events under the `junior` namespace, plus
5309
- * every enabled plugin registration. Plugins may not use the `junior` namespace.
5413
+ * Includes core Workspace snapshot events under the `junior` namespace, Slack
5414
+ * channel-message events under the `slack` namespace, plus every enabled plugin
5415
+ * registration. Plugins may not use the `junior` or `slack` namespaces.
5310
5416
  */
5311
5417
  function getEventCatalog() {
5312
- const catalog = { [CORE_EVENT_NAMESPACE]: { resourceTypes: [...workspaceSnapshotEvents().resourceTypes, {
5313
- type: "timer",
5314
- supportedEvents: ["timer.fired"],
5315
- suggestedEvents: []
5316
- }] } };
5418
+ const catalog = {
5419
+ [CORE_EVENT_NAMESPACE]: { resourceTypes: [...workspaceSnapshotEvents().resourceTypes, {
5420
+ type: "timer",
5421
+ supportedEvents: ["timer.fired"],
5422
+ suggestedEvents: []
5423
+ }] },
5424
+ [SLACK_EVENT_NAMESPACE]: slackChannelMessageEvents()
5425
+ };
5317
5426
  for (const plugin of getPlugins()) {
5318
5427
  const registration = plugin.events;
5319
5428
  if (!registration || registration.isEnabled?.() === false) continue;
5320
- if (plugin.manifest.name === "junior") throw new Error(`Plugin "${CORE_EVENT_NAMESPACE}" cannot register events; that namespace is reserved for core events`);
5429
+ if (plugin.manifest.name === "junior" || plugin.manifest.name === "slack") throw new Error(`Plugin "${plugin.manifest.name}" cannot register events; that namespace is reserved for core events`);
5321
5430
  catalog[plugin.manifest.name] = registration;
5322
5431
  }
5323
5432
  return catalog;
@@ -9618,6 +9727,8 @@ function createUpdatePlanTool() {
9618
9727
  //#region src/chat/agent-invocations/types.ts
9619
9728
  const exactStringSchema = z.string().min(1).refine((value) => value === value.trim());
9620
9729
  const agentNameSchema = exactStringSchema.max(64);
9730
+ /** `messageAgent` target for the parent of a child agent. */
9731
+ const PARENT_AGENT_TARGET = "parent";
9621
9732
  const agentInvocationMailboxStatusSchema = z.enum(AGENT_INVOCATION_MAILBOX_STATUSES);
9622
9733
  const agentInvocationParentMailboxStatusSchema = z.enum(AGENT_INVOCATION_PARENT_MAILBOX_STATUSES);
9623
9734
  const agentBindingSchema = z.object({
@@ -9677,13 +9788,19 @@ const createAgentInvocationSchema = z.object({
9677
9788
  reasoningLevel: z.enum(TURN_REASONING_LEVELS).optional(),
9678
9789
  title: exactStringSchema.optional()
9679
9790
  }).strict();
9791
+ /** How a finished task of a child agent ended. A stop is not a result. */
9792
+ const CHILD_AGENT_RESULTS = [
9793
+ "completed",
9794
+ "blocked",
9795
+ "failed"
9796
+ ];
9680
9797
  //#endregion
9681
9798
  //#region src/chat/agent-invocations/errors.ts
9682
- /** Named child cannot accept another invocation until its active work ends. */
9683
- var AgentInvocationBusyError = class extends Error {
9799
+ /** A child agent already uses this name in the parent Conversation. */
9800
+ var AgentNameInUseError = class extends Error {
9684
9801
  constructor(name) {
9685
- super(`Named agent "${name}" already has active work`);
9686
- this.name = "AgentInvocationBusyError";
9802
+ super(`A child agent named "${name}" already exists`);
9803
+ this.name = "AgentNameInUseError";
9687
9804
  }
9688
9805
  };
9689
9806
  /** Parent already has the maximum number of non-terminal child invocations. */
@@ -9693,10 +9810,28 @@ var AgentInvocationLimitError = class extends Error {
9693
9810
  this.name = "AgentInvocationLimitError";
9694
9811
  }
9695
9812
  };
9813
+ /** A child agent already has the maximum number of open tasks. */
9814
+ var AgentMessageQueueFullError = class extends Error {
9815
+ constructor(limit) {
9816
+ super(`The child agent already has ${limit} open tasks`);
9817
+ this.name = "AgentMessageQueueFullError";
9818
+ }
9819
+ };
9820
+ /** A messageAgent target is not the parent or a child of the sender. */
9821
+ var AgentMessageTargetError = class extends Error {
9822
+ constructor(message) {
9823
+ super(message);
9824
+ this.name = "AgentMessageTargetError";
9825
+ }
9826
+ };
9696
9827
  //#endregion
9697
9828
  //#region src/chat/agent-invocations/store.ts
9698
9829
  const CREATE_LOCK_PREFIX = "junior:agent_invocation:create";
9699
- function createLockName(parentConversationId) {
9830
+ /**
9831
+ * Return the SQL lock that one parent Conversation takes to start, message,
9832
+ * or stop its child agents.
9833
+ */
9834
+ function getChildAgentLockName(parentConversationId) {
9700
9835
  return `${CREATE_LOCK_PREFIX}:${parentConversationId}`;
9701
9836
  }
9702
9837
  const TERMINAL_AGENT_INVOCATION_STATUSES = [
@@ -9705,6 +9840,7 @@ const TERMINAL_AGENT_INVOCATION_STATUSES = [
9705
9840
  "failed",
9706
9841
  "stopped"
9707
9842
  ];
9843
+ /** Statuses of an agent invocation that has not finished. */
9708
9844
  const NON_TERMINAL_AGENT_INVOCATION_STATUSES = [
9709
9845
  "pending",
9710
9846
  "running",
@@ -9732,6 +9868,13 @@ function getNamedAgentConversationId(parentConversationId, name) {
9732
9868
  function getAgentInvocationIdForResult(resultInvocationId) {
9733
9869
  return stableId("agent-invocation", "result", resultInvocationId);
9734
9870
  }
9871
+ /**
9872
+ * Return the stable identity of the agent invocation that gives one message
9873
+ * from a child agent to the child Conversation that started that child agent.
9874
+ */
9875
+ function getAgentInvocationIdForMessage(messageId) {
9876
+ return stableId("agent-invocation", "message", messageId);
9877
+ }
9735
9878
  /** Return the stable turn identity advanced by one agent invocation. */
9736
9879
  function getAgentInvocationTurnId(invocationId) {
9737
9880
  return `agent-invocation:${invocationId}`;
@@ -9751,6 +9894,7 @@ function bindingFromRow(row) {
9751
9894
  parentConversationId: row.parentConversationId
9752
9895
  });
9753
9896
  }
9897
+ /** Parse one agent invocation row. */
9754
9898
  function invocationFromRow(row) {
9755
9899
  return agentInvocationSchema.parse({
9756
9900
  actor: row.actor,
@@ -9804,87 +9948,74 @@ async function getLatestAgentInvocationForConversation(childConversationId) {
9804
9948
  const rows = await getSqlExecutor().db().select().from(juniorAgentInvocations).where(eq(juniorAgentInvocations.childConversationId, childConversationId)).orderBy(desc(juniorAgentInvocations.createdAt)).limit(1);
9805
9949
  return rows[0] ? invocationFromRow(rows[0]) : void 0;
9806
9950
  }
9807
- /** Bound the agent invocations that one child agent listing reads. */
9808
- const CHILD_AGENT_LIST_INVOCATION_LIMIT = 200;
9809
- /** Return the child state that one child's agent invocations show. */
9810
- function childAgentFromInvocations(invocations, resultInvocationIds) {
9811
- const latest = invocations[0];
9812
- let current = latest;
9813
- for (const invocation of invocations) if (!isTerminalAgentInvocation(invocation)) current = invocation;
9814
- const task = invocations.find((invocation) => !resultInvocationIds.has(invocation.invocationId)) ?? invocations[invocations.length - 1];
9815
- const terminal = isTerminalAgentInvocation(current) ? current : void 0;
9816
- return {
9817
- ...latest.agentName ? { agentName: latest.agentName } : void 0,
9818
- childConversationId: latest.childConversationId,
9819
- ...terminal && terminal.status !== "completed" ? { errorMessage: terminal.errorMessage } : void 0,
9820
- status: terminal?.parentMailboxStatus === "replaced" ? "pending" : terminal?.parentMailboxStatus === "skipped" ? "stopped" : current.status,
9821
- task: task.input,
9822
- ...task.title ? { title: task.title } : void 0,
9823
- updatedAtMs: Math.max(...invocations.map((invocation) => invocation.updatedAtMs))
9824
- };
9825
- }
9826
9951
  /**
9827
- * List the direct child agents of one parent Conversation, with the newest
9828
- * activity first. The state comes from SQL agent invocation records, not from
9829
- * model output.
9952
+ * Read the name and the newest task title of one child Conversation, for
9953
+ * labels. Agent invocations that give a nested result to the child have no
9954
+ * title, so an agent invocation with a title comes first.
9830
9955
  */
9831
- async function listChildAgents(parentConversationId) {
9832
- const db = getSqlExecutor().db();
9833
- const rows = await db.select().from(juniorAgentInvocations).where(eq(juniorAgentInvocations.parentConversationId, parentConversationId)).orderBy(desc(juniorAgentInvocations.createdAt)).limit(CHILD_AGENT_LIST_INVOCATION_LIMIT);
9834
- if (rows.length === 0) return [];
9835
- const byChild = /* @__PURE__ */ new Map();
9836
- for (const invocation of rows.map(invocationFromRow)) {
9837
- const invocations = byChild.get(invocation.childConversationId) ?? [];
9838
- invocations.push(invocation);
9839
- byChild.set(invocation.childConversationId, invocations);
9840
- }
9841
- const grandchildren = await db.select({ invocationId: juniorAgentInvocations.invocationId }).from(juniorAgentInvocations).where(inArray(juniorAgentInvocations.parentConversationId, [...byChild.keys()]));
9842
- const resultInvocationIds = new Set(grandchildren.map((row) => getAgentInvocationIdForResult(row.invocationId)));
9843
- return [...byChild.values()].map((invocations) => childAgentFromInvocations(invocations, resultInvocationIds)).sort((left, right) => right.updatedAtMs - left.updatedAtMs);
9956
+ async function getChildAgentLabel(childConversationId) {
9957
+ const [row] = await getSqlExecutor().db().select({
9958
+ agentName: juniorAgentInvocations.agentName,
9959
+ title: juniorAgentInvocations.title
9960
+ }).from(juniorAgentInvocations).where(eq(juniorAgentInvocations.childConversationId, childConversationId)).orderBy(sql`${juniorAgentInvocations.title} is null`, desc(juniorAgentInvocations.createdAt)).limit(1);
9961
+ return row;
9844
9962
  }
9845
9963
  async function getNonTerminalAgentInvocationForConversation(childConversationId) {
9846
9964
  const rows = await getSqlExecutor().db().select().from(juniorAgentInvocations).where(and(eq(juniorAgentInvocations.childConversationId, childConversationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES))).orderBy(asc(juniorAgentInvocations.createdAt)).limit(1);
9847
9965
  return rows[0] ? invocationFromRow(rows[0]) : void 0;
9848
9966
  }
9849
- async function countNonTerminalAgentInvocationsForParent(parentConversationId) {
9850
- return (await getSqlExecutor().db().select({ value: count() }).from(juniorAgentInvocations).where(and(eq(juniorAgentInvocations.parentConversationId, parentConversationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES))))[0]?.value ?? 0;
9967
+ async function countNonTerminalAgentInvocationsForChild(childConversationId) {
9968
+ return (await getSqlExecutor().db().select({ value: count() }).from(juniorAgentInvocations).where(and(eq(juniorAgentInvocations.childConversationId, childConversationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES))))[0]?.value ?? 0;
9851
9969
  }
9852
9970
  /**
9853
- * Create or replay one invocation, reusing named child conversations and
9854
- * keeping unnamed child identities scoped to the invocation.
9971
+ * A child agent can only start or give work to child agents while it has
9972
+ * open work. A stopped child has none. A root Conversation has no agent
9973
+ * invocation, so it always passes.
9974
+ */
9975
+ async function assertCanGiveChildWork(parentConversationId) {
9976
+ if (await getLatestAgentInvocationForConversation(parentConversationId) && !await getNonTerminalAgentInvocationForConversation(parentConversationId)) throw new Error(`Agent conversation ${parentConversationId} has no open work and cannot give work to child agents`);
9977
+ }
9978
+ /**
9979
+ * Count the child agents of one parent that have open work. Messages queued
9980
+ * for a busy child do not count again.
9981
+ */
9982
+ async function countActiveChildAgentsForParent(parentConversationId) {
9983
+ return (await getSqlExecutor().db().select({ value: countDistinct(juniorAgentInvocations.childConversationId) }).from(juniorAgentInvocations).where(and(eq(juniorAgentInvocations.parentConversationId, parentConversationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES))))[0]?.value ?? 0;
9984
+ }
9985
+ /**
9986
+ * Create or replay the first invocation of one new child agent. A name gives
9987
+ * the child a stable Conversation id that later messages can address. A name
9988
+ * that is already used throws: later work for that child goes through
9989
+ * `createAgentInvocationForMessage`.
9855
9990
  */
9856
9991
  async function createAgentInvocation(rawInput, nowMs = Date.now()) {
9857
9992
  const input = createAgentInvocationSchema.parse(rawInput);
9858
9993
  const invocationId = getAgentInvocationId(input.parentConversationId, input.idempotencyKey);
9859
9994
  const childConversationId = input.agentName ? getNamedAgentConversationId(input.parentConversationId, input.agentName) : getUnnamedAgentConversationId(invocationId);
9860
- const lockName = createLockName(input.parentConversationId);
9995
+ const lockName = getChildAgentLockName(input.parentConversationId);
9861
9996
  return await getSqlExecutor().withLock(lockName, async () => {
9862
9997
  const existing = await getAgentInvocation(invocationId);
9863
9998
  if (existing) {
9864
9999
  if (!sameCreateInput(existing, input)) throw new Error(`Agent invocation idempotency key was reused with different input for ${invocationId}`);
9865
10000
  return existing;
9866
10001
  }
9867
- if (await getLatestAgentInvocationForConversation(input.parentConversationId) && !await getNonTerminalAgentInvocationForConversation(input.parentConversationId)) throw new Error(`Agent conversation ${input.parentConversationId} has no open work and cannot start child agents`);
9868
- if (await countNonTerminalAgentInvocationsForParent(input.parentConversationId) >= 8) throw new AgentInvocationLimitError(8);
10002
+ await assertCanGiveChildWork(input.parentConversationId);
10003
+ if (input.agentName && await getAgentBinding({
10004
+ name: input.agentName,
10005
+ parentConversationId: input.parentConversationId
10006
+ })) throw new AgentNameInUseError(input.agentName);
10007
+ if (await countActiveChildAgentsForParent(input.parentConversationId) >= 8) throw new AgentInvocationLimitError(8);
9869
10008
  await getConversationStore().createChild({
9870
10009
  childConversationId,
9871
10010
  parentConversationId: input.parentConversationId,
9872
10011
  nowMs,
9873
10012
  source: "internal"
9874
10013
  });
9875
- if (input.agentName) {
9876
- await getSqlExecutor().db().insert(juniorAgentBindings).values({
9877
- childConversationId,
9878
- name: input.agentName,
9879
- parentConversationId: input.parentConversationId
9880
- }).onConflictDoNothing();
9881
- const binding = await getAgentBinding({
9882
- name: input.agentName,
9883
- parentConversationId: input.parentConversationId
9884
- });
9885
- if (!binding || binding.childConversationId !== childConversationId) throw new Error(`Named agent binding did not resolve to ${childConversationId}`);
9886
- if (await getNonTerminalAgentInvocationForConversation(childConversationId)) throw new AgentInvocationBusyError(input.agentName);
9887
- }
10014
+ if (input.agentName) await getSqlExecutor().db().insert(juniorAgentBindings).values({
10015
+ childConversationId,
10016
+ name: input.agentName,
10017
+ parentConversationId: input.parentConversationId
10018
+ });
9888
10019
  await getSqlExecutor().db().insert(juniorAgentInvocations).values({
9889
10020
  invocationId,
9890
10021
  parentConversationId: input.parentConversationId,
@@ -9910,13 +10041,14 @@ async function createAgentInvocation(rawInput, nowMs = Date.now()) {
9910
10041
  });
9911
10042
  }
9912
10043
  /**
9913
- * Create or replay the agent invocation that gives one child agent result to
9914
- * the child Conversation that started it. It continues that Conversation for
9915
- * the same parent, Actor, and credentials as `latest`. It skips the busy and
9916
- * active-count checks because it continues work that already passed them.
10044
+ * Create or replay the agent invocation that gives one child agent result or
10045
+ * message to the child Conversation that started that child agent. It
10046
+ * continues that Conversation for the same parent, Actor, and credentials as
10047
+ * `latest`. It skips the queue and active-count checks because it continues
10048
+ * work that already passed them.
9917
10049
  */
9918
- async function createAgentInvocationForResult(args, nowMs = Date.now()) {
9919
- const invocationId = getAgentInvocationIdForResult(args.resultInvocationId);
10050
+ async function createAgentInvocationFromLatest(args, nowMs = Date.now()) {
10051
+ const { invocationId } = args;
9920
10052
  await getSqlExecutor().db().insert(juniorAgentInvocations).values({
9921
10053
  invocationId,
9922
10054
  parentConversationId: args.latest.parentConversationId,
@@ -9938,6 +10070,73 @@ async function createAgentInvocationForResult(args, nowMs = Date.now()) {
9938
10070
  if (!invocation || invocation.childConversationId !== args.latest.childConversationId) throw new Error(`Agent invocation creation raced for ${invocationId}`);
9939
10071
  return invocation;
9940
10072
  }
10073
+ /** Find a direct child agent of one parent by name or child Conversation id. */
10074
+ async function findChildAgentConversationId(args) {
10075
+ const binding = await getAgentBinding({
10076
+ name: args.target,
10077
+ parentConversationId: args.parentConversationId
10078
+ });
10079
+ if (binding) return binding.childConversationId;
10080
+ return (await getSqlExecutor().db().select({ childConversationId: juniorAgentInvocations.childConversationId }).from(juniorAgentInvocations).where(and(eq(juniorAgentInvocations.parentConversationId, args.parentConversationId), eq(juniorAgentInvocations.childConversationId, args.target))).limit(1))[0]?.childConversationId;
10081
+ }
10082
+ /**
10083
+ * Return whether a pending agent invocation still waits for older open work
10084
+ * on its child.
10085
+ */
10086
+ async function isQueuedBehindOpenWork(invocation) {
10087
+ if (invocation.status !== "pending") return false;
10088
+ return (await getSqlExecutor().db().select({ invocationId: juniorAgentInvocations.invocationId }).from(juniorAgentInvocations).where(and(eq(juniorAgentInvocations.childConversationId, invocation.childConversationId), lt(juniorAgentInvocations.createdAt, new Date(invocation.createdAtMs)), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES))).limit(1)).length > 0;
10089
+ }
10090
+ /**
10091
+ * Create or replay the agent invocation for one message from a parent to one
10092
+ * of its child agents. The child continues its history. If the child already
10093
+ * has open work, the new agent invocation waits for that work and the parent
10094
+ * gets one report for both. A stopped child starts again.
10095
+ */
10096
+ async function createAgentInvocationForMessage(input, nowMs = Date.now()) {
10097
+ const invocationId = getAgentInvocationId(input.parentConversationId, input.idempotencyKey);
10098
+ return await getSqlExecutor().withLock(getChildAgentLockName(input.parentConversationId), async () => {
10099
+ const existing = await getAgentInvocation(invocationId);
10100
+ if (existing) {
10101
+ if (existing.childConversationId !== input.childConversationId || existing.input !== input.input) throw new Error(`Agent invocation idempotency key was reused with different input for ${invocationId}`);
10102
+ return {
10103
+ invocation: existing,
10104
+ queued: await isQueuedBehindOpenWork(existing)
10105
+ };
10106
+ }
10107
+ const latest = await getLatestAgentInvocationForConversation(input.childConversationId);
10108
+ if (!latest || latest.parentConversationId !== input.parentConversationId) throw new Error(`${input.childConversationId} is not a child agent of ${input.parentConversationId}`);
10109
+ await assertCanGiveChildWork(input.parentConversationId);
10110
+ const openCount = await countNonTerminalAgentInvocationsForChild(input.childConversationId);
10111
+ if (openCount >= 4) throw new AgentMessageQueueFullError(4);
10112
+ if (openCount === 0 && await countActiveChildAgentsForParent(input.parentConversationId) >= 8) throw new AgentInvocationLimitError(8);
10113
+ await getSqlExecutor().db().insert(juniorAgentInvocations).values({
10114
+ invocationId,
10115
+ parentConversationId: input.parentConversationId,
10116
+ childConversationId: input.childConversationId,
10117
+ agentName: latest.agentName ?? null,
10118
+ input: input.input,
10119
+ title: null,
10120
+ actor: input.actor,
10121
+ credentialContext: input.credentialContext ?? null,
10122
+ source: { kind: "agent_invocation" },
10123
+ destination: input.destination,
10124
+ reasoningLevel: latest.reasoningLevel ?? null,
10125
+ parentToolCallId: input.parentToolCallId,
10126
+ status: "pending",
10127
+ mailboxStatus: "pending",
10128
+ createdAt: new Date(nowMs),
10129
+ updatedAt: new Date(nowMs),
10130
+ terminalAt: null
10131
+ }).onConflictDoNothing();
10132
+ const invocation = await getAgentInvocation(invocationId);
10133
+ if (!invocation) throw new Error(`Agent invocation creation raced for ${invocationId}`);
10134
+ return {
10135
+ invocation,
10136
+ queued: openCount > 0
10137
+ };
10138
+ });
10139
+ }
9941
10140
  /**
9942
10141
  * Return whether a child Conversation has work that will start another agent
9943
10142
  * invocation on it: another open agent invocation, an open child agent, or a
@@ -10020,76 +10219,90 @@ async function completeAgentInvocation(args) {
10020
10219
  }).where(and(eq(juniorAgentInvocations.invocationId, args.invocationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES)));
10021
10220
  return await getAgentInvocation(args.invocationId);
10022
10221
  }
10023
- /**
10024
- * List one child Conversation and every Conversation below it, child first.
10025
- * It takes the create lock of each Conversation before it reads that
10026
- * Conversation's children, so no child agent can start in the tree until the
10027
- * caller's transaction ends.
10028
- */
10029
- async function lockAgentConversationTree(childConversationId) {
10030
- const executor = getSqlExecutor();
10031
- const conversationIds = [childConversationId];
10032
- let parents = conversationIds;
10033
- while (parents.length > 0) {
10034
- for (const parent of parents) await executor.withLock(createLockName(parent), async () => void 0);
10035
- parents = (await executor.db().selectDistinct({ childConversationId: juniorAgentInvocations.childConversationId }).from(juniorAgentInvocations).where(inArray(juniorAgentInvocations.parentConversationId, parents))).map((row) => row.childConversationId).filter((id) => !conversationIds.includes(id));
10036
- conversationIds.push(...parents);
10037
- }
10038
- return conversationIds;
10039
- }
10040
- /**
10041
- * Stop the agent invocations of one direct child agent and of every child
10042
- * agent below it. Returns undefined when the child does not belong to the
10043
- * parent.
10044
- */
10045
- async function stopChildAgentInvocations(args) {
10046
- if ((await getSqlExecutor().db().select({ invocationId: juniorAgentInvocations.invocationId }).from(juniorAgentInvocations).where(and(eq(juniorAgentInvocations.parentConversationId, args.parentConversationId), eq(juniorAgentInvocations.childConversationId, args.childConversationId))).limit(1)).length === 0) return;
10047
- return await stopAgentInvocationTree(args.childConversationId, args.nowMs);
10222
+ /** Return whether an agent invocation has finished. */
10223
+ function isTerminalAgentInvocation(invocation) {
10224
+ return TERMINAL_AGENT_INVOCATION_STATUSES.includes(invocation.status);
10048
10225
  }
10049
- /**
10050
- * Stop the agent invocations of one agent Conversation and of every child
10051
- * agent below it. Open agent invocations become `stopped`. Results that would
10052
- * still start or report work in the tree become `skipped`.
10053
- */
10054
- async function stopAgentInvocationTree(conversationId, nowMs = Date.now()) {
10055
- return await getSqlExecutor().withLock(createLockName(conversationId), () => stopLockedAgentInvocationTree(conversationId, nowMs));
10226
+ //#endregion
10227
+ //#region src/chat/agent-invocations/list.ts
10228
+ function isChildAgentResult(status) {
10229
+ return CHILD_AGENT_RESULTS.includes(status);
10056
10230
  }
10057
- async function stopLockedAgentInvocationTree(conversationId, nowMs) {
10058
- const db = getSqlExecutor().db();
10059
- const now = new Date(nowMs);
10060
- const conversationIds = await lockAgentConversationTree(conversationId);
10061
- const stopped = await db.update(juniorAgentInvocations).set({
10062
- errorMessage: "Stopped by the parent conversation",
10063
- parentMailboxStatus: "skipped",
10064
- result: null,
10065
- status: "stopped",
10066
- terminalAt: now,
10067
- updatedAt: now
10068
- }).where(and(inArray(juniorAgentInvocations.childConversationId, conversationIds), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES))).returning();
10069
- await db.update(juniorAgentInvocations).set({
10070
- parentMailboxStatus: "skipped",
10071
- updatedAt: now
10072
- }).where(or(and(inArray(juniorAgentInvocations.parentConversationId, conversationIds), inArray(juniorAgentInvocations.parentMailboxStatus, ["pending", "appended"])), and(inArray(juniorAgentInvocations.childConversationId, conversationIds), eq(juniorAgentInvocations.parentMailboxStatus, "replaced"))));
10231
+ /** Bound the agent invocations that one child agent listing reads. */
10232
+ const CHILD_AGENT_LIST_INVOCATION_LIMIT = 200;
10233
+ /** Return the child state that one child's agent invocations show. */
10234
+ function childAgentFromInvocations(invocations, resultInvocationIds) {
10235
+ const latest = invocations[0];
10236
+ let current = latest;
10237
+ for (const invocation of invocations) if (!isTerminalAgentInvocation(invocation)) current = invocation;
10238
+ const tasks = invocations.filter((invocation) => !resultInvocationIds.has(invocation.invocationId));
10239
+ const task = tasks.find((invocation) => invocation.parentToolCallId) ?? tasks[0] ?? invocations[invocations.length - 1];
10240
+ const terminal = isTerminalAgentInvocation(current) ? current : void 0;
10241
+ const lastResult = invocations.filter(isTerminalAgentInvocation).filter((invocation) => invocation.parentMailboxStatus !== "replaced" && invocation.parentMailboxStatus !== "skipped").map((invocation) => invocation.status).find(isChildAgentResult);
10073
10242
  return {
10074
- conversationIds,
10075
- stopped: stopped.map(invocationFromRow).filter(isTerminalAgentInvocation)
10243
+ ...latest.agentName ? { agentName: latest.agentName } : void 0,
10244
+ childConversationId: latest.childConversationId,
10245
+ ...terminal && terminal.status !== "completed" ? { errorMessage: terminal.errorMessage } : void 0,
10246
+ ...lastResult ? { lastResult } : void 0,
10247
+ status: terminal?.parentMailboxStatus === "replaced" ? "pending" : terminal?.parentMailboxStatus === "skipped" ? "stopped" : current.status,
10248
+ task: task.input,
10249
+ ...task.title ? { title: task.title } : void 0,
10250
+ updatedAtMs: Math.max(...invocations.map((invocation) => invocation.updatedAtMs))
10076
10251
  };
10077
10252
  }
10078
- /** Return whether an agent invocation has finished. */
10079
- function isTerminalAgentInvocation(invocation) {
10080
- return TERMINAL_AGENT_INVOCATION_STATUSES.includes(invocation.status);
10253
+ /**
10254
+ * List the direct child agents of one parent Conversation, with the newest
10255
+ * activity first. The state comes from SQL agent invocation records, not from
10256
+ * model output.
10257
+ */
10258
+ async function listChildAgents(parentConversationId) {
10259
+ const db = getSqlExecutor().db();
10260
+ const rows = await db.select().from(juniorAgentInvocations).where(eq(juniorAgentInvocations.parentConversationId, parentConversationId)).orderBy(desc(juniorAgentInvocations.createdAt)).limit(CHILD_AGENT_LIST_INVOCATION_LIMIT);
10261
+ if (rows.length === 0) return [];
10262
+ const byChild = /* @__PURE__ */ new Map();
10263
+ for (const invocation of rows.map(invocationFromRow)) {
10264
+ const invocations = byChild.get(invocation.childConversationId) ?? [];
10265
+ invocations.push(invocation);
10266
+ byChild.set(invocation.childConversationId, invocations);
10267
+ }
10268
+ const grandchildren = await db.select({ invocationId: juniorAgentInvocations.invocationId }).from(juniorAgentInvocations).where(inArray(juniorAgentInvocations.parentConversationId, [...byChild.keys()]));
10269
+ const resultInvocationIds = new Set(grandchildren.map((row) => getAgentInvocationIdForResult(row.invocationId)));
10270
+ return [...byChild.values()].map((invocations) => childAgentFromInvocations(invocations, resultInvocationIds)).sort((left, right) => right.updatedAtMs - left.updatedAtMs);
10081
10271
  }
10082
10272
  //#endregion
10083
10273
  //#region src/chat/tools/runtime/list-agents.ts
10084
10274
  const TASK_MAX_LENGTH = 1e3;
10085
10275
  const ERROR_MAX_LENGTH = 500;
10276
+ /**
10277
+ * What a child agent does now. The words match the Conversation execution
10278
+ * status, plus `stopped` after stopAgent.
10279
+ */
10280
+ const CHILD_AGENT_STATUSES = [
10281
+ "pending",
10282
+ "running",
10283
+ "paused",
10284
+ "idle",
10285
+ "stopped"
10286
+ ];
10287
+ function childAgentStatus(agent) {
10288
+ switch (agent.status) {
10289
+ case "pending":
10290
+ case "running":
10291
+ case "paused":
10292
+ case "stopped": return agent.status;
10293
+ case "blocked":
10294
+ case "completed":
10295
+ case "failed": return "idle";
10296
+ }
10297
+ }
10086
10298
  function truncate(text, maxLength) {
10087
10299
  return text.length > maxLength ? `${text.slice(0, maxLength)}…` : text;
10088
10300
  }
10089
10301
  const listAgentsOutputSchema = juniorToolOutputSchema.extend({ agents: z.array(z.object({
10090
10302
  conversation_id: z.string().min(1).describe("Child conversation ID. Pass it to searchConversationEvents to read the child's history."),
10091
- name: agentNameSchema.optional().describe("Child-agent name. Pass it to spawnAgent to reuse it."),
10092
- status: z.enum(AGENT_INVOCATION_STATUSES).describe("pending: waiting to run. running or paused: working. completed, blocked, failed, or stopped: not working."),
10303
+ name: agentNameSchema.optional().describe("Child-agent name. Pass it to messageAgent."),
10304
+ status: z.enum(CHILD_AGENT_STATUSES).describe("What the child agent does now. pending: waiting to run. running or paused: working. idle: finished its work and waits for a message. stopped: stopAgent stopped it."),
10305
+ last_result: z.enum(CHILD_AGENT_RESULTS).optional().describe("How the latest finished task of the child agent ended"),
10093
10306
  task: z.string().describe("Latest task given to the child agent"),
10094
10307
  title: z.string().optional().describe("Short display title of the latest task"),
10095
10308
  error: z.string().optional().describe("Why the child agent is blocked, failed, or stopped"),
@@ -10098,7 +10311,7 @@ const listAgentsOutputSchema = juniorToolOutputSchema.extend({ agents: z.array(z
10098
10311
  /** Create the read-only tool that lists the child agents of one conversation. */
10099
10312
  function createListAgentsTool(conversationId) {
10100
10313
  return zodTool({
10101
- description: "List the child agents that this conversation started, with the state recorded by the runtime. Use it to check which child agents are still working, or to find a named child agent before you give it more work with spawnAgent. Results still arrive as new tasks; do not poll with this tool.",
10314
+ description: "List the child agents that this conversation started, with the state recorded by the runtime. Use it to check which child agents are still working, or to find a child agent before you give it more work with messageAgent. Results still arrive as new tasks; do not poll with this tool.",
10102
10315
  annotations: {
10103
10316
  destructiveHint: false,
10104
10317
  idempotentHint: true,
@@ -10110,7 +10323,8 @@ function createListAgentsTool(conversationId) {
10110
10323
  execute: async () => ({ agents: (await listChildAgents(conversationId)).map((agent) => ({
10111
10324
  conversation_id: agent.childConversationId,
10112
10325
  ...agent.agentName ? { name: agent.agentName } : void 0,
10113
- status: agent.status,
10326
+ status: childAgentStatus(agent),
10327
+ ...agent.lastResult ? { last_result: agent.lastResult } : void 0,
10114
10328
  task: truncate(agent.task, TASK_MAX_LENGTH),
10115
10329
  ...agent.title ? { title: agent.title } : void 0,
10116
10330
  ...agent.errorMessage ? { error: truncate(agent.errorMessage, ERROR_MAX_LENGTH) } : void 0,
@@ -10119,12 +10333,59 @@ function createListAgentsTool(conversationId) {
10119
10333
  });
10120
10334
  }
10121
10335
  //#endregion
10336
+ //#region src/chat/tools/runtime/message-agent.ts
10337
+ /** Bound the text of one message so a loop cannot fill a mailbox quickly. */
10338
+ const MESSAGE_AGENT_TEXT_MAX_LENGTH = 8e3;
10339
+ const messageAgentOutputSchema = juniorToolOutputSchema.extend({
10340
+ conversation_id: z.string().min(1).describe("Target conversation ID"),
10341
+ delivery: z.enum(["started", "queued"]).describe("started: the child agent starts work on the message now. queued: the target reads the message after its current work.")
10342
+ });
10343
+ /** Create the tool that sends one message to the parent or a child agent. */
10344
+ function createMessageAgentTool(messageAgent) {
10345
+ return zodTool({
10346
+ description: `Send a message to your parent agent or to a child agent that this conversation started. Use it to give an existing child agent more work, an answer, or a correction, and to send your parent agent a finding or a question while you keep working. A message to a child continues its history. If the child is still working, it reads the message after its current work, and one result covers both. The result arrives later as a new task in this conversation; do not wait or poll for it. A message to your parent does not end your task. Use spawnAgent only to start a new child agent.`,
10347
+ annotations: {
10348
+ destructiveHint: false,
10349
+ idempotentHint: true,
10350
+ openWorldHint: false,
10351
+ readOnlyHint: false
10352
+ },
10353
+ inputSchema: z.object({
10354
+ to: z.string().trim().min(1).describe(`"${PARENT_AGENT_TARGET}" for your parent agent, or the name or conversation_id of a child agent from spawnAgent or listAgents`),
10355
+ text: z.string().trim().min(1).max(MESSAGE_AGENT_TEXT_MAX_LENGTH).describe("Message text. Include all context the target needs.")
10356
+ }).strict(),
10357
+ outputSchema: messageAgentOutputSchema,
10358
+ execute: async (input, options) => {
10359
+ if (!options.toolCallId) throw new ToolInputError("messageAgent requires an active tool call ID");
10360
+ let result;
10361
+ try {
10362
+ result = await messageAgent({
10363
+ text: input.text,
10364
+ to: input.to
10365
+ }, {
10366
+ ...options.signal ? { signal: options.signal } : void 0,
10367
+ toolCallId: options.toolCallId
10368
+ });
10369
+ } catch (error) {
10370
+ if (error instanceof AgentMessageTargetError) throw new ToolInputError(`${error.message}. Use listAgents to find a child agent.`, { cause: error });
10371
+ if (error instanceof AgentMessageQueueFullError) throw new ToolInputError(`${error.message}. Wait for its result, then send one message with everything it needs.`, { cause: error });
10372
+ if (error instanceof AgentInvocationLimitError) throw new ToolInputError(`${error.message}. Wait for active children to finish before you give more work.`, { cause: error });
10373
+ throw error;
10374
+ }
10375
+ return {
10376
+ conversation_id: result.conversationId,
10377
+ delivery: result.delivery
10378
+ };
10379
+ }
10380
+ });
10381
+ }
10382
+ //#endregion
10122
10383
  //#region src/chat/tools/runtime/spawn-agent.ts
10123
10384
  const spawnAgentOutputSchema = juniorToolOutputSchema.extend({ conversation_id: z.string().min(1).describe("Child conversation ID. Pass it to searchConversationEvents to read the child's history.") });
10124
10385
  /** Create the asynchronous child-agent tool for one parent run. */
10125
10386
  function createSpawnAgentTool(spawnAgent) {
10126
10387
  return zodTool({
10127
- description: "Start an asynchronous child agent on a delegated task. Give it a name to reuse the same child agent and its history on later calls; omit the name for a one-off agent. This call confirms durable scheduling, not task completion. The result arrives later as a new task in this conversation; do not wait or poll for it.",
10388
+ description: "Start a new asynchronous child agent on a delegated task. Give it a name when you may send it more work later. To give more work to a child agent that already exists, use messageAgent instead. This call confirms durable scheduling, not task completion. The result arrives later as a new task in this conversation; do not wait or poll for it.",
10128
10389
  annotations: {
10129
10390
  destructiveHint: false,
10130
10391
  idempotentHint: true,
@@ -10134,7 +10395,7 @@ function createSpawnAgentTool(spawnAgent) {
10134
10395
  inputSchema: z.object({
10135
10396
  task: z.string().trim().min(1).describe("Task for the child agent"),
10136
10397
  title: z.string().trim().min(1).max(60).nullable().optional().describe("Optional short display title for the task. Omit to generate one from the task."),
10137
- name: agentNameSchema.nullable().optional().describe("Stable child-agent name, or omit for a one-off agent"),
10398
+ name: agentNameSchema.refine((name) => name !== PARENT_AGENT_TARGET, { message: `"${PARENT_AGENT_TARGET}" is reserved` }).nullable().optional().describe("New child-agent name that messageAgent can address, or omit for a one-off agent"),
10138
10399
  reasoning_level: z.enum(TURN_REASONING_LEVELS).nullable().optional().describe("Optional reasoning level for this delegated task only")
10139
10400
  }).strict(),
10140
10401
  outputSchema: spawnAgentOutputSchema,
@@ -10158,7 +10419,7 @@ function createSpawnAgentTool(spawnAgent) {
10158
10419
  toolCallId: options.toolCallId
10159
10420
  });
10160
10421
  } catch (error) {
10161
- if (error instanceof AgentInvocationBusyError) throw new ToolInputError(`${error.message}. Wait for it to finish or use a different name.`, { cause: error });
10422
+ if (error instanceof AgentNameInUseError) throw new ToolInputError(`${error.message}. Use messageAgent to give it more work, or choose a different name.`, { cause: error });
10162
10423
  if (error instanceof AgentInvocationLimitError) throw new ToolInputError(`${error.message}. Wait for active children to finish before spawning more.`, { cause: error });
10163
10424
  throw error;
10164
10425
  }
@@ -10169,18 +10430,33 @@ function createSpawnAgentTool(spawnAgent) {
10169
10430
  //#endregion
10170
10431
  //#region src/chat/agent-invocations/markers.ts
10171
10432
  /**
10172
- * Dashboard markers for agent invocations. The parent Conversation gets one
10433
+ * Dashboard records for agent invocations. The parent Conversation gets one
10173
10434
  * `subagent_started` and one `subagent_ended` event for each agent invocation.
10435
+ * The child Conversation gets `message` events for its input and its result,
10436
+ * because a child has no Location that records visible messages.
10174
10437
  */
10175
10438
  const SUBAGENT_KIND = "agent";
10176
10439
  /**
10177
- * Record in the parent Conversation that one agent invocation started. The
10178
- * dashboard pairs this event with `subagent_ended` to show the child and its
10440
+ * Record in the parent Conversation that one agent invocation started, and
10441
+ * record its input as a user message in the child Conversation. The dashboard
10442
+ * pairs the parent event with `subagent_ended` to show the child and its
10179
10443
  * status. It must be written before the child mailbox append, so the child
10180
10444
  * cannot end first.
10181
10445
  */
10182
10446
  async function recordSubagentStarted(invocation) {
10183
- await getConversationEventStore().append(invocation.parentConversationId, [{
10447
+ const eventStore = getConversationEventStore();
10448
+ const messageId = getAgentInvocationMessageId(invocation.invocationId);
10449
+ await eventStore.append(invocation.childConversationId, [{
10450
+ idempotencyKey: `message:${messageId}`,
10451
+ data: {
10452
+ type: "message",
10453
+ messageId,
10454
+ role: "user",
10455
+ text: invocation.input
10456
+ },
10457
+ createdAtMs: invocation.createdAtMs
10458
+ }]);
10459
+ await eventStore.append(invocation.parentConversationId, [{
10184
10460
  idempotencyKey: `subagent_started:${invocation.invocationId}`,
10185
10461
  data: {
10186
10462
  type: "subagent_started",
@@ -10205,12 +10481,14 @@ const SUBAGENT_OUTCOMES = {
10205
10481
  stopped: "aborted"
10206
10482
  };
10207
10483
  /**
10208
- * Record in the parent Conversation how one agent invocation ended. The
10484
+ * Record in the parent Conversation how one agent invocation ended, and record
10485
+ * a completed result as an assistant message in the child Conversation. The
10209
10486
  * heartbeat result repair also calls this, because the child work can be
10210
10487
  * acknowledged before the worker reports the result.
10211
10488
  */
10212
10489
  async function recordSubagentEnded(invocation) {
10213
- await getConversationEventStore().append(invocation.parentConversationId, [{
10490
+ const eventStore = getConversationEventStore();
10491
+ await eventStore.append(invocation.parentConversationId, [{
10214
10492
  idempotencyKey: `subagent_ended:${invocation.invocationId}`,
10215
10493
  data: {
10216
10494
  type: "subagent_ended",
@@ -10219,6 +10497,19 @@ async function recordSubagentEnded(invocation) {
10219
10497
  },
10220
10498
  createdAtMs: invocation.terminalAtMs
10221
10499
  }]);
10500
+ const result = invocation.status === "completed" ? invocation.result.trim() : "";
10501
+ if (!result) return;
10502
+ const messageId = `agent-invocation:${invocation.invocationId}:reply`;
10503
+ await eventStore.append(invocation.childConversationId, [{
10504
+ idempotencyKey: `message:${messageId}`,
10505
+ data: {
10506
+ type: "message",
10507
+ messageId,
10508
+ role: "assistant",
10509
+ text: result
10510
+ },
10511
+ createdAtMs: invocation.terminalAtMs
10512
+ }]);
10222
10513
  }
10223
10514
  //#endregion
10224
10515
  //#region src/chat/agent-invocations/stop.ts
@@ -10251,6 +10542,61 @@ async function finishStoppedTree(tree, conversationIdsToStop, state) {
10251
10542
  state
10252
10543
  });
10253
10544
  }
10545
+ /**
10546
+ * List one child Conversation and every Conversation below it, child first.
10547
+ * It takes the create lock of each Conversation before it reads that
10548
+ * Conversation's children, so no child agent can start in the tree until the
10549
+ * caller's transaction ends.
10550
+ */
10551
+ async function lockAgentConversationTree(childConversationId) {
10552
+ const executor = getSqlExecutor();
10553
+ const conversationIds = [childConversationId];
10554
+ let parents = conversationIds;
10555
+ while (parents.length > 0) {
10556
+ for (const parent of parents) await executor.withLock(getChildAgentLockName(parent), async () => void 0);
10557
+ parents = (await executor.db().selectDistinct({ childConversationId: juniorAgentInvocations.childConversationId }).from(juniorAgentInvocations).where(inArray(juniorAgentInvocations.parentConversationId, parents))).map((row) => row.childConversationId).filter((id) => !conversationIds.includes(id));
10558
+ conversationIds.push(...parents);
10559
+ }
10560
+ return conversationIds;
10561
+ }
10562
+ /**
10563
+ * Stop the agent invocations of one direct child agent and of every child
10564
+ * agent below it. Returns undefined when the child does not belong to the
10565
+ * parent.
10566
+ */
10567
+ async function stopChildAgentInvocations(args) {
10568
+ if ((await getSqlExecutor().db().select({ invocationId: juniorAgentInvocations.invocationId }).from(juniorAgentInvocations).where(and(eq(juniorAgentInvocations.parentConversationId, args.parentConversationId), eq(juniorAgentInvocations.childConversationId, args.childConversationId))).limit(1)).length === 0) return;
10569
+ return await stopAgentInvocationTree(args.childConversationId, args.nowMs);
10570
+ }
10571
+ /**
10572
+ * Stop the agent invocations of one agent Conversation and of every child
10573
+ * agent below it. Open agent invocations become `stopped`. Results that would
10574
+ * still start or report work in the tree become `skipped`.
10575
+ */
10576
+ async function stopAgentInvocationTree(conversationId, nowMs = Date.now()) {
10577
+ return await getSqlExecutor().withLock(getChildAgentLockName(conversationId), () => stopLockedAgentInvocationTree(conversationId, nowMs));
10578
+ }
10579
+ async function stopLockedAgentInvocationTree(conversationId, nowMs) {
10580
+ const db = getSqlExecutor().db();
10581
+ const now = new Date(nowMs);
10582
+ const conversationIds = await lockAgentConversationTree(conversationId);
10583
+ const stopped = await db.update(juniorAgentInvocations).set({
10584
+ errorMessage: "Stopped by the parent conversation",
10585
+ parentMailboxStatus: "skipped",
10586
+ result: null,
10587
+ status: "stopped",
10588
+ terminalAt: now,
10589
+ updatedAt: now
10590
+ }).where(and(inArray(juniorAgentInvocations.childConversationId, conversationIds), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES))).returning();
10591
+ await db.update(juniorAgentInvocations).set({
10592
+ parentMailboxStatus: "skipped",
10593
+ updatedAt: now
10594
+ }).where(or(and(inArray(juniorAgentInvocations.parentConversationId, conversationIds), inArray(juniorAgentInvocations.parentMailboxStatus, ["pending", "appended"])), and(inArray(juniorAgentInvocations.childConversationId, conversationIds), eq(juniorAgentInvocations.parentMailboxStatus, "replaced"))));
10595
+ return {
10596
+ conversationIds,
10597
+ stopped: stopped.map(invocationFromRow).filter(isTerminalAgentInvocation)
10598
+ };
10599
+ }
10254
10600
  //#endregion
10255
10601
  //#region src/chat/tools/runtime/stop-agent.ts
10256
10602
  const stopAgentOutputSchema = juniorToolOutputSchema.extend({
@@ -10260,7 +10606,7 @@ const stopAgentOutputSchema = juniorToolOutputSchema.extend({
10260
10606
  /** Create the tool that stops one child agent of one conversation. */
10261
10607
  function createStopAgentTool(conversationId) {
10262
10608
  return zodTool({
10263
- description: "Stop a child agent that this conversation started, and every child agent it started. Use it when the child agent's work is no longer needed or goes in the wrong direction. A stopped child agent sends no result. To give a named child agent new work later, call spawnAgent with its name.",
10609
+ description: "Stop a child agent that this conversation started, and every child agent it started. Use it when the child agent's work is no longer needed or goes in the wrong direction. A stopped child agent sends no result. To give it new work later, use messageAgent.",
10264
10610
  annotations: {
10265
10611
  destructiveHint: true,
10266
10612
  idempotentHint: true,
@@ -10321,6 +10667,23 @@ function createFinishAutomationRunTool(options) {
10321
10667
  });
10322
10668
  }
10323
10669
  //#endregion
10670
+ //#region src/chat/tools/runtime/report-blocked.ts
10671
+ /** Create the tool that ends a child agent run as blocked. */
10672
+ function createReportBlockedTool() {
10673
+ return zodTool({
10674
+ annotations: {
10675
+ destructiveHint: false,
10676
+ idempotentHint: true,
10677
+ openWorldHint: false,
10678
+ readOnlyHint: false
10679
+ },
10680
+ description: "End this delegated task as blocked. Call this when missing access, credentials, input, or a decision stops the task, also when a search for the missing part found nothing. Do not guess, mock, or fake results to work around it.",
10681
+ inputSchema: z.object({ missing: z.string().trim().min(1).describe("What the task needs and who can give it. Add the work you already did, so the parent does not repeat it.") }).strict(),
10682
+ outputSchema: juniorToolOutputSchema.extend({ missing: z.string() }),
10683
+ execute: async (input) => ({ missing: input.missing })
10684
+ });
10685
+ }
10686
+ //#endregion
10324
10687
  //#region src/chat/tools/list-watches.ts
10325
10688
  const listedResourceWatchSchema = z.object({
10326
10689
  id: z.string().min(1),
@@ -10592,6 +10955,7 @@ function createWatchEventsTool(context, catalog) {
10592
10955
  outputSchema: outputSchema$2,
10593
10956
  async execute(input) {
10594
10957
  if (input.namespace === "junior" && input.resourceType === "timer") throw new ToolInputError("Use watchTimer to create a timer Watch.");
10958
+ if (input.namespace === "slack") throw new ToolInputError("Slack channel messages can trigger only an event automation in that channel. Use createEventAutomation.");
10595
10959
  const events = cleanStrings(input.events);
10596
10960
  for (const eventType of events) if (!pluginSupportsEvent(catalog, input.namespace, input.resourceType, eventType)) throw new ToolInputError(`Resource type "${input.namespace}:${input.resourceType}" does not support event "${eventType}".`);
10597
10961
  const intent = input.intent.trim();
@@ -10756,7 +11120,7 @@ const eventAutomationListToolResultSchema = juniorToolOutputSchema.extend({
10756
11120
  }).strict();
10757
11121
  /** Build the validated event trigger accepted by event-automation tools. */
10758
11122
  function registeredEventAutomationTriggerSchema(catalog) {
10759
- const plugins = pluginEventCatalog(catalog);
11123
+ const plugins = automationEventCatalog(catalog);
10760
11124
  return z.object({
10761
11125
  namespace: eventNamespaceSchema(plugins),
10762
11126
  identifier: z.string().trim().min(1).max(300),
@@ -10768,7 +11132,7 @@ function registeredEventAutomationTriggerSchema(catalog) {
10768
11132
  }
10769
11133
  /** Reject an event-automation trigger that is no longer supported by the catalog. */
10770
11134
  function requireSupportedEventAutomationTrigger(catalog, trigger) {
10771
- const plugins = pluginEventCatalog(catalog);
11135
+ const plugins = automationEventCatalog(catalog);
10772
11136
  for (const eventType of trigger.events) if (!pluginSupportsEvent(plugins, trigger.namespace, trigger.resourceType, eventType)) throw new ToolInputError(`Resource type "${trigger.namespace}:${trigger.resourceType}" does not support event "${eventType}".`);
10773
11137
  return requireSupportedEventMatch(plugins, {
10774
11138
  match: trigger.match,
@@ -10810,7 +11174,7 @@ async function writableEventAutomation(context, id) {
10810
11174
  return task;
10811
11175
  }
10812
11176
  function eventAutomationTriggerAvailable(task, catalog) {
10813
- const registration = pluginEventCatalog(catalog)[task.trigger.namespace];
11177
+ const registration = automationEventCatalog(catalog)[task.trigger.namespace];
10814
11178
  return Boolean(registration && task.trigger.events.every((eventType) => registration.resourceTypes.some((resourceType) => resourceType.supportedEvents.includes(eventType))));
10815
11179
  }
10816
11180
  /** Project an event automation into the bounded tool-result shape. */
@@ -10934,7 +11298,7 @@ function createEventAutomationTool(context, catalog) {
10934
11298
  instruction: input.instruction,
10935
11299
  title: input.title
10936
11300
  });
10937
- const task = {
11301
+ const task = requireSlackChannelMessageScope({
10938
11302
  id,
10939
11303
  destinationVisibility: source.visibility,
10940
11304
  createdAtMs: Date.now(),
@@ -10956,7 +11320,7 @@ function createEventAutomationTool(context, catalog) {
10956
11320
  events: [...new Set(input.trigger.events)],
10957
11321
  ...match ? { match } : void 0
10958
11322
  }
10959
- };
11323
+ });
10960
11324
  return eventAutomationToolResult(context.conversationId, await createEventAutomation(db, task), catalog, actor.userId);
10961
11325
  }
10962
11326
  });
@@ -11088,11 +11452,17 @@ async function editEventAutomation(current, input, isCreator, catalog) {
11088
11452
  };
11089
11453
  }
11090
11454
  const changesExecution = input.instruction !== void 0 && input.instruction !== current.task.text || changesEventAutomationTrigger(current.trigger, trigger);
11091
- return {
11455
+ const next = {
11092
11456
  ...current,
11093
11457
  ...await editAutomationFields(current, input, isCreator, changesExecution),
11094
11458
  trigger
11095
11459
  };
11460
+ try {
11461
+ return requireSlackChannelMessageScope(next);
11462
+ } catch (error) {
11463
+ if (error instanceof ToolInputError) throw new AutomationEditError(error.message, "trigger");
11464
+ throw error;
11465
+ }
11096
11466
  }
11097
11467
  //#endregion
11098
11468
  //#region src/chat/tools/update-event-automation.ts
@@ -11165,7 +11535,7 @@ function createUpdateEventAutomationTool(context, catalog) {
11165
11535
  function createEventAutomationTools(context, catalog) {
11166
11536
  if (context.source.kind !== "slack" || context.destination.platform !== "slack" || context.actor?.platform !== "slack") return {};
11167
11537
  return {
11168
- ...hasPluginEventCatalogEntries(catalog) ? { createEventAutomation: createEventAutomationTool(context, catalog) } : void 0,
11538
+ createEventAutomation: createEventAutomationTool(context, catalog),
11169
11539
  listEventAutomations: createListEventAutomationsTool(context, catalog),
11170
11540
  updateEventAutomation: createUpdateEventAutomationTool(context, catalog),
11171
11541
  deleteEventAutomation: createDeleteEventAutomationTool(context, catalog)
@@ -16700,6 +17070,8 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
16700
17070
  tools.listAgents = createListAgentsTool(context.conversationId);
16701
17071
  tools.stopAgent = createStopAgentTool(context.conversationId);
16702
17072
  }
17073
+ if (context.messageAgent) tools.messageAgent = createMessageAgentTool(context.messageAgent);
17074
+ if (context.source.kind === "agent_invocation") tools[REPORT_BLOCKED_TOOL_NAME] = createReportBlockedTool();
16703
17075
  if (context.automation) tools[FINISH_AUTOMATION_RUN_TOOL_NAME] = createFinishAutomationRunTool(context.automation);
16704
17076
  if (context.mcpToolManager) {
16705
17077
  tools.searchMcpTools = createSearchMcpToolsTool(context.mcpToolManager);
@@ -19656,6 +20028,7 @@ async function wireAgentTools(args) {
19656
20028
  surface: args.surface,
19657
20029
  ...args.currentActor ? { resolveActorIdentity: async () => await readActorIdentity(args.currentActor) } : void 0,
19658
20030
  ...args.durability.spawnAgent ? { spawnAgent: args.durability.spawnAgent } : void 0,
20031
+ ...args.durability.messageAgent ? { messageAgent: args.durability.messageAgent } : void 0,
19659
20032
  ...args.requestHandoff ? { handoff: args.requestHandoff } : void 0,
19660
20033
  ...args.run.dispatch?.declaresResult ? { automation: { sendsMessage: args.run.dispatch.outcomes?.length !== 0 } } : void 0
19661
20034
  };
@@ -20493,7 +20866,8 @@ async function compactActiveContextIfNeeded(args, deps) {
20493
20866
  const EXCLUSIVE_TOOL_NAMES = [
20494
20867
  HANDOFF_TOOL_NAME,
20495
20868
  "switchWorkspace",
20496
- FINISH_AUTOMATION_RUN_TOOL_NAME
20869
+ FINISH_AUTOMATION_RUN_TOOL_NAME,
20870
+ REPORT_BLOCKED_TOOL_NAME
20497
20871
  ];
20498
20872
  /** Return the exclusive tool name when one is present among the calls. */
20499
20873
  function exclusiveToolName(toolCalls) {
@@ -21229,7 +21603,7 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
21229
21603
  const sourceResult = timedOutResult ?? result;
21230
21604
  const projectedResult = wiring.projectActionReviewResult(toolCall.id, sourceResult);
21231
21605
  const override = timedOutResult || projectedResult !== result ? projectedResult : void 0;
21232
- if (toolCall.name === "finishAutomationRun" && !isError && !override) return { terminate: true };
21606
+ if ((toolCall.name === "finishAutomationRun" || toolCall.name === "reportBlocked") && !isError && !override) return { terminate: true };
21233
21607
  return override;
21234
21608
  },
21235
21609
  prepareNextTurnWithContext: async (nextTurn, hookSignal) => {
@@ -21589,9 +21963,11 @@ function createAgentRunner(execute, options) {
21589
21963
  const streamFn = options?.streamFn;
21590
21964
  const tracePropagation = options?.tracePropagation;
21591
21965
  const bindSpawnAgent = options?.bindSpawnAgent;
21966
+ const bindMessageAgent = options?.bindMessageAgent;
21592
21967
  const canBindSpawn = Boolean(bindSpawnAgent) && isExperimentalFeatureEnabled("subagents");
21593
21968
  return { run: async (run) => {
21594
21969
  const spawnAgent = bindSpawnAgent && canBindSpawn && !isAgentRunFeatureDisabled(run.disabledFeatures, "subagents") ? bindSpawnAgent(run) : void 0;
21970
+ const messageAgent = bindMessageAgent && canBindSpawn && (spawnAgent || run.source.kind === "agent_invocation") ? bindMessageAgent(run) : void 0;
21595
21971
  const nextRun = {
21596
21972
  ...run,
21597
21973
  environment: {
@@ -21599,13 +21975,14 @@ function createAgentRunner(execute, options) {
21599
21975
  attachmentStorage: run.environment?.attachmentStorage ?? attachmentStorage,
21600
21976
  sandboxTracePropagation: run.environment?.sandboxTracePropagation ?? tracePropagation
21601
21977
  },
21602
- ...spawnAgent ? { durability: {
21978
+ ...spawnAgent || messageAgent ? { durability: {
21603
21979
  ...run.durability,
21604
- spawnAgent
21980
+ ...spawnAgent ? { spawnAgent } : void 0,
21981
+ ...messageAgent ? { messageAgent } : void 0
21605
21982
  } } : void 0
21606
21983
  };
21607
21984
  if (run.instruction.storedAttachments?.length) {
21608
- const { loadInputImages } = await import("./images-CvMreau5.js").then((n) => n.n);
21985
+ const { loadInputImages } = await import("./images-DZliDtSH.js").then((n) => n.n);
21609
21986
  const storage = nextRun.environment?.attachmentStorage;
21610
21987
  if (!storage) throw new Error("Attachment storage is unavailable.");
21611
21988
  nextRun.instruction = {
@@ -21621,4 +21998,4 @@ function createAgentRunner(execute, options) {
21621
21998
  } };
21622
21999
  }
21623
22000
  //#endregion
21624
- export { getLatestAgentInvocationForConversation as $, resolveSandboxEgressProviderForHost as $t, resolveSlackChannelIdFromMessage as A, drainConversationMailbox as At, effectiveTaskOutcomes as B, requestAnotherSlice as Bt, renderBlockText as C, setConfigDefaults as Cn, checkInConversationWork as Ct, storeAttachment as D, completeConversationWork as Dt, requestAttachmentDeletion as E, completeConversationStop as Et, logScheduledAutomationRunSkipped as F, listActiveConversationIds as Ft, completeAgentInvocation as G, CONVERSATION_WORK_STALE_ENQUEUE_MS as Gt, stopChildAgentsOf as H, startConversationWork as Ht, scheduledAutomationRunAttributes as I, recordAttemptFailure as It, getActiveAgentInvocationForConversation as J, isFinalAttempt as Jt, createAgentInvocation as K, hasConversationStop as Kt, editEventAutomation as L, recordConversationRetry as Lt, getConversationInfo as M, getConversation as Mt, listThreadReplies as N, getConversationWorkState as Nt, parseSlackThreadId as O, countPendingConversationMessages as Ot, editScheduledAutomation as P, hasRunnableConversationWork as Pt, getAgentInvocationTurnId as Q, recordFinishedTurnForAutomatedLimit as Qt, AutomationEditError as R, releaseConversationWork as Rt, renderAttachmentText as S, getConfigDefaults as Sn, cancelHumanFacingPendingMessages as St, readLiveAttachment as T, clearExpiredConversationLease as Tt, recordSubagentEnded as U, stopConversationWork as Ut, resolveTaskOutcomes as V, requestConversationWork as Vt, recordSubagentStarted as W, CONVERSATION_WORK_CHECK_IN_INTERVAL_MS as Wt, getAgentInvocationMessageId as X, renderTaskInput as Xt, getAgentInvocation as Y, isInvalidConversationRecordError as Yt, getAgentInvocationResultMessageId as Z, maybePostAutomatedTurnLimitNotice as Zt, resolvePluginOAuthAccount as _, AuthorizationFlowDisabledError as _n, pluginEventCatalog as _t, buildAuthPauseResponse as a, consumeSandboxEgressPermissionDeniedSignal as an, markAgentInvocationParentMailboxAppended as at, unpublishedArtifactHeaders as b, runDispatchOutcome as bn, appendAndEnqueueInboundMessage as bt, deleteMcpAuthSessionsForUserProvider as c, formatProviderLabel as cn, reportReplacedAgentInvocation as ct, getMcpAuthSession as d, startOAuthFlow as dn, ingestEvent as dt, normalizeSandboxEgressTracePropagationDomains as en, isTerminalAgentInvocation as et, getMcpStoredOAuthCredentials as f, createUserTokenStore as fn, EVENT_WAIT_MS as ft, readStats as g, toolInvocationDestination as gn, eventGuidance as gt, latestProgressStatus as h, actorFromRun as hn, getEventCatalog as ht, executeCredentialedEgressRequest as i, consumeSandboxEgressAuthRequiredSignal as in, markAgentInvocationMailboxAppended as it, resolveSlackChannelIdFromThreadId as j, ensureConversationWake as jt, readSlackRawMessageContext as k, deadLetterAttempt as kt, deleteMcpServerSessionId as l, parseOAuthStatePayload as ln, getMcpProviderErrorAttributes as lt, PluginCredentialFailureError as m, RetryableDeliveryError as mn, timerWatchCredentialSubject as mt, executeAgentRun as n, SANDBOX_EGRESS_PROXY_PATH as nn, listPendingAgentInvocationMailboxAppends as nt, finalizeMcpAuthorization as o, parseSandboxEgressCredentialToken as on, markAgentInvocationPaused as ot, putMcpStoredOAuthCredentials as p, buildSteeringPiMessage as pn, isEventMailboxMetadata as pt, createAgentInvocationForResult as q, hasRunnableConversationWork$1 as qt, createContextCompactor as r, clearSandboxEgressSignals as rn, listPendingAgentInvocationResults as rt, deleteMcpAuthSession as s, sandboxEgressSignalsResponseSchema as sn, markAgentInvocationRunning as st, createAgentRunner as t, shouldPropagateSandboxEgressTrace as tn, listChildAgents as tt, deleteMcpStoredOAuthCredentials as u, resolveBaseUrl as un, processWorkspaceSnapshotJob as ut, publicArtifactHeaders as v, deliversFinalText as vn, ackMessages as vt, collectAttachmentGarbage as w, clearConsumedConversationWake as wt, lookupSlackUserProfile as x, shouldEmitDevAgentTrace as xn, beginConversationResume as xt, readPublicArtifact as y, finishedRunReply as yn, appendAndEnqueueExclusiveInboundMessage as yt, eventAutomationTriggerAvailable as z, removeActiveConversation as zt };
22001
+ export { getAgentInvocationIdForMessage as $, stopConversationWork as $t, resolveSlackChannelIdFromMessage as A, finishedRunReply as An, appendAndEnqueueInboundMessage as At, effectiveTaskOutcomes as B, drainConversationMailbox as Bt, renderBlockText as C, buildSteeringPiMessage as Cn, SLACK_EVENT_NAMESPACE as Ct, storeAttachment as D, AuthorizationFlowDisabledError as Dn, eventGuidance as Dt, requestAttachmentDeletion as E, toolInvocationDestination as En, automationEventCatalog as Et, logScheduledAutomationRunSkipped as F, clearExpiredConversationLease as Ft, listChildAgents as G, listActiveConversationIds as Gt, stopChildAgentsOf as H, getConversation as Ht, scheduledAutomationRunAttributes as I, completeConversationStop as It, createAgentInvocationForMessage as J, releaseConversationWork as Jt, completeAgentInvocation as K, recordAttemptFailure as Kt, editEventAutomation as L, completeConversationWork as Lt, getConversationInfo as M, shouldEmitDevAgentTrace as Mn, cancelHumanFacingPendingMessages as Mt, listThreadReplies as N, getConfigDefaults as Nn, checkInConversationWork as Nt, parseSlackThreadId as O, readBlockedReport as On, ackMessages as Ot, editScheduledAutomation as P, setConfigDefaults as Pn, clearConsumedConversationWake as Pt, getAgentInvocation as Q, startConversationWork as Qt, AutomationEditError as R, countPendingConversationMessages as Rt, renderAttachmentText as S, createUserTokenStore as Sn, getEventCatalog as St, readLiveAttachment as T, actorFromRun as Tn, slackChannelMessageReplyThreadTs as Tt, recordSubagentEnded as U, getConversationWorkState as Ut, resolveTaskOutcomes as V, ensureConversationWake as Vt, recordSubagentStarted as W, hasRunnableConversationWork as Wt, findChildAgentConversationId as X, requestAnotherSlice as Xt, createAgentInvocationFromLatest as Y, removeActiveConversation as Yt, getActiveAgentInvocationForConversation as Z, requestConversationWork as Zt, resolvePluginOAuthAccount as _, sandboxEgressSignalsResponseSchema as _n, processWorkspaceSnapshotJob as _t, buildAuthPauseResponse as a, isInvalidConversationRecordError as an, getLatestAgentInvocationForConversation as at, unpublishedArtifactHeaders as b, resolveBaseUrl as bn, isEventMailboxMetadata as bt, deleteMcpAuthSessionsForUserProvider as c, recordFinishedTurnForAutomatedLimit as cn, listPendingAgentInvocationResults as ct, getMcpAuthSession as d, shouldPropagateSandboxEgressTrace as dn, markAgentInvocationPaused as dt, CONVERSATION_WORK_CHECK_IN_INTERVAL_MS as en, getAgentInvocationIdForResult as et, getMcpStoredOAuthCredentials as f, SANDBOX_EGRESS_PROXY_PATH as fn, markAgentInvocationRunning as ft, readStats as g, parseSandboxEgressCredentialToken as gn, getMcpProviderErrorAttributes as gt, latestProgressStatus as h, consumeSandboxEgressPermissionDeniedSignal as hn, PARENT_AGENT_TARGET as ht, executeCredentialedEgressRequest as i, isFinalAttempt as in, getChildAgentLabel as it, resolveSlackChannelIdFromThreadId as j, runDispatchOutcome as jn, beginConversationResume as jt, readSlackRawMessageContext as k, deliversFinalText as kn, appendAndEnqueueExclusiveInboundMessage as kt, deleteMcpServerSessionId as l, resolveSandboxEgressProviderForHost as ln, markAgentInvocationMailboxAppended as lt, PluginCredentialFailureError as m, consumeSandboxEgressAuthRequiredSignal as mn, AgentMessageTargetError as mt, executeAgentRun as n, hasConversationStop as nn, getAgentInvocationResultMessageId as nt, finalizeMcpAuthorization as o, renderTaskInput as on, isTerminalAgentInvocation as ot, putMcpStoredOAuthCredentials as p, clearSandboxEgressSignals as pn, reportReplacedAgentInvocation as pt, createAgentInvocation as q, recordConversationRetry as qt, createContextCompactor as r, hasRunnableConversationWork$1 as rn, getAgentInvocationTurnId as rt, deleteMcpAuthSession as s, maybePostAutomatedTurnLimitNotice as sn, listPendingAgentInvocationMailboxAppends as st, createAgentRunner as t, CONVERSATION_WORK_STALE_ENQUEUE_MS as tn, getAgentInvocationMessageId as tt, deleteMcpStoredOAuthCredentials as u, normalizeSandboxEgressTracePropagationDomains as un, markAgentInvocationParentMailboxAppended as ut, publicArtifactHeaders as v, formatProviderLabel as vn, ingestEvent as vt, collectAttachmentGarbage as w, RetryableDeliveryError as wn, slackChannelMessageEvent as wt, lookupSlackUserProfile as x, startOAuthFlow as xn, timerWatchCredentialSubject as xt, readPublicArtifact as y, parseOAuthStatePayload as yn, EVENT_WAIT_MS as yt, eventAutomationTriggerAvailable as z, deadLetterAttempt as zt };