@ai-sdk/harness-cline 1.0.60 → 1.0.61
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/CHANGELOG.md +10 -0
- package/dist/index.d.ts +786 -783
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +1788 -2046
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
package/dist/index.js
CHANGED
|
@@ -1,2144 +1,1886 @@
|
|
|
1
|
-
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
} from "
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
RuntimeOAuthTokenManager
|
|
21
|
-
} from "@cline/core";
|
|
22
|
-
|
|
23
|
-
// src/cline-auth.ts
|
|
24
|
-
import {
|
|
25
|
-
getAiGatewayAuthFromEnv,
|
|
26
|
-
isHarnessAuthenticationEnvironment
|
|
27
|
-
} from "@ai-sdk/harness/utils";
|
|
28
|
-
function resolveClineEnv({
|
|
29
|
-
auth = "auto",
|
|
30
|
-
env = process.env
|
|
31
|
-
}) {
|
|
32
|
-
if (isHarnessAuthenticationEnvironment(auth)) {
|
|
33
|
-
const gatewayAuth2 = getAiGatewayAuthFromEnv({ env: auth });
|
|
34
|
-
return gatewayAuth2.apiKey ? toGatewayClineEnv(gatewayAuth2) : pickDirectClineEnv({ env: auth });
|
|
35
|
-
}
|
|
36
|
-
if (auth === "direct") {
|
|
37
|
-
return pickDirectClineEnv({ env });
|
|
38
|
-
}
|
|
39
|
-
const gatewayAuth = getAiGatewayAuthFromEnv({ env });
|
|
40
|
-
if (auth === "ai-gateway" || gatewayAuth.apiKey) {
|
|
41
|
-
return toGatewayClineEnv(gatewayAuth);
|
|
42
|
-
}
|
|
43
|
-
return pickDirectClineEnv({ env });
|
|
1
|
+
import { HARNESS_V1_BUILTIN_TOOLS, HarnessCapabilityUnsupportedError, commonTool } from "@ai-sdk/harness";
|
|
2
|
+
import { getAiGatewayAuthFromEnv, getRestrictedSandboxSession, isHarnessAuthenticationEnvironment, resolveSandboxHomeDir, shellQuote, shouldResolveNativeSubscription } from "@ai-sdk/harness/utils";
|
|
3
|
+
import { safeParseJSON, secureJsonParse, tool } from "@ai-sdk/provider-utils";
|
|
4
|
+
import { z } from "zod/v4";
|
|
5
|
+
import { homedir } from "node:os";
|
|
6
|
+
import path, { join, resolve } from "node:path";
|
|
7
|
+
import { Llms, ProviderSettingsManager, RuntimeOAuthTokenManager, createDefaultTools } from "@cline/core";
|
|
8
|
+
import { createHash } from "node:crypto";
|
|
9
|
+
import { Agent, createTool } from "@cline/agents";
|
|
10
|
+
//#region src/cline-auth.ts
|
|
11
|
+
function resolveClineEnv({ auth = "auto", env = process.env }) {
|
|
12
|
+
if (isHarnessAuthenticationEnvironment(auth)) {
|
|
13
|
+
const gatewayAuth = getAiGatewayAuthFromEnv({ env: auth });
|
|
14
|
+
return gatewayAuth.apiKey ? toGatewayClineEnv(gatewayAuth) : pickDirectClineEnv({ env: auth });
|
|
15
|
+
}
|
|
16
|
+
if (auth === "direct") return pickDirectClineEnv({ env });
|
|
17
|
+
const gatewayAuth = getAiGatewayAuthFromEnv({ env });
|
|
18
|
+
if (auth === "ai-gateway" || gatewayAuth.apiKey) return toGatewayClineEnv(gatewayAuth);
|
|
19
|
+
return pickDirectClineEnv({ env });
|
|
44
20
|
}
|
|
45
21
|
function toGatewayClineEnv(gatewayAuth) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
22
|
+
return {
|
|
23
|
+
...gatewayAuth.apiKey ? { AI_GATEWAY_API_KEY: gatewayAuth.apiKey } : {},
|
|
24
|
+
AI_GATEWAY_BASE_URL: gatewayAuth.baseUrl
|
|
25
|
+
};
|
|
50
26
|
}
|
|
51
|
-
function pickDirectClineEnv({
|
|
52
|
-
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
...env.CLINE_API_BASE_URL ? { CLINE_API_BASE_URL: env.CLINE_API_BASE_URL } : {}
|
|
57
|
-
};
|
|
27
|
+
function pickDirectClineEnv({ env }) {
|
|
28
|
+
return {
|
|
29
|
+
...env.CLINE_API_KEY ? { CLINE_API_KEY: env.CLINE_API_KEY } : {},
|
|
30
|
+
...env.CLINE_API_BASE_URL ? { CLINE_API_BASE_URL: env.CLINE_API_BASE_URL } : {}
|
|
31
|
+
};
|
|
58
32
|
}
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
async function resolveClineAuthentication({
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
33
|
+
//#endregion
|
|
34
|
+
//#region src/cline-subscription.ts
|
|
35
|
+
async function resolveClineAuthentication({ auth, env = process.env, providerId = "cline", apiKey, readSubscription = readClineSubscription }) {
|
|
36
|
+
const environment = resolveClineEnv({
|
|
37
|
+
auth,
|
|
38
|
+
env
|
|
39
|
+
});
|
|
40
|
+
if (isHarnessAuthenticationEnvironment(auth)) return { environment };
|
|
41
|
+
if (!shouldResolveNativeSubscription({
|
|
42
|
+
auth,
|
|
43
|
+
env: environment,
|
|
44
|
+
hasDirectCredential: apiKey != null || hasApplicableProviderApiKey({
|
|
45
|
+
providerId,
|
|
46
|
+
env
|
|
47
|
+
})
|
|
48
|
+
})) return { environment };
|
|
49
|
+
const subscriptionApiKey = await readSubscription({
|
|
50
|
+
providerId,
|
|
51
|
+
env
|
|
52
|
+
});
|
|
53
|
+
return subscriptionApiKey == null ? { environment } : {
|
|
54
|
+
environment,
|
|
55
|
+
subscriptionApiKey
|
|
56
|
+
};
|
|
81
57
|
}
|
|
82
|
-
async function readClineSubscription({
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
})
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
});
|
|
91
|
-
const manager = new ProviderSettingsManager({ filePath });
|
|
92
|
-
const entry = manager.read().providers[providerId];
|
|
93
|
-
if (entry?.tokenSource !== "oauth") return void 0;
|
|
94
|
-
const resolution = await new RuntimeOAuthTokenManager({
|
|
95
|
-
providerSettingsManager: manager
|
|
96
|
-
}).resolveProviderApiKey({ providerId });
|
|
97
|
-
return resolution?.apiKey;
|
|
58
|
+
async function readClineSubscription({ providerId, env = process.env, homeDirectory = homedir() }) {
|
|
59
|
+
const filePath = resolveClineProviderSettingsPath({
|
|
60
|
+
env,
|
|
61
|
+
homeDirectory
|
|
62
|
+
});
|
|
63
|
+
const manager = new ProviderSettingsManager({ filePath });
|
|
64
|
+
if (manager.read().providers[providerId]?.tokenSource !== "oauth") return void 0;
|
|
65
|
+
return (await new RuntimeOAuthTokenManager({ providerSettingsManager: manager }).resolveProviderApiKey({ providerId }))?.apiKey;
|
|
98
66
|
}
|
|
99
|
-
function resolveClineProviderSettingsPath({
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
if (explicitPath) return resolve(explicitPath);
|
|
105
|
-
const dataDirectory = env.CLINE_DATA_DIR?.trim();
|
|
106
|
-
return join(
|
|
107
|
-
dataDirectory ? resolve(dataDirectory) : join(homeDirectory, ".cline", "data"),
|
|
108
|
-
"settings",
|
|
109
|
-
"providers.json"
|
|
110
|
-
);
|
|
67
|
+
function resolveClineProviderSettingsPath({ env, homeDirectory }) {
|
|
68
|
+
const explicitPath = env.CLINE_PROVIDER_SETTINGS_PATH?.trim();
|
|
69
|
+
if (explicitPath) return resolve(explicitPath);
|
|
70
|
+
const dataDirectory = env.CLINE_DATA_DIR?.trim();
|
|
71
|
+
return join(dataDirectory ? resolve(dataDirectory) : join(homeDirectory, ".cline", "data"), "settings", "providers.json");
|
|
111
72
|
}
|
|
112
|
-
function hasApplicableProviderApiKey({
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
}) {
|
|
116
|
-
if (providerId === "cline" && env.CLINE_API_KEY != null) return true;
|
|
117
|
-
return Llms.getProviderCollectionSync(providerId)?.provider.env?.some(
|
|
118
|
-
(name) => env[name] != null
|
|
119
|
-
) ?? false;
|
|
73
|
+
function hasApplicableProviderApiKey({ providerId, env }) {
|
|
74
|
+
if (providerId === "cline" && env.CLINE_API_KEY != null) return true;
|
|
75
|
+
return Llms.getProviderCollectionSync(providerId)?.provider.env?.some((name) => env[name] != null) ?? false;
|
|
120
76
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
import path from "path";
|
|
125
|
-
import {
|
|
126
|
-
safeParseJSON
|
|
127
|
-
} from "@ai-sdk/provider-utils";
|
|
128
|
-
import { z } from "zod/v4";
|
|
129
|
-
var CLINE_HISTORY_FILE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*\.json$/;
|
|
77
|
+
//#endregion
|
|
78
|
+
//#region src/cline-resume-state.ts
|
|
79
|
+
const CLINE_HISTORY_FILE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*\.json$/;
|
|
130
80
|
function safeClineHistoryFileName(historyFileName) {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
}
|
|
134
|
-
return historyFileName;
|
|
81
|
+
if (!CLINE_HISTORY_FILE_NAME_PATTERN.test(historyFileName)) throw new Error(`Invalid Cline history file name: ${historyFileName}`);
|
|
82
|
+
return historyFileName;
|
|
135
83
|
}
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
84
|
+
const clineHistoryFileNameSchema = z.string().refine((historyFileName) => CLINE_HISTORY_FILE_NAME_PATTERN.test(historyFileName), "Cline historyFileName must be a safe .json basename.");
|
|
85
|
+
/**
|
|
86
|
+
* Schema for the adapter-specific portion of lifecycle state `data` produced
|
|
87
|
+
* by the Cline harness's resumable lifecycle methods. Carries the basename of
|
|
88
|
+
* the serialized conversation history. The actual history bytes live in a
|
|
89
|
+
* private, session-scoped directory under sandbox HOME so they survive
|
|
90
|
+
* cross-process resume without appearing in the agent workspace.
|
|
91
|
+
*/
|
|
92
|
+
const clineResumeStateSchema = z.looseObject({ historyFileName: clineHistoryFileNameSchema.optional() });
|
|
93
|
+
const CLINE_DEFAULT_HISTORY_FILE_NAME = "history.json";
|
|
144
94
|
function resolveClinePrivateSessionDirectory(input) {
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
sessionKey
|
|
151
|
-
);
|
|
152
|
-
const relativePath = path.posix.relative(
|
|
153
|
-
input.sessionWorkDir,
|
|
154
|
-
privateSessionDir
|
|
155
|
-
);
|
|
156
|
-
if (relativePath === "" || !relativePath.startsWith("../") && !path.posix.isAbsolute(relativePath)) {
|
|
157
|
-
throw new Error(
|
|
158
|
-
`Cline private session directory ${JSON.stringify(privateSessionDir)} must be outside sessionWorkDir ${JSON.stringify(input.sessionWorkDir)}.`
|
|
159
|
-
);
|
|
160
|
-
}
|
|
161
|
-
return privateSessionDir;
|
|
95
|
+
const sessionKey = createHash("sha256").update(input.sessionId).digest("hex");
|
|
96
|
+
const privateSessionDir = path.posix.join(input.sandboxHomeDir, ".ai-sdk", "harness-cline", sessionKey);
|
|
97
|
+
const relativePath = path.posix.relative(input.sessionWorkDir, privateSessionDir);
|
|
98
|
+
if (relativePath === "" || !relativePath.startsWith("../") && !path.posix.isAbsolute(relativePath)) throw new Error(`Cline private session directory ${JSON.stringify(privateSessionDir)} must be outside sessionWorkDir ${JSON.stringify(input.sessionWorkDir)}.`);
|
|
99
|
+
return privateSessionDir;
|
|
162
100
|
}
|
|
163
101
|
function resolveContainedSandboxPath(input) {
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
const relativePath = path.posix.relative(historyDir, filePath);
|
|
170
|
-
if (relativePath === "" || relativePath.startsWith("..") || path.posix.isAbsolute(relativePath)) {
|
|
171
|
-
throw new Error(
|
|
172
|
-
`Invalid Cline history file name: ${input.historyFileName}`
|
|
173
|
-
);
|
|
174
|
-
}
|
|
175
|
-
return filePath;
|
|
102
|
+
const historyDir = path.posix.resolve(input.privateSessionDir);
|
|
103
|
+
const filePath = path.posix.resolve(historyDir, safeClineHistoryFileName(input.historyFileName));
|
|
104
|
+
const relativePath = path.posix.relative(historyDir, filePath);
|
|
105
|
+
if (relativePath === "" || relativePath.startsWith("..") || path.posix.isAbsolute(relativePath)) throw new Error(`Invalid Cline history file name: ${input.historyFileName}`);
|
|
106
|
+
return filePath;
|
|
176
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Persist the runtime's conversation history into private sandbox state so a
|
|
110
|
+
* future process can resume the session after
|
|
111
|
+
* `HarnessV1SandboxProvider.resume?.({ sessionId })` reattaches the sandbox.
|
|
112
|
+
*/
|
|
177
113
|
async function persistHistoryToSandbox(args) {
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
114
|
+
const remotePath = resolveContainedSandboxPath({
|
|
115
|
+
privateSessionDir: args.privateSessionDir,
|
|
116
|
+
historyFileName: args.historyFileName
|
|
117
|
+
});
|
|
118
|
+
await args.sandbox.writeTextFile({
|
|
119
|
+
path: remotePath,
|
|
120
|
+
content: JSON.stringify(args.messages),
|
|
121
|
+
...args.abortSignal ? { abortSignal: args.abortSignal } : {}
|
|
122
|
+
});
|
|
187
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Pull a previously persisted conversation history from the sandbox. Returns
|
|
126
|
+
* `undefined` when the file is missing or unparsable — resume then falls back
|
|
127
|
+
* to a fresh conversation rather than failing `doStart`.
|
|
128
|
+
*/
|
|
188
129
|
async function pullHistoryFromSandbox(args) {
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
130
|
+
const remotePath = resolveContainedSandboxPath({
|
|
131
|
+
privateSessionDir: args.privateSessionDir,
|
|
132
|
+
historyFileName: args.historyFileName
|
|
133
|
+
});
|
|
134
|
+
const content = await args.sandbox.readTextFile({
|
|
135
|
+
path: remotePath,
|
|
136
|
+
...args.abortSignal ? { abortSignal: args.abortSignal } : {}
|
|
137
|
+
});
|
|
138
|
+
if (content == null) return void 0;
|
|
139
|
+
const parsed = await safeParseJSON({ text: content });
|
|
140
|
+
return parsed.success && Array.isArray(parsed.value) ? parsed.value : void 0;
|
|
200
141
|
}
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
HarnessCapabilityUnsupportedError as HarnessCapabilityUnsupportedError2
|
|
205
|
-
} from "@ai-sdk/harness";
|
|
206
|
-
import {
|
|
207
|
-
getRestrictedSandboxSession,
|
|
208
|
-
resolveSandboxHomeDir
|
|
209
|
-
} from "@ai-sdk/harness/utils";
|
|
210
|
-
import {
|
|
211
|
-
Agent,
|
|
212
|
-
createTool as createTool2
|
|
213
|
-
} from "@cline/agents";
|
|
214
|
-
import { Llms as Llms2 } from "@cline/core";
|
|
215
|
-
|
|
216
|
-
// src/cline-mcp.ts
|
|
217
|
-
var CLINE_CORE_PACKAGE = "@cline/core";
|
|
142
|
+
//#endregion
|
|
143
|
+
//#region src/cline-mcp.ts
|
|
144
|
+
const CLINE_CORE_PACKAGE = "@cline/core";
|
|
218
145
|
function isRecord(value) {
|
|
219
|
-
|
|
146
|
+
return value != null && typeof value === "object" && !Array.isArray(value);
|
|
220
147
|
}
|
|
221
148
|
function optionalStringArray(value) {
|
|
222
|
-
|
|
149
|
+
return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
|
|
223
150
|
}
|
|
224
151
|
function optionalStringRecord(value) {
|
|
225
|
-
|
|
152
|
+
return isRecord(value) && Object.values(value).every((item) => typeof item === "string") ? value : void 0;
|
|
226
153
|
}
|
|
227
154
|
function resolveTransport(input) {
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
}
|
|
251
|
-
throw new Error(
|
|
252
|
-
`Cline MCP server ${JSON.stringify(input.serverName)} must define a transport, command, or URL.`
|
|
253
|
-
);
|
|
155
|
+
if (isRecord(input.config.transport)) return input.config.transport;
|
|
156
|
+
if (typeof input.config.command === "string") {
|
|
157
|
+
const args = optionalStringArray(input.config.args);
|
|
158
|
+
const env = optionalStringRecord(input.config.env);
|
|
159
|
+
return {
|
|
160
|
+
type: "stdio",
|
|
161
|
+
command: input.config.command,
|
|
162
|
+
...args ? { args } : {},
|
|
163
|
+
...typeof input.config.cwd === "string" ? { cwd: input.config.cwd } : {},
|
|
164
|
+
...env ? { env } : {}
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
if (typeof input.config.url === "string") {
|
|
168
|
+
const configuredType = input.config.type ?? input.config.transportType;
|
|
169
|
+
const headers = optionalStringRecord(input.config.headers);
|
|
170
|
+
return {
|
|
171
|
+
type: configuredType === "http" || configuredType === "streamableHttp" ? "streamableHttp" : "sse",
|
|
172
|
+
url: input.config.url,
|
|
173
|
+
...headers ? { headers } : {}
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
throw new Error(`Cline MCP server ${JSON.stringify(input.serverName)} must define a transport, command, or URL.`);
|
|
254
177
|
}
|
|
255
178
|
function resolveRegistration(input) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
...typeof input.config.disabled === "boolean" ? { disabled: input.config.disabled } : {},
|
|
268
|
-
...isRecord(input.config.metadata) ? { metadata: input.config.metadata } : {},
|
|
269
|
-
...isRecord(input.config.oauth) ? { oauth: input.config.oauth } : {}
|
|
270
|
-
};
|
|
179
|
+
if (!isRecord(input.config)) throw new Error(`Cline MCP server ${JSON.stringify(input.serverName)} must be configured with an object value.`);
|
|
180
|
+
return {
|
|
181
|
+
name: input.serverName,
|
|
182
|
+
transport: resolveTransport({
|
|
183
|
+
serverName: input.serverName,
|
|
184
|
+
config: input.config
|
|
185
|
+
}),
|
|
186
|
+
...typeof input.config.disabled === "boolean" ? { disabled: input.config.disabled } : {},
|
|
187
|
+
...isRecord(input.config.metadata) ? { metadata: input.config.metadata } : {},
|
|
188
|
+
...isRecord(input.config.oauth) ? { oauth: input.config.oauth } : {}
|
|
189
|
+
};
|
|
271
190
|
}
|
|
272
191
|
async function createClineMcpRuntime(input) {
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
await manager.dispose().catch(() => {
|
|
304
|
-
});
|
|
305
|
-
throw error;
|
|
306
|
-
}
|
|
307
|
-
return {
|
|
308
|
-
tools,
|
|
309
|
-
toolNames: new Set(tools.map((tool2) => tool2.name)),
|
|
310
|
-
dispose: async () => manager.dispose()
|
|
311
|
-
};
|
|
192
|
+
if (input.mcpServers == null || Object.keys(input.mcpServers).length === 0) return {
|
|
193
|
+
tools: [],
|
|
194
|
+
toolNames: /* @__PURE__ */ new Set(),
|
|
195
|
+
dispose: async () => {}
|
|
196
|
+
};
|
|
197
|
+
const { createDefaultMcpServerClientFactory, createMcpTools, InMemoryMcpManager } = await import(CLINE_CORE_PACKAGE);
|
|
198
|
+
const manager = new InMemoryMcpManager({ clientFactory: createDefaultMcpServerClientFactory() });
|
|
199
|
+
const tools = [];
|
|
200
|
+
try {
|
|
201
|
+
for (const [serverName, config] of Object.entries(input.mcpServers)) {
|
|
202
|
+
const registration = resolveRegistration({
|
|
203
|
+
serverName,
|
|
204
|
+
config
|
|
205
|
+
});
|
|
206
|
+
await manager.registerServer(registration);
|
|
207
|
+
if (registration.disabled) continue;
|
|
208
|
+
tools.push(...await createMcpTools({
|
|
209
|
+
serverName,
|
|
210
|
+
provider: manager
|
|
211
|
+
}));
|
|
212
|
+
}
|
|
213
|
+
} catch (error) {
|
|
214
|
+
await manager.dispose().catch(() => {});
|
|
215
|
+
throw error;
|
|
216
|
+
}
|
|
217
|
+
return {
|
|
218
|
+
tools,
|
|
219
|
+
toolNames: new Set(tools.map((tool) => tool.name)),
|
|
220
|
+
dispose: async () => manager.dispose()
|
|
221
|
+
};
|
|
312
222
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
import path2 from "path";
|
|
316
|
-
import { shellQuote } from "@ai-sdk/harness/utils";
|
|
223
|
+
//#endregion
|
|
224
|
+
//#region src/cline-remote-ops.ts
|
|
317
225
|
function createClineRemoteOps(options) {
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
...typeof input.context === "number" && input.context > 0 ? ["-C", String(input.context)] : [],
|
|
495
|
-
...input.glob ? ["--include", input.glob] : []
|
|
496
|
-
];
|
|
497
|
-
const limit = Math.max(1, input.limit ?? 100);
|
|
498
|
-
const result = await runShell({
|
|
499
|
-
command: [
|
|
500
|
-
`if [ ! -e ${shellQuote(
|
|
501
|
-
target
|
|
502
|
-
)} ]; then echo "__CLINE_GREP_NOT_FOUND__"; exit 2; fi`,
|
|
503
|
-
`grep ${flags.map(shellQuote).join(" ")} -- ${shellQuote(
|
|
504
|
-
pattern
|
|
505
|
-
)} ${shellQuote(target)} 2>/dev/null | head -n ${limit}`
|
|
506
|
-
].join("; ")
|
|
507
|
-
});
|
|
508
|
-
const output = result.output.trim();
|
|
509
|
-
if (output.includes("__CLINE_GREP_NOT_FOUND__")) {
|
|
510
|
-
throw new Error(`Path not found: ${input.path ?? "."}`);
|
|
511
|
-
}
|
|
512
|
-
return output || "No matches found";
|
|
513
|
-
},
|
|
514
|
-
async glob(pattern, inputPath = ".", limit = 1e3) {
|
|
515
|
-
const remotePath = resolvePath(inputPath);
|
|
516
|
-
const target = await resolveReadableSandboxPath({
|
|
517
|
-
remotePath,
|
|
518
|
-
inputPath
|
|
519
|
-
});
|
|
520
|
-
const result = await runShell({
|
|
521
|
-
command: [
|
|
522
|
-
`if [ ! -e ${shellQuote(
|
|
523
|
-
target
|
|
524
|
-
)} ]; then echo "__CLINE_FIND_NOT_FOUND__"; exit 2; fi`,
|
|
525
|
-
`if [ -d ${shellQuote(target)} ]; then find ${shellQuote(
|
|
526
|
-
target
|
|
527
|
-
)} -type f -print; else printf '%s\\n' ${shellQuote(target)}; fi`
|
|
528
|
-
].join("; ")
|
|
529
|
-
});
|
|
530
|
-
const output = result.output.trim();
|
|
531
|
-
if (output.includes("__CLINE_FIND_NOT_FOUND__")) {
|
|
532
|
-
throw new Error(`Path not found: ${inputPath}`);
|
|
533
|
-
}
|
|
534
|
-
return output.split("\n").filter(Boolean).map((absolutePath) => {
|
|
535
|
-
if (absolutePath === target) {
|
|
536
|
-
return path2.posix.basename(absolutePath);
|
|
537
|
-
}
|
|
538
|
-
return path2.posix.relative(target, absolutePath);
|
|
539
|
-
}).filter(
|
|
540
|
-
(candidate) => candidate.length > 0 && path2.matchesGlob(candidate, pattern)
|
|
541
|
-
).sort(
|
|
542
|
-
(left, right) => left.toLowerCase().localeCompare(right.toLowerCase())
|
|
543
|
-
).slice(0, limit);
|
|
544
|
-
},
|
|
545
|
-
async ls(inputPath = ".", limit = 500) {
|
|
546
|
-
const remotePath = resolvePath(inputPath);
|
|
547
|
-
const target = await resolveReadableSandboxPath({
|
|
548
|
-
remotePath,
|
|
549
|
-
inputPath
|
|
550
|
-
});
|
|
551
|
-
const result = await runShell({
|
|
552
|
-
command: [
|
|
553
|
-
`if [ ! -e ${shellQuote(
|
|
554
|
-
target
|
|
555
|
-
)} ]; then echo "__CLINE_LS_NOT_FOUND__"; exit 2; fi`,
|
|
556
|
-
`if [ ! -d ${shellQuote(
|
|
557
|
-
target
|
|
558
|
-
)} ]; then echo "__CLINE_LS_NOT_DIR__"; exit 3; fi`,
|
|
559
|
-
`cd ${shellQuote(target)}`,
|
|
560
|
-
"ls -1Ap"
|
|
561
|
-
].join("; ")
|
|
562
|
-
});
|
|
563
|
-
const output = result.output.trim();
|
|
564
|
-
if (output.includes("__CLINE_LS_NOT_FOUND__")) {
|
|
565
|
-
throw new Error(`Path not found: ${inputPath}`);
|
|
566
|
-
}
|
|
567
|
-
if (output.includes("__CLINE_LS_NOT_DIR__")) {
|
|
568
|
-
throw new Error(`Not a directory: ${inputPath}`);
|
|
569
|
-
}
|
|
570
|
-
return output.split("\n").filter(Boolean).map((line) => line.replace(/[*=@|]$/, "")).sort(
|
|
571
|
-
(left, right) => left.toLowerCase().localeCompare(right.toLowerCase())
|
|
572
|
-
).slice(0, limit);
|
|
573
|
-
}
|
|
574
|
-
};
|
|
226
|
+
const { sandbox, workDir } = options;
|
|
227
|
+
const normalizedWorkDir = path.posix.normalize(workDir);
|
|
228
|
+
const isInsidePath = ({ parent, candidate }) => {
|
|
229
|
+
const relative = path.posix.relative(parent, candidate);
|
|
230
|
+
return relative === "" || relative !== ".." && !relative.startsWith("../") && !path.posix.isAbsolute(relative);
|
|
231
|
+
};
|
|
232
|
+
const assertWorkspacePath = (inputPath) => {
|
|
233
|
+
const normalized = path.posix.normalize(inputPath);
|
|
234
|
+
if (!isInsidePath({
|
|
235
|
+
parent: normalizedWorkDir,
|
|
236
|
+
candidate: normalized
|
|
237
|
+
})) throw new Error(`Cline path escapes the workspace: ${inputPath}`);
|
|
238
|
+
return normalized;
|
|
239
|
+
};
|
|
240
|
+
const resolvePath = (inputPath) => {
|
|
241
|
+
const resolved = path.posix.isAbsolute(inputPath) ? path.posix.normalize(inputPath) : path.posix.normalize(path.posix.join(normalizedWorkDir, inputPath));
|
|
242
|
+
return assertWorkspacePath(resolved);
|
|
243
|
+
};
|
|
244
|
+
const runShell = async ({ command, signal }) => {
|
|
245
|
+
const result = await sandbox.run({
|
|
246
|
+
command,
|
|
247
|
+
workingDirectory: normalizedWorkDir,
|
|
248
|
+
...signal ? { abortSignal: signal } : {}
|
|
249
|
+
});
|
|
250
|
+
return {
|
|
251
|
+
exitCode: result.exitCode,
|
|
252
|
+
output: `${result.stdout}${result.stderr}`
|
|
253
|
+
};
|
|
254
|
+
};
|
|
255
|
+
const lastOutputLine = (output) => output.trim().split("\n").filter(Boolean).at(-1);
|
|
256
|
+
const resolveExistingSandboxPath = async ({ remotePath, inputPath, missingMessage }) => {
|
|
257
|
+
const result = await runShell({ command: [
|
|
258
|
+
`target=${shellQuote(remotePath)}`,
|
|
259
|
+
"if [ ! -e \"$target\" ]; then echo \"__CLINE_REALPATH_NOT_FOUND__\"; exit 2; fi",
|
|
260
|
+
"resolved=$(realpath \"$target\" 2>/dev/null) || { echo \"__CLINE_REALPATH_FAILED__\"; exit 3; }",
|
|
261
|
+
"printf '%s\\n' \"$resolved\""
|
|
262
|
+
].join("; ") });
|
|
263
|
+
if (result.output.includes("__CLINE_REALPATH_NOT_FOUND__")) throw new Error(missingMessage ?? `Path not found: ${inputPath}`);
|
|
264
|
+
if (result.output.includes("__CLINE_REALPATH_FAILED__") || result.exitCode !== 0) throw new Error(`Unable to resolve path: ${inputPath}`);
|
|
265
|
+
const resolvedPath = lastOutputLine(result.output);
|
|
266
|
+
if (!resolvedPath) throw new Error(`Unable to resolve path: ${inputPath}`);
|
|
267
|
+
return resolvedPath;
|
|
268
|
+
};
|
|
269
|
+
const resolveReadableSandboxPath = async ({ remotePath, inputPath, missingMessage }) => assertWorkspacePath(await resolveExistingSandboxPath({
|
|
270
|
+
remotePath,
|
|
271
|
+
inputPath,
|
|
272
|
+
...missingMessage ? { missingMessage } : {}
|
|
273
|
+
}));
|
|
274
|
+
const resolveWritableSandboxPath = async ({ remotePath, inputPath }) => {
|
|
275
|
+
const result = await runShell({ command: [
|
|
276
|
+
`target=${shellQuote(remotePath)}`,
|
|
277
|
+
"if [ -e \"$target\" ] || [ -L \"$target\" ]; then resolved=$(realpath \"$target\" 2>/dev/null) || { echo \"__CLINE_REALPATH_FAILED__\"; exit 3; }; printf '%s\\n' \"$resolved\"; exit 0; fi",
|
|
278
|
+
"dir=$(dirname \"$target\")",
|
|
279
|
+
"base=$(basename \"$target\")",
|
|
280
|
+
"missing=\"$base\"",
|
|
281
|
+
"while [ ! -e \"$dir\" ] && [ ! -L \"$dir\" ]; do parent=$(dirname \"$dir\"); if [ \"$parent\" = \"$dir\" ]; then echo \"__CLINE_REALPATH_NOT_FOUND__\"; exit 2; fi; missing=\"$(basename \"$dir\")/$missing\"; dir=\"$parent\"; done",
|
|
282
|
+
"resolved_dir=$(realpath \"$dir\" 2>/dev/null) || { echo \"__CLINE_REALPATH_FAILED__\"; exit 3; }",
|
|
283
|
+
"printf '%s/%s\\n' \"$resolved_dir\" \"$missing\""
|
|
284
|
+
].join("; ") });
|
|
285
|
+
if (result.output.includes("__CLINE_REALPATH_NOT_FOUND__") || result.output.includes("__CLINE_REALPATH_FAILED__") || result.exitCode !== 0) throw new Error(`Unable to resolve path: ${inputPath}`);
|
|
286
|
+
const resolvedPath = lastOutputLine(result.output);
|
|
287
|
+
if (!resolvedPath) throw new Error(`Unable to resolve path: ${inputPath}`);
|
|
288
|
+
return assertWorkspacePath(resolvedPath);
|
|
289
|
+
};
|
|
290
|
+
const readFile = async (inputPath) => {
|
|
291
|
+
const remotePath = resolvePath(inputPath);
|
|
292
|
+
const resolved = await resolveReadableSandboxPath({
|
|
293
|
+
remotePath,
|
|
294
|
+
inputPath,
|
|
295
|
+
missingMessage: `File not found: ${inputPath}`
|
|
296
|
+
});
|
|
297
|
+
const content = await sandbox.readTextFile({ path: resolved });
|
|
298
|
+
if (content == null) throw new Error(`File not found: ${inputPath}`);
|
|
299
|
+
return content;
|
|
300
|
+
};
|
|
301
|
+
const writeFile = async (inputPath, content) => {
|
|
302
|
+
const remotePath = resolvePath(inputPath);
|
|
303
|
+
const resolved = await resolveWritableSandboxPath({
|
|
304
|
+
remotePath,
|
|
305
|
+
inputPath
|
|
306
|
+
});
|
|
307
|
+
await sandbox.writeTextFile({
|
|
308
|
+
path: resolved,
|
|
309
|
+
content
|
|
310
|
+
});
|
|
311
|
+
};
|
|
312
|
+
const editFile = async (inputPath, oldText, newText) => {
|
|
313
|
+
const remotePath = resolvePath(inputPath);
|
|
314
|
+
const resolved = assertWorkspacePath(await resolveExistingSandboxPath({
|
|
315
|
+
remotePath,
|
|
316
|
+
inputPath,
|
|
317
|
+
missingMessage: `File not found: ${inputPath}`
|
|
318
|
+
}));
|
|
319
|
+
const current = await sandbox.readTextFile({ path: resolved });
|
|
320
|
+
if (current == null) throw new Error(`File not found: ${inputPath}`);
|
|
321
|
+
const index = current.indexOf(oldText);
|
|
322
|
+
if (index === -1) throw new Error(`Text to replace was not found in ${inputPath}`);
|
|
323
|
+
const updated = `${current.slice(0, index)}${newText}${current.slice(index + oldText.length)}`;
|
|
324
|
+
await writeFile(inputPath, updated);
|
|
325
|
+
};
|
|
326
|
+
return {
|
|
327
|
+
resolvePath,
|
|
328
|
+
readFile,
|
|
329
|
+
writeFile,
|
|
330
|
+
editFile,
|
|
331
|
+
async bash(command, input) {
|
|
332
|
+
const controller = new AbortController();
|
|
333
|
+
const timeoutId = typeof input?.timeout === "number" && input.timeout > 0 ? setTimeout(() => controller.abort(), input.timeout * 1e3) : void 0;
|
|
334
|
+
const forwardedSignal = input?.signal;
|
|
335
|
+
const onAbort = () => controller.abort();
|
|
336
|
+
forwardedSignal?.addEventListener("abort", onAbort, { once: true });
|
|
337
|
+
try {
|
|
338
|
+
const result = await runShell({
|
|
339
|
+
command,
|
|
340
|
+
signal: controller.signal
|
|
341
|
+
});
|
|
342
|
+
return {
|
|
343
|
+
output: result.output,
|
|
344
|
+
exitCode: result.exitCode
|
|
345
|
+
};
|
|
346
|
+
} finally {
|
|
347
|
+
forwardedSignal?.removeEventListener("abort", onAbort);
|
|
348
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
349
|
+
}
|
|
350
|
+
},
|
|
351
|
+
async grep(pattern, input = {}) {
|
|
352
|
+
const inputPath = input.path ?? ".";
|
|
353
|
+
const remotePath = resolvePath(inputPath);
|
|
354
|
+
const target = await resolveReadableSandboxPath({
|
|
355
|
+
remotePath,
|
|
356
|
+
inputPath
|
|
357
|
+
});
|
|
358
|
+
const flags = [
|
|
359
|
+
"-r",
|
|
360
|
+
"-n",
|
|
361
|
+
"--binary-files=without-match",
|
|
362
|
+
...input.ignoreCase ? ["-i"] : [],
|
|
363
|
+
...input.literal ? ["-F"] : [],
|
|
364
|
+
...typeof input.context === "number" && input.context > 0 ? ["-C", String(input.context)] : [],
|
|
365
|
+
...input.glob ? ["--include", input.glob] : []
|
|
366
|
+
];
|
|
367
|
+
const limit = Math.max(1, input.limit ?? 100);
|
|
368
|
+
const output = (await runShell({ command: [`if [ ! -e ${shellQuote(target)} ]; then echo "__CLINE_GREP_NOT_FOUND__"; exit 2; fi`, `grep ${flags.map(shellQuote).join(" ")} -- ${shellQuote(pattern)} ${shellQuote(target)} 2>/dev/null | head -n ${limit}`].join("; ") })).output.trim();
|
|
369
|
+
if (output.includes("__CLINE_GREP_NOT_FOUND__")) throw new Error(`Path not found: ${input.path ?? "."}`);
|
|
370
|
+
return output || "No matches found";
|
|
371
|
+
},
|
|
372
|
+
async glob(pattern, inputPath = ".", limit = 1e3) {
|
|
373
|
+
const remotePath = resolvePath(inputPath);
|
|
374
|
+
const target = await resolveReadableSandboxPath({
|
|
375
|
+
remotePath,
|
|
376
|
+
inputPath
|
|
377
|
+
});
|
|
378
|
+
const output = (await runShell({ command: [`if [ ! -e ${shellQuote(target)} ]; then echo "__CLINE_FIND_NOT_FOUND__"; exit 2; fi`, `if [ -d ${shellQuote(target)} ]; then find ${shellQuote(target)} -type f -print; else printf '%s\\n' ${shellQuote(target)}; fi`].join("; ") })).output.trim();
|
|
379
|
+
if (output.includes("__CLINE_FIND_NOT_FOUND__")) throw new Error(`Path not found: ${inputPath}`);
|
|
380
|
+
return output.split("\n").filter(Boolean).map((absolutePath) => {
|
|
381
|
+
if (absolutePath === target) return path.posix.basename(absolutePath);
|
|
382
|
+
return path.posix.relative(target, absolutePath);
|
|
383
|
+
}).filter((candidate) => candidate.length > 0 && path.matchesGlob(candidate, pattern)).sort((left, right) => left.toLowerCase().localeCompare(right.toLowerCase())).slice(0, limit);
|
|
384
|
+
},
|
|
385
|
+
async ls(inputPath = ".", limit = 500) {
|
|
386
|
+
const remotePath = resolvePath(inputPath);
|
|
387
|
+
const target = await resolveReadableSandboxPath({
|
|
388
|
+
remotePath,
|
|
389
|
+
inputPath
|
|
390
|
+
});
|
|
391
|
+
const output = (await runShell({ command: [
|
|
392
|
+
`if [ ! -e ${shellQuote(target)} ]; then echo "__CLINE_LS_NOT_FOUND__"; exit 2; fi`,
|
|
393
|
+
`if [ ! -d ${shellQuote(target)} ]; then echo "__CLINE_LS_NOT_DIR__"; exit 3; fi`,
|
|
394
|
+
`cd ${shellQuote(target)}`,
|
|
395
|
+
"ls -1Ap"
|
|
396
|
+
].join("; ") })).output.trim();
|
|
397
|
+
if (output.includes("__CLINE_LS_NOT_FOUND__")) throw new Error(`Path not found: ${inputPath}`);
|
|
398
|
+
if (output.includes("__CLINE_LS_NOT_DIR__")) throw new Error(`Not a directory: ${inputPath}`);
|
|
399
|
+
return output.split("\n").filter(Boolean).map((line) => line.replace(/[*=@|]$/, "")).sort((left, right) => left.toLowerCase().localeCompare(right.toLowerCase())).slice(0, limit);
|
|
400
|
+
}
|
|
401
|
+
};
|
|
575
402
|
}
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
403
|
+
//#endregion
|
|
404
|
+
//#region src/cline-skills.ts
|
|
405
|
+
const CLINE_SKILL_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;
|
|
406
|
+
/**
|
|
407
|
+
* Build Cline's native skills tool around an immutable in-host projection of
|
|
408
|
+
* the harness skills. The low-level Agent does not discover skills itself, so
|
|
409
|
+
* the adapter provides the same tool contract that Cline's core orchestration
|
|
410
|
+
* layer uses without involving the sandbox filesystem.
|
|
411
|
+
*/
|
|
412
|
+
function createClineSkillsRuntime({ skills }) {
|
|
413
|
+
const projectedSkills = projectClineSkills({ skills });
|
|
414
|
+
const signature = JSON.stringify(projectedSkills);
|
|
415
|
+
if (projectedSkills.length === 0) return { signature };
|
|
416
|
+
const executor = async (...executorArguments) => {
|
|
417
|
+
const [requestedSkill, args] = executorArguments;
|
|
418
|
+
const skill = projectedSkills.find((candidate) => candidate.id === normalizeSkillToken(requestedSkill));
|
|
419
|
+
if (skill == null) {
|
|
420
|
+
const availableSkills = projectedSkills.map((skill) => skill.name);
|
|
421
|
+
return availableSkills.length > 0 ? `Skill "${requestedSkill}" not found. Available skills: ${availableSkills.join(", ")}` : "No skills are currently available.";
|
|
422
|
+
}
|
|
423
|
+
return renderSkillInstructions({
|
|
424
|
+
skill,
|
|
425
|
+
args
|
|
426
|
+
});
|
|
427
|
+
};
|
|
428
|
+
executor.configuredSkills = projectedSkills.map((skill) => ({
|
|
429
|
+
id: skill.id,
|
|
430
|
+
name: skill.name,
|
|
431
|
+
description: skill.description,
|
|
432
|
+
disabled: false
|
|
433
|
+
}));
|
|
434
|
+
const tool = createDefaultTools({
|
|
435
|
+
executors: { skills: executor },
|
|
436
|
+
enableReadFiles: false,
|
|
437
|
+
enableSearch: false,
|
|
438
|
+
enableBash: false,
|
|
439
|
+
enableWebFetch: false,
|
|
440
|
+
enableApplyPatch: false,
|
|
441
|
+
enableEditor: false,
|
|
442
|
+
enableSkills: true,
|
|
443
|
+
enableAskQuestion: false,
|
|
444
|
+
enableSubmitAndExit: false
|
|
445
|
+
}).find((tool) => tool.name === "skills");
|
|
446
|
+
if (tool == null) throw new Error("Cline did not create its native skills tool.");
|
|
447
|
+
return {
|
|
448
|
+
signature,
|
|
449
|
+
tool
|
|
450
|
+
};
|
|
622
451
|
}
|
|
623
|
-
function projectClineSkills({
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
path: normalizeSkillFilePath({
|
|
648
|
-
skillName: skill.name,
|
|
649
|
-
filePath: file.path
|
|
650
|
-
}),
|
|
651
|
-
content: file.content
|
|
652
|
-
})).sort(
|
|
653
|
-
(left, right) => left.path.localeCompare(right.path) || left.content.localeCompare(right.content)
|
|
654
|
-
)
|
|
655
|
-
};
|
|
656
|
-
}).sort((left, right) => left.name.localeCompare(right.name));
|
|
452
|
+
function projectClineSkills({ skills }) {
|
|
453
|
+
const names = /* @__PURE__ */ new Set();
|
|
454
|
+
const ids = /* @__PURE__ */ new Set();
|
|
455
|
+
return skills.map((skill) => {
|
|
456
|
+
if (!CLINE_SKILL_NAME_PATTERN.test(skill.name) || skill.name === "." || skill.name === "..") throw new Error(`Invalid Cline skill name: ${skill.name}`);
|
|
457
|
+
if (names.has(skill.name)) throw new Error(`Duplicate Cline skill name: ${skill.name}`);
|
|
458
|
+
names.add(skill.name);
|
|
459
|
+
const id = normalizeSkillToken(skill.name);
|
|
460
|
+
if (ids.has(id)) throw new Error(`Duplicate Cline skill identifier: ${id}`);
|
|
461
|
+
ids.add(id);
|
|
462
|
+
return {
|
|
463
|
+
id,
|
|
464
|
+
name: skill.name,
|
|
465
|
+
description: skill.description,
|
|
466
|
+
content: skill.content,
|
|
467
|
+
files: (skill.files ?? []).map((file) => ({
|
|
468
|
+
path: normalizeSkillFilePath({
|
|
469
|
+
skillName: skill.name,
|
|
470
|
+
filePath: file.path
|
|
471
|
+
}),
|
|
472
|
+
content: file.content
|
|
473
|
+
})).sort((left, right) => left.path.localeCompare(right.path) || left.content.localeCompare(right.content))
|
|
474
|
+
};
|
|
475
|
+
}).sort((left, right) => left.name.localeCompare(right.name));
|
|
657
476
|
}
|
|
658
477
|
function normalizeSkillToken(skill) {
|
|
659
|
-
|
|
478
|
+
return skill.trim().replace(/^\/+/, "").toLowerCase();
|
|
660
479
|
}
|
|
661
|
-
function normalizeSkillFilePath({
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
const normalized = path3.posix.normalize(filePath);
|
|
666
|
-
if (normalized === "" || normalized === "." || normalized.startsWith("../") || normalized.includes("/../") || normalized.endsWith("/..") || path3.posix.isAbsolute(normalized)) {
|
|
667
|
-
throw new Error(
|
|
668
|
-
`Invalid Cline skill file path for ${skillName}: ${filePath}`
|
|
669
|
-
);
|
|
670
|
-
}
|
|
671
|
-
return normalized;
|
|
480
|
+
function normalizeSkillFilePath({ skillName, filePath }) {
|
|
481
|
+
const normalized = path.posix.normalize(filePath);
|
|
482
|
+
if (normalized === "" || normalized === "." || normalized.startsWith("../") || normalized.includes("/../") || normalized.endsWith("/..") || path.posix.isAbsolute(normalized)) throw new Error(`Invalid Cline skill file path for ${skillName}: ${filePath}`);
|
|
483
|
+
return normalized;
|
|
672
484
|
}
|
|
673
|
-
function renderSkillInstructions({
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
<command-args>${trimmedArgs}</command-args>` : "";
|
|
680
|
-
const description = skill.description.trim() ? `Description: ${skill.description.trim()}
|
|
681
|
-
|
|
682
|
-
` : "";
|
|
683
|
-
const files = skill.files.length === 0 ? "" : `
|
|
684
|
-
|
|
685
|
-
<skill-files>
|
|
686
|
-
${skill.files.map(
|
|
687
|
-
(file) => `<skill-file path=${JSON.stringify(file.path)}>
|
|
688
|
-
${file.content}
|
|
689
|
-
</skill-file>`
|
|
690
|
-
).join("\n")}
|
|
691
|
-
</skill-files>`;
|
|
692
|
-
return `<command-name>${skill.name}</command-name>${argsTag}
|
|
693
|
-
<command-instructions>
|
|
694
|
-
${description}${skill.content}${files}
|
|
695
|
-
</command-instructions>`;
|
|
485
|
+
function renderSkillInstructions({ skill, args }) {
|
|
486
|
+
const trimmedArgs = args?.trim();
|
|
487
|
+
const argsTag = trimmedArgs ? `\n<command-args>${trimmedArgs}</command-args>` : "";
|
|
488
|
+
const description = skill.description.trim() ? `Description: ${skill.description.trim()}\n\n` : "";
|
|
489
|
+
const files = skill.files.length === 0 ? "" : `\n\n<skill-files>\n${skill.files.map((file) => `<skill-file path=${JSON.stringify(file.path)}>\n${file.content}\n</skill-file>`).join("\n")}\n</skill-files>`;
|
|
490
|
+
return `<command-name>${skill.name}</command-name>${argsTag}\n<command-instructions>\n${description}${skill.content}${files}\n</command-instructions>`;
|
|
696
491
|
}
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
import {
|
|
706
|
-
HarnessCapabilityUnsupportedError
|
|
707
|
-
} from "@ai-sdk/harness";
|
|
708
|
-
import { secureJsonParse } from "@ai-sdk/provider-utils";
|
|
709
|
-
var HARNESS_ID = "cline";
|
|
492
|
+
//#endregion
|
|
493
|
+
//#region src/cline-utils.ts
|
|
494
|
+
const HARNESS_ID$1 = "cline";
|
|
495
|
+
/**
|
|
496
|
+
* Extract a single user text string from a `HarnessV1Prompt`. The Cline
|
|
497
|
+
* runtime's `run`/`continue` accept a plain string; multimodal user content
|
|
498
|
+
* is not supported in this foundational version.
|
|
499
|
+
*/
|
|
710
500
|
function extractUserText(prompt) {
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
harnessId: HARNESS_ID
|
|
724
|
-
});
|
|
725
|
-
}
|
|
726
|
-
parts.push(part.text);
|
|
727
|
-
}
|
|
728
|
-
return parts.join("\n\n");
|
|
501
|
+
if (typeof prompt === "string") return prompt;
|
|
502
|
+
const { content } = prompt;
|
|
503
|
+
if (typeof content === "string") return content;
|
|
504
|
+
const parts = [];
|
|
505
|
+
for (const part of content) {
|
|
506
|
+
if (part.type !== "text") throw new HarnessCapabilityUnsupportedError({
|
|
507
|
+
message: `cline: only text user-message parts are supported; got '${part.type}'.`,
|
|
508
|
+
harnessId: HARNESS_ID$1
|
|
509
|
+
});
|
|
510
|
+
parts.push(part.text);
|
|
511
|
+
}
|
|
512
|
+
return parts.join("\n\n");
|
|
729
513
|
}
|
|
730
514
|
function getErrorText(error) {
|
|
731
|
-
|
|
515
|
+
return error instanceof Error ? error.message : String(error);
|
|
732
516
|
}
|
|
517
|
+
/**
|
|
518
|
+
* Coerce an arbitrary tool output into a JSON-safe value for a `tool-result`
|
|
519
|
+
* stream part. Strings and JSON-serializable objects pass through; anything
|
|
520
|
+
* that can't survive `JSON.stringify` round-tripping (functions, cycles) is
|
|
521
|
+
* stringified. `undefined` becomes `null` — the runtime schema deliberately
|
|
522
|
+
* accepts `null` for tools with no output.
|
|
523
|
+
*/
|
|
733
524
|
function toToolResultValue(output) {
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
return serialized === void 0 ? String(output) : secureJsonParse(serialized);
|
|
743
|
-
} catch {
|
|
744
|
-
return String(output);
|
|
745
|
-
}
|
|
525
|
+
if (output === void 0 || output === null) return null;
|
|
526
|
+
if (typeof output === "string") return output;
|
|
527
|
+
try {
|
|
528
|
+
const serialized = JSON.stringify(output);
|
|
529
|
+
return serialized === void 0 ? String(output) : secureJsonParse(serialized);
|
|
530
|
+
} catch {
|
|
531
|
+
return String(output);
|
|
532
|
+
}
|
|
746
533
|
}
|
|
534
|
+
/**
|
|
535
|
+
* Map the Cline model-level finish reason (`AgentModelFinishReason`) onto the
|
|
536
|
+
* V4 unified finish-reason vocabulary. Used for `finish-step` parts.
|
|
537
|
+
*/
|
|
747
538
|
function mapModelFinishReason(reason) {
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
539
|
+
switch (reason) {
|
|
540
|
+
case "stop": return {
|
|
541
|
+
unified: "stop",
|
|
542
|
+
raw: reason
|
|
543
|
+
};
|
|
544
|
+
case "tool-calls": return {
|
|
545
|
+
unified: "tool-calls",
|
|
546
|
+
raw: reason
|
|
547
|
+
};
|
|
548
|
+
case "max-tokens": return {
|
|
549
|
+
unified: "length",
|
|
550
|
+
raw: reason
|
|
551
|
+
};
|
|
552
|
+
case "error": return {
|
|
553
|
+
unified: "error",
|
|
554
|
+
raw: reason
|
|
555
|
+
};
|
|
556
|
+
default: return {
|
|
557
|
+
unified: "other",
|
|
558
|
+
raw: reason
|
|
559
|
+
};
|
|
560
|
+
}
|
|
760
561
|
}
|
|
562
|
+
/**
|
|
563
|
+
* Map a completed run's status onto the V4 unified finish reason. Used for
|
|
564
|
+
* the terminal `finish` part.
|
|
565
|
+
*/
|
|
761
566
|
function mapRunFinishReason(status) {
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
567
|
+
switch (status) {
|
|
568
|
+
case "completed": return {
|
|
569
|
+
unified: "stop",
|
|
570
|
+
raw: status
|
|
571
|
+
};
|
|
572
|
+
case "aborted": return {
|
|
573
|
+
unified: "other",
|
|
574
|
+
raw: status
|
|
575
|
+
};
|
|
576
|
+
case "failed": return {
|
|
577
|
+
unified: "error",
|
|
578
|
+
raw: status
|
|
579
|
+
};
|
|
580
|
+
}
|
|
770
581
|
}
|
|
582
|
+
/**
|
|
583
|
+
* Convert the Cline runtime's cumulative `AgentUsage` into the V4 usage shape.
|
|
584
|
+
*/
|
|
771
585
|
function usageFromAgentUsage(usage) {
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
586
|
+
return {
|
|
587
|
+
inputTokens: {
|
|
588
|
+
total: usage.inputTokens,
|
|
589
|
+
noCache: void 0,
|
|
590
|
+
cacheRead: usage.cacheReadTokens,
|
|
591
|
+
cacheWrite: usage.cacheWriteTokens
|
|
592
|
+
},
|
|
593
|
+
outputTokens: {
|
|
594
|
+
total: usage.outputTokens,
|
|
595
|
+
text: void 0,
|
|
596
|
+
reasoning: usage.reasoningTokenCount
|
|
597
|
+
},
|
|
598
|
+
...usage.totalCost !== void 0 ? { raw: { totalCost: usage.totalCost } } : {}
|
|
599
|
+
};
|
|
786
600
|
}
|
|
601
|
+
/**
|
|
602
|
+
* Convert a single assistant message's per-call metrics into the V4 usage
|
|
603
|
+
* shape. Metrics may be absent; all fields stay optional in that case.
|
|
604
|
+
*/
|
|
787
605
|
function usageFromMessageMetrics(metrics) {
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
606
|
+
return {
|
|
607
|
+
inputTokens: {
|
|
608
|
+
total: metrics?.inputTokens,
|
|
609
|
+
noCache: void 0,
|
|
610
|
+
cacheRead: metrics?.cacheReadTokens,
|
|
611
|
+
cacheWrite: metrics?.cacheWriteTokens
|
|
612
|
+
},
|
|
613
|
+
outputTokens: {
|
|
614
|
+
total: metrics?.outputTokens,
|
|
615
|
+
text: void 0,
|
|
616
|
+
reasoning: metrics?.reasoningTokenCount
|
|
617
|
+
}
|
|
618
|
+
};
|
|
801
619
|
}
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
620
|
+
//#endregion
|
|
621
|
+
//#region src/cline-tools.ts
|
|
622
|
+
/**
|
|
623
|
+
* Native names of the Cline harness's built-in tools. The Cline runtime
|
|
624
|
+
* requires snake_case tool names, and every one of these already matches its
|
|
625
|
+
* cross-harness common name, so the public (wire) names and native names are
|
|
626
|
+
* identical.
|
|
627
|
+
*/
|
|
628
|
+
const CLINE_NATIVE_BUILTIN_NAMES = [
|
|
629
|
+
"ask_question",
|
|
630
|
+
"read",
|
|
631
|
+
"write",
|
|
632
|
+
"edit",
|
|
633
|
+
"bash",
|
|
634
|
+
"grep",
|
|
635
|
+
"glob",
|
|
636
|
+
"ls",
|
|
637
|
+
"skills"
|
|
814
638
|
];
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
639
|
+
const CLINE_NATIVE_TOOL_KINDS = {
|
|
640
|
+
ask_question: "readonly",
|
|
641
|
+
read: "readonly",
|
|
642
|
+
write: "edit",
|
|
643
|
+
edit: "edit",
|
|
644
|
+
bash: "bash",
|
|
645
|
+
grep: "readonly",
|
|
646
|
+
glob: "readonly",
|
|
647
|
+
ls: "readonly",
|
|
648
|
+
skills: "readonly"
|
|
825
649
|
};
|
|
826
650
|
function isClineBuiltinToolName(name) {
|
|
827
|
-
|
|
651
|
+
return CLINE_NATIVE_BUILTIN_NAMES.includes(name);
|
|
828
652
|
}
|
|
653
|
+
/**
|
|
654
|
+
* Resolve which built-in tools stay active for a session under the framework's
|
|
655
|
+
* `builtinToolFiltering`. Names map 1:1 (native === common), so filtering is a
|
|
656
|
+
* plain include/exclude.
|
|
657
|
+
*/
|
|
829
658
|
function resolveActiveClineBuiltinNames(toolFiltering) {
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
if (toolFiltering.mode === "allow") {
|
|
835
|
-
return CLINE_NATIVE_BUILTIN_NAMES.filter(
|
|
836
|
-
(name) => filteredNames.includes(name)
|
|
837
|
-
);
|
|
838
|
-
}
|
|
839
|
-
return CLINE_NATIVE_BUILTIN_NAMES.filter(
|
|
840
|
-
(name) => !filteredNames.includes(name)
|
|
841
|
-
);
|
|
659
|
+
if (toolFiltering == null) return CLINE_NATIVE_BUILTIN_NAMES;
|
|
660
|
+
const filteredNames = toolFiltering.toolNames.map((name) => name === "askUserQuestions" ? "ask_question" : name);
|
|
661
|
+
if (toolFiltering.mode === "allow") return CLINE_NATIVE_BUILTIN_NAMES.filter((name) => filteredNames.includes(name));
|
|
662
|
+
return CLINE_NATIVE_BUILTIN_NAMES.filter((name) => !filteredNames.includes(name));
|
|
842
663
|
}
|
|
843
664
|
function clineQuestionKey(input) {
|
|
844
|
-
|
|
665
|
+
return JSON.stringify([input.question, input.options]);
|
|
845
666
|
}
|
|
846
|
-
|
|
847
|
-
function createClineToolResult({
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
output,
|
|
854
|
-
...isError ? { isError: true } : {}
|
|
855
|
-
};
|
|
667
|
+
const clineToolResultMarker = Symbol("cline-tool-result");
|
|
668
|
+
function createClineToolResult({ output, isError }) {
|
|
669
|
+
return {
|
|
670
|
+
[clineToolResultMarker]: true,
|
|
671
|
+
output,
|
|
672
|
+
...isError ? { isError: true } : {}
|
|
673
|
+
};
|
|
856
674
|
}
|
|
857
675
|
function unwrapClineToolResult(value) {
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
...result.isError ? { isError: true } : {}
|
|
865
|
-
};
|
|
676
|
+
if (value == null || typeof value !== "object" || value[clineToolResultMarker] !== true) return;
|
|
677
|
+
const result = value;
|
|
678
|
+
return {
|
|
679
|
+
output: result.output,
|
|
680
|
+
...result.isError ? { isError: true } : {}
|
|
681
|
+
};
|
|
866
682
|
}
|
|
867
683
|
async function guarded(run) {
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
684
|
+
try {
|
|
685
|
+
return createClineToolResult({ output: await run() });
|
|
686
|
+
} catch (error) {
|
|
687
|
+
return createClineToolResult({
|
|
688
|
+
output: { error: getErrorText(error) },
|
|
689
|
+
isError: true
|
|
690
|
+
});
|
|
691
|
+
}
|
|
876
692
|
}
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
}) {
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
693
|
+
/**
|
|
694
|
+
* Build the sandbox-backed coding tools handed to the Cline runtime. These
|
|
695
|
+
* schemas mirror their declarations in `CLINE_BUILTIN_TOOLS`
|
|
696
|
+
* (cline-harness.ts). The in-host skills tool is built separately.
|
|
697
|
+
*/
|
|
698
|
+
function buildBuiltinAgentTools({ ops, activeNames, pendingQuestions = /* @__PURE__ */ new Map(), pendingQuestionResults = /* @__PURE__ */ new Map() }) {
|
|
699
|
+
const tools = {
|
|
700
|
+
ask_question: createDefaultTools({
|
|
701
|
+
executors: { askQuestion: async (question, options, context) => {
|
|
702
|
+
if (context.toolCallId == null) throw new Error("Cline ask_question did not provide a tool call id.");
|
|
703
|
+
const questionKey = clineQuestionKey({
|
|
704
|
+
question,
|
|
705
|
+
options
|
|
706
|
+
});
|
|
707
|
+
const pendingResult = pendingQuestionResults.get(questionKey);
|
|
708
|
+
if (pendingResult != null) {
|
|
709
|
+
pendingQuestionResults.delete(questionKey);
|
|
710
|
+
return toClineQuestionResult({
|
|
711
|
+
nativeInput: {
|
|
712
|
+
question,
|
|
713
|
+
options
|
|
714
|
+
},
|
|
715
|
+
output: pendingResult.output
|
|
716
|
+
});
|
|
717
|
+
}
|
|
718
|
+
return new Promise((resolve) => {
|
|
719
|
+
pendingQuestions.set(context.toolCallId, {
|
|
720
|
+
input: {
|
|
721
|
+
question,
|
|
722
|
+
options
|
|
723
|
+
},
|
|
724
|
+
resolve
|
|
725
|
+
});
|
|
726
|
+
});
|
|
727
|
+
} },
|
|
728
|
+
enableReadFiles: false,
|
|
729
|
+
enableSearch: false,
|
|
730
|
+
enableBash: false,
|
|
731
|
+
enableWebFetch: false,
|
|
732
|
+
enableApplyPatch: false,
|
|
733
|
+
enableEditor: false,
|
|
734
|
+
enableSkills: false,
|
|
735
|
+
enableAskQuestion: true
|
|
736
|
+
}).find((tool) => tool.name === "ask_question"),
|
|
737
|
+
read: createTool({
|
|
738
|
+
name: "read",
|
|
739
|
+
description: "Read file contents.",
|
|
740
|
+
inputSchema: {
|
|
741
|
+
type: "object",
|
|
742
|
+
properties: { file_path: {
|
|
743
|
+
type: "string",
|
|
744
|
+
description: "Path of the file to read."
|
|
745
|
+
} },
|
|
746
|
+
required: ["file_path"]
|
|
747
|
+
},
|
|
748
|
+
execute: async (input) => guarded(() => ops.readFile(input.file_path))
|
|
749
|
+
}),
|
|
750
|
+
write: createTool({
|
|
751
|
+
name: "write",
|
|
752
|
+
description: "Overwrite or create a file.",
|
|
753
|
+
inputSchema: {
|
|
754
|
+
type: "object",
|
|
755
|
+
properties: {
|
|
756
|
+
file_path: {
|
|
757
|
+
type: "string",
|
|
758
|
+
description: "Path of the file to write."
|
|
759
|
+
},
|
|
760
|
+
content: {
|
|
761
|
+
type: "string",
|
|
762
|
+
description: "Full file content to write."
|
|
763
|
+
}
|
|
764
|
+
},
|
|
765
|
+
required: ["file_path", "content"]
|
|
766
|
+
},
|
|
767
|
+
execute: async (input) => guarded(async () => {
|
|
768
|
+
await ops.writeFile(input.file_path, input.content);
|
|
769
|
+
return `Wrote ${input.file_path}`;
|
|
770
|
+
})
|
|
771
|
+
}),
|
|
772
|
+
edit: createTool({
|
|
773
|
+
name: "edit",
|
|
774
|
+
description: "Edit a file by exact string replacement.",
|
|
775
|
+
inputSchema: {
|
|
776
|
+
type: "object",
|
|
777
|
+
properties: {
|
|
778
|
+
file_path: {
|
|
779
|
+
type: "string",
|
|
780
|
+
description: "Path of the file to edit."
|
|
781
|
+
},
|
|
782
|
+
old_string: {
|
|
783
|
+
type: "string",
|
|
784
|
+
description: "Exact text to replace."
|
|
785
|
+
},
|
|
786
|
+
new_string: {
|
|
787
|
+
type: "string",
|
|
788
|
+
description: "Replacement text."
|
|
789
|
+
}
|
|
790
|
+
},
|
|
791
|
+
required: [
|
|
792
|
+
"file_path",
|
|
793
|
+
"old_string",
|
|
794
|
+
"new_string"
|
|
795
|
+
]
|
|
796
|
+
},
|
|
797
|
+
execute: async (input) => guarded(async () => {
|
|
798
|
+
await ops.editFile(input.file_path, input.old_string, input.new_string);
|
|
799
|
+
return `Edited ${input.file_path}`;
|
|
800
|
+
})
|
|
801
|
+
}),
|
|
802
|
+
bash: createTool({
|
|
803
|
+
name: "bash",
|
|
804
|
+
description: "Execute a shell command in the sandbox workspace. Returns combined stdout/stderr and the exit code.",
|
|
805
|
+
inputSchema: {
|
|
806
|
+
type: "object",
|
|
807
|
+
properties: {
|
|
808
|
+
command: {
|
|
809
|
+
type: "string",
|
|
810
|
+
description: "Shell command to execute."
|
|
811
|
+
},
|
|
812
|
+
timeout: {
|
|
813
|
+
type: "number",
|
|
814
|
+
description: "Optional timeout in seconds."
|
|
815
|
+
}
|
|
816
|
+
},
|
|
817
|
+
required: ["command"]
|
|
818
|
+
},
|
|
819
|
+
execute: async (input, context) => guarded(async () => {
|
|
820
|
+
const result = await ops.bash(input.command, {
|
|
821
|
+
...typeof input.timeout === "number" ? { timeout: input.timeout } : {},
|
|
822
|
+
...context.signal ? { signal: context.signal } : {}
|
|
823
|
+
});
|
|
824
|
+
return `${result.output}${result.exitCode != null ? `\n\n(exit ${result.exitCode})` : ""}`.trim();
|
|
825
|
+
})
|
|
826
|
+
}),
|
|
827
|
+
grep: createTool({
|
|
828
|
+
name: "grep",
|
|
829
|
+
description: "Search file contents with regex.",
|
|
830
|
+
inputSchema: {
|
|
831
|
+
type: "object",
|
|
832
|
+
properties: {
|
|
833
|
+
pattern: {
|
|
834
|
+
type: "string",
|
|
835
|
+
description: "Regex pattern to search for."
|
|
836
|
+
},
|
|
837
|
+
path: {
|
|
838
|
+
type: "string",
|
|
839
|
+
description: "File or directory to search."
|
|
840
|
+
},
|
|
841
|
+
glob: {
|
|
842
|
+
type: "string",
|
|
843
|
+
description: "Glob filter for file names."
|
|
844
|
+
},
|
|
845
|
+
ignoreCase: { type: "boolean" },
|
|
846
|
+
literal: {
|
|
847
|
+
type: "boolean",
|
|
848
|
+
description: "Treat pattern as a literal string."
|
|
849
|
+
},
|
|
850
|
+
context: {
|
|
851
|
+
type: "number",
|
|
852
|
+
description: "Lines of context around matches."
|
|
853
|
+
},
|
|
854
|
+
limit: {
|
|
855
|
+
type: "number",
|
|
856
|
+
description: "Maximum matching lines to return."
|
|
857
|
+
}
|
|
858
|
+
},
|
|
859
|
+
required: ["pattern"]
|
|
860
|
+
},
|
|
861
|
+
execute: async (input) => guarded(() => ops.grep(input.pattern, input))
|
|
862
|
+
}),
|
|
863
|
+
glob: createTool({
|
|
864
|
+
name: "glob",
|
|
865
|
+
description: "Find files matching a glob pattern.",
|
|
866
|
+
inputSchema: {
|
|
867
|
+
type: "object",
|
|
868
|
+
properties: {
|
|
869
|
+
pattern: {
|
|
870
|
+
type: "string",
|
|
871
|
+
description: "Glob pattern to match."
|
|
872
|
+
},
|
|
873
|
+
path: {
|
|
874
|
+
type: "string",
|
|
875
|
+
description: "Directory to search from."
|
|
876
|
+
},
|
|
877
|
+
limit: {
|
|
878
|
+
type: "number",
|
|
879
|
+
description: "Maximum paths to return."
|
|
880
|
+
}
|
|
881
|
+
},
|
|
882
|
+
required: ["pattern"]
|
|
883
|
+
},
|
|
884
|
+
execute: async (input) => guarded(async () => {
|
|
885
|
+
const matches = await ops.glob(input.pattern, input.path ?? ".", input.limit ?? 1e3);
|
|
886
|
+
return matches.length > 0 ? matches.join("\n") : "No files found";
|
|
887
|
+
})
|
|
888
|
+
}),
|
|
889
|
+
ls: createTool({
|
|
890
|
+
name: "ls",
|
|
891
|
+
description: "List directory entries.",
|
|
892
|
+
inputSchema: {
|
|
893
|
+
type: "object",
|
|
894
|
+
properties: {
|
|
895
|
+
path: {
|
|
896
|
+
type: "string",
|
|
897
|
+
description: "Directory to list."
|
|
898
|
+
},
|
|
899
|
+
limit: {
|
|
900
|
+
type: "number",
|
|
901
|
+
description: "Maximum entries to return."
|
|
902
|
+
}
|
|
903
|
+
}
|
|
904
|
+
},
|
|
905
|
+
execute: async (input) => guarded(async () => {
|
|
906
|
+
return (await ops.ls(input.path ?? ".", input.limit ?? 500)).join("\n");
|
|
907
|
+
})
|
|
908
|
+
})
|
|
909
|
+
};
|
|
910
|
+
return activeNames.map((name) => tools[name]).filter((tool) => tool != null);
|
|
1071
911
|
}
|
|
1072
912
|
function toClineQuestionResult(input) {
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
return input.nativeInput.options[index];
|
|
1082
|
-
}).filter((value) => value != null).join(", ");
|
|
1083
|
-
return result;
|
|
913
|
+
if (input.output.action === "declined" || input.output.action === "cancelled") return `The user ${input.output.action} the question.`;
|
|
914
|
+
const answer = input.output.answers["question-1"];
|
|
915
|
+
if (answer == null) return "The user did not answer the question.";
|
|
916
|
+
if (answer.freeform != null) return answer.freeform;
|
|
917
|
+
return answer.optionIds.map((id) => {
|
|
918
|
+
const index = Number(id.replace(/^option-/, "")) - 1;
|
|
919
|
+
return input.nativeInput.options[index];
|
|
920
|
+
}).filter((value) => value != null).join(", ");
|
|
1084
921
|
}
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
isError: true
|
|
1118
|
-
})
|
|
1119
|
-
);
|
|
1120
|
-
}
|
|
1121
|
-
},
|
|
1122
|
-
{ once: true }
|
|
1123
|
-
);
|
|
1124
|
-
});
|
|
1125
|
-
}
|
|
1126
|
-
})
|
|
1127
|
-
);
|
|
922
|
+
/**
|
|
923
|
+
* Build host-executed user tools. When the model calls one, the tool parks a
|
|
924
|
+
* promise keyed by the runtime's `toolCallId`; the harness caller supplies the
|
|
925
|
+
* output via `submitToolResult`, which resolves the promise and lets the loop
|
|
926
|
+
* continue. The adapter never executes these itself.
|
|
927
|
+
*/
|
|
928
|
+
function buildUserAgentTools({ specs, pendingToolResults }) {
|
|
929
|
+
return specs.map((spec) => createTool({
|
|
930
|
+
name: spec.name,
|
|
931
|
+
description: spec.description ?? `User-registered tool ${spec.name}`,
|
|
932
|
+
inputSchema: spec.inputSchema ?? {
|
|
933
|
+
type: "object",
|
|
934
|
+
properties: {},
|
|
935
|
+
additionalProperties: true
|
|
936
|
+
},
|
|
937
|
+
execute: async (_input, context) => {
|
|
938
|
+
const toolCallId = context.toolCallId;
|
|
939
|
+
if (!toolCallId) return createClineToolResult({
|
|
940
|
+
output: { error: "Tool call is missing a toolCallId." },
|
|
941
|
+
isError: true
|
|
942
|
+
});
|
|
943
|
+
return new Promise((resolve) => {
|
|
944
|
+
pendingToolResults.set(toolCallId, { resolve });
|
|
945
|
+
context.signal?.addEventListener("abort", () => {
|
|
946
|
+
if (pendingToolResults.delete(toolCallId)) resolve(createClineToolResult({
|
|
947
|
+
output: { error: "Turn was aborted before a tool result was submitted." },
|
|
948
|
+
isError: true
|
|
949
|
+
}));
|
|
950
|
+
}, { once: true });
|
|
951
|
+
});
|
|
952
|
+
}
|
|
953
|
+
}));
|
|
1128
954
|
}
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
function createClineTranslatorState({
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
ignoredToolNames: new Set(ignoredToolNames),
|
|
1142
|
-
mcpToolNames: new Set(mcpToolNames),
|
|
1143
|
-
emittedToolCalls: /* @__PURE__ */ new Set(),
|
|
1144
|
-
blockCounter: 0
|
|
1145
|
-
};
|
|
955
|
+
//#endregion
|
|
956
|
+
//#region src/cline-translate.ts
|
|
957
|
+
function createClineTranslatorState({ builtinToolNames, hostToolNames = [], ignoredToolNames = [], mcpToolNames = [] }) {
|
|
958
|
+
return {
|
|
959
|
+
builtinToolNames: new Set(builtinToolNames),
|
|
960
|
+
dynamicToolCallIds: /* @__PURE__ */ new Set(),
|
|
961
|
+
hostToolNames: new Set(hostToolNames),
|
|
962
|
+
ignoredToolNames: new Set(ignoredToolNames),
|
|
963
|
+
mcpToolNames: new Set(mcpToolNames),
|
|
964
|
+
emittedToolCalls: /* @__PURE__ */ new Set(),
|
|
965
|
+
blockCounter: 0
|
|
966
|
+
};
|
|
1146
967
|
}
|
|
1147
968
|
function closeOpenBlocks(state) {
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
969
|
+
const parts = [];
|
|
970
|
+
if (state.openReasoningBlockId) {
|
|
971
|
+
parts.push({
|
|
972
|
+
type: "reasoning-end",
|
|
973
|
+
id: state.openReasoningBlockId
|
|
974
|
+
});
|
|
975
|
+
state.openReasoningBlockId = void 0;
|
|
976
|
+
}
|
|
977
|
+
if (state.openTextBlockId) {
|
|
978
|
+
parts.push({
|
|
979
|
+
type: "text-end",
|
|
980
|
+
id: state.openTextBlockId
|
|
981
|
+
});
|
|
982
|
+
state.openTextBlockId = void 0;
|
|
983
|
+
}
|
|
984
|
+
return parts;
|
|
1158
985
|
}
|
|
1159
986
|
function toolCallPart(state, toolCall) {
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
return {
|
|
1194
|
-
type: "tool-call",
|
|
1195
|
-
toolCallId: toolCall.toolCallId,
|
|
1196
|
-
toolName: toolCall.toolName,
|
|
1197
|
-
input: JSON.stringify(toolCall.input ?? {}),
|
|
1198
|
-
/*
|
|
1199
|
-
* Built-ins and external MCP tools are executed by the Cline runtime.
|
|
1200
|
-
* Typed host tools wait for dispatch through `submitToolResult`, including
|
|
1201
|
-
* when a host tool has the same name as a configured MCP tool.
|
|
1202
|
-
*/
|
|
1203
|
-
...state.builtinToolNames.has(toolCall.toolName) || isMcpTool ? { providerExecuted: true } : {},
|
|
1204
|
-
...isMcpTool ? { dynamic: true } : {}
|
|
1205
|
-
};
|
|
987
|
+
const isMcpTool = state.mcpToolNames.has(toolCall.toolName) && !state.hostToolNames.has(toolCall.toolName);
|
|
988
|
+
if (isMcpTool) state.dynamicToolCallIds.add(toolCall.toolCallId);
|
|
989
|
+
if (toolCall.toolName === "ask_question") {
|
|
990
|
+
const nativeInput = toolCall.input;
|
|
991
|
+
return {
|
|
992
|
+
type: "tool-call",
|
|
993
|
+
toolCallId: toolCall.toolCallId,
|
|
994
|
+
toolName: "askUserQuestions",
|
|
995
|
+
input: JSON.stringify({
|
|
996
|
+
allowPartialAnswers: false,
|
|
997
|
+
questions: [{
|
|
998
|
+
id: "question-1",
|
|
999
|
+
question: nativeInput.question,
|
|
1000
|
+
options: nativeInput.options.map((label, index) => ({
|
|
1001
|
+
id: `option-${index + 1}`,
|
|
1002
|
+
label
|
|
1003
|
+
})),
|
|
1004
|
+
allowFreeForm: true
|
|
1005
|
+
}]
|
|
1006
|
+
}),
|
|
1007
|
+
providerExecuted: false,
|
|
1008
|
+
providerMetadata: { cline: { nativeRequest: nativeInput } },
|
|
1009
|
+
nativeName: "ask_question"
|
|
1010
|
+
};
|
|
1011
|
+
}
|
|
1012
|
+
return {
|
|
1013
|
+
type: "tool-call",
|
|
1014
|
+
toolCallId: toolCall.toolCallId,
|
|
1015
|
+
toolName: toolCall.toolName,
|
|
1016
|
+
input: JSON.stringify(toolCall.input ?? {}),
|
|
1017
|
+
...state.builtinToolNames.has(toolCall.toolName) || isMcpTool ? { providerExecuted: true } : {},
|
|
1018
|
+
...isMcpTool ? { dynamic: true } : {}
|
|
1019
|
+
};
|
|
1206
1020
|
}
|
|
1021
|
+
/**
|
|
1022
|
+
* Emit the `tool-call` + `tool-approval-request` pair for a built-in tool
|
|
1023
|
+
* that requires approval. The Cline runtime asks for approval *before* its
|
|
1024
|
+
* `tool-started` event fires, so the `tool-call` part is emitted here and
|
|
1025
|
+
* deduplicated when `tool-started` arrives.
|
|
1026
|
+
*/
|
|
1207
1027
|
function toolApprovalParts(state, toolCall) {
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1028
|
+
const parts = [];
|
|
1029
|
+
if (!state.emittedToolCalls.has(toolCall.toolCallId)) {
|
|
1030
|
+
state.emittedToolCalls.add(toolCall.toolCallId);
|
|
1031
|
+
parts.push(toolCallPart(state, toolCall));
|
|
1032
|
+
}
|
|
1033
|
+
parts.push({
|
|
1034
|
+
type: "tool-approval-request",
|
|
1035
|
+
approvalId: toolCall.toolCallId,
|
|
1036
|
+
toolCallId: toolCall.toolCallId
|
|
1037
|
+
});
|
|
1038
|
+
return parts;
|
|
1219
1039
|
}
|
|
1040
|
+
/**
|
|
1041
|
+
* Translate one Cline runtime event into zero or more harness stream parts.
|
|
1042
|
+
* Terminal events (`run-finished`, `run-failed`) are intentionally not
|
|
1043
|
+
* translated here — the session driver emits `finish`/`error` after the
|
|
1044
|
+
* runtime's promise settles, so a suspend can close the stream cleanly.
|
|
1045
|
+
*/
|
|
1220
1046
|
function translateClineEvent(event, state) {
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
}
|
|
1322
|
-
}
|
|
1323
|
-
];
|
|
1324
|
-
default:
|
|
1325
|
-
return [];
|
|
1326
|
-
}
|
|
1047
|
+
switch (event.type) {
|
|
1048
|
+
case "assistant-reasoning-delta": {
|
|
1049
|
+
const parts = [];
|
|
1050
|
+
if (state.openTextBlockId) {
|
|
1051
|
+
parts.push({
|
|
1052
|
+
type: "text-end",
|
|
1053
|
+
id: state.openTextBlockId
|
|
1054
|
+
});
|
|
1055
|
+
state.openTextBlockId = void 0;
|
|
1056
|
+
}
|
|
1057
|
+
if (!state.openReasoningBlockId) {
|
|
1058
|
+
state.openReasoningBlockId = `reasoning-${++state.blockCounter}`;
|
|
1059
|
+
parts.push({
|
|
1060
|
+
type: "reasoning-start",
|
|
1061
|
+
id: state.openReasoningBlockId
|
|
1062
|
+
});
|
|
1063
|
+
}
|
|
1064
|
+
parts.push({
|
|
1065
|
+
type: "reasoning-delta",
|
|
1066
|
+
id: state.openReasoningBlockId,
|
|
1067
|
+
delta: event.text
|
|
1068
|
+
});
|
|
1069
|
+
return parts;
|
|
1070
|
+
}
|
|
1071
|
+
case "assistant-text-delta": {
|
|
1072
|
+
const parts = [];
|
|
1073
|
+
if (state.openReasoningBlockId) {
|
|
1074
|
+
parts.push({
|
|
1075
|
+
type: "reasoning-end",
|
|
1076
|
+
id: state.openReasoningBlockId
|
|
1077
|
+
});
|
|
1078
|
+
state.openReasoningBlockId = void 0;
|
|
1079
|
+
}
|
|
1080
|
+
if (!state.openTextBlockId) {
|
|
1081
|
+
state.openTextBlockId = `text-${++state.blockCounter}`;
|
|
1082
|
+
parts.push({
|
|
1083
|
+
type: "text-start",
|
|
1084
|
+
id: state.openTextBlockId
|
|
1085
|
+
});
|
|
1086
|
+
}
|
|
1087
|
+
parts.push({
|
|
1088
|
+
type: "text-delta",
|
|
1089
|
+
id: state.openTextBlockId,
|
|
1090
|
+
delta: event.text
|
|
1091
|
+
});
|
|
1092
|
+
return parts;
|
|
1093
|
+
}
|
|
1094
|
+
case "assistant-message": {
|
|
1095
|
+
const parts = closeOpenBlocks(state);
|
|
1096
|
+
state.pendingFinishStep = {
|
|
1097
|
+
type: "finish-step",
|
|
1098
|
+
finishReason: mapModelFinishReason(event.finishReason),
|
|
1099
|
+
usage: usageFromMessageMetrics(event.message.metrics)
|
|
1100
|
+
};
|
|
1101
|
+
return parts;
|
|
1102
|
+
}
|
|
1103
|
+
case "turn-finished": {
|
|
1104
|
+
const pendingFinishStep = state.pendingFinishStep;
|
|
1105
|
+
if (!pendingFinishStep) return [];
|
|
1106
|
+
state.pendingFinishStep = void 0;
|
|
1107
|
+
return [pendingFinishStep];
|
|
1108
|
+
}
|
|
1109
|
+
case "tool-started":
|
|
1110
|
+
if (state.ignoredToolNames.has(event.toolCall.toolName)) return [];
|
|
1111
|
+
if (state.emittedToolCalls.has(event.toolCall.toolCallId)) return [];
|
|
1112
|
+
state.emittedToolCalls.add(event.toolCall.toolCallId);
|
|
1113
|
+
return [toolCallPart(state, event.toolCall)];
|
|
1114
|
+
case "tool-finished": {
|
|
1115
|
+
if (state.ignoredToolNames.has(event.toolCall.toolName)) return [];
|
|
1116
|
+
const resultPart = event.message.content.find((part) => part.type === "tool-result" && part.toolCallId === event.toolCall.toolCallId);
|
|
1117
|
+
const output = resultPart?.output;
|
|
1118
|
+
const dynamic = state.dynamicToolCallIds.delete(event.toolCall.toolCallId);
|
|
1119
|
+
return [{
|
|
1120
|
+
type: "tool-result",
|
|
1121
|
+
toolCallId: event.toolCall.toolCallId,
|
|
1122
|
+
toolName: event.toolCall.toolName === "ask_question" ? "askUserQuestions" : event.toolCall.toolName,
|
|
1123
|
+
result: toToolResultValue(output),
|
|
1124
|
+
...resultPart?.isError ? { isError: true } : {},
|
|
1125
|
+
...dynamic ? { dynamic: true } : {}
|
|
1126
|
+
}];
|
|
1127
|
+
}
|
|
1128
|
+
case "tool-updated": return [{
|
|
1129
|
+
type: "raw",
|
|
1130
|
+
rawValue: {
|
|
1131
|
+
type: "tool-updated",
|
|
1132
|
+
toolCallId: event.toolCall.toolCallId,
|
|
1133
|
+
toolName: event.toolCall.toolName,
|
|
1134
|
+
update: event.update
|
|
1135
|
+
}
|
|
1136
|
+
}];
|
|
1137
|
+
case "status-notice": return [{
|
|
1138
|
+
type: "raw",
|
|
1139
|
+
rawValue: {
|
|
1140
|
+
type: "status-notice",
|
|
1141
|
+
message: event.message,
|
|
1142
|
+
...event.metadata ? { metadata: event.metadata } : {}
|
|
1143
|
+
}
|
|
1144
|
+
}];
|
|
1145
|
+
default: return [];
|
|
1146
|
+
}
|
|
1327
1147
|
}
|
|
1148
|
+
/**
|
|
1149
|
+
* Close any blocks still open when the turn ends (e.g. an aborted turn whose
|
|
1150
|
+
* `assistant-message` never arrived). A buffered step that never reached
|
|
1151
|
+
* Cline's `turn-finished` boundary is incomplete and must not be emitted.
|
|
1152
|
+
*/
|
|
1328
1153
|
function finishClineTranslation(state) {
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1154
|
+
const parts = closeOpenBlocks(state);
|
|
1155
|
+
state.pendingFinishStep = void 0;
|
|
1156
|
+
return parts;
|
|
1332
1157
|
}
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1158
|
+
//#endregion
|
|
1159
|
+
//#region src/cline-session.ts
|
|
1160
|
+
const HARNESS_ID = "cline";
|
|
1161
|
+
const parkedClineSessions = /* @__PURE__ */ new Map();
|
|
1162
|
+
/**
|
|
1163
|
+
* Whether a thrown error is an abort — the expected result of
|
|
1164
|
+
* `doSuspendTurn` aborting the in-flight turn. Only these are safe to swallow
|
|
1165
|
+
* while `suspending`; any other error must surface as an `error` chunk.
|
|
1166
|
+
*/
|
|
1337
1167
|
function isAbortError(value) {
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
const text = typeof value === "string" ? value : value instanceof Error ? value.message : String(value);
|
|
1344
|
-
return /\baborted\b|AbortError/i.test(text);
|
|
1168
|
+
if (value == null) return false;
|
|
1169
|
+
const name = value.name;
|
|
1170
|
+
if (name === "AbortError" || name === "AgentRuntimeAbortError") return true;
|
|
1171
|
+
const text = typeof value === "string" ? value : value instanceof Error ? value.message : String(value);
|
|
1172
|
+
return /\baborted\b|AbortError/i.test(text);
|
|
1345
1173
|
}
|
|
1346
1174
|
function clineBuiltinToolRequiresApproval(input) {
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1175
|
+
if (input.permissionMode === "allow-all") return false;
|
|
1176
|
+
if (input.permissionMode === "allow-edits") return input.kind === "bash";
|
|
1177
|
+
return input.kind === "edit" || input.kind === "bash";
|
|
1350
1178
|
}
|
|
1351
1179
|
function buildSystemPrompt(input) {
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
`## Sandbox
|
|
1358
|
-
|
|
1359
|
-
${input.sandboxDescription}`
|
|
1360
|
-
].join("\n\n");
|
|
1180
|
+
return [
|
|
1181
|
+
"You are Cline, an autonomous coding agent operating inside a sandboxed workspace.",
|
|
1182
|
+
`## Workspace\n\nThe workspace root is \`${input.sessionWorkDir}\`. All relative file paths and shell commands resolve against it. Use the provided tools (read, write, edit, bash, grep, glob, ls) to inspect and modify the workspace.`,
|
|
1183
|
+
`## Sandbox\n\n${input.sandboxDescription}`
|
|
1184
|
+
].join("\n\n");
|
|
1361
1185
|
}
|
|
1362
1186
|
function toClineBackendProviderBaseUrl(baseUrl) {
|
|
1363
|
-
|
|
1187
|
+
return `${baseUrl.replace(/\/+$/, "")}/api/v1`;
|
|
1364
1188
|
}
|
|
1365
1189
|
function toAiGatewayProviderBaseUrl(baseUrl) {
|
|
1366
|
-
|
|
1367
|
-
|
|
1190
|
+
const trimmed = baseUrl.replace(/\/+$/, "");
|
|
1191
|
+
return trimmed.endsWith("/v1") ? trimmed : `${trimmed}/v1`;
|
|
1368
1192
|
}
|
|
1369
|
-
function createClineAgentModel({
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
const modelOptions = settings.reasoningEffort === "none" ? { reasoning: { enabled: false } } : {
|
|
1404
|
-
reasoning: {
|
|
1405
|
-
enabled: true,
|
|
1406
|
-
effort: settings.reasoningEffort
|
|
1407
|
-
}
|
|
1408
|
-
};
|
|
1409
|
-
const model = settings.reasoningEffort === void 0 ? gateway.createAgentModel(modelSelection) : gateway.createAgentModel(modelSelection, modelOptions);
|
|
1410
|
-
return {
|
|
1411
|
-
model,
|
|
1412
|
-
providerId
|
|
1413
|
-
};
|
|
1193
|
+
function createClineAgentModel({ settings, clientApp, modelId }) {
|
|
1194
|
+
const gatewayBaseUrl = settings.authEnv.AI_GATEWAY_BASE_URL;
|
|
1195
|
+
const isAiGateway = gatewayBaseUrl != null;
|
|
1196
|
+
const providerId = isAiGateway ? "cline" : settings.providerId ?? "cline";
|
|
1197
|
+
const apiKey = isAiGateway ? settings.authEnv.AI_GATEWAY_API_KEY : settings.apiKey ?? (providerId === "cline" ? settings.authEnv.CLINE_API_KEY : void 0);
|
|
1198
|
+
const baseUrl = isAiGateway ? toAiGatewayProviderBaseUrl(gatewayBaseUrl) : settings.baseUrl ?? (providerId === "cline" && settings.authEnv.CLINE_API_BASE_URL ? toClineBackendProviderBaseUrl(settings.authEnv.CLINE_API_BASE_URL) : void 0);
|
|
1199
|
+
const headers = isAiGateway ? {
|
|
1200
|
+
...settings.headers,
|
|
1201
|
+
...settings.agentHeaders,
|
|
1202
|
+
"User-Agent": clientApp,
|
|
1203
|
+
"x-client-app": clientApp
|
|
1204
|
+
} : settings.headers != null || settings.agentHeaders != null ? {
|
|
1205
|
+
...settings.headers,
|
|
1206
|
+
...settings.agentHeaders
|
|
1207
|
+
} : void 0;
|
|
1208
|
+
const gateway = Llms.createGateway({ providerConfigs: [{
|
|
1209
|
+
providerId,
|
|
1210
|
+
...apiKey ? { apiKey } : {},
|
|
1211
|
+
...baseUrl ? { baseUrl } : {},
|
|
1212
|
+
...headers ? { headers } : {},
|
|
1213
|
+
...isAiGateway || settings.isAuthenticationEnvironmentOverride ? { apiKeyEnv: [] } : {}
|
|
1214
|
+
}] });
|
|
1215
|
+
const modelSelection = {
|
|
1216
|
+
providerId,
|
|
1217
|
+
...modelId ? { modelId } : {}
|
|
1218
|
+
};
|
|
1219
|
+
const modelOptions = settings.reasoningEffort === "none" ? { reasoning: { enabled: false } } : { reasoning: {
|
|
1220
|
+
enabled: true,
|
|
1221
|
+
effort: settings.reasoningEffort
|
|
1222
|
+
} };
|
|
1223
|
+
return {
|
|
1224
|
+
model: settings.reasoningEffort === void 0 ? gateway.createAgentModel(modelSelection) : gateway.createAgentModel(modelSelection, modelOptions),
|
|
1225
|
+
providerId
|
|
1226
|
+
};
|
|
1414
1227
|
}
|
|
1415
1228
|
async function createClineSession(input) {
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
1596
|
-
|
|
1597
|
-
|
|
1598
|
-
|
|
1599
|
-
|
|
1600
|
-
|
|
1601
|
-
|
|
1602
|
-
|
|
1603
|
-
|
|
1604
|
-
|
|
1605
|
-
|
|
1606
|
-
|
|
1607
|
-
|
|
1608
|
-
|
|
1609
|
-
|
|
1610
|
-
|
|
1611
|
-
|
|
1612
|
-
|
|
1613
|
-
|
|
1614
|
-
|
|
1615
|
-
|
|
1616
|
-
|
|
1617
|
-
|
|
1618
|
-
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1625
|
-
|
|
1626
|
-
|
|
1627
|
-
|
|
1628
|
-
|
|
1629
|
-
|
|
1630
|
-
|
|
1631
|
-
|
|
1632
|
-
|
|
1633
|
-
|
|
1634
|
-
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1641
|
-
|
|
1642
|
-
|
|
1643
|
-
|
|
1644
|
-
|
|
1645
|
-
|
|
1646
|
-
|
|
1647
|
-
|
|
1648
|
-
|
|
1649
|
-
|
|
1650
|
-
|
|
1651
|
-
|
|
1652
|
-
|
|
1653
|
-
|
|
1654
|
-
|
|
1655
|
-
|
|
1656
|
-
|
|
1657
|
-
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1662
|
-
|
|
1663
|
-
|
|
1664
|
-
|
|
1665
|
-
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
|
|
1672
|
-
|
|
1673
|
-
|
|
1674
|
-
|
|
1675
|
-
|
|
1676
|
-
|
|
1677
|
-
|
|
1678
|
-
|
|
1679
|
-
|
|
1680
|
-
|
|
1681
|
-
|
|
1682
|
-
|
|
1683
|
-
|
|
1684
|
-
|
|
1685
|
-
|
|
1686
|
-
|
|
1687
|
-
|
|
1688
|
-
|
|
1689
|
-
|
|
1690
|
-
|
|
1691
|
-
|
|
1692
|
-
|
|
1693
|
-
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1700
|
-
|
|
1701
|
-
|
|
1702
|
-
|
|
1703
|
-
|
|
1704
|
-
|
|
1705
|
-
|
|
1706
|
-
|
|
1707
|
-
|
|
1708
|
-
|
|
1709
|
-
|
|
1710
|
-
|
|
1711
|
-
|
|
1712
|
-
|
|
1713
|
-
|
|
1714
|
-
|
|
1715
|
-
|
|
1716
|
-
|
|
1717
|
-
|
|
1718
|
-
|
|
1719
|
-
|
|
1720
|
-
|
|
1721
|
-
|
|
1722
|
-
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
1726
|
-
|
|
1727
|
-
|
|
1728
|
-
|
|
1729
|
-
|
|
1730
|
-
|
|
1731
|
-
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
|
|
1735
|
-
|
|
1736
|
-
|
|
1737
|
-
|
|
1738
|
-
|
|
1739
|
-
|
|
1740
|
-
|
|
1741
|
-
|
|
1742
|
-
|
|
1743
|
-
|
|
1744
|
-
|
|
1745
|
-
|
|
1746
|
-
|
|
1747
|
-
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
|
|
1751
|
-
|
|
1752
|
-
|
|
1753
|
-
|
|
1754
|
-
|
|
1755
|
-
|
|
1756
|
-
|
|
1757
|
-
|
|
1758
|
-
|
|
1759
|
-
|
|
1760
|
-
|
|
1761
|
-
|
|
1762
|
-
|
|
1763
|
-
|
|
1764
|
-
|
|
1765
|
-
|
|
1766
|
-
|
|
1767
|
-
|
|
1768
|
-
|
|
1769
|
-
|
|
1770
|
-
|
|
1771
|
-
|
|
1772
|
-
|
|
1773
|
-
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
|
|
1779
|
-
|
|
1780
|
-
|
|
1781
|
-
|
|
1782
|
-
|
|
1783
|
-
|
|
1784
|
-
|
|
1785
|
-
|
|
1786
|
-
|
|
1787
|
-
|
|
1788
|
-
|
|
1789
|
-
|
|
1790
|
-
|
|
1791
|
-
|
|
1792
|
-
|
|
1793
|
-
|
|
1794
|
-
|
|
1795
|
-
|
|
1796
|
-
|
|
1797
|
-
|
|
1798
|
-
|
|
1799
|
-
|
|
1800
|
-
|
|
1801
|
-
|
|
1802
|
-
|
|
1803
|
-
|
|
1804
|
-
|
|
1805
|
-
|
|
1806
|
-
|
|
1807
|
-
|
|
1808
|
-
|
|
1809
|
-
|
|
1810
|
-
|
|
1811
|
-
|
|
1812
|
-
|
|
1813
|
-
|
|
1814
|
-
|
|
1815
|
-
|
|
1816
|
-
|
|
1817
|
-
|
|
1818
|
-
|
|
1819
|
-
|
|
1820
|
-
|
|
1821
|
-
|
|
1822
|
-
|
|
1823
|
-
|
|
1824
|
-
|
|
1825
|
-
|
|
1826
|
-
|
|
1827
|
-
|
|
1828
|
-
|
|
1829
|
-
|
|
1830
|
-
|
|
1831
|
-
|
|
1832
|
-
|
|
1833
|
-
|
|
1834
|
-
|
|
1835
|
-
|
|
1836
|
-
|
|
1837
|
-
|
|
1838
|
-
|
|
1839
|
-
|
|
1840
|
-
|
|
1841
|
-
|
|
1842
|
-
|
|
1843
|
-
|
|
1844
|
-
|
|
1845
|
-
|
|
1846
|
-
|
|
1847
|
-
|
|
1848
|
-
|
|
1849
|
-
|
|
1850
|
-
|
|
1851
|
-
|
|
1852
|
-
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
|
|
1856
|
-
|
|
1857
|
-
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
|
|
1861
|
-
|
|
1862
|
-
|
|
1863
|
-
|
|
1864
|
-
|
|
1865
|
-
|
|
1866
|
-
|
|
1867
|
-
|
|
1868
|
-
|
|
1869
|
-
|
|
1870
|
-
|
|
1871
|
-
|
|
1872
|
-
|
|
1873
|
-
|
|
1874
|
-
|
|
1875
|
-
|
|
1876
|
-
|
|
1877
|
-
|
|
1878
|
-
|
|
1879
|
-
|
|
1880
|
-
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
|
|
1884
|
-
|
|
1885
|
-
|
|
1886
|
-
|
|
1887
|
-
|
|
1888
|
-
|
|
1889
|
-
|
|
1890
|
-
|
|
1891
|
-
|
|
1892
|
-
|
|
1893
|
-
|
|
1894
|
-
|
|
1895
|
-
|
|
1896
|
-
|
|
1897
|
-
|
|
1898
|
-
|
|
1899
|
-
|
|
1900
|
-
|
|
1901
|
-
|
|
1902
|
-
|
|
1903
|
-
|
|
1904
|
-
|
|
1905
|
-
|
|
1906
|
-
|
|
1907
|
-
|
|
1908
|
-
|
|
1909
|
-
|
|
1910
|
-
|
|
1911
|
-
|
|
1912
|
-
|
|
1913
|
-
|
|
1914
|
-
|
|
1915
|
-
|
|
1916
|
-
|
|
1917
|
-
|
|
1918
|
-
|
|
1919
|
-
|
|
1920
|
-
|
|
1921
|
-
});
|
|
1922
|
-
},
|
|
1923
|
-
doCompact: async () => {
|
|
1924
|
-
throw new HarnessCapabilityUnsupportedError2({
|
|
1925
|
-
message: "cline: manual compaction is not supported by the standalone Cline runtime.",
|
|
1926
|
-
harnessId: HARNESS_ID2
|
|
1927
|
-
});
|
|
1928
|
-
},
|
|
1929
|
-
doDestroy: async () => {
|
|
1930
|
-
if (stopped) return;
|
|
1931
|
-
stopped = true;
|
|
1932
|
-
parkedClineSessions.delete(input.sessionId);
|
|
1933
|
-
settlePendingToolResults("Cline session stopped");
|
|
1934
|
-
settlePendingToolApprovals("Cline session stopped");
|
|
1935
|
-
agent?.abort("Cline session destroyed");
|
|
1936
|
-
await teardown();
|
|
1937
|
-
},
|
|
1938
|
-
doStop,
|
|
1939
|
-
doDetach: async () => {
|
|
1940
|
-
if (activeTurn != null || pendingToolResults.size > 0 || pendingQuestions.size > 0) {
|
|
1941
|
-
parkedClineSessions.set(input.sessionId, sessionImpl);
|
|
1942
|
-
try {
|
|
1943
|
-
await persistHistory();
|
|
1944
|
-
} catch {
|
|
1945
|
-
}
|
|
1946
|
-
return {
|
|
1947
|
-
type: "resume-session",
|
|
1948
|
-
harnessId: HARNESS_ID2,
|
|
1949
|
-
specificationVersion: "harness-v1",
|
|
1950
|
-
data: { historyFileName: CLINE_DEFAULT_HISTORY_FILE_NAME }
|
|
1951
|
-
};
|
|
1952
|
-
}
|
|
1953
|
-
return doStop();
|
|
1954
|
-
},
|
|
1955
|
-
doSuspendTurn: async () => {
|
|
1956
|
-
if (stopped) {
|
|
1957
|
-
throw new Error("Cline session has been stopped.");
|
|
1958
|
-
}
|
|
1959
|
-
if (activeTurn != null && (pendingToolResults.size > 0 || pendingToolApprovals.size > 0 || pendingQuestions.size > 0)) {
|
|
1960
|
-
parkedClineSessions.set(input.sessionId, sessionImpl);
|
|
1961
|
-
try {
|
|
1962
|
-
await persistHistory();
|
|
1963
|
-
} catch {
|
|
1964
|
-
}
|
|
1965
|
-
return {
|
|
1966
|
-
type: "continue-turn",
|
|
1967
|
-
harnessId: HARNESS_ID2,
|
|
1968
|
-
specificationVersion: "harness-v1",
|
|
1969
|
-
data: { historyFileName: CLINE_DEFAULT_HISTORY_FILE_NAME }
|
|
1970
|
-
};
|
|
1971
|
-
}
|
|
1972
|
-
suspending = true;
|
|
1973
|
-
agent?.abort("Cline session suspended");
|
|
1974
|
-
if (activeTurn) {
|
|
1975
|
-
await activeTurn.done.catch(() => {
|
|
1976
|
-
});
|
|
1977
|
-
}
|
|
1978
|
-
try {
|
|
1979
|
-
await persistHistory();
|
|
1980
|
-
} catch {
|
|
1981
|
-
}
|
|
1982
|
-
stopped = true;
|
|
1983
|
-
parkedClineSessions.delete(input.sessionId);
|
|
1984
|
-
settlePendingToolResults("Cline session suspended");
|
|
1985
|
-
settlePendingToolApprovals("Cline session suspended");
|
|
1986
|
-
await teardown();
|
|
1987
|
-
return {
|
|
1988
|
-
type: "continue-turn",
|
|
1989
|
-
harnessId: HARNESS_ID2,
|
|
1990
|
-
specificationVersion: "harness-v1",
|
|
1991
|
-
data: { historyFileName: CLINE_DEFAULT_HISTORY_FILE_NAME }
|
|
1992
|
-
};
|
|
1993
|
-
}
|
|
1994
|
-
};
|
|
1995
|
-
return sessionImpl;
|
|
1229
|
+
if (input.isResume) {
|
|
1230
|
+
const parkedSession = parkedClineSessions.get(input.sessionId);
|
|
1231
|
+
if (parkedSession) {
|
|
1232
|
+
parkedClineSessions.delete(input.sessionId);
|
|
1233
|
+
return {
|
|
1234
|
+
...parkedSession,
|
|
1235
|
+
isResume: true
|
|
1236
|
+
};
|
|
1237
|
+
}
|
|
1238
|
+
}
|
|
1239
|
+
const toolSafeSandboxSession = getRestrictedSandboxSession(input.sandboxSession);
|
|
1240
|
+
const privateSessionDir = resolveClinePrivateSessionDirectory({
|
|
1241
|
+
sandboxHomeDir: await resolveSandboxHomeDir({
|
|
1242
|
+
sandbox: toolSafeSandboxSession,
|
|
1243
|
+
...input.abortSignal ? { abortSignal: input.abortSignal } : {}
|
|
1244
|
+
}),
|
|
1245
|
+
sessionWorkDir: input.sessionWorkDir,
|
|
1246
|
+
sessionId: input.sessionId
|
|
1247
|
+
});
|
|
1248
|
+
const permissionMode = input.permissionMode ?? "allow-all";
|
|
1249
|
+
const activeBuiltinNames = resolveActiveClineBuiltinNames(input.builtinToolFiltering);
|
|
1250
|
+
const ops = createClineRemoteOps({
|
|
1251
|
+
sandbox: toolSafeSandboxSession,
|
|
1252
|
+
workDir: input.sessionWorkDir
|
|
1253
|
+
});
|
|
1254
|
+
let currentMessages = [];
|
|
1255
|
+
if (input.isResume && input.resumeHistoryFileName) {
|
|
1256
|
+
const restored = await pullHistoryFromSandbox({
|
|
1257
|
+
sandbox: toolSafeSandboxSession,
|
|
1258
|
+
privateSessionDir,
|
|
1259
|
+
historyFileName: safeClineHistoryFileName(input.resumeHistoryFileName),
|
|
1260
|
+
...input.abortSignal ? { abortSignal: input.abortSignal } : {}
|
|
1261
|
+
});
|
|
1262
|
+
if (restored) currentMessages = restored;
|
|
1263
|
+
}
|
|
1264
|
+
const mcpRuntime = await createClineMcpRuntime({ mcpServers: input.settings.mcpServers });
|
|
1265
|
+
let activeModelId;
|
|
1266
|
+
let agentModel = createClineAgentModel({
|
|
1267
|
+
settings: input.settings,
|
|
1268
|
+
clientApp: input.clientApp,
|
|
1269
|
+
modelId: activeModelId
|
|
1270
|
+
});
|
|
1271
|
+
let agent;
|
|
1272
|
+
let unsubscribe;
|
|
1273
|
+
let lastAgentConfigurationSignature;
|
|
1274
|
+
let agentHasRun = false;
|
|
1275
|
+
let stopped = false;
|
|
1276
|
+
let suspending = false;
|
|
1277
|
+
const pendingToolResults = /* @__PURE__ */ new Map();
|
|
1278
|
+
const pendingToolApprovals = /* @__PURE__ */ new Map();
|
|
1279
|
+
const pendingUserMessages = [];
|
|
1280
|
+
let acceptingUserMessages = false;
|
|
1281
|
+
let currentEmit;
|
|
1282
|
+
let translatorState;
|
|
1283
|
+
const pendingQuestions = /* @__PURE__ */ new Map();
|
|
1284
|
+
const pendingQuestionResults = /* @__PURE__ */ new Map();
|
|
1285
|
+
let activeTurn;
|
|
1286
|
+
function settlePendingToolResults(reason) {
|
|
1287
|
+
for (const pending of pendingToolResults.values()) pending.resolve(createClineToolResult({
|
|
1288
|
+
output: { error: reason },
|
|
1289
|
+
isError: true
|
|
1290
|
+
}));
|
|
1291
|
+
pendingToolResults.clear();
|
|
1292
|
+
}
|
|
1293
|
+
function settlePendingToolApprovals(reason) {
|
|
1294
|
+
for (const pending of pendingToolApprovals.values()) pending.resolve({
|
|
1295
|
+
approved: false,
|
|
1296
|
+
reason
|
|
1297
|
+
});
|
|
1298
|
+
pendingToolApprovals.clear();
|
|
1299
|
+
}
|
|
1300
|
+
function consumePendingUserMessage() {
|
|
1301
|
+
const pending = pendingUserMessages.shift();
|
|
1302
|
+
if (pending == null) return void 0;
|
|
1303
|
+
pending.resolve();
|
|
1304
|
+
return pending.text;
|
|
1305
|
+
}
|
|
1306
|
+
function rejectPendingUserMessages(error) {
|
|
1307
|
+
for (const pending of pendingUserMessages) pending.reject(error);
|
|
1308
|
+
pendingUserMessages.length = 0;
|
|
1309
|
+
}
|
|
1310
|
+
function settlePendingQuestions(reason) {
|
|
1311
|
+
for (const pending of pendingQuestions.values()) pending.resolve(reason);
|
|
1312
|
+
pendingQuestions.clear();
|
|
1313
|
+
pendingQuestionResults.clear();
|
|
1314
|
+
}
|
|
1315
|
+
async function persistHistory() {
|
|
1316
|
+
const messages = agent?.snapshot().messages ?? currentMessages;
|
|
1317
|
+
await persistHistoryToSandbox({
|
|
1318
|
+
sandbox: toolSafeSandboxSession,
|
|
1319
|
+
privateSessionDir,
|
|
1320
|
+
historyFileName: CLINE_DEFAULT_HISTORY_FILE_NAME,
|
|
1321
|
+
messages
|
|
1322
|
+
});
|
|
1323
|
+
}
|
|
1324
|
+
function requestBuiltinToolApproval(toolCall) {
|
|
1325
|
+
if (translatorState && currentEmit) for (const part of toolApprovalParts(translatorState, toolCall)) currentEmit(part);
|
|
1326
|
+
return new Promise((resolve) => {
|
|
1327
|
+
pendingToolApprovals.set(toolCall.toolCallId, { resolve });
|
|
1328
|
+
});
|
|
1329
|
+
}
|
|
1330
|
+
function rebuildAgent(rebuildInput) {
|
|
1331
|
+
unsubscribe?.();
|
|
1332
|
+
unsubscribe = void 0;
|
|
1333
|
+
agent = new Agent({
|
|
1334
|
+
model: agentModel.model,
|
|
1335
|
+
...activeModelId ? { messageModelInfo: {
|
|
1336
|
+
id: activeModelId,
|
|
1337
|
+
provider: agentModel.providerId
|
|
1338
|
+
} } : {},
|
|
1339
|
+
sessionId: input.sessionId,
|
|
1340
|
+
systemPrompt: (() => {
|
|
1341
|
+
const baseSystemPrompt = buildSystemPrompt({
|
|
1342
|
+
sessionWorkDir: input.sessionWorkDir,
|
|
1343
|
+
sandboxDescription: toolSafeSandboxSession.description
|
|
1344
|
+
});
|
|
1345
|
+
return rebuildInput.instructions ? `${baseSystemPrompt}\n\n${rebuildInput.instructions}` : baseSystemPrompt;
|
|
1346
|
+
})(),
|
|
1347
|
+
initialMessages: currentMessages,
|
|
1348
|
+
toolExecution: "parallel",
|
|
1349
|
+
...input.settings.maxIterations !== void 0 ? { maxIterations: input.settings.maxIterations } : {},
|
|
1350
|
+
tools: [
|
|
1351
|
+
...buildBuiltinAgentTools({
|
|
1352
|
+
ops,
|
|
1353
|
+
activeNames: activeBuiltinNames,
|
|
1354
|
+
pendingQuestions,
|
|
1355
|
+
pendingQuestionResults
|
|
1356
|
+
}),
|
|
1357
|
+
...rebuildInput.skillsRuntime.tool && activeBuiltinNames.includes("skills") ? [rebuildInput.skillsRuntime.tool] : [],
|
|
1358
|
+
...mcpRuntime.tools,
|
|
1359
|
+
...buildUserAgentTools({
|
|
1360
|
+
specs: rebuildInput.userTools,
|
|
1361
|
+
pendingToolResults
|
|
1362
|
+
}),
|
|
1363
|
+
...rebuildInput.responseFormat?.type === "json" && rebuildInput.responseFormat.schema != null ? [createTool({
|
|
1364
|
+
name: "structured_output",
|
|
1365
|
+
description: rebuildInput.responseFormat.description ?? "Return the final structured output.",
|
|
1366
|
+
inputSchema: {
|
|
1367
|
+
type: "object",
|
|
1368
|
+
properties: { output: rebuildInput.responseFormat.schema },
|
|
1369
|
+
required: ["output"],
|
|
1370
|
+
additionalProperties: false
|
|
1371
|
+
},
|
|
1372
|
+
lifecycle: { completesRun: true },
|
|
1373
|
+
execute: async ({ output }) => JSON.stringify(output)
|
|
1374
|
+
})] : []
|
|
1375
|
+
],
|
|
1376
|
+
...rebuildInput.responseFormat?.type === "json" ? { completionPolicy: { requireCompletionTool: true } } : {},
|
|
1377
|
+
hooks: { afterTool: ({ result }) => {
|
|
1378
|
+
const toolResult = unwrapClineToolResult(result.output);
|
|
1379
|
+
return toolResult == null ? void 0 : { result: toolResult };
|
|
1380
|
+
} },
|
|
1381
|
+
toolPolicies: Object.fromEntries(activeBuiltinNames.filter((name) => clineBuiltinToolRequiresApproval({
|
|
1382
|
+
permissionMode,
|
|
1383
|
+
kind: CLINE_NATIVE_TOOL_KINDS[name]
|
|
1384
|
+
})).map((name) => [name, { autoApprove: false }])),
|
|
1385
|
+
requestToolApproval: (request) => {
|
|
1386
|
+
if (!isClineBuiltinToolName(request.toolName)) return { approved: true };
|
|
1387
|
+
return requestBuiltinToolApproval({
|
|
1388
|
+
toolCallId: request.toolCallId,
|
|
1389
|
+
toolName: request.toolName,
|
|
1390
|
+
input: request.input
|
|
1391
|
+
});
|
|
1392
|
+
},
|
|
1393
|
+
consumePendingUserMessage
|
|
1394
|
+
});
|
|
1395
|
+
agentHasRun = false;
|
|
1396
|
+
unsubscribe = agent.subscribe((event) => {
|
|
1397
|
+
if (!translatorState || !currentEmit) return;
|
|
1398
|
+
for (const part of translateClineEvent(event, translatorState)) currentEmit(part);
|
|
1399
|
+
});
|
|
1400
|
+
}
|
|
1401
|
+
function createPromptControl(controlInput) {
|
|
1402
|
+
const abortHandler = () => {
|
|
1403
|
+
settlePendingToolResults("Turn aborted");
|
|
1404
|
+
settlePendingToolApprovals("Turn aborted");
|
|
1405
|
+
settlePendingQuestions("The question was cancelled.");
|
|
1406
|
+
agent?.abort("Turn aborted by caller");
|
|
1407
|
+
};
|
|
1408
|
+
if (controlInput.abortSignal) {
|
|
1409
|
+
controlInput.abortSignal.addEventListener("abort", abortHandler, { once: true });
|
|
1410
|
+
controlInput.done.then(() => {
|
|
1411
|
+
controlInput.abortSignal?.removeEventListener("abort", abortHandler);
|
|
1412
|
+
}, () => {
|
|
1413
|
+
controlInput.abortSignal?.removeEventListener("abort", abortHandler);
|
|
1414
|
+
});
|
|
1415
|
+
}
|
|
1416
|
+
return {
|
|
1417
|
+
async submitToolResult(args) {
|
|
1418
|
+
const pendingQuestion = pendingQuestions.get(args.toolCallId);
|
|
1419
|
+
if (pendingQuestion != null) {
|
|
1420
|
+
pendingQuestions.delete(args.toolCallId);
|
|
1421
|
+
pendingQuestion.resolve(toClineQuestionResult({
|
|
1422
|
+
nativeInput: pendingQuestion.input,
|
|
1423
|
+
output: args.output
|
|
1424
|
+
}));
|
|
1425
|
+
return;
|
|
1426
|
+
}
|
|
1427
|
+
if (args.toolResult?.toolName === "askUserQuestions") {
|
|
1428
|
+
const nativeRequest = args.toolResult.providerOptions?.cline?.nativeRequest;
|
|
1429
|
+
if (nativeRequest == null) return;
|
|
1430
|
+
pendingQuestionResults.set(clineQuestionKey(nativeRequest), { output: args.output });
|
|
1431
|
+
return;
|
|
1432
|
+
}
|
|
1433
|
+
const pending = pendingToolResults.get(args.toolCallId);
|
|
1434
|
+
if (!pending) return;
|
|
1435
|
+
pendingToolResults.delete(args.toolCallId);
|
|
1436
|
+
pending.resolve(createClineToolResult({
|
|
1437
|
+
output: args.output,
|
|
1438
|
+
isError: args.isError
|
|
1439
|
+
}));
|
|
1440
|
+
},
|
|
1441
|
+
async submitToolApproval(args) {
|
|
1442
|
+
const pending = pendingToolApprovals.get(args.approvalId);
|
|
1443
|
+
if (!pending) return;
|
|
1444
|
+
pendingToolApprovals.delete(args.approvalId);
|
|
1445
|
+
pending.resolve({
|
|
1446
|
+
approved: args.approved,
|
|
1447
|
+
...args.reason !== void 0 ? { reason: args.reason } : {}
|
|
1448
|
+
});
|
|
1449
|
+
},
|
|
1450
|
+
submitUserMessage(text) {
|
|
1451
|
+
if (!acceptingUserMessages) return Promise.reject(/* @__PURE__ */ new Error("Cline has no running turn to steer."));
|
|
1452
|
+
return new Promise((resolve, reject) => {
|
|
1453
|
+
pendingUserMessages.push({
|
|
1454
|
+
text,
|
|
1455
|
+
resolve,
|
|
1456
|
+
reject
|
|
1457
|
+
});
|
|
1458
|
+
});
|
|
1459
|
+
},
|
|
1460
|
+
done: controlInput.done
|
|
1461
|
+
};
|
|
1462
|
+
}
|
|
1463
|
+
async function runTurn(turnOpts) {
|
|
1464
|
+
if (stopped) throw new Error("Cline session has been stopped.");
|
|
1465
|
+
const userTools = turnOpts.tools;
|
|
1466
|
+
const skillsRuntime = createClineSkillsRuntime({ skills: turnOpts.skills });
|
|
1467
|
+
if (turnOpts.responseFormat?.type === "json" && turnOpts.responseFormat.schema == null) throw new HarnessCapabilityUnsupportedError({
|
|
1468
|
+
message: "Harness 'cline' requires a JSON schema for structured output.",
|
|
1469
|
+
harnessId: HARNESS_ID
|
|
1470
|
+
});
|
|
1471
|
+
if (turnOpts.responseFormat?.type === "json" && agentModel.providerId === "openai-codex-cli") throw new HarnessCapabilityUnsupportedError({
|
|
1472
|
+
message: "Harness 'cline' cannot require structured output with the openai-codex-cli provider because that provider does not expose external tools.",
|
|
1473
|
+
harnessId: HARNESS_ID
|
|
1474
|
+
});
|
|
1475
|
+
if (turnOpts.model != null && turnOpts.model !== activeModelId) {
|
|
1476
|
+
activeModelId = turnOpts.model;
|
|
1477
|
+
agentModel = createClineAgentModel({
|
|
1478
|
+
settings: input.settings,
|
|
1479
|
+
clientApp: input.clientApp,
|
|
1480
|
+
modelId: activeModelId
|
|
1481
|
+
});
|
|
1482
|
+
}
|
|
1483
|
+
const signature = JSON.stringify({
|
|
1484
|
+
modelId: activeModelId,
|
|
1485
|
+
tools: userTools,
|
|
1486
|
+
responseFormat: turnOpts.responseFormat,
|
|
1487
|
+
instructions: turnOpts.instructions,
|
|
1488
|
+
skills: skillsRuntime.signature
|
|
1489
|
+
});
|
|
1490
|
+
if (agent == null || signature !== lastAgentConfigurationSignature) {
|
|
1491
|
+
currentMessages = agent?.snapshot().messages ?? currentMessages;
|
|
1492
|
+
rebuildAgent({
|
|
1493
|
+
userTools,
|
|
1494
|
+
skillsRuntime,
|
|
1495
|
+
responseFormat: turnOpts.responseFormat,
|
|
1496
|
+
...turnOpts.instructions ? { instructions: turnOpts.instructions } : {}
|
|
1497
|
+
});
|
|
1498
|
+
lastAgentConfigurationSignature = signature;
|
|
1499
|
+
}
|
|
1500
|
+
currentEmit = turnOpts.emit;
|
|
1501
|
+
const userToolNames = new Set(userTools.map((tool) => tool.name));
|
|
1502
|
+
translatorState = createClineTranslatorState({
|
|
1503
|
+
builtinToolNames: activeBuiltinNames.filter((name) => !userToolNames.has(name)),
|
|
1504
|
+
hostToolNames: userToolNames,
|
|
1505
|
+
ignoredToolNames: turnOpts.responseFormat?.type === "json" ? ["structured_output"] : [],
|
|
1506
|
+
mcpToolNames: mcpRuntime.toolNames
|
|
1507
|
+
});
|
|
1508
|
+
turnOpts.emit({
|
|
1509
|
+
type: "stream-start",
|
|
1510
|
+
...activeModelId ? { modelId: activeModelId } : {}
|
|
1511
|
+
});
|
|
1512
|
+
acceptingUserMessages = true;
|
|
1513
|
+
const turnPromise = (async () => {
|
|
1514
|
+
const runtime = agent;
|
|
1515
|
+
try {
|
|
1516
|
+
let nextText = turnOpts.text;
|
|
1517
|
+
let result;
|
|
1518
|
+
for (;;) {
|
|
1519
|
+
result = nextText === void 0 ? await runtime.continue() : agentHasRun ? await runtime.continue(nextText) : await runtime.run(nextText);
|
|
1520
|
+
agentHasRun = true;
|
|
1521
|
+
currentMessages = result.messages;
|
|
1522
|
+
if (translatorState) for (const part of finishClineTranslation(translatorState)) currentEmit?.(part);
|
|
1523
|
+
if (result.status !== "completed") break;
|
|
1524
|
+
const pendingUserMessage = consumePendingUserMessage();
|
|
1525
|
+
if (pendingUserMessage == null) break;
|
|
1526
|
+
nextText = pendingUserMessage;
|
|
1527
|
+
}
|
|
1528
|
+
if (turnOpts.responseFormat?.type === "json" && result.status === "completed") {
|
|
1529
|
+
const id = `structured-output-${input.sessionId}`;
|
|
1530
|
+
currentEmit?.({
|
|
1531
|
+
type: "text-start",
|
|
1532
|
+
id
|
|
1533
|
+
});
|
|
1534
|
+
currentEmit?.({
|
|
1535
|
+
type: "text-delta",
|
|
1536
|
+
id,
|
|
1537
|
+
delta: result.outputText
|
|
1538
|
+
});
|
|
1539
|
+
currentEmit?.({
|
|
1540
|
+
type: "text-end",
|
|
1541
|
+
id
|
|
1542
|
+
});
|
|
1543
|
+
currentEmit?.({
|
|
1544
|
+
type: "finish-step",
|
|
1545
|
+
finishReason: {
|
|
1546
|
+
unified: "stop",
|
|
1547
|
+
raw: "structured-output"
|
|
1548
|
+
},
|
|
1549
|
+
usage: {
|
|
1550
|
+
inputTokens: {
|
|
1551
|
+
total: 0,
|
|
1552
|
+
noCache: void 0,
|
|
1553
|
+
cacheRead: void 0,
|
|
1554
|
+
cacheWrite: void 0
|
|
1555
|
+
},
|
|
1556
|
+
outputTokens: {
|
|
1557
|
+
total: 0,
|
|
1558
|
+
text: void 0,
|
|
1559
|
+
reasoning: void 0
|
|
1560
|
+
}
|
|
1561
|
+
}
|
|
1562
|
+
});
|
|
1563
|
+
}
|
|
1564
|
+
if (result.status === "aborted") {
|
|
1565
|
+
if (suspending) return;
|
|
1566
|
+
currentEmit?.({
|
|
1567
|
+
type: "finish",
|
|
1568
|
+
finishReason: mapRunFinishReason(result.status),
|
|
1569
|
+
totalUsage: usageFromAgentUsage(result.usage)
|
|
1570
|
+
});
|
|
1571
|
+
return;
|
|
1572
|
+
}
|
|
1573
|
+
if (result.status === "failed") {
|
|
1574
|
+
currentEmit?.({
|
|
1575
|
+
type: "error",
|
|
1576
|
+
error: result.error ?? /* @__PURE__ */ new Error("Cline agent run failed")
|
|
1577
|
+
});
|
|
1578
|
+
return;
|
|
1579
|
+
}
|
|
1580
|
+
currentEmit?.({
|
|
1581
|
+
type: "finish",
|
|
1582
|
+
finishReason: mapRunFinishReason(result.status),
|
|
1583
|
+
totalUsage: usageFromAgentUsage(result.usage)
|
|
1584
|
+
});
|
|
1585
|
+
} catch (error) {
|
|
1586
|
+
if (suspending && isAbortError(error)) return;
|
|
1587
|
+
currentEmit?.({
|
|
1588
|
+
type: "error",
|
|
1589
|
+
error
|
|
1590
|
+
});
|
|
1591
|
+
} finally {
|
|
1592
|
+
acceptingUserMessages = false;
|
|
1593
|
+
rejectPendingUserMessages(/* @__PURE__ */ new Error("Cline turn ended before accepting the user message."));
|
|
1594
|
+
}
|
|
1595
|
+
})();
|
|
1596
|
+
const activeTurnToken = {};
|
|
1597
|
+
const done = turnPromise.finally(() => {
|
|
1598
|
+
if (activeTurn?.token === activeTurnToken) activeTurn = void 0;
|
|
1599
|
+
currentEmit = void 0;
|
|
1600
|
+
});
|
|
1601
|
+
activeTurn = {
|
|
1602
|
+
token: activeTurnToken,
|
|
1603
|
+
done
|
|
1604
|
+
};
|
|
1605
|
+
return createPromptControl({
|
|
1606
|
+
done,
|
|
1607
|
+
...turnOpts.abortSignal ? { abortSignal: turnOpts.abortSignal } : {}
|
|
1608
|
+
});
|
|
1609
|
+
}
|
|
1610
|
+
async function teardown() {
|
|
1611
|
+
unsubscribe?.();
|
|
1612
|
+
unsubscribe = void 0;
|
|
1613
|
+
agent = void 0;
|
|
1614
|
+
await mcpRuntime.dispose();
|
|
1615
|
+
}
|
|
1616
|
+
const doStop = async () => {
|
|
1617
|
+
if (stopped) throw new Error("Cline session has been stopped.");
|
|
1618
|
+
stopped = true;
|
|
1619
|
+
parkedClineSessions.delete(input.sessionId);
|
|
1620
|
+
settlePendingToolResults("Cline session stopped");
|
|
1621
|
+
settlePendingToolApprovals("Cline session stopped");
|
|
1622
|
+
settlePendingQuestions("The question was cancelled.");
|
|
1623
|
+
try {
|
|
1624
|
+
await persistHistory();
|
|
1625
|
+
} catch {}
|
|
1626
|
+
agent?.abort("Cline session stopped");
|
|
1627
|
+
await teardown();
|
|
1628
|
+
return {
|
|
1629
|
+
type: "resume-session",
|
|
1630
|
+
harnessId: HARNESS_ID,
|
|
1631
|
+
specificationVersion: "harness-v1",
|
|
1632
|
+
data: { historyFileName: CLINE_DEFAULT_HISTORY_FILE_NAME }
|
|
1633
|
+
};
|
|
1634
|
+
};
|
|
1635
|
+
const sessionImpl = {
|
|
1636
|
+
sessionId: input.sessionId,
|
|
1637
|
+
isResume: input.isResume,
|
|
1638
|
+
doPromptTurn: async (promptOpts) => {
|
|
1639
|
+
return runTurn({
|
|
1640
|
+
text: extractUserText(promptOpts.prompt),
|
|
1641
|
+
...promptOpts.model ? { model: promptOpts.model } : {},
|
|
1642
|
+
skills: promptOpts.skills,
|
|
1643
|
+
tools: promptOpts.tools ?? [],
|
|
1644
|
+
...promptOpts.instructions ? { instructions: promptOpts.instructions } : {},
|
|
1645
|
+
emit: promptOpts.emit,
|
|
1646
|
+
...promptOpts.abortSignal ? { abortSignal: promptOpts.abortSignal } : {},
|
|
1647
|
+
responseFormat: promptOpts.responseFormat
|
|
1648
|
+
});
|
|
1649
|
+
},
|
|
1650
|
+
doContinueTurn: async (continueOpts) => {
|
|
1651
|
+
if (activeTurn != null) {
|
|
1652
|
+
currentEmit = continueOpts.emit;
|
|
1653
|
+
return createPromptControl({
|
|
1654
|
+
done: activeTurn.done,
|
|
1655
|
+
...continueOpts.abortSignal ? { abortSignal: continueOpts.abortSignal } : {}
|
|
1656
|
+
});
|
|
1657
|
+
}
|
|
1658
|
+
return runTurn({
|
|
1659
|
+
text: void 0,
|
|
1660
|
+
...continueOpts.model ? { model: continueOpts.model } : {},
|
|
1661
|
+
skills: continueOpts.skills,
|
|
1662
|
+
tools: continueOpts.tools ?? [],
|
|
1663
|
+
...continueOpts.instructions ? { instructions: continueOpts.instructions } : {},
|
|
1664
|
+
emit: continueOpts.emit,
|
|
1665
|
+
...continueOpts.abortSignal ? { abortSignal: continueOpts.abortSignal } : {},
|
|
1666
|
+
responseFormat: continueOpts.responseFormat
|
|
1667
|
+
});
|
|
1668
|
+
},
|
|
1669
|
+
doCompact: async () => {
|
|
1670
|
+
throw new HarnessCapabilityUnsupportedError({
|
|
1671
|
+
message: "cline: manual compaction is not supported by the standalone Cline runtime.",
|
|
1672
|
+
harnessId: HARNESS_ID
|
|
1673
|
+
});
|
|
1674
|
+
},
|
|
1675
|
+
doDestroy: async () => {
|
|
1676
|
+
if (stopped) return;
|
|
1677
|
+
stopped = true;
|
|
1678
|
+
parkedClineSessions.delete(input.sessionId);
|
|
1679
|
+
settlePendingToolResults("Cline session stopped");
|
|
1680
|
+
settlePendingToolApprovals("Cline session stopped");
|
|
1681
|
+
agent?.abort("Cline session destroyed");
|
|
1682
|
+
await teardown();
|
|
1683
|
+
},
|
|
1684
|
+
doStop,
|
|
1685
|
+
doDetach: async () => {
|
|
1686
|
+
if (activeTurn != null || pendingToolResults.size > 0 || pendingQuestions.size > 0) {
|
|
1687
|
+
parkedClineSessions.set(input.sessionId, sessionImpl);
|
|
1688
|
+
try {
|
|
1689
|
+
await persistHistory();
|
|
1690
|
+
} catch {}
|
|
1691
|
+
return {
|
|
1692
|
+
type: "resume-session",
|
|
1693
|
+
harnessId: HARNESS_ID,
|
|
1694
|
+
specificationVersion: "harness-v1",
|
|
1695
|
+
data: { historyFileName: CLINE_DEFAULT_HISTORY_FILE_NAME }
|
|
1696
|
+
};
|
|
1697
|
+
}
|
|
1698
|
+
return doStop();
|
|
1699
|
+
},
|
|
1700
|
+
doSuspendTurn: async () => {
|
|
1701
|
+
if (stopped) throw new Error("Cline session has been stopped.");
|
|
1702
|
+
if (activeTurn != null && (pendingToolResults.size > 0 || pendingToolApprovals.size > 0 || pendingQuestions.size > 0)) {
|
|
1703
|
+
parkedClineSessions.set(input.sessionId, sessionImpl);
|
|
1704
|
+
try {
|
|
1705
|
+
await persistHistory();
|
|
1706
|
+
} catch {}
|
|
1707
|
+
return {
|
|
1708
|
+
type: "continue-turn",
|
|
1709
|
+
harnessId: HARNESS_ID,
|
|
1710
|
+
specificationVersion: "harness-v1",
|
|
1711
|
+
data: { historyFileName: CLINE_DEFAULT_HISTORY_FILE_NAME }
|
|
1712
|
+
};
|
|
1713
|
+
}
|
|
1714
|
+
suspending = true;
|
|
1715
|
+
agent?.abort("Cline session suspended");
|
|
1716
|
+
if (activeTurn) await activeTurn.done.catch(() => {});
|
|
1717
|
+
try {
|
|
1718
|
+
await persistHistory();
|
|
1719
|
+
} catch {}
|
|
1720
|
+
stopped = true;
|
|
1721
|
+
parkedClineSessions.delete(input.sessionId);
|
|
1722
|
+
settlePendingToolResults("Cline session suspended");
|
|
1723
|
+
settlePendingToolApprovals("Cline session suspended");
|
|
1724
|
+
await teardown();
|
|
1725
|
+
return {
|
|
1726
|
+
type: "continue-turn",
|
|
1727
|
+
harnessId: HARNESS_ID,
|
|
1728
|
+
specificationVersion: "harness-v1",
|
|
1729
|
+
data: { historyFileName: CLINE_DEFAULT_HISTORY_FILE_NAME }
|
|
1730
|
+
};
|
|
1731
|
+
}
|
|
1732
|
+
};
|
|
1733
|
+
return sessionImpl;
|
|
1996
1734
|
}
|
|
1997
|
-
|
|
1998
|
-
|
|
1999
|
-
|
|
2000
|
-
|
|
2001
|
-
|
|
2002
|
-
|
|
2003
|
-
|
|
2004
|
-
|
|
2005
|
-
|
|
2006
|
-
|
|
2007
|
-
|
|
2008
|
-
|
|
2009
|
-
|
|
2010
|
-
|
|
2011
|
-
|
|
2012
|
-
|
|
2013
|
-
|
|
2014
|
-
|
|
2015
|
-
|
|
2016
|
-
|
|
2017
|
-
|
|
2018
|
-
|
|
2019
|
-
|
|
2020
|
-
|
|
2021
|
-
|
|
2022
|
-
|
|
2023
|
-
|
|
2024
|
-
|
|
2025
|
-
|
|
2026
|
-
|
|
2027
|
-
|
|
2028
|
-
|
|
2029
|
-
|
|
2030
|
-
|
|
2031
|
-
|
|
2032
|
-
|
|
2033
|
-
|
|
2034
|
-
|
|
2035
|
-
|
|
2036
|
-
|
|
2037
|
-
|
|
2038
|
-
|
|
2039
|
-
|
|
2040
|
-
|
|
2041
|
-
|
|
2042
|
-
|
|
2043
|
-
|
|
2044
|
-
|
|
2045
|
-
|
|
2046
|
-
|
|
2047
|
-
|
|
2048
|
-
|
|
2049
|
-
|
|
2050
|
-
|
|
2051
|
-
|
|
2052
|
-
|
|
2053
|
-
|
|
2054
|
-
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
|
|
2059
|
-
|
|
2060
|
-
|
|
2061
|
-
|
|
2062
|
-
|
|
2063
|
-
|
|
2064
|
-
|
|
2065
|
-
|
|
2066
|
-
|
|
2067
|
-
|
|
2068
|
-
|
|
2069
|
-
|
|
2070
|
-
|
|
2071
|
-
|
|
2072
|
-
|
|
2073
|
-
|
|
2074
|
-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
2078
|
-
|
|
2079
|
-
|
|
2080
|
-
|
|
2081
|
-
|
|
2082
|
-
|
|
2083
|
-
|
|
2084
|
-
|
|
2085
|
-
|
|
2086
|
-
|
|
2087
|
-
|
|
2088
|
-
|
|
2089
|
-
|
|
2090
|
-
|
|
2091
|
-
|
|
2092
|
-
|
|
1735
|
+
//#endregion
|
|
1736
|
+
//#region src/version.ts
|
|
1737
|
+
const VERSION = "1.0.61";
|
|
1738
|
+
//#endregion
|
|
1739
|
+
//#region src/cline-harness.ts
|
|
1740
|
+
/**
|
|
1741
|
+
* Value used to identify the harness in provider request headers.
|
|
1742
|
+
*/
|
|
1743
|
+
const CLINE_CLIENT_APP = `ai-sdk-harness-cline/${VERSION}`;
|
|
1744
|
+
const CLINE_BUILTIN_TOOLS = {
|
|
1745
|
+
askUserQuestions: {
|
|
1746
|
+
...HARNESS_V1_BUILTIN_TOOLS.askUserQuestions,
|
|
1747
|
+
nativeName: "ask_question",
|
|
1748
|
+
toolUseKind: "readonly"
|
|
1749
|
+
},
|
|
1750
|
+
read: commonTool("read", {
|
|
1751
|
+
nativeName: "read",
|
|
1752
|
+
toolUseKind: "readonly",
|
|
1753
|
+
description: "Read file contents.",
|
|
1754
|
+
inputSchema: z.object({ file_path: z.string() })
|
|
1755
|
+
}),
|
|
1756
|
+
write: commonTool("write", {
|
|
1757
|
+
nativeName: "write",
|
|
1758
|
+
toolUseKind: "edit",
|
|
1759
|
+
description: "Overwrite or create a file.",
|
|
1760
|
+
inputSchema: z.object({
|
|
1761
|
+
file_path: z.string(),
|
|
1762
|
+
content: z.string()
|
|
1763
|
+
})
|
|
1764
|
+
}),
|
|
1765
|
+
edit: commonTool("edit", {
|
|
1766
|
+
nativeName: "edit",
|
|
1767
|
+
toolUseKind: "edit",
|
|
1768
|
+
description: "Edit a file by exact string replacement.",
|
|
1769
|
+
inputSchema: z.object({
|
|
1770
|
+
file_path: z.string(),
|
|
1771
|
+
old_string: z.string(),
|
|
1772
|
+
new_string: z.string()
|
|
1773
|
+
})
|
|
1774
|
+
}),
|
|
1775
|
+
bash: commonTool("bash", {
|
|
1776
|
+
nativeName: "bash",
|
|
1777
|
+
toolUseKind: "bash",
|
|
1778
|
+
description: "Execute a shell command in the sandbox.",
|
|
1779
|
+
inputSchema: z.object({
|
|
1780
|
+
command: z.string(),
|
|
1781
|
+
timeout: z.number().optional()
|
|
1782
|
+
})
|
|
1783
|
+
}),
|
|
1784
|
+
grep: commonTool("grep", {
|
|
1785
|
+
nativeName: "grep",
|
|
1786
|
+
toolUseKind: "readonly",
|
|
1787
|
+
description: "Search file contents with regex.",
|
|
1788
|
+
inputSchema: z.object({
|
|
1789
|
+
pattern: z.string(),
|
|
1790
|
+
path: z.string().optional(),
|
|
1791
|
+
glob: z.string().optional(),
|
|
1792
|
+
ignoreCase: z.boolean().optional(),
|
|
1793
|
+
literal: z.boolean().optional(),
|
|
1794
|
+
context: z.number().optional(),
|
|
1795
|
+
limit: z.number().optional()
|
|
1796
|
+
})
|
|
1797
|
+
}),
|
|
1798
|
+
glob: commonTool("glob", {
|
|
1799
|
+
nativeName: "glob",
|
|
1800
|
+
toolUseKind: "readonly",
|
|
1801
|
+
description: "Find files matching a glob pattern.",
|
|
1802
|
+
inputSchema: z.object({
|
|
1803
|
+
pattern: z.string(),
|
|
1804
|
+
path: z.string().optional(),
|
|
1805
|
+
limit: z.number().optional()
|
|
1806
|
+
})
|
|
1807
|
+
}),
|
|
1808
|
+
ls: {
|
|
1809
|
+
...tool({
|
|
1810
|
+
description: "List directory entries.",
|
|
1811
|
+
inputSchema: z.object({
|
|
1812
|
+
path: z.string().optional(),
|
|
1813
|
+
limit: z.number().optional()
|
|
1814
|
+
}),
|
|
1815
|
+
outputSchema: z.unknown()
|
|
1816
|
+
}),
|
|
1817
|
+
nativeName: "ls",
|
|
1818
|
+
toolUseKind: "readonly"
|
|
1819
|
+
},
|
|
1820
|
+
skills: {
|
|
1821
|
+
...tool({
|
|
1822
|
+
description: "Execute a configured skill by name.",
|
|
1823
|
+
inputSchema: z.object({
|
|
1824
|
+
skill: z.string(),
|
|
1825
|
+
args: z.string().optional()
|
|
1826
|
+
}),
|
|
1827
|
+
outputSchema: z.string()
|
|
1828
|
+
}),
|
|
1829
|
+
nativeName: "skills",
|
|
1830
|
+
toolUseKind: "readonly"
|
|
1831
|
+
}
|
|
2093
1832
|
};
|
|
2094
1833
|
function createCline(settings = {}) {
|
|
2095
|
-
|
|
2096
|
-
|
|
2097
|
-
|
|
2098
|
-
|
|
2099
|
-
|
|
2100
|
-
|
|
2101
|
-
|
|
2102
|
-
|
|
2103
|
-
|
|
2104
|
-
|
|
2105
|
-
|
|
2106
|
-
|
|
2107
|
-
|
|
2108
|
-
|
|
2109
|
-
|
|
2110
|
-
|
|
2111
|
-
|
|
2112
|
-
|
|
2113
|
-
|
|
2114
|
-
|
|
2115
|
-
|
|
2116
|
-
|
|
2117
|
-
|
|
2118
|
-
|
|
2119
|
-
|
|
2120
|
-
|
|
2121
|
-
|
|
2122
|
-
|
|
2123
|
-
|
|
2124
|
-
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2130
|
-
|
|
2131
|
-
|
|
2132
|
-
|
|
2133
|
-
|
|
2134
|
-
|
|
1834
|
+
return {
|
|
1835
|
+
specificationVersion: "harness-v1",
|
|
1836
|
+
harnessId: "cline",
|
|
1837
|
+
builtinTools: CLINE_BUILTIN_TOOLS,
|
|
1838
|
+
supportsBuiltinToolApprovals: true,
|
|
1839
|
+
supportsBuiltinToolFiltering: true,
|
|
1840
|
+
lifecycleStateSchema: clineResumeStateSchema,
|
|
1841
|
+
doStart: async (startOpts) => {
|
|
1842
|
+
const authentication = await resolveClineAuthentication({
|
|
1843
|
+
auth: settings.auth,
|
|
1844
|
+
providerId: settings.providerId,
|
|
1845
|
+
apiKey: settings.apiKey
|
|
1846
|
+
});
|
|
1847
|
+
const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;
|
|
1848
|
+
const resumeData = lifecycleState?.data;
|
|
1849
|
+
return createClineSession({
|
|
1850
|
+
sessionId: startOpts.sessionId,
|
|
1851
|
+
sandboxSession: startOpts.sandboxSession,
|
|
1852
|
+
sessionWorkDir: startOpts.sessionWorkDir,
|
|
1853
|
+
settings: {
|
|
1854
|
+
authEnv: authentication.environment,
|
|
1855
|
+
isAuthenticationEnvironmentOverride: isHarnessAuthenticationEnvironment(settings.auth),
|
|
1856
|
+
...settings.mcpServers ? { mcpServers: settings.mcpServers } : {},
|
|
1857
|
+
...settings.providerId ? { providerId: settings.providerId } : {},
|
|
1858
|
+
...settings.apiKey ?? authentication.subscriptionApiKey ? { apiKey: settings.apiKey ?? authentication.subscriptionApiKey } : {},
|
|
1859
|
+
...settings.baseUrl ? { baseUrl: settings.baseUrl } : {},
|
|
1860
|
+
...settings.headers ? { headers: settings.headers } : {},
|
|
1861
|
+
...startOpts.headers ? { agentHeaders: startOpts.headers } : {},
|
|
1862
|
+
...settings.reasoningEffort !== void 0 ? { reasoningEffort: settings.reasoningEffort } : {},
|
|
1863
|
+
...settings.maxIterations !== void 0 ? { maxIterations: settings.maxIterations } : {}
|
|
1864
|
+
},
|
|
1865
|
+
clientApp: CLINE_CLIENT_APP,
|
|
1866
|
+
isResume: lifecycleState != null,
|
|
1867
|
+
...startOpts.permissionMode ? { permissionMode: startOpts.permissionMode } : {},
|
|
1868
|
+
...startOpts.builtinToolFiltering ? { builtinToolFiltering: startOpts.builtinToolFiltering } : {},
|
|
1869
|
+
...resumeData?.historyFileName ? { resumeHistoryFileName: resumeData.historyFileName } : {},
|
|
1870
|
+
...startOpts.abortSignal ? { abortSignal: startOpts.abortSignal } : {}
|
|
1871
|
+
});
|
|
1872
|
+
}
|
|
1873
|
+
};
|
|
2135
1874
|
}
|
|
1875
|
+
//#endregion
|
|
1876
|
+
//#region src/index.ts
|
|
1877
|
+
/**
|
|
1878
|
+
* Default `cline` harness instance with no overrides — suitable for the
|
|
1879
|
+
* common case where the Cline SDK's defaults are fine. Equivalent to
|
|
1880
|
+
* `createCline()`.
|
|
1881
|
+
*/
|
|
1882
|
+
const cline = createCline();
|
|
1883
|
+
//#endregion
|
|
1884
|
+
export { VERSION, cline, createCline };
|
|
2136
1885
|
|
|
2137
|
-
// src/index.ts
|
|
2138
|
-
var cline = createCline();
|
|
2139
|
-
export {
|
|
2140
|
-
VERSION,
|
|
2141
|
-
cline,
|
|
2142
|
-
createCline
|
|
2143
|
-
};
|
|
2144
1886
|
//# sourceMappingURL=index.js.map
|