@sentry/junior 0.242.0 → 0.244.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 (67) hide show
  1. package/dist/{admin-C54kvHVC.js → admin-CF9lQg0M.js} +1 -1
  2. package/dist/{agent-hooks-BVttjZvb.js → agent-hooks-Cj_rSeWd.js} +22 -12
  3. package/dist/agent-invocations-BO6ddZax.js +177 -0
  4. package/dist/{agent-runner-BTgnxJ7g.js → agent-runner-DtEtbaOb.js} +646 -70
  5. package/dist/api/schema.d.ts +67 -1
  6. package/dist/api/schema.js +4 -4
  7. package/dist/api.js +53 -22
  8. package/dist/{app-MEd7wbF0.d.ts → app-DRLh-8zs.d.ts} +5 -0
  9. package/dist/app.d.ts +1 -1
  10. package/dist/app.js +41 -24
  11. package/dist/{briefs-CKUsv9F1.js → briefs-BMzTiGJO.js} +4 -4
  12. package/dist/{catalog-runtime-C2YkfMZz.js → catalog-runtime-CpxhLwZG.js} +1 -1
  13. package/dist/cli/chat.js +12 -12
  14. package/dist/cli/check.js +3 -3
  15. package/dist/cli/main.js +2 -2
  16. package/dist/cli/plugins.js +4 -4
  17. package/dist/cli/snapshot-create.js +3 -3
  18. package/dist/cli/upgrade.js +3 -3
  19. package/dist/{client-D51G-Jdg.js → client-BVaGEb1-.js} +2 -2
  20. package/dist/{config-C6Uu3Re9.js → config-q9xXVN_5.js} +7 -2
  21. package/dist/{conversation-DwD-vquD.js → conversation-BxvgH64Q.js} +5 -1
  22. package/dist/{conversation-privacy-CzL47nXs.js → conversation-privacy-Dyh_63Ee.js} +1 -1
  23. package/dist/{credential-sync-DvDaE-Uj.js → credential-sync-Bsn-6m16.js} +14 -14
  24. package/dist/{executor-CGUukwus.js → executor-DA7eHhmC.js} +5 -124
  25. package/dist/{history-BLTUgFYO.js → history-Nte1oVed.js} +4 -0
  26. package/dist/{images-DSxZt5vk.js → images-CvMreau5.js} +3 -3
  27. package/dist/instrumentation.js +2 -2
  28. package/dist/{job-queue-hLylM9cy.js → job-queue-mVyF9jgv.js} +1 -1
  29. package/dist/{logging-BdZ_mK3q.js → logging-z7_w-KN_.js} +1 -1
  30. package/dist/nitro.d.ts +1 -1
  31. package/dist/nitro.js +3 -3
  32. package/dist/{oauth-authorization-BokpeHv-.js → oauth-authorization-Db1yGVvG.js} +1 -1
  33. package/dist/{oauth-callback-server-C3BOSmT_.js → oauth-callback-server-_MfOtSxS.js} +2 -2
  34. package/dist/{pending-auth-DOzL4D24.js → pending-auth-OndKPW4M.js} +2 -2
  35. package/dist/{profile-2gPzzMib.js → profile-yeKVNECl.js} +4 -4
  36. package/dist/{prompt-CMU-B4FA.js → prompt-CX36ZlbV.js} +1 -1
  37. package/dist/{registry-XJkXDNK2.js → registry-DvzzL8-v.js} +1 -1
  38. package/dist/{resolve-DhcdtDt6.js → resolve-4u5gEU04.js} +2 -2
  39. package/dist/{runner-CGyrnIPm.js → runner-Y6A6pww4.js} +10 -10
  40. package/dist/{sandbox-egress-signals-D6F3OZJP.js → sandbox-egress-signals-D7AOkuoe.js} +1 -1
  41. package/dist/{schema-Cabm4jcj.js → schema-Dyyi9oRc.js} +35 -3
  42. package/dist/{skills-C6PqcBPU.js → skills-DFduSvuO.js} +2 -2
  43. package/dist/{spawn-DL9x3T1o.js → spawn-DYg7jhhR.js} +7 -5
  44. package/dist/{task-queue-vZnBGu9B.js → task-queue-BQ0vnumm.js} +3 -3
  45. package/dist/{task-runner-C-c0t5F5.js → task-runner-D4jvUpFV.js} +12 -11
  46. package/dist/{tasks-EqCtmUJ2.js → tasks-DBZAgTx7.js} +49 -17
  47. package/dist/{thread-state-B8IKs_J8.js → thread-state-hj70P2Cp.js} +7 -7
  48. package/dist/{turn-failure-response-BS7OO1BE.js → turn-failure-response-S2pwtR8b.js} +9 -9
  49. package/dist/{turn-lifecycle-Cxrt8qMD.js → turn-lifecycle-CNFOMAJq.js} +1 -1
  50. package/dist/{turn-session-routing-BlGt2AUb.js → turn-session-routing-1eTgv6ns.js} +1 -1
  51. package/dist/{validation-C8hci6pj.js → validation-DcsBjzdi.js} +1 -1
  52. package/dist/{version-CjnHQ8aY.js → version-DVCHJYcY.js} +1 -1
  53. package/dist/version.js +1 -1
  54. package/dist/{viewer-BOgEy5zC.js → viewer-BBnCMR1g.js} +6 -8
  55. package/dist/{web-authorization-DSMV5tMK.js → web-authorization-CTltRLsB.js} +6 -6
  56. package/dist/{web-input-TD5P9jdo.js → web-input-CIsRGF07.js} +227 -31
  57. package/dist/{work-D39OAwP1.js → work-fje9VXgQ.js} +111 -225
  58. package/dist/{worker-CWiLgCWH.js → worker-Byd2Et9r.js} +5 -5
  59. package/migrations/0050_agent_invocation_parent_mailbox.sql +4 -0
  60. package/migrations/0051_agent_invocation_parent_tool_call.sql +1 -0
  61. package/migrations/0052_agent_invocation_title.sql +1 -0
  62. package/migrations/meta/0050_snapshot.json +3753 -0
  63. package/migrations/meta/0051_snapshot.json +3759 -0
  64. package/migrations/meta/0052_snapshot.json +3765 -0
  65. package/migrations/meta/_journal.json +22 -1
  66. package/package.json +5 -5
  67. package/dist/destinations-BeBiJeEM.js +0 -29
@@ -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-D51G-Jdg.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-BdZ_mK3q.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-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";
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-CzL47nXs.js";
5
- import { i as getChatConfig, n as botConfig, v as normalizeSlackEmojiName } from "./config-C6Uu3Re9.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";
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-BOgEy5zC.js";
9
- import { n as juniorDestinations } from "./destinations-BeBiJeEM.js";
10
- import { A as juniorArtifacts, D as juniorConversations, O as juniorIdentities, S as juniorConversationAnnotations, T as juniorAttachments, _ as juniorConversationBriefs, a as juniorSchedulerRuns, f as AGENT_INVOCATION_MAILBOX_STATUSES, l as juniorStats } from "./executor-CGUukwus.js";
11
- import { d as instructionActors, f as instructionProvenanceFor, p as sameActorIdentity, t as KNOWN_CONVERSATION_EVENT_TYPES, u as contextProvenance } from "./history-BLTUgFYO.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";
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-XJkXDNK2.js";
14
- import { n as pluginCatalogRuntime } from "./catalog-runtime-C2YkfMZz.js";
15
- import { a as parseSkillInvocation, i as parseSkillFile, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-C6PqcBPU.js";
16
- import { A as formatSlackLink, Ct as loadConversationProjection, D as automationRevision, Dt as openConversationProjection, Et as loadTurnRoute, F as addReactionToMessage, G as loadTurnCheckpoint, Gt as fallbackShortTitle, I as getSlackMessagePermalink, It as recordToolExecutionStarted, K as saveTurnCheckpoint, Kt as resolveTaskTitle, L as openSlackDirectMessage, Lt as recordTurnRoute, Mt as recordAuthorizationRequested, N as slackMention, Nt as recordGuardianActionReviewed, Ot as recordAgentsInstructionsUpdated, Pt as recordMcpProviderConnected, R as postSlackEphemeralMessage, St as loadConnectedMcpProviders, V as uploadFilesToConversation, W as continuableMessages, _t as trimTrailingAssistantMessages, a as readScheduledAutomation, an as getAgentContextCompactionTriggerTokens, at as getPiMessageRole, b as saveEventAutomation, cn as piMessageFromHistoryItem, ct as hasRuntimeTurnContext, dt as isContinuablePiBoundary, et as TurnSliceLimitExceededError, f as createEventAutomation, ft as isToolResultError, g as getEventAutomation, gt as stripRuntimeTurnContext, ht as retainRuntimeTurnContext, in as estimateTextTokens, it as extractAssistantText, k as escapeSlackMrkdwnText, kt as recordAttachmentsDelivered, mt as normalizeToolNameFromResult, o as saveScheduledAutomation, on as getAgentContextInputLimitTokens, ot as getTerminalAssistantMessages, p as deleteEventAutomation, pt as isToolResultMessage, r as listScheduledAutomationsForTeam, rt as isTurnExecutionLimitExceededError, sn as historyItemFromPiMessage, st as getUserMessageInstructionText, t as createScheduledAutomation, tt as assertTurnToolCallLimit, ut as isAssistantMessage, v as listEventAutomationsForTeam, vt as TURN_CONTEXT_TAG, x as setEventAutomationStatus, yt as ToolInputError, z as postSlackMessage } from "./tasks-EqCtmUJ2.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-BVttjZvb.js";
18
- import { F as isVisionImageMediaType, G as removedCardSchema, L as briefLinkSchema, R as briefOutcomeStatusSchema, z as conversationBriefSchema } from "./conversation-DwD-vquD.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-2gPzzMib.js";
20
- import { n as canReusePendingAuthLink, t as abandonReplacedPendingAuth } from "./pending-auth-DOzL4D24.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-BokpeHv-.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";
22
22
  import { i as isValidTimeZone, r as getZonedDateTimeParts } from "./cadence-D00SCzT1.js";
23
- import { r as EVENT_AUTHOR_ID } from "./prompt-CMU-B4FA.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-DhcdtDt6.js";
25
- import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-hLylM9cy.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";
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, desc, eq, gt, gte, inArray, isNotNull, isNull, lt, lte, ne, notExists, or, sql } from "drizzle-orm";
37
+ import { and, asc, count, 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";
@@ -676,6 +676,7 @@ function createRepositoryInstructionsContext(options) {
676
676
  const context = currentUpdate?.context ?? currentContext;
677
677
  const messages = [...context.messages, message];
678
678
  options.setMessages(messages);
679
+ await options.commit(messages);
679
680
  visibleFingerprint = instructions?.fingerprint;
680
681
  instructionsVisible = Boolean(instructions);
681
682
  await options.onTransition?.(instructions);
@@ -1185,7 +1186,7 @@ function trimContextForRouter(text) {
1185
1186
  originalCharCount: trimmed.length
1186
1187
  };
1187
1188
  }
1188
- function buildClassifierSystemPrompt(profiles, profileNames, defaultProfile) {
1189
+ function buildClassifierSystemPrompt(profiles, profileNames, currentProfile) {
1189
1190
  const profileList = formatModelProfiles(profiles, profileNames);
1190
1191
  return [
1191
1192
  "Select the model profile and reasoning level for the current request. Do not execute the task.",
@@ -1200,10 +1201,10 @@ function buildClassifierSystemPrompt(profiles, profileNames, defaultProfile) {
1200
1201
  "Use xhigh for the most complex tasks: code changes, debugging/root-cause analysis, broad refactors, architecture decisions, multi-file implementation, or any task where deep reasoning across multiple systems or files is required.",
1201
1202
  "When unsure between two non-none buckets, choose the higher bucket. Do not use low as the default.",
1202
1203
  "",
1203
- `Default profile: "${defaultProfile}". Keep it unless another profile's description is a clearly better fit.`,
1204
+ `Current profile: "${currentProfile}". Keep it unless another profile's description is a clearly better fit.`,
1204
1205
  "Configured profiles (use each description's use and avoid cases; names are labels only; non-default does not mean stronger):",
1205
1206
  profileList,
1206
- "A previous profile choice is not binding. Keep follow-ups about unfinished work with that task; do not carry completed work into a new request.",
1207
+ "Keep follow-ups about unfinished work on the current profile. A new request after completed work can move to another profile.",
1207
1208
  "",
1208
1209
  "Classify based on the substance of the task, not the length of the current message. When the current instruction is a short affirmation (for example: 'go', 'do it', 'yes please', 'proceed') and prior thread context contains a pending task, classify the pending task — not the affirmation.",
1209
1210
  "",
@@ -1270,7 +1271,7 @@ async function selectTurnRoute(args) {
1270
1271
  actorId: args.context?.actorId ?? "",
1271
1272
  runId: args.context?.runId ?? ""
1272
1273
  },
1273
- defaultProfile: args.defaultProfile,
1274
+ currentProfile: args.currentProfile,
1274
1275
  profiles: args.profiles,
1275
1276
  prompt
1276
1277
  }), { minimum: trimmedContext || turnBlockCount > 0 ? "medium" : void 0 }), args.profiles);
@@ -1307,7 +1308,7 @@ function applyProfileReasoningOverride(route, profiles) {
1307
1308
  * The fast classifier model occasionally returns a malformed object
1308
1309
  * (`ProviderError` kind `invalid_response`, e.g. the AI SDK's
1309
1310
  * `NoObjectGeneratedError`) even though a retry usually succeeds. Give it
1310
- * one retry before falling back to `defaultProfile`; any other error kind
1311
+ * one retry before falling back to `currentProfile`; any other error kind
1311
1312
  * still falls back immediately.
1312
1313
  */
1313
1314
  const CLASSIFIER_MAX_ATTEMPTS = 2;
@@ -1325,7 +1326,7 @@ async function classifyTurn(args) {
1325
1326
  metadata: args.metadata,
1326
1327
  prompt: args.prompt,
1327
1328
  thinkingLevel: "low",
1328
- system: buildClassifierSystemPrompt(args.profiles, Object.keys(args.profiles), args.defaultProfile),
1329
+ system: buildClassifierSystemPrompt(args.profiles, Object.keys(args.profiles), args.currentProfile),
1329
1330
  temperature: 0,
1330
1331
  promptName: "junior.thinking_route"
1331
1332
  });
@@ -1334,7 +1335,7 @@ async function classifyTurn(args) {
1334
1335
  if (parsed.confidence < CLASSIFIER_CONFIDENCE_THRESHOLD) return {
1335
1336
  confidence: parsed.confidence,
1336
1337
  ...result.costUsd !== void 0 ? { costUsd: result.costUsd } : void 0,
1337
- profile: args.defaultProfile,
1338
+ profile: args.currentProfile,
1338
1339
  reasoningLevel: CLASSIFIER_FALLBACK_REASONING_LEVEL,
1339
1340
  reason: `low_confidence_medium_default:${reason}`,
1340
1341
  source: "router"
@@ -1354,7 +1355,7 @@ async function classifyTurn(args) {
1354
1355
  }
1355
1356
  logWarn("turn.router.classifier.failed", { "exception.message": lastError instanceof Error ? lastError.message : String(lastError) });
1356
1357
  return {
1357
- profile: args.defaultProfile,
1358
+ profile: args.currentProfile,
1358
1359
  reasoningLevel: CLASSIFIER_FALLBACK_REASONING_LEVEL,
1359
1360
  reason: "classifier_error_default",
1360
1361
  source: "router"
@@ -1934,7 +1935,12 @@ function buildOutputSection(platform, mode) {
1934
1935
  ].join("\n");
1935
1936
  }
1936
1937
  function buildIdentitySection(platform) {
1937
- return ["# Identity", platform === "slack" ? `Your Slack username is \`${botConfig.userName}\`.` : `Your assistant name is \`${botConfig.userName}\`.`].join("\n");
1938
+ if (platform !== "slack") return ["# Identity", `Your assistant name is \`${botConfig.userName}\`.`].join("\n");
1939
+ return [
1940
+ "# Identity",
1941
+ `Your Slack username is \`${botConfig.userName}\`. Your Slack user ID is \`slack.bot_user_id\` in the runtime context.`,
1942
+ "Slack text shows people as `<@U…>` and channels as `<#C…>`. A mention of your own user ID means you."
1943
+ ].join("\n");
1938
1944
  }
1939
1945
  function buildPersonalitySection() {
1940
1946
  return ["# Personality", JUNIOR_PERSONALITY.trim()].join("\n");
@@ -1948,7 +1954,8 @@ function buildRuntimeSection(params) {
1948
1954
  `- sandbox.workspace_root: ${escapeXml(SANDBOX_WORKSPACE_ROOT)}`,
1949
1955
  params.conversationId ? `- gen_ai.conversation.id: ${escapeXml(params.conversationId)}` : "",
1950
1956
  params.slackConversation?.type ? `- slack.conversation.type: ${escapeXml(params.slackConversation.type)}` : "",
1951
- params.slackConversation?.name ? `- slack.conversation.name: ${escapeXml(params.slackConversation.name)}` : ""
1957
+ params.slackConversation?.name ? `- slack.conversation.name: ${escapeXml(params.slackConversation.name)}` : "",
1958
+ params.slackConversation?.botUserId ? `- slack.bot_user_id: ${escapeXml(params.slackConversation.botUserId)}` : ""
1952
1959
  ].filter(Boolean);
1953
1960
  if (lines.length === 0) return null;
1954
1961
  return renderTagBlock("runtime", lines.join("\n"));
@@ -5343,7 +5350,7 @@ function isEventMailboxMetadata(value) {
5343
5350
  const event = record.event;
5344
5351
  if (!event || typeof event !== "object" || Array.isArray(event)) return false;
5345
5352
  const fields = event;
5346
- return typeof fields.eventKey === "string" && typeof fields.eventType === "string" && typeof fields.namespace === "string" && typeof fields.identifier === "string" && typeof fields.subscriptionId === "string" && (fields.objectType === void 0 || objectTypeSchema.safeParse(fields.objectType).success) && (fields.trustedSummary === void 0 || typeof fields.trustedSummary === "string");
5353
+ return typeof fields.eventKey === "string" && typeof fields.eventType === "string" && typeof fields.namespace === "string" && typeof fields.identifier === "string" && (fields.subscriptionId === void 0 || typeof fields.subscriptionId === "string") && (fields.objectType === void 0 || objectTypeSchema.safeParse(fields.objectType).success) && (fields.trustedSummary === void 0 || typeof fields.trustedSummary === "string");
5347
5354
  }
5348
5355
  /**
5349
5356
  * Return the timer creator subject when every event in the batch carries a
@@ -5355,7 +5362,7 @@ function timerWatchCredentialSubject(conversationId, messages) {
5355
5362
  const metadata = message.input.metadata;
5356
5363
  if (!isEventMailboxMetadata(metadata)) return void 0;
5357
5364
  const parsed = credentialSubjectSchema.safeParse(metadata.credentialSubject);
5358
- if (!parsed.success || !verifyTimerWatchCredentialSubject({
5365
+ if (!parsed.success || !metadata.event.subscriptionId || !verifyTimerWatchCredentialSubject({
5359
5366
  conversationId,
5360
5367
  subject: parsed.data,
5361
5368
  watchId: metadata.event.subscriptionId
@@ -9608,26 +9615,11 @@ function createUpdatePlanTool() {
9608
9615
  });
9609
9616
  }
9610
9617
  //#endregion
9611
- //#region src/chat/agent-invocations/errors.ts
9612
- /** Named child cannot accept another invocation until its active work ends. */
9613
- var AgentInvocationBusyError = class extends Error {
9614
- constructor(name) {
9615
- super(`Named agent "${name}" already has active work`);
9616
- this.name = "AgentInvocationBusyError";
9617
- }
9618
- };
9619
- /** Parent already has the maximum number of non-terminal child invocations. */
9620
- var AgentInvocationLimitError = class extends Error {
9621
- constructor(limit) {
9622
- super(`Parent already has ${limit} active child agent invocations (limit ${limit})`);
9623
- this.name = "AgentInvocationLimitError";
9624
- }
9625
- };
9626
- //#endregion
9627
9618
  //#region src/chat/agent-invocations/types.ts
9628
9619
  const exactStringSchema = z.string().min(1).refine((value) => value === value.trim());
9629
9620
  const agentNameSchema = exactStringSchema.max(64);
9630
9621
  const agentInvocationMailboxStatusSchema = z.enum(AGENT_INVOCATION_MAILBOX_STATUSES);
9622
+ const agentInvocationParentMailboxStatusSchema = z.enum(AGENT_INVOCATION_PARENT_MAILBOX_STATUSES);
9631
9623
  const agentBindingSchema = z.object({
9632
9624
  childConversationId: exactStringSchema,
9633
9625
  name: agentNameSchema,
@@ -9644,24 +9636,32 @@ const agentInvocationBaseSchema = z.object({
9644
9636
  invocationId: exactStringSchema,
9645
9637
  mailboxStatus: agentInvocationMailboxStatusSchema,
9646
9638
  parentConversationId: exactStringSchema,
9639
+ parentToolCallId: exactStringSchema.optional(),
9647
9640
  reasoningLevel: z.enum(TURN_REASONING_LEVELS).optional(),
9648
9641
  source: sourceSchema,
9642
+ title: exactStringSchema.optional(),
9649
9643
  updatedAtMs: z.number().finite()
9650
9644
  }).strict();
9651
9645
  const agentInvocationSchema = z.discriminatedUnion("status", [
9652
9646
  agentInvocationBaseSchema.extend({ status: z.enum([
9653
9647
  "pending",
9654
9648
  "running",
9655
- "awaiting_resume"
9649
+ "paused"
9656
9650
  ]) }),
9657
9651
  agentInvocationBaseSchema.extend({
9652
+ parentMailboxStatus: agentInvocationParentMailboxStatusSchema,
9658
9653
  result: z.string(),
9659
9654
  status: z.literal("completed"),
9660
9655
  terminalAtMs: z.number().finite()
9661
9656
  }),
9662
9657
  agentInvocationBaseSchema.extend({
9663
9658
  errorMessage: z.string(),
9664
- status: z.enum(["blocked", "failed"]),
9659
+ parentMailboxStatus: agentInvocationParentMailboxStatusSchema,
9660
+ status: z.enum([
9661
+ "blocked",
9662
+ "failed",
9663
+ "stopped"
9664
+ ]),
9665
9665
  terminalAtMs: z.number().finite()
9666
9666
  })
9667
9667
  ]);
@@ -9673,15 +9673,458 @@ const createAgentInvocationSchema = z.object({
9673
9673
  idempotencyKey: exactStringSchema,
9674
9674
  input: exactStringSchema,
9675
9675
  parentConversationId: exactStringSchema,
9676
- reasoningLevel: z.enum(TURN_REASONING_LEVELS).optional()
9676
+ parentToolCallId: exactStringSchema.optional(),
9677
+ reasoningLevel: z.enum(TURN_REASONING_LEVELS).optional(),
9678
+ title: exactStringSchema.optional()
9677
9679
  }).strict();
9678
9680
  //#endregion
9681
+ //#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 {
9684
+ constructor(name) {
9685
+ super(`Named agent "${name}" already has active work`);
9686
+ this.name = "AgentInvocationBusyError";
9687
+ }
9688
+ };
9689
+ /** Parent already has the maximum number of non-terminal child invocations. */
9690
+ var AgentInvocationLimitError = class extends Error {
9691
+ constructor(limit) {
9692
+ super(`Parent already has ${limit} active child agent invocations (limit ${limit})`);
9693
+ this.name = "AgentInvocationLimitError";
9694
+ }
9695
+ };
9696
+ //#endregion
9697
+ //#region src/chat/agent-invocations/store.ts
9698
+ const CREATE_LOCK_PREFIX = "junior:agent_invocation:create";
9699
+ function createLockName(parentConversationId) {
9700
+ return `${CREATE_LOCK_PREFIX}:${parentConversationId}`;
9701
+ }
9702
+ const TERMINAL_AGENT_INVOCATION_STATUSES = [
9703
+ "blocked",
9704
+ "completed",
9705
+ "failed",
9706
+ "stopped"
9707
+ ];
9708
+ const NON_TERMINAL_AGENT_INVOCATION_STATUSES = [
9709
+ "pending",
9710
+ "running",
9711
+ "paused"
9712
+ ];
9713
+ function stableId(prefix, ...parts) {
9714
+ return `${prefix}:${createHash("sha256").update(parts.join("\0")).digest("hex").slice(0, 32)}`;
9715
+ }
9716
+ /** Return the stable identity for one retry-safe delegated task. */
9717
+ function getAgentInvocationId(parentConversationId, idempotencyKey) {
9718
+ return stableId("agent-invocation", parentConversationId, idempotencyKey);
9719
+ }
9720
+ /** Return the stable child identity for one invocation without a named binding. */
9721
+ function getUnnamedAgentConversationId(invocationId) {
9722
+ return stableId("agent", invocationId);
9723
+ }
9724
+ /** Return the stable child identity for one named binding. */
9725
+ function getNamedAgentConversationId(parentConversationId, name) {
9726
+ return stableId("agent", parentConversationId, name);
9727
+ }
9728
+ /**
9729
+ * Return the stable identity of the agent invocation that gives one child
9730
+ * agent result to the child Conversation that started that child agent.
9731
+ */
9732
+ function getAgentInvocationIdForResult(resultInvocationId) {
9733
+ return stableId("agent-invocation", "result", resultInvocationId);
9734
+ }
9735
+ /** Return the stable turn identity advanced by one agent invocation. */
9736
+ function getAgentInvocationTurnId(invocationId) {
9737
+ return `agent-invocation:${invocationId}`;
9738
+ }
9739
+ /** Return the stable mailbox identity for one agent invocation. */
9740
+ function getAgentInvocationMessageId(invocationId) {
9741
+ return `agent-invocation:${invocationId}:input`;
9742
+ }
9743
+ /** Return the stable parent mailbox identity for one terminal result. */
9744
+ function getAgentInvocationResultMessageId(invocationId) {
9745
+ return `agent-invocation:${invocationId}:result`;
9746
+ }
9747
+ function bindingFromRow(row) {
9748
+ return agentBindingSchema.parse({
9749
+ childConversationId: row.childConversationId,
9750
+ name: row.name,
9751
+ parentConversationId: row.parentConversationId
9752
+ });
9753
+ }
9754
+ function invocationFromRow(row) {
9755
+ return agentInvocationSchema.parse({
9756
+ actor: row.actor,
9757
+ ...row.agentName ? { agentName: row.agentName } : void 0,
9758
+ childConversationId: row.childConversationId,
9759
+ createdAtMs: row.createdAt.getTime(),
9760
+ ...row.credentialContext ? { credentialContext: row.credentialContext } : void 0,
9761
+ destination: row.destination,
9762
+ ...row.errorMessage !== null ? { errorMessage: row.errorMessage } : void 0,
9763
+ input: row.input,
9764
+ invocationId: row.invocationId,
9765
+ mailboxStatus: row.mailboxStatus,
9766
+ parentConversationId: row.parentConversationId,
9767
+ ...row.parentMailboxStatus ? { parentMailboxStatus: row.parentMailboxStatus } : void 0,
9768
+ ...row.parentToolCallId ? { parentToolCallId: row.parentToolCallId } : void 0,
9769
+ ...row.reasoningLevel ? { reasoningLevel: row.reasoningLevel } : void 0,
9770
+ ...row.result !== null ? { result: row.result } : void 0,
9771
+ source: row.source,
9772
+ status: row.status,
9773
+ ...row.terminalAt ? { terminalAtMs: row.terminalAt.getTime() } : void 0,
9774
+ ...row.title ? { title: row.title } : void 0,
9775
+ updatedAtMs: row.updatedAt.getTime()
9776
+ });
9777
+ }
9778
+ /**
9779
+ * Require every durable creation input to match before replaying one key.
9780
+ * The title is not compared: a retry can generate a different one, and the
9781
+ * stored title wins.
9782
+ */
9783
+ function sameCreateInput(invocation, input) {
9784
+ return invocation.parentConversationId === input.parentConversationId && invocation.agentName === input.agentName && invocation.input === input.input && invocation.reasoningLevel === input.reasoningLevel && JSON.stringify(invocation.actor) === JSON.stringify(input.actor) && JSON.stringify(invocation.credentialContext) === JSON.stringify(input.credentialContext) && JSON.stringify(invocation.destination) === JSON.stringify(input.destination);
9785
+ }
9786
+ /** Read one named child binding in its parent-agent scope. */
9787
+ async function getAgentBinding(args) {
9788
+ const rows = await getSqlExecutor().db().select().from(juniorAgentBindings).where(and(eq(juniorAgentBindings.parentConversationId, args.parentConversationId), eq(juniorAgentBindings.name, args.name)));
9789
+ return rows[0] ? bindingFromRow(rows[0]) : void 0;
9790
+ }
9791
+ /** Read one durable agent invocation. */
9792
+ async function getAgentInvocation(invocationId) {
9793
+ const rows = await getSqlExecutor().db().select().from(juniorAgentInvocations).where(eq(juniorAgentInvocations.invocationId, invocationId));
9794
+ return rows[0] ? invocationFromRow(rows[0]) : void 0;
9795
+ }
9796
+ /** Resolve the single invocation that may resume for one child conversation. */
9797
+ async function getActiveAgentInvocationForConversation(childConversationId) {
9798
+ const rows = await getSqlExecutor().db().select().from(juniorAgentInvocations).where(and(eq(juniorAgentInvocations.childConversationId, childConversationId), inArray(juniorAgentInvocations.status, ["running", "paused"]))).orderBy(asc(juniorAgentInvocations.createdAt)).limit(2);
9799
+ if (rows.length > 1) throw new Error(`Child conversation ${childConversationId} has multiple active agent invocations`);
9800
+ return rows[0] ? invocationFromRow(rows[0]) : void 0;
9801
+ }
9802
+ /** Read the newest agent invocation of one child Conversation. */
9803
+ async function getLatestAgentInvocationForConversation(childConversationId) {
9804
+ const rows = await getSqlExecutor().db().select().from(juniorAgentInvocations).where(eq(juniorAgentInvocations.childConversationId, childConversationId)).orderBy(desc(juniorAgentInvocations.createdAt)).limit(1);
9805
+ return rows[0] ? invocationFromRow(rows[0]) : void 0;
9806
+ }
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
+ /**
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.
9830
+ */
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);
9844
+ }
9845
+ async function getNonTerminalAgentInvocationForConversation(childConversationId) {
9846
+ 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
+ return rows[0] ? invocationFromRow(rows[0]) : void 0;
9848
+ }
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;
9851
+ }
9852
+ /**
9853
+ * Create or replay one invocation, reusing named child conversations and
9854
+ * keeping unnamed child identities scoped to the invocation.
9855
+ */
9856
+ async function createAgentInvocation(rawInput, nowMs = Date.now()) {
9857
+ const input = createAgentInvocationSchema.parse(rawInput);
9858
+ const invocationId = getAgentInvocationId(input.parentConversationId, input.idempotencyKey);
9859
+ const childConversationId = input.agentName ? getNamedAgentConversationId(input.parentConversationId, input.agentName) : getUnnamedAgentConversationId(invocationId);
9860
+ const lockName = createLockName(input.parentConversationId);
9861
+ return await getSqlExecutor().withLock(lockName, async () => {
9862
+ const existing = await getAgentInvocation(invocationId);
9863
+ if (existing) {
9864
+ if (!sameCreateInput(existing, input)) throw new Error(`Agent invocation idempotency key was reused with different input for ${invocationId}`);
9865
+ return existing;
9866
+ }
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);
9869
+ await getConversationStore().createChild({
9870
+ childConversationId,
9871
+ parentConversationId: input.parentConversationId,
9872
+ nowMs,
9873
+ source: "internal"
9874
+ });
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
+ }
9888
+ await getSqlExecutor().db().insert(juniorAgentInvocations).values({
9889
+ invocationId,
9890
+ parentConversationId: input.parentConversationId,
9891
+ childConversationId,
9892
+ agentName: input.agentName ?? null,
9893
+ input: input.input,
9894
+ title: input.title ?? null,
9895
+ actor: input.actor,
9896
+ credentialContext: input.credentialContext ?? null,
9897
+ source: { kind: "agent_invocation" },
9898
+ destination: input.destination,
9899
+ reasoningLevel: input.reasoningLevel ?? null,
9900
+ parentToolCallId: input.parentToolCallId ?? null,
9901
+ status: "pending",
9902
+ mailboxStatus: "pending",
9903
+ createdAt: new Date(nowMs),
9904
+ updatedAt: new Date(nowMs),
9905
+ terminalAt: null
9906
+ }).onConflictDoNothing();
9907
+ const invocation = await getAgentInvocation(invocationId);
9908
+ if (!invocation || !sameCreateInput(invocation, input)) throw new Error(`Agent invocation creation raced for ${invocationId}`);
9909
+ return invocation;
9910
+ });
9911
+ }
9912
+ /**
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.
9917
+ */
9918
+ async function createAgentInvocationForResult(args, nowMs = Date.now()) {
9919
+ const invocationId = getAgentInvocationIdForResult(args.resultInvocationId);
9920
+ await getSqlExecutor().db().insert(juniorAgentInvocations).values({
9921
+ invocationId,
9922
+ parentConversationId: args.latest.parentConversationId,
9923
+ childConversationId: args.latest.childConversationId,
9924
+ agentName: args.latest.agentName ?? null,
9925
+ input: args.input,
9926
+ actor: args.latest.actor,
9927
+ credentialContext: args.latest.credentialContext ?? null,
9928
+ source: { kind: "agent_invocation" },
9929
+ destination: args.latest.destination,
9930
+ reasoningLevel: args.latest.reasoningLevel ?? null,
9931
+ status: "pending",
9932
+ mailboxStatus: "pending",
9933
+ createdAt: new Date(nowMs),
9934
+ updatedAt: new Date(nowMs),
9935
+ terminalAt: null
9936
+ }).onConflictDoNothing();
9937
+ const invocation = await getAgentInvocation(invocationId);
9938
+ if (!invocation || invocation.childConversationId !== args.latest.childConversationId) throw new Error(`Agent invocation creation raced for ${invocationId}`);
9939
+ return invocation;
9940
+ }
9941
+ /**
9942
+ * Return whether a child Conversation has work that will start another agent
9943
+ * invocation on it: another open agent invocation, an open child agent, or a
9944
+ * sent child agent result that no agent invocation has received yet.
9945
+ */
9946
+ async function hasOpenChildWork(invocation) {
9947
+ const db = getSqlExecutor().db();
9948
+ const conversationId = invocation.childConversationId;
9949
+ if ((await db.select({ invocationId: juniorAgentInvocations.invocationId }).from(juniorAgentInvocations).where(and(or(eq(juniorAgentInvocations.childConversationId, conversationId), eq(juniorAgentInvocations.parentConversationId, conversationId)), ne(juniorAgentInvocations.invocationId, invocation.invocationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES))).limit(1)).length > 0) return true;
9950
+ const sent = await db.select({ invocationId: juniorAgentInvocations.invocationId }).from(juniorAgentInvocations).where(and(eq(juniorAgentInvocations.parentConversationId, conversationId), inArray(juniorAgentInvocations.parentMailboxStatus, ["pending", "appended"])));
9951
+ if (sent.length === 0) return false;
9952
+ return (await db.select({ invocationId: juniorAgentInvocations.invocationId }).from(juniorAgentInvocations).where(inArray(juniorAgentInvocations.invocationId, sent.map((row) => getAgentInvocationIdForResult(row.invocationId))))).length < sent.length;
9953
+ }
9954
+ /** List bounded invocation records whose mailbox append still needs repair. */
9955
+ async function listPendingAgentInvocationMailboxAppends(limit = 100) {
9956
+ return (await getSqlExecutor().db().select().from(juniorAgentInvocations).where(and(eq(juniorAgentInvocations.mailboxStatus, "pending"), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES))).orderBy(asc(juniorAgentInvocations.createdAt)).limit(limit)).map(invocationFromRow);
9957
+ }
9958
+ /** List terminal agent invocation results that still need a parent mailbox append. */
9959
+ async function listPendingAgentInvocationResults(limit = 100) {
9960
+ return (await getSqlExecutor().db().select().from(juniorAgentInvocations).where(eq(juniorAgentInvocations.parentMailboxStatus, "pending")).orderBy(asc(juniorAgentInvocations.terminalAt)).limit(limit)).map(invocationFromRow).filter(isTerminalAgentInvocation);
9961
+ }
9962
+ /**
9963
+ * Send a `replaced` result to the parent now. A stop ended the open work of
9964
+ * the child, so no later agent invocation sends the report.
9965
+ */
9966
+ async function reportReplacedAgentInvocation(invocationId, nowMs = Date.now()) {
9967
+ await getSqlExecutor().db().update(juniorAgentInvocations).set({
9968
+ parentMailboxStatus: "pending",
9969
+ updatedAt: new Date(nowMs)
9970
+ }).where(and(eq(juniorAgentInvocations.invocationId, invocationId), eq(juniorAgentInvocations.parentMailboxStatus, "replaced")));
9971
+ return await getAgentInvocation(invocationId);
9972
+ }
9973
+ /** Record that one terminal result is in its parent mailbox. */
9974
+ async function markAgentInvocationParentMailboxAppended(invocationId, nowMs = Date.now()) {
9975
+ await getSqlExecutor().db().update(juniorAgentInvocations).set({
9976
+ parentMailboxStatus: "appended",
9977
+ updatedAt: new Date(nowMs)
9978
+ }).where(and(eq(juniorAgentInvocations.invocationId, invocationId), eq(juniorAgentInvocations.parentMailboxStatus, "pending")));
9979
+ }
9980
+ /** Record that the invocation's idempotent mailbox append completed. */
9981
+ async function markAgentInvocationMailboxAppended(invocationId, nowMs = Date.now()) {
9982
+ await getSqlExecutor().db().update(juniorAgentInvocations).set({
9983
+ mailboxStatus: "appended",
9984
+ updatedAt: new Date(nowMs)
9985
+ }).where(eq(juniorAgentInvocations.invocationId, invocationId));
9986
+ }
9987
+ /** Mark one non-terminal invocation as actively executing. */
9988
+ async function markAgentInvocationRunning(invocationId, nowMs = Date.now()) {
9989
+ await getSqlExecutor().db().update(juniorAgentInvocations).set({
9990
+ status: "running",
9991
+ updatedAt: new Date(nowMs)
9992
+ }).where(and(eq(juniorAgentInvocations.invocationId, invocationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES)));
9993
+ return await getAgentInvocation(invocationId);
9994
+ }
9995
+ /** Mark one invocation as waiting for another shared execution slice. */
9996
+ async function markAgentInvocationPaused(invocationId, nowMs = Date.now()) {
9997
+ await getSqlExecutor().db().update(juniorAgentInvocations).set({
9998
+ status: "paused",
9999
+ updatedAt: new Date(nowMs)
10000
+ }).where(and(eq(juniorAgentInvocations.invocationId, invocationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES)));
10001
+ }
10002
+ /**
10003
+ * Persist one terminal invocation result without overwriting an earlier
10004
+ * result. The same write marks the result for the parent mailbox. If the
10005
+ * child still has open work, the result is `replaced`: the parent gets one
10006
+ * report, from the agent invocation that finishes the child's work.
10007
+ */
10008
+ async function completeAgentInvocation(args) {
10009
+ const nowMs = args.nowMs ?? Date.now();
10010
+ const current = await getAgentInvocation(args.invocationId);
10011
+ if (!current || isTerminalAgentInvocation(current)) return current;
10012
+ const parentMailboxStatus = args.status !== "stopped" && await hasOpenChildWork(current) ? "replaced" : "pending";
10013
+ await getSqlExecutor().db().update(juniorAgentInvocations).set({
10014
+ status: args.status,
10015
+ result: args.status === "completed" ? args.result : null,
10016
+ errorMessage: args.status === "completed" ? null : args.errorMessage,
10017
+ parentMailboxStatus,
10018
+ terminalAt: new Date(nowMs),
10019
+ updatedAt: new Date(nowMs)
10020
+ }).where(and(eq(juniorAgentInvocations.invocationId, args.invocationId), inArray(juniorAgentInvocations.status, NON_TERMINAL_AGENT_INVOCATION_STATUSES)));
10021
+ return await getAgentInvocation(args.invocationId);
10022
+ }
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);
10048
+ }
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));
10056
+ }
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"))));
10073
+ return {
10074
+ conversationIds,
10075
+ stopped: stopped.map(invocationFromRow).filter(isTerminalAgentInvocation)
10076
+ };
10077
+ }
10078
+ /** Return whether an agent invocation has finished. */
10079
+ function isTerminalAgentInvocation(invocation) {
10080
+ return TERMINAL_AGENT_INVOCATION_STATUSES.includes(invocation.status);
10081
+ }
10082
+ //#endregion
10083
+ //#region src/chat/tools/runtime/list-agents.ts
10084
+ const TASK_MAX_LENGTH = 1e3;
10085
+ const ERROR_MAX_LENGTH = 500;
10086
+ function truncate(text, maxLength) {
10087
+ return text.length > maxLength ? `${text.slice(0, maxLength)}…` : text;
10088
+ }
10089
+ const listAgentsOutputSchema = juniorToolOutputSchema.extend({ agents: z.array(z.object({
10090
+ 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."),
10093
+ task: z.string().describe("Latest task given to the child agent"),
10094
+ title: z.string().optional().describe("Short display title of the latest task"),
10095
+ error: z.string().optional().describe("Why the child agent is blocked, failed, or stopped"),
10096
+ updated_at: z.string().describe("Time of the latest change")
10097
+ }).strict()).describe("Child agents of this conversation, newest activity first") });
10098
+ /** Create the read-only tool that lists the child agents of one conversation. */
10099
+ function createListAgentsTool(conversationId) {
10100
+ 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.",
10102
+ annotations: {
10103
+ destructiveHint: false,
10104
+ idempotentHint: true,
10105
+ openWorldHint: false,
10106
+ readOnlyHint: true
10107
+ },
10108
+ inputSchema: z.object({}).strict(),
10109
+ outputSchema: listAgentsOutputSchema,
10110
+ execute: async () => ({ agents: (await listChildAgents(conversationId)).map((agent) => ({
10111
+ conversation_id: agent.childConversationId,
10112
+ ...agent.agentName ? { name: agent.agentName } : void 0,
10113
+ status: agent.status,
10114
+ task: truncate(agent.task, TASK_MAX_LENGTH),
10115
+ ...agent.title ? { title: agent.title } : void 0,
10116
+ ...agent.errorMessage ? { error: truncate(agent.errorMessage, ERROR_MAX_LENGTH) } : void 0,
10117
+ updated_at: new Date(agent.updatedAtMs).toISOString()
10118
+ })) })
10119
+ });
10120
+ }
10121
+ //#endregion
9679
10122
  //#region src/chat/tools/runtime/spawn-agent.ts
9680
- const spawnAgentOutputSchema = juniorToolOutputSchema.extend({ invocation_id: z.string().min(1) });
10123
+ const spawnAgentOutputSchema = juniorToolOutputSchema.extend({ conversation_id: z.string().min(1).describe("Child conversation ID. Pass it to searchConversationEvents to read the child's history.") });
9681
10124
  /** Create the asynchronous child-agent tool for one parent run. */
9682
10125
  function createSpawnAgentTool(spawnAgent) {
9683
10126
  return zodTool({
9684
- 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.",
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.",
9685
10128
  annotations: {
9686
10129
  destructiveHint: false,
9687
10130
  idempotentHint: true,
@@ -9690,16 +10133,24 @@ function createSpawnAgentTool(spawnAgent) {
9690
10133
  },
9691
10134
  inputSchema: z.object({
9692
10135
  task: z.string().trim().min(1).describe("Task for the child agent"),
10136
+ 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."),
9693
10137
  name: agentNameSchema.nullable().optional().describe("Stable child-agent name, or omit for a one-off agent"),
9694
10138
  reasoning_level: z.enum(TURN_REASONING_LEVELS).nullable().optional().describe("Optional reasoning level for this delegated task only")
9695
10139
  }).strict(),
9696
10140
  outputSchema: spawnAgentOutputSchema,
9697
10141
  execute: async (input, options) => {
9698
10142
  if (!options.toolCallId) throw new ToolInputError("spawnAgent requires an active tool call ID");
10143
+ const title = await resolveTaskTitle({
10144
+ completeText,
10145
+ instruction: input.task,
10146
+ signal: options.signal,
10147
+ title: input.title
10148
+ });
9699
10149
  let result;
9700
10150
  try {
9701
10151
  result = await spawnAgent({
9702
10152
  task: input.task,
10153
+ ...title ? { title } : void 0,
9703
10154
  ...input.name ? { name: input.name } : void 0,
9704
10155
  ...input.reasoning_level ? { reasoningLevel: input.reasoning_level } : void 0
9705
10156
  }, {
@@ -9711,7 +10162,123 @@ function createSpawnAgentTool(spawnAgent) {
9711
10162
  if (error instanceof AgentInvocationLimitError) throw new ToolInputError(`${error.message}. Wait for active children to finish before spawning more.`, { cause: error });
9712
10163
  throw error;
9713
10164
  }
9714
- return { invocation_id: result.invocationId };
10165
+ return { conversation_id: result.childConversationId };
10166
+ }
10167
+ });
10168
+ }
10169
+ //#endregion
10170
+ //#region src/chat/agent-invocations/markers.ts
10171
+ /**
10172
+ * Dashboard markers for agent invocations. The parent Conversation gets one
10173
+ * `subagent_started` and one `subagent_ended` event for each agent invocation.
10174
+ */
10175
+ const SUBAGENT_KIND = "agent";
10176
+ /**
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
10179
+ * status. It must be written before the child mailbox append, so the child
10180
+ * cannot end first.
10181
+ */
10182
+ async function recordSubagentStarted(invocation) {
10183
+ await getConversationEventStore().append(invocation.parentConversationId, [{
10184
+ idempotencyKey: `subagent_started:${invocation.invocationId}`,
10185
+ data: {
10186
+ type: "subagent_started",
10187
+ subagentInvocationId: invocation.invocationId,
10188
+ subagentKind: SUBAGENT_KIND,
10189
+ childConversationId: invocation.childConversationId,
10190
+ ...invocation.agentName ? { agentName: invocation.agentName } : void 0,
10191
+ ...invocation.parentToolCallId ? {
10192
+ parentToolCallId: invocation.parentToolCallId,
10193
+ task: invocation.input
10194
+ } : void 0,
10195
+ ...invocation.title ? { title: invocation.title } : void 0,
10196
+ ...invocation.reasoningLevel ? { reasoningLevel: invocation.reasoningLevel } : void 0
10197
+ },
10198
+ createdAtMs: invocation.createdAtMs
10199
+ }]);
10200
+ }
10201
+ const SUBAGENT_OUTCOMES = {
10202
+ blocked: "blocked",
10203
+ completed: "success",
10204
+ failed: "error",
10205
+ stopped: "aborted"
10206
+ };
10207
+ /**
10208
+ * Record in the parent Conversation how one agent invocation ended. The
10209
+ * heartbeat result repair also calls this, because the child work can be
10210
+ * acknowledged before the worker reports the result.
10211
+ */
10212
+ async function recordSubagentEnded(invocation) {
10213
+ await getConversationEventStore().append(invocation.parentConversationId, [{
10214
+ idempotencyKey: `subagent_ended:${invocation.invocationId}`,
10215
+ data: {
10216
+ type: "subagent_ended",
10217
+ subagentInvocationId: invocation.invocationId,
10218
+ outcome: SUBAGENT_OUTCOMES[invocation.status]
10219
+ },
10220
+ createdAtMs: invocation.terminalAtMs
10221
+ }]);
10222
+ }
10223
+ //#endregion
10224
+ //#region src/chat/agent-invocations/stop.ts
10225
+ /**
10226
+ * Stop one child agent of a parent Conversation and every child agent below
10227
+ * it. The parent gets no result. SQL stops the work first. Then a
10228
+ * Conversation stop request aborts each run that is still working.
10229
+ *
10230
+ * Returns undefined when the child does not belong to the parent.
10231
+ */
10232
+ async function stopChildAgent(args) {
10233
+ const tree = await stopChildAgentInvocations(args);
10234
+ if (!tree) return;
10235
+ await finishStoppedTree(tree, tree.conversationIds, args.state);
10236
+ return { stoppedCount: tree.stopped.length };
10237
+ }
10238
+ /**
10239
+ * Stop the child agents of an agent Conversation that a stop interrupted.
10240
+ * The worker of that Conversation calls this after it records its own stop,
10241
+ * so this sends no stop request to that Conversation.
10242
+ */
10243
+ async function stopChildAgentsOf(conversationId, state) {
10244
+ const tree = await stopAgentInvocationTree(conversationId);
10245
+ await finishStoppedTree(tree, tree.conversationIds.filter((id) => id !== conversationId), state);
10246
+ }
10247
+ async function finishStoppedTree(tree, conversationIdsToStop, state) {
10248
+ for (const invocation of tree.stopped) await recordSubagentEnded(invocation);
10249
+ for (const conversationId of conversationIdsToStop) await stopConversationWork({
10250
+ conversationId,
10251
+ state
10252
+ });
10253
+ }
10254
+ //#endregion
10255
+ //#region src/chat/tools/runtime/stop-agent.ts
10256
+ const stopAgentOutputSchema = juniorToolOutputSchema.extend({
10257
+ conversation_id: z.string().min(1).describe("Stopped child conversation ID"),
10258
+ stopped: z.boolean().describe("True when work was stopped. False when the child agent had already stopped.")
10259
+ });
10260
+ /** Create the tool that stops one child agent of one conversation. */
10261
+ function createStopAgentTool(conversationId) {
10262
+ 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.",
10264
+ annotations: {
10265
+ destructiveHint: true,
10266
+ idempotentHint: true,
10267
+ openWorldHint: false,
10268
+ readOnlyHint: false
10269
+ },
10270
+ inputSchema: z.object({ conversation_id: z.string().trim().min(1).describe("Child conversation ID from spawnAgent or listAgents to stop") }).strict(),
10271
+ outputSchema: stopAgentOutputSchema,
10272
+ execute: async (input) => {
10273
+ const stopped = await stopChildAgent({
10274
+ childConversationId: input.conversation_id,
10275
+ parentConversationId: conversationId
10276
+ });
10277
+ if (!stopped) throw new ToolInputError(`${input.conversation_id} is not a child agent of this conversation. Use listAgents to find its conversation_id.`);
10278
+ return {
10279
+ conversation_id: input.conversation_id,
10280
+ stopped: stopped.stoppedCount > 0
10281
+ };
9715
10282
  }
9716
10283
  });
9717
10284
  }
@@ -16128,7 +16695,11 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
16128
16695
  });
16129
16696
  }
16130
16697
  if (context.handoff) tools.handoff = createHandoffTool(context.handoff);
16131
- if (context.spawnAgent) tools.spawnAgent = createSpawnAgentTool(context.spawnAgent);
16698
+ if (context.spawnAgent) {
16699
+ tools.spawnAgent = createSpawnAgentTool(context.spawnAgent);
16700
+ tools.listAgents = createListAgentsTool(context.conversationId);
16701
+ tools.stopAgent = createStopAgentTool(context.conversationId);
16702
+ }
16132
16703
  if (context.automation) tools[FINISH_AUTOMATION_RUN_TOOL_NAME] = createFinishAutomationRunTool(context.automation);
16133
16704
  if (context.mcpToolManager) {
16134
16705
  tools.searchMcpTools = createSearchMcpToolsTool(context.mcpToolManager);
@@ -20049,11 +20620,12 @@ function applyHandoff(args) {
20049
20620
  * artifacts, and transcript state for destination-owned completion.
20050
20621
  */
20051
20622
  const AGENT_ABORT_SETTLE_GRACE_MS = 5e3;
20623
+ /** Read visibility from the Conversation, or from its nearest parent with one. */
20052
20624
  async function readRunConversationVisibility(conversationId) {
20053
20625
  const conversationStore = getConversationStore();
20054
- const conversation = await conversationStore.get({ conversationId });
20055
- if (conversation?.visibility || !conversation?.parentConversationId) return conversation?.visibility;
20056
- return (await conversationStore.get({ conversationId: conversation.parentConversationId }))?.visibility;
20626
+ let conversation = await conversationStore.get({ conversationId });
20627
+ while (!conversation?.visibility && conversation?.parentConversationId) conversation = await conversationStore.get({ conversationId: conversation.parentConversationId });
20628
+ return conversation?.visibility;
20057
20629
  }
20058
20630
  /** Preserve delivery-error ownership across the agent generation boundary. */
20059
20631
  var AssistantMessageDeliveryError = class extends Error {
@@ -20326,6 +20898,7 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
20326
20898
  });
20327
20899
  const preAgentPromptMessages = () => existingSessionRecord?.piMessages ?? [...input.piMessages ?? []];
20328
20900
  const handoffEnabled = !isAgentRunFeatureDisabled(policy.disabledFeatures, "handoff");
20901
+ const routeIsFixed = Object.keys(botConfig.profiles).length === 1 && (profileConfig(botConfig, activeModelProfile).reasoningLevel ?? configuredReasoningLevel) !== void 0;
20329
20902
  const storedTurnRoute = await loadTurnRoute({
20330
20903
  conversationId,
20331
20904
  turnId
@@ -20345,7 +20918,8 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
20345
20918
  reason: `persisted:${storedTurnRoute.source}`,
20346
20919
  source: storedTurnRoute.source
20347
20920
  };
20348
- } else if (handoffEnabled) {
20921
+ } else if (handoffEnabled && !routeIsFixed) {
20922
+ const lastProfile = await loadLastModelProfile({ conversationId });
20349
20923
  turnRoute = await selectTurnRoute({
20350
20924
  completeObject,
20351
20925
  conversationContext: input.conversationContext,
@@ -20355,8 +20929,8 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
20355
20929
  actorId: slackActor?.userId,
20356
20930
  runId
20357
20931
  },
20932
+ currentProfile: lastProfile && Object.hasOwn(botConfig.profiles, lastProfile) ? lastProfile : botConfig.defaultProfile,
20358
20933
  currentTurnBlocks: routerBlocks,
20359
- defaultProfile: botConfig.defaultProfile,
20360
20934
  fastModelId: botConfig.fastModelId,
20361
20935
  messageText: userInput,
20362
20936
  profiles: botConfig.profiles
@@ -20538,6 +21112,9 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
20538
21112
  baseInstructions = assembledBaseInstructions;
20539
21113
  const repositoryInstructionsContext = createRepositoryInstructionsContext({
20540
21114
  capture: wiring.captureRepositoryInstructions,
21115
+ async commit(messages) {
21116
+ await runResume.persistSafeBoundary(messages, [contextProvenance]);
21117
+ },
20541
21118
  hasSandbox: () => Boolean(wiring.getSandboxRef()),
20542
21119
  initialInstructions: initialRepositoryInstructions,
20543
21120
  onTransition: recordAgentsTransition,
@@ -20658,16 +21235,15 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
20658
21235
  prepareNextTurnWithContext: async (nextTurn, hookSignal) => {
20659
21236
  try {
20660
21237
  const handoffUpdate = applyPendingHandoff();
21238
+ const contextUpdate = await repositoryInstructionsContext.applyUpdate(handoffUpdate, nextTurn.context);
20661
21239
  const pendingMessages = await drainSteeringMessages();
20662
- const capacityUpdate = await applyActiveContextCompaction(handoffUpdate ? currentAgentMessages() : nextTurn.context.messages, hookSignal, pendingMessages, true);
20663
- const combinedUpdate = capacityUpdate ? {
20664
- ...handoffUpdate,
20665
- ...capacityUpdate
20666
- } : handoffUpdate;
20667
- const repositoryInstructionsUpdate = await repositoryInstructionsContext.applyUpdate(combinedUpdate, nextTurn.context);
21240
+ const capacityUpdate = await applyActiveContextCompaction(contextUpdate ? currentAgentMessages() : nextTurn.context.messages, hookSignal, pendingMessages, true);
20668
21241
  const yieldError = runResume.prepareYieldIfDue(currentAgentMessages());
20669
21242
  if (yieldError) throw yieldError;
20670
- return repositoryInstructionsUpdate;
21243
+ return capacityUpdate ? {
21244
+ ...contextUpdate,
21245
+ ...capacityUpdate
21246
+ } : contextUpdate;
20671
21247
  } catch (error) {
20672
21248
  pendingPiHookError ??= error instanceof Error ? error : new Error(String(error));
20673
21249
  throw error;
@@ -21029,7 +21605,7 @@ function createAgentRunner(execute, options) {
21029
21605
  } } : void 0
21030
21606
  };
21031
21607
  if (run.instruction.storedAttachments?.length) {
21032
- const { loadInputImages } = await import("./images-DSxZt5vk.js").then((n) => n.n);
21608
+ const { loadInputImages } = await import("./images-CvMreau5.js").then((n) => n.n);
21033
21609
  const storage = nextRun.environment?.attachmentStorage;
21034
21610
  if (!storage) throw new Error("Attachment storage is unavailable.");
21035
21611
  nextRun.instruction = {
@@ -21045,4 +21621,4 @@ function createAgentRunner(execute, options) {
21045
21621
  } };
21046
21622
  }
21047
21623
  //#endregion
21048
- export { getEventCatalog as $, actorFromRun as $t, resolveSlackChannelIdFromMessage as A, hasConversationStop as At, effectiveTaskOutcomes as B, SANDBOX_EGRESS_PROXY_PATH as Bt, renderBlockText as C, removeActiveConversation as Ct, storeAttachment as D, stopConversationWork as Dt, requestAttachmentDeletion as E, startConversationWork as Et, logScheduledAutomationRunSkipped as F, maybePostAutomatedTurnLimitNotice as Ft, AgentInvocationBusyError as G, sandboxEgressSignalsResponseSchema as Gt, agentBindingSchema as H, consumeSandboxEgressAuthRequiredSignal as Ht, scheduledAutomationRunAttributes as I, recordFinishedTurnForAutomatedLimit as It, processWorkspaceSnapshotJob as J, resolveBaseUrl as Jt, AgentInvocationLimitError as K, formatProviderLabel as Kt, editEventAutomation as L, resolveSandboxEgressProviderForHost as Lt, getConversationInfo as M, isFinalAttempt as Mt, listThreadReplies as N, isInvalidConversationRecordError as Nt, parseSlackThreadId as O, CONVERSATION_WORK_CHECK_IN_INTERVAL_MS as Ot, editScheduledAutomation as P, renderTaskInput as Pt, timerWatchCredentialSubject as Q, RetryableDeliveryError as Qt, AutomationEditError as R, normalizeSandboxEgressTracePropagationDomains as Rt, renderAttachmentText as S, releaseConversationWork as St, readLiveAttachment as T, requestConversationWork as Tt, agentInvocationSchema as U, consumeSandboxEgressPermissionDeniedSignal as Ut, resolveTaskOutcomes as V, clearSandboxEgressSignals as Vt, createAgentInvocationSchema as W, parseSandboxEgressCredentialToken as Wt, EVENT_WAIT_MS as X, createUserTokenStore as Xt, ingestEvent as Y, startOAuthFlow as Yt, isEventMailboxMetadata as Z, buildSteeringPiMessage as Zt, resolvePluginOAuthAccount as _, getConversationWorkState as _t, buildAuthPauseResponse as a, shouldEmitDevAgentTrace as an, beginConversationResume as at, unpublishedArtifactHeaders as b, recordAttemptFailure as bt, deleteMcpAuthSessionsForUserProvider as c, clearConsumedConversationWake as ct, getMcpAuthSession as d, completeConversationWork as dt, toolInvocationDestination as en, eventGuidance as et, getMcpStoredOAuthCredentials as f, countPendingConversationMessages as ft, readStats as g, getConversation as gt, latestProgressStatus as h, ensureConversationWake as ht, executeCredentialedEgressRequest as i, runDispatchOutcome as in, appendAndEnqueueInboundMessage as it, resolveSlackChannelIdFromThreadId as j, hasRunnableConversationWork$1 as jt, readSlackRawMessageContext as k, CONVERSATION_WORK_STALE_ENQUEUE_MS as kt, deleteMcpServerSessionId as l, clearExpiredConversationLease as lt, PluginCredentialFailureError as m, drainConversationMailbox as mt, executeAgentRun as n, deliversFinalText as nn, ackMessages as nt, finalizeMcpAuthorization as o, getConfigDefaults as on, cancelHumanFacingPendingMessages as ot, putMcpStoredOAuthCredentials as p, deadLetterAttempt as pt, getMcpProviderErrorAttributes as q, parseOAuthStatePayload as qt, createContextCompactor as r, finishedRunReply as rn, appendAndEnqueueExclusiveInboundMessage as rt, deleteMcpAuthSession as s, setConfigDefaults as sn, checkInConversationWork as st, createAgentRunner as t, AuthorizationFlowDisabledError as tn, pluginEventCatalog as tt, deleteMcpStoredOAuthCredentials as u, completeConversationStop as ut, publicArtifactHeaders as v, hasRunnableConversationWork as vt, collectAttachmentGarbage as w, requestAnotherSlice as wt, lookupSlackUserProfile as x, recordConversationRetry as xt, readPublicArtifact as y, listActiveConversationIds as yt, eventAutomationTriggerAvailable as z, shouldPropagateSandboxEgressTrace as zt };
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 };