@sentry/junior 0.132.0 → 0.134.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/{agent-hooks-ONXOJDR6.js → agent-hooks-HGXNYOOD.js} +9 -9
- package/dist/api/schema/task.d.ts +60 -0
- package/dist/api/schema.d.ts +2 -2
- package/dist/api/schema.js +3 -1
- package/dist/api.js +90 -20
- package/dist/app.js +110 -39
- package/dist/{catalog-runtime-BATAY4R7.js → catalog-runtime-P6QHU6XE.js} +5 -5
- package/dist/chat/agent/prompt.d.ts +7 -5
- package/dist/chat/agent/repository-context.d.ts +20 -0
- package/dist/chat/agent/sandbox.d.ts +8 -0
- package/dist/chat/agent/tools.d.ts +3 -0
- package/dist/chat/config.d.ts +2 -0
- package/dist/chat/conversations/projection.d.ts +3 -1
- package/dist/chat/conversations/sql/bindings.d.ts +13 -0
- package/dist/chat/conversations/sql/store.d.ts +3 -0
- package/dist/chat/conversations/store.d.ts +15 -0
- package/dist/chat/plugins/agent-hooks.d.ts +2 -2
- package/dist/chat/repository-instructions.d.ts +44 -0
- package/dist/chat/runtime/reply-executor.d.ts +1 -0
- package/dist/chat/runtime/slack-runtime.d.ts +2 -0
- package/dist/chat/sandbox/noninteractive-command.d.ts +2 -0
- package/dist/chat/sandbox/sandbox.d.ts +3 -0
- package/dist/chat/sandbox/session.d.ts +1 -0
- package/dist/chat/services/context-compaction.d.ts +1 -0
- package/dist/chat/slack/dispatch-turn.d.ts +1 -0
- package/dist/chat/slack/reply.d.ts +1 -1
- package/dist/chat/slack/tools/user-lookup.d.ts +2 -6
- package/dist/chat/tasks/execution-stats.d.ts +29 -0
- package/dist/chat/tasks/registered.d.ts +3 -0
- package/dist/chat/tools/index.d.ts +2 -0
- package/dist/chat/tools/sandbox/bash.d.ts +1 -0
- package/dist/chat/tools/system-time.d.ts +11 -1
- package/dist/{chunk-O2UXPUVQ.js → chunk-52DIKFJV.js} +14 -6
- package/dist/{chunk-GOEZU33G.js → chunk-6T3AYZT7.js} +1 -1
- package/dist/{chunk-GPXVDJML.js → chunk-7GKQ7UIO.js} +449 -39
- package/dist/{chunk-QVEXYYFM.js → chunk-7UTBPDXK.js} +24 -1
- package/dist/{chunk-U76PS7AT.js → chunk-AS3I7OIL.js} +2 -2
- package/dist/{chunk-HX236W2U.js → chunk-CDQ4YIZM.js} +9 -0
- package/dist/{chunk-ODIP5CEM.js → chunk-FDNAUM7X.js} +32 -32
- package/dist/{chunk-GTKNPQNN.js → chunk-FL466XX5.js} +36 -24
- package/dist/{chunk-2425Q4OR.js → chunk-GSP5SMZJ.js} +1 -1
- package/dist/{chunk-XN656LB4.js → chunk-KWGJG4HO.js} +6 -12
- package/dist/{chunk-ISNRPCPG.js → chunk-LKK4XMUV.js} +1 -1
- package/dist/{chunk-G3WYA2RS.js → chunk-MFERLE3G.js} +1 -1
- package/dist/{chunk-ZMV2YBT6.js → chunk-NF2TNZPD.js} +788 -674
- package/dist/chunk-NOA34AJY.js +122 -0
- package/dist/{chunk-JYQAE2ZU.js → chunk-NYWSCJVW.js} +1 -1
- package/dist/{chunk-KB2M27DL.js → chunk-QLEKAO7O.js} +130 -40
- package/dist/{chunk-BKE47OOW.js → chunk-S6DMTEOM.js} +2 -2
- package/dist/{chunk-ALDPUN3Z.js → chunk-TR6UEEYD.js} +8 -6
- package/dist/{chunk-WEJIS7OC.js → chunk-U3ZI7CCW.js} +1 -1
- package/dist/{chunk-B7IY6SVJ.js → chunk-VEGF62FV.js} +1 -1
- package/dist/{chunk-SKWIIUS7.js → chunk-VGXJMECA.js} +7 -7
- package/dist/{chunk-V44FFBO3.js → chunk-VP6NCPRS.js} +1 -1
- package/dist/{chunk-WYQP7QSB.js → chunk-VPYKNCTN.js} +101 -61
- package/dist/{chunk-NNHUGGO4.js → chunk-XG5PHKPM.js} +19 -3
- package/dist/{chunk-AW5Z3T7V.js → chunk-YNLQQ622.js} +9 -8
- package/dist/cli/chat.js +25 -25
- package/dist/cli/check.js +6 -6
- package/dist/cli/init.js +1 -1
- package/dist/cli/plugins.js +12 -12
- package/dist/cli/snapshot-create.js +9 -9
- package/dist/cli/upgrade.js +9 -8
- package/dist/db/executor.d.ts +2 -1
- package/dist/db/neon.d.ts +1 -0
- package/dist/db/postgres.d.ts +1 -0
- package/dist/db/schema/conversation-bindings.d.ts +110 -0
- package/dist/db/schema/task-executions.d.ts +110 -0
- package/dist/db/schema.d.ts +221 -1
- package/dist/{db-QFFZACXF.js → db-GFK4NHWJ.js} +7 -7
- package/dist/instrumentation.js +4 -4
- package/dist/nitro.js +2 -2
- package/dist/{oauth-callback-server-5WXLD533.js → oauth-callback-server-HUJLAQ5N.js} +19 -18
- package/dist/{runner-Y3LTUNAA.js → runner-T77AFAH4.js} +13 -12
- package/dist/{skills-7M5C24GM.js → skills-3KUFJOOR.js} +6 -6
- package/dist/{validation-MRILMYDI.js → validation-FMWDHKYU.js} +6 -6
- package/dist/version.js +1 -1
- package/migrations/0018_curvy_excalibur.sql +12 -0
- package/migrations/0019_lowly_valkyrie.sql +14 -0
- package/migrations/meta/0018_snapshot.json +1756 -0
- package/migrations/meta/0019_snapshot.json +1904 -0
- package/migrations/meta/_journal.json +15 -1
- package/package.json +5 -5
|
@@ -30,30 +30,41 @@ import {
|
|
|
30
30
|
toMcpProviderError,
|
|
31
31
|
toolInvocationDestination,
|
|
32
32
|
uploadFilesToConversation
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-VGXJMECA.js";
|
|
34
34
|
import {
|
|
35
|
+
AGENTS_REMOVAL_NOTICE,
|
|
36
|
+
AGENTS_REPLACEMENT_NOTICE,
|
|
35
37
|
CooperativeTurnYieldError,
|
|
38
|
+
DEFAULT_SEARCH_COMMAND_TIMEOUT_MS,
|
|
36
39
|
JUNIOR_THREAD_STATE_TTL_MS,
|
|
40
|
+
MAX_TEXT_CHARS,
|
|
37
41
|
NO_REPLY_MARKER,
|
|
42
|
+
RIPGREP_EXCLUDED_GLOBS,
|
|
38
43
|
TURN_CONTEXT_TAG,
|
|
39
44
|
TurnInputCommitLostError,
|
|
40
45
|
TurnSliceLimitExceededError,
|
|
41
46
|
abandonReplacedPendingAuth,
|
|
47
|
+
buildAgentsInstructionsMessage,
|
|
42
48
|
canReusePendingAuthLink,
|
|
49
|
+
collectFiles,
|
|
43
50
|
containsNoReplyMarker,
|
|
44
51
|
decideReply,
|
|
45
52
|
estimateTextTokens,
|
|
46
53
|
extractAssistantText,
|
|
54
|
+
findSingleRepositoryDirectory,
|
|
55
|
+
findVisibleAgentsInstructions,
|
|
47
56
|
getAgentContextCompactionTriggerTokens,
|
|
48
57
|
getAgentContextInputLimitTokens,
|
|
49
58
|
getAssistantReplyText,
|
|
50
59
|
getPiMessageRole,
|
|
60
|
+
getRipgrepSearchLocation,
|
|
51
61
|
getTerminalAssistantMessages,
|
|
52
62
|
getUserMessageInstructionText,
|
|
53
63
|
hasRuntimeTurnContext,
|
|
54
64
|
historyItemFromPiMessage,
|
|
55
65
|
isAssistantMessage,
|
|
56
66
|
isContinuablePiBoundary,
|
|
67
|
+
isMissingPathError,
|
|
57
68
|
isNoReplyMarker,
|
|
58
69
|
isToolResultError,
|
|
59
70
|
isToolResultMessage,
|
|
@@ -63,6 +74,8 @@ import {
|
|
|
63
74
|
loadTurnRoute,
|
|
64
75
|
loadTurnSessionRecord,
|
|
65
76
|
mergeArtifactsState,
|
|
77
|
+
missingPathSearchResult,
|
|
78
|
+
normalizeToLf,
|
|
66
79
|
normalizeToolNameFromResult,
|
|
67
80
|
openConversationProjection,
|
|
68
81
|
persistAuthPauseSessionRecord,
|
|
@@ -70,17 +83,22 @@ import {
|
|
|
70
83
|
persistRunningSessionRecord,
|
|
71
84
|
persistYieldSessionRecord,
|
|
72
85
|
piMessageFromHistoryItem,
|
|
86
|
+
positiveInteger,
|
|
73
87
|
recordAuthorizationRequested,
|
|
74
88
|
recordGuardianActionReviewed,
|
|
75
89
|
recordMcpProviderConnected,
|
|
76
90
|
recordToolExecutionStarted,
|
|
77
91
|
recordTurnRoute,
|
|
92
|
+
renderAgentsInstructions,
|
|
93
|
+
resolveRepositoryInstructions,
|
|
94
|
+
resolveWorkspacePath,
|
|
78
95
|
retainRuntimeTurnContext,
|
|
79
96
|
sanitizeAssistantText,
|
|
80
97
|
stripRuntimeTurnContext,
|
|
81
98
|
trimTrailingAssistantMessages,
|
|
99
|
+
truncateText,
|
|
82
100
|
updateConversationStats
|
|
83
|
-
} from "./chunk-
|
|
101
|
+
} from "./chunk-7GKQ7UIO.js";
|
|
84
102
|
import {
|
|
85
103
|
parseSandboxEgressAuthRequiredSignal,
|
|
86
104
|
parseSandboxEgressPermissionDeniedSignal,
|
|
@@ -89,6 +107,7 @@ import {
|
|
|
89
107
|
} from "./chunk-EPBBYMNE.js";
|
|
90
108
|
import {
|
|
91
109
|
bindSandboxFileSystem,
|
|
110
|
+
buildCommandScript,
|
|
92
111
|
buildNonInteractiveShellScript,
|
|
93
112
|
createSandboxSession,
|
|
94
113
|
getSandboxResources,
|
|
@@ -104,10 +123,9 @@ import {
|
|
|
104
123
|
runNonInteractiveCommand,
|
|
105
124
|
throwSandboxOperationError,
|
|
106
125
|
wrapSandboxSetupError
|
|
107
|
-
} from "./chunk-
|
|
126
|
+
} from "./chunk-YNLQQ622.js";
|
|
108
127
|
import {
|
|
109
128
|
SCHEDULED_TASK_SYSTEM_ACTOR,
|
|
110
|
-
ToolInputError,
|
|
111
129
|
compactEventTask,
|
|
112
130
|
createEventTask,
|
|
113
131
|
createSchedulerSqlStore,
|
|
@@ -136,14 +154,15 @@ import {
|
|
|
136
154
|
resolveLocalScheduleAtMs,
|
|
137
155
|
saveEventTask,
|
|
138
156
|
writableEventTask
|
|
139
|
-
} from "./chunk-
|
|
157
|
+
} from "./chunk-KWGJG4HO.js";
|
|
140
158
|
import {
|
|
141
159
|
parseSlackThreadId
|
|
142
|
-
} from "./chunk-
|
|
160
|
+
} from "./chunk-MFERLE3G.js";
|
|
143
161
|
import {
|
|
162
|
+
ToolInputError,
|
|
144
163
|
addAgentTurnUsage,
|
|
145
164
|
hasAgentTurnUsage
|
|
146
|
-
} from "./chunk-
|
|
165
|
+
} from "./chunk-CDQ4YIZM.js";
|
|
147
166
|
import {
|
|
148
167
|
privateTraceResultAttributes
|
|
149
168
|
} from "./chunk-UIE3R5XU.js";
|
|
@@ -162,7 +181,7 @@ import {
|
|
|
162
181
|
readActorIdentity,
|
|
163
182
|
resolveChannelCapabilities,
|
|
164
183
|
withSlackRetries
|
|
165
|
-
} from "./chunk-
|
|
184
|
+
} from "./chunk-TR6UEEYD.js";
|
|
166
185
|
import {
|
|
167
186
|
KNOWN_CONVERSATION_EVENT_TYPES,
|
|
168
187
|
contextProvenance,
|
|
@@ -173,7 +192,7 @@ import {
|
|
|
173
192
|
instructionActors,
|
|
174
193
|
instructionProvenanceFor,
|
|
175
194
|
sameActorIdentity
|
|
176
|
-
} from "./chunk-
|
|
195
|
+
} from "./chunk-VPYKNCTN.js";
|
|
177
196
|
import {
|
|
178
197
|
SANDBOX_DATA_ROOT,
|
|
179
198
|
SANDBOX_SKILLS_ROOT,
|
|
@@ -183,7 +202,7 @@ import {
|
|
|
183
202
|
sandboxSkillFile,
|
|
184
203
|
sandboxSkillPathResolution,
|
|
185
204
|
sleep
|
|
186
|
-
} from "./chunk-
|
|
205
|
+
} from "./chunk-FDNAUM7X.js";
|
|
187
206
|
import {
|
|
188
207
|
ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX,
|
|
189
208
|
COMPACTION_SUMMARY_PREFIX,
|
|
@@ -217,7 +236,7 @@ import {
|
|
|
217
236
|
renderCurrentInstruction,
|
|
218
237
|
resolveGatewayModel,
|
|
219
238
|
unwrapCurrentInstruction
|
|
220
|
-
} from "./chunk-
|
|
239
|
+
} from "./chunk-XG5PHKPM.js";
|
|
221
240
|
import {
|
|
222
241
|
canExposeConversationPayload,
|
|
223
242
|
resolveConversationPrivacy,
|
|
@@ -227,16 +246,16 @@ import {
|
|
|
227
246
|
toGenAiMessagesTraceAttributes,
|
|
228
247
|
toGenAiPayloadMetadata,
|
|
229
248
|
toGenAiPayloadTraceAttributes
|
|
230
|
-
} from "./chunk-
|
|
249
|
+
} from "./chunk-LKK4XMUV.js";
|
|
231
250
|
import {
|
|
232
251
|
discoverSkills,
|
|
233
252
|
findSkillByName,
|
|
234
253
|
loadSkillsByName,
|
|
235
254
|
parseSkillInvocation
|
|
236
|
-
} from "./chunk-
|
|
255
|
+
} from "./chunk-S6DMTEOM.js";
|
|
237
256
|
import {
|
|
238
257
|
pluginCatalogRuntime
|
|
239
|
-
} from "./chunk-
|
|
258
|
+
} from "./chunk-VEGF62FV.js";
|
|
240
259
|
import {
|
|
241
260
|
CredentialUnavailableError,
|
|
242
261
|
OAuthProviderError,
|
|
@@ -244,7 +263,7 @@ import {
|
|
|
244
263
|
getOAuthProviderErrorAttributes,
|
|
245
264
|
resolveAuthTokenPlaceholder,
|
|
246
265
|
resolvePluginCommandEnv
|
|
247
|
-
} from "./chunk-
|
|
266
|
+
} from "./chunk-VP6NCPRS.js";
|
|
248
267
|
import {
|
|
249
268
|
credentialUserSubjectId
|
|
250
269
|
} from "./chunk-77VDLF3Z.js";
|
|
@@ -273,7 +292,7 @@ import {
|
|
|
273
292
|
summarizeMessageText,
|
|
274
293
|
withLogContext,
|
|
275
294
|
withSpan
|
|
276
|
-
} from "./chunk-
|
|
295
|
+
} from "./chunk-6T3AYZT7.js";
|
|
277
296
|
import {
|
|
278
297
|
startInactiveSpan,
|
|
279
298
|
withActiveSpan
|
|
@@ -568,6 +587,56 @@ var SkillSandbox = class {
|
|
|
568
587
|
}
|
|
569
588
|
};
|
|
570
589
|
|
|
590
|
+
// src/chat/agent/repository-context.ts
|
|
591
|
+
function findRestoredInstructions(messages, provenance) {
|
|
592
|
+
const restoredContext = messages?.map((message, index) => ({ message, provenance: provenance?.[index] })).reverse().find(
|
|
593
|
+
({ message, provenance: messageProvenance }) => messageProvenance?.authority === "context" ? Boolean(findVisibleAgentsInstructions([message])) : false
|
|
594
|
+
);
|
|
595
|
+
return restoredContext ? findVisibleAgentsInstructions([restoredContext.message]) : void 0;
|
|
596
|
+
}
|
|
597
|
+
function createRepositoryInstructionsContext(options) {
|
|
598
|
+
const visibleInstructions = options.shouldPromptAgent ? findVisibleAgentsInstructions([
|
|
599
|
+
{
|
|
600
|
+
role: "user",
|
|
601
|
+
content: options.promptContextContentParts
|
|
602
|
+
}
|
|
603
|
+
]) : findRestoredInstructions(
|
|
604
|
+
options.restoredMessages,
|
|
605
|
+
options.restoredProvenance
|
|
606
|
+
);
|
|
607
|
+
let instructionsVisible = visibleInstructions?.active === true;
|
|
608
|
+
let visibleFingerprint = options.initialInstructions && visibleInstructions?.active === true && visibleInstructions.directory === options.initialInstructions.directory && visibleInstructions.text === options.initialInstructions.text ? options.initialInstructions.fingerprint : void 0;
|
|
609
|
+
return {
|
|
610
|
+
async applyUpdate(currentUpdate, currentContext) {
|
|
611
|
+
if (!options.hasSandbox()) {
|
|
612
|
+
return currentUpdate;
|
|
613
|
+
}
|
|
614
|
+
const instructions = await options.capture();
|
|
615
|
+
if (instructions && instructions.fingerprint === visibleFingerprint && instructionsVisible) {
|
|
616
|
+
return currentUpdate;
|
|
617
|
+
}
|
|
618
|
+
if (!instructions && !instructionsVisible) {
|
|
619
|
+
return currentUpdate;
|
|
620
|
+
}
|
|
621
|
+
const message = instructions ? buildAgentsInstructionsMessage({
|
|
622
|
+
directory: instructions.directory,
|
|
623
|
+
text: instructionsVisible ? `${AGENTS_REPLACEMENT_NOTICE}
|
|
624
|
+
|
|
625
|
+
${instructions.text}` : instructions.text
|
|
626
|
+
}) : buildAgentsInstructionsMessage({ text: AGENTS_REMOVAL_NOTICE });
|
|
627
|
+
const context = currentUpdate?.context ?? currentContext;
|
|
628
|
+
const messages = [...context.messages, message];
|
|
629
|
+
options.setMessages(messages);
|
|
630
|
+
visibleFingerprint = instructions?.fingerprint;
|
|
631
|
+
instructionsVisible = Boolean(instructions);
|
|
632
|
+
return {
|
|
633
|
+
...currentUpdate,
|
|
634
|
+
context: { ...context, messages }
|
|
635
|
+
};
|
|
636
|
+
}
|
|
637
|
+
};
|
|
638
|
+
}
|
|
639
|
+
|
|
571
640
|
// src/chat/pi/traced-stream.ts
|
|
572
641
|
import {
|
|
573
642
|
streamSimple
|
|
@@ -1302,11 +1371,11 @@ function projectToolActionRejection(pending, toolCallId, result) {
|
|
|
1302
1371
|
function restoreToolActionRejections(messages) {
|
|
1303
1372
|
const priorRejections = [];
|
|
1304
1373
|
for (const message of messages) {
|
|
1305
|
-
const
|
|
1306
|
-
if (
|
|
1374
|
+
const record2 = message;
|
|
1375
|
+
if (record2.role !== "toolResult" || record2.isError !== true) {
|
|
1307
1376
|
continue;
|
|
1308
1377
|
}
|
|
1309
|
-
const rejection = getToolActionRejectionMarker(
|
|
1378
|
+
const rejection = getToolActionRejectionMarker(record2);
|
|
1310
1379
|
if (!rejection) {
|
|
1311
1380
|
continue;
|
|
1312
1381
|
}
|
|
@@ -1806,6 +1875,7 @@ var EXECUTION_CONTRACT_RULES = [
|
|
|
1806
1875
|
var CONVERSATION_RULES = [
|
|
1807
1876
|
"- In thread follow-ups, answer from prior thread context; do not repeat resolved clarifying questions.",
|
|
1808
1877
|
"- Preserve attribution roles from thread context: the actor is the person asking now, which may differ from the original reporter or subject.",
|
|
1878
|
+
"- Direct system/developer/user instructions (as part of a prompt) take precedence over AGENTS.md instructions.",
|
|
1809
1879
|
"- Runtime owns continuation and authorization notices; on resumed turns, answer with the final requested content only."
|
|
1810
1880
|
];
|
|
1811
1881
|
var SLACK_ACTION_RULES = [
|
|
@@ -2082,7 +2152,6 @@ function buildTurnContextPrompt(params) {
|
|
|
2082
2152
|
const sections = [
|
|
2083
2153
|
`<${TURN_CONTEXT_TAG}>`,
|
|
2084
2154
|
TURN_CONTEXT_HEADER,
|
|
2085
|
-
"The current user instruction appears after this block in `<current-instruction>` in the same message.",
|
|
2086
2155
|
...runtimeSections,
|
|
2087
2156
|
`</${TURN_CONTEXT_TAG}>`
|
|
2088
2157
|
].filter((section) => Boolean(section));
|
|
@@ -2103,17 +2172,8 @@ function renderThreadContextForPrompt(context) {
|
|
|
2103
2172
|
}
|
|
2104
2173
|
return ["<thread-background>", context, "</thread-background>"].join("\n");
|
|
2105
2174
|
}
|
|
2106
|
-
function buildUserTurnText(userInput,
|
|
2107
|
-
|
|
2108
|
-
const currentInstruction = renderCurrentInstruction(userInput, actor);
|
|
2109
|
-
if (!trimmedContext) {
|
|
2110
|
-
return currentInstruction;
|
|
2111
|
-
}
|
|
2112
|
-
return [
|
|
2113
|
-
renderThreadContextForPrompt(trimmedContext),
|
|
2114
|
-
"",
|
|
2115
|
-
currentInstruction
|
|
2116
|
-
].join("\n");
|
|
2175
|
+
function buildUserTurnText(userInput, actor) {
|
|
2176
|
+
return renderCurrentInstruction(userInput, actor);
|
|
2117
2177
|
}
|
|
2118
2178
|
function renderExplicitSkillInstructions(skill) {
|
|
2119
2179
|
return [
|
|
@@ -2246,24 +2306,24 @@ function buildUserTurnInput(args) {
|
|
|
2246
2306
|
})
|
|
2247
2307
|
});
|
|
2248
2308
|
}
|
|
2249
|
-
return { routerBlocks, userContentParts };
|
|
2309
|
+
return { contextContentParts: [], routerBlocks, userContentParts };
|
|
2250
2310
|
}
|
|
2251
2311
|
function buildPromptInput(input) {
|
|
2252
2312
|
const promptConversationContext = input.piMessages && input.piMessages.length > 0 && !input.includeConversationContextWithPiMessages ? void 0 : input.conversationContext;
|
|
2253
|
-
const
|
|
2254
|
-
input.messageText,
|
|
2255
|
-
promptConversationContext,
|
|
2256
|
-
input.actor
|
|
2257
|
-
);
|
|
2258
|
-
return buildUserTurnInput({
|
|
2313
|
+
const promptInput = buildUserTurnInput({
|
|
2259
2314
|
omittedImageAttachmentCount: input.omittedImageAttachmentCount ?? 0,
|
|
2260
2315
|
userAttachments: input.userAttachments,
|
|
2261
|
-
userTurnText
|
|
2316
|
+
userTurnText: buildUserTurnText(input.messageText, input.actor)
|
|
2262
2317
|
});
|
|
2318
|
+
const trimmedContext = promptConversationContext?.trim();
|
|
2319
|
+
return {
|
|
2320
|
+
...promptInput,
|
|
2321
|
+
contextContentParts: trimmedContext ? [{ type: "text", text: renderThreadContextForPrompt(trimmedContext) }] : []
|
|
2322
|
+
};
|
|
2263
2323
|
}
|
|
2264
2324
|
function buildSteeringPiMessage(message) {
|
|
2265
2325
|
const { userContentParts } = buildUserTurnInput({
|
|
2266
|
-
userTurnText: buildUserTurnText(message.text,
|
|
2326
|
+
userTurnText: buildUserTurnText(message.text, message.actor),
|
|
2267
2327
|
userAttachments: message.userAttachments,
|
|
2268
2328
|
omittedImageAttachmentCount: message.omittedImageAttachmentCount ?? 0
|
|
2269
2329
|
});
|
|
@@ -2275,19 +2335,25 @@ function buildSteeringPiMessage(message) {
|
|
|
2275
2335
|
}
|
|
2276
2336
|
function withoutTrailingUncheckpointedUserPrompt(messages, userContentParts) {
|
|
2277
2337
|
if (!messages || messages.length === 0) {
|
|
2278
|
-
return [];
|
|
2338
|
+
return { messages: [] };
|
|
2279
2339
|
}
|
|
2280
2340
|
const lastMessage = messages.at(-1);
|
|
2281
2341
|
if (lastMessage?.role !== "user") {
|
|
2282
|
-
return messages;
|
|
2342
|
+
return { messages };
|
|
2283
2343
|
}
|
|
2284
2344
|
const comparableLastMessage = stripRuntimeTurnContext([
|
|
2285
2345
|
lastMessage
|
|
2286
2346
|
])[0];
|
|
2287
2347
|
if (!userPromptContentMatches(comparableLastMessage?.content, userContentParts)) {
|
|
2288
|
-
return messages;
|
|
2348
|
+
return { messages };
|
|
2349
|
+
}
|
|
2350
|
+
const promptTimestamp = typeof lastMessage.timestamp === "number" ? lastMessage.timestamp : void 0;
|
|
2351
|
+
const withoutPrompt = messages.slice(0, -1);
|
|
2352
|
+
const previousMessage = withoutPrompt.at(-1);
|
|
2353
|
+
if (previousMessage && stripRuntimeTurnContext([previousMessage, lastMessage]).length === 1) {
|
|
2354
|
+
return { messages: withoutPrompt.slice(0, -1), promptTimestamp };
|
|
2289
2355
|
}
|
|
2290
|
-
return messages
|
|
2356
|
+
return { messages: withoutPrompt, promptTimestamp };
|
|
2291
2357
|
}
|
|
2292
2358
|
function userPromptContentMatches(storedContent, currentContent) {
|
|
2293
2359
|
return isDeepStrictEqual(storedContent, currentContent);
|
|
@@ -2301,53 +2367,58 @@ function isUserContentPart(value) {
|
|
|
2301
2367
|
}
|
|
2302
2368
|
return value.type === "image" && "data" in value && typeof value.data === "string" && "mimeType" in value && typeof value.mimeType === "string";
|
|
2303
2369
|
}
|
|
2304
|
-
function
|
|
2305
|
-
if (!args.messages || args.turnStartMessageIndex === void 0 || args.turnStartMessageIndex
|
|
2370
|
+
function checkpointedPrompt(args) {
|
|
2371
|
+
if (!args.messages || args.turnStartMessageIndex === void 0 || args.turnStartMessageIndex >= args.messages.length) {
|
|
2306
2372
|
return void 0;
|
|
2307
2373
|
}
|
|
2308
|
-
const
|
|
2374
|
+
const turnMessages = args.messages.slice(args.turnStartMessageIndex);
|
|
2375
|
+
const durableTurnMessages = stripRuntimeTurnContext(turnMessages);
|
|
2376
|
+
if (durableTurnMessages.length !== 1) {
|
|
2377
|
+
return void 0;
|
|
2378
|
+
}
|
|
2379
|
+
const message = durableTurnMessages[0];
|
|
2309
2380
|
if (message?.role !== "user" || !Array.isArray(message.content)) {
|
|
2310
2381
|
return void 0;
|
|
2311
2382
|
}
|
|
2312
|
-
|
|
2313
|
-
if (!userPromptContentMatches(durableMessage?.content, args.userContentParts)) {
|
|
2383
|
+
if (!userPromptContentMatches(message.content, args.userContentParts)) {
|
|
2314
2384
|
return void 0;
|
|
2315
2385
|
}
|
|
2316
|
-
|
|
2317
|
-
if (!content.every(isUserContentPart)) {
|
|
2386
|
+
if (!message.content.every(isUserContentPart)) {
|
|
2318
2387
|
return void 0;
|
|
2319
2388
|
}
|
|
2320
|
-
|
|
2389
|
+
const contextContentParts = retainRuntimeTurnContext(turnMessages).flatMap(
|
|
2390
|
+
(runtimeMessage) => {
|
|
2391
|
+
const content = runtimeMessage.content;
|
|
2392
|
+
return Array.isArray(content) && content.every(isUserContentPart) ? content : [];
|
|
2393
|
+
}
|
|
2394
|
+
);
|
|
2395
|
+
return {
|
|
2396
|
+
contextContentParts,
|
|
2397
|
+
userContentParts: message.content
|
|
2398
|
+
};
|
|
2321
2399
|
}
|
|
2322
2400
|
async function assemblePrompt(args) {
|
|
2323
2401
|
const source = args.routing.source;
|
|
2324
2402
|
const hasPromptCheckpoint = args.resumedFromSessionRecord && args.existingTurnStartMessageIndex !== void 0;
|
|
2325
2403
|
const shouldPromptAgent = !args.resumedFromSessionRecord || !hasPromptCheckpoint;
|
|
2326
|
-
const requestContentParts =
|
|
2327
|
-
|
|
2328
|
-
{
|
|
2329
|
-
type: "text",
|
|
2330
|
-
text: renderExplicitSkillInstructions(args.explicitSkill)
|
|
2331
|
-
}
|
|
2332
|
-
] : [],
|
|
2333
|
-
...args.userContentParts
|
|
2334
|
-
];
|
|
2335
|
-
const promptHistoryMessages = shouldPromptAgent ? withoutTrailingUncheckpointedUserPrompt(
|
|
2404
|
+
const requestContentParts = args.userContentParts;
|
|
2405
|
+
const trimmedPrompt = shouldPromptAgent ? withoutTrailingUncheckpointedUserPrompt(
|
|
2336
2406
|
args.priorPiMessages,
|
|
2337
2407
|
requestContentParts
|
|
2338
|
-
) : args.existingSessionPiMessages;
|
|
2339
|
-
const
|
|
2408
|
+
) : { messages: args.existingSessionPiMessages };
|
|
2409
|
+
const promptHistoryMessages = trimmedPrompt.messages;
|
|
2410
|
+
const replayedPrompt = shouldPromptAgent && !args.resumedFromSessionRecord ? checkpointedPrompt({
|
|
2340
2411
|
messages: args.existingSessionPiMessages,
|
|
2341
2412
|
turnStartMessageIndex: args.existingTurnStartMessageIndex,
|
|
2342
2413
|
userContentParts: requestContentParts
|
|
2343
2414
|
}) : void 0;
|
|
2344
|
-
const needsBootstrapContextForPrompt = shouldPromptAgent && !
|
|
2415
|
+
const needsBootstrapContextForPrompt = shouldPromptAgent && !replayedPrompt && !hasRuntimeTurnContext(promptHistoryMessages);
|
|
2345
2416
|
const systemPromptContributions = await getPluginSystemPromptContributions(source);
|
|
2346
2417
|
const pluginSystemPrompt = buildPluginSystemPromptContributions(
|
|
2347
2418
|
systemPromptContributions
|
|
2348
2419
|
);
|
|
2349
2420
|
const baseInstructions = [buildSystemPrompt({ source }), pluginSystemPrompt].filter((section) => Boolean(section)).join("\n\n");
|
|
2350
|
-
const pluginUserPromptContributions = !shouldPromptAgent ||
|
|
2421
|
+
const pluginUserPromptContributions = !shouldPromptAgent || replayedPrompt ? [] : await getPluginUserPromptContributions({
|
|
2351
2422
|
context: args.toolRuntimeContext,
|
|
2352
2423
|
turnId: args.turnId
|
|
2353
2424
|
});
|
|
@@ -2371,18 +2442,33 @@ async function assemblePrompt(args) {
|
|
|
2371
2442
|
configuration: args.configurationValues
|
|
2372
2443
|
}) : null;
|
|
2373
2444
|
const turnContextParts = turnContextPrompt ? [{ type: "text", text: turnContextPrompt }] : [];
|
|
2374
|
-
const
|
|
2445
|
+
const contextContentParts = replayedPrompt?.contextContentParts ?? [
|
|
2375
2446
|
...turnContextParts,
|
|
2376
|
-
...
|
|
2447
|
+
...args.explicitSkill ? [
|
|
2448
|
+
{
|
|
2449
|
+
type: "text",
|
|
2450
|
+
text: renderExplicitSkillInstructions(args.explicitSkill)
|
|
2451
|
+
}
|
|
2452
|
+
] : [],
|
|
2453
|
+
...args.contextContentParts
|
|
2377
2454
|
];
|
|
2455
|
+
const userContentParts = replayedPrompt?.userContentParts ?? requestContentParts;
|
|
2378
2456
|
const inputMessages = [
|
|
2379
2457
|
{
|
|
2380
2458
|
role: "system",
|
|
2381
2459
|
content: [{ type: "text", text: baseInstructions }]
|
|
2382
2460
|
},
|
|
2461
|
+
...contextContentParts.length > 0 ? [
|
|
2462
|
+
{
|
|
2463
|
+
role: "user",
|
|
2464
|
+
content: contextContentParts.map(
|
|
2465
|
+
(part) => toObservablePromptPart(part)
|
|
2466
|
+
)
|
|
2467
|
+
}
|
|
2468
|
+
] : [],
|
|
2383
2469
|
{
|
|
2384
2470
|
role: "user",
|
|
2385
|
-
content:
|
|
2471
|
+
content: userContentParts.map((part) => toObservablePromptPart(part))
|
|
2386
2472
|
}
|
|
2387
2473
|
];
|
|
2388
2474
|
const inputMessagesAttribute = serializeGenAiAttribute(
|
|
@@ -2391,9 +2477,17 @@ async function assemblePrompt(args) {
|
|
|
2391
2477
|
role: "system",
|
|
2392
2478
|
parts: [{ type: "text", content: baseInstructions }]
|
|
2393
2479
|
},
|
|
2480
|
+
...contextContentParts.length > 0 ? [
|
|
2481
|
+
{
|
|
2482
|
+
role: "user",
|
|
2483
|
+
parts: contextContentParts.flatMap(
|
|
2484
|
+
(part) => part.type === "text" ? [{ type: "text", content: part.text }] : []
|
|
2485
|
+
)
|
|
2486
|
+
}
|
|
2487
|
+
] : [],
|
|
2394
2488
|
{
|
|
2395
2489
|
role: "user",
|
|
2396
|
-
parts:
|
|
2490
|
+
parts: userContentParts.flatMap(
|
|
2397
2491
|
(part) => part.type === "text" ? [{ type: "text", content: part.text }] : []
|
|
2398
2492
|
)
|
|
2399
2493
|
}
|
|
@@ -2401,14 +2495,16 @@ async function assemblePrompt(args) {
|
|
|
2401
2495
|
);
|
|
2402
2496
|
return {
|
|
2403
2497
|
baseInstructions,
|
|
2498
|
+
contextContentParts,
|
|
2404
2499
|
inputMessages,
|
|
2405
2500
|
inputMessagesAttribute,
|
|
2406
|
-
|
|
2501
|
+
...trimmedPrompt.promptTimestamp !== void 0 ? { promptTimestamp: trimmedPrompt.promptTimestamp } : {},
|
|
2407
2502
|
promptHistoryMessages,
|
|
2408
2503
|
shouldPromptAgent,
|
|
2409
2504
|
turnContexts: pluginUserPromptContributions.flatMap(
|
|
2410
2505
|
(contribution) => contribution.context ? [contribution.context] : []
|
|
2411
|
-
)
|
|
2506
|
+
),
|
|
2507
|
+
userContentParts
|
|
2412
2508
|
};
|
|
2413
2509
|
}
|
|
2414
2510
|
|
|
@@ -2967,8 +3063,8 @@ function sandboxProxyUrl(credentialToken) {
|
|
|
2967
3063
|
"Cannot determine base URL for sandbox credential egress (set JUNIOR_BASE_URL or deploy to Vercel)"
|
|
2968
3064
|
);
|
|
2969
3065
|
}
|
|
2970
|
-
const
|
|
2971
|
-
return new URL(
|
|
3066
|
+
const path8 = credentialToken ? `${SANDBOX_EGRESS_PROXY_PATH}/${credentialToken}` : SANDBOX_EGRESS_PROXY_PATH;
|
|
3067
|
+
return new URL(path8, baseUrl).toString();
|
|
2972
3068
|
}
|
|
2973
3069
|
function buildSandboxEgressNetworkPolicy(input) {
|
|
2974
3070
|
const allow = {
|
|
@@ -3776,7 +3872,7 @@ function createSandboxRuntime(options) {
|
|
|
3776
3872
|
const commandResult2 = await sandboxInstance.runCommand({
|
|
3777
3873
|
cmd: "bash",
|
|
3778
3874
|
args: ["-c", script],
|
|
3779
|
-
cwd: SANDBOX_WORKSPACE_ROOT,
|
|
3875
|
+
cwd: input.cwd ?? SANDBOX_WORKSPACE_ROOT,
|
|
3780
3876
|
signal: controller.signal
|
|
3781
3877
|
});
|
|
3782
3878
|
return await readCommandOutput(commandResult2);
|
|
@@ -3844,191 +3940,6 @@ function createSandboxRuntime(options) {
|
|
|
3844
3940
|
};
|
|
3845
3941
|
}
|
|
3846
3942
|
|
|
3847
|
-
// src/chat/tools/sandbox/file-utils.ts
|
|
3848
|
-
import path4 from "path";
|
|
3849
|
-
var MAX_TEXT_CHARS = 6e4;
|
|
3850
|
-
var RIPGREP_EXCLUDED_GLOBS = [
|
|
3851
|
-
"!**/.git/**",
|
|
3852
|
-
"!**/node_modules/**"
|
|
3853
|
-
];
|
|
3854
|
-
var DEFAULT_SEARCH_COMMAND_TIMEOUT_MS = 3e4;
|
|
3855
|
-
var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
3856
|
-
function positiveInteger(value) {
|
|
3857
|
-
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
3858
|
-
return void 0;
|
|
3859
|
-
}
|
|
3860
|
-
const integer = Math.floor(value);
|
|
3861
|
-
return integer > 0 ? integer : void 0;
|
|
3862
|
-
}
|
|
3863
|
-
function normalizeToLf(value) {
|
|
3864
|
-
return value.replace(/\r\n/g, "\n").replace(/\r/g, "\n");
|
|
3865
|
-
}
|
|
3866
|
-
function truncateText(value, maxChars = MAX_TEXT_CHARS) {
|
|
3867
|
-
if (value.length <= maxChars) {
|
|
3868
|
-
return { content: value, truncated: false };
|
|
3869
|
-
}
|
|
3870
|
-
const removed = value.length - maxChars;
|
|
3871
|
-
return {
|
|
3872
|
-
content: `${value.slice(0, maxChars)}
|
|
3873
|
-
|
|
3874
|
-
[output truncated: ${removed} characters removed]`,
|
|
3875
|
-
truncated: true
|
|
3876
|
-
};
|
|
3877
|
-
}
|
|
3878
|
-
function isMissingPathError(error) {
|
|
3879
|
-
if (!error || typeof error !== "object") {
|
|
3880
|
-
return false;
|
|
3881
|
-
}
|
|
3882
|
-
const candidate = error;
|
|
3883
|
-
if (candidate.code === "ENOENT") {
|
|
3884
|
-
return true;
|
|
3885
|
-
}
|
|
3886
|
-
return typeof candidate.message === "string" && candidate.message.startsWith("File not found:");
|
|
3887
|
-
}
|
|
3888
|
-
function missingPathSearchResult(params) {
|
|
3889
|
-
const textPath = params.displayPath ?? params.missingPath ?? params.path;
|
|
3890
|
-
throw new ToolInputError(`Path not found: ${textPath}`);
|
|
3891
|
-
}
|
|
3892
|
-
function escapeRegExp(value) {
|
|
3893
|
-
return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
3894
|
-
}
|
|
3895
|
-
function globToRegExp(pattern) {
|
|
3896
|
-
let source = "";
|
|
3897
|
-
for (let index = 0; index < pattern.length; index += 1) {
|
|
3898
|
-
const char = pattern[index];
|
|
3899
|
-
const next = pattern[index + 1];
|
|
3900
|
-
if (char === "*" && next === "*") {
|
|
3901
|
-
if (pattern[index + 2] === "/") {
|
|
3902
|
-
source += "(?:.*/)?";
|
|
3903
|
-
index += 2;
|
|
3904
|
-
continue;
|
|
3905
|
-
}
|
|
3906
|
-
source += ".*";
|
|
3907
|
-
index += 1;
|
|
3908
|
-
continue;
|
|
3909
|
-
}
|
|
3910
|
-
if (char === "*") {
|
|
3911
|
-
source += "[^/]*";
|
|
3912
|
-
continue;
|
|
3913
|
-
}
|
|
3914
|
-
if (char === "?") {
|
|
3915
|
-
source += "[^/]";
|
|
3916
|
-
continue;
|
|
3917
|
-
}
|
|
3918
|
-
source += escapeRegExp(char);
|
|
3919
|
-
}
|
|
3920
|
-
return new RegExp(`^${source}$`);
|
|
3921
|
-
}
|
|
3922
|
-
function matchesGlob(relativePath, pattern) {
|
|
3923
|
-
const matcher = globToRegExp(pattern);
|
|
3924
|
-
if (matcher.test(relativePath)) {
|
|
3925
|
-
return true;
|
|
3926
|
-
}
|
|
3927
|
-
if (pattern.startsWith("**/") && matchesGlob(relativePath, pattern.slice(3))) {
|
|
3928
|
-
return true;
|
|
3929
|
-
}
|
|
3930
|
-
return !pattern.includes("/") && matcher.test(path4.posix.basename(relativePath));
|
|
3931
|
-
}
|
|
3932
|
-
function resolveWorkspacePath(input, fallback = ".") {
|
|
3933
|
-
const requested = (input ?? "").trim() || fallback;
|
|
3934
|
-
const absolute = requested.startsWith("/") ? requested : path4.posix.join(SANDBOX_WORKSPACE_ROOT, requested);
|
|
3935
|
-
const normalized = path4.posix.normalize(absolute);
|
|
3936
|
-
if (normalized !== SANDBOX_WORKSPACE_ROOT && !normalized.startsWith(`${SANDBOX_WORKSPACE_ROOT}/`)) {
|
|
3937
|
-
throw new ToolInputError(
|
|
3938
|
-
`Path must stay within ${SANDBOX_WORKSPACE_ROOT}: ${requested}`
|
|
3939
|
-
);
|
|
3940
|
-
}
|
|
3941
|
-
return normalized;
|
|
3942
|
-
}
|
|
3943
|
-
function getRipgrepSearchLocation(root, rootIsDirectory) {
|
|
3944
|
-
if (rootIsDirectory) {
|
|
3945
|
-
return { cwd: root, target: "." };
|
|
3946
|
-
}
|
|
3947
|
-
return {
|
|
3948
|
-
cwd: path4.posix.dirname(root),
|
|
3949
|
-
target: path4.posix.basename(root)
|
|
3950
|
-
};
|
|
3951
|
-
}
|
|
3952
|
-
async function collectFiles(params) {
|
|
3953
|
-
const files = [];
|
|
3954
|
-
let limitReached = false;
|
|
3955
|
-
let missingPath;
|
|
3956
|
-
const visit = async (dirPath) => {
|
|
3957
|
-
if (missingPath) {
|
|
3958
|
-
return;
|
|
3959
|
-
}
|
|
3960
|
-
let entries;
|
|
3961
|
-
try {
|
|
3962
|
-
entries = (await params.fs.readdir(dirPath)).sort(
|
|
3963
|
-
(a, b) => a.toLowerCase().localeCompare(b.toLowerCase())
|
|
3964
|
-
);
|
|
3965
|
-
} catch (error) {
|
|
3966
|
-
if (isMissingPathError(error)) {
|
|
3967
|
-
missingPath = dirPath;
|
|
3968
|
-
return;
|
|
3969
|
-
}
|
|
3970
|
-
throw error;
|
|
3971
|
-
}
|
|
3972
|
-
for (const entry of entries) {
|
|
3973
|
-
const fullPath = path4.posix.join(dirPath, entry);
|
|
3974
|
-
let stat2;
|
|
3975
|
-
try {
|
|
3976
|
-
stat2 = await params.fs.stat(fullPath);
|
|
3977
|
-
} catch (error) {
|
|
3978
|
-
if (isMissingPathError(error)) {
|
|
3979
|
-
missingPath = fullPath;
|
|
3980
|
-
return;
|
|
3981
|
-
}
|
|
3982
|
-
throw error;
|
|
3983
|
-
}
|
|
3984
|
-
if (stat2.isDirectory()) {
|
|
3985
|
-
if (!SKIPPED_DIRECTORIES.has(entry)) {
|
|
3986
|
-
await visit(fullPath);
|
|
3987
|
-
}
|
|
3988
|
-
if (limitReached) return;
|
|
3989
|
-
continue;
|
|
3990
|
-
}
|
|
3991
|
-
const relativePath = path4.posix.relative(params.root, fullPath);
|
|
3992
|
-
if (!params.pattern || matchesGlob(relativePath, params.pattern)) {
|
|
3993
|
-
files.push(fullPath);
|
|
3994
|
-
if (params.limit && files.length >= params.limit) {
|
|
3995
|
-
limitReached = true;
|
|
3996
|
-
return;
|
|
3997
|
-
}
|
|
3998
|
-
}
|
|
3999
|
-
}
|
|
4000
|
-
};
|
|
4001
|
-
let stat;
|
|
4002
|
-
try {
|
|
4003
|
-
stat = await params.fs.stat(params.root);
|
|
4004
|
-
} catch (error) {
|
|
4005
|
-
if (isMissingPathError(error)) {
|
|
4006
|
-
return {
|
|
4007
|
-
files,
|
|
4008
|
-
limitReached: false,
|
|
4009
|
-
missingPath: params.root,
|
|
4010
|
-
missingRoot: true
|
|
4011
|
-
};
|
|
4012
|
-
}
|
|
4013
|
-
throw error;
|
|
4014
|
-
}
|
|
4015
|
-
if (!stat.isDirectory()) {
|
|
4016
|
-
const relativePath = path4.posix.basename(params.root);
|
|
4017
|
-
return {
|
|
4018
|
-
files: !params.pattern || matchesGlob(relativePath, params.pattern) ? [params.root] : [],
|
|
4019
|
-
limitReached: false,
|
|
4020
|
-
missingRoot: false
|
|
4021
|
-
};
|
|
4022
|
-
}
|
|
4023
|
-
await visit(params.root);
|
|
4024
|
-
return {
|
|
4025
|
-
files,
|
|
4026
|
-
limitReached,
|
|
4027
|
-
missingPath,
|
|
4028
|
-
missingRoot: missingPath === params.root
|
|
4029
|
-
};
|
|
4030
|
-
}
|
|
4031
|
-
|
|
4032
3943
|
// src/chat/tools/sandbox/text-edits.ts
|
|
4033
3944
|
var MAX_DIFF_LINE_CHARS = 4e3;
|
|
4034
3945
|
function truncateDiffLine(value) {
|
|
@@ -4094,12 +4005,12 @@ function prepareTextReplacementArguments(input) {
|
|
|
4094
4005
|
if (!edit || typeof edit !== "object") {
|
|
4095
4006
|
return edit;
|
|
4096
4007
|
}
|
|
4097
|
-
const
|
|
4098
|
-
const { old_text, new_text, ...rest } =
|
|
4008
|
+
const record2 = edit;
|
|
4009
|
+
const { old_text, new_text, ...rest } = record2;
|
|
4099
4010
|
return {
|
|
4100
4011
|
...rest,
|
|
4101
|
-
oldText:
|
|
4102
|
-
newText:
|
|
4012
|
+
oldText: record2.oldText ?? old_text,
|
|
4013
|
+
newText: record2.newText ?? new_text
|
|
4103
4014
|
};
|
|
4104
4015
|
});
|
|
4105
4016
|
}
|
|
@@ -4242,8 +4153,8 @@ function parseToolContent(content) {
|
|
|
4242
4153
|
}
|
|
4243
4154
|
return content;
|
|
4244
4155
|
}
|
|
4245
|
-
function formatZodPath(
|
|
4246
|
-
return
|
|
4156
|
+
function formatZodPath(path8) {
|
|
4157
|
+
return path8.length > 0 ? path8.map(String).join(".") : "root";
|
|
4247
4158
|
}
|
|
4248
4159
|
function formatToolInputError(error) {
|
|
4249
4160
|
const details = error.issues.slice(0, 5).map((issue) => `${formatZodPath(issue.path)}: ${issue.message}`).join("; ");
|
|
@@ -4390,7 +4301,7 @@ function createEditFileTool() {
|
|
|
4390
4301
|
}
|
|
4391
4302
|
|
|
4392
4303
|
// src/chat/tools/sandbox/find-files.ts
|
|
4393
|
-
import
|
|
4304
|
+
import path4 from "path";
|
|
4394
4305
|
import { z as z5 } from "zod";
|
|
4395
4306
|
var DEFAULT_FIND_LIMIT = 100;
|
|
4396
4307
|
async function findFiles(params) {
|
|
@@ -4423,7 +4334,7 @@ async function findFiles(params) {
|
|
|
4423
4334
|
});
|
|
4424
4335
|
}
|
|
4425
4336
|
const relativePaths = files.map(
|
|
4426
|
-
(filePath) =>
|
|
4337
|
+
(filePath) => path4.posix.relative(root, filePath)
|
|
4427
4338
|
);
|
|
4428
4339
|
const bounded = truncateText(
|
|
4429
4340
|
relativePaths.length > 0 ? relativePaths.join("\n") : "No files found matching pattern"
|
|
@@ -4509,8 +4420,8 @@ async function findFilesWithRipgrep(params) {
|
|
|
4509
4420
|
throw new Error(`ripgrep file search failed: ${detail}`);
|
|
4510
4421
|
}
|
|
4511
4422
|
const allPaths = result.stdout.split("\0").filter(Boolean).map((filePath) => {
|
|
4512
|
-
const absolute = filePath.startsWith("/") ?
|
|
4513
|
-
return rootIsDirectory ?
|
|
4423
|
+
const absolute = filePath.startsWith("/") ? path4.posix.normalize(filePath) : path4.posix.resolve(location.cwd, filePath);
|
|
4424
|
+
return rootIsDirectory ? path4.posix.relative(params.root, absolute) : path4.posix.basename(absolute);
|
|
4514
4425
|
});
|
|
4515
4426
|
const limitReached = allPaths.length > params.limit;
|
|
4516
4427
|
const relativePaths = allPaths.slice(0, params.limit);
|
|
@@ -4583,7 +4494,7 @@ function createFindFilesTool() {
|
|
|
4583
4494
|
}
|
|
4584
4495
|
|
|
4585
4496
|
// src/chat/tools/sandbox/grep.ts
|
|
4586
|
-
import
|
|
4497
|
+
import path5 from "path";
|
|
4587
4498
|
import { z as z6 } from "zod";
|
|
4588
4499
|
var DEFAULT_GREP_LIMIT = 100;
|
|
4589
4500
|
var MAX_GREP_LINE_CHARS = 500;
|
|
@@ -4674,7 +4585,7 @@ async function grepFiles(params) {
|
|
|
4674
4585
|
continue;
|
|
4675
4586
|
}
|
|
4676
4587
|
const lines = normalizeToLf(content).split("\n");
|
|
4677
|
-
const relativePath = files.length === 1 && filePath === root ?
|
|
4588
|
+
const relativePath = files.length === 1 && filePath === root ? path5.posix.basename(filePath) : path5.posix.relative(root, filePath);
|
|
4678
4589
|
const matchedLines = [];
|
|
4679
4590
|
for (let lineIndex = 0; lineIndex < lines.length; lineIndex += 1) {
|
|
4680
4591
|
if (!lineMatches({
|
|
@@ -4813,37 +4724,37 @@ async function grepFilesWithRipgrep(params) {
|
|
|
4813
4724
|
if (!rawLine) {
|
|
4814
4725
|
continue;
|
|
4815
4726
|
}
|
|
4816
|
-
let
|
|
4727
|
+
let record2;
|
|
4817
4728
|
try {
|
|
4818
|
-
|
|
4729
|
+
record2 = JSON.parse(rawLine);
|
|
4819
4730
|
} catch (error) {
|
|
4820
4731
|
throw new Error("ripgrep returned invalid JSON output", { cause: error });
|
|
4821
4732
|
}
|
|
4822
4733
|
parsedRecordCount += 1;
|
|
4823
|
-
if (
|
|
4734
|
+
if (record2.type !== "match" && record2.type !== "context") {
|
|
4824
4735
|
continue;
|
|
4825
4736
|
}
|
|
4826
|
-
const line =
|
|
4827
|
-
const rawPath = decodeRipgrepText(
|
|
4737
|
+
const line = record2.data?.line_number;
|
|
4738
|
+
const rawPath = decodeRipgrepText(record2.data?.path);
|
|
4828
4739
|
if (!line || !rawPath) {
|
|
4829
4740
|
continue;
|
|
4830
4741
|
}
|
|
4831
|
-
const absolutePath = rawPath.startsWith("/") ?
|
|
4832
|
-
const displayPath = rootIsDirectory ?
|
|
4742
|
+
const absolutePath = rawPath.startsWith("/") ? path5.posix.normalize(rawPath) : path5.posix.resolve(location.cwd, rawPath);
|
|
4743
|
+
const displayPath = rootIsDirectory ? path5.posix.relative(params.root, absolutePath) : path5.posix.basename(absolutePath);
|
|
4833
4744
|
let records = recordsByPath.get(displayPath);
|
|
4834
4745
|
if (!records) {
|
|
4835
4746
|
records = [];
|
|
4836
4747
|
recordsByPath.set(displayPath, records);
|
|
4837
4748
|
pathOrder.push(displayPath);
|
|
4838
4749
|
}
|
|
4839
|
-
const matched =
|
|
4750
|
+
const matched = record2.type === "match";
|
|
4840
4751
|
if (matched) {
|
|
4841
4752
|
totalMatches += 1;
|
|
4842
4753
|
}
|
|
4843
4754
|
records.push({
|
|
4844
4755
|
line,
|
|
4845
4756
|
matched,
|
|
4846
|
-
text: decodeRipgrepText(
|
|
4757
|
+
text: decodeRipgrepText(record2.data?.lines).replace(/\r?\n$/, "")
|
|
4847
4758
|
});
|
|
4848
4759
|
}
|
|
4849
4760
|
let remainingMatches = params.limit;
|
|
@@ -4852,9 +4763,9 @@ async function grepFilesWithRipgrep(params) {
|
|
|
4852
4763
|
for (const displayPath of pathOrder) {
|
|
4853
4764
|
const records = recordsByPath.get(displayPath) ?? [];
|
|
4854
4765
|
const selectedMatchLines = [];
|
|
4855
|
-
for (const
|
|
4856
|
-
if (
|
|
4857
|
-
selectedMatchLines.push(
|
|
4766
|
+
for (const record2 of records) {
|
|
4767
|
+
if (record2.matched && remainingMatches > 0) {
|
|
4768
|
+
selectedMatchLines.push(record2.line);
|
|
4858
4769
|
remainingMatches -= 1;
|
|
4859
4770
|
}
|
|
4860
4771
|
}
|
|
@@ -4863,22 +4774,22 @@ async function grepFilesWithRipgrep(params) {
|
|
|
4863
4774
|
}
|
|
4864
4775
|
const selectedMatchSet = new Set(selectedMatchLines);
|
|
4865
4776
|
const emittedLines = /* @__PURE__ */ new Set();
|
|
4866
|
-
for (const
|
|
4867
|
-
if (emittedLines.has(
|
|
4777
|
+
for (const record2 of records) {
|
|
4778
|
+
if (emittedLines.has(record2.line)) {
|
|
4868
4779
|
continue;
|
|
4869
4780
|
}
|
|
4870
4781
|
const include = selectedMatchLines.some(
|
|
4871
|
-
(matchLine) => Math.abs(
|
|
4782
|
+
(matchLine) => Math.abs(record2.line - matchLine) <= params.context
|
|
4872
4783
|
);
|
|
4873
4784
|
if (!include) {
|
|
4874
4785
|
continue;
|
|
4875
4786
|
}
|
|
4876
|
-
emittedLines.add(
|
|
4877
|
-
const truncated = truncateGrepLine(
|
|
4787
|
+
emittedLines.add(record2.line);
|
|
4788
|
+
const truncated = truncateGrepLine(record2.text);
|
|
4878
4789
|
lineTruncated ||= truncated.truncated;
|
|
4879
|
-
const separator = selectedMatchSet.has(
|
|
4790
|
+
const separator = selectedMatchSet.has(record2.line) ? ":" : "-";
|
|
4880
4791
|
output.push(
|
|
4881
|
-
`${displayPath}${separator}${
|
|
4792
|
+
`${displayPath}${separator}${record2.line}${separator} ${truncated.line}`
|
|
4882
4793
|
);
|
|
4883
4794
|
}
|
|
4884
4795
|
}
|
|
@@ -4961,7 +4872,7 @@ function createGrepTool() {
|
|
|
4961
4872
|
}
|
|
4962
4873
|
|
|
4963
4874
|
// src/chat/tools/sandbox/list-dir.ts
|
|
4964
|
-
import
|
|
4875
|
+
import path6 from "path";
|
|
4965
4876
|
import { z as z7 } from "zod";
|
|
4966
4877
|
var DEFAULT_LIST_LIMIT = 500;
|
|
4967
4878
|
async function listDir(params) {
|
|
@@ -4997,7 +4908,7 @@ async function listDir(params) {
|
|
|
4997
4908
|
entryLimitReached = true;
|
|
4998
4909
|
break;
|
|
4999
4910
|
}
|
|
5000
|
-
const entryPath =
|
|
4911
|
+
const entryPath = path6.posix.join(dirPath, entry);
|
|
5001
4912
|
try {
|
|
5002
4913
|
const entryStat = await params.fs.stat(entryPath);
|
|
5003
4914
|
output.push(`${entry}${entryStat.isDirectory() ? "/" : ""}`);
|
|
@@ -5139,8 +5050,8 @@ function sliceFileContent(params) {
|
|
|
5139
5050
|
} : {}
|
|
5140
5051
|
});
|
|
5141
5052
|
}
|
|
5142
|
-
function missingFileResult(
|
|
5143
|
-
throw new ToolInputError(`File not found: ${
|
|
5053
|
+
function missingFileResult(path8) {
|
|
5054
|
+
throw new ToolInputError(`File not found: ${path8}`);
|
|
5144
5055
|
}
|
|
5145
5056
|
|
|
5146
5057
|
// src/chat/tools/sandbox/read-file.ts
|
|
@@ -5324,6 +5235,7 @@ function createSandbox(options) {
|
|
|
5324
5235
|
};
|
|
5325
5236
|
const executeBashTool = async (rawInput, command, context) => {
|
|
5326
5237
|
const env = parseEnv(rawInput.env);
|
|
5238
|
+
const cwd = typeof rawInput.cwd === "string" ? resolveWorkspacePath(rawInput.cwd) : SANDBOX_WORKSPACE_ROOT;
|
|
5327
5239
|
const timeoutMs = positiveInteger(rawInput.timeoutMs);
|
|
5328
5240
|
logSandboxBootRequest("tool.bash", {
|
|
5329
5241
|
"app.sandbox.command_length": command.length
|
|
@@ -5340,6 +5252,7 @@ function createSandbox(options) {
|
|
|
5340
5252
|
try {
|
|
5341
5253
|
const response = await executeBash({
|
|
5342
5254
|
command,
|
|
5255
|
+
cwd,
|
|
5343
5256
|
...env ? { env } : {},
|
|
5344
5257
|
...timeoutMs ? { timeoutMs } : {},
|
|
5345
5258
|
...context.signal ? { signal: context.signal } : {}
|
|
@@ -5371,7 +5284,7 @@ function createSandbox(options) {
|
|
|
5371
5284
|
return formatSandboxCommandResult({
|
|
5372
5285
|
ok: result.exitCode === 0,
|
|
5373
5286
|
command,
|
|
5374
|
-
cwd
|
|
5287
|
+
cwd,
|
|
5375
5288
|
exit_code: result.exitCode,
|
|
5376
5289
|
signal: null,
|
|
5377
5290
|
timed_out: Boolean(result.timedOut),
|
|
@@ -5695,6 +5608,18 @@ function createSandbox(options) {
|
|
|
5695
5608
|
})
|
|
5696
5609
|
};
|
|
5697
5610
|
return {
|
|
5611
|
+
async captureRepositoryInstructions() {
|
|
5612
|
+
if (!runtime.sandboxRef()) {
|
|
5613
|
+
return void 0;
|
|
5614
|
+
}
|
|
5615
|
+
const { fs: fs5 } = await runtime.tools();
|
|
5616
|
+
const selected = await findSingleRepositoryDirectory(fs5);
|
|
5617
|
+
if (!selected) return void 0;
|
|
5618
|
+
return await resolveRepositoryInstructions({
|
|
5619
|
+
cwd: selected,
|
|
5620
|
+
fs: fs5
|
|
5621
|
+
});
|
|
5622
|
+
},
|
|
5698
5623
|
workspace,
|
|
5699
5624
|
tools: {
|
|
5700
5625
|
supports(toolName) {
|
|
@@ -5712,7 +5637,7 @@ function createSandbox(options) {
|
|
|
5712
5637
|
}
|
|
5713
5638
|
|
|
5714
5639
|
// src/chat/tools/sandbox/file-uploads.ts
|
|
5715
|
-
import
|
|
5640
|
+
import path7 from "path";
|
|
5716
5641
|
import { z as z9 } from "zod";
|
|
5717
5642
|
var MAX_SANDBOX_FILE_UPLOAD_BYTES = 10 * 1024 * 1024;
|
|
5718
5643
|
var SANDBOX_ARTIFACTS_DIR = "/tmp/junior/artifacts";
|
|
@@ -5748,9 +5673,9 @@ var MIME_BY_EXTENSION = {
|
|
|
5748
5673
|
".log": "text/plain"
|
|
5749
5674
|
};
|
|
5750
5675
|
var SandboxFileNotFoundError = class extends ToolInputError {
|
|
5751
|
-
constructor(
|
|
5752
|
-
super(`failed to read file: ${
|
|
5753
|
-
this.path =
|
|
5676
|
+
constructor(path8) {
|
|
5677
|
+
super(`failed to read file: ${path8}`);
|
|
5678
|
+
this.path = path8;
|
|
5754
5679
|
}
|
|
5755
5680
|
path;
|
|
5756
5681
|
};
|
|
@@ -5759,27 +5684,27 @@ function normalizeSandboxPath(inputPath) {
|
|
|
5759
5684
|
if (!trimmed) {
|
|
5760
5685
|
throw new Error("path is required");
|
|
5761
5686
|
}
|
|
5762
|
-
if (
|
|
5687
|
+
if (path7.posix.isAbsolute(trimmed)) {
|
|
5763
5688
|
return trimmed;
|
|
5764
5689
|
}
|
|
5765
|
-
return
|
|
5690
|
+
return path7.posix.join(SANDBOX_WORKSPACE_ROOT, trimmed);
|
|
5766
5691
|
}
|
|
5767
5692
|
function sanitizeFilename(value, fallbackPath) {
|
|
5768
5693
|
const candidate = (value ?? "").trim();
|
|
5769
5694
|
if (candidate) {
|
|
5770
|
-
const base =
|
|
5695
|
+
const base = path7.posix.basename(candidate);
|
|
5771
5696
|
if (base && base !== "." && base !== "..") {
|
|
5772
5697
|
return base;
|
|
5773
5698
|
}
|
|
5774
5699
|
}
|
|
5775
|
-
const derived =
|
|
5700
|
+
const derived = path7.posix.basename(fallbackPath);
|
|
5776
5701
|
if (derived && derived !== "." && derived !== "..") {
|
|
5777
5702
|
return derived;
|
|
5778
5703
|
}
|
|
5779
5704
|
return "attachment.bin";
|
|
5780
5705
|
}
|
|
5781
5706
|
function sandboxArtifactPath(filename) {
|
|
5782
|
-
return
|
|
5707
|
+
return path7.posix.join(
|
|
5783
5708
|
SANDBOX_ARTIFACTS_DIR,
|
|
5784
5709
|
sanitizeFilename(filename, "artifact.bin")
|
|
5785
5710
|
);
|
|
@@ -5789,7 +5714,7 @@ function inferMimeType(filename, explicitMimeType) {
|
|
|
5789
5714
|
if (explicit) {
|
|
5790
5715
|
return explicit;
|
|
5791
5716
|
}
|
|
5792
|
-
const ext =
|
|
5717
|
+
const ext = path7.extname(filename).toLowerCase();
|
|
5793
5718
|
return MIME_BY_EXTENSION[ext] ?? "application/octet-stream";
|
|
5794
5719
|
}
|
|
5795
5720
|
async function detectMimeType(workspace, targetPath) {
|
|
@@ -5880,94 +5805,321 @@ async function writeSandboxGeneratedArtifacts(workspace, files) {
|
|
|
5880
5805
|
return artifacts.map((artifact) => artifact.ref);
|
|
5881
5806
|
}
|
|
5882
5807
|
|
|
5883
|
-
// src/chat/
|
|
5884
|
-
function
|
|
5885
|
-
|
|
5886
|
-
|
|
5887
|
-
|
|
5888
|
-
const command = input.command;
|
|
5889
|
-
return typeof command === "string" && command.trim() ? command.trim() : void 0;
|
|
5890
|
-
}
|
|
5891
|
-
function createAgentSandbox(options) {
|
|
5892
|
-
const sandbox = createSandbox({
|
|
5893
|
-
sandboxRef: options.sandboxRef,
|
|
5894
|
-
skills: options.skills,
|
|
5895
|
-
referenceFiles: listReferenceFiles(),
|
|
5896
|
-
traceContext: options.traceContext,
|
|
5897
|
-
tracePropagation: options.tracePropagation,
|
|
5898
|
-
egressSignals: options.egressSignals,
|
|
5899
|
-
credentialEgress: options.credentialEgress,
|
|
5900
|
-
prepare: options.prepareSandbox,
|
|
5901
|
-
onSandboxRefChanged: async (sandboxRef) => {
|
|
5902
|
-
options.onSandboxRefChanged(sandboxRef);
|
|
5903
|
-
await options.persistSandboxRef?.(sandboxRef);
|
|
5808
|
+
// src/chat/tool-support/normalize-result.ts
|
|
5809
|
+
function isToolContent(value) {
|
|
5810
|
+
return Array.isArray(value) && value.every((part) => {
|
|
5811
|
+
if (!part || typeof part !== "object") {
|
|
5812
|
+
return false;
|
|
5904
5813
|
}
|
|
5905
|
-
|
|
5906
|
-
|
|
5907
|
-
|
|
5908
|
-
sandboxRef: sandbox.sandboxRef,
|
|
5909
|
-
close: sandbox.close,
|
|
5910
|
-
tools: {
|
|
5911
|
-
supports: sandbox.tools.supports,
|
|
5912
|
-
async execute(params) {
|
|
5913
|
-
const command = params.toolName === "bash" ? bashCommand(params.input) : void 0;
|
|
5914
|
-
if (command) {
|
|
5915
|
-
const result = await maybeExecuteJrRpcCustomCommand(command, {
|
|
5916
|
-
activeSkill: options.getActiveSkill(),
|
|
5917
|
-
channelConfiguration: options.channelConfiguration,
|
|
5918
|
-
actorId: isUserActor(options.actor) ? options.actor.userId : void 0,
|
|
5919
|
-
onConfigurationValueChanged: (key, value) => {
|
|
5920
|
-
if (value === void 0) {
|
|
5921
|
-
delete options.configurationValues[key];
|
|
5922
|
-
return;
|
|
5923
|
-
}
|
|
5924
|
-
options.configurationValues[key] = value;
|
|
5925
|
-
}
|
|
5926
|
-
});
|
|
5927
|
-
if (result.handled) {
|
|
5928
|
-
return formatSandboxCommandResult(result.result);
|
|
5929
|
-
}
|
|
5930
|
-
}
|
|
5931
|
-
return await sandbox.tools.execute(params);
|
|
5932
|
-
}
|
|
5933
|
-
},
|
|
5934
|
-
async writeGeneratedArtifacts(files) {
|
|
5935
|
-
return await writeSandboxGeneratedArtifacts(sandbox.workspace, files);
|
|
5814
|
+
const record2 = part;
|
|
5815
|
+
if (record2.type === "text") {
|
|
5816
|
+
return typeof record2.text === "string";
|
|
5936
5817
|
}
|
|
5937
|
-
|
|
5818
|
+
if (record2.type === "image") {
|
|
5819
|
+
return typeof record2.data === "string" && typeof record2.mimeType === "string";
|
|
5820
|
+
}
|
|
5821
|
+
return false;
|
|
5822
|
+
});
|
|
5938
5823
|
}
|
|
5939
|
-
|
|
5940
|
-
|
|
5941
|
-
|
|
5942
|
-
missingToolAnnotationKeys
|
|
5943
|
-
} from "@sentry/junior-plugin-api";
|
|
5944
|
-
|
|
5945
|
-
// src/chat/mcp/client.ts
|
|
5946
|
-
import { AsyncLocalStorage } from "async_hooks";
|
|
5947
|
-
import { Client } from "@modelcontextprotocol/sdk/client";
|
|
5948
|
-
import {
|
|
5949
|
-
UnauthorizedError
|
|
5950
|
-
} from "@modelcontextprotocol/sdk/client/auth.js";
|
|
5951
|
-
import {
|
|
5952
|
-
StreamableHTTPClientTransport,
|
|
5953
|
-
StreamableHTTPError
|
|
5954
|
-
} from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
5955
|
-
var MCP_CLIENT_INFO = {
|
|
5956
|
-
name: "junior-mcp-client",
|
|
5957
|
-
version: "1.0.0"
|
|
5958
|
-
};
|
|
5959
|
-
var MCP_TOOLS_CALL_METHOD = "tools/call";
|
|
5960
|
-
function getDefaultPort(url) {
|
|
5961
|
-
if (url.protocol === "https:") {
|
|
5962
|
-
return 443;
|
|
5963
|
-
}
|
|
5964
|
-
if (url.protocol === "http:") {
|
|
5965
|
-
return 80;
|
|
5966
|
-
}
|
|
5967
|
-
return void 0;
|
|
5824
|
+
function isStructuredToolExecutionResult(value) {
|
|
5825
|
+
const content = value?.content;
|
|
5826
|
+
return typeof value === "object" && value !== null && isToolContent(content) && "details" in value;
|
|
5968
5827
|
}
|
|
5969
|
-
function
|
|
5970
|
-
const
|
|
5828
|
+
function isContentOnlyToolExecutionResult(value) {
|
|
5829
|
+
const content = value?.content;
|
|
5830
|
+
return typeof value === "object" && value !== null && isToolContent(content) && !("details" in value);
|
|
5831
|
+
}
|
|
5832
|
+
function toToolContentText(value) {
|
|
5833
|
+
if (typeof value === "string") return value;
|
|
5834
|
+
try {
|
|
5835
|
+
return JSON.stringify(value);
|
|
5836
|
+
} catch {
|
|
5837
|
+
return String(value);
|
|
5838
|
+
}
|
|
5839
|
+
}
|
|
5840
|
+
function withoutFirstGeneratedTextContent(content) {
|
|
5841
|
+
const [first, ...rest] = content;
|
|
5842
|
+
if (first?.type === "text") {
|
|
5843
|
+
return rest;
|
|
5844
|
+
}
|
|
5845
|
+
return content;
|
|
5846
|
+
}
|
|
5847
|
+
function replaceGeneratedTextContent(content, text) {
|
|
5848
|
+
return [
|
|
5849
|
+
{
|
|
5850
|
+
type: "text",
|
|
5851
|
+
text
|
|
5852
|
+
},
|
|
5853
|
+
...withoutFirstGeneratedTextContent(content)
|
|
5854
|
+
];
|
|
5855
|
+
}
|
|
5856
|
+
function isRecord3(value) {
|
|
5857
|
+
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
5858
|
+
}
|
|
5859
|
+
function hasContinuation(value) {
|
|
5860
|
+
return isRecord3(value) && isRecord3(value.continuation);
|
|
5861
|
+
}
|
|
5862
|
+
function stringField(record2, key) {
|
|
5863
|
+
const value = record2[key];
|
|
5864
|
+
return typeof value === "string" ? value : "";
|
|
5865
|
+
}
|
|
5866
|
+
function numberField(record2, key) {
|
|
5867
|
+
const value = record2[key];
|
|
5868
|
+
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
5869
|
+
}
|
|
5870
|
+
function stringListField(record2, key) {
|
|
5871
|
+
const value = record2[key];
|
|
5872
|
+
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
5873
|
+
}
|
|
5874
|
+
function accountText(value) {
|
|
5875
|
+
if (!isRecord3(value)) {
|
|
5876
|
+
return void 0;
|
|
5877
|
+
}
|
|
5878
|
+
const label = stringField(value, "label") || stringField(value, "id");
|
|
5879
|
+
const id = stringField(value, "id");
|
|
5880
|
+
if (!label) {
|
|
5881
|
+
return void 0;
|
|
5882
|
+
}
|
|
5883
|
+
return id && id !== label ? `${label} (${id})` : label;
|
|
5884
|
+
}
|
|
5885
|
+
function upstreamPermissionDeniedText(value) {
|
|
5886
|
+
if (!isRecord3(value) || !isRecord3(value.permission_denied)) {
|
|
5887
|
+
return void 0;
|
|
5888
|
+
}
|
|
5889
|
+
const signal = value.permission_denied;
|
|
5890
|
+
if (signal.source !== "upstream") {
|
|
5891
|
+
return void 0;
|
|
5892
|
+
}
|
|
5893
|
+
const provider = stringField(signal, "provider");
|
|
5894
|
+
const message = stringField(signal, "message");
|
|
5895
|
+
const upstreamHost = stringField(signal, "upstreamHost");
|
|
5896
|
+
const upstreamPath = stringField(signal, "upstreamPath");
|
|
5897
|
+
const status = numberField(signal, "status");
|
|
5898
|
+
if (!provider || !message || !upstreamHost || !upstreamPath || !status) {
|
|
5899
|
+
return void 0;
|
|
5900
|
+
}
|
|
5901
|
+
const grant = isRecord3(signal.grant) ? signal.grant : {};
|
|
5902
|
+
const grantName = stringField(grant, "name");
|
|
5903
|
+
const grantAccess = stringField(grant, "access");
|
|
5904
|
+
const grantReason = stringField(grant, "reason");
|
|
5905
|
+
const grantRequirements = stringListField(grant, "requirements");
|
|
5906
|
+
const account = accountText(signal.account);
|
|
5907
|
+
const command = stringField(value, "command");
|
|
5908
|
+
const stderr = stringField(value, "stderr").trim();
|
|
5909
|
+
const stdout = stringField(value, "stdout").trim();
|
|
5910
|
+
const acceptedPermissions = stringField(signal, "acceptedPermissions");
|
|
5911
|
+
const sso = stringField(signal, "sso");
|
|
5912
|
+
return [
|
|
5913
|
+
"Upstream permission denied.",
|
|
5914
|
+
message,
|
|
5915
|
+
"",
|
|
5916
|
+
`Provider: ${provider}`,
|
|
5917
|
+
...account ? [`Provider account: ${account}`] : [],
|
|
5918
|
+
`Grant: ${grantName || "unknown"}${grantAccess ? ` (${grantAccess}${grantReason ? `, ${grantReason}` : ""})` : ""}`,
|
|
5919
|
+
...grantRequirements.length > 0 ? [
|
|
5920
|
+
"Provider requirements:",
|
|
5921
|
+
...grantRequirements.map((item) => `- ${item}`)
|
|
5922
|
+
] : [],
|
|
5923
|
+
`Upstream: ${upstreamHost}${upstreamPath}`,
|
|
5924
|
+
`Status: ${status}`,
|
|
5925
|
+
...acceptedPermissions ? [`Accepted provider permissions: ${acceptedPermissions}`] : [],
|
|
5926
|
+
...sso ? [`Provider SSO: ${sso}`] : [],
|
|
5927
|
+
...command ? [`Command: ${command}`] : [],
|
|
5928
|
+
"",
|
|
5929
|
+
"Junior had a credential lease for this grant and forwarded the request. Do not diagnose this as a missing user token or a local Junior runtime block; diagnose provider-side permissions, installation scope, SSO, or actor-provider account access.",
|
|
5930
|
+
...stderr ? ["", `stderr:
|
|
5931
|
+
${stderr}`] : [],
|
|
5932
|
+
...stdout ? ["", `stdout:
|
|
5933
|
+
${stdout}`] : []
|
|
5934
|
+
].join("\n");
|
|
5935
|
+
}
|
|
5936
|
+
function normalizeDetails(details, options) {
|
|
5937
|
+
const continuationToolName = options.toolName && hasContinuation(details) ? options.toolName : void 0;
|
|
5938
|
+
if (!continuationToolName) {
|
|
5939
|
+
return { details, replaceEnvelopeText: false };
|
|
5940
|
+
}
|
|
5941
|
+
return {
|
|
5942
|
+
details: injectContinuationToolName(details, continuationToolName),
|
|
5943
|
+
replaceEnvelopeText: true
|
|
5944
|
+
};
|
|
5945
|
+
}
|
|
5946
|
+
function normalizeToolResult(result, options = {}) {
|
|
5947
|
+
const unwrapped = result;
|
|
5948
|
+
if (isStructuredToolExecutionResult(unwrapped)) {
|
|
5949
|
+
const normalized2 = normalizeDetails(unwrapped.details, options);
|
|
5950
|
+
const permissionText = upstreamPermissionDeniedText(normalized2.details);
|
|
5951
|
+
if (!permissionText && normalized2.details === unwrapped.details) {
|
|
5952
|
+
return unwrapped;
|
|
5953
|
+
}
|
|
5954
|
+
const content = permissionText || normalized2.replaceEnvelopeText ? replaceGeneratedTextContent(
|
|
5955
|
+
unwrapped.content,
|
|
5956
|
+
permissionText ?? toToolContentText(normalized2.details)
|
|
5957
|
+
) : unwrapped.content;
|
|
5958
|
+
return {
|
|
5959
|
+
content,
|
|
5960
|
+
details: normalized2.details
|
|
5961
|
+
};
|
|
5962
|
+
}
|
|
5963
|
+
if (isContentOnlyToolExecutionResult(unwrapped)) {
|
|
5964
|
+
if (options.requireStructuredResult) {
|
|
5965
|
+
throw new TypeError(
|
|
5966
|
+
"Structured tools must return details matching their outputSchema."
|
|
5967
|
+
);
|
|
5968
|
+
}
|
|
5969
|
+
return {
|
|
5970
|
+
content: unwrapped.content,
|
|
5971
|
+
details: {}
|
|
5972
|
+
};
|
|
5973
|
+
}
|
|
5974
|
+
const normalized = normalizeDetails(unwrapped, options);
|
|
5975
|
+
return {
|
|
5976
|
+
content: [
|
|
5977
|
+
{
|
|
5978
|
+
type: "text",
|
|
5979
|
+
text: upstreamPermissionDeniedText(normalized.details) ?? toToolContentText(normalized.details)
|
|
5980
|
+
}
|
|
5981
|
+
],
|
|
5982
|
+
details: normalized.details
|
|
5983
|
+
};
|
|
5984
|
+
}
|
|
5985
|
+
|
|
5986
|
+
// src/chat/agent/sandbox.ts
|
|
5987
|
+
function record(value) {
|
|
5988
|
+
return value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
5989
|
+
}
|
|
5990
|
+
function textContent(value) {
|
|
5991
|
+
return Array.isArray(value) ? value.flatMap((part) => {
|
|
5992
|
+
const item = record(part);
|
|
5993
|
+
return item.type === "text" && typeof item.text === "string" ? [item.text] : [];
|
|
5994
|
+
}).join("\n") : "";
|
|
5995
|
+
}
|
|
5996
|
+
function createPluginToolSandbox(sandbox, options) {
|
|
5997
|
+
return {
|
|
5998
|
+
root: SANDBOX_WORKSPACE_ROOT,
|
|
5999
|
+
juniorRoot: `${SANDBOX_WORKSPACE_ROOT}/.junior`,
|
|
6000
|
+
async readFile(filePath) {
|
|
6001
|
+
return await sandbox.workspace.readFileToBuffer({ path: filePath }) ?? null;
|
|
6002
|
+
},
|
|
6003
|
+
async run(input) {
|
|
6004
|
+
input.signal?.throwIfAborted();
|
|
6005
|
+
const result = await sandbox.tools.execute({
|
|
6006
|
+
toolName: "bash",
|
|
6007
|
+
input: {
|
|
6008
|
+
command: `${input.sudo ? "sudo -- " : ""}${buildCommandScript(input)}`,
|
|
6009
|
+
...input.cwd ? { cwd: input.cwd } : {},
|
|
6010
|
+
...input.env ? { env: input.env } : {}
|
|
6011
|
+
},
|
|
6012
|
+
...input.signal ? { signal: input.signal } : {}
|
|
6013
|
+
});
|
|
6014
|
+
const normalized = normalizeToolResult(result, { toolName: "bash" });
|
|
6015
|
+
await options.handleAuthSignal(normalized.details);
|
|
6016
|
+
input.signal?.throwIfAborted();
|
|
6017
|
+
const details = record(normalized.details);
|
|
6018
|
+
return {
|
|
6019
|
+
exitCode: typeof details.exit_code === "number" ? details.exit_code : 1,
|
|
6020
|
+
stdout: typeof details.stdout === "string" ? details.stdout : "",
|
|
6021
|
+
stderr: details.permission_denied !== void 0 ? textContent(normalized.content) : typeof details.stderr === "string" ? details.stderr : ""
|
|
6022
|
+
};
|
|
6023
|
+
},
|
|
6024
|
+
async writeFile(input) {
|
|
6025
|
+
await sandbox.workspace.writeFiles([
|
|
6026
|
+
{
|
|
6027
|
+
path: input.path,
|
|
6028
|
+
content: input.content,
|
|
6029
|
+
...input.mode !== void 0 ? { mode: input.mode } : {}
|
|
6030
|
+
}
|
|
6031
|
+
]);
|
|
6032
|
+
}
|
|
6033
|
+
};
|
|
6034
|
+
}
|
|
6035
|
+
function bashCommand(input) {
|
|
6036
|
+
if (!input || typeof input !== "object" || Array.isArray(input)) {
|
|
6037
|
+
return void 0;
|
|
6038
|
+
}
|
|
6039
|
+
const command = input.command;
|
|
6040
|
+
return typeof command === "string" && command.trim() ? command.trim() : void 0;
|
|
6041
|
+
}
|
|
6042
|
+
function createAgentSandbox(options) {
|
|
6043
|
+
const sandbox = createSandbox({
|
|
6044
|
+
sandboxRef: options.sandboxRef,
|
|
6045
|
+
skills: options.skills,
|
|
6046
|
+
referenceFiles: listReferenceFiles(),
|
|
6047
|
+
traceContext: options.traceContext,
|
|
6048
|
+
tracePropagation: options.tracePropagation,
|
|
6049
|
+
egressSignals: options.egressSignals,
|
|
6050
|
+
credentialEgress: options.credentialEgress,
|
|
6051
|
+
prepare: options.prepareSandbox,
|
|
6052
|
+
onSandboxRefChanged: async (sandboxRef) => {
|
|
6053
|
+
options.onSandboxRefChanged(sandboxRef);
|
|
6054
|
+
await options.persistSandboxRef?.(sandboxRef);
|
|
6055
|
+
}
|
|
6056
|
+
});
|
|
6057
|
+
return {
|
|
6058
|
+
captureRepositoryInstructions: sandbox.captureRepositoryInstructions,
|
|
6059
|
+
workspace: sandbox.workspace,
|
|
6060
|
+
sandboxRef: sandbox.sandboxRef,
|
|
6061
|
+
close: sandbox.close,
|
|
6062
|
+
tools: {
|
|
6063
|
+
supports: sandbox.tools.supports,
|
|
6064
|
+
async execute(params) {
|
|
6065
|
+
const command = params.toolName === "bash" ? bashCommand(params.input) : void 0;
|
|
6066
|
+
if (command) {
|
|
6067
|
+
const result = await maybeExecuteJrRpcCustomCommand(command, {
|
|
6068
|
+
activeSkill: options.getActiveSkill(),
|
|
6069
|
+
channelConfiguration: options.channelConfiguration,
|
|
6070
|
+
actorId: isUserActor(options.actor) ? options.actor.userId : void 0,
|
|
6071
|
+
onConfigurationValueChanged: (key, value) => {
|
|
6072
|
+
if (value === void 0) {
|
|
6073
|
+
delete options.configurationValues[key];
|
|
6074
|
+
return;
|
|
6075
|
+
}
|
|
6076
|
+
options.configurationValues[key] = value;
|
|
6077
|
+
}
|
|
6078
|
+
});
|
|
6079
|
+
if (result.handled) {
|
|
6080
|
+
return formatSandboxCommandResult(result.result);
|
|
6081
|
+
}
|
|
6082
|
+
}
|
|
6083
|
+
return await sandbox.tools.execute(params);
|
|
6084
|
+
}
|
|
6085
|
+
},
|
|
6086
|
+
async writeGeneratedArtifacts(files) {
|
|
6087
|
+
return await writeSandboxGeneratedArtifacts(sandbox.workspace, files);
|
|
6088
|
+
}
|
|
6089
|
+
};
|
|
6090
|
+
}
|
|
6091
|
+
|
|
6092
|
+
// src/chat/mcp/tool-manager.ts
|
|
6093
|
+
import {
|
|
6094
|
+
missingToolAnnotationKeys
|
|
6095
|
+
} from "@sentry/junior-plugin-api";
|
|
6096
|
+
|
|
6097
|
+
// src/chat/mcp/client.ts
|
|
6098
|
+
import { AsyncLocalStorage } from "async_hooks";
|
|
6099
|
+
import { Client } from "@modelcontextprotocol/sdk/client";
|
|
6100
|
+
import {
|
|
6101
|
+
UnauthorizedError
|
|
6102
|
+
} from "@modelcontextprotocol/sdk/client/auth.js";
|
|
6103
|
+
import {
|
|
6104
|
+
StreamableHTTPClientTransport,
|
|
6105
|
+
StreamableHTTPError
|
|
6106
|
+
} from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
6107
|
+
var MCP_CLIENT_INFO = {
|
|
6108
|
+
name: "junior-mcp-client",
|
|
6109
|
+
version: "1.0.0"
|
|
6110
|
+
};
|
|
6111
|
+
var MCP_TOOLS_CALL_METHOD = "tools/call";
|
|
6112
|
+
function getDefaultPort(url) {
|
|
6113
|
+
if (url.protocol === "https:") {
|
|
6114
|
+
return 443;
|
|
6115
|
+
}
|
|
6116
|
+
if (url.protocol === "http:") {
|
|
6117
|
+
return 80;
|
|
6118
|
+
}
|
|
6119
|
+
return void 0;
|
|
6120
|
+
}
|
|
6121
|
+
function getMcpNetworkAttributes(url) {
|
|
6122
|
+
const port = url.port ? Number(url.port) : getDefaultPort(url);
|
|
5971
6123
|
return {
|
|
5972
6124
|
"server.address": url.hostname,
|
|
5973
6125
|
...port !== void 0 ? { "server.port": port } : {},
|
|
@@ -6762,6 +6914,7 @@ function createBashTool() {
|
|
|
6762
6914
|
description: "Run a bash command inside the isolated sandbox workspace. Use this for repository inspection/execution tasks that need shell access. Do not use for network-sensitive or destructive actions unless explicitly required.",
|
|
6763
6915
|
inputSchema: z10.object({
|
|
6764
6916
|
command: z10.string().min(1).describe("Bash command to run inside the sandbox."),
|
|
6917
|
+
cwd: z10.string().min(1).describe("Optional working directory for this command only.").optional(),
|
|
6765
6918
|
timeoutMs: z10.coerce.number().int().min(1e3).describe(
|
|
6766
6919
|
"Optional command timeout in milliseconds. Use for commands that may hang."
|
|
6767
6920
|
).optional()
|
|
@@ -7355,13 +7508,13 @@ function createViewImageTool(sandbox, supportsImageInput, deps = {}) {
|
|
|
7355
7508
|
description: "View an image file from the sandbox workspace when visual inspection is needed. Supports PNG, JPEG, GIF, and WebP images up to 5 MB.",
|
|
7356
7509
|
exposure: "direct",
|
|
7357
7510
|
inputSchema: viewImageInputSchema,
|
|
7358
|
-
async execute({ path:
|
|
7511
|
+
async execute({ path: path8 }) {
|
|
7359
7512
|
if (!supportsImageInput()) {
|
|
7360
7513
|
throw new ToolInputError(
|
|
7361
7514
|
"viewImage is unavailable because the current model does not support image inputs"
|
|
7362
7515
|
);
|
|
7363
7516
|
}
|
|
7364
|
-
const targetPath = normalizeSandboxPath(
|
|
7517
|
+
const targetPath = normalizeSandboxPath(path8);
|
|
7365
7518
|
const data = deps.readFile ? await deps.readFile(targetPath) : await sandbox.readFileToBuffer({ path: targetPath });
|
|
7366
7519
|
if (!data) {
|
|
7367
7520
|
throw new SandboxFileNotFoundError(targetPath);
|
|
@@ -7618,8 +7771,8 @@ async function indexTtlMs(state, subscriptionIds, nowMs) {
|
|
|
7618
7771
|
const latestExpiresAtMs = Math.max(
|
|
7619
7772
|
nowMs,
|
|
7620
7773
|
...records.filter(
|
|
7621
|
-
(
|
|
7622
|
-
).map((
|
|
7774
|
+
(record2) => record2 !== void 0 && activeAt(record2, nowMs)
|
|
7775
|
+
).map((record2) => record2.expiresAtMs)
|
|
7623
7776
|
);
|
|
7624
7777
|
return ttlUntil(latestExpiresAtMs, nowMs);
|
|
7625
7778
|
}
|
|
@@ -7655,7 +7808,7 @@ async function createResourceEventSubscription(input, options = {}) {
|
|
|
7655
7808
|
identifier: input.identifier,
|
|
7656
7809
|
teamId: subscriptionTeamId(input.destination)
|
|
7657
7810
|
});
|
|
7658
|
-
const
|
|
7811
|
+
const record2 = {
|
|
7659
7812
|
conversationId: input.conversationId,
|
|
7660
7813
|
createdAtMs: nowMs,
|
|
7661
7814
|
destination: input.destination,
|
|
@@ -7670,7 +7823,7 @@ async function createResourceEventSubscription(input, options = {}) {
|
|
|
7670
7823
|
status: "active",
|
|
7671
7824
|
updatedAtMs: nowMs
|
|
7672
7825
|
};
|
|
7673
|
-
const parsed = subscriptionSchema.parse(
|
|
7826
|
+
const parsed = subscriptionSchema.parse(record2);
|
|
7674
7827
|
await state.set(
|
|
7675
7828
|
subscriptionKey(id),
|
|
7676
7829
|
parsed,
|
|
@@ -7708,7 +7861,7 @@ async function listResourceEventSubscriptions(input) {
|
|
|
7708
7861
|
)
|
|
7709
7862
|
);
|
|
7710
7863
|
return records.filter(
|
|
7711
|
-
(
|
|
7864
|
+
(record2) => record2 !== void 0 && record2.conversationId === input.conversationId && activeAt(record2, nowMs)
|
|
7712
7865
|
).sort((left, right) => left.createdAtMs - right.createdAtMs);
|
|
7713
7866
|
}
|
|
7714
7867
|
async function cancelResourceEventSubscription(input) {
|
|
@@ -7776,7 +7929,7 @@ async function findMatchingResourceEventSubscriptions(input) {
|
|
|
7776
7929
|
)
|
|
7777
7930
|
);
|
|
7778
7931
|
return records.filter(
|
|
7779
|
-
(
|
|
7932
|
+
(record2) => record2 !== void 0 && matchesEvent(record2, {
|
|
7780
7933
|
eventType: input.eventType,
|
|
7781
7934
|
nowMs,
|
|
7782
7935
|
namespace: input.namespace,
|
|
@@ -11192,8 +11345,8 @@ function normalizeUser(raw) {
|
|
|
11192
11345
|
display_name: raw.profile?.display_name || void 0,
|
|
11193
11346
|
title: raw.profile?.title || void 0,
|
|
11194
11347
|
email: raw.profile?.email || void 0,
|
|
11195
|
-
status_text: raw.profile?.status_text
|
|
11196
|
-
status_emoji: raw.profile?.status_emoji
|
|
11348
|
+
status_text: raw.profile?.status_text ?? void 0,
|
|
11349
|
+
status_emoji: raw.profile?.status_emoji ?? void 0,
|
|
11197
11350
|
is_bot: raw.is_bot ?? false,
|
|
11198
11351
|
is_deleted: raw.deleted ?? false,
|
|
11199
11352
|
timezone: raw.tz || void 0,
|
|
@@ -11310,10 +11463,6 @@ async function searchSlackUsers(options) {
|
|
|
11310
11463
|
|
|
11311
11464
|
// src/chat/slack/tools/user-lookup.ts
|
|
11312
11465
|
import { z as z49 } from "zod";
|
|
11313
|
-
var booleanInput4 = (description) => z49.preprocess(
|
|
11314
|
-
(value) => value === "true" ? true : value === "false" ? false : value,
|
|
11315
|
-
z49.boolean()
|
|
11316
|
-
).describe(description);
|
|
11317
11466
|
function explicitUserLookupError(error) {
|
|
11318
11467
|
if (error.apiError === "user_not_found") {
|
|
11319
11468
|
return "No Slack user found for the supplied user ID.";
|
|
@@ -11334,7 +11483,7 @@ function explicitUserLookupError(error) {
|
|
|
11334
11483
|
}
|
|
11335
11484
|
function createSlackUserLookupTool() {
|
|
11336
11485
|
return zodTool({
|
|
11337
|
-
description: "Look up Slack user profiles by user ID, email, or name search.
|
|
11486
|
+
description: "Look up Slack user profiles by user ID, email, or name search. Set mode to match the identifier in value. Returns profile fields including title, status, and custom fields.",
|
|
11338
11487
|
annotations: {
|
|
11339
11488
|
destructiveHint: false,
|
|
11340
11489
|
idempotentHint: true,
|
|
@@ -11342,51 +11491,18 @@ function createSlackUserLookupTool() {
|
|
|
11342
11491
|
readOnlyHint: true
|
|
11343
11492
|
},
|
|
11344
11493
|
inputSchema: z49.object({
|
|
11345
|
-
|
|
11346
|
-
"
|
|
11347
|
-
)
|
|
11348
|
-
|
|
11349
|
-
"
|
|
11350
|
-
)
|
|
11351
|
-
|
|
11352
|
-
"Name to search for (matches against username, display name, real name). Mutually exclusive with user_id and email."
|
|
11353
|
-
).optional(),
|
|
11354
|
-
limit: z49.coerce.number().int().min(1).max(20).describe(
|
|
11355
|
-
"Maximum number of results to return for name search. Defaults to 10."
|
|
11356
|
-
).optional(),
|
|
11357
|
-
max_pages: z49.coerce.number().int().min(1).max(5).describe(
|
|
11358
|
-
"Maximum number of Slack API pages to scan for name search. Defaults to 3."
|
|
11359
|
-
).optional(),
|
|
11360
|
-
include_bots: booleanInput4(
|
|
11361
|
-
"Include bot accounts in name search results. Defaults to false."
|
|
11362
|
-
).optional()
|
|
11363
|
-
}),
|
|
11494
|
+
mode: z49.enum(["user_id", "email", "query"]).describe(
|
|
11495
|
+
"Lookup method: user_id for Slack IDs, email for email addresses, or query for name searches."
|
|
11496
|
+
),
|
|
11497
|
+
value: z49.string().trim().min(2).describe(
|
|
11498
|
+
"The Slack user ID, email address, or name to look up, interpreted according to mode."
|
|
11499
|
+
)
|
|
11500
|
+
}).strict(),
|
|
11364
11501
|
outputSchema: juniorToolOutputSchema,
|
|
11365
|
-
execute: async ({
|
|
11366
|
-
user_id,
|
|
11367
|
-
email,
|
|
11368
|
-
query,
|
|
11369
|
-
limit,
|
|
11370
|
-
max_pages,
|
|
11371
|
-
include_bots
|
|
11372
|
-
}) => {
|
|
11373
|
-
const modes = [user_id, email, query].filter(Boolean);
|
|
11374
|
-
if (modes.length === 0) {
|
|
11375
|
-
throw new ToolInputError(
|
|
11376
|
-
"Provide exactly one of user_id, email, or query to look up a Slack user."
|
|
11377
|
-
);
|
|
11378
|
-
}
|
|
11379
|
-
if (modes.length > 1) {
|
|
11380
|
-
throw new ToolInputError(
|
|
11381
|
-
"Only one of user_id, email, or query can be provided."
|
|
11382
|
-
);
|
|
11383
|
-
}
|
|
11502
|
+
execute: async ({ mode, value }) => {
|
|
11384
11503
|
try {
|
|
11385
|
-
if (user_id) {
|
|
11386
|
-
const parsedUserId = parseRequiredSlackUserIdParam(
|
|
11387
|
-
"user_id",
|
|
11388
|
-
user_id
|
|
11389
|
-
);
|
|
11504
|
+
if (mode === "user_id") {
|
|
11505
|
+
const parsedUserId = parseRequiredSlackUserIdParam("value", value);
|
|
11390
11506
|
if (!parsedUserId.ok) {
|
|
11391
11507
|
throw new ToolInputError(parsedUserId.error);
|
|
11392
11508
|
}
|
|
@@ -11395,11 +11511,11 @@ function createSlackUserLookupTool() {
|
|
|
11395
11511
|
user: await lookupSlackUserProfile(parsedUserId.value)
|
|
11396
11512
|
};
|
|
11397
11513
|
}
|
|
11398
|
-
if (email) {
|
|
11399
|
-
const profile = await lookupSlackUserByEmail(
|
|
11514
|
+
if (mode === "email") {
|
|
11515
|
+
const profile = await lookupSlackUserByEmail(value);
|
|
11400
11516
|
if (!profile) {
|
|
11401
11517
|
throw new ToolInputError(
|
|
11402
|
-
`No Slack user found with email address ${
|
|
11518
|
+
`No Slack user found with email address ${value}.`
|
|
11403
11519
|
);
|
|
11404
11520
|
}
|
|
11405
11521
|
return {
|
|
@@ -11408,14 +11524,14 @@ function createSlackUserLookupTool() {
|
|
|
11408
11524
|
};
|
|
11409
11525
|
}
|
|
11410
11526
|
const result = await searchSlackUsers({
|
|
11411
|
-
query,
|
|
11412
|
-
limit:
|
|
11413
|
-
maxPages:
|
|
11414
|
-
includeBots:
|
|
11527
|
+
query: value,
|
|
11528
|
+
limit: 10,
|
|
11529
|
+
maxPages: 3,
|
|
11530
|
+
includeBots: false
|
|
11415
11531
|
});
|
|
11416
11532
|
return {
|
|
11417
11533
|
mode: "query",
|
|
11418
|
-
query,
|
|
11534
|
+
query: value,
|
|
11419
11535
|
count: result.users.length,
|
|
11420
11536
|
searched_pages: result.searched_pages,
|
|
11421
11537
|
searched_user_count: result.searched_user_count,
|
|
@@ -11437,43 +11553,94 @@ function createSlackUserLookupTool() {
|
|
|
11437
11553
|
|
|
11438
11554
|
// src/chat/tools/system-time.ts
|
|
11439
11555
|
import { z as z50 } from "zod";
|
|
11556
|
+
var systemTimeInputSchema = z50.object({
|
|
11557
|
+
timezone: z50.string().trim().min(1).optional().describe(
|
|
11558
|
+
"Optional IANA timezone, for example America/Los_Angeles. When set, iso_local and timezone_offset_minutes are for that zone."
|
|
11559
|
+
)
|
|
11560
|
+
});
|
|
11440
11561
|
var systemTimeOutputSchema = juniorToolOutputSchema.extend({
|
|
11441
11562
|
unix_ms: z50.number(),
|
|
11442
11563
|
iso_utc: z50.string(),
|
|
11443
11564
|
iso_local: z50.string(),
|
|
11565
|
+
timezone: z50.string().nullable(),
|
|
11444
11566
|
timezone_offset_minutes: z50.number()
|
|
11445
11567
|
});
|
|
11446
11568
|
function createSystemTimeTool() {
|
|
11447
11569
|
return zodTool({
|
|
11448
|
-
description: "Return current system time in UTC and local
|
|
11570
|
+
description: "Return current system time in UTC and optional local IANA timezone formats. Use when the user asks for current time/date context. Pass timezone when the answer should be in a known IANA zone such as America/Los_Angeles.",
|
|
11449
11571
|
annotations: {
|
|
11450
11572
|
destructiveHint: false,
|
|
11451
11573
|
idempotentHint: true,
|
|
11452
11574
|
openWorldHint: false,
|
|
11453
11575
|
readOnlyHint: true
|
|
11454
11576
|
},
|
|
11455
|
-
inputSchema:
|
|
11577
|
+
inputSchema: systemTimeInputSchema,
|
|
11456
11578
|
outputSchema: systemTimeOutputSchema,
|
|
11457
11579
|
privateTraceResult: (result) => ({
|
|
11458
11580
|
unix_ms: result.unix_ms,
|
|
11459
11581
|
iso_utc: result.iso_utc,
|
|
11460
11582
|
iso_local: result.iso_local,
|
|
11583
|
+
timezone: result.timezone,
|
|
11461
11584
|
timezone_offset_minutes: result.timezone_offset_minutes
|
|
11462
11585
|
}),
|
|
11463
|
-
execute: async () => {
|
|
11586
|
+
execute: async (input) => {
|
|
11464
11587
|
const now = /* @__PURE__ */ new Date();
|
|
11465
|
-
const
|
|
11588
|
+
const timezone = input.timezone?.trim() || null;
|
|
11589
|
+
if (timezone) {
|
|
11590
|
+
if (!isValidTimeZone2(timezone)) {
|
|
11591
|
+
throw new Error("timezone must be a valid IANA time zone.");
|
|
11592
|
+
}
|
|
11593
|
+
const parts = getZonedDateTimeParts(now.getTime(), timezone);
|
|
11594
|
+
const isoLocal = [
|
|
11595
|
+
String(parts.year).padStart(4, "0"),
|
|
11596
|
+
String(parts.month).padStart(2, "0"),
|
|
11597
|
+
String(parts.day).padStart(2, "0")
|
|
11598
|
+
].join("-") + "T" + [
|
|
11599
|
+
String(parts.hour).padStart(2, "0"),
|
|
11600
|
+
String(parts.minute).padStart(2, "0"),
|
|
11601
|
+
String(parts.second).padStart(2, "0")
|
|
11602
|
+
].join(":");
|
|
11603
|
+
return {
|
|
11604
|
+
unix_ms: now.getTime(),
|
|
11605
|
+
iso_utc: now.toISOString(),
|
|
11606
|
+
iso_local: isoLocal,
|
|
11607
|
+
timezone,
|
|
11608
|
+
timezone_offset_minutes: getTimeZoneOffsetMinutes(
|
|
11609
|
+
now.getTime(),
|
|
11610
|
+
timezone
|
|
11611
|
+
)
|
|
11612
|
+
};
|
|
11613
|
+
}
|
|
11614
|
+
return {
|
|
11466
11615
|
unix_ms: now.getTime(),
|
|
11467
11616
|
iso_utc: now.toISOString(),
|
|
11468
11617
|
iso_local: new Date(now.getTime() - now.getTimezoneOffset() * 6e4).toISOString().replace("Z", ""),
|
|
11618
|
+
timezone: null,
|
|
11469
11619
|
timezone_offset_minutes: now.getTimezoneOffset()
|
|
11470
11620
|
};
|
|
11471
|
-
return {
|
|
11472
|
-
...details
|
|
11473
|
-
};
|
|
11474
11621
|
}
|
|
11475
11622
|
});
|
|
11476
11623
|
}
|
|
11624
|
+
function isValidTimeZone2(timezone) {
|
|
11625
|
+
try {
|
|
11626
|
+
new Intl.DateTimeFormat("en-US", { timeZone: timezone }).format();
|
|
11627
|
+
return true;
|
|
11628
|
+
} catch {
|
|
11629
|
+
return false;
|
|
11630
|
+
}
|
|
11631
|
+
}
|
|
11632
|
+
function getTimeZoneOffsetMinutes(timestampMs, timezone) {
|
|
11633
|
+
const parts = getZonedDateTimeParts(timestampMs, timezone);
|
|
11634
|
+
const asUtcMs = Date.UTC(
|
|
11635
|
+
parts.year,
|
|
11636
|
+
parts.month - 1,
|
|
11637
|
+
parts.day,
|
|
11638
|
+
parts.hour,
|
|
11639
|
+
parts.minute,
|
|
11640
|
+
parts.second
|
|
11641
|
+
);
|
|
11642
|
+
return Math.round((timestampMs - asUtcMs) / 6e4);
|
|
11643
|
+
}
|
|
11477
11644
|
|
|
11478
11645
|
// src/chat/tools/query-conversation-events.ts
|
|
11479
11646
|
import { z as z51 } from "zod";
|
|
@@ -11894,16 +12061,16 @@ async function resolvePublicHostname(hostname) {
|
|
|
11894
12061
|
throw new Error("Could not resolve hostname");
|
|
11895
12062
|
}
|
|
11896
12063
|
const deduped = /* @__PURE__ */ new Map();
|
|
11897
|
-
for (const
|
|
11898
|
-
const family =
|
|
11899
|
-
if (family === 4 && isPrivateIpv4(
|
|
12064
|
+
for (const record2 of records) {
|
|
12065
|
+
const family = record2.family === 6 ? 6 : 4;
|
|
12066
|
+
if (family === 4 && isPrivateIpv4(record2.address)) {
|
|
11900
12067
|
throw new Error("Resolved to a private IPv4 address");
|
|
11901
12068
|
}
|
|
11902
|
-
if (family === 6 && isPrivateIpv6(
|
|
12069
|
+
if (family === 6 && isPrivateIpv6(record2.address)) {
|
|
11903
12070
|
throw new Error("Resolved to a private IPv6 address");
|
|
11904
12071
|
}
|
|
11905
|
-
deduped.set(`${family}:${
|
|
11906
|
-
address:
|
|
12072
|
+
deduped.set(`${family}:${record2.address}`, {
|
|
12073
|
+
address: record2.address,
|
|
11907
12074
|
family
|
|
11908
12075
|
});
|
|
11909
12076
|
}
|
|
@@ -12564,7 +12731,9 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
|
|
|
12564
12731
|
);
|
|
12565
12732
|
}
|
|
12566
12733
|
}
|
|
12567
|
-
for (const [name, pluginTool] of Object.entries(
|
|
12734
|
+
for (const [name, pluginTool] of Object.entries(
|
|
12735
|
+
getPluginTools(context, options.pluginSandbox)
|
|
12736
|
+
)) {
|
|
12568
12737
|
if (tools[name]) {
|
|
12569
12738
|
throw new Error(`Plugin tool "${name}" conflicts with a core tool`);
|
|
12570
12739
|
}
|
|
@@ -12684,6 +12853,7 @@ function buildSandboxInput(toolName, params) {
|
|
|
12684
12853
|
if (toolName === "bash") {
|
|
12685
12854
|
return {
|
|
12686
12855
|
command: String(params.command ?? ""),
|
|
12856
|
+
...typeof params.cwd === "string" ? { cwd: params.cwd } : {},
|
|
12687
12857
|
...params.env && typeof params.env === "object" && !Array.isArray(params.env) ? { env: params.env } : {},
|
|
12688
12858
|
...optionalNumber(params.timeoutMs) ? { timeoutMs: optionalNumber(params.timeoutMs) } : {}
|
|
12689
12859
|
};
|
|
@@ -12734,184 +12904,6 @@ function buildSandboxInput(toolName, params) {
|
|
|
12734
12904
|
return params;
|
|
12735
12905
|
}
|
|
12736
12906
|
|
|
12737
|
-
// src/chat/tool-support/normalize-result.ts
|
|
12738
|
-
function isToolContent(value) {
|
|
12739
|
-
return Array.isArray(value) && value.every((part) => {
|
|
12740
|
-
if (!part || typeof part !== "object") {
|
|
12741
|
-
return false;
|
|
12742
|
-
}
|
|
12743
|
-
const record = part;
|
|
12744
|
-
if (record.type === "text") {
|
|
12745
|
-
return typeof record.text === "string";
|
|
12746
|
-
}
|
|
12747
|
-
if (record.type === "image") {
|
|
12748
|
-
return typeof record.data === "string" && typeof record.mimeType === "string";
|
|
12749
|
-
}
|
|
12750
|
-
return false;
|
|
12751
|
-
});
|
|
12752
|
-
}
|
|
12753
|
-
function isStructuredToolExecutionResult(value) {
|
|
12754
|
-
const content = value?.content;
|
|
12755
|
-
return typeof value === "object" && value !== null && isToolContent(content) && "details" in value;
|
|
12756
|
-
}
|
|
12757
|
-
function isContentOnlyToolExecutionResult(value) {
|
|
12758
|
-
const content = value?.content;
|
|
12759
|
-
return typeof value === "object" && value !== null && isToolContent(content) && !("details" in value);
|
|
12760
|
-
}
|
|
12761
|
-
function toToolContentText(value) {
|
|
12762
|
-
if (typeof value === "string") return value;
|
|
12763
|
-
try {
|
|
12764
|
-
return JSON.stringify(value);
|
|
12765
|
-
} catch {
|
|
12766
|
-
return String(value);
|
|
12767
|
-
}
|
|
12768
|
-
}
|
|
12769
|
-
function withoutFirstGeneratedTextContent(content) {
|
|
12770
|
-
const [first, ...rest] = content;
|
|
12771
|
-
if (first?.type === "text") {
|
|
12772
|
-
return rest;
|
|
12773
|
-
}
|
|
12774
|
-
return content;
|
|
12775
|
-
}
|
|
12776
|
-
function replaceGeneratedTextContent(content, text) {
|
|
12777
|
-
return [
|
|
12778
|
-
{
|
|
12779
|
-
type: "text",
|
|
12780
|
-
text
|
|
12781
|
-
},
|
|
12782
|
-
...withoutFirstGeneratedTextContent(content)
|
|
12783
|
-
];
|
|
12784
|
-
}
|
|
12785
|
-
function isRecord3(value) {
|
|
12786
|
-
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
12787
|
-
}
|
|
12788
|
-
function hasContinuation(value) {
|
|
12789
|
-
return isRecord3(value) && isRecord3(value.continuation);
|
|
12790
|
-
}
|
|
12791
|
-
function stringField(record, key) {
|
|
12792
|
-
const value = record[key];
|
|
12793
|
-
return typeof value === "string" ? value : "";
|
|
12794
|
-
}
|
|
12795
|
-
function numberField(record, key) {
|
|
12796
|
-
const value = record[key];
|
|
12797
|
-
return typeof value === "number" && Number.isFinite(value) ? value : 0;
|
|
12798
|
-
}
|
|
12799
|
-
function stringListField(record, key) {
|
|
12800
|
-
const value = record[key];
|
|
12801
|
-
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
12802
|
-
}
|
|
12803
|
-
function accountText(value) {
|
|
12804
|
-
if (!isRecord3(value)) {
|
|
12805
|
-
return void 0;
|
|
12806
|
-
}
|
|
12807
|
-
const label = stringField(value, "label") || stringField(value, "id");
|
|
12808
|
-
const id = stringField(value, "id");
|
|
12809
|
-
if (!label) {
|
|
12810
|
-
return void 0;
|
|
12811
|
-
}
|
|
12812
|
-
return id && id !== label ? `${label} (${id})` : label;
|
|
12813
|
-
}
|
|
12814
|
-
function upstreamPermissionDeniedText(value) {
|
|
12815
|
-
if (!isRecord3(value) || !isRecord3(value.permission_denied)) {
|
|
12816
|
-
return void 0;
|
|
12817
|
-
}
|
|
12818
|
-
const signal = value.permission_denied;
|
|
12819
|
-
if (signal.source !== "upstream") {
|
|
12820
|
-
return void 0;
|
|
12821
|
-
}
|
|
12822
|
-
const provider = stringField(signal, "provider");
|
|
12823
|
-
const message = stringField(signal, "message");
|
|
12824
|
-
const upstreamHost = stringField(signal, "upstreamHost");
|
|
12825
|
-
const upstreamPath = stringField(signal, "upstreamPath");
|
|
12826
|
-
const status = numberField(signal, "status");
|
|
12827
|
-
if (!provider || !message || !upstreamHost || !upstreamPath || !status) {
|
|
12828
|
-
return void 0;
|
|
12829
|
-
}
|
|
12830
|
-
const grant = isRecord3(signal.grant) ? signal.grant : {};
|
|
12831
|
-
const grantName = stringField(grant, "name");
|
|
12832
|
-
const grantAccess = stringField(grant, "access");
|
|
12833
|
-
const grantReason = stringField(grant, "reason");
|
|
12834
|
-
const grantRequirements = stringListField(grant, "requirements");
|
|
12835
|
-
const account = accountText(signal.account);
|
|
12836
|
-
const command = stringField(value, "command");
|
|
12837
|
-
const stderr = stringField(value, "stderr").trim();
|
|
12838
|
-
const stdout = stringField(value, "stdout").trim();
|
|
12839
|
-
const acceptedPermissions = stringField(signal, "acceptedPermissions");
|
|
12840
|
-
const sso = stringField(signal, "sso");
|
|
12841
|
-
return [
|
|
12842
|
-
"Upstream permission denied.",
|
|
12843
|
-
message,
|
|
12844
|
-
"",
|
|
12845
|
-
`Provider: ${provider}`,
|
|
12846
|
-
...account ? [`Provider account: ${account}`] : [],
|
|
12847
|
-
`Grant: ${grantName || "unknown"}${grantAccess ? ` (${grantAccess}${grantReason ? `, ${grantReason}` : ""})` : ""}`,
|
|
12848
|
-
...grantRequirements.length > 0 ? [
|
|
12849
|
-
"Provider requirements:",
|
|
12850
|
-
...grantRequirements.map((item) => `- ${item}`)
|
|
12851
|
-
] : [],
|
|
12852
|
-
`Upstream: ${upstreamHost}${upstreamPath}`,
|
|
12853
|
-
`Status: ${status}`,
|
|
12854
|
-
...acceptedPermissions ? [`Accepted provider permissions: ${acceptedPermissions}`] : [],
|
|
12855
|
-
...sso ? [`Provider SSO: ${sso}`] : [],
|
|
12856
|
-
...command ? [`Command: ${command}`] : [],
|
|
12857
|
-
"",
|
|
12858
|
-
"Junior had a credential lease for this grant and forwarded the request. Do not diagnose this as a missing user token or a local Junior runtime block; diagnose provider-side permissions, installation scope, SSO, or actor-provider account access.",
|
|
12859
|
-
...stderr ? ["", `stderr:
|
|
12860
|
-
${stderr}`] : [],
|
|
12861
|
-
...stdout ? ["", `stdout:
|
|
12862
|
-
${stdout}`] : []
|
|
12863
|
-
].join("\n");
|
|
12864
|
-
}
|
|
12865
|
-
function normalizeDetails(details, options) {
|
|
12866
|
-
const continuationToolName = options.toolName && hasContinuation(details) ? options.toolName : void 0;
|
|
12867
|
-
if (!continuationToolName) {
|
|
12868
|
-
return { details, replaceEnvelopeText: false };
|
|
12869
|
-
}
|
|
12870
|
-
return {
|
|
12871
|
-
details: injectContinuationToolName(details, continuationToolName),
|
|
12872
|
-
replaceEnvelopeText: true
|
|
12873
|
-
};
|
|
12874
|
-
}
|
|
12875
|
-
function normalizeToolResult(result, options = {}) {
|
|
12876
|
-
const unwrapped = result;
|
|
12877
|
-
if (isStructuredToolExecutionResult(unwrapped)) {
|
|
12878
|
-
const normalized2 = normalizeDetails(unwrapped.details, options);
|
|
12879
|
-
const permissionText = upstreamPermissionDeniedText(normalized2.details);
|
|
12880
|
-
if (!permissionText && normalized2.details === unwrapped.details) {
|
|
12881
|
-
return unwrapped;
|
|
12882
|
-
}
|
|
12883
|
-
const content = permissionText || normalized2.replaceEnvelopeText ? replaceGeneratedTextContent(
|
|
12884
|
-
unwrapped.content,
|
|
12885
|
-
permissionText ?? toToolContentText(normalized2.details)
|
|
12886
|
-
) : unwrapped.content;
|
|
12887
|
-
return {
|
|
12888
|
-
content,
|
|
12889
|
-
details: normalized2.details
|
|
12890
|
-
};
|
|
12891
|
-
}
|
|
12892
|
-
if (isContentOnlyToolExecutionResult(unwrapped)) {
|
|
12893
|
-
if (options.requireStructuredResult) {
|
|
12894
|
-
throw new TypeError(
|
|
12895
|
-
"Structured tools must return details matching their outputSchema."
|
|
12896
|
-
);
|
|
12897
|
-
}
|
|
12898
|
-
return {
|
|
12899
|
-
content: unwrapped.content,
|
|
12900
|
-
details: {}
|
|
12901
|
-
};
|
|
12902
|
-
}
|
|
12903
|
-
const normalized = normalizeDetails(unwrapped, options);
|
|
12904
|
-
return {
|
|
12905
|
-
content: [
|
|
12906
|
-
{
|
|
12907
|
-
type: "text",
|
|
12908
|
-
text: upstreamPermissionDeniedText(normalized.details) ?? toToolContentText(normalized.details)
|
|
12909
|
-
}
|
|
12910
|
-
],
|
|
12911
|
-
details: normalized.details
|
|
12912
|
-
};
|
|
12913
|
-
}
|
|
12914
|
-
|
|
12915
12907
|
// src/chat/tools/execution/tool-error-handler.ts
|
|
12916
12908
|
import { PluginToolInputError } from "@sentry/junior-plugin-api";
|
|
12917
12909
|
|
|
@@ -15082,8 +15074,8 @@ function contentText(content) {
|
|
|
15082
15074
|
if (!part || typeof part !== "object") {
|
|
15083
15075
|
return [];
|
|
15084
15076
|
}
|
|
15085
|
-
const
|
|
15086
|
-
return typeof
|
|
15077
|
+
const record2 = part;
|
|
15078
|
+
return typeof record2.text === "string" ? [record2.text] : [];
|
|
15087
15079
|
}).join("\n");
|
|
15088
15080
|
}
|
|
15089
15081
|
function assistantEntries(message) {
|
|
@@ -15103,13 +15095,13 @@ function assistantEntries(message) {
|
|
|
15103
15095
|
if (!part || typeof part !== "object") {
|
|
15104
15096
|
continue;
|
|
15105
15097
|
}
|
|
15106
|
-
const
|
|
15107
|
-
if (
|
|
15098
|
+
const record2 = part;
|
|
15099
|
+
if (record2.type !== "toolCall" || typeof record2.name !== "string" || !record2.name) {
|
|
15108
15100
|
continue;
|
|
15109
15101
|
}
|
|
15110
|
-
const toolInput = serializedText(
|
|
15102
|
+
const toolInput = serializedText(record2.arguments);
|
|
15111
15103
|
if (toolInput.trim()) {
|
|
15112
|
-
entries.push({ role: `tool ${
|
|
15104
|
+
entries.push({ role: `tool ${record2.name} call`, text: toolInput });
|
|
15113
15105
|
}
|
|
15114
15106
|
}
|
|
15115
15107
|
return entries;
|
|
@@ -15434,7 +15426,12 @@ async function wireAgentTools(args) {
|
|
|
15434
15426
|
}
|
|
15435
15427
|
},
|
|
15436
15428
|
toolRuntimeContext,
|
|
15437
|
-
{
|
|
15429
|
+
{
|
|
15430
|
+
includeLoadSkill: !explicitSkillLoaded,
|
|
15431
|
+
pluginSandbox: createPluginToolSandbox(agentSandbox, {
|
|
15432
|
+
handleAuthSignal: pluginAuth.maybeHandleAuthSignal
|
|
15433
|
+
})
|
|
15434
|
+
}
|
|
15438
15435
|
);
|
|
15439
15436
|
const plannedToolExposure = planToolExposure(
|
|
15440
15437
|
tools
|
|
@@ -15509,6 +15506,7 @@ async function wireAgentTools(args) {
|
|
|
15509
15506
|
return actionReview.projectToolResult(toolCallId, result);
|
|
15510
15507
|
},
|
|
15511
15508
|
getSandboxRef: agentSandbox.sandboxRef,
|
|
15509
|
+
captureRepositoryInstructions: agentSandbox.captureRepositoryInstructions,
|
|
15512
15510
|
async close() {
|
|
15513
15511
|
agentSandbox.close();
|
|
15514
15512
|
try {
|
|
@@ -15799,6 +15797,59 @@ function userMessage(text) {
|
|
|
15799
15797
|
timestamp: Date.now()
|
|
15800
15798
|
};
|
|
15801
15799
|
}
|
|
15800
|
+
function userMessageContent(message) {
|
|
15801
|
+
const content = message.content;
|
|
15802
|
+
return Array.isArray(content) ? content : [];
|
|
15803
|
+
}
|
|
15804
|
+
function hasTurnBootstrap(message) {
|
|
15805
|
+
return userMessageContent(message).some(
|
|
15806
|
+
(part) => textPart(part)?.startsWith(`<${TURN_CONTEXT_TAG}>`) === true
|
|
15807
|
+
);
|
|
15808
|
+
}
|
|
15809
|
+
function effectiveRuntimeContext(messages) {
|
|
15810
|
+
let latestBootstrapIndex = -1;
|
|
15811
|
+
for (let index = messages.length - 1; index >= 0; index -= 1) {
|
|
15812
|
+
if (hasTurnBootstrap(messages[index])) {
|
|
15813
|
+
latestBootstrapIndex = index;
|
|
15814
|
+
break;
|
|
15815
|
+
}
|
|
15816
|
+
}
|
|
15817
|
+
const effectiveMessages = latestBootstrapIndex < 0 ? messages : messages.slice(latestBootstrapIndex);
|
|
15818
|
+
const allContent = effectiveMessages.flatMap(userMessageContent);
|
|
15819
|
+
const firstAgentsIndex = allContent.findIndex(
|
|
15820
|
+
(part) => textPart(part)?.startsWith("# AGENTS.md instructions") === true
|
|
15821
|
+
);
|
|
15822
|
+
const content = allContent.filter(
|
|
15823
|
+
(part) => textPart(part)?.startsWith("# AGENTS.md instructions") !== true
|
|
15824
|
+
);
|
|
15825
|
+
const visibleAgents = findVisibleAgentsInstructions(effectiveMessages);
|
|
15826
|
+
if (visibleAgents?.active === true) {
|
|
15827
|
+
const insertionIndex = firstAgentsIndex < 0 ? content.length : Math.min(firstAgentsIndex, content.length);
|
|
15828
|
+
content.splice(insertionIndex, 0, {
|
|
15829
|
+
type: "text",
|
|
15830
|
+
text: renderAgentsInstructions({
|
|
15831
|
+
directory: visibleAgents.directory,
|
|
15832
|
+
text: visibleAgents.text
|
|
15833
|
+
})
|
|
15834
|
+
});
|
|
15835
|
+
}
|
|
15836
|
+
const latestTimestamp = effectiveMessages.at(-1)?.timestamp;
|
|
15837
|
+
return {
|
|
15838
|
+
content,
|
|
15839
|
+
timestamp: typeof latestTimestamp === "number" ? latestTimestamp : Date.now()
|
|
15840
|
+
};
|
|
15841
|
+
}
|
|
15842
|
+
function runtimeContextMessage(messages, timestamp) {
|
|
15843
|
+
const runtimeContext = effectiveRuntimeContext(messages);
|
|
15844
|
+
if (runtimeContext.content.length === 0) {
|
|
15845
|
+
return void 0;
|
|
15846
|
+
}
|
|
15847
|
+
return {
|
|
15848
|
+
role: "user",
|
|
15849
|
+
content: runtimeContext.content,
|
|
15850
|
+
timestamp: timestamp ?? runtimeContext.timestamp
|
|
15851
|
+
};
|
|
15852
|
+
}
|
|
15802
15853
|
function selectRetainedUserMessageEntries(messages, maxTokens = RETAINED_USER_MESSAGE_TOKENS) {
|
|
15803
15854
|
const selected = [];
|
|
15804
15855
|
let remaining = maxTokens;
|
|
@@ -16062,21 +16113,19 @@ function createContextCompactor(deps) {
|
|
|
16062
16113
|
};
|
|
16063
16114
|
}
|
|
16064
16115
|
async function compactContextForHandoff(args, deps) {
|
|
16065
|
-
const
|
|
16066
|
-
if (!
|
|
16116
|
+
const contextMessage = runtimeContextMessage(args.runtimeContext);
|
|
16117
|
+
if (!contextMessage) {
|
|
16067
16118
|
throw new Error("Handoff requires the current runtime turn context");
|
|
16068
16119
|
}
|
|
16069
16120
|
const generatedSummary = await summarizeContext(args, deps);
|
|
16070
16121
|
const summary = `${MODEL_HANDOFF_SUMMARY_PREFIX}
|
|
16071
16122
|
${generatedSummary}`;
|
|
16072
|
-
const
|
|
16073
|
-
|
|
16074
|
-
content: [
|
|
16075
|
-
|
|
16076
|
-
{ type: "text", text: renderCurrentInstruction(summary) }
|
|
16077
|
-
]
|
|
16123
|
+
const instructionMessage = {
|
|
16124
|
+
role: "user",
|
|
16125
|
+
content: [{ type: "text", text: renderCurrentInstruction(summary) }],
|
|
16126
|
+
timestamp: contextMessage.timestamp
|
|
16078
16127
|
};
|
|
16079
|
-
const messages = [
|
|
16128
|
+
const messages = [contextMessage, instructionMessage];
|
|
16080
16129
|
const replacementMessages = stripRuntimeTurnContext(messages);
|
|
16081
16130
|
args.signal?.throwIfAborted();
|
|
16082
16131
|
await getConversationEventStore().replaceHistory(args.conversationId, {
|
|
@@ -16138,10 +16187,18 @@ async function compactActiveContextIfNeeded(args, deps) {
|
|
|
16138
16187
|
`${ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX}
|
|
16139
16188
|
${summary}`
|
|
16140
16189
|
);
|
|
16141
|
-
const runtimeMessages = retainRuntimeTurnContext(args.piMessages);
|
|
16142
16190
|
const retainedInstruction = pendingMessages.length === 0 ? await loadLastInstruction(args.conversationId) : void 0;
|
|
16143
16191
|
const instructionMessages = retainedInstruction ? [retainedInstruction.message] : pendingMessages.map((entry) => entry.message);
|
|
16144
16192
|
const instructionProvenance = retainedInstruction ? [retainedInstruction.provenance] : pendingMessages.map((entry) => entry.provenance);
|
|
16193
|
+
const instructionTimestamp = instructionMessages[0]?.timestamp;
|
|
16194
|
+
const retainedRuntimeContext = retainRuntimeTurnContext(
|
|
16195
|
+
args.runtimeContextMessages ?? args.piMessages
|
|
16196
|
+
);
|
|
16197
|
+
const contextMessage = runtimeContextMessage(
|
|
16198
|
+
retainedRuntimeContext,
|
|
16199
|
+
typeof instructionTimestamp === "number" ? instructionTimestamp : void 0
|
|
16200
|
+
);
|
|
16201
|
+
const runtimeMessages = contextMessage ? [contextMessage] : [];
|
|
16145
16202
|
const messages = [...runtimeMessages, ...instructionMessages, summaryMessage];
|
|
16146
16203
|
const replacementInputTokens = estimateHistoryTokens(messages);
|
|
16147
16204
|
if (replacementInputTokens >= inputLimitTokens) {
|
|
@@ -16447,7 +16504,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
|
|
|
16447
16504
|
syncLoadedSkillNamesForResume
|
|
16448
16505
|
});
|
|
16449
16506
|
const explicitSkill = invokedSkill ? activeSkills.find((skill) => skill.name === invokedSkill.name) ?? null : null;
|
|
16450
|
-
const { routerBlocks, userContentParts } = buildPromptInput(input);
|
|
16507
|
+
const { contextContentParts, routerBlocks, userContentParts } = buildPromptInput(input);
|
|
16451
16508
|
const preAgentPromptMessages = () => existingSessionRecord?.piMessages ?? [...input.piMessages ?? []];
|
|
16452
16509
|
const storedTurnRoute = await loadTurnRoute({ conversationId, turnId });
|
|
16453
16510
|
if (storedTurnRoute) {
|
|
@@ -16667,6 +16724,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
|
|
|
16667
16724
|
});
|
|
16668
16725
|
mcpToolManager = wiring.mcpToolManager;
|
|
16669
16726
|
closeTools = wiring.close;
|
|
16727
|
+
const initialRepositoryInstructions = wiring.getSandboxRef() ? await wiring.captureRepositoryInstructions() : void 0;
|
|
16670
16728
|
const getPendingAuthPause = wiring.getPendingAuthPause;
|
|
16671
16729
|
const toolsWithoutHandoff = wiring.agentTools.filter(
|
|
16672
16730
|
(tool) => tool.name !== HANDOFF_TOOL_NAME
|
|
@@ -16694,18 +16752,29 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
|
|
|
16694
16752
|
};
|
|
16695
16753
|
const {
|
|
16696
16754
|
baseInstructions,
|
|
16755
|
+
contextContentParts: promptContextContentParts,
|
|
16697
16756
|
inputMessages,
|
|
16698
16757
|
inputMessagesAttribute,
|
|
16699
|
-
|
|
16758
|
+
promptTimestamp: restoredPromptTimestamp,
|
|
16700
16759
|
promptHistoryMessages,
|
|
16701
16760
|
shouldPromptAgent,
|
|
16702
|
-
turnContexts
|
|
16761
|
+
turnContexts,
|
|
16762
|
+
userContentParts: promptUserContentParts
|
|
16703
16763
|
} = await assemblePrompt({
|
|
16704
16764
|
activeMcpCatalogs: wiring.activeMcpCatalogs,
|
|
16705
16765
|
currentActor: actor,
|
|
16706
16766
|
artifactState: state.artifactState,
|
|
16707
16767
|
availableSkills,
|
|
16708
16768
|
configurationValues,
|
|
16769
|
+
contextContentParts: [
|
|
16770
|
+
...initialRepositoryInstructions ? [
|
|
16771
|
+
{
|
|
16772
|
+
type: "text",
|
|
16773
|
+
text: renderAgentsInstructions(initialRepositoryInstructions)
|
|
16774
|
+
}
|
|
16775
|
+
] : [],
|
|
16776
|
+
...contextContentParts
|
|
16777
|
+
],
|
|
16709
16778
|
conversationPrivacy,
|
|
16710
16779
|
existingSessionPiMessages: existingSessionRecord?.piMessages,
|
|
16711
16780
|
existingTurnStartMessageIndex: existingSessionRecord?.turnStartMessageIndex,
|
|
@@ -16719,6 +16788,18 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
|
|
|
16719
16788
|
toolRuntimeContext: wiring.toolRuntimeContext,
|
|
16720
16789
|
userContentParts
|
|
16721
16790
|
});
|
|
16791
|
+
const repositoryInstructionsContext = createRepositoryInstructionsContext({
|
|
16792
|
+
capture: wiring.captureRepositoryInstructions,
|
|
16793
|
+
hasSandbox: () => Boolean(wiring.getSandboxRef()),
|
|
16794
|
+
initialInstructions: initialRepositoryInstructions,
|
|
16795
|
+
promptContextContentParts,
|
|
16796
|
+
restoredMessages: existingSessionRecord?.piMessages,
|
|
16797
|
+
restoredProvenance: existingSessionRecord?.piMessageProvenance,
|
|
16798
|
+
setMessages(messages) {
|
|
16799
|
+
agent.state.messages = messages;
|
|
16800
|
+
},
|
|
16801
|
+
shouldPromptAgent
|
|
16802
|
+
});
|
|
16722
16803
|
runResume.setTurnContexts(turnContexts);
|
|
16723
16804
|
const applyPendingHandoff = () => {
|
|
16724
16805
|
if (!pendingHandoff) {
|
|
@@ -16750,7 +16831,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
|
|
|
16750
16831
|
thinkingLevel
|
|
16751
16832
|
};
|
|
16752
16833
|
};
|
|
16753
|
-
const applyActiveContextCompaction = async (messages, hookSignal, pendingMessages, clearSteeringQueueOnCommit = false) => {
|
|
16834
|
+
const applyActiveContextCompaction = async (messages, hookSignal, pendingMessages, clearSteeringQueueOnCommit = false, pairPendingRuntimeContext = false) => {
|
|
16754
16835
|
const compaction = await compactActiveContextIfNeeded(
|
|
16755
16836
|
{
|
|
16756
16837
|
conversationContext: input.conversationContext,
|
|
@@ -16766,6 +16847,12 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
|
|
|
16766
16847
|
onCompactionStart: () => observers.onStatus?.({ text: "Compacting context" }),
|
|
16767
16848
|
pendingMessages,
|
|
16768
16849
|
piMessages: messages,
|
|
16850
|
+
...pairPendingRuntimeContext && pendingMessages ? {
|
|
16851
|
+
runtimeContextMessages: [
|
|
16852
|
+
...messages,
|
|
16853
|
+
...pendingMessages.map((entry) => entry.message)
|
|
16854
|
+
]
|
|
16855
|
+
} : {},
|
|
16769
16856
|
signal: hookSignal
|
|
16770
16857
|
},
|
|
16771
16858
|
{
|
|
@@ -16903,13 +16990,18 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
|
|
|
16903
16990
|
pendingMessages,
|
|
16904
16991
|
true
|
|
16905
16992
|
);
|
|
16993
|
+
const combinedUpdate = capacityUpdate && handoffUpdate ? { ...handoffUpdate, ...capacityUpdate } : capacityUpdate ?? handoffUpdate;
|
|
16994
|
+
const repositoryInstructionsUpdate = await repositoryInstructionsContext.applyUpdate(
|
|
16995
|
+
combinedUpdate,
|
|
16996
|
+
nextTurn.context
|
|
16997
|
+
);
|
|
16906
16998
|
const yieldError = runResume.prepareYieldIfDue(
|
|
16907
16999
|
currentAgentMessages()
|
|
16908
17000
|
);
|
|
16909
17001
|
if (yieldError) {
|
|
16910
17002
|
throw yieldError;
|
|
16911
17003
|
}
|
|
16912
|
-
return
|
|
17004
|
+
return repositoryInstructionsUpdate;
|
|
16913
17005
|
} catch (error) {
|
|
16914
17006
|
pendingPiHookError ??= error instanceof Error ? error : new Error(String(error));
|
|
16915
17007
|
throw error;
|
|
@@ -16964,20 +17056,37 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
|
|
|
16964
17056
|
"gen_ai.invoke_agent",
|
|
16965
17057
|
spanContext,
|
|
16966
17058
|
async () => {
|
|
17059
|
+
const promptTimestamp = restoredPromptTimestamp ?? Date.now();
|
|
17060
|
+
const contextMessage = shouldPromptAgent && promptContextContentParts.length > 0 ? {
|
|
17061
|
+
role: "user",
|
|
17062
|
+
content: promptContextContentParts,
|
|
17063
|
+
timestamp: promptTimestamp
|
|
17064
|
+
} : void 0;
|
|
16967
17065
|
const freshPromptMessage = {
|
|
16968
17066
|
role: "user",
|
|
16969
|
-
content:
|
|
16970
|
-
timestamp:
|
|
17067
|
+
content: promptUserContentParts,
|
|
17068
|
+
timestamp: promptTimestamp
|
|
16971
17069
|
};
|
|
16972
17070
|
if (shouldPromptAgent) {
|
|
16973
17071
|
const promptPersisted = await runResume.requireDurableInputCheckpoint([
|
|
16974
17072
|
...agent.state.messages,
|
|
17073
|
+
...contextMessage ? [contextMessage] : [],
|
|
16975
17074
|
freshPromptMessage
|
|
16976
17075
|
]);
|
|
16977
17076
|
if (promptPersisted) {
|
|
16978
17077
|
await runResume.commitInput();
|
|
16979
17078
|
}
|
|
16980
17079
|
}
|
|
17080
|
+
if (contextMessage) {
|
|
17081
|
+
agent.state.messages = [...agent.state.messages, contextMessage];
|
|
17082
|
+
}
|
|
17083
|
+
if (!shouldPromptAgent) {
|
|
17084
|
+
await repositoryInstructionsContext.applyUpdate(void 0, {
|
|
17085
|
+
messages: agent.state.messages,
|
|
17086
|
+
systemPrompt: agent.state.systemPrompt,
|
|
17087
|
+
tools: agent.state.tools
|
|
17088
|
+
});
|
|
17089
|
+
}
|
|
16981
17090
|
const runAgentStep = async (run2, abortRun = () => agent.abort()) => {
|
|
16982
17091
|
let timeoutId;
|
|
16983
17092
|
let removeAbortListener;
|
|
@@ -17074,7 +17183,10 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
|
|
|
17074
17183
|
await runAgentStep(
|
|
17075
17184
|
scheduleHandoff({
|
|
17076
17185
|
profile: requestedProfile,
|
|
17077
|
-
runtimeContextSourceMessages: shouldPromptAgent ? [
|
|
17186
|
+
runtimeContextSourceMessages: shouldPromptAgent ? [
|
|
17187
|
+
...contextMessage ? [contextMessage] : [],
|
|
17188
|
+
freshPromptMessage
|
|
17189
|
+
] : void 0,
|
|
17078
17190
|
signal: handoffAbortController.signal,
|
|
17079
17191
|
sourceMessages: [...agent.state.messages]
|
|
17080
17192
|
}),
|
|
@@ -17092,7 +17204,9 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
|
|
|
17092
17204
|
message: freshPromptMessage,
|
|
17093
17205
|
provenance: instructionProvenanceFor(actor)
|
|
17094
17206
|
}
|
|
17095
|
-
] : void 0
|
|
17207
|
+
] : void 0,
|
|
17208
|
+
false,
|
|
17209
|
+
Boolean(contextMessage)
|
|
17096
17210
|
),
|
|
17097
17211
|
() => compactionAbortController.abort()
|
|
17098
17212
|
);
|