@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.
- package/dist/{adapter-BDYDi1F9.js → adapter-umap1cth.js} +1 -1
- package/dist/{admin-Dj7EFycG.js → admin-0o6E_3lo.js} +1 -1
- package/dist/{agent-runner-Cl6Usx2Y.js → agent-runner-D7Qz-lGO.js} +517 -298
- package/dist/api/schema.d.ts +1 -1
- package/dist/api/schema.js +4 -4
- package/dist/api.d.ts +1 -1
- package/dist/api.js +56 -573
- package/dist/{app-DjZblE2J.d.ts → app-BcyhJq8x.d.ts} +4 -9
- package/dist/app.d.ts +1 -1
- package/dist/app.js +673 -127
- package/dist/{assistant-reply-DN2w6JL_.js → assistant-reply-wZ7i4OFa.js} +1 -1
- package/dist/{automation-CV7g5h3A.js → automation-yffXcZt9.js} +1 -1
- package/dist/{briefs-PysAqnjV.js → briefs-CgMnlpIp.js} +4 -4
- package/dist/{catalog-runtime-BNeCWIaF.js → catalog-runtime-TygDsPOU.js} +1 -1
- package/dist/cli/chat.js +15 -15
- package/dist/cli/check.js +3 -3
- package/dist/cli/init.js +1 -1
- package/dist/cli/main.js +3 -3
- package/dist/cli/plugins.js +4 -4
- package/dist/cli/snapshot-create.js +4 -4
- package/dist/cli/upgrade.d.ts +1 -1
- package/dist/cli/upgrade.js +3 -3
- package/dist/{client-FRphcDWW.js → client-DhqbV0Fe.js} +63 -5
- package/dist/{reporting-schema-CZREhYc-.js → common-Eu6sx09c.js} +4 -1
- package/dist/{config-BUP9wjiR.js → config-C6FTd7PX.js} +6 -14
- package/dist/{connections-CesHau9u.js → connections-Dq72KajR.js} +11 -11
- package/dist/{conversation-BtQzLoK8.js → conversation-T4oabbBi.js} +10 -0
- package/dist/{conversation-privacy-DiGvA9a3.js → conversation-privacy-BeRgrsph.js} +1 -1
- package/dist/{credential-sync-D2odJC30.js → credential-sync-BmCbHAp4.js} +15 -16
- package/dist/{db-BPLmOwXf.d.ts → db-DOzBGLn5.d.ts} +1 -1
- package/dist/{executor-DGDpyegI.js → executor-BafgvQcc.js} +2 -2
- package/dist/instrumentation.js +114 -3
- package/dist/{job-queue-CjB97sii.js → job-queue-C4lphW9Y.js} +3 -3
- package/dist/{logging-CjkDr-jP.js → logging-CNs1e04n.js} +6 -1
- package/dist/{message-D9y8PKA1.js → message-CETC5lsl.js} +9 -9
- package/dist/nitro.d.ts +1 -1
- package/dist/nitro.js +3 -3
- package/dist/{oauth-callback-server-02Pej6hj.js → oauth-callback-server-DcQZsKMm.js} +2 -2
- package/dist/{credentialed-CgTulRDH.js → projection-BOpi891B.js} +4576 -49
- package/dist/{prompt-vfceH0c-.js → prompt-B1ySjUyf.js} +1 -1
- package/dist/{registry-7XgwNgQe.js → registry-B-yUrv3h.js} +2 -2
- package/dist/{resolve-CuuxNfWG.js → resolve-DqVtd4Q9.js} +3 -3
- package/dist/{result-BRhb6iN1.js → result-DQwTiLTw.js} +9 -9
- package/dist/{runner-DaWCofuS.js → runner-Dco5qMq6.js} +11 -11
- package/dist/{sandbox-egress-signals-Bn6FA1UC.js → sandbox-egress-signals-DFc_9Mcv.js} +1 -1
- package/dist/{schema-D5VLF4NT.js → schema-5xHDGlBW.js} +9 -7
- package/dist/{schema-CNjAzYOw.d.ts → schema-9OGdsaxF.d.ts} +13 -0
- package/dist/{service-account-D9vrsi8d.js → service-account-CWfk9Cs-.js} +3 -3
- package/dist/{service-account-H45LIAG2.js → service-account-smdPFWNy.js} +3 -3
- package/dist/{session-stop-HKl0ECX0.js → session-stop-DaT_7tYa.js} +11 -11
- package/dist/{skills-CsiKu3-g.js → skills-BbtJSF2-.js} +2 -2
- package/dist/{spawn-DqjBxqWh.js → spawn-Ceikdjkp.js} +2 -2
- package/dist/{list-JZUP1yog.js → stop-dKUc-x2K.js} +996 -432
- package/dist/{store-DyVKH0QB.js → store-C1xuZcLd.js} +4 -4
- package/dist/{store-CPF2kzbu.js → store-DaxUuOmD.js} +3 -3
- package/dist/{task-queue-BbQXCcNX.js → task-queue-ymAh_-P1.js} +4 -4
- package/dist/{task-runner-ChzW5E7R.js → task-runner-vlH6pEm3.js} +6 -6
- package/dist/{thread-state-Ds8JTsX6.js → thread-state-BkUpE9Ae.js} +4 -5
- package/dist/{turn-failure-response-DCMvjsA0.js → turn-failure-response-DIsD7ip_.js} +7 -8
- package/dist/{turn-lifecycle-DM_77q33.js → turn-lifecycle-BdPKdPS8.js} +1 -1
- package/dist/turn-session-routing-BdCTDq9m.js +1625 -0
- package/dist/{validation-DcGelbBi.js → validation-BQjCfh84.js} +1 -1
- package/dist/{version-C48azPKq.js → version-BpiFGZZW.js} +1 -1
- package/dist/version.js +1 -1
- package/dist/{viewer-BCRuO8Wg.js → viewer-C6Y1aXjZ.js} +27 -15
- package/dist/{work-zxkmbJAp.js → work-ChPh_YEh.js} +10 -38
- package/dist/{worker-CNNxHF1V.js → worker-Be_sK_ou.js} +6 -6
- package/package.json +4 -4
- package/dist/turn-session-routing-BV_gX7n3.js +0 -6072
- 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
|
|
3
|
-
import { _ as
|
|
4
|
-
import {
|
|
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-
|
|
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-
|
|
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,
|
|
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-
|
|
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-
|
|
16
|
-
import { n as pluginCatalogRuntime } from "./catalog-runtime-
|
|
17
|
-
import { a as parseSkillInvocation, i as parseSkillFile, n as findSkillByName, r as loadSkillsByName, t as discoverSkills } from "./skills-
|
|
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-
|
|
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-
|
|
23
|
-
import { r as EVENT_AUTHOR_ID } from "./prompt-
|
|
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-
|
|
25
|
-
import { a as isNoReplyMarker, i as NO_REPLY_MARKER, n as getAssistantReplyText, r as sanitizeAssistantText, t as decideReply } from "./assistant-reply-
|
|
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-
|
|
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-
|
|
29
|
-
import { a as getVercelConversationWorkQueue, f as getTurnRequestDeadline, n as sendNextWorkspaceSnapshotJob, r as sendWorkspaceSnapshotJob } from "./job-queue-
|
|
30
|
-
import { D as stopConversationWork, n as appendAndEnqueueExclusiveInboundMessage, r as appendAndEnqueueInboundMessage } from "./store-
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
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
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
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
|
-
|
|
371
|
+
tree.repositories.sort(compareStrings);
|
|
372
|
+
return tree;
|
|
422
373
|
}
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
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
|
-
|
|
433
|
-
|
|
434
|
-
|
|
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
|
-
|
|
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
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
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)
|
|
454
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
|
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
|
-
/**
|
|
560
|
-
|
|
561
|
-
|
|
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
|
|
565
|
-
const gitRoot =
|
|
539
|
+
for (const directory of cwds) {
|
|
540
|
+
const gitRoot = findGitRoot(tree, directory);
|
|
566
541
|
if (!gitRoot) continue;
|
|
567
|
-
const sources =
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
4193
|
-
|
|
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
|
|
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
|
|
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
|
|
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 {
|
|
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
|
|
5713
|
-
|
|
5714
|
-
|
|
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
|
-
|
|
16759
|
-
|
|
16760
|
-
|
|
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
|
-
|
|
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.
|
|
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
|