@jskit-ai/assistant-core 0.2.7 → 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 +13 -10
- 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
|
@@ -64,32 +64,42 @@ export function createAiConnectionClient(connection, { fetch, timeoutMs = 120_00
|
|
|
64
64
|
abortSignal: signal, timeout: timeoutMs, maxRetries: 0, temperature, maxOutputTokens, reasoning: effort
|
|
65
65
|
};
|
|
66
66
|
}
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
}
|
|
67
|
+
|
|
68
|
+
async function createChatCompletion(input) {
|
|
69
|
+
const result = await generateText(options(input));
|
|
70
|
+
return { choices: [{ message: { role: "assistant", content: result.text,
|
|
71
|
+
tool_calls: result.toolCalls.map(call => ({ id: call.toolCallId, type: "function",
|
|
72
|
+
function: { name: call.toolName, arguments: JSON.stringify(call.input) } })) }, finish_reason: result.finishReason }] };
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async function* createChatCompletionStream(input) {
|
|
76
|
+
const result = streamText({ ...options(input), onError() {} });
|
|
77
|
+
let toolIndex = 0;
|
|
78
|
+
for await (const part of result.fullStream) {
|
|
79
|
+
if (part.type === "error") throw part.error;
|
|
80
|
+
if (part.type === "abort") throw input?.signal?.reason || new Error("Assistant request was aborted.");
|
|
81
|
+
if (part.type === "text-delta") yield { choices: [{ delta: { content: part.text } }] };
|
|
82
|
+
if (part.type === "finish") yield { choices: [{ delta: {}, finish_reason: part.finishReason }], usage: part.totalUsage };
|
|
83
|
+
if (part.type === "tool-call") {
|
|
84
|
+
// Only the application catalogue can reject an unavailable action
|
|
85
|
+
// and persist its no-effect receipt. Other invalid calls stay errors.
|
|
86
|
+
const unavailable = reportUnavailableToolCalls === true && NoSuchToolError.isInstance(part.error) &&
|
|
87
|
+
!part.providerExecuted && part.input !== null && typeof part.input === "object" && !Array.isArray(part.input);
|
|
88
|
+
if (part.invalid && !unavailable) throw part.error;
|
|
89
|
+
yield { choices: [{ delta: { tool_calls: [{ index: toolIndex++, id: part.toolCallId,
|
|
90
|
+
function: { name: part.toolName, arguments: JSON.stringify(part.input) } }] } }] };
|
|
92
91
|
}
|
|
93
92
|
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return Object.freeze({
|
|
96
|
+
enabled: true,
|
|
97
|
+
supportsAttachments: true,
|
|
98
|
+
provider: connection.providerId,
|
|
99
|
+
defaultModel: connection.model,
|
|
100
|
+
// The AI SDK model itself, for the agent engine's loop.
|
|
101
|
+
model,
|
|
102
|
+
createChatCompletion,
|
|
103
|
+
createChatCompletionStream
|
|
94
104
|
});
|
|
95
105
|
}
|
package/src/server/lib/ndjson.js
CHANGED
|
@@ -7,6 +7,7 @@ function setNdjsonHeaders(reply) {
|
|
|
7
7
|
reply.header("Cache-Control", "no-cache");
|
|
8
8
|
reply.header("X-Accel-Buffering", "no");
|
|
9
9
|
|
|
10
|
+
// readable-check: allow method-probe - platform object: a Fastify reply's raw Node response, when it has one
|
|
10
11
|
if (reply?.raw && typeof reply.raw.setHeader === "function") {
|
|
11
12
|
reply.raw.setHeader("Content-Type", NDJSON_CONTENT_TYPE);
|
|
12
13
|
reply.raw.setHeader("Cache-Control", "no-cache");
|
|
@@ -297,6 +297,10 @@ async function fetchAnthropicMessage({
|
|
|
297
297
|
}
|
|
298
298
|
}
|
|
299
299
|
|
|
300
|
+
/**
|
|
301
|
+
* An assistant client for Anthropic's Messages API, or a disabled client without a key.
|
|
302
|
+
* @param {{ enabled?: boolean, apiKey?: string, baseUrl?: string, model?: string, timeoutMs?: number }} [options]
|
|
303
|
+
*/
|
|
300
304
|
function createAnthropicClient({
|
|
301
305
|
enabled = true,
|
|
302
306
|
apiKey = "",
|
|
@@ -339,17 +343,22 @@ function createAnthropicClient({
|
|
|
339
343
|
};
|
|
340
344
|
}
|
|
341
345
|
|
|
346
|
+
const streaming = createAiConnectionClient({
|
|
347
|
+
providerId: "anthropic", sdkPackage: "@ai-sdk/anthropic", apiKey: normalizedApiKey,
|
|
348
|
+
model: normalizedModel, baseURL: `${normalizedBaseUrl}/v1`
|
|
349
|
+
}, { timeoutMs, maxOutputTokens: DEFAULT_ANTHROPIC_MAX_OUTPUT_LIMIT });
|
|
350
|
+
|
|
351
|
+
function createChatCompletionStream(input) {
|
|
352
|
+
return streaming.createChatCompletionStream({ temperature: 0.2, ...input });
|
|
353
|
+
}
|
|
354
|
+
|
|
342
355
|
return Object.freeze({
|
|
343
356
|
enabled: true,
|
|
344
357
|
provider: "anthropic",
|
|
345
358
|
defaultModel: normalizedModel,
|
|
359
|
+
model: streaming.model,
|
|
346
360
|
createChatCompletion: createCompletion,
|
|
347
|
-
createChatCompletionStream
|
|
348
|
-
return createAiConnectionClient({
|
|
349
|
-
providerId: "anthropic", sdkPackage: "@ai-sdk/anthropic", apiKey: normalizedApiKey,
|
|
350
|
-
model: normalizedModel, baseURL: `${normalizedBaseUrl}/v1`
|
|
351
|
-
}, { timeoutMs, maxOutputTokens: DEFAULT_ANTHROPIC_MAX_OUTPUT_LIMIT }).createChatCompletionStream({ temperature: 0.2, ...input });
|
|
352
|
-
}
|
|
361
|
+
createChatCompletionStream
|
|
353
362
|
});
|
|
354
363
|
}
|
|
355
364
|
|
|
@@ -61,16 +61,20 @@ function createDisabledClient({ provider = DEFAULT_AI_PROVIDER, model = "" } = {
|
|
|
61
61
|
throw new AppError(503, "Assistant provider is not configured.", { code: "assistant.provider.notConfigured" });
|
|
62
62
|
};
|
|
63
63
|
|
|
64
|
+
async function createChatCompletion() {
|
|
65
|
+
disabledError();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
async function createChatCompletionStream() {
|
|
69
|
+
disabledError();
|
|
70
|
+
}
|
|
71
|
+
|
|
64
72
|
return Object.freeze({
|
|
65
73
|
enabled: false,
|
|
66
74
|
provider,
|
|
67
75
|
defaultModel: model,
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
},
|
|
71
|
-
async createChatCompletionStream() {
|
|
72
|
-
disabledError();
|
|
73
|
-
}
|
|
76
|
+
createChatCompletion,
|
|
77
|
+
createChatCompletionStream
|
|
74
78
|
});
|
|
75
79
|
}
|
|
76
80
|
|
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import OpenAI from "openai";
|
|
2
|
+
import { createOpenAI } from "@ai-sdk/openai";
|
|
3
|
+
import { chatModel } from "@jskit-ai/agent/server";
|
|
2
4
|
import { normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
3
5
|
import {
|
|
4
6
|
createDisabledClient,
|
|
@@ -7,6 +9,11 @@ import {
|
|
|
7
9
|
normalizeTimeoutMs
|
|
8
10
|
} from "./common.js";
|
|
9
11
|
|
|
12
|
+
/**
|
|
13
|
+
* An assistant client for an OpenAI-compatible chat completions API, or a disabled client without a key.
|
|
14
|
+
* @param {{ enabled?: boolean, apiKey?: string, baseUrl?: string, model?: string, defaultModel?: string, provider?: string,
|
|
15
|
+
* timeoutMs?: number }} [options]
|
|
16
|
+
*/
|
|
10
17
|
function createOpenAiCompatibleClient({
|
|
11
18
|
enabled = true,
|
|
12
19
|
apiKey = "",
|
|
@@ -36,33 +43,43 @@ function createOpenAiCompatibleClient({
|
|
|
36
43
|
timeout: normalizeTimeoutMs(timeoutMs)
|
|
37
44
|
});
|
|
38
45
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
46
|
+
const baseURL = normalizedBaseUrl || undefined;
|
|
47
|
+
|
|
48
|
+
async function createChatCompletion({ messages = [], tools = [], temperature = 0, signal } = {}) {
|
|
49
|
+
const requestPayload = {
|
|
50
|
+
model: normalizedModel,
|
|
51
|
+
messages,
|
|
52
|
+
...(Array.isArray(tools) && tools.length > 0 ? { tools } : {}),
|
|
53
|
+
temperature,
|
|
54
|
+
stream: false
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
return client.chat.completions.create(requestPayload, signal ? { signal } : undefined);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
async function createChatCompletionStream({ messages = [], tools = [], signal, temperature = 0.2 } = {}) {
|
|
61
|
+
return client.chat.completions.create(
|
|
62
|
+
{
|
|
45
63
|
model: normalizedModel,
|
|
46
64
|
messages,
|
|
47
|
-
|
|
65
|
+
tools,
|
|
48
66
|
temperature,
|
|
49
|
-
stream:
|
|
50
|
-
}
|
|
67
|
+
stream: true
|
|
68
|
+
},
|
|
69
|
+
signal ? { signal } : undefined
|
|
70
|
+
);
|
|
71
|
+
}
|
|
51
72
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
},
|
|
63
|
-
signal ? { signal } : undefined
|
|
64
|
-
);
|
|
65
|
-
}
|
|
73
|
+
return Object.freeze({
|
|
74
|
+
enabled: true,
|
|
75
|
+
provider: normalizedProvider,
|
|
76
|
+
defaultModel: normalizedModel,
|
|
77
|
+
// The same endpoint as an AI SDK model, for the agent engine's loop.
|
|
78
|
+
model: normalizedProvider === "deepseek"
|
|
79
|
+
? chatModel({ apiKey: normalizedApiKey, model: normalizedModel, baseURL })
|
|
80
|
+
: createOpenAI({ apiKey: normalizedApiKey, baseURL }).chat(normalizedModel),
|
|
81
|
+
createChatCompletion,
|
|
82
|
+
createChatCompletionStream
|
|
66
83
|
});
|
|
67
84
|
}
|
|
68
85
|
|
|
@@ -1,16 +1,13 @@
|
|
|
1
|
+
import { AppError } from "@jskit-ai/kernel/server/runtime/errors";
|
|
1
2
|
import { requireAuth } from "@jskit-ai/kernel/server/runtime";
|
|
2
3
|
import { normalizeSurfaceId } from "@jskit-ai/kernel/shared/surface/registry";
|
|
3
4
|
import { normalizePositiveInteger, normalizeText } from "@jskit-ai/kernel/shared/support/normalize";
|
|
4
|
-
import {
|
|
5
|
-
resolveSchemaTransportSchemaDefinition,
|
|
6
|
-
validateSchemaPayload
|
|
7
|
-
} from "@jskit-ai/kernel/shared/validators";
|
|
5
|
+
import { validateSchemaPayload } from "@jskit-ai/kernel/shared/validators";
|
|
8
6
|
import { resolveWorkspaceSlug } from "./resolveWorkspaceSlug.js";
|
|
7
|
+
import { AUTOMATION_CHANNEL, normalizePermissionSpec, resolveActionToolEntries } from "./serviceToolEntries.js";
|
|
9
8
|
|
|
10
|
-
const AUTOMATION_CHANNEL = "automation";
|
|
11
9
|
const DEFAULT_MAX_DIRECT_TOOLS = 32;
|
|
12
10
|
const MAX_ALWAYS_AVAILABLE_TOOLS = 8;
|
|
13
|
-
const MAX_PREFLIGHT_INTENTS_PER_TOOL = 8;
|
|
14
11
|
const DEFAULT_DISCOVERY_PAGE_SIZE = 10;
|
|
15
12
|
const MAX_DISCOVERY_PAGE_SIZE = 20;
|
|
16
13
|
const DEFAULT_MAX_TOOL_ARGUMENT_BYTES = 32 * 1024;
|
|
@@ -143,122 +140,6 @@ const DISCOVERY_TOOL_DESCRIPTORS = Object.freeze([
|
|
|
143
140
|
})
|
|
144
141
|
]);
|
|
145
142
|
|
|
146
|
-
function normalizePreflightIntents(value) {
|
|
147
|
-
const source = Array.isArray(value) ? value : [value];
|
|
148
|
-
const normalized = source
|
|
149
|
-
.map((entry) => normalizeText(entry).toLowerCase())
|
|
150
|
-
.filter(Boolean);
|
|
151
|
-
return Object.freeze([...new Set(normalized)].slice(0, MAX_PREFLIGHT_INTENTS_PER_TOOL));
|
|
152
|
-
}
|
|
153
|
-
|
|
154
|
-
function normalizeAssistantExtension(value) {
|
|
155
|
-
const source = value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
156
|
-
if (source.transformResult != null && typeof source.transformResult !== "function") {
|
|
157
|
-
throw new TypeError("extensions.assistant.transformResult must be a function when provided.");
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
return Object.freeze({
|
|
161
|
-
description: normalizeText(source.description),
|
|
162
|
-
exclude: normalizeText(source.exclude),
|
|
163
|
-
alwaysAvailable: source.alwaysAvailable === true,
|
|
164
|
-
preflight: normalizePreflightIntents(source.preflight),
|
|
165
|
-
output: Object.hasOwn(source, "output") ? source.output : null,
|
|
166
|
-
transformResult: typeof source.transformResult === "function" ? source.transformResult : null
|
|
167
|
-
});
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
function normalizeAssistantActionExtension(action = {}) {
|
|
171
|
-
const source = action && typeof action === "object" && !Array.isArray(action) ? action : {};
|
|
172
|
-
const actionId = normalizeText(source.id);
|
|
173
|
-
|
|
174
|
-
if (Object.prototype.hasOwnProperty.call(source, "assistantTool")) {
|
|
175
|
-
throw new Error(
|
|
176
|
-
`Action definition \"${actionId || "<unknown>"}\" assistantTool is not supported. Use extensions.assistant instead.`
|
|
177
|
-
);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
const extensions = source.extensions && typeof source.extensions === "object" && !Array.isArray(source.extensions)
|
|
181
|
-
? source.extensions
|
|
182
|
-
: {};
|
|
183
|
-
return normalizeAssistantExtension(extensions.assistant);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
function normalizeBarredEntry(value) {
|
|
187
|
-
return normalizeText(value).toLowerCase();
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function normalizeBarredActionSet(value) {
|
|
191
|
-
const source = Array.isArray(value) ? value : [value];
|
|
192
|
-
const exact = new Set();
|
|
193
|
-
const prefixes = [];
|
|
194
|
-
|
|
195
|
-
for (const entry of source) {
|
|
196
|
-
const normalized = normalizeBarredEntry(entry);
|
|
197
|
-
if (!normalized) {
|
|
198
|
-
continue;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
if (normalized.endsWith(".*")) {
|
|
202
|
-
const prefix = normalized.slice(0, -1);
|
|
203
|
-
if (prefix) {
|
|
204
|
-
prefixes.push(prefix);
|
|
205
|
-
}
|
|
206
|
-
continue;
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
exact.add(normalized);
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
return Object.freeze({
|
|
213
|
-
exact,
|
|
214
|
-
prefixes: Object.freeze(prefixes)
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
function isActionBarred(barredRules, actionId) {
|
|
219
|
-
const normalizedActionId = normalizeText(actionId).toLowerCase();
|
|
220
|
-
if (!normalizedActionId) {
|
|
221
|
-
return true;
|
|
222
|
-
}
|
|
223
|
-
|
|
224
|
-
if (barredRules.exact.has(normalizedActionId)) {
|
|
225
|
-
return true;
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
return barredRules.prefixes.some((prefix) => normalizedActionId.startsWith(prefix));
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function sanitizeToolName(value) {
|
|
232
|
-
const normalized = String(value || "")
|
|
233
|
-
.trim()
|
|
234
|
-
.toLowerCase()
|
|
235
|
-
.replace(/[^a-z0-9_]+/g, "_")
|
|
236
|
-
.replace(/_+/g, "_")
|
|
237
|
-
.replace(/^_+|_+$/g, "");
|
|
238
|
-
|
|
239
|
-
if (!normalized) {
|
|
240
|
-
return "tool";
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
return normalized;
|
|
244
|
-
}
|
|
245
|
-
|
|
246
|
-
function resolveUniqueToolName(baseName, used) {
|
|
247
|
-
const normalizedBase = sanitizeToolName(baseName) || "tool";
|
|
248
|
-
let candidate = normalizedBase.slice(0, 64);
|
|
249
|
-
let suffix = 1;
|
|
250
|
-
|
|
251
|
-
while (used.has(candidate)) {
|
|
252
|
-
const suffixText = `_${suffix}`;
|
|
253
|
-
const baseBudget = Math.max(1, 64 - suffixText.length);
|
|
254
|
-
candidate = `${normalizedBase.slice(0, baseBudget)}${suffixText}`;
|
|
255
|
-
suffix += 1;
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
used.add(candidate);
|
|
259
|
-
return candidate;
|
|
260
|
-
}
|
|
261
|
-
|
|
262
143
|
// Empty arguments mean "no input"; anything else must be one JSON object, so a
|
|
263
144
|
// truncated or malformed call fails instead of running the action with {}.
|
|
264
145
|
function parseToolPayload(argumentsText) {
|
|
@@ -293,11 +174,7 @@ function serializedByteLength(value) {
|
|
|
293
174
|
}
|
|
294
175
|
|
|
295
176
|
function createToolError(status, code, message) {
|
|
296
|
-
|
|
297
|
-
error.status = status;
|
|
298
|
-
error.statusCode = status;
|
|
299
|
-
error.code = code;
|
|
300
|
-
return error;
|
|
177
|
+
return new AppError(status, message, { code });
|
|
301
178
|
}
|
|
302
179
|
|
|
303
180
|
function ensureSerializedSize(value, maxBytes, { code = "assistant_tool_result_too_large", label = "Tool result" } = {}) {
|
|
@@ -411,21 +288,6 @@ function canInvokeMethod(permission, context) {
|
|
|
411
288
|
}
|
|
412
289
|
}
|
|
413
290
|
|
|
414
|
-
function normalizePermissionSpec(permission) {
|
|
415
|
-
const source = permission && typeof permission === "object" && !Array.isArray(permission)
|
|
416
|
-
? permission
|
|
417
|
-
: {};
|
|
418
|
-
const requireMode = normalizeText(source.require || "none").toLowerCase();
|
|
419
|
-
const permissions = Array.isArray(source.permissions) ? source.permissions : [];
|
|
420
|
-
|
|
421
|
-
return Object.freeze({
|
|
422
|
-
require: requireMode || "none",
|
|
423
|
-
permissions,
|
|
424
|
-
message: normalizeText(source.message),
|
|
425
|
-
code: normalizeText(source.code)
|
|
426
|
-
});
|
|
427
|
-
}
|
|
428
|
-
|
|
429
291
|
function stripWorkspaceSlugFromSchema(schema, context = {}) {
|
|
430
292
|
if (!schema || typeof schema !== "object" || Array.isArray(schema)) {
|
|
431
293
|
return schema;
|
|
@@ -457,31 +319,13 @@ function stripWorkspaceSlugFromSchema(schema, context = {}) {
|
|
|
457
319
|
};
|
|
458
320
|
}
|
|
459
321
|
|
|
460
|
-
function hasAutomationChannel(action = {}) {
|
|
461
|
-
const channels = Array.isArray(action.channels) ? action.channels : [];
|
|
462
|
-
return channels.some((channel) => normalizeText(channel).toLowerCase() === AUTOMATION_CHANNEL);
|
|
463
|
-
}
|
|
464
|
-
|
|
465
|
-
function normalizeSurfaceList(value) {
|
|
466
|
-
const source = Array.isArray(value) ? value : [value];
|
|
467
|
-
const resolved = [];
|
|
468
|
-
|
|
469
|
-
for (const entry of source) {
|
|
470
|
-
const normalized = normalizeSurfaceId(entry);
|
|
471
|
-
if (normalized) {
|
|
472
|
-
resolved.push(normalized);
|
|
473
|
-
}
|
|
474
|
-
}
|
|
475
|
-
|
|
476
|
-
return Object.freeze(resolved);
|
|
477
|
-
}
|
|
478
|
-
|
|
479
322
|
function canUseToolOnSurface(entry = {}, context = {}) {
|
|
480
323
|
const toolSurfaces = Array.isArray(entry.surfaces) ? entry.surfaces : [];
|
|
481
324
|
if (toolSurfaces.length < 1) {
|
|
482
325
|
return true;
|
|
483
326
|
}
|
|
484
327
|
|
|
328
|
+
// readable-check: allow no-optional-after-default - input validation: a caller may pass a null context
|
|
485
329
|
const contextSurfaceId = normalizeSurfaceId(context?.surface);
|
|
486
330
|
if (!contextSurfaceId) {
|
|
487
331
|
return true;
|
|
@@ -490,125 +334,22 @@ function canUseToolOnSurface(entry = {}, context = {}) {
|
|
|
490
334
|
return toolSurfaces.includes("*") || toolSurfaces.includes(contextSurfaceId);
|
|
491
335
|
}
|
|
492
336
|
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
let assistantExtension = null;
|
|
512
|
-
try {
|
|
513
|
-
assistantExtension = normalizeAssistantActionExtension(action);
|
|
514
|
-
} catch {
|
|
515
|
-
continue;
|
|
516
|
-
}
|
|
517
|
-
|
|
518
|
-
if (assistantExtension.exclude) {
|
|
519
|
-
continue;
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
const outputDefinition = assistantExtension.output || action.output;
|
|
523
|
-
const inputSchema = resolveSchemaTransportSchemaDefinition(action.input, {
|
|
524
|
-
context: `Action definition "${actionId}" input`,
|
|
525
|
-
defaultMode: "patch"
|
|
526
|
-
}) || null;
|
|
527
|
-
const outputSchema = resolveSchemaTransportSchemaDefinition(outputDefinition, {
|
|
528
|
-
context: `Action definition "${actionId}" assistant output`,
|
|
529
|
-
defaultMode: "replace"
|
|
530
|
-
}) || null;
|
|
531
|
-
if (!inputSchema || !outputSchema) {
|
|
532
|
-
continue;
|
|
533
|
-
}
|
|
534
|
-
|
|
535
|
-
const actionVersion = Number(action.version) || 1;
|
|
536
|
-
const actionKey = actionId.toLowerCase();
|
|
537
|
-
const nextEntry = Object.freeze({
|
|
538
|
-
actionId,
|
|
539
|
-
actionVersion,
|
|
540
|
-
kind: normalizeText(action.kind).toLowerCase() || "command",
|
|
541
|
-
toolBaseName: actionId,
|
|
542
|
-
description: assistantExtension.description || `Run ${actionId}.`,
|
|
543
|
-
alwaysAvailable: assistantExtension.alwaysAvailable,
|
|
544
|
-
preflight: assistantExtension.preflight,
|
|
545
|
-
inputSchema,
|
|
546
|
-
outputDefinition,
|
|
547
|
-
outputSchema,
|
|
548
|
-
transformResult: assistantExtension.transformResult,
|
|
549
|
-
permission: normalizePermissionSpec(action.permission),
|
|
550
|
-
surfaces: normalizeSurfaceList(action.surfaces)
|
|
551
|
-
});
|
|
552
|
-
const existing = entriesByActionId.get(actionKey);
|
|
553
|
-
if (!existing || actionVersion >= Number(existing.actionVersion || 0)) {
|
|
554
|
-
entriesByActionId.set(actionKey, nextEntry);
|
|
555
|
-
}
|
|
556
|
-
}
|
|
557
|
-
|
|
558
|
-
return entriesByActionId;
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
function resolveActionToolEntries(
|
|
562
|
-
actions,
|
|
563
|
-
{ barredActionIds = [], skipActionPrefixes = [] } = {}
|
|
564
|
-
) {
|
|
565
|
-
const actionBackedEntries = resolveActionBackedToolEntries(actions);
|
|
566
|
-
const barredRules = normalizeBarredActionSet(barredActionIds);
|
|
567
|
-
const usedToolNames = new Set();
|
|
568
|
-
const entries = [];
|
|
569
|
-
|
|
570
|
-
for (const actionEntry of actionBackedEntries.values()) {
|
|
571
|
-
const actionId = normalizeText(actionEntry?.actionId);
|
|
572
|
-
if (!actionId) {
|
|
573
|
-
continue;
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
const normalizedActionId = actionId.toLowerCase();
|
|
577
|
-
const skipByPrefix = skipActionPrefixes.some((prefix) => normalizedActionId.startsWith(prefix));
|
|
578
|
-
if (skipByPrefix) {
|
|
579
|
-
continue;
|
|
580
|
-
}
|
|
581
|
-
|
|
582
|
-
if (isActionBarred(barredRules, actionId)) {
|
|
583
|
-
continue;
|
|
584
|
-
}
|
|
585
|
-
|
|
586
|
-
const toolName = resolveUniqueToolName(actionEntry.toolBaseName, usedToolNames);
|
|
587
|
-
entries.push(
|
|
588
|
-
Object.freeze({
|
|
589
|
-
descriptor: Object.freeze({
|
|
590
|
-
name: toolName,
|
|
591
|
-
actionId,
|
|
592
|
-
actionVersion: Number(actionEntry.actionVersion) || null,
|
|
593
|
-
description: normalizeText(actionEntry.description) || `Run ${actionId}.`,
|
|
594
|
-
alwaysAvailable: actionEntry.alwaysAvailable === true,
|
|
595
|
-
preflight: actionEntry.preflight,
|
|
596
|
-
parameters: actionEntry.inputSchema,
|
|
597
|
-
outputSchema: actionEntry.outputSchema
|
|
598
|
-
}),
|
|
599
|
-
kind: actionEntry.kind,
|
|
600
|
-
acceptsWorkspaceSlug: Object.hasOwn(actionEntry.inputSchema.properties || {}, "workspaceSlug"),
|
|
601
|
-
outputDefinition: actionEntry.outputDefinition,
|
|
602
|
-
transformResult: actionEntry.transformResult,
|
|
603
|
-
permission: actionEntry.permission,
|
|
604
|
-
surfaces: actionEntry.surfaces
|
|
605
|
-
})
|
|
606
|
-
);
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
return Object.freeze(entries.sort((left, right) => left.descriptor.name.localeCompare(right.descriptor.name)));
|
|
610
|
-
}
|
|
611
|
-
|
|
337
|
+
// A tool call whose caller does not watch its failures.
|
|
338
|
+
function reportNoToolFailure() {}
|
|
339
|
+
|
|
340
|
+
/**
|
|
341
|
+
* The assistant tools an application's actions offer one caller: direct tools, or discovery
|
|
342
|
+
* tools when there are too many, each checked against permission, surface and policy.
|
|
343
|
+
* @param {{ listDefinitions(): object[], execute(request: object): Promise<unknown> }} actions The application's action runtime.
|
|
344
|
+
* @param {object} [options]
|
|
345
|
+
* @param {(request: { actionId: string, kind: string, context: object }) => boolean} [options.isActionAvailable]
|
|
346
|
+
* @param {string[] | string} [options.barredActionIds]
|
|
347
|
+
* @param {string[] | string} [options.skipActionPrefixes]
|
|
348
|
+
* @param {number} [options.maxDirectTools]
|
|
349
|
+
* @param {number} [options.discoveryPageSize]
|
|
350
|
+
* @param {number} [options.maxToolArgumentBytes]
|
|
351
|
+
* @param {number} [options.maxToolResultBytes]
|
|
352
|
+
*/
|
|
612
353
|
function createServiceToolCatalog(
|
|
613
354
|
actions,
|
|
614
355
|
{
|
|
@@ -621,9 +362,11 @@ function createServiceToolCatalog(
|
|
|
621
362
|
maxToolResultBytes: rawMaxToolResultBytes = DEFAULT_MAX_TOOL_RESULT_BYTES
|
|
622
363
|
} = {}
|
|
623
364
|
) {
|
|
365
|
+
// readable-check: allow method-probe - input validation: the application passes its action runtime
|
|
624
366
|
if (!actions || typeof actions.listDefinitions !== "function" || typeof actions.execute !== "function") {
|
|
625
367
|
throw new TypeError("createServiceToolCatalog requires runtime.actions.");
|
|
626
368
|
}
|
|
369
|
+
// readable-check: allow method-probe - input validation: an optional action policy must be a function
|
|
627
370
|
if (isActionAvailable !== undefined && typeof isActionAvailable !== "function") {
|
|
628
371
|
throw new TypeError("isActionAvailable must be a synchronous action policy.");
|
|
629
372
|
}
|
|
@@ -875,7 +618,7 @@ function createServiceToolCatalog(
|
|
|
875
618
|
};
|
|
876
619
|
}
|
|
877
620
|
|
|
878
|
-
async function executeToolCall({ toolName = "", argumentsText = "", context = {}, toolSet = null, onFailure } = {}) {
|
|
621
|
+
async function executeToolCall({ toolName = "", argumentsText = "", context = {}, toolSet = null, onFailure = reportNoToolFailure } = {}) {
|
|
879
622
|
const normalizedToolName = normalizeText(toolName);
|
|
880
623
|
const suppliedState = toolSet && typeof toolSet === "object" ? toolSetStates.get(toolSet) : null;
|
|
881
624
|
const resolvedToolSet = suppliedState ? toolSet : resolveToolSet(context);
|
|
@@ -920,13 +663,16 @@ function createServiceToolCatalog(
|
|
|
920
663
|
result
|
|
921
664
|
};
|
|
922
665
|
} catch (error) {
|
|
923
|
-
onFailure
|
|
666
|
+
onFailure(error);
|
|
924
667
|
return resolveToolFailure(error);
|
|
925
668
|
}
|
|
926
669
|
}
|
|
927
670
|
|
|
928
671
|
return Object.freeze({
|
|
929
|
-
limits: Object.freeze({
|
|
672
|
+
limits: Object.freeze({
|
|
673
|
+
maxToolArgumentBytes,
|
|
674
|
+
maxToolResultBytes
|
|
675
|
+
}),
|
|
930
676
|
resolveToolSet,
|
|
931
677
|
toOpenAiToolSchema,
|
|
932
678
|
executeToolCall
|