@sentry/junior 0.272.0 → 0.273.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 (70) hide show
  1. package/dist/{adapter-BDYDi1F9.js → adapter-umap1cth.js} +1 -1
  2. package/dist/{admin-Dj7EFycG.js → admin-0o6E_3lo.js} +1 -1
  3. package/dist/{agent-runner-Cl6Usx2Y.js → agent-runner-D7Qz-lGO.js} +517 -298
  4. package/dist/api/schema.d.ts +1 -1
  5. package/dist/api/schema.js +4 -4
  6. package/dist/api.d.ts +1 -1
  7. package/dist/api.js +56 -573
  8. package/dist/{app-DjZblE2J.d.ts → app-BcyhJq8x.d.ts} +4 -9
  9. package/dist/app.d.ts +1 -1
  10. package/dist/app.js +673 -127
  11. package/dist/{assistant-reply-DN2w6JL_.js → assistant-reply-wZ7i4OFa.js} +1 -1
  12. package/dist/{automation-CV7g5h3A.js → automation-yffXcZt9.js} +1 -1
  13. package/dist/{briefs-PysAqnjV.js → briefs-CgMnlpIp.js} +4 -4
  14. package/dist/{catalog-runtime-BNeCWIaF.js → catalog-runtime-TygDsPOU.js} +1 -1
  15. package/dist/cli/chat.js +15 -15
  16. package/dist/cli/check.js +3 -3
  17. package/dist/cli/init.js +1 -1
  18. package/dist/cli/main.js +3 -3
  19. package/dist/cli/plugins.js +4 -4
  20. package/dist/cli/snapshot-create.js +4 -4
  21. package/dist/cli/upgrade.d.ts +1 -1
  22. package/dist/cli/upgrade.js +3 -3
  23. package/dist/{client-FRphcDWW.js → client-DhqbV0Fe.js} +63 -5
  24. package/dist/{reporting-schema-CZREhYc-.js → common-Eu6sx09c.js} +4 -1
  25. package/dist/{config-BUP9wjiR.js → config-C6FTd7PX.js} +6 -14
  26. package/dist/{connections-CesHau9u.js → connections-Dq72KajR.js} +11 -11
  27. package/dist/{conversation-BtQzLoK8.js → conversation-T4oabbBi.js} +10 -0
  28. package/dist/{conversation-privacy-DiGvA9a3.js → conversation-privacy-BeRgrsph.js} +1 -1
  29. package/dist/{credential-sync-D2odJC30.js → credential-sync-BmCbHAp4.js} +15 -16
  30. package/dist/{db-BPLmOwXf.d.ts → db-DOzBGLn5.d.ts} +1 -1
  31. package/dist/{executor-DGDpyegI.js → executor-BafgvQcc.js} +2 -2
  32. package/dist/instrumentation.js +114 -3
  33. package/dist/{job-queue-CjB97sii.js → job-queue-C4lphW9Y.js} +3 -3
  34. package/dist/{logging-CjkDr-jP.js → logging-CNs1e04n.js} +6 -1
  35. package/dist/{message-D9y8PKA1.js → message-CETC5lsl.js} +9 -9
  36. package/dist/nitro.d.ts +1 -1
  37. package/dist/nitro.js +3 -3
  38. package/dist/{oauth-callback-server-02Pej6hj.js → oauth-callback-server-DcQZsKMm.js} +2 -2
  39. package/dist/{credentialed-CgTulRDH.js → projection-BOpi891B.js} +4576 -49
  40. package/dist/{prompt-vfceH0c-.js → prompt-B1ySjUyf.js} +1 -1
  41. package/dist/{registry-7XgwNgQe.js → registry-B-yUrv3h.js} +2 -2
  42. package/dist/{resolve-CuuxNfWG.js → resolve-DqVtd4Q9.js} +3 -3
  43. package/dist/{result-BRhb6iN1.js → result-DQwTiLTw.js} +9 -9
  44. package/dist/{runner-DaWCofuS.js → runner-Dco5qMq6.js} +11 -11
  45. package/dist/{sandbox-egress-signals-Bn6FA1UC.js → sandbox-egress-signals-DFc_9Mcv.js} +1 -1
  46. package/dist/{schema-D5VLF4NT.js → schema-5xHDGlBW.js} +9 -7
  47. package/dist/{schema-CNjAzYOw.d.ts → schema-9OGdsaxF.d.ts} +13 -0
  48. package/dist/{service-account-D9vrsi8d.js → service-account-CWfk9Cs-.js} +3 -3
  49. package/dist/{service-account-H45LIAG2.js → service-account-smdPFWNy.js} +3 -3
  50. package/dist/{session-stop-HKl0ECX0.js → session-stop-DaT_7tYa.js} +11 -11
  51. package/dist/{skills-CsiKu3-g.js → skills-BbtJSF2-.js} +2 -2
  52. package/dist/{spawn-DqjBxqWh.js → spawn-Ceikdjkp.js} +2 -2
  53. package/dist/{list-JZUP1yog.js → stop-dKUc-x2K.js} +996 -432
  54. package/dist/{store-DyVKH0QB.js → store-C1xuZcLd.js} +4 -4
  55. package/dist/{store-CPF2kzbu.js → store-DaxUuOmD.js} +3 -3
  56. package/dist/{task-queue-BbQXCcNX.js → task-queue-ymAh_-P1.js} +4 -4
  57. package/dist/{task-runner-ChzW5E7R.js → task-runner-vlH6pEm3.js} +6 -6
  58. package/dist/{thread-state-Ds8JTsX6.js → thread-state-BkUpE9Ae.js} +4 -5
  59. package/dist/{turn-failure-response-DCMvjsA0.js → turn-failure-response-DIsD7ip_.js} +7 -8
  60. package/dist/{turn-lifecycle-DM_77q33.js → turn-lifecycle-BdPKdPS8.js} +1 -1
  61. package/dist/turn-session-routing-BdCTDq9m.js +1625 -0
  62. package/dist/{validation-DcGelbBi.js → validation-BQjCfh84.js} +1 -1
  63. package/dist/{version-C48azPKq.js → version-BpiFGZZW.js} +1 -1
  64. package/dist/version.js +1 -1
  65. package/dist/{viewer-BCRuO8Wg.js → viewer-C6Y1aXjZ.js} +27 -15
  66. package/dist/{work-zxkmbJAp.js → work-ChPh_YEh.js} +10 -38
  67. package/dist/{worker-CNNxHF1V.js → worker-Be_sK_ou.js} +6 -6
  68. package/package.json +4 -4
  69. package/dist/turn-session-routing-BV_gX7n3.js +0 -6072
  70. package/dist/{oauth-relay-CTygMkNx.js → oauth-relay-C6ZAyJgf.js} +1 -1
@@ -1,43 +1,43 @@
1
1
  import { t as __exportAll } from "./rolldown-runtime-nwUcYXjP.js";
2
- import { $ as saveEventAutomation, A as readAutomationRuns, Ar as trimTrailingAssistantMessages, B as readScheduledAutomation, Br as estimateTextTokens, Bt as recordMcpProviderConnected, C as readAutomationExecutionByConversationId, Cn as loadTurnCheckpoint, Cr as isAssistantMessage, Ct as requireEventIdentifier, D as readAutomationExecutionStatusHours, Dr as normalizeToolNameFromResult, Dt as loadConnectedMcpProviders, E as readAutomationExecutionStatusDays, Er as isToolResultMessage, Fr as buildDeterministicTurnId, Ft as recordAttachmentsDelivered, Hr as getAgentContextInputLimitTokens, Ht as recordToolExecutionStarted, I as createScheduledAutomation, J as deleteEventAutomation, Jr as historyItemFromPiMessage, L as deleteScheduledAutomation, Mr as SESSION_CONTEXT_SECTION_TAG, Mt as loadTurnRoute, Nr as TURN_CONTEXT_TAG, Nt as openConversationProjection, O as readAutomationExecutionSummaries, Or as retainRuntimeTurnContext, Ot as loadConversationProjection, Pt as recordAgentsInstructionsUpdated, Q as parseEventAutomationRow, Qt as resolveTaskTitle, Rt as recordAuthorizationRequested, Sn as continuableMessages, St as registeredResourceTypeSchema, T as readAutomationExecutionHours, Tr as isToolResultError, Ut as recordTurnRoute, V as saveScheduledAutomation, Vr as getAgentContextCompactionTriggerTokens, Wr as isExperimentalFeatureEnabled, X as getEventAutomation, Xr as getConfigDefaults, Yr as piMessageFromHistoryItem, Z as listDeletedEventAutomationsCreatedBy, Zt as fallbackShortTitle, _n as abandonReplacedPendingAuth, _r as extractAssistantText, _t as eventWatchUnavailable, at as listAutomationVersions, br as getUserMessageInstructionText, bt as registeredEventTriggerProblems, c as getDispatchIdForConversation, cr as TURN_LIMIT_WRAP_UP_MESSAGE, dn as PluginCredentialFailureError, dr as TurnToolCallLimitExceededError, et as setEventAutomationStatus, fn as createPluginAuthOrchestration, ft as automationEventCatalog, gn as AuthorizationPauseError, gt as eventNamespaceSchema, hn as AuthorizationFlowDisabledError, hr as CODEMODE_TOOL_NAME, jr as REQUEST_CONTEXT_SECTION_TAG, k as readAutomationExecutions, kr as stripRuntimeTurnContext, kt as loadLastModelProfile, l as getDispatchInputMessageId, lr as TurnBudgetExceededError, lt as automationRevision, mn as AuthPausePersistenceError, mr as isTurnExecutionLimitExceededError, ot as readAutomationVersion, pn as authorizationId, pr as createTurnLimitGate, pt as describeRegisteredEventTrigger, q as createEventAutomation, rt as automationDefinition, st as sameDefinitionValue, t as resolveConversationRouting, u as getDispatchRecord, un as createPluginEgress, ur as TurnSliceLimitExceededError, ut as requireAutomationRevision, vn as canReusePendingAuthLink, vr as getPiMessageRole, vt as hasEventCatalogEntries, w as readAutomationExecutionDays, wn as saveTurnCheckpoint, wr as isContinuablePiBoundary, wt as requireValidEventTrigger, x as emptyAutomationRunWindows, xr as hasRuntimeTurnContext, xt as registeredEventTypeSchema, yr as getTerminalAssistantMessages, yt as registeredEventMatchSchema, z as parseScheduledAutomationRow, zt as recordGuardianActionReviewed } from "./turn-session-routing-BV_gX7n3.js";
3
- import { _ as getProviderErrorAttributes, a as completeText, b as getGatewayApiKey, c as COMPACTION_SUMMARY_PREFIX, d as renderCurrentInstruction, f as unwrapCurrentInstruction, g as findProviderError, h as createProviderError, i as completeObject, l as hasCompactedConversationContext, m as ProviderError, n as GEN_AI_SERVER_ADDRESS, p as escapeXml, s as resolveGatewayModel, t as GEN_AI_PROVIDER_NAME, u as isCompactionSummaryText, x as resolveGatewayCredential, y as isProviderRetryError } from "./client-FRphcDWW.js";
4
- import { n as botConfig, v as normalizeSlackEmojiName } from "./config-BUP9wjiR.js";
5
- import { B as isRecord$4, C as summarizeMessageText, E as withSpan, H as toOptionalString, S as setTags, T as withLogContext, _ as runBestEffort, a as extractGenAiUsageSummary, b as setSpanAttributes, f as logError, g as normalizeGenAiFinishReason, h as logWarn, i as extractGenAiUsageAttributes, l as getLogContextAttributesForSpan, m as logInfo, p as logException, u as getTracePropagationHeaders, v as serializeGenAiAttribute, x as setSpanStatus, z as normalizeIdentityEmail } from "./logging-CjkDr-jP.js";
2
+ import { $ as loadTurnCheckpoint, $n as canAgentChangeReviewItem, $r as isAlreadyExistsError, $t as isAssistantMessage, Ai as findMatchingWatches, An as getPluginSystemPromptContributions, Ar as formatProviderLabel, B as createPluginEgress, Ba as matchSlackAttachments, Bn as closeReviewItem, Br as addReactionToMessage, Bt as TurnSliceLimitExceededError, Ci as STOP_WATCHING_TOOL_NAME, Cn as getOAuthAccountProviders, Cr as hash, D as fallbackShortTitle, Di as createTimerWatch, En as createPluginHookRunner, Er as getSandboxProvider, G as AuthorizationFlowDisabledError, Ga as createPluginAnnotations, Gi as getSlackClient, Gn as insertReviewItem, Gt as CODEMODE_TOOL_NAME, H as createPluginAuthOrchestration, Hi as downloadPrivateSlackFile, Hn as dismissOpenReviewItems, Hr as getSlackMessagePermalink, Ir as normalizeSlackReplyMarkdown, J as canReusePendingAuthLink, Ja as defaultTimezone, Ji as normalizeSlackConversationId, Jn as searchReviewItems, Jr as updateSlackMessage, Jt as getPiMessageRole, K as AuthorizationPauseError, Ki as isConversationScopedChannel, Kr as postSlackMessage, Mi as listWatches, Mn as getPluginUserPromptContributions, Mr as resolveBaseUrl, Nn as getPlugins, O as resolveTaskTitle, Oi as createWatch, Pr as escapeSlackMrkdwnText, Q as continuableMessages, Qr as isAbortError, Rr as slackMention, Rt as TURN_LIMIT_WRAP_UP_MESSAGE, Si as RESOURCE_WATCH_TOOL_SOURCE, Sn as createUserTokenStore, Sr as create, Ti as cancelWatch, Tr as ensureDockerDaemon, U as authorizationId, Ui as getFilePermalink, Un as getOpenReviewItemByDedupeKey, Ur as openSlackDirectMessage, Ut as createTurnLimitGate, V as PluginCredentialFailureError, Vi as SlackActionError, Vr as deleteSlackMessage, Vt as TurnToolCallLimitExceededError, W as AuthPausePersistenceError, Wa as saveObjectAnnotations, Wn as getReviewItem, Wt as isTurnExecutionLimitExceededError, Xi as withSlackRetries, Xn as updateReviewItemContent, Xt as getUserMessageInstructionText, Ya as resolveUserTimezone, Yi as runWithSlackInstallationToken, Yr as uploadFilesToConversation, Yt as getTerminalAssistantMessages, Zi as sleep, Zn as PLUGIN_REVIEW_ITEM_KEY_PREFIX, Zt as hasRuntimeTurnContext, _a as sandboxSkillDir, _i as workspaceRepoCheckoutPath, _n as getAgentContextCompactionTriggerTokens, _r as WorkspaceValidationError, a as loadLastModelProfile, ai as workspaceSnapshotNotReadyUserMessage, an as stripRuntimeTurnContext, bi as RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS, bn as isExperimentalFeatureEnabled, br as setWorkspaceSnapshot, c as loadTurnRoute, ca as buildCommandScript, ci as consumeSandboxEgressPermissionDeniedSignal, cn as SESSION_CONTEXT_SECTION_TAG, cr as resolveAssignee, d as recordAttachmentsDelivered, da as SANDBOX_COMMAND_PATH, dn as buildDeterministicTurnId, dr as deleteWorkspace, ei as throwSandboxOperationError, en as isContinuablePiBoundary, eo as historyItemFromPiMessage, er as canReadReviewItem, et as saveTurnCheckpoint, fa as SANDBOX_DATA_ROOT, fi as buildSandboxEgressNetworkPolicy, fr as getWorkspace, g as recordMcpProviderConnected, ga as SANDBOX_WORKSPACE_ROOT, gi as tryWorkspaceRepoCheckoutPath, gn as estimateTextTokens, gr as updateWorkspace, h as recordGuardianActionReviewed, ha as SANDBOX_SKILLS_ROOT, hi as SANDBOX_RUNTIME, i as loadConversationProjection, ia as getDashboardTaskLink, ii as isWorkspaceSnapshotNotReadyError, in as retainRuntimeTurnContext, io as getConfigDefaults, ir as requireReviewItemLinks, ji as getWatch, jn as getPluginTools, ki as deliverWatch, kr as deliverOAuthAuthorization, l as openConversationProjection, la as buildNonInteractiveShellScript, li as createSandboxEgressCredentialToken, ln as TURN_CONTEXT_TAG, lr as resolveAssignees, m as recordAuthorizationRequested, ma as SANDBOX_REPOS_ROOT, mr as listWorkspaces, ni as WorkspaceSnapshotNotReadyError, nn as isToolResultMessage, no as JUNIOR_NATIVE_EVENT_NAMESPACE, nr as readReviewItemAgentScope, oa as ToolInputError, oi as clearSandboxEgressSignals, on as trimTrailingAssistantMessages, or as EVERYONE_ASSIGNEE, pa as SANDBOX_JUNIOR_ROOT, pi as resolveSandboxCommandEnvironment, pr as getWorkspaceByName, q as abandonReplacedPendingAuth, qt as extractAssistantText, r as loadConnectedMcpProviders, ra as getDashboardReviewItemLink, ri as getWorkspaceSnapshotNotReadyError, rn as normalizeToolNameFromResult, ro as conversationNoteEvent, rr as requireReviewItemAssignees, sa as PluginToolDeniedError, si as consumeSandboxEgressAuthRequiredSignal, sn as REQUEST_CONTEXT_SECTION_TAG, sr as automationCreatorUserId, ta as getDashboardConversationLink, ti as wrapSandboxSetupError, tn as isToolResultError, to as piMessageFromHistoryItem, u as recordAgentsInstructionsUpdated, ua as runNonInteractiveCommand, ur as createWorkspace, v as recordToolExecutionStarted, va as sandboxSkillFile, vi as resolveChannelCapabilities, vn as getAgentContextInputLimitTokens, vr as invalidateReadySnapshot, wi as cancelSubscriptions, wr as isStale, xi as RESOURCE_SUBSCRIPTION_MAX_TTL_MS, xr as setWorkspaceSnapshotBuild, y as recordTurnRoute, ya as sandboxSkillPathResolution, yi as getSlackToolContext, yr as loadSnapshotsForProfile, zt as TurnBudgetExceededError } from "./projection-BOpi891B.js";
3
+ import { S as resolveGatewayCredential, _ as findProviderError, a as completeText, b as isProviderRetryError, c as resolveGatewayModel, d as isCompactionSummaryText, f as renderCurrentInstruction, g as createProviderError, h as ProviderError, i as completeObject, l as COMPACTION_SUMMARY_PREFIX, m as escapeXml, n as GEN_AI_SERVER_ADDRESS, p as unwrapCurrentInstruction, t as GEN_AI_PROVIDER_NAME, u as hasCompactedConversationContext, v as getProviderErrorAttributes, x as getGatewayApiKey } from "./client-DhqbV0Fe.js";
4
+ import { _ as normalizeSlackEmojiName, n as botConfig } from "./config-C6FTd7PX.js";
5
+ import { B as isRecord$4, C as summarizeMessageText, E as withSpan, H as toOptionalString, S as setTags, T as withLogContext, _ as runBestEffort, a as extractGenAiUsageSummary, b as setSpanAttributes, f as logError, g as normalizeGenAiFinishReason, h as logWarn, i as extractGenAiUsageAttributes, l as getLogContextAttributesForSpan, m as logInfo, p as logException, u as getTracePropagationHeaders, v as serializeGenAiAttribute, x as setSpanStatus, z as normalizeIdentityEmail } from "./logging-CNs1e04n.js";
6
6
  import { g as withActiveSpan, i as getActiveSpan, m as startInactiveSpan } from "./sentry-MDkYAVPV.js";
7
- 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-DiGvA9a3.js";
7
+ 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-BeRgrsph.js";
8
8
  import { a as formatModelProfile, c as modelProfileSchema, l as profileConfig, n as ModelProfileNotConfiguredError, o as formatModelProfiles, r as boundProfile, s as modelIdForProfile, u as TURN_REASONING_LEVELS } from "./model-profile-B998sjVd.js";
9
9
  import { d as parseSlackChannelReferenceId, f as parseSlackTeamId, h as parseSlackMessageTs, n as createSlackActor, o as isUserActor, p as parseSlackUserId, u as parseSlackChannelId } from "./actor-B4DYygOB.js";
10
- import { C as getSqlExecutor, M as resolveActorIdentityId, Q as sameDestination, S as getDb, U as addAgentTurnUsage, W as agentTurnCostUsd, X as parseDestination, a as readActorIdentityFromSql, b as getConversationMessageSearchStore, f as identityAvatarUrl, h as readUserAvatarUrls, i as readActorIdentity, j as SYSTEM_IDENTITY_ID, q as hasAgentTurnUsage, x as getConversationStore, y as getConversationEventStore } from "./viewer-BCRuO8Wg.js";
10
+ import { $ as sameDestination, C as getSqlExecutor, G as agentTurnCostUsd, J as hasAgentTurnUsage, M as SYSTEM_IDENTITY_ID, N as resolveActorIdentityId, S as getDb, W as addAgentTurnUsage, Z as parseDestination, a as readActorIdentityFromSql, b as getConversationMessageSearchStore, f as identityAvatarUrl, h as readUserAvatarUrls, i as readActorIdentity, x as getConversationStore, y as getConversationEventStore } from "./viewer-C6Y1aXjZ.js";
11
11
  import { a as juniorAgentInvocations, c as juniorIdentities, d as juniorDestinations, i as juniorAgentBindings, l as juniorUsers, s as juniorConversations } from "./agent-invocations-C1RNOCs8.js";
12
- import { E as juniorArtifacts, S as juniorConversationAnnotations, T as juniorAttachments, _ as juniorConversationBriefs, d as juniorAutomations, f as juniorStats, l as juniorAutomationRuns, s as juniorReviewItemSlackCards } from "./executor-DGDpyegI.js";
12
+ import { E as juniorArtifacts, S as juniorConversationAnnotations, T as juniorAttachments, _ as juniorConversationBriefs, d as juniorAutomations, f as juniorStats, l as juniorAutomationRuns, s as juniorReviewItemSlackCards } from "./executor-BafgvQcc.js";
13
13
  import { _ as sameActorIdentity, g as instructionProvenanceFor, h as instructionActors, i as KNOWN_CONVERSATION_EVENT_TYPES, m as contextProvenance, n as detectImageMediaType, p as agentInstructionProvenance, r as isVisionImageMediaType } from "./media-U6Tuz6Mi.js";
14
14
  import { a as listReferenceFiles, c as soulPathCandidates, l as worldPathCandidates } from "./package-discovery-DJBompdd.js";
15
- import { c as fetchWithBoundedOAuthErrorBodies, d as actorLogContext, f as credentialContextForActor, g as credentialUserSubjectId, i as resolvePluginHeaderEnvRefs, l as getOAuthProviderErrorAttributes, s as OAuthProviderError } from "./registry-7XgwNgQe.js";
16
- import { n as pluginCatalogRuntime } from "./catalog-runtime-BNeCWIaF.js";
17
- import { a as parseSkillInvocation, i as parseSkillFile, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-CsiKu3-g.js";
18
- import { $ as loadSnapshotsForProfile, $n as sandboxSkillDir, $t as workspaceRepoCheckoutPath, A as updateReviewItemContent, An as normalizeSlackConversationId, B as EVERYONE_ASSIGNEE, Bn as getDashboardTaskLink, Bt as clearSandboxEgressSignals, C as getOpenReviewItemByDedupeKey, Cn as SlackActionError, Ct as openSlackDirectMessage, Dn as getSlackClient, Et as postSlackMessage, Ft as wrapSandboxSetupError, G as deleteWorkspace, Gn as buildNonInteractiveShellScript, H as resolveAssignee, Hn as ToolInputError, Ht as consumeSandboxEgressPermissionDeniedSignal, I as readReviewItemAgentScope, Ir as conversationNoteEvent, It as WorkspaceSnapshotNotReadyError, J as listWorkspaces, Jn as SANDBOX_DATA_ROOT, Jt as resolveSandboxCommandEnvironment, K as getWorkspace, Kn as runNonInteractiveCommand, L as requireReviewItemAssignees, Ln as getDashboardConversationLink, Lt as getWorkspaceSnapshotNotReadyError, Mn as withSlackRetries, Mr as JUNIOR_NATIVE_EVENT_NAMESPACE, Mt as isAbortError, N as canAgentChangeReviewItem, Nn as sleep, Nt as isAlreadyExistsError, O as searchReviewItems, On as isConversationScopedChannel, Or as defaultTimezone, Ot as updateSlackMessage, P as canReadReviewItem, Pt as throwSandboxOperationError, Q as invalidateReadySnapshot, Qn as SANDBOX_WORKSPACE_ROOT, Qt as tryWorkspaceRepoCheckoutPath, R as requireReviewItemLinks, Rt as isWorkspaceSnapshotNotReadyError, S as dismissOpenReviewItems, St as getSlackMessagePermalink, T as insertReviewItem, Tn as getFilePermalink, Tr as createPluginAnnotations, U as resolveAssignees, Un as PluginToolDeniedError, Ut as createSandboxEgressCredentialToken, V as automationCreatorUserId, Vt as consumeSandboxEgressAuthRequiredSignal, W as createWorkspace, Wn as buildCommandScript, X as updateWorkspace, Xn as SANDBOX_REPOS_ROOT, Yn as SANDBOX_JUNIOR_ROOT, Z as WorkspaceValidationError, Zn as SANDBOX_SKILLS_ROOT, Zt as SANDBOX_RUNTIME, an as STOP_WATCHING_TOOL_NAME, at as ensureDockerDaemon, b as closeReviewItem, br as matchSlackAttachments, bt as addReactionToMessage, d as getPluginTools, dn as deliverWatch, en as resolveChannelCapabilities, er as sandboxSkillFile, et as setWorkspaceSnapshot, f as getPluginUserPromptContributions, fn as findMatchingWatches, ft as resolveBaseUrl, gt as normalizeSlackReplyMarkdown, in as RESOURCE_WATCH_TOOL_SOURCE, it as isStale, j as PLUGIN_REVIEW_ITEM_KEY_PREFIX, jn as runWithSlackInstallationToken, kr as resolveUserTimezone, kt as uploadFilesToConversation, ln as createTimerWatch, lt as deliverOAuthAuthorization, mn as listWatches, mt as escapeSlackMrkdwnText, n as createUserTokenStore, nn as RESOURCE_SUBSCRIPTION_DEFAULT_TTL_MS, nt as create, o as createPluginHookRunner, on as cancelSubscriptions, ot as getSandboxProvider, p as getPlugins, pn as getWatch, q as getWorkspaceByName, qn as SANDBOX_COMMAND_PATH, qt as buildSandboxEgressNetworkPolicy, r as getOAuthAccountProviders, rn as RESOURCE_SUBSCRIPTION_MAX_TTL_MS, rt as hash, sn as cancelWatch, tn as getSlackToolContext, tr as sandboxSkillPathResolution, tt as setWorkspaceSnapshotBuild, u as getPluginSystemPromptContributions, un as createWatch, ut as formatProviderLabel, vt as slackMention, w as getReviewItem, wn as downloadPrivateSlackFile, wr as saveObjectAnnotations, xt as deleteSlackMessage, zn as getDashboardReviewItemLink, zt as workspaceSnapshotNotReadyUserMessage } from "./credentialed-CgTulRDH.js";
15
+ import { c as fetchWithBoundedOAuthErrorBodies, d as actorLogContext, f as credentialContextForActor, g as credentialUserSubjectId, i as resolvePluginHeaderEnvRefs, l as getOAuthProviderErrorAttributes, s as OAuthProviderError } from "./registry-B-yUrv3h.js";
16
+ import { n as pluginCatalogRuntime } from "./catalog-runtime-TygDsPOU.js";
17
+ import { a as parseSkillInvocation, i as parseSkillFile, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-BbtJSF2-.js";
19
18
  import { a as withLock, i as fenceLock } from "./locks-D-0h111u.js";
20
- import { r as getStateAdapter } from "./adapter-BDYDi1F9.js";
19
+ import { r as getStateAdapter } from "./adapter-umap1cth.js";
21
20
  import { i as isValidTimeZone, r as getZonedDateTimeParts, t as getFirstRunAtMs } from "./cadence-R7Em4RjE.js";
22
- import { J as briefOutcomeStatusSchema, V as removedCardSchema, Y as conversationBriefSchema, q as briefLinkSchema } from "./conversation-BtQzLoK8.js";
23
- import { r as EVENT_AUTHOR_ID } from "./prompt-vfceH0c-.js";
24
- import { A as AgentMessagePausedError, F as CHILD_AGENT_RESULTS, I as PARENT_AGENT_TARGET, J as isTurnInputCommitLostError, L as agentNameSchema, M as AgentMessageTargetError, N as AgentNameInUseError, R as AGENT_TYPES, U as CooperativeTurnYieldError, V as formatAgentTypes, W as TurnInputCommitLostError, _ as invocationFromRow, d as getAgentInvocationMessageId, g as getLatestAgentInvocationForConversation, h as getChildAgentLockName, j as AgentMessageQueueFullError, k as AgentInvocationLimitError, t as NON_TERMINAL_AGENT_INVOCATION_STATUSES, u as getAgentInvocationIdForResult, v as isTerminalAgentInvocation } from "./store-DyVKH0QB.js";
25
- import { a as isNoReplyMarker, i as NO_REPLY_MARKER, n as getAssistantReplyText, r as sanitizeAssistantText, t as decideReply } from "./assistant-reply-DN2w6JL_.js";
26
- import { E as resolveSlackReplyTarget, L as readAutomationResult, M as FINISH_AUTOMATION_RUN_TOOL_NAME, N as automationResultSchema$1, R as remindMissingAutomationResult, _ as slackChannelMessageTrigger, a as EVENT_WAIT_MS, b as workspaceSnapshotFinishedEvent, d as getEventCatalog, n as markConversationsStopped, o as enqueueEventNotification, p as requireSlackChannelMessageScope, t as getConversationStoppedAtMs, v as WORKSPACE_SNAPSHOT_FAILED_EVENT, x as workspaceSnapshotWatch, y as WORKSPACE_SNAPSHOT_READY_EVENT } from "./session-stop-HKl0ECX0.js";
21
+ import { J as briefOutcomeStatusSchema, V as removedCardSchema, Y as conversationBriefSchema, q as briefLinkSchema } from "./conversation-T4oabbBi.js";
22
+ import { r as EVENT_AUTHOR_ID } from "./prompt-B1ySjUyf.js";
23
+ import { A as AgentMessagePausedError, F as CHILD_AGENT_RESULTS, I as PARENT_AGENT_TARGET, J as isTurnInputCommitLostError, L as agentNameSchema, M as AgentMessageTargetError, N as AgentNameInUseError, R as AGENT_TYPES, U as CooperativeTurnYieldError, V as formatAgentTypes, W as TurnInputCommitLostError, _ as invocationFromRow, d as getAgentInvocationMessageId, g as getLatestAgentInvocationForConversation, h as getChildAgentLockName, j as AgentMessageQueueFullError, k as AgentInvocationLimitError, t as NON_TERMINAL_AGENT_INVOCATION_STATUSES, u as getAgentInvocationIdForResult, v as isTerminalAgentInvocation } from "./store-C1xuZcLd.js";
24
+ import { a as isNoReplyMarker, i as NO_REPLY_MARKER, n as getAssistantReplyText, r as sanitizeAssistantText, t as decideReply } from "./assistant-reply-wZ7i4OFa.js";
25
+ import { E as resolveSlackReplyTarget, L as readAutomationResult, M as FINISH_AUTOMATION_RUN_TOOL_NAME, N as automationResultSchema$1, R as remindMissingAutomationResult, _ as slackChannelMessageTrigger, a as EVENT_WAIT_MS, b as workspaceSnapshotFinishedEvent, d as getEventCatalog, n as markConversationsStopped, o as enqueueEventNotification, p as requireSlackChannelMessageScope, t as getConversationStoppedAtMs, v as WORKSPACE_SNAPSHOT_FAILED_EVENT, x as workspaceSnapshotWatch, y as WORKSPACE_SNAPSHOT_READY_EVENT } from "./session-stop-DaT_7tYa.js";
27
26
  import { C as userCustomizationTextSchema, b as webMessageId, g as formatUserInputAnswer, h as buttonUserInputField, m as USER_INPUT_NOTE, p as USER_INPUT_LIMITS, s as sumUtcHoursIntoSixHours, v as normalizeUserInputAnswer, x as USER_CUSTOMIZATION_MAX_CHARS, y as userInputRequestSchema } from "./reporting-window-DgHgwQB3.js";
28
- import { a as setSnapshotSpanAttributes, i as postinstall, n as resolve$1, r as dependencies, s as getSandboxResources } from "./resolve-CuuxNfWG.js";
29
- import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-CjB97sii.js";
30
- import { D as stopConversationWork, n as appendAndEnqueueExclusiveInboundMessage, r as appendAndEnqueueInboundMessage } from "./store-CPF2kzbu.js";
31
- import { _ as getMcpProviderErrorAttributes, a as serviceAccountOnly, c as createMcpServiceAccountClientProvider, d as PluginMcpClient, f as McpProviderError, g as getMcpAwareTelemetryMessage, h as getMcpAwareErrorType, i as serviceAccountAllows, l as getMcpServiceAccountCredentials, m as McpToolError, p as McpServiceAccountRequiredError, u as McpAuthorizationRequiredError, v as toMcpProviderError } from "./service-account-H45LIAG2.js";
32
- import { A as SCHEDULED_AUTOMATION_SYSTEM_ACTOR, D as taskOutcomeInputSchema, E as automationTitleSchema, F as scheduleIntentSchema, I as weekdaySchema, N as ScheduleIntentError, P as compileScheduleIntent, T as automationInstructionToolSchema, k as eventAutomationTriggerSchema, l as automationListQuerySchema } from "./automation-CV7g5h3A.js";
27
+ import { a as setSnapshotSpanAttributes, i as postinstall, n as resolve$1, r as dependencies, s as getSandboxResources } from "./resolve-DqVtd4Q9.js";
28
+ import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-C4lphW9Y.js";
29
+ import { D as stopConversationWork, n as appendAndEnqueueExclusiveInboundMessage, r as appendAndEnqueueInboundMessage } from "./store-DaxUuOmD.js";
30
+ import { $ as saveEventAutomation, A as readAutomationRuns, B as readScheduledAutomation, C as readAutomationExecutionByConversationId, Ct as requireEventIdentifier, D as readAutomationExecutionStatusHours, E as readAutomationExecutionStatusDays, I as createScheduledAutomation, J as deleteEventAutomation, L as deleteScheduledAutomation, O as readAutomationExecutionSummaries, Q as parseEventAutomationRow, St as registeredResourceTypeSchema, T as readAutomationExecutionHours, V as saveScheduledAutomation, X as getEventAutomation, Z as listDeletedEventAutomationsCreatedBy, _t as eventWatchUnavailable, at as listAutomationVersions, bt as registeredEventTriggerProblems, c as getDispatchIdForConversation, et as setEventAutomationStatus, ft as automationEventCatalog, gt as eventNamespaceSchema, k as readAutomationExecutions, l as getDispatchInputMessageId, lt as automationRevision, ot as readAutomationVersion, pt as describeRegisteredEventTrigger, q as createEventAutomation, rt as automationDefinition, st as sameDefinitionValue, t as resolveConversationRouting, u as getDispatchRecord, ut as requireAutomationRevision, vt as hasEventCatalogEntries, w as readAutomationExecutionDays, wt as requireValidEventTrigger, x as emptyAutomationRunWindows, xt as registeredEventTypeSchema, yt as registeredEventMatchSchema, z as parseScheduledAutomationRow } from "./turn-session-routing-BdCTDq9m.js";
31
+ import { _ as getMcpProviderErrorAttributes, a as serviceAccountOnly, c as createMcpServiceAccountClientProvider, d as PluginMcpClient, f as McpProviderError, g as getMcpAwareTelemetryMessage, h as getMcpAwareErrorType, i as serviceAccountAllows, l as getMcpServiceAccountCredentials, m as McpToolError, p as McpServiceAccountRequiredError, u as McpAuthorizationRequiredError, v as toMcpProviderError } from "./service-account-smdPFWNy.js";
32
+ import { A as SCHEDULED_AUTOMATION_SYSTEM_ACTOR, D as taskOutcomeInputSchema, E as automationTitleSchema, F as scheduleIntentSchema, I as weekdaySchema, N as ScheduleIntentError, P as compileScheduleIntent, T as automationInstructionToolSchema, k as eventAutomationTriggerSchema, l as automationListQuerySchema } from "./automation-yffXcZt9.js";
33
33
  import { n as privateTraceResultAttributes } from "./private-trace-result-CE7_lEdq.js";
34
34
  import { streamSimple } from "@earendil-works/pi-ai/compat";
35
35
  import { createGatewayProvider } from "@ai-sdk/gateway";
36
36
  import { NoImageGeneratedError, generateImage, generateText } from "ai";
37
37
  import path from "node:path";
38
38
  import { isDeepStrictEqual } from "node:util";
39
- import { z } from "zod";
40
39
  import { createHash, randomBytes, randomInt, randomUUID } from "node:crypto";
40
+ import { z } from "zod";
41
41
  import { and, asc, count, desc, eq, exists, gt, gte, inArray, isNotNull, isNull, lt, lte, ne, notExists, notInArray, or, sql } from "drizzle-orm";
42
42
  import { PluginToolInputError, createWebSource, eventMatchFieldSchema, eventMatchSchema, eventReachSchema, eventSchema, eventTriggerText, localDestinationSchema, missingToolAnnotationKeys, ownedObjectAnnotationSchema, sourceSchema, stableEventMatchKey, watchResultSchema } from "@sentry/junior-plugin-api";
43
43
  import { alias } from "drizzle-orm/pg-core";
@@ -48,6 +48,7 @@ import http from "node:http";
48
48
  import dns from "node:dns/promises";
49
49
  import net from "node:net";
50
50
  import { SignJWT, importPKCS8 } from "jose";
51
+ import { performance as performance$1 } from "node:perf_hooks";
51
52
  import { createAssistantMessageEventStream, isRetryableAssistantError } from "@earendil-works/pi-ai";
52
53
  import { Bash, defineCommand } from "just-bash";
53
54
  import { structuredPatch } from "diff";
@@ -202,172 +203,6 @@ var SkillSandbox = class {
202
203
  throw new Error("No active skill is loaded. Call loadSkill first or pass skill_name explicitly.");
203
204
  }
204
205
  };
205
- // src/chat/tools/sandbox/file-utils.ts
206
- const MAX_TEXT_CHARS = 6e4;
207
- const RIPGREP_EXCLUDED_GLOBS = ["!**/.git/**", "!**/node_modules/**"];
208
- const DEFAULT_SEARCH_COMMAND_TIMEOUT_MS = 3e4;
209
- const SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
210
- /** Normalize model-supplied numeric knobs before they reach filesystem tools. */
211
- function positiveInteger$1(value) {
212
- if (typeof value !== "number" || !Number.isFinite(value)) return;
213
- const integer = Math.floor(value);
214
- return integer > 0 ? integer : void 0;
215
- }
216
- /** Compare and slice text in one line-ending space while preserving callers' output rules. */
217
- function normalizeToLf$1(value) {
218
- return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
219
- }
220
- /** Keep tool output inside the model-facing budget with an explicit notice. */
221
- function truncateText$1(value, maxChars = MAX_TEXT_CHARS) {
222
- if (value.length <= maxChars) return {
223
- content: value,
224
- truncated: false
225
- };
226
- const removed = value.length - maxChars;
227
- return {
228
- content: `${value.slice(0, maxChars)}\n\n[output truncated: ${removed} characters removed]`,
229
- truncated: true
230
- };
231
- }
232
- /** Detect model-facing missing paths without swallowing sandbox lifecycle/API failures. */
233
- function isMissingPathError(error) {
234
- if (!error || typeof error !== "object") return false;
235
- const candidate = error;
236
- if (candidate.code === "ENOENT") return true;
237
- return typeof candidate.message === "string" && candidate.message.startsWith("File not found:");
238
- }
239
- /** Reject an expected missing search/list path through the tool-error channel. */
240
- function missingPathSearchResult(params) {
241
- const textPath = params.displayPath ?? params.missingPath ?? params.path;
242
- throw new ToolInputError(`Path not found: ${textPath}`);
243
- }
244
- function escapeRegExp(value) {
245
- return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
246
- }
247
- function globToRegExp(pattern) {
248
- let source = "";
249
- for (let index = 0; index < pattern.length; index += 1) {
250
- const char = pattern.charAt(index);
251
- const next = pattern[index + 1];
252
- if (char === "*" && next === "*") {
253
- if (pattern[index + 2] === "/") {
254
- source += "(?:.*/)?";
255
- index += 2;
256
- continue;
257
- }
258
- source += ".*";
259
- index += 1;
260
- continue;
261
- }
262
- if (char === "*") {
263
- source += "[^/]*";
264
- continue;
265
- }
266
- if (char === "?") {
267
- source += "[^/]";
268
- continue;
269
- }
270
- source += escapeRegExp(char);
271
- }
272
- return new RegExp(`^${source}$`);
273
- }
274
- function matchesGlob(relativePath, pattern) {
275
- const matcher = globToRegExp(pattern);
276
- if (matcher.test(relativePath)) return true;
277
- if (pattern.startsWith("**/") && matchesGlob(relativePath, pattern.slice(3))) return true;
278
- return !pattern.includes("/") && matcher.test(path.posix.basename(relativePath));
279
- }
280
- /** Keep sandbox filesystem tools scoped to the mounted workspace root. */
281
- function resolveWorkspacePath(input, fallback = ".") {
282
- const requested = (input ?? "").trim() || fallback;
283
- const absolute = requested.startsWith("/") ? requested : path.posix.join(SANDBOX_WORKSPACE_ROOT, requested);
284
- const normalized = path.posix.normalize(absolute);
285
- if (normalized !== SANDBOX_WORKSPACE_ROOT && !normalized.startsWith(`${SANDBOX_WORKSPACE_ROOT}/`)) throw new ToolInputError(`Path must stay within ${SANDBOX_WORKSPACE_ROOT}: ${requested}`);
286
- return normalized;
287
- }
288
- /** Anchor ripgrep paths and globs to the requested search root. */
289
- function getRipgrepSearchLocation(root, rootIsDirectory) {
290
- if (rootIsDirectory) return {
291
- cwd: root,
292
- target: "."
293
- };
294
- return {
295
- cwd: path.posix.dirname(root),
296
- target: path.posix.basename(root)
297
- };
298
- }
299
- /** Share bounded workspace traversal across search tools so their skip rules stay aligned. */
300
- async function collectFiles(params) {
301
- const files = [];
302
- let limitReached = false;
303
- let missingPath;
304
- const visit = async (dirPath) => {
305
- if (missingPath) return;
306
- let entries;
307
- try {
308
- entries = (await params.fs.readdir(dirPath)).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
309
- } catch (error) {
310
- if (isMissingPathError(error)) {
311
- missingPath = dirPath;
312
- return;
313
- }
314
- throw error;
315
- }
316
- for (const entry of entries) {
317
- const fullPath = path.posix.join(dirPath, entry);
318
- let stat;
319
- try {
320
- stat = await params.fs.stat(fullPath);
321
- } catch (error) {
322
- if (isMissingPathError(error)) {
323
- missingPath = fullPath;
324
- return;
325
- }
326
- throw error;
327
- }
328
- if (stat.isDirectory()) {
329
- if (!SKIPPED_DIRECTORIES.has(entry)) await visit(fullPath);
330
- if (limitReached) return;
331
- continue;
332
- }
333
- const relativePath = path.posix.relative(params.root, fullPath);
334
- if (!params.pattern || matchesGlob(relativePath, params.pattern)) {
335
- files.push(fullPath);
336
- if (params.limit && files.length >= params.limit) {
337
- limitReached = true;
338
- return;
339
- }
340
- }
341
- }
342
- };
343
- let stat;
344
- try {
345
- stat = await params.fs.stat(params.root);
346
- } catch (error) {
347
- if (isMissingPathError(error)) return {
348
- files,
349
- limitReached: false,
350
- missingPath: params.root,
351
- missingRoot: true
352
- };
353
- throw error;
354
- }
355
- if (!stat.isDirectory()) {
356
- const relativePath = path.posix.basename(params.root);
357
- return {
358
- files: !params.pattern || matchesGlob(relativePath, params.pattern) ? [params.root] : [],
359
- limitReached: false,
360
- missingRoot: false
361
- };
362
- }
363
- await visit(params.root);
364
- return {
365
- files,
366
- limitReached,
367
- missingPath,
368
- missingRoot: missingPath === params.root
369
- };
370
- }
371
206
  // src/chat/repository-instructions.ts
372
207
  const AGENTS_FILENAME = "AGENTS.md";
373
208
  const MAX_REPOSITORY_INSTRUCTIONS_BYTES = 32768;
@@ -382,81 +217,207 @@ const REPOSITORY_SKILLS_HEADER = [
382
217
  ].join("\n");
383
218
  const AGENTS_REPLACEMENT_NOTICE = "These AGENTS.md instructions replace all previously provided AGENTS.md instructions.";
384
219
  const AGENTS_REMOVAL_NOTICE = "The previously provided AGENTS.md instructions no longer apply.";
385
- async function pathExists(fs, target) {
386
- try {
387
- await fs.stat(target);
388
- return true;
389
- } catch (error) {
390
- if (isMissingPathError(error)) return false;
391
- throw error;
392
- }
220
+ /**
221
+ * Read limit for each AGENTS.md and SKILL.md file. It is 3 bytes past the
222
+ * budget: a UTF-8 character has at most 4 bytes, so a character cut at this
223
+ * limit starts after the budget, and the budgeted text is the same as with a
224
+ * full read.
225
+ */
226
+ const FILE_READ_LIMIT_BYTES = 32771;
227
+ /**
228
+ * Print every path and file that the instructions read needs, one record per
229
+ * line. The cwd is the workspace root, and all paths are relative to it.
230
+ *
231
+ * Arguments: the file read limit, then `repos` to read every Git worktree in
232
+ * it or "", then one group for each selected directory. A group holds the
233
+ * directories from the selected directory up to the workspace root, then "".
234
+ * A group stops at its first directory with `.git`.
235
+ *
236
+ * Records: `R <dir>` for a Git worktree in repos/, `G <dir>/.git`, `D <path>`
237
+ * for a directory in a skill root, `E <path>` for another entry in a skill
238
+ * root, and `F <base64> <path>` for an AGENTS.md or SKILL.md file. File
239
+ * tests follow symlinks like `stat -L`. Globs skip dot entries like `ls -1`,
240
+ * which the Vercel Sandbox `readdir` runs.
241
+ */
242
+ const READ_SCRIPT = `
243
+ limit=$1
244
+ repos=$2
245
+ shift 2
246
+
247
+ nl='
248
+ '
249
+ # A glob result is an entry when it exists. Like the lines of \`ls -1\`, a
250
+ # name with a line break does not name an entry.
251
+ listed() {
252
+ case $1 in *"$nl"*) return 1 ;; esac
253
+ [ -e "$1" ] || [ -L "$1" ]
393
254
  }
394
- async function directoryExists(fs, target) {
395
- try {
396
- return (await fs.stat(target)).isDirectory();
397
- } catch (error) {
398
- if (isMissingPathError(error)) return false;
399
- throw error;
400
- }
255
+
256
+ emit_file() {
257
+ [ -f "$1" ] || return 0
258
+ if [ ! -r "$1" ]; then
259
+ printf 'cannot read %s\\n' "$1" >&2
260
+ exit 1
261
+ fi
262
+ printf 'F '
263
+ head -c "$limit" < "$1" | base64 | tr -d '\\n'
264
+ printf ' %s\\n' "$1"
265
+ }
266
+
267
+ emit_skills() {
268
+ for entry in "$1/.agents/skills"/* "$1/.claude/skills"/*; do
269
+ listed "$entry" || continue
270
+ # The first skill root wins for each name. Skip entries that it shadows.
271
+ case $entry in
272
+ "$1/.claude/skills/"*) listed "$1/.agents/skills/\${entry##*/}" && continue ;;
273
+ esac
274
+ if [ -d "$entry" ]; then
275
+ printf 'D %s\\n' "$entry"
276
+ emit_file "$entry/SKILL.md"
277
+ else
278
+ printf 'E %s\\n' "$entry"
279
+ fi
280
+ done
401
281
  }
402
- async function findGitRoot(fs, cwd) {
282
+
283
+ if [ -n "$repos" ]; then
284
+ for repo in "$repos"/*; do
285
+ if listed "$repo" && [ -d "$repo" ] && [ -e "$repo/.git" ]; then
286
+ printf 'R %s\\n' "$repo"
287
+ set -- "$@" "$repo" ""
288
+ fi
289
+ done
290
+ fi
291
+
292
+ found=
293
+ for dir in "$@"; do
294
+ if [ -z "$dir" ]; then
295
+ found=
296
+ elif [ -z "$found" ]; then
297
+ emit_file "$dir/AGENTS.md"
298
+ if [ -e "$dir/.git" ]; then
299
+ printf 'G %s/.git\\n' "$dir"
300
+ emit_skills "$dir"
301
+ found=1
302
+ fi
303
+ fi
304
+ done
305
+ `;
306
+ const BASE64_PATTERN = /^[A-Za-z0-9+/]*={0,2}$/;
307
+ function compareStrings(left, right) {
308
+ return left < right ? -1 : left > right ? 1 : 0;
309
+ }
310
+ /** Directories that the Git root search checks, from cwd up to the workspace root. */
311
+ function gitRootCandidates(cwd) {
312
+ const candidates = [];
403
313
  let current = cwd;
404
314
  for (;;) {
405
- if (await pathExists(fs, path.posix.join(current, ".git"))) return current;
406
- if (current === SANDBOX_WORKSPACE_ROOT) return;
315
+ candidates.push(current);
316
+ if (current === SANDBOX_WORKSPACE_ROOT) return candidates;
407
317
  const parent = path.posix.dirname(current);
408
- if (parent === current || parent !== SANDBOX_WORKSPACE_ROOT && !parent.startsWith(`${SANDBOX_WORKSPACE_ROOT}/`)) return;
318
+ if (parent === current || parent !== SANDBOX_WORKSPACE_ROOT && !parent.startsWith(`${SANDBOX_WORKSPACE_ROOT}/`)) return candidates;
409
319
  current = parent;
410
320
  }
411
321
  }
412
- /** Return Git worktrees under repos/, sorted for stable instruction capture. */
413
- async function listRepositoryDirectories(fs) {
414
- if (!await directoryExists(fs, SANDBOX_REPOS_ROOT)) return [];
415
- const entries = await fs.readdir(SANDBOX_REPOS_ROOT);
416
- const repositories = [];
417
- for (const entry of entries) {
418
- const candidate = path.posix.join(SANDBOX_REPOS_ROOT, entry);
419
- if (await directoryExists(fs, candidate) && await pathExists(fs, path.posix.join(candidate, ".git"))) repositories.push(candidate);
322
+ function findGitRoot(tree, cwd) {
323
+ return gitRootCandidates(cwd).find((directory) => tree.gitMarkers.has(path.posix.join(directory, ".git")));
324
+ }
325
+ function invalidOutput() {
326
+ return /* @__PURE__ */ new Error("Repository instructions read printed invalid output");
327
+ }
328
+ function parseRepositoryTree(stdout) {
329
+ const tree = {
330
+ entries: /* @__PURE__ */ new Map(),
331
+ files: /* @__PURE__ */ new Map(),
332
+ gitMarkers: /* @__PURE__ */ new Set(),
333
+ repositories: []
334
+ };
335
+ const lines = stdout.split("\n");
336
+ if (lines.pop() !== "") throw invalidOutput();
337
+ for (const line of lines) {
338
+ const kind = line.slice(0, 2);
339
+ let target = line.slice(2);
340
+ let content = "";
341
+ if (kind === "F ") {
342
+ const separator = target.indexOf(" ");
343
+ if (separator < 0) throw invalidOutput();
344
+ content = target.slice(0, separator);
345
+ target = target.slice(separator + 1);
346
+ if (!BASE64_PATTERN.test(content)) throw invalidOutput();
347
+ }
348
+ if (!target) throw invalidOutput();
349
+ const absolute = path.posix.join(SANDBOX_WORKSPACE_ROOT, target);
350
+ switch (kind) {
351
+ case "R ":
352
+ tree.repositories.push(absolute);
353
+ break;
354
+ case "G ":
355
+ tree.gitMarkers.add(absolute);
356
+ break;
357
+ case "D ":
358
+ case "E ": {
359
+ const root = path.posix.dirname(absolute);
360
+ const entries = tree.entries.get(root) ?? /* @__PURE__ */ new Map();
361
+ entries.set(path.posix.basename(absolute), kind === "D ");
362
+ tree.entries.set(root, entries);
363
+ break;
364
+ }
365
+ case "F ":
366
+ tree.files.set(absolute, Buffer.from(content, "base64").toString("utf8"));
367
+ break;
368
+ default: throw invalidOutput();
369
+ }
420
370
  }
421
- return repositories.sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
371
+ tree.repositories.sort(compareStrings);
372
+ return tree;
422
373
  }
423
- async function readInstructionFile(fs, filePath) {
424
- try {
425
- const content = await fs.readFile(filePath, { encoding: "utf8" });
426
- return content.trim() ? content : void 0;
427
- } catch (error) {
428
- if (isMissingPathError(error)) return;
429
- throw error;
430
- }
374
+ /** Read what the selected directories need with one sandbox command. */
375
+ async function readRepositoryTree(args) {
376
+ const relative = (target) => path.posix.relative(SANDBOX_WORKSPACE_ROOT, target) || ".";
377
+ const result = await args.runCommand({
378
+ cmd: "sh",
379
+ args: [
380
+ "-c",
381
+ READ_SCRIPT,
382
+ "sh",
383
+ String(FILE_READ_LIMIT_BYTES),
384
+ args.discover ? relative(SANDBOX_REPOS_ROOT) : "",
385
+ ...args.directories.flatMap((directory) => [...gitRootCandidates(directory).map(relative), ""])
386
+ ],
387
+ cwd: SANDBOX_WORKSPACE_ROOT
388
+ });
389
+ if (result.exitCode !== 0) throw new Error(`Repository instructions read failed with exit code ${result.exitCode}: ${result.stderr.trim()}`);
390
+ return parseRepositoryTree(result.stdout);
431
391
  }
432
- async function readOptionalDirectory(fs, directory) {
433
- try {
434
- return await fs.readdir(directory);
435
- } catch (error) {
436
- if (isMissingPathError(error)) return [];
437
- throw error;
438
- }
392
+ function readInstructionFile(tree, filePath) {
393
+ const content = tree.files.get(filePath);
394
+ return content?.trim() ? content : void 0;
439
395
  }
440
396
  /** Read model-invocable skills from the repository skill directories. */
441
- async function readRepositorySkills(fs, gitRoot) {
442
- const roots = REPOSITORY_SKILL_ROOTS.map((root) => path.posix.join(gitRoot, root));
443
- const listings = await Promise.all(roots.map(async (root) => await readOptionalDirectory(fs, root)));
397
+ function readRepositorySkills(tree, gitRoot) {
444
398
  const candidates = /* @__PURE__ */ new Map();
445
- listings.forEach((entries, index) => {
446
- for (const entry of [...entries].sort()) if (!candidates.has(entry)) candidates.set(entry, path.posix.join(roots[index], entry));
447
- });
448
- return (await Promise.all([...candidates].map(async ([name, skillDirectory]) => {
449
- if (!await directoryExists(fs, skillDirectory)) return;
450
- const filePath = path.posix.join(skillDirectory, "SKILL.md");
451
- const raw = await readInstructionFile(fs, filePath);
399
+ for (const root of REPOSITORY_SKILL_ROOTS) {
400
+ const rootPath = path.posix.join(gitRoot, root);
401
+ const entries = [...tree.entries.get(rootPath) ?? []].sort(([left], [right]) => compareStrings(left, right));
402
+ for (const [name, directory] of entries) if (!candidates.has(name)) candidates.set(name, {
403
+ directory,
404
+ path: path.posix.join(rootPath, name)
405
+ });
406
+ }
407
+ const skills = [];
408
+ for (const [name, candidate] of candidates) {
409
+ if (!candidate.directory) continue;
410
+ const filePath = path.posix.join(candidate.path, "SKILL.md");
411
+ const raw = readInstructionFile(tree, filePath);
452
412
  const parsed = raw ? parseSkillFile(raw, name) : void 0;
453
- if (!parsed?.ok || parsed.skill.disableModelInvocation) return;
454
- return {
413
+ if (!parsed?.ok || parsed.skill.disableModelInvocation) continue;
414
+ skills.push({
455
415
  description: parsed.skill.description.replace(/\s+/g, " ").trim(),
456
416
  name,
457
417
  path: filePath
458
- };
459
- }))).filter((skill) => Boolean(skill)).sort((left, right) => left.name < right.name ? -1 : left.name > right.name ? 1 : 0);
418
+ });
419
+ }
420
+ return skills.sort((left, right) => compareStrings(left.name, right.name));
460
421
  }
461
422
  function sourceBytes(sources) {
462
423
  return sources.reduce((total, source) => total + Buffer.byteLength(source.content, "utf8"), 0);
@@ -465,7 +426,7 @@ function skillListBytes(skills) {
465
426
  return skills.reduce((total, skill) => total + repositorySkillBytes(skill), 0);
466
427
  }
467
428
  /** Read root-to-cwd AGENTS.md files within the byte budget. */
468
- async function readAgentsSources(args) {
429
+ function readAgentsSources(args) {
469
430
  const directories = [];
470
431
  let current = args.cwd;
471
432
  for (;;) {
@@ -479,7 +440,7 @@ async function readAgentsSources(args) {
479
440
  for (const directory of directories) {
480
441
  if (remaining <= 0) break;
481
442
  const filePath = path.posix.join(directory, AGENTS_FILENAME);
482
- const content = await readInstructionFile(args.fs, filePath);
443
+ const content = readInstructionFile(args.tree, filePath);
483
444
  if (!content) continue;
484
445
  const bytes = Buffer.from(content, "utf8");
485
446
  const bounded = bytes.length <= remaining ? content : bytes.subarray(0, remaining).toString("utf8");
@@ -492,11 +453,11 @@ async function readAgentsSources(args) {
492
453
  return sources;
493
454
  }
494
455
  /** Read repository skills that fit the byte budget, keeping each entry whole. */
495
- async function readRepositorySkillsWithinBudget(args) {
456
+ function readRepositorySkillsWithinBudget(args) {
496
457
  const skills = [];
497
458
  let remaining = args.maxBytes;
498
459
  if (remaining <= 0) return skills;
499
- for (const skill of await readRepositorySkills(args.fs, args.gitRoot)) {
460
+ for (const skill of readRepositorySkills(args.tree, args.gitRoot)) {
500
461
  const bytes = repositorySkillBytes(skill);
501
462
  if (bytes > remaining) break;
502
463
  skills.push(skill);
@@ -556,19 +517,33 @@ function mergeRepositoryInstructions(bundles) {
556
517
  text
557
518
  };
558
519
  }
559
- /** Resolve AGENTS.md instructions for each selected repository directory. */
560
- async function resolveRepositoryInstructionsForDirectories(args) {
561
- const uniqueDirectories = [...new Set(args.directories.filter((directory) => directory === SANDBOX_WORKSPACE_ROOT || directory.startsWith(`${SANDBOX_WORKSPACE_ROOT}/`)))].sort((left, right) => left < right ? -1 : left > right ? 1 : 0);
520
+ /**
521
+ * Read AGENTS.md instructions and repository skills for the selected
522
+ * directories with one sandbox command. Without directories, use every Git
523
+ * worktree under repos/.
524
+ */
525
+ async function readRepositoryInstructions(args) {
526
+ const selected = [...new Set((args.directories ?? []).filter((directory) => directory === SANDBOX_WORKSPACE_ROOT || directory.startsWith(`${SANDBOX_WORKSPACE_ROOT}/`)))].sort(compareStrings);
527
+ if (args.directories && selected.length === 0) return {
528
+ directories: args.directories,
529
+ instructions: void 0
530
+ };
531
+ const tree = await readRepositoryTree({
532
+ directories: selected,
533
+ discover: !args.directories,
534
+ runCommand: args.runCommand
535
+ });
536
+ const cwds = args.directories ? selected : tree.repositories;
562
537
  let remaining = MAX_REPOSITORY_INSTRUCTIONS_BYTES;
563
538
  const repositories = [];
564
- for (const directory of uniqueDirectories) {
565
- const gitRoot = await findGitRoot(args.fs, directory);
539
+ for (const directory of cwds) {
540
+ const gitRoot = findGitRoot(tree, directory);
566
541
  if (!gitRoot) continue;
567
- const sources = await readAgentsSources({
542
+ const sources = readAgentsSources({
568
543
  cwd: directory,
569
- fs: args.fs,
570
544
  gitRoot,
571
- maxBytes: remaining
545
+ maxBytes: remaining,
546
+ tree
572
547
  });
573
548
  remaining -= sourceBytes(sources);
574
549
  repositories.push({
@@ -579,16 +554,19 @@ async function resolveRepositoryInstructionsForDirectories(args) {
579
554
  }
580
555
  const bundles = [];
581
556
  for (const repository of repositories) {
582
- const skills = await readRepositorySkillsWithinBudget({
583
- fs: args.fs,
557
+ const skills = readRepositorySkillsWithinBudget({
584
558
  gitRoot: repository.gitRoot,
585
- maxBytes: remaining
559
+ maxBytes: remaining,
560
+ tree
586
561
  });
587
562
  remaining -= skillListBytes(skills);
588
563
  const bundle = buildRepositoryInstructions(repository.directory, repository.sources, skills);
589
564
  if (bundle) bundles.push(bundle);
590
565
  }
591
- return mergeRepositoryInstructions(bundles);
566
+ return {
567
+ directories: args.directories ?? cwds,
568
+ instructions: mergeRepositoryInstructions(bundles)
569
+ };
592
570
  }
593
571
  /** Render repository instructions with Codex's exact model-visible wrapper. */
594
572
  function renderAgentsInstructions(args) {
@@ -725,6 +703,7 @@ function buildChatEndAttributes(message, mode) {
725
703
  Object.assign(attributes, extractGenAiUsageAttributes(message));
726
704
  if (message.stopReason) attributes["gen_ai.response.finish_reasons"] = [normalizeGenAiFinishReason(message.stopReason)];
727
705
  if (message.model) attributes["gen_ai.response.model"] = message.model;
706
+ if (message.responseId) attributes["gen_ai.response.id"] = message.responseId;
728
707
  if (message.stopReason === "error") {
729
708
  const providerError = createProviderError(message.errorMessage ?? "");
730
709
  Object.assign(attributes, getProviderErrorAttributes(providerError));
@@ -761,6 +740,22 @@ function createResponseTiming(requestStartedAtMs) {
761
740
  }
762
741
  };
763
742
  }
743
+ function traceResponseHeaders(span, options) {
744
+ const eventLoopStart = performance$1.eventLoopUtilization();
745
+ const onResponse = options?.onResponse;
746
+ return {
747
+ ...options,
748
+ onResponse: async (response, model) => {
749
+ try {
750
+ const vercelId = response.headers["x-vercel-id"];
751
+ if (vercelId) span.setAttribute("app.ai.gateway.vercel_id", vercelId);
752
+ const { utilization } = performance$1.eventLoopUtilization(eventLoopStart);
753
+ span.setAttribute("app.ai.event_loop.utilization", Math.round(utilization * 1e3) / 1e3);
754
+ } catch {}
755
+ await onResponse?.(response, model);
756
+ }
757
+ };
758
+ }
764
759
  function relayStream(source, onEvent) {
765
760
  const relayed = createAssistantMessageEventStream();
766
761
  const finished = (async () => {
@@ -801,7 +796,7 @@ function createTracedStreamFn(baseOrOptions = streamSimple) {
801
796
  });
802
797
  try {
803
798
  const requestStartedAtMs = Date.now();
804
- const stream = await withActiveSpan(span, () => Promise.resolve(base(model, context, options)));
799
+ const stream = await withActiveSpan(span, () => Promise.resolve(base(model, context, traceResponseHeaders(span, options))));
805
800
  const responseTiming = createResponseTiming(requestStartedAtMs);
806
801
  const relay = relayStream(stream, responseTiming.observe);
807
802
  relay.finished.then(() => stream.result()).then((finalMessage) => {
@@ -3961,9 +3956,10 @@ function createSandboxRuntime(options) {
3961
3956
  withSpan: withSandboxSpan
3962
3957
  });
3963
3958
  };
3959
+ const ensureDocker = async (targetSandbox) => await withSandboxSpan("sandbox.docker.ensure", "sandbox.docker.ensure", {}, async () => await ensureDockerDaemon(targetSandbox));
3964
3960
  const prepareSandbox = async (targetSandbox) => {
3965
3961
  await syncSkills(targetSandbox);
3966
- await withSandboxSpan("sandbox.docker.ensure", "sandbox.docker.ensure", {}, async () => await ensureDockerDaemon(targetSandbox));
3962
+ await ensureDocker(targetSandbox);
3967
3963
  await options.onSandboxPrepare?.(targetSandbox);
3968
3964
  };
3969
3965
  const applyNetworkPolicy = async (targetSandbox, traceHeaders) => {
@@ -4187,10 +4183,17 @@ function createSandboxRuntime(options) {
4187
4183
  }
4188
4184
  throw new Error("sandbox restore failed", { cause: error });
4189
4185
  }
4190
- let networkPolicyKey;
4191
4186
  try {
4192
- networkPolicyKey = await applyNetworkPolicy(hintedSandbox);
4193
- await prepareSandbox(hintedSandbox);
4187
+ const steps = await Promise.allSettled([
4188
+ applyNetworkPolicy(hintedSandbox),
4189
+ syncSkills(hintedSandbox),
4190
+ ensureDocker(hintedSandbox)
4191
+ ]);
4192
+ const failures = steps.flatMap((step) => step.status === "rejected" ? [step.reason] : []);
4193
+ if (failures.length > 0) throw failures.find((error) => getSandboxProvider().errors.isUnavailable(error)) ?? failures[0];
4194
+ const [policy] = steps;
4195
+ const networkPolicyKey = policy.status === "fulfilled" ? policy.value : void 0;
4196
+ await options.onSandboxPrepare?.(hintedSandbox);
4194
4197
  dependencyProfileHash = ref.profileHash ?? dependencyProfileHash;
4195
4198
  await persistSandboxRef({
4196
4199
  ...ref,
@@ -4212,17 +4215,26 @@ function createSandboxRuntime(options) {
4212
4215
  const cachedSandbox = await tryReuseCachedSandbox();
4213
4216
  if (cachedSandbox) {
4214
4217
  setSpanAttributes({ "app.sandbox.acquire.outcome": "memory" });
4215
- return cachedSandbox;
4218
+ return {
4219
+ session: cachedSandbox,
4220
+ fresh: false
4221
+ };
4216
4222
  }
4217
4223
  signal?.throwIfAborted();
4218
4224
  const hintedSandbox = await tryRestoreHintedSandbox(signal);
4219
4225
  if (hintedSandbox) {
4220
4226
  setSpanAttributes({ "app.sandbox.acquire.outcome": "resumed" });
4221
- return hintedSandbox;
4227
+ return {
4228
+ session: hintedSandbox,
4229
+ fresh: true
4230
+ };
4222
4231
  }
4223
4232
  const createdSandbox = await createFreshSandbox(signal);
4224
4233
  setSpanAttributes({ "app.sandbox.acquire.outcome": "created" });
4225
- return createdSandbox;
4234
+ return {
4235
+ session: createdSandbox,
4236
+ fresh: true
4237
+ };
4226
4238
  });
4227
4239
  };
4228
4240
  const getOrAcquireSandbox = async (signal) => {
@@ -4378,10 +4390,12 @@ function createSandboxRuntime(options) {
4378
4390
  };
4379
4391
  };
4380
4392
  const ensureReadySandbox = async (signal) => {
4381
- const session = await getOrAcquireSandbox(signal);
4382
- signal?.throwIfAborted();
4383
- await probeSession(session);
4393
+ const { session, fresh } = await getOrAcquireSandbox(signal);
4384
4394
  signal?.throwIfAborted();
4395
+ if (!fresh) {
4396
+ await probeSession(session);
4397
+ signal?.throwIfAborted();
4398
+ }
4385
4399
  await extendKeepAlive(session);
4386
4400
  startKeepAlive(session);
4387
4401
  return session;
@@ -4428,7 +4442,7 @@ function createSandboxRuntime(options) {
4428
4442
  await stopSession(previous?.session);
4429
4443
  },
4430
4444
  async acquire(signal) {
4431
- return await getOrAcquireSandbox(signal);
4445
+ return (await getOrAcquireSandbox(signal)).session;
4432
4446
  },
4433
4447
  async tools(signal) {
4434
4448
  return createToolExecutors(await ensureReadySandbox(signal));
@@ -4447,6 +4461,172 @@ function createSandboxRuntime(options) {
4447
4461
  }
4448
4462
  };
4449
4463
  }
4464
+ // src/chat/tools/sandbox/file-utils.ts
4465
+ const MAX_TEXT_CHARS = 6e4;
4466
+ const RIPGREP_EXCLUDED_GLOBS = ["!**/.git/**", "!**/node_modules/**"];
4467
+ const DEFAULT_SEARCH_COMMAND_TIMEOUT_MS = 3e4;
4468
+ const SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
4469
+ /** Normalize model-supplied numeric knobs before they reach filesystem tools. */
4470
+ function positiveInteger$1(value) {
4471
+ if (typeof value !== "number" || !Number.isFinite(value)) return;
4472
+ const integer = Math.floor(value);
4473
+ return integer > 0 ? integer : void 0;
4474
+ }
4475
+ /** Compare and slice text in one line-ending space while preserving callers' output rules. */
4476
+ function normalizeToLf$1(value) {
4477
+ return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
4478
+ }
4479
+ /** Keep tool output inside the model-facing budget with an explicit notice. */
4480
+ function truncateText$1(value, maxChars = MAX_TEXT_CHARS) {
4481
+ if (value.length <= maxChars) return {
4482
+ content: value,
4483
+ truncated: false
4484
+ };
4485
+ const removed = value.length - maxChars;
4486
+ return {
4487
+ content: `${value.slice(0, maxChars)}\n\n[output truncated: ${removed} characters removed]`,
4488
+ truncated: true
4489
+ };
4490
+ }
4491
+ /** Detect model-facing missing paths without swallowing sandbox lifecycle/API failures. */
4492
+ function isMissingPathError(error) {
4493
+ if (!error || typeof error !== "object") return false;
4494
+ const candidate = error;
4495
+ if (candidate.code === "ENOENT") return true;
4496
+ return typeof candidate.message === "string" && candidate.message.startsWith("File not found:");
4497
+ }
4498
+ /** Reject an expected missing search/list path through the tool-error channel. */
4499
+ function missingPathSearchResult(params) {
4500
+ const textPath = params.displayPath ?? params.missingPath ?? params.path;
4501
+ throw new ToolInputError(`Path not found: ${textPath}`);
4502
+ }
4503
+ function escapeRegExp(value) {
4504
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
4505
+ }
4506
+ function globToRegExp(pattern) {
4507
+ let source = "";
4508
+ for (let index = 0; index < pattern.length; index += 1) {
4509
+ const char = pattern.charAt(index);
4510
+ const next = pattern[index + 1];
4511
+ if (char === "*" && next === "*") {
4512
+ if (pattern[index + 2] === "/") {
4513
+ source += "(?:.*/)?";
4514
+ index += 2;
4515
+ continue;
4516
+ }
4517
+ source += ".*";
4518
+ index += 1;
4519
+ continue;
4520
+ }
4521
+ if (char === "*") {
4522
+ source += "[^/]*";
4523
+ continue;
4524
+ }
4525
+ if (char === "?") {
4526
+ source += "[^/]";
4527
+ continue;
4528
+ }
4529
+ source += escapeRegExp(char);
4530
+ }
4531
+ return new RegExp(`^${source}$`);
4532
+ }
4533
+ function matchesGlob(relativePath, pattern) {
4534
+ const matcher = globToRegExp(pattern);
4535
+ if (matcher.test(relativePath)) return true;
4536
+ if (pattern.startsWith("**/") && matchesGlob(relativePath, pattern.slice(3))) return true;
4537
+ return !pattern.includes("/") && matcher.test(path.posix.basename(relativePath));
4538
+ }
4539
+ /** Keep sandbox filesystem tools scoped to the mounted workspace root. */
4540
+ function resolveWorkspacePath(input, fallback = ".") {
4541
+ const requested = (input ?? "").trim() || fallback;
4542
+ const absolute = requested.startsWith("/") ? requested : path.posix.join(SANDBOX_WORKSPACE_ROOT, requested);
4543
+ const normalized = path.posix.normalize(absolute);
4544
+ if (normalized !== SANDBOX_WORKSPACE_ROOT && !normalized.startsWith(`${SANDBOX_WORKSPACE_ROOT}/`)) throw new ToolInputError(`Path must stay within ${SANDBOX_WORKSPACE_ROOT}: ${requested}`);
4545
+ return normalized;
4546
+ }
4547
+ /** Anchor ripgrep paths and globs to the requested search root. */
4548
+ function getRipgrepSearchLocation(root, rootIsDirectory) {
4549
+ if (rootIsDirectory) return {
4550
+ cwd: root,
4551
+ target: "."
4552
+ };
4553
+ return {
4554
+ cwd: path.posix.dirname(root),
4555
+ target: path.posix.basename(root)
4556
+ };
4557
+ }
4558
+ /** Share bounded workspace traversal across search tools so their skip rules stay aligned. */
4559
+ async function collectFiles(params) {
4560
+ const files = [];
4561
+ let limitReached = false;
4562
+ let missingPath;
4563
+ const visit = async (dirPath) => {
4564
+ if (missingPath) return;
4565
+ let entries;
4566
+ try {
4567
+ entries = (await params.fs.readdir(dirPath)).sort((a, b) => a.toLowerCase().localeCompare(b.toLowerCase()));
4568
+ } catch (error) {
4569
+ if (isMissingPathError(error)) {
4570
+ missingPath = dirPath;
4571
+ return;
4572
+ }
4573
+ throw error;
4574
+ }
4575
+ for (const entry of entries) {
4576
+ const fullPath = path.posix.join(dirPath, entry);
4577
+ let stat;
4578
+ try {
4579
+ stat = await params.fs.stat(fullPath);
4580
+ } catch (error) {
4581
+ if (isMissingPathError(error)) {
4582
+ missingPath = fullPath;
4583
+ return;
4584
+ }
4585
+ throw error;
4586
+ }
4587
+ if (stat.isDirectory()) {
4588
+ if (!SKIPPED_DIRECTORIES.has(entry)) await visit(fullPath);
4589
+ if (limitReached) return;
4590
+ continue;
4591
+ }
4592
+ const relativePath = path.posix.relative(params.root, fullPath);
4593
+ if (!params.pattern || matchesGlob(relativePath, params.pattern)) {
4594
+ files.push(fullPath);
4595
+ if (params.limit && files.length >= params.limit) {
4596
+ limitReached = true;
4597
+ return;
4598
+ }
4599
+ }
4600
+ }
4601
+ };
4602
+ let stat;
4603
+ try {
4604
+ stat = await params.fs.stat(params.root);
4605
+ } catch (error) {
4606
+ if (isMissingPathError(error)) return {
4607
+ files,
4608
+ limitReached: false,
4609
+ missingPath: params.root,
4610
+ missingRoot: true
4611
+ };
4612
+ throw error;
4613
+ }
4614
+ if (!stat.isDirectory()) {
4615
+ const relativePath = path.posix.basename(params.root);
4616
+ return {
4617
+ files: !params.pattern || matchesGlob(relativePath, params.pattern) ? [params.root] : [],
4618
+ limitReached: false,
4619
+ missingRoot: false
4620
+ };
4621
+ }
4622
+ await visit(params.root);
4623
+ return {
4624
+ files,
4625
+ limitReached,
4626
+ missingPath,
4627
+ missingRoot: missingPath === params.root
4628
+ };
4629
+ }
4450
4630
  // src/chat/tools/sandbox/text-edits.ts
4451
4631
  const MAX_DIFF_LINE_CHARS = 4e3;
4452
4632
  /** Preserve a text artifact's dominant line-ending style across rewrites. */
@@ -5706,13 +5886,12 @@ function createSandbox(options) {
5706
5886
  return {
5707
5887
  async captureRepositoryInstructions() {
5708
5888
  if (!runtime.sandboxRef()) return;
5709
- const { fs } = await runtime.tools();
5889
+ const { runCommand } = await runtime.tools();
5710
5890
  const workspaceDirectories = activeWorkspace?.repos.map((repo) => tryWorkspaceRepoCheckoutPath(repo.repo)).filter((checkoutPath) => Boolean(checkoutPath)).map((checkoutPath) => `${SANDBOX_WORKSPACE_ROOT}/${checkoutPath}`) ?? [];
5711
5891
  return await withSandboxSpan("sandbox.repository_instructions.read", "sandbox.repository_instructions.read", {}, async (setSpanAttributes) => {
5712
- const directories = workspaceDirectories.length > 0 ? workspaceDirectories : await listRepositoryDirectories(fs);
5713
- const instructions = await resolveRepositoryInstructionsForDirectories({
5714
- directories,
5715
- fs
5892
+ const { directories, instructions } = await readRepositoryInstructions({
5893
+ ...workspaceDirectories.length > 0 ? { directories: workspaceDirectories } : void 0,
5894
+ runCommand
5716
5895
  });
5717
5896
  setSpanAttributes({
5718
5897
  "app.repository_instructions.repo_count": directories.length,
@@ -16717,6 +16896,12 @@ function createToolActionReview(options) {
16717
16896
  const pendingRejections = /* @__PURE__ */ new Map();
16718
16897
  let consecutiveRejections = 0;
16719
16898
  /**
16899
+ * Rejection state of each attempt, so an attempt counts only once. Pi runs
16900
+ * a `codemode` call alone, so no other attempt changes the count while
16901
+ * its batches run.
16902
+ */
16903
+ const attempts = /* @__PURE__ */ new Map();
16904
+ /**
16720
16905
  * Review one action and record its decision. Gate state does not change
16721
16906
  * here, so concurrent reviews in one batch cannot corrupt it.
16722
16907
  */
@@ -16743,8 +16928,50 @@ function createToolActionReview(options) {
16743
16928
  };
16744
16929
  }
16745
16930
  };
16931
+ /**
16932
+ * Count each attempt in the batch once, in batch order. An attempt with a
16933
+ * rejection adds one to the count from before the attempt, also when it
16934
+ * has allowed actions or spans several batches. An attempt with only
16935
+ * allowed actions resets the count. Skipped and unavailable actions do not
16936
+ * change it. Return the attempt state of each action.
16937
+ */
16938
+ const countAttempts = (actions, steps) => {
16939
+ const states = actions.map((action) => {
16940
+ const id = action.attemptId ?? action.toolCallId;
16941
+ let state = attempts.get(id);
16942
+ if (!state) {
16943
+ state = {
16944
+ before: consecutiveRejections,
16945
+ exhausted: false,
16946
+ rejected: false
16947
+ };
16948
+ attempts.set(id, state);
16949
+ }
16950
+ return state;
16951
+ });
16952
+ states.forEach((state, index) => {
16953
+ const step = steps[index];
16954
+ if (step.kind !== "reviewed" || state.rejected) return;
16955
+ if (step.decision.decision === "allow") {
16956
+ consecutiveRejections = 0;
16957
+ return;
16958
+ }
16959
+ state.rejected = true;
16960
+ consecutiveRejections = state.before + 1;
16961
+ if (consecutiveRejections < MAX_CONSECUTIVE_REJECTIONS) return;
16962
+ state.exhausted = true;
16963
+ logException(new ToolActionReviewLimitError(), "guardian.action_review.exhausted", {
16964
+ "app.guardian.decision": step.decision.decision,
16965
+ "app.guardian.risk_level": step.decision.riskLevel,
16966
+ "app.guardian.user_authorization": step.decision.userAuthorization,
16967
+ "gen_ai.tool.name": step.proposal.tool.name,
16968
+ "gen_ai.tool.call.id": actions[index].toolCallId
16969
+ });
16970
+ });
16971
+ return states;
16972
+ };
16746
16973
  /** Apply one review step to gate state and return the action's result. */
16747
- const settle = (toolCallId, step) => {
16974
+ const settle = (toolCallId, step, exhausted) => {
16748
16975
  if (step.kind === "skipped") return { allowed: true };
16749
16976
  if (step.kind === "unavailable") {
16750
16977
  options.onFatal(step.error);
@@ -16754,24 +16981,12 @@ function createToolActionReview(options) {
16754
16981
  };
16755
16982
  }
16756
16983
  const { decision, proposal } = step;
16757
- if (decision.decision === "allow") {
16758
- consecutiveRejections = 0;
16759
- return {
16760
- allowed: true,
16761
- decision
16762
- };
16763
- }
16984
+ if (decision.decision === "allow") return {
16985
+ allowed: true,
16986
+ decision
16987
+ };
16764
16988
  const reviewedAction = projectedRejection(proposal, decision);
16765
16989
  addPriorRejection(priorRejections, reviewedAction);
16766
- consecutiveRejections += 1;
16767
- const exhausted = consecutiveRejections >= MAX_CONSECUTIVE_REJECTIONS;
16768
- if (exhausted) logException(new ToolActionReviewLimitError(), "guardian.action_review.exhausted", {
16769
- "app.guardian.decision": decision.decision,
16770
- "app.guardian.risk_level": decision.riskLevel,
16771
- "app.guardian.user_authorization": decision.userAuthorization,
16772
- "gen_ai.tool.name": proposal.tool.name,
16773
- "gen_ai.tool.call.id": toolCallId
16774
- });
16775
16990
  pendingRejections.set(toolCallId, {
16776
16991
  decision: decision.decision,
16777
16992
  priorRejection: reviewedAction,
@@ -16798,7 +17013,8 @@ function createToolActionReview(options) {
16798
17013
  return await withSpan("guardian.action_review", "guardian.action_review", {}, async () => {
16799
17014
  const rejections = [...priorRejections];
16800
17015
  const steps = await mapConcurrent(actions, MAX_CONCURRENT_REVIEWS, (action) => reviewStep(action, rejections, signal));
16801
- return new Map(actions.map((action, index) => [action.toolCallId, settle(action.toolCallId, steps[index])]));
17016
+ const states = countAttempts(actions, steps);
17017
+ return new Map(actions.map((action, index) => [action.toolCallId, settle(action.toolCallId, steps[index], states[index].exhausted)]));
16802
17018
  }, { "app.guardian.batch.size": actions.length });
16803
17019
  },
16804
17020
  projectToolResult(toolCallId, result) {
@@ -17506,6 +17722,7 @@ async function runCodeModeScript(run) {
17506
17722
  });
17507
17723
  return await run.pipeline.reviewScriptCalls(actions.map((action) => ({
17508
17724
  ...action,
17725
+ attemptId: run.toolCallId,
17509
17726
  script: {
17510
17727
  source: run.source,
17511
17728
  calls: earlier
@@ -17668,7 +17885,8 @@ function renderScriptTools(tools) {
17668
17885
  "declare const tools: {",
17669
17886
  ...shown.map((entry) => entry.text),
17670
17887
  "};",
17671
- ...omitted > 0 ? [`${omitted} more tools are not declared here. Each ${SEARCH_TOOLS_NAME} result is also callable as \`tools.<tool_name>(args)\`, with its input_schema.`] : []
17888
+ ...omitted > 0 ? [`${omitted} more tools are not declared here. To find one, call ${SEARCH_TOOLS_NAME} directly before the script. Then call each result as \`tools.<tool_name>(args)\`, with its input_schema. A script cannot call ${SEARCH_TOOLS_NAME} or ${EXECUTE_TOOL_NAME}.`] : [],
17889
+ ...Object.hasOwn(tools, "callMcpTool") ? ["Call an MCP tool as `tools.callMcpTool({ tool_name, arguments })`. `tools.mcp__<provider>__<tool>` does not exist."] : []
17672
17890
  ].join("\n");
17673
17891
  }
17674
17892
  function renderDescription(tools) {
@@ -21260,6 +21478,7 @@ async function executeAgentRunInPrivacyContext(run, conversationPrivacy, runLogC
21260
21478
  };
21261
21479
  agent = new Agent({
21262
21480
  getApiKey: getGatewayApiKey,
21481
+ sessionId: conversationId,
21263
21482
  streamFn: createTracedStreamFn({
21264
21483
  conversationPrivacy,
21265
21484
  ...streamFn ? { base: streamFn } : void 0