@sentry/junior 0.245.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 (53) hide show
  1. package/dist/{admin-BEbNsG87.js → admin-Bh-MYw-j.js} +1 -1
  2. package/dist/{agent-hooks-Caa0Qh_Q.js → agent-hooks-DpklSWZg.js} +8 -8
  3. package/dist/{agent-runner-BEuPFbbv.js → agent-runner-DSKpHjHW.js} +419 -160
  4. package/dist/api/schema.d.ts +23 -1
  5. package/dist/api/schema.js +2 -2
  6. package/dist/api.js +77 -19
  7. package/dist/app.js +30 -64
  8. package/dist/{briefs-DFAh_jTW.js → briefs-cZ5vWqP_.js} +2 -2
  9. package/dist/{catalog-runtime-wmq_fqff.js → catalog-runtime-CTaQXYqS.js} +1 -1
  10. package/dist/cli/chat.js +17 -13
  11. package/dist/cli/check.js +3 -3
  12. package/dist/cli/main.js +2 -2
  13. package/dist/cli/plugins.js +4 -4
  14. package/dist/cli/snapshot-create.js +3 -3
  15. package/dist/cli/upgrade.js +3 -3
  16. package/dist/{client-Bn9SRFCi.js → client--xtsAxKP.js} +2 -2
  17. package/dist/{config-_XAINZMN.js → config-BK53Ip5C.js} +2 -2
  18. package/dist/{conversation-privacy-BN5SGxsF.js → conversation-privacy-BDBFw6Ws.js} +1 -1
  19. package/dist/{credential-sync-BvaUm6HQ.js → credential-sync-BiuhC9Ds.js} +14 -14
  20. package/dist/{executor-CFIqoCNJ.js → executor-bmmuRY-w.js} +2 -2
  21. package/dist/{images-Ci43I3ek.js → images-DZliDtSH.js} +2 -2
  22. package/dist/instrumentation.js +2 -2
  23. package/dist/{job-queue-CGicV7x6.js → job-queue-Cz3M7gg4.js} +1 -1
  24. package/dist/{logging-Bs3dV81s.js → logging-DVtMYOdD.js} +1 -1
  25. package/dist/message-BSN-Ma5f.js +317 -0
  26. package/dist/nitro.js +3 -3
  27. package/dist/{oauth-authorization-COzWmMjO.js → oauth-authorization-By_MVRDC.js} +1 -1
  28. package/dist/{oauth-callback-server-17c9_39v.js → oauth-callback-server-BrUo6w1e.js} +2 -2
  29. package/dist/{pending-auth-Bnhg7Q9k.js → pending-auth-BuQLZ6dK.js} +2 -2
  30. package/dist/{profile-BA471c-l.js → profile-BevMNSlr.js} +4 -4
  31. package/dist/{registry-BYNOFO6e.js → registry-C2rSEio1.js} +1 -1
  32. package/dist/{resolve-rpz8No2i.js → resolve-qPhrznTf.js} +2 -2
  33. package/dist/{runner-C8DRk9X6.js → runner-CNh5eMWx.js} +9 -9
  34. package/dist/{sandbox-egress-signals-B8hurjFt.js → sandbox-egress-signals-BnDEKu8d.js} +1 -1
  35. package/dist/{schema-WRgR9EL6.js → schema-BWMRg8JS.js} +21 -1
  36. package/dist/{skills-C4yrD2NR.js → skills-C6zJbudq.js} +2 -2
  37. package/dist/{spawn-CdAaTXrY.js → spawn-C3efZSx3.js} +2 -2
  38. package/dist/{task-queue-BBxHzlIB.js → task-queue-BX4vG6uA.js} +3 -3
  39. package/dist/{task-runner-BqFapTjs.js → task-runner-T51kll0U.js} +10 -10
  40. package/dist/{tasks-Ddd7OAkp.js → tasks-B9OIjl2K.js} +8 -8
  41. package/dist/{thread-state-DqErwbA8.js → thread-state-AGZtVyXp.js} +6 -6
  42. package/dist/{turn-failure-response-DtL1-fOl.js → turn-failure-response-DJ8KU2bo.js} +8 -8
  43. package/dist/{turn-lifecycle-B5pzyD6W.js → turn-lifecycle-DZJkJ95q.js} +1 -1
  44. package/dist/{turn-session-routing-D6pTU5uF.js → turn-session-routing-mRqLbMZW.js} +1 -1
  45. package/dist/{validation-BAzlyNIc.js → validation-Dg0Ihxzo.js} +1 -1
  46. package/dist/{version-CiXDH3oe.js → version-Deub4try.js} +1 -1
  47. package/dist/version.js +1 -1
  48. package/dist/{viewer-wCn8xneI.js → viewer-D5hbx6TE.js} +3 -3
  49. package/dist/{web-authorization-d5MtGpfG.js → web-authorization-DCxixz5S.js} +6 -6
  50. package/dist/{web-input-BZTiCtvP.js → web-input-0sorYVLh.js} +62 -198
  51. package/dist/{work-4QPqTd5_.js → work-rm2X7jj4.js} +13 -6
  52. package/dist/{worker-mc62tybz.js → worker-bnGsAOP3.js} +5 -5
  53. package/package.json +5 -5
@@ -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-Bn9SRFCi.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-Bs3dV81s.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-BN5SGxsF.js";
5
- import { i as getChatConfig, n as botConfig, v as normalizeSlackEmojiName } from "./config-_XAINZMN.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-wCn8xneI.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-CFIqoCNJ.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-BYNOFO6e.js";
14
- import { n as pluginCatalogRuntime } from "./catalog-runtime-wmq_fqff.js";
15
- import { a as parseSkillInvocation, i as parseSkillFile, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-C4yrD2NR.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-Ddd7OAkp.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-Caa0Qh_Q.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
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-BA471c-l.js";
20
- import { n as canReusePendingAuthLink, t as abandonReplacedPendingAuth } from "./pending-auth-Bnhg7Q9k.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-COzWmMjO.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
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-rpz8No2i.js";
25
- import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-CGicV7x6.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");
@@ -9687,6 +9727,8 @@ function createUpdatePlanTool() {
9687
9727
  //#region src/chat/agent-invocations/types.ts
9688
9728
  const exactStringSchema = z.string().min(1).refine((value) => value === value.trim());
9689
9729
  const agentNameSchema = exactStringSchema.max(64);
9730
+ /** `messageAgent` target for the parent of a child agent. */
9731
+ const PARENT_AGENT_TARGET = "parent";
9690
9732
  const agentInvocationMailboxStatusSchema = z.enum(AGENT_INVOCATION_MAILBOX_STATUSES);
9691
9733
  const agentInvocationParentMailboxStatusSchema = z.enum(AGENT_INVOCATION_PARENT_MAILBOX_STATUSES);
9692
9734
  const agentBindingSchema = z.object({
@@ -9746,13 +9788,19 @@ const createAgentInvocationSchema = z.object({
9746
9788
  reasoningLevel: z.enum(TURN_REASONING_LEVELS).optional(),
9747
9789
  title: exactStringSchema.optional()
9748
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
+ ];
9749
9797
  //#endregion
9750
9798
  //#region src/chat/agent-invocations/errors.ts
9751
- /** Named child cannot accept another invocation until its active work ends. */
9752
- var AgentInvocationBusyError = class extends Error {
9799
+ /** A child agent already uses this name in the parent Conversation. */
9800
+ var AgentNameInUseError = class extends Error {
9753
9801
  constructor(name) {
9754
- super(`Named agent "${name}" already has active work`);
9755
- this.name = "AgentInvocationBusyError";
9802
+ super(`A child agent named "${name}" already exists`);
9803
+ this.name = "AgentNameInUseError";
9756
9804
  }
9757
9805
  };
9758
9806
  /** Parent already has the maximum number of non-terminal child invocations. */
@@ -9762,10 +9810,28 @@ var AgentInvocationLimitError = class extends Error {
9762
9810
  this.name = "AgentInvocationLimitError";
9763
9811
  }
9764
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
+ };
9765
9827
  //#endregion
9766
9828
  //#region src/chat/agent-invocations/store.ts
9767
9829
  const CREATE_LOCK_PREFIX = "junior:agent_invocation:create";
9768
- 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) {
9769
9835
  return `${CREATE_LOCK_PREFIX}:${parentConversationId}`;
9770
9836
  }
9771
9837
  const TERMINAL_AGENT_INVOCATION_STATUSES = [
@@ -9774,6 +9840,7 @@ const TERMINAL_AGENT_INVOCATION_STATUSES = [
9774
9840
  "failed",
9775
9841
  "stopped"
9776
9842
  ];
9843
+ /** Statuses of an agent invocation that has not finished. */
9777
9844
  const NON_TERMINAL_AGENT_INVOCATION_STATUSES = [
9778
9845
  "pending",
9779
9846
  "running",
@@ -9801,6 +9868,13 @@ function getNamedAgentConversationId(parentConversationId, name) {
9801
9868
  function getAgentInvocationIdForResult(resultInvocationId) {
9802
9869
  return stableId("agent-invocation", "result", resultInvocationId);
9803
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
+ }
9804
9878
  /** Return the stable turn identity advanced by one agent invocation. */
9805
9879
  function getAgentInvocationTurnId(invocationId) {
9806
9880
  return `agent-invocation:${invocationId}`;
@@ -9820,6 +9894,7 @@ function bindingFromRow(row) {
9820
9894
  parentConversationId: row.parentConversationId
9821
9895
  });
9822
9896
  }
9897
+ /** Parse one agent invocation row. */
9823
9898
  function invocationFromRow(row) {
9824
9899
  return agentInvocationSchema.parse({
9825
9900
  actor: row.actor,
@@ -9885,87 +9960,62 @@ async function getChildAgentLabel(childConversationId) {
9885
9960
  }).from(juniorAgentInvocations).where(eq(juniorAgentInvocations.childConversationId, childConversationId)).orderBy(sql`${juniorAgentInvocations.title} is null`, desc(juniorAgentInvocations.createdAt)).limit(1);
9886
9961
  return row;
9887
9962
  }
9888
- /** Bound the agent invocations that one child agent listing reads. */
9889
- const CHILD_AGENT_LIST_INVOCATION_LIMIT = 200;
9890
- /** Return the child state that one child's agent invocations show. */
9891
- function childAgentFromInvocations(invocations, resultInvocationIds) {
9892
- const latest = invocations[0];
9893
- let current = latest;
9894
- for (const invocation of invocations) if (!isTerminalAgentInvocation(invocation)) current = invocation;
9895
- const task = invocations.find((invocation) => !resultInvocationIds.has(invocation.invocationId)) ?? invocations[invocations.length - 1];
9896
- const terminal = isTerminalAgentInvocation(current) ? current : void 0;
9897
- return {
9898
- ...latest.agentName ? { agentName: latest.agentName } : void 0,
9899
- childConversationId: latest.childConversationId,
9900
- ...terminal && terminal.status !== "completed" ? { errorMessage: terminal.errorMessage } : void 0,
9901
- status: terminal?.parentMailboxStatus === "replaced" ? "pending" : terminal?.parentMailboxStatus === "skipped" ? "stopped" : current.status,
9902
- task: task.input,
9903
- ...task.title ? { title: task.title } : void 0,
9904
- updatedAtMs: Math.max(...invocations.map((invocation) => invocation.updatedAtMs))
9905
- };
9906
- }
9907
- /**
9908
- * List the direct child agents of one parent Conversation, with the newest
9909
- * activity first. The state comes from SQL agent invocation records, not from
9910
- * model output.
9911
- */
9912
- async function listChildAgents(parentConversationId) {
9913
- const db = getSqlExecutor().db();
9914
- const rows = await db.select().from(juniorAgentInvocations).where(eq(juniorAgentInvocations.parentConversationId, parentConversationId)).orderBy(desc(juniorAgentInvocations.createdAt)).limit(CHILD_AGENT_LIST_INVOCATION_LIMIT);
9915
- if (rows.length === 0) return [];
9916
- const byChild = /* @__PURE__ */ new Map();
9917
- for (const invocation of rows.map(invocationFromRow)) {
9918
- const invocations = byChild.get(invocation.childConversationId) ?? [];
9919
- invocations.push(invocation);
9920
- byChild.set(invocation.childConversationId, invocations);
9921
- }
9922
- const grandchildren = await db.select({ invocationId: juniorAgentInvocations.invocationId }).from(juniorAgentInvocations).where(inArray(juniorAgentInvocations.parentConversationId, [...byChild.keys()]));
9923
- const resultInvocationIds = new Set(grandchildren.map((row) => getAgentInvocationIdForResult(row.invocationId)));
9924
- return [...byChild.values()].map((invocations) => childAgentFromInvocations(invocations, resultInvocationIds)).sort((left, right) => right.updatedAtMs - left.updatedAtMs);
9925
- }
9926
9963
  async function getNonTerminalAgentInvocationForConversation(childConversationId) {
9927
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);
9928
9965
  return rows[0] ? invocationFromRow(rows[0]) : void 0;
9929
9966
  }
9930
- async function countNonTerminalAgentInvocationsForParent(parentConversationId) {
9931
- 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;
9932
9969
  }
9933
9970
  /**
9934
- * Create or replay one invocation, reusing named child conversations and
9935
- * 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`.
9936
9990
  */
9937
9991
  async function createAgentInvocation(rawInput, nowMs = Date.now()) {
9938
9992
  const input = createAgentInvocationSchema.parse(rawInput);
9939
9993
  const invocationId = getAgentInvocationId(input.parentConversationId, input.idempotencyKey);
9940
9994
  const childConversationId = input.agentName ? getNamedAgentConversationId(input.parentConversationId, input.agentName) : getUnnamedAgentConversationId(invocationId);
9941
- const lockName = createLockName(input.parentConversationId);
9995
+ const lockName = getChildAgentLockName(input.parentConversationId);
9942
9996
  return await getSqlExecutor().withLock(lockName, async () => {
9943
9997
  const existing = await getAgentInvocation(invocationId);
9944
9998
  if (existing) {
9945
9999
  if (!sameCreateInput(existing, input)) throw new Error(`Agent invocation idempotency key was reused with different input for ${invocationId}`);
9946
10000
  return existing;
9947
10001
  }
9948
- 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`);
9949
- 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);
9950
10008
  await getConversationStore().createChild({
9951
10009
  childConversationId,
9952
10010
  parentConversationId: input.parentConversationId,
9953
10011
  nowMs,
9954
10012
  source: "internal"
9955
10013
  });
9956
- if (input.agentName) {
9957
- await getSqlExecutor().db().insert(juniorAgentBindings).values({
9958
- childConversationId,
9959
- name: input.agentName,
9960
- parentConversationId: input.parentConversationId
9961
- }).onConflictDoNothing();
9962
- const binding = await getAgentBinding({
9963
- name: input.agentName,
9964
- parentConversationId: input.parentConversationId
9965
- });
9966
- if (!binding || binding.childConversationId !== childConversationId) throw new Error(`Named agent binding did not resolve to ${childConversationId}`);
9967
- if (await getNonTerminalAgentInvocationForConversation(childConversationId)) throw new AgentInvocationBusyError(input.agentName);
9968
- }
10014
+ if (input.agentName) await getSqlExecutor().db().insert(juniorAgentBindings).values({
10015
+ childConversationId,
10016
+ name: input.agentName,
10017
+ parentConversationId: input.parentConversationId
10018
+ });
9969
10019
  await getSqlExecutor().db().insert(juniorAgentInvocations).values({
9970
10020
  invocationId,
9971
10021
  parentConversationId: input.parentConversationId,
@@ -9991,13 +10041,14 @@ async function createAgentInvocation(rawInput, nowMs = Date.now()) {
9991
10041
  });
9992
10042
  }
9993
10043
  /**
9994
- * Create or replay the agent invocation that gives one child agent result to
9995
- * the child Conversation that started it. It continues that Conversation for
9996
- * the same parent, Actor, and credentials as `latest`. It skips the busy and
9997
- * 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.
9998
10049
  */
9999
- async function createAgentInvocationForResult(args, nowMs = Date.now()) {
10000
- const invocationId = getAgentInvocationIdForResult(args.resultInvocationId);
10050
+ async function createAgentInvocationFromLatest(args, nowMs = Date.now()) {
10051
+ const { invocationId } = args;
10001
10052
  await getSqlExecutor().db().insert(juniorAgentInvocations).values({
10002
10053
  invocationId,
10003
10054
  parentConversationId: args.latest.parentConversationId,
@@ -10019,6 +10070,73 @@ async function createAgentInvocationForResult(args, nowMs = Date.now()) {
10019
10070
  if (!invocation || invocation.childConversationId !== args.latest.childConversationId) throw new Error(`Agent invocation creation raced for ${invocationId}`);
10020
10071
  return invocation;
10021
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
+ }
10022
10140
  /**
10023
10141
  * Return whether a child Conversation has work that will start another agent
10024
10142
  * invocation on it: another open agent invocation, an open child agent, or a
@@ -10101,76 +10219,90 @@ async function completeAgentInvocation(args) {
10101
10219
  }).where(and(eq(juniorAgentInvocations.invocationId, args.invocationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES)));
10102
10220
  return await getAgentInvocation(args.invocationId);
10103
10221
  }
10104
- /**
10105
- * List one child Conversation and every Conversation below it, child first.
10106
- * It takes the create lock of each Conversation before it reads that
10107
- * Conversation's children, so no child agent can start in the tree until the
10108
- * caller's transaction ends.
10109
- */
10110
- async function lockAgentConversationTree(childConversationId) {
10111
- const executor = getSqlExecutor();
10112
- const conversationIds = [childConversationId];
10113
- let parents = conversationIds;
10114
- while (parents.length > 0) {
10115
- for (const parent of parents) await executor.withLock(createLockName(parent), async () => void 0);
10116
- parents = (await executor.db().selectDistinct({ childConversationId: juniorAgentInvocations.childConversationId }).from(juniorAgentInvocations).where(inArray(juniorAgentInvocations.parentConversationId, parents))).map((row) => row.childConversationId).filter((id) => !conversationIds.includes(id));
10117
- conversationIds.push(...parents);
10118
- }
10119
- return conversationIds;
10120
- }
10121
- /**
10122
- * Stop the agent invocations of one direct child agent and of every child
10123
- * agent below it. Returns undefined when the child does not belong to the
10124
- * parent.
10125
- */
10126
- async function stopChildAgentInvocations(args) {
10127
- 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;
10128
- 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);
10129
10225
  }
10130
- /**
10131
- * Stop the agent invocations of one agent Conversation and of every child
10132
- * agent below it. Open agent invocations become `stopped`. Results that would
10133
- * still start or report work in the tree become `skipped`.
10134
- */
10135
- async function stopAgentInvocationTree(conversationId, nowMs = Date.now()) {
10136
- 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);
10137
10230
  }
10138
- async function stopLockedAgentInvocationTree(conversationId, nowMs) {
10139
- const db = getSqlExecutor().db();
10140
- const now = new Date(nowMs);
10141
- const conversationIds = await lockAgentConversationTree(conversationId);
10142
- const stopped = await db.update(juniorAgentInvocations).set({
10143
- errorMessage: "Stopped by the parent conversation",
10144
- parentMailboxStatus: "skipped",
10145
- result: null,
10146
- status: "stopped",
10147
- terminalAt: now,
10148
- updatedAt: now
10149
- }).where(and(inArray(juniorAgentInvocations.childConversationId, conversationIds), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES))).returning();
10150
- await db.update(juniorAgentInvocations).set({
10151
- parentMailboxStatus: "skipped",
10152
- updatedAt: now
10153
- }).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);
10154
10242
  return {
10155
- conversationIds,
10156
- 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))
10157
10251
  };
10158
10252
  }
10159
- /** Return whether an agent invocation has finished. */
10160
- function isTerminalAgentInvocation(invocation) {
10161
- 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);
10162
10271
  }
10163
10272
  //#endregion
10164
10273
  //#region src/chat/tools/runtime/list-agents.ts
10165
10274
  const TASK_MAX_LENGTH = 1e3;
10166
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
+ }
10167
10298
  function truncate(text, maxLength) {
10168
10299
  return text.length > maxLength ? `${text.slice(0, maxLength)}…` : text;
10169
10300
  }
10170
10301
  const listAgentsOutputSchema = juniorToolOutputSchema.extend({ agents: z.array(z.object({
10171
10302
  conversation_id: z.string().min(1).describe("Child conversation ID. Pass it to searchConversationEvents to read the child's history."),
10172
- name: agentNameSchema.optional().describe("Child-agent name. Pass it to spawnAgent to reuse it."),
10173
- 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"),
10174
10306
  task: z.string().describe("Latest task given to the child agent"),
10175
10307
  title: z.string().optional().describe("Short display title of the latest task"),
10176
10308
  error: z.string().optional().describe("Why the child agent is blocked, failed, or stopped"),
@@ -10179,7 +10311,7 @@ const listAgentsOutputSchema = juniorToolOutputSchema.extend({ agents: z.array(z
10179
10311
  /** Create the read-only tool that lists the child agents of one conversation. */
10180
10312
  function createListAgentsTool(conversationId) {
10181
10313
  return zodTool({
10182
- 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.",
10183
10315
  annotations: {
10184
10316
  destructiveHint: false,
10185
10317
  idempotentHint: true,
@@ -10191,7 +10323,8 @@ function createListAgentsTool(conversationId) {
10191
10323
  execute: async () => ({ agents: (await listChildAgents(conversationId)).map((agent) => ({
10192
10324
  conversation_id: agent.childConversationId,
10193
10325
  ...agent.agentName ? { name: agent.agentName } : void 0,
10194
- status: agent.status,
10326
+ status: childAgentStatus(agent),
10327
+ ...agent.lastResult ? { last_result: agent.lastResult } : void 0,
10195
10328
  task: truncate(agent.task, TASK_MAX_LENGTH),
10196
10329
  ...agent.title ? { title: agent.title } : void 0,
10197
10330
  ...agent.errorMessage ? { error: truncate(agent.errorMessage, ERROR_MAX_LENGTH) } : void 0,
@@ -10200,12 +10333,59 @@ function createListAgentsTool(conversationId) {
10200
10333
  });
10201
10334
  }
10202
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
10203
10383
  //#region src/chat/tools/runtime/spawn-agent.ts
10204
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.") });
10205
10385
  /** Create the asynchronous child-agent tool for one parent run. */
10206
10386
  function createSpawnAgentTool(spawnAgent) {
10207
10387
  return zodTool({
10208
- 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.",
10209
10389
  annotations: {
10210
10390
  destructiveHint: false,
10211
10391
  idempotentHint: true,
@@ -10215,7 +10395,7 @@ function createSpawnAgentTool(spawnAgent) {
10215
10395
  inputSchema: z.object({
10216
10396
  task: z.string().trim().min(1).describe("Task for the child agent"),
10217
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."),
10218
- 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"),
10219
10399
  reasoning_level: z.enum(TURN_REASONING_LEVELS).nullable().optional().describe("Optional reasoning level for this delegated task only")
10220
10400
  }).strict(),
10221
10401
  outputSchema: spawnAgentOutputSchema,
@@ -10239,7 +10419,7 @@ function createSpawnAgentTool(spawnAgent) {
10239
10419
  toolCallId: options.toolCallId
10240
10420
  });
10241
10421
  } catch (error) {
10242
- 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 });
10243
10423
  if (error instanceof AgentInvocationLimitError) throw new ToolInputError(`${error.message}. Wait for active children to finish before spawning more.`, { cause: error });
10244
10424
  throw error;
10245
10425
  }
@@ -10362,6 +10542,61 @@ async function finishStoppedTree(tree, conversationIdsToStop, state) {
10362
10542
  state
10363
10543
  });
10364
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
+ }
10365
10600
  //#endregion
10366
10601
  //#region src/chat/tools/runtime/stop-agent.ts
10367
10602
  const stopAgentOutputSchema = juniorToolOutputSchema.extend({
@@ -10371,7 +10606,7 @@ const stopAgentOutputSchema = juniorToolOutputSchema.extend({
10371
10606
  /** Create the tool that stops one child agent of one conversation. */
10372
10607
  function createStopAgentTool(conversationId) {
10373
10608
  return zodTool({
10374
- 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.",
10375
10610
  annotations: {
10376
10611
  destructiveHint: true,
10377
10612
  idempotentHint: true,
@@ -10432,6 +10667,23 @@ function createFinishAutomationRunTool(options) {
10432
10667
  });
10433
10668
  }
10434
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
10435
10687
  //#region src/chat/tools/list-watches.ts
10436
10688
  const listedResourceWatchSchema = z.object({
10437
10689
  id: z.string().min(1),
@@ -16818,6 +17070,8 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
16818
17070
  tools.listAgents = createListAgentsTool(context.conversationId);
16819
17071
  tools.stopAgent = createStopAgentTool(context.conversationId);
16820
17072
  }
17073
+ if (context.messageAgent) tools.messageAgent = createMessageAgentTool(context.messageAgent);
17074
+ if (context.source.kind === "agent_invocation") tools[REPORT_BLOCKED_TOOL_NAME] = createReportBlockedTool();
16821
17075
  if (context.automation) tools[FINISH_AUTOMATION_RUN_TOOL_NAME] = createFinishAutomationRunTool(context.automation);
16822
17076
  if (context.mcpToolManager) {
16823
17077
  tools.searchMcpTools = createSearchMcpToolsTool(context.mcpToolManager);
@@ -19774,6 +20028,7 @@ async function wireAgentTools(args) {
19774
20028
  surface: args.surface,
19775
20029
  ...args.currentActor ? { resolveActorIdentity: async () => await readActorIdentity(args.currentActor) } : void 0,
19776
20030
  ...args.durability.spawnAgent ? { spawnAgent: args.durability.spawnAgent } : void 0,
20031
+ ...args.durability.messageAgent ? { messageAgent: args.durability.messageAgent } : void 0,
19777
20032
  ...args.requestHandoff ? { handoff: args.requestHandoff } : void 0,
19778
20033
  ...args.run.dispatch?.declaresResult ? { automation: { sendsMessage: args.run.dispatch.outcomes?.length !== 0 } } : void 0
19779
20034
  };
@@ -20611,7 +20866,8 @@ async function compactActiveContextIfNeeded(args, deps) {
20611
20866
  const EXCLUSIVE_TOOL_NAMES = [
20612
20867
  HANDOFF_TOOL_NAME,
20613
20868
  "switchWorkspace",
20614
- FINISH_AUTOMATION_RUN_TOOL_NAME
20869
+ FINISH_AUTOMATION_RUN_TOOL_NAME,
20870
+ REPORT_BLOCKED_TOOL_NAME
20615
20871
  ];
20616
20872
  /** Return the exclusive tool name when one is present among the calls. */
20617
20873
  function exclusiveToolName(toolCalls) {
@@ -21347,7 +21603,7 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
21347
21603
  const sourceResult = timedOutResult ?? result;
21348
21604
  const projectedResult = wiring.projectActionReviewResult(toolCall.id, sourceResult);
21349
21605
  const override = timedOutResult || projectedResult !== result ? projectedResult : void 0;
21350
- if (toolCall.name === "finishAutomationRun" && !isError && !override) return { terminate: true };
21606
+ if ((toolCall.name === "finishAutomationRun" || toolCall.name === "reportBlocked") && !isError && !override) return { terminate: true };
21351
21607
  return override;
21352
21608
  },
21353
21609
  prepareNextTurnWithContext: async (nextTurn, hookSignal) => {
@@ -21707,9 +21963,11 @@ function createAgentRunner(execute, options) {
21707
21963
  const streamFn = options?.streamFn;
21708
21964
  const tracePropagation = options?.tracePropagation;
21709
21965
  const bindSpawnAgent = options?.bindSpawnAgent;
21966
+ const bindMessageAgent = options?.bindMessageAgent;
21710
21967
  const canBindSpawn = Boolean(bindSpawnAgent) && isExperimentalFeatureEnabled("subagents");
21711
21968
  return { run: async (run) => {
21712
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;
21713
21971
  const nextRun = {
21714
21972
  ...run,
21715
21973
  environment: {
@@ -21717,13 +21975,14 @@ function createAgentRunner(execute, options) {
21717
21975
  attachmentStorage: run.environment?.attachmentStorage ?? attachmentStorage,
21718
21976
  sandboxTracePropagation: run.environment?.sandboxTracePropagation ?? tracePropagation
21719
21977
  },
21720
- ...spawnAgent ? { durability: {
21978
+ ...spawnAgent || messageAgent ? { durability: {
21721
21979
  ...run.durability,
21722
- spawnAgent
21980
+ ...spawnAgent ? { spawnAgent } : void 0,
21981
+ ...messageAgent ? { messageAgent } : void 0
21723
21982
  } } : void 0
21724
21983
  };
21725
21984
  if (run.instruction.storedAttachments?.length) {
21726
- const { loadInputImages } = await import("./images-Ci43I3ek.js").then((n) => n.n);
21985
+ const { loadInputImages } = await import("./images-DZliDtSH.js").then((n) => n.n);
21727
21986
  const storage = nextRun.environment?.attachmentStorage;
21728
21987
  if (!storage) throw new Error("Attachment storage is unavailable.");
21729
21988
  nextRun.instruction = {
@@ -21739,4 +21998,4 @@ function createAgentRunner(execute, options) {
21739
21998
  } };
21740
21999
  }
21741
22000
  //#endregion
21742
- export { getChildAgentLabel as $, isInvalidConversationRecordError as $t, resolveSlackChannelIdFromMessage as A, completeConversationStop as At, effectiveTaskOutcomes as B, recordAttemptFailure as Bt, renderBlockText as C, finishedRunReply as Cn, appendAndEnqueueExclusiveInboundMessage as Ct, storeAttachment as D, setConfigDefaults as Dn, checkInConversationWork as Dt, requestAttachmentDeletion as E, getConfigDefaults as En, cancelHumanFacingPendingMessages as Et, logScheduledAutomationRunSkipped as F, ensureConversationWake as Ft, completeAgentInvocation as G, requestConversationWork as Gt, stopChildAgentsOf as H, releaseConversationWork as Ht, scheduledAutomationRunAttributes as I, getConversation as It, getActiveAgentInvocationForConversation as J, CONVERSATION_WORK_CHECK_IN_INTERVAL_MS as Jt, createAgentInvocation as K, startConversationWork as Kt, editEventAutomation as L, getConversationWorkState as Lt, getConversationInfo as M, countPendingConversationMessages as Mt, listThreadReplies as N, deadLetterAttempt as Nt, parseSlackThreadId as O, clearConsumedConversationWake as Ot, editScheduledAutomation as P, drainConversationMailbox as Pt, getAgentInvocationTurnId as Q, isFinalAttempt as Qt, AutomationEditError as R, hasRunnableConversationWork as Rt, renderAttachmentText as S, deliversFinalText as Sn, ackMessages as St, readLiveAttachment as T, shouldEmitDevAgentTrace as Tn, beginConversationResume as Tt, recordSubagentEnded as U, removeActiveConversation as Ut, resolveTaskOutcomes as V, recordConversationRetry as Vt, recordSubagentStarted as W, requestAnotherSlice as Wt, getAgentInvocationMessageId as X, hasConversationStop as Xt, getAgentInvocation as Y, CONVERSATION_WORK_STALE_ENQUEUE_MS as Yt, getAgentInvocationResultMessageId as Z, hasRunnableConversationWork$1 as Zt, resolvePluginOAuthAccount as _, buildSteeringPiMessage as _n, SLACK_EVENT_NAMESPACE as _t, buildAuthPauseResponse as a, shouldPropagateSandboxEgressTrace as an, markAgentInvocationMailboxAppended as at, unpublishedArtifactHeaders as b, toolInvocationDestination as bn, automationEventCatalog as bt, deleteMcpAuthSessionsForUserProvider as c, consumeSandboxEgressAuthRequiredSignal as cn, markAgentInvocationRunning as ct, getMcpAuthSession as d, sandboxEgressSignalsResponseSchema as dn, processWorkspaceSnapshotJob as dt, renderTaskInput as en, getLatestAgentInvocationForConversation as et, getMcpStoredOAuthCredentials as f, formatProviderLabel as fn, ingestEvent as ft, readStats as g, createUserTokenStore as gn, getEventCatalog as gt, latestProgressStatus as h, startOAuthFlow as hn, timerWatchCredentialSubject as ht, executeCredentialedEgressRequest as i, normalizeSandboxEgressTracePropagationDomains as in, listPendingAgentInvocationResults as it, resolveSlackChannelIdFromThreadId as j, completeConversationWork as jt, readSlackRawMessageContext as k, clearExpiredConversationLease as kt, deleteMcpServerSessionId as l, consumeSandboxEgressPermissionDeniedSignal as ln, reportReplacedAgentInvocation as lt, PluginCredentialFailureError as m, resolveBaseUrl as mn, isEventMailboxMetadata as mt, executeAgentRun as n, recordFinishedTurnForAutomatedLimit as nn, listChildAgents as nt, finalizeMcpAuthorization as o, SANDBOX_EGRESS_PROXY_PATH as on, markAgentInvocationParentMailboxAppended as ot, putMcpStoredOAuthCredentials as p, parseOAuthStatePayload as pn, EVENT_WAIT_MS as pt, createAgentInvocationForResult as q, stopConversationWork as qt, createContextCompactor as r, resolveSandboxEgressProviderForHost as rn, listPendingAgentInvocationMailboxAppends as rt, deleteMcpAuthSession as s, clearSandboxEgressSignals as sn, markAgentInvocationPaused as st, createAgentRunner as t, maybePostAutomatedTurnLimitNotice as tn, isTerminalAgentInvocation as tt, deleteMcpStoredOAuthCredentials as u, parseSandboxEgressCredentialToken as un, getMcpProviderErrorAttributes as ut, publicArtifactHeaders as v, RetryableDeliveryError as vn, slackChannelMessageEvent as vt, collectAttachmentGarbage as w, runDispatchOutcome as wn, appendAndEnqueueInboundMessage as wt, lookupSlackUserProfile as x, AuthorizationFlowDisabledError as xn, eventGuidance as xt, readPublicArtifact as y, actorFromRun as yn, slackChannelMessageReplyThreadTs as yt, eventAutomationTriggerAvailable as z, listActiveConversationIds 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 };