@sentry/junior 0.159.0 → 0.161.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-IAD3AANZ.js → agent-hooks-O75UJNC6.js} +13 -10
- package/dist/api/conversations/events.d.ts +1 -1
- package/dist/api/locations/detail.d.ts +17 -0
- package/dist/api/people/plugin-reports.d.ts +7 -0
- package/dist/api/schema/conversation.d.ts +109 -8
- package/dist/api/schema/location.d.ts +29 -0
- package/dist/api/schema/person.d.ts +29 -0
- package/dist/api/schema.js +1 -1
- package/dist/api.js +137 -27
- package/dist/app.js +73 -42
- package/dist/{catalog-runtime-6VANKLH5.js → catalog-runtime-Z3FHU4ZY.js} +5 -5
- package/dist/chat/artifacts/store.d.ts +42 -0
- package/dist/chat/conversations/history.d.ts +31 -1
- package/dist/chat/conversations/projection.d.ts +13 -0
- package/dist/chat/plugins/agent-hooks.d.ts +9 -1
- package/dist/chat/plugins/annotations.d.ts +3 -0
- package/dist/chat/plugins/conversation-sidebar.d.ts +3 -0
- package/dist/chat/plugins/viewer.d.ts +4 -0
- package/dist/chat/slack/mrkdwn.d.ts +3 -4
- package/dist/chat/slack/tools/send-files.d.ts +2 -2
- package/dist/chat/tool-support/action-review.d.ts +5 -4
- package/dist/chat/tools/publish-image.d.ts +44 -0
- package/dist/chat/tools/search-conversation-events.d.ts +1 -1
- package/dist/chat/tools/unpublish-image.d.ts +35 -0
- package/dist/{chunk-YSMAMSOU.js → chunk-4WAZ6DGX.js} +10 -10
- package/dist/{chunk-AIQKUJKK.js → chunk-6ILVZOF6.js} +4 -4
- package/dist/{chunk-YCBZ47LC.js → chunk-6WTDZKFR.js} +1 -1
- package/dist/{chunk-CNHELV2P.js → chunk-CGAIDDLD.js} +1 -1
- package/dist/{chunk-U7D4VLUF.js → chunk-DGKLYDX7.js} +2 -2
- package/dist/{chunk-TD7ZQZ5W.js → chunk-DNSIU27S.js} +1 -1
- package/dist/{chunk-PT7B6A3O.js → chunk-EAIQBR66.js} +1 -1
- package/dist/{chunk-O2DZWUPQ.js → chunk-FLMD25Y5.js} +239 -199
- package/dist/{chunk-FWDN67U3.js → chunk-G35DUPNB.js} +4 -4
- package/dist/{chunk-UG673UVL.js → chunk-HSFYTIS2.js} +1 -1
- package/dist/{chunk-WTQ3CKC3.js → chunk-IRHX2S46.js} +1 -1
- package/dist/{chunk-DXSZJAVZ.js → chunk-IW7C5C7E.js} +19 -21
- package/dist/{chunk-Q5X37U6G.js → chunk-IYFKONIL.js} +1 -1
- package/dist/{chunk-EBP6IS66.js → chunk-MNY5M7RB.js} +537 -27
- package/dist/{chunk-6IADZQJR.js → chunk-MVG5UPLM.js} +1 -1
- package/dist/{chunk-27UQ7UB3.js → chunk-O7JTSJPU.js} +6 -6
- package/dist/{chunk-NCJV265M.js → chunk-OPIJ6HY3.js} +9 -9
- package/dist/{chunk-X7AIXRVJ.js → chunk-OY2DBUV4.js} +5 -5
- package/dist/{chunk-SQOVZSWF.js → chunk-OYQEO72I.js} +480 -125
- package/dist/{chunk-UP6KQVF5.js → chunk-QHXT7LLF.js} +1 -1
- package/dist/{chunk-UKKOKMRE.js → chunk-QQ7SY5IZ.js} +5 -5
- package/dist/{chunk-PQWWCKNB.js → chunk-RULQD7OX.js} +4 -4
- package/dist/{chunk-L2PEQ2PY.js → chunk-RVENVPNO.js} +2 -2
- package/dist/{chunk-EQXVKNLM.js → chunk-SA6BEEAB.js} +27 -9
- package/dist/{chunk-54O6IKUS.js → chunk-STZVUVMS.js} +40 -4
- package/dist/{chunk-QODFNKQS.js → chunk-U2XOBFDK.js} +17 -3
- package/dist/{chunk-AZW5HKC5.js → chunk-V67ORYKQ.js} +8 -10
- package/dist/{chunk-DJZPA72K.js → chunk-VXFDVY4C.js} +8 -36
- package/dist/{chunk-LUIKMY35.js → chunk-WKN4ZQTZ.js} +1 -1
- package/dist/{chunk-OGMHLKNO.js → chunk-WVSQ2SCS.js} +1 -1
- package/dist/{chunk-IJ6KWS3V.js → chunk-YSWHAOPQ.js} +1 -1
- package/dist/{chunk-NT6V5WEE.js → chunk-ZBCLWBMV.js} +5 -5
- package/dist/cli/chat.js +29 -30
- package/dist/cli/check.js +6 -6
- package/dist/cli/plugins.js +12 -13
- package/dist/cli/snapshot-create.js +9 -9
- package/dist/cli/upgrade.js +7 -7
- package/dist/db/schema/artifacts.d.ts +178 -0
- package/dist/db/schema.d.ts +179 -1
- package/dist/{db-BIIMAPFB.js → db-KKOZRE62.js} +7 -7
- package/dist/handlers/artifacts.d.ts +8 -0
- package/dist/instrumentation.js +4 -4
- package/dist/nitro.js +2 -2
- package/dist/{oauth-callback-server-4BENCPP3.js → oauth-callback-server-5AUZDWXA.js} +22 -23
- package/dist/{runner-TUBJLEYO.js → runner-K5H5NDEC.js} +12 -13
- package/dist/{skills-MBRLNBRV.js → skills-OZGPB6G4.js} +6 -6
- package/dist/spawn-RCQSC2L5.js +37 -0
- package/dist/{validation-YTJGRJYT.js → validation-TVHCNCYL.js} +6 -6
- package/dist/version.js +1 -1
- package/dist/{work-5MRHKA54.js → work-DXCQMJ5U.js} +17 -17
- package/dist/worker-24HZ5HRI.js +24 -0
- package/migrations/0028_artifacts.sql +16 -0
- package/migrations/meta/0028_snapshot.json +2685 -0
- package/migrations/meta/_journal.json +8 -1
- package/package.json +4 -4
- package/dist/chunk-K2LAI7RW.js +0 -441
- package/dist/spawn-ZWIXA46E.js +0 -37
- package/dist/worker-S2V7HZJ4.js +0 -24
|
@@ -7,7 +7,7 @@ import {
|
|
|
7
7
|
PluginCredentialFailureError,
|
|
8
8
|
agentNameSchema,
|
|
9
9
|
createPluginAuthOrchestration
|
|
10
|
-
} from "./chunk-
|
|
10
|
+
} from "./chunk-OY2DBUV4.js";
|
|
11
11
|
import {
|
|
12
12
|
authorizationId
|
|
13
13
|
} from "./chunk-DIB4GX3G.js";
|
|
@@ -32,7 +32,7 @@ import {
|
|
|
32
32
|
searchSlackUsers,
|
|
33
33
|
selectPluginGrant,
|
|
34
34
|
toMcpProviderError
|
|
35
|
-
} from "./chunk-
|
|
35
|
+
} from "./chunk-V67ORYKQ.js";
|
|
36
36
|
import {
|
|
37
37
|
RetryableDeliveryError,
|
|
38
38
|
actorFromRun,
|
|
@@ -48,7 +48,7 @@ import {
|
|
|
48
48
|
getSlackMessagePermalink,
|
|
49
49
|
resolveBaseUrl,
|
|
50
50
|
uploadFilesToConversation
|
|
51
|
-
} from "./chunk-
|
|
51
|
+
} from "./chunk-VXFDVY4C.js";
|
|
52
52
|
import {
|
|
53
53
|
parseSandboxEgressAuthRequiredSignal,
|
|
54
54
|
parseSandboxEgressPermissionDeniedSignal,
|
|
@@ -74,7 +74,7 @@ import {
|
|
|
74
74
|
runNonInteractiveCommand,
|
|
75
75
|
throwSandboxOperationError,
|
|
76
76
|
wrapSandboxSetupError
|
|
77
|
-
} from "./chunk-
|
|
77
|
+
} from "./chunk-RULQD7OX.js";
|
|
78
78
|
import {
|
|
79
79
|
SCHEDULED_TASK_SYSTEM_ACTOR,
|
|
80
80
|
compactEventTask,
|
|
@@ -107,10 +107,10 @@ import {
|
|
|
107
107
|
scheduledTaskAttributes,
|
|
108
108
|
storeAttachments,
|
|
109
109
|
writableEventTask
|
|
110
|
-
} from "./chunk-
|
|
110
|
+
} from "./chunk-ZBCLWBMV.js";
|
|
111
111
|
import {
|
|
112
112
|
parseSlackThreadId
|
|
113
|
-
} from "./chunk-
|
|
113
|
+
} from "./chunk-YSWHAOPQ.js";
|
|
114
114
|
import {
|
|
115
115
|
AGENTS_REMOVAL_NOTICE,
|
|
116
116
|
AGENTS_REPLACEMENT_NOTICE,
|
|
@@ -165,6 +165,7 @@ import {
|
|
|
165
165
|
piMessageFromHistoryItem,
|
|
166
166
|
positiveInteger,
|
|
167
167
|
recordAgentsInstructionsUpdated,
|
|
168
|
+
recordAttachmentsDelivered,
|
|
168
169
|
recordAuthorizationRequested,
|
|
169
170
|
recordGuardianActionReviewed,
|
|
170
171
|
recordMcpProviderConnected,
|
|
@@ -180,7 +181,7 @@ import {
|
|
|
180
181
|
stripRuntimeTurnContext,
|
|
181
182
|
trimTrailingAssistantMessages,
|
|
182
183
|
truncateText
|
|
183
|
-
} from "./chunk-
|
|
184
|
+
} from "./chunk-STZVUVMS.js";
|
|
184
185
|
import {
|
|
185
186
|
JUNIOR_THREAD_STATE_TTL_MS
|
|
186
187
|
} from "./chunk-3Y47VMHV.js";
|
|
@@ -188,24 +189,22 @@ import {
|
|
|
188
189
|
privateTraceResultAttributes
|
|
189
190
|
} from "./chunk-UIE3R5XU.js";
|
|
190
191
|
import {
|
|
192
|
+
SlackActionError,
|
|
191
193
|
createPluginHookRunner,
|
|
194
|
+
downloadPrivateSlackFile,
|
|
192
195
|
getDashboardTaskLink,
|
|
196
|
+
getFilePermalink,
|
|
193
197
|
getPluginSystemPromptContributions,
|
|
194
198
|
getPluginTools,
|
|
195
199
|
getPluginUserPromptContributions,
|
|
196
|
-
getSlackToolContext,
|
|
197
|
-
readActorIdentity,
|
|
198
|
-
resolveChannelCapabilities
|
|
199
|
-
} from "./chunk-EBP6IS66.js";
|
|
200
|
-
import {
|
|
201
|
-
SlackActionError,
|
|
202
|
-
downloadPrivateSlackFile,
|
|
203
|
-
getFilePermalink,
|
|
204
200
|
getSlackClient,
|
|
201
|
+
getSlackToolContext,
|
|
205
202
|
isConversationScopedChannel,
|
|
206
203
|
normalizeSlackConversationId,
|
|
204
|
+
readActorIdentity,
|
|
205
|
+
resolveChannelCapabilities,
|
|
207
206
|
withSlackRetries
|
|
208
|
-
} from "./chunk-
|
|
207
|
+
} from "./chunk-MNY5M7RB.js";
|
|
209
208
|
import {
|
|
210
209
|
KNOWN_CONVERSATION_EVENT_TYPES,
|
|
211
210
|
contextProvenance,
|
|
@@ -217,10 +216,11 @@ import {
|
|
|
217
216
|
instructionActors,
|
|
218
217
|
instructionProvenanceFor,
|
|
219
218
|
sameActorIdentity
|
|
220
|
-
} from "./chunk-
|
|
219
|
+
} from "./chunk-U2XOBFDK.js";
|
|
221
220
|
import {
|
|
221
|
+
juniorArtifacts,
|
|
222
222
|
juniorIdentities
|
|
223
|
-
} from "./chunk-
|
|
223
|
+
} from "./chunk-FLMD25Y5.js";
|
|
224
224
|
import {
|
|
225
225
|
SANDBOX_DATA_ROOT,
|
|
226
226
|
SANDBOX_SKILLS_ROOT,
|
|
@@ -232,7 +232,7 @@ import {
|
|
|
232
232
|
} from "./chunk-EM225ITJ.js";
|
|
233
233
|
import {
|
|
234
234
|
getStateAdapter
|
|
235
|
-
} from "./chunk-
|
|
235
|
+
} from "./chunk-IYFKONIL.js";
|
|
236
236
|
import {
|
|
237
237
|
ACTIVE_TURN_COMPACTION_SUMMARY_PREFIX,
|
|
238
238
|
COMPACTION_SUMMARY_PREFIX,
|
|
@@ -265,7 +265,7 @@ import {
|
|
|
265
265
|
resolveGatewayCredential,
|
|
266
266
|
resolveGatewayModel,
|
|
267
267
|
unwrapCurrentInstruction
|
|
268
|
-
} from "./chunk-
|
|
268
|
+
} from "./chunk-RVENVPNO.js";
|
|
269
269
|
import {
|
|
270
270
|
canExposeConversationPayload,
|
|
271
271
|
resolveConversationPrivacy,
|
|
@@ -275,16 +275,16 @@ import {
|
|
|
275
275
|
toGenAiMessagesTraceAttributes,
|
|
276
276
|
toGenAiPayloadMetadata,
|
|
277
277
|
toGenAiPayloadTraceAttributes
|
|
278
|
-
} from "./chunk-
|
|
278
|
+
} from "./chunk-EAIQBR66.js";
|
|
279
279
|
import {
|
|
280
280
|
discoverSkills,
|
|
281
281
|
findSkillByName,
|
|
282
282
|
loadSkillsByName,
|
|
283
283
|
parseSkillInvocation
|
|
284
|
-
} from "./chunk-
|
|
284
|
+
} from "./chunk-DGKLYDX7.js";
|
|
285
285
|
import {
|
|
286
286
|
pluginCatalogRuntime
|
|
287
|
-
} from "./chunk-
|
|
287
|
+
} from "./chunk-CGAIDDLD.js";
|
|
288
288
|
import {
|
|
289
289
|
CredentialUnavailableError,
|
|
290
290
|
OAuthProviderError,
|
|
@@ -292,7 +292,7 @@ import {
|
|
|
292
292
|
getOAuthProviderErrorAttributes,
|
|
293
293
|
resolveAuthTokenPlaceholder,
|
|
294
294
|
resolvePluginCommandEnv
|
|
295
|
-
} from "./chunk-
|
|
295
|
+
} from "./chunk-6WTDZKFR.js";
|
|
296
296
|
import {
|
|
297
297
|
credentialUserSubjectId
|
|
298
298
|
} from "./chunk-BDUENQD3.js";
|
|
@@ -320,7 +320,7 @@ import {
|
|
|
320
320
|
summarizeMessageText,
|
|
321
321
|
withLogContext,
|
|
322
322
|
withSpan
|
|
323
|
-
} from "./chunk-
|
|
323
|
+
} from "./chunk-DNSIU27S.js";
|
|
324
324
|
import {
|
|
325
325
|
startInactiveSpan,
|
|
326
326
|
withActiveSpan
|
|
@@ -1731,7 +1731,6 @@ function buildOutputSection(platform) {
|
|
|
1731
1731
|
`<output format="markdown">`,
|
|
1732
1732
|
"- Start with the answer or result, not internal process narration.",
|
|
1733
1733
|
"- Use concise Markdown suitable for terminal output: short paragraphs, bullets, links, and fenced code blocks when helpful.",
|
|
1734
|
-
"- When you mention a pull request, include a direct link. Prefer the full PR URL or a Markdown link; do not leave bare `PR #123` text without a URL or repo.",
|
|
1735
1734
|
"- End every turn with a final user-facing response.",
|
|
1736
1735
|
"</output>"
|
|
1737
1736
|
].join("\n");
|
|
@@ -1741,7 +1740,6 @@ function buildOutputSection(platform) {
|
|
|
1741
1740
|
"- Default to the shortest complete reply\u2014usually 1\u20135 sentences and under 800 characters. Include only the outcome, decisive evidence, and any blocker or required next action. If useful detail would exceed that, put it in a Slack canvas and reply with the link. An explicit user request for detail overrides this target.",
|
|
1742
1741
|
"- Start with the answer or result, not internal process narration.",
|
|
1743
1742
|
"- Use Slack-flavored Markdown: **bold** section labels, `code`, [text](url) links, bullet lists, and fenced code blocks. No hash-prefixed headings and no tables. When the answer primarily lists several URLs, show each URL bare instead of as a labeled link.",
|
|
1744
|
-
"- When you mention a pull request, include a direct link. Prefer the full PR URL or a Markdown link; `owner/repo#number` is also accepted and is linkified on delivery. Do not leave bare `PR #123` text without a URL or repo.",
|
|
1745
1743
|
"- End every turn with a final user-facing markdown response unless the Slack action rules allow a no-reply completion.",
|
|
1746
1744
|
"</output>"
|
|
1747
1745
|
].join("\n");
|
|
@@ -3981,7 +3979,7 @@ function parseToolInput(schema, args) {
|
|
|
3981
3979
|
return result.data;
|
|
3982
3980
|
}
|
|
3983
3981
|
function zodTool(definition) {
|
|
3984
|
-
const { inputSchema: inputSchema2, outputSchema:
|
|
3982
|
+
const { inputSchema: inputSchema2, outputSchema: outputSchema7, prepareArguments, execute, ...toolDef } = definition;
|
|
3985
3983
|
let modelInputSchema;
|
|
3986
3984
|
try {
|
|
3987
3985
|
modelInputSchema = z3.toJSONSchema(inputSchema2);
|
|
@@ -3992,9 +3990,9 @@ function zodTool(definition) {
|
|
|
3992
3990
|
);
|
|
3993
3991
|
}
|
|
3994
3992
|
let modelOutputSchema;
|
|
3995
|
-
if (
|
|
3993
|
+
if (outputSchema7) {
|
|
3996
3994
|
try {
|
|
3997
|
-
modelOutputSchema = z3.toJSONSchema(
|
|
3995
|
+
modelOutputSchema = z3.toJSONSchema(outputSchema7);
|
|
3998
3996
|
} catch (error) {
|
|
3999
3997
|
throw new TypeError(
|
|
4000
3998
|
"zodTool() outputSchema must be representable as JSON Schema.",
|
|
@@ -4018,7 +4016,7 @@ function zodTool(definition) {
|
|
|
4018
4016
|
input,
|
|
4019
4017
|
options
|
|
4020
4018
|
);
|
|
4021
|
-
if (!
|
|
4019
|
+
if (!outputSchema7) {
|
|
4022
4020
|
if (isContentOnlyToolResult(result)) {
|
|
4023
4021
|
return result;
|
|
4024
4022
|
}
|
|
@@ -4029,10 +4027,10 @@ function zodTool(definition) {
|
|
|
4029
4027
|
if (isStructuredToolResultEnvelope(result)) {
|
|
4030
4028
|
return {
|
|
4031
4029
|
content: parseToolContent(result.content),
|
|
4032
|
-
details:
|
|
4030
|
+
details: outputSchema7.parse(result.details)
|
|
4033
4031
|
};
|
|
4034
4032
|
}
|
|
4035
|
-
return
|
|
4033
|
+
return outputSchema7.parse(result);
|
|
4036
4034
|
}
|
|
4037
4035
|
} : {}
|
|
4038
4036
|
};
|
|
@@ -6493,7 +6491,7 @@ var McpToolManager = class {
|
|
|
6493
6491
|
return client;
|
|
6494
6492
|
}
|
|
6495
6493
|
toManagedTool(plugin, client, tool) {
|
|
6496
|
-
const
|
|
6494
|
+
const outputSchema7 = toOptionalRecord(tool.outputSchema);
|
|
6497
6495
|
const annotations = tool.annotations;
|
|
6498
6496
|
const missingAnnotationKeys = missingToolAnnotationKeys(annotations);
|
|
6499
6497
|
if (missingAnnotationKeys.length > 0) {
|
|
@@ -6510,7 +6508,7 @@ var McpToolManager = class {
|
|
|
6510
6508
|
provider: plugin.manifest.name,
|
|
6511
6509
|
rawName: tool.name,
|
|
6512
6510
|
...tool.title?.trim() ? { title: tool.title.trim() } : {},
|
|
6513
|
-
...
|
|
6511
|
+
...outputSchema7 ? { outputSchema: outputSchema7 } : {},
|
|
6514
6512
|
...annotations ? { annotations } : {},
|
|
6515
6513
|
execute: async (args, options) => {
|
|
6516
6514
|
const resolvedArgs = typeof args === "object" && args !== null ? args : {};
|
|
@@ -10545,7 +10543,8 @@ var sendFilesResultSchema = juniorToolOutputSchema.extend({
|
|
|
10545
10543
|
attachment_refs: z42.array(
|
|
10546
10544
|
z42.object({
|
|
10547
10545
|
id: z42.string().min(1),
|
|
10548
|
-
|
|
10546
|
+
// Same noun as storage, delivery events, and the report API.
|
|
10547
|
+
filename: z42.string().min(1)
|
|
10549
10548
|
})
|
|
10550
10549
|
)
|
|
10551
10550
|
});
|
|
@@ -10583,7 +10582,7 @@ function createSendFilesTool(context, state, materializeFile, attachments) {
|
|
|
10583
10582
|
)
|
|
10584
10583
|
}),
|
|
10585
10584
|
outputSchema: sendFilesResultSchema,
|
|
10586
|
-
execute: async ({ files }) => {
|
|
10585
|
+
execute: async ({ files }, options) => {
|
|
10587
10586
|
const filesToSend = normalizeFiles(files);
|
|
10588
10587
|
const activeChannelId = context.sourceChannelId;
|
|
10589
10588
|
if (!activeChannelId) {
|
|
@@ -10605,8 +10604,15 @@ function createSendFilesTool(context, state, materializeFile, attachments) {
|
|
|
10605
10604
|
});
|
|
10606
10605
|
const cached = state.getOperationResult(operationKey);
|
|
10607
10606
|
if (cached) {
|
|
10607
|
+
if (attachments && cached.delivered.length > 0) {
|
|
10608
|
+
await recordAttachmentsDelivered({
|
|
10609
|
+
attachments: cached.delivered,
|
|
10610
|
+
conversationId: attachments.conversationId,
|
|
10611
|
+
...cached.toolCallId ? { toolCallId: cached.toolCallId } : {}
|
|
10612
|
+
});
|
|
10613
|
+
}
|
|
10608
10614
|
return sendFilesResultSchema.parse({
|
|
10609
|
-
...cached,
|
|
10615
|
+
...cached.result,
|
|
10610
10616
|
deduplicated: true
|
|
10611
10617
|
});
|
|
10612
10618
|
}
|
|
@@ -10625,13 +10631,36 @@ function createSendFilesTool(context, state, materializeFile, attachments) {
|
|
|
10625
10631
|
files: uploads,
|
|
10626
10632
|
threadTs
|
|
10627
10633
|
});
|
|
10634
|
+
const delivered = stored.map(
|
|
10635
|
+
(attachment, index) => {
|
|
10636
|
+
const file = materializedFiles[index];
|
|
10637
|
+
return {
|
|
10638
|
+
id: attachment.id,
|
|
10639
|
+
filename: file.filename,
|
|
10640
|
+
contentType: file.mimeType,
|
|
10641
|
+
bytes: file.bytes
|
|
10642
|
+
};
|
|
10643
|
+
}
|
|
10644
|
+
);
|
|
10628
10645
|
const response = {
|
|
10629
|
-
|
|
10646
|
+
// Tool result stays minimal; transcript/report carries full metadata.
|
|
10647
|
+
attachment_refs: delivered.map((attachment) => ({
|
|
10630
10648
|
id: attachment.id,
|
|
10631
|
-
|
|
10649
|
+
filename: attachment.filename
|
|
10632
10650
|
}))
|
|
10633
10651
|
};
|
|
10634
|
-
state.setOperationResult(operationKey,
|
|
10652
|
+
state.setOperationResult(operationKey, {
|
|
10653
|
+
delivered,
|
|
10654
|
+
result: response,
|
|
10655
|
+
...options.toolCallId ? { toolCallId: options.toolCallId } : {}
|
|
10656
|
+
});
|
|
10657
|
+
if (attachments && delivered.length > 0) {
|
|
10658
|
+
await recordAttachmentsDelivered({
|
|
10659
|
+
attachments: delivered,
|
|
10660
|
+
conversationId: attachments.conversationId,
|
|
10661
|
+
...options.toolCallId ? { toolCallId: options.toolCallId } : {}
|
|
10662
|
+
});
|
|
10663
|
+
}
|
|
10635
10664
|
return response;
|
|
10636
10665
|
}
|
|
10637
10666
|
});
|
|
@@ -12275,28 +12304,318 @@ function getTimeZoneOffsetMinutes(timestampMs, timezone) {
|
|
|
12275
12304
|
return Math.round((timestampMs - asUtcMs) / 6e4);
|
|
12276
12305
|
}
|
|
12277
12306
|
|
|
12278
|
-
// src/chat/tools/
|
|
12307
|
+
// src/chat/tools/publish-image.ts
|
|
12279
12308
|
import { z as z56 } from "zod";
|
|
12309
|
+
|
|
12310
|
+
// src/chat/artifacts/store.ts
|
|
12311
|
+
import { createHash as createHash5, randomUUID as randomUUID3 } from "crypto";
|
|
12312
|
+
import { and as and2, eq as eq2, isNull } from "drizzle-orm";
|
|
12313
|
+
var MAX_IMAGE_BYTES = 10 * 1024 * 1024;
|
|
12314
|
+
var IMAGE_TYPES = {
|
|
12315
|
+
gif: "image/gif",
|
|
12316
|
+
jpg: "image/jpeg",
|
|
12317
|
+
png: "image/png",
|
|
12318
|
+
webp: "image/webp"
|
|
12319
|
+
};
|
|
12320
|
+
var FILENAME_RE = /^([0-9a-f]{8}-[0-9a-f]{4}-[1-8][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12})\.(gif|jpg|png|webp)$/i;
|
|
12321
|
+
function detectImageType(data) {
|
|
12322
|
+
if (data.length >= 8 && data.subarray(0, 8).equals(Buffer.from([137, 80, 78, 71, 13, 10, 26, 10]))) {
|
|
12323
|
+
return { contentType: IMAGE_TYPES.png, extension: "png" };
|
|
12324
|
+
}
|
|
12325
|
+
if (data.length >= 3 && data[0] === 255 && data[1] === 216 && data[2] === 255) {
|
|
12326
|
+
return { contentType: IMAGE_TYPES.jpg, extension: "jpg" };
|
|
12327
|
+
}
|
|
12328
|
+
if (data.length >= 6 && (data.subarray(0, 6).toString("ascii") === "GIF87a" || data.subarray(0, 6).toString("ascii") === "GIF89a")) {
|
|
12329
|
+
return { contentType: IMAGE_TYPES.gif, extension: "gif" };
|
|
12330
|
+
}
|
|
12331
|
+
if (data.length >= 12 && data.subarray(0, 4).toString("ascii") === "RIFF" && data.subarray(8, 12).toString("ascii") === "WEBP") {
|
|
12332
|
+
return { contentType: IMAGE_TYPES.webp, extension: "webp" };
|
|
12333
|
+
}
|
|
12334
|
+
return null;
|
|
12335
|
+
}
|
|
12336
|
+
function artifactKey(filename) {
|
|
12337
|
+
return `artifacts/${filename}`;
|
|
12338
|
+
}
|
|
12339
|
+
function parseArtifactFilename(filename) {
|
|
12340
|
+
const match = filename.match(FILENAME_RE);
|
|
12341
|
+
if (!match) return null;
|
|
12342
|
+
return {
|
|
12343
|
+
id: match[1].toLowerCase(),
|
|
12344
|
+
ext: match[2].toLowerCase()
|
|
12345
|
+
};
|
|
12346
|
+
}
|
|
12347
|
+
function artifactFilenameFromRef(ref) {
|
|
12348
|
+
const trimmed = ref.trim();
|
|
12349
|
+
if (FILENAME_RE.test(trimmed)) {
|
|
12350
|
+
const parsed = parseArtifactFilename(trimmed);
|
|
12351
|
+
return parsed ? `${parsed.id}.${parsed.ext}` : null;
|
|
12352
|
+
}
|
|
12353
|
+
try {
|
|
12354
|
+
const url = new URL(trimmed);
|
|
12355
|
+
const marker = "/public/artifacts/";
|
|
12356
|
+
const index = url.pathname.lastIndexOf(marker);
|
|
12357
|
+
if (index === -1) return null;
|
|
12358
|
+
const filename = url.pathname.slice(index + marker.length);
|
|
12359
|
+
const parsed = parseArtifactFilename(filename);
|
|
12360
|
+
return parsed ? `${parsed.id}.${parsed.ext}` : null;
|
|
12361
|
+
} catch {
|
|
12362
|
+
return null;
|
|
12363
|
+
}
|
|
12364
|
+
}
|
|
12365
|
+
function resolvePublicBaseUrl(publicBaseUrl) {
|
|
12366
|
+
const baseUrl = publicBaseUrl.trim().replace(/\/+$/, "");
|
|
12367
|
+
let parsedBaseUrl;
|
|
12368
|
+
try {
|
|
12369
|
+
parsedBaseUrl = new URL(baseUrl);
|
|
12370
|
+
} catch {
|
|
12371
|
+
throw new Error("public base URL must be an absolute HTTP(S) URL");
|
|
12372
|
+
}
|
|
12373
|
+
if (parsedBaseUrl.protocol !== "http:" && parsedBaseUrl.protocol !== "https:") {
|
|
12374
|
+
throw new Error("public base URL must use HTTP(S)");
|
|
12375
|
+
}
|
|
12376
|
+
return baseUrl;
|
|
12377
|
+
}
|
|
12378
|
+
async function publishImage(args) {
|
|
12379
|
+
if (args.body.byteLength === 0) {
|
|
12380
|
+
throw new ToolInputError("image is empty");
|
|
12381
|
+
}
|
|
12382
|
+
if (args.body.byteLength > MAX_IMAGE_BYTES) {
|
|
12383
|
+
throw new ToolInputError(
|
|
12384
|
+
`image exceeds ${MAX_IMAGE_BYTES} bytes (${args.body.byteLength} bytes)`
|
|
12385
|
+
);
|
|
12386
|
+
}
|
|
12387
|
+
const imageType = detectImageType(args.body);
|
|
12388
|
+
if (!imageType) {
|
|
12389
|
+
throw new ToolInputError(
|
|
12390
|
+
"unsupported image format; use PNG, JPEG, GIF, or WebP"
|
|
12391
|
+
);
|
|
12392
|
+
}
|
|
12393
|
+
const baseUrl = resolvePublicBaseUrl(args.publicBaseUrl);
|
|
12394
|
+
const sha256 = createHash5("sha256").update(args.body).digest("hex");
|
|
12395
|
+
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
12396
|
+
const [existing] = await args.db.db().select({
|
|
12397
|
+
id: juniorArtifacts.id
|
|
12398
|
+
}).from(juniorArtifacts).where(
|
|
12399
|
+
and2(
|
|
12400
|
+
eq2(juniorArtifacts.conversationId, args.conversationId),
|
|
12401
|
+
eq2(juniorArtifacts.sha256, sha256)
|
|
12402
|
+
)
|
|
12403
|
+
);
|
|
12404
|
+
const id = existing?.id ?? randomUUID3();
|
|
12405
|
+
const filename = `${id}.${imageType.extension}`;
|
|
12406
|
+
const storageKey = artifactKey(filename);
|
|
12407
|
+
await args.storage.put({
|
|
12408
|
+
body: args.body,
|
|
12409
|
+
contentType: imageType.contentType,
|
|
12410
|
+
key: storageKey
|
|
12411
|
+
});
|
|
12412
|
+
const [row] = await args.db.db().insert(juniorArtifacts).values({
|
|
12413
|
+
bytes: args.body.byteLength,
|
|
12414
|
+
contentType: imageType.contentType,
|
|
12415
|
+
conversationId: args.conversationId,
|
|
12416
|
+
createdAt: now,
|
|
12417
|
+
deleteRequestedAt: null,
|
|
12418
|
+
ext: imageType.extension,
|
|
12419
|
+
id,
|
|
12420
|
+
public: true,
|
|
12421
|
+
sha256,
|
|
12422
|
+
storageKey
|
|
12423
|
+
}).onConflictDoUpdate({
|
|
12424
|
+
target: [juniorArtifacts.conversationId, juniorArtifacts.sha256],
|
|
12425
|
+
set: {
|
|
12426
|
+
bytes: args.body.byteLength,
|
|
12427
|
+
contentType: imageType.contentType,
|
|
12428
|
+
deleteRequestedAt: null,
|
|
12429
|
+
ext: imageType.extension,
|
|
12430
|
+
public: true
|
|
12431
|
+
// Keep the winning row's id/storage key. A raced put may leave an
|
|
12432
|
+
// unused object; that is fine for mvp.
|
|
12433
|
+
}
|
|
12434
|
+
}).returning({
|
|
12435
|
+
ext: juniorArtifacts.ext,
|
|
12436
|
+
id: juniorArtifacts.id
|
|
12437
|
+
});
|
|
12438
|
+
if (!row) {
|
|
12439
|
+
throw new Error("failed to publish artifact row");
|
|
12440
|
+
}
|
|
12441
|
+
return {
|
|
12442
|
+
bytes: args.body.byteLength,
|
|
12443
|
+
contentType: imageType.contentType,
|
|
12444
|
+
url: `${baseUrl}/public/artifacts/${row.id}.${row.ext}`
|
|
12445
|
+
};
|
|
12446
|
+
}
|
|
12447
|
+
async function unpublishArtifact(args) {
|
|
12448
|
+
const filename = artifactFilenameFromRef(args.ref);
|
|
12449
|
+
if (!filename) {
|
|
12450
|
+
throw new ToolInputError(
|
|
12451
|
+
"artifact ref must be a public artifact URL or <id>.<ext> filename"
|
|
12452
|
+
);
|
|
12453
|
+
}
|
|
12454
|
+
const parsed = parseArtifactFilename(filename);
|
|
12455
|
+
if (!parsed) {
|
|
12456
|
+
throw new ToolInputError(
|
|
12457
|
+
"artifact ref must be a public artifact URL or <id>.<ext> filename"
|
|
12458
|
+
);
|
|
12459
|
+
}
|
|
12460
|
+
const now = args.now ?? /* @__PURE__ */ new Date();
|
|
12461
|
+
const updated = await args.db.db().update(juniorArtifacts).set({ deleteRequestedAt: now }).where(
|
|
12462
|
+
and2(
|
|
12463
|
+
eq2(juniorArtifacts.id, parsed.id),
|
|
12464
|
+
eq2(juniorArtifacts.conversationId, args.conversationId)
|
|
12465
|
+
)
|
|
12466
|
+
).returning({ id: juniorArtifacts.id });
|
|
12467
|
+
if (updated.length === 0) {
|
|
12468
|
+
throw new ToolInputError(
|
|
12469
|
+
`artifact not found for this conversation: ${filename}`
|
|
12470
|
+
);
|
|
12471
|
+
}
|
|
12472
|
+
return { filename };
|
|
12473
|
+
}
|
|
12474
|
+
async function readPublicArtifact(args) {
|
|
12475
|
+
const parsed = parseArtifactFilename(args.filename);
|
|
12476
|
+
if (!parsed) return null;
|
|
12477
|
+
const [row] = await args.db.db().select({
|
|
12478
|
+
contentType: juniorArtifacts.contentType,
|
|
12479
|
+
storageKey: juniorArtifacts.storageKey
|
|
12480
|
+
}).from(juniorArtifacts).where(
|
|
12481
|
+
and2(
|
|
12482
|
+
eq2(juniorArtifacts.id, parsed.id),
|
|
12483
|
+
eq2(juniorArtifacts.public, true),
|
|
12484
|
+
isNull(juniorArtifacts.deleteRequestedAt)
|
|
12485
|
+
)
|
|
12486
|
+
);
|
|
12487
|
+
if (!row) return null;
|
|
12488
|
+
const body = await args.storage.get(row.storageKey);
|
|
12489
|
+
if (!body) return null;
|
|
12490
|
+
return { body, contentType: row.contentType };
|
|
12491
|
+
}
|
|
12492
|
+
function publicArtifactHeaders(contentType) {
|
|
12493
|
+
return new Headers({
|
|
12494
|
+
// Keep TTL short so unpublish can take effect. Do not mark immutable.
|
|
12495
|
+
"cache-control": "public, max-age=300, must-revalidate",
|
|
12496
|
+
"content-type": contentType,
|
|
12497
|
+
"x-content-type-options": "nosniff"
|
|
12498
|
+
});
|
|
12499
|
+
}
|
|
12500
|
+
function unpublishedArtifactHeaders() {
|
|
12501
|
+
return new Headers({
|
|
12502
|
+
"cache-control": "private, no-store",
|
|
12503
|
+
"x-content-type-options": "nosniff"
|
|
12504
|
+
});
|
|
12505
|
+
}
|
|
12506
|
+
|
|
12507
|
+
// src/chat/tools/publish-image.ts
|
|
12508
|
+
var outputSchema5 = juniorToolOutputSchema.extend({
|
|
12509
|
+
bytes: z56.number().int().positive(),
|
|
12510
|
+
content_type: z56.string().min(1),
|
|
12511
|
+
public: z56.literal(true),
|
|
12512
|
+
url: z56.string().url()
|
|
12513
|
+
});
|
|
12514
|
+
function createPublishImageTool(args) {
|
|
12515
|
+
return zodTool({
|
|
12516
|
+
annotations: {
|
|
12517
|
+
destructiveHint: false,
|
|
12518
|
+
idempotentHint: true,
|
|
12519
|
+
openWorldHint: true,
|
|
12520
|
+
readOnlyHint: false
|
|
12521
|
+
},
|
|
12522
|
+
approvalMode: "review",
|
|
12523
|
+
description: "Publish one sandbox image to a durable public HTTPS URL. Anyone on the internet with the URL can view the image. Use only when an external destination needs a public image URL. Do not use for private file sharing or private conversation storage. Supports PNG, JPEG, GIF, and WebP up to 10 MB.",
|
|
12524
|
+
describeProposal: ({ path: path8 }) => `Publish sandbox image ${path8} to a public internet URL.`,
|
|
12525
|
+
executionMode: "sequential",
|
|
12526
|
+
inputSchema: z56.object({
|
|
12527
|
+
path: z56.string().min(1).describe("Exact sandbox path of an existing image file.")
|
|
12528
|
+
}).strict(),
|
|
12529
|
+
outputSchema: outputSchema5,
|
|
12530
|
+
async execute({ path: path8 }) {
|
|
12531
|
+
const publicBaseUrl = (args.publicBaseUrl ?? resolveBaseUrl)()?.trim();
|
|
12532
|
+
if (!publicBaseUrl) {
|
|
12533
|
+
throw new Error(
|
|
12534
|
+
"publishImage requires JUNIOR_BASE_URL or a Vercel deployment URL."
|
|
12535
|
+
);
|
|
12536
|
+
}
|
|
12537
|
+
const targetPath = normalizeSandboxPath(path8);
|
|
12538
|
+
const body = await args.workspace.readFileToBuffer({ path: targetPath });
|
|
12539
|
+
if (!body) {
|
|
12540
|
+
throw new SandboxFileNotFoundError(targetPath);
|
|
12541
|
+
}
|
|
12542
|
+
const image = await publishImage({
|
|
12543
|
+
body,
|
|
12544
|
+
conversationId: args.conversationId,
|
|
12545
|
+
db: args.db,
|
|
12546
|
+
publicBaseUrl,
|
|
12547
|
+
storage: args.storage
|
|
12548
|
+
});
|
|
12549
|
+
return {
|
|
12550
|
+
bytes: image.bytes,
|
|
12551
|
+
content_type: image.contentType,
|
|
12552
|
+
public: true,
|
|
12553
|
+
url: image.url
|
|
12554
|
+
};
|
|
12555
|
+
}
|
|
12556
|
+
});
|
|
12557
|
+
}
|
|
12558
|
+
|
|
12559
|
+
// src/chat/tools/unpublish-image.ts
|
|
12560
|
+
import { z as z57 } from "zod";
|
|
12561
|
+
var outputSchema6 = juniorToolOutputSchema.extend({
|
|
12562
|
+
filename: z57.string().min(1),
|
|
12563
|
+
unpublished: z57.literal(true)
|
|
12564
|
+
});
|
|
12565
|
+
function createUnpublishImageTool(args) {
|
|
12566
|
+
return zodTool({
|
|
12567
|
+
annotations: {
|
|
12568
|
+
destructiveHint: true,
|
|
12569
|
+
idempotentHint: true,
|
|
12570
|
+
openWorldHint: false,
|
|
12571
|
+
readOnlyHint: false
|
|
12572
|
+
},
|
|
12573
|
+
approvalMode: "review",
|
|
12574
|
+
description: "Unpublish one public image previously published in this conversation so its public URL stops serving. Pass the public artifact URL or `<id>.<ext>` filename. Only images owned by this conversation can be unpublished. Republishing the same image bytes later in this conversation restores a live URL for this conversation. Does not remove copies already fetched by browsers or GitHub.",
|
|
12575
|
+
describeProposal: ({ ref }) => `Unpublish public image ${ref}.`,
|
|
12576
|
+
executionMode: "sequential",
|
|
12577
|
+
inputSchema: z57.object({
|
|
12578
|
+
ref: z57.string().min(1).describe(
|
|
12579
|
+
"Public artifact URL or artifact filename (`<id>.<ext>`)."
|
|
12580
|
+
)
|
|
12581
|
+
}).strict(),
|
|
12582
|
+
outputSchema: outputSchema6,
|
|
12583
|
+
async execute({ ref }) {
|
|
12584
|
+
const result = await unpublishArtifact({
|
|
12585
|
+
conversationId: args.conversationId,
|
|
12586
|
+
db: args.db,
|
|
12587
|
+
ref
|
|
12588
|
+
});
|
|
12589
|
+
return {
|
|
12590
|
+
filename: result.filename,
|
|
12591
|
+
unpublished: true
|
|
12592
|
+
};
|
|
12593
|
+
}
|
|
12594
|
+
});
|
|
12595
|
+
}
|
|
12596
|
+
|
|
12597
|
+
// src/chat/tools/search-conversation-events.ts
|
|
12598
|
+
import { z as z58 } from "zod";
|
|
12280
12599
|
var DEFAULT_LIMIT3 = 25;
|
|
12281
12600
|
var MAX_LIMIT = 100;
|
|
12282
12601
|
var MAX_EVENT_DATA_BYTES = 4e3;
|
|
12283
12602
|
var MAX_EVENTS_JSON_BYTES = 2e4;
|
|
12284
12603
|
var MAX_IDENTITY_CHARS = 256;
|
|
12285
12604
|
var textEncoder = new TextEncoder();
|
|
12286
|
-
var knownEventTypeSchema =
|
|
12287
|
-
var projectedEventSchema =
|
|
12288
|
-
seq:
|
|
12289
|
-
history_version:
|
|
12290
|
-
created_at:
|
|
12291
|
-
idempotency_key:
|
|
12292
|
-
data:
|
|
12605
|
+
var knownEventTypeSchema = z58.enum(KNOWN_CONVERSATION_EVENT_TYPES);
|
|
12606
|
+
var projectedEventSchema = z58.object({
|
|
12607
|
+
seq: z58.number().int().nonnegative().describe("Stable event position for pagination."),
|
|
12608
|
+
history_version: z58.number().int().nonnegative().describe("Agent history generation after replacement."),
|
|
12609
|
+
created_at: z58.iso.datetime().describe("When the event was recorded."),
|
|
12610
|
+
idempotency_key: z58.string().min(1).optional().describe("Retry-stable event identity, when assigned."),
|
|
12611
|
+
data: z58.record(z58.string(), z58.unknown()).describe("Bounded event data.")
|
|
12293
12612
|
}).strict();
|
|
12294
12613
|
var searchConversationEventsOutputSchema = juniorToolOutputSchema.extend({
|
|
12295
|
-
conversation_id:
|
|
12296
|
-
events:
|
|
12297
|
-
has_older:
|
|
12298
|
-
has_newer:
|
|
12299
|
-
omitted_event_count:
|
|
12614
|
+
conversation_id: z58.string().min(1),
|
|
12615
|
+
events: z58.array(projectedEventSchema),
|
|
12616
|
+
has_older: z58.boolean().describe("Whether matching older events exist."),
|
|
12617
|
+
has_newer: z58.boolean().describe("Whether matching newer events exist."),
|
|
12618
|
+
omitted_event_count: z58.number().int().nonnegative().optional().describe("Events omitted by the response byte limit.")
|
|
12300
12619
|
});
|
|
12301
12620
|
function createSearchConversationEventsTool(context) {
|
|
12302
12621
|
return zodTool({
|
|
@@ -12309,16 +12628,16 @@ function createSearchConversationEventsTool(context) {
|
|
|
12309
12628
|
openWorldHint: false,
|
|
12310
12629
|
readOnlyHint: true
|
|
12311
12630
|
},
|
|
12312
|
-
inputSchema:
|
|
12313
|
-
conversation_id:
|
|
12631
|
+
inputSchema: z58.object({
|
|
12632
|
+
conversation_id: z58.string().trim().min(1).nullable().describe(
|
|
12314
12633
|
"Conversation ID to search. Omit or null for the current conversation."
|
|
12315
12634
|
).optional(),
|
|
12316
|
-
after_seq:
|
|
12317
|
-
before_seq:
|
|
12318
|
-
limit:
|
|
12635
|
+
after_seq: z58.number().int().nonnegative().nullable().describe("Exclusive event sequence lower bound; returns ascending.").optional(),
|
|
12636
|
+
before_seq: z58.number().int().nonnegative().nullable().describe("Exclusive event sequence upper bound.").optional(),
|
|
12637
|
+
limit: z58.number().int().min(1).max(MAX_LIMIT).nullable().describe(
|
|
12319
12638
|
`Maximum events. Default ${DEFAULT_LIMIT3}; max ${MAX_LIMIT}.`
|
|
12320
12639
|
).optional(),
|
|
12321
|
-
types:
|
|
12640
|
+
types: z58.array(knownEventTypeSchema).min(1).nullable().describe("Event types to include; omit for all types.").optional()
|
|
12322
12641
|
}).strict(),
|
|
12323
12642
|
outputSchema: searchConversationEventsOutputSchema,
|
|
12324
12643
|
execute: async (input) => {
|
|
@@ -12564,10 +12883,10 @@ function stripReplacementHistoryField(value) {
|
|
|
12564
12883
|
}
|
|
12565
12884
|
|
|
12566
12885
|
// src/chat/tools/handoff/tool.ts
|
|
12567
|
-
import { z as
|
|
12886
|
+
import { z as z59 } from "zod";
|
|
12568
12887
|
var HANDOFF_TOOL_NAME = "handoff";
|
|
12569
12888
|
function createHandoffTool(handoff) {
|
|
12570
|
-
const profileSchema =
|
|
12889
|
+
const profileSchema = z59.enum(handoff.profiles);
|
|
12571
12890
|
const handoffResultSchema = juniorToolOutputSchema.extend({
|
|
12572
12891
|
model_profile: modelProfileSchema
|
|
12573
12892
|
});
|
|
@@ -12581,7 +12900,7 @@ function createHandoffTool(handoff) {
|
|
|
12581
12900
|
},
|
|
12582
12901
|
description: `Switch to another execution profile and continue the same task. Profiles: ${profileNames}. Call this as the only tool.`,
|
|
12583
12902
|
executionMode: "sequential",
|
|
12584
|
-
inputSchema:
|
|
12903
|
+
inputSchema: z59.object({
|
|
12585
12904
|
profile: profileSchema.describe("Target execution profile")
|
|
12586
12905
|
}).strict(),
|
|
12587
12906
|
outputSchema: handoffResultSchema,
|
|
@@ -12602,7 +12921,7 @@ function createHandoffTool(handoff) {
|
|
|
12602
12921
|
}
|
|
12603
12922
|
|
|
12604
12923
|
// src/chat/tools/web/fetch-tool.ts
|
|
12605
|
-
import { z as
|
|
12924
|
+
import { z as z60 } from "zod";
|
|
12606
12925
|
|
|
12607
12926
|
// src/chat/tools/web/constants.ts
|
|
12608
12927
|
var USER_AGENT = "junior-bot/0.1";
|
|
@@ -13045,9 +13364,9 @@ function createWebFetchTool(hooks, options = {}) {
|
|
|
13045
13364
|
openWorldHint: true,
|
|
13046
13365
|
readOnlyHint: true
|
|
13047
13366
|
},
|
|
13048
|
-
inputSchema:
|
|
13049
|
-
url:
|
|
13050
|
-
max_chars:
|
|
13367
|
+
inputSchema: z60.object({
|
|
13368
|
+
url: z60.string().min(1).describe("HTTP(S) URL to fetch."),
|
|
13369
|
+
max_chars: z60.coerce.number().int().min(500).max(MAX_FETCH_CHARS).describe("Optional maximum number of extracted characters to return.").optional()
|
|
13051
13370
|
}),
|
|
13052
13371
|
outputSchema: juniorToolOutputSchema,
|
|
13053
13372
|
execute: async ({ url, max_chars }) => {
|
|
@@ -13092,7 +13411,7 @@ function createWebFetchTool(hooks, options = {}) {
|
|
|
13092
13411
|
}
|
|
13093
13412
|
|
|
13094
13413
|
// src/chat/tools/web/search.ts
|
|
13095
|
-
import { z as
|
|
13414
|
+
import { z as z61 } from "zod";
|
|
13096
13415
|
import { generateText } from "ai";
|
|
13097
13416
|
import { createGatewayProvider } from "@ai-sdk/gateway";
|
|
13098
13417
|
var SEARCH_TIMEOUT_MS = 6e4;
|
|
@@ -13134,9 +13453,9 @@ function createWebSearchTool(modelId, override) {
|
|
|
13134
13453
|
openWorldHint: true,
|
|
13135
13454
|
readOnlyHint: true
|
|
13136
13455
|
},
|
|
13137
|
-
inputSchema:
|
|
13138
|
-
query:
|
|
13139
|
-
max_results:
|
|
13456
|
+
inputSchema: z61.object({
|
|
13457
|
+
query: z61.string().min(1).max(500).describe("Search query."),
|
|
13458
|
+
max_results: z61.coerce.number().int().min(1).max(MAX_RESULTS2).describe("Max results to return.").optional()
|
|
13140
13459
|
}),
|
|
13141
13460
|
outputSchema: juniorToolOutputSchema,
|
|
13142
13461
|
execute: async ({ query, max_results }) => {
|
|
@@ -13178,7 +13497,7 @@ function createWebSearchTool(modelId, override) {
|
|
|
13178
13497
|
}
|
|
13179
13498
|
|
|
13180
13499
|
// src/chat/tools/sandbox/write-file.ts
|
|
13181
|
-
import { z as
|
|
13500
|
+
import { z as z62 } from "zod";
|
|
13182
13501
|
function createWriteFileTool() {
|
|
13183
13502
|
return zodTool({
|
|
13184
13503
|
annotations: {
|
|
@@ -13189,9 +13508,9 @@ function createWriteFileTool() {
|
|
|
13189
13508
|
},
|
|
13190
13509
|
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.",
|
|
13191
13510
|
executionMode: "sequential",
|
|
13192
|
-
inputSchema:
|
|
13193
|
-
path:
|
|
13194
|
-
content:
|
|
13511
|
+
inputSchema: z62.object({
|
|
13512
|
+
path: z62.string().min(1).describe("Path to write in the sandbox workspace."),
|
|
13513
|
+
content: z62.string().describe("UTF-8 file content to write.")
|
|
13195
13514
|
}),
|
|
13196
13515
|
outputSchema: juniorToolOutputSchema,
|
|
13197
13516
|
execute: async () => {
|
|
@@ -13268,6 +13587,18 @@ function createTools(availableSkills, hooks = {}, context, options = {}) {
|
|
|
13268
13587
|
hooks.toolOverrides?.imageGenerate
|
|
13269
13588
|
);
|
|
13270
13589
|
}
|
|
13590
|
+
if (context.attachmentStorage && context.conversationId) {
|
|
13591
|
+
tools.publishImage = createPublishImageTool({
|
|
13592
|
+
conversationId: context.conversationId,
|
|
13593
|
+
db: getSqlExecutor(),
|
|
13594
|
+
storage: context.attachmentStorage,
|
|
13595
|
+
workspace: context.workspace
|
|
13596
|
+
});
|
|
13597
|
+
tools.unpublishImage = createUnpublishImageTool({
|
|
13598
|
+
conversationId: context.conversationId,
|
|
13599
|
+
db: getSqlExecutor()
|
|
13600
|
+
});
|
|
13601
|
+
}
|
|
13271
13602
|
if (context.handoff) {
|
|
13272
13603
|
tools.handoff = createHandoffTool(context.handoff);
|
|
13273
13604
|
}
|
|
@@ -13519,6 +13850,9 @@ function buildSandboxInput(toolName, params) {
|
|
|
13519
13850
|
import { PluginToolInputError } from "@sentry/junior-plugin-api";
|
|
13520
13851
|
|
|
13521
13852
|
// src/chat/tool-support/action-review.ts
|
|
13853
|
+
import {
|
|
13854
|
+
missingToolAnnotationKeys as missingToolAnnotationKeys2
|
|
13855
|
+
} from "@sentry/junior-plugin-api";
|
|
13522
13856
|
var ACTION_CONFIRMATION_INSTRUCTIONS = [
|
|
13523
13857
|
"The action was not executed because explicit user confirmation is required.",
|
|
13524
13858
|
"Stop tool use for this turn and respond to the user now with a direct, concise confirmation question that names the exact action, target, and material side effects.",
|
|
@@ -13531,6 +13865,13 @@ var ACTION_DENIAL_INSTRUCTIONS = [
|
|
|
13531
13865
|
"If the reason is missing or withheld authorization and exact confirmation could make the action allowable, stop tool use for this turn and respond to the user now with a direct, concise confirmation question that names the exact action, target, and material side effects.",
|
|
13532
13866
|
"Proceed only with a materially safer alternative. Otherwise, stop and explain the specific risk to the user without mentioning Guardian or internal review mechanics."
|
|
13533
13867
|
].join("\n");
|
|
13868
|
+
var ACTION_REVIEW_LIMIT_INSTRUCTIONS = [
|
|
13869
|
+
"This action was rejected due to unacceptable risk.",
|
|
13870
|
+
"Action review rejected three consecutive tool execution attempts.",
|
|
13871
|
+
"Do not retry this action or an equivalent write this turn.",
|
|
13872
|
+
"Stop tool use for this turn and respond to the user now with a direct, concise explanation of the risk.",
|
|
13873
|
+
"Do not mention Guardian, the runtime, policy, or internal review mechanics."
|
|
13874
|
+
].join("\n");
|
|
13534
13875
|
var ToolActionRejectedError = class extends Error {
|
|
13535
13876
|
decision;
|
|
13536
13877
|
reason;
|
|
@@ -13538,11 +13879,9 @@ var ToolActionRejectedError = class extends Error {
|
|
|
13538
13879
|
riskLevel;
|
|
13539
13880
|
userAuthorization;
|
|
13540
13881
|
constructor(decision, reason, assessment = {}) {
|
|
13541
|
-
|
|
13542
|
-
|
|
13543
|
-
Reason: ${reason}`
|
|
13544
|
-
Reason: ${reason}`
|
|
13545
|
-
);
|
|
13882
|
+
const instructions = assessment.instructions ?? (decision === "ask" ? ACTION_CONFIRMATION_INSTRUCTIONS : ACTION_DENIAL_INSTRUCTIONS);
|
|
13883
|
+
super(`${instructions}
|
|
13884
|
+
Reason: ${reason}`);
|
|
13546
13885
|
this.name = "ToolActionRejectedError";
|
|
13547
13886
|
this.decision = decision;
|
|
13548
13887
|
this.reason = reason;
|
|
@@ -13565,7 +13904,7 @@ var ToolActionReviewUnavailableError = class extends Error {
|
|
|
13565
13904
|
var ToolActionReviewLimitError = class extends Error {
|
|
13566
13905
|
constructor() {
|
|
13567
13906
|
super(
|
|
13568
|
-
"Action review rejected three consecutive tool execution attempts
|
|
13907
|
+
"Action review rejected three consecutive tool execution attempts."
|
|
13569
13908
|
);
|
|
13570
13909
|
this.name = "ToolActionReviewLimitError";
|
|
13571
13910
|
}
|
|
@@ -13582,6 +13921,9 @@ function effectiveApprovalMode(tool, resolved) {
|
|
|
13582
13921
|
return "review";
|
|
13583
13922
|
}
|
|
13584
13923
|
const annotations = resolved?.annotations ?? tool.annotations;
|
|
13924
|
+
if (missingToolAnnotationKeys2(annotations).length > 0) {
|
|
13925
|
+
return "review";
|
|
13926
|
+
}
|
|
13585
13927
|
if (annotations?.destructiveHint === true || annotations?.openWorldHint === true || annotations?.readOnlyHint === false) {
|
|
13586
13928
|
return "review";
|
|
13587
13929
|
}
|
|
@@ -13782,10 +14124,19 @@ function createToolActionReview(options) {
|
|
|
13782
14124
|
const reviewedAction = projectedRejection(proposal, decision);
|
|
13783
14125
|
appendVisibleRejection(priorRejections, reviewedAction);
|
|
13784
14126
|
consecutiveRejections += 1;
|
|
13785
|
-
|
|
13786
|
-
|
|
13787
|
-
|
|
13788
|
-
|
|
14127
|
+
const exhausted = consecutiveRejections >= MAX_CONSECUTIVE_REJECTIONS;
|
|
14128
|
+
if (exhausted) {
|
|
14129
|
+
logException(
|
|
14130
|
+
new ToolActionReviewLimitError(),
|
|
14131
|
+
"guardian.action_review.exhausted",
|
|
14132
|
+
{
|
|
14133
|
+
"app.guardian.decision": decision.decision,
|
|
14134
|
+
"app.guardian.risk_level": decision.riskLevel,
|
|
14135
|
+
"app.guardian.user_authorization": decision.userAuthorization,
|
|
14136
|
+
"gen_ai.tool.name": proposal.tool.name,
|
|
14137
|
+
"gen_ai.tool.call.id": toolCallId
|
|
14138
|
+
}
|
|
14139
|
+
);
|
|
13789
14140
|
}
|
|
13790
14141
|
pendingRejections.set(toolCallId, {
|
|
13791
14142
|
decision: decision.decision,
|
|
@@ -13798,7 +14149,8 @@ function createToolActionReview(options) {
|
|
|
13798
14149
|
throw new ToolActionRejectedError(decision.decision, decision.reason, {
|
|
13799
14150
|
riskLevel: decision.riskLevel,
|
|
13800
14151
|
reviewedAction,
|
|
13801
|
-
userAuthorization: decision.userAuthorization
|
|
14152
|
+
userAuthorization: decision.userAuthorization,
|
|
14153
|
+
...exhausted ? { instructions: ACTION_REVIEW_LIMIT_INSTRUCTIONS } : {}
|
|
13802
14154
|
});
|
|
13803
14155
|
},
|
|
13804
14156
|
projectToolResult(toolCallId, result) {
|
|
@@ -13893,7 +14245,7 @@ function handleToolExecutionError(error, toolName, toolCallId, shouldTrace, conv
|
|
|
13893
14245
|
}
|
|
13894
14246
|
|
|
13895
14247
|
// src/chat/tools/execute-tool.ts
|
|
13896
|
-
import { z as
|
|
14248
|
+
import { z as z63 } from "zod";
|
|
13897
14249
|
var EXECUTE_TOOL_NAME = "executeTool";
|
|
13898
14250
|
function createExecuteToolTool() {
|
|
13899
14251
|
return zodTool({
|
|
@@ -13905,11 +14257,11 @@ function createExecuteToolTool() {
|
|
|
13905
14257
|
},
|
|
13906
14258
|
description: "Execute any catalog tool by exact tool_name from searchTools or a tool-result execution hint. Put tool-specific parameters inside arguments.",
|
|
13907
14259
|
executionMode: "sequential",
|
|
13908
|
-
inputSchema:
|
|
13909
|
-
tool_name:
|
|
14260
|
+
inputSchema: z63.object({
|
|
14261
|
+
tool_name: z63.string().min(1).describe(
|
|
13910
14262
|
"Exact catalog tool_name returned by searchTools or a tool-result execution hint."
|
|
13911
14263
|
),
|
|
13912
|
-
arguments:
|
|
14264
|
+
arguments: z63.record(z63.string(), z63.unknown()).describe(
|
|
13913
14265
|
'Arguments matching the selected catalog tool schema, for example { "query": "..." }.'
|
|
13914
14266
|
).optional()
|
|
13915
14267
|
}).strict(),
|
|
@@ -13949,7 +14301,7 @@ function planToolExposure(tools) {
|
|
|
13949
14301
|
}
|
|
13950
14302
|
|
|
13951
14303
|
// src/chat/tools/search-tools.ts
|
|
13952
|
-
import { z as
|
|
14304
|
+
import { z as z64 } from "zod";
|
|
13953
14305
|
var SEARCH_TOOLS_NAME = "searchTools";
|
|
13954
14306
|
var DEFAULT_MAX_RESULTS2 = 5;
|
|
13955
14307
|
var MAX_RESULTS3 = 20;
|
|
@@ -13994,29 +14346,29 @@ var SEARCH_STOP_WORDS = /* @__PURE__ */ new Set([
|
|
|
13994
14346
|
"you",
|
|
13995
14347
|
"your"
|
|
13996
14348
|
]);
|
|
13997
|
-
var searchToolsSourceSchema =
|
|
13998
|
-
id:
|
|
13999
|
-
description:
|
|
14349
|
+
var searchToolsSourceSchema = z64.object({
|
|
14350
|
+
id: z64.string(),
|
|
14351
|
+
description: z64.string()
|
|
14000
14352
|
}).strict();
|
|
14001
|
-
var searchToolsToolSchema =
|
|
14002
|
-
tool_name:
|
|
14003
|
-
description:
|
|
14004
|
-
exposure:
|
|
14005
|
-
source:
|
|
14006
|
-
input_schema:
|
|
14007
|
-
call_notes:
|
|
14008
|
-
annotations:
|
|
14353
|
+
var searchToolsToolSchema = z64.object({
|
|
14354
|
+
tool_name: z64.string(),
|
|
14355
|
+
description: z64.string(),
|
|
14356
|
+
exposure: z64.enum(["direct", "deferred", "modelOnly", "hidden"]),
|
|
14357
|
+
source: z64.string().optional(),
|
|
14358
|
+
input_schema: z64.unknown(),
|
|
14359
|
+
call_notes: z64.array(z64.string()),
|
|
14360
|
+
annotations: z64.record(z64.string(), z64.unknown())
|
|
14009
14361
|
}).strict();
|
|
14010
14362
|
var searchToolsOutputSchema = juniorToolOutputSchema.extend({
|
|
14011
|
-
query:
|
|
14012
|
-
source:
|
|
14013
|
-
sources:
|
|
14014
|
-
total_catalog_tools:
|
|
14015
|
-
total_eligible_tools:
|
|
14016
|
-
total_matches:
|
|
14017
|
-
returned_tools:
|
|
14018
|
-
execution_tool:
|
|
14019
|
-
tools:
|
|
14363
|
+
query: z64.string().nullable(),
|
|
14364
|
+
source: z64.string().nullable(),
|
|
14365
|
+
sources: z64.array(searchToolsSourceSchema),
|
|
14366
|
+
total_catalog_tools: z64.number().int().nonnegative(),
|
|
14367
|
+
total_eligible_tools: z64.number().int().nonnegative(),
|
|
14368
|
+
total_matches: z64.number().int().nonnegative(),
|
|
14369
|
+
returned_tools: z64.number().int().nonnegative(),
|
|
14370
|
+
execution_tool: z64.literal("executeTool"),
|
|
14371
|
+
tools: z64.array(searchToolsToolSchema)
|
|
14020
14372
|
}).strict();
|
|
14021
14373
|
function terms(value) {
|
|
14022
14374
|
const chunks = value.match(/[\p{L}\p{N}]+/gu) ?? [];
|
|
@@ -14227,14 +14579,14 @@ function createSearchToolsTool(catalogTools) {
|
|
|
14227
14579
|
openWorldHint: false,
|
|
14228
14580
|
readOnlyHint: true
|
|
14229
14581
|
},
|
|
14230
|
-
inputSchema:
|
|
14231
|
-
query:
|
|
14582
|
+
inputSchema: z64.object({
|
|
14583
|
+
query: z64.string().nullable().describe(
|
|
14232
14584
|
"Optional search terms describing the tool, owner, action, or arguments needed. Empty string lists catalog tools."
|
|
14233
14585
|
).optional(),
|
|
14234
|
-
source:
|
|
14586
|
+
source: z64.string().nullable().describe(
|
|
14235
14587
|
"Optional source id to search within, such as a plugin source returned in sources."
|
|
14236
14588
|
).optional(),
|
|
14237
|
-
max_results:
|
|
14589
|
+
max_results: z64.number().int().min(1).max(MAX_RESULTS3).nullable().describe("Maximum matching catalog tool descriptors to return.").optional()
|
|
14238
14590
|
}).strict(),
|
|
14239
14591
|
outputSchema: searchToolsOutputSchema,
|
|
14240
14592
|
privateTraceResult: (result) => ({
|
|
@@ -14680,7 +15032,7 @@ function createMcpAuthOrchestration(input) {
|
|
|
14680
15032
|
}
|
|
14681
15033
|
|
|
14682
15034
|
// src/chat/egress/plugin.ts
|
|
14683
|
-
import { randomUUID as
|
|
15035
|
+
import { randomUUID as randomUUID4 } from "crypto";
|
|
14684
15036
|
|
|
14685
15037
|
// src/chat/sandbox/egress/credentials.ts
|
|
14686
15038
|
var HTTP_READ_METHODS = /* @__PURE__ */ new Set(["GET", "HEAD", "OPTIONS"]);
|
|
@@ -15434,8 +15786,8 @@ async function executeCredentialedEgressRequest(input) {
|
|
|
15434
15786
|
function credentialContextForPluginEgress(credentialContext) {
|
|
15435
15787
|
return {
|
|
15436
15788
|
credentials: credentialContext,
|
|
15437
|
-
contextId:
|
|
15438
|
-
egressId: `plugin-egress:${
|
|
15789
|
+
contextId: randomUUID4(),
|
|
15790
|
+
egressId: `plugin-egress:${randomUUID4()}`,
|
|
15439
15791
|
expiresAtMs: Date.now() + 30 * 60 * 1e3
|
|
15440
15792
|
};
|
|
15441
15793
|
}
|
|
@@ -15493,7 +15845,7 @@ function createPluginEgress(deps) {
|
|
|
15493
15845
|
}
|
|
15494
15846
|
|
|
15495
15847
|
// src/chat/services/guardian-action-review.ts
|
|
15496
|
-
import { z as
|
|
15848
|
+
import { z as z65 } from "zod";
|
|
15497
15849
|
|
|
15498
15850
|
// src/chat/services/guardian-action-policy.ts
|
|
15499
15851
|
var GUARDIAN_ACTION_POLICY = `You are judging one planned Junior agent action.
|
|
@@ -15561,17 +15913,17 @@ Assess the exact action's intrinsic risk and whether the user's intent authorize
|
|
|
15561
15913
|
- Keep the reason to one concise, specific sentence grounded in the main risk or authorization fact. For ask, name the exact action, target, and material side effects the user must confirm.`;
|
|
15562
15914
|
|
|
15563
15915
|
// src/chat/services/guardian-action-review.ts
|
|
15564
|
-
var guardianDecisionSchema =
|
|
15565
|
-
decision:
|
|
15566
|
-
reason:
|
|
15567
|
-
riskLevel:
|
|
15568
|
-
userAuthorization:
|
|
15916
|
+
var guardianDecisionSchema = z65.object({
|
|
15917
|
+
decision: z65.enum(["allow", "ask", "deny"]),
|
|
15918
|
+
reason: z65.string().trim().min(1).max(500),
|
|
15919
|
+
riskLevel: z65.enum(["low", "medium", "high", "critical"]),
|
|
15920
|
+
userAuthorization: z65.enum(["high", "medium", "low", "unknown"])
|
|
15569
15921
|
}).strict();
|
|
15570
15922
|
var GUARDIAN_REVIEW_TIMEOUT_MS = 6e4;
|
|
15571
15923
|
var GUARDIAN_REVIEW_MAX_TOKENS = 2e3;
|
|
15572
15924
|
var MAX_PROPOSAL_CHARS = 192e3;
|
|
15573
15925
|
function guardianPrompt(proposal) {
|
|
15574
|
-
const proposalJson = JSON.stringify(
|
|
15926
|
+
const proposalJson = JSON.stringify(z65.json().parse(proposal), null, 2);
|
|
15575
15927
|
if (proposalJson.length > MAX_PROPOSAL_CHARS) {
|
|
15576
15928
|
throw new Error(
|
|
15577
15929
|
`Guardian action proposal exceeds ${MAX_PROPOSAL_CHARS} characters.`
|
|
@@ -18167,6 +18519,9 @@ export {
|
|
|
18167
18519
|
listThreadReplies,
|
|
18168
18520
|
renderBlockText,
|
|
18169
18521
|
renderAttachmentText,
|
|
18522
|
+
readPublicArtifact,
|
|
18523
|
+
publicArtifactHeaders,
|
|
18524
|
+
unpublishedArtifactHeaders,
|
|
18170
18525
|
latestReportedProgress,
|
|
18171
18526
|
executeCredentialedEgressRequest,
|
|
18172
18527
|
createContextCompactor,
|