@ai-sdk/harness-cline 1.0.18 → 1.0.20

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 CHANGED
@@ -1,5 +1,29 @@
1
1
  # @ai-sdk/harness-cline
2
2
 
3
+ ## 1.0.20
4
+
5
+ ### Patch Changes
6
+
7
+ - 7608210: feat(harness): add `model` parameter to `HarnessAgent` instead of having each harness adapter support it on their own constructor functions
8
+ - f7bd978: chore(harness): update underlying adapter SDKs to their latest versions
9
+ - 14d4fc0: feat(harness): allow changing harness settings between turns via `prepareCall()` support on `HarnessAgent`
10
+ - 5021768: fix(harness-cline): use native `skills` tool instead of manual file-based workaround
11
+ - Updated dependencies [cc9f6ce]
12
+ - Updated dependencies [7608210]
13
+ - Updated dependencies [6f8a2d7]
14
+ - Updated dependencies [14d4fc0]
15
+ - Updated dependencies [90192f1]
16
+ - @ai-sdk/harness@1.0.93
17
+ - @ai-sdk/provider-utils@5.0.33
18
+
19
+ ## 1.0.19
20
+
21
+ ### Patch Changes
22
+
23
+ - e0d7cfb: feat(harness): allow harness sessions to optionally authenticate from an isolated environment supplied through the `auth` option, and remove support for the formerly deprecated legacy auth options types
24
+ - Updated dependencies [e0d7cfb]
25
+ - @ai-sdk/harness@1.0.92
26
+
3
27
  ## 1.0.18
4
28
 
5
29
  ### Patch Changes
package/README.md CHANGED
@@ -53,6 +53,6 @@ The adapter requires a `HarnessV1SandboxProvider`. Cline has no in-sandbox bridg
53
53
 
54
54
  ## Authentication
55
55
 
56
- The default `auto` authentication mode uses `AI_GATEWAY_API_KEY` or `VERCEL_OIDC_TOKEN` when available and otherwise falls back to `CLINE_API_KEY`. Use `createCline({ auth: 'ai-gateway' })` or `createCline({ auth: 'direct' })` to select a mode explicitly. `AI_GATEWAY_BASE_URL` and `CLINE_API_BASE_URL` configure their respective endpoints.
56
+ The default `auto` authentication mode uses `AI_GATEWAY_API_KEY` or `VERCEL_OIDC_TOKEN` when available and otherwise falls back to `CLINE_API_KEY`. Use `createCline({ auth: 'ai-gateway' })` or `createCline({ auth: 'direct' })` to select a mode explicitly, or pass an isolated authentication environment with `createCline({ auth: { CLINE_API_KEY: token } })`. `AI_GATEWAY_BASE_URL` and `CLINE_API_BASE_URL` configure their respective endpoints.
57
57
 
58
58
  See the [Cline harness documentation](https://ai-sdk.dev/providers/ai-sdk-harnesses/cline) for model configuration, reasoning, tools, skills, MCP, and session lifecycle details.
package/dist/index.d.ts CHANGED
@@ -1,8 +1,7 @@
1
1
  import * as _ai_sdk_harness from '@ai-sdk/harness';
2
- import { HarnessV1, HarnessV1BuiltinTool } from '@ai-sdk/harness';
2
+ import { HarnessV1Authentication, HarnessV1, HarnessV1BuiltinTool } from '@ai-sdk/harness';
3
3
 
4
- type ClineAuthenticationMode = 'auto' | 'direct' | 'ai-gateway';
5
- type ClineAuthOptions = ClineAuthenticationMode;
4
+ type ClineAuthenticationMode = HarnessV1Authentication;
6
5
 
7
6
  type ClineReasoningEffort = 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xhigh' | 'max';
8
7
 
@@ -13,7 +12,7 @@ type ClineReasoningEffort = 'none' | 'minimal' | 'low' | 'medium' | 'high' | 'xh
13
12
  */
14
13
  type ClineHarnessSettings = {
15
14
  /** Where Cline sources direct or AI Gateway credentials from. */
16
- readonly auth?: ClineAuthOptions;
15
+ readonly auth?: ClineAuthenticationMode;
17
16
  /**
18
17
  * MCP server definitions keyed by server name. Each definition uses the
19
18
  * underlying runtime's native MCP server configuration format.
@@ -27,11 +26,14 @@ type ClineHarnessSettings = {
27
26
  /**
28
27
  * Model id for the configured provider. When omitted, Cline selects the
29
28
  * provider's default model.
29
+ *
30
+ * @deprecated Use `model` on `HarnessAgent` instead.
30
31
  */
31
32
  readonly modelId?: string;
32
33
  /**
33
34
  * Provider API key. When omitted, the Cline gateway falls back to the
34
35
  * provider's environment variable (e.g. `ANTHROPIC_API_KEY`).
36
+ * A record-shaped authentication override disables that fallback.
35
37
  */
36
38
  readonly apiKey?: string;
37
39
  /** Custom provider endpoint. */
@@ -80,6 +82,7 @@ declare const CLINE_BUILTIN_TOOLS: {
80
82
  limit?: number | undefined;
81
83
  }, unknown>;
82
84
  readonly ls: HarnessV1BuiltinTool;
85
+ readonly skills: HarnessV1BuiltinTool;
83
86
  };
84
87
  declare function createCline(settings?: ClineHarnessSettings): HarnessV1<typeof CLINE_BUILTIN_TOOLS>;
85
88
 
@@ -122,6 +125,7 @@ declare const cline: _ai_sdk_harness.HarnessV1<{
122
125
  limit?: number | undefined;
123
126
  }, unknown>;
124
127
  readonly ls: _ai_sdk_harness.HarnessV1BuiltinTool;
128
+ readonly skills: _ai_sdk_harness.HarnessV1BuiltinTool;
125
129
  }>;
126
130
 
127
- export { type ClineAuthOptions, type ClineAuthenticationMode, type ClineHarnessSettings, VERSION, cline, createCline };
131
+ export { type ClineAuthenticationMode, type ClineHarnessSettings, VERSION, cline, createCline };
package/dist/index.js CHANGED
@@ -2,27 +2,38 @@
2
2
  import {
3
3
  commonTool
4
4
  } from "@ai-sdk/harness";
5
+ import { isHarnessAuthenticationEnvironment as isHarnessAuthenticationEnvironment2 } from "@ai-sdk/harness/utils";
5
6
  import { tool } from "@ai-sdk/provider-utils";
6
7
  import { z as z2 } from "zod/v4";
7
8
 
8
9
  // src/cline-auth.ts
9
- import { getAiGatewayAuthFromEnv } from "@ai-sdk/harness/utils";
10
+ import {
11
+ getAiGatewayAuthFromEnv,
12
+ isHarnessAuthenticationEnvironment
13
+ } from "@ai-sdk/harness/utils";
10
14
  function resolveClineEnv({
11
15
  auth = "auto",
12
16
  env = process.env
13
17
  }) {
18
+ if (isHarnessAuthenticationEnvironment(auth)) {
19
+ const gatewayAuth2 = getAiGatewayAuthFromEnv({ env: auth });
20
+ return gatewayAuth2.apiKey ? toGatewayClineEnv(gatewayAuth2) : pickDirectClineEnv({ env: auth });
21
+ }
14
22
  if (auth === "direct") {
15
23
  return pickDirectClineEnv({ env });
16
24
  }
17
25
  const gatewayAuth = getAiGatewayAuthFromEnv({ env });
18
26
  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
- };
27
+ return toGatewayClineEnv(gatewayAuth);
23
28
  }
24
29
  return pickDirectClineEnv({ env });
25
30
  }
31
+ function toGatewayClineEnv(gatewayAuth) {
32
+ return {
33
+ ...gatewayAuth.apiKey ? { AI_GATEWAY_API_KEY: gatewayAuth.apiKey } : {},
34
+ AI_GATEWAY_BASE_URL: gatewayAuth.baseUrl
35
+ };
36
+ }
26
37
  function pickDirectClineEnv({
27
38
  env
28
39
  }) {
@@ -230,7 +241,6 @@ import { shellQuote } from "@ai-sdk/harness/utils";
230
241
  function createClineRemoteOps(options) {
231
242
  const { sandbox, workDir } = options;
232
243
  const normalizedWorkDir = path2.posix.normalize(workDir);
233
- const readableRoots = options.readableRoots?.map((root) => path2.posix.normalize(root.sandboxDir)) ?? [];
234
244
  const isInsidePath = ({
235
245
  parent,
236
246
  candidate
@@ -245,25 +255,10 @@ function createClineRemoteOps(options) {
245
255
  }
246
256
  return normalized;
247
257
  };
248
- const assertReadablePath = (inputPath) => {
249
- const normalized = path2.posix.normalize(inputPath);
250
- if (!isInsidePath({ parent: normalizedWorkDir, candidate: normalized }) && !readableRoots.some(
251
- (parent) => isInsidePath({ parent, candidate: normalized })
252
- )) {
253
- throw new Error(`Cline path escapes the readable roots: ${inputPath}`);
254
- }
255
- return normalized;
256
- };
257
258
  const resolvePath = (inputPath) => {
258
259
  const resolved = path2.posix.isAbsolute(inputPath) ? path2.posix.normalize(inputPath) : path2.posix.normalize(path2.posix.join(normalizedWorkDir, inputPath));
259
260
  return assertWorkspacePath(resolved);
260
261
  };
261
- const resolveReadablePath = (inputPath) => {
262
- if (!path2.posix.isAbsolute(inputPath)) {
263
- return resolvePath(inputPath);
264
- }
265
- return assertReadablePath(inputPath);
266
- };
267
262
  const runShell = async ({
268
263
  command,
269
264
  signal
@@ -308,7 +303,7 @@ function createClineRemoteOps(options) {
308
303
  remotePath,
309
304
  inputPath,
310
305
  missingMessage
311
- }) => assertReadablePath(
306
+ }) => assertWorkspacePath(
312
307
  await resolveExistingSandboxPath({
313
308
  remotePath,
314
309
  inputPath,
@@ -341,7 +336,7 @@ function createClineRemoteOps(options) {
341
336
  return assertWorkspacePath(resolvedPath);
342
337
  };
343
338
  const readFile = async (inputPath) => {
344
- const remotePath = resolveReadablePath(inputPath);
339
+ const remotePath = resolvePath(inputPath);
345
340
  const resolved = await resolveReadableSandboxPath({
346
341
  remotePath,
347
342
  inputPath,
@@ -409,7 +404,7 @@ function createClineRemoteOps(options) {
409
404
  },
410
405
  async grep(pattern, input = {}) {
411
406
  const inputPath = input.path ?? ".";
412
- const remotePath = resolveReadablePath(inputPath);
407
+ const remotePath = resolvePath(inputPath);
413
408
  const target = await resolveReadableSandboxPath({
414
409
  remotePath,
415
410
  inputPath
@@ -441,7 +436,7 @@ function createClineRemoteOps(options) {
441
436
  return output || "No matches found";
442
437
  },
443
438
  async glob(pattern, inputPath = ".", limit = 1e3) {
444
- const remotePath = resolveReadablePath(inputPath);
439
+ const remotePath = resolvePath(inputPath);
445
440
  const target = await resolveReadableSandboxPath({
446
441
  remotePath,
447
442
  inputPath
@@ -472,7 +467,7 @@ function createClineRemoteOps(options) {
472
467
  ).slice(0, limit);
473
468
  },
474
469
  async ls(inputPath = ".", limit = 500) {
475
- const remotePath = resolveReadablePath(inputPath);
470
+ const remotePath = resolvePath(inputPath);
476
471
  const target = await resolveReadableSandboxPath({
477
472
  remotePath,
478
473
  inputPath
@@ -505,33 +500,123 @@ function createClineRemoteOps(options) {
505
500
 
506
501
  // src/cline-skills.ts
507
502
  import path3 from "path";
508
- import { writeSkills } from "@ai-sdk/harness/utils";
509
- async function writeClineSkills({
510
- sandbox,
511
- sandboxHomeDir,
512
- skills,
513
- abortSignal
503
+ import { createDefaultTools } from "@cline/core";
504
+ var CLINE_SKILL_NAME_PATTERN = /^[A-Za-z0-9._-]+$/;
505
+ function createClineSkillsRuntime({
506
+ skills
514
507
  }) {
515
- if (skills.length === 0) return void 0;
516
- const skillRootDir = path3.posix.join(sandboxHomeDir, ".agents", "skills");
517
- await writeSkills({
518
- sandbox,
519
- rootDir: skillRootDir,
520
- skills,
521
- ...abortSignal ? { abortSignal } : {}
522
- });
523
- return skillRootDir;
508
+ const projectedSkills = projectClineSkills({ skills });
509
+ const signature = JSON.stringify(projectedSkills);
510
+ if (projectedSkills.length === 0) {
511
+ return { signature };
512
+ }
513
+ const executor = async (...executorArguments) => {
514
+ const [requestedSkill, args] = executorArguments;
515
+ const skill = projectedSkills.find(
516
+ (candidate) => candidate.id === normalizeSkillToken(requestedSkill)
517
+ );
518
+ if (skill == null) {
519
+ const availableSkills = projectedSkills.map((skill2) => skill2.name);
520
+ return availableSkills.length > 0 ? `Skill "${requestedSkill}" not found. Available skills: ${availableSkills.join(", ")}` : "No skills are currently available.";
521
+ }
522
+ return renderSkillInstructions({ skill, args });
523
+ };
524
+ executor.configuredSkills = projectedSkills.map((skill) => ({
525
+ id: skill.id,
526
+ name: skill.name,
527
+ description: skill.description,
528
+ disabled: false
529
+ }));
530
+ const tool2 = createDefaultTools({
531
+ executors: { skills: executor },
532
+ enableReadFiles: false,
533
+ enableSearch: false,
534
+ enableBash: false,
535
+ enableWebFetch: false,
536
+ enableApplyPatch: false,
537
+ enableEditor: false,
538
+ enableSkills: true,
539
+ enableAskQuestion: false,
540
+ enableSubmitAndExit: false
541
+ }).find((tool3) => tool3.name === "skills");
542
+ if (tool2 == null) {
543
+ throw new Error("Cline did not create its native skills tool.");
544
+ }
545
+ return { signature, tool: tool2 };
546
+ }
547
+ function projectClineSkills({
548
+ skills
549
+ }) {
550
+ const names = /* @__PURE__ */ new Set();
551
+ const ids = /* @__PURE__ */ new Set();
552
+ return skills.map((skill) => {
553
+ if (!CLINE_SKILL_NAME_PATTERN.test(skill.name) || skill.name === "." || skill.name === "..") {
554
+ throw new Error(`Invalid Cline skill name: ${skill.name}`);
555
+ }
556
+ if (names.has(skill.name)) {
557
+ throw new Error(`Duplicate Cline skill name: ${skill.name}`);
558
+ }
559
+ names.add(skill.name);
560
+ const id = normalizeSkillToken(skill.name);
561
+ if (ids.has(id)) {
562
+ throw new Error(`Duplicate Cline skill identifier: ${id}`);
563
+ }
564
+ ids.add(id);
565
+ return {
566
+ id,
567
+ name: skill.name,
568
+ description: skill.description,
569
+ content: skill.content,
570
+ files: (skill.files ?? []).map((file) => ({
571
+ path: normalizeSkillFilePath({
572
+ skillName: skill.name,
573
+ filePath: file.path
574
+ }),
575
+ content: file.content
576
+ })).sort(
577
+ (left, right) => left.path.localeCompare(right.path) || left.content.localeCompare(right.content)
578
+ )
579
+ };
580
+ }).sort((left, right) => left.name.localeCompare(right.name));
581
+ }
582
+ function normalizeSkillToken(skill) {
583
+ return skill.trim().replace(/^\/+/, "").toLowerCase();
584
+ }
585
+ function normalizeSkillFilePath({
586
+ skillName,
587
+ filePath
588
+ }) {
589
+ const normalized = path3.posix.normalize(filePath);
590
+ if (normalized === "" || normalized === "." || normalized.startsWith("../") || normalized.includes("/../") || normalized.endsWith("/..") || path3.posix.isAbsolute(normalized)) {
591
+ throw new Error(
592
+ `Invalid Cline skill file path for ${skillName}: ${filePath}`
593
+ );
594
+ }
595
+ return normalized;
524
596
  }
525
- function renderSkillsPromptSection(skills, skillRootDir) {
526
- const entries = skills.map(
527
- (skill) => `- ${skill.name}: ${skill.description}
528
- SKILL.md: ${path3.posix.join(
529
- skillRootDir,
530
- skill.name,
531
- "SKILL.md"
532
- )}`
533
- ).join("\n");
534
- 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;
597
+ function renderSkillInstructions({
598
+ skill,
599
+ args
600
+ }) {
601
+ const trimmedArgs = args?.trim();
602
+ const argsTag = trimmedArgs ? `
603
+ <command-args>${trimmedArgs}</command-args>` : "";
604
+ const description = skill.description.trim() ? `Description: ${skill.description.trim()}
605
+
606
+ ` : "";
607
+ const files = skill.files.length === 0 ? "" : `
608
+
609
+ <skill-files>
610
+ ${skill.files.map(
611
+ (file) => `<skill-file path=${JSON.stringify(file.path)}>
612
+ ${file.content}
613
+ </skill-file>`
614
+ ).join("\n")}
615
+ </skill-files>`;
616
+ return `<command-name>${skill.name}</command-name>${argsTag}
617
+ <command-instructions>
618
+ ${description}${skill.content}${files}
619
+ </command-instructions>`;
535
620
  }
536
621
 
537
622
  // src/cline-tools.ts
@@ -646,7 +731,8 @@ var CLINE_NATIVE_BUILTIN_NAMES = [
646
731
  "bash",
647
732
  "grep",
648
733
  "glob",
649
- "ls"
734
+ "ls",
735
+ "skills"
650
736
  ];
651
737
  var CLINE_NATIVE_TOOL_KINDS = {
652
738
  read: "readonly",
@@ -655,7 +741,8 @@ var CLINE_NATIVE_TOOL_KINDS = {
655
741
  bash: "bash",
656
742
  grep: "readonly",
657
743
  glob: "readonly",
658
- ls: "readonly"
744
+ ls: "readonly",
745
+ skills: "readonly"
659
746
  };
660
747
  function isClineBuiltinToolName(name) {
661
748
  return CLINE_NATIVE_BUILTIN_NAMES.includes(name);
@@ -1099,7 +1186,7 @@ function clineBuiltinToolRequiresApproval(input) {
1099
1186
  return input.kind === "edit" || input.kind === "bash";
1100
1187
  }
1101
1188
  function buildSystemPrompt(input) {
1102
- const sections = [
1189
+ return [
1103
1190
  "You are Cline, an autonomous coding agent operating inside a sandboxed workspace.",
1104
1191
  `## Workspace
1105
1192
 
@@ -1107,11 +1194,7 @@ The workspace root is \`${input.sessionWorkDir}\`. All relative file paths and s
1107
1194
  `## Sandbox
1108
1195
 
1109
1196
  ${input.sandboxDescription}`
1110
- ];
1111
- if (input.skillsSection) {
1112
- sections.push(input.skillsSection);
1113
- }
1114
- return sections.join("\n\n");
1197
+ ].join("\n\n");
1115
1198
  }
1116
1199
  function toClineBackendProviderBaseUrl(baseUrl) {
1117
1200
  return `${baseUrl.replace(/\/+$/, "")}/api/v1`;
@@ -1141,7 +1224,7 @@ function createClineAgentModel({
1141
1224
  ...apiKey ? { apiKey } : {},
1142
1225
  ...baseUrl ? { baseUrl } : {},
1143
1226
  ...headers ? { headers } : {},
1144
- ...isAiGateway ? { apiKeyEnv: [] } : {}
1227
+ ...isAiGateway || settings.isAuthenticationEnvironmentOverride ? { apiKeyEnv: [] } : {}
1145
1228
  }
1146
1229
  ]
1147
1230
  });
@@ -1188,28 +1271,9 @@ async function createClineSession(input) {
1188
1271
  const activeBuiltinNames = resolveActiveClineBuiltinNames(
1189
1272
  input.builtinToolFiltering
1190
1273
  );
1191
- let skillsSection;
1192
- let skillRootDir;
1193
- if (input.skills.length > 0) {
1194
- skillRootDir = await writeClineSkills({
1195
- sandbox: toolSafeSandboxSession,
1196
- sandboxHomeDir,
1197
- skills: input.skills,
1198
- ...input.abortSignal ? { abortSignal: input.abortSignal } : {}
1199
- });
1200
- if (skillRootDir) {
1201
- skillsSection = renderSkillsPromptSection(input.skills, skillRootDir);
1202
- }
1203
- }
1204
1274
  const ops = createClineRemoteOps({
1205
1275
  sandbox: toolSafeSandboxSession,
1206
- workDir: input.sessionWorkDir,
1207
- ...skillRootDir ? { readableRoots: [{ sandboxDir: skillRootDir }] } : {}
1208
- });
1209
- const baseSystemPrompt = buildSystemPrompt({
1210
- sessionWorkDir: input.sessionWorkDir,
1211
- sandboxDescription: toolSafeSandboxSession.description,
1212
- ...skillsSection ? { skillsSection } : {}
1276
+ workDir: input.sessionWorkDir
1213
1277
  });
1214
1278
  let currentMessages = [];
1215
1279
  if (input.isResume && input.resumeHistoryFileName) {
@@ -1232,7 +1296,7 @@ async function createClineSession(input) {
1232
1296
  });
1233
1297
  let agent;
1234
1298
  let unsubscribe;
1235
- let lastToolsSignature;
1299
+ let lastAgentConfigurationSignature;
1236
1300
  let agentHasRun = false;
1237
1301
  let stopped = false;
1238
1302
  let suspending = false;
@@ -1303,14 +1367,21 @@ async function createClineSession(input) {
1303
1367
  }
1304
1368
  } : {},
1305
1369
  sessionId: input.sessionId,
1306
- systemPrompt: rebuildInput.instructions ? `${baseSystemPrompt}
1370
+ systemPrompt: (() => {
1371
+ const baseSystemPrompt = buildSystemPrompt({
1372
+ sessionWorkDir: input.sessionWorkDir,
1373
+ sandboxDescription: toolSafeSandboxSession.description
1374
+ });
1375
+ return rebuildInput.instructions ? `${baseSystemPrompt}
1307
1376
 
1308
- ${rebuildInput.instructions}` : baseSystemPrompt,
1377
+ ${rebuildInput.instructions}` : baseSystemPrompt;
1378
+ })(),
1309
1379
  initialMessages: currentMessages,
1310
1380
  toolExecution: "parallel",
1311
1381
  ...input.settings.maxIterations !== void 0 ? { maxIterations: input.settings.maxIterations } : {},
1312
1382
  tools: [
1313
1383
  ...buildBuiltinAgentTools({ ops, activeNames: activeBuiltinNames }),
1384
+ ...rebuildInput.skillsRuntime.tool && activeBuiltinNames.includes("skills") ? [rebuildInput.skillsRuntime.tool] : [],
1314
1385
  ...mcpRuntime.tools,
1315
1386
  ...buildUserAgentTools({
1316
1387
  specs: rebuildInput.userTools,
@@ -1427,11 +1498,14 @@ ${rebuildInput.instructions}` : baseSystemPrompt,
1427
1498
  done: controlInput.done
1428
1499
  };
1429
1500
  }
1430
- function runTurn(turnOpts) {
1501
+ async function runTurn(turnOpts) {
1431
1502
  if (stopped) {
1432
1503
  throw new Error("Cline session has been stopped.");
1433
1504
  }
1434
1505
  const userTools = turnOpts.tools;
1506
+ const skillsRuntime = createClineSkillsRuntime({
1507
+ skills: turnOpts.skills
1508
+ });
1435
1509
  if (turnOpts.responseFormat?.type === "json" && turnOpts.responseFormat.schema == null) {
1436
1510
  throw new HarnessCapabilityUnsupportedError2({
1437
1511
  message: "Harness 'cline' requires a JSON schema for structured output.",
@@ -1445,22 +1519,28 @@ ${rebuildInput.instructions}` : baseSystemPrompt,
1445
1519
  });
1446
1520
  }
1447
1521
  const signature = JSON.stringify({
1448
- tools: userTools.map((t) => t.name).sort(),
1449
- responseFormat: turnOpts.responseFormat
1522
+ tools: userTools,
1523
+ responseFormat: turnOpts.responseFormat,
1524
+ instructions: turnOpts.instructions,
1525
+ skills: skillsRuntime.signature
1450
1526
  });
1451
- if (agent == null || signature !== lastToolsSignature) {
1527
+ if (agent == null || signature !== lastAgentConfigurationSignature) {
1452
1528
  currentMessages = agent?.snapshot().messages ?? currentMessages;
1453
1529
  rebuildAgent({
1454
1530
  userTools,
1531
+ skillsRuntime,
1455
1532
  responseFormat: turnOpts.responseFormat,
1456
1533
  ...turnOpts.instructions ? { instructions: turnOpts.instructions } : {}
1457
1534
  });
1458
- lastToolsSignature = signature;
1535
+ lastAgentConfigurationSignature = signature;
1459
1536
  }
1460
1537
  currentEmit = turnOpts.emit;
1538
+ const userToolNames = new Set(userTools.map((tool2) => tool2.name));
1461
1539
  translatorState = createClineTranslatorState({
1462
- builtinToolNames: activeBuiltinNames,
1463
- hostToolNames: userTools.map((tool2) => tool2.name),
1540
+ builtinToolNames: activeBuiltinNames.filter(
1541
+ (name) => !userToolNames.has(name)
1542
+ ),
1543
+ hostToolNames: userToolNames,
1464
1544
  ignoredToolNames: turnOpts.responseFormat?.type === "json" ? ["structured_output"] : [],
1465
1545
  mcpToolNames: mcpRuntime.toolNames
1466
1546
  });
@@ -1599,6 +1679,7 @@ ${rebuildInput.instructions}` : baseSystemPrompt,
1599
1679
  doPromptTurn: async (promptOpts) => {
1600
1680
  return runTurn({
1601
1681
  text: extractUserText(promptOpts.prompt),
1682
+ skills: promptOpts.skills,
1602
1683
  tools: promptOpts.tools ?? [],
1603
1684
  ...promptOpts.instructions ? { instructions: promptOpts.instructions } : {},
1604
1685
  emit: promptOpts.emit,
@@ -1616,6 +1697,7 @@ ${rebuildInput.instructions}` : baseSystemPrompt,
1616
1697
  }
1617
1698
  return runTurn({
1618
1699
  text: void 0,
1700
+ skills: continueOpts.skills,
1619
1701
  tools: continueOpts.tools ?? [],
1620
1702
  ...continueOpts.instructions ? { instructions: continueOpts.instructions } : {},
1621
1703
  emit: continueOpts.emit,
@@ -1699,7 +1781,7 @@ ${rebuildInput.instructions}` : baseSystemPrompt,
1699
1781
  }
1700
1782
 
1701
1783
  // src/version.ts
1702
- var VERSION = true ? "1.0.18" : "0.0.0-test";
1784
+ var VERSION = true ? "1.0.20" : "0.0.0-test";
1703
1785
 
1704
1786
  // src/cline-harness.ts
1705
1787
  var CLINE_CLIENT_APP = `ai-sdk/harness-cline/${VERSION}`;
@@ -1775,6 +1857,18 @@ var CLINE_BUILTIN_TOOLS = {
1775
1857
  }),
1776
1858
  nativeName: "ls",
1777
1859
  toolUseKind: "readonly"
1860
+ },
1861
+ skills: {
1862
+ ...tool({
1863
+ description: "Execute a configured skill by name.",
1864
+ inputSchema: z2.object({
1865
+ skill: z2.string(),
1866
+ args: z2.string().optional()
1867
+ }),
1868
+ outputSchema: z2.string()
1869
+ }),
1870
+ nativeName: "skills",
1871
+ toolUseKind: "readonly"
1778
1872
  }
1779
1873
  };
1780
1874
  function createCline(settings = {}) {
@@ -1786,6 +1880,7 @@ function createCline(settings = {}) {
1786
1880
  supportsBuiltinToolFiltering: true,
1787
1881
  lifecycleStateSchema: clineResumeStateSchema,
1788
1882
  doStart: async (startOpts) => {
1883
+ const modelId = startOpts.model ?? settings.modelId;
1789
1884
  const authEnv = resolveClineEnv({ auth: settings.auth });
1790
1885
  const lifecycleState = startOpts.continueFrom ?? startOpts.resumeFrom;
1791
1886
  const resumeData = lifecycleState?.data;
@@ -1793,12 +1888,12 @@ function createCline(settings = {}) {
1793
1888
  sessionId: startOpts.sessionId,
1794
1889
  sandboxSession: startOpts.sandboxSession,
1795
1890
  sessionWorkDir: startOpts.sessionWorkDir,
1796
- skills: startOpts.skills ?? [],
1797
1891
  settings: {
1798
1892
  authEnv,
1893
+ isAuthenticationEnvironmentOverride: isHarnessAuthenticationEnvironment2(settings.auth),
1799
1894
  ...settings.mcpServers ? { mcpServers: settings.mcpServers } : {},
1800
1895
  ...settings.providerId ? { providerId: settings.providerId } : {},
1801
- ...settings.modelId ? { modelId: settings.modelId } : {},
1896
+ ...modelId == null ? {} : { modelId },
1802
1897
  ...settings.apiKey ? { apiKey: settings.apiKey } : {},
1803
1898
  ...settings.baseUrl ? { baseUrl: settings.baseUrl } : {},
1804
1899
  ...settings.headers ? { headers: settings.headers } : {},