@sentry/junior 0.114.0 → 0.115.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-WNBUY4OB.js → agent-hooks-4EUUXP2F.js} +12 -11
- package/dist/api/conversations/detail.d.ts +2 -0
- package/dist/api/conversations/event-list.d.ts +2 -0
- package/dist/api/health.d.ts +7 -0
- package/dist/api/plugin-reports.d.ts +62 -0
- package/dist/api/plugins.d.ts +9 -0
- package/dist/api/runtime.d.ts +24 -0
- package/dist/api/schema/conversation.d.ts +8 -0
- package/dist/api/schema.d.ts +2 -2
- package/dist/api/schema.js +6 -6
- package/dist/api/skills.d.ts +6 -0
- package/dist/api.js +123 -99
- package/dist/app.js +1104 -5006
- package/dist/catalog-runtime-Y32UEQLB.js +18 -0
- package/dist/chat/agent/request.d.ts +6 -0
- package/dist/chat/agent/sandbox.d.ts +2 -0
- package/dist/chat/agent/tools.d.ts +2 -0
- package/dist/chat/config.d.ts +2 -0
- package/dist/chat/conversations/history.d.ts +6 -0
- package/dist/chat/local/credential-sync.d.ts +5 -0
- package/dist/chat/local/oauth-relay.d.ts +4 -0
- package/dist/chat/mcp/oauth.d.ts +1 -0
- package/dist/chat/oauth-authorization.d.ts +17 -0
- package/dist/chat/oauth-flow.d.ts +9 -0
- package/dist/chat/sandbox/egress/schemas.d.ts +46 -0
- package/dist/chat/sandbox/egress/signals.d.ts +9 -0
- package/dist/chat/sandbox/sandbox.d.ts +2 -0
- package/dist/chat/sentry-payload-filter.d.ts +4 -4
- package/dist/chat/services/mcp-auth-orchestration.d.ts +5 -3
- package/dist/chat/services/plugin-auth-orchestration.d.ts +2 -0
- package/dist/chat/slack/message-control.d.ts +2 -0
- package/dist/chat/slack/oauth-authorization-message.d.ts +3 -0
- package/dist/chat/task-execution/slack-work.d.ts +2 -0
- package/dist/chat/tool-support/zod-tool.d.ts +1 -1
- package/dist/chat/tools/definition.d.ts +5 -0
- package/dist/{chunk-TLGM5X2M.js → chunk-2QG4MZQ7.js} +2 -2
- package/dist/{chunk-WV4UJBAS.js → chunk-43RYMWJ2.js} +523 -2373
- package/dist/chunk-4CODA7P5.js +43 -0
- package/dist/{chunk-YU6PO42Q.js → chunk-67CVNDJ7.js} +1 -1
- package/dist/{chunk-OSKEC4XU.js → chunk-6FKLPVF3.js} +1 -1
- package/dist/{chunk-2ZTTXTSZ.js → chunk-6OPTF4BQ.js} +10 -4
- package/dist/{chunk-W6RDUAO3.js → chunk-6V762AB4.js} +1 -1
- package/dist/{chunk-ABPSDO6M.js → chunk-AGLQYELI.js} +77 -8
- package/dist/{chunk-6QWPVTMO.js → chunk-CEYOL62C.js} +9 -7
- package/dist/{chunk-RD7EJI7H.js → chunk-CWEZEPD4.js} +3 -3
- package/dist/chunk-DIB4GX3G.js +8 -0
- package/dist/{chunk-K2W4CK7K.js → chunk-EW5MLKT5.js} +10 -75
- package/dist/{chunk-42WRQLJ6.js → chunk-F3B2YJ76.js} +0 -14
- package/dist/chunk-FA6POGLH.js +70 -0
- package/dist/{chunk-PM3PX42K.js → chunk-FHC4JUSF.js} +1 -1
- package/dist/{chunk-6Y5L4BOM.js → chunk-FXOYURO4.js} +5 -3
- package/dist/{chunk-S4QDH23A.js → chunk-G36V2YGB.js} +83 -81
- package/dist/{chunk-O4GT7LYB.js → chunk-G5SX4UPR.js} +3 -43
- package/dist/chunk-HDSDCEPK.js +80 -0
- package/dist/{chunk-FIGAF6KL.js → chunk-HWCQRGLL.js} +5 -3
- package/dist/{chunk-C4MZ3GP5.js → chunk-HZF5P5UN.js} +1 -1
- package/dist/chunk-LYA2AB5G.js +1914 -0
- package/dist/{chunk-4FUEG3QY.js → chunk-NVUQSFWX.js} +2 -2
- package/dist/chunk-OB42YVAE.js +16 -0
- package/dist/{chunk-XLFBQJUO.js → chunk-PVZHG2XZ.js} +4 -4
- package/dist/{chunk-VNNBSKCO.js → chunk-RAVKCKXA.js} +7 -7
- package/dist/{chunk-ZGXH3REA.js → chunk-SYJT7AKB.js} +12 -2
- package/dist/{chunk-5CZ7RETL.js → chunk-TVE25LPW.js} +1 -1
- package/dist/chunk-W3U3YQYI.js +71 -0
- package/dist/chunk-YCLO7D7M.js +4267 -0
- package/dist/cli/chat.js +72 -40
- package/dist/cli/check.js +10 -8
- package/dist/cli/init.js +1 -0
- package/dist/cli/plugins.js +16 -14
- package/dist/cli/snapshot-warmup.js +13 -11
- package/dist/cli/upgrade.js +10 -8
- package/dist/{db-4VLP7KBF.js → db-KBSJHAZU.js} +8 -7
- package/dist/handlers/sandbox-egress-signals.d.ts +4 -0
- package/dist/instrumentation.js +72 -4
- package/dist/nitro.js +2 -2
- package/dist/oauth-callback-server-EYRMJTCS.js +231 -0
- package/dist/oauth-relay-3ZU72UGL.js +9 -0
- package/dist/reporting-schema.d.ts +12 -4
- package/dist/reporting.d.ts +5 -5
- package/dist/{runner-IPXLWQOF.js → runner-RQGZ5Z2S.js} +139 -72
- package/dist/sandbox-egress-signals-CDFAP67O.js +75 -0
- package/dist/{skills-QCNZISNF.js → skills-JJQIE3BT.js} +10 -8
- package/dist/validation-MZ47PZP5.js +21 -0
- package/dist/version.js +1 -1
- package/package.json +5 -5
- package/dist/catalog-runtime-6O4M6JGA.js +0 -16
- package/dist/chat/oauth-authorization-message.d.ts +0 -6
- package/dist/validation-I2BW3UHD.js +0 -19
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
import {
|
|
2
|
+
authorizationId
|
|
3
|
+
} from "./chunk-DIB4GX3G.js";
|
|
4
|
+
import {
|
|
5
|
+
RetryableDeliveryError,
|
|
6
|
+
actorFromRouting,
|
|
7
|
+
addReactionToMessage,
|
|
8
|
+
assertRunRoutingConsistency,
|
|
9
|
+
createMcpOAuthClientProvider,
|
|
10
|
+
createUserTokenStore,
|
|
11
|
+
deleteMcpAuthSession,
|
|
12
|
+
deliverOAuthAuthorization,
|
|
13
|
+
formatProviderLabel,
|
|
14
|
+
getMcpAuthSession,
|
|
15
|
+
getSlackMessagePermalink,
|
|
16
|
+
hasEgressCredentialHooks,
|
|
17
|
+
issuePluginCredential,
|
|
18
|
+
issueProviderCredentialLease,
|
|
19
|
+
onPluginEgressResponse,
|
|
20
|
+
patchMcpAuthSession,
|
|
21
|
+
resolveBaseUrl,
|
|
22
|
+
selectPluginGrant,
|
|
23
|
+
slackOutputPolicy,
|
|
24
|
+
startOAuthFlow,
|
|
25
|
+
surfaceFromRouting,
|
|
26
|
+
toolInvocationDestination,
|
|
27
|
+
uploadFilesToConversation
|
|
28
|
+
} from "./chunk-LYA2AB5G.js";
|
|
1
29
|
import {
|
|
2
30
|
CooperativeTurnYieldError,
|
|
3
31
|
JUNIOR_THREAD_STATE_TTL_MS,
|
|
@@ -10,7 +38,6 @@ import {
|
|
|
10
38
|
extractAssistantText,
|
|
11
39
|
getAgentContextCompactionTriggerTokens,
|
|
12
40
|
getAgentContextInputLimitTokens,
|
|
13
|
-
getInterruptionMarker,
|
|
14
41
|
getPiMessageRole,
|
|
15
42
|
getTerminalAssistantMessages,
|
|
16
43
|
hasRuntimeTurnContext,
|
|
@@ -38,7 +65,13 @@ import {
|
|
|
38
65
|
stripRuntimeTurnContext,
|
|
39
66
|
trimTrailingAssistantMessages,
|
|
40
67
|
updateConversationStats
|
|
41
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-CEYOL62C.js";
|
|
69
|
+
import {
|
|
70
|
+
parseSandboxEgressAuthRequiredSignal,
|
|
71
|
+
parseSandboxEgressPermissionDeniedSignal,
|
|
72
|
+
sandboxEgressCredentialContextSchema,
|
|
73
|
+
sandboxEgressCredentialLeaseSchema
|
|
74
|
+
} from "./chunk-HDSDCEPK.js";
|
|
42
75
|
import {
|
|
43
76
|
buildNonInteractiveShellScript,
|
|
44
77
|
createSandboxSession,
|
|
@@ -55,7 +88,7 @@ import {
|
|
|
55
88
|
runNonInteractiveCommand,
|
|
56
89
|
throwSandboxOperationError,
|
|
57
90
|
wrapSandboxSetupError
|
|
58
|
-
} from "./chunk-
|
|
91
|
+
} from "./chunk-PVZHG2XZ.js";
|
|
59
92
|
import {
|
|
60
93
|
addAgentTurnUsage,
|
|
61
94
|
hasAgentTurnUsage
|
|
@@ -65,43 +98,37 @@ import {
|
|
|
65
98
|
} from "./chunk-UIE3R5XU.js";
|
|
66
99
|
import {
|
|
67
100
|
createPluginHookRunner,
|
|
68
|
-
createPluginLogger,
|
|
69
101
|
getPluginSystemPromptContributions,
|
|
70
102
|
getPluginTools,
|
|
71
103
|
getPluginUserPromptContributions,
|
|
72
|
-
getPlugins,
|
|
73
104
|
getSlackToolContext,
|
|
74
105
|
resolveChannelCapabilities
|
|
75
|
-
} from "./chunk-
|
|
106
|
+
} from "./chunk-RAVKCKXA.js";
|
|
76
107
|
import {
|
|
77
108
|
SANDBOX_DATA_ROOT,
|
|
78
109
|
SANDBOX_SKILLS_ROOT,
|
|
79
110
|
SANDBOX_WORKSPACE_ROOT,
|
|
80
|
-
acquireActiveLock,
|
|
81
111
|
getStateAdapter,
|
|
82
112
|
sandboxSkillDir,
|
|
83
113
|
sandboxSkillFile
|
|
84
|
-
} from "./chunk-
|
|
114
|
+
} from "./chunk-67CVNDJ7.js";
|
|
85
115
|
import {
|
|
86
116
|
SlackActionError,
|
|
87
117
|
downloadPrivateSlackFile,
|
|
88
118
|
getFilePermalink,
|
|
89
119
|
getSlackClient,
|
|
90
120
|
isConversationScopedChannel,
|
|
91
|
-
isDmChannel,
|
|
92
121
|
normalizeSlackConversationId,
|
|
93
122
|
withSlackRetries
|
|
94
|
-
} from "./chunk-
|
|
123
|
+
} from "./chunk-CWEZEPD4.js";
|
|
95
124
|
import {
|
|
96
125
|
contextProvenance,
|
|
97
126
|
getConversationEventStore,
|
|
98
127
|
getConversationSearchStore,
|
|
99
128
|
getConversationStore,
|
|
100
|
-
getDb,
|
|
101
129
|
instructionActors,
|
|
102
|
-
instructionProvenanceFor
|
|
103
|
-
|
|
104
|
-
} from "./chunk-FIGAF6KL.js";
|
|
130
|
+
instructionProvenanceFor
|
|
131
|
+
} from "./chunk-HWCQRGLL.js";
|
|
105
132
|
import {
|
|
106
133
|
sleep
|
|
107
134
|
} from "./chunk-4ZNGQH7C.js";
|
|
@@ -134,7 +161,7 @@ import {
|
|
|
134
161
|
renderCurrentInstruction,
|
|
135
162
|
resolveGatewayModel,
|
|
136
163
|
unwrapCurrentInstruction
|
|
137
|
-
} from "./chunk-
|
|
164
|
+
} from "./chunk-SYJT7AKB.js";
|
|
138
165
|
import {
|
|
139
166
|
parseSlackThreadId,
|
|
140
167
|
resolveConversationPrivacy,
|
|
@@ -144,26 +171,27 @@ import {
|
|
|
144
171
|
toGenAiMessagesTraceAttributes,
|
|
145
172
|
toGenAiPayloadMetadata,
|
|
146
173
|
toGenAiPayloadTraceAttributes
|
|
147
|
-
} from "./chunk-
|
|
174
|
+
} from "./chunk-FXOYURO4.js";
|
|
148
175
|
import {
|
|
149
176
|
discoverSkills,
|
|
150
177
|
findSkillByName,
|
|
151
178
|
loadSkillsByName,
|
|
152
179
|
parseSkillInvocation
|
|
153
|
-
} from "./chunk-
|
|
180
|
+
} from "./chunk-2QG4MZQ7.js";
|
|
154
181
|
import {
|
|
155
182
|
pluginCatalogRuntime
|
|
156
|
-
} from "./chunk-
|
|
183
|
+
} from "./chunk-6FKLPVF3.js";
|
|
157
184
|
import {
|
|
158
185
|
CredentialUnavailableError,
|
|
159
|
-
credentialContextSchema,
|
|
160
|
-
credentialUserSubjectId,
|
|
161
186
|
resolveAuthTokenPlaceholder,
|
|
162
187
|
resolvePluginCommandEnv
|
|
163
|
-
} from "./chunk-
|
|
188
|
+
} from "./chunk-EW5MLKT5.js";
|
|
189
|
+
import {
|
|
190
|
+
credentialUserSubjectId
|
|
191
|
+
} from "./chunk-W3U3YQYI.js";
|
|
164
192
|
import {
|
|
165
193
|
isUserActor
|
|
166
|
-
} from "./chunk-
|
|
194
|
+
} from "./chunk-HZF5P5UN.js";
|
|
167
195
|
import {
|
|
168
196
|
extractGenAiUsageAttributes,
|
|
169
197
|
extractGenAiUsageSummary,
|
|
@@ -181,13 +209,12 @@ import {
|
|
|
181
209
|
summarizeMessageText,
|
|
182
210
|
withLogContext,
|
|
183
211
|
withSpan
|
|
184
|
-
} from "./chunk-
|
|
212
|
+
} from "./chunk-NVUQSFWX.js";
|
|
185
213
|
import {
|
|
186
|
-
isRecord,
|
|
187
214
|
parseSlackChannelId,
|
|
188
215
|
parseSlackMessageTs,
|
|
189
216
|
parseSlackUserId
|
|
190
|
-
} from "./chunk-
|
|
217
|
+
} from "./chunk-F3B2YJ76.js";
|
|
191
218
|
import {
|
|
192
219
|
startInactiveSpan,
|
|
193
220
|
withActiveSpan
|
|
@@ -920,12 +947,12 @@ function sortJsonValue(value) {
|
|
|
920
947
|
Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, sortJsonValue(item)])
|
|
921
948
|
);
|
|
922
949
|
}
|
|
923
|
-
function
|
|
950
|
+
function isRecord(value) {
|
|
924
951
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
925
952
|
}
|
|
926
953
|
function injectContinuationToolName(details, toolName) {
|
|
927
954
|
const parsed = juniorToolResultSchema.parse(details);
|
|
928
|
-
if (!
|
|
955
|
+
if (!isRecord(parsed.continuation)) {
|
|
929
956
|
return parsed;
|
|
930
957
|
}
|
|
931
958
|
return juniorToolResultWithBoundContinuationSchema.parse({
|
|
@@ -1299,69 +1326,6 @@ var AuthorizationFlowDisabledError = class extends Error {
|
|
|
1299
1326
|
}
|
|
1300
1327
|
};
|
|
1301
1328
|
|
|
1302
|
-
// src/chat/agent/request.ts
|
|
1303
|
-
var RetryableDeliveryError = class extends Error {
|
|
1304
|
-
constructor(cause) {
|
|
1305
|
-
super("Assistant delivery was transient or ambiguous", { cause });
|
|
1306
|
-
this.name = "RetryableDeliveryError";
|
|
1307
|
-
}
|
|
1308
|
-
};
|
|
1309
|
-
function actorFromRouting(routing) {
|
|
1310
|
-
if (routing.dispatch?.actor) {
|
|
1311
|
-
return routing.dispatch.actor;
|
|
1312
|
-
}
|
|
1313
|
-
if (routing.actor) {
|
|
1314
|
-
return routing.actor;
|
|
1315
|
-
}
|
|
1316
|
-
if (routing.credentialContext && !("type" in routing.credentialContext.actor)) {
|
|
1317
|
-
return routing.credentialContext.actor;
|
|
1318
|
-
}
|
|
1319
|
-
return void 0;
|
|
1320
|
-
}
|
|
1321
|
-
function assertRunRoutingConsistency(request) {
|
|
1322
|
-
const { destination, source } = request.routing;
|
|
1323
|
-
if (source.platform !== destination.platform) {
|
|
1324
|
-
throw new TypeError("Run source and destination platforms do not match");
|
|
1325
|
-
}
|
|
1326
|
-
if (source.platform === "slack" && destination.platform === "slack") {
|
|
1327
|
-
if (source.teamId !== destination.teamId) {
|
|
1328
|
-
throw new TypeError("Slack source and destination teams do not match");
|
|
1329
|
-
}
|
|
1330
|
-
} else if (source.platform === "local" && destination.platform === "local" && (source.conversationId !== request.conversationId || destination.conversationId !== request.conversationId)) {
|
|
1331
|
-
throw new TypeError(
|
|
1332
|
-
"Local source, destination, and run conversation IDs do not match"
|
|
1333
|
-
);
|
|
1334
|
-
}
|
|
1335
|
-
const actor = request.routing.dispatch?.actor ?? request.routing.actor;
|
|
1336
|
-
if (!actor || actor.platform === "system") {
|
|
1337
|
-
return;
|
|
1338
|
-
}
|
|
1339
|
-
if (actor.platform !== destination.platform) {
|
|
1340
|
-
throw new TypeError(
|
|
1341
|
-
`Actor platform "${actor.platform}" does not match destination platform "${destination.platform}"`
|
|
1342
|
-
);
|
|
1343
|
-
}
|
|
1344
|
-
if (actor.platform === "slack" && destination.platform === "slack" && actor.teamId !== destination.teamId) {
|
|
1345
|
-
throw new TypeError("Slack actor team does not match destination team");
|
|
1346
|
-
}
|
|
1347
|
-
}
|
|
1348
|
-
function toolInvocationDestination(routing) {
|
|
1349
|
-
if (routing.destination.platform !== "slack" || !routing.toolChannelId) {
|
|
1350
|
-
return routing.destination;
|
|
1351
|
-
}
|
|
1352
|
-
return {
|
|
1353
|
-
platform: "slack",
|
|
1354
|
-
teamId: routing.destination.teamId,
|
|
1355
|
-
channelId: routing.toolChannelId
|
|
1356
|
-
};
|
|
1357
|
-
}
|
|
1358
|
-
function surfaceFromRouting(routing) {
|
|
1359
|
-
if (routing.surface) {
|
|
1360
|
-
return routing.surface;
|
|
1361
|
-
}
|
|
1362
|
-
return routing.source.platform === "slack" ? "slack" : "internal";
|
|
1363
|
-
}
|
|
1364
|
-
|
|
1365
1329
|
// src/chat/agent/session.ts
|
|
1366
1330
|
async function restoreSessionRecord(args) {
|
|
1367
1331
|
const sessionRecordState = await loadTurnSessionRecord({
|
|
@@ -1387,7 +1351,7 @@ function parseMcpProviderFromToolName(toolName) {
|
|
|
1387
1351
|
|
|
1388
1352
|
// src/chat/pi/derived-state.ts
|
|
1389
1353
|
var MCP_BRIDGE_TOOLS = /* @__PURE__ */ new Set(["callMcpTool", "searchMcpTools"]);
|
|
1390
|
-
function
|
|
1354
|
+
function isRecord2(value) {
|
|
1391
1355
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
1392
1356
|
}
|
|
1393
1357
|
function providerFromToolName(value) {
|
|
@@ -1409,7 +1373,7 @@ function addBridgeToolProvider(toolName, value, providers) {
|
|
|
1409
1373
|
if (bridgeTool === "searchMcpTools") {
|
|
1410
1374
|
for (const argsKey of ["input", "args", "arguments", "params"]) {
|
|
1411
1375
|
const args = value[argsKey];
|
|
1412
|
-
if (
|
|
1376
|
+
if (isRecord2(args)) {
|
|
1413
1377
|
addString(providers, args.provider);
|
|
1414
1378
|
}
|
|
1415
1379
|
}
|
|
@@ -1418,7 +1382,7 @@ function addBridgeToolProvider(toolName, value, providers) {
|
|
|
1418
1382
|
if (bridgeTool === "callMcpTool") {
|
|
1419
1383
|
for (const argsKey of ["input", "args", "arguments", "params"]) {
|
|
1420
1384
|
const args = value[argsKey];
|
|
1421
|
-
if (
|
|
1385
|
+
if (isRecord2(args)) {
|
|
1422
1386
|
addString(providers, providerFromToolName(args.tool_name));
|
|
1423
1387
|
}
|
|
1424
1388
|
}
|
|
@@ -1431,18 +1395,18 @@ function addMcpResultProvider(message, providers) {
|
|
|
1431
1395
|
return;
|
|
1432
1396
|
}
|
|
1433
1397
|
if (toolName === "loadSkill") {
|
|
1434
|
-
if (
|
|
1398
|
+
if (isRecord2(message.details)) {
|
|
1435
1399
|
addString(providers, message.details.mcp_provider);
|
|
1436
1400
|
}
|
|
1437
1401
|
addString(providers, message.mcp_provider);
|
|
1438
1402
|
return;
|
|
1439
1403
|
}
|
|
1440
1404
|
if (toolName === "searchMcpTools") {
|
|
1441
|
-
if (
|
|
1405
|
+
if (isRecord2(message.details)) {
|
|
1442
1406
|
addString(providers, message.details.provider);
|
|
1443
1407
|
if (Array.isArray(message.details.tools)) {
|
|
1444
1408
|
for (const tool of message.details.tools) {
|
|
1445
|
-
if (
|
|
1409
|
+
if (isRecord2(tool)) {
|
|
1446
1410
|
addString(providers, providerFromToolName(tool.tool_name));
|
|
1447
1411
|
}
|
|
1448
1412
|
}
|
|
@@ -1454,11 +1418,11 @@ function addMcpResultProvider(message, providers) {
|
|
|
1454
1418
|
if (toolName === "callMcpTool") {
|
|
1455
1419
|
for (const argsKey of ["input", "args", "arguments", "params"]) {
|
|
1456
1420
|
const args = message[argsKey];
|
|
1457
|
-
if (
|
|
1421
|
+
if (isRecord2(args)) {
|
|
1458
1422
|
addString(providers, providerFromToolName(args.tool_name));
|
|
1459
1423
|
}
|
|
1460
1424
|
}
|
|
1461
|
-
if (
|
|
1425
|
+
if (isRecord2(message.details)) {
|
|
1462
1426
|
addString(providers, message.details.provider);
|
|
1463
1427
|
addString(providers, providerFromToolName(message.details.tool_name));
|
|
1464
1428
|
}
|
|
@@ -1466,7 +1430,7 @@ function addMcpResultProvider(message, providers) {
|
|
|
1466
1430
|
}
|
|
1467
1431
|
}
|
|
1468
1432
|
function scanMcpProviders(message, providers) {
|
|
1469
|
-
if (!
|
|
1433
|
+
if (!isRecord2(message)) {
|
|
1470
1434
|
return;
|
|
1471
1435
|
}
|
|
1472
1436
|
if (message.role === "toolResult") {
|
|
@@ -1478,15 +1442,15 @@ function scanMcpProviders(message, providers) {
|
|
|
1478
1442
|
return;
|
|
1479
1443
|
}
|
|
1480
1444
|
for (const part of content) {
|
|
1481
|
-
if (!
|
|
1445
|
+
if (!isRecord2(part)) {
|
|
1482
1446
|
continue;
|
|
1483
1447
|
}
|
|
1484
1448
|
addBridgeToolProvider(getToolName(part), part, providers);
|
|
1485
1449
|
}
|
|
1486
1450
|
}
|
|
1487
1451
|
function scanLoadedSkills(message, skills) {
|
|
1488
|
-
if (
|
|
1489
|
-
if (
|
|
1452
|
+
if (isRecord2(message) && message.role === "toolResult" && message.toolName === "loadSkill" && message.isError !== true) {
|
|
1453
|
+
if (isRecord2(message.details)) {
|
|
1490
1454
|
addString(skills, message.details.skill_name);
|
|
1491
1455
|
}
|
|
1492
1456
|
addString(skills, message.skill_name);
|
|
@@ -1573,424 +1537,6 @@ import { isDeepStrictEqual } from "util";
|
|
|
1573
1537
|
// src/chat/prompt.ts
|
|
1574
1538
|
import fs2 from "fs";
|
|
1575
1539
|
import path2 from "path";
|
|
1576
|
-
|
|
1577
|
-
// src/chat/slack/status-format.ts
|
|
1578
|
-
var SLACK_STATUS_MAX_LENGTH = 50;
|
|
1579
|
-
function truncateStatusText(text) {
|
|
1580
|
-
const trimmed = text.trim();
|
|
1581
|
-
if (!trimmed) {
|
|
1582
|
-
return "";
|
|
1583
|
-
}
|
|
1584
|
-
if (trimmed.length <= SLACK_STATUS_MAX_LENGTH) {
|
|
1585
|
-
return trimmed;
|
|
1586
|
-
}
|
|
1587
|
-
return `${trimmed.slice(0, SLACK_STATUS_MAX_LENGTH - 3).trimEnd()}...`;
|
|
1588
|
-
}
|
|
1589
|
-
|
|
1590
|
-
// src/chat/slack/mrkdwn.ts
|
|
1591
|
-
function escapeSlackMrkdwnText(text) {
|
|
1592
|
-
return text.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">");
|
|
1593
|
-
}
|
|
1594
|
-
function escapeSlackLinkUrl(url) {
|
|
1595
|
-
return url.replaceAll("&", "&").replaceAll("<", "%3C").replaceAll(">", "%3E");
|
|
1596
|
-
}
|
|
1597
|
-
function formatSlackLink(url, label) {
|
|
1598
|
-
return `<${escapeSlackLinkUrl(url)}|${escapeSlackMrkdwnText(label)}>`;
|
|
1599
|
-
}
|
|
1600
|
-
function readInlineCodeSpan(line, start) {
|
|
1601
|
-
if (line[start] !== "`") {
|
|
1602
|
-
return void 0;
|
|
1603
|
-
}
|
|
1604
|
-
let n = 1;
|
|
1605
|
-
while (line[start + n] === "`") {
|
|
1606
|
-
n++;
|
|
1607
|
-
}
|
|
1608
|
-
const marker = "`".repeat(n);
|
|
1609
|
-
let search = start + n;
|
|
1610
|
-
while (search < line.length) {
|
|
1611
|
-
const close = line.indexOf(marker, search);
|
|
1612
|
-
if (close === -1) {
|
|
1613
|
-
return void 0;
|
|
1614
|
-
}
|
|
1615
|
-
const after = close + n;
|
|
1616
|
-
if (line[after] !== "`") {
|
|
1617
|
-
return { text: line.slice(start, after), end: after };
|
|
1618
|
-
}
|
|
1619
|
-
search = after + 1;
|
|
1620
|
-
}
|
|
1621
|
-
return void 0;
|
|
1622
|
-
}
|
|
1623
|
-
function readExistingSlackAngleToken(line, start) {
|
|
1624
|
-
if (line[start] !== "<") {
|
|
1625
|
-
return void 0;
|
|
1626
|
-
}
|
|
1627
|
-
const close = line.indexOf(">", start + 1);
|
|
1628
|
-
if (close === -1) {
|
|
1629
|
-
return void 0;
|
|
1630
|
-
}
|
|
1631
|
-
const body = line.slice(start + 1, close);
|
|
1632
|
-
if (/^(?:https?:\/\/|@|#|!)/.test(body)) {
|
|
1633
|
-
return { text: line.slice(start, close + 1), end: close + 1 };
|
|
1634
|
-
}
|
|
1635
|
-
return void 0;
|
|
1636
|
-
}
|
|
1637
|
-
function readMarkdownLink(line, start) {
|
|
1638
|
-
if (line[start] !== "[") {
|
|
1639
|
-
return void 0;
|
|
1640
|
-
}
|
|
1641
|
-
const labelEnd = line.indexOf("](", start + 1);
|
|
1642
|
-
if (labelEnd === -1) {
|
|
1643
|
-
return void 0;
|
|
1644
|
-
}
|
|
1645
|
-
const destStart = labelEnd + 2;
|
|
1646
|
-
if (!line.startsWith("http://", destStart) && !line.startsWith("https://", destStart)) {
|
|
1647
|
-
return void 0;
|
|
1648
|
-
}
|
|
1649
|
-
const closeParens = line.indexOf(")", destStart);
|
|
1650
|
-
if (closeParens === -1) {
|
|
1651
|
-
return void 0;
|
|
1652
|
-
}
|
|
1653
|
-
return { text: line.slice(start, closeParens + 1), end: closeParens + 1 };
|
|
1654
|
-
}
|
|
1655
|
-
function hasUnmatchedClosingParen(text) {
|
|
1656
|
-
let balance = 0;
|
|
1657
|
-
for (const ch of text) {
|
|
1658
|
-
if (ch === "(") balance++;
|
|
1659
|
-
else if (ch === ")") balance--;
|
|
1660
|
-
}
|
|
1661
|
-
return balance < 0;
|
|
1662
|
-
}
|
|
1663
|
-
function readBareUrl(line, start) {
|
|
1664
|
-
let end = start;
|
|
1665
|
-
while (end < line.length) {
|
|
1666
|
-
const ch = line[end];
|
|
1667
|
-
if (/\s/.test(ch) || ch === "<" || ch === ">" || ch === '"' || ch === "`" || ch === "|" || ch === "*") {
|
|
1668
|
-
break;
|
|
1669
|
-
}
|
|
1670
|
-
end++;
|
|
1671
|
-
}
|
|
1672
|
-
if (end === start) {
|
|
1673
|
-
return void 0;
|
|
1674
|
-
}
|
|
1675
|
-
let raw = line.slice(start, end);
|
|
1676
|
-
let suffix = "";
|
|
1677
|
-
const peel = () => {
|
|
1678
|
-
suffix = raw.slice(-1) + suffix;
|
|
1679
|
-
raw = raw.slice(0, -1);
|
|
1680
|
-
};
|
|
1681
|
-
const shouldPeel = () => raw.endsWith("_") || /[.,!?;:]$/.test(raw) || raw.endsWith(")") && hasUnmatchedClosingParen(raw);
|
|
1682
|
-
while (raw.length > 0 && shouldPeel()) {
|
|
1683
|
-
peel();
|
|
1684
|
-
}
|
|
1685
|
-
if (!/^https?:\/\/.+/.test(raw)) {
|
|
1686
|
-
return void 0;
|
|
1687
|
-
}
|
|
1688
|
-
return { url: raw, suffix, end };
|
|
1689
|
-
}
|
|
1690
|
-
function wrapBareUrlsOnLine(line) {
|
|
1691
|
-
let result = "";
|
|
1692
|
-
let i = 0;
|
|
1693
|
-
while (i < line.length) {
|
|
1694
|
-
const codeSpan = readInlineCodeSpan(line, i);
|
|
1695
|
-
if (codeSpan) {
|
|
1696
|
-
result += codeSpan.text;
|
|
1697
|
-
i = codeSpan.end;
|
|
1698
|
-
continue;
|
|
1699
|
-
}
|
|
1700
|
-
const angleToken = readExistingSlackAngleToken(line, i);
|
|
1701
|
-
if (angleToken) {
|
|
1702
|
-
result += angleToken.text;
|
|
1703
|
-
i = angleToken.end;
|
|
1704
|
-
continue;
|
|
1705
|
-
}
|
|
1706
|
-
const mdLink = readMarkdownLink(line, i);
|
|
1707
|
-
if (mdLink) {
|
|
1708
|
-
result += mdLink.text;
|
|
1709
|
-
i = mdLink.end;
|
|
1710
|
-
continue;
|
|
1711
|
-
}
|
|
1712
|
-
if (line.startsWith("https://", i) || line.startsWith("http://", i)) {
|
|
1713
|
-
const parsed = readBareUrl(line, i);
|
|
1714
|
-
if (parsed) {
|
|
1715
|
-
result += `<${parsed.url}>${parsed.suffix}`;
|
|
1716
|
-
i = parsed.end;
|
|
1717
|
-
continue;
|
|
1718
|
-
}
|
|
1719
|
-
}
|
|
1720
|
-
result += line[i];
|
|
1721
|
-
i++;
|
|
1722
|
-
}
|
|
1723
|
-
return result;
|
|
1724
|
-
}
|
|
1725
|
-
function wrapBareUrls(text) {
|
|
1726
|
-
const lines = text.split("\n");
|
|
1727
|
-
const out = [];
|
|
1728
|
-
let inCodeBlock = false;
|
|
1729
|
-
for (const line of lines) {
|
|
1730
|
-
if (line.trimStart().startsWith("```")) {
|
|
1731
|
-
inCodeBlock = !inCodeBlock;
|
|
1732
|
-
out.push(line);
|
|
1733
|
-
continue;
|
|
1734
|
-
}
|
|
1735
|
-
out.push(inCodeBlock ? line : wrapBareUrlsOnLine(line));
|
|
1736
|
-
}
|
|
1737
|
-
return out.join("\n");
|
|
1738
|
-
}
|
|
1739
|
-
function ensureBlockSpacing(text) {
|
|
1740
|
-
const codeBlockPattern = /^```/;
|
|
1741
|
-
const listItemPattern = /^[-*•]\s|^\d+\.\s/;
|
|
1742
|
-
const lines = text.split("\n");
|
|
1743
|
-
const result = [];
|
|
1744
|
-
let inCodeBlock = false;
|
|
1745
|
-
for (let i = 0; i < lines.length; i++) {
|
|
1746
|
-
const line = lines[i];
|
|
1747
|
-
const isCodeFence = codeBlockPattern.test(line.trimStart());
|
|
1748
|
-
if (isCodeFence) {
|
|
1749
|
-
if (!inCodeBlock) {
|
|
1750
|
-
const prev2 = result.length > 0 ? result[result.length - 1] : void 0;
|
|
1751
|
-
if (prev2 !== void 0 && prev2.trim() !== "") {
|
|
1752
|
-
result.push("");
|
|
1753
|
-
}
|
|
1754
|
-
}
|
|
1755
|
-
inCodeBlock = !inCodeBlock;
|
|
1756
|
-
result.push(line);
|
|
1757
|
-
continue;
|
|
1758
|
-
}
|
|
1759
|
-
if (inCodeBlock) {
|
|
1760
|
-
result.push(line);
|
|
1761
|
-
continue;
|
|
1762
|
-
}
|
|
1763
|
-
const prev = result.length > 0 ? result[result.length - 1] : void 0;
|
|
1764
|
-
if (prev !== void 0 && prev.trim() !== "" && line.trim() !== "" && !(listItemPattern.test(prev.trimStart()) && listItemPattern.test(line.trimStart()))) {
|
|
1765
|
-
result.push("");
|
|
1766
|
-
}
|
|
1767
|
-
result.push(line);
|
|
1768
|
-
}
|
|
1769
|
-
return result.join("\n");
|
|
1770
|
-
}
|
|
1771
|
-
function normalizeSlackReplyMarkdown(text) {
|
|
1772
|
-
let normalized = text.replace(/\r\n?/g, "\n").replace(/[ \t]+$/gm, "");
|
|
1773
|
-
normalized = wrapBareUrls(normalized);
|
|
1774
|
-
normalized = ensureBlockSpacing(normalized);
|
|
1775
|
-
return normalized.replace(/\n{3,}/g, "\n\n").trim();
|
|
1776
|
-
}
|
|
1777
|
-
function normalizeSlackStatusText(text) {
|
|
1778
|
-
const trimmed = text.trim();
|
|
1779
|
-
if (!trimmed) {
|
|
1780
|
-
return "";
|
|
1781
|
-
}
|
|
1782
|
-
return truncateStatusText(trimmed.replace(/(?:\.\s*)+$/, "").trim());
|
|
1783
|
-
}
|
|
1784
|
-
|
|
1785
|
-
// src/chat/slack/output.ts
|
|
1786
|
-
var MAX_INLINE_CHARS = 2200;
|
|
1787
|
-
var MAX_INLINE_LINES = 45;
|
|
1788
|
-
var CONTINUED_MARKER = "\n\n[Continued below]";
|
|
1789
|
-
function countSlackLines(text) {
|
|
1790
|
-
if (!text) {
|
|
1791
|
-
return 0;
|
|
1792
|
-
}
|
|
1793
|
-
return text.split("\n").length;
|
|
1794
|
-
}
|
|
1795
|
-
function fitsInlineBudget(text, maxChars = MAX_INLINE_CHARS, maxLines = MAX_INLINE_LINES) {
|
|
1796
|
-
return text.length <= maxChars && countSlackLines(text) <= maxLines;
|
|
1797
|
-
}
|
|
1798
|
-
function findSplitIndex(text, maxChars) {
|
|
1799
|
-
if (text.length <= maxChars) {
|
|
1800
|
-
return text.length;
|
|
1801
|
-
}
|
|
1802
|
-
const bounded = text.slice(0, maxChars);
|
|
1803
|
-
const newlineIndex = bounded.lastIndexOf("\n");
|
|
1804
|
-
if (newlineIndex > 0) {
|
|
1805
|
-
return newlineIndex;
|
|
1806
|
-
}
|
|
1807
|
-
const spaceIndex = bounded.lastIndexOf(" ");
|
|
1808
|
-
if (spaceIndex > 0) {
|
|
1809
|
-
return spaceIndex;
|
|
1810
|
-
}
|
|
1811
|
-
return maxChars;
|
|
1812
|
-
}
|
|
1813
|
-
function splitByLineBudget(text, maxLines) {
|
|
1814
|
-
if (maxLines <= 0) {
|
|
1815
|
-
return "";
|
|
1816
|
-
}
|
|
1817
|
-
const lines = text.split("\n");
|
|
1818
|
-
if (lines.length <= maxLines) {
|
|
1819
|
-
return text;
|
|
1820
|
-
}
|
|
1821
|
-
return lines.slice(0, maxLines).join("\n");
|
|
1822
|
-
}
|
|
1823
|
-
function reserveInlineBudgetForSuffix(suffix, maxChars = MAX_INLINE_CHARS, maxLines = MAX_INLINE_LINES) {
|
|
1824
|
-
return {
|
|
1825
|
-
maxChars: Math.max(1, maxChars - suffix.length),
|
|
1826
|
-
maxLines: Math.max(1, maxLines - Math.max(0, countSlackLines(suffix) - 1))
|
|
1827
|
-
};
|
|
1828
|
-
}
|
|
1829
|
-
function forceSplitBudget(text, budget) {
|
|
1830
|
-
const lineCount = countSlackLines(text);
|
|
1831
|
-
return {
|
|
1832
|
-
maxChars: text.length <= budget.maxChars ? Math.max(1, text.length - 1) : budget.maxChars,
|
|
1833
|
-
maxLines: lineCount <= budget.maxLines ? Math.max(1, lineCount - 1) : budget.maxLines
|
|
1834
|
-
};
|
|
1835
|
-
}
|
|
1836
|
-
function getFenceContinuation(text) {
|
|
1837
|
-
let open;
|
|
1838
|
-
for (const line of text.split("\n")) {
|
|
1839
|
-
const trimmed = line.trimStart();
|
|
1840
|
-
const openerMatch = trimmed.match(/^(`{3,}|~{3,})(.*)$/);
|
|
1841
|
-
if (!openerMatch) {
|
|
1842
|
-
continue;
|
|
1843
|
-
}
|
|
1844
|
-
if (!open) {
|
|
1845
|
-
open = {
|
|
1846
|
-
fence: openerMatch[1],
|
|
1847
|
-
openerLine: trimmed
|
|
1848
|
-
};
|
|
1849
|
-
continue;
|
|
1850
|
-
}
|
|
1851
|
-
if (new RegExp(`^${open.fence}\\s*$`).test(trimmed)) {
|
|
1852
|
-
open = void 0;
|
|
1853
|
-
}
|
|
1854
|
-
}
|
|
1855
|
-
if (!open) {
|
|
1856
|
-
return null;
|
|
1857
|
-
}
|
|
1858
|
-
return {
|
|
1859
|
-
closeSuffix: text.endsWith("\n") ? open.fence : `
|
|
1860
|
-
${open.fence}`,
|
|
1861
|
-
reopenPrefix: `${open.openerLine}
|
|
1862
|
-
`
|
|
1863
|
-
};
|
|
1864
|
-
}
|
|
1865
|
-
function appendSlackSuffix(text, marker) {
|
|
1866
|
-
const carryover = getFenceContinuation(text);
|
|
1867
|
-
return `${text}${carryover?.closeSuffix ?? ""}${marker}`;
|
|
1868
|
-
}
|
|
1869
|
-
function stripTrailingContinuationMarker(text) {
|
|
1870
|
-
return text.endsWith(CONTINUED_MARKER) ? text.slice(0, -CONTINUED_MARKER.length) : text;
|
|
1871
|
-
}
|
|
1872
|
-
function takeSlackContinuationChunk(text, budget) {
|
|
1873
|
-
let { prefix, rest } = takeSlackInlinePrefix(text, budget);
|
|
1874
|
-
if (!rest) {
|
|
1875
|
-
({ prefix, rest } = takeSlackInlinePrefix(
|
|
1876
|
-
text,
|
|
1877
|
-
forceSplitBudget(text, budget)
|
|
1878
|
-
));
|
|
1879
|
-
}
|
|
1880
|
-
let carryover = rest ? getFenceContinuation(prefix) : null;
|
|
1881
|
-
if (!carryover) {
|
|
1882
|
-
return { prefix, rest };
|
|
1883
|
-
}
|
|
1884
|
-
const carryoverBudget = reserveInlineBudgetForSuffix(
|
|
1885
|
-
`${carryover.closeSuffix}${CONTINUED_MARKER}`
|
|
1886
|
-
);
|
|
1887
|
-
({ prefix, rest } = takeSlackInlinePrefix(text, carryoverBudget));
|
|
1888
|
-
if (!rest) {
|
|
1889
|
-
({ prefix, rest } = takeSlackInlinePrefix(
|
|
1890
|
-
text,
|
|
1891
|
-
forceSplitBudget(text, carryoverBudget)
|
|
1892
|
-
));
|
|
1893
|
-
}
|
|
1894
|
-
carryover = rest ? getFenceContinuation(prefix) : null;
|
|
1895
|
-
if (!carryover) {
|
|
1896
|
-
return { prefix, rest };
|
|
1897
|
-
}
|
|
1898
|
-
return {
|
|
1899
|
-
prefix,
|
|
1900
|
-
rest: `${carryover.reopenPrefix}${rest}`
|
|
1901
|
-
};
|
|
1902
|
-
}
|
|
1903
|
-
function takeSlackContinuationPrefix(text, options) {
|
|
1904
|
-
const budget = {
|
|
1905
|
-
maxChars: options?.maxChars ?? getSlackContinuationBudget().maxChars,
|
|
1906
|
-
maxLines: options?.maxLines ?? getSlackContinuationBudget().maxLines
|
|
1907
|
-
};
|
|
1908
|
-
const { prefix, rest } = (() => {
|
|
1909
|
-
if (options?.forceSplit) {
|
|
1910
|
-
return takeSlackContinuationChunk(text, budget);
|
|
1911
|
-
}
|
|
1912
|
-
const initial = takeSlackInlinePrefix(text, budget);
|
|
1913
|
-
return initial.rest ? takeSlackContinuationChunk(text, budget) : initial;
|
|
1914
|
-
})();
|
|
1915
|
-
return {
|
|
1916
|
-
prefix,
|
|
1917
|
-
renderedPrefix: rest ? appendSlackSuffix(prefix, CONTINUED_MARKER) : prefix,
|
|
1918
|
-
rest
|
|
1919
|
-
};
|
|
1920
|
-
}
|
|
1921
|
-
function takeSlackInlinePrefix(text, options) {
|
|
1922
|
-
const maxChars = options?.maxChars ?? MAX_INLINE_CHARS;
|
|
1923
|
-
const maxLines = options?.maxLines ?? MAX_INLINE_LINES;
|
|
1924
|
-
const normalized = text.replace(/\r\n?/g, "\n");
|
|
1925
|
-
if (!normalized) {
|
|
1926
|
-
return { prefix: "", rest: "" };
|
|
1927
|
-
}
|
|
1928
|
-
if (fitsInlineBudget(normalized, maxChars, maxLines)) {
|
|
1929
|
-
return { prefix: normalized, rest: "" };
|
|
1930
|
-
}
|
|
1931
|
-
const lineBounded = splitByLineBudget(normalized, maxLines);
|
|
1932
|
-
const cutIndex = findSplitIndex(lineBounded, maxChars);
|
|
1933
|
-
const prefix = lineBounded.slice(0, cutIndex).trimEnd();
|
|
1934
|
-
if (prefix) {
|
|
1935
|
-
return {
|
|
1936
|
-
prefix,
|
|
1937
|
-
rest: normalized.slice(prefix.length).trimStart()
|
|
1938
|
-
};
|
|
1939
|
-
}
|
|
1940
|
-
const hardPrefix = normalized.slice(0, Math.max(1, maxChars)).trimEnd();
|
|
1941
|
-
return {
|
|
1942
|
-
prefix: hardPrefix || normalized.slice(0, Math.max(1, maxChars)),
|
|
1943
|
-
rest: normalized.slice(hardPrefix.length || Math.max(1, maxChars)).trimStart()
|
|
1944
|
-
};
|
|
1945
|
-
}
|
|
1946
|
-
function splitSlackReplyText(text, options) {
|
|
1947
|
-
const normalized = normalizeSlackReplyMarkdown(text);
|
|
1948
|
-
if (!normalized) {
|
|
1949
|
-
return [];
|
|
1950
|
-
}
|
|
1951
|
-
const chunks = [];
|
|
1952
|
-
const continuationBudget = reserveInlineBudgetForSuffix(CONTINUED_MARKER);
|
|
1953
|
-
let remaining = normalized;
|
|
1954
|
-
while (remaining) {
|
|
1955
|
-
const fitsFinalChunk = options?.interrupted ? fitsInlineBudget(appendSlackSuffix(remaining, getInterruptionMarker())) : fitsInlineBudget(remaining);
|
|
1956
|
-
if (fitsFinalChunk) {
|
|
1957
|
-
chunks.push(
|
|
1958
|
-
options?.interrupted ? appendSlackSuffix(remaining, getInterruptionMarker()) : remaining
|
|
1959
|
-
);
|
|
1960
|
-
break;
|
|
1961
|
-
}
|
|
1962
|
-
const { renderedPrefix, rest } = takeSlackContinuationPrefix(remaining, {
|
|
1963
|
-
...continuationBudget,
|
|
1964
|
-
forceSplit: true
|
|
1965
|
-
});
|
|
1966
|
-
chunks.push(renderedPrefix);
|
|
1967
|
-
remaining = rest;
|
|
1968
|
-
}
|
|
1969
|
-
if (chunks.length === 2) {
|
|
1970
|
-
chunks[0] = stripTrailingContinuationMarker(chunks[0] ?? "");
|
|
1971
|
-
}
|
|
1972
|
-
return chunks;
|
|
1973
|
-
}
|
|
1974
|
-
function getSlackContinuationBudget() {
|
|
1975
|
-
return reserveInlineBudgetForSuffix(CONTINUED_MARKER);
|
|
1976
|
-
}
|
|
1977
|
-
function buildSlackOutputMessage(text) {
|
|
1978
|
-
const normalized = normalizeSlackReplyMarkdown(text);
|
|
1979
|
-
if (!normalized) {
|
|
1980
|
-
throw new Error(
|
|
1981
|
-
`Slack output normalized to empty content: original_length=${text.length} parsed_length=${normalized.length}`
|
|
1982
|
-
);
|
|
1983
|
-
}
|
|
1984
|
-
return {
|
|
1985
|
-
markdown: normalized
|
|
1986
|
-
};
|
|
1987
|
-
}
|
|
1988
|
-
var slackOutputPolicy = {
|
|
1989
|
-
maxInlineChars: MAX_INLINE_CHARS,
|
|
1990
|
-
maxInlineLines: MAX_INLINE_LINES
|
|
1991
|
-
};
|
|
1992
|
-
|
|
1993
|
-
// src/chat/prompt.ts
|
|
1994
1540
|
var DEFAULT_SOUL = "You are Junior, a practical and concise assistant.";
|
|
1995
1541
|
function getLoggedMarkdownFiles() {
|
|
1996
1542
|
const globalState = globalThis;
|
|
@@ -2824,160 +2370,6 @@ async function assemblePrompt(args) {
|
|
|
2824
2370
|
};
|
|
2825
2371
|
}
|
|
2826
2372
|
|
|
2827
|
-
// src/chat/capabilities/catalog.ts
|
|
2828
|
-
var cachedCatalog;
|
|
2829
|
-
function getCapabilityCatalog() {
|
|
2830
|
-
const signature = pluginCatalogRuntime.getSignature();
|
|
2831
|
-
if (cachedCatalog?.signature === signature) return cachedCatalog;
|
|
2832
|
-
const providers = pluginCatalogRuntime.getCapabilityProviders();
|
|
2833
|
-
const capabilityToProvider = /* @__PURE__ */ new Map();
|
|
2834
|
-
for (const provider of providers) {
|
|
2835
|
-
for (const capability of provider.capabilities) {
|
|
2836
|
-
if (capabilityToProvider.has(capability)) {
|
|
2837
|
-
throw new Error(
|
|
2838
|
-
`Duplicate capability registration for "${capability}"`
|
|
2839
|
-
);
|
|
2840
|
-
}
|
|
2841
|
-
capabilityToProvider.set(capability, provider);
|
|
2842
|
-
}
|
|
2843
|
-
}
|
|
2844
|
-
cachedCatalog = { signature, providers, capabilityToProvider };
|
|
2845
|
-
return cachedCatalog;
|
|
2846
|
-
}
|
|
2847
|
-
var catalogLogged = false;
|
|
2848
|
-
function logCapabilityCatalogLoadedOnce() {
|
|
2849
|
-
if (catalogLogged) return;
|
|
2850
|
-
catalogLogged = true;
|
|
2851
|
-
const { providers } = getCapabilityCatalog();
|
|
2852
|
-
const capabilityNames = providers.flatMap((p) => p.capabilities).sort();
|
|
2853
|
-
const configKeys = [
|
|
2854
|
-
...new Set(providers.flatMap((p) => p.configKeys))
|
|
2855
|
-
].sort();
|
|
2856
|
-
logInfo(
|
|
2857
|
-
"capability_catalog_loaded",
|
|
2858
|
-
{},
|
|
2859
|
-
{
|
|
2860
|
-
"app.capability.providers": providers.map((p) => p.provider),
|
|
2861
|
-
"app.capability.count": capabilityNames.length,
|
|
2862
|
-
"app.capability.names": capabilityNames,
|
|
2863
|
-
"app.config.key_count": configKeys.length,
|
|
2864
|
-
"app.config.keys": configKeys
|
|
2865
|
-
},
|
|
2866
|
-
"Loaded capability provider catalog"
|
|
2867
|
-
);
|
|
2868
|
-
}
|
|
2869
|
-
|
|
2870
|
-
// src/chat/capabilities/router.ts
|
|
2871
|
-
var ProviderCredentialRouter = class {
|
|
2872
|
-
brokersByProvider;
|
|
2873
|
-
constructor(input) {
|
|
2874
|
-
this.brokersByProvider = input.brokersByProvider;
|
|
2875
|
-
}
|
|
2876
|
-
async issue(input) {
|
|
2877
|
-
const broker = this.brokersByProvider[input.provider];
|
|
2878
|
-
if (!broker) {
|
|
2879
|
-
throw new Error(
|
|
2880
|
-
`No credential broker registered for provider: ${input.provider}`
|
|
2881
|
-
);
|
|
2882
|
-
}
|
|
2883
|
-
return await broker.issue({
|
|
2884
|
-
context: input.context,
|
|
2885
|
-
reason: input.reason
|
|
2886
|
-
});
|
|
2887
|
-
}
|
|
2888
|
-
};
|
|
2889
|
-
|
|
2890
|
-
// src/chat/credentials/user-token-store.ts
|
|
2891
|
-
import {
|
|
2892
|
-
pluginStoredTokensSchema
|
|
2893
|
-
} from "@sentry/junior-plugin-api";
|
|
2894
|
-
var storedTokensSchema = pluginStoredTokensSchema;
|
|
2895
|
-
|
|
2896
|
-
// src/chat/credentials/state-adapter-token-store.ts
|
|
2897
|
-
var KEY_PREFIX = "oauth-token";
|
|
2898
|
-
var BUFFER_MS = 24 * 60 * 60 * 1e3;
|
|
2899
|
-
var LONG_LIVED_TTL_MS = 365 * 24 * 60 * 60 * 1e3;
|
|
2900
|
-
var REFRESH_LOCK_WAIT_MS = 3e4;
|
|
2901
|
-
var REFRESH_LOCK_RETRY_MS = 100;
|
|
2902
|
-
function tokenKey(userId, provider) {
|
|
2903
|
-
return `${KEY_PREFIX}:${userId}:${provider}`;
|
|
2904
|
-
}
|
|
2905
|
-
function refreshLockKey(userId, provider) {
|
|
2906
|
-
return `${tokenKey(userId, provider)}:refresh`;
|
|
2907
|
-
}
|
|
2908
|
-
var StateAdapterTokenStore = class {
|
|
2909
|
-
state;
|
|
2910
|
-
constructor(stateAdapter) {
|
|
2911
|
-
this.state = stateAdapter;
|
|
2912
|
-
}
|
|
2913
|
-
async get(userId, provider) {
|
|
2914
|
-
const stored = await this.state.get(tokenKey(userId, provider));
|
|
2915
|
-
return stored === null || stored === void 0 ? void 0 : storedTokensSchema.parse(stored);
|
|
2916
|
-
}
|
|
2917
|
-
async set(userId, provider, tokens) {
|
|
2918
|
-
const parsed = storedTokensSchema.parse(tokens);
|
|
2919
|
-
const expiresAt = parsed.refreshTokenExpiresAt ?? parsed.expiresAt;
|
|
2920
|
-
const ttlMs2 = expiresAt ? Math.max(expiresAt - Date.now() + BUFFER_MS, BUFFER_MS) : LONG_LIVED_TTL_MS;
|
|
2921
|
-
await this.state.set(tokenKey(userId, provider), parsed, ttlMs2);
|
|
2922
|
-
}
|
|
2923
|
-
async delete(userId, provider) {
|
|
2924
|
-
await this.state.delete(tokenKey(userId, provider));
|
|
2925
|
-
}
|
|
2926
|
-
/** Wait for the per-slot refresh gate so rotated refresh tokens are used once. */
|
|
2927
|
-
async withRefresh(userId, provider, callback) {
|
|
2928
|
-
const lockKey = refreshLockKey(userId, provider);
|
|
2929
|
-
const deadline = Date.now() + REFRESH_LOCK_WAIT_MS;
|
|
2930
|
-
while (true) {
|
|
2931
|
-
const lock = await acquireActiveLock(this.state, lockKey);
|
|
2932
|
-
if (lock) {
|
|
2933
|
-
try {
|
|
2934
|
-
return await callback();
|
|
2935
|
-
} finally {
|
|
2936
|
-
await this.state.releaseLock(lock);
|
|
2937
|
-
}
|
|
2938
|
-
}
|
|
2939
|
-
if (Date.now() >= deadline) {
|
|
2940
|
-
throw new Error(`Could not acquire OAuth token refresh lock`);
|
|
2941
|
-
}
|
|
2942
|
-
await sleep(REFRESH_LOCK_RETRY_MS);
|
|
2943
|
-
}
|
|
2944
|
-
}
|
|
2945
|
-
};
|
|
2946
|
-
|
|
2947
|
-
// src/chat/capabilities/factory.ts
|
|
2948
|
-
var sandboxEgressRouters = /* @__PURE__ */ new WeakMap();
|
|
2949
|
-
function createUserTokenStore() {
|
|
2950
|
-
return new StateAdapterTokenStore(getStateAdapter());
|
|
2951
|
-
}
|
|
2952
|
-
function createProviderCredentialRouter(userTokenStore) {
|
|
2953
|
-
logCapabilityCatalogLoadedOnce();
|
|
2954
|
-
const brokersByProvider = {};
|
|
2955
|
-
for (const plugin of pluginCatalogRuntime.getProviders()) {
|
|
2956
|
-
const { name } = plugin.manifest;
|
|
2957
|
-
if (!plugin.manifest.credentials && !plugin.manifest.apiHeaders) {
|
|
2958
|
-
continue;
|
|
2959
|
-
}
|
|
2960
|
-
brokersByProvider[name] = pluginCatalogRuntime.createBroker(name, {
|
|
2961
|
-
userTokenStore
|
|
2962
|
-
});
|
|
2963
|
-
}
|
|
2964
|
-
return new ProviderCredentialRouter({ brokersByProvider });
|
|
2965
|
-
}
|
|
2966
|
-
function getSandboxEgressRouter() {
|
|
2967
|
-
const stateAdapter = getStateAdapter();
|
|
2968
|
-
let router = sandboxEgressRouters.get(stateAdapter);
|
|
2969
|
-
if (!router) {
|
|
2970
|
-
router = createProviderCredentialRouter(
|
|
2971
|
-
new StateAdapterTokenStore(stateAdapter)
|
|
2972
|
-
);
|
|
2973
|
-
sandboxEgressRouters.set(stateAdapter, router);
|
|
2974
|
-
}
|
|
2975
|
-
return router;
|
|
2976
|
-
}
|
|
2977
|
-
async function issueProviderCredentialLease(input) {
|
|
2978
|
-
return await getSandboxEgressRouter().issue(input);
|
|
2979
|
-
}
|
|
2980
|
-
|
|
2981
2373
|
// src/chat/capabilities/jr-rpc-command.ts
|
|
2982
2374
|
import { Bash, defineCommand } from "just-bash";
|
|
2983
2375
|
function commandResult(input) {
|
|
@@ -3248,631 +2640,96 @@ function createJrRpcCommand(deps) {
|
|
|
3248
2640
|
"jr-rpc config get <key>",
|
|
3249
2641
|
"jr-rpc config set <key> <value> [--json]",
|
|
3250
2642
|
"jr-rpc config unset <key>",
|
|
3251
|
-
"jr-rpc config list [--prefix <value>]",
|
|
3252
|
-
"jr-rpc plugins list"
|
|
3253
|
-
].join("\n");
|
|
3254
|
-
const verb = (args[0] ?? "").trim();
|
|
3255
|
-
if (verb === "config") {
|
|
3256
|
-
return handleConfigCommand(args.slice(1), deps);
|
|
3257
|
-
}
|
|
3258
|
-
if (verb === "plugins") {
|
|
3259
|
-
return handlePluginsCommand(args.slice(1));
|
|
3260
|
-
}
|
|
3261
|
-
return commandResult({
|
|
3262
|
-
stderr: `Unsupported jr-rpc command. Use:
|
|
3263
|
-
${usage}
|
|
3264
|
-
`,
|
|
3265
|
-
exitCode: 2
|
|
3266
|
-
});
|
|
3267
|
-
});
|
|
3268
|
-
}
|
|
3269
|
-
async function maybeExecuteJrRpcCustomCommand(command, deps) {
|
|
3270
|
-
const normalized = command.trim();
|
|
3271
|
-
if (!/^jr-rpc(?:\s|$)/.test(normalized)) {
|
|
3272
|
-
return { handled: false };
|
|
3273
|
-
}
|
|
3274
|
-
const shell = new Bash({
|
|
3275
|
-
customCommands: [createJrRpcCommand(deps)]
|
|
3276
|
-
});
|
|
3277
|
-
const execResult = await shell.exec(normalized);
|
|
3278
|
-
return {
|
|
3279
|
-
handled: true,
|
|
3280
|
-
result: {
|
|
3281
|
-
ok: execResult.exitCode === 0,
|
|
3282
|
-
command: normalized,
|
|
3283
|
-
cwd: "/",
|
|
3284
|
-
exit_code: execResult.exitCode,
|
|
3285
|
-
signal: null,
|
|
3286
|
-
timed_out: false,
|
|
3287
|
-
stdout: execResult.stdout,
|
|
3288
|
-
stderr: execResult.stderr,
|
|
3289
|
-
stdout_truncated: false,
|
|
3290
|
-
stderr_truncated: false
|
|
3291
|
-
}
|
|
3292
|
-
};
|
|
3293
|
-
}
|
|
3294
|
-
|
|
3295
|
-
// src/chat/sandbox/command-result.ts
|
|
3296
|
-
function formatSandboxCommandResult(params) {
|
|
3297
|
-
return makeStructuredToolResult({
|
|
3298
|
-
ok: params.ok,
|
|
3299
|
-
status: params.ok ? "success" : "error",
|
|
3300
|
-
target: params.command,
|
|
3301
|
-
data: {
|
|
3302
|
-
command: params.command,
|
|
3303
|
-
cwd: params.cwd,
|
|
3304
|
-
exit_code: params.exit_code,
|
|
3305
|
-
signal: params.signal,
|
|
3306
|
-
timed_out: params.timed_out,
|
|
3307
|
-
aborted: Boolean(params.aborted),
|
|
3308
|
-
stdout: params.stdout,
|
|
3309
|
-
stderr: params.stderr,
|
|
3310
|
-
stdout_truncated: params.stdout_truncated,
|
|
3311
|
-
stderr_truncated: params.stderr_truncated
|
|
3312
|
-
},
|
|
3313
|
-
truncated: params.stdout_truncated || params.stderr_truncated,
|
|
3314
|
-
...!params.ok ? {
|
|
3315
|
-
error: {
|
|
3316
|
-
kind: params.aborted ? "outcome_unknown" : params.timed_out ? "timeout" : "nonzero_exit",
|
|
3317
|
-
message: params.aborted ? "Command was interrupted before its outcome was confirmed. It may have produced side effects; reconcile external state before retrying or reporting failure." : params.stderr.trim() || `Command exited with code ${params.exit_code}`,
|
|
3318
|
-
retryable: params.timed_out
|
|
3319
|
-
}
|
|
3320
|
-
} : {},
|
|
3321
|
-
command: params.command,
|
|
3322
|
-
cwd: params.cwd,
|
|
3323
|
-
exit_code: params.exit_code,
|
|
3324
|
-
signal: params.signal,
|
|
3325
|
-
timed_out: params.timed_out,
|
|
3326
|
-
aborted: Boolean(params.aborted),
|
|
3327
|
-
stdout: params.stdout,
|
|
3328
|
-
stderr: params.stderr,
|
|
3329
|
-
stdout_truncated: params.stdout_truncated,
|
|
3330
|
-
stderr_truncated: params.stderr_truncated,
|
|
3331
|
-
...params.auth_required ? { auth_required: params.auth_required } : {},
|
|
3332
|
-
...params.permission_denied ? { permission_denied: params.permission_denied } : {}
|
|
3333
|
-
});
|
|
3334
|
-
}
|
|
3335
|
-
|
|
3336
|
-
// src/chat/sandbox/sandbox.ts
|
|
3337
|
-
import fs4 from "fs/promises";
|
|
3338
|
-
|
|
3339
|
-
// src/chat/oauth-flow.ts
|
|
3340
|
-
import { randomBytes } from "crypto";
|
|
3341
|
-
import {
|
|
3342
|
-
sourceSchema
|
|
3343
|
-
} from "@sentry/junior-plugin-api";
|
|
3344
|
-
|
|
3345
|
-
// src/chat/slack/outbound.ts
|
|
3346
|
-
var MAX_SLACK_MESSAGE_TEXT_CHARS = 4e4;
|
|
3347
|
-
function requireSlackConversationId(channelId, action) {
|
|
3348
|
-
const normalized = normalizeSlackConversationId(channelId);
|
|
3349
|
-
if (!normalized) {
|
|
3350
|
-
throw new Error(`${action} requires a valid channel ID`);
|
|
3351
|
-
}
|
|
3352
|
-
return normalized;
|
|
3353
|
-
}
|
|
3354
|
-
function requireSlackThreadTimestamp(threadTs, action) {
|
|
3355
|
-
const normalized = threadTs.trim();
|
|
3356
|
-
if (!normalized) {
|
|
3357
|
-
throw new Error(`${action} requires a thread timestamp`);
|
|
3358
|
-
}
|
|
3359
|
-
return normalized;
|
|
3360
|
-
}
|
|
3361
|
-
function requireSlackMessageTimestamp(timestamp, action) {
|
|
3362
|
-
const normalized = parseSlackMessageTs(timestamp);
|
|
3363
|
-
if (!normalized) {
|
|
3364
|
-
throw new Error(`${action} requires a target message timestamp`);
|
|
3365
|
-
}
|
|
3366
|
-
return normalized;
|
|
3367
|
-
}
|
|
3368
|
-
function requireSlackMessageText(text, action) {
|
|
3369
|
-
if (text.trim().length === 0) {
|
|
3370
|
-
throw new Error(`${action} requires non-empty text`);
|
|
3371
|
-
}
|
|
3372
|
-
if (text.length > MAX_SLACK_MESSAGE_TEXT_CHARS) {
|
|
3373
|
-
throw new Error(
|
|
3374
|
-
`${action} text exceeds Slack's 40000 character truncation limit`
|
|
3375
|
-
);
|
|
3376
|
-
}
|
|
3377
|
-
return text;
|
|
3378
|
-
}
|
|
3379
|
-
async function getSlackMessagePermalink(args) {
|
|
3380
|
-
try {
|
|
3381
|
-
const response = await withSlackRetries(
|
|
3382
|
-
() => getSlackClient().chat.getPermalink({
|
|
3383
|
-
channel: args.channelId,
|
|
3384
|
-
message_ts: args.messageTs
|
|
3385
|
-
}),
|
|
3386
|
-
3,
|
|
3387
|
-
{
|
|
3388
|
-
action: "chat.getPermalink",
|
|
3389
|
-
idempotent: true,
|
|
3390
|
-
spanAttributes: {
|
|
3391
|
-
"app.slack.channel_id": args.channelId,
|
|
3392
|
-
"app.slack.message_ts": args.messageTs
|
|
3393
|
-
}
|
|
3394
|
-
}
|
|
3395
|
-
);
|
|
3396
|
-
return response.permalink;
|
|
3397
|
-
} catch {
|
|
3398
|
-
return void 0;
|
|
3399
|
-
}
|
|
3400
|
-
}
|
|
3401
|
-
async function postSlackMessage(input) {
|
|
3402
|
-
const channelId = requireSlackConversationId(
|
|
3403
|
-
input.channelId,
|
|
3404
|
-
"Slack message posting"
|
|
3405
|
-
);
|
|
3406
|
-
const text = requireSlackMessageText(input.text, "Slack message posting");
|
|
3407
|
-
const threadTs = input.threadTs ? requireSlackThreadTimestamp(
|
|
3408
|
-
input.threadTs,
|
|
3409
|
-
"Slack thread message posting"
|
|
3410
|
-
) : void 0;
|
|
3411
|
-
const response = await withSlackRetries(
|
|
3412
|
-
() => getSlackClient().chat.postMessage({
|
|
3413
|
-
channel: channelId,
|
|
3414
|
-
text,
|
|
3415
|
-
...input.blocks?.length ? {
|
|
3416
|
-
blocks: input.blocks
|
|
3417
|
-
} : {},
|
|
3418
|
-
...threadTs ? { thread_ts: threadTs } : {}
|
|
3419
|
-
}),
|
|
3420
|
-
3,
|
|
3421
|
-
{
|
|
3422
|
-
action: "chat.postMessage",
|
|
3423
|
-
spanAttributes: {
|
|
3424
|
-
"app.slack.channel_id": channelId,
|
|
3425
|
-
...threadTs ? { "app.slack.thread_ts": threadTs } : {}
|
|
3426
|
-
}
|
|
3427
|
-
}
|
|
3428
|
-
);
|
|
3429
|
-
const messageTs = parseSlackMessageTs(response.ts);
|
|
3430
|
-
if (!messageTs) {
|
|
3431
|
-
throw new Error("Slack message posted without ts");
|
|
3432
|
-
}
|
|
3433
|
-
return {
|
|
3434
|
-
ts: messageTs,
|
|
3435
|
-
...input.includePermalink ? {
|
|
3436
|
-
permalink: await getSlackMessagePermalink({
|
|
3437
|
-
channelId,
|
|
3438
|
-
messageTs
|
|
3439
|
-
})
|
|
3440
|
-
} : {}
|
|
3441
|
-
};
|
|
3442
|
-
}
|
|
3443
|
-
async function postSlackEphemeralMessage(input) {
|
|
3444
|
-
const channelId = requireSlackConversationId(
|
|
3445
|
-
input.channelId,
|
|
3446
|
-
"Slack ephemeral message posting"
|
|
3447
|
-
);
|
|
3448
|
-
const userId = parseSlackUserId(input.userId);
|
|
3449
|
-
if (!userId) {
|
|
3450
|
-
throw new Error("Slack ephemeral message posting requires a user ID");
|
|
3451
|
-
}
|
|
3452
|
-
const text = requireSlackMessageText(
|
|
3453
|
-
input.text,
|
|
3454
|
-
"Slack ephemeral message posting"
|
|
3455
|
-
);
|
|
3456
|
-
const threadTs = input.threadTs ? requireSlackThreadTimestamp(
|
|
3457
|
-
input.threadTs,
|
|
3458
|
-
"Slack ephemeral thread message posting"
|
|
3459
|
-
) : void 0;
|
|
3460
|
-
const response = await withSlackRetries(
|
|
3461
|
-
() => getSlackClient().chat.postEphemeral({
|
|
3462
|
-
channel: channelId,
|
|
3463
|
-
user: userId,
|
|
3464
|
-
text,
|
|
3465
|
-
...threadTs ? { thread_ts: threadTs } : {}
|
|
3466
|
-
}),
|
|
3467
|
-
3,
|
|
3468
|
-
{
|
|
3469
|
-
action: "chat.postEphemeral",
|
|
3470
|
-
spanAttributes: {
|
|
3471
|
-
"app.slack.channel_id": channelId,
|
|
3472
|
-
"app.slack.user_id": userId,
|
|
3473
|
-
...threadTs ? { "app.slack.thread_ts": threadTs } : {}
|
|
3474
|
-
}
|
|
3475
|
-
}
|
|
3476
|
-
);
|
|
3477
|
-
return {
|
|
3478
|
-
messageTs: response.message_ts
|
|
3479
|
-
};
|
|
3480
|
-
}
|
|
3481
|
-
async function uploadFilesToConversation(input) {
|
|
3482
|
-
const channelId = requireSlackConversationId(
|
|
3483
|
-
input.channelId,
|
|
3484
|
-
"Slack file upload"
|
|
3485
|
-
);
|
|
3486
|
-
const threadTs = input.threadTs ? requireSlackThreadTimestamp(input.threadTs, "Slack file upload") : void 0;
|
|
3487
|
-
if (input.files.length === 0) {
|
|
3488
|
-
throw new Error("Slack file upload requires at least one file");
|
|
3489
|
-
}
|
|
3490
|
-
const fileUploads = input.files.map((file) => {
|
|
3491
|
-
const filename = file.filename.trim();
|
|
3492
|
-
if (!filename) {
|
|
3493
|
-
throw new Error(
|
|
3494
|
-
"Slack file upload requires every file to have a filename"
|
|
3495
|
-
);
|
|
3496
|
-
}
|
|
3497
|
-
return {
|
|
3498
|
-
file: file.data,
|
|
3499
|
-
filename
|
|
3500
|
-
};
|
|
3501
|
-
});
|
|
3502
|
-
const response = await withSlackRetries(
|
|
3503
|
-
() => getSlackClient().filesUploadV2({
|
|
3504
|
-
channel_id: channelId,
|
|
3505
|
-
...threadTs ? { thread_ts: threadTs } : {},
|
|
3506
|
-
file_uploads: fileUploads
|
|
3507
|
-
}),
|
|
3508
|
-
3,
|
|
3509
|
-
{
|
|
3510
|
-
action: "filesUploadV2",
|
|
3511
|
-
spanAttributes: {
|
|
3512
|
-
"app.slack.channel_id": channelId,
|
|
3513
|
-
...threadTs ? { "app.slack.thread_ts": threadTs } : {}
|
|
3514
|
-
}
|
|
3515
|
-
}
|
|
3516
|
-
);
|
|
3517
|
-
return {
|
|
3518
|
-
files: (response.files ?? []).flatMap(
|
|
3519
|
-
(completion) => (completion.files ?? []).map((file) => file.id ? { id: file.id } : {})
|
|
3520
|
-
)
|
|
3521
|
-
};
|
|
3522
|
-
}
|
|
3523
|
-
async function addReactionToMessage(input) {
|
|
3524
|
-
const channelId = requireSlackConversationId(
|
|
3525
|
-
input.channelId,
|
|
3526
|
-
"Slack reaction"
|
|
3527
|
-
);
|
|
3528
|
-
const timestamp = requireSlackMessageTimestamp(
|
|
3529
|
-
input.timestamp,
|
|
3530
|
-
"Slack reaction"
|
|
3531
|
-
);
|
|
3532
|
-
const emoji = normalizeSlackEmojiName(input.emoji);
|
|
3533
|
-
if (!emoji) {
|
|
3534
|
-
throw new Error("Slack reaction requires a valid emoji alias name");
|
|
3535
|
-
}
|
|
3536
|
-
try {
|
|
3537
|
-
await withSlackRetries(
|
|
3538
|
-
() => getSlackClient().reactions.add({
|
|
3539
|
-
channel: channelId,
|
|
3540
|
-
timestamp,
|
|
3541
|
-
name: emoji
|
|
3542
|
-
}),
|
|
3543
|
-
3,
|
|
3544
|
-
{
|
|
3545
|
-
action: "reactions.add",
|
|
3546
|
-
idempotent: true,
|
|
3547
|
-
spanAttributes: {
|
|
3548
|
-
"app.slack.channel_id": channelId,
|
|
3549
|
-
"app.slack.message_ts": timestamp,
|
|
3550
|
-
"app.slack.reaction": emoji
|
|
3551
|
-
}
|
|
3552
|
-
}
|
|
3553
|
-
);
|
|
3554
|
-
} catch (error) {
|
|
3555
|
-
if (error instanceof SlackActionError && error.code === "already_reacted") {
|
|
3556
|
-
return { ok: true };
|
|
3557
|
-
}
|
|
3558
|
-
throw error;
|
|
3559
|
-
}
|
|
3560
|
-
return { ok: true };
|
|
3561
|
-
}
|
|
3562
|
-
async function removeReactionFromMessage(input) {
|
|
3563
|
-
const channelId = requireSlackConversationId(
|
|
3564
|
-
input.channelId,
|
|
3565
|
-
"Slack reaction removal"
|
|
3566
|
-
);
|
|
3567
|
-
const timestamp = requireSlackMessageTimestamp(
|
|
3568
|
-
input.timestamp,
|
|
3569
|
-
"Slack reaction removal"
|
|
3570
|
-
);
|
|
3571
|
-
const emoji = normalizeSlackEmojiName(input.emoji);
|
|
3572
|
-
if (!emoji) {
|
|
3573
|
-
throw new Error("Slack reaction removal requires a valid emoji alias name");
|
|
3574
|
-
}
|
|
3575
|
-
try {
|
|
3576
|
-
await withSlackRetries(
|
|
3577
|
-
() => getSlackClient().reactions.remove({
|
|
3578
|
-
channel: channelId,
|
|
3579
|
-
timestamp,
|
|
3580
|
-
name: emoji
|
|
3581
|
-
}),
|
|
3582
|
-
3,
|
|
3583
|
-
{
|
|
3584
|
-
action: "reactions.remove",
|
|
3585
|
-
idempotent: true,
|
|
3586
|
-
spanAttributes: {
|
|
3587
|
-
"app.slack.channel_id": channelId,
|
|
3588
|
-
"app.slack.message_ts": timestamp,
|
|
3589
|
-
"app.slack.reaction": emoji
|
|
3590
|
-
}
|
|
3591
|
-
}
|
|
3592
|
-
);
|
|
3593
|
-
} catch (error) {
|
|
3594
|
-
if (error instanceof SlackActionError && error.code === "no_reaction") {
|
|
3595
|
-
return { ok: true };
|
|
3596
|
-
}
|
|
3597
|
-
throw error;
|
|
3598
|
-
}
|
|
3599
|
-
return { ok: true };
|
|
3600
|
-
}
|
|
3601
|
-
|
|
3602
|
-
// src/chat/oauth-authorization-message.ts
|
|
3603
|
-
function formatOAuthAuthorizationMessage(args) {
|
|
3604
|
-
return `${formatSlackLink(args.authorizationUrl, args.label)}. ${args.completionText}`;
|
|
3605
|
-
}
|
|
3606
|
-
|
|
3607
|
-
// src/chat/oauth-flow.ts
|
|
3608
|
-
var OAUTH_STATE_TTL_MS = 10 * 60 * 1e3;
|
|
3609
|
-
function optionalString(value) {
|
|
3610
|
-
return typeof value === "string" ? value : void 0;
|
|
3611
|
-
}
|
|
3612
|
-
function parseOAuthStatePayload(value) {
|
|
3613
|
-
if (!isRecord(value)) {
|
|
3614
|
-
return void 0;
|
|
3615
|
-
}
|
|
3616
|
-
if (typeof value.userId !== "string" || typeof value.provider !== "string") {
|
|
3617
|
-
return void 0;
|
|
3618
|
-
}
|
|
3619
|
-
const destination = parseDestination(value.destination);
|
|
3620
|
-
if (value.destination !== void 0 && !destination) {
|
|
3621
|
-
return void 0;
|
|
3622
|
-
}
|
|
3623
|
-
const source = value.source === void 0 ? void 0 : sourceSchema.safeParse(value.source);
|
|
3624
|
-
if (value.source !== void 0 && (!source || !source.success)) {
|
|
3625
|
-
return void 0;
|
|
3626
|
-
}
|
|
3627
|
-
return {
|
|
3628
|
-
userId: value.userId,
|
|
3629
|
-
provider: value.provider,
|
|
3630
|
-
...optionalString(value.channelId) ? { channelId: optionalString(value.channelId) } : {},
|
|
3631
|
-
...destination ? { destination } : {},
|
|
3632
|
-
...source?.success ? { source: source.data } : {},
|
|
3633
|
-
...optionalString(value.threadTs) ? { threadTs: optionalString(value.threadTs) } : {},
|
|
3634
|
-
...optionalString(value.resumeConversationId) ? { resumeConversationId: optionalString(value.resumeConversationId) } : {},
|
|
3635
|
-
...optionalString(value.resumeSessionId) ? { resumeSessionId: optionalString(value.resumeSessionId) } : {},
|
|
3636
|
-
...optionalString(value.scope) ? { scope: optionalString(value.scope) } : {}
|
|
3637
|
-
};
|
|
3638
|
-
}
|
|
3639
|
-
function formatProviderLabel(provider) {
|
|
3640
|
-
const displayName = pluginCatalogRuntime.getDisplayName(provider);
|
|
3641
|
-
if (!displayName) {
|
|
3642
|
-
throw new Error(`Unknown plugin provider display name: "${provider}"`);
|
|
3643
|
-
}
|
|
3644
|
-
return displayName;
|
|
3645
|
-
}
|
|
3646
|
-
function resolveBaseUrl() {
|
|
3647
|
-
const explicit = process.env.JUNIOR_BASE_URL?.trim();
|
|
3648
|
-
if (explicit) return explicit;
|
|
3649
|
-
const vercelProd = process.env.VERCEL_PROJECT_PRODUCTION_URL?.trim();
|
|
3650
|
-
if (vercelProd) return `https://${vercelProd}`;
|
|
3651
|
-
const vercelUrl = process.env.VERCEL_URL?.trim();
|
|
3652
|
-
if (vercelUrl) return `https://${vercelUrl}`;
|
|
3653
|
-
return void 0;
|
|
3654
|
-
}
|
|
3655
|
-
async function deliverPrivateMessage(input) {
|
|
3656
|
-
let client;
|
|
3657
|
-
try {
|
|
3658
|
-
client = getSlackClient();
|
|
3659
|
-
} catch {
|
|
3660
|
-
logWarn(
|
|
3661
|
-
"oauth_private_delivery_skip",
|
|
3662
|
-
{},
|
|
3663
|
-
{ "app.reason": "missing_bot_token" },
|
|
3664
|
-
"Skipped private message delivery \u2014 no SLACK_BOT_TOKEN"
|
|
3665
|
-
);
|
|
3666
|
-
return false;
|
|
3667
|
-
}
|
|
3668
|
-
if (input.channelId) {
|
|
3669
|
-
try {
|
|
3670
|
-
if (isDmChannel(input.channelId)) {
|
|
3671
|
-
await postSlackMessage({
|
|
3672
|
-
channelId: input.channelId,
|
|
3673
|
-
text: input.text,
|
|
3674
|
-
threadTs: input.threadTs
|
|
3675
|
-
});
|
|
3676
|
-
} else {
|
|
3677
|
-
await postSlackEphemeralMessage({
|
|
3678
|
-
channelId: input.channelId,
|
|
3679
|
-
userId: input.userId,
|
|
3680
|
-
text: input.text,
|
|
3681
|
-
threadTs: input.threadTs
|
|
3682
|
-
});
|
|
3683
|
-
}
|
|
3684
|
-
return "in_context";
|
|
3685
|
-
} catch (error) {
|
|
3686
|
-
logWarn(
|
|
3687
|
-
"oauth_private_delivery_failed",
|
|
3688
|
-
{},
|
|
3689
|
-
{
|
|
3690
|
-
"app.slack.error": error instanceof Error ? error.message : String(error),
|
|
3691
|
-
"app.slack.channel": input.channelId
|
|
3692
|
-
},
|
|
3693
|
-
"Private message delivery failed, falling back to DM"
|
|
3694
|
-
);
|
|
3695
|
-
}
|
|
3696
|
-
}
|
|
3697
|
-
try {
|
|
3698
|
-
const dmChannelId = (await withSlackRetries(
|
|
3699
|
-
() => client.conversations.open({ users: input.userId }),
|
|
3700
|
-
3,
|
|
3701
|
-
{
|
|
3702
|
-
action: "conversations.open",
|
|
3703
|
-
spanAttributes: { "app.slack.user_id": input.userId }
|
|
3704
|
-
}
|
|
3705
|
-
)).channel?.id;
|
|
3706
|
-
if (!dmChannelId) {
|
|
3707
|
-
logWarn(
|
|
3708
|
-
"oauth_dm_fallback_failed",
|
|
3709
|
-
{},
|
|
3710
|
-
{ "app.reason": "no_dm_channel_id" },
|
|
3711
|
-
"conversations.open returned no channel ID"
|
|
3712
|
-
);
|
|
3713
|
-
return false;
|
|
3714
|
-
}
|
|
3715
|
-
await postSlackMessage({ channelId: dmChannelId, text: input.text });
|
|
3716
|
-
return "fallback_dm";
|
|
3717
|
-
} catch (error) {
|
|
3718
|
-
logWarn(
|
|
3719
|
-
"oauth_dm_fallback_failed",
|
|
3720
|
-
{},
|
|
3721
|
-
{
|
|
3722
|
-
"app.slack.error": error instanceof Error ? error.message : String(error)
|
|
3723
|
-
},
|
|
3724
|
-
"DM fallback delivery failed"
|
|
3725
|
-
);
|
|
3726
|
-
return false;
|
|
3727
|
-
}
|
|
3728
|
-
}
|
|
3729
|
-
async function startOAuthFlow(provider, input) {
|
|
3730
|
-
const providerConfig = pluginCatalogRuntime.getOAuthConfig(provider);
|
|
3731
|
-
if (!providerConfig) {
|
|
3732
|
-
return {
|
|
3733
|
-
ok: false,
|
|
3734
|
-
error: `Provider "${provider}" does not support OAuth authorization`
|
|
3735
|
-
};
|
|
3736
|
-
}
|
|
3737
|
-
const clientId = process.env[providerConfig.clientIdEnv]?.trim();
|
|
3738
|
-
if (!clientId) {
|
|
3739
|
-
return {
|
|
3740
|
-
ok: false,
|
|
3741
|
-
error: `Missing ${providerConfig.clientIdEnv} environment variable`
|
|
3742
|
-
};
|
|
3743
|
-
}
|
|
3744
|
-
const baseUrl = resolveBaseUrl();
|
|
3745
|
-
if (!baseUrl) {
|
|
3746
|
-
return {
|
|
3747
|
-
ok: false,
|
|
3748
|
-
error: "Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)"
|
|
3749
|
-
};
|
|
3750
|
-
}
|
|
3751
|
-
const state = randomBytes(32).toString("hex");
|
|
3752
|
-
const requestedScope = input.scope ?? providerConfig.scope;
|
|
3753
|
-
await getStateAdapter().set(
|
|
3754
|
-
`oauth-state:${state}`,
|
|
3755
|
-
{
|
|
3756
|
-
userId: input.actorId,
|
|
3757
|
-
provider,
|
|
3758
|
-
...input.channelId ? { channelId: input.channelId } : {},
|
|
3759
|
-
...input.destination ? { destination: input.destination } : {},
|
|
3760
|
-
...input.source ? { source: input.source } : {},
|
|
3761
|
-
...input.threadTs ? { threadTs: input.threadTs } : {},
|
|
3762
|
-
...input.resumeConversationId ? { resumeConversationId: input.resumeConversationId } : {},
|
|
3763
|
-
...input.resumeSessionId ? { resumeSessionId: input.resumeSessionId } : {},
|
|
3764
|
-
...requestedScope ? { scope: requestedScope } : {}
|
|
3765
|
-
},
|
|
3766
|
-
OAUTH_STATE_TTL_MS
|
|
3767
|
-
);
|
|
3768
|
-
const authorizeParams = new URLSearchParams({
|
|
3769
|
-
client_id: clientId,
|
|
3770
|
-
state,
|
|
3771
|
-
redirect_uri: `${baseUrl}${providerConfig.callbackPath}`,
|
|
3772
|
-
response_type: "code"
|
|
2643
|
+
"jr-rpc config list [--prefix <value>]",
|
|
2644
|
+
"jr-rpc plugins list"
|
|
2645
|
+
].join("\n");
|
|
2646
|
+
const verb = (args[0] ?? "").trim();
|
|
2647
|
+
if (verb === "config") {
|
|
2648
|
+
return handleConfigCommand(args.slice(1), deps);
|
|
2649
|
+
}
|
|
2650
|
+
if (verb === "plugins") {
|
|
2651
|
+
return handlePluginsCommand(args.slice(1));
|
|
2652
|
+
}
|
|
2653
|
+
return commandResult({
|
|
2654
|
+
stderr: `Unsupported jr-rpc command. Use:
|
|
2655
|
+
${usage}
|
|
2656
|
+
`,
|
|
2657
|
+
exitCode: 2
|
|
2658
|
+
});
|
|
3773
2659
|
});
|
|
3774
|
-
|
|
3775
|
-
|
|
3776
|
-
|
|
3777
|
-
|
|
3778
|
-
|
|
3779
|
-
)) {
|
|
3780
|
-
authorizeParams.set(key, value);
|
|
2660
|
+
}
|
|
2661
|
+
async function maybeExecuteJrRpcCustomCommand(command, deps) {
|
|
2662
|
+
const normalized = command.trim();
|
|
2663
|
+
if (!/^jr-rpc(?:\s|$)/.test(normalized)) {
|
|
2664
|
+
return { handled: false };
|
|
3781
2665
|
}
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
"app.credential.provider": provider,
|
|
3787
|
-
...input.activeSkillName ? { "app.skill.name": input.activeSkillName } : {}
|
|
3788
|
-
},
|
|
3789
|
-
"Initiated OAuth authorization code flow"
|
|
3790
|
-
);
|
|
3791
|
-
const authorizationUrl = `${providerConfig.authorizeEndpoint}?${authorizeParams.toString()}`;
|
|
2666
|
+
const shell = new Bash({
|
|
2667
|
+
customCommands: [createJrRpcCommand(deps)]
|
|
2668
|
+
});
|
|
2669
|
+
const execResult = await shell.exec(normalized);
|
|
3792
2670
|
return {
|
|
3793
|
-
|
|
3794
|
-
|
|
3795
|
-
|
|
3796
|
-
|
|
3797
|
-
|
|
3798
|
-
|
|
3799
|
-
|
|
3800
|
-
|
|
3801
|
-
|
|
3802
|
-
|
|
3803
|
-
|
|
2671
|
+
handled: true,
|
|
2672
|
+
result: {
|
|
2673
|
+
ok: execResult.exitCode === 0,
|
|
2674
|
+
command: normalized,
|
|
2675
|
+
cwd: "/",
|
|
2676
|
+
exit_code: execResult.exitCode,
|
|
2677
|
+
signal: null,
|
|
2678
|
+
timed_out: false,
|
|
2679
|
+
stdout: execResult.stdout,
|
|
2680
|
+
stderr: execResult.stderr,
|
|
2681
|
+
stdout_truncated: false,
|
|
2682
|
+
stderr_truncated: false
|
|
2683
|
+
}
|
|
3804
2684
|
};
|
|
3805
2685
|
}
|
|
3806
2686
|
|
|
3807
|
-
// src/chat/sandbox/
|
|
3808
|
-
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
|
|
3813
|
-
|
|
3814
|
-
|
|
3815
|
-
|
|
3816
|
-
|
|
3817
|
-
|
|
3818
|
-
|
|
3819
|
-
|
|
3820
|
-
|
|
3821
|
-
|
|
3822
|
-
|
|
3823
|
-
|
|
3824
|
-
|
|
3825
|
-
|
|
3826
|
-
|
|
3827
|
-
|
|
3828
|
-
|
|
3829
|
-
|
|
3830
|
-
|
|
3831
|
-
|
|
3832
|
-
|
|
3833
|
-
|
|
3834
|
-
|
|
3835
|
-
|
|
3836
|
-
|
|
3837
|
-
|
|
3838
|
-
|
|
3839
|
-
|
|
3840
|
-
|
|
3841
|
-
|
|
3842
|
-
|
|
3843
|
-
|
|
3844
|
-
|
|
3845
|
-
|
|
3846
|
-
ctx.addIssue({
|
|
3847
|
-
code: z3.ZodIssueCode.custom,
|
|
3848
|
-
message: "Auth signal authorization provider must match provider",
|
|
3849
|
-
path: ["authorization", "provider"]
|
|
3850
|
-
});
|
|
3851
|
-
}
|
|
3852
|
-
});
|
|
3853
|
-
var sandboxEgressPermissionDeniedSignalSchema = z3.object({
|
|
3854
|
-
account: pluginProviderAccountSchema.optional(),
|
|
3855
|
-
acceptedPermissions: z3.string().optional(),
|
|
3856
|
-
grant: sandboxEgressGrantSchema,
|
|
3857
|
-
message: z3.string().min(1),
|
|
3858
|
-
provider: providerNameSchema,
|
|
3859
|
-
source: z3.literal("upstream"),
|
|
3860
|
-
sso: z3.string().optional(),
|
|
3861
|
-
status: httpStatusSchema,
|
|
3862
|
-
upstreamHost: z3.string().min(1),
|
|
3863
|
-
upstreamPath: z3.string().min(1),
|
|
3864
|
-
createdAtMs: finiteNumberSchema
|
|
3865
|
-
}).strict();
|
|
3866
|
-
function parseSandboxEgressAuthRequiredSignal(value) {
|
|
3867
|
-
const result = sandboxEgressAuthRequiredSignalSchema.safeParse(value);
|
|
3868
|
-
return result.success ? result.data : void 0;
|
|
3869
|
-
}
|
|
3870
|
-
function parseSandboxEgressPermissionDeniedSignal(value) {
|
|
3871
|
-
const result = sandboxEgressPermissionDeniedSignalSchema.safeParse(value);
|
|
3872
|
-
return result.success ? result.data : void 0;
|
|
2687
|
+
// src/chat/sandbox/command-result.ts
|
|
2688
|
+
function formatSandboxCommandResult(params) {
|
|
2689
|
+
return makeStructuredToolResult({
|
|
2690
|
+
ok: params.ok,
|
|
2691
|
+
status: params.ok ? "success" : "error",
|
|
2692
|
+
target: params.command,
|
|
2693
|
+
data: {
|
|
2694
|
+
command: params.command,
|
|
2695
|
+
cwd: params.cwd,
|
|
2696
|
+
exit_code: params.exit_code,
|
|
2697
|
+
signal: params.signal,
|
|
2698
|
+
timed_out: params.timed_out,
|
|
2699
|
+
aborted: Boolean(params.aborted),
|
|
2700
|
+
stdout: params.stdout,
|
|
2701
|
+
stderr: params.stderr,
|
|
2702
|
+
stdout_truncated: params.stdout_truncated,
|
|
2703
|
+
stderr_truncated: params.stderr_truncated
|
|
2704
|
+
},
|
|
2705
|
+
truncated: params.stdout_truncated || params.stderr_truncated,
|
|
2706
|
+
...!params.ok ? {
|
|
2707
|
+
error: {
|
|
2708
|
+
kind: params.aborted ? "outcome_unknown" : params.timed_out ? "timeout" : "nonzero_exit",
|
|
2709
|
+
message: params.aborted ? "Command was interrupted before its outcome was confirmed. It may have produced side effects; reconcile external state before retrying or reporting failure." : params.stderr.trim() || `Command exited with code ${params.exit_code}`,
|
|
2710
|
+
retryable: params.timed_out
|
|
2711
|
+
}
|
|
2712
|
+
} : {},
|
|
2713
|
+
command: params.command,
|
|
2714
|
+
cwd: params.cwd,
|
|
2715
|
+
exit_code: params.exit_code,
|
|
2716
|
+
signal: params.signal,
|
|
2717
|
+
timed_out: params.timed_out,
|
|
2718
|
+
aborted: Boolean(params.aborted),
|
|
2719
|
+
stdout: params.stdout,
|
|
2720
|
+
stderr: params.stderr,
|
|
2721
|
+
stdout_truncated: params.stdout_truncated,
|
|
2722
|
+
stderr_truncated: params.stderr_truncated,
|
|
2723
|
+
...params.auth_required ? { auth_required: params.auth_required } : {},
|
|
2724
|
+
...params.permission_denied ? { permission_denied: params.permission_denied } : {}
|
|
2725
|
+
});
|
|
3873
2726
|
}
|
|
3874
2727
|
|
|
2728
|
+
// src/chat/sandbox/sandbox.ts
|
|
2729
|
+
import fs4 from "fs/promises";
|
|
2730
|
+
|
|
3875
2731
|
// src/chat/sandbox/egress/session.ts
|
|
2732
|
+
import { createHmac, randomUUID, timingSafeEqual } from "crypto";
|
|
3876
2733
|
var SANDBOX_EGRESS_PROXY_PATH = "/api/internal/sandbox-egress";
|
|
3877
2734
|
var SANDBOX_EGRESS_TOKEN_VERSION = "v1";
|
|
3878
2735
|
var SANDBOX_EGRESS_HMAC_CONTEXT = "junior.sandbox_egress.v1";
|
|
@@ -5321,10 +4178,10 @@ function validateAndApplyTextEdits(content, edits, targetName) {
|
|
|
5321
4178
|
}
|
|
5322
4179
|
|
|
5323
4180
|
// src/chat/tools/sandbox/edit-file.ts
|
|
5324
|
-
import { z as
|
|
4181
|
+
import { z as z4 } from "zod";
|
|
5325
4182
|
|
|
5326
4183
|
// src/chat/tool-support/zod-tool.ts
|
|
5327
|
-
import { z as
|
|
4184
|
+
import { z as z3 } from "zod";
|
|
5328
4185
|
function isContentOnlyToolResult(value) {
|
|
5329
4186
|
return value !== null && typeof value === "object" && Array.isArray(value.content) && !("details" in value);
|
|
5330
4187
|
}
|
|
@@ -5348,7 +4205,7 @@ function zodTool(definition) {
|
|
|
5348
4205
|
const { inputSchema, outputSchema, prepareArguments, execute, ...toolDef } = definition;
|
|
5349
4206
|
let modelInputSchema;
|
|
5350
4207
|
try {
|
|
5351
|
-
modelInputSchema =
|
|
4208
|
+
modelInputSchema = z3.toJSONSchema(inputSchema);
|
|
5352
4209
|
} catch (error) {
|
|
5353
4210
|
throw new TypeError(
|
|
5354
4211
|
"zodTool() inputSchema must be representable as JSON Schema.",
|
|
@@ -5358,7 +4215,7 @@ function zodTool(definition) {
|
|
|
5358
4215
|
let modelOutputSchema;
|
|
5359
4216
|
if (outputSchema) {
|
|
5360
4217
|
try {
|
|
5361
|
-
modelOutputSchema =
|
|
4218
|
+
modelOutputSchema = z3.toJSONSchema(outputSchema);
|
|
5362
4219
|
} catch (error) {
|
|
5363
4220
|
throw new TypeError(
|
|
5364
4221
|
"zodTool() outputSchema must be representable as JSON Schema.",
|
|
@@ -5494,20 +4351,20 @@ async function editFile(params) {
|
|
|
5494
4351
|
truncated: diff.truncated
|
|
5495
4352
|
});
|
|
5496
4353
|
}
|
|
5497
|
-
var editReplacementSchema =
|
|
5498
|
-
oldText:
|
|
4354
|
+
var editReplacementSchema = z4.object({
|
|
4355
|
+
oldText: z4.string().min(1).describe(
|
|
5499
4356
|
"Exact text to replace. It must be unique in the original file and must not overlap another edit."
|
|
5500
4357
|
),
|
|
5501
|
-
newText:
|
|
4358
|
+
newText: z4.string().describe("Replacement text for this edit.")
|
|
5502
4359
|
});
|
|
5503
4360
|
function createEditFileTool() {
|
|
5504
4361
|
return zodTool({
|
|
5505
4362
|
description: "Edit one sandbox workspace file with exact text replacements. Use for precise changes to existing files; prefer this over writeFile for targeted changes. Each oldText must match exactly, be unique, and not overlap another edit. Returns a diff. Multiple changes to the same file: use one edits[] call.",
|
|
5506
4363
|
prepareArguments: prepareEditFileArguments,
|
|
5507
4364
|
executionMode: "sequential",
|
|
5508
|
-
inputSchema:
|
|
5509
|
-
path:
|
|
5510
|
-
edits:
|
|
4365
|
+
inputSchema: z4.object({
|
|
4366
|
+
path: z4.string().min(1).describe("Path to edit in the sandbox workspace."),
|
|
4367
|
+
edits: z4.array(editReplacementSchema).min(1).describe(
|
|
5511
4368
|
"Exact replacements matched against the original file, not incrementally."
|
|
5512
4369
|
)
|
|
5513
4370
|
}),
|
|
@@ -5522,7 +4379,7 @@ function createEditFileTool() {
|
|
|
5522
4379
|
|
|
5523
4380
|
// src/chat/tools/sandbox/find-files.ts
|
|
5524
4381
|
import path5 from "path";
|
|
5525
|
-
import { z as
|
|
4382
|
+
import { z as z5 } from "zod";
|
|
5526
4383
|
var DEFAULT_FIND_LIMIT = 1e3;
|
|
5527
4384
|
async function findFiles(params) {
|
|
5528
4385
|
if (!params.pattern.trim()) {
|
|
@@ -5582,14 +4439,14 @@ function createFindFilesTool() {
|
|
|
5582
4439
|
return zodTool({
|
|
5583
4440
|
description: "Find sandbox workspace files by glob pattern. Returns bounded paths relative to the search root and skips dependency/cache directories.",
|
|
5584
4441
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
5585
|
-
inputSchema:
|
|
5586
|
-
pattern:
|
|
4442
|
+
inputSchema: z5.object({
|
|
4443
|
+
pattern: z5.string().min(1).describe(
|
|
5587
4444
|
"Glob pattern to match, for example '*.ts', '**/*.json', or 'src/**/*.test.ts'."
|
|
5588
4445
|
),
|
|
5589
|
-
path:
|
|
4446
|
+
path: z5.string().min(1).describe(
|
|
5590
4447
|
"Directory or file path in the sandbox workspace. Defaults to the workspace root."
|
|
5591
4448
|
).optional(),
|
|
5592
|
-
limit:
|
|
4449
|
+
limit: z5.coerce.number().int().min(1).describe("Maximum number of file paths to return. Defaults to 1000.").optional()
|
|
5593
4450
|
}),
|
|
5594
4451
|
outputSchema: juniorToolResultSchema,
|
|
5595
4452
|
execute: async () => {
|
|
@@ -5602,12 +4459,12 @@ function createFindFilesTool() {
|
|
|
5602
4459
|
|
|
5603
4460
|
// src/chat/tools/sandbox/grep.ts
|
|
5604
4461
|
import path6 from "path";
|
|
5605
|
-
import { z as
|
|
4462
|
+
import { z as z6 } from "zod";
|
|
5606
4463
|
var DEFAULT_GREP_LIMIT = 100;
|
|
5607
4464
|
var MAX_GREP_LINE_CHARS = 500;
|
|
5608
|
-
var booleanInput = (description) =>
|
|
4465
|
+
var booleanInput = (description) => z6.preprocess(
|
|
5609
4466
|
(value) => value === "true" ? true : value === "false" ? false : value,
|
|
5610
|
-
|
|
4467
|
+
z6.boolean()
|
|
5611
4468
|
).describe(description);
|
|
5612
4469
|
function truncateGrepLine(value) {
|
|
5613
4470
|
if (value.length <= MAX_GREP_LINE_CHARS) {
|
|
@@ -5756,20 +4613,20 @@ function createGrepTool() {
|
|
|
5756
4613
|
return zodTool({
|
|
5757
4614
|
description: "Search sandbox workspace file contents. Returns bounded matching lines with file paths and line numbers. Respects path/glob filters and includes truncation notices.",
|
|
5758
4615
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
5759
|
-
inputSchema:
|
|
5760
|
-
pattern:
|
|
5761
|
-
path:
|
|
4616
|
+
inputSchema: z6.object({
|
|
4617
|
+
pattern: z6.string().min(1).describe("Regex pattern or literal text to search for."),
|
|
4618
|
+
path: z6.string().min(1).describe(
|
|
5762
4619
|
"Directory or file path in the sandbox workspace. Defaults to the workspace root."
|
|
5763
4620
|
).optional(),
|
|
5764
|
-
glob:
|
|
4621
|
+
glob: z6.string().min(1).describe("Optional glob filter such as '*.ts' or '**/*.test.ts'.").optional(),
|
|
5765
4622
|
ignoreCase: booleanInput(
|
|
5766
4623
|
"Whether matching should ignore case."
|
|
5767
4624
|
).optional(),
|
|
5768
4625
|
literal: booleanInput(
|
|
5769
4626
|
"Treat pattern as literal text instead of regex."
|
|
5770
4627
|
).optional(),
|
|
5771
|
-
context:
|
|
5772
|
-
limit:
|
|
4628
|
+
context: z6.coerce.number().int().min(0).describe("Number of surrounding context lines to include.").optional(),
|
|
4629
|
+
limit: z6.coerce.number().int().min(1).describe("Maximum matches to return. Defaults to 100.").optional()
|
|
5773
4630
|
}),
|
|
5774
4631
|
outputSchema: juniorToolResultSchema,
|
|
5775
4632
|
execute: async () => {
|
|
@@ -5780,7 +4637,7 @@ function createGrepTool() {
|
|
|
5780
4637
|
|
|
5781
4638
|
// src/chat/tools/sandbox/list-dir.ts
|
|
5782
4639
|
import path7 from "path";
|
|
5783
|
-
import { z as
|
|
4640
|
+
import { z as z7 } from "zod";
|
|
5784
4641
|
var DEFAULT_LIST_LIMIT = 500;
|
|
5785
4642
|
async function listDir(params) {
|
|
5786
4643
|
const dirPath = resolveWorkspacePath(params.path);
|
|
@@ -5861,11 +4718,11 @@ function createListDirTool() {
|
|
|
5861
4718
|
return zodTool({
|
|
5862
4719
|
description: "List a sandbox workspace directory. Returns sorted entries with '/' suffixes for directories and bounded truncation notices.",
|
|
5863
4720
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
5864
|
-
inputSchema:
|
|
5865
|
-
path:
|
|
4721
|
+
inputSchema: z7.object({
|
|
4722
|
+
path: z7.string().min(1).describe(
|
|
5866
4723
|
"Directory path in the sandbox workspace. Defaults to the workspace root."
|
|
5867
4724
|
).optional(),
|
|
5868
|
-
limit:
|
|
4725
|
+
limit: z7.coerce.number().int().min(1).describe("Maximum entries to return. Defaults to 500.").optional()
|
|
5869
4726
|
}),
|
|
5870
4727
|
outputSchema: juniorToolResultSchema,
|
|
5871
4728
|
execute: async () => {
|
|
@@ -5877,7 +4734,7 @@ function createListDirTool() {
|
|
|
5877
4734
|
}
|
|
5878
4735
|
|
|
5879
4736
|
// src/chat/tools/sandbox/read-file.ts
|
|
5880
|
-
import { z as
|
|
4737
|
+
import { z as z8 } from "zod";
|
|
5881
4738
|
|
|
5882
4739
|
// src/chat/tool-support/text-range-result.ts
|
|
5883
4740
|
var DEFAULT_READ_LIMIT = 1e3;
|
|
@@ -5952,10 +4809,10 @@ function createReadFileTool() {
|
|
|
5952
4809
|
return zodTool({
|
|
5953
4810
|
description: "Read a bounded line range from a file in the sandbox workspace. Use when you need exact file contents to verify facts or make edits safely. Prefer grep/findFiles/listDir for broad discovery.",
|
|
5954
4811
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
5955
|
-
inputSchema:
|
|
5956
|
-
path:
|
|
5957
|
-
offset:
|
|
5958
|
-
limit:
|
|
4812
|
+
inputSchema: z8.object({
|
|
4813
|
+
path: z8.string().min(1).describe("Path to the file in the sandbox workspace."),
|
|
4814
|
+
offset: z8.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
|
|
4815
|
+
limit: z8.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
|
|
5959
4816
|
}),
|
|
5960
4817
|
outputSchema: juniorToolResultSchema,
|
|
5961
4818
|
execute: async () => {
|
|
@@ -6020,6 +4877,30 @@ function createSandbox(options) {
|
|
|
6020
4877
|
});
|
|
6021
4878
|
return token;
|
|
6022
4879
|
};
|
|
4880
|
+
const clearEgressSignals = async (egressId) => {
|
|
4881
|
+
if (!options.egressSignals) {
|
|
4882
|
+
await clearSandboxEgressSignals(egressId);
|
|
4883
|
+
return;
|
|
4884
|
+
}
|
|
4885
|
+
await options.egressSignals.clear(
|
|
4886
|
+
sandboxEgressCredentialTokenFor(egressId)
|
|
4887
|
+
);
|
|
4888
|
+
};
|
|
4889
|
+
const consumeEgressSignals = async (egressId) => {
|
|
4890
|
+
if (options.egressSignals) {
|
|
4891
|
+
return await options.egressSignals.consume(
|
|
4892
|
+
sandboxEgressCredentialTokenFor(egressId)
|
|
4893
|
+
);
|
|
4894
|
+
}
|
|
4895
|
+
const [authRequired, permissionDenied] = await Promise.all([
|
|
4896
|
+
consumeSandboxEgressAuthRequiredSignal(egressId),
|
|
4897
|
+
consumeSandboxEgressPermissionDeniedSignal(egressId)
|
|
4898
|
+
]);
|
|
4899
|
+
return {
|
|
4900
|
+
...authRequired ? { authRequired } : {},
|
|
4901
|
+
...permissionDenied ? { permissionDenied } : {}
|
|
4902
|
+
};
|
|
4903
|
+
};
|
|
6023
4904
|
const runtime = createSandboxRuntime({
|
|
6024
4905
|
sandboxRef: options.sandboxRef,
|
|
6025
4906
|
skills: options.skills,
|
|
@@ -6063,7 +4944,7 @@ function createSandbox(options) {
|
|
|
6063
4944
|
"app.sandbox.command_length": command.length
|
|
6064
4945
|
});
|
|
6065
4946
|
const { bash: executeBash, sessionId: activeSessionId } = await runtime.tools();
|
|
6066
|
-
await
|
|
4947
|
+
await clearEgressSignals(activeSessionId);
|
|
6067
4948
|
const result = await withSandboxToolSpan(
|
|
6068
4949
|
"bash",
|
|
6069
4950
|
"process.exec",
|
|
@@ -6101,8 +4982,7 @@ function createSandbox(options) {
|
|
|
6101
4982
|
}
|
|
6102
4983
|
}
|
|
6103
4984
|
);
|
|
6104
|
-
const authRequired = await
|
|
6105
|
-
const permissionDenied = await consumeSandboxEgressPermissionDeniedSignal(activeSessionId);
|
|
4985
|
+
const { authRequired, permissionDenied } = await consumeEgressSignals(activeSessionId);
|
|
6106
4986
|
return formatSandboxCommandResult({
|
|
6107
4987
|
ok: result.exitCode === 0,
|
|
6108
4988
|
command,
|
|
@@ -6421,25 +5301,25 @@ function createSandbox(options) {
|
|
|
6421
5301
|
|
|
6422
5302
|
// src/chat/tools/sandbox/file-uploads.ts
|
|
6423
5303
|
import path8 from "path";
|
|
6424
|
-
import { z as
|
|
5304
|
+
import { z as z9 } from "zod";
|
|
6425
5305
|
var MAX_SANDBOX_FILE_UPLOAD_BYTES = 10 * 1024 * 1024;
|
|
6426
5306
|
var SANDBOX_ARTIFACTS_DIR = "/tmp/junior/artifacts";
|
|
6427
|
-
var sandboxFileReferenceSchema =
|
|
6428
|
-
path:
|
|
5307
|
+
var sandboxFileReferenceSchema = z9.object({
|
|
5308
|
+
path: z9.string().min(1).describe(
|
|
6429
5309
|
"Exact source path of an existing sandbox file. Preserve paths returned by other tools unchanged; do not move or rewrite them before sending."
|
|
6430
5310
|
),
|
|
6431
|
-
filename:
|
|
5311
|
+
filename: z9.string().min(1).nullable().optional().describe(
|
|
6432
5312
|
"Optional filename override shown to the recipient. Null is treated as omitted."
|
|
6433
5313
|
),
|
|
6434
|
-
mimeType:
|
|
6435
|
-
bytes:
|
|
5314
|
+
mimeType: z9.string().min(1).nullable().optional().describe("Optional MIME type override. Null is treated as omitted."),
|
|
5315
|
+
bytes: z9.number().int().nonnegative().nullable().optional().describe(
|
|
6436
5316
|
"Optional known file size returned by a producing tool. Null is treated as omitted; sending tools validate the file contents directly."
|
|
6437
5317
|
)
|
|
6438
5318
|
});
|
|
6439
5319
|
var generatedArtifactFileRefSchema = sandboxFileReferenceSchema.extend({
|
|
6440
|
-
filename:
|
|
6441
|
-
mimeType:
|
|
6442
|
-
bytes:
|
|
5320
|
+
filename: z9.string().min(1),
|
|
5321
|
+
mimeType: z9.string().min(1).optional(),
|
|
5322
|
+
bytes: z9.number().int().nonnegative()
|
|
6443
5323
|
}).strict();
|
|
6444
5324
|
var MIME_BY_EXTENSION = {
|
|
6445
5325
|
".png": "image/png",
|
|
@@ -6603,6 +5483,7 @@ function createAgentSandbox(options) {
|
|
|
6603
5483
|
referenceFiles: listReferenceFiles(),
|
|
6604
5484
|
traceContext: options.traceContext,
|
|
6605
5485
|
tracePropagation: options.tracePropagation,
|
|
5486
|
+
egressSignals: options.egressSignals,
|
|
6606
5487
|
credentialEgress: options.credentialEgress,
|
|
6607
5488
|
prepare: options.prepareSandbox,
|
|
6608
5489
|
onSandboxRefChanged: async (sandboxRef) => {
|
|
@@ -7342,13 +6223,13 @@ function serializeMcpPayload(payload, privacy, options = {}) {
|
|
|
7342
6223
|
}
|
|
7343
6224
|
|
|
7344
6225
|
// src/chat/tools/sandbox/bash.ts
|
|
7345
|
-
import { z as
|
|
6226
|
+
import { z as z10 } from "zod";
|
|
7346
6227
|
function createBashTool2() {
|
|
7347
6228
|
return zodTool({
|
|
7348
6229
|
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.",
|
|
7349
|
-
inputSchema:
|
|
7350
|
-
command:
|
|
7351
|
-
timeoutMs:
|
|
6230
|
+
inputSchema: z10.object({
|
|
6231
|
+
command: z10.string().min(1).describe("Bash command to run inside the sandbox."),
|
|
6232
|
+
timeoutMs: z10.coerce.number().int().min(1e3).describe(
|
|
7352
6233
|
"Optional command timeout in milliseconds. Use for commands that may hang."
|
|
7353
6234
|
).optional()
|
|
7354
6235
|
}),
|
|
@@ -7362,14 +6243,14 @@ function createBashTool2() {
|
|
|
7362
6243
|
}
|
|
7363
6244
|
|
|
7364
6245
|
// src/chat/tools/web/image-generate.ts
|
|
7365
|
-
import { z as
|
|
6246
|
+
import { z as z11 } from "zod";
|
|
7366
6247
|
var imageGenerateOutputSchema = juniorToolResultSchema.extend({
|
|
7367
|
-
model:
|
|
7368
|
-
prompt:
|
|
7369
|
-
enrichedPrompt:
|
|
7370
|
-
image_count:
|
|
7371
|
-
images:
|
|
7372
|
-
delivery:
|
|
6248
|
+
model: z11.string(),
|
|
6249
|
+
prompt: z11.string(),
|
|
6250
|
+
enrichedPrompt: z11.string(),
|
|
6251
|
+
image_count: z11.number().int().nonnegative(),
|
|
6252
|
+
images: z11.array(generatedArtifactFileRefSchema),
|
|
6253
|
+
delivery: z11.string()
|
|
7373
6254
|
}).strict();
|
|
7374
6255
|
var ENRICHMENT_SYSTEM_PROMPT = `You are an image prompt enrichment agent. Your job is to rewrite image generation requests to reflect a specific visual identity and mood.
|
|
7375
6256
|
|
|
@@ -7430,8 +6311,8 @@ function parseImageGenerationError(status, body, model) {
|
|
|
7430
6311
|
function createImageGenerateTool(hooks, options, deps = {}) {
|
|
7431
6312
|
return zodTool({
|
|
7432
6313
|
description: "Generate images from a prompt. Use when the user wants to visually show or represent something \u2014 feelings, concepts, art, humor, or any visual idea. Also use for explicit image creation requests.",
|
|
7433
|
-
inputSchema:
|
|
7434
|
-
prompt:
|
|
6314
|
+
inputSchema: z11.object({
|
|
6315
|
+
prompt: z11.string().min(1).max(4e3).describe("Image generation prompt.")
|
|
7435
6316
|
}),
|
|
7436
6317
|
outputSchema: imageGenerateOutputSchema,
|
|
7437
6318
|
execute: async ({ prompt }) => {
|
|
@@ -7517,7 +6398,7 @@ function createImageGenerateTool(hooks, options, deps = {}) {
|
|
|
7517
6398
|
}
|
|
7518
6399
|
|
|
7519
6400
|
// src/chat/tools/skill/call-mcp-tool.ts
|
|
7520
|
-
import { z as
|
|
6401
|
+
import { z as z12 } from "zod";
|
|
7521
6402
|
function resolveMcpArguments(input) {
|
|
7522
6403
|
const extraKeys = Object.keys(input).filter(
|
|
7523
6404
|
(key) => key !== "tool_name" && key !== "arguments"
|
|
@@ -7553,9 +6434,9 @@ function missingToolMessage(toolName, provider) {
|
|
|
7553
6434
|
function createCallMcpToolTool(mcpToolManager) {
|
|
7554
6435
|
return zodTool({
|
|
7555
6436
|
description: "Call an active MCP tool by exact tool_name. Use searchMcpTools to discover tool names and schemas; copy required provider fields into arguments. Do not call with only tool_name unless the discovered tool has no arguments. Authorization is handled by the runtime when required.",
|
|
7556
|
-
inputSchema:
|
|
7557
|
-
tool_name:
|
|
7558
|
-
arguments:
|
|
6437
|
+
inputSchema: z12.object({
|
|
6438
|
+
tool_name: z12.string().min(1).describe("Exact MCP tool_name from searchMcpTools."),
|
|
6439
|
+
arguments: z12.record(z12.string(), z12.unknown()).describe(
|
|
7559
6440
|
'Arguments matching the disclosed MCP tool schema, for example { "query": "..." } when searchMcpTools shows query is required.'
|
|
7560
6441
|
).optional()
|
|
7561
6442
|
}).passthrough(),
|
|
@@ -7596,7 +6477,7 @@ function createCallMcpToolTool(mcpToolManager) {
|
|
|
7596
6477
|
}
|
|
7597
6478
|
|
|
7598
6479
|
// src/chat/tools/skill/load-skill.ts
|
|
7599
|
-
import { z as
|
|
6480
|
+
import { z as z13 } from "zod";
|
|
7600
6481
|
function toLoadedSkill(result, availableSkills) {
|
|
7601
6482
|
if (result.ok !== true || typeof result.skill_name !== "string" || typeof result.description !== "string" || typeof result.skill_dir !== "string" || typeof result.instructions !== "string") {
|
|
7602
6483
|
return null;
|
|
@@ -7645,8 +6526,8 @@ async function loadSkillFromHost(availableSkills, skillName) {
|
|
|
7645
6526
|
function createLoadSkillTool(availableSkills, options) {
|
|
7646
6527
|
return zodTool({
|
|
7647
6528
|
description: "Load a skill by name for this turn. The result includes working_directory; resolve skill paths there and run skill-owned bash commands from there or with absolute paths. When the result includes mcp_provider, use searchMcpTools before callMcpTool. Use when a request clearly matches a known skill.",
|
|
7648
|
-
inputSchema:
|
|
7649
|
-
skill_name:
|
|
6529
|
+
inputSchema: z13.object({
|
|
6530
|
+
skill_name: z13.string().min(1).describe("Skill name to load, without the leading slash.")
|
|
7650
6531
|
}),
|
|
7651
6532
|
outputSchema: juniorToolResultSchema,
|
|
7652
6533
|
execute: async ({ skill_name }) => {
|
|
@@ -7664,7 +6545,7 @@ function createLoadSkillTool(availableSkills, options) {
|
|
|
7664
6545
|
}
|
|
7665
6546
|
|
|
7666
6547
|
// src/chat/tools/skill/search-mcp-tools.ts
|
|
7667
|
-
import { z as
|
|
6548
|
+
import { z as z14 } from "zod";
|
|
7668
6549
|
|
|
7669
6550
|
// src/chat/tool-support/schema-summary.ts
|
|
7670
6551
|
function getSchemaProperties(schema) {
|
|
@@ -7794,37 +6675,37 @@ function toActiveMcpCatalogSummaries(toolDefs) {
|
|
|
7794
6675
|
// src/chat/tools/skill/search-mcp-tools.ts
|
|
7795
6676
|
var DEFAULT_MAX_RESULTS = 5;
|
|
7796
6677
|
var MAX_RESULTS = 20;
|
|
7797
|
-
var providerSummarySchema =
|
|
7798
|
-
provider:
|
|
7799
|
-
description:
|
|
7800
|
-
active:
|
|
6678
|
+
var providerSummarySchema = z14.object({
|
|
6679
|
+
provider: z14.string(),
|
|
6680
|
+
description: z14.string(),
|
|
6681
|
+
active: z14.boolean()
|
|
7801
6682
|
}).strict();
|
|
7802
|
-
var mcpCallExampleSchema =
|
|
7803
|
-
tool_name:
|
|
7804
|
-
arguments:
|
|
6683
|
+
var mcpCallExampleSchema = z14.object({
|
|
6684
|
+
tool_name: z14.string(),
|
|
6685
|
+
arguments: z14.record(z14.string(), z14.string())
|
|
7805
6686
|
}).strict();
|
|
7806
|
-
var exposedToolSummarySchema =
|
|
7807
|
-
tool_name:
|
|
7808
|
-
mcp_tool_name:
|
|
7809
|
-
provider:
|
|
7810
|
-
title:
|
|
7811
|
-
description:
|
|
7812
|
-
signature:
|
|
6687
|
+
var exposedToolSummarySchema = z14.object({
|
|
6688
|
+
tool_name: z14.string(),
|
|
6689
|
+
mcp_tool_name: z14.string(),
|
|
6690
|
+
provider: z14.string(),
|
|
6691
|
+
title: z14.string().optional(),
|
|
6692
|
+
description: z14.string(),
|
|
6693
|
+
signature: z14.string(),
|
|
7813
6694
|
call: mcpCallExampleSchema,
|
|
7814
|
-
input_schema:
|
|
7815
|
-
input_schema_summary:
|
|
7816
|
-
output_schema:
|
|
7817
|
-
annotations:
|
|
6695
|
+
input_schema: z14.record(z14.string(), z14.unknown()),
|
|
6696
|
+
input_schema_summary: z14.string(),
|
|
6697
|
+
output_schema: z14.record(z14.string(), z14.unknown()).optional(),
|
|
6698
|
+
annotations: z14.record(z14.string(), z14.unknown()).optional()
|
|
7818
6699
|
}).strict();
|
|
7819
6700
|
var searchMcpToolsOutputSchema = juniorToolResultSchema.extend({
|
|
7820
|
-
query:
|
|
7821
|
-
provider:
|
|
7822
|
-
total_active_tools:
|
|
7823
|
-
returned_tools:
|
|
7824
|
-
execution_tool:
|
|
6701
|
+
query: z14.string().nullable(),
|
|
6702
|
+
provider: z14.string().nullable(),
|
|
6703
|
+
total_active_tools: z14.number().int().nonnegative(),
|
|
6704
|
+
returned_tools: z14.number().int().nonnegative(),
|
|
6705
|
+
execution_tool: z14.literal("callMcpTool"),
|
|
7825
6706
|
execution_example: mcpCallExampleSchema,
|
|
7826
|
-
available_providers:
|
|
7827
|
-
tools:
|
|
6707
|
+
available_providers: z14.array(providerSummarySchema),
|
|
6708
|
+
tools: z14.array(exposedToolSummarySchema)
|
|
7828
6709
|
}).strict();
|
|
7829
6710
|
function normalize(value) {
|
|
7830
6711
|
return value.toLowerCase().replace(/[^a-z0-9_]+/g, " ").trim();
|
|
@@ -7944,14 +6825,14 @@ function searchProviderCatalog(providers, query) {
|
|
|
7944
6825
|
function createSearchMcpToolsTool(mcpToolManager) {
|
|
7945
6826
|
return zodTool({
|
|
7946
6827
|
description: "List or search MCP providers and active MCP tools. When provider is supplied and not yet active, Junior connects to it on demand and returns tool descriptors including schemas. Without provider, returns active tools plus matching configured providers without connecting. Use when choosing a provider tool or when callMcpTool arguments are unclear.",
|
|
7947
|
-
inputSchema:
|
|
7948
|
-
query:
|
|
6828
|
+
inputSchema: z14.object({
|
|
6829
|
+
query: z14.string().min(1).describe(
|
|
7949
6830
|
"Optional search terms describing the MCP tool or arguments needed."
|
|
7950
6831
|
).optional(),
|
|
7951
|
-
provider:
|
|
6832
|
+
provider: z14.string().min(1).describe(
|
|
7952
6833
|
"Optional provider name to list or search within. If configured but not yet connected, Junior activates it on demand."
|
|
7953
6834
|
).optional(),
|
|
7954
|
-
max_results:
|
|
6835
|
+
max_results: z14.number().int().min(1).max(MAX_RESULTS).describe("Maximum matching tool descriptors to return.").optional()
|
|
7955
6836
|
}).strict(),
|
|
7956
6837
|
outputSchema: searchMcpToolsOutputSchema,
|
|
7957
6838
|
privateTraceResult: (result) => ({
|
|
@@ -8006,12 +6887,12 @@ function createSearchMcpToolsTool(mcpToolManager) {
|
|
|
8006
6887
|
}
|
|
8007
6888
|
|
|
8008
6889
|
// src/chat/tools/runtime/report-progress.ts
|
|
8009
|
-
import { z as
|
|
6890
|
+
import { z as z15 } from "zod";
|
|
8010
6891
|
function createReportProgressTool() {
|
|
8011
6892
|
return zodTool({
|
|
8012
6893
|
description: "Update the user-visible assistant loading message with a short progress phase. For every non-trivial turn, call this early with the initial major work phase, then call it again only when the major phase meaningfully changes. Messages must be written in sentence case with a present-participle verb (e.g. 'Searching docs', 'Reviewing results', 'Running checks'). Skip trivial direct answers, generic filler, and minor substeps.",
|
|
8013
|
-
inputSchema:
|
|
8014
|
-
message:
|
|
6894
|
+
inputSchema: z15.object({
|
|
6895
|
+
message: z15.string().min(1).describe("Short user-facing progress message.")
|
|
8015
6896
|
}),
|
|
8016
6897
|
outputSchema: juniorToolResultSchema,
|
|
8017
6898
|
execute: async () => ({
|
|
@@ -8022,34 +6903,34 @@ function createReportProgressTool() {
|
|
|
8022
6903
|
}
|
|
8023
6904
|
|
|
8024
6905
|
// src/chat/tools/resource-events.ts
|
|
8025
|
-
import { z as
|
|
6906
|
+
import { z as z17 } from "zod";
|
|
8026
6907
|
|
|
8027
6908
|
// src/chat/resource-events/store.ts
|
|
8028
6909
|
import { createHash } from "crypto";
|
|
8029
6910
|
import { destinationSchema } from "@sentry/junior-plugin-api";
|
|
8030
|
-
import { z as
|
|
6911
|
+
import { z as z16 } from "zod";
|
|
8031
6912
|
var RESOURCE_EVENT_PREFIX = "junior:resource_event_subscription";
|
|
8032
6913
|
var INDEX_LOCK_TTL_MS = 1e4;
|
|
8033
6914
|
var SUBSCRIPTION_LOCK_TTL_MS = 1e4;
|
|
8034
6915
|
var SUBSCRIPTION_LOCK_WAIT_MS = 1e4;
|
|
8035
6916
|
var SUBSCRIPTION_LOCK_RETRY_MS = 25;
|
|
8036
6917
|
var SUBSCRIPTION_LOCK_HEARTBEAT_MS = 3e3;
|
|
8037
|
-
var subscriptionStatusSchema =
|
|
8038
|
-
var subscriptionIdIndexSchema =
|
|
8039
|
-
var subscriptionSchema =
|
|
8040
|
-
conversationId:
|
|
8041
|
-
createdAtMs:
|
|
6918
|
+
var subscriptionStatusSchema = z16.enum(["active", "cancelled", "completed"]);
|
|
6919
|
+
var subscriptionIdIndexSchema = z16.array(z16.string().min(1));
|
|
6920
|
+
var subscriptionSchema = z16.object({
|
|
6921
|
+
conversationId: z16.string().min(1),
|
|
6922
|
+
createdAtMs: z16.number().finite(),
|
|
8042
6923
|
destination: destinationSchema,
|
|
8043
|
-
events:
|
|
8044
|
-
expiresAtMs:
|
|
8045
|
-
id:
|
|
8046
|
-
intent:
|
|
8047
|
-
label:
|
|
8048
|
-
provider:
|
|
8049
|
-
resourceRef:
|
|
8050
|
-
resourceType:
|
|
6924
|
+
events: z16.array(z16.string().min(1)).min(1),
|
|
6925
|
+
expiresAtMs: z16.number().finite(),
|
|
6926
|
+
id: z16.string().min(1),
|
|
6927
|
+
intent: z16.string().min(1),
|
|
6928
|
+
label: z16.string().min(1),
|
|
6929
|
+
provider: z16.string().min(1),
|
|
6930
|
+
resourceRef: z16.string().min(1),
|
|
6931
|
+
resourceType: z16.string().min(1),
|
|
8051
6932
|
status: subscriptionStatusSchema,
|
|
8052
|
-
updatedAtMs:
|
|
6933
|
+
updatedAtMs: z16.number().finite()
|
|
8053
6934
|
}).strict();
|
|
8054
6935
|
function digest(value) {
|
|
8055
6936
|
return createHash("sha256").update(value).digest("hex").slice(0, 32);
|
|
@@ -8405,18 +7286,18 @@ var RESOURCE_WATCH_TOOL_SOURCE = {
|
|
|
8405
7286
|
id: "resource-watches",
|
|
8406
7287
|
description: "Inspect or stop resource watches for the current conversation."
|
|
8407
7288
|
};
|
|
8408
|
-
var subscribeInputSchema =
|
|
8409
|
-
resourceRef:
|
|
8410
|
-
provider:
|
|
8411
|
-
resourceType:
|
|
8412
|
-
label:
|
|
8413
|
-
events:
|
|
7289
|
+
var subscribeInputSchema = z17.object({
|
|
7290
|
+
resourceRef: z17.string().describe("Opaque resource ref copied from a subscribable tool result."),
|
|
7291
|
+
provider: z17.string().describe("Provider that owns the resource ref."),
|
|
7292
|
+
resourceType: z17.string().describe("Provider-defined resource type from the subscribable hint."),
|
|
7293
|
+
label: z17.string().describe("Human-readable resource label from the subscribable hint."),
|
|
7294
|
+
events: z17.array(z17.string()).min(1).describe(
|
|
8414
7295
|
"High-signal event names to deliver to this conversation when they occur."
|
|
8415
7296
|
),
|
|
8416
|
-
intent:
|
|
7297
|
+
intent: z17.string().describe(
|
|
8417
7298
|
"Concise reason this conversation wants these events, used when an event arrives."
|
|
8418
7299
|
),
|
|
8419
|
-
ttlMs:
|
|
7300
|
+
ttlMs: z17.coerce.number().describe(
|
|
8420
7301
|
"How long to keep the subscription active. Defaults to 14 days and is capped at 30 days."
|
|
8421
7302
|
).optional()
|
|
8422
7303
|
});
|
|
@@ -8509,7 +7390,7 @@ function createListResourceEventSubscriptionsTool(context) {
|
|
|
8509
7390
|
description: "List active resource event subscriptions for the current conversation.",
|
|
8510
7391
|
exposure: "deferred",
|
|
8511
7392
|
source: RESOURCE_WATCH_TOOL_SOURCE,
|
|
8512
|
-
inputSchema:
|
|
7393
|
+
inputSchema: z17.object({}),
|
|
8513
7394
|
outputSchema: juniorToolResultSchema,
|
|
8514
7395
|
async execute() {
|
|
8515
7396
|
const conversationId = requireConversationContext(context);
|
|
@@ -8542,7 +7423,7 @@ function createStopWatchingResourcesTool(context) {
|
|
|
8542
7423
|
description: "Stop every resource watch for the current conversation. Infer the user's intent from context instead of requiring a special command, and call this tool before confirming that watching stopped.",
|
|
8543
7424
|
exposure: "deferred",
|
|
8544
7425
|
source: RESOURCE_WATCH_TOOL_SOURCE,
|
|
8545
|
-
inputSchema:
|
|
7426
|
+
inputSchema: z17.object({}),
|
|
8546
7427
|
outputSchema: juniorToolResultSchema,
|
|
8547
7428
|
async execute() {
|
|
8548
7429
|
const conversationId = requireConversationContext(context);
|
|
@@ -8649,11 +7530,11 @@ async function listThreadReplies(input) {
|
|
|
8649
7530
|
}
|
|
8650
7531
|
|
|
8651
7532
|
// src/chat/slack/timestamp-param.ts
|
|
8652
|
-
import { z as
|
|
7533
|
+
import { z as z18 } from "zod";
|
|
8653
7534
|
function slackTimestampParam(description) {
|
|
8654
|
-
return
|
|
7535
|
+
return z18.preprocess(
|
|
8655
7536
|
(value) => typeof value === "number" && Number.isFinite(value) ? String(value) : value,
|
|
8656
|
-
|
|
7537
|
+
z18.string().min(1)
|
|
8657
7538
|
).describe(description);
|
|
8658
7539
|
}
|
|
8659
7540
|
function optionalSlackTimestampParam(description) {
|
|
@@ -8684,10 +7565,10 @@ function parseRequiredSlackTimestampParam(field, value) {
|
|
|
8684
7565
|
}
|
|
8685
7566
|
|
|
8686
7567
|
// src/chat/slack/tools/channel-list-messages.ts
|
|
8687
|
-
import { z as
|
|
8688
|
-
var booleanInput2 = (description) =>
|
|
7568
|
+
import { z as z19 } from "zod";
|
|
7569
|
+
var booleanInput2 = (description) => z19.preprocess(
|
|
8689
7570
|
(value) => value === "true" ? true : value === "false" ? false : value,
|
|
8690
|
-
|
|
7571
|
+
z19.boolean()
|
|
8691
7572
|
).describe(description);
|
|
8692
7573
|
function normalizeRangeTimestamp(field, value, targetChannelId) {
|
|
8693
7574
|
if (value === void 0) {
|
|
@@ -8714,9 +7595,9 @@ function createSlackChannelListMessagesTool(context) {
|
|
|
8714
7595
|
return zodTool({
|
|
8715
7596
|
description: "List channel messages from Slack history in the active channel context. Use when the user asks for recent or historical channel context outside this thread. Do not use for live monitoring or when current thread context already answers the question.",
|
|
8716
7597
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
8717
|
-
inputSchema:
|
|
8718
|
-
limit:
|
|
8719
|
-
cursor:
|
|
7598
|
+
inputSchema: z19.object({
|
|
7599
|
+
limit: z19.coerce.number().int().min(1).max(1e3).describe("Maximum number of messages to return across pages.").optional(),
|
|
7600
|
+
cursor: z19.string().min(1).describe("Optional cursor to continue from a prior call.").optional(),
|
|
8720
7601
|
oldest: optionalSlackTimestampParam(
|
|
8721
7602
|
"Optional oldest message timestamp (Slack ts) for range filtering."
|
|
8722
7603
|
),
|
|
@@ -8726,7 +7607,7 @@ function createSlackChannelListMessagesTool(context) {
|
|
|
8726
7607
|
inclusive: booleanInput2(
|
|
8727
7608
|
"Whether oldest/latest bounds should be inclusive."
|
|
8728
7609
|
).optional(),
|
|
8729
|
-
max_pages:
|
|
7610
|
+
max_pages: z19.coerce.number().int().min(1).max(10).describe("Maximum number of API pages to traverse in a single call.").optional()
|
|
8730
7611
|
}),
|
|
8731
7612
|
outputSchema: juniorToolResultSchema,
|
|
8732
7613
|
execute: async ({
|
|
@@ -8800,20 +7681,20 @@ function createSlackChannelListMessagesTool(context) {
|
|
|
8800
7681
|
}
|
|
8801
7682
|
|
|
8802
7683
|
// src/chat/slack/tools/conversation-search.ts
|
|
8803
|
-
import { z as
|
|
7684
|
+
import { z as z20 } from "zod";
|
|
8804
7685
|
var DEFAULT_LIMIT = 5;
|
|
8805
7686
|
var conversationSearchOutputSchema = juniorToolResultSchema.extend({
|
|
8806
|
-
query:
|
|
8807
|
-
count:
|
|
8808
|
-
threads:
|
|
8809
|
-
|
|
8810
|
-
conversation_id:
|
|
8811
|
-
thread_ts:
|
|
8812
|
-
message_id:
|
|
8813
|
-
message_role:
|
|
8814
|
-
message_timestamp:
|
|
8815
|
-
excerpt:
|
|
8816
|
-
permalink:
|
|
7687
|
+
query: z20.string(),
|
|
7688
|
+
count: z20.number().int().nonnegative(),
|
|
7689
|
+
threads: z20.array(
|
|
7690
|
+
z20.object({
|
|
7691
|
+
conversation_id: z20.string().min(1),
|
|
7692
|
+
thread_ts: z20.string().min(1),
|
|
7693
|
+
message_id: z20.string().min(1),
|
|
7694
|
+
message_role: z20.enum(["assistant", "user"]),
|
|
7695
|
+
message_timestamp: z20.string().datetime(),
|
|
7696
|
+
excerpt: z20.string(),
|
|
7697
|
+
permalink: z20.string().url().optional()
|
|
8817
7698
|
}).strict()
|
|
8818
7699
|
)
|
|
8819
7700
|
});
|
|
@@ -8826,9 +7707,9 @@ function createSlackConversationSearchTool(scope, currentConversationId, deps =
|
|
|
8826
7707
|
description: "Search retained public Junior conversation threads in the current Slack workspace."
|
|
8827
7708
|
},
|
|
8828
7709
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
8829
|
-
inputSchema:
|
|
8830
|
-
query:
|
|
8831
|
-
limit:
|
|
7710
|
+
inputSchema: z20.object({
|
|
7711
|
+
query: z20.string().trim().min(1).max(200).describe("Words or a short phrase to find in prior conversations."),
|
|
7712
|
+
limit: z20.number().int().min(1).max(10).describe("Maximum number of prior conversation threads to return.").nullable().optional()
|
|
8832
7713
|
}),
|
|
8833
7714
|
outputSchema: conversationSearchOutputSchema,
|
|
8834
7715
|
execute: async ({ query, limit }) => {
|
|
@@ -8875,27 +7756,27 @@ function createSlackConversationSearchTool(scope, currentConversationId, deps =
|
|
|
8875
7756
|
}
|
|
8876
7757
|
|
|
8877
7758
|
// src/chat/slack/tools/public-search.ts
|
|
8878
|
-
import { z as
|
|
7759
|
+
import { z as z21 } from "zod";
|
|
8879
7760
|
var DEFAULT_LIMIT2 = 10;
|
|
8880
|
-
var optionalTimestampSchema =
|
|
7761
|
+
var optionalTimestampSchema = z21.preprocess(
|
|
8881
7762
|
(value) => typeof value === "string" && value.trim() === "" ? void 0 : value,
|
|
8882
|
-
|
|
7763
|
+
z21.coerce.number().int().nonnegative().describe("Optional Unix timestamp bound.").optional()
|
|
8883
7764
|
);
|
|
8884
|
-
var searchMessageSchema =
|
|
8885
|
-
author_name:
|
|
8886
|
-
author_user_id:
|
|
8887
|
-
channel_id:
|
|
8888
|
-
channel_name:
|
|
8889
|
-
message_ts:
|
|
8890
|
-
content:
|
|
8891
|
-
is_author_bot:
|
|
8892
|
-
permalink:
|
|
7765
|
+
var searchMessageSchema = z21.object({
|
|
7766
|
+
author_name: z21.string().optional(),
|
|
7767
|
+
author_user_id: z21.string().optional(),
|
|
7768
|
+
channel_id: z21.string().min(1),
|
|
7769
|
+
channel_name: z21.string().optional(),
|
|
7770
|
+
message_ts: z21.string().min(1),
|
|
7771
|
+
content: z21.string(),
|
|
7772
|
+
is_author_bot: z21.boolean().optional(),
|
|
7773
|
+
permalink: z21.string().url()
|
|
8893
7774
|
});
|
|
8894
7775
|
var publicSearchOutputSchema = juniorToolResultSchema.extend({
|
|
8895
|
-
query:
|
|
8896
|
-
count:
|
|
8897
|
-
messages:
|
|
8898
|
-
next_cursor:
|
|
7776
|
+
query: z21.string(),
|
|
7777
|
+
count: z21.number().int().nonnegative(),
|
|
7778
|
+
messages: z21.array(searchMessageSchema),
|
|
7779
|
+
next_cursor: z21.string().optional()
|
|
8899
7780
|
});
|
|
8900
7781
|
function normalizeMessage(value) {
|
|
8901
7782
|
const parsed = searchMessageSchema.safeParse(value);
|
|
@@ -8917,8 +7798,8 @@ function createSlackPublicSearchTool(actionToken) {
|
|
|
8917
7798
|
return zodTool({
|
|
8918
7799
|
description: "Search public Slack channel messages across the current workspace. Use when the user asks about company activity, announcements, public mentions, or context outside the active channel. Search only when requested or clearly needed, prefer focused keywords and time bounds, and cite returned permalinks. This never searches private channels or DMs.",
|
|
8919
7800
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
8920
|
-
inputSchema:
|
|
8921
|
-
query:
|
|
7801
|
+
inputSchema: z21.object({
|
|
7802
|
+
query: z21.string().trim().min(1).max(500).describe(
|
|
8922
7803
|
"A focused Slack search query, including Slack search filters when useful."
|
|
8923
7804
|
),
|
|
8924
7805
|
after: optionalTimestampSchema.describe(
|
|
@@ -8927,10 +7808,10 @@ function createSlackPublicSearchTool(actionToken) {
|
|
|
8927
7808
|
before: optionalTimestampSchema.describe(
|
|
8928
7809
|
"Optional Unix timestamp upper bound."
|
|
8929
7810
|
),
|
|
8930
|
-
cursor:
|
|
8931
|
-
limit:
|
|
8932
|
-
sort:
|
|
8933
|
-
sort_dir:
|
|
7811
|
+
cursor: z21.string().min(1).describe("Cursor for the next result page.").optional(),
|
|
7812
|
+
limit: z21.coerce.number().int().min(1).max(20).describe("Maximum results to return; Slack allows at most 20.").optional(),
|
|
7813
|
+
sort: z21.enum(["score", "timestamp"]).describe("Rank by relevance or timestamp.").optional(),
|
|
7814
|
+
sort_dir: z21.enum(["asc", "desc"]).describe("Sort direction.").optional()
|
|
8934
7815
|
}),
|
|
8935
7816
|
outputSchema: publicSearchOutputSchema,
|
|
8936
7817
|
execute: async ({
|
|
@@ -8996,7 +7877,7 @@ function createSlackPublicSearchTool(actionToken) {
|
|
|
8996
7877
|
}
|
|
8997
7878
|
|
|
8998
7879
|
// src/chat/slack/tools/message-add-reaction.ts
|
|
8999
|
-
import { z as
|
|
7880
|
+
import { z as z22 } from "zod";
|
|
9000
7881
|
|
|
9001
7882
|
// src/chat/tools/idempotency.ts
|
|
9002
7883
|
function stableSerialize(value) {
|
|
@@ -9020,8 +7901,8 @@ function createOperationKey(toolName, input) {
|
|
|
9020
7901
|
function createSlackMessageAddReactionTool(context, state) {
|
|
9021
7902
|
return zodTool({
|
|
9022
7903
|
description: "Add an emoji reaction to the current inbound Slack message. Use when the user asks for a reaction on the current message without another target. Provide a Slack emoji alias name (for example `thumbsup`, `white_check_mark`, or `thumbsup::skin-tone-6`), not a unicode emoji glyph. The target message is injected by runtime context; do not use this for arbitrary historical messages.",
|
|
9023
|
-
inputSchema:
|
|
9024
|
-
emoji:
|
|
7904
|
+
inputSchema: z22.object({
|
|
7905
|
+
emoji: z22.string().min(1).max(64).describe(
|
|
9025
7906
|
"Slack emoji alias name to react with (for example `thumbsup`, `white_check_mark`, or `thumbsup::skin-tone-6`). Optional surrounding colons are allowed."
|
|
9026
7907
|
)
|
|
9027
7908
|
}),
|
|
@@ -9072,18 +7953,18 @@ function createSlackMessageAddReactionTool(context, state) {
|
|
|
9072
7953
|
|
|
9073
7954
|
// src/chat/slack/tools/send-files.ts
|
|
9074
7955
|
import { createHash as createHash2 } from "crypto";
|
|
9075
|
-
import { z as
|
|
9076
|
-
var sendFilesDataSchema =
|
|
9077
|
-
channel_id:
|
|
9078
|
-
deduplicated:
|
|
9079
|
-
file_count:
|
|
9080
|
-
file_ids:
|
|
9081
|
-
thread_ts:
|
|
7956
|
+
import { z as z23 } from "zod";
|
|
7957
|
+
var sendFilesDataSchema = z23.object({
|
|
7958
|
+
channel_id: z23.string().min(1),
|
|
7959
|
+
deduplicated: z23.boolean().optional(),
|
|
7960
|
+
file_count: z23.number().int().nonnegative(),
|
|
7961
|
+
file_ids: z23.array(z23.string().min(1)).optional(),
|
|
7962
|
+
thread_ts: z23.string().min(1)
|
|
9082
7963
|
});
|
|
9083
7964
|
var sendFilesResultSchema = juniorToolResultSchema.extend({
|
|
9084
|
-
ok:
|
|
9085
|
-
status:
|
|
9086
|
-
target:
|
|
7965
|
+
ok: z23.literal(true),
|
|
7966
|
+
status: z23.literal("success"),
|
|
7967
|
+
target: z23.string().min(1),
|
|
9087
7968
|
data: sendFilesDataSchema
|
|
9088
7969
|
});
|
|
9089
7970
|
function normalizeFiles(files) {
|
|
@@ -9108,8 +7989,8 @@ function fileOperationInput(files) {
|
|
|
9108
7989
|
function createSendFilesTool(context, state, materializeFile) {
|
|
9109
7990
|
return zodTool({
|
|
9110
7991
|
description: "Send one or more sandbox files into the active Slack conversation. Use when the user asks to attach, send, or share files here, in this conversation, or in this thread. Do not use for ordinary assistant text, top-level channel posts, other named channels, inline @mentions, or pinging mentioned users.",
|
|
9111
|
-
inputSchema:
|
|
9112
|
-
files:
|
|
7992
|
+
inputSchema: z23.object({
|
|
7993
|
+
files: z23.array(sandboxFileReferenceSchema).min(1).describe(
|
|
9113
7994
|
"One or more existing sandbox files. Objects returned by imageGenerate or webFetch can be passed unchanged."
|
|
9114
7995
|
)
|
|
9115
7996
|
}),
|
|
@@ -9413,13 +8294,13 @@ function storedCanvasUrl(state, canvasId) {
|
|
|
9413
8294
|
}
|
|
9414
8295
|
|
|
9415
8296
|
// src/chat/slack/tools/canvas/create.ts
|
|
9416
|
-
import { z as
|
|
8297
|
+
import { z as z24 } from "zod";
|
|
9417
8298
|
function createSlackCanvasCreateTool(context, state) {
|
|
9418
8299
|
return zodTool({
|
|
9419
8300
|
description: "Create a Slack canvas for long-form output in the active assistant context channel. Use when the answer is better as a reusable document than a thread reply: long-form research, timelines, bios/profiles, structured notes, plans, comparisons, or anything likely to exceed one compact Slack reply. After creating it, reply with one or two short sentences plus the canvas link; do not recap the canvas contents. Do not use for short answers that fit cleanly in one normal thread reply.",
|
|
9420
|
-
inputSchema:
|
|
9421
|
-
title:
|
|
9422
|
-
markdown:
|
|
8301
|
+
inputSchema: z24.object({
|
|
8302
|
+
title: z24.string().min(1).max(160).describe("Canvas title."),
|
|
8303
|
+
markdown: z24.string().min(1).describe("Canvas markdown body content.")
|
|
9423
8304
|
}),
|
|
9424
8305
|
outputSchema: juniorToolResultSchema,
|
|
9425
8306
|
execute: async ({ title, markdown }) => {
|
|
@@ -9482,35 +8363,35 @@ function createSlackCanvasCreateTool(context, state) {
|
|
|
9482
8363
|
}
|
|
9483
8364
|
|
|
9484
8365
|
// src/chat/slack/tools/canvas/edit.ts
|
|
9485
|
-
import { z as
|
|
8366
|
+
import { z as z25 } from "zod";
|
|
9486
8367
|
function prepareCanvasEditArguments(input) {
|
|
9487
8368
|
return prepareTextReplacementArguments(input);
|
|
9488
8369
|
}
|
|
9489
|
-
var editReplacementSchema2 =
|
|
9490
|
-
oldText:
|
|
8370
|
+
var editReplacementSchema2 = z25.object({
|
|
8371
|
+
oldText: z25.string().min(1).describe(
|
|
9491
8372
|
"Exact Canvas markdown to replace. It must be unique in the current Canvas body and must not overlap another edit."
|
|
9492
8373
|
),
|
|
9493
|
-
newText:
|
|
8374
|
+
newText: z25.string().describe("Replacement Canvas markdown for this edit.")
|
|
9494
8375
|
});
|
|
9495
8376
|
var slackCanvasEditOutputSchema = juniorToolResultSchema.extend({
|
|
9496
|
-
canvas_id:
|
|
9497
|
-
title:
|
|
9498
|
-
permalink:
|
|
9499
|
-
diff:
|
|
9500
|
-
first_changed_line:
|
|
9501
|
-
replacements:
|
|
9502
|
-
normalized_heading_count:
|
|
9503
|
-
summary:
|
|
9504
|
-
deduplicated:
|
|
8377
|
+
canvas_id: z25.string().optional(),
|
|
8378
|
+
title: z25.string().optional(),
|
|
8379
|
+
permalink: z25.string().optional(),
|
|
8380
|
+
diff: z25.string().optional(),
|
|
8381
|
+
first_changed_line: z25.number().int().positive().optional(),
|
|
8382
|
+
replacements: z25.number().int().nonnegative().optional(),
|
|
8383
|
+
normalized_heading_count: z25.number().int().nonnegative().optional(),
|
|
8384
|
+
summary: z25.string().optional(),
|
|
8385
|
+
deduplicated: z25.boolean().optional()
|
|
9505
8386
|
}).strict();
|
|
9506
8387
|
function createSlackCanvasEditTool(state) {
|
|
9507
8388
|
return zodTool({
|
|
9508
8389
|
description: "Edit one Slack canvas with exact markdown replacements. Use for precise changes to existing Canvas content; prefer this over slackCanvasWrite for targeted changes. Each oldText must match exactly, be unique, and not overlap another edit. Returns a diff. Multiple changes to the same canvas: use one edits[] call.",
|
|
9509
8390
|
prepareArguments: prepareCanvasEditArguments,
|
|
9510
8391
|
executionMode: "sequential",
|
|
9511
|
-
inputSchema:
|
|
9512
|
-
canvas:
|
|
9513
|
-
edits:
|
|
8392
|
+
inputSchema: z25.object({
|
|
8393
|
+
canvas: z25.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
|
|
8394
|
+
edits: z25.array(editReplacementSchema2).min(1).describe(
|
|
9514
8395
|
"Exact replacements matched against the current Canvas body, not incrementally."
|
|
9515
8396
|
)
|
|
9516
8397
|
}),
|
|
@@ -9588,29 +8469,29 @@ function createSlackCanvasEditTool(state) {
|
|
|
9588
8469
|
}
|
|
9589
8470
|
|
|
9590
8471
|
// src/chat/slack/tools/canvas/read.ts
|
|
9591
|
-
import { z as
|
|
8472
|
+
import { z as z26 } from "zod";
|
|
9592
8473
|
var slackCanvasReadOutputSchema = juniorToolResultSchema.extend({
|
|
9593
|
-
canvas_id:
|
|
9594
|
-
title:
|
|
9595
|
-
permalink:
|
|
9596
|
-
mimetype:
|
|
9597
|
-
filetype:
|
|
9598
|
-
original_byte_length:
|
|
9599
|
-
content:
|
|
9600
|
-
start_line:
|
|
9601
|
-
end_line:
|
|
9602
|
-
total_lines:
|
|
8474
|
+
canvas_id: z26.string().optional(),
|
|
8475
|
+
title: z26.string().optional(),
|
|
8476
|
+
permalink: z26.string().optional(),
|
|
8477
|
+
mimetype: z26.string().optional(),
|
|
8478
|
+
filetype: z26.string().optional(),
|
|
8479
|
+
original_byte_length: z26.number().int().nonnegative().optional(),
|
|
8480
|
+
content: z26.string().optional(),
|
|
8481
|
+
start_line: z26.number().int().positive().optional(),
|
|
8482
|
+
end_line: z26.number().int().nonnegative().optional(),
|
|
8483
|
+
total_lines: z26.number().int().nonnegative().optional()
|
|
9603
8484
|
}).strict();
|
|
9604
8485
|
function createSlackCanvasReadTool() {
|
|
9605
8486
|
return zodTool({
|
|
9606
8487
|
description: "Read a bounded line range from a Slack canvas as markdown. Use when you need exact Canvas contents to verify facts or make edits safely. Do not use for generic web pages \u2014 use webFetch for those.",
|
|
9607
8488
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
9608
|
-
inputSchema:
|
|
9609
|
-
canvas:
|
|
8489
|
+
inputSchema: z26.object({
|
|
8490
|
+
canvas: z26.string().min(1).describe(
|
|
9610
8491
|
"Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL (e.g. `https://team.slack.com/docs/T.../F...`)."
|
|
9611
8492
|
),
|
|
9612
|
-
offset:
|
|
9613
|
-
limit:
|
|
8493
|
+
offset: z26.coerce.number().int().min(1).describe("1-indexed line number to start reading from.").optional(),
|
|
8494
|
+
limit: z26.coerce.number().int().min(1).describe("Maximum number of lines to read. Defaults to 1000.").optional()
|
|
9614
8495
|
}),
|
|
9615
8496
|
outputSchema: slackCanvasReadOutputSchema,
|
|
9616
8497
|
execute: async ({ canvas, offset, limit }) => {
|
|
@@ -9667,14 +8548,14 @@ function createSlackCanvasReadTool() {
|
|
|
9667
8548
|
}
|
|
9668
8549
|
|
|
9669
8550
|
// src/chat/slack/tools/canvas/write.ts
|
|
9670
|
-
import { z as
|
|
8551
|
+
import { z as z27 } from "zod";
|
|
9671
8552
|
function createSlackCanvasWriteTool(state) {
|
|
9672
8553
|
return zodTool({
|
|
9673
8554
|
description: "Write UTF-8 markdown content to a Slack canvas. Use for deliberate full-Canvas replacement after validation; use slackCanvasEdit for targeted changes to existing canvas content.",
|
|
9674
8555
|
executionMode: "sequential",
|
|
9675
|
-
inputSchema:
|
|
9676
|
-
canvas:
|
|
9677
|
-
content:
|
|
8556
|
+
inputSchema: z27.object({
|
|
8557
|
+
canvas: z27.string().min(1).describe("Canvas/file ID (e.g. `F0ABCDEF`) or Slack canvas/docs URL."),
|
|
8558
|
+
content: z27.string().describe("UTF-8 markdown content to write.")
|
|
9678
8559
|
}),
|
|
9679
8560
|
outputSchema: juniorToolResultSchema,
|
|
9680
8561
|
execute: async ({ canvas, content }) => {
|
|
@@ -9910,12 +8791,12 @@ async function updateListItem(input) {
|
|
|
9910
8791
|
}
|
|
9911
8792
|
|
|
9912
8793
|
// src/chat/slack/id-param.ts
|
|
9913
|
-
import { z as
|
|
8794
|
+
import { z as z28 } from "zod";
|
|
9914
8795
|
function slackChannelIdParam(description) {
|
|
9915
|
-
return
|
|
8796
|
+
return z28.string().min(1).describe(description);
|
|
9916
8797
|
}
|
|
9917
8798
|
function slackUserIdParam(description) {
|
|
9918
|
-
return
|
|
8799
|
+
return z28.string().min(1).describe(description);
|
|
9919
8800
|
}
|
|
9920
8801
|
function parseRequiredSlackChannelIdParam(field, value) {
|
|
9921
8802
|
const channelId = parseSlackChannelId(value);
|
|
@@ -9939,16 +8820,16 @@ function parseRequiredSlackUserIdParam(field, value) {
|
|
|
9939
8820
|
}
|
|
9940
8821
|
|
|
9941
8822
|
// src/chat/slack/tools/list/add-items.ts
|
|
9942
|
-
import { z as
|
|
8823
|
+
import { z as z29 } from "zod";
|
|
9943
8824
|
function createSlackListAddItemsTool(state) {
|
|
9944
8825
|
return zodTool({
|
|
9945
8826
|
description: "Add tasks to the active Slack list tracked in artifact context. Use when the user wants actionable items recorded in the current thread list. Do not use when no list exists and list creation was not requested.",
|
|
9946
|
-
inputSchema:
|
|
9947
|
-
items:
|
|
8827
|
+
inputSchema: z29.object({
|
|
8828
|
+
items: z29.array(z29.string().min(1)).min(1).max(25).describe("List item titles to create."),
|
|
9948
8829
|
assignee_user_id: slackUserIdParam(
|
|
9949
8830
|
"Optional Slack user ID assigned to all created items."
|
|
9950
8831
|
).optional(),
|
|
9951
|
-
due_date:
|
|
8832
|
+
due_date: z29.string().regex(/^\d{4}-\d{2}-\d{2}$/).describe("Optional due date in YYYY-MM-DD format.").optional()
|
|
9952
8833
|
}),
|
|
9953
8834
|
outputSchema: juniorToolResultSchema,
|
|
9954
8835
|
execute: async ({ items, assignee_user_id, due_date }) => {
|
|
@@ -10002,12 +8883,12 @@ function createSlackListAddItemsTool(state) {
|
|
|
10002
8883
|
}
|
|
10003
8884
|
|
|
10004
8885
|
// src/chat/slack/tools/list/create.ts
|
|
10005
|
-
import { z as
|
|
8886
|
+
import { z as z30 } from "zod";
|
|
10006
8887
|
function createSlackListCreateTool(state) {
|
|
10007
8888
|
return zodTool({
|
|
10008
8889
|
description: "Create a Slack todo list for action tracking. Use when the user needs structured tasks with ownership/completion tracking. Do not use for one-off notes without task management needs.",
|
|
10009
|
-
inputSchema:
|
|
10010
|
-
name:
|
|
8890
|
+
inputSchema: z30.object({
|
|
8891
|
+
name: z30.string().min(1).max(160).describe("Name for the new Slack list.")
|
|
10011
8892
|
}),
|
|
10012
8893
|
outputSchema: juniorToolResultSchema,
|
|
10013
8894
|
execute: async ({ name }) => {
|
|
@@ -10039,13 +8920,13 @@ function createSlackListCreateTool(state) {
|
|
|
10039
8920
|
}
|
|
10040
8921
|
|
|
10041
8922
|
// src/chat/slack/tools/list/get-items.ts
|
|
10042
|
-
import { z as
|
|
8923
|
+
import { z as z31 } from "zod";
|
|
10043
8924
|
function createSlackListGetItemsTool(state) {
|
|
10044
8925
|
return zodTool({
|
|
10045
8926
|
description: "Read items from the active Slack list tracked in artifact context. Use when the user asks for task status, open items, or list contents. Do not use when list state is already known from the immediately prior result.",
|
|
10046
8927
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
10047
|
-
inputSchema:
|
|
10048
|
-
limit:
|
|
8928
|
+
inputSchema: z31.object({
|
|
8929
|
+
limit: z31.coerce.number().int().min(1).max(200).describe("Maximum number of list items to return.").optional()
|
|
10049
8930
|
}),
|
|
10050
8931
|
outputSchema: juniorToolResultSchema,
|
|
10051
8932
|
execute: async ({ limit }) => {
|
|
@@ -10070,21 +8951,21 @@ function createSlackListGetItemsTool(state) {
|
|
|
10070
8951
|
}
|
|
10071
8952
|
|
|
10072
8953
|
// src/chat/slack/tools/list/update-item.ts
|
|
10073
|
-
import { z as
|
|
10074
|
-
var booleanInput3 = (description) =>
|
|
8954
|
+
import { z as z32 } from "zod";
|
|
8955
|
+
var booleanInput3 = (description) => z32.preprocess(
|
|
10075
8956
|
(value) => value === "true" ? true : value === "false" ? false : value,
|
|
10076
|
-
|
|
8957
|
+
z32.boolean()
|
|
10077
8958
|
).describe(description);
|
|
10078
|
-
var updateListItemInputSchema =
|
|
10079
|
-
|
|
10080
|
-
item_id:
|
|
8959
|
+
var updateListItemInputSchema = z32.union([
|
|
8960
|
+
z32.object({
|
|
8961
|
+
item_id: z32.string().min(1).describe("ID of the Slack list item to update."),
|
|
10081
8962
|
completed: booleanInput3("Optional completion status update."),
|
|
10082
|
-
title:
|
|
8963
|
+
title: z32.string().min(1).describe("Optional new item title.").optional()
|
|
10083
8964
|
}),
|
|
10084
|
-
|
|
10085
|
-
item_id:
|
|
8965
|
+
z32.object({
|
|
8966
|
+
item_id: z32.string().min(1).describe("ID of the Slack list item to update."),
|
|
10086
8967
|
completed: booleanInput3("Optional completion status update.").optional(),
|
|
10087
|
-
title:
|
|
8968
|
+
title: z32.string().min(1).describe("Optional new item title.")
|
|
10088
8969
|
})
|
|
10089
8970
|
]);
|
|
10090
8971
|
function createSlackListUpdateItemTool(state) {
|
|
@@ -10160,7 +9041,7 @@ async function checkSlackChannelReadAccess(args) {
|
|
|
10160
9041
|
}
|
|
10161
9042
|
|
|
10162
9043
|
// src/chat/slack/tools/thread-read.ts
|
|
10163
|
-
import { z as
|
|
9044
|
+
import { z as z33 } from "zod";
|
|
10164
9045
|
|
|
10165
9046
|
// src/chat/slack/tools/slack-message-url.ts
|
|
10166
9047
|
var SLACK_HOST_PATTERN = /^[a-z0-9-]+\.slack(?:-gov)?\.com$/;
|
|
@@ -10333,8 +9214,8 @@ function createSlackThreadReadTool(context, deps = {}) {
|
|
|
10333
9214
|
return zodTool({
|
|
10334
9215
|
description: "Read a Slack thread from a shared Slack message archive URL or explicit channel + timestamp. Use when the user shares a Slack message link (https://*.slack.com/archives/...) and you need the referenced message and its thread context. Only the current conversation and public channels Junior has seen in this workspace are readable.",
|
|
10335
9216
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
10336
|
-
inputSchema:
|
|
10337
|
-
url:
|
|
9217
|
+
inputSchema: z33.object({
|
|
9218
|
+
url: z33.string().min(1).describe(
|
|
10338
9219
|
"Slack message archive URL, e.g. https://workspace.slack.com/archives/C123/p1700000000123456"
|
|
10339
9220
|
).optional(),
|
|
10340
9221
|
channel_id: slackChannelIdParam(
|
|
@@ -10343,8 +9224,8 @@ function createSlackThreadReadTool(context, deps = {}) {
|
|
|
10343
9224
|
ts: slackTimestampParam(
|
|
10344
9225
|
"Slack message timestamp (e.g. 1700000000.123456). May be the thread root or any message in the thread."
|
|
10345
9226
|
).optional(),
|
|
10346
|
-
limit:
|
|
10347
|
-
max_pages:
|
|
9227
|
+
limit: z33.coerce.number().int().min(1).max(1e3).describe("Maximum number of thread messages to fetch.").optional(),
|
|
9228
|
+
max_pages: z33.coerce.number().int().min(1).max(10).describe("Maximum number of Slack API pages to traverse.").optional()
|
|
10348
9229
|
}),
|
|
10349
9230
|
outputSchema: juniorToolResultSchema,
|
|
10350
9231
|
execute: async ({ url, channel_id, ts, limit, max_pages }) => {
|
|
@@ -10600,29 +9481,29 @@ async function searchSlackUsers(options) {
|
|
|
10600
9481
|
}
|
|
10601
9482
|
|
|
10602
9483
|
// src/chat/slack/tools/user-lookup.ts
|
|
10603
|
-
import { z as
|
|
10604
|
-
var booleanInput4 = (description) =>
|
|
9484
|
+
import { z as z34 } from "zod";
|
|
9485
|
+
var booleanInput4 = (description) => z34.preprocess(
|
|
10605
9486
|
(value) => value === "true" ? true : value === "false" ? false : value,
|
|
10606
|
-
|
|
9487
|
+
z34.boolean()
|
|
10607
9488
|
).describe(description);
|
|
10608
9489
|
function createSlackUserLookupTool() {
|
|
10609
9490
|
return zodTool({
|
|
10610
9491
|
description: "Look up Slack user profiles by user ID, email, or name search. Use when you need to identify a user, resolve cross-platform identity, or look up profile details like title or status. Returns profile fields including custom fields. For user ID lookup, pass a Slack user ID (e.g. U039RR91S). For search, pass a name query.",
|
|
10611
9492
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
10612
|
-
inputSchema:
|
|
9493
|
+
inputSchema: z34.object({
|
|
10613
9494
|
user_id: slackUserIdParam(
|
|
10614
9495
|
"Slack user ID to look up (e.g. U039RR91S). Mutually exclusive with email and query."
|
|
10615
9496
|
).optional(),
|
|
10616
|
-
email:
|
|
9497
|
+
email: z34.string().min(3).describe(
|
|
10617
9498
|
"Email address to look up. Mutually exclusive with user_id and query."
|
|
10618
9499
|
).optional(),
|
|
10619
|
-
query:
|
|
9500
|
+
query: z34.string().min(2).describe(
|
|
10620
9501
|
"Name to search for (matches against username, display name, real name). Mutually exclusive with user_id and email."
|
|
10621
9502
|
).optional(),
|
|
10622
|
-
limit:
|
|
9503
|
+
limit: z34.coerce.number().int().min(1).max(20).describe(
|
|
10623
9504
|
"Maximum number of results to return for name search. Defaults to 10."
|
|
10624
9505
|
).optional(),
|
|
10625
|
-
max_pages:
|
|
9506
|
+
max_pages: z34.coerce.number().int().min(1).max(5).describe(
|
|
10626
9507
|
"Maximum number of Slack API pages to scan for name search. Defaults to 3."
|
|
10627
9508
|
).optional(),
|
|
10628
9509
|
include_bots: booleanInput4(
|
|
@@ -10726,18 +9607,18 @@ function createSlackUserLookupTool() {
|
|
|
10726
9607
|
}
|
|
10727
9608
|
|
|
10728
9609
|
// src/chat/tools/system-time.ts
|
|
10729
|
-
import { z as
|
|
9610
|
+
import { z as z35 } from "zod";
|
|
10730
9611
|
var systemTimeOutputSchema = juniorToolResultSchema.extend({
|
|
10731
|
-
unix_ms:
|
|
10732
|
-
iso_utc:
|
|
10733
|
-
iso_local:
|
|
10734
|
-
timezone_offset_minutes:
|
|
9612
|
+
unix_ms: z35.number(),
|
|
9613
|
+
iso_utc: z35.string(),
|
|
9614
|
+
iso_local: z35.string(),
|
|
9615
|
+
timezone_offset_minutes: z35.number()
|
|
10735
9616
|
});
|
|
10736
9617
|
function createSystemTimeTool() {
|
|
10737
9618
|
return zodTool({
|
|
10738
9619
|
description: "Return current system time in UTC and local ISO formats. Use when the user asks for current time/date context. Do not use as a substitute for historical or timezone-conversion research.",
|
|
10739
9620
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
10740
|
-
inputSchema:
|
|
9621
|
+
inputSchema: z35.object({}),
|
|
10741
9622
|
outputSchema: systemTimeOutputSchema,
|
|
10742
9623
|
privateTraceResult: (result) => ({
|
|
10743
9624
|
ok: result.ok,
|
|
@@ -10766,10 +9647,10 @@ function createSystemTimeTool() {
|
|
|
10766
9647
|
}
|
|
10767
9648
|
|
|
10768
9649
|
// src/chat/tools/handoff/tool.ts
|
|
10769
|
-
import { z as
|
|
9650
|
+
import { z as z36 } from "zod";
|
|
10770
9651
|
var HANDOFF_TOOL_NAME = "handoff";
|
|
10771
9652
|
function createHandoffTool(handoff) {
|
|
10772
|
-
const profileSchema =
|
|
9653
|
+
const profileSchema = z36.enum(handoff.profiles);
|
|
10773
9654
|
const handoffResultSchema = juniorToolResultSchema.extend({
|
|
10774
9655
|
model_profile: profileSchema
|
|
10775
9656
|
});
|
|
@@ -10777,7 +9658,7 @@ function createHandoffTool(handoff) {
|
|
|
10777
9658
|
return zodTool({
|
|
10778
9659
|
description: `Switch to another execution profile and continue the same task. Profiles: ${profileNames}. Call this as the only tool.`,
|
|
10779
9660
|
executionMode: "sequential",
|
|
10780
|
-
inputSchema:
|
|
9661
|
+
inputSchema: z36.object({
|
|
10781
9662
|
profile: profileSchema.describe("Target execution profile")
|
|
10782
9663
|
}).strict(),
|
|
10783
9664
|
outputSchema: handoffResultSchema,
|
|
@@ -10800,7 +9681,7 @@ function createHandoffTool(handoff) {
|
|
|
10800
9681
|
}
|
|
10801
9682
|
|
|
10802
9683
|
// src/chat/tools/web/fetch-tool.ts
|
|
10803
|
-
import { z as
|
|
9684
|
+
import { z as z37 } from "zod";
|
|
10804
9685
|
|
|
10805
9686
|
// src/chat/tools/web/constants.ts
|
|
10806
9687
|
var USER_AGENT = "junior-bot/0.1";
|
|
@@ -11250,9 +10131,9 @@ function createWebFetchTool(hooks, options = {}) {
|
|
|
11250
10131
|
destructiveHint: false,
|
|
11251
10132
|
openWorldHint: true
|
|
11252
10133
|
},
|
|
11253
|
-
inputSchema:
|
|
11254
|
-
url:
|
|
11255
|
-
max_chars:
|
|
10134
|
+
inputSchema: z37.object({
|
|
10135
|
+
url: z37.string().min(1).describe("HTTP(S) URL to fetch."),
|
|
10136
|
+
max_chars: z37.coerce.number().int().min(500).max(MAX_FETCH_CHARS).describe("Optional maximum number of extracted characters to return.").optional()
|
|
11256
10137
|
}),
|
|
11257
10138
|
outputSchema: juniorToolResultSchema,
|
|
11258
10139
|
execute: async ({ url, max_chars }) => {
|
|
@@ -11313,7 +10194,7 @@ function createWebFetchTool(hooks, options = {}) {
|
|
|
11313
10194
|
}
|
|
11314
10195
|
|
|
11315
10196
|
// src/chat/tools/web/search.ts
|
|
11316
|
-
import { z as
|
|
10197
|
+
import { z as z38 } from "zod";
|
|
11317
10198
|
import { generateText } from "ai";
|
|
11318
10199
|
import { createGatewayProvider } from "@ai-sdk/gateway";
|
|
11319
10200
|
var SEARCH_TIMEOUT_MS = 6e4;
|
|
@@ -11362,9 +10243,9 @@ function createWebSearchTool(modelId, override) {
|
|
|
11362
10243
|
destructiveHint: false,
|
|
11363
10244
|
openWorldHint: true
|
|
11364
10245
|
},
|
|
11365
|
-
inputSchema:
|
|
11366
|
-
query:
|
|
11367
|
-
max_results:
|
|
10246
|
+
inputSchema: z38.object({
|
|
10247
|
+
query: z38.string().min(1).max(500).describe("Search query."),
|
|
10248
|
+
max_results: z38.coerce.number().int().min(1).max(MAX_RESULTS2).describe("Max results to return.").optional()
|
|
11368
10249
|
}),
|
|
11369
10250
|
outputSchema: juniorToolResultSchema,
|
|
11370
10251
|
execute: async ({ query, max_results }) => {
|
|
@@ -11433,14 +10314,14 @@ function createWebSearchTool(modelId, override) {
|
|
|
11433
10314
|
}
|
|
11434
10315
|
|
|
11435
10316
|
// src/chat/tools/sandbox/write-file.ts
|
|
11436
|
-
import { z as
|
|
10317
|
+
import { z as z39 } from "zod";
|
|
11437
10318
|
function createWriteFileTool() {
|
|
11438
10319
|
return zodTool({
|
|
11439
10320
|
description: "Write UTF-8 content to a file in the sandbox workspace. Use for intentional file creation or deliberate full-file replacement after validation; use editFile instead for targeted changes to existing files. Do not use for exploratory analysis-only turns.",
|
|
11440
10321
|
executionMode: "sequential",
|
|
11441
|
-
inputSchema:
|
|
11442
|
-
path:
|
|
11443
|
-
content:
|
|
10322
|
+
inputSchema: z39.object({
|
|
10323
|
+
path: z39.string().min(1).describe("Path to write in the sandbox workspace."),
|
|
10324
|
+
content: z39.string().describe("UTF-8 file content to write.")
|
|
11444
10325
|
}),
|
|
11445
10326
|
outputSchema: juniorToolResultSchema,
|
|
11446
10327
|
execute: async () => {
|
|
@@ -11799,11 +10680,11 @@ function replaceGeneratedTextContent(content, text) {
|
|
|
11799
10680
|
...withoutFirstGeneratedTextContent(content)
|
|
11800
10681
|
];
|
|
11801
10682
|
}
|
|
11802
|
-
function
|
|
10683
|
+
function isRecord3(value) {
|
|
11803
10684
|
return Boolean(value && typeof value === "object" && !Array.isArray(value));
|
|
11804
10685
|
}
|
|
11805
10686
|
function hasContinuation(value) {
|
|
11806
|
-
return
|
|
10687
|
+
return isRecord3(value) && isRecord3(value.continuation);
|
|
11807
10688
|
}
|
|
11808
10689
|
function stringField(record, key) {
|
|
11809
10690
|
const value = record[key];
|
|
@@ -11818,7 +10699,7 @@ function stringListField(record, key) {
|
|
|
11818
10699
|
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
11819
10700
|
}
|
|
11820
10701
|
function accountText(value) {
|
|
11821
|
-
if (!
|
|
10702
|
+
if (!isRecord3(value)) {
|
|
11822
10703
|
return void 0;
|
|
11823
10704
|
}
|
|
11824
10705
|
const label = stringField(value, "label") || stringField(value, "id");
|
|
@@ -11829,7 +10710,7 @@ function accountText(value) {
|
|
|
11829
10710
|
return id && id !== label ? `${label} (${id})` : label;
|
|
11830
10711
|
}
|
|
11831
10712
|
function upstreamPermissionDeniedText(value) {
|
|
11832
|
-
if (!
|
|
10713
|
+
if (!isRecord3(value) || !isRecord3(value.permission_denied)) {
|
|
11833
10714
|
return void 0;
|
|
11834
10715
|
}
|
|
11835
10716
|
const signal = value.permission_denied;
|
|
@@ -11844,7 +10725,7 @@ function upstreamPermissionDeniedText(value) {
|
|
|
11844
10725
|
if (!provider || !message || !upstreamHost || !upstreamPath || !status) {
|
|
11845
10726
|
return void 0;
|
|
11846
10727
|
}
|
|
11847
|
-
const grant =
|
|
10728
|
+
const grant = isRecord3(signal.grant) ? signal.grant : {};
|
|
11848
10729
|
const grantName = stringField(grant, "name");
|
|
11849
10730
|
const grantAccess = stringField(grant, "access");
|
|
11850
10731
|
const grantReason = stringField(grant, "reason");
|
|
@@ -11967,9 +10848,6 @@ function pluginAuthRequiredSignal(details) {
|
|
|
11967
10848
|
...parsedSignal.authorization ? { authorization: parsedSignal.authorization } : {}
|
|
11968
10849
|
};
|
|
11969
10850
|
}
|
|
11970
|
-
function authorizationId(args) {
|
|
11971
|
-
return `${args.sessionId}:${args.kind}:${args.provider}`;
|
|
11972
|
-
}
|
|
11973
10851
|
function createPluginAuthOrchestration(input) {
|
|
11974
10852
|
let pendingPause;
|
|
11975
10853
|
const startAuthorizationPause = async (provider, options) => {
|
|
@@ -12006,7 +10884,8 @@ function createPluginAuthOrchestration(input) {
|
|
|
12006
10884
|
threadTs: input.threadTs,
|
|
12007
10885
|
...options?.scope ? { scope: options.scope } : {},
|
|
12008
10886
|
resumeConversationId: input.conversationId,
|
|
12009
|
-
resumeSessionId: input.sessionId
|
|
10887
|
+
resumeSessionId: input.sessionId,
|
|
10888
|
+
authorization: input.authorization
|
|
12010
10889
|
});
|
|
12011
10890
|
if (!oauthResult.ok) {
|
|
12012
10891
|
throw new Error(oauthResult.error);
|
|
@@ -12184,17 +11063,17 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, trac
|
|
|
12184
11063
|
}
|
|
12185
11064
|
|
|
12186
11065
|
// src/chat/tools/execute-tool.ts
|
|
12187
|
-
import { z as
|
|
11066
|
+
import { z as z40 } from "zod";
|
|
12188
11067
|
var EXECUTE_TOOL_NAME = "executeTool";
|
|
12189
11068
|
function createExecuteToolTool() {
|
|
12190
11069
|
return zodTool({
|
|
12191
11070
|
description: "Execute any catalog tool by exact tool_name from searchTools or a tool-result execution hint. Put tool-specific parameters inside arguments.",
|
|
12192
11071
|
executionMode: "sequential",
|
|
12193
|
-
inputSchema:
|
|
12194
|
-
tool_name:
|
|
11072
|
+
inputSchema: z40.object({
|
|
11073
|
+
tool_name: z40.string().min(1).describe(
|
|
12195
11074
|
"Exact catalog tool_name returned by searchTools or a tool-result execution hint."
|
|
12196
11075
|
),
|
|
12197
|
-
arguments:
|
|
11076
|
+
arguments: z40.record(z40.string(), z40.unknown()).describe(
|
|
12198
11077
|
'Arguments matching the selected catalog tool schema, for example { "query": "..." }.'
|
|
12199
11078
|
).optional()
|
|
12200
11079
|
}).strict(),
|
|
@@ -12234,42 +11113,42 @@ function planToolExposure(tools) {
|
|
|
12234
11113
|
}
|
|
12235
11114
|
|
|
12236
11115
|
// src/chat/tools/search-tools.ts
|
|
12237
|
-
import { z as
|
|
11116
|
+
import { z as z41 } from "zod";
|
|
12238
11117
|
var SEARCH_TOOLS_NAME = "searchTools";
|
|
12239
11118
|
var DEFAULT_MAX_RESULTS2 = 5;
|
|
12240
11119
|
var MAX_RESULTS3 = 20;
|
|
12241
11120
|
var MODEL_VISIBLE_DESCRIPTION_CAP = 180;
|
|
12242
|
-
var searchToolsSourceSchema =
|
|
12243
|
-
id:
|
|
12244
|
-
description:
|
|
11121
|
+
var searchToolsSourceSchema = z41.object({
|
|
11122
|
+
id: z41.string(),
|
|
11123
|
+
description: z41.string()
|
|
12245
11124
|
}).strict();
|
|
12246
|
-
var toolCallExampleSchema =
|
|
12247
|
-
tool_name:
|
|
12248
|
-
arguments:
|
|
11125
|
+
var toolCallExampleSchema = z41.object({
|
|
11126
|
+
tool_name: z41.string(),
|
|
11127
|
+
arguments: z41.record(z41.string(), z41.string())
|
|
12249
11128
|
}).strict();
|
|
12250
|
-
var searchToolsToolSchema =
|
|
12251
|
-
tool_name:
|
|
12252
|
-
description:
|
|
12253
|
-
exposure:
|
|
12254
|
-
source:
|
|
12255
|
-
signature:
|
|
11129
|
+
var searchToolsToolSchema = z41.object({
|
|
11130
|
+
tool_name: z41.string(),
|
|
11131
|
+
description: z41.string(),
|
|
11132
|
+
exposure: z41.enum(["direct", "deferred", "modelOnly", "hidden"]),
|
|
11133
|
+
source: z41.string().optional(),
|
|
11134
|
+
signature: z41.string(),
|
|
12256
11135
|
call: toolCallExampleSchema,
|
|
12257
|
-
input_schema:
|
|
12258
|
-
input_schema_summary:
|
|
12259
|
-
call_notes:
|
|
12260
|
-
annotations:
|
|
11136
|
+
input_schema: z41.unknown(),
|
|
11137
|
+
input_schema_summary: z41.string(),
|
|
11138
|
+
call_notes: z41.array(z41.string()),
|
|
11139
|
+
annotations: z41.record(z41.string(), z41.unknown())
|
|
12261
11140
|
}).strict();
|
|
12262
11141
|
var searchToolsOutputSchema = juniorToolResultSchema.extend({
|
|
12263
|
-
query:
|
|
12264
|
-
source:
|
|
12265
|
-
sources:
|
|
12266
|
-
total_catalog_tools:
|
|
12267
|
-
total_eligible_tools:
|
|
12268
|
-
total_matches:
|
|
12269
|
-
returned_tools:
|
|
12270
|
-
execution_tool:
|
|
11142
|
+
query: z41.string().nullable(),
|
|
11143
|
+
source: z41.string().nullable(),
|
|
11144
|
+
sources: z41.array(searchToolsSourceSchema),
|
|
11145
|
+
total_catalog_tools: z41.number().int().nonnegative(),
|
|
11146
|
+
total_eligible_tools: z41.number().int().nonnegative(),
|
|
11147
|
+
total_matches: z41.number().int().nonnegative(),
|
|
11148
|
+
returned_tools: z41.number().int().nonnegative(),
|
|
11149
|
+
execution_tool: z41.literal("executeTool"),
|
|
12271
11150
|
execution_example: toolCallExampleSchema,
|
|
12272
|
-
tools:
|
|
11151
|
+
tools: z41.array(searchToolsToolSchema)
|
|
12273
11152
|
}).strict();
|
|
12274
11153
|
function normalize2(value) {
|
|
12275
11154
|
return value.toLowerCase().replace(/[^a-z0-9_]+/g, " ").trim();
|
|
@@ -12461,14 +11340,14 @@ function createSearchToolsTool(catalogTools) {
|
|
|
12461
11340
|
return zodTool({
|
|
12462
11341
|
description: renderSearchToolsDescription(knownSources),
|
|
12463
11342
|
annotations: { readOnlyHint: true, destructiveHint: false },
|
|
12464
|
-
inputSchema:
|
|
12465
|
-
query:
|
|
11343
|
+
inputSchema: z41.object({
|
|
11344
|
+
query: z41.string().nullable().describe(
|
|
12466
11345
|
"Optional search terms describing the tool, owner, action, or arguments needed. Empty string lists catalog tools."
|
|
12467
11346
|
).optional(),
|
|
12468
|
-
source:
|
|
11347
|
+
source: z41.string().nullable().describe(
|
|
12469
11348
|
"Optional source id to search within, such as a plugin source returned in sources."
|
|
12470
11349
|
).optional(),
|
|
12471
|
-
max_results:
|
|
11350
|
+
max_results: z41.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
|
|
12472
11351
|
}).strict(),
|
|
12473
11352
|
outputSchema: searchToolsOutputSchema,
|
|
12474
11353
|
privateTraceResult: (result) => ({
|
|
@@ -12754,542 +11633,12 @@ function createPiAgentTools(tools, sandbox, spanContext, onStatus, sandboxTools,
|
|
|
12754
11633
|
}));
|
|
12755
11634
|
}
|
|
12756
11635
|
|
|
12757
|
-
// src/chat/mcp/oauth.ts
|
|
12758
|
-
import { randomUUID as randomUUID3 } from "crypto";
|
|
12759
|
-
import { StreamableHTTPClientTransport as StreamableHTTPClientTransport2 } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
|
|
12760
|
-
|
|
12761
|
-
// src/chat/mcp/auth-store.ts
|
|
12762
|
-
import {
|
|
12763
|
-
sourceSchema as sourceSchema2
|
|
12764
|
-
} from "@sentry/junior-plugin-api";
|
|
12765
|
-
var MCP_AUTH_SESSION_PREFIX = "junior:mcp_oauth_attempt:v2";
|
|
12766
|
-
var MCP_AUTH_CREDENTIALS_PREFIX = "junior:mcp_auth_credentials";
|
|
12767
|
-
var MCP_AUTH_SESSION_INDEX_PREFIX = "junior:mcp_oauth_attempt_index:v2";
|
|
12768
|
-
var MCP_SERVER_SESSION_PREFIX = "junior:mcp_server_session";
|
|
12769
|
-
var MCP_AUTH_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
12770
|
-
var MCP_AUTH_CREDENTIALS_TTL_MS = 30 * 24 * 60 * 60 * 1e3;
|
|
12771
|
-
var MCP_SERVER_SESSION_TTL_MS = 24 * 60 * 60 * 1e3;
|
|
12772
|
-
function sessionKey(authSessionId) {
|
|
12773
|
-
return `${MCP_AUTH_SESSION_PREFIX}:${authSessionId}`;
|
|
12774
|
-
}
|
|
12775
|
-
function credentialsKey(userId, provider) {
|
|
12776
|
-
return `${MCP_AUTH_CREDENTIALS_PREFIX}:${userId}:${provider}`;
|
|
12777
|
-
}
|
|
12778
|
-
function sessionIndexKey(userId, provider) {
|
|
12779
|
-
return `${MCP_AUTH_SESSION_INDEX_PREFIX}:${userId}:${provider}`;
|
|
12780
|
-
}
|
|
12781
|
-
function serverSessionKey(userId, provider) {
|
|
12782
|
-
return `${MCP_SERVER_SESSION_PREFIX}:${userId}:${provider}`;
|
|
12783
|
-
}
|
|
12784
|
-
function parseSessionIndex(value) {
|
|
12785
|
-
if (typeof value !== "string") {
|
|
12786
|
-
return [];
|
|
12787
|
-
}
|
|
12788
|
-
try {
|
|
12789
|
-
const parsed = JSON.parse(value);
|
|
12790
|
-
if (!Array.isArray(parsed)) {
|
|
12791
|
-
return [];
|
|
12792
|
-
}
|
|
12793
|
-
return [
|
|
12794
|
-
...new Set(parsed.filter((id) => typeof id === "string"))
|
|
12795
|
-
];
|
|
12796
|
-
} catch {
|
|
12797
|
-
return [];
|
|
12798
|
-
}
|
|
12799
|
-
}
|
|
12800
|
-
function parseMcpAuthSession(value) {
|
|
12801
|
-
if (typeof value !== "string") {
|
|
12802
|
-
return void 0;
|
|
12803
|
-
}
|
|
12804
|
-
try {
|
|
12805
|
-
const parsed = JSON.parse(value);
|
|
12806
|
-
if (!isRecord(parsed)) {
|
|
12807
|
-
return void 0;
|
|
12808
|
-
}
|
|
12809
|
-
if (parsed.schemaVersion !== 2 || typeof parsed.authSessionId !== "string" || typeof parsed.provider !== "string" || typeof parsed.userId !== "string" || typeof parsed.conversationId !== "string" || typeof parsed.sessionId !== "string" || typeof parsed.userMessage !== "string" || typeof parsed.createdAtMs !== "number" || typeof parsed.updatedAtMs !== "number") {
|
|
12810
|
-
return void 0;
|
|
12811
|
-
}
|
|
12812
|
-
const destination = parsed.destination === void 0 ? void 0 : parseDestination(parsed.destination);
|
|
12813
|
-
if (parsed.destination !== void 0 && !destination) {
|
|
12814
|
-
return void 0;
|
|
12815
|
-
}
|
|
12816
|
-
const source = parsed.source === void 0 ? void 0 : sourceSchema2.safeParse(parsed.source);
|
|
12817
|
-
if (parsed.source !== void 0 && (!source || !source.success)) {
|
|
12818
|
-
return void 0;
|
|
12819
|
-
}
|
|
12820
|
-
return {
|
|
12821
|
-
schemaVersion: 2,
|
|
12822
|
-
authSessionId: parsed.authSessionId,
|
|
12823
|
-
provider: parsed.provider,
|
|
12824
|
-
userId: parsed.userId,
|
|
12825
|
-
conversationId: parsed.conversationId,
|
|
12826
|
-
...destination ? { destination } : {},
|
|
12827
|
-
...source?.success ? { source: source.data } : {},
|
|
12828
|
-
sessionId: parsed.sessionId,
|
|
12829
|
-
userMessage: parsed.userMessage,
|
|
12830
|
-
createdAtMs: parsed.createdAtMs,
|
|
12831
|
-
updatedAtMs: parsed.updatedAtMs,
|
|
12832
|
-
...typeof parsed.channelId === "string" ? { channelId: parsed.channelId } : {},
|
|
12833
|
-
...typeof parsed.threadTs === "string" ? { threadTs: parsed.threadTs } : {},
|
|
12834
|
-
...typeof parsed.toolChannelId === "string" ? { toolChannelId: parsed.toolChannelId } : {},
|
|
12835
|
-
...isRecord(parsed.configuration) ? { configuration: parsed.configuration } : {},
|
|
12836
|
-
...isRecord(parsed.artifactState) ? { artifactState: parsed.artifactState } : {},
|
|
12837
|
-
...typeof parsed.authorizationUrl === "string" ? { authorizationUrl: parsed.authorizationUrl } : {},
|
|
12838
|
-
...typeof parsed.codeVerifier === "string" ? { codeVerifier: parsed.codeVerifier } : {}
|
|
12839
|
-
};
|
|
12840
|
-
} catch {
|
|
12841
|
-
return void 0;
|
|
12842
|
-
}
|
|
12843
|
-
}
|
|
12844
|
-
function parseServerSession(value) {
|
|
12845
|
-
if (typeof value !== "string") {
|
|
12846
|
-
return void 0;
|
|
12847
|
-
}
|
|
12848
|
-
try {
|
|
12849
|
-
const parsed = JSON.parse(value);
|
|
12850
|
-
if (!isRecord(parsed) || typeof parsed.sessionId !== "string" || typeof parsed.updatedAtMs !== "number") {
|
|
12851
|
-
return void 0;
|
|
12852
|
-
}
|
|
12853
|
-
return {
|
|
12854
|
-
sessionId: parsed.sessionId,
|
|
12855
|
-
updatedAtMs: parsed.updatedAtMs
|
|
12856
|
-
};
|
|
12857
|
-
} catch {
|
|
12858
|
-
return void 0;
|
|
12859
|
-
}
|
|
12860
|
-
}
|
|
12861
|
-
function parseStoredCredentials(value) {
|
|
12862
|
-
if (typeof value !== "string") {
|
|
12863
|
-
return void 0;
|
|
12864
|
-
}
|
|
12865
|
-
try {
|
|
12866
|
-
const parsed = JSON.parse(value);
|
|
12867
|
-
if (!isRecord(parsed)) {
|
|
12868
|
-
return void 0;
|
|
12869
|
-
}
|
|
12870
|
-
return {
|
|
12871
|
-
...isRecord(parsed.clientInformation) ? {
|
|
12872
|
-
clientInformation: parsed.clientInformation
|
|
12873
|
-
} : {},
|
|
12874
|
-
...isRecord(parsed.discoveryState) ? {
|
|
12875
|
-
discoveryState: parsed.discoveryState
|
|
12876
|
-
} : {},
|
|
12877
|
-
...isRecord(parsed.tokens) ? { tokens: parsed.tokens } : {}
|
|
12878
|
-
};
|
|
12879
|
-
} catch {
|
|
12880
|
-
return void 0;
|
|
12881
|
-
}
|
|
12882
|
-
}
|
|
12883
|
-
async function getConnectedStateAdapter() {
|
|
12884
|
-
const stateAdapter = getStateAdapter();
|
|
12885
|
-
await stateAdapter.connect();
|
|
12886
|
-
return stateAdapter;
|
|
12887
|
-
}
|
|
12888
|
-
async function getMcpAuthSession(authSessionId) {
|
|
12889
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
12890
|
-
return parseMcpAuthSession(await stateAdapter.get(sessionKey(authSessionId)));
|
|
12891
|
-
}
|
|
12892
|
-
async function putMcpAuthSession(session, ttlMs2 = MCP_AUTH_SESSION_TTL_MS) {
|
|
12893
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
12894
|
-
await stateAdapter.set(
|
|
12895
|
-
sessionKey(session.authSessionId),
|
|
12896
|
-
JSON.stringify(session),
|
|
12897
|
-
ttlMs2
|
|
12898
|
-
);
|
|
12899
|
-
const nextIndex = parseSessionIndex(
|
|
12900
|
-
await stateAdapter.get(sessionIndexKey(session.userId, session.provider))
|
|
12901
|
-
);
|
|
12902
|
-
if (!nextIndex.includes(session.authSessionId)) {
|
|
12903
|
-
nextIndex.push(session.authSessionId);
|
|
12904
|
-
}
|
|
12905
|
-
await stateAdapter.set(
|
|
12906
|
-
sessionIndexKey(session.userId, session.provider),
|
|
12907
|
-
JSON.stringify(nextIndex),
|
|
12908
|
-
ttlMs2
|
|
12909
|
-
);
|
|
12910
|
-
}
|
|
12911
|
-
async function patchMcpAuthSession(authSessionId, patch) {
|
|
12912
|
-
const current = await getMcpAuthSession(authSessionId);
|
|
12913
|
-
if (!current) {
|
|
12914
|
-
throw new Error(`Unknown MCP auth session: ${authSessionId}`);
|
|
12915
|
-
}
|
|
12916
|
-
const next = {
|
|
12917
|
-
...current,
|
|
12918
|
-
...patch,
|
|
12919
|
-
schemaVersion: 2,
|
|
12920
|
-
authSessionId: current.authSessionId,
|
|
12921
|
-
provider: current.provider,
|
|
12922
|
-
userId: current.userId,
|
|
12923
|
-
conversationId: current.conversationId,
|
|
12924
|
-
...current.destination ? { destination: current.destination } : {},
|
|
12925
|
-
sessionId: current.sessionId,
|
|
12926
|
-
userMessage: current.userMessage,
|
|
12927
|
-
createdAtMs: current.createdAtMs,
|
|
12928
|
-
updatedAtMs: Date.now()
|
|
12929
|
-
};
|
|
12930
|
-
await putMcpAuthSession(next);
|
|
12931
|
-
return next;
|
|
12932
|
-
}
|
|
12933
|
-
async function deleteMcpAuthSession(authSessionId) {
|
|
12934
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
12935
|
-
const current = parseMcpAuthSession(
|
|
12936
|
-
await stateAdapter.get(sessionKey(authSessionId))
|
|
12937
|
-
);
|
|
12938
|
-
await stateAdapter.delete(sessionKey(authSessionId));
|
|
12939
|
-
if (!current) {
|
|
12940
|
-
return;
|
|
12941
|
-
}
|
|
12942
|
-
const nextIndex = parseSessionIndex(
|
|
12943
|
-
await stateAdapter.get(sessionIndexKey(current.userId, current.provider))
|
|
12944
|
-
).filter((id) => id !== authSessionId);
|
|
12945
|
-
if (nextIndex.length > 0) {
|
|
12946
|
-
await stateAdapter.set(
|
|
12947
|
-
sessionIndexKey(current.userId, current.provider),
|
|
12948
|
-
JSON.stringify(nextIndex),
|
|
12949
|
-
MCP_AUTH_SESSION_TTL_MS
|
|
12950
|
-
);
|
|
12951
|
-
return;
|
|
12952
|
-
}
|
|
12953
|
-
await stateAdapter.delete(sessionIndexKey(current.userId, current.provider));
|
|
12954
|
-
}
|
|
12955
|
-
async function deleteMcpAuthSessionsForUserProvider(userId, provider) {
|
|
12956
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
12957
|
-
const indexKey = sessionIndexKey(userId, provider);
|
|
12958
|
-
const authSessionIds = parseSessionIndex(await stateAdapter.get(indexKey));
|
|
12959
|
-
for (const authSessionId of authSessionIds) {
|
|
12960
|
-
await stateAdapter.delete(sessionKey(authSessionId));
|
|
12961
|
-
}
|
|
12962
|
-
await stateAdapter.delete(indexKey);
|
|
12963
|
-
}
|
|
12964
|
-
async function getMcpStoredOAuthCredentials(userId, provider) {
|
|
12965
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
12966
|
-
return parseStoredCredentials(
|
|
12967
|
-
await stateAdapter.get(credentialsKey(userId, provider))
|
|
12968
|
-
);
|
|
12969
|
-
}
|
|
12970
|
-
async function putMcpStoredOAuthCredentials(userId, provider, value, ttlMs2 = MCP_AUTH_CREDENTIALS_TTL_MS) {
|
|
12971
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
12972
|
-
await stateAdapter.set(
|
|
12973
|
-
credentialsKey(userId, provider),
|
|
12974
|
-
JSON.stringify(value),
|
|
12975
|
-
ttlMs2
|
|
12976
|
-
);
|
|
12977
|
-
}
|
|
12978
|
-
async function deleteMcpStoredOAuthCredentials(userId, provider) {
|
|
12979
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
12980
|
-
await stateAdapter.delete(credentialsKey(userId, provider));
|
|
12981
|
-
}
|
|
12982
|
-
async function getMcpServerSessionId(userId, provider) {
|
|
12983
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
12984
|
-
return parseServerSession(
|
|
12985
|
-
await stateAdapter.get(serverSessionKey(userId, provider))
|
|
12986
|
-
)?.sessionId;
|
|
12987
|
-
}
|
|
12988
|
-
async function putMcpServerSessionId(userId, provider, sessionId, ttlMs2 = MCP_SERVER_SESSION_TTL_MS) {
|
|
12989
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
12990
|
-
await stateAdapter.set(
|
|
12991
|
-
serverSessionKey(userId, provider),
|
|
12992
|
-
JSON.stringify({
|
|
12993
|
-
sessionId,
|
|
12994
|
-
updatedAtMs: Date.now()
|
|
12995
|
-
}),
|
|
12996
|
-
ttlMs2
|
|
12997
|
-
);
|
|
12998
|
-
}
|
|
12999
|
-
async function deleteMcpServerSessionId(userId, provider) {
|
|
13000
|
-
const stateAdapter = await getConnectedStateAdapter();
|
|
13001
|
-
await stateAdapter.delete(serverSessionKey(userId, provider));
|
|
13002
|
-
}
|
|
13003
|
-
|
|
13004
|
-
// src/chat/mcp/oauth-provider.ts
|
|
13005
|
-
function createClientMetadata(callbackUrl) {
|
|
13006
|
-
return {
|
|
13007
|
-
client_name: "Junior MCP Client",
|
|
13008
|
-
redirect_uris: [callbackUrl],
|
|
13009
|
-
grant_types: ["authorization_code", "refresh_token"],
|
|
13010
|
-
response_types: ["code"],
|
|
13011
|
-
token_endpoint_auth_method: "none"
|
|
13012
|
-
};
|
|
13013
|
-
}
|
|
13014
|
-
function clientAllowsRedirectUri(clientInformation, callbackUrl) {
|
|
13015
|
-
const redirectUris = clientInformation.redirect_uris;
|
|
13016
|
-
if (!Array.isArray(redirectUris)) {
|
|
13017
|
-
return true;
|
|
13018
|
-
}
|
|
13019
|
-
return redirectUris.includes(callbackUrl);
|
|
13020
|
-
}
|
|
13021
|
-
var StateBackedMcpOAuthClientProvider = class {
|
|
13022
|
-
constructor(authSessionId, callbackUrl, sessionContext, runCredentialMutation) {
|
|
13023
|
-
this.authSessionId = authSessionId;
|
|
13024
|
-
this.callbackUrl = callbackUrl;
|
|
13025
|
-
this.sessionContext = sessionContext;
|
|
13026
|
-
this.runCredentialMutation = runCredentialMutation;
|
|
13027
|
-
this.clientMetadata = createClientMetadata(callbackUrl);
|
|
13028
|
-
}
|
|
13029
|
-
authSessionId;
|
|
13030
|
-
callbackUrl;
|
|
13031
|
-
sessionContext;
|
|
13032
|
-
runCredentialMutation;
|
|
13033
|
-
clientMetadata;
|
|
13034
|
-
get redirectUrl() {
|
|
13035
|
-
return this.callbackUrl;
|
|
13036
|
-
}
|
|
13037
|
-
async state() {
|
|
13038
|
-
return this.authSessionId;
|
|
13039
|
-
}
|
|
13040
|
-
async clientInformation() {
|
|
13041
|
-
const session = await this.getCredentialContext();
|
|
13042
|
-
const credentials = await getMcpStoredOAuthCredentials(
|
|
13043
|
-
session.userId,
|
|
13044
|
-
session.provider
|
|
13045
|
-
);
|
|
13046
|
-
const clientInformation = credentials?.clientInformation;
|
|
13047
|
-
if (!clientInformation) {
|
|
13048
|
-
return void 0;
|
|
13049
|
-
}
|
|
13050
|
-
if (clientAllowsRedirectUri(clientInformation, this.callbackUrl)) {
|
|
13051
|
-
return clientInformation;
|
|
13052
|
-
}
|
|
13053
|
-
return void 0;
|
|
13054
|
-
}
|
|
13055
|
-
async saveClientInformation(clientInformation) {
|
|
13056
|
-
await this.mutateCredentials(async () => {
|
|
13057
|
-
const session = await this.getCredentialContext();
|
|
13058
|
-
const credentials = await getMcpStoredOAuthCredentials(
|
|
13059
|
-
session.userId,
|
|
13060
|
-
session.provider
|
|
13061
|
-
) ?? {};
|
|
13062
|
-
await putMcpStoredOAuthCredentials(session.userId, session.provider, {
|
|
13063
|
-
...credentials,
|
|
13064
|
-
clientInformation
|
|
13065
|
-
});
|
|
13066
|
-
});
|
|
13067
|
-
}
|
|
13068
|
-
async tokens() {
|
|
13069
|
-
const session = await this.getCredentialContext();
|
|
13070
|
-
const credentials = await getMcpStoredOAuthCredentials(
|
|
13071
|
-
session.userId,
|
|
13072
|
-
session.provider
|
|
13073
|
-
);
|
|
13074
|
-
return credentials?.tokens;
|
|
13075
|
-
}
|
|
13076
|
-
async saveTokens(tokens) {
|
|
13077
|
-
await this.mutateCredentials(async () => {
|
|
13078
|
-
const session = await this.getCredentialContext();
|
|
13079
|
-
const credentials = await getMcpStoredOAuthCredentials(
|
|
13080
|
-
session.userId,
|
|
13081
|
-
session.provider
|
|
13082
|
-
) ?? {};
|
|
13083
|
-
await putMcpStoredOAuthCredentials(session.userId, session.provider, {
|
|
13084
|
-
...credentials,
|
|
13085
|
-
tokens
|
|
13086
|
-
});
|
|
13087
|
-
});
|
|
13088
|
-
}
|
|
13089
|
-
async redirectToAuthorization(authorizationUrl) {
|
|
13090
|
-
const existing = await getMcpAuthSession(this.authSessionId);
|
|
13091
|
-
if (existing?.authorizationUrl && existing.authorizationUrl !== authorizationUrl.toString()) {
|
|
13092
|
-
throw new Error("MCP OAuth authorization attempt is already initialized");
|
|
13093
|
-
}
|
|
13094
|
-
await this.ensureSession({
|
|
13095
|
-
authorizationUrl: authorizationUrl.toString()
|
|
13096
|
-
});
|
|
13097
|
-
}
|
|
13098
|
-
async saveCodeVerifier(codeVerifier) {
|
|
13099
|
-
const existing = await getMcpAuthSession(this.authSessionId);
|
|
13100
|
-
if (existing?.codeVerifier && existing.codeVerifier !== codeVerifier) {
|
|
13101
|
-
throw new Error("MCP OAuth authorization attempt is already initialized");
|
|
13102
|
-
}
|
|
13103
|
-
await this.ensureSession({ codeVerifier });
|
|
13104
|
-
}
|
|
13105
|
-
async codeVerifier() {
|
|
13106
|
-
const session = await this.requireSession();
|
|
13107
|
-
if (!session.codeVerifier) {
|
|
13108
|
-
throw new Error("Missing MCP OAuth code verifier");
|
|
13109
|
-
}
|
|
13110
|
-
return session.codeVerifier;
|
|
13111
|
-
}
|
|
13112
|
-
async saveDiscoveryState(state) {
|
|
13113
|
-
await this.mutateCredentials(async () => {
|
|
13114
|
-
const session = await this.getCredentialContext();
|
|
13115
|
-
const credentials = await getMcpStoredOAuthCredentials(
|
|
13116
|
-
session.userId,
|
|
13117
|
-
session.provider
|
|
13118
|
-
) ?? {};
|
|
13119
|
-
await putMcpStoredOAuthCredentials(session.userId, session.provider, {
|
|
13120
|
-
...credentials,
|
|
13121
|
-
discoveryState: state
|
|
13122
|
-
});
|
|
13123
|
-
});
|
|
13124
|
-
}
|
|
13125
|
-
async discoveryState() {
|
|
13126
|
-
const session = await this.getCredentialContext();
|
|
13127
|
-
const credentials = await getMcpStoredOAuthCredentials(
|
|
13128
|
-
session.userId,
|
|
13129
|
-
session.provider
|
|
13130
|
-
);
|
|
13131
|
-
return credentials?.discoveryState;
|
|
13132
|
-
}
|
|
13133
|
-
async invalidateCredentials(scope) {
|
|
13134
|
-
await this.mutateCredentials(async () => {
|
|
13135
|
-
const session = await this.getCredentialContext();
|
|
13136
|
-
const credentials = await getMcpStoredOAuthCredentials(
|
|
13137
|
-
session.userId,
|
|
13138
|
-
session.provider
|
|
13139
|
-
) ?? {};
|
|
13140
|
-
await putMcpStoredOAuthCredentials(session.userId, session.provider, {
|
|
13141
|
-
...scope === "tokens" || scope === "all" ? {} : credentials.tokens ? { tokens: credentials.tokens } : {},
|
|
13142
|
-
...scope === "client" || scope === "all" ? {} : credentials.clientInformation ? { clientInformation: credentials.clientInformation } : {},
|
|
13143
|
-
...scope === "discovery" || scope === "all" ? {} : credentials.discoveryState ? { discoveryState: credentials.discoveryState } : {}
|
|
13144
|
-
});
|
|
13145
|
-
});
|
|
13146
|
-
}
|
|
13147
|
-
async getMcpServerSessionId() {
|
|
13148
|
-
const session = await this.getCredentialContext();
|
|
13149
|
-
return await getMcpServerSessionId(session.userId, session.provider);
|
|
13150
|
-
}
|
|
13151
|
-
async saveMcpServerSessionId(sessionId) {
|
|
13152
|
-
await this.mutateCredentials(async () => {
|
|
13153
|
-
const session = await this.getCredentialContext();
|
|
13154
|
-
if (!sessionId) {
|
|
13155
|
-
await deleteMcpServerSessionId(session.userId, session.provider);
|
|
13156
|
-
return;
|
|
13157
|
-
}
|
|
13158
|
-
await putMcpServerSessionId(session.userId, session.provider, sessionId);
|
|
13159
|
-
});
|
|
13160
|
-
}
|
|
13161
|
-
/** Route shared credential writes through callback-owned freshness control. */
|
|
13162
|
-
async mutateCredentials(mutation) {
|
|
13163
|
-
return this.runCredentialMutation ? await this.runCredentialMutation(mutation) : await mutation();
|
|
13164
|
-
}
|
|
13165
|
-
async getCredentialContext() {
|
|
13166
|
-
return this.sessionContext ?? await this.requireSession();
|
|
13167
|
-
}
|
|
13168
|
-
async ensureSession(patch) {
|
|
13169
|
-
const existing = await getMcpAuthSession(this.authSessionId);
|
|
13170
|
-
if (existing) {
|
|
13171
|
-
return await patchMcpAuthSession(this.authSessionId, patch);
|
|
13172
|
-
}
|
|
13173
|
-
if (!this.sessionContext) {
|
|
13174
|
-
throw new Error(`Unknown MCP auth session: ${this.authSessionId}`);
|
|
13175
|
-
}
|
|
13176
|
-
const now = Date.now();
|
|
13177
|
-
const nextSession = {
|
|
13178
|
-
schemaVersion: 2,
|
|
13179
|
-
authSessionId: this.authSessionId,
|
|
13180
|
-
...this.sessionContext,
|
|
13181
|
-
...patch,
|
|
13182
|
-
createdAtMs: now,
|
|
13183
|
-
updatedAtMs: now
|
|
13184
|
-
};
|
|
13185
|
-
await putMcpAuthSession(nextSession);
|
|
13186
|
-
return nextSession;
|
|
13187
|
-
}
|
|
13188
|
-
async requireSession() {
|
|
13189
|
-
const session = await getMcpAuthSession(this.authSessionId);
|
|
13190
|
-
if (!session) {
|
|
13191
|
-
throw new Error(`Unknown MCP auth session: ${this.authSessionId}`);
|
|
13192
|
-
}
|
|
13193
|
-
return session;
|
|
13194
|
-
}
|
|
13195
|
-
};
|
|
13196
|
-
|
|
13197
|
-
// src/chat/mcp/oauth.ts
|
|
13198
|
-
function getMcpOAuthCallbackPath(provider) {
|
|
13199
|
-
return `/api/oauth/callback/mcp/${provider}`;
|
|
13200
|
-
}
|
|
13201
|
-
function requirePluginWithMcp(provider) {
|
|
13202
|
-
const plugin = pluginCatalogRuntime.getDefinition(provider);
|
|
13203
|
-
if (!plugin?.manifest.mcp) {
|
|
13204
|
-
throw new Error(`Plugin "${provider}" does not support MCP`);
|
|
13205
|
-
}
|
|
13206
|
-
return plugin;
|
|
13207
|
-
}
|
|
13208
|
-
async function createMcpOAuthClientProvider(input) {
|
|
13209
|
-
requirePluginWithMcp(input.provider);
|
|
13210
|
-
const baseUrl = resolveBaseUrl();
|
|
13211
|
-
if (!baseUrl) {
|
|
13212
|
-
throw new Error(
|
|
13213
|
-
"Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)"
|
|
13214
|
-
);
|
|
13215
|
-
}
|
|
13216
|
-
const authSessionId = randomUUID3();
|
|
13217
|
-
return new StateBackedMcpOAuthClientProvider(
|
|
13218
|
-
authSessionId,
|
|
13219
|
-
`${baseUrl}${getMcpOAuthCallbackPath(input.provider)}`,
|
|
13220
|
-
{
|
|
13221
|
-
provider: input.provider,
|
|
13222
|
-
userId: input.userId,
|
|
13223
|
-
conversationId: input.conversationId,
|
|
13224
|
-
...input.destination ? { destination: input.destination } : {},
|
|
13225
|
-
...input.source ? { source: input.source } : {},
|
|
13226
|
-
sessionId: input.sessionId,
|
|
13227
|
-
userMessage: input.userMessage,
|
|
13228
|
-
...input.channelId ? { channelId: input.channelId } : {},
|
|
13229
|
-
...input.threadTs ? { threadTs: input.threadTs } : {},
|
|
13230
|
-
...input.toolChannelId ? { toolChannelId: input.toolChannelId } : {},
|
|
13231
|
-
...input.configuration ? { configuration: input.configuration } : {},
|
|
13232
|
-
...input.artifactState ? { artifactState: input.artifactState } : {}
|
|
13233
|
-
}
|
|
13234
|
-
);
|
|
13235
|
-
}
|
|
13236
|
-
async function finalizeMcpAuthorization(provider, authSessionId, authorizationCode, runCredentialMutation) {
|
|
13237
|
-
const plugin = requirePluginWithMcp(provider);
|
|
13238
|
-
const mcp = plugin.manifest.mcp;
|
|
13239
|
-
if (!mcp) {
|
|
13240
|
-
throw new Error(`Plugin "${provider}" does not support MCP`);
|
|
13241
|
-
}
|
|
13242
|
-
const session = await getMcpAuthSession(authSessionId);
|
|
13243
|
-
if (!session) {
|
|
13244
|
-
throw new Error(`Unknown MCP auth session: ${authSessionId}`);
|
|
13245
|
-
}
|
|
13246
|
-
if (session.provider !== provider) {
|
|
13247
|
-
throw new Error(
|
|
13248
|
-
`MCP auth session provider mismatch: expected "${provider}", got "${session.provider}"`
|
|
13249
|
-
);
|
|
13250
|
-
}
|
|
13251
|
-
const baseUrl = resolveBaseUrl();
|
|
13252
|
-
if (!baseUrl) {
|
|
13253
|
-
throw new Error(
|
|
13254
|
-
"Cannot determine base URL (set JUNIOR_BASE_URL or deploy to Vercel)"
|
|
13255
|
-
);
|
|
13256
|
-
}
|
|
13257
|
-
const callbackUrl = `${baseUrl}${getMcpOAuthCallbackPath(provider)}`;
|
|
13258
|
-
const authProvider = new StateBackedMcpOAuthClientProvider(
|
|
13259
|
-
authSessionId,
|
|
13260
|
-
callbackUrl,
|
|
13261
|
-
void 0,
|
|
13262
|
-
runCredentialMutation
|
|
13263
|
-
);
|
|
13264
|
-
const requestInit = {};
|
|
13265
|
-
if (mcp.headers && Object.keys(mcp.headers).length > 0) {
|
|
13266
|
-
requestInit.headers = new Headers(mcp.headers);
|
|
13267
|
-
}
|
|
13268
|
-
const transport = new StreamableHTTPClientTransport2(new URL(mcp.url), {
|
|
13269
|
-
...Object.keys(requestInit).length > 0 ? { requestInit } : {},
|
|
13270
|
-
authProvider
|
|
13271
|
-
});
|
|
13272
|
-
try {
|
|
13273
|
-
await transport.finishAuth(authorizationCode);
|
|
13274
|
-
} finally {
|
|
13275
|
-
await transport.close();
|
|
13276
|
-
}
|
|
13277
|
-
const nextSession = await getMcpAuthSession(authSessionId);
|
|
13278
|
-
if (!nextSession) {
|
|
13279
|
-
throw new Error(`Unknown MCP auth session: ${authSessionId}`);
|
|
13280
|
-
}
|
|
13281
|
-
return nextSession;
|
|
13282
|
-
}
|
|
13283
|
-
|
|
13284
11636
|
// src/chat/services/mcp-auth-orchestration.ts
|
|
13285
11637
|
var McpAuthorizationPauseError = class extends AuthorizationPauseError {
|
|
13286
11638
|
constructor(provider, providerDisplayName, disposition, requestText) {
|
|
13287
11639
|
super("mcp", provider, providerDisplayName, disposition, requestText);
|
|
13288
11640
|
}
|
|
13289
11641
|
};
|
|
13290
|
-
function authorizationId2(args) {
|
|
13291
|
-
return `${args.sessionId}:${args.kind}:${args.provider}`;
|
|
13292
|
-
}
|
|
13293
11642
|
function createMcpAuthOrchestration(input) {
|
|
13294
11643
|
let pendingPause;
|
|
13295
11644
|
const authSessionIdsByProvider = /* @__PURE__ */ new Map();
|
|
@@ -13314,7 +11663,8 @@ function createMcpAuthOrchestration(input) {
|
|
|
13314
11663
|
...input.threadTs ? { threadTs: input.threadTs } : {},
|
|
13315
11664
|
...input.toolChannelId ? { toolChannelId: input.toolChannelId } : {},
|
|
13316
11665
|
configuration: input.getConfiguration(),
|
|
13317
|
-
artifactState: input.getArtifactState()
|
|
11666
|
+
artifactState: input.getArtifactState(),
|
|
11667
|
+
createAuthorizationState: input.authorization?.createState
|
|
13318
11668
|
});
|
|
13319
11669
|
authSessionIdsByProvider.set(plugin.manifest.name, provider.authSessionId);
|
|
13320
11670
|
return provider;
|
|
@@ -13374,15 +11724,16 @@ function createMcpAuthOrchestration(input) {
|
|
|
13374
11724
|
);
|
|
13375
11725
|
}
|
|
13376
11726
|
await recordPendingAuth(nextPendingAuth);
|
|
13377
|
-
const
|
|
11727
|
+
const authorizationRequest = {
|
|
11728
|
+
authorizationUrl: authSession.authorizationUrl,
|
|
11729
|
+
label: `Click here to link your ${providerLabel} MCP access`,
|
|
11730
|
+
completionText: "Once you've authorized, Junior will continue automatically."
|
|
11731
|
+
};
|
|
11732
|
+
const delivery = await deliverOAuthAuthorization(authorizationRequest, {
|
|
11733
|
+
authorization: input.authorization,
|
|
13378
11734
|
channelId: authSession.channelId,
|
|
13379
11735
|
threadTs: authSession.threadTs,
|
|
13380
|
-
userId: authSession.userId
|
|
13381
|
-
text: formatOAuthAuthorizationMessage({
|
|
13382
|
-
authorizationUrl: authSession.authorizationUrl,
|
|
13383
|
-
label: `Click here to link your ${providerLabel} MCP access`,
|
|
13384
|
-
completionText: "Once you've authorized, this thread will continue automatically."
|
|
13385
|
-
})
|
|
11736
|
+
userId: authSession.userId
|
|
13386
11737
|
});
|
|
13387
11738
|
if (!delivery) {
|
|
13388
11739
|
await deleteMcpAuthSession(authSessionId);
|
|
@@ -13405,7 +11756,7 @@ function createMcpAuthOrchestration(input) {
|
|
|
13405
11756
|
kind: "mcp",
|
|
13406
11757
|
provider,
|
|
13407
11758
|
actorId,
|
|
13408
|
-
authorizationId:
|
|
11759
|
+
authorizationId: authorizationId({
|
|
13409
11760
|
kind: "mcp",
|
|
13410
11761
|
provider,
|
|
13411
11762
|
sessionId
|
|
@@ -13429,197 +11780,7 @@ function createMcpAuthOrchestration(input) {
|
|
|
13429
11780
|
}
|
|
13430
11781
|
|
|
13431
11782
|
// src/chat/egress/plugin.ts
|
|
13432
|
-
import { randomUUID as
|
|
13433
|
-
|
|
13434
|
-
// src/chat/plugins/credential-hooks.ts
|
|
13435
|
-
import {
|
|
13436
|
-
pluginAuthorizationSchema as pluginAuthorizationSchema2,
|
|
13437
|
-
pluginCredentialResultSchema,
|
|
13438
|
-
pluginGrantSchema as pluginGrantSchema2,
|
|
13439
|
-
pluginProviderAccountSchema as pluginProviderAccountSchema2
|
|
13440
|
-
} from "@sentry/junior-plugin-api";
|
|
13441
|
-
function parseSchema(schema, value, message) {
|
|
13442
|
-
const result = schema.safeParse(value);
|
|
13443
|
-
if (!result.success) {
|
|
13444
|
-
throw new Error(message);
|
|
13445
|
-
}
|
|
13446
|
-
return result.data;
|
|
13447
|
-
}
|
|
13448
|
-
function parseAuthorization(value, pluginName) {
|
|
13449
|
-
if (value === void 0) {
|
|
13450
|
-
return void 0;
|
|
13451
|
-
}
|
|
13452
|
-
const authorization = parseSchema(
|
|
13453
|
-
pluginAuthorizationSchema2,
|
|
13454
|
-
value,
|
|
13455
|
-
`Plugin "${pluginName}" grant authorization is invalid`
|
|
13456
|
-
);
|
|
13457
|
-
if (authorization.provider !== pluginName) {
|
|
13458
|
-
throw new Error(
|
|
13459
|
-
`Plugin "${pluginName}" grant authorization provider must match the issuing plugin`
|
|
13460
|
-
);
|
|
13461
|
-
}
|
|
13462
|
-
return authorization;
|
|
13463
|
-
}
|
|
13464
|
-
function parseGrant(value, pluginName) {
|
|
13465
|
-
return parseSchema(
|
|
13466
|
-
pluginGrantSchema2,
|
|
13467
|
-
value,
|
|
13468
|
-
`Plugin "${pluginName}" grantForEgress returned an invalid grant`
|
|
13469
|
-
);
|
|
13470
|
-
}
|
|
13471
|
-
function pluginFor(provider) {
|
|
13472
|
-
return getPlugins().find((candidate) => candidate.manifest.name === provider);
|
|
13473
|
-
}
|
|
13474
|
-
function basePluginContext(plugin) {
|
|
13475
|
-
const pluginName = plugin.manifest.name;
|
|
13476
|
-
return {
|
|
13477
|
-
plugin: { name: pluginName },
|
|
13478
|
-
log: createPluginLogger(pluginName),
|
|
13479
|
-
db: getDb()
|
|
13480
|
-
};
|
|
13481
|
-
}
|
|
13482
|
-
function parseCredentialResult(value, pluginName) {
|
|
13483
|
-
const result = parseSchema(
|
|
13484
|
-
pluginCredentialResultSchema,
|
|
13485
|
-
value,
|
|
13486
|
-
`Plugin "${pluginName}" issueCredential result is invalid`
|
|
13487
|
-
);
|
|
13488
|
-
if (result.type === "lease") {
|
|
13489
|
-
parseAuthorization(result.lease.authorization, pluginName);
|
|
13490
|
-
return result;
|
|
13491
|
-
}
|
|
13492
|
-
if (result.type === "unavailable") {
|
|
13493
|
-
return result;
|
|
13494
|
-
}
|
|
13495
|
-
parseAuthorization(result.authorization, pluginName);
|
|
13496
|
-
return result;
|
|
13497
|
-
}
|
|
13498
|
-
async function selectPluginGrant(input) {
|
|
13499
|
-
const plugin = pluginFor(input.provider);
|
|
13500
|
-
const hook = plugin?.hooks?.grantForEgress;
|
|
13501
|
-
if (!plugin || !hook) {
|
|
13502
|
-
return void 0;
|
|
13503
|
-
}
|
|
13504
|
-
const result = await hook({
|
|
13505
|
-
...basePluginContext(plugin),
|
|
13506
|
-
request: {
|
|
13507
|
-
...input.bodyText !== void 0 ? { bodyText: input.bodyText } : {},
|
|
13508
|
-
method: input.method,
|
|
13509
|
-
...input.operation ? { operation: input.operation } : {},
|
|
13510
|
-
url: input.upstreamUrl.toString()
|
|
13511
|
-
}
|
|
13512
|
-
});
|
|
13513
|
-
return result === void 0 ? void 0 : parseGrant(result, plugin.manifest.name);
|
|
13514
|
-
}
|
|
13515
|
-
async function onPluginEgressResponse(input) {
|
|
13516
|
-
const plugin = pluginFor(input.provider);
|
|
13517
|
-
const hook = plugin?.hooks?.onEgressResponse;
|
|
13518
|
-
if (!plugin || !hook) {
|
|
13519
|
-
return {};
|
|
13520
|
-
}
|
|
13521
|
-
let permissionDenied;
|
|
13522
|
-
await hook({
|
|
13523
|
-
...basePluginContext(plugin),
|
|
13524
|
-
grant: input.grant,
|
|
13525
|
-
permissionDenied(message) {
|
|
13526
|
-
const trimmed = message.trim();
|
|
13527
|
-
if (!trimmed) {
|
|
13528
|
-
throw new Error(
|
|
13529
|
-
`Plugin "${plugin.manifest.name}" onEgressResponse permissionDenied message is empty`
|
|
13530
|
-
);
|
|
13531
|
-
}
|
|
13532
|
-
permissionDenied = { message: trimmed };
|
|
13533
|
-
},
|
|
13534
|
-
request: {
|
|
13535
|
-
method: input.method,
|
|
13536
|
-
...input.operation ? { operation: input.operation } : {},
|
|
13537
|
-
url: input.upstreamUrl.toString()
|
|
13538
|
-
},
|
|
13539
|
-
response: input.response
|
|
13540
|
-
});
|
|
13541
|
-
return permissionDenied ? { permissionDenied } : {};
|
|
13542
|
-
}
|
|
13543
|
-
function hasEgressCredentialHooks(provider) {
|
|
13544
|
-
const hooks = pluginFor(provider)?.hooks;
|
|
13545
|
-
return Boolean(hooks?.grantForEgress || hooks?.issueCredential);
|
|
13546
|
-
}
|
|
13547
|
-
async function resolvePluginOAuthAccount(input) {
|
|
13548
|
-
const plugin = pluginFor(input.provider);
|
|
13549
|
-
const hook = plugin?.hooks?.resolveOAuthAccount;
|
|
13550
|
-
if (!plugin || !hook) {
|
|
13551
|
-
return void 0;
|
|
13552
|
-
}
|
|
13553
|
-
const account = await hook({
|
|
13554
|
-
...basePluginContext(plugin),
|
|
13555
|
-
tokens: input.tokens
|
|
13556
|
-
});
|
|
13557
|
-
return account === void 0 ? void 0 : parseSchema(
|
|
13558
|
-
pluginProviderAccountSchema2,
|
|
13559
|
-
account,
|
|
13560
|
-
`Plugin "${plugin.manifest.name}" resolveOAuthAccount returned an invalid account`
|
|
13561
|
-
);
|
|
13562
|
-
}
|
|
13563
|
-
async function issuePluginCredential(input) {
|
|
13564
|
-
const plugin = pluginFor(input.provider);
|
|
13565
|
-
const hook = plugin?.hooks?.issueCredential;
|
|
13566
|
-
if (!plugin || !hook) {
|
|
13567
|
-
throw new Error(`Plugin "${input.provider}" has no issueCredential hook`);
|
|
13568
|
-
}
|
|
13569
|
-
const currentUserId = "type" in input.actor ? input.actor.userId : void 0;
|
|
13570
|
-
const credentialSubjectUserId = input.credentialSubject?.userId;
|
|
13571
|
-
const result = await hook({
|
|
13572
|
-
...basePluginContext(plugin),
|
|
13573
|
-
actor: input.actor,
|
|
13574
|
-
grant: input.grant,
|
|
13575
|
-
...input.credentialSubject ? { credentialSubject: input.credentialSubject } : {},
|
|
13576
|
-
tokens: {
|
|
13577
|
-
...currentUserId ? {
|
|
13578
|
-
currentUser: {
|
|
13579
|
-
userId: currentUserId,
|
|
13580
|
-
get: async () => await input.userTokenStore.get(
|
|
13581
|
-
currentUserId,
|
|
13582
|
-
plugin.manifest.name
|
|
13583
|
-
),
|
|
13584
|
-
set: async (tokens) => {
|
|
13585
|
-
await input.userTokenStore.set(
|
|
13586
|
-
currentUserId,
|
|
13587
|
-
plugin.manifest.name,
|
|
13588
|
-
tokens
|
|
13589
|
-
);
|
|
13590
|
-
},
|
|
13591
|
-
withRefresh: async (callback) => await input.userTokenStore.withRefresh(
|
|
13592
|
-
currentUserId,
|
|
13593
|
-
plugin.manifest.name,
|
|
13594
|
-
callback
|
|
13595
|
-
)
|
|
13596
|
-
}
|
|
13597
|
-
} : {},
|
|
13598
|
-
...credentialSubjectUserId ? {
|
|
13599
|
-
credentialSubject: {
|
|
13600
|
-
userId: credentialSubjectUserId,
|
|
13601
|
-
get: async () => await input.userTokenStore.get(
|
|
13602
|
-
credentialSubjectUserId,
|
|
13603
|
-
plugin.manifest.name
|
|
13604
|
-
),
|
|
13605
|
-
set: async (tokens) => {
|
|
13606
|
-
await input.userTokenStore.set(
|
|
13607
|
-
credentialSubjectUserId,
|
|
13608
|
-
plugin.manifest.name,
|
|
13609
|
-
tokens
|
|
13610
|
-
);
|
|
13611
|
-
},
|
|
13612
|
-
withRefresh: async (callback) => await input.userTokenStore.withRefresh(
|
|
13613
|
-
credentialSubjectUserId,
|
|
13614
|
-
plugin.manifest.name,
|
|
13615
|
-
callback
|
|
13616
|
-
)
|
|
13617
|
-
}
|
|
13618
|
-
} : {}
|
|
13619
|
-
}
|
|
13620
|
-
});
|
|
13621
|
-
return parseCredentialResult(result, plugin.manifest.name);
|
|
13622
|
-
}
|
|
11783
|
+
import { randomUUID as randomUUID3 } from "crypto";
|
|
13623
11784
|
|
|
13624
11785
|
// src/chat/sandbox/egress/credentials.ts
|
|
13625
11786
|
var HTTP_READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
@@ -14392,8 +12553,8 @@ async function executeCredentialedEgressRequest(input) {
|
|
|
14392
12553
|
function credentialContextForPluginEgress(credentialContext) {
|
|
14393
12554
|
return {
|
|
14394
12555
|
credentials: credentialContext,
|
|
14395
|
-
contextId:
|
|
14396
|
-
egressId: `plugin-egress:${
|
|
12556
|
+
contextId: randomUUID3(),
|
|
12557
|
+
egressId: `plugin-egress:${randomUUID3()}`,
|
|
14397
12558
|
expiresAtMs: Date.now() + 30 * 60 * 1e3
|
|
14398
12559
|
};
|
|
14399
12560
|
}
|
|
@@ -14464,6 +12625,7 @@ async function wireAgentTools(args) {
|
|
|
14464
12625
|
skills: args.availableSkills,
|
|
14465
12626
|
traceContext: args.spanContext,
|
|
14466
12627
|
tracePropagation: args.policy.sandboxTracePropagation,
|
|
12628
|
+
egressSignals: args.policy.sandboxEgressSignals,
|
|
14467
12629
|
credentialEgress: args.routing.credentialContext,
|
|
14468
12630
|
actor: args.currentActor,
|
|
14469
12631
|
channelConfiguration: args.policy.channelConfiguration,
|
|
@@ -14494,7 +12656,8 @@ async function wireAgentTools(args) {
|
|
|
14494
12656
|
args.artifactStatePatch
|
|
14495
12657
|
),
|
|
14496
12658
|
recordPendingAuth: args.durability.recordPendingAuth,
|
|
14497
|
-
authorizationFlowMode: args.policy.authorizationFlowMode
|
|
12659
|
+
authorizationFlowMode: args.policy.authorizationFlowMode,
|
|
12660
|
+
authorization: args.authorization
|
|
14498
12661
|
});
|
|
14499
12662
|
const pluginAuth = createPluginAuthOrchestration({
|
|
14500
12663
|
abortAgent: args.abortAgent,
|
|
@@ -14509,7 +12672,8 @@ async function wireAgentTools(args) {
|
|
|
14509
12672
|
pendingAuth: args.state.pendingAuth,
|
|
14510
12673
|
recordPendingAuth: args.durability.recordPendingAuth,
|
|
14511
12674
|
authorizationFlowMode: args.policy.authorizationFlowMode,
|
|
14512
|
-
userTokenStore
|
|
12675
|
+
userTokenStore,
|
|
12676
|
+
authorization: args.authorization
|
|
14513
12677
|
});
|
|
14514
12678
|
const mcpToolManager = new McpToolManager(
|
|
14515
12679
|
pluginCatalogRuntime.getMcpProviders(),
|
|
@@ -15178,6 +13342,7 @@ ${summary}`)
|
|
|
15178
13342
|
retainedMessageCount: replacement.length - 1,
|
|
15179
13343
|
summaryChars: summary.length
|
|
15180
13344
|
},
|
|
13345
|
+
summary,
|
|
15181
13346
|
replacementHistory: replacement.map((message, index) => {
|
|
15182
13347
|
const sourceEventSeq = index < retained.length ? sourceProjection.seqs[retained[index].sourceIndex] : void 0;
|
|
15183
13348
|
return {
|
|
@@ -15211,8 +13376,9 @@ async function compactContextForHandoff(args, deps) {
|
|
|
15211
13376
|
if (!runtimeMessage) {
|
|
15212
13377
|
throw new Error("Handoff requires the current runtime turn context");
|
|
15213
13378
|
}
|
|
13379
|
+
const generatedSummary = await summarizeContext(args, deps);
|
|
15214
13380
|
const summary = `${MODEL_HANDOFF_SUMMARY_PREFIX}
|
|
15215
|
-
${
|
|
13381
|
+
${generatedSummary}`;
|
|
15216
13382
|
const message = {
|
|
15217
13383
|
...runtimeMessage,
|
|
15218
13384
|
content: [
|
|
@@ -15231,6 +13397,7 @@ ${await summarizeContext(args, deps)}`;
|
|
|
15231
13397
|
modelId: args.target.modelId,
|
|
15232
13398
|
...args.target.reasoningLevel ? { reasoningLevel: args.target.reasoningLevel } : {},
|
|
15233
13399
|
...args.triggeringToolCallId ? { triggeringToolCallId: args.triggeringToolCallId } : {},
|
|
13400
|
+
summary: generatedSummary,
|
|
15234
13401
|
replacementHistory: replacementMessages.map((replacementMessage) => ({
|
|
15235
13402
|
message: replacementMessage,
|
|
15236
13403
|
provenance: contextProvenance
|
|
@@ -15330,6 +13497,7 @@ ${summary}`;
|
|
|
15330
13497
|
retainedMessageCount: pendingMessages.length,
|
|
15331
13498
|
summaryChars: summary.length
|
|
15332
13499
|
},
|
|
13500
|
+
summary,
|
|
15333
13501
|
replacementHistory: replacementMessages.map((message, index) => ({
|
|
15334
13502
|
message,
|
|
15335
13503
|
provenance: index === 0 ? contextProvenance : pendingMessages[index - 1]?.provenance ?? contextProvenance
|
|
@@ -15432,6 +13600,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
|
|
|
15432
13600
|
const state = request.state ?? {};
|
|
15433
13601
|
const observers = request.observers ?? {};
|
|
15434
13602
|
const delivery = request.delivery;
|
|
13603
|
+
const authorization = request.authorization;
|
|
15435
13604
|
const durability = request.durability ?? {};
|
|
15436
13605
|
signal?.throwIfAborted();
|
|
15437
13606
|
assertRunRoutingConsistency(request);
|
|
@@ -15783,6 +13952,7 @@ async function executeAgentRunInPrivacyContext(request, conversationPrivacy, run
|
|
|
15783
13952
|
connectedMcpProviders,
|
|
15784
13953
|
conversationPrivacy,
|
|
15785
13954
|
durability,
|
|
13955
|
+
authorization,
|
|
15786
13956
|
generatedFiles,
|
|
15787
13957
|
invokedSkill,
|
|
15788
13958
|
observers,
|
|
@@ -16443,24 +14613,12 @@ export {
|
|
|
16443
14613
|
getConfigDefaults,
|
|
16444
14614
|
shouldEmitDevAgentTrace,
|
|
16445
14615
|
AuthorizationFlowDisabledError,
|
|
16446
|
-
RetryableDeliveryError,
|
|
16447
|
-
truncateStatusText,
|
|
16448
|
-
escapeSlackMrkdwnText,
|
|
16449
|
-
formatSlackLink,
|
|
16450
|
-
normalizeSlackStatusText,
|
|
16451
|
-
splitSlackReplyText,
|
|
16452
|
-
buildSlackOutputMessage,
|
|
16453
14616
|
buildSteeringPiMessage,
|
|
16454
|
-
createUserTokenStore,
|
|
16455
|
-
postSlackMessage,
|
|
16456
|
-
addReactionToMessage,
|
|
16457
|
-
removeReactionFromMessage,
|
|
16458
|
-
parseOAuthStatePayload,
|
|
16459
|
-
formatProviderLabel,
|
|
16460
|
-
resolveBaseUrl,
|
|
16461
|
-
startOAuthFlow,
|
|
16462
14617
|
SANDBOX_EGRESS_PROXY_PATH,
|
|
16463
14618
|
parseSandboxEgressCredentialToken,
|
|
14619
|
+
clearSandboxEgressSignals,
|
|
14620
|
+
consumeSandboxEgressAuthRequiredSignal,
|
|
14621
|
+
consumeSandboxEgressPermissionDeniedSignal,
|
|
16464
14622
|
normalizeSandboxEgressTracePropagationDomains,
|
|
16465
14623
|
shouldPropagateSandboxEgressTrace,
|
|
16466
14624
|
resolveSandboxEgressProviderForHost,
|
|
@@ -16472,14 +14630,6 @@ export {
|
|
|
16472
14630
|
appendSlackLegacyAttachmentText,
|
|
16473
14631
|
latestReportedProgress,
|
|
16474
14632
|
PluginCredentialFailureError,
|
|
16475
|
-
getMcpAuthSession,
|
|
16476
|
-
deleteMcpAuthSession,
|
|
16477
|
-
deleteMcpAuthSessionsForUserProvider,
|
|
16478
|
-
getMcpStoredOAuthCredentials,
|
|
16479
|
-
deleteMcpStoredOAuthCredentials,
|
|
16480
|
-
deleteMcpServerSessionId,
|
|
16481
|
-
finalizeMcpAuthorization,
|
|
16482
|
-
resolvePluginOAuthAccount,
|
|
16483
14633
|
executeCredentialedEgressRequest,
|
|
16484
14634
|
createContextCompactor,
|
|
16485
14635
|
executeAgentRun,
|