@sentry/junior 0.62.0 → 0.64.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/README.md +5 -1
- package/dist/api-reference.d.ts +2 -0
- package/dist/app.d.ts +5 -10
- package/dist/app.js +348 -874
- package/dist/build/virtual-config.d.ts +18 -2
- package/dist/chat/logging.d.ts +12 -2
- package/dist/chat/plugins/agent-hooks.d.ts +11 -4
- package/dist/chat/plugins/inline-manifest-source.d.ts +5 -0
- package/dist/chat/plugins/manifest.d.ts +5 -3
- package/dist/chat/plugins/package-discovery.d.ts +5 -0
- package/dist/chat/plugins/registry.d.ts +2 -2
- package/dist/chat/plugins/types.d.ts +8 -3
- package/dist/chat/prompt.d.ts +0 -1
- package/dist/chat/state/turn-session.d.ts +1 -1
- package/dist/{chunk-I4FDGMFI.js → chunk-4CRYMG7M.js} +787 -31
- package/dist/chunk-5VDO6LSG.js +104 -0
- package/dist/{chunk-ITOW4DED.js → chunk-D23WCM66.js} +2 -2
- package/dist/{chunk-FKEKRBUB.js → chunk-IGVHCX2U.js} +28 -2
- package/dist/{chunk-5LUISFEY.js → chunk-KVZL5NZS.js} +6 -1
- package/dist/{chunk-H652GMDH.js → chunk-WDPWFMCE.js} +297 -84
- package/dist/{chunk-QDGD5WVN.js → chunk-WZFQQ6SP.js} +3 -28
- package/dist/cli/check.js +9 -4
- package/dist/cli/snapshot-warmup.js +4 -4
- package/dist/nitro.d.ts +10 -3
- package/dist/nitro.js +161 -6
- package/dist/plugins.d.ts +22 -0
- package/dist/reporting.d.ts +26 -11
- package/dist/reporting.js +62 -25
- package/package.json +3 -3
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import {
|
|
2
|
+
SANDBOX_DATA_ROOT,
|
|
3
|
+
SANDBOX_WORKSPACE_ROOT,
|
|
4
|
+
botConfig,
|
|
2
5
|
getChatConfig,
|
|
3
6
|
getConnectedStateContext,
|
|
4
|
-
getStateAdapter
|
|
5
|
-
|
|
7
|
+
getStateAdapter,
|
|
8
|
+
sandboxSkillDir
|
|
9
|
+
} from "./chunk-IGVHCX2U.js";
|
|
6
10
|
import {
|
|
7
|
-
isRecord
|
|
8
|
-
|
|
11
|
+
isRecord,
|
|
12
|
+
logInfo,
|
|
13
|
+
logWarn
|
|
14
|
+
} from "./chunk-WDPWFMCE.js";
|
|
9
15
|
import {
|
|
10
16
|
sentry_exports
|
|
11
17
|
} from "./chunk-Z3YD6NHK.js";
|
|
18
|
+
import {
|
|
19
|
+
listReferenceFiles,
|
|
20
|
+
soulPathCandidates,
|
|
21
|
+
worldPathCandidates
|
|
22
|
+
} from "./chunk-KVZL5NZS.js";
|
|
12
23
|
|
|
13
24
|
// src/handlers/health.ts
|
|
14
25
|
function GET() {
|
|
@@ -19,6 +30,753 @@ function GET() {
|
|
|
19
30
|
});
|
|
20
31
|
}
|
|
21
32
|
|
|
33
|
+
// src/chat/prompt.ts
|
|
34
|
+
import fs from "fs";
|
|
35
|
+
import path from "path";
|
|
36
|
+
|
|
37
|
+
// src/chat/turn-context-tag.ts
|
|
38
|
+
var TURN_CONTEXT_TAG = "runtime-turn-context";
|
|
39
|
+
|
|
40
|
+
// src/chat/interruption-marker.ts
|
|
41
|
+
var INTERRUPTED_MARKER = "\n\n[Response interrupted before completion]";
|
|
42
|
+
function getInterruptionMarker() {
|
|
43
|
+
return INTERRUPTED_MARKER;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/chat/slack/status-format.ts
|
|
47
|
+
var SLACK_STATUS_MAX_LENGTH = 50;
|
|
48
|
+
function truncateStatusText(text) {
|
|
49
|
+
const trimmed = text.trim();
|
|
50
|
+
if (!trimmed) {
|
|
51
|
+
return "";
|
|
52
|
+
}
|
|
53
|
+
if (trimmed.length <= SLACK_STATUS_MAX_LENGTH) {
|
|
54
|
+
return trimmed;
|
|
55
|
+
}
|
|
56
|
+
return `${trimmed.slice(0, SLACK_STATUS_MAX_LENGTH - 3).trimEnd()}...`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// src/chat/slack/mrkdwn.ts
|
|
60
|
+
function ensureBlockSpacing(text) {
|
|
61
|
+
const codeBlockPattern = /^```/;
|
|
62
|
+
const listItemPattern = /^[-*•]\s|^\d+\.\s/;
|
|
63
|
+
const lines = text.split("\n");
|
|
64
|
+
const result = [];
|
|
65
|
+
let inCodeBlock = false;
|
|
66
|
+
for (let i = 0; i < lines.length; i++) {
|
|
67
|
+
const line = lines[i];
|
|
68
|
+
const isCodeFence = codeBlockPattern.test(line.trimStart());
|
|
69
|
+
if (isCodeFence) {
|
|
70
|
+
if (!inCodeBlock) {
|
|
71
|
+
const prev2 = result.length > 0 ? result[result.length - 1] : void 0;
|
|
72
|
+
if (prev2 !== void 0 && prev2.trim() !== "") {
|
|
73
|
+
result.push("");
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
inCodeBlock = !inCodeBlock;
|
|
77
|
+
result.push(line);
|
|
78
|
+
continue;
|
|
79
|
+
}
|
|
80
|
+
if (inCodeBlock) {
|
|
81
|
+
result.push(line);
|
|
82
|
+
continue;
|
|
83
|
+
}
|
|
84
|
+
const prev = result.length > 0 ? result[result.length - 1] : void 0;
|
|
85
|
+
if (prev !== void 0 && prev.trim() !== "" && line.trim() !== "" && !(listItemPattern.test(prev.trimStart()) && listItemPattern.test(line.trimStart()))) {
|
|
86
|
+
result.push("");
|
|
87
|
+
}
|
|
88
|
+
result.push(line);
|
|
89
|
+
}
|
|
90
|
+
return result.join("\n");
|
|
91
|
+
}
|
|
92
|
+
function renderSlackMrkdwn(text) {
|
|
93
|
+
let normalized = text.replace(/\r\n?/g, "\n").replace(/[ \t]+$/gm, "");
|
|
94
|
+
normalized = ensureBlockSpacing(normalized);
|
|
95
|
+
return normalized.replace(/\n{3,}/g, "\n\n").trim();
|
|
96
|
+
}
|
|
97
|
+
function normalizeSlackStatusText(text) {
|
|
98
|
+
const trimmed = text.trim();
|
|
99
|
+
if (!trimmed) {
|
|
100
|
+
return "";
|
|
101
|
+
}
|
|
102
|
+
return truncateStatusText(trimmed.replace(/(?:\.\s*)+$/, "").trim());
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// src/chat/slack/output.ts
|
|
106
|
+
var MAX_INLINE_CHARS = 2200;
|
|
107
|
+
var MAX_INLINE_LINES = 45;
|
|
108
|
+
var CONTINUED_MARKER = "\n\n[Continued below]";
|
|
109
|
+
function countSlackLines(text) {
|
|
110
|
+
if (!text) {
|
|
111
|
+
return 0;
|
|
112
|
+
}
|
|
113
|
+
return text.split("\n").length;
|
|
114
|
+
}
|
|
115
|
+
function fitsInlineBudget(text, maxChars = MAX_INLINE_CHARS, maxLines = MAX_INLINE_LINES) {
|
|
116
|
+
return text.length <= maxChars && countSlackLines(text) <= maxLines;
|
|
117
|
+
}
|
|
118
|
+
function findSplitIndex(text, maxChars) {
|
|
119
|
+
if (text.length <= maxChars) {
|
|
120
|
+
return text.length;
|
|
121
|
+
}
|
|
122
|
+
const bounded = text.slice(0, maxChars);
|
|
123
|
+
const newlineIndex = bounded.lastIndexOf("\n");
|
|
124
|
+
if (newlineIndex > 0) {
|
|
125
|
+
return newlineIndex;
|
|
126
|
+
}
|
|
127
|
+
const spaceIndex = bounded.lastIndexOf(" ");
|
|
128
|
+
if (spaceIndex > 0) {
|
|
129
|
+
return spaceIndex;
|
|
130
|
+
}
|
|
131
|
+
return maxChars;
|
|
132
|
+
}
|
|
133
|
+
function splitByLineBudget(text, maxLines) {
|
|
134
|
+
if (maxLines <= 0) {
|
|
135
|
+
return "";
|
|
136
|
+
}
|
|
137
|
+
const lines = text.split("\n");
|
|
138
|
+
if (lines.length <= maxLines) {
|
|
139
|
+
return text;
|
|
140
|
+
}
|
|
141
|
+
return lines.slice(0, maxLines).join("\n");
|
|
142
|
+
}
|
|
143
|
+
function reserveInlineBudgetForSuffix(suffix, maxChars = MAX_INLINE_CHARS, maxLines = MAX_INLINE_LINES) {
|
|
144
|
+
return {
|
|
145
|
+
maxChars: Math.max(1, maxChars - suffix.length),
|
|
146
|
+
maxLines: Math.max(1, maxLines - Math.max(0, countSlackLines(suffix) - 1))
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
function forceSplitBudget(text, budget) {
|
|
150
|
+
const lineCount = countSlackLines(text);
|
|
151
|
+
return {
|
|
152
|
+
maxChars: text.length <= budget.maxChars ? Math.max(1, text.length - 1) : budget.maxChars,
|
|
153
|
+
maxLines: lineCount <= budget.maxLines ? Math.max(1, lineCount - 1) : budget.maxLines
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
function getFenceContinuation(text) {
|
|
157
|
+
let open;
|
|
158
|
+
for (const line of text.split("\n")) {
|
|
159
|
+
const trimmed = line.trimStart();
|
|
160
|
+
const openerMatch = trimmed.match(/^(`{3,}|~{3,})(.*)$/);
|
|
161
|
+
if (!openerMatch) {
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (!open) {
|
|
165
|
+
open = {
|
|
166
|
+
fence: openerMatch[1],
|
|
167
|
+
openerLine: trimmed
|
|
168
|
+
};
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
if (new RegExp(`^${open.fence}\\s*$`).test(trimmed)) {
|
|
172
|
+
open = void 0;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (!open) {
|
|
176
|
+
return null;
|
|
177
|
+
}
|
|
178
|
+
return {
|
|
179
|
+
closeSuffix: text.endsWith("\n") ? open.fence : `
|
|
180
|
+
${open.fence}`,
|
|
181
|
+
reopenPrefix: `${open.openerLine}
|
|
182
|
+
`
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
function appendSlackSuffix(text, marker) {
|
|
186
|
+
const carryover = getFenceContinuation(text);
|
|
187
|
+
return `${text}${carryover?.closeSuffix ?? ""}${marker}`;
|
|
188
|
+
}
|
|
189
|
+
function stripTrailingContinuationMarker(text) {
|
|
190
|
+
return text.endsWith(CONTINUED_MARKER) ? text.slice(0, -CONTINUED_MARKER.length) : text;
|
|
191
|
+
}
|
|
192
|
+
function takeSlackContinuationChunk(text, budget) {
|
|
193
|
+
let { prefix, rest } = takeSlackInlinePrefix(text, budget);
|
|
194
|
+
if (!rest) {
|
|
195
|
+
({ prefix, rest } = takeSlackInlinePrefix(
|
|
196
|
+
text,
|
|
197
|
+
forceSplitBudget(text, budget)
|
|
198
|
+
));
|
|
199
|
+
}
|
|
200
|
+
let carryover = rest ? getFenceContinuation(prefix) : null;
|
|
201
|
+
if (!carryover) {
|
|
202
|
+
return { prefix, rest };
|
|
203
|
+
}
|
|
204
|
+
const carryoverBudget = reserveInlineBudgetForSuffix(
|
|
205
|
+
`${carryover.closeSuffix}${CONTINUED_MARKER}`
|
|
206
|
+
);
|
|
207
|
+
({ prefix, rest } = takeSlackInlinePrefix(text, carryoverBudget));
|
|
208
|
+
if (!rest) {
|
|
209
|
+
({ prefix, rest } = takeSlackInlinePrefix(
|
|
210
|
+
text,
|
|
211
|
+
forceSplitBudget(text, carryoverBudget)
|
|
212
|
+
));
|
|
213
|
+
}
|
|
214
|
+
carryover = rest ? getFenceContinuation(prefix) : null;
|
|
215
|
+
if (!carryover) {
|
|
216
|
+
return { prefix, rest };
|
|
217
|
+
}
|
|
218
|
+
return {
|
|
219
|
+
prefix,
|
|
220
|
+
rest: `${carryover.reopenPrefix}${rest}`
|
|
221
|
+
};
|
|
222
|
+
}
|
|
223
|
+
function takeSlackContinuationPrefix(text, options) {
|
|
224
|
+
const budget = {
|
|
225
|
+
maxChars: options?.maxChars ?? getSlackContinuationBudget().maxChars,
|
|
226
|
+
maxLines: options?.maxLines ?? getSlackContinuationBudget().maxLines
|
|
227
|
+
};
|
|
228
|
+
const { prefix, rest } = (() => {
|
|
229
|
+
if (options?.forceSplit) {
|
|
230
|
+
return takeSlackContinuationChunk(text, budget);
|
|
231
|
+
}
|
|
232
|
+
const initial = takeSlackInlinePrefix(text, budget);
|
|
233
|
+
return initial.rest ? takeSlackContinuationChunk(text, budget) : initial;
|
|
234
|
+
})();
|
|
235
|
+
return {
|
|
236
|
+
prefix,
|
|
237
|
+
renderedPrefix: rest ? appendSlackSuffix(prefix, CONTINUED_MARKER) : prefix,
|
|
238
|
+
rest
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
function takeSlackInlinePrefix(text, options) {
|
|
242
|
+
const maxChars = options?.maxChars ?? MAX_INLINE_CHARS;
|
|
243
|
+
const maxLines = options?.maxLines ?? MAX_INLINE_LINES;
|
|
244
|
+
const normalized = text.replace(/\r\n?/g, "\n");
|
|
245
|
+
if (!normalized) {
|
|
246
|
+
return { prefix: "", rest: "" };
|
|
247
|
+
}
|
|
248
|
+
if (fitsInlineBudget(normalized, maxChars, maxLines)) {
|
|
249
|
+
return { prefix: normalized, rest: "" };
|
|
250
|
+
}
|
|
251
|
+
const lineBounded = splitByLineBudget(normalized, maxLines);
|
|
252
|
+
const cutIndex = findSplitIndex(lineBounded, maxChars);
|
|
253
|
+
const prefix = lineBounded.slice(0, cutIndex).trimEnd();
|
|
254
|
+
if (prefix) {
|
|
255
|
+
return {
|
|
256
|
+
prefix,
|
|
257
|
+
rest: normalized.slice(prefix.length).trimStart()
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
const hardPrefix = normalized.slice(0, Math.max(1, maxChars)).trimEnd();
|
|
261
|
+
return {
|
|
262
|
+
prefix: hardPrefix || normalized.slice(0, Math.max(1, maxChars)),
|
|
263
|
+
rest: normalized.slice(hardPrefix.length || Math.max(1, maxChars)).trimStart()
|
|
264
|
+
};
|
|
265
|
+
}
|
|
266
|
+
function splitSlackReplyText(text, options) {
|
|
267
|
+
const normalized = renderSlackMrkdwn(text);
|
|
268
|
+
if (!normalized) {
|
|
269
|
+
return [];
|
|
270
|
+
}
|
|
271
|
+
const chunks = [];
|
|
272
|
+
const continuationBudget = reserveInlineBudgetForSuffix(CONTINUED_MARKER);
|
|
273
|
+
let remaining = normalized;
|
|
274
|
+
while (remaining) {
|
|
275
|
+
const fitsFinalChunk = options?.interrupted ? fitsInlineBudget(appendSlackSuffix(remaining, getInterruptionMarker())) : fitsInlineBudget(remaining);
|
|
276
|
+
if (fitsFinalChunk) {
|
|
277
|
+
chunks.push(
|
|
278
|
+
options?.interrupted ? appendSlackSuffix(remaining, getInterruptionMarker()) : remaining
|
|
279
|
+
);
|
|
280
|
+
break;
|
|
281
|
+
}
|
|
282
|
+
const { renderedPrefix, rest } = takeSlackContinuationPrefix(remaining, {
|
|
283
|
+
...continuationBudget,
|
|
284
|
+
forceSplit: true
|
|
285
|
+
});
|
|
286
|
+
chunks.push(renderedPrefix);
|
|
287
|
+
remaining = rest;
|
|
288
|
+
}
|
|
289
|
+
if (chunks.length === 2) {
|
|
290
|
+
chunks[0] = stripTrailingContinuationMarker(chunks[0] ?? "");
|
|
291
|
+
}
|
|
292
|
+
return chunks;
|
|
293
|
+
}
|
|
294
|
+
function getSlackContinuationBudget() {
|
|
295
|
+
return reserveInlineBudgetForSuffix(CONTINUED_MARKER);
|
|
296
|
+
}
|
|
297
|
+
function buildSlackOutputMessage(text, files) {
|
|
298
|
+
const normalized = renderSlackMrkdwn(text);
|
|
299
|
+
const fileCount = files?.length ?? 0;
|
|
300
|
+
if (!normalized) {
|
|
301
|
+
if (fileCount > 0) {
|
|
302
|
+
return {
|
|
303
|
+
raw: "",
|
|
304
|
+
files
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
throw new Error(
|
|
308
|
+
`Slack output normalized to empty content: original_length=${text.length} parsed_length=${normalized.length}`
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
markdown: normalized,
|
|
313
|
+
files
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
var slackOutputPolicy = {
|
|
317
|
+
maxInlineChars: MAX_INLINE_CHARS,
|
|
318
|
+
maxInlineLines: MAX_INLINE_LINES
|
|
319
|
+
};
|
|
320
|
+
|
|
321
|
+
// src/chat/xml.ts
|
|
322
|
+
function escapeXml(value) {
|
|
323
|
+
return value.replaceAll("&", "&").replaceAll("<", "<").replaceAll(">", ">").replaceAll('"', """).replaceAll("'", "'");
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// src/chat/prompt.ts
|
|
327
|
+
var DEFAULT_SOUL = "You are Junior, a practical and concise assistant.";
|
|
328
|
+
function getLoggedMarkdownFiles() {
|
|
329
|
+
const globalState = globalThis;
|
|
330
|
+
globalState.__juniorLoggedMarkdownFiles ??= /* @__PURE__ */ new Set();
|
|
331
|
+
return globalState.__juniorLoggedMarkdownFiles;
|
|
332
|
+
}
|
|
333
|
+
function loadOptionalMarkdownFile(candidates, fileName) {
|
|
334
|
+
for (const resolved of candidates) {
|
|
335
|
+
try {
|
|
336
|
+
const raw = fs.readFileSync(resolved, "utf8").trim();
|
|
337
|
+
if (raw.length > 0) {
|
|
338
|
+
const loggedMarkdownFiles = getLoggedMarkdownFiles();
|
|
339
|
+
const logKey = `${fileName}:${resolved}`;
|
|
340
|
+
if (!loggedMarkdownFiles.has(logKey)) {
|
|
341
|
+
loggedMarkdownFiles.add(logKey);
|
|
342
|
+
logInfo(
|
|
343
|
+
`${fileName.toLowerCase()}_loaded`,
|
|
344
|
+
{},
|
|
345
|
+
{
|
|
346
|
+
"file.path": resolved
|
|
347
|
+
},
|
|
348
|
+
`Loaded ${fileName}`
|
|
349
|
+
);
|
|
350
|
+
}
|
|
351
|
+
return raw;
|
|
352
|
+
}
|
|
353
|
+
} catch {
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
function loadSoul() {
|
|
360
|
+
const soul = loadOptionalMarkdownFile(soulPathCandidates(), "SOUL.md");
|
|
361
|
+
if (soul) {
|
|
362
|
+
return soul;
|
|
363
|
+
}
|
|
364
|
+
logWarn(
|
|
365
|
+
"soul_load_fallback",
|
|
366
|
+
{},
|
|
367
|
+
{
|
|
368
|
+
"app.file.candidates": soulPathCandidates()
|
|
369
|
+
},
|
|
370
|
+
"SOUL.md not found; using built-in default personality"
|
|
371
|
+
);
|
|
372
|
+
return DEFAULT_SOUL;
|
|
373
|
+
}
|
|
374
|
+
function loadWorld() {
|
|
375
|
+
return loadOptionalMarkdownFile(worldPathCandidates(), "WORLD.md");
|
|
376
|
+
}
|
|
377
|
+
var JUNIOR_PERSONALITY = (() => {
|
|
378
|
+
try {
|
|
379
|
+
return loadSoul();
|
|
380
|
+
} catch (error) {
|
|
381
|
+
logWarn(
|
|
382
|
+
"soul_load_failed",
|
|
383
|
+
{},
|
|
384
|
+
{
|
|
385
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
386
|
+
},
|
|
387
|
+
"Failed to load SOUL.md; using built-in default personality"
|
|
388
|
+
);
|
|
389
|
+
return DEFAULT_SOUL;
|
|
390
|
+
}
|
|
391
|
+
})();
|
|
392
|
+
var JUNIOR_WORLD = (() => {
|
|
393
|
+
try {
|
|
394
|
+
return loadWorld();
|
|
395
|
+
} catch (error) {
|
|
396
|
+
logWarn(
|
|
397
|
+
"world_load_failed",
|
|
398
|
+
{},
|
|
399
|
+
{
|
|
400
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
401
|
+
},
|
|
402
|
+
"Failed to load WORLD.md; omitting world prompt context"
|
|
403
|
+
);
|
|
404
|
+
return null;
|
|
405
|
+
}
|
|
406
|
+
})();
|
|
407
|
+
function workspaceSkillDir(skillName) {
|
|
408
|
+
return sandboxSkillDir(skillName);
|
|
409
|
+
}
|
|
410
|
+
function formatConfigurationValue(value) {
|
|
411
|
+
if (typeof value === "string") {
|
|
412
|
+
return escapeXml(value);
|
|
413
|
+
}
|
|
414
|
+
try {
|
|
415
|
+
return escapeXml(JSON.stringify(value));
|
|
416
|
+
} catch {
|
|
417
|
+
return escapeXml(String(value));
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
function renderRequesterBlock(fields) {
|
|
421
|
+
const lines = Object.entries(fields).filter(([, value]) => Boolean(value)).map(([key2, value]) => `- ${key2}: ${escapeXml(value)}`);
|
|
422
|
+
if (lines.length === 0) {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
return ["<requester>", ...lines, "</requester>"];
|
|
426
|
+
}
|
|
427
|
+
function renderTag(tag, lines) {
|
|
428
|
+
return [`<${tag}>`, ...lines, `</${tag}>`];
|
|
429
|
+
}
|
|
430
|
+
function renderTagBlock(tag, content) {
|
|
431
|
+
return [`<${tag}>`, content, `</${tag}>`].join("\n");
|
|
432
|
+
}
|
|
433
|
+
function formatSkillEntry(skill) {
|
|
434
|
+
const skillLocation = `${workspaceSkillDir(skill.name)}/SKILL.md`;
|
|
435
|
+
const lines = [];
|
|
436
|
+
lines.push(" <skill>");
|
|
437
|
+
lines.push(` <name>${escapeXml(skill.name)}</name>`);
|
|
438
|
+
lines.push(` <description>${escapeXml(skill.description)}</description>`);
|
|
439
|
+
lines.push(` <location>${escapeXml(skillLocation)}</location>`);
|
|
440
|
+
lines.push(" </skill>");
|
|
441
|
+
return lines;
|
|
442
|
+
}
|
|
443
|
+
function formatAvailableSkillsForPrompt(skills, invocation) {
|
|
444
|
+
const autoSelectable = skills.filter(
|
|
445
|
+
(s) => s.disableModelInvocation !== true
|
|
446
|
+
);
|
|
447
|
+
const invokedExplicitOnly = invocation ? skills.filter(
|
|
448
|
+
(s) => s.disableModelInvocation === true && s.name === invocation.skillName
|
|
449
|
+
) : [];
|
|
450
|
+
const sections = [];
|
|
451
|
+
if (autoSelectable.length > 0) {
|
|
452
|
+
const available = [
|
|
453
|
+
"<available-skills>",
|
|
454
|
+
"Scan before answering. Load the most specific matching skill; do not answer from memory when a skill fits. A request that names a skill, plugin, provider, or account matching a skill name is a skill match. If none fits, do not load a skill."
|
|
455
|
+
];
|
|
456
|
+
for (const skill of autoSelectable) {
|
|
457
|
+
available.push(...formatSkillEntry(skill));
|
|
458
|
+
}
|
|
459
|
+
available.push("</available-skills>");
|
|
460
|
+
sections.push(available.join("\n"));
|
|
461
|
+
}
|
|
462
|
+
if (invokedExplicitOnly.length > 0) {
|
|
463
|
+
const userCallable = [
|
|
464
|
+
"<user-callable-skills>",
|
|
465
|
+
"The user's current message explicitly references this skill by name. Load it when relevant to the request."
|
|
466
|
+
];
|
|
467
|
+
for (const skill of invokedExplicitOnly) {
|
|
468
|
+
userCallable.push(...formatSkillEntry(skill));
|
|
469
|
+
}
|
|
470
|
+
userCallable.push("</user-callable-skills>");
|
|
471
|
+
sections.push(userCallable.join("\n"));
|
|
472
|
+
}
|
|
473
|
+
return sections.length > 0 ? sections.join("\n") : null;
|
|
474
|
+
}
|
|
475
|
+
function formatActiveMcpCatalogsForPrompt(catalogs) {
|
|
476
|
+
if (catalogs.length === 0) {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
const lines = [
|
|
480
|
+
"Active MCP provider catalogs are available through `searchMcpTools`. Call it with provider to list descriptors or with query to narrow results, then pass the exact returned `tool_name` to `callMcpTool`. Put provider fields inside `arguments`."
|
|
481
|
+
];
|
|
482
|
+
for (const catalog of catalogs) {
|
|
483
|
+
lines.push(" <catalog>");
|
|
484
|
+
lines.push(` <provider>${escapeXml(catalog.provider)}</provider>`);
|
|
485
|
+
lines.push(
|
|
486
|
+
` <available_tool_count>${catalog.available_tool_count}</available_tool_count>`
|
|
487
|
+
);
|
|
488
|
+
lines.push(" </catalog>");
|
|
489
|
+
}
|
|
490
|
+
return lines.join("\n");
|
|
491
|
+
}
|
|
492
|
+
function formatToolGuidanceForPrompt(tools) {
|
|
493
|
+
const guidedTools = tools.filter(
|
|
494
|
+
(tool) => Boolean(tool.promptSnippet?.trim()) || (tool.promptGuidelines?.length ?? 0) > 0
|
|
495
|
+
);
|
|
496
|
+
if (guidedTools.length === 0) {
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
const lines = [];
|
|
500
|
+
for (const tool of guidedTools) {
|
|
501
|
+
lines.push(` <tool name="${escapeXml(tool.name)}">`);
|
|
502
|
+
if (tool.promptSnippet?.trim()) {
|
|
503
|
+
lines.push(` - ${escapeXml(tool.promptSnippet.trim())}`);
|
|
504
|
+
}
|
|
505
|
+
if (tool.promptGuidelines && tool.promptGuidelines.length > 0) {
|
|
506
|
+
for (const guideline of tool.promptGuidelines) {
|
|
507
|
+
lines.push(` - ${escapeXml(guideline)}`);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
lines.push(" </tool>");
|
|
511
|
+
}
|
|
512
|
+
return lines.join("\n");
|
|
513
|
+
}
|
|
514
|
+
function formatReferenceFilesLines() {
|
|
515
|
+
const files = listReferenceFiles();
|
|
516
|
+
if (files.length === 0) {
|
|
517
|
+
return null;
|
|
518
|
+
}
|
|
519
|
+
return files.map((filePath) => {
|
|
520
|
+
const name = path.basename(filePath);
|
|
521
|
+
return `- ${escapeXml(name)} (${escapeXml(`${SANDBOX_DATA_ROOT}/${name}`)})`;
|
|
522
|
+
});
|
|
523
|
+
}
|
|
524
|
+
function formatArtifactsLines(artifactState) {
|
|
525
|
+
if (!artifactState) return null;
|
|
526
|
+
const lines = [];
|
|
527
|
+
if (artifactState.lastCanvasId) {
|
|
528
|
+
lines.push(`- last_canvas_id: ${escapeXml(artifactState.lastCanvasId)}`);
|
|
529
|
+
}
|
|
530
|
+
if (artifactState.lastCanvasUrl) {
|
|
531
|
+
lines.push(`- last_canvas_url: ${escapeXml(artifactState.lastCanvasUrl)}`);
|
|
532
|
+
}
|
|
533
|
+
if (artifactState.recentCanvases && artifactState.recentCanvases.length > 0) {
|
|
534
|
+
lines.push("- recent_canvases:");
|
|
535
|
+
for (const canvas of artifactState.recentCanvases) {
|
|
536
|
+
lines.push(` - id: ${escapeXml(canvas.id)}`);
|
|
537
|
+
if (canvas.title) lines.push(` title: ${escapeXml(canvas.title)}`);
|
|
538
|
+
if (canvas.url) lines.push(` url: ${escapeXml(canvas.url)}`);
|
|
539
|
+
if (canvas.createdAt) {
|
|
540
|
+
lines.push(` created_at: ${escapeXml(canvas.createdAt)}`);
|
|
541
|
+
}
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
if (artifactState.lastListId) {
|
|
545
|
+
lines.push(`- last_list_id: ${escapeXml(artifactState.lastListId)}`);
|
|
546
|
+
}
|
|
547
|
+
if (artifactState.lastListUrl) {
|
|
548
|
+
lines.push(`- last_list_url: ${escapeXml(artifactState.lastListUrl)}`);
|
|
549
|
+
}
|
|
550
|
+
return lines.length > 0 ? lines : null;
|
|
551
|
+
}
|
|
552
|
+
function formatConfigurationLines(configuration) {
|
|
553
|
+
const keys = Object.keys(configuration ?? {}).sort(
|
|
554
|
+
(a, b) => a.localeCompare(b)
|
|
555
|
+
);
|
|
556
|
+
if (keys.length === 0) return null;
|
|
557
|
+
return keys.map(
|
|
558
|
+
(key2) => `- ${escapeXml(key2)}: ${formatConfigurationValue(configuration?.[key2])}`
|
|
559
|
+
);
|
|
560
|
+
}
|
|
561
|
+
var HEADER = "You are a Slack-based helper assistant. Follow the personality section for voice and tone in every reply. Platform mechanics and output rules override personality and world context when they conflict.";
|
|
562
|
+
var TURN_CONTEXT_HEADER = "Runtime context for this request. Treat these blocks as trusted runtime facts; the static system prompt remains authoritative.";
|
|
563
|
+
var TOOL_POLICY_RULES = [
|
|
564
|
+
"- Tool schemas are the source of truth for parameters; tool names are case-sensitive, so call tools exactly by their exposed names and do not invent arguments.",
|
|
565
|
+
"- Use tools for actionable work and for facts that are mutable, external, repository-backed, provider-backed, or requested as verified/current. Stable general knowledge and already-provided context may be answered directly.",
|
|
566
|
+
"- Resolve provider action targets before calls: explicit target wins; ambient `<configuration>` fills omitted targets. Treat non-target links/references as context.",
|
|
567
|
+
"- Verification source order: conversation/thread context; user-provided attachments, links, and reference files; local/sandbox files when present; loaded skill references; repository/provider tools; public web. Use the nearest authoritative available source before weaker sources.",
|
|
568
|
+
"- For repository or implementation questions, inspect the target repository first: local checkout when present, otherwise the configured GitHub/source provider. Do not treat loaded skill files as repo source unless the user asks about the skill. Cite file paths, symbols, PRs/issues, commits, or URLs that support the answer.",
|
|
569
|
+
`- Sandbox-backed file and shell tools operate in an isolated workspace rooted at ${SANDBOX_WORKSPACE_ROOT}; readFile/writeFile paths are sandbox-workspace paths, bash runs inside that workspace, and attachFile accepts absolute or workspace-relative sandbox paths.`,
|
|
570
|
+
"- If a sandbox-backed tool reports that sandbox execution is unavailable, treat that as a blocker for local file/shell inspection; do not pretend host files were inspected.",
|
|
571
|
+
"- For user-provided URLs, use `webFetch`; for discovery, use `webSearch` then fetch/read promising sources; for current time/date context, use `systemTime`.",
|
|
572
|
+
"- If the first result is empty, stale, ambiguous, or incomplete, try a focused alternate query, path, command, or source before concluding the answer cannot be verified."
|
|
573
|
+
];
|
|
574
|
+
var TOOL_CALL_STYLE_RULES = [
|
|
575
|
+
"- For routine low-risk tool use, call the tool directly without narrating the obvious step first.",
|
|
576
|
+
"- Briefly narrate only when it helps the user understand multi-step work, sensitive actions, destructive actions, or a notable change in approach.",
|
|
577
|
+
"- When a first-class tool exists for an action, use it directly instead of asking the user to run an equivalent command, slash command, or manual lookup.",
|
|
578
|
+
"- Keep tool-call explanations separate from final answers; final answers should report results, evidence, or blockers."
|
|
579
|
+
];
|
|
580
|
+
var SKILL_POLICY_RULES = [
|
|
581
|
+
"- Only load skills listed in `<available-skills>`, `<user-callable-skills>`, or named by `<explicit-skill-trigger>`. Never guess or invent a skill name.",
|
|
582
|
+
"- Load one skill at a time. After `loadSkill`, follow the instructions returned by that tool result."
|
|
583
|
+
];
|
|
584
|
+
var EXECUTION_CONTRACT_RULES = [
|
|
585
|
+
"- Actionable request: act in this turn.",
|
|
586
|
+
"- Continue until done or genuinely blocked. Do not finish with a plan, promise, or offer to check next when an available tool or source can move the request forward.",
|
|
587
|
+
"- Completion means the final answer covers the user's actual ask, including requested follow-up checks, and is grounded in the best evidence you could access.",
|
|
588
|
+
"- Ask the user only for missing access, approval, or a decision that blocks safe progress. Ask one focused question; otherwise infer conservatively and continue.",
|
|
589
|
+
"- For conflicting evidence, compare sources and state which source is authoritative for the answer.",
|
|
590
|
+
"- For non-trivial or long-running work, call `reportProgress` early when available, then only when the major phase changes. Routine tool calls should stay silent."
|
|
591
|
+
];
|
|
592
|
+
var CONVERSATION_RULES = [
|
|
593
|
+
"- In thread follow-ups, answer from prior thread context; do not repeat resolved clarifying questions.",
|
|
594
|
+
"- Preserve attribution roles from thread context: the requester is the person asking now, which may differ from the original reporter or subject.",
|
|
595
|
+
"- Runtime owns continuation and authorization notices; on resumed turns, answer with the final requested content only."
|
|
596
|
+
];
|
|
597
|
+
var SLACK_ACTION_RULES = [
|
|
598
|
+
"- Context-bound Slack tools use runtime-owned targets; do not invent channel, canvas, list, or message IDs.",
|
|
599
|
+
"- Use first-class Slack tools for Slack side effects; do not use bash, curl, or provider APIs to bypass Slack tool targeting.",
|
|
600
|
+
"- Use channel-post and emoji-reaction tools only when the user explicitly asks for that Slack side effect.",
|
|
601
|
+
"- For explicit channel-post or emoji-reaction requests, skip a duplicate thread text reply when the tool result already satisfies the request.",
|
|
602
|
+
"- Do not claim an attachment, canvas, channel post, list update, or reaction succeeded unless the tool returned success this turn; when it did, include any link the tool returned.",
|
|
603
|
+
"- Do not use reactions as progress indicators."
|
|
604
|
+
];
|
|
605
|
+
var SAFETY_RULES = [
|
|
606
|
+
"- Stay within the user's request and the runtime's available capabilities; do not pursue independent goals, persistence, replication, credential gathering, or access expansion.",
|
|
607
|
+
"- Respect stop, pause, audit, and approval boundaries. Do not bypass safeguards or persuade the user to weaken them.",
|
|
608
|
+
"- Do not change system prompts, tool policies, security settings, credentials, or runtime configuration unless the user explicitly requests that exact administrative action and an available tool permits it."
|
|
609
|
+
];
|
|
610
|
+
var FAILURE_RULES = [
|
|
611
|
+
"- For tool/runtime failures, run the named check before diagnosing and report the exact failed command plus stderr/exit code.",
|
|
612
|
+
"- If a fact cannot be verified after focused checks, say what you checked and what blocked a stronger answer.",
|
|
613
|
+
"- Do not surface raw tool payloads, execution-escape text, or internal routing metadata as the final answer."
|
|
614
|
+
];
|
|
615
|
+
function renderRuleSection(tag, lines) {
|
|
616
|
+
return [`<${tag}>`, ...lines, `</${tag}>`].join("\n");
|
|
617
|
+
}
|
|
618
|
+
function buildBehaviorSection() {
|
|
619
|
+
return [
|
|
620
|
+
renderRuleSection("tool-policy", TOOL_POLICY_RULES),
|
|
621
|
+
renderRuleSection("tool-call-style", TOOL_CALL_STYLE_RULES),
|
|
622
|
+
renderRuleSection("skill-policy", SKILL_POLICY_RULES),
|
|
623
|
+
renderRuleSection("execution-contract", EXECUTION_CONTRACT_RULES),
|
|
624
|
+
renderRuleSection("conversation", CONVERSATION_RULES),
|
|
625
|
+
renderRuleSection("slack-actions", SLACK_ACTION_RULES),
|
|
626
|
+
renderRuleSection("safety", SAFETY_RULES),
|
|
627
|
+
renderRuleSection("failure-handling", FAILURE_RULES)
|
|
628
|
+
].join("\n\n");
|
|
629
|
+
}
|
|
630
|
+
function buildOutputSection() {
|
|
631
|
+
const openTag = `<output format="slack-markdown" max_inline_chars="${slackOutputPolicy.maxInlineChars}" max_inline_lines="${slackOutputPolicy.maxInlineLines}">`;
|
|
632
|
+
return [
|
|
633
|
+
openTag,
|
|
634
|
+
"- Start with the answer or result, not internal process narration.",
|
|
635
|
+
"- 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.",
|
|
636
|
+
"- Keep replies brief and scannable; use bullets or short code blocks when helpful, and one compact thread reply when it fits.",
|
|
637
|
+
"- When a research or document-style answer would benefit from continuation, multiple sections, or future reference value, create a Slack canvas and keep the thread reply to one or two short sentences plus the link; do not recap the canvas contents.",
|
|
638
|
+
"- Unless a successful Slack side-effect tool intentionally satisfied the request by itself, end every turn with a final user-facing markdown response.",
|
|
639
|
+
"</output>"
|
|
640
|
+
].join("\n");
|
|
641
|
+
}
|
|
642
|
+
function buildIdentitySection() {
|
|
643
|
+
return [
|
|
644
|
+
"# Identity",
|
|
645
|
+
`Your Slack username is \`${botConfig.userName}\`.`
|
|
646
|
+
].join("\n");
|
|
647
|
+
}
|
|
648
|
+
function buildPersonalitySection() {
|
|
649
|
+
return ["# Personality", JUNIOR_PERSONALITY.trim()].join("\n");
|
|
650
|
+
}
|
|
651
|
+
function buildWorldSection() {
|
|
652
|
+
if (!JUNIOR_WORLD) {
|
|
653
|
+
return null;
|
|
654
|
+
}
|
|
655
|
+
return ["# World", JUNIOR_WORLD.trim()].join("\n");
|
|
656
|
+
}
|
|
657
|
+
function buildRuntimeSection(params) {
|
|
658
|
+
const lines = [
|
|
659
|
+
params.conversationId ? `- gen_ai.conversation.id: ${escapeXml(params.conversationId)}` : ""
|
|
660
|
+
].filter(Boolean);
|
|
661
|
+
if (lines.length === 0) {
|
|
662
|
+
return null;
|
|
663
|
+
}
|
|
664
|
+
return renderTagBlock("runtime", lines.join("\n"));
|
|
665
|
+
}
|
|
666
|
+
function buildContextSection(params) {
|
|
667
|
+
const blocks = [];
|
|
668
|
+
const referenceLines = formatReferenceFilesLines();
|
|
669
|
+
if (referenceLines) {
|
|
670
|
+
blocks.push(
|
|
671
|
+
renderTag("reference-files", [
|
|
672
|
+
"Additional reference documents available in the sandbox. Read them with `readFile` when relevant.",
|
|
673
|
+
...referenceLines
|
|
674
|
+
])
|
|
675
|
+
);
|
|
676
|
+
}
|
|
677
|
+
const requesterLines = renderRequesterBlock({
|
|
678
|
+
full_name: params.requester?.fullName,
|
|
679
|
+
user_name: params.requester?.userName,
|
|
680
|
+
user_id: params.requester?.userId
|
|
681
|
+
});
|
|
682
|
+
if (requesterLines) {
|
|
683
|
+
blocks.push(requesterLines);
|
|
684
|
+
}
|
|
685
|
+
const artifactLines = formatArtifactsLines(params.artifactState);
|
|
686
|
+
if (artifactLines) {
|
|
687
|
+
blocks.push(renderTag("artifacts", artifactLines));
|
|
688
|
+
}
|
|
689
|
+
const configLines = formatConfigurationLines(params.configuration);
|
|
690
|
+
if (configLines) {
|
|
691
|
+
blocks.push(
|
|
692
|
+
renderTag("configuration", [
|
|
693
|
+
"Ambient provider defaults; explicit targets win. Run `jr-rpc config get|set|unset|list` as standalone bash commands; do not chain with `cd`, `&&`, pipes, or provider commands.",
|
|
694
|
+
...configLines
|
|
695
|
+
])
|
|
696
|
+
);
|
|
697
|
+
}
|
|
698
|
+
if (params.invocation) {
|
|
699
|
+
blocks.push(
|
|
700
|
+
renderTag("explicit-skill-trigger", [
|
|
701
|
+
"Treat this skill as selected. Load it unless the tool says it is unavailable.",
|
|
702
|
+
`/${escapeXml(params.invocation.skillName)}`
|
|
703
|
+
])
|
|
704
|
+
);
|
|
705
|
+
}
|
|
706
|
+
const body = blocks.map((block) => block.join("\n")).join("\n\n");
|
|
707
|
+
if (!body) {
|
|
708
|
+
return null;
|
|
709
|
+
}
|
|
710
|
+
return renderTagBlock("context", body);
|
|
711
|
+
}
|
|
712
|
+
function buildCapabilitiesSection(params) {
|
|
713
|
+
const blocks = [];
|
|
714
|
+
const availableSkills = formatAvailableSkillsForPrompt(
|
|
715
|
+
params.availableSkills,
|
|
716
|
+
params.invocation
|
|
717
|
+
);
|
|
718
|
+
if (availableSkills) {
|
|
719
|
+
blocks.push(availableSkills);
|
|
720
|
+
}
|
|
721
|
+
const activeCatalogs = formatActiveMcpCatalogsForPrompt(
|
|
722
|
+
params.activeMcpCatalogs
|
|
723
|
+
);
|
|
724
|
+
if (activeCatalogs) {
|
|
725
|
+
blocks.push(renderTagBlock("active-mcp-catalogs", activeCatalogs));
|
|
726
|
+
}
|
|
727
|
+
const toolGuidance = formatToolGuidanceForPrompt(params.toolGuidance ?? []);
|
|
728
|
+
if (toolGuidance) {
|
|
729
|
+
blocks.push(renderTagBlock("tool-guidance", toolGuidance));
|
|
730
|
+
}
|
|
731
|
+
if (blocks.length === 0) {
|
|
732
|
+
return null;
|
|
733
|
+
}
|
|
734
|
+
return blocks.join("\n\n");
|
|
735
|
+
}
|
|
736
|
+
var STATIC_SYSTEM_PROMPT = [
|
|
737
|
+
HEADER,
|
|
738
|
+
buildIdentitySection(),
|
|
739
|
+
buildPersonalitySection(),
|
|
740
|
+
buildWorldSection(),
|
|
741
|
+
buildBehaviorSection(),
|
|
742
|
+
buildOutputSection()
|
|
743
|
+
].filter((section) => Boolean(section)).join("\n\n");
|
|
744
|
+
function buildSystemPrompt() {
|
|
745
|
+
return STATIC_SYSTEM_PROMPT;
|
|
746
|
+
}
|
|
747
|
+
function buildTurnContextPrompt(params) {
|
|
748
|
+
const includeSessionContext = params.includeSessionContext ?? true;
|
|
749
|
+
if (!includeSessionContext) {
|
|
750
|
+
return null;
|
|
751
|
+
}
|
|
752
|
+
const runtimeSections = [
|
|
753
|
+
buildCapabilitiesSection({
|
|
754
|
+
availableSkills: params.availableSkills,
|
|
755
|
+
activeMcpCatalogs: params.activeMcpCatalogs ?? [],
|
|
756
|
+
invocation: params.invocation,
|
|
757
|
+
toolGuidance: params.toolGuidance ?? []
|
|
758
|
+
}),
|
|
759
|
+
buildContextSection({
|
|
760
|
+
requester: params.requester,
|
|
761
|
+
artifactState: params.artifactState,
|
|
762
|
+
configuration: params.configuration,
|
|
763
|
+
invocation: params.invocation
|
|
764
|
+
}),
|
|
765
|
+
buildRuntimeSection(params.runtime ?? {})
|
|
766
|
+
].filter((section) => Boolean(section));
|
|
767
|
+
if (runtimeSections.length === 0) {
|
|
768
|
+
return null;
|
|
769
|
+
}
|
|
770
|
+
const sections = [
|
|
771
|
+
`<${TURN_CONTEXT_TAG}>`,
|
|
772
|
+
TURN_CONTEXT_HEADER,
|
|
773
|
+
"The current user instruction appears after this block in the same message.",
|
|
774
|
+
...runtimeSections,
|
|
775
|
+
`</${TURN_CONTEXT_TAG}>`
|
|
776
|
+
].filter((section) => Boolean(section));
|
|
777
|
+
return sections.join("\n\n");
|
|
778
|
+
}
|
|
779
|
+
|
|
22
780
|
// src/chat/state/turn-session.ts
|
|
23
781
|
import { THREAD_STATE_TTL_MS } from "chat";
|
|
24
782
|
|
|
@@ -493,9 +1251,7 @@ function parseAgentTurnSessionFields(parsed) {
|
|
|
493
1251
|
const sliceId = toFiniteNonNegativeNumber(parsed.sliceId);
|
|
494
1252
|
const version = toFiniteNonNegativeNumber(parsed.version);
|
|
495
1253
|
const updatedAtMs = toFiniteNonNegativeNumber(parsed.updatedAtMs);
|
|
496
|
-
const cumulativeDurationMs = toFiniteNonNegativeNumber(
|
|
497
|
-
parsed.cumulativeDurationMs
|
|
498
|
-
);
|
|
1254
|
+
const cumulativeDurationMs = toFiniteNonNegativeNumber(parsed.cumulativeDurationMs) ?? 0;
|
|
499
1255
|
const cumulativeUsage = parseAgentTurnUsage(parsed.cumulativeUsage);
|
|
500
1256
|
const lastProgressAtMs = toFiniteNonNegativeNumber(parsed.lastProgressAtMs);
|
|
501
1257
|
const logSessionId = typeof parsed.logSessionId === "string" ? parsed.logSessionId : void 0;
|
|
@@ -515,8 +1271,8 @@ function parseAgentTurnSessionFields(parsed) {
|
|
|
515
1271
|
startedAtMs: startedAtMs ?? updatedAtMs,
|
|
516
1272
|
lastProgressAtMs: lastProgressAtMs ?? updatedAtMs,
|
|
517
1273
|
updatedAtMs,
|
|
1274
|
+
cumulativeDurationMs,
|
|
518
1275
|
...logSessionId ? { logSessionId } : {},
|
|
519
|
-
...cumulativeDurationMs !== void 0 ? { cumulativeDurationMs } : {},
|
|
520
1276
|
...cumulativeUsage ? { cumulativeUsage } : {},
|
|
521
1277
|
...requester ? { requester } : {},
|
|
522
1278
|
...Array.isArray(parsed.loadedSkillNames) ? {
|
|
@@ -605,7 +1361,7 @@ function materializeAgentTurnSessionRecord(stored, piMessages) {
|
|
|
605
1361
|
lastProgressAtMs: stored.lastProgressAtMs,
|
|
606
1362
|
updatedAtMs: stored.updatedAtMs,
|
|
607
1363
|
piMessages,
|
|
608
|
-
|
|
1364
|
+
cumulativeDurationMs: stored.cumulativeDurationMs,
|
|
609
1365
|
...stored.cumulativeUsage ? { cumulativeUsage: stored.cumulativeUsage } : {},
|
|
610
1366
|
...stored.resumeReason ? { resumeReason: stored.resumeReason } : {},
|
|
611
1367
|
...stored.errorMessage ? { errorMessage: stored.errorMessage } : {},
|
|
@@ -666,12 +1422,7 @@ function buildStoredRecord(args) {
|
|
|
666
1422
|
updatedAtMs: nowMs,
|
|
667
1423
|
committedMessageCount: args.committedMessageCount,
|
|
668
1424
|
...args.logSessionId ? { logSessionId: args.logSessionId } : {},
|
|
669
|
-
|
|
670
|
-
cumulativeDurationMs: Math.max(
|
|
671
|
-
0,
|
|
672
|
-
Math.floor(args.cumulativeDurationMs)
|
|
673
|
-
)
|
|
674
|
-
} : {},
|
|
1425
|
+
cumulativeDurationMs: args.cumulativeDurationMs,
|
|
675
1426
|
...args.cumulativeUsage ? { cumulativeUsage: args.cumulativeUsage } : {},
|
|
676
1427
|
...args.requester ? { requester: args.requester } : {},
|
|
677
1428
|
...Array.isArray(args.loadedSkillNames) ? {
|
|
@@ -725,7 +1476,7 @@ async function updateAgentTurnSessionState(args) {
|
|
|
725
1476
|
lastProgressAtMs: parsed.lastProgressAtMs,
|
|
726
1477
|
previousVersion: parsed.version,
|
|
727
1478
|
...parsed.logSessionId ? { logSessionId: parsed.logSessionId } : {},
|
|
728
|
-
|
|
1479
|
+
cumulativeDurationMs: args.existing.cumulativeDurationMs,
|
|
729
1480
|
...args.existing.cumulativeUsage ? { cumulativeUsage: args.existing.cumulativeUsage } : {},
|
|
730
1481
|
...args.existing.loadedSkillNames ? { loadedSkillNames: args.existing.loadedSkillNames } : {},
|
|
731
1482
|
...args.existing.requester ? { requester: args.existing.requester } : {},
|
|
@@ -764,7 +1515,7 @@ async function upsertAgentTurnSessionRecord(args) {
|
|
|
764
1515
|
committedMessageCount: args.piMessages.length,
|
|
765
1516
|
logSessionId: commit.sessionId,
|
|
766
1517
|
previousVersion: existingRecord?.version,
|
|
767
|
-
|
|
1518
|
+
cumulativeDurationMs: toFiniteNonNegativeNumber(args.cumulativeDurationMs) ?? existingRecord?.cumulativeDurationMs ?? 0,
|
|
768
1519
|
...args.cumulativeUsage ? { cumulativeUsage: args.cumulativeUsage } : {},
|
|
769
1520
|
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
770
1521
|
...args.requester ?? existingRecord?.requester ? { requester: args.requester ?? existingRecord?.requester } : {},
|
|
@@ -796,12 +1547,7 @@ async function recordAgentTurnSessionSummary(args) {
|
|
|
796
1547
|
lastProgressAtMs: args.lastProgressAtMs ?? nowMs,
|
|
797
1548
|
state: args.state,
|
|
798
1549
|
updatedAtMs: nowMs,
|
|
799
|
-
|
|
800
|
-
cumulativeDurationMs: Math.max(
|
|
801
|
-
0,
|
|
802
|
-
Math.floor(args.cumulativeDurationMs)
|
|
803
|
-
)
|
|
804
|
-
} : existing?.cumulativeDurationMs !== void 0 ? { cumulativeDurationMs: existing.cumulativeDurationMs } : {},
|
|
1550
|
+
cumulativeDurationMs: toFiniteNonNegativeNumber(args.cumulativeDurationMs) ?? existing?.cumulativeDurationMs ?? 0,
|
|
805
1551
|
...args.cumulativeUsage ?? existing?.cumulativeUsage ? { cumulativeUsage: args.cumulativeUsage ?? existing?.cumulativeUsage } : {},
|
|
806
1552
|
...args.requester ?? existing?.requester ? { requester: args.requester ?? existing?.requester } : {},
|
|
807
1553
|
...Array.isArray(args.loadedSkillNames) ? {
|
|
@@ -888,8 +1634,8 @@ function buildSentryWebBaseUrl(dsn) {
|
|
|
888
1634
|
return "https://sentry.io";
|
|
889
1635
|
}
|
|
890
1636
|
const port = dsn.port ? `:${dsn.port}` : "";
|
|
891
|
-
const
|
|
892
|
-
return `${dsn.protocol}://${dsn.host}${port}${
|
|
1637
|
+
const path2 = dsn.path ? `/${dsn.path}` : "";
|
|
1638
|
+
return `${dsn.protocol}://${dsn.host}${port}${path2}`;
|
|
893
1639
|
}
|
|
894
1640
|
function buildSentryConversationUrl(conversationId) {
|
|
895
1641
|
const client = sentry_exports.getClient();
|
|
@@ -904,11 +1650,11 @@ function buildSentryConversationUrl(conversationId) {
|
|
|
904
1650
|
const encodedId = encodeURIComponent(conversationId);
|
|
905
1651
|
const params = new URLSearchParams();
|
|
906
1652
|
params.set("project", dsn.projectId);
|
|
907
|
-
const
|
|
1653
|
+
const path2 = `explore/conversations/${encodedId}/?${params.toString()}`;
|
|
908
1654
|
if (isSentrySaasDsnHost(dsn.host)) {
|
|
909
|
-
return `https://${orgSlug}.sentry.io/${
|
|
1655
|
+
return `https://${orgSlug}.sentry.io/${path2}`;
|
|
910
1656
|
}
|
|
911
|
-
return `${buildSentryWebBaseUrl(dsn)}/organizations/${orgSlug}/${
|
|
1657
|
+
return `${buildSentryWebBaseUrl(dsn)}/organizations/${orgSlug}/${path2}`;
|
|
912
1658
|
}
|
|
913
1659
|
function buildSentryTraceUrl(traceId) {
|
|
914
1660
|
const client = sentry_exports.getClient();
|
|
@@ -923,15 +1669,25 @@ function buildSentryTraceUrl(traceId) {
|
|
|
923
1669
|
const encodedTraceId = encodeURIComponent(traceId);
|
|
924
1670
|
const params = new URLSearchParams();
|
|
925
1671
|
params.set("project", dsn.projectId);
|
|
926
|
-
const
|
|
1672
|
+
const path2 = `performance/trace/${encodedTraceId}/?${params.toString()}`;
|
|
927
1673
|
if (isSentrySaasDsnHost(dsn.host)) {
|
|
928
|
-
return `https://${orgSlug}.sentry.io/${
|
|
1674
|
+
return `https://${orgSlug}.sentry.io/${path2}`;
|
|
929
1675
|
}
|
|
930
|
-
return `${buildSentryWebBaseUrl(dsn)}/organizations/${orgSlug}/${
|
|
1676
|
+
return `${buildSentryWebBaseUrl(dsn)}/organizations/${orgSlug}/${path2}`;
|
|
931
1677
|
}
|
|
932
1678
|
|
|
933
1679
|
export {
|
|
934
1680
|
GET,
|
|
1681
|
+
TURN_CONTEXT_TAG,
|
|
1682
|
+
getInterruptionMarker,
|
|
1683
|
+
truncateStatusText,
|
|
1684
|
+
normalizeSlackStatusText,
|
|
1685
|
+
splitSlackReplyText,
|
|
1686
|
+
buildSlackOutputMessage,
|
|
1687
|
+
escapeXml,
|
|
1688
|
+
JUNIOR_PERSONALITY,
|
|
1689
|
+
buildSystemPrompt,
|
|
1690
|
+
buildTurnContextPrompt,
|
|
935
1691
|
loadProjection,
|
|
936
1692
|
loadConnectedMcpProviders,
|
|
937
1693
|
recordMcpProviderConnected,
|