@sentry/junior 0.106.0 → 0.107.1
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-KR5BCWOK.js → agent-hooks-ICPIJAFY.js} +9 -7
- package/dist/api/conversations/detail.d.ts +1 -1
- package/dist/api/conversations/events.d.ts +14 -0
- package/dist/api/conversations/schema.d.ts +196 -463
- package/dist/api/schema.d.ts +2 -7
- package/dist/api/schema.js +10 -8
- package/dist/api.js +377 -810
- package/dist/app.js +3586 -3614
- package/dist/{catalog-runtime-5CVM2DOF.js → catalog-runtime-FOFNPR3P.js} +3 -2
- package/dist/chat/agent/prompt.d.ts +1 -1
- package/dist/chat/agent/request.d.ts +28 -29
- package/dist/chat/agent/resume.d.ts +7 -7
- package/dist/chat/agent/session.d.ts +5 -6
- package/dist/chat/agent/tools.d.ts +3 -3
- package/dist/chat/agent-dispatch/runner.d.ts +2 -0
- package/dist/chat/config.d.ts +2 -2
- package/dist/chat/conversations/history.d.ts +582 -167
- package/dist/chat/conversations/message-projection.d.ts +6 -0
- package/dist/chat/conversations/message-summaries.d.ts +9 -0
- package/dist/chat/conversations/messages.d.ts +13 -27
- package/dist/chat/conversations/projection.d.ts +30 -53
- package/dist/chat/conversations/provenance.d.ts +42 -0
- package/dist/chat/conversations/search.d.ts +2 -2
- package/dist/chat/conversations/sql/conversation-row.d.ts +1 -1
- package/dist/chat/conversations/sql/event-lock.d.ts +3 -0
- package/dist/chat/conversations/sql/history.d.ts +3 -3
- package/dist/chat/conversations/sql/privacy.d.ts +27 -0
- package/dist/chat/conversations/sql/purge.d.ts +2 -10
- package/dist/chat/conversations/sql/store.d.ts +0 -5
- package/dist/chat/conversations/store.d.ts +8 -13
- package/dist/chat/conversations/turn-lifecycle.d.ts +41 -0
- package/dist/chat/db.d.ts +4 -10
- package/dist/chat/model-profile.d.ts +10 -0
- package/dist/chat/oauth-flow.d.ts +0 -5
- package/dist/chat/pi/conversation-events.d.ts +33 -0
- package/dist/chat/pi/sql-model-usage.d.ts +10 -0
- package/dist/chat/plugins/agent-hooks.d.ts +4 -2
- package/dist/chat/resource-events/ingest.d.ts +0 -13
- package/dist/chat/runtime/delivered-turn-state.d.ts +1 -1
- package/dist/chat/runtime/generated-artifacts.d.ts +1 -1
- package/dist/chat/runtime/reply-executor.d.ts +2 -0
- package/dist/chat/runtime/slack-resume.d.ts +23 -28
- package/dist/chat/runtime/slack-runtime.d.ts +2 -0
- package/dist/chat/runtime/turn.d.ts +14 -1
- package/dist/chat/services/context-compaction-marker.d.ts +0 -1
- package/dist/chat/services/context-compaction.d.ts +1 -0
- package/dist/chat/services/conversation-memory.d.ts +9 -0
- package/dist/chat/services/plugin-auth-orchestration.d.ts +1 -4
- package/dist/chat/services/subscribed-decision.d.ts +1 -1
- package/dist/chat/services/turn-failure-response.d.ts +10 -3
- package/dist/chat/services/turn-result.d.ts +7 -12
- package/dist/chat/services/{turn-reasoning-level.d.ts → turn-router.d.ts} +12 -8
- package/dist/chat/services/turn-session-record.d.ts +15 -16
- package/dist/chat/slack/adapter.d.ts +2 -2
- package/dist/chat/slack/errors.d.ts +2 -0
- package/dist/chat/slack/outbound.d.ts +0 -1
- package/dist/chat/slack/reply.d.ts +2 -8
- package/dist/chat/slack/tools/channel-capabilities.d.ts +2 -2
- package/dist/chat/slack/tools/{send-message.d.ts → send-files.d.ts} +27 -19
- package/dist/chat/state/conversation.d.ts +4 -4
- package/dist/chat/state/turn-id.d.ts +2 -0
- package/dist/chat/state/turn-session-keys.d.ts +6 -0
- package/dist/chat/state/turn-session.d.ts +11 -9
- package/dist/chat/task-execution/state.d.ts +6 -1
- package/dist/chat/task-execution/store.d.ts +1 -1
- package/dist/chat/tool-support/pi-tool-adapter.d.ts +1 -1
- package/dist/chat/tool-support/tool-execution-report.d.ts +1 -0
- package/dist/chat/tools/handoff/tool.d.ts +2 -2
- package/dist/chat/tools/sandbox/file-uploads.d.ts +28 -6
- package/dist/chat/tools/types.d.ts +6 -9
- package/dist/chat/tools/web/image-generate.d.ts +4 -6
- package/dist/{chunk-DDRQ4U7I.js → chunk-4YF7Z6IA.js} +390 -270
- package/dist/{chunk-AZ5OMSN5.js → chunk-AUUOHQAT.js} +2 -2
- package/dist/chunk-B2Z2H66D.js +250 -0
- package/dist/{chunk-4W4O4DCV.js → chunk-CQ7KSO2B.js} +75 -45
- package/dist/{chunk-MJSJAHBW.js → chunk-DY7TRYB5.js} +1 -1
- package/dist/{chunk-GPPUTY5M.js → chunk-EDLNHZH3.js} +861 -891
- package/dist/{chunk-R5BFYK2O.js → chunk-IGHMVDWI.js} +134 -18
- package/dist/chunk-MU6HHZEN.js +99 -0
- package/dist/{chunk-XTKJPSMF.js → chunk-NVOTGWYX.js} +458 -684
- package/dist/{chunk-AEWZ7F65.js → chunk-O24R4DCK.js} +2 -179
- package/dist/{chunk-CCFROYAV.js → chunk-PUAMO4PI.js} +115 -125
- package/dist/{chunk-ZMRLBBD5.js → chunk-RMVOAJRL.js} +30 -13
- package/dist/{chunk-3NYUCBLE.js → chunk-SPUAJVVH.js} +6 -4
- package/dist/{chunk-4UYIRWDW.js → chunk-SS67LUOK.js} +41 -290
- package/dist/{chunk-FXFQNHRU.js → chunk-VFUK3X5B.js} +4 -2
- package/dist/{chunk-3MPPVPGZ.js → chunk-VH6KWKG2.js} +2 -2
- package/dist/chunk-VIXFG2VC.js +182 -0
- package/dist/{chunk-BKM4UXUD.js → chunk-YFQ7CQDE.js} +669 -18
- package/dist/{chunk-77LHNQK4.js → chunk-YIPD3L6L.js} +4 -2
- package/dist/{chunk-3OUSJPR7.js → chunk-YNP2ATQX.js} +6 -4
- package/dist/{chunk-WR7H7VLJ.js → chunk-YT72TOEK.js} +2 -2
- package/dist/cli/chat.js +21 -20
- package/dist/cli/check.js +4 -3
- package/dist/cli/plugins.js +11 -9
- package/dist/cli/snapshot-warmup.js +7 -5
- package/dist/cli/upgrade.js +2041 -105
- package/dist/db/schema/{agent-steps.d.ts → conversation-events.d.ts} +37 -21
- package/dist/db/schema.d.ts +28 -159
- package/dist/{db-S33LFWG2.js → db-DIGO4TGW.js} +6 -8
- package/dist/{runner-HA5XAL6K.js → runner-ACR2HAIC.js} +209 -101
- package/dist/{skills-722AXRV5.js → skills-ITNHBARB.js} +4 -3
- package/dist/{validation-EUI2UFRM.js → validation-AGQ46ZGV.js} +4 -3
- package/migrations/0005_conversation_events.sql +122 -0
- package/migrations/README.md +23 -10
- package/migrations/meta/0005_snapshot.json +948 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +5 -5
- package/dist/api/conversations/activity.d.ts +0 -40
- package/dist/api/conversations/detail-projection.d.ts +0 -11
- package/dist/api/conversations/detail.query.d.ts +0 -3
- package/dist/api/conversations/subagent.d.ts +0 -10
- package/dist/api/conversations/transcript.d.ts +0 -28
- package/dist/chat/conversations/legacy-advisor-session.d.ts +0 -7
- package/dist/chat/conversations/legacy-import.d.ts +0 -38
- package/dist/chat/conversations/sql/legacy-history-import.d.ts +0 -55
- package/dist/chat/conversations/sql/messages.d.ts +0 -4
- package/dist/chat/conversations/visible-compactions.d.ts +0 -11
- package/dist/chat/conversations/visible-messages.d.ts +0 -47
- package/dist/chat/services/reply-delivery-plan.d.ts +0 -5
- package/dist/chat/state/session-log.d.ts +0 -384
- package/dist/chunk-P4P7O5WG.js +0 -1072
- package/dist/chunk-XIMUQ4IU.js +0 -147
- package/dist/db/schema/conversation-messages.d.ts +0 -153
- package/dist/handlers/github-webhook/check-suite.d.ts +0 -3
- package/dist/handlers/github-webhook/issue-comment.d.ts +0 -3
- package/dist/handlers/github-webhook/pull-request-review-comment.d.ts +0 -3
- package/dist/handlers/github-webhook/pull-request-review.d.ts +0 -3
- package/dist/handlers/github-webhook/pull-request.d.ts +0 -3
- package/dist/handlers/github-webhook/resource.d.ts +0 -10
- package/dist/handlers/github-webhook.d.ts +0 -15
- package/dist/legacy-import-TMJISRKO.js +0 -18
|
@@ -1,41 +1,73 @@
|
|
|
1
1
|
import {
|
|
2
2
|
JUNIOR_THREAD_STATE_TTL_MS,
|
|
3
|
-
buildConversationStatePatch
|
|
4
|
-
} from "./chunk-XIMUQ4IU.js";
|
|
5
|
-
import {
|
|
6
3
|
abandonAgentTurnSessionRecord,
|
|
7
|
-
|
|
4
|
+
buildConversationStatePatch,
|
|
8
5
|
getAgentTurnSessionRecord,
|
|
6
|
+
getAgentTurnSessionRecordForResume,
|
|
9
7
|
upsertAgentTurnSessionRecord
|
|
10
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-4YF7Z6IA.js";
|
|
9
|
+
import {
|
|
10
|
+
addAgentTurnUsage
|
|
11
|
+
} from "./chunk-MU6HHZEN.js";
|
|
12
|
+
import {
|
|
13
|
+
getStateAdapter
|
|
14
|
+
} from "./chunk-B2Z2H66D.js";
|
|
11
15
|
import {
|
|
12
|
-
|
|
13
|
-
} from "./chunk-
|
|
16
|
+
getConversationEventStore
|
|
17
|
+
} from "./chunk-NVOTGWYX.js";
|
|
14
18
|
import {
|
|
15
19
|
sleep
|
|
16
20
|
} from "./chunk-4ZNGQH7C.js";
|
|
17
21
|
import {
|
|
18
22
|
botConfig,
|
|
23
|
+
escapeXml,
|
|
19
24
|
getPiMessageRole,
|
|
20
|
-
|
|
25
|
+
resolveGatewayModel,
|
|
26
|
+
standardModelId,
|
|
21
27
|
trimTrailingAssistantMessages
|
|
22
|
-
} from "./chunk-
|
|
28
|
+
} from "./chunk-SS67LUOK.js";
|
|
23
29
|
import {
|
|
24
30
|
getActiveTraceId,
|
|
25
|
-
logException
|
|
26
|
-
|
|
31
|
+
logException,
|
|
32
|
+
logWarn,
|
|
33
|
+
setSpanAttributes
|
|
34
|
+
} from "./chunk-O24R4DCK.js";
|
|
27
35
|
import {
|
|
28
36
|
isRecord,
|
|
29
37
|
toOptionalString
|
|
30
38
|
} from "./chunk-OB42YVAE.js";
|
|
39
|
+
import {
|
|
40
|
+
parseSlackMessageTs
|
|
41
|
+
} from "./chunk-7FBGKXPE.js";
|
|
31
42
|
|
|
32
43
|
// src/chat/state/turn-id.ts
|
|
33
44
|
function buildDeterministicTurnId(messageId) {
|
|
34
45
|
const sanitized = messageId.replace(/[^a-zA-Z0-9_-]/g, "_");
|
|
35
46
|
return `turn_${sanitized}`;
|
|
36
47
|
}
|
|
48
|
+
function buildDeterministicAssistantMessageId(turnId) {
|
|
49
|
+
return `${turnId}:assistant:1`;
|
|
50
|
+
}
|
|
37
51
|
|
|
38
52
|
// src/chat/runtime/turn.ts
|
|
53
|
+
var ConversationTurnBoundaryError = class extends Error {
|
|
54
|
+
eventId;
|
|
55
|
+
failureCode;
|
|
56
|
+
constructor(args) {
|
|
57
|
+
super(
|
|
58
|
+
args.cause instanceof Error ? args.cause.message : String(args.cause),
|
|
59
|
+
{
|
|
60
|
+
cause: args.cause
|
|
61
|
+
}
|
|
62
|
+
);
|
|
63
|
+
this.name = "ConversationTurnBoundaryError";
|
|
64
|
+
this.eventId = args.eventId;
|
|
65
|
+
this.failureCode = args.failureCode;
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
function getConversationTurnBoundaryError(error) {
|
|
69
|
+
return error instanceof ConversationTurnBoundaryError ? error : void 0;
|
|
70
|
+
}
|
|
39
71
|
var CooperativeTurnYieldError = class extends Error {
|
|
40
72
|
code = "cooperative_turn_yield";
|
|
41
73
|
constructor(message = "Agent turn yielded at a safe boundary") {
|
|
@@ -166,13 +198,14 @@ function resumableBoundary(messages, fallbackMessages) {
|
|
|
166
198
|
return trimTrailingAssistantMessages(fallbackMessages ?? []);
|
|
167
199
|
}
|
|
168
200
|
async function loadTurnSessionRecord(ctx) {
|
|
169
|
-
const
|
|
170
|
-
|
|
201
|
+
const existingSessionRecord = await getAgentTurnSessionRecordForResume(
|
|
202
|
+
ctx.conversationId,
|
|
203
|
+
ctx.sessionId
|
|
204
|
+
);
|
|
171
205
|
const hasAwaitingResumeRecord = Boolean(
|
|
172
206
|
existingSessionRecord && existingSessionRecord.state === "awaiting_resume"
|
|
173
207
|
);
|
|
174
208
|
return {
|
|
175
|
-
canUseTurnSession,
|
|
176
209
|
resumedFromSessionRecord: hasAwaitingResumeRecord,
|
|
177
210
|
currentSliceId: hasAwaitingResumeRecord ? existingSessionRecord.sliceId : 1,
|
|
178
211
|
existingSessionRecord
|
|
@@ -351,7 +384,7 @@ async function persistAuthPauseSessionRecord(args) {
|
|
|
351
384
|
}
|
|
352
385
|
return void 0;
|
|
353
386
|
}
|
|
354
|
-
async function
|
|
387
|
+
async function persistContinuationSessionRecord(args) {
|
|
355
388
|
const nextSliceId = args.currentSliceId + 1;
|
|
356
389
|
try {
|
|
357
390
|
const latestSessionRecord = await getAgentTurnSessionRecord(
|
|
@@ -395,7 +428,7 @@ async function persistTimeoutSessionRecord(args) {
|
|
|
395
428
|
...args.reasoningLevel ?? latestSessionRecord?.reasoningLevel ? {
|
|
396
429
|
reasoningLevel: args.reasoningLevel ?? latestSessionRecord?.reasoningLevel
|
|
397
430
|
} : {},
|
|
398
|
-
resumeReason:
|
|
431
|
+
resumeReason: args.resumeReason,
|
|
399
432
|
resumedFromSliceId: latestSessionRecord?.resumedFromSliceId,
|
|
400
433
|
errorMessage: new TurnSliceLimitExceededError(
|
|
401
434
|
botConfig.maxSlicesPerTurn
|
|
@@ -421,7 +454,7 @@ async function persistTimeoutSessionRecord(args) {
|
|
|
421
454
|
...args.reasoningLevel ?? latestSessionRecord?.reasoningLevel ? {
|
|
422
455
|
reasoningLevel: args.reasoningLevel ?? latestSessionRecord?.reasoningLevel
|
|
423
456
|
} : {},
|
|
424
|
-
resumeReason:
|
|
457
|
+
resumeReason: args.resumeReason,
|
|
425
458
|
resumedFromSliceId: args.currentSliceId,
|
|
426
459
|
errorMessage: args.errorMessage,
|
|
427
460
|
...args.actor ?? latestSessionRecord?.actor ? { actor: args.actor ?? latestSessionRecord?.actor } : {},
|
|
@@ -497,6 +530,605 @@ async function persistYieldSessionRecord(args) {
|
|
|
497
530
|
}
|
|
498
531
|
}
|
|
499
532
|
|
|
533
|
+
// src/chat/services/context-budget.ts
|
|
534
|
+
var COMPACTION_TRIGGER_INPUT_RATIO = 0.75;
|
|
535
|
+
var COMPACTION_OUTPUT_RESERVE_RATIO = 0.25;
|
|
536
|
+
var COMPACTION_TARGET_RATIO = 0.8;
|
|
537
|
+
var FALLBACK_CONTEXT_WINDOW_TOKENS = 4e5;
|
|
538
|
+
var FALLBACK_MAX_OUTPUT_TOKENS = 128e3;
|
|
539
|
+
function positiveInteger(value, fallback) {
|
|
540
|
+
return Number.isFinite(value) && value > 0 ? Math.floor(value) : fallback;
|
|
541
|
+
}
|
|
542
|
+
function estimateTextTokens(text) {
|
|
543
|
+
return Math.ceil(text.length / 4);
|
|
544
|
+
}
|
|
545
|
+
function calculateContextCompactionTriggerTokens(model) {
|
|
546
|
+
const contextWindow = positiveInteger(
|
|
547
|
+
model.contextWindow,
|
|
548
|
+
FALLBACK_CONTEXT_WINDOW_TOKENS
|
|
549
|
+
);
|
|
550
|
+
const maxTokens = positiveInteger(
|
|
551
|
+
model.maxTokens,
|
|
552
|
+
FALLBACK_MAX_OUTPUT_TOKENS
|
|
553
|
+
);
|
|
554
|
+
const outputReserve = Math.min(
|
|
555
|
+
maxTokens,
|
|
556
|
+
Math.floor(contextWindow * COMPACTION_OUTPUT_RESERVE_RATIO)
|
|
557
|
+
);
|
|
558
|
+
const usableInputTokens = Math.max(1, contextWindow - outputReserve);
|
|
559
|
+
return Math.max(
|
|
560
|
+
1,
|
|
561
|
+
Math.floor(usableInputTokens * COMPACTION_TRIGGER_INPUT_RATIO)
|
|
562
|
+
);
|
|
563
|
+
}
|
|
564
|
+
function calculateContextCompactionTargetTokens(triggerTokens) {
|
|
565
|
+
return Math.max(1, Math.floor(triggerTokens * COMPACTION_TARGET_RATIO));
|
|
566
|
+
}
|
|
567
|
+
function getAgentContextCompactionTriggerTokens() {
|
|
568
|
+
const model = resolveGatewayModel(standardModelId(botConfig));
|
|
569
|
+
return calculateContextCompactionTriggerTokens({
|
|
570
|
+
contextWindow: botConfig.modelContextWindowTokens ?? model.contextWindow,
|
|
571
|
+
maxTokens: model.maxTokens
|
|
572
|
+
});
|
|
573
|
+
}
|
|
574
|
+
function getConversationContextCompactionTriggerTokens() {
|
|
575
|
+
const model = resolveGatewayModel(botConfig.fastModelId);
|
|
576
|
+
return calculateContextCompactionTriggerTokens({
|
|
577
|
+
contextWindow: model.contextWindow,
|
|
578
|
+
maxTokens: model.maxTokens
|
|
579
|
+
});
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
// src/chat/services/conversation-memory.ts
|
|
583
|
+
var CONTEXT_MIN_LIVE_MESSAGES = 12;
|
|
584
|
+
var CONTEXT_COMPACTION_BATCH_SIZE = 24;
|
|
585
|
+
var CONTEXT_MAX_COMPACTIONS = 16;
|
|
586
|
+
var CONTEXT_MAX_MESSAGE_CHARS = 3200;
|
|
587
|
+
var SLACK_USER_ID_DISPLAY_PATTERN = /^[UW][A-Z0-9]{5,}$/;
|
|
588
|
+
function generateConversationId(prefix) {
|
|
589
|
+
return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 10)}`;
|
|
590
|
+
}
|
|
591
|
+
function normalizeConversationText(text) {
|
|
592
|
+
return text.trim().replace(/\s+/g, " ").slice(0, CONTEXT_MAX_MESSAGE_CHARS);
|
|
593
|
+
}
|
|
594
|
+
function buildImageContextSuffix(message, conversation) {
|
|
595
|
+
const byFileId = conversation?.vision.byFileId;
|
|
596
|
+
const imageFileIds = message.meta?.imageFileIds ?? [];
|
|
597
|
+
if (!byFileId || imageFileIds.length === 0) {
|
|
598
|
+
return "";
|
|
599
|
+
}
|
|
600
|
+
const summaries = imageFileIds.map((fileId) => byFileId[fileId]?.summary?.trim()).filter((summary) => Boolean(summary));
|
|
601
|
+
if (summaries.length === 0) {
|
|
602
|
+
return "";
|
|
603
|
+
}
|
|
604
|
+
return ` [image context: ${summaries.join(" | ")}]`;
|
|
605
|
+
}
|
|
606
|
+
function renderConversationMessageLine(message, conversation) {
|
|
607
|
+
const displayName = conversationAuthorDisplayName(message);
|
|
608
|
+
const markers = [];
|
|
609
|
+
if (message.meta?.replied === false) {
|
|
610
|
+
markers.push(
|
|
611
|
+
`assistant skipped: ${message.meta?.skippedReason ?? "no-reply route"}`
|
|
612
|
+
);
|
|
613
|
+
}
|
|
614
|
+
if (message.meta?.explicitMention) {
|
|
615
|
+
markers.push("explicit mention");
|
|
616
|
+
}
|
|
617
|
+
const markerSuffix = markers.length > 0 ? ` (${markers.join("; ")})` : "";
|
|
618
|
+
const imageContext = buildImageContextSuffix(message, conversation);
|
|
619
|
+
return `[${message.role}] ${displayName}: ${message.text}${imageContext}${markerSuffix}`;
|
|
620
|
+
}
|
|
621
|
+
function conversationAuthorDisplayName(message) {
|
|
622
|
+
const author = message.author;
|
|
623
|
+
const fullName = authorDisplayField(author?.fullName, author?.userId);
|
|
624
|
+
const userName = authorDisplayField(author?.userName, author?.userId);
|
|
625
|
+
return fullName ?? userName ?? (message.role === "assistant" ? botConfig.userName : message.role);
|
|
626
|
+
}
|
|
627
|
+
function authorDisplayField(value, userId) {
|
|
628
|
+
if (!value || value === userId || SLACK_USER_ID_DISPLAY_PATTERN.test(value)) {
|
|
629
|
+
return void 0;
|
|
630
|
+
}
|
|
631
|
+
return value;
|
|
632
|
+
}
|
|
633
|
+
function updateConversationStats(conversation) {
|
|
634
|
+
const contextText = buildConversationContext(conversation);
|
|
635
|
+
conversation.stats.estimatedContextTokens = estimateTextTokens(
|
|
636
|
+
contextText ?? ""
|
|
637
|
+
);
|
|
638
|
+
conversation.stats.totalMessageCount = conversation.messages.length;
|
|
639
|
+
conversation.stats.updatedAtMs = Date.now();
|
|
640
|
+
}
|
|
641
|
+
function upsertConversationMessage(conversation, message) {
|
|
642
|
+
const existingIndex = conversation.messages.findIndex(
|
|
643
|
+
(entry) => entry.id === message.id
|
|
644
|
+
);
|
|
645
|
+
if (existingIndex >= 0) {
|
|
646
|
+
conversation.messages[existingIndex] = {
|
|
647
|
+
...conversation.messages[existingIndex],
|
|
648
|
+
...message,
|
|
649
|
+
meta: {
|
|
650
|
+
...conversation.messages[existingIndex]?.meta,
|
|
651
|
+
...message.meta
|
|
652
|
+
}
|
|
653
|
+
};
|
|
654
|
+
updateConversationStats(conversation);
|
|
655
|
+
return message.id;
|
|
656
|
+
}
|
|
657
|
+
conversation.messages.push(message);
|
|
658
|
+
updateConversationStats(conversation);
|
|
659
|
+
return message.id;
|
|
660
|
+
}
|
|
661
|
+
function recordDeliveredAssistantMessage(args) {
|
|
662
|
+
const prefix = `${args.sessionId}:assistant:`;
|
|
663
|
+
const ordinal = args.conversation.messages.filter(
|
|
664
|
+
(message) => message.id.startsWith(prefix)
|
|
665
|
+
).length + 1;
|
|
666
|
+
const messageId = `${prefix}${ordinal}`;
|
|
667
|
+
markConversationMessage(args.conversation, args.userMessageId, {
|
|
668
|
+
replied: true,
|
|
669
|
+
skippedReason: void 0
|
|
670
|
+
});
|
|
671
|
+
upsertConversationMessage(args.conversation, {
|
|
672
|
+
id: messageId,
|
|
673
|
+
role: "assistant",
|
|
674
|
+
text: normalizeConversationText(args.text),
|
|
675
|
+
createdAtMs: Date.now(),
|
|
676
|
+
author: {
|
|
677
|
+
userName: botConfig.userName,
|
|
678
|
+
isBot: true
|
|
679
|
+
},
|
|
680
|
+
meta: {
|
|
681
|
+
replied: true
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
return messageId;
|
|
685
|
+
}
|
|
686
|
+
function turnHasReply(conversation, turnId) {
|
|
687
|
+
const assistantPrefix = `${turnId}:assistant:`;
|
|
688
|
+
return conversation.messages.some(
|
|
689
|
+
(message) => message.role === "assistant" && message.id.startsWith(assistantPrefix)
|
|
690
|
+
);
|
|
691
|
+
}
|
|
692
|
+
function markConversationMessage(conversation, messageId, patch) {
|
|
693
|
+
if (!messageId) return;
|
|
694
|
+
const messageIndex = conversation.messages.findIndex(
|
|
695
|
+
(entry) => entry.id === messageId
|
|
696
|
+
);
|
|
697
|
+
if (messageIndex < 0) return;
|
|
698
|
+
const current = conversation.messages[messageIndex];
|
|
699
|
+
conversation.messages[messageIndex] = {
|
|
700
|
+
...current,
|
|
701
|
+
meta: {
|
|
702
|
+
...current.meta ?? {},
|
|
703
|
+
...patch
|
|
704
|
+
}
|
|
705
|
+
};
|
|
706
|
+
updateConversationStats(conversation);
|
|
707
|
+
}
|
|
708
|
+
function buildConversationContext(conversation, options = {}) {
|
|
709
|
+
const messages = conversation.messages.filter(
|
|
710
|
+
(entry) => entry.id !== options.excludeMessageId
|
|
711
|
+
);
|
|
712
|
+
if (messages.length === 0 && conversation.compactions.length === 0) {
|
|
713
|
+
return void 0;
|
|
714
|
+
}
|
|
715
|
+
const lines = [];
|
|
716
|
+
if (conversation.compactions.length > 0) {
|
|
717
|
+
lines.push("<thread-compactions>");
|
|
718
|
+
for (const [index, compaction] of conversation.compactions.entries()) {
|
|
719
|
+
lines.push(
|
|
720
|
+
` <compaction index="${index + 1}" covered_messages="${compaction.coveredMessageCount}" created_at="${new Date(compaction.createdAtMs).toISOString()}">`,
|
|
721
|
+
compaction.summary,
|
|
722
|
+
" </compaction>"
|
|
723
|
+
);
|
|
724
|
+
}
|
|
725
|
+
lines.push("</thread-compactions>");
|
|
726
|
+
}
|
|
727
|
+
if (messages.length > 0) {
|
|
728
|
+
if (lines.length > 0) {
|
|
729
|
+
lines.push("");
|
|
730
|
+
}
|
|
731
|
+
lines.push("<thread-transcript>");
|
|
732
|
+
for (const [index, message] of messages.entries()) {
|
|
733
|
+
const author = escapeXml(conversationAuthorDisplayName(message));
|
|
734
|
+
const actorIdAttr = message.author?.userId ? ` actor_id="${escapeXml(message.author.userId)}"` : "";
|
|
735
|
+
const ts = new Date(message.createdAtMs).toISOString();
|
|
736
|
+
const slackTsAttr = message.meta?.slackTs ? ` slack_ts="${escapeXml(message.meta.slackTs)}"` : "";
|
|
737
|
+
lines.push(
|
|
738
|
+
` <message index="${index + 1}" ts="${ts}" role="${message.role}" author="${author}"${actorIdAttr}${slackTsAttr}>`,
|
|
739
|
+
renderConversationMessageLine(message, conversation),
|
|
740
|
+
" </message>"
|
|
741
|
+
);
|
|
742
|
+
}
|
|
743
|
+
lines.push("</thread-transcript>");
|
|
744
|
+
}
|
|
745
|
+
return lines.join("\n");
|
|
746
|
+
}
|
|
747
|
+
function pruneCompactions(compactions) {
|
|
748
|
+
if (compactions.length <= CONTEXT_MAX_COMPACTIONS) {
|
|
749
|
+
return compactions;
|
|
750
|
+
}
|
|
751
|
+
const overflowCount = compactions.length - CONTEXT_MAX_COMPACTIONS + 1;
|
|
752
|
+
const merged = compactions.slice(0, overflowCount);
|
|
753
|
+
const mergedSummary = merged.map((entry) => entry.summary).join("\n").slice(0, 3500);
|
|
754
|
+
const coveredMessageCount = merged.reduce(
|
|
755
|
+
(count, entry) => count + entry.coveredMessageCount,
|
|
756
|
+
0
|
|
757
|
+
);
|
|
758
|
+
const compacted = {
|
|
759
|
+
id: generateConversationId("compaction"),
|
|
760
|
+
createdAtMs: Date.now(),
|
|
761
|
+
summary: mergedSummary,
|
|
762
|
+
coveredMessageCount
|
|
763
|
+
};
|
|
764
|
+
return [compacted, ...compactions.slice(overflowCount)];
|
|
765
|
+
}
|
|
766
|
+
async function summarizeConversationChunk(messages, conversation, context, deps) {
|
|
767
|
+
const transcript = messages.map((message) => renderConversationMessageLine(message, conversation)).join("\n");
|
|
768
|
+
try {
|
|
769
|
+
const result = await deps.completeText({
|
|
770
|
+
modelId: botConfig.fastModelId,
|
|
771
|
+
temperature: 0,
|
|
772
|
+
messages: [
|
|
773
|
+
{
|
|
774
|
+
role: "user",
|
|
775
|
+
content: [
|
|
776
|
+
"Summarize the following older Slack thread transcript segment for future assistant turns.",
|
|
777
|
+
"Keep the summary factual and concise. Do not invent details.",
|
|
778
|
+
"",
|
|
779
|
+
"Output exactly three XML sections in this order:",
|
|
780
|
+
"<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>",
|
|
781
|
+
"<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>",
|
|
782
|
+
"<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>",
|
|
783
|
+
"",
|
|
784
|
+
"Do not output any text outside the three sections.",
|
|
785
|
+
"",
|
|
786
|
+
transcript
|
|
787
|
+
].join("\n"),
|
|
788
|
+
timestamp: Date.now()
|
|
789
|
+
}
|
|
790
|
+
],
|
|
791
|
+
metadata: {
|
|
792
|
+
modelId: botConfig.fastModelId,
|
|
793
|
+
threadId: context.threadId ?? "",
|
|
794
|
+
channelId: context.channelId ?? "",
|
|
795
|
+
actorId: context.actorId ?? "",
|
|
796
|
+
runId: context.runId ?? ""
|
|
797
|
+
}
|
|
798
|
+
});
|
|
799
|
+
const summary = result.text.trim();
|
|
800
|
+
if (summary.length > 0) {
|
|
801
|
+
return summary.slice(0, 3500);
|
|
802
|
+
}
|
|
803
|
+
} catch (error) {
|
|
804
|
+
logWarn(
|
|
805
|
+
"conversation_compaction_summary_failed",
|
|
806
|
+
{
|
|
807
|
+
slackThreadId: context.threadId,
|
|
808
|
+
slackUserId: context.actorId,
|
|
809
|
+
slackChannelId: context.channelId,
|
|
810
|
+
runId: context.runId,
|
|
811
|
+
assistantUserName: botConfig.userName,
|
|
812
|
+
modelId: botConfig.fastModelId
|
|
813
|
+
},
|
|
814
|
+
{
|
|
815
|
+
"exception.message": error instanceof Error ? error.message : String(error),
|
|
816
|
+
"app.compaction_messages_covered": messages.length
|
|
817
|
+
},
|
|
818
|
+
"Compaction summarization failed; using fallback summary"
|
|
819
|
+
);
|
|
820
|
+
}
|
|
821
|
+
return transcript.slice(0, 2800);
|
|
822
|
+
}
|
|
823
|
+
async function generateThreadTitleWithDeps(sourceText, deps) {
|
|
824
|
+
const result = await deps.completeText({
|
|
825
|
+
modelId: botConfig.fastModelId,
|
|
826
|
+
temperature: 0,
|
|
827
|
+
messages: [
|
|
828
|
+
{
|
|
829
|
+
role: "user",
|
|
830
|
+
content: [
|
|
831
|
+
"Generate a concise 5-8 word Slack conversation title from the first user message below.",
|
|
832
|
+
"Capture the user's main request.",
|
|
833
|
+
"Reply with ONLY the title, with no quotes or trailing punctuation.",
|
|
834
|
+
"",
|
|
835
|
+
`First user message: ${sourceText.slice(0, 500)}`
|
|
836
|
+
].join("\n"),
|
|
837
|
+
timestamp: Date.now()
|
|
838
|
+
}
|
|
839
|
+
],
|
|
840
|
+
metadata: {
|
|
841
|
+
modelId: botConfig.fastModelId
|
|
842
|
+
}
|
|
843
|
+
});
|
|
844
|
+
return result.text.trim().slice(0, 60);
|
|
845
|
+
}
|
|
846
|
+
function getThreadTitleSourceMessage(conversation) {
|
|
847
|
+
let firstMessage;
|
|
848
|
+
for (const message of conversation.messages) {
|
|
849
|
+
if (!isHumanConversationMessage(message)) {
|
|
850
|
+
continue;
|
|
851
|
+
}
|
|
852
|
+
if (!firstMessage) {
|
|
853
|
+
firstMessage = message;
|
|
854
|
+
continue;
|
|
855
|
+
}
|
|
856
|
+
if (message.createdAtMs < firstMessage.createdAtMs) {
|
|
857
|
+
firstMessage = message;
|
|
858
|
+
continue;
|
|
859
|
+
}
|
|
860
|
+
if (message.createdAtMs === firstMessage.createdAtMs && message.id < firstMessage.id) {
|
|
861
|
+
firstMessage = message;
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
return firstMessage;
|
|
865
|
+
}
|
|
866
|
+
async function compactConversationIfNeededWithDeps(conversation, context, deps) {
|
|
867
|
+
updateConversationStats(conversation);
|
|
868
|
+
let estimatedTokens = conversation.stats.estimatedContextTokens;
|
|
869
|
+
setSpanAttributes({
|
|
870
|
+
"app.context_tokens_estimated": estimatedTokens
|
|
871
|
+
});
|
|
872
|
+
const triggerTokens = getConversationContextCompactionTriggerTokens();
|
|
873
|
+
const targetTokens = calculateContextCompactionTargetTokens(triggerTokens);
|
|
874
|
+
while (estimatedTokens > triggerTokens && conversation.messages.length > CONTEXT_MIN_LIVE_MESSAGES) {
|
|
875
|
+
const compactCount = Math.min(
|
|
876
|
+
CONTEXT_COMPACTION_BATCH_SIZE,
|
|
877
|
+
conversation.messages.length - CONTEXT_MIN_LIVE_MESSAGES
|
|
878
|
+
);
|
|
879
|
+
if (compactCount <= 0) {
|
|
880
|
+
break;
|
|
881
|
+
}
|
|
882
|
+
const compactedChunk = conversation.messages.slice(0, compactCount);
|
|
883
|
+
const summary = await summarizeConversationChunk(
|
|
884
|
+
compactedChunk,
|
|
885
|
+
conversation,
|
|
886
|
+
context,
|
|
887
|
+
deps
|
|
888
|
+
);
|
|
889
|
+
conversation.compactions.push({
|
|
890
|
+
id: generateConversationId("compaction"),
|
|
891
|
+
createdAtMs: Date.now(),
|
|
892
|
+
summary,
|
|
893
|
+
coveredMessageCount: compactedChunk.length
|
|
894
|
+
});
|
|
895
|
+
conversation.compactions = pruneCompactions(conversation.compactions);
|
|
896
|
+
conversation.messages = conversation.messages.slice(compactCount);
|
|
897
|
+
conversation.stats.compactedMessageCount += compactCount;
|
|
898
|
+
updateConversationStats(conversation);
|
|
899
|
+
estimatedTokens = conversation.stats.estimatedContextTokens;
|
|
900
|
+
setSpanAttributes({
|
|
901
|
+
"app.compaction_messages_covered": compactCount,
|
|
902
|
+
"app.compaction.trigger_tokens": triggerTokens,
|
|
903
|
+
"app.compaction.target_tokens": targetTokens,
|
|
904
|
+
"app.context_tokens_estimated": estimatedTokens
|
|
905
|
+
});
|
|
906
|
+
if (estimatedTokens <= targetTokens) {
|
|
907
|
+
break;
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
function createConversationMemoryService(deps) {
|
|
912
|
+
return {
|
|
913
|
+
compactConversationIfNeeded: async (conversation, context) => await compactConversationIfNeededWithDeps(conversation, context, deps),
|
|
914
|
+
generateThreadTitle: async (sourceText) => await generateThreadTitleWithDeps(sourceText, deps)
|
|
915
|
+
};
|
|
916
|
+
}
|
|
917
|
+
function isHumanConversationMessage(message) {
|
|
918
|
+
return message.role === "user" && message.author?.isBot !== true;
|
|
919
|
+
}
|
|
920
|
+
function getConversationMessageSlackTs(message) {
|
|
921
|
+
return parseSlackMessageTs(message.meta?.slackTs);
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
// src/chat/conversations/messages.ts
|
|
925
|
+
import { createHash } from "crypto";
|
|
926
|
+
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
927
|
+
|
|
928
|
+
// src/chat/conversations/message-summaries.ts
|
|
929
|
+
import { isDeepStrictEqual } from "util";
|
|
930
|
+
function projectConversationMessageSummaries(events) {
|
|
931
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
932
|
+
const data = events[index]?.data;
|
|
933
|
+
if (data?.type === "messages_summarized") {
|
|
934
|
+
return data.compactions;
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
return [];
|
|
938
|
+
}
|
|
939
|
+
async function persistConversationMessageSummaries(args) {
|
|
940
|
+
const eventStore = getConversationEventStore();
|
|
941
|
+
const history = await eventStore.loadMessageHistory(args.conversationId);
|
|
942
|
+
const existing = history.compaction ? projectConversationMessageSummaries([history.compaction]) : [];
|
|
943
|
+
if (isDeepStrictEqual(existing, args.conversation.compactions)) {
|
|
944
|
+
return;
|
|
945
|
+
}
|
|
946
|
+
const liveMessageIds = new Set(
|
|
947
|
+
args.conversation.messages.map((message) => message.id)
|
|
948
|
+
);
|
|
949
|
+
const historyFromSeq = history.events.find(
|
|
950
|
+
(event) => event.data.type === "message" && liveMessageIds.has(event.data.messageId)
|
|
951
|
+
)?.seq;
|
|
952
|
+
const nextHistoryFromSeq = historyFromSeq ?? Math.max(
|
|
953
|
+
history.compaction?.data.type === "messages_summarized" ? history.compaction.data.historyFromSeq : 0,
|
|
954
|
+
(history.events.at(-1)?.seq ?? -1) + 1
|
|
955
|
+
);
|
|
956
|
+
await eventStore.append(args.conversationId, [
|
|
957
|
+
{
|
|
958
|
+
data: {
|
|
959
|
+
type: "messages_summarized",
|
|
960
|
+
historyFromSeq: nextHistoryFromSeq,
|
|
961
|
+
compactions: args.conversation.compactions
|
|
962
|
+
},
|
|
963
|
+
createdAtMs: args.conversation.compactions.at(-1)?.createdAtMs ?? Date.now()
|
|
964
|
+
}
|
|
965
|
+
]);
|
|
966
|
+
}
|
|
967
|
+
|
|
968
|
+
// src/chat/conversations/message-projection.ts
|
|
969
|
+
function isMessageEvent(event) {
|
|
970
|
+
return event.data.type === "message" || event.data.type === "message_updated" || event.data.type === "message_handled";
|
|
971
|
+
}
|
|
972
|
+
function splitMeta(meta) {
|
|
973
|
+
const rest = { ...meta ?? {} };
|
|
974
|
+
const author = rest.author;
|
|
975
|
+
delete rest.author;
|
|
976
|
+
return {
|
|
977
|
+
...author ? { author } : {},
|
|
978
|
+
...Object.keys(rest).length > 0 ? { meta: rest } : {}
|
|
979
|
+
};
|
|
980
|
+
}
|
|
981
|
+
function missingBaselineError(event) {
|
|
982
|
+
return new Error(
|
|
983
|
+
`Message event ${event.data.type} at seq ${event.seq} references ${event.data.messageId} before message`
|
|
984
|
+
);
|
|
985
|
+
}
|
|
986
|
+
function projectConversationMessages(events, options = {}) {
|
|
987
|
+
const byId = /* @__PURE__ */ new Map();
|
|
988
|
+
for (const event of events) {
|
|
989
|
+
if (!isMessageEvent(event)) continue;
|
|
990
|
+
const data = event.data;
|
|
991
|
+
const current = byId.get(data.messageId);
|
|
992
|
+
if (data.type === "message" || data.type === "message_updated") {
|
|
993
|
+
const message = {
|
|
994
|
+
id: data.messageId,
|
|
995
|
+
role: data.role,
|
|
996
|
+
text: data.text,
|
|
997
|
+
createdAtMs: event.createdAtMs,
|
|
998
|
+
...splitMeta(data.meta)
|
|
999
|
+
};
|
|
1000
|
+
if (data.type === "message" && current)
|
|
1001
|
+
throw new Error(
|
|
1002
|
+
`Duplicate message event for ${data.messageId} at seq ${event.seq}`
|
|
1003
|
+
);
|
|
1004
|
+
if (data.type === "message_updated" && !current) {
|
|
1005
|
+
if ((options.historyFromSeq ?? 0) > 0) continue;
|
|
1006
|
+
throw missingBaselineError(event);
|
|
1007
|
+
}
|
|
1008
|
+
byId.set(data.messageId, {
|
|
1009
|
+
message: {
|
|
1010
|
+
...message,
|
|
1011
|
+
createdAtMs: current?.message.createdAtMs ?? message.createdAtMs
|
|
1012
|
+
},
|
|
1013
|
+
...current?.repliedAtMs === void 0 ? {} : { repliedAtMs: current.repliedAtMs }
|
|
1014
|
+
});
|
|
1015
|
+
continue;
|
|
1016
|
+
}
|
|
1017
|
+
if (!current) {
|
|
1018
|
+
if ((options.historyFromSeq ?? 0) > 0) continue;
|
|
1019
|
+
throw missingBaselineError(event);
|
|
1020
|
+
}
|
|
1021
|
+
current.repliedAtMs ??= event.createdAtMs;
|
|
1022
|
+
}
|
|
1023
|
+
return [...byId.values()].map(({ message, repliedAtMs }) => {
|
|
1024
|
+
if (repliedAtMs === void 0) return message;
|
|
1025
|
+
return {
|
|
1026
|
+
...message,
|
|
1027
|
+
meta: { ...message.meta ?? {}, replied: true }
|
|
1028
|
+
};
|
|
1029
|
+
});
|
|
1030
|
+
}
|
|
1031
|
+
|
|
1032
|
+
// src/chat/conversations/messages.ts
|
|
1033
|
+
function toStoredConversationMessage(message) {
|
|
1034
|
+
const meta = {};
|
|
1035
|
+
if (message.author) meta.author = message.author;
|
|
1036
|
+
const { replied, ...restMeta } = message.meta ?? {};
|
|
1037
|
+
Object.assign(meta, restMeta);
|
|
1038
|
+
if (replied === false) meta.replied = false;
|
|
1039
|
+
return {
|
|
1040
|
+
messageId: message.id,
|
|
1041
|
+
role: message.role,
|
|
1042
|
+
text: message.text,
|
|
1043
|
+
...Object.keys(meta).length > 0 ? { meta } : {},
|
|
1044
|
+
createdAtMs: message.createdAtMs
|
|
1045
|
+
};
|
|
1046
|
+
}
|
|
1047
|
+
function canonicalJson(value) {
|
|
1048
|
+
if (Array.isArray(value)) return value.map(canonicalJson);
|
|
1049
|
+
if (!value || typeof value !== "object") return value;
|
|
1050
|
+
return Object.fromEntries(
|
|
1051
|
+
Object.entries(value).filter(([, item]) => item !== void 0).sort(([left], [right]) => left.localeCompare(right)).map(([key, item]) => [key, canonicalJson(item)])
|
|
1052
|
+
);
|
|
1053
|
+
}
|
|
1054
|
+
function messageStateKey(message) {
|
|
1055
|
+
return createHash("sha256").update(JSON.stringify(canonicalJson(message))).digest("hex").slice(0, 32);
|
|
1056
|
+
}
|
|
1057
|
+
async function hydrateConversationMessages(args) {
|
|
1058
|
+
if (!args.conversationId) {
|
|
1059
|
+
args.conversation.messages = [];
|
|
1060
|
+
return;
|
|
1061
|
+
}
|
|
1062
|
+
const history = await getConversationEventStore().loadMessageHistory(
|
|
1063
|
+
args.conversationId
|
|
1064
|
+
);
|
|
1065
|
+
args.conversation.compactions = history.compaction ? projectConversationMessageSummaries([history.compaction]) : [];
|
|
1066
|
+
const historyFromSeq = history.compaction?.data.type === "messages_summarized" ? history.compaction.data.historyFromSeq : 0;
|
|
1067
|
+
args.conversation.messages = projectConversationMessages(history.events, {
|
|
1068
|
+
historyFromSeq
|
|
1069
|
+
});
|
|
1070
|
+
updateConversationStats(args.conversation);
|
|
1071
|
+
}
|
|
1072
|
+
async function persistConversationMessages(args) {
|
|
1073
|
+
if (!args.conversationId || args.conversation.messages.length === 0) return;
|
|
1074
|
+
const history = await getConversationEventStore().loadMessageHistory(
|
|
1075
|
+
args.conversationId
|
|
1076
|
+
);
|
|
1077
|
+
const existingMessages = new Map(
|
|
1078
|
+
projectConversationMessages(history.events).map((message) => [
|
|
1079
|
+
message.id,
|
|
1080
|
+
message
|
|
1081
|
+
])
|
|
1082
|
+
);
|
|
1083
|
+
const handledAtMs = args.repliedAtMs ?? Date.now();
|
|
1084
|
+
const events = args.conversation.messages.flatMap((message) => {
|
|
1085
|
+
const stored = toStoredConversationMessage(message);
|
|
1086
|
+
const existing = existingMessages.get(stored.messageId);
|
|
1087
|
+
const changed = existing !== void 0 && !isDeepStrictEqual2(toStoredConversationMessage(existing), stored);
|
|
1088
|
+
const messageEvent = existing ? changed ? [
|
|
1089
|
+
{
|
|
1090
|
+
idempotencyKey: `message:${stored.messageId}:update:${messageStateKey(stored)}`,
|
|
1091
|
+
createdAtMs: handledAtMs,
|
|
1092
|
+
data: {
|
|
1093
|
+
type: "message_updated",
|
|
1094
|
+
messageId: stored.messageId,
|
|
1095
|
+
role: stored.role,
|
|
1096
|
+
text: stored.text,
|
|
1097
|
+
...stored.authorIdentityId ? { authorIdentityId: stored.authorIdentityId } : {},
|
|
1098
|
+
...stored.meta ? { meta: stored.meta } : {}
|
|
1099
|
+
}
|
|
1100
|
+
}
|
|
1101
|
+
] : [] : [
|
|
1102
|
+
{
|
|
1103
|
+
idempotencyKey: `message:${stored.messageId}`,
|
|
1104
|
+
createdAtMs: stored.createdAtMs,
|
|
1105
|
+
data: {
|
|
1106
|
+
type: "message",
|
|
1107
|
+
messageId: stored.messageId,
|
|
1108
|
+
role: stored.role,
|
|
1109
|
+
text: stored.text,
|
|
1110
|
+
...stored.authorIdentityId ? { authorIdentityId: stored.authorIdentityId } : {},
|
|
1111
|
+
...stored.meta ? { meta: stored.meta } : {}
|
|
1112
|
+
}
|
|
1113
|
+
}
|
|
1114
|
+
];
|
|
1115
|
+
return [
|
|
1116
|
+
...messageEvent,
|
|
1117
|
+
...message.meta?.replied === true ? [
|
|
1118
|
+
{
|
|
1119
|
+
idempotencyKey: `message:${stored.messageId}:handled`,
|
|
1120
|
+
createdAtMs: handledAtMs,
|
|
1121
|
+
data: {
|
|
1122
|
+
type: "message_handled",
|
|
1123
|
+
messageId: stored.messageId
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
] : []
|
|
1127
|
+
];
|
|
1128
|
+
});
|
|
1129
|
+
await getConversationEventStore().append(args.conversationId, events);
|
|
1130
|
+
}
|
|
1131
|
+
|
|
500
1132
|
// src/chat/state/artifacts.ts
|
|
501
1133
|
function coerceThreadArtifactsState(value) {
|
|
502
1134
|
if (!value || typeof value !== "object") {
|
|
@@ -940,6 +1572,9 @@ function isPendingAuthLatestRequest(conversation, pendingAuth) {
|
|
|
940
1572
|
|
|
941
1573
|
export {
|
|
942
1574
|
buildDeterministicTurnId,
|
|
1575
|
+
buildDeterministicAssistantMessageId,
|
|
1576
|
+
ConversationTurnBoundaryError,
|
|
1577
|
+
getConversationTurnBoundaryError,
|
|
943
1578
|
CooperativeTurnYieldError,
|
|
944
1579
|
isCooperativeTurnYieldError,
|
|
945
1580
|
TurnInputCommitLostError,
|
|
@@ -958,7 +1593,7 @@ export {
|
|
|
958
1593
|
persistCompletedSessionRecord,
|
|
959
1594
|
completeDeliveredTurn,
|
|
960
1595
|
persistAuthPauseSessionRecord,
|
|
961
|
-
|
|
1596
|
+
persistContinuationSessionRecord,
|
|
962
1597
|
persistYieldSessionRecord,
|
|
963
1598
|
getInterruptionMarker,
|
|
964
1599
|
canReusePendingAuthLink,
|
|
@@ -966,6 +1601,22 @@ export {
|
|
|
966
1601
|
clearPendingAuth,
|
|
967
1602
|
abandonReplacedPendingAuth,
|
|
968
1603
|
isPendingAuthLatestRequest,
|
|
1604
|
+
persistConversationMessageSummaries,
|
|
1605
|
+
estimateTextTokens,
|
|
1606
|
+
getAgentContextCompactionTriggerTokens,
|
|
1607
|
+
normalizeConversationText,
|
|
1608
|
+
updateConversationStats,
|
|
1609
|
+
upsertConversationMessage,
|
|
1610
|
+
recordDeliveredAssistantMessage,
|
|
1611
|
+
turnHasReply,
|
|
1612
|
+
markConversationMessage,
|
|
1613
|
+
buildConversationContext,
|
|
1614
|
+
getThreadTitleSourceMessage,
|
|
1615
|
+
createConversationMemoryService,
|
|
1616
|
+
isHumanConversationMessage,
|
|
1617
|
+
getConversationMessageSlackTs,
|
|
1618
|
+
hydrateConversationMessages,
|
|
1619
|
+
persistConversationMessages,
|
|
969
1620
|
coerceThreadArtifactsState,
|
|
970
1621
|
mergeArtifactsState,
|
|
971
1622
|
getPersistedSandboxState,
|