@ai-sdk/harness-cline 1.0.27 → 1.0.28

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 CHANGED
@@ -1,6 +1,7 @@
1
1
  // src/cline-harness.ts
2
2
  import {
3
- commonTool
3
+ commonTool,
4
+ HARNESS_V1_BUILTIN_TOOLS
4
5
  } from "@ai-sdk/harness";
5
6
  import { isHarnessAuthenticationEnvironment as isHarnessAuthenticationEnvironment2 } from "@ai-sdk/harness/utils";
6
7
  import { tool } from "@ai-sdk/provider-utils";
@@ -623,6 +624,7 @@ ${description}${skill.content}${files}
623
624
  import {
624
625
  createTool
625
626
  } from "@cline/agents";
627
+ import { createDefaultTools as createDefaultTools2 } from "@cline/core";
626
628
 
627
629
  // src/cline-utils.ts
628
630
  import {
@@ -725,6 +727,7 @@ function usageFromMessageMetrics(metrics) {
725
727
 
726
728
  // src/cline-tools.ts
727
729
  var CLINE_NATIVE_BUILTIN_NAMES = [
730
+ "ask_question",
728
731
  "read",
729
732
  "write",
730
733
  "edit",
@@ -735,6 +738,7 @@ var CLINE_NATIVE_BUILTIN_NAMES = [
735
738
  "skills"
736
739
  ];
737
740
  var CLINE_NATIVE_TOOL_KINDS = {
741
+ ask_question: "readonly",
738
742
  read: "readonly",
739
743
  write: "edit",
740
744
  edit: "edit",
@@ -749,15 +753,21 @@ function isClineBuiltinToolName(name) {
749
753
  }
750
754
  function resolveActiveClineBuiltinNames(toolFiltering) {
751
755
  if (toolFiltering == null) return CLINE_NATIVE_BUILTIN_NAMES;
756
+ const filteredNames = toolFiltering.toolNames.map(
757
+ (name) => name === "askUserQuestions" ? "ask_question" : name
758
+ );
752
759
  if (toolFiltering.mode === "allow") {
753
760
  return CLINE_NATIVE_BUILTIN_NAMES.filter(
754
- (name) => toolFiltering.toolNames.includes(name)
761
+ (name) => filteredNames.includes(name)
755
762
  );
756
763
  }
757
764
  return CLINE_NATIVE_BUILTIN_NAMES.filter(
758
- (name) => !toolFiltering.toolNames.includes(name)
765
+ (name) => !filteredNames.includes(name)
759
766
  );
760
767
  }
768
+ function clineQuestionKey(input) {
769
+ return JSON.stringify([input.question, input.options]);
770
+ }
761
771
  var clineToolResultMarker = /* @__PURE__ */ Symbol("cline-tool-result");
762
772
  function createClineToolResult({
763
773
  output,
@@ -791,9 +801,45 @@ async function guarded(run) {
791
801
  }
792
802
  function buildBuiltinAgentTools({
793
803
  ops,
794
- activeNames
804
+ activeNames,
805
+ pendingQuestions = /* @__PURE__ */ new Map(),
806
+ pendingQuestionResults = /* @__PURE__ */ new Map()
795
807
  }) {
796
808
  const tools = {
809
+ ask_question: createDefaultTools2({
810
+ executors: {
811
+ askQuestion: async (question, options, context) => {
812
+ if (context.toolCallId == null) {
813
+ throw new Error(
814
+ "Cline ask_question did not provide a tool call id."
815
+ );
816
+ }
817
+ const questionKey = clineQuestionKey({ question, options });
818
+ const pendingResult = pendingQuestionResults.get(questionKey);
819
+ if (pendingResult != null) {
820
+ pendingQuestionResults.delete(questionKey);
821
+ return toClineQuestionResult({
822
+ nativeInput: { question, options },
823
+ output: pendingResult.output
824
+ });
825
+ }
826
+ return new Promise((resolve) => {
827
+ pendingQuestions.set(context.toolCallId, {
828
+ input: { question, options },
829
+ resolve
830
+ });
831
+ });
832
+ }
833
+ },
834
+ enableReadFiles: false,
835
+ enableSearch: false,
836
+ enableBash: false,
837
+ enableWebFetch: false,
838
+ enableApplyPatch: false,
839
+ enableEditor: false,
840
+ enableSkills: false,
841
+ enableAskQuestion: true
842
+ }).find((tool2) => tool2.name === "ask_question"),
797
843
  read: createTool({
798
844
  name: "read",
799
845
  description: "Read file contents.",
@@ -948,6 +994,19 @@ function buildBuiltinAgentTools({
948
994
  };
949
995
  return activeNames.map((name) => tools[name]).filter((tool2) => tool2 != null);
950
996
  }
997
+ function toClineQuestionResult(input) {
998
+ if (input.output.action === "declined" || input.output.action === "cancelled") {
999
+ return `The user ${input.output.action} the question.`;
1000
+ }
1001
+ const answer = input.output.answers["question-1"];
1002
+ if (answer == null) return "The user did not answer the question.";
1003
+ if (answer.freeform != null) return answer.freeform;
1004
+ const result = answer.optionIds.map((id) => {
1005
+ const index = Number(id.replace(/^option-/, "")) - 1;
1006
+ return input.nativeInput.options[index];
1007
+ }).filter((value) => value != null).join(", ");
1008
+ return result;
1009
+ }
951
1010
  function buildUserAgentTools({
952
1011
  specs,
953
1012
  pendingToolResults
@@ -1027,6 +1086,35 @@ function toolCallPart(state, toolCall) {
1027
1086
  if (isMcpTool) {
1028
1087
  state.dynamicToolCallIds.add(toolCall.toolCallId);
1029
1088
  }
1089
+ if (toolCall.toolName === "ask_question") {
1090
+ const nativeInput = toolCall.input;
1091
+ return {
1092
+ type: "tool-call",
1093
+ toolCallId: toolCall.toolCallId,
1094
+ toolName: "askUserQuestions",
1095
+ input: JSON.stringify({
1096
+ allowPartialAnswers: false,
1097
+ questions: [
1098
+ {
1099
+ id: "question-1",
1100
+ question: nativeInput.question,
1101
+ options: nativeInput.options.map((label, index) => ({
1102
+ id: `option-${index + 1}`,
1103
+ label
1104
+ })),
1105
+ allowFreeForm: true
1106
+ }
1107
+ ]
1108
+ }),
1109
+ providerExecuted: false,
1110
+ providerMetadata: {
1111
+ cline: {
1112
+ nativeRequest: nativeInput
1113
+ }
1114
+ },
1115
+ nativeName: "ask_question"
1116
+ };
1117
+ }
1030
1118
  return {
1031
1119
  type: "tool-call",
1032
1120
  toolCallId: toolCall.toolCallId,
@@ -1128,7 +1216,7 @@ function translateClineEvent(event, state) {
1128
1216
  {
1129
1217
  type: "tool-result",
1130
1218
  toolCallId: event.toolCall.toolCallId,
1131
- toolName: event.toolCall.toolName,
1219
+ toolName: event.toolCall.toolName === "ask_question" ? "askUserQuestions" : event.toolCall.toolName,
1132
1220
  result: toToolResultValue(output),
1133
1221
  ...resultPart?.isError ? { isError: true } : {},
1134
1222
  ...dynamic ? { dynamic: true } : {}
@@ -1309,6 +1397,8 @@ async function createClineSession(input) {
1309
1397
  let acceptingUserMessages = false;
1310
1398
  let currentEmit;
1311
1399
  let translatorState;
1400
+ const pendingQuestions = /* @__PURE__ */ new Map();
1401
+ const pendingQuestionResults = /* @__PURE__ */ new Map();
1312
1402
  let activeTurn;
1313
1403
  function settlePendingToolResults(reason) {
1314
1404
  for (const pending of pendingToolResults.values()) {
@@ -1339,6 +1429,13 @@ async function createClineSession(input) {
1339
1429
  }
1340
1430
  pendingUserMessages.length = 0;
1341
1431
  }
1432
+ function settlePendingQuestions(reason) {
1433
+ for (const pending of pendingQuestions.values()) {
1434
+ pending.resolve(reason);
1435
+ }
1436
+ pendingQuestions.clear();
1437
+ pendingQuestionResults.clear();
1438
+ }
1342
1439
  async function persistHistory() {
1343
1440
  const messages = agent?.snapshot().messages ?? currentMessages;
1344
1441
  await persistHistoryToSandbox({
@@ -1383,7 +1480,12 @@ ${rebuildInput.instructions}` : baseSystemPrompt;
1383
1480
  toolExecution: "parallel",
1384
1481
  ...input.settings.maxIterations !== void 0 ? { maxIterations: input.settings.maxIterations } : {},
1385
1482
  tools: [
1386
- ...buildBuiltinAgentTools({ ops, activeNames: activeBuiltinNames }),
1483
+ ...buildBuiltinAgentTools({
1484
+ ops,
1485
+ activeNames: activeBuiltinNames,
1486
+ pendingQuestions,
1487
+ pendingQuestionResults
1488
+ }),
1387
1489
  ...rebuildInput.skillsRuntime.tool && activeBuiltinNames.includes("skills") ? [rebuildInput.skillsRuntime.tool] : [],
1388
1490
  ...mcpRuntime.tools,
1389
1491
  ...buildUserAgentTools({
@@ -1452,6 +1554,7 @@ ${rebuildInput.instructions}` : baseSystemPrompt;
1452
1554
  const abortHandler = () => {
1453
1555
  settlePendingToolResults("Turn aborted");
1454
1556
  settlePendingToolApprovals("Turn aborted");
1557
+ settlePendingQuestions("The question was cancelled.");
1455
1558
  agent?.abort("Turn aborted by caller");
1456
1559
  };
1457
1560
  if (controlInput.abortSignal) {
@@ -1469,6 +1572,25 @@ ${rebuildInput.instructions}` : baseSystemPrompt;
1469
1572
  }
1470
1573
  return {
1471
1574
  async submitToolResult(args) {
1575
+ const pendingQuestion = pendingQuestions.get(args.toolCallId);
1576
+ if (pendingQuestion != null) {
1577
+ pendingQuestions.delete(args.toolCallId);
1578
+ pendingQuestion.resolve(
1579
+ toClineQuestionResult({
1580
+ nativeInput: pendingQuestion.input,
1581
+ output: args.output
1582
+ })
1583
+ );
1584
+ return;
1585
+ }
1586
+ if (args.toolResult?.toolName === "askUserQuestions") {
1587
+ const nativeRequest = args.toolResult.providerOptions?.cline?.nativeRequest;
1588
+ if (nativeRequest == null) return;
1589
+ pendingQuestionResults.set(clineQuestionKey(nativeRequest), {
1590
+ output: args.output
1591
+ });
1592
+ return;
1593
+ }
1472
1594
  const pending = pendingToolResults.get(args.toolCallId);
1473
1595
  if (!pending) return;
1474
1596
  pendingToolResults.delete(args.toolCallId);
@@ -1668,6 +1790,7 @@ ${rebuildInput.instructions}` : baseSystemPrompt;
1668
1790
  parkedClineSessions.delete(input.sessionId);
1669
1791
  settlePendingToolResults("Cline session stopped");
1670
1792
  settlePendingToolApprovals("Cline session stopped");
1793
+ settlePendingQuestions("The question was cancelled.");
1671
1794
  try {
1672
1795
  await persistHistory();
1673
1796
  } catch {
@@ -1735,7 +1858,7 @@ ${rebuildInput.instructions}` : baseSystemPrompt;
1735
1858
  },
1736
1859
  doStop,
1737
1860
  doDetach: async () => {
1738
- if (activeTurn != null || pendingToolResults.size > 0) {
1861
+ if (activeTurn != null || pendingToolResults.size > 0 || pendingQuestions.size > 0) {
1739
1862
  parkedClineSessions.set(input.sessionId, sessionImpl);
1740
1863
  try {
1741
1864
  await persistHistory();
@@ -1754,7 +1877,7 @@ ${rebuildInput.instructions}` : baseSystemPrompt;
1754
1877
  if (stopped) {
1755
1878
  throw new Error("Cline session has been stopped.");
1756
1879
  }
1757
- if (activeTurn != null && (pendingToolResults.size > 0 || pendingToolApprovals.size > 0)) {
1880
+ if (activeTurn != null && (pendingToolResults.size > 0 || pendingToolApprovals.size > 0 || pendingQuestions.size > 0)) {
1758
1881
  parkedClineSessions.set(input.sessionId, sessionImpl);
1759
1882
  try {
1760
1883
  await persistHistory();
@@ -1794,11 +1917,16 @@ ${rebuildInput.instructions}` : baseSystemPrompt;
1794
1917
  }
1795
1918
 
1796
1919
  // src/version.ts
1797
- var VERSION = true ? "1.0.27" : "0.0.0-test";
1920
+ var VERSION = true ? "1.0.28" : "0.0.0-test";
1798
1921
 
1799
1922
  // src/cline-harness.ts
1800
1923
  var CLINE_CLIENT_APP = `ai-sdk/harness-cline/${VERSION}`;
1801
1924
  var CLINE_BUILTIN_TOOLS = {
1925
+ askUserQuestions: {
1926
+ ...HARNESS_V1_BUILTIN_TOOLS.askUserQuestions,
1927
+ nativeName: "ask_question",
1928
+ toolUseKind: "readonly"
1929
+ },
1802
1930
  read: commonTool("read", {
1803
1931
  nativeName: "read",
1804
1932
  toolUseKind: "readonly",