@usabledev/usable-chat 1.207.3 → 1.207.5

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.
Files changed (2) hide show
  1. package/cli.js +43 -10
  2. package/package.json +1 -1
package/cli.js CHANGED
@@ -362969,18 +362969,35 @@ function shouldRecoverSilentStop({
362969
362969
  recoveryAttempted,
362970
362970
  recoverPreToolStop = false
362971
362971
  }) {
362972
- return !recoveryAttempted && !text5?.trim() && (lastStepHadToolErrors || lastStepHadToolActivity || recoverPreToolStop);
362972
+ const stoppedBeforeAction = !text5?.trim() || recoverPreToolStop && isIncompleteWorkActionPreamble(text5);
362973
+ return !recoveryAttempted && stoppedBeforeAction && (lastStepHadToolErrors || lastStepHadToolActivity || recoverPreToolStop);
362974
+ }
362975
+ function isIncompleteWorkActionPreamble(text5) {
362976
+ const value = text5?.trim();
362977
+ if (!value || value.length > 160 || /[\n:;—–]|```/.test(value)) return false;
362978
+ const withoutLeadIn = value.replace(/^sure[,!.]?\s*/i, "");
362979
+ const withoutTerminalPunctuation = withoutLeadIn.replace(/[.!?]$/, "");
362980
+ if (/[,.!?]/.test(withoutTerminalPunctuation)) return false;
362981
+ if (!/(?:\bfor you|\bnow|\bnext|\bright away|\bshortly|\bmomentarily|\bfirst)[.!?]?$/i.test(value)) {
362982
+ return false;
362983
+ }
362984
+ return /^(?:sure[,!.]?\s*)?(?:i(?:'ll|\s+will|\s+am going to)|let me)\s+(?:now\s+)?(?:retrieve|fetch|look up|check|find|list|gather|inspect|run|execute|search|load|open|read|create|update|write|analy[sz]e)\b/i.test(
362985
+ value
362986
+ );
362973
362987
  }
362974
362988
  function resolveSilentStopResponse({
362975
362989
  text: text5,
362976
362990
  recoveryAttempted,
362977
362991
  recoverPreToolStop
362978
362992
  }) {
362979
- if (recoverPreToolStop && recoveryAttempted && !text5?.trim()) {
362993
+ if (recoverPreToolStop && recoveryAttempted && (!text5?.trim() || isIncompleteWorkActionPreamble(text5))) {
362980
362994
  return "I could not complete that Work request. Please try again.";
362981
362995
  }
362982
362996
  return text5 || "";
362983
362997
  }
362998
+ function resolveWorkForcedToolName(input) {
362999
+ return input.isWorkMode && !input.alreadySatisfied && typeof input.requestedToolName === "string" && input.offeredToolNames.includes(input.requestedToolName) ? input.requestedToolName : void 0;
363000
+ }
362984
363001
  function shouldInjectVisualizationPrompt(config3) {
362985
363002
  return !config3.localFilesystem && config3.visualizationsEnabled !== false;
362986
363003
  }
@@ -363746,7 +363763,8 @@ async function callLLM({
363746
363763
  scriptedLlm,
363747
363764
  providerRouting,
363748
363765
  providerPrivacy,
363749
- providerAttemptBudget
363766
+ providerAttemptBudget,
363767
+ forcedToolName
363750
363768
  }) {
363751
363769
  if (process.env.E2E_ALLOW_TEST_SEAMS === "true" && scriptedLlm && scriptedLlm.length > 0) {
363752
363770
  const turn = selectScriptedTurn(scriptedLlm, conversationMessages);
@@ -363975,7 +363993,7 @@ async function callLLM({
363975
363993
  include_usage: true
363976
363994
  },
363977
363995
  tools,
363978
- tool_choice: shouldEnableTools ? "auto" : void 0,
363996
+ tool_choice: shouldEnableTools ? forcedToolName ? { type: "function", function: { name: forcedToolName } } : "auto" : void 0,
363979
363997
  temperature,
363980
363998
  max_tokens: maxTokens,
363981
363999
  // Only OpenRouter accepts this extension. Compatible routes
@@ -366648,7 +366666,9 @@ ${combinedSystemMessage}` : combinedSystemMessage;
366648
366666
  );
366649
366667
  if (receipt) {
366650
366668
  if (receipt.outcome === "unknown")
366651
- throw new Error("Earlier tool outcome is unknown; repeating this action requires user resolution.");
366669
+ throw new Error(
366670
+ "Earlier tool outcome is unknown; repeating this action requires user resolution."
366671
+ );
366652
366672
  return receipt.result;
366653
366673
  }
366654
366674
  }
@@ -366848,6 +366868,7 @@ ${combinedSystemMessage}` : combinedSystemMessage;
366848
366868
  let lastStepHadToolActivity = false;
366849
366869
  let recoveryAttempted = false;
366850
366870
  let hookContinuationAttempted = false;
366871
+ let requiredWorkToolSatisfied = false;
366851
366872
  const repeatedToolCallCounts = /* @__PURE__ */ new Map();
366852
366873
  const MAX_IDENTICAL_TOOL_CALLS = 3;
366853
366874
  const stableStringify = (value) => {
@@ -366960,13 +366981,21 @@ ${submitHooks.context.join("\n")}
366960
366981
  );
366961
366982
  const useOpenAIToolFormat = !isClaude || providerRouting?.provider === "usable";
366962
366983
  const tools = shouldEnableTools && rawTools && !forceNoTools ? useOpenAIToolFormat ? adaptToolSchemasForProvider(convertToOpenRouterTools(rawTools), selectedModelId) : convertToClaudeTools(rawTools) : void 0;
366984
+ const toolNames2 = tools ? !useOpenAIToolFormat ? tools.map((tool2) => tool2.name) : tools.map((tool2) => tool2.function.name) : [];
366985
+ const requestedWorkToolName = context3.metadata?.workRequiredToolName;
366986
+ const forcedToolName = resolveWorkForcedToolName({
366987
+ isWorkMode: context3.metadata?.workRuntime === "machine",
366988
+ requestedToolName: requestedWorkToolName,
366989
+ offeredToolNames: toolNames2,
366990
+ alreadySatisfied: requiredWorkToolSatisfied
366991
+ });
366963
366992
  if (tools) {
366964
- const toolNames2 = !useOpenAIToolFormat ? tools.map((t14) => t14.name) : tools.map((t14) => t14.function.name);
366965
366993
  orchestrationLogger.info("Tools being sent to provider", {
366966
366994
  provider: isClaude ? "claude" : "openrouter",
366967
366995
  toolCount: toolNames2.length,
366968
366996
  toolNames: toolNames2,
366969
- hasAskSubagent: toolNames2.includes("ask-subagent")
366997
+ hasAskSubagent: toolNames2.includes("ask-subagent"),
366998
+ forcedToolName
366970
366999
  });
366971
367000
  }
366972
367001
  const toCountableMessage = (msg) => {
@@ -367345,7 +367374,8 @@ Re-read them (working_memory on web, bash on the CLI \u2014 grep/sed projection,
367345
367374
  scriptedLlm: config3.__scriptedLlm,
367346
367375
  providerRouting,
367347
367376
  providerPrivacy,
367348
- providerAttemptBudget
367377
+ providerAttemptBudget,
367378
+ forcedToolName
367349
367379
  });
367350
367380
  const overflowResult = result;
367351
367381
  if (overflowResult.finishReason === "error" && (overflowResult.contextOverflow || isContextOverflowError(overflowResult.errorMessage))) {
@@ -367439,6 +367469,9 @@ Re-read them (working_memory on web, bash on the CLI \u2014 grep/sed projection,
367439
367469
  finishReason: result.finishReason,
367440
367470
  toolCallsCount: result.toolCalls.length
367441
367471
  });
367472
+ if (forcedToolName && result.toolCalls.some((toolCall) => toolCall.name === forcedToolName)) {
367473
+ requiredWorkToolSatisfied = true;
367474
+ }
367442
367475
  orchestrationLogger.info(
367443
367476
  `Step ${currentStep} complete`,
367444
367477
  {
@@ -367470,7 +367503,7 @@ Re-read them (working_memory on web, bash on the CLI \u2014 grep/sed projection,
367470
367503
  recoverPreToolStop: context3.metadata?.workRuntime === "machine"
367471
367504
  })) {
367472
367505
  recoveryAttempted = true;
367473
- const recoveryPrompt = lastStepHadToolErrors ? "The previous tool call failed. Briefly summarise to the user what happened, why it likely failed, and any next steps they could take (without retrying the failed tool)." : lastStepHadToolActivity ? "The previous step used tools but did not produce a visible final answer. Using the available tool results, answer the user now. Do not call more tools unless absolutely required." : "The previous response ended before producing visible content or a tool call. Complete the user's original request now. Use the available tools when the request requires them.";
367506
+ const recoveryPrompt = lastStepHadToolErrors ? "The previous tool call failed. Briefly summarise to the user what happened, why it likely failed, and any next steps they could take (without retrying the failed tool)." : lastStepHadToolActivity ? "The previous step used tools but did not produce a visible final answer. Using the available tool results, answer the user now. Do not call more tools unless absolutely required." : "The previous response stopped before completing the requested action. Do not merely announce what you will do. Complete the user's original request now and call the required tool when one is available.";
367474
367507
  orchestrationLogger.warn(
367475
367508
  "Model stopped silently \u2014 injecting recovery prompt",
367476
367509
  {
@@ -391558,7 +391591,7 @@ init_tui_select();
391558
391591
  init_model_registry();
391559
391592
 
391560
391593
  // package.json
391561
- var version2 = "1.207.3";
391594
+ var version2 = "1.207.5";
391562
391595
 
391563
391596
  // src/adapters/cli/model-catalog.ts
391564
391597
  init_codex_auth();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@usabledev/usable-chat",
3
- "version": "1.207.3",
3
+ "version": "1.207.5",
4
4
  "description": "usable-chat — terminal harness for usable-chat (headless + TUI)",
5
5
  "type": "module",
6
6
  "bin": {