@sentry/junior 0.148.0 → 0.150.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-VAUYT6PD.js → agent-hooks-SEOGOGKX.js} +10 -10
- package/dist/api/conversations/aggregate.d.ts +2 -0
- package/dist/api/conversations/create.d.ts +1 -1
- package/dist/api/conversations/pending-messages.d.ts +15 -0
- package/dist/api/people/shared.d.ts +2 -0
- package/dist/api/schema/conversation.d.ts +78 -4
- package/dist/api/schema.d.ts +2 -2
- package/dist/api/schema.js +7 -1
- package/dist/api.d.ts +1 -1
- package/dist/api.js +186 -35
- package/dist/app.js +152 -186
- package/dist/{catalog-runtime-WADOZVTB.js → catalog-runtime-HA7RIWYT.js} +5 -5
- package/dist/chat/agent/index.d.ts +5 -4
- package/dist/chat/agent/prompt.d.ts +8 -5
- package/dist/chat/agent/resume.d.ts +2 -2
- package/dist/chat/agent/tools.d.ts +4 -7
- package/dist/chat/agent/{request.d.ts → types.d.ts} +138 -129
- package/dist/chat/agent-dispatch/types.d.ts +2 -2
- package/dist/chat/agent-invocations/spawn.d.ts +2 -2
- package/dist/chat/api-turns/work.d.ts +6 -1
- package/dist/chat/app/services.d.ts +1 -1
- package/dist/chat/plugins/viewer.d.ts +4 -0
- package/dist/chat/runtime/agent-runner.d.ts +7 -5
- package/dist/chat/runtime/reply-executor.d.ts +0 -2
- package/dist/chat/runtime/slack-resume.d.ts +3 -3
- package/dist/chat/services/conversation-memory.d.ts +2 -2
- package/dist/chat/services/conversation-title.d.ts +20 -0
- package/dist/chat/slack/assistant-thread/title.d.ts +10 -27
- package/dist/chat/state/conversation.d.ts +1 -0
- package/dist/chat/task-execution/paused-turn.d.ts +2 -2
- package/dist/chat/tools/types.d.ts +1 -1
- package/dist/{chunk-QOWHHM3F.js → chunk-3GL327SC.js} +1 -1
- package/dist/{chunk-45LOR4PU.js → chunk-3JVUWLR7.js} +45 -1
- package/dist/{chunk-4GXJUYJV.js → chunk-3ZMKDHCT.js} +2 -2
- package/dist/{chunk-DUY5CDAM.js → chunk-4KAYAIBV.js} +1 -1
- package/dist/{chunk-ULAU5ZGU.js → chunk-4VH7KLGC.js} +106 -113
- package/dist/{chunk-V2FXMOEA.js → chunk-64K7GKRW.js} +2 -2
- package/dist/{chunk-2B3PT3V2.js → chunk-6PFDWAK2.js} +1 -1
- package/dist/{chunk-LNVEIJ2L.js → chunk-7D4GNUV2.js} +5 -5
- package/dist/{chunk-ZPL2RR7F.js → chunk-7GOP6VFJ.js} +40 -36
- package/dist/{chunk-HWQGBIH7.js → chunk-7NLW2NF5.js} +1 -1
- package/dist/{chunk-55F4QU2V.js → chunk-7QB2CKCW.js} +1 -1
- package/dist/{chunk-WCN4YSH7.js → chunk-DWETNV66.js} +6 -6
- package/dist/{chunk-D3WPMGEX.js → chunk-F5NMSEBL.js} +1 -1
- package/dist/{chunk-IL4EDBMV.js → chunk-FEFEIA3Z.js} +5 -5
- package/dist/{chunk-WOONT5ZP.js → chunk-H2RLIJ7G.js} +1 -1
- package/dist/{chunk-VRHTNBA6.js → chunk-ILYL6YFD.js} +3 -3
- package/dist/{chunk-VCYSOXON.js → chunk-IPGNWXA3.js} +545 -7
- package/dist/{chunk-LAZGHEYU.js → chunk-IZRSLANA.js} +1 -1
- package/dist/{chunk-EAI67NTM.js → chunk-JQXJ2E53.js} +1 -1
- package/dist/{chunk-HLPLT3NY.js → chunk-M5OZSVJI.js} +8 -4
- package/dist/{chunk-XJ42NBP5.js → chunk-MCNP7A7T.js} +8 -8
- package/dist/{chunk-OJCP3C3J.js → chunk-NCGVQXX5.js} +22 -26
- package/dist/{chunk-4AQZYNHM.js → chunk-NNOCRVQO.js} +2 -2
- package/dist/{chunk-GXYDVZKM.js → chunk-P43QM7JY.js} +5 -5
- package/dist/{chunk-EINWQFXI.js → chunk-QPIPYVGE.js} +5 -5
- package/dist/{chunk-GDFF3BQG.js → chunk-RF6J3DIO.js} +34 -36
- package/dist/{chunk-D6YX4NNN.js → chunk-SH6ZKJFZ.js} +4 -4
- package/dist/{chunk-LE5H7LMT.js → chunk-SHDREBGC.js} +1 -1
- package/dist/{chunk-VMNRXKAH.js → chunk-TZLYAO2J.js} +191 -134
- package/dist/{chunk-RRJV5UR5.js → chunk-UIMBNY6O.js} +11 -337
- package/dist/{chunk-ZCHIC2GM.js → chunk-UOFQEVR5.js} +15 -6
- package/dist/{chunk-7OEIXFC3.js → chunk-Y2VZ54CQ.js} +12 -12
- package/dist/{chunk-VY6W3T43.js → chunk-ZBOGYZ53.js} +4 -4
- package/dist/{chunk-POSZWRGL.js → chunk-ZUQT35YC.js} +1 -1
- package/dist/cli/chat.js +31 -32
- package/dist/cli/check.js +6 -6
- package/dist/cli/plugins.js +13 -13
- package/dist/cli/snapshot-create.js +9 -9
- package/dist/cli/upgrade.js +7 -7
- package/dist/{db-XUKZRFFK.js → db-HUW75QG7.js} +7 -7
- package/dist/instrumentation.js +4 -4
- package/dist/nitro.js +2 -2
- package/dist/{oauth-callback-server-LPZMMGFI.js → oauth-callback-server-DP7NIZPQ.js} +24 -25
- package/dist/{runner-DGKUTMPX.js → runner-AINV7K3M.js} +47 -45
- package/dist/{skills-P7YUFSXM.js → skills-PKS37E6B.js} +6 -6
- package/dist/spawn-NJCDEPPL.js +38 -0
- package/dist/{validation-S7U5M5NR.js → validation-PDC3MVMM.js} +6 -6
- package/dist/version.js +1 -1
- package/dist/{work-GAA7TVAD.js → work-KJLJRPXS.js} +17 -17
- package/dist/{worker-JXPJH3QB.js → worker-TYQ7YVIR.js} +11 -11
- package/package.json +5 -5
- package/dist/chunk-4EA2SFK2.js +0 -137
- package/dist/spawn-DSK3ZEVD.js +0 -38
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
instructionProvenanceFor,
|
|
14
14
|
sanitizePostgresJson,
|
|
15
15
|
withConversationEventLock
|
|
16
|
-
} from "./chunk-
|
|
16
|
+
} from "./chunk-ILYL6YFD.js";
|
|
17
17
|
import {
|
|
18
18
|
SANDBOX_WORKSPACE_ROOT,
|
|
19
19
|
sleep
|
|
@@ -23,18 +23,26 @@ import {
|
|
|
23
23
|
fenceLock,
|
|
24
24
|
getStateAdapter,
|
|
25
25
|
withLock
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-6PFDWAK2.js";
|
|
27
27
|
import {
|
|
28
28
|
botConfig,
|
|
29
|
+
completeText,
|
|
30
|
+
escapeXml,
|
|
31
|
+
resolveGatewayModel,
|
|
29
32
|
unwrapCurrentInstruction
|
|
30
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-3ZMKDHCT.js";
|
|
31
34
|
import {
|
|
32
35
|
toStoredSlackActor
|
|
33
36
|
} from "./chunk-HZF5P5UN.js";
|
|
37
|
+
import {
|
|
38
|
+
parseSlackMessageTs
|
|
39
|
+
} from "./chunk-F3B2YJ76.js";
|
|
34
40
|
import {
|
|
35
41
|
getActiveTraceId,
|
|
36
|
-
logException
|
|
37
|
-
|
|
42
|
+
logException,
|
|
43
|
+
logWarn,
|
|
44
|
+
setSpanAttributes
|
|
45
|
+
} from "./chunk-JQXJ2E53.js";
|
|
38
46
|
|
|
39
47
|
// src/chat/turn-context-tag.ts
|
|
40
48
|
var TURN_CONTEXT_TAG = "runtime-turn-context";
|
|
@@ -173,10 +181,518 @@ function stripRuntimeTurnContext(messages) {
|
|
|
173
181
|
});
|
|
174
182
|
}
|
|
175
183
|
|
|
184
|
+
// src/chat/services/context-budget.ts
|
|
185
|
+
var COMPACTION_TRIGGER_RATIO = 0.9;
|
|
186
|
+
var CONTEXT_INPUT_LIMIT_RATIO = 0.95;
|
|
187
|
+
var COMPACTION_TARGET_RATIO = 0.8;
|
|
188
|
+
var FALLBACK_CONTEXT_WINDOW_TOKENS = 4e5;
|
|
189
|
+
function positiveInteger(value, fallback) {
|
|
190
|
+
return Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
191
|
+
}
|
|
192
|
+
function estimateTextTokens(text) {
|
|
193
|
+
return Math.ceil(text.length / 4);
|
|
194
|
+
}
|
|
195
|
+
function calculateContextCompactionTriggerTokens(model) {
|
|
196
|
+
const contextWindow = positiveInteger(
|
|
197
|
+
model.contextWindow,
|
|
198
|
+
FALLBACK_CONTEXT_WINDOW_TOKENS
|
|
199
|
+
);
|
|
200
|
+
return Math.max(1, Math.floor(contextWindow * COMPACTION_TRIGGER_RATIO));
|
|
201
|
+
}
|
|
202
|
+
function calculateContextInputLimitTokens(model) {
|
|
203
|
+
const contextWindow = positiveInteger(
|
|
204
|
+
model.contextWindow,
|
|
205
|
+
FALLBACK_CONTEXT_WINDOW_TOKENS
|
|
206
|
+
);
|
|
207
|
+
return Math.max(1, Math.floor(contextWindow * CONTEXT_INPUT_LIMIT_RATIO));
|
|
208
|
+
}
|
|
209
|
+
function calculateContextCompactionTargetTokens(triggerTokens) {
|
|
210
|
+
return Math.max(1, Math.floor(triggerTokens * COMPACTION_TARGET_RATIO));
|
|
211
|
+
}
|
|
212
|
+
function getModelContextBudget(modelId) {
|
|
213
|
+
const model = resolveGatewayModel(modelId);
|
|
214
|
+
const advertisedContextWindow = positiveInteger(
|
|
215
|
+
model.contextWindow,
|
|
216
|
+
FALLBACK_CONTEXT_WINDOW_TOKENS
|
|
217
|
+
);
|
|
218
|
+
return {
|
|
219
|
+
contextWindow: Math.min(
|
|
220
|
+
botConfig.contextWindowTokens,
|
|
221
|
+
advertisedContextWindow
|
|
222
|
+
)
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
function getAgentContextCompactionTriggerTokens(modelId) {
|
|
226
|
+
return calculateContextCompactionTriggerTokens(
|
|
227
|
+
getModelContextBudget(modelId)
|
|
228
|
+
);
|
|
229
|
+
}
|
|
230
|
+
function getAgentContextInputLimitTokens(modelId) {
|
|
231
|
+
return calculateContextInputLimitTokens(getModelContextBudget(modelId));
|
|
232
|
+
}
|
|
233
|
+
function getConversationContextCompactionTriggerTokens() {
|
|
234
|
+
return calculateContextCompactionTriggerTokens(
|
|
235
|
+
getModelContextBudget(botConfig.fastModelId)
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// src/chat/services/conversation-memory.ts
|
|
240
|
+
var CONTEXT_MIN_LIVE_MESSAGES = 12;
|
|
241
|
+
var CONTEXT_COMPACTION_BATCH_SIZE = 24;
|
|
242
|
+
var CONTEXT_MAX_COMPACTIONS = 16;
|
|
243
|
+
var CONTEXT_MAX_MESSAGE_CHARS = 3200;
|
|
244
|
+
var SLACK_USER_ID_DISPLAY_PATTERN = /^[UW][A-Z0-9]{5,}$/;
|
|
245
|
+
function generateConversationId(prefix) {
|
|
246
|
+
return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
247
|
+
}
|
|
248
|
+
function normalizeConversationText(text) {
|
|
249
|
+
return text.replace(/\r\n?/g, "\n").trim();
|
|
250
|
+
}
|
|
251
|
+
function buildImageContextSuffix(message, conversation) {
|
|
252
|
+
const byFileId = conversation?.vision.byFileId;
|
|
253
|
+
const imageFileIds = message.meta?.imageFileIds ?? [];
|
|
254
|
+
if (!byFileId || imageFileIds.length === 0) {
|
|
255
|
+
return "";
|
|
256
|
+
}
|
|
257
|
+
const summaries = imageFileIds.map((fileId) => byFileId[fileId]?.summary?.trim()).filter((summary) => Boolean(summary));
|
|
258
|
+
if (summaries.length === 0) {
|
|
259
|
+
return "";
|
|
260
|
+
}
|
|
261
|
+
return ` [image context: ${summaries.join(" | ")}]`;
|
|
262
|
+
}
|
|
263
|
+
function renderConversationMessageLine(message, conversation) {
|
|
264
|
+
const displayName = conversationAuthorDisplayName(message);
|
|
265
|
+
const text = message.text.replace(/\s+/g, " ").slice(0, CONTEXT_MAX_MESSAGE_CHARS);
|
|
266
|
+
const markers = [];
|
|
267
|
+
if (message.meta?.replied === false) {
|
|
268
|
+
markers.push(
|
|
269
|
+
`assistant skipped: ${message.meta?.skippedReason ?? "no-reply route"}`
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
if (message.meta?.explicitMention) {
|
|
273
|
+
markers.push("explicit mention");
|
|
274
|
+
}
|
|
275
|
+
const markerSuffix = markers.length > 0 ? ` (${markers.join("; ")})` : "";
|
|
276
|
+
const imageContext = buildImageContextSuffix(message, conversation);
|
|
277
|
+
return `[${message.role}] ${displayName}: ${text}${imageContext}${markerSuffix}`;
|
|
278
|
+
}
|
|
279
|
+
function conversationAuthorDisplayName(message) {
|
|
280
|
+
const author = message.author;
|
|
281
|
+
const fullName = authorDisplayField(author?.fullName, author?.userId);
|
|
282
|
+
const userName = authorDisplayField(author?.userName, author?.userId);
|
|
283
|
+
return fullName ?? userName ?? (message.role === "assistant" ? botConfig.userName : message.role);
|
|
284
|
+
}
|
|
285
|
+
function authorDisplayField(value, userId) {
|
|
286
|
+
if (!value || value === userId || SLACK_USER_ID_DISPLAY_PATTERN.test(value)) {
|
|
287
|
+
return void 0;
|
|
288
|
+
}
|
|
289
|
+
return value;
|
|
290
|
+
}
|
|
291
|
+
function estimateConversationContextTokens(conversation) {
|
|
292
|
+
return estimateTextTokens(buildConversationContext(conversation) ?? "");
|
|
293
|
+
}
|
|
294
|
+
function upsertConversationMessage(conversation, message) {
|
|
295
|
+
const existingIndex = conversation.messages.findIndex(
|
|
296
|
+
(entry) => entry.id === message.id
|
|
297
|
+
);
|
|
298
|
+
if (existingIndex >= 0) {
|
|
299
|
+
conversation.messages[existingIndex] = {
|
|
300
|
+
...conversation.messages[existingIndex],
|
|
301
|
+
...message,
|
|
302
|
+
meta: {
|
|
303
|
+
...conversation.messages[existingIndex]?.meta,
|
|
304
|
+
...message.meta
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
return message.id;
|
|
308
|
+
}
|
|
309
|
+
conversation.messages.push(message);
|
|
310
|
+
return message.id;
|
|
311
|
+
}
|
|
312
|
+
function recordDeliveredAssistantMessage(args) {
|
|
313
|
+
const prefix = `${args.sessionId}:assistant:`;
|
|
314
|
+
const ordinal = args.conversation.messages.filter(
|
|
315
|
+
(message) => message.id.startsWith(prefix)
|
|
316
|
+
).length + 1;
|
|
317
|
+
const messageId = `${prefix}${ordinal}`;
|
|
318
|
+
markConversationMessage(args.conversation, args.userMessageId, {
|
|
319
|
+
replied: true,
|
|
320
|
+
skippedReason: void 0
|
|
321
|
+
});
|
|
322
|
+
upsertConversationMessage(args.conversation, {
|
|
323
|
+
id: messageId,
|
|
324
|
+
role: "assistant",
|
|
325
|
+
text: normalizeConversationText(args.text),
|
|
326
|
+
createdAtMs: Date.now(),
|
|
327
|
+
author: {
|
|
328
|
+
userName: botConfig.userName,
|
|
329
|
+
isBot: true
|
|
330
|
+
},
|
|
331
|
+
meta: {
|
|
332
|
+
replied: true,
|
|
333
|
+
...args.source ? { source: args.source } : {}
|
|
334
|
+
}
|
|
335
|
+
});
|
|
336
|
+
return messageId;
|
|
337
|
+
}
|
|
338
|
+
function turnHasReply(conversation, turnId) {
|
|
339
|
+
const assistantPrefix = `${turnId}:assistant:`;
|
|
340
|
+
return conversation.messages.some(
|
|
341
|
+
(message) => message.role === "assistant" && message.id.startsWith(assistantPrefix)
|
|
342
|
+
);
|
|
343
|
+
}
|
|
344
|
+
function markConversationMessage(conversation, messageId, patch) {
|
|
345
|
+
if (!messageId) return;
|
|
346
|
+
const messageIndex = conversation.messages.findIndex(
|
|
347
|
+
(entry) => entry.id === messageId
|
|
348
|
+
);
|
|
349
|
+
if (messageIndex < 0) return;
|
|
350
|
+
const current = conversation.messages[messageIndex];
|
|
351
|
+
conversation.messages[messageIndex] = {
|
|
352
|
+
...current,
|
|
353
|
+
meta: {
|
|
354
|
+
...current.meta ?? {},
|
|
355
|
+
...patch
|
|
356
|
+
}
|
|
357
|
+
};
|
|
358
|
+
}
|
|
359
|
+
function buildConversationContext(conversation, options = {}) {
|
|
360
|
+
const messages = conversation.messages.filter(
|
|
361
|
+
(entry) => entry.id !== options.excludeMessageId
|
|
362
|
+
);
|
|
363
|
+
if (messages.length === 0 && conversation.compactions.length === 0) {
|
|
364
|
+
return void 0;
|
|
365
|
+
}
|
|
366
|
+
const lines = [];
|
|
367
|
+
if (conversation.compactions.length > 0) {
|
|
368
|
+
lines.push("<thread-compactions>");
|
|
369
|
+
for (const [index, compaction] of conversation.compactions.entries()) {
|
|
370
|
+
lines.push(
|
|
371
|
+
` <compaction index="${index + 1}" covered_messages="${compaction.coveredMessageCount}" created_at="${new Date(compaction.createdAtMs).toISOString()}">`,
|
|
372
|
+
compaction.summary,
|
|
373
|
+
" </compaction>"
|
|
374
|
+
);
|
|
375
|
+
}
|
|
376
|
+
lines.push("</thread-compactions>");
|
|
377
|
+
}
|
|
378
|
+
if (messages.length > 0) {
|
|
379
|
+
if (lines.length > 0) {
|
|
380
|
+
lines.push("");
|
|
381
|
+
}
|
|
382
|
+
lines.push("<thread-transcript>");
|
|
383
|
+
for (const [index, message] of messages.entries()) {
|
|
384
|
+
const author = escapeXml(conversationAuthorDisplayName(message));
|
|
385
|
+
const actorIdAttr = message.author?.userId ? ` actor_id="${escapeXml(message.author.userId)}"` : "";
|
|
386
|
+
const ts = new Date(message.createdAtMs).toISOString();
|
|
387
|
+
const slackTsAttr = message.meta?.slackTs ? ` slack_ts="${escapeXml(message.meta.slackTs)}"` : "";
|
|
388
|
+
lines.push(
|
|
389
|
+
` <message index="${index + 1}" ts="${ts}" role="${message.role}" author="${author}"${actorIdAttr}${slackTsAttr}>`,
|
|
390
|
+
renderConversationMessageLine(message, conversation),
|
|
391
|
+
" </message>"
|
|
392
|
+
);
|
|
393
|
+
}
|
|
394
|
+
lines.push("</thread-transcript>");
|
|
395
|
+
}
|
|
396
|
+
return lines.join("\n");
|
|
397
|
+
}
|
|
398
|
+
function pruneCompactions(compactions) {
|
|
399
|
+
if (compactions.length <= CONTEXT_MAX_COMPACTIONS) {
|
|
400
|
+
return compactions;
|
|
401
|
+
}
|
|
402
|
+
const overflowCount = compactions.length - CONTEXT_MAX_COMPACTIONS + 1;
|
|
403
|
+
const merged = compactions.slice(0, overflowCount);
|
|
404
|
+
const mergedSummary = merged.map((entry) => entry.summary).join("\n").slice(0, 3500);
|
|
405
|
+
const coveredMessageCount = merged.reduce(
|
|
406
|
+
(count, entry) => count + entry.coveredMessageCount,
|
|
407
|
+
0
|
|
408
|
+
);
|
|
409
|
+
const compacted = {
|
|
410
|
+
id: generateConversationId("compaction"),
|
|
411
|
+
createdAtMs: Date.now(),
|
|
412
|
+
summary: mergedSummary,
|
|
413
|
+
coveredMessageCount
|
|
414
|
+
};
|
|
415
|
+
return [compacted, ...compactions.slice(overflowCount)];
|
|
416
|
+
}
|
|
417
|
+
async function summarizeConversationChunk(messages, conversation, context, deps) {
|
|
418
|
+
const transcript = messages.map((message) => renderConversationMessageLine(message, conversation)).join("\n");
|
|
419
|
+
try {
|
|
420
|
+
const result = await deps.completeText({
|
|
421
|
+
modelId: botConfig.fastModelId,
|
|
422
|
+
temperature: 0,
|
|
423
|
+
promptName: "junior.thread_compaction",
|
|
424
|
+
messages: [
|
|
425
|
+
{
|
|
426
|
+
role: "user",
|
|
427
|
+
content: [
|
|
428
|
+
"Summarize the following older Slack thread transcript segment for future assistant turns.",
|
|
429
|
+
"Keep the summary factual and concise. Do not invent details.",
|
|
430
|
+
"",
|
|
431
|
+
"Output exactly three XML sections in this order:",
|
|
432
|
+
"<active-asks> one bullet per outstanding user ask that has not been narrowed, answered, or replaced by a later turn. Omit the section body if none. </active-asks>",
|
|
433
|
+
"<resolved-or-replaced-asks> one bullet per ask that has been replaced, narrowed, answered, or already acted on in this segment. Include the replacement/outcome inline. Omit the section body if none. </resolved-or-replaced-asks>",
|
|
434
|
+
"<facts> one bullet per durable fact useful regardless of scope: names, ids, URLs, decisions, locations, preferences, constraints that remain true. Omit the section body if none. </facts>",
|
|
435
|
+
"",
|
|
436
|
+
"Do not output any text outside the three sections.",
|
|
437
|
+
"",
|
|
438
|
+
transcript
|
|
439
|
+
].join("\n"),
|
|
440
|
+
timestamp: Date.now()
|
|
441
|
+
}
|
|
442
|
+
],
|
|
443
|
+
metadata: {
|
|
444
|
+
modelId: botConfig.fastModelId,
|
|
445
|
+
threadId: context.threadId ?? "",
|
|
446
|
+
channelId: context.channelId ?? "",
|
|
447
|
+
actorId: context.actorId ?? "",
|
|
448
|
+
runId: context.runId ?? ""
|
|
449
|
+
}
|
|
450
|
+
});
|
|
451
|
+
const summary = result.text.trim();
|
|
452
|
+
if (summary.length > 0) {
|
|
453
|
+
return summary.slice(0, 3500);
|
|
454
|
+
}
|
|
455
|
+
} catch (error) {
|
|
456
|
+
logWarn("conversation.compaction.summary.failed", {
|
|
457
|
+
"exception.message": error instanceof Error ? error.message : String(error),
|
|
458
|
+
"app.compaction_messages_covered": messages.length
|
|
459
|
+
});
|
|
460
|
+
}
|
|
461
|
+
return transcript.slice(0, 2800);
|
|
462
|
+
}
|
|
463
|
+
function getThreadTitleSourceMessage(conversation) {
|
|
464
|
+
let firstMessage;
|
|
465
|
+
for (const message of conversation.messages) {
|
|
466
|
+
if (!isHumanConversationMessage(message)) {
|
|
467
|
+
continue;
|
|
468
|
+
}
|
|
469
|
+
if (!firstMessage) {
|
|
470
|
+
firstMessage = message;
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
if (message.createdAtMs < firstMessage.createdAtMs) {
|
|
474
|
+
firstMessage = message;
|
|
475
|
+
continue;
|
|
476
|
+
}
|
|
477
|
+
if (message.createdAtMs === firstMessage.createdAtMs && message.id < firstMessage.id) {
|
|
478
|
+
firstMessage = message;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
return firstMessage;
|
|
482
|
+
}
|
|
483
|
+
async function compactConversationIfNeededWithDeps(conversation, context, deps) {
|
|
484
|
+
let estimatedTokens = estimateConversationContextTokens(conversation);
|
|
485
|
+
setSpanAttributes({
|
|
486
|
+
"app.context_tokens_estimated": estimatedTokens
|
|
487
|
+
});
|
|
488
|
+
const triggerTokens = getConversationContextCompactionTriggerTokens();
|
|
489
|
+
const targetTokens = calculateContextCompactionTargetTokens(triggerTokens);
|
|
490
|
+
while (estimatedTokens > triggerTokens && conversation.messages.length > CONTEXT_MIN_LIVE_MESSAGES) {
|
|
491
|
+
const compactCount = Math.min(
|
|
492
|
+
CONTEXT_COMPACTION_BATCH_SIZE,
|
|
493
|
+
conversation.messages.length - CONTEXT_MIN_LIVE_MESSAGES
|
|
494
|
+
);
|
|
495
|
+
if (compactCount <= 0) {
|
|
496
|
+
break;
|
|
497
|
+
}
|
|
498
|
+
const compactedChunk = conversation.messages.slice(0, compactCount);
|
|
499
|
+
const summary = await summarizeConversationChunk(
|
|
500
|
+
compactedChunk,
|
|
501
|
+
conversation,
|
|
502
|
+
context,
|
|
503
|
+
deps
|
|
504
|
+
);
|
|
505
|
+
conversation.compactions.push({
|
|
506
|
+
id: generateConversationId("compaction"),
|
|
507
|
+
createdAtMs: Date.now(),
|
|
508
|
+
summary,
|
|
509
|
+
coveredMessageCount: compactedChunk.length
|
|
510
|
+
});
|
|
511
|
+
conversation.compactions = pruneCompactions(conversation.compactions);
|
|
512
|
+
conversation.messages = conversation.messages.slice(compactCount);
|
|
513
|
+
estimatedTokens = estimateConversationContextTokens(conversation);
|
|
514
|
+
setSpanAttributes({
|
|
515
|
+
"app.compaction_messages_covered": compactCount,
|
|
516
|
+
"app.compaction.trigger_tokens": triggerTokens,
|
|
517
|
+
"app.compaction.target_tokens": targetTokens,
|
|
518
|
+
"app.context_tokens_estimated": estimatedTokens
|
|
519
|
+
});
|
|
520
|
+
if (estimatedTokens <= targetTokens) {
|
|
521
|
+
break;
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
function createConversationMemoryService(deps) {
|
|
526
|
+
return {
|
|
527
|
+
compactConversationIfNeeded: async (conversation, context) => await compactConversationIfNeededWithDeps(conversation, context, deps)
|
|
528
|
+
};
|
|
529
|
+
}
|
|
530
|
+
function isHumanConversationMessage(message) {
|
|
531
|
+
return message.role === "user" && message.author?.isBot !== true;
|
|
532
|
+
}
|
|
533
|
+
function getConversationMessageSlackTs(message) {
|
|
534
|
+
return parseSlackMessageTs(message.meta?.slackTs);
|
|
535
|
+
}
|
|
536
|
+
|
|
176
537
|
// src/chat/conversations/messages.ts
|
|
177
538
|
import { createHash } from "crypto";
|
|
178
539
|
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
179
540
|
|
|
541
|
+
// src/chat/services/short-title.ts
|
|
542
|
+
var SHORT_TITLE_MAX_LENGTH = 60;
|
|
543
|
+
var TITLE_PROMPTS = {
|
|
544
|
+
conversation: [
|
|
545
|
+
"Generate a concise 5-8 word conversation title from the first user message below.",
|
|
546
|
+
"Capture the user's main request.",
|
|
547
|
+
"Reply with ONLY the title, with no quotes or trailing punctuation."
|
|
548
|
+
],
|
|
549
|
+
task: [
|
|
550
|
+
"Generate a concise 5-8 word task title from the task instruction below.",
|
|
551
|
+
"Capture the task's main purpose.",
|
|
552
|
+
"Reply with ONLY the title, with no quotes or trailing punctuation."
|
|
553
|
+
]
|
|
554
|
+
};
|
|
555
|
+
function normalizeShortTitle(value) {
|
|
556
|
+
return value.replace(/\s+/g, " ").trim().slice(0, SHORT_TITLE_MAX_LENGTH);
|
|
557
|
+
}
|
|
558
|
+
function fallbackShortTitle(sourceText, fallback = "Untitled") {
|
|
559
|
+
const firstLine = sourceText.split(/\r?\n/).map((line) => line.trim()).find((line) => line.length > 0) ?? "";
|
|
560
|
+
return normalizeShortTitle(firstLine) || fallback;
|
|
561
|
+
}
|
|
562
|
+
async function generateShortTitle(args) {
|
|
563
|
+
const sourceText = args.sourceText.trim();
|
|
564
|
+
if (!sourceText) return void 0;
|
|
565
|
+
try {
|
|
566
|
+
const result = await args.completeText({
|
|
567
|
+
modelId: botConfig.fastModelId,
|
|
568
|
+
temperature: 0,
|
|
569
|
+
promptName: `junior.${args.kind}_title`,
|
|
570
|
+
messages: [
|
|
571
|
+
{
|
|
572
|
+
role: "user",
|
|
573
|
+
content: [
|
|
574
|
+
...TITLE_PROMPTS[args.kind],
|
|
575
|
+
"",
|
|
576
|
+
args.kind === "conversation" ? `First user message: ${sourceText.slice(0, 500)}` : `Task instruction: ${sourceText.slice(0, 500)}`
|
|
577
|
+
].join("\n"),
|
|
578
|
+
timestamp: Date.now()
|
|
579
|
+
}
|
|
580
|
+
],
|
|
581
|
+
metadata: {
|
|
582
|
+
modelId: botConfig.fastModelId
|
|
583
|
+
}
|
|
584
|
+
});
|
|
585
|
+
const title = normalizeShortTitle(result.text);
|
|
586
|
+
return title || void 0;
|
|
587
|
+
} catch (error) {
|
|
588
|
+
logWarn(`${args.kind}.title.generation.failed`, {
|
|
589
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
590
|
+
});
|
|
591
|
+
return void 0;
|
|
592
|
+
}
|
|
593
|
+
}
|
|
594
|
+
async function resolveTaskTitle(args) {
|
|
595
|
+
const explicit = normalizeShortTitle(args.title ?? "");
|
|
596
|
+
if (explicit) return explicit;
|
|
597
|
+
return generateShortTitle({
|
|
598
|
+
completeText: args.completeText,
|
|
599
|
+
kind: "task",
|
|
600
|
+
sourceText: args.instruction
|
|
601
|
+
});
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
// src/chat/services/conversation-title.ts
|
|
605
|
+
var inFlightTitles = /* @__PURE__ */ new Map();
|
|
606
|
+
async function ensureConversationTitle(args) {
|
|
607
|
+
const pending = inFlightTitles.get(args.conversationId);
|
|
608
|
+
if (pending) {
|
|
609
|
+
return pending;
|
|
610
|
+
}
|
|
611
|
+
const work = ensureConversationTitleOnce(args).finally(() => {
|
|
612
|
+
inFlightTitles.delete(args.conversationId);
|
|
613
|
+
});
|
|
614
|
+
inFlightTitles.set(args.conversationId, work);
|
|
615
|
+
return work;
|
|
616
|
+
}
|
|
617
|
+
async function ensureConversationTitleOnce(args) {
|
|
618
|
+
if (!args.conversation.messages.some(isHumanConversationMessage)) {
|
|
619
|
+
return void 0;
|
|
620
|
+
}
|
|
621
|
+
const store = args.conversationStore ?? getConversationStore();
|
|
622
|
+
try {
|
|
623
|
+
const stored = await store.get({ conversationId: args.conversationId });
|
|
624
|
+
if (stored?.title?.trim()) {
|
|
625
|
+
return void 0;
|
|
626
|
+
}
|
|
627
|
+
if (stored?.lineage) {
|
|
628
|
+
return void 0;
|
|
629
|
+
}
|
|
630
|
+
const titleSourceMessage = getThreadTitleSourceMessage(args.conversation);
|
|
631
|
+
const sourceText = titleSourceMessage?.text.trim() ?? "";
|
|
632
|
+
if (!titleSourceMessage || !sourceText) {
|
|
633
|
+
return void 0;
|
|
634
|
+
}
|
|
635
|
+
let title;
|
|
636
|
+
try {
|
|
637
|
+
const generated = await generateShortTitle({
|
|
638
|
+
completeText,
|
|
639
|
+
kind: "conversation",
|
|
640
|
+
sourceText
|
|
641
|
+
});
|
|
642
|
+
title = generated ?? fallbackShortTitle(sourceText, "Conversation");
|
|
643
|
+
} catch (error) {
|
|
644
|
+
logWarn("conversation.title.generation.failed", {
|
|
645
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
646
|
+
});
|
|
647
|
+
title = fallbackShortTitle(sourceText, "Conversation");
|
|
648
|
+
}
|
|
649
|
+
const normalized = title.trim();
|
|
650
|
+
if (!normalized) {
|
|
651
|
+
return void 0;
|
|
652
|
+
}
|
|
653
|
+
const activityAtMs = titleSourceMessage.createdAtMs;
|
|
654
|
+
const nowMs = args.nowMs ?? Date.now();
|
|
655
|
+
try {
|
|
656
|
+
await store.recordActivity({
|
|
657
|
+
activityAtMs,
|
|
658
|
+
conversationId: args.conversationId,
|
|
659
|
+
nowMs,
|
|
660
|
+
title: normalized
|
|
661
|
+
});
|
|
662
|
+
} catch (error) {
|
|
663
|
+
logException(error, "conversation.title.persist.failed");
|
|
664
|
+
return void 0;
|
|
665
|
+
}
|
|
666
|
+
return normalized;
|
|
667
|
+
} catch (error) {
|
|
668
|
+
logException(error, "conversation.title.ensure.failed");
|
|
669
|
+
return void 0;
|
|
670
|
+
}
|
|
671
|
+
}
|
|
672
|
+
function scheduleConversationTitle(args) {
|
|
673
|
+
if (!args.conversationId) {
|
|
674
|
+
return;
|
|
675
|
+
}
|
|
676
|
+
void ensureConversationTitle({
|
|
677
|
+
conversation: args.conversation,
|
|
678
|
+
conversationId: args.conversationId
|
|
679
|
+
}).catch((error) => {
|
|
680
|
+
logException(error, "conversation.title.task.failed");
|
|
681
|
+
});
|
|
682
|
+
}
|
|
683
|
+
async function resolveConversationTitle(args) {
|
|
684
|
+
const pending = inFlightTitles.get(args.conversationId);
|
|
685
|
+
if (pending) {
|
|
686
|
+
const generated = await pending;
|
|
687
|
+
if (generated?.trim()) {
|
|
688
|
+
return generated.trim();
|
|
689
|
+
}
|
|
690
|
+
}
|
|
691
|
+
const store = args.conversationStore ?? getConversationStore();
|
|
692
|
+
const stored = await store.get({ conversationId: args.conversationId });
|
|
693
|
+
return stored?.title?.trim() || void 0;
|
|
694
|
+
}
|
|
695
|
+
|
|
180
696
|
// src/chat/conversations/message-summaries.ts
|
|
181
697
|
import { isDeepStrictEqual } from "util";
|
|
182
698
|
function projectConversationMessageSummaries(events) {
|
|
@@ -320,6 +836,10 @@ async function persistConversationMessages(args) {
|
|
|
320
836
|
...args,
|
|
321
837
|
conversationId: args.conversationId
|
|
322
838
|
});
|
|
839
|
+
scheduleConversationTitle({
|
|
840
|
+
conversation: args.conversation,
|
|
841
|
+
conversationId: args.conversationId
|
|
842
|
+
});
|
|
323
843
|
}
|
|
324
844
|
async function appendConversationMessages(eventStore, args) {
|
|
325
845
|
if (args.conversation.messages.length === 0) return;
|
|
@@ -1276,7 +1796,7 @@ var RIPGREP_EXCLUDED_GLOBS = [
|
|
|
1276
1796
|
];
|
|
1277
1797
|
var DEFAULT_SEARCH_COMMAND_TIMEOUT_MS = 3e4;
|
|
1278
1798
|
var SKIPPED_DIRECTORIES = /* @__PURE__ */ new Set([".git", "node_modules"]);
|
|
1279
|
-
function
|
|
1799
|
+
function positiveInteger2(value) {
|
|
1280
1800
|
if (typeof value !== "number" || !Number.isFinite(value)) {
|
|
1281
1801
|
return void 0;
|
|
1282
1802
|
}
|
|
@@ -2615,6 +3135,24 @@ export {
|
|
|
2615
3135
|
stripRuntimeTurnContext,
|
|
2616
3136
|
historyItemFromPiMessage,
|
|
2617
3137
|
piMessageFromHistoryItem,
|
|
3138
|
+
estimateTextTokens,
|
|
3139
|
+
getAgentContextCompactionTriggerTokens,
|
|
3140
|
+
getAgentContextInputLimitTokens,
|
|
3141
|
+
normalizeConversationText,
|
|
3142
|
+
estimateConversationContextTokens,
|
|
3143
|
+
upsertConversationMessage,
|
|
3144
|
+
recordDeliveredAssistantMessage,
|
|
3145
|
+
turnHasReply,
|
|
3146
|
+
markConversationMessage,
|
|
3147
|
+
buildConversationContext,
|
|
3148
|
+
createConversationMemoryService,
|
|
3149
|
+
isHumanConversationMessage,
|
|
3150
|
+
getConversationMessageSlackTs,
|
|
3151
|
+
SHORT_TITLE_MAX_LENGTH,
|
|
3152
|
+
fallbackShortTitle,
|
|
3153
|
+
generateShortTitle,
|
|
3154
|
+
resolveTaskTitle,
|
|
3155
|
+
resolveConversationTitle,
|
|
2618
3156
|
persistConversationMessageSummaries,
|
|
2619
3157
|
hydrateConversationMessages,
|
|
2620
3158
|
persistConversationMessages,
|
|
@@ -2638,7 +3176,7 @@ export {
|
|
|
2638
3176
|
MAX_TEXT_CHARS,
|
|
2639
3177
|
RIPGREP_EXCLUDED_GLOBS,
|
|
2640
3178
|
DEFAULT_SEARCH_COMMAND_TIMEOUT_MS,
|
|
2641
|
-
positiveInteger,
|
|
3179
|
+
positiveInteger2 as positiveInteger,
|
|
2642
3180
|
normalizeToLf,
|
|
2643
3181
|
truncateText,
|
|
2644
3182
|
isMissingPathError,
|
|
@@ -8,14 +8,14 @@ import {
|
|
|
8
8
|
getConversationStore,
|
|
9
9
|
parseDestination,
|
|
10
10
|
sameDestination
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-ILYL6YFD.js";
|
|
12
12
|
import {
|
|
13
13
|
getDefaultRedisStateAdapterFor,
|
|
14
14
|
getStateAdapter
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-6PFDWAK2.js";
|
|
16
16
|
import {
|
|
17
17
|
getChatConfig
|
|
18
|
-
} from "./chunk-
|
|
18
|
+
} from "./chunk-3ZMKDHCT.js";
|
|
19
19
|
import {
|
|
20
20
|
parseStoredSlackActor
|
|
21
21
|
} from "./chunk-HZF5P5UN.js";
|
|
@@ -23,7 +23,7 @@ import {
|
|
|
23
23
|
isRecord,
|
|
24
24
|
toOptionalNumber,
|
|
25
25
|
toOptionalString
|
|
26
|
-
} from "./chunk-
|
|
26
|
+
} from "./chunk-JQXJ2E53.js";
|
|
27
27
|
|
|
28
28
|
// src/chat/task-execution/state.ts
|
|
29
29
|
import { randomUUID } from "crypto";
|
|
@@ -1300,6 +1300,9 @@ async function recordExecutionMetadata(args) {
|
|
|
1300
1300
|
updatedAtMs: conversation.updatedAtMs
|
|
1301
1301
|
});
|
|
1302
1302
|
}
|
|
1303
|
+
async function getConversation2(args) {
|
|
1304
|
+
return await getConversation(args);
|
|
1305
|
+
}
|
|
1303
1306
|
async function getConversationWorkState2(args) {
|
|
1304
1307
|
return await getConversationWorkState(args);
|
|
1305
1308
|
}
|
|
@@ -1500,6 +1503,7 @@ export {
|
|
|
1500
1503
|
CONVERSATION_WORK_STALE_ENQUEUE_MS,
|
|
1501
1504
|
CONVERSATION_WORK_MAX_RETRIES,
|
|
1502
1505
|
isFinalAttempt,
|
|
1506
|
+
getConversation2 as getConversation,
|
|
1503
1507
|
getConversationWorkState2 as getConversationWorkState,
|
|
1504
1508
|
recordConversationRetry2 as recordConversationRetry,
|
|
1505
1509
|
countPendingConversationMessages,
|