@sentry/junior 0.107.1 → 0.109.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-ICPIJAFY.js → agent-hooks-FHHVLNPA.js} +12 -12
- package/dist/api/conversations/events.d.ts +1 -1
- package/dist/api/conversations/list.d.ts +1 -0
- package/dist/api/conversations/schema.d.ts +57 -6
- package/dist/api/schema.js +1 -1
- package/dist/api.js +116 -27
- package/dist/app.d.ts +1 -0
- package/dist/app.js +1741 -458
- package/dist/{catalog-runtime-FOFNPR3P.js → catalog-runtime-DMGJSLV2.js} +6 -4
- package/dist/chat/conversations/history.d.ts +16 -5
- package/dist/chat/conversations/message-projection.d.ts +3 -5
- package/dist/chat/conversations/sql/store.d.ts +0 -7
- package/dist/chat/log-context.d.ts +40 -0
- package/dist/chat/logging.d.ts +3 -26
- package/dist/chat/resource-events/store.d.ts +6 -0
- package/dist/chat/runtime/slack-resume.d.ts +2 -2
- package/dist/chat/runtime/slack-runtime.d.ts +3 -0
- package/dist/chat/services/turn-session-record.d.ts +0 -2
- package/dist/chat/slack/reply.d.ts +7 -19
- package/dist/chat/state/conversation.d.ts +1 -0
- package/dist/chat/task-execution/slack-work.d.ts +237 -10
- package/dist/chat/tool-support/turn-deadline-result.d.ts +3 -0
- package/dist/chunk-3SXUV423.js +10 -0
- package/dist/{chunk-YFQ7CQDE.js → chunk-72OMGPDS.js} +1017 -39
- package/dist/{chunk-2XXEKR44.js → chunk-7Q6WCISF.js} +1 -1
- package/dist/{chunk-NVOTGWYX.js → chunk-FENLIFJD.js} +97 -601
- package/dist/{chunk-IGHMVDWI.js → chunk-FWJRXN4F.js} +3 -3
- package/dist/{chunk-PUAMO4PI.js → chunk-HHVY4SMP.js} +41 -7
- package/dist/{chunk-SPUAJVVH.js → chunk-HO5NT6OE.js} +5 -7
- package/dist/{chunk-O24R4DCK.js → chunk-ISMWX44P.js} +93 -55
- package/dist/{chunk-7FBGKXPE.js → chunk-IVVGFRQQ.js} +5 -0
- package/dist/{chunk-EDLNHZH3.js → chunk-IXKSQGIO.js} +226 -191
- package/dist/{chunk-SS67LUOK.js → chunk-JNAVF45E.js} +6 -7
- package/dist/{chunk-DY7TRYB5.js → chunk-MGUMR35T.js} +1 -1
- package/dist/{chunk-CQ7KSO2B.js → chunk-MXW4GL53.js} +11 -15
- package/dist/{chunk-RT5BUHR5.js → chunk-O3JMSZLI.js} +1 -1
- package/dist/chunk-OCD3OZRK.js +125 -0
- package/dist/{chunk-B2Z2H66D.js → chunk-OUHNWH46.js} +60 -5
- package/dist/{chunk-VIXFG2VC.js → chunk-PLB7MRCQ.js} +1 -2
- package/dist/{chunk-RMVOAJRL.js → chunk-QY636HZU.js} +114 -12
- package/dist/{chunk-VH6KWKG2.js → chunk-RGX4V75M.js} +3 -3
- package/dist/{chunk-B7PUBQQM.js → chunk-SYAQIR5Z.js} +1 -1
- package/dist/chunk-TMBRFULF.js +471 -0
- package/dist/{chunk-YIPD3L6L.js → chunk-TT6GMXNA.js} +8 -10
- package/dist/{chunk-YT72TOEK.js → chunk-WHMOBKT7.js} +2 -2
- package/dist/cli/chat.js +23 -23
- package/dist/cli/check.js +8 -6
- package/dist/cli/plugins.js +16 -16
- package/dist/cli/snapshot-warmup.js +10 -9
- package/dist/cli/upgrade.js +314 -3505
- package/dist/db/db.d.ts +1 -1
- package/dist/{db-DIGO4TGW.js → db-V3NU72O2.js} +8 -6
- package/dist/instrumentation.js +3 -2
- package/dist/nitro.js +5 -4
- package/dist/{runner-ACR2HAIC.js → runner-JYBCQBLK.js} +17 -20
- package/dist/{skills-ITNHBARB.js → skills-U6MDAST7.js} +7 -5
- package/dist/{validation-AGQ46ZGV.js → validation-72N3JVRU.js} +7 -5
- package/dist/version.d.ts +2 -0
- package/dist/version.js +7 -0
- package/migrations/0006_drop_legacy_conversation_messages.sql +1 -0
- package/migrations/README.md +12 -9
- package/migrations/meta/0006_snapshot.json +948 -0
- package/migrations/meta/_journal.json +7 -0
- package/package.json +11 -7
- package/dist/chunk-4YF7Z6IA.js +0 -1061
- package/dist/chunk-AUUOHQAT.js +0 -115
- package/dist/chunk-G3E7SCME.js +0 -28
- package/dist/chunk-YNP2ATQX.js +0 -1282
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
buildConversationStatePatch,
|
|
5
|
-
getAgentTurnSessionRecord,
|
|
6
|
-
getAgentTurnSessionRecordForResume,
|
|
7
|
-
upsertAgentTurnSessionRecord
|
|
8
|
-
} from "./chunk-4YF7Z6IA.js";
|
|
9
|
-
import {
|
|
10
|
-
addAgentTurnUsage
|
|
2
|
+
addAgentTurnUsage,
|
|
3
|
+
agentTurnUsageSchema
|
|
11
4
|
} from "./chunk-MU6HHZEN.js";
|
|
12
5
|
import {
|
|
13
6
|
getStateAdapter
|
|
14
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-OUHNWH46.js";
|
|
15
8
|
import {
|
|
16
|
-
|
|
17
|
-
|
|
9
|
+
contextProvenance,
|
|
10
|
+
createSqlConversationEventStore,
|
|
11
|
+
getConversationEventStore,
|
|
12
|
+
getConversationStore,
|
|
13
|
+
getSqlExecutor,
|
|
14
|
+
instructionActors,
|
|
15
|
+
instructionProvenanceFor,
|
|
16
|
+
sanitizePostgresJson,
|
|
17
|
+
withConversationEventLock
|
|
18
|
+
} from "./chunk-FENLIFJD.js";
|
|
18
19
|
import {
|
|
19
20
|
sleep
|
|
20
21
|
} from "./chunk-4ZNGQH7C.js";
|
|
@@ -23,22 +24,345 @@ import {
|
|
|
23
24
|
escapeXml,
|
|
24
25
|
getPiMessageRole,
|
|
25
26
|
resolveGatewayModel,
|
|
27
|
+
retainRuntimeTurnContext,
|
|
26
28
|
standardModelId,
|
|
29
|
+
stripRuntimeTurnContext,
|
|
27
30
|
trimTrailingAssistantMessages
|
|
28
|
-
} from "./chunk-
|
|
31
|
+
} from "./chunk-JNAVF45E.js";
|
|
32
|
+
import {
|
|
33
|
+
toStoredSlackActor
|
|
34
|
+
} from "./chunk-PLB7MRCQ.js";
|
|
29
35
|
import {
|
|
30
36
|
getActiveTraceId,
|
|
31
37
|
logException,
|
|
32
38
|
logWarn,
|
|
33
39
|
setSpanAttributes
|
|
34
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-ISMWX44P.js";
|
|
35
41
|
import {
|
|
36
42
|
isRecord,
|
|
43
|
+
toOptionalNumber,
|
|
37
44
|
toOptionalString
|
|
38
45
|
} from "./chunk-OB42YVAE.js";
|
|
39
46
|
import {
|
|
40
47
|
parseSlackMessageTs
|
|
41
|
-
} from "./chunk-
|
|
48
|
+
} from "./chunk-IVVGFRQQ.js";
|
|
49
|
+
|
|
50
|
+
// src/chat/conversations/projection.ts
|
|
51
|
+
import { isDeepStrictEqual } from "util";
|
|
52
|
+
|
|
53
|
+
// src/chat/pi/messages.ts
|
|
54
|
+
import { z } from "zod";
|
|
55
|
+
var piMessageSchema = z.object({
|
|
56
|
+
role: z.string()
|
|
57
|
+
}).passthrough().transform((value) => value);
|
|
58
|
+
var piContentMessageSchema = z.object({
|
|
59
|
+
content: z.array(z.unknown()),
|
|
60
|
+
role: z.string().min(1)
|
|
61
|
+
}).passthrough().transform((value) => value);
|
|
62
|
+
|
|
63
|
+
// src/chat/pi/conversation-events.ts
|
|
64
|
+
function authorizationObservationMessage(data, createdAtMs) {
|
|
65
|
+
const label = data.kind === "mcp" ? "MCP authorization" : "Authorization";
|
|
66
|
+
return piMessageSchema.parse({
|
|
67
|
+
role: "user",
|
|
68
|
+
content: [
|
|
69
|
+
{
|
|
70
|
+
type: "text",
|
|
71
|
+
text: `${label} completed for provider "${data.provider}". Continue the blocked request and retry the provider operation if needed.`
|
|
72
|
+
}
|
|
73
|
+
],
|
|
74
|
+
timestamp: createdAtMs
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
function messageEventProvenance(data) {
|
|
78
|
+
return data.provenance ?? contextProvenance;
|
|
79
|
+
}
|
|
80
|
+
function durableMessages(message) {
|
|
81
|
+
return stripRuntimeTurnContext([piMessageSchema.parse(message)]);
|
|
82
|
+
}
|
|
83
|
+
function projectConversationEvents(events, options) {
|
|
84
|
+
const messages = [];
|
|
85
|
+
const provenance = [];
|
|
86
|
+
const seqs = [];
|
|
87
|
+
let modelProfile = "standard";
|
|
88
|
+
let modelId;
|
|
89
|
+
for (const event of events) {
|
|
90
|
+
if (options?.maxSeq !== void 0 && event.seq > options.maxSeq) break;
|
|
91
|
+
if (event.data.type === "unknown") {
|
|
92
|
+
throw new Error(
|
|
93
|
+
`Unsupported conversation event "${event.data.originalType}" at seq ${event.seq} (schema version ${event.schemaVersion})`
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
if (event.data.type === "compaction" || event.data.type === "handoff" || event.data.type === "rollback") {
|
|
97
|
+
modelProfile = event.data.modelProfile;
|
|
98
|
+
modelId = event.data.modelId;
|
|
99
|
+
for (const replacement of event.data.replacementHistory) {
|
|
100
|
+
for (const message of durableMessages(replacement.message)) {
|
|
101
|
+
messages.push(message);
|
|
102
|
+
provenance.push(replacement.provenance ?? contextProvenance);
|
|
103
|
+
seqs.push(replacement.sourceEventSeq ?? event.seq);
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
continue;
|
|
107
|
+
}
|
|
108
|
+
if (event.data.type === "agent_step") {
|
|
109
|
+
for (const message of durableMessages(event.data.message)) {
|
|
110
|
+
messages.push(message);
|
|
111
|
+
provenance.push(messageEventProvenance(event.data));
|
|
112
|
+
seqs.push(event.seq);
|
|
113
|
+
}
|
|
114
|
+
continue;
|
|
115
|
+
}
|
|
116
|
+
if (event.data.type === "authorization_completed") {
|
|
117
|
+
messages.push(
|
|
118
|
+
authorizationObservationMessage(event.data, event.createdAtMs)
|
|
119
|
+
);
|
|
120
|
+
provenance.push(contextProvenance);
|
|
121
|
+
seqs.push(event.seq);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
return { messages, provenance, seqs, modelProfile, modelId };
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// src/chat/conversations/projection.ts
|
|
128
|
+
function connectedMcpProvidersFromEvents(events) {
|
|
129
|
+
const providers = /* @__PURE__ */ new Set();
|
|
130
|
+
for (const event of events) {
|
|
131
|
+
if (event.data.type === "mcp_provider_connected") {
|
|
132
|
+
providers.add(event.data.provider);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
return [...providers].sort((left, right) => left.localeCompare(right));
|
|
136
|
+
}
|
|
137
|
+
function isUserMessage(message) {
|
|
138
|
+
return message.role === "user";
|
|
139
|
+
}
|
|
140
|
+
function countMatchingPrefix(left, right) {
|
|
141
|
+
const limit = Math.min(left.length, right.length);
|
|
142
|
+
for (let index = 0; index < limit; index += 1) {
|
|
143
|
+
if (!isDeepStrictEqual(left[index], right[index])) {
|
|
144
|
+
return index;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return limit;
|
|
148
|
+
}
|
|
149
|
+
function normalizeDurableMessage(message) {
|
|
150
|
+
return piMessageSchema.parse(
|
|
151
|
+
JSON.parse(JSON.stringify(sanitizePostgresJson(message)))
|
|
152
|
+
);
|
|
153
|
+
}
|
|
154
|
+
function resolveCommitProvenance(args) {
|
|
155
|
+
if (args.explicitProvenance) {
|
|
156
|
+
if (args.explicitProvenance.length !== args.nextMessages.length) {
|
|
157
|
+
throw new Error("commit provenance must align one-to-one with messages");
|
|
158
|
+
}
|
|
159
|
+
return args.explicitProvenance;
|
|
160
|
+
}
|
|
161
|
+
const matchingPrefix = args.matchingPrefix;
|
|
162
|
+
const provenance = args.nextMessages.map(
|
|
163
|
+
(_, index) => index < matchingPrefix ? args.existing.provenance[index] ?? contextProvenance : contextProvenance
|
|
164
|
+
);
|
|
165
|
+
if (args.newMessageProvenance) {
|
|
166
|
+
for (let index = args.nextMessages.length - 1; index >= matchingPrefix; index -= 1) {
|
|
167
|
+
if (isUserMessage(args.nextMessages[index])) {
|
|
168
|
+
provenance[index] = args.newMessageProvenance;
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
if (args.trailingMessageProvenance) {
|
|
174
|
+
if (args.trailingMessageProvenance.length > provenance.length) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
"trailing commit provenance cannot exceed committed messages"
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
const newMessageCount = args.nextMessages.length - matchingPrefix;
|
|
180
|
+
if (args.trailingMessageProvenance.length > newMessageCount) {
|
|
181
|
+
throw new Error(
|
|
182
|
+
"trailing commit provenance must align to newly committed messages"
|
|
183
|
+
);
|
|
184
|
+
}
|
|
185
|
+
const start = provenance.length - args.trailingMessageProvenance.length;
|
|
186
|
+
args.trailingMessageProvenance.forEach((entry, offset) => {
|
|
187
|
+
provenance[start + offset] = entry;
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return provenance;
|
|
191
|
+
}
|
|
192
|
+
async function loadProjection(args) {
|
|
193
|
+
const events = await getConversationEventStore().loadCurrentHistory(
|
|
194
|
+
args.conversationId
|
|
195
|
+
);
|
|
196
|
+
return projectConversationEvents(events).messages;
|
|
197
|
+
}
|
|
198
|
+
async function loadConversationProjection(args) {
|
|
199
|
+
const events = await getConversationEventStore().loadCurrentHistory(
|
|
200
|
+
args.conversationId
|
|
201
|
+
);
|
|
202
|
+
return projectConversationEvents(events);
|
|
203
|
+
}
|
|
204
|
+
async function openConversationProjection(args) {
|
|
205
|
+
const events = await getConversationEventStore().loadCurrentHistory(
|
|
206
|
+
args.conversationId
|
|
207
|
+
);
|
|
208
|
+
return projectConversationEvents(events);
|
|
209
|
+
}
|
|
210
|
+
async function loadTurnProjection(args) {
|
|
211
|
+
const eventStore = getConversationEventStore();
|
|
212
|
+
if (args.committedSeq < 0) {
|
|
213
|
+
return projectConversationEvents(
|
|
214
|
+
await eventStore.loadCurrentHistory(args.conversationId)
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
const historyEvents = await eventStore.loadHistoryContaining(
|
|
218
|
+
args.conversationId,
|
|
219
|
+
args.committedSeq,
|
|
220
|
+
args.includeTail ? void 0 : args.committedSeq
|
|
221
|
+
);
|
|
222
|
+
if (!historyEvents) {
|
|
223
|
+
return void 0;
|
|
224
|
+
}
|
|
225
|
+
return projectConversationEvents(historyEvents);
|
|
226
|
+
}
|
|
227
|
+
async function loadConnectedMcpProviders(args) {
|
|
228
|
+
const events = await getConversationEventStore().loadCurrentHistory(
|
|
229
|
+
args.conversationId
|
|
230
|
+
);
|
|
231
|
+
return connectedMcpProvidersFromEvents(events);
|
|
232
|
+
}
|
|
233
|
+
function messageTimestamp(message) {
|
|
234
|
+
const timestamp = message.timestamp;
|
|
235
|
+
return typeof timestamp === "number" ? timestamp : Date.now();
|
|
236
|
+
}
|
|
237
|
+
async function commitMessages(args) {
|
|
238
|
+
const executor = args.executor ?? getSqlExecutor();
|
|
239
|
+
return await withConversationEventLock(
|
|
240
|
+
executor,
|
|
241
|
+
args.conversationId,
|
|
242
|
+
async () => executor.transaction(
|
|
243
|
+
async () => await commitMessagesLocked(args, executor)
|
|
244
|
+
)
|
|
245
|
+
);
|
|
246
|
+
}
|
|
247
|
+
async function commitMessagesLocked(args, executor) {
|
|
248
|
+
const eventStore = createSqlConversationEventStore(executor);
|
|
249
|
+
const currentEvents = await eventStore.loadCurrentHistory(
|
|
250
|
+
args.conversationId
|
|
251
|
+
);
|
|
252
|
+
const current = projectConversationEvents(currentEvents);
|
|
253
|
+
const nextLocalMessages = stripRuntimeTurnContext(args.messages).map(
|
|
254
|
+
normalizeDurableMessage
|
|
255
|
+
);
|
|
256
|
+
const matchingPrefix = countMatchingPrefix(
|
|
257
|
+
current.messages,
|
|
258
|
+
nextLocalMessages
|
|
259
|
+
);
|
|
260
|
+
const nextLocalProvenance = resolveCommitProvenance({
|
|
261
|
+
existing: current,
|
|
262
|
+
nextMessages: nextLocalMessages,
|
|
263
|
+
matchingPrefix,
|
|
264
|
+
...args.provenance ? { explicitProvenance: args.provenance } : {},
|
|
265
|
+
...args.trailingMessageProvenance ? { trailingMessageProvenance: args.trailingMessageProvenance } : {},
|
|
266
|
+
...args.newMessageProvenance ? { newMessageProvenance: args.newMessageProvenance } : {}
|
|
267
|
+
});
|
|
268
|
+
if (matchingPrefix === current.messages.length) {
|
|
269
|
+
const newMessages = nextLocalMessages.slice(matchingPrefix);
|
|
270
|
+
await eventStore.append(
|
|
271
|
+
args.conversationId,
|
|
272
|
+
newMessages.map((message, index) => ({
|
|
273
|
+
data: {
|
|
274
|
+
type: "agent_step",
|
|
275
|
+
message,
|
|
276
|
+
provenance: nextLocalProvenance[matchingPrefix + index]
|
|
277
|
+
},
|
|
278
|
+
createdAtMs: messageTimestamp(message)
|
|
279
|
+
}))
|
|
280
|
+
);
|
|
281
|
+
} else {
|
|
282
|
+
throw new Error(
|
|
283
|
+
`Agent history for ${args.conversationId} changed before its committed boundary`
|
|
284
|
+
);
|
|
285
|
+
}
|
|
286
|
+
const committedEvents = await eventStore.loadCurrentHistory(
|
|
287
|
+
args.conversationId
|
|
288
|
+
);
|
|
289
|
+
const committed = projectConversationEvents(committedEvents);
|
|
290
|
+
return {
|
|
291
|
+
committedSeq: committedEvents.at(-1)?.seq ?? -1,
|
|
292
|
+
historyVersion: committedEvents.at(-1)?.historyVersion ?? 0,
|
|
293
|
+
messageSeqs: committed.seqs,
|
|
294
|
+
messages: nextLocalMessages,
|
|
295
|
+
provenance: nextLocalProvenance
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
async function recordMcpProviderConnected(args) {
|
|
299
|
+
const eventStore = getConversationEventStore();
|
|
300
|
+
const events = await eventStore.loadCurrentHistory(args.conversationId);
|
|
301
|
+
if (connectedMcpProvidersFromEvents(events).includes(args.provider)) {
|
|
302
|
+
return;
|
|
303
|
+
}
|
|
304
|
+
await eventStore.append(args.conversationId, [
|
|
305
|
+
{
|
|
306
|
+
data: { type: "mcp_provider_connected", provider: args.provider },
|
|
307
|
+
createdAtMs: Date.now()
|
|
308
|
+
}
|
|
309
|
+
]);
|
|
310
|
+
}
|
|
311
|
+
async function recordAuthorizationRequested(args) {
|
|
312
|
+
const eventStore = getConversationEventStore();
|
|
313
|
+
const events = await eventStore.loadCurrentHistory(args.conversationId);
|
|
314
|
+
if (events.some(
|
|
315
|
+
(event) => event.data.type === "authorization_requested" && event.data.authorizationId === args.authorizationId
|
|
316
|
+
)) {
|
|
317
|
+
return;
|
|
318
|
+
}
|
|
319
|
+
await eventStore.append(args.conversationId, [
|
|
320
|
+
{
|
|
321
|
+
data: {
|
|
322
|
+
type: "authorization_requested",
|
|
323
|
+
kind: args.kind,
|
|
324
|
+
provider: args.provider,
|
|
325
|
+
actorId: args.actorId,
|
|
326
|
+
authorizationId: args.authorizationId,
|
|
327
|
+
delivery: args.delivery
|
|
328
|
+
},
|
|
329
|
+
createdAtMs: Date.now()
|
|
330
|
+
}
|
|
331
|
+
]);
|
|
332
|
+
}
|
|
333
|
+
async function recordAuthorizationCompleted(args) {
|
|
334
|
+
const eventStore = getConversationEventStore();
|
|
335
|
+
const events = await eventStore.loadCurrentHistory(args.conversationId);
|
|
336
|
+
if (events.some(
|
|
337
|
+
(event) => event.data.type === "authorization_completed" && event.data.authorizationId === args.authorizationId
|
|
338
|
+
)) {
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
await eventStore.append(args.conversationId, [
|
|
342
|
+
{
|
|
343
|
+
data: {
|
|
344
|
+
type: "authorization_completed",
|
|
345
|
+
kind: args.kind,
|
|
346
|
+
provider: args.provider,
|
|
347
|
+
actorId: args.actorId,
|
|
348
|
+
authorizationId: args.authorizationId
|
|
349
|
+
},
|
|
350
|
+
createdAtMs: Date.now()
|
|
351
|
+
}
|
|
352
|
+
]);
|
|
353
|
+
}
|
|
354
|
+
async function recordToolExecutionStarted(args) {
|
|
355
|
+
await getConversationEventStore().append(args.conversationId, [
|
|
356
|
+
{
|
|
357
|
+
data: {
|
|
358
|
+
type: "tool_execution_started",
|
|
359
|
+
toolCallId: args.toolCallId,
|
|
360
|
+
toolName: args.toolName
|
|
361
|
+
},
|
|
362
|
+
createdAtMs: args.createdAtMs ?? Date.now()
|
|
363
|
+
}
|
|
364
|
+
]);
|
|
365
|
+
}
|
|
42
366
|
|
|
43
367
|
// src/chat/state/turn-id.ts
|
|
44
368
|
function buildDeterministicTurnId(messageId) {
|
|
@@ -145,6 +469,531 @@ async function persistWithRetry(persist) {
|
|
|
145
469
|
throw lastError;
|
|
146
470
|
}
|
|
147
471
|
|
|
472
|
+
// src/chat/state/turn-session.ts
|
|
473
|
+
import { THREAD_STATE_TTL_MS } from "chat";
|
|
474
|
+
import {
|
|
475
|
+
actorSchema,
|
|
476
|
+
destinationSchema,
|
|
477
|
+
sourceSchema
|
|
478
|
+
} from "@sentry/junior-plugin-api";
|
|
479
|
+
import { z as z2 } from "zod";
|
|
480
|
+
|
|
481
|
+
// src/chat/state/turn-session-keys.ts
|
|
482
|
+
var AGENT_TURN_SESSION_PREFIX = "junior:agent_turn_session";
|
|
483
|
+
function agentTurnSessionKey(conversationId, sessionId) {
|
|
484
|
+
return `${AGENT_TURN_SESSION_PREFIX}:${conversationId}:${sessionId}`;
|
|
485
|
+
}
|
|
486
|
+
function agentTurnSessionConversationIndexKey(conversationId) {
|
|
487
|
+
return `${AGENT_TURN_SESSION_PREFIX}:conversation:${conversationId}:index`;
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// src/chat/state/turn-session.ts
|
|
491
|
+
var AGENT_TURN_SESSION_INDEX_KEY = `${AGENT_TURN_SESSION_PREFIX}:index`;
|
|
492
|
+
var AGENT_TURN_SESSION_INDEX_MAX_LENGTH = 5e3;
|
|
493
|
+
var AGENT_TURN_SESSION_TTL_MS = THREAD_STATE_TTL_MS;
|
|
494
|
+
var agentTurnSessionStatusSchema = z2.enum([
|
|
495
|
+
"running",
|
|
496
|
+
"awaiting_resume",
|
|
497
|
+
"completed",
|
|
498
|
+
"failed",
|
|
499
|
+
"abandoned"
|
|
500
|
+
]);
|
|
501
|
+
var agentTurnSurfaceSchema = z2.enum([
|
|
502
|
+
"slack",
|
|
503
|
+
"api",
|
|
504
|
+
"scheduler",
|
|
505
|
+
"internal"
|
|
506
|
+
]);
|
|
507
|
+
var agentTurnResumeReasonSchema = z2.enum([
|
|
508
|
+
"timeout",
|
|
509
|
+
"auth",
|
|
510
|
+
"yield",
|
|
511
|
+
"retry"
|
|
512
|
+
]);
|
|
513
|
+
var nonNegativeNumberSchema = z2.number().finite().nonnegative();
|
|
514
|
+
var seqCursorSchema = z2.number().int().min(-1);
|
|
515
|
+
var agentTurnSessionSummarySchema = z2.object({
|
|
516
|
+
channelName: z2.string().min(1).optional(),
|
|
517
|
+
version: z2.number().int().nonnegative(),
|
|
518
|
+
conversationId: z2.string().min(1),
|
|
519
|
+
cumulativeDurationMs: nonNegativeNumberSchema,
|
|
520
|
+
cumulativeUsage: agentTurnUsageSchema.optional(),
|
|
521
|
+
destination: destinationSchema.optional(),
|
|
522
|
+
source: sourceSchema.optional(),
|
|
523
|
+
lastProgressAtMs: nonNegativeNumberSchema,
|
|
524
|
+
loadedSkillNames: z2.array(z2.string()).optional(),
|
|
525
|
+
modelId: z2.string().min(1).optional(),
|
|
526
|
+
reasoningLevel: z2.string().min(1).optional(),
|
|
527
|
+
actor: actorSchema.optional(),
|
|
528
|
+
resumeReason: agentTurnResumeReasonSchema.optional(),
|
|
529
|
+
resumedFromSliceId: z2.number().int().nonnegative().optional(),
|
|
530
|
+
sessionId: z2.string().min(1),
|
|
531
|
+
sliceId: z2.number().int().nonnegative(),
|
|
532
|
+
startedAtMs: nonNegativeNumberSchema,
|
|
533
|
+
state: agentTurnSessionStatusSchema,
|
|
534
|
+
surface: agentTurnSurfaceSchema.optional(),
|
|
535
|
+
traceId: z2.string().optional(),
|
|
536
|
+
updatedAtMs: nonNegativeNumberSchema
|
|
537
|
+
}).strict();
|
|
538
|
+
var storedAgentTurnSessionRecordSchema = agentTurnSessionSummarySchema.extend({
|
|
539
|
+
actors: z2.array(actorSchema).optional(),
|
|
540
|
+
committedSeq: seqCursorSchema,
|
|
541
|
+
historyVersion: z2.number().int().nonnegative().optional(),
|
|
542
|
+
errorMessage: z2.string().optional(),
|
|
543
|
+
turnStartSeq: seqCursorSchema.optional(),
|
|
544
|
+
runtimeContext: z2.array(piMessageSchema).optional()
|
|
545
|
+
}).strict();
|
|
546
|
+
function conversationExecutionFromSummary(summary) {
|
|
547
|
+
const status = summary.state === "completed" || summary.state === "abandoned" ? "idle" : summary.state;
|
|
548
|
+
return {
|
|
549
|
+
status,
|
|
550
|
+
runId: summary.sessionId,
|
|
551
|
+
updatedAtMs: summary.updatedAtMs
|
|
552
|
+
};
|
|
553
|
+
}
|
|
554
|
+
function sessionLogActor(actor) {
|
|
555
|
+
return actor?.platform === "slack" ? toStoredSlackActor(actor) : void 0;
|
|
556
|
+
}
|
|
557
|
+
function parseAgentTurnSessionRecord(value) {
|
|
558
|
+
return storedAgentTurnSessionRecordSchema.parse(value);
|
|
559
|
+
}
|
|
560
|
+
function parseAgentTurnSessionSummary(value) {
|
|
561
|
+
return agentTurnSessionSummarySchema.parse(value);
|
|
562
|
+
}
|
|
563
|
+
async function appendAgentTurnSessionSummary(summary, ttlMs) {
|
|
564
|
+
const stateAdapter = getStateAdapter();
|
|
565
|
+
await Promise.all([
|
|
566
|
+
stateAdapter.appendToList(AGENT_TURN_SESSION_INDEX_KEY, summary, {
|
|
567
|
+
maxLength: AGENT_TURN_SESSION_INDEX_MAX_LENGTH,
|
|
568
|
+
ttlMs
|
|
569
|
+
}),
|
|
570
|
+
stateAdapter.appendToList(
|
|
571
|
+
agentTurnSessionConversationIndexKey(summary.conversationId),
|
|
572
|
+
summary,
|
|
573
|
+
{ ttlMs }
|
|
574
|
+
)
|
|
575
|
+
]);
|
|
576
|
+
}
|
|
577
|
+
async function recordConversationActivityMetadata(args) {
|
|
578
|
+
const conversationStore = args.conversationStore ?? getConversationStore();
|
|
579
|
+
const source = args.summary.destination?.platform === "local" ? "local" : args.summary.surface;
|
|
580
|
+
await conversationStore.recordActivity({
|
|
581
|
+
activityAtMs: args.summary.updatedAtMs,
|
|
582
|
+
channelName: args.summary.channelName,
|
|
583
|
+
conversationId: args.summary.conversationId,
|
|
584
|
+
destination: args.summary.destination,
|
|
585
|
+
nowMs: args.nowMs,
|
|
586
|
+
actor: sessionLogActor(args.summary.actor),
|
|
587
|
+
source,
|
|
588
|
+
visibility: args.destinationVisibility
|
|
589
|
+
});
|
|
590
|
+
await conversationStore.recordExecution({
|
|
591
|
+
channelName: args.summary.channelName,
|
|
592
|
+
conversationId: args.summary.conversationId,
|
|
593
|
+
createdAtMs: args.summary.startedAtMs,
|
|
594
|
+
destination: args.summary.destination,
|
|
595
|
+
execution: conversationExecutionFromSummary(args.summary),
|
|
596
|
+
lastActivityAtMs: args.summary.updatedAtMs,
|
|
597
|
+
metrics: {
|
|
598
|
+
durationMs: args.summary.cumulativeDurationMs,
|
|
599
|
+
...args.summary.cumulativeUsage ? { usage: args.summary.cumulativeUsage } : {}
|
|
600
|
+
},
|
|
601
|
+
actor: sessionLogActor(args.summary.actor),
|
|
602
|
+
source,
|
|
603
|
+
updatedAtMs: args.nowMs,
|
|
604
|
+
visibility: args.destinationVisibility
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
function materializeAgentTurnSessionRecord(stored, piProjection, turnStartMessageIndex, restoreVolatileContext = true) {
|
|
608
|
+
const piMessages = restoreVolatileContext && (stored.state === "running" || stored.state === "awaiting_resume") ? restoreRuntimeContext(piProjection.messages, stored.runtimeContext) : piProjection.messages;
|
|
609
|
+
return {
|
|
610
|
+
version: stored.version,
|
|
611
|
+
...stored.channelName ? { channelName: stored.channelName } : {},
|
|
612
|
+
conversationId: stored.conversationId,
|
|
613
|
+
sessionId: stored.sessionId,
|
|
614
|
+
sliceId: stored.sliceId,
|
|
615
|
+
state: stored.state,
|
|
616
|
+
startedAtMs: stored.startedAtMs,
|
|
617
|
+
lastProgressAtMs: stored.lastProgressAtMs,
|
|
618
|
+
updatedAtMs: stored.updatedAtMs,
|
|
619
|
+
piMessages,
|
|
620
|
+
piMessageProvenance: piProjection.provenance,
|
|
621
|
+
actors: stored.actors ?? instructionActors(piProjection.provenance),
|
|
622
|
+
cumulativeDurationMs: stored.cumulativeDurationMs,
|
|
623
|
+
...stored.destination ? { destination: stored.destination } : {},
|
|
624
|
+
...stored.source ? { source: stored.source } : {},
|
|
625
|
+
...stored.cumulativeUsage ? { cumulativeUsage: stored.cumulativeUsage } : {},
|
|
626
|
+
...stored.resumeReason ? { resumeReason: stored.resumeReason } : {},
|
|
627
|
+
...stored.errorMessage ? { errorMessage: stored.errorMessage } : {},
|
|
628
|
+
...stored.loadedSkillNames ? { loadedSkillNames: stored.loadedSkillNames } : {},
|
|
629
|
+
...stored.modelId ? { modelId: stored.modelId } : {},
|
|
630
|
+
...stored.reasoningLevel ? { reasoningLevel: stored.reasoningLevel } : {},
|
|
631
|
+
...stored.actor ? { actor: stored.actor } : {},
|
|
632
|
+
...stored.resumedFromSliceId !== void 0 ? { resumedFromSliceId: stored.resumedFromSliceId } : {},
|
|
633
|
+
...stored.surface ? { surface: stored.surface } : {},
|
|
634
|
+
...stored.traceId ? { traceId: stored.traceId } : {},
|
|
635
|
+
...turnStartMessageIndex !== void 0 ? { turnStartMessageIndex } : {}
|
|
636
|
+
};
|
|
637
|
+
}
|
|
638
|
+
function restoreRuntimeContext(messages, runtimeContext) {
|
|
639
|
+
if (!runtimeContext || runtimeContext.length === 0) return messages;
|
|
640
|
+
const restored = [...messages];
|
|
641
|
+
for (const runtimeMessage of runtimeContext) {
|
|
642
|
+
const runtime = runtimeMessage;
|
|
643
|
+
const targetIndex = restored.findIndex((message) => {
|
|
644
|
+
const candidate = message;
|
|
645
|
+
return candidate.role === "user" && candidate.timestamp === runtime.timestamp;
|
|
646
|
+
});
|
|
647
|
+
if (targetIndex < 0) {
|
|
648
|
+
restored.push(runtimeMessage);
|
|
649
|
+
continue;
|
|
650
|
+
}
|
|
651
|
+
const target = restored[targetIndex];
|
|
652
|
+
restored[targetIndex] = {
|
|
653
|
+
...restored[targetIndex],
|
|
654
|
+
content: [
|
|
655
|
+
...Array.isArray(runtime.content) ? runtime.content : [],
|
|
656
|
+
...Array.isArray(target.content) ? target.content : []
|
|
657
|
+
]
|
|
658
|
+
};
|
|
659
|
+
}
|
|
660
|
+
return restored;
|
|
661
|
+
}
|
|
662
|
+
async function getStoredAgentTurnSessionRecord(conversationId, sessionId) {
|
|
663
|
+
const stateAdapter = getStateAdapter();
|
|
664
|
+
await stateAdapter.connect();
|
|
665
|
+
const value = await stateAdapter.get(
|
|
666
|
+
agentTurnSessionKey(conversationId, sessionId)
|
|
667
|
+
);
|
|
668
|
+
return value == null ? void 0 : parseAgentTurnSessionRecord(value);
|
|
669
|
+
}
|
|
670
|
+
async function materializeStoredAgentTurnSessionRecord(conversationId, sessionId, followCurrentReplacement) {
|
|
671
|
+
const parsed = await getStoredAgentTurnSessionRecord(
|
|
672
|
+
conversationId,
|
|
673
|
+
sessionId
|
|
674
|
+
);
|
|
675
|
+
if (!parsed) {
|
|
676
|
+
return void 0;
|
|
677
|
+
}
|
|
678
|
+
const pinnedProjection = await loadTurnProjection({
|
|
679
|
+
conversationId,
|
|
680
|
+
committedSeq: parsed.committedSeq,
|
|
681
|
+
// Unfinished records include the current-epoch tail so parked input
|
|
682
|
+
// appended after the last safe boundary stays model-visible on resume.
|
|
683
|
+
includeTail: parsed.state === "running" || parsed.state === "awaiting_resume"
|
|
684
|
+
});
|
|
685
|
+
if (!pinnedProjection) {
|
|
686
|
+
return void 0;
|
|
687
|
+
}
|
|
688
|
+
const currentHistory = await getConversationEventStore().loadCurrentHistory(conversationId);
|
|
689
|
+
const currentHistoryVersion = currentHistory.at(-1)?.historyVersion ?? parsed.historyVersion ?? 0;
|
|
690
|
+
const followsReplacement = followCurrentReplacement && (parsed.state === "running" || parsed.state === "awaiting_resume") && parsed.historyVersion !== void 0 && parsed.historyVersion !== currentHistoryVersion;
|
|
691
|
+
const piProjection = followsReplacement ? projectConversationEvents(currentHistory) : pinnedProjection;
|
|
692
|
+
const turnStartMessageIndex = followsReplacement ? 0 : parsed.turnStartSeq === void 0 ? void 0 : piProjection.seqs.filter((seq) => seq <= parsed.turnStartSeq).length;
|
|
693
|
+
return materializeAgentTurnSessionRecord(
|
|
694
|
+
parsed,
|
|
695
|
+
piProjection,
|
|
696
|
+
turnStartMessageIndex,
|
|
697
|
+
!followsReplacement && (parsed.historyVersion === void 0 || parsed.historyVersion === currentHistoryVersion)
|
|
698
|
+
);
|
|
699
|
+
}
|
|
700
|
+
async function getAgentTurnSessionRecord(conversationId, sessionId) {
|
|
701
|
+
return await materializeStoredAgentTurnSessionRecord(
|
|
702
|
+
conversationId,
|
|
703
|
+
sessionId,
|
|
704
|
+
false
|
|
705
|
+
);
|
|
706
|
+
}
|
|
707
|
+
async function getAgentTurnSessionRecordForResume(conversationId, sessionId) {
|
|
708
|
+
return await materializeStoredAgentTurnSessionRecord(
|
|
709
|
+
conversationId,
|
|
710
|
+
sessionId,
|
|
711
|
+
true
|
|
712
|
+
);
|
|
713
|
+
}
|
|
714
|
+
function buildStoredRecord(args) {
|
|
715
|
+
const nowMs = Date.now();
|
|
716
|
+
return {
|
|
717
|
+
version: (args.previousVersion ?? 0) + 1,
|
|
718
|
+
...args.channelName ? { channelName: args.channelName } : {},
|
|
719
|
+
conversationId: args.conversationId,
|
|
720
|
+
sessionId: args.sessionId,
|
|
721
|
+
sliceId: args.sliceId,
|
|
722
|
+
state: args.state,
|
|
723
|
+
startedAtMs: args.startedAtMs ?? nowMs,
|
|
724
|
+
lastProgressAtMs: args.lastProgressAtMs ?? nowMs,
|
|
725
|
+
updatedAtMs: nowMs,
|
|
726
|
+
committedSeq: args.committedSeq,
|
|
727
|
+
...args.historyVersion !== void 0 ? { historyVersion: args.historyVersion } : {},
|
|
728
|
+
...args.turnStartSeq !== void 0 ? { turnStartSeq: args.turnStartSeq } : {},
|
|
729
|
+
...args.runtimeContext && args.runtimeContext.length > 0 ? { runtimeContext: args.runtimeContext } : {},
|
|
730
|
+
cumulativeDurationMs: args.cumulativeDurationMs,
|
|
731
|
+
...args.cumulativeUsage ? { cumulativeUsage: args.cumulativeUsage } : {},
|
|
732
|
+
...args.destination ? { destination: args.destination } : {},
|
|
733
|
+
...args.source ? { source: args.source } : {},
|
|
734
|
+
...args.actor ? { actor: args.actor } : {},
|
|
735
|
+
...args.actors ? { actors: args.actors } : {},
|
|
736
|
+
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
737
|
+
...args.modelId ? { modelId: args.modelId } : {},
|
|
738
|
+
...args.reasoningLevel ? { reasoningLevel: args.reasoningLevel } : {},
|
|
739
|
+
...args.resumeReason ? { resumeReason: args.resumeReason } : {},
|
|
740
|
+
...args.errorMessage ? { errorMessage: args.errorMessage } : {},
|
|
741
|
+
...args.resumedFromSliceId !== void 0 ? { resumedFromSliceId: args.resumedFromSliceId } : {},
|
|
742
|
+
...args.surface ? { surface: args.surface } : {},
|
|
743
|
+
...args.traceId ? { traceId: args.traceId } : {}
|
|
744
|
+
};
|
|
745
|
+
}
|
|
746
|
+
async function setStoredRecord(args) {
|
|
747
|
+
const stateAdapter = getStateAdapter();
|
|
748
|
+
await stateAdapter.connect();
|
|
749
|
+
await recordConversationActivityMetadata({
|
|
750
|
+
conversationStore: args.conversationStore,
|
|
751
|
+
destinationVisibility: args.destinationVisibility,
|
|
752
|
+
nowMs: Date.now(),
|
|
753
|
+
summary: args.record
|
|
754
|
+
});
|
|
755
|
+
await stateAdapter.set(
|
|
756
|
+
agentTurnSessionKey(args.record.conversationId, args.record.sessionId),
|
|
757
|
+
args.record,
|
|
758
|
+
args.ttlMs
|
|
759
|
+
);
|
|
760
|
+
const {
|
|
761
|
+
actors: _actors,
|
|
762
|
+
committedSeq: _committedSeq,
|
|
763
|
+
historyVersion: _historyVersion,
|
|
764
|
+
errorMessage: _errorMessage,
|
|
765
|
+
turnStartSeq: _turnStartSeq,
|
|
766
|
+
runtimeContext: _runtimeContext,
|
|
767
|
+
...summary
|
|
768
|
+
} = args.record;
|
|
769
|
+
await appendAgentTurnSessionSummary(summary, args.ttlMs);
|
|
770
|
+
return materializeAgentTurnSessionRecord(
|
|
771
|
+
args.record,
|
|
772
|
+
{
|
|
773
|
+
messages: [...args.piMessages],
|
|
774
|
+
provenance: [...args.piMessageProvenance]
|
|
775
|
+
},
|
|
776
|
+
args.turnStartMessageIndex
|
|
777
|
+
);
|
|
778
|
+
}
|
|
779
|
+
async function updateAgentTurnSessionState(args) {
|
|
780
|
+
const parsed = await getStoredAgentTurnSessionRecord(
|
|
781
|
+
args.existing.conversationId,
|
|
782
|
+
args.existing.sessionId
|
|
783
|
+
);
|
|
784
|
+
if (!parsed || parsed.version !== args.existing.version) {
|
|
785
|
+
return void 0;
|
|
786
|
+
}
|
|
787
|
+
return await setStoredRecord({
|
|
788
|
+
piMessages: args.existing.piMessages,
|
|
789
|
+
piMessageProvenance: args.existing.piMessageProvenance,
|
|
790
|
+
ttlMs: AGENT_TURN_SESSION_TTL_MS,
|
|
791
|
+
...args.existing.turnStartMessageIndex !== void 0 ? { turnStartMessageIndex: args.existing.turnStartMessageIndex } : {},
|
|
792
|
+
record: buildStoredRecord({
|
|
793
|
+
conversationId: args.existing.conversationId,
|
|
794
|
+
sessionId: args.existing.sessionId,
|
|
795
|
+
sliceId: args.existing.sliceId,
|
|
796
|
+
state: args.state,
|
|
797
|
+
committedSeq: parsed.committedSeq,
|
|
798
|
+
...parsed.historyVersion !== void 0 ? { historyVersion: parsed.historyVersion } : {},
|
|
799
|
+
...parsed.turnStartSeq !== void 0 ? { turnStartSeq: parsed.turnStartSeq } : {},
|
|
800
|
+
...parsed.runtimeContext ? { runtimeContext: parsed.runtimeContext } : {},
|
|
801
|
+
...parsed.channelName ? { channelName: parsed.channelName } : {},
|
|
802
|
+
startedAtMs: parsed.startedAtMs,
|
|
803
|
+
lastProgressAtMs: parsed.lastProgressAtMs,
|
|
804
|
+
previousVersion: parsed.version,
|
|
805
|
+
cumulativeDurationMs: args.existing.cumulativeDurationMs,
|
|
806
|
+
...args.existing.cumulativeUsage ? { cumulativeUsage: args.existing.cumulativeUsage } : {},
|
|
807
|
+
...args.existing.destination ? { destination: args.existing.destination } : {},
|
|
808
|
+
...args.existing.source ? { source: args.existing.source } : {},
|
|
809
|
+
...args.existing.loadedSkillNames ? { loadedSkillNames: args.existing.loadedSkillNames } : {},
|
|
810
|
+
...args.existing.modelId ? { modelId: args.existing.modelId } : {},
|
|
811
|
+
...args.existing.reasoningLevel ? { reasoningLevel: args.existing.reasoningLevel } : {},
|
|
812
|
+
...args.existing.actor ? { actor: args.existing.actor } : {},
|
|
813
|
+
actors: args.existing.actors,
|
|
814
|
+
...args.existing.resumeReason ? { resumeReason: args.existing.resumeReason } : {},
|
|
815
|
+
...args.existing.resumedFromSliceId !== void 0 ? { resumedFromSliceId: args.existing.resumedFromSliceId } : {},
|
|
816
|
+
...args.existing.surface ? { surface: args.existing.surface } : {},
|
|
817
|
+
...args.existing.traceId ? { traceId: args.existing.traceId } : {},
|
|
818
|
+
...args.errorMessage ?? args.existing.errorMessage ? { errorMessage: args.errorMessage ?? args.existing.errorMessage } : {}
|
|
819
|
+
})
|
|
820
|
+
});
|
|
821
|
+
}
|
|
822
|
+
async function upsertAgentTurnSessionRecord(args) {
|
|
823
|
+
const existingRecord = await getStoredAgentTurnSessionRecord(
|
|
824
|
+
args.conversationId,
|
|
825
|
+
args.sessionId
|
|
826
|
+
);
|
|
827
|
+
const ttlMs = Math.max(1, args.ttlMs ?? AGENT_TURN_SESSION_TTL_MS);
|
|
828
|
+
const instructionActor = args.actor ?? existingRecord?.actor;
|
|
829
|
+
const commit = await commitMessages({
|
|
830
|
+
conversationId: args.conversationId,
|
|
831
|
+
messages: args.piMessages,
|
|
832
|
+
...instructionActor ? { newMessageProvenance: instructionProvenanceFor(instructionActor) } : {},
|
|
833
|
+
...args.trailingMessageProvenance ? { trailingMessageProvenance: args.trailingMessageProvenance } : {}
|
|
834
|
+
});
|
|
835
|
+
const durableTurnStartMessageIndex = args.turnStartMessageIndex === void 0 ? void 0 : stripRuntimeTurnContext(
|
|
836
|
+
args.piMessages.slice(0, args.turnStartMessageIndex)
|
|
837
|
+
).length;
|
|
838
|
+
const runtimeContext = retainRuntimeTurnContext(args.piMessages);
|
|
839
|
+
const retainedRuntimeContext = runtimeContext.length > 0 ? runtimeContext : existingRecord?.historyVersion === commit.historyVersion ? existingRecord.runtimeContext : void 0;
|
|
840
|
+
const turnStartSeq = durableTurnStartMessageIndex === void 0 ? existingRecord?.turnStartSeq : durableTurnStartMessageIndex <= 0 ? -1 : commit.messageSeqs[durableTurnStartMessageIndex - 1] ?? commit.committedSeq;
|
|
841
|
+
const turnStartMessageIndex = durableTurnStartMessageIndex ?? (turnStartSeq === void 0 ? void 0 : commit.messageSeqs.filter((seq) => seq <= turnStartSeq).length);
|
|
842
|
+
return await setStoredRecord({
|
|
843
|
+
conversationStore: args.conversationStore,
|
|
844
|
+
destinationVisibility: args.destinationVisibility,
|
|
845
|
+
piMessages: commit.messages,
|
|
846
|
+
piMessageProvenance: commit.provenance,
|
|
847
|
+
ttlMs,
|
|
848
|
+
...turnStartMessageIndex !== void 0 ? { turnStartMessageIndex } : {},
|
|
849
|
+
record: buildStoredRecord({
|
|
850
|
+
...args.channelName ?? existingRecord?.channelName ? { channelName: args.channelName ?? existingRecord?.channelName } : {},
|
|
851
|
+
conversationId: args.conversationId,
|
|
852
|
+
sessionId: args.sessionId,
|
|
853
|
+
sliceId: args.sliceId,
|
|
854
|
+
state: args.state,
|
|
855
|
+
...existingRecord?.startedAtMs !== void 0 ? { startedAtMs: existingRecord.startedAtMs } : {},
|
|
856
|
+
...args.lastProgressAtMs !== void 0 ? { lastProgressAtMs: args.lastProgressAtMs } : {},
|
|
857
|
+
committedSeq: commit.committedSeq,
|
|
858
|
+
historyVersion: commit.historyVersion,
|
|
859
|
+
...turnStartSeq !== void 0 ? { turnStartSeq } : {},
|
|
860
|
+
...retainedRuntimeContext ? { runtimeContext: retainedRuntimeContext } : {},
|
|
861
|
+
previousVersion: existingRecord?.version,
|
|
862
|
+
cumulativeDurationMs: args.cumulativeDurationMs ?? existingRecord?.cumulativeDurationMs ?? 0,
|
|
863
|
+
...args.cumulativeUsage ? { cumulativeUsage: args.cumulativeUsage } : {},
|
|
864
|
+
...args.destination ?? existingRecord?.destination ? { destination: args.destination ?? existingRecord?.destination } : {},
|
|
865
|
+
...args.source ?? existingRecord?.source ? { source: args.source ?? existingRecord?.source } : {},
|
|
866
|
+
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : {},
|
|
867
|
+
modelId: args.modelId,
|
|
868
|
+
...args.reasoningLevel ?? existingRecord?.reasoningLevel ? {
|
|
869
|
+
reasoningLevel: args.reasoningLevel ?? existingRecord?.reasoningLevel
|
|
870
|
+
} : {},
|
|
871
|
+
...args.actor ?? existingRecord?.actor ? { actor: args.actor ?? existingRecord?.actor } : {},
|
|
872
|
+
actors: instructionActors([
|
|
873
|
+
...(existingRecord?.actors ?? []).map(instructionProvenanceFor),
|
|
874
|
+
...(args.actors ?? []).map(instructionProvenanceFor),
|
|
875
|
+
...commit.provenance
|
|
876
|
+
]),
|
|
877
|
+
...args.resumeReason ? { resumeReason: args.resumeReason } : {},
|
|
878
|
+
...args.errorMessage ? { errorMessage: args.errorMessage } : {},
|
|
879
|
+
...args.resumedFromSliceId !== void 0 ? { resumedFromSliceId: args.resumedFromSliceId } : {},
|
|
880
|
+
...args.surface ?? existingRecord?.surface ? { surface: args.surface ?? existingRecord?.surface } : {},
|
|
881
|
+
...args.traceId ?? existingRecord?.traceId ? { traceId: args.traceId ?? existingRecord?.traceId } : {}
|
|
882
|
+
})
|
|
883
|
+
});
|
|
884
|
+
}
|
|
885
|
+
async function recordAgentTurnSessionSummary(args) {
|
|
886
|
+
const existing = await getStoredAgentTurnSessionRecord(
|
|
887
|
+
args.conversationId,
|
|
888
|
+
args.sessionId
|
|
889
|
+
);
|
|
890
|
+
const nowMs = Date.now();
|
|
891
|
+
const ttlMs = Math.max(1, args.ttlMs ?? AGENT_TURN_SESSION_TTL_MS);
|
|
892
|
+
const summary = {
|
|
893
|
+
version: existing?.version ?? 0,
|
|
894
|
+
...args.channelName ?? existing?.channelName ? { channelName: args.channelName ?? existing?.channelName } : {},
|
|
895
|
+
conversationId: args.conversationId,
|
|
896
|
+
sessionId: args.sessionId,
|
|
897
|
+
sliceId: args.sliceId,
|
|
898
|
+
startedAtMs: existing?.startedAtMs ?? args.startedAtMs ?? nowMs,
|
|
899
|
+
lastProgressAtMs: args.lastProgressAtMs ?? nowMs,
|
|
900
|
+
state: args.state,
|
|
901
|
+
updatedAtMs: nowMs,
|
|
902
|
+
cumulativeDurationMs: args.cumulativeDurationMs ?? existing?.cumulativeDurationMs ?? 0,
|
|
903
|
+
...args.cumulativeUsage ?? existing?.cumulativeUsage ? { cumulativeUsage: args.cumulativeUsage ?? existing?.cumulativeUsage } : {},
|
|
904
|
+
...args.destination ?? existing?.destination ? { destination: args.destination ?? existing?.destination } : {},
|
|
905
|
+
...args.source ?? existing?.source ? { source: args.source ?? existing?.source } : {},
|
|
906
|
+
...args.actor ?? existing?.actor ? { actor: args.actor ?? existing?.actor } : {},
|
|
907
|
+
...args.loadedSkillNames ? { loadedSkillNames: args.loadedSkillNames } : existing?.loadedSkillNames ? { loadedSkillNames: existing.loadedSkillNames } : {},
|
|
908
|
+
...args.modelId ?? existing?.modelId ? { modelId: args.modelId ?? existing?.modelId } : {},
|
|
909
|
+
...args.reasoningLevel ?? existing?.reasoningLevel ? { reasoningLevel: args.reasoningLevel ?? existing?.reasoningLevel } : {},
|
|
910
|
+
...args.resumeReason ? { resumeReason: args.resumeReason } : {},
|
|
911
|
+
...args.surface ?? existing?.surface ? { surface: args.surface ?? existing?.surface } : {},
|
|
912
|
+
...args.traceId ?? existing?.traceId ? { traceId: args.traceId ?? existing?.traceId } : {}
|
|
913
|
+
};
|
|
914
|
+
await recordConversationActivityMetadata({
|
|
915
|
+
conversationStore: args.conversationStore,
|
|
916
|
+
destinationVisibility: args.destinationVisibility,
|
|
917
|
+
nowMs,
|
|
918
|
+
summary
|
|
919
|
+
});
|
|
920
|
+
await appendAgentTurnSessionSummary(summary, ttlMs);
|
|
921
|
+
}
|
|
922
|
+
async function readAgentTurnSessionSummariesFromIndex(key, stateAdapter) {
|
|
923
|
+
await stateAdapter.connect();
|
|
924
|
+
const values = await stateAdapter.getList(key);
|
|
925
|
+
const summaries = /* @__PURE__ */ new Map();
|
|
926
|
+
for (const value of [...values].reverse()) {
|
|
927
|
+
let summary;
|
|
928
|
+
try {
|
|
929
|
+
summary = parseAgentTurnSessionSummary(value);
|
|
930
|
+
} catch (error) {
|
|
931
|
+
logWarn(
|
|
932
|
+
"agent_turn_session_summary_parse_failed",
|
|
933
|
+
{},
|
|
934
|
+
{
|
|
935
|
+
"app.state.key": key,
|
|
936
|
+
"exception.message": error instanceof Error ? error.message : String(error)
|
|
937
|
+
},
|
|
938
|
+
"Skipping an invalid turn-session summary index entry"
|
|
939
|
+
);
|
|
940
|
+
continue;
|
|
941
|
+
}
|
|
942
|
+
const summaryKey = `${summary.conversationId}:${summary.sessionId}`;
|
|
943
|
+
if (!summaries.has(summaryKey)) {
|
|
944
|
+
summaries.set(summaryKey, summary);
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
return [...summaries.values()].sort(
|
|
948
|
+
(left, right) => right.updatedAtMs - left.updatedAtMs
|
|
949
|
+
);
|
|
950
|
+
}
|
|
951
|
+
async function listAgentTurnSessionSummariesForConversation(conversationId) {
|
|
952
|
+
const stateAdapter = getStateAdapter();
|
|
953
|
+
const summaries = await listBoundedAgentTurnSessionSummariesForConversation(conversationId);
|
|
954
|
+
if (summaries.length > 0) {
|
|
955
|
+
return summaries;
|
|
956
|
+
}
|
|
957
|
+
return (await readAgentTurnSessionSummariesFromIndex(
|
|
958
|
+
AGENT_TURN_SESSION_INDEX_KEY,
|
|
959
|
+
stateAdapter
|
|
960
|
+
)).filter((summary) => summary.conversationId === conversationId);
|
|
961
|
+
}
|
|
962
|
+
async function listBoundedAgentTurnSessionSummariesForConversation(conversationId) {
|
|
963
|
+
return readAgentTurnSessionSummariesFromIndex(
|
|
964
|
+
agentTurnSessionConversationIndexKey(conversationId),
|
|
965
|
+
getStateAdapter()
|
|
966
|
+
);
|
|
967
|
+
}
|
|
968
|
+
async function abandonAgentTurnSessionRecord(args) {
|
|
969
|
+
const existing = await getAgentTurnSessionRecord(
|
|
970
|
+
args.conversationId,
|
|
971
|
+
args.sessionId
|
|
972
|
+
);
|
|
973
|
+
if (!existing || existing.state === "completed" || existing.state === "failed" || existing.state === "abandoned") {
|
|
974
|
+
return void 0;
|
|
975
|
+
}
|
|
976
|
+
return await updateAgentTurnSessionState({
|
|
977
|
+
existing,
|
|
978
|
+
state: "abandoned",
|
|
979
|
+
errorMessage: args.errorMessage ?? existing.errorMessage
|
|
980
|
+
});
|
|
981
|
+
}
|
|
982
|
+
async function failAgentTurnSessionRecord(args) {
|
|
983
|
+
const existing = await getAgentTurnSessionRecord(
|
|
984
|
+
args.conversationId,
|
|
985
|
+
args.sessionId
|
|
986
|
+
);
|
|
987
|
+
if (!existing || existing.state === "completed" || existing.state === "failed" || existing.state === "abandoned" || existing.version !== args.expectedVersion) {
|
|
988
|
+
return void 0;
|
|
989
|
+
}
|
|
990
|
+
return await updateAgentTurnSessionState({
|
|
991
|
+
existing,
|
|
992
|
+
state: "failed",
|
|
993
|
+
errorMessage: args.errorMessage ?? existing.errorMessage
|
|
994
|
+
});
|
|
995
|
+
}
|
|
996
|
+
|
|
148
997
|
// src/chat/services/turn-limit.ts
|
|
149
998
|
var TurnSliceLimitExceededError = class extends Error {
|
|
150
999
|
constructor(maxSlices) {
|
|
@@ -162,9 +1011,9 @@ function logSessionRecordError(error, eventName, args, attributes, message) {
|
|
|
162
1011
|
error,
|
|
163
1012
|
eventName,
|
|
164
1013
|
{
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
1014
|
+
messageConversationId: args.logContext.threadId,
|
|
1015
|
+
userId: args.logContext.actorId,
|
|
1016
|
+
destinationName: args.logContext.channelId,
|
|
168
1017
|
runId: args.logContext.runId,
|
|
169
1018
|
assistantUserName: args.logContext.assistantUserName,
|
|
170
1019
|
modelId: args.modelId
|
|
@@ -319,7 +1168,6 @@ async function completeDeliveredTurn(args) {
|
|
|
319
1168
|
sliceId: args.sliceId,
|
|
320
1169
|
allMessages: args.messages,
|
|
321
1170
|
loadedSkillNames: args.loadedSkillNames,
|
|
322
|
-
logContext: args.logContext,
|
|
323
1171
|
modelId: args.modelId,
|
|
324
1172
|
actor: args.actor,
|
|
325
1173
|
reasoningLevel: args.reasoningLevel,
|
|
@@ -530,6 +1378,122 @@ async function persistYieldSessionRecord(args) {
|
|
|
530
1378
|
}
|
|
531
1379
|
}
|
|
532
1380
|
|
|
1381
|
+
// src/chat/state/conversation.ts
|
|
1382
|
+
function defaultConversationState() {
|
|
1383
|
+
const nowMs = Date.now();
|
|
1384
|
+
return {
|
|
1385
|
+
schemaVersion: 1,
|
|
1386
|
+
messages: [],
|
|
1387
|
+
compactions: [],
|
|
1388
|
+
backfill: {},
|
|
1389
|
+
processing: {},
|
|
1390
|
+
stats: {
|
|
1391
|
+
estimatedContextTokens: 0,
|
|
1392
|
+
totalMessageCount: 0,
|
|
1393
|
+
compactedMessageCount: 0,
|
|
1394
|
+
updatedAtMs: nowMs
|
|
1395
|
+
},
|
|
1396
|
+
vision: {
|
|
1397
|
+
byFileId: {}
|
|
1398
|
+
}
|
|
1399
|
+
};
|
|
1400
|
+
}
|
|
1401
|
+
function coercePendingAuthState(value) {
|
|
1402
|
+
if (!isRecord(value)) {
|
|
1403
|
+
return void 0;
|
|
1404
|
+
}
|
|
1405
|
+
const kind = value.kind;
|
|
1406
|
+
const provider = toOptionalString(value.provider);
|
|
1407
|
+
const actorId = toOptionalString(value.actorId);
|
|
1408
|
+
const authSessionId = toOptionalString(value.authSessionId);
|
|
1409
|
+
const scope = toOptionalString(value.scope);
|
|
1410
|
+
const sessionId = toOptionalString(value.sessionId);
|
|
1411
|
+
const linkSentAtMs = toOptionalNumber(value.linkSentAtMs);
|
|
1412
|
+
if (kind !== "mcp" && kind !== "plugin" || !provider || !actorId || kind === "mcp" && !authSessionId || !sessionId || typeof linkSentAtMs !== "number") {
|
|
1413
|
+
return void 0;
|
|
1414
|
+
}
|
|
1415
|
+
const base = {
|
|
1416
|
+
provider,
|
|
1417
|
+
actorId,
|
|
1418
|
+
...scope ? { scope } : {},
|
|
1419
|
+
sessionId,
|
|
1420
|
+
linkSentAtMs
|
|
1421
|
+
};
|
|
1422
|
+
return kind === "mcp" ? { ...base, authSessionId, kind } : { ...base, kind };
|
|
1423
|
+
}
|
|
1424
|
+
function coerceThreadConversationState(value) {
|
|
1425
|
+
if (!isRecord(value)) {
|
|
1426
|
+
return defaultConversationState();
|
|
1427
|
+
}
|
|
1428
|
+
const root = value;
|
|
1429
|
+
const rawConversation = isRecord(root.conversation) ? root.conversation : {};
|
|
1430
|
+
const base = defaultConversationState();
|
|
1431
|
+
const messages = [];
|
|
1432
|
+
const compactions = [];
|
|
1433
|
+
const rawBackfill = isRecord(rawConversation.backfill) ? rawConversation.backfill : {};
|
|
1434
|
+
const backfill = {
|
|
1435
|
+
completedAtMs: toOptionalNumber(rawBackfill.completedAtMs),
|
|
1436
|
+
source: rawBackfill.source === "recent_messages" || rawBackfill.source === "thread_fetch" ? rawBackfill.source : void 0
|
|
1437
|
+
};
|
|
1438
|
+
const rawProcessing = isRecord(rawConversation.processing) ? rawConversation.processing : {};
|
|
1439
|
+
const processing = {
|
|
1440
|
+
activeTurnId: toOptionalString(rawProcessing.activeTurnId),
|
|
1441
|
+
lastCompletedAtMs: toOptionalNumber(rawProcessing.lastCompletedAtMs),
|
|
1442
|
+
pendingAuth: coercePendingAuthState(rawProcessing.pendingAuth)
|
|
1443
|
+
};
|
|
1444
|
+
const rawStats = isRecord(rawConversation.stats) ? rawConversation.stats : {};
|
|
1445
|
+
const stats = {
|
|
1446
|
+
estimatedContextTokens: toOptionalNumber(rawStats.estimatedContextTokens) ?? base.stats.estimatedContextTokens,
|
|
1447
|
+
totalMessageCount: toOptionalNumber(rawStats.totalMessageCount) ?? messages.length,
|
|
1448
|
+
compactedMessageCount: toOptionalNumber(rawStats.compactedMessageCount) ?? 0,
|
|
1449
|
+
updatedAtMs: toOptionalNumber(rawStats.updatedAtMs) ?? base.stats.updatedAtMs
|
|
1450
|
+
};
|
|
1451
|
+
const rawVision = isRecord(rawConversation.vision) ? rawConversation.vision : {};
|
|
1452
|
+
const rawVisionByFileId = isRecord(rawVision.byFileId) ? rawVision.byFileId : {};
|
|
1453
|
+
const byFileId = {};
|
|
1454
|
+
for (const [fileId, value2] of Object.entries(rawVisionByFileId)) {
|
|
1455
|
+
if (typeof fileId !== "string" || fileId.trim().length === 0) continue;
|
|
1456
|
+
if (!isRecord(value2)) continue;
|
|
1457
|
+
const summary = toOptionalString(value2.summary);
|
|
1458
|
+
const analyzedAtMs = toOptionalNumber(value2.analyzedAtMs);
|
|
1459
|
+
if (!summary || !analyzedAtMs) continue;
|
|
1460
|
+
byFileId[fileId] = {
|
|
1461
|
+
summary,
|
|
1462
|
+
analyzedAtMs
|
|
1463
|
+
};
|
|
1464
|
+
}
|
|
1465
|
+
return {
|
|
1466
|
+
schemaVersion: 1,
|
|
1467
|
+
messages,
|
|
1468
|
+
compactions,
|
|
1469
|
+
backfill,
|
|
1470
|
+
processing,
|
|
1471
|
+
stats,
|
|
1472
|
+
vision: {
|
|
1473
|
+
backfillCompletedAtMs: toOptionalNumber(rawVision.backfillCompletedAtMs),
|
|
1474
|
+
byFileId
|
|
1475
|
+
}
|
|
1476
|
+
};
|
|
1477
|
+
}
|
|
1478
|
+
function buildConversationStatePatch(conversation) {
|
|
1479
|
+
const {
|
|
1480
|
+
compactions: _compactions,
|
|
1481
|
+
messages: _messages,
|
|
1482
|
+
...scratch
|
|
1483
|
+
} = conversation;
|
|
1484
|
+
return {
|
|
1485
|
+
conversation: {
|
|
1486
|
+
...scratch,
|
|
1487
|
+
schemaVersion: 1,
|
|
1488
|
+
stats: {
|
|
1489
|
+
...conversation.stats,
|
|
1490
|
+
totalMessageCount: conversation.messages.length,
|
|
1491
|
+
updatedAtMs: Date.now()
|
|
1492
|
+
}
|
|
1493
|
+
}
|
|
1494
|
+
};
|
|
1495
|
+
}
|
|
1496
|
+
|
|
533
1497
|
// src/chat/services/context-budget.ts
|
|
534
1498
|
var COMPACTION_TRIGGER_INPUT_RATIO = 0.75;
|
|
535
1499
|
var COMPACTION_OUTPUT_RESERVE_RATIO = 0.25;
|
|
@@ -804,9 +1768,9 @@ async function summarizeConversationChunk(messages, conversation, context, deps)
|
|
|
804
1768
|
logWarn(
|
|
805
1769
|
"conversation_compaction_summary_failed",
|
|
806
1770
|
{
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
1771
|
+
messageConversationId: context.threadId,
|
|
1772
|
+
userId: context.actorId,
|
|
1773
|
+
destinationName: context.channelId,
|
|
810
1774
|
runId: context.runId,
|
|
811
1775
|
assistantUserName: botConfig.userName,
|
|
812
1776
|
modelId: botConfig.fastModelId
|
|
@@ -923,10 +1887,10 @@ function getConversationMessageSlackTs(message) {
|
|
|
923
1887
|
|
|
924
1888
|
// src/chat/conversations/messages.ts
|
|
925
1889
|
import { createHash } from "crypto";
|
|
926
|
-
import { isDeepStrictEqual as
|
|
1890
|
+
import { isDeepStrictEqual as isDeepStrictEqual3 } from "util";
|
|
927
1891
|
|
|
928
1892
|
// src/chat/conversations/message-summaries.ts
|
|
929
|
-
import { isDeepStrictEqual } from "util";
|
|
1893
|
+
import { isDeepStrictEqual as isDeepStrictEqual2 } from "util";
|
|
930
1894
|
function projectConversationMessageSummaries(events) {
|
|
931
1895
|
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
932
1896
|
const data = events[index]?.data;
|
|
@@ -940,7 +1904,7 @@ async function persistConversationMessageSummaries(args) {
|
|
|
940
1904
|
const eventStore = getConversationEventStore();
|
|
941
1905
|
const history = await eventStore.loadMessageHistory(args.conversationId);
|
|
942
1906
|
const existing = history.compaction ? projectConversationMessageSummaries([history.compaction]) : [];
|
|
943
|
-
if (
|
|
1907
|
+
if (isDeepStrictEqual2(existing, args.conversation.compactions)) {
|
|
944
1908
|
return;
|
|
945
1909
|
}
|
|
946
1910
|
const liveMessageIds = new Set(
|
|
@@ -949,10 +1913,7 @@ async function persistConversationMessageSummaries(args) {
|
|
|
949
1913
|
const historyFromSeq = history.events.find(
|
|
950
1914
|
(event) => event.data.type === "message" && liveMessageIds.has(event.data.messageId)
|
|
951
1915
|
)?.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
|
-
);
|
|
1916
|
+
const nextHistoryFromSeq = historyFromSeq ?? Math.max(history.historyFromSeq, (history.events.at(-1)?.seq ?? -1) + 1);
|
|
956
1917
|
await eventStore.append(args.conversationId, [
|
|
957
1918
|
{
|
|
958
1919
|
data: {
|
|
@@ -983,9 +1944,9 @@ function missingBaselineError(event) {
|
|
|
983
1944
|
`Message event ${event.data.type} at seq ${event.seq} references ${event.data.messageId} before message`
|
|
984
1945
|
);
|
|
985
1946
|
}
|
|
986
|
-
function projectConversationMessages(
|
|
1947
|
+
function projectConversationMessages(history) {
|
|
987
1948
|
const byId = /* @__PURE__ */ new Map();
|
|
988
|
-
for (const event of events) {
|
|
1949
|
+
for (const event of history.events) {
|
|
989
1950
|
if (!isMessageEvent(event)) continue;
|
|
990
1951
|
const data = event.data;
|
|
991
1952
|
const current = byId.get(data.messageId);
|
|
@@ -1002,7 +1963,7 @@ function projectConversationMessages(events, options = {}) {
|
|
|
1002
1963
|
`Duplicate message event for ${data.messageId} at seq ${event.seq}`
|
|
1003
1964
|
);
|
|
1004
1965
|
if (data.type === "message_updated" && !current) {
|
|
1005
|
-
if (
|
|
1966
|
+
if (history.historyFromSeq > 0) continue;
|
|
1006
1967
|
throw missingBaselineError(event);
|
|
1007
1968
|
}
|
|
1008
1969
|
byId.set(data.messageId, {
|
|
@@ -1015,7 +1976,7 @@ function projectConversationMessages(events, options = {}) {
|
|
|
1015
1976
|
continue;
|
|
1016
1977
|
}
|
|
1017
1978
|
if (!current) {
|
|
1018
|
-
if (
|
|
1979
|
+
if (history.historyFromSeq > 0) continue;
|
|
1019
1980
|
throw missingBaselineError(event);
|
|
1020
1981
|
}
|
|
1021
1982
|
current.repliedAtMs ??= event.createdAtMs;
|
|
@@ -1063,10 +2024,7 @@ async function hydrateConversationMessages(args) {
|
|
|
1063
2024
|
args.conversationId
|
|
1064
2025
|
);
|
|
1065
2026
|
args.conversation.compactions = history.compaction ? projectConversationMessageSummaries([history.compaction]) : [];
|
|
1066
|
-
|
|
1067
|
-
args.conversation.messages = projectConversationMessages(history.events, {
|
|
1068
|
-
historyFromSeq
|
|
1069
|
-
});
|
|
2027
|
+
args.conversation.messages = projectConversationMessages(history);
|
|
1070
2028
|
updateConversationStats(args.conversation);
|
|
1071
2029
|
}
|
|
1072
2030
|
async function persistConversationMessages(args) {
|
|
@@ -1075,7 +2033,7 @@ async function persistConversationMessages(args) {
|
|
|
1075
2033
|
args.conversationId
|
|
1076
2034
|
);
|
|
1077
2035
|
const existingMessages = new Map(
|
|
1078
|
-
projectConversationMessages(history
|
|
2036
|
+
projectConversationMessages(history).map((message) => [
|
|
1079
2037
|
message.id,
|
|
1080
2038
|
message
|
|
1081
2039
|
])
|
|
@@ -1084,7 +2042,7 @@ async function persistConversationMessages(args) {
|
|
|
1084
2042
|
const events = args.conversation.messages.flatMap((message) => {
|
|
1085
2043
|
const stored = toStoredConversationMessage(message);
|
|
1086
2044
|
const existing = existingMessages.get(stored.messageId);
|
|
1087
|
-
const changed = existing !== void 0 && !
|
|
2045
|
+
const changed = existing !== void 0 && !isDeepStrictEqual3(toStoredConversationMessage(existing), stored);
|
|
1088
2046
|
const messageEvent = existing ? changed ? [
|
|
1089
2047
|
{
|
|
1090
2048
|
idempotencyKey: `message:${stored.messageId}:update:${messageStateKey(stored)}`,
|
|
@@ -1378,6 +2336,9 @@ function createChannelConfigurationService(storage) {
|
|
|
1378
2336
|
};
|
|
1379
2337
|
}
|
|
1380
2338
|
|
|
2339
|
+
// src/chat/state/ttl.ts
|
|
2340
|
+
var JUNIOR_THREAD_STATE_TTL_MS = 7 * 24 * 60 * 60 * 1e3;
|
|
2341
|
+
|
|
1381
2342
|
// src/chat/runtime/thread-state.ts
|
|
1382
2343
|
function threadStateKey(threadId) {
|
|
1383
2344
|
return `thread-state:${threadId}`;
|
|
@@ -1571,6 +2532,15 @@ function isPendingAuthLatestRequest(conversation, pendingAuth) {
|
|
|
1571
2532
|
}
|
|
1572
2533
|
|
|
1573
2534
|
export {
|
|
2535
|
+
loadProjection,
|
|
2536
|
+
loadConversationProjection,
|
|
2537
|
+
openConversationProjection,
|
|
2538
|
+
loadConnectedMcpProviders,
|
|
2539
|
+
commitMessages,
|
|
2540
|
+
recordMcpProviderConnected,
|
|
2541
|
+
recordAuthorizationRequested,
|
|
2542
|
+
recordAuthorizationCompleted,
|
|
2543
|
+
recordToolExecutionStarted,
|
|
1574
2544
|
buildDeterministicTurnId,
|
|
1575
2545
|
buildDeterministicAssistantMessageId,
|
|
1576
2546
|
ConversationTurnBoundaryError,
|
|
@@ -1585,6 +2555,12 @@ export {
|
|
|
1585
2555
|
markTurnClosed,
|
|
1586
2556
|
markTurnCompleted,
|
|
1587
2557
|
markTurnFailed,
|
|
2558
|
+
getAgentTurnSessionRecord,
|
|
2559
|
+
getAgentTurnSessionRecordForResume,
|
|
2560
|
+
recordAgentTurnSessionSummary,
|
|
2561
|
+
listAgentTurnSessionSummariesForConversation,
|
|
2562
|
+
abandonAgentTurnSessionRecord,
|
|
2563
|
+
failAgentTurnSessionRecord,
|
|
1588
2564
|
persistWithRetry,
|
|
1589
2565
|
TurnSliceLimitExceededError,
|
|
1590
2566
|
buildTurnLimitResponse,
|
|
@@ -1596,11 +2572,13 @@ export {
|
|
|
1596
2572
|
persistContinuationSessionRecord,
|
|
1597
2573
|
persistYieldSessionRecord,
|
|
1598
2574
|
getInterruptionMarker,
|
|
2575
|
+
JUNIOR_THREAD_STATE_TTL_MS,
|
|
1599
2576
|
canReusePendingAuthLink,
|
|
1600
2577
|
getConversationPendingAuth,
|
|
1601
2578
|
clearPendingAuth,
|
|
1602
2579
|
abandonReplacedPendingAuth,
|
|
1603
2580
|
isPendingAuthLatestRequest,
|
|
2581
|
+
coerceThreadConversationState,
|
|
1604
2582
|
persistConversationMessageSummaries,
|
|
1605
2583
|
estimateTextTokens,
|
|
1606
2584
|
getAgentContextCompactionTriggerTokens,
|