@ai-sdk/harness-cline 0.0.0 → 1.0.0

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/dist/index.js ADDED
@@ -0,0 +1,1587 @@
1
+ // src/cline-harness.ts
2
+ import {
3
+ commonTool
4
+ } from "@ai-sdk/harness";
5
+ import { tool } from "@ai-sdk/provider-utils";
6
+ import { z as z2 } from "zod/v4";
7
+
8
+ // src/cline-auth.ts
9
+ import { getAiGatewayAuthFromEnv } from "@ai-sdk/harness/utils";
10
+ function resolveClineEnv({
11
+ auth = "auto",
12
+ env = process.env
13
+ }) {
14
+ if (auth === "direct") {
15
+ return pickDirectClineEnv({ env });
16
+ }
17
+ const gatewayAuth = getAiGatewayAuthFromEnv({ env });
18
+ if (auth === "ai-gateway" || gatewayAuth.apiKey) {
19
+ return {
20
+ ...gatewayAuth.apiKey ? { AI_GATEWAY_API_KEY: gatewayAuth.apiKey } : {},
21
+ AI_GATEWAY_BASE_URL: gatewayAuth.baseUrl
22
+ };
23
+ }
24
+ return pickDirectClineEnv({ env });
25
+ }
26
+ function pickDirectClineEnv({
27
+ env
28
+ }) {
29
+ return {
30
+ ...env.CLINE_API_KEY ? { CLINE_API_KEY: env.CLINE_API_KEY } : {},
31
+ ...env.CLINE_API_BASE_URL ? { CLINE_API_BASE_URL: env.CLINE_API_BASE_URL } : {}
32
+ };
33
+ }
34
+
35
+ // src/cline-resume-state.ts
36
+ import { createHash } from "crypto";
37
+ import path from "path";
38
+ import {
39
+ safeParseJSON
40
+ } from "@ai-sdk/provider-utils";
41
+ import { z } from "zod/v4";
42
+ var CLINE_HISTORY_FILE_NAME_PATTERN = /^[A-Za-z0-9][A-Za-z0-9._-]*\.json$/;
43
+ function safeClineHistoryFileName(historyFileName) {
44
+ if (!CLINE_HISTORY_FILE_NAME_PATTERN.test(historyFileName)) {
45
+ throw new Error(`Invalid Cline history file name: ${historyFileName}`);
46
+ }
47
+ return historyFileName;
48
+ }
49
+ var clineHistoryFileNameSchema = z.string().refine(
50
+ (historyFileName) => CLINE_HISTORY_FILE_NAME_PATTERN.test(historyFileName),
51
+ "Cline historyFileName must be a safe .json basename."
52
+ );
53
+ var clineResumeStateSchema = z.looseObject({
54
+ historyFileName: clineHistoryFileNameSchema.optional()
55
+ });
56
+ var CLINE_DEFAULT_HISTORY_FILE_NAME = "history.json";
57
+ function resolveClinePrivateSessionDirectory(input) {
58
+ const sessionKey = createHash("sha256").update(input.sessionId).digest("hex");
59
+ const privateSessionDir = path.posix.join(
60
+ input.sandboxHomeDir,
61
+ ".ai-sdk",
62
+ "harness-cline",
63
+ sessionKey
64
+ );
65
+ const relativePath = path.posix.relative(
66
+ input.sessionWorkDir,
67
+ privateSessionDir
68
+ );
69
+ if (relativePath === "" || !relativePath.startsWith("../") && !path.posix.isAbsolute(relativePath)) {
70
+ throw new Error(
71
+ `Cline private session directory ${JSON.stringify(privateSessionDir)} must be outside sessionWorkDir ${JSON.stringify(input.sessionWorkDir)}.`
72
+ );
73
+ }
74
+ return privateSessionDir;
75
+ }
76
+ function resolveContainedSandboxPath(input) {
77
+ const historyDir = path.posix.resolve(input.privateSessionDir);
78
+ const filePath = path.posix.resolve(
79
+ historyDir,
80
+ safeClineHistoryFileName(input.historyFileName)
81
+ );
82
+ const relativePath = path.posix.relative(historyDir, filePath);
83
+ if (relativePath === "" || relativePath.startsWith("..") || path.posix.isAbsolute(relativePath)) {
84
+ throw new Error(
85
+ `Invalid Cline history file name: ${input.historyFileName}`
86
+ );
87
+ }
88
+ return filePath;
89
+ }
90
+ async function persistHistoryToSandbox(args) {
91
+ const remotePath = resolveContainedSandboxPath({
92
+ privateSessionDir: args.privateSessionDir,
93
+ historyFileName: args.historyFileName
94
+ });
95
+ await args.sandbox.writeTextFile({
96
+ path: remotePath,
97
+ content: JSON.stringify(args.messages),
98
+ ...args.abortSignal ? { abortSignal: args.abortSignal } : {}
99
+ });
100
+ }
101
+ async function pullHistoryFromSandbox(args) {
102
+ const remotePath = resolveContainedSandboxPath({
103
+ privateSessionDir: args.privateSessionDir,
104
+ historyFileName: args.historyFileName
105
+ });
106
+ const content = await args.sandbox.readTextFile({
107
+ path: remotePath,
108
+ ...args.abortSignal ? { abortSignal: args.abortSignal } : {}
109
+ });
110
+ if (content == null) return void 0;
111
+ const parsed = await safeParseJSON({ text: content });
112
+ return parsed.success && Array.isArray(parsed.value) ? parsed.value : void 0;
113
+ }
114
+
115
+ // src/cline-session.ts
116
+ import {
117
+ HarnessCapabilityUnsupportedError as HarnessCapabilityUnsupportedError2
118
+ } from "@ai-sdk/harness";
119
+ import { resolveSandboxHomeDir } from "@ai-sdk/harness/utils";
120
+ import {
121
+ Agent
122
+ } from "@cline/agents";
123
+ import { Llms } from "@cline/core";
124
+
125
+ // src/cline-mcp.ts
126
+ var CLINE_CORE_PACKAGE = "@cline/core";
127
+ function isRecord(value) {
128
+ return value != null && typeof value === "object" && !Array.isArray(value);
129
+ }
130
+ function optionalStringArray(value) {
131
+ return Array.isArray(value) && value.every((item) => typeof item === "string") ? value : void 0;
132
+ }
133
+ function optionalStringRecord(value) {
134
+ return isRecord(value) && Object.values(value).every((item) => typeof item === "string") ? value : void 0;
135
+ }
136
+ function resolveTransport(input) {
137
+ if (isRecord(input.config.transport)) {
138
+ return input.config.transport;
139
+ }
140
+ if (typeof input.config.command === "string") {
141
+ const args = optionalStringArray(input.config.args);
142
+ const env = optionalStringRecord(input.config.env);
143
+ return {
144
+ type: "stdio",
145
+ command: input.config.command,
146
+ ...args ? { args } : {},
147
+ ...typeof input.config.cwd === "string" ? { cwd: input.config.cwd } : {},
148
+ ...env ? { env } : {}
149
+ };
150
+ }
151
+ if (typeof input.config.url === "string") {
152
+ const configuredType = input.config.type ?? input.config.transportType;
153
+ const headers = optionalStringRecord(input.config.headers);
154
+ return {
155
+ type: configuredType === "http" || configuredType === "streamableHttp" ? "streamableHttp" : "sse",
156
+ url: input.config.url,
157
+ ...headers ? { headers } : {}
158
+ };
159
+ }
160
+ throw new Error(
161
+ `Cline MCP server ${JSON.stringify(input.serverName)} must define a transport, command, or URL.`
162
+ );
163
+ }
164
+ function resolveRegistration(input) {
165
+ if (!isRecord(input.config)) {
166
+ throw new Error(
167
+ `Cline MCP server ${JSON.stringify(input.serverName)} must be configured with an object value.`
168
+ );
169
+ }
170
+ return {
171
+ name: input.serverName,
172
+ transport: resolveTransport({
173
+ serverName: input.serverName,
174
+ config: input.config
175
+ }),
176
+ ...typeof input.config.disabled === "boolean" ? { disabled: input.config.disabled } : {},
177
+ ...isRecord(input.config.metadata) ? { metadata: input.config.metadata } : {},
178
+ ...isRecord(input.config.oauth) ? { oauth: input.config.oauth } : {}
179
+ };
180
+ }
181
+ async function createClineMcpRuntime(input) {
182
+ if (input.mcpServers == null || Object.keys(input.mcpServers).length === 0) {
183
+ return {
184
+ tools: [],
185
+ toolNames: /* @__PURE__ */ new Set(),
186
+ dispose: async () => {
187
+ }
188
+ };
189
+ }
190
+ const {
191
+ createDefaultMcpServerClientFactory,
192
+ createMcpTools,
193
+ InMemoryMcpManager
194
+ } = await import(CLINE_CORE_PACKAGE);
195
+ const manager = new InMemoryMcpManager({
196
+ clientFactory: createDefaultMcpServerClientFactory()
197
+ });
198
+ const tools = [];
199
+ try {
200
+ for (const [serverName, config] of Object.entries(input.mcpServers)) {
201
+ const registration = resolveRegistration({ serverName, config });
202
+ await manager.registerServer(registration);
203
+ if (registration.disabled) continue;
204
+ tools.push(
205
+ ...await createMcpTools({
206
+ serverName,
207
+ provider: manager
208
+ })
209
+ );
210
+ }
211
+ } catch (error) {
212
+ await manager.dispose().catch(() => {
213
+ });
214
+ throw error;
215
+ }
216
+ return {
217
+ tools,
218
+ toolNames: new Set(tools.map((tool2) => tool2.name)),
219
+ dispose: async () => manager.dispose()
220
+ };
221
+ }
222
+
223
+ // src/cline-remote-ops.ts
224
+ import path2 from "path";
225
+ import { shellQuote } from "@ai-sdk/harness/utils";
226
+ function createClineRemoteOps(options) {
227
+ const { sandbox, workDir } = options;
228
+ const resolvePath = (inputPath) => {
229
+ const normalized = path2.posix.isAbsolute(inputPath) ? path2.posix.normalize(inputPath) : path2.posix.normalize(path2.posix.join(workDir, inputPath));
230
+ return normalized;
231
+ };
232
+ const runShell = async (command, input) => {
233
+ const result = await sandbox.run({
234
+ command,
235
+ workingDirectory: workDir,
236
+ ...input?.signal ? { abortSignal: input.signal } : {}
237
+ });
238
+ return {
239
+ exitCode: result.exitCode,
240
+ output: `${result.stdout}${result.stderr}`
241
+ };
242
+ };
243
+ const readFile = async (inputPath) => {
244
+ const resolved = resolvePath(inputPath);
245
+ const content = await sandbox.readTextFile({ path: resolved });
246
+ if (content == null) {
247
+ throw new Error(`File not found: ${inputPath}`);
248
+ }
249
+ return content;
250
+ };
251
+ const writeFile = async (inputPath, content) => {
252
+ const resolved = resolvePath(inputPath);
253
+ await sandbox.writeTextFile({ path: resolved, content });
254
+ };
255
+ const editFile = async (inputPath, oldText, newText) => {
256
+ const current = await readFile(inputPath);
257
+ const index = current.indexOf(oldText);
258
+ if (index === -1) {
259
+ throw new Error(`Text to replace was not found in ${inputPath}`);
260
+ }
261
+ const updated = `${current.slice(0, index)}${newText}${current.slice(
262
+ index + oldText.length
263
+ )}`;
264
+ await writeFile(inputPath, updated);
265
+ };
266
+ return {
267
+ resolvePath,
268
+ readFile,
269
+ writeFile,
270
+ editFile,
271
+ async bash(command, input) {
272
+ const controller = new AbortController();
273
+ const timeoutId = typeof input?.timeout === "number" && input.timeout > 0 ? setTimeout(() => controller.abort(), input.timeout * 1e3) : void 0;
274
+ const forwardedSignal = input?.signal;
275
+ const onAbort = () => controller.abort();
276
+ forwardedSignal?.addEventListener("abort", onAbort, { once: true });
277
+ try {
278
+ const result = await runShell(command, { signal: controller.signal });
279
+ return { output: result.output, exitCode: result.exitCode };
280
+ } finally {
281
+ forwardedSignal?.removeEventListener("abort", onAbort);
282
+ if (timeoutId) {
283
+ clearTimeout(timeoutId);
284
+ }
285
+ }
286
+ },
287
+ async grep(pattern, input = {}) {
288
+ const target = resolvePath(input.path ?? ".");
289
+ const flags = [
290
+ "-R",
291
+ "-n",
292
+ "--binary-files=without-match",
293
+ ...input.ignoreCase ? ["-i"] : [],
294
+ ...input.literal ? ["-F"] : [],
295
+ ...typeof input.context === "number" && input.context > 0 ? ["-C", String(input.context)] : [],
296
+ ...input.glob ? ["--include", input.glob] : []
297
+ ];
298
+ const limit = Math.max(1, input.limit ?? 100);
299
+ const result = await runShell(
300
+ [
301
+ `if [ ! -e ${shellQuote(
302
+ target
303
+ )} ]; then echo "__CLINE_GREP_NOT_FOUND__"; exit 2; fi`,
304
+ `grep ${flags.map(shellQuote).join(" ")} -- ${shellQuote(
305
+ pattern
306
+ )} ${shellQuote(target)} 2>/dev/null | head -n ${limit}`
307
+ ].join("; ")
308
+ );
309
+ const output = result.output.trim();
310
+ if (output.includes("__CLINE_GREP_NOT_FOUND__")) {
311
+ throw new Error(`Path not found: ${input.path ?? "."}`);
312
+ }
313
+ return output || "No matches found";
314
+ },
315
+ async glob(pattern, inputPath = ".", limit = 1e3) {
316
+ const target = resolvePath(inputPath);
317
+ const result = await runShell(
318
+ [
319
+ `if [ ! -e ${shellQuote(
320
+ target
321
+ )} ]; then echo "__CLINE_FIND_NOT_FOUND__"; exit 2; fi`,
322
+ `if [ -d ${shellQuote(target)} ]; then find ${shellQuote(
323
+ target
324
+ )} -type f -print; else printf '%s\\n' ${shellQuote(target)}; fi`
325
+ ].join("; ")
326
+ );
327
+ const output = result.output.trim();
328
+ if (output.includes("__CLINE_FIND_NOT_FOUND__")) {
329
+ throw new Error(`Path not found: ${inputPath}`);
330
+ }
331
+ return output.split("\n").filter(Boolean).map((absolutePath) => {
332
+ if (absolutePath === target) {
333
+ return path2.posix.basename(absolutePath);
334
+ }
335
+ return path2.posix.relative(target, absolutePath);
336
+ }).filter(
337
+ (candidate) => candidate.length > 0 && path2.matchesGlob(candidate, pattern)
338
+ ).sort(
339
+ (left, right) => left.toLowerCase().localeCompare(right.toLowerCase())
340
+ ).slice(0, limit);
341
+ },
342
+ async ls(inputPath = ".", limit = 500) {
343
+ const target = resolvePath(inputPath);
344
+ const result = await runShell(
345
+ [
346
+ `if [ ! -e ${shellQuote(
347
+ target
348
+ )} ]; then echo "__CLINE_LS_NOT_FOUND__"; exit 2; fi`,
349
+ `if [ ! -d ${shellQuote(
350
+ target
351
+ )} ]; then echo "__CLINE_LS_NOT_DIR__"; exit 3; fi`,
352
+ `cd ${shellQuote(target)}`,
353
+ "ls -1Ap"
354
+ ].join("; ")
355
+ );
356
+ const output = result.output.trim();
357
+ if (output.includes("__CLINE_LS_NOT_FOUND__")) {
358
+ throw new Error(`Path not found: ${inputPath}`);
359
+ }
360
+ if (output.includes("__CLINE_LS_NOT_DIR__")) {
361
+ throw new Error(`Not a directory: ${inputPath}`);
362
+ }
363
+ return output.split("\n").filter(Boolean).map((line) => line.replace(/[*=@|]$/, "")).sort(
364
+ (left, right) => left.toLowerCase().localeCompare(right.toLowerCase())
365
+ ).slice(0, limit);
366
+ }
367
+ };
368
+ }
369
+
370
+ // src/cline-skills.ts
371
+ import path3 from "path";
372
+ import { writeSkills } from "@ai-sdk/harness/utils";
373
+ async function writeClineSkills({
374
+ sandbox,
375
+ sandboxHomeDir,
376
+ skills,
377
+ abortSignal
378
+ }) {
379
+ if (skills.length === 0) return void 0;
380
+ const skillRootDir = path3.posix.join(sandboxHomeDir, ".agents", "skills");
381
+ await writeSkills({
382
+ sandbox,
383
+ rootDir: skillRootDir,
384
+ skills,
385
+ ...abortSignal ? { abortSignal } : {}
386
+ });
387
+ return skillRootDir;
388
+ }
389
+ function renderSkillsPromptSection(skills, skillRootDir) {
390
+ const entries = skills.map(
391
+ (skill) => `- ${skill.name}: ${skill.description}
392
+ SKILL.md: ${path3.posix.join(
393
+ skillRootDir,
394
+ skill.name,
395
+ "SKILL.md"
396
+ )}`
397
+ ).join("\n");
398
+ return "## Skills\n\nThe following skills are available. When a task matches a skill, read its SKILL.md with the `read` tool and follow its instructions.\n\n" + entries;
399
+ }
400
+
401
+ // src/cline-tools.ts
402
+ import {
403
+ createTool
404
+ } from "@cline/agents";
405
+
406
+ // src/cline-utils.ts
407
+ import {
408
+ HarnessCapabilityUnsupportedError
409
+ } from "@ai-sdk/harness";
410
+ import { secureJsonParse } from "@ai-sdk/provider-utils";
411
+ var HARNESS_ID = "cline";
412
+ function extractUserText(prompt) {
413
+ if (typeof prompt === "string") {
414
+ return prompt;
415
+ }
416
+ const { content } = prompt;
417
+ if (typeof content === "string") {
418
+ return content;
419
+ }
420
+ const parts = [];
421
+ for (const part of content) {
422
+ if (part.type !== "text") {
423
+ throw new HarnessCapabilityUnsupportedError({
424
+ message: `cline: only text user-message parts are supported; got '${part.type}'.`,
425
+ harnessId: HARNESS_ID
426
+ });
427
+ }
428
+ parts.push(part.text);
429
+ }
430
+ return parts.join("\n\n");
431
+ }
432
+ function getErrorText(error) {
433
+ return error instanceof Error ? error.message : String(error);
434
+ }
435
+ function toToolResultValue(output) {
436
+ if (output === void 0 || output === null) {
437
+ return null;
438
+ }
439
+ if (typeof output === "string") {
440
+ return output;
441
+ }
442
+ try {
443
+ const serialized = JSON.stringify(output);
444
+ return serialized === void 0 ? String(output) : secureJsonParse(serialized);
445
+ } catch {
446
+ return String(output);
447
+ }
448
+ }
449
+ function mapModelFinishReason(reason) {
450
+ switch (reason) {
451
+ case "stop":
452
+ return { unified: "stop", raw: reason };
453
+ case "tool-calls":
454
+ return { unified: "tool-calls", raw: reason };
455
+ case "max-tokens":
456
+ return { unified: "length", raw: reason };
457
+ case "error":
458
+ return { unified: "error", raw: reason };
459
+ default:
460
+ return { unified: "other", raw: reason };
461
+ }
462
+ }
463
+ function mapRunFinishReason(status) {
464
+ switch (status) {
465
+ case "completed":
466
+ return { unified: "stop", raw: status };
467
+ case "aborted":
468
+ return { unified: "other", raw: status };
469
+ case "failed":
470
+ return { unified: "error", raw: status };
471
+ }
472
+ }
473
+ function usageFromAgentUsage(usage) {
474
+ return {
475
+ inputTokens: {
476
+ total: usage.inputTokens,
477
+ noCache: void 0,
478
+ cacheRead: usage.cacheReadTokens,
479
+ cacheWrite: usage.cacheWriteTokens
480
+ },
481
+ outputTokens: {
482
+ total: usage.outputTokens,
483
+ text: void 0,
484
+ reasoning: usage.reasoningTokenCount
485
+ },
486
+ ...usage.totalCost !== void 0 ? { raw: { totalCost: usage.totalCost } } : {}
487
+ };
488
+ }
489
+ function usageFromMessageMetrics(metrics) {
490
+ return {
491
+ inputTokens: {
492
+ total: metrics?.inputTokens,
493
+ noCache: void 0,
494
+ cacheRead: metrics?.cacheReadTokens,
495
+ cacheWrite: metrics?.cacheWriteTokens
496
+ },
497
+ outputTokens: {
498
+ total: metrics?.outputTokens,
499
+ text: void 0,
500
+ reasoning: metrics?.reasoningTokenCount
501
+ }
502
+ };
503
+ }
504
+
505
+ // src/cline-tools.ts
506
+ var CLINE_NATIVE_BUILTIN_NAMES = [
507
+ "read",
508
+ "write",
509
+ "edit",
510
+ "bash",
511
+ "grep",
512
+ "glob",
513
+ "ls"
514
+ ];
515
+ var CLINE_NATIVE_TOOL_KINDS = {
516
+ read: "readonly",
517
+ write: "edit",
518
+ edit: "edit",
519
+ bash: "bash",
520
+ grep: "readonly",
521
+ glob: "readonly",
522
+ ls: "readonly"
523
+ };
524
+ function isClineBuiltinToolName(name) {
525
+ return CLINE_NATIVE_BUILTIN_NAMES.includes(name);
526
+ }
527
+ function resolveActiveClineBuiltinNames(toolFiltering) {
528
+ if (toolFiltering == null) return CLINE_NATIVE_BUILTIN_NAMES;
529
+ if (toolFiltering.mode === "allow") {
530
+ return CLINE_NATIVE_BUILTIN_NAMES.filter(
531
+ (name) => toolFiltering.toolNames.includes(name)
532
+ );
533
+ }
534
+ return CLINE_NATIVE_BUILTIN_NAMES.filter(
535
+ (name) => !toolFiltering.toolNames.includes(name)
536
+ );
537
+ }
538
+ var clineToolResultMarker = /* @__PURE__ */ Symbol("cline-tool-result");
539
+ function createClineToolResult({
540
+ output,
541
+ isError
542
+ }) {
543
+ return {
544
+ [clineToolResultMarker]: true,
545
+ output,
546
+ ...isError ? { isError: true } : {}
547
+ };
548
+ }
549
+ function unwrapClineToolResult(value) {
550
+ if (value == null || typeof value !== "object" || value[clineToolResultMarker] !== true) {
551
+ return void 0;
552
+ }
553
+ const result = value;
554
+ return {
555
+ output: result.output,
556
+ ...result.isError ? { isError: true } : {}
557
+ };
558
+ }
559
+ async function guarded(run) {
560
+ try {
561
+ return createClineToolResult({ output: await run() });
562
+ } catch (error) {
563
+ return createClineToolResult({
564
+ output: { error: getErrorText(error) },
565
+ isError: true
566
+ });
567
+ }
568
+ }
569
+ function buildBuiltinAgentTools({
570
+ ops,
571
+ activeNames
572
+ }) {
573
+ const tools = {
574
+ read: createTool({
575
+ name: "read",
576
+ description: "Read file contents.",
577
+ inputSchema: {
578
+ type: "object",
579
+ properties: {
580
+ file_path: {
581
+ type: "string",
582
+ description: "Path of the file to read."
583
+ }
584
+ },
585
+ required: ["file_path"]
586
+ },
587
+ execute: async (input) => guarded(() => ops.readFile(input.file_path))
588
+ }),
589
+ write: createTool({
590
+ name: "write",
591
+ description: "Overwrite or create a file.",
592
+ inputSchema: {
593
+ type: "object",
594
+ properties: {
595
+ file_path: {
596
+ type: "string",
597
+ description: "Path of the file to write."
598
+ },
599
+ content: {
600
+ type: "string",
601
+ description: "Full file content to write."
602
+ }
603
+ },
604
+ required: ["file_path", "content"]
605
+ },
606
+ execute: async (input) => guarded(async () => {
607
+ await ops.writeFile(input.file_path, input.content);
608
+ return `Wrote ${input.file_path}`;
609
+ })
610
+ }),
611
+ edit: createTool({
612
+ name: "edit",
613
+ description: "Edit a file by exact string replacement.",
614
+ inputSchema: {
615
+ type: "object",
616
+ properties: {
617
+ file_path: {
618
+ type: "string",
619
+ description: "Path of the file to edit."
620
+ },
621
+ old_string: { type: "string", description: "Exact text to replace." },
622
+ new_string: { type: "string", description: "Replacement text." }
623
+ },
624
+ required: ["file_path", "old_string", "new_string"]
625
+ },
626
+ execute: async (input) => guarded(async () => {
627
+ await ops.editFile(
628
+ input.file_path,
629
+ input.old_string,
630
+ input.new_string
631
+ );
632
+ return `Edited ${input.file_path}`;
633
+ })
634
+ }),
635
+ bash: createTool({
636
+ name: "bash",
637
+ description: "Execute a shell command in the sandbox workspace. Returns combined stdout/stderr and the exit code.",
638
+ inputSchema: {
639
+ type: "object",
640
+ properties: {
641
+ command: { type: "string", description: "Shell command to execute." },
642
+ timeout: {
643
+ type: "number",
644
+ description: "Optional timeout in seconds."
645
+ }
646
+ },
647
+ required: ["command"]
648
+ },
649
+ execute: async (input, context) => guarded(async () => {
650
+ const result = await ops.bash(input.command, {
651
+ ...typeof input.timeout === "number" ? { timeout: input.timeout } : {},
652
+ ...context.signal ? { signal: context.signal } : {}
653
+ });
654
+ return `${result.output}${result.exitCode != null ? `
655
+
656
+ (exit ${result.exitCode})` : ""}`.trim();
657
+ })
658
+ }),
659
+ grep: createTool({
660
+ name: "grep",
661
+ description: "Search file contents with regex.",
662
+ inputSchema: {
663
+ type: "object",
664
+ properties: {
665
+ pattern: {
666
+ type: "string",
667
+ description: "Regex pattern to search for."
668
+ },
669
+ path: { type: "string", description: "File or directory to search." },
670
+ glob: { type: "string", description: "Glob filter for file names." },
671
+ ignoreCase: { type: "boolean" },
672
+ literal: {
673
+ type: "boolean",
674
+ description: "Treat pattern as a literal string."
675
+ },
676
+ context: {
677
+ type: "number",
678
+ description: "Lines of context around matches."
679
+ },
680
+ limit: {
681
+ type: "number",
682
+ description: "Maximum matching lines to return."
683
+ }
684
+ },
685
+ required: ["pattern"]
686
+ },
687
+ execute: async (input) => guarded(() => ops.grep(input.pattern, input))
688
+ }),
689
+ glob: createTool({
690
+ name: "glob",
691
+ description: "Find files matching a glob pattern.",
692
+ inputSchema: {
693
+ type: "object",
694
+ properties: {
695
+ pattern: { type: "string", description: "Glob pattern to match." },
696
+ path: { type: "string", description: "Directory to search from." },
697
+ limit: { type: "number", description: "Maximum paths to return." }
698
+ },
699
+ required: ["pattern"]
700
+ },
701
+ execute: async (input) => guarded(async () => {
702
+ const matches = await ops.glob(
703
+ input.pattern,
704
+ input.path ?? ".",
705
+ input.limit ?? 1e3
706
+ );
707
+ return matches.length > 0 ? matches.join("\n") : "No files found";
708
+ })
709
+ }),
710
+ ls: createTool({
711
+ name: "ls",
712
+ description: "List directory entries.",
713
+ inputSchema: {
714
+ type: "object",
715
+ properties: {
716
+ path: { type: "string", description: "Directory to list." },
717
+ limit: { type: "number", description: "Maximum entries to return." }
718
+ }
719
+ },
720
+ execute: async (input) => guarded(async () => {
721
+ const entries = await ops.ls(input.path ?? ".", input.limit ?? 500);
722
+ return entries.join("\n");
723
+ })
724
+ })
725
+ };
726
+ return activeNames.map((name) => tools[name]).filter((tool2) => tool2 != null);
727
+ }
728
+ function buildUserAgentTools({
729
+ specs,
730
+ pendingToolResults
731
+ }) {
732
+ return specs.map(
733
+ (spec) => createTool({
734
+ name: spec.name,
735
+ description: spec.description ?? `User-registered tool ${spec.name}`,
736
+ inputSchema: spec.inputSchema ?? {
737
+ type: "object",
738
+ properties: {},
739
+ additionalProperties: true
740
+ },
741
+ execute: async (_input, context) => {
742
+ const toolCallId = context.toolCallId;
743
+ if (!toolCallId) {
744
+ return createClineToolResult({
745
+ output: { error: "Tool call is missing a toolCallId." },
746
+ isError: true
747
+ });
748
+ }
749
+ return new Promise((resolve) => {
750
+ pendingToolResults.set(toolCallId, { resolve });
751
+ context.signal?.addEventListener(
752
+ "abort",
753
+ () => {
754
+ if (pendingToolResults.delete(toolCallId)) {
755
+ resolve(
756
+ createClineToolResult({
757
+ output: {
758
+ error: "Turn was aborted before a tool result was submitted."
759
+ },
760
+ isError: true
761
+ })
762
+ );
763
+ }
764
+ },
765
+ { once: true }
766
+ );
767
+ });
768
+ }
769
+ })
770
+ );
771
+ }
772
+
773
+ // src/cline-translate.ts
774
+ function createClineTranslatorState({
775
+ builtinToolNames,
776
+ hostToolNames = [],
777
+ mcpToolNames = []
778
+ }) {
779
+ return {
780
+ builtinToolNames: new Set(builtinToolNames),
781
+ dynamicToolCallIds: /* @__PURE__ */ new Set(),
782
+ hostToolNames: new Set(hostToolNames),
783
+ mcpToolNames: new Set(mcpToolNames),
784
+ emittedToolCalls: /* @__PURE__ */ new Set(),
785
+ blockCounter: 0
786
+ };
787
+ }
788
+ function closeOpenBlocks(state) {
789
+ const parts = [];
790
+ if (state.openReasoningBlockId) {
791
+ parts.push({ type: "reasoning-end", id: state.openReasoningBlockId });
792
+ state.openReasoningBlockId = void 0;
793
+ }
794
+ if (state.openTextBlockId) {
795
+ parts.push({ type: "text-end", id: state.openTextBlockId });
796
+ state.openTextBlockId = void 0;
797
+ }
798
+ return parts;
799
+ }
800
+ function toolCallPart(state, toolCall) {
801
+ const isMcpTool = state.mcpToolNames.has(toolCall.toolName) && !state.hostToolNames.has(toolCall.toolName);
802
+ if (isMcpTool) {
803
+ state.dynamicToolCallIds.add(toolCall.toolCallId);
804
+ }
805
+ return {
806
+ type: "tool-call",
807
+ toolCallId: toolCall.toolCallId,
808
+ toolName: toolCall.toolName,
809
+ input: JSON.stringify(toolCall.input ?? {}),
810
+ /*
811
+ * Built-ins and external MCP tools are executed by the Cline runtime.
812
+ * Typed host tools wait for dispatch through `submitToolResult`, including
813
+ * when a host tool has the same name as a configured MCP tool.
814
+ */
815
+ ...state.builtinToolNames.has(toolCall.toolName) || isMcpTool ? { providerExecuted: true } : {},
816
+ ...isMcpTool ? { dynamic: true } : {}
817
+ };
818
+ }
819
+ function toolApprovalParts(state, toolCall) {
820
+ const parts = [];
821
+ if (!state.emittedToolCalls.has(toolCall.toolCallId)) {
822
+ state.emittedToolCalls.add(toolCall.toolCallId);
823
+ parts.push(toolCallPart(state, toolCall));
824
+ }
825
+ parts.push({
826
+ type: "tool-approval-request",
827
+ approvalId: toolCall.toolCallId,
828
+ toolCallId: toolCall.toolCallId
829
+ });
830
+ return parts;
831
+ }
832
+ function translateClineEvent(event, state) {
833
+ switch (event.type) {
834
+ case "assistant-reasoning-delta": {
835
+ const parts = [];
836
+ if (state.openTextBlockId) {
837
+ parts.push({ type: "text-end", id: state.openTextBlockId });
838
+ state.openTextBlockId = void 0;
839
+ }
840
+ if (!state.openReasoningBlockId) {
841
+ state.openReasoningBlockId = `reasoning-${++state.blockCounter}`;
842
+ parts.push({ type: "reasoning-start", id: state.openReasoningBlockId });
843
+ }
844
+ parts.push({
845
+ type: "reasoning-delta",
846
+ id: state.openReasoningBlockId,
847
+ delta: event.text
848
+ });
849
+ return parts;
850
+ }
851
+ case "assistant-text-delta": {
852
+ const parts = [];
853
+ if (state.openReasoningBlockId) {
854
+ parts.push({ type: "reasoning-end", id: state.openReasoningBlockId });
855
+ state.openReasoningBlockId = void 0;
856
+ }
857
+ if (!state.openTextBlockId) {
858
+ state.openTextBlockId = `text-${++state.blockCounter}`;
859
+ parts.push({ type: "text-start", id: state.openTextBlockId });
860
+ }
861
+ parts.push({
862
+ type: "text-delta",
863
+ id: state.openTextBlockId,
864
+ delta: event.text
865
+ });
866
+ return parts;
867
+ }
868
+ case "assistant-message": {
869
+ const parts = closeOpenBlocks(state);
870
+ state.pendingFinishStep = {
871
+ type: "finish-step",
872
+ finishReason: mapModelFinishReason(event.finishReason),
873
+ usage: usageFromMessageMetrics(event.message.metrics)
874
+ };
875
+ return parts;
876
+ }
877
+ case "turn-finished": {
878
+ const pendingFinishStep = state.pendingFinishStep;
879
+ if (!pendingFinishStep) {
880
+ return [];
881
+ }
882
+ state.pendingFinishStep = void 0;
883
+ return [pendingFinishStep];
884
+ }
885
+ case "tool-started": {
886
+ if (state.emittedToolCalls.has(event.toolCall.toolCallId)) {
887
+ return [];
888
+ }
889
+ state.emittedToolCalls.add(event.toolCall.toolCallId);
890
+ return [toolCallPart(state, event.toolCall)];
891
+ }
892
+ case "tool-finished": {
893
+ const resultPart = event.message.content.find(
894
+ (part) => part.type === "tool-result" && part.toolCallId === event.toolCall.toolCallId
895
+ );
896
+ const output = resultPart?.output;
897
+ const dynamic = state.dynamicToolCallIds.delete(
898
+ event.toolCall.toolCallId
899
+ );
900
+ return [
901
+ {
902
+ type: "tool-result",
903
+ toolCallId: event.toolCall.toolCallId,
904
+ toolName: event.toolCall.toolName,
905
+ result: toToolResultValue(output),
906
+ ...resultPart?.isError ? { isError: true } : {},
907
+ ...dynamic ? { dynamic: true } : {}
908
+ }
909
+ ];
910
+ }
911
+ case "tool-updated":
912
+ return [
913
+ {
914
+ type: "raw",
915
+ rawValue: {
916
+ type: "tool-updated",
917
+ toolCallId: event.toolCall.toolCallId,
918
+ toolName: event.toolCall.toolName,
919
+ update: event.update
920
+ }
921
+ }
922
+ ];
923
+ case "status-notice":
924
+ return [
925
+ {
926
+ type: "raw",
927
+ rawValue: {
928
+ type: "status-notice",
929
+ message: event.message,
930
+ ...event.metadata ? { metadata: event.metadata } : {}
931
+ }
932
+ }
933
+ ];
934
+ default:
935
+ return [];
936
+ }
937
+ }
938
+ function finishClineTranslation(state) {
939
+ const parts = closeOpenBlocks(state);
940
+ state.pendingFinishStep = void 0;
941
+ return parts;
942
+ }
943
+
944
+ // src/cline-session.ts
945
+ var HARNESS_ID2 = "cline";
946
+ var parkedClineSessions = /* @__PURE__ */ new Map();
947
+ function isAbortError(value) {
948
+ if (value == null) return false;
949
+ const name = value.name;
950
+ if (name === "AbortError" || name === "AgentRuntimeAbortError") {
951
+ return true;
952
+ }
953
+ const text = typeof value === "string" ? value : value instanceof Error ? value.message : String(value);
954
+ return /\baborted\b|AbortError/i.test(text);
955
+ }
956
+ function clineBuiltinToolRequiresApproval(input) {
957
+ if (input.permissionMode === "allow-all") return false;
958
+ if (input.permissionMode === "allow-edits") return input.kind === "bash";
959
+ return input.kind === "edit" || input.kind === "bash";
960
+ }
961
+ function buildSystemPrompt(input) {
962
+ const sections = [
963
+ "You are Cline, an autonomous coding agent operating inside a sandboxed workspace.",
964
+ `## Workspace
965
+
966
+ The 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.`,
967
+ `## Sandbox
968
+
969
+ ${input.sandboxDescription}`
970
+ ];
971
+ if (input.skillsSection) {
972
+ sections.push(input.skillsSection);
973
+ }
974
+ return sections.join("\n\n");
975
+ }
976
+ function toClineBackendProviderBaseUrl(baseUrl) {
977
+ return `${baseUrl.replace(/\/+$/, "")}/api/v1`;
978
+ }
979
+ function toAiGatewayProviderBaseUrl(baseUrl) {
980
+ const trimmed = baseUrl.replace(/\/+$/, "");
981
+ return trimmed.endsWith("/v1") ? trimmed : `${trimmed}/v1`;
982
+ }
983
+ function createClineAgentModel({
984
+ settings,
985
+ clientApp
986
+ }) {
987
+ const gatewayBaseUrl = settings.authEnv.AI_GATEWAY_BASE_URL;
988
+ const isAiGateway = gatewayBaseUrl != null;
989
+ const providerId = isAiGateway ? "cline" : settings.providerId ?? "cline";
990
+ const apiKey = isAiGateway ? settings.authEnv.AI_GATEWAY_API_KEY : settings.apiKey ?? (providerId === "cline" ? settings.authEnv.CLINE_API_KEY : void 0);
991
+ const baseUrl = isAiGateway ? toAiGatewayProviderBaseUrl(gatewayBaseUrl) : settings.baseUrl ?? (providerId === "cline" && settings.authEnv.CLINE_API_BASE_URL ? toClineBackendProviderBaseUrl(settings.authEnv.CLINE_API_BASE_URL) : void 0);
992
+ const headers = isAiGateway ? {
993
+ ...settings.headers,
994
+ "User-Agent": clientApp,
995
+ "x-client-app": clientApp
996
+ } : settings.headers;
997
+ const gateway = Llms.createGateway({
998
+ providerConfigs: [
999
+ {
1000
+ providerId,
1001
+ ...apiKey ? { apiKey } : {},
1002
+ ...baseUrl ? { baseUrl } : {},
1003
+ ...headers ? { headers } : {},
1004
+ ...isAiGateway ? { apiKeyEnv: [] } : {}
1005
+ }
1006
+ ]
1007
+ });
1008
+ const modelSelection = {
1009
+ providerId,
1010
+ ...settings.modelId ? { modelId: settings.modelId } : {}
1011
+ };
1012
+ const modelOptions = settings.reasoningEffort === "none" ? { reasoning: { enabled: false } } : {
1013
+ reasoning: {
1014
+ enabled: true,
1015
+ effort: settings.reasoningEffort
1016
+ }
1017
+ };
1018
+ const model = settings.reasoningEffort === void 0 ? gateway.createAgentModel(modelSelection) : gateway.createAgentModel(modelSelection, modelOptions);
1019
+ return {
1020
+ model,
1021
+ providerId
1022
+ };
1023
+ }
1024
+ async function createClineSession(input) {
1025
+ if (input.isResume) {
1026
+ const parkedSession = parkedClineSessions.get(input.sessionId);
1027
+ if (parkedSession) {
1028
+ parkedClineSessions.delete(input.sessionId);
1029
+ return {
1030
+ ...parkedSession,
1031
+ isResume: true
1032
+ };
1033
+ }
1034
+ }
1035
+ const sandbox = input.sandboxSession.restricted();
1036
+ const sandboxHomeDir = await resolveSandboxHomeDir({
1037
+ sandbox,
1038
+ ...input.abortSignal ? { abortSignal: input.abortSignal } : {}
1039
+ });
1040
+ const privateSessionDir = resolveClinePrivateSessionDirectory({
1041
+ sandboxHomeDir,
1042
+ sessionWorkDir: input.sessionWorkDir,
1043
+ sessionId: input.sessionId
1044
+ });
1045
+ const permissionMode = input.permissionMode ?? "allow-all";
1046
+ const activeBuiltinNames = resolveActiveClineBuiltinNames(
1047
+ input.builtinToolFiltering
1048
+ );
1049
+ const ops = createClineRemoteOps({
1050
+ sandbox,
1051
+ workDir: input.sessionWorkDir
1052
+ });
1053
+ let skillsSection;
1054
+ if (input.skills.length > 0) {
1055
+ const skillRootDir = await writeClineSkills({
1056
+ sandbox,
1057
+ sandboxHomeDir,
1058
+ skills: input.skills,
1059
+ ...input.abortSignal ? { abortSignal: input.abortSignal } : {}
1060
+ });
1061
+ if (skillRootDir) {
1062
+ skillsSection = renderSkillsPromptSection(input.skills, skillRootDir);
1063
+ }
1064
+ }
1065
+ const baseSystemPrompt = buildSystemPrompt({
1066
+ sessionWorkDir: input.sessionWorkDir,
1067
+ sandboxDescription: sandbox.description,
1068
+ ...skillsSection ? { skillsSection } : {}
1069
+ });
1070
+ let currentMessages = [];
1071
+ if (input.isResume && input.resumeHistoryFileName) {
1072
+ const restored = await pullHistoryFromSandbox({
1073
+ sandbox,
1074
+ privateSessionDir,
1075
+ historyFileName: safeClineHistoryFileName(input.resumeHistoryFileName),
1076
+ ...input.abortSignal ? { abortSignal: input.abortSignal } : {}
1077
+ });
1078
+ if (restored) {
1079
+ currentMessages = restored;
1080
+ }
1081
+ }
1082
+ const mcpRuntime = await createClineMcpRuntime({
1083
+ mcpServers: input.settings.mcpServers
1084
+ });
1085
+ const { model, providerId } = createClineAgentModel({
1086
+ settings: input.settings,
1087
+ clientApp: input.clientApp
1088
+ });
1089
+ let agent;
1090
+ let unsubscribe;
1091
+ let lastToolsSignature;
1092
+ let agentHasRun = false;
1093
+ let stopped = false;
1094
+ let suspending = false;
1095
+ const pendingToolResults = /* @__PURE__ */ new Map();
1096
+ const pendingToolApprovals = /* @__PURE__ */ new Map();
1097
+ const pendingUserMessages = [];
1098
+ let currentEmit;
1099
+ let translatorState;
1100
+ let activeTurn;
1101
+ function settlePendingToolResults(reason) {
1102
+ for (const pending of pendingToolResults.values()) {
1103
+ pending.resolve(
1104
+ createClineToolResult({
1105
+ output: { error: reason },
1106
+ isError: true
1107
+ })
1108
+ );
1109
+ }
1110
+ pendingToolResults.clear();
1111
+ }
1112
+ function settlePendingToolApprovals(reason) {
1113
+ for (const pending of pendingToolApprovals.values()) {
1114
+ pending.resolve({ approved: false, reason });
1115
+ }
1116
+ pendingToolApprovals.clear();
1117
+ }
1118
+ async function persistHistory() {
1119
+ const messages = agent?.snapshot().messages ?? currentMessages;
1120
+ await persistHistoryToSandbox({
1121
+ sandbox,
1122
+ privateSessionDir,
1123
+ historyFileName: CLINE_DEFAULT_HISTORY_FILE_NAME,
1124
+ messages
1125
+ });
1126
+ }
1127
+ function requestBuiltinToolApproval(toolCall) {
1128
+ if (translatorState && currentEmit) {
1129
+ for (const part of toolApprovalParts(translatorState, toolCall)) {
1130
+ currentEmit(part);
1131
+ }
1132
+ }
1133
+ return new Promise((resolve) => {
1134
+ pendingToolApprovals.set(toolCall.toolCallId, { resolve });
1135
+ });
1136
+ }
1137
+ function rebuildAgent(rebuildInput) {
1138
+ unsubscribe?.();
1139
+ unsubscribe = void 0;
1140
+ agent = new Agent({
1141
+ model,
1142
+ ...input.settings.modelId ? {
1143
+ messageModelInfo: {
1144
+ id: input.settings.modelId,
1145
+ provider: providerId
1146
+ }
1147
+ } : {},
1148
+ sessionId: input.sessionId,
1149
+ systemPrompt: rebuildInput.instructions ? `${baseSystemPrompt}
1150
+
1151
+ ${rebuildInput.instructions}` : baseSystemPrompt,
1152
+ initialMessages: currentMessages,
1153
+ toolExecution: "parallel",
1154
+ ...input.settings.maxIterations !== void 0 ? { maxIterations: input.settings.maxIterations } : {},
1155
+ tools: [
1156
+ ...buildBuiltinAgentTools({ ops, activeNames: activeBuiltinNames }),
1157
+ ...mcpRuntime.tools,
1158
+ ...buildUserAgentTools({
1159
+ specs: rebuildInput.userTools,
1160
+ pendingToolResults
1161
+ })
1162
+ ],
1163
+ hooks: {
1164
+ afterTool: ({ result }) => {
1165
+ const toolResult = unwrapClineToolResult(result.output);
1166
+ return toolResult == null ? void 0 : { result: toolResult };
1167
+ }
1168
+ },
1169
+ // Built-in tools whose kind requires approval under the session's
1170
+ // permission mode are marked `autoApprove: false`; the runtime then
1171
+ // routes them through `requestToolApproval` below. User tools are
1172
+ // approved by the harness framework, never by the adapter.
1173
+ toolPolicies: Object.fromEntries(
1174
+ activeBuiltinNames.filter(
1175
+ (name) => clineBuiltinToolRequiresApproval({
1176
+ permissionMode,
1177
+ kind: CLINE_NATIVE_TOOL_KINDS[name]
1178
+ })
1179
+ ).map((name) => [name, { autoApprove: false }])
1180
+ ),
1181
+ requestToolApproval: (request) => {
1182
+ if (!isClineBuiltinToolName(request.toolName)) {
1183
+ return { approved: true };
1184
+ }
1185
+ return requestBuiltinToolApproval({
1186
+ toolCallId: request.toolCallId,
1187
+ toolName: request.toolName,
1188
+ input: request.input
1189
+ });
1190
+ },
1191
+ // Mid-turn user messages injected via `submitUserMessage` are consumed
1192
+ // by the runtime between loop iterations, before the next model call.
1193
+ consumePendingUserMessage: () => pendingUserMessages.shift()
1194
+ });
1195
+ agentHasRun = false;
1196
+ unsubscribe = agent.subscribe((event) => {
1197
+ if (!translatorState || !currentEmit) return;
1198
+ for (const part of translateClineEvent(event, translatorState)) {
1199
+ currentEmit(part);
1200
+ }
1201
+ });
1202
+ }
1203
+ function createPromptControl(controlInput) {
1204
+ const abortHandler = () => {
1205
+ settlePendingToolResults("Turn aborted");
1206
+ settlePendingToolApprovals("Turn aborted");
1207
+ agent?.abort("Turn aborted by caller");
1208
+ };
1209
+ if (controlInput.abortSignal) {
1210
+ controlInput.abortSignal.addEventListener("abort", abortHandler, {
1211
+ once: true
1212
+ });
1213
+ void controlInput.done.then(
1214
+ () => {
1215
+ controlInput.abortSignal?.removeEventListener("abort", abortHandler);
1216
+ },
1217
+ () => {
1218
+ controlInput.abortSignal?.removeEventListener("abort", abortHandler);
1219
+ }
1220
+ );
1221
+ }
1222
+ return {
1223
+ async submitToolResult(args) {
1224
+ const pending = pendingToolResults.get(args.toolCallId);
1225
+ if (!pending) return;
1226
+ pendingToolResults.delete(args.toolCallId);
1227
+ pending.resolve(
1228
+ createClineToolResult({
1229
+ output: args.output,
1230
+ isError: args.isError
1231
+ })
1232
+ );
1233
+ },
1234
+ async submitToolApproval(args) {
1235
+ const pending = pendingToolApprovals.get(args.approvalId);
1236
+ if (!pending) return;
1237
+ pendingToolApprovals.delete(args.approvalId);
1238
+ pending.resolve({
1239
+ approved: args.approved,
1240
+ ...args.reason !== void 0 ? { reason: args.reason } : {}
1241
+ });
1242
+ },
1243
+ async submitUserMessage(text) {
1244
+ pendingUserMessages.push(text);
1245
+ },
1246
+ done: controlInput.done
1247
+ };
1248
+ }
1249
+ function runTurn(turnOpts) {
1250
+ if (stopped) {
1251
+ throw new Error("Cline session has been stopped.");
1252
+ }
1253
+ const userTools = turnOpts.tools;
1254
+ const signature = JSON.stringify(userTools.map((t) => t.name).sort());
1255
+ if (agent == null || signature !== lastToolsSignature) {
1256
+ currentMessages = agent?.snapshot().messages ?? currentMessages;
1257
+ rebuildAgent({
1258
+ userTools,
1259
+ ...turnOpts.instructions ? { instructions: turnOpts.instructions } : {}
1260
+ });
1261
+ lastToolsSignature = signature;
1262
+ }
1263
+ currentEmit = turnOpts.emit;
1264
+ translatorState = createClineTranslatorState({
1265
+ builtinToolNames: activeBuiltinNames,
1266
+ hostToolNames: userTools.map((tool2) => tool2.name),
1267
+ mcpToolNames: mcpRuntime.toolNames
1268
+ });
1269
+ turnOpts.emit({
1270
+ type: "stream-start",
1271
+ ...input.settings.modelId ? { modelId: input.settings.modelId } : {}
1272
+ });
1273
+ const turnPromise = (async () => {
1274
+ const runtime = agent;
1275
+ let result;
1276
+ try {
1277
+ result = turnOpts.text === void 0 ? await runtime.continue() : agentHasRun ? await runtime.continue(turnOpts.text) : await runtime.run(turnOpts.text);
1278
+ agentHasRun = true;
1279
+ } catch (error) {
1280
+ if (suspending && isAbortError(error)) return;
1281
+ currentEmit?.({ type: "error", error });
1282
+ return;
1283
+ }
1284
+ currentMessages = result.messages;
1285
+ if (translatorState) {
1286
+ for (const part of finishClineTranslation(translatorState)) {
1287
+ currentEmit?.(part);
1288
+ }
1289
+ }
1290
+ if (result.status === "aborted") {
1291
+ if (suspending) return;
1292
+ currentEmit?.({
1293
+ type: "finish",
1294
+ finishReason: mapRunFinishReason(result.status),
1295
+ totalUsage: usageFromAgentUsage(result.usage)
1296
+ });
1297
+ return;
1298
+ }
1299
+ if (result.status === "failed") {
1300
+ currentEmit?.({
1301
+ type: "error",
1302
+ error: result.error ?? new Error("Cline agent run failed")
1303
+ });
1304
+ return;
1305
+ }
1306
+ currentEmit?.({
1307
+ type: "finish",
1308
+ finishReason: mapRunFinishReason(result.status),
1309
+ totalUsage: usageFromAgentUsage(result.usage)
1310
+ });
1311
+ })();
1312
+ const activeTurnToken = {};
1313
+ const done = turnPromise.finally(() => {
1314
+ if (activeTurn?.token === activeTurnToken) {
1315
+ activeTurn = void 0;
1316
+ }
1317
+ currentEmit = void 0;
1318
+ });
1319
+ activeTurn = {
1320
+ token: activeTurnToken,
1321
+ done
1322
+ };
1323
+ return createPromptControl({
1324
+ done,
1325
+ ...turnOpts.abortSignal ? { abortSignal: turnOpts.abortSignal } : {}
1326
+ });
1327
+ }
1328
+ async function teardown() {
1329
+ unsubscribe?.();
1330
+ unsubscribe = void 0;
1331
+ agent = void 0;
1332
+ await mcpRuntime.dispose();
1333
+ }
1334
+ const doStop = async () => {
1335
+ if (stopped) {
1336
+ throw new Error("Cline session has been stopped.");
1337
+ }
1338
+ stopped = true;
1339
+ parkedClineSessions.delete(input.sessionId);
1340
+ settlePendingToolResults("Cline session stopped");
1341
+ settlePendingToolApprovals("Cline session stopped");
1342
+ try {
1343
+ await persistHistory();
1344
+ } catch {
1345
+ }
1346
+ agent?.abort("Cline session stopped");
1347
+ await teardown();
1348
+ return {
1349
+ type: "resume-session",
1350
+ harnessId: HARNESS_ID2,
1351
+ specificationVersion: "harness-v1",
1352
+ data: { historyFileName: CLINE_DEFAULT_HISTORY_FILE_NAME }
1353
+ };
1354
+ };
1355
+ const sessionImpl = {
1356
+ sessionId: input.sessionId,
1357
+ isResume: input.isResume,
1358
+ ...input.settings.modelId ? { modelId: input.settings.modelId } : {},
1359
+ // The Cline runtime has no bridge to attach to and no in-sandbox event
1360
+ // log to replay; its only cross-process resume path is restoring the
1361
+ // persisted history on a fresh runtime, i.e. `rerun`.
1362
+ doPromptTurn: async (promptOpts) => {
1363
+ return runTurn({
1364
+ text: extractUserText(promptOpts.prompt),
1365
+ tools: promptOpts.tools ?? [],
1366
+ ...promptOpts.instructions ? { instructions: promptOpts.instructions } : {},
1367
+ emit: promptOpts.emit,
1368
+ ...promptOpts.abortSignal ? { abortSignal: promptOpts.abortSignal } : {}
1369
+ });
1370
+ },
1371
+ doContinueTurn: async (continueOpts) => {
1372
+ if (activeTurn != null) {
1373
+ currentEmit = continueOpts.emit;
1374
+ return createPromptControl({
1375
+ done: activeTurn.done,
1376
+ ...continueOpts.abortSignal ? { abortSignal: continueOpts.abortSignal } : {}
1377
+ });
1378
+ }
1379
+ return runTurn({
1380
+ text: void 0,
1381
+ tools: continueOpts.tools ?? [],
1382
+ ...continueOpts.instructions ? { instructions: continueOpts.instructions } : {},
1383
+ emit: continueOpts.emit,
1384
+ ...continueOpts.abortSignal ? { abortSignal: continueOpts.abortSignal } : {}
1385
+ });
1386
+ },
1387
+ doCompact: async () => {
1388
+ throw new HarnessCapabilityUnsupportedError2({
1389
+ message: "cline: manual compaction is not supported by the standalone Cline runtime.",
1390
+ harnessId: HARNESS_ID2
1391
+ });
1392
+ },
1393
+ doDestroy: async () => {
1394
+ if (stopped) return;
1395
+ stopped = true;
1396
+ parkedClineSessions.delete(input.sessionId);
1397
+ settlePendingToolResults("Cline session stopped");
1398
+ settlePendingToolApprovals("Cline session stopped");
1399
+ agent?.abort("Cline session destroyed");
1400
+ await teardown();
1401
+ },
1402
+ doStop,
1403
+ doDetach: async () => {
1404
+ if (activeTurn != null || pendingToolResults.size > 0) {
1405
+ parkedClineSessions.set(input.sessionId, sessionImpl);
1406
+ try {
1407
+ await persistHistory();
1408
+ } catch {
1409
+ }
1410
+ return {
1411
+ type: "resume-session",
1412
+ harnessId: HARNESS_ID2,
1413
+ specificationVersion: "harness-v1",
1414
+ data: { historyFileName: CLINE_DEFAULT_HISTORY_FILE_NAME }
1415
+ };
1416
+ }
1417
+ return doStop();
1418
+ },
1419
+ doSuspendTurn: async () => {
1420
+ if (stopped) {
1421
+ throw new Error("Cline session has been stopped.");
1422
+ }
1423
+ if (activeTurn != null && (pendingToolResults.size > 0 || pendingToolApprovals.size > 0)) {
1424
+ parkedClineSessions.set(input.sessionId, sessionImpl);
1425
+ try {
1426
+ await persistHistory();
1427
+ } catch {
1428
+ }
1429
+ return {
1430
+ type: "continue-turn",
1431
+ harnessId: HARNESS_ID2,
1432
+ specificationVersion: "harness-v1",
1433
+ data: { historyFileName: CLINE_DEFAULT_HISTORY_FILE_NAME }
1434
+ };
1435
+ }
1436
+ suspending = true;
1437
+ agent?.abort("Cline session suspended");
1438
+ if (activeTurn) {
1439
+ await activeTurn.done.catch(() => {
1440
+ });
1441
+ }
1442
+ try {
1443
+ await persistHistory();
1444
+ } catch {
1445
+ }
1446
+ stopped = true;
1447
+ parkedClineSessions.delete(input.sessionId);
1448
+ settlePendingToolResults("Cline session suspended");
1449
+ settlePendingToolApprovals("Cline session suspended");
1450
+ await teardown();
1451
+ return {
1452
+ type: "continue-turn",
1453
+ harnessId: HARNESS_ID2,
1454
+ specificationVersion: "harness-v1",
1455
+ data: { historyFileName: CLINE_DEFAULT_HISTORY_FILE_NAME }
1456
+ };
1457
+ }
1458
+ };
1459
+ return sessionImpl;
1460
+ }
1461
+
1462
+ // src/version.ts
1463
+ var VERSION = true ? "1.0.0" : "0.0.0-test";
1464
+
1465
+ // src/cline-harness.ts
1466
+ var CLINE_CLIENT_APP = `ai-sdk/harness-cline/${VERSION}`;
1467
+ var CLINE_BUILTIN_TOOLS = {
1468
+ read: commonTool("read", {
1469
+ nativeName: "read",
1470
+ toolUseKind: "readonly",
1471
+ description: "Read file contents.",
1472
+ inputSchema: z2.object({
1473
+ file_path: z2.string()
1474
+ })
1475
+ }),
1476
+ write: commonTool("write", {
1477
+ nativeName: "write",
1478
+ toolUseKind: "edit",
1479
+ description: "Overwrite or create a file.",
1480
+ inputSchema: z2.object({
1481
+ file_path: z2.string(),
1482
+ content: z2.string()
1483
+ })
1484
+ }),
1485
+ edit: commonTool("edit", {
1486
+ nativeName: "edit",
1487
+ toolUseKind: "edit",
1488
+ description: "Edit a file by exact string replacement.",
1489
+ inputSchema: z2.object({
1490
+ file_path: z2.string(),
1491
+ old_string: z2.string(),
1492
+ new_string: z2.string()
1493
+ })
1494
+ }),
1495
+ bash: commonTool("bash", {
1496
+ nativeName: "bash",
1497
+ toolUseKind: "bash",
1498
+ description: "Execute a shell command in the sandbox.",
1499
+ inputSchema: z2.object({
1500
+ command: z2.string(),
1501
+ timeout: z2.number().optional()
1502
+ })
1503
+ }),
1504
+ grep: commonTool("grep", {
1505
+ nativeName: "grep",
1506
+ toolUseKind: "readonly",
1507
+ description: "Search file contents with regex.",
1508
+ inputSchema: z2.object({
1509
+ pattern: z2.string(),
1510
+ path: z2.string().optional(),
1511
+ glob: z2.string().optional(),
1512
+ ignoreCase: z2.boolean().optional(),
1513
+ literal: z2.boolean().optional(),
1514
+ context: z2.number().optional(),
1515
+ limit: z2.number().optional()
1516
+ })
1517
+ }),
1518
+ glob: commonTool("glob", {
1519
+ nativeName: "glob",
1520
+ toolUseKind: "readonly",
1521
+ description: "Find files matching a glob pattern.",
1522
+ inputSchema: z2.object({
1523
+ pattern: z2.string(),
1524
+ path: z2.string().optional(),
1525
+ limit: z2.number().optional()
1526
+ })
1527
+ }),
1528
+ ls: {
1529
+ ...tool({
1530
+ description: "List directory entries.",
1531
+ inputSchema: z2.object({
1532
+ path: z2.string().optional(),
1533
+ limit: z2.number().optional()
1534
+ }),
1535
+ outputSchema: z2.unknown()
1536
+ }),
1537
+ nativeName: "ls",
1538
+ toolUseKind: "readonly"
1539
+ }
1540
+ };
1541
+ function createCline(settings = {}) {
1542
+ return {
1543
+ specificationVersion: "harness-v1",
1544
+ harnessId: "cline",
1545
+ builtinTools: CLINE_BUILTIN_TOOLS,
1546
+ supportsBuiltinToolApprovals: true,
1547
+ supportsBuiltinToolFiltering: true,
1548
+ lifecycleStateSchema: clineResumeStateSchema,
1549
+ doStart: async (startOpts) => {
1550
+ const authEnv = resolveClineEnv({ auth: settings.auth });
1551
+ const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;
1552
+ const resumeData = lifecycleState?.data;
1553
+ return createClineSession({
1554
+ sessionId: startOpts.sessionId,
1555
+ sandboxSession: startOpts.sandboxSession,
1556
+ sessionWorkDir: startOpts.sessionWorkDir,
1557
+ skills: startOpts.skills ?? [],
1558
+ settings: {
1559
+ authEnv,
1560
+ ...settings.mcpServers ? { mcpServers: settings.mcpServers } : {},
1561
+ ...settings.providerId ? { providerId: settings.providerId } : {},
1562
+ ...settings.modelId ? { modelId: settings.modelId } : {},
1563
+ ...settings.apiKey ? { apiKey: settings.apiKey } : {},
1564
+ ...settings.baseUrl ? { baseUrl: settings.baseUrl } : {},
1565
+ ...settings.headers ? { headers: settings.headers } : {},
1566
+ ...settings.reasoningEffort !== void 0 ? { reasoningEffort: settings.reasoningEffort } : {},
1567
+ ...settings.maxIterations !== void 0 ? { maxIterations: settings.maxIterations } : {}
1568
+ },
1569
+ clientApp: CLINE_CLIENT_APP,
1570
+ isResume: lifecycleState != null,
1571
+ ...startOpts.permissionMode ? { permissionMode: startOpts.permissionMode } : {},
1572
+ ...startOpts.builtinToolFiltering ? { builtinToolFiltering: startOpts.builtinToolFiltering } : {},
1573
+ ...resumeData?.historyFileName ? { resumeHistoryFileName: resumeData.historyFileName } : {},
1574
+ ...startOpts.abortSignal ? { abortSignal: startOpts.abortSignal } : {}
1575
+ });
1576
+ }
1577
+ };
1578
+ }
1579
+
1580
+ // src/index.ts
1581
+ var cline = createCline();
1582
+ export {
1583
+ VERSION,
1584
+ cline,
1585
+ createCline
1586
+ };
1587
+ //# sourceMappingURL=index.js.map