@sentry/junior 0.60.1 → 0.62.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/app.js +203 -96
- package/dist/chat/respond-helpers.d.ts +1 -1
- package/dist/chat/runtime/turn-preparation.d.ts +0 -1
- package/dist/chat/sandbox/sandbox.d.ts +1 -0
- package/dist/chat/sandbox/session.d.ts +2 -0
- package/dist/chat/slack/assistant-thread/title.d.ts +9 -4
- package/dist/chat/tools/definition.d.ts +11 -0
- package/package.json +3 -3
package/dist/app.js
CHANGED
|
@@ -2969,13 +2969,7 @@ var editReplacementSchema = Type2.Object(
|
|
|
2969
2969
|
);
|
|
2970
2970
|
function createEditFileTool() {
|
|
2971
2971
|
return tool({
|
|
2972
|
-
description: "Edit one sandbox workspace file with exact text replacements. Use for precise changes to existing files. Each oldText must match exactly, be unique, and not overlap another edit. Returns a diff.",
|
|
2973
|
-
promptSnippet: "existing-file exact edits; returns diff",
|
|
2974
|
-
promptGuidelines: [
|
|
2975
|
-
"prefer over writeFile for targeted changes",
|
|
2976
|
-
"oldText exact, unique, non-overlapping",
|
|
2977
|
-
"multiple same-file changes: one edits[] call"
|
|
2978
|
-
],
|
|
2972
|
+
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.",
|
|
2979
2973
|
prepareArguments: prepareEditFileArguments,
|
|
2980
2974
|
executionMode: "sequential",
|
|
2981
2975
|
inputSchema: Type2.Object(
|
|
@@ -5406,13 +5400,7 @@ function createSlackCanvasReadTool() {
|
|
|
5406
5400
|
}
|
|
5407
5401
|
function createSlackCanvasEditTool(state) {
|
|
5408
5402
|
return tool({
|
|
5409
|
-
description: "Edit one Slack canvas with exact markdown replacements. Use for precise changes to existing Canvas content. Each oldText must match exactly, be unique, and not overlap another edit. Returns a diff.",
|
|
5410
|
-
promptSnippet: "existing-canvas exact edits; returns diff",
|
|
5411
|
-
promptGuidelines: [
|
|
5412
|
-
"prefer over slackCanvasWrite for targeted changes",
|
|
5413
|
-
"oldText exact, unique, non-overlapping",
|
|
5414
|
-
"multiple same-canvas changes: one edits[] call"
|
|
5415
|
-
],
|
|
5403
|
+
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.",
|
|
5416
5404
|
prepareArguments: prepareCanvasEditArguments,
|
|
5417
5405
|
executionMode: "sequential",
|
|
5418
5406
|
inputSchema: Type16.Object(
|
|
@@ -5499,9 +5487,7 @@ function createSlackCanvasEditTool(state) {
|
|
|
5499
5487
|
}
|
|
5500
5488
|
function createSlackCanvasWriteTool(state) {
|
|
5501
5489
|
return tool({
|
|
5502
|
-
description: "Write UTF-8 markdown content to a Slack canvas. Use for deliberate full-Canvas replacement after validation
|
|
5503
|
-
promptSnippet: "deliberate full-canvas replacement",
|
|
5504
|
-
promptGuidelines: ["targeted existing-canvas changes: slackCanvasEdit"],
|
|
5490
|
+
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.",
|
|
5505
5491
|
executionMode: "sequential",
|
|
5506
5492
|
inputSchema: Type16.Object(
|
|
5507
5493
|
{
|
|
@@ -6599,15 +6585,22 @@ function summarizeMessageText(text) {
|
|
|
6599
6585
|
}
|
|
6600
6586
|
return normalized.length > 1200 ? `${normalized.slice(0, 1200)}...` : normalized;
|
|
6601
6587
|
}
|
|
6588
|
+
function isStructuredThreadContext(context) {
|
|
6589
|
+
return /^<thread-(compactions|transcript)>/.test(context);
|
|
6590
|
+
}
|
|
6591
|
+
function renderThreadContextForPrompt(context) {
|
|
6592
|
+
if (isStructuredThreadContext(context)) {
|
|
6593
|
+
return context;
|
|
6594
|
+
}
|
|
6595
|
+
return ["<thread-background>", context, "</thread-background>"].join("\n");
|
|
6596
|
+
}
|
|
6602
6597
|
function buildUserTurnText(userInput, conversationContext) {
|
|
6603
6598
|
const trimmedContext = conversationContext?.trim();
|
|
6604
6599
|
if (!trimmedContext) {
|
|
6605
6600
|
return userInput;
|
|
6606
6601
|
}
|
|
6607
6602
|
return [
|
|
6608
|
-
|
|
6609
|
-
trimmedContext,
|
|
6610
|
-
"</thread-background>",
|
|
6603
|
+
renderThreadContextForPrompt(trimmedContext),
|
|
6611
6604
|
"",
|
|
6612
6605
|
"<current-instruction>",
|
|
6613
6606
|
userInput,
|
|
@@ -6816,7 +6809,7 @@ function createStateAdvisorSessionStore() {
|
|
|
6816
6809
|
}
|
|
6817
6810
|
|
|
6818
6811
|
// src/chat/tools/advisor/tool.ts
|
|
6819
|
-
var ADVISOR_TOOL_DESCRIPTION = "
|
|
6812
|
+
var ADVISOR_TOOL_DESCRIPTION = "Use this before committing to a non-obvious technical plan or declaring complex work complete. Call proactively after initial orientation when repository context matters \u2014 especially for architecture, algorithms, data modeling, concurrency, security-sensitive logic, unclear requirements, broad refactors, difficult debugging, repeated failures, or high-risk changes. Provide a focused question plus curated context: exact evidence, constraints, relevant code snippets, command output, diffs, current plan, and alternatives considered. The advisor does not automatically receive the parent transcript, keeps its own advisor history for this parent conversation, and can use read-only inspection tools to verify evidence. Follow-up calls can build on prior advisor guidance but must include new evidence or changed constraints. Do not use for greetings, simple deterministic edits, routine formatting, or when the next action is obvious from fresh tool output.";
|
|
6820
6813
|
var ADVISOR_SYSTEM_PROMPT = [
|
|
6821
6814
|
"You are a senior technical advisor for the executor.",
|
|
6822
6815
|
"Analyze the executor-supplied context deeply. Use read-only tools when direct inspection or verification would materially improve the advice.",
|
|
@@ -7660,9 +7653,7 @@ function createWebSearchTool(override) {
|
|
|
7660
7653
|
import { Type as Type24 } from "@sinclair/typebox";
|
|
7661
7654
|
function createWriteFileTool() {
|
|
7662
7655
|
return tool({
|
|
7663
|
-
description: "Write UTF-8 content to a file in the sandbox workspace. Use for intentional file creation or replacement after validation. Do not use for exploratory analysis-only turns.",
|
|
7664
|
-
promptSnippet: "new file or deliberate full-file replacement",
|
|
7665
|
-
promptGuidelines: ["targeted existing-file changes: editFile"],
|
|
7656
|
+
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.",
|
|
7666
7657
|
executionMode: "sequential",
|
|
7667
7658
|
inputSchema: Type24.Object(
|
|
7668
7659
|
{
|
|
@@ -8635,6 +8626,7 @@ async function syncSkillsToSandbox(params) {
|
|
|
8635
8626
|
|
|
8636
8627
|
// src/chat/sandbox/session.ts
|
|
8637
8628
|
var DEFAULT_MAX_OUTPUT_LENGTH = 3e4;
|
|
8629
|
+
var DEFAULT_BASH_COMMAND_TIMEOUT_MS = 5 * 60 * 1e3;
|
|
8638
8630
|
var SANDBOX_RUNTIME = "node22";
|
|
8639
8631
|
var SANDBOX_RUNTIME_BIN_DIR = `${SANDBOX_WORKSPACE_ROOT}/.junior/bin`;
|
|
8640
8632
|
var SNAPSHOT_BOOT_RETRY_COUNT = 3;
|
|
@@ -8707,6 +8699,16 @@ function getCommandStreamInterruptedResult() {
|
|
|
8707
8699
|
stderrTruncated: false
|
|
8708
8700
|
};
|
|
8709
8701
|
}
|
|
8702
|
+
function getCommandAbortedResult() {
|
|
8703
|
+
return {
|
|
8704
|
+
stdout: "",
|
|
8705
|
+
stderr: "Command aborted because the agent turn was cancelled.",
|
|
8706
|
+
exitCode: 130,
|
|
8707
|
+
stdoutTruncated: false,
|
|
8708
|
+
stderrTruncated: false,
|
|
8709
|
+
aborted: true
|
|
8710
|
+
};
|
|
8711
|
+
}
|
|
8710
8712
|
function createSandboxSessionManager(options) {
|
|
8711
8713
|
let sandbox = null;
|
|
8712
8714
|
let sandboxIdHint = options?.sandboxId;
|
|
@@ -8962,7 +8964,9 @@ function createSandboxSessionManager(options) {
|
|
|
8962
8964
|
"sandbox_hint_discarded_profile_mismatch",
|
|
8963
8965
|
traceContext,
|
|
8964
8966
|
{
|
|
8965
|
-
...options?.sandboxDependencyProfileHash ? {
|
|
8967
|
+
...options?.sandboxDependencyProfileHash ? {
|
|
8968
|
+
"app.sandbox.previous_profile_hash": options.sandboxDependencyProfileHash
|
|
8969
|
+
} : {},
|
|
8966
8970
|
...dependencyProfileHash ? { "app.sandbox.current_profile_hash": dependencyProfileHash } : {}
|
|
8967
8971
|
},
|
|
8968
8972
|
"Dependency profile changed; discarding sandbox hint and creating fresh session"
|
|
@@ -9130,37 +9134,63 @@ function createSandboxSessionManager(options) {
|
|
|
9130
9134
|
return {
|
|
9131
9135
|
bash: async (input) => {
|
|
9132
9136
|
let timedOut = false;
|
|
9137
|
+
let aborted = false;
|
|
9133
9138
|
let timeoutId;
|
|
9139
|
+
let onAbort;
|
|
9134
9140
|
try {
|
|
9141
|
+
if (input.signal?.aborted) {
|
|
9142
|
+
return getCommandAbortedResult();
|
|
9143
|
+
}
|
|
9135
9144
|
await refreshNetworkPolicy(sandboxInstance);
|
|
9145
|
+
if (input.signal?.aborted) {
|
|
9146
|
+
return getCommandAbortedResult();
|
|
9147
|
+
}
|
|
9136
9148
|
const sandboxCommandEnv = await resolveCommandEnv();
|
|
9149
|
+
if (input.signal?.aborted) {
|
|
9150
|
+
return getCommandAbortedResult();
|
|
9151
|
+
}
|
|
9137
9152
|
const script = buildNonInteractiveShellScript(input.command, {
|
|
9138
9153
|
env: { ...sandboxCommandEnv, ...input.env ?? {} },
|
|
9139
9154
|
pathPrefix: `${SANDBOX_RUNTIME_BIN_DIR}:$PATH`
|
|
9140
9155
|
});
|
|
9141
|
-
const controller =
|
|
9142
|
-
|
|
9156
|
+
const controller = new AbortController();
|
|
9157
|
+
const timeoutMs2 = input.timeoutMs && input.timeoutMs > 0 ? input.timeoutMs : DEFAULT_BASH_COMMAND_TIMEOUT_MS;
|
|
9158
|
+
onAbort = () => {
|
|
9159
|
+
aborted = true;
|
|
9160
|
+
controller.abort(input.signal?.reason);
|
|
9161
|
+
};
|
|
9162
|
+
if (input.signal) {
|
|
9163
|
+
input.signal.addEventListener("abort", onAbort, { once: true });
|
|
9164
|
+
if (input.signal.aborted) {
|
|
9165
|
+
onAbort();
|
|
9166
|
+
}
|
|
9167
|
+
}
|
|
9168
|
+
timeoutId = setTimeout(() => {
|
|
9143
9169
|
timedOut = true;
|
|
9144
9170
|
controller.abort();
|
|
9145
|
-
},
|
|
9171
|
+
}, timeoutMs2);
|
|
9172
|
+
timeoutId.unref?.();
|
|
9146
9173
|
const commandResult2 = await sandboxInstance.runCommand({
|
|
9147
9174
|
cmd: "bash",
|
|
9148
9175
|
args: ["-c", script],
|
|
9149
9176
|
cwd: SANDBOX_WORKSPACE_ROOT,
|
|
9150
|
-
|
|
9177
|
+
signal: controller.signal
|
|
9151
9178
|
});
|
|
9152
9179
|
return await readCommandOutput(commandResult2);
|
|
9153
9180
|
} catch (error) {
|
|
9154
9181
|
if (timedOut) {
|
|
9155
9182
|
return {
|
|
9156
9183
|
stdout: "",
|
|
9157
|
-
stderr: `Command timed out after ${input.timeoutMs}ms`,
|
|
9184
|
+
stderr: `Command timed out after ${input.timeoutMs && input.timeoutMs > 0 ? input.timeoutMs : DEFAULT_BASH_COMMAND_TIMEOUT_MS}ms`,
|
|
9158
9185
|
exitCode: 124,
|
|
9159
9186
|
stdoutTruncated: false,
|
|
9160
9187
|
stderrTruncated: false,
|
|
9161
9188
|
timedOut: true
|
|
9162
9189
|
};
|
|
9163
9190
|
}
|
|
9191
|
+
if (aborted || input.signal?.aborted) {
|
|
9192
|
+
return getCommandAbortedResult();
|
|
9193
|
+
}
|
|
9164
9194
|
if (isSandboxCommandStreamInterruptedError(error)) {
|
|
9165
9195
|
return getCommandStreamInterruptedResult();
|
|
9166
9196
|
}
|
|
@@ -9169,6 +9199,9 @@ function createSandboxSessionManager(options) {
|
|
|
9169
9199
|
if (timeoutId) {
|
|
9170
9200
|
clearTimeout(timeoutId);
|
|
9171
9201
|
}
|
|
9202
|
+
if (input.signal && onAbort) {
|
|
9203
|
+
input.signal.removeEventListener("abort", onAbort);
|
|
9204
|
+
}
|
|
9172
9205
|
}
|
|
9173
9206
|
},
|
|
9174
9207
|
readFile: async (input) => await executeReadFile(input, {
|
|
@@ -9344,7 +9377,7 @@ function createSandboxExecutor(options) {
|
|
|
9344
9377
|
"Sandbox boot requested"
|
|
9345
9378
|
);
|
|
9346
9379
|
};
|
|
9347
|
-
const executeBashTool = async (rawInput, command) => {
|
|
9380
|
+
const executeBashTool = async (rawInput, command, signal) => {
|
|
9348
9381
|
const env = parseEnv(rawInput.env);
|
|
9349
9382
|
const timeoutMs = positiveInteger(rawInput.timeoutMs);
|
|
9350
9383
|
logSandboxBootRequest("tool.bash", {
|
|
@@ -9362,7 +9395,8 @@ function createSandboxExecutor(options) {
|
|
|
9362
9395
|
const response = await executeBash({
|
|
9363
9396
|
command,
|
|
9364
9397
|
...env ? { env } : {},
|
|
9365
|
-
...timeoutMs ? { timeoutMs } : {}
|
|
9398
|
+
...timeoutMs ? { timeoutMs } : {},
|
|
9399
|
+
...signal ? { signal } : {}
|
|
9366
9400
|
});
|
|
9367
9401
|
setSpanAttributes({
|
|
9368
9402
|
"process.exit.code": response.exitCode,
|
|
@@ -9632,7 +9666,7 @@ function createSandboxExecutor(options) {
|
|
|
9632
9666
|
return { result: custom.result };
|
|
9633
9667
|
}
|
|
9634
9668
|
}
|
|
9635
|
-
return await executeBashTool(rawInput, bashCommand);
|
|
9669
|
+
return await executeBashTool(rawInput, bashCommand, params.signal);
|
|
9636
9670
|
}
|
|
9637
9671
|
try {
|
|
9638
9672
|
if (params.toolName === "readFile") {
|
|
@@ -10471,7 +10505,7 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
|
|
|
10471
10505
|
parameters: toolDef.inputSchema,
|
|
10472
10506
|
prepareArguments: toolDef.prepareArguments,
|
|
10473
10507
|
executionMode: toolDef.executionMode,
|
|
10474
|
-
execute: async (toolCallId, params) => {
|
|
10508
|
+
execute: async (toolCallId, params, signal) => {
|
|
10475
10509
|
const normalizedToolCallId = typeof toolCallId === "string" && toolCallId.length > 0 ? toolCallId : void 0;
|
|
10476
10510
|
const toolArgumentsAttribute = serializeToolPayload(params);
|
|
10477
10511
|
const toolArgumentsMetadata = toGenAiPayloadTraceAttributes(
|
|
@@ -10519,9 +10553,11 @@ function createAgentTools(tools, sandbox, spanContext, onStatus, sandboxExecutor
|
|
|
10519
10553
|
const isSandbox = Boolean(sandboxExecutor?.canExecute(toolName));
|
|
10520
10554
|
const result = isSandbox ? await sandboxExecutor.execute({
|
|
10521
10555
|
toolName,
|
|
10522
|
-
input: sandboxInput
|
|
10556
|
+
input: sandboxInput,
|
|
10557
|
+
...signal ? { signal } : {}
|
|
10523
10558
|
}) : await toolDef.execute(toolInput, {
|
|
10524
|
-
experimental_context: sandbox
|
|
10559
|
+
experimental_context: sandbox,
|
|
10560
|
+
...signal ? { signal } : {}
|
|
10525
10561
|
});
|
|
10526
10562
|
const normalized = normalizeToolResult(result, isSandbox);
|
|
10527
10563
|
if (bashCommand && pluginAuthOrchestration) {
|
|
@@ -11500,13 +11536,13 @@ function buildClassifierSystemPrompt() {
|
|
|
11500
11536
|
"Choose exactly one bucket: none, low, medium, high, or xhigh.",
|
|
11501
11537
|
"",
|
|
11502
11538
|
"Use none only for greetings, acknowledgments, and turns that need no substantive assistant work.",
|
|
11503
|
-
"Use low rarely: only for deterministic one-step answers or transformations with no tools, no current/external facts, no thread-
|
|
11539
|
+
"Use low rarely: only for deterministic one-step answers or transformations with no tools, no current/external facts, no prior thread-context interpretation, and no source verification.",
|
|
11504
11540
|
"Use medium for normal assistant work: explanations, source-backed checks, thread follow-ups, tool choice, likely tool use, ambiguous asks, multi-step analysis, or anything where a confident but shallow answer would be risky.",
|
|
11505
11541
|
"Use high for research-heavy work, non-trivial drafting, or explicit requests to be thorough.",
|
|
11506
11542
|
"Use xhigh for the most complex tasks: code changes, debugging/root-cause analysis, broad refactors, architecture decisions, multi-file implementation, or any task where deep reasoning across multiple systems or files is required.",
|
|
11507
11543
|
"When unsure between two non-none buckets, choose the higher bucket. Do not use low as the default.",
|
|
11508
11544
|
"",
|
|
11509
|
-
"Classify based on the substance of the task, not the length of the current message. When the current instruction is a short affirmation (for example: 'go', 'do it', 'yes please', 'proceed') and
|
|
11545
|
+
"Classify based on the substance of the task, not the length of the current message. When the current instruction is a short affirmation (for example: 'go', 'do it', 'yes please', 'proceed') and prior thread context contains a pending task, classify the pending task \u2014 not the affirmation.",
|
|
11510
11546
|
"",
|
|
11511
11547
|
"Return JSON only with thinking_level, confidence, and reason.",
|
|
11512
11548
|
"confidence must be a number from 0 to 1, not a word label."
|
|
@@ -11515,12 +11551,17 @@ function buildClassifierSystemPrompt() {
|
|
|
11515
11551
|
function buildClassifierPrompt(args) {
|
|
11516
11552
|
const sections = [];
|
|
11517
11553
|
if (args.conversationContext) {
|
|
11518
|
-
|
|
11519
|
-
|
|
11520
|
-
|
|
11521
|
-
|
|
11522
|
-
|
|
11523
|
-
|
|
11554
|
+
const contextText = args.conversationContext.text;
|
|
11555
|
+
if (/^<thread-(compactions|transcript)>/.test(contextText)) {
|
|
11556
|
+
sections.push(contextText, "");
|
|
11557
|
+
} else {
|
|
11558
|
+
sections.push(
|
|
11559
|
+
"<thread-background>",
|
|
11560
|
+
contextText,
|
|
11561
|
+
"</thread-background>",
|
|
11562
|
+
""
|
|
11563
|
+
);
|
|
11564
|
+
}
|
|
11524
11565
|
}
|
|
11525
11566
|
sections.push(
|
|
11526
11567
|
"<current-instruction>",
|
|
@@ -12312,9 +12353,38 @@ function createMcpAuthOrchestration(deps, abortAgent) {
|
|
|
12312
12353
|
|
|
12313
12354
|
// src/chat/respond.ts
|
|
12314
12355
|
var PROVIDER_RETRY_DELAYS_MS = [1e3, 2e3];
|
|
12356
|
+
var AGENT_ABORT_SETTLE_GRACE_MS = 5e3;
|
|
12315
12357
|
function sleep3(ms) {
|
|
12316
12358
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
12317
12359
|
}
|
|
12360
|
+
function waitForAbortSettlement(promise, timeoutMs) {
|
|
12361
|
+
return new Promise((resolve) => {
|
|
12362
|
+
let done = false;
|
|
12363
|
+
const timeoutId = setTimeout(() => {
|
|
12364
|
+
if (!done) {
|
|
12365
|
+
done = true;
|
|
12366
|
+
resolve(false);
|
|
12367
|
+
}
|
|
12368
|
+
}, timeoutMs);
|
|
12369
|
+
timeoutId.unref?.();
|
|
12370
|
+
promise.then(
|
|
12371
|
+
() => {
|
|
12372
|
+
if (!done) {
|
|
12373
|
+
done = true;
|
|
12374
|
+
clearTimeout(timeoutId);
|
|
12375
|
+
resolve(true);
|
|
12376
|
+
}
|
|
12377
|
+
},
|
|
12378
|
+
() => {
|
|
12379
|
+
if (!done) {
|
|
12380
|
+
done = true;
|
|
12381
|
+
clearTimeout(timeoutId);
|
|
12382
|
+
resolve(true);
|
|
12383
|
+
}
|
|
12384
|
+
}
|
|
12385
|
+
);
|
|
12386
|
+
});
|
|
12387
|
+
}
|
|
12318
12388
|
var startupDiscoveryLogged = false;
|
|
12319
12389
|
var MAX_ROUTER_ATTACHMENT_PREVIEW_CHARS = 2e3;
|
|
12320
12390
|
function buildOmittedImageAttachmentNotice(count) {
|
|
@@ -13070,8 +13140,20 @@ async function generateAssistantReply(messageText2, context = {}) {
|
|
|
13070
13140
|
},
|
|
13071
13141
|
"Agent turn timed out and was aborted"
|
|
13072
13142
|
);
|
|
13073
|
-
await
|
|
13074
|
-
|
|
13143
|
+
const settled = await waitForAbortSettlement(
|
|
13144
|
+
run2,
|
|
13145
|
+
AGENT_ABORT_SETTLE_GRACE_MS
|
|
13146
|
+
);
|
|
13147
|
+
if (!settled) {
|
|
13148
|
+
logWarn(
|
|
13149
|
+
"agent_turn_abort_settle_timeout",
|
|
13150
|
+
{},
|
|
13151
|
+
{
|
|
13152
|
+
"app.ai.abort_settle_grace_ms": AGENT_ABORT_SETTLE_GRACE_MS
|
|
13153
|
+
},
|
|
13154
|
+
"Timed-out agent run did not settle after abort before resume snapshot"
|
|
13155
|
+
);
|
|
13156
|
+
}
|
|
13075
13157
|
timeoutResumeMessages = [...agent.state.messages];
|
|
13076
13158
|
}
|
|
13077
13159
|
if (getPendingAuthPause()) {
|
|
@@ -13463,20 +13545,25 @@ function buildConversationContext(conversation, options = {}) {
|
|
|
13463
13545
|
" </compaction>"
|
|
13464
13546
|
);
|
|
13465
13547
|
}
|
|
13466
|
-
lines.push("</thread-compactions>"
|
|
13548
|
+
lines.push("</thread-compactions>");
|
|
13467
13549
|
}
|
|
13468
|
-
|
|
13469
|
-
|
|
13470
|
-
|
|
13471
|
-
|
|
13472
|
-
|
|
13473
|
-
|
|
13474
|
-
|
|
13475
|
-
|
|
13476
|
-
"
|
|
13477
|
-
|
|
13550
|
+
if (messages.length > 0) {
|
|
13551
|
+
if (lines.length > 0) {
|
|
13552
|
+
lines.push("");
|
|
13553
|
+
}
|
|
13554
|
+
lines.push("<thread-transcript>");
|
|
13555
|
+
for (const [index, message] of messages.entries()) {
|
|
13556
|
+
const author = escapeXml(message.author?.userName ?? message.role);
|
|
13557
|
+
const ts = new Date(message.createdAtMs).toISOString();
|
|
13558
|
+
const slackTsAttr = message.meta?.slackTs ? ` slack_ts="${escapeXml(message.meta.slackTs)}"` : "";
|
|
13559
|
+
lines.push(
|
|
13560
|
+
` <message index="${index + 1}" ts="${ts}" role="${message.role}" author="${author}"${slackTsAttr}>`,
|
|
13561
|
+
renderConversationMessageLine(message, conversation),
|
|
13562
|
+
" </message>"
|
|
13563
|
+
);
|
|
13564
|
+
}
|
|
13565
|
+
lines.push("</thread-transcript>");
|
|
13478
13566
|
}
|
|
13479
|
-
lines.push("</thread-transcript>");
|
|
13480
13567
|
return lines.join("\n");
|
|
13481
13568
|
}
|
|
13482
13569
|
function pruneCompactions(compactions) {
|
|
@@ -19637,7 +19724,7 @@ function getSlackMessageTs(message) {
|
|
|
19637
19724
|
// src/chat/slack/assistant-thread/title.ts
|
|
19638
19725
|
function maybeUpdateAssistantTitle(args) {
|
|
19639
19726
|
const assistantThreadContext = args.assistantThreadContext;
|
|
19640
|
-
if (!assistantThreadContext?.channelId || !assistantThreadContext.threadTs
|
|
19727
|
+
if (!assistantThreadContext?.channelId || !assistantThreadContext.threadTs) {
|
|
19641
19728
|
return Promise.resolve(void 0);
|
|
19642
19729
|
}
|
|
19643
19730
|
const titleSourceMessage = getThreadTitleSourceMessage(args.conversation);
|
|
@@ -19647,40 +19734,12 @@ function maybeUpdateAssistantTitle(args) {
|
|
|
19647
19734
|
if (args.artifacts.assistantTitleSourceMessageId === titleSourceMessage.id) {
|
|
19648
19735
|
return Promise.resolve(void 0);
|
|
19649
19736
|
}
|
|
19737
|
+
const isDm = isDmChannel(assistantThreadContext.channelId);
|
|
19650
19738
|
return (async () => {
|
|
19739
|
+
let title;
|
|
19651
19740
|
try {
|
|
19652
|
-
|
|
19653
|
-
await args.getSlackAdapter().setAssistantTitle(
|
|
19654
|
-
assistantThreadContext.channelId,
|
|
19655
|
-
assistantThreadContext.threadTs,
|
|
19656
|
-
title
|
|
19657
|
-
);
|
|
19658
|
-
return { sourceMessageId: titleSourceMessage.id, title };
|
|
19741
|
+
title = await args.generateThreadTitle(titleSourceMessage.text);
|
|
19659
19742
|
} catch (error) {
|
|
19660
|
-
const slackErrorCode = getSlackApiErrorCode(error);
|
|
19661
|
-
const assistantTitleErrorAttributes = {
|
|
19662
|
-
"app.slack.assistant_title.outcome": "permission_denied",
|
|
19663
|
-
...slackErrorCode ? {
|
|
19664
|
-
"app.slack.assistant_title.error_code": slackErrorCode
|
|
19665
|
-
} : {}
|
|
19666
|
-
};
|
|
19667
|
-
if (isSlackTitlePermissionError(error)) {
|
|
19668
|
-
setSpanAttributes(assistantTitleErrorAttributes);
|
|
19669
|
-
logError(
|
|
19670
|
-
"thread_title_generation_permission_denied",
|
|
19671
|
-
{
|
|
19672
|
-
slackThreadId: args.threadId,
|
|
19673
|
-
slackUserId: args.requesterId,
|
|
19674
|
-
slackChannelId: args.channelId,
|
|
19675
|
-
runId: args.runId,
|
|
19676
|
-
assistantUserName: args.assistantUserName,
|
|
19677
|
-
modelId: args.modelId
|
|
19678
|
-
},
|
|
19679
|
-
assistantTitleErrorAttributes,
|
|
19680
|
-
"Skipping thread title update due to Slack permission error"
|
|
19681
|
-
);
|
|
19682
|
-
return { sourceMessageId: titleSourceMessage.id };
|
|
19683
|
-
}
|
|
19684
19743
|
logWarn(
|
|
19685
19744
|
"thread_title_generation_failed",
|
|
19686
19745
|
{
|
|
@@ -19698,6 +19757,56 @@ function maybeUpdateAssistantTitle(args) {
|
|
|
19698
19757
|
);
|
|
19699
19758
|
return void 0;
|
|
19700
19759
|
}
|
|
19760
|
+
if (isDm) {
|
|
19761
|
+
try {
|
|
19762
|
+
await args.getSlackAdapter().setAssistantTitle(
|
|
19763
|
+
assistantThreadContext.channelId,
|
|
19764
|
+
assistantThreadContext.threadTs,
|
|
19765
|
+
title
|
|
19766
|
+
);
|
|
19767
|
+
} catch (error) {
|
|
19768
|
+
const slackErrorCode = getSlackApiErrorCode(error);
|
|
19769
|
+
const assistantTitleErrorAttributes = {
|
|
19770
|
+
"app.slack.assistant_title.outcome": "permission_denied",
|
|
19771
|
+
...slackErrorCode ? {
|
|
19772
|
+
"app.slack.assistant_title.error_code": slackErrorCode
|
|
19773
|
+
} : {}
|
|
19774
|
+
};
|
|
19775
|
+
if (isSlackTitlePermissionError(error)) {
|
|
19776
|
+
setSpanAttributes(assistantTitleErrorAttributes);
|
|
19777
|
+
logError(
|
|
19778
|
+
"thread_title_generation_permission_denied",
|
|
19779
|
+
{
|
|
19780
|
+
slackThreadId: args.threadId,
|
|
19781
|
+
slackUserId: args.requesterId,
|
|
19782
|
+
slackChannelId: args.channelId,
|
|
19783
|
+
runId: args.runId,
|
|
19784
|
+
assistantUserName: args.assistantUserName,
|
|
19785
|
+
modelId: args.modelId
|
|
19786
|
+
},
|
|
19787
|
+
assistantTitleErrorAttributes,
|
|
19788
|
+
"Skipping Slack thread title update due to permission error"
|
|
19789
|
+
);
|
|
19790
|
+
} else {
|
|
19791
|
+
logWarn(
|
|
19792
|
+
"thread_title_slack_update_failed",
|
|
19793
|
+
{
|
|
19794
|
+
slackThreadId: args.threadId,
|
|
19795
|
+
slackUserId: args.requesterId,
|
|
19796
|
+
slackChannelId: args.channelId,
|
|
19797
|
+
runId: args.runId,
|
|
19798
|
+
assistantUserName: args.assistantUserName,
|
|
19799
|
+
modelId: args.modelId
|
|
19800
|
+
},
|
|
19801
|
+
{
|
|
19802
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
19803
|
+
},
|
|
19804
|
+
"Slack thread title update failed"
|
|
19805
|
+
);
|
|
19806
|
+
}
|
|
19807
|
+
}
|
|
19808
|
+
}
|
|
19809
|
+
return { sourceMessageId: titleSourceMessage.id, title };
|
|
19701
19810
|
})();
|
|
19702
19811
|
}
|
|
19703
19812
|
|
|
@@ -20111,7 +20220,7 @@ function createReplyToThread(deps) {
|
|
|
20111
20220
|
if (conversationId && loadedPiMessages.canCompact && piMessages?.length) {
|
|
20112
20221
|
const compaction = await deps.services.contextCompactor.maybeCompact({
|
|
20113
20222
|
conversation: preparedState.conversation,
|
|
20114
|
-
conversationContext: preparedState.
|
|
20223
|
+
conversationContext: preparedState.conversationContext,
|
|
20115
20224
|
conversationId,
|
|
20116
20225
|
metadata: {
|
|
20117
20226
|
threadId,
|
|
@@ -20153,7 +20262,7 @@ function createReplyToThread(deps) {
|
|
|
20153
20262
|
fullName: message.author.fullName ?? fallbackIdentity?.fullName,
|
|
20154
20263
|
email: fallbackIdentity?.email
|
|
20155
20264
|
},
|
|
20156
|
-
conversationContext: preparedState.
|
|
20265
|
+
conversationContext: preparedState.conversationContext,
|
|
20157
20266
|
artifactState: preparedState.artifacts,
|
|
20158
20267
|
piMessages,
|
|
20159
20268
|
pendingAuth: preparedState.conversation.processing.pendingAuth,
|
|
@@ -20714,8 +20823,7 @@ function createPrepareTurnState(deps) {
|
|
|
20714
20823
|
requesterId: args.context.requesterId,
|
|
20715
20824
|
runId: args.context.runId
|
|
20716
20825
|
});
|
|
20717
|
-
const conversationContext = buildConversationContext(conversation
|
|
20718
|
-
const routingContext = buildConversationContext(conversation, {
|
|
20826
|
+
const conversationContext = buildConversationContext(conversation, {
|
|
20719
20827
|
excludeMessageId: userMessageId
|
|
20720
20828
|
});
|
|
20721
20829
|
setSpanAttributes({
|
|
@@ -20730,7 +20838,6 @@ function createPrepareTurnState(deps) {
|
|
|
20730
20838
|
sandboxId: existingSandboxId,
|
|
20731
20839
|
sandboxDependencyProfileHash: existingSandboxDependencyProfileHash,
|
|
20732
20840
|
conversationContext,
|
|
20733
|
-
routingContext,
|
|
20734
20841
|
userMessageId
|
|
20735
20842
|
};
|
|
20736
20843
|
};
|
|
@@ -20777,7 +20884,7 @@ function createSlackRuntime(options) {
|
|
|
20777
20884
|
conversation: preparedState.conversation
|
|
20778
20885
|
});
|
|
20779
20886
|
},
|
|
20780
|
-
getPreparedConversationContext: (preparedState) => preparedState.
|
|
20887
|
+
getPreparedConversationContext: (preparedState) => preparedState.conversationContext,
|
|
20781
20888
|
decideSubscribedReply: services.subscribedReplyPolicy,
|
|
20782
20889
|
recordSkippedSubscribedMessage: async ({
|
|
20783
20890
|
thread,
|
|
@@ -44,7 +44,7 @@ export declare function toObservablePromptPart(part: {
|
|
|
44
44
|
export declare function summarizeMessageText(text: string): string;
|
|
45
45
|
/**
|
|
46
46
|
* Put prior thread text before the current instruction when no Pi history
|
|
47
|
-
* exists.
|
|
47
|
+
* exists. Structured thread XML is already a top-level prompt block.
|
|
48
48
|
*/
|
|
49
49
|
export declare function buildUserTurnText(userInput: string, conversationContext?: string): string;
|
|
50
50
|
/** Encode a non-image attachment as base64 XML for the prompt. */
|
|
@@ -8,7 +8,6 @@ export interface PreparedTurnState {
|
|
|
8
8
|
channelConfiguration?: ChannelConfigurationService;
|
|
9
9
|
conversation: ThreadConversationState;
|
|
10
10
|
conversationContext?: string;
|
|
11
|
-
routingContext?: string;
|
|
12
11
|
sandboxId?: string;
|
|
13
12
|
sandboxDependencyProfileHash?: string;
|
|
14
13
|
userMessageId?: string;
|
|
@@ -6,6 +6,7 @@ interface SandboxToolExecutors {
|
|
|
6
6
|
bash: (input: {
|
|
7
7
|
command: string;
|
|
8
8
|
env?: Record<string, string>;
|
|
9
|
+
signal?: AbortSignal;
|
|
9
10
|
timeoutMs?: number;
|
|
10
11
|
}) => Promise<{
|
|
11
12
|
stdout: string;
|
|
@@ -13,6 +14,7 @@ interface SandboxToolExecutors {
|
|
|
13
14
|
exitCode: number;
|
|
14
15
|
stdoutTruncated: boolean;
|
|
15
16
|
stderrTruncated: boolean;
|
|
17
|
+
aborted?: boolean;
|
|
16
18
|
timedOut?: boolean;
|
|
17
19
|
}>;
|
|
18
20
|
readFile: (input: {
|
|
@@ -3,12 +3,17 @@ import { type ConversationMemoryService } from "@/chat/services/conversation-mem
|
|
|
3
3
|
import type { ThreadArtifactsState } from "@/chat/state/artifacts";
|
|
4
4
|
import type { ThreadConversationState } from "@/chat/state/conversation";
|
|
5
5
|
/**
|
|
6
|
-
* Best-effort
|
|
6
|
+
* Best-effort conversation title generation for all Slack conversations.
|
|
7
7
|
*
|
|
8
8
|
* Title generation is intentionally detached from reply generation and visible
|
|
9
|
-
* reply delivery.
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* reply delivery. For DM assistant threads the generated title is also pushed
|
|
10
|
+
* to Slack via `setAssistantTitle`. For channel conversations the title is
|
|
11
|
+
* generated and returned for dashboard reporting only — the Slack API for
|
|
12
|
+
* setting thread titles is DM-only and is not called.
|
|
13
|
+
*
|
|
14
|
+
* Stable Slack permission failures on DM title updates are treated as a
|
|
15
|
+
* terminal skip for the current source message so later turns do not keep
|
|
16
|
+
* paying for the same fast-model call that Slack will reject.
|
|
12
17
|
*/
|
|
13
18
|
export declare function maybeUpdateAssistantTitle(args: {
|
|
14
19
|
assistantThreadContext?: {
|
|
@@ -5,12 +5,23 @@ export interface ToolDefinition<TInputSchema extends TSchema = TSchema> {
|
|
|
5
5
|
description: string;
|
|
6
6
|
inputSchema: TInputSchema;
|
|
7
7
|
annotations?: ToolAnnotations;
|
|
8
|
+
/**
|
|
9
|
+
* @deprecated Put tool-selection and usage guidance directly in `description`
|
|
10
|
+
* and parameter descriptions. Retained for plugin compatibility; may be
|
|
11
|
+
* removed in a future major version.
|
|
12
|
+
*/
|
|
8
13
|
promptSnippet?: string;
|
|
14
|
+
/**
|
|
15
|
+
* @deprecated Put tool-selection and usage guidance directly in `description`
|
|
16
|
+
* and parameter descriptions. Retained for plugin compatibility; may be
|
|
17
|
+
* removed in a future major version.
|
|
18
|
+
*/
|
|
9
19
|
promptGuidelines?: string[];
|
|
10
20
|
prepareArguments?: (args: unknown) => Static<TInputSchema>;
|
|
11
21
|
executionMode?: ToolExecutionMode;
|
|
12
22
|
execute?: (input: Static<TInputSchema>, options: {
|
|
13
23
|
experimental_context?: unknown;
|
|
24
|
+
signal?: AbortSignal;
|
|
14
25
|
}) => Promise<unknown> | unknown;
|
|
15
26
|
}
|
|
16
27
|
/** Infer execute parameter types from the inputSchema via generic binding. */
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sentry/junior",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.62.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -64,7 +64,7 @@
|
|
|
64
64
|
"node-html-markdown": "^2.0.0",
|
|
65
65
|
"yaml": "^2.9.0",
|
|
66
66
|
"zod": "^4.4.3",
|
|
67
|
-
"@sentry/junior-plugin-api": "0.
|
|
67
|
+
"@sentry/junior-plugin-api": "0.62.0"
|
|
68
68
|
},
|
|
69
69
|
"devDependencies": {
|
|
70
70
|
"@types/node": "^25.9.1",
|
|
@@ -76,7 +76,7 @@
|
|
|
76
76
|
"typescript": "^6.0.3",
|
|
77
77
|
"vercel": "^54.4.0",
|
|
78
78
|
"vitest": "^4.1.7",
|
|
79
|
-
"@sentry/junior-scheduler": "0.
|
|
79
|
+
"@sentry/junior-scheduler": "0.62.0"
|
|
80
80
|
},
|
|
81
81
|
"scripts": {
|
|
82
82
|
"build": "tsup && tsc -p tsconfig.build.json --emitDeclarationOnly",
|