@agent-native/core 0.196.0 → 0.196.1-nightly-20260928200103
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/corpus/README.md +1 -1
- package/corpus/templates/clips/actions/create-recording.ts +1 -1
- package/corpus/templates/clips/actions/lib/auto-title-candidates.ts +42 -0
- package/corpus/templates/clips/actions/lib/create-recording-schema.ts +1 -1
- package/corpus/templates/clips/actions/lib/transcript-text.ts +16 -0
- package/corpus/templates/clips/actions/list-ai-requests.ts +49 -31
- package/corpus/templates/clips/actions/list-recordings.ts +2 -4
- package/corpus/templates/clips/actions/list-transactional-email-ai-requests.ts +12 -6
- package/corpus/templates/clips/actions/regenerate-title.ts +4 -1
- package/corpus/templates/clips/actions/request-transcript.ts +1 -1
- package/corpus/templates/clips/actions/save-browser-transcript.ts +1 -1
- package/corpus/templates/clips/app/components/editable-recording-title.tsx +1 -1
- package/corpus/templates/clips/app/components/library/recording-card.tsx +1 -1
- package/corpus/templates/clips/app/hooks/use-auto-title.ts +194 -219
- package/corpus/templates/clips/app/routes/_app.r.$recordingId.tsx +2 -1
- package/corpus/templates/clips/app/routes/share.$shareId.tsx +1 -1
- package/corpus/templates/clips/server/lib/transactional-email-store.ts +42 -2
- package/corpus/templates/clips/shared/ai-request-status.ts +6 -0
- package/corpus/templates/clips/{actions/lib → shared}/title-source.ts +9 -2
- package/dist/agent/production-agent.js +2 -0
- package/dist/agent/run-manager.d.ts +2 -0
- package/dist/agent/run-manager.js +9 -2
- package/dist/agent/thread-data-builder.d.ts +1 -0
- package/dist/agent/thread-data-builder.js +148 -0
- package/dist/automation/index.d.ts +1 -1
- package/dist/chat-threads/store.d.ts +1 -0
- package/dist/chat-threads/store.js +244 -21
- package/dist/client/AgentKitAssistantChat.js +23 -12
- package/dist/client/chat/agentkit-agent-native.js +409 -8
- package/dist/client/chat/agentkit-protocol.js +3 -0
- package/dist/collab/awareness.d.ts +2 -2
- package/dist/file-upload/actions/upload-image.d.ts +1 -1
- package/dist/notifications/routes.d.ts +3 -3
- package/dist/observability/routes.d.ts +2 -2
- package/dist/resources/handlers.d.ts +1 -1
- package/dist/server/agent-chat-plugin.d.ts +2 -0
- package/dist/server/agent-chat-plugin.js +12 -7
- package/dist/server/agent-engine-api-key-route.d.ts +1 -1
- package/dist/server/agent-engine-ollama-models-route.d.ts +2 -2
- package/dist/server/realtime-token.d.ts +1 -1
- package/package.json +1 -1
|
@@ -7657,6 +7657,8 @@ export function createProductionAgentHandler(options) {
|
|
|
7657
7657
|
backgroundFunction: runsInBackgroundFunction,
|
|
7658
7658
|
noProgressTimeoutMs: options.runNoProgressTimeoutMs,
|
|
7659
7659
|
turnId: effectiveTurnId,
|
|
7660
|
+
agentKitApprovalContinuation: internalContinuation &&
|
|
7661
|
+
Boolean(approvedToolCallsForExecution?.length),
|
|
7660
7662
|
turnInitiator,
|
|
7661
7663
|
parentId: requestParentId,
|
|
7662
7664
|
waitUntil: getRequestRunContext()?.waitUntil,
|
|
@@ -6,6 +6,7 @@ export interface ActiveRun {
|
|
|
6
6
|
threadId: string;
|
|
7
7
|
parentId?: string | null;
|
|
8
8
|
turnId: string;
|
|
9
|
+
agentKitApprovalContinuation?: boolean;
|
|
9
10
|
events: RunEvent[];
|
|
10
11
|
status: RunStatus;
|
|
11
12
|
subscribers: Set<(event: RunEvent) => void>;
|
|
@@ -54,6 +55,7 @@ export interface StartRunOptions {
|
|
|
54
55
|
softTimeoutMs?: number;
|
|
55
56
|
useHostedSoftTimeoutDefault?: boolean;
|
|
56
57
|
turnId?: string;
|
|
58
|
+
agentKitApprovalContinuation?: boolean;
|
|
57
59
|
parentId?: string | null;
|
|
58
60
|
backgroundFunction?: boolean;
|
|
59
61
|
noProgressTimeoutMs?: number;
|
|
@@ -486,6 +486,9 @@ export function startRun(runId, threadId, runFn, onComplete, options) {
|
|
|
486
486
|
threadId,
|
|
487
487
|
...(options?.parentId !== undefined ? { parentId: options.parentId } : {}),
|
|
488
488
|
turnId: options?.turnId ?? runId,
|
|
489
|
+
...(options?.agentKitApprovalContinuation
|
|
490
|
+
? { agentKitApprovalContinuation: true }
|
|
491
|
+
: {}),
|
|
489
492
|
events: [],
|
|
490
493
|
status: "running",
|
|
491
494
|
subscribers: new Set(),
|
|
@@ -1795,7 +1798,8 @@ export async function getActiveRunForThreadAsync(threadId) {
|
|
|
1795
1798
|
const memRun = getActiveRunForThread(threadId);
|
|
1796
1799
|
if (memRun && (memRun.status === "running" || memRun.events.length > 0)) {
|
|
1797
1800
|
const sqlSnapshot = await fetchRunThreadSnapshot(memRun.runId, threadId);
|
|
1798
|
-
if (
|
|
1801
|
+
if (memRun.status !== "running" &&
|
|
1802
|
+
(!sqlSnapshot || sqlSnapshot.status === "running")) {
|
|
1799
1803
|
const successor = await fetchNewerNonTerminalRunForSameTurn(threadId, memRun);
|
|
1800
1804
|
if (successor) {
|
|
1801
1805
|
return {
|
|
@@ -1813,7 +1817,10 @@ export async function getActiveRunForThreadAsync(threadId) {
|
|
|
1813
1817
|
};
|
|
1814
1818
|
}
|
|
1815
1819
|
}
|
|
1816
|
-
const
|
|
1820
|
+
const sqlStatus = sqlSnapshot?.status === "running" && memRun.status !== "running"
|
|
1821
|
+
? memRun.status
|
|
1822
|
+
: (sqlSnapshot?.status ?? memRun.status);
|
|
1823
|
+
const status = legacyWireRunStatus(sqlStatus);
|
|
1817
1824
|
const heartbeatAt = status === "running"
|
|
1818
1825
|
? Date.now()
|
|
1819
1826
|
: (sqlSnapshot?.heartbeatAt ?? memRun.startedAt);
|
|
@@ -107,6 +107,7 @@ export declare function foldAssistantTurn(repo: any, assistantMsg: AssistantMess
|
|
|
107
107
|
turnId?: string;
|
|
108
108
|
runId?: string;
|
|
109
109
|
parentId?: string | null;
|
|
110
|
+
agentKitOwnsContinuation?: boolean;
|
|
110
111
|
}): any;
|
|
111
112
|
export declare function normalizeThreadTitle(value: unknown): string;
|
|
112
113
|
export declare function extractThreadMeta(repo: any): {
|
|
@@ -1039,6 +1039,149 @@ export function claimQueuedMessage(repo, messageId) {
|
|
|
1039
1039
|
normalized[CLAIMED_QUEUED_MESSAGE_IDS_KEY] = [...claimed, messageId].slice(-MAX_CLAIMED_QUEUED_MESSAGE_IDS);
|
|
1040
1040
|
return pruneClaimedQueuedMessages(normalized);
|
|
1041
1041
|
}
|
|
1042
|
+
function snapshotEntryId(entry, kind) {
|
|
1043
|
+
if (!entry || typeof entry !== "object")
|
|
1044
|
+
return undefined;
|
|
1045
|
+
if (kind === "widget") {
|
|
1046
|
+
const messageId = entry.messageId;
|
|
1047
|
+
const widgetId = entry.widget?.id;
|
|
1048
|
+
return typeof messageId === "string" && typeof widgetId === "string"
|
|
1049
|
+
? JSON.stringify([messageId, widgetId])
|
|
1050
|
+
: undefined;
|
|
1051
|
+
}
|
|
1052
|
+
return typeof entry.id === "string" ? entry.id : undefined;
|
|
1053
|
+
}
|
|
1054
|
+
function snapshotMessageRunIds(agentKit) {
|
|
1055
|
+
const runIds = new Map();
|
|
1056
|
+
for (const event of Array.isArray(agentKit?.events) ? agentKit.events : []) {
|
|
1057
|
+
if ((event?.type === "message.created" ||
|
|
1058
|
+
event?.type === "message.completed") &&
|
|
1059
|
+
event.message?.role === "assistant" &&
|
|
1060
|
+
typeof event.message.id === "string" &&
|
|
1061
|
+
typeof event.runId === "string") {
|
|
1062
|
+
runIds.set(event.message.id, event.runId);
|
|
1063
|
+
}
|
|
1064
|
+
}
|
|
1065
|
+
for (const run of Array.isArray(agentKit?.runs) ? agentKit.runs : []) {
|
|
1066
|
+
if (typeof run?.activeMessageId === "string" &&
|
|
1067
|
+
typeof run.id === "string") {
|
|
1068
|
+
runIds.set(run.activeMessageId, run.id);
|
|
1069
|
+
}
|
|
1070
|
+
}
|
|
1071
|
+
return runIds;
|
|
1072
|
+
}
|
|
1073
|
+
function snapshotAssistantTextKey(message, runIds) {
|
|
1074
|
+
const runId = (typeof message?.id === "string" ? runIds.get(message.id) : undefined) ??
|
|
1075
|
+
message?.metadata?.runId ??
|
|
1076
|
+
message?.metadata?.custom?.runId;
|
|
1077
|
+
if (message?.role !== "assistant" ||
|
|
1078
|
+
typeof runId !== "string" ||
|
|
1079
|
+
!Array.isArray(message.parts) ||
|
|
1080
|
+
message.parts.length === 0 ||
|
|
1081
|
+
!message.parts.every((part) => part?.type === "text" && typeof part.text === "string")) {
|
|
1082
|
+
return undefined;
|
|
1083
|
+
}
|
|
1084
|
+
return JSON.stringify([
|
|
1085
|
+
runId,
|
|
1086
|
+
message.parts.map((part) => [part.text, part.format ?? null]),
|
|
1087
|
+
]);
|
|
1088
|
+
}
|
|
1089
|
+
function preferIncomingSnapshotEntry(kind, existing, incoming) {
|
|
1090
|
+
if (kind === "message") {
|
|
1091
|
+
const rank = (message) => message.status === "complete" ? 2 : message.status === "error" ? 1 : 0;
|
|
1092
|
+
if (rank(existing) !== rank(incoming))
|
|
1093
|
+
return rank(incoming) > rank(existing);
|
|
1094
|
+
const partCount = (message) => Array.isArray(message.parts) ? message.parts.length : 0;
|
|
1095
|
+
if (partCount(existing) !== partCount(incoming)) {
|
|
1096
|
+
return partCount(incoming) > partCount(existing);
|
|
1097
|
+
}
|
|
1098
|
+
const textLength = (message) => (Array.isArray(message.parts) ? message.parts : []).reduce((total, part) => total + (typeof part?.text === "string" ? part.text.length : 0), 0);
|
|
1099
|
+
return textLength(incoming) > textLength(existing);
|
|
1100
|
+
}
|
|
1101
|
+
if (kind === "toolCall") {
|
|
1102
|
+
return existing.status === "running" && incoming.status !== "running";
|
|
1103
|
+
}
|
|
1104
|
+
return (existing.widget?.state === "active" && incoming.widget?.state !== "active");
|
|
1105
|
+
}
|
|
1106
|
+
function mergeAgentKitHistoryArray(existing, incoming, kind, existingMessageRunIds, incomingMessageRunIds) {
|
|
1107
|
+
if (!Array.isArray(existing) && !Array.isArray(incoming))
|
|
1108
|
+
return undefined;
|
|
1109
|
+
const merged = Array.isArray(existing) ? [...existing] : [];
|
|
1110
|
+
const positions = new Map();
|
|
1111
|
+
const assistantTextPositions = new Map();
|
|
1112
|
+
merged.forEach((entry, index) => {
|
|
1113
|
+
const id = snapshotEntryId(entry, kind);
|
|
1114
|
+
if (id && !positions.has(id))
|
|
1115
|
+
positions.set(id, index);
|
|
1116
|
+
if (kind === "message") {
|
|
1117
|
+
const key = snapshotAssistantTextKey(entry, existingMessageRunIds);
|
|
1118
|
+
if (key) {
|
|
1119
|
+
assistantTextPositions.set(key, [
|
|
1120
|
+
...(assistantTextPositions.get(key) ?? []),
|
|
1121
|
+
index,
|
|
1122
|
+
]);
|
|
1123
|
+
}
|
|
1124
|
+
}
|
|
1125
|
+
});
|
|
1126
|
+
const matchedAssistantTextPositions = new Set();
|
|
1127
|
+
for (const entry of Array.isArray(incoming) ? incoming : []) {
|
|
1128
|
+
const id = snapshotEntryId(entry, kind);
|
|
1129
|
+
const idIndex = id ? positions.get(id) : undefined;
|
|
1130
|
+
const textKey = kind === "message"
|
|
1131
|
+
? snapshotAssistantTextKey(entry, incomingMessageRunIds)
|
|
1132
|
+
: undefined;
|
|
1133
|
+
const textIndex = idIndex === undefined && textKey
|
|
1134
|
+
? assistantTextPositions
|
|
1135
|
+
.get(textKey)
|
|
1136
|
+
?.find((candidate) => !matchedAssistantTextPositions.has(candidate))
|
|
1137
|
+
: undefined;
|
|
1138
|
+
const index = idIndex ?? textIndex;
|
|
1139
|
+
if (index === undefined) {
|
|
1140
|
+
if (id)
|
|
1141
|
+
positions.set(id, merged.length);
|
|
1142
|
+
merged.push(entry);
|
|
1143
|
+
}
|
|
1144
|
+
else {
|
|
1145
|
+
if (kind === "message")
|
|
1146
|
+
matchedAssistantTextPositions.add(index);
|
|
1147
|
+
if (id && idIndex === undefined)
|
|
1148
|
+
positions.set(id, index);
|
|
1149
|
+
const preferIncoming = preferIncomingSnapshotEntry(kind, merged[index], entry);
|
|
1150
|
+
const preferCurrentMessageId = kind === "message" &&
|
|
1151
|
+
textIndex !== undefined &&
|
|
1152
|
+
!preferIncomingSnapshotEntry(kind, entry, merged[index]);
|
|
1153
|
+
if (preferIncoming || preferCurrentMessageId) {
|
|
1154
|
+
merged[index] = entry;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
}
|
|
1158
|
+
return merged;
|
|
1159
|
+
}
|
|
1160
|
+
function mergeAgentKitHistory(existing, incoming) {
|
|
1161
|
+
if (!existing ||
|
|
1162
|
+
typeof existing !== "object" ||
|
|
1163
|
+
Array.isArray(existing) ||
|
|
1164
|
+
!incoming ||
|
|
1165
|
+
typeof incoming !== "object" ||
|
|
1166
|
+
Array.isArray(incoming)) {
|
|
1167
|
+
return incoming ?? existing;
|
|
1168
|
+
}
|
|
1169
|
+
const previous = existing;
|
|
1170
|
+
const next = incoming;
|
|
1171
|
+
const merged = { ...previous, ...next };
|
|
1172
|
+
const existingMessageRunIds = snapshotMessageRunIds(previous);
|
|
1173
|
+
const incomingMessageRunIds = snapshotMessageRunIds(next);
|
|
1174
|
+
for (const [key, kind] of [
|
|
1175
|
+
["messages", "message"],
|
|
1176
|
+
["toolCalls", "toolCall"],
|
|
1177
|
+
["widgets", "widget"],
|
|
1178
|
+
]) {
|
|
1179
|
+
const entries = mergeAgentKitHistoryArray(previous[key], next[key], kind, existingMessageRunIds, incomingMessageRunIds);
|
|
1180
|
+
if (entries)
|
|
1181
|
+
merged[key] = entries;
|
|
1182
|
+
}
|
|
1183
|
+
return merged;
|
|
1184
|
+
}
|
|
1042
1185
|
function pruneClaimedQueuedMessages(repo) {
|
|
1043
1186
|
if (!Array.isArray(repo?.queuedMessages))
|
|
1044
1187
|
return repo;
|
|
@@ -1079,6 +1222,9 @@ export function mergeThreadDataForClientSave(existingRepo, incomingRepo, options
|
|
|
1079
1222
|
merged.queuedMessages === undefined) {
|
|
1080
1223
|
merged.queuedMessages = existingNormalized.queuedMessages;
|
|
1081
1224
|
}
|
|
1225
|
+
if (merged.agentKit !== undefined) {
|
|
1226
|
+
merged.agentKit = mergeAgentKitHistory(existingNormalized?.agentKit, merged.agentKit);
|
|
1227
|
+
}
|
|
1082
1228
|
const existingMessages = Array.isArray(existingNormalized?.messages)
|
|
1083
1229
|
? existingNormalized.messages
|
|
1084
1230
|
: null;
|
|
@@ -1509,6 +1655,8 @@ function appendFoldedContent(existing, incoming) {
|
|
|
1509
1655
|
}).content;
|
|
1510
1656
|
}
|
|
1511
1657
|
export function foldAssistantTurn(repo, assistantMsg, options) {
|
|
1658
|
+
if (options.agentKitOwnsContinuation)
|
|
1659
|
+
return repo;
|
|
1512
1660
|
const turnId = options.turnId;
|
|
1513
1661
|
const runId = options.runId;
|
|
1514
1662
|
if (!turnId)
|
|
@@ -139,8 +139,8 @@ export declare function createAutomationCallbackHandler(runtime: AutomationRunti
|
|
|
139
139
|
duplicate?: undefined;
|
|
140
140
|
eventId?: undefined;
|
|
141
141
|
} | {
|
|
142
|
+
error?: undefined;
|
|
142
143
|
accepted: boolean;
|
|
143
144
|
duplicate: boolean;
|
|
144
145
|
eventId: string;
|
|
145
|
-
error?: undefined;
|
|
146
146
|
}>;
|
|
@@ -256,28 +256,253 @@ function normalizeForkSourceSnapshot(source) {
|
|
|
256
256
|
catch {
|
|
257
257
|
return null;
|
|
258
258
|
}
|
|
259
|
-
const
|
|
260
|
-
|
|
261
|
-
: 0;
|
|
262
|
-
if (repoMessageCount <= 0)
|
|
259
|
+
const messageCount = countThreadMessages(parsed, 0);
|
|
260
|
+
if (messageCount <= 0)
|
|
263
261
|
return null;
|
|
264
262
|
return {
|
|
265
263
|
threadData: JSON.stringify(parsed),
|
|
266
264
|
title: typeof source.title === "string" ? source.title : "",
|
|
267
265
|
preview: typeof source.preview === "string" ? source.preview : "",
|
|
268
|
-
messageCount
|
|
266
|
+
messageCount,
|
|
267
|
+
...(typeof source.fromMessageId === "string"
|
|
268
|
+
? { fromMessageId: source.fromMessageId }
|
|
269
|
+
: {}),
|
|
269
270
|
...(Object.prototype.hasOwnProperty.call(source, "scope")
|
|
270
271
|
? { scope: source.scope ?? null }
|
|
271
272
|
: {}),
|
|
272
273
|
};
|
|
273
274
|
}
|
|
275
|
+
function countThreadMessages(value, fallback) {
|
|
276
|
+
const repo = normalizeThreadRepository(value);
|
|
277
|
+
if (!repo || typeof repo !== "object")
|
|
278
|
+
return fallback;
|
|
279
|
+
const repoMessageCount = Array.isArray(repo.messages)
|
|
280
|
+
? repo.messages.length
|
|
281
|
+
: undefined;
|
|
282
|
+
const agentKitMessageCount = Array.isArray(repo.agentKit?.messages)
|
|
283
|
+
? repo.agentKit.messages.length
|
|
284
|
+
: undefined;
|
|
285
|
+
if (repoMessageCount === undefined && agentKitMessageCount === undefined) {
|
|
286
|
+
return fallback;
|
|
287
|
+
}
|
|
288
|
+
return Math.max(repoMessageCount ?? 0, agentKitMessageCount ?? 0);
|
|
289
|
+
}
|
|
290
|
+
function forkThreadData(threadData, forkId, fromMessageId) {
|
|
291
|
+
const parsed = normalizeThreadRepository(JSON.parse(threadData));
|
|
292
|
+
const repository = parsed && typeof parsed === "object" && !Array.isArray(parsed)
|
|
293
|
+
? parsed
|
|
294
|
+
: undefined;
|
|
295
|
+
const agentKit = repository?.agentKit;
|
|
296
|
+
if (!agentKit || typeof agentKit !== "object" || Array.isArray(agentKit)) {
|
|
297
|
+
return threadData;
|
|
298
|
+
}
|
|
299
|
+
const agentKitRecord = agentKit;
|
|
300
|
+
const asRecord = (value) => value && typeof value === "object" && !Array.isArray(value)
|
|
301
|
+
? value
|
|
302
|
+
: undefined;
|
|
303
|
+
const repositoryMessages = Array.isArray(repository.messages)
|
|
304
|
+
? repository.messages
|
|
305
|
+
: [];
|
|
306
|
+
const protocolMessages = Array.isArray(agentKitRecord.messages)
|
|
307
|
+
? agentKitRecord.messages
|
|
308
|
+
: [];
|
|
309
|
+
const messageId = (value) => {
|
|
310
|
+
const outer = asRecord(value);
|
|
311
|
+
const message = asRecord(outer?.message) ?? outer;
|
|
312
|
+
return typeof message?.id === "string" ? message.id : undefined;
|
|
313
|
+
};
|
|
314
|
+
const messageIds = new Set();
|
|
315
|
+
if (fromMessageId) {
|
|
316
|
+
for (const messages of [repositoryMessages, protocolMessages]) {
|
|
317
|
+
const throughIndex = messages.findIndex((message) => messageId(message) === fromMessageId);
|
|
318
|
+
if (throughIndex >= 0) {
|
|
319
|
+
for (const message of messages.slice(0, throughIndex + 1)) {
|
|
320
|
+
const id = messageId(message);
|
|
321
|
+
if (id)
|
|
322
|
+
messageIds.add(id);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
}
|
|
326
|
+
if (!messageIds.has(fromMessageId)) {
|
|
327
|
+
throw new Error(`Unknown message for fork: ${fromMessageId}`);
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
else {
|
|
331
|
+
for (const message of [...repositoryMessages, ...protocolMessages]) {
|
|
332
|
+
const id = messageId(message);
|
|
333
|
+
if (id)
|
|
334
|
+
messageIds.add(id);
|
|
335
|
+
}
|
|
336
|
+
}
|
|
337
|
+
const events = Array.isArray(agentKitRecord.events)
|
|
338
|
+
? agentKitRecord.events
|
|
339
|
+
: [];
|
|
340
|
+
const runs = Array.isArray(agentKitRecord.runs) ? agentKitRecord.runs : [];
|
|
341
|
+
const eventBoundaries = new Map();
|
|
342
|
+
for (const rawEvent of events) {
|
|
343
|
+
const event = asRecord(rawEvent);
|
|
344
|
+
const message = asRecord(event?.message);
|
|
345
|
+
const messageId = typeof event?.messageId === "string"
|
|
346
|
+
? event.messageId
|
|
347
|
+
: typeof message?.id === "string"
|
|
348
|
+
? message.id
|
|
349
|
+
: undefined;
|
|
350
|
+
if (event?.type === "message.completed" &&
|
|
351
|
+
typeof event?.runId === "string" &&
|
|
352
|
+
typeof event.sequence === "number" &&
|
|
353
|
+
messageId &&
|
|
354
|
+
messageIds.has(messageId)) {
|
|
355
|
+
eventBoundaries.set(event.runId, Math.max(eventBoundaries.get(event.runId) ?? 0, event.sequence));
|
|
356
|
+
}
|
|
357
|
+
}
|
|
358
|
+
for (const rawRun of runs) {
|
|
359
|
+
const run = asRecord(rawRun);
|
|
360
|
+
if (typeof run?.id === "string" &&
|
|
361
|
+
typeof run.activeMessageId === "string" &&
|
|
362
|
+
messageIds.has(run.activeMessageId) &&
|
|
363
|
+
typeof run.lastSequence === "number") {
|
|
364
|
+
eventBoundaries.set(run.id, Math.max(eventBoundaries.get(run.id) ?? 0, run.lastSequence));
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
const retainedRunIds = new Set(eventBoundaries.keys());
|
|
368
|
+
const nonterminalRunIds = new Set(runs.flatMap((rawRun) => {
|
|
369
|
+
const run = asRecord(rawRun);
|
|
370
|
+
return typeof run?.id === "string" &&
|
|
371
|
+
(run.status === "queued" ||
|
|
372
|
+
run.status === "running" ||
|
|
373
|
+
run.status === "awaiting_approval" ||
|
|
374
|
+
run.status === "awaiting_input")
|
|
375
|
+
? [run.id]
|
|
376
|
+
: [];
|
|
377
|
+
}));
|
|
378
|
+
const activeMessageIds = new Set(runs.flatMap((rawRun) => {
|
|
379
|
+
const run = asRecord(rawRun);
|
|
380
|
+
return typeof run?.id === "string" &&
|
|
381
|
+
nonterminalRunIds.has(run.id) &&
|
|
382
|
+
typeof run.activeMessageId === "string"
|
|
383
|
+
? [run.activeMessageId]
|
|
384
|
+
: [];
|
|
385
|
+
}));
|
|
386
|
+
const retainedMessageId = (value) => {
|
|
387
|
+
const id = messageId(value);
|
|
388
|
+
return Boolean(id && messageIds.has(id));
|
|
389
|
+
};
|
|
390
|
+
const remapThreadId = (value) => value && typeof value === "object" && !Array.isArray(value)
|
|
391
|
+
? { ...value, threadId: forkId }
|
|
392
|
+
: value;
|
|
393
|
+
return JSON.stringify({
|
|
394
|
+
...repository,
|
|
395
|
+
...(Array.isArray(repository.messages)
|
|
396
|
+
? { messages: repositoryMessages.filter(retainedMessageId) }
|
|
397
|
+
: {}),
|
|
398
|
+
agentKit: {
|
|
399
|
+
...agentKitRecord,
|
|
400
|
+
...(Array.isArray(agentKitRecord.messages)
|
|
401
|
+
? {
|
|
402
|
+
messages: agentKitRecord.messages
|
|
403
|
+
.filter(retainedMessageId)
|
|
404
|
+
.map((message) => {
|
|
405
|
+
const record = asRecord(message);
|
|
406
|
+
return typeof record.id === "string" &&
|
|
407
|
+
activeMessageIds.has(record.id) &&
|
|
408
|
+
record.status === "streaming"
|
|
409
|
+
? { ...record, status: "complete" }
|
|
410
|
+
: record;
|
|
411
|
+
}),
|
|
412
|
+
}
|
|
413
|
+
: {}),
|
|
414
|
+
...(Array.isArray(agentKitRecord.events)
|
|
415
|
+
? {
|
|
416
|
+
events: events.flatMap((rawEvent) => {
|
|
417
|
+
const event = asRecord(rawEvent);
|
|
418
|
+
const runId = event?.runId;
|
|
419
|
+
const boundary = typeof runId === "string"
|
|
420
|
+
? eventBoundaries.get(runId)
|
|
421
|
+
: undefined;
|
|
422
|
+
if (typeof runId !== "string" ||
|
|
423
|
+
boundary === undefined ||
|
|
424
|
+
typeof event?.sequence !== "number" ||
|
|
425
|
+
event.sequence > boundary) {
|
|
426
|
+
return [];
|
|
427
|
+
}
|
|
428
|
+
if (nonterminalRunIds.has(runId) &&
|
|
429
|
+
(event.type === "activity.started" ||
|
|
430
|
+
event.type === "activity.updated")) {
|
|
431
|
+
const activity = asRecord(event.activity);
|
|
432
|
+
if (activity?.status === "running") {
|
|
433
|
+
return [
|
|
434
|
+
{
|
|
435
|
+
...event,
|
|
436
|
+
type: "activity.completed",
|
|
437
|
+
activity: { ...activity, status: "cancelled" },
|
|
438
|
+
threadId: forkId,
|
|
439
|
+
},
|
|
440
|
+
];
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
return [remapThreadId(rawEvent)];
|
|
444
|
+
}),
|
|
445
|
+
}
|
|
446
|
+
: {}),
|
|
447
|
+
...(Array.isArray(agentKitRecord.runs)
|
|
448
|
+
? {
|
|
449
|
+
runs: runs.flatMap((rawRun) => {
|
|
450
|
+
const run = asRecord(rawRun);
|
|
451
|
+
if (typeof run?.id !== "string" || !retainedRunIds.has(run.id)) {
|
|
452
|
+
return [];
|
|
453
|
+
}
|
|
454
|
+
const copied = {
|
|
455
|
+
...run,
|
|
456
|
+
threadId: forkId,
|
|
457
|
+
};
|
|
458
|
+
if (nonterminalRunIds.has(run.id)) {
|
|
459
|
+
copied.status = "cancelled";
|
|
460
|
+
delete copied.activeMessageId;
|
|
461
|
+
}
|
|
462
|
+
return [copied];
|
|
463
|
+
}),
|
|
464
|
+
}
|
|
465
|
+
: {}),
|
|
466
|
+
...(fromMessageId && Array.isArray(agentKitRecord.toolCalls)
|
|
467
|
+
? {
|
|
468
|
+
toolCalls: agentKitRecord.toolCalls.filter((rawToolCall) => {
|
|
469
|
+
const toolCall = asRecord(rawToolCall);
|
|
470
|
+
if (typeof toolCall?.messageId === "string") {
|
|
471
|
+
return messageIds.has(toolCall.messageId);
|
|
472
|
+
}
|
|
473
|
+
return (typeof toolCall?.runId === "string" &&
|
|
474
|
+
retainedRunIds.has(toolCall.runId));
|
|
475
|
+
}),
|
|
476
|
+
}
|
|
477
|
+
: {}),
|
|
478
|
+
...(Array.isArray(agentKitRecord.widgets)
|
|
479
|
+
? {
|
|
480
|
+
widgets: agentKitRecord.widgets.filter((widget) => {
|
|
481
|
+
const record = asRecord(widget);
|
|
482
|
+
return (typeof record?.messageId === "string" &&
|
|
483
|
+
messageIds.has(record.messageId));
|
|
484
|
+
}),
|
|
485
|
+
}
|
|
486
|
+
: {}),
|
|
487
|
+
...(Array.isArray(agentKitRecord.annotations)
|
|
488
|
+
? {
|
|
489
|
+
annotations: agentKitRecord.annotations.filter((annotation) => {
|
|
490
|
+
const record = asRecord(annotation);
|
|
491
|
+
return (typeof record?.messageId === "string" &&
|
|
492
|
+
messageIds.has(record.messageId));
|
|
493
|
+
}),
|
|
494
|
+
}
|
|
495
|
+
: {}),
|
|
496
|
+
...(fromMessageId ? { suggestions: [] } : {}),
|
|
497
|
+
activeRunIds: [],
|
|
498
|
+
},
|
|
499
|
+
});
|
|
500
|
+
}
|
|
274
501
|
function deriveMessageCount(threadData, fallback) {
|
|
275
502
|
if (typeof threadData !== "string" || !threadData.trim())
|
|
276
503
|
return fallback;
|
|
277
504
|
try {
|
|
278
|
-
|
|
279
|
-
if (Array.isArray(repo.messages))
|
|
280
|
-
return repo.messages.length;
|
|
505
|
+
return countThreadMessages(JSON.parse(threadData), fallback);
|
|
281
506
|
}
|
|
282
507
|
catch {
|
|
283
508
|
// Keep the stored count if the JSON blob is malformed.
|
|
@@ -485,18 +710,17 @@ export async function forkThread(sourceId, ownerEmail, opts) {
|
|
|
485
710
|
}
|
|
486
711
|
else if (snapshot &&
|
|
487
712
|
source.ownerEmail === ownerEmail &&
|
|
488
|
-
snapshot.messageCount > source.messageCount) {
|
|
489
|
-
//
|
|
490
|
-
//
|
|
491
|
-
//
|
|
492
|
-
// latest user-visible content. Guard with messageCount > stored to avoid
|
|
493
|
-
// clobbering a fresher persisted row with a stale snapshot from another
|
|
494
|
-
// tab.
|
|
713
|
+
(snapshot.fromMessageId || snapshot.messageCount > source.messageCount)) {
|
|
714
|
+
// Message-scoped forks intentionally carry a truncated snapshot even when
|
|
715
|
+
// the persisted source has later messages; full forks only overlay fresher
|
|
716
|
+
// client snapshots.
|
|
495
717
|
source = {
|
|
496
718
|
...source,
|
|
497
719
|
threadData: snapshot.threadData,
|
|
498
720
|
title: snapshot.title || source.title,
|
|
499
|
-
preview: snapshot.
|
|
721
|
+
preview: snapshot.fromMessageId
|
|
722
|
+
? snapshot.preview
|
|
723
|
+
: snapshot.preview || source.preview,
|
|
500
724
|
messageCount: snapshot.messageCount,
|
|
501
725
|
};
|
|
502
726
|
}
|
|
@@ -505,6 +729,7 @@ export async function forkThread(sourceId, ownerEmail, opts) {
|
|
|
505
729
|
return null;
|
|
506
730
|
}
|
|
507
731
|
const id = opts?.id ?? generateId();
|
|
732
|
+
const threadData = forkThreadData(source.threadData, id, snapshot?.fromMessageId);
|
|
508
733
|
const now = Date.now();
|
|
509
734
|
const title = source.title ? `${source.title} (fork)` : "";
|
|
510
735
|
const client = getDbExec();
|
|
@@ -516,7 +741,7 @@ export async function forkThread(sourceId, ownerEmail, opts) {
|
|
|
516
741
|
ownerEmail,
|
|
517
742
|
title,
|
|
518
743
|
source.preview,
|
|
519
|
-
|
|
744
|
+
threadData,
|
|
520
745
|
source.messageCount,
|
|
521
746
|
now,
|
|
522
747
|
now,
|
|
@@ -534,7 +759,7 @@ export async function forkThread(sourceId, ownerEmail, opts) {
|
|
|
534
759
|
ownerEmail,
|
|
535
760
|
title,
|
|
536
761
|
preview: source.preview,
|
|
537
|
-
threadData
|
|
762
|
+
threadData,
|
|
538
763
|
messageCount: source.messageCount,
|
|
539
764
|
createdAt: now,
|
|
540
765
|
updatedAt: now,
|
|
@@ -787,9 +1012,7 @@ export async function updateThreadData(id, threadData, title, preview, messageCo
|
|
|
787
1012
|
preserveExistingTopLevelKeys: options.preserveExistingTopLevelKeys ?? true,
|
|
788
1013
|
});
|
|
789
1014
|
nextThreadData = JSON.stringify(merged);
|
|
790
|
-
|
|
791
|
-
nextMessageCount = merged.messages.length;
|
|
792
|
-
}
|
|
1015
|
+
nextMessageCount = countThreadMessages(merged, messageCount);
|
|
793
1016
|
}
|
|
794
1017
|
catch {
|
|
795
1018
|
// Keep the caller's serialized value if either JSON blob is malformed.
|