@jskit-ai/assistant-core 0.2.6 → 0.2.8
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 +18 -7
- package/src/client/browser.js +21 -0
- package/src/client/conversation/AssistantAttachmentQueue.vue +25 -108
- package/src/client/conversation/AssistantComposerActions.vue +1 -1
- package/src/client/conversation/AssistantConversationElement.vue +14 -52
- package/src/client/conversation/AssistantGoalControl.vue +18 -12
- package/src/client/conversation/AssistantPromptInput.vue +6 -91
- package/src/client/conversation/AssistantTranscript.css +505 -0
- package/src/client/conversation/AssistantTranscript.vue +24 -1197
- package/src/client/conversation/attachmentFiles.js +158 -0
- package/src/client/conversation/attachmentQueueItems.js +121 -0
- package/src/client/conversation/attachmentUploadRows.js +265 -0
- package/src/client/conversation/conversationDisplay.js +54 -0
- package/src/client/conversation/messageDelivery.js +13 -9
- package/src/client/conversation/retainedConversation.js +14 -3
- package/src/client/conversation/submitText.js +13 -2
- package/src/client/conversation/transcriptEntries.js +225 -0
- package/src/client/conversation/useAssistantAttachments.js +86 -388
- package/src/client/conversation/useAssistantQuestions.js +16 -3
- package/src/client/conversation/useAssistantSuggestions.js +16 -2
- package/src/client/conversation/useScrollToBottom.js +17 -19
- package/src/client/conversation/useTextareaAutoGrow.js +115 -0
- package/src/client/conversation/useTranscriptScrollFollow.js +525 -0
- package/src/client/lib/assistantApi.js +192 -161
- package/src/client/types.js +110 -0
- package/src/server/conversation/attachments.js +21 -6
- package/src/server/conversation/boundedToolLoop.js +1 -0
- package/src/server/conversation/claudeClient.js +71 -20
- package/src/server/conversation/claudeCommandRun.js +191 -0
- package/src/server/conversation/claudeHistory.js +7 -0
- package/src/server/conversation/claudeNativeTurn.js +708 -0
- package/src/server/conversation/claudeProcess.js +186 -112
- package/src/server/conversation/claudeProcessStart.js +214 -0
- package/src/server/conversation/claudePromptDelivery.js +183 -0
- package/src/server/conversation/claudeSessionCleanup.js +248 -0
- package/src/server/conversation/claudeTools.js +15 -3
- package/src/server/conversation/claudeTurn.js +212 -1388
- package/src/server/conversation/codexAccountReader.js +168 -0
- package/src/server/conversation/codexAuthPreflight.js +5 -4
- package/src/server/conversation/codexClient.js +18 -6
- package/src/server/conversation/codexConfiguration.js +8 -844
- package/src/server/conversation/codexConversationStops.js +377 -0
- package/src/server/conversation/codexConversationStore.js +119 -85
- package/src/server/conversation/codexConversationTurns.js +378 -0
- package/src/server/conversation/codexConversations.js +134 -866
- package/src/server/conversation/codexDetachedConversation.js +203 -0
- package/src/server/conversation/codexDetachedTurn.js +145 -104
- package/src/server/conversation/codexEvents.js +5 -200
- package/src/server/conversation/codexGoalCommands.js +14 -4
- package/src/server/conversation/codexHelperExecution.js +57 -57
- package/src/server/conversation/codexHelperThreadLedger.js +30 -288
- package/src/server/conversation/codexHelperThreadRecord.js +304 -0
- package/src/server/conversation/codexHelperThreadRecovery.js +54 -76
- package/src/server/conversation/codexHelperThreads.js +94 -93
- package/src/server/conversation/codexHistoryAdapter.js +13 -8
- package/src/server/conversation/codexIsolation.js +672 -0
- package/src/server/conversation/codexLiveItems.js +379 -0
- package/src/server/conversation/codexMessageCommands.js +68 -286
- package/src/server/conversation/codexMessageDelivery.js +46 -37
- package/src/server/conversation/codexModelCatalog.js +86 -7
- package/src/server/conversation/codexNotificationObserver.js +81 -88
- package/src/server/conversation/codexNotificationQueue.js +21 -10
- package/src/server/conversation/codexObservationControl.js +105 -89
- package/src/server/conversation/codexOutput.js +84 -419
- package/src/server/conversation/codexProcess.js +29 -1053
- package/src/server/conversation/codexProcessLeader.js +1 -0
- package/src/server/conversation/codexPromptDispatch.js +241 -0
- package/src/server/conversation/codexProvider.js +147 -1807
- package/src/server/conversation/codexProviderAuth.js +348 -0
- package/src/server/conversation/codexProviderInvalidation.js +371 -0
- package/src/server/conversation/codexProviderInventories.js +437 -0
- package/src/server/conversation/codexProviderOwner.js +149 -957
- package/src/server/conversation/codexProviderRelease.js +465 -0
- package/src/server/conversation/codexProviderSelection.js +26 -15
- package/src/server/conversation/codexProviderThreads.js +698 -0
- package/src/server/conversation/codexProviderTurns.js +453 -0
- package/src/server/conversation/codexReasoning.js +32 -23
- package/src/server/conversation/codexRenewalCommands.js +18 -6
- package/src/server/conversation/codexRenewalThreads.js +7 -22
- package/src/server/conversation/codexResultSettlement.js +70 -88
- package/src/server/conversation/codexRunJournal.js +91 -97
- package/src/server/conversation/codexRuntime.js +426 -0
- package/src/server/conversation/codexRuntimeInventory.js +113 -0
- package/src/server/conversation/codexRuntimeLifecycle.js +39 -13
- package/src/server/conversation/codexRuntimeStart.js +431 -0
- package/src/server/conversation/codexRuntimeStop.js +324 -0
- package/src/server/conversation/codexSessionCleanup.js +25 -10
- package/src/server/conversation/codexSessionReconciliation.js +21 -7
- package/src/server/conversation/codexThreadReadiness.js +49 -52
- package/src/server/conversation/codexThreadSnapshot.js +210 -0
- package/src/server/conversation/codexThreadStatus.js +48 -62
- package/src/server/conversation/codexTurn.js +171 -55
- package/src/server/conversation/codexTurnActivity.js +210 -0
- package/src/server/conversation/codexTurnControl.js +44 -48
- package/src/server/conversation/codexTurnEndings.js +610 -0
- package/src/server/conversation/codexTurnRecovery.js +97 -848
- package/src/server/conversation/codexTurnState.js +138 -111
- package/src/server/conversation/completedResponses.js +227 -0
- package/src/server/conversation/configuration.js +2 -2
- package/src/server/conversation/continuity.js +43 -6
- package/src/server/conversation/conversationDelivery.js +394 -0
- package/src/server/conversation/conversationEntries.js +365 -0
- package/src/server/conversation/conversationEvents.js +192 -0
- package/src/server/conversation/conversationGoals.js +187 -0
- package/src/server/conversation/conversationHandles.js +237 -0
- package/src/server/conversation/conversationReads.js +206 -0
- package/src/server/conversation/conversationRefusals.js +58 -0
- package/src/server/conversation/conversationReplacement.js +254 -0
- package/src/server/conversation/conversationSending.js +273 -0
- package/src/server/conversation/conversationStopping.js +121 -0
- package/src/server/conversation/conversationTurnDelivery.js +413 -0
- package/src/server/conversation/conversationTurns.js +511 -0
- package/src/server/conversation/deliveryTiming.js +25 -3
- package/src/server/conversation/fileStorage.js +11 -6
- package/src/server/conversation/hookBridge.js +14 -3
- package/src/server/conversation/index.js +1 -1
- package/src/server/conversation/localExecution.js +82 -625
- package/src/server/conversation/nativeCommandCallbacks.js +10 -0
- package/src/server/conversation/nativeConversationLifecycle.js +379 -0
- package/src/server/conversation/nativeHistoryExport.js +27 -18
- package/src/server/conversation/normalize.js +9 -0
- package/src/server/conversation/openCodeClient.js +295 -366
- package/src/server/conversation/openCodeNativeHistory.js +141 -0
- package/src/server/conversation/openCodeNativeSession.js +79 -0
- package/src/server/conversation/openCodeProcess.js +109 -53
- package/src/server/conversation/openCodeProcessOwner.js +565 -0
- package/src/server/conversation/openCodeRuntime.js +111 -1814
- package/src/server/conversation/openCodeSessionTurns.js +547 -0
- package/src/server/conversation/openCodeTemporaryConversations.js +394 -0
- package/src/server/conversation/openCodeTools.js +28 -14
- package/src/server/conversation/openCodeTurn.js +80 -74
- package/src/server/conversation/openCodeTurnMonitor.js +388 -0
- package/src/server/conversation/processIdentity.js +582 -0
- package/src/server/conversation/provider.js +21 -6
- package/src/server/conversation/providers/api.js +268 -107
- package/src/server/conversation/providers/claude.js +29 -6
- package/src/server/conversation/providers/claudeDriver.js +325 -250
- package/src/server/conversation/providers/codex.js +69 -33
- package/src/server/conversation/providers/codexDriver.js +503 -1081
- package/src/server/conversation/providers/codexDriverConnection.js +331 -0
- package/src/server/conversation/providers/codexDriverTurns.js +464 -0
- package/src/server/conversation/providers/opencode.js +30 -4
- package/src/server/conversation/providers/opencodeDriver.js +321 -659
- package/src/server/conversation/providers/opencodeStandalone.js +475 -0
- package/src/server/conversation/providers/scoped.js +64 -19
- package/src/server/conversation/runtime.js +171 -2863
- package/src/server/conversation/storage.js +91 -50
- package/src/server/conversation/streams.js +15 -2
- package/src/server/conversation/structuredOutput.js +7 -11
- package/src/server/conversation/systemPrompt.js +13 -1
- package/src/server/conversation/tools.js +47 -15
- package/src/server/conversation/transcript.js +49 -20
- package/src/server/hosts/claudeTurn.js +2 -4
- package/src/server/hosts/codexConfiguration.js +6 -8
- package/src/server/hosts/codexEvents.js +4 -2
- package/src/server/hosts/codexProcess.js +1 -1
- package/src/server/hosts/codexProvider.js +11 -9
- package/src/server/hosts/codexTurn.js +3 -3
- package/src/server/hosts/openCodeClient.js +4 -2
- package/src/server/index.js +0 -2
- package/src/server/lib/aiConnectionClient.js +35 -25
- package/src/server/lib/ndjson.js +1 -0
- package/src/server/lib/providers/anthropicClient.js +15 -6
- package/src/server/lib/providers/common.js +10 -6
- package/src/server/lib/providers/openAiCompatibleClient.js +40 -23
- package/src/server/lib/serviceToolCatalog.js +29 -283
- package/src/server/lib/serviceToolEntries.js +270 -0
- package/src/server/types.js +78 -0
- package/src/shared/assistantResource.js +0 -23
- package/src/shared/conversation/answerChoiceSugar.js +2 -14
- package/src/shared/conversation/messageInputSugar.js +18 -0
- package/src/shared/conversation/numberedQuestionSugar.js +6 -16
- package/src/shared/conversation/pagination.js +1 -1
- package/src/shared/conversation/questions.js +1 -0
- package/src/shared/conversation/streaming.js +1 -0
- package/src/shared/index.js +0 -1
- package/src/testing/conversationStorageContract.js +20 -0
- package/test/aiConnectionClient.test.js +42 -1
- package/test/apiConversationDriver.test.js +217 -38
- package/test/assistantBoundedToolLoop.test.js +1 -1
- package/test/claudeConversationSession.test.js +15 -11
- package/test/claudeInstructionResume.test.js +1 -1
- package/test/claudeProcess.test.js +9 -9
- package/test/claudeTurn.test.js +21 -8
- package/test/codexAccountChange.test.js +5 -3
- package/test/codexConversationSession.test.js +22 -21
- package/test/codexEvents.test.js +3 -1
- package/test/codexHelperDeletion.test.js +3 -7
- package/test/codexIsolation.test.js +6 -6
- package/test/codexModelCatalog.test.js +6 -7
- package/test/codexProcess.test.js +3 -3
- package/test/codexThreadSelection.test.js +4 -1
- package/test/codexTurn.test.js +29 -20
- package/test/conversationApiFacilities.test.js +11 -9
- package/test/conversationAttachments.test.js +3 -3
- package/test/conversationConfiguration.test.js +4 -4
- package/test/conversationNativeStorage.test.js +11 -10
- package/test/conversationReplacement.test.js +13 -13
- package/test/conversationRuntime.test.js +9 -5
- package/test/conversationSession.test.js +78 -22
- package/test/conversationTools.test.js +47 -26
- package/test/fileConversationStorage.test.js +10 -3
- package/test/fixtures/codexHelperThreadLedger.js +3 -3
- package/test/frameworkActions.test.js +33 -18
- package/test/localConversationExecution.test.js +4 -4
- package/test/localExecution.test.js +1 -1
- package/test/openCodeConnectionChange.test.js +3 -2
- package/test/openCodeRuntime.test.js +8 -7
- package/test/openCodeTools.test.js +1 -1
- package/test/opencodeConversationSession.test.js +4 -3
- package/test/serviceToolCatalog.test.js +3 -8
- package/src/server/lib/assistantCompletion.js +0 -190
- package/src/server/lib/assistantToolLoop.js +0 -259
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { appendQueryString } from "@jskit-ai/kernel/shared/support";
|
|
2
|
+
import { isRecord } from "@jskit-ai/kernel/shared/support/normalize";
|
|
2
3
|
import { encodeJsonApiResourceQueryObject } from "@jskit-ai/http-runtime/shared";
|
|
3
4
|
import {
|
|
4
5
|
ASSISTANT_CONVERSATIONS_TRANSPORT,
|
|
@@ -9,8 +10,6 @@ import {
|
|
|
9
10
|
normalizeAssistantStreamEventType
|
|
10
11
|
} from "../../shared/index.js";
|
|
11
12
|
|
|
12
|
-
const isRecord = (value) => Boolean(value) && typeof value === "object" && !Array.isArray(value);
|
|
13
|
-
|
|
14
13
|
// A stream error event keeps its code and English message, plus the params and
|
|
15
14
|
// messageCode useJskitText().serverMessage(error) needs to show it in the active language.
|
|
16
15
|
function buildStreamEventError(event) {
|
|
@@ -60,8 +59,8 @@ function normalizeSurfaceHeaderValue(value) {
|
|
|
60
59
|
return String(value || "").trim().toLowerCase();
|
|
61
60
|
}
|
|
62
61
|
|
|
63
|
-
function resolveAssistantRequestHeaders(resolveSurfaceId) {
|
|
64
|
-
if (
|
|
62
|
+
function resolveAssistantRequestHeaders(resolveSurfaceId, sendsSurface) {
|
|
63
|
+
if (!sendsSurface) {
|
|
65
64
|
return null;
|
|
66
65
|
}
|
|
67
66
|
|
|
@@ -88,183 +87,215 @@ function resolveRequiredBasePath(resolveBasePath) {
|
|
|
88
87
|
return resolved;
|
|
89
88
|
}
|
|
90
89
|
|
|
91
|
-
|
|
90
|
+
/** Ignores a stream event when the caller passed no onEvent. */
|
|
91
|
+
function ignoreStreamEvent() {}
|
|
92
|
+
|
|
93
|
+
/**
|
|
94
|
+
* The assistant's HTTP API for one application: its conversations, their messages and goals, its chat stream and its
|
|
95
|
+
* settings, all under the base path `resolveBasePath()` returns.
|
|
96
|
+
* @param {{
|
|
97
|
+
* request: (url: string, options: object) => Promise<any>,
|
|
98
|
+
* requestStream?: (url: string, options: object, handlers: object) => Promise<void>,
|
|
99
|
+
* resolveBasePath: () => string,
|
|
100
|
+
* resolveSurfaceId?: (() => string) | null
|
|
101
|
+
* }} transport
|
|
102
|
+
*/
|
|
103
|
+
function createAssistantApi({ request, requestStream, resolveBasePath, resolveSurfaceId }) {
|
|
104
|
+
// readable-check: allow method-probe - input validation: an application builds its API with the request() it sends through
|
|
92
105
|
if (typeof request !== "function") {
|
|
93
106
|
throw new Error("createAssistantApi requires request().");
|
|
94
107
|
}
|
|
108
|
+
// readable-check: allow method-probe - optional operation: an API built without requestStream has no chat stream, so streamChat() rejects
|
|
109
|
+
const streams = typeof requestStream === "function";
|
|
110
|
+
// readable-check: allow method-probe - optional operation: an API built without resolveSurfaceId sends no surface header
|
|
111
|
+
const sendsSurface = typeof resolveSurfaceId === "function";
|
|
95
112
|
|
|
96
113
|
function requestConversation(conversationId, suffix, options) {
|
|
97
114
|
const basePath = resolveRequiredBasePath(resolveBasePath);
|
|
98
115
|
const encodedId = encodeURIComponent(String(conversationId || "").trim());
|
|
99
|
-
const headers = resolveAssistantRequestHeaders(resolveSurfaceId);
|
|
116
|
+
const headers = resolveAssistantRequestHeaders(resolveSurfaceId, sendsSurface);
|
|
100
117
|
return request(`${basePath}/conversations/${encodedId}${suffix}`, {
|
|
101
118
|
...options,
|
|
102
119
|
...(headers ? { headers } : {})
|
|
103
120
|
});
|
|
104
121
|
}
|
|
105
122
|
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
...(Object.hasOwn(payload, "steer") ? { steer: payload.steer } : {})
|
|
123
|
-
}
|
|
124
|
-
});
|
|
125
|
-
},
|
|
126
|
-
|
|
127
|
-
cancelConversation(conversationId, { signal } = {}) {
|
|
128
|
-
return requestConversation(conversationId, "/cancel", { method: "POST", signal });
|
|
129
|
-
},
|
|
130
|
-
|
|
131
|
-
inspectConversationDelivery(conversationId, messageId, { signal } = {}) {
|
|
132
|
-
const encodedMessageId = encodeURIComponent(String(messageId || "").trim());
|
|
133
|
-
return requestConversation(conversationId, `/deliveries/${encodedMessageId}/inspect`, { method: "POST", signal });
|
|
134
|
-
},
|
|
135
|
-
|
|
136
|
-
cancelConversationDelivery(conversationId, messageId, { signal } = {}) {
|
|
137
|
-
const encodedMessageId = encodeURIComponent(String(messageId || "").trim());
|
|
138
|
-
return requestConversation(conversationId, `/deliveries/${encodedMessageId}/cancel`, { method: "POST", signal });
|
|
139
|
-
},
|
|
140
|
-
|
|
141
|
-
readConversationGoal(conversationId, { signal } = {}) {
|
|
142
|
-
return requestConversation(conversationId, "/goal", { method: "GET", signal });
|
|
143
|
-
},
|
|
144
|
-
|
|
145
|
-
updateConversationGoal(conversationId, payload, { signal } = {}) {
|
|
146
|
-
const body = {};
|
|
147
|
-
for (const key of ["action", "expectedSegmentId", "expectedGoalId", "messageId", "objective", "tokenBudget", "attachmentIds"]) {
|
|
148
|
-
if (Object.hasOwn(payload, key)) body[key] = payload[key];
|
|
123
|
+
function readConversation(conversationId, { signal, beforeTurnId, limit } = {}) {
|
|
124
|
+
const params = new URLSearchParams();
|
|
125
|
+
appendQueryParam(params, "beforeTurnId", beforeTurnId);
|
|
126
|
+
appendQueryParam(params, "limit", limit);
|
|
127
|
+
return requestConversation(conversationId, params.size ? `?${params}` : "", { method: "GET", signal });
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function sendConversationMessage(conversationId, payload, { signal } = {}) {
|
|
131
|
+
return requestConversation(conversationId, "/messages", {
|
|
132
|
+
method: "POST", signal,
|
|
133
|
+
body: {
|
|
134
|
+
messageId: payload.messageId,
|
|
135
|
+
text: payload.text,
|
|
136
|
+
...(Object.hasOwn(payload, "data") ? { data: payload.data } : {}),
|
|
137
|
+
...(Object.hasOwn(payload, "attachmentIds") ? { attachmentIds: payload.attachmentIds } : {}),
|
|
138
|
+
...(Object.hasOwn(payload, "steer") ? { steer: payload.steer } : {})
|
|
149
139
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
140
|
+
});
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function cancelConversation(conversationId, { signal } = {}) {
|
|
144
|
+
return requestConversation(conversationId, "/cancel", { method: "POST", signal });
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function inspectConversationDelivery(conversationId, messageId, { signal } = {}) {
|
|
148
|
+
const encodedMessageId = encodeURIComponent(String(messageId || "").trim());
|
|
149
|
+
return requestConversation(conversationId, `/deliveries/${encodedMessageId}/inspect`, { method: "POST", signal });
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function cancelConversationDelivery(conversationId, messageId, { signal } = {}) {
|
|
153
|
+
const encodedMessageId = encodeURIComponent(String(messageId || "").trim());
|
|
154
|
+
return requestConversation(conversationId, `/deliveries/${encodedMessageId}/cancel`, { method: "POST", signal });
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function readConversationGoal(conversationId, { signal } = {}) {
|
|
158
|
+
return requestConversation(conversationId, "/goal", { method: "GET", signal });
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
function updateConversationGoal(conversationId, payload, { signal } = {}) {
|
|
162
|
+
const body = {};
|
|
163
|
+
for (const key of ["action", "expectedSegmentId", "expectedGoalId", "messageId", "objective", "tokenBudget", "attachmentIds"]) {
|
|
164
|
+
if (Object.hasOwn(payload, key)) body[key] = payload[key];
|
|
165
|
+
}
|
|
166
|
+
return requestConversation(conversationId, "/goal", { method: "POST", signal, body });
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function configureConversation(conversationId, configuration, { signal } = {}) {
|
|
170
|
+
return requestConversation(conversationId, "/configuration", {
|
|
171
|
+
method: "PATCH", signal, body: { configuration }
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function selectConversation(conversationId, selection, { signal } = {}) {
|
|
176
|
+
return requestConversation(conversationId, "/selection", {
|
|
177
|
+
method: "POST", signal, body: { selection }
|
|
178
|
+
});
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function replaceConversation(conversationId, replacement, { signal } = {}) {
|
|
182
|
+
return requestConversation(conversationId, "/replacement", {
|
|
183
|
+
method: "POST", signal, body: { replacement }
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
async function streamChat(payload, { signal, onEvent = ignoreStreamEvent, onMalformedLine, rejectOnErrorEvent = true } = {}) {
|
|
188
|
+
if (!streams) throw new TypeError("streamChat requires requestStream().");
|
|
189
|
+
const basePath = resolveRequiredBasePath(resolveBasePath);
|
|
190
|
+
let streamEventError = null;
|
|
191
|
+
const requestHeaders = resolveAssistantRequestHeaders(resolveSurfaceId, sendsSurface);
|
|
192
|
+
|
|
193
|
+
const streamHandlers = {
|
|
194
|
+
onEvent(event) {
|
|
195
|
+
const eventType = normalizeAssistantStreamEventType(event?.type, "");
|
|
196
|
+
if (rejectOnErrorEvent && eventType === ASSISTANT_STREAM_EVENT_TYPES.ERROR && !streamEventError) {
|
|
197
|
+
streamEventError = buildStreamEventError(event);
|
|
187
198
|
}
|
|
199
|
+
|
|
200
|
+
onEvent(event);
|
|
201
|
+
}
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
if (typeof onMalformedLine === "function") {
|
|
205
|
+
streamHandlers.onMalformedLine = (line, parseError) => {
|
|
206
|
+
onMalformedLine(line, parseError);
|
|
188
207
|
};
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
await requestStream(
|
|
211
|
+
`${basePath}/chat/stream`,
|
|
212
|
+
{
|
|
213
|
+
method: "POST",
|
|
214
|
+
...(requestHeaders ? { headers: requestHeaders } : {}),
|
|
215
|
+
body: payload,
|
|
216
|
+
signal
|
|
217
|
+
},
|
|
218
|
+
streamHandlers
|
|
219
|
+
);
|
|
220
|
+
|
|
221
|
+
if (streamEventError) {
|
|
222
|
+
throw streamEventError;
|
|
223
|
+
}
|
|
224
|
+
}
|
|
189
225
|
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
226
|
+
function listConversations(query = {}) {
|
|
227
|
+
const basePath = resolveRequiredBasePath(resolveBasePath);
|
|
228
|
+
const params = createAssistantQueryParams(query, ASSISTANT_CONVERSATIONS_TRANSPORT);
|
|
229
|
+
const requestHeaders = resolveAssistantRequestHeaders(resolveSurfaceId, sendsSurface);
|
|
230
|
+
|
|
231
|
+
return request(
|
|
232
|
+
appendQueryString(`${basePath}/conversations`, params.toString()),
|
|
233
|
+
{
|
|
234
|
+
...(requestHeaders ? { headers: requestHeaders } : {}),
|
|
235
|
+
transport: ASSISTANT_CONVERSATIONS_TRANSPORT
|
|
194
236
|
}
|
|
237
|
+
);
|
|
238
|
+
}
|
|
195
239
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
if (streamEventError) {
|
|
208
|
-
throw streamEventError;
|
|
240
|
+
function getConversationMessages(conversationId, query = {}) {
|
|
241
|
+
const basePath = resolveRequiredBasePath(resolveBasePath);
|
|
242
|
+
const encodedConversationId = encodeURIComponent(String(conversationId || "").trim());
|
|
243
|
+
const params = createAssistantQueryParams(query, ASSISTANT_CONVERSATION_MESSAGES_TRANSPORT);
|
|
244
|
+
const requestHeaders = resolveAssistantRequestHeaders(resolveSurfaceId, sendsSurface);
|
|
245
|
+
|
|
246
|
+
return request(
|
|
247
|
+
appendQueryString(`${basePath}/conversations/${encodedConversationId}/messages`, params.toString()),
|
|
248
|
+
{
|
|
249
|
+
...(requestHeaders ? { headers: requestHeaders } : {}),
|
|
250
|
+
transport: ASSISTANT_CONVERSATION_MESSAGES_TRANSPORT
|
|
209
251
|
}
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
{
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
return request(
|
|
259
|
-
`${basePath}/settings`,
|
|
260
|
-
{
|
|
261
|
-
method: "PATCH",
|
|
262
|
-
...(requestHeaders ? { headers: requestHeaders } : {}),
|
|
263
|
-
body: payload,
|
|
264
|
-
transport: ASSISTANT_SETTINGS_UPDATE_TRANSPORT
|
|
265
|
-
}
|
|
266
|
-
);
|
|
267
|
-
}
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function getSettings() {
|
|
256
|
+
const basePath = resolveRequiredBasePath(resolveBasePath);
|
|
257
|
+
const requestHeaders = resolveAssistantRequestHeaders(resolveSurfaceId, sendsSurface);
|
|
258
|
+
|
|
259
|
+
return request(
|
|
260
|
+
`${basePath}/settings`,
|
|
261
|
+
{
|
|
262
|
+
...(requestHeaders ? { headers: requestHeaders } : {}),
|
|
263
|
+
transport: ASSISTANT_SETTINGS_TRANSPORT
|
|
264
|
+
}
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
function updateSettings(payload = {}) {
|
|
269
|
+
const basePath = resolveRequiredBasePath(resolveBasePath);
|
|
270
|
+
const requestHeaders = resolveAssistantRequestHeaders(resolveSurfaceId, sendsSurface);
|
|
271
|
+
|
|
272
|
+
return request(
|
|
273
|
+
`${basePath}/settings`,
|
|
274
|
+
{
|
|
275
|
+
method: "PATCH",
|
|
276
|
+
...(requestHeaders ? { headers: requestHeaders } : {}),
|
|
277
|
+
body: payload,
|
|
278
|
+
transport: ASSISTANT_SETTINGS_UPDATE_TRANSPORT
|
|
279
|
+
}
|
|
280
|
+
);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
return Object.freeze({
|
|
284
|
+
readConversation,
|
|
285
|
+
sendConversationMessage,
|
|
286
|
+
cancelConversation,
|
|
287
|
+
inspectConversationDelivery,
|
|
288
|
+
cancelConversationDelivery,
|
|
289
|
+
readConversationGoal,
|
|
290
|
+
updateConversationGoal,
|
|
291
|
+
configureConversation,
|
|
292
|
+
selectConversation,
|
|
293
|
+
replaceConversation,
|
|
294
|
+
streamChat,
|
|
295
|
+
listConversations,
|
|
296
|
+
getConversationMessages,
|
|
297
|
+
getSettings,
|
|
298
|
+
updateSettings
|
|
268
299
|
});
|
|
269
300
|
}
|
|
270
301
|
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
// The values the assistant's client code hands out and receives across files: what its composables and factories
|
|
2
|
+
// return, and the contracts an application implements to supply uploads, a goal and a whole conversation.
|
|
3
|
+
|
|
4
|
+
/** @typedef {ReturnType<typeof import("./conversation/useAssistantAttachments.js").useAssistantAttachments>} AssistantAttachments */
|
|
5
|
+
/** @typedef {ReturnType<typeof import("./conversation/useAssistantSuggestions.js").useAssistantSuggestions>} AssistantSuggestions */
|
|
6
|
+
/** @typedef {ReturnType<typeof import("./conversation/useAssistantQuestions.js").useAssistantQuestions>} AssistantQuestions */
|
|
7
|
+
/** @typedef {ReturnType<typeof import("./conversation/messageDelivery.js").createAssistantMessageDelivery>} AssistantMessageDelivery */
|
|
8
|
+
/** @typedef {ReturnType<typeof import("./lib/assistantApi.js").createAssistantApi>} AssistantApi */
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* What the application's storage returns for one uploaded file, completed by the queue with its client id, session
|
|
12
|
+
* and size.
|
|
13
|
+
* @typedef {object} AssistantAttachmentReceipt
|
|
14
|
+
* @property {string} attachmentId
|
|
15
|
+
* @property {string} clientId
|
|
16
|
+
* @property {string} fileName
|
|
17
|
+
* @property {string} sessionId
|
|
18
|
+
* @property {number} size
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Uploads one file for a session. It returns the stored file's receipt, as a promise, or as `{ promise }` with the
|
|
23
|
+
* `abort()` or `cancel()` that stops the upload.
|
|
24
|
+
* @typedef {(
|
|
25
|
+
* sessionId: string,
|
|
26
|
+
* file: File,
|
|
27
|
+
* options: { onProgress: (progress: unknown, total?: number) => void, signal: AbortSignal }
|
|
28
|
+
* ) => unknown} AssistantAttachmentUpload
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Deletes an uploaded file no message will send. A response with `ok: false` reports a failed deletion.
|
|
33
|
+
* @typedef {(sessionId: string, attachmentId: string) => unknown} AssistantAttachmentDeletion
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The goal a conversation is working towards, as AssistantGoalControl shows and changes it.
|
|
38
|
+
* @typedef {object} AssistantGoalState
|
|
39
|
+
* @property {boolean} [enabled] False hides the control's menu and stops its clock.
|
|
40
|
+
* @property {boolean} [loading]
|
|
41
|
+
* @property {boolean} [available] False while the conversation offers no goals.
|
|
42
|
+
* @property {string} [action] "set" while a new goal is starting.
|
|
43
|
+
* @property {boolean} [working]
|
|
44
|
+
* @property {boolean} [pending]
|
|
45
|
+
* @property {boolean} [stopPending]
|
|
46
|
+
* @property {boolean} [pauseBlocked] True when a blocked goal can still be paused.
|
|
47
|
+
* @property {boolean} [pauseInterruptsTurn]
|
|
48
|
+
* @property {boolean} [cancelInterruptsTurn]
|
|
49
|
+
* @property {boolean} [tokenBudgetSupported]
|
|
50
|
+
* @property {string} [error]
|
|
51
|
+
* @property {{ status: string, objective?: string, elapsedSeconds?: number, sampledAt?: number, createdAt?: string } | null} [goal]
|
|
52
|
+
* @property {((input: { objective: string, tokenBudget?: number }) => unknown) | null} [set]
|
|
53
|
+
* Starts a goal; absent while starting one is not allowed, so Start stays disabled.
|
|
54
|
+
* @property {() => unknown} [pause]
|
|
55
|
+
* @property {() => unknown} [resume]
|
|
56
|
+
* @property {() => unknown} [cancel]
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* What an application supplies to AssistantConversationElement. Only `conversation` is required; each other part
|
|
61
|
+
* adds its controls when present.
|
|
62
|
+
* @typedef {object} AssistantConversationAdapter
|
|
63
|
+
* @property {Record<string, any> & { turns: any[] }} conversation The transcript's props, plus its preview messages
|
|
64
|
+
* (`previewMessage`, `previewMessages`) and the reply still streaming (`interimReply`).
|
|
65
|
+
* @property {Record<string, any>} [composer] The prompt input's props, plus `draft`, `canSend`, `canStop`,
|
|
66
|
+
* `stopPending`, `queueWhileSending` and the `payload` every message carries.
|
|
67
|
+
* @property {AssistantConversationActions} [actions]
|
|
68
|
+
* @property {AssistantMessageDelivery} [delivery] Present when the element sends and tracks messages itself.
|
|
69
|
+
* @property {import("vue").UnwrapNestedRefs<AssistantAttachments> & {
|
|
70
|
+
* enabled?: boolean,
|
|
71
|
+
* accept?: string,
|
|
72
|
+
* open?: (attachment: unknown) => void
|
|
73
|
+
* }} [attachments]
|
|
74
|
+
* @property {import("vue").UnwrapNestedRefs<AssistantSuggestions>} [suggestions]
|
|
75
|
+
* @property {import("vue").UnwrapNestedRefs<AssistantQuestions>} [questions]
|
|
76
|
+
* @property {AssistantModelChoices} [models]
|
|
77
|
+
* @property {AssistantGoalState} [goal]
|
|
78
|
+
* @property {{ label: string }} [activity] Replaces the sending and working status line.
|
|
79
|
+
*/
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* What the application does for the conversation. The element sends, resends, edits and cancels through its
|
|
83
|
+
* delivery when the application supplies no action of its own; it leaves out the controls of the others.
|
|
84
|
+
* @typedef {object} AssistantConversationActions
|
|
85
|
+
* @property {(draft: string) => void} [setDraft]
|
|
86
|
+
* @property {(input: { configuration: object, attachments: object[] }) => unknown} [submit]
|
|
87
|
+
* @property {(messageId: string) => unknown} [resend]
|
|
88
|
+
* @property {(messageId: string) => unknown} [cancel]
|
|
89
|
+
* @property {(messageId: string) => unknown} [edit]
|
|
90
|
+
* @property {() => unknown} [stop]
|
|
91
|
+
* @property {(messageId: string) => unknown} [checkDelivery]
|
|
92
|
+
* @property {(request: { complete: (result?: { changed?: boolean }) => void }) => unknown} [loadMore]
|
|
93
|
+
* @property {() => unknown} [reload]
|
|
94
|
+
* @property {(link: unknown) => unknown} [openLink]
|
|
95
|
+
* @property {(configuration: object) => unknown} [updateConfiguration]
|
|
96
|
+
*/
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* The model menu's rows and choices: AssistantModelControl's props, and what applying or changing them does.
|
|
100
|
+
* @typedef {Record<string, any> & {
|
|
101
|
+
* enabled?: boolean,
|
|
102
|
+
* apply: () => unknown,
|
|
103
|
+
* selectModel?: (modelId: string) => unknown,
|
|
104
|
+
* selectProvider?: (providerId: string) => unknown,
|
|
105
|
+
* selectVariant?: (variantId: string) => unknown,
|
|
106
|
+
* reload?: () => unknown
|
|
107
|
+
* }} AssistantModelChoices
|
|
108
|
+
*/
|
|
109
|
+
|
|
110
|
+
export {};
|
|
@@ -1,11 +1,20 @@
|
|
|
1
|
+
import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
|
|
1
2
|
import path from "node:path";
|
|
2
3
|
import { ASSISTANT_ATTACHMENT_ID_MAX_LENGTH, ASSISTANT_ATTACHMENT_MAX_ITEMS, conversationAttachmentManifest } from "../../shared/conversation/attachments.js";
|
|
3
4
|
|
|
4
5
|
// messageCode names the sentence; the shared code stays for callers.
|
|
5
6
|
function invalid(message, { messageCode, messageParams = {} }) {
|
|
6
|
-
return
|
|
7
|
+
return new AppError(400, message, { code: "conversation_invalid_attachments", messageCode, messageParams });
|
|
7
8
|
}
|
|
8
9
|
|
|
10
|
+
/** The attachments of an application that offers none: every file it is asked to resolve is refused. */
|
|
11
|
+
export const NO_ATTACHMENTS = Object.freeze({
|
|
12
|
+
resolve() {
|
|
13
|
+
throw new AppError(400, "File attachments are not configured for this conversation.", { code: "conversation_attachments_not_configured",
|
|
14
|
+
messageCode: "assistant.conversation.attachmentsNotConfigured" });
|
|
15
|
+
}
|
|
16
|
+
});
|
|
17
|
+
|
|
9
18
|
export function conversationAttachmentIds(value = []) {
|
|
10
19
|
if (!Array.isArray(value) || value.length > ASSISTANT_ATTACHMENT_MAX_ITEMS ||
|
|
11
20
|
value.some(id => typeof id !== "string" || !id.trim() || id.length > ASSISTANT_ATTACHMENT_ID_MAX_LENGTH) ||
|
|
@@ -17,15 +26,21 @@ export function conversationAttachmentIds(value = []) {
|
|
|
17
26
|
return [...value];
|
|
18
27
|
}
|
|
19
28
|
|
|
20
|
-
/**
|
|
29
|
+
/**
|
|
30
|
+
* Resolve app-owned files for one dispatch. Only receipts enter the transcript.
|
|
31
|
+
* @param {object} options
|
|
32
|
+
* @param {import("../types.js").ConversationAttachments} options.attachments The application's resolver, or NO_ATTACHMENTS.
|
|
33
|
+
* @param {object} options.context
|
|
34
|
+
* @param {string} options.conversationId
|
|
35
|
+
* @param {AbortSignal} options.signal
|
|
36
|
+
* @param {() => Promise<void>} options.authorize Rechecks admission before each resolver call.
|
|
37
|
+
* @param {readonly string[]} options.types The file types the engine accepts.
|
|
38
|
+
* @param {number} options.maximumBytes
|
|
39
|
+
*/
|
|
21
40
|
export function createConversationAttachmentReader({ attachments, context, conversationId, signal, authorize, types, maximumBytes }) {
|
|
22
41
|
let bytes = 0;
|
|
23
42
|
return async references => {
|
|
24
43
|
if (!references?.length) return { attachments: [], content: [], localFiles: [], attachmentManifest: "" };
|
|
25
|
-
if (typeof attachments?.resolve !== "function") {
|
|
26
|
-
throw Object.assign(new Error("File attachments are not configured for this conversation."), { code: "conversation_unsupported", statusCode: 400,
|
|
27
|
-
messageCode: "assistant.conversation.attachmentsNotConfigured", messageParams: {} });
|
|
28
|
-
}
|
|
29
44
|
const receipts = [];
|
|
30
45
|
const content = [];
|
|
31
46
|
const localFiles = [];
|
|
@@ -109,6 +109,7 @@ async function runBoundedAssistantToolLoop({ prompt, signal, policy, complete, o
|
|
|
109
109
|
if (decision !== "end") throw new TypeError("Invalid completed-response settlement decision.");
|
|
110
110
|
return response.text;
|
|
111
111
|
}
|
|
112
|
+
// readable-check: allow method-probe - input validation: an application's completed response carries its durable tools
|
|
112
113
|
if (typeof result?.tools?.execute !== "function") {
|
|
113
114
|
throw new TypeError("Completed application operations require the current durable tools.");
|
|
114
115
|
}
|