@jskit-ai/assistant-core 0.1.239 → 0.1.241
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/examples/conversation/package.json +5 -5
- package/package.json +5 -5
- package/src/client/conversation/AssistantTranscript.vue +26 -29
- package/src/client/conversation/LongTextPreviewBlocks.vue +11 -0
- package/src/client/conversation/messageDelivery.js +8 -2
- package/src/client/conversation/useAssistantAttachments.js +1 -1
- package/src/server/conversation/agentRun.js +12 -198
- package/src/server/conversation/attachments.js +6 -5
- package/src/server/conversation/boundedToolLoop.js +160 -0
- package/src/server/conversation/claudeHistory.js +20 -16
- package/src/server/conversation/claudeProcess.js +2 -2
- package/src/server/conversation/claudeTurn.js +51 -40
- package/src/server/conversation/codexAuthPreflight.js +104 -0
- package/src/server/conversation/codexClient.js +11 -16
- package/src/server/conversation/codexConfiguration.js +112 -98
- package/src/server/conversation/codexConversationStore.js +189 -0
- package/src/server/conversation/codexConversations.js +47 -44
- package/src/server/conversation/codexDetachedTurn.js +12 -11
- package/src/server/conversation/codexEvents.js +95 -99
- package/src/server/conversation/codexGoalCommands.js +1 -1
- package/src/server/conversation/codexHelperExecution.js +3 -13
- package/src/server/conversation/codexHelperThreadLedger.js +7 -13
- package/src/server/conversation/codexHelperThreadRecovery.js +2 -16
- package/src/server/conversation/codexHelperThreads.js +5 -6
- package/src/server/conversation/codexHistoryAdapter.js +32 -26
- package/src/server/conversation/codexMessageCommands.js +22 -22
- package/src/server/conversation/codexMessageDelivery.js +12 -7
- package/src/server/conversation/codexModelCatalog.js +97 -0
- package/src/server/conversation/codexNotificationObserver.js +24 -40
- package/src/server/conversation/codexNotificationQueue.js +2 -3
- package/src/server/conversation/codexOutput.js +20 -27
- package/src/server/conversation/codexProcess.js +123 -127
- package/src/server/conversation/codexProcessLeader.js +3 -3
- package/src/server/conversation/codexProvider.js +31 -614
- package/src/server/conversation/codexProviderOwner.js +2 -2
- package/src/server/conversation/codexProviderSelection.js +38 -40
- package/src/server/conversation/codexRenewalCommands.js +13 -10
- package/src/server/conversation/codexRenewalThreads.js +412 -0
- package/src/server/conversation/codexRunJournal.js +24 -42
- package/src/server/conversation/codexRuntimeLifecycle.js +19 -15
- package/src/server/conversation/codexSessionCleanup.js +11 -10
- package/src/server/conversation/codexSessionReconciliation.js +6 -6
- package/src/server/conversation/codexThreadReadiness.js +19 -33
- package/src/server/conversation/codexThreadStatus.js +3 -3
- package/src/server/conversation/codexTurn.js +124 -297
- package/src/server/conversation/codexTurnRecovery.js +49 -28
- package/src/server/conversation/codexTurnState.js +34 -13
- package/src/server/conversation/commandWrapper.js +3 -0
- package/src/server/conversation/deliveryTiming.js +14 -5
- package/src/server/conversation/index.js +1 -0
- package/src/server/conversation/localExecution.js +13 -17
- package/src/server/conversation/normalize.js +8 -3
- package/src/server/conversation/openCodeClient.js +9 -10
- package/src/server/conversation/openCodeProcess.js +9 -5
- package/src/server/conversation/openCodeRuntime.js +10 -11
- package/src/server/conversation/openCodeTurn.js +43 -29
- package/src/server/conversation/providers/claudeDriver.js +10 -16
- package/src/server/conversation/providers/codexDriver.js +27 -43
- package/src/server/conversation/providers/opencodeDriver.js +17 -42
- package/src/server/conversation/providers/scoped.js +48 -4
- package/src/server/conversation/runtime.js +50 -49
- package/src/server/conversation/runtimeStateUpgrade.js +34 -22
- package/src/server/conversation/transcript.js +10 -10
- package/src/server/hosts/codexConfiguration.js +1 -0
- package/src/server/hosts/codexProvider.js +8 -6
- package/src/server/index.js +2 -1
- package/src/server/lib/assistantToolLoop.js +1 -153
- package/src/server/lib/serviceToolCatalog.js +17 -42
- package/src/shared/assistantResource.js +4 -2
- package/src/shared/conversation/attachments.js +4 -0
- package/src/shared/conversation/longTextBlocks.js +54 -19
- package/src/shared/conversation/numberedQuestionSugar.js +17 -10
- package/src/shared/conversation/pagination.js +4 -6
- package/src/shared/conversation/patches.js +1 -4
- package/src/shared/conversation/streaming.js +3 -11
- package/src/shared/conversation/turns.js +11 -0
- package/src/shared/index.js +1 -0
- package/test/assistantBoundedToolLoop.test.js +2 -2
- package/test/assistantResource.test.js +4 -4
- package/test/assistantScroll.browser.test.js +29 -6
- package/test/assistantSettingsResource.test.js +2 -2
- package/test/claudeConversationSession.test.js +20 -3
- package/test/claudeTurn.test.js +27 -7
- package/test/codexClient.test.js +20 -0
- package/test/codexConversationSession.test.js +5 -3
- package/test/codexConversationsCheckpoint.test.js +51 -0
- package/test/codexModelCatalog.test.js +1 -1
- package/test/codexProviderHome.test.js +51 -0
- package/test/codexProviderSelection.test.js +71 -0
- package/test/codexTurn.test.js +222 -6
- package/test/conversationAgentRun.test.js +2 -1
- package/test/conversationAttachments.test.js +27 -2
- package/test/conversationDeliveryTiming.test.js +21 -0
- package/test/conversationNormalize.test.js +11 -0
- package/test/fixtures/nestedListQuestionMessage.md +37 -0
- package/test/frameworkActions.test.js +2 -2
- package/test/longTextBlocks.test.js +102 -0
- package/test/messageDelivery.test.js +7 -1
- package/test/openCodeClient.test.js +20 -0
- package/test/openCodeRuntime.test.js +1 -1
- package/test/openCodeTurn.test.js +90 -0
- package/test/opencodeConversationSession.test.js +1 -1
- package/test/serviceToolCatalog.test.js +27 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createConversationProviderFactory } from "./provider.js";
|
|
2
|
-
import { createConversationDeliveryTiming } from "./deliveryTiming.js";
|
|
2
|
+
import { createConversationDeliveryTiming, normalizeConversationDeliveryHostStages } from "./deliveryTiming.js";
|
|
3
3
|
import { normalizeConversationConfiguration } from "./configuration.js";
|
|
4
4
|
import { runScopedConversationTurn, readPersistentConversation, projectStoredPersistentConversation, startPersistentConversationTurn,
|
|
5
5
|
inspectPersistentConversationAdmission, preparePersistentConversationChangeover,
|
|
@@ -9,13 +9,14 @@ import { randomUUID } from "node:crypto";
|
|
|
9
9
|
import { createConversationTranscript, isCompletedEnvelopeTurn } from "./transcript.js";
|
|
10
10
|
import { createConversationStreams } from "./streams.js";
|
|
11
11
|
import { createReentrantConversationStorage } from "./storage.js";
|
|
12
|
-
import { createCodexConversationStore } from "./
|
|
12
|
+
import { createCodexConversationStore } from "./codexConversationStore.js";
|
|
13
13
|
import { codexAppServerTurnStateFromAgentRun } from "./codexTurn.js";
|
|
14
14
|
import { hasUnfinishedConversationRewind, createConversationRuntimeReplacement,
|
|
15
|
-
releaseConversationRuntimeBinding, finishConversationRuntimeReplacement
|
|
15
|
+
releaseConversationRuntimeBinding, finishConversationRuntimeReplacement,
|
|
16
|
+
conversationNativeIdentity, conversationNativeBinding } from "./runtimeStateUpgrade.js";
|
|
16
17
|
import { createServiceToolCatalog } from "../lib/serviceToolCatalog.js";
|
|
17
18
|
import { createConversationTools } from "./tools.js";
|
|
18
|
-
import { readAssistantResponseEnvelope } from "
|
|
19
|
+
import { readAssistantResponseEnvelope } from "./boundedToolLoop.js";
|
|
19
20
|
import { conversationAttachmentIds, createConversationAttachmentReader } from "./attachments.js";
|
|
20
21
|
import { conversationAttachmentManifest } from "../../shared/conversation/attachments.js";
|
|
21
22
|
import { createConversationChangeover, conversationContinuity, conversationHistoryVersions,
|
|
@@ -27,8 +28,6 @@ function failure(message, code, statusCode = 409) {
|
|
|
27
28
|
|
|
28
29
|
const submittedMessage = turn => turn.user || turn.system;
|
|
29
30
|
const retainedSegments = state => [...new Map([...state.predecessors, state].map(segment => [segment.segmentId, segment])).values()];
|
|
30
|
-
const nativeIdentity = segment => segment.engine === "codex" ? segment.binding?.threadId
|
|
31
|
-
: segment.engine === "claude" ? segment.binding?.conversationId : segment.binding?.sessionId;
|
|
32
31
|
const pendingSuccessor = state => state.engine !== "api" && state.predecessors.findLast(segment => segment.successorId === state.segmentId &&
|
|
33
32
|
segment.segmentId !== state.segmentId && segment.replacement?.operation === "replace" && !segment.acceptedAt);
|
|
34
33
|
const sameRequest = (left, right) => left.text === right.text && (left.origin || "user") === right.origin &&
|
|
@@ -37,9 +36,10 @@ const sameRequest = (left, right) => left.text === right.text && (left.origin ||
|
|
|
37
36
|
|
|
38
37
|
/** One application conversation API. Storage and authorization remain host facilities. */
|
|
39
38
|
export function createConversationRuntime({ engine: defaultEngine = "api", defaultIntegrationId, storage, authorize, connections, apiClientFactory, apiHistory,
|
|
40
|
-
actions, toolPolicy, toolCatalog, attachments, fetch, host: defaultHost, onDeliveryTiming, persistCommentary = true, completedEnvelope = false, limits = {} } = {}) {
|
|
39
|
+
actions, toolPolicy, toolCatalog, attachments, fetch, host: defaultHost, onDeliveryTiming, hostDeliveryStages, persistCommentary = true, completedEnvelope = false, limits = {} } = {}) {
|
|
41
40
|
if (typeof authorize !== "function") throw new TypeError("Conversation access requires an authorize function.");
|
|
42
41
|
if (onDeliveryTiming !== undefined && typeof onDeliveryTiming !== "function") throw new TypeError("Delivery timing requires a server-owned observer.");
|
|
42
|
+
const deliveryHostStages = normalizeConversationDeliveryHostStages(hostDeliveryStages);
|
|
43
43
|
if (typeof completedEnvelope !== "boolean") throw new TypeError("Completed envelope policy requires a server-side boolean.");
|
|
44
44
|
if (typeof persistCommentary !== "boolean") throw new TypeError("Conversation commentary persistence requires an explicit server-side boolean.");
|
|
45
45
|
if (apiHistory !== undefined && typeof apiHistory !== "function") throw new TypeError("API request history requires a server-owned selector.");
|
|
@@ -188,10 +188,15 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
188
188
|
? await entry.driver.readState({ conversation: entry.conversation, context })
|
|
189
189
|
: await entry.conversation.read(context);
|
|
190
190
|
entry.segmentId = value.threadId ? `${entry.engine}:${value.threadId}` : null;
|
|
191
|
-
const
|
|
191
|
+
const saved = value.delivery?.engines[entry.engine]?.pending;
|
|
192
|
+
// The saved receipt is the delivery proof, as in changeover recovery. A
|
|
193
|
+
// restart after native admission leaves only a stale attempt marker, which
|
|
194
|
+
// must not leave the conversation unconfirmed until another message is sent.
|
|
195
|
+
const pending = (saved?.attempted && !saved.inspectionOnly &&
|
|
196
|
+
await entry.conversation.transcript.hasMessage(saved.messageId)) ? null : saved;
|
|
192
197
|
return { engine: entry.engine, configuration: value.configuration, segmentId: entry.segmentId,
|
|
193
|
-
binding: entry.engine
|
|
194
|
-
|
|
198
|
+
binding: { ...conversationNativeBinding(entry.engine, value.threadId),
|
|
199
|
+
...(entry.engine === "codex" ? { codexAppServerRun: value.run } : {}) },
|
|
195
200
|
nativeTurn: value.turn, nativeResult: value.nativeResult,
|
|
196
201
|
lastEngine: value.delivery?.lastEngine ?? entry.engine,
|
|
197
202
|
replacement: ["preparing", "ready"].includes(value.delivery?.replacement?.status) ? value.delivery.replacement : null,
|
|
@@ -386,11 +391,11 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
386
391
|
return { lastEngine: runtime.lastEngine, engines,
|
|
387
392
|
...(preparing ? { replacement: { engineId: preparing.engine, status: "preparing",
|
|
388
393
|
operationId: preparing.request.operationId, handover: preparing.request.briefing || "",
|
|
389
|
-
bindingNames: ["binding"], previous: { engineId: runtime.engine, conversationId:
|
|
394
|
+
bindingNames: ["binding"], previous: { engineId: runtime.engine, conversationId: conversationNativeIdentity(runtime) },
|
|
390
395
|
...(preparing.preparedAt ? { preparedAt: preparing.preparedAt } : {}) } }
|
|
391
396
|
: predecessor ? { replacement: { engineId: runtime.engine, status: "ready",
|
|
392
397
|
operationId: predecessor.replacement.operationId, handover: predecessor.replacement.briefing || "",
|
|
393
|
-
previous: { engineId: predecessor.engine, conversationId:
|
|
398
|
+
previous: { engineId: predecessor.engine, conversationId: conversationNativeIdentity(predecessor) },
|
|
394
399
|
...(predecessor.preparedAt ? { preparedAt: predecessor.preparedAt } : {}) } } : {}) };
|
|
395
400
|
},
|
|
396
401
|
write(value) {
|
|
@@ -473,7 +478,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
473
478
|
return { admission: observation.accepted ? "accepted" : "unknown" };
|
|
474
479
|
}
|
|
475
480
|
const { runtime } = await storage.read(entry.id, transaction => transaction.readMetadata());
|
|
476
|
-
if (!threadId || threadId !==
|
|
481
|
+
if (!threadId || threadId !== conversationNativeIdentity(runtime) || runtime.request?.messageId !== messageId) return null;
|
|
477
482
|
observation = await entry.provider.inspectAdmission({ ...runtime.request, context, configuration: runtime.configuration });
|
|
478
483
|
await access(context, entry.id, request?.operation || "inspectDelivery", entry);
|
|
479
484
|
return { admission: observation.accepted ? "accepted" : "unknown" };
|
|
@@ -604,7 +609,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
604
609
|
const authored = submittedMessage(turn || {});
|
|
605
610
|
const saved = turn?.metadata?.runtime;
|
|
606
611
|
if (runtime?.version !== 3 || runtime.engine !== engine || runtime.segmentId !== segmentId ||
|
|
607
|
-
runtime.replacement || runtime.request || !
|
|
612
|
+
runtime.replacement || runtime.request || !conversationNativeIdentity(runtime) || authored?.messageId !== messageId ||
|
|
608
613
|
authored.origin !== "application" || saved?.completedEnvelope !== true || saved.engine !== engine ||
|
|
609
614
|
saved.segmentId !== segmentId || saved.status !== "complete" || saved.supersededBy ||
|
|
610
615
|
typeof turn.assistant?.text !== "string" || !turn.assistant.text || !turn.assistant.messageId) {
|
|
@@ -644,7 +649,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
644
649
|
await access(context, entry.id, "tool", entry);
|
|
645
650
|
const saved = await storage.read(entry.id, transaction => exact(transaction));
|
|
646
651
|
const observation = await entry.provider.inspectAdmission({ messageId,
|
|
647
|
-
threadId:
|
|
652
|
+
threadId: conversationNativeIdentity(saved.runtime), nativeTurnId: saved.turn.metadata.runtime.nativeTurnId,
|
|
648
653
|
configuration: saved.runtime.configuration, receiptBinding: saved.runtime.binding, context, verifyAccount: true });
|
|
649
654
|
signal.throwIfAborted();
|
|
650
655
|
await access(context, entry.id, "tool", entry);
|
|
@@ -777,6 +782,13 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
777
782
|
let toolFailure;
|
|
778
783
|
const applicationTools = entry.applicationTools;
|
|
779
784
|
const toolStorage = applicationTools?.storage || storage;
|
|
785
|
+
// Every saved turn and pending request records its attribution identically.
|
|
786
|
+
const turnAttribution = (request, extra = {}) => ({ engine, segmentId, origin: request.input.origin,
|
|
787
|
+
...(request.completedEnvelope ? { completedEnvelope: true } : {}),
|
|
788
|
+
...(typeof request.nativeToolAttempt === "boolean" ? { nativeToolAttempt: request.nativeToolAttempt } : {}), ...extra });
|
|
789
|
+
const authoredRequestRecord = request => ({ ...request.input, at: request.at,
|
|
790
|
+
...(request.completedEnvelope ? { completedEnvelope: true, excludedMessageIds: request.excludedMessageIds } : {}),
|
|
791
|
+
...(typeof request.nativeToolAttempt === "boolean" ? { nativeToolAttempt: request.nativeToolAttempt } : {}) });
|
|
780
792
|
|
|
781
793
|
function prepareTools(request) {
|
|
782
794
|
if (request.completedEnvelope || !catalog || entry.conversation && !applicationTools) return;
|
|
@@ -822,12 +834,10 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
822
834
|
if (request.answer?.text.trim()) await transaction.replaceAssistant(request.turnId, request.answer);
|
|
823
835
|
}
|
|
824
836
|
const saved = driver.canonicalTranscript ? await transaction.readTurn(request.turnId) : null;
|
|
825
|
-
await transaction.updateTurnMetadata(request.turnId, { runtime: { ...saved?.metadata?.runtime, status,
|
|
826
|
-
...(request.completedEnvelope ? { completedEnvelope: true } : {}),
|
|
827
|
-
...(typeof request.nativeToolAttempt === "boolean" ? { nativeToolAttempt: request.nativeToolAttempt } : {}),
|
|
837
|
+
await transaction.updateTurnMetadata(request.turnId, { runtime: { ...saved?.metadata?.runtime, status, ...turnAttribution(request, {
|
|
828
838
|
...(request.nativeTurnId ? { nativeTurnId: request.nativeTurnId } : {}),
|
|
829
839
|
...(request.goalMessageId ? { goalMessageId: request.goalMessageId } : {}),
|
|
830
|
-
...(request.continuedBy ? { continuedBy: request.continuedBy } : {}), ...extra, ...(error ? { error } : {}) } });
|
|
840
|
+
...(request.continuedBy ? { continuedBy: request.continuedBy } : {}) }), ...extra, ...(error ? { error } : {}) } });
|
|
831
841
|
return conversationHistoryVersions([{ turnId: request.turnId,
|
|
832
842
|
messages: [...request.progress.values()].filter(message => !(message.turnId === request.turnId && message.status === "complete"))
|
|
833
843
|
.concat(request.answer?.text.trim() ? [request.answer] : []) }]);
|
|
@@ -895,11 +905,9 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
895
905
|
} else {
|
|
896
906
|
id = await transaction.nextTurnId();
|
|
897
907
|
await transaction.appendMessage(id, { role: request.input.origin === "application" ? "system" : "user", ...request.input, at: new Date().toISOString(),
|
|
898
|
-
turnMetadata: { runtime: { status: "running",
|
|
899
|
-
...(request.completedEnvelope ? { completedEnvelope: true } : {}),
|
|
900
|
-
...(typeof request.nativeToolAttempt === "boolean" ? { nativeToolAttempt: request.nativeToolAttempt } : {}),
|
|
908
|
+
turnMetadata: { runtime: { status: "running", ...turnAttribution(request, {
|
|
901
909
|
...(request.goalMessageId ? { goalMessageId: request.goalMessageId } : {}),
|
|
902
|
-
...(nativeTurnId ? { nativeTurnId } : {}) } } });
|
|
910
|
+
...(nativeTurnId ? { nativeTurnId } : {}) }) } } });
|
|
903
911
|
}
|
|
904
912
|
if (engine === "api") {
|
|
905
913
|
metadata.runtime.seen = { ...(seen || metadata.runtime.seen), ...conversationHistoryVersions([await transaction.readTurn(id)]) };
|
|
@@ -1085,9 +1093,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
1085
1093
|
throw failure("Authorized attachment paths changed after this instruction was prepared. Restore the original files or send a new message.", "conversation_attachment_manifest_changed");
|
|
1086
1094
|
}
|
|
1087
1095
|
metadata.runtime.request = { ...(pending?.messageId === request.input.messageId ? pending
|
|
1088
|
-
: { ...request
|
|
1089
|
-
...(request.completedEnvelope ? { completedEnvelope: true, excludedMessageIds: request.excludedMessageIds } : {}),
|
|
1090
|
-
...(typeof request.nativeToolAttempt === "boolean" ? { nativeToolAttempt: request.nativeToolAttempt } : {}) }), attachmentManifest };
|
|
1096
|
+
: { ...authoredRequestRecord(request), attempted: false }), attachmentManifest };
|
|
1091
1097
|
await transaction.writeMetadata(metadata);
|
|
1092
1098
|
});
|
|
1093
1099
|
const admitted = Promise.withResolvers();
|
|
@@ -1113,9 +1119,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
1113
1119
|
threadId = native?.threadId;
|
|
1114
1120
|
if (!threadId) throw new Error("Native admission requires the saved conversation identity.");
|
|
1115
1121
|
await input.onPromptSending(entry.conversation ? native : { threadId, displayAttachments: files.attachments,
|
|
1116
|
-
turnMetadata: { runtime:
|
|
1117
|
-
...(request.completedEnvelope ? { completedEnvelope: true } : {}),
|
|
1118
|
-
...(typeof request.nativeToolAttempt === "boolean" ? { nativeToolAttempt: request.nativeToolAttempt } : {}) } } });
|
|
1122
|
+
turnMetadata: { runtime: turnAttribution(request) } });
|
|
1119
1123
|
if (request.input.goal && request.context?.signal?.aborted) {
|
|
1120
1124
|
throw Object.assign(failure("The goal command was stopped before delivery.", "conversation_goal_stopped"), { delivery: "not-sent" });
|
|
1121
1125
|
}
|
|
@@ -1167,9 +1171,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
1167
1171
|
const metadata = await transaction.readMetadata();
|
|
1168
1172
|
const pending = metadata.runtime.request;
|
|
1169
1173
|
if (pending && pending.messageId !== request.input.messageId && (pending.attempted || pending.inspectionOnly)) throw new Error("The pending conversation instruction changed.");
|
|
1170
|
-
metadata.runtime.request = { ...request
|
|
1171
|
-
...(request.completedEnvelope ? { completedEnvelope: true, excludedMessageIds: request.excludedMessageIds } : {}),
|
|
1172
|
-
...(typeof request.nativeToolAttempt === "boolean" ? { nativeToolAttempt: request.nativeToolAttempt } : {}),
|
|
1174
|
+
metadata.runtime.request = { ...authoredRequestRecord(request),
|
|
1173
1175
|
message: rendered.message, displayMessage: rendered.displayMessage,
|
|
1174
1176
|
displayAttachments, attachmentIds: rendered.attachmentIds,
|
|
1175
1177
|
attachmentManifest,
|
|
@@ -1181,7 +1183,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
1181
1183
|
},
|
|
1182
1184
|
async onPromptRejected() {
|
|
1183
1185
|
if (entry.conversation) return request.originalInput?.onPromptRejected?.();
|
|
1184
|
-
|
|
1186
|
+
await storage.write(entry.id, async transaction => {
|
|
1185
1187
|
const metadata = await transaction.readMetadata();
|
|
1186
1188
|
if (metadata.runtime.request?.messageId === request.input.messageId) {
|
|
1187
1189
|
metadata.runtime.request.attempted = false;
|
|
@@ -1309,7 +1311,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
1309
1311
|
const turn = await transaction.readTurn(initial.turnId);
|
|
1310
1312
|
const saved = turn?.metadata?.runtime;
|
|
1311
1313
|
if (metadata.runtime.engine !== engine || metadata.runtime.segmentId !== segmentId ||
|
|
1312
|
-
|
|
1314
|
+
conversationNativeIdentity(metadata.runtime) !== threadId || submittedMessage(turn || {})?.messageId !== messageId ||
|
|
1313
1315
|
saved?.engine !== engine || saved.segmentId !== segmentId || saved.completedEnvelope !== true ||
|
|
1314
1316
|
typeof saved.nativeToolAttempt !== "boolean" || saved.status !== "running" || saved.supersededBy ||
|
|
1315
1317
|
entry.active !== active || current !== initial || initial.finished || initial.sealed || active.controller.signal.aborted) {
|
|
@@ -1475,7 +1477,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
1475
1477
|
|
|
1476
1478
|
async function send(entry, context, input, origin = "user", goal, representation, wakeOptions) {
|
|
1477
1479
|
const deliveryTiming = createConversationDeliveryTiming({ engine: entry.engine,
|
|
1478
|
-
conversationId: entry.id, messageId: input?.messageId, report: onDeliveryTiming });
|
|
1480
|
+
conversationId: entry.id, messageId: input?.messageId, report: onDeliveryTiming, hostStages: deliveryHostStages });
|
|
1479
1481
|
deliveryTiming("received");
|
|
1480
1482
|
const grant = new Set();
|
|
1481
1483
|
try {
|
|
@@ -1701,9 +1703,6 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
1701
1703
|
await access(context, entry.id, "goal", entry);
|
|
1702
1704
|
context?.signal?.throwIfAborted();
|
|
1703
1705
|
if (!capabilities(entry).goals) throw failure("This connection does not support native goals.", "conversation_unsupported", 400);
|
|
1704
|
-
if (representation !== "native" && !capabilities(entry).goals) {
|
|
1705
|
-
throw failure("This bound conversation does not yet map native goal messages to canonical receipts.", "conversation_unsupported", 400);
|
|
1706
|
-
}
|
|
1707
1706
|
if (representation === "native") {
|
|
1708
1707
|
const { operation } = await serial(entry, async () => {
|
|
1709
1708
|
await access(context, entry.id, "goal", entry);
|
|
@@ -1817,8 +1816,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
1817
1816
|
// Release the queue once Send is reserved, just as an ordinary message
|
|
1818
1817
|
// does. Stop remains available while native acknowledgement is pending.
|
|
1819
1818
|
if (delivery) return delivery;
|
|
1820
|
-
|
|
1821
|
-
entry.goal = entry.conversation ? updated.goal : updated;
|
|
1819
|
+
entry.goal = updated;
|
|
1822
1820
|
await emit(entry, { type: "goal", goal: entry.goal });
|
|
1823
1821
|
return { goal: structuredClone(entry.goal) };
|
|
1824
1822
|
});
|
|
@@ -1851,13 +1849,16 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
1851
1849
|
const history = await readTranscript(entry);
|
|
1852
1850
|
const existing = history.find(turn => submittedMessage(turn)?.messageId === messageId);
|
|
1853
1851
|
const receipt = existing && { status: "accepted", messageId, turnId: existing.turnId, duplicate: true };
|
|
1854
|
-
|
|
1852
|
+
// Turns written before the first runtime segment carry no runtime metadata.
|
|
1853
|
+
const existingRuntime = existing?.metadata?.runtime;
|
|
1854
|
+
const goalMessageId = existingRuntime?.goalMessageId;
|
|
1855
1855
|
const goalRequest = Boolean(submittedMessage(existing || {})?.goal || goalMessageId === messageId);
|
|
1856
|
-
if (existing && (
|
|
1857
|
-
|
|
1856
|
+
if (existing && (existingRuntime?.segmentId !== entry.segmentId || existingRuntime.supersededBy ||
|
|
1857
|
+
existingRuntime.status === "complete" && !goalRequest)) return receipt;
|
|
1858
1858
|
if (goalMessageId && goalMessageId !== messageId) {
|
|
1859
1859
|
const owner = history.find(turn => submittedMessage(turn)?.messageId === goalMessageId);
|
|
1860
|
-
|
|
1860
|
+
const ownerRuntime = owner?.metadata?.runtime;
|
|
1861
|
+
if (ownerRuntime?.goalMessageId !== goalMessageId || ownerRuntime.segmentId !== entry.segmentId) throw new Error("The goal command for this native continuation is missing.");
|
|
1861
1862
|
const result = await inspectDelivery(entry, context, goalMessageId);
|
|
1862
1863
|
return { ...receipt, recovered: result.recovered === true };
|
|
1863
1864
|
}
|
|
@@ -1873,7 +1874,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
1873
1874
|
const request = existing ? submittedMessage(existing) : metadata.runtime.request;
|
|
1874
1875
|
if (!request || request.messageId !== messageId || !entry.provider.inspectAdmission) return receipt || { status: "unknown", messageId };
|
|
1875
1876
|
const pending = metadata.runtime.request?.messageId === messageId ? metadata.runtime.request : null;
|
|
1876
|
-
if (pending?.threadId && pending.threadId !==
|
|
1877
|
+
if (pending?.threadId && pending.threadId !== conversationNativeIdentity(metadata.runtime)) return receipt || { status: "unknown", messageId };
|
|
1877
1878
|
const observation = suppliedObservation || await entry.provider.inspectAdmission({ ...request, nativeTurnId: existing?.metadata?.runtime?.nativeTurnId,
|
|
1878
1879
|
context, configuration: metadata.runtime.configuration });
|
|
1879
1880
|
await access(context, entry.id, "inspectDelivery", entry);
|
|
@@ -1998,7 +1999,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
1998
1999
|
}
|
|
1999
2000
|
const row = (await readTranscript(entry)).find(turn => submittedMessage(turn)?.messageId === messageId);
|
|
2000
2001
|
if (row) return { status: "accepted", messageId, turnId: row.turnId, duplicate: true };
|
|
2001
|
-
const threadId = state.request?.messageId === messageId ? state.request.threadId :
|
|
2002
|
+
const threadId = state.request?.messageId === messageId ? state.request.threadId : conversationNativeIdentity(state);
|
|
2002
2003
|
if (!threadId) return { status: "unknown", messageId };
|
|
2003
2004
|
const observation = await entry.provider.inspectAdmission({ messageId, threadId, context });
|
|
2004
2005
|
await access(context, entry.id, "inspectDelivery", entry);
|
|
@@ -2106,7 +2107,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
2106
2107
|
? normalizeConversationConfiguration(input.configuration, { engine, defaultIntegrationId }) : state.configuration);
|
|
2107
2108
|
const driver = createDriver(engine, entry.host);
|
|
2108
2109
|
driver.validateConfiguration(configuration);
|
|
2109
|
-
const predecessorId =
|
|
2110
|
+
const predecessorId = conversationNativeIdentity(state);
|
|
2110
2111
|
const nativeReplacement = !selection && state.engine !== "api" && predecessorId &&
|
|
2111
2112
|
(state.engine !== "claude" || state.binding.sent);
|
|
2112
2113
|
const retainNativeBinding = input.retireNative !== true && engine === state.engine &&
|
|
@@ -2189,7 +2190,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
2189
2190
|
}
|
|
2190
2191
|
await callback({
|
|
2191
2192
|
async releaseBinding(replacement) {
|
|
2192
|
-
if (replacement &&
|
|
2193
|
+
if (replacement && conversationNativeIdentity(state) !== replacement.previous.conversationId) {
|
|
2193
2194
|
throw failure("The native predecessor identity changed before its binding was released.", "conversation_replacement_conflict");
|
|
2194
2195
|
}
|
|
2195
2196
|
releaseConversationRuntimeBinding(state, pending, request, engine);
|
|
@@ -2216,7 +2217,7 @@ export function createConversationRuntime({ engine: defaultEngine = "api", defau
|
|
|
2216
2217
|
identity: {
|
|
2217
2218
|
async inspect(expectedId) {
|
|
2218
2219
|
const { runtime } = await storage.read(entry.id, transaction => transaction.readMetadata());
|
|
2219
|
-
if (runtime.segmentId !== input.expectedSegmentId ||
|
|
2220
|
+
if (runtime.segmentId !== input.expectedSegmentId || conversationNativeIdentity(runtime) !== expectedId) {
|
|
2220
2221
|
throw failure("The native predecessor identity changed or is incomplete.", "conversation_replacement_conflict");
|
|
2221
2222
|
}
|
|
2222
2223
|
return { bindingNames: ["binding"], previous: { engineId: runtime.engine, conversationId: expectedId } };
|
|
@@ -31,9 +31,15 @@ function validateSegment(segment) {
|
|
|
31
31
|
}
|
|
32
32
|
}
|
|
33
33
|
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
34
|
+
const nativeIdentityField = engine => engine === "codex" ? "threadId" : engine === "claude" ? "conversationId" : "sessionId";
|
|
35
|
+
|
|
36
|
+
/** The saved binding field that holds a native segment's conversation identity. */
|
|
37
|
+
export function conversationNativeIdentity(segment) {
|
|
38
|
+
return segment.binding?.[nativeIdentityField(segment.engine)];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export function conversationNativeBinding(engine, id) {
|
|
42
|
+
return { [nativeIdentityField(engine)]: id };
|
|
37
43
|
}
|
|
38
44
|
|
|
39
45
|
function inspectRequest(request, segment, version, warnings) {
|
|
@@ -44,7 +50,7 @@ function inspectRequest(request, segment, version, warnings) {
|
|
|
44
50
|
invalid("Conversation runtime has an unsupported pending request. Inspect it before upgrading.");
|
|
45
51
|
}
|
|
46
52
|
if (segment.engine === "api") return;
|
|
47
|
-
const threadId =
|
|
53
|
+
const threadId = conversationNativeIdentity(segment);
|
|
48
54
|
if (version === 3 && !Object.hasOwn(request, "inspectionOnly")) {
|
|
49
55
|
if (typeof request.attempted !== "boolean" || request.attempted &&
|
|
50
56
|
(typeof threadId !== "string" || !threadId || request.threadId !== threadId || typeof request.message !== "string") ||
|
|
@@ -143,30 +149,21 @@ function retireCodexBinding(runtime, retirement) {
|
|
|
143
149
|
finishConversationRuntimeReplacement(runtime, { engine: "codex", selection: true, retireNative: true, pending });
|
|
144
150
|
}
|
|
145
151
|
|
|
146
|
-
/**
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
}
|
|
151
|
-
if (metadata.runtime === undefined) {
|
|
152
|
-
if (retirement !== undefined) invalid("Offline native retirement requires the exact saved runtime; it cannot create a historical binding.");
|
|
153
|
-
return { metadata, changed: false, warnings: [] };
|
|
154
|
-
}
|
|
155
|
-
const source = metadata.runtime;
|
|
156
|
-
if (!object(source) || ![2, 3].includes(source.version) || !Array.isArray(source.predecessors)) {
|
|
152
|
+
/** Validates a saved runtime and its retained requests. A version 2 request is
|
|
153
|
+
* marked inspection-only in place, so the upgrade passes its own copy. */
|
|
154
|
+
function validateConversationRuntimeState(runtime, warnings) {
|
|
155
|
+
if (!object(runtime) || ![2, 3].includes(runtime.version) || !Array.isArray(runtime.predecessors)) {
|
|
157
156
|
invalid("This conversation has an unsupported runtime version. Inspect its original format and provide an offline conversion before opening it.");
|
|
158
157
|
}
|
|
159
|
-
if (hasUnfinishedConversationRewind(
|
|
158
|
+
if (hasUnfinishedConversationRewind(runtime)) {
|
|
160
159
|
invalid("This conversation has an unfinished Undo operation from an earlier runtime. Inspect its saved native and local history offline before upgrading; no history was changed.");
|
|
161
160
|
}
|
|
162
|
-
if (
|
|
161
|
+
if (runtime.version === 3 && (typeof runtime.lastEngine !== "string" || runtime.lastEngine && !engines.has(runtime.lastEngine))) {
|
|
163
162
|
invalid("Conversation runtime has an invalid last-delivered engine. Inspect it before upgrading.");
|
|
164
163
|
}
|
|
165
|
-
const runtime = structuredClone(source);
|
|
166
|
-
const warnings = [];
|
|
167
164
|
for (const segment of [...runtime.predecessors, runtime]) {
|
|
168
165
|
validateSegment(segment);
|
|
169
|
-
inspectRequest(segment.request, segment,
|
|
166
|
+
inspectRequest(segment.request, segment, runtime.version, warnings);
|
|
170
167
|
}
|
|
171
168
|
if (runtime.replacement !== undefined) {
|
|
172
169
|
const pending = runtime.replacement;
|
|
@@ -177,11 +174,26 @@ export function upgradeConversationRuntimeState({ metadata, conversationLog, ret
|
|
|
177
174
|
}
|
|
178
175
|
if (pending.binding) {
|
|
179
176
|
validateSegment(pending);
|
|
180
|
-
inspectRequest(pending.submission, pending,
|
|
177
|
+
inspectRequest(pending.submission, pending, runtime.version, warnings);
|
|
181
178
|
} else if (pending.submission !== undefined) {
|
|
182
179
|
invalid("An unfinished replacement has a request without its exact saved binding. Inspect it before upgrading.");
|
|
183
180
|
}
|
|
184
181
|
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/** Pure offline transform. The application owns enumeration, backups and publication. */
|
|
185
|
+
export function upgradeConversationRuntimeState({ metadata, conversationLog, retirement }) {
|
|
186
|
+
if (!object(metadata) || !Array.isArray(conversationLog)) {
|
|
187
|
+
invalid("Conversation runtime upgrade requires metadata and the complete stored transcript.");
|
|
188
|
+
}
|
|
189
|
+
if (metadata.runtime === undefined) {
|
|
190
|
+
if (retirement !== undefined) invalid("Offline native retirement requires the exact saved runtime; it cannot create a historical binding.");
|
|
191
|
+
return { metadata, changed: false, warnings: [] };
|
|
192
|
+
}
|
|
193
|
+
const source = metadata.runtime;
|
|
194
|
+
const runtime = structuredClone(source);
|
|
195
|
+
const warnings = [];
|
|
196
|
+
validateConversationRuntimeState(runtime, warnings);
|
|
185
197
|
if (retirement !== undefined) {
|
|
186
198
|
retireCodexBinding(runtime, retirement);
|
|
187
199
|
return { metadata: { ...metadata, runtime }, changed: true, warnings };
|
|
@@ -211,7 +223,7 @@ export async function prepareConversationRuntimeCopy(transaction, { at }) {
|
|
|
211
223
|
const runtime = metadata.runtime;
|
|
212
224
|
if (runtime) {
|
|
213
225
|
if (runtime.version !== 3) invalid("Upgrade the parent conversation before copying its runtime state.");
|
|
214
|
-
|
|
226
|
+
validateConversationRuntimeState(runtime, []);
|
|
215
227
|
if (runtime.replacement) invalid("Finish the parent conversation changeover before copying it.");
|
|
216
228
|
for (const segment of [...runtime.predecessors, runtime]) {
|
|
217
229
|
if (segment.binding) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { normalizeNullableText } from "./normalize.js";
|
|
2
2
|
import { conversationLogPage } from "../../shared/conversation/pagination.js";
|
|
3
3
|
|
|
4
4
|
/** Conversation policy, independent of database, filesystem, identity and HTTP. */
|
|
@@ -25,7 +25,7 @@ export function createConversationTranscript({ storage, clock = () => new Date()
|
|
|
25
25
|
? (await Promise.all(allIds.map(id => transaction.readTurn(id))))
|
|
26
26
|
.filter(turn => hasMessages(turn) && (!presentation || !isCompletedEnvelopeTurn(turn))) : null;
|
|
27
27
|
const ids = visible ? visible.map(turn => turn.turnId) : allIds;
|
|
28
|
-
const before =
|
|
28
|
+
const before = normalizeNullableText(beforeTurnId);
|
|
29
29
|
const requestedLimit = Number.parseInt(String(limit || ""), 10);
|
|
30
30
|
const pageLimit = Number.isFinite(requestedLimit) && requestedLimit > 0 ? Math.min(requestedLimit, 100) : 0;
|
|
31
31
|
let page;
|
|
@@ -66,13 +66,13 @@ export function createConversationTranscript({ storage, clock = () => new Date()
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
async function append(scope, role, { text = "", messageId = "", at = "", attachments = [], data, turnMetadata = null, requireOpenTurn = false, outputId = "", turnId: requestedTurnId = "" } = {}) {
|
|
69
|
-
const messageText =
|
|
70
|
-
const id =
|
|
69
|
+
const messageText = normalizeNullableText(text);
|
|
70
|
+
const id = normalizeNullableText(messageId);
|
|
71
71
|
if (!messageText && !(role === "user" && Array.isArray(attachments) && attachments.length)) return null;
|
|
72
72
|
return storage.write(scope, async (transaction) => {
|
|
73
73
|
if (id && await transaction.hasMessage(id)) return null;
|
|
74
74
|
const createdAt = new Date(at || clock());
|
|
75
|
-
const targetId = ["assistant", "commentary", "thinking"].includes(role) ?
|
|
75
|
+
const targetId = ["assistant", "commentary", "thinking"].includes(role) ? normalizeNullableText(requestedTurnId) : "";
|
|
76
76
|
const target = targetId ? await transaction.readTurn(targetId) : null;
|
|
77
77
|
if (targetId && !target) throw new Error("The native output's original turn no longer exists.");
|
|
78
78
|
const tail = target || (["assistant", "commentary", "thinking"].includes(role) ? await tailTurn(transaction) : null);
|
|
@@ -84,7 +84,7 @@ export function createConversationTranscript({ storage, clock = () => new Date()
|
|
|
84
84
|
const turnId = target?.turnId || open?.turnId || (thinkingOnly ? tail.turnId : await transaction.nextTurnId());
|
|
85
85
|
await transaction.appendMessage(turnId, {
|
|
86
86
|
role, text: messageText, messageId: id, at: createdAt.toISOString(),
|
|
87
|
-
...(["assistant", "commentary", "thinking"].includes(role) &&
|
|
87
|
+
...(["assistant", "commentary", "thinking"].includes(role) && normalizeNullableText(outputId) ? { outputId: normalizeNullableText(outputId) } : {}),
|
|
88
88
|
...(role === "user" ? { attachments, turnMetadata } : {}),
|
|
89
89
|
...(data !== undefined ? { data: structuredClone(data) } : {})
|
|
90
90
|
});
|
|
@@ -94,7 +94,7 @@ export function createConversationTranscript({ storage, clock = () => new Date()
|
|
|
94
94
|
|
|
95
95
|
// The first turn whose submitted (user or application) message has this ID.
|
|
96
96
|
async function findSubmittedTurn(scope, messageId) {
|
|
97
|
-
const id =
|
|
97
|
+
const id = normalizeNullableText(messageId);
|
|
98
98
|
if (!id) return null;
|
|
99
99
|
return storage.read(scope, async (transaction) => {
|
|
100
100
|
const submitted = turn => hasMessages(turn) && (turn.user || turn.system)?.messageId === id ? turn : null;
|
|
@@ -116,17 +116,17 @@ export function createConversationTranscript({ storage, clock = () => new Date()
|
|
|
116
116
|
readConversationLog,
|
|
117
117
|
findSubmittedTurn,
|
|
118
118
|
readConversationLogPage,
|
|
119
|
-
conversationMessageIdExists: (scope, messageId) => storage.read(scope, (transaction) => transaction.hasMessage(
|
|
119
|
+
conversationMessageIdExists: (scope, messageId) => storage.read(scope, (transaction) => transaction.hasMessage(normalizeNullableText(messageId))),
|
|
120
120
|
writeConversationUserMessage: (scope, input) => append(scope, "user", input),
|
|
121
121
|
writeConversationAssistantMessage: (scope, input) => append(scope, "assistant", input),
|
|
122
122
|
writeConversationCommentaryMessage: (scope, input) => append(scope, "commentary", input),
|
|
123
123
|
writeConversationThinkingMessage: (scope, input) => append(scope, "thinking", input),
|
|
124
124
|
writeConversationSystemMessage: (scope, input) => append(scope, "system", input),
|
|
125
125
|
async upsertConversationAssistantMessage(scope, { turnId = "", text = "" } = {}) {
|
|
126
|
-
const messageText =
|
|
126
|
+
const messageText = normalizeNullableText(text);
|
|
127
127
|
if (!messageText) return null;
|
|
128
128
|
return storage.write(scope, async (transaction) => {
|
|
129
|
-
const id =
|
|
129
|
+
const id = normalizeNullableText(turnId);
|
|
130
130
|
await transaction.replaceAssistant(id, { role: "assistant", text: messageText, at: new Date(clock()).toISOString() });
|
|
131
131
|
return transaction.readTurn(id);
|
|
132
132
|
});
|
|
@@ -1,26 +1,28 @@
|
|
|
1
1
|
export {
|
|
2
2
|
CODEX_APP_SERVER_INVALID_REQUEST_CODE,
|
|
3
3
|
CodexAppServerAgentProvider,
|
|
4
|
-
assertCodexAuthPreflightReady,
|
|
5
4
|
codexAppServerEndpointForTarget,
|
|
6
5
|
codexAppServerRequestIsInvalid,
|
|
7
6
|
codexAppServerRuntimeStopWasVerified,
|
|
8
7
|
codexCliResumeCommand,
|
|
9
|
-
codexRenewalThreadError,
|
|
10
8
|
codexTextInput,
|
|
11
9
|
codexTurnInput,
|
|
12
10
|
createCodexAppServerProviderOwner,
|
|
13
|
-
defineCodexRenewalThreadIds,
|
|
14
11
|
deleteCodexAppServerHelperThread,
|
|
15
12
|
deleteCodexAppServerThread,
|
|
16
13
|
ensureCodexAppServerThread,
|
|
17
14
|
inspectCodexAppServerMessageAdmission,
|
|
18
15
|
retireCodexConversationHistory,
|
|
19
16
|
readCodexNativeGoal,
|
|
20
|
-
resumeExactCodexAppServerThread,
|
|
21
17
|
sendCodexAppServerPrompt,
|
|
22
|
-
shellQuote
|
|
23
|
-
startFreshCodexAppServerThread
|
|
18
|
+
shellQuote
|
|
24
19
|
} from "../conversation/codexProvider.js";
|
|
20
|
+
export { assertCodexAuthPreflightReady } from "../conversation/codexAuthPreflight.js";
|
|
21
|
+
export {
|
|
22
|
+
codexRenewalThreadError,
|
|
23
|
+
defineCodexRenewalThreadIds,
|
|
24
|
+
resumeExactCodexAppServerThread,
|
|
25
|
+
startFreshCodexAppServerThread
|
|
26
|
+
} from "../conversation/codexRenewalThreads.js";
|
|
25
27
|
|
|
26
28
|
export { readCodexHistoryRows } from "../conversation/codexHistoryAdapter.js";
|
package/src/server/index.js
CHANGED
|
@@ -15,4 +15,5 @@ export {
|
|
|
15
15
|
export { resolveWorkspaceSlug } from "./lib/resolveWorkspaceSlug.js";
|
|
16
16
|
export { createServiceToolCatalog } from "./lib/serviceToolCatalog.js";
|
|
17
17
|
export { consumeCompletionStream, sanitizeAssistantMessageText } from "./lib/assistantCompletion.js";
|
|
18
|
-
export { runAssistantToolLoop
|
|
18
|
+
export { runAssistantToolLoop } from "./lib/assistantToolLoop.js";
|
|
19
|
+
export { runBoundedAssistantToolLoop, readAssistantResponseEnvelope, readPartialAssistantReply } from "./conversation/boundedToolLoop.js";
|