@jskit-ai/assistant-core 0.2.7 → 0.2.9
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
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
import { execFile } from "node:child_process";
|
|
2
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
3
|
+
import { isRecord, normalizePositiveInteger } from "@jskit-ai/kernel/shared/support/normalize";
|
|
4
|
+
import { normalizeNullableText } from "./normalize.js";
|
|
5
|
+
import { setTimeout as delay } from "node:timers/promises";
|
|
6
|
+
import { promisify } from "node:util";
|
|
7
|
+
|
|
8
|
+
const execFileAsync = promisify(execFile);
|
|
9
|
+
|
|
10
|
+
// Fixed locale and zone make `lstart` parseable and stable across restarts.
|
|
11
|
+
async function runPs(args) {
|
|
12
|
+
const { stdout } = await execFileAsync("ps", args, {
|
|
13
|
+
env: { ...process.env, LC_ALL: "C", TZ: "UTC" },
|
|
14
|
+
maxBuffer: 64 * 1024 * 1024
|
|
15
|
+
});
|
|
16
|
+
return stdout;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Process-group ownership proof used by execution hosts; callers retain their metadata contract.
|
|
21
|
+
* @param {object} [options]
|
|
22
|
+
* @param {string} [options.runtimeTokenEnvironmentName]
|
|
23
|
+
* @param {string} [options.commandHashEnvironmentName]
|
|
24
|
+
* @param {string} [options.label]
|
|
25
|
+
* @param {string} [options.unavailableCode]
|
|
26
|
+
* @param {NodeJS.Platform} [options.platform]
|
|
27
|
+
* @param {(args: string[]) => Promise<string>} [options.ps] Runs ps; the system's own by default.
|
|
28
|
+
*/
|
|
29
|
+
export function createConversationProcessIdentity({
|
|
30
|
+
runtimeTokenEnvironmentName = "JSKIT_EXECUTION_RUNTIME_TOKEN",
|
|
31
|
+
commandHashEnvironmentName = "JSKIT_EXECUTION_COMMAND_HASH",
|
|
32
|
+
label = "Native",
|
|
33
|
+
unavailableCode = "native_process_identity_unavailable",
|
|
34
|
+
platform = process.platform,
|
|
35
|
+
ps = runPs
|
|
36
|
+
} = {}) {
|
|
37
|
+
const PROCESS_IDENTITY_SETTLE_TIMEOUT_MS = 1000;
|
|
38
|
+
// Darwin inspection spawns ps instead of reading /proc, so it polls less often.
|
|
39
|
+
const PROCESS_IDENTITY_SETTLE_POLL_MS = platform === "darwin" ? 100 : 25;
|
|
40
|
+
const PROCESS_IDENTITY_VERSION = 1;
|
|
41
|
+
const PROCESS_IDENTITY_PLATFORMS = Object.freeze({ linux: "linux-proc", darwin: "darwin" });
|
|
42
|
+
const hostIdentityPlatform = PROCESS_IDENTITY_PLATFORMS[platform] || "";
|
|
43
|
+
const PROCESS_STATE = Object.freeze({ RUNNING: "running", STOPPED: "stopped" });
|
|
44
|
+
const PROCESS_IDENTITY_STATUS = Object.freeze({
|
|
45
|
+
ABSENT: "absent", AMBIGUOUS: "ambiguous", EXACT: "exact", INVALID: "invalid", MISMATCH: "mismatch"
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
function processMetadataIsIdentifiable(metadata = {}) {
|
|
49
|
+
return Boolean(
|
|
50
|
+
Number.isSafeInteger(Number(metadata.pid)) && Number(metadata.pid) > 0 &&
|
|
51
|
+
processIdentityIsWellFormed(metadata.processIdentity) &&
|
|
52
|
+
[PROCESS_STATE.RUNNING, PROCESS_STATE.STOPPED].includes(metadata.processState)
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function processIsAlive(pid) {
|
|
57
|
+
const normalizedPid = Number(pid);
|
|
58
|
+
if (!Number.isSafeInteger(normalizedPid) || normalizedPid <= 0) {
|
|
59
|
+
return false;
|
|
60
|
+
}
|
|
61
|
+
try {
|
|
62
|
+
process.kill(normalizedPid, 0);
|
|
63
|
+
return true;
|
|
64
|
+
} catch {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function processGroupIsAlive(processGroupId) {
|
|
70
|
+
const normalizedProcessGroupId = Number(processGroupId);
|
|
71
|
+
if (!Number.isSafeInteger(normalizedProcessGroupId) || normalizedProcessGroupId <= 0) {
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
74
|
+
if (process.platform === "win32") {
|
|
75
|
+
return processIsAlive(normalizedProcessGroupId);
|
|
76
|
+
}
|
|
77
|
+
try {
|
|
78
|
+
process.kill(-normalizedProcessGroupId, 0);
|
|
79
|
+
return true;
|
|
80
|
+
} catch {
|
|
81
|
+
return false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function signalProcessGroup(processGroupId, signal) {
|
|
86
|
+
const normalizedProcessGroupId = Number(processGroupId);
|
|
87
|
+
const target = process.platform === "win32"
|
|
88
|
+
? normalizedProcessGroupId
|
|
89
|
+
: -normalizedProcessGroupId;
|
|
90
|
+
try {
|
|
91
|
+
process.kill(target, signal);
|
|
92
|
+
return true;
|
|
93
|
+
} catch (error) {
|
|
94
|
+
if (!["ESRCH", "EPERM"].includes(String(error?.code || ""))) {
|
|
95
|
+
throw error;
|
|
96
|
+
}
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function linuxProcessStat(value = "") {
|
|
102
|
+
const text = String(value || "");
|
|
103
|
+
const commandEnd = text.lastIndexOf(")");
|
|
104
|
+
if (commandEnd < 0) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
const fields = text.slice(commandEnd + 1).trim().split(/\s+/u);
|
|
108
|
+
const state = String(fields[0] || "");
|
|
109
|
+
const parentPid = Number(fields[1]);
|
|
110
|
+
const processGroupId = Number(fields[2]);
|
|
111
|
+
const startTimeTicks = String(fields[19] || "");
|
|
112
|
+
if (
|
|
113
|
+
!/^[A-Z]$/u.test(state) ||
|
|
114
|
+
!Number.isSafeInteger(parentPid) || parentPid < 0 ||
|
|
115
|
+
!Number.isSafeInteger(processGroupId) || processGroupId <= 0 ||
|
|
116
|
+
!/^\d+$/u.test(startTimeTicks)
|
|
117
|
+
) {
|
|
118
|
+
return null;
|
|
119
|
+
}
|
|
120
|
+
return {
|
|
121
|
+
parentPid,
|
|
122
|
+
processGroupId,
|
|
123
|
+
startTimeTicks,
|
|
124
|
+
state
|
|
125
|
+
};
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
function normalizeProcessIdentity(value = {}) {
|
|
129
|
+
const normalized = isRecord(value) ? value : {};
|
|
130
|
+
return {
|
|
131
|
+
commandHash: normalizeNullableText(normalized.commandHash),
|
|
132
|
+
platform: normalizeNullableText(normalized.platform),
|
|
133
|
+
runtimeToken: normalizeNullableText(normalized.runtimeToken),
|
|
134
|
+
startTimeTicks: normalizeNullableText(normalized.startTimeTicks),
|
|
135
|
+
version: Number(normalized.version || 0)
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
function processIdentityIsWellFormed(value = {}) {
|
|
140
|
+
const identity = normalizeProcessIdentity(value);
|
|
141
|
+
return Boolean(
|
|
142
|
+
identity.version === PROCESS_IDENTITY_VERSION &&
|
|
143
|
+
Object.values(PROCESS_IDENTITY_PLATFORMS).includes(identity.platform) &&
|
|
144
|
+
/^[a-f0-9]{12}$/u.test(identity.commandHash) &&
|
|
145
|
+
/^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/iu.test(identity.runtimeToken) &&
|
|
146
|
+
/^\d+$/u.test(identity.startTimeTicks)
|
|
147
|
+
);
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/** The value of `name` in a list of NAME=value environment entries. */
|
|
151
|
+
function environmentValue(entries = [], name = "") {
|
|
152
|
+
const entry = entries.find((assignment) => assignment.startsWith(`${name}=`));
|
|
153
|
+
return entry ? entry.slice(name.length + 1) : "";
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
async function linuxProcessRecords() {
|
|
157
|
+
const records = [];
|
|
158
|
+
const entries = await readdir("/proc", {
|
|
159
|
+
withFileTypes: true
|
|
160
|
+
}).catch(() => []);
|
|
161
|
+
for (const entry of entries) {
|
|
162
|
+
if (!entry.isDirectory() || !/^\d+$/u.test(entry.name)) {
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
const pid = Number(entry.name);
|
|
166
|
+
const statValue = await readFile(`/proc/${entry.name}/stat`, "utf8").catch(() => "");
|
|
167
|
+
const processStat = linuxProcessStat(statValue);
|
|
168
|
+
if (!processStat || processStat.state === "Z") {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
let environment = null;
|
|
172
|
+
let environmentReadable = true;
|
|
173
|
+
try {
|
|
174
|
+
environment = await readFile(`/proc/${entry.name}/environ`);
|
|
175
|
+
} catch (error) {
|
|
176
|
+
if (String(error?.code || "") === "ENOENT") {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
environmentReadable = false;
|
|
180
|
+
}
|
|
181
|
+
const entries = environmentReadable ? environment.toString("utf8").split("\0") : [];
|
|
182
|
+
records.push({
|
|
183
|
+
...processStat,
|
|
184
|
+
commandHash: environmentValue(entries, commandHashEnvironmentName),
|
|
185
|
+
environmentReadable,
|
|
186
|
+
pid,
|
|
187
|
+
runtimeToken: environmentValue(entries, runtimeTokenEnvironmentName)
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
return records;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const DARWIN_MONTHS = ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"];
|
|
194
|
+
|
|
195
|
+
/** `ps -o lstart=` in the C locale and UTC, as whole epoch seconds. */
|
|
196
|
+
function darwinStartTime(value = "") {
|
|
197
|
+
const match = /^\w{3}\s+(\w{3})\s+(\d{1,2})\s+(\d{2}):(\d{2}):(\d{2})\s+(\d{4})$/u.exec(String(value).trim());
|
|
198
|
+
const month = match ? DARWIN_MONTHS.indexOf(match[1]) : -1;
|
|
199
|
+
if (month < 0) {
|
|
200
|
+
return "";
|
|
201
|
+
}
|
|
202
|
+
const [, , day, hours, minutes, seconds, year] = match.map(Number);
|
|
203
|
+
return String(Date.UTC(year, month, day, hours, minutes, seconds) / 1000);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function darwinCommandsByPid(output = "") {
|
|
207
|
+
const commands = new Map();
|
|
208
|
+
for (const line of String(output).split("\n")) {
|
|
209
|
+
const match = /^\s*(\d+) (.*)$/u.exec(line);
|
|
210
|
+
if (match) {
|
|
211
|
+
commands.set(Number(match[1]), match[2]);
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return commands;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
/** The `ps -E` text after the argument list, as its assignments, or null when no environment is shown. */
|
|
218
|
+
function darwinEnvironment(command, commandWithEnvironment) {
|
|
219
|
+
if (command === undefined || !commandWithEnvironment?.startsWith(command)) {
|
|
220
|
+
return null;
|
|
221
|
+
}
|
|
222
|
+
const assignments = commandWithEnvironment.slice(command.length).split(/\s+/u).filter((entry) => /^[^=\s]+=/u.test(entry));
|
|
223
|
+
return assignments.length ? assignments : null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
/** The darwin counterpart of the /proc scan. Only the current user's environments are readable;
|
|
227
|
+
* a listing without a separate environment section stays unreadable so inspection degrades to ambiguous. */
|
|
228
|
+
async function darwinProcessRecords() {
|
|
229
|
+
const currentUid = process.getuid();
|
|
230
|
+
const ownProcesses = ["-x", "-U", String(currentUid), "-ww", "-o", "pid=,command="];
|
|
231
|
+
const [table, commandOutput, environmentOutput] = await Promise.all([
|
|
232
|
+
ps(["-ax", "-o", "pid=,pgid=,uid=,stat=,lstart="]),
|
|
233
|
+
ps(ownProcesses),
|
|
234
|
+
ps(["-E", ...ownProcesses])
|
|
235
|
+
]);
|
|
236
|
+
const commands = darwinCommandsByPid(commandOutput);
|
|
237
|
+
const commandsWithEnvironment = darwinCommandsByPid(environmentOutput);
|
|
238
|
+
const records = [];
|
|
239
|
+
for (const line of String(table).split("\n")) {
|
|
240
|
+
const match = /^\s*(\d+)\s+(\d+)\s+(\d+)\s+(\S+)\s+(\w{3}\s+\w{3}\s+\d{1,2}\s+[\d:]{8}\s+\d{4})\s*$/u.exec(line);
|
|
241
|
+
if (!match || match[4].startsWith("Z")) {
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const pid = Number(match[1]);
|
|
245
|
+
const environment = Number(match[3]) === currentUid
|
|
246
|
+
? darwinEnvironment(commands.get(pid), commandsWithEnvironment.get(pid))
|
|
247
|
+
: null;
|
|
248
|
+
records.push({
|
|
249
|
+
commandHash: environmentValue(environment || [], commandHashEnvironmentName),
|
|
250
|
+
environmentReadable: Boolean(environment),
|
|
251
|
+
pid,
|
|
252
|
+
processGroupId: Number(match[2]),
|
|
253
|
+
runtimeToken: environmentValue(environment || [], runtimeTokenEnvironmentName),
|
|
254
|
+
startTimeTicks: darwinStartTime(match[5])
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
return records;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
/** An inspector's answer, which may be null, as an exact or ambiguous set of process groups. */
|
|
261
|
+
function normalizeProcessIdentityInspection(value) {
|
|
262
|
+
const status = Object.values(PROCESS_IDENTITY_STATUS).includes(value?.status)
|
|
263
|
+
? value.status
|
|
264
|
+
: PROCESS_IDENTITY_STATUS.AMBIGUOUS;
|
|
265
|
+
return {
|
|
266
|
+
processGroupIds: [...new Set((Array.isArray(value?.processGroupIds)
|
|
267
|
+
? value.processGroupIds
|
|
268
|
+
: [])
|
|
269
|
+
.map(Number)
|
|
270
|
+
.filter((processGroupId) => Number.isSafeInteger(processGroupId) && processGroupId > 0))],
|
|
271
|
+
status
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function inspectHostProcessIdentity(metadata = {}) {
|
|
276
|
+
if (!processMetadataIsIdentifiable(metadata)) {
|
|
277
|
+
return {
|
|
278
|
+
processGroupIds: [],
|
|
279
|
+
status: PROCESS_IDENTITY_STATUS.INVALID
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
if (metadata.processState === PROCESS_STATE.STOPPED) {
|
|
283
|
+
return {
|
|
284
|
+
processGroupIds: [],
|
|
285
|
+
status: PROCESS_IDENTITY_STATUS.ABSENT
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
const identity = metadata.processIdentity;
|
|
289
|
+
const records = identity.platform !== hostIdentityPlatform
|
|
290
|
+
? null
|
|
291
|
+
: platform === "darwin"
|
|
292
|
+
? await darwinProcessRecords().catch(() => null)
|
|
293
|
+
: await linuxProcessRecords();
|
|
294
|
+
if (!records) {
|
|
295
|
+
return {
|
|
296
|
+
processGroupIds: [],
|
|
297
|
+
status: PROCESS_IDENTITY_STATUS.AMBIGUOUS
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
const rootProcessGroupId = Number(metadata.pid);
|
|
301
|
+
const rootGroupMembers = records.filter(({ processGroupId }) => processGroupId === rootProcessGroupId);
|
|
302
|
+
const exactRecords = [];
|
|
303
|
+
const rootUnreadable = rootGroupMembers.some(({ environmentReadable }) => !environmentReadable);
|
|
304
|
+
for (const record of records) {
|
|
305
|
+
if (!record.environmentReadable) {
|
|
306
|
+
continue;
|
|
307
|
+
}
|
|
308
|
+
if (record.runtimeToken === identity.runtimeToken && record.commandHash === identity.commandHash) {
|
|
309
|
+
exactRecords.push(record);
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
const exactLeader = exactRecords.find(({ pid }) => pid === rootProcessGroupId);
|
|
313
|
+
if (exactLeader && exactLeader.startTimeTicks !== identity.startTimeTicks) {
|
|
314
|
+
return {
|
|
315
|
+
processGroupIds: [],
|
|
316
|
+
status: PROCESS_IDENTITY_STATUS.AMBIGUOUS
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
if (exactRecords.length === 0) {
|
|
320
|
+
if (rootUnreadable) {
|
|
321
|
+
return {
|
|
322
|
+
processGroupIds: [],
|
|
323
|
+
status: PROCESS_IDENTITY_STATUS.AMBIGUOUS
|
|
324
|
+
};
|
|
325
|
+
}
|
|
326
|
+
return {
|
|
327
|
+
processGroupIds: [],
|
|
328
|
+
status: rootGroupMembers.length > 0 || processGroupIsAlive(rootProcessGroupId)
|
|
329
|
+
? PROCESS_IDENTITY_STATUS.MISMATCH
|
|
330
|
+
: PROCESS_IDENTITY_STATUS.ABSENT
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
const processGroupIds = [...new Set(exactRecords.map(({ processGroupId }) => processGroupId))];
|
|
334
|
+
for (const processGroupId of processGroupIds) {
|
|
335
|
+
const groupMembers = records.filter((record) => record.processGroupId === processGroupId);
|
|
336
|
+
if (groupMembers.some((record) => (
|
|
337
|
+
!record.environmentReadable || !exactRecords.some(({ pid }) => pid === record.pid)
|
|
338
|
+
))) {
|
|
339
|
+
return {
|
|
340
|
+
processGroupIds: [],
|
|
341
|
+
status: PROCESS_IDENTITY_STATUS.AMBIGUOUS
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
if (
|
|
346
|
+
rootGroupMembers.some((record) => !exactRecords.some(({ pid }) => pid === record.pid))
|
|
347
|
+
) {
|
|
348
|
+
return {
|
|
349
|
+
processGroupIds: [],
|
|
350
|
+
status: PROCESS_IDENTITY_STATUS.AMBIGUOUS
|
|
351
|
+
};
|
|
352
|
+
}
|
|
353
|
+
return {
|
|
354
|
+
processGroupIds,
|
|
355
|
+
status: PROCESS_IDENTITY_STATUS.EXACT
|
|
356
|
+
};
|
|
357
|
+
}
|
|
358
|
+
|
|
359
|
+
async function inspectProcessIdentity(metadata = {}, options = {}) {
|
|
360
|
+
const inspector = options.processIdentityInspector ?? inspectHostProcessIdentity;
|
|
361
|
+
return normalizeProcessIdentityInspection(await inspector(metadata));
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
async function waitForProcessIdentityExit(metadata = {}, options = {}, timeoutMs = 0) {
|
|
365
|
+
const deadline = Date.now() + timeoutMs;
|
|
366
|
+
let inspection = await inspectProcessIdentity(metadata, options);
|
|
367
|
+
while (
|
|
368
|
+
Date.now() < deadline &&
|
|
369
|
+
inspection.status === PROCESS_IDENTITY_STATUS.EXACT
|
|
370
|
+
) {
|
|
371
|
+
await delay(100);
|
|
372
|
+
inspection = await inspectProcessIdentity(metadata, options);
|
|
373
|
+
}
|
|
374
|
+
return inspection;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
async function waitForProcessIdentityToSettle(metadata = {}, options = {}) {
|
|
378
|
+
const timeoutMs = normalizePositiveInteger(
|
|
379
|
+
options.processIdentitySettleTimeoutMs,
|
|
380
|
+
{ fallback: PROCESS_IDENTITY_SETTLE_TIMEOUT_MS }
|
|
381
|
+
);
|
|
382
|
+
const pollMs = normalizePositiveInteger(
|
|
383
|
+
options.processIdentitySettlePollMs,
|
|
384
|
+
{ fallback: PROCESS_IDENTITY_SETTLE_POLL_MS }
|
|
385
|
+
);
|
|
386
|
+
const deadline = Date.now() + timeoutMs;
|
|
387
|
+
let inspection = await inspectProcessIdentity(metadata, options);
|
|
388
|
+
while (
|
|
389
|
+
inspection.status === PROCESS_IDENTITY_STATUS.AMBIGUOUS &&
|
|
390
|
+
Date.now() < deadline
|
|
391
|
+
) {
|
|
392
|
+
await delay(Math.min(pollMs, Math.max(1, deadline - Date.now())));
|
|
393
|
+
inspection = await inspectProcessIdentity(metadata, options);
|
|
394
|
+
}
|
|
395
|
+
return inspection;
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
async function signalVerifiedProcessGroups(metadata = {}, signal = "SIGTERM", options = {}) {
|
|
399
|
+
const signalProcessGroupImpl = options.signalProcessGroup ?? signalProcessGroup;
|
|
400
|
+
const initial = await waitForProcessIdentityToSettle(metadata, options);
|
|
401
|
+
if (initial.status !== PROCESS_IDENTITY_STATUS.EXACT) {
|
|
402
|
+
return {
|
|
403
|
+
inspection: initial,
|
|
404
|
+
signaledProcessGroups: []
|
|
405
|
+
};
|
|
406
|
+
}
|
|
407
|
+
const rootProcessGroupId = Number(metadata.pid);
|
|
408
|
+
const orderedProcessGroupIds = [
|
|
409
|
+
...initial.processGroupIds.filter((processGroupId) => processGroupId !== rootProcessGroupId),
|
|
410
|
+
...initial.processGroupIds.filter((processGroupId) => processGroupId === rootProcessGroupId)
|
|
411
|
+
];
|
|
412
|
+
const signaledProcessGroups = [];
|
|
413
|
+
for (const processGroupId of orderedProcessGroupIds) {
|
|
414
|
+
const current = await waitForProcessIdentityToSettle(metadata, options);
|
|
415
|
+
if (
|
|
416
|
+
current.status === PROCESS_IDENTITY_STATUS.ABSENT ||
|
|
417
|
+
current.status === PROCESS_IDENTITY_STATUS.MISMATCH
|
|
418
|
+
) {
|
|
419
|
+
return {
|
|
420
|
+
inspection: current,
|
|
421
|
+
signaledProcessGroups
|
|
422
|
+
};
|
|
423
|
+
}
|
|
424
|
+
if (current.status !== PROCESS_IDENTITY_STATUS.EXACT) {
|
|
425
|
+
return {
|
|
426
|
+
inspection: current,
|
|
427
|
+
signaledProcessGroups
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
if (!current.processGroupIds.includes(processGroupId)) {
|
|
431
|
+
continue;
|
|
432
|
+
}
|
|
433
|
+
signalProcessGroupImpl(processGroupId, signal);
|
|
434
|
+
signaledProcessGroups.push(processGroupId);
|
|
435
|
+
}
|
|
436
|
+
return {
|
|
437
|
+
inspection: await waitForProcessIdentityToSettle(metadata, options),
|
|
438
|
+
signaledProcessGroups
|
|
439
|
+
};
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
async function stopProcessGroup(metadata = {}, options = {}) {
|
|
443
|
+
if (!processMetadataIsIdentifiable(metadata)) {
|
|
444
|
+
return {
|
|
445
|
+
identityStatus: PROCESS_IDENTITY_STATUS.INVALID,
|
|
446
|
+
processExitVerified: false,
|
|
447
|
+
stopped: false
|
|
448
|
+
};
|
|
449
|
+
}
|
|
450
|
+
if (
|
|
451
|
+
metadata.processState === PROCESS_STATE.STOPPED &&
|
|
452
|
+
metadata.processExitVerifiedAt
|
|
453
|
+
) {
|
|
454
|
+
return {
|
|
455
|
+
alreadyStopped: true,
|
|
456
|
+
identityStatus: PROCESS_IDENTITY_STATUS.ABSENT,
|
|
457
|
+
processExitVerified: true,
|
|
458
|
+
stopped: false
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
const before = await waitForProcessIdentityToSettle(metadata, options);
|
|
462
|
+
if (
|
|
463
|
+
before.status === PROCESS_IDENTITY_STATUS.ABSENT ||
|
|
464
|
+
before.status === PROCESS_IDENTITY_STATUS.MISMATCH
|
|
465
|
+
) {
|
|
466
|
+
return {
|
|
467
|
+
identityStatus: before.status,
|
|
468
|
+
processExitVerified: true,
|
|
469
|
+
stopped: false
|
|
470
|
+
};
|
|
471
|
+
}
|
|
472
|
+
if (before.status !== PROCESS_IDENTITY_STATUS.EXACT) {
|
|
473
|
+
return {
|
|
474
|
+
identityStatus: before.status,
|
|
475
|
+
processExitVerified: false,
|
|
476
|
+
stopped: false
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
const term = await signalVerifiedProcessGroups(metadata, "SIGTERM", options);
|
|
480
|
+
if (term.inspection.status === PROCESS_IDENTITY_STATUS.AMBIGUOUS) {
|
|
481
|
+
return {
|
|
482
|
+
identityStatus: term.inspection.status,
|
|
483
|
+
processExitVerified: false,
|
|
484
|
+
signaledProcessGroups: term.signaledProcessGroups,
|
|
485
|
+
stopped: false
|
|
486
|
+
};
|
|
487
|
+
}
|
|
488
|
+
let after = await waitForProcessIdentityExit(
|
|
489
|
+
metadata,
|
|
490
|
+
options,
|
|
491
|
+
normalizePositiveInteger(options.termTimeoutMs, { fallback: 3000 })
|
|
492
|
+
);
|
|
493
|
+
let signaledProcessGroups = [...term.signaledProcessGroups];
|
|
494
|
+
if (after.status === PROCESS_IDENTITY_STATUS.EXACT) {
|
|
495
|
+
const kill = await signalVerifiedProcessGroups(metadata, "SIGKILL", options);
|
|
496
|
+
signaledProcessGroups = [...new Set([
|
|
497
|
+
...signaledProcessGroups,
|
|
498
|
+
...kill.signaledProcessGroups
|
|
499
|
+
])];
|
|
500
|
+
if (kill.inspection.status === PROCESS_IDENTITY_STATUS.AMBIGUOUS) {
|
|
501
|
+
return {
|
|
502
|
+
identityStatus: kill.inspection.status,
|
|
503
|
+
processExitVerified: false,
|
|
504
|
+
signaledProcessGroups,
|
|
505
|
+
stopped: false
|
|
506
|
+
};
|
|
507
|
+
}
|
|
508
|
+
after = await waitForProcessIdentityExit(
|
|
509
|
+
metadata,
|
|
510
|
+
options,
|
|
511
|
+
normalizePositiveInteger(options.killTimeoutMs, { fallback: 1000 })
|
|
512
|
+
);
|
|
513
|
+
}
|
|
514
|
+
const processExitVerified = [
|
|
515
|
+
PROCESS_IDENTITY_STATUS.ABSENT,
|
|
516
|
+
PROCESS_IDENTITY_STATUS.MISMATCH
|
|
517
|
+
].includes(after.status);
|
|
518
|
+
return {
|
|
519
|
+
descendantProcessGroups: signaledProcessGroups.filter((processGroupId) => (
|
|
520
|
+
processGroupId !== Number(metadata.pid)
|
|
521
|
+
)),
|
|
522
|
+
identityStatus: after.status,
|
|
523
|
+
pid: Number(metadata.pid),
|
|
524
|
+
processExitVerified,
|
|
525
|
+
signaledProcessGroups,
|
|
526
|
+
stopped: processExitVerified && signaledProcessGroups.length > 0
|
|
527
|
+
};
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
async function captureProcessStart(pid) {
|
|
531
|
+
if (platform !== "darwin") {
|
|
532
|
+
return linuxProcessStat(await readFile(`/proc/${pid}/stat`, "utf8").catch(() => ""));
|
|
533
|
+
}
|
|
534
|
+
const output = String(await ps(["-o", "pgid=,lstart=", "-p", String(pid)]).catch(() => ""));
|
|
535
|
+
const match = /^\s*(\d+)\s+(.+?)\s*$/u.exec(output);
|
|
536
|
+
return match ? { processGroupId: Number(match[1]), startTimeTicks: darwinStartTime(match[2]) } : null;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
async function captureProcessIdentity({
|
|
540
|
+
commandHash = "",
|
|
541
|
+
pid = null,
|
|
542
|
+
reportedIdentity = null,
|
|
543
|
+
runtimeToken = ""
|
|
544
|
+
} = {}) {
|
|
545
|
+
const expectedCommandHash = normalizeNullableText(commandHash);
|
|
546
|
+
const expectedRuntimeToken = normalizeNullableText(runtimeToken);
|
|
547
|
+
const reported = normalizeProcessIdentity(reportedIdentity);
|
|
548
|
+
if (
|
|
549
|
+
processIdentityIsWellFormed(reported) &&
|
|
550
|
+
reported.commandHash === expectedCommandHash &&
|
|
551
|
+
reported.runtimeToken === expectedRuntimeToken
|
|
552
|
+
) {
|
|
553
|
+
return reported;
|
|
554
|
+
}
|
|
555
|
+
const normalizedPid = Number(pid);
|
|
556
|
+
const processStat = hostIdentityPlatform && Number.isSafeInteger(normalizedPid) && normalizedPid > 0
|
|
557
|
+
? await captureProcessStart(normalizedPid)
|
|
558
|
+
: null;
|
|
559
|
+
if (!processStat?.startTimeTicks || processStat.processGroupId !== normalizedPid) {
|
|
560
|
+
const error = new Error(`${label} process identity could not be captured.`);
|
|
561
|
+
error.code = unavailableCode;
|
|
562
|
+
throw error;
|
|
563
|
+
}
|
|
564
|
+
return {
|
|
565
|
+
commandHash: expectedCommandHash,
|
|
566
|
+
platform: hostIdentityPlatform,
|
|
567
|
+
runtimeToken: expectedRuntimeToken,
|
|
568
|
+
startTimeTicks: processStat.startTimeTicks,
|
|
569
|
+
version: PROCESS_IDENTITY_VERSION
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
return Object.freeze({
|
|
574
|
+
normalize: normalizeProcessIdentity,
|
|
575
|
+
isValid: processIdentityIsWellFormed,
|
|
576
|
+
normalizeInspection: normalizeProcessIdentityInspection,
|
|
577
|
+
capture: captureProcessIdentity,
|
|
578
|
+
inspect: inspectProcessIdentity,
|
|
579
|
+
stop: stopProcessGroup,
|
|
580
|
+
isProcessAlive: processIsAlive
|
|
581
|
+
});
|
|
582
|
+
}
|
|
@@ -4,9 +4,18 @@ import { createCodexConversationDriver } from "./providers/codexDriver.js";
|
|
|
4
4
|
import { createOpenCodeConversationDriver } from "./providers/opencodeDriver.js";
|
|
5
5
|
import { validateCommandWrapper } from "./commandWrapper.js";
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
/**
|
|
8
|
+
* Creates the engine driver a conversation runs on and opens its provider. The connections,
|
|
9
|
+
* limits and application tools are shared by every driver; each call names its host.
|
|
10
|
+
* @param {object} options
|
|
11
|
+
* @param {import("../types.js").ConversationConnections | undefined} options.connections
|
|
12
|
+
* @param {import("../types.js").ConversationApiClientFactory | undefined} options.apiClientFactory
|
|
13
|
+
* @param {typeof fetch | undefined} options.fetch
|
|
14
|
+
* @param {import("../types.js").ConversationLimits} options.limits
|
|
15
|
+
* @param {object | undefined} options.actions The application actions offered as tools, when there are any.
|
|
16
|
+
* @param {import("../types.js").ServiceToolCatalog | undefined} options.toolCatalog
|
|
17
|
+
*/
|
|
18
|
+
export function createConversationProviderFactory({ connections, apiClientFactory, fetch, limits, actions, toolCatalog }) {
|
|
10
19
|
function createDriver(engine, host) {
|
|
11
20
|
if (host?.nativeTools !== undefined && typeof host.nativeTools !== "boolean") throw new TypeError("host.nativeTools must be an explicit server-side boolean grant.");
|
|
12
21
|
if (host?.commandWrapper !== undefined) {
|
|
@@ -21,10 +30,16 @@ export function createConversationProviderFactory({ connections, apiClientFactor
|
|
|
21
30
|
if ((actions || toolCatalog) && !driver.capabilities.tools) throw new TypeError(`Application tools are not yet supported by the ${engine} conversation adapter.`);
|
|
22
31
|
return driver;
|
|
23
32
|
}
|
|
33
|
+
|
|
34
|
+
function open(driver, { binding, conversation, onFailure, writeBinding }) {
|
|
35
|
+
return driver.open({ binding, conversation, onFailure, writeBinding });
|
|
36
|
+
}
|
|
37
|
+
|
|
24
38
|
return Object.freeze({
|
|
25
39
|
createDriver,
|
|
26
|
-
open
|
|
27
|
-
return driver.open({ binding, conversation, onFailure, writeBinding });
|
|
28
|
-
}
|
|
40
|
+
open
|
|
29
41
|
});
|
|
30
42
|
}
|
|
43
|
+
|
|
44
|
+
/** @typedef {ReturnType<typeof createConversationProviderFactory>} ConversationProviderFactory */
|
|
45
|
+
/** @typedef {ReturnType<ConversationProviderFactory["createDriver"]>} ConversationDriver */
|